From 15b3de1a6765de755af4b88b6033f6be59370222 Mon Sep 17 00:00:00 2001 From: bb7133 Date: Fri, 13 Oct 2023 21:22:08 +0800 Subject: [PATCH] *: move most of the packages to /pkg and /cmd --- .github/licenserc.yml | 10 +- .gitmodules | 2 +- Makefile | 44 +- Makefile.common | 16 +- autoid_service/BUILD.bazel | 46 - autoid_service/autoid.go | 570 - autoid_service/autoid_test.go | 196 - bindinfo/BUILD.bazel | 83 - bindinfo/handle.go | 1288 - bindinfo/handle_test.go | 609 - bindinfo/internal/BUILD.bazel | 15 - bindinfo/internal/testutil.go | 40 - bindinfo/main_test.go | 33 - bindinfo/optimize_test.go | 38 - bindinfo/stat.go | 40 - bindinfo/tests/BUILD.bazel | 30 - bindinfo/tests/main_test.go | 33 - br/cmd/br/BUILD.bazel | 12 +- br/cmd/br/backup.go | 6 +- br/cmd/br/cmd.go | 6 +- br/cmd/br/debug.go | 2 +- br/cmd/br/restore.go | 4 +- br/pkg/backup/BUILD.bazel | 40 +- br/pkg/backup/client.go | 18 +- br/pkg/backup/client_test.go | 12 +- br/pkg/backup/main_test.go | 2 +- br/pkg/backup/schema.go | 8 +- br/pkg/backup/schema_test.go | 6 +- br/pkg/checkpoint/BUILD.bazel | 4 +- br/pkg/checkpoint/checkpoint_test.go | 2 +- br/pkg/checkpoint/log_restore.go | 2 +- br/pkg/checksum/BUILD.bazel | 22 +- br/pkg/checksum/executor.go | 12 +- br/pkg/checksum/executor_test.go | 8 +- br/pkg/checksum/main_test.go | 2 +- br/pkg/conn/BUILD.bazel | 8 +- br/pkg/conn/conn.go | 6 +- br/pkg/conn/main_test.go | 2 +- br/pkg/conn/util/BUILD.bazel | 2 +- br/pkg/conn/util/util.go | 2 +- br/pkg/glue/BUILD.bazel | 10 +- br/pkg/glue/glue.go | 10 +- br/pkg/gluetidb/BUILD.bazel | 34 +- br/pkg/gluetidb/glue.go | 20 +- br/pkg/gluetidb/glue_test.go | 22 +- br/pkg/gluetikv/BUILD.bazel | 8 +- br/pkg/gluetikv/glue.go | 8 +- br/pkg/lightning/BUILD.bazel | 8 +- br/pkg/lightning/backend/BUILD.bazel | 4 +- br/pkg/lightning/backend/backend.go | 2 +- br/pkg/lightning/backend/backend_test.go | 2 +- br/pkg/lightning/backend/encode/BUILD.bazel | 6 +- br/pkg/lightning/backend/encode/encode.go | 6 +- br/pkg/lightning/backend/external/BUILD.bazel | 18 +- .../lightning/backend/external/byte_reader.go | 4 +- br/pkg/lightning/backend/external/engine.go | 4 +- .../lightning/backend/external/engine_test.go | 2 +- br/pkg/lightning/backend/external/iter.go | 2 +- .../lightning/backend/external/kv_reader.go | 2 +- .../lightning/backend/external/split_test.go | 2 +- br/pkg/lightning/backend/external/util.go | 6 +- br/pkg/lightning/backend/external/writer.go | 8 +- .../lightning/backend/external/writer_test.go | 4 +- br/pkg/lightning/backend/kv/BUILD.bazel | 58 +- br/pkg/lightning/backend/kv/allocator.go | 2 +- br/pkg/lightning/backend/kv/base.go | 18 +- br/pkg/lightning/backend/kv/base_test.go | 10 +- br/pkg/lightning/backend/kv/kv2sql.go | 12 +- br/pkg/lightning/backend/kv/kv2sql_test.go | 18 +- br/pkg/lightning/backend/kv/session.go | 12 +- br/pkg/lightning/backend/kv/session_test.go | 2 +- br/pkg/lightning/backend/kv/sql2kv.go | 18 +- br/pkg/lightning/backend/kv/sql2kv_test.go | 28 +- br/pkg/lightning/backend/local/BUILD.bazel | 68 +- br/pkg/lightning/backend/local/checksum.go | 6 +- .../lightning/backend/local/checksum_test.go | 12 +- br/pkg/lightning/backend/local/compress.go | 2 +- br/pkg/lightning/backend/local/duplicate.go | 20 +- .../lightning/backend/local/duplicate_test.go | 16 +- br/pkg/lightning/backend/local/engine.go | 4 +- br/pkg/lightning/backend/local/local.go | 14 +- br/pkg/lightning/backend/local/local_test.go | 16 +- br/pkg/lightning/backend/local/localhelper.go | 4 +- .../backend/local/localhelper_test.go | 12 +- br/pkg/lightning/backend/local/region_job.go | 6 +- br/pkg/lightning/backend/tidb/BUILD.bazel | 26 +- br/pkg/lightning/backend/tidb/tidb.go | 12 +- br/pkg/lightning/backend/tidb/tidb_test.go | 14 +- br/pkg/lightning/checkpoints/BUILD.bazel | 16 +- br/pkg/lightning/checkpoints/checkpoints.go | 4 +- .../checkpoints/checkpoints_file_test.go | 2 +- .../checkpoints/checkpoints_sql_test.go | 2 +- .../checkpointspb/file_checkpoints.pb.go | 5 +- .../lightning/checkpoints/glue_checkpoint.go | 8 +- br/pkg/lightning/checkpoints/main_test.go | 2 +- br/pkg/lightning/checkpoints/tidb.go | 2 +- br/pkg/lightning/common/BUILD.bazel | 50 +- br/pkg/lightning/common/common.go | 6 +- br/pkg/lightning/common/common_test.go | 18 +- br/pkg/lightning/common/key_adapter.go | 2 +- br/pkg/lightning/common/main_test.go | 2 +- br/pkg/lightning/common/retry.go | 4 +- br/pkg/lightning/common/retry_test.go | 4 +- br/pkg/lightning/common/security.go | 2 +- br/pkg/lightning/common/util.go | 16 +- br/pkg/lightning/common/util_test.go | 4 +- br/pkg/lightning/config/BUILD.bazel | 12 +- br/pkg/lightning/config/config.go | 12 +- br/pkg/lightning/duplicate/BUILD.bazel | 6 +- br/pkg/lightning/duplicate/detector.go | 2 +- br/pkg/lightning/duplicate/detector_test.go | 2 +- br/pkg/lightning/duplicate/internal.go | 2 +- br/pkg/lightning/duplicate/worker.go | 2 +- br/pkg/lightning/errormanager/BUILD.bazel | 16 +- br/pkg/lightning/errormanager/errormanager.go | 8 +- .../errormanager/errormanager_test.go | 8 +- br/pkg/lightning/importer/BUILD.bazel | 100 +- br/pkg/lightning/importer/check_info_test.go | 14 +- br/pkg/lightning/importer/checksum_helper.go | 2 +- br/pkg/lightning/importer/chunk_process.go | 16 +- .../lightning/importer/chunk_process_test.go | 16 +- br/pkg/lightning/importer/dup_detect.go | 12 +- br/pkg/lightning/importer/dup_detect_test.go | 12 +- br/pkg/lightning/importer/get_pre_info.go | 22 +- .../lightning/importer/get_pre_info_test.go | 6 +- br/pkg/lightning/importer/import.go | 24 +- br/pkg/lightning/importer/import_test.go | 14 +- .../lightning/importer/meta_manager_test.go | 18 +- br/pkg/lightning/importer/mock/BUILD.bazel | 12 +- br/pkg/lightning/importer/mock/mock.go | 10 +- br/pkg/lightning/importer/mock/mock_test.go | 2 +- br/pkg/lightning/importer/precheck_impl.go | 16 +- .../lightning/importer/restore_schema_test.go | 14 +- br/pkg/lightning/importer/table_import.go | 20 +- .../lightning/importer/table_import_test.go | 22 +- br/pkg/lightning/importer/tidb.go | 12 +- br/pkg/lightning/importer/tidb_test.go | 16 +- br/pkg/lightning/lightning.go | 8 +- br/pkg/lightning/log/BUILD.bazel | 4 +- br/pkg/lightning/log/log.go | 2 +- br/pkg/lightning/log/log_test.go | 2 +- br/pkg/lightning/metric/BUILD.bazel | 4 +- br/pkg/lightning/metric/metric.go | 2 +- br/pkg/lightning/metric/metric_test.go | 2 +- br/pkg/lightning/mydump/BUILD.bazel | 30 +- br/pkg/lightning/mydump/csv_parser.go | 6 +- br/pkg/lightning/mydump/csv_parser_test.go | 2 +- br/pkg/lightning/mydump/loader.go | 4 +- br/pkg/lightning/mydump/loader_test.go | 4 +- br/pkg/lightning/mydump/main_test.go | 2 +- br/pkg/lightning/mydump/parquet_parser.go | 2 +- .../lightning/mydump/parquet_parser_test.go | 2 +- br/pkg/lightning/mydump/parser.go | 6 +- br/pkg/lightning/mydump/parser_test.go | 4 +- br/pkg/lightning/mydump/region.go | 2 +- br/pkg/lightning/mydump/router.go | 4 +- br/pkg/lightning/mydump/router_test.go | 2 +- br/pkg/lightning/run_options.go | 2 +- br/pkg/lightning/tikv/BUILD.bazel | 4 +- br/pkg/lightning/tikv/tikv.go | 4 +- br/pkg/logutil/BUILD.bazel | 2 +- br/pkg/logutil/logging.go | 2 +- br/pkg/metautil/BUILD.bazel | 10 +- br/pkg/metautil/main_test.go | 2 +- br/pkg/metautil/metafile.go | 8 +- br/pkg/mock/BUILD.bazel | 16 +- br/pkg/mock/backend.go | 2 +- br/pkg/mock/encode.go | 2 +- br/pkg/mock/mock_cluster.go | 12 +- br/pkg/pdutil/BUILD.bazel | 12 +- br/pkg/pdutil/main_test.go | 2 +- br/pkg/pdutil/pd.go | 4 +- br/pkg/pdutil/pd_serial_test.go | 4 +- br/pkg/pdutil/utils.go | 6 +- br/pkg/restore/BUILD.bazel | 70 +- br/pkg/restore/batcher_test.go | 2 +- br/pkg/restore/client.go | 34 +- br/pkg/restore/client_test.go | 10 +- br/pkg/restore/data.go | 4 +- br/pkg/restore/db.go | 14 +- br/pkg/restore/db_test.go | 12 +- br/pkg/restore/import.go | 4 +- br/pkg/restore/import_retry_test.go | 6 +- br/pkg/restore/ingestrec/BUILD.bazel | 6 +- br/pkg/restore/ingestrec/ingest_recorder.go | 4 +- .../restore/ingestrec/ingest_recorder_test.go | 2 +- br/pkg/restore/log_client.go | 2 +- br/pkg/restore/main_test.go | 2 +- br/pkg/restore/merge.go | 4 +- br/pkg/restore/merge_fuzz_test.go | 2 +- br/pkg/restore/merge_test.go | 8 +- br/pkg/restore/pipeline_items.go | 2 +- br/pkg/restore/prealloc_table_id/BUILD.bazel | 4 +- br/pkg/restore/prealloc_table_id/alloc.go | 2 +- .../restore/prealloc_table_id/alloc_test.go | 2 +- br/pkg/restore/range.go | 2 +- br/pkg/restore/range_test.go | 2 +- br/pkg/restore/rawkv_client.go | 2 +- br/pkg/restore/rawkv_client_test.go | 4 +- br/pkg/restore/search.go | 2 +- br/pkg/restore/search_test.go | 2 +- br/pkg/restore/split.go | 4 +- br/pkg/restore/split/BUILD.bazel | 4 +- br/pkg/restore/split/client.go | 2 +- br/pkg/restore/split/sum_sorted.go | 2 +- br/pkg/restore/split_test.go | 8 +- br/pkg/restore/stream_metas.go | 2 +- br/pkg/restore/systable_restore.go | 6 +- br/pkg/restore/systable_restore_test.go | 2 +- br/pkg/restore/tiflashrec/BUILD.bazel | 12 +- br/pkg/restore/tiflashrec/tiflash_recorder.go | 8 +- .../tiflashrec/tiflash_recorder_test.go | 4 +- br/pkg/restore/util.go | 6 +- br/pkg/restore/util_test.go | 6 +- br/pkg/rtree/BUILD.bazel | 2 +- br/pkg/rtree/main_test.go | 2 +- br/pkg/storage/BUILD.bazel | 4 +- br/pkg/storage/helper.go | 4 +- br/pkg/storage/storage.go | 2 +- br/pkg/stream/BUILD.bazel | 30 +- br/pkg/stream/meta_kv.go | 6 +- br/pkg/stream/meta_kv_test.go | 6 +- br/pkg/stream/rewrite_meta_rawkv.go | 8 +- br/pkg/stream/rewrite_meta_rawkv_test.go | 12 +- br/pkg/stream/stream_mgr.go | 12 +- br/pkg/streamhelper/BUILD.bazel | 20 +- br/pkg/streamhelper/advancer.go | 4 +- br/pkg/streamhelper/advancer_cliext.go | 2 +- br/pkg/streamhelper/advancer_daemon.go | 4 +- br/pkg/streamhelper/advancer_env.go | 4 +- br/pkg/streamhelper/advancer_test.go | 2 +- br/pkg/streamhelper/basic_lib_for_test.go | 4 +- br/pkg/streamhelper/client.go | 4 +- br/pkg/streamhelper/collector.go | 4 +- br/pkg/streamhelper/daemon/BUILD.bazel | 4 +- br/pkg/streamhelper/daemon/owner_daemon.go | 2 +- .../streamhelper/daemon/owner_daemon_test.go | 2 +- br/pkg/streamhelper/flush_subscriber.go | 4 +- br/pkg/streamhelper/integration_test.go | 2 +- br/pkg/streamhelper/models.go | 2 +- br/pkg/streamhelper/prefix_scanner.go | 2 +- br/pkg/streamhelper/regioniter.go | 4 +- br/pkg/streamhelper/regioniter_test.go | 2 +- br/pkg/streamhelper/spans/BUILD.bazel | 2 +- br/pkg/streamhelper/spans/sorted.go | 2 +- br/pkg/summary/BUILD.bazel | 2 +- br/pkg/summary/main_test.go | 2 +- br/pkg/task/BUILD.bazel | 34 +- br/pkg/task/backup.go | 14 +- br/pkg/task/backup_ebs.go | 2 +- br/pkg/task/common.go | 6 +- br/pkg/task/common_test.go | 2 +- br/pkg/task/restore.go | 6 +- br/pkg/task/restore_data.go | 2 +- br/pkg/task/restore_test.go | 6 +- br/pkg/task/show/BUILD.bazel | 2 +- br/pkg/task/show/cmd_test.go | 2 +- br/pkg/task/stream.go | 10 +- br/pkg/trace/BUILD.bazel | 2 +- br/pkg/trace/main_test.go | 2 +- br/pkg/utils/BUILD.bazel | 42 +- br/pkg/utils/db.go | 6 +- br/pkg/utils/db_test.go | 12 +- br/pkg/utils/dyn_pprof_other.go | 2 +- br/pkg/utils/dyn_pprof_unix.go | 2 +- br/pkg/utils/key.go | 2 +- br/pkg/utils/key_test.go | 2 +- br/pkg/utils/main_test.go | 2 +- br/pkg/utils/misc.go | 4 +- br/pkg/utils/misc_test.go | 4 +- br/pkg/utils/pprof.go | 2 +- br/pkg/utils/retry.go | 4 +- br/pkg/utils/schema.go | 4 +- br/pkg/utils/schema_test.go | 6 +- br/pkg/utils/suspend_importing.go | 2 +- br/pkg/version/BUILD.bazel | 6 +- br/pkg/version/build/BUILD.bazel | 6 +- br/pkg/version/build/info.go | 6 +- br/pkg/version/version.go | 4 +- br/pkg/version/version_test.go | 2 +- br/tests/br_key_locked/BUILD.bazel | 12 +- br/tests/br_key_locked/codec.go | 2 +- br/tests/br_key_locked/locker.go | 10 +- build/linter/deferrecover/analyzer.go | 2 +- build/nogo_config.json | 696 +- cmd/benchdb/BUILD.bazel | 10 +- cmd/benchdb/main.go | 10 +- cmd/benchkv/BUILD.bazel | 6 +- cmd/benchkv/main.go | 6 +- cmd/benchraw/BUILD.bazel | 2 +- cmd/benchraw/main.go | 2 +- cmd/ddltest/BUILD.bazel | 36 +- cmd/ddltest/column_test.go | 10 +- cmd/ddltest/ddl_test.go | 30 +- cmd/ddltest/index_test.go | 4 +- cmd/ddltest/main_test.go | 4 +- cmd/importer/BUILD.bazel | 22 +- cmd/importer/data.go | 2 +- cmd/importer/db.go | 2 +- cmd/importer/parser.go | 14 +- cmd/importer/stats.go | 8 +- cmd/mirror/BUILD.bazel | 4 +- cmd/mirror/mirror.go | 12 +- cmd/pluginpkg/pluginpkg.go | 2 +- cmd/tidb-server/BUILD.bazel | 112 + {tidb-server => cmd/tidb-server}/main.go | 102 +- cmd/tidb-server/main_test.go | 82 + config/BUILD.bazel | 51 - config/config.go | 1554 - config/main_test.go | 34 - ddl/BUILD.bazel | 314 - ddl/callback.go | 234 - ddl/cluster.go | 829 - ddl/cluster_test.go | 278 - ddl/column.go | 2029 -- ddl/column_test.go | 948 - ddl/constant.go | 85 - ddl/constraint.go | 417 - ddl/copr/BUILD.bazel | 33 - ddl/db_test.go | 1092 - ddl/ddl.go | 1863 -- ddl/ddl_error_test.go | 182 - ddl/ddl_test.go | 287 - ddl/fail_test.go | 65 - ddl/foreign_key.go | 729 - ddl/foreign_key_test.go | 432 - ddl/index.go | 2515 -- ddl/ingest/BUILD.bazel | 87 - ddl/ingest/config.go | 164 - ddl/ingest/integration_test.go | 311 - ddl/ingest/mock.go | 234 - ddl/ingest/tests/BUILD.bazel | 17 - ddl/ingest/tests/partition_table_test.go | 74 - ddl/ingest/testutil/BUILD.bazel | 14 - ddl/ingest/testutil/testutil.go | 43 - ddl/integration_test.go | 60 - ddl/internal/session/BUILD.bazel | 41 - ddl/internal/session/session.go | 137 - ddl/label/BUILD.bazel | 37 - ddl/label/main_test.go | 33 - ddl/label/rule.go | 172 - ddl/main_test.go | 74 - ddl/mock.go | 250 - ddl/options.go | 70 - ddl/partition.go | 4253 --- ddl/partition_test.go | 248 - ddl/placement/BUILD.bazel | 49 - ddl/placement/rule.go | 263 - ddl/reorg.go | 896 - ddl/resourcegroup/BUILD.bazel | 16 - ddl/resourcegroup/group.go | 83 - ddl/schema.go | 367 - ddl/schema_test.go | 433 - ddl/schematracker/BUILD.bazel | 61 - ddl/schematracker/checker.go | 608 - ddl/stat.go | 87 - ddl/stat_test.go | 210 - ddl/syncer/BUILD.bazel | 54 - ddl/table.go | 1934 -- ddl/table_test.go | 580 - ddl/tests/adminpause/BUILD.bazel | 50 - ddl/tests/adminpause/main_test.go | 45 - ddl/tests/fail/BUILD.bazel | 34 - ddl/tests/fail/main_test.go | 45 - ddl/tests/fk/BUILD.bazel | 30 - ddl/tests/fk/foreign_key_test.go | 1837 -- ddl/tests/fk/main_test.go | 56 - ddl/tests/indexmerge/BUILD.bazel | 33 - ddl/tests/indexmerge/main_test.go | 56 - ddl/tests/metadatalock/BUILD.bazel | 23 - ddl/tests/metadatalock/main_test.go | 45 - ddl/tests/multivaluedindex/BUILD.bazel | 19 - ddl/tests/multivaluedindex/main_test.go | 35 - .../multi_valued_index_test.go | 47 - ddl/tests/partition/BUILD.bazel | 46 - ddl/tests/partition/main_test.go | 72 - ddl/tests/resourcegroup/BUILD.bazel | 24 - .../resourcegroup/resource_group_test.go | 466 - ddl/tests/serial/BUILD.bazel | 45 - ddl/tests/serial/main_test.go | 78 - ddl/tests/tiflash/BUILD.bazel | 42 - ddl/tests/tiflash/main_test.go | 49 - ddl/testutil/BUILD.bazel | 22 - ddl/testutil/testutil.go | 130 - ddl/ttl.go | 234 - ddl/util/BUILD.bazel | 41 - ddl/util/callback/BUILD.bazel | 28 - ddl/util/callback/callback.go | 173 - ddl/util/main_test.go | 33 - ddl/util/util.go | 364 - distsql/BUILD.bazel | 98 - distsql/bench_test.go | 74 - distsql/distsql.go | 261 - distsql/distsql_test.go | 435 - distsql/main_test.go | 33 - disttask/framework/BUILD.bazel | 31 - disttask/framework/dispatcher/BUILD.bazel | 59 - disttask/framework/dispatcher/dispatcher.go | 767 - .../framework/dispatcher/dispatcher_test.go | 560 - disttask/framework/dispatcher/interface.go | 133 - disttask/framework/dispatcher/main_test.go | 62 - .../framework_dynamic_dispatch_test.go | 111 - disttask/framework/framework_ha_test.go | 191 - .../framework_pause_and_resume_test.go | 83 - disttask/framework/handle/BUILD.bazel | 36 - disttask/framework/handle/handle.go | 188 - disttask/framework/handle/handle_test.go | 129 - disttask/framework/mock/BUILD.bazel | 18 - disttask/framework/mock/execute/BUILD.bazel | 12 - disttask/framework/planner/BUILD.bazel | 35 - disttask/framework/planner/plan.go | 115 - disttask/framework/planner/plan_test.go | 38 - disttask/framework/planner/planner.go | 46 - disttask/framework/planner/planner_test.go | 59 - disttask/framework/proto/BUILD.bazel | 17 - disttask/framework/scheduler/BUILD.bazel | 55 - .../framework/scheduler/execute/BUILD.bazel | 17 - .../framework/scheduler/execute/interface.go | 37 - .../framework/scheduler/execute/summary.go | 93 - disttask/framework/scheduler/interface.go | 102 - disttask/framework/scheduler/manager.go | 394 - disttask/framework/scheduler/manager_test.go | 248 - disttask/framework/scheduler/scheduler.go | 609 - disttask/framework/storage/BUILD.bazel | 46 - disttask/framework/storage/table_test.go | 575 - disttask/framework/storage/util.go | 64 - disttask/importinto/BUILD.bazel | 129 - disttask/importinto/dispatcher.go | 757 - disttask/importinto/dispatcher_test.go | 185 - disttask/importinto/job.go | 292 - disttask/importinto/metrics.go | 81 - disttask/importinto/mock/BUILD.bazel | 12 - disttask/importinto/planner.go | 506 - disttask/importinto/planner_test.go | 262 - disttask/importinto/scheduler.go | 519 - disttask/operator/BUILD.bazel | 27 - domain/BUILD.bazel | 166 - domain/db_test.go | 127 - domain/extract.go | 526 - domain/extract_test.go | 109 - domain/globalconfigsync/BUILD.bazel | 32 - domain/infosync/BUILD.bazel | 78 - domain/infosync/error.go | 28 - domain/infosync/region.go | 86 - domain/main_test.go | 36 - domain/metrics/BUILD.bazel | 12 - domain/metrics/metrics.go | 52 - domain/plan_replayer.go | 572 - domain/plan_replayer_test.go | 136 - domain/resourcegroup/BUILD.bazel | 18 - domain/resourcegroup/runaway.go | 568 - domain/runaway.go | 592 - dumpling/export/BUILD.bazel | 38 +- dumpling/export/block_allow_list_test.go | 4 +- dumpling/export/config.go | 6 +- dumpling/export/consistency_test.go | 4 +- dumpling/export/dump.go | 14 +- dumpling/export/dump_test.go | 4 +- dumpling/export/ir_impl_test.go | 2 +- dumpling/export/main_test.go | 2 +- dumpling/export/metrics.go | 2 +- dumpling/export/metrics_test.go | 2 +- dumpling/export/retry.go | 2 +- dumpling/export/sql.go | 8 +- dumpling/export/sql_test.go | 4 +- dumpling/export/writer_serial_test.go | 2 +- dumpling/export/writer_test.go | 2 +- errno/BUILD.bazel | 28 - errno/main_test.go | 27 - executor/BUILD.bazel | 478 - executor/admin.go | 890 - executor/aggfuncs/BUILD.bazel | 117 - executor/aggfuncs/builder.go | 727 - executor/aggfuncs/main_test.go | 33 - executor/aggregate/BUILD.bazel | 42 - executor/analyze.go | 721 - executor/analyze_test.go | 243 - executor/asyncloaddata/BUILD.bazel | 46 - executor/asyncloaddata/util.go | 595 - executor/asyncloaddata/util_test.go | 345 - executor/benchmark_test.go | 2188 -- executor/builder.go | 5481 ---- executor/checksum.go | 292 - executor/coprocessor.go | 304 - executor/cte_test.go | 507 - executor/ddl.go | 766 - executor/distsql.go | 1570 - executor/distsql_test.go | 700 - executor/executor.go | 2693 -- executor/executor_test.go | 169 - executor/explain.go | 361 - executor/foreign_key.go | 974 - executor/importer/BUILD.bazel | 136 - executor/importer/import.go | 1324 - executor/importer/job.go | 366 - executor/internal/BUILD.bazel | 9 - executor/internal/applycache/BUILD.bazel | 39 - executor/internal/applycache/main_test.go | 52 - executor/internal/builder/BUILD.bazel | 16 - .../internal/calibrateresource/BUILD.bazel | 55 - .../internal/calibrateresource/main_test.go | 52 - executor/internal/exec/BUILD.bazel | 23 - executor/internal/exec/executor.go | 308 - executor/internal/mpp/BUILD.bazel | 45 - executor/internal/pdhelper/BUILD.bazel | 38 - executor/internal/pdhelper/main_test.go | 52 - executor/internal/pdhelper/pd.go | 124 - executor/internal/querywatch/BUILD.bazel | 46 - executor/internal/querywatch/main_test.go | 52 - executor/internal/testkit.go | 31 - executor/internal/util/BUILD.bazel | 15 - executor/internal/vecgroupchecker/BUILD.bazel | 40 - .../internal/vecgroupchecker/main_test.go | 52 - executor/join.go | 1660 -- executor/join_test.go | 100 - executor/lockstats/BUILD.bazel | 36 - executor/main_test.go | 74 - executor/metrics/BUILD.bazel | 12 - executor/metrics/metrics.go | 257 - executor/mppcoordmanager/BUILD.bazel | 31 - executor/partition_table_test.go | 4296 --- executor/plan_replayer.go | 531 - executor/prepared_test.go | 1275 - executor/sample.go | 400 - executor/sample_test.go | 282 - executor/set.go | 331 - executor/set_test.go | 2171 -- executor/show_test.go | 66 - executor/simple_test.go | 163 - executor/sort.go | 550 - executor/split_test.go | 493 - executor/stmtsummary.go | 407 - executor/temporary_table_test.go | 158 - executor/test/admintest/BUILD.bazel | 39 - executor/test/admintest/main_test.go | 49 - executor/test/aggregate/BUILD.bazel | 26 - executor/test/aggregate/main_test.go | 45 - executor/test/analyzetest/BUILD.bazel | 37 - executor/test/analyzetest/analyze_test.go | 3216 -- executor/test/analyzetest/main_test.go | 34 - .../analyzetest/memorycontrol/BUILD.bazel | 24 - .../analyzetest/memorycontrol/main_test.go | 36 - executor/test/autoidtest/BUILD.bazel | 28 - executor/test/autoidtest/autoid_test.go | 808 - executor/test/autoidtest/main_test.go | 45 - executor/test/ddl/BUILD.bazel | 44 - executor/test/ddl/ddl_test.go | 1378 - executor/test/ddl/main_test.go | 44 - executor/test/distsqltest/BUILD.bazel | 22 - executor/test/distsqltest/distsql_test.go | 77 - executor/test/distsqltest/main_test.go | 44 - executor/test/executor/BUILD.bazel | 60 - executor/test/executor/executor_test.go | 4301 --- executor/test/executor/main_test.go | 49 - executor/test/fktest/BUILD.bazel | 35 - executor/test/fktest/foreign_key_test.go | 2539 -- executor/test/fktest/main_test.go | 45 - executor/test/indexmergereadtest/BUILD.bazel | 27 - executor/test/indexmergereadtest/main_test.go | 45 - executor/test/issuetest/BUILD.bazel | 29 - executor/test/issuetest/main_test.go | 45 - executor/test/jointest/BUILD.bazel | 25 - executor/test/jointest/hashjoin/BUILD.bazel | 26 - executor/test/jointest/hashjoin/main_test.go | 45 - executor/test/jointest/join_test.go | 1437 - executor/test/jointest/main_test.go | 45 - executor/test/kvtest/BUILD.bazel | 20 - executor/test/kvtest/main_test.go | 45 - executor/test/loaddatatest/BUILD.bazel | 26 - executor/test/loaddatatest/main_test.go | 49 - executor/test/loadremotetest/BUILD.bazel | 24 - executor/test/loadremotetest/error_test.go | 308 - executor/test/loadremotetest/util_test.go | 64 - executor/test/memtest/BUILD.bazel | 20 - executor/test/memtest/main_test.go | 45 - executor/test/oomtest/BUILD.bazel | 22 - executor/test/partitiontest/BUILD.bazel | 18 - executor/test/partitiontest/partition_test.go | 503 - executor/test/passwordtest/BUILD.bazel | 25 - executor/test/seqtest/BUILD.bazel | 49 - executor/test/seqtest/main_test.go | 39 - executor/test/seqtest/prepared_test.go | 697 - executor/test/showtest/BUILD.bazel | 34 - executor/test/showtest/main_test.go | 44 - executor/test/showtest/show_test.go | 1981 -- executor/test/simpletest/BUILD.bazel | 32 - executor/test/simpletest/simple_test.go | 1152 - executor/test/splittest/BUILD.bazel | 33 - executor/test/tiflashtest/BUILD.bazel | 36 - executor/test/tiflashtest/main_test.go | 49 - executor/test/unstabletest/BUILD.bazel | 25 - executor/test/unstabletest/main_test.go | 49 - executor/test/writetest/BUILD.bazel | 37 - executor/test/writetest/main_test.go | 49 - executor/trace.go | 412 - executor/trace_test.go | 88 - executor/update.go | 563 - executor/update_test.go | 644 - executor/write.go | 354 - expression/BUILD.bazel | 238 - expression/aggregation/BUILD.bazel | 77 - expression/aggregation/bench_test.go | 99 - expression/aggregation/explain.go | 67 - expression/aggregation/main_test.go | 32 - expression/aggregation/util.go | 95 - expression/aggregation/util_test.go | 45 - expression/bench_test.go | 2143 -- expression/collation_test.go | 765 - expression/column.go | 790 - expression/column_test.go | 265 - expression/constant.go | 528 - expression/errors.go | 117 - expression/explain.go | 192 - expression/expression_test.go | 292 - expression/extension.go | 209 - expression/generator/helper/BUILD.bazel | 8 - expression/generator/helper/helper.go | 49 - expression/helper.go | 191 - expression/helper_test.go | 183 - expression/integration_test/BUILD.bazel | 44 - .../integration_test/integration_test.go | 3273 -- expression/integration_test/main_test.go | 56 - expression/main_test.go | 66 - expression/schema.go | 294 - expression/test/multivaluedindex/BUILD.bazel | 30 - expression/test/multivaluedindex/main_test.go | 56 - .../multi_valued_index_test.go | 523 - expression/util.go | 1796 -- expression/util_test.go | 602 - extension/BUILD.bazel | 63 - extension/_import/BUILD.bazel | 8 - extension/bootstrap_test.go | 48 - extension/enterprise | 1 - extension/extensionimpl/BUILD.bazel | 17 - extension/extensionimpl/bootstrap.go | 95 - extension/main_test.go | 33 - extension/registry_test.go | 349 - extension/session.go | 150 - go.mod | 4 +- infoschema/BUILD.bazel | 99 - infoschema/builder.go | 1166 - infoschema/cache.go | 216 - infoschema/cluster.go | 160 - infoschema/error.go | 109 - infoschema/infoschema.go | 777 - infoschema/infoschema_test.go | 853 - infoschema/internal/BUILD.bazel | 9 - infoschema/main_test.go | 33 - infoschema/metrics/BUILD.bazel | 12 - infoschema/metrics/metrics.go | 58 - infoschema/perfschema/BUILD.bazel | 57 - infoschema/perfschema/main_test.go | 33 - infoschema/perfschema/tables.go | 414 - infoschema/perfschema/tables_test.go | 218 - infoschema/tables.go | 2488 -- infoschema/test/cachetest/BUILD.bazel | 18 - infoschema/test/cachetest/cache_test.go | 213 - infoschema/test/cachetest/main_test.go | 33 - infoschema/test/clustertablestest/BUILD.bazel | 58 - .../test/clustertablestest/main_test.go | 33 - .../test/clustertablestest/tables_test.go | 1419 - keyspace/BUILD.bazel | 27 - kv/BUILD.bazel | 100 - kv/error.go | 113 - kv/error_test.go | 43 - kv/main_test.go | 35 - kv/mpp.go | 283 - kv/txn.go | 248 - lock/BUILD.bazel | 16 - meta/BUILD.bazel | 45 - meta/autoid/BUILD.bazel | 70 - meta/autoid/autoid.go | 1364 - meta/autoid/autoid_test.go | 651 - meta/autoid/bench_test.go | 133 - meta/autoid/errors.go | 74 - meta/autoid/main_test.go | 33 - meta/main_test.go | 33 - metrics/BUILD.bazel | 66 - metrics/import.go | 35 - metrics/main_test.go | 33 - metrics/metrics.go | 326 - metrics/server.go | 387 - metrics/telemetry.go | 586 - owner/BUILD.bazel | 61 - owner/fail_test.go | 112 - owner/main_test.go | 33 - owner/manager.go | 471 - owner/manager_test.go | 311 - owner/mock.go | 221 - parser/BUILD.bazel | 65 - parser/ast/BUILD.bazel | 63 - parser/ast/ast.go | 258 - parser/ast/base.go | 135 - parser/ast/base_test.go | 42 - parser/ast/ddl.go | 4754 --- parser/ast/ddl_test.go | 920 - parser/ast/format_test.go | 98 - parser/ast/misc.go | 4090 --- parser/ast/misc_test.go | 426 - parser/ast/stats.go | 346 - parser/ast/util_test.go | 221 - parser/auth/BUILD.bazel | 36 - parser/charset/BUILD.bazel | 49 - parser/charset/charset.go | 655 - parser/duration/BUILD.bazel | 18 - parser/format/BUILD.bazel | 21 - parser/goyacc/BUILD.bazel | 26 - parser/model/BUILD.bazel | 41 - parser/model/ddl.go | 1000 - parser/model/ddl_test.go | 106 - parser/model/reorg.go | 139 - parser/mysql/BUILD.bazel | 38 - parser/opcode/BUILD.bazel | 17 - parser/parser.go | 24733 ---------------- parser/parser_test.go | 7472 ----- parser/terror/BUILD.bazel | 27 - parser/terror/terror.go | 337 - parser/test_driver/BUILD.bazel | 21 - parser/tidb/BUILD.bazel | 8 - parser/types/BUILD.bazel | 39 - parser/types/etc.go | 164 - parser/types/etc_test.go | 34 - parser/types/field_type.go | 696 - parser/types/field_type_test.go | 331 - pkg/autoid_service/BUILD.bazel | 46 + {autoid_service => pkg/autoid_service}/OWNERS | 0 pkg/autoid_service/autoid.go | 570 + pkg/autoid_service/autoid_test.go | 196 + pkg/bindinfo/BUILD.bazel | 83 + {bindinfo => pkg/bindinfo}/bind_cache.go | 10 +- {bindinfo => pkg/bindinfo}/bind_cache_test.go | 4 +- {bindinfo => pkg/bindinfo}/bind_record.go | 12 +- {bindinfo => pkg/bindinfo}/capture_test.go | 20 +- pkg/bindinfo/handle.go | 1288 + pkg/bindinfo/handle_test.go | 609 + pkg/bindinfo/internal/BUILD.bazel | 15 + pkg/bindinfo/internal/testutil.go | 40 + pkg/bindinfo/main_test.go | 33 + pkg/bindinfo/optimize_test.go | 38 + {bindinfo => pkg/bindinfo}/session_handle.go | 16 +- .../bindinfo}/session_handle_test.go | 16 +- pkg/bindinfo/stat.go | 40 + {bindinfo => pkg/bindinfo}/temptable_test.go | 4 +- pkg/bindinfo/tests/BUILD.bazel | 30 + {bindinfo => pkg/bindinfo}/tests/bind_test.go | 24 +- pkg/bindinfo/tests/main_test.go | 33 + pkg/config/BUILD.bazel | 51 + {config => pkg/config}/OWNERS | 0 pkg/config/config.go | 1554 + {config => pkg/config}/config.toml.example | 0 {config => pkg/config}/config_test.go | 2 +- {config => pkg/config}/config_util.go | 0 {config => pkg/config}/config_util_test.go | 0 {config => pkg/config}/const.go | 0 pkg/config/main_test.go | 34 + pkg/ddl/BUILD.bazel | 314 + {ddl => pkg/ddl}/attributes_sql_test.go | 20 +- {ddl => pkg/ddl}/backfilling.go | 38 +- {ddl => pkg/ddl}/backfilling_clean_s3.go | 8 +- {ddl => pkg/ddl}/backfilling_dispatcher.go | 26 +- .../ddl}/backfilling_dispatcher_test.go | 14 +- .../ddl}/backfilling_dist_scheduler.go | 14 +- {ddl => pkg/ddl}/backfilling_import_cloud.go | 10 +- {ddl => pkg/ddl}/backfilling_import_local.go | 10 +- {ddl => pkg/ddl}/backfilling_merge_sort.go | 12 +- {ddl => pkg/ddl}/backfilling_operators.go | 34 +- {ddl => pkg/ddl}/backfilling_read_index.go | 20 +- {ddl => pkg/ddl}/backfilling_scheduler.go | 36 +- {ddl => pkg/ddl}/backfilling_test.go | 10 +- pkg/ddl/callback.go | 234 + {ddl => pkg/ddl}/cancel_test.go | 18 +- pkg/ddl/cluster.go | 829 + pkg/ddl/cluster_test.go | 278 + pkg/ddl/column.go | 2029 ++ {ddl => pkg/ddl}/column_change_test.go | 28 +- {ddl => pkg/ddl}/column_modify_test.go | 32 +- pkg/ddl/column_test.go | 948 + {ddl => pkg/ddl}/column_type_change_test.go | 40 +- pkg/ddl/constant.go | 85 + pkg/ddl/constraint.go | 417 + {ddl => pkg/ddl}/constraint_test.go | 16 +- pkg/ddl/copr/BUILD.bazel | 33 + {ddl => pkg/ddl}/copr/copr_ctx.go | 10 +- {ddl => pkg/ddl}/copr/copr_ctx_test.go | 10 +- {ddl => pkg/ddl}/db_cache_test.go | 16 +- {ddl => pkg/ddl}/db_change_failpoints_test.go | 30 +- {ddl => pkg/ddl}/db_change_test.go | 34 +- {ddl => pkg/ddl}/db_integration_test.go | 52 +- {ddl => pkg/ddl}/db_rename_test.go | 12 +- {ddl => pkg/ddl}/db_table_test.go | 42 +- pkg/ddl/db_test.go | 1100 + pkg/ddl/ddl.go | 1863 ++ {ddl => pkg/ddl}/ddl_algorithm.go | 4 +- {ddl => pkg/ddl}/ddl_algorithm_test.go | 6 +- {ddl => pkg/ddl}/ddl_api.go | 78 +- {ddl => pkg/ddl}/ddl_api_test.go | 10 +- pkg/ddl/ddl_error_test.go | 182 + pkg/ddl/ddl_test.go | 287 + {ddl => pkg/ddl}/ddl_tiflash_api.go | 20 +- {ddl => pkg/ddl}/ddl_worker.go | 38 +- {ddl => pkg/ddl}/ddl_worker_test.go | 16 +- {ddl => pkg/ddl}/ddl_workerpool.go | 2 +- {ddl => pkg/ddl}/ddl_workerpool_test.go | 0 {ddl => pkg/ddl}/delete_range.go | 20 +- {ddl => pkg/ddl}/delete_range_util.go | 0 {ddl => pkg/ddl}/dist_owner.go | 0 {ddl => pkg/ddl}/export_test.go | 16 +- pkg/ddl/fail_test.go | 65 + pkg/ddl/foreign_key.go | 729 + pkg/ddl/foreign_key_test.go | 432 + {ddl => pkg/ddl}/generated_column.go | 18 +- pkg/ddl/index.go | 2515 ++ {ddl => pkg/ddl}/index_change_test.go | 20 +- {ddl => pkg/ddl}/index_cop.go | 46 +- {ddl => pkg/ddl}/index_cop_test.go | 16 +- {ddl => pkg/ddl}/index_merge_tmp.go | 16 +- {ddl => pkg/ddl}/index_modify_test.go | 36 +- pkg/ddl/ingest/BUILD.bazel | 87 + {ddl => pkg/ddl}/ingest/backend.go | 12 +- {ddl => pkg/ddl}/ingest/backend_mgr.go | 8 +- {ddl => pkg/ddl}/ingest/checkpoint.go | 12 +- {ddl => pkg/ddl}/ingest/checkpoint_test.go | 6 +- pkg/ddl/ingest/config.go | 164 + {ddl => pkg/ddl}/ingest/disk_root.go | 6 +- {ddl => pkg/ddl}/ingest/engine.go | 6 +- {ddl => pkg/ddl}/ingest/engine_mgr.go | 4 +- {ddl => pkg/ddl}/ingest/env.go | 10 +- {ddl => pkg/ddl}/ingest/env_test.go | 4 +- pkg/ddl/ingest/integration_test.go | 311 + {ddl => pkg/ddl}/ingest/main_test.go | 0 {ddl => pkg/ddl}/ingest/mem_root.go | 0 {ddl => pkg/ddl}/ingest/mem_root_test.go | 2 +- {ddl => pkg/ddl}/ingest/message.go | 4 +- pkg/ddl/ingest/mock.go | 234 + pkg/ddl/ingest/tests/BUILD.bazel | 17 + pkg/ddl/ingest/tests/partition_table_test.go | 74 + pkg/ddl/ingest/testutil/BUILD.bazel | 14 + pkg/ddl/ingest/testutil/testutil.go | 43 + pkg/ddl/integration_test.go | 60 + pkg/ddl/internal/session/BUILD.bazel | 41 + pkg/ddl/internal/session/session.go | 137 + .../ddl}/internal/session/session_pool.go | 12 +- .../internal/session/session_pool_test.go | 4 +- {ddl => pkg/ddl}/job_table.go | 30 +- {ddl => pkg/ddl}/job_table_test.go | 20 +- pkg/ddl/label/BUILD.bazel | 37 + {ddl => pkg/ddl}/label/attributes.go | 0 {ddl => pkg/ddl}/label/attributes_test.go | 0 {ddl => pkg/ddl}/label/errors.go | 0 pkg/ddl/label/main_test.go | 33 + pkg/ddl/label/rule.go | 172 + {ddl => pkg/ddl}/label/rule_test.go | 2 +- pkg/ddl/main_test.go | 74 + pkg/ddl/mock.go | 250 + {ddl => pkg/ddl}/modify_column_test.go | 42 +- {ddl => pkg/ddl}/multi_schema_change.go | 14 +- {ddl => pkg/ddl}/multi_schema_change_test.go | 22 +- {ddl => pkg/ddl}/mv_index_test.go | 8 +- pkg/ddl/options.go | 70 + {ddl => pkg/ddl}/options_test.go | 6 +- pkg/ddl/partition.go | 4253 +++ pkg/ddl/partition_test.go | 248 + pkg/ddl/placement/BUILD.bazel | 49 + {ddl => pkg/ddl}/placement/bundle.go | 6 +- {ddl => pkg/ddl}/placement/bundle_test.go | 10 +- {ddl => pkg/ddl}/placement/common.go | 0 {ddl => pkg/ddl}/placement/common_test.go | 0 {ddl => pkg/ddl}/placement/constraint.go | 0 {ddl => pkg/ddl}/placement/constraint_test.go | 0 {ddl => pkg/ddl}/placement/constraints.go | 0 .../ddl}/placement/constraints_test.go | 0 {ddl => pkg/ddl}/placement/errors.go | 0 .../ddl}/placement/meta_bundle_test.go | 14 +- pkg/ddl/placement/rule.go | 263 + {ddl => pkg/ddl}/placement/rule_test.go | 0 {ddl => pkg/ddl}/placement_policy.go | 12 +- {ddl => pkg/ddl}/placement_policy_ddl_test.go | 16 +- {ddl => pkg/ddl}/placement_policy_test.go | 66 +- {ddl => pkg/ddl}/placement_sql_test.go | 18 +- {ddl => pkg/ddl}/primary_key_handle_test.go | 20 +- pkg/ddl/reorg.go | 896 + {ddl => pkg/ddl}/reorg_partition_test.go | 30 +- {ddl => pkg/ddl}/repair_table_test.go | 24 +- {ddl => pkg/ddl}/resource_group.go | 16 +- pkg/ddl/resourcegroup/BUILD.bazel | 16 + {ddl => pkg/ddl}/resourcegroup/errors.go | 0 pkg/ddl/resourcegroup/group.go | 83 + {ddl => pkg/ddl}/restart_test.go | 12 +- {ddl => pkg/ddl}/rollingback.go | 16 +- {ddl => pkg/ddl}/rollingback_test.go | 16 +- {ddl => pkg/ddl}/sanity_check.go | 20 +- pkg/ddl/schema.go | 367 + pkg/ddl/schema_test.go | 433 + pkg/ddl/schematracker/BUILD.bazel | 61 + pkg/ddl/schematracker/checker.go | 608 + {ddl => pkg/ddl}/schematracker/dm_tracker.go | 36 +- .../ddl}/schematracker/dm_tracker_test.go | 22 +- {ddl => pkg/ddl}/schematracker/info_store.go | 8 +- .../ddl}/schematracker/info_store_test.go | 4 +- {ddl => pkg/ddl}/sequence.go | 14 +- {ddl => pkg/ddl}/sequence_test.go | 14 +- {ddl => pkg/ddl}/split_region.go | 16 +- pkg/ddl/stat.go | 87 + pkg/ddl/stat_test.go | 210 + pkg/ddl/syncer/BUILD.bazel | 54 + {ddl => pkg/ddl}/syncer/state_syncer.go | 8 +- {ddl => pkg/ddl}/syncer/state_syncer_test.go | 14 +- {ddl => pkg/ddl}/syncer/syncer.go | 12 +- {ddl => pkg/ddl}/syncer/syncer_test.go | 16 +- pkg/ddl/table.go | 1934 ++ {ddl => pkg/ddl}/table_lock.go | 8 +- {ddl => pkg/ddl}/table_modify_test.go | 22 +- {ddl => pkg/ddl}/table_split_test.go | 12 +- pkg/ddl/table_test.go | 580 + pkg/ddl/tests/adminpause/BUILD.bazel | 50 + .../tests/adminpause/ddl_data_generation.go | 2 +- .../ddl}/tests/adminpause/ddl_stmt_cases.go | 4 +- {ddl => pkg/ddl}/tests/adminpause/global.go | 8 +- pkg/ddl/tests/adminpause/main_test.go | 45 + .../tests/adminpause/pause_cancel_test.go | 14 +- .../tests/adminpause/pause_negative_test.go | 20 +- .../tests/adminpause/pause_resume_test.go | 14 +- pkg/ddl/tests/fail/BUILD.bazel | 34 + {ddl => pkg/ddl}/tests/fail/fail_db_test.go | 74 +- pkg/ddl/tests/fail/main_test.go | 45 + pkg/ddl/tests/fk/BUILD.bazel | 30 + pkg/ddl/tests/fk/foreign_key_test.go | 1837 ++ pkg/ddl/tests/fk/main_test.go | 56 + pkg/ddl/tests/indexmerge/BUILD.bazel | 33 + pkg/ddl/tests/indexmerge/main_test.go | 56 + .../ddl}/tests/indexmerge/merge_test.go | 50 +- pkg/ddl/tests/metadatalock/BUILD.bazel | 23 + pkg/ddl/tests/metadatalock/main_test.go | 45 + .../ddl}/tests/metadatalock/mdl_test.go | 10 +- pkg/ddl/tests/multivaluedindex/BUILD.bazel | 19 + pkg/ddl/tests/multivaluedindex/main_test.go | 35 + .../multi_valued_index_test.go | 47 + pkg/ddl/tests/partition/BUILD.bazel | 46 + .../ddl}/tests/partition/db_partition_test.go | 74 +- pkg/ddl/tests/partition/main_test.go | 72 + pkg/ddl/tests/resourcegroup/BUILD.bazel | 24 + .../resourcegroup/resource_group_test.go | 466 + pkg/ddl/tests/serial/BUILD.bazel | 45 + pkg/ddl/tests/serial/main_test.go | 78 + {ddl => pkg/ddl}/tests/serial/serial_test.go | 70 +- pkg/ddl/tests/tiflash/BUILD.bazel | 42 + .../ddl}/tests/tiflash/ddl_tiflash_test.go | 112 +- pkg/ddl/tests/tiflash/main_test.go | 49 + pkg/ddl/testutil/BUILD.bazel | 22 + pkg/ddl/testutil/testutil.go | 130 + {ddl => pkg/ddl}/tiflash_replica_test.go | 50 +- pkg/ddl/ttl.go | 234 + {ddl => pkg/ddl}/ttl_test.go | 4 +- pkg/ddl/util/BUILD.bazel | 41 + pkg/ddl/util/callback/BUILD.bazel | 28 + pkg/ddl/util/callback/callback.go | 173 + .../ddl}/util/callback/callback_test.go | 2 +- .../ddl}/util/dead_table_lock_checker.go | 4 +- {ddl => pkg/ddl}/util/event.go | 2 +- pkg/ddl/util/main_test.go | 33 + {ddl => pkg/ddl}/util/mock.go | 0 pkg/ddl/util/util.go | 364 + pkg/distsql/BUILD.bazel | 98 + {distsql => pkg/distsql}/OWNERS | 0 pkg/distsql/bench_test.go | 74 + pkg/distsql/distsql.go | 261 + pkg/distsql/distsql_test.go | 435 + pkg/distsql/main_test.go | 33 + {distsql => pkg/distsql}/request_builder.go | 24 +- .../distsql}/request_builder_test.go | 22 +- {distsql => pkg/distsql}/select_result.go | 36 +- .../distsql}/select_result_test.go | 10 +- pkg/disttask/framework/BUILD.bazel | 31 + pkg/disttask/framework/dispatcher/BUILD.bazel | 59 + .../framework/dispatcher/dispatcher.go | 767 + .../dispatcher/dispatcher_manager.go | 16 +- .../framework/dispatcher/dispatcher_test.go | 560 + .../framework/dispatcher/interface.go | 133 + .../framework/dispatcher/main_test.go | 62 + .../framework_dynamic_dispatch_test.go | 111 + .../framework/framework_err_handling_test.go | 8 +- pkg/disttask/framework/framework_ha_test.go | 191 + .../framework_pause_and_resume_test.go | 83 + .../framework/framework_rollback_test.go | 16 +- .../disttask}/framework/framework_test.go | 82 +- pkg/disttask/framework/handle/BUILD.bazel | 36 + pkg/disttask/framework/handle/handle.go | 188 + pkg/disttask/framework/handle/handle_test.go | 129 + pkg/disttask/framework/mock/BUILD.bazel | 18 + .../framework/mock/dispatcher_mock.go | 4 +- .../framework/mock/execute/BUILD.bazel | 12 + .../framework/mock/execute/execute_mock.go | 4 +- .../disttask}/framework/mock/plan_mock.go | 4 +- .../framework/mock/scheduler_mock.go | 6 +- pkg/disttask/framework/planner/BUILD.bazel | 35 + pkg/disttask/framework/planner/plan.go | 115 + pkg/disttask/framework/planner/plan_test.go | 38 + pkg/disttask/framework/planner/planner.go | 46 + .../framework/planner/planner_test.go | 59 + pkg/disttask/framework/proto/BUILD.bazel | 17 + .../disttask}/framework/proto/task.go | 0 .../disttask}/framework/proto/task_test.go | 0 pkg/disttask/framework/scheduler/BUILD.bazel | 55 + .../framework/scheduler/execute/BUILD.bazel | 17 + .../framework/scheduler/execute/interface.go | 37 + .../framework/scheduler/execute/summary.go | 93 + pkg/disttask/framework/scheduler/interface.go | 102 + pkg/disttask/framework/scheduler/manager.go | 394 + .../framework/scheduler/manager_test.go | 248 + .../disttask}/framework/scheduler/register.go | 4 +- .../framework/scheduler/register_test.go | 2 +- pkg/disttask/framework/scheduler/scheduler.go | 609 + .../framework/scheduler/scheduler_test.go | 6 +- pkg/disttask/framework/storage/BUILD.bazel | 46 + pkg/disttask/framework/storage/table_test.go | 575 + .../disttask}/framework/storage/task_table.go | 16 +- pkg/disttask/framework/storage/util.go | 64 + pkg/disttask/importinto/BUILD.bazel | 129 + .../disttask}/importinto/clean_s3.go | 6 +- pkg/disttask/importinto/dispatcher.go | 757 + pkg/disttask/importinto/dispatcher_test.go | 185 + .../importinto/dispatcher_testkit_test.go | 26 +- .../importinto/encode_and_sort_operator.go | 14 +- .../encode_and_sort_operator_test.go | 16 +- pkg/disttask/importinto/job.go | 292 + .../disttask}/importinto/job_testkit_test.go | 10 +- pkg/disttask/importinto/metrics.go | 81 + .../disttask}/importinto/metrics_test.go | 0 pkg/disttask/importinto/mock/BUILD.bazel | 12 + .../disttask}/importinto/mock/import_mock.go | 2 +- pkg/disttask/importinto/planner.go | 506 + pkg/disttask/importinto/planner_test.go | 262 + .../disttask}/importinto/proto.go | 8 +- pkg/disttask/importinto/scheduler.go | 519 + .../disttask}/importinto/subtask_executor.go | 18 +- .../importinto/subtask_executor_test.go | 16 +- .../disttask}/importinto/wrapper.go | 0 .../disttask}/importinto/wrapper_test.go | 0 pkg/disttask/operator/BUILD.bazel | 27 + .../disttask}/operator/compose.go | 0 .../disttask}/operator/operator.go | 4 +- .../disttask}/operator/pipeline.go | 0 .../disttask}/operator/pipeline_test.go | 0 .../disttask}/operator/wrapper.go | 0 pkg/domain/BUILD.bazel | 166 + pkg/domain/db_test.go | 127 + {domain => pkg/domain}/domain.go | 102 +- {domain => pkg/domain}/domain_sysvars.go | 2 +- {domain => pkg/domain}/domain_test.go | 52 +- {domain => pkg/domain}/domain_utils_test.go | 4 +- {domain => pkg/domain}/domainctx.go | 2 +- {domain => pkg/domain}/domainctx_test.go | 2 +- pkg/domain/extract.go | 526 + pkg/domain/extract_test.go | 109 + pkg/domain/globalconfigsync/BUILD.bazel | 32 + .../domain}/globalconfigsync/globalconfig.go | 2 +- .../globalconfigsync/globalconfig_test.go | 10 +- {domain => pkg/domain}/historical_stats.go | 10 +- pkg/domain/infosync/BUILD.bazel | 78 + pkg/domain/infosync/error.go | 28 + {domain => pkg/domain}/infosync/info.go | 40 +- {domain => pkg/domain}/infosync/info_test.go | 28 +- .../domain}/infosync/label_manager.go | 4 +- {domain => pkg/domain}/infosync/mock_info.go | 8 +- .../domain}/infosync/placement_manager.go | 4 +- pkg/domain/infosync/region.go | 86 + .../infosync/resource_manager_client.go | 2 +- .../domain}/infosync/schedule_manager.go | 2 +- .../domain}/infosync/tiflash_manager.go | 14 +- pkg/domain/main_test.go | 36 + pkg/domain/metrics/BUILD.bazel | 12 + pkg/domain/metrics/metrics.go | 52 + {domain => pkg/domain}/optimize_trace.go | 0 pkg/domain/plan_replayer.go | 572 + {domain => pkg/domain}/plan_replayer_dump.go | 26 +- .../domain}/plan_replayer_handle_test.go | 4 +- pkg/domain/plan_replayer_test.go | 136 + pkg/domain/resourcegroup/BUILD.bazel | 18 + pkg/domain/resourcegroup/runaway.go | 568 + pkg/domain/runaway.go | 592 + {domain => pkg/domain}/schema_checker.go | 2 +- {domain => pkg/domain}/schema_checker_test.go | 2 +- {domain => pkg/domain}/schema_validator.go | 10 +- .../domain}/schema_validator_test.go | 4 +- {domain => pkg/domain}/session_pool_test.go | 0 {domain => pkg/domain}/sysvar_cache.go | 12 +- {domain => pkg/domain}/test_helper.go | 4 +- {domain => pkg/domain}/topn_slow_query.go | 4 +- .../domain}/topn_slow_query_test.go | 0 pkg/errno/BUILD.bazel | 28 + {errno => pkg/errno}/errcode.go | 0 {errno => pkg/errno}/errname.go | 2 +- {errno => pkg/errno}/infoschema.go | 0 {errno => pkg/errno}/infoschema_test.go | 0 {errno => pkg/errno}/logredaction.md | 0 pkg/errno/main_test.go | 27 + pkg/executor/BUILD.bazel | 478 + {executor => pkg/executor}/adapter.go | 84 +- {executor => pkg/executor}/adapter_test.go | 6 +- pkg/executor/admin.go | 890 + {executor => pkg/executor}/admin_plugins.go | 10 +- {executor => pkg/executor}/admin_telemetry.go | 8 +- pkg/executor/aggfuncs/BUILD.bazel | 117 + .../executor}/aggfuncs/aggfunc_test.go | 28 +- .../executor}/aggfuncs/aggfuncs.go | 8 +- pkg/executor/aggfuncs/builder.go | 727 + .../executor}/aggfuncs/export_test.go | 0 .../executor}/aggfuncs/func_avg.go | 12 +- .../executor}/aggfuncs/func_avg_test.go | 10 +- .../executor}/aggfuncs/func_bitfuncs.go | 4 +- .../executor}/aggfuncs/func_bitfuncs_test.go | 6 +- .../executor}/aggfuncs/func_count.go | 4 +- .../executor}/aggfuncs/func_count_distinct.go | 20 +- .../executor}/aggfuncs/func_count_test.go | 14 +- .../executor}/aggfuncs/func_cume_dist.go | 4 +- .../executor}/aggfuncs/func_cume_dist_test.go | 6 +- .../executor}/aggfuncs/func_first_row.go | 10 +- .../executor}/aggfuncs/func_first_row_test.go | 10 +- .../executor}/aggfuncs/func_group_concat.go | 18 +- .../aggfuncs/func_group_concat_test.go | 22 +- .../executor}/aggfuncs/func_json_arrayagg.go | 6 +- .../aggfuncs/func_json_arrayagg_test.go | 10 +- .../executor}/aggfuncs/func_json_objectagg.go | 12 +- .../aggfuncs/func_json_objectagg_test.go | 18 +- .../executor}/aggfuncs/func_lead_lag.go | 6 +- .../executor}/aggfuncs/func_lead_lag_test.go | 10 +- .../executor}/aggfuncs/func_max_min.go | 12 +- .../executor}/aggfuncs/func_max_min_test.go | 12 +- .../executor}/aggfuncs/func_ntile.go | 4 +- .../executor}/aggfuncs/func_ntile_test.go | 6 +- .../executor}/aggfuncs/func_percent_rank.go | 4 +- .../aggfuncs/func_percent_rank_test.go | 6 +- .../executor}/aggfuncs/func_percentile.go | 8 +- .../aggfuncs/func_percentile_test.go | 8 +- .../executor}/aggfuncs/func_rank.go | 6 +- .../executor}/aggfuncs/func_rank_test.go | 6 +- .../executor}/aggfuncs/func_stddevpop.go | 4 +- .../executor}/aggfuncs/func_stddevpop_test.go | 4 +- .../executor}/aggfuncs/func_stddevsamp.go | 4 +- .../aggfuncs/func_stddevsamp_test.go | 4 +- .../executor}/aggfuncs/func_sum.go | 12 +- .../executor}/aggfuncs/func_sum_test.go | 10 +- .../executor}/aggfuncs/func_value.go | 10 +- .../executor}/aggfuncs/func_value_test.go | 10 +- .../executor}/aggfuncs/func_varpop.go | 6 +- .../executor}/aggfuncs/func_varpop_test.go | 10 +- .../executor}/aggfuncs/func_varsamp.go | 4 +- .../executor}/aggfuncs/func_varsamp_test.go | 4 +- pkg/executor/aggfuncs/main_test.go | 33 + .../executor}/aggfuncs/row_number.go | 4 +- .../executor}/aggfuncs/row_number_test.go | 6 +- .../executor}/aggfuncs/window_func_test.go | 18 +- pkg/executor/aggregate/BUILD.bazel | 42 + .../aggregate/agg_hash_base_worker.go | 10 +- .../executor}/aggregate/agg_hash_executor.go | 26 +- .../aggregate/agg_hash_final_worker.go | 12 +- .../aggregate/agg_hash_partial_worker.go | 8 +- .../executor}/aggregate/agg_spill.go | 4 +- .../aggregate/agg_stream_executor.go | 10 +- .../executor}/aggregate/agg_util.go | 24 +- pkg/executor/analyze.go | 721 + {executor => pkg/executor}/analyze_col.go | 30 +- {executor => pkg/executor}/analyze_col_v2.go | 42 +- .../executor}/analyze_global_stats.go | 12 +- {executor => pkg/executor}/analyze_idx.go | 24 +- pkg/executor/analyze_test.go | 243 + {executor => pkg/executor}/analyze_utils.go | 10 +- .../executor}/analyze_utils_test.go | 2 +- {executor => pkg/executor}/analyze_worker.go | 10 +- pkg/executor/asyncloaddata/BUILD.bazel | 46 + .../executor}/asyncloaddata/main_test.go | 0 .../executor}/asyncloaddata/progress.go | 0 .../executor}/asyncloaddata/progress_test.go | 0 pkg/executor/asyncloaddata/util.go | 595 + pkg/executor/asyncloaddata/util_test.go | 345 + {executor => pkg/executor}/batch_checker.go | 28 +- {executor => pkg/executor}/batch_point_get.go | 34 +- .../executor}/batch_point_get_test.go | 12 +- pkg/executor/benchmark_test.go | 2188 ++ {executor => pkg/executor}/bind.go | 12 +- {executor => pkg/executor}/brie.go | 44 +- {executor => pkg/executor}/brie_test.go | 20 +- pkg/executor/builder.go | 5481 ++++ {executor => pkg/executor}/change.go | 10 +- {executor => pkg/executor}/charset_test.go | 6 +- pkg/executor/checksum.go | 292 + .../executor}/chunk_size_control_test.go | 18 +- .../executor}/cluster_table_test.go | 18 +- {executor => pkg/executor}/compact_table.go | 14 +- .../executor}/compact_table_test.go | 8 +- {executor => pkg/executor}/compiler.go | 24 +- {executor => pkg/executor}/concurrent_map.go | 4 +- .../executor}/concurrent_map_test.go | 4 +- {executor => pkg/executor}/copr_cache_test.go | 14 +- pkg/executor/coprocessor.go | 304 + {executor => pkg/executor}/cte.go | 20 +- .../executor}/cte_table_reader.go | 6 +- pkg/executor/cte_test.go | 507 + pkg/executor/ddl.go | 766 + {executor => pkg/executor}/delete.go | 26 +- {executor => pkg/executor}/delete_test.go | 4 +- pkg/executor/distsql.go | 1570 + pkg/executor/distsql_test.go | 700 + pkg/executor/executor.go | 2693 ++ .../executor}/executor_failpoint_test.go | 62 +- .../executor}/executor_pkg_test.go | 32 +- .../executor}/executor_required_rows_test.go | 32 +- pkg/executor/executor_test.go | 169 + .../executor}/executor_txn_test.go | 6 +- pkg/executor/explain.go | 361 + {executor => pkg/executor}/explain_test.go | 14 +- .../executor}/explain_unit_test.go | 14 +- {executor => pkg/executor}/explainfor_test.go | 10 +- pkg/executor/foreign_key.go | 974 + {executor => pkg/executor}/grant.go | 36 +- {executor => pkg/executor}/grant_test.go | 14 +- {executor => pkg/executor}/hash_table.go | 20 +- {executor => pkg/executor}/hash_table_test.go | 12 +- .../executor}/historical_stats_test.go | 34 +- .../hot_regions_history_table_test.go | 18 +- {executor => pkg/executor}/import_into.go | 38 +- .../executor}/import_into_test.go | 8 +- pkg/executor/importer/BUILD.bazel | 136 + .../executor}/importer/chunk_process.go | 6 +- .../executor}/importer/chunk_process_test.go | 2 +- .../importer/chunk_process_testkit_test.go | 10 +- .../executor}/importer/engine_process.go | 2 +- pkg/executor/importer/import.go | 1324 + .../executor}/importer/import_test.go | 20 +- pkg/executor/importer/job.go | 366 + .../executor}/importer/job_test.go | 10 +- .../executor}/importer/kv_encode.go | 16 +- .../executor}/importer/precheck.go | 14 +- .../executor}/importer/precheck_test.go | 16 +- .../executor}/importer/table_import.go | 12 +- .../executor}/importer/table_import_test.go | 2 +- {executor => pkg/executor}/index_advise.go | 14 +- .../executor}/index_advise_test.go | 6 +- .../executor}/index_lookup_hash_join.go | 20 +- .../executor}/index_lookup_join.go | 32 +- .../executor}/index_lookup_join_test.go | 10 +- .../executor}/index_lookup_merge_join.go | 28 +- .../executor}/index_lookup_merge_join_test.go | 16 +- .../executor}/index_merge_reader.go | 42 +- .../infoschema_cluster_table_test.go | 30 +- .../executor}/infoschema_reader.go | 96 +- .../infoschema_reader_internal_test.go | 4 +- .../executor}/infoschema_reader_test.go | 22 +- {executor => pkg/executor}/insert.go | 32 +- {executor => pkg/executor}/insert_common.go | 48 +- {executor => pkg/executor}/insert_test.go | 18 +- .../executor}/inspection_common.go | 6 +- .../executor}/inspection_common_test.go | 6 +- .../executor}/inspection_profile.go | 8 +- .../executor}/inspection_result.go | 22 +- .../executor}/inspection_result_test.go | 18 +- .../executor}/inspection_summary.go | 16 +- .../executor}/inspection_summary_test.go | 14 +- pkg/executor/internal/BUILD.bazel | 9 + pkg/executor/internal/applycache/BUILD.bazel | 39 + .../internal/applycache/apply_cache.go | 12 +- .../internal/applycache/apply_cache_test.go | 8 +- pkg/executor/internal/applycache/main_test.go | 52 + pkg/executor/internal/builder/BUILD.bazel | 16 + .../internal/builder/builder_utils.go | 10 +- .../internal/calibrateresource/BUILD.bazel | 55 + .../calibrateresource/calibrate_resource.go | 26 +- .../calibrate_resource_test.go | 14 +- .../internal/calibrateresource/main_test.go | 52 + pkg/executor/internal/exec/BUILD.bazel | 23 + pkg/executor/internal/exec/executor.go | 308 + pkg/executor/internal/mpp/BUILD.bazel | 45 + .../internal/mpp/local_mpp_coordinator.go | 32 +- .../mpp/local_mpp_coordinator_test.go | 2 +- pkg/executor/internal/pdhelper/BUILD.bazel | 38 + pkg/executor/internal/pdhelper/main_test.go | 52 + pkg/executor/internal/pdhelper/pd.go | 124 + .../executor}/internal/pdhelper/pd_test.go | 2 +- pkg/executor/internal/querywatch/BUILD.bazel | 46 + pkg/executor/internal/querywatch/main_test.go | 52 + .../internal/querywatch/query_watch.go | 22 +- .../internal/querywatch/query_watch_test.go | 6 +- pkg/executor/internal/testkit.go | 31 + pkg/executor/internal/util/BUILD.bazel | 15 + .../internal/util/partition_table.go | 0 .../executor}/internal/util/util.go | 0 .../internal/vecgroupchecker/BUILD.bazel | 40 + .../internal/vecgroupchecker/main_test.go | 52 + .../vecgroupchecker/vec_group_checker.go | 10 +- .../vecgroupchecker/vec_group_checker_test.go | 10 +- pkg/executor/join.go | 1660 ++ {executor => pkg/executor}/join_pkg_test.go | 12 +- pkg/executor/join_test.go | 100 + {executor => pkg/executor}/joiner.go | 12 +- {executor => pkg/executor}/joiner_test.go | 8 +- {executor => pkg/executor}/load_data.go | 36 +- {executor => pkg/executor}/load_stats.go | 12 +- pkg/executor/lockstats/BUILD.bazel | 36 + .../lockstats/lock_stats_executor.go | 16 +- .../lockstats/lock_stats_executor_test.go | 6 +- .../lockstats/unlock_stats_executor.go | 8 +- pkg/executor/main_test.go | 74 + {executor => pkg/executor}/mem_reader.go | 32 +- {executor => pkg/executor}/memtable_reader.go | 34 +- .../executor}/memtable_reader_test.go | 14 +- {executor => pkg/executor}/merge_join.go | 16 +- {executor => pkg/executor}/merge_join_test.go | 16 +- pkg/executor/metrics/BUILD.bazel | 12 + pkg/executor/metrics/metrics.go | 257 + {executor => pkg/executor}/metrics_reader.go | 20 +- .../executor}/metrics_reader_test.go | 10 +- {executor => pkg/executor}/mpp_gather.go | 26 +- pkg/executor/mppcoordmanager/BUILD.bazel | 31 + .../mpp_coordinator_manager.go | 8 +- .../mpp_coordinator_manager_test.go | 4 +- .../executor}/opt_rule_blacklist.go | 14 +- {executor => pkg/executor}/parallel_apply.go | 18 +- .../executor}/parallel_apply_test.go | 12 +- pkg/executor/partition_table_test.go | 4296 +++ .../executor}/pipelined_window.go | 18 +- {executor => pkg/executor}/pkg_test.go | 16 +- pkg/executor/plan_replayer.go | 531 + {executor => pkg/executor}/point_get.go | 38 +- {executor => pkg/executor}/point_get_test.go | 28 +- {executor => pkg/executor}/prepared.go | 30 +- pkg/executor/prepared_test.go | 1275 + {executor => pkg/executor}/projection.go | 14 +- {executor => pkg/executor}/recover_test.go | 62 +- .../reload_expr_pushdown_blacklist.go | 14 +- {executor => pkg/executor}/replace.go | 16 +- .../executor}/resource_tag_test.go | 20 +- {executor => pkg/executor}/revoke.go | 30 +- {executor => pkg/executor}/revoke_test.go | 10 +- {executor => pkg/executor}/rowid_test.go | 4 +- pkg/executor/sample.go | 400 + pkg/executor/sample_test.go | 282 + {executor => pkg/executor}/select_into.go | 12 +- .../executor}/select_into_test.go | 8 +- pkg/executor/set.go | 331 + {executor => pkg/executor}/set_config.go | 24 +- pkg/executor/set_test.go | 2171 ++ {executor => pkg/executor}/show.go | 104 +- {executor => pkg/executor}/show_placement.go | 26 +- .../executor}/show_placement_labels_test.go | 4 +- .../executor}/show_placement_test.go | 8 +- {executor => pkg/executor}/show_stats.go | 16 +- {executor => pkg/executor}/show_stats_test.go | 8 +- pkg/executor/show_test.go | 66 + {executor => pkg/executor}/shuffle.go | 18 +- {executor => pkg/executor}/shuffle_test.go | 10 +- {executor => pkg/executor}/simple.go | 76 +- pkg/executor/simple_test.go | 163 + {executor => pkg/executor}/slow_query.go | 34 +- .../executor}/slow_query_sql_test.go | 14 +- {executor => pkg/executor}/slow_query_test.go | 30 +- pkg/executor/sort.go | 550 + {executor => pkg/executor}/sort_test.go | 20 +- {executor => pkg/executor}/split.go | 30 +- pkg/executor/split_test.go | 493 + {executor => pkg/executor}/stale_txn_test.go | 86 +- .../executor}/statement_context_test.go | 6 +- pkg/executor/stmtsummary.go | 407 + .../executor}/stmtsummary_test.go | 12 +- {executor => pkg/executor}/table_reader.go | 44 +- .../table_readers_required_rows_test.go | 26 +- pkg/executor/temporary_table_test.go | 158 + pkg/executor/test/admintest/BUILD.bazel | 39 + .../executor}/test/admintest/admin_test.go | 34 +- pkg/executor/test/admintest/main_test.go | 49 + pkg/executor/test/aggregate/BUILD.bazel | 26 + .../test/aggregate/aggregate_test.go | 16 +- pkg/executor/test/aggregate/main_test.go | 45 + .../test/aggregate/testdata/agg_suite_in.json | 0 .../aggregate/testdata/agg_suite_out.json | 0 pkg/executor/test/analyzetest/BUILD.bazel | 37 + .../test/analyzetest/analyze_bench_test.go | 2 +- pkg/executor/test/analyzetest/analyze_test.go | 3216 ++ pkg/executor/test/analyzetest/main_test.go | 34 + .../analyzetest/memorycontrol/BUILD.bazel | 24 + .../analyzetest/memorycontrol/main_test.go | 36 + .../memorycontrol/memory_control_test.go | 36 +- pkg/executor/test/autoidtest/BUILD.bazel | 28 + pkg/executor/test/autoidtest/autoid_test.go | 808 + pkg/executor/test/autoidtest/main_test.go | 45 + pkg/executor/test/ddl/BUILD.bazel | 44 + pkg/executor/test/ddl/ddl_test.go | 1378 + pkg/executor/test/ddl/main_test.go | 44 + pkg/executor/test/distsqltest/BUILD.bazel | 22 + pkg/executor/test/distsqltest/distsql_test.go | 77 + pkg/executor/test/distsqltest/main_test.go | 44 + pkg/executor/test/executor/BUILD.bazel | 60 + pkg/executor/test/executor/executor_test.go | 4301 +++ pkg/executor/test/executor/main_test.go | 49 + pkg/executor/test/fktest/BUILD.bazel | 35 + pkg/executor/test/fktest/foreign_key_test.go | 2539 ++ pkg/executor/test/fktest/main_test.go | 45 + .../test/indexmergereadtest/BUILD.bazel | 27 + .../index_merge_reader_test.go | 88 +- .../test/indexmergereadtest/main_test.go | 45 + pkg/executor/test/issuetest/BUILD.bazel | 29 + .../test/issuetest/executor_issue_test.go | 54 +- pkg/executor/test/issuetest/main_test.go | 45 + pkg/executor/test/jointest/BUILD.bazel | 25 + .../test/jointest/hashjoin/BUILD.bazel | 26 + .../test/jointest/hashjoin/hash_join_test.go | 42 +- .../test/jointest/hashjoin/main_test.go | 45 + pkg/executor/test/jointest/join_test.go | 1437 + pkg/executor/test/jointest/main_test.go | 45 + pkg/executor/test/kvtest/BUILD.bazel | 20 + .../executor}/test/kvtest/kv_test.go | 2 +- pkg/executor/test/kvtest/main_test.go | 45 + pkg/executor/test/loaddatatest/BUILD.bazel | 26 + .../test/loaddatatest/load_data_test.go | 8 +- pkg/executor/test/loaddatatest/main_test.go | 49 + pkg/executor/test/loadremotetest/BUILD.bazel | 24 + .../test/loadremotetest/error_test.go | 308 + .../test/loadremotetest/main_test.go | 0 .../test/loadremotetest/multi_file_test.go | 2 +- .../test/loadremotetest/one_csv_test.go | 2 +- pkg/executor/test/loadremotetest/util_test.go | 64 + pkg/executor/test/memtest/BUILD.bazel | 20 + pkg/executor/test/memtest/main_test.go | 45 + .../executor}/test/memtest/mem_test.go | 2 +- pkg/executor/test/oomtest/BUILD.bazel | 22 + .../executor}/test/oomtest/oom_test.go | 12 +- pkg/executor/test/partitiontest/BUILD.bazel | 18 + .../executor}/test/partitiontest/main_test.go | 0 .../test/partitiontest/partition_test.go | 503 + pkg/executor/test/passwordtest/BUILD.bazel | 25 + .../executor}/test/passwordtest/main_test.go | 0 .../passwordtest/password_management_test.go | 18 +- pkg/executor/test/seqtest/BUILD.bazel | 49 + pkg/executor/test/seqtest/main_test.go | 39 + pkg/executor/test/seqtest/prepared_test.go | 697 + .../test/seqtest/seq_executor_test.go | 84 +- pkg/executor/test/showtest/BUILD.bazel | 34 + pkg/executor/test/showtest/main_test.go | 44 + pkg/executor/test/showtest/show_test.go | 1981 ++ pkg/executor/test/simpletest/BUILD.bazel | 32 + .../test/simpletest/chunk_reuse_test.go | 2 +- .../executor}/test/simpletest/main_test.go | 0 pkg/executor/test/simpletest/simple_test.go | 1152 + pkg/executor/test/splittest/BUILD.bazel | 33 + .../executor}/test/splittest/main_test.go | 0 .../test/splittest/split_table_test.go | 32 +- pkg/executor/test/tiflashtest/BUILD.bazel | 36 + pkg/executor/test/tiflashtest/main_test.go | 49 + .../test/tiflashtest/tiflash_test.go | 104 +- pkg/executor/test/unstabletest/BUILD.bazel | 25 + .../executor}/test/unstabletest/README.md | 0 pkg/executor/test/unstabletest/main_test.go | 49 + .../test/unstabletest/memory_test.go | 4 +- .../test/unstabletest/unstable_test.go | 8 +- pkg/executor/test/writetest/BUILD.bazel | 37 + pkg/executor/test/writetest/main_test.go | 49 + .../executor}/test/writetest/write_test.go | 48 +- .../executor}/testdata/analyze_test_data.sql | 0 .../executor}/testdata/executor_suite_in.json | 0 .../testdata/executor_suite_out.json | 0 .../testdata/point_get_suite_in.json | 0 .../testdata/point_get_suite_out.json | 0 .../executor}/testdata/prepare_suite_in.json | 0 .../executor}/testdata/prepare_suite_out.json | 0 .../testdata/slow_query_suite_in.json | 0 .../testdata/slow_query_suite_out.json | 0 .../testdata/tiflash_v620_dt_segments.json | 0 .../testdata/tiflash_v620_dt_tables.json | 0 .../testdata/tiflash_v630_dt_segments.json | 0 .../testdata/tiflash_v640_dt_tables.json | 0 .../tikv_regions_peers_table_test.go | 6 +- pkg/executor/trace.go | 412 + pkg/executor/trace_test.go | 88 + {executor => pkg/executor}/union_scan.go | 26 +- {executor => pkg/executor}/union_scan_test.go | 10 +- pkg/executor/update.go | 563 + pkg/executor/update_test.go | 644 + {executor => pkg/executor}/utils.go | 0 {executor => pkg/executor}/utils_test.go | 8 +- {executor => pkg/executor}/window.go | 18 +- {executor => pkg/executor}/window_test.go | 4 +- pkg/executor/write.go | 354 + .../executor}/write_concurrent_test.go | 2 +- pkg/expression/BUILD.bazel | 238 + {expression => pkg/expression}/OWNERS | 0 pkg/expression/aggregation/BUILD.bazel | 77 + .../expression}/aggregation/agg_to_pb.go | 14 +- .../expression}/aggregation/agg_to_pb_test.go | 12 +- .../expression}/aggregation/aggregation.go | 16 +- .../aggregation/aggregation_test.go | 16 +- .../expression}/aggregation/avg.go | 12 +- .../expression}/aggregation/base_func.go | 18 +- .../expression}/aggregation/base_func_test.go | 10 +- pkg/expression/aggregation/bench_test.go | 99 + .../expression}/aggregation/bit_and.go | 6 +- .../expression}/aggregation/bit_or.go | 6 +- .../expression}/aggregation/bit_xor.go | 6 +- .../expression}/aggregation/concat.go | 10 +- .../expression}/aggregation/count.go | 6 +- .../expression}/aggregation/descriptor.go | 18 +- pkg/expression/aggregation/explain.go | 67 + .../expression}/aggregation/first_row.go | 6 +- pkg/expression/aggregation/main_test.go | 32 + .../expression}/aggregation/max_min.go | 8 +- .../expression}/aggregation/sum.go | 6 +- pkg/expression/aggregation/util.go | 95 + pkg/expression/aggregation/util_test.go | 45 + .../expression}/aggregation/window_func.go | 10 +- pkg/expression/bench_test.go | 2143 ++ {expression => pkg/expression}/builtin.go | 20 +- .../expression}/builtin_arithmetic.go | 12 +- .../expression}/builtin_arithmetic_test.go | 12 +- .../expression}/builtin_arithmetic_vec.go | 10 +- .../builtin_arithmetic_vec_test.go | 10 +- .../expression}/builtin_cast.go | 20 +- .../expression}/builtin_cast_bench_test.go | 8 +- .../expression}/builtin_cast_test.go | 12 +- .../expression}/builtin_cast_vec.go | 6 +- .../expression}/builtin_cast_vec_test.go | 10 +- .../expression}/builtin_compare.go | 18 +- .../expression}/builtin_compare_test.go | 10 +- .../expression}/builtin_compare_vec.go | 6 +- .../builtin_compare_vec_generated.go | 4 +- .../builtin_compare_vec_generated_test.go | 4 +- .../expression}/builtin_compare_vec_test.go | 6 +- .../expression}/builtin_control.go | 12 +- .../expression}/builtin_control_test.go | 8 +- .../builtin_control_vec_generated.go | 4 +- .../builtin_control_vec_generated_test.go | 4 +- .../expression}/builtin_convert_charset.go | 20 +- .../expression}/builtin_encryption.go | 16 +- .../expression}/builtin_encryption_test.go | 20 +- .../expression}/builtin_encryption_vec.go | 12 +- .../builtin_encryption_vec_test.go | 4 +- .../expression}/builtin_func_param.go | 2 +- .../expression}/builtin_grouping.go | 10 +- .../expression}/builtin_grouping_test.go | 8 +- .../expression}/builtin_ilike.go | 10 +- .../expression}/builtin_ilike_test.go | 14 +- .../expression}/builtin_ilike_vec.go | 8 +- .../expression}/builtin_info.go | 22 +- .../expression}/builtin_info_test.go | 18 +- .../expression}/builtin_info_vec.go | 10 +- .../expression}/builtin_info_vec_test.go | 10 +- .../expression}/builtin_json.go | 14 +- .../expression}/builtin_json_test.go | 12 +- .../expression}/builtin_json_vec.go | 8 +- .../expression}/builtin_json_vec_test.go | 4 +- .../expression}/builtin_like.go | 8 +- .../expression}/builtin_like_test.go | 12 +- .../expression}/builtin_like_vec.go | 2 +- .../expression}/builtin_like_vec_test.go | 4 +- .../expression}/builtin_math.go | 12 +- .../expression}/builtin_math_test.go | 16 +- .../expression}/builtin_math_vec.go | 8 +- .../expression}/builtin_math_vec_test.go | 6 +- .../expression}/builtin_miscellaneous.go | 14 +- .../expression}/builtin_miscellaneous_test.go | 12 +- .../expression}/builtin_miscellaneous_vec.go | 6 +- .../builtin_miscellaneous_vec_test.go | 8 +- {expression => pkg/expression}/builtin_op.go | 10 +- .../expression}/builtin_op_test.go | 10 +- .../expression}/builtin_op_vec.go | 6 +- .../expression}/builtin_op_vec_test.go | 10 +- .../expression}/builtin_other.go | 18 +- .../expression}/builtin_other_test.go | 14 +- .../expression}/builtin_other_vec.go | 6 +- .../builtin_other_vec_generated.go | 8 +- .../builtin_other_vec_generated_test.go | 6 +- .../expression}/builtin_other_vec_test.go | 8 +- .../expression}/builtin_regexp.go | 16 +- .../expression}/builtin_regexp_test.go | 14 +- .../expression}/builtin_regexp_util.go | 2 +- .../builtin_regexp_vec_const_test.go | 10 +- .../expression}/builtin_string.go | 20 +- .../expression}/builtin_string_test.go | 22 +- .../expression}/builtin_string_vec.go | 14 +- .../builtin_string_vec_generated.go | 2 +- .../builtin_string_vec_generated_test.go | 4 +- .../expression}/builtin_string_vec_test.go | 8 +- .../expression}/builtin_test.go | 16 +- .../expression}/builtin_time.go | 24 +- .../expression}/builtin_time_test.go | 30 +- .../expression}/builtin_time_vec.go | 10 +- .../expression}/builtin_time_vec_generated.go | 8 +- .../builtin_time_vec_generated_test.go | 6 +- .../expression}/builtin_time_vec_test.go | 10 +- .../expression}/builtin_vectorized.go | 6 +- .../expression}/builtin_vectorized_test.go | 8 +- .../expression}/chunk_executor.go | 10 +- {expression => pkg/expression}/collation.go | 18 +- pkg/expression/collation_test.go | 765 + pkg/expression/column.go | 790 + pkg/expression/column_test.go | 265 + pkg/expression/constant.go | 528 + .../expression}/constant_fold.go | 10 +- .../expression}/constant_propagation.go | 18 +- .../expression}/constant_test.go | 10 +- .../expression}/distsql_builtin.go | 18 +- .../expression}/distsql_builtin_test.go | 14 +- pkg/expression/errors.go | 117 + {expression => pkg/expression}/evaluator.go | 4 +- .../expression}/evaluator_test.go | 14 +- pkg/expression/explain.go | 192 + {expression => pkg/expression}/expr_to_pb.go | 18 +- .../expression}/expr_to_pb_test.go | 30 +- {expression => pkg/expression}/expression.go | 30 +- pkg/expression/expression_test.go | 292 + pkg/expression/extension.go | 209 + .../expression}/function_traits.go | 4 +- .../expression}/function_traits_test.go | 2 +- .../expression}/generator/compare_vec.go | 10 +- .../expression}/generator/control_vec.go | 18 +- pkg/expression/generator/helper/BUILD.bazel | 8 + pkg/expression/generator/helper/helper.go | 49 + .../expression}/generator/other_vec.go | 16 +- .../expression}/generator/string_vec.go | 8 +- .../expression}/generator/time_vec.go | 16 +- .../expression}/grouping_sets.go | 12 +- .../expression}/grouping_sets_test.go | 8 +- pkg/expression/helper.go | 191 + pkg/expression/helper_test.go | 183 + pkg/expression/integration_test/BUILD.bazel | 44 + .../expression}/integration_test/README.md | 0 .../integration_test/integration_test.go | 3273 ++ pkg/expression/integration_test/main_test.go | 56 + pkg/expression/main_test.go | 66 + .../expression}/scalar_function.go | 22 +- .../expression}/scalar_function_test.go | 12 +- pkg/expression/schema.go | 294 + {expression => pkg/expression}/schema_test.go | 0 .../expression}/simple_rewriter.go | 12 +- .../test/multivaluedindex/BUILD.bazel | 30 + .../test/multivaluedindex/main_test.go | 56 + .../multi_valued_index_test.go | 523 + .../expression}/typeinfer_test.go | 22 +- pkg/expression/util.go | 1796 ++ pkg/expression/util_test.go | 602 + {expression => pkg/expression}/vectorized.go | 6 +- {extension => pkg/extension}/.gitignore | 0 pkg/extension/BUILD.bazel | 63 + pkg/extension/_import/BUILD.bazel | 8 + .../extension}/_import/import.go | 0 pkg/extension/bootstrap_test.go | 48 + pkg/extension/enterprise | 1 + .../extension}/event_listener_test.go | 22 +- pkg/extension/extensionimpl/BUILD.bazel | 17 + pkg/extension/extensionimpl/bootstrap.go | 95 + {extension => pkg/extension}/extensions.go | 0 {extension => pkg/extension}/function.go | 8 +- {extension => pkg/extension}/function_test.go | 16 +- pkg/extension/main_test.go | 33 + {extension => pkg/extension}/manifest.go | 6 +- {extension => pkg/extension}/registry.go | 0 pkg/extension/registry_test.go | 349 + pkg/extension/session.go | 150 + {extension => pkg/extension}/util.go | 0 pkg/infoschema/BUILD.bazel | 99 + pkg/infoschema/builder.go | 1166 + pkg/infoschema/cache.go | 216 + pkg/infoschema/cluster.go | 160 + pkg/infoschema/error.go | 109 + pkg/infoschema/infoschema.go | 777 + pkg/infoschema/infoschema_test.go | 853 + pkg/infoschema/internal/BUILD.bazel | 9 + .../infoschema}/internal/testkit.go | 0 pkg/infoschema/main_test.go | 33 + .../infoschema}/metric_table_def.go | 0 pkg/infoschema/metrics/BUILD.bazel | 12 + pkg/infoschema/metrics/metrics.go | 58 + .../infoschema}/metrics_schema.go | 14 +- .../infoschema}/metrics_schema_test.go | 4 +- pkg/infoschema/perfschema/BUILD.bazel | 57 + .../infoschema}/perfschema/const.go | 0 .../infoschema}/perfschema/init.go | 18 +- pkg/infoschema/perfschema/main_test.go | 33 + pkg/infoschema/perfschema/tables.go | 414 + pkg/infoschema/perfschema/tables_test.go | 218 + .../perfschema/testdata/test.pprof | Bin .../perfschema/testdata/tikv.cpu.profile | Bin pkg/infoschema/tables.go | 2488 ++ pkg/infoschema/test/cachetest/BUILD.bazel | 18 + pkg/infoschema/test/cachetest/cache_test.go | 213 + pkg/infoschema/test/cachetest/main_test.go | 33 + .../test/clustertablestest/BUILD.bazel | 58 + .../clustertablestest/cluster_tables_test.go | 46 +- .../test/clustertablestest/main_test.go | 33 + .../test/clustertablestest/tables_test.go | 1419 + pkg/keyspace/BUILD.bazel | 27 + {keyspace => pkg/keyspace}/keyspace.go | 2 +- {keyspace => pkg/keyspace}/keyspace_test.go | 2 +- pkg/kv/BUILD.bazel | 100 + {kv => pkg/kv}/cachedb.go | 0 {kv => pkg/kv}/checker.go | 0 {kv => pkg/kv}/checker_test.go | 2 +- pkg/kv/error.go | 113 + pkg/kv/error_test.go | 43 + {kv => pkg/kv}/fault_injection.go | 0 {kv => pkg/kv}/fault_injection_test.go | 2 +- {kv => pkg/kv}/interface_mock_test.go | 2 +- {kv => pkg/kv}/iter.go | 0 {kv => pkg/kv}/key.go | 6 +- {kv => pkg/kv}/key_test.go | 10 +- {kv => pkg/kv}/keyflags.go | 0 {kv => pkg/kv}/kv.go | 12 +- pkg/kv/main_test.go | 35 + {kv => pkg/kv}/mock_test.go | 0 pkg/kv/mpp.go | 283 + {kv => pkg/kv}/option.go | 0 {kv => pkg/kv}/option_test.go | 0 pkg/kv/txn.go | 248 + {kv => pkg/kv}/txn_scope_var.go | 2 +- {kv => pkg/kv}/txn_test.go | 0 {kv => pkg/kv}/utils.go | 0 {kv => pkg/kv}/utils_test.go | 0 {kv => pkg/kv}/variables.go | 0 {kv => pkg/kv}/version.go | 0 {kv => pkg/kv}/version_test.go | 0 pkg/lock/BUILD.bazel | 16 + {lock => pkg/lock}/lock.go | 12 +- pkg/meta/BUILD.bazel | 45 + pkg/meta/autoid/BUILD.bazel | 70 + pkg/meta/autoid/autoid.go | 1364 + {meta => pkg/meta}/autoid/autoid_service.go | 8 +- pkg/meta/autoid/autoid_test.go | 651 + pkg/meta/autoid/bench_test.go | 133 + pkg/meta/autoid/errors.go | 74 + pkg/meta/autoid/main_test.go | 33 + {meta => pkg/meta}/autoid/memid.go | 2 +- {meta => pkg/meta}/autoid/memid_test.go | 12 +- {meta => pkg/meta}/autoid/seq_autoid_test.go | 12 +- pkg/meta/main_test.go | 33 + {meta => pkg/meta}/meta.go | 16 +- {meta => pkg/meta}/meta_autoid.go | 2 +- {meta => pkg/meta}/meta_test.go | 10 +- pkg/metrics/BUILD.bazel | 66 + .../metrics}/alertmanager/tidb.rules.yml | 0 {metrics => pkg/metrics}/bindinfo.go | 0 {metrics => pkg/metrics}/ddl.go | 0 {metrics => pkg/metrics}/distsql.go | 0 {metrics => pkg/metrics}/disttask.go | 2 +- {metrics => pkg/metrics}/domain.go | 0 {metrics => pkg/metrics}/executor.go | 0 {metrics => pkg/metrics}/gc_worker.go | 0 {metrics => pkg/metrics}/grafana/README.md | 0 .../metrics}/grafana/generate_json.sh | 0 .../metrics}/grafana/overview.json | 0 .../grafana/performance_overview.json | 0 {metrics => pkg/metrics}/grafana/tidb.json | 0 .../grafana/tidb_resource_control.json | 0 .../metrics}/grafana/tidb_runtime.json | 0 .../metrics}/grafana/tidb_summary.json | 0 .../metrics}/grafana/tidb_summary.jsonnet | 0 pkg/metrics/import.go | 35 + {metrics => pkg/metrics}/log_backup.go | 0 pkg/metrics/main_test.go | 33 + {metrics => pkg/metrics}/meta.go | 0 pkg/metrics/metrics.go | 326 + .../metrics}/metrics_internal_test.go | 0 {metrics => pkg/metrics}/metrics_test.go | 6 +- {metrics => pkg/metrics}/owner.go | 0 {metrics => pkg/metrics}/resourcemanager.go | 0 pkg/metrics/server.go | 387 + {metrics => pkg/metrics}/session.go | 0 {metrics => pkg/metrics}/sli.go | 0 {metrics => pkg/metrics}/stats.go | 0 pkg/metrics/telemetry.go | 586 + {metrics => pkg/metrics}/topsql.go | 0 {metrics => pkg/metrics}/ttl.go | 0 {metrics => pkg/metrics}/wrapper.go | 0 pkg/owner/BUILD.bazel | 61 + pkg/owner/fail_test.go | 112 + pkg/owner/main_test.go | 33 + pkg/owner/manager.go | 471 + pkg/owner/manager_test.go | 311 + pkg/owner/mock.go | 221 + {parser => pkg/parser}/.editorconfig | 0 {parser => pkg/parser}/.gitignore | 0 pkg/parser/BUILD.bazel | 65 + {parser => pkg/parser}/LICENSE | 0 {parser => pkg/parser}/Makefile | 0 {parser => pkg/parser}/OWNERS | 0 {parser => pkg/parser}/README.md | 0 {parser => pkg/parser}/SECURITY.md | 0 pkg/parser/ast/BUILD.bazel | 63 + {parser => pkg/parser}/ast/advisor.go | 2 +- pkg/parser/ast/ast.go | 258 + pkg/parser/ast/base.go | 135 + pkg/parser/ast/base_test.go | 42 + pkg/parser/ast/ddl.go | 4754 +++ pkg/parser/ast/ddl_test.go | 920 + {parser => pkg/parser}/ast/dml.go | 8 +- {parser => pkg/parser}/ast/dml_test.go | 6 +- {parser => pkg/parser}/ast/expressions.go | 6 +- .../parser}/ast/expressions_test.go | 6 +- {parser => pkg/parser}/ast/flag.go | 0 {parser => pkg/parser}/ast/flag_test.go | 4 +- pkg/parser/ast/format_test.go | 98 + {parser => pkg/parser}/ast/functions.go | 6 +- {parser => pkg/parser}/ast/functions_test.go | 8 +- pkg/parser/ast/misc.go | 4090 +++ pkg/parser/ast/misc_test.go | 426 + {parser => pkg/parser}/ast/procedure.go | 4 +- {parser => pkg/parser}/ast/procedure_test.go | 4 +- pkg/parser/ast/stats.go | 346 + {parser => pkg/parser}/ast/util.go | 0 pkg/parser/ast/util_test.go | 221 + pkg/parser/auth/BUILD.bazel | 36 + {parser => pkg/parser}/auth/auth.go | 2 +- {parser => pkg/parser}/auth/caching_sha2.go | 2 +- .../parser}/auth/caching_sha2_test.go | 2 +- .../parser}/auth/mysql_native_password.go | 2 +- .../auth/mysql_native_password_test.go | 0 {parser => pkg/parser}/auth/tidb_sm3.go | 0 {parser => pkg/parser}/auth/tidb_sm3_test.go | 2 +- {parser => pkg/parser}/bench_test.go | 0 pkg/parser/charset/BUILD.bazel | 49 + pkg/parser/charset/charset.go | 655 + .../parser}/charset/charset_test.go | 0 {parser => pkg/parser}/charset/encoding.go | 0 .../parser}/charset/encoding_ascii.go | 0 .../parser}/charset/encoding_base.go | 4 +- .../parser}/charset/encoding_bin.go | 0 .../parser}/charset/encoding_gbk.go | 0 .../parser}/charset/encoding_latin1.go | 0 .../parser}/charset/encoding_table.go | 0 .../parser}/charset/encoding_test.go | 2 +- .../parser}/charset/encoding_utf8.go | 0 {parser => pkg/parser}/consistent_test.go | 0 {parser => pkg/parser}/digester.go | 2 +- {parser => pkg/parser}/digester_test.go | 2 +- {parser => pkg/parser}/docs/quickstart.md | 0 pkg/parser/duration/BUILD.bazel | 18 + {parser => pkg/parser}/duration/duration.go | 0 .../parser}/duration/duration_test.go | 0 pkg/parser/format/BUILD.bazel | 21 + {parser => pkg/parser}/format/format.go | 0 {parser => pkg/parser}/format/format_test.go | 0 {parser => pkg/parser}/go.mod | 2 +- {parser => pkg/parser}/go.sum | 0 pkg/parser/goyacc/BUILD.bazel | 26 + {parser => pkg/parser}/goyacc/format_yacc.go | 2 +- {parser => pkg/parser}/goyacc/main.go | 0 {parser => pkg/parser}/hintparser.go | 4 +- {parser => pkg/parser}/hintparser.y | 4 +- {parser => pkg/parser}/hintparser_test.go | 8 +- {parser => pkg/parser}/hintparserimpl.go | 6 +- {parser => pkg/parser}/lexer.go | 6 +- {parser => pkg/parser}/lexer_test.go | 2 +- {parser => pkg/parser}/main_test.go | 0 {parser => pkg/parser}/misc.go | 0 pkg/parser/model/BUILD.bazel | 41 + pkg/parser/model/ddl.go | 1000 + pkg/parser/model/ddl_test.go | 106 + {parser => pkg/parser}/model/flags.go | 0 {parser => pkg/parser}/model/model.go | 10 +- {parser => pkg/parser}/model/model_test.go | 6 +- pkg/parser/model/reorg.go | 139 + pkg/parser/mysql/BUILD.bazel | 38 + {parser => pkg/parser}/mysql/charset.go | 0 {parser => pkg/parser}/mysql/const.go | 2 +- {parser => pkg/parser}/mysql/const_test.go | 0 {parser => pkg/parser}/mysql/errcode.go | 2 +- {parser => pkg/parser}/mysql/errname.go | 0 {parser => pkg/parser}/mysql/error.go | 0 {parser => pkg/parser}/mysql/error_test.go | 0 {parser => pkg/parser}/mysql/locale_format.go | 0 {parser => pkg/parser}/mysql/privs.go | 0 {parser => pkg/parser}/mysql/privs_test.go | 0 {parser => pkg/parser}/mysql/state.go | 0 {parser => pkg/parser}/mysql/type.go | 0 {parser => pkg/parser}/mysql/type_test.go | 0 {parser => pkg/parser}/mysql/util.go | 0 pkg/parser/opcode/BUILD.bazel | 17 + {parser => pkg/parser}/opcode/opcode.go | 2 +- {parser => pkg/parser}/opcode/opcode_test.go | 0 pkg/parser/parser.go | 24733 ++++++++++++++++ {parser => pkg/parser}/parser.y | 16 +- pkg/parser/parser_test.go | 7472 +++++ {parser => pkg/parser}/reserved_words_test.go | 2 +- pkg/parser/terror/BUILD.bazel | 27 + pkg/parser/terror/terror.go | 337 + {parser => pkg/parser}/terror/terror_test.go | 0 {parser => pkg/parser}/test.sh | 0 pkg/parser/test_driver/BUILD.bazel | 21 + .../parser}/test_driver/test_driver.go | 8 +- .../parser}/test_driver/test_driver_datum.go | 6 +- .../parser}/test_driver/test_driver_helper.go | 0 .../test_driver/test_driver_mydecimal.go | 0 pkg/parser/tidb/BUILD.bazel | 8 + {parser => pkg/parser}/tidb/features.go | 0 pkg/parser/types/BUILD.bazel | 39 + pkg/parser/types/etc.go | 164 + pkg/parser/types/etc_test.go | 34 + {parser => pkg/parser}/types/eval_type.go | 0 pkg/parser/types/field_type.go | 696 + pkg/parser/types/field_type_test.go | 331 + {parser => pkg/parser}/yy_parser.go | 12 +- pkg/planner/BUILD.bazel | 34 + pkg/planner/cardinality/BUILD.bazel | 93 + .../planner}/cardinality/cross_estimation.go | 16 +- pkg/planner/cardinality/join.go | 51 + pkg/planner/cardinality/main_test.go | 65 + {planner => pkg/planner}/cardinality/ndv.go | 8 +- .../planner}/cardinality/pseudo.go | 14 +- .../planner}/cardinality/row_count_column.go | 16 +- .../planner}/cardinality/row_count_index.go | 22 +- .../planner}/cardinality/row_count_test.go | 10 +- .../planner}/cardinality/row_size.go | 14 +- .../planner}/cardinality/row_size_test.go | 8 +- .../planner}/cardinality/selectivity.go | 24 +- .../planner}/cardinality/selectivity_test.go | 40 +- .../testdata/cardinality_suite_in.json | 0 .../testdata/cardinality_suite_out.json | 306 +- pkg/planner/cardinality/trace.go | 306 + pkg/planner/cardinality/trace_test.go | 262 + pkg/planner/cascades/BUILD.bazel | 63 + .../planner}/cascades/enforcer_rules.go | 10 +- .../planner}/cascades/enforcer_rules_test.go | 6 +- .../planner}/cascades/implementation_rules.go | 10 +- pkg/planner/cascades/integration_test.go | 59 + pkg/planner/cascades/main_test.go | 64 + pkg/planner/cascades/optimize.go | 383 + pkg/planner/cascades/optimize_test.go | 237 + pkg/planner/cascades/stringer.go | 119 + pkg/planner/cascades/stringer_test.go | 83 + .../testdata/integration_suite_in.json | 0 .../testdata/integration_suite_out.json | 0 .../cascades/testdata/stringer_suite_in.json | 0 .../cascades/testdata/stringer_suite_out.json | 0 .../transformation_rules_suite_in.json | 0 .../transformation_rules_suite_out.json | 0 .../planner}/cascades/transformation_rules.go | 24 +- .../cascades/transformation_rules_test.go | 14 +- pkg/planner/core/BUILD.bazel | 289 + .../planner}/core/access_object.go | 8 +- pkg/planner/core/binary_plan_test.go | 428 + pkg/planner/core/casetest/BUILD.bazel | 32 + .../core/casetest/binaryplan/BUILD.bazel | 24 + .../casetest/binaryplan/binary_plan_test.go | 141 + .../core/casetest/binaryplan/main_test.go | 58 + .../testdata/binary_plan_suite_in.json | 0 .../testdata/binary_plan_suite_out.json | 0 pkg/planner/core/casetest/cbotest/BUILD.bazel | 31 + pkg/planner/core/casetest/cbotest/cbo_test.go | 633 + .../core/casetest/cbotest/main_test.go | 52 + .../analyzeSuiteTestIndexEqualUnknownT.json | 0 ...analyzeSuiteTestLimitIndexEstimationT.json | 0 ...lyzeSuiteTestLowSelIndexGreedySearchT.json | 0 .../cbotest/testdata/analyze_suite_in.json | 0 .../cbotest/testdata/analyze_suite_out.json | 0 .../testdata/analyzesSuiteTestIndexReadT.json | 0 pkg/planner/core/casetest/dag/BUILD.bazel | 31 + .../planner}/core/casetest/dag/dag_test.go | 24 +- pkg/planner/core/casetest/dag/main_test.go | 52 + .../casetest/dag/testdata/plan_suite_in.json | 0 .../casetest/dag/testdata/plan_suite_out.json | 0 .../core/casetest/enforcempp/BUILD.bazel | 27 + .../casetest/enforcempp/enforce_mpp_test.go | 727 + .../core/casetest/enforcempp/main_test.go | 53 + .../testdata/enforce_mpp_suite_in.json | 0 .../testdata/enforce_mpp_suite_out.json | 0 .../core/casetest/flatplan/BUILD.bazel | 27 + .../core/casetest/flatplan/flat_plan_test.go | 16 +- .../core/casetest/flatplan/main_test.go | 59 + .../flatplan/testdata/flat_plan_suite_in.json | 0 .../testdata/flat_plan_suite_out.json | 0 pkg/planner/core/casetest/hint/BUILD.bazel | 26 + .../planner}/core/casetest/hint/hint_test.go | 12 +- pkg/planner/core/casetest/hint/main_test.go | 59 + .../hint/testdata/integration_suite_in.json | 0 .../hint/testdata/integration_suite_out.json | 0 pkg/planner/core/casetest/index/BUILD.bazel | 23 + pkg/planner/core/casetest/index/index_test.go | 419 + pkg/planner/core/casetest/index/main_test.go | 54 + .../index/testdata/integration_suite_in.json | 0 .../index/testdata/integration_suite_out.json | 0 pkg/planner/core/casetest/integration_test.go | 441 + pkg/planner/core/casetest/main_test.go | 69 + pkg/planner/core/casetest/mpp/BUILD.bazel | 25 + pkg/planner/core/casetest/mpp/main_test.go | 59 + .../planner}/core/casetest/mpp/mpp_test.go | 12 +- .../mpp/testdata/integration_suite_in.json | 0 .../mpp/testdata/integration_suite_out.json | 0 .../core/casetest/partition/BUILD.bazel | 26 + .../partition/integration_partition_test.go | 248 + .../core/casetest/partition/main_test.go | 62 + .../partition/partition_pruner_test.go | 16 +- .../integration_partition_suite_in.json | 0 .../integration_partition_suite_out.json | 0 .../testdata/partition_pruner_in.json | 0 .../testdata/partition_pruner_out.json | 0 .../casetest/physicalplantest/BUILD.bazel | 37 + .../casetest/physicalplantest/main_test.go | 52 + .../physicalplantest/physical_plan_test.go | 2243 ++ .../testdata/plan_suite_in.json | 0 .../testdata/plan_suite_out.json | 0 pkg/planner/core/casetest/plan_test.go | 234 + .../core/casetest/planstats/BUILD.bazel | 34 + .../core/casetest/planstats/main_test.go | 54 + .../casetest/planstats/plan_stats_test.go | 28 +- .../testdata/plan_stats_suite_in.json | 0 .../testdata/plan_stats_suite_out.json | 0 .../core/casetest/pushdown/BUILD.bazel | 23 + .../core/casetest/pushdown/main_test.go | 54 + .../core/casetest/pushdown/push_down_test.go | 8 +- .../testdata/integration_suite_in.json | 0 .../testdata/integration_suite_out.json | 0 pkg/planner/core/casetest/rule/BUILD.bazel | 33 + pkg/planner/core/casetest/rule/main_test.go | 57 + .../rule/rule_derive_topn_from_window_test.go | 8 +- .../rule/rule_inject_extra_projection_test.go | 14 +- .../casetest/rule/rule_join_reorder_test.go | 114 + .../testdata/derive_topn_from_window_in.json | 0 .../testdata/derive_topn_from_window_out.json | 0 .../rule/testdata/join_reorder_suite_in.json | 0 .../rule/testdata/join_reorder_suite_out.json | 0 .../core/casetest/scalarsubquery/BUILD.bazel | 20 + .../casetest/scalarsubquery/cases_test.go | 4 +- .../core/casetest/scalarsubquery/main_test.go | 52 + .../testdata/plan_suite_in.json | 0 .../testdata/plan_suite_out.json | 0 pkg/planner/core/casetest/stats_test.go | 153 + .../testdata/integration_suite_in.json | 0 .../testdata/integration_suite_out.json | 0 .../casetest/testdata/json_plan_suite_in.json | 0 .../testdata/json_plan_suite_out.json | 0 .../testdata/plan_normalized_suite_in.json | 0 .../testdata/plan_normalized_suite_out.json | 0 .../casetest/testdata/stats_suite_in.json | 0 .../casetest/testdata/stats_suite_out.json | 0 ...ash_selection_late_materialization_test.go | 10 +- pkg/planner/core/casetest/windows/BUILD.bazel | 24 + .../core/casetest/windows/main_test.go | 52 + .../testdata/window_push_down_suite_in.json | 0 .../testdata/window_push_down_suite_out.json | 0 .../casetest/windows/window_push_down_test.go | 12 +- pkg/planner/core/cbo_test.go | 235 + .../core/collect_column_stats_usage.go | 4 +- .../core/collect_column_stats_usage_test.go | 10 +- {planner => pkg/planner}/core/common_plans.go | 36 +- .../planner}/core/common_plans_test.go | 4 +- pkg/planner/core/debugtrace.go | 259 + {planner => pkg/planner}/core/encode.go | 6 +- pkg/planner/core/enforce_mpp_test.go | 62 + pkg/planner/core/errors.go | 122 + pkg/planner/core/errors_test.go | 91 + .../planner}/core/exhaust_physical_plans.go | 42 +- .../core/exhaust_physical_plans_test.go | 24 +- pkg/planner/core/explain.go | 1112 + .../planner}/core/expression_rewriter.go | 44 +- .../planner}/core/expression_rewriter_test.go | 8 +- pkg/planner/core/expression_test.go | 334 + .../planner}/core/find_best_task.go | 40 +- .../planner}/core/find_best_task_test.go | 8 +- {planner => pkg/planner}/core/flat_plan.go | 6 +- pkg/planner/core/foreign_key.go | 499 + {planner => pkg/planner}/core/fragment.go | 28 +- .../planner}/core/fragment_test.go | 0 {planner => pkg/planner}/core/handle_cols.go | 22 +- {planner => pkg/planner}/core/hashcode.go | 2 +- {planner => pkg/planner}/core/hints.go | 10 +- .../core/indexmerge_intersection_test.go | 6 +- .../planner}/core/indexmerge_path.go | 28 +- .../planner}/core/indexmerge_path_test.go | 2 +- .../planner}/core/indexmerge_test.go | 14 +- {planner => pkg/planner}/core/initialize.go | 18 +- .../core/integration_partition_test.go | 315 + pkg/planner/core/integration_test.go | 2570 ++ pkg/planner/core/internal/BUILD.bazel | 22 + pkg/planner/core/internal/base/BUILD.bazel | 16 + pkg/planner/core/internal/base/plan.go | 138 + pkg/planner/core/internal/testkit.go | 78 + pkg/planner/core/internal/util.go | 31 + pkg/planner/core/issuetest/BUILD.bazel | 24 + pkg/planner/core/issuetest/main_test.go | 41 + .../core/issuetest/planner_issue_test.go | 8 +- .../planner}/core/logical_plan_builder.go | 78 +- .../planner}/core/logical_plan_trace_test.go | 6 +- .../planner}/core/logical_plans.go | 38 +- .../planner}/core/logical_plans_test.go | 32 +- pkg/planner/core/main_test.go | 64 + .../core/memtable_predicate_extractor.go | 30 +- .../core/memtable_predicate_extractor_test.go | 24 +- pkg/planner/core/metrics/BUILD.bazel | 12 + pkg/planner/core/metrics/metrics.go | 82 + pkg/planner/core/mock.go | 611 + {planner => pkg/planner}/core/optimizer.go | 44 +- .../planner}/core/optimizer_test.go | 16 +- .../planner}/core/partition_prune.go | 10 +- .../planner}/core/partition_pruning_test.go | 16 +- {planner => pkg/planner}/core/pb_to_plan.go | 18 +- pkg/planner/core/physical_plan_test.go | 471 + .../planner}/core/physical_plan_trace_test.go | 14 +- .../planner}/core/physical_plans.go | 40 +- pkg/planner/core/plan.go | 855 + {planner => pkg/planner}/core/plan_cache.go | 46 +- .../planner}/core/plan_cache_lru.go | 16 +- .../planner}/core/plan_cache_lru_test.go | 12 +- .../planner}/core/plan_cache_param.go | 14 +- .../planner}/core/plan_cache_param_test.go | 4 +- .../planner}/core/plan_cache_test.go | 26 +- .../planner}/core/plan_cache_utils.go | 42 +- .../planner}/core/plan_cacheable_checker.go | 28 +- .../core/plan_cacheable_checker_test.go | 22 +- .../planner}/core/plan_cost_detail.go | 4 +- .../planner}/core/plan_cost_detail_test.go | 16 +- .../planner}/core/plan_cost_ver1.go | 14 +- .../planner}/core/plan_cost_ver1_test.go | 6 +- .../planner}/core/plan_cost_ver2.go | 18 +- .../planner}/core/plan_cost_ver2_test.go | 12 +- .../core/plan_replayer_capture_test.go | 14 +- {planner => pkg/planner}/core/plan_stats.go | 20 +- pkg/planner/core/plan_test.go | 763 + {planner => pkg/planner}/core/plan_to_pb.go | 18 +- .../planner}/core/plan_to_pb_test.go | 10 +- {planner => pkg/planner}/core/planbuilder.go | 82 +- .../planner}/core/planbuilder_test.go | 28 +- .../planner}/core/point_get_plan.go | 62 +- .../planner}/core/point_get_plan_test.go | 14 +- {planner => pkg/planner}/core/preprocess.go | 54 +- .../planner}/core/preprocess_test.go | 26 +- .../planner}/core/property_cols_prune.go | 4 +- .../planner}/core/resolve_indices.go | 6 +- .../core/rule_aggregation_elimination.go | 12 +- .../core/rule_aggregation_push_down.go | 14 +- .../core/rule_aggregation_skew_rewrite.go | 8 +- .../planner}/core/rule_build_key_info.go | 8 +- .../planner}/core/rule_column_pruning.go | 14 +- .../core/rule_constant_propagation.go | 4 +- .../planner}/core/rule_decorrelate.go | 12 +- .../core/rule_derive_topn_from_window.go | 8 +- .../core/rule_eliminate_projection.go | 6 +- .../core/rule_generate_column_substitute.go | 6 +- .../rule_generate_column_substitute_test.go | 12 +- .../core/rule_inject_extra_projection.go | 12 +- .../planner}/core/rule_join_elimination.go | 6 +- .../planner}/core/rule_join_reorder.go | 10 +- .../planner}/core/rule_join_reorder_dp.go | 4 +- .../core/rule_join_reorder_dp_test.go | 16 +- .../planner}/core/rule_join_reorder_greedy.go | 2 +- pkg/planner/core/rule_join_reorder_test.go | 70 + .../planner}/core/rule_max_min_eliminate.go | 16 +- .../planner}/core/rule_partition_processor.go | 28 +- .../planner}/core/rule_predicate_push_down.go | 18 +- .../core/rule_predicate_simplification.go | 6 +- .../planner}/core/rule_push_down_sequence.go | 0 .../core/rule_resolve_grouping_expand.go | 0 .../planner}/core/rule_result_reorder.go | 4 +- .../planner}/core/rule_semi_join_rewrite.go | 6 +- .../planner}/core/rule_topn_push_down.go | 6 +- .../planner}/core/runtime_filter.go | 12 +- .../planner}/core/runtime_filter_generator.go | 14 +- .../core/runtime_filter_generator_test.go | 18 +- .../planner}/core/scalar_subq_expression.go | 16 +- .../planner}/core/show_predicate_extractor.go | 10 +- pkg/planner/core/stats.go | 1042 + pkg/planner/core/stringer.go | 378 + pkg/planner/core/stringer_test.go | 127 + pkg/planner/core/task.go | 2747 ++ pkg/planner/core/telemetry.go | 55 + .../core/testdata/index_merge_suite_in.json | 0 .../core/testdata/index_merge_suite_out.json | 0 .../testdata/plan_suite_unexported_in.json | 0 .../testdata/plan_suite_unexported_out.json | 0 .../runtime_filter_generator_suite_in.json | 0 .../runtime_filter_generator_suite_out.json | 0 pkg/planner/core/tests/prepare/BUILD.bazel | 32 + .../core/tests/prepare/issue/BUILD.bazel | 18 + .../core/tests/prepare/issue/issue_test.go | 4 +- .../core/tests/prepare/issue/main_test.go | 33 + pkg/planner/core/tests/prepare/main_test.go | 33 + .../core/tests/prepare/prepare_test.go | 26 +- .../tiflash_selection_late_materialization.go | 12 +- pkg/planner/core/trace.go | 31 + pkg/planner/core/util.go | 474 + pkg/planner/funcdep/BUILD.bazel | 46 + {planner => pkg/planner}/funcdep/doc.go | 0 .../planner}/funcdep/extract_fd_test.go | 16 +- .../planner}/funcdep/fast_int_set.go | 0 .../funcdep/fast_int_set_bench_test.go | 0 .../planner}/funcdep/fast_int_set_test.go | 0 {planner => pkg/planner}/funcdep/fd_graph.go | 2 +- .../planner}/funcdep/fd_graph_test.go | 0 pkg/planner/funcdep/main_test.go | 33 + pkg/planner/implementation/BUILD.bazel | 43 + pkg/planner/implementation/base.go | 66 + pkg/planner/implementation/base_test.go | 43 + .../planner}/implementation/datasource.go | 14 +- pkg/planner/implementation/join.go | 74 + pkg/planner/implementation/main_test.go | 32 + .../planner}/implementation/simple_plans.go | 4 +- pkg/planner/implementation/sort.go | 65 + pkg/planner/memo/BUILD.bazel | 48 + .../planner}/memo/expr_iterator.go | 0 .../planner}/memo/expr_iterator_test.go | 6 +- pkg/planner/memo/group.go | 272 + {planner => pkg/planner}/memo/group_expr.go | 4 +- .../planner}/memo/group_expr_test.go | 4 +- {planner => pkg/planner}/memo/group_test.go | 16 +- .../planner}/memo/implementation.go | 2 +- pkg/planner/memo/main_test.go | 33 + {planner => pkg/planner}/memo/pattern.go | 2 +- {planner => pkg/planner}/memo/pattern_test.go | 2 +- pkg/planner/optimize.go | 903 + pkg/planner/property/BUILD.bazel | 23 + .../planner}/property/logical_property.go | 2 +- .../planner}/property/physical_property.go | 10 +- .../planner}/property/stats_info.go | 4 +- .../planner}/property/task_type.go | 0 pkg/planner/util/BUILD.bazel | 45 + {planner => pkg/planner}/util/byitem.go | 6 +- pkg/planner/util/debugtrace/BUILD.bazel | 9 + pkg/planner/util/debugtrace/base.go | 162 + pkg/planner/util/fixcontrol/BUILD.bazel | 33 + .../util/fixcontrol/fixcontrol_test.go | 6 +- .../planner}/util/fixcontrol/get.go | 0 pkg/planner/util/fixcontrol/main_test.go | 51 + .../planner}/util/fixcontrol/set.go | 0 .../testdata/fix_control_suite_in.json | 0 .../testdata/fix_control_suite_out.json | 0 pkg/planner/util/main_test.go | 33 + pkg/planner/util/misc.go | 61 + {planner => pkg/planner}/util/path.go | 16 +- {planner => pkg/planner}/util/path_test.go | 14 +- pkg/plugin/BUILD.bazel | 54 + {plugin => pkg/plugin}/README.md | 0 {plugin => pkg/plugin}/audit.go | 2 +- pkg/plugin/conn_ip_example/BUILD.bazel | 30 + .../conn_ip_example/conn_ip_example.go | 4 +- .../conn_ip_example/conn_ip_example_test.go | 4 +- pkg/plugin/conn_ip_example/main_test.go | 33 + .../plugin}/conn_ip_example/manifest.toml | 0 {plugin => pkg/plugin}/const.go | 0 {plugin => pkg/plugin}/const_test.go | 0 pkg/plugin/errors.go | 29 + {plugin => pkg/plugin}/helper.go | 0 {plugin => pkg/plugin}/helper_test.go | 0 pkg/plugin/integration_test.go | 786 + pkg/plugin/main_test.go | 36 + {plugin => pkg/plugin}/plugin.go | 6 +- {plugin => pkg/plugin}/plugin_test.go | 2 +- {plugin => pkg/plugin}/spi.go | 0 {plugin => pkg/plugin}/spi_test.go | 4 +- pkg/privilege/BUILD.bazel | 16 + pkg/privilege/conn/BUILD.bazel | 8 + {privilege => pkg/privilege}/conn/conn.go | 0 {privilege => pkg/privilege}/privilege.go | 12 +- pkg/privilege/privileges/BUILD.bazel | 88 + pkg/privilege/privileges/cache.go | 1659 ++ pkg/privilege/privileges/cache_test.go | 574 + pkg/privilege/privileges/errors.go | 31 + pkg/privilege/privileges/ldap/BUILD.bazel | 33 + .../privilege}/privileges/ldap/const.go | 0 .../privilege}/privileges/ldap/ldap_common.go | 0 .../privileges/ldap/ldap_common_test.go | 0 .../privilege}/privileges/ldap/sasl.go | 2 +- .../privilege}/privileges/ldap/simple.go | 0 .../privilege}/privileges/ldap/test/ca.crt | 0 .../privilege}/privileges/ldap/test/ldap.crt | 0 .../privilege}/privileges/ldap/test/ldap.key | 0 pkg/privilege/privileges/main_test.go | 41 + .../privilege}/privileges/privileges.go | 32 +- pkg/privilege/privileges/privileges_test.go | 2117 ++ .../privilege}/privileges/tidb_auth_token.go | 2 +- .../privileges/tidb_auth_token_test.go | 2 +- pkg/resourcemanager/BUILD.bazel | 33 + pkg/resourcemanager/pool/BUILD.bazel | 9 + .../resourcemanager}/pool/basepool.go | 0 pkg/resourcemanager/pool/spool/BUILD.bazel | 43 + pkg/resourcemanager/pool/spool/main_test.go | 33 + .../resourcemanager}/pool/spool/option.go | 0 .../resourcemanager}/pool/spool/spool.go | 14 +- .../resourcemanager}/pool/spool/spool_test.go | 4 +- .../pool/workerpool/BUILD.bazel | 37 + .../pool/workerpool/main_test.go | 33 + .../pool/workerpool/workerpool.go | 8 +- .../pool/workerpool/workpool_test.go | 4 +- pkg/resourcemanager/poolmanager/BUILD.bazel | 13 + .../poolmanager/task_manager.go | 0 .../poolmanager/task_manager_iterator.go | 0 .../poolmanager/task_manager_scheduler.go | 0 .../resourcemanager}/rm.go | 8 +- .../resourcemanager}/schedule.go | 4 +- .../resourcemanager}/schedule_test.go | 4 +- pkg/resourcemanager/scheduler/BUILD.bazel | 15 + .../scheduler/cpu_scheduler.go | 4 +- pkg/resourcemanager/scheduler/scheduler.go | 36 + pkg/resourcemanager/util/BUILD.bazel | 29 + .../resourcemanager}/util/mock_gpool.go | 0 .../resourcemanager}/util/shard_pool_map.go | 2 +- .../util/shard_pool_map_test.go | 2 +- .../resourcemanager}/util/util.go | 0 pkg/server/BUILD.bazel | 187 + {server => pkg/server}/conn.go | 92 +- {server => pkg/server}/conn_stmt.go | 40 +- {server => pkg/server}/conn_stmt_test.go | 20 +- {server => pkg/server}/conn_test.go | 154 +- {server => pkg/server}/driver.go | 8 +- {server => pkg/server}/driver_tidb.go | 34 +- {server => pkg/server}/driver_tidb_test.go | 12 +- pkg/server/err/BUILD.bazel | 12 + pkg/server/err/error.go | 47 + pkg/server/extension.go | 254 + pkg/server/extract.go | 26 + pkg/server/handler/BUILD.bazel | 36 + .../handler/extactorhandler/BUILD.bazel | 48 + .../handler/extactorhandler/extactor.go | 8 +- .../handler/extactorhandler/extract_test.go | 125 + .../handler/extactorhandler/main_test.go | 73 + pkg/server/handler/optimizor/BUILD.bazel | 74 + pkg/server/handler/optimizor/main_test.go | 73 + .../handler/optimizor/optimize_trace.go | 8 +- .../handler/optimizor/optimize_trace_test.go | 14 +- pkg/server/handler/optimizor/plan_replayer.go | 375 + .../handler/optimizor/plan_replayer_test.go | 452 + .../handler/optimizor/statistics_handler.go | 18 +- .../optimizor/statistics_handler_test.go | 20 +- pkg/server/handler/tests/BUILD.bazel | 60 + .../handler/tests/http_handler_serial_test.go | 44 +- .../handler/tests/http_handler_test.go | 64 +- pkg/server/handler/tests/main_test.go | 73 + pkg/server/handler/tikv_handler.go | 278 + pkg/server/handler/tikvhandler/BUILD.bazel | 44 + .../handler/tikvhandler/tikv_handler.go | 2023 ++ pkg/server/handler/ttlhandler/BUILD.bazel | 19 + pkg/server/handler/ttlhandler/ttl.go | 73 + .../server}/handler/upgrade_handler.go | 8 +- pkg/server/handler/util.go | 108 + {server => pkg/server}/http_handler.go | 14 +- {server => pkg/server}/http_status.go | 36 +- pkg/server/internal/BUILD.bazel | 34 + pkg/server/internal/column/BUILD.bazel | 46 + pkg/server/internal/column/column.go | 250 + pkg/server/internal/column/column_test.go | 252 + pkg/server/internal/column/convert.go | 91 + .../server}/internal/column/result_encoder.go | 6 +- .../internal/column/result_encoder_test.go | 0 pkg/server/internal/dump/BUILD.bazel | 26 + pkg/server/internal/dump/dump.go | 168 + pkg/server/internal/dump/dump_test.go | 125 + pkg/server/internal/handshake/BUILD.bazel | 9 + .../server}/internal/handshake/handshake.go | 0 {server => pkg/server}/internal/packetio.go | 12 +- .../server}/internal/packetio_test.go | 6 +- pkg/server/internal/parse/BUILD.bazel | 45 + .../server}/internal/parse/handshake_test.go | 2 +- .../server}/internal/parse/parse.go | 22 +- .../server}/internal/parse/parse_test.go | 12 +- pkg/server/internal/resultset/BUILD.bazel | 18 + .../server}/internal/resultset/cursor.go | 2 +- .../server}/internal/resultset/resultset.go | 10 +- .../internal/testserverclient/BUILD.bazel | 23 + .../testserverclient/server_client.go | 18 +- pkg/server/internal/testutil/BUILD.bazel | 8 + .../server}/internal/testutil/testutil.go | 0 pkg/server/internal/util/BUILD.bazel | 25 + .../internal/util/buffered_read_conn.go | 0 pkg/server/internal/util/util.go | 230 + .../server}/internal/util/util_test.go | 0 pkg/server/main_test.go | 84 + pkg/server/metrics/BUILD.bazel | 13 + pkg/server/metrics/metrics.go | 95 + {server => pkg/server}/mock_conn.go | 20 +- {server => pkg/server}/mock_conn_test.go | 4 +- {server => pkg/server}/rpc_server.go | 26 +- pkg/server/server.go | 1095 + {server => pkg/server}/server_test.go | 32 +- pkg/server/stat.go | 76 + pkg/server/stat_test.go | 65 + .../server}/testdata/optimizer_suite_in.json | 0 .../server}/testdata/optimizer_suite_out.json | 116 +- pkg/server/tests/BUILD.bazel | 54 + pkg/server/tests/main_test.go | 73 + .../server}/tests/tidb_serial_test.go | 22 +- pkg/server/tests/tidb_test.go | 3124 ++ {server => pkg/server}/tidb_library_test.go | 6 +- pkg/server/tidb_test.go | 133 + {server => pkg/server}/tokenlimiter.go | 0 pkg/session/BUILD.bazel | 168 + {session => pkg/session}/OWNERS | 0 {session => pkg/session}/advisory_locks.go | 4 +- pkg/session/bench_test.go | 1945 ++ pkg/session/bootstrap.go | 3152 ++ pkg/session/bootstrap_test.go | 2089 ++ pkg/session/bootstraptest/BUILD.bazel | 36 + .../bootstraptest/bootstrap_upgrade_test.go | 30 +- pkg/session/bootstraptest/main_test.go | 63 + pkg/session/clusteredindextest/BUILD.bazel | 26 + .../clustered_index_test.go | 6 +- pkg/session/clusteredindextest/main_test.go | 65 + .../session}/index_usage_sync_lease_test.go | 0 pkg/session/main_test.go | 80 + pkg/session/metrics/BUILD.bazel | 12 + pkg/session/metrics/metrics.go | 146 + {session => pkg/session}/mock_bootstrap.go | 2 +- {session => pkg/session}/nontransactional.go | 42 +- pkg/session/nontransactionaltest/BUILD.bazel | 23 + pkg/session/nontransactionaltest/main_test.go | 61 + .../nontransactional_test.go | 22 +- pkg/session/resourcegrouptest/BUILD.bazel | 13 + .../resourcegrouptest/resource_group_test.go | 59 + pkg/session/schematest/BUILD.bazel | 33 + pkg/session/schematest/main_test.go | 62 + pkg/session/schematest/schema_test.go | 681 + pkg/session/session.go | 4434 +++ {session => pkg/session}/sync_upgrade.go | 12 +- pkg/session/temporarytabletest/BUILD.bazel | 26 + pkg/session/temporarytabletest/main_test.go | 61 + .../temporary_table_test.go | 517 + pkg/session/test/BUILD.bazel | 40 + pkg/session/test/common/BUILD.bazel | 29 + pkg/session/test/common/common_test.go | 365 + pkg/session/test/common/main_test.go | 61 + pkg/session/test/main_test.go | 61 + pkg/session/test/meta/BUILD.bazel | 29 + pkg/session/test/meta/main_test.go | 61 + pkg/session/test/meta/session_test.go | 174 + pkg/session/test/privileges/BUILD.bazel | 23 + pkg/session/test/privileges/main_test.go | 61 + .../test/privileges/privileges_test.go | 173 + pkg/session/test/session_test.go | 1048 + pkg/session/test/txn/BUILD.bazel | 28 + pkg/session/test/txn/main_test.go | 61 + pkg/session/test/txn/txn_test.go | 621 + pkg/session/test/variable/BUILD.bazel | 27 + pkg/session/test/variable/main_test.go | 61 + pkg/session/test/variable/variable_test.go | 388 + pkg/session/test/vars/BUILD.bazel | 28 + pkg/session/test/vars/main_test.go | 61 + .../session}/test/vars/vars_test.go | 12 +- pkg/session/testutil.go | 95 + {session => pkg/session}/tidb.go | 40 +- pkg/session/tidb_test.go | 114 + pkg/session/txn.go | 749 + pkg/session/txninfo/BUILD.bazel | 20 + pkg/session/txninfo/summary.go | 162 + {session => pkg/session}/txninfo/txn_info.go | 8 +- {session => pkg/session}/txnmanager.go | 18 +- pkg/sessionctx/BUILD.bazel | 42 + pkg/sessionctx/binloginfo/BUILD.bazel | 61 + .../sessionctx}/binloginfo/binloginfo.go | 20 +- .../sessionctx}/binloginfo/binloginfo_test.go | 36 +- pkg/sessionctx/binloginfo/main_test.go | 33 + pkg/sessionctx/context.go | 266 + .../sessionctx}/context_test.go | 0 pkg/sessionctx/main_test.go | 33 + pkg/sessionctx/sessionstates/BUILD.bazel | 51 + .../sessionstates/session_states.go | 12 +- .../sessionstates/session_states_test.go | 22 +- .../sessionstates/session_token.go | 2 +- .../sessionstates/session_token_test.go | 4 +- pkg/sessionctx/stmtctx/BUILD.bazel | 58 + pkg/sessionctx/stmtctx/main_test.go | 33 + .../sessionctx}/stmtctx/stmtctx.go | 32 +- .../sessionctx}/stmtctx/stmtctx_test.go | 14 +- pkg/sessionctx/variable/BUILD.bazel | 125 + .../sessionctx}/variable/OWNERS | 0 pkg/sessionctx/variable/error.go | 51 + .../variable/featuretag/disttask/BUILD.bazel | 11 + .../variable/featuretag/disttask/default.go | 0 .../featuretag/disttask/non_default.go | 0 pkg/sessionctx/variable/main_test.go | 33 + .../variable/mock_globalaccessor.go | 0 .../variable/mock_globalaccessor_test.go | 0 .../sessionctx}/variable/noop.go | 0 .../sessionctx}/variable/removed.go | 0 .../sessionctx}/variable/removed_test.go | 0 .../sessionctx}/variable/sequence_state.go | 0 pkg/sessionctx/variable/session.go | 3659 +++ pkg/sessionctx/variable/session_test.go | 538 + .../sessionctx}/variable/setvar_affect.go | 0 .../sessionctx}/variable/statusvar.go | 2 +- .../sessionctx}/variable/statusvar_test.go | 0 .../sessionctx}/variable/sysvar.go | 56 +- pkg/sessionctx/variable/sysvar_test.go | 1427 + .../sessionctx}/variable/tidb_vars.go | 18 +- pkg/sessionctx/variable/variable.go | 664 + pkg/sessionctx/variable/variable_test.go | 720 + .../sessionctx}/variable/varsutil.go | 12 +- .../sessionctx}/variable/varsutil_test.go | 8 +- pkg/sessiontxn/BUILD.bazel | 55 + pkg/sessiontxn/failpoint.go | 181 + {sessiontxn => pkg/sessiontxn}/future.go | 0 pkg/sessiontxn/interface.go | 237 + pkg/sessiontxn/internal/BUILD.bazel | 16 + pkg/sessiontxn/internal/txn.go | 82 + pkg/sessiontxn/isolation/BUILD.bazel | 76 + pkg/sessiontxn/isolation/base.go | 575 + pkg/sessiontxn/isolation/main_test.go | 176 + pkg/sessiontxn/isolation/metrics/BUILD.bazel | 12 + pkg/sessiontxn/isolation/metrics/metrics.go | 36 + .../sessiontxn}/isolation/optimistic.go | 14 +- .../sessiontxn}/isolation/optimistic_test.go | 22 +- .../sessiontxn}/isolation/readcommitted.go | 18 +- .../isolation/readcommitted_test.go | 30 +- .../sessiontxn}/isolation/repeatable_read.go | 16 +- .../isolation/repeatable_read_test.go | 42 +- .../sessiontxn}/isolation/serializable.go | 10 +- .../isolation/serializable_test.go | 22 +- pkg/sessiontxn/staleread/BUILD.bazel | 68 + pkg/sessiontxn/staleread/errors.go | 24 + .../sessiontxn}/staleread/externalts_test.go | 6 +- pkg/sessiontxn/staleread/failpoint.go | 37 + pkg/sessiontxn/staleread/main_test.go | 34 + .../sessiontxn}/staleread/processor.go | 12 +- .../sessiontxn}/staleread/processor_test.go | 16 +- .../sessiontxn}/staleread/provider.go | 18 +- .../sessiontxn}/staleread/provider_test.go | 12 +- pkg/sessiontxn/staleread/util.go | 94 + .../sessiontxn}/txn_context_test.go | 60 +- .../sessiontxn}/txn_manager_test.go | 24 +- .../sessiontxn}/txn_rc_tso_optimize_test.go | 54 +- pkg/statistics/BUILD.bazel | 102 + {statistics => pkg/statistics}/analyze.go | 0 .../statistics}/analyze_jobs.go | 0 pkg/statistics/builder.go | 461 + {statistics => pkg/statistics}/cmsketch.go | 18 +- .../statistics}/cmsketch_test.go | 10 +- .../statistics}/cmsketch_util.go | 6 +- pkg/statistics/column.go | 268 + pkg/statistics/debugtrace.go | 267 + {statistics => pkg/statistics}/estimate.go | 2 +- {statistics => pkg/statistics}/fmsketch.go | 6 +- .../statistics}/fmsketch_test.go | 4 +- pkg/statistics/handle/BUILD.bazel | 90 + pkg/statistics/handle/autoanalyze/BUILD.bazel | 43 + .../handle/autoanalyze/autoanalyze.go | 28 +- .../handle/autoanalyze/autoanalyze_test.go | 8 +- pkg/statistics/handle/bootstrap.go | 557 + pkg/statistics/handle/cache/BUILD.bazel | 43 + pkg/statistics/handle/cache/bench_test.go | 180 + .../handle/cache/internal/BUILD.bazel | 17 + .../handle/cache/internal/inner.go | 2 +- .../handle/cache/internal/lfu/BUILD.bazel | 40 + .../handle/cache/internal/lfu/key_set.go | 2 +- .../cache/internal/lfu/key_set_shard.go | 2 +- .../handle/cache/internal/lfu/lfu_cache.go | 14 +- .../cache/internal/lfu/lfu_cache_test.go | 4 +- .../cache/internal/mapcache/BUILD.bazel | 12 + .../cache/internal/mapcache/map_cache.go | 4 +- .../handle/cache/internal/metrics/BUILD.bazel | 12 + .../handle/cache/internal/metrics/metrics.go | 68 + .../cache/internal/testutil/BUILD.bazel | 14 + .../cache/internal/testutil/testutil.go | 91 + .../statistics}/handle/cache/statscache.go | 10 +- .../handle/cache/statscacheinner.go | 28 +- pkg/statistics/handle/ddl.go | 393 + pkg/statistics/handle/ddl_test.go | 286 + pkg/statistics/handle/dump.go | 327 + pkg/statistics/handle/dump_test.go | 619 + pkg/statistics/handle/extstats/BUILD.bazel | 19 + .../handle/extstats/extended_stats.go | 14 +- .../statistics}/handle/gc_test.go | 4 +- pkg/statistics/handle/globalstats/BUILD.bazel | 50 + .../handle/globalstats/global_stats.go | 14 +- .../handle/globalstats/global_stats_async.go | 18 +- .../handle/globalstats/merge_worker.go | 4 +- pkg/statistics/handle/globalstats/topn.go | 115 + .../handle/globalstats/topn_bench_test.go | 16 +- pkg/statistics/handle/handle.go | 528 + .../statistics}/handle/handle_hist.go | 24 +- .../statistics}/handle/handle_hist_test.go | 24 +- pkg/statistics/handle/handletest/BUILD.bazel | 35 + .../handle/handletest/analyze/BUILD.bazel | 22 + .../handle/handletest/analyze/analyze_test.go | 246 + .../handle/handletest/analyze/main_test.go | 33 + .../handle/handletest/globalstats/BUILD.bazel | 21 + .../globalstats/globalstats_test.go | 14 +- .../handletest/globalstats/main_test.go | 33 + .../handle/handletest/handle_test.go | 1687 ++ .../handle/handletest/lockstats/BUILD.bazel | 23 + .../lockstats/lock_partition_stats_test.go | 8 +- .../lockstats/lock_table_stats_test.go | 10 +- .../handle/handletest/lockstats/main_test.go | 33 + pkg/statistics/handle/handletest/main_test.go | 33 + .../handle/handletest/statstest/BUILD.bazel | 22 + .../handle/handletest/statstest/main_test.go | 33 + .../handle/handletest/statstest/stats_test.go | 290 + pkg/statistics/handle/history/BUILD.bazel | 17 + .../handle/history/history_stats.go | 10 +- pkg/statistics/handle/internal/BUILD.bazel | 12 + pkg/statistics/handle/internal/testutil.go | 80 + pkg/statistics/handle/lockstats/BUILD.bazel | 48 + .../handle/lockstats/lock_stats.go | 8 +- .../handle/lockstats/lock_stats_test.go | 10 +- .../handle/lockstats/query_lock.go | 4 +- .../handle/lockstats/query_lock_test.go | 12 +- .../handle/lockstats/unlock_stats.go | 6 +- .../handle/lockstats/unlock_stats_test.go | 14 +- pkg/statistics/handle/main_test.go | 33 + pkg/statistics/handle/metrics/BUILD.bazel | 12 + pkg/statistics/handle/metrics/metrics.go | 47 + pkg/statistics/handle/storage/BUILD.bazel | 56 + .../statistics}/handle/storage/gc.go | 22 +- .../statistics}/handle/storage/json.go | 18 +- .../statistics}/handle/storage/read.go | 28 +- .../statistics}/handle/storage/read_test.go | 8 +- .../statistics}/handle/storage/save.go | 20 +- pkg/statistics/handle/storage/update.go | 220 + pkg/statistics/handle/update.go | 58 + pkg/statistics/handle/updatetest/BUILD.bazel | 31 + pkg/statistics/handle/updatetest/main_test.go | 33 + .../handle/updatetest/update_test.go | 1310 + pkg/statistics/handle/usage/BUILD.bazel | 37 + .../statistics}/handle/usage/index_usage.go | 8 +- .../handle/usage/predicate_column.go | 16 +- .../handle/usage/session_stats_collect.go | 18 +- .../usage/session_stats_collect_test.go | 0 pkg/statistics/handle/util/BUILD.bazel | 31 + .../statistics}/handle/util/interfaces.go | 8 +- .../statistics}/handle/util/table_info.go | 4 +- pkg/statistics/handle/util/util.go | 215 + {statistics => pkg/statistics}/histogram.go | 28 +- .../statistics}/histogram_bench_test.go | 10 +- .../statistics}/histogram_test.go | 10 +- pkg/statistics/index.go | 226 + pkg/statistics/integration_test.go | 483 + pkg/statistics/main_test.go | 147 + {statistics => pkg/statistics}/row_sampler.go | 18 +- pkg/statistics/sample.go | 318 + pkg/statistics/sample_test.go | 319 + {statistics => pkg/statistics}/scalar.go | 8 +- {statistics => pkg/statistics}/scalar_test.go | 4 +- .../statistics}/statistics_test.go | 24 +- pkg/statistics/table.go | 685 + .../testdata/integration_suite_in.json | 0 .../testdata/integration_suite_out.json | 0 pkg/store/BUILD.bazel | 45 + pkg/store/batch_coprocessor_test.go | 118 + pkg/store/copr/BUILD.bazel | 105 + .../store}/copr/batch_coprocessor.go | 20 +- pkg/store/copr/batch_coprocessor_test.go | 284 + .../store}/copr/batch_request_sender.go | 2 +- pkg/store/copr/copr_test/BUILD.bazel | 23 + pkg/store/copr/copr_test/coprocessor_test.go | 184 + pkg/store/copr/copr_test/main_test.go | 61 + pkg/store/copr/coprocessor.go | 2091 ++ .../store}/copr/coprocessor_cache.go | 2 +- .../store}/copr/coprocessor_cache_test.go | 2 +- pkg/store/copr/coprocessor_test.go | 793 + {store => pkg/store}/copr/key_ranges.go | 2 +- {store => pkg/store}/copr/key_ranges_test.go | 2 +- pkg/store/copr/main_test.go | 46 + pkg/store/copr/metrics/BUILD.bazel | 12 + pkg/store/copr/metrics/metrics.go | 38 + pkg/store/copr/mpp.go | 338 + {store => pkg/store}/copr/mpp_probe.go | 4 +- {store => pkg/store}/copr/mpp_probe_test.go | 0 {store => pkg/store}/copr/region_cache.go | 10 +- pkg/store/copr/store.go | 143 + pkg/store/driver/BUILD.bazel | 67 + pkg/store/driver/backoff/BUILD.bazel | 13 + .../store}/driver/backoff/backoff.go | 4 +- pkg/store/driver/client_test.go | 111 + {store => pkg/store}/driver/config_test.go | 0 pkg/store/driver/error/BUILD.bazel | 33 + pkg/store/driver/error/error.go | 187 + pkg/store/driver/error/error_test.go | 53 + pkg/store/driver/main_test.go | 162 + pkg/store/driver/options/BUILD.bazel | 12 + pkg/store/driver/options/options.go | 41 + .../store}/driver/snap_interceptor_test.go | 4 +- {store => pkg/store}/driver/sql_fail_test.go | 4 +- {store => pkg/store}/driver/tikv_driver.go | 18 +- pkg/store/driver/txn/BUILD.bazel | 70 + .../store}/driver/txn/batch_getter.go | 2 +- .../store}/driver/txn/batch_getter_test.go | 2 +- {store => pkg/store}/driver/txn/binlog.go | 4 +- .../store}/driver/txn/driver_test.go | 2 +- pkg/store/driver/txn/error.go | 258 + pkg/store/driver/txn/main_test.go | 33 + {store => pkg/store}/driver/txn/scanner.go | 2 +- {store => pkg/store}/driver/txn/snapshot.go | 6 +- {store => pkg/store}/driver/txn/txn_driver.go | 16 +- {store => pkg/store}/driver/txn/union_iter.go | 2 +- .../store}/driver/txn/union_iter_test.go | 4 +- .../store}/driver/txn/unionstore_driver.go | 4 +- pkg/store/driver/txn_test.go | 222 + pkg/store/gcworker/BUILD.bazel | 80 + {store => pkg/store}/gcworker/gc_worker.go | 32 +- .../store}/gcworker/gc_worker_test.go | 58 +- pkg/store/gcworker/main_test.go | 44 + pkg/store/helper/BUILD.bazel | 54 + pkg/store/helper/helper.go | 1221 + pkg/store/helper/helper_test.go | 482 + pkg/store/helper/main_test.go | 33 + pkg/store/main_test.go | 33 + pkg/store/mockstore/BUILD.bazel | 56 + pkg/store/mockstore/cluster_test.go | 132 + pkg/store/mockstore/main_test.go | 40 + pkg/store/mockstore/mockcopr/BUILD.bazel | 76 + .../store}/mockstore/mockcopr/aggregate.go | 12 +- pkg/store/mockstore/mockcopr/analyze.go | 294 + .../store}/mockstore/mockcopr/checksum.go | 0 .../mockstore/mockcopr/cop_handler_dag.go | 30 +- .../store}/mockstore/mockcopr/copr_handler.go | 2 +- pkg/store/mockstore/mockcopr/executor.go | 675 + pkg/store/mockstore/mockcopr/executor_test.go | 139 + pkg/store/mockstore/mockcopr/main_test.go | 40 + .../store}/mockstore/mockcopr/rpc_copr.go | 2 +- pkg/store/mockstore/mockcopr/topn.go | 141 + pkg/store/mockstore/mockstorage/BUILD.bazel | 16 + pkg/store/mockstore/mockstorage/storage.go | 139 + pkg/store/mockstore/mockstore.go | 236 + {store => pkg/store}/mockstore/redirector.go | 2 +- pkg/store/mockstore/tikv.go | 40 + {store => pkg/store}/mockstore/tikv_test.go | 2 +- {store => pkg/store}/mockstore/unistore.go | 6 +- pkg/store/mockstore/unistore/BUILD.bazel | 64 + .../mockstore/unistore/client/BUILD.bazel | 9 + .../mockstore/unistore/client/client.go | 0 pkg/store/mockstore/unistore/cluster.go | 151 + .../mockstore/unistore/config/BUILD.bazel | 12 + .../unistore/config/config-template.toml | 0 .../mockstore/unistore/config/config.go | 0 .../mockstore/unistore/cophandler/BUILD.bazel | 87 + .../mockstore/unistore/cophandler/analyze.go | 664 + .../unistore/cophandler/closure_exec.go | 36 +- .../unistore/cophandler/cop_handler.go | 34 +- .../unistore/cophandler/cop_handler_test.go | 24 +- .../unistore/cophandler/main_test.go | 33 + .../mockstore/unistore/cophandler/mpp.go | 680 + .../mockstore/unistore/cophandler/mpp_exec.go | 26 +- .../mockstore/unistore/cophandler/topn.go | 150 + .../mockstore/unistore/lockstore/BUILD.bazel | 35 + .../mockstore/unistore/lockstore/arena.go | 0 .../mockstore/unistore/lockstore/iterator.go | 0 .../mockstore/unistore/lockstore/load_dump.go | 0 .../mockstore/unistore/lockstore/lockstore.go | 0 .../unistore/lockstore/lockstore_test.go | 0 .../mockstore/unistore/lockstore/main_test.go | 33 + pkg/store/mockstore/unistore/main_test.go | 33 + .../mockstore/unistore/metrics/BUILD.bazel | 9 + .../mockstore/unistore/metrics/metrics.go | 0 pkg/store/mockstore/unistore/mock.go | 72 + pkg/store/mockstore/unistore/pd.go | 271 + pkg/store/mockstore/unistore/pd/BUILD.bazel | 18 + .../store}/mockstore/unistore/pd/client.go | 0 .../store}/mockstore/unistore/pd_test.go | 0 .../store}/mockstore/unistore/raw_handler.go | 2 +- .../mockstore/unistore/raw_handler_test.go | 0 .../store}/mockstore/unistore/rpc.go | 6 +- .../mockstore/unistore/server/BUILD.bazel | 18 + pkg/store/mockstore/unistore/server/server.go | 151 + pkg/store/mockstore/unistore/testutil.go | 116 + pkg/store/mockstore/unistore/tikv/BUILD.bazel | 89 + .../unistore/tikv/dbreader/BUILD.bazel | 16 + .../unistore/tikv/dbreader/db_reader.go | 4 +- .../mockstore/unistore/tikv/deadlock.go | 6 +- .../mockstore/unistore/tikv/detector.go | 2 +- .../mockstore/unistore/tikv/detector_test.go | 0 .../mockstore/unistore/tikv/inner_server.go | 4 +- .../unistore/tikv/kverrors/BUILD.bazel | 13 + .../unistore/tikv/kverrors/errors.go | 160 + .../mockstore/unistore/tikv/main_test.go | 33 + .../mockstore/unistore/tikv/mock_region.go | 12 +- pkg/store/mockstore/unistore/tikv/mvcc.go | 1966 ++ .../mockstore/unistore/tikv/mvcc/BUILD.bazel | 20 + .../mockstore/unistore/tikv/mvcc/db_writer.go | 2 +- .../mockstore/unistore/tikv/mvcc/mvcc.go | 187 + .../mockstore/unistore/tikv/mvcc/tikv.go | 131 + .../mockstore/unistore/tikv/mvcc_test.go | 10 +- .../unistore/tikv/pberror/BUILD.bazel | 9 + .../unistore/tikv/pberror/pberror.go | 0 pkg/store/mockstore/unistore/tikv/region.go | 798 + pkg/store/mockstore/unistore/tikv/server.go | 1175 + .../mockstore/unistore/tikv/server_batch.go | 0 .../store}/mockstore/unistore/tikv/util.go | 0 pkg/store/mockstore/unistore/tikv/write.go | 354 + .../unistore/util/lockwaiter/BUILD.bazel | 33 + .../unistore/util/lockwaiter/lockwaiter.go | 2 +- .../util/lockwaiter/lockwaiter_test.go | 2 +- .../unistore/util/lockwaiter/main_test.go | 33 + pkg/store/pdtypes/BUILD.bazel | 21 + {store => pkg/store}/pdtypes/api.go | 0 {store => pkg/store}/pdtypes/config.go | 0 {store => pkg/store}/pdtypes/placement.go | 0 {store => pkg/store}/pdtypes/region_tree.go | 0 {store => pkg/store}/pdtypes/statistics.go | 0 {store => pkg/store}/pdtypes/typeutil.go | 0 pkg/store/store.go | 123 + pkg/store/store_test.go | 875 + pkg/structure/BUILD.bazel | 42 + {structure => pkg/structure}/hash.go | 2 +- pkg/structure/list.go | 246 + pkg/structure/main_test.go | 35 + {structure => pkg/structure}/string.go | 2 +- {structure => pkg/structure}/structure.go | 6 +- .../structure}/structure_test.go | 10 +- pkg/structure/type.go | 127 + pkg/table/BUILD.bazel | 69 + pkg/table/column.go | 742 + pkg/table/column_test.go | 498 + pkg/table/constraint.go | 253 + pkg/table/index.go | 100 + pkg/table/main_test.go | 33 + pkg/table/table.go | 285 + pkg/table/table_test.go | 43 + pkg/table/tables/BUILD.bazel | 116 + pkg/table/tables/cache.go | 361 + pkg/table/tables/cache_test.go | 560 + pkg/table/tables/index.go | 742 + pkg/table/tables/index_test.go | 170 + pkg/table/tables/main_test.go | 33 + .../table}/tables/mutation_checker.go | 24 +- .../table}/tables/mutation_checker_test.go | 22 +- pkg/table/tables/partition.go | 1902 ++ {table => pkg/table}/tables/state_remote.go | 8 +- .../table}/tables/state_remote_test.go | 8 +- pkg/table/tables/tables.go | 2383 ++ pkg/table/tables/tables_test.go | 1710 ++ pkg/table/tables/test/partition/BUILD.bazel | 29 + pkg/table/tables/test/partition/main_test.go | 33 + .../tables/test/partition/partition_test.go | 3049 ++ pkg/table/tables/testutil.go | 36 + pkg/table/temptable/BUILD.bazel | 61 + pkg/table/temptable/ddl.go | 193 + pkg/table/temptable/ddl_test.go | 226 + pkg/table/temptable/infoschema.go | 68 + {table => pkg/table}/temptable/interceptor.go | 12 +- .../table}/temptable/interceptor_test.go | 12 +- pkg/table/temptable/main_test.go | 258 + pkg/tablecodec/BUILD.bazel | 56 + pkg/tablecodec/bench_test.go | 68 + pkg/tablecodec/main_test.go | 33 + pkg/tablecodec/rowindexcodec/BUILD.bazel | 24 + pkg/tablecodec/rowindexcodec/main_test.go | 33 + .../rowindexcodec/rowindexcodec.go | 0 .../rowindexcodec/rowindexcodec_test.go | 0 {tablecodec => pkg/tablecodec}/tablecodec.go | 28 +- .../tablecodec}/tablecodec_test.go | 20 +- pkg/telemetry/BUILD.bazel | 87 + pkg/telemetry/cte_test/BUILD.bazel | 20 + pkg/telemetry/cte_test/cte_test.go | 133 + {telemetry => pkg/telemetry}/data.go | 4 +- .../telemetry}/data_cluster_hardware.go | 4 +- .../telemetry}/data_cluster_hardware_test.go | 0 .../telemetry}/data_cluster_info.go | 4 +- .../telemetry}/data_feature_usage.go | 18 +- .../telemetry}/data_feature_usage_test.go | 22 +- .../telemetry}/data_slow_query.go | 6 +- .../telemetry}/data_telemetry_host_extra.go | 0 {telemetry => pkg/telemetry}/data_window.go | 4 +- .../telemetry}/data_window_test.go | 12 +- {telemetry => pkg/telemetry}/id.go | 0 pkg/telemetry/main_test.go | 49 + {telemetry => pkg/telemetry}/status.go | 0 pkg/telemetry/telemetry.go | 178 + .../telemetry}/telemetry_test.go | 8 +- pkg/telemetry/ttl.go | 215 + {telemetry => pkg/telemetry}/util.go | 0 {telemetry => pkg/telemetry}/util_test.go | 0 pkg/testkit/BUILD.bazel | 63 + {testkit => pkg/testkit}/asynctestkit.go | 8 +- {testkit => pkg/testkit}/dbtestkit.go | 0 pkg/testkit/ddlhelper/BUILD.bazel | 13 + pkg/testkit/ddlhelper/helper.go | 27 + pkg/testkit/external/BUILD.bazel | 16 + pkg/testkit/external/util.go | 72 + .../testkit}/mocksessionmanager.go | 16 +- pkg/testkit/mockstore.go | 256 + {testkit => pkg/testkit}/result.go | 0 {testkit => pkg/testkit}/stepped.go | 6 +- pkg/testkit/testdata/BUILD.bazel | 12 + {testkit => pkg/testkit}/testdata/testdata.go | 2 +- pkg/testkit/testenv/BUILD.bazel | 9 + {testkit => pkg/testkit}/testenv/testenv.go | 2 +- pkg/testkit/testfork/BUILD.bazel | 21 + {testkit => pkg/testkit}/testfork/fork.go | 0 .../testkit}/testfork/fork_test.go | 0 pkg/testkit/testkit.go | 676 + {testkit => pkg/testkit}/testkit_test.go | 0 pkg/testkit/testmain/BUILD.bazel | 12 + {testkit => pkg/testkit}/testmain/bench.go | 0 {testkit => pkg/testkit}/testmain/wrapper.go | 0 pkg/testkit/testsetup/BUILD.bazel | 13 + {testkit => pkg/testkit}/testsetup/bridge.go | 0 pkg/testkit/testutil/BUILD.bazel | 37 + pkg/testkit/testutil/handle.go | 51 + {testkit => pkg/testkit}/testutil/loghook.go | 2 +- {testkit => pkg/testkit}/testutil/require.go | 8 +- .../testkit}/testutil/require_test.go | 0 .../tidb-binlog}/driver/README.md | 0 pkg/tidb-binlog/driver/reader/BUILD.bazel | 31 + .../tidb-binlog}/driver/reader/offset.go | 2 +- .../tidb-binlog}/driver/reader/offset_test.go | 2 +- pkg/tidb-binlog/driver/reader/reader.go | 239 + pkg/tidb-binlog/node/BUILD.bazel | 33 + {tidb-binlog => pkg/tidb-binlog}/node/node.go | 0 .../tidb-binlog}/node/registry.go | 2 +- pkg/tidb-binlog/node/registry_test.go | 111 + .../tidb-binlog}/proto/generate-binlog.sh | 0 pkg/tidb-binlog/proto/go-binlog/BUILD.bazel | 9 + .../proto/go-binlog/secondary_binlog.pb.go | 0 .../proto/proto/secondary_binlog.proto | 0 pkg/tidb-binlog/pump_client/BUILD.bazel | 47 + .../tidb-binlog}/pump_client/bench_test.go | 0 pkg/tidb-binlog/pump_client/client.go | 614 + pkg/tidb-binlog/pump_client/client_test.go | 335 + .../tidb-binlog}/pump_client/pump.go | 2 +- .../tidb-binlog}/pump_client/selector.go | 0 pkg/timer/BUILD.bazel | 28 + {timer => pkg/timer}/README.md | 0 pkg/timer/api/BUILD.bazel | 48 + pkg/timer/api/client.go | 272 + {timer => pkg/timer}/api/client_test.go | 0 {timer => pkg/timer}/api/error.go | 0 {timer => pkg/timer}/api/hook.go | 0 pkg/timer/api/main_test.go | 27 + {timer => pkg/timer}/api/mem_store.go | 2 +- .../timer}/api/schedule_policy_test.go | 0 {timer => pkg/timer}/api/store.go | 0 pkg/timer/api/store_test.go | 345 + pkg/timer/api/timer.go | 281 + {timer => pkg/timer}/api/timer_test.go | 0 pkg/timer/main_test.go | 34 + pkg/timer/metrics/BUILD.bazel | 9 + {timer => pkg/timer}/metrics/metrics.go | 0 pkg/timer/runtime/BUILD.bazel | 53 + pkg/timer/runtime/cache.go | 243 + pkg/timer/runtime/cache_test.go | 477 + pkg/timer/runtime/main_test.go | 183 + {timer => pkg/timer}/runtime/runtime.go | 8 +- {timer => pkg/timer}/runtime/runtime_test.go | 4 +- pkg/timer/runtime/worker.go | 450 + {timer => pkg/timer}/runtime/worker_test.go | 6 +- .../timer}/store_intergartion_test.go | 12 +- pkg/timer/tablestore/BUILD.bazel | 52 + {timer => pkg/timer}/tablestore/notifier.go | 6 +- pkg/timer/tablestore/sql.go | 484 + pkg/timer/tablestore/sql_test.go | 715 + pkg/timer/tablestore/store.go | 435 + pkg/ttl/cache/BUILD.bazel | 71 + {ttl => pkg/ttl}/cache/base.go | 0 {ttl => pkg/ttl}/cache/base_test.go | 0 pkg/ttl/cache/infoschema.go | 112 + pkg/ttl/cache/infoschema_test.go | 73 + pkg/ttl/cache/main_test.go | 33 + pkg/ttl/cache/split_test.go | 826 + pkg/ttl/cache/table.go | 489 + pkg/ttl/cache/table_test.go | 216 + pkg/ttl/cache/task.go | 198 + {ttl => pkg/ttl}/cache/task_test.go | 12 +- {ttl => pkg/ttl}/cache/ttlstatus.go | 6 +- {ttl => pkg/ttl}/cache/ttlstatus_test.go | 8 +- pkg/ttl/client/BUILD.bazel | 32 + {ttl => pkg/ttl}/client/command.go | 2 +- {ttl => pkg/ttl}/client/command_test.go | 0 {ttl => pkg/ttl}/client/notification.go | 2 +- pkg/ttl/metrics/BUILD.bazel | 21 + pkg/ttl/metrics/metrics.go | 257 + {ttl => pkg/ttl}/metrics/metrics_test.go | 0 pkg/ttl/session/BUILD.bazel | 41 + pkg/ttl/session/main_test.go | 33 + pkg/ttl/session/session.go | 184 + pkg/ttl/session/session_test.go | 66 + pkg/ttl/session/sysvar_test.go | 125 + pkg/ttl/sqlbuilder/BUILD.bazel | 46 + pkg/ttl/sqlbuilder/main_test.go | 33 + pkg/ttl/sqlbuilder/sql.go | 482 + pkg/ttl/sqlbuilder/sql_test.go | 949 + pkg/ttl/ttlworker/BUILD.bazel | 112 + {ttl => pkg/ttl}/ttlworker/config.go | 0 {ttl => pkg/ttl}/ttlworker/del.go | 14 +- {ttl => pkg/ttl}/ttlworker/del_test.go | 8 +- pkg/ttl/ttlworker/job.go | 157 + {ttl => pkg/ttl}/ttlworker/job_manager.go | 24 +- .../ttlworker/job_manager_integration_test.go | 66 +- .../ttl}/ttlworker/job_manager_test.go | 14 +- {ttl => pkg/ttl}/ttlworker/scan.go | 16 +- {ttl => pkg/ttl}/ttlworker/scan_test.go | 10 +- pkg/ttl/ttlworker/session.go | 248 + pkg/ttl/ttlworker/session_test.go | 358 + {ttl => pkg/ttl}/ttlworker/task_manager.go | 14 +- .../task_manager_integration_test.go | 18 +- .../ttl}/ttlworker/task_manager_test.go | 8 +- pkg/ttl/ttlworker/timer.go | 281 + {ttl => pkg/ttl}/ttlworker/timer_sync.go | 14 +- {ttl => pkg/ttl}/ttlworker/timer_sync_test.go | 18 +- {ttl => pkg/ttl}/ttlworker/timer_test.go | 6 +- pkg/ttl/ttlworker/worker.go | 141 + pkg/types/BUILD.bazel | 116 + pkg/types/benchmark_test.go | 60 + {types => pkg/types}/binary_literal.go | 2 +- {types => pkg/types}/binary_literal_test.go | 2 +- pkg/types/compare.go | 86 + {types => pkg/types}/compare_test.go | 6 +- {types => pkg/types}/const_test.go | 2 +- pkg/types/context.go | 33 + pkg/types/context/BUILD.bazel | 18 + pkg/types/context/context.go | 166 + {types => pkg/types}/context/context_test.go | 0 pkg/types/convert.go | 782 + {types => pkg/types}/convert_test.go | 8 +- {types => pkg/types}/core_time.go | 0 {types => pkg/types}/core_time_test.go | 0 {types => pkg/types}/datum.go | 16 +- {types => pkg/types}/datum_eval.go | 4 +- {types => pkg/types}/datum_test.go | 10 +- {types => pkg/types}/enum.go | 4 +- {types => pkg/types}/enum_test.go | 2 +- pkg/types/errors.go | 97 + pkg/types/errors_test.go | 55 + pkg/types/etc.go | 208 + pkg/types/etc_test.go | 346 + {types => pkg/types}/eval_type.go | 2 +- {types => pkg/types}/explain_format.go | 0 {types => pkg/types}/export_test.go | 0 {types => pkg/types}/field_name.go | 6 +- pkg/types/field_type.go | 1490 + {types => pkg/types}/field_type_builder.go | 0 pkg/types/field_type_test.go | 466 + pkg/types/format_test.go | 199 + {types => pkg/types}/fsp.go | 0 {types => pkg/types}/fsp_test.go | 0 {types => pkg/types}/helper.go | 0 {types => pkg/types}/helper_test.go | 0 {types => pkg/types}/json_binary.go | 8 +- {types => pkg/types}/json_binary_functions.go | 6 +- .../types}/json_binary_functions_test.go | 0 {types => pkg/types}/json_binary_test.go | 0 {types => pkg/types}/json_constants.go | 4 +- {types => pkg/types}/json_path_expr.go | 4 +- {types => pkg/types}/json_path_expr_test.go | 0 pkg/types/main_test.go | 33 + {types => pkg/types}/mydecimal.go | 6 +- .../types}/mydecimal_benchmark_test.go | 0 {types => pkg/types}/mydecimal_test.go | 0 {types => pkg/types}/overflow.go | 0 {types => pkg/types}/overflow_test.go | 0 pkg/types/parser_driver/BUILD.bazel | 34 + pkg/types/parser_driver/main_test.go | 32 + .../types}/parser_driver/value_expr.go | 10 +- .../types}/parser_driver/value_expr_test.go | 4 +- pkg/types/set.go | 132 + pkg/types/set_test.go | 104 + {types => pkg/types}/time.go | 16 +- {types => pkg/types}/time_test.go | 10 +- pkg/util/BUILD.bazel | 90 + pkg/util/admin/BUILD.bazel | 46 + pkg/util/admin/admin.go | 263 + .../util}/admin/admin_integration_test.go | 4 +- pkg/util/admin/main_test.go | 40 + pkg/util/arena/BUILD.bazel | 24 + {util => pkg/util}/arena/arena.go | 0 {util => pkg/util}/arena/arena_test.go | 0 pkg/util/arena/main_test.go | 32 + pkg/util/backoff/BUILD.bazel | 17 + {util => pkg/util}/backoff/backoff.go | 0 {util => pkg/util}/backoff/backoff_test.go | 0 pkg/util/benchdaily/BUILD.bazel | 23 + {util => pkg/util}/benchdaily/bench_daily.go | 0 .../util}/benchdaily/bench_daily_test.go | 0 pkg/util/benchdaily/main_test.go | 32 + pkg/util/bitmap/BUILD.bazel | 24 + {util => pkg/util}/bitmap/concurrent.go | 0 {util => pkg/util}/bitmap/concurrent_test.go | 0 pkg/util/bitmap/main_test.go | 32 + pkg/util/breakpoint/BUILD.bazel | 13 + {util => pkg/util}/breakpoint/breakpoint.go | 4 +- pkg/util/cgroup/BUILD.bazel | 76 + {util => pkg/util}/cgroup/cgroup.go | 0 {util => pkg/util}/cgroup/cgroup_cpu.go | 0 {util => pkg/util}/cgroup/cgroup_cpu_linux.go | 0 {util => pkg/util}/cgroup/cgroup_cpu_test.go | 0 .../util}/cgroup/cgroup_cpu_unsupport.go | 0 {util => pkg/util}/cgroup/cgroup_memory.go | 0 {util => pkg/util}/cgroup/cgroup_mock_test.go | 0 pkg/util/channel/BUILD.bazel | 8 + {util => pkg/util}/channel/channel.go | 0 pkg/util/checksum/BUILD.bazel | 26 + pkg/util/checksum/checksum.go | 183 + {util => pkg/util}/checksum/checksum_test.go | 2 +- pkg/util/checksum/main_test.go | 32 + pkg/util/chunk/BUILD.bazel | 78 + {util => pkg/util}/chunk/alloc.go | 4 +- {util => pkg/util}/chunk/alloc_test.go | 4 +- {util => pkg/util}/chunk/chunk.go | 4 +- {util => pkg/util}/chunk/chunk_test.go | 8 +- {util => pkg/util}/chunk/chunk_util.go | 0 {util => pkg/util}/chunk/chunk_util_test.go | 2 +- pkg/util/chunk/codec.go | 354 + pkg/util/chunk/codec_test.go | 191 + pkg/util/chunk/column.go | 764 + pkg/util/chunk/column_test.go | 995 + pkg/util/chunk/compare.go | 245 + {util => pkg/util}/chunk/disk.go | 14 +- {util => pkg/util}/chunk/disk_test.go | 8 +- {util => pkg/util}/chunk/iterator.go | 0 {util => pkg/util}/chunk/iterator_test.go | 4 +- pkg/util/chunk/list.go | 179 + {util => pkg/util}/chunk/list_test.go | 6 +- pkg/util/chunk/main_test.go | 49 + {util => pkg/util}/chunk/mutrow.go | 6 +- {util => pkg/util}/chunk/mutrow_test.go | 8 +- {util => pkg/util}/chunk/pool.go | 2 +- pkg/util/chunk/pool_test.go | 110 + {util => pkg/util}/chunk/row.go | 4 +- {util => pkg/util}/chunk/row_container.go | 8 +- .../util}/chunk/row_container_reader.go | 2 +- .../util}/chunk/row_container_test.go | 20 +- pkg/util/codec/BUILD.bazel | 54 + pkg/util/codec/bench_test.go | 117 + {util => pkg/util}/codec/bytes.go | 0 {util => pkg/util}/codec/bytes_test.go | 0 pkg/util/codec/codec.go | 1381 + pkg/util/codec/codec_test.go | 1300 + pkg/util/codec/collation_test.go | 158 + {util => pkg/util}/codec/decimal.go | 4 +- {util => pkg/util}/codec/decimal_test.go | 2 +- {util => pkg/util}/codec/float.go | 0 pkg/util/codec/main_test.go | 32 + {util => pkg/util}/codec/number.go | 0 pkg/util/collate/BUILD.bazel | 51 + {util => pkg/util}/collate/bin.go | 2 +- pkg/util/collate/charset.go | 28 + {util => pkg/util}/collate/collate.go | 10 +- .../util}/collate/collate_bench_test.go | 0 {util => pkg/util}/collate/collate_test.go | 0 {util => pkg/util}/collate/gbk_bin.go | 2 +- {util => pkg/util}/collate/gbk_chinese_ci.go | 2 +- .../util}/collate/gbk_chinese_ci_data.go | 0 {util => pkg/util}/collate/general_ci.go | 2 +- pkg/util/collate/main_test.go | 32 + .../util}/collate/pinyin_tidb_as_cs.go | 0 pkg/util/collate/ucadata/BUILD.bazel | 25 + {util => pkg/util}/collate/ucadata/data.go | 0 .../collate/ucadata/generator/BUILD.bazel | 22 + .../ucadata/generator/allkeys-4.0.0.txt | 0 .../ucadata/generator/allkeys-9.0.0.txt | 0 .../collate/ucadata/generator/data.go.tpl | 0 .../util}/collate/ucadata/generator/magic.go | 0 .../util}/collate/ucadata/generator/main.go | 0 .../unicode_0900_ai_ci_data_generated.go | 0 .../ucadata/unicode_0900_ai_ci_data_test.go | 0 .../ucadata/unicode_ci_data_generated.go | 0 .../ucadata/unicode_ci_data_original_test.go | 0 .../collate/ucadata/unicode_ci_data_test.go | 0 pkg/util/collate/ucaimpl/BUILD.bazel | 15 + {util => pkg/util}/collate/ucaimpl/main.go | 0 .../util}/collate/ucaimpl/unicode_ci.go.tpl | 0 .../collate/unicode_0400_ci_generated.go | 0 .../util}/collate/unicode_0400_ci_impl.go | 4 +- .../collate/unicode_0900_ai_ci_generated.go | 0 .../util}/collate/unicode_0900_ai_ci_impl.go | 4 +- pkg/util/column-mapping/BUILD.bazel | 21 + {util => pkg/util}/column-mapping/README.md | 0 pkg/util/column-mapping/column.go | 539 + .../util}/column-mapping/column_test.go | 0 pkg/util/compress/BUILD.bazel | 9 + {util => pkg/util}/compress/gzip.go | 0 pkg/util/cpu/BUILD.bazel | 39 + pkg/util/cpu/cpu.go | 122 + {util => pkg/util}/cpu/cpu_test.go | 12 +- pkg/util/cpu/main_test.go | 33 + {util => pkg/util}/cpu_posix.go | 0 {util => pkg/util}/cpu_windows.go | 0 pkg/util/cpuprofile/BUILD.bazel | 33 + {util => pkg/util}/cpuprofile/cpuprofile.go | 6 +- .../util}/cpuprofile/cpuprofile_test.go | 4 +- {util => pkg/util}/cpuprofile/pprof_api.go | 4 +- pkg/util/cpuprofile/testutil/BUILD.bazel | 8 + .../util}/cpuprofile/testutil/util.go | 0 pkg/util/cteutil/BUILD.bazel | 34 + pkg/util/cteutil/main_test.go | 32 + pkg/util/cteutil/storage.go | 271 + {util => pkg/util}/cteutil/storage_test.go | 6 +- pkg/util/dbterror/BUILD.bazel | 34 + {util => pkg/util}/dbterror/ddl_terror.go | 4 +- pkg/util/dbterror/exeerrors/BUILD.bazel | 13 + pkg/util/dbterror/exeerrors/errors.go | 102 + pkg/util/dbterror/main_test.go | 31 + pkg/util/dbterror/terror.go | 57 + {util => pkg/util}/dbterror/terror_test.go | 2 +- pkg/util/dbutil/BUILD.bazel | 65 + {util => pkg/util}/dbutil/README.md | 0 pkg/util/dbutil/common.go | 891 + pkg/util/dbutil/common_test.go | 257 + pkg/util/dbutil/index.go | 209 + pkg/util/dbutil/index_test.go | 101 + {util => pkg/util}/dbutil/interface.go | 0 {util => pkg/util}/dbutil/query.go | 0 {util => pkg/util}/dbutil/retry.go | 2 +- {util => pkg/util}/dbutil/retry_test.go | 2 +- pkg/util/dbutil/table.go | 146 + pkg/util/dbutil/table_test.go | 165 + pkg/util/dbutil/types.go | 47 + pkg/util/dbutil/variable.go | 155 + {util => pkg/util}/dbutil/variable_test.go | 0 pkg/util/ddl-checker/BUILD.bazel | 34 + {util => pkg/util}/ddl-checker/ddl_syncer.go | 2 +- .../util}/ddl-checker/executable_checker.go | 10 +- .../ddl-checker/executable_checker_test.go | 2 +- pkg/util/deadlockhistory/BUILD.bazel | 39 + .../util}/deadlockhistory/deadlock_history.go | 8 +- .../deadlockhistory/deadlock_history_test.go | 6 +- pkg/util/deadlockhistory/main_test.go | 32 + pkg/util/disjointset/BUILD.bazel | 24 + {util => pkg/util}/disjointset/int_set.go | 0 .../util}/disjointset/int_set_test.go | 0 pkg/util/disjointset/main_test.go | 32 + pkg/util/disk/BUILD.bazel | 38 + pkg/util/disk/main_test.go | 32 + {util => pkg/util}/disk/tempDir.go | 4 +- {util => pkg/util}/disk/tempDir_test.go | 2 +- pkg/util/disk/tracker.go | 30 + pkg/util/distrole/BUILD.bazel | 8 + {util => pkg/util}/distrole/role.go | 0 pkg/util/disttask/BUILD.bazel | 18 + {util => pkg/util}/disttask/idservice.go | 2 +- {util => pkg/util}/disttask/idservice_test.go | 0 pkg/util/domainutil/BUILD.bazel | 9 + {util => pkg/util}/domainutil/repair_vars.go | 2 +- pkg/util/encrypt/BUILD.bazel | 32 + {util => pkg/util}/encrypt/aes.go | 0 {util => pkg/util}/encrypt/aes_layer.go | 0 {util => pkg/util}/encrypt/aes_layer_test.go | 2 +- {util => pkg/util}/encrypt/aes_test.go | 0 {util => pkg/util}/encrypt/crypt.go | 0 {util => pkg/util}/encrypt/crypt_test.go | 0 pkg/util/encrypt/main_test.go | 28 + pkg/util/engine/BUILD.bazel | 9 + {util => pkg/util}/engine/engine.go | 0 {util => pkg/util}/errors.go | 0 pkg/util/errors_test.go | 36 + {util => pkg/util}/etcd.go | 6 +- pkg/util/etcd/BUILD.bazel | 27 + {util => pkg/util}/etcd/etcd.go | 0 {util => pkg/util}/etcd/etcd_test.go | 0 pkg/util/execdetails/BUILD.bazel | 33 + {util => pkg/util}/execdetails/execdetails.go | 0 .../util}/execdetails/execdetails_test.go | 0 pkg/util/execdetails/main_test.go | 31 + pkg/util/expensivequery/BUILD.bazel | 29 + .../expensivequery/expensivequerey_test.go | 2 +- .../util}/expensivequery/expensivequery.go | 8 +- pkg/util/extsort/BUILD.bazel | 42 + {util => pkg/util}/extsort/disk_sorter.go | 4 +- .../util}/extsort/disk_sorter_test.go | 0 {util => pkg/util}/extsort/external_sorter.go | 0 .../util}/extsort/external_sorter_test.go | 0 pkg/util/fastrand/BUILD.bazel | 24 + pkg/util/fastrand/main_test.go | 32 + {util => pkg/util}/fastrand/random.go | 0 {util => pkg/util}/fastrand/random_test.go | 0 pkg/util/filter/BUILD.bazel | 28 + {util => pkg/util}/filter/README.md | 0 {util => pkg/util}/filter/filter.go | 4 +- {util => pkg/util}/filter/filter_test.go | 0 {util => pkg/util}/filter/schema.go | 0 {util => pkg/util}/filter/schema_test.go | 0 pkg/util/format/BUILD.bazel | 24 + {util => pkg/util}/format/format.go | 0 {util => pkg/util}/format/format_test.go | 0 pkg/util/format/main_test.go | 32 + pkg/util/gctuner/BUILD.bazel | 40 + {util => pkg/util}/gctuner/finalizer.go | 0 {util => pkg/util}/gctuner/finalizer_test.go | 0 {util => pkg/util}/gctuner/mem.go | 2 +- {util => pkg/util}/gctuner/mem_test.go | 0 .../util}/gctuner/memory_limit_tuner.go | 6 +- .../util}/gctuner/memory_limit_tuner_test.go | 6 +- {util => pkg/util}/gctuner/tuner.go | 2 +- {util => pkg/util}/gctuner/tuner_test.go | 0 pkg/util/gcutil/BUILD.bazel | 18 + {util => pkg/util}/gcutil/gcutil.go | 10 +- pkg/util/generatedexpr/BUILD.bazel | 34 + .../util}/generatedexpr/gen_expr_test.go | 4 +- .../util}/generatedexpr/generated_expr.go | 10 +- pkg/util/generatedexpr/main_test.go | 32 + pkg/util/generic/BUILD.bazel | 19 + {util => pkg/util}/generic/sync_map.go | 0 {util => pkg/util}/generic/sync_map_test.go | 2 +- pkg/util/globalconn/BUILD.bazel | 32 + {util => pkg/util}/globalconn/globalconn.go | 2 +- .../util}/globalconn/globalconn_test.go | 2 +- {util => pkg/util}/globalconn/pool.go | 0 pkg/util/globalconn/pool_test.go | 500 + {util => pkg/util}/gogc.go | 2 +- pkg/util/hack/BUILD.bazel | 23 + {util => pkg/util}/hack/hack.go | 0 {util => pkg/util}/hack/hack_test.go | 0 pkg/util/hack/main_test.go | 32 + pkg/util/hint/BUILD.bazel | 20 + {util => pkg/util}/hint/hint_processor.go | 16 +- {util => pkg/util}/id_generator.go | 0 pkg/util/importer/BUILD.bazel | 28 + pkg/util/importer/config.go | 39 + {util => pkg/util}/importer/data.go | 0 {util => pkg/util}/importer/db.go | 4 +- {util => pkg/util}/importer/importer.go | 0 {util => pkg/util}/importer/job.go | 0 pkg/util/importer/parser.go | 275 + {util => pkg/util}/importer/rand.go | 0 pkg/util/intest/BUILD.bazel | 23 + {util => pkg/util}/intest/assert.go | 0 {util => pkg/util}/intest/assert_test.go | 2 +- {util => pkg/util}/intest/common.go | 0 {util => pkg/util}/intest/intest.go | 0 pkg/util/israce/BUILD.bazel | 11 + {util => pkg/util}/israce/israce.go | 0 {util => pkg/util}/israce/norace.go | 0 pkg/util/keydecoder/BUILD.bazel | 43 + {util => pkg/util}/keydecoder/keydecoder.go | 10 +- .../util}/keydecoder/keydecoder_test.go | 18 +- pkg/util/keydecoder/main_test.go | 32 + pkg/util/kvcache/BUILD.bazel | 29 + pkg/util/kvcache/main_test.go | 32 + {util => pkg/util}/kvcache/simple_lru.go | 4 +- {util => pkg/util}/kvcache/simple_lru_test.go | 2 +- pkg/util/localpool/BUILD.bazel | 29 + {util => pkg/util}/localpool/localpool.go | 0 .../util}/localpool/localpool_norace.go | 0 .../util}/localpool/localpool_race.go | 0 .../util}/localpool/localpool_test.go | 2 +- pkg/util/localpool/main_test.go | 32 + pkg/util/logutil/BUILD.bazel | 49 + pkg/util/logutil/consistency/BUILD.bazel | 22 + pkg/util/logutil/consistency/reporter.go | 294 + {util => pkg/util}/logutil/hex.go | 0 {util => pkg/util}/logutil/hex_test.go | 4 +- {util => pkg/util}/logutil/log.go | 2 +- {util => pkg/util}/logutil/log_test.go | 2 +- pkg/util/logutil/main_test.go | 51 + .../util}/logutil/slow_query_logger.go | 0 pkg/util/main_test.go | 32 + pkg/util/mathutil/BUILD.bazel | 31 + .../util}/mathutil/exponential_average.go | 0 .../mathutil/exponential_average_test.go | 0 pkg/util/mathutil/main_test.go | 32 + {util => pkg/util}/mathutil/math.go | 0 {util => pkg/util}/mathutil/math_test.go | 0 {util => pkg/util}/mathutil/rand.go | 0 {util => pkg/util}/mathutil/rand_test.go | 0 pkg/util/memory/BUILD.bazel | 47 + {util => pkg/util}/memory/action.go | 6 +- {util => pkg/util}/memory/bench_test.go | 0 pkg/util/memory/main_test.go | 28 + {util => pkg/util}/memory/meminfo.go | 6 +- {util => pkg/util}/memory/memstats.go | 0 pkg/util/memory/tracker.go | 868 + {util => pkg/util}/memory/tracker_test.go | 6 +- pkg/util/memoryusagealarm/BUILD.bazel | 34 + .../memoryusagealarm/memoryusagealarm.go | 12 +- .../memoryusagealarm/memoryusagealarm_test.go | 8 +- pkg/util/metricsutil/BUILD.bazel | 31 + pkg/util/metricsutil/common.go | 144 + pkg/util/misc.go | 696 + pkg/util/misc_test.go | 215 + pkg/util/mock/BUILD.bazel | 57 + pkg/util/mock/client.go | 33 + pkg/util/mock/context.go | 490 + {util => pkg/util}/mock/iter.go | 2 +- {util => pkg/util}/mock/iter_test.go | 2 +- pkg/util/mock/main_test.go | 33 + {util => pkg/util}/mock/metrics.go | 0 {util => pkg/util}/mock/mock_test.go | 0 pkg/util/mock/store.go | 87 + pkg/util/mvmap/BUILD.bazel | 29 + {util => pkg/util}/mvmap/bench_test.go | 0 {util => pkg/util}/mvmap/fnv.go | 0 pkg/util/mvmap/main_test.go | 32 + {util => pkg/util}/mvmap/mvmap.go | 2 +- {util => pkg/util}/mvmap/mvmap_test.go | 0 pkg/util/nocopy/BUILD.bazel | 8 + {util => pkg/util}/nocopy/nocopy.go | 0 pkg/util/paging/BUILD.bazel | 24 + pkg/util/paging/main_test.go | 32 + {util => pkg/util}/paging/paging.go | 0 {util => pkg/util}/paging/paging_test.go | 0 pkg/util/parser/BUILD.bazel | 37 + pkg/util/parser/ast.go | 140 + {util => pkg/util}/parser/ast_test.go | 6 +- pkg/util/parser/main_test.go | 32 + {util => pkg/util}/parser/parser.go | 0 pkg/util/parser/parser_test.go | 170 + pkg/util/password-validation/BUILD.bazel | 25 + .../password_validation.go | 4 +- .../password_validation_test.go | 4 +- pkg/util/pdapi/BUILD.bazel | 8 + {util => pkg/util}/pdapi/const.go | 0 pkg/util/plancache/BUILD.bazel | 9 + pkg/util/plancache/util.go | 36 + pkg/util/plancodec/BUILD.bazel | 41 + .../util}/plancodec/binary_plan_decode.go | 4 +- pkg/util/plancodec/codec.go | 448 + pkg/util/plancodec/codec_test.go | 57 + {util => pkg/util}/plancodec/id.go | 0 {util => pkg/util}/plancodec/id_test.go | 0 pkg/util/plancodec/main_test.go | 32 + {util => pkg/util}/prefix_helper.go | 2 +- {util => pkg/util}/prefix_helper_test.go | 8 +- {util => pkg/util}/printer.go | 0 pkg/util/printer/BUILD.bazel | 32 + pkg/util/printer/main_test.go | 32 + {util => pkg/util}/printer/printer.go | 10 +- {util => pkg/util}/printer/printer_test.go | 0 {util => pkg/util}/processinfo.go | 14 +- pkg/util/profile/BUILD.bazel | 45 + {util => pkg/util}/profile/flamegraph.go | 4 +- {util => pkg/util}/profile/flamegraph_test.go | 4 +- pkg/util/profile/main_test.go | 32 + {util => pkg/util}/profile/profile.go | 6 +- {util => pkg/util}/profile/profile_test.go | 12 +- .../util}/profile/testdata/test.pprof | Bin pkg/util/promutil/BUILD.bazel | 24 + {util => pkg/util}/promutil/factory.go | 0 {util => pkg/util}/promutil/registry.go | 0 {util => pkg/util}/promutil/registry_test.go | 0 pkg/util/ranger/BUILD.bazel | 67 + pkg/util/ranger/bench_test.go | 133 + pkg/util/ranger/checker.go | 222 + {util => pkg/util}/ranger/detacher.go | 22 +- pkg/util/ranger/main_test.go | 47 + {util => pkg/util}/ranger/points.go | 18 +- {util => pkg/util}/ranger/ranger.go | 26 +- {util => pkg/util}/ranger/ranger_test.go | 24 +- pkg/util/ranger/types.go | 280 + {util => pkg/util}/ranger/types_test.go | 10 +- pkg/util/regexpr-router/BUILD.bazel | 26 + .../util}/regexpr-router/regexpr_router.go | 4 +- .../regexpr-router/regexpr_router_test.go | 4 +- pkg/util/replayer/BUILD.bazel | 13 + {util => pkg/util}/replayer/replayer.go | 2 +- pkg/util/resourcegrouptag/BUILD.bazel | 39 + pkg/util/resourcegrouptag/main_test.go | 32 + .../resourcegrouptag/resource_group_tag.go | 4 +- .../resource_group_tag_test.go | 4 +- {util => pkg/util}/rlimit_other.go | 2 +- {util => pkg/util}/rlimit_windows.go | 0 pkg/util/rowDecoder/BUILD.bazel | 50 + pkg/util/rowDecoder/decoder.go | 205 + {util => pkg/util}/rowDecoder/decoder_test.go | 28 +- pkg/util/rowDecoder/main_test.go | 32 + pkg/util/rowcodec/BUILD.bazel | 52 + pkg/util/rowcodec/bench_test.go | 122 + pkg/util/rowcodec/common.go | 356 + pkg/util/rowcodec/decoder.go | 524 + {util => pkg/util}/rowcodec/encoder.go | 10 +- pkg/util/rowcodec/main_test.go | 64 + {util => pkg/util}/rowcodec/row.go | 0 {util => pkg/util}/rowcodec/rowcodec_test.go | 20 +- pkg/util/schemacmp/BUILD.bazel | 46 + {util => pkg/util}/schemacmp/lattice.go | 8 +- {util => pkg/util}/schemacmp/lattice_test.go | 4 +- pkg/util/schemacmp/table.go | 429 + pkg/util/schemacmp/table_test.go | 517 + pkg/util/schemacmp/type.go | 214 + {util => pkg/util}/schemacmp/type_test.go | 6 +- pkg/util/schemacmp/util.go | 69 + {util => pkg/util}/security.go | 0 {util => pkg/util}/security_test.go | 2 +- pkg/util/selection/BUILD.bazel | 24 + pkg/util/selection/main_test.go | 32 + {util => pkg/util}/selection/selection.go | 0 .../util}/selection/selection_test.go | 0 pkg/util/sem/BUILD.bazel | 31 + pkg/util/sem/main_test.go | 33 + {util => pkg/util}/sem/sem.go | 6 +- {util => pkg/util}/sem/sem_test.go | 4 +- pkg/util/servermemorylimit/BUILD.bazel | 32 + .../servermemorylimit/servermemorylimit.go | 10 +- .../servermemorylimit_test.go | 4 +- pkg/util/set/BUILD.bazel | 39 + {util => pkg/util}/set/float64_set.go | 0 {util => pkg/util}/set/float64_set_test.go | 0 {util => pkg/util}/set/int_set.go | 0 {util => pkg/util}/set/int_set_test.go | 0 pkg/util/set/main_test.go | 32 + {util => pkg/util}/set/mem_aware_map.go | 2 +- {util => pkg/util}/set/mem_aware_map_test.go | 0 .../util}/set/set_with_memory_usage.go | 4 +- .../util}/set/set_with_memory_usage_test.go | 0 {util => pkg/util}/set/string_set.go | 0 {util => pkg/util}/set/string_set_test.go | 0 pkg/util/signal/BUILD.bazel | 63 + {util => pkg/util}/signal/signal_posix.go | 2 +- {util => pkg/util}/signal/signal_wasm.go | 0 {util => pkg/util}/signal/signal_windows.go | 2 +- pkg/util/size/BUILD.bazel | 8 + {util => pkg/util}/size/size.go | 0 pkg/util/skip/BUILD.bazel | 8 + {util => pkg/util}/skip/skip.go | 0 pkg/util/sli/BUILD.bazel | 12 + {util => pkg/util}/sli/sli.go | 2 +- pkg/util/slice/BUILD.bazel | 24 + pkg/util/slice/main_test.go | 32 + {util => pkg/util}/slice/slice.go | 0 {util => pkg/util}/slice/slice_test.go | 0 pkg/util/sqlexec/BUILD.bazel | 39 + pkg/util/sqlexec/main_test.go | 33 + pkg/util/sqlexec/mock/BUILD.bazel | 14 + .../mock/restricted_sql_executor_mock.go | 8 +- .../util}/sqlexec/restricted_sql_executor.go | 10 +- .../util}/sqlexec/simple_record_set.go | 6 +- {util => pkg/util}/sqlexec/utils.go | 2 +- {util => pkg/util}/sqlexec/utils_test.go | 0 pkg/util/stmtsummary/BUILD.bazel | 59 + {util => pkg/util}/stmtsummary/evicted.go | 4 +- .../util}/stmtsummary/evicted_test.go | 4 +- pkg/util/stmtsummary/main_test.go | 32 + pkg/util/stmtsummary/reader.go | 635 + .../util}/stmtsummary/statement_summary.go | 12 +- .../stmtsummary/statement_summary_test.go | 16 +- pkg/util/stmtsummary/v2/BUILD.bazel | 61 + pkg/util/stmtsummary/v2/column.go | 522 + pkg/util/stmtsummary/v2/column_test.go | 82 + {util => pkg/util}/stmtsummary/v2/logger.go | 2 +- pkg/util/stmtsummary/v2/main_test.go | 33 + pkg/util/stmtsummary/v2/reader.go | 862 + .../util}/stmtsummary/v2/reader_test.go | 10 +- {util => pkg/util}/stmtsummary/v2/record.go | 8 +- .../util}/stmtsummary/v2/record_test.go | 0 pkg/util/stmtsummary/v2/stmtsummary.go | 613 + .../util}/stmtsummary/v2/stmtsummary_test.go | 0 pkg/util/stmtsummary/v2/tests/BUILD.bazel | 25 + pkg/util/stmtsummary/v2/tests/main_test.go | 34 + pkg/util/stmtsummary/v2/tests/table_test.go | 570 + pkg/util/stringutil/BUILD.bazel | 29 + pkg/util/stringutil/main_test.go | 32 + {util => pkg/util}/stringutil/string_util.go | 4 +- .../util}/stringutil/string_util_test.go | 0 pkg/util/syncutil/BUILD.bazel | 12 + {util => pkg/util}/syncutil/mutex_deadlock.go | 0 {util => pkg/util}/syncutil/mutex_sync.go | 0 pkg/util/sys/linux/BUILD.bazel | 70 + pkg/util/sys/linux/main_test.go | 32 + {util => pkg/util}/sys/linux/sys_linux.go | 0 {util => pkg/util}/sys/linux/sys_other.go | 0 pkg/util/sys/linux/sys_test.go | 27 + {util => pkg/util}/sys/linux/sys_windows.go | 0 pkg/util/sys/storage/BUILD.bazel | 34 + pkg/util/sys/storage/main_test.go | 32 + {util => pkg/util}/sys/storage/sys_other.go | 0 {util => pkg/util}/sys/storage/sys_posix.go | 0 pkg/util/sys/storage/sys_test.go | 30 + {util => pkg/util}/sys/storage/sys_windows.go | 0 pkg/util/systimemon/BUILD.bazel | 29 + pkg/util/systimemon/main_test.go | 34 + {util => pkg/util}/systimemon/systime_mon.go | 2 +- .../util}/systimemon/systime_mon_test.go | 0 pkg/util/table-filter/BUILD.bazel | 30 + {util => pkg/util}/table-filter/README.md | 0 .../util}/table-filter/column_filter.go | 0 .../util}/table-filter/column_filter_test.go | 2 +- {util => pkg/util}/table-filter/compat.go | 0 .../util}/table-filter/compat_test.go | 2 +- {util => pkg/util}/table-filter/matchers.go | 0 {util => pkg/util}/table-filter/parser.go | 0 .../util}/table-filter/table_filter.go | 0 .../util}/table-filter/table_filter_test.go | 2 +- pkg/util/table-router/BUILD.bazel | 24 + {util => pkg/util}/table-router/router.go | 2 +- .../util}/table-router/router_test.go | 2 +- pkg/util/table-rule-selector/BUILD.bazel | 18 + .../table-rule-selector/selector_test.go | 0 .../table-rule-selector/trie_selector.go | 0 pkg/util/tableutil/BUILD.bazel | 12 + {util => pkg/util}/tableutil/tableutil.go | 4 +- pkg/util/texttree/BUILD.bazel | 24 + pkg/util/texttree/main_test.go | 32 + {util => pkg/util}/texttree/texttree.go | 0 {util => pkg/util}/texttree/texttree_test.go | 2 +- pkg/util/tiflash/BUILD.bazel | 8 + .../util}/tiflash/tiflash_replica_read.go | 0 pkg/util/tiflashcompute/BUILD.bazel | 18 + .../util}/tiflashcompute/dispatch_policy.go | 0 .../util}/tiflashcompute/topo_fetcher.go | 6 +- pkg/util/tikvutil/BUILD.bazel | 9 + {util => pkg/util}/tikvutil/tikvutil.go | 0 pkg/util/timeutil/BUILD.bazel | 37 + pkg/util/timeutil/errors.go | 23 + pkg/util/timeutil/main_test.go | 32 + {util => pkg/util}/timeutil/time.go | 0 {util => pkg/util}/timeutil/time_test.go | 0 {util => pkg/util}/timeutil/time_zone.go | 4 +- {util => pkg/util}/timeutil/time_zone_test.go | 0 pkg/util/tls/BUILD.bazel | 9 + {util => pkg/util}/tls/tls.go | 0 pkg/util/topsql/BUILD.bazel | 49 + pkg/util/topsql/collector/BUILD.bazel | 33 + pkg/util/topsql/collector/cpu.go | 266 + pkg/util/topsql/collector/main_test.go | 110 + pkg/util/topsql/collector/mock/BUILD.bazel | 17 + pkg/util/topsql/collector/mock/mock.go | 221 + pkg/util/topsql/main_test.go | 32 + pkg/util/topsql/reporter/BUILD.bazel | 61 + .../util}/topsql/reporter/datamodel.go | 12 +- .../util}/topsql/reporter/datamodel_test.go | 4 +- .../util}/topsql/reporter/datasink.go | 2 +- .../util}/topsql/reporter/datasink_test.go | 0 pkg/util/topsql/reporter/main_test.go | 32 + pkg/util/topsql/reporter/metrics/BUILD.bazel | 12 + pkg/util/topsql/reporter/metrics/metrics.go | 64 + pkg/util/topsql/reporter/mock/BUILD.bazel | 17 + pkg/util/topsql/reporter/mock/pubsub.go | 67 + pkg/util/topsql/reporter/mock/server.go | 241 + pkg/util/topsql/reporter/pubsub.go | 268 + .../util}/topsql/reporter/pubsub_test.go | 0 pkg/util/topsql/reporter/reporter.go | 333 + .../util}/topsql/reporter/reporter_test.go | 6 +- .../util}/topsql/reporter/single_target.go | 6 +- .../topsql/reporter/single_target_test.go | 4 +- pkg/util/topsql/state/BUILD.bazel | 9 + {util => pkg/util}/topsql/state/state.go | 0 pkg/util/topsql/stmtstats/BUILD.bazel | 40 + .../util}/topsql/stmtstats/aggregator.go | 2 +- .../util}/topsql/stmtstats/aggregator_test.go | 2 +- .../util}/topsql/stmtstats/kv_exec_count.go | 2 +- .../topsql/stmtstats/kv_exec_count_test.go | 2 +- pkg/util/topsql/stmtstats/main_test.go | 32 + .../util}/topsql/stmtstats/stmtstats.go | 0 .../util}/topsql/stmtstats/stmtstats_test.go | 0 {util => pkg/util}/topsql/topsql.go | 12 +- {util => pkg/util}/topsql/topsql_test.go | 20 +- pkg/util/tracing/BUILD.bazel | 37 + pkg/util/tracing/main_test.go | 32 + {util => pkg/util}/tracing/noop_bench_test.go | 0 {util => pkg/util}/tracing/opt_trace.go | 0 {util => pkg/util}/tracing/opt_trace_test.go | 0 pkg/util/tracing/util.go | 134 + pkg/util/tracing/util_test.go | 152 + pkg/util/trxevents/BUILD.bazel | 9 + {util => pkg/util}/trxevents/trx_events.go | 0 {util => pkg/util}/urls.go | 0 {util => pkg/util}/urls_test.go | 0 pkg/util/util.go | 298 + pkg/util/util_test.go | 116 + pkg/util/versioninfo/BUILD.bazel | 8 + {util => pkg/util}/versioninfo/versioninfo.go | 0 pkg/util/vitess/BUILD.bazel | 25 + pkg/util/vitess/main_test.go | 32 + {util => pkg/util}/vitess/vitess_hash.go | 0 {util => pkg/util}/vitess/vitess_hash_test.go | 0 {util => pkg/util}/wait_group_wrapper.go | 2 +- {util => pkg/util}/wait_group_wrapper_test.go | 0 pkg/util/watcher/BUILD.bazel | 24 + {util => pkg/util}/watcher/event.go | 0 {util => pkg/util}/watcher/watcher.go | 0 {util => pkg/util}/watcher/watcher_test.go | 0 pkg/util/zeropool/BUILD.bazel | 20 + {util => pkg/util}/zeropool/pool.go | 0 pkg/util/zeropool/pool_test.go | 178 + planner/BUILD.bazel | 34 - planner/cardinality/BUILD.bazel | 93 - planner/cardinality/join.go | 51 - planner/cardinality/main_test.go | 65 - planner/cardinality/trace.go | 306 - planner/cardinality/trace_test.go | 262 - planner/cascades/BUILD.bazel | 63 - planner/cascades/integration_test.go | 59 - planner/cascades/main_test.go | 64 - planner/cascades/optimize.go | 383 - planner/cascades/optimize_test.go | 237 - planner/cascades/stringer.go | 119 - planner/cascades/stringer_test.go | 83 - planner/core/BUILD.bazel | 289 - planner/core/binary_plan_test.go | 428 - planner/core/casetest/BUILD.bazel | 32 - planner/core/casetest/binaryplan/BUILD.bazel | 24 - .../casetest/binaryplan/binary_plan_test.go | 141 - planner/core/casetest/binaryplan/main_test.go | 58 - planner/core/casetest/cbotest/BUILD.bazel | 31 - planner/core/casetest/cbotest/cbo_test.go | 633 - planner/core/casetest/cbotest/main_test.go | 52 - planner/core/casetest/dag/BUILD.bazel | 31 - planner/core/casetest/dag/main_test.go | 52 - planner/core/casetest/enforcempp/BUILD.bazel | 27 - .../casetest/enforcempp/enforce_mpp_test.go | 727 - planner/core/casetest/enforcempp/main_test.go | 53 - planner/core/casetest/flatplan/BUILD.bazel | 27 - planner/core/casetest/flatplan/main_test.go | 59 - planner/core/casetest/hint/BUILD.bazel | 26 - planner/core/casetest/hint/main_test.go | 59 - planner/core/casetest/index/BUILD.bazel | 23 - planner/core/casetest/index/index_test.go | 419 - planner/core/casetest/index/main_test.go | 54 - planner/core/casetest/integration_test.go | 441 - planner/core/casetest/main_test.go | 69 - planner/core/casetest/mpp/BUILD.bazel | 25 - planner/core/casetest/mpp/main_test.go | 59 - planner/core/casetest/partition/BUILD.bazel | 26 - .../partition/integration_partition_test.go | 248 - planner/core/casetest/partition/main_test.go | 62 - .../casetest/physicalplantest/BUILD.bazel | 37 - .../casetest/physicalplantest/main_test.go | 52 - .../physicalplantest/physical_plan_test.go | 2243 -- planner/core/casetest/plan_test.go | 234 - planner/core/casetest/planstats/BUILD.bazel | 34 - planner/core/casetest/planstats/main_test.go | 54 - planner/core/casetest/pushdown/BUILD.bazel | 23 - planner/core/casetest/pushdown/main_test.go | 54 - planner/core/casetest/rule/BUILD.bazel | 33 - planner/core/casetest/rule/main_test.go | 57 - .../casetest/rule/rule_join_reorder_test.go | 114 - .../core/casetest/scalarsubquery/BUILD.bazel | 20 - .../core/casetest/scalarsubquery/main_test.go | 52 - planner/core/casetest/stats_test.go | 153 - planner/core/casetest/windows/BUILD.bazel | 24 - planner/core/casetest/windows/main_test.go | 52 - planner/core/cbo_test.go | 235 - planner/core/debugtrace.go | 259 - planner/core/enforce_mpp_test.go | 62 - planner/core/errors.go | 122 - planner/core/errors_test.go | 91 - planner/core/explain.go | 1112 - planner/core/expression_test.go | 334 - planner/core/foreign_key.go | 499 - planner/core/integration_partition_test.go | 315 - planner/core/integration_test.go | 2570 -- planner/core/internal/BUILD.bazel | 22 - planner/core/internal/base/BUILD.bazel | 16 - planner/core/internal/base/plan.go | 138 - planner/core/internal/testkit.go | 78 - planner/core/internal/util.go | 31 - planner/core/issuetest/BUILD.bazel | 24 - planner/core/issuetest/main_test.go | 41 - planner/core/main_test.go | 64 - planner/core/metrics/BUILD.bazel | 12 - planner/core/metrics/metrics.go | 82 - planner/core/mock.go | 611 - planner/core/physical_plan_test.go | 471 - planner/core/plan.go | 855 - planner/core/plan_test.go | 763 - planner/core/rule_join_reorder_test.go | 70 - planner/core/stats.go | 1042 - planner/core/stringer.go | 378 - planner/core/stringer_test.go | 127 - planner/core/task.go | 2747 -- planner/core/telemetry.go | 55 - planner/core/tests/prepare/BUILD.bazel | 32 - planner/core/tests/prepare/issue/BUILD.bazel | 18 - planner/core/tests/prepare/issue/main_test.go | 33 - planner/core/tests/prepare/main_test.go | 33 - planner/core/trace.go | 31 - planner/core/util.go | 474 - planner/funcdep/BUILD.bazel | 46 - planner/funcdep/main_test.go | 33 - planner/implementation/BUILD.bazel | 43 - planner/implementation/base.go | 66 - planner/implementation/base_test.go | 43 - planner/implementation/join.go | 74 - planner/implementation/main_test.go | 32 - planner/implementation/sort.go | 65 - planner/memo/BUILD.bazel | 48 - planner/memo/group.go | 272 - planner/memo/main_test.go | 33 - planner/optimize.go | 903 - planner/property/BUILD.bazel | 23 - planner/util/BUILD.bazel | 45 - planner/util/debugtrace/BUILD.bazel | 9 - planner/util/debugtrace/base.go | 162 - planner/util/fixcontrol/BUILD.bazel | 33 - planner/util/fixcontrol/main_test.go | 51 - planner/util/main_test.go | 33 - planner/util/misc.go | 61 - plugin/BUILD.bazel | 54 - plugin/conn_ip_example/BUILD.bazel | 30 - plugin/conn_ip_example/main_test.go | 33 - plugin/errors.go | 29 - plugin/integration_test.go | 786 - plugin/main_test.go | 36 - privilege/BUILD.bazel | 16 - privilege/conn/BUILD.bazel | 8 - privilege/privileges/BUILD.bazel | 88 - privilege/privileges/cache.go | 1659 -- privilege/privileges/cache_test.go | 574 - privilege/privileges/errors.go | 31 - privilege/privileges/ldap/BUILD.bazel | 33 - privilege/privileges/main_test.go | 41 - privilege/privileges/privileges_test.go | 2117 -- resourcemanager/BUILD.bazel | 33 - resourcemanager/pool/BUILD.bazel | 9 - resourcemanager/pool/spool/BUILD.bazel | 43 - resourcemanager/pool/spool/main_test.go | 33 - resourcemanager/pool/workerpool/BUILD.bazel | 37 - resourcemanager/pool/workerpool/main_test.go | 33 - resourcemanager/poolmanager/BUILD.bazel | 13 - resourcemanager/scheduler/BUILD.bazel | 15 - resourcemanager/scheduler/scheduler.go | 36 - resourcemanager/util/BUILD.bazel | 29 - server/BUILD.bazel | 187 - server/err/BUILD.bazel | 12 - server/err/error.go | 47 - server/extension.go | 254 - server/extract.go | 26 - server/handler/BUILD.bazel | 36 - server/handler/extactorhandler/BUILD.bazel | 48 - .../handler/extactorhandler/extract_test.go | 125 - server/handler/extactorhandler/main_test.go | 73 - server/handler/optimizor/BUILD.bazel | 74 - server/handler/optimizor/main_test.go | 73 - server/handler/optimizor/plan_replayer.go | 375 - .../handler/optimizor/plan_replayer_test.go | 452 - server/handler/tests/BUILD.bazel | 60 - server/handler/tests/main_test.go | 73 - server/handler/tikv_handler.go | 278 - server/handler/tikvhandler/BUILD.bazel | 44 - server/handler/tikvhandler/tikv_handler.go | 2023 -- server/handler/ttlhandler/BUILD.bazel | 19 - server/handler/ttlhandler/ttl.go | 73 - server/handler/util.go | 108 - server/internal/BUILD.bazel | 34 - server/internal/column/BUILD.bazel | 46 - server/internal/column/column.go | 250 - server/internal/column/column_test.go | 252 - server/internal/column/convert.go | 91 - server/internal/dump/BUILD.bazel | 26 - server/internal/dump/dump.go | 168 - server/internal/dump/dump_test.go | 125 - server/internal/handshake/BUILD.bazel | 9 - server/internal/parse/BUILD.bazel | 45 - server/internal/resultset/BUILD.bazel | 18 - server/internal/testserverclient/BUILD.bazel | 23 - server/internal/testutil/BUILD.bazel | 8 - server/internal/util/BUILD.bazel | 25 - server/internal/util/util.go | 230 - server/main_test.go | 84 - server/metrics/BUILD.bazel | 13 - server/metrics/metrics.go | 95 - server/server.go | 1095 - server/stat.go | 76 - server/stat_test.go | 65 - server/tests/BUILD.bazel | 54 - server/tests/main_test.go | 73 - server/tests/tidb_test.go | 3124 -- server/tidb_test.go | 133 - session/BUILD.bazel | 168 - session/bench_test.go | 1945 -- session/bootstrap.go | 3152 -- session/bootstrap_test.go | 2089 -- session/bootstraptest/BUILD.bazel | 36 - session/bootstraptest/main_test.go | 63 - session/clusteredindextest/BUILD.bazel | 26 - session/clusteredindextest/main_test.go | 65 - session/main_test.go | 80 - session/metrics/BUILD.bazel | 12 - session/metrics/metrics.go | 146 - session/nontransactionaltest/BUILD.bazel | 23 - session/nontransactionaltest/main_test.go | 61 - session/resourcegrouptest/BUILD.bazel | 13 - .../resourcegrouptest/resource_group_test.go | 59 - session/schematest/BUILD.bazel | 33 - session/schematest/main_test.go | 62 - session/schematest/schema_test.go | 681 - session/session.go | 4434 --- session/temporarytabletest/BUILD.bazel | 26 - session/temporarytabletest/main_test.go | 61 - .../temporary_table_test.go | 517 - session/test/BUILD.bazel | 40 - session/test/common/BUILD.bazel | 29 - session/test/common/common_test.go | 365 - session/test/common/main_test.go | 61 - session/test/main_test.go | 61 - session/test/meta/BUILD.bazel | 29 - session/test/meta/main_test.go | 61 - session/test/meta/session_test.go | 174 - session/test/privileges/BUILD.bazel | 23 - session/test/privileges/main_test.go | 61 - session/test/privileges/privileges_test.go | 173 - session/test/session_test.go | 1046 - session/test/txn/BUILD.bazel | 28 - session/test/txn/main_test.go | 61 - session/test/txn/txn_test.go | 621 - session/test/variable/BUILD.bazel | 27 - session/test/variable/main_test.go | 61 - session/test/variable/variable_test.go | 388 - session/test/vars/BUILD.bazel | 28 - session/test/vars/main_test.go | 61 - session/testutil.go | 95 - session/tidb_test.go | 114 - session/txn.go | 749 - session/txninfo/BUILD.bazel | 20 - session/txninfo/summary.go | 162 - sessionctx/BUILD.bazel | 42 - sessionctx/binloginfo/BUILD.bazel | 61 - sessionctx/binloginfo/main_test.go | 33 - sessionctx/context.go | 266 - sessionctx/main_test.go | 33 - sessionctx/sessionstates/BUILD.bazel | 51 - sessionctx/stmtctx/BUILD.bazel | 58 - sessionctx/stmtctx/main_test.go | 33 - sessionctx/variable/BUILD.bazel | 125 - sessionctx/variable/error.go | 51 - .../variable/featuretag/disttask/BUILD.bazel | 11 - sessionctx/variable/main_test.go | 33 - sessionctx/variable/session.go | 3659 --- sessionctx/variable/session_test.go | 538 - sessionctx/variable/sysvar_test.go | 1427 - sessionctx/variable/variable.go | 664 - sessionctx/variable/variable_test.go | 720 - sessiontxn/BUILD.bazel | 55 - sessiontxn/failpoint.go | 181 - sessiontxn/interface.go | 237 - sessiontxn/internal/BUILD.bazel | 16 - sessiontxn/internal/txn.go | 82 - sessiontxn/isolation/BUILD.bazel | 76 - sessiontxn/isolation/base.go | 575 - sessiontxn/isolation/main_test.go | 176 - sessiontxn/isolation/metrics/BUILD.bazel | 12 - sessiontxn/isolation/metrics/metrics.go | 36 - sessiontxn/staleread/BUILD.bazel | 68 - sessiontxn/staleread/errors.go | 24 - sessiontxn/staleread/failpoint.go | 37 - sessiontxn/staleread/main_test.go | 34 - sessiontxn/staleread/util.go | 94 - statistics/BUILD.bazel | 102 - statistics/builder.go | 461 - statistics/column.go | 268 - statistics/debugtrace.go | 267 - statistics/handle/BUILD.bazel | 90 - statistics/handle/autoanalyze/BUILD.bazel | 43 - statistics/handle/bootstrap.go | 557 - statistics/handle/cache/BUILD.bazel | 43 - statistics/handle/cache/bench_test.go | 180 - statistics/handle/cache/internal/BUILD.bazel | 17 - .../handle/cache/internal/lfu/BUILD.bazel | 40 - .../cache/internal/mapcache/BUILD.bazel | 12 - .../handle/cache/internal/metrics/BUILD.bazel | 12 - .../handle/cache/internal/metrics/metrics.go | 68 - .../cache/internal/testutil/BUILD.bazel | 14 - .../cache/internal/testutil/testutil.go | 91 - statistics/handle/ddl.go | 393 - statistics/handle/ddl_test.go | 286 - statistics/handle/dump.go | 327 - statistics/handle/dump_test.go | 619 - statistics/handle/extstats/BUILD.bazel | 19 - statistics/handle/globalstats/BUILD.bazel | 50 - statistics/handle/globalstats/topn.go | 115 - statistics/handle/handle.go | 528 - statistics/handle/handletest/BUILD.bazel | 35 - .../handle/handletest/analyze/BUILD.bazel | 22 - .../handle/handletest/analyze/analyze_test.go | 246 - .../handle/handletest/analyze/main_test.go | 33 - .../handle/handletest/globalstats/BUILD.bazel | 21 - .../handletest/globalstats/main_test.go | 33 - statistics/handle/handletest/handle_test.go | 1687 -- .../handle/handletest/lockstats/BUILD.bazel | 23 - .../handle/handletest/lockstats/main_test.go | 33 - statistics/handle/handletest/main_test.go | 33 - .../handle/handletest/statstest/BUILD.bazel | 22 - .../handle/handletest/statstest/main_test.go | 33 - .../handle/handletest/statstest/stats_test.go | 290 - statistics/handle/history/BUILD.bazel | 17 - statistics/handle/internal/BUILD.bazel | 12 - statistics/handle/internal/testutil.go | 80 - statistics/handle/lockstats/BUILD.bazel | 48 - statistics/handle/main_test.go | 33 - statistics/handle/metrics/BUILD.bazel | 12 - statistics/handle/metrics/metrics.go | 47 - statistics/handle/storage/BUILD.bazel | 56 - statistics/handle/storage/update.go | 220 - statistics/handle/update.go | 58 - statistics/handle/updatetest/BUILD.bazel | 31 - statistics/handle/updatetest/main_test.go | 33 - statistics/handle/updatetest/update_test.go | 1310 - statistics/handle/usage/BUILD.bazel | 37 - statistics/handle/util/BUILD.bazel | 31 - statistics/handle/util/util.go | 215 - statistics/index.go | 226 - statistics/integration_test.go | 483 - statistics/main_test.go | 147 - statistics/sample.go | 318 - statistics/sample_test.go | 319 - statistics/table.go | 685 - store/BUILD.bazel | 45 - store/batch_coprocessor_test.go | 118 - store/copr/BUILD.bazel | 105 - store/copr/batch_coprocessor_test.go | 284 - store/copr/copr_test/BUILD.bazel | 23 - store/copr/copr_test/coprocessor_test.go | 184 - store/copr/copr_test/main_test.go | 61 - store/copr/coprocessor.go | 2091 -- store/copr/coprocessor_test.go | 793 - store/copr/main_test.go | 46 - store/copr/metrics/BUILD.bazel | 12 - store/copr/metrics/metrics.go | 38 - store/copr/mpp.go | 338 - store/copr/store.go | 143 - store/driver/BUILD.bazel | 67 - store/driver/backoff/BUILD.bazel | 13 - store/driver/client_test.go | 111 - store/driver/error/BUILD.bazel | 33 - store/driver/error/error.go | 187 - store/driver/error/error_test.go | 53 - store/driver/main_test.go | 162 - store/driver/options/BUILD.bazel | 12 - store/driver/options/options.go | 41 - store/driver/txn/BUILD.bazel | 70 - store/driver/txn/error.go | 258 - store/driver/txn/main_test.go | 33 - store/driver/txn_test.go | 222 - store/gcworker/BUILD.bazel | 80 - store/gcworker/main_test.go | 44 - store/helper/BUILD.bazel | 54 - store/helper/helper.go | 1221 - store/helper/helper_test.go | 482 - store/helper/main_test.go | 33 - store/main_test.go | 33 - store/mockstore/BUILD.bazel | 56 - store/mockstore/cluster_test.go | 132 - store/mockstore/main_test.go | 40 - store/mockstore/mockcopr/BUILD.bazel | 76 - store/mockstore/mockcopr/analyze.go | 294 - store/mockstore/mockcopr/executor.go | 675 - store/mockstore/mockcopr/executor_test.go | 139 - store/mockstore/mockcopr/main_test.go | 40 - store/mockstore/mockcopr/topn.go | 141 - store/mockstore/mockstorage/BUILD.bazel | 16 - store/mockstore/mockstorage/storage.go | 139 - store/mockstore/mockstore.go | 236 - store/mockstore/tikv.go | 40 - store/mockstore/unistore/BUILD.bazel | 64 - store/mockstore/unistore/client/BUILD.bazel | 9 - store/mockstore/unistore/cluster.go | 151 - store/mockstore/unistore/config/BUILD.bazel | 12 - .../mockstore/unistore/cophandler/BUILD.bazel | 87 - .../mockstore/unistore/cophandler/analyze.go | 664 - .../unistore/cophandler/main_test.go | 33 - store/mockstore/unistore/cophandler/mpp.go | 680 - store/mockstore/unistore/cophandler/topn.go | 150 - .../mockstore/unistore/lockstore/BUILD.bazel | 35 - .../mockstore/unistore/lockstore/main_test.go | 33 - store/mockstore/unistore/main_test.go | 33 - store/mockstore/unistore/metrics/BUILD.bazel | 9 - store/mockstore/unistore/mock.go | 72 - store/mockstore/unistore/pd.go | 271 - store/mockstore/unistore/pd/BUILD.bazel | 18 - store/mockstore/unistore/server/BUILD.bazel | 18 - store/mockstore/unistore/server/server.go | 151 - store/mockstore/unistore/testutil.go | 116 - store/mockstore/unistore/tikv/BUILD.bazel | 89 - .../unistore/tikv/dbreader/BUILD.bazel | 16 - .../unistore/tikv/kverrors/BUILD.bazel | 13 - .../unistore/tikv/kverrors/errors.go | 160 - store/mockstore/unistore/tikv/main_test.go | 33 - store/mockstore/unistore/tikv/mvcc.go | 1966 -- .../mockstore/unistore/tikv/mvcc/BUILD.bazel | 20 - store/mockstore/unistore/tikv/mvcc/mvcc.go | 187 - store/mockstore/unistore/tikv/mvcc/tikv.go | 131 - .../unistore/tikv/pberror/BUILD.bazel | 9 - store/mockstore/unistore/tikv/region.go | 798 - store/mockstore/unistore/tikv/server.go | 1175 - store/mockstore/unistore/tikv/write.go | 354 - .../unistore/util/lockwaiter/BUILD.bazel | 33 - .../unistore/util/lockwaiter/main_test.go | 33 - store/pdtypes/BUILD.bazel | 21 - store/store.go | 123 - store/store_test.go | 875 - structure/BUILD.bazel | 42 - structure/list.go | 246 - structure/main_test.go | 35 - structure/type.go | 127 - table/BUILD.bazel | 69 - table/column.go | 742 - table/column_test.go | 498 - table/constraint.go | 253 - table/index.go | 100 - table/main_test.go | 33 - table/table.go | 285 - table/table_test.go | 43 - table/tables/BUILD.bazel | 116 - table/tables/cache.go | 361 - table/tables/cache_test.go | 560 - table/tables/index.go | 742 - table/tables/index_test.go | 170 - table/tables/main_test.go | 33 - table/tables/partition.go | 1902 -- table/tables/tables.go | 2383 -- table/tables/tables_test.go | 1710 -- table/tables/test/partition/BUILD.bazel | 29 - table/tables/test/partition/main_test.go | 33 - table/tables/test/partition/partition_test.go | 3049 -- table/tables/testutil.go | 36 - table/temptable/BUILD.bazel | 61 - table/temptable/ddl.go | 193 - table/temptable/ddl_test.go | 226 - table/temptable/infoschema.go | 68 - table/temptable/main_test.go | 258 - tablecodec/BUILD.bazel | 56 - tablecodec/bench_test.go | 68 - tablecodec/main_test.go | 33 - tablecodec/rowindexcodec/BUILD.bazel | 24 - tablecodec/rowindexcodec/main_test.go | 33 - telemetry/BUILD.bazel | 87 - telemetry/cte_test/BUILD.bazel | 20 - telemetry/cte_test/cte_test.go | 133 - telemetry/main_test.go | 49 - telemetry/telemetry.go | 178 - telemetry/ttl.go | 215 - testkit/BUILD.bazel | 63 - testkit/ddlhelper/BUILD.bazel | 13 - testkit/ddlhelper/helper.go | 27 - testkit/external/BUILD.bazel | 16 - testkit/external/util.go | 72 - testkit/mockstore.go | 256 - testkit/testdata/BUILD.bazel | 12 - testkit/testenv/BUILD.bazel | 9 - testkit/testfork/BUILD.bazel | 21 - testkit/testkit.go | 676 - testkit/testmain/BUILD.bazel | 12 - testkit/testsetup/BUILD.bazel | 13 - testkit/testutil/BUILD.bazel | 37 - testkit/testutil/handle.go | 51 - tests/globalkilltest/BUILD.bazel | 4 +- tests/globalkilltest/global_kill_test.go | 6 +- tests/globalkilltest/main_test.go | 2 +- tests/graceshutdown/BUILD.bazel | 2 +- tests/graceshutdown/main_test.go | 2 +- tests/readonlytest/BUILD.bazel | 2 +- tests/readonlytest/main_test.go | 2 +- tests/realtikvtest/BUILD.bazel | 20 +- tests/realtikvtest/addindextest/BUILD.bazel | 44 +- .../addindextest/add_index_test.go | 34 +- tests/realtikvtest/addindextest/common.go | 20 +- .../addindextest/compatibility.go | 4 +- .../addindextest/global_sort_test.go | 14 +- .../addindextest/integration_test.go | 32 +- tests/realtikvtest/addindextest/main_test.go | 2 +- .../addindextest/operator_test.go | 34 +- tests/realtikvtest/addindextest/workload.go | 4 +- tests/realtikvtest/brietest/BUILD.bazel | 14 +- .../brietest/backup_restore_test.go | 4 +- tests/realtikvtest/brietest/binlog_test.go | 8 +- tests/realtikvtest/brietest/brie_test.go | 8 +- tests/realtikvtest/brietest/main_test.go | 2 +- tests/realtikvtest/flashbacktest/BUILD.bazel | 16 +- .../flashbacktest/flashback_test.go | 68 +- tests/realtikvtest/flashbacktest/main_test.go | 2 +- tests/realtikvtest/importintotest/BUILD.bazel | 34 +- .../importintotest/detach_test.go | 4 +- .../importintotest/from_server_test.go | 4 +- .../importintotest/import_into_test.go | 60 +- tests/realtikvtest/importintotest/job_test.go | 60 +- .../realtikvtest/importintotest/main_test.go | 2 +- .../importintotest/one_parquet_test.go | 6 +- .../importintotest/precheck_test.go | 4 +- .../realtikvtest/importintotest/util_test.go | 4 +- .../realtikvtest/importintotest2/BUILD.bazel | 12 +- .../realtikvtest/importintotest2/main_test.go | 6 +- .../write_after_import_test.go | 8 +- .../realtikvtest/importintotest3/BUILD.bazel | 6 +- .../importintotest3/file_compression_test.go | 2 +- .../realtikvtest/importintotest3/main_test.go | 6 +- .../realtikvtest/importintotest4/BUILD.bazel | 14 +- .../importintotest4/global_sort_test.go | 18 +- .../realtikvtest/importintotest4/main_test.go | 6 +- .../importintotest4/split_file_test.go | 6 +- .../realtikvtest/pessimistictest/BUILD.bazel | 46 +- .../pessimistictest/pessimistic_test.go | 74 +- tests/realtikvtest/sessiontest/BUILD.bazel | 20 +- tests/realtikvtest/sessiontest/paging_test.go | 4 +- tests/realtikvtest/sessiontest/retry_test.go | 16 +- .../sessiontest/session_fail_test.go | 22 +- tests/realtikvtest/statisticstest/BUILD.bazel | 2 +- .../statisticstest/statistics_test.go | 2 +- tests/realtikvtest/testkit.go | 20 +- tests/realtikvtest/txntest/BUILD.bazel | 14 +- tests/realtikvtest/txntest/isolation_test.go | 6 +- tests/realtikvtest/txntest/txn_state_test.go | 20 +- tests/realtikvtest/txntest/txn_test.go | 6 +- tidb-binlog/driver/reader/BUILD.bazel | 31 - tidb-binlog/driver/reader/reader.go | 239 - tidb-binlog/node/BUILD.bazel | 33 - tidb-binlog/node/registry_test.go | 111 - tidb-binlog/proto/go-binlog/BUILD.bazel | 9 - tidb-binlog/pump_client/BUILD.bazel | 47 - tidb-binlog/pump_client/client.go | 614 - tidb-binlog/pump_client/client_test.go | 335 - tidb-server/BUILD.bazel | 112 - tidb-server/main_test.go | 82 - timer/BUILD.bazel | 28 - timer/api/BUILD.bazel | 48 - timer/api/client.go | 272 - timer/api/main_test.go | 27 - timer/api/store_test.go | 345 - timer/api/timer.go | 281 - timer/main_test.go | 34 - timer/metrics/BUILD.bazel | 9 - timer/runtime/BUILD.bazel | 53 - timer/runtime/cache.go | 243 - timer/runtime/cache_test.go | 477 - timer/runtime/main_test.go | 183 - timer/runtime/worker.go | 450 - timer/tablestore/BUILD.bazel | 52 - timer/tablestore/sql.go | 484 - timer/tablestore/sql_test.go | 715 - timer/tablestore/store.go | 435 - tools/check/check-errdoc.sh | 2 +- tools/check/xprog.go | 4 +- tools/tazel/BUILD.bazel | 4 +- tools/tazel/main.go | 2 +- tools/tazel/util.go | 10 +- ttl/cache/BUILD.bazel | 71 - ttl/cache/infoschema.go | 112 - ttl/cache/infoschema_test.go | 73 - ttl/cache/main_test.go | 33 - ttl/cache/split_test.go | 826 - ttl/cache/table.go | 489 - ttl/cache/table_test.go | 216 - ttl/cache/task.go | 198 - ttl/client/BUILD.bazel | 32 - ttl/metrics/BUILD.bazel | 21 - ttl/metrics/metrics.go | 257 - ttl/session/BUILD.bazel | 41 - ttl/session/main_test.go | 33 - ttl/session/session.go | 184 - ttl/session/session_test.go | 66 - ttl/session/sysvar_test.go | 125 - ttl/sqlbuilder/BUILD.bazel | 46 - ttl/sqlbuilder/main_test.go | 33 - ttl/sqlbuilder/sql.go | 482 - ttl/sqlbuilder/sql_test.go | 949 - ttl/ttlworker/BUILD.bazel | 112 - ttl/ttlworker/job.go | 157 - ttl/ttlworker/session.go | 248 - ttl/ttlworker/session_test.go | 358 - ttl/ttlworker/timer.go | 281 - ttl/ttlworker/worker.go | 141 - types/BUILD.bazel | 116 - types/benchmark_test.go | 60 - types/compare.go | 86 - types/context.go | 33 - types/context/BUILD.bazel | 18 - types/context/context.go | 166 - types/convert.go | 782 - types/errors.go | 97 - types/errors_test.go | 55 - types/etc.go | 208 - types/etc_test.go | 346 - types/field_type.go | 1490 - types/field_type_test.go | 466 - types/format_test.go | 199 - types/main_test.go | 33 - types/parser_driver/BUILD.bazel | 34 - types/parser_driver/main_test.go | 32 - types/set.go | 132 - types/set_test.go | 104 - util/BUILD.bazel | 90 - util/admin/BUILD.bazel | 46 - util/admin/admin.go | 263 - util/admin/main_test.go | 40 - util/arena/BUILD.bazel | 24 - util/arena/main_test.go | 32 - util/backoff/BUILD.bazel | 17 - util/benchdaily/BUILD.bazel | 23 - util/benchdaily/main_test.go | 32 - util/bitmap/BUILD.bazel | 24 - util/bitmap/main_test.go | 32 - util/breakpoint/BUILD.bazel | 13 - util/cgroup/BUILD.bazel | 76 - util/channel/BUILD.bazel | 8 - util/checksum/BUILD.bazel | 26 - util/checksum/checksum.go | 183 - util/checksum/main_test.go | 32 - util/chunk/BUILD.bazel | 78 - util/chunk/codec.go | 354 - util/chunk/codec_test.go | 191 - util/chunk/column.go | 764 - util/chunk/column_test.go | 995 - util/chunk/compare.go | 245 - util/chunk/list.go | 179 - util/chunk/main_test.go | 49 - util/chunk/pool_test.go | 110 - util/codec/BUILD.bazel | 54 - util/codec/bench_test.go | 117 - util/codec/codec.go | 1381 - util/codec/codec_test.go | 1300 - util/codec/collation_test.go | 158 - util/codec/main_test.go | 32 - util/collate/BUILD.bazel | 51 - util/collate/charset.go | 28 - util/collate/main_test.go | 32 - util/collate/ucadata/BUILD.bazel | 25 - util/collate/ucadata/generator/BUILD.bazel | 22 - util/collate/ucaimpl/BUILD.bazel | 15 - util/column-mapping/BUILD.bazel | 21 - util/column-mapping/column.go | 539 - util/compress/BUILD.bazel | 9 - util/cpu/BUILD.bazel | 39 - util/cpu/cpu.go | 122 - util/cpu/main_test.go | 33 - util/cpuprofile/BUILD.bazel | 33 - util/cpuprofile/testutil/BUILD.bazel | 8 - util/cteutil/BUILD.bazel | 34 - util/cteutil/main_test.go | 32 - util/cteutil/storage.go | 271 - util/dbterror/BUILD.bazel | 34 - util/dbterror/exeerrors/BUILD.bazel | 13 - util/dbterror/exeerrors/errors.go | 102 - util/dbterror/main_test.go | 31 - util/dbterror/terror.go | 57 - util/dbutil/BUILD.bazel | 65 - util/dbutil/common.go | 891 - util/dbutil/common_test.go | 257 - util/dbutil/index.go | 209 - util/dbutil/index_test.go | 101 - util/dbutil/table.go | 146 - util/dbutil/table_test.go | 165 - util/dbutil/types.go | 47 - util/dbutil/variable.go | 155 - util/ddl-checker/BUILD.bazel | 34 - util/deadlockhistory/BUILD.bazel | 39 - util/deadlockhistory/main_test.go | 32 - util/disjointset/BUILD.bazel | 24 - util/disjointset/main_test.go | 32 - util/disk/BUILD.bazel | 38 - util/disk/main_test.go | 32 - util/disk/tracker.go | 30 - util/distrole/BUILD.bazel | 8 - util/disttask/BUILD.bazel | 18 - util/domainutil/BUILD.bazel | 9 - util/encrypt/BUILD.bazel | 32 - util/encrypt/main_test.go | 28 - util/engine/BUILD.bazel | 9 - util/errors_test.go | 36 - util/etcd/BUILD.bazel | 27 - util/execdetails/BUILD.bazel | 33 - util/execdetails/main_test.go | 31 - util/expensivequery/BUILD.bazel | 29 - util/extsort/BUILD.bazel | 42 - util/fastrand/BUILD.bazel | 24 - util/fastrand/main_test.go | 32 - util/filter/BUILD.bazel | 28 - util/format/BUILD.bazel | 24 - util/format/main_test.go | 32 - util/gctuner/BUILD.bazel | 40 - util/gcutil/BUILD.bazel | 18 - util/generatedexpr/BUILD.bazel | 34 - util/generatedexpr/main_test.go | 32 - util/generic/BUILD.bazel | 19 - util/globalconn/BUILD.bazel | 32 - util/globalconn/pool_test.go | 500 - util/hack/BUILD.bazel | 23 - util/hack/main_test.go | 32 - util/hint/BUILD.bazel | 20 - util/importer/BUILD.bazel | 28 - util/importer/config.go | 39 - util/importer/parser.go | 275 - util/intest/BUILD.bazel | 23 - util/israce/BUILD.bazel | 11 - util/keydecoder/BUILD.bazel | 43 - util/keydecoder/main_test.go | 32 - util/kvcache/BUILD.bazel | 29 - util/kvcache/main_test.go | 32 - util/localpool/BUILD.bazel | 29 - util/localpool/main_test.go | 32 - util/logutil/BUILD.bazel | 49 - util/logutil/consistency/BUILD.bazel | 22 - util/logutil/consistency/reporter.go | 294 - util/logutil/main_test.go | 51 - util/main_test.go | 32 - util/mathutil/BUILD.bazel | 31 - util/mathutil/main_test.go | 32 - util/memory/BUILD.bazel | 47 - util/memory/main_test.go | 28 - util/memory/tracker.go | 868 - util/memoryusagealarm/BUILD.bazel | 34 - util/metricsutil/BUILD.bazel | 31 - util/metricsutil/common.go | 144 - util/misc.go | 696 - util/misc_test.go | 215 - util/mock/BUILD.bazel | 57 - util/mock/client.go | 33 - util/mock/context.go | 490 - util/mock/main_test.go | 33 - util/mock/store.go | 87 - util/mvmap/BUILD.bazel | 29 - util/mvmap/main_test.go | 32 - util/nocopy/BUILD.bazel | 8 - util/paging/BUILD.bazel | 24 - util/paging/main_test.go | 32 - util/parser/BUILD.bazel | 37 - util/parser/ast.go | 140 - util/parser/main_test.go | 32 - util/parser/parser_test.go | 170 - util/password-validation/BUILD.bazel | 25 - util/pdapi/BUILD.bazel | 8 - util/plancache/BUILD.bazel | 9 - util/plancache/util.go | 36 - util/plancodec/BUILD.bazel | 41 - util/plancodec/codec.go | 448 - util/plancodec/codec_test.go | 57 - util/plancodec/main_test.go | 32 - util/printer/BUILD.bazel | 32 - util/printer/main_test.go | 32 - util/profile/BUILD.bazel | 45 - util/profile/main_test.go | 32 - util/promutil/BUILD.bazel | 24 - util/ranger/BUILD.bazel | 67 - util/ranger/bench_test.go | 133 - util/ranger/checker.go | 222 - util/ranger/main_test.go | 47 - util/ranger/types.go | 280 - util/regexpr-router/BUILD.bazel | 26 - util/replayer/BUILD.bazel | 13 - util/resourcegrouptag/BUILD.bazel | 39 - util/resourcegrouptag/main_test.go | 32 - util/rowDecoder/BUILD.bazel | 50 - util/rowDecoder/decoder.go | 205 - util/rowDecoder/main_test.go | 32 - util/rowcodec/BUILD.bazel | 52 - util/rowcodec/bench_test.go | 122 - util/rowcodec/common.go | 356 - util/rowcodec/decoder.go | 524 - util/rowcodec/main_test.go | 64 - util/schemacmp/BUILD.bazel | 46 - util/schemacmp/table.go | 429 - util/schemacmp/table_test.go | 517 - util/schemacmp/type.go | 214 - util/schemacmp/util.go | 69 - util/selection/BUILD.bazel | 24 - util/selection/main_test.go | 32 - util/sem/BUILD.bazel | 31 - util/sem/main_test.go | 33 - util/servermemorylimit/BUILD.bazel | 32 - util/set/BUILD.bazel | 39 - util/set/main_test.go | 32 - util/signal/BUILD.bazel | 63 - util/size/BUILD.bazel | 8 - util/skip/BUILD.bazel | 8 - util/sli/BUILD.bazel | 12 - util/slice/BUILD.bazel | 24 - util/slice/main_test.go | 32 - util/sqlexec/BUILD.bazel | 39 - util/sqlexec/main_test.go | 33 - util/sqlexec/mock/BUILD.bazel | 14 - util/stmtsummary/BUILD.bazel | 59 - util/stmtsummary/main_test.go | 32 - util/stmtsummary/reader.go | 635 - util/stmtsummary/v2/BUILD.bazel | 61 - util/stmtsummary/v2/column.go | 522 - util/stmtsummary/v2/column_test.go | 82 - util/stmtsummary/v2/main_test.go | 33 - util/stmtsummary/v2/reader.go | 862 - util/stmtsummary/v2/stmtsummary.go | 613 - util/stmtsummary/v2/tests/BUILD.bazel | 25 - util/stmtsummary/v2/tests/main_test.go | 34 - util/stmtsummary/v2/tests/table_test.go | 570 - util/stringutil/BUILD.bazel | 29 - util/stringutil/main_test.go | 32 - util/syncutil/BUILD.bazel | 12 - util/sys/linux/BUILD.bazel | 70 - util/sys/linux/main_test.go | 32 - util/sys/linux/sys_test.go | 27 - util/sys/storage/BUILD.bazel | 34 - util/sys/storage/main_test.go | 32 - util/sys/storage/sys_test.go | 30 - util/systimemon/BUILD.bazel | 29 - util/systimemon/main_test.go | 34 - util/table-filter/BUILD.bazel | 30 - util/table-router/BUILD.bazel | 24 - util/table-rule-selector/BUILD.bazel | 18 - util/tableutil/BUILD.bazel | 12 - util/texttree/BUILD.bazel | 24 - util/texttree/main_test.go | 32 - util/tiflash/BUILD.bazel | 8 - util/tiflashcompute/BUILD.bazel | 18 - util/tikvutil/BUILD.bazel | 9 - util/timeutil/BUILD.bazel | 37 - util/timeutil/errors.go | 23 - util/timeutil/main_test.go | 32 - util/tls/BUILD.bazel | 9 - util/topsql/BUILD.bazel | 49 - util/topsql/collector/BUILD.bazel | 33 - util/topsql/collector/cpu.go | 266 - util/topsql/collector/main_test.go | 110 - util/topsql/collector/mock/BUILD.bazel | 17 - util/topsql/collector/mock/mock.go | 221 - util/topsql/main_test.go | 32 - util/topsql/reporter/BUILD.bazel | 61 - util/topsql/reporter/main_test.go | 32 - util/topsql/reporter/metrics/BUILD.bazel | 12 - util/topsql/reporter/metrics/metrics.go | 64 - util/topsql/reporter/mock/BUILD.bazel | 17 - util/topsql/reporter/mock/pubsub.go | 67 - util/topsql/reporter/mock/server.go | 241 - util/topsql/reporter/pubsub.go | 268 - util/topsql/reporter/reporter.go | 333 - util/topsql/state/BUILD.bazel | 9 - util/topsql/stmtstats/BUILD.bazel | 40 - util/topsql/stmtstats/main_test.go | 32 - util/tracing/BUILD.bazel | 37 - util/tracing/main_test.go | 32 - util/tracing/util.go | 134 - util/tracing/util_test.go | 152 - util/trxevents/BUILD.bazel | 9 - util/util.go | 298 - util/util_test.go | 116 - util/versioninfo/BUILD.bazel | 8 - util/vitess/BUILD.bazel | 25 - util/vitess/main_test.go | 32 - util/watcher/BUILD.bazel | 24 - util/zeropool/BUILD.bazel | 20 - util/zeropool/pool_test.go | 178 - 4495 files changed, 349293 insertions(+), 349278 deletions(-) delete mode 100644 autoid_service/BUILD.bazel delete mode 100644 autoid_service/autoid.go delete mode 100644 autoid_service/autoid_test.go delete mode 100644 bindinfo/BUILD.bazel delete mode 100644 bindinfo/handle.go delete mode 100644 bindinfo/handle_test.go delete mode 100644 bindinfo/internal/BUILD.bazel delete mode 100644 bindinfo/internal/testutil.go delete mode 100644 bindinfo/main_test.go delete mode 100644 bindinfo/optimize_test.go delete mode 100644 bindinfo/stat.go delete mode 100644 bindinfo/tests/BUILD.bazel delete mode 100644 bindinfo/tests/main_test.go create mode 100644 cmd/tidb-server/BUILD.bazel rename {tidb-server => cmd/tidb-server}/main.go (93%) create mode 100644 cmd/tidb-server/main_test.go delete mode 100644 config/BUILD.bazel delete mode 100644 config/config.go delete mode 100644 config/main_test.go delete mode 100644 ddl/BUILD.bazel delete mode 100644 ddl/callback.go delete mode 100644 ddl/cluster.go delete mode 100644 ddl/cluster_test.go delete mode 100644 ddl/column.go delete mode 100644 ddl/column_test.go delete mode 100644 ddl/constant.go delete mode 100644 ddl/constraint.go delete mode 100644 ddl/copr/BUILD.bazel delete mode 100644 ddl/db_test.go delete mode 100644 ddl/ddl.go delete mode 100644 ddl/ddl_error_test.go delete mode 100644 ddl/ddl_test.go delete mode 100644 ddl/fail_test.go delete mode 100644 ddl/foreign_key.go delete mode 100644 ddl/foreign_key_test.go delete mode 100644 ddl/index.go delete mode 100644 ddl/ingest/BUILD.bazel delete mode 100644 ddl/ingest/config.go delete mode 100644 ddl/ingest/integration_test.go delete mode 100644 ddl/ingest/mock.go delete mode 100644 ddl/ingest/tests/BUILD.bazel delete mode 100644 ddl/ingest/tests/partition_table_test.go delete mode 100644 ddl/ingest/testutil/BUILD.bazel delete mode 100644 ddl/ingest/testutil/testutil.go delete mode 100644 ddl/integration_test.go delete mode 100644 ddl/internal/session/BUILD.bazel delete mode 100644 ddl/internal/session/session.go delete mode 100644 ddl/label/BUILD.bazel delete mode 100644 ddl/label/main_test.go delete mode 100644 ddl/label/rule.go delete mode 100644 ddl/main_test.go delete mode 100644 ddl/mock.go delete mode 100644 ddl/options.go delete mode 100644 ddl/partition.go delete mode 100644 ddl/partition_test.go delete mode 100644 ddl/placement/BUILD.bazel delete mode 100644 ddl/placement/rule.go delete mode 100644 ddl/reorg.go delete mode 100644 ddl/resourcegroup/BUILD.bazel delete mode 100644 ddl/resourcegroup/group.go delete mode 100644 ddl/schema.go delete mode 100644 ddl/schema_test.go delete mode 100644 ddl/schematracker/BUILD.bazel delete mode 100644 ddl/schematracker/checker.go delete mode 100644 ddl/stat.go delete mode 100644 ddl/stat_test.go delete mode 100644 ddl/syncer/BUILD.bazel delete mode 100644 ddl/table.go delete mode 100644 ddl/table_test.go delete mode 100644 ddl/tests/adminpause/BUILD.bazel delete mode 100644 ddl/tests/adminpause/main_test.go delete mode 100644 ddl/tests/fail/BUILD.bazel delete mode 100644 ddl/tests/fail/main_test.go delete mode 100644 ddl/tests/fk/BUILD.bazel delete mode 100644 ddl/tests/fk/foreign_key_test.go delete mode 100644 ddl/tests/fk/main_test.go delete mode 100644 ddl/tests/indexmerge/BUILD.bazel delete mode 100644 ddl/tests/indexmerge/main_test.go delete mode 100644 ddl/tests/metadatalock/BUILD.bazel delete mode 100644 ddl/tests/metadatalock/main_test.go delete mode 100644 ddl/tests/multivaluedindex/BUILD.bazel delete mode 100644 ddl/tests/multivaluedindex/main_test.go delete mode 100644 ddl/tests/multivaluedindex/multi_valued_index_test.go delete mode 100644 ddl/tests/partition/BUILD.bazel delete mode 100644 ddl/tests/partition/main_test.go delete mode 100644 ddl/tests/resourcegroup/BUILD.bazel delete mode 100644 ddl/tests/resourcegroup/resource_group_test.go delete mode 100644 ddl/tests/serial/BUILD.bazel delete mode 100644 ddl/tests/serial/main_test.go delete mode 100644 ddl/tests/tiflash/BUILD.bazel delete mode 100644 ddl/tests/tiflash/main_test.go delete mode 100644 ddl/testutil/BUILD.bazel delete mode 100644 ddl/testutil/testutil.go delete mode 100644 ddl/ttl.go delete mode 100644 ddl/util/BUILD.bazel delete mode 100644 ddl/util/callback/BUILD.bazel delete mode 100644 ddl/util/callback/callback.go delete mode 100644 ddl/util/main_test.go delete mode 100644 ddl/util/util.go delete mode 100644 distsql/BUILD.bazel delete mode 100644 distsql/bench_test.go delete mode 100644 distsql/distsql.go delete mode 100644 distsql/distsql_test.go delete mode 100644 distsql/main_test.go delete mode 100644 disttask/framework/BUILD.bazel delete mode 100644 disttask/framework/dispatcher/BUILD.bazel delete mode 100644 disttask/framework/dispatcher/dispatcher.go delete mode 100644 disttask/framework/dispatcher/dispatcher_test.go delete mode 100644 disttask/framework/dispatcher/interface.go delete mode 100644 disttask/framework/dispatcher/main_test.go delete mode 100644 disttask/framework/framework_dynamic_dispatch_test.go delete mode 100644 disttask/framework/framework_ha_test.go delete mode 100644 disttask/framework/framework_pause_and_resume_test.go delete mode 100644 disttask/framework/handle/BUILD.bazel delete mode 100644 disttask/framework/handle/handle.go delete mode 100644 disttask/framework/handle/handle_test.go delete mode 100644 disttask/framework/mock/BUILD.bazel delete mode 100644 disttask/framework/mock/execute/BUILD.bazel delete mode 100644 disttask/framework/planner/BUILD.bazel delete mode 100644 disttask/framework/planner/plan.go delete mode 100644 disttask/framework/planner/plan_test.go delete mode 100644 disttask/framework/planner/planner.go delete mode 100644 disttask/framework/planner/planner_test.go delete mode 100644 disttask/framework/proto/BUILD.bazel delete mode 100644 disttask/framework/scheduler/BUILD.bazel delete mode 100644 disttask/framework/scheduler/execute/BUILD.bazel delete mode 100644 disttask/framework/scheduler/execute/interface.go delete mode 100644 disttask/framework/scheduler/execute/summary.go delete mode 100644 disttask/framework/scheduler/interface.go delete mode 100644 disttask/framework/scheduler/manager.go delete mode 100644 disttask/framework/scheduler/manager_test.go delete mode 100644 disttask/framework/scheduler/scheduler.go delete mode 100644 disttask/framework/storage/BUILD.bazel delete mode 100644 disttask/framework/storage/table_test.go delete mode 100644 disttask/framework/storage/util.go delete mode 100644 disttask/importinto/BUILD.bazel delete mode 100644 disttask/importinto/dispatcher.go delete mode 100644 disttask/importinto/dispatcher_test.go delete mode 100644 disttask/importinto/job.go delete mode 100644 disttask/importinto/metrics.go delete mode 100644 disttask/importinto/mock/BUILD.bazel delete mode 100644 disttask/importinto/planner.go delete mode 100644 disttask/importinto/planner_test.go delete mode 100644 disttask/importinto/scheduler.go delete mode 100644 disttask/operator/BUILD.bazel delete mode 100644 domain/BUILD.bazel delete mode 100644 domain/db_test.go delete mode 100644 domain/extract.go delete mode 100644 domain/extract_test.go delete mode 100644 domain/globalconfigsync/BUILD.bazel delete mode 100644 domain/infosync/BUILD.bazel delete mode 100644 domain/infosync/error.go delete mode 100644 domain/infosync/region.go delete mode 100644 domain/main_test.go delete mode 100644 domain/metrics/BUILD.bazel delete mode 100644 domain/metrics/metrics.go delete mode 100644 domain/plan_replayer.go delete mode 100644 domain/plan_replayer_test.go delete mode 100644 domain/resourcegroup/BUILD.bazel delete mode 100644 domain/resourcegroup/runaway.go delete mode 100644 domain/runaway.go delete mode 100644 errno/BUILD.bazel delete mode 100644 errno/main_test.go delete mode 100644 executor/BUILD.bazel delete mode 100644 executor/admin.go delete mode 100644 executor/aggfuncs/BUILD.bazel delete mode 100644 executor/aggfuncs/builder.go delete mode 100644 executor/aggfuncs/main_test.go delete mode 100644 executor/aggregate/BUILD.bazel delete mode 100644 executor/analyze.go delete mode 100644 executor/analyze_test.go delete mode 100644 executor/asyncloaddata/BUILD.bazel delete mode 100644 executor/asyncloaddata/util.go delete mode 100644 executor/asyncloaddata/util_test.go delete mode 100644 executor/benchmark_test.go delete mode 100644 executor/builder.go delete mode 100644 executor/checksum.go delete mode 100644 executor/coprocessor.go delete mode 100644 executor/cte_test.go delete mode 100644 executor/ddl.go delete mode 100644 executor/distsql.go delete mode 100644 executor/distsql_test.go delete mode 100644 executor/executor.go delete mode 100644 executor/executor_test.go delete mode 100644 executor/explain.go delete mode 100644 executor/foreign_key.go delete mode 100644 executor/importer/BUILD.bazel delete mode 100644 executor/importer/import.go delete mode 100644 executor/importer/job.go delete mode 100644 executor/internal/BUILD.bazel delete mode 100644 executor/internal/applycache/BUILD.bazel delete mode 100644 executor/internal/applycache/main_test.go delete mode 100644 executor/internal/builder/BUILD.bazel delete mode 100644 executor/internal/calibrateresource/BUILD.bazel delete mode 100644 executor/internal/calibrateresource/main_test.go delete mode 100644 executor/internal/exec/BUILD.bazel delete mode 100644 executor/internal/exec/executor.go delete mode 100644 executor/internal/mpp/BUILD.bazel delete mode 100644 executor/internal/pdhelper/BUILD.bazel delete mode 100644 executor/internal/pdhelper/main_test.go delete mode 100644 executor/internal/pdhelper/pd.go delete mode 100644 executor/internal/querywatch/BUILD.bazel delete mode 100644 executor/internal/querywatch/main_test.go delete mode 100644 executor/internal/testkit.go delete mode 100644 executor/internal/util/BUILD.bazel delete mode 100644 executor/internal/vecgroupchecker/BUILD.bazel delete mode 100644 executor/internal/vecgroupchecker/main_test.go delete mode 100644 executor/join.go delete mode 100644 executor/join_test.go delete mode 100644 executor/lockstats/BUILD.bazel delete mode 100644 executor/main_test.go delete mode 100644 executor/metrics/BUILD.bazel delete mode 100644 executor/metrics/metrics.go delete mode 100644 executor/mppcoordmanager/BUILD.bazel delete mode 100644 executor/partition_table_test.go delete mode 100644 executor/plan_replayer.go delete mode 100644 executor/prepared_test.go delete mode 100644 executor/sample.go delete mode 100644 executor/sample_test.go delete mode 100644 executor/set.go delete mode 100644 executor/set_test.go delete mode 100644 executor/show_test.go delete mode 100644 executor/simple_test.go delete mode 100644 executor/sort.go delete mode 100644 executor/split_test.go delete mode 100644 executor/stmtsummary.go delete mode 100644 executor/temporary_table_test.go delete mode 100644 executor/test/admintest/BUILD.bazel delete mode 100644 executor/test/admintest/main_test.go delete mode 100644 executor/test/aggregate/BUILD.bazel delete mode 100644 executor/test/aggregate/main_test.go delete mode 100644 executor/test/analyzetest/BUILD.bazel delete mode 100644 executor/test/analyzetest/analyze_test.go delete mode 100644 executor/test/analyzetest/main_test.go delete mode 100644 executor/test/analyzetest/memorycontrol/BUILD.bazel delete mode 100644 executor/test/analyzetest/memorycontrol/main_test.go delete mode 100644 executor/test/autoidtest/BUILD.bazel delete mode 100644 executor/test/autoidtest/autoid_test.go delete mode 100644 executor/test/autoidtest/main_test.go delete mode 100644 executor/test/ddl/BUILD.bazel delete mode 100644 executor/test/ddl/ddl_test.go delete mode 100644 executor/test/ddl/main_test.go delete mode 100644 executor/test/distsqltest/BUILD.bazel delete mode 100644 executor/test/distsqltest/distsql_test.go delete mode 100644 executor/test/distsqltest/main_test.go delete mode 100644 executor/test/executor/BUILD.bazel delete mode 100644 executor/test/executor/executor_test.go delete mode 100644 executor/test/executor/main_test.go delete mode 100644 executor/test/fktest/BUILD.bazel delete mode 100644 executor/test/fktest/foreign_key_test.go delete mode 100644 executor/test/fktest/main_test.go delete mode 100644 executor/test/indexmergereadtest/BUILD.bazel delete mode 100644 executor/test/indexmergereadtest/main_test.go delete mode 100644 executor/test/issuetest/BUILD.bazel delete mode 100644 executor/test/issuetest/main_test.go delete mode 100644 executor/test/jointest/BUILD.bazel delete mode 100644 executor/test/jointest/hashjoin/BUILD.bazel delete mode 100644 executor/test/jointest/hashjoin/main_test.go delete mode 100644 executor/test/jointest/join_test.go delete mode 100644 executor/test/jointest/main_test.go delete mode 100644 executor/test/kvtest/BUILD.bazel delete mode 100644 executor/test/kvtest/main_test.go delete mode 100644 executor/test/loaddatatest/BUILD.bazel delete mode 100644 executor/test/loaddatatest/main_test.go delete mode 100644 executor/test/loadremotetest/BUILD.bazel delete mode 100644 executor/test/loadremotetest/error_test.go delete mode 100644 executor/test/loadremotetest/util_test.go delete mode 100644 executor/test/memtest/BUILD.bazel delete mode 100644 executor/test/memtest/main_test.go delete mode 100644 executor/test/oomtest/BUILD.bazel delete mode 100644 executor/test/partitiontest/BUILD.bazel delete mode 100644 executor/test/partitiontest/partition_test.go delete mode 100644 executor/test/passwordtest/BUILD.bazel delete mode 100644 executor/test/seqtest/BUILD.bazel delete mode 100644 executor/test/seqtest/main_test.go delete mode 100644 executor/test/seqtest/prepared_test.go delete mode 100644 executor/test/showtest/BUILD.bazel delete mode 100644 executor/test/showtest/main_test.go delete mode 100644 executor/test/showtest/show_test.go delete mode 100644 executor/test/simpletest/BUILD.bazel delete mode 100644 executor/test/simpletest/simple_test.go delete mode 100644 executor/test/splittest/BUILD.bazel delete mode 100644 executor/test/tiflashtest/BUILD.bazel delete mode 100644 executor/test/tiflashtest/main_test.go delete mode 100644 executor/test/unstabletest/BUILD.bazel delete mode 100644 executor/test/unstabletest/main_test.go delete mode 100644 executor/test/writetest/BUILD.bazel delete mode 100644 executor/test/writetest/main_test.go delete mode 100644 executor/trace.go delete mode 100644 executor/trace_test.go delete mode 100644 executor/update.go delete mode 100644 executor/update_test.go delete mode 100644 executor/write.go delete mode 100644 expression/BUILD.bazel delete mode 100644 expression/aggregation/BUILD.bazel delete mode 100644 expression/aggregation/bench_test.go delete mode 100644 expression/aggregation/explain.go delete mode 100644 expression/aggregation/main_test.go delete mode 100644 expression/aggregation/util.go delete mode 100644 expression/aggregation/util_test.go delete mode 100644 expression/bench_test.go delete mode 100644 expression/collation_test.go delete mode 100644 expression/column.go delete mode 100644 expression/column_test.go delete mode 100644 expression/constant.go delete mode 100644 expression/errors.go delete mode 100644 expression/explain.go delete mode 100644 expression/expression_test.go delete mode 100644 expression/extension.go delete mode 100644 expression/generator/helper/BUILD.bazel delete mode 100644 expression/generator/helper/helper.go delete mode 100644 expression/helper.go delete mode 100644 expression/helper_test.go delete mode 100644 expression/integration_test/BUILD.bazel delete mode 100644 expression/integration_test/integration_test.go delete mode 100644 expression/integration_test/main_test.go delete mode 100644 expression/main_test.go delete mode 100644 expression/schema.go delete mode 100644 expression/test/multivaluedindex/BUILD.bazel delete mode 100644 expression/test/multivaluedindex/main_test.go delete mode 100644 expression/test/multivaluedindex/multi_valued_index_test.go delete mode 100644 expression/util.go delete mode 100644 expression/util_test.go delete mode 100644 extension/BUILD.bazel delete mode 100644 extension/_import/BUILD.bazel delete mode 100644 extension/bootstrap_test.go delete mode 160000 extension/enterprise delete mode 100644 extension/extensionimpl/BUILD.bazel delete mode 100644 extension/extensionimpl/bootstrap.go delete mode 100644 extension/main_test.go delete mode 100644 extension/registry_test.go delete mode 100644 extension/session.go delete mode 100644 infoschema/BUILD.bazel delete mode 100644 infoschema/builder.go delete mode 100644 infoschema/cache.go delete mode 100644 infoschema/cluster.go delete mode 100644 infoschema/error.go delete mode 100644 infoschema/infoschema.go delete mode 100644 infoschema/infoschema_test.go delete mode 100644 infoschema/internal/BUILD.bazel delete mode 100644 infoschema/main_test.go delete mode 100644 infoschema/metrics/BUILD.bazel delete mode 100644 infoschema/metrics/metrics.go delete mode 100644 infoschema/perfschema/BUILD.bazel delete mode 100644 infoschema/perfschema/main_test.go delete mode 100644 infoschema/perfschema/tables.go delete mode 100644 infoschema/perfschema/tables_test.go delete mode 100644 infoschema/tables.go delete mode 100644 infoschema/test/cachetest/BUILD.bazel delete mode 100644 infoschema/test/cachetest/cache_test.go delete mode 100644 infoschema/test/cachetest/main_test.go delete mode 100644 infoschema/test/clustertablestest/BUILD.bazel delete mode 100644 infoschema/test/clustertablestest/main_test.go delete mode 100644 infoschema/test/clustertablestest/tables_test.go delete mode 100644 keyspace/BUILD.bazel delete mode 100644 kv/BUILD.bazel delete mode 100644 kv/error.go delete mode 100644 kv/error_test.go delete mode 100644 kv/main_test.go delete mode 100644 kv/mpp.go delete mode 100644 kv/txn.go delete mode 100644 lock/BUILD.bazel delete mode 100644 meta/BUILD.bazel delete mode 100644 meta/autoid/BUILD.bazel delete mode 100644 meta/autoid/autoid.go delete mode 100644 meta/autoid/autoid_test.go delete mode 100644 meta/autoid/bench_test.go delete mode 100644 meta/autoid/errors.go delete mode 100644 meta/autoid/main_test.go delete mode 100644 meta/main_test.go delete mode 100644 metrics/BUILD.bazel delete mode 100644 metrics/import.go delete mode 100644 metrics/main_test.go delete mode 100644 metrics/metrics.go delete mode 100644 metrics/server.go delete mode 100644 metrics/telemetry.go delete mode 100644 owner/BUILD.bazel delete mode 100644 owner/fail_test.go delete mode 100644 owner/main_test.go delete mode 100644 owner/manager.go delete mode 100644 owner/manager_test.go delete mode 100644 owner/mock.go delete mode 100644 parser/BUILD.bazel delete mode 100644 parser/ast/BUILD.bazel delete mode 100644 parser/ast/ast.go delete mode 100644 parser/ast/base.go delete mode 100644 parser/ast/base_test.go delete mode 100644 parser/ast/ddl.go delete mode 100644 parser/ast/ddl_test.go delete mode 100644 parser/ast/format_test.go delete mode 100644 parser/ast/misc.go delete mode 100644 parser/ast/misc_test.go delete mode 100644 parser/ast/stats.go delete mode 100644 parser/ast/util_test.go delete mode 100644 parser/auth/BUILD.bazel delete mode 100644 parser/charset/BUILD.bazel delete mode 100644 parser/charset/charset.go delete mode 100644 parser/duration/BUILD.bazel delete mode 100644 parser/format/BUILD.bazel delete mode 100644 parser/goyacc/BUILD.bazel delete mode 100644 parser/model/BUILD.bazel delete mode 100644 parser/model/ddl.go delete mode 100644 parser/model/ddl_test.go delete mode 100644 parser/model/reorg.go delete mode 100644 parser/mysql/BUILD.bazel delete mode 100644 parser/opcode/BUILD.bazel delete mode 100644 parser/parser.go delete mode 100644 parser/parser_test.go delete mode 100644 parser/terror/BUILD.bazel delete mode 100644 parser/terror/terror.go delete mode 100644 parser/test_driver/BUILD.bazel delete mode 100644 parser/tidb/BUILD.bazel delete mode 100644 parser/types/BUILD.bazel delete mode 100644 parser/types/etc.go delete mode 100644 parser/types/etc_test.go delete mode 100644 parser/types/field_type.go delete mode 100644 parser/types/field_type_test.go create mode 100644 pkg/autoid_service/BUILD.bazel rename {autoid_service => pkg/autoid_service}/OWNERS (100%) create mode 100644 pkg/autoid_service/autoid.go create mode 100644 pkg/autoid_service/autoid_test.go create mode 100644 pkg/bindinfo/BUILD.bazel rename {bindinfo => pkg/bindinfo}/bind_cache.go (97%) rename {bindinfo => pkg/bindinfo}/bind_cache_test.go (96%) rename {bindinfo => pkg/bindinfo}/bind_record.go (97%) rename {bindinfo => pkg/bindinfo}/capture_test.go (99%) create mode 100644 pkg/bindinfo/handle.go create mode 100644 pkg/bindinfo/handle_test.go create mode 100644 pkg/bindinfo/internal/BUILD.bazel create mode 100644 pkg/bindinfo/internal/testutil.go create mode 100644 pkg/bindinfo/main_test.go create mode 100644 pkg/bindinfo/optimize_test.go rename {bindinfo => pkg/bindinfo}/session_handle.go (94%) rename {bindinfo => pkg/bindinfo}/session_handle_test.go (98%) create mode 100644 pkg/bindinfo/stat.go rename {bindinfo => pkg/bindinfo}/temptable_test.go (98%) create mode 100644 pkg/bindinfo/tests/BUILD.bazel rename {bindinfo => pkg/bindinfo}/tests/bind_test.go (99%) create mode 100644 pkg/bindinfo/tests/main_test.go create mode 100644 pkg/config/BUILD.bazel rename {config => pkg/config}/OWNERS (100%) create mode 100644 pkg/config/config.go rename {config => pkg/config}/config.toml.example (100%) rename {config => pkg/config}/config_test.go (99%) rename {config => pkg/config}/config_util.go (100%) rename {config => pkg/config}/config_util_test.go (100%) rename {config => pkg/config}/const.go (100%) create mode 100644 pkg/config/main_test.go create mode 100644 pkg/ddl/BUILD.bazel rename {ddl => pkg/ddl}/attributes_sql_test.go (96%) rename {ddl => pkg/ddl}/backfilling.go (97%) rename {ddl => pkg/ddl}/backfilling_clean_s3.go (92%) rename {ddl => pkg/ddl}/backfilling_dispatcher.go (95%) rename {ddl => pkg/ddl}/backfilling_dispatcher_test.go (95%) rename {ddl => pkg/ddl}/backfilling_dist_scheduler.go (94%) rename {ddl => pkg/ddl}/backfilling_import_cloud.go (93%) rename {ddl => pkg/ddl}/backfilling_import_local.go (90%) rename {ddl => pkg/ddl}/backfilling_merge_sort.go (92%) rename {ddl => pkg/ddl}/backfilling_operators.go (96%) rename {ddl => pkg/ddl}/backfilling_read_index.go (94%) rename {ddl => pkg/ddl}/backfilling_scheduler.go (95%) rename {ddl => pkg/ddl}/backfilling_test.go (92%) create mode 100644 pkg/ddl/callback.go rename {ddl => pkg/ddl}/cancel_test.go (98%) create mode 100644 pkg/ddl/cluster.go create mode 100644 pkg/ddl/cluster_test.go create mode 100644 pkg/ddl/column.go rename {ddl => pkg/ddl}/column_change_test.go (96%) rename {ddl => pkg/ddl}/column_modify_test.go (97%) create mode 100644 pkg/ddl/column_test.go rename {ddl => pkg/ddl}/column_type_change_test.go (95%) create mode 100644 pkg/ddl/constant.go create mode 100644 pkg/ddl/constraint.go rename {ddl => pkg/ddl}/constraint_test.go (95%) create mode 100644 pkg/ddl/copr/BUILD.bazel rename {ddl => pkg/ddl}/copr/copr_ctx.go (97%) rename {ddl => pkg/ddl}/copr/copr_ctx_test.go (96%) rename {ddl => pkg/ddl}/db_cache_test.go (94%) rename {ddl => pkg/ddl}/db_change_failpoints_test.go (86%) rename {ddl => pkg/ddl}/db_change_test.go (99%) rename {ddl => pkg/ddl}/db_integration_test.go (99%) rename {ddl => pkg/ddl}/db_rename_test.go (97%) rename {ddl => pkg/ddl}/db_table_test.go (97%) create mode 100644 pkg/ddl/db_test.go create mode 100644 pkg/ddl/ddl.go rename {ddl => pkg/ddl}/ddl_algorithm.go (97%) rename {ddl => pkg/ddl}/ddl_algorithm_test.go (97%) rename {ddl => pkg/ddl}/ddl_api.go (99%) rename {ddl => pkg/ddl}/ddl_api_test.go (95%) create mode 100644 pkg/ddl/ddl_error_test.go create mode 100644 pkg/ddl/ddl_test.go rename {ddl => pkg/ddl}/ddl_tiflash_api.go (97%) rename {ddl => pkg/ddl}/ddl_worker.go (98%) rename {ddl => pkg/ddl}/ddl_worker_test.go (96%) rename {ddl => pkg/ddl}/ddl_workerpool.go (98%) rename {ddl => pkg/ddl}/ddl_workerpool_test.go (100%) rename {ddl => pkg/ddl}/delete_range.go (97%) rename {ddl => pkg/ddl}/delete_range_util.go (100%) rename {ddl => pkg/ddl}/dist_owner.go (100%) rename {ddl => pkg/ddl}/export_test.go (85%) create mode 100644 pkg/ddl/fail_test.go create mode 100644 pkg/ddl/foreign_key.go create mode 100644 pkg/ddl/foreign_key_test.go rename {ddl => pkg/ddl}/generated_column.go (97%) create mode 100644 pkg/ddl/index.go rename {ddl => pkg/ddl}/index_change_test.go (96%) rename {ddl => pkg/ddl}/index_cop.go (91%) rename {ddl => pkg/ddl}/index_cop_test.go (91%) rename {ddl => pkg/ddl}/index_merge_tmp.go (96%) rename {ddl => pkg/ddl}/index_modify_test.go (98%) create mode 100644 pkg/ddl/ingest/BUILD.bazel rename {ddl => pkg/ddl}/ingest/backend.go (97%) rename {ddl => pkg/ddl}/ingest/backend_mgr.go (97%) rename {ddl => pkg/ddl}/ingest/checkpoint.go (98%) rename {ddl => pkg/ddl}/ingest/checkpoint_test.go (97%) create mode 100644 pkg/ddl/ingest/config.go rename {ddl => pkg/ddl}/ingest/disk_root.go (96%) rename {ddl => pkg/ddl}/ingest/engine.go (98%) rename {ddl => pkg/ddl}/ingest/engine_mgr.go (97%) rename {ddl => pkg/ddl}/ingest/env.go (94%) rename {ddl => pkg/ddl}/ingest/env_test.go (92%) create mode 100644 pkg/ddl/ingest/integration_test.go rename {ddl => pkg/ddl}/ingest/main_test.go (100%) rename {ddl => pkg/ddl}/ingest/mem_root.go (100%) rename {ddl => pkg/ddl}/ingest/mem_root_test.go (98%) rename {ddl => pkg/ddl}/ingest/message.go (97%) create mode 100644 pkg/ddl/ingest/mock.go create mode 100644 pkg/ddl/ingest/tests/BUILD.bazel create mode 100644 pkg/ddl/ingest/tests/partition_table_test.go create mode 100644 pkg/ddl/ingest/testutil/BUILD.bazel create mode 100644 pkg/ddl/ingest/testutil/testutil.go create mode 100644 pkg/ddl/integration_test.go create mode 100644 pkg/ddl/internal/session/BUILD.bazel create mode 100644 pkg/ddl/internal/session/session.go rename {ddl => pkg/ddl}/internal/session/session_pool.go (91%) rename {ddl => pkg/ddl}/internal/session/session_pool_test.go (95%) rename {ddl => pkg/ddl}/job_table.go (97%) rename {ddl => pkg/ddl}/job_table_test.go (92%) create mode 100644 pkg/ddl/label/BUILD.bazel rename {ddl => pkg/ddl}/label/attributes.go (100%) rename {ddl => pkg/ddl}/label/attributes_test.go (100%) rename {ddl => pkg/ddl}/label/errors.go (100%) create mode 100644 pkg/ddl/label/main_test.go create mode 100644 pkg/ddl/label/rule.go rename {ddl => pkg/ddl}/label/rule_test.go (98%) create mode 100644 pkg/ddl/main_test.go create mode 100644 pkg/ddl/mock.go rename {ddl => pkg/ddl}/modify_column_test.go (93%) rename {ddl => pkg/ddl}/multi_schema_change.go (98%) rename {ddl => pkg/ddl}/multi_schema_change_test.go (98%) rename {ddl => pkg/ddl}/mv_index_test.go (94%) create mode 100644 pkg/ddl/options.go rename {ddl => pkg/ddl}/options_test.go (92%) create mode 100644 pkg/ddl/partition.go create mode 100644 pkg/ddl/partition_test.go create mode 100644 pkg/ddl/placement/BUILD.bazel rename {ddl => pkg/ddl}/placement/bundle.go (99%) rename {ddl => pkg/ddl}/placement/bundle_test.go (99%) rename {ddl => pkg/ddl}/placement/common.go (100%) rename {ddl => pkg/ddl}/placement/common_test.go (100%) rename {ddl => pkg/ddl}/placement/constraint.go (100%) rename {ddl => pkg/ddl}/placement/constraint_test.go (100%) rename {ddl => pkg/ddl}/placement/constraints.go (100%) rename {ddl => pkg/ddl}/placement/constraints_test.go (100%) rename {ddl => pkg/ddl}/placement/errors.go (100%) rename {ddl => pkg/ddl}/placement/meta_bundle_test.go (97%) create mode 100644 pkg/ddl/placement/rule.go rename {ddl => pkg/ddl}/placement/rule_test.go (100%) rename {ddl => pkg/ddl}/placement_policy.go (98%) rename {ddl => pkg/ddl}/placement_policy_ddl_test.go (94%) rename {ddl => pkg/ddl}/placement_policy_test.go (97%) rename {ddl => pkg/ddl}/placement_sql_test.go (98%) rename {ddl => pkg/ddl}/primary_key_handle_test.go (96%) create mode 100644 pkg/ddl/reorg.go rename {ddl => pkg/ddl}/reorg_partition_test.go (96%) rename {ddl => pkg/ddl}/repair_table_test.go (94%) rename {ddl => pkg/ddl}/resource_group.go (93%) create mode 100644 pkg/ddl/resourcegroup/BUILD.bazel rename {ddl => pkg/ddl}/resourcegroup/errors.go (100%) create mode 100644 pkg/ddl/resourcegroup/group.go rename {ddl => pkg/ddl}/restart_test.go (96%) rename {ddl => pkg/ddl}/rollingback.go (98%) rename {ddl => pkg/ddl}/rollingback_test.go (86%) rename {ddl => pkg/ddl}/sanity_check.go (95%) create mode 100644 pkg/ddl/schema.go create mode 100644 pkg/ddl/schema_test.go create mode 100644 pkg/ddl/schematracker/BUILD.bazel create mode 100644 pkg/ddl/schematracker/checker.go rename {ddl => pkg/ddl}/schematracker/dm_tracker.go (98%) rename {ddl => pkg/ddl}/schematracker/dm_tracker_test.go (97%) rename {ddl => pkg/ddl}/schematracker/info_store.go (97%) rename {ddl => pkg/ddl}/schematracker/info_store_test.go (98%) rename {ddl => pkg/ddl}/sequence.go (96%) rename {ddl => pkg/ddl}/sequence_test.go (98%) rename {ddl => pkg/ddl}/split_region.go (95%) create mode 100644 pkg/ddl/stat.go create mode 100644 pkg/ddl/stat_test.go create mode 100644 pkg/ddl/syncer/BUILD.bazel rename {ddl => pkg/ddl}/syncer/state_syncer.go (97%) rename {ddl => pkg/ddl}/syncer/state_syncer_test.go (92%) rename {ddl => pkg/ddl}/syncer/syncer.go (98%) rename {ddl => pkg/ddl}/syncer/syncer_test.go (93%) create mode 100644 pkg/ddl/table.go rename {ddl => pkg/ddl}/table_lock.go (97%) rename {ddl => pkg/ddl}/table_modify_test.go (93%) rename {ddl => pkg/ddl}/table_split_test.go (91%) create mode 100644 pkg/ddl/table_test.go create mode 100644 pkg/ddl/tests/adminpause/BUILD.bazel rename {ddl => pkg/ddl}/tests/adminpause/ddl_data_generation.go (99%) rename {ddl => pkg/ddl}/tests/adminpause/ddl_stmt_cases.go (99%) rename {ddl => pkg/ddl}/tests/adminpause/global.go (88%) create mode 100644 pkg/ddl/tests/adminpause/main_test.go rename {ddl => pkg/ddl}/tests/adminpause/pause_cancel_test.go (96%) rename {ddl => pkg/ddl}/tests/adminpause/pause_negative_test.go (88%) rename {ddl => pkg/ddl}/tests/adminpause/pause_resume_test.go (97%) create mode 100644 pkg/ddl/tests/fail/BUILD.bazel rename {ddl => pkg/ddl}/tests/fail/fail_db_test.go (88%) create mode 100644 pkg/ddl/tests/fail/main_test.go create mode 100644 pkg/ddl/tests/fk/BUILD.bazel create mode 100644 pkg/ddl/tests/fk/foreign_key_test.go create mode 100644 pkg/ddl/tests/fk/main_test.go create mode 100644 pkg/ddl/tests/indexmerge/BUILD.bazel create mode 100644 pkg/ddl/tests/indexmerge/main_test.go rename {ddl => pkg/ddl}/tests/indexmerge/merge_test.go (93%) create mode 100644 pkg/ddl/tests/metadatalock/BUILD.bazel create mode 100644 pkg/ddl/tests/metadatalock/main_test.go rename {ddl => pkg/ddl}/tests/metadatalock/mdl_test.go (99%) create mode 100644 pkg/ddl/tests/multivaluedindex/BUILD.bazel create mode 100644 pkg/ddl/tests/multivaluedindex/main_test.go create mode 100644 pkg/ddl/tests/multivaluedindex/multi_valued_index_test.go create mode 100644 pkg/ddl/tests/partition/BUILD.bazel rename {ddl => pkg/ddl}/tests/partition/db_partition_test.go (98%) create mode 100644 pkg/ddl/tests/partition/main_test.go create mode 100644 pkg/ddl/tests/resourcegroup/BUILD.bazel create mode 100644 pkg/ddl/tests/resourcegroup/resource_group_test.go create mode 100644 pkg/ddl/tests/serial/BUILD.bazel create mode 100644 pkg/ddl/tests/serial/main_test.go rename {ddl => pkg/ddl}/tests/serial/serial_test.go (96%) create mode 100644 pkg/ddl/tests/tiflash/BUILD.bazel rename {ddl => pkg/ddl}/tests/tiflash/ddl_tiflash_test.go (92%) create mode 100644 pkg/ddl/tests/tiflash/main_test.go create mode 100644 pkg/ddl/testutil/BUILD.bazel create mode 100644 pkg/ddl/testutil/testutil.go rename {ddl => pkg/ddl}/tiflash_replica_test.go (92%) create mode 100644 pkg/ddl/ttl.go rename {ddl => pkg/ddl}/ttl_test.go (97%) create mode 100644 pkg/ddl/util/BUILD.bazel create mode 100644 pkg/ddl/util/callback/BUILD.bazel create mode 100644 pkg/ddl/util/callback/callback.go rename {ddl => pkg/ddl}/util/callback/callback_test.go (96%) rename {ddl => pkg/ddl}/util/dead_table_lock_checker.go (97%) rename {ddl => pkg/ddl}/util/event.go (97%) create mode 100644 pkg/ddl/util/main_test.go rename {ddl => pkg/ddl}/util/mock.go (100%) create mode 100644 pkg/ddl/util/util.go create mode 100644 pkg/distsql/BUILD.bazel rename {distsql => pkg/distsql}/OWNERS (100%) create mode 100644 pkg/distsql/bench_test.go create mode 100644 pkg/distsql/distsql.go create mode 100644 pkg/distsql/distsql_test.go create mode 100644 pkg/distsql/main_test.go rename {distsql => pkg/distsql}/request_builder.go (98%) rename {distsql => pkg/distsql}/request_builder_test.go (98%) rename {distsql => pkg/distsql}/select_result.go (97%) rename {distsql => pkg/distsql}/select_result_test.go (91%) create mode 100644 pkg/disttask/framework/BUILD.bazel create mode 100644 pkg/disttask/framework/dispatcher/BUILD.bazel create mode 100644 pkg/disttask/framework/dispatcher/dispatcher.go rename {disttask => pkg/disttask}/framework/dispatcher/dispatcher_manager.go (96%) create mode 100644 pkg/disttask/framework/dispatcher/dispatcher_test.go create mode 100644 pkg/disttask/framework/dispatcher/interface.go create mode 100644 pkg/disttask/framework/dispatcher/main_test.go create mode 100644 pkg/disttask/framework/framework_dynamic_dispatch_test.go rename {disttask => pkg/disttask}/framework/framework_err_handling_test.go (95%) create mode 100644 pkg/disttask/framework/framework_ha_test.go create mode 100644 pkg/disttask/framework/framework_pause_and_resume_test.go rename {disttask => pkg/disttask}/framework/framework_rollback_test.go (86%) rename {disttask => pkg/disttask}/framework/framework_test.go (82%) create mode 100644 pkg/disttask/framework/handle/BUILD.bazel create mode 100644 pkg/disttask/framework/handle/handle.go create mode 100644 pkg/disttask/framework/handle/handle_test.go create mode 100644 pkg/disttask/framework/mock/BUILD.bazel rename {disttask => pkg/disttask}/framework/mock/dispatcher_mock.go (95%) create mode 100644 pkg/disttask/framework/mock/execute/BUILD.bazel rename {disttask => pkg/disttask}/framework/mock/execute/execute_mock.go (95%) rename {disttask => pkg/disttask}/framework/mock/plan_mock.go (95%) rename {disttask => pkg/disttask}/framework/mock/scheduler_mock.go (98%) create mode 100644 pkg/disttask/framework/planner/BUILD.bazel create mode 100644 pkg/disttask/framework/planner/plan.go create mode 100644 pkg/disttask/framework/planner/plan_test.go create mode 100644 pkg/disttask/framework/planner/planner.go create mode 100644 pkg/disttask/framework/planner/planner_test.go create mode 100644 pkg/disttask/framework/proto/BUILD.bazel rename {disttask => pkg/disttask}/framework/proto/task.go (100%) rename {disttask => pkg/disttask}/framework/proto/task_test.go (100%) create mode 100644 pkg/disttask/framework/scheduler/BUILD.bazel create mode 100644 pkg/disttask/framework/scheduler/execute/BUILD.bazel create mode 100644 pkg/disttask/framework/scheduler/execute/interface.go create mode 100644 pkg/disttask/framework/scheduler/execute/summary.go create mode 100644 pkg/disttask/framework/scheduler/interface.go create mode 100644 pkg/disttask/framework/scheduler/manager.go create mode 100644 pkg/disttask/framework/scheduler/manager_test.go rename {disttask => pkg/disttask}/framework/scheduler/register.go (93%) rename {disttask => pkg/disttask}/framework/scheduler/register_test.go (95%) create mode 100644 pkg/disttask/framework/scheduler/scheduler.go rename {disttask => pkg/disttask}/framework/scheduler/scheduler_test.go (99%) create mode 100644 pkg/disttask/framework/storage/BUILD.bazel create mode 100644 pkg/disttask/framework/storage/table_test.go rename {disttask => pkg/disttask}/framework/storage/task_table.go (98%) create mode 100644 pkg/disttask/framework/storage/util.go create mode 100644 pkg/disttask/importinto/BUILD.bazel rename {disttask => pkg/disttask}/importinto/clean_s3.go (93%) create mode 100644 pkg/disttask/importinto/dispatcher.go create mode 100644 pkg/disttask/importinto/dispatcher_test.go rename {disttask => pkg/disttask}/importinto/dispatcher_testkit_test.go (94%) rename {disttask => pkg/disttask}/importinto/encode_and_sort_operator.go (96%) rename {disttask => pkg/disttask}/importinto/encode_and_sort_operator_test.go (94%) create mode 100644 pkg/disttask/importinto/job.go rename {disttask => pkg/disttask}/importinto/job_testkit_test.go (91%) create mode 100644 pkg/disttask/importinto/metrics.go rename {disttask => pkg/disttask}/importinto/metrics_test.go (100%) create mode 100644 pkg/disttask/importinto/mock/BUILD.bazel rename {disttask => pkg/disttask}/importinto/mock/import_mock.go (94%) create mode 100644 pkg/disttask/importinto/planner.go create mode 100644 pkg/disttask/importinto/planner_test.go rename {disttask => pkg/disttask}/importinto/proto.go (97%) create mode 100644 pkg/disttask/importinto/scheduler.go rename {disttask => pkg/disttask}/importinto/subtask_executor.go (95%) rename {disttask => pkg/disttask}/importinto/subtask_executor_test.go (81%) rename {disttask => pkg/disttask}/importinto/wrapper.go (100%) rename {disttask => pkg/disttask}/importinto/wrapper_test.go (100%) create mode 100644 pkg/disttask/operator/BUILD.bazel rename {disttask => pkg/disttask}/operator/compose.go (100%) rename {disttask => pkg/disttask}/operator/operator.go (96%) rename {disttask => pkg/disttask}/operator/pipeline.go (100%) rename {disttask => pkg/disttask}/operator/pipeline_test.go (100%) rename {disttask => pkg/disttask}/operator/wrapper.go (100%) create mode 100644 pkg/domain/BUILD.bazel create mode 100644 pkg/domain/db_test.go rename {domain => pkg/domain}/domain.go (97%) rename {domain => pkg/domain}/domain_sysvars.go (98%) rename {domain => pkg/domain}/domain_test.go (81%) rename {domain => pkg/domain}/domain_utils_test.go (92%) rename {domain => pkg/domain}/domainctx.go (96%) rename {domain => pkg/domain}/domainctx_test.go (95%) create mode 100644 pkg/domain/extract.go create mode 100644 pkg/domain/extract_test.go create mode 100644 pkg/domain/globalconfigsync/BUILD.bazel rename {domain => pkg/domain}/globalconfigsync/globalconfig.go (97%) rename {domain => pkg/domain}/globalconfigsync/globalconfig_test.go (94%) rename {domain => pkg/domain}/historical_stats.go (92%) create mode 100644 pkg/domain/infosync/BUILD.bazel create mode 100644 pkg/domain/infosync/error.go rename {domain => pkg/domain}/infosync/info.go (97%) rename {domain => pkg/domain}/infosync/info_test.go (90%) rename {domain => pkg/domain}/infosync/label_manager.go (98%) rename {domain => pkg/domain}/infosync/mock_info.go (94%) rename {domain => pkg/domain}/infosync/placement_manager.go (97%) create mode 100644 pkg/domain/infosync/region.go rename {domain => pkg/domain}/infosync/resource_manager_client.go (98%) rename {domain => pkg/domain}/infosync/schedule_manager.go (98%) rename {domain => pkg/domain}/infosync/tiflash_manager.go (99%) create mode 100644 pkg/domain/main_test.go create mode 100644 pkg/domain/metrics/BUILD.bazel create mode 100644 pkg/domain/metrics/metrics.go rename {domain => pkg/domain}/optimize_trace.go (100%) create mode 100644 pkg/domain/plan_replayer.go rename {domain => pkg/domain}/plan_replayer_dump.go (97%) rename {domain => pkg/domain}/plan_replayer_handle_test.go (98%) create mode 100644 pkg/domain/plan_replayer_test.go create mode 100644 pkg/domain/resourcegroup/BUILD.bazel create mode 100644 pkg/domain/resourcegroup/runaway.go create mode 100644 pkg/domain/runaway.go rename {domain => pkg/domain}/schema_checker.go (98%) rename {domain => pkg/domain}/schema_checker_test.go (98%) rename {domain => pkg/domain}/schema_validator.go (97%) rename {domain => pkg/domain}/schema_validator_test.go (99%) rename {domain => pkg/domain}/session_pool_test.go (100%) rename {domain => pkg/domain}/sysvar_cache.go (95%) rename {domain => pkg/domain}/test_helper.go (95%) rename {domain => pkg/domain}/topn_slow_query.go (98%) rename {domain => pkg/domain}/topn_slow_query_test.go (100%) create mode 100644 pkg/errno/BUILD.bazel rename {errno => pkg/errno}/errcode.go (100%) rename {errno => pkg/errno}/errname.go (99%) rename {errno => pkg/errno}/infoschema.go (100%) rename {errno => pkg/errno}/infoschema_test.go (100%) rename {errno => pkg/errno}/logredaction.md (100%) create mode 100644 pkg/errno/main_test.go create mode 100644 pkg/executor/BUILD.bazel rename {executor => pkg/executor}/adapter.go (97%) rename {executor => pkg/executor}/adapter_test.go (92%) create mode 100644 pkg/executor/admin.go rename {executor => pkg/executor}/admin_plugins.go (86%) rename {executor => pkg/executor}/admin_telemetry.go (91%) create mode 100644 pkg/executor/aggfuncs/BUILD.bazel rename {executor => pkg/executor}/aggfuncs/aggfunc_test.go (98%) rename {executor => pkg/executor}/aggfuncs/aggfuncs.go (98%) create mode 100644 pkg/executor/aggfuncs/builder.go rename {executor => pkg/executor}/aggfuncs/export_test.go (100%) rename {executor => pkg/executor}/aggfuncs/func_avg.go (98%) rename {executor => pkg/executor}/aggfuncs/func_avg_test.go (91%) rename {executor => pkg/executor}/aggfuncs/func_bitfuncs.go (98%) rename {executor => pkg/executor}/aggfuncs/func_bitfuncs_test.go (91%) rename {executor => pkg/executor}/aggfuncs/func_count.go (99%) rename {executor => pkg/executor}/aggfuncs/func_count_distinct.go (98%) rename {executor => pkg/executor}/aggfuncs/func_count_test.go (97%) rename {executor => pkg/executor}/aggfuncs/func_cume_dist.go (95%) rename {executor => pkg/executor}/aggfuncs/func_cume_dist_test.go (89%) rename {executor => pkg/executor}/aggfuncs/func_first_row.go (98%) rename {executor => pkg/executor}/aggfuncs/func_first_row_test.go (95%) rename {executor => pkg/executor}/aggfuncs/func_group_concat.go (97%) rename {executor => pkg/executor}/aggfuncs/func_group_concat_test.go (93%) rename {executor => pkg/executor}/aggfuncs/func_json_arrayagg.go (95%) rename {executor => pkg/executor}/aggfuncs/func_json_arrayagg_test.go (95%) rename {executor => pkg/executor}/aggfuncs/func_json_objectagg.go (95%) rename {executor => pkg/executor}/aggfuncs/func_json_objectagg_test.go (95%) rename {executor => pkg/executor}/aggfuncs/func_lead_lag.go (95%) rename {executor => pkg/executor}/aggfuncs/func_lead_lag_test.go (97%) rename {executor => pkg/executor}/aggfuncs/func_max_min.go (99%) rename {executor => pkg/executor}/aggfuncs/func_max_min_test.go (98%) rename {executor => pkg/executor}/aggfuncs/func_ntile.go (96%) rename {executor => pkg/executor}/aggfuncs/func_ntile_test.go (89%) rename {executor => pkg/executor}/aggfuncs/func_percent_rank.go (96%) rename {executor => pkg/executor}/aggfuncs/func_percent_rank_test.go (89%) rename {executor => pkg/executor}/aggfuncs/func_percentile.go (98%) rename {executor => pkg/executor}/aggfuncs/func_percentile_test.go (91%) rename {executor => pkg/executor}/aggfuncs/func_rank.go (95%) rename {executor => pkg/executor}/aggfuncs/func_rank_test.go (89%) rename {executor => pkg/executor}/aggfuncs/func_stddevpop.go (94%) rename {executor => pkg/executor}/aggfuncs/func_stddevpop_test.go (92%) rename {executor => pkg/executor}/aggfuncs/func_stddevsamp.go (94%) rename {executor => pkg/executor}/aggfuncs/func_stddevsamp_test.go (92%) rename {executor => pkg/executor}/aggfuncs/func_sum.go (97%) rename {executor => pkg/executor}/aggfuncs/func_sum_test.go (91%) rename {executor => pkg/executor}/aggfuncs/func_value.go (98%) rename {executor => pkg/executor}/aggfuncs/func_value_test.go (95%) rename {executor => pkg/executor}/aggfuncs/func_varpop.go (97%) rename {executor => pkg/executor}/aggfuncs/func_varpop_test.go (89%) rename {executor => pkg/executor}/aggfuncs/func_varsamp.go (94%) rename {executor => pkg/executor}/aggfuncs/func_varsamp_test.go (92%) create mode 100644 pkg/executor/aggfuncs/main_test.go rename {executor => pkg/executor}/aggfuncs/row_number.go (95%) rename {executor => pkg/executor}/aggfuncs/row_number_test.go (87%) rename {executor => pkg/executor}/aggfuncs/window_func_test.go (95%) create mode 100644 pkg/executor/aggregate/BUILD.bazel rename {executor => pkg/executor}/aggregate/agg_hash_base_worker.go (92%) rename {executor => pkg/executor}/aggregate/agg_hash_executor.go (97%) rename {executor => pkg/executor}/aggregate/agg_hash_final_worker.go (95%) rename {executor => pkg/executor}/aggregate/agg_hash_partial_worker.go (96%) rename {executor => pkg/executor}/aggregate/agg_spill.go (95%) rename {executor => pkg/executor}/aggregate/agg_stream_executor.go (96%) rename {executor => pkg/executor}/aggregate/agg_util.go (93%) create mode 100644 pkg/executor/analyze.go rename {executor => pkg/executor}/analyze_col.go (95%) rename {executor => pkg/executor}/analyze_col_v2.go (97%) rename {executor => pkg/executor}/analyze_global_stats.go (95%) rename {executor => pkg/executor}/analyze_idx.go (95%) create mode 100644 pkg/executor/analyze_test.go rename {executor => pkg/executor}/analyze_utils.go (94%) rename {executor => pkg/executor}/analyze_utils_test.go (95%) rename {executor => pkg/executor}/analyze_worker.go (89%) create mode 100644 pkg/executor/asyncloaddata/BUILD.bazel rename {executor => pkg/executor}/asyncloaddata/main_test.go (100%) rename {executor => pkg/executor}/asyncloaddata/progress.go (100%) rename {executor => pkg/executor}/asyncloaddata/progress_test.go (100%) create mode 100644 pkg/executor/asyncloaddata/util.go create mode 100644 pkg/executor/asyncloaddata/util_test.go rename {executor => pkg/executor}/batch_checker.go (93%) rename {executor => pkg/executor}/batch_point_get.go (95%) rename {executor => pkg/executor}/batch_point_get_test.go (97%) create mode 100644 pkg/executor/benchmark_test.go rename {executor => pkg/executor}/bind.go (95%) rename {executor => pkg/executor}/brie.go (95%) rename {executor => pkg/executor}/brie_test.go (91%) create mode 100644 pkg/executor/builder.go rename {executor => pkg/executor}/change.go (88%) rename {executor => pkg/executor}/charset_test.go (97%) create mode 100644 pkg/executor/checksum.go rename {executor => pkg/executor}/chunk_size_control_test.go (95%) rename {executor => pkg/executor}/cluster_table_test.go (98%) rename {executor => pkg/executor}/compact_table.go (97%) rename {executor => pkg/executor}/compact_table_test.go (99%) rename {executor => pkg/executor}/compiler.go (96%) rename {executor => pkg/executor}/concurrent_map.go (97%) rename {executor => pkg/executor}/concurrent_map_test.go (97%) rename {executor => pkg/executor}/copr_cache_test.go (87%) create mode 100644 pkg/executor/coprocessor.go rename {executor => pkg/executor}/cte.go (97%) rename {executor => pkg/executor}/cte_table_reader.go (94%) create mode 100644 pkg/executor/cte_test.go create mode 100644 pkg/executor/ddl.go rename {executor => pkg/executor}/delete.go (94%) rename {executor => pkg/executor}/delete_test.go (97%) create mode 100644 pkg/executor/distsql.go create mode 100644 pkg/executor/distsql_test.go create mode 100644 pkg/executor/executor.go rename {executor => pkg/executor}/executor_failpoint_test.go (90%) rename {executor => pkg/executor}/executor_pkg_test.go (93%) rename {executor => pkg/executor}/executor_required_rows_test.go (97%) create mode 100644 pkg/executor/executor_test.go rename {executor => pkg/executor}/executor_txn_test.go (99%) create mode 100644 pkg/executor/explain.go rename {executor => pkg/executor}/explain_test.go (99%) rename {executor => pkg/executor}/explain_unit_test.go (88%) rename {executor => pkg/executor}/explainfor_test.go (99%) create mode 100644 pkg/executor/foreign_key.go rename {executor => pkg/executor}/grant.go (97%) rename {executor => pkg/executor}/grant_test.go (99%) rename {executor => pkg/executor}/hash_table.go (98%) rename {executor => pkg/executor}/hash_table_test.go (96%) rename {executor => pkg/executor}/historical_stats_test.go (92%) rename {executor => pkg/executor}/hot_regions_history_table_test.go (98%) rename {executor => pkg/executor}/import_into.go (91%) rename {executor => pkg/executor}/import_into_test.go (97%) create mode 100644 pkg/executor/importer/BUILD.bazel rename {executor => pkg/executor}/importer/chunk_process.go (99%) rename {executor => pkg/executor}/importer/chunk_process_test.go (97%) rename {executor => pkg/executor}/importer/chunk_process_testkit_test.go (94%) rename {executor => pkg/executor}/importer/engine_process.go (98%) create mode 100644 pkg/executor/importer/import.go rename {executor => pkg/executor}/importer/import_test.go (94%) create mode 100644 pkg/executor/importer/job.go rename {executor => pkg/executor}/importer/job_test.go (98%) rename {executor => pkg/executor}/importer/kv_encode.go (94%) rename {executor => pkg/executor}/importer/precheck.go (94%) rename {executor => pkg/executor}/importer/precheck_test.go (94%) rename {executor => pkg/executor}/importer/table_import.go (98%) rename {executor => pkg/executor}/importer/table_import_test.go (98%) rename {executor => pkg/executor}/index_advise.go (93%) rename {executor => pkg/executor}/index_advise_test.go (98%) rename {executor => pkg/executor}/index_lookup_hash_join.go (98%) rename {executor => pkg/executor}/index_lookup_join.go (97%) rename {executor => pkg/executor}/index_lookup_join_test.go (98%) rename {executor => pkg/executor}/index_lookup_merge_join.go (97%) rename {executor => pkg/executor}/index_lookup_merge_join_test.go (95%) rename {executor => pkg/executor}/index_merge_reader.go (98%) rename {executor => pkg/executor}/infoschema_cluster_table_test.go (95%) rename {executor => pkg/executor}/infoschema_reader.go (98%) rename {executor => pkg/executor}/infoschema_reader_internal_test.go (96%) rename {executor => pkg/executor}/infoschema_reader_test.go (98%) rename {executor => pkg/executor}/insert.go (95%) rename {executor => pkg/executor}/insert_common.go (97%) rename {executor => pkg/executor}/insert_test.go (99%) rename {executor => pkg/executor}/inspection_common.go (93%) rename {executor => pkg/executor}/inspection_common_test.go (93%) rename {executor => pkg/executor}/inspection_profile.go (99%) rename {executor => pkg/executor}/inspection_result.go (98%) rename {executor => pkg/executor}/inspection_result_test.go (98%) rename {executor => pkg/executor}/inspection_summary.go (97%) rename {executor => pkg/executor}/inspection_summary_test.go (93%) create mode 100644 pkg/executor/internal/BUILD.bazel create mode 100644 pkg/executor/internal/applycache/BUILD.bazel rename {executor => pkg/executor}/internal/applycache/apply_cache.go (92%) rename {executor => pkg/executor}/internal/applycache/apply_cache_test.go (93%) create mode 100644 pkg/executor/internal/applycache/main_test.go create mode 100644 pkg/executor/internal/builder/BUILD.bazel rename {executor => pkg/executor}/internal/builder/builder_utils.go (90%) create mode 100644 pkg/executor/internal/calibrateresource/BUILD.bazel rename {executor => pkg/executor}/internal/calibrateresource/calibrate_resource.go (97%) rename {executor => pkg/executor}/internal/calibrateresource/calibrate_resource_test.go (99%) create mode 100644 pkg/executor/internal/calibrateresource/main_test.go create mode 100644 pkg/executor/internal/exec/BUILD.bazel create mode 100644 pkg/executor/internal/exec/executor.go create mode 100644 pkg/executor/internal/mpp/BUILD.bazel rename {executor => pkg/executor}/internal/mpp/local_mpp_coordinator.go (97%) rename {executor => pkg/executor}/internal/mpp/local_mpp_coordinator_test.go (95%) create mode 100644 pkg/executor/internal/pdhelper/BUILD.bazel create mode 100644 pkg/executor/internal/pdhelper/main_test.go create mode 100644 pkg/executor/internal/pdhelper/pd.go rename {executor => pkg/executor}/internal/pdhelper/pd_test.go (98%) create mode 100644 pkg/executor/internal/querywatch/BUILD.bazel create mode 100644 pkg/executor/internal/querywatch/main_test.go rename {executor => pkg/executor}/internal/querywatch/query_watch.go (92%) rename {executor => pkg/executor}/internal/querywatch/query_watch_test.go (98%) create mode 100644 pkg/executor/internal/testkit.go create mode 100644 pkg/executor/internal/util/BUILD.bazel rename {executor => pkg/executor}/internal/util/partition_table.go (100%) rename {executor => pkg/executor}/internal/util/util.go (100%) create mode 100644 pkg/executor/internal/vecgroupchecker/BUILD.bazel create mode 100644 pkg/executor/internal/vecgroupchecker/main_test.go rename {executor => pkg/executor}/internal/vecgroupchecker/vec_group_checker.go (98%) rename {executor => pkg/executor}/internal/vecgroupchecker/vec_group_checker_test.go (97%) create mode 100644 pkg/executor/join.go rename {executor => pkg/executor}/join_pkg_test.go (93%) create mode 100644 pkg/executor/join_test.go rename {executor => pkg/executor}/joiner.go (99%) rename {executor => pkg/executor}/joiner_test.go (94%) rename {executor => pkg/executor}/load_data.go (96%) rename {executor => pkg/executor}/load_stats.go (90%) create mode 100644 pkg/executor/lockstats/BUILD.bazel rename {executor => pkg/executor}/lockstats/lock_stats_executor.go (92%) rename {executor => pkg/executor}/lockstats/lock_stats_executor_test.go (95%) rename {executor => pkg/executor}/lockstats/unlock_stats_executor.go (93%) create mode 100644 pkg/executor/main_test.go rename {executor => pkg/executor}/mem_reader.go (97%) rename {executor => pkg/executor}/memtable_reader.go (97%) rename {executor => pkg/executor}/memtable_reader_test.go (98%) rename {executor => pkg/executor}/merge_join.go (96%) rename {executor => pkg/executor}/merge_join_test.go (98%) create mode 100644 pkg/executor/metrics/BUILD.bazel create mode 100644 pkg/executor/metrics/metrics.go rename {executor => pkg/executor}/metrics_reader.go (96%) rename {executor => pkg/executor}/metrics_reader_test.go (92%) rename {executor => pkg/executor}/mpp_gather.go (89%) create mode 100644 pkg/executor/mppcoordmanager/BUILD.bazel rename {executor => pkg/executor}/mppcoordmanager/mpp_coordinator_manager.go (97%) rename {executor => pkg/executor}/mppcoordmanager/mpp_coordinator_manager_test.go (97%) rename {executor => pkg/executor}/opt_rule_blacklist.go (83%) rename {executor => pkg/executor}/parallel_apply.go (96%) rename {executor => pkg/executor}/parallel_apply_test.go (98%) create mode 100644 pkg/executor/partition_table_test.go rename {executor => pkg/executor}/pipelined_window.go (96%) rename {executor => pkg/executor}/pkg_test.go (91%) create mode 100644 pkg/executor/plan_replayer.go rename {executor => pkg/executor}/point_get.go (96%) rename {executor => pkg/executor}/point_get_test.go (98%) rename {executor => pkg/executor}/prepared.go (90%) create mode 100644 pkg/executor/prepared_test.go rename {executor => pkg/executor}/projection.go (97%) rename {executor => pkg/executor}/recover_test.go (90%) rename {executor => pkg/executor}/reload_expr_pushdown_blacklist.go (97%) rename {executor => pkg/executor}/replace.go (95%) rename {executor => pkg/executor}/resource_tag_test.go (91%) rename {executor => pkg/executor}/revoke.go (95%) rename {executor => pkg/executor}/revoke_test.go (98%) rename {executor => pkg/executor}/rowid_test.go (98%) create mode 100644 pkg/executor/sample.go create mode 100644 pkg/executor/sample_test.go rename {executor => pkg/executor}/select_into.go (96%) rename {executor => pkg/executor}/select_into_test.go (98%) create mode 100644 pkg/executor/set.go rename {executor => pkg/executor}/set_config.go (92%) create mode 100644 pkg/executor/set_test.go rename {executor => pkg/executor}/show.go (97%) rename {executor => pkg/executor}/show_placement.go (96%) rename {executor => pkg/executor}/show_placement_labels_test.go (96%) rename {executor => pkg/executor}/show_placement_test.go (99%) rename {executor => pkg/executor}/show_stats.go (98%) rename {executor => pkg/executor}/show_stats_test.go (99%) create mode 100644 pkg/executor/show_test.go rename {executor => pkg/executor}/shuffle.go (96%) rename {executor => pkg/executor}/shuffle_test.go (89%) rename {executor => pkg/executor}/simple.go (98%) create mode 100644 pkg/executor/simple_test.go rename {executor => pkg/executor}/slow_query.go (97%) rename {executor => pkg/executor}/slow_query_sql_test.go (98%) rename {executor => pkg/executor}/slow_query_test.go (96%) create mode 100644 pkg/executor/sort.go rename {executor => pkg/executor}/sort_test.go (86%) rename {executor => pkg/executor}/split.go (97%) create mode 100644 pkg/executor/split_test.go rename {executor => pkg/executor}/stale_txn_test.go (93%) rename {executor => pkg/executor}/statement_context_test.go (97%) create mode 100644 pkg/executor/stmtsummary.go rename {executor => pkg/executor}/stmtsummary_test.go (95%) rename {executor => pkg/executor}/table_reader.go (94%) rename {executor => pkg/executor}/table_readers_required_rows_test.go (92%) create mode 100644 pkg/executor/temporary_table_test.go create mode 100644 pkg/executor/test/admintest/BUILD.bazel rename {executor => pkg/executor}/test/admintest/admin_test.go (98%) create mode 100644 pkg/executor/test/admintest/main_test.go create mode 100644 pkg/executor/test/aggregate/BUILD.bazel rename {executor => pkg/executor}/test/aggregate/aggregate_test.go (98%) create mode 100644 pkg/executor/test/aggregate/main_test.go rename {executor => pkg/executor}/test/aggregate/testdata/agg_suite_in.json (100%) rename {executor => pkg/executor}/test/aggregate/testdata/agg_suite_out.json (100%) create mode 100644 pkg/executor/test/analyzetest/BUILD.bazel rename {executor => pkg/executor}/test/analyzetest/analyze_bench_test.go (97%) create mode 100644 pkg/executor/test/analyzetest/analyze_test.go create mode 100644 pkg/executor/test/analyzetest/main_test.go create mode 100644 pkg/executor/test/analyzetest/memorycontrol/BUILD.bazel create mode 100644 pkg/executor/test/analyzetest/memorycontrol/main_test.go rename {executor => pkg/executor}/test/analyzetest/memorycontrol/memory_control_test.go (80%) create mode 100644 pkg/executor/test/autoidtest/BUILD.bazel create mode 100644 pkg/executor/test/autoidtest/autoid_test.go create mode 100644 pkg/executor/test/autoidtest/main_test.go create mode 100644 pkg/executor/test/ddl/BUILD.bazel create mode 100644 pkg/executor/test/ddl/ddl_test.go create mode 100644 pkg/executor/test/ddl/main_test.go create mode 100644 pkg/executor/test/distsqltest/BUILD.bazel create mode 100644 pkg/executor/test/distsqltest/distsql_test.go create mode 100644 pkg/executor/test/distsqltest/main_test.go create mode 100644 pkg/executor/test/executor/BUILD.bazel create mode 100644 pkg/executor/test/executor/executor_test.go create mode 100644 pkg/executor/test/executor/main_test.go create mode 100644 pkg/executor/test/fktest/BUILD.bazel create mode 100644 pkg/executor/test/fktest/foreign_key_test.go create mode 100644 pkg/executor/test/fktest/main_test.go create mode 100644 pkg/executor/test/indexmergereadtest/BUILD.bazel rename {executor => pkg/executor}/test/indexmergereadtest/index_merge_reader_test.go (93%) create mode 100644 pkg/executor/test/indexmergereadtest/main_test.go create mode 100644 pkg/executor/test/issuetest/BUILD.bazel rename {executor => pkg/executor}/test/issuetest/executor_issue_test.go (92%) create mode 100644 pkg/executor/test/issuetest/main_test.go create mode 100644 pkg/executor/test/jointest/BUILD.bazel create mode 100644 pkg/executor/test/jointest/hashjoin/BUILD.bazel rename {executor => pkg/executor}/test/jointest/hashjoin/hash_join_test.go (95%) create mode 100644 pkg/executor/test/jointest/hashjoin/main_test.go create mode 100644 pkg/executor/test/jointest/join_test.go create mode 100644 pkg/executor/test/jointest/main_test.go create mode 100644 pkg/executor/test/kvtest/BUILD.bazel rename {executor => pkg/executor}/test/kvtest/kv_test.go (98%) create mode 100644 pkg/executor/test/kvtest/main_test.go create mode 100644 pkg/executor/test/loaddatatest/BUILD.bazel rename {executor => pkg/executor}/test/loaddatatest/load_data_test.go (99%) create mode 100644 pkg/executor/test/loaddatatest/main_test.go create mode 100644 pkg/executor/test/loadremotetest/BUILD.bazel create mode 100644 pkg/executor/test/loadremotetest/error_test.go rename {executor => pkg/executor}/test/loadremotetest/main_test.go (100%) rename {executor => pkg/executor}/test/loadremotetest/multi_file_test.go (99%) rename {executor => pkg/executor}/test/loadremotetest/one_csv_test.go (99%) create mode 100644 pkg/executor/test/loadremotetest/util_test.go create mode 100644 pkg/executor/test/memtest/BUILD.bazel create mode 100644 pkg/executor/test/memtest/main_test.go rename {executor => pkg/executor}/test/memtest/mem_test.go (97%) create mode 100644 pkg/executor/test/oomtest/BUILD.bazel rename {executor => pkg/executor}/test/oomtest/oom_test.go (96%) create mode 100644 pkg/executor/test/partitiontest/BUILD.bazel rename {executor => pkg/executor}/test/partitiontest/main_test.go (100%) create mode 100644 pkg/executor/test/partitiontest/partition_test.go create mode 100644 pkg/executor/test/passwordtest/BUILD.bazel rename {executor => pkg/executor}/test/passwordtest/main_test.go (100%) rename {executor => pkg/executor}/test/passwordtest/password_management_test.go (99%) create mode 100644 pkg/executor/test/seqtest/BUILD.bazel create mode 100644 pkg/executor/test/seqtest/main_test.go create mode 100644 pkg/executor/test/seqtest/prepared_test.go rename {executor => pkg/executor}/test/seqtest/seq_executor_test.go (95%) create mode 100644 pkg/executor/test/showtest/BUILD.bazel create mode 100644 pkg/executor/test/showtest/main_test.go create mode 100644 pkg/executor/test/showtest/show_test.go create mode 100644 pkg/executor/test/simpletest/BUILD.bazel rename {executor => pkg/executor}/test/simpletest/chunk_reuse_test.go (99%) rename {executor => pkg/executor}/test/simpletest/main_test.go (100%) create mode 100644 pkg/executor/test/simpletest/simple_test.go create mode 100644 pkg/executor/test/splittest/BUILD.bazel rename {executor => pkg/executor}/test/splittest/main_test.go (100%) rename {executor => pkg/executor}/test/splittest/split_table_test.go (98%) create mode 100644 pkg/executor/test/tiflashtest/BUILD.bazel create mode 100644 pkg/executor/test/tiflashtest/main_test.go rename {executor => pkg/executor}/test/tiflashtest/tiflash_test.go (94%) create mode 100644 pkg/executor/test/unstabletest/BUILD.bazel rename {executor => pkg/executor}/test/unstabletest/README.md (100%) create mode 100644 pkg/executor/test/unstabletest/main_test.go rename {executor => pkg/executor}/test/unstabletest/memory_test.go (96%) rename {executor => pkg/executor}/test/unstabletest/unstable_test.go (98%) create mode 100644 pkg/executor/test/writetest/BUILD.bazel create mode 100644 pkg/executor/test/writetest/main_test.go rename {executor => pkg/executor}/test/writetest/write_test.go (98%) rename {executor => pkg/executor}/testdata/analyze_test_data.sql (100%) rename {executor => pkg/executor}/testdata/executor_suite_in.json (100%) rename {executor => pkg/executor}/testdata/executor_suite_out.json (100%) rename {executor => pkg/executor}/testdata/point_get_suite_in.json (100%) rename {executor => pkg/executor}/testdata/point_get_suite_out.json (100%) rename {executor => pkg/executor}/testdata/prepare_suite_in.json (100%) rename {executor => pkg/executor}/testdata/prepare_suite_out.json (100%) rename {executor => pkg/executor}/testdata/slow_query_suite_in.json (100%) rename {executor => pkg/executor}/testdata/slow_query_suite_out.json (100%) rename {executor => pkg/executor}/testdata/tiflash_v620_dt_segments.json (100%) rename {executor => pkg/executor}/testdata/tiflash_v620_dt_tables.json (100%) rename {executor => pkg/executor}/testdata/tiflash_v630_dt_segments.json (100%) rename {executor => pkg/executor}/testdata/tiflash_v640_dt_tables.json (100%) rename {executor => pkg/executor}/tikv_regions_peers_table_test.go (97%) create mode 100644 pkg/executor/trace.go create mode 100644 pkg/executor/trace_test.go rename {executor => pkg/executor}/union_scan.go (93%) rename {executor => pkg/executor}/union_scan_test.go (99%) create mode 100644 pkg/executor/update.go create mode 100644 pkg/executor/update_test.go rename {executor => pkg/executor}/utils.go (100%) rename {executor => pkg/executor}/utils_test.go (95%) rename {executor => pkg/executor}/window.go (97%) rename {executor => pkg/executor}/window_test.go (99%) create mode 100644 pkg/executor/write.go rename {executor => pkg/executor}/write_concurrent_test.go (98%) create mode 100644 pkg/expression/BUILD.bazel rename {expression => pkg/expression}/OWNERS (100%) create mode 100644 pkg/expression/aggregation/BUILD.bazel rename {expression => pkg/expression}/aggregation/agg_to_pb.go (96%) rename {expression => pkg/expression}/aggregation/agg_to_pb_test.go (94%) rename {expression => pkg/expression}/aggregation/aggregation.go (95%) rename {expression => pkg/expression}/aggregation/aggregation_test.go (98%) rename {expression => pkg/expression}/aggregation/avg.go (90%) rename {expression => pkg/expression}/aggregation/base_func.go (97%) rename {expression => pkg/expression}/aggregation/base_func_test.go (90%) create mode 100644 pkg/expression/aggregation/bench_test.go rename {expression => pkg/expression}/aggregation/bit_and.go (93%) rename {expression => pkg/expression}/aggregation/bit_or.go (93%) rename {expression => pkg/expression}/aggregation/bit_xor.go (93%) rename {expression => pkg/expression}/aggregation/concat.go (94%) rename {expression => pkg/expression}/aggregation/count.go (93%) rename {expression => pkg/expression}/aggregation/descriptor.go (97%) create mode 100644 pkg/expression/aggregation/explain.go rename {expression => pkg/expression}/aggregation/first_row.go (92%) create mode 100644 pkg/expression/aggregation/main_test.go rename {expression => pkg/expression}/aggregation/max_min.go (90%) rename {expression => pkg/expression}/aggregation/sum.go (90%) create mode 100644 pkg/expression/aggregation/util.go create mode 100644 pkg/expression/aggregation/util_test.go rename {expression => pkg/expression}/aggregation/window_func.go (96%) create mode 100644 pkg/expression/bench_test.go rename {expression => pkg/expression}/builtin.go (99%) rename {expression => pkg/expression}/builtin_arithmetic.go (99%) rename {expression => pkg/expression}/builtin_arithmetic_test.go (98%) rename {expression => pkg/expression}/builtin_arithmetic_vec.go (99%) rename {expression => pkg/expression}/builtin_arithmetic_vec_test.go (98%) rename {expression => pkg/expression}/builtin_cast.go (99%) rename {expression => pkg/expression}/builtin_cast_bench_test.go (91%) rename {expression => pkg/expression}/builtin_cast_test.go (99%) rename {expression => pkg/expression}/builtin_cast_vec.go (99%) rename {expression => pkg/expression}/builtin_cast_vec_test.go (98%) rename {expression => pkg/expression}/builtin_compare.go (99%) rename {expression => pkg/expression}/builtin_compare_test.go (98%) rename {expression => pkg/expression}/builtin_compare_vec.go (99%) rename {expression => pkg/expression}/builtin_compare_vec_generated.go (99%) rename {expression => pkg/expression}/builtin_compare_vec_generated_test.go (98%) rename {expression => pkg/expression}/builtin_compare_vec_test.go (98%) rename {expression => pkg/expression}/builtin_control.go (99%) rename {expression => pkg/expression}/builtin_control_test.go (96%) rename {expression => pkg/expression}/builtin_control_vec_generated.go (99%) rename {expression => pkg/expression}/builtin_control_vec_generated_test.go (99%) rename {expression => pkg/expression}/builtin_convert_charset.go (96%) rename {expression => pkg/expression}/builtin_encryption.go (98%) rename {expression => pkg/expression}/builtin_encryption_test.go (98%) rename {expression => pkg/expression}/builtin_encryption_vec.go (98%) rename {expression => pkg/expression}/builtin_encryption_vec_test.go (98%) rename {expression => pkg/expression}/builtin_func_param.go (98%) rename {expression => pkg/expression}/builtin_grouping.go (97%) rename {expression => pkg/expression}/builtin_grouping_test.go (95%) rename {expression => pkg/expression}/builtin_ilike.go (94%) rename {expression => pkg/expression}/builtin_ilike_test.go (95%) rename {expression => pkg/expression}/builtin_ilike_vec.go (97%) rename {expression => pkg/expression}/builtin_info.go (98%) rename {expression => pkg/expression}/builtin_info_test.go (96%) rename {expression => pkg/expression}/builtin_info_vec.go (97%) rename {expression => pkg/expression}/builtin_info_vec_test.go (95%) rename {expression => pkg/expression}/builtin_json.go (99%) rename {expression => pkg/expression}/builtin_json_test.go (99%) rename {expression => pkg/expression}/builtin_json_vec.go (99%) rename {expression => pkg/expression}/builtin_json_vec_test.go (99%) rename {expression => pkg/expression}/builtin_like.go (94%) rename {expression => pkg/expression}/builtin_like_test.go (95%) rename {expression => pkg/expression}/builtin_like_vec.go (97%) rename {expression => pkg/expression}/builtin_like_vec_test.go (94%) rename {expression => pkg/expression}/builtin_math.go (99%) rename {expression => pkg/expression}/builtin_math_test.go (98%) rename {expression => pkg/expression}/builtin_math_vec.go (99%) rename {expression => pkg/expression}/builtin_math_vec_test.go (98%) rename {expression => pkg/expression}/builtin_miscellaneous.go (99%) rename {expression => pkg/expression}/builtin_miscellaneous_test.go (98%) rename {expression => pkg/expression}/builtin_miscellaneous_vec.go (99%) rename {expression => pkg/expression}/builtin_miscellaneous_vec_test.go (98%) rename {expression => pkg/expression}/builtin_op.go (99%) rename {expression => pkg/expression}/builtin_op_test.go (98%) rename {expression => pkg/expression}/builtin_op_vec.go (99%) rename {expression => pkg/expression}/builtin_op_vec_test.go (97%) rename {expression => pkg/expression}/builtin_other.go (99%) rename {expression => pkg/expression}/builtin_other_test.go (97%) rename {expression => pkg/expression}/builtin_other_vec.go (98%) rename {expression => pkg/expression}/builtin_other_vec_generated.go (98%) rename {expression => pkg/expression}/builtin_other_vec_generated_test.go (98%) rename {expression => pkg/expression}/builtin_other_vec_test.go (94%) rename {expression => pkg/expression}/builtin_regexp.go (99%) rename {expression => pkg/expression}/builtin_regexp_test.go (99%) rename {expression => pkg/expression}/builtin_regexp_util.go (98%) rename {expression => pkg/expression}/builtin_regexp_vec_const_test.go (94%) rename {expression => pkg/expression}/builtin_string.go (99%) rename {expression => pkg/expression}/builtin_string_test.go (99%) rename {expression => pkg/expression}/builtin_string_vec.go (99%) rename {expression => pkg/expression}/builtin_string_vec_generated.go (98%) rename {expression => pkg/expression}/builtin_string_vec_generated_test.go (95%) rename {expression => pkg/expression}/builtin_string_vec_test.go (99%) rename {expression => pkg/expression}/builtin_test.go (94%) rename {expression => pkg/expression}/builtin_time.go (99%) rename {expression => pkg/expression}/builtin_time_test.go (99%) rename {expression => pkg/expression}/builtin_time_vec.go (99%) rename {expression => pkg/expression}/builtin_time_vec_generated.go (99%) rename {expression => pkg/expression}/builtin_time_vec_generated_test.go (99%) rename {expression => pkg/expression}/builtin_time_vec_test.go (99%) rename {expression => pkg/expression}/builtin_vectorized.go (96%) rename {expression => pkg/expression}/builtin_vectorized_test.go (99%) rename {expression => pkg/expression}/chunk_executor.go (98%) rename {expression => pkg/expression}/collation.go (98%) create mode 100644 pkg/expression/collation_test.go create mode 100644 pkg/expression/column.go create mode 100644 pkg/expression/column_test.go create mode 100644 pkg/expression/constant.go rename {expression => pkg/expression}/constant_fold.go (97%) rename {expression => pkg/expression}/constant_propagation.go (98%) rename {expression => pkg/expression}/constant_test.go (98%) rename {expression => pkg/expression}/distsql_builtin.go (99%) rename {expression => pkg/expression}/distsql_builtin_test.go (99%) create mode 100644 pkg/expression/errors.go rename {expression => pkg/expression}/evaluator.go (98%) rename {expression => pkg/expression}/evaluator_test.go (98%) create mode 100644 pkg/expression/explain.go rename {expression => pkg/expression}/expr_to_pb.go (95%) rename {expression => pkg/expression}/expr_to_pb_test.go (98%) rename {expression => pkg/expression}/expression.go (98%) create mode 100644 pkg/expression/expression_test.go create mode 100644 pkg/expression/extension.go rename {expression => pkg/expression}/function_traits.go (99%) rename {expression => pkg/expression}/function_traits_test.go (94%) rename {expression => pkg/expression}/generator/compare_vec.go (98%) rename {expression => pkg/expression}/generator/control_vec.go (96%) create mode 100644 pkg/expression/generator/helper/BUILD.bazel create mode 100644 pkg/expression/generator/helper/helper.go rename {expression => pkg/expression}/generator/other_vec.go (97%) rename {expression => pkg/expression}/generator/string_vec.go (96%) rename {expression => pkg/expression}/generator/time_vec.go (99%) rename {expression => pkg/expression}/grouping_sets.go (98%) rename {expression => pkg/expression}/grouping_sets_test.go (98%) create mode 100644 pkg/expression/helper.go create mode 100644 pkg/expression/helper_test.go create mode 100644 pkg/expression/integration_test/BUILD.bazel rename {expression => pkg/expression}/integration_test/README.md (100%) create mode 100644 pkg/expression/integration_test/integration_test.go create mode 100644 pkg/expression/integration_test/main_test.go create mode 100644 pkg/expression/main_test.go rename {expression => pkg/expression}/scalar_function.go (98%) rename {expression => pkg/expression}/scalar_function_test.go (94%) create mode 100644 pkg/expression/schema.go rename {expression => pkg/expression}/schema_test.go (100%) rename {expression => pkg/expression}/simple_rewriter.go (95%) create mode 100644 pkg/expression/test/multivaluedindex/BUILD.bazel create mode 100644 pkg/expression/test/multivaluedindex/main_test.go create mode 100644 pkg/expression/test/multivaluedindex/multi_valued_index_test.go rename {expression => pkg/expression}/typeinfer_test.go (99%) create mode 100644 pkg/expression/util.go create mode 100644 pkg/expression/util_test.go rename {expression => pkg/expression}/vectorized.go (96%) rename {extension => pkg/extension}/.gitignore (100%) create mode 100644 pkg/extension/BUILD.bazel create mode 100644 pkg/extension/_import/BUILD.bazel rename {extension => pkg/extension}/_import/import.go (100%) create mode 100644 pkg/extension/bootstrap_test.go create mode 160000 pkg/extension/enterprise rename {extension => pkg/extension}/event_listener_test.go (96%) create mode 100644 pkg/extension/extensionimpl/BUILD.bazel create mode 100644 pkg/extension/extensionimpl/bootstrap.go rename {extension => pkg/extension}/extensions.go (100%) rename {extension => pkg/extension}/function.go (92%) rename {extension => pkg/extension}/function_test.go (98%) create mode 100644 pkg/extension/main_test.go rename {extension => pkg/extension}/manifest.go (97%) rename {extension => pkg/extension}/registry.go (100%) create mode 100644 pkg/extension/registry_test.go create mode 100644 pkg/extension/session.go rename {extension => pkg/extension}/util.go (100%) create mode 100644 pkg/infoschema/BUILD.bazel create mode 100644 pkg/infoschema/builder.go create mode 100644 pkg/infoschema/cache.go create mode 100644 pkg/infoschema/cluster.go create mode 100644 pkg/infoschema/error.go create mode 100644 pkg/infoschema/infoschema.go create mode 100644 pkg/infoschema/infoschema_test.go create mode 100644 pkg/infoschema/internal/BUILD.bazel rename {infoschema => pkg/infoschema}/internal/testkit.go (100%) create mode 100644 pkg/infoschema/main_test.go rename {infoschema => pkg/infoschema}/metric_table_def.go (100%) create mode 100644 pkg/infoschema/metrics/BUILD.bazel create mode 100644 pkg/infoschema/metrics/metrics.go rename {infoschema => pkg/infoschema}/metrics_schema.go (94%) rename {infoschema => pkg/infoschema}/metrics_schema_test.go (97%) create mode 100644 pkg/infoschema/perfschema/BUILD.bazel rename {infoschema => pkg/infoschema}/perfschema/const.go (100%) rename {infoschema => pkg/infoschema}/perfschema/init.go (84%) create mode 100644 pkg/infoschema/perfschema/main_test.go create mode 100644 pkg/infoschema/perfschema/tables.go create mode 100644 pkg/infoschema/perfschema/tables_test.go rename {infoschema => pkg/infoschema}/perfschema/testdata/test.pprof (100%) rename {infoschema => pkg/infoschema}/perfschema/testdata/tikv.cpu.profile (100%) create mode 100644 pkg/infoschema/tables.go create mode 100644 pkg/infoschema/test/cachetest/BUILD.bazel create mode 100644 pkg/infoschema/test/cachetest/cache_test.go create mode 100644 pkg/infoschema/test/cachetest/main_test.go create mode 100644 pkg/infoschema/test/clustertablestest/BUILD.bazel rename {infoschema => pkg/infoschema}/test/clustertablestest/cluster_tables_test.go (98%) create mode 100644 pkg/infoschema/test/clustertablestest/main_test.go create mode 100644 pkg/infoschema/test/clustertablestest/tables_test.go create mode 100644 pkg/keyspace/BUILD.bazel rename {keyspace => pkg/keyspace}/keyspace.go (98%) rename {keyspace => pkg/keyspace}/keyspace_test.go (97%) create mode 100644 pkg/kv/BUILD.bazel rename {kv => pkg/kv}/cachedb.go (100%) rename {kv => pkg/kv}/checker.go (100%) rename {kv => pkg/kv}/checker_test.go (97%) create mode 100644 pkg/kv/error.go create mode 100644 pkg/kv/error_test.go rename {kv => pkg/kv}/fault_injection.go (100%) rename {kv => pkg/kv}/fault_injection_test.go (98%) rename {kv => pkg/kv}/interface_mock_test.go (99%) rename {kv => pkg/kv}/iter.go (100%) rename {kv => pkg/kv}/key.go (99%) rename {kv => pkg/kv}/key_test.go (97%) rename {kv => pkg/kv}/keyflags.go (100%) rename {kv => pkg/kv}/kv.go (99%) create mode 100644 pkg/kv/main_test.go rename {kv => pkg/kv}/mock_test.go (100%) create mode 100644 pkg/kv/mpp.go rename {kv => pkg/kv}/option.go (100%) rename {kv => pkg/kv}/option_test.go (100%) create mode 100644 pkg/kv/txn.go rename {kv => pkg/kv}/txn_scope_var.go (98%) rename {kv => pkg/kv}/txn_test.go (100%) rename {kv => pkg/kv}/utils.go (100%) rename {kv => pkg/kv}/utils_test.go (100%) rename {kv => pkg/kv}/variables.go (100%) rename {kv => pkg/kv}/version.go (100%) rename {kv => pkg/kv}/version_test.go (100%) create mode 100644 pkg/lock/BUILD.bazel rename {lock => pkg/lock}/lock.go (94%) create mode 100644 pkg/meta/BUILD.bazel create mode 100644 pkg/meta/autoid/BUILD.bazel create mode 100644 pkg/meta/autoid/autoid.go rename {meta => pkg/meta}/autoid/autoid_service.go (97%) create mode 100644 pkg/meta/autoid/autoid_test.go create mode 100644 pkg/meta/autoid/bench_test.go create mode 100644 pkg/meta/autoid/errors.go create mode 100644 pkg/meta/autoid/main_test.go rename {meta => pkg/meta}/autoid/memid.go (99%) rename {meta => pkg/meta}/autoid/memid_test.go (93%) rename {meta => pkg/meta}/autoid/seq_autoid_test.go (96%) create mode 100644 pkg/meta/main_test.go rename {meta => pkg/meta}/meta.go (99%) rename {meta => pkg/meta}/meta_autoid.go (99%) rename {meta => pkg/meta}/meta_test.go (98%) create mode 100644 pkg/metrics/BUILD.bazel rename {metrics => pkg/metrics}/alertmanager/tidb.rules.yml (100%) rename {metrics => pkg/metrics}/bindinfo.go (100%) rename {metrics => pkg/metrics}/ddl.go (100%) rename {metrics => pkg/metrics}/distsql.go (100%) rename {metrics => pkg/metrics}/disttask.go (98%) rename {metrics => pkg/metrics}/domain.go (100%) rename {metrics => pkg/metrics}/executor.go (100%) rename {metrics => pkg/metrics}/gc_worker.go (100%) rename {metrics => pkg/metrics}/grafana/README.md (100%) rename {metrics => pkg/metrics}/grafana/generate_json.sh (100%) rename {metrics => pkg/metrics}/grafana/overview.json (100%) rename {metrics => pkg/metrics}/grafana/performance_overview.json (100%) rename {metrics => pkg/metrics}/grafana/tidb.json (100%) rename {metrics => pkg/metrics}/grafana/tidb_resource_control.json (100%) rename {metrics => pkg/metrics}/grafana/tidb_runtime.json (100%) rename {metrics => pkg/metrics}/grafana/tidb_summary.json (100%) rename {metrics => pkg/metrics}/grafana/tidb_summary.jsonnet (100%) create mode 100644 pkg/metrics/import.go rename {metrics => pkg/metrics}/log_backup.go (100%) create mode 100644 pkg/metrics/main_test.go rename {metrics => pkg/metrics}/meta.go (100%) create mode 100644 pkg/metrics/metrics.go rename {metrics => pkg/metrics}/metrics_internal_test.go (100%) rename {metrics => pkg/metrics}/metrics_test.go (89%) rename {metrics => pkg/metrics}/owner.go (100%) rename {metrics => pkg/metrics}/resourcemanager.go (100%) create mode 100644 pkg/metrics/server.go rename {metrics => pkg/metrics}/session.go (100%) rename {metrics => pkg/metrics}/sli.go (100%) rename {metrics => pkg/metrics}/stats.go (100%) create mode 100644 pkg/metrics/telemetry.go rename {metrics => pkg/metrics}/topsql.go (100%) rename {metrics => pkg/metrics}/ttl.go (100%) rename {metrics => pkg/metrics}/wrapper.go (100%) create mode 100644 pkg/owner/BUILD.bazel create mode 100644 pkg/owner/fail_test.go create mode 100644 pkg/owner/main_test.go create mode 100644 pkg/owner/manager.go create mode 100644 pkg/owner/manager_test.go create mode 100644 pkg/owner/mock.go rename {parser => pkg/parser}/.editorconfig (100%) rename {parser => pkg/parser}/.gitignore (100%) create mode 100644 pkg/parser/BUILD.bazel rename {parser => pkg/parser}/LICENSE (100%) rename {parser => pkg/parser}/Makefile (100%) rename {parser => pkg/parser}/OWNERS (100%) rename {parser => pkg/parser}/README.md (100%) rename {parser => pkg/parser}/SECURITY.md (100%) create mode 100644 pkg/parser/ast/BUILD.bazel rename {parser => pkg/parser}/ast/advisor.go (97%) create mode 100644 pkg/parser/ast/ast.go create mode 100644 pkg/parser/ast/base.go create mode 100644 pkg/parser/ast/base_test.go create mode 100644 pkg/parser/ast/ddl.go create mode 100644 pkg/parser/ast/ddl_test.go rename {parser => pkg/parser}/ast/dml.go (99%) rename {parser => pkg/parser}/ast/dml_test.go (99%) rename {parser => pkg/parser}/ast/expressions.go (99%) rename {parser => pkg/parser}/ast/expressions_test.go (99%) rename {parser => pkg/parser}/ast/flag.go (100%) rename {parser => pkg/parser}/ast/flag_test.go (97%) create mode 100644 pkg/parser/ast/format_test.go rename {parser => pkg/parser}/ast/functions.go (99%) rename {parser => pkg/parser}/ast/functions_test.go (98%) create mode 100644 pkg/parser/ast/misc.go create mode 100644 pkg/parser/ast/misc_test.go rename {parser => pkg/parser}/ast/procedure.go (99%) rename {parser => pkg/parser}/ast/procedure_test.go (99%) create mode 100644 pkg/parser/ast/stats.go rename {parser => pkg/parser}/ast/util.go (100%) create mode 100644 pkg/parser/ast/util_test.go create mode 100644 pkg/parser/auth/BUILD.bazel rename {parser => pkg/parser}/auth/auth.go (98%) rename {parser => pkg/parser}/auth/caching_sha2.go (99%) rename {parser => pkg/parser}/auth/caching_sha2_test.go (98%) rename {parser => pkg/parser}/auth/mysql_native_password.go (98%) rename {parser => pkg/parser}/auth/mysql_native_password_test.go (100%) rename {parser => pkg/parser}/auth/tidb_sm3.go (100%) rename {parser => pkg/parser}/auth/tidb_sm3_test.go (98%) rename {parser => pkg/parser}/bench_test.go (100%) create mode 100644 pkg/parser/charset/BUILD.bazel create mode 100644 pkg/parser/charset/charset.go rename {parser => pkg/parser}/charset/charset_test.go (100%) rename {parser => pkg/parser}/charset/encoding.go (100%) rename {parser => pkg/parser}/charset/encoding_ascii.go (100%) rename {parser => pkg/parser}/charset/encoding_base.go (97%) rename {parser => pkg/parser}/charset/encoding_bin.go (100%) rename {parser => pkg/parser}/charset/encoding_gbk.go (100%) rename {parser => pkg/parser}/charset/encoding_latin1.go (100%) rename {parser => pkg/parser}/charset/encoding_table.go (100%) rename {parser => pkg/parser}/charset/encoding_test.go (99%) rename {parser => pkg/parser}/charset/encoding_utf8.go (100%) rename {parser => pkg/parser}/consistent_test.go (100%) rename {parser => pkg/parser}/digester.go (99%) rename {parser => pkg/parser}/digester_test.go (99%) rename {parser => pkg/parser}/docs/quickstart.md (100%) create mode 100644 pkg/parser/duration/BUILD.bazel rename {parser => pkg/parser}/duration/duration.go (100%) rename {parser => pkg/parser}/duration/duration_test.go (100%) create mode 100644 pkg/parser/format/BUILD.bazel rename {parser => pkg/parser}/format/format.go (100%) rename {parser => pkg/parser}/format/format_test.go (100%) rename {parser => pkg/parser}/go.mod (96%) rename {parser => pkg/parser}/go.sum (100%) create mode 100644 pkg/parser/goyacc/BUILD.bazel rename {parser => pkg/parser}/goyacc/format_yacc.go (99%) rename {parser => pkg/parser}/goyacc/main.go (100%) rename {parser => pkg/parser}/hintparser.go (99%) rename {parser => pkg/parser}/hintparser.y (99%) rename {parser => pkg/parser}/hintparser_test.go (98%) rename {parser => pkg/parser}/hintparserimpl.go (97%) rename {parser => pkg/parser}/lexer.go (99%) rename {parser => pkg/parser}/lexer_test.go (99%) rename {parser => pkg/parser}/main_test.go (100%) rename {parser => pkg/parser}/misc.go (100%) create mode 100644 pkg/parser/model/BUILD.bazel create mode 100644 pkg/parser/model/ddl.go create mode 100644 pkg/parser/model/ddl_test.go rename {parser => pkg/parser}/model/flags.go (100%) rename {parser => pkg/parser}/model/model.go (99%) rename {parser => pkg/parser}/model/model_test.go (99%) create mode 100644 pkg/parser/model/reorg.go create mode 100644 pkg/parser/mysql/BUILD.bazel rename {parser => pkg/parser}/mysql/charset.go (100%) rename {parser => pkg/parser}/mysql/const.go (99%) rename {parser => pkg/parser}/mysql/const_test.go (100%) rename {parser => pkg/parser}/mysql/errcode.go (99%) rename {parser => pkg/parser}/mysql/errname.go (100%) rename {parser => pkg/parser}/mysql/error.go (100%) rename {parser => pkg/parser}/mysql/error_test.go (100%) rename {parser => pkg/parser}/mysql/locale_format.go (100%) rename {parser => pkg/parser}/mysql/privs.go (100%) rename {parser => pkg/parser}/mysql/privs_test.go (100%) rename {parser => pkg/parser}/mysql/state.go (100%) rename {parser => pkg/parser}/mysql/type.go (100%) rename {parser => pkg/parser}/mysql/type_test.go (100%) rename {parser => pkg/parser}/mysql/util.go (100%) create mode 100644 pkg/parser/opcode/BUILD.bazel rename {parser => pkg/parser}/opcode/opcode.go (98%) rename {parser => pkg/parser}/opcode/opcode_test.go (100%) create mode 100644 pkg/parser/parser.go rename {parser => pkg/parser}/parser.y (99%) create mode 100644 pkg/parser/parser_test.go rename {parser => pkg/parser}/reserved_words_test.go (98%) create mode 100644 pkg/parser/terror/BUILD.bazel create mode 100644 pkg/parser/terror/terror.go rename {parser => pkg/parser}/terror/terror_test.go (100%) rename {parser => pkg/parser}/test.sh (100%) create mode 100644 pkg/parser/test_driver/BUILD.bazel rename {parser => pkg/parser}/test_driver/test_driver.go (97%) rename {parser => pkg/parser}/test_driver/test_driver_datum.go (98%) rename {parser => pkg/parser}/test_driver/test_driver_helper.go (100%) rename {parser => pkg/parser}/test_driver/test_driver_mydecimal.go (100%) create mode 100644 pkg/parser/tidb/BUILD.bazel rename {parser => pkg/parser}/tidb/features.go (100%) create mode 100644 pkg/parser/types/BUILD.bazel create mode 100644 pkg/parser/types/etc.go create mode 100644 pkg/parser/types/etc_test.go rename {parser => pkg/parser}/types/eval_type.go (100%) create mode 100644 pkg/parser/types/field_type.go create mode 100644 pkg/parser/types/field_type_test.go rename {parser => pkg/parser}/yy_parser.go (98%) create mode 100644 pkg/planner/BUILD.bazel create mode 100644 pkg/planner/cardinality/BUILD.bazel rename {planner => pkg/planner}/cardinality/cross_estimation.go (97%) create mode 100644 pkg/planner/cardinality/join.go create mode 100644 pkg/planner/cardinality/main_test.go rename {planner => pkg/planner}/cardinality/ndv.go (95%) rename {planner => pkg/planner}/cardinality/pseudo.go (95%) rename {planner => pkg/planner}/cardinality/row_count_column.go (97%) rename {planner => pkg/planner}/cardinality/row_count_index.go (97%) rename {planner => pkg/planner}/cardinality/row_count_test.go (89%) rename {planner => pkg/planner}/cardinality/row_size.go (95%) rename {planner => pkg/planner}/cardinality/row_size_test.go (96%) rename {planner => pkg/planner}/cardinality/selectivity.go (98%) rename {planner => pkg/planner}/cardinality/selectivity_test.go (97%) rename {planner => pkg/planner}/cardinality/testdata/cardinality_suite_in.json (100%) rename {planner => pkg/planner}/cardinality/testdata/cardinality_suite_out.json (91%) create mode 100644 pkg/planner/cardinality/trace.go create mode 100644 pkg/planner/cardinality/trace_test.go create mode 100644 pkg/planner/cascades/BUILD.bazel rename {planner => pkg/planner}/cascades/enforcer_rules.go (92%) rename {planner => pkg/planner}/cascades/enforcer_rules_test.go (92%) rename {planner => pkg/planner}/cascades/implementation_rules.go (99%) create mode 100644 pkg/planner/cascades/integration_test.go create mode 100644 pkg/planner/cascades/main_test.go create mode 100644 pkg/planner/cascades/optimize.go create mode 100644 pkg/planner/cascades/optimize_test.go create mode 100644 pkg/planner/cascades/stringer.go create mode 100644 pkg/planner/cascades/stringer_test.go rename {planner => pkg/planner}/cascades/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/cascades/testdata/integration_suite_out.json (100%) rename {planner => pkg/planner}/cascades/testdata/stringer_suite_in.json (100%) rename {planner => pkg/planner}/cascades/testdata/stringer_suite_out.json (100%) rename {planner => pkg/planner}/cascades/testdata/transformation_rules_suite_in.json (100%) rename {planner => pkg/planner}/cascades/testdata/transformation_rules_suite_out.json (100%) rename {planner => pkg/planner}/cascades/transformation_rules.go (99%) rename {planner => pkg/planner}/cascades/transformation_rules_test.go (97%) create mode 100644 pkg/planner/core/BUILD.bazel rename {planner => pkg/planner}/core/access_object.go (98%) create mode 100644 pkg/planner/core/binary_plan_test.go create mode 100644 pkg/planner/core/casetest/BUILD.bazel create mode 100644 pkg/planner/core/casetest/binaryplan/BUILD.bazel create mode 100644 pkg/planner/core/casetest/binaryplan/binary_plan_test.go create mode 100644 pkg/planner/core/casetest/binaryplan/main_test.go rename {planner => pkg/planner}/core/casetest/binaryplan/testdata/binary_plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/binaryplan/testdata/binary_plan_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/cbotest/BUILD.bazel create mode 100644 pkg/planner/core/casetest/cbotest/cbo_test.go create mode 100644 pkg/planner/core/casetest/cbotest/main_test.go rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyzeSuiteTestIndexEqualUnknownT.json (100%) rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyzeSuiteTestLimitIndexEstimationT.json (100%) rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyzeSuiteTestLowSelIndexGreedySearchT.json (100%) rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyze_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyze_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/cbotest/testdata/analyzesSuiteTestIndexReadT.json (100%) create mode 100644 pkg/planner/core/casetest/dag/BUILD.bazel rename {planner => pkg/planner}/core/casetest/dag/dag_test.go (95%) create mode 100644 pkg/planner/core/casetest/dag/main_test.go rename {planner => pkg/planner}/core/casetest/dag/testdata/plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/dag/testdata/plan_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/enforcempp/BUILD.bazel create mode 100644 pkg/planner/core/casetest/enforcempp/enforce_mpp_test.go create mode 100644 pkg/planner/core/casetest/enforcempp/main_test.go rename {planner => pkg/planner}/core/casetest/enforcempp/testdata/enforce_mpp_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/enforcempp/testdata/enforce_mpp_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/flatplan/BUILD.bazel rename {planner => pkg/planner}/core/casetest/flatplan/flat_plan_test.go (89%) create mode 100644 pkg/planner/core/casetest/flatplan/main_test.go rename {planner => pkg/planner}/core/casetest/flatplan/testdata/flat_plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/flatplan/testdata/flat_plan_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/hint/BUILD.bazel rename {planner => pkg/planner}/core/casetest/hint/hint_test.go (97%) create mode 100644 pkg/planner/core/casetest/hint/main_test.go rename {planner => pkg/planner}/core/casetest/hint/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/hint/testdata/integration_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/index/BUILD.bazel create mode 100644 pkg/planner/core/casetest/index/index_test.go create mode 100644 pkg/planner/core/casetest/index/main_test.go rename {planner => pkg/planner}/core/casetest/index/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/index/testdata/integration_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/integration_test.go create mode 100644 pkg/planner/core/casetest/main_test.go create mode 100644 pkg/planner/core/casetest/mpp/BUILD.bazel create mode 100644 pkg/planner/core/casetest/mpp/main_test.go rename {planner => pkg/planner}/core/casetest/mpp/mpp_test.go (98%) rename {planner => pkg/planner}/core/casetest/mpp/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/mpp/testdata/integration_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/partition/BUILD.bazel create mode 100644 pkg/planner/core/casetest/partition/integration_partition_test.go create mode 100644 pkg/planner/core/casetest/partition/main_test.go rename {planner => pkg/planner}/core/casetest/partition/partition_pruner_test.go (94%) rename {planner => pkg/planner}/core/casetest/partition/testdata/integration_partition_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/partition/testdata/integration_partition_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/partition/testdata/partition_pruner_in.json (100%) rename {planner => pkg/planner}/core/casetest/partition/testdata/partition_pruner_out.json (100%) create mode 100644 pkg/planner/core/casetest/physicalplantest/BUILD.bazel create mode 100644 pkg/planner/core/casetest/physicalplantest/main_test.go create mode 100644 pkg/planner/core/casetest/physicalplantest/physical_plan_test.go rename {planner => pkg/planner}/core/casetest/physicalplantest/testdata/plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/physicalplantest/testdata/plan_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/plan_test.go create mode 100644 pkg/planner/core/casetest/planstats/BUILD.bazel create mode 100644 pkg/planner/core/casetest/planstats/main_test.go rename {planner => pkg/planner}/core/casetest/planstats/plan_stats_test.go (96%) rename {planner => pkg/planner}/core/casetest/planstats/testdata/plan_stats_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/planstats/testdata/plan_stats_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/pushdown/BUILD.bazel create mode 100644 pkg/planner/core/casetest/pushdown/main_test.go rename {planner => pkg/planner}/core/casetest/pushdown/push_down_test.go (98%) rename {planner => pkg/planner}/core/casetest/pushdown/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/pushdown/testdata/integration_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/rule/BUILD.bazel create mode 100644 pkg/planner/core/casetest/rule/main_test.go rename {planner => pkg/planner}/core/casetest/rule/rule_derive_topn_from_window_test.go (90%) rename {planner => pkg/planner}/core/casetest/rule/rule_inject_extra_projection_test.go (87%) create mode 100644 pkg/planner/core/casetest/rule/rule_join_reorder_test.go rename {planner => pkg/planner}/core/casetest/rule/testdata/derive_topn_from_window_in.json (100%) rename {planner => pkg/planner}/core/casetest/rule/testdata/derive_topn_from_window_out.json (100%) rename {planner => pkg/planner}/core/casetest/rule/testdata/join_reorder_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/rule/testdata/join_reorder_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/scalarsubquery/BUILD.bazel rename {planner => pkg/planner}/core/casetest/scalarsubquery/cases_test.go (96%) create mode 100644 pkg/planner/core/casetest/scalarsubquery/main_test.go rename {planner => pkg/planner}/core/casetest/scalarsubquery/testdata/plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/scalarsubquery/testdata/plan_suite_out.json (100%) create mode 100644 pkg/planner/core/casetest/stats_test.go rename {planner => pkg/planner}/core/casetest/testdata/integration_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/integration_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/json_plan_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/json_plan_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/plan_normalized_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/plan_normalized_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/stats_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/testdata/stats_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/tiflash_selection_late_materialization_test.go (92%) create mode 100644 pkg/planner/core/casetest/windows/BUILD.bazel create mode 100644 pkg/planner/core/casetest/windows/main_test.go rename {planner => pkg/planner}/core/casetest/windows/testdata/window_push_down_suite_in.json (100%) rename {planner => pkg/planner}/core/casetest/windows/testdata/window_push_down_suite_out.json (100%) rename {planner => pkg/planner}/core/casetest/windows/window_push_down_test.go (92%) create mode 100644 pkg/planner/core/cbo_test.go rename {planner => pkg/planner}/core/collect_column_stats_usage.go (99%) rename {planner => pkg/planner}/core/collect_column_stats_usage_test.go (97%) rename {planner => pkg/planner}/core/common_plans.go (98%) rename {planner => pkg/planner}/core/common_plans_test.go (97%) create mode 100644 pkg/planner/core/debugtrace.go rename {planner => pkg/planner}/core/encode.go (98%) create mode 100644 pkg/planner/core/enforce_mpp_test.go create mode 100644 pkg/planner/core/errors.go create mode 100644 pkg/planner/core/errors_test.go rename {planner => pkg/planner}/core/exhaust_physical_plans.go (99%) rename {planner => pkg/planner}/core/exhaust_physical_plans_test.go (97%) create mode 100644 pkg/planner/core/explain.go rename {planner => pkg/planner}/core/expression_rewriter.go (98%) rename {planner => pkg/planner}/core/expression_rewriter_test.go (99%) create mode 100644 pkg/planner/core/expression_test.go rename {planner => pkg/planner}/core/find_best_task.go (99%) rename {planner => pkg/planner}/core/find_best_task_test.go (98%) rename {planner => pkg/planner}/core/flat_plan.go (99%) create mode 100644 pkg/planner/core/foreign_key.go rename {planner => pkg/planner}/core/fragment.go (97%) rename {planner => pkg/planner}/core/fragment_test.go (100%) rename {planner => pkg/planner}/core/handle_cols.go (95%) rename {planner => pkg/planner}/core/hashcode.go (98%) rename {planner => pkg/planner}/core/hints.go (97%) rename {planner => pkg/planner}/core/indexmerge_intersection_test.go (98%) rename {planner => pkg/planner}/core/indexmerge_path.go (98%) rename {planner => pkg/planner}/core/indexmerge_path_test.go (99%) rename {planner => pkg/planner}/core/indexmerge_test.go (91%) rename {planner => pkg/planner}/core/initialize.go (98%) create mode 100644 pkg/planner/core/integration_partition_test.go create mode 100644 pkg/planner/core/integration_test.go create mode 100644 pkg/planner/core/internal/BUILD.bazel create mode 100644 pkg/planner/core/internal/base/BUILD.bazel create mode 100644 pkg/planner/core/internal/base/plan.go create mode 100644 pkg/planner/core/internal/testkit.go create mode 100644 pkg/planner/core/internal/util.go create mode 100644 pkg/planner/core/issuetest/BUILD.bazel create mode 100644 pkg/planner/core/issuetest/main_test.go rename {planner => pkg/planner}/core/issuetest/planner_issue_test.go (91%) rename {planner => pkg/planner}/core/logical_plan_builder.go (99%) rename {planner => pkg/planner}/core/logical_plan_trace_test.go (99%) rename {planner => pkg/planner}/core/logical_plans.go (99%) rename {planner => pkg/planner}/core/logical_plans_test.go (99%) create mode 100644 pkg/planner/core/main_test.go rename {planner => pkg/planner}/core/memtable_predicate_extractor.go (98%) rename {planner => pkg/planner}/core/memtable_predicate_extractor_test.go (99%) create mode 100644 pkg/planner/core/metrics/BUILD.bazel create mode 100644 pkg/planner/core/metrics/metrics.go create mode 100644 pkg/planner/core/mock.go rename {planner => pkg/planner}/core/optimizer.go (98%) rename {planner => pkg/planner}/core/optimizer_test.go (97%) rename {planner => pkg/planner}/core/partition_prune.go (89%) rename {planner => pkg/planner}/core/partition_pruning_test.go (98%) rename {planner => pkg/planner}/core/pb_to_plan.go (96%) create mode 100644 pkg/planner/core/physical_plan_test.go rename {planner => pkg/planner}/core/physical_plan_trace_test.go (96%) rename {planner => pkg/planner}/core/physical_plans.go (98%) create mode 100644 pkg/planner/core/plan.go rename {planner => pkg/planner}/core/plan_cache.go (96%) rename {planner => pkg/planner}/core/plan_cache_lru.go (96%) rename {planner => pkg/planner}/core/plan_cache_lru_test.go (98%) rename {planner => pkg/planner}/core/plan_cache_param.go (95%) rename {planner => pkg/planner}/core/plan_cache_param_test.go (98%) rename {planner => pkg/planner}/core/plan_cache_test.go (99%) rename {planner => pkg/planner}/core/plan_cache_utils.go (96%) rename {planner => pkg/planner}/core/plan_cacheable_checker.go (97%) rename {planner => pkg/planner}/core/plan_cacheable_checker_test.go (97%) rename {planner => pkg/planner}/core/plan_cost_detail.go (99%) rename {planner => pkg/planner}/core/plan_cost_detail_test.go (92%) rename {planner => pkg/planner}/core/plan_cost_ver1.go (99%) rename {planner => pkg/planner}/core/plan_cost_ver1_test.go (97%) rename {planner => pkg/planner}/core/plan_cost_ver2.go (99%) rename {planner => pkg/planner}/core/plan_cost_ver2_test.go (96%) rename {planner => pkg/planner}/core/plan_replayer_capture_test.go (89%) rename {planner => pkg/planner}/core/plan_stats.go (96%) create mode 100644 pkg/planner/core/plan_test.go rename {planner => pkg/planner}/core/plan_to_pb.go (98%) rename {planner => pkg/planner}/core/plan_to_pb_test.go (93%) rename {planner => pkg/planner}/core/planbuilder.go (99%) rename {planner => pkg/planner}/core/planbuilder_test.go (97%) rename {planner => pkg/planner}/core/point_get_plan.go (97%) rename {planner => pkg/planner}/core/point_get_plan_test.go (97%) rename {planner => pkg/planner}/core/preprocess.go (98%) rename {planner => pkg/planner}/core/preprocess_test.go (98%) rename {planner => pkg/planner}/core/property_cols_prune.go (98%) rename {planner => pkg/planner}/core/resolve_indices.go (99%) rename {planner => pkg/planner}/core/rule_aggregation_elimination.go (97%) rename {planner => pkg/planner}/core/rule_aggregation_push_down.go (98%) rename {planner => pkg/planner}/core/rule_aggregation_skew_rewrite.go (98%) rename {planner => pkg/planner}/core/rule_build_key_info.go (98%) rename {planner => pkg/planner}/core/rule_column_pruning.go (98%) rename {planner => pkg/planner}/core/rule_constant_propagation.go (99%) rename {planner => pkg/planner}/core/rule_decorrelate.go (98%) rename {planner => pkg/planner}/core/rule_derive_topn_from_window.go (96%) rename {planner => pkg/planner}/core/rule_eliminate_projection.go (99%) rename {planner => pkg/planner}/core/rule_generate_column_substitute.go (98%) rename {planner => pkg/planner}/core/rule_generate_column_substitute_test.go (96%) rename {planner => pkg/planner}/core/rule_inject_extra_projection.go (97%) rename {planner => pkg/planner}/core/rule_join_elimination.go (98%) rename {planner => pkg/planner}/core/rule_join_reorder.go (99%) rename {planner => pkg/planner}/core/rule_join_reorder_dp.go (99%) rename {planner => pkg/planner}/core/rule_join_reorder_dp_test.go (96%) rename {planner => pkg/planner}/core/rule_join_reorder_greedy.go (99%) create mode 100644 pkg/planner/core/rule_join_reorder_test.go rename {planner => pkg/planner}/core/rule_max_min_eliminate.go (97%) rename {planner => pkg/planner}/core/rule_partition_processor.go (99%) rename {planner => pkg/planner}/core/rule_predicate_push_down.go (99%) rename {planner => pkg/planner}/core/rule_predicate_simplification.go (97%) rename {planner => pkg/planner}/core/rule_push_down_sequence.go (100%) rename {planner => pkg/planner}/core/rule_resolve_grouping_expand.go (100%) rename {planner => pkg/planner}/core/rule_result_reorder.go (97%) rename {planner => pkg/planner}/core/rule_semi_join_rewrite.go (97%) rename {planner => pkg/planner}/core/rule_topn_push_down.go (98%) rename {planner => pkg/planner}/core/runtime_filter.go (97%) rename {planner => pkg/planner}/core/runtime_filter_generator.go (96%) rename {planner => pkg/planner}/core/runtime_filter_generator_test.go (82%) rename {planner => pkg/planner}/core/scalar_subq_expression.go (96%) rename {planner => pkg/planner}/core/show_predicate_extractor.go (94%) create mode 100644 pkg/planner/core/stats.go create mode 100644 pkg/planner/core/stringer.go create mode 100644 pkg/planner/core/stringer_test.go create mode 100644 pkg/planner/core/task.go create mode 100644 pkg/planner/core/telemetry.go rename {planner => pkg/planner}/core/testdata/index_merge_suite_in.json (100%) rename {planner => pkg/planner}/core/testdata/index_merge_suite_out.json (100%) rename {planner => pkg/planner}/core/testdata/plan_suite_unexported_in.json (100%) rename {planner => pkg/planner}/core/testdata/plan_suite_unexported_out.json (100%) rename {planner => pkg/planner}/core/testdata/runtime_filter_generator_suite_in.json (100%) rename {planner => pkg/planner}/core/testdata/runtime_filter_generator_suite_out.json (100%) create mode 100644 pkg/planner/core/tests/prepare/BUILD.bazel create mode 100644 pkg/planner/core/tests/prepare/issue/BUILD.bazel rename {planner => pkg/planner}/core/tests/prepare/issue/issue_test.go (98%) create mode 100644 pkg/planner/core/tests/prepare/issue/main_test.go create mode 100644 pkg/planner/core/tests/prepare/main_test.go rename {planner => pkg/planner}/core/tests/prepare/prepare_test.go (99%) rename {planner => pkg/planner}/core/tiflash_selection_late_materialization.go (97%) create mode 100644 pkg/planner/core/trace.go create mode 100644 pkg/planner/core/util.go create mode 100644 pkg/planner/funcdep/BUILD.bazel rename {planner => pkg/planner}/funcdep/doc.go (100%) rename {planner => pkg/planner}/funcdep/extract_fd_test.go (98%) rename {planner => pkg/planner}/funcdep/fast_int_set.go (100%) rename {planner => pkg/planner}/funcdep/fast_int_set_bench_test.go (100%) rename {planner => pkg/planner}/funcdep/fast_int_set_test.go (100%) rename {planner => pkg/planner}/funcdep/fd_graph.go (99%) rename {planner => pkg/planner}/funcdep/fd_graph_test.go (100%) create mode 100644 pkg/planner/funcdep/main_test.go create mode 100644 pkg/planner/implementation/BUILD.bazel create mode 100644 pkg/planner/implementation/base.go create mode 100644 pkg/planner/implementation/base_test.go rename {planner => pkg/planner}/implementation/datasource.go (95%) create mode 100644 pkg/planner/implementation/join.go create mode 100644 pkg/planner/implementation/main_test.go rename {planner => pkg/planner}/implementation/simple_plans.go (98%) create mode 100644 pkg/planner/implementation/sort.go create mode 100644 pkg/planner/memo/BUILD.bazel rename {planner => pkg/planner}/memo/expr_iterator.go (100%) rename {planner => pkg/planner}/memo/expr_iterator_test.go (98%) create mode 100644 pkg/planner/memo/group.go rename {planner => pkg/planner}/memo/group_expr.go (96%) rename {planner => pkg/planner}/memo/group_expr_test.go (93%) rename {planner => pkg/planner}/memo/group_test.go (96%) rename {planner => pkg/planner}/memo/implementation.go (95%) create mode 100644 pkg/planner/memo/main_test.go rename {planner => pkg/planner}/memo/pattern.go (99%) rename {planner => pkg/planner}/memo/pattern_test.go (98%) create mode 100644 pkg/planner/optimize.go create mode 100644 pkg/planner/property/BUILD.bazel rename {planner => pkg/planner}/property/logical_property.go (95%) rename {planner => pkg/planner}/property/physical_property.go (98%) rename {planner => pkg/planner}/property/stats_info.go (97%) rename {planner => pkg/planner}/property/task_type.go (100%) create mode 100644 pkg/planner/util/BUILD.bazel rename {planner => pkg/planner}/util/byitem.go (91%) create mode 100644 pkg/planner/util/debugtrace/BUILD.bazel create mode 100644 pkg/planner/util/debugtrace/base.go create mode 100644 pkg/planner/util/fixcontrol/BUILD.bazel rename {planner => pkg/planner}/util/fixcontrol/fixcontrol_test.go (95%) rename {planner => pkg/planner}/util/fixcontrol/get.go (100%) create mode 100644 pkg/planner/util/fixcontrol/main_test.go rename {planner => pkg/planner}/util/fixcontrol/set.go (100%) rename {planner => pkg/planner}/util/fixcontrol/testdata/fix_control_suite_in.json (100%) rename {planner => pkg/planner}/util/fixcontrol/testdata/fix_control_suite_out.json (100%) create mode 100644 pkg/planner/util/main_test.go create mode 100644 pkg/planner/util/misc.go rename {planner => pkg/planner}/util/path.go (97%) rename {planner => pkg/planner}/util/path_test.go (92%) create mode 100644 pkg/plugin/BUILD.bazel rename {plugin => pkg/plugin}/README.md (100%) rename {plugin => pkg/plugin}/audit.go (98%) create mode 100644 pkg/plugin/conn_ip_example/BUILD.bazel rename {plugin => pkg/plugin}/conn_ip_example/conn_ip_example.go (98%) rename {plugin => pkg/plugin}/conn_ip_example/conn_ip_example_test.go (97%) create mode 100644 pkg/plugin/conn_ip_example/main_test.go rename {plugin => pkg/plugin}/conn_ip_example/manifest.toml (100%) rename {plugin => pkg/plugin}/const.go (100%) rename {plugin => pkg/plugin}/const_test.go (100%) create mode 100644 pkg/plugin/errors.go rename {plugin => pkg/plugin}/helper.go (100%) rename {plugin => pkg/plugin}/helper_test.go (100%) create mode 100644 pkg/plugin/integration_test.go create mode 100644 pkg/plugin/main_test.go rename {plugin => pkg/plugin}/plugin.go (99%) rename {plugin => pkg/plugin}/plugin_test.go (99%) rename {plugin => pkg/plugin}/spi.go (100%) rename {plugin => pkg/plugin}/spi_test.go (94%) create mode 100644 pkg/privilege/BUILD.bazel create mode 100644 pkg/privilege/conn/BUILD.bazel rename {privilege => pkg/privilege}/conn/conn.go (100%) rename {privilege => pkg/privilege}/privilege.go (95%) create mode 100644 pkg/privilege/privileges/BUILD.bazel create mode 100644 pkg/privilege/privileges/cache.go create mode 100644 pkg/privilege/privileges/cache_test.go create mode 100644 pkg/privilege/privileges/errors.go create mode 100644 pkg/privilege/privileges/ldap/BUILD.bazel rename {privilege => pkg/privilege}/privileges/ldap/const.go (100%) rename {privilege => pkg/privilege}/privileges/ldap/ldap_common.go (100%) rename {privilege => pkg/privilege}/privileges/ldap/ldap_common_test.go (100%) rename {privilege => pkg/privilege}/privileges/ldap/sasl.go (98%) rename {privilege => pkg/privilege}/privileges/ldap/simple.go (100%) rename {privilege => pkg/privilege}/privileges/ldap/test/ca.crt (100%) rename {privilege => pkg/privilege}/privileges/ldap/test/ldap.crt (100%) rename {privilege => pkg/privilege}/privileges/ldap/test/ldap.key (100%) create mode 100644 pkg/privilege/privileges/main_test.go rename {privilege => pkg/privilege}/privileges/privileges.go (98%) create mode 100644 pkg/privilege/privileges/privileges_test.go rename {privilege => pkg/privilege}/privileges/tidb_auth_token.go (98%) rename {privilege => pkg/privilege}/privileges/tidb_auth_token_test.go (99%) create mode 100644 pkg/resourcemanager/BUILD.bazel create mode 100644 pkg/resourcemanager/pool/BUILD.bazel rename {resourcemanager => pkg/resourcemanager}/pool/basepool.go (100%) create mode 100644 pkg/resourcemanager/pool/spool/BUILD.bazel create mode 100644 pkg/resourcemanager/pool/spool/main_test.go rename {resourcemanager => pkg/resourcemanager}/pool/spool/option.go (100%) rename {resourcemanager => pkg/resourcemanager}/pool/spool/spool.go (94%) rename {resourcemanager => pkg/resourcemanager}/pool/spool/spool_test.go (98%) create mode 100644 pkg/resourcemanager/pool/workerpool/BUILD.bazel create mode 100644 pkg/resourcemanager/pool/workerpool/main_test.go rename {resourcemanager => pkg/resourcemanager}/pool/workerpool/workerpool.go (96%) rename {resourcemanager => pkg/resourcemanager}/pool/workerpool/workpool_test.go (97%) create mode 100644 pkg/resourcemanager/poolmanager/BUILD.bazel rename {resourcemanager => pkg/resourcemanager}/poolmanager/task_manager.go (100%) rename {resourcemanager => pkg/resourcemanager}/poolmanager/task_manager_iterator.go (100%) rename {resourcemanager => pkg/resourcemanager}/poolmanager/task_manager_scheduler.go (100%) rename {resourcemanager => pkg/resourcemanager}/rm.go (92%) rename {resourcemanager => pkg/resourcemanager}/schedule.go (95%) rename {resourcemanager => pkg/resourcemanager}/schedule_test.go (90%) create mode 100644 pkg/resourcemanager/scheduler/BUILD.bazel rename {resourcemanager => pkg/resourcemanager}/scheduler/cpu_scheduler.go (92%) create mode 100644 pkg/resourcemanager/scheduler/scheduler.go create mode 100644 pkg/resourcemanager/util/BUILD.bazel rename {resourcemanager => pkg/resourcemanager}/util/mock_gpool.go (100%) rename {resourcemanager => pkg/resourcemanager}/util/shard_pool_map.go (97%) rename {resourcemanager => pkg/resourcemanager}/util/shard_pool_map_test.go (97%) rename {resourcemanager => pkg/resourcemanager}/util/util.go (100%) create mode 100644 pkg/server/BUILD.bazel rename {server => pkg/server}/conn.go (97%) rename {server => pkg/server}/conn_stmt.go (95%) rename {server => pkg/server}/conn_stmt_test.go (97%) rename {server => pkg/server}/conn_test.go (90%) rename {server => pkg/server}/driver.go (93%) rename {server => pkg/server}/driver_tidb.go (94%) rename {server => pkg/server}/driver_tidb_test.go (92%) create mode 100644 pkg/server/err/BUILD.bazel create mode 100644 pkg/server/err/error.go create mode 100644 pkg/server/extension.go create mode 100644 pkg/server/extract.go create mode 100644 pkg/server/handler/BUILD.bazel create mode 100644 pkg/server/handler/extactorhandler/BUILD.bazel rename {server => pkg/server}/handler/extactorhandler/extactor.go (96%) create mode 100644 pkg/server/handler/extactorhandler/extract_test.go create mode 100644 pkg/server/handler/extactorhandler/main_test.go create mode 100644 pkg/server/handler/optimizor/BUILD.bazel create mode 100644 pkg/server/handler/optimizor/main_test.go rename {server => pkg/server}/handler/optimizor/optimize_trace.go (91%) rename {server => pkg/server}/handler/optimizor/optimize_trace_test.go (88%) create mode 100644 pkg/server/handler/optimizor/plan_replayer.go create mode 100644 pkg/server/handler/optimizor/plan_replayer_test.go rename {server => pkg/server}/handler/optimizor/statistics_handler.go (91%) rename {server => pkg/server}/handler/optimizor/statistics_handler_test.go (94%) create mode 100644 pkg/server/handler/tests/BUILD.bazel rename {server => pkg/server}/handler/tests/http_handler_serial_test.go (94%) rename {server => pkg/server}/handler/tests/http_handler_test.go (96%) create mode 100644 pkg/server/handler/tests/main_test.go create mode 100644 pkg/server/handler/tikv_handler.go create mode 100644 pkg/server/handler/tikvhandler/BUILD.bazel create mode 100644 pkg/server/handler/tikvhandler/tikv_handler.go create mode 100644 pkg/server/handler/ttlhandler/BUILD.bazel create mode 100644 pkg/server/handler/ttlhandler/ttl.go rename {server => pkg/server}/handler/upgrade_handler.go (97%) create mode 100644 pkg/server/handler/util.go rename {server => pkg/server}/http_handler.go (85%) rename {server => pkg/server}/http_status.go (96%) create mode 100644 pkg/server/internal/BUILD.bazel create mode 100644 pkg/server/internal/column/BUILD.bazel create mode 100644 pkg/server/internal/column/column.go create mode 100644 pkg/server/internal/column/column_test.go create mode 100644 pkg/server/internal/column/convert.go rename {server => pkg/server}/internal/column/result_encoder.go (97%) rename {server => pkg/server}/internal/column/result_encoder_test.go (100%) create mode 100644 pkg/server/internal/dump/BUILD.bazel create mode 100644 pkg/server/internal/dump/dump.go create mode 100644 pkg/server/internal/dump/dump_test.go create mode 100644 pkg/server/internal/handshake/BUILD.bazel rename {server => pkg/server}/internal/handshake/handshake.go (100%) rename {server => pkg/server}/internal/packetio.go (97%) rename {server => pkg/server}/internal/packetio_test.go (98%) create mode 100644 pkg/server/internal/parse/BUILD.bazel rename {server => pkg/server}/internal/parse/handshake_test.go (97%) rename {server => pkg/server}/internal/parse/parse.go (96%) rename {server => pkg/server}/internal/parse/parse_test.go (95%) create mode 100644 pkg/server/internal/resultset/BUILD.bazel rename {server => pkg/server}/internal/resultset/cursor.go (97%) rename {server => pkg/server}/internal/resultset/resultset.go (93%) create mode 100644 pkg/server/internal/testserverclient/BUILD.bazel rename {server => pkg/server}/internal/testserverclient/server_client.go (99%) create mode 100644 pkg/server/internal/testutil/BUILD.bazel rename {server => pkg/server}/internal/testutil/testutil.go (100%) create mode 100644 pkg/server/internal/util/BUILD.bazel rename {server => pkg/server}/internal/util/buffered_read_conn.go (100%) create mode 100644 pkg/server/internal/util/util.go rename {server => pkg/server}/internal/util/util_test.go (100%) create mode 100644 pkg/server/main_test.go create mode 100644 pkg/server/metrics/BUILD.bazel create mode 100644 pkg/server/metrics/metrics.go rename {server => pkg/server}/mock_conn.go (90%) rename {server => pkg/server}/mock_conn_test.go (94%) rename {server => pkg/server}/rpc_server.go (93%) create mode 100644 pkg/server/server.go rename {server => pkg/server}/server_test.go (88%) create mode 100644 pkg/server/stat.go create mode 100644 pkg/server/stat_test.go rename {server => pkg/server}/testdata/optimizer_suite_in.json (100%) rename {server => pkg/server}/testdata/optimizer_suite_out.json (84%) create mode 100644 pkg/server/tests/BUILD.bazel create mode 100644 pkg/server/tests/main_test.go rename {server => pkg/server}/tests/tidb_serial_test.go (97%) create mode 100644 pkg/server/tests/tidb_test.go rename {server => pkg/server}/tidb_library_test.go (92%) create mode 100644 pkg/server/tidb_test.go rename {server => pkg/server}/tokenlimiter.go (100%) create mode 100644 pkg/session/BUILD.bazel rename {session => pkg/session}/OWNERS (100%) rename {session => pkg/session}/advisory_locks.go (97%) create mode 100644 pkg/session/bench_test.go create mode 100644 pkg/session/bootstrap.go create mode 100644 pkg/session/bootstrap_test.go create mode 100644 pkg/session/bootstraptest/BUILD.bazel rename {session => pkg/session}/bootstraptest/bootstrap_upgrade_test.go (97%) create mode 100644 pkg/session/bootstraptest/main_test.go create mode 100644 pkg/session/clusteredindextest/BUILD.bazel rename {session => pkg/session}/clusteredindextest/clustered_index_test.go (98%) create mode 100644 pkg/session/clusteredindextest/main_test.go rename {session => pkg/session}/index_usage_sync_lease_test.go (100%) create mode 100644 pkg/session/main_test.go create mode 100644 pkg/session/metrics/BUILD.bazel create mode 100644 pkg/session/metrics/metrics.go rename {session => pkg/session}/mock_bootstrap.go (99%) rename {session => pkg/session}/nontransactional.go (96%) create mode 100644 pkg/session/nontransactionaltest/BUILD.bazel create mode 100644 pkg/session/nontransactionaltest/main_test.go rename {session => pkg/session}/nontransactionaltest/nontransactional_test.go (92%) create mode 100644 pkg/session/resourcegrouptest/BUILD.bazel create mode 100644 pkg/session/resourcegrouptest/resource_group_test.go create mode 100644 pkg/session/schematest/BUILD.bazel create mode 100644 pkg/session/schematest/main_test.go create mode 100644 pkg/session/schematest/schema_test.go create mode 100644 pkg/session/session.go rename {session => pkg/session}/sync_upgrade.go (95%) create mode 100644 pkg/session/temporarytabletest/BUILD.bazel create mode 100644 pkg/session/temporarytabletest/main_test.go create mode 100644 pkg/session/temporarytabletest/temporary_table_test.go create mode 100644 pkg/session/test/BUILD.bazel create mode 100644 pkg/session/test/common/BUILD.bazel create mode 100644 pkg/session/test/common/common_test.go create mode 100644 pkg/session/test/common/main_test.go create mode 100644 pkg/session/test/main_test.go create mode 100644 pkg/session/test/meta/BUILD.bazel create mode 100644 pkg/session/test/meta/main_test.go create mode 100644 pkg/session/test/meta/session_test.go create mode 100644 pkg/session/test/privileges/BUILD.bazel create mode 100644 pkg/session/test/privileges/main_test.go create mode 100644 pkg/session/test/privileges/privileges_test.go create mode 100644 pkg/session/test/session_test.go create mode 100644 pkg/session/test/txn/BUILD.bazel create mode 100644 pkg/session/test/txn/main_test.go create mode 100644 pkg/session/test/txn/txn_test.go create mode 100644 pkg/session/test/variable/BUILD.bazel create mode 100644 pkg/session/test/variable/main_test.go create mode 100644 pkg/session/test/variable/variable_test.go create mode 100644 pkg/session/test/vars/BUILD.bazel create mode 100644 pkg/session/test/vars/main_test.go rename {session => pkg/session}/test/vars/vars_test.go (98%) create mode 100644 pkg/session/testutil.go rename {session => pkg/session}/tidb.go (93%) create mode 100644 pkg/session/tidb_test.go create mode 100644 pkg/session/txn.go create mode 100644 pkg/session/txninfo/BUILD.bazel create mode 100644 pkg/session/txninfo/summary.go rename {session => pkg/session}/txninfo/txn_info.go (98%) rename {session => pkg/session}/txnmanager.go (96%) create mode 100644 pkg/sessionctx/BUILD.bazel create mode 100644 pkg/sessionctx/binloginfo/BUILD.bazel rename {sessionctx => pkg/sessionctx}/binloginfo/binloginfo.go (95%) rename {sessionctx => pkg/sessionctx}/binloginfo/binloginfo_test.go (97%) create mode 100644 pkg/sessionctx/binloginfo/main_test.go create mode 100644 pkg/sessionctx/context.go rename {sessionctx => pkg/sessionctx}/context_test.go (100%) create mode 100644 pkg/sessionctx/main_test.go create mode 100644 pkg/sessionctx/sessionstates/BUILD.bazel rename {sessionctx => pkg/sessionctx}/sessionstates/session_states.go (93%) rename {sessionctx => pkg/sessionctx}/sessionstates/session_states_test.go (99%) rename {sessionctx => pkg/sessionctx}/sessionstates/session_token.go (99%) rename {sessionctx => pkg/sessionctx}/sessionstates/session_token_test.go (98%) create mode 100644 pkg/sessionctx/stmtctx/BUILD.bazel create mode 100644 pkg/sessionctx/stmtctx/main_test.go rename {sessionctx => pkg/sessionctx}/stmtctx/stmtctx.go (98%) rename {sessionctx => pkg/sessionctx}/stmtctx/stmtctx_test.go (97%) create mode 100644 pkg/sessionctx/variable/BUILD.bazel rename {sessionctx => pkg/sessionctx}/variable/OWNERS (100%) create mode 100644 pkg/sessionctx/variable/error.go create mode 100644 pkg/sessionctx/variable/featuretag/disttask/BUILD.bazel rename {sessionctx => pkg/sessionctx}/variable/featuretag/disttask/default.go (100%) rename {sessionctx => pkg/sessionctx}/variable/featuretag/disttask/non_default.go (100%) create mode 100644 pkg/sessionctx/variable/main_test.go rename {sessionctx => pkg/sessionctx}/variable/mock_globalaccessor.go (100%) rename {sessionctx => pkg/sessionctx}/variable/mock_globalaccessor_test.go (100%) rename {sessionctx => pkg/sessionctx}/variable/noop.go (100%) rename {sessionctx => pkg/sessionctx}/variable/removed.go (100%) rename {sessionctx => pkg/sessionctx}/variable/removed_test.go (100%) rename {sessionctx => pkg/sessionctx}/variable/sequence_state.go (100%) create mode 100644 pkg/sessionctx/variable/session.go create mode 100644 pkg/sessionctx/variable/session_test.go rename {sessionctx => pkg/sessionctx}/variable/setvar_affect.go (100%) rename {sessionctx => pkg/sessionctx}/variable/statusvar.go (99%) rename {sessionctx => pkg/sessionctx}/variable/statusvar_test.go (100%) rename {sessionctx => pkg/sessionctx}/variable/sysvar.go (99%) create mode 100644 pkg/sessionctx/variable/sysvar_test.go rename {sessionctx => pkg/sessionctx}/variable/tidb_vars.go (99%) create mode 100644 pkg/sessionctx/variable/variable.go create mode 100644 pkg/sessionctx/variable/variable_test.go rename {sessionctx => pkg/sessionctx}/variable/varsutil.go (98%) rename {sessionctx => pkg/sessionctx}/variable/varsutil_test.go (99%) create mode 100644 pkg/sessiontxn/BUILD.bazel create mode 100644 pkg/sessiontxn/failpoint.go rename {sessiontxn => pkg/sessiontxn}/future.go (100%) create mode 100644 pkg/sessiontxn/interface.go create mode 100644 pkg/sessiontxn/internal/BUILD.bazel create mode 100644 pkg/sessiontxn/internal/txn.go create mode 100644 pkg/sessiontxn/isolation/BUILD.bazel create mode 100644 pkg/sessiontxn/isolation/base.go create mode 100644 pkg/sessiontxn/isolation/main_test.go create mode 100644 pkg/sessiontxn/isolation/metrics/BUILD.bazel create mode 100644 pkg/sessiontxn/isolation/metrics/metrics.go rename {sessiontxn => pkg/sessiontxn}/isolation/optimistic.go (94%) rename {sessiontxn => pkg/sessiontxn}/isolation/optimistic_test.go (96%) rename {sessiontxn => pkg/sessiontxn}/isolation/readcommitted.go (96%) rename {sessiontxn => pkg/sessiontxn}/isolation/readcommitted_test.go (96%) rename {sessiontxn => pkg/sessiontxn}/isolation/repeatable_read.go (96%) rename {sessiontxn => pkg/sessiontxn}/isolation/repeatable_read_test.go (94%) rename {sessiontxn => pkg/sessiontxn}/isolation/serializable.go (91%) rename {sessiontxn => pkg/sessiontxn}/isolation/serializable_test.go (95%) create mode 100644 pkg/sessiontxn/staleread/BUILD.bazel create mode 100644 pkg/sessiontxn/staleread/errors.go rename {sessiontxn => pkg/sessiontxn}/staleread/externalts_test.go (94%) create mode 100644 pkg/sessiontxn/staleread/failpoint.go create mode 100644 pkg/sessiontxn/staleread/main_test.go rename {sessiontxn => pkg/sessiontxn}/staleread/processor.go (97%) rename {sessiontxn => pkg/sessiontxn}/staleread/processor_test.go (97%) rename {sessiontxn => pkg/sessiontxn}/staleread/provider.go (95%) rename {sessiontxn => pkg/sessiontxn}/staleread/provider_test.go (93%) create mode 100644 pkg/sessiontxn/staleread/util.go rename {sessiontxn => pkg/sessiontxn}/txn_context_test.go (94%) rename {sessiontxn => pkg/sessiontxn}/txn_manager_test.go (97%) rename {sessiontxn => pkg/sessiontxn}/txn_rc_tso_optimize_test.go (94%) create mode 100644 pkg/statistics/BUILD.bazel rename {statistics => pkg/statistics}/analyze.go (100%) rename {statistics => pkg/statistics}/analyze_jobs.go (100%) create mode 100644 pkg/statistics/builder.go rename {statistics => pkg/statistics}/cmsketch.go (98%) rename {statistics => pkg/statistics}/cmsketch_test.go (97%) rename {statistics => pkg/statistics}/cmsketch_util.go (94%) create mode 100644 pkg/statistics/column.go create mode 100644 pkg/statistics/debugtrace.go rename {statistics => pkg/statistics}/estimate.go (97%) rename {statistics => pkg/statistics}/fmsketch.go (97%) rename {statistics => pkg/statistics}/fmsketch_test.go (97%) create mode 100644 pkg/statistics/handle/BUILD.bazel create mode 100644 pkg/statistics/handle/autoanalyze/BUILD.bazel rename {statistics => pkg/statistics}/handle/autoanalyze/autoanalyze.go (96%) rename {statistics => pkg/statistics}/handle/autoanalyze/autoanalyze_test.go (98%) create mode 100644 pkg/statistics/handle/bootstrap.go create mode 100644 pkg/statistics/handle/cache/BUILD.bazel create mode 100644 pkg/statistics/handle/cache/bench_test.go create mode 100644 pkg/statistics/handle/cache/internal/BUILD.bazel rename {statistics => pkg/statistics}/handle/cache/internal/inner.go (97%) create mode 100644 pkg/statistics/handle/cache/internal/lfu/BUILD.bazel rename {statistics => pkg/statistics}/handle/cache/internal/lfu/key_set.go (97%) rename {statistics => pkg/statistics}/handle/cache/internal/lfu/key_set_shard.go (97%) rename {statistics => pkg/statistics}/handle/cache/internal/lfu/lfu_cache.go (95%) rename {statistics => pkg/statistics}/handle/cache/internal/lfu/lfu_cache_test.go (98%) create mode 100644 pkg/statistics/handle/cache/internal/mapcache/BUILD.bazel rename {statistics => pkg/statistics}/handle/cache/internal/mapcache/map_cache.go (96%) create mode 100644 pkg/statistics/handle/cache/internal/metrics/BUILD.bazel create mode 100644 pkg/statistics/handle/cache/internal/metrics/metrics.go create mode 100644 pkg/statistics/handle/cache/internal/testutil/BUILD.bazel create mode 100644 pkg/statistics/handle/cache/internal/testutil/testutil.go rename {statistics => pkg/statistics}/handle/cache/statscache.go (92%) rename {statistics => pkg/statistics}/handle/cache/statscacheinner.go (93%) create mode 100644 pkg/statistics/handle/ddl.go create mode 100644 pkg/statistics/handle/ddl_test.go create mode 100644 pkg/statistics/handle/dump.go create mode 100644 pkg/statistics/handle/dump_test.go create mode 100644 pkg/statistics/handle/extstats/BUILD.bazel rename {statistics => pkg/statistics}/handle/extstats/extended_stats.go (93%) rename {statistics => pkg/statistics}/handle/gc_test.go (98%) create mode 100644 pkg/statistics/handle/globalstats/BUILD.bazel rename {statistics => pkg/statistics}/handle/globalstats/global_stats.go (93%) rename {statistics => pkg/statistics}/handle/globalstats/global_stats_async.go (97%) rename {statistics => pkg/statistics}/handle/globalstats/merge_worker.go (98%) create mode 100644 pkg/statistics/handle/globalstats/topn.go rename {statistics => pkg/statistics}/handle/globalstats/topn_bench_test.go (92%) create mode 100644 pkg/statistics/handle/handle.go rename {statistics => pkg/statistics}/handle/handle_hist.go (96%) rename {statistics => pkg/statistics}/handle/handle_hist_test.go (90%) create mode 100644 pkg/statistics/handle/handletest/BUILD.bazel create mode 100644 pkg/statistics/handle/handletest/analyze/BUILD.bazel create mode 100644 pkg/statistics/handle/handletest/analyze/analyze_test.go create mode 100644 pkg/statistics/handle/handletest/analyze/main_test.go create mode 100644 pkg/statistics/handle/handletest/globalstats/BUILD.bazel rename {statistics => pkg/statistics}/handle/handletest/globalstats/globalstats_test.go (99%) create mode 100644 pkg/statistics/handle/handletest/globalstats/main_test.go create mode 100644 pkg/statistics/handle/handletest/handle_test.go create mode 100644 pkg/statistics/handle/handletest/lockstats/BUILD.bazel rename {statistics => pkg/statistics}/handle/handletest/lockstats/lock_partition_stats_test.go (99%) rename {statistics => pkg/statistics}/handle/handletest/lockstats/lock_table_stats_test.go (98%) create mode 100644 pkg/statistics/handle/handletest/lockstats/main_test.go create mode 100644 pkg/statistics/handle/handletest/main_test.go create mode 100644 pkg/statistics/handle/handletest/statstest/BUILD.bazel create mode 100644 pkg/statistics/handle/handletest/statstest/main_test.go create mode 100644 pkg/statistics/handle/handletest/statstest/stats_test.go create mode 100644 pkg/statistics/handle/history/BUILD.bazel rename {statistics => pkg/statistics}/handle/history/history_stats.go (94%) create mode 100644 pkg/statistics/handle/internal/BUILD.bazel create mode 100644 pkg/statistics/handle/internal/testutil.go create mode 100644 pkg/statistics/handle/lockstats/BUILD.bazel rename {statistics => pkg/statistics}/handle/lockstats/lock_stats.go (98%) rename {statistics => pkg/statistics}/handle/lockstats/lock_stats_test.go (97%) rename {statistics => pkg/statistics}/handle/lockstats/query_lock.go (94%) rename {statistics => pkg/statistics}/handle/lockstats/query_lock_test.go (92%) rename {statistics => pkg/statistics}/handle/lockstats/unlock_stats.go (97%) rename {statistics => pkg/statistics}/handle/lockstats/unlock_stats_test.go (96%) create mode 100644 pkg/statistics/handle/main_test.go create mode 100644 pkg/statistics/handle/metrics/BUILD.bazel create mode 100644 pkg/statistics/handle/metrics/metrics.go create mode 100644 pkg/statistics/handle/storage/BUILD.bazel rename {statistics => pkg/statistics}/handle/storage/gc.go (96%) rename {statistics => pkg/statistics}/handle/storage/json.go (96%) rename {statistics => pkg/statistics}/handle/storage/read.go (97%) rename {statistics => pkg/statistics}/handle/storage/read_test.go (96%) rename {statistics => pkg/statistics}/handle/storage/save.go (97%) create mode 100644 pkg/statistics/handle/storage/update.go create mode 100644 pkg/statistics/handle/update.go create mode 100644 pkg/statistics/handle/updatetest/BUILD.bazel create mode 100644 pkg/statistics/handle/updatetest/main_test.go create mode 100644 pkg/statistics/handle/updatetest/update_test.go create mode 100644 pkg/statistics/handle/usage/BUILD.bazel rename {statistics => pkg/statistics}/handle/usage/index_usage.go (97%) rename {statistics => pkg/statistics}/handle/usage/predicate_column.go (95%) rename {statistics => pkg/statistics}/handle/usage/session_stats_collect.go (97%) rename {statistics => pkg/statistics}/handle/usage/session_stats_collect_test.go (100%) create mode 100644 pkg/statistics/handle/util/BUILD.bazel rename {statistics => pkg/statistics}/handle/util/interfaces.go (97%) rename {statistics => pkg/statistics}/handle/util/table_info.go (96%) create mode 100644 pkg/statistics/handle/util/util.go rename {statistics => pkg/statistics}/histogram.go (98%) rename {statistics => pkg/statistics}/histogram_bench_test.go (93%) rename {statistics => pkg/statistics}/histogram_test.go (98%) create mode 100644 pkg/statistics/index.go create mode 100644 pkg/statistics/integration_test.go create mode 100644 pkg/statistics/main_test.go rename {statistics => pkg/statistics}/row_sampler.go (97%) create mode 100644 pkg/statistics/sample.go create mode 100644 pkg/statistics/sample_test.go rename {statistics => pkg/statistics}/scalar.go (98%) rename {statistics => pkg/statistics}/scalar_test.go (99%) rename {statistics => pkg/statistics}/statistics_test.go (97%) create mode 100644 pkg/statistics/table.go rename {statistics => pkg/statistics}/testdata/integration_suite_in.json (100%) rename {statistics => pkg/statistics}/testdata/integration_suite_out.json (100%) create mode 100644 pkg/store/BUILD.bazel create mode 100644 pkg/store/batch_coprocessor_test.go create mode 100644 pkg/store/copr/BUILD.bazel rename {store => pkg/store}/copr/batch_coprocessor.go (99%) create mode 100644 pkg/store/copr/batch_coprocessor_test.go rename {store => pkg/store}/copr/batch_request_sender.go (99%) create mode 100644 pkg/store/copr/copr_test/BUILD.bazel create mode 100644 pkg/store/copr/copr_test/coprocessor_test.go create mode 100644 pkg/store/copr/copr_test/main_test.go create mode 100644 pkg/store/copr/coprocessor.go rename {store => pkg/store}/copr/coprocessor_cache.go (98%) rename {store => pkg/store}/copr/coprocessor_cache_test.go (99%) create mode 100644 pkg/store/copr/coprocessor_test.go rename {store => pkg/store}/copr/key_ranges.go (99%) rename {store => pkg/store}/copr/key_ranges_test.go (99%) create mode 100644 pkg/store/copr/main_test.go create mode 100644 pkg/store/copr/metrics/BUILD.bazel create mode 100644 pkg/store/copr/metrics/metrics.go create mode 100644 pkg/store/copr/mpp.go rename {store => pkg/store}/copr/mpp_probe.go (98%) rename {store => pkg/store}/copr/mpp_probe_test.go (100%) rename {store => pkg/store}/copr/region_cache.go (97%) create mode 100644 pkg/store/copr/store.go create mode 100644 pkg/store/driver/BUILD.bazel create mode 100644 pkg/store/driver/backoff/BUILD.bazel rename {store => pkg/store}/driver/backoff/backoff.go (96%) create mode 100644 pkg/store/driver/client_test.go rename {store => pkg/store}/driver/config_test.go (100%) create mode 100644 pkg/store/driver/error/BUILD.bazel create mode 100644 pkg/store/driver/error/error.go create mode 100644 pkg/store/driver/error/error_test.go create mode 100644 pkg/store/driver/main_test.go create mode 100644 pkg/store/driver/options/BUILD.bazel create mode 100644 pkg/store/driver/options/options.go rename {store => pkg/store}/driver/snap_interceptor_test.go (98%) rename {store => pkg/store}/driver/sql_fail_test.go (95%) rename {store => pkg/store}/driver/tikv_driver.go (96%) create mode 100644 pkg/store/driver/txn/BUILD.bazel rename {store => pkg/store}/driver/txn/batch_getter.go (99%) rename {store => pkg/store}/driver/txn/batch_getter_test.go (98%) rename {store => pkg/store}/driver/txn/binlog.go (95%) rename {store => pkg/store}/driver/txn/driver_test.go (99%) create mode 100644 pkg/store/driver/txn/error.go create mode 100644 pkg/store/driver/txn/main_test.go rename {store => pkg/store}/driver/txn/scanner.go (96%) rename {store => pkg/store}/driver/txn/snapshot.go (97%) rename {store => pkg/store}/driver/txn/txn_driver.go (97%) rename {store => pkg/store}/driver/txn/union_iter.go (99%) rename {store => pkg/store}/driver/txn/union_iter_test.go (98%) rename {store => pkg/store}/driver/txn/unionstore_driver.go (98%) create mode 100644 pkg/store/driver/txn_test.go create mode 100644 pkg/store/gcworker/BUILD.bazel rename {store => pkg/store}/gcworker/gc_worker.go (99%) rename {store => pkg/store}/gcworker/gc_worker_test.go (96%) create mode 100644 pkg/store/gcworker/main_test.go create mode 100644 pkg/store/helper/BUILD.bazel create mode 100644 pkg/store/helper/helper.go create mode 100644 pkg/store/helper/helper_test.go create mode 100644 pkg/store/helper/main_test.go create mode 100644 pkg/store/main_test.go create mode 100644 pkg/store/mockstore/BUILD.bazel create mode 100644 pkg/store/mockstore/cluster_test.go create mode 100644 pkg/store/mockstore/main_test.go create mode 100644 pkg/store/mockstore/mockcopr/BUILD.bazel rename {store => pkg/store}/mockstore/mockcopr/aggregate.go (97%) create mode 100644 pkg/store/mockstore/mockcopr/analyze.go rename {store => pkg/store}/mockstore/mockcopr/checksum.go (100%) rename {store => pkg/store}/mockstore/mockcopr/cop_handler_dag.go (97%) rename {store => pkg/store}/mockstore/mockcopr/copr_handler.go (98%) create mode 100644 pkg/store/mockstore/mockcopr/executor.go create mode 100644 pkg/store/mockstore/mockcopr/executor_test.go create mode 100644 pkg/store/mockstore/mockcopr/main_test.go rename {store => pkg/store}/mockstore/mockcopr/rpc_copr.go (98%) create mode 100644 pkg/store/mockstore/mockcopr/topn.go create mode 100644 pkg/store/mockstore/mockstorage/BUILD.bazel create mode 100644 pkg/store/mockstore/mockstorage/storage.go create mode 100644 pkg/store/mockstore/mockstore.go rename {store => pkg/store}/mockstore/redirector.go (98%) create mode 100644 pkg/store/mockstore/tikv.go rename {store => pkg/store}/mockstore/tikv_test.go (97%) rename {store => pkg/store}/mockstore/unistore.go (89%) create mode 100644 pkg/store/mockstore/unistore/BUILD.bazel create mode 100644 pkg/store/mockstore/unistore/client/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/client/client.go (100%) create mode 100644 pkg/store/mockstore/unistore/cluster.go create mode 100644 pkg/store/mockstore/unistore/config/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/config/config-template.toml (100%) rename {store => pkg/store}/mockstore/unistore/config/config.go (100%) create mode 100644 pkg/store/mockstore/unistore/cophandler/BUILD.bazel create mode 100644 pkg/store/mockstore/unistore/cophandler/analyze.go rename {store => pkg/store}/mockstore/unistore/cophandler/closure_exec.go (97%) rename {store => pkg/store}/mockstore/unistore/cophandler/cop_handler.go (95%) rename {store => pkg/store}/mockstore/unistore/cophandler/cop_handler_test.go (96%) create mode 100644 pkg/store/mockstore/unistore/cophandler/main_test.go create mode 100644 pkg/store/mockstore/unistore/cophandler/mpp.go rename {store => pkg/store}/mockstore/unistore/cophandler/mpp_exec.go (97%) create mode 100644 pkg/store/mockstore/unistore/cophandler/topn.go create mode 100644 pkg/store/mockstore/unistore/lockstore/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/lockstore/arena.go (100%) rename {store => pkg/store}/mockstore/unistore/lockstore/iterator.go (100%) rename {store => pkg/store}/mockstore/unistore/lockstore/load_dump.go (100%) rename {store => pkg/store}/mockstore/unistore/lockstore/lockstore.go (100%) rename {store => pkg/store}/mockstore/unistore/lockstore/lockstore_test.go (100%) create mode 100644 pkg/store/mockstore/unistore/lockstore/main_test.go create mode 100644 pkg/store/mockstore/unistore/main_test.go create mode 100644 pkg/store/mockstore/unistore/metrics/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/metrics/metrics.go (100%) create mode 100644 pkg/store/mockstore/unistore/mock.go create mode 100644 pkg/store/mockstore/unistore/pd.go create mode 100644 pkg/store/mockstore/unistore/pd/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/pd/client.go (100%) rename {store => pkg/store}/mockstore/unistore/pd_test.go (100%) rename {store => pkg/store}/mockstore/unistore/raw_handler.go (98%) rename {store => pkg/store}/mockstore/unistore/raw_handler_test.go (100%) rename {store => pkg/store}/mockstore/unistore/rpc.go (99%) create mode 100644 pkg/store/mockstore/unistore/server/BUILD.bazel create mode 100644 pkg/store/mockstore/unistore/server/server.go create mode 100644 pkg/store/mockstore/unistore/testutil.go create mode 100644 pkg/store/mockstore/unistore/tikv/BUILD.bazel create mode 100644 pkg/store/mockstore/unistore/tikv/dbreader/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/tikv/dbreader/db_reader.go (98%) rename {store => pkg/store}/mockstore/unistore/tikv/deadlock.go (97%) rename {store => pkg/store}/mockstore/unistore/tikv/detector.go (98%) rename {store => pkg/store}/mockstore/unistore/tikv/detector_test.go (100%) rename {store => pkg/store}/mockstore/unistore/tikv/inner_server.go (94%) create mode 100644 pkg/store/mockstore/unistore/tikv/kverrors/BUILD.bazel create mode 100644 pkg/store/mockstore/unistore/tikv/kverrors/errors.go create mode 100644 pkg/store/mockstore/unistore/tikv/main_test.go rename {store => pkg/store}/mockstore/unistore/tikv/mock_region.go (98%) create mode 100644 pkg/store/mockstore/unistore/tikv/mvcc.go create mode 100644 pkg/store/mockstore/unistore/tikv/mvcc/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/tikv/mvcc/db_writer.go (96%) create mode 100644 pkg/store/mockstore/unistore/tikv/mvcc/mvcc.go create mode 100644 pkg/store/mockstore/unistore/tikv/mvcc/tikv.go rename {store => pkg/store}/mockstore/unistore/tikv/mvcc_test.go (99%) create mode 100644 pkg/store/mockstore/unistore/tikv/pberror/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/tikv/pberror/pberror.go (100%) create mode 100644 pkg/store/mockstore/unistore/tikv/region.go create mode 100644 pkg/store/mockstore/unistore/tikv/server.go rename {store => pkg/store}/mockstore/unistore/tikv/server_batch.go (100%) rename {store => pkg/store}/mockstore/unistore/tikv/util.go (100%) create mode 100644 pkg/store/mockstore/unistore/tikv/write.go create mode 100644 pkg/store/mockstore/unistore/util/lockwaiter/BUILD.bazel rename {store => pkg/store}/mockstore/unistore/util/lockwaiter/lockwaiter.go (99%) rename {store => pkg/store}/mockstore/unistore/util/lockwaiter/lockwaiter_test.go (98%) create mode 100644 pkg/store/mockstore/unistore/util/lockwaiter/main_test.go create mode 100644 pkg/store/pdtypes/BUILD.bazel rename {store => pkg/store}/pdtypes/api.go (100%) rename {store => pkg/store}/pdtypes/config.go (100%) rename {store => pkg/store}/pdtypes/placement.go (100%) rename {store => pkg/store}/pdtypes/region_tree.go (100%) rename {store => pkg/store}/pdtypes/statistics.go (100%) rename {store => pkg/store}/pdtypes/typeutil.go (100%) create mode 100644 pkg/store/store.go create mode 100644 pkg/store/store_test.go create mode 100644 pkg/structure/BUILD.bazel rename {structure => pkg/structure}/hash.go (99%) create mode 100644 pkg/structure/list.go create mode 100644 pkg/structure/main_test.go rename {structure => pkg/structure}/string.go (98%) rename {structure => pkg/structure}/structure.go (93%) rename {structure => pkg/structure}/structure_test.go (97%) create mode 100644 pkg/structure/type.go create mode 100644 pkg/table/BUILD.bazel create mode 100644 pkg/table/column.go create mode 100644 pkg/table/column_test.go create mode 100644 pkg/table/constraint.go create mode 100644 pkg/table/index.go create mode 100644 pkg/table/main_test.go create mode 100644 pkg/table/table.go create mode 100644 pkg/table/table_test.go create mode 100644 pkg/table/tables/BUILD.bazel create mode 100644 pkg/table/tables/cache.go create mode 100644 pkg/table/tables/cache_test.go create mode 100644 pkg/table/tables/index.go create mode 100644 pkg/table/tables/index_test.go create mode 100644 pkg/table/tables/main_test.go rename {table => pkg/table}/tables/mutation_checker.go (97%) rename {table => pkg/table}/tables/mutation_checker_test.go (95%) create mode 100644 pkg/table/tables/partition.go rename {table => pkg/table}/tables/state_remote.go (98%) rename {table => pkg/table}/tables/state_remote_test.go (96%) create mode 100644 pkg/table/tables/tables.go create mode 100644 pkg/table/tables/tables_test.go create mode 100644 pkg/table/tables/test/partition/BUILD.bazel create mode 100644 pkg/table/tables/test/partition/main_test.go create mode 100644 pkg/table/tables/test/partition/partition_test.go create mode 100644 pkg/table/tables/testutil.go create mode 100644 pkg/table/temptable/BUILD.bazel create mode 100644 pkg/table/temptable/ddl.go create mode 100644 pkg/table/temptable/ddl_test.go create mode 100644 pkg/table/temptable/infoschema.go rename {table => pkg/table}/temptable/interceptor.go (96%) rename {table => pkg/table}/temptable/interceptor_test.go (99%) create mode 100644 pkg/table/temptable/main_test.go create mode 100644 pkg/tablecodec/BUILD.bazel create mode 100644 pkg/tablecodec/bench_test.go create mode 100644 pkg/tablecodec/main_test.go create mode 100644 pkg/tablecodec/rowindexcodec/BUILD.bazel create mode 100644 pkg/tablecodec/rowindexcodec/main_test.go rename {tablecodec => pkg/tablecodec}/rowindexcodec/rowindexcodec.go (100%) rename {tablecodec => pkg/tablecodec}/rowindexcodec/rowindexcodec_test.go (100%) rename {tablecodec => pkg/tablecodec}/tablecodec.go (99%) rename {tablecodec => pkg/tablecodec}/tablecodec_test.go (97%) create mode 100644 pkg/telemetry/BUILD.bazel create mode 100644 pkg/telemetry/cte_test/BUILD.bazel create mode 100644 pkg/telemetry/cte_test/cte_test.go rename {telemetry => pkg/telemetry}/data.go (96%) rename {telemetry => pkg/telemetry}/data_cluster_hardware.go (98%) rename {telemetry => pkg/telemetry}/data_cluster_hardware_test.go (100%) rename {telemetry => pkg/telemetry}/data_cluster_info.go (96%) rename {telemetry => pkg/telemetry}/data_feature_usage.go (97%) rename {telemetry => pkg/telemetry}/data_feature_usage_test.go (98%) rename {telemetry => pkg/telemetry}/data_slow_query.go (97%) rename {telemetry => pkg/telemetry}/data_telemetry_host_extra.go (100%) rename {telemetry => pkg/telemetry}/data_window.go (99%) rename {telemetry => pkg/telemetry}/data_window_test.go (92%) rename {telemetry => pkg/telemetry}/id.go (100%) create mode 100644 pkg/telemetry/main_test.go rename {telemetry => pkg/telemetry}/status.go (100%) create mode 100644 pkg/telemetry/telemetry.go rename {telemetry => pkg/telemetry}/telemetry_test.go (96%) create mode 100644 pkg/telemetry/ttl.go rename {telemetry => pkg/telemetry}/util.go (100%) rename {telemetry => pkg/telemetry}/util_test.go (100%) create mode 100644 pkg/testkit/BUILD.bazel rename {testkit => pkg/testkit}/asynctestkit.go (97%) rename {testkit => pkg/testkit}/dbtestkit.go (100%) create mode 100644 pkg/testkit/ddlhelper/BUILD.bazel create mode 100644 pkg/testkit/ddlhelper/helper.go create mode 100644 pkg/testkit/external/BUILD.bazel create mode 100644 pkg/testkit/external/util.go rename {testkit => pkg/testkit}/mocksessionmanager.go (94%) create mode 100644 pkg/testkit/mockstore.go rename {testkit => pkg/testkit}/result.go (100%) rename {testkit => pkg/testkit}/stepped.go (97%) create mode 100644 pkg/testkit/testdata/BUILD.bazel rename {testkit => pkg/testkit}/testdata/testdata.go (99%) create mode 100644 pkg/testkit/testenv/BUILD.bazel rename {testkit => pkg/testkit}/testenv/testenv.go (94%) create mode 100644 pkg/testkit/testfork/BUILD.bazel rename {testkit => pkg/testkit}/testfork/fork.go (100%) rename {testkit => pkg/testkit}/testfork/fork_test.go (100%) create mode 100644 pkg/testkit/testkit.go rename {testkit => pkg/testkit}/testkit_test.go (100%) create mode 100644 pkg/testkit/testmain/BUILD.bazel rename {testkit => pkg/testkit}/testmain/bench.go (100%) rename {testkit => pkg/testkit}/testmain/wrapper.go (100%) create mode 100644 pkg/testkit/testsetup/BUILD.bazel rename {testkit => pkg/testkit}/testsetup/bridge.go (100%) create mode 100644 pkg/testkit/testutil/BUILD.bazel create mode 100644 pkg/testkit/testutil/handle.go rename {testkit => pkg/testkit}/testutil/loghook.go (98%) rename {testkit => pkg/testkit}/testutil/require.go (93%) rename {testkit => pkg/testkit}/testutil/require_test.go (100%) rename {tidb-binlog => pkg/tidb-binlog}/driver/README.md (100%) create mode 100644 pkg/tidb-binlog/driver/reader/BUILD.bazel rename {tidb-binlog => pkg/tidb-binlog}/driver/reader/offset.go (98%) rename {tidb-binlog => pkg/tidb-binlog}/driver/reader/offset_test.go (98%) create mode 100644 pkg/tidb-binlog/driver/reader/reader.go create mode 100644 pkg/tidb-binlog/node/BUILD.bazel rename {tidb-binlog => pkg/tidb-binlog}/node/node.go (100%) rename {tidb-binlog => pkg/tidb-binlog}/node/registry.go (99%) create mode 100644 pkg/tidb-binlog/node/registry_test.go rename {tidb-binlog => pkg/tidb-binlog}/proto/generate-binlog.sh (100%) create mode 100644 pkg/tidb-binlog/proto/go-binlog/BUILD.bazel rename {tidb-binlog => pkg/tidb-binlog}/proto/go-binlog/secondary_binlog.pb.go (100%) rename {tidb-binlog => pkg/tidb-binlog}/proto/proto/secondary_binlog.proto (100%) create mode 100644 pkg/tidb-binlog/pump_client/BUILD.bazel rename {tidb-binlog => pkg/tidb-binlog}/pump_client/bench_test.go (100%) create mode 100644 pkg/tidb-binlog/pump_client/client.go create mode 100644 pkg/tidb-binlog/pump_client/client_test.go rename {tidb-binlog => pkg/tidb-binlog}/pump_client/pump.go (98%) rename {tidb-binlog => pkg/tidb-binlog}/pump_client/selector.go (100%) create mode 100644 pkg/timer/BUILD.bazel rename {timer => pkg/timer}/README.md (100%) create mode 100644 pkg/timer/api/BUILD.bazel create mode 100644 pkg/timer/api/client.go rename {timer => pkg/timer}/api/client_test.go (100%) rename {timer => pkg/timer}/api/error.go (100%) rename {timer => pkg/timer}/api/hook.go (100%) create mode 100644 pkg/timer/api/main_test.go rename {timer => pkg/timer}/api/mem_store.go (99%) rename {timer => pkg/timer}/api/schedule_policy_test.go (100%) rename {timer => pkg/timer}/api/store.go (100%) create mode 100644 pkg/timer/api/store_test.go create mode 100644 pkg/timer/api/timer.go rename {timer => pkg/timer}/api/timer_test.go (100%) create mode 100644 pkg/timer/main_test.go create mode 100644 pkg/timer/metrics/BUILD.bazel rename {timer => pkg/timer}/metrics/metrics.go (100%) create mode 100644 pkg/timer/runtime/BUILD.bazel create mode 100644 pkg/timer/runtime/cache.go create mode 100644 pkg/timer/runtime/cache_test.go create mode 100644 pkg/timer/runtime/main_test.go rename {timer => pkg/timer}/runtime/runtime.go (98%) rename {timer => pkg/timer}/runtime/runtime_test.go (99%) create mode 100644 pkg/timer/runtime/worker.go rename {timer => pkg/timer}/runtime/worker_test.go (99%) rename {timer => pkg/timer}/store_intergartion_test.go (99%) create mode 100644 pkg/timer/tablestore/BUILD.bazel rename {timer => pkg/timer}/tablestore/notifier.go (98%) create mode 100644 pkg/timer/tablestore/sql.go create mode 100644 pkg/timer/tablestore/sql_test.go create mode 100644 pkg/timer/tablestore/store.go create mode 100644 pkg/ttl/cache/BUILD.bazel rename {ttl => pkg/ttl}/cache/base.go (100%) rename {ttl => pkg/ttl}/cache/base_test.go (100%) create mode 100644 pkg/ttl/cache/infoschema.go create mode 100644 pkg/ttl/cache/infoschema_test.go create mode 100644 pkg/ttl/cache/main_test.go create mode 100644 pkg/ttl/cache/split_test.go create mode 100644 pkg/ttl/cache/table.go create mode 100644 pkg/ttl/cache/table_test.go create mode 100644 pkg/ttl/cache/task.go rename {ttl => pkg/ttl}/cache/task_test.go (94%) rename {ttl => pkg/ttl}/cache/ttlstatus.go (97%) rename {ttl => pkg/ttl}/cache/ttlstatus_test.go (97%) create mode 100644 pkg/ttl/client/BUILD.bazel rename {ttl => pkg/ttl}/client/command.go (99%) rename {ttl => pkg/ttl}/client/command_test.go (100%) rename {ttl => pkg/ttl}/client/notification.go (98%) create mode 100644 pkg/ttl/metrics/BUILD.bazel create mode 100644 pkg/ttl/metrics/metrics.go rename {ttl => pkg/ttl}/metrics/metrics_test.go (100%) create mode 100644 pkg/ttl/session/BUILD.bazel create mode 100644 pkg/ttl/session/main_test.go create mode 100644 pkg/ttl/session/session.go create mode 100644 pkg/ttl/session/session_test.go create mode 100644 pkg/ttl/session/sysvar_test.go create mode 100644 pkg/ttl/sqlbuilder/BUILD.bazel create mode 100644 pkg/ttl/sqlbuilder/main_test.go create mode 100644 pkg/ttl/sqlbuilder/sql.go create mode 100644 pkg/ttl/sqlbuilder/sql_test.go create mode 100644 pkg/ttl/ttlworker/BUILD.bazel rename {ttl => pkg/ttl}/ttlworker/config.go (100%) rename {ttl => pkg/ttl}/ttlworker/del.go (95%) rename {ttl => pkg/ttl}/ttlworker/del_test.go (98%) create mode 100644 pkg/ttl/ttlworker/job.go rename {ttl => pkg/ttl}/ttlworker/job_manager.go (98%) rename {ttl => pkg/ttl}/ttlworker/job_manager_integration_test.go (93%) rename {ttl => pkg/ttl}/ttlworker/job_manager_test.go (98%) rename {ttl => pkg/ttl}/ttlworker/scan.go (96%) rename {ttl => pkg/ttl}/ttlworker/scan_test.go (98%) create mode 100644 pkg/ttl/ttlworker/session.go create mode 100644 pkg/ttl/ttlworker/session_test.go rename {ttl => pkg/ttl}/ttlworker/task_manager.go (98%) rename {ttl => pkg/ttl}/ttlworker/task_manager_integration_test.go (97%) rename {ttl => pkg/ttl}/ttlworker/task_manager_test.go (96%) create mode 100644 pkg/ttl/ttlworker/timer.go rename {ttl => pkg/ttl}/ttlworker/timer_sync.go (97%) rename {ttl => pkg/ttl}/ttlworker/timer_sync_test.go (98%) rename {ttl => pkg/ttl}/ttlworker/timer_test.go (99%) create mode 100644 pkg/ttl/ttlworker/worker.go create mode 100644 pkg/types/BUILD.bazel create mode 100644 pkg/types/benchmark_test.go rename {types => pkg/types}/binary_literal.go (99%) rename {types => pkg/types}/binary_literal_test.go (99%) create mode 100644 pkg/types/compare.go rename {types => pkg/types}/compare_test.go (98%) rename {types => pkg/types}/const_test.go (98%) create mode 100644 pkg/types/context.go create mode 100644 pkg/types/context/BUILD.bazel create mode 100644 pkg/types/context/context.go rename {types => pkg/types}/context/context_test.go (100%) create mode 100644 pkg/types/convert.go rename {types => pkg/types}/convert_test.go (99%) rename {types => pkg/types}/core_time.go (100%) rename {types => pkg/types}/core_time_test.go (100%) rename {types => pkg/types}/datum.go (99%) rename {types => pkg/types}/datum_eval.go (95%) rename {types => pkg/types}/datum_test.go (99%) rename {types => pkg/types}/enum.go (96%) rename {types => pkg/types}/enum_test.go (98%) create mode 100644 pkg/types/errors.go create mode 100644 pkg/types/errors_test.go create mode 100644 pkg/types/etc.go create mode 100644 pkg/types/etc_test.go rename {types => pkg/types}/eval_type.go (96%) rename {types => pkg/types}/explain_format.go (100%) rename {types => pkg/types}/export_test.go (100%) rename {types => pkg/types}/field_name.go (95%) create mode 100644 pkg/types/field_type.go rename {types => pkg/types}/field_type_builder.go (100%) create mode 100644 pkg/types/field_type_test.go create mode 100644 pkg/types/format_test.go rename {types => pkg/types}/fsp.go (100%) rename {types => pkg/types}/fsp_test.go (100%) rename {types => pkg/types}/helper.go (100%) rename {types => pkg/types}/helper_test.go (100%) rename {types => pkg/types}/json_binary.go (99%) rename {types => pkg/types}/json_binary_functions.go (99%) rename {types => pkg/types}/json_binary_functions_test.go (100%) rename {types => pkg/types}/json_binary_test.go (100%) rename {types => pkg/types}/json_constants.go (98%) rename {types => pkg/types}/json_path_expr.go (99%) rename {types => pkg/types}/json_path_expr_test.go (100%) create mode 100644 pkg/types/main_test.go rename {types => pkg/types}/mydecimal.go (99%) rename {types => pkg/types}/mydecimal_benchmark_test.go (100%) rename {types => pkg/types}/mydecimal_test.go (100%) rename {types => pkg/types}/overflow.go (100%) rename {types => pkg/types}/overflow_test.go (100%) create mode 100644 pkg/types/parser_driver/BUILD.bazel create mode 100644 pkg/types/parser_driver/main_test.go rename {types => pkg/types}/parser_driver/value_expr.go (97%) rename {types => pkg/types}/parser_driver/value_expr_test.go (97%) create mode 100644 pkg/types/set.go create mode 100644 pkg/types/set_test.go rename {types => pkg/types}/time.go (99%) rename {types => pkg/types}/time_test.go (99%) create mode 100644 pkg/util/BUILD.bazel create mode 100644 pkg/util/admin/BUILD.bazel create mode 100644 pkg/util/admin/admin.go rename {util => pkg/util}/admin/admin_integration_test.go (95%) create mode 100644 pkg/util/admin/main_test.go create mode 100644 pkg/util/arena/BUILD.bazel rename {util => pkg/util}/arena/arena.go (100%) rename {util => pkg/util}/arena/arena_test.go (100%) create mode 100644 pkg/util/arena/main_test.go create mode 100644 pkg/util/backoff/BUILD.bazel rename {util => pkg/util}/backoff/backoff.go (100%) rename {util => pkg/util}/backoff/backoff_test.go (100%) create mode 100644 pkg/util/benchdaily/BUILD.bazel rename {util => pkg/util}/benchdaily/bench_daily.go (100%) rename {util => pkg/util}/benchdaily/bench_daily_test.go (100%) create mode 100644 pkg/util/benchdaily/main_test.go create mode 100644 pkg/util/bitmap/BUILD.bazel rename {util => pkg/util}/bitmap/concurrent.go (100%) rename {util => pkg/util}/bitmap/concurrent_test.go (100%) create mode 100644 pkg/util/bitmap/main_test.go create mode 100644 pkg/util/breakpoint/BUILD.bazel rename {util => pkg/util}/breakpoint/breakpoint.go (92%) create mode 100644 pkg/util/cgroup/BUILD.bazel rename {util => pkg/util}/cgroup/cgroup.go (100%) rename {util => pkg/util}/cgroup/cgroup_cpu.go (100%) rename {util => pkg/util}/cgroup/cgroup_cpu_linux.go (100%) rename {util => pkg/util}/cgroup/cgroup_cpu_test.go (100%) rename {util => pkg/util}/cgroup/cgroup_cpu_unsupport.go (100%) rename {util => pkg/util}/cgroup/cgroup_memory.go (100%) rename {util => pkg/util}/cgroup/cgroup_mock_test.go (100%) create mode 100644 pkg/util/channel/BUILD.bazel rename {util => pkg/util}/channel/channel.go (100%) create mode 100644 pkg/util/checksum/BUILD.bazel create mode 100644 pkg/util/checksum/checksum.go rename {util => pkg/util}/checksum/checksum_test.go (99%) create mode 100644 pkg/util/checksum/main_test.go create mode 100644 pkg/util/chunk/BUILD.bazel rename {util => pkg/util}/chunk/alloc.go (98%) rename {util => pkg/util}/chunk/alloc_test.go (99%) rename {util => pkg/util}/chunk/chunk.go (99%) rename {util => pkg/util}/chunk/chunk_test.go (99%) rename {util => pkg/util}/chunk/chunk_util.go (100%) rename {util => pkg/util}/chunk/chunk_util_test.go (99%) create mode 100644 pkg/util/chunk/codec.go create mode 100644 pkg/util/chunk/codec_test.go create mode 100644 pkg/util/chunk/column.go create mode 100644 pkg/util/chunk/column_test.go create mode 100644 pkg/util/chunk/compare.go rename {util => pkg/util}/chunk/disk.go (97%) rename {util => pkg/util}/chunk/disk_test.go (98%) rename {util => pkg/util}/chunk/iterator.go (100%) rename {util => pkg/util}/chunk/iterator_test.go (98%) create mode 100644 pkg/util/chunk/list.go rename {util => pkg/util}/chunk/list_test.go (97%) create mode 100644 pkg/util/chunk/main_test.go rename {util => pkg/util}/chunk/mutrow.go (98%) rename {util => pkg/util}/chunk/mutrow_test.go (97%) rename {util => pkg/util}/chunk/pool.go (98%) create mode 100644 pkg/util/chunk/pool_test.go rename {util => pkg/util}/chunk/row.go (98%) rename {util => pkg/util}/chunk/row_container.go (99%) rename {util => pkg/util}/chunk/row_container_reader.go (98%) rename {util => pkg/util}/chunk/row_container_test.go (96%) create mode 100644 pkg/util/codec/BUILD.bazel create mode 100644 pkg/util/codec/bench_test.go rename {util => pkg/util}/codec/bytes.go (100%) rename {util => pkg/util}/codec/bytes_test.go (100%) create mode 100644 pkg/util/codec/codec.go create mode 100644 pkg/util/codec/codec_test.go create mode 100644 pkg/util/codec/collation_test.go rename {util => pkg/util}/codec/decimal.go (96%) rename {util => pkg/util}/codec/decimal_test.go (98%) rename {util => pkg/util}/codec/float.go (100%) create mode 100644 pkg/util/codec/main_test.go rename {util => pkg/util}/codec/number.go (100%) create mode 100644 pkg/util/collate/BUILD.bazel rename {util => pkg/util}/collate/bin.go (97%) create mode 100644 pkg/util/collate/charset.go rename {util => pkg/util}/collate/collate.go (98%) rename {util => pkg/util}/collate/collate_bench_test.go (100%) rename {util => pkg/util}/collate/collate_test.go (100%) rename {util => pkg/util}/collate/gbk_bin.go (98%) rename {util => pkg/util}/collate/gbk_chinese_ci.go (97%) rename {util => pkg/util}/collate/gbk_chinese_ci_data.go (100%) rename {util => pkg/util}/collate/general_ci.go (99%) create mode 100644 pkg/util/collate/main_test.go rename {util => pkg/util}/collate/pinyin_tidb_as_cs.go (100%) create mode 100644 pkg/util/collate/ucadata/BUILD.bazel rename {util => pkg/util}/collate/ucadata/data.go (100%) create mode 100644 pkg/util/collate/ucadata/generator/BUILD.bazel rename {util => pkg/util}/collate/ucadata/generator/allkeys-4.0.0.txt (100%) rename {util => pkg/util}/collate/ucadata/generator/allkeys-9.0.0.txt (100%) rename {util => pkg/util}/collate/ucadata/generator/data.go.tpl (100%) rename {util => pkg/util}/collate/ucadata/generator/magic.go (100%) rename {util => pkg/util}/collate/ucadata/generator/main.go (100%) rename {util => pkg/util}/collate/ucadata/unicode_0900_ai_ci_data_generated.go (100%) rename {util => pkg/util}/collate/ucadata/unicode_0900_ai_ci_data_test.go (100%) rename {util => pkg/util}/collate/ucadata/unicode_ci_data_generated.go (100%) rename {util => pkg/util}/collate/ucadata/unicode_ci_data_original_test.go (100%) rename {util => pkg/util}/collate/ucadata/unicode_ci_data_test.go (100%) create mode 100644 pkg/util/collate/ucaimpl/BUILD.bazel rename {util => pkg/util}/collate/ucaimpl/main.go (100%) rename {util => pkg/util}/collate/ucaimpl/unicode_ci.go.tpl (100%) rename {util => pkg/util}/collate/unicode_0400_ci_generated.go (100%) rename {util => pkg/util}/collate/unicode_0400_ci_impl.go (95%) rename {util => pkg/util}/collate/unicode_0900_ai_ci_generated.go (100%) rename {util => pkg/util}/collate/unicode_0900_ai_ci_impl.go (95%) create mode 100644 pkg/util/column-mapping/BUILD.bazel rename {util => pkg/util}/column-mapping/README.md (100%) create mode 100644 pkg/util/column-mapping/column.go rename {util => pkg/util}/column-mapping/column_test.go (100%) create mode 100644 pkg/util/compress/BUILD.bazel rename {util => pkg/util}/compress/gzip.go (100%) create mode 100644 pkg/util/cpu/BUILD.bazel create mode 100644 pkg/util/cpu/cpu.go rename {util => pkg/util}/cpu/cpu_test.go (86%) create mode 100644 pkg/util/cpu/main_test.go rename {util => pkg/util}/cpu_posix.go (100%) rename {util => pkg/util}/cpu_windows.go (100%) create mode 100644 pkg/util/cpuprofile/BUILD.bazel rename {util => pkg/util}/cpuprofile/cpuprofile.go (97%) rename {util => pkg/util}/cpuprofile/cpuprofile_test.go (98%) rename {util => pkg/util}/cpuprofile/pprof_api.go (98%) create mode 100644 pkg/util/cpuprofile/testutil/BUILD.bazel rename {util => pkg/util}/cpuprofile/testutil/util.go (100%) create mode 100644 pkg/util/cteutil/BUILD.bazel create mode 100644 pkg/util/cteutil/main_test.go create mode 100644 pkg/util/cteutil/storage.go rename {util => pkg/util}/cteutil/storage_test.go (98%) create mode 100644 pkg/util/dbterror/BUILD.bazel rename {util => pkg/util}/dbterror/ddl_terror.go (99%) create mode 100644 pkg/util/dbterror/exeerrors/BUILD.bazel create mode 100644 pkg/util/dbterror/exeerrors/errors.go create mode 100644 pkg/util/dbterror/main_test.go create mode 100644 pkg/util/dbterror/terror.go rename {util => pkg/util}/dbterror/terror_test.go (99%) create mode 100644 pkg/util/dbutil/BUILD.bazel rename {util => pkg/util}/dbutil/README.md (100%) create mode 100644 pkg/util/dbutil/common.go create mode 100644 pkg/util/dbutil/common_test.go create mode 100644 pkg/util/dbutil/index.go create mode 100644 pkg/util/dbutil/index_test.go rename {util => pkg/util}/dbutil/interface.go (100%) rename {util => pkg/util}/dbutil/query.go (100%) rename {util => pkg/util}/dbutil/retry.go (98%) rename {util => pkg/util}/dbutil/retry_test.go (99%) create mode 100644 pkg/util/dbutil/table.go create mode 100644 pkg/util/dbutil/table_test.go create mode 100644 pkg/util/dbutil/types.go create mode 100644 pkg/util/dbutil/variable.go rename {util => pkg/util}/dbutil/variable_test.go (100%) create mode 100644 pkg/util/ddl-checker/BUILD.bazel rename {util => pkg/util}/ddl-checker/ddl_syncer.go (97%) rename {util => pkg/util}/ddl-checker/executable_checker.go (95%) rename {util => pkg/util}/ddl-checker/executable_checker_test.go (99%) create mode 100644 pkg/util/deadlockhistory/BUILD.bazel rename {util => pkg/util}/deadlockhistory/deadlock_history.go (98%) rename {util => pkg/util}/deadlockhistory/deadlock_history_test.go (98%) create mode 100644 pkg/util/deadlockhistory/main_test.go create mode 100644 pkg/util/disjointset/BUILD.bazel rename {util => pkg/util}/disjointset/int_set.go (100%) rename {util => pkg/util}/disjointset/int_set_test.go (100%) create mode 100644 pkg/util/disjointset/main_test.go create mode 100644 pkg/util/disk/BUILD.bazel create mode 100644 pkg/util/disk/main_test.go rename {util => pkg/util}/disk/tempDir.go (97%) rename {util => pkg/util}/disk/tempDir_test.go (97%) create mode 100644 pkg/util/disk/tracker.go create mode 100644 pkg/util/distrole/BUILD.bazel rename {util => pkg/util}/distrole/role.go (100%) create mode 100644 pkg/util/disttask/BUILD.bazel rename {util => pkg/util}/disttask/idservice.go (97%) rename {util => pkg/util}/disttask/idservice_test.go (100%) create mode 100644 pkg/util/domainutil/BUILD.bazel rename {util => pkg/util}/domainutil/repair_vars.go (99%) create mode 100644 pkg/util/encrypt/BUILD.bazel rename {util => pkg/util}/encrypt/aes.go (100%) rename {util => pkg/util}/encrypt/aes_layer.go (100%) rename {util => pkg/util}/encrypt/aes_layer_test.go (99%) rename {util => pkg/util}/encrypt/aes_test.go (100%) rename {util => pkg/util}/encrypt/crypt.go (100%) rename {util => pkg/util}/encrypt/crypt_test.go (100%) create mode 100644 pkg/util/encrypt/main_test.go create mode 100644 pkg/util/engine/BUILD.bazel rename {util => pkg/util}/engine/engine.go (100%) rename {util => pkg/util}/errors.go (100%) create mode 100644 pkg/util/errors_test.go rename {util => pkg/util}/etcd.go (95%) create mode 100644 pkg/util/etcd/BUILD.bazel rename {util => pkg/util}/etcd/etcd.go (100%) rename {util => pkg/util}/etcd/etcd_test.go (100%) create mode 100644 pkg/util/execdetails/BUILD.bazel rename {util => pkg/util}/execdetails/execdetails.go (100%) rename {util => pkg/util}/execdetails/execdetails_test.go (100%) create mode 100644 pkg/util/execdetails/main_test.go create mode 100644 pkg/util/expensivequery/BUILD.bazel rename {util => pkg/util}/expensivequery/expensivequerey_test.go (95%) rename {util => pkg/util}/expensivequery/expensivequery.go (96%) create mode 100644 pkg/util/extsort/BUILD.bazel rename {util => pkg/util}/extsort/disk_sorter.go (99%) rename {util => pkg/util}/extsort/disk_sorter_test.go (100%) rename {util => pkg/util}/extsort/external_sorter.go (100%) rename {util => pkg/util}/extsort/external_sorter_test.go (100%) create mode 100644 pkg/util/fastrand/BUILD.bazel create mode 100644 pkg/util/fastrand/main_test.go rename {util => pkg/util}/fastrand/random.go (100%) rename {util => pkg/util}/fastrand/random_test.go (100%) create mode 100644 pkg/util/filter/BUILD.bazel rename {util => pkg/util}/filter/README.md (100%) rename {util => pkg/util}/filter/filter.go (98%) rename {util => pkg/util}/filter/filter_test.go (100%) rename {util => pkg/util}/filter/schema.go (100%) rename {util => pkg/util}/filter/schema_test.go (100%) create mode 100644 pkg/util/format/BUILD.bazel rename {util => pkg/util}/format/format.go (100%) rename {util => pkg/util}/format/format_test.go (100%) create mode 100644 pkg/util/format/main_test.go create mode 100644 pkg/util/gctuner/BUILD.bazel rename {util => pkg/util}/gctuner/finalizer.go (100%) rename {util => pkg/util}/gctuner/finalizer_test.go (100%) rename {util => pkg/util}/gctuner/mem.go (94%) rename {util => pkg/util}/gctuner/mem_test.go (100%) rename {util => pkg/util}/gctuner/memory_limit_tuner.go (97%) rename {util => pkg/util}/gctuner/memory_limit_tuner_test.go (94%) rename {util => pkg/util}/gctuner/tuner.go (99%) rename {util => pkg/util}/gctuner/tuner_test.go (100%) create mode 100644 pkg/util/gcutil/BUILD.bazel rename {util => pkg/util}/gcutil/gcutil.go (93%) create mode 100644 pkg/util/generatedexpr/BUILD.bazel rename {util => pkg/util}/generatedexpr/gen_expr_test.go (90%) rename {util => pkg/util}/generatedexpr/generated_expr.go (92%) create mode 100644 pkg/util/generatedexpr/main_test.go create mode 100644 pkg/util/generic/BUILD.bazel rename {util => pkg/util}/generic/sync_map.go (100%) rename {util => pkg/util}/generic/sync_map_test.go (97%) create mode 100644 pkg/util/globalconn/BUILD.bazel rename {util => pkg/util}/globalconn/globalconn.go (99%) rename {util => pkg/util}/globalconn/globalconn_test.go (99%) rename {util => pkg/util}/globalconn/pool.go (100%) create mode 100644 pkg/util/globalconn/pool_test.go rename {util => pkg/util}/gogc.go (96%) create mode 100644 pkg/util/hack/BUILD.bazel rename {util => pkg/util}/hack/hack.go (100%) rename {util => pkg/util}/hack/hack_test.go (100%) create mode 100644 pkg/util/hack/main_test.go create mode 100644 pkg/util/hint/BUILD.bazel rename {util => pkg/util}/hint/hint_processor.go (98%) rename {util => pkg/util}/id_generator.go (100%) create mode 100644 pkg/util/importer/BUILD.bazel create mode 100644 pkg/util/importer/config.go rename {util => pkg/util}/importer/data.go (100%) rename {util => pkg/util}/importer/db.go (98%) rename {util => pkg/util}/importer/importer.go (100%) rename {util => pkg/util}/importer/job.go (100%) create mode 100644 pkg/util/importer/parser.go rename {util => pkg/util}/importer/rand.go (100%) create mode 100644 pkg/util/intest/BUILD.bazel rename {util => pkg/util}/intest/assert.go (100%) rename {util => pkg/util}/intest/assert_test.go (98%) rename {util => pkg/util}/intest/common.go (100%) rename {util => pkg/util}/intest/intest.go (100%) create mode 100644 pkg/util/israce/BUILD.bazel rename {util => pkg/util}/israce/israce.go (100%) rename {util => pkg/util}/israce/norace.go (100%) create mode 100644 pkg/util/keydecoder/BUILD.bazel rename {util => pkg/util}/keydecoder/keydecoder.go (96%) rename {util => pkg/util}/keydecoder/keydecoder_test.go (95%) create mode 100644 pkg/util/keydecoder/main_test.go create mode 100644 pkg/util/kvcache/BUILD.bazel create mode 100644 pkg/util/kvcache/main_test.go rename {util => pkg/util}/kvcache/simple_lru.go (97%) rename {util => pkg/util}/kvcache/simple_lru_test.go (99%) create mode 100644 pkg/util/localpool/BUILD.bazel rename {util => pkg/util}/localpool/localpool.go (100%) rename {util => pkg/util}/localpool/localpool_norace.go (100%) rename {util => pkg/util}/localpool/localpool_race.go (100%) rename {util => pkg/util}/localpool/localpool_test.go (97%) create mode 100644 pkg/util/localpool/main_test.go create mode 100644 pkg/util/logutil/BUILD.bazel create mode 100644 pkg/util/logutil/consistency/BUILD.bazel create mode 100644 pkg/util/logutil/consistency/reporter.go rename {util => pkg/util}/logutil/hex.go (100%) rename {util => pkg/util}/logutil/hex_test.go (96%) rename {util => pkg/util}/logutil/log.go (99%) rename {util => pkg/util}/logutil/log_test.go (99%) create mode 100644 pkg/util/logutil/main_test.go rename {util => pkg/util}/logutil/slow_query_logger.go (100%) create mode 100644 pkg/util/main_test.go create mode 100644 pkg/util/mathutil/BUILD.bazel rename {util => pkg/util}/mathutil/exponential_average.go (100%) rename {util => pkg/util}/mathutil/exponential_average_test.go (100%) create mode 100644 pkg/util/mathutil/main_test.go rename {util => pkg/util}/mathutil/math.go (100%) rename {util => pkg/util}/mathutil/math_test.go (100%) rename {util => pkg/util}/mathutil/rand.go (100%) rename {util => pkg/util}/mathutil/rand_test.go (100%) create mode 100644 pkg/util/memory/BUILD.bazel rename {util => pkg/util}/memory/action.go (98%) rename {util => pkg/util}/memory/bench_test.go (100%) create mode 100644 pkg/util/memory/main_test.go rename {util => pkg/util}/memory/meminfo.go (97%) rename {util => pkg/util}/memory/memstats.go (100%) create mode 100644 pkg/util/memory/tracker.go rename {util => pkg/util}/memory/tracker_test.go (99%) create mode 100644 pkg/util/memoryusagealarm/BUILD.bazel rename {util => pkg/util}/memoryusagealarm/memoryusagealarm.go (98%) rename {util => pkg/util}/memoryusagealarm/memoryusagealarm_test.go (98%) create mode 100644 pkg/util/metricsutil/BUILD.bazel create mode 100644 pkg/util/metricsutil/common.go create mode 100644 pkg/util/misc.go create mode 100644 pkg/util/misc_test.go create mode 100644 pkg/util/mock/BUILD.bazel create mode 100644 pkg/util/mock/client.go create mode 100644 pkg/util/mock/context.go rename {util => pkg/util}/mock/iter.go (98%) rename {util => pkg/util}/mock/iter_test.go (98%) create mode 100644 pkg/util/mock/main_test.go rename {util => pkg/util}/mock/metrics.go (100%) rename {util => pkg/util}/mock/mock_test.go (100%) create mode 100644 pkg/util/mock/store.go create mode 100644 pkg/util/mvmap/BUILD.bazel rename {util => pkg/util}/mvmap/bench_test.go (100%) rename {util => pkg/util}/mvmap/fnv.go (100%) create mode 100644 pkg/util/mvmap/main_test.go rename {util => pkg/util}/mvmap/mvmap.go (99%) rename {util => pkg/util}/mvmap/mvmap_test.go (100%) create mode 100644 pkg/util/nocopy/BUILD.bazel rename {util => pkg/util}/nocopy/nocopy.go (100%) create mode 100644 pkg/util/paging/BUILD.bazel create mode 100644 pkg/util/paging/main_test.go rename {util => pkg/util}/paging/paging.go (100%) rename {util => pkg/util}/paging/paging_test.go (100%) create mode 100644 pkg/util/parser/BUILD.bazel create mode 100644 pkg/util/parser/ast.go rename {util => pkg/util}/parser/ast_test.go (91%) create mode 100644 pkg/util/parser/main_test.go rename {util => pkg/util}/parser/parser.go (100%) create mode 100644 pkg/util/parser/parser_test.go create mode 100644 pkg/util/password-validation/BUILD.bazel rename {util => pkg/util}/password-validation/password_validation.go (98%) rename {util => pkg/util}/password-validation/password_validation_test.go (98%) create mode 100644 pkg/util/pdapi/BUILD.bazel rename {util => pkg/util}/pdapi/const.go (100%) create mode 100644 pkg/util/plancache/BUILD.bazel create mode 100644 pkg/util/plancache/util.go create mode 100644 pkg/util/plancodec/BUILD.bazel rename {util => pkg/util}/plancodec/binary_plan_decode.go (99%) create mode 100644 pkg/util/plancodec/codec.go create mode 100644 pkg/util/plancodec/codec_test.go rename {util => pkg/util}/plancodec/id.go (100%) rename {util => pkg/util}/plancodec/id_test.go (100%) create mode 100644 pkg/util/plancodec/main_test.go rename {util => pkg/util}/prefix_helper.go (98%) rename {util => pkg/util}/prefix_helper_test.go (95%) rename {util => pkg/util}/printer.go (100%) create mode 100644 pkg/util/printer/BUILD.bazel create mode 100644 pkg/util/printer/main_test.go rename {util => pkg/util}/printer/printer.go (95%) rename {util => pkg/util}/printer/printer_test.go (100%) rename {util => pkg/util}/processinfo.go (95%) create mode 100644 pkg/util/profile/BUILD.bazel rename {util => pkg/util}/profile/flamegraph.go (98%) rename {util => pkg/util}/profile/flamegraph_test.go (98%) create mode 100644 pkg/util/profile/main_test.go rename {util => pkg/util}/profile/profile.go (97%) rename {util => pkg/util}/profile/profile_test.go (87%) rename {util => pkg/util}/profile/testdata/test.pprof (100%) create mode 100644 pkg/util/promutil/BUILD.bazel rename {util => pkg/util}/promutil/factory.go (100%) rename {util => pkg/util}/promutil/registry.go (100%) rename {util => pkg/util}/promutil/registry_test.go (100%) create mode 100644 pkg/util/ranger/BUILD.bazel create mode 100644 pkg/util/ranger/bench_test.go create mode 100644 pkg/util/ranger/checker.go rename {util => pkg/util}/ranger/detacher.go (99%) create mode 100644 pkg/util/ranger/main_test.go rename {util => pkg/util}/ranger/points.go (98%) rename {util => pkg/util}/ranger/ranger.go (98%) rename {util => pkg/util}/ranger/ranger_test.go (99%) create mode 100644 pkg/util/ranger/types.go rename {util => pkg/util}/ranger/types_test.go (96%) create mode 100644 pkg/util/regexpr-router/BUILD.bazel rename {util => pkg/util}/regexpr-router/regexpr_router.go (98%) rename {util => pkg/util}/regexpr-router/regexpr_router_test.go (98%) create mode 100644 pkg/util/replayer/BUILD.bazel rename {util => pkg/util}/replayer/replayer.go (98%) create mode 100644 pkg/util/resourcegrouptag/BUILD.bazel create mode 100644 pkg/util/resourcegrouptag/main_test.go rename {util => pkg/util}/resourcegrouptag/resource_group_tag.go (97%) rename {util => pkg/util}/resourcegrouptag/resource_group_tag_test.go (99%) rename {util => pkg/util}/rlimit_other.go (96%) rename {util => pkg/util}/rlimit_windows.go (100%) create mode 100644 pkg/util/rowDecoder/BUILD.bazel create mode 100644 pkg/util/rowDecoder/decoder.go rename {util => pkg/util}/rowDecoder/decoder_test.go (92%) create mode 100644 pkg/util/rowDecoder/main_test.go create mode 100644 pkg/util/rowcodec/BUILD.bazel create mode 100644 pkg/util/rowcodec/bench_test.go create mode 100644 pkg/util/rowcodec/common.go create mode 100644 pkg/util/rowcodec/decoder.go rename {util => pkg/util}/rowcodec/encoder.go (96%) create mode 100644 pkg/util/rowcodec/main_test.go rename {util => pkg/util}/rowcodec/row.go (100%) rename {util => pkg/util}/rowcodec/rowcodec_test.go (99%) create mode 100644 pkg/util/schemacmp/BUILD.bazel rename {util => pkg/util}/schemacmp/lattice.go (99%) rename {util => pkg/util}/schemacmp/lattice_test.go (99%) create mode 100644 pkg/util/schemacmp/table.go create mode 100644 pkg/util/schemacmp/table_test.go create mode 100644 pkg/util/schemacmp/type.go rename {util => pkg/util}/schemacmp/type_test.go (98%) create mode 100644 pkg/util/schemacmp/util.go rename {util => pkg/util}/security.go (100%) rename {util => pkg/util}/security_test.go (99%) create mode 100644 pkg/util/selection/BUILD.bazel create mode 100644 pkg/util/selection/main_test.go rename {util => pkg/util}/selection/selection.go (100%) rename {util => pkg/util}/selection/selection_test.go (100%) create mode 100644 pkg/util/sem/BUILD.bazel create mode 100644 pkg/util/sem/main_test.go rename {util => pkg/util}/sem/sem.go (97%) rename {util => pkg/util}/sem/sem_test.go (97%) create mode 100644 pkg/util/servermemorylimit/BUILD.bazel rename {util => pkg/util}/servermemorylimit/servermemorylimit.go (97%) rename {util => pkg/util}/servermemorylimit/servermemorylimit_test.go (96%) create mode 100644 pkg/util/set/BUILD.bazel rename {util => pkg/util}/set/float64_set.go (100%) rename {util => pkg/util}/set/float64_set_test.go (100%) rename {util => pkg/util}/set/int_set.go (100%) rename {util => pkg/util}/set/int_set_test.go (100%) create mode 100644 pkg/util/set/main_test.go rename {util => pkg/util}/set/mem_aware_map.go (98%) rename {util => pkg/util}/set/mem_aware_map_test.go (100%) rename {util => pkg/util}/set/set_with_memory_usage.go (97%) rename {util => pkg/util}/set/set_with_memory_usage_test.go (100%) rename {util => pkg/util}/set/string_set.go (100%) rename {util => pkg/util}/set/string_set_test.go (100%) create mode 100644 pkg/util/signal/BUILD.bazel rename {util => pkg/util}/signal/signal_posix.go (97%) rename {util => pkg/util}/signal/signal_wasm.go (100%) rename {util => pkg/util}/signal/signal_windows.go (96%) create mode 100644 pkg/util/size/BUILD.bazel rename {util => pkg/util}/size/size.go (100%) create mode 100644 pkg/util/skip/BUILD.bazel rename {util => pkg/util}/skip/skip.go (100%) create mode 100644 pkg/util/sli/BUILD.bazel rename {util => pkg/util}/sli/sli.go (98%) create mode 100644 pkg/util/slice/BUILD.bazel create mode 100644 pkg/util/slice/main_test.go rename {util => pkg/util}/slice/slice.go (100%) rename {util => pkg/util}/slice/slice_test.go (100%) create mode 100644 pkg/util/sqlexec/BUILD.bazel create mode 100644 pkg/util/sqlexec/main_test.go create mode 100644 pkg/util/sqlexec/mock/BUILD.bazel rename {util => pkg/util}/sqlexec/mock/restricted_sql_executor_mock.go (94%) rename {util => pkg/util}/sqlexec/restricted_sql_executor.go (97%) rename {util => pkg/util}/sqlexec/simple_record_set.go (93%) rename {util => pkg/util}/sqlexec/utils.go (99%) rename {util => pkg/util}/sqlexec/utils_test.go (100%) create mode 100644 pkg/util/stmtsummary/BUILD.bazel rename {util => pkg/util}/stmtsummary/evicted.go (99%) rename {util => pkg/util}/stmtsummary/evicted_test.go (99%) create mode 100644 pkg/util/stmtsummary/main_test.go create mode 100644 pkg/util/stmtsummary/reader.go rename {util => pkg/util}/stmtsummary/statement_summary.go (99%) rename {util => pkg/util}/stmtsummary/statement_summary_test.go (99%) create mode 100644 pkg/util/stmtsummary/v2/BUILD.bazel create mode 100644 pkg/util/stmtsummary/v2/column.go create mode 100644 pkg/util/stmtsummary/v2/column_test.go rename {util => pkg/util}/stmtsummary/v2/logger.go (98%) create mode 100644 pkg/util/stmtsummary/v2/main_test.go create mode 100644 pkg/util/stmtsummary/v2/reader.go rename {util => pkg/util}/stmtsummary/v2/reader_test.go (98%) rename {util => pkg/util}/stmtsummary/v2/record.go (99%) rename {util => pkg/util}/stmtsummary/v2/record_test.go (100%) create mode 100644 pkg/util/stmtsummary/v2/stmtsummary.go rename {util => pkg/util}/stmtsummary/v2/stmtsummary_test.go (100%) create mode 100644 pkg/util/stmtsummary/v2/tests/BUILD.bazel create mode 100644 pkg/util/stmtsummary/v2/tests/main_test.go create mode 100644 pkg/util/stmtsummary/v2/tests/table_test.go create mode 100644 pkg/util/stringutil/BUILD.bazel create mode 100644 pkg/util/stringutil/main_test.go rename {util => pkg/util}/stringutil/string_util.go (99%) rename {util => pkg/util}/stringutil/string_util_test.go (100%) create mode 100644 pkg/util/syncutil/BUILD.bazel rename {util => pkg/util}/syncutil/mutex_deadlock.go (100%) rename {util => pkg/util}/syncutil/mutex_sync.go (100%) create mode 100644 pkg/util/sys/linux/BUILD.bazel create mode 100644 pkg/util/sys/linux/main_test.go rename {util => pkg/util}/sys/linux/sys_linux.go (100%) rename {util => pkg/util}/sys/linux/sys_other.go (100%) create mode 100644 pkg/util/sys/linux/sys_test.go rename {util => pkg/util}/sys/linux/sys_windows.go (100%) create mode 100644 pkg/util/sys/storage/BUILD.bazel create mode 100644 pkg/util/sys/storage/main_test.go rename {util => pkg/util}/sys/storage/sys_other.go (100%) rename {util => pkg/util}/sys/storage/sys_posix.go (100%) create mode 100644 pkg/util/sys/storage/sys_test.go rename {util => pkg/util}/sys/storage/sys_windows.go (100%) create mode 100644 pkg/util/systimemon/BUILD.bazel create mode 100644 pkg/util/systimemon/main_test.go rename {util => pkg/util}/systimemon/systime_mon.go (96%) rename {util => pkg/util}/systimemon/systime_mon_test.go (100%) create mode 100644 pkg/util/table-filter/BUILD.bazel rename {util => pkg/util}/table-filter/README.md (100%) rename {util => pkg/util}/table-filter/column_filter.go (100%) rename {util => pkg/util}/table-filter/column_filter_test.go (99%) rename {util => pkg/util}/table-filter/compat.go (100%) rename {util => pkg/util}/table-filter/compat_test.go (99%) rename {util => pkg/util}/table-filter/matchers.go (100%) rename {util => pkg/util}/table-filter/parser.go (100%) rename {util => pkg/util}/table-filter/table_filter.go (100%) rename {util => pkg/util}/table-filter/table_filter_test.go (99%) create mode 100644 pkg/util/table-router/BUILD.bazel rename {util => pkg/util}/table-router/router.go (99%) rename {util => pkg/util}/table-router/router_test.go (98%) create mode 100644 pkg/util/table-rule-selector/BUILD.bazel rename {util => pkg/util}/table-rule-selector/selector_test.go (100%) rename {util => pkg/util}/table-rule-selector/trie_selector.go (100%) create mode 100644 pkg/util/tableutil/BUILD.bazel rename {util => pkg/util}/tableutil/tableutil.go (94%) create mode 100644 pkg/util/texttree/BUILD.bazel create mode 100644 pkg/util/texttree/main_test.go rename {util => pkg/util}/texttree/texttree.go (100%) rename {util => pkg/util}/texttree/texttree_test.go (96%) create mode 100644 pkg/util/tiflash/BUILD.bazel rename {util => pkg/util}/tiflash/tiflash_replica_read.go (100%) create mode 100644 pkg/util/tiflashcompute/BUILD.bazel rename {util => pkg/util}/tiflashcompute/dispatch_policy.go (100%) rename {util => pkg/util}/tiflashcompute/topo_fetcher.go (99%) create mode 100644 pkg/util/tikvutil/BUILD.bazel rename {util => pkg/util}/tikvutil/tikvutil.go (100%) create mode 100644 pkg/util/timeutil/BUILD.bazel create mode 100644 pkg/util/timeutil/errors.go create mode 100644 pkg/util/timeutil/main_test.go rename {util => pkg/util}/timeutil/time.go (100%) rename {util => pkg/util}/timeutil/time_test.go (100%) rename {util => pkg/util}/timeutil/time_zone.go (99%) rename {util => pkg/util}/timeutil/time_zone_test.go (100%) create mode 100644 pkg/util/tls/BUILD.bazel rename {util => pkg/util}/tls/tls.go (100%) create mode 100644 pkg/util/topsql/BUILD.bazel create mode 100644 pkg/util/topsql/collector/BUILD.bazel create mode 100644 pkg/util/topsql/collector/cpu.go create mode 100644 pkg/util/topsql/collector/main_test.go create mode 100644 pkg/util/topsql/collector/mock/BUILD.bazel create mode 100644 pkg/util/topsql/collector/mock/mock.go create mode 100644 pkg/util/topsql/main_test.go create mode 100644 pkg/util/topsql/reporter/BUILD.bazel rename {util => pkg/util}/topsql/reporter/datamodel.go (98%) rename {util => pkg/util}/topsql/reporter/datamodel_test.go (99%) rename {util => pkg/util}/topsql/reporter/datasink.go (98%) rename {util => pkg/util}/topsql/reporter/datasink_test.go (100%) create mode 100644 pkg/util/topsql/reporter/main_test.go create mode 100644 pkg/util/topsql/reporter/metrics/BUILD.bazel create mode 100644 pkg/util/topsql/reporter/metrics/metrics.go create mode 100644 pkg/util/topsql/reporter/mock/BUILD.bazel create mode 100644 pkg/util/topsql/reporter/mock/pubsub.go create mode 100644 pkg/util/topsql/reporter/mock/server.go create mode 100644 pkg/util/topsql/reporter/pubsub.go rename {util => pkg/util}/topsql/reporter/pubsub_test.go (100%) create mode 100644 pkg/util/topsql/reporter/reporter.go rename {util => pkg/util}/topsql/reporter/reporter_test.go (98%) rename {util => pkg/util}/topsql/reporter/single_target.go (98%) rename {util => pkg/util}/topsql/reporter/single_target_test.go (96%) create mode 100644 pkg/util/topsql/state/BUILD.bazel rename {util => pkg/util}/topsql/state/state.go (100%) create mode 100644 pkg/util/topsql/stmtstats/BUILD.bazel rename {util => pkg/util}/topsql/stmtstats/aggregator.go (98%) rename {util => pkg/util}/topsql/stmtstats/aggregator_test.go (98%) rename {util => pkg/util}/topsql/stmtstats/kv_exec_count.go (97%) rename {util => pkg/util}/topsql/stmtstats/kv_exec_count_test.go (96%) create mode 100644 pkg/util/topsql/stmtstats/main_test.go rename {util => pkg/util}/topsql/stmtstats/stmtstats.go (100%) rename {util => pkg/util}/topsql/stmtstats/stmtstats_test.go (100%) rename {util => pkg/util}/topsql/topsql.go (95%) rename {util => pkg/util}/topsql/topsql_test.go (95%) create mode 100644 pkg/util/tracing/BUILD.bazel create mode 100644 pkg/util/tracing/main_test.go rename {util => pkg/util}/tracing/noop_bench_test.go (100%) rename {util => pkg/util}/tracing/opt_trace.go (100%) rename {util => pkg/util}/tracing/opt_trace_test.go (100%) create mode 100644 pkg/util/tracing/util.go create mode 100644 pkg/util/tracing/util_test.go create mode 100644 pkg/util/trxevents/BUILD.bazel rename {util => pkg/util}/trxevents/trx_events.go (100%) rename {util => pkg/util}/urls.go (100%) rename {util => pkg/util}/urls_test.go (100%) create mode 100644 pkg/util/util.go create mode 100644 pkg/util/util_test.go create mode 100644 pkg/util/versioninfo/BUILD.bazel rename {util => pkg/util}/versioninfo/versioninfo.go (100%) create mode 100644 pkg/util/vitess/BUILD.bazel create mode 100644 pkg/util/vitess/main_test.go rename {util => pkg/util}/vitess/vitess_hash.go (100%) rename {util => pkg/util}/vitess/vitess_hash_test.go (100%) rename {util => pkg/util}/wait_group_wrapper.go (99%) rename {util => pkg/util}/wait_group_wrapper_test.go (100%) create mode 100644 pkg/util/watcher/BUILD.bazel rename {util => pkg/util}/watcher/event.go (100%) rename {util => pkg/util}/watcher/watcher.go (100%) rename {util => pkg/util}/watcher/watcher_test.go (100%) create mode 100644 pkg/util/zeropool/BUILD.bazel rename {util => pkg/util}/zeropool/pool.go (100%) create mode 100644 pkg/util/zeropool/pool_test.go delete mode 100644 planner/BUILD.bazel delete mode 100644 planner/cardinality/BUILD.bazel delete mode 100644 planner/cardinality/join.go delete mode 100644 planner/cardinality/main_test.go delete mode 100644 planner/cardinality/trace.go delete mode 100644 planner/cardinality/trace_test.go delete mode 100644 planner/cascades/BUILD.bazel delete mode 100644 planner/cascades/integration_test.go delete mode 100644 planner/cascades/main_test.go delete mode 100644 planner/cascades/optimize.go delete mode 100644 planner/cascades/optimize_test.go delete mode 100644 planner/cascades/stringer.go delete mode 100644 planner/cascades/stringer_test.go delete mode 100644 planner/core/BUILD.bazel delete mode 100644 planner/core/binary_plan_test.go delete mode 100644 planner/core/casetest/BUILD.bazel delete mode 100644 planner/core/casetest/binaryplan/BUILD.bazel delete mode 100644 planner/core/casetest/binaryplan/binary_plan_test.go delete mode 100644 planner/core/casetest/binaryplan/main_test.go delete mode 100644 planner/core/casetest/cbotest/BUILD.bazel delete mode 100644 planner/core/casetest/cbotest/cbo_test.go delete mode 100644 planner/core/casetest/cbotest/main_test.go delete mode 100644 planner/core/casetest/dag/BUILD.bazel delete mode 100644 planner/core/casetest/dag/main_test.go delete mode 100644 planner/core/casetest/enforcempp/BUILD.bazel delete mode 100644 planner/core/casetest/enforcempp/enforce_mpp_test.go delete mode 100644 planner/core/casetest/enforcempp/main_test.go delete mode 100644 planner/core/casetest/flatplan/BUILD.bazel delete mode 100644 planner/core/casetest/flatplan/main_test.go delete mode 100644 planner/core/casetest/hint/BUILD.bazel delete mode 100644 planner/core/casetest/hint/main_test.go delete mode 100644 planner/core/casetest/index/BUILD.bazel delete mode 100644 planner/core/casetest/index/index_test.go delete mode 100644 planner/core/casetest/index/main_test.go delete mode 100644 planner/core/casetest/integration_test.go delete mode 100644 planner/core/casetest/main_test.go delete mode 100644 planner/core/casetest/mpp/BUILD.bazel delete mode 100644 planner/core/casetest/mpp/main_test.go delete mode 100644 planner/core/casetest/partition/BUILD.bazel delete mode 100644 planner/core/casetest/partition/integration_partition_test.go delete mode 100644 planner/core/casetest/partition/main_test.go delete mode 100644 planner/core/casetest/physicalplantest/BUILD.bazel delete mode 100644 planner/core/casetest/physicalplantest/main_test.go delete mode 100644 planner/core/casetest/physicalplantest/physical_plan_test.go delete mode 100644 planner/core/casetest/plan_test.go delete mode 100644 planner/core/casetest/planstats/BUILD.bazel delete mode 100644 planner/core/casetest/planstats/main_test.go delete mode 100644 planner/core/casetest/pushdown/BUILD.bazel delete mode 100644 planner/core/casetest/pushdown/main_test.go delete mode 100644 planner/core/casetest/rule/BUILD.bazel delete mode 100644 planner/core/casetest/rule/main_test.go delete mode 100644 planner/core/casetest/rule/rule_join_reorder_test.go delete mode 100644 planner/core/casetest/scalarsubquery/BUILD.bazel delete mode 100644 planner/core/casetest/scalarsubquery/main_test.go delete mode 100644 planner/core/casetest/stats_test.go delete mode 100644 planner/core/casetest/windows/BUILD.bazel delete mode 100644 planner/core/casetest/windows/main_test.go delete mode 100644 planner/core/cbo_test.go delete mode 100644 planner/core/debugtrace.go delete mode 100644 planner/core/enforce_mpp_test.go delete mode 100644 planner/core/errors.go delete mode 100644 planner/core/errors_test.go delete mode 100644 planner/core/explain.go delete mode 100644 planner/core/expression_test.go delete mode 100644 planner/core/foreign_key.go delete mode 100644 planner/core/integration_partition_test.go delete mode 100644 planner/core/integration_test.go delete mode 100644 planner/core/internal/BUILD.bazel delete mode 100644 planner/core/internal/base/BUILD.bazel delete mode 100644 planner/core/internal/base/plan.go delete mode 100644 planner/core/internal/testkit.go delete mode 100644 planner/core/internal/util.go delete mode 100644 planner/core/issuetest/BUILD.bazel delete mode 100644 planner/core/issuetest/main_test.go delete mode 100644 planner/core/main_test.go delete mode 100644 planner/core/metrics/BUILD.bazel delete mode 100644 planner/core/metrics/metrics.go delete mode 100644 planner/core/mock.go delete mode 100644 planner/core/physical_plan_test.go delete mode 100644 planner/core/plan.go delete mode 100644 planner/core/plan_test.go delete mode 100644 planner/core/rule_join_reorder_test.go delete mode 100644 planner/core/stats.go delete mode 100644 planner/core/stringer.go delete mode 100644 planner/core/stringer_test.go delete mode 100644 planner/core/task.go delete mode 100644 planner/core/telemetry.go delete mode 100644 planner/core/tests/prepare/BUILD.bazel delete mode 100644 planner/core/tests/prepare/issue/BUILD.bazel delete mode 100644 planner/core/tests/prepare/issue/main_test.go delete mode 100644 planner/core/tests/prepare/main_test.go delete mode 100644 planner/core/trace.go delete mode 100644 planner/core/util.go delete mode 100644 planner/funcdep/BUILD.bazel delete mode 100644 planner/funcdep/main_test.go delete mode 100644 planner/implementation/BUILD.bazel delete mode 100644 planner/implementation/base.go delete mode 100644 planner/implementation/base_test.go delete mode 100644 planner/implementation/join.go delete mode 100644 planner/implementation/main_test.go delete mode 100644 planner/implementation/sort.go delete mode 100644 planner/memo/BUILD.bazel delete mode 100644 planner/memo/group.go delete mode 100644 planner/memo/main_test.go delete mode 100644 planner/optimize.go delete mode 100644 planner/property/BUILD.bazel delete mode 100644 planner/util/BUILD.bazel delete mode 100644 planner/util/debugtrace/BUILD.bazel delete mode 100644 planner/util/debugtrace/base.go delete mode 100644 planner/util/fixcontrol/BUILD.bazel delete mode 100644 planner/util/fixcontrol/main_test.go delete mode 100644 planner/util/main_test.go delete mode 100644 planner/util/misc.go delete mode 100644 plugin/BUILD.bazel delete mode 100644 plugin/conn_ip_example/BUILD.bazel delete mode 100644 plugin/conn_ip_example/main_test.go delete mode 100644 plugin/errors.go delete mode 100644 plugin/integration_test.go delete mode 100644 plugin/main_test.go delete mode 100644 privilege/BUILD.bazel delete mode 100644 privilege/conn/BUILD.bazel delete mode 100644 privilege/privileges/BUILD.bazel delete mode 100644 privilege/privileges/cache.go delete mode 100644 privilege/privileges/cache_test.go delete mode 100644 privilege/privileges/errors.go delete mode 100644 privilege/privileges/ldap/BUILD.bazel delete mode 100644 privilege/privileges/main_test.go delete mode 100644 privilege/privileges/privileges_test.go delete mode 100644 resourcemanager/BUILD.bazel delete mode 100644 resourcemanager/pool/BUILD.bazel delete mode 100644 resourcemanager/pool/spool/BUILD.bazel delete mode 100644 resourcemanager/pool/spool/main_test.go delete mode 100644 resourcemanager/pool/workerpool/BUILD.bazel delete mode 100644 resourcemanager/pool/workerpool/main_test.go delete mode 100644 resourcemanager/poolmanager/BUILD.bazel delete mode 100644 resourcemanager/scheduler/BUILD.bazel delete mode 100644 resourcemanager/scheduler/scheduler.go delete mode 100644 resourcemanager/util/BUILD.bazel delete mode 100644 server/BUILD.bazel delete mode 100644 server/err/BUILD.bazel delete mode 100644 server/err/error.go delete mode 100644 server/extension.go delete mode 100644 server/extract.go delete mode 100644 server/handler/BUILD.bazel delete mode 100644 server/handler/extactorhandler/BUILD.bazel delete mode 100644 server/handler/extactorhandler/extract_test.go delete mode 100644 server/handler/extactorhandler/main_test.go delete mode 100644 server/handler/optimizor/BUILD.bazel delete mode 100644 server/handler/optimizor/main_test.go delete mode 100644 server/handler/optimizor/plan_replayer.go delete mode 100644 server/handler/optimizor/plan_replayer_test.go delete mode 100644 server/handler/tests/BUILD.bazel delete mode 100644 server/handler/tests/main_test.go delete mode 100644 server/handler/tikv_handler.go delete mode 100644 server/handler/tikvhandler/BUILD.bazel delete mode 100644 server/handler/tikvhandler/tikv_handler.go delete mode 100644 server/handler/ttlhandler/BUILD.bazel delete mode 100644 server/handler/ttlhandler/ttl.go delete mode 100644 server/handler/util.go delete mode 100644 server/internal/BUILD.bazel delete mode 100644 server/internal/column/BUILD.bazel delete mode 100644 server/internal/column/column.go delete mode 100644 server/internal/column/column_test.go delete mode 100644 server/internal/column/convert.go delete mode 100644 server/internal/dump/BUILD.bazel delete mode 100644 server/internal/dump/dump.go delete mode 100644 server/internal/dump/dump_test.go delete mode 100644 server/internal/handshake/BUILD.bazel delete mode 100644 server/internal/parse/BUILD.bazel delete mode 100644 server/internal/resultset/BUILD.bazel delete mode 100644 server/internal/testserverclient/BUILD.bazel delete mode 100644 server/internal/testutil/BUILD.bazel delete mode 100644 server/internal/util/BUILD.bazel delete mode 100644 server/internal/util/util.go delete mode 100644 server/main_test.go delete mode 100644 server/metrics/BUILD.bazel delete mode 100644 server/metrics/metrics.go delete mode 100644 server/server.go delete mode 100644 server/stat.go delete mode 100644 server/stat_test.go delete mode 100644 server/tests/BUILD.bazel delete mode 100644 server/tests/main_test.go delete mode 100644 server/tests/tidb_test.go delete mode 100644 server/tidb_test.go delete mode 100644 session/BUILD.bazel delete mode 100644 session/bench_test.go delete mode 100644 session/bootstrap.go delete mode 100644 session/bootstrap_test.go delete mode 100644 session/bootstraptest/BUILD.bazel delete mode 100644 session/bootstraptest/main_test.go delete mode 100644 session/clusteredindextest/BUILD.bazel delete mode 100644 session/clusteredindextest/main_test.go delete mode 100644 session/main_test.go delete mode 100644 session/metrics/BUILD.bazel delete mode 100644 session/metrics/metrics.go delete mode 100644 session/nontransactionaltest/BUILD.bazel delete mode 100644 session/nontransactionaltest/main_test.go delete mode 100644 session/resourcegrouptest/BUILD.bazel delete mode 100644 session/resourcegrouptest/resource_group_test.go delete mode 100644 session/schematest/BUILD.bazel delete mode 100644 session/schematest/main_test.go delete mode 100644 session/schematest/schema_test.go delete mode 100644 session/session.go delete mode 100644 session/temporarytabletest/BUILD.bazel delete mode 100644 session/temporarytabletest/main_test.go delete mode 100644 session/temporarytabletest/temporary_table_test.go delete mode 100644 session/test/BUILD.bazel delete mode 100644 session/test/common/BUILD.bazel delete mode 100644 session/test/common/common_test.go delete mode 100644 session/test/common/main_test.go delete mode 100644 session/test/main_test.go delete mode 100644 session/test/meta/BUILD.bazel delete mode 100644 session/test/meta/main_test.go delete mode 100644 session/test/meta/session_test.go delete mode 100644 session/test/privileges/BUILD.bazel delete mode 100644 session/test/privileges/main_test.go delete mode 100644 session/test/privileges/privileges_test.go delete mode 100644 session/test/session_test.go delete mode 100644 session/test/txn/BUILD.bazel delete mode 100644 session/test/txn/main_test.go delete mode 100644 session/test/txn/txn_test.go delete mode 100644 session/test/variable/BUILD.bazel delete mode 100644 session/test/variable/main_test.go delete mode 100644 session/test/variable/variable_test.go delete mode 100644 session/test/vars/BUILD.bazel delete mode 100644 session/test/vars/main_test.go delete mode 100644 session/testutil.go delete mode 100644 session/tidb_test.go delete mode 100644 session/txn.go delete mode 100644 session/txninfo/BUILD.bazel delete mode 100644 session/txninfo/summary.go delete mode 100644 sessionctx/BUILD.bazel delete mode 100644 sessionctx/binloginfo/BUILD.bazel delete mode 100644 sessionctx/binloginfo/main_test.go delete mode 100644 sessionctx/context.go delete mode 100644 sessionctx/main_test.go delete mode 100644 sessionctx/sessionstates/BUILD.bazel delete mode 100644 sessionctx/stmtctx/BUILD.bazel delete mode 100644 sessionctx/stmtctx/main_test.go delete mode 100644 sessionctx/variable/BUILD.bazel delete mode 100644 sessionctx/variable/error.go delete mode 100644 sessionctx/variable/featuretag/disttask/BUILD.bazel delete mode 100644 sessionctx/variable/main_test.go delete mode 100644 sessionctx/variable/session.go delete mode 100644 sessionctx/variable/session_test.go delete mode 100644 sessionctx/variable/sysvar_test.go delete mode 100644 sessionctx/variable/variable.go delete mode 100644 sessionctx/variable/variable_test.go delete mode 100644 sessiontxn/BUILD.bazel delete mode 100644 sessiontxn/failpoint.go delete mode 100644 sessiontxn/interface.go delete mode 100644 sessiontxn/internal/BUILD.bazel delete mode 100644 sessiontxn/internal/txn.go delete mode 100644 sessiontxn/isolation/BUILD.bazel delete mode 100644 sessiontxn/isolation/base.go delete mode 100644 sessiontxn/isolation/main_test.go delete mode 100644 sessiontxn/isolation/metrics/BUILD.bazel delete mode 100644 sessiontxn/isolation/metrics/metrics.go delete mode 100644 sessiontxn/staleread/BUILD.bazel delete mode 100644 sessiontxn/staleread/errors.go delete mode 100644 sessiontxn/staleread/failpoint.go delete mode 100644 sessiontxn/staleread/main_test.go delete mode 100644 sessiontxn/staleread/util.go delete mode 100644 statistics/BUILD.bazel delete mode 100644 statistics/builder.go delete mode 100644 statistics/column.go delete mode 100644 statistics/debugtrace.go delete mode 100644 statistics/handle/BUILD.bazel delete mode 100644 statistics/handle/autoanalyze/BUILD.bazel delete mode 100644 statistics/handle/bootstrap.go delete mode 100644 statistics/handle/cache/BUILD.bazel delete mode 100644 statistics/handle/cache/bench_test.go delete mode 100644 statistics/handle/cache/internal/BUILD.bazel delete mode 100644 statistics/handle/cache/internal/lfu/BUILD.bazel delete mode 100644 statistics/handle/cache/internal/mapcache/BUILD.bazel delete mode 100644 statistics/handle/cache/internal/metrics/BUILD.bazel delete mode 100644 statistics/handle/cache/internal/metrics/metrics.go delete mode 100644 statistics/handle/cache/internal/testutil/BUILD.bazel delete mode 100644 statistics/handle/cache/internal/testutil/testutil.go delete mode 100644 statistics/handle/ddl.go delete mode 100644 statistics/handle/ddl_test.go delete mode 100644 statistics/handle/dump.go delete mode 100644 statistics/handle/dump_test.go delete mode 100644 statistics/handle/extstats/BUILD.bazel delete mode 100644 statistics/handle/globalstats/BUILD.bazel delete mode 100644 statistics/handle/globalstats/topn.go delete mode 100644 statistics/handle/handle.go delete mode 100644 statistics/handle/handletest/BUILD.bazel delete mode 100644 statistics/handle/handletest/analyze/BUILD.bazel delete mode 100644 statistics/handle/handletest/analyze/analyze_test.go delete mode 100644 statistics/handle/handletest/analyze/main_test.go delete mode 100644 statistics/handle/handletest/globalstats/BUILD.bazel delete mode 100644 statistics/handle/handletest/globalstats/main_test.go delete mode 100644 statistics/handle/handletest/handle_test.go delete mode 100644 statistics/handle/handletest/lockstats/BUILD.bazel delete mode 100644 statistics/handle/handletest/lockstats/main_test.go delete mode 100644 statistics/handle/handletest/main_test.go delete mode 100644 statistics/handle/handletest/statstest/BUILD.bazel delete mode 100644 statistics/handle/handletest/statstest/main_test.go delete mode 100644 statistics/handle/handletest/statstest/stats_test.go delete mode 100644 statistics/handle/history/BUILD.bazel delete mode 100644 statistics/handle/internal/BUILD.bazel delete mode 100644 statistics/handle/internal/testutil.go delete mode 100644 statistics/handle/lockstats/BUILD.bazel delete mode 100644 statistics/handle/main_test.go delete mode 100644 statistics/handle/metrics/BUILD.bazel delete mode 100644 statistics/handle/metrics/metrics.go delete mode 100644 statistics/handle/storage/BUILD.bazel delete mode 100644 statistics/handle/storage/update.go delete mode 100644 statistics/handle/update.go delete mode 100644 statistics/handle/updatetest/BUILD.bazel delete mode 100644 statistics/handle/updatetest/main_test.go delete mode 100644 statistics/handle/updatetest/update_test.go delete mode 100644 statistics/handle/usage/BUILD.bazel delete mode 100644 statistics/handle/util/BUILD.bazel delete mode 100644 statistics/handle/util/util.go delete mode 100644 statistics/index.go delete mode 100644 statistics/integration_test.go delete mode 100644 statistics/main_test.go delete mode 100644 statistics/sample.go delete mode 100644 statistics/sample_test.go delete mode 100644 statistics/table.go delete mode 100644 store/BUILD.bazel delete mode 100644 store/batch_coprocessor_test.go delete mode 100644 store/copr/BUILD.bazel delete mode 100644 store/copr/batch_coprocessor_test.go delete mode 100644 store/copr/copr_test/BUILD.bazel delete mode 100644 store/copr/copr_test/coprocessor_test.go delete mode 100644 store/copr/copr_test/main_test.go delete mode 100644 store/copr/coprocessor.go delete mode 100644 store/copr/coprocessor_test.go delete mode 100644 store/copr/main_test.go delete mode 100644 store/copr/metrics/BUILD.bazel delete mode 100644 store/copr/metrics/metrics.go delete mode 100644 store/copr/mpp.go delete mode 100644 store/copr/store.go delete mode 100644 store/driver/BUILD.bazel delete mode 100644 store/driver/backoff/BUILD.bazel delete mode 100644 store/driver/client_test.go delete mode 100644 store/driver/error/BUILD.bazel delete mode 100644 store/driver/error/error.go delete mode 100644 store/driver/error/error_test.go delete mode 100644 store/driver/main_test.go delete mode 100644 store/driver/options/BUILD.bazel delete mode 100644 store/driver/options/options.go delete mode 100644 store/driver/txn/BUILD.bazel delete mode 100644 store/driver/txn/error.go delete mode 100644 store/driver/txn/main_test.go delete mode 100644 store/driver/txn_test.go delete mode 100644 store/gcworker/BUILD.bazel delete mode 100644 store/gcworker/main_test.go delete mode 100644 store/helper/BUILD.bazel delete mode 100644 store/helper/helper.go delete mode 100644 store/helper/helper_test.go delete mode 100644 store/helper/main_test.go delete mode 100644 store/main_test.go delete mode 100644 store/mockstore/BUILD.bazel delete mode 100644 store/mockstore/cluster_test.go delete mode 100644 store/mockstore/main_test.go delete mode 100644 store/mockstore/mockcopr/BUILD.bazel delete mode 100644 store/mockstore/mockcopr/analyze.go delete mode 100644 store/mockstore/mockcopr/executor.go delete mode 100644 store/mockstore/mockcopr/executor_test.go delete mode 100644 store/mockstore/mockcopr/main_test.go delete mode 100644 store/mockstore/mockcopr/topn.go delete mode 100644 store/mockstore/mockstorage/BUILD.bazel delete mode 100644 store/mockstore/mockstorage/storage.go delete mode 100644 store/mockstore/mockstore.go delete mode 100644 store/mockstore/tikv.go delete mode 100644 store/mockstore/unistore/BUILD.bazel delete mode 100644 store/mockstore/unistore/client/BUILD.bazel delete mode 100644 store/mockstore/unistore/cluster.go delete mode 100644 store/mockstore/unistore/config/BUILD.bazel delete mode 100644 store/mockstore/unistore/cophandler/BUILD.bazel delete mode 100644 store/mockstore/unistore/cophandler/analyze.go delete mode 100644 store/mockstore/unistore/cophandler/main_test.go delete mode 100644 store/mockstore/unistore/cophandler/mpp.go delete mode 100644 store/mockstore/unistore/cophandler/topn.go delete mode 100644 store/mockstore/unistore/lockstore/BUILD.bazel delete mode 100644 store/mockstore/unistore/lockstore/main_test.go delete mode 100644 store/mockstore/unistore/main_test.go delete mode 100644 store/mockstore/unistore/metrics/BUILD.bazel delete mode 100644 store/mockstore/unistore/mock.go delete mode 100644 store/mockstore/unistore/pd.go delete mode 100644 store/mockstore/unistore/pd/BUILD.bazel delete mode 100644 store/mockstore/unistore/server/BUILD.bazel delete mode 100644 store/mockstore/unistore/server/server.go delete mode 100644 store/mockstore/unistore/testutil.go delete mode 100644 store/mockstore/unistore/tikv/BUILD.bazel delete mode 100644 store/mockstore/unistore/tikv/dbreader/BUILD.bazel delete mode 100644 store/mockstore/unistore/tikv/kverrors/BUILD.bazel delete mode 100644 store/mockstore/unistore/tikv/kverrors/errors.go delete mode 100644 store/mockstore/unistore/tikv/main_test.go delete mode 100644 store/mockstore/unistore/tikv/mvcc.go delete mode 100644 store/mockstore/unistore/tikv/mvcc/BUILD.bazel delete mode 100644 store/mockstore/unistore/tikv/mvcc/mvcc.go delete mode 100644 store/mockstore/unistore/tikv/mvcc/tikv.go delete mode 100644 store/mockstore/unistore/tikv/pberror/BUILD.bazel delete mode 100644 store/mockstore/unistore/tikv/region.go delete mode 100644 store/mockstore/unistore/tikv/server.go delete mode 100644 store/mockstore/unistore/tikv/write.go delete mode 100644 store/mockstore/unistore/util/lockwaiter/BUILD.bazel delete mode 100644 store/mockstore/unistore/util/lockwaiter/main_test.go delete mode 100644 store/pdtypes/BUILD.bazel delete mode 100644 store/store.go delete mode 100644 store/store_test.go delete mode 100644 structure/BUILD.bazel delete mode 100644 structure/list.go delete mode 100644 structure/main_test.go delete mode 100644 structure/type.go delete mode 100644 table/BUILD.bazel delete mode 100644 table/column.go delete mode 100644 table/column_test.go delete mode 100644 table/constraint.go delete mode 100644 table/index.go delete mode 100644 table/main_test.go delete mode 100644 table/table.go delete mode 100644 table/table_test.go delete mode 100644 table/tables/BUILD.bazel delete mode 100644 table/tables/cache.go delete mode 100644 table/tables/cache_test.go delete mode 100644 table/tables/index.go delete mode 100644 table/tables/index_test.go delete mode 100644 table/tables/main_test.go delete mode 100644 table/tables/partition.go delete mode 100644 table/tables/tables.go delete mode 100644 table/tables/tables_test.go delete mode 100644 table/tables/test/partition/BUILD.bazel delete mode 100644 table/tables/test/partition/main_test.go delete mode 100644 table/tables/test/partition/partition_test.go delete mode 100644 table/tables/testutil.go delete mode 100644 table/temptable/BUILD.bazel delete mode 100644 table/temptable/ddl.go delete mode 100644 table/temptable/ddl_test.go delete mode 100644 table/temptable/infoschema.go delete mode 100644 table/temptable/main_test.go delete mode 100644 tablecodec/BUILD.bazel delete mode 100644 tablecodec/bench_test.go delete mode 100644 tablecodec/main_test.go delete mode 100644 tablecodec/rowindexcodec/BUILD.bazel delete mode 100644 tablecodec/rowindexcodec/main_test.go delete mode 100644 telemetry/BUILD.bazel delete mode 100644 telemetry/cte_test/BUILD.bazel delete mode 100644 telemetry/cte_test/cte_test.go delete mode 100644 telemetry/main_test.go delete mode 100644 telemetry/telemetry.go delete mode 100644 telemetry/ttl.go delete mode 100644 testkit/BUILD.bazel delete mode 100644 testkit/ddlhelper/BUILD.bazel delete mode 100644 testkit/ddlhelper/helper.go delete mode 100644 testkit/external/BUILD.bazel delete mode 100644 testkit/external/util.go delete mode 100644 testkit/mockstore.go delete mode 100644 testkit/testdata/BUILD.bazel delete mode 100644 testkit/testenv/BUILD.bazel delete mode 100644 testkit/testfork/BUILD.bazel delete mode 100644 testkit/testkit.go delete mode 100644 testkit/testmain/BUILD.bazel delete mode 100644 testkit/testsetup/BUILD.bazel delete mode 100644 testkit/testutil/BUILD.bazel delete mode 100644 testkit/testutil/handle.go delete mode 100644 tidb-binlog/driver/reader/BUILD.bazel delete mode 100644 tidb-binlog/driver/reader/reader.go delete mode 100644 tidb-binlog/node/BUILD.bazel delete mode 100644 tidb-binlog/node/registry_test.go delete mode 100644 tidb-binlog/proto/go-binlog/BUILD.bazel delete mode 100644 tidb-binlog/pump_client/BUILD.bazel delete mode 100644 tidb-binlog/pump_client/client.go delete mode 100644 tidb-binlog/pump_client/client_test.go delete mode 100644 tidb-server/BUILD.bazel delete mode 100644 tidb-server/main_test.go delete mode 100644 timer/BUILD.bazel delete mode 100644 timer/api/BUILD.bazel delete mode 100644 timer/api/client.go delete mode 100644 timer/api/main_test.go delete mode 100644 timer/api/store_test.go delete mode 100644 timer/api/timer.go delete mode 100644 timer/main_test.go delete mode 100644 timer/metrics/BUILD.bazel delete mode 100644 timer/runtime/BUILD.bazel delete mode 100644 timer/runtime/cache.go delete mode 100644 timer/runtime/cache_test.go delete mode 100644 timer/runtime/main_test.go delete mode 100644 timer/runtime/worker.go delete mode 100644 timer/tablestore/BUILD.bazel delete mode 100644 timer/tablestore/sql.go delete mode 100644 timer/tablestore/sql_test.go delete mode 100644 timer/tablestore/store.go delete mode 100644 ttl/cache/BUILD.bazel delete mode 100644 ttl/cache/infoschema.go delete mode 100644 ttl/cache/infoschema_test.go delete mode 100644 ttl/cache/main_test.go delete mode 100644 ttl/cache/split_test.go delete mode 100644 ttl/cache/table.go delete mode 100644 ttl/cache/table_test.go delete mode 100644 ttl/cache/task.go delete mode 100644 ttl/client/BUILD.bazel delete mode 100644 ttl/metrics/BUILD.bazel delete mode 100644 ttl/metrics/metrics.go delete mode 100644 ttl/session/BUILD.bazel delete mode 100644 ttl/session/main_test.go delete mode 100644 ttl/session/session.go delete mode 100644 ttl/session/session_test.go delete mode 100644 ttl/session/sysvar_test.go delete mode 100644 ttl/sqlbuilder/BUILD.bazel delete mode 100644 ttl/sqlbuilder/main_test.go delete mode 100644 ttl/sqlbuilder/sql.go delete mode 100644 ttl/sqlbuilder/sql_test.go delete mode 100644 ttl/ttlworker/BUILD.bazel delete mode 100644 ttl/ttlworker/job.go delete mode 100644 ttl/ttlworker/session.go delete mode 100644 ttl/ttlworker/session_test.go delete mode 100644 ttl/ttlworker/timer.go delete mode 100644 ttl/ttlworker/worker.go delete mode 100644 types/BUILD.bazel delete mode 100644 types/benchmark_test.go delete mode 100644 types/compare.go delete mode 100644 types/context.go delete mode 100644 types/context/BUILD.bazel delete mode 100644 types/context/context.go delete mode 100644 types/convert.go delete mode 100644 types/errors.go delete mode 100644 types/errors_test.go delete mode 100644 types/etc.go delete mode 100644 types/etc_test.go delete mode 100644 types/field_type.go delete mode 100644 types/field_type_test.go delete mode 100644 types/format_test.go delete mode 100644 types/main_test.go delete mode 100644 types/parser_driver/BUILD.bazel delete mode 100644 types/parser_driver/main_test.go delete mode 100644 types/set.go delete mode 100644 types/set_test.go delete mode 100644 util/BUILD.bazel delete mode 100644 util/admin/BUILD.bazel delete mode 100644 util/admin/admin.go delete mode 100644 util/admin/main_test.go delete mode 100644 util/arena/BUILD.bazel delete mode 100644 util/arena/main_test.go delete mode 100644 util/backoff/BUILD.bazel delete mode 100644 util/benchdaily/BUILD.bazel delete mode 100644 util/benchdaily/main_test.go delete mode 100644 util/bitmap/BUILD.bazel delete mode 100644 util/bitmap/main_test.go delete mode 100644 util/breakpoint/BUILD.bazel delete mode 100644 util/cgroup/BUILD.bazel delete mode 100644 util/channel/BUILD.bazel delete mode 100644 util/checksum/BUILD.bazel delete mode 100644 util/checksum/checksum.go delete mode 100644 util/checksum/main_test.go delete mode 100644 util/chunk/BUILD.bazel delete mode 100644 util/chunk/codec.go delete mode 100644 util/chunk/codec_test.go delete mode 100644 util/chunk/column.go delete mode 100644 util/chunk/column_test.go delete mode 100644 util/chunk/compare.go delete mode 100644 util/chunk/list.go delete mode 100644 util/chunk/main_test.go delete mode 100644 util/chunk/pool_test.go delete mode 100644 util/codec/BUILD.bazel delete mode 100644 util/codec/bench_test.go delete mode 100644 util/codec/codec.go delete mode 100644 util/codec/codec_test.go delete mode 100644 util/codec/collation_test.go delete mode 100644 util/codec/main_test.go delete mode 100644 util/collate/BUILD.bazel delete mode 100644 util/collate/charset.go delete mode 100644 util/collate/main_test.go delete mode 100644 util/collate/ucadata/BUILD.bazel delete mode 100644 util/collate/ucadata/generator/BUILD.bazel delete mode 100644 util/collate/ucaimpl/BUILD.bazel delete mode 100644 util/column-mapping/BUILD.bazel delete mode 100644 util/column-mapping/column.go delete mode 100644 util/compress/BUILD.bazel delete mode 100644 util/cpu/BUILD.bazel delete mode 100644 util/cpu/cpu.go delete mode 100644 util/cpu/main_test.go delete mode 100644 util/cpuprofile/BUILD.bazel delete mode 100644 util/cpuprofile/testutil/BUILD.bazel delete mode 100644 util/cteutil/BUILD.bazel delete mode 100644 util/cteutil/main_test.go delete mode 100644 util/cteutil/storage.go delete mode 100644 util/dbterror/BUILD.bazel delete mode 100644 util/dbterror/exeerrors/BUILD.bazel delete mode 100644 util/dbterror/exeerrors/errors.go delete mode 100644 util/dbterror/main_test.go delete mode 100644 util/dbterror/terror.go delete mode 100644 util/dbutil/BUILD.bazel delete mode 100644 util/dbutil/common.go delete mode 100644 util/dbutil/common_test.go delete mode 100644 util/dbutil/index.go delete mode 100644 util/dbutil/index_test.go delete mode 100644 util/dbutil/table.go delete mode 100644 util/dbutil/table_test.go delete mode 100644 util/dbutil/types.go delete mode 100644 util/dbutil/variable.go delete mode 100644 util/ddl-checker/BUILD.bazel delete mode 100644 util/deadlockhistory/BUILD.bazel delete mode 100644 util/deadlockhistory/main_test.go delete mode 100644 util/disjointset/BUILD.bazel delete mode 100644 util/disjointset/main_test.go delete mode 100644 util/disk/BUILD.bazel delete mode 100644 util/disk/main_test.go delete mode 100644 util/disk/tracker.go delete mode 100644 util/distrole/BUILD.bazel delete mode 100644 util/disttask/BUILD.bazel delete mode 100644 util/domainutil/BUILD.bazel delete mode 100644 util/encrypt/BUILD.bazel delete mode 100644 util/encrypt/main_test.go delete mode 100644 util/engine/BUILD.bazel delete mode 100644 util/errors_test.go delete mode 100644 util/etcd/BUILD.bazel delete mode 100644 util/execdetails/BUILD.bazel delete mode 100644 util/execdetails/main_test.go delete mode 100644 util/expensivequery/BUILD.bazel delete mode 100644 util/extsort/BUILD.bazel delete mode 100644 util/fastrand/BUILD.bazel delete mode 100644 util/fastrand/main_test.go delete mode 100644 util/filter/BUILD.bazel delete mode 100644 util/format/BUILD.bazel delete mode 100644 util/format/main_test.go delete mode 100644 util/gctuner/BUILD.bazel delete mode 100644 util/gcutil/BUILD.bazel delete mode 100644 util/generatedexpr/BUILD.bazel delete mode 100644 util/generatedexpr/main_test.go delete mode 100644 util/generic/BUILD.bazel delete mode 100644 util/globalconn/BUILD.bazel delete mode 100644 util/globalconn/pool_test.go delete mode 100644 util/hack/BUILD.bazel delete mode 100644 util/hack/main_test.go delete mode 100644 util/hint/BUILD.bazel delete mode 100644 util/importer/BUILD.bazel delete mode 100644 util/importer/config.go delete mode 100644 util/importer/parser.go delete mode 100644 util/intest/BUILD.bazel delete mode 100644 util/israce/BUILD.bazel delete mode 100644 util/keydecoder/BUILD.bazel delete mode 100644 util/keydecoder/main_test.go delete mode 100644 util/kvcache/BUILD.bazel delete mode 100644 util/kvcache/main_test.go delete mode 100644 util/localpool/BUILD.bazel delete mode 100644 util/localpool/main_test.go delete mode 100644 util/logutil/BUILD.bazel delete mode 100644 util/logutil/consistency/BUILD.bazel delete mode 100644 util/logutil/consistency/reporter.go delete mode 100644 util/logutil/main_test.go delete mode 100644 util/main_test.go delete mode 100644 util/mathutil/BUILD.bazel delete mode 100644 util/mathutil/main_test.go delete mode 100644 util/memory/BUILD.bazel delete mode 100644 util/memory/main_test.go delete mode 100644 util/memory/tracker.go delete mode 100644 util/memoryusagealarm/BUILD.bazel delete mode 100644 util/metricsutil/BUILD.bazel delete mode 100644 util/metricsutil/common.go delete mode 100644 util/misc.go delete mode 100644 util/misc_test.go delete mode 100644 util/mock/BUILD.bazel delete mode 100644 util/mock/client.go delete mode 100644 util/mock/context.go delete mode 100644 util/mock/main_test.go delete mode 100644 util/mock/store.go delete mode 100644 util/mvmap/BUILD.bazel delete mode 100644 util/mvmap/main_test.go delete mode 100644 util/nocopy/BUILD.bazel delete mode 100644 util/paging/BUILD.bazel delete mode 100644 util/paging/main_test.go delete mode 100644 util/parser/BUILD.bazel delete mode 100644 util/parser/ast.go delete mode 100644 util/parser/main_test.go delete mode 100644 util/parser/parser_test.go delete mode 100644 util/password-validation/BUILD.bazel delete mode 100644 util/pdapi/BUILD.bazel delete mode 100644 util/plancache/BUILD.bazel delete mode 100644 util/plancache/util.go delete mode 100644 util/plancodec/BUILD.bazel delete mode 100644 util/plancodec/codec.go delete mode 100644 util/plancodec/codec_test.go delete mode 100644 util/plancodec/main_test.go delete mode 100644 util/printer/BUILD.bazel delete mode 100644 util/printer/main_test.go delete mode 100644 util/profile/BUILD.bazel delete mode 100644 util/profile/main_test.go delete mode 100644 util/promutil/BUILD.bazel delete mode 100644 util/ranger/BUILD.bazel delete mode 100644 util/ranger/bench_test.go delete mode 100644 util/ranger/checker.go delete mode 100644 util/ranger/main_test.go delete mode 100644 util/ranger/types.go delete mode 100644 util/regexpr-router/BUILD.bazel delete mode 100644 util/replayer/BUILD.bazel delete mode 100644 util/resourcegrouptag/BUILD.bazel delete mode 100644 util/resourcegrouptag/main_test.go delete mode 100644 util/rowDecoder/BUILD.bazel delete mode 100644 util/rowDecoder/decoder.go delete mode 100644 util/rowDecoder/main_test.go delete mode 100644 util/rowcodec/BUILD.bazel delete mode 100644 util/rowcodec/bench_test.go delete mode 100644 util/rowcodec/common.go delete mode 100644 util/rowcodec/decoder.go delete mode 100644 util/rowcodec/main_test.go delete mode 100644 util/schemacmp/BUILD.bazel delete mode 100644 util/schemacmp/table.go delete mode 100644 util/schemacmp/table_test.go delete mode 100644 util/schemacmp/type.go delete mode 100644 util/schemacmp/util.go delete mode 100644 util/selection/BUILD.bazel delete mode 100644 util/selection/main_test.go delete mode 100644 util/sem/BUILD.bazel delete mode 100644 util/sem/main_test.go delete mode 100644 util/servermemorylimit/BUILD.bazel delete mode 100644 util/set/BUILD.bazel delete mode 100644 util/set/main_test.go delete mode 100644 util/signal/BUILD.bazel delete mode 100644 util/size/BUILD.bazel delete mode 100644 util/skip/BUILD.bazel delete mode 100644 util/sli/BUILD.bazel delete mode 100644 util/slice/BUILD.bazel delete mode 100644 util/slice/main_test.go delete mode 100644 util/sqlexec/BUILD.bazel delete mode 100644 util/sqlexec/main_test.go delete mode 100644 util/sqlexec/mock/BUILD.bazel delete mode 100644 util/stmtsummary/BUILD.bazel delete mode 100644 util/stmtsummary/main_test.go delete mode 100644 util/stmtsummary/reader.go delete mode 100644 util/stmtsummary/v2/BUILD.bazel delete mode 100644 util/stmtsummary/v2/column.go delete mode 100644 util/stmtsummary/v2/column_test.go delete mode 100644 util/stmtsummary/v2/main_test.go delete mode 100644 util/stmtsummary/v2/reader.go delete mode 100644 util/stmtsummary/v2/stmtsummary.go delete mode 100644 util/stmtsummary/v2/tests/BUILD.bazel delete mode 100644 util/stmtsummary/v2/tests/main_test.go delete mode 100644 util/stmtsummary/v2/tests/table_test.go delete mode 100644 util/stringutil/BUILD.bazel delete mode 100644 util/stringutil/main_test.go delete mode 100644 util/syncutil/BUILD.bazel delete mode 100644 util/sys/linux/BUILD.bazel delete mode 100644 util/sys/linux/main_test.go delete mode 100644 util/sys/linux/sys_test.go delete mode 100644 util/sys/storage/BUILD.bazel delete mode 100644 util/sys/storage/main_test.go delete mode 100644 util/sys/storage/sys_test.go delete mode 100644 util/systimemon/BUILD.bazel delete mode 100644 util/systimemon/main_test.go delete mode 100644 util/table-filter/BUILD.bazel delete mode 100644 util/table-router/BUILD.bazel delete mode 100644 util/table-rule-selector/BUILD.bazel delete mode 100644 util/tableutil/BUILD.bazel delete mode 100644 util/texttree/BUILD.bazel delete mode 100644 util/texttree/main_test.go delete mode 100644 util/tiflash/BUILD.bazel delete mode 100644 util/tiflashcompute/BUILD.bazel delete mode 100644 util/tikvutil/BUILD.bazel delete mode 100644 util/timeutil/BUILD.bazel delete mode 100644 util/timeutil/errors.go delete mode 100644 util/timeutil/main_test.go delete mode 100644 util/tls/BUILD.bazel delete mode 100644 util/topsql/BUILD.bazel delete mode 100644 util/topsql/collector/BUILD.bazel delete mode 100644 util/topsql/collector/cpu.go delete mode 100644 util/topsql/collector/main_test.go delete mode 100644 util/topsql/collector/mock/BUILD.bazel delete mode 100644 util/topsql/collector/mock/mock.go delete mode 100644 util/topsql/main_test.go delete mode 100644 util/topsql/reporter/BUILD.bazel delete mode 100644 util/topsql/reporter/main_test.go delete mode 100644 util/topsql/reporter/metrics/BUILD.bazel delete mode 100644 util/topsql/reporter/metrics/metrics.go delete mode 100644 util/topsql/reporter/mock/BUILD.bazel delete mode 100644 util/topsql/reporter/mock/pubsub.go delete mode 100644 util/topsql/reporter/mock/server.go delete mode 100644 util/topsql/reporter/pubsub.go delete mode 100644 util/topsql/reporter/reporter.go delete mode 100644 util/topsql/state/BUILD.bazel delete mode 100644 util/topsql/stmtstats/BUILD.bazel delete mode 100644 util/topsql/stmtstats/main_test.go delete mode 100644 util/tracing/BUILD.bazel delete mode 100644 util/tracing/main_test.go delete mode 100644 util/tracing/util.go delete mode 100644 util/tracing/util_test.go delete mode 100644 util/trxevents/BUILD.bazel delete mode 100644 util/util.go delete mode 100644 util/util_test.go delete mode 100644 util/versioninfo/BUILD.bazel delete mode 100644 util/vitess/BUILD.bazel delete mode 100644 util/vitess/main_test.go delete mode 100644 util/watcher/BUILD.bazel delete mode 100644 util/zeropool/BUILD.bazel delete mode 100644 util/zeropool/pool_test.go diff --git a/.github/licenserc.yml b/.github/licenserc.yml index 66f1458bf632d..e49c1e35a0563 100644 --- a/.github/licenserc.yml +++ b/.github/licenserc.yml @@ -36,10 +36,10 @@ header: - "**/go.sum" - "LICENSE" - ".github/" - - "parser/" + - "pkg/parser/" - "dumpling/" - - "tidb-binlog/driver/example" - - "tidb-binlog/proto/go-binlog/secondary_binlog.pb.go" + - "pkg/tidb-binlog/driver/example" + - "pkg/tidb-binlog/proto/go-binlog/secondary_binlog.pb.go" - "**/*.sql" - "**/*.csv" - "**/*.parquet" @@ -48,6 +48,6 @@ header: - "build/image/.ci_bazel" - "**/OWNERS" - "OWNERS_ALIASES" - - "disttask/**/mock/**/*_mock.go" - - "util/sqlexec/mock/*_mock.go" + - "pkg/disttask/**/mock/**/*_mock.go" + - "pkg/util/sqlexec/mock/*_mock.go" comment: on-failure diff --git a/.gitmodules b/.gitmodules index 1872575856207..1d3a0208fe1ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "extension/enterprise"] - path = extension/enterprise + path = pkg/extension/enterprise url = git@github.com:pingcap-inc/enterprise-extensions.git diff --git a/Makefile b/Makefile index 6d15aaef2fbe5..217a968738a2f 100644 --- a/Makefile +++ b/Makefile @@ -103,16 +103,16 @@ test_part_parser: parser_yacc test_part_parser_dev test_part_parser_dev: parser_fmt parser_unit_test parser: - @cd parser && make parser + @cd pkg/parser && make parser parser_yacc: - @cd parser && mv parser.go parser.go.committed && make parser && diff -u parser.go.committed parser.go && rm parser.go.committed + @cd pkg/parser && mv parser.go parser.go.committed && make parser && diff -u parser.go.committed parser.go && rm parser.go.committed parser_fmt: - @cd parser && make fmt + @cd pkg/parser && make fmt parser_unit_test: - @cd parser && make test + @cd pkg/parser && make test test_part_br: br_unit_test br_integration_test @@ -158,26 +158,26 @@ race: failpoint-enable server: ifeq ($(TARGET), "") - CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server ./tidb-server + CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server ./cmd/tidb-server else - CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./tidb-server + CGO_ENABLED=1 $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./cmd/tidb-server endif server_debug: ifeq ($(TARGET), "") - CGO_ENABLED=1 $(GOBUILD) -gcflags="all=-N -l" $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server-debug ./tidb-server + CGO_ENABLED=1 $(GOBUILD) -gcflags="all=-N -l" $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server-debug ./cmd/tidb-server else - CGO_ENABLED=1 $(GOBUILD) -gcflags="all=-N -l" $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./tidb-server + CGO_ENABLED=1 $(GOBUILD) -gcflags="all=-N -l" $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./cmd/tidb-server endif init-submodule: - git submodule init && git submodule update --force + # git submodule init && git submodule update --force enterprise-prepare: - cd extension/enterprise/generate && $(GO) generate -run genfile main.go + cd pkg/extension/enterprise/generate && $(GO) generate -run genfile main.go enterprise-clear: - cd extension/enterprise/generate && $(GO) generate -run clear main.go + cd pkg/extension/enterprise/generate && $(GO) generate -run clear main.go enterprise-docker: init-submodule enterprise-prepare docker build -t "$(DOCKERPREFIX)tidb:latest" --build-arg 'GOPROXY=$(shell go env GOPROXY),' -f Dockerfile.enterprise . @@ -185,9 +185,9 @@ enterprise-docker: init-submodule enterprise-prepare enterprise-server-build: TIDB_EDITION=Enterprise enterprise-server-build: ifeq ($(TARGET), "") - CGO_ENABLED=1 $(GOBUILD) -tags enterprise $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG) $(EXTENSION_FLAG)' -o bin/tidb-server tidb-server/main.go + CGO_ENABLED=1 $(GOBUILD) -tags enterprise $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG) $(EXTENSION_FLAG)' -o bin/tidb-server cmd/tidb-server/main.go else - CGO_ENABLED=1 $(GOBUILD) -tags enterprise $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG) $(EXTENSION_FLAG)' -o '$(TARGET)' tidb-server/main.go + CGO_ENABLED=1 $(GOBUILD) -tags enterprise $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG) $(EXTENSION_FLAG)' -o '$(TARGET)' cmd/tidb-server/main.go endif enterprise-server: @@ -197,16 +197,16 @@ enterprise-server: server_check: ifeq ($(TARGET), "") - $(GOBUILD) -cover $(RACE_FLAG) -ldflags '$(CHECK_LDFLAGS)' -o bin/tidb-server ./tidb-server + $(GOBUILD) -cover $(RACE_FLAG) -ldflags '$(CHECK_LDFLAGS)' -o bin/tidb-server ./cmd/tidb-server else - $(GOBUILD) -cover $(RACE_FLAG) -ldflags '$(CHECK_LDFLAGS)' -o '$(TARGET)' ./tidb-server + $(GOBUILD) -cover $(RACE_FLAG) -ldflags '$(CHECK_LDFLAGS)' -o '$(TARGET)' ./cmd/tidb-server endif linux: ifeq ($(TARGET), "") - GOOS=linux $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server-linux ./tidb-server + GOOS=linux $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o bin/tidb-server-linux ./cmd/tidb-server else - GOOS=linux $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./tidb-server + GOOS=linux $(GOBUILD) $(RACE_FLAG) -ldflags '$(LDFLAGS) $(CHECK_FLAG)' -o '$(TARGET)' ./cmd/tidb-server endif server_coverage: @@ -506,13 +506,13 @@ bazel_build: bazel $(BAZEL_GLOBAL_CONFIG) build $(BAZEL_CMD_CONFIG) \ //... --//build:with_nogo_flag=true bazel $(BAZEL_GLOBAL_CONFIG) build $(BAZEL_CMD_CONFIG) \ - //cmd/importer:importer //tidb-server:tidb-server //tidb-server:tidb-server-check --//build:with_nogo_flag=true - cp bazel-out/k8-fastbuild/bin/tidb-server/tidb-server_/tidb-server ./bin + //cmd/importer:importer //cmd/tidb-server:tidb-server //cmd/tidb-server:tidb-server-check --//build:with_nogo_flag=true + cp bazel-out/k8-fastbuild/bin/cmd/tidb-server/tidb-server_/tidb-server ./bin cp bazel-out/k8-fastbuild/bin/cmd/importer/importer_/importer ./bin - cp bazel-out/k8-fastbuild/bin/tidb-server/tidb-server-check_/tidb-server-check ./bin + cp bazel-out/k8-fastbuild/bin/cmd/tidb-server/tidb-server-check_/tidb-server-check ./bin bazel $(BAZEL_GLOBAL_CONFIG) build $(BAZEL_CMD_CONFIG) \ - //tidb-server:tidb-server --stamp --workspace_status_command=./build/print-enterprise-workspace-status.sh --define gotags=enterprise - ./bazel-out/k8-fastbuild/bin/tidb-server/tidb-server_/tidb-server -V + //cmd/tidb-server:tidb-server --stamp --workspace_status_command=./build/print-enterprise-workspace-status.sh --define gotags=enterprise + ./bazel-out/k8-fastbuild/bin/cmd/tidb-server/tidb-server_/tidb-server -V bazel_fail_build: failpoint-enable bazel_ci_prepare bazel $(BAZEL_GLOBAL_CONFIG) build $(BAZEL_CMD_CONFIG) \ diff --git a/Makefile.common b/Makefile.common index 5af6fe6134264..8731f1c9a50c0 100644 --- a/Makefile.common +++ b/Makefile.common @@ -60,19 +60,19 @@ UNCONVERT_PACKAGES := $$($(UNCONVERT_PACKAGES_LIST)) FAILPOINT_ENABLE := find $$PWD/ -type d | grep -vE "(\.git|tools)" | xargs tools/bin/failpoint-ctl enable FAILPOINT_DISABLE := find $$PWD/ -type d | grep -vE "(\.git|tools)" | xargs tools/bin/failpoint-ctl disable -LDFLAGS += -X "github.com/pingcap/tidb/parser/mysql.TiDBReleaseVersion=$(shell git describe --tags --dirty --always)" -LDFLAGS += -X "github.com/pingcap/tidb/util/versioninfo.TiDBBuildTS=$(shell date -u '+%Y-%m-%d %H:%M:%S')" -LDFLAGS += -X "github.com/pingcap/tidb/util/versioninfo.TiDBGitHash=$(shell git rev-parse HEAD)" -LDFLAGS += -X "github.com/pingcap/tidb/util/versioninfo.TiDBGitBranch=$(shell git rev-parse --abbrev-ref HEAD)" -LDFLAGS += -X "github.com/pingcap/tidb/util/versioninfo.TiDBEdition=$(TIDB_EDITION)" +LDFLAGS += -X "github.com/pingcap/tidb/pkg/parser/mysql.TiDBReleaseVersion=$(shell git describe --tags --dirty --always)" +LDFLAGS += -X "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBBuildTS=$(shell date -u '+%Y-%m-%d %H:%M:%S')" +LDFLAGS += -X "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitHash=$(shell git rev-parse HEAD)" +LDFLAGS += -X "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitBranch=$(shell git rev-parse --abbrev-ref HEAD)" +LDFLAGS += -X "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBEdition=$(TIDB_EDITION)" EXTENSION_FLAG = ifeq ($(shell if [ -d extension/enterprise/.git ]; then echo "true"; fi),true) - EXTENSION_FLAG += -X "github.com/pingcap/tidb/util/versioninfo.TiDBEnterpriseExtensionGitHash=$(shell cd extension/enterprise && git rev-parse HEAD)" + EXTENSION_FLAG += -X "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBEnterpriseExtensionGitHash=$(shell cd extension/enterprise && git rev-parse HEAD)" endif -TEST_LDFLAGS = -X "github.com/pingcap/tidb/config.checkBeforeDropLDFlag=1" -COVERAGE_SERVER_LDFLAGS = -X "github.com/pingcap/tidb/tidb-server.isCoverageServer=1" +TEST_LDFLAGS = -X "github.com/pingcap/tidb/pkg/config.checkBeforeDropLDFlag=1" +COVERAGE_SERVER_LDFLAGS = -X "github.com/pingcap/tidb/cmd/tidb-server.isCoverageServer=1" CHECK_LDFLAGS += $(LDFLAGS) ${TEST_LDFLAGS} diff --git a/autoid_service/BUILD.bazel b/autoid_service/BUILD.bazel deleted file mode 100644 index 31049e60d97da..0000000000000 --- a/autoid_service/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "autoid_service", - srcs = ["autoid.go"], - importpath = "github.com/pingcap/tidb/autoid_service", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//keyspace", - "//kv", - "//meta", - "//meta/autoid", - "//metrics", - "//owner", - "//parser/model", - "//util/etcd", - "//util/logutil", - "//util/mathutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/autoid", - "@io_etcd_go_etcd_client_v3//:client", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//keepalive", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "autoid_service_test", - timeout = "short", - srcs = ["autoid_test.go"], - embed = [":autoid_service"], - flaky = True, - deps = [ - "//parser/model", - "//testkit", - "@com_github_pingcap_kvproto//pkg/autoid", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - ], -) diff --git a/autoid_service/autoid.go b/autoid_service/autoid.go deleted file mode 100644 index 9f5a34e1385b4..0000000000000 --- a/autoid_service/autoid.go +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package autoid - -import ( - "context" - "crypto/tls" - "math" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/autoid" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - autoid1 "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - clientv3 "go.etcd.io/etcd/client/v3" - "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/keepalive" -) - -var ( - errAutoincReadFailed = errors.New("auto increment action failed") -) - -const ( - autoIDLeaderPath = "tidb/autoid/leader" -) - -type autoIDKey struct { - dbID int64 - tblID int64 -} - -type autoIDValue struct { - sync.Mutex - base int64 - end int64 - isUnsigned bool - token chan struct{} -} - -func (alloc *autoIDValue) alloc4Unsigned(ctx context.Context, store kv.Storage, dbID, tblID int64, isUnsigned bool, - n uint64, increment, offset int64) (min int64, max int64, err error) { - // Check offset rebase if necessary. - if uint64(offset-1) > uint64(alloc.base) { - if err := alloc.rebase4Unsigned(ctx, store, dbID, tblID, uint64(offset-1)); err != nil { - return 0, 0, err - } - } - // calcNeededBatchSize calculates the total batch size needed. - n1 := calcNeededBatchSize(alloc.base, int64(n), increment, offset, isUnsigned) - - // The local rest is not enough for alloc, skip it. - if uint64(alloc.base)+uint64(n1) > uint64(alloc.end) || alloc.base == 0 { - var newBase, newEnd int64 - nextStep := int64(batch) - // Although it may skip a segment here, we still treat it as consumed. - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) - var err1 error - newBase, err1 = idAcc.Get() - if err1 != nil { - return err1 - } - // calcNeededBatchSize calculates the total batch size needed on new base. - n1 = calcNeededBatchSize(newBase, int64(n), increment, offset, isUnsigned) - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep < n1 { - nextStep = n1 - } - tmpStep := int64(mathutil.Min(math.MaxUint64-uint64(newBase), uint64(nextStep))) - // The global rest is not enough for alloc. - if tmpStep < n1 { - return errAutoincReadFailed - } - newEnd, err1 = idAcc.Inc(tmpStep) - return err1 - }) - if err != nil { - return 0, 0, err - } - if uint64(newBase) == math.MaxUint64 { - return 0, 0, errAutoincReadFailed - } - alloc.base, alloc.end = newBase, newEnd - } - min = alloc.base - // Use uint64 n directly. - alloc.base = int64(uint64(alloc.base) + uint64(n1)) - return min, alloc.base, nil -} - -func (alloc *autoIDValue) alloc4Signed(ctx context.Context, - store kv.Storage, - dbID, tblID int64, - isUnsigned bool, - n uint64, increment, offset int64) (min int64, max int64, err error) { - // Check offset rebase if necessary. - if offset-1 > alloc.base { - if err := alloc.rebase4Signed(ctx, store, dbID, tblID, offset-1); err != nil { - return 0, 0, err - } - } - // calcNeededBatchSize calculates the total batch size needed. - n1 := calcNeededBatchSize(alloc.base, int64(n), increment, offset, isUnsigned) - - // Condition alloc.base+N1 > alloc.end will overflow when alloc.base + N1 > MaxInt64. So need this. - if math.MaxInt64-alloc.base <= n1 { - return 0, 0, errAutoincReadFailed - } - - // The local rest is not enough for allocN, skip it. - // If alloc.base is 0, the alloc may not be initialized, force fetch from remote. - if alloc.base+n1 > alloc.end || alloc.base == 0 { - var newBase, newEnd int64 - nextStep := int64(batch) - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) - var err1 error - newBase, err1 = idAcc.Get() - if err1 != nil { - return err1 - } - // calcNeededBatchSize calculates the total batch size needed on global base. - n1 = calcNeededBatchSize(newBase, int64(n), increment, offset, isUnsigned) - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep < n1 { - nextStep = n1 - } - tmpStep := mathutil.Min(math.MaxInt64-newBase, nextStep) - // The global rest is not enough for alloc. - if tmpStep < n1 { - return errAutoincReadFailed - } - newEnd, err1 = idAcc.Inc(tmpStep) - return err1 - }) - if err != nil { - return 0, 0, err - } - if newBase == math.MaxInt64 { - return 0, 0, errAutoincReadFailed - } - alloc.base, alloc.end = newBase, newEnd - } - min = alloc.base - alloc.base += n1 - return min, alloc.base, nil -} - -func (alloc *autoIDValue) rebase4Unsigned(ctx context.Context, - store kv.Storage, - dbID, tblID int64, - requiredBase uint64) error { - // Satisfied by alloc.base, nothing to do. - if requiredBase <= uint64(alloc.base) { - return nil - } - // Satisfied by alloc.end, need to update alloc.base. - if requiredBase <= uint64(alloc.end) { - alloc.base = int64(requiredBase) - return nil - } - - var newBase, newEnd uint64 - var oldValue int64 - startTime := time.Now() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - oldValue = currentEnd - uCurrentEnd := uint64(currentEnd) - newBase = mathutil.Max(uCurrentEnd, requiredBase) - newEnd = mathutil.Min(math.MaxUint64-uint64(batch), newBase) + uint64(batch) - _, err1 = idAcc.Inc(int64(newEnd - uCurrentEnd)) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return err - } - - logutil.BgLogger().Info("rebase4Unsigned from", - zap.String("category", "autoid service"), - zap.Int64("dbID", dbID), - zap.Int64("tblID", tblID), - zap.Int64("from", oldValue), - zap.Uint64("to", newEnd)) - alloc.base, alloc.end = int64(newBase), int64(newEnd) - return nil -} - -func (alloc *autoIDValue) rebase4Signed(ctx context.Context, store kv.Storage, dbID, tblID int64, requiredBase int64) error { - // Satisfied by alloc.base, nothing to do. - if requiredBase <= alloc.base { - return nil - } - // Satisfied by alloc.end, need to update alloc.base. - if requiredBase <= alloc.end { - alloc.base = requiredBase - return nil - } - - var oldValue, newBase, newEnd int64 - startTime := time.Now() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - oldValue = currentEnd - newBase = mathutil.Max(currentEnd, requiredBase) - newEnd = mathutil.Min(math.MaxInt64-batch, newBase) + batch - _, err1 = idAcc.Inc(newEnd - currentEnd) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return err - } - - logutil.BgLogger().Info("rebase4Signed from", - zap.Int64("dbID", dbID), - zap.Int64("tblID", tblID), - zap.Int64("from", oldValue), - zap.Int64("to", newEnd), - zap.String("category", "autoid service")) - alloc.base, alloc.end = newBase, newEnd - return nil -} - -// Service implement the grpc AutoIDAlloc service, defined in kvproto/pkg/autoid. -type Service struct { - autoIDLock sync.Mutex - autoIDMap map[autoIDKey]*autoIDValue - - leaderShip owner.Manager - store kv.Storage -} - -// New return a Service instance. -func New(selfAddr string, etcdAddr []string, store kv.Storage, tlsConfig *tls.Config) *Service { - cfg := config.GetGlobalConfig() - etcdLogCfg := zap.NewProductionConfig() - - cli, err := clientv3.New(clientv3.Config{ - LogConfig: &etcdLogCfg, - Endpoints: etcdAddr, - AutoSyncInterval: 30 * time.Second, - DialTimeout: 5 * time.Second, - DialOptions: []grpc.DialOption{ - grpc.WithBackoffMaxDelay(time.Second * 3), - grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: time.Duration(cfg.TiKVClient.GrpcKeepAliveTime) * time.Second, - Timeout: time.Duration(cfg.TiKVClient.GrpcKeepAliveTimeout) * time.Second, - }), - }, - TLS: tlsConfig, - }) - if store.GetCodec().GetKeyspace() != nil { - etcd.SetEtcdCliByNamespace(cli, keyspace.MakeKeyspaceEtcdNamespaceSlash(store.GetCodec())) - } - if err != nil { - panic(err) - } - return newWithCli(selfAddr, cli, store) -} - -func newWithCli(selfAddr string, cli *clientv3.Client, store kv.Storage) *Service { - l := owner.NewOwnerManager(context.Background(), cli, "autoid", selfAddr, autoIDLeaderPath) - l.SetBeOwnerHook(func() { - logutil.BgLogger().Info("leader change of autoid service, this node become owner", - zap.String("addr", selfAddr), - zap.String("category", "autoid service")) - }) - // 10 means that autoid service's etcd lease is 10s. - err := l.CampaignOwner(10) - if err != nil { - panic(err) - } - - return &Service{ - autoIDMap: make(map[autoIDKey]*autoIDValue), - leaderShip: l, - store: store, - } -} - -type mockClient struct { - Service -} - -func (m *mockClient) AllocAutoID(ctx context.Context, in *autoid.AutoIDRequest, _ ...grpc.CallOption) (*autoid.AutoIDResponse, error) { - return m.Service.AllocAutoID(ctx, in) -} - -func (m *mockClient) Rebase(ctx context.Context, in *autoid.RebaseRequest, _ ...grpc.CallOption) (*autoid.RebaseResponse, error) { - return m.Service.Rebase(ctx, in) -} - -var global = make(map[string]*mockClient) - -// MockForTest is used for testing, the UT test and unistore use this. -func MockForTest(store kv.Storage) autoid.AutoIDAllocClient { - uuid := store.UUID() - ret, ok := global[uuid] - if !ok { - ret = &mockClient{ - Service{ - autoIDMap: make(map[autoIDKey]*autoIDValue), - leaderShip: nil, - store: store, - }, - } - global[uuid] = ret - } - return ret -} - -// Close closes the Service and clean up resource. -func (s *Service) Close() { - if s.leaderShip != nil && s.leaderShip.IsOwner() { - for k, v := range s.autoIDMap { - if v.base > 0 { - err := v.forceRebase(context.Background(), s.store, k.dbID, k.tblID, v.base, v.isUnsigned) - if err != nil { - logutil.BgLogger().Warn("save cached ID fail when service exit", zap.String("category", "autoid service"), - zap.Int64("db id", k.dbID), - zap.Int64("table id", k.tblID), - zap.Int64("value", v.base), - zap.Error(err)) - } - } - } - s.leaderShip.Cancel() - } -} - -// seekToFirstAutoIDSigned seeks to the next valid signed position. -func seekToFirstAutoIDSigned(base, increment, offset int64) int64 { - nr := (base + increment - offset) / increment - nr = nr*increment + offset - return nr -} - -// seekToFirstAutoIDUnSigned seeks to the next valid unsigned position. -func seekToFirstAutoIDUnSigned(base, increment, offset uint64) uint64 { - nr := (base + increment - offset) / increment - nr = nr*increment + offset - return nr -} - -func calcNeededBatchSize(base, n, increment, offset int64, isUnsigned bool) int64 { - if increment == 1 { - return n - } - if isUnsigned { - // SeekToFirstAutoIDUnSigned seeks to the next unsigned valid position. - nr := seekToFirstAutoIDUnSigned(uint64(base), uint64(increment), uint64(offset)) - // calculate the total batch size needed. - nr += (uint64(n) - 1) * uint64(increment) - return int64(nr - uint64(base)) - } - nr := seekToFirstAutoIDSigned(base, increment, offset) - // calculate the total batch size needed. - nr += (n - 1) * increment - return nr - base -} - -const batch = 4000 - -// AllocAutoID implements gRPC AutoIDAlloc interface. -func (s *Service) AllocAutoID(ctx context.Context, req *autoid.AutoIDRequest) (*autoid.AutoIDResponse, error) { - serviceKeyspaceID := uint32(s.store.GetCodec().GetKeyspaceID()) - if req.KeyspaceID != serviceKeyspaceID { - logutil.BgLogger().Info("Current service is not request keyspace leader.", zap.Uint32("req-keyspace-id", req.KeyspaceID), zap.Uint32("service-keyspace-id", serviceKeyspaceID)) - return nil, errors.Trace(errors.New("not leader")) - } - var res *autoid.AutoIDResponse - for { - var err error - res, err = s.allocAutoID(ctx, req) - if err != nil { - return nil, errors.Trace(err) - } - if res != nil { - break - } - } - return res, nil -} - -func (s *Service) getAlloc(dbID, tblID int64, isUnsigned bool) *autoIDValue { - key := autoIDKey{dbID: dbID, tblID: tblID} - s.autoIDLock.Lock() - defer s.autoIDLock.Unlock() - - val, ok := s.autoIDMap[key] - if !ok { - val = &autoIDValue{ - isUnsigned: isUnsigned, - token: make(chan struct{}, 1), - } - s.autoIDMap[key] = val - } - - return val -} - -func (s *Service) allocAutoID(ctx context.Context, req *autoid.AutoIDRequest) (*autoid.AutoIDResponse, error) { - if s.leaderShip != nil && !s.leaderShip.IsOwner() { - logutil.BgLogger().Info("Alloc AutoID fail, not leader", zap.String("category", "autoid service")) - return nil, errors.New("not leader") - } - - failpoint.Inject("mockErr", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(nil, errors.New("mock reload failed")) - } - }) - - val := s.getAlloc(req.DbID, req.TblID, req.IsUnsigned) - - if req.N == 0 { - if val.base != 0 { - return &autoid.AutoIDResponse{ - Min: val.base, - Max: val.base, - }, nil - } - // This item is not initialized, get the data from remote. - var currentEnd int64 - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, s.store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(req.DbID, req.TblID).IncrementID(model.TableInfoVersion5) - var err1 error - currentEnd, err1 = idAcc.Get() - if err1 != nil { - return err1 - } - val.end = currentEnd - return nil - }) - if err != nil { - return &autoid.AutoIDResponse{Errmsg: []byte(err.Error())}, nil - } - return &autoid.AutoIDResponse{ - Min: currentEnd, - Max: currentEnd, - }, nil - } - - val.Lock() - defer val.Unlock() - - var min, max int64 - var err error - if req.IsUnsigned { - min, max, err = val.alloc4Unsigned(ctx, s.store, req.DbID, req.TblID, req.IsUnsigned, req.N, req.Increment, req.Offset) - } else { - min, max, err = val.alloc4Signed(ctx, s.store, req.DbID, req.TblID, req.IsUnsigned, req.N, req.Increment, req.Offset) - } - - if err != nil { - return &autoid.AutoIDResponse{Errmsg: []byte(err.Error())}, nil - } - return &autoid.AutoIDResponse{ - Min: min, - Max: max, - }, nil -} - -func (alloc *autoIDValue) forceRebase(ctx context.Context, store kv.Storage, dbID, tblID, requiredBase int64, isUnsigned bool) error { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - var oldValue int64 - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - oldValue = currentEnd - var step int64 - if !isUnsigned { - step = requiredBase - currentEnd - } else { - uRequiredBase, uCurrentEnd := uint64(requiredBase), uint64(currentEnd) - step = int64(uRequiredBase - uCurrentEnd) - } - _, err1 = idAcc.Inc(step) - return err1 - }) - if err != nil { - return err - } - logutil.BgLogger().Info("forceRebase from", - zap.Int64("dbID", dbID), - zap.Int64("tblID", tblID), - zap.Int64("from", oldValue), - zap.Int64("to", requiredBase), - zap.Bool("isUnsigned", isUnsigned), - zap.String("category", "autoid service")) - alloc.base, alloc.end = requiredBase, requiredBase - return nil -} - -// Rebase implements gRPC AutoIDAlloc interface. -// req.N = 0 is handled specially, it is used to return the current auto ID value. -func (s *Service) Rebase(ctx context.Context, req *autoid.RebaseRequest) (*autoid.RebaseResponse, error) { - if s.leaderShip != nil && !s.leaderShip.IsOwner() { - logutil.BgLogger().Info("Rebase() fail, not leader", zap.String("category", "autoid service")) - return nil, errors.New("not leader") - } - - val := s.getAlloc(req.DbID, req.TblID, req.IsUnsigned) - if req.Force { - err := val.forceRebase(ctx, s.store, req.DbID, req.TblID, req.Base, req.IsUnsigned) - if err != nil { - return &autoid.RebaseResponse{Errmsg: []byte(err.Error())}, nil - } - } - - var err error - if req.IsUnsigned { - err = val.rebase4Unsigned(ctx, s.store, req.DbID, req.TblID, uint64(req.Base)) - } else { - err = val.rebase4Signed(ctx, s.store, req.DbID, req.TblID, req.Base) - } - if err != nil { - return &autoid.RebaseResponse{Errmsg: []byte(err.Error())}, nil - } - return &autoid.RebaseResponse{}, nil -} - -func init() { - autoid1.MockForTest = MockForTest -} diff --git a/autoid_service/autoid_test.go b/autoid_service/autoid_test.go deleted file mode 100644 index c1668816713b1..0000000000000 --- a/autoid_service/autoid_test.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package autoid - -import ( - "context" - "math" - "net" - "testing" - "time" - - "github.com/pingcap/kvproto/pkg/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "go.etcd.io/etcd/tests/v3/integration" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -type autoIDResp struct { - *autoid.AutoIDResponse - error - *testing.T -} - -func (resp autoIDResp) check(min, max int64) { - require.NoError(resp.T, resp.error) - require.Equal(resp.T, resp.AutoIDResponse, &autoid.AutoIDResponse{Min: min, Max: max}) -} - -func (resp autoIDResp) checkErrmsg() { - require.NoError(resp.T, resp.error) - require.True(resp.T, len(resp.GetErrmsg()) > 0) -} - -type rebaseResp struct { - *autoid.RebaseResponse - error - *testing.T -} - -func (resp rebaseResp) check(msg string) { - require.NoError(resp.T, resp.error) - require.Equal(resp.T, string(resp.RebaseResponse.GetErrmsg()), msg) -} - -func TestAPI(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - cli := MockForTest(store) - tk.MustExec("use test") - tk.MustExec("create table t (id int key auto_increment);") - is := dom.InfoSchema() - dbInfo, ok := is.SchemaByName(model.NewCIStr("test")) - require.True(t, ok) - - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tbInfo := tbl.Meta() - - ctx := context.Background() - checkCurrValue := func(t *testing.T, cli autoid.AutoIDAllocClient, min, max int64) { - req := &autoid.AutoIDRequest{DbID: dbInfo.ID, TblID: tbInfo.ID, N: 0, KeyspaceID: uint32(tikv.NullspaceID)} - resp, err := cli.AllocAutoID(ctx, req) - require.NoError(t, err) - require.Equal(t, resp, &autoid.AutoIDResponse{Min: min, Max: max}) - } - autoIDRequest := func(t *testing.T, cli autoid.AutoIDAllocClient, unsigned bool, n uint64, more ...int64) autoIDResp { - increment := int64(1) - offset := int64(1) - if len(more) >= 1 { - increment = more[0] - } - if len(more) >= 2 { - offset = more[1] - } - req := &autoid.AutoIDRequest{DbID: dbInfo.ID, TblID: tbInfo.ID, IsUnsigned: unsigned, N: n, Increment: increment, Offset: offset, KeyspaceID: uint32(tikv.NullspaceID)} - resp, err := cli.AllocAutoID(ctx, req) - return autoIDResp{resp, err, t} - } - rebaseRequest := func(t *testing.T, cli autoid.AutoIDAllocClient, unsigned bool, n int64, force ...struct{}) rebaseResp { - req := &autoid.RebaseRequest{ - DbID: dbInfo.ID, - TblID: tbInfo.ID, - Base: n, - IsUnsigned: unsigned, - Force: len(force) > 0, - } - resp, err := cli.Rebase(ctx, req) - return rebaseResp{resp, err, t} - } - var force = struct{}{} - - // basic auto id operation - autoIDRequest(t, cli, false, 1).check(0, 1) - autoIDRequest(t, cli, false, 10).check(1, 11) - checkCurrValue(t, cli, 11, 11) - autoIDRequest(t, cli, false, 128).check(11, 139) - autoIDRequest(t, cli, false, 1, 10, 5).check(139, 145) - - // basic rebase operation - rebaseRequest(t, cli, false, 666).check("") - autoIDRequest(t, cli, false, 1).check(666, 667) - - rebaseRequest(t, cli, false, 6666).check("") - autoIDRequest(t, cli, false, 1).check(6666, 6667) - - // rebase will not decrease the value without 'force' - rebaseRequest(t, cli, false, 44).check("") - checkCurrValue(t, cli, 6667, 6667) - rebaseRequest(t, cli, false, 44, force).check("") - checkCurrValue(t, cli, 44, 44) - - // max increase 1 - rebaseRequest(t, cli, false, math.MaxInt64, force).check("") - checkCurrValue(t, cli, math.MaxInt64, math.MaxInt64) - autoIDRequest(t, cli, false, 1).checkErrmsg() - - rebaseRequest(t, cli, true, 0, force).check("") - checkCurrValue(t, cli, 0, 0) - autoIDRequest(t, cli, true, 1).check(0, 1) - autoIDRequest(t, cli, true, 10).check(1, 11) - autoIDRequest(t, cli, true, 128).check(11, 139) - autoIDRequest(t, cli, true, 1, 10, 5).check(139, 145) - - // max increase 1 - rebaseRequest(t, cli, true, math.MaxInt64).check("") - checkCurrValue(t, cli, math.MaxInt64, math.MaxInt64) - autoIDRequest(t, cli, true, 1).check(math.MaxInt64, math.MinInt64) - autoIDRequest(t, cli, true, 1).check(math.MinInt64, math.MinInt64+1) - - rebaseRequest(t, cli, true, -1).check("") - checkCurrValue(t, cli, -1, -1) - autoIDRequest(t, cli, true, 1).check(-1, 0) -} - -func TestGRPC(t *testing.T) { - integration.BeforeTestExternal(t) - store := testkit.CreateMockStore(t) - cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - defer cluster.Terminate(t) - etcdCli := cluster.RandClient() - - listener, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer listener.Close() - addr := listener.Addr().String() - - service := newWithCli(addr, etcdCli, store) - defer service.Close() - - var i int - for !service.leaderShip.IsOwner() { - time.Sleep(100 * time.Millisecond) - i++ - if i >= 20 { - break - } - } - require.Less(t, i, 20) - - grpcServer := grpc.NewServer() - autoid.RegisterAutoIDAllocServer(grpcServer, service) - go func() { - grpcServer.Serve(listener) - }() - defer grpcServer.Stop() - - grpcConn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - cli := autoid.NewAutoIDAllocClient(grpcConn) - _, err = cli.AllocAutoID(context.Background(), &autoid.AutoIDRequest{ - DbID: 0, - TblID: 0, - N: 1, - Increment: 1, - Offset: 1, - IsUnsigned: false, - KeyspaceID: uint32(tikv.NullspaceID), - }) - require.NoError(t, err) -} diff --git a/bindinfo/BUILD.bazel b/bindinfo/BUILD.bazel deleted file mode 100644 index fb2b9df90043c..0000000000000 --- a/bindinfo/BUILD.bazel +++ /dev/null @@ -1,83 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "bindinfo", - srcs = [ - "bind_cache.go", - "bind_record.go", - "handle.go", - "session_handle.go", - "stat.go", - ], - importpath = "github.com/pingcap/tidb/bindinfo", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//metrics", - "//parser", - "//parser/ast", - "//parser/format", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//types", - "//types/parser_driver", - "//util/chunk", - "//util/hack", - "//util/hint", - "//util/kvcache", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/parser", - "//util/sqlexec", - "//util/stmtsummary/v2:stmtsummary", - "//util/table-filter", - "//util/timeutil", - "@org_golang_x_exp//maps", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "bindinfo_test", - timeout = "moderate", - srcs = [ - "bind_cache_test.go", - "capture_test.go", - "handle_test.go", - "main_test.go", - "optimize_test.go", - "session_handle_test.go", - "temptable_test.go", - ], - embed = [":bindinfo"], - flaky = True, - race = "on", - shard_count = 42, - deps = [ - "//bindinfo/internal", - "//config", - "//domain", - "//errno", - "//metrics", - "//parser", - "//parser/auth", - "//parser/model", - "//server", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//util/hack", - "//util/parser", - "//util/stmtsummary", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/bindinfo/handle.go b/bindinfo/handle.go deleted file mode 100644 index 7961e2f86b059..0000000000000 --- a/bindinfo/handle.go +++ /dev/null @@ -1,1288 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package bindinfo - -import ( - "context" - "fmt" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/logutil" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/sqlexec" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - tablefilter "github.com/pingcap/tidb/util/table-filter" - "github.com/pingcap/tidb/util/timeutil" - "go.uber.org/zap" - "golang.org/x/exp/maps" -) - -// BindHandle is used to handle all global sql bind operations. -type BindHandle struct { - sctx struct { - sync.Mutex - sessionctx.Context - } - - // bindInfo caches the sql bind info from storage. - // - // The Mutex protects that there is only one goroutine changes the content - // of atomic.Value. - // - // NOTE: Concurrent Value Write: - // - // bindInfo.Lock() - // newCache := bindInfo.Value.Load() - // do the write operation on the newCache - // bindInfo.Value.Store(newCache) - // bindInfo.Unlock() - // - // NOTE: Concurrent Value Read: - // - // cache := bindInfo.Load(). - // read the content - // - bindInfo struct { - sync.Mutex - atomic.Value - parser *parser.Parser - lastUpdateTime types.Time - } - - // invalidBindRecordMap indicates the invalid bind records found during querying. - // A record will be deleted from this map, after 2 bind-lease, after it is dropped from the kv. - invalidBindRecordMap tmpBindRecordMap - - // pendingVerifyBindRecordMap indicates the pending verify bind records that found during query. - pendingVerifyBindRecordMap tmpBindRecordMap -} - -// Lease influences the duration of loading bind info and handling invalid bind. -var Lease = 3 * time.Second - -const ( - // OwnerKey is the bindinfo owner path that is saved to etcd. - OwnerKey = "/tidb/bindinfo/owner" - // Prompt is the prompt for bindinfo owner manager. - Prompt = "bindinfo" - // BuiltinPseudoSQL4BindLock is used to simulate LOCK TABLE for mysql.bind_info. - BuiltinPseudoSQL4BindLock = "builtin_pseudo_sql_for_bind_lock" -) - -type bindRecordUpdate struct { - bindRecord *BindRecord - updateTime time.Time -} - -// NewBindHandle creates a new BindHandle. -func NewBindHandle(ctx sessionctx.Context) *BindHandle { - handle := &BindHandle{} - handle.Reset(ctx) - return handle -} - -// Reset is to reset the BindHandle and clean old info. -func (h *BindHandle) Reset(ctx sessionctx.Context) { - h.bindInfo.Lock() - defer h.bindInfo.Unlock() - h.sctx.Context = ctx - h.bindInfo.Value.Store(newBindCache()) - h.bindInfo.parser = parser.New() - h.invalidBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate)) - h.invalidBindRecordMap.flushFunc = func(record *BindRecord) error { - _, err := h.DropBindRecord(record.OriginalSQL, record.Db, &record.Bindings[0]) - return err - } - h.pendingVerifyBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate)) - h.pendingVerifyBindRecordMap.flushFunc = func(record *BindRecord) error { - // BindSQL has already been validated when coming here, so we use nil sctx parameter. - return h.AddBindRecord(nil, record) - } - variable.RegisterStatistics(h) -} - -// Update updates the global sql bind cache. -func (h *BindHandle) Update(fullLoad bool) (err error) { - h.bindInfo.Lock() - lastUpdateTime := h.bindInfo.lastUpdateTime - var timeCondition string - if !fullLoad { - timeCondition = fmt.Sprintf("WHERE update_time>'%s'", lastUpdateTime.String()) - } - - exec := h.sctx.Context.(sqlexec.RestrictedSQLExecutor) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - // No need to acquire the session context lock for ExecRestrictedSQL, it - // uses another background session. - selectStmt := fmt.Sprintf(`SELECT original_sql, bind_sql, default_db, status, create_time, - update_time, charset, collation, source, sql_digest, plan_digest FROM mysql.bind_info - %s ORDER BY update_time, create_time`, timeCondition) - rows, _, err := exec.ExecRestrictedSQL(ctx, nil, selectStmt) - - if err != nil { - h.bindInfo.Unlock() - return err - } - - newCache, memExceededErr := h.bindInfo.Value.Load().(*bindCache).Copy() - defer func() { - h.bindInfo.lastUpdateTime = lastUpdateTime - h.bindInfo.Value.Store(newCache) - h.bindInfo.Unlock() - }() - - for _, row := range rows { - // If the memory usage of the binding_cache exceeds its capacity, we will break and do not handle. - if memExceededErr != nil { - break - } - // Skip the builtin record which is designed for binding synchronization. - if row.GetString(0) == BuiltinPseudoSQL4BindLock { - continue - } - hash, meta, err := h.newBindRecord(row) - - // Update lastUpdateTime to the newest one. - // Even if this one is an invalid bind. - if meta.Bindings[0].UpdateTime.Compare(lastUpdateTime) > 0 { - lastUpdateTime = meta.Bindings[0].UpdateTime - } - - if err != nil { - logutil.BgLogger().Debug("failed to generate bind record from data row", zap.String("category", "sql-bind"), zap.Error(err)) - continue - } - - oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) - newRecord := merge(oldRecord, meta).removeDeletedBindings() - if len(newRecord.Bindings) > 0 { - err = newCache.SetBindRecord(hash, newRecord) - if err != nil { - memExceededErr = err - } - } else { - newCache.RemoveBindRecord(hash, newRecord) - } - updateMetrics(metrics.ScopeGlobal, oldRecord, newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db), true) - } - if memExceededErr != nil { - // When the memory capacity of bing_cache is not enough, - // there will be some memory-related errors in multiple places. - // Only needs to be handled once. - logutil.BgLogger().Warn("BindHandle.Update", zap.String("category", "sql-bind"), zap.Error(memExceededErr)) - } - return nil -} - -// CreateBindRecord creates a BindRecord to the storage and the cache. -// It replaces all the exists bindings for the same normalized SQL. -func (h *BindHandle) CreateBindRecord(sctx sessionctx.Context, record *BindRecord) (err error) { - err = record.prepareHints(sctx) - if err != nil { - return err - } - - record.Db = strings.ToLower(record.Db) - h.bindInfo.Lock() - h.sctx.Lock() - defer func() { - h.sctx.Unlock() - h.bindInfo.Unlock() - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - if err != nil { - return - } - - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - - sqlDigest := parser.DigestNormalized(record.OriginalSQL) - h.setBindRecord(sqlDigest.String(), record) - }() - - // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. - if err = h.lockBindInfoTable(); err != nil { - return err - } - - now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) - - updateTs := now.String() - _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %?`, - deleted, updateTs, record.OriginalSQL, updateTs) - if err != nil { - return err - } - - for i := range record.Bindings { - record.Bindings[i].CreateTime = now - record.Bindings[i].UpdateTime = now - - // Insert the BindRecord to the storage. - _, err = exec.ExecuteInternal(ctx, `INSERT INTO mysql.bind_info VALUES (%?,%?, %?, %?, %?, %?, %?, %?, %?, %?, %?)`, - record.OriginalSQL, - record.Bindings[i].BindSQL, - record.Db, - record.Bindings[i].Status, - record.Bindings[i].CreateTime.String(), - record.Bindings[i].UpdateTime.String(), - record.Bindings[i].Charset, - record.Bindings[i].Collation, - record.Bindings[i].Source, - record.Bindings[i].SQLDigest, - record.Bindings[i].PlanDigest, - ) - if err != nil { - return err - } - } - return nil -} - -// AddBindRecord adds a BindRecord to the storage and BindRecord to the cache. -func (h *BindHandle) AddBindRecord(sctx sessionctx.Context, record *BindRecord) (err error) { - err = record.prepareHints(sctx) - if err != nil { - return err - } - - record.Db = strings.ToLower(record.Db) - oldRecord := h.GetBindRecord(parser.DigestNormalized(record.OriginalSQL).String(), record.OriginalSQL, record.Db) - var duplicateBinding *Binding - if oldRecord != nil { - binding := oldRecord.FindBinding(record.Bindings[0].ID) - if binding != nil { - // There is already a binding with status `Enabled`, `Disabled`, `PendingVerify` or `Rejected`, we could directly cancel the job. - if record.Bindings[0].Status == PendingVerify { - return nil - } - // Otherwise, we need to remove it before insert. - duplicateBinding = binding - } - } - - h.bindInfo.Lock() - h.sctx.Lock() - defer func() { - h.sctx.Unlock() - h.bindInfo.Unlock() - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - if err != nil { - return - } - - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - - h.appendBindRecord(parser.DigestNormalized(record.OriginalSQL).String(), record) - }() - - // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. - if err = h.lockBindInfoTable(); err != nil { - return err - } - if duplicateBinding != nil { - _, err = exec.ExecuteInternal(ctx, `DELETE FROM mysql.bind_info WHERE original_sql = %? AND bind_sql = %?`, record.OriginalSQL, duplicateBinding.BindSQL) - if err != nil { - return err - } - } - - now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) - for i := range record.Bindings { - if duplicateBinding != nil { - record.Bindings[i].CreateTime = duplicateBinding.CreateTime - } else { - record.Bindings[i].CreateTime = now - } - record.Bindings[i].UpdateTime = now - - if record.Bindings[i].SQLDigest == "" { - parser4binding := parser.New() - var originNode ast.StmtNode - originNode, err = parser4binding.ParseOneStmt(record.OriginalSQL, record.Bindings[i].Charset, record.Bindings[i].Collation) - if err != nil { - return err - } - _, sqlDigestWithDB := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(originNode, record.Db, record.OriginalSQL)) - record.Bindings[i].SQLDigest = sqlDigestWithDB.String() - } - // Insert the BindRecord to the storage. - _, err = exec.ExecuteInternal(ctx, `INSERT INTO mysql.bind_info VALUES (%?, %?, %?, %?, %?, %?, %?, %?, %?, %?, %?)`, - record.OriginalSQL, - record.Bindings[i].BindSQL, - record.Db, - record.Bindings[i].Status, - record.Bindings[i].CreateTime.String(), - record.Bindings[i].UpdateTime.String(), - record.Bindings[i].Charset, - record.Bindings[i].Collation, - record.Bindings[i].Source, - record.Bindings[i].SQLDigest, - record.Bindings[i].PlanDigest, - ) - if err != nil { - return err - } - } - return nil -} - -// DropBindRecord drops a BindRecord to the storage and BindRecord int the cache. -func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (deletedRows uint64, err error) { - db = strings.ToLower(db) - h.bindInfo.Lock() - h.sctx.Lock() - defer func() { - h.sctx.Unlock() - h.bindInfo.Unlock() - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - if err != nil { - return 0, err - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil || deletedRows == 0 { - return - } - - record := &BindRecord{OriginalSQL: originalSQL, Db: db} - if binding != nil { - record.Bindings = append(record.Bindings, *binding) - } - h.removeBindRecord(parser.DigestNormalized(originalSQL).String(), record) - }() - - // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. - if err = h.lockBindInfoTable(); err != nil { - return 0, err - } - - updateTs := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3).String() - - if binding == nil { - _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND status != %?`, - deleted, updateTs, originalSQL, updateTs, deleted) - } else { - _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND bind_sql = %? and status != %?`, - deleted, updateTs, originalSQL, updateTs, binding.BindSQL, deleted) - } - if err != nil { - return 0, err - } - - return h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows(), nil -} - -// DropBindRecordByDigest drop BindRecord to the storage and BindRecord int the cache. -func (h *BindHandle) DropBindRecordByDigest(sqlDigest string) (deletedRows uint64, err error) { - oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest) - if err != nil { - return 0, err - } - return h.DropBindRecord(oldRecord.OriginalSQL, strings.ToLower(oldRecord.Db), nil) -} - -// SetBindRecordStatus set a BindRecord's status to the storage and bind cache. -func (h *BindHandle) SetBindRecordStatus(originalSQL string, binding *Binding, newStatus string) (ok bool, err error) { - h.bindInfo.Lock() - h.sctx.Lock() - defer func() { - h.sctx.Unlock() - h.bindInfo.Unlock() - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - if err != nil { - return - } - var ( - updateTs types.Time - oldStatus0, oldStatus1 string - affectRows int - ) - if newStatus == Disabled { - // For compatibility reasons, when we need to 'set binding disabled for ', - // we need to consider both the 'enabled' and 'using' status. - oldStatus0 = Using - oldStatus1 = Enabled - } else if newStatus == Enabled { - // In order to unify the code, two identical old statuses are set. - oldStatus0 = Disabled - oldStatus1 = Disabled - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - if affectRows == 0 { - return - } - - // The set binding status operation is success. - ok = true - record := &BindRecord{OriginalSQL: originalSQL} - sqlDigest := parser.DigestNormalized(record.OriginalSQL) - oldRecord := h.GetBindRecord(sqlDigest.String(), originalSQL, "") - setBindingStatusInCacheSucc := false - if oldRecord != nil && len(oldRecord.Bindings) > 0 { - record.Bindings = make([]Binding, len(oldRecord.Bindings)) - copy(record.Bindings, oldRecord.Bindings) - for ind, oldBinding := range record.Bindings { - if oldBinding.Status == oldStatus0 || oldBinding.Status == oldStatus1 { - if binding == nil || (binding != nil && oldBinding.isSame(binding)) { - setBindingStatusInCacheSucc = true - record.Bindings[ind].Status = newStatus - record.Bindings[ind].UpdateTime = updateTs - } - } - } - } - if setBindingStatusInCacheSucc { - h.setBindRecord(sqlDigest.String(), record) - } - }() - - // Lock mysql.bind_info to synchronize with SetBindingStatus on other tidb instances. - if err = h.lockBindInfoTable(); err != nil { - return - } - - updateTs = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) - updateTsStr := updateTs.String() - - if binding == nil { - _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND status IN (%?, %?)`, - newStatus, updateTsStr, originalSQL, updateTsStr, oldStatus0, oldStatus1) - } else { - _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND bind_sql = %? AND status IN (%?, %?)`, - newStatus, updateTsStr, originalSQL, updateTsStr, binding.BindSQL, oldStatus0, oldStatus1) - } - affectRows = int(h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows()) - return -} - -// SetBindRecordStatusByDigest set a BindRecord's status to the storage and bind cache. -func (h *BindHandle) SetBindRecordStatusByDigest(newStatus, sqlDigest string) (ok bool, err error) { - oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest) - if err != nil { - return false, err - } - return h.SetBindRecordStatus(oldRecord.OriginalSQL, nil, newStatus) -} - -// GCBindRecord physically removes the deleted bind records in mysql.bind_info. -func (h *BindHandle) GCBindRecord() (err error) { - h.bindInfo.Lock() - h.sctx.Lock() - defer func() { - h.sctx.Unlock() - h.bindInfo.Unlock() - }() - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - if err != nil { - return err - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - }() - - // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. - if err = h.lockBindInfoTable(); err != nil { - return err - } - - // To make sure that all the deleted bind records have been acknowledged to all tidb, - // we only garbage collect those records with update_time before 10 leases. - updateTime := time.Now().Add(-(10 * Lease)) - updateTimeStr := types.NewTime(types.FromGoTime(updateTime), mysql.TypeTimestamp, 3).String() - _, err = exec.ExecuteInternal(ctx, `DELETE FROM mysql.bind_info WHERE status = 'deleted' and update_time < %?`, updateTimeStr) - return err -} - -// lockBindInfoTable simulates `LOCK TABLE mysql.bind_info WRITE` by acquiring a pessimistic lock on a -// special builtin row of mysql.bind_info. Note that this function must be called with h.sctx.Lock() held. -// We can replace this implementation to normal `LOCK TABLE mysql.bind_info WRITE` if that feature is -// generally available later. -// This lock would enforce the CREATE / DROP GLOBAL BINDING statements to be executed sequentially, -// even if they come from different tidb instances. -func (h *BindHandle) lockBindInfoTable() error { - // h.sctx already locked. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) - _, err := exec.ExecuteInternal(ctx, h.LockBindInfoSQL()) - return err -} - -// LockBindInfoSQL simulates LOCK TABLE by updating a same row in each pessimistic transaction. -func (*BindHandle) LockBindInfoSQL() string { - sql, err := sqlexec.EscapeSQL("UPDATE mysql.bind_info SET source= %? WHERE original_sql= %?", Builtin, BuiltinPseudoSQL4BindLock) - if err != nil { - return "" - } - return sql -} - -// tmpBindRecordMap is used to temporarily save bind record changes. -// Those changes will be flushed into store periodically. -type tmpBindRecordMap struct { - sync.Mutex - atomic.Value - flushFunc func(record *BindRecord) error -} - -// flushToStore calls flushFunc for items in tmpBindRecordMap and removes them with a delay. -func (tmpMap *tmpBindRecordMap) flushToStore() { - tmpMap.Lock() - defer tmpMap.Unlock() - newMap := copyBindRecordUpdateMap(tmpMap.Load().(map[string]*bindRecordUpdate)) - for key, bindRecord := range newMap { - if bindRecord.updateTime.IsZero() { - err := tmpMap.flushFunc(bindRecord.bindRecord) - if err != nil { - logutil.BgLogger().Debug("flush bind record failed", zap.String("category", "sql-bind"), zap.Error(err)) - } - bindRecord.updateTime = time.Now() - continue - } - - if time.Since(bindRecord.updateTime) > 6*time.Second { - delete(newMap, key) - updateMetrics(metrics.ScopeGlobal, bindRecord.bindRecord, nil, false) - } - } - tmpMap.Store(newMap) -} - -// Add puts a BindRecord into tmpBindRecordMap. -func (tmpMap *tmpBindRecordMap) Add(bindRecord *BindRecord) { - key := bindRecord.OriginalSQL + ":" + bindRecord.Db + ":" + bindRecord.Bindings[0].ID - if _, ok := tmpMap.Load().(map[string]*bindRecordUpdate)[key]; ok { - return - } - tmpMap.Lock() - defer tmpMap.Unlock() - if _, ok := tmpMap.Load().(map[string]*bindRecordUpdate)[key]; ok { - return - } - newMap := copyBindRecordUpdateMap(tmpMap.Load().(map[string]*bindRecordUpdate)) - newMap[key] = &bindRecordUpdate{ - bindRecord: bindRecord, - } - tmpMap.Store(newMap) - updateMetrics(metrics.ScopeGlobal, nil, bindRecord, false) -} - -// DropInvalidBindRecord executes the drop BindRecord tasks. -func (h *BindHandle) DropInvalidBindRecord() { - h.invalidBindRecordMap.flushToStore() -} - -// AddDropInvalidBindTask adds BindRecord which needs to be deleted into invalidBindRecordMap. -func (h *BindHandle) AddDropInvalidBindTask(invalidBindRecord *BindRecord) { - h.invalidBindRecordMap.Add(invalidBindRecord) -} - -// Size returns the size of bind info cache. -func (h *BindHandle) Size() int { - size := len(h.bindInfo.Load().(*bindCache).GetAllBindRecords()) - return size -} - -// GetBindRecord returns the BindRecord of the (normdOrigSQL,db) if BindRecord exist. -func (h *BindHandle) GetBindRecord(hash, normdOrigSQL, db string) *BindRecord { - return h.bindInfo.Load().(*bindCache).GetBindRecord(hash, normdOrigSQL, db) -} - -// GetBindRecordBySQLDigest returns the BindRecord of the sql digest. -func (h *BindHandle) GetBindRecordBySQLDigest(sqlDigest string) (*BindRecord, error) { - return h.bindInfo.Load().(*bindCache).GetBindRecordBySQLDigest(sqlDigest) -} - -// GetAllBindRecord returns all bind records in cache. -func (h *BindHandle) GetAllBindRecord() (bindRecords []*BindRecord) { - return h.bindInfo.Load().(*bindCache).GetAllBindRecords() -} - -// SetBindCacheCapacity reset the capacity for the bindCache. -// It will not affect already cached BindRecords. -func (h *BindHandle) SetBindCacheCapacity(capacity int64) { - h.bindInfo.Load().(*bindCache).SetMemCapacity(capacity) -} - -// GetMemUsage returns the memory usage for the bind cache. -func (h *BindHandle) GetMemUsage() (memUsage int64) { - return h.bindInfo.Load().(*bindCache).GetMemUsage() -} - -// GetMemCapacity returns the memory capacity for the bind cache. -func (h *BindHandle) GetMemCapacity() (memCapacity int64) { - return h.bindInfo.Load().(*bindCache).GetMemCapacity() -} - -// newBindRecord builds BindRecord from a tuple in storage. -func (h *BindHandle) newBindRecord(row chunk.Row) (string, *BindRecord, error) { - status := row.GetString(3) - // For compatibility, the 'Using' status binding will be converted to the 'Enabled' status binding. - if status == Using { - status = Enabled - } - hint := Binding{ - BindSQL: row.GetString(1), - Status: status, - CreateTime: row.GetTime(4), - UpdateTime: row.GetTime(5), - Charset: row.GetString(6), - Collation: row.GetString(7), - Source: row.GetString(8), - SQLDigest: row.GetString(9), - PlanDigest: row.GetString(10), - } - bindRecord := &BindRecord{ - OriginalSQL: row.GetString(0), - Db: strings.ToLower(row.GetString(2)), - Bindings: []Binding{hint}, - } - hash := parser.DigestNormalized(bindRecord.OriginalSQL) - h.sctx.Lock() - defer h.sctx.Unlock() - h.sctx.GetSessionVars().CurrentDB = bindRecord.Db - err := bindRecord.prepareHints(h.sctx.Context) - return hash.String(), bindRecord, err -} - -// setBindRecord sets the BindRecord to the cache, if there already exists a BindRecord, -// it will be overridden. -func (h *BindHandle) setBindRecord(hash string, meta *BindRecord) { - newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() - if err0 != nil { - logutil.BgLogger().Warn("BindHandle.setBindRecord", zap.String("category", "sql-bind"), zap.Error(err0)) - } - oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) - err1 := newCache.SetBindRecord(hash, meta) - if err1 != nil && err0 == nil { - logutil.BgLogger().Warn("BindHandle.setBindRecord", zap.String("category", "sql-bind"), zap.Error(err1)) - } - h.bindInfo.Value.Store(newCache) - updateMetrics(metrics.ScopeGlobal, oldRecord, meta, false) -} - -// appendBindRecord adds the BindRecord to the cache, all the stale BindRecords are -// removed from the cache after this operation. -func (h *BindHandle) appendBindRecord(hash string, meta *BindRecord) { - newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() - if err0 != nil { - logutil.BgLogger().Warn("BindHandle.appendBindRecord", zap.String("category", "sql-bind"), zap.Error(err0)) - } - oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) - newRecord := merge(oldRecord, meta) - err1 := newCache.SetBindRecord(hash, newRecord) - if err1 != nil && err0 == nil { - // Only need to handle the error once. - logutil.BgLogger().Warn("BindHandle.appendBindRecord", zap.String("category", "sql-bind"), zap.Error(err1)) - } - h.bindInfo.Value.Store(newCache) - updateMetrics(metrics.ScopeGlobal, oldRecord, newRecord, false) -} - -// removeBindRecord removes the BindRecord from the cache. -func (h *BindHandle) removeBindRecord(hash string, meta *BindRecord) { - newCache, err := h.bindInfo.Value.Load().(*bindCache).Copy() - if err != nil { - logutil.BgLogger().Warn("", zap.String("category", "sql-bind"), zap.Error(err)) - } - oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) - newCache.RemoveBindRecord(hash, meta) - h.bindInfo.Value.Store(newCache) - updateMetrics(metrics.ScopeGlobal, oldRecord, newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db), false) -} - -func copyBindRecordUpdateMap(oldMap map[string]*bindRecordUpdate) map[string]*bindRecordUpdate { - newMap := make(map[string]*bindRecordUpdate, len(oldMap)) - maps.Copy(newMap, oldMap) - return newMap -} - -type captureFilter struct { - frequency int64 - tables []tablefilter.Filter // `schema.table` - users map[string]struct{} - - fail bool - currentDB string -} - -func (cf *captureFilter) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - if x, ok := in.(*ast.TableName); ok { - tblEntry := stmtctx.TableEntry{ - DB: x.Schema.L, - Table: x.Name.L, - } - if x.Schema.L == "" { - tblEntry.DB = cf.currentDB - } - for _, tableFilter := range cf.tables { - if tableFilter.MatchTable(tblEntry.DB, tblEntry.Table) { - cf.fail = true // some filter is matched - } - } - } - return in, cf.fail -} - -func (*captureFilter) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, true -} - -func (cf *captureFilter) isEmpty() bool { - return len(cf.tables) == 0 && len(cf.users) == 0 -} - -// ParseCaptureTableFilter checks whether this filter is valid and parses it. -func ParseCaptureTableFilter(tableFilter string) (f tablefilter.Filter, valid bool) { - // forbid wildcards '!' and '@' for safety, - // please see https://github.com/pingcap/tidb-tools/tree/master/pkg/table-filter for more details. - tableFilter = strings.TrimLeft(tableFilter, " \t") - if tableFilter == "" { - return nil, false - } - if tableFilter[0] == '!' || tableFilter[0] == '@' { - return nil, false - } - var err error - f, err = tablefilter.Parse([]string{tableFilter}) - if err != nil { - return nil, false - } - return f, true -} - -func (h *BindHandle) extractCaptureFilterFromStorage() (filter *captureFilter) { - filter = &captureFilter{ - frequency: 1, - users: make(map[string]struct{}), - } - exec := h.sctx.Context.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - // No need to acquire the session context lock for ExecRestrictedSQL, it - // uses another background session. - rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT filter_type, filter_value FROM mysql.capture_plan_baselines_blacklist order by filter_type`) - if err != nil { - logutil.BgLogger().Warn("failed to load mysql.capture_plan_baselines_blacklist", zap.String("category", "sql-bind"), zap.Error(err)) - return - } - for _, row := range rows { - filterTp := strings.ToLower(row.GetString(0)) - valStr := strings.ToLower(row.GetString(1)) - switch filterTp { - case "table": - tfilter, valid := ParseCaptureTableFilter(valStr) - if !valid { - logutil.BgLogger().Warn("capture table filter is invalid, ignore it", zap.String("category", "sql-bind"), zap.String("filter_value", valStr)) - continue - } - filter.tables = append(filter.tables, tfilter) - case "user": - filter.users[valStr] = struct{}{} - case "frequency": - f, err := strconv.ParseInt(valStr, 10, 64) - if err != nil { - logutil.BgLogger().Warn("failed to parse frequency type value, ignore it", zap.String("category", "sql-bind"), zap.String("filter_value", valStr), zap.Error(err)) - continue - } - if f < 1 { - logutil.BgLogger().Warn("frequency threshold is less than 1, ignore it", zap.String("category", "sql-bind"), zap.Int64("frequency", f)) - continue - } - if f > filter.frequency { - filter.frequency = f - } - default: - logutil.BgLogger().Warn("unknown capture filter type, ignore it", zap.String("category", "sql-bind"), zap.String("filter_type", filterTp)) - } - } - return -} - -// CaptureBaselines is used to automatically capture plan baselines. -func (h *BindHandle) CaptureBaselines() { - parser4Capture := parser.New() - captureFilter := h.extractCaptureFilterFromStorage() - emptyCaptureFilter := captureFilter.isEmpty() - bindableStmts := stmtsummaryv2.GetMoreThanCntBindableStmt(captureFilter.frequency) - for _, bindableStmt := range bindableStmts { - stmt, err := parser4Capture.ParseOneStmt(bindableStmt.Query, bindableStmt.Charset, bindableStmt.Collation) - if err != nil { - logutil.BgLogger().Debug("parse SQL failed in baseline capture", zap.String("category", "sql-bind"), zap.String("SQL", bindableStmt.Query), zap.Error(err)) - continue - } - if insertStmt, ok := stmt.(*ast.InsertStmt); ok && insertStmt.Select == nil { - continue - } - if !emptyCaptureFilter { - captureFilter.fail = false - captureFilter.currentDB = bindableStmt.Schema - stmt.Accept(captureFilter) - if captureFilter.fail { - continue - } - - if len(captureFilter.users) > 0 { - filteredByUser := true - for user := range bindableStmt.Users { - if _, ok := captureFilter.users[user]; !ok { - filteredByUser = false // some user not in the black-list has processed this stmt - break - } - } - if filteredByUser { - continue - } - } - } - dbName := utilparser.GetDefaultDB(stmt, bindableStmt.Schema) - normalizedSQL, digest := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmt, dbName, bindableStmt.Query)) - if r := h.GetBindRecord(digest.String(), normalizedSQL, dbName); r != nil && r.HasAvailableBinding() { - continue - } - bindSQL := GenerateBindSQL(context.TODO(), stmt, bindableStmt.PlanHint, true, dbName) - if bindSQL == "" { - continue - } - h.sctx.Lock() - charset, collation := h.sctx.GetSessionVars().GetCharsetInfo() - h.sctx.Unlock() - binding := Binding{ - BindSQL: bindSQL, - Status: Enabled, - Charset: charset, - Collation: collation, - Source: Capture, - SQLDigest: digest.String(), - } - // We don't need to pass the `sctx` because the BindSQL has been validated already. - err = h.CreateBindRecord(nil, &BindRecord{OriginalSQL: normalizedSQL, Db: dbName, Bindings: []Binding{binding}}) - if err != nil { - logutil.BgLogger().Debug("create bind record failed in baseline capture", zap.String("category", "sql-bind"), zap.String("SQL", bindableStmt.Query), zap.Error(err)) - } - } -} - -func getHintsForSQL(sctx sessionctx.Context, sql string) (string, error) { - origVals := sctx.GetSessionVars().UsePlanBaselines - sctx.GetSessionVars().UsePlanBaselines = false - - // Usually passing a sprintf to ExecuteInternal is not recommended, but in this case - // it is safe because ExecuteInternal does not permit MultiStatement execution. Thus, - // the statement won't be able to "break out" from EXPLAIN. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, fmt.Sprintf("EXPLAIN FORMAT='hint' %s", sql)) - sctx.GetSessionVars().UsePlanBaselines = origVals - if rs != nil { - defer func() { - // Audit log is collected in Close(), set InRestrictedSQL to avoid 'create sql binding' been recorded as 'explain'. - origin := sctx.GetSessionVars().InRestrictedSQL - sctx.GetSessionVars().InRestrictedSQL = true - terror.Call(rs.Close) - sctx.GetSessionVars().InRestrictedSQL = origin - }() - } - if err != nil { - return "", err - } - chk := rs.NewChunk(nil) - err = rs.Next(context.TODO(), chk) - if err != nil { - return "", err - } - return chk.GetRow(0).GetString(0), nil -} - -// GenerateBindSQL generates binding sqls from stmt node and plan hints. -func GenerateBindSQL(ctx context.Context, stmtNode ast.StmtNode, planHint string, skipCheckIfHasParam bool, defaultDB string) string { - // If would be nil for very simple cases such as point get, we do not need to evolve for them. - if planHint == "" { - return "" - } - if !skipCheckIfHasParam { - paramChecker := ¶mMarkerChecker{} - stmtNode.Accept(paramChecker) - // We need to evolve on current sql, but we cannot restore values for paramMarkers yet, - // so just ignore them now. - if paramChecker.hasParamMarker { - return "" - } - } - // We need to evolve plan based on the current sql, not the original sql which may have different parameters. - // So here we would remove the hint and inject the current best plan hint. - hint.BindHint(stmtNode, &hint.HintsSet{}) - bindSQL := utilparser.RestoreWithDefaultDB(stmtNode, defaultDB, "") - if bindSQL == "" { - return "" - } - switch n := stmtNode.(type) { - case *ast.DeleteStmt: - deleteIdx := strings.Index(bindSQL, "DELETE") - // Remove possible `explain` prefix. - bindSQL = bindSQL[deleteIdx:] - return strings.Replace(bindSQL, "DELETE", fmt.Sprintf("DELETE /*+ %s*/", planHint), 1) - case *ast.UpdateStmt: - updateIdx := strings.Index(bindSQL, "UPDATE") - // Remove possible `explain` prefix. - bindSQL = bindSQL[updateIdx:] - return strings.Replace(bindSQL, "UPDATE", fmt.Sprintf("UPDATE /*+ %s*/", planHint), 1) - case *ast.SelectStmt: - var selectIdx int - if n.With != nil { - var withSb strings.Builder - withIdx := strings.Index(bindSQL, "WITH") - restoreCtx := format.NewRestoreCtx(format.RestoreStringSingleQuotes|format.RestoreSpacesAroundBinaryOperation|format.RestoreStringWithoutCharset|format.RestoreNameBackQuotes, &withSb) - restoreCtx.DefaultDB = defaultDB - if err := n.With.Restore(restoreCtx); err != nil { - logutil.BgLogger().Debug("restore SQL failed", zap.String("category", "sql-bind"), zap.Error(err)) - return "" - } - withEnd := withIdx + len(withSb.String()) - tmp := strings.Replace(bindSQL[withEnd:], "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) - return strings.Join([]string{bindSQL[withIdx:withEnd], tmp}, "") - } - selectIdx = strings.Index(bindSQL, "SELECT") - // Remove possible `explain` prefix. - bindSQL = bindSQL[selectIdx:] - return strings.Replace(bindSQL, "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) - case *ast.InsertStmt: - insertIdx := int(0) - if n.IsReplace { - insertIdx = strings.Index(bindSQL, "REPLACE") - } else { - insertIdx = strings.Index(bindSQL, "INSERT") - } - // Remove possible `explain` prefix. - bindSQL = bindSQL[insertIdx:] - return strings.Replace(bindSQL, "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) - } - logutil.Logger(ctx).Debug("unexpected statement type when generating bind SQL", zap.String("category", "sql-bind"), zap.Any("statement", stmtNode)) - return "" -} - -type paramMarkerChecker struct { - hasParamMarker bool -} - -func (e *paramMarkerChecker) Enter(in ast.Node) (ast.Node, bool) { - if _, ok := in.(*driver.ParamMarkerExpr); ok { - e.hasParamMarker = true - return in, true - } - return in, false -} - -func (*paramMarkerChecker) Leave(in ast.Node) (ast.Node, bool) { - return in, true -} - -// AddEvolvePlanTask adds the evolve plan task into memory cache. It would be flushed to store periodically. -func (h *BindHandle) AddEvolvePlanTask(originalSQL, db string, binding Binding) { - br := &BindRecord{ - OriginalSQL: originalSQL, - Db: db, - Bindings: []Binding{binding}, - } - h.pendingVerifyBindRecordMap.Add(br) -} - -// SaveEvolveTasksToStore saves the evolve task into store. -func (h *BindHandle) SaveEvolveTasksToStore() { - h.pendingVerifyBindRecordMap.flushToStore() -} - -func getEvolveParameters(sctx sessionctx.Context) (time.Duration, time.Time, time.Time, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL( - ctx, - nil, - "SELECT variable_name, variable_value FROM mysql.global_variables WHERE variable_name IN (%?, %?, %?)", - variable.TiDBEvolvePlanTaskMaxTime, - variable.TiDBEvolvePlanTaskStartTime, - variable.TiDBEvolvePlanTaskEndTime, - ) - if err != nil { - return 0, time.Time{}, time.Time{}, err - } - maxTime, startTimeStr, endTimeStr := int64(variable.DefTiDBEvolvePlanTaskMaxTime), variable.DefTiDBEvolvePlanTaskStartTime, variable.DefAutoAnalyzeEndTime - for _, row := range rows { - switch row.GetString(0) { - case variable.TiDBEvolvePlanTaskMaxTime: - maxTime, err = strconv.ParseInt(row.GetString(1), 10, 64) - if err != nil { - return 0, time.Time{}, time.Time{}, err - } - case variable.TiDBEvolvePlanTaskStartTime: - startTimeStr = row.GetString(1) - case variable.TiDBEvolvePlanTaskEndTime: - endTimeStr = row.GetString(1) - } - } - startTime, err := time.ParseInLocation(variable.FullDayTimeFormat, startTimeStr, time.UTC) - if err != nil { - return 0, time.Time{}, time.Time{}, err - } - endTime, err := time.ParseInLocation(variable.FullDayTimeFormat, endTimeStr, time.UTC) - if err != nil { - return 0, time.Time{}, time.Time{}, err - } - return time.Duration(maxTime) * time.Second, startTime, endTime, nil -} - -const ( - // acceptFactor is the factor to decide should we accept the pending verified plan. - // A pending verified plan will be accepted if it performs at least `acceptFactor` times better than the accepted plans. - acceptFactor = 1.5 - // verifyTimeoutFactor is how long to wait to verify the pending plan. - // For debugging purposes it is useful to wait a few times longer than the current execution time so that - // an informative error can be written to the log. - verifyTimeoutFactor = 2.0 - // nextVerifyDuration is the duration that we will retry the rejected plans. - nextVerifyDuration = 7 * 24 * time.Hour -) - -func (h *BindHandle) getOnePendingVerifyJob() (originalSQL, db string, binding Binding) { - cache := h.bindInfo.Value.Load().(*bindCache) - for _, bindRecord := range cache.GetAllBindRecords() { - for _, bind := range bindRecord.Bindings { - if bind.Status == PendingVerify { - return bindRecord.OriginalSQL, bindRecord.Db, bind - } - if bind.Status != Rejected { - continue - } - dur, err := bind.SinceUpdateTime() - // Should not happen. - if err != nil { - continue - } - // Rejected and retry it now. - if dur > nextVerifyDuration { - return bindRecord.OriginalSQL, bindRecord.Db, bind - } - } - } - return "", "", Binding{} -} - -func (*BindHandle) getRunningDuration(sctx sessionctx.Context, db, sql string, maxTime time.Duration) (time.Duration, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) - if db != "" { - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "use %n", db) - if err != nil { - return 0, err - } - } - ctx, cancelFunc := context.WithCancel(ctx) - timer := time.NewTimer(maxTime) - defer timer.Stop() - resultChan := make(chan error) - startTime := time.Now() - go runSQL(ctx, sctx, sql, resultChan) - select { - case err := <-resultChan: - cancelFunc() - if err != nil { - return 0, err - } - return time.Since(startTime), nil - case <-timer.C: - cancelFunc() - logutil.BgLogger().Debug("plan verification timed out", zap.String("category", "sql-bind"), zap.Duration("timeElapsed", time.Since(startTime)), zap.String("query", sql)) - } - <-resultChan - return -1, nil -} - -func runSQL(ctx context.Context, sctx sessionctx.Context, sql string, resultChan chan<- error) { - defer func() { - if r := recover(); r != nil { - buf := make([]byte, 4096) - stackSize := runtime.Stack(buf, false) - buf = buf[:stackSize] - resultChan <- fmt.Errorf("run sql panicked: %v", string(buf)) - } - }() - rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) - if err != nil { - if rs != nil { - terror.Call(rs.Close) - } - resultChan <- err - return - } - chk := rs.NewChunk(nil) - for { - err = rs.Next(ctx, chk) - if err != nil || chk.NumRows() == 0 { - break - } - } - terror.Call(rs.Close) - resultChan <- err -} - -// HandleEvolvePlanTask tries to evolve one plan task. -// It only processes one task at a time because we want each task to use the latest parameters. -func (h *BindHandle) HandleEvolvePlanTask(sctx sessionctx.Context, adminEvolve bool) error { - originalSQL, db, binding := h.getOnePendingVerifyJob() - if originalSQL == "" { - return nil - } - maxTime, startTime, endTime, err := getEvolveParameters(sctx) - if err != nil { - return err - } - if maxTime == 0 || (!timeutil.WithinDayTimePeriod(startTime, endTime, time.Now()) && !adminEvolve) { - return nil - } - sctx.GetSessionVars().UsePlanBaselines = true - currentPlanTime, err := h.getRunningDuration(sctx, db, binding.BindSQL, maxTime) - // If we just return the error to the caller, this job will be retried again and again and cause endless logs, - // since it is still in the bind record. Now we just drop it and if it is actually retryable, - // we will hope for that we can capture this evolve task again. - if err != nil { - _, err = h.DropBindRecord(originalSQL, db, &binding) - return err - } - // If the accepted plan timeouts, it is hard to decide the timeout for verify plan. - // Currently we simply mark the verify plan as `using` if it could run successfully within maxTime. - if currentPlanTime > 0 { - maxTime = time.Duration(float64(currentPlanTime) * verifyTimeoutFactor) - } - sctx.GetSessionVars().UsePlanBaselines = false - verifyPlanTime, err := h.getRunningDuration(sctx, db, binding.BindSQL, maxTime) - if err != nil { - _, err = h.DropBindRecord(originalSQL, db, &binding) - return err - } - if verifyPlanTime == -1 || (float64(verifyPlanTime)*acceptFactor > float64(currentPlanTime)) { - binding.Status = Rejected - digestText, _ := parser.NormalizeDigest(binding.BindSQL) // for log desensitization - logutil.BgLogger().Debug("new plan rejected", zap.String("category", "sql-bind"), - zap.Duration("currentPlanTime", currentPlanTime), - zap.Duration("verifyPlanTime", verifyPlanTime), - zap.String("digestText", digestText), - ) - } else { - binding.Status = Enabled - } - // We don't need to pass the `sctx` because the BindSQL has been validated already. - return h.AddBindRecord(nil, &BindRecord{OriginalSQL: originalSQL, Db: db, Bindings: []Binding{binding}}) -} - -// Clear resets the bind handle. It is only used for test. -func (h *BindHandle) Clear() { - h.bindInfo.Lock() - h.bindInfo.Store(newBindCache()) - h.bindInfo.lastUpdateTime = types.ZeroTimestamp - h.bindInfo.Unlock() - h.invalidBindRecordMap.Store(make(map[string]*bindRecordUpdate)) - h.pendingVerifyBindRecordMap.Store(make(map[string]*bindRecordUpdate)) -} - -// FlushBindings flushes the BindRecord in temp maps to storage and loads them into cache. -func (h *BindHandle) FlushBindings() error { - h.DropInvalidBindRecord() - h.SaveEvolveTasksToStore() - return h.Update(false) -} - -// ReloadBindings clears existing binding cache and do a full load from mysql.bind_info. -// It is used to maintain consistency between cache and mysql.bind_info if the table is deleted or truncated. -func (h *BindHandle) ReloadBindings() error { - h.bindInfo.Lock() - h.bindInfo.Store(newBindCache()) - h.bindInfo.lastUpdateTime = types.ZeroTimestamp - h.bindInfo.Unlock() - return h.Update(true) -} diff --git a/bindinfo/handle_test.go b/bindinfo/handle_test.go deleted file mode 100644 index d8e70a7d4c219..0000000000000 --- a/bindinfo/handle_test.go +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package bindinfo_test - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/bindinfo/internal" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/testkit" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" -) - -func TestBindingCache(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create global binding for select * from t using select * from t use index(idx);") - tk.MustExec("create database tmp") - tk.MustExec("use tmp") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create global binding for select * from t using select * from t use index(idx);") - - require.Nil(t, dom.BindHandle().Update(false)) - require.Nil(t, dom.BindHandle().Update(false)) - res := tk.MustQuery("show global bindings") - require.Equal(t, 2, len(res.Rows())) - - tk.MustExec("drop global binding for select * from t;") - require.Nil(t, dom.BindHandle().Update(false)) - require.Equal(t, 1, len(dom.BindHandle().GetAllBindRecord())) -} - -func TestBindingLastUpdateTime(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t0;") - tk.MustExec("create table t0(a int, key(a));") - tk.MustExec("create global binding for select * from t0 using select * from t0 use index(a);") - tk.MustExec("admin reload bindings;") - - bindHandle := bindinfo.NewBindHandle(tk.Session()) - err := bindHandle.Update(true) - require.NoError(t, err) - sql, hash := parser.NormalizeDigest("select * from test . t0") - bindData := bindHandle.GetBindRecord(hash.String(), sql, "test") - require.Equal(t, 1, len(bindData.Bindings)) - bind := bindData.Bindings[0] - updateTime := bind.UpdateTime.String() - - rows1 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() - updateTime1 := rows1[0][1] - require.Equal(t, updateTime, updateTime1) - - rows2 := tk.MustQuery("show session status like 'last_plan_binding_update_time';").Rows() - updateTime2 := rows2[0][1] - require.Equal(t, updateTime, updateTime2) - tk.MustQuery(`show global status like 'last_plan_binding_update_time';`).Check(testkit.Rows()) -} - -func TestBindingLastUpdateTimeWithInvalidBind(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - rows0 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() - updateTime0 := rows0[0][1] - require.Equal(t, updateTime0, "0000-00-00 00:00:00") - - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t`', 'select * from `test` . `t` use index(`idx`)', 'test', 'enabled', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("admin reload bindings;") - - rows1 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() - updateTime1 := rows1[0][1] - require.Equal(t, updateTime1, "2000-01-01 09:00:00.000") - - rows2 := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows2, 0) -} - -func TestBindParse(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(i int)") - tk.MustExec("create index index_t on t(i)") - - originSQL := "select * from `test` . `t`" - bindSQL := "select * from `test` . `t` use index(index_t)" - defaultDb := "test" - status := bindinfo.Enabled - charset := "utf8mb4" - collation := "utf8mb4_bin" - source := bindinfo.Manual - mockDigest := "0f644e22c38ecc71d4592c52df127df7f86b6ca7f7c0ee899113b794578f9396" - sql := fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation,source, sql_digest, plan_digest) VALUES ('%s', '%s', '%s', '%s', NOW(), NOW(),'%s', '%s', '%s', '%s', '%s')`, - originSQL, bindSQL, defaultDb, status, charset, collation, source, mockDigest, mockDigest) - tk.MustExec(sql) - bindHandle := bindinfo.NewBindHandle(tk.Session()) - err := bindHandle.Update(true) - require.NoError(t, err) - require.Equal(t, 1, bindHandle.Size()) - - sql, hash := parser.NormalizeDigest("select * from test . t") - bindData := bindHandle.GetBindRecord(hash.String(), sql, "test") - require.NotNil(t, bindData) - require.Equal(t, "select * from `test` . `t`", bindData.OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, "select * from `test` . `t` use index(index_t)", bind.BindSQL) - require.Equal(t, "test", bindData.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.Equal(t, "utf8mb4", bind.Charset) - require.Equal(t, "utf8mb4_bin", bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) - dur, err := bind.SinceUpdateTime() - require.NoError(t, err) - require.GreaterOrEqual(t, int64(dur), int64(0)) - - // Test fields with quotes or slashes. - sql = `CREATE GLOBAL BINDING FOR select * from t where i BETWEEN "a" and "b" USING select * from t use index(index_t) where i BETWEEN "a\nb\rc\td\0e" and 'x'` - tk.MustExec(sql) - tk.MustExec(`DROP global binding for select * from t use index(idx) where i BETWEEN "a\nb\rc\td\0e" and "x"`) - - // Test SetOprStmt. - tk.MustExec(`create binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()`) - tk.MustExec(`drop binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()`) - tk.MustExec(`create binding for select * from t INTERSECT select * from t using select * from t use index(index_t) INTERSECT select * from t use index()`) - tk.MustExec(`drop binding for select * from t INTERSECT select * from t using select * from t use index(index_t) INTERSECT select * from t use index()`) - tk.MustExec(`create binding for select * from t EXCEPT select * from t using select * from t use index(index_t) EXCEPT select * from t use index()`) - tk.MustExec(`drop binding for select * from t EXCEPT select * from t using select * from t use index(index_t) EXCEPT select * from t use index()`) - tk.MustExec(`create binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())`) - tk.MustExec(`drop binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())`) - - // Test Update / Delete. - tk.MustExec("create table t1(a int, b int, c int, key(b), key(c))") - tk.MustExec("create table t2(a int, b int, c int, key(b), key(c))") - tk.MustExec("create binding for delete from t1 where b = 1 and c > 1 using delete /*+ use_index(t1, c) */ from t1 where b = 1 and c > 1") - tk.MustExec("drop binding for delete from t1 where b = 1 and c > 1 using delete /*+ use_index(t1, c) */ from t1 where b = 1 and c > 1") - tk.MustExec("create binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1 using delete /*+ hash_join(t1, t2), use_index(t1, c) */ t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1") - tk.MustExec("drop binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1 using delete /*+ hash_join(t1, t2), use_index(t1, c) */ t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1") - tk.MustExec("create binding for update t1 set a = 1 where b = 1 and c > 1 using update /*+ use_index(t1, c) */ t1 set a = 1 where b = 1 and c > 1") - tk.MustExec("drop binding for update t1 set a = 1 where b = 1 and c > 1 using update /*+ use_index(t1, c) */ t1 set a = 1 where b = 1 and c > 1") - tk.MustExec("create binding for update t1, t2 set t1.a = 1 where t1.b = t2.b using update /*+ inl_join(t1) */ t1, t2 set t1.a = 1 where t1.b = t2.b") - tk.MustExec("drop binding for update t1, t2 set t1.a = 1 where t1.b = t2.b using update /*+ inl_join(t1) */ t1, t2 set t1.a = 1 where t1.b = t2.b") - // Test Insert / Replace. - tk.MustExec("create binding for insert into t1 select * from t2 where t2.b = 1 and t2.c > 1 using insert into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") - tk.MustExec("drop binding for insert into t1 select * from t2 where t2.b = 1 and t2.c > 1 using insert into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") - tk.MustExec("create binding for replace into t1 select * from t2 where t2.b = 1 and t2.c > 1 using replace into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") - tk.MustExec("drop binding for replace into t1 select * from t2 where t2.b = 1 and t2.c > 1 using replace into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") - err = tk.ExecToErr("create binding for insert into t1 values(1,1,1) using insert into t1 values(1,1,1)") - require.Equal(t, "create binding only supports INSERT / REPLACE INTO SELECT", err.Error()) - err = tk.ExecToErr("create binding for replace into t1 values(1,1,1) using replace into t1 values(1,1,1)") - require.Equal(t, "create binding only supports INSERT / REPLACE INTO SELECT", err.Error()) - - // Test errors. - tk.MustExec(`drop table if exists t1`) - tk.MustExec("create table t1(i int, s varchar(20))") - _, err = tk.Exec("create global binding for select * from t using select * from t1 use index for join(index_t)") - require.NotNil(t, err, "err %v", err) -} - -func TestEvolveInvalidBindings(t *testing.T) { - originalVal := config.CheckTableBeforeDrop - config.CheckTableBeforeDrop = true - defer func() { - config.CheckTableBeforeDrop = originalVal - }() - - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx_a(a))") - tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t) */ * from t where a > 10") - // Manufacture a rejected binding by hacking mysql.bind_info. - tk.MustExec("insert into mysql.bind_info values('select * from test . t where a > ?', 'SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10', 'test', 'rejected', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - tk.MustQuery("select bind_sql, status from mysql.bind_info where source != 'builtin'").Sort().Check(testkit.Rows( - "SELECT /*+ USE_INDEX(`t` )*/ * FROM `test`.`t` WHERE `a` > 10 enabled", - "SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10 rejected", - )) - // Reload cache from mysql.bind_info. - dom.BindHandle().Clear() - require.Nil(t, dom.BindHandle().Update(true)) - - tk.MustExec("alter table t drop index idx_a") - tk.MustExec("admin evolve bindings") - require.Nil(t, dom.BindHandle().Update(false)) - rows := tk.MustQuery("show global bindings").Sort().Rows() - require.Equal(t, 2, len(rows)) - // Make sure this "enabled" binding is not overrided. - require.Equal(t, "SELECT /*+ USE_INDEX(`t` )*/ * FROM `test`.`t` WHERE `a` > 10", rows[0][1]) - status := rows[0][3].(string) - require.True(t, status == bindinfo.Enabled) - require.Equal(t, "SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10", rows[1][1]) - status = rows[1][3].(string) - require.True(t, status == bindinfo.Enabled || status == bindinfo.Rejected) - _, sqlDigestWithDB := parser.NormalizeDigest("select * from test.t where a > 10") // test sqlDigest if exists after add columns to mysql.bind_info - require.Equal(t, rows[0][9], sqlDigestWithDB.String()) -} - -func TestSetBindingStatus(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index idx_a(a))") - tk.MustQuery("show global bindings").Check(testkit.Rows()) - tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t, idx_a) */ * from t where a > 10") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - - tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 There are no bindings can be set the status. Please check the SQL text")) - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - - tk.MustExec("set binding disabled for select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Disabled, rows[0][3]) - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - - tk.MustExec("set binding enabled for select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - - tk.MustExec("set binding disabled for select * from t where a > 10") - tk.MustExec("create global binding for select * from t where a > 10 using select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - - tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Disabled, rows[0][3]) - tk.MustExec("select * from t where a > 10") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - - tk.MustExec("set binding enabled for select * from t where a > 10 using select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - - tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") - tk.MustExec("drop global binding for select * from t where a > 10 using select * from t where a > 10") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 0) -} - -func TestSetBindingStatusWithoutBindingInCache(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index idx_a(a))") - internal.UtilCleanBindingEnv(tk, dom) - tk.MustQuery("show global bindings").Check(testkit.Rows()) - - // Simulate creating bindings on other machines - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'enabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - dom.BindHandle().Clear() - tk.MustExec("set binding disabled for select * from t where a > 10") - tk.MustExec("admin reload bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Disabled, rows[0][3]) - - // clear the mysql.bind_info - internal.UtilCleanBindingEnv(tk, dom) - - // Simulate creating bindings on other machines - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'disabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" + - bindinfo.Manual + "', '', '')") - dom.BindHandle().Clear() - tk.MustExec("set binding enabled for select * from t where a > 10") - tk.MustExec("admin reload bindings") - rows = tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - require.Equal(t, bindinfo.Enabled, rows[0][3]) - - internal.UtilCleanBindingEnv(tk, dom) -} - -var testSQLs = []struct { - createSQL string - overlaySQL string - querySQL string - originSQL string - bindSQL string - dropSQL string - memoryUsage float64 -}{ - { - createSQL: "binding for select * from t where i>100 using select * from t use index(index_t) where i>100", - overlaySQL: "binding for select * from t where i>99 using select * from t use index(index_t) where i>99", - querySQL: "select * from t where i > 30.0", - originSQL: "select * from `test` . `t` where `i` > ?", - bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) WHERE `i` > 99", - dropSQL: "binding for select * from t where i>100", - memoryUsage: float64(167), - }, - { - createSQL: "binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()", - overlaySQL: "", - querySQL: "select * from t union all select * from t", - originSQL: "select * from `test` . `t` union all select * from `test` . `t`", - bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) UNION ALL SELECT * FROM `test`.`t` USE INDEX ()", - dropSQL: "binding for select * from t union all select * from t", - memoryUsage: float64(237), - }, - { - createSQL: "binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())", - overlaySQL: "", - querySQL: "(select * from t) union all (select * from t)", - originSQL: "( select * from `test` . `t` ) union all ( select * from `test` . `t` )", - bindSQL: "(SELECT * FROM `test`.`t` USE INDEX (`index_t`)) UNION ALL (SELECT * FROM `test`.`t` USE INDEX ())", - dropSQL: "binding for (select * from t) union all (select * from t)", - memoryUsage: float64(249), - }, - { - createSQL: "binding for select * from t intersect select * from t using select * from t use index(index_t) intersect select * from t use index()", - overlaySQL: "", - querySQL: "select * from t intersect select * from t", - originSQL: "select * from `test` . `t` intersect select * from `test` . `t`", - bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) INTERSECT SELECT * FROM `test`.`t` USE INDEX ()", - dropSQL: "binding for select * from t intersect select * from t", - memoryUsage: float64(237), - }, - { - createSQL: "binding for select * from t except select * from t using select * from t use index(index_t) except select * from t use index()", - overlaySQL: "", - querySQL: "select * from t except select * from t", - originSQL: "select * from `test` . `t` except select * from `test` . `t`", - bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) EXCEPT SELECT * FROM `test`.`t` USE INDEX ()", - dropSQL: "binding for select * from t except select * from t", - memoryUsage: float64(231), - }, - { - createSQL: "binding for select * from t using select /*+ use_index(t,index_t)*/ * from t", - overlaySQL: "", - querySQL: "select * from t ", - originSQL: "select * from `test` . `t`", - bindSQL: "SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t`", - dropSQL: "binding for select * from t", - memoryUsage: float64(166), - }, - { - createSQL: "binding for delete from t where i = 1 using delete /*+ use_index(t,index_t) */ from t where i = 1", - overlaySQL: "", - querySQL: "delete from t where i = 2", - originSQL: "delete from `test` . `t` where `i` = ?", - bindSQL: "DELETE /*+ use_index(`t` `index_t`)*/ FROM `test`.`t` WHERE `i` = 1", - dropSQL: "binding for delete from t where i = 1", - memoryUsage: float64(190), - }, - { - createSQL: "binding for delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 1 using delete /*+ use_index(t,index_t), hash_join(t,t1) */ t, t1 from t inner join t1 on t.s = t1.s where t.i = 1", - overlaySQL: "", - querySQL: "delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 2", - originSQL: "delete `test` . `t` , `test` . `t1` from `test` . `t` join `test` . `t1` on `t` . `s` = `t1` . `s` where `t` . `i` = ?", - bindSQL: "DELETE /*+ use_index(`t` `index_t`) hash_join(`t`, `t1`)*/ `test`.`t`,`test`.`t1` FROM `test`.`t` JOIN `test`.`t1` ON `t`.`s` = `t1`.`s` WHERE `t`.`i` = 1", - dropSQL: "binding for delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 1", - memoryUsage: float64(402), - }, - { - createSQL: "binding for update t set s = 'a' where i = 1 using update /*+ use_index(t,index_t) */ t set s = 'a' where i = 1", - overlaySQL: "", - querySQL: "update t set s='b' where i=2", - originSQL: "update `test` . `t` set `s` = ? where `i` = ?", - bindSQL: "UPDATE /*+ use_index(`t` `index_t`)*/ `test`.`t` SET `s`='a' WHERE `i` = 1", - dropSQL: "binding for update t set s = 'a' where i = 1", - memoryUsage: float64(204), - }, - { - createSQL: "binding for update t, t1 set t.s = 'a' where t.i = t1.i using update /*+ inl_join(t1) */ t, t1 set t.s = 'a' where t.i = t1.i", - overlaySQL: "", - querySQL: "update t , t1 set t.s='b' where t.i=t1.i", - originSQL: "update ( `test` . `t` ) join `test` . `t1` set `t` . `s` = ? where `t` . `i` = `t1` . `i`", - bindSQL: "UPDATE /*+ inl_join(`t1`)*/ (`test`.`t`) JOIN `test`.`t1` SET `t`.`s`='a' WHERE `t`.`i` = `t1`.`i`", - dropSQL: "binding for update t, t1 set t.s = 'a' where t.i = t1.i", - memoryUsage: float64(262), - }, - { - createSQL: "binding for insert into t1 select * from t where t.i = 1 using insert into t1 select /*+ use_index(t,index_t) */ * from t where t.i = 1", - overlaySQL: "", - querySQL: "insert into t1 select * from t where t.i = 2", - originSQL: "insert into `test` . `t1` select * from `test` . `t` where `t` . `i` = ?", - bindSQL: "INSERT INTO `test`.`t1` SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t` WHERE `t`.`i` = 1", - dropSQL: "binding for insert into t1 select * from t where t.i = 1", - memoryUsage: float64(254), - }, - { - createSQL: "binding for replace into t1 select * from t where t.i = 1 using replace into t1 select /*+ use_index(t,index_t) */ * from t where t.i = 1", - overlaySQL: "", - querySQL: "replace into t1 select * from t where t.i = 2", - originSQL: "replace into `test` . `t1` select * from `test` . `t` where `t` . `i` = ?", - bindSQL: "REPLACE INTO `test`.`t1` SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t` WHERE `t`.`i` = 1", - dropSQL: "binding for replace into t1 select * from t where t.i = 1", - memoryUsage: float64(256), - }, -} - -func TestGlobalBinding(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - for _, testSQL := range testSQLs { - internal.UtilCleanBindingEnv(tk, dom) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(i int, s varchar(20))") - tk.MustExec("create table t1(i int, s varchar(20))") - tk.MustExec("create index index_t on t(i,s)") - - metrics.BindTotalGauge.Reset() - metrics.BindMemoryUsage.Reset() - - _, err := tk.Exec("create global " + testSQL.createSQL) - require.NoError(t, err, "err %v", err) - - if testSQL.overlaySQL != "" { - _, err = tk.Exec("create global " + testSQL.overlaySQL) - require.NoError(t, err) - } - - pb := &dto.Metric{} - err = metrics.BindTotalGauge.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) - require.NoError(t, err) - require.Equal(t, float64(1), pb.GetGauge().GetValue()) - err = metrics.BindMemoryUsage.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) - require.NoError(t, err) - require.Equal(t, testSQL.memoryUsage, pb.GetGauge().GetValue()) - - sql, hash := internal.UtilNormalizeWithDefaultDB(t, testSQL.querySQL) - - bindData := dom.BindHandle().GetBindRecord(hash, sql, "test") - require.NotNil(t, bindData) - require.Equal(t, testSQL.originSQL, bindData.OriginalSQL) - bind := bindData.Bindings[0] - require.Equal(t, testSQL.bindSQL, bind.BindSQL) - require.Equal(t, "test", bindData.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) - - rs, err := tk.Exec("show global bindings") - require.NoError(t, err) - chk := rs.NewChunk(nil) - err = rs.Next(context.TODO(), chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, testSQL.originSQL, row.GetString(0)) - require.Equal(t, testSQL.bindSQL, row.GetString(1)) - require.Equal(t, "test", row.GetString(2)) - require.Equal(t, bindinfo.Enabled, row.GetString(3)) - require.NotNil(t, row.GetTime(4)) - require.NotNil(t, row.GetTime(5)) - require.NotNil(t, row.GetString(6)) - require.NotNil(t, row.GetString(7)) - - bindHandle := bindinfo.NewBindHandle(tk.Session()) - err = bindHandle.Update(true) - require.NoError(t, err) - require.Equal(t, 1, bindHandle.Size()) - - bindData = bindHandle.GetBindRecord(hash, sql, "test") - require.NotNil(t, bindData) - require.Equal(t, testSQL.originSQL, bindData.OriginalSQL) - bind = bindData.Bindings[0] - require.Equal(t, testSQL.bindSQL, bind.BindSQL) - require.Equal(t, "test", bindData.Db) - require.Equal(t, bindinfo.Enabled, bind.Status) - require.NotNil(t, bind.Charset) - require.NotNil(t, bind.Collation) - require.NotNil(t, bind.CreateTime) - require.NotNil(t, bind.UpdateTime) - - _, err = tk.Exec("drop global " + testSQL.dropSQL) - require.Equal(t, uint64(1), tk.Session().AffectedRows()) - require.NoError(t, err) - bindData = dom.BindHandle().GetBindRecord(hash, sql, "test") - require.Nil(t, bindData) - - err = metrics.BindTotalGauge.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) - require.NoError(t, err) - require.Equal(t, float64(0), pb.GetGauge().GetValue()) - err = metrics.BindMemoryUsage.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) - require.NoError(t, err) - // From newly created global bind handle. - require.Equal(t, testSQL.memoryUsage, pb.GetGauge().GetValue()) - - bindHandle = bindinfo.NewBindHandle(tk.Session()) - err = bindHandle.Update(true) - require.NoError(t, err) - require.Equal(t, 0, bindHandle.Size()) - - bindData = bindHandle.GetBindRecord(hash, sql, "test") - require.Nil(t, bindData) - - rs, err = tk.Exec("show global bindings") - require.NoError(t, err) - chk = rs.NewChunk(nil) - err = rs.Next(context.TODO(), chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - - _, err = tk.Exec("delete from mysql.bind_info where source != 'builtin'") - require.NoError(t, err) - } -} - -func TestOutdatedInfoSchema(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create global binding for select * from t using select * from t use index(idx)") - require.Nil(t, dom.BindHandle().Update(false)) - internal.UtilCleanBindingEnv(tk, dom) - tk.MustExec("create global binding for select * from t using select * from t use index(idx)") -} - -func TestReloadBindings(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create global binding for select * from t using select * from t use index(idx)") - rows := tk.MustQuery("show global bindings").Rows() - require.Equal(t, 1, len(rows)) - rows = tk.MustQuery("select * from mysql.bind_info where source != 'builtin'").Rows() - require.Equal(t, 1, len(rows)) - tk.MustExec("delete from mysql.bind_info where source != 'builtin'") - require.Nil(t, dom.BindHandle().Update(false)) - rows = tk.MustQuery("show global bindings").Rows() - require.Equal(t, 1, len(rows)) - require.Nil(t, dom.BindHandle().Update(true)) - rows = tk.MustQuery("show global bindings").Rows() - require.Equal(t, 1, len(rows)) - tk.MustExec("admin reload bindings") - rows = tk.MustQuery("show global bindings").Rows() - require.Equal(t, 0, len(rows)) -} diff --git a/bindinfo/internal/BUILD.bazel b/bindinfo/internal/BUILD.bazel deleted file mode 100644 index 1338e493f61f6..0000000000000 --- a/bindinfo/internal/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/bindinfo/internal", - visibility = ["//bindinfo:__subpackages__"], - deps = [ - "//domain", - "//parser", - "//testkit", - "//util/parser", - "@com_github_stretchr_testify//require", - ], -) diff --git a/bindinfo/internal/testutil.go b/bindinfo/internal/testutil.go deleted file mode 100644 index 12fe0419fbd62..0000000000000 --- a/bindinfo/internal/testutil.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/testkit" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/stretchr/testify/require" -) - -// UtilCleanBindingEnv cleans the binding environment. -func UtilCleanBindingEnv(tk *testkit.TestKit, dom *domain.Domain) { - tk.MustExec("delete from mysql.bind_info where source != 'builtin'") - dom.BindHandle().Clear() -} - -// UtilNormalizeWithDefaultDB normalizes the SQL and returns the normalized SQL and its digest. -func UtilNormalizeWithDefaultDB(t *testing.T, sql string) (normalized, digest string) { - testParser := parser.New() - stmt, err := testParser.ParseOneStmt(sql, "", "") - require.NoError(t, err) - normalized, digestResult := parser.NormalizeDigestForBinding(utilparser.RestoreWithDefaultDB(stmt, "test", "")) - return normalized, digestResult.String() -} diff --git a/bindinfo/main_test.go b/bindinfo/main_test.go deleted file mode 100644 index aa82e4c7998cc..0000000000000 --- a/bindinfo/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package bindinfo_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/bindinfo/optimize_test.go b/bindinfo/optimize_test.go deleted file mode 100644 index cdd7203768cba..0000000000000 --- a/bindinfo/optimize_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package bindinfo_test - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestOptimizeOnlyOnce(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idxa(a))") - tk.MustExec("create global binding for select * from t using select * from t use index(idxa)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/checkOptimizeCountOne", "return(\"select * from t\")")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/checkOptimizeCountOne")) - }() - tk.MustQuery("select * from t").Check(testkit.Rows()) -} diff --git a/bindinfo/stat.go b/bindinfo/stat.go deleted file mode 100644 index ccb67150304b8..0000000000000 --- a/bindinfo/stat.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package bindinfo - -import ( - "github.com/pingcap/tidb/sessionctx/variable" -) - -var ( - lastPlanBindingUpdateTime = "last_plan_binding_update_time" -) - -// GetScope gets the status variables scope. -func (*BindHandle) GetScope(_ string) variable.ScopeFlag { - return variable.ScopeSession -} - -// Stats returns the server statistics. -func (h *BindHandle) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { - h.bindInfo.Lock() - defer func() { - h.bindInfo.Unlock() - }() - m := make(map[string]interface{}) - m[lastPlanBindingUpdateTime] = h.bindInfo.lastUpdateTime.String() - - return m, nil -} diff --git a/bindinfo/tests/BUILD.bazel b/bindinfo/tests/BUILD.bazel deleted file mode 100644 index 9f01e28dd3a0a..0000000000000 --- a/bindinfo/tests/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tests_test", - timeout = "short", - srcs = [ - "bind_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 28, - deps = [ - "//bindinfo", - "//bindinfo/internal", - "//config", - "//domain", - "//parser", - "//parser/auth", - "//parser/model", - "//parser/terror", - "//testkit", - "//testkit/testsetup", - "//util", - "//util/parser", - "//util/stmtsummary", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/bindinfo/tests/main_test.go b/bindinfo/tests/main_test.go deleted file mode 100644 index bf587660f72a9..0000000000000 --- a/bindinfo/tests/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/br/cmd/br/BUILD.bazel b/br/cmd/br/BUILD.bazel index 1c20553569792..0115777f2046a 100644 --- a/br/cmd/br/BUILD.bazel +++ b/br/cmd/br/BUILD.bazel @@ -31,12 +31,12 @@ go_library( "//br/pkg/trace", "//br/pkg/utils", "//br/pkg/version/build", - "//config", - "//parser/model", - "//session", - "//util", - "//util/logutil", - "//util/metricsutil", + "//pkg/config", + "//pkg/parser/model", + "//pkg/session", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/metricsutil", "@com_github_gogo_protobuf//proto", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/brpb", diff --git a/br/cmd/br/backup.go b/br/cmd/br/backup.go index d971d99200804..5bf64a455c8a9 100644 --- a/br/cmd/br/backup.go +++ b/br/cmd/br/backup.go @@ -11,9 +11,9 @@ import ( "github.com/pingcap/tidb/br/pkg/trace" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util/metricsutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util/metricsutil" "github.com/spf13/cobra" "go.uber.org/zap" "sourcegraph.com/sourcegraph/appdash" diff --git a/br/cmd/br/cmd.go b/br/cmd/br/cmd.go index f3aeb3393df52..66d8e989f0a37 100644 --- a/br/cmd/br/cmd.go +++ b/br/cmd/br/cmd.go @@ -19,9 +19,9 @@ import ( "github.com/pingcap/tidb/br/pkg/task" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/config" - tidbutils "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + tidbutils "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/spf13/cobra" ) diff --git a/br/cmd/br/debug.go b/br/cmd/br/debug.go index 199092e48b588..abd723f66f9d7 100644 --- a/br/cmd/br/debug.go +++ b/br/cmd/br/debug.go @@ -26,7 +26,7 @@ import ( "github.com/pingcap/tidb/br/pkg/task" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/spf13/cobra" "go.uber.org/zap" ) diff --git a/br/cmd/br/restore.go b/br/cmd/br/restore.go index 41635e9fc1e25..7e8cb6b8d9324 100644 --- a/br/cmd/br/restore.go +++ b/br/cmd/br/restore.go @@ -14,8 +14,8 @@ import ( "github.com/pingcap/tidb/br/pkg/trace" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util/metricsutil" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util/metricsutil" "github.com/spf13/cobra" "go.uber.org/zap" "sourcegraph.com/sourcegraph/appdash" diff --git a/br/pkg/backup/BUILD.bazel b/br/pkg/backup/BUILD.bazel index 64d2d89874dd9..62fd7b1e49c4b 100644 --- a/br/pkg/backup/BUILD.bazel +++ b/br/pkg/backup/BUILD.bazel @@ -26,17 +26,17 @@ go_library( "//br/pkg/summary", "//br/pkg/utils", "//br/pkg/version", - "//ddl", - "//distsql", - "//kv", - "//meta", - "//parser/model", - "//statistics/handle", - "//statistics/handle/storage", - "//util", - "//util/codec", - "//util/ranger", - "//util/table-filter", + "//pkg/ddl", + "//pkg/distsql", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/statistics/handle", + "//pkg/statistics/handle/storage", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/ranger", + "//pkg/util/table-filter", "@com_github_google_btree//:btree", "@com_github_opentracing_opentracing_go//:opentracing-go", "@com_github_pingcap_errors//:errors", @@ -79,15 +79,15 @@ go_test( "//br/pkg/pdutil", "//br/pkg/storage", "//br/pkg/utils", - "//kv", - "//parser/model", - "//sessionctx/variable", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/table-filter", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx/variable", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/table-filter", "@com_github_golang_protobuf//proto", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_pingcap_kvproto//pkg/encryptionpb", diff --git a/br/pkg/backup/client.go b/br/pkg/backup/client.go index b15255b41733d..1721fab2c45ba 100644 --- a/br/pkg/backup/client.go +++ b/br/pkg/backup/client.go @@ -37,15 +37,15 @@ import ( "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/ranger" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/ranger" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/txnlock" diff --git a/br/pkg/backup/client_test.go b/br/pkg/backup/client_test.go index 592416e8ec03c..ec97584c139cd 100644 --- a/br/pkg/backup/client_test.go +++ b/br/pkg/backup/client_test.go @@ -20,12 +20,12 @@ import ( "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/pdutil" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/testutils" diff --git a/br/pkg/backup/main_test.go b/br/pkg/backup/main_test.go index c5a2ddf9647db..399489c0186c2 100644 --- a/br/pkg/backup/main_test.go +++ b/br/pkg/backup/main_test.go @@ -17,7 +17,7 @@ package backup import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/backup/schema.go b/br/pkg/backup/schema.go index 8b6949cd484a1..eb9f910cb0b2a 100644 --- a/br/pkg/backup/schema.go +++ b/br/pkg/backup/schema.go @@ -18,10 +18,10 @@ import ( "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" kvutil "github.com/tikv/client-go/v2/util" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/backup/schema_test.go b/br/pkg/backup/schema_test.go index 4c9cee12ffc50..87baf681ec58d 100644 --- a/br/pkg/backup/schema_test.go +++ b/br/pkg/backup/schema_test.go @@ -18,9 +18,9 @@ import ( "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/br/pkg/checkpoint/BUILD.bazel b/br/pkg/checkpoint/BUILD.bazel index 363f58036f46b..a64b950e14157 100644 --- a/br/pkg/checkpoint/BUILD.bazel +++ b/br/pkg/checkpoint/BUILD.bazel @@ -19,7 +19,7 @@ go_library( "//br/pkg/storage", "//br/pkg/summary", "//br/pkg/utils", - "//parser/model", + "//pkg/parser/model", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", "@com_github_pingcap_kvproto//pkg/brpb", @@ -41,7 +41,7 @@ go_test( ":checkpoint", "//br/pkg/pdutil", "//br/pkg/storage", - "//parser/model", + "//pkg/parser/model", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_pingcap_kvproto//pkg/encryptionpb", "@com_github_stretchr_testify//require", diff --git a/br/pkg/checkpoint/checkpoint_test.go b/br/pkg/checkpoint/checkpoint_test.go index d8b43bf43c5cb..f2844487d7374 100644 --- a/br/pkg/checkpoint/checkpoint_test.go +++ b/br/pkg/checkpoint/checkpoint_test.go @@ -26,7 +26,7 @@ import ( "github.com/pingcap/tidb/br/pkg/checkpoint" "github.com/pingcap/tidb/br/pkg/pdutil" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) diff --git a/br/pkg/checkpoint/log_restore.go b/br/pkg/checkpoint/log_restore.go index 3a41aa0433a31..2162c47bd4410 100644 --- a/br/pkg/checkpoint/log_restore.go +++ b/br/pkg/checkpoint/log_restore.go @@ -24,7 +24,7 @@ import ( backuppb "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "go.uber.org/zap" ) diff --git a/br/pkg/checksum/BUILD.bazel b/br/pkg/checksum/BUILD.bazel index 8a3b452dad6e9..b1bc3803198c8 100644 --- a/br/pkg/checksum/BUILD.bazel +++ b/br/pkg/checksum/BUILD.bazel @@ -14,12 +14,12 @@ go_library( "//br/pkg/storage", "//br/pkg/summary", "//br/pkg/utils", - "//distsql", - "//kv", - "//parser/model", - "//sessionctx/variable", - "//tablecodec", - "//util/ranger", + "//pkg/distsql", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx/variable", + "//pkg/tablecodec", + "//pkg/util/ranger", "@com_github_gogo_protobuf//proto", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", @@ -43,11 +43,11 @@ go_test( "//br/pkg/backup", "//br/pkg/metautil", "//br/pkg/mock", - "//kv", - "//parser/model", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", "@org_uber_go_goleak//:goleak", diff --git a/br/pkg/checksum/executor.go b/br/pkg/checksum/executor.go index 896a7297bab83..22f5a13d23a65 100644 --- a/br/pkg/checksum/executor.go +++ b/br/pkg/checksum/executor.go @@ -11,12 +11,12 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/br/pkg/checksum/executor_test.go b/br/pkg/checksum/executor_test.go index a432d29234997..ebe58b62b0da1 100644 --- a/br/pkg/checksum/executor_test.go +++ b/br/pkg/checksum/executor_test.go @@ -12,10 +12,10 @@ import ( "github.com/pingcap/tidb/br/pkg/checksum" "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/mock" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/checksum/main_test.go b/br/pkg/checksum/main_test.go index 1a6589d31b913..8893f24fe6d29 100644 --- a/br/pkg/checksum/main_test.go +++ b/br/pkg/checksum/main_test.go @@ -17,7 +17,7 @@ package checksum import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/conn/BUILD.bazel b/br/pkg/conn/BUILD.bazel index d6352901c5a00..0d0227fc9db48 100644 --- a/br/pkg/conn/BUILD.bazel +++ b/br/pkg/conn/BUILD.bazel @@ -13,9 +13,9 @@ go_library( "//br/pkg/pdutil", "//br/pkg/utils", "//br/pkg/version", - "//config", - "//domain", - "//kv", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", "@com_github_docker_go_units//:go-units", "@com_github_opentracing_opentracing_go//:opentracing-go", "@com_github_pingcap_errors//:errors", @@ -50,7 +50,7 @@ go_test( "//br/pkg/conn/util", "//br/pkg/pdutil", "//br/pkg/utils", - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_docker_go_units//:go-units", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", diff --git a/br/pkg/conn/conn.go b/br/pkg/conn/conn.go index 34ba7608c5b5a..bf67f0d96ddff 100644 --- a/br/pkg/conn/conn.go +++ b/br/pkg/conn/conn.go @@ -27,9 +27,9 @@ import ( "github.com/pingcap/tidb/br/pkg/pdutil" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/txnlock" diff --git a/br/pkg/conn/main_test.go b/br/pkg/conn/main_test.go index a31428bef0ca1..ee998da3e7499 100644 --- a/br/pkg/conn/main_test.go +++ b/br/pkg/conn/main_test.go @@ -17,7 +17,7 @@ package conn import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/conn/util/BUILD.bazel b/br/pkg/conn/util/BUILD.bazel index 8745487800dcb..3411e60373c99 100644 --- a/br/pkg/conn/util/BUILD.bazel +++ b/br/pkg/conn/util/BUILD.bazel @@ -7,7 +7,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//br/pkg/errors", - "//util/engine", + "//pkg/util/engine", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/metapb", "@com_github_tikv_pd_client//:client", diff --git a/br/pkg/conn/util/util.go b/br/pkg/conn/util/util.go index 58f400a231d25..9f8e1531acfd7 100644 --- a/br/pkg/conn/util/util.go +++ b/br/pkg/conn/util/util.go @@ -8,7 +8,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/metapb" berrors "github.com/pingcap/tidb/br/pkg/errors" - "github.com/pingcap/tidb/util/engine" + "github.com/pingcap/tidb/pkg/util/engine" pd "github.com/tikv/pd/client" ) diff --git a/br/pkg/glue/BUILD.bazel b/br/pkg/glue/BUILD.bazel index dc993f47c31d8..cff22faa27ee9 100644 --- a/br/pkg/glue/BUILD.bazel +++ b/br/pkg/glue/BUILD.bazel @@ -11,11 +11,11 @@ go_library( visibility = ["//visibility:public"], deps = [ "//br/pkg/utils", - "//ddl", - "//domain", - "//kv", - "//parser/model", - "//sessionctx", + "//pkg/ddl", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx", "@com_github_fatih_color//:color", "@com_github_tikv_pd_client//:client", "@com_github_vbauerster_mpb_v7//:mpb", diff --git a/br/pkg/glue/glue.go b/br/pkg/glue/glue.go index 0ca9671f0c2b0..086582e680789 100644 --- a/br/pkg/glue/glue.go +++ b/br/pkg/glue/glue.go @@ -5,11 +5,11 @@ package glue import ( "context" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" pd "github.com/tikv/pd/client" ) diff --git a/br/pkg/gluetidb/BUILD.bazel b/br/pkg/gluetidb/BUILD.bazel index 1554061033ae1..979f05b4e5b48 100644 --- a/br/pkg/gluetidb/BUILD.bazel +++ b/br/pkg/gluetidb/BUILD.bazel @@ -9,16 +9,16 @@ go_library( "//br/pkg/glue", "//br/pkg/gluetikv", "//br/pkg/logutil", - "//config", - "//ddl", - "//domain", - "//executor", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//session", - "//sessionctx", + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/executor", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/sessionctx", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", "@com_github_tikv_pd_client//:client", @@ -35,13 +35,13 @@ go_test( shard_count = 4, deps = [ "//br/pkg/glue", - "//ddl", - "//kv", - "//meta", - "//parser/model", - "//sessionctx", - "//testkit", - "//types", + "//pkg/ddl", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/testkit", + "//pkg/types", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", ], diff --git a/br/pkg/gluetidb/glue.go b/br/pkg/gluetidb/glue.go index 264af6db3512a..da42b1b1d9b71 100644 --- a/br/pkg/gluetidb/glue.go +++ b/br/pkg/gluetidb/glue.go @@ -13,16 +13,16 @@ import ( "github.com/pingcap/tidb/br/pkg/glue" "github.com/pingcap/tidb/br/pkg/gluetikv" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" pd "github.com/tikv/pd/client" "go.uber.org/zap" ) diff --git a/br/pkg/gluetidb/glue_test.go b/br/pkg/gluetidb/glue_test.go index e42e28ec52dfc..8c4b3420dc669 100644 --- a/br/pkg/gluetidb/glue_test.go +++ b/br/pkg/gluetidb/glue_test.go @@ -21,13 +21,13 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/glue" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) @@ -139,7 +139,7 @@ func TestSplitBatchCreateTable(t *testing.T) { // keep/reused table id verification tk.Session().SetValue(sessionctx.QueryString, "skip") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/RestoreBatchCreateTableEntryTooLarge", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/RestoreBatchCreateTableEntryTooLarge", "return(1)")) err := se.SplitBatchCreateTable(model.NewCIStr("test"), infos, ddl.AllocTableIDIf(func(ti *model.TableInfo) bool { return false })) @@ -177,7 +177,7 @@ func TestSplitBatchCreateTable(t *testing.T) { tk.MustQuery("select tidb_table_id from information_schema.tables where table_name = 'tables_3'"). Check(testkit.Rows("1236")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/RestoreBatchCreateTableEntryTooLarge")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/RestoreBatchCreateTableEntryTooLarge")) } // batch create table with table id reused @@ -206,14 +206,14 @@ func TestSplitBatchCreateTableFailWithEntryTooLarge(t *testing.T) { se := &tidbSession{se: tk.Session()} tk.Session().SetValue(sessionctx.QueryString, "skip") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/RestoreBatchCreateTableEntryTooLarge", "return(0)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/RestoreBatchCreateTableEntryTooLarge", "return(0)")) err := se.SplitBatchCreateTable(model.NewCIStr("test"), infos, ddl.AllocTableIDIf(func(ti *model.TableInfo) bool { return true })) require.True(t, kv.ErrEntryTooLarge.Equal(err)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/RestoreBatchCreateTableEntryTooLarge")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/RestoreBatchCreateTableEntryTooLarge")) } func TestTheSessionIsoation(t *testing.T) { diff --git a/br/pkg/gluetikv/BUILD.bazel b/br/pkg/gluetikv/BUILD.bazel index 2c82b6d85b40f..d8645f5e8e3d1 100644 --- a/br/pkg/gluetikv/BUILD.bazel +++ b/br/pkg/gluetikv/BUILD.bazel @@ -10,10 +10,10 @@ go_library( "//br/pkg/summary", "//br/pkg/utils", "//br/pkg/version/build", - "//config", - "//domain", - "//kv", - "//store/driver", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/store/driver", "@com_github_tikv_pd_client//:client", ], ) diff --git a/br/pkg/gluetikv/glue.go b/br/pkg/gluetikv/glue.go index a8c020528c771..ad3ae045f478f 100644 --- a/br/pkg/gluetikv/glue.go +++ b/br/pkg/gluetikv/glue.go @@ -9,10 +9,10 @@ import ( "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/driver" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/driver" pd "github.com/tikv/pd/client" ) diff --git a/br/pkg/lightning/BUILD.bazel b/br/pkg/lightning/BUILD.bazel index 7721acebf1551..b2f8c1bbd24f5 100644 --- a/br/pkg/lightning/BUILD.bazel +++ b/br/pkg/lightning/BUILD.bazel @@ -26,10 +26,10 @@ go_library( "//br/pkg/storage", "//br/pkg/utils", "//br/pkg/version/build", - "//expression", - "//planner/core", - "//util", - "//util/promutil", + "//pkg/expression", + "//pkg/planner/core", + "//pkg/util", + "//pkg/util/promutil", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_google_uuid//:uuid", "@com_github_pingcap_errors//:errors", diff --git a/br/pkg/lightning/backend/BUILD.bazel b/br/pkg/lightning/backend/BUILD.bazel index 70472e3ccdc8a..be3d5e324ee3f 100644 --- a/br/pkg/lightning/backend/BUILD.bazel +++ b/br/pkg/lightning/backend/BUILD.bazel @@ -12,7 +12,7 @@ go_library( "//br/pkg/lightning/log", "//br/pkg/lightning/metric", "//br/pkg/lightning/mydump", - "//parser/model", + "//pkg/parser/model", "@com_github_google_uuid//:uuid", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", @@ -30,7 +30,7 @@ go_test( ":backend", "//br/pkg/lightning/backend/encode", "//br/pkg/mock", - "//parser/mysql", + "//pkg/parser/mysql", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_google_uuid//:uuid", "@com_github_pingcap_errors//:errors", diff --git a/br/pkg/lightning/backend/backend.go b/br/pkg/lightning/backend/backend.go index 1c6d77a291a9b..f1ebb9484843c 100644 --- a/br/pkg/lightning/backend/backend.go +++ b/br/pkg/lightning/backend/backend.go @@ -28,7 +28,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/backend_test.go b/br/pkg/lightning/backend/backend_test.go index 0037dc829e25a..74478da6e14c8 100644 --- a/br/pkg/lightning/backend/backend_test.go +++ b/br/pkg/lightning/backend/backend_test.go @@ -12,7 +12,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend" "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/mock" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "go.uber.org/mock/gomock" diff --git a/br/pkg/lightning/backend/encode/BUILD.bazel b/br/pkg/lightning/backend/encode/BUILD.bazel index 32fb115860145..ac740253b5e57 100644 --- a/br/pkg/lightning/backend/encode/BUILD.bazel +++ b/br/pkg/lightning/backend/encode/BUILD.bazel @@ -8,8 +8,8 @@ go_library( deps = [ "//br/pkg/lightning/log", "//br/pkg/lightning/verification", - "//parser/mysql", - "//table", - "//types", + "//pkg/parser/mysql", + "//pkg/table", + "//pkg/types", ], ) diff --git a/br/pkg/lightning/backend/encode/encode.go b/br/pkg/lightning/backend/encode/encode.go index f42cb90d1cd8a..40d1b4cfb71ad 100644 --- a/br/pkg/lightning/backend/encode/encode.go +++ b/br/pkg/lightning/backend/encode/encode.go @@ -19,9 +19,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" ) // EncodingConfig is the configuration for the encoding backend. diff --git a/br/pkg/lightning/backend/external/BUILD.bazel b/br/pkg/lightning/backend/external/BUILD.bazel index 3000bded8943c..07e7bd9747b9b 100644 --- a/br/pkg/lightning/backend/external/BUILD.bazel +++ b/br/pkg/lightning/backend/external/BUILD.bazel @@ -27,12 +27,12 @@ go_library( "//br/pkg/lightning/log", "//br/pkg/membuf", "//br/pkg/storage", - "//kv", - "//sessionctx/variable", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/size", + "//pkg/kv", + "//pkg/sessionctx/variable", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/size", "@com_github_cockroachdb_pebble//:pebble", "@com_github_jfcg_sorty_v2//:sorty", "@com_github_pingcap_errors//:errors", @@ -63,9 +63,9 @@ go_test( "//br/pkg/lightning/backend/kv", "//br/pkg/lightning/common", "//br/pkg/storage", - "//kv", - "//util/codec", - "//util/size", + "//pkg/kv", + "//pkg/util/codec", + "//pkg/util/size", "@com_github_aws_aws_sdk_go//aws", "@com_github_aws_aws_sdk_go//aws/credentials", "@com_github_aws_aws_sdk_go//aws/session", diff --git a/br/pkg/lightning/backend/external/byte_reader.go b/br/pkg/lightning/backend/external/byte_reader.go index 2b272731f7324..d762699e2f658 100644 --- a/br/pkg/lightning/backend/external/byte_reader.go +++ b/br/pkg/lightning/backend/external/byte_reader.go @@ -20,8 +20,8 @@ import ( "github.com/pingcap/tidb/br/pkg/membuf" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/external/engine.go b/br/pkg/lightning/backend/external/engine.go index 0e00c94887035..bf41cd9fd9af6 100644 --- a/br/pkg/lightning/backend/external/engine.go +++ b/br/pkg/lightning/backend/external/engine.go @@ -29,8 +29,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/membuf" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/atomic" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/lightning/backend/external/engine_test.go b/br/pkg/lightning/backend/external/engine_test.go index 6e5a95fdcf474..fd673925f50a2 100644 --- a/br/pkg/lightning/backend/external/engine_test.go +++ b/br/pkg/lightning/backend/external/engine_test.go @@ -26,7 +26,7 @@ import ( "github.com/cockroachdb/pebble" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" ) diff --git a/br/pkg/lightning/backend/external/iter.go b/br/pkg/lightning/backend/external/iter.go index faa449fded8c9..718a8f1143768 100644 --- a/br/pkg/lightning/backend/external/iter.go +++ b/br/pkg/lightning/backend/external/iter.go @@ -22,7 +22,7 @@ import ( "github.com/pingcap/tidb/br/pkg/membuf" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/lightning/backend/external/kv_reader.go b/br/pkg/lightning/backend/external/kv_reader.go index eea698ead7871..7659ecd6bb4f6 100644 --- a/br/pkg/lightning/backend/external/kv_reader.go +++ b/br/pkg/lightning/backend/external/kv_reader.go @@ -21,7 +21,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/external/split_test.go b/br/pkg/lightning/backend/external/split_test.go index ae772e623f2e2..056b360833b18 100644 --- a/br/pkg/lightning/backend/external/split_test.go +++ b/br/pkg/lightning/backend/external/split_test.go @@ -23,7 +23,7 @@ import ( "time" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" ) diff --git a/br/pkg/lightning/backend/external/util.go b/br/pkg/lightning/backend/external/util.go index 6c6aa3e229044..c5e9f6df49d3b 100644 --- a/br/pkg/lightning/backend/external/util.go +++ b/br/pkg/lightning/backend/external/util.go @@ -23,9 +23,9 @@ import ( "strings" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/external/writer.go b/br/pkg/lightning/backend/external/writer.go index b834328fca8f1..b3adefe53508b 100644 --- a/br/pkg/lightning/backend/external/writer.go +++ b/br/pkg/lightning/backend/external/writer.go @@ -31,10 +31,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/membuf" "github.com/pingcap/tidb/br/pkg/storage" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/external/writer_test.go b/br/pkg/lightning/backend/external/writer_test.go index a8f62c655a88e..b3ea9d97887e9 100644 --- a/br/pkg/lightning/backend/external/writer_test.go +++ b/br/pkg/lightning/backend/external/writer_test.go @@ -31,8 +31,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/storage" - dbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/size" + dbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/size" "github.com/stretchr/testify/require" "golang.org/x/exp/rand" ) diff --git a/br/pkg/lightning/backend/kv/BUILD.bazel b/br/pkg/lightning/backend/kv/BUILD.bazel index 72ee0992dfcb2..f104d0aee76ef 100644 --- a/br/pkg/lightning/backend/kv/BUILD.bazel +++ b/br/pkg/lightning/backend/kv/BUILD.bazel @@ -22,20 +22,20 @@ go_library( "//br/pkg/logutil", "//br/pkg/redact", "//br/pkg/utils", - "//expression", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util/chunk", - "//util/mathutil", - "//util/topsql/stmtstats", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mathutil", + "//pkg/util/topsql/stmtstats", "@com_github_docker_go_units//:go-units", "@com_github_pingcap_errors//:errors", "@org_uber_go_zap//:zap", @@ -62,21 +62,21 @@ go_test( "//br/pkg/lightning/common", "//br/pkg/lightning/log", "//br/pkg/lightning/verification", - "//ddl", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//sessionctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util/mock", + "//pkg/ddl", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/mock", "@com_github_docker_go_units//:go-units", "@com_github_stretchr_testify//require", "@org_uber_go_zap//:zap", diff --git a/br/pkg/lightning/backend/kv/allocator.go b/br/pkg/lightning/backend/kv/allocator.go index d99679ff44837..0741500dda9b4 100644 --- a/br/pkg/lightning/backend/kv/allocator.go +++ b/br/pkg/lightning/backend/kv/allocator.go @@ -20,7 +20,7 @@ import ( "context" "sync/atomic" - "github.com/pingcap/tidb/meta/autoid" + "github.com/pingcap/tidb/pkg/meta/autoid" ) // panickingAllocator is an ID allocator which panics on all operations except Rebase diff --git a/br/pkg/lightning/backend/kv/base.go b/br/pkg/lightning/backend/kv/base.go index e4c0dacc2eb2f..ab4c24f53ccb5 100644 --- a/br/pkg/lightning/backend/kv/base.go +++ b/br/pkg/lightning/backend/kv/base.go @@ -24,15 +24,15 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/redact" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/br/pkg/lightning/backend/kv/base_test.go b/br/pkg/lightning/backend/kv/base_test.go index d02956e4016f0..d9e76554293a6 100644 --- a/br/pkg/lightning/backend/kv/base_test.go +++ b/br/pkg/lightning/backend/kv/base_test.go @@ -22,11 +22,11 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/backend/kv/kv2sql.go b/br/pkg/lightning/backend/kv/kv2sql.go index 06a20881c9ea9..66173f9f1e9e4 100644 --- a/br/pkg/lightning/backend/kv/kv2sql.go +++ b/br/pkg/lightning/backend/kv/kv2sql.go @@ -19,12 +19,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" ) // TableKVDecoder is a KVDecoder that decodes the key-value pairs of a table. diff --git a/br/pkg/lightning/backend/kv/kv2sql_test.go b/br/pkg/lightning/backend/kv/kv2sql_test.go index 1a19427e75f86..bda845d92196c 100644 --- a/br/pkg/lightning/backend/kv/kv2sql_test.go +++ b/br/pkg/lightning/backend/kv/kv2sql_test.go @@ -20,15 +20,15 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/backend/kv/session.go b/br/pkg/lightning/backend/kv/session.go index d3b21b1dec561..ac04a462e8618 100644 --- a/br/pkg/lightning/backend/kv/session.go +++ b/br/pkg/lightning/backend/kv/session.go @@ -29,12 +29,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/manual" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/backend/kv/session_test.go b/br/pkg/lightning/backend/kv/session_test.go index a037661cc3b03..ea543fac57b96 100644 --- a/br/pkg/lightning/backend/kv/session_test.go +++ b/br/pkg/lightning/backend/kv/session_test.go @@ -20,7 +20,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/backend/kv/sql2kv.go b/br/pkg/lightning/backend/kv/sql2kv.go index b30452cfa00b5..489bb6e30d824 100644 --- a/br/pkg/lightning/backend/kv/sql2kv.go +++ b/br/pkg/lightning/backend/kv/sql2kv.go @@ -28,15 +28,15 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" //nolint: goimports - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" //nolint: goimports + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" ) type tableKVEncoder struct { diff --git a/br/pkg/lightning/backend/kv/sql2kv_test.go b/br/pkg/lightning/backend/kv/sql2kv_test.go index e1dc1c5d21ccc..4b806d6e790c3 100644 --- a/br/pkg/lightning/backend/kv/sql2kv_test.go +++ b/br/pkg/lightning/backend/kv/sql2kv_test.go @@ -25,20 +25,20 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - _ "github.com/pingcap/tidb/planner/core" // to setup expression.EvalAstExpr. Otherwise we cannot parse the default value - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + _ "github.com/pingcap/tidb/pkg/planner/core" // to setup expression.EvalAstExpr. Otherwise we cannot parse the default value + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/br/pkg/lightning/backend/local/BUILD.bazel b/br/pkg/lightning/backend/local/BUILD.bazel index 9b867d0c972d6..084d050066090 100644 --- a/br/pkg/lightning/backend/local/BUILD.bazel +++ b/br/pkg/lightning/backend/local/BUILD.bazel @@ -44,22 +44,22 @@ go_library( "//br/pkg/storage", "//br/pkg/utils", "//br/pkg/version", - "//distsql", - "//infoschema", - "//kv", - "//parser/model", - "//parser/mysql", - "//sessionctx/variable", - "//store/pdtypes", - "//table", - "//tablecodec", - "//types", - "//util/codec", - "//util/compress", - "//util/engine", - "//util/hack", - "//util/mathutil", - "//util/ranger", + "//pkg/distsql", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/store/pdtypes", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/compress", + "//pkg/util/engine", + "//pkg/util/hack", + "//pkg/util/mathutil", + "//pkg/util/ranger", "@com_github_cockroachdb_pebble//:pebble", "@com_github_cockroachdb_pebble//sstable", "@com_github_coreos_go_semver//semver", @@ -131,24 +131,24 @@ go_test( "//br/pkg/restore/split", "//br/pkg/storage", "//br/pkg/utils", - "//ddl", - "//errno", - "//keyspace", - "//kv", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//store/pdtypes", - "//table/tables", - "//tablecodec", - "//types", - "//util", - "//util/codec", - "//util/engine", - "//util/hack", - "//util/mock", + "//pkg/ddl", + "//pkg/errno", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/store/pdtypes", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/engine", + "//pkg/util/hack", + "//pkg/util/mock", "@com_github_cockroachdb_pebble//:pebble", "@com_github_cockroachdb_pebble//sstable", "@com_github_coreos_go_semver//semver", diff --git a/br/pkg/lightning/backend/local/checksum.go b/br/pkg/lightning/backend/local/checksum.go index 9505e0d39940a..ef0f82d010a4d 100644 --- a/br/pkg/lightning/backend/local/checksum.go +++ b/br/pkg/lightning/backend/local/checksum.go @@ -31,9 +31,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/oracle" diff --git a/br/pkg/lightning/backend/local/checksum_test.go b/br/pkg/lightning/backend/local/checksum_test.go index 3506995514928..4e9334492b051 100644 --- a/br/pkg/lightning/backend/local/checksum_test.go +++ b/br/pkg/lightning/backend/local/checksum_test.go @@ -13,12 +13,12 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" . "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" - tmysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - pmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" + tmysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + pmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" diff --git a/br/pkg/lightning/backend/local/compress.go b/br/pkg/lightning/backend/local/compress.go index 4e5375973feea..0ee75f7fc7874 100644 --- a/br/pkg/lightning/backend/local/compress.go +++ b/br/pkg/lightning/backend/local/compress.go @@ -18,7 +18,7 @@ import ( "io" "github.com/klauspost/compress/gzip" // faster than stdlib - "github.com/pingcap/tidb/util/compress" + "github.com/pingcap/tidb/pkg/util/compress" "google.golang.org/grpc" ) diff --git a/br/pkg/lightning/backend/local/duplicate.go b/br/pkg/lightning/backend/local/duplicate.go index bdd3457ceca79..a9a655e8dd4fb 100644 --- a/br/pkg/lightning/backend/local/duplicate.go +++ b/br/pkg/lightning/backend/local/duplicate.go @@ -40,16 +40,16 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/distsql" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/distsql" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/ranger" tikverror "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/tikv" kvutil "github.com/tikv/client-go/v2/util" diff --git a/br/pkg/lightning/backend/local/duplicate_test.go b/br/pkg/lightning/backend/local/duplicate_test.go index 28cadea5652a0..268e19b8047c6 100644 --- a/br/pkg/lightning/backend/local/duplicate_test.go +++ b/br/pkg/lightning/backend/local/duplicate_test.go @@ -22,14 +22,14 @@ import ( lkv "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/backend/local" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/backend/local/engine.go b/br/pkg/lightning/backend/local/engine.go index 88b6c560562bf..986e6cff57d3b 100644 --- a/br/pkg/lightning/backend/local/engine.go +++ b/br/pkg/lightning/backend/local/engine.go @@ -42,8 +42,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/membuf" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/tikv/client-go/v2/tikv" "go.uber.org/atomic" "go.uber.org/zap" diff --git a/br/pkg/lightning/backend/local/local.go b/br/pkg/lightning/backend/local/local.go index 3114956fa2d50..04fdd7fddf38e 100644 --- a/br/pkg/lightning/backend/local/local.go +++ b/br/pkg/lightning/backend/local/local.go @@ -53,13 +53,13 @@ import ( "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/engine" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/engine" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/tikv/client-go/v2/oracle" tikvclient "github.com/tikv/client-go/v2/tikv" pd "github.com/tikv/pd/client" diff --git a/br/pkg/lightning/backend/local/local_test.go b/br/pkg/lightning/backend/local/local_test.go index 60c34c96c1e69..e86fa421bc531 100644 --- a/br/pkg/lightning/backend/local/local_test.go +++ b/br/pkg/lightning/backend/local/local_test.go @@ -50,14 +50,14 @@ import ( "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/keyspace" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/engine" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/keyspace" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/engine" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" "google.golang.org/grpc" diff --git a/br/pkg/lightning/backend/local/localhelper.go b/br/pkg/lightning/backend/local/localhelper.go index 0daa6e874bd4b..9f4ca3bd3b59e 100644 --- a/br/pkg/lightning/backend/local/localhelper.go +++ b/br/pkg/lightning/backend/local/localhelper.go @@ -40,8 +40,8 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/multierr" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/lightning/backend/local/localhelper_test.go b/br/pkg/lightning/backend/local/localhelper_test.go index 75ab382e2b0b7..80d0980b05e75 100644 --- a/br/pkg/lightning/backend/local/localhelper_test.go +++ b/br/pkg/lightning/backend/local/localhelper_test.go @@ -31,12 +31,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/restore/split" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "go.uber.org/atomic" ) diff --git a/br/pkg/lightning/backend/local/region_job.go b/br/pkg/lightning/backend/local/region_job.go index 83ffa5b446e48..fa67ce39e5a16 100644 --- a/br/pkg/lightning/backend/local/region_job.go +++ b/br/pkg/lightning/backend/local/region_job.go @@ -35,9 +35,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/restore/split" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/br/pkg/lightning/backend/tidb/BUILD.bazel b/br/pkg/lightning/backend/tidb/BUILD.bazel index 7d23f3dbc338f..6e715be18eaaa 100644 --- a/br/pkg/lightning/backend/tidb/BUILD.bazel +++ b/br/pkg/lightning/backend/tidb/BUILD.bazel @@ -17,12 +17,12 @@ go_library( "//br/pkg/redact", "//br/pkg/utils", "//br/pkg/version", - "//errno", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//table", - "//types", + "//pkg/errno", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/table", + "//pkg/types", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_google_uuid//:uuid", "@com_github_pingcap_errors//:errors", @@ -48,13 +48,13 @@ go_test( "//br/pkg/lightning/errormanager", "//br/pkg/lightning/log", "//br/pkg/lightning/verification", - "//errno", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//table", - "//table/tables", - "//types", + "//pkg/errno", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/table", + "//pkg/table/tables", + "//pkg/types", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_stretchr_testify//require", diff --git a/br/pkg/lightning/backend/tidb/tidb.go b/br/pkg/lightning/backend/tidb/tidb.go index 935b8aa36184a..7636e960e498e 100644 --- a/br/pkg/lightning/backend/tidb/tidb.go +++ b/br/pkg/lightning/backend/tidb/tidb.go @@ -38,12 +38,12 @@ import ( "github.com/pingcap/tidb/br/pkg/redact" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/br/pkg/lightning/backend/tidb/tidb_test.go b/br/pkg/lightning/backend/tidb/tidb_test.go index 5d031d31b4448..f869999f00603 100644 --- a/br/pkg/lightning/backend/tidb/tidb_test.go +++ b/br/pkg/lightning/backend/tidb/tidb_test.go @@ -33,13 +33,13 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/errormanager" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "go.uber.org/atomic" ) diff --git a/br/pkg/lightning/checkpoints/BUILD.bazel b/br/pkg/lightning/checkpoints/BUILD.bazel index 4c114e22beb48..ad0e6e373d0f9 100644 --- a/br/pkg/lightning/checkpoints/BUILD.bazel +++ b/br/pkg/lightning/checkpoints/BUILD.bazel @@ -18,12 +18,12 @@ go_library( "//br/pkg/lightning/verification", "//br/pkg/storage", "//br/pkg/version/build", - "//parser/ast", - "//parser/model", - "//types", - "//util/chunk", - "//util/mathutil", - "//util/sqlexec", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mathutil", + "//pkg/util/sqlexec", "@com_github_joho_sqltocsv//:sqltocsv", "@com_github_pingcap_errors//:errors", "@org_uber_go_zap//:zap", @@ -49,8 +49,8 @@ go_test( "//br/pkg/lightning/mydump", "//br/pkg/lightning/verification", "//br/pkg/version/build", - "//parser/model", - "//testkit/testsetup", + "//pkg/parser/model", + "//pkg/testkit/testsetup", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_pingcap_errors//:errors", "@com_github_stretchr_testify//require", diff --git a/br/pkg/lightning/checkpoints/checkpoints.go b/br/pkg/lightning/checkpoints/checkpoints.go index 865cb6d4e759c..4d0328a88ddc3 100644 --- a/br/pkg/lightning/checkpoints/checkpoints.go +++ b/br/pkg/lightning/checkpoints/checkpoints.go @@ -38,8 +38,8 @@ import ( verify "github.com/pingcap/tidb/br/pkg/lightning/verification" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/checkpoints/checkpoints_file_test.go b/br/pkg/lightning/checkpoints/checkpoints_file_test.go index 5051ab3002889..4143d10462d98 100644 --- a/br/pkg/lightning/checkpoints/checkpoints_file_test.go +++ b/br/pkg/lightning/checkpoints/checkpoints_file_test.go @@ -11,7 +11,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/checkpoints/checkpoints_sql_test.go b/br/pkg/lightning/checkpoints/checkpoints_sql_test.go index f03473462b63c..7f62fe0e7ca88 100644 --- a/br/pkg/lightning/checkpoints/checkpoints_sql_test.go +++ b/br/pkg/lightning/checkpoints/checkpoints_sql_test.go @@ -14,7 +14,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/lightning/verification" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/checkpoints/checkpointspb/file_checkpoints.pb.go b/br/pkg/lightning/checkpoints/checkpointspb/file_checkpoints.pb.go index 349ff36fc16dd..957b712794ed2 100644 --- a/br/pkg/lightning/checkpoints/checkpointspb/file_checkpoints.pb.go +++ b/br/pkg/lightning/checkpoints/checkpointspb/file_checkpoints.pb.go @@ -6,11 +6,12 @@ package checkpointspb import ( encoding_binary "encoding/binary" fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/br/pkg/lightning/checkpoints/glue_checkpoint.go b/br/pkg/lightning/checkpoints/glue_checkpoint.go index fa256ab34ab27..a1a8cd96fed3b 100644 --- a/br/pkg/lightning/checkpoints/glue_checkpoint.go +++ b/br/pkg/lightning/checkpoints/glue_checkpoint.go @@ -28,10 +28,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/mydump" verify "github.com/pingcap/tidb/br/pkg/lightning/verification" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/checkpoints/main_test.go b/br/pkg/lightning/checkpoints/main_test.go index a602127696d63..7fa4555bbb116 100644 --- a/br/pkg/lightning/checkpoints/main_test.go +++ b/br/pkg/lightning/checkpoints/main_test.go @@ -17,7 +17,7 @@ package checkpoints_test import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/lightning/checkpoints/tidb.go b/br/pkg/lightning/checkpoints/tidb.go index 4b71187b4afb4..ad342c70c82b0 100644 --- a/br/pkg/lightning/checkpoints/tidb.go +++ b/br/pkg/lightning/checkpoints/tidb.go @@ -15,7 +15,7 @@ package checkpoints import ( - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) // TidbDBInfo is the database info in TiDB. diff --git a/br/pkg/lightning/common/BUILD.bazel b/br/pkg/lightning/common/BUILD.bazel index 46b877b62f15d..725c79ae1f337 100644 --- a/br/pkg/lightning/common/BUILD.bazel +++ b/br/pkg/lightning/common/BUILD.bazel @@ -27,18 +27,18 @@ go_library( "//br/pkg/lightning/log", "//br/pkg/logutil", "//br/pkg/utils", - "//errno", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//sessionctx/variable", - "//store/driver/error", - "//table/tables", - "//types", - "//util", - "//util/codec", - "//util/format", + "//pkg/errno", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/store/driver/error", + "//pkg/table/tables", + "//pkg/types", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/format", "@com_github_cockroachdb_pebble//:pebble", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", @@ -116,19 +116,19 @@ go_test( deps = [ "//br/pkg/errors", "//br/pkg/lightning/log", - "//ddl", - "//errno", - "//kv", - "//meta", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//store/driver/error", - "//store/mockstore", - "//testkit/testsetup", - "//util/dbutil", - "//util/mock", + "//pkg/ddl", + "//pkg/errno", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/store/driver/error", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "//pkg/util/dbutil", + "//pkg/util/mock", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", diff --git a/br/pkg/lightning/common/common.go b/br/pkg/lightning/common/common.go index ede322d241feb..a451cee4328cc 100644 --- a/br/pkg/lightning/common/common.go +++ b/br/pkg/lightning/common/common.go @@ -18,9 +18,9 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" ) const ( diff --git a/br/pkg/lightning/common/common_test.go b/br/pkg/lightning/common/common_test.go index 461523a9f2218..916ee35cb6c03 100644 --- a/br/pkg/lightning/common/common_test.go +++ b/br/pkg/lightning/common/common_test.go @@ -20,15 +20,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - tmock "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + tmock "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/common/key_adapter.go b/br/pkg/lightning/common/key_adapter.go index 5df5837ed8c8c..d2e9ee339075a 100644 --- a/br/pkg/lightning/common/key_adapter.go +++ b/br/pkg/lightning/common/key_adapter.go @@ -18,7 +18,7 @@ import ( "math" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" ) // KeyAdapter is used to encode and decode keys so that duplicate key can be diff --git a/br/pkg/lightning/common/main_test.go b/br/pkg/lightning/common/main_test.go index 8adf839e5f779..476b2537d9743 100644 --- a/br/pkg/lightning/common/main_test.go +++ b/br/pkg/lightning/common/main_test.go @@ -17,7 +17,7 @@ package common_test import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/lightning/common/retry.go b/br/pkg/lightning/common/retry.go index c3bb979a9bd32..19afcd2f90c46 100644 --- a/br/pkg/lightning/common/retry.go +++ b/br/pkg/lightning/common/retry.go @@ -26,8 +26,8 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" - tmysql "github.com/pingcap/tidb/errno" - drivererr "github.com/pingcap/tidb/store/driver/error" + tmysql "github.com/pingcap/tidb/pkg/errno" + drivererr "github.com/pingcap/tidb/pkg/store/driver/error" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/br/pkg/lightning/common/retry_test.go b/br/pkg/lightning/common/retry_test.go index cbfc87085f11a..939f4bb956942 100644 --- a/br/pkg/lightning/common/retry_test.go +++ b/br/pkg/lightning/common/retry_test.go @@ -23,8 +23,8 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" - tmysql "github.com/pingcap/tidb/errno" - drivererr "github.com/pingcap/tidb/store/driver/error" + tmysql "github.com/pingcap/tidb/pkg/errno" + drivererr "github.com/pingcap/tidb/pkg/store/driver/error" "github.com/stretchr/testify/require" "go.uber.org/multierr" "google.golang.org/grpc/codes" diff --git a/br/pkg/lightning/common/security.go b/br/pkg/lightning/common/security.go index 9932b10c8ed9b..543c896e253dd 100644 --- a/br/pkg/lightning/common/security.go +++ b/br/pkg/lightning/common/security.go @@ -24,7 +24,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/httputil" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" "github.com/tikv/client-go/v2/config" pd "github.com/tikv/pd/client" "google.golang.org/grpc" diff --git a/br/pkg/lightning/common/util.go b/br/pkg/lightning/common/util.go index 480e0fd2bfb33..39a0c8f9fa363 100644 --- a/br/pkg/lightning/common/util.go +++ b/br/pkg/lightning/common/util.go @@ -36,14 +36,14 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/format" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/format" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/common/util_test.go b/br/pkg/lightning/common/util_test.go index 3b8b3722fb4e9..7739362720ca0 100644 --- a/br/pkg/lightning/common/util_test.go +++ b/br/pkg/lightning/common/util_test.go @@ -31,8 +31,8 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/dbutil" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/dbutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/config/BUILD.bazel b/br/pkg/lightning/config/BUILD.bazel index 7e15cb366339f..dbb7d17d6e317 100644 --- a/br/pkg/lightning/config/BUILD.bazel +++ b/br/pkg/lightning/config/BUILD.bazel @@ -15,12 +15,12 @@ go_library( "//br/pkg/lightning/common", "//br/pkg/lightning/log", "//br/pkg/version/build", - "//config", - "//parser/mysql", - "//util", - "//util/mathutil", - "//util/table-filter", - "//util/table-router", + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/util", + "//pkg/util/mathutil", + "//pkg/util/table-filter", + "//pkg/util/table-router", "@com_github_burntsushi_toml//:toml", "@com_github_carlmjohnson_flagext//:flagext", "@com_github_docker_go_units//:go-units", diff --git a/br/pkg/lightning/config/config.go b/br/pkg/lightning/config/config.go index 9632b080d08bc..6d71f35795b92 100644 --- a/br/pkg/lightning/config/config.go +++ b/br/pkg/lightning/config/config.go @@ -36,12 +36,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/log" - tidbcfg "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mathutil" - filter "github.com/pingcap/tidb/util/table-filter" - router "github.com/pingcap/tidb/util/table-router" + tidbcfg "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mathutil" + filter "github.com/pingcap/tidb/pkg/util/table-filter" + router "github.com/pingcap/tidb/pkg/util/table-router" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/duplicate/BUILD.bazel b/br/pkg/lightning/duplicate/BUILD.bazel index 4224a96649b26..42c79688c4255 100644 --- a/br/pkg/lightning/duplicate/BUILD.bazel +++ b/br/pkg/lightning/duplicate/BUILD.bazel @@ -11,8 +11,8 @@ go_library( visibility = ["//visibility:public"], deps = [ "//br/pkg/lightning/log", - "//util/codec", - "//util/extsort", + "//pkg/util/codec", + "//pkg/util/extsort", "@com_github_pingcap_errors//:errors", "@org_golang_x_sync//errgroup", "@org_uber_go_zap//:zap", @@ -32,7 +32,7 @@ go_test( shard_count = 4, deps = [ "//br/pkg/lightning/log", - "//util/extsort", + "//pkg/util/extsort", "@com_github_stretchr_testify//require", ], ) diff --git a/br/pkg/lightning/duplicate/detector.go b/br/pkg/lightning/duplicate/detector.go index 21309e0095313..4a0db62cc62a1 100644 --- a/br/pkg/lightning/duplicate/detector.go +++ b/br/pkg/lightning/duplicate/detector.go @@ -22,7 +22,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/util/extsort" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/lightning/duplicate/detector_test.go b/br/pkg/lightning/duplicate/detector_test.go index 91625406e98e1..847d854df31a1 100644 --- a/br/pkg/lightning/duplicate/detector_test.go +++ b/br/pkg/lightning/duplicate/detector_test.go @@ -26,7 +26,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/duplicate" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/util/extsort" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/duplicate/internal.go b/br/pkg/lightning/duplicate/internal.go index 1a9b37c7f2e6e..9fc325270780c 100644 --- a/br/pkg/lightning/duplicate/internal.go +++ b/br/pkg/lightning/duplicate/internal.go @@ -18,7 +18,7 @@ import ( "bytes" "fmt" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" ) type internalKey struct { diff --git a/br/pkg/lightning/duplicate/worker.go b/br/pkg/lightning/duplicate/worker.go index 17de3cb9eeedd..bc2d76a542d87 100644 --- a/br/pkg/lightning/duplicate/worker.go +++ b/br/pkg/lightning/duplicate/worker.go @@ -22,7 +22,7 @@ import ( "sync/atomic" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/util/extsort" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/errormanager/BUILD.bazel b/br/pkg/lightning/errormanager/BUILD.bazel index 8d09690482e56..d099121c777f2 100644 --- a/br/pkg/lightning/errormanager/BUILD.bazel +++ b/br/pkg/lightning/errormanager/BUILD.bazel @@ -14,10 +14,10 @@ go_library( "//br/pkg/logutil", "//br/pkg/redact", "//br/pkg/utils", - "//parser/mysql", - "//table", - "//table/tables", - "//tablecodec", + "//pkg/parser/mysql", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", "@com_github_jedib0t_go_pretty_v6//table", "@com_github_jedib0t_go_pretty_v6//text", "@com_github_pingcap_errors//:errors", @@ -43,10 +43,10 @@ go_test( "//br/pkg/lightning/config", "//br/pkg/lightning/log", "//br/pkg/utils", - "//parser/model", - "//parser/mysql", - "//table/tables", - "//types", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/table/tables", + "//pkg/types", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_stretchr_testify//require", "@org_uber_go_atomic//:atomic", diff --git a/br/pkg/lightning/errormanager/errormanager.go b/br/pkg/lightning/errormanager/errormanager.go index 2985484a99897..9c841bb810beb 100644 --- a/br/pkg/lightning/errormanager/errormanager.go +++ b/br/pkg/lightning/errormanager/errormanager.go @@ -34,10 +34,10 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/redact" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/mysql" - tidbtbl "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/parser/mysql" + tidbtbl "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" tikverr "github.com/tikv/client-go/v2/error" "go.uber.org/atomic" "go.uber.org/multierr" diff --git a/br/pkg/lightning/errormanager/errormanager_test.go b/br/pkg/lightning/errormanager/errormanager_test.go index affce1b2c27a9..83eeaa006866d 100644 --- a/br/pkg/lightning/errormanager/errormanager_test.go +++ b/br/pkg/lightning/errormanager/errormanager_test.go @@ -31,10 +31,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "go.uber.org/atomic" ) diff --git a/br/pkg/lightning/importer/BUILD.bazel b/br/pkg/lightning/importer/BUILD.bazel index 36c36340c09a9..e8d2987c5ff0f 100644 --- a/br/pkg/lightning/importer/BUILD.bazel +++ b/br/pkg/lightning/importer/BUILD.bazel @@ -47,35 +47,35 @@ go_library( "//br/pkg/utils", "//br/pkg/version", "//br/pkg/version/build", - "//config", - "//ddl", - "//errno", - "//keyspace", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//sessionctx/variable", - "//store/driver", - "//store/driver/txn", - "//store/pdtypes", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/engine", - "//util/extsort", - "//util/mathutil", - "//util/mock", - "//util/regexpr-router", - "//util/set", + "//pkg/config", + "//pkg/ddl", + "//pkg/errno", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/sessionctx/variable", + "//pkg/store/driver", + "//pkg/store/driver/txn", + "//pkg/store/pdtypes", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/engine", + "//pkg/util/extsort", + "//pkg/util/mathutil", + "//pkg/util/mock", + "//pkg/util/regexpr-router", + "//pkg/util/set", "@com_github_coreos_go_semver//semver", "@com_github_docker_go_units//:go-units", "@com_github_go_sql_driver_mysql//:mysql", @@ -144,27 +144,27 @@ go_test( "//br/pkg/storage", "//br/pkg/streamhelper", "//br/pkg/version/build", - "//ddl", - "//errno", - "//kv", - "//meta", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//store/mockstore", - "//store/pdtypes", - "//table/tables", - "//tablecodec", - "//types", - "//util/codec", - "//util/dbutil", - "//util/extsort", - "//util/mock", - "//util/promutil", - "//util/table-filter", - "//util/table-router", + "//pkg/ddl", + "//pkg/errno", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/store/mockstore", + "//pkg/store/pdtypes", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/dbutil", + "//pkg/util/extsort", + "//pkg/util/mock", + "//pkg/util/promutil", + "//pkg/util/table-filter", + "//pkg/util/table-router", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_docker_go_units//:go-units", "@com_github_go_sql_driver_mysql//:mysql", diff --git a/br/pkg/lightning/importer/check_info_test.go b/br/pkg/lightning/importer/check_info_test.go index 71e83f388303e..455cc8f87f69f 100644 --- a/br/pkg/lightning/importer/check_info_test.go +++ b/br/pkg/lightning/importer/check_info_test.go @@ -31,13 +31,13 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/precheck" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - tmock "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + tmock "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/importer/checksum_helper.go b/br/pkg/lightning/importer/checksum_helper.go index 655df471f7254..c1dee11e62f4f 100644 --- a/br/pkg/lightning/importer/checksum_helper.go +++ b/br/pkg/lightning/importer/checksum_helper.go @@ -25,7 +25,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/pdutil" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/importer/chunk_process.go b/br/pkg/lightning/importer/chunk_process.go index 41aacb2198b28..f8d56c823e595 100644 --- a/br/pkg/lightning/importer/chunk_process.go +++ b/br/pkg/lightning/importer/chunk_process.go @@ -35,14 +35,14 @@ import ( verify "github.com/pingcap/tidb/br/pkg/lightning/verification" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/extsort" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/importer/chunk_process_test.go b/br/pkg/lightning/importer/chunk_process_test.go index 979b8e31aaa60..2ece12e2eca0f 100644 --- a/br/pkg/lightning/importer/chunk_process_test.go +++ b/br/pkg/lightning/importer/chunk_process_test.go @@ -40,14 +40,14 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - tmock "github.com/pingcap/tidb/util/mock" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + tmock "github.com/pingcap/tidb/pkg/util/mock" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" diff --git a/br/pkg/lightning/importer/dup_detect.go b/br/pkg/lightning/importer/dup_detect.go index 1e72ebaeb6cc9..90445c544eb8a 100644 --- a/br/pkg/lightning/importer/dup_detect.go +++ b/br/pkg/lightning/importer/dup_detect.go @@ -28,12 +28,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/duplicate" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/extsort" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/lightning/importer/dup_detect_test.go b/br/pkg/lightning/importer/dup_detect_test.go index 53f209023acc8..9753fb4ce5e55 100644 --- a/br/pkg/lightning/importer/dup_detect_test.go +++ b/br/pkg/lightning/importer/dup_detect_test.go @@ -21,12 +21,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/duplicate" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbutil" - "github.com/pingcap/tidb/util/extsort" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbutil" + "github.com/pingcap/tidb/pkg/util/extsort" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/importer/get_pre_info.go b/br/pkg/lightning/importer/get_pre_info.go index 6bf00dbd56d2c..d36bd39b4f937 100644 --- a/br/pkg/lightning/importer/get_pre_info.go +++ b/br/pkg/lightning/importer/get_pre_info.go @@ -39,17 +39,17 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/verification" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - _ "github.com/pingcap/tidb/planner/core" // to setup expression.EvalAstExpr. Otherwise we cannot parse the default value - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + _ "github.com/pingcap/tidb/pkg/planner/core" // to setup expression.EvalAstExpr. Otherwise we cannot parse the default value + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/mock" pd "github.com/tikv/pd/client" "go.uber.org/zap" "golang.org/x/exp/maps" diff --git a/br/pkg/lightning/importer/get_pre_info_test.go b/br/pkg/lightning/importer/get_pre_info_test.go index 7fda215beaa85..4639b494ef841 100644 --- a/br/pkg/lightning/importer/get_pre_info_test.go +++ b/br/pkg/lightning/importer/get_pre_info_test.go @@ -30,9 +30,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/importer/mock" ropts "github.com/pingcap/tidb/br/pkg/lightning/importer/opts" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" pqt_buf_src "github.com/xitongsys/parquet-go-source/buffer" pqtwriter "github.com/xitongsys/parquet-go/writer" diff --git a/br/pkg/lightning/importer/import.go b/br/pkg/lightning/importer/import.go index 2f41331b3ce1a..ea5a25458529c 100644 --- a/br/pkg/lightning/importer/import.go +++ b/br/pkg/lightning/importer/import.go @@ -51,18 +51,18 @@ import ( "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" "github.com/pingcap/tidb/br/pkg/version/build" - tidbconfig "github.com/pingcap/tidb/config" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" - regexprrouter "github.com/pingcap/tidb/util/regexpr-router" - "github.com/pingcap/tidb/util/set" + tidbconfig "github.com/pingcap/tidb/pkg/config" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" + regexprrouter "github.com/pingcap/tidb/pkg/util/regexpr-router" + "github.com/pingcap/tidb/pkg/util/set" "github.com/prometheus/client_golang/prometheus" tikvconfig "github.com/tikv/client-go/v2/config" kvutil "github.com/tikv/client-go/v2/util" diff --git a/br/pkg/lightning/importer/import_test.go b/br/pkg/lightning/importer/import_test.go index f311b81177498..c5dd97f3ea530 100644 --- a/br/pkg/lightning/importer/import_test.go +++ b/br/pkg/lightning/importer/import_test.go @@ -30,13 +30,13 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" - tmock "github.com/pingcap/tidb/util/mock" - router "github.com/pingcap/tidb/util/table-router" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" + tmock "github.com/pingcap/tidb/pkg/util/mock" + router "github.com/pingcap/tidb/pkg/util/table-router" "github.com/stretchr/testify/require" tikvconfig "github.com/tikv/client-go/v2/config" ) diff --git a/br/pkg/lightning/importer/meta_manager_test.go b/br/pkg/lightning/importer/meta_manager_test.go index 677fa101389e0..08c23b891c425 100644 --- a/br/pkg/lightning/importer/meta_manager_test.go +++ b/br/pkg/lightning/importer/meta_manager_test.go @@ -16,15 +16,15 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - tmock "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + tmock "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/importer/mock/BUILD.bazel b/br/pkg/lightning/importer/mock/BUILD.bazel index f6a3335d23e80..303d8a87ff01b 100644 --- a/br/pkg/lightning/importer/mock/BUILD.bazel +++ b/br/pkg/lightning/importer/mock/BUILD.bazel @@ -9,11 +9,11 @@ go_library( "//br/pkg/lightning/importer/opts", "//br/pkg/lightning/mydump", "//br/pkg/storage", - "//errno", - "//parser/model", - "//store/pdtypes", - "//util/dbterror", - "//util/filter", + "//pkg/errno", + "//pkg/parser/model", + "//pkg/store/pdtypes", + "//pkg/util/dbterror", + "//pkg/util/filter", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/metapb", ], @@ -27,7 +27,7 @@ go_test( flaky = True, deps = [ "//br/pkg/lightning/importer", - "//parser/model", + "//pkg/parser/model", "@com_github_stretchr_testify//require", ], ) diff --git a/br/pkg/lightning/importer/mock/mock.go b/br/pkg/lightning/importer/mock/mock.go index 6b0809729e1ef..f24fb073b1208 100644 --- a/br/pkg/lightning/importer/mock/mock.go +++ b/br/pkg/lightning/importer/mock/mock.go @@ -22,11 +22,11 @@ import ( ropts "github.com/pingcap/tidb/br/pkg/lightning/importer/opts" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/filter" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/filter" ) // SourceFile defines a mock source file. diff --git a/br/pkg/lightning/importer/mock/mock_test.go b/br/pkg/lightning/importer/mock/mock_test.go index 021821abd10bd..4efb250d6ffc5 100644 --- a/br/pkg/lightning/importer/mock/mock_test.go +++ b/br/pkg/lightning/importer/mock/mock_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/lightning/importer" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/importer/precheck_impl.go b/br/pkg/lightning/importer/precheck_impl.go index ea8c63ff31ffd..380f7b7997797 100644 --- a/br/pkg/lightning/importer/precheck_impl.go +++ b/br/pkg/lightning/importer/precheck_impl.go @@ -39,14 +39,14 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/engine" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/engine" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/set" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/lightning/importer/restore_schema_test.go b/br/pkg/lightning/importer/restore_schema_test.go index 18b7dc00639b2..f6dfbc16ecc6d 100644 --- a/br/pkg/lightning/importer/restore_schema_test.go +++ b/br/pkg/lightning/importer/restore_schema_test.go @@ -28,13 +28,13 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - tmock "github.com/pingcap/tidb/util/mock" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + tmock "github.com/pingcap/tidb/pkg/util/mock" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "go.uber.org/mock/gomock" diff --git a/br/pkg/lightning/importer/table_import.go b/br/pkg/lightning/importer/table_import.go index ae4b5d7f87857..953d5cd8f0abb 100644 --- a/br/pkg/lightning/importer/table_import.go +++ b/br/pkg/lightning/importer/table_import.go @@ -43,16 +43,16 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/web" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/errno" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/extsort" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/errno" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/extsort" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/multierr" "go.uber.org/zap" "google.golang.org/grpc/codes" diff --git a/br/pkg/lightning/importer/table_import_test.go b/br/pkg/lightning/importer/table_import_test.go index 5f7611ed83504..4a01a2632c602 100644 --- a/br/pkg/lightning/importer/table_import_test.go +++ b/br/pkg/lightning/importer/table_import_test.go @@ -55,17 +55,17 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - tmock "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/promutil" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + tmock "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/promutil" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" "github.com/tikv/client-go/v2/testutils" diff --git a/br/pkg/lightning/importer/tidb.go b/br/pkg/lightning/importer/tidb.go index 49bd454464c86..2ca91a1cff535 100644 --- a/br/pkg/lightning/importer/tidb.go +++ b/br/pkg/lightning/importer/tidb.go @@ -29,12 +29,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" "golang.org/x/exp/maps" diff --git a/br/pkg/lightning/importer/tidb_test.go b/br/pkg/lightning/importer/tidb_test.go index 3a08504a0ec0f..81ac54d948562 100644 --- a/br/pkg/lightning/importer/tidb_test.go +++ b/br/pkg/lightning/importer/tidb_test.go @@ -26,14 +26,14 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/lightning.go b/br/pkg/lightning/lightning.go index cf49afa97bef2..3c6d9013d262e 100644 --- a/br/pkg/lightning/lightning.go +++ b/br/pkg/lightning/lightning.go @@ -56,10 +56,10 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - _ "github.com/pingcap/tidb/expression" // get rid of `import cycle`: just init expression.RewriteAstExpr,and called at package `backend.kv`. - _ "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/promutil" + _ "github.com/pingcap/tidb/pkg/expression" // get rid of `import cycle`: just init expression.RewriteAstExpr,and called at package `backend.kv`. + _ "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" diff --git a/br/pkg/lightning/log/BUILD.bazel b/br/pkg/lightning/log/BUILD.bazel index 1544e7e13dea8..215f55094fc4f 100644 --- a/br/pkg/lightning/log/BUILD.bazel +++ b/br/pkg/lightning/log/BUILD.bazel @@ -10,7 +10,7 @@ go_library( importpath = "github.com/pingcap/tidb/br/pkg/lightning/log", visibility = ["//visibility:public"], deps = [ - "//util/logutil", + "//pkg/util/logutil", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", "@org_golang_google_grpc//codes", @@ -33,7 +33,7 @@ go_test( shard_count = 4, deps = [ ":log", - "//util/logutil", + "//pkg/util/logutil", "@com_github_pingcap_log//:log", "@com_github_stretchr_testify//require", "@org_uber_go_zap//:zap", diff --git a/br/pkg/lightning/log/log.go b/br/pkg/lightning/log/log.go index ba9d822296dfb..913e1296dd359 100644 --- a/br/pkg/lightning/log/log.go +++ b/br/pkg/lightning/log/log.go @@ -22,7 +22,7 @@ import ( "github.com/pingcap/errors" pclog "github.com/pingcap/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "go.uber.org/zap/zapcore" "google.golang.org/grpc/codes" diff --git a/br/pkg/lightning/log/log_test.go b/br/pkg/lightning/log/log_test.go index a75f438eb9265..69706d50d9c29 100644 --- a/br/pkg/lightning/log/log_test.go +++ b/br/pkg/lightning/log/log_test.go @@ -21,7 +21,7 @@ import ( zaplog "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/metric/BUILD.bazel b/br/pkg/lightning/metric/BUILD.bazel index 0efda0acaab5b..6db994ea21fb3 100644 --- a/br/pkg/lightning/metric/BUILD.bazel +++ b/br/pkg/lightning/metric/BUILD.bazel @@ -6,7 +6,7 @@ go_library( importpath = "github.com/pingcap/tidb/br/pkg/lightning/metric", visibility = ["//visibility:public"], deps = [ - "//util/promutil", + "//pkg/util/promutil", "@com_github_prometheus_client_golang//prometheus", "@com_github_prometheus_client_model//go", ], @@ -20,7 +20,7 @@ go_test( shard_count = 6, deps = [ ":metric", - "//util/promutil", + "//pkg/util/promutil", "@com_github_prometheus_client_golang//prometheus", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", diff --git a/br/pkg/lightning/metric/metric.go b/br/pkg/lightning/metric/metric.go index 470a1491b3dc6..139a3f36b77d1 100644 --- a/br/pkg/lightning/metric/metric.go +++ b/br/pkg/lightning/metric/metric.go @@ -18,7 +18,7 @@ import ( "context" "math" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" ) diff --git a/br/pkg/lightning/metric/metric_test.go b/br/pkg/lightning/metric/metric_test.go index fa02692eec3c9..734114eff0f45 100644 --- a/br/pkg/lightning/metric/metric_test.go +++ b/br/pkg/lightning/metric/metric_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/lightning/metric" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/br/pkg/lightning/mydump/BUILD.bazel b/br/pkg/lightning/mydump/BUILD.bazel index ad868651261bb..92f1971fb8ab0 100644 --- a/br/pkg/lightning/mydump/BUILD.bazel +++ b/br/pkg/lightning/mydump/BUILD.bazel @@ -23,15 +23,15 @@ go_library( "//br/pkg/lightning/metric", "//br/pkg/lightning/worker", "//br/pkg/storage", - "//config", - "//parser/mysql", - "//types", - "//util/filter", - "//util/mathutil", - "//util/regexpr-router", - "//util/slice", - "//util/table-filter", - "//util/zeropool", + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/filter", + "//pkg/util/mathutil", + "//pkg/util/regexpr-router", + "//pkg/util/slice", + "//pkg/util/table-filter", + "//pkg/util/zeropool", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", "@com_github_spkg_bom//:bom", @@ -76,12 +76,12 @@ go_test( "//br/pkg/lightning/worker", "//br/pkg/mock/storage", "//br/pkg/storage", - "//parser/mysql", - "//testkit/testsetup", - "//types", - "//util/filter", - "//util/table-filter", - "//util/table-router", + "//pkg/parser/mysql", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/filter", + "//pkg/util/table-filter", + "//pkg/util/table-router", "@com_github_pingcap_errors//:errors", "@com_github_stretchr_testify//assert", "@com_github_stretchr_testify//require", diff --git a/br/pkg/lightning/mydump/csv_parser.go b/br/pkg/lightning/mydump/csv_parser.go index ad93b8d885756..5d202689a0884 100644 --- a/br/pkg/lightning/mydump/csv_parser.go +++ b/br/pkg/lightning/mydump/csv_parser.go @@ -27,9 +27,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/worker" - tidbconfig "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + tidbconfig "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" ) var ( diff --git a/br/pkg/lightning/mydump/csv_parser_test.go b/br/pkg/lightning/mydump/csv_parser_test.go index 6c3d9041c71fe..3b6728cd06a41 100644 --- a/br/pkg/lightning/mydump/csv_parser_test.go +++ b/br/pkg/lightning/mydump/csv_parser_test.go @@ -17,7 +17,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/lightning/worker" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" diff --git a/br/pkg/lightning/mydump/loader.go b/br/pkg/lightning/mydump/loader.go index b9b44b372f580..091567463cbf7 100644 --- a/br/pkg/lightning/mydump/loader.go +++ b/br/pkg/lightning/mydump/loader.go @@ -27,8 +27,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/storage" - regexprrouter "github.com/pingcap/tidb/util/regexpr-router" - filter "github.com/pingcap/tidb/util/table-filter" + regexprrouter "github.com/pingcap/tidb/pkg/util/regexpr-router" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/mydump/loader_test.go b/br/pkg/lightning/mydump/loader_test.go index 69c3474d4cd1d..2789eb838a64c 100644 --- a/br/pkg/lightning/mydump/loader_test.go +++ b/br/pkg/lightning/mydump/loader_test.go @@ -30,8 +30,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" md "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/storage" - filter "github.com/pingcap/tidb/util/table-filter" - router "github.com/pingcap/tidb/util/table-router" + filter "github.com/pingcap/tidb/pkg/util/table-filter" + router "github.com/pingcap/tidb/pkg/util/table-router" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/xitongsys/parquet-go/parquet" diff --git a/br/pkg/lightning/mydump/main_test.go b/br/pkg/lightning/mydump/main_test.go index 30f12c270ca3d..57b3db6db90eb 100644 --- a/br/pkg/lightning/mydump/main_test.go +++ b/br/pkg/lightning/mydump/main_test.go @@ -17,7 +17,7 @@ package mydump import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/lightning/mydump/parquet_parser.go b/br/pkg/lightning/mydump/parquet_parser.go index 86614bbfebf5b..ec11532f7b15e 100644 --- a/br/pkg/lightning/mydump/parquet_parser.go +++ b/br/pkg/lightning/mydump/parquet_parser.go @@ -14,7 +14,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" "github.com/xitongsys/parquet-go/parquet" preader "github.com/xitongsys/parquet-go/reader" "github.com/xitongsys/parquet-go/source" diff --git a/br/pkg/lightning/mydump/parquet_parser_test.go b/br/pkg/lightning/mydump/parquet_parser_test.go index 3f099045a8713..18ac1fda12bff 100644 --- a/br/pkg/lightning/mydump/parquet_parser_test.go +++ b/br/pkg/lightning/mydump/parquet_parser_test.go @@ -9,7 +9,7 @@ import ( "time" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/xitongsys/parquet-go-source/local" diff --git a/br/pkg/lightning/mydump/parser.go b/br/pkg/lightning/mydump/parser.go index a2b0d786d785d..89750a6bd14b7 100644 --- a/br/pkg/lightning/mydump/parser.go +++ b/br/pkg/lightning/mydump/parser.go @@ -30,9 +30,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/zeropool" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/zeropool" "github.com/spkg/bom" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/br/pkg/lightning/mydump/parser_test.go b/br/pkg/lightning/mydump/parser_test.go index bd471ccabce15..246a64bea98ab 100644 --- a/br/pkg/lightning/mydump/parser_test.go +++ b/br/pkg/lightning/mydump/parser_test.go @@ -23,8 +23,8 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/mydump/region.go b/br/pkg/lightning/mydump/region.go index 84a31d9ea31f2..0e24530f2364e 100644 --- a/br/pkg/lightning/mydump/region.go +++ b/br/pkg/lightning/mydump/region.go @@ -26,7 +26,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/worker" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/lightning/mydump/router.go b/br/pkg/lightning/mydump/router.go index 164d804632c0a..430fdbacbf426 100644 --- a/br/pkg/lightning/mydump/router.go +++ b/br/pkg/lightning/mydump/router.go @@ -11,8 +11,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/filter" - "github.com/pingcap/tidb/util/slice" + "github.com/pingcap/tidb/pkg/util/filter" + "github.com/pingcap/tidb/pkg/util/slice" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/mydump/router_test.go b/br/pkg/lightning/mydump/router_test.go index ab97769e30ce8..477e05f941fb9 100644 --- a/br/pkg/lightning/mydump/router_test.go +++ b/br/pkg/lightning/mydump/router_test.go @@ -6,7 +6,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/filter" + "github.com/pingcap/tidb/pkg/util/filter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/lightning/run_options.go b/br/pkg/lightning/run_options.go index 6a49b09382331..9e6367759bf0c 100644 --- a/br/pkg/lightning/run_options.go +++ b/br/pkg/lightning/run_options.go @@ -19,7 +19,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/br/pkg/lightning/tikv/BUILD.bazel b/br/pkg/lightning/tikv/BUILD.bazel index 2cc86e75e3511..076999ab0fd51 100644 --- a/br/pkg/lightning/tikv/BUILD.bazel +++ b/br/pkg/lightning/tikv/BUILD.bazel @@ -11,8 +11,8 @@ go_library( "//br/pkg/lightning/log", "//br/pkg/pdutil", "//br/pkg/version", - "//kv", - "//parser/model", + "//pkg/kv", + "//pkg/parser/model", "@com_github_coreos_go_semver//semver", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/debugpb", diff --git a/br/pkg/lightning/tikv/tikv.go b/br/pkg/lightning/tikv/tikv.go index c1a8cf508fd42..22aaf5db4e2b9 100644 --- a/br/pkg/lightning/tikv/tikv.go +++ b/br/pkg/lightning/tikv/tikv.go @@ -30,8 +30,8 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/pdutil" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/logutil/BUILD.bazel b/br/pkg/logutil/BUILD.bazel index 5ad7322e1fd37..40f3d1a35f3fc 100644 --- a/br/pkg/logutil/BUILD.bazel +++ b/br/pkg/logutil/BUILD.bazel @@ -12,7 +12,7 @@ go_library( deps = [ "//br/pkg/lightning/metric", "//br/pkg/redact", - "//kv", + "//pkg/kv", "@com_github_google_uuid//:uuid", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/brpb", diff --git a/br/pkg/logutil/logging.go b/br/pkg/logutil/logging.go index 1727325b84d84..b98330f78c8b6 100644 --- a/br/pkg/logutil/logging.go +++ b/br/pkg/logutil/logging.go @@ -15,7 +15,7 @@ import ( "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/redact" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/br/pkg/metautil/BUILD.bazel b/br/pkg/metautil/BUILD.bazel index b1f9fd1f71fce..4cfcc10d6d4e3 100644 --- a/br/pkg/metautil/BUILD.bazel +++ b/br/pkg/metautil/BUILD.bazel @@ -10,10 +10,10 @@ go_library( "//br/pkg/logutil", "//br/pkg/storage", "//br/pkg/summary", - "//parser/model", - "//statistics/handle/storage", - "//tablecodec", - "//util/encrypt", + "//pkg/parser/model", + "//pkg/statistics/handle/storage", + "//pkg/tablecodec", + "//pkg/util/encrypt", "@com_github_docker_go_units//:go-units", "@com_github_gogo_protobuf//proto", "@com_github_opentracing_opentracing_go//:opentracing-go", @@ -37,7 +37,7 @@ go_test( shard_count = 6, deps = [ "//br/pkg/mock/storage", - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_pingcap_kvproto//pkg/encryptionpb", "@com_github_stretchr_testify//require", diff --git a/br/pkg/metautil/main_test.go b/br/pkg/metautil/main_test.go index 0e215aa6e5331..969f54de2426b 100644 --- a/br/pkg/metautil/main_test.go +++ b/br/pkg/metautil/main_test.go @@ -17,7 +17,7 @@ package metautil import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/metautil/metafile.go b/br/pkg/metautil/metafile.go index ee5512c8f22da..8e4e2995dc607 100644 --- a/br/pkg/metautil/metafile.go +++ b/br/pkg/metautil/metafile.go @@ -23,10 +23,10 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/summary" - "github.com/pingcap/tidb/parser/model" - handle "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/encrypt" + "github.com/pingcap/tidb/pkg/parser/model" + handle "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/encrypt" "go.uber.org/zap" ) diff --git a/br/pkg/mock/BUILD.bazel b/br/pkg/mock/BUILD.bazel index 32ec1a59ac225..f0d1626685ef3 100644 --- a/br/pkg/mock/BUILD.bazel +++ b/br/pkg/mock/BUILD.bazel @@ -16,14 +16,14 @@ go_library( "//br/pkg/lightning/backend", "//br/pkg/lightning/backend/encode", "//br/pkg/lightning/verification", - "//config", - "//domain", - "//kv", - "//parser/model", - "//server", - "//session", - "//store/mockstore", - "//types", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/server", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/types", "@com_github_aws_aws_sdk_go//aws/request", "@com_github_aws_aws_sdk_go//service/s3", "@com_github_go_sql_driver_mysql//:mysql", diff --git a/br/pkg/mock/backend.go b/br/pkg/mock/backend.go index cdf20bb4c5582..cb1d1a484229e 100644 --- a/br/pkg/mock/backend.go +++ b/br/pkg/mock/backend.go @@ -12,7 +12,7 @@ import ( uuid "github.com/google/uuid" backend "github.com/pingcap/tidb/br/pkg/lightning/backend" encode "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" - model "github.com/pingcap/tidb/parser/model" + model "github.com/pingcap/tidb/pkg/parser/model" gomock "go.uber.org/mock/gomock" ) diff --git a/br/pkg/mock/encode.go b/br/pkg/mock/encode.go index b6551a92c2e9a..e92c6ffd17848 100644 --- a/br/pkg/mock/encode.go +++ b/br/pkg/mock/encode.go @@ -10,7 +10,7 @@ import ( encode "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" verification "github.com/pingcap/tidb/br/pkg/lightning/verification" - types "github.com/pingcap/tidb/types" + types "github.com/pingcap/tidb/pkg/types" gomock "go.uber.org/mock/gomock" ) diff --git a/br/pkg/mock/mock_cluster.go b/br/pkg/mock/mock_cluster.go index 41caff5345aa6..0e87d4b411c2d 100644 --- a/br/pkg/mock/mock_cluster.go +++ b/br/pkg/mock/mock_cluster.go @@ -15,12 +15,12 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikv" pd "github.com/tikv/pd/client" diff --git a/br/pkg/pdutil/BUILD.bazel b/br/pkg/pdutil/BUILD.bazel index a1d10737203b7..b941c500fc046 100644 --- a/br/pkg/pdutil/BUILD.bazel +++ b/br/pkg/pdutil/BUILD.bazel @@ -12,9 +12,9 @@ go_library( "//br/pkg/errors", "//br/pkg/httputil", "//br/pkg/lightning/common", - "//store/pdtypes", - "//tablecodec", - "//util/codec", + "//pkg/store/pdtypes", + "//pkg/tablecodec", + "//pkg/util/codec", "@com_github_coreos_go_semver//semver", "@com_github_docker_go_units//:go-units", "@com_github_google_uuid//:uuid", @@ -39,9 +39,9 @@ go_test( flaky = True, shard_count = 8, deps = [ - "//store/pdtypes", - "//testkit/testsetup", - "//util/codec", + "//pkg/store/pdtypes", + "//pkg/testkit/testsetup", + "//pkg/util/codec", "@com_github_coreos_go_semver//semver", "@com_github_pingcap_failpoint//:failpoint", "@com_github_pingcap_kvproto//pkg/metapb", diff --git a/br/pkg/pdutil/main_test.go b/br/pkg/pdutil/main_test.go index 42bd76b1ab388..6a73f877ab289 100644 --- a/br/pkg/pdutil/main_test.go +++ b/br/pkg/pdutil/main_test.go @@ -17,7 +17,7 @@ package pdutil import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/pdutil/pd.go b/br/pkg/pdutil/pd.go index b6533a30a15a2..9d403017c69d6 100644 --- a/br/pkg/pdutil/pd.go +++ b/br/pkg/pdutil/pd.go @@ -29,8 +29,8 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/httputil" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/util/codec" pd "github.com/tikv/pd/client" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/br/pkg/pdutil/pd_serial_test.go b/br/pkg/pdutil/pd_serial_test.go index 32a415ed8800d..32f69106b2139 100644 --- a/br/pkg/pdutil/pd_serial_test.go +++ b/br/pkg/pdutil/pd_serial_test.go @@ -20,8 +20,8 @@ import ( "github.com/coreos/go-semver/semver" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/pdutil/utils.go b/br/pkg/pdutil/utils.go index c74ba76a0ac86..41f13b9edd437 100644 --- a/br/pkg/pdutil/utils.go +++ b/br/pkg/pdutil/utils.go @@ -13,9 +13,9 @@ import ( "github.com/pingcap/errors" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/httputil" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" ) // UndoFunc is a 'undo' operation of some undoable command. diff --git a/br/pkg/restore/BUILD.bazel b/br/pkg/restore/BUILD.bazel index 9e264b1c348f7..7cf76fefe5932 100644 --- a/br/pkg/restore/BUILD.bazel +++ b/br/pkg/restore/BUILD.bazel @@ -47,27 +47,27 @@ go_library( "//br/pkg/utils/iter", "//br/pkg/utils/storewatch", "//br/pkg/version", - "//config", - "//ddl", - "//ddl/util", - "//domain", - "//domain/infosync", - "//kv", - "//meta", - "//parser/model", - "//parser/mysql", - "//sessionctx/variable", - "//statistics/handle", - "//store/helper", - "//store/pdtypes", - "//tablecodec", - "//util", - "//util/codec", - "//util/collate", - "//util/hack", - "//util/mathutil", - "//util/sqlexec", - "//util/table-filter", + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle", + "//pkg/store/helper", + "//pkg/store/pdtypes", + "//pkg/tablecodec", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/mathutil", + "//pkg/util/sqlexec", + "//pkg/util/table-filter", "@com_github_emirpasic_gods//maps/treemap", "@com_github_fatih_color//:color", "@com_github_go_sql_driver_mysql//:mysql", @@ -147,20 +147,20 @@ go_test( "//br/pkg/stream", "//br/pkg/utils", "//br/pkg/utils/iter", - "//infoschema", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//sessionctx/stmtctx", - "//store/pdtypes", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/table-filter", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/sessionctx/stmtctx", + "//pkg/store/pdtypes", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/table-filter", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_golang_protobuf//proto", "@com_github_pingcap_errors//:errors", diff --git a/br/pkg/restore/batcher_test.go b/br/pkg/restore/batcher_test.go index 3a85c8ddc25b7..d6d7cab7e90c7 100644 --- a/br/pkg/restore/batcher_test.go +++ b/br/pkg/restore/batcher_test.go @@ -15,7 +15,7 @@ import ( "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/rtree" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/br/pkg/restore/client.go b/br/pkg/restore/client.go index a2462c80b341c..c79b45673e42a 100644 --- a/br/pkg/restore/client.go +++ b/br/pkg/restore/client.go @@ -45,23 +45,23 @@ import ( "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/utils/iter" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/config" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sqlexec" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/config" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/tikv/client-go/v2/oracle" kvutil "github.com/tikv/client-go/v2/util" pd "github.com/tikv/pd/client" diff --git a/br/pkg/restore/client_test.go b/br/pkg/restore/client_test.go index d9722c42e3abb..eeec487895fe7 100644 --- a/br/pkg/restore/client_test.go +++ b/br/pkg/restore/client_test.go @@ -27,11 +27,11 @@ import ( "github.com/pingcap/tidb/br/pkg/stream" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/utils/iter" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/tablecodec" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/tablecodec" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" "google.golang.org/grpc/keepalive" diff --git a/br/pkg/restore/data.go b/br/pkg/restore/data.go index acbe595b59007..b8c8459df9a6b 100644 --- a/br/pkg/restore/data.go +++ b/br/pkg/restore/data.go @@ -16,8 +16,8 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/utils/storewatch" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/util/mathutil" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/rangetask" diff --git a/br/pkg/restore/db.go b/br/pkg/restore/db.go index 3bbaae026a865..6bf06504eeb31 100644 --- a/br/pkg/restore/db.go +++ b/br/pkg/restore/db.go @@ -15,13 +15,13 @@ import ( "github.com/pingcap/tidb/br/pkg/metautil" prealloctableid "github.com/pingcap/tidb/br/pkg/restore/prealloc_table_id" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - tidbutil "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + tidbutil "github.com/pingcap/tidb/pkg/util" "go.uber.org/zap" ) diff --git a/br/pkg/restore/db_test.go b/br/pkg/restore/db_test.go index 3a5416501e4df..822b922b5010e 100644 --- a/br/pkg/restore/db_test.go +++ b/br/pkg/restore/db_test.go @@ -18,12 +18,12 @@ import ( "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" diff --git a/br/pkg/restore/import.go b/br/pkg/restore/import.go index 62189651fff65..c04737b831df1 100644 --- a/br/pkg/restore/import.go +++ b/br/pkg/restore/import.go @@ -29,8 +29,8 @@ import ( "github.com/pingcap/tidb/br/pkg/stream" "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/codec" kvutil "github.com/tikv/client-go/v2/util" pd "github.com/tikv/pd/client" "go.uber.org/multierr" diff --git a/br/pkg/restore/import_retry_test.go b/br/pkg/restore/import_retry_test.go index 8a8cff0da303b..97d1d10aacae0 100644 --- a/br/pkg/restore/import_retry_test.go +++ b/br/pkg/restore/import_retry_test.go @@ -21,9 +21,9 @@ import ( "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/br/pkg/restore/ingestrec/BUILD.bazel b/br/pkg/restore/ingestrec/BUILD.bazel index 6def98c6ea56c..53b905b4f0ac9 100644 --- a/br/pkg/restore/ingestrec/BUILD.bazel +++ b/br/pkg/restore/ingestrec/BUILD.bazel @@ -6,8 +6,8 @@ go_library( importpath = "github.com/pingcap/tidb/br/pkg/restore/ingestrec", visibility = ["//visibility:public"], deps = [ - "//parser/model", - "//types", + "//pkg/parser/model", + "//pkg/types", "@com_github_pingcap_errors//:errors", ], ) @@ -20,7 +20,7 @@ go_test( shard_count = 3, deps = [ ":ingestrec", - "//parser/model", + "//pkg/parser/model", "@com_github_pkg_errors//:errors", "@com_github_stretchr_testify//require", ], diff --git a/br/pkg/restore/ingestrec/ingest_recorder.go b/br/pkg/restore/ingestrec/ingest_recorder.go index deaad0dec0ef4..277a031b050d3 100644 --- a/br/pkg/restore/ingestrec/ingest_recorder.go +++ b/br/pkg/restore/ingestrec/ingest_recorder.go @@ -19,8 +19,8 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" ) // IngestIndexInfo records the information used to generate index drop/re-add SQL. diff --git a/br/pkg/restore/ingestrec/ingest_recorder_test.go b/br/pkg/restore/ingestrec/ingest_recorder_test.go index 683d3e008bb9b..1c31e9f8ee1c1 100644 --- a/br/pkg/restore/ingestrec/ingest_recorder_test.go +++ b/br/pkg/restore/ingestrec/ingest_recorder_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/restore/ingestrec" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/pkg/errors" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/log_client.go b/br/pkg/restore/log_client.go index 21ccd6721471e..f01b736a712ab 100644 --- a/br/pkg/restore/log_client.go +++ b/br/pkg/restore/log_client.go @@ -17,7 +17,7 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/stream" "github.com/pingcap/tidb/br/pkg/utils/iter" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "go.uber.org/zap" ) diff --git a/br/pkg/restore/main_test.go b/br/pkg/restore/main_test.go index ce00b4abe1400..7f22f2e41d7af 100644 --- a/br/pkg/restore/main_test.go +++ b/br/pkg/restore/main_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/mock" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/restore/merge.go b/br/pkg/restore/merge.go index fadcc43e16d8a..c9b072a2184cc 100644 --- a/br/pkg/restore/merge.go +++ b/br/pkg/restore/merge.go @@ -11,8 +11,8 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/rtree" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/tablecodec" ) const ( diff --git a/br/pkg/restore/merge_fuzz_test.go b/br/pkg/restore/merge_fuzz_test.go index e47f53524d1f8..aec86b850bb36 100644 --- a/br/pkg/restore/merge_fuzz_test.go +++ b/br/pkg/restore/merge_fuzz_test.go @@ -9,7 +9,7 @@ import ( backup "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/rtree" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/tablecodec" ) func FuzzMerge(f *testing.F) { diff --git a/br/pkg/restore/merge_test.go b/br/pkg/restore/merge_test.go index 6b8f38f75cf98..c53cbb0f80771 100644 --- a/br/pkg/restore/merge_test.go +++ b/br/pkg/restore/merge_test.go @@ -15,10 +15,10 @@ import ( "github.com/pingcap/tidb/br/pkg/conn" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/restore" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/pipeline_items.go b/br/pkg/restore/pipeline_items.go index e3c3c008fb10c..2d8fe440e34bc 100644 --- a/br/pkg/restore/pipeline_items.go +++ b/br/pkg/restore/pipeline_items.go @@ -16,7 +16,7 @@ import ( "github.com/pingcap/tidb/br/pkg/rtree" "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/restore/prealloc_table_id/BUILD.bazel b/br/pkg/restore/prealloc_table_id/BUILD.bazel index b8c69a206ad21..9496362873763 100644 --- a/br/pkg/restore/prealloc_table_id/BUILD.bazel +++ b/br/pkg/restore/prealloc_table_id/BUILD.bazel @@ -7,7 +7,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//br/pkg/metautil", - "//parser/model", + "//pkg/parser/model", ], ) @@ -20,7 +20,7 @@ go_test( deps = [ ":prealloc_table_id", "//br/pkg/metautil", - "//parser/model", + "//pkg/parser/model", "@com_github_stretchr_testify//require", ], ) diff --git a/br/pkg/restore/prealloc_table_id/alloc.go b/br/pkg/restore/prealloc_table_id/alloc.go index 8554de5e9891b..bafccac362f6f 100644 --- a/br/pkg/restore/prealloc_table_id/alloc.go +++ b/br/pkg/restore/prealloc_table_id/alloc.go @@ -7,7 +7,7 @@ import ( "math" "github.com/pingcap/tidb/br/pkg/metautil" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) const ( diff --git a/br/pkg/restore/prealloc_table_id/alloc_test.go b/br/pkg/restore/prealloc_table_id/alloc_test.go index c1c3f018a2de8..7fc05f943c04a 100644 --- a/br/pkg/restore/prealloc_table_id/alloc_test.go +++ b/br/pkg/restore/prealloc_table_id/alloc_test.go @@ -8,7 +8,7 @@ import ( "github.com/pingcap/tidb/br/pkg/metautil" prealloctableid "github.com/pingcap/tidb/br/pkg/restore/prealloc_table_id" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/range.go b/br/pkg/restore/range.go index f1d5ed4dce693..874398f1174e4 100644 --- a/br/pkg/restore/range.go +++ b/br/pkg/restore/range.go @@ -9,7 +9,7 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/rtree" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/tablecodec" "go.uber.org/zap" ) diff --git a/br/pkg/restore/range_test.go b/br/pkg/restore/range_test.go index 362bc0e6398b7..a03271de3da03 100644 --- a/br/pkg/restore/range_test.go +++ b/br/pkg/restore/range_test.go @@ -8,7 +8,7 @@ import ( "github.com/pingcap/kvproto/pkg/import_sstpb" "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/rtree" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/tablecodec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/rawkv_client.go b/br/pkg/restore/rawkv_client.go index 2495a8a2922ce..0b897a3ebca9b 100644 --- a/br/pkg/restore/rawkv_client.go +++ b/br/pkg/restore/rawkv_client.go @@ -7,7 +7,7 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/tikv/client-go/v2/config" "github.com/tikv/client-go/v2/rawkv" pd "github.com/tikv/pd/client" diff --git a/br/pkg/restore/rawkv_client_test.go b/br/pkg/restore/rawkv_client_test.go index 1458ce04ff822..8ddc4bab9081a 100644 --- a/br/pkg/restore/rawkv_client_test.go +++ b/br/pkg/restore/rawkv_client_test.go @@ -11,8 +11,8 @@ import ( "github.com/pingcap/errors" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/restore" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/rawkv" ) diff --git a/br/pkg/restore/search.go b/br/pkg/restore/search.go index 65078f5b6af1e..59f01687cc17e 100644 --- a/br/pkg/restore/search.go +++ b/br/pkg/restore/search.go @@ -18,7 +18,7 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/stream" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/restore/search_test.go b/br/pkg/restore/search_test.go index 6af96b10b3536..7fed62cdf8d10 100644 --- a/br/pkg/restore/search_test.go +++ b/br/pkg/restore/search_test.go @@ -14,7 +14,7 @@ import ( backuppb "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/stream" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/split.go b/br/pkg/restore/split.go index e218313f2212f..c50a330c658a6 100644 --- a/br/pkg/restore/split.go +++ b/br/pkg/restore/split.go @@ -23,8 +23,8 @@ import ( "github.com/pingcap/tidb/br/pkg/rtree" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/utils/iter" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "go.uber.org/multierr" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/restore/split/BUILD.bazel b/br/pkg/restore/split/BUILD.bazel index 236f789173017..41969eb7324cc 100644 --- a/br/pkg/restore/split/BUILD.bazel +++ b/br/pkg/restore/split/BUILD.bazel @@ -18,8 +18,8 @@ go_library( "//br/pkg/logutil", "//br/pkg/redact", "//br/pkg/utils", - "//kv", - "//store/pdtypes", + "//pkg/kv", + "//pkg/store/pdtypes", "@com_github_google_btree//:btree", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", diff --git a/br/pkg/restore/split/client.go b/br/pkg/restore/split/client.go index 7a4d96f357388..1b471e42e201a 100644 --- a/br/pkg/restore/split/client.go +++ b/br/pkg/restore/split/client.go @@ -29,7 +29,7 @@ import ( "github.com/pingcap/tidb/br/pkg/httputil" "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/store/pdtypes" + "github.com/pingcap/tidb/pkg/store/pdtypes" pd "github.com/tikv/pd/client" "go.uber.org/multierr" "go.uber.org/zap" diff --git a/br/pkg/restore/split/sum_sorted.go b/br/pkg/restore/split/sum_sorted.go index c4e9657900e35..1ab51588ba6ca 100644 --- a/br/pkg/restore/split/sum_sorted.go +++ b/br/pkg/restore/split/sum_sorted.go @@ -8,7 +8,7 @@ import ( "github.com/google/btree" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // Value is the value type of stored in the span tree. diff --git a/br/pkg/restore/split_test.go b/br/pkg/restore/split_test.go index 3ceef8bf4fdb7..3afb9ec21dc99 100644 --- a/br/pkg/restore/split_test.go +++ b/br/pkg/restore/split_test.go @@ -24,10 +24,10 @@ import ( "github.com/pingcap/tidb/br/pkg/rtree" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/utils/iter" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "go.uber.org/multierr" "google.golang.org/grpc/codes" diff --git a/br/pkg/restore/stream_metas.go b/br/pkg/restore/stream_metas.go index 87e576fe18c40..6d2889111a74e 100644 --- a/br/pkg/restore/stream_metas.go +++ b/br/pkg/restore/stream_metas.go @@ -16,7 +16,7 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/stream" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/restore/systable_restore.go b/br/pkg/restore/systable_restore.go index 39f6921206a40..409a4243c7121 100644 --- a/br/pkg/restore/systable_restore.go +++ b/br/pkg/restore/systable_restore.go @@ -12,9 +12,9 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "go.uber.org/multierr" "go.uber.org/zap" ) diff --git a/br/pkg/restore/systable_restore_test.go b/br/pkg/restore/systable_restore_test.go index 2371f066a43a1..0b1c089048297 100644 --- a/br/pkg/restore/systable_restore_test.go +++ b/br/pkg/restore/systable_restore_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/tiflashrec/BUILD.bazel b/br/pkg/restore/tiflashrec/BUILD.bazel index 4c2b705a56828..44e9ece230e8a 100644 --- a/br/pkg/restore/tiflashrec/BUILD.bazel +++ b/br/pkg/restore/tiflashrec/BUILD.bazel @@ -8,10 +8,10 @@ go_library( deps = [ "//br/pkg/logutil", "//br/pkg/utils", - "//infoschema", - "//parser/ast", - "//parser/format", - "//parser/model", + "//pkg/infoschema", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", "@com_github_pingcap_log//:log", "@org_uber_go_zap//:zap", ], @@ -25,8 +25,8 @@ go_test( shard_count = 3, deps = [ ":tiflashrec", - "//infoschema", - "//parser/model", + "//pkg/infoschema", + "//pkg/parser/model", "@com_github_stretchr_testify//require", ], ) diff --git a/br/pkg/restore/tiflashrec/tiflash_recorder.go b/br/pkg/restore/tiflashrec/tiflash_recorder.go index e739c5a5bc3d6..e540704174070 100644 --- a/br/pkg/restore/tiflashrec/tiflash_recorder.go +++ b/br/pkg/restore/tiflashrec/tiflash_recorder.go @@ -21,10 +21,10 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" "go.uber.org/zap" ) diff --git a/br/pkg/restore/tiflashrec/tiflash_recorder_test.go b/br/pkg/restore/tiflashrec/tiflash_recorder_test.go index f7316a1ed3133..b729fd97ea4ec 100644 --- a/br/pkg/restore/tiflashrec/tiflash_recorder_test.go +++ b/br/pkg/restore/tiflashrec/tiflash_recorder_test.go @@ -19,8 +19,8 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/restore/tiflashrec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/restore/util.go b/br/pkg/restore/util.go index c1366aa67c472..a94aa4f24f29c 100644 --- a/br/pkg/restore/util.go +++ b/br/pkg/restore/util.go @@ -24,9 +24,9 @@ import ( "github.com/pingcap/tidb/br/pkg/restore/split" "github.com/pingcap/tidb/br/pkg/rtree" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/br/pkg/restore/util_test.go b/br/pkg/restore/util_test.go index fd2973f9b1745..43c16a656ed73 100644 --- a/br/pkg/restore/util_test.go +++ b/br/pkg/restore/util_test.go @@ -16,9 +16,9 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/restore/split" - "github.com/pingcap/tidb/store/pdtypes" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/store/pdtypes" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/rtree/BUILD.bazel b/br/pkg/rtree/BUILD.bazel index 0070cb3917051..2b17afb100a22 100644 --- a/br/pkg/rtree/BUILD.bazel +++ b/br/pkg/rtree/BUILD.bazel @@ -32,7 +32,7 @@ go_test( shard_count = 3, deps = [ ":rtree", - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_stretchr_testify//require", "@org_uber_go_goleak//:goleak", diff --git a/br/pkg/rtree/main_test.go b/br/pkg/rtree/main_test.go index 23ffa41059682..8a562ca4ee33a 100644 --- a/br/pkg/rtree/main_test.go +++ b/br/pkg/rtree/main_test.go @@ -17,7 +17,7 @@ package rtree_test import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/storage/BUILD.bazel b/br/pkg/storage/BUILD.bazel index 63f11fb1a6b00..0396098a6fd96 100644 --- a/br/pkg/storage/BUILD.bazel +++ b/br/pkg/storage/BUILD.bazel @@ -25,8 +25,8 @@ go_library( "//br/pkg/errors", "//br/pkg/lightning/log", "//br/pkg/logutil", - "//sessionctx/variable", - "//util/intest", + "//pkg/sessionctx/variable", + "//pkg/util/intest", "@com_github_aliyun_alibaba_cloud_sdk_go//sdk/auth/credentials", "@com_github_aliyun_alibaba_cloud_sdk_go//sdk/auth/credentials/providers", "@com_github_aws_aws_sdk_go//aws", diff --git a/br/pkg/storage/helper.go b/br/pkg/storage/helper.go index 5aa17241b5b4f..4f09e341e0067 100644 --- a/br/pkg/storage/helper.go +++ b/br/pkg/storage/helper.go @@ -5,8 +5,8 @@ package storage import ( "context" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/intest" ) func init() { diff --git a/br/pkg/storage/storage.go b/br/pkg/storage/storage.go index 2be4daff3a4bb..ab8b14128b442 100644 --- a/br/pkg/storage/storage.go +++ b/br/pkg/storage/storage.go @@ -12,7 +12,7 @@ import ( backuppb "github.com/pingcap/kvproto/pkg/brpb" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/util/intest" "go.uber.org/zap" ) diff --git a/br/pkg/stream/BUILD.bazel b/br/pkg/stream/BUILD.bazel index 7abbadeb1c0fd..fc013f01bd3a1 100644 --- a/br/pkg/stream/BUILD.bazel +++ b/br/pkg/stream/BUILD.bazel @@ -22,13 +22,13 @@ go_library( "//br/pkg/storage", "//br/pkg/streamhelper", "//br/pkg/utils", - "//kv", - "//meta", - "//parser/model", - "//tablecodec", - "//util", - "//util/codec", - "//util/table-filter", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/tablecodec", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/table-filter", "@com_github_fatih_color//:color", "@com_github_klauspost_compress//zstd", "@com_github_pingcap_errors//:errors", @@ -58,14 +58,14 @@ go_test( deps = [ "//br/pkg/storage", "//br/pkg/streamhelper", - "//meta", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//tablecodec", - "//types", - "//util/codec", - "//util/table-filter", + "//pkg/meta", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/table-filter", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_stretchr_testify//require", "@com_github_tikv_client_go_v2//oracle", diff --git a/br/pkg/stream/meta_kv.go b/br/pkg/stream/meta_kv.go index fb7c2f79f17d1..590c16b40aff3 100644 --- a/br/pkg/stream/meta_kv.go +++ b/br/pkg/stream/meta_kv.go @@ -17,9 +17,9 @@ package stream import ( "github.com/pingcap/errors" berrors "github.com/pingcap/tidb/br/pkg/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" ) // RawMetaKey specified a transaction meta key. diff --git a/br/pkg/stream/meta_kv_test.go b/br/pkg/stream/meta_kv_test.go index 7a8c5e4fed8b6..0ac5b54763022 100644 --- a/br/pkg/stream/meta_kv_test.go +++ b/br/pkg/stream/meta_kv_test.go @@ -6,9 +6,9 @@ import ( "bytes" "testing" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/stream/rewrite_meta_rawkv.go b/br/pkg/stream/rewrite_meta_rawkv.go index 9913ba852182b..55086f17d99d4 100644 --- a/br/pkg/stream/rewrite_meta_rawkv.go +++ b/br/pkg/stream/rewrite_meta_rawkv.go @@ -25,10 +25,10 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/restore/ingestrec" "github.com/pingcap/tidb/br/pkg/restore/tiflashrec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "go.uber.org/zap" ) diff --git a/br/pkg/stream/rewrite_meta_rawkv_test.go b/br/pkg/stream/rewrite_meta_rawkv_test.go index 752796c2ac574..d09e137ddae61 100644 --- a/br/pkg/stream/rewrite_meta_rawkv_test.go +++ b/br/pkg/stream/rewrite_meta_rawkv_test.go @@ -7,12 +7,12 @@ import ( "encoding/json" "testing" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/stream/stream_mgr.go b/br/pkg/stream/stream_mgr.go index 42e8bf4459d6c..3292edd683cd6 100644 --- a/br/pkg/stream/stream_mgr.go +++ b/br/pkg/stream/stream_mgr.go @@ -24,12 +24,12 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/br/pkg/streamhelper/BUILD.bazel b/br/pkg/streamhelper/BUILD.bazel index 19926e96aa0df..e1eb7adfb95ba 100644 --- a/br/pkg/streamhelper/BUILD.bazel +++ b/br/pkg/streamhelper/BUILD.bazel @@ -23,13 +23,13 @@ go_library( "//br/pkg/streamhelper/config", "//br/pkg/streamhelper/spans", "//br/pkg/utils", - "//config", - "//kv", - "//metrics", - "//owner", - "//util/codec", - "//util/engine", - "//util/mathutil", + "//pkg/config", + "//pkg/kv", + "//pkg/metrics", + "//pkg/owner", + "//pkg/util/codec", + "//pkg/util/engine", + "//pkg/util/mathutil", "@com_github_gogo_protobuf//proto", "@com_github_golang_protobuf//proto", "@com_github_google_uuid//:uuid", @@ -78,9 +78,9 @@ go_test( "//br/pkg/streamhelper/config", "//br/pkg/streamhelper/spans", "//br/pkg/utils", - "//kv", - "//tablecodec", - "//util/codec", + "//pkg/kv", + "//pkg/tablecodec", + "//pkg/util/codec", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", "@com_github_pingcap_kvproto//pkg/brpb", diff --git a/br/pkg/streamhelper/advancer.go b/br/pkg/streamhelper/advancer.go index 51b60bc04632c..81e1559147510 100644 --- a/br/pkg/streamhelper/advancer.go +++ b/br/pkg/streamhelper/advancer.go @@ -19,8 +19,8 @@ import ( "github.com/pingcap/tidb/br/pkg/streamhelper/config" "github.com/pingcap/tidb/br/pkg/streamhelper/spans" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" diff --git a/br/pkg/streamhelper/advancer_cliext.go b/br/pkg/streamhelper/advancer_cliext.go index 891625486b26b..d0593550d537e 100644 --- a/br/pkg/streamhelper/advancer_cliext.go +++ b/br/pkg/streamhelper/advancer_cliext.go @@ -18,7 +18,7 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/redact" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/br/pkg/streamhelper/advancer_daemon.go b/br/pkg/streamhelper/advancer_daemon.go index 5bac78fe83604..c202a1b44f820 100644 --- a/br/pkg/streamhelper/advancer_daemon.go +++ b/br/pkg/streamhelper/advancer_daemon.go @@ -7,8 +7,8 @@ import ( "github.com/google/uuid" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/br/pkg/streamhelper/advancer_env.go b/br/pkg/streamhelper/advancer_env.go index 7707783c495f1..a7f835b847394 100644 --- a/br/pkg/streamhelper/advancer_env.go +++ b/br/pkg/streamhelper/advancer_env.go @@ -8,8 +8,8 @@ import ( logbackup "github.com/pingcap/kvproto/pkg/logbackuppb" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/util/engine" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util/engine" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/txnlock" pd "github.com/tikv/pd/client" diff --git a/br/pkg/streamhelper/advancer_test.go b/br/pkg/streamhelper/advancer_test.go index 3d6fdcee79cba..fe34b56d08be3 100644 --- a/br/pkg/streamhelper/advancer_test.go +++ b/br/pkg/streamhelper/advancer_test.go @@ -16,7 +16,7 @@ import ( "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/streamhelper/config" "github.com/pingcap/tidb/br/pkg/streamhelper/spans" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/txnlock" diff --git a/br/pkg/streamhelper/basic_lib_for_test.go b/br/pkg/streamhelper/basic_lib_for_test.go index 3ee3393535626..59490e32ec7b9 100644 --- a/br/pkg/streamhelper/basic_lib_for_test.go +++ b/br/pkg/streamhelper/basic_lib_for_test.go @@ -26,8 +26,8 @@ import ( "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/streamhelper/spans" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" "github.com/tikv/client-go/v2/txnkv/txnlock" diff --git a/br/pkg/streamhelper/client.go b/br/pkg/streamhelper/client.go index 3a004fc80d3e1..7fdd49f79e5f2 100644 --- a/br/pkg/streamhelper/client.go +++ b/br/pkg/streamhelper/client.go @@ -14,8 +14,8 @@ import ( "github.com/pingcap/log" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/redact" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/mathutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/br/pkg/streamhelper/collector.go b/br/pkg/streamhelper/collector.go index bc9285e05e8a8..8f11e419678fa 100644 --- a/br/pkg/streamhelper/collector.go +++ b/br/pkg/streamhelper/collector.go @@ -15,8 +15,8 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" "go.uber.org/zap" ) diff --git a/br/pkg/streamhelper/daemon/BUILD.bazel b/br/pkg/streamhelper/daemon/BUILD.bazel index 751ade885e324..91637a6f0cc9c 100644 --- a/br/pkg/streamhelper/daemon/BUILD.bazel +++ b/br/pkg/streamhelper/daemon/BUILD.bazel @@ -10,7 +10,7 @@ go_library( visibility = ["//visibility:public"], deps = [ "//br/pkg/logutil", - "//owner", + "//pkg/owner", "@com_github_pingcap_log//:log", "@org_uber_go_zap//:zap", ], @@ -23,7 +23,7 @@ go_test( flaky = True, deps = [ ":daemon", - "//owner", + "//pkg/owner", "@com_github_pingcap_log//:log", "@com_github_stretchr_testify//require", ], diff --git a/br/pkg/streamhelper/daemon/owner_daemon.go b/br/pkg/streamhelper/daemon/owner_daemon.go index 00f577f592507..5956b643c971d 100644 --- a/br/pkg/streamhelper/daemon/owner_daemon.go +++ b/br/pkg/streamhelper/daemon/owner_daemon.go @@ -8,7 +8,7 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/owner" + "github.com/pingcap/tidb/pkg/owner" "go.uber.org/zap" ) diff --git a/br/pkg/streamhelper/daemon/owner_daemon_test.go b/br/pkg/streamhelper/daemon/owner_daemon_test.go index 02a1f7b67d741..e7693a4c1d124 100644 --- a/br/pkg/streamhelper/daemon/owner_daemon_test.go +++ b/br/pkg/streamhelper/daemon/owner_daemon_test.go @@ -10,7 +10,7 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/streamhelper/daemon" - "github.com/pingcap/tidb/owner" + "github.com/pingcap/tidb/pkg/owner" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/streamhelper/flush_subscriber.go b/br/pkg/streamhelper/flush_subscriber.go index 18cf94cba7d6d..7db4647057bad 100644 --- a/br/pkg/streamhelper/flush_subscriber.go +++ b/br/pkg/streamhelper/flush_subscriber.go @@ -15,8 +15,8 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/streamhelper/spans" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/codec" "go.uber.org/multierr" "go.uber.org/zap" "google.golang.org/grpc/codes" diff --git a/br/pkg/streamhelper/integration_test.go b/br/pkg/streamhelper/integration_test.go index 4b989dc4ab2ba..f856ca74d14a4 100644 --- a/br/pkg/streamhelper/integration_test.go +++ b/br/pkg/streamhelper/integration_test.go @@ -21,7 +21,7 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/streamhelper" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/tablecodec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/kv" clientv3 "go.etcd.io/etcd/client/v3" diff --git a/br/pkg/streamhelper/models.go b/br/pkg/streamhelper/models.go index 327704b3e1db5..60f2164afe2f3 100644 --- a/br/pkg/streamhelper/models.go +++ b/br/pkg/streamhelper/models.go @@ -12,7 +12,7 @@ import ( backuppb "github.com/pingcap/kvproto/pkg/brpb" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "go.uber.org/zap" ) diff --git a/br/pkg/streamhelper/prefix_scanner.go b/br/pkg/streamhelper/prefix_scanner.go index c06b3b9a26867..f3c4dfac26199 100644 --- a/br/pkg/streamhelper/prefix_scanner.go +++ b/br/pkg/streamhelper/prefix_scanner.go @@ -6,7 +6,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/redact" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" kvutil "github.com/tikv/client-go/v2/kv" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/br/pkg/streamhelper/regioniter.go b/br/pkg/streamhelper/regioniter.go index a741824ea36c2..2fd510ab0df8f 100644 --- a/br/pkg/streamhelper/regioniter.go +++ b/br/pkg/streamhelper/regioniter.go @@ -15,8 +15,8 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/redact" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" ) const ( diff --git a/br/pkg/streamhelper/regioniter_test.go b/br/pkg/streamhelper/regioniter_test.go index c2ad92c9a58da..f51e145664b6a 100644 --- a/br/pkg/streamhelper/regioniter_test.go +++ b/br/pkg/streamhelper/regioniter_test.go @@ -14,7 +14,7 @@ import ( "github.com/pingcap/tidb/br/pkg/redact" "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/streamhelper/spans" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" diff --git a/br/pkg/streamhelper/spans/BUILD.bazel b/br/pkg/streamhelper/spans/BUILD.bazel index 0a9093a8f2a22..93d2d90ecb4eb 100644 --- a/br/pkg/streamhelper/spans/BUILD.bazel +++ b/br/pkg/streamhelper/spans/BUILD.bazel @@ -12,7 +12,7 @@ go_library( deps = [ "//br/pkg/logutil", "//br/pkg/utils", - "//kv", + "//pkg/kv", "@com_github_google_btree//:btree", ], ) diff --git a/br/pkg/streamhelper/spans/sorted.go b/br/pkg/streamhelper/spans/sorted.go index a15138bf8124c..6523ceefc24a8 100644 --- a/br/pkg/streamhelper/spans/sorted.go +++ b/br/pkg/streamhelper/spans/sorted.go @@ -9,7 +9,7 @@ import ( "github.com/google/btree" "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // Value is the value type of stored in the span tree. diff --git a/br/pkg/summary/BUILD.bazel b/br/pkg/summary/BUILD.bazel index ea7a3eb2cebae..432c67a4e8bea 100644 --- a/br/pkg/summary/BUILD.bazel +++ b/br/pkg/summary/BUILD.bazel @@ -26,7 +26,7 @@ go_test( embed = [":summary"], flaky = True, deps = [ - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_stretchr_testify//require", "@org_uber_go_goleak//:goleak", "@org_uber_go_zap//:zap", diff --git a/br/pkg/summary/main_test.go b/br/pkg/summary/main_test.go index 9150de5865620..68a1a8afad13d 100644 --- a/br/pkg/summary/main_test.go +++ b/br/pkg/summary/main_test.go @@ -17,7 +17,7 @@ package summary import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/task/BUILD.bazel b/br/pkg/task/BUILD.bazel index 7f0501e7d3d48..ff0c779b3842f 100644 --- a/br/pkg/task/BUILD.bazel +++ b/br/pkg/task/BUILD.bazel @@ -43,19 +43,19 @@ go_library( "//br/pkg/summary", "//br/pkg/utils", "//br/pkg/version", - "//config", - "//ddl", - "//kv", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//statistics/handle", - "//types", - "//util", - "//util/mathutil", - "//util/sqlexec", - "//util/table-filter", + "//pkg/config", + "//pkg/ddl", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle", + "//pkg/types", + "//pkg/util", + "//pkg/util/mathutil", + "//pkg/util/sqlexec", + "//pkg/util/table-filter", "@com_github_docker_go_units//:go-units", "@com_github_fatih_color//:color", "@com_github_gogo_protobuf//proto", @@ -108,10 +108,10 @@ go_test( "//br/pkg/storage", "//br/pkg/stream", "//br/pkg/utils", - "//config", - "//parser/model", - "//statistics/handle/storage", - "//tablecodec", + "//pkg/config", + "//pkg/parser/model", + "//pkg/statistics/handle/storage", + "//pkg/tablecodec", "@com_github_golang_protobuf//proto", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/brpb", diff --git a/br/pkg/task/backup.go b/br/pkg/task/backup.go index ec4dcc42ae348..dc2a94c5e115a 100644 --- a/br/pkg/task/backup.go +++ b/br/pkg/task/backup.go @@ -32,13 +32,13 @@ import ( "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/spf13/pflag" "github.com/tikv/client-go/v2/oracle" kvutil "github.com/tikv/client-go/v2/util" diff --git a/br/pkg/task/backup_ebs.go b/br/pkg/task/backup_ebs.go index e459cfcb60bff..e9f59f6d35a8e 100644 --- a/br/pkg/task/backup_ebs.go +++ b/br/pkg/task/backup_ebs.go @@ -33,7 +33,7 @@ import ( "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/spf13/pflag" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/br/pkg/task/common.go b/br/pkg/task/common.go index 4f5466b8e5bd5..e17d81aedb32b 100644 --- a/br/pkg/task/common.go +++ b/br/pkg/task/common.go @@ -28,9 +28,9 @@ import ( "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/spf13/cobra" "github.com/spf13/pflag" pd "github.com/tikv/pd/client" diff --git a/br/pkg/task/common_test.go b/br/pkg/task/common_test.go index b124f6977b9fa..0ee14c52f02ea 100644 --- a/br/pkg/task/common_test.go +++ b/br/pkg/task/common_test.go @@ -9,7 +9,7 @@ import ( backup "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/kvproto/pkg/encryptionpb" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/spf13/pflag" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/task/restore.go b/br/pkg/task/restore.go index bd0a0906ae561..d0f79845cb369 100644 --- a/br/pkg/task/restore.go +++ b/br/pkg/task/restore.go @@ -28,9 +28,9 @@ import ( "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/tikv/client-go/v2/tikv" diff --git a/br/pkg/task/restore_data.go b/br/pkg/task/restore_data.go index 74663ea28d39d..357e56672e894 100644 --- a/br/pkg/task/restore_data.go +++ b/br/pkg/task/restore_data.go @@ -19,7 +19,7 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" - tidbconfig "github.com/pingcap/tidb/config" + tidbconfig "github.com/pingcap/tidb/pkg/config" "go.uber.org/zap" ) diff --git a/br/pkg/task/restore_test.go b/br/pkg/task/restore_test.go index b5e4ebd6ecb66..91a0c1fc16735 100644 --- a/br/pkg/task/restore_test.go +++ b/br/pkg/task/restore_test.go @@ -17,9 +17,9 @@ import ( "github.com/pingcap/tidb/br/pkg/restore" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/model" - handle "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/parser/model" + handle "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/tablecodec" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" "google.golang.org/grpc/keepalive" diff --git a/br/pkg/task/show/BUILD.bazel b/br/pkg/task/show/BUILD.bazel index fe4c6949ad400..f4a62d81bbe4b 100644 --- a/br/pkg/task/show/BUILD.bazel +++ b/br/pkg/task/show/BUILD.bazel @@ -33,7 +33,7 @@ go_test( shard_count = 4, deps = [ ":show", - "//testkit", + "//pkg/testkit", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/brpb", "@com_github_pingcap_kvproto//pkg/encryptionpb", diff --git a/br/pkg/task/show/cmd_test.go b/br/pkg/task/show/cmd_test.go index 5da6c66d3dca1..6e70dba899078 100644 --- a/br/pkg/task/show/cmd_test.go +++ b/br/pkg/task/show/cmd_test.go @@ -19,7 +19,7 @@ import ( backuppb "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/kvproto/pkg/encryptionpb" "github.com/pingcap/tidb/br/pkg/task/show" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/task/stream.go b/br/pkg/task/stream.go index ba633947e4e5e..3c3133edbd1b3 100644 --- a/br/pkg/task/stream.go +++ b/br/pkg/task/stream.go @@ -49,11 +49,11 @@ import ( "github.com/pingcap/tidb/br/pkg/streamhelper/daemon" "github.com/pingcap/tidb/br/pkg/summary" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/spf13/pflag" "github.com/tikv/client-go/v2/config" "github.com/tikv/client-go/v2/oracle" diff --git a/br/pkg/trace/BUILD.bazel b/br/pkg/trace/BUILD.bazel index c8bcdd2a1c267..340415da8622e 100644 --- a/br/pkg/trace/BUILD.bazel +++ b/br/pkg/trace/BUILD.bazel @@ -25,7 +25,7 @@ go_test( embed = [":trace"], flaky = True, deps = [ - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_opentracing_opentracing_go//:opentracing-go", "@com_github_stretchr_testify//require", "@org_uber_go_goleak//:goleak", diff --git a/br/pkg/trace/main_test.go b/br/pkg/trace/main_test.go index 0c69a7c12f4d9..07c5102202deb 100644 --- a/br/pkg/trace/main_test.go +++ b/br/pkg/trace/main_test.go @@ -17,7 +17,7 @@ package trace import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/utils/BUILD.bazel b/br/pkg/utils/BUILD.bazel index 224e9ab4f36ca..ce3e3d89a9f78 100644 --- a/br/pkg/utils/BUILD.bazel +++ b/br/pkg/utils/BUILD.bazel @@ -31,16 +31,16 @@ go_library( "//br/pkg/errors", "//br/pkg/logutil", "//br/pkg/metautil", - "//errno", - "//kv", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "//sessionctx", - "//util", - "//util/engine", - "//util/sqlexec", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/sessionctx", + "//pkg/util", + "//pkg/util/engine", + "//pkg/util/sqlexec", "@com_github_cheggaaa_pb_v3//:pb", "@com_github_cznic_mathutil//:mathutil", "@com_github_docker_go_units//:go-units", @@ -99,17 +99,17 @@ go_test( "//br/pkg/errors", "//br/pkg/metautil", "//br/pkg/storage", - "//kv", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//statistics/handle/storage", - "//tablecodec", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/sqlexec", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/statistics/handle/storage", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/sqlexec", "@com_github_golang_protobuf//proto", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", diff --git a/br/pkg/utils/db.go b/br/pkg/utils/db.go index 77814ace779ce..4ce494b7082a8 100644 --- a/br/pkg/utils/db.go +++ b/br/pkg/utils/db.go @@ -12,9 +12,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/br/pkg/utils/db_test.go b/br/pkg/utils/db_test.go index 1004764b0d206..e58e0d17a2b6e 100644 --- a/br/pkg/utils/db_test.go +++ b/br/pkg/utils/db_test.go @@ -9,12 +9,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/utils/dyn_pprof_other.go b/br/pkg/utils/dyn_pprof_other.go index 76756181c02d6..0236835114333 100644 --- a/br/pkg/utils/dyn_pprof_other.go +++ b/br/pkg/utils/dyn_pprof_other.go @@ -4,7 +4,7 @@ package utils -import tidbutils "github.com/pingcap/tidb/util" +import tidbutils "github.com/pingcap/tidb/pkg/util" // StartDynamicPProfListener starts the listener that will enable pprof when received `startPProfSignal` func StartDynamicPProfListener(tls *tidbutils.TLS) { diff --git a/br/pkg/utils/dyn_pprof_unix.go b/br/pkg/utils/dyn_pprof_unix.go index fe7b1c374c0a7..65f6f43027d54 100644 --- a/br/pkg/utils/dyn_pprof_unix.go +++ b/br/pkg/utils/dyn_pprof_unix.go @@ -10,7 +10,7 @@ import ( "syscall" "github.com/pingcap/log" - tidbutils "github.com/pingcap/tidb/util" + tidbutils "github.com/pingcap/tidb/pkg/util" "go.uber.org/zap" ) diff --git a/br/pkg/utils/key.go b/br/pkg/utils/key.go index db4e884003d9d..3e26211adf59e 100644 --- a/br/pkg/utils/key.go +++ b/br/pkg/utils/key.go @@ -13,7 +13,7 @@ import ( "github.com/pingcap/log" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "go.uber.org/zap" ) diff --git a/br/pkg/utils/key_test.go b/br/pkg/utils/key_test.go index 763ad4e740b12..10723ed4c8a16 100644 --- a/br/pkg/utils/key_test.go +++ b/br/pkg/utils/key_test.go @@ -7,7 +7,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/utils/main_test.go b/br/pkg/utils/main_test.go index 516eb946fdfc7..f0f7ff871ae91 100644 --- a/br/pkg/utils/main_test.go +++ b/br/pkg/utils/main_test.go @@ -17,7 +17,7 @@ package utils import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/br/pkg/utils/misc.go b/br/pkg/utils/misc.go index 714d5f11bb7ba..ec2d05156f1f4 100644 --- a/br/pkg/utils/misc.go +++ b/br/pkg/utils/misc.go @@ -21,8 +21,8 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/metapb" berrors "github.com/pingcap/tidb/br/pkg/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" diff --git a/br/pkg/utils/misc_test.go b/br/pkg/utils/misc_test.go index 6e6afd0eae074..6d243625b3e5b 100644 --- a/br/pkg/utils/misc_test.go +++ b/br/pkg/utils/misc_test.go @@ -16,8 +16,8 @@ package utils import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/utils/pprof.go b/br/pkg/utils/pprof.go index 9df50ce74c00c..c2e5ad63c8e5a 100644 --- a/br/pkg/utils/pprof.go +++ b/br/pkg/utils/pprof.go @@ -17,7 +17,7 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/log" berrors "github.com/pingcap/tidb/br/pkg/errors" - tidbutils "github.com/pingcap/tidb/util" + tidbutils "github.com/pingcap/tidb/pkg/util" "go.uber.org/zap" ) diff --git a/br/pkg/utils/retry.go b/br/pkg/utils/retry.go index b4ab0437cf651..efab1f843e4c0 100644 --- a/br/pkg/utils/retry.go +++ b/br/pkg/utils/retry.go @@ -10,8 +10,8 @@ import ( "github.com/cznic/mathutil" "github.com/pingcap/errors" - tmysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" + tmysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/tikv/client-go/v2/tikv" "go.uber.org/multierr" ) diff --git a/br/pkg/utils/schema.go b/br/pkg/utils/schema.go index 0987e446bfc49..94a1dd01887fe 100644 --- a/br/pkg/utils/schema.go +++ b/br/pkg/utils/schema.go @@ -10,8 +10,8 @@ import ( "github.com/pingcap/errors" backuppb "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/tidb/br/pkg/metautil" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" ) // temporaryDBNamePrefix is the prefix name of system db, e.g. mysql system db will be rename to __TiDB_BR_Temporary_mysql diff --git a/br/pkg/utils/schema_test.go b/br/pkg/utils/schema_test.go index fa0d9cb0fc6c6..e5a47be7a8511 100644 --- a/br/pkg/utils/schema_test.go +++ b/br/pkg/utils/schema_test.go @@ -13,9 +13,9 @@ import ( "github.com/pingcap/kvproto/pkg/encryptionpb" "github.com/pingcap/tidb/br/pkg/metautil" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/parser/model" - handle "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/parser/model" + handle "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/tablecodec" "github.com/stretchr/testify/require" ) diff --git a/br/pkg/utils/suspend_importing.go b/br/pkg/utils/suspend_importing.go index c2df70229c525..60693e7c00fff 100644 --- a/br/pkg/utils/suspend_importing.go +++ b/br/pkg/utils/suspend_importing.go @@ -9,7 +9,7 @@ import ( "github.com/pingcap/kvproto/pkg/metapb" berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/logutil" - "github.com/pingcap/tidb/util/engine" + "github.com/pingcap/tidb/pkg/util/engine" pd "github.com/tikv/pd/client" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/br/pkg/version/BUILD.bazel b/br/pkg/version/BUILD.bazel index a984fd3870020..5411e891f066e 100644 --- a/br/pkg/version/BUILD.bazel +++ b/br/pkg/version/BUILD.bazel @@ -10,8 +10,8 @@ go_library( "//br/pkg/logutil", "//br/pkg/utils", "//br/pkg/version/build", - "//parser/model", - "//util/engine", + "//pkg/parser/model", + "//pkg/util/engine", "@com_github_coreos_go_semver//semver", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/metapb", @@ -30,7 +30,7 @@ go_test( shard_count = 10, deps = [ "//br/pkg/version/build", - "//parser/model", + "//pkg/parser/model", "@com_github_coreos_go_semver//semver", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_pingcap_kvproto//pkg/metapb", diff --git a/br/pkg/version/build/BUILD.bazel b/br/pkg/version/build/BUILD.bazel index b014fd839b44b..c5783485b38d2 100644 --- a/br/pkg/version/build/BUILD.bazel +++ b/br/pkg/version/build/BUILD.bazel @@ -6,9 +6,9 @@ go_library( importpath = "github.com/pingcap/tidb/br/pkg/version/build", visibility = ["//visibility:public"], deps = [ - "//parser/mysql", - "//util/israce", - "//util/versioninfo", + "//pkg/parser/mysql", + "//pkg/util/israce", + "//pkg/util/versioninfo", "@com_github_pingcap_log//:log", "@org_uber_go_zap//:zap", ], diff --git a/br/pkg/version/build/info.go b/br/pkg/version/build/info.go index a1d0eb04db697..6c011b6d79e9d 100644 --- a/br/pkg/version/build/info.go +++ b/br/pkg/version/build/info.go @@ -8,9 +8,9 @@ import ( "runtime" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/israce" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/israce" + "github.com/pingcap/tidb/pkg/util/versioninfo" "go.uber.org/zap" ) diff --git a/br/pkg/version/version.go b/br/pkg/version/version.go index 1d251f03f2e76..d11b728d6515e 100644 --- a/br/pkg/version/version.go +++ b/br/pkg/version/version.go @@ -18,8 +18,8 @@ import ( "github.com/pingcap/tidb/br/pkg/logutil" "github.com/pingcap/tidb/br/pkg/utils" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/engine" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/engine" pd "github.com/tikv/pd/client" "go.uber.org/zap" ) diff --git a/br/pkg/version/version_test.go b/br/pkg/version/version_test.go index 5a2d0d40e8ecd..6e986791d49e1 100644 --- a/br/pkg/version/version_test.go +++ b/br/pkg/version/version_test.go @@ -13,7 +13,7 @@ import ( "github.com/coreos/go-semver/semver" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/tidb/br/pkg/version/build" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" ) diff --git a/br/tests/br_key_locked/BUILD.bazel b/br/tests/br_key_locked/BUILD.bazel index a6e6a40edf2f2..c160486e62af7 100644 --- a/br/tests/br_key_locked/BUILD.bazel +++ b/br/tests/br_key_locked/BUILD.bazel @@ -11,12 +11,12 @@ go_library( deps = [ "//br/pkg/httputil", "//br/pkg/task", - "//config", - "//kv", - "//parser/model", - "//store/driver", - "//tablecodec", - "//util/codec", + "//pkg/config", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/store/driver", + "//pkg/tablecodec", + "//pkg/util/codec", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_kvproto//pkg/kvrpcpb", "@com_github_pingcap_kvproto//pkg/metapb", diff --git a/br/tests/br_key_locked/codec.go b/br/tests/br_key_locked/codec.go index af4cfb8459edf..9ed40d2677ef5 100644 --- a/br/tests/br_key_locked/codec.go +++ b/br/tests/br_key_locked/codec.go @@ -21,7 +21,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/util/codec" pd "github.com/tikv/pd/client" ) diff --git a/br/tests/br_key_locked/locker.go b/br/tests/br_key_locked/locker.go index 39e733fa1358c..f8e22c4f3d786 100644 --- a/br/tests/br_key_locked/locker.go +++ b/br/tests/br_key_locked/locker.go @@ -36,11 +36,11 @@ import ( "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/httputil" "github.com/pingcap/tidb/br/pkg/task" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/tablecodec" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/build/linter/deferrecover/analyzer.go b/build/linter/deferrecover/analyzer.go index ad9d09b380cb3..58ea39d319d76 100644 --- a/build/linter/deferrecover/analyzer.go +++ b/build/linter/deferrecover/analyzer.go @@ -32,7 +32,7 @@ var Analyzer = &analysis.Analyzer{ } const ( - packagePath = "github.com/pingcap/tidb/util" + packagePath = "github.com/pingcap/tidb/pkg/util" packageName = "util" funcName = "Recover" ) diff --git a/build/nogo_config.json b/build/nogo_config.json index 20256280b056c..fd5412c1edd1e 100644 --- a/build/nogo_config.json +++ b/build/nogo_config.json @@ -1,18 +1,18 @@ { "all_revive": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/rules_go_work-*": "ignore generated code", ".*_/testmain\\.go$": "ignore code", - "extension/enterprise/audit/entry.go": "extension/enterprise/audit/entry.go", - "extension/enterprise/audit/filter.go": "extension/enterprise/audit/filter.go" + "pkg/extension/enterprise/audit/entry.go": "pkg/extension/enterprise/audit/entry.go", + "pkg/extension/enterprise/audit/filter.go": "pkg/extension/enterprise/audit/filter.go" } }, "asciicheck": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "br/pkg/lightning/web/res_vfsdata.go": "ignore code" @@ -20,70 +20,70 @@ }, "asmdecl": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "assign": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "atomic": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "atomicalign": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "bodyclose": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "bools": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "buildtag": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "printf": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "unreachable": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "composites": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "br/pkg/glue/console_glue_test.go": "ignore code", @@ -93,7 +93,7 @@ }, "copylocks": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/cgo/": "ignore cgo code" @@ -101,28 +101,28 @@ }, "ctrlflow": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "deadcode": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "deepequalerrors": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "durationcheck": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", "/rules_go_work-*": "ignore generated code", ".*_generated\\.go$": "ignore generated code" @@ -130,7 +130,7 @@ }, "errorsas": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } @@ -140,26 +140,26 @@ "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", ".*_test\\.go$": "ignore generated code", - "util/logutil": "ignore util/logutil code", + "pkg/util/logutil": "ignore util/logutil code", "tools/": "ignore tools code", "/src/net/conf.go": "ignore code", "/rules_go_work-*": "ignore generated code", "GOROOT/": "ignore code", "/parser/": "ignore code", - "server/internal/testserverclient/server_client.go": "ignore code", + "pkg/server/internal/testserverclient/server_client.go": "ignore code", ".*_/testmain\\.go$": "ignore code" } }, "exportloopref": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "filepermission": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".cgo/": "no need to cgo code", ".*_generated\\.go$": "ignore generated code", @@ -168,63 +168,63 @@ }, "fieldalignment": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", ".*_/testmain\\.go$": "ignore code", ".*_test\\.go$": "ignore test code" }, "only_files": { - "statistics/": "statistics/ code", - "util/checksum": "util/checksum code", - "util/processinfo.go": "util/processinfo.go code", - "util/cpuprofile/": "util/cpuprofile/ code", - "util/cteutil/": "util/cteutil/ code", - "util/dbutil/": "util/dbutil/ code", - "util/deadlockhistory/": "util/deadlockhistory/ code", - "util/domainutil/": "util/domainutil/ code", - "util/encrypt/": "util/encrypt/ code", - "util/etcd/": "util/etcd/ code", - "util/expensivequery/": "util/expensivequery/ code", - "util/filter/": "util/filter/ code", - "util/importer/": "util/importer/ code", - "util/keydecoder/": "util/keydecoder/ code", - "util/kvcache/": "util/kvcache/ code", - "util/localpool/": "util/localpool/ code", - "util/mathutil/": "util/mathutil/ code", - "util/memory/": "util/memory/ code", - "util/mock/": "util/mock/ code", - "util/mvmap/": "util/mvmap/ code", - "util/profile/": "util/profile/ code", - "util/ranger/": "util/ranger/ code", - "util/regexpr-router/": "util/regexpr-router/ code", - "util/schemacmp/": "util/schemacmp/ code", - "util/sqlexec/": "util/sqlexec/ code", - "util/stringutil/": "util/stringutil/ code", - "util/table-router/": "util/table-router/ code", - "util/timeutil/": "util/timeutil/ code", - "util/topsql/": "util/topsql/ code", - "util/tracing/": "util/tracing/ code", - "util/trxevents/": "util/trxevents/ code", - "util/watcher/": "util/watcher/ code", - "util/gctuner": "util/gctuner", - "store/mockstore/unistore/util": "store/mockstore/unistore/util code", - "ddl/util/": "ddl/util code", - "server/internal": "server/internal code", - "planner/core/internal": "planner/core/internal code", - "executor/internal": "executor/internal code" + "pkg/statistics/": "statistics/ code", + "pkg/util/checksum": "util/checksum code", + "pkg/util/processinfo.go": "util/processinfo.go code", + "pkg/util/cpuprofile/": "util/cpuprofile/ code", + "pkg/util/cteutil/": "util/cteutil/ code", + "pkg/util/dbutil/": "util/dbutil/ code", + "pkg/util/deadlockhistory/": "util/deadlockhistory/ code", + "pkg/util/domainutil/": "util/domainutil/ code", + "pkg/util/encrypt/": "util/encrypt/ code", + "pkg/util/etcd/": "util/etcd/ code", + "pkg/util/expensivequery/": "util/expensivequery/ code", + "pkg/util/filter/": "util/filter/ code", + "pkg/util/importer/": "util/importer/ code", + "pkg/util/keydecoder/": "util/keydecoder/ code", + "pkg/util/kvcache/": "util/kvcache/ code", + "pkg/util/localpool/": "util/localpool/ code", + "pkg/util/mathutil/": "util/mathutil/ code", + "pkg/util/memory/": "util/memory/ code", + "pkg/util/mock/": "util/mock/ code", + "pkg/util/mvmap/": "util/mvmap/ code", + "pkg/util/profile/": "util/profile/ code", + "pkg/util/ranger/": "util/ranger/ code", + "pkg/util/regexpr-router/": "util/regexpr-router/ code", + "pkg/util/schemacmp/": "util/schemacmp/ code", + "pkg/util/sqlexec/": "util/sqlexec/ code", + "pkg/util/stringutil/": "util/stringutil/ code", + "pkg/util/table-router/": "util/table-router/ code", + "pkg/util/timeutil/": "util/timeutil/ code", + "pkg/util/topsql/": "util/topsql/ code", + "pkg/util/tracing/": "util/tracing/ code", + "pkg/util/trxevents/": "util/trxevents/ code", + "pkg/util/watcher/": "util/watcher/ code", + "pkg/util/gctuner": "util/gctuner", + "pkg/store/mockstore/unistore/util": "store/mockstore/unistore/util code", + "pkg/ddl/util/": "ddl/util code", + "pkg/server/internal": "server/internal code", + "pkg/planner/core/internal": "planner/core/internal code", + "pkg/executor/internal": "executor/internal code" } }, "findcall": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "forcetypeassert": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*test_/testmain\\.go$": "ignore generated code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" @@ -233,25 +233,25 @@ "util/gctuner": "only for util/gctuner", "br/pkg/lightning/mydump/": "only for br/pkg/lightning/mydump/", "br/pkg/lightning/importer/opts": "only for br/pkg/lightning/importer/opts", - "executor/aggregate.go": "only for executor/aggregate.go", - "types/json_binary_functions.go": "only for types/json_binary_functions.go", - "types/json_binary_test.go": "only for types/json_binary_test.go", - "ddl/backfilling.go": "only for ddl/backfilling.go", - "ddl/column.go": "only for ddl/column.go", - "ddl/index.go": "only for ddl/index.go", - "ddl/ingest/": "only for ddl/ingest/", - "util/cgroup": "only for util/cgroup code", - "server/conn.go": "only for server/conn.go", - "server/conn_stmt.go": "only for server/conn_stmt.go", - "server/conn_test.go": "only for server/conn_test.go", - "planner/core/plan.go": "only for planner/core/plan.go", - "errno/": "only for errno/", - "extension/": "extension code" + "pkg/executor/aggregate.go": "only for executor/aggregate.go", + "pkg/types/json_binary_functions.go": "only for types/json_binary_functions.go", + "pkg/types/json_binary_test.go": "only for types/json_binary_test.go", + "pkg/ddl/backfilling.go": "only for ddl/backfilling.go", + "pkg/ddl/column.go": "only for ddl/column.go", + "pkg/ddl/index.go": "only for ddl/index.go", + "pkg/ddl/ingest/": "only for ddl/ingest/", + "pkg/util/cgroup": "only for util/cgroup code", + "pkg/server/conn.go": "only for server/conn.go", + "pkg/server/conn_stmt.go": "only for server/conn_stmt.go", + "pkg/server/conn_test.go": "only for server/conn_test.go", + "pkg/planner/core/plan.go": "only for planner/core/plan.go", + "pkg/errno/": "only for errno/", + "pkg/extension/": "extension code" } }, "gofmt": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/cgo/": "ignore cgo code", @@ -269,14 +269,14 @@ "/rules_go_work-*": "ignore generated code", ".*test_/testmain\\.go$": "ignore generated code", ".*failpoint_binding__.go$": "ignore generated code", - "util/printer/printer.go": "ignore util/printer code", - "parser/parser.go": "ignore parser code", - "parser/hintparser.go": "ignore parser/hintparser code" + "pkg/util/printer/printer.go": "ignore util/printer code", + "pkg/parser/parser.go": "ignore parser code", + "pkg/parser/hintparser.go": "ignore parser/hintparser code" } }, "gosec": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", "parser/goyacc/": "ignore goyacc code", ".*_test\\.go$": "ignore generated code", @@ -289,27 +289,27 @@ "tools/check/xprog.go:": "ignore tools/check code", "tests/integrationtest/main.go": "ignore tests/integrationtest code", "GOROOT/": "ignore code", - "server/internal/testserverclient/server_client.go": "ignore server_client code", + "pkg/server/internal/testserverclient/server_client.go": "ignore server_client code", ".*_generated\\.go$": "ignore generated code" } }, "httpresponse": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "ifaceassert": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "ineffassign": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/cgo/": "no need to vet cgo code" @@ -317,21 +317,21 @@ }, "inspect": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "loopclosure": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "lostcancel": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } @@ -377,57 +377,57 @@ "br/pkg/restore/": "ignore restore code", "br/pkg/task/": "ignore task code", "br/pkg/version/": "ignore version code", - "ddl/util/util.go": "ignore util code", - "ddl/syncer/syncer.go": "ignore syncer code", - "ddl/ingest/backend.go": "ignore backend code", - "ddl/ingest/backend_mgr.go": "ignore backend_mgr code", - "ddl/ingest/engine_mgr.go": "ignore engine_mgr code", - "util/column-mapping/column.go": "ignore error", - "parser/mysql/": "ignore mysql code", - "parser/auth": "ignore auth code", - "parser/charset/charset.go": "ignore charset code", - "parser/types/field_type.go": "ignore field_type code", - "parser/model/ddl.go": "ignore ddl code", - "parser/model/model.go": "ignore model code", - "parser/model/model_test.go": "ignore tables code", - "parser/ast": "ignore ast code", - "parser/parser.go": "ignore parser code", - "parser/hintparser.go": "ignore hintparser code", - "parser/yy_parser.go": "ignore yy_parser code", - "parser/digester.go": "ignore digester code", - "parser/consistent_test.go": "ignore consistent code", - "parser/bench_test.go": "ignore bench code", - "parser/parser_test.go": "ignore parser_test code", - "parser/digester_test.go": "ignore digester_test code", - "parser/hintparser_test.go": "ignore hintparser_test code", - "errno/errname.go": "ignore errname code", - "planner/funcdep/fd_graph.go": "ignore fd_graph code", - "planner/funcdep/doc.go": "ignore funcdep code", - "planner/util/path.go": "ignore path code", - "planner/memo/": "ignore memo code", - "ttl/cache/ttlstatus.go": "ignore ttlstatus code", - "ttl/ttlworker/": "ignore ttlworker code", - "ttl/sqlbuilder/sql_test.go": "ignore sql_test code", - "planner/core/": "ignore core code", - "planner/optimize.go": "ignore optimize code", - "planner/cascades/": "ignore cascades code", - "planner/cardinality/": "ignore cardinality code", - "planner/funcdep/extract_fd_test.go": "ignore extract_fd code", - "planner/funcdep/only_full_group_by_test.go": "ignore only_full_group_by code", + "pkg/ddl/util/util.go": "ignore util code", + "pkg/ddl/syncer/syncer.go": "ignore syncer code", + "pkg/ddl/ingest/backend.go": "ignore backend code", + "pkg/ddl/ingest/backend_mgr.go": "ignore backend_mgr code", + "pkg/ddl/ingest/engine_mgr.go": "ignore engine_mgr code", + "pkg/util/column-mapping/column.go": "ignore error", + "pkg/parser/mysql/": "ignore mysql code", + "pkg/parser/auth": "ignore auth code", + "pkg/parser/charset/charset.go": "ignore charset code", + "pkg/parser/types/field_type.go": "ignore field_type code", + "pkg/parser/model/ddl.go": "ignore ddl code", + "pkg/parser/model/model.go": "ignore model code", + "pkg/parser/model/model_test.go": "ignore tables code", + "pkg/parser/ast": "ignore ast code", + "pkg/parser/parser.go": "ignore parser code", + "pkg/parser/hintparser.go": "ignore hintparser code", + "pkg/parser/yy_parser.go": "ignore yy_parser code", + "pkg/parser/digester.go": "ignore digester code", + "pkg/parser/consistent_test.go": "ignore consistent code", + "pkg/parser/bench_test.go": "ignore bench code", + "pkg/parser/parser_test.go": "ignore parser_test code", + "pkg/parser/digester_test.go": "ignore digester_test code", + "pkg/parser/hintparser_test.go": "ignore hintparser_test code", + "pkg/errno/errname.go": "ignore errname code", + "pkg/planner/funcdep/fd_graph.go": "ignore fd_graph code", + "pkg/planner/funcdep/doc.go": "ignore funcdep code", + "pkg/planner/util/path.go": "ignore path code", + "pkg/planner/memo/": "ignore memo code", + "pkg/ttl/cache/ttlstatus.go": "ignore ttlstatus code", + "pkg/ttl/ttlworker/": "ignore ttlworker code", + "pkg/ttl/sqlbuilder/sql_test.go": "ignore sql_test code", + "pkg/planner/core/": "ignore core code", + "pkg/planner/optimize.go": "ignore optimize code", + "pkg/planner/cascades/": "ignore cascades code", + "pkg/planner/cardinality/": "ignore cardinality code", + "pkg/planner/funcdep/extract_fd_test.go": "ignore extract_fd code", + "pkg/planner/funcdep/only_full_group_by_test.go": "ignore only_full_group_by code", "dumpling/export": "ignore export code", - "ddl/placement/": "ignore placement code" + "pkg/ddl/placement/": "ignore placement code" }, "only_files": { "br/": "only for br code", - "ttl/": "only for ttl code", - "planner/": "only for planner code", - "parser/": "only for parser code", + "pkg/ttl/": "only for ttl code", + "pkg/planner/": "only for planner code", + "pkg/parser/": "only for parser code", "dumpling/": "only for dumpling code" } }, "makezero": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "/cgo/": "ignore cgo code", ".*_test\\.go$": "ignore generated code", "external/": "no need to vet third party code", @@ -436,7 +436,7 @@ }, "mirror": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "/cgo/": "ignore cgo code", "external/": "no need to vet third party code", "tools/": "ignore tools code", @@ -445,7 +445,7 @@ }, "misspell": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "/cgo/": "ignore cgo code", "external/": "no need to vet third party code", "$GOROOT/": "ignore GOROOT code", @@ -454,14 +454,14 @@ }, "nilfunc": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "nilness": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/cgo/": "ignore cgo" @@ -469,139 +469,139 @@ }, "noloopclosure": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" }, "only_files": { "br/pkg/lightning/mydump/": "br/pkg/lightning/mydump/", "br/pkg/lightning/importer/opts": "br/pkg/lightning/importer/opts", - "kv/": "kv code", - "util/memory": "util/memory", - "ddl/": "ddl", - "planner/": "planner", - "extension/": "extension code" + "pkg/kv/": "kv code", + "pkg/util/memory": "util/memory", + "pkg/ddl/": "ddl", + "pkg/planner/": "planner", + "pkg/extension/": "extension code" } }, "pkgfact": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "revive": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", "GOROOT/": "ignore code", "/cgo/": "ignore cgo", "tools/": "ignore tool code", - "parser/goyacc/": "ignore goyacc code", - "parser/ast/": "ignore parser/ast code", - "parser/test_driver/": "ignore parser/test_driver code", + "pkg/parser/goyacc/": "ignore goyacc code", + "pkg/parser/ast/": "ignore parser/ast code", + "pkg/parser/test_driver/": "ignore parser/test_driver code", "dumpling/export/sql_type.go": "please fix it", ".*_test\\.go$": "ignore generated code", ".*_generated\\.go$": "ignore generated code", - "plugin/conn_ip_example/": "plugin/conn_ip_example/" + "pkg/plugin/conn_ip_example/": "plugin/conn_ip_example/" }, "only_files": { - "autoid_service/": "autoid_service", - "bindinfo/": "bindinfo", + "pkg/autoid_service/": "autoid_service", + "pkg/bindinfo/": "bindinfo", "br/pkg/lightning/": "br/pkg/lightning/", - "executor/": "executor/", - "types/json_binary_functions.go": "types/json_binary_functions.go", - "types/json_binary_test.go": "types/json_binary_test.go", - "ddl/": "ddl", - "expression/a": "expression/a code", - "expression/builtin.go": "expression/builtin code", - "expression/builtin_cast.go": "expression/builtin_cast code", - "server/": "server/ code", - "distsql/": "distsql code", - "disttask": "disttask code", + "pkg/executor/": "executor/", + "pkg/types/json_binary_functions.go": "types/json_binary_functions.go", + "pkg/types/json_binary_test.go": "types/json_binary_test.go", + "pkg/ddl/": "ddl", + "pkg/expression/a": "expression/a code", + "pkg/expression/builtin.go": "expression/builtin code", + "pkg/expression/builtin_cast.go": "expression/builtin_cast code", + "pkg/server/": "server/ code", + "pkg/distsql/": "distsql code", + "pkg/disttask": "disttask code", "dumpling/export": "dumpling/export code", - "lock/": "lock file", - "errno/": "errno code", - "session/": "session code", - "structure/": "structure code", - "statistics/": "statistics/ code", - "telemetry/": "telemetry code", - "parser/": "parser code", - "planner/": "planner code", - "plugin/": "plugin code", - "util/": "util code", - "meta/": "parser code", - "extension/": "extension code", - "resourcemanager/": "resourcemanager code", - "keyspace/": "keyspace code", - "owner/": "owner code", - "timer/": "timer code" + "pkg/lock/": "lock file", + "pkg/errno/": "errno code", + "pkg/session/": "session code", + "pkg/structure/": "structure code", + "pkg/statistics/": "statistics/ code", + "pkg/telemetry/": "telemetry code", + "pkg/parser/": "parser code", + "pkg/planner/": "planner code", + "pkg/plugin/": "plugin code", + "pkg/util/": "util code", + "pkg/meta/": "parser code", + "pkg/extension/": "extension code", + "pkg/resourcemanager/": "resourcemanager code", + "pkg/keyspace/": "keyspace code", + "pkg/owner/": "owner code", + "pkg/timer/": "timer code" } }, "shift": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "sortslice": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "stdmethods": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "stringintconv": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "structtag": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "testinggoroutine": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "tests": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "toomanytests": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "/cgo/": "ignore cgo code", "/rules_go_work-*": "ignore generated code", ".*test_/testmain\\.go$": "ignore generated code", ".*failpoint_binding__.go$": "ignore generated code", - "util/chunk": "ignore util/chunk", + "pkg/util/chunk": "ignore util/chunk", "br/pkg/lightning/mydump/": "more than 50", "br/pkg/lightning/importer/": "more than 50", "br/pkg/lightning/backend/local/": "more than 50", "br/pkg/restore/": "more than 50", - "ddl/tests/partition/": "more than 50" + "pkg/ddl/tests/partition/": "more than 50" }, "only_files": { "br/pkg/version/": "br/pkg/version/ coded", @@ -625,61 +625,61 @@ "br/pkg/conn/": "br/pkg/conn/ coded", "br/pkg/checkpoint/": "br/pkg/checkpoint/ coded", "br/pkg/backup/": "br/pkg/backup/ coded", - "planer/core/casetest/binaryplan": "planer/core/casetest/binaryplan", - "planer/core/casetest/cbotest": "planer/core/casetest/cbotest", - "planer/core/casetest/dag": "planer/core/casetest/dag", - "planer/core/casetest/enforcempp": "planer/core/casetest/enforcempp", - "planer/core/casetest/flatplan": "planer/core/casetest/flatplan", - "planer/core/casetest/hint": "planer/core/casetest/hint", - "planer/core/casetest/mpp": "planer/core/casetest/mpp", - "planer/core/casetest/partition": "planer/core/casetest/partition", - "planer/core/casetest/pushdown": "planer/core/casetest/pushdown", - "planer/core/casetest/rule": "planer/core/casetest/rule", - "planer/core/casetest/scalarsubquery": "planer/core/casetest/scalarsubquery", - "planer/core/casetest/testdata": "planer/core/casetest/testdata", - "planer/core/casetest/windows": "planer/core/casetest/windows", - "planer/core/tests/": "planer/core/tests/", - "executor/test/admintest": "executor/test/admintest", - "executor/test/aggregate": "executor/test/aggregate", - "executor/test/autoidtest": "executor/test/autoidtest", - "executor/test/distsqltest": "executor/test/distsqltest", - "executor/test/fktest": "executor/test/fktest", - "executor/test/indexmergereadtest": "executor/test/indexmergereadtest", - "executor/test/jointest": "executor/test/jointest", - "executor/test/kvtest": "executor/test/kvtest", - "executor/test/loaddatatest": "executor/test/loaddatatest", - "executor/test/loadremotetest": "executor/test/loadremotetest", - "executor/test/memtest": "executor/test/memtest", - "executor/test/oomtest": "executor/test/oomtest", - "executor/test/partitiontest": "executor/test/partitiontest", - "executor/test/passwordtest": "executor/test/passwordtest", - "executor/test/seqtest": "executor/test/seqtest", - "executor/test/showtest": "executor/test/showtest", - "executor/test/simpletest": "executor/test/simpletest", - "executor/test/splittest": "executor/test/splittest", - "executor/test/tiflashtest": "executor/test/tiflashtest", - "executor/test/unstabletest": "executor/test/unstabletest", + "pkg/planer/core/casetest/binaryplan": "planer/core/casetest/binaryplan", + "pkg/planer/core/casetest/cbotest": "planer/core/casetest/cbotest", + "pkg/planer/core/casetest/dag": "planer/core/casetest/dag", + "pkg/planer/core/casetest/enforcempp": "planer/core/casetest/enforcempp", + "pkg/planer/core/casetest/flatplan": "planer/core/casetest/flatplan", + "pkg/planer/core/casetest/hint": "planer/core/casetest/hint", + "pkg/planer/core/casetest/mpp": "planer/core/casetest/mpp", + "pkg/planer/core/casetest/partition": "planer/core/casetest/partition", + "pkg/planer/core/casetest/pushdown": "planer/core/casetest/pushdown", + "pkg/planer/core/casetest/rule": "planer/core/casetest/rule", + "pkg/planer/core/casetest/scalarsubquery": "planer/core/casetest/scalarsubquery", + "pkg/planer/core/casetest/testdata": "planer/core/casetest/testdata", + "pkg/planer/core/casetest/windows": "planer/core/casetest/windows", + "pkg/planer/core/tests/": "planer/core/tests/", + "pkg/executor/test/admintest": "executor/test/admintest", + "pkg/executor/test/aggregate": "executor/test/aggregate", + "pkg/executor/test/autoidtest": "executor/test/autoidtest", + "pkg/executor/test/distsqltest": "executor/test/distsqltest", + "pkg/executor/test/fktest": "executor/test/fktest", + "pkg/executor/test/indexmergereadtest": "executor/test/indexmergereadtest", + "pkg/executor/test/jointest": "executor/test/jointest", + "pkg/executor/test/kvtest": "executor/test/kvtest", + "pkg/executor/test/loaddatatest": "executor/test/loaddatatest", + "pkg/executor/test/loadremotetest": "executor/test/loadremotetest", + "pkg/executor/test/memtest": "executor/test/memtest", + "pkg/executor/test/oomtest": "executor/test/oomtest", + "pkg/executor/test/partitiontest": "executor/test/partitiontest", + "pkg/executor/test/passwordtest": "executor/test/passwordtest", + "pkg/executor/test/seqtest": "executor/test/seqtest", + "pkg/executor/test/showtest": "executor/test/showtest", + "pkg/executor/test/simpletest": "executor/test/simpletest", + "pkg/executor/test/splittest": "executor/test/splittest", + "pkg/executor/test/tiflashtest": "executor/test/tiflashtest", + "pkg/executor/test/unstabletest": "executor/test/unstabletest", "br/": "br code", - "session/test": "session/test code", - "ddl/tests": "ddl/tests code", - "disttask/": "disttask code", - "timer/": "timer code", - "util/": "util code", + "pkg/session/test": "session/test code", + "pkg/ddl/tests": "ddl/tests code", + "pkg/disttask/": "disttask code", + "pkg/timer/": "timer code", + "pkg/util/": "util code", "br/pkg/lightning/config/": "br/pkg/lightning/config code", "br/pkg/storage": "br/pkg/storage code", "br/pkg/utils": "br/pkg/utils code", - "planner/cascades": "planner/cascades code", - "store/": "store code", - "ttl/": "ttl code", - "bindinfo/": "bindinfo code", - "domain/": "domain code" + "pkg/planner/cascades": "planner/cascades code", + "pkg/store/": "store code", + "pkg/ttl/": "ttl code", + "pkg/bindinfo/": "bindinfo code", + "pkg/domain/": "domain code" } }, "unconvert": { "exclude_files": { "external/": "no need to vet third party code", ".*\\.pb\\.go$": "generated code", - "parser/parser.go": "generated code", + "pkg/parser/parser.go": "generated code", "/cgo/": "no need to vet third party code for cgo", "br/pkg/lightning/common/conn.go": "ignore: to fix it", "br/pkg/lightning/common/storage_unix.go": "ignore: to fix it", @@ -689,175 +689,175 @@ }, "unmarshal": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "unsafeptr": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/digester.go": "ignore code" + "pkg/parser/digester.go": "ignore code" } }, "unusedresult": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/digester_test.go": "ignore code" + "pkg/parser/digester_test.go": "ignore code" } }, "rowserrcheck": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "server/tidb_test.go": "ignore test code", - "server/tests/tidb_test.go": "ignore test code", - "server/tests/tidb_serial_test.go": "ignore test code", - "server/tidb_serial_test.go": "ignore test code", - "server/statistics_handler_test.go": "ignore test code", - "server/handler/optimizor/optimize_trace_test.go": "ignore test code", - "server/handler/optimizor/plan_replayer_test.go": "ignore test code", - "server/handler/optimizor/statistics_handler_test.go": "ignore test code", - "server/server_test.go": "ignore test code", - "server/optimize_trace_test.go": "ignore test code", - "server/plan_replayer_test.go": "ignore test code", - "server/internal/testserverclient/server_client.go": "ignore test code" + "pkg/server/tidb_test.go": "ignore test code", + "pkg/server/tests/tidb_test.go": "ignore test code", + "pkg/server/tests/tidb_serial_test.go": "ignore test code", + "pkg/server/tidb_serial_test.go": "ignore test code", + "pkg/server/statistics_handler_test.go": "ignore test code", + "pkg/server/handler/optimizor/optimize_trace_test.go": "ignore test code", + "pkg/server/handler/optimizor/plan_replayer_test.go": "ignore test code", + "pkg/server/handler/optimizor/statistics_handler_test.go": "ignore test code", + "pkg/server/server_test.go": "ignore test code", + "pkg/server/optimize_trace_test.go": "ignore test code", + "pkg/server/plan_replayer_test.go": "ignore test code", + "pkg/server/internal/testserverclient/server_client.go": "ignore test code" } }, "S1000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1001": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1002": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1003": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1004": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1005": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1006": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1007": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1008": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1009": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1010": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1011": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1012": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1013": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1014": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1015": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1016": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1017": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1018": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } @@ -866,19 +866,19 @@ "exclude_files": { "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/parser.go": "ignore code" + "pkg/parser/parser.go": "ignore code" } }, "S1020": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1021": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", "tools/check/ut.go": "ignore code" @@ -886,7 +886,7 @@ }, "S1022": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } @@ -895,117 +895,117 @@ "exclude_files": { "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/parser.go": "ignore code" + "pkg/parser/parser.go": "ignore code" } }, "S1024": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1025": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1026": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1027": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1028": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1029": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1030": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1031": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1032": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1033": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1034": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1035": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1036": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1037": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1038": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "S1039": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } @@ -1014,12 +1014,12 @@ "exclude_files": { "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/parser.go": "ignore generated code" + "pkg/parser/parser.go": "ignore generated code" } }, "SA1019": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "/build/": "no need to linter code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", @@ -1030,32 +1030,32 @@ "br/pkg/lightning/checkpoints/glue_checkpoint.go": "cfg.TikvImporter.Addr is deprecated", "br/pkg/lightning/backend/local/local.go": "grpc Compressor/Decompressor is deprecated", "br/pkg/lightning/backend/local/compress.go": "grpc Compressor/Decompressor is deprecated", - "domain/infosync/resource_manager_client.go": "github.com/golang/protobuf deprecated" + "pkg/domain/infosync/resource_manager_client.go": "github.com/golang/protobuf deprecated" }, "only_files": { - "expression/bench_test.go": "expression/bench_test.go", - "domain/": "domain code", - "util/gctuner": "util/gctuner", - "util/cgroup": "util/cgroup code", - "util/watcher": "util/watcher", - "br/pkg/": "br/pkg", - "executor/aggregate.go": "executor/aggregate.go", - "types/": "types", - "ddl/": "enable to ddl", - "expression/builtin_cast.go": "enable expression/builtin_cast.go", - "planner/core/plan.go": "planner/core/plan.go", - "extension/": "extension code", - "resourcemanager/": "resourcemanager code", - "keyspace/": "keyspace code", - "server/": "server code", - "owner/": "owner code", - "meta": "meta code", - "timer/": "timer code" + "pkg/expression/bench_test.go": "expression/bench_test.go", + "pkg/domain/": "domain code", + "pkg/util/gctuner": "util/gctuner", + "pkg/util/cgroup": "util/cgroup code", + "pkg/util/watcher": "util/watcher", + "pkg/br/pkg/": "br/pkg", + "pkg/executor/aggregate.go": "executor/aggregate.go", + "pkg/types/": "types", + "pkg/ddl/": "enable to ddl", + "pkg/expression/builtin_cast.go": "enable expression/builtin_cast.go", + "pkg/planner/core/plan.go": "planner/core/plan.go", + "pkg/extension/": "extension code", + "pkg/resourcemanager/": "resourcemanager code", + "pkg/keyspace/": "keyspace code", + "pkg/server/": "server code", + "pkg/owner/": "owner code", + "pkg/meta": "meta code", + "pkg/timer/": "timer code" } }, "SA1029": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "/external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", ".*_test\\.go$": "ignore test code" @@ -1063,42 +1063,42 @@ }, "SA2000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA2001": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA2003": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA3000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA3001": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA4004": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "server/http_status.go": "ignore server/http_status.go", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" @@ -1106,138 +1106,138 @@ }, "SA4009": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA4018": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5001": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5002": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5003": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5004": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5005": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5007": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5008": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5009": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5010": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5011": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA5012": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA6000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA6001": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "SA6002": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", - "util/zeropool/pool_test.go": "util/zeropool/pool_test.go", + "pkg/util/zeropool/pool_test.go": "util/zeropool/pool_test.go", ".*_generated\\.go$": "ignore generated code" } }, "SA6005": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "prealloc": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "cmd/mirror": "cmd/mirror code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", - "parser/yy_parser.go": "ignore generated code", + "pkg/parser/yy_parser.go": "ignore generated code", "/cgo/": "no need to vet third party code for cgo" } }, @@ -1246,21 +1246,21 @@ "external/": "no need to vet third party code", "cmd/mirror": "no need to mirror", ".*_generated\\.go$": "ignore generated code", - "parser/yy_parser.go": "ignore generated code", - "parser/parser.go": "ignore generated code", + "pkg/parser/yy_parser.go": "ignore generated code", + "pkg/parser/parser.go": "ignore generated code", "/cgo/": "no need to vet third party code for cgo" } }, "U1000": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code" } }, "etcdconfig": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*_test.go": "ignore test code", ".*_generated\\.go$": "ignore generated code", "external/": "no need to vet third party code" @@ -1268,7 +1268,7 @@ }, "deferrecover": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*_test.go": "ignore test code", ".*_generated\\.go$": "ignore generated code", "external/": "no need to vet third party code" @@ -1276,7 +1276,7 @@ }, "QF1002": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*_test.go": "ignore test code", ".*_generated\\.go$": "ignore generated code", "external/": "no need to vet third party code" @@ -1284,7 +1284,7 @@ }, "QF1004": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*_test.go": "ignore test code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", @@ -1293,7 +1293,7 @@ }, "QF1012": { "exclude_files": { - "parser/parser.go": "parser/parser.go code", + "pkg/parser/parser.go": "parser/parser.go code", ".*_test.go": "ignore test code", "external/": "no need to vet third party code", ".*_generated\\.go$": "ignore generated code", @@ -1302,7 +1302,7 @@ }, "bootstrap": { "only_files": { - "session/": "bootstrap code" + "pkg/session/": "bootstrap code" } } } diff --git a/cmd/benchdb/BUILD.bazel b/cmd/benchdb/BUILD.bazel index 0a8da1e0ad9cb..cb9959c92f175 100644 --- a/cmd/benchdb/BUILD.bazel +++ b/cmd/benchdb/BUILD.bazel @@ -6,11 +6,11 @@ go_library( importpath = "github.com/pingcap/tidb/cmd/benchdb", visibility = ["//visibility:private"], deps = [ - "//parser/terror", - "//session", - "//store", - "//store/driver", - "//util/logutil", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/store", + "//pkg/store/driver", + "//pkg/util/logutil", "@com_github_pingcap_log//:log", "@com_github_tikv_client_go_v2//tikv", "@org_uber_go_zap//:zap", diff --git a/cmd/benchdb/main.go b/cmd/benchdb/main.go index 4b32f5b1aa1cb..3af0e74fe7cc5 100644 --- a/cmd/benchdb/main.go +++ b/cmd/benchdb/main.go @@ -24,11 +24,11 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" ) diff --git a/cmd/benchkv/BUILD.bazel b/cmd/benchkv/BUILD.bazel index 53f1f9155f3d8..110fc29365666 100644 --- a/cmd/benchkv/BUILD.bazel +++ b/cmd/benchkv/BUILD.bazel @@ -6,9 +6,9 @@ go_library( importpath = "github.com/pingcap/tidb/cmd/benchkv", visibility = ["//visibility:private"], deps = [ - "//kv", - "//parser/terror", - "//store/driver", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/store/driver", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", diff --git a/cmd/benchkv/main.go b/cmd/benchkv/main.go index 2b7cf5c9e9abb..fed6c6db31d8b 100644 --- a/cmd/benchkv/main.go +++ b/cmd/benchkv/main.go @@ -27,9 +27,9 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/store/driver" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/store/driver" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" diff --git a/cmd/benchraw/BUILD.bazel b/cmd/benchraw/BUILD.bazel index 2fbb1d8812ea3..66110bb7ae6f3 100644 --- a/cmd/benchraw/BUILD.bazel +++ b/cmd/benchraw/BUILD.bazel @@ -6,7 +6,7 @@ go_library( importpath = "github.com/pingcap/tidb/cmd/benchraw", visibility = ["//visibility:private"], deps = [ - "//parser/terror", + "//pkg/parser/terror", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", "@com_github_tikv_client_go_v2//config", diff --git a/cmd/benchraw/main.go b/cmd/benchraw/main.go index c042d59e9f180..28f196da53a6d 100644 --- a/cmd/benchraw/main.go +++ b/cmd/benchraw/main.go @@ -26,7 +26,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/tikv/client-go/v2/config" "github.com/tikv/client-go/v2/rawkv" "go.uber.org/zap" diff --git a/cmd/ddltest/BUILD.bazel b/cmd/ddltest/BUILD.bazel index 07882d4e3c403..a036bcd5bba6f 100644 --- a/cmd/ddltest/BUILD.bazel +++ b/cmd/ddltest/BUILD.bazel @@ -14,24 +14,24 @@ go_test( race = "on", shard_count = 6, deps = [ - "//config", - "//domain", - "//kv", - "//parser/model", - "//parser/terror", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn", - "//store", - "//store/driver", - "//store/gcworker", - "//table", - "//table/tables", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/logutil", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store", + "//pkg/store/driver", + "//pkg/store/gcworker", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/logutil", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", diff --git a/cmd/ddltest/column_test.go b/cmd/ddltest/column_test.go index 41006782b8a77..78393a44e4e95 100644 --- a/cmd/ddltest/column_test.go +++ b/cmd/ddltest/column_test.go @@ -24,11 +24,11 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/cmd/ddltest/ddl_test.go b/cmd/ddltest/ddl_test.go index 974094d3a1689..f3792ee17f257 100644 --- a/cmd/ddltest/ddl_test.go +++ b/cmd/ddltest/ddl_test.go @@ -33,21 +33,21 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store" - tidbdriver "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store" + tidbdriver "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/cmd/ddltest/index_test.go b/cmd/ddltest/index_test.go index dbd96aa62aa39..d92af1e90b29a 100644 --- a/cmd/ddltest/index_test.go +++ b/cmd/ddltest/index_test.go @@ -25,8 +25,8 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/table" "github.com/stretchr/testify/require" ) diff --git a/cmd/ddltest/main_test.go b/cmd/ddltest/main_test.go index 4a894eff1b7d8..55262fcc77b4f 100644 --- a/cmd/ddltest/main_test.go +++ b/cmd/ddltest/main_test.go @@ -20,8 +20,8 @@ import ( "testing" zaplog "github.com/pingcap/log" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/goleak" ) diff --git a/cmd/importer/BUILD.bazel b/cmd/importer/BUILD.bazel index b0f69c400cfc2..8753db8c5cb09 100644 --- a/cmd/importer/BUILD.bazel +++ b/cmd/importer/BUILD.bazel @@ -15,17 +15,17 @@ go_library( importpath = "github.com/pingcap/tidb/cmd/importer", visibility = ["//visibility:private"], deps = [ - "//ddl", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//statistics", - "//statistics/handle/storage", - "//types", - "//util/mathutil", - "//util/mock", + "//pkg/ddl", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/statistics", + "//pkg/statistics/handle/storage", + "//pkg/types", + "//pkg/util/mathutil", + "//pkg/util/mock", "@com_github_burntsushi_toml//:toml", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", diff --git a/cmd/importer/data.go b/cmd/importer/data.go index d041ef41cb99a..b549ad6ef445e 100644 --- a/cmd/importer/data.go +++ b/cmd/importer/data.go @@ -20,7 +20,7 @@ import ( "sync" "time" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" ) type datum struct { diff --git a/cmd/importer/db.go b/cmd/importer/db.go index b8ecf83abfc4b..786cf6cdbea29 100644 --- a/cmd/importer/db.go +++ b/cmd/importer/db.go @@ -25,7 +25,7 @@ import ( mysql2 "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "go.uber.org/zap" ) diff --git a/cmd/importer/parser.go b/cmd/importer/parser.go index 445ce437fd6dc..fc82045380c97 100644 --- a/cmd/importer/parser.go +++ b/cmd/importer/parser.go @@ -21,13 +21,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - _ "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + _ "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "go.uber.org/zap" ) diff --git a/cmd/importer/stats.go b/cmd/importer/stats.go index 1bf39ac40e69a..1307eca0bf59c 100644 --- a/cmd/importer/stats.go +++ b/cmd/importer/stats.go @@ -22,10 +22,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/model" - stats "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + stats "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/types" "go.uber.org/zap" ) diff --git a/cmd/mirror/BUILD.bazel b/cmd/mirror/BUILD.bazel index a725799ec05a8..4745704d5d2af 100644 --- a/cmd/mirror/BUILD.bazel +++ b/cmd/mirror/BUILD.bazel @@ -24,8 +24,8 @@ go_binary( "//:go.mod", "//:go.sum", "//build/patches:all_patches", - "//parser:go.mod", - "//parser:go.sum", + "//pkg/parser:go.mod", + "//pkg/parser:go.sum", "@go_sdk//:files", ], embed = [":mirror_lib"], diff --git a/cmd/mirror/mirror.go b/cmd/mirror/mirror.go index fc3d32a7fe480..70c901ce04a6c 100644 --- a/cmd/mirror/mirror.go +++ b/cmd/mirror/mirror.go @@ -145,7 +145,7 @@ func createTmpDir() (tmpdir string, err error) { if err != nil { return } - err = os.MkdirAll(filepath.Join(tmpdir, "parser"), os.ModePerm) + err = os.MkdirAll(filepath.Join(tmpdir, "pkg/parser"), os.ModePerm) if err != nil { return } @@ -157,13 +157,13 @@ func createTmpDir() (tmpdir string, err error) { if err != nil { return } - parsergomod := strings.Replace(gomod, "go.mod", "parser/go.mod", 1) - parsergosum := strings.Replace(gosum, "go.sum", "parser/go.sum", 1) + parsergomod := strings.Replace(gomod, "go.mod", "pkg/parser/go.mod", 1) + parsergosum := strings.Replace(gosum, "go.sum", "pkg/parser/go.sum", 1) err = copyFile(gomod, filepath.Join(tmpdir, "go.mod")) if err != nil { return } - err = copyFile(parsergomod, filepath.Join(tmpdir, "parser/go.mod")) + err = copyFile(parsergomod, filepath.Join(tmpdir, "pkg/parser/go.mod")) if err != nil { return } @@ -171,7 +171,7 @@ func createTmpDir() (tmpdir string, err error) { if err != nil { return } - err = copyFile(parsergosum, filepath.Join(tmpdir, "parser/go.sum")) + err = copyFile(parsergosum, filepath.Join(tmpdir, "pkg/parser/go.sum")) return } @@ -355,7 +355,7 @@ def go_deps(): # this function FIRST, before calls to pull in dependencies for # third-party libraries (e.g. rules_go, gazelle, etc.)`) for _, repoName := range sorted { - if repoName == "com_github_pingcap_tidb_parser" { + if repoName == "com_github_pingcap_tidb_pkg_parser" { continue } path := repoNameToModPath[repoName] diff --git a/cmd/pluginpkg/pluginpkg.go b/cmd/pluginpkg/pluginpkg.go index 68e09c94c2a02..b8a7492910e18 100644 --- a/cmd/pluginpkg/pluginpkg.go +++ b/cmd/pluginpkg/pluginpkg.go @@ -38,7 +38,7 @@ const codeTemplate = ` package main import ( - "github.com/pingcap/tidb/plugin" + "github.com/pingcap/tidb/pkg/plugin" ) func PluginManifest() *plugin.Manifest { diff --git a/cmd/tidb-server/BUILD.bazel b/cmd/tidb-server/BUILD.bazel new file mode 100644 index 0000000000000..79fd60d759856 --- /dev/null +++ b/cmd/tidb-server/BUILD.bazel @@ -0,0 +1,112 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") + +go_library( + name = "tidb-server_lib", + srcs = ["main.go"], + importpath = "github.com/pingcap/tidb/cmd/tidb-server", + visibility = ["//visibility:private"], + deps = [ + "//pkg/bindinfo", + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/executor", + "//pkg/executor/mppcoordmanager", + "//pkg/extension", + "//pkg/extension/_import", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/planner/core", + "//pkg/plugin", + "//pkg/privilege/privileges", + "//pkg/resourcemanager", + "//pkg/server", + "//pkg/session", + "//pkg/session/txninfo", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/store", + "//pkg/store/copr", + "//pkg/store/driver", + "//pkg/store/mockstore", + "//pkg/tidb-binlog/pump_client", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/cpuprofile", + "//pkg/util/deadlockhistory", + "//pkg/util/disk", + "//pkg/util/distrole", + "//pkg/util/domainutil", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/metricsutil", + "//pkg/util/printer", + "//pkg/util/sem", + "//pkg/util/signal", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/sys/linux", + "//pkg/util/sys/storage", + "//pkg/util/systimemon", + "//pkg/util/tiflashcompute", + "//pkg/util/topsql", + "//pkg/util/versioninfo", + "@com_github_opentracing_opentracing_go//:opentracing-go", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_golang//prometheus/push", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//txnkv/transaction", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_automaxprocs//maxprocs", + "@org_uber_go_zap//:zap", + ], +) + +go_binary( + name = "tidb-server", + embed = [":tidb-server_lib"], + visibility = ["//visibility:public"], + x_defs = { + "github.com/pingcap/tidb/pkg/parser/mysql.TiDBReleaseVersion": "{STABLE_TiDB_RELEASE_VERSION}", + "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBBuildTS": "{STABLE_TiDB_BUILD_UTCTIME}", + "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitHash": "{STABLE_TIDB_GIT_HASH}", + "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBEnterpriseExtensionGitHash": "{STABLE_TIDB_ENTERPRISE_EXTENSION_GIT_HASH}", + "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitBranch": "{STABLE_TIDB_GIT_BRANCH}", + "github.com/pingcap/tidb/pkg/util/versioninfo.TiDBEdition": "{STABLE_TIDB_EDITION}", + }, +) + +go_binary( + name = "tidb-server-check", + embed = [":tidb-server_lib"], + gc_linkopts = [ + "-X", + "github.com/pingcap/tidb/pkg/config.checkBeforeDropLDFlag=1", + ], + visibility = ["//visibility:public"], +) + +go_test( + name = "tidb-server_test", + timeout = "short", + srcs = ["main_test.go"], + embed = [":tidb-server_lib"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/tidb-server/main.go b/cmd/tidb-server/main.go similarity index 93% rename from tidb-server/main.go rename to cmd/tidb-server/main.go index 81d87ee4a9074..cd7da32550a4d 100644 --- a/tidb-server/main.go +++ b/cmd/tidb-server/main.go @@ -30,56 +30,56 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/executor/mppcoordmanager" - "github.com/pingcap/tidb/extension" - _ "github.com/pingcap/tidb/extension/_import" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - parsertypes "github.com/pingcap/tidb/parser/types" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/resourcemanager" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - kvstore "github.com/pingcap/tidb/store" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/store/mockstore" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/disk" - distroleutil "github.com/pingcap/tidb/util/distrole" - "github.com/pingcap/tidb/util/domainutil" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/metricsutil" - "github.com/pingcap/tidb/util/printer" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/signal" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - "github.com/pingcap/tidb/util/sys/linux" - storageSys "github.com/pingcap/tidb/util/sys/storage" - "github.com/pingcap/tidb/util/systimemon" - "github.com/pingcap/tidb/util/tiflashcompute" - "github.com/pingcap/tidb/util/topsql" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/executor/mppcoordmanager" + "github.com/pingcap/tidb/pkg/extension" + _ "github.com/pingcap/tidb/pkg/extension/_import" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + parsertypes "github.com/pingcap/tidb/pkg/parser/types" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/resourcemanager" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + kvstore "github.com/pingcap/tidb/pkg/store" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/store/mockstore" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/disk" + distroleutil "github.com/pingcap/tidb/pkg/util/distrole" + "github.com/pingcap/tidb/pkg/util/domainutil" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/metricsutil" + "github.com/pingcap/tidb/pkg/util/printer" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/signal" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + "github.com/pingcap/tidb/pkg/util/sys/linux" + storageSys "github.com/pingcap/tidb/pkg/util/sys/storage" + "github.com/pingcap/tidb/pkg/util/systimemon" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/util/topsql" + "github.com/pingcap/tidb/pkg/util/versioninfo" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/push" "github.com/tikv/client-go/v2/tikv" @@ -283,7 +283,7 @@ func main() { // Enable failpoints in tikv/client-go if the test API is enabled. // It appears in the main function to be set before any use of client-go to prevent data race. - if _, err := failpoint.Status("github.com/pingcap/tidb/server/enableTestAPI"); err == nil { + if _, err := failpoint.Status("github.com/pingcap/tidb/pkg/server/enableTestAPI"); err == nil { warnMsg := "tikv/client-go failpoint is enabled, this should NOT happen in the production environment" logutil.BgLogger().Warn(warnMsg) tikv.EnableFailpoints() diff --git a/cmd/tidb-server/main_test.go b/cmd/tidb-server/main_test.go new file mode 100644 index 0000000000000..15510b3156a4c --- /dev/null +++ b/cmd/tidb-server/main_test.go @@ -0,0 +1,82 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package main + +import ( + "os" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/require" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +var isCoverageServer string + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} + +// TestRunMain is a dummy test case, which contains only the main function of tidb-server, +// and it is used to generate coverage_server. +func TestRunMain(t *testing.T) { + if isCoverageServer == "1" { + main() + } +} + +func TestSetGlobalVars(t *testing.T) { + defer view.Stop() + require.Equal(t, "tikv,tiflash,tidb", variable.GetSysVar(variable.TiDBIsolationReadEngines).Value) + require.Equal(t, "1073741824", variable.GetSysVar(variable.TiDBMemQuotaQuery).Value) + require.NotEqual(t, "test", variable.GetSysVar(variable.Version).Value) + + config.UpdateGlobal(func(conf *config.Config) { + conf.IsolationRead.Engines = []string{"tikv", "tidb"} + conf.ServerVersion = "test" + }) + setGlobalVars() + + require.Equal(t, "tikv,tidb", variable.GetSysVar(variable.TiDBIsolationReadEngines).Value) + require.Equal(t, "test", variable.GetSysVar(variable.Version).Value) + require.Equal(t, variable.GetSysVar(variable.Version).Value, mysql.ServerVersion) + + config.UpdateGlobal(func(conf *config.Config) { + conf.ServerVersion = "" + }) + setGlobalVars() + + // variable.Version won't change when len(conf.ServerVersion) == 0 + require.Equal(t, "test", variable.GetSysVar(variable.Version).Value) + require.Equal(t, variable.GetSysVar(variable.Version).Value, mysql.ServerVersion) + + cfg := config.GetGlobalConfig() + require.Equal(t, cfg.Socket, variable.GetSysVar(variable.Socket).Value) + + if hostname, err := os.Hostname(); err == nil { + require.Equal(t, variable.GetSysVar(variable.Hostname).Value, hostname) + } +} diff --git a/config/BUILD.bazel b/config/BUILD.bazel deleted file mode 100644 index db2e165331bca..0000000000000 --- a/config/BUILD.bazel +++ /dev/null @@ -1,51 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "config", - srcs = [ - "config.go", - "config_util.go", - "const.go", - ], - importpath = "github.com/pingcap/tidb/config", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/streamhelper/config", - "//parser/terror", - "//util/logutil", - "//util/tiflashcompute", - "//util/tikvutil", - "//util/versioninfo", - "@com_github_burntsushi_toml//:toml", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//config", - "@com_github_uber_jaeger_client_go//config", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "config_test", - timeout = "short", - srcs = [ - "config_test.go", - "config_util_test.go", - "main_test.go", - ], - data = glob(["**"]), - embed = [":config"], - flaky = True, - shard_count = 23, - deps = [ - "//testkit/testsetup", - "//util/logutil", - "@com_github_burntsushi_toml//:toml", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_uber_jaeger_client_go//config", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 1ec031d8875d3..0000000000000 --- a/config/config.go +++ /dev/null @@ -1,1554 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package config - -import ( - "bytes" - "encoding/base64" - "encoding/json" - "flag" - "fmt" - "math" - "os" - "os/user" - "path/filepath" - "sort" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/BurntSushi/toml" - "github.com/pingcap/errors" - zaplog "github.com/pingcap/log" - logbackupconf "github.com/pingcap/tidb/br/pkg/streamhelper/config" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tiflashcompute" - "github.com/pingcap/tidb/util/tikvutil" - "github.com/pingcap/tidb/util/versioninfo" - tikvcfg "github.com/tikv/client-go/v2/config" - tracing "github.com/uber/jaeger-client-go/config" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -// Config number limitations -const ( - MaxLogFileSize = 4096 // MB - // MaxTxnEntrySize is the max value of TxnEntrySizeLimit. - MaxTxnEntrySizeLimit = 120 * 1024 * 1024 // 120MB - // DefTxnEntrySizeLimit is the default value of TxnEntrySizeLimit. - DefTxnEntrySizeLimit = 6 * 1024 * 1024 - // DefTxnTotalSizeLimit is the default value of TxnTxnTotalSizeLimit. - DefTxnTotalSizeLimit = 100 * 1024 * 1024 - SuperLargeTxnSize uint64 = 100 * 1024 * 1024 * 1024 * 1024 // 100T, we expect a txn can never be this large - // DefMaxIndexLength is the maximum index length(in bytes). This value is consistent with MySQL. - DefMaxIndexLength = 3072 - // DefMaxOfMaxIndexLength is the maximum index length(in bytes) for TiDB v3.0.7 and previous version. - DefMaxOfMaxIndexLength = 3072 * 4 - // DefIndexLimit is the limitation of index on a single table. This value is consistent with MySQL. - DefIndexLimit = 64 - // DefMaxOfIndexLimit is the maximum limitation of index on a single table for TiDB. - DefMaxOfIndexLimit = 64 * 8 - // DefPort is the default port of TiDB - DefPort = 4000 - // DefStatusPort is the default status port of TiDB - DefStatusPort = 10080 - // DefHost is the default host of TiDB - DefHost = "0.0.0.0" - // DefStatusHost is the default status host of TiDB - DefStatusHost = "0.0.0.0" - // DefTableColumnCountLimit is limit of the number of columns in a table - DefTableColumnCountLimit = 1017 - // DefMaxOfTableColumnCountLimit is maximum limitation of the number of columns in a table - DefMaxOfTableColumnCountLimit = 4096 - // DefStatsLoadConcurrencyLimit is limit of the concurrency of stats-load - DefStatsLoadConcurrencyLimit = 1 - // DefMaxOfStatsLoadConcurrencyLimit is maximum limitation of the concurrency of stats-load - DefMaxOfStatsLoadConcurrencyLimit = 128 - // DefStatsLoadQueueSizeLimit is limit of the size of stats-load request queue - DefStatsLoadQueueSizeLimit = 1 - // DefMaxOfStatsLoadQueueSizeLimit is maximum limitation of the size of stats-load request queue - DefMaxOfStatsLoadQueueSizeLimit = 100000 - // DefDDLSlowOprThreshold sets log DDL operations whose execution time exceeds the threshold value. - DefDDLSlowOprThreshold = 300 - // DefExpensiveQueryTimeThreshold indicates the time threshold of expensive query. - DefExpensiveQueryTimeThreshold = 60 - // DefExpensiveTxnTimeThreshold indicates the time threshold of expensive txn. - DefExpensiveTxnTimeThreshold = 600 - // DefMemoryUsageAlarmRatio is the threshold triggering an alarm which the memory usage of tidb-server instance exceeds. - DefMemoryUsageAlarmRatio = 0.8 - // DefTempDir is the default temporary directory path for TiDB. - DefTempDir = "/tmp/tidb" - // DefAuthTokenRefreshInterval is the default time interval to refresh tidb auth token. - DefAuthTokenRefreshInterval = time.Hour - // EnvVarKeyspaceName is the system env name for keyspace name. - EnvVarKeyspaceName = "KEYSPACE_NAME" -) - -// Valid config maps -var ( - ValidStorage = map[string]bool{ - "mocktikv": true, - "tikv": true, - "unistore": true, - } - // CheckTableBeforeDrop enable to execute `admin check table` before `drop table`. - CheckTableBeforeDrop = false - // checkBeforeDropLDFlag is a go build flag. - checkBeforeDropLDFlag = "None" - // tempStorageDirName is the default temporary storage dir name by base64 encoding a string `port/statusPort` - tempStorageDirName = encodeDefTempStorageDir(os.TempDir(), DefHost, DefStatusHost, DefPort, DefStatusPort) -) - -// InstanceConfigSection indicates a config session that has options moved to [instance] session. -type InstanceConfigSection struct { - // SectionName indicates the origin section name. - SectionName string - // NameMappings maps the origin name to the name in [instance]. - NameMappings map[string]string -} - -var ( - // sectionMovedToInstance records all config section and options that should be moved to [instance]. - sectionMovedToInstance = []InstanceConfigSection{ - { - "", - map[string]string{ - "check-mb4-value-in-utf8": "tidb_check_mb4_value_in_utf8", - "enable-collect-execution-info": "tidb_enable_collect_execution_info", - "max-server-connections": "max_connections", - "run-ddl": "tidb_enable_ddl", - }, - }, - { - "log", - map[string]string{ - "enable-slow-log": "tidb_enable_slow_log", - "slow-threshold": "tidb_slow_log_threshold", - "record-plan-in-slow-log": "tidb_record_plan_in_slow_log", - }, - }, - { - "performance", - map[string]string{ - "force-priority": "tidb_force_priority", - "memory-usage-alarm-ratio": "tidb_memory_usage_alarm_ratio", - }, - }, - { - "plugin", - map[string]string{ - "load": "plugin_load", - "dir": "plugin_dir", - }, - }, - } - - // ConflictOptions indicates the conflict config options existing in both [instance] and other sections in config file. - ConflictOptions []InstanceConfigSection - - // DeprecatedOptions indicates the config options existing in some other sections in config file. - // They should be moved to [instance] section. - DeprecatedOptions []InstanceConfigSection - - // TikvConfigLock protects against concurrent tikv config refresh - TikvConfigLock sync.Mutex -) - -// Config contains configuration options. -type Config struct { - Host string `toml:"host" json:"host"` - AdvertiseAddress string `toml:"advertise-address" json:"advertise-address"` - Port uint `toml:"port" json:"port"` - Cors string `toml:"cors" json:"cors"` - Store string `toml:"store" json:"store"` - Path string `toml:"path" json:"path"` - Socket string `toml:"socket" json:"socket"` - Lease string `toml:"lease" json:"lease"` - SplitTable bool `toml:"split-table" json:"split-table"` - TokenLimit uint `toml:"token-limit" json:"token-limit"` - TempDir string `toml:"temp-dir" json:"temp-dir"` - TempStoragePath string `toml:"tmp-storage-path" json:"tmp-storage-path"` - // TempStorageQuota describe the temporary storage Quota during query exector when TiDBEnableTmpStorageOnOOM is enabled - // If the quota exceed the capacity of the TempStoragePath, the tidb-server would exit with fatal error - TempStorageQuota int64 `toml:"tmp-storage-quota" json:"tmp-storage-quota"` // Bytes - TxnLocalLatches tikvcfg.TxnLocalLatches `toml:"-" json:"-"` - ServerVersion string `toml:"server-version" json:"server-version"` - VersionComment string `toml:"version-comment" json:"version-comment"` - TiDBEdition string `toml:"tidb-edition" json:"tidb-edition"` - TiDBReleaseVersion string `toml:"tidb-release-version" json:"tidb-release-version"` - KeyspaceName string `toml:"keyspace-name" json:"keyspace-name"` - Log Log `toml:"log" json:"log"` - Instance Instance `toml:"instance" json:"instance"` - Security Security `toml:"security" json:"security"` - Status Status `toml:"status" json:"status"` - Performance Performance `toml:"performance" json:"performance"` - PreparedPlanCache PreparedPlanCache `toml:"prepared-plan-cache" json:"prepared-plan-cache"` - OpenTracing OpenTracing `toml:"opentracing" json:"opentracing"` - ProxyProtocol ProxyProtocol `toml:"proxy-protocol" json:"proxy-protocol"` - PDClient tikvcfg.PDClient `toml:"pd-client" json:"pd-client"` - TiKVClient tikvcfg.TiKVClient `toml:"tikv-client" json:"tikv-client"` - Binlog Binlog `toml:"binlog" json:"binlog"` - CompatibleKillQuery bool `toml:"compatible-kill-query" json:"compatible-kill-query"` - PessimisticTxn PessimisticTxn `toml:"pessimistic-txn" json:"pessimistic-txn"` - MaxIndexLength int `toml:"max-index-length" json:"max-index-length"` - IndexLimit int `toml:"index-limit" json:"index-limit"` - TableColumnCountLimit uint32 `toml:"table-column-count-limit" json:"table-column-count-limit"` - GracefulWaitBeforeShutdown int `toml:"graceful-wait-before-shutdown" json:"graceful-wait-before-shutdown"` - // AlterPrimaryKey is used to control alter primary key feature. - AlterPrimaryKey bool `toml:"alter-primary-key" json:"alter-primary-key"` - // TreatOldVersionUTF8AsUTF8MB4 is use to treat old version table/column UTF8 charset as UTF8MB4. This is for compatibility. - // Currently not support dynamic modify, because this need to reload all old version schema. - TreatOldVersionUTF8AsUTF8MB4 bool `toml:"treat-old-version-utf8-as-utf8mb4" json:"treat-old-version-utf8-as-utf8mb4"` - // EnableTableLock indicate whether enable table lock. - // TODO: remove this after table lock features stable. - EnableTableLock bool `toml:"enable-table-lock" json:"enable-table-lock"` - DelayCleanTableLock uint64 `toml:"delay-clean-table-lock" json:"delay-clean-table-lock"` - SplitRegionMaxNum uint64 `toml:"split-region-max-num" json:"split-region-max-num"` - TopSQL TopSQL `toml:"top-sql" json:"top-sql"` - // RepairMode indicates that the TiDB is in the repair mode for table meta. - RepairMode bool `toml:"repair-mode" json:"repair-mode"` - RepairTableList []string `toml:"repair-table-list" json:"repair-table-list"` - // IsolationRead indicates that the TiDB reads data from which isolation level(engine and label). - IsolationRead IsolationRead `toml:"isolation-read" json:"isolation-read"` - // NewCollationsEnabledOnFirstBootstrap indicates if the new collations are enabled, it effects only when a TiDB cluster bootstrapped on the first time. - NewCollationsEnabledOnFirstBootstrap bool `toml:"new_collations_enabled_on_first_bootstrap" json:"new_collations_enabled_on_first_bootstrap"` - // Experimental contains parameters for experimental features. - Experimental Experimental `toml:"experimental" json:"experimental"` - // SkipRegisterToDashboard tells TiDB don't register itself to the dashboard. - SkipRegisterToDashboard bool `toml:"skip-register-to-dashboard" json:"skip-register-to-dashboard"` - // EnableTelemetry enables the usage data report to PingCAP. - EnableTelemetry bool `toml:"enable-telemetry" json:"enable-telemetry"` - // Labels indicates the labels set for the tidb server. The labels describe some specific properties for the tidb - // server like `zone`/`rack`/`host`. Currently, labels won't affect the tidb server except for some special - // label keys. Now we have following special keys: - // 1. 'group' is a special label key which should be automatically set by tidb-operator. We don't suggest - // users to set 'group' in labels. - // 2. 'zone' is a special key that indicates the DC location of this tidb-server. If it is set, the value for this - // key will be the default value of the session variable `txn_scope` for this tidb-server. - Labels map[string]string `toml:"labels" json:"labels"` - // EnableGlobalIndex enables creating global index. - EnableGlobalIndex bool `toml:"enable-global-index" json:"enable-global-index"` - // DeprecateIntegerDisplayWidth indicates whether deprecating the max display length for integer. - DeprecateIntegerDisplayWidth bool `toml:"deprecate-integer-display-length" json:"deprecate-integer-display-length"` - // EnableEnumLengthLimit indicates whether the enum/set element length is limited. - // According to MySQL 8.0 Refman: - // The maximum supported length of an individual SET element is M <= 255 and (M x w) <= 1020, - // where M is the element literal length and w is the number of bytes required for the maximum-length character in the character set. - // See https://dev.mysql.com/doc/refman/8.0/en/string-type-syntax.html for more details. - EnableEnumLengthLimit bool `toml:"enable-enum-length-limit" json:"enable-enum-length-limit"` - // StoresRefreshInterval indicates the interval of refreshing stores info, the unit is second. - StoresRefreshInterval uint64 `toml:"stores-refresh-interval" json:"stores-refresh-interval"` - // EnableTCP4Only enables net.Listen("tcp4",...) - // Note that: it can make lvs with toa work and thus tidb can get real client ip. - EnableTCP4Only bool `toml:"enable-tcp4-only" json:"enable-tcp4-only"` - // The client will forward the requests through the follower - // if one of the following conditions happens: - // 1. there is a network partition problem between TiDB and PD leader. - // 2. there is a network partition problem between TiDB and TiKV leader. - EnableForwarding bool `toml:"enable-forwarding" json:"enable-forwarding"` - // MaxBallastObjectSize set the max size of the ballast object, the unit is byte. - // The default value is the smallest of the following two values: 2GB or - // one quarter of the total physical memory in the current system. - MaxBallastObjectSize int `toml:"max-ballast-object-size" json:"max-ballast-object-size"` - // BallastObjectSize set the initial size of the ballast object, the unit is byte. - BallastObjectSize int `toml:"ballast-object-size" json:"ballast-object-size"` - TrxSummary TrxSummary `toml:"transaction-summary" json:"transaction-summary"` - // EnableGlobalKill indicates whether to enable global kill. - EnableGlobalKill bool `toml:"enable-global-kill" json:"enable-global-kill"` - // Enable32BitsConnectionID indicates whether to enable 32bits connection ID for global kill. - Enable32BitsConnectionID bool `toml:"enable-32bits-connection-id" json:"enable-32bits-connection-id"` - // InitializeSQLFile is a file that will be executed after first bootstrap only. - // It can be used to set GLOBAL system variable values - InitializeSQLFile string `toml:"initialize-sql-file" json:"initialize-sql-file"` - - // The following items are deprecated. We need to keep them here temporarily - // to support the upgrade process. They can be removed in future. - - // EnableBatchDML, MemQuotaQuery, OOMAction unused since bootstrap v90 - EnableBatchDML bool `toml:"enable-batch-dml" json:"enable-batch-dml"` - MemQuotaQuery int64 `toml:"mem-quota-query" json:"mem-quota-query"` - OOMAction string `toml:"oom-action" json:"oom-action"` - - // OOMUseTmpStorage unused since bootstrap v93 - OOMUseTmpStorage bool `toml:"oom-use-tmp-storage" json:"oom-use-tmp-storage"` - - // These items are deprecated because they are turned into instance system variables. - CheckMb4ValueInUTF8 AtomicBool `toml:"check-mb4-value-in-utf8" json:"check-mb4-value-in-utf8"` - EnableCollectExecutionInfo bool `toml:"enable-collect-execution-info" json:"enable-collect-execution-info"` - Plugin Plugin `toml:"plugin" json:"plugin"` - MaxServerConnections uint32 `toml:"max-server-connections" json:"max-server-connections"` - RunDDL bool `toml:"run-ddl" json:"run-ddl"` - - // These configs are related to disaggregated-tiflash mode. - DisaggregatedTiFlash bool `toml:"disaggregated-tiflash" json:"disaggregated-tiflash"` - TiFlashComputeAutoScalerType string `toml:"autoscaler-type" json:"autoscaler-type"` - TiFlashComputeAutoScalerAddr string `toml:"autoscaler-addr" json:"autoscaler-addr"` - IsTiFlashComputeFixedPool bool `toml:"is-tiflashcompute-fixed-pool" json:"is-tiflashcompute-fixed-pool"` - AutoScalerClusterID string `toml:"autoscaler-cluster-id" json:"autoscaler-cluster-id"` - UseAutoScaler bool `toml:"use-autoscaler" json:"use-autoscaler"` - - // TiDBMaxReuseChunk indicates max cached chunk num - TiDBMaxReuseChunk uint32 `toml:"tidb-max-reuse-chunk" json:"tidb-max-reuse-chunk"` - // TiDBMaxReuseColumn indicates max cached column num - TiDBMaxReuseColumn uint32 `toml:"tidb-max-reuse-column" json:"tidb-max-reuse-column"` - // TiDBEnableExitCheck indicates whether exit-checking in domain for background process - TiDBEnableExitCheck bool `toml:"tidb-enable-exit-check" json:"tidb-enable-exit-check"` - - // InMemSlowQueryTopNNum indicates the number of TopN slow queries stored in memory. - InMemSlowQueryTopNNum int `toml:"in-mem-slow-query-topn-num" json:"in-mem-slow-query-topn-num"` - // InMemSlowQueryRecentNum indicates the number of recent slow queries stored in memory. - InMemSlowQueryRecentNum int `toml:"in-mem-slow-query-recent-num" json:"in-mem-slow-query-recent-num"` -} - -// UpdateTempStoragePath is to update the `TempStoragePath` if port/statusPort was changed -// and the `tmp-storage-path` was not specified in the conf.toml or was specified the same as the default value. -func (c *Config) UpdateTempStoragePath() { - if c.TempStoragePath == tempStorageDirName { - c.TempStoragePath = encodeDefTempStorageDir(os.TempDir(), c.Host, c.Status.StatusHost, c.Port, c.Status.StatusPort) - } else { - c.TempStoragePath = encodeDefTempStorageDir(c.TempStoragePath, c.Host, c.Status.StatusHost, c.Port, c.Status.StatusPort) - } -} - -// GetTiKVConfig returns configuration options from tikvcfg -func (c *Config) GetTiKVConfig() *tikvcfg.Config { - return &tikvcfg.Config{ - CommitterConcurrency: int(tikvutil.CommitterConcurrency.Load()), - MaxTxnTTL: c.Performance.MaxTxnTTL, - TiKVClient: c.TiKVClient, - Security: c.Security.ClusterSecurity(), - PDClient: c.PDClient, - PessimisticTxn: tikvcfg.PessimisticTxn{MaxRetryCount: c.PessimisticTxn.MaxRetryCount}, - TxnLocalLatches: c.TxnLocalLatches, - StoresRefreshInterval: c.StoresRefreshInterval, - OpenTracingEnable: c.OpenTracing.Enable, - Path: c.Path, - EnableForwarding: c.EnableForwarding, - TxnScope: c.Labels["zone"], - } -} - -func encodeDefTempStorageDir(tempDir string, host, statusHost string, port, statusPort uint) string { - dirName := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v/%v:%v", host, port, statusHost, statusPort))) - osUID := "" - currentUser, err := user.Current() - if err == nil { - osUID = currentUser.Uid - } - return filepath.Join(tempDir, osUID+"_tidb", dirName, "tmp-storage") -} - -// nullableBool defaults unset bool options to unset instead of false, which enables us to know if the user has set 2 -// conflict options at the same time. -type nullableBool struct { - IsValid bool - IsTrue bool -} - -var ( - nbUnset = nullableBool{false, false} - nbFalse = nullableBool{true, false} - nbTrue = nullableBool{true, true} -) - -func (b *nullableBool) toBool() bool { - return b.IsValid && b.IsTrue -} - -func (b nullableBool) MarshalJSON() ([]byte, error) { - switch b { - case nbTrue: - return json.Marshal(true) - case nbFalse: - return json.Marshal(false) - default: - return json.Marshal(nil) - } -} - -func (b *nullableBool) UnmarshalText(text []byte) error { - str := string(text) - switch str { - case "", "null": - *b = nbUnset - return nil - case "true": - *b = nbTrue - case "false": - *b = nbFalse - default: - *b = nbUnset - return errors.New("Invalid value for bool type: " + str) - } - return nil -} - -func (b nullableBool) MarshalText() ([]byte, error) { - if !b.IsValid { - return []byte(""), nil - } - if b.IsTrue { - return []byte("true"), nil - } - return []byte("false"), nil -} - -func (b *nullableBool) UnmarshalJSON(data []byte) error { - var err error - var v interface{} - if err = json.Unmarshal(data, &v); err != nil { - return err - } - switch raw := v.(type) { - case bool: - *b = nullableBool{true, raw} - default: - *b = nbUnset - } - return err -} - -// AtomicBool is a helper type for atomic operations on a boolean value. -type AtomicBool struct { - atomicutil.Bool -} - -// NewAtomicBool creates an AtomicBool. -func NewAtomicBool(v bool) *AtomicBool { - return &AtomicBool{*atomicutil.NewBool(v)} -} - -// MarshalText implements the encoding.TextMarshaler interface. -func (b AtomicBool) MarshalText() ([]byte, error) { - if b.Load() { - return []byte("true"), nil - } - return []byte("false"), nil -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (b *AtomicBool) UnmarshalText(text []byte) error { - str := string(text) - switch str { - case "", "null": - *b = AtomicBool{*atomicutil.NewBool(false)} - case "true": - *b = AtomicBool{*atomicutil.NewBool(true)} - case "false": - *b = AtomicBool{*atomicutil.NewBool(false)} - default: - *b = AtomicBool{*atomicutil.NewBool(false)} - return errors.New("Invalid value for bool type: " + str) - } - return nil -} - -// LogBackup is the config for log backup service. -// For now, it includes the embed advancer. -type LogBackup struct { - Advancer logbackupconf.Config `toml:"advancer" json:"advancer"` - Enabled bool `toml:"enabled" json:"enabled"` -} - -// Log is the log section of config. -type Log struct { - // Log level. - Level string `toml:"level" json:"level"` - // Log format, one of json or text. - Format string `toml:"format" json:"format"` - // Disable automatic timestamps in output. Deprecated: use EnableTimestamp instead. - DisableTimestamp nullableBool `toml:"disable-timestamp" json:"disable-timestamp"` - // EnableTimestamp enables automatic timestamps in log output. - EnableTimestamp nullableBool `toml:"enable-timestamp" json:"enable-timestamp"` - // DisableErrorStack stops annotating logs with the full stack error - // message. Deprecated: use EnableErrorStack instead. - DisableErrorStack nullableBool `toml:"disable-error-stack" json:"disable-error-stack"` - // EnableErrorStack enables annotating logs with the full stack error - // message. - EnableErrorStack nullableBool `toml:"enable-error-stack" json:"enable-error-stack"` - // File log config. - File logutil.FileLogConfig `toml:"file" json:"file"` - - SlowQueryFile string `toml:"slow-query-file" json:"slow-query-file"` - // ExpensiveThreshold is deprecated. - ExpensiveThreshold uint `toml:"expensive-threshold" json:"expensive-threshold"` - - // The following items are deprecated. We need to keep them here temporarily - // to support the upgrade process. They can be removed in future. - - // QueryLogMaxLen unused since bootstrap v90 - QueryLogMaxLen uint64 `toml:"query-log-max-len" json:"query-log-max-len"` - // EnableSlowLog, SlowThreshold, RecordPlanInSlowLog are deprecated. - EnableSlowLog AtomicBool `toml:"enable-slow-log" json:"enable-slow-log"` - SlowThreshold uint64 `toml:"slow-threshold" json:"slow-threshold"` - RecordPlanInSlowLog uint32 `toml:"record-plan-in-slow-log" json:"record-plan-in-slow-log"` - - // Make tidb panic if write log operation hang in `Timeout` seconds - Timeout int `toml:"timeout" json:"timeout"` -} - -// Instance is the section of instance scope system variables. -type Instance struct { - // These variables only exist in [instance] section. - - // TiDBGeneralLog is used to log every query in the server in info level. - TiDBGeneralLog bool `toml:"tidb_general_log" json:"tidb_general_log"` - // EnablePProfSQLCPU is used to add label sql label to pprof result. - EnablePProfSQLCPU bool `toml:"tidb_pprof_sql_cpu" json:"tidb_pprof_sql_cpu"` - // DDLSlowOprThreshold sets log DDL operations whose execution time exceeds the threshold value. - DDLSlowOprThreshold uint32 `toml:"ddl_slow_threshold" json:"ddl_slow_threshold"` - // ExpensiveQueryTimeThreshold indicates the time threshold of expensive query. - ExpensiveQueryTimeThreshold uint64 `toml:"tidb_expensive_query_time_threshold" json:"tidb_expensive_query_time_threshold"` - // ExpensiveTxnTimeThreshold indicates the time threshold of expensive transaction. - ExpensiveTxnTimeThreshold uint64 `toml:"tidb_expensive_txn_time_threshold" json:"tidb_expensive_txn_time_threshold"` - // StmtSummaryEnablePersistent indicates whether to enable file persistence for stmtsummary. - StmtSummaryEnablePersistent bool `toml:"tidb_stmt_summary_enable_persistent" json:"tidb_stmt_summary_enable_persistent"` - // StmtSummaryFilename indicates the file name written by stmtsummary - // when StmtSummaryEnablePersistent is true. - StmtSummaryFilename string `toml:"tidb_stmt_summary_filename" json:"tidb_stmt_summary_filename"` - // StmtSummaryFileMaxDays indicates how many days the files written by - // stmtsummary will be kept when StmtSummaryEnablePersistent is true. - StmtSummaryFileMaxDays int `toml:"tidb_stmt_summary_file_max_days" json:"tidb_stmt_summary_file_max_days"` - // StmtSummaryFileMaxSize indicates the maximum size (in mb) of a single file - // written by stmtsummary when StmtSummaryEnablePersistent is true. - StmtSummaryFileMaxSize int `toml:"tidb_stmt_summary_file_max_size" json:"tidb_stmt_summary_file_max_size"` - // StmtSummaryFileMaxBackups indicates the maximum number of files written - // by stmtsummary when StmtSummaryEnablePersistent is true. - StmtSummaryFileMaxBackups int `toml:"tidb_stmt_summary_file_max_backups" json:"tidb_stmt_summary_file_max_backups"` - - // These variables exist in both 'instance' section and another place. - // The configuration in 'instance' section takes precedence. - - EnableSlowLog AtomicBool `toml:"tidb_enable_slow_log" json:"tidb_enable_slow_log"` - SlowThreshold uint64 `toml:"tidb_slow_log_threshold" json:"tidb_slow_log_threshold"` - RecordPlanInSlowLog uint32 `toml:"tidb_record_plan_in_slow_log" json:"tidb_record_plan_in_slow_log"` - CheckMb4ValueInUTF8 AtomicBool `toml:"tidb_check_mb4_value_in_utf8" json:"tidb_check_mb4_value_in_utf8"` - ForcePriority string `toml:"tidb_force_priority" json:"tidb_force_priority"` - MemoryUsageAlarmRatio float64 `toml:"tidb_memory_usage_alarm_ratio" json:"tidb_memory_usage_alarm_ratio"` - // EnableCollectExecutionInfo enables the TiDB to collect execution info. - EnableCollectExecutionInfo AtomicBool `toml:"tidb_enable_collect_execution_info" json:"tidb_enable_collect_execution_info"` - PluginDir string `toml:"plugin_dir" json:"plugin_dir"` - PluginLoad string `toml:"plugin_load" json:"plugin_load"` - // MaxConnections is the maximum permitted number of simultaneous client connections. - MaxConnections uint32 `toml:"max_connections" json:"max_connections"` - TiDBEnableDDL AtomicBool `toml:"tidb_enable_ddl" json:"tidb_enable_ddl"` - TiDBRCReadCheckTS bool `toml:"tidb_rc_read_check_ts" json:"tidb_rc_read_check_ts"` - // TiDBServiceScope indicates the role for tidb for distributed task framework. - TiDBServiceScope string `toml:"tidb_service_scope" json:"tidb_service_scope"` -} - -func (l *Log) getDisableTimestamp() bool { - if l.EnableTimestamp == nbUnset && l.DisableTimestamp == nbUnset { - return false - } - if l.EnableTimestamp == nbUnset { - return l.DisableTimestamp.toBool() - } - return !l.EnableTimestamp.toBool() -} - -func (l *Log) getDisableErrorStack() bool { - if l.EnableErrorStack == nbUnset && l.DisableErrorStack == nbUnset { - return true - } - if l.EnableErrorStack == nbUnset { - return l.DisableErrorStack.toBool() - } - return !l.EnableErrorStack.toBool() -} - -// The following constants represents the valid action configurations for Security.SpilledFileEncryptionMethod. -// "plaintext" means encryption is disabled. -// NOTE: Although the values is case insensitive, we should use lower-case -// strings because the configuration value will be transformed to lower-case -// string and compared with these constants in the further usage. -const ( - SpilledFileEncryptionMethodPlaintext = "plaintext" - SpilledFileEncryptionMethodAES128CTR = "aes128-ctr" -) - -// Security is the security section of the config. -type Security struct { - SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"` - SSLCA string `toml:"ssl-ca" json:"ssl-ca"` - SSLCert string `toml:"ssl-cert" json:"ssl-cert"` - SSLKey string `toml:"ssl-key" json:"ssl-key"` - ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"` - ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"` - ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"` - ClusterVerifyCN []string `toml:"cluster-verify-cn" json:"cluster-verify-cn"` - // Used for auth plugin `tidb_session_token`. - SessionTokenSigningCert string `toml:"session-token-signing-cert" json:"session-token-signing-cert"` - SessionTokenSigningKey string `toml:"session-token-signing-key" json:"session-token-signing-key"` - // If set to "plaintext", the spilled files will not be encrypted. - SpilledFileEncryptionMethod string `toml:"spilled-file-encryption-method" json:"spilled-file-encryption-method"` - // EnableSEM prevents SUPER users from having full access. - EnableSEM bool `toml:"enable-sem" json:"enable-sem"` - // Allow automatic TLS certificate generation - AutoTLS bool `toml:"auto-tls" json:"auto-tls"` - MinTLSVersion string `toml:"tls-version" json:"tls-version"` - RSAKeySize int `toml:"rsa-key-size" json:"rsa-key-size"` - SecureBootstrap bool `toml:"secure-bootstrap" json:"secure-bootstrap"` - // The path of the JWKS for tidb_auth_token authentication - AuthTokenJWKS string `toml:"auth-token-jwks" json:"auth-token-jwks"` - // The refresh time interval of JWKS - AuthTokenRefreshInterval string `toml:"auth-token-refresh-interval" json:"auth-token-refresh-interval"` - // Disconnect directly when the password is expired - DisconnectOnExpiredPassword bool `toml:"disconnect-on-expired-password" json:"disconnect-on-expired-password"` -} - -// The ErrConfigValidationFailed error is used so that external callers can do a type assertion -// to defer handling of this specific error when someone does not want strict type checking. -// This is needed only because logging hasn't been set up at the time we parse the config file. -// This should all be ripped out once strict config checking is made the default behavior. -type ErrConfigValidationFailed struct { - confFile string - UndecodedItems []string -} - -func (e *ErrConfigValidationFailed) Error() string { - return fmt.Sprintf("config file %s contained invalid configuration options: %s; check "+ - "TiDB manual to make sure this option has not been deprecated and removed from your TiDB "+ - "version if the option does not appear to be a typo", e.confFile, strings.Join( - e.UndecodedItems, ", ")) -} - -// ErrConfigInstanceSection error is used to warning the user -// which config options should be moved to 'instance'. -type ErrConfigInstanceSection struct { - confFile string - configSections *[]InstanceConfigSection - deprecatedSections *[]InstanceConfigSection -} - -func (e *ErrConfigInstanceSection) Error() string { - var builder strings.Builder - if len(*e.configSections) > 0 { - builder.WriteString("Conflict configuration options exists on both [instance] section and some other sections. ") - } - if len(*e.deprecatedSections) > 0 { - builder.WriteString("Some configuration options should be moved to [instance] section. ") - } - builder.WriteString("Please use the latter config options in [instance] instead: ") - for _, configSection := range *e.configSections { - for oldName, newName := range configSection.NameMappings { - builder.WriteString(fmt.Sprintf(" (%s, %s)", oldName, newName)) - } - } - for _, configSection := range *e.deprecatedSections { - for oldName, newName := range configSection.NameMappings { - builder.WriteString(fmt.Sprintf(" (%s, %s)", oldName, newName)) - } - } - builder.WriteString(".") - - return builder.String() -} - -// ClusterSecurity returns Security info for cluster -func (s *Security) ClusterSecurity() tikvcfg.Security { - return tikvcfg.NewSecurity(s.ClusterSSLCA, s.ClusterSSLCert, s.ClusterSSLKey, s.ClusterVerifyCN) -} - -// Status is the status section of the config. -type Status struct { - StatusHost string `toml:"status-host" json:"status-host"` - MetricsAddr string `toml:"metrics-addr" json:"metrics-addr"` - StatusPort uint `toml:"status-port" json:"status-port"` - MetricsInterval uint `toml:"metrics-interval" json:"metrics-interval"` - ReportStatus bool `toml:"report-status" json:"report-status"` - RecordQPSbyDB bool `toml:"record-db-qps" json:"record-db-qps"` - RecordDBLabel bool `toml:"record-db-label" json:"record-db-label"` - // After a duration of this time in seconds if the server doesn't see any activity it pings - // the client to see if the transport is still alive. - GRPCKeepAliveTime uint `toml:"grpc-keepalive-time" json:"grpc-keepalive-time"` - // After having pinged for keepalive check, the server waits for a duration of timeout in seconds - // and if no activity is seen even after that the connection is closed. - GRPCKeepAliveTimeout uint `toml:"grpc-keepalive-timeout" json:"grpc-keepalive-timeout"` - // The number of max concurrent streams/requests on a client connection. - GRPCConcurrentStreams uint `toml:"grpc-concurrent-streams" json:"grpc-concurrent-streams"` - // Sets window size for stream. The default value is 2MB. - GRPCInitialWindowSize int `toml:"grpc-initial-window-size" json:"grpc-initial-window-size"` - // Set maximum message length in bytes that gRPC can send. `-1` means unlimited. The default value is 10MB. - GRPCMaxSendMsgSize int `toml:"grpc-max-send-msg-size" json:"grpc-max-send-msg-size"` -} - -// Performance is the performance section of the config. -type Performance struct { - MaxProcs uint `toml:"max-procs" json:"max-procs"` - // Deprecated: use ServerMemoryQuota instead - MaxMemory uint64 `toml:"max-memory" json:"max-memory"` - ServerMemoryQuota uint64 `toml:"server-memory-quota" json:"server-memory-quota"` - StatsLease string `toml:"stats-lease" json:"stats-lease"` - StmtCountLimit uint `toml:"stmt-count-limit" json:"stmt-count-limit"` - PseudoEstimateRatio float64 `toml:"pseudo-estimate-ratio" json:"pseudo-estimate-ratio"` - BindInfoLease string `toml:"bind-info-lease" json:"bind-info-lease"` - TxnEntrySizeLimit uint64 `toml:"txn-entry-size-limit" json:"txn-entry-size-limit"` - TxnTotalSizeLimit uint64 `toml:"txn-total-size-limit" json:"txn-total-size-limit"` - TCPKeepAlive bool `toml:"tcp-keep-alive" json:"tcp-keep-alive"` - TCPNoDelay bool `toml:"tcp-no-delay" json:"tcp-no-delay"` - CrossJoin bool `toml:"cross-join" json:"cross-join"` - DistinctAggPushDown bool `toml:"distinct-agg-push-down" json:"distinct-agg-push-down"` - // Whether enable projection push down for coprocessors (both tikv & tiflash), default false. - ProjectionPushDown bool `toml:"projection-push-down" json:"projection-push-down"` - MaxTxnTTL uint64 `toml:"max-txn-ttl" json:"max-txn-ttl"` - // Deprecated - MemProfileInterval string `toml:"-" json:"-"` - - IndexUsageSyncLease string `toml:"index-usage-sync-lease" json:"index-usage-sync-lease"` - PlanReplayerGCLease string `toml:"plan-replayer-gc-lease" json:"plan-replayer-gc-lease"` - GOGC int `toml:"gogc" json:"gogc"` - EnforceMPP bool `toml:"enforce-mpp" json:"enforce-mpp"` - StatsLoadConcurrency uint `toml:"stats-load-concurrency" json:"stats-load-concurrency"` - StatsLoadQueueSize uint `toml:"stats-load-queue-size" json:"stats-load-queue-size"` - AnalyzePartitionConcurrencyQuota uint `toml:"analyze-partition-concurrency-quota" json:"analyze-partition-concurrency-quota"` - PlanReplayerDumpWorkerConcurrency uint `toml:"plan-replayer-dump-worker-concurrency" json:"plan-replayer-dump-worker-concurrency"` - EnableStatsCacheMemQuota bool `toml:"enable-stats-cache-mem-quota" json:"enable-stats-cache-mem-quota"` - // The following items are deprecated. We need to keep them here temporarily - // to support the upgrade process. They can be removed in future. - - // CommitterConcurrency, RunAutoAnalyze unused since bootstrap v90 - CommitterConcurrency int `toml:"committer-concurrency" json:"committer-concurrency"` - RunAutoAnalyze bool `toml:"run-auto-analyze" json:"run-auto-analyze"` - - // ForcePriority, MemoryUsageAlarmRatio are deprecated. - ForcePriority string `toml:"force-priority" json:"force-priority"` - MemoryUsageAlarmRatio float64 `toml:"memory-usage-alarm-ratio" json:"memory-usage-alarm-ratio"` - - EnableLoadFMSketch bool `toml:"enable-load-fmsketch" json:"enable-load-fmsketch"` - - LiteInitStats bool `toml:"lite-init-stats" json:"lite-init-stats"` - - // If ForceInitStats is true, when tidb starts up, it doesn't provide service until init stats is finished. - // If ForceInitStats is false, tidb can provide service before init stats is finished. Note that during the period - // of init stats the optimizer may make bad decisions due to pseudo stats. - ForceInitStats bool `toml:"force-init-stats" json:"force-init-stats"` -} - -// PlanCache is the PlanCache section of the config. -type PlanCache struct { - Enabled bool `toml:"enabled" json:"enabled"` - Capacity uint `toml:"capacity" json:"capacity"` - Shards uint `toml:"shards" json:"shards"` -} - -// PreparedPlanCache is the PreparedPlanCache section of the config. -type PreparedPlanCache struct { - Enabled bool `toml:"enabled" json:"enabled"` - Capacity uint `toml:"capacity" json:"capacity"` - MemoryGuardRatio float64 `toml:"memory-guard-ratio" json:"memory-guard-ratio"` -} - -// OpenTracing is the opentracing section of the config. -type OpenTracing struct { - Enable bool `toml:"enable" json:"enable"` - RPCMetrics bool `toml:"rpc-metrics" json:"rpc-metrics"` - Sampler OpenTracingSampler `toml:"sampler" json:"sampler"` - Reporter OpenTracingReporter `toml:"reporter" json:"reporter"` -} - -// OpenTracingSampler is the config for opentracing sampler. -// See https://godoc.org/github.com/uber/jaeger-client-go/config#SamplerConfig -type OpenTracingSampler struct { - Type string `toml:"type" json:"type"` - Param float64 `toml:"param" json:"param"` - SamplingServerURL string `toml:"sampling-server-url" json:"sampling-server-url"` - MaxOperations int `toml:"max-operations" json:"max-operations"` - SamplingRefreshInterval time.Duration `toml:"sampling-refresh-interval" json:"sampling-refresh-interval"` -} - -// OpenTracingReporter is the config for opentracing reporter. -// See https://godoc.org/github.com/uber/jaeger-client-go/config#ReporterConfig -type OpenTracingReporter struct { - QueueSize int `toml:"queue-size" json:"queue-size"` - BufferFlushInterval time.Duration `toml:"buffer-flush-interval" json:"buffer-flush-interval"` - LogSpans bool `toml:"log-spans" json:"log-spans"` - LocalAgentHostPort string `toml:"local-agent-host-port" json:"local-agent-host-port"` -} - -// ProxyProtocol is the PROXY protocol section of the config. -type ProxyProtocol struct { - // PROXY protocol acceptable client networks. - // Empty string means disable PROXY protocol, - // * means all networks. - Networks string `toml:"networks" json:"networks"` - // PROXY protocol header read timeout, Unit is second. - HeaderTimeout uint `toml:"header-timeout" json:"header-timeout"` - // PROXY protocol header process fallback-able. - // If set to true and not send PROXY protocol header, connection will return connection's client IP. - Fallbackable bool `toml:"fallbackable" json:"fallbackable"` -} - -// Binlog is the config for binlog. -type Binlog struct { - Enable bool `toml:"enable" json:"enable"` - // If IgnoreError is true, when writing binlog meets error, TiDB would - // ignore the error. - IgnoreError bool `toml:"ignore-error" json:"ignore-error"` - WriteTimeout string `toml:"write-timeout" json:"write-timeout"` - // Use socket file to write binlog, for compatible with kafka version tidb-binlog. - BinlogSocket string `toml:"binlog-socket" json:"binlog-socket"` - // The strategy for sending binlog to pump, value can be "range" or "hash" now. - Strategy string `toml:"strategy" json:"strategy"` -} - -// PessimisticTxn is the config for pessimistic transaction. -type PessimisticTxn struct { - // The max count of retry for a single statement in a pessimistic transaction. - MaxRetryCount uint `toml:"max-retry-count" json:"max-retry-count"` - // The max count of deadlock events that will be recorded in the information_schema.deadlocks table. - DeadlockHistoryCapacity uint `toml:"deadlock-history-capacity" json:"deadlock-history-capacity"` - // Whether retryable deadlocks (in-statement deadlocks) are collected to the information_schema.deadlocks table. - DeadlockHistoryCollectRetryable bool `toml:"deadlock-history-collect-retryable" json:"deadlock-history-collect-retryable"` - // PessimisticAutoCommit represents if true it means the auto-commit transactions will be in pessimistic mode. - PessimisticAutoCommit AtomicBool `toml:"pessimistic-auto-commit" json:"pessimistic-auto-commit"` - // ConstraintCheckInPlacePessimistic is the default value for the session variable `tidb_constraint_check_in_place_pessimistic` - ConstraintCheckInPlacePessimistic bool `toml:"constraint-check-in-place-pessimistic" json:"constraint-check-in-place-pessimistic"` -} - -// TrxSummary is the config for transaction summary collecting. -type TrxSummary struct { - // how many transaction summary in `transaction_summary` each TiDB node should keep. - TransactionSummaryCapacity uint `toml:"transaction-summary-capacity" json:"transaction-summary-capacity"` - // how long a transaction should be executed to make it be recorded in `transaction_id_digest`. - TransactionIDDigestMinDuration uint `toml:"transaction-id-digest-min-duration" json:"transaction-id-digest-min-duration"` -} - -// Valid Validatse TrxSummary configs -func (config *TrxSummary) Valid() error { - if config.TransactionSummaryCapacity > 5000 { - return errors.New("transaction-summary.transaction-summary-capacity should not be larger than 5000") - } - return nil -} - -// DefaultPessimisticTxn returns the default configuration for PessimisticTxn -func DefaultPessimisticTxn() PessimisticTxn { - return PessimisticTxn{ - MaxRetryCount: 256, - DeadlockHistoryCapacity: 10, - DeadlockHistoryCollectRetryable: false, - PessimisticAutoCommit: *NewAtomicBool(false), - ConstraintCheckInPlacePessimistic: true, - } -} - -// DefaultTrxSummary returns the default configuration for TrxSummary collector -func DefaultTrxSummary() TrxSummary { - // TrxSummary is not enabled by default before GA - return TrxSummary{ - TransactionSummaryCapacity: 500, - TransactionIDDigestMinDuration: 2147483647, - } -} - -// Plugin is the config for plugin -type Plugin struct { - Dir string `toml:"dir" json:"dir"` - Load string `toml:"load" json:"load"` -} - -// TopSQL is the config for TopSQL. -type TopSQL struct { - // The TopSQL's data receiver address. - ReceiverAddress string `toml:"receiver-address" json:"receiver-address"` -} - -// IsolationRead is the config for isolation read. -type IsolationRead struct { - // Engines filters tidb-server access paths by engine type. - Engines []string `toml:"engines" json:"engines"` -} - -// Experimental controls the features that are still experimental: their semantics, interfaces are subject to change. -// Using these features in the production environment is not recommended. -type Experimental struct { - // Whether enable creating expression index. - AllowsExpressionIndex bool `toml:"allow-expression-index" json:"allow-expression-index"` - // Whether enable charset feature. - EnableNewCharset bool `toml:"enable-new-charset" json:"-"` -} - -var defTiKVCfg = tikvcfg.DefaultConfig() -var defaultConf = Config{ - Host: DefHost, - AdvertiseAddress: "", - Port: DefPort, - Socket: "/tmp/tidb-{Port}.sock", - Cors: "", - Store: "unistore", - Path: "/tmp/tidb", - RunDDL: true, - SplitTable: true, - Lease: "45s", - TokenLimit: 1000, - OOMUseTmpStorage: true, - TempDir: DefTempDir, - TempStorageQuota: -1, - TempStoragePath: tempStorageDirName, - MemQuotaQuery: 1 << 30, - OOMAction: "cancel", - EnableBatchDML: false, - CheckMb4ValueInUTF8: *NewAtomicBool(true), - MaxIndexLength: 3072, - IndexLimit: 64, - TableColumnCountLimit: 1017, - AlterPrimaryKey: false, - TreatOldVersionUTF8AsUTF8MB4: true, - EnableTableLock: false, - DelayCleanTableLock: 0, - SplitRegionMaxNum: 1000, - RepairMode: false, - RepairTableList: []string{}, - MaxServerConnections: 0, - TxnLocalLatches: defTiKVCfg.TxnLocalLatches, - GracefulWaitBeforeShutdown: 0, - ServerVersion: "", - TiDBEdition: "", - VersionComment: "", - TiDBReleaseVersion: "", - Log: Log{ - Level: "info", - Format: "text", - File: logutil.NewFileLogConfig(logutil.DefaultLogMaxSize), - SlowQueryFile: "tidb-slow.log", - SlowThreshold: logutil.DefaultSlowThreshold, - ExpensiveThreshold: 10000, // ExpensiveThreshold is deprecated. - DisableErrorStack: nbUnset, - EnableErrorStack: nbUnset, // If both options are nbUnset, getDisableErrorStack() returns true - EnableTimestamp: nbUnset, - DisableTimestamp: nbUnset, // If both options are nbUnset, getDisableTimestamp() returns false - QueryLogMaxLen: logutil.DefaultQueryLogMaxLen, - RecordPlanInSlowLog: logutil.DefaultRecordPlanInSlowLog, - EnableSlowLog: *NewAtomicBool(logutil.DefaultTiDBEnableSlowLog), - }, - Instance: Instance{ - TiDBGeneralLog: false, - EnablePProfSQLCPU: false, - DDLSlowOprThreshold: DefDDLSlowOprThreshold, - ExpensiveQueryTimeThreshold: DefExpensiveQueryTimeThreshold, - ExpensiveTxnTimeThreshold: DefExpensiveTxnTimeThreshold, - StmtSummaryEnablePersistent: false, - StmtSummaryFilename: "tidb-statements.log", - StmtSummaryFileMaxDays: 3, - StmtSummaryFileMaxSize: 64, - StmtSummaryFileMaxBackups: 0, - EnableSlowLog: *NewAtomicBool(logutil.DefaultTiDBEnableSlowLog), - SlowThreshold: logutil.DefaultSlowThreshold, - RecordPlanInSlowLog: logutil.DefaultRecordPlanInSlowLog, - CheckMb4ValueInUTF8: *NewAtomicBool(true), - ForcePriority: "NO_PRIORITY", - MemoryUsageAlarmRatio: DefMemoryUsageAlarmRatio, - EnableCollectExecutionInfo: *NewAtomicBool(true), - PluginDir: "/data/deploy/plugin", - PluginLoad: "", - MaxConnections: 0, - TiDBEnableDDL: *NewAtomicBool(true), - TiDBRCReadCheckTS: false, - TiDBServiceScope: "", - }, - Status: Status{ - ReportStatus: true, - StatusHost: DefStatusHost, - StatusPort: DefStatusPort, - MetricsInterval: 15, - RecordQPSbyDB: false, - RecordDBLabel: false, - GRPCKeepAliveTime: 10, - GRPCKeepAliveTimeout: 3, - GRPCConcurrentStreams: 1024, - GRPCInitialWindowSize: 2 * 1024 * 1024, - GRPCMaxSendMsgSize: math.MaxInt32, - }, - Performance: Performance{ - MaxMemory: 0, - ServerMemoryQuota: 0, - MemoryUsageAlarmRatio: DefMemoryUsageAlarmRatio, - TCPKeepAlive: true, - TCPNoDelay: true, - CrossJoin: true, - StatsLease: "3s", - StmtCountLimit: 5000, - PseudoEstimateRatio: 0.8, - ForcePriority: "NO_PRIORITY", - BindInfoLease: "3s", - TxnEntrySizeLimit: DefTxnEntrySizeLimit, - TxnTotalSizeLimit: DefTxnTotalSizeLimit, - DistinctAggPushDown: false, - ProjectionPushDown: false, - CommitterConcurrency: defTiKVCfg.CommitterConcurrency, - MaxTxnTTL: defTiKVCfg.MaxTxnTTL, // 1hour - // TODO: set indexUsageSyncLease to 60s. - IndexUsageSyncLease: "0s", - GOGC: 100, - EnforceMPP: false, - PlanReplayerGCLease: "10m", - StatsLoadConcurrency: 5, - StatsLoadQueueSize: 1000, - AnalyzePartitionConcurrencyQuota: 16, - PlanReplayerDumpWorkerConcurrency: 1, - EnableStatsCacheMemQuota: true, - RunAutoAnalyze: true, - EnableLoadFMSketch: false, - LiteInitStats: true, - ForceInitStats: true, - }, - ProxyProtocol: ProxyProtocol{ - Networks: "", - HeaderTimeout: 5, - Fallbackable: false, - }, - PreparedPlanCache: PreparedPlanCache{ - Enabled: true, - Capacity: 100, - MemoryGuardRatio: 0.1, - }, - OpenTracing: OpenTracing{ - Enable: false, - Sampler: OpenTracingSampler{ - Type: "const", - Param: 1.0, - }, - Reporter: OpenTracingReporter{}, - }, - PDClient: defTiKVCfg.PDClient, - TiKVClient: defTiKVCfg.TiKVClient, - Binlog: Binlog{ - WriteTimeout: "15s", - Strategy: "range", - }, - Plugin: Plugin{ - Dir: "/data/deploy/plugin", - Load: "", - }, - PessimisticTxn: DefaultPessimisticTxn(), - IsolationRead: IsolationRead{ - Engines: []string{"tikv", "tiflash", "tidb"}, - }, - Experimental: Experimental{}, - EnableCollectExecutionInfo: true, - EnableTelemetry: false, - Labels: make(map[string]string), - EnableGlobalIndex: false, - Security: Security{ - SpilledFileEncryptionMethod: SpilledFileEncryptionMethodPlaintext, - EnableSEM: false, - AutoTLS: false, - RSAKeySize: 4096, - AuthTokenJWKS: "", - AuthTokenRefreshInterval: DefAuthTokenRefreshInterval.String(), - DisconnectOnExpiredPassword: true, - }, - DeprecateIntegerDisplayWidth: false, - EnableEnumLengthLimit: true, - StoresRefreshInterval: defTiKVCfg.StoresRefreshInterval, - EnableForwarding: defTiKVCfg.EnableForwarding, - NewCollationsEnabledOnFirstBootstrap: true, - EnableGlobalKill: true, - Enable32BitsConnectionID: true, - TrxSummary: DefaultTrxSummary(), - DisaggregatedTiFlash: false, - TiFlashComputeAutoScalerType: tiflashcompute.DefASStr, - TiFlashComputeAutoScalerAddr: tiflashcompute.DefAWSAutoScalerAddr, - IsTiFlashComputeFixedPool: false, - AutoScalerClusterID: "", - UseAutoScaler: false, - TiDBMaxReuseChunk: 64, - TiDBMaxReuseColumn: 256, - TiDBEnableExitCheck: false, - InMemSlowQueryTopNNum: 30, - InMemSlowQueryRecentNum: 500, -} - -var ( - globalConf atomic.Value -) - -// NewConfig creates a new config instance with default value. -func NewConfig() *Config { - conf := defaultConf - return &conf -} - -// GetGlobalConfig returns the global configuration for this server. -// It should store configuration from command line and configuration file. -// Other parts of the system can read the global configuration use this function. -func GetGlobalConfig() *Config { - return globalConf.Load().(*Config) -} - -// StoreGlobalConfig stores a new config to the globalConf. It mostly uses in the test to avoid some data races. -func StoreGlobalConfig(config *Config) { - globalConf.Store(config) - TikvConfigLock.Lock() - defer TikvConfigLock.Unlock() - cfg := *config.GetTiKVConfig() - tikvcfg.StoreGlobalConfig(&cfg) -} - -// removedConfig contains items that are no longer supported. -// they might still be in the config struct to support import, -// but are not actively used. -var removedConfig = map[string]struct{}{ - "pessimistic-txn.ttl": {}, - "pessimistic-txn.enable": {}, - "log.file.log-rotate": {}, - "log.log-slow-query": {}, - "txn-local-latches": {}, - "txn-local-latches.enabled": {}, - "txn-local-latches.capacity": {}, - "performance.max-memory": {}, - "max-txn-time-use": {}, - "experimental.allow-auto-random": {}, - "enable-redact-log": {}, // use variable tidb_redact_log instead - "enable-streaming": {}, - "performance.mem-profile-interval": {}, - "security.require-secure-transport": {}, - "lower-case-table-names": {}, - "stmt-summary": {}, - "stmt-summary.enable": {}, - "stmt-summary.enable-internal-query": {}, - "stmt-summary.max-stmt-count": {}, - "stmt-summary.max-sql-length": {}, - "stmt-summary.refresh-interval": {}, - "stmt-summary.history-size": {}, - "enable-batch-dml": {}, // use tidb_enable_batch_dml - "mem-quota-query": {}, - "log.query-log-max-len": {}, - "performance.committer-concurrency": {}, - "experimental.enable-global-kill": {}, - "performance.run-auto-analyze": {}, // use tidb_enable_auto_analyze - // use tidb_enable_prepared_plan_cache, tidb_prepared_plan_cache_size and tidb_prepared_plan_cache_memory_guard_ratio - "prepared-plan-cache.enabled": {}, - "prepared-plan-cache.capacity": {}, - "prepared-plan-cache.memory-guard-ratio": {}, - "oom-action": {}, - "check-mb4-value-in-utf8": {}, // use tidb_check_mb4_value_in_utf8 - "enable-collect-execution-info": {}, // use tidb_enable_collect_execution_info - "log.enable-slow-log": {}, // use tidb_enable_slow_log - "log.slow-threshold": {}, // use tidb_slow_log_threshold - "log.record-plan-in-slow-log": {}, // use tidb_record_plan_in_slow_log - "log.expensive-threshold": {}, - "performance.force-priority": {}, // use tidb_force_priority - "performance.memory-usage-alarm-ratio": {}, // use tidb_memory_usage_alarm_ratio - "plugin.load": {}, // use plugin_load - "plugin.dir": {}, // use plugin_dir - "performance.feedback-probability": {}, // This feature is deprecated - "performance.query-feedback-limit": {}, - "oom-use-tmp-storage": {}, // use tidb_enable_tmp_storage_on_oom - "max-server-connections": {}, // use sysvar max_connections - "run-ddl": {}, // use sysvar tidb_enable_ddl - "instance.tidb_memory_usage_alarm_ratio": {}, // use sysvar tidb_memory_usage_alarm_ratio -} - -// isAllRemovedConfigItems returns true if all the items that couldn't validate -// belong to the list of removedConfig items. -func isAllRemovedConfigItems(items []string) bool { - for _, item := range items { - if _, ok := removedConfig[item]; !ok { - return false - } - } - return true -} - -// InitializeConfig initialize the global config handler. -// The function enforceCmdArgs is used to merge the config file with command arguments: -// For example, if you start TiDB by the command "./tidb-server --port=3000", the port number should be -// overwritten to 3000 and ignore the port number in the config file. -func InitializeConfig(confPath string, configCheck, configStrict bool, enforceCmdArgs func(*Config, *flag.FlagSet), fset *flag.FlagSet) { - cfg := GetGlobalConfig() - var err error - if confPath != "" { - if err = cfg.Load(confPath); err != nil { - // Unused config item error turns to warnings. - if tmp, ok := err.(*ErrConfigValidationFailed); ok { - // This block is to accommodate an interim situation where strict config checking - // is not the default behavior of TiDB. The warning message must be deferred until - // logging has been set up. After strict config checking is the default behavior, - // This should all be removed. - if (!configCheck && !configStrict) || isAllRemovedConfigItems(tmp.UndecodedItems) { - fmt.Fprintln(os.Stderr, err.Error()) - err = nil - } - } else if tmp, ok := err.(*ErrConfigInstanceSection); ok { - logutil.BgLogger().Warn(tmp.Error()) - err = nil - } - } - // In configCheck we always print out which options in the config file - // have been removed. This helps users upgrade better. - if configCheck { - err = cfg.RemovedVariableCheck(confPath) - if err != nil { - logutil.BgLogger().Warn(err.Error()) - err = nil // treat as warning - } - } - - terror.MustNil(err) - } else { - // configCheck should have the config file specified. - if configCheck { - fmt.Fprintln(os.Stderr, "config check failed", errors.New("no config file specified for config-check")) - os.Exit(1) - } - } - enforceCmdArgs(cfg, fset) - - if err := cfg.Valid(); err != nil { - if !filepath.IsAbs(confPath) { - if tmp, err := filepath.Abs(confPath); err == nil { - confPath = tmp - } - } - fmt.Fprintln(os.Stderr, "load config file:", confPath) - fmt.Fprintln(os.Stderr, "invalid config", err) - os.Exit(1) - } - if configCheck { - fmt.Println("config check successful") - os.Exit(0) - } - StoreGlobalConfig(cfg) -} - -// RemovedVariableCheck checks if the config file contains any items -// which have been removed. These will not take effect any more. -func (c *Config) RemovedVariableCheck(confFile string) error { - metaData, err := toml.DecodeFile(confFile, c) - if err != nil { - return err - } - var removed []string - for item := range removedConfig { - // We need to split the string to account for the top level - // and the section hierarchy of config. - tmp := strings.Split(item, ".") - if len(tmp) == 2 && metaData.IsDefined(tmp[0], tmp[1]) { - removed = append(removed, item) - } else if len(tmp) == 1 && metaData.IsDefined(tmp[0]) { - removed = append(removed, item) - } - } - if len(removed) > 0 { - sort.Strings(removed) // deterministic for tests - return fmt.Errorf("The following configuration options are no longer supported in this version of TiDB. Check the release notes for more information: %s", strings.Join(removed, ", ")) - } - return nil -} - -// Load loads config options from a toml file. -func (c *Config) Load(confFile string) error { - metaData, err := toml.DecodeFile(confFile, c) - if c.TokenLimit == 0 { - c.TokenLimit = 1000 - } - // If any items in confFile file are not mapped into the Config struct, issue - // an error and stop the server from starting. - undecoded := metaData.Undecoded() - if len(undecoded) > 0 && err == nil { - var undecodedItems []string - for _, item := range undecoded { - undecodedItems = append(undecodedItems, item.String()) - } - err = &ErrConfigValidationFailed{confFile, undecodedItems} - } - - for _, section := range sectionMovedToInstance { - newConflictSection := InstanceConfigSection{SectionName: section.SectionName, NameMappings: map[string]string{}} - newDeprecatedSection := InstanceConfigSection{SectionName: section.SectionName, NameMappings: map[string]string{}} - for oldName, newName := range section.NameMappings { - if section.SectionName == "" && metaData.IsDefined(oldName) || - section.SectionName != "" && metaData.IsDefined(section.SectionName, oldName) { - if metaData.IsDefined("instance", newName) { - newConflictSection.NameMappings[oldName] = newName - } else { - newDeprecatedSection.NameMappings[oldName] = newName - } - } - } - if len(newConflictSection.NameMappings) > 0 { - ConflictOptions = append(ConflictOptions, newConflictSection) - } - if len(newDeprecatedSection.NameMappings) > 0 { - DeprecatedOptions = append(DeprecatedOptions, newDeprecatedSection) - } - } - if len(ConflictOptions) > 0 || len(DeprecatedOptions) > 0 { - // Give a warning that the 'instance' section should be used. - err = &ErrConfigInstanceSection{confFile, &ConflictOptions, &DeprecatedOptions} - } - - return err -} - -// Valid checks if this config is valid. -func (c *Config) Valid() error { - if c.Log.EnableErrorStack == c.Log.DisableErrorStack && c.Log.EnableErrorStack != nbUnset { - logutil.BgLogger().Warn(fmt.Sprintf("\"enable-error-stack\" (%v) conflicts \"disable-error-stack\" (%v). \"disable-error-stack\" is deprecated, please use \"enable-error-stack\" instead. disable-error-stack is ignored.", c.Log.EnableErrorStack, c.Log.DisableErrorStack)) - // if two options conflict, we will use the value of EnableErrorStack - c.Log.DisableErrorStack = nbUnset - } - if c.Log.EnableTimestamp == c.Log.DisableTimestamp && c.Log.EnableTimestamp != nbUnset { - logutil.BgLogger().Warn(fmt.Sprintf("\"enable-timestamp\" (%v) conflicts \"disable-timestamp\" (%v). \"disable-timestamp\" is deprecated, please use \"enable-timestamp\" instead", c.Log.EnableTimestamp, c.Log.DisableTimestamp)) - // if two options conflict, we will use the value of EnableTimestamp - c.Log.DisableTimestamp = nbUnset - } - if c.Security.SkipGrantTable && !hasRootPrivilege() { - return fmt.Errorf("TiDB run with skip-grant-table need root privilege") - } - if !ValidStorage[c.Store] { - nameList := make([]string, 0, len(ValidStorage)) - for k, v := range ValidStorage { - if v { - nameList = append(nameList, k) - } - } - return fmt.Errorf("invalid store=%s, valid storages=%v", c.Store, nameList) - } - if c.Store == "mocktikv" && !c.Instance.TiDBEnableDDL.Load() { - return fmt.Errorf("can't disable DDL on mocktikv") - } - if c.MaxIndexLength < DefMaxIndexLength || c.MaxIndexLength > DefMaxOfMaxIndexLength { - return fmt.Errorf("max-index-length should be [%d, %d]", DefMaxIndexLength, DefMaxOfMaxIndexLength) - } - if c.IndexLimit < DefIndexLimit || c.IndexLimit > DefMaxOfIndexLimit { - return fmt.Errorf("index-limit should be [%d, %d]", DefIndexLimit, DefMaxOfIndexLimit) - } - if c.Log.File.MaxSize > MaxLogFileSize { - return fmt.Errorf("invalid max log file size=%v which is larger than max=%v", c.Log.File.MaxSize, MaxLogFileSize) - } - if c.TableColumnCountLimit < DefTableColumnCountLimit || c.TableColumnCountLimit > DefMaxOfTableColumnCountLimit { - return fmt.Errorf("table-column-limit should be [%d, %d]", DefIndexLimit, DefMaxOfTableColumnCountLimit) - } - - // txn-local-latches - if err := c.TxnLocalLatches.Valid(); err != nil { - return err - } - - // For tikvclient. - if err := c.TiKVClient.Valid(); err != nil { - return err - } - if err := c.TrxSummary.Valid(); err != nil { - return err - } - - if c.Performance.TxnTotalSizeLimit > 1<<40 { - return fmt.Errorf("txn-total-size-limit should be less than %d", 1<<40) - } - - if c.Instance.MemoryUsageAlarmRatio > 1 || c.Instance.MemoryUsageAlarmRatio < 0 { - return fmt.Errorf("tidb_memory_usage_alarm_ratio in [Instance] must be greater than or equal to 0 and less than or equal to 1") - } - - if len(c.IsolationRead.Engines) < 1 { - return fmt.Errorf("the number of [isolation-read]engines for isolation read should be at least 1") - } - for _, engine := range c.IsolationRead.Engines { - if engine != "tidb" && engine != "tikv" && engine != "tiflash" { - return fmt.Errorf("type of [isolation-read]engines can't be %v should be one of tidb or tikv or tiflash", engine) - } - } - - // test security - c.Security.SpilledFileEncryptionMethod = strings.ToLower(c.Security.SpilledFileEncryptionMethod) - switch c.Security.SpilledFileEncryptionMethod { - case SpilledFileEncryptionMethodPlaintext, SpilledFileEncryptionMethodAES128CTR: - default: - return fmt.Errorf("unsupported [security]spilled-file-encryption-method %v, TiDB only supports [%v, %v]", - c.Security.SpilledFileEncryptionMethod, SpilledFileEncryptionMethodPlaintext, SpilledFileEncryptionMethodAES128CTR) - } - - // check stats load config - if c.Performance.StatsLoadConcurrency < DefStatsLoadConcurrencyLimit || c.Performance.StatsLoadConcurrency > DefMaxOfStatsLoadConcurrencyLimit { - return fmt.Errorf("stats-load-concurrency should be [%d, %d]", DefStatsLoadConcurrencyLimit, DefMaxOfStatsLoadConcurrencyLimit) - } - if c.Performance.StatsLoadQueueSize < DefStatsLoadQueueSizeLimit || c.Performance.StatsLoadQueueSize > DefMaxOfStatsLoadQueueSizeLimit { - return fmt.Errorf("stats-load-queue-size should be [%d, %d]", DefStatsLoadQueueSizeLimit, DefMaxOfStatsLoadQueueSizeLimit) - } - - // Check tiflash_compute topo fetch is valid. - if c.DisaggregatedTiFlash && c.UseAutoScaler { - if !tiflashcompute.IsValidAutoScalerConfig(c.TiFlashComputeAutoScalerType) { - return fmt.Errorf("invalid AutoScaler type, expect %s, %s or %s, got %s", - tiflashcompute.MockASStr, tiflashcompute.AWSASStr, tiflashcompute.GCPASStr, c.TiFlashComputeAutoScalerType) - } - if c.TiFlashComputeAutoScalerAddr == "" { - return fmt.Errorf("autoscaler-addr cannot be empty when disaggregated-tiflash mode is true") - } - } - - // test log level - l := zap.NewAtomicLevel() - return l.UnmarshalText([]byte(c.Log.Level)) -} - -// UpdateGlobal updates the global config, and provide a restore function that can be used to restore to the original. -func UpdateGlobal(f func(conf *Config)) { - g := GetGlobalConfig() - newConf := *g - f(&newConf) - StoreGlobalConfig(&newConf) -} - -// RestoreFunc gets a function that restore the config to the current value. -func RestoreFunc() (restore func()) { - g := GetGlobalConfig() - return func() { - StoreGlobalConfig(g) - } -} - -func hasRootPrivilege() bool { - return os.Geteuid() == 0 -} - -// TableLockEnabled uses to check whether enabled the table lock feature. -func TableLockEnabled() bool { - return GetGlobalConfig().EnableTableLock -} - -// TableLockDelayClean uses to get the time of delay clean table lock. -var TableLockDelayClean = func() uint64 { - return GetGlobalConfig().DelayCleanTableLock -} - -// ToLogConfig converts *Log to *logutil.LogConfig. -func (l *Log) ToLogConfig() *logutil.LogConfig { - return logutil.NewLogConfig(l.Level, l.Format, l.SlowQueryFile, l.File, l.getDisableTimestamp(), - func(config *zaplog.Config) { config.DisableErrorVerbose = l.getDisableErrorStack() }, - func(config *zaplog.Config) { config.Timeout = l.Timeout }, - ) -} - -// ToTracingConfig converts *OpenTracing to *tracing.Configuration. -func (t *OpenTracing) ToTracingConfig() *tracing.Configuration { - ret := &tracing.Configuration{ - Disabled: !t.Enable, - RPCMetrics: t.RPCMetrics, - Reporter: &tracing.ReporterConfig{}, - Sampler: &tracing.SamplerConfig{}, - } - ret.Reporter.QueueSize = t.Reporter.QueueSize - ret.Reporter.BufferFlushInterval = t.Reporter.BufferFlushInterval - ret.Reporter.LogSpans = t.Reporter.LogSpans - ret.Reporter.LocalAgentHostPort = t.Reporter.LocalAgentHostPort - - ret.Sampler.Type = t.Sampler.Type - ret.Sampler.Param = t.Sampler.Param - ret.Sampler.SamplingServerURL = t.Sampler.SamplingServerURL - ret.Sampler.MaxOperations = t.Sampler.MaxOperations - ret.Sampler.SamplingRefreshInterval = t.Sampler.SamplingRefreshInterval - return ret -} - -func init() { - initByLDFlags(versioninfo.TiDBEdition, checkBeforeDropLDFlag) -} - -func initByLDFlags(edition, checkBeforeDropLDFlag string) { - if edition != versioninfo.CommunityEdition { - defaultConf.EnableTelemetry = false - } - conf := defaultConf - StoreGlobalConfig(&conf) - if checkBeforeDropLDFlag == "1" { - CheckTableBeforeDrop = true - } -} - -// hideConfig is used to filter a single line of config for hiding. -var hideConfig = []string{ - "performance.index-usage-sync-lease", -} - -// GetJSONConfig returns the config as JSON with hidden items removed -// It replaces the earlier HideConfig() which used strings.Split() in -// an way that didn't work for similarly named items (like enable). -func GetJSONConfig() (string, error) { - j, err := json.Marshal(GetGlobalConfig()) - if err != nil { - return "", err - } - - jsonValue := make(map[string]interface{}) - err = json.Unmarshal(j, &jsonValue) - if err != nil { - return "", err - } - - removedPaths := make([]string, 0, len(removedConfig)+len(hideConfig)) - for removedItem := range removedConfig { - removedPaths = append(removedPaths, removedItem) - } - removedPaths = append(removedPaths, hideConfig...) - - for _, path := range removedPaths { - s := strings.Split(path, ".") - curValue := jsonValue - for i, key := range s { - if i == len(s)-1 { - delete(curValue, key) - } - if curValue[key] == nil { - break - } - mapValue, ok := curValue[key].(map[string]interface{}) - if !ok { - break - } - curValue = mapValue - } - } - - buf, err := json.Marshal(jsonValue) - if err != nil { - return "", err - } - - var resBuf bytes.Buffer - if err = json.Indent(&resBuf, buf, "", "\t"); err != nil { - return "", err - } - return resBuf.String(), nil -} - -// ContainHiddenConfig checks whether it contains the configuration that needs to be hidden. -func ContainHiddenConfig(s string) bool { - s = strings.ToLower(s) - for _, hc := range hideConfig { - if strings.Contains(s, hc) { - return true - } - } - for dc := range removedConfig { - if strings.Contains(s, dc) { - return true - } - } - return false -} - -// GetGlobalKeyspaceName is used to get global keyspace name -// from config file or command line. -func GetGlobalKeyspaceName() string { - return GetGlobalConfig().KeyspaceName -} diff --git a/config/main_test.go b/config/main_test.go deleted file mode 100644 index e3538afda9259..0000000000000 --- a/config/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package config - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("syscall.Syscall"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/BUILD.bazel b/ddl/BUILD.bazel deleted file mode 100644 index 43740e59f2a1f..0000000000000 --- a/ddl/BUILD.bazel +++ /dev/null @@ -1,314 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package_group( - name = "ddl_friend", - packages = [ - "-//planner/...", - "//...", - ], -) - -go_library( - name = "ddl", - srcs = [ - "backfilling.go", - "backfilling_clean_s3.go", - "backfilling_dispatcher.go", - "backfilling_dist_scheduler.go", - "backfilling_import_cloud.go", - "backfilling_import_local.go", - "backfilling_merge_sort.go", - "backfilling_operators.go", - "backfilling_read_index.go", - "backfilling_scheduler.go", - "callback.go", - "cluster.go", - "column.go", - "constant.go", - "constraint.go", - "ddl.go", - "ddl_algorithm.go", - "ddl_api.go", - "ddl_tiflash_api.go", - "ddl_worker.go", - "ddl_workerpool.go", - "delete_range.go", - "delete_range_util.go", - "dist_owner.go", - "foreign_key.go", - "generated_column.go", - "index.go", - "index_cop.go", - "index_merge_tmp.go", - "job_table.go", - "mock.go", - "multi_schema_change.go", - "options.go", - "partition.go", - "placement_policy.go", - "reorg.go", - "resource_group.go", - "rollingback.go", - "sanity_check.go", - "schema.go", - "sequence.go", - "split_region.go", - "stat.go", - "table.go", - "table_lock.go", - "ttl.go", - ], - importpath = "github.com/pingcap/tidb/ddl", - visibility = [ - ":ddl_friend", - ], - deps = [ - "//br/pkg/lightning/backend", - "//br/pkg/lightning/backend/external", - "//br/pkg/lightning/common", - "//br/pkg/lightning/config", - "//br/pkg/storage", - "//config", - "//ddl/copr", - "//ddl/ingest", - "//ddl/internal/session", - "//ddl/label", - "//ddl/placement", - "//ddl/resourcegroup", - "//ddl/syncer", - "//ddl/util", - "//distsql", - "//disttask/framework/dispatcher", - "//disttask/framework/handle", - "//disttask/framework/proto", - "//disttask/framework/scheduler", - "//disttask/framework/scheduler/execute", - "//disttask/framework/storage", - "//disttask/operator", - "//domain/infosync", - "//domain/resourcegroup", - "//expression", - "//infoschema", - "//kv", - "//meta", - "//meta/autoid", - "//metrics", - "//owner", - "//parser", - "//parser/ast", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/types", - "//privilege", - "//resourcemanager/pool/workerpool", - "//resourcemanager/util", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//statistics", - "//statistics/handle", - "//store/copr", - "//store/driver/backoff", - "//store/helper", - "//table", - "//table/tables", - "//tablecodec", - "//tidb-binlog/pump_client", - "//types", - "//types/parser_driver", - "//util", - "//util/backoff", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/disttask", - "//util/domainutil", - "//util/filter", - "//util/gcutil", - "//util/hack", - "//util/intest", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/mock", - "//util/ranger", - "//util/resourcegrouptag", - "//util/rowDecoder", - "//util/rowcodec", - "//util/set", - "//util/size", - "//util/slice", - "//util/sqlexec", - "//util/stringutil", - "//util/syncutil", - "//util/timeutil", - "//util/topsql", - "//util/topsql/state", - "@com_github_google_uuid//:uuid", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//txnkv/rangetask", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//:client", - "@org_golang_x_sync//errgroup", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "ddl_test", - timeout = "moderate", - srcs = [ - "attributes_sql_test.go", - "backfilling_dispatcher_test.go", - "backfilling_test.go", - "cancel_test.go", - "cluster_test.go", - "column_change_test.go", - "column_modify_test.go", - "column_test.go", - "column_type_change_test.go", - "constraint_test.go", - "db_cache_test.go", - "db_change_failpoints_test.go", - "db_change_test.go", - "db_integration_test.go", - "db_rename_test.go", - "db_table_test.go", - "db_test.go", - "ddl_algorithm_test.go", - "ddl_api_test.go", - "ddl_error_test.go", - "ddl_test.go", - "ddl_worker_test.go", - "ddl_workerpool_test.go", - "export_test.go", - "fail_test.go", - "foreign_key_test.go", - "index_change_test.go", - "index_cop_test.go", - "index_modify_test.go", - "integration_test.go", - "job_table_test.go", - "main_test.go", - "modify_column_test.go", - "multi_schema_change_test.go", - "mv_index_test.go", - "options_test.go", - "partition_test.go", - "placement_policy_ddl_test.go", - "placement_policy_test.go", - "placement_sql_test.go", - "primary_key_handle_test.go", - "reorg_partition_test.go", - "repair_table_test.go", - "restart_test.go", - "rollingback_test.go", - "schema_test.go", - "sequence_test.go", - "stat_test.go", - "table_modify_test.go", - "table_split_test.go", - "table_test.go", - "tiflash_replica_test.go", - "ttl_test.go", - ], - embed = [":ddl"], - flaky = True, - shard_count = 50, - deps = [ - "//autoid_service", - "//config", - "//ddl/copr", - "//ddl/ingest", - "//ddl/internal/session", - "//ddl/placement", - "//ddl/schematracker", - "//ddl/syncer", - "//ddl/testutil", - "//ddl/util", - "//ddl/util/callback", - "//disttask/framework/proto", - "//domain", - "//domain/infosync", - "//errno", - "//executor", - "//infoschema", - "//keyspace", - "//kv", - "//meta", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "//planner/core", - "//server", - "//session", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//store/gcworker", - "//store/helper", - "//store/mockstore", - "//table", - "//table/tables", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/domainutil", - "//util/gcutil", - "//util/logutil", - "//util/mathutil", - "//util/mock", - "//util/sem", - "//util/sqlexec", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//:client", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ddl/callback.go b/ddl/callback.go deleted file mode 100644 index bfc25bd9d6c48..0000000000000 --- a/ddl/callback.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// Interceptor is used for DDL. -type Interceptor interface { - // OnGetInfoSchema is an intercept which is called in the function ddl.GetInfoSchema(). It is used in the tests. - OnGetInfoSchema(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema -} - -// BaseInterceptor implements Interceptor. -type BaseInterceptor struct{} - -// OnGetInfoSchema implements Interceptor.OnGetInfoSchema interface. -func (*BaseInterceptor) OnGetInfoSchema(_ sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { - return is -} - -// Callback is used for DDL. -type Callback interface { - ReorgCallback - // OnChanged is called after a ddl statement is finished. - OnChanged(err error) error - // OnSchemaStateChanged is called after a schema state is changed. - OnSchemaStateChanged(schemaVer int64) - // OnJobRunBefore is called before running job. - OnJobRunBefore(job *model.Job) - // OnJobRunAfter is called after running job. - OnJobRunAfter(job *model.Job) - // OnJobUpdated is called after the running job is updated. - OnJobUpdated(job *model.Job) - // OnWatched is called after watching owner is completed. - OnWatched(ctx context.Context) - // OnGetJobBefore is called before getting job. - OnGetJobBefore(jobType string) - // OnGetJobAfter is called after getting job. - OnGetJobAfter(jobType string, job *model.Job) -} - -// BaseCallback implements Callback.OnChanged interface. -type BaseCallback struct { -} - -// OnChanged implements Callback interface. -func (*BaseCallback) OnChanged(err error) error { - return err -} - -// OnSchemaStateChanged implements Callback interface. -func (*BaseCallback) OnSchemaStateChanged(_ int64) { - // Nothing to do. -} - -// OnJobRunBefore implements Callback.OnJobRunBefore interface. -func (*BaseCallback) OnJobRunBefore(_ *model.Job) { - // Nothing to do. -} - -// OnJobRunAfter implements Callback.OnJobRunAfter interface. -func (*BaseCallback) OnJobRunAfter(_ *model.Job) { - // Nothing to do. -} - -// OnJobUpdated implements Callback.OnJobUpdated interface. -func (*BaseCallback) OnJobUpdated(_ *model.Job) { - // Nothing to do. -} - -// OnWatched implements Callback.OnWatched interface. -func (*BaseCallback) OnWatched(_ context.Context) { - // Nothing to do. -} - -// OnGetJobBefore implements Callback.OnGetJobBefore interface. -func (*BaseCallback) OnGetJobBefore(_ string) { - // Nothing to do. -} - -// OnGetJobAfter implements Callback.OnGetJobAfter interface. -func (*BaseCallback) OnGetJobAfter(_ string, _ *model.Job) { - // Nothing to do. -} - -// OnUpdateReorgInfo implements ReorgCallback interface. -func (*BaseCallback) OnUpdateReorgInfo(_ *model.Job, _ int64) { -} - -// DomainReloader is used to avoid import loop. -type DomainReloader interface { - Reload() error -} - -// ReorgCallback is the callback for DDL reorganization. -type ReorgCallback interface { - // OnUpdateReorgInfo is called after updating reorg info for partitions. - OnUpdateReorgInfo(job *model.Job, pid int64) -} - -// ****************************** Start of Customized DDL Callback Instance **************************************** - -// DefaultCallback is the default callback that TiDB will use. -type DefaultCallback struct { - *BaseCallback - do DomainReloader -} - -// OnChanged overrides ddl Callback interface. -func (c *DefaultCallback) OnChanged(err error) error { - if err != nil { - return err - } - logutil.BgLogger().Info("performing DDL change, must reload") - - err = c.do.Reload() - if err != nil { - logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) - } - - return nil -} - -// OnSchemaStateChanged overrides the ddl Callback interface. -func (c *DefaultCallback) OnSchemaStateChanged(_ int64) { - err := c.do.Reload() - if err != nil { - logutil.BgLogger().Error("domain callback failed on schema state changed", zap.Error(err)) - } -} - -func newDefaultCallBack(do DomainReloader) Callback { - return &DefaultCallback{BaseCallback: &BaseCallback{}, do: do} -} - -// ****************************** End of Default DDL Callback Instance ********************************************* - -// ****************************** Start of CTC DDL Callback Instance *********************************************** - -// ctcCallback is the customized callback that TiDB will use. -// ctc is named from column type change, here after we call them ctc for short. -type ctcCallback struct { - *BaseCallback - do DomainReloader -} - -// OnChanged overrides ddl Callback interface. -func (c *ctcCallback) OnChanged(err error) error { - if err != nil { - return err - } - logutil.BgLogger().Info("performing DDL change, must reload") - - err = c.do.Reload() - if err != nil { - logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) - } - return nil -} - -// OnSchemaStateChanged overrides the ddl Callback interface. -func (c *ctcCallback) OnSchemaStateChanged(_ int64) { - err := c.do.Reload() - if err != nil { - logutil.BgLogger().Error("domain callback failed on schema state changed", zap.Error(err)) - } -} - -// OnJobRunBefore is used to run the user customized logic of `onJobRunBefore` first. -func (*ctcCallback) OnJobRunBefore(job *model.Job) { - log.Info("on job run before", zap.String("job", job.String())) - // Only block the ctc type ddl here. - if job.Type != model.ActionModifyColumn { - return - } - switch job.SchemaState { - case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: - logutil.BgLogger().Warn(fmt.Sprintf("[DDL_HOOK] Hang for 0.5 seconds on %s state triggered", job.SchemaState.String())) - time.Sleep(500 * time.Millisecond) - } -} - -func newCTCCallBack(do DomainReloader) Callback { - return &ctcCallback{do: do} -} - -// ****************************** End of CTC DDL Callback Instance *************************************************** - -var ( - customizedCallBackRegisterMap = map[string]func(do DomainReloader) Callback{} -) - -func init() { - // init the callback register map. - customizedCallBackRegisterMap["default_hook"] = newDefaultCallBack - customizedCallBackRegisterMap["ctc_hook"] = newCTCCallBack -} - -// GetCustomizedHook get the hook registered in the hookMap. -func GetCustomizedHook(s string) (func(do DomainReloader) Callback, error) { - s = strings.ToLower(s) - s = strings.TrimSpace(s) - fact, ok := customizedCallBackRegisterMap[s] - if !ok { - logutil.BgLogger().Error("bad ddl hook " + s) - return nil, errors.Errorf("ddl hook `%s` is not found in hook registered map", s) - } - return fact, nil -} diff --git a/ddl/cluster.go b/ddl/cluster.go deleted file mode 100644 index ca3e90154c63d..0000000000000 --- a/ddl/cluster.go +++ /dev/null @@ -1,829 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "bytes" - "cmp" - "context" - "encoding/hex" - "fmt" - "slices" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/errorpb" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/filter" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/logutil" - tikvstore "github.com/tikv/client-go/v2/kv" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - "github.com/tikv/client-go/v2/txnkv/rangetask" - "go.uber.org/atomic" - "go.uber.org/zap" -) - -var pdScheduleKey = []string{ - "merge-schedule-limit", -} - -const ( - flashbackMaxBackoff = 1800000 // 1800s - flashbackTimeout = 3 * time.Minute // 3min -) - -const ( - pdScheduleArgsOffset = 1 + iota - gcEnabledOffset - autoAnalyzeOffset - readOnlyOffset - totalLockedRegionsOffset - startTSOffset - commitTSOffset - ttlJobEnableOffSet - keyRangesOffset -) - -func closePDSchedule() error { - closeMap := make(map[string]interface{}) - for _, key := range pdScheduleKey { - closeMap[key] = 0 - } - return infosync.SetPDScheduleConfig(context.Background(), closeMap) -} - -func savePDSchedule(job *model.Job) error { - retValue, err := infosync.GetPDScheduleConfig(context.Background()) - if err != nil { - return err - } - saveValue := make(map[string]interface{}) - for _, key := range pdScheduleKey { - saveValue[key] = retValue[key] - } - job.Args[pdScheduleArgsOffset] = &saveValue - return nil -} - -func recoverPDSchedule(pdScheduleParam map[string]interface{}) error { - if pdScheduleParam == nil { - return nil - } - return infosync.SetPDScheduleConfig(context.Background(), pdScheduleParam) -} - -func getStoreGlobalMinSafeTS(s kv.Storage) time.Time { - minSafeTS := s.GetMinSafeTS(kv.GlobalTxnScope) - // Inject mocked SafeTS for test. - failpoint.Inject("injectSafeTS", func(val failpoint.Value) { - injectTS := val.(int) - minSafeTS = uint64(injectTS) - }) - return oracle.GetTimeFromTS(minSafeTS) -} - -// ValidateFlashbackTS validates that flashBackTS in range [gcSafePoint, currentTS). -func ValidateFlashbackTS(ctx context.Context, sctx sessionctx.Context, flashBackTS uint64) error { - currentTS, err := sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) - // If we fail to calculate currentTS from local time, fallback to get a timestamp from PD. - if err != nil { - metrics.ValidateReadTSFromPDCount.Inc() - currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) - if err != nil { - return errors.Errorf("fail to validate flashback timestamp: %v", err) - } - currentTS = currentVer.Ver - } - oracleFlashbackTS := oracle.GetTimeFromTS(flashBackTS) - if oracleFlashbackTS.After(oracle.GetTimeFromTS(currentTS)) { - return errors.Errorf("cannot set flashback timestamp to future time") - } - - flashbackGetMinSafeTimeTimeout := time.Minute - failpoint.Inject("changeFlashbackGetMinSafeTimeTimeout", func(val failpoint.Value) { - t := val.(int) - flashbackGetMinSafeTimeTimeout = time.Duration(t) - }) - - start := time.Now() - minSafeTime := getStoreGlobalMinSafeTS(sctx.GetStore()) - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - for oracleFlashbackTS.After(minSafeTime) { - if time.Since(start) >= flashbackGetMinSafeTimeTimeout { - return errors.Errorf("cannot set flashback timestamp after min-resolved-ts(%s)", minSafeTime) - } - select { - case <-ticker.C: - minSafeTime = getStoreGlobalMinSafeTS(sctx.GetStore()) - case <-ctx.Done(): - return ctx.Err() - } - } - - gcSafePoint, err := gcutil.GetGCSafePoint(sctx) - if err != nil { - return err - } - - return gcutil.ValidateSnapshotWithGCSafePoint(flashBackTS, gcSafePoint) -} - -func getTiDBTTLJobEnable(sess sessionctx.Context) (string, error) { - val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBTTLJobEnable) - if err != nil { - return "", errors.Trace(err) - } - return val, nil -} - -func setTiDBTTLJobEnable(ctx context.Context, sess sessionctx.Context, value string) error { - return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBTTLJobEnable, value) -} - -func setTiDBEnableAutoAnalyze(ctx context.Context, sess sessionctx.Context, value string) error { - return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBEnableAutoAnalyze, value) -} - -func getTiDBEnableAutoAnalyze(sess sessionctx.Context) (string, error) { - val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableAutoAnalyze) - if err != nil { - return "", errors.Trace(err) - } - return val, nil -} - -func setTiDBSuperReadOnly(ctx context.Context, sess sessionctx.Context, value string) error { - return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBSuperReadOnly, value) -} - -func getTiDBSuperReadOnly(sess sessionctx.Context) (string, error) { - val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBSuperReadOnly) - if err != nil { - return "", errors.Trace(err) - } - return val, nil -} - -func isFlashbackSupportedDDLAction(action model.ActionType) bool { - switch action { - case model.ActionSetTiFlashReplica, model.ActionUpdateTiFlashReplicaStatus, model.ActionAlterPlacementPolicy, - model.ActionAlterTablePlacement, model.ActionAlterTablePartitionPlacement, model.ActionCreatePlacementPolicy, - model.ActionDropPlacementPolicy, model.ActionModifySchemaDefaultPlacement, - model.ActionAlterTableAttributes, model.ActionAlterTablePartitionAttributes: - return false - default: - return true - } -} - -func checkSystemSchemaID(t *meta.Meta, schemaID int64, flashbackTSString string) error { - if schemaID <= 0 { - return nil - } - dbInfo, err := t.GetDatabase(schemaID) - if err != nil || dbInfo == nil { - return errors.Trace(err) - } - if filter.IsSystemSchema(dbInfo.Name.L) { - return errors.Errorf("Detected modified system table during [%s, now), can't do flashback", flashbackTSString) - } - return nil -} - -func checkAndSetFlashbackClusterInfo(se sessionctx.Context, d *ddlCtx, t *meta.Meta, job *model.Job, flashbackTS uint64) (err error) { - if err = ValidateFlashbackTS(d.ctx, se, flashbackTS); err != nil { - return err - } - - if err = gcutil.DisableGC(se); err != nil { - return err - } - if err = closePDSchedule(); err != nil { - return err - } - if err = setTiDBEnableAutoAnalyze(d.ctx, se, variable.Off); err != nil { - return err - } - if err = setTiDBSuperReadOnly(d.ctx, se, variable.On); err != nil { - return err - } - if err = setTiDBTTLJobEnable(d.ctx, se, variable.Off); err != nil { - return err - } - - nowSchemaVersion, err := t.GetSchemaVersion() - if err != nil { - return errors.Trace(err) - } - - flashbackSnapshotMeta := meta.NewSnapshotMeta(d.store.GetSnapshot(kv.NewVersion(flashbackTS))) - flashbackSchemaVersion, err := flashbackSnapshotMeta.GetSchemaVersion() - if err != nil { - return errors.Trace(err) - } - - flashbackTSString := oracle.GetTimeFromTS(flashbackTS).String() - - // Check if there is an upgrade during [flashbackTS, now) - sql := fmt.Sprintf("select VARIABLE_VALUE from mysql.tidb as of timestamp '%s' where VARIABLE_NAME='tidb_server_version'", flashbackTSString) - rows, err := sess.NewSession(se).Execute(d.ctx, sql, "check_tidb_server_version") - if err != nil || len(rows) == 0 { - return errors.Errorf("Get history `tidb_server_version` failed, can't do flashback") - } - sql = fmt.Sprintf("select 1 from mysql.tidb where VARIABLE_NAME='tidb_server_version' and VARIABLE_VALUE=%s", rows[0].GetString(0)) - rows, err = sess.NewSession(se).Execute(d.ctx, sql, "check_tidb_server_version") - if err != nil { - return errors.Trace(err) - } - if len(rows) == 0 { - return errors.Errorf("Detected TiDB upgrade during [%s, now), can't do flashback", flashbackTSString) - } - - // Check is there a DDL task at flashbackTS. - sql = fmt.Sprintf("select count(*) from mysql.%s as of timestamp '%s'", JobTable, flashbackTSString) - rows, err = sess.NewSession(se).Execute(d.ctx, sql, "check_history_job") - if err != nil || len(rows) == 0 { - return errors.Errorf("Get history ddl jobs failed, can't do flashback") - } - if rows[0].GetInt64(0) != 0 { - return errors.Errorf("Detected another DDL job at %s, can't do flashback", flashbackTSString) - } - - // If flashbackSchemaVersion not same as nowSchemaVersion, we should check all schema diffs during [flashbackTs, now). - for i := flashbackSchemaVersion + 1; i <= nowSchemaVersion; i++ { - diff, err := t.GetSchemaDiff(i) - if err != nil { - return errors.Trace(err) - } - if diff == nil { - continue - } - if !isFlashbackSupportedDDLAction(diff.Type) { - return errors.Errorf("Detected unsupported DDL job type(%s) during [%s, now), can't do flashback", diff.Type.String(), flashbackTSString) - } - err = checkSystemSchemaID(flashbackSnapshotMeta, diff.SchemaID, flashbackTSString) - if err != nil { - return errors.Trace(err) - } - } - - jobs, err := GetAllDDLJobs(se) - if err != nil { - return errors.Trace(err) - } - // Other ddl jobs in queue, return error. - if len(jobs) != 1 { - var otherJob *model.Job - for _, j := range jobs { - if j.ID != job.ID { - otherJob = j - break - } - } - return errors.Errorf("have other ddl jobs(jobID: %d) in queue, can't do flashback", otherJob.ID) - } - return nil -} - -func addToSlice(schema string, tableName string, tableID int64, flashbackIDs []int64) []int64 { - if filter.IsSystemSchema(schema) && !strings.HasPrefix(tableName, "stats_") && tableName != "gc_delete_range" { - flashbackIDs = append(flashbackIDs, tableID) - } - return flashbackIDs -} - -// GetTableDataKeyRanges get keyRanges by `flashbackIDs`. -// This func will return all flashback table data key ranges. -func GetTableDataKeyRanges(nonFlashbackTableIDs []int64) []kv.KeyRange { - var keyRanges []kv.KeyRange - - nonFlashbackTableIDs = append(nonFlashbackTableIDs, -1) - - slices.SortFunc(nonFlashbackTableIDs, func(a, b int64) int { - return cmp.Compare(a, b) - }) - - for i := 1; i < len(nonFlashbackTableIDs); i++ { - keyRanges = append(keyRanges, kv.KeyRange{ - StartKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[i-1] + 1), - EndKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[i]), - }) - } - - // Add all other key ranges. - keyRanges = append(keyRanges, kv.KeyRange{ - StartKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[len(nonFlashbackTableIDs)-1] + 1), - EndKey: tablecodec.EncodeTablePrefix(meta.MaxGlobalID), - }) - - return keyRanges -} - -// GetFlashbackKeyRanges get keyRanges for flashback cluster. -// It contains all non system table key ranges and meta data key ranges. -// The time complexity is O(nlogn). -func GetFlashbackKeyRanges(sess sessionctx.Context, flashbackTS uint64) ([]kv.KeyRange, error) { - schemas := sess.GetDomainInfoSchema().(infoschema.InfoSchema).AllSchemas() - - // The semantic of keyRanges(output). - keyRanges := make([]kv.KeyRange, 0) - - // get snapshot schema IDs. - flashbackSnapshotMeta := meta.NewSnapshotMeta(sess.GetStore().GetSnapshot(kv.NewVersion(flashbackTS))) - snapshotSchemas, err := flashbackSnapshotMeta.ListDatabases() - if err != nil { - return nil, errors.Trace(err) - } - - schemaIDs := make(map[int64]struct{}) - for _, schema := range schemas { - if !filter.IsSystemSchema(schema.Name.L) { - schemaIDs[schema.ID] = struct{}{} - } - } - for _, schema := range snapshotSchemas { - if !filter.IsSystemSchema(schema.Name.L) { - schemaIDs[schema.ID] = struct{}{} - } - } - - // The meta data key ranges. - for schemaID := range schemaIDs { - metaStartKey := tablecodec.EncodeMetaKeyPrefix(meta.DBkey(schemaID)) - metaEndKey := tablecodec.EncodeMetaKeyPrefix(meta.DBkey(schemaID + 1)) - keyRanges = append(keyRanges, kv.KeyRange{ - StartKey: metaStartKey, - EndKey: metaEndKey, - }) - } - - startKey := tablecodec.EncodeMetaKeyPrefix([]byte("DBs")) - keyRanges = append(keyRanges, kv.KeyRange{ - StartKey: startKey, - EndKey: startKey.PrefixNext(), - }) - - var nonFlashbackTableIDs []int64 - for _, db := range schemas { - for _, table := range db.Tables { - if !table.IsBaseTable() || table.ID > meta.MaxGlobalID { - continue - } - nonFlashbackTableIDs = addToSlice(db.Name.L, table.Name.L, table.ID, nonFlashbackTableIDs) - if table.Partition != nil { - for _, partition := range table.Partition.Definitions { - nonFlashbackTableIDs = addToSlice(db.Name.L, table.Name.L, partition.ID, nonFlashbackTableIDs) - } - } - } - } - - return append(keyRanges, GetTableDataKeyRanges(nonFlashbackTableIDs)...), nil -} - -// SendPrepareFlashbackToVersionRPC prepares regions for flashback, the purpose is to put region into flashback state which region stop write -// Function also be called by BR for volume snapshot backup and restore -func SendPrepareFlashbackToVersionRPC( - ctx context.Context, - s tikv.Storage, - flashbackTS, startTS uint64, - r tikvstore.KeyRange, -) (rangetask.TaskStat, error) { - startKey, rangeEndKey := r.StartKey, r.EndKey - var taskStat rangetask.TaskStat - bo := tikv.NewBackoffer(ctx, flashbackMaxBackoff) - for { - select { - case <-ctx.Done(): - return taskStat, errors.WithStack(ctx.Err()) - default: - } - - if len(rangeEndKey) > 0 && bytes.Compare(startKey, rangeEndKey) >= 0 { - break - } - - loc, err := s.GetRegionCache().LocateKey(bo, startKey) - if err != nil { - return taskStat, err - } - - endKey := loc.EndKey - isLast := len(endKey) == 0 || (len(rangeEndKey) > 0 && bytes.Compare(endKey, rangeEndKey) >= 0) - // If it is the last region. - if isLast { - endKey = rangeEndKey - } - - logutil.BgLogger().Info("send prepare flashback request", zap.String("category", "ddl"), zap.Uint64("region_id", loc.Region.GetID()), - zap.String("start_key", hex.EncodeToString(startKey)), zap.String("end_key", hex.EncodeToString(endKey))) - - req := tikvrpc.NewRequest(tikvrpc.CmdPrepareFlashbackToVersion, &kvrpcpb.PrepareFlashbackToVersionRequest{ - StartKey: startKey, - EndKey: endKey, - StartTs: startTS, - Version: flashbackTS, - }) - - resp, err := s.SendReq(bo, req, loc.Region, flashbackTimeout) - if err != nil { - return taskStat, err - } - regionErr, err := resp.GetRegionError() - if err != nil { - return taskStat, err - } - failpoint.Inject("mockPrepareMeetsEpochNotMatch", func(val failpoint.Value) { - if val.(bool) && bo.ErrorsNum() == 0 { - regionErr = &errorpb.Error{ - Message: "stale epoch", - EpochNotMatch: &errorpb.EpochNotMatch{}, - } - } - }) - if regionErr != nil { - err = bo.Backoff(tikv.BoRegionMiss(), errors.New(regionErr.String())) - if err != nil { - return taskStat, err - } - continue - } - if resp.Resp == nil { - logutil.BgLogger().Warn("prepare flashback miss resp body", zap.Uint64("region_id", loc.Region.GetID())) - err = bo.Backoff(tikv.BoTiKVRPC(), errors.New("prepare flashback rpc miss resp body")) - if err != nil { - return taskStat, err - } - continue - } - prepareFlashbackToVersionResp := resp.Resp.(*kvrpcpb.PrepareFlashbackToVersionResponse) - if err := prepareFlashbackToVersionResp.GetError(); err != "" { - boErr := bo.Backoff(tikv.BoTiKVRPC(), errors.New(err)) - if boErr != nil { - return taskStat, boErr - } - continue - } - taskStat.CompletedRegions++ - if isLast { - break - } - bo = tikv.NewBackoffer(ctx, flashbackMaxBackoff) - startKey = endKey - } - return taskStat, nil -} - -// SendFlashbackToVersionRPC flashback the MVCC key to the version -// Function also be called by BR for volume snapshot backup and restore -func SendFlashbackToVersionRPC( - ctx context.Context, - s tikv.Storage, - version uint64, - startTS, commitTS uint64, - r tikvstore.KeyRange, -) (rangetask.TaskStat, error) { - startKey, rangeEndKey := r.StartKey, r.EndKey - var taskStat rangetask.TaskStat - bo := tikv.NewBackoffer(ctx, flashbackMaxBackoff) - for { - select { - case <-ctx.Done(): - return taskStat, errors.WithStack(ctx.Err()) - default: - } - - if len(rangeEndKey) > 0 && bytes.Compare(startKey, rangeEndKey) >= 0 { - break - } - - loc, err := s.GetRegionCache().LocateKey(bo, startKey) - if err != nil { - return taskStat, err - } - - endKey := loc.EndKey - isLast := len(endKey) == 0 || (len(rangeEndKey) > 0 && bytes.Compare(endKey, rangeEndKey) >= 0) - // If it is the last region. - if isLast { - endKey = rangeEndKey - } - - logutil.BgLogger().Info("send flashback request", zap.String("category", "ddl"), zap.Uint64("region_id", loc.Region.GetID()), - zap.String("start_key", hex.EncodeToString(startKey)), zap.String("end_key", hex.EncodeToString(endKey))) - - req := tikvrpc.NewRequest(tikvrpc.CmdFlashbackToVersion, &kvrpcpb.FlashbackToVersionRequest{ - Version: version, - StartKey: startKey, - EndKey: endKey, - StartTs: startTS, - CommitTs: commitTS, - }) - - resp, err := s.SendReq(bo, req, loc.Region, flashbackTimeout) - if err != nil { - logutil.BgLogger().Warn("send request meets error", zap.Uint64("region_id", loc.Region.GetID()), zap.Error(err)) - if err.Error() != fmt.Sprintf("region %d is not prepared for the flashback", loc.Region.GetID()) { - return taskStat, err - } - } else { - regionErr, err := resp.GetRegionError() - if err != nil { - return taskStat, err - } - if regionErr != nil { - err = bo.Backoff(tikv.BoRegionMiss(), errors.New(regionErr.String())) - if err != nil { - return taskStat, err - } - continue - } - if resp.Resp == nil { - logutil.BgLogger().Warn("flashback miss resp body", zap.Uint64("region_id", loc.Region.GetID())) - err = bo.Backoff(tikv.BoTiKVRPC(), errors.New("flashback rpc miss resp body")) - if err != nil { - return taskStat, err - } - continue - } - flashbackToVersionResp := resp.Resp.(*kvrpcpb.FlashbackToVersionResponse) - if respErr := flashbackToVersionResp.GetError(); respErr != "" { - boErr := bo.Backoff(tikv.BoTiKVRPC(), errors.New(respErr)) - if boErr != nil { - return taskStat, boErr - } - continue - } - } - taskStat.CompletedRegions++ - if isLast { - break - } - bo = tikv.NewBackoffer(ctx, flashbackMaxBackoff) - startKey = endKey - } - return taskStat, nil -} - -func flashbackToVersion( - ctx context.Context, - d *ddlCtx, - handler rangetask.TaskHandler, - startKey []byte, endKey []byte, -) (err error) { - return rangetask.NewRangeTaskRunner( - "flashback-to-version-runner", - d.store.(tikv.Storage), - int(variable.GetDDLFlashbackConcurrency()), - handler, - ).RunOnRange(ctx, startKey, endKey) -} - -func splitRegionsByKeyRanges(d *ddlCtx, keyRanges []kv.KeyRange) { - if s, ok := d.store.(kv.SplittableStore); ok { - for _, keys := range keyRanges { - for { - // tableID is useless when scatter == false - _, err := s.SplitRegions(d.ctx, [][]byte{keys.StartKey, keys.EndKey}, false, nil) - if err == nil { - break - } - } - } - } -} - -// A Flashback has 4 different stages. -// 1. before lock flashbackClusterJobID, check clusterJobID and lock it. -// 2. before flashback start, check timestamp, disable GC and close PD schedule, get flashback key ranges. -// 3. phase 1, lock flashback key ranges. -// 4. phase 2, send flashback RPC, do flashback jobs. -func (w *worker) onFlashbackCluster(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - inFlashbackTest := false - failpoint.Inject("mockFlashbackTest", func(val failpoint.Value) { - if val.(bool) { - inFlashbackTest = true - } - }) - // TODO: Support flashback in unistore. - if d.store.Name() != "TiKV" && !inFlashbackTest { - job.State = model.JobStateCancelled - return ver, errors.Errorf("Not support flashback cluster in non-TiKV env") - } - - var flashbackTS, lockedRegions, startTS, commitTS uint64 - var pdScheduleValue map[string]interface{} - var autoAnalyzeValue, readOnlyValue, ttlJobEnableValue string - var gcEnabledValue bool - var keyRanges []kv.KeyRange - if err := job.DecodeArgs(&flashbackTS, &pdScheduleValue, &gcEnabledValue, &autoAnalyzeValue, &readOnlyValue, &lockedRegions, &startTS, &commitTS, &ttlJobEnableValue, &keyRanges); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - var totalRegions, completedRegions atomic.Uint64 - totalRegions.Store(lockedRegions) - - sess, err := w.sessPool.Get() - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - defer w.sessPool.Put(sess) - - switch job.SchemaState { - // Stage 1, check and set FlashbackClusterJobID, and update job args. - case model.StateNone: - if err = savePDSchedule(job); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - gcEnableValue, err := gcutil.CheckGCEnable(sess) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.Args[gcEnabledOffset] = &gcEnableValue - autoAnalyzeValue, err = getTiDBEnableAutoAnalyze(sess) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.Args[autoAnalyzeOffset] = &autoAnalyzeValue - readOnlyValue, err = getTiDBSuperReadOnly(sess) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.Args[readOnlyOffset] = &readOnlyValue - ttlJobEnableValue, err = getTiDBTTLJobEnable(sess) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.Args[ttlJobEnableOffSet] = &ttlJobEnableValue - job.SchemaState = model.StateDeleteOnly - return ver, nil - // Stage 2, check flashbackTS, close GC and PD schedule, get flashback key ranges. - case model.StateDeleteOnly: - if err = checkAndSetFlashbackClusterInfo(sess, d, t, job, flashbackTS); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // We should get startTS here to avoid lost startTS when TiDB crashed during send prepare flashback RPC. - startTS, err = d.store.GetOracle().GetTimestamp(d.ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.Args[startTSOffset] = startTS - keyRanges, err = GetFlashbackKeyRanges(sess, flashbackTS) - if err != nil { - return ver, errors.Trace(err) - } - job.Args[keyRangesOffset] = keyRanges - job.SchemaState = model.StateWriteOnly - return updateSchemaVersion(d, t, job) - // Stage 3, lock related key ranges. - case model.StateWriteOnly: - // TODO: Support flashback in unistore. - if inFlashbackTest { - job.SchemaState = model.StateWriteReorganization - return updateSchemaVersion(d, t, job) - } - // Split region by keyRanges, make sure no unrelated key ranges be locked. - splitRegionsByKeyRanges(d, keyRanges) - totalRegions.Store(0) - for _, r := range keyRanges { - if err = flashbackToVersion(d.ctx, d, - func(ctx context.Context, r tikvstore.KeyRange) (rangetask.TaskStat, error) { - stats, err := SendPrepareFlashbackToVersionRPC(ctx, d.store.(tikv.Storage), flashbackTS, startTS, r) - totalRegions.Add(uint64(stats.CompletedRegions)) - return stats, err - }, r.StartKey, r.EndKey); err != nil { - logutil.BgLogger().Warn("Get error when do flashback", zap.String("category", "ddl"), zap.Error(err)) - return ver, err - } - } - job.Args[totalLockedRegionsOffset] = totalRegions.Load() - - // We should get commitTS here to avoid lost commitTS when TiDB crashed during send flashback RPC. - commitTS, err = d.store.GetOracle().GetTimestamp(d.ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) - if err != nil { - return ver, errors.Trace(err) - } - job.Args[commitTSOffset] = commitTS - job.SchemaState = model.StateWriteReorganization - return ver, nil - // Stage 4, get key ranges and send flashback RPC. - case model.StateWriteReorganization: - // TODO: Support flashback in unistore. - if inFlashbackTest { - asyncNotifyEvent(d, &util.Event{Tp: model.ActionFlashbackCluster}) - job.State = model.JobStateDone - job.SchemaState = model.StatePublic - return ver, nil - } - - for _, r := range keyRanges { - if err = flashbackToVersion(d.ctx, d, - func(ctx context.Context, r tikvstore.KeyRange) (rangetask.TaskStat, error) { - // Use same startTS as prepare phase to simulate 1PC txn. - stats, err := SendFlashbackToVersionRPC(ctx, d.store.(tikv.Storage), flashbackTS, startTS, commitTS, r) - completedRegions.Add(uint64(stats.CompletedRegions)) - logutil.BgLogger().Info("flashback cluster stats", zap.String("category", "ddl"), - zap.Uint64("complete regions", completedRegions.Load()), - zap.Uint64("total regions", totalRegions.Load()), - zap.Error(err)) - return stats, err - }, r.StartKey, r.EndKey); err != nil { - logutil.BgLogger().Warn("Get error when do flashback", zap.String("category", "ddl"), zap.Error(err)) - return ver, errors.Trace(err) - } - } - - asyncNotifyEvent(d, &util.Event{Tp: model.ActionFlashbackCluster}) - job.State = model.JobStateDone - job.SchemaState = model.StatePublic - return updateSchemaVersion(d, t, job) - } - return ver, nil -} - -func finishFlashbackCluster(w *worker, job *model.Job) error { - // Didn't do anything during flashback, return directly - if job.SchemaState == model.StateNone { - return nil - } - - var flashbackTS, lockedRegions, startTS, commitTS uint64 - var pdScheduleValue map[string]interface{} - var autoAnalyzeValue, readOnlyValue, ttlJobEnableValue string - var gcEnabled bool - - if err := job.DecodeArgs(&flashbackTS, &pdScheduleValue, &gcEnabled, &autoAnalyzeValue, &readOnlyValue, &lockedRegions, &startTS, &commitTS, &ttlJobEnableValue); err != nil { - return errors.Trace(err) - } - sess, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(sess) - - err = kv.RunInNewTxn(w.ctx, w.store, true, func(ctx context.Context, txn kv.Transaction) error { - if err = recoverPDSchedule(pdScheduleValue); err != nil { - return err - } - if gcEnabled { - if err = gcutil.EnableGC(sess); err != nil { - return err - } - } - if err = setTiDBSuperReadOnly(w.ctx, sess, readOnlyValue); err != nil { - return err - } - - if job.IsCancelled() { - // only restore `tidb_ttl_job_enable` when flashback failed - if err = setTiDBTTLJobEnable(w.ctx, sess, ttlJobEnableValue); err != nil { - return err - } - } - - return setTiDBEnableAutoAnalyze(w.ctx, sess, autoAnalyzeValue) - }) - if err != nil { - return err - } - - return nil -} diff --git a/ddl/cluster_test.go b/ddl/cluster_test.go deleted file mode 100644 index 05ebb87f475ca..0000000000000 --- a/ddl/cluster_test.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" -) - -func TestGetTableDataKeyRanges(t *testing.T) { - // case 1, empty flashbackIDs - keyRanges := ddl.GetTableDataKeyRanges([]int64{}) - require.Len(t, keyRanges, 1) - require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) - require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) - - // case 2, insert a execluded table ID - keyRanges = ddl.GetTableDataKeyRanges([]int64{3}) - require.Len(t, keyRanges, 2) - require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) - require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(3)) - require.Equal(t, keyRanges[1].StartKey, tablecodec.EncodeTablePrefix(4)) - require.Equal(t, keyRanges[1].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) - - // case 3, insert some execluded table ID - keyRanges = ddl.GetTableDataKeyRanges([]int64{3, 5, 9}) - require.Len(t, keyRanges, 4) - require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) - require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(3)) - require.Equal(t, keyRanges[1].StartKey, tablecodec.EncodeTablePrefix(4)) - require.Equal(t, keyRanges[1].EndKey, tablecodec.EncodeTablePrefix(5)) - require.Equal(t, keyRanges[2].StartKey, tablecodec.EncodeTablePrefix(6)) - require.Equal(t, keyRanges[2].EndKey, tablecodec.EncodeTablePrefix(9)) - require.Equal(t, keyRanges[3].StartKey, tablecodec.EncodeTablePrefix(10)) - require.Equal(t, keyRanges[3].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) -} - -func TestFlashbackCloseAndResetPDSchedule(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - originHook := dom.DDL().GetHook() - tk := testkit.NewTestKit(t, store) - - injectSafeTS := oracle.GoTimeToTS(time.Now().Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", - fmt.Sprintf("return(%v)", injectSafeTS))) - - oldValue := map[string]interface{}{ - "merge-schedule-limit": 1, - } - require.NoError(t, infosync.SetPDScheduleConfig(context.Background(), oldValue)) - - timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) - defer resetGC() - tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - assert.Equal(t, model.ActionFlashbackCluster, job.Type) - if job.SchemaState == model.StateWriteReorganization { - closeValue, err := infosync.GetPDScheduleConfig(context.Background()) - assert.NoError(t, err) - assert.Equal(t, closeValue["merge-schedule-limit"], 0) - // cancel flashback job - job.State = model.JobStateCancelled - job.Error = dbterror.ErrCancelledDDLJob - } - } - dom.DDL().SetHook(hook) - - time.Sleep(10 * time.Millisecond) - ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) - require.NoError(t, err) - - tk.MustGetErrCode(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts)), errno.ErrCancelledDDLJob) - dom.DDL().SetHook(originHook) - - finishValue, err := infosync.GetPDScheduleConfig(context.Background()) - require.NoError(t, err) - require.EqualValues(t, finishValue["merge-schedule-limit"], 1) - - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) -} - -func TestAddDDLDuringFlashback(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - originHook := dom.DDL().GetHook() - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - - time.Sleep(10 * time.Millisecond) - ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) - require.NoError(t, err) - - injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", - fmt.Sprintf("return(%v)", injectSafeTS))) - - timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) - defer resetGC() - tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - assert.Equal(t, model.ActionFlashbackCluster, job.Type) - if job.SchemaState == model.StateWriteOnly { - tk1 := testkit.NewTestKit(t, store) - _, err := tk1.Exec("alter table test.t add column b int") - assert.ErrorContains(t, err, "Can't add ddl job, have flashback cluster job") - } - } - dom.DDL().SetHook(hook) - tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) - - dom.DDL().SetHook(originHook) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) -} - -func TestGlobalVariablesOnFlashback(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - originHook := dom.DDL().GetHook() - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - - time.Sleep(10 * time.Millisecond) - ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) - require.NoError(t, err) - - injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", - fmt.Sprintf("return(%v)", injectSafeTS))) - - timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) - defer resetGC() - tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - assert.Equal(t, model.ActionFlashbackCluster, job.Type) - if job.SchemaState == model.StateWriteReorganization { - rs, err := tk.Exec("show variables like 'tidb_gc_enable'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - rs, err = tk.Exec("show variables like 'tidb_enable_auto_analyze'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - rs, err = tk.Exec("show variables like 'tidb_super_read_only'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) - rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - } - } - dom.DDL().SetHook(hook) - // first try with `tidb_gc_enable` = on and `tidb_super_read_only` = off and `tidb_ttl_job_enable` = on - tk.MustExec("set global tidb_gc_enable = on") - tk.MustExec("set global tidb_super_read_only = off") - tk.MustExec("set global tidb_ttl_job_enable = on") - - tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) - - rs, err := tk.Exec("show variables like 'tidb_super_read_only'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - rs, err = tk.Exec("show variables like 'tidb_gc_enable'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) - rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - - // second try with `tidb_gc_enable` = off and `tidb_super_read_only` = on and `tidb_ttl_job_enable` = off - tk.MustExec("set global tidb_gc_enable = off") - tk.MustExec("set global tidb_super_read_only = on") - tk.MustExec("set global tidb_ttl_job_enable = off") - - ts, err = tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) - require.NoError(t, err) - tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) - rs, err = tk.Exec("show variables like 'tidb_super_read_only'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) - rs, err = tk.Exec("show variables like 'tidb_gc_enable'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - - dom.DDL().SetHook(originHook) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) -} - -func TestCancelFlashbackCluster(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - originHook := dom.DDL().GetHook() - tk := testkit.NewTestKit(t, store) - - time.Sleep(10 * time.Millisecond) - ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) - require.NoError(t, err) - - injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", - fmt.Sprintf("return(%v)", injectSafeTS))) - - timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) - defer resetGC() - tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) - - // Try canceled on StateDeleteOnly, cancel success - hook := newCancelJobHook(t, store, dom, func(job *model.Job) bool { - return job.SchemaState == model.StateDeleteOnly - }) - dom.DDL().SetHook(hook) - tk.MustExec("set global tidb_ttl_job_enable = on") - tk.MustGetErrCode(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts)), errno.ErrCancelledDDLJob) - hook.MustCancelDone(t) - - rs, err := tk.Exec("show variables like 'tidb_ttl_job_enable'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) - - // Try canceled on StateWriteReorganization, cancel failed - hook = newCancelJobHook(t, store, dom, func(job *model.Job) bool { - return job.SchemaState == model.StateWriteReorganization - }) - dom.DDL().SetHook(hook) - tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) - hook.MustCancelFailed(t) - - rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") - assert.NoError(t, err) - assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) - - dom.DDL().SetHook(originHook) - - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) -} diff --git a/ddl/column.go b/ddl/column.go deleted file mode 100644 index 596d200856f92..0000000000000 --- a/ddl/column.go +++ /dev/null @@ -1,2029 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "math/bits" - "sort" - "strings" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - sess "github.com/pingcap/tidb/ddl/internal/session" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/sqlexec" - kvutil "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -// InitAndAddColumnToTable initializes the ColumnInfo in-place and adds it to the table. -func InitAndAddColumnToTable(tblInfo *model.TableInfo, colInfo *model.ColumnInfo) *model.ColumnInfo { - cols := tblInfo.Columns - colInfo.ID = AllocateColumnID(tblInfo) - colInfo.State = model.StateNone - // To support add column asynchronous, we should mark its offset as the last column. - // So that we can use origin column offset to get value from row. - colInfo.Offset = len(cols) - // Append the column info to the end of the tblInfo.Columns. - // It will reorder to the right offset in "Columns" when it state change to public. - tblInfo.Columns = append(cols, colInfo) - return colInfo -} - -func checkAddColumn(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ColumnInfo, *model.ColumnInfo, - *ast.ColumnPosition, bool /* ifNotExists */, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, nil, false, errors.Trace(err) - } - col := &model.ColumnInfo{} - pos := &ast.ColumnPosition{} - offset := 0 - ifNotExists := false - err = job.DecodeArgs(col, pos, &offset, &ifNotExists) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, nil, false, errors.Trace(err) - } - - columnInfo := model.FindColumnInfo(tblInfo.Columns, col.Name.L) - if columnInfo != nil { - if columnInfo.State == model.StatePublic { - // We already have a column with the same column name. - job.State = model.JobStateCancelled - return nil, nil, nil, nil, ifNotExists, infoschema.ErrColumnExists.GenWithStackByArgs(col.Name) - } - } - - err = CheckAfterPositionExists(tblInfo, pos) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, nil, false, infoschema.ErrColumnExists.GenWithStackByArgs(col.Name) - } - - return tblInfo, columnInfo, col, pos, false, nil -} - -func onAddColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - // Handle the rolling back job. - if job.IsRollingback() { - ver, err = onDropColumn(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - return ver, nil - } - - failpoint.Inject("errorBeforeDecodeArgs", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - failpoint.Return(ver, errors.New("occur an error before decode args")) - } - }) - - tblInfo, columnInfo, colFromArgs, pos, ifNotExists, err := checkAddColumn(t, job) - if err != nil { - if ifNotExists && infoschema.ErrColumnExists.Equal(err) { - job.Warning = toTError(err) - job.State = model.JobStateDone - return ver, nil - } - return ver, errors.Trace(err) - } - if columnInfo == nil { - columnInfo = InitAndAddColumnToTable(tblInfo, colFromArgs) - logutil.BgLogger().Info("run add column job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Reflect("columnInfo", *columnInfo)) - if err = checkAddColumnTooManyColumns(len(tblInfo.Columns)); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - - originalState := columnInfo.State - switch columnInfo.State { - case model.StateNone: - // none -> delete only - columnInfo.State = model.StateDeleteOnly - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != columnInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateDeleteOnly - case model.StateDeleteOnly: - // delete only -> write only - columnInfo.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - // Update the job state when all affairs done. - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - // write only -> reorganization - columnInfo.State = model.StateWriteReorganization - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - // Update the job state when all affairs done. - job.SchemaState = model.StateWriteReorganization - job.MarkNonRevertible() - case model.StateWriteReorganization: - // reorganization -> public - // Adjust table column offset. - offset, err := LocateOffsetToMove(columnInfo.Offset, pos, tblInfo) - if err != nil { - return ver, errors.Trace(err) - } - tblInfo.MoveColumnInfo(columnInfo.Offset, offset) - columnInfo.State = model.StatePublic - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - asyncNotifyEvent(d, &ddlutil.Event{Tp: model.ActionAddColumn, TableInfo: tblInfo, ColumnInfos: []*model.ColumnInfo{columnInfo}}) - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("column", columnInfo.State) - } - - return ver, errors.Trace(err) -} - -// CheckAfterPositionExists makes sure the column specified in AFTER clause is exists. -// For example, ALTER TABLE t ADD COLUMN c3 INT AFTER c1. -func CheckAfterPositionExists(tblInfo *model.TableInfo, pos *ast.ColumnPosition) error { - if pos != nil && pos.Tp == ast.ColumnPositionAfter { - c := model.FindColumnInfo(tblInfo.Columns, pos.RelativeColumn.Name.L) - if c == nil { - return infoschema.ErrColumnNotExists.GenWithStackByArgs(pos.RelativeColumn, tblInfo.Name) - } - } - return nil -} - -func setIndicesState(indexInfos []*model.IndexInfo, state model.SchemaState) { - for _, indexInfo := range indexInfos { - indexInfo.State = state - } -} - -func checkDropColumnForStatePublic(colInfo *model.ColumnInfo) (err error) { - // When the dropping column has not-null flag and it hasn't the default value, we can backfill the column value like "add column". - // NOTE: If the state of StateWriteOnly can be rollbacked, we'd better reconsider the original default value. - // And we need consider the column without not-null flag. - if colInfo.GetOriginDefaultValue() == nil && mysql.HasNotNullFlag(colInfo.GetFlag()) { - // If the column is timestamp default current_timestamp, and DDL owner is new version TiDB that set column.Version to 1, - // then old TiDB update record in the column write only stage will uses the wrong default value of the dropping column. - // Because new version of the column default value is UTC time, but old version TiDB will think the default value is the time in system timezone. - // But currently will be ok, because we can't cancel the drop column job when the job is running, - // so the column will be dropped succeed and client will never see the wrong default value of the dropped column. - // More info about this problem, see PR#9115. - originDefVal, err := generateOriginDefaultValue(colInfo, nil) - if err != nil { - return err - } - return colInfo.SetOriginDefaultValue(originDefVal) - } - return nil -} - -func onDropColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, colInfo, idxInfos, ifExists, err := checkDropColumn(d, t, job) - if err != nil { - if ifExists && dbterror.ErrCantDropFieldOrKey.Equal(err) { - // Convert the "not exists" error to a warning. - job.Warning = toTError(err) - job.State = model.JobStateDone - return ver, nil - } - return ver, errors.Trace(err) - } - if job.MultiSchemaInfo != nil && !job.IsRollingback() && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - job.SchemaState = colInfo.State - // Store the mark and enter the next DDL handling loop. - return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) - } - - originalState := colInfo.State - switch colInfo.State { - case model.StatePublic: - // public -> write only - colInfo.State = model.StateWriteOnly - setIndicesState(idxInfos, model.StateWriteOnly) - tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) - err = checkDropColumnForStatePublic(colInfo) - if err != nil { - return ver, errors.Trace(err) - } - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != colInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateWriteOnly: - // write only -> delete only - colInfo.State = model.StateDeleteOnly - tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) - if len(idxInfos) > 0 { - newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) - for _, idx := range tblInfo.Indices { - if !indexInfoContains(idx.ID, idxInfos) { - newIndices = append(newIndices, idx) - } - } - tblInfo.Indices = newIndices - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - job.Args = append(job.Args, indexInfosToIDList(idxInfos)) - case model.StateDeleteOnly: - // delete only -> reorganization - colInfo.State = model.StateDeleteReorganization - tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateDeleteReorganization: - // reorganization -> absent - // All reorganization jobs are done, drop this column. - tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) - tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-1] - colInfo.State = model.StateNone - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - if job.IsRollingback() { - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - } else { - // We should set related index IDs for job - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - job.Args = append(job.Args, getPartitionIDs(tblInfo)) - } - default: - return ver, errors.Trace(dbterror.ErrInvalidDDLJob.GenWithStackByArgs("table", tblInfo.State)) - } - job.SchemaState = colInfo.State - return ver, errors.Trace(err) -} - -func checkDropColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ColumnInfo, []*model.IndexInfo, bool /* ifExists */, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, false, errors.Trace(err) - } - - var colName model.CIStr - var ifExists bool - // indexIds is used to make sure we don't truncate args when decoding the rawArgs. - var indexIds []int64 - err = job.DecodeArgs(&colName, &ifExists, &indexIds) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, false, errors.Trace(err) - } - - colInfo := model.FindColumnInfo(tblInfo.Columns, colName.L) - if colInfo == nil || colInfo.Hidden { - job.State = model.JobStateCancelled - return nil, nil, nil, ifExists, dbterror.ErrCantDropFieldOrKey.GenWithStack("column %s doesn't exist", colName) - } - if err = isDroppableColumn(tblInfo, colName); err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, false, errors.Trace(err) - } - if err = checkDropColumnWithForeignKeyConstraintInOwner(d, t, job, tblInfo, colName.L); err != nil { - return nil, nil, nil, false, errors.Trace(err) - } - if err = checkDropColumnWithTTLConfig(tblInfo, colName.L); err != nil { - return nil, nil, nil, false, errors.Trace(err) - } - idxInfos := listIndicesWithColumn(colName.L, tblInfo.Indices) - return tblInfo, colInfo, idxInfos, false, nil -} - -func onSetDefaultValue(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - newCol := &model.ColumnInfo{} - err := job.DecodeArgs(newCol) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - return updateColumnDefaultValue(d, t, job, newCol, &newCol.Name) -} - -func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool { - toUnsigned := mysql.HasUnsignedFlag(newCol.GetFlag()) - originUnsigned := mysql.HasUnsignedFlag(oldCol.GetFlag()) - needTruncationOrToggleSign := func() bool { - return (newCol.GetFlen() > 0 && (newCol.GetFlen() < oldCol.GetFlen() || newCol.GetDecimal() < oldCol.GetDecimal())) || - (toUnsigned != originUnsigned) - } - // Ignore the potential max display length represented by integer's flen, use default flen instead. - defaultOldColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(oldCol.GetType()) - defaultNewColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(newCol.GetType()) - needTruncationOrToggleSignForInteger := func() bool { - return (defaultNewColFlen > 0 && defaultNewColFlen < defaultOldColFlen) || (toUnsigned != originUnsigned) - } - - // Deal with the same type. - if oldCol.GetType() == newCol.GetType() { - switch oldCol.GetType() { - case mysql.TypeNewDecimal: - // Since type decimal will encode the precision, frac, negative(signed) and wordBuf into storage together, there is no short - // cut to eliminate data reorg change for column type change between decimal. - return oldCol.GetFlen() != newCol.GetFlen() || oldCol.GetDecimal() != newCol.GetDecimal() || toUnsigned != originUnsigned - case mysql.TypeEnum, mysql.TypeSet: - return IsElemsChangedToModifyColumn(oldCol.GetElems(), newCol.GetElems()) - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - return toUnsigned != originUnsigned - case mysql.TypeString: - // Due to the behavior of padding \x00 at binary type, always change column data when binary length changed - if types.IsBinaryStr(&oldCol.FieldType) { - return newCol.GetFlen() != oldCol.GetFlen() - } - } - - return needTruncationOrToggleSign() - } - - if ConvertBetweenCharAndVarchar(oldCol.GetType(), newCol.GetType()) { - return true - } - - // Deal with the different type. - switch oldCol.GetType() { - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - switch newCol.GetType() { - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - return needTruncationOrToggleSign() - } - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - switch newCol.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - return needTruncationOrToggleSignForInteger() - } - // conversion between float and double needs reorganization, see issue #31372 - } - - return true -} - -// ConvertBetweenCharAndVarchar check whether column converted between char and varchar -// TODO: it is used for plugins. so change plugin's using and remove it. -func ConvertBetweenCharAndVarchar(oldCol, newCol byte) bool { - return types.ConvertBetweenCharAndVarchar(oldCol, newCol) -} - -// IsElemsChangedToModifyColumn check elems changed -func IsElemsChangedToModifyColumn(oldElems, newElems []string) bool { - if len(newElems) < len(oldElems) { - return true - } - for index, oldElem := range oldElems { - newElem := newElems[index] - if oldElem != newElem { - return true - } - } - return false -} - -type modifyingColInfo struct { - newCol *model.ColumnInfo - oldColName *model.CIStr - modifyColumnTp byte - updatedAutoRandomBits uint64 - changingCol *model.ColumnInfo - changingIdxs []*model.IndexInfo - pos *ast.ColumnPosition - removedIdxs []int64 -} - -func getModifyColumnInfo(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ColumnInfo, *modifyingColInfo, error) { - modifyInfo := &modifyingColInfo{pos: &ast.ColumnPosition{}} - err := job.DecodeArgs(&modifyInfo.newCol, &modifyInfo.oldColName, modifyInfo.pos, &modifyInfo.modifyColumnTp, - &modifyInfo.updatedAutoRandomBits, &modifyInfo.changingCol, &modifyInfo.changingIdxs, &modifyInfo.removedIdxs) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, modifyInfo, errors.Trace(err) - } - - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return nil, nil, nil, modifyInfo, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return nil, nil, nil, modifyInfo, errors.Trace(err) - } - - oldCol := model.FindColumnInfo(tblInfo.Columns, modifyInfo.oldColName.L) - if oldCol == nil || oldCol.State != model.StatePublic { - job.State = model.JobStateCancelled - return nil, nil, nil, modifyInfo, errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(*(modifyInfo.oldColName), tblInfo.Name)) - } - - return dbInfo, tblInfo, oldCol, modifyInfo, errors.Trace(err) -} - -// GetOriginDefaultValueForModifyColumn gets the original default value for modifying column. -// Since column type change is implemented as adding a new column then substituting the old one. -// Case exists when update-where statement fetch a NULL for not-null column without any default data, -// it will errors. -// So we set original default value here to prevent this error. If the oldCol has the original default value, we use it. -// Otherwise we set the zero value as original default value. -// Besides, in insert & update records, we have already implement using the casted value of relative column to insert -// rather than the original default value. -func GetOriginDefaultValueForModifyColumn(sessCtx sessionctx.Context, changingCol, oldCol *model.ColumnInfo) (interface{}, error) { - var err error - originDefVal := oldCol.GetOriginDefaultValue() - if originDefVal != nil { - odv, err := table.CastValue(sessCtx, types.NewDatum(originDefVal), changingCol, false, false) - if err != nil { - logutil.BgLogger().Info("cast origin default value failed", zap.String("category", "ddl"), zap.Error(err)) - } - if !odv.IsNull() { - if originDefVal, err = odv.ToString(); err != nil { - originDefVal = nil - logutil.BgLogger().Info("convert default value to string failed", zap.String("category", "ddl"), zap.Error(err)) - } - } - } - if originDefVal == nil { - originDefVal, err = generateOriginDefaultValue(changingCol, nil) - if err != nil { - return nil, errors.Trace(err) - } - } - return originDefVal, nil -} - -func (w *worker) onModifyColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - dbInfo, tblInfo, oldCol, modifyInfo, err := getModifyColumnInfo(t, job) - if err != nil { - return ver, err - } - - if job.IsRollingback() { - // For those column-type-change jobs which don't reorg the data. - if !needChangeColumnData(oldCol, modifyInfo.newCol) { - return rollbackModifyColumnJob(d, t, tblInfo, job, modifyInfo.newCol, oldCol, modifyInfo.modifyColumnTp) - } - // For those column-type-change jobs which reorg the data. - return rollbackModifyColumnJobWithData(d, t, tblInfo, job, oldCol, modifyInfo) - } - - // If we want to rename the column name, we need to check whether it already exists. - if modifyInfo.newCol.Name.L != modifyInfo.oldColName.L { - c := model.FindColumnInfo(tblInfo.Columns, modifyInfo.newCol.Name.L) - if c != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(infoschema.ErrColumnExists.GenWithStackByArgs(modifyInfo.newCol.Name)) - } - } - - failpoint.Inject("uninitializedOffsetAndState", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - if modifyInfo.newCol.State != model.StatePublic { - failpoint.Return(ver, errors.New("the column state is wrong")) - } - } - }) - - err = checkAndApplyAutoRandomBits(d, t, dbInfo, tblInfo, oldCol, modifyInfo.newCol, modifyInfo.updatedAutoRandomBits) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if !needChangeColumnData(oldCol, modifyInfo.newCol) { - return w.doModifyColumn(d, t, job, dbInfo, tblInfo, modifyInfo.newCol, oldCol, modifyInfo.pos) - } - - if err = isGeneratedRelatedColumn(tblInfo, modifyInfo.newCol, oldCol); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - if tblInfo.Partition != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs("table is partition table")) - } - - changingCol := modifyInfo.changingCol - if changingCol == nil { - newColName := model.NewCIStr(genChangingColumnUniqueName(tblInfo, oldCol)) - if mysql.HasPriKeyFlag(oldCol.GetFlag()) { - job.State = model.JobStateCancelled - msg := "this column has primary key flag" - return ver, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) - } - - changingCol = modifyInfo.newCol.Clone() - changingCol.Name = newColName - changingCol.ChangeStateInfo = &model.ChangeStateInfo{DependencyColumnOffset: oldCol.Offset} - - originDefVal, err := GetOriginDefaultValueForModifyColumn(newContext(d.store), changingCol, oldCol) - if err != nil { - return ver, errors.Trace(err) - } - if err = changingCol.SetOriginDefaultValue(originDefVal); err != nil { - return ver, errors.Trace(err) - } - - InitAndAddColumnToTable(tblInfo, changingCol) - indexesToChange := FindRelatedIndexesToChange(tblInfo, oldCol.Name) - for _, info := range indexesToChange { - newIdxID := AllocateIndexID(tblInfo) - if !info.isTemp { - // We create a temp index for each normal index. - tmpIdx := info.IndexInfo.Clone() - tmpIdxName := genChangingIndexUniqueName(tblInfo, info.IndexInfo) - setIdxIDName(tmpIdx, newIdxID, model.NewCIStr(tmpIdxName)) - SetIdxColNameOffset(tmpIdx.Columns[info.Offset], changingCol) - tblInfo.Indices = append(tblInfo.Indices, tmpIdx) - } else { - // The index is a temp index created by previous modify column job(s). - // We can overwrite it to reduce reorg cost, because it will be dropped eventually. - tmpIdx := info.IndexInfo - oldTempIdxID := tmpIdx.ID - setIdxIDName(tmpIdx, newIdxID, tmpIdx.Name /* unchanged */) - SetIdxColNameOffset(tmpIdx.Columns[info.Offset], changingCol) - modifyInfo.removedIdxs = append(modifyInfo.removedIdxs, oldTempIdxID) - } - } - } else { - changingCol = model.FindColumnInfoByID(tblInfo.Columns, modifyInfo.changingCol.ID) - if changingCol == nil { - logutil.BgLogger().Error("the changing column has been removed", zap.String("category", "ddl"), zap.Error(err)) - job.State = model.JobStateCancelled - return ver, errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) - } - } - - return w.doModifyColumnTypeWithData(d, t, job, dbInfo, tblInfo, changingCol, oldCol, modifyInfo.newCol.Name, modifyInfo.pos, modifyInfo.removedIdxs) -} - -func setIdxIDName(idxInfo *model.IndexInfo, newID int64, newName model.CIStr) { - idxInfo.ID = newID - idxInfo.Name = newName -} - -// SetIdxColNameOffset sets index column name and offset from changing ColumnInfo. -func SetIdxColNameOffset(idxCol *model.IndexColumn, changingCol *model.ColumnInfo) { - idxCol.Name = changingCol.Name - idxCol.Offset = changingCol.Offset - canPrefix := types.IsTypePrefixable(changingCol.GetType()) - if !canPrefix || (changingCol.GetFlen() < idxCol.Length) { - idxCol.Length = types.UnspecifiedLength - } -} - -// rollbackModifyColumnJobWithData is used to rollback modify-column job which need to reorg the data. -func rollbackModifyColumnJobWithData(d *ddlCtx, t *meta.Meta, tblInfo *model.TableInfo, job *model.Job, oldCol *model.ColumnInfo, modifyInfo *modifyingColInfo) (ver int64, err error) { - // If the not-null change is included, we should clean the flag info in oldCol. - if modifyInfo.modifyColumnTp == mysql.TypeNull { - // Reset NotNullFlag flag. - tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.NotNullFlag) - // Reset PreventNullInsertFlag flag. - tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.PreventNullInsertFlag) - } - var changingIdxIDs []int64 - if modifyInfo.changingCol != nil { - changingIdxIDs = buildRelatedIndexIDs(tblInfo, modifyInfo.changingCol.ID) - // The job is in the middle state. The appended changingCol and changingIndex should - // be removed from the tableInfo as well. - removeChangingColAndIdxs(tblInfo, modifyInfo.changingCol.ID) - } - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - // Reconstruct the job args to add the temporary index ids into delete range table. - job.Args = []interface{}{changingIdxIDs, getPartitionIDs(tblInfo)} - return ver, nil -} - -func removeChangingColAndIdxs(tblInfo *model.TableInfo, changingColID int64) { - restIdx := tblInfo.Indices[:0] - for _, idx := range tblInfo.Indices { - if !idx.HasColumnInIndexColumns(tblInfo, changingColID) { - restIdx = append(restIdx, idx) - } - } - tblInfo.Indices = restIdx - - restCols := tblInfo.Columns[:0] - for _, c := range tblInfo.Columns { - if c.ID != changingColID { - restCols = append(restCols, c) - } - } - tblInfo.Columns = restCols -} - -func (w *worker) doModifyColumnTypeWithData( - d *ddlCtx, t *meta.Meta, job *model.Job, - dbInfo *model.DBInfo, tblInfo *model.TableInfo, changingCol, oldCol *model.ColumnInfo, - colName model.CIStr, pos *ast.ColumnPosition, rmIdxIDs []int64) (ver int64, _ error) { - var err error - originalState := changingCol.State - targetCol := changingCol.Clone() - targetCol.Name = colName - changingIdxs := buildRelatedIndexInfos(tblInfo, changingCol.ID) - switch changingCol.State { - case model.StateNone: - // Column from null to not null. - if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(changingCol.GetFlag()) { - // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. - err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, targetCol, oldCol.GetType() != changingCol.GetType()) - if err != nil { - if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { - job.State = model.JobStateRollingback - } - return ver, err - } - } - // none -> delete only - updateChangingObjState(changingCol, changingIdxs, model.StateDeleteOnly) - failpoint.Inject("mockInsertValueAfterCheckNull", func(val failpoint.Value) { - if valStr, ok := val.(string); ok { - var sctx sessionctx.Context - sctx, err := w.sessPool.Get() - if err != nil { - failpoint.Return(ver, err) - } - defer w.sessPool.Put(sctx) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - //nolint:forcetypeassert - _, _, err = sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, valStr) - if err != nil { - job.State = model.JobStateCancelled - failpoint.Return(ver, err) - } - } - }) - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != changingCol.State) - if err != nil { - return ver, errors.Trace(err) - } - // Make sure job args change after `updateVersionAndTableInfoWithCheck`, otherwise, the job args will - // be updated in `updateDDLJob` even if it meets an error in `updateVersionAndTableInfoWithCheck`. - job.SchemaState = model.StateDeleteOnly - metrics.GetBackfillProgressByLabel(metrics.LblModifyColumn, job.SchemaName, tblInfo.Name.String()).Set(0) - job.Args = append(job.Args, changingCol, changingIdxs, rmIdxIDs) - case model.StateDeleteOnly: - // Column from null to not null. - if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(changingCol.GetFlag()) { - // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. - err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, targetCol, oldCol.GetType() != changingCol.GetType()) - if err != nil { - if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { - job.State = model.JobStateRollingback - } - return ver, err - } - } - // delete only -> write only - updateChangingObjState(changingCol, changingIdxs, model.StateWriteOnly) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - // write only -> reorganization - updateChangingObjState(changingCol, changingIdxs, model.StateWriteReorganization) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) - if err != nil { - return ver, errors.Trace(err) - } - // Initialize SnapshotVer to 0 for later reorganization check. - job.SnapshotVer = 0 - job.SchemaState = model.StateWriteReorganization - case model.StateWriteReorganization: - tbl, err := getTable(d.store, dbInfo.ID, tblInfo) - if err != nil { - return ver, errors.Trace(err) - } - - var done bool - if job.MultiSchemaInfo != nil { - done, ver, err = doReorgWorkForModifyColumnMultiSchema(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) - } else { - done, ver, err = doReorgWorkForModifyColumn(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) - } - if !done { - return ver, err - } - - rmIdxIDs = append(buildRelatedIndexIDs(tblInfo, oldCol.ID), rmIdxIDs...) - - err = adjustTableInfoAfterModifyColumnWithData(tblInfo, pos, oldCol, changingCol, colName, changingIdxs) - if err != nil { - job.State = model.JobStateRollingback - return ver, errors.Trace(err) - } - - updateChangingObjState(changingCol, changingIdxs, model.StatePublic) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - // Refactor the job args to add the old index ids into delete range table. - job.Args = []interface{}{rmIdxIDs, getPartitionIDs(tblInfo)} - asyncNotifyEvent(d, &ddlutil.Event{Tp: model.ActionModifyColumn, TableInfo: tblInfo, ColumnInfos: []*model.ColumnInfo{changingCol}}) - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("column", changingCol.State) - } - - return ver, errors.Trace(err) -} - -func doReorgWorkForModifyColumnMultiSchema(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, - oldCol, changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) (done bool, ver int64, err error) { - if job.MultiSchemaInfo.Revertible { - done, ver, err = doReorgWorkForModifyColumn(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) - if done { - // We need another round to wait for all the others sub-jobs to finish. - job.MarkNonRevertible() - } - // We need another round to run the reorg process. - return false, ver, err - } - // Non-revertible means all the sub jobs finished. - return true, ver, err -} - -func doReorgWorkForModifyColumn(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, - oldCol, changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) (done bool, ver int64, err error) { - job.ReorgMeta.ReorgTp = model.ReorgTypeTxn - sctx, err1 := w.sessPool.Get() - if err1 != nil { - err = errors.Trace(err1) - return - } - defer w.sessPool.Put(sctx) - rh := newReorgHandler(sess.NewSession(sctx)) - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return false, ver, errors.Trace(err) - } - reorgInfo, err := getReorgInfo(d.jobContext(job.ID, job.ReorgMeta), - d, rh, job, dbInfo, tbl, BuildElements(changingCol, changingIdxs), false) - if err != nil || reorgInfo == nil || reorgInfo.first { - // If we run reorg firstly, we should update the job snapshot version - // and then run the reorg next time. - return false, ver, errors.Trace(err) - } - - // Inject a failpoint so that we can pause here and do verification on other components. - // With a failpoint-enabled version of TiDB, you can trigger this failpoint by the following command: - // enable: curl -X PUT -d "pause" "http://127.0.0.1:10080/fail/github.com/pingcap/tidb/ddl/mockDelayInModifyColumnTypeWithData". - // disable: curl -X DELETE "http://127.0.0.1:10080/fail/github.com/pingcap/tidb/ddl/mockDelayInModifyColumnTypeWithData" - failpoint.Inject("mockDelayInModifyColumnTypeWithData", func() {}) - err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (addIndexErr error) { - defer util.Recover(metrics.LabelDDL, "onModifyColumn", - func() { - addIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("modify table `%v` column `%v` panic", tbl.Meta().Name, oldCol.Name) - }, false) - // Use old column name to generate less confusing error messages. - changingColCpy := changingCol.Clone() - changingColCpy.Name = oldCol.Name - return w.updateCurrentElement(tbl, reorgInfo) - }) - if err != nil { - if dbterror.ErrPausedDDLJob.Equal(err) { - return false, ver, nil - } - - if dbterror.ErrWaitReorgTimeout.Equal(err) { - // If timeout, we should return, check for the owner and re-wait job done. - return false, ver, nil - } - if kv.IsTxnRetryableError(err) || dbterror.ErrNotOwner.Equal(err) { - return false, ver, errors.Trace(err) - } - if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { - logutil.BgLogger().Warn("run modify column job failed, RemoveDDLReorgHandle failed, can't convert job to rollback", zap.String("category", "ddl"), - zap.String("job", job.String()), zap.Error(err1)) - } - logutil.BgLogger().Warn("run modify column job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) - job.State = model.JobStateRollingback - return false, ver, errors.Trace(err) - } - return true, ver, nil -} - -func adjustTableInfoAfterModifyColumnWithData(tblInfo *model.TableInfo, pos *ast.ColumnPosition, - oldCol, changingCol *model.ColumnInfo, newName model.CIStr, changingIdxs []*model.IndexInfo) (err error) { - if pos != nil && pos.RelativeColumn != nil && oldCol.Name.L == pos.RelativeColumn.Name.L { - // For cases like `modify column b after b`, it should report this error. - return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) - } - internalColName := changingCol.Name - changingCol = replaceOldColumn(tblInfo, oldCol, changingCol, newName) - if len(changingIdxs) > 0 { - updateNewIdxColsNameOffset(changingIdxs, internalColName, changingCol) - indexesToRemove := filterIndexesToRemove(changingIdxs, newName, tblInfo) - replaceOldIndexes(tblInfo, indexesToRemove) - } - if tblInfo.TTLInfo != nil { - updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, changingCol.Name) - } - // Move the new column to a correct offset. - destOffset, err := LocateOffsetToMove(changingCol.Offset, pos, tblInfo) - if err != nil { - return errors.Trace(err) - } - tblInfo.MoveColumnInfo(changingCol.Offset, destOffset) - return nil -} - -func replaceOldColumn(tblInfo *model.TableInfo, oldCol, changingCol *model.ColumnInfo, - newName model.CIStr) *model.ColumnInfo { - tblInfo.MoveColumnInfo(changingCol.Offset, len(tblInfo.Columns)-1) - changingCol = updateChangingCol(changingCol, newName, oldCol.Offset) - tblInfo.Columns[oldCol.Offset] = changingCol - tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-1] - return changingCol -} - -func replaceOldIndexes(tblInfo *model.TableInfo, changingIdxs []*model.IndexInfo) { - // Remove the changing indexes. - for i, idx := range tblInfo.Indices { - for _, cIdx := range changingIdxs { - if cIdx.ID == idx.ID { - tblInfo.Indices[i] = nil - break - } - } - } - tmp := tblInfo.Indices[:0] - for _, idx := range tblInfo.Indices { - if idx != nil { - tmp = append(tmp, idx) - } - } - tblInfo.Indices = tmp - // Replace the old indexes with changing indexes. - for _, cIdx := range changingIdxs { - // The index name should be changed from '_Idx$_name' to 'name'. - idxName := getChangingIndexOriginName(cIdx) - for i, idx := range tblInfo.Indices { - if strings.EqualFold(idxName, idx.Name.O) { - cIdx.Name = model.NewCIStr(idxName) - tblInfo.Indices[i] = cIdx - break - } - } - } -} - -// updateNewIdxColsNameOffset updates the name&offset of the index column. -func updateNewIdxColsNameOffset(changingIdxs []*model.IndexInfo, - oldName model.CIStr, changingCol *model.ColumnInfo) { - for _, idx := range changingIdxs { - for _, col := range idx.Columns { - if col.Name.L == oldName.L { - SetIdxColNameOffset(col, changingCol) - } - } - } -} - -func updateFKInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model.CIStr) { - if oldCol.L == newCol.L { - return - } - for _, fk := range tblInfo.ForeignKeys { - for i := range fk.Cols { - if fk.Cols[i].L == oldCol.L { - fk.Cols[i] = newCol - } - } - } -} - -func updateTTLInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model.CIStr) { - if oldCol.L == newCol.L { - return - } - if tblInfo.TTLInfo != nil { - if tblInfo.TTLInfo.ColumnName.L == oldCol.L { - tblInfo.TTLInfo.ColumnName = newCol - } - } -} - -// filterIndexesToRemove filters out the indexes that can be removed. -func filterIndexesToRemove(changingIdxs []*model.IndexInfo, colName model.CIStr, tblInfo *model.TableInfo) []*model.IndexInfo { - indexesToRemove := make([]*model.IndexInfo, 0, len(changingIdxs)) - for _, idx := range changingIdxs { - var hasOtherChangingCol bool - for _, col := range idx.Columns { - if col.Name.L == colName.L { - continue // ignore the current modifying column. - } - if !hasOtherChangingCol { - hasOtherChangingCol = tblInfo.Columns[col.Offset].ChangeStateInfo != nil - } - } - // For the indexes that still contains other changing column, skip removing it now. - // We leave the removal work to the last modify column job. - if !hasOtherChangingCol { - indexesToRemove = append(indexesToRemove, idx) - } - } - return indexesToRemove -} - -func updateChangingCol(col *model.ColumnInfo, newName model.CIStr, newOffset int) *model.ColumnInfo { - col.Name = newName - col.ChangeStateInfo = nil - col.Offset = newOffset - // After changing the column, the column's type is change, so it needs to set OriginDefaultValue back - // so that there is no error in getting the default value from OriginDefaultValue. - // Besides, nil data that was not backfilled in the "add column" is backfilled after the column is changed. - // So it can set OriginDefaultValue to nil. - col.OriginDefaultValue = nil - return col -} - -func buildRelatedIndexInfos(tblInfo *model.TableInfo, colID int64) []*model.IndexInfo { - var indexInfos []*model.IndexInfo - for _, idx := range tblInfo.Indices { - if idx.HasColumnInIndexColumns(tblInfo, colID) { - indexInfos = append(indexInfos, idx) - } - } - return indexInfos -} - -func buildRelatedIndexIDs(tblInfo *model.TableInfo, colID int64) []int64 { - var oldIdxIDs []int64 - for _, idx := range tblInfo.Indices { - if idx.HasColumnInIndexColumns(tblInfo, colID) { - oldIdxIDs = append(oldIdxIDs, idx.ID) - } - } - return oldIdxIDs -} - -// LocateOffsetToMove returns the offset of the column to move. -func LocateOffsetToMove(currentOffset int, pos *ast.ColumnPosition, tblInfo *model.TableInfo) (destOffset int, err error) { - if pos == nil { - return currentOffset, nil - } - // Get column offset. - switch pos.Tp { - case ast.ColumnPositionFirst: - return 0, nil - case ast.ColumnPositionAfter: - c := model.FindColumnInfo(tblInfo.Columns, pos.RelativeColumn.Name.L) - if c == nil || c.State != model.StatePublic { - return 0, infoschema.ErrColumnNotExists.GenWithStackByArgs(pos.RelativeColumn, tblInfo.Name) - } - if currentOffset <= c.Offset { - return c.Offset, nil - } - return c.Offset + 1, nil - case ast.ColumnPositionNone: - return currentOffset, nil - default: - return 0, errors.Errorf("unknown column position type") - } -} - -// BuildElements is exported for testing. -func BuildElements(changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) []*meta.Element { - elements := make([]*meta.Element, 0, len(changingIdxs)+1) - elements = append(elements, &meta.Element{ID: changingCol.ID, TypeKey: meta.ColumnElementKey}) - for _, idx := range changingIdxs { - elements = append(elements, &meta.Element{ID: idx.ID, TypeKey: meta.IndexElementKey}) - } - return elements -} - -func (w *worker) updatePhysicalTableRow(t table.Table, reorgInfo *reorgInfo) error { - logutil.BgLogger().Info("start to update table row", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) - if tbl, ok := t.(table.PartitionedTable); ok { - done := false - for !done { - p := tbl.GetPartition(reorgInfo.PhysicalTableID) - if p == nil { - return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, t.Meta().ID) - } - workType := typeReorgPartitionWorker - switch reorgInfo.Job.Type { - case model.ActionReorganizePartition, - model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning: - // Expected - default: - // workType = typeUpdateColumnWorker - // TODO: Support Modify Column on partitioned table - // https://github.com/pingcap/tidb/issues/38297 - return dbterror.ErrCancelledDDLJob.GenWithStack("Modify Column on partitioned table / typeUpdateColumnWorker not yet supported.") - } - err := w.writePhysicalTableRecord(w.sessPool, p, workType, reorgInfo) - if err != nil { - return err - } - done, err = updateReorgInfo(w.sessPool, tbl, reorgInfo) - if err != nil { - return errors.Trace(err) - } - } - return nil - } - if tbl, ok := t.(table.PhysicalTable); ok { - return w.writePhysicalTableRecord(w.sessPool, tbl, typeUpdateColumnWorker, reorgInfo) - } - return dbterror.ErrCancelledDDLJob.GenWithStack("internal error for phys tbl id: %d tbl id: %d", reorgInfo.PhysicalTableID, t.Meta().ID) -} - -// TestReorgGoroutineRunning is only used in test to indicate the reorg goroutine has been started. -var TestReorgGoroutineRunning = make(chan interface{}) - -// updateCurrentElement update the current element for reorgInfo. -func (w *worker) updateCurrentElement(t table.Table, reorgInfo *reorgInfo) error { - failpoint.Inject("mockInfiniteReorgLogic", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - a := new(interface{}) - TestReorgGoroutineRunning <- a - for { - time.Sleep(30 * time.Millisecond) - if w.isReorgCancelled(reorgInfo.Job.ID) { - // Job is cancelled. So it can't be done. - failpoint.Return(dbterror.ErrCancelledDDLJob) - } - } - } - }) - // TODO: Support partition tables. - if bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { - //nolint:forcetypeassert - err := w.updatePhysicalTableRow(t.(table.PhysicalTable), reorgInfo) - if err != nil { - return errors.Trace(err) - } - } - - if _, ok := t.(table.PartitionedTable); ok { - // TODO: remove when modify column of partitioned table is supported - // https://github.com/pingcap/tidb/issues/38297 - return dbterror.ErrCancelledDDLJob.GenWithStack("Modify Column on partitioned table / typeUpdateColumnWorker not yet supported.") - } - // Get the original start handle and end handle. - currentVer, err := getValidCurrentVersion(reorgInfo.d.store) - if err != nil { - return errors.Trace(err) - } - //nolint:forcetypeassert - originalStartHandle, originalEndHandle, err := getTableRange(reorgInfo.NewJobContext(), reorgInfo.d, t.(table.PhysicalTable), currentVer.Ver, reorgInfo.Job.Priority) - if err != nil { - return errors.Trace(err) - } - - startElementOffset := 0 - startElementOffsetToResetHandle := -1 - // This backfill job starts with backfilling index data, whose index ID is currElement.ID. - if bytes.Equal(reorgInfo.currElement.TypeKey, meta.IndexElementKey) { - for i, element := range reorgInfo.elements[1:] { - if reorgInfo.currElement.ID == element.ID { - startElementOffset = i - startElementOffsetToResetHandle = i - break - } - } - } - - for i := startElementOffset; i < len(reorgInfo.elements[1:]); i++ { - // This backfill job has been exited during processing. At that time, the element is reorgInfo.elements[i+1] and handle range is [reorgInfo.StartHandle, reorgInfo.EndHandle]. - // Then the handle range of the rest elements' is [originalStartHandle, originalEndHandle]. - if i == startElementOffsetToResetHandle+1 { - reorgInfo.StartKey, reorgInfo.EndKey = originalStartHandle, originalEndHandle - } - - // Update the element in the reorgInfo for updating the reorg meta below. - reorgInfo.currElement = reorgInfo.elements[i+1] - // Write the reorg info to store so the whole reorganize process can recover from panic. - err := reorgInfo.UpdateReorgMeta(reorgInfo.StartKey, w.sessPool) - logutil.BgLogger().Info("update column and indexes", zap.String("category", "ddl"), - zap.Int64("job ID", reorgInfo.Job.ID), - zap.Stringer("element", reorgInfo.currElement), - zap.String("start key", hex.EncodeToString(reorgInfo.StartKey)), - zap.String("end key", hex.EncodeToString(reorgInfo.EndKey))) - if err != nil { - return errors.Trace(err) - } - err = w.addTableIndex(t, reorgInfo) - if err != nil { - return errors.Trace(err) - } - } - return nil -} - -type updateColumnWorker struct { - *backfillCtx - oldColInfo *model.ColumnInfo - newColInfo *model.ColumnInfo - - // The following attributes are used to reduce memory allocation. - rowRecords []*rowRecord - rowDecoder *decoder.RowDecoder - - rowMap map[int64]types.Datum - - checksumBuffer rowcodec.RowData - checksumNeeded bool -} - -func newUpdateColumnWorker(sessCtx sessionctx.Context, id int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) *updateColumnWorker { - if !bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { - logutil.BgLogger().Error("Element type for updateColumnWorker incorrect", zap.String("jobQuery", reorgInfo.Query), - zap.String("reorgInfo", reorgInfo.String())) - return nil - } - var oldCol, newCol *model.ColumnInfo - for _, col := range t.WritableCols() { - if col.ID == reorgInfo.currElement.ID { - newCol = col.ColumnInfo - oldCol = table.FindCol(t.Cols(), getChangingColumnOriginName(newCol)).ColumnInfo - break - } - } - rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) - checksumNeeded := false - failpoint.Inject("forceRowLevelChecksumOnUpdateColumnBackfill", func() { - orig := variable.EnableRowLevelChecksum.Load() - defer variable.EnableRowLevelChecksum.Store(orig) - variable.EnableRowLevelChecksum.Store(true) - }) - // We use global `EnableRowLevelChecksum` to detect whether checksum is enabled in ddl backfill worker because - // `SessionVars.IsRowLevelChecksumEnabled` will filter out internal sessions. - if variable.EnableRowLevelChecksum.Load() { - if numNonPubCols := len(t.DeletableCols()) - len(t.Cols()); numNonPubCols > 1 { - cols := make([]*model.ColumnInfo, len(t.DeletableCols())) - for i, col := range t.DeletableCols() { - cols[i] = col.ToInfo() - } - logutil.BgLogger().Warn("skip checksum in update-column backfill since the number of non-public columns is greater than 1", - zap.String("jobQuery", reorgInfo.Query), zap.String("reorgInfo", reorgInfo.String()), zap.Any("cols", cols)) - } else { - checksumNeeded = true - } - } - return &updateColumnWorker{ - backfillCtx: newBackfillCtx(reorgInfo.d, id, sessCtx, reorgInfo.SchemaName, t, jc, "update_col_rate", false), - oldColInfo: oldCol, - newColInfo: newCol, - rowDecoder: rowDecoder, - rowMap: make(map[int64]types.Datum, len(decodeColMap)), - checksumNeeded: checksumNeeded, - } -} - -func (w *updateColumnWorker) AddMetricInfo(cnt float64) { - w.metricCounter.Add(cnt) -} - -func (*updateColumnWorker) String() string { - return typeUpdateColumnWorker.String() -} - -func (w *updateColumnWorker) GetCtx() *backfillCtx { - return w.backfillCtx -} - -type rowRecord struct { - key []byte // It's used to lock a record. Record it to reduce the encoding time. - vals []byte // It's the record. - warning *terror.Error // It's used to record the cast warning of a record. -} - -// getNextHandleKey gets next handle of entry that we are going to process. -func getNextHandleKey(taskRange reorgBackfillTask, - taskDone bool, lastAccessedHandle kv.Key) (nextHandle kv.Key) { - if !taskDone { - // The task is not done. So we need to pick the last processed entry's handle and add one. - return lastAccessedHandle.Next() - } - - return taskRange.endKey.Next() -} - -func (w *updateColumnWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*rowRecord, kv.Key, bool, error) { - w.rowRecords = w.rowRecords[:0] - startTime := time.Now() - - // taskDone means that the added handle is out of taskRange.endHandle. - taskDone := false - var lastAccessedHandle kv.Key - oprStartTime := startTime - err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, taskRange.physicalTable.RecordPrefix(), - txn.StartTS(), taskRange.startKey, taskRange.endKey, func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { - oprEndTime := time.Now() - logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in updateColumnWorker fetchRowColVals", 0) - oprStartTime = oprEndTime - - taskDone = recordKey.Cmp(taskRange.endKey) >= 0 - - if taskDone || len(w.rowRecords) >= w.batchCnt { - return false, nil - } - - if err1 := w.getRowRecord(handle, recordKey, rawRow); err1 != nil { - return false, errors.Trace(err1) - } - lastAccessedHandle = recordKey - if recordKey.Cmp(taskRange.endKey) == 0 { - taskDone = true - return false, nil - } - return true, nil - }) - - if len(w.rowRecords) == 0 { - taskDone = true - } - - logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) - return w.rowRecords, getNextHandleKey(taskRange, taskDone, lastAccessedHandle), taskDone, errors.Trace(err) -} - -func (w *updateColumnWorker) getRowRecord(handle kv.Handle, recordKey []byte, rawRow []byte) error { - sysTZ := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() - _, err := w.rowDecoder.DecodeTheExistedColumnMap(w.sessCtx, handle, rawRow, sysTZ, w.rowMap) - if err != nil { - return errors.Trace(dbterror.ErrCantDecodeRecord.GenWithStackByArgs("column", err)) - } - - if _, ok := w.rowMap[w.newColInfo.ID]; ok { - // The column is already added by update or insert statement, skip it. - w.cleanRowMap() - return nil - } - - var recordWarning *terror.Error - // Since every updateColumnWorker handle their own work individually, we can cache warning in statement context when casting datum. - oldWarn := w.sessCtx.GetSessionVars().StmtCtx.GetWarnings() - if oldWarn == nil { - oldWarn = []stmtctx.SQLWarn{} - } else { - oldWarn = oldWarn[:0] - } - w.sessCtx.GetSessionVars().StmtCtx.SetWarnings(oldWarn) - val := w.rowMap[w.oldColInfo.ID] - col := w.newColInfo - if val.Kind() == types.KindNull && col.FieldType.GetType() == mysql.TypeTimestamp && mysql.HasNotNullFlag(col.GetFlag()) { - if v, err := expression.GetTimeCurrentTimestamp(w.sessCtx, col.GetType(), col.GetDecimal()); err == nil { - // convert null value to timestamp should be substituted with current timestamp if NOT_NULL flag is set. - w.rowMap[w.oldColInfo.ID] = v - } - } - newColVal, err := table.CastValue(w.sessCtx, w.rowMap[w.oldColInfo.ID], w.newColInfo, false, false) - if err != nil { - return w.reformatErrors(err) - } - warn := w.sessCtx.GetSessionVars().StmtCtx.GetWarnings() - if len(warn) != 0 { - //nolint:forcetypeassert - recordWarning = errors.Cause(w.reformatErrors(warn[0].Err)).(*terror.Error) - } - - failpoint.Inject("MockReorgTimeoutInOneRegion", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - if handle.IntValue() == 3000 && atomic.CompareAndSwapInt32(&TestCheckReorgTimeout, 0, 1) { - failpoint.Return(errors.Trace(dbterror.ErrWaitReorgTimeout)) - } - } - }) - - w.rowMap[w.newColInfo.ID] = newColVal - _, err = w.rowDecoder.EvalRemainedExprColumnMap(w.sessCtx, w.rowMap) - if err != nil { - return errors.Trace(err) - } - newColumnIDs := make([]int64, 0, len(w.rowMap)) - newRow := make([]types.Datum, 0, len(w.rowMap)) - for colID, val := range w.rowMap { - newColumnIDs = append(newColumnIDs, colID) - newRow = append(newRow, val) - } - checksums := w.calcChecksums() - sctx, rd := w.sessCtx.GetSessionVars().StmtCtx, &w.sessCtx.GetSessionVars().RowEncoder - newRowVal, err := tablecodec.EncodeRow(sctx, newRow, newColumnIDs, nil, nil, rd, checksums...) - if err != nil { - return errors.Trace(err) - } - - w.rowRecords = append(w.rowRecords, &rowRecord{key: recordKey, vals: newRowVal, warning: recordWarning}) - w.cleanRowMap() - return nil -} - -func (w *updateColumnWorker) calcChecksums() []uint32 { - if !w.checksumNeeded { - return nil - } - // when w.checksumNeeded is true, it indicates that there is only one write-reorg column (the new column) and other - // columns are public, thus we have to calculate two checksums that one of which only contains the old column and - // the other only contains the new column. - var checksums [2]uint32 - for i, id := range []int64{w.newColInfo.ID, w.oldColInfo.ID} { - if len(w.checksumBuffer.Cols) > 0 { - w.checksumBuffer.Cols = w.checksumBuffer.Cols[:0] - } - for _, col := range w.table.DeletableCols() { - if col.ID == id || (col.IsVirtualGenerated()) { - continue - } - d := w.rowMap[col.ID] - w.checksumBuffer.Cols = append(w.checksumBuffer.Cols, rowcodec.ColData{ColumnInfo: col.ToInfo(), Datum: &d}) - } - if !sort.IsSorted(w.checksumBuffer) { - sort.Sort(w.checksumBuffer) - } - checksum, err := w.checksumBuffer.Checksum() - if err != nil { - logutil.BgLogger().Warn("skip checksum in update-column backfill due to encode error", zap.Error(err)) - return nil - } - checksums[i] = checksum - } - return checksums[:] -} - -// reformatErrors casted error because `convertTo` function couldn't package column name and datum value for some errors. -func (w *updateColumnWorker) reformatErrors(err error) error { - // Since row count is not precious in concurrent reorganization, here we substitute row count with datum value. - if types.ErrTruncated.Equal(err) || types.ErrDataTooLong.Equal(err) { - dStr := datumToStringNoErr(w.rowMap[w.oldColInfo.ID]) - err = types.ErrTruncated.GenWithStack("Data truncated for column '%s', value is '%s'", w.oldColInfo.Name, dStr) - } - - if types.ErrWarnDataOutOfRange.Equal(err) { - dStr := datumToStringNoErr(w.rowMap[w.oldColInfo.ID]) - err = types.ErrWarnDataOutOfRange.GenWithStack("Out of range value for column '%s', the value is '%s'", w.oldColInfo.Name, dStr) - } - return err -} - -func datumToStringNoErr(d types.Datum) string { - if v, err := d.ToString(); err == nil { - return v - } - return fmt.Sprintf("%v", d.GetValue()) -} - -func (w *updateColumnWorker) cleanRowMap() { - for id := range w.rowMap { - delete(w.rowMap, id) - } -} - -// BackfillData will backfill the table record in a transaction. A lock corresponds to a rowKey if the value of rowKey is changed. -func (w *updateColumnWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { - oprStartTime := time.Now() - ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) - errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - taskCtx.addedCount = 0 - taskCtx.scanCount = 0 - - // Because TiCDC do not want this kind of change, - // so we set the lossy DDL reorg txn source to 1 to - // avoid TiCDC to replicate this kind of change. - var txnSource uint64 - if val := txn.GetOption(kv.TxnSource); val != nil { - txnSource, _ = val.(uint64) - } - err := kv.SetLossyDDLReorgSource(&txnSource, kv.LossyDDLColumnReorgSource) - if err != nil { - return errors.Trace(err) - } - txn.SetOption(kv.TxnSource, txnSource) - - txn.SetOption(kv.Priority, handleRange.priority) - if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { - txn.SetOption(kv.ResourceGroupTagger, tagger) - } - txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) - - rowRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) - if err != nil { - return errors.Trace(err) - } - taskCtx.nextKey = nextKey - taskCtx.done = taskDone - - // Optimize for few warnings! - warningsMap := make(map[errors.ErrorID]*terror.Error, 2) - warningsCountMap := make(map[errors.ErrorID]int64, 2) - for _, rowRecord := range rowRecords { - taskCtx.scanCount++ - - err = txn.Set(rowRecord.key, rowRecord.vals) - if err != nil { - return errors.Trace(err) - } - taskCtx.addedCount++ - if rowRecord.warning != nil { - if _, ok := warningsCountMap[rowRecord.warning.ID()]; ok { - warningsCountMap[rowRecord.warning.ID()]++ - } else { - warningsCountMap[rowRecord.warning.ID()] = 1 - warningsMap[rowRecord.warning.ID()] = rowRecord.warning - } - } - } - - // Collect the warnings. - taskCtx.warnings, taskCtx.warningsCount = warningsMap, warningsCountMap - - return nil - }) - logSlowOperations(time.Since(oprStartTime), "BackfillData", 3000) - - return -} - -func updateChangingObjState(changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo, schemaState model.SchemaState) { - changingCol.State = schemaState - for _, idx := range changingIdxs { - idx.State = schemaState - } -} - -// doModifyColumn updates the column information and reorders all columns. It does not support modifying column data. -func (w *worker) doModifyColumn( - d *ddlCtx, t *meta.Meta, job *model.Job, dbInfo *model.DBInfo, tblInfo *model.TableInfo, - newCol, oldCol *model.ColumnInfo, pos *ast.ColumnPosition) (ver int64, _ error) { - if oldCol.ID != newCol.ID { - job.State = model.JobStateRollingback - return ver, dbterror.ErrColumnInChange.GenWithStackByArgs(oldCol.Name, newCol.ID) - } - // Column from null to not null. - if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(newCol.GetFlag()) { - noPreventNullFlag := !mysql.HasPreventNullInsertFlag(oldCol.GetFlag()) - - // lease = 0 means it's in an integration test. In this case we don't delay so the test won't run too slowly. - // We need to check after the flag is set - if d.lease > 0 && !noPreventNullFlag { - delayForAsyncCommit() - } - - // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. - err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, newCol, oldCol.GetType() != newCol.GetType()) - if err != nil { - if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { - job.State = model.JobStateRollingback - } - return ver, err - } - // The column should get into prevent null status first. - if noPreventNullFlag { - return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) - } - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - // Store the mark and enter the next DDL handling loop. - return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) - } - - if err := adjustTableInfoAfterModifyColumn(tblInfo, newCol, oldCol, pos); err != nil { - job.State = model.JobStateRollingback - return ver, errors.Trace(err) - } - - childTableInfos, err := adjustForeignKeyChildTableInfoAfterModifyColumn(d, t, job, tblInfo, newCol, oldCol) - if err != nil { - return ver, errors.Trace(err) - } - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true, childTableInfos...) - if err != nil { - // Modified the type definition of 'null' to 'not null' before this, so rollBack the job when an error occurs. - job.State = model.JobStateRollingback - return ver, errors.Trace(err) - } - - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - // For those column-type-change type which doesn't need reorg data, we should also mock the job args for delete range. - job.Args = []interface{}{[]int64{}, []int64{}} - return ver, nil -} - -func adjustTableInfoAfterModifyColumn( - tblInfo *model.TableInfo, newCol, oldCol *model.ColumnInfo, pos *ast.ColumnPosition) error { - // We need the latest column's offset and state. This information can be obtained from the store. - newCol.Offset = oldCol.Offset - newCol.State = oldCol.State - if pos != nil && pos.RelativeColumn != nil && oldCol.Name.L == pos.RelativeColumn.Name.L { - // For cases like `modify column b after b`, it should report this error. - return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) - } - destOffset, err := LocateOffsetToMove(oldCol.Offset, pos, tblInfo) - if err != nil { - return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) - } - tblInfo.Columns[oldCol.Offset] = newCol - tblInfo.MoveColumnInfo(oldCol.Offset, destOffset) - updateNewIdxColsNameOffset(tblInfo.Indices, oldCol.Name, newCol) - updateFKInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name) - updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name) - return nil -} - -func adjustForeignKeyChildTableInfoAfterModifyColumn(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, newCol, oldCol *model.ColumnInfo) ([]schemaIDAndTableInfo, error) { - if !variable.EnableForeignKey.Load() || newCol.Name.L == oldCol.Name.L { - return nil, nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return nil, err - } - referredFKs := is.GetTableReferredForeignKeys(job.SchemaName, tblInfo.Name.L) - if len(referredFKs) == 0 { - return nil, nil - } - fkh := newForeignKeyHelper() - fkh.addLoadedTable(job.SchemaName, tblInfo.Name.L, job.SchemaID, tblInfo) - for _, referredFK := range referredFKs { - info, err := fkh.getTableFromStorage(is, t, referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - if infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err) { - continue - } - return nil, err - } - fkInfo := model.FindFKInfoByName(info.tblInfo.ForeignKeys, referredFK.ChildFKName.L) - if fkInfo == nil { - continue - } - for i := range fkInfo.RefCols { - if fkInfo.RefCols[i].L == oldCol.Name.L { - fkInfo.RefCols[i] = newCol.Name - } - } - } - infoList := make([]schemaIDAndTableInfo, 0, len(fkh.loaded)) - for _, info := range fkh.loaded { - if info.tblInfo.ID == tblInfo.ID { - continue - } - infoList = append(infoList, info) - } - return infoList, nil -} - -func checkAndApplyAutoRandomBits(d *ddlCtx, m *meta.Meta, dbInfo *model.DBInfo, tblInfo *model.TableInfo, - oldCol *model.ColumnInfo, newCol *model.ColumnInfo, newAutoRandBits uint64) error { - if newAutoRandBits == 0 { - return nil - } - idAcc := m.GetAutoIDAccessors(dbInfo.ID, tblInfo.ID) - err := checkNewAutoRandomBits(idAcc, oldCol, newCol, newAutoRandBits, tblInfo.AutoRandomRangeBits, tblInfo.SepAutoInc()) - if err != nil { - return err - } - return applyNewAutoRandomBits(d, m, dbInfo, tblInfo, oldCol, newAutoRandBits) -} - -// checkNewAutoRandomBits checks whether the new auto_random bits number can cause overflow. -func checkNewAutoRandomBits(idAccessors meta.AutoIDAccessors, oldCol *model.ColumnInfo, - newCol *model.ColumnInfo, newShardBits, newRangeBits uint64, sepAutoInc bool) error { - shardFmt := autoid.NewShardIDFormat(&newCol.FieldType, newShardBits, newRangeBits) - - idAcc := idAccessors.RandomID() - convertedFromAutoInc := mysql.HasAutoIncrementFlag(oldCol.GetFlag()) - if convertedFromAutoInc { - if sepAutoInc { - idAcc = idAccessors.IncrementID(model.TableInfoVersion5) - } else { - idAcc = idAccessors.RowID() - } - } - // Generate a new auto ID first to prevent concurrent update in DML. - _, err := idAcc.Inc(1) - if err != nil { - return err - } - currentIncBitsVal, err := idAcc.Get() - if err != nil { - return err - } - // Find the max number of available shard bits by - // counting leading zeros in current inc part of auto_random ID. - usedBits := uint64(64 - bits.LeadingZeros64(uint64(currentIncBitsVal))) - if usedBits > shardFmt.IncrementalBits { - overflowCnt := usedBits - shardFmt.IncrementalBits - errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, newShardBits-overflowCnt, newShardBits, oldCol.Name.O) - return dbterror.ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) - } - return nil -} - -// applyNewAutoRandomBits set auto_random bits to TableInfo and -// migrate auto_increment ID to auto_random ID if possible. -func applyNewAutoRandomBits(d *ddlCtx, m *meta.Meta, dbInfo *model.DBInfo, - tblInfo *model.TableInfo, oldCol *model.ColumnInfo, newAutoRandBits uint64) error { - tblInfo.AutoRandomBits = newAutoRandBits - needMigrateFromAutoIncToAutoRand := mysql.HasAutoIncrementFlag(oldCol.GetFlag()) - if !needMigrateFromAutoIncToAutoRand { - return nil - } - autoRandAlloc := autoid.NewAllocatorsFromTblInfo(d.store, dbInfo.ID, tblInfo).Get(autoid.AutoRandomType) - if autoRandAlloc == nil { - errMsg := fmt.Sprintf(autoid.AutoRandomAllocatorNotFound, dbInfo.Name.O, tblInfo.Name.O) - return dbterror.ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) - } - idAcc := m.GetAutoIDAccessors(dbInfo.ID, tblInfo.ID).RowID() - nextAutoIncID, err := idAcc.Get() - if err != nil { - return errors.Trace(err) - } - err = autoRandAlloc.Rebase(context.Background(), nextAutoIncID, false) - if err != nil { - return errors.Trace(err) - } - if err := idAcc.Del(); err != nil { - return errors.Trace(err) - } - return nil -} - -// checkForNullValue ensure there are no null values of the column of this table. -// `isDataTruncated` indicates whether the new field and the old field type are the same, in order to be compatible with mysql. -func checkForNullValue(ctx context.Context, sctx sessionctx.Context, isDataTruncated bool, schema, table model.CIStr, newCol *model.ColumnInfo, oldCols ...*model.ColumnInfo) error { - needCheckNullValue := false - for _, oldCol := range oldCols { - if oldCol.GetType() != mysql.TypeTimestamp && newCol.GetType() == mysql.TypeTimestamp { - // special case for convert null value of non-timestamp type to timestamp type, null value will be substituted with current timestamp. - continue - } - needCheckNullValue = true - } - if !needCheckNullValue { - return nil - } - var buf strings.Builder - buf.WriteString("select 1 from %n.%n where ") - paramsList := make([]interface{}, 0, 2+len(oldCols)) - paramsList = append(paramsList, schema.L, table.L) - for i, col := range oldCols { - if i == 0 { - buf.WriteString("%n is null") - paramsList = append(paramsList, col.Name.L) - } else { - buf.WriteString(" or %n is null") - paramsList = append(paramsList, col.Name.L) - } - } - buf.WriteString(" limit 1") - //nolint:forcetypeassert - rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, buf.String(), paramsList...) - if err != nil { - return errors.Trace(err) - } - rowCount := len(rows) - if rowCount != 0 { - if isDataTruncated { - return dbterror.ErrWarnDataTruncated.GenWithStackByArgs(newCol.Name.L, rowCount) - } - return dbterror.ErrInvalidUseOfNull - } - return nil -} - -func updateColumnDefaultValue(d *ddlCtx, t *meta.Meta, job *model.Job, newCol *model.ColumnInfo, oldColName *model.CIStr) (ver int64, _ error) { - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - // Store the mark and enter the next DDL handling loop. - return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) - } - - oldCol := model.FindColumnInfo(tblInfo.Columns, oldColName.L) - if oldCol == nil || oldCol.State != model.StatePublic { - job.State = model.JobStateCancelled - return ver, infoschema.ErrColumnNotExists.GenWithStackByArgs(newCol.Name, tblInfo.Name) - } - - if hasDefaultValue, _, err := checkColumnDefaultValue(newContext(d.store), table.ToColumn(oldCol.Clone()), newCol.DefaultValue); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } else if !hasDefaultValue { - job.State = model.JobStateCancelled - return ver, dbterror.ErrInvalidDefaultValue.GenWithStackByArgs(newCol.Name) - } - - // The newCol's offset may be the value of the old schema version, so we can't use newCol directly. - oldCol.DefaultValue = newCol.DefaultValue - oldCol.DefaultValueBit = newCol.DefaultValueBit - oldCol.DefaultIsExpr = newCol.DefaultIsExpr - if mysql.HasNoDefaultValueFlag(newCol.GetFlag()) { - oldCol.AddFlag(mysql.NoDefaultValueFlag) - } else { - oldCol.DelFlag(mysql.NoDefaultValueFlag) - sctx := newContext(d.store) - err = checkDefaultValue(sctx, table.ToColumn(oldCol), true) - if err != nil { - job.State = model.JobStateCancelled - return ver, err - } - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func isColumnWithIndex(colName string, indices []*model.IndexInfo) bool { - for _, indexInfo := range indices { - for _, col := range indexInfo.Columns { - if col.Name.L == colName { - return true - } - } - } - return false -} - -func isColumnCanDropWithIndex(colName string, indices []*model.IndexInfo) error { - for _, indexInfo := range indices { - if indexInfo.Primary || len(indexInfo.Columns) > 1 { - for _, col := range indexInfo.Columns { - if col.Name.L == colName { - return dbterror.ErrCantDropColWithIndex.GenWithStack("can't drop column %s with composite index covered or Primary Key covered now", colName) - } - } - } - } - return nil -} - -func listIndicesWithColumn(colName string, indices []*model.IndexInfo) []*model.IndexInfo { - ret := make([]*model.IndexInfo, 0) - for _, indexInfo := range indices { - if len(indexInfo.Columns) == 1 && colName == indexInfo.Columns[0].Name.L { - ret = append(ret, indexInfo) - } - } - return ret -} - -// GetColumnForeignKeyInfo returns the wanted foreign key info -func GetColumnForeignKeyInfo(colName string, fkInfos []*model.FKInfo) *model.FKInfo { - for _, fkInfo := range fkInfos { - for _, col := range fkInfo.Cols { - if col.L == colName { - return fkInfo - } - } - } - return nil -} - -// AllocateColumnID allocates next column ID from TableInfo. -func AllocateColumnID(tblInfo *model.TableInfo) int64 { - tblInfo.MaxColumnID++ - return tblInfo.MaxColumnID -} - -func checkAddColumnTooManyColumns(colNum int) error { - if uint32(colNum) > atomic.LoadUint32(&config.GetGlobalConfig().TableColumnCountLimit) { - return dbterror.ErrTooManyFields - } - return nil -} - -// rollbackModifyColumnJob rollbacks the job when an error occurs. -func rollbackModifyColumnJob(d *ddlCtx, t *meta.Meta, tblInfo *model.TableInfo, job *model.Job, newCol, oldCol *model.ColumnInfo, modifyColumnTp byte) (ver int64, _ error) { - var err error - if oldCol.ID == newCol.ID && modifyColumnTp == mysql.TypeNull { - // field NotNullFlag flag reset. - tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.NotNullFlag) - // field PreventNullInsertFlag flag reset. - tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.PreventNullInsertFlag) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - } - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - // For those column-type-change type which doesn't need reorg data, we should also mock the job args for delete range. - job.Args = []interface{}{[]int64{}, []int64{}} - return ver, nil -} - -// modifyColsFromNull2NotNull modifies the type definitions of 'null' to 'not null'. -// Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. -func modifyColsFromNull2NotNull(w *worker, dbInfo *model.DBInfo, tblInfo *model.TableInfo, cols []*model.ColumnInfo, newCol *model.ColumnInfo, isDataTruncated bool) error { - // Get sessionctx from context resource pool. - var sctx sessionctx.Context - sctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(sctx) - - skipCheck := false - failpoint.Inject("skipMockContextDoExec", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - skipCheck = true - } - }) - if !skipCheck { - // If there is a null value inserted, it cannot be modified and needs to be rollback. - err = checkForNullValue(w.ctx, sctx, isDataTruncated, dbInfo.Name, tblInfo.Name, newCol, cols...) - if err != nil { - return errors.Trace(err) - } - } - - // Prevent this field from inserting null values. - for _, col := range cols { - col.AddFlag(mysql.PreventNullInsertFlag) - } - return nil -} - -func generateOriginDefaultValue(col *model.ColumnInfo, ctx sessionctx.Context) (interface{}, error) { - var err error - odValue := col.GetDefaultValue() - if odValue == nil && mysql.HasNotNullFlag(col.GetFlag()) { - switch col.GetType() { - // Just use enum field's first element for OriginDefaultValue. - case mysql.TypeEnum: - defEnum, verr := types.ParseEnumValue(col.GetElems(), 1) - if verr != nil { - return nil, errors.Trace(verr) - } - defVal := types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()) - return defVal.ToString() - default: - zeroVal := table.GetZeroValue(col) - odValue, err = zeroVal.ToString() - if err != nil { - return nil, errors.Trace(err) - } - } - } - - if odValue == strings.ToUpper(ast.CurrentTimestamp) { - var t time.Time - if ctx == nil { - t = time.Now() - } else { - t, _ = expression.GetStmtTimestamp(ctx) - } - if col.GetType() == mysql.TypeTimestamp { - odValue = types.NewTime(types.FromGoTime(t.UTC()), col.GetType(), col.GetDecimal()).String() - } else if col.GetType() == mysql.TypeDatetime { - odValue = types.NewTime(types.FromGoTime(t), col.GetType(), col.GetDecimal()).String() - } - } - return odValue, nil -} - -func indexInfoContains(idxID int64, idxInfos []*model.IndexInfo) bool { - for _, idxInfo := range idxInfos { - if idxID == idxInfo.ID { - return true - } - } - return false -} - -func indexInfosToIDList(idxInfos []*model.IndexInfo) []int64 { - ids := make([]int64, 0, len(idxInfos)) - for _, idxInfo := range idxInfos { - ids = append(ids, idxInfo.ID) - } - return ids -} - -func genChangingColumnUniqueName(tblInfo *model.TableInfo, oldCol *model.ColumnInfo) string { - suffix := 0 - newColumnNamePrefix := fmt.Sprintf("%s%s", changingColumnPrefix, oldCol.Name.O) - newColumnLowerName := fmt.Sprintf("%s_%d", strings.ToLower(newColumnNamePrefix), suffix) - // Check whether the new column name is used. - columnNameMap := make(map[string]bool, len(tblInfo.Columns)) - for _, col := range tblInfo.Columns { - columnNameMap[col.Name.L] = true - } - for columnNameMap[newColumnLowerName] { - suffix++ - newColumnLowerName = fmt.Sprintf("%s_%d", strings.ToLower(newColumnNamePrefix), suffix) - } - return fmt.Sprintf("%s_%d", newColumnNamePrefix, suffix) -} - -func genChangingIndexUniqueName(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) string { - suffix := 0 - newIndexNamePrefix := fmt.Sprintf("%s%s", changingIndexPrefix, idxInfo.Name.O) - newIndexLowerName := fmt.Sprintf("%s_%d", strings.ToLower(newIndexNamePrefix), suffix) - // Check whether the new index name is used. - indexNameMap := make(map[string]bool, len(tblInfo.Indices)) - for _, idx := range tblInfo.Indices { - indexNameMap[idx.Name.L] = true - } - for indexNameMap[newIndexLowerName] { - suffix++ - newIndexLowerName = fmt.Sprintf("%s_%d", strings.ToLower(newIndexNamePrefix), suffix) - } - return fmt.Sprintf("%s_%d", newIndexNamePrefix, suffix) -} - -func getChangingIndexOriginName(changingIdx *model.IndexInfo) string { - idxName := strings.TrimPrefix(changingIdx.Name.O, changingIndexPrefix) - // Since the unique idxName may contain the suffix number (indexName_num), better trim the suffix. - var pos int - if pos = strings.LastIndex(idxName, "_"); pos == -1 { - return idxName - } - return idxName[:pos] -} - -func getChangingColumnOriginName(changingColumn *model.ColumnInfo) string { - columnName := strings.TrimPrefix(changingColumn.Name.O, changingColumnPrefix) - var pos int - if pos = strings.LastIndex(columnName, "_"); pos == -1 { - return columnName - } - return columnName[:pos] -} diff --git a/ddl/column_test.go b/ddl/column_test.go deleted file mode 100644 index 3da35757a6936..0000000000000 --- a/ddl/column_test.go +++ /dev/null @@ -1,948 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "reflect" - "strconv" - "sync" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func testCreateColumn(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, - colName string, pos string, defaultValue interface{}, dom *domain.Domain) int64 { - sql := fmt.Sprintf("alter table t1 add column %s int ", colName) - if defaultValue != nil { - sql += fmt.Sprintf("default %v ", defaultValue) - } - sql += pos - tk.MustExec(sql) - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - v := getSchemaVer(t, ctx) - require.NoError(t, dom.Reload()) - tblInfo, exist := dom.InfoSchema().TableByID(tblID) - require.True(t, exist) - checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) - return id -} - -func testCreateColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, - colNames []string, positions []string, defaultValue interface{}, dom *domain.Domain) int64 { - sql := "alter table t1 add column " - for i, colName := range colNames { - if i != 0 { - sql += ", add column " - } - sql += fmt.Sprintf("%s int %s", colName, positions[i]) - if defaultValue != nil { - sql += fmt.Sprintf(" default %v", defaultValue) - } - } - tk.MustExec(sql) - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - v := getSchemaVer(t, ctx) - require.NoError(t, dom.Reload()) - tblInfo, exist := dom.InfoSchema().TableByID(tblID) - require.True(t, exist) - checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) - return id -} - -func testDropColumnInternal(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName string, isError bool, dom *domain.Domain) int64 { - sql := fmt.Sprintf("alter table t1 drop column %s ", colName) - _, err := tk.Exec(sql) - if isError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - v := getSchemaVer(t, ctx) - require.NoError(t, dom.Reload()) - tblInfo, exist := dom.InfoSchema().TableByID(tblID) - require.True(t, exist) - checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) - return id -} - -func testDropTable(tk *testkit.TestKit, t *testing.T, dbName, tblName string, dom *domain.Domain) int64 { - sql := fmt.Sprintf("drop table %s ", tblName) - tk.MustExec("use " + dbName) - tk.MustExec(sql) - - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - require.NoError(t, dom.Reload()) - _, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) - require.Error(t, err) - return id -} - -func testCreateIndex(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, unique bool, indexName string, colName string, dom *domain.Domain) int64 { - un := "" - if unique { - un = "unique" - } - sql := fmt.Sprintf("alter table t1 add %s index %s(%s)", un, indexName, colName) - tk.MustExec(sql) - - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - v := getSchemaVer(t, ctx) - require.NoError(t, dom.Reload()) - tblInfo, exist := dom.InfoSchema().TableByID(tblID) - require.True(t, exist) - checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) - return id -} - -func testDropColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName []string, isError bool, dom *domain.Domain) int64 { - sql := "alter table t1 drop column " - for i, name := range colName { - if i != 0 { - sql += ", drop column " - } - sql += name - } - _, err := tk.Exec(sql) - if isError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - - idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) - id := int64(idi) - v := getSchemaVer(t, ctx) - require.NoError(t, dom.Reload()) - tblInfo, exist := dom.InfoSchema().TableByID(tblID) - require.True(t, exist) - checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) - return id -} - -func TestColumnBasic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") - - num := 10 - for i := 0; i < num; i++ { - tk.MustExec(fmt.Sprintf("insert into t1 values(%d, %d, %d)", i, 10*i, 100*i)) - } - - ctx := testNewContext(store) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - - tbl := testGetTable(t, dom, tableID) - - i := 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Len(t, data, 3) - require.Equal(t, data[0].GetInt64(), int64(i)) - require.Equal(t, data[1].GetInt64(), int64(10*i)) - require.Equal(t, data[2].GetInt64(), int64(100*i)) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equal(t, i, num) - - require.Nil(t, table.FindCol(tbl.Cols(), "c4")) - - jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "after c3", 100, dom) - testCheckJobDone(t, store, jobID, true) - - tbl = testGetTable(t, dom, tableID) - require.NotNil(t, table.FindCol(tbl.Cols(), "c4")) - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), - func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Len(t, data, 4) - require.Equal(t, data[0].GetInt64(), int64(i)) - require.Equal(t, data[1].GetInt64(), int64(10*i)) - require.Equal(t, data[2].GetInt64(), int64(100*i)) - require.Equal(t, data[3].GetInt64(), int64(100)) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equal(t, i, num) - - h, err := tbl.AddRecord(ctx, types.MakeDatums(11, 12, 13, 14)) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - values, err := tables.RowWithCols(tbl, ctx, h, tbl.Cols()) - require.NoError(t, err) - - require.Len(t, values, 4) - require.Equal(t, values[3].GetInt64(), int64(14)) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom) - testCheckJobDone(t, store, jobID, false) - - tbl = testGetTable(t, dom, tableID) - values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) - require.NoError(t, err) - - require.Len(t, values, 3) - require.Equal(t, values[2].GetInt64(), int64(13)) - - jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "", 111, dom) - testCheckJobDone(t, store, jobID, true) - - tbl = testGetTable(t, dom, tableID) - values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) - require.NoError(t, err) - - require.Len(t, values, 4) - require.Equal(t, values[3].GetInt64(), int64(111)) - - jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", "", 101, dom) - testCheckJobDone(t, store, jobID, true) - - tbl = testGetTable(t, dom, tableID) - values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) - require.NoError(t, err) - - require.Len(t, values, 5) - require.Equal(t, values[4].GetInt64(), int64(101)) - - jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", "first", 202, dom) - testCheckJobDone(t, store, jobID, true) - - tbl = testGetTable(t, dom, tableID) - cols := tbl.Cols() - require.Len(t, cols, 6) - require.Equal(t, cols[0].Offset, 0) - require.Equal(t, cols[0].Name.L, "c6") - require.Equal(t, cols[1].Offset, 1) - require.Equal(t, cols[1].Name.L, "c1") - require.Equal(t, cols[2].Offset, 2) - require.Equal(t, cols[2].Name.L, "c2") - require.Equal(t, cols[3].Offset, 3) - require.Equal(t, cols[3].Name.L, "c3") - require.Equal(t, cols[4].Offset, 4) - require.Equal(t, cols[4].Name.L, "c4") - require.Equal(t, cols[5].Offset, 5) - require.Equal(t, cols[5].Name.L, "c5") - - values, err = tables.RowWithCols(tbl, ctx, h, cols) - require.NoError(t, err) - - require.Len(t, values, 6) - require.Equal(t, values[0].GetInt64(), int64(202)) - require.Equal(t, values[5].GetInt64(), int64(101)) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c2", false, dom) - testCheckJobDone(t, store, jobID, false) - - tbl = testGetTable(t, dom, tableID) - - values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) - require.NoError(t, err) - require.Len(t, values, 5) - require.Equal(t, values[0].GetInt64(), int64(202)) - require.Equal(t, values[4].GetInt64(), int64(101)) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c1", false, dom) - testCheckJobDone(t, store, jobID, false) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c3", false, dom) - testCheckJobDone(t, store, jobID, false) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom) - testCheckJobDone(t, store, jobID, false) - - jobID = testCreateIndex(tk, t, testkit.NewTestKit(t, store).Session(), tableID, false, "c5_idx", "c5", dom) - testCheckJobDone(t, store, jobID, true) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", false, dom) - testCheckJobDone(t, store, jobID, false) - - jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", true, dom) - testCheckJobDone(t, store, jobID, false) - - testDropTable(tk, t, "test", "t1", dom) -} - -func checkColumnKVExist(ctx sessionctx.Context, t table.Table, handle kv.Handle, col *table.Column, columnValue interface{}, isExist bool) error { - err := sessiontxn.NewTxn(context.Background(), ctx) - if err != nil { - return errors.Trace(err) - } - defer func() { - if txn, err1 := ctx.Txn(true); err1 == nil { - err = txn.Commit(context.Background()) - if err != nil { - panic(err) - } - } - }() - key := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) - txn, err := ctx.Txn(true) - if err != nil { - return errors.Trace(err) - } - data, err := txn.Get(context.TODO(), key) - if !isExist { - if terror.ErrorEqual(err, kv.ErrNotExist) { - return nil - } - } - if err != nil { - return errors.Trace(err) - } - colMap := make(map[int64]*types.FieldType) - colMap[col.ID] = &col.FieldType - rowMap, err := tablecodec.DecodeRowToDatumMap(data, colMap, ctx.GetSessionVars().Location()) - if err != nil { - return errors.Trace(err) - } - val, ok := rowMap[col.ID] - if isExist { - if !ok || val.GetValue() != columnValue { - return errors.Errorf("%v is not equal to %v", val.GetValue(), columnValue) - } - } else { - if ok { - return errors.Errorf("column value should not exists") - } - } - return nil -} - -func checkNoneColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, columnValue interface{}, dom *domain.Domain) { - tbl := testGetTable(t, dom, tableID) - err := checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) - require.NoError(t, err) - err = testGetColumn(tbl, col.Name.L, false) - require.NoError(t, err) -} - -func checkDeleteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { - tbl := testGetTable(t, dom, tableID) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - i := 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) - require.NoError(t, err) - // Test add a new row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - newRow := types.MakeDatums(int64(11), int64(22), int64(33)) - newHandle, err := tbl.AddRecord(ctx, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - rows := [][]types.Datum{row, newRow} - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 2, i, "expect 2, got %v", i) - - err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) - require.NoError(t, err) - // Test remove a row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - err = tbl.RemoveRecord(ctx, newHandle, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - i++ - return true, nil - }) - require.NoError(t, err) - - require.Equalf(t, 1, i, "expect 1, got %v", i) - err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false) - require.NoError(t, err) - err = testGetColumn(tbl, col.Name.L, false) - require.NoError(t, err) -} - -func checkWriteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { - tbl := testGetTable(t, dom, tableID) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i := 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - - err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) - require.NoError(t, err) - - // Test add a new row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - newRow := types.MakeDatums(int64(11), int64(22), int64(33)) - newHandle, err := tbl.AddRecord(ctx, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - rows := [][]types.Datum{row, newRow} - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 2, i, "expect 2, got %v", i) - - err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true) - require.NoError(t, err) - // Test remove a row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - err = tbl.RemoveRecord(ctx, newHandle, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - - err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false) - require.NoError(t, err) - err = testGetColumn(tbl, col.Name.L, false) - require.NoError(t, err) -} - -func checkReorganizationColumn(t *testing.T, ctx sessionctx.Context, tableID int64, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { - tbl := testGetTable(t, dom, tableID) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i := 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - - // Test add a new row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - newRow := types.MakeDatums(int64(11), int64(22), int64(33)) - newHandle, err := tbl.AddRecord(ctx, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - rows := [][]types.Datum{row, newRow} - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 2, i, "expect 2, got %v", i) - - err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true) - require.NoError(t, err) - - // Test remove a row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - err = tbl.RemoveRecord(ctx, newHandle, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - err = testGetColumn(tbl, col.Name.L, false) - require.NoError(t, err) -} - -func checkPublicColumn(t *testing.T, ctx sessionctx.Context, tableID int64, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, columnCnt int) { - tbl := testGetTable(t, dom, tableID) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i := 0 - var updatedRow []types.Datum - updatedRow = append(updatedRow, oldRow...) - for j := 0; j < columnCnt; j++ { - updatedRow = append(updatedRow, types.NewDatum(columnValue)) - } - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, updatedRow), "%v not equal to %v", data, updatedRow) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - - // Test add a new row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - newRow := types.MakeDatums(int64(11), int64(22), int64(33), int64(44)) - for j := 1; j < columnCnt; j++ { - newRow = append(newRow, types.NewDatum(int64(44))) - } - handle, err := tbl.AddRecord(ctx, newRow) - require.NoError(t, err) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - rows := [][]types.Datum{updatedRow, newRow} - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 2, i, "expect 2, got %v", i) - - // Test remove a row. - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - err = tbl.RemoveRecord(ctx, handle, newRow) - require.NoError(t, err) - - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - i = 0 - err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) - i++ - return true, nil - }) - require.NoError(t, err) - require.Equalf(t, 1, i, "expect 1, got %v", i) - - err = testGetColumn(tbl, newCol.Name.L, true) - require.NoError(t, err) -} - -func checkAddColumn(t *testing.T, state model.SchemaState, tableID int64, handle kv.Handle, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, store kv.Storage, columnCnt int) { - ctx := testNewContext(store) - switch state { - case model.StateNone: - checkNoneColumn(t, ctx, tableID, handle, newCol, columnValue, dom) - case model.StateDeleteOnly: - checkDeleteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom) - case model.StateWriteOnly: - checkWriteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom) - case model.StateWriteReorganization, model.StateDeleteReorganization: - checkReorganizationColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom) - case model.StatePublic: - checkPublicColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom, columnCnt) - } -} - -func testGetColumn(t table.Table, name string, isExist bool) error { - col := table.FindCol(t.Cols(), name) - if isExist { - if col == nil { - return errors.Errorf("column should not be nil") - } - } else { - if col != nil { - return errors.Errorf("column should be nil") - } - } - return nil -} - -func TestAddColumn(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) - - d := dom.DDL() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - tbl := testGetTable(t, dom, tableID) - - ctx := testNewContext(store) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - oldRow := types.MakeDatums(int64(1), int64(2), int64(3)) - handle, err := tbl.AddRecord(ctx, oldRow) - require.NoError(t, err) - - txn, err := ctx.Txn(true) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - newColName := "c4" - defaultColValue := int64(4) - - checkOK := false - - tc := &callback.TestDDLCallback{Do: dom} - onJobUpdatedExportedFunc := func(job *model.Job) { - if checkOK { - return - } - - tbl := testGetTable(t, dom, tableID) - newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName) - if newCol == nil { - return - } - - checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 1) - - if newCol.State == model.StatePublic { - checkOK = true - } - } - tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - d.SetHook(tc) - - jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColName, "", defaultColValue, dom) - testCheckJobDone(t, store, jobID, true) - - require.True(t, checkOK) - - jobID = testDropTable(tk, t, "test", "t1", dom) - testCheckJobDone(t, store, jobID, false) -} - -func TestAddColumns(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) - - d := dom.DDL() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") - - newColNames := []string{"c4", "c5", "c6"} - positions := make([]string, 3) - for i := range positions { - positions[i] = "" - } - defaultColValue := int64(4) - - var mu sync.Mutex - var hookErr error - checkOK := false - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - tbl := testGetTable(t, dom, tableID) - - ctx := testNewContext(store) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - oldRow := types.MakeDatums(int64(1), int64(2), int64(3)) - handle, err := tbl.AddRecord(ctx, oldRow) - require.NoError(t, err) - - txn, err := ctx.Txn(true) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - tc := &callback.TestDDLCallback{Do: dom} - onJobUpdatedExportedFunc := func(job *model.Job) { - mu.Lock() - defer mu.Unlock() - if checkOK { - return - } - - tbl := testGetTable(t, dom, tableID) - for _, newColName := range newColNames { - newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName) - if newCol == nil { - return - } - - checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 3) - - if newCol.State == model.StatePublic { - checkOK = true - } - } - } - tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - d.SetHook(tc) - - jobID := testCreateColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColNames, positions, defaultColValue, dom) - - testCheckJobDone(t, store, jobID, true) - mu.Lock() - hErr := hookErr - ok := checkOK - mu.Unlock() - require.NoError(t, hErr) - require.True(t, ok) - - jobID = testDropTable(tk, t, "test", "t1", dom) - testCheckJobDone(t, store, jobID, false) -} - -func TestDropColumnInColumnTest(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);") - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - tbl := testGetTable(t, dom, tableID) - - ctx := testNewContext(store) - colName := "c4" - defaultColValue := int64(4) - row := types.MakeDatums(int64(1), int64(2), int64(3)) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - _, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue))) - require.NoError(t, err) - - txn, err := ctx.Txn(true) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - checkOK := false - var hookErr error - var mu sync.Mutex - - d := dom.DDL() - tc := &callback.TestDDLCallback{Do: dom} - onJobUpdatedExportedFunc := func(job *model.Job) { - mu.Lock() - defer mu.Unlock() - if checkOK { - return - } - tbl := testGetTable(t, dom, tableID) - col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName) - if col == nil { - checkOK = true - return - } - } - tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - d.SetHook(tc) - - jobID := testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colName, false, dom) - testCheckJobDone(t, store, jobID, false) - mu.Lock() - hErr := hookErr - ok := checkOK - mu.Unlock() - require.NoError(t, hErr) - require.True(t, ok) - - jobID = testDropTable(tk, t, "test", "t1", dom) - testCheckJobDone(t, store, jobID, false) -} - -func TestDropColumns(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);") - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - tbl := testGetTable(t, dom, tableID) - - ctx := testNewContext(store) - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - colNames := []string{"c3", "c4"} - defaultColValue := int64(4) - row := types.MakeDatums(int64(1), int64(2), int64(3)) - _, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue))) - require.NoError(t, err) - - txn, err := ctx.Txn(true) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - checkOK := false - var hookErr error - var mu sync.Mutex - - d := dom.DDL() - tc := &callback.TestDDLCallback{Do: dom} - onJobUpdatedExportedFunc := func(job *model.Job) { - mu.Lock() - defer mu.Unlock() - if checkOK { - return - } - tbl := testGetTable(t, dom, tableID) - for _, colName := range colNames { - col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName) - if col == nil { - checkOK = true - return - } - } - } - tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - d.SetHook(tc) - - jobID := testDropColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colNames, false, dom) - testCheckJobDone(t, store, jobID, false) - mu.Lock() - hErr := hookErr - ok := checkOK - mu.Unlock() - require.NoError(t, hErr) - require.True(t, ok) - - jobID = testDropTable(tk, t, "test", "t1", dom) - testCheckJobDone(t, store, jobID, false) -} - -func testGetTable(t *testing.T, dom *domain.Domain, tableID int64) table.Table { - require.NoError(t, dom.Reload()) - tbl, exist := dom.InfoSchema().TableByID(tableID) - require.True(t, exist) - return tbl -} - -func TestWriteDataWriteOnlyMode(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk2.MustExec("use test") - tk.MustExec("CREATE TABLE t (`col1` bigint(20) DEFAULT 1,`col2` float,UNIQUE KEY `key1` (`col1`))") - - originalCallback := dom.DDL().GetHook() - defer dom.DDL().SetHook(originalCallback) - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateWriteOnly { - return - } - tk2.MustExec("insert ignore into t values (1, 2)") - tk2.MustExec("insert ignore into t values (2, 2)") - } - dom.DDL().SetHook(hook) - tk.MustExec("alter table t change column `col1` `col1` varchar(20)") - - hook = &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateWriteOnly { - return - } - tk2.MustExec("insert ignore into t values (1)") - tk2.MustExec("insert ignore into t values (2)") - } - dom.DDL().SetHook(hook) - tk.MustExec("alter table t drop column `col1`") - dom.DDL().SetHook(originalCallback) -} diff --git a/ddl/constant.go b/ddl/constant.go deleted file mode 100644 index f9750359eeca1..0000000000000 --- a/ddl/constant.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "github.com/pingcap/tidb/meta" -) - -const ( - // JobTable stores the information of DDL jobs. - JobTable = "tidb_ddl_job" - // ReorgTable stores the information of DDL reorganization. - ReorgTable = "tidb_ddl_reorg" - // HistoryTable stores the history DDL jobs. - HistoryTable = "tidb_ddl_history" - - // JobTableID is the table ID of `tidb_ddl_job`. - JobTableID = meta.MaxInt48 - 1 - // ReorgTableID is the table ID of `tidb_ddl_reorg`. - ReorgTableID = meta.MaxInt48 - 2 - // HistoryTableID is the table ID of `tidb_ddl_history`. - HistoryTableID = meta.MaxInt48 - 3 - // MDLTableID is the table ID of `tidb_mdl_info`. - MDLTableID = meta.MaxInt48 - 4 - // BackgroundSubtaskTableID is the table ID of `tidb_background_subtask`. - BackgroundSubtaskTableID = meta.MaxInt48 - 5 - // BackgroundSubtaskHistoryTableID is the table ID of `tidb_background_subtask_history`. - BackgroundSubtaskHistoryTableID = meta.MaxInt48 - 6 - - // JobTableSQL is the CREATE TABLE SQL of `tidb_ddl_job`. - JobTableSQL = "create table " + JobTable + "(job_id bigint not null, reorg int, schema_ids text(65535), table_ids text(65535), job_meta longblob, type int, processing int, primary key(job_id))" - // ReorgTableSQL is the CREATE TABLE SQL of `tidb_ddl_reorg`. - ReorgTableSQL = "create table " + ReorgTable + "(job_id bigint not null, ele_id bigint, ele_type blob, start_key blob, end_key blob, physical_id bigint, reorg_meta longblob, unique key(job_id, ele_id, ele_type(20)))" - // HistoryTableSQL is the CREATE TABLE SQL of `tidb_ddl_history`. - HistoryTableSQL = "create table " + HistoryTable + "(job_id bigint not null, job_meta longblob, db_name char(64), table_name char(64), schema_ids text(65535), table_ids text(65535), create_time datetime, primary key(job_id))" - // BackgroundSubtaskTableSQL is the CREATE TABLE SQL of `tidb_background_subtask`. - BackgroundSubtaskTableSQL = `create table tidb_background_subtask ( - id bigint not null auto_increment primary key, - step int, - namespace varchar(256), - task_key varchar(256), - ddl_physical_tid bigint(20), - type int, - exec_id varchar(256), - exec_expired timestamp, - state varchar(64) not null, - checkpoint longblob not null, - start_time bigint, - state_update_time bigint, - meta longblob, - error BLOB, - summary json, - key idx_task_key(task_key))` - // BackgroundSubtaskHistoryTableSQL is the CREATE TABLE SQL of `tidb_background_subtask_history`. - BackgroundSubtaskHistoryTableSQL = `create table tidb_background_subtask_history ( - id bigint not null auto_increment primary key, - step int, - namespace varchar(256), - task_key varchar(256), - ddl_physical_tid bigint(20), - type int, - exec_id varchar(256), - exec_expired timestamp, - state varchar(64) not null, - checkpoint longblob not null, - start_time bigint, - state_update_time bigint, - meta longblob, - error BLOB, - summary json, - key idx_task_key(task_key), - key idx_state_update_time(state_update_time))` -) diff --git a/ddl/constraint.go b/ddl/constraint.go deleted file mode 100644 index 6313f31540e79..0000000000000 --- a/ddl/constraint.go +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2023-2023 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/sqlexec" -) - -func (w *worker) onAddCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - // Handle the rolling back job. - if job.IsRollingback() { - ver, err = onDropCheckConstraint(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - return ver, nil - } - - failpoint.Inject("errorBeforeDecodeArgs", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(ver, errors.New("occur an error before decode args")) - } - }) - - dbInfo, tblInfo, constraintInfoInMeta, constraintInfoInJob, err := checkAddCheckConstraint(t, job) - if err != nil { - return ver, errors.Trace(err) - } - if constraintInfoInMeta == nil { - // It's first time to run add constraint job, so there is no constraint info in meta. - // Use the raw constraint info from job directly and modify table info here. - constraintInfoInJob.ID = allocateConstraintID(tblInfo) - // Reset constraint name according to real-time constraints name at this point. - constrNames := map[string]bool{} - for _, constr := range tblInfo.Constraints { - constrNames[constr.Name.L] = true - } - setNameForConstraintInfo(tblInfo.Name.L, constrNames, []*model.ConstraintInfo{constraintInfoInJob}) - // Double check the constraint dependency. - existedColsMap := make(map[string]struct{}) - cols := tblInfo.Columns - for _, v := range cols { - if v.State == model.StatePublic { - existedColsMap[v.Name.L] = struct{}{} - } - } - dependedCols := constraintInfoInJob.ConstraintCols - for _, k := range dependedCols { - if _, ok := existedColsMap[k.L]; !ok { - // The table constraint depended on a non-existed column. - return ver, dbterror.ErrTableCheckConstraintReferUnknown.GenWithStackByArgs(constraintInfoInJob.Name, k) - } - } - - tblInfo.Constraints = append(tblInfo.Constraints, constraintInfoInJob) - constraintInfoInMeta = constraintInfoInJob - } - - originalState := constraintInfoInMeta.State - switch constraintInfoInMeta.State { - case model.StateNone: - job.SchemaState = model.StateWriteOnly - constraintInfoInMeta.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) - case model.StateWriteOnly: - job.SchemaState = model.StateWriteReorganization - constraintInfoInMeta.State = model.StateWriteReorganization - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) - case model.StateWriteReorganization: - err = w.verifyRemainRecordsForCheckConstraint(dbInfo, tblInfo, constraintInfoInMeta, job) - if err != nil { - return ver, errors.Trace(err) - } - constraintInfoInMeta.State = model.StatePublic - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("constraint", constraintInfoInMeta.State) - } - - return ver, errors.Trace(err) -} - -func checkAddCheckConstraint(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ConstraintInfo, *model.ConstraintInfo, error) { - schemaID := job.SchemaID - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return nil, nil, nil, nil, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, nil, errors.Trace(err) - } - constraintInfo1 := &model.ConstraintInfo{} - err = job.DecodeArgs(constraintInfo1) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, nil, errors.Trace(err) - } - // do the double-check with constraint existence. - constraintInfo2 := tblInfo.FindConstraintInfoByName(constraintInfo1.Name.L) - if constraintInfo2 != nil { - if constraintInfo2.State == model.StatePublic { - // We already have a constraint with the same constraint name. - job.State = model.JobStateCancelled - return nil, nil, nil, nil, infoschema.ErrColumnExists.GenWithStackByArgs(constraintInfo1.Name) - } - // if not, that means constraint was in intermediate state. - } - return dbInfo, tblInfo, constraintInfo2, constraintInfo1, nil -} - -// onDropCheckConstraint can be called from two case: -// 1: rollback in add constraint.(in rollback function the job.args will be changed) -// 2: user drop constraint ddl. -func onDropCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, constraintInfo, err := checkDropCheckConstraint(t, job) - if err != nil { - return ver, errors.Trace(err) - } - - originalState := constraintInfo.State - switch constraintInfo.State { - case model.StatePublic: - job.SchemaState = model.StateWriteOnly - constraintInfo.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) - case model.StateWriteOnly: - // write only state constraint will still take effect to check the newly inserted data. - // So the dependent column shouldn't be dropped even in this intermediate state. - constraintInfo.State = model.StateNone - // remove the constraint from tableInfo. - for i, constr := range tblInfo.Constraints { - if constr.Name.L == constraintInfo.Name.L { - tblInfo.Constraints = append(tblInfo.Constraints[0:i], tblInfo.Constraints[i+1:]...) - } - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != constraintInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - if job.IsRollingback() { - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - } else { - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - } - default: - err = dbterror.ErrInvalidDDLJob.GenWithStackByArgs("constraint", tblInfo.State) - } - return ver, errors.Trace(err) -} - -func checkDropCheckConstraint(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ConstraintInfo, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, errors.Trace(err) - } - - var constrName model.CIStr - err = job.DecodeArgs(&constrName) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, errors.Trace(err) - } - - // double check with constraint existence. - constraintInfo := tblInfo.FindConstraintInfoByName(constrName.L) - if constraintInfo == nil { - job.State = model.JobStateCancelled - return nil, nil, dbterror.ErrConstraintNotFound.GenWithStackByArgs(constrName) - } - return tblInfo, constraintInfo, nil -} - -func (w *worker) onAlterCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - dbInfo, tblInfo, constraintInfo, enforced, err := checkAlterCheckConstraint(t, job) - if err != nil { - return ver, errors.Trace(err) - } - - // enforced will fetch table data and check the constraint. - if enforced { - originalState := constraintInfo.State - switch constraintInfo.State { - case model.StatePublic: - job.SchemaState = model.StateWriteReorganization - constraintInfo.State = model.StateWriteReorganization - constraintInfo.Enforced = enforced - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) - case model.StateWriteReorganization: - job.SchemaState = model.StateWriteOnly - constraintInfo.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) - case model.StateWriteOnly: - err = w.verifyRemainRecordsForCheckConstraint(dbInfo, tblInfo, constraintInfo, job) - if err != nil { - if !table.ErrCheckConstraintViolated.Equal(err) { - return ver, errors.Trace(err) - } - constraintInfo.Enforced = !enforced - } - constraintInfo.State = model.StatePublic - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - } - } else { - constraintInfo.Enforced = enforced - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) - if err != nil { - // update version and tableInfo error will cause retry. - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - } - return ver, err -} - -func checkAlterCheckConstraint(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ConstraintInfo, bool, error) { - schemaID := job.SchemaID - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return nil, nil, nil, false, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, false, errors.Trace(err) - } - - var ( - enforced bool - constrName model.CIStr - ) - err = job.DecodeArgs(&constrName, &enforced) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, false, errors.Trace(err) - } - // do the double check with constraint existence. - constraintInfo := tblInfo.FindConstraintInfoByName(constrName.L) - if constraintInfo == nil { - job.State = model.JobStateCancelled - return nil, nil, nil, false, dbterror.ErrConstraintNotFound.GenWithStackByArgs(constrName) - } - return dbInfo, tblInfo, constraintInfo, enforced, nil -} - -func allocateConstraintID(tblInfo *model.TableInfo) int64 { - tblInfo.MaxConstraintID++ - return tblInfo.MaxConstraintID -} - -func buildConstraintInfo(tblInfo *model.TableInfo, dependedCols []model.CIStr, constr *ast.Constraint, state model.SchemaState) (*model.ConstraintInfo, error) { - constraintName := model.NewCIStr(constr.Name) - if err := checkTooLongConstraint(constraintName); err != nil { - return nil, errors.Trace(err) - } - - // Restore check constraint expression to string. - var sb strings.Builder - restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | - format.RestoreSpacesAroundBinaryOperation - restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) - - sb.Reset() - err := constr.Expr.Restore(restoreCtx) - if err != nil { - return nil, errors.Trace(err) - } - - // Create constraint info. - constraintInfo := &model.ConstraintInfo{ - Name: constraintName, - Table: tblInfo.Name, - ConstraintCols: dependedCols, - ExprString: sb.String(), - Enforced: constr.Enforced, - InColumn: constr.InColumn, - State: state, - } - - return constraintInfo, nil -} - -func checkTooLongConstraint(constr model.CIStr) error { - if len(constr.L) > mysql.MaxConstraintIdentifierLen { - return dbterror.ErrTooLongIdent.GenWithStackByArgs(constr) - } - return nil -} - -// findDependentColsInExpr returns a set of string, which indicates -// the names of the columns that are dependent by exprNode. -func findDependentColsInExpr(expr ast.ExprNode) map[string]struct{} { - colNames := FindColumnNamesInExpr(expr) - colsMap := make(map[string]struct{}, len(colNames)) - for _, depCol := range colNames { - colsMap[depCol.Name.L] = struct{}{} - } - return colsMap -} - -func (w *worker) verifyRemainRecordsForCheckConstraint(dbInfo *model.DBInfo, tableInfo *model.TableInfo, constr *model.ConstraintInfo, job *model.Job) error { - // Inject a fail-point to skip the remaining records check. - failpoint.Inject("mockVerifyRemainDataSuccess", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(nil) - } - }) - // Get sessionctx from ddl context resource pool in ddl worker. - var sctx sessionctx.Context - sctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(sctx) - - // If there is any row can't pass the check expression, the add constraint action will error. - // It's no need to construct expression node out and pull the chunk rows through it. Here we - // can let the check expression restored string as the filter in where clause directly. - // Prepare internal SQL to fetch data from physical table under this filter. - sql := fmt.Sprintf("select 1 from `%s`.`%s` where not %s limit 1", dbInfo.Name.L, tableInfo.Name.L, constr.ExprString) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, sql) - if err != nil { - return errors.Trace(err) - } - rowCount := len(rows) - if rowCount != 0 { - // If check constraint fail, the job state should be changed to canceled, otherwise it will tracked in. - job.State = model.JobStateCancelled - return dbterror.ErrCheckConstraintIsViolated.GenWithStackByArgs(constr.Name.L) - } - return nil -} - -func setNameForConstraintInfo(tableLowerName string, namesMap map[string]bool, infos []*model.ConstraintInfo) { - cnt := 1 - constraintPrefix := tableLowerName + "_chk_" - for _, constrInfo := range infos { - if constrInfo.Name.O == "" { - constrName := fmt.Sprintf("%s%d", constraintPrefix, cnt) - for { - // loop until find constrName that haven't been used. - if !namesMap[constrName] { - namesMap[constrName] = true - break - } - cnt++ - constrName = fmt.Sprintf("%s%d", constraintPrefix, cnt) - } - constrInfo.Name = model.NewCIStr(constrName) - } - } -} - -// IsColumnDroppableWithCheckConstraint check whether the column in check-constraint whose dependent col is more than 1 -func IsColumnDroppableWithCheckConstraint(col model.CIStr, tblInfo *model.TableInfo) error { - for _, cons := range tblInfo.Constraints { - if len(cons.ConstraintCols) > 1 { - for _, colName := range cons.ConstraintCols { - if colName.L == col.L { - return dbterror.ErrCantDropColWithCheckConstraint.GenWithStackByArgs(cons.Name, col) - } - } - } - } - return nil -} - -// IsColumnRenameableWithCheckConstraint check whether the column is referenced in check-constraint -func IsColumnRenameableWithCheckConstraint(col model.CIStr, tblInfo *model.TableInfo) error { - for _, cons := range tblInfo.Constraints { - for _, colName := range cons.ConstraintCols { - if colName.L == col.L { - return dbterror.ErrCantDropColWithCheckConstraint.GenWithStackByArgs(cons.Name, col) - } - } - } - return nil -} diff --git a/ddl/copr/BUILD.bazel b/ddl/copr/BUILD.bazel deleted file mode 100644 index 2cd6c6fbcd089..0000000000000 --- a/ddl/copr/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "copr", - srcs = ["copr_ctx.go"], - importpath = "github.com/pingcap/tidb/ddl/copr", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//parser/model", - "//sessionctx", - "//table/tables", - "//types", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "copr_test", - timeout = "short", - srcs = ["copr_ctx_test.go"], - embed = [":copr"], - flaky = True, - shard_count = 3, - deps = [ - "//expression", - "//parser/model", - "//parser/mysql", - "//types", - "//util/mock", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/db_test.go b/ddl/db_test.go deleted file mode 100644 index bb4fd2e475651..0000000000000 --- a/ddl/db_test.go +++ /dev/null @@ -1,1092 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "math" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - parsertypes "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" -) - -const ( - // waitForCleanDataRound indicates how many times should we check data is cleaned or not. - waitForCleanDataRound = 150 - // waitForCleanDataInterval is a min duration between 2 check for data clean. - waitForCleanDataInterval = time.Millisecond * 100 -) - -const defaultBatchSize = 1024 - -const dbTestLease = 600 * time.Millisecond - -func TestGetTimeZone(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - testCases := []struct { - tzSQL string - tzStr string - tzName string - offset int - err string - }{ - {"set time_zone = '+00:00'", "", "", 0, ""}, - {"set time_zone = '-00:00'", "", "", 0, ""}, - {"set time_zone = 'UTC'", "UTC", "UTC", 0, ""}, - {"set time_zone = '+05:00'", "", "", 18000, ""}, - {"set time_zone = '-08:00'", "", "", -28800, ""}, - {"set time_zone = '+08:00'", "", "", 28800, ""}, - {"set time_zone = 'Asia/Shanghai'", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, - {"set time_zone = 'SYSTEM'", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, - {"set time_zone = DEFAULT", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, - {"set time_zone = 'GMT'", "GMT", "GMT", 0, ""}, - {"set time_zone = 'GMT+1'", "GMT", "GMT", 0, "[variable:1298]Unknown or incorrect time zone: 'GMT+1'"}, - {"set time_zone = 'Etc/GMT+12'", "Etc/GMT+12", "Etc/GMT+12", 0, ""}, - {"set time_zone = 'Etc/GMT-12'", "Etc/GMT-12", "Etc/GMT-12", 0, ""}, - {"set time_zone = 'EST'", "EST", "EST", 0, ""}, - {"set time_zone = 'Australia/Lord_Howe'", "Australia/Lord_Howe", "Australia/Lord_Howe", 0, ""}, - } - for _, tc := range testCases { - if tc.err != "" { - tk.MustGetErrMsg(tc.tzSQL, tc.err) - } else { - tk.MustExec(tc.tzSQL) - } - require.Equal(t, tc.tzStr, tk.Session().GetSessionVars().TimeZone.String(), fmt.Sprintf("sql: %s", tc.tzSQL)) - tz, offset := ddlutil.GetTimeZone(tk.Session()) - require.Equal(t, tz, tc.tzName, fmt.Sprintf("sql: %s, offset: %d", tc.tzSQL, offset)) - require.Equal(t, offset, tc.offset, fmt.Sprintf("sql: %s", tc.tzSQL)) - } -} - -func TestIssue22819(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("set global tidb_enable_metadata_lock=0") - tk1.MustExec("use test;") - tk1.MustExec("create table t1 (v int) partition by hash (v) partitions 2") - tk1.MustExec("insert into t1 values (1)") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test;") - tk1.MustExec("begin") - tk1.MustExec("update t1 set v = 2 where v = 1") - - tk2.MustExec("alter table t1 truncate partition p0") - - err := tk1.ExecToErr("commit") - require.Error(t, err) - require.Regexp(t, ".*8028.*Information schema is changed during the execution of the statement.*", err.Error()) -} - -func TestIssue22307(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values(1, 1);") - - hook := &callback.TestDDLCallback{Do: dom} - var checkErr1, checkErr2 error - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateWriteOnly { - return - } - _, checkErr1 = tk.Exec("update t set a = 3 where b = 1;") - _, checkErr2 = tk.Exec("update t set a = 3 order by b;") - } - dom.DDL().SetHook(hook) - done := make(chan error, 1) - // test transaction on add column. - go backgroundExec(store, "test", "alter table t drop column b;", done) - err := <-done - require.NoError(t, err) - require.EqualError(t, checkErr1, "[planner:1054]Unknown column 'b' in 'where clause'") - require.EqualError(t, checkErr2, "[planner:1054]Unknown column 'b' in 'order clause'") -} - -func TestIssue23473(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_23473;") - tk.MustExec("create table t_23473 (k int primary key, v int)") - tk.MustExec("alter table t_23473 change column k k bigint") - - tbl := external.GetTableByName(t, tk, "test", "t_23473") - require.True(t, mysql.HasNoDefaultValueFlag(tbl.Cols()[0].GetFlag())) -} - -func TestAutoConvertBlobTypeByLength(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - sql := fmt.Sprintf("create table t0(c0 Blob(%d), c1 Blob(%d), c2 Blob(%d), c3 Blob(%d))", - 255-1, 65535-1, 16777215-1, 4294967295-1) - tk.MustExec(sql) - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t0' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - - tbl, exist := dom.InfoSchema().TableByID(tableID) - require.True(t, exist) - - require.Equal(t, tbl.Cols()[0].GetType(), mysql.TypeTinyBlob) - require.Equal(t, tbl.Cols()[0].GetFlen(), 255) - require.Equal(t, tbl.Cols()[1].GetType(), mysql.TypeBlob) - require.Equal(t, tbl.Cols()[1].GetFlen(), 65535) - require.Equal(t, tbl.Cols()[2].GetType(), mysql.TypeMediumBlob) - require.Equal(t, tbl.Cols()[2].GetFlen(), 16777215) - require.Equal(t, tbl.Cols()[3].GetType(), mysql.TypeLongBlob) - require.Equal(t, tbl.Cols()[3].GetFlen(), 4294967295) -} - -func TestAddExpressionIndexRollback(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") - tk.MustExec("insert into t1 values (20, 20, 20), (40, 40, 40), (80, 80, 80), (160, 160, 160);") - - var checkErr error - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - - d := dom.DDL() - hook := &callback.TestDDLCallback{Do: dom} - var currJob *model.Job - ctx := mock.NewContext() - ctx.Store = store - times := 0 - onJobUpdatedExportedFunc := func(job *model.Job) { - if checkErr != nil { - return - } - switch job.SchemaState { - case model.StateDeleteOnly: - _, checkErr = tk1.Exec("insert into t1 values (6, 3, 3) on duplicate key update c1 = 10") - if checkErr == nil { - _, checkErr = tk1.Exec("update t1 set c1 = 7 where c2=6;") - } - if checkErr == nil { - _, checkErr = tk1.Exec("delete from t1 where c1 = 40;") - } - case model.StateWriteOnly: - _, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)") - if checkErr == nil { - _, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80") - } - case model.StateWriteReorganization: - if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { - _, checkErr = tk1.Exec("insert into t1 values (4, 4, 4)") - if checkErr != nil { - return - } - _, checkErr = tk1.Exec("update t1 set c1 = 5 where c2 = 80") - if checkErr != nil { - return - } - currJob = job - times++ - } - } - } - hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - d.SetHook(hook) - - tk.MustGetErrMsg("alter table t1 add index expr_idx ((pow(c1, c2)));", "[types:1690]DOUBLE value is out of range in 'pow(160, 160)'") - require.NoError(t, checkErr) - tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows("2 2 2", "4 4 4", "5 80 80", "10 3 3", "20 20 20", "160 160 160")) - - // Check whether the reorg information is cleaned up. - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - element, start, end, physicalID, err := ddl.NewReorgHandlerForTest(testkit.NewTestKit(t, store).Session()).GetDDLReorgHandle(currJob) - require.True(t, meta.ErrDDLReorgElementNotExist.Equal(err)) - require.Nil(t, element) - require.Nil(t, start) - require.Nil(t, end) - require.Equal(t, int64(0), physicalID) -} - -func TestDropTableOnTiKVDiskFull(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table test_disk_full_drop_table(a int);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull")) - }() - tk.MustExec("drop table test_disk_full_drop_table;") -} - -func TestRebaseAutoID(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) }() - - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("drop database if exists tidb;") - tk.MustExec("create database tidb;") - tk.MustExec("use tidb;") - tk.MustExec("create table tidb.test (a int auto_increment primary key, b int);") - tk.MustExec("insert tidb.test values (null, 1);") - tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1")) - tk.MustExec("alter table tidb.test auto_increment = 6000;") - tk.MustExec("insert tidb.test values (null, 1);") - tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1")) - tk.MustExec("alter table tidb.test auto_increment = 5;") - tk.MustExec("insert tidb.test values (null, 1);") - tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1", "11000 1")) - - // Current range for table test is [11000, 15999]. - // Though it does not have a tuple "a = 15999", its global next auto increment id should be 16000. - // Anyway it is not compatible with MySQL. - tk.MustExec("alter table tidb.test auto_increment = 12000;") - tk.MustExec("insert tidb.test values (null, 1);") - tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1", "11000 1", "16000 1")) - - tk.MustExec("create table tidb.test2 (a int);") - tk.MustGetErrCode("alter table tidb.test2 add column b int auto_increment key, auto_increment=10;", errno.ErrUnsupportedDDLOperation) -} - -func TestProcessColumnFlags(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // check `processColumnFlags()` - tk.MustExec("create table t(a year(4) comment 'xxx', b year, c bit)") - defer tk.MustExec("drop table t;") - - check := func(n string, f func(uint) bool) { - tbl := external.GetTableByName(t, tk, "test", "t") - for _, col := range tbl.Cols() { - if strings.EqualFold(col.Name.L, n) { - require.True(t, f(col.GetFlag())) - break - } - } - } - - yearcheck := func(f uint) bool { - return mysql.HasUnsignedFlag(f) && mysql.HasZerofillFlag(f) && !mysql.HasBinaryFlag(f) - } - - tk.MustExec("alter table t modify a year(4)") - check("a", yearcheck) - - tk.MustExec("alter table t modify a year(4) unsigned") - check("a", yearcheck) - - tk.MustExec("alter table t modify a year(4) zerofill") - - tk.MustExec("alter table t modify b year") - check("b", yearcheck) - - tk.MustExec("alter table t modify c bit") - check("c", func(f uint) bool { - return mysql.HasUnsignedFlag(f) && !mysql.HasBinaryFlag(f) - }) -} - -func TestForbidCacheTableForSystemTable(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - sysTables := make([]string, 0, 24) - memOrSysDB := []string{"MySQL", "INFORMATION_SCHEMA", "PERFORMANCE_SCHEMA", "METRICS_SCHEMA"} - for _, db := range memOrSysDB { - tk.MustExec("use " + db) - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) - rows := tk.MustQuery("show tables").Rows() - for i := 0; i < len(rows); i++ { - sysTables = append(sysTables, rows[i][0].(string)) - } - for _, one := range sysTables { - err := tk.ExecToErr(fmt.Sprintf("alter table `%s` cache", one)) - if db == "MySQL" { - if one == "tidb_mdl_view" { - require.EqualError(t, err, "[ddl:1347]'MySQL.tidb_mdl_view' is not BASE TABLE") - } else { - require.EqualError(t, err, "[ddl:8200]ALTER table cache for tables in system database is currently unsupported") - } - } else { - require.EqualError(t, err, fmt.Sprintf("[planner:1142]ALTER command denied to user 'root'@'%%' for table '%s'", strings.ToLower(one))) - } - } - sysTables = sysTables[:0] - } -} - -func TestAlterShardRowIDBits(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - // Test alter shard_row_id_bits - tk.MustExec("create table t1 (a int) shard_row_id_bits = 5") - tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) - tk.MustExec("insert into t1 set a=1;") - - // Test increase shard_row_id_bits failed by overflow global auto ID. - tk.MustGetErrMsg("alter table t1 SHARD_ROW_ID_BITS = 10;", "[autoid:1467]shard_row_id_bits 10 will cause next global auto ID 72057594037932936 overflow") - - // Test reduce shard_row_id_bits will be ok. - tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 3;") - checkShardRowID := func(maxShardRowIDBits, shardRowIDBits uint64) { - tbl := external.GetTableByName(t, tk, "test", "t1") - require.True(t, tbl.Meta().MaxShardRowIDBits == maxShardRowIDBits) - require.True(t, tbl.Meta().ShardRowIDBits == shardRowIDBits) - } - checkShardRowID(5, 3) - - // Test reduce shard_row_id_bits but calculate overflow should use the max record shard_row_id_bits. - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int) shard_row_id_bits = 10") - tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 5;") - checkShardRowID(10, 5) - tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) - tk.MustGetErrMsg("insert into t1 set a=1;", "[autoid:1467]Failed to read auto-increment value from storage engine") -} - -func TestDDLJobErrorCount(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists ddl_error_table, new_ddl_error_table") - tk.MustExec("create table ddl_error_table(a int)") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockErrEntrySizeTooLarge", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockErrEntrySizeTooLarge")) - }() - - var jobID int64 - hook := &callback.TestDDLCallback{} - onJobUpdatedExportedFunc := func(job *model.Job) { - jobID = job.ID - } - hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - originHook := dom.DDL().GetHook() - dom.DDL().SetHook(hook) - defer dom.DDL().SetHook(originHook) - - tk.MustGetErrCode("rename table ddl_error_table to new_ddl_error_table", errno.ErrEntryTooLarge) - - historyJob, err := ddl.GetHistoryJobByID(tk.Session(), jobID) - require.NoError(t, err) - require.NotNil(t, historyJob) - require.Equal(t, int64(1), historyJob.ErrorCount) - require.True(t, kv.ErrEntryTooLarge.Equal(historyJob.Error)) - tk.MustQuery("select * from ddl_error_table;").Check(testkit.Rows()) -} - -// TestAddIndexFailOnCaseWhenCanExit is used to close #19325. -func TestAddIndexFailOnCaseWhenCanExit(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockCaseWhenParseFailure", `return(true)`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockCaseWhenParseFailure")) }() - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - originalVal := variable.GetDDLErrorCountLimit() - tk.MustExec("set @@global.tidb_ddl_error_count_limit = 1") - defer tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_error_count_limit = %d", originalVal)) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1, 1)") - tk.MustGetErrMsg("alter table t add index idx(b)", "[ddl:-1]job.ErrCount:0, mock unknown type: ast.whenClause.") - tk.MustExec("drop table if exists t") -} - -func TestCreateTableWithIntegerLengthWarning(t *testing.T) { - // Inject the strict-integer-display-width variable in parser directly. - parsertypes.TiDBStrictIntegerDisplayWidth = true - defer func() { parsertypes.TiDBStrictIntegerDisplayWidth = false }() - store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - - tk.MustExec("create table t(a tinyint(1))") - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a smallint(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a mediumint(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a bigint(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a integer(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int1(1))") // Note that int1(1) is tinyint(1) which is boolean-ish - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int2(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int3(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int4(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int8(2))") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) - - tk.MustExec("drop table if exists t") -} -func TestShowCountWarningsOrErrors(t *testing.T) { - // Inject the strict-integer-display-width variable in parser directly. - parsertypes.TiDBStrictIntegerDisplayWidth = true - defer func() { parsertypes.TiDBStrictIntegerDisplayWidth = false }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // test sql run work - tk.MustExec("show count(*) warnings") - tk.MustExec("show count(*) errors") - - // test count warnings - tk.MustExec("drop table if exists t1,t2,t3") - // Warning: Integer display width is deprecated and will be removed in a future release. - tk.MustExec("create table t(a int8(2));" + - "create table t1(a int4(2));" + - "create table t2(a int4(2));") - tk.MustQuery("show count(*) warnings").Check(tk.MustQuery("select @@session.warning_count").Rows()) - - // test count errors - tk.MustExec("drop table if exists show_errors") - tk.MustExec("create table show_errors (a int)") - // Error: Table exist - _, _ = tk.Exec("create table show_errors (a int)") - tk.MustQuery("show count(*) errors").Check(tk.MustQuery("select @@session.error_count").Rows()) -} - -// Close issue #24172. -// See https://github.com/pingcap/tidb/issues/24172 -func TestCancelJobWriteConflict(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - - tk1.MustExec("create table t(id int)") - - var cancelErr error - var rs []sqlexec.RecordSet - hook := &callback.TestDDLCallback{Do: dom} - d := dom.DDL() - originalHook := d.GetHook() - d.SetHook(hook) - defer d.SetHook(originalHook) - - // Test when cancelling cannot be retried and adding index succeeds. - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/mockCommitErrorInNewTxn", `return("no_retry")`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/kv/mockCommitErrorInNewTxn")) }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFailedCommandOnConcurencyDDL", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFailedCommandOnConcurencyDDL")) - }() - - stmt := fmt.Sprintf("admin cancel ddl jobs %d", job.ID) - rs, cancelErr = tk2.Session().Execute(context.Background(), stmt) - } - } - tk1.MustExec("alter table t add index (id)") - require.EqualError(t, cancelErr, "mock failed admin command on ddl jobs") - - // Test when cancelling is retried only once and adding index is cancelled in the end. - var jobID int64 - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { - jobID = job.ID - stmt := fmt.Sprintf("admin cancel ddl jobs %d", job.ID) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/mockCommitErrorInNewTxn", `return("retry_once")`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/kv/mockCommitErrorInNewTxn")) }() - rs, cancelErr = tk2.Session().Execute(context.Background(), stmt) - } - } - tk1.MustGetErrCode("alter table t add index (id)", errno.ErrCancelledDDLJob) - require.NoError(t, cancelErr) - result := tk2.ResultSetToResultWithCtx(context.Background(), rs[0], "cancel ddl job fails") - result.Check(testkit.Rows(fmt.Sprintf("%d successful", jobID))) -} - -func TestTxnSavepointWithDDL(t *testing.T) { - store, _ := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("set global tidb_enable_metadata_lock=0") - tk2.MustExec("use test;") - - prepareFn := func() { - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (c1 int primary key, c2 int)") - tk.MustExec("create table t2 (c1 int primary key, c2 int)") - } - prepareFn() - - tk.MustExec("begin pessimistic") - tk.MustExec("savepoint s1") - tk.MustExec("insert t1 values (1, 11)") - tk.MustExec("rollback to s1") - tk2.MustExec("alter table t1 add index idx2(c2)") - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustExec("admin check table t1") - - tk.MustExec("begin pessimistic") - tk.MustExec("savepoint s1") - tk.MustExec("insert t1 values (1, 11)") - tk.MustExec("savepoint s2") - tk.MustExec("insert t2 values (1, 11)") - tk.MustExec("rollback to s2") - tk2.MustExec("alter table t2 add index idx2(c2)") - tk.MustExec("commit") - tk.MustQuery("select * from t2").Check(testkit.Rows()) - tk.MustExec("admin check table t1, t2") - - prepareFn() - tk.MustExec("truncate table t1") - tk.MustExec("begin pessimistic") - tk.MustExec("savepoint s1") - tk.MustExec("insert t1 values (1, 11)") - tk.MustExec("savepoint s2") - tk.MustExec("insert t2 values (1, 11)") - tk.MustExec("rollback to s2") - tk2.MustExec("alter table t1 add index idx2(c2)") - tk2.MustExec("alter table t2 add index idx2(c2)") - err := tk.ExecToErr("commit") - require.Error(t, err) - require.Regexp(t, ".*8028.*Information schema is changed during the execution of the statement.*", err.Error()) - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustExec("admin check table t1, t2") -} - -func TestSnapshotVersion(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - - dd := dom.DDL() - ddl.DisableTiFlashPoll(dd) - require.Equal(t, dbTestLease, dd.GetLease()) - - snapTS := oracle.GoTimeToTS(time.Now()) - tk.MustExec("create database test2") - tk.MustExec("use test2") - tk.MustExec("create table t(a int)") - - is := dom.InfoSchema() - require.NotNil(t, is) - - // For updating the self schema version. - goCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) - err := dd.SchemaSyncer().OwnerCheckAllVersions(goCtx, 0, is.SchemaMetaVersion()) - cancel() - require.NoError(t, err) - - snapIs, err := dom.GetSnapshotInfoSchema(snapTS) - require.NotNil(t, snapIs) - require.NoError(t, err) - - // Make sure that the self schema version doesn't be changed. - goCtx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond) - err = dd.SchemaSyncer().OwnerCheckAllVersions(goCtx, 0, is.SchemaMetaVersion()) - cancel() - require.NoError(t, err) - - // for GetSnapshotInfoSchema - currSnapTS := oracle.GoTimeToTS(time.Now()) - currSnapIs, err := dom.GetSnapshotInfoSchema(currSnapTS) - require.NoError(t, err) - require.NotNil(t, currSnapTS) - require.Equal(t, is.SchemaMetaVersion(), currSnapIs.SchemaMetaVersion()) - - // for GetSnapshotMeta - dbInfo, ok := currSnapIs.SchemaByName(model.NewCIStr("test2")) - require.True(t, ok) - - tbl, err := currSnapIs.TableByName(model.NewCIStr("test2"), model.NewCIStr("t")) - require.NoError(t, err) - - m, err := dom.GetSnapshotMeta(snapTS) - require.NoError(t, err) - - tblInfo1, err := m.GetTable(dbInfo.ID, tbl.Meta().ID) - require.True(t, meta.ErrDBNotExists.Equal(err)) - require.Nil(t, tblInfo1) - - m, err = dom.GetSnapshotMeta(currSnapTS) - require.NoError(t, err) - - tblInfo2, err := m.GetTable(dbInfo.ID, tbl.Meta().ID) - require.NoError(t, err) - require.Equal(t, tblInfo2, tbl.Meta()) -} - -func TestSchemaValidator(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - tk := testkit.NewTestKit(t, store) - - dd := dom.DDL() - ddl.DisableTiFlashPoll(dd) - require.Equal(t, dbTestLease, dd.GetLease()) - - tk.MustExec("create table test.t(a int)") - - err := dom.Reload() - require.NoError(t, err) - schemaVer := dom.InfoSchema().SchemaMetaVersion() - ver, err := store.CurrentVersion(kv.GlobalTxnScope) - require.NoError(t, err) - - ts := ver.Ver - _, res := dom.SchemaValidator.Check(ts, schemaVer, nil, true) - require.Equal(t, domain.ResultSucc, res) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed", `return(true)`)) - - err = dom.Reload() - require.Error(t, err) - _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) - require.Equal(t, domain.ResultSucc, res) - time.Sleep(dbTestLease) - - ver, err = store.CurrentVersion(kv.GlobalTxnScope) - require.NoError(t, err) - ts = ver.Ver - _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) - require.Equal(t, domain.ResultUnknown, res) - - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed")) - err = dom.Reload() - require.NoError(t, err) - - _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) - require.Equal(t, domain.ResultSucc, res) - - // For schema check, it tests for getting the result of "ResultUnknown". - is := dom.InfoSchema() - schemaChecker := domain.NewSchemaChecker(dom, is.SchemaMetaVersion(), nil, true) - // Make sure it will retry one time and doesn't take a long time. - domain.SchemaOutOfDateRetryTimes.Store(1) - domain.SchemaOutOfDateRetryInterval.Store(time.Millisecond * 1) - dom.SchemaValidator.Stop() - _, err = schemaChecker.Check(uint64(123456)) - require.EqualError(t, err, domain.ErrInfoSchemaExpired.Error()) -} - -func TestLogAndShowSlowLog(t *testing.T) { - _, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "aaa", Duration: time.Second, Internal: true}) - dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "bbb", Duration: 3 * time.Second, SessAlias: "alias1"}) - dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "ccc", Duration: 2 * time.Second}) - // Collecting slow queries is asynchronous, wait a while to ensure it's done. - time.Sleep(5 * time.Millisecond) - - result := dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2}) - require.Len(t, result, 2) - require.Equal(t, "bbb", result[0].SQL) - require.Equal(t, "alias1", result[0].SessAlias) - require.Equal(t, 3*time.Second, result[0].Duration) - require.Equal(t, "ccc", result[1].SQL) - require.Equal(t, 2*time.Second, result[1].Duration) - require.Empty(t, result[1].SessAlias) - - result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2, Kind: ast.ShowSlowKindInternal}) - require.Len(t, result, 1) - require.Equal(t, "aaa", result[0].SQL) - require.Equal(t, time.Second, result[0].Duration) - require.True(t, result[0].Internal) - - result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 4, Kind: ast.ShowSlowKindAll}) - require.Len(t, result, 3) - require.Equal(t, "bbb", result[0].SQL) - require.Equal(t, 3*time.Second, result[0].Duration) - require.Equal(t, "alias1", result[0].SessAlias) - require.Equal(t, "ccc", result[1].SQL) - require.Equal(t, 2*time.Second, result[1].Duration) - require.Empty(t, result[1].SessAlias) - require.Equal(t, "aaa", result[2].SQL) - require.Equal(t, time.Second, result[2].Duration) - require.True(t, result[2].Internal) - require.Empty(t, result[2].SessAlias) - - result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowRecent, Count: 2}) - require.Len(t, result, 2) - require.Equal(t, "ccc", result[0].SQL) - require.Equal(t, 2*time.Second, result[0].Duration) - require.Empty(t, result[0].SessAlias) - require.Equal(t, "bbb", result[1].SQL) - require.Equal(t, 3*time.Second, result[1].Duration) - require.Equal(t, "alias1", result[1].SessAlias) -} - -func TestReportingMinStartTimestamp(t *testing.T) { - _, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) - - infoSyncer := dom.InfoSyncer() - sm := &testkit.MockSessionManager{ - PS: make([]*util.ProcessInfo, 0), - } - infoSyncer.SetSessionManager(sm) - beforeTS := oracle.GoTimeToTS(time.Now()) - infoSyncer.ReportMinStartTS(dom.Store()) - afterTS := oracle.GoTimeToTS(time.Now()) - require.False(t, infoSyncer.GetMinStartTS() > beforeTS && infoSyncer.GetMinStartTS() < afterTS) - - now := time.Now() - validTS := oracle.GoTimeToLowerLimitStartTS(now.Add(time.Minute), tikv.MaxTxnTimeUse) - lowerLimit := oracle.GoTimeToLowerLimitStartTS(now, tikv.MaxTxnTimeUse) - sm.PS = []*util.ProcessInfo{ - {CurTxnStartTS: 0}, - {CurTxnStartTS: math.MaxUint64}, - {CurTxnStartTS: lowerLimit}, - {CurTxnStartTS: validTS}, - } - infoSyncer.SetSessionManager(sm) - infoSyncer.ReportMinStartTS(dom.Store()) - require.Equal(t, validTS, infoSyncer.GetMinStartTS()) -} - -// for issue #34931 -func TestBuildMaxLengthIndexWithNonRestrictedSqlMode(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - maxIndexLength := config.GetGlobalConfig().MaxIndexLength - - tt := []struct { - ColType string - SpecifiedColLen bool - SpecifiedIndexLen bool - }{ - { - "text", - false, - true, - }, - { - "blob", - false, - true, - }, - { - "varchar", - true, - false, - }, - { - "varbinary", - true, - false, - }, - } - - sqlTemplate := "create table %s (id int, name %s, age int, %s index(name%s%s)) charset=%s;" - // test character strings for varchar and text - for _, tc := range tt { - for _, cs := range charset.CharacterSetInfos { - tableName := fmt.Sprintf("t_%s", cs.Name) - tk.MustExec(fmt.Sprintf("drop table if exists %s", tableName)) - tk.MustExec("set @@sql_mode=default") - - // test in strict sql mode - maxLen := cs.Maxlen - if tc.ColType == "varbinary" || tc.ColType == "blob" { - maxLen = 1 - } - expectKeyLength := maxIndexLength / maxLen - length := 2 * expectKeyLength - - indexLen := "" - // specify index length for text type - if tc.SpecifiedIndexLen { - indexLen = fmt.Sprintf("(%d)", length) - } - - col := tc.ColType - // specify column length for varchar type - if tc.SpecifiedColLen { - col += fmt.Sprintf("(%d)", length) - } - sql := fmt.Sprintf(sqlTemplate, - tableName, col, "", indexLen, "", cs.Name) - tk.MustGetErrCode(sql, errno.ErrTooLongKey) - - tk.MustExec("set @@sql_mode=''") - - err := tk.ExecToErr(sql) - require.NoErrorf(t, err, "exec sql '%s' failed", sql) - - require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - - warnErr := tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err - tErr := errors.Cause(warnErr).(*terror.Error) - sqlErr := terror.ToSQLError(tErr) - require.Equal(t, errno.ErrTooLongKey, int(sqlErr.Code)) - - if cs.Name == charset.CharsetBin { - if tc.ColType == "varchar" || tc.ColType == "varbinary" { - col = fmt.Sprintf("varbinary(%d)", length) - } else { - col = "blob" - } - } - rows := fmt.Sprintf("%s CREATE TABLE `%s` (\n `id` int(11) DEFAULT NULL,\n `name` %s DEFAULT NULL,\n `age` int(11) DEFAULT NULL,\n KEY `name` (`name`(%d))\n) ENGINE=InnoDB DEFAULT CHARSET=%s", - tableName, tableName, col, expectKeyLength, cs.Name) - // add collation for binary charset - if cs.Name != charset.CharsetBin { - rows += fmt.Sprintf(" COLLATE=%s", cs.DefaultCollation) - } - - tk.MustQuery(fmt.Sprintf("show create table %s", tableName)).Check(testkit.Rows(rows)) - - ukTable := fmt.Sprintf("t_%s_uk", cs.Name) - mkTable := fmt.Sprintf("t_%s_mk", cs.Name) - tk.MustExec(fmt.Sprintf("drop table if exists %s", ukTable)) - tk.MustExec(fmt.Sprintf("drop table if exists %s", mkTable)) - - // For a unique index, an error occurs regardless of SQL mode because reducing - //the index length might enable insertion of non-unique entries that do not meet - //the specified uniqueness requirement. - sql = fmt.Sprintf(sqlTemplate, ukTable, col, "unique", indexLen, "", cs.Name) - tk.MustGetErrCode(sql, errno.ErrTooLongKey) - - // The multiple column index in which the length sum exceeds the maximum size - // will return an error instead produce a warning in strict sql mode. - indexLen = fmt.Sprintf("(%d)", expectKeyLength) - sql = fmt.Sprintf(sqlTemplate, mkTable, col, "", indexLen, ", age", cs.Name) - tk.MustGetErrCode(sql, errno.ErrTooLongKey) - } - } -} - -func TestTiDBDownBeforeUpdateGlobalVersion(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDownBeforeUpdateGlobalVersion", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/checkDownBeforeUpdateGlobalVersion", `return(true)`)) - tk.MustExec("alter table t add column b int") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDownBeforeUpdateGlobalVersion")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/checkDownBeforeUpdateGlobalVersion")) -} - -func TestDDLBlockedCreateView(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - - hook := &callback.TestDDLCallback{Do: dom} - first := true - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateWriteOnly { - return - } - if !first { - return - } - first = false - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("create view v as select * from t") - } - dom.DDL().SetHook(hook) - tk.MustExec("alter table t modify column a char(10)") -} - -func TestHashPartitionAddColumn(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int) partition by hash(a) partitions 4") - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateWriteOnly { - return - } - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("delete from t") - } - dom.DDL().SetHook(hook) - tk.MustExec("alter table t add column c int") -} - -func TestSetInvalidDefaultValueAfterModifyColumn(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - - var wg sync.WaitGroup - var checkErr error - one := false - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateDeleteOnly { - return - } - if one { - return - } - one = true - wg.Add(1) - go func() { - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - _, checkErr = tk2.Exec("alter table t alter column a set default 1") - wg.Done() - }() - } - dom.DDL().SetHook(hook) - tk.MustExec("alter table t modify column a text(100)") - wg.Wait() - require.EqualError(t, checkErr, "[ddl:1101]BLOB/TEXT/JSON column 'a' can't have a default value") -} - -func TestMDLTruncateTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk3 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int);") - tk.MustExec("begin") - tk.MustExec("select * from t for update") - - var wg sync.WaitGroup - - hook := &callback.TestDDLCallback{Do: dom} - wg.Add(2) - var timetk2 time.Time - var timetk3 time.Time - - one := false - f := func(job *model.Job) { - if one { - return - } - one = true - go func() { - tk3.MustExec("truncate table test.t") - timetk3 = time.Now() - wg.Done() - }() - } - - hook.OnJobUpdatedExported.Store(&f) - dom.DDL().SetHook(hook) - - go func() { - tk2.MustExec("truncate table test.t") - timetk2 = time.Now() - wg.Done() - }() - - time.Sleep(2 * time.Second) - timeMain := time.Now() - tk.MustExec("commit") - wg.Wait() - require.True(t, timetk2.After(timeMain)) - require.True(t, timetk3.After(timeMain)) -} diff --git a/ddl/ddl.go b/ddl/ddl.go deleted file mode 100644 index a288fa1fd9e55..0000000000000 --- a/ddl/ddl.go +++ /dev/null @@ -1,1863 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package ddl - -import ( - "cmp" - "context" - "fmt" - "runtime" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/google/uuid" - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/table" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/syncutil" - "github.com/tikv/client-go/v2/tikvrpc" - clientv3 "go.etcd.io/etcd/client/v3" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -const ( - // currentVersion is for all new DDL jobs. - currentVersion = 1 - // DDLOwnerKey is the ddl owner path that is saved to etcd, and it's exported for testing. - DDLOwnerKey = "/tidb/ddl/fg/owner" - // addingDDLJobPrefix is the path prefix used to record the newly added DDL job, and it's saved to etcd. - addingDDLJobPrefix = "/tidb/ddl/add_ddl_job_" - ddlPrompt = "ddl" - - shardRowIDBitsMax = 15 - - batchAddingJobs = 10 - - reorgWorkerCnt = 10 - generalWorkerCnt = 1 - - // checkFlagIndexInJobArgs is the recoverCheckFlag index used in RecoverTable/RecoverSchema job arg list. - checkFlagIndexInJobArgs = 1 -) - -const ( - // The recoverCheckFlag is used to judge the gc work status when RecoverTable/RecoverSchema. - recoverCheckFlagNone int64 = iota - recoverCheckFlagEnableGC - recoverCheckFlagDisableGC -) - -// OnExist specifies what to do when a new object has a name collision. -type OnExist uint8 - -// AllocTableIDIf specifies whether to retain the old table ID. -// If this returns "false", then we would assume the table ID has been -// allocated before calling `CreateTableWithInfo` family. -type AllocTableIDIf func(*model.TableInfo) bool - -// CreateTableWithInfoConfig is the configuration of `CreateTableWithInfo`. -type CreateTableWithInfoConfig struct { - OnExist OnExist - ShouldAllocTableID AllocTableIDIf -} - -// CreateTableWithInfoConfigurier is the "diff" which can be applied to the -// CreateTableWithInfoConfig, currently implementations are "OnExist" and "AllocTableIDIf". -type CreateTableWithInfoConfigurier interface { - // Apply the change over the config. - Apply(*CreateTableWithInfoConfig) -} - -// GetCreateTableWithInfoConfig applies the series of configurier from default config -// and returns the final config. -func GetCreateTableWithInfoConfig(cs []CreateTableWithInfoConfigurier) CreateTableWithInfoConfig { - config := CreateTableWithInfoConfig{} - for _, c := range cs { - c.Apply(&config) - } - if config.ShouldAllocTableID == nil { - config.ShouldAllocTableID = func(*model.TableInfo) bool { return true } - } - return config -} - -// Apply implements Configurier. -func (o OnExist) Apply(c *CreateTableWithInfoConfig) { - c.OnExist = o -} - -// Apply implements Configurier. -func (a AllocTableIDIf) Apply(c *CreateTableWithInfoConfig) { - c.ShouldAllocTableID = a -} - -const ( - // OnExistError throws an error on name collision. - OnExistError OnExist = iota - // OnExistIgnore skips creating the new object. - OnExistIgnore - // OnExistReplace replaces the old object by the new object. This is only - // supported by VIEWs at the moment. For other object types, this is - // equivalent to OnExistError. - OnExistReplace - - jobRecordCapacity = 16 - jobOnceCapacity = 1000 -) - -var ( - // EnableSplitTableRegion is a flag to decide whether to split a new region for - // a newly created table. It takes effect only if the Storage supports split - // region. - EnableSplitTableRegion = uint32(0) -) - -// DDL is responsible for updating schema in data store and maintaining in-memory InfoSchema cache. -type DDL interface { - CreateSchema(ctx sessionctx.Context, stmt *ast.CreateDatabaseStmt) error - AlterSchema(sctx sessionctx.Context, stmt *ast.AlterDatabaseStmt) error - DropSchema(ctx sessionctx.Context, stmt *ast.DropDatabaseStmt) error - CreateTable(ctx sessionctx.Context, stmt *ast.CreateTableStmt) error - CreateView(ctx sessionctx.Context, stmt *ast.CreateViewStmt) error - DropTable(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) - RecoverTable(ctx sessionctx.Context, recoverInfo *RecoverInfo) (err error) - RecoverSchema(ctx sessionctx.Context, recoverSchemaInfo *RecoverSchemaInfo) error - DropView(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) - CreateIndex(ctx sessionctx.Context, stmt *ast.CreateIndexStmt) error - DropIndex(ctx sessionctx.Context, stmt *ast.DropIndexStmt) error - AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast.AlterTableStmt) error - TruncateTable(ctx sessionctx.Context, tableIdent ast.Ident) error - RenameTable(ctx sessionctx.Context, stmt *ast.RenameTableStmt) error - LockTables(ctx sessionctx.Context, stmt *ast.LockTablesStmt) error - UnlockTables(ctx sessionctx.Context, lockedTables []model.TableLockTpInfo) error - CleanupTableLock(ctx sessionctx.Context, tables []*ast.TableName) error - UpdateTableReplicaInfo(ctx sessionctx.Context, physicalID int64, available bool) error - RepairTable(ctx sessionctx.Context, createStmt *ast.CreateTableStmt) error - CreateSequence(ctx sessionctx.Context, stmt *ast.CreateSequenceStmt) error - DropSequence(ctx sessionctx.Context, stmt *ast.DropSequenceStmt) (err error) - AlterSequence(ctx sessionctx.Context, stmt *ast.AlterSequenceStmt) error - CreatePlacementPolicy(ctx sessionctx.Context, stmt *ast.CreatePlacementPolicyStmt) error - DropPlacementPolicy(ctx sessionctx.Context, stmt *ast.DropPlacementPolicyStmt) error - AlterPlacementPolicy(ctx sessionctx.Context, stmt *ast.AlterPlacementPolicyStmt) error - AddResourceGroup(ctx sessionctx.Context, stmt *ast.CreateResourceGroupStmt) error - AlterResourceGroup(ctx sessionctx.Context, stmt *ast.AlterResourceGroupStmt) error - DropResourceGroup(ctx sessionctx.Context, stmt *ast.DropResourceGroupStmt) error - FlashbackCluster(ctx sessionctx.Context, flashbackTS uint64) error - - // CreateSchemaWithInfo creates a database (schema) given its database info. - // - // WARNING: the DDL owns the `info` after calling this function, and will modify its fields - // in-place. If you want to keep using `info`, please call Clone() first. - CreateSchemaWithInfo( - ctx sessionctx.Context, - info *model.DBInfo, - onExist OnExist) error - - // CreateTableWithInfo creates a table, view or sequence given its table info. - // - // WARNING: the DDL owns the `info` after calling this function, and will modify its fields - // in-place. If you want to keep using `info`, please call Clone() first. - CreateTableWithInfo( - ctx sessionctx.Context, - schema model.CIStr, - info *model.TableInfo, - cs ...CreateTableWithInfoConfigurier) error - - // BatchCreateTableWithInfo is like CreateTableWithInfo, but can handle multiple tables. - BatchCreateTableWithInfo(ctx sessionctx.Context, - schema model.CIStr, - info []*model.TableInfo, - cs ...CreateTableWithInfoConfigurier) error - - // CreatePlacementPolicyWithInfo creates a placement policy - // - // WARNING: the DDL owns the `policy` after calling this function, and will modify its fields - // in-place. If you want to keep using `policy`, please call Clone() first. - CreatePlacementPolicyWithInfo(ctx sessionctx.Context, policy *model.PolicyInfo, onExist OnExist) error - - // Start campaigns the owner and starts workers. - // ctxPool is used for the worker's delRangeManager and creates sessions. - Start(ctxPool *pools.ResourcePool) error - // GetLease returns current schema lease time. - GetLease() time.Duration - // Stats returns the DDL statistics. - Stats(vars *variable.SessionVars) (map[string]interface{}, error) - // GetScope gets the status variables scope. - GetScope(status string) variable.ScopeFlag - // Stop stops DDL worker. - Stop() error - // RegisterStatsHandle registers statistics handle and its corresponding event channel for ddl. - RegisterStatsHandle(*handle.Handle) - // SchemaSyncer gets the schema syncer. - SchemaSyncer() syncer.SchemaSyncer - // StateSyncer gets the cluster state syncer. - StateSyncer() syncer.StateSyncer - // OwnerManager gets the owner manager. - OwnerManager() owner.Manager - // GetID gets the ddl ID. - GetID() string - // GetTableMaxHandle gets the max row ID of a normal table or a partition. - GetTableMaxHandle(ctx *JobContext, startTS uint64, tbl table.PhysicalTable) (kv.Handle, bool, error) - // SetBinlogClient sets the binlog client for DDL worker. It's exported for testing. - SetBinlogClient(*pumpcli.PumpsClient) - // GetHook gets the hook. It's exported for testing. - GetHook() Callback - // SetHook sets the hook. - SetHook(h Callback) - // GetInfoSchemaWithInterceptor gets the infoschema binding to d. It's exported for testing. - GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema - // DoDDLJob does the DDL job, it's exported for test. - DoDDLJob(ctx sessionctx.Context, job *model.Job) error -} - -type limitJobTask struct { - job *model.Job - err chan error - cacheErr error -} - -// ddl is used to handle the statements that define the structure or schema of the database. -type ddl struct { - m sync.RWMutex - wg tidbutil.WaitGroupWrapper // It's only used to deal with data race in restart_test. - limitJobCh chan *limitJobTask - - *ddlCtx - sessPool *sess.Pool - delRangeMgr delRangeManager - enableTiFlashPoll *atomicutil.Bool - // used in the concurrency ddl. - reorgWorkerPool *workerPool - generalDDLWorkerPool *workerPool - // get notification if any DDL coming. - ddlJobCh chan struct{} -} - -// waitSchemaSyncedController is to control whether to waitSchemaSynced or not. -type waitSchemaSyncedController struct { - mu sync.RWMutex - job map[int64]struct{} - - // Use to check if the DDL job is the first run on this owner. - onceMap map[int64]struct{} -} - -func newWaitSchemaSyncedController() *waitSchemaSyncedController { - return &waitSchemaSyncedController{ - job: make(map[int64]struct{}, jobRecordCapacity), - onceMap: make(map[int64]struct{}, jobOnceCapacity), - } -} - -func (w *waitSchemaSyncedController) registerSync(job *model.Job) { - w.mu.Lock() - defer w.mu.Unlock() - w.job[job.ID] = struct{}{} -} - -func (w *waitSchemaSyncedController) isSynced(job *model.Job) bool { - w.mu.RLock() - defer w.mu.RUnlock() - _, ok := w.job[job.ID] - return !ok -} - -func (w *waitSchemaSyncedController) synced(job *model.Job) { - w.mu.Lock() - defer w.mu.Unlock() - delete(w.job, job.ID) -} - -// maybeAlreadyRunOnce returns true means that the job may be the first run on this owner. -// Returns false means that the job must not be the first run on this owner. -func (w *waitSchemaSyncedController) maybeAlreadyRunOnce(id int64) bool { - w.mu.Lock() - defer w.mu.Unlock() - _, ok := w.onceMap[id] - return ok -} - -func (w *waitSchemaSyncedController) setAlreadyRunOnce(id int64) { - w.mu.Lock() - defer w.mu.Unlock() - if len(w.onceMap) > jobOnceCapacity { - // If the map is too large, we reset it. These jobs may need to check schema synced again, but it's ok. - w.onceMap = make(map[int64]struct{}, jobRecordCapacity) - } - w.onceMap[id] = struct{}{} -} - -// ddlCtx is the context when we use worker to handle DDL jobs. -type ddlCtx struct { - ctx context.Context - cancel context.CancelFunc - uuid string - store kv.Storage - ownerManager owner.Manager - schemaSyncer syncer.SchemaSyncer - stateSyncer syncer.StateSyncer - ddlJobDoneCh chan struct{} - ddlEventCh chan<- *util.Event - lease time.Duration // lease is schema lease. - binlogCli *pumpcli.PumpsClient // binlogCli is used for Binlog. - infoCache *infoschema.InfoCache - statsHandle *handle.Handle - tableLockCkr util.DeadTableLockChecker - etcdCli *clientv3.Client - // backfillJobCh gets notification if any backfill jobs coming. - backfillJobCh chan struct{} - - *waitSchemaSyncedController - *schemaVersionManager - // recording the running jobs. - runningJobs struct { - sync.RWMutex - ids map[int64]struct{} - } - // It holds the running DDL jobs ID. - runningJobIDs []string - // reorgCtx is used for reorganization. - reorgCtx reorgContexts - // backfillCtx is used for backfill workers. - backfillCtx struct { - syncutil.RWMutex - jobCtxMap map[int64]*JobContext - } - - jobCtx struct { - sync.RWMutex - // jobCtxMap maps job ID to job's ctx. - jobCtxMap map[int64]*JobContext - } - - // hook may be modified. - mu struct { - sync.RWMutex - hook Callback - interceptor Interceptor - } - - ddlSeqNumMu struct { - sync.Mutex - seqNum uint64 - } -} - -// schemaVersionManager is used to manage the schema version. To prevent the conflicts on this key between different DDL job, -// we use another transaction to update the schema version, so that we need to lock the schema version and unlock it until the job is committed. -type schemaVersionManager struct { - schemaVersionMu sync.Mutex - // lockOwner stores the job ID that is holding the lock. - lockOwner atomicutil.Int64 -} - -func newSchemaVersionManager() *schemaVersionManager { - return &schemaVersionManager{} -} - -func (sv *schemaVersionManager) setSchemaVersion(job *model.Job, store kv.Storage) (schemaVersion int64, err error) { - sv.lockSchemaVersion(job.ID) - err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - var err error - m := meta.NewMeta(txn) - schemaVersion, err = m.GenSchemaVersion() - return err - }) - return schemaVersion, err -} - -// lockSchemaVersion gets the lock to prevent the schema version from being updated. -func (sv *schemaVersionManager) lockSchemaVersion(jobID int64) { - ownerID := sv.lockOwner.Load() - // There may exist one job update schema version many times in multiple-schema-change, so we do not lock here again - // if they are the same job. - if ownerID != jobID { - sv.schemaVersionMu.Lock() - sv.lockOwner.Store(jobID) - } -} - -// unlockSchemaVersion releases the lock. -func (sv *schemaVersionManager) unlockSchemaVersion(jobID int64) { - ownerID := sv.lockOwner.Load() - if ownerID == jobID { - sv.lockOwner.Store(0) - sv.schemaVersionMu.Unlock() - } -} - -func (dc *ddlCtx) isOwner() bool { - isOwner := dc.ownerManager.IsOwner() - logutil.BgLogger().Debug("check whether is the DDL owner", zap.String("category", "ddl"), zap.Bool("isOwner", isOwner), zap.String("selfID", dc.uuid)) - if isOwner { - metrics.DDLCounter.WithLabelValues(metrics.DDLOwner + "_" + mysql.TiDBReleaseVersion).Inc() - } - return isOwner -} - -func (dc *ddlCtx) setDDLLabelForTopSQL(jobID int64, jobQuery string) { - dc.jobCtx.Lock() - defer dc.jobCtx.Unlock() - ctx, exists := dc.jobCtx.jobCtxMap[jobID] - if !exists { - ctx = NewJobContext() - dc.jobCtx.jobCtxMap[jobID] = ctx - } - ctx.setDDLLabelForTopSQL(jobQuery) -} - -func (dc *ddlCtx) setDDLSourceForDiagnosis(jobID int64, jobType model.ActionType) { - dc.jobCtx.Lock() - defer dc.jobCtx.Unlock() - ctx, exists := dc.jobCtx.jobCtxMap[jobID] - if !exists { - ctx = NewJobContext() - dc.jobCtx.jobCtxMap[jobID] = ctx - } - ctx.setDDLLabelForDiagnosis(jobType) -} - -func (dc *ddlCtx) getResourceGroupTaggerForTopSQL(jobID int64) tikvrpc.ResourceGroupTagger { - dc.jobCtx.Lock() - defer dc.jobCtx.Unlock() - ctx, exists := dc.jobCtx.jobCtxMap[jobID] - if !exists { - return nil - } - return ctx.getResourceGroupTaggerForTopSQL() -} - -func (dc *ddlCtx) removeJobCtx(job *model.Job) { - dc.jobCtx.Lock() - defer dc.jobCtx.Unlock() - delete(dc.jobCtx.jobCtxMap, job.ID) -} - -func (dc *ddlCtx) jobContext(jobID int64, reorgMeta *model.DDLReorgMeta) *JobContext { - dc.jobCtx.RLock() - defer dc.jobCtx.RUnlock() - var ctx *JobContext - if jobContext, exists := dc.jobCtx.jobCtxMap[jobID]; exists { - ctx = jobContext - } else { - ctx = NewJobContext() - } - if reorgMeta != nil && len(ctx.resourceGroupName) == 0 { - ctx.resourceGroupName = reorgMeta.ResourceGroupName - } - return ctx -} - -func (dc *ddlCtx) removeBackfillCtxJobCtx(jobID int64) { - dc.backfillCtx.Lock() - delete(dc.backfillCtx.jobCtxMap, jobID) - dc.backfillCtx.Unlock() -} - -func (dc *ddlCtx) backfillCtxJobIDs() []int64 { - dc.backfillCtx.Lock() - defer dc.backfillCtx.Unlock() - - runningJobIDs := make([]int64, 0, len(dc.backfillCtx.jobCtxMap)) - for id := range dc.backfillCtx.jobCtxMap { - runningJobIDs = append(runningJobIDs, id) - } - return runningJobIDs -} - -type reorgContexts struct { - sync.RWMutex - // reorgCtxMap maps job ID to reorg context. - reorgCtxMap map[int64]*reorgCtx -} - -func (dc *ddlCtx) getReorgCtx(jobID int64) *reorgCtx { - dc.reorgCtx.RLock() - defer dc.reorgCtx.RUnlock() - return dc.reorgCtx.reorgCtxMap[jobID] -} - -func (dc *ddlCtx) newReorgCtx(jobID int64, rowCount int64) *reorgCtx { - dc.reorgCtx.Lock() - defer dc.reorgCtx.Unlock() - existedRC, ok := dc.reorgCtx.reorgCtxMap[jobID] - if ok { - existedRC.references.Add(1) - return existedRC - } - rc := &reorgCtx{} - rc.doneCh = make(chan error, 1) - // initial reorgCtx - rc.setRowCount(rowCount) - rc.mu.warnings = make(map[errors.ErrorID]*terror.Error) - rc.mu.warningsCount = make(map[errors.ErrorID]int64) - rc.references.Add(1) - dc.reorgCtx.reorgCtxMap[jobID] = rc - return rc -} - -func (dc *ddlCtx) removeReorgCtx(jobID int64) { - dc.reorgCtx.Lock() - defer dc.reorgCtx.Unlock() - ctx, ok := dc.reorgCtx.reorgCtxMap[jobID] - if ok { - ctx.references.Sub(1) - if ctx.references.Load() == 0 { - delete(dc.reorgCtx.reorgCtxMap, jobID) - } - } -} - -func (dc *ddlCtx) notifyReorgWorkerJobStateChange(job *model.Job) { - rc := dc.getReorgCtx(job.ID) - if rc == nil { - logutil.BgLogger().Warn("cannot find reorgCtx", zap.Int64("Job ID", job.ID)) - return - } - logutil.BgLogger().Info("notify reorg worker the job's state", - zap.Int64("Job ID", job.ID), zap.String("Job State", job.State.String()), - zap.String("Schema State", job.SchemaState.String()), zap.String("category", "ddl")) - rc.notifyJobState(job.State) -} - -// EnableTiFlashPoll enables TiFlash poll loop aka PollTiFlashReplicaStatus. -func EnableTiFlashPoll(d interface{}) { - if dd, ok := d.(*ddl); ok { - dd.enableTiFlashPoll.Store(true) - } -} - -// DisableTiFlashPoll disables TiFlash poll loop aka PollTiFlashReplicaStatus. -func DisableTiFlashPoll(d interface{}) { - if dd, ok := d.(*ddl); ok { - dd.enableTiFlashPoll.Store(false) - } -} - -// IsTiFlashPollEnabled reveals enableTiFlashPoll -func (d *ddl) IsTiFlashPollEnabled() bool { - return d.enableTiFlashPoll.Load() -} - -// RegisterStatsHandle registers statistics handle and its corresponding even channel for ddl. -func (d *ddl) RegisterStatsHandle(h *handle.Handle) { - d.ddlCtx.statsHandle = h - d.ddlEventCh = h.DDLEventCh() -} - -// asyncNotifyEvent will notify the ddl event to outside world, say statistic handle. When the channel is full, we may -// give up notify and log it. -func asyncNotifyEvent(d *ddlCtx, e *util.Event) { - if d.ddlEventCh != nil { - if d.lease == 0 { - // If lease is 0, it's always used in test. - select { - case d.ddlEventCh <- e: - default: - } - return - } - for i := 0; i < 10; i++ { - select { - case d.ddlEventCh <- e: - return - default: - time.Sleep(time.Microsecond * 10) - } - } - logutil.BgLogger().Warn("fail to notify DDL event", zap.String("category", "ddl"), zap.String("event", e.String())) - } -} - -// NewDDL creates a new DDL. -func NewDDL(ctx context.Context, options ...Option) DDL { - return newDDL(ctx, options...) -} - -func newDDL(ctx context.Context, options ...Option) *ddl { - opt := &Options{ - Hook: &BaseCallback{}, - } - for _, o := range options { - o(opt) - } - - id := uuid.New().String() - var manager owner.Manager - var schemaSyncer syncer.SchemaSyncer - var stateSyncer syncer.StateSyncer - var deadLockCkr util.DeadTableLockChecker - if etcdCli := opt.EtcdCli; etcdCli == nil { - // The etcdCli is nil if the store is localstore which is only used for testing. - // So we use mockOwnerManager and MockSchemaSyncer. - manager = owner.NewMockManager(ctx, id, opt.Store, DDLOwnerKey) - schemaSyncer = NewMockSchemaSyncer() - stateSyncer = NewMockStateSyncer() - } else { - manager = owner.NewOwnerManager(ctx, etcdCli, ddlPrompt, id, DDLOwnerKey) - schemaSyncer = syncer.NewSchemaSyncer(etcdCli, id) - stateSyncer = syncer.NewStateSyncer(etcdCli, util.ServerGlobalState) - deadLockCkr = util.NewDeadTableLockChecker(etcdCli) - } - - // TODO: make store and infoCache explicit arguments - // these two should be ensured to exist - if opt.Store == nil { - panic("store should not be nil") - } - if opt.InfoCache == nil { - panic("infoCache should not be nil") - } - - ddlCtx := &ddlCtx{ - uuid: id, - store: opt.Store, - lease: opt.Lease, - ddlJobDoneCh: make(chan struct{}, 1), - ownerManager: manager, - schemaSyncer: schemaSyncer, - stateSyncer: stateSyncer, - binlogCli: binloginfo.GetPumpsClient(), - infoCache: opt.InfoCache, - tableLockCkr: deadLockCkr, - etcdCli: opt.EtcdCli, - schemaVersionManager: newSchemaVersionManager(), - waitSchemaSyncedController: newWaitSchemaSyncedController(), - runningJobIDs: make([]string, 0, jobRecordCapacity), - } - ddlCtx.reorgCtx.reorgCtxMap = make(map[int64]*reorgCtx) - ddlCtx.jobCtx.jobCtxMap = make(map[int64]*JobContext) - ddlCtx.mu.hook = opt.Hook - ddlCtx.mu.interceptor = &BaseInterceptor{} - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) - ddlCtx.ctx, ddlCtx.cancel = context.WithCancel(ctx) - ddlCtx.runningJobs.ids = make(map[int64]struct{}) - - d := &ddl{ - ddlCtx: ddlCtx, - limitJobCh: make(chan *limitJobTask, batchAddingJobs), - enableTiFlashPoll: atomicutil.NewBool(true), - ddlJobCh: make(chan struct{}, 100), - } - - scheduler.RegisterTaskType(BackfillTaskType, - func(ctx context.Context, id string, task *proto.Task, taskTable scheduler.TaskTable) scheduler.Scheduler { - return newBackfillDistScheduler(ctx, id, task, taskTable, d) - }, scheduler.WithSummary, - ) - - backFillDsp, err := NewBackfillingDispatcherExt(d) - if err != nil { - logutil.BgLogger().Warn("NewBackfillingDispatcherExt failed", zap.String("category", "ddl"), zap.Error(err)) - } else { - dispatcher.RegisterDispatcherFactory(BackfillTaskType, - func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { - return newLitBackfillDispatcher(ctx, taskMgr, serverID, task, backFillDsp) - }) - dispatcher.RegisterDispatcherCleanUpFactory(BackfillTaskType, newBackfillCleanUpS3) - } - - // Register functions for enable/disable ddl when changing system variable `tidb_enable_ddl`. - variable.EnableDDL = d.EnableDDL - variable.DisableDDL = d.DisableDDL - variable.SwitchMDL = d.SwitchMDL - - return d -} - -// Stop implements DDL.Stop interface. -func (d *ddl) Stop() error { - d.m.Lock() - defer d.m.Unlock() - - d.close() - logutil.BgLogger().Info("stop DDL", zap.String("category", "ddl"), zap.String("ID", d.uuid)) - return nil -} - -func (d *ddl) newDeleteRangeManager(mock bool) delRangeManager { - var delRangeMgr delRangeManager - if !mock { - delRangeMgr = newDelRangeManager(d.store, d.sessPool) - logutil.BgLogger().Info("start delRangeManager OK", zap.String("category", "ddl"), zap.Bool("is a emulator", !d.store.SupportDeleteRange())) - } else { - delRangeMgr = newMockDelRangeManager() - } - - delRangeMgr.start() - return delRangeMgr -} - -func (d *ddl) prepareWorkers4ConcurrencyDDL() { - workerFactory := func(tp workerType) func() (pools.Resource, error) { - return func() (pools.Resource, error) { - wk := newWorker(d.ctx, tp, d.sessPool, d.delRangeMgr, d.ddlCtx) - sessForJob, err := d.sessPool.Get() - if err != nil { - return nil, err - } - sessForJob.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) - wk.sess = sess.NewSession(sessForJob) - metrics.DDLCounter.WithLabelValues(fmt.Sprintf("%s_%s", metrics.CreateDDL, wk.String())).Inc() - return wk, nil - } - } - // reorg worker count at least 1 at most 10. - reorgCnt := min(max(runtime.GOMAXPROCS(0)/4, 1), reorgWorkerCnt) - d.reorgWorkerPool = newDDLWorkerPool(pools.NewResourcePool(workerFactory(addIdxWorker), reorgCnt, reorgCnt, 0), reorg) - d.generalDDLWorkerPool = newDDLWorkerPool(pools.NewResourcePool(workerFactory(generalWorker), generalWorkerCnt, generalWorkerCnt, 0), general) - failpoint.Inject("NoDDLDispatchLoop", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return() - } - }) - d.wg.Run(d.startDispatchLoop) -} - -// Start implements DDL.Start interface. -func (d *ddl) Start(ctxPool *pools.ResourcePool) error { - logutil.BgLogger().Info("start DDL", zap.String("category", "ddl"), zap.String("ID", d.uuid), zap.Bool("runWorker", config.GetGlobalConfig().Instance.TiDBEnableDDL.Load())) - - d.wg.Run(d.limitDDLJobs) - d.sessPool = sess.NewSessionPool(ctxPool, d.store) - d.ownerManager.SetBeOwnerHook(func() { - var err error - d.ddlSeqNumMu.Lock() - defer d.ddlSeqNumMu.Unlock() - d.ddlSeqNumMu.seqNum, err = d.GetNextDDLSeqNum() - if err != nil { - logutil.BgLogger().Error("error when getting the ddl history count", zap.Error(err)) - } - }) - - d.delRangeMgr = d.newDeleteRangeManager(ctxPool == nil) - - if err := d.stateSyncer.Init(d.ctx); err != nil { - logutil.BgLogger().Warn("start DDL init state syncer failed", zap.String("category", "ddl"), zap.Error(err)) - return errors.Trace(err) - } - - d.prepareWorkers4ConcurrencyDDL() - - if config.TableLockEnabled() { - d.wg.Add(1) - go d.startCleanDeadTableLock() - } - - // If tidb_enable_ddl is true, we need campaign owner and do DDL jobs. Besides, we also can do backfill jobs. - // Otherwise, we needn't do that. - if config.GetGlobalConfig().Instance.TiDBEnableDDL.Load() { - if err := d.EnableDDL(); err != nil { - return err - } - } - - variable.RegisterStatistics(d) - - metrics.DDLCounter.WithLabelValues(metrics.CreateDDLInstance).Inc() - - // Start some background routine to manage TiFlash replica. - d.wg.Run(d.PollTiFlashRoutine) - - ctx, err := d.sessPool.Get() - if err != nil { - return err - } - defer d.sessPool.Put(ctx) - - ingest.InitGlobalLightningEnv(d.ctx, ctx) - - return nil -} - -// EnableDDL enable this node to execute ddl. -// Since ownerManager.CampaignOwner will start a new goroutine to run ownerManager.campaignLoop, -// we should make sure that before invoking EnableDDL(), ddl is DISABLE. -func (d *ddl) EnableDDL() error { - err := d.ownerManager.CampaignOwner() - return errors.Trace(err) -} - -// DisableDDL disable this node to execute ddl. -// We should make sure that before invoking DisableDDL(), ddl is ENABLE. -func (d *ddl) DisableDDL() error { - if d.ownerManager.IsOwner() { - // If there is only one node, we should NOT disable ddl. - serverInfo, err := infosync.GetAllServerInfo(d.ctx) - if err != nil { - logutil.BgLogger().Error("error when GetAllServerInfo", zap.String("category", "ddl"), zap.Error(err)) - return err - } - if len(serverInfo) <= 1 { - return dbterror.ErrDDLSetting.GenWithStackByArgs("disabling", "can not disable ddl owner when it is the only one tidb instance") - } - // FIXME: if possible, when this node is the only node with DDL, ths setting of DisableDDL should fail. - } - - // disable campaign by interrupting campaignLoop - d.ownerManager.CampaignCancel() - return nil -} - -// GetNextDDLSeqNum return the next DDL seq num. -func (d *ddl) GetNextDDLSeqNum() (uint64, error) { - var count uint64 - ctx := kv.WithInternalSourceType(d.ctx, kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - var err error - count, err = t.GetHistoryDDLCount() - return err - }) - return count, err -} - -func (d *ddl) close() { - if isChanClosed(d.ctx.Done()) { - return - } - - startTime := time.Now() - d.cancel() - d.wg.Wait() - d.ownerManager.Cancel() - d.schemaSyncer.Close() - if d.reorgWorkerPool != nil { - d.reorgWorkerPool.close() - } - if d.generalDDLWorkerPool != nil { - d.generalDDLWorkerPool.close() - } - - // d.delRangeMgr using sessions from d.sessPool. - // Put it before d.sessPool.close to reduce the time spent by d.sessPool.close. - if d.delRangeMgr != nil { - d.delRangeMgr.clear() - } - if d.sessPool != nil { - d.sessPool.Close() - } - variable.UnregisterStatistics(d) - - logutil.BgLogger().Info("DDL closed", zap.String("category", "ddl"), zap.String("ID", d.uuid), zap.Duration("take time", time.Since(startTime))) -} - -// GetLease implements DDL.GetLease interface. -func (d *ddl) GetLease() time.Duration { - lease := d.lease - return lease -} - -// GetInfoSchemaWithInterceptor gets the infoschema binding to d. It's exported for testing. -// Please don't use this function, it is used by TestParallelDDLBeforeRunDDLJob to intercept the calling of d.infoHandle.Get(), use d.infoHandle.Get() instead. -// Otherwise, the TestParallelDDLBeforeRunDDLJob will hang up forever. -func (d *ddl) GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema { - is := d.infoCache.GetLatest() - - d.mu.RLock() - defer d.mu.RUnlock() - return d.mu.interceptor.OnGetInfoSchema(ctx, is) -} - -func (d *ddl) genGlobalIDs(count int) ([]int64, error) { - var ret []int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { - failpoint.Inject("mockGenGlobalIDFail", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(errors.New("gofail genGlobalIDs error")) - } - }) - - m := meta.NewMeta(txn) - var err error - ret, err = m.GenGlobalIDs(count) - return err - }) - - return ret, err -} - -func (d *ddl) genPlacementPolicyID() (int64, error) { - var ret int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - var err error - ret, err = m.GenPlacementPolicyID() - return err - }) - - return ret, err -} - -// SchemaSyncer implements DDL.SchemaSyncer interface. -func (d *ddl) SchemaSyncer() syncer.SchemaSyncer { - return d.schemaSyncer -} - -// StateSyncer implements DDL.StateSyncer interface. -func (d *ddl) StateSyncer() syncer.StateSyncer { - return d.stateSyncer -} - -// OwnerManager implements DDL.OwnerManager interface. -func (d *ddl) OwnerManager() owner.Manager { - return d.ownerManager -} - -// GetID implements DDL.GetID interface. -func (d *ddl) GetID() string { - return d.uuid -} - -var ( - fastDDLIntervalPolicy = []time.Duration{ - 500 * time.Millisecond, - } - normalDDLIntervalPolicy = []time.Duration{ - 500 * time.Millisecond, - 500 * time.Millisecond, - 1 * time.Second, - } - slowDDLIntervalPolicy = []time.Duration{ - 500 * time.Millisecond, - 500 * time.Millisecond, - 1 * time.Second, - 1 * time.Second, - 3 * time.Second, - } -) - -func getIntervalFromPolicy(policy []time.Duration, i int) (time.Duration, bool) { - plen := len(policy) - if i < plen { - return policy[i], true - } - return policy[plen-1], false -} - -func getJobCheckInterval(job *model.Job, i int) (time.Duration, bool) { - switch job.Type { - case model.ActionAddIndex, model.ActionAddPrimaryKey, model.ActionModifyColumn, - model.ActionReorganizePartition, - model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning: - return getIntervalFromPolicy(slowDDLIntervalPolicy, i) - case model.ActionCreateTable, model.ActionCreateSchema: - return getIntervalFromPolicy(fastDDLIntervalPolicy, i) - default: - return getIntervalFromPolicy(normalDDLIntervalPolicy, i) - } -} - -func (dc *ddlCtx) asyncNotifyWorker(ch chan struct{}, etcdPath string, jobID int64, jobType string) { - // If the workers don't run, we needn't notify workers. - // TODO: It does not affect informing the backfill worker. - if !config.GetGlobalConfig().Instance.TiDBEnableDDL.Load() { - return - } - if dc.isOwner() { - asyncNotify(ch) - } else { - dc.asyncNotifyByEtcd(etcdPath, jobID, jobType) - } -} - -func updateTickerInterval(ticker *time.Ticker, lease time.Duration, job *model.Job, i int) *time.Ticker { - interval, changed := getJobCheckInterval(job, i) - if !changed { - return ticker - } - // For now we should stop old ticker and create a new ticker - ticker.Stop() - return time.NewTicker(chooseLeaseTime(lease, interval)) -} - -func recordLastDDLInfo(ctx sessionctx.Context, job *model.Job) { - if job == nil { - return - } - ctx.GetSessionVars().LastDDLInfo.Query = job.Query - ctx.GetSessionVars().LastDDLInfo.SeqNum = job.SeqNum -} - -func setDDLJobQuery(ctx sessionctx.Context, job *model.Job) { - switch job.Type { - case model.ActionUpdateTiFlashReplicaStatus, model.ActionUnlockTable: - job.Query = "" - default: - job.Query, _ = ctx.Value(sessionctx.QueryString).(string) - } -} - -// DoDDLJob will return -// - nil: found in history DDL job and no job error -// - context.Cancel: job has been sent to worker, but not found in history DDL job before cancel -// - other: found in history DDL job and return that job error -func (d *ddl) DoDDLJob(ctx sessionctx.Context, job *model.Job) error { - job.TraceInfo = &model.TraceInfo{ - ConnectionID: ctx.GetSessionVars().ConnectionID, - SessionAlias: ctx.GetSessionVars().SessionAlias, - } - if mci := ctx.GetSessionVars().StmtCtx.MultiSchemaInfo; mci != nil { - // In multiple schema change, we don't run the job. - // Instead, we merge all the jobs into one pending job. - return appendToSubJobs(mci, job) - } - // Get a global job ID and put the DDL job in the queue. - setDDLJobQuery(ctx, job) - task := &limitJobTask{job, make(chan error), nil} - d.limitJobCh <- task - - failpoint.Inject("mockParallelSameDDLJobTwice", func(val failpoint.Value) { - if val.(bool) { - <-task.err - // The same job will be put to the DDL queue twice. - job = job.Clone() - task1 := &limitJobTask{job, make(chan error), nil} - d.limitJobCh <- task1 - // The second job result is used for test. - task = task1 - } - }) - - // worker should restart to continue handling tasks in limitJobCh, and send back through task.err - err := <-task.err - if err != nil { - // The transaction of enqueuing job is failed. - return errors.Trace(err) - } - - sessVars := ctx.GetSessionVars() - sessVars.StmtCtx.IsDDLJobInQueue = true - - // Notice worker that we push a new job and wait the job done. - d.asyncNotifyWorker(d.ddlJobCh, addingDDLJobConcurrent, job.ID, job.Type.String()) - logutil.BgLogger().Info("start DDL job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.String("query", job.Query)) - - var historyJob *model.Job - jobID := job.ID - - // Attach the context of the jobId to the calling session so that - // KILL can cancel this DDL job. - ctx.GetSessionVars().StmtCtx.DDLJobID = jobID - - // For a job from start to end, the state of it will be none -> delete only -> write only -> reorganization -> public - // For every state changes, we will wait as lease 2 * lease time, so here the ticker check is 10 * lease. - // But we use etcd to speed up, normally it takes less than 0.5s now, so we use 0.5s or 1s or 3s as the max value. - initInterval, _ := getJobCheckInterval(job, 0) - ticker := time.NewTicker(chooseLeaseTime(10*d.lease, initInterval)) - startTime := time.Now() - metrics.JobsGauge.WithLabelValues(job.Type.String()).Inc() - defer func() { - ticker.Stop() - metrics.JobsGauge.WithLabelValues(job.Type.String()).Dec() - metrics.HandleJobHistogram.WithLabelValues(job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - recordLastDDLInfo(ctx, historyJob) - }() - i := 0 - for { - failpoint.Inject("storeCloseInLoop", func(_ failpoint.Value) { - _ = d.Stop() - }) - - select { - case <-d.ddlJobDoneCh: - case <-ticker.C: - i++ - ticker = updateTickerInterval(ticker, 10*d.lease, job, i) - case <-d.ctx.Done(): - logutil.BgLogger().Info("DoDDLJob will quit because context done", zap.String("category", "ddl")) - return context.Canceled - } - - // If the connection being killed, we need to CANCEL the DDL job. - if atomic.LoadUint32(&sessVars.Killed) == 1 { - if atomic.LoadInt32(&sessVars.ConnectionStatus) == variable.ConnStatusShutdown { - logutil.BgLogger().Info("DoDDLJob will quit because context done", zap.String("category", "ddl")) - return context.Canceled - } - if sessVars.StmtCtx.DDLJobID != 0 { - se, err := d.sessPool.Get() - if err != nil { - logutil.BgLogger().Error("get session failed, check again", zap.String("category", "ddl"), zap.Error(err)) - continue - } - sessVars.StmtCtx.DDLJobID = 0 // Avoid repeat. - errs, err := CancelJobsBySystem(se, []int64{jobID}) - d.sessPool.Put(se) - if len(errs) > 0 { - logutil.BgLogger().Warn("error canceling DDL job", zap.Error(errs[0])) - } - if err != nil { - logutil.BgLogger().Warn("Kill command could not cancel DDL job", zap.Error(err)) - continue - } - } - } - - se, err := d.sessPool.Get() - if err != nil { - logutil.BgLogger().Error("get session failed, check again", zap.String("category", "ddl"), zap.Error(err)) - continue - } - historyJob, err = GetHistoryJobByID(se, jobID) - d.sessPool.Put(se) - if err != nil { - logutil.BgLogger().Error("get history DDL job failed, check again", zap.String("category", "ddl"), zap.Error(err)) - continue - } - if historyJob == nil { - logutil.BgLogger().Debug("DDL job is not in history, maybe not run", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) - continue - } - - d.checkHistoryJobInTest(ctx, historyJob) - - // If a job is a history job, the state must be JobStateSynced or JobStateRollbackDone or JobStateCancelled. - if historyJob.IsSynced() { - // Judge whether there are some warnings when executing DDL under the certain SQL mode. - if historyJob.ReorgMeta != nil && len(historyJob.ReorgMeta.Warnings) != 0 { - if len(historyJob.ReorgMeta.Warnings) != len(historyJob.ReorgMeta.WarningsCount) { - logutil.BgLogger().Info("DDL warnings doesn't match the warnings count", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) - } else { - for key, warning := range historyJob.ReorgMeta.Warnings { - keyCount := historyJob.ReorgMeta.WarningsCount[key] - if keyCount == 1 { - ctx.GetSessionVars().StmtCtx.AppendWarning(warning) - } else { - newMsg := fmt.Sprintf("%d warnings with this error code, first warning: "+warning.GetMsg(), keyCount) - newWarning := dbterror.ClassTypes.Synthesize(terror.ErrCode(warning.Code()), newMsg) - ctx.GetSessionVars().StmtCtx.AppendWarning(newWarning) - } - } - } - } - appendMultiChangeWarningsToOwnerCtx(ctx, historyJob) - - logutil.BgLogger().Info("DDL job is finished", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) - return nil - } - - if historyJob.Error != nil { - logutil.BgLogger().Info("DDL job is failed", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) - return errors.Trace(historyJob.Error) - } - panic("When the state is JobStateRollbackDone or JobStateCancelled, historyJob.Error should never be nil") - } -} - -func (d *ddl) callHookOnChanged(job *model.Job, err error) error { - if job.State == model.JobStateNone { - // We don't call the hook if the job haven't run yet. - return err - } - d.mu.RLock() - defer d.mu.RUnlock() - - err = d.mu.hook.OnChanged(err) - return errors.Trace(err) -} - -// SetBinlogClient implements DDL.SetBinlogClient interface. -func (d *ddl) SetBinlogClient(binlogCli *pumpcli.PumpsClient) { - d.binlogCli = binlogCli -} - -// GetHook implements DDL.GetHook interface. -func (d *ddl) GetHook() Callback { - d.mu.Lock() - defer d.mu.Unlock() - - return d.mu.hook -} - -// SetHook set the customized hook. -func (d *ddl) SetHook(h Callback) { - d.mu.Lock() - defer d.mu.Unlock() - - d.mu.hook = h -} - -func (d *ddl) startCleanDeadTableLock() { - defer func() { - d.wg.Done() - }() - - defer tidbutil.Recover(metrics.LabelDDL, "startCleanDeadTableLock", nil, false) - - ticker := time.NewTicker(time.Second * 10) - defer ticker.Stop() - for { - select { - case <-ticker.C: - if !d.ownerManager.IsOwner() { - continue - } - deadLockTables, err := d.tableLockCkr.GetDeadLockedTables(d.ctx, d.infoCache.GetLatest().AllSchemas()) - if err != nil { - logutil.BgLogger().Info("get dead table lock failed.", zap.String("category", "ddl"), zap.Error(err)) - continue - } - for se, tables := range deadLockTables { - err := d.CleanDeadTableLock(tables, se) - if err != nil { - logutil.BgLogger().Info("clean dead table lock failed.", zap.String("category", "ddl"), zap.Error(err)) - } - } - case <-d.ctx.Done(): - return - } - } -} - -// SwitchMDL enables MDL or disable MDL. -func (d *ddl) SwitchMDL(enable bool) error { - isEnableBefore := variable.EnableMDL.Load() - if isEnableBefore == enable { - return nil - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() - - // Check if there is any DDL running. - // This check can not cover every corner cases, so users need to guarantee that there is no DDL running by themselves. - sessCtx, err := d.sessPool.Get() - if err != nil { - return err - } - defer d.sessPool.Put(sessCtx) - se := sess.NewSession(sessCtx) - rows, err := se.Execute(ctx, "select 1 from mysql.tidb_ddl_job", "check job") - if err != nil { - return err - } - if len(rows) != 0 { - return errors.New("please wait for all jobs done") - } - - variable.EnableMDL.Store(enable) - err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), d.store, true, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - oldEnable, _, err := m.GetMetadataLock() - if err != nil { - return err - } - if oldEnable != enable { - err = m.SetMetadataLock(enable) - } - return err - }) - if err != nil { - logutil.BgLogger().Warn("switch metadata lock feature", zap.String("category", "ddl"), zap.Bool("enable", enable), zap.Error(err)) - return err - } - logutil.BgLogger().Info("switch metadata lock feature", zap.String("category", "ddl"), zap.Bool("enable", enable)) - return nil -} - -// RecoverInfo contains information needed by DDL.RecoverTable. -type RecoverInfo struct { - SchemaID int64 - TableInfo *model.TableInfo - DropJobID int64 - SnapshotTS uint64 - AutoIDs meta.AutoIDGroup - OldSchemaName string - OldTableName string -} - -// RecoverSchemaInfo contains information needed by DDL.RecoverSchema. -type RecoverSchemaInfo struct { - *model.DBInfo - RecoverTabsInfo []*RecoverInfo - DropJobID int64 - SnapshotTS uint64 - OldSchemaName model.CIStr -} - -// delayForAsyncCommit sleeps `SafeWindow + AllowedClockDrift` before a DDL job finishes. -// It should be called before any DDL that could break data consistency. -// This provides a safe window for async commit and 1PC to commit with an old schema. -func delayForAsyncCommit() { - if variable.EnableMDL.Load() { - // If metadata lock is enabled. The transaction of DDL must begin after prewrite of the async commit transaction, - // then the commit ts of DDL must be greater than the async commit transaction. In this case, the corresponding schema of the async commit transaction - // is correct. But if metadata lock is disabled, we can't ensure that the corresponding schema of the async commit transaction isn't change. - return - } - cfg := config.GetGlobalConfig().TiKVClient.AsyncCommit - duration := cfg.SafeWindow + cfg.AllowedClockDrift - logutil.BgLogger().Info("sleep before DDL finishes to make async commit and 1PC safe", - zap.Duration("duration", duration)) - time.Sleep(duration) -} - -var ( - // RunInGoTest is used to identify whether ddl in running in the test. - RunInGoTest bool -) - -// GetDropOrTruncateTableInfoFromJobsByStore implements GetDropOrTruncateTableInfoFromJobs -func GetDropOrTruncateTableInfoFromJobsByStore(jobs []*model.Job, gcSafePoint uint64, getTable func(uint64, int64, int64) (*model.TableInfo, error), fn func(*model.Job, *model.TableInfo) (bool, error)) (bool, error) { - for _, job := range jobs { - // Check GC safe point for getting snapshot infoSchema. - err := gcutil.ValidateSnapshotWithGCSafePoint(job.StartTS, gcSafePoint) - if err != nil { - return false, err - } - if job.Type != model.ActionDropTable && job.Type != model.ActionTruncateTable { - continue - } - - tbl, err := getTable(job.StartTS, job.SchemaID, job.TableID) - if err != nil { - if meta.ErrDBNotExists.Equal(err) { - // The dropped/truncated DDL maybe execute failed that caused by the parallel DDL execution, - // then can't find the table from the snapshot info-schema. Should just ignore error here, - // see more in TestParallelDropSchemaAndDropTable. - continue - } - return false, err - } - if tbl == nil { - // The dropped/truncated DDL maybe execute failed that caused by the parallel DDL execution, - // then can't find the table from the snapshot info-schema. Should just ignore error here, - // see more in TestParallelDropSchemaAndDropTable. - continue - } - finish, err := fn(job, tbl) - if err != nil || finish { - return finish, err - } - } - return false, nil -} - -// Info is for DDL information. -type Info struct { - SchemaVer int64 - ReorgHandle kv.Key // It's only used for DDL information. - Jobs []*model.Job // It's the currently running jobs. -} - -// GetDDLInfoWithNewTxn returns DDL information using a new txn. -func GetDDLInfoWithNewTxn(s sessionctx.Context) (*Info, error) { - se := sess.NewSession(s) - err := se.Begin() - if err != nil { - return nil, err - } - info, err := GetDDLInfo(s) - se.Rollback() - return info, err -} - -// GetDDLInfo returns DDL information. -func GetDDLInfo(s sessionctx.Context) (*Info, error) { - var err error - info := &Info{} - se := sess.NewSession(s) - txn, err := se.Txn() - if err != nil { - return nil, errors.Trace(err) - } - t := meta.NewMeta(txn) - info.Jobs = make([]*model.Job, 0, 2) - var generalJob, reorgJob *model.Job - generalJob, reorgJob, err = get2JobsFromTable(se) - if err != nil { - return nil, errors.Trace(err) - } - - if generalJob != nil { - info.Jobs = append(info.Jobs, generalJob) - } - - if reorgJob != nil { - info.Jobs = append(info.Jobs, reorgJob) - } - - info.SchemaVer, err = t.GetSchemaVersionWithNonEmptyDiff() - if err != nil { - return nil, errors.Trace(err) - } - if reorgJob == nil { - return info, nil - } - - _, info.ReorgHandle, _, _, err = newReorgHandler(se).GetDDLReorgHandle(reorgJob) - if err != nil { - if meta.ErrDDLReorgElementNotExist.Equal(err) { - return info, nil - } - return nil, errors.Trace(err) - } - - return info, nil -} - -func get2JobsFromTable(sess *sess.Session) (*model.Job, *model.Job, error) { - var generalJob, reorgJob *model.Job - jobs, err := getJobsBySQL(sess, JobTable, "not reorg order by job_id limit 1") - if err != nil { - return nil, nil, errors.Trace(err) - } - - if len(jobs) != 0 { - generalJob = jobs[0] - } - jobs, err = getJobsBySQL(sess, JobTable, "reorg order by job_id limit 1") - if err != nil { - return nil, nil, errors.Trace(err) - } - if len(jobs) != 0 { - reorgJob = jobs[0] - } - return generalJob, reorgJob, nil -} - -// cancelRunningJob cancel a DDL job that is in the concurrent state. -func cancelRunningJob(_ *sess.Session, job *model.Job, - byWho model.AdminCommandOperator) (err error) { - // These states can't be cancelled. - if job.IsDone() || job.IsSynced() { - return dbterror.ErrCancelFinishedDDLJob.GenWithStackByArgs(job.ID) - } - - // If the state is rolling back, it means the work is cleaning the data after cancelling the job. - if job.IsCancelled() || job.IsRollingback() || job.IsRollbackDone() { - return nil - } - - if !job.IsRollbackable() { - return dbterror.ErrCannotCancelDDLJob.GenWithStackByArgs(job.ID) - } - job.State = model.JobStateCancelling - job.AdminOperator = byWho - return nil -} - -// pauseRunningJob check and pause the running Job -func pauseRunningJob(_ *sess.Session, job *model.Job, - byWho model.AdminCommandOperator) (err error) { - if job.IsPausing() || job.IsPaused() { - return dbterror.ErrPausedDDLJob.GenWithStackByArgs(job.ID) - } - if !job.IsPausable() { - errMsg := fmt.Sprintf("state [%s] or schema state [%s]", job.State.String(), job.SchemaState.String()) - err = dbterror.ErrCannotPauseDDLJob.GenWithStackByArgs(job.ID, errMsg) - if err != nil { - return err - } - } - - job.State = model.JobStatePausing - job.AdminOperator = byWho - return nil -} - -// resumePausedJob check and resume the Paused Job -func resumePausedJob(_ *sess.Session, job *model.Job, - byWho model.AdminCommandOperator) (err error) { - if !job.IsResumable() { - errMsg := fmt.Sprintf("job has not been paused, job state:%s, schema state:%s", - job.State, job.SchemaState) - return dbterror.ErrCannotResumeDDLJob.GenWithStackByArgs(job.ID, errMsg) - } - // The Paused job should only be resumed by who paused it - if job.AdminOperator != byWho { - errMsg := fmt.Sprintf("job has been paused by [%s], should not resumed by [%s]", - job.AdminOperator.String(), byWho.String()) - return dbterror.ErrCannotResumeDDLJob.GenWithStackByArgs(job.ID, errMsg) - } - - job.State = model.JobStateQueueing - - return nil -} - -// processJobs command on the Job according to the process -func processJobs(process func(*sess.Session, *model.Job, model.AdminCommandOperator) (err error), - sessCtx sessionctx.Context, - ids []int64, - byWho model.AdminCommandOperator) (jobErrs []error, err error) { - failpoint.Inject("mockFailedCommandOnConcurencyDDL", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(nil, errors.New("mock failed admin command on ddl jobs")) - } - }) - - if len(ids) == 0 { - return nil, nil - } - - ns := sess.NewSession(sessCtx) - // We should process (and try) all the jobs in one Transaction. - for tryN := uint(0); tryN < 3; tryN++ { - jobErrs = make([]error, len(ids)) - // Need to figure out which one could not be paused - jobMap := make(map[int64]int, len(ids)) - idsStr := make([]string, 0, len(ids)) - for idx, id := range ids { - jobMap[id] = idx - idsStr = append(idsStr, strconv.FormatInt(id, 10)) - } - - err = ns.Begin() - if err != nil { - return nil, err - } - jobs, err := getJobsBySQL(ns, JobTable, fmt.Sprintf("job_id in (%s) order by job_id", strings.Join(idsStr, ", "))) - if err != nil { - ns.Rollback() - return nil, err - } - - for _, job := range jobs { - i, ok := jobMap[job.ID] - if !ok { - logutil.BgLogger().Debug("Job ID from meta is not consistent with requested job id,", - zap.Int64("fetched job ID", job.ID)) - jobErrs[i] = dbterror.ErrInvalidDDLJob.GenWithStackByArgs(job.ID) - continue - } - delete(jobMap, job.ID) - - err = process(ns, job, byWho) - if err != nil { - jobErrs[i] = err - continue - } - - err = updateDDLJob2Table(ns, job, false) - if err != nil { - jobErrs[i] = err - continue - } - } - - failpoint.Inject("mockCommitFailedOnDDLCommand", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(jobErrs, errors.New("mock commit failed on admin command on ddl jobs")) - } - }) - - // There may be some conflict during the update, try it again - if err = ns.Commit(); err != nil { - continue - } - - for id, idx := range jobMap { - jobErrs[idx] = dbterror.ErrDDLJobNotFound.GenWithStackByArgs(id) - } - - return jobErrs, nil - } - - return jobErrs, err -} - -// CancelJobs cancels the DDL jobs according to user command. -func CancelJobs(se sessionctx.Context, ids []int64) (errs []error, err error) { - return processJobs(cancelRunningJob, se, ids, model.AdminCommandByEndUser) -} - -// PauseJobs pause all the DDL jobs according to user command. -func PauseJobs(se sessionctx.Context, ids []int64) ([]error, error) { - return processJobs(pauseRunningJob, se, ids, model.AdminCommandByEndUser) -} - -// ResumeJobs resume all the DDL jobs according to user command. -func ResumeJobs(se sessionctx.Context, ids []int64) ([]error, error) { - return processJobs(resumePausedJob, se, ids, model.AdminCommandByEndUser) -} - -// CancelJobsBySystem cancels Jobs because of internal reasons. -func CancelJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { - return processJobs(cancelRunningJob, se, ids, model.AdminCommandBySystem) -} - -// PauseJobsBySystem pauses Jobs because of internal reasons. -func PauseJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { - return processJobs(pauseRunningJob, se, ids, model.AdminCommandBySystem) -} - -// ResumeJobsBySystem resumes Jobs that are paused by TiDB itself. -func ResumeJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { - return processJobs(resumePausedJob, se, ids, model.AdminCommandBySystem) -} - -// pprocessAllJobs processes all the jobs in the job table, 100 jobs at a time in case of high memory usage. -func processAllJobs(process func(*sess.Session, *model.Job, model.AdminCommandOperator) (err error), - se sessionctx.Context, byWho model.AdminCommandOperator) (map[int64]error, error) { - var err error - var jobErrs = make(map[int64]error) - - ns := sess.NewSession(se) - err = ns.Begin() - if err != nil { - return nil, err - } - - var jobID int64 - var jobIDMax int64 - var limit = 100 - for { - var jobs []*model.Job - jobs, err = getJobsBySQL(ns, JobTable, - fmt.Sprintf("job_id >= %s order by job_id asc limit %s", - strconv.FormatInt(jobID, 10), - strconv.FormatInt(int64(limit), 10))) - if err != nil { - ns.Rollback() - return nil, err - } - - for _, job := range jobs { - err = process(ns, job, byWho) - if err != nil { - jobErrs[job.ID] = err - continue - } - - err = updateDDLJob2Table(ns, job, false) - if err != nil { - jobErrs[job.ID] = err - continue - } - } - - // Just in case the job ID is not sequential - if len(jobs) > 0 && jobs[len(jobs)-1].ID > jobIDMax { - jobIDMax = jobs[len(jobs)-1].ID - } - - // If rows returned is smaller than $limit, then there is no more records - if len(jobs) < limit { - break - } - - jobID = jobIDMax + 1 - } - - err = ns.Commit() - if err != nil { - return nil, err - } - return jobErrs, nil -} - -// PauseAllJobsBySystem pauses all running Jobs because of internal reasons. -func PauseAllJobsBySystem(se sessionctx.Context) (map[int64]error, error) { - return processAllJobs(pauseRunningJob, se, model.AdminCommandBySystem) -} - -// ResumeAllJobsBySystem resumes all paused Jobs because of internal reasons. -func ResumeAllJobsBySystem(se sessionctx.Context) (map[int64]error, error) { - return processAllJobs(resumePausedJob, se, model.AdminCommandBySystem) -} - -// GetAllDDLJobs get all DDL jobs and sorts jobs by job.ID. -func GetAllDDLJobs(se sessionctx.Context) ([]*model.Job, error) { - return getJobsBySQL(sess.NewSession(se), JobTable, "1 order by job_id") -} - -// DefNumHistoryJobs is default value of the default number of history job -const DefNumHistoryJobs = 10 - -const batchNumHistoryJobs = 128 - -// GetLastNHistoryDDLJobs returns the DDL history jobs and an error. -// The maximum count of history jobs is num. -func GetLastNHistoryDDLJobs(t *meta.Meta, maxNumJobs int) ([]*model.Job, error) { - iterator, err := GetLastHistoryDDLJobsIterator(t) - if err != nil { - return nil, errors.Trace(err) - } - return iterator.GetLastJobs(maxNumJobs, nil) -} - -// IterHistoryDDLJobs iterates history DDL jobs until the `finishFn` return true or error. -func IterHistoryDDLJobs(txn kv.Transaction, finishFn func([]*model.Job) (bool, error)) error { - txnMeta := meta.NewMeta(txn) - iter, err := GetLastHistoryDDLJobsIterator(txnMeta) - if err != nil { - return err - } - cacheJobs := make([]*model.Job, 0, DefNumHistoryJobs) - for { - cacheJobs, err = iter.GetLastJobs(DefNumHistoryJobs, cacheJobs) - if err != nil || len(cacheJobs) == 0 { - return err - } - finish, err := finishFn(cacheJobs) - if err != nil || finish { - return err - } - } -} - -// IterAllDDLJobs will iterates running DDL jobs first, return directly if `finishFn` return true or error, -// then iterates history DDL jobs until the `finishFn` return true or error. -func IterAllDDLJobs(ctx sessionctx.Context, txn kv.Transaction, finishFn func([]*model.Job) (bool, error)) error { - jobs, err := GetAllDDLJobs(ctx) - if err != nil { - return err - } - - finish, err := finishFn(jobs) - if err != nil || finish { - return err - } - return IterHistoryDDLJobs(txn, finishFn) -} - -// GetLastHistoryDDLJobsIterator gets latest N history DDL jobs iterator. -func GetLastHistoryDDLJobsIterator(m *meta.Meta) (meta.LastJobIterator, error) { - return m.GetLastHistoryDDLJobsIterator() -} - -// GetAllHistoryDDLJobs get all the done DDL jobs. -func GetAllHistoryDDLJobs(m *meta.Meta) ([]*model.Job, error) { - iterator, err := GetLastHistoryDDLJobsIterator(m) - if err != nil { - return nil, errors.Trace(err) - } - allJobs := make([]*model.Job, 0, batchNumHistoryJobs) - for { - jobs, err := iterator.GetLastJobs(batchNumHistoryJobs, nil) - if err != nil { - return nil, errors.Trace(err) - } - allJobs = append(allJobs, jobs...) - if len(jobs) < batchNumHistoryJobs { - break - } - } - // sort job. - slices.SortFunc(allJobs, func(i, j *model.Job) int { - return cmp.Compare(i.ID, j.ID) - }) - return allJobs, nil -} - -// ScanHistoryDDLJobs get some of the done DDL jobs. -// When the DDL history is quite large, GetAllHistoryDDLJobs() API can't work well, because it makes the server OOM. -// The result is in descending order by job ID. -func ScanHistoryDDLJobs(m *meta.Meta, startJobID int64, limit int) ([]*model.Job, error) { - var iter meta.LastJobIterator - var err error - if startJobID == 0 { - iter, err = m.GetLastHistoryDDLJobsIterator() - } else { - if limit == 0 { - return nil, errors.New("when 'start_job_id' is specified, it must work with a 'limit'") - } - iter, err = m.GetHistoryDDLJobsIterator(startJobID) - } - if err != nil { - return nil, errors.Trace(err) - } - return iter.GetLastJobs(limit, nil) -} - -// GetHistoryJobByID return history DDL job by ID. -func GetHistoryJobByID(sess sessionctx.Context, id int64) (*model.Job, error) { - err := sessiontxn.NewTxn(context.Background(), sess) - if err != nil { - return nil, err - } - defer func() { - // we can ignore the commit error because this txn is readonly. - _ = sess.CommitTxn(context.Background()) - }() - txn, err := sess.Txn(true) - if err != nil { - return nil, err - } - t := meta.NewMeta(txn) - job, err := t.GetHistoryDDLJob(id) - return job, errors.Trace(err) -} - -// AddHistoryDDLJob record the history job. -func AddHistoryDDLJob(sess *sess.Session, t *meta.Meta, job *model.Job, updateRawArgs bool) error { - err := addHistoryDDLJob2Table(sess, job, updateRawArgs) - if err != nil { - logutil.BgLogger().Info("failed to add DDL job to history table", zap.String("category", "ddl"), zap.Error(err)) - } - // we always add history DDL job to job list at this moment. - return t.AddHistoryDDLJob(job, updateRawArgs) -} - -// addHistoryDDLJob2Table adds DDL job to history table. -func addHistoryDDLJob2Table(sess *sess.Session, job *model.Job, updateRawArgs bool) error { - b, err := job.Encode(updateRawArgs) - if err != nil { - return err - } - _, err = sess.Execute(context.Background(), - fmt.Sprintf("insert ignore into mysql.tidb_ddl_history(job_id, job_meta, db_name, table_name, schema_ids, table_ids, create_time) values (%d, %s, %s, %s, %s, %s, %v)", - job.ID, util.WrapKey2String(b), strconv.Quote(job.SchemaName), strconv.Quote(job.TableName), - strconv.Quote(strconv.FormatInt(job.SchemaID, 10)), - strconv.Quote(strconv.FormatInt(job.TableID, 10)), - strconv.Quote(model.TSConvert2Time(job.StartTS).String())), - "insert_history") - return errors.Trace(err) -} diff --git a/ddl/ddl_error_test.go b/ddl/ddl_error_test.go deleted file mode 100644 index 180d20955001d..0000000000000 --- a/ddl/ddl_error_test.go +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. -// - -package ddl_test - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -// This test file contains tests that test the expected or unexpected DDL error. -// For expected error, we use SQL to check it. -// For unexpected error, we mock a SQL job to check it. - -func TestTableError(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, testLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table testDrop(a int)") - // Schema ID is wrong, so dropping table is failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - err := tk.ExecToErr("drop table testDrop") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) - - // Table ID is wrong, so dropping table is failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobTableId", `return(-1)`)) - err = tk.ExecToErr("drop table testDrop") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobTableId")) - - // Args is wrong, so creating table is failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobArg", `return(true)`)) - err = tk.ExecToErr("create table test.t1(a int)") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobArg")) - - // Table exists, so creating table is failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - err = tk.ExecToErr("create table test.t1(a int)") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) - // Table exists, so creating table is failed. - tk.MustExec("create table test.t2(a int)") - tk.MustGetErrCode("create table test.t2(a int)", errno.ErrTableExists) -} - -func TestViewError(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, testLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - - // Args is wrong, so creating view is failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobArg", `return(true)`)) - err := tk.ExecToErr("create view v as select * from t") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobArg")) -} - -func TestForeignKeyError(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, testLease) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int, index(a))") - tk.MustExec("create table t1 (a int, FOREIGN KEY fk(a) REFERENCES t(a))") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - err := tk.ExecToErr("alter table t1 add foreign key idx(a) REFERENCES t(a)") - require.Error(t, err) - err = tk.ExecToErr("alter table t1 drop index fk") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) -} - -func TestIndexError(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, testLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - tk.MustExec("alter table t add index a(a)") - - // Schema ID is wrong. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - err := tk.ExecToErr("alter table t add index idx(a)") - require.Error(t, err) - err = tk.ExecToErr("alter table t1 drop a") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) - - // for adding index - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobArg", `return(true)`)) - err = tk.ExecToErr("alter table t add index idx(a)") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop index a") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobArg")) -} - -func TestColumnError(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, testLease) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int, aa int, ab int)") - tk.MustExec("alter table t add index a(a)") - - // Invalid schema ID. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - err := tk.ExecToErr("alter table t add column ta int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t add column ta int, add column tb int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa, drop column ab") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) - - // Invalid table ID. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobTableId", `return(-1)`)) - err = tk.ExecToErr("alter table t add column ta int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t add column ta int, add column tb int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa, drop column ab") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobTableId")) - - // Invalid argument. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockModifyJobArg", `return(true)`)) - err = tk.ExecToErr("alter table t add column ta int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa") - require.Error(t, err) - err = tk.ExecToErr("alter table t add column ta int, add column tb int") - require.Error(t, err) - err = tk.ExecToErr("alter table t drop column aa, drop column ab") - require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockModifyJobArg")) - - tk.MustGetErrCode("alter table t add column c int after c5", errno.ErrBadField) - tk.MustGetErrCode("alter table t drop column c5", errno.ErrCantDropFieldOrKey) - tk.MustGetErrCode("alter table t add column c int after c5, add column d int", errno.ErrBadField) - tk.MustGetErrCode("alter table t drop column ab, drop column c5", errno.ErrCantDropFieldOrKey) -} - -func TestCreateDatabaseError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId", `return(-1)`)) - tk.MustExec("create database db1;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockModifyJobSchemaId")) -} diff --git a/ddl/ddl_test.go b/ddl/ddl_test.go deleted file mode 100644 index 73c28ffed3aa8..0000000000000 --- a/ddl/ddl_test.go +++ /dev/null @@ -1,287 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -// DDLForTest exports for testing. -type DDLForTest interface { - // SetInterceptor sets the interceptor. - SetInterceptor(h Interceptor) - NewReorgCtx(jobID int64, rowCount int64) *reorgCtx - GetReorgCtx(jobID int64) *reorgCtx - RemoveReorgCtx(id int64) -} - -// SetInterceptor implements DDL.SetInterceptor interface. -func (d *ddl) SetInterceptor(i Interceptor) { - d.mu.Lock() - defer d.mu.Unlock() - - d.mu.interceptor = i -} - -// IsReorgCanceled exports for testing. -func (rc *reorgCtx) IsReorgCanceled() bool { - return rc.isReorgCanceled() -} - -// NewReorgCtx exports for testing. -func (d *ddl) NewReorgCtx(jobID int64, rowCount int64) *reorgCtx { - return d.newReorgCtx(jobID, rowCount) -} - -// GetReorgCtx exports for testing. -func (d *ddl) GetReorgCtx(jobID int64) *reorgCtx { - return d.getReorgCtx(jobID) -} - -// RemoveReorgCtx exports for testing. -func (d *ddl) RemoveReorgCtx(id int64) { - d.removeReorgCtx(id) -} - -// JobNeedGCForTest is only used for test. -var JobNeedGCForTest = jobNeedGC - -func createMockStore(t *testing.T) kv.Storage { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - return store -} - -func TestGetIntervalFromPolicy(t *testing.T) { - policy := []time.Duration{ - 1 * time.Second, - 2 * time.Second, - } - var ( - val time.Duration - changed bool - ) - - val, changed = getIntervalFromPolicy(policy, 0) - require.Equal(t, val, 1*time.Second) - require.True(t, changed) - - val, changed = getIntervalFromPolicy(policy, 1) - require.Equal(t, val, 2*time.Second) - require.True(t, changed) - - val, changed = getIntervalFromPolicy(policy, 2) - require.Equal(t, val, 2*time.Second) - require.False(t, changed) - - val, changed = getIntervalFromPolicy(policy, 3) - require.Equal(t, val, 2*time.Second) - require.False(t, changed) -} - -func colDefStrToFieldType(t *testing.T, str string, ctx sessionctx.Context) *types.FieldType { - sqlA := "alter table t modify column a " + str - stmt, err := parser.New().ParseOneStmt(sqlA, "", "") - require.NoError(t, err) - colDef := stmt.(*ast.AlterTableStmt).Specs[0].NewColumns[0] - chs, coll := charset.GetDefaultCharsetAndCollate() - col, _, err := buildColumnAndConstraint(ctx, 0, colDef, nil, chs, coll) - require.NoError(t, err) - return &col.FieldType -} - -func TestModifyColumn(t *testing.T) { - ctx := mock.NewContext() - tests := []struct { - origin string - to string - err error - }{ - {"int", "bigint", nil}, - {"int", "int unsigned", nil}, - {"varchar(10)", "text", nil}, - {"varbinary(10)", "blob", nil}, - {"text", "blob", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")}, - {"varchar(10)", "varchar(8)", nil}, - {"varchar(10)", "varchar(11)", nil}, - {"varchar(10) character set utf8 collate utf8_bin", "varchar(10) character set utf8", nil}, - {"decimal(2,1)", "decimal(3,2)", nil}, - {"decimal(2,1)", "decimal(2,2)", nil}, - {"decimal(2,1)", "decimal(2,1)", nil}, - {"decimal(2,1)", "int", nil}, - {"decimal", "int", nil}, - {"decimal(2,1)", "bigint", nil}, - {"int", "varchar(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from binary to gbk")}, - {"varchar(10) character set gbk", "int", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to binary")}, - {"varchar(10) character set gbk", "varchar(10) character set utf8", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to utf8")}, - {"varchar(10) character set gbk", "char(10) character set utf8", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to utf8")}, - {"varchar(10) character set utf8", "char(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8 to gbk")}, - {"varchar(10) character set utf8", "varchar(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8 to gbk")}, - {"varchar(10) character set gbk", "varchar(255) character set gbk", nil}, - } - for _, tt := range tests { - ftA := colDefStrToFieldType(t, tt.origin, ctx) - ftB := colDefStrToFieldType(t, tt.to, ctx) - err := checkModifyTypes(ftA, ftB, false) - if err == nil { - require.NoErrorf(t, tt.err, "origin:%v, to:%v", tt.origin, tt.to) - } else { - require.EqualError(t, err, tt.err.Error()) - } - } -} - -func TestFieldCase(t *testing.T) { - var fields = []string{"field", "Field"} - colObjects := make([]*model.ColumnInfo, len(fields)) - for i, name := range fields { - colObjects[i] = &model.ColumnInfo{ - Name: model.NewCIStr(name), - } - } - err := checkDuplicateColumn(colObjects) - require.EqualError(t, err, infoschema.ErrColumnExists.GenWithStackByArgs("Field").Error()) -} - -func TestIgnorableSpec(t *testing.T) { - specs := []ast.AlterTableType{ - ast.AlterTableOption, - ast.AlterTableAddColumns, - ast.AlterTableAddConstraint, - ast.AlterTableDropColumn, - ast.AlterTableDropPrimaryKey, - ast.AlterTableDropIndex, - ast.AlterTableDropForeignKey, - ast.AlterTableModifyColumn, - ast.AlterTableChangeColumn, - ast.AlterTableRenameTable, - ast.AlterTableAlterColumn, - } - for _, spec := range specs { - require.False(t, isIgnorableSpec(spec)) - } - - ignorableSpecs := []ast.AlterTableType{ - ast.AlterTableLock, - ast.AlterTableAlgorithm, - } - for _, spec := range ignorableSpecs { - require.True(t, isIgnorableSpec(spec)) - } -} - -func TestBuildJobDependence(t *testing.T) { - store := createMockStore(t) - defer func() { - require.NoError(t, store.Close()) - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - // Add some non-add-index jobs. - job1 := &model.Job{ID: 1, TableID: 1, Type: model.ActionAddColumn} - job2 := &model.Job{ID: 2, TableID: 1, Type: model.ActionCreateTable} - job3 := &model.Job{ID: 3, TableID: 2, Type: model.ActionDropColumn} - job6 := &model.Job{ID: 6, TableID: 1, Type: model.ActionDropTable} - job7 := &model.Job{ID: 7, TableID: 2, Type: model.ActionModifyColumn} - job9 := &model.Job{ID: 9, SchemaID: 111, Type: model.ActionDropSchema} - job11 := &model.Job{ID: 11, TableID: 2, Type: model.ActionRenameTable, Args: []interface{}{int64(111), "old db name"}} - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - require.NoError(t, m.EnQueueDDLJob(job1)) - require.NoError(t, m.EnQueueDDLJob(job2)) - require.NoError(t, m.EnQueueDDLJob(job3)) - require.NoError(t, m.EnQueueDDLJob(job6)) - require.NoError(t, m.EnQueueDDLJob(job7)) - require.NoError(t, m.EnQueueDDLJob(job9)) - require.NoError(t, m.EnQueueDDLJob(job11)) - return nil - }) - require.NoError(t, err) - job4 := &model.Job{ID: 4, TableID: 1, Type: model.ActionAddIndex} - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err := buildJobDependence(m, job4) - require.NoError(t, err) - require.Equal(t, job4.DependencyID, int64(2)) - return nil - }) - require.NoError(t, err) - job5 := &model.Job{ID: 5, TableID: 2, Type: model.ActionAddIndex} - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err := buildJobDependence(m, job5) - require.NoError(t, err) - require.Equal(t, job5.DependencyID, int64(3)) - return nil - }) - require.NoError(t, err) - job8 := &model.Job{ID: 8, TableID: 3, Type: model.ActionAddIndex} - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err := buildJobDependence(m, job8) - require.NoError(t, err) - require.Equal(t, job8.DependencyID, int64(0)) - return nil - }) - require.NoError(t, err) - job10 := &model.Job{ID: 10, SchemaID: 111, TableID: 3, Type: model.ActionAddIndex} - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err := buildJobDependence(m, job10) - require.NoError(t, err) - require.Equal(t, job10.DependencyID, int64(9)) - return nil - }) - require.NoError(t, err) - job12 := &model.Job{ID: 12, SchemaID: 112, TableID: 2, Type: model.ActionAddIndex} - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err := buildJobDependence(m, job12) - require.NoError(t, err) - require.Equal(t, job12.DependencyID, int64(11)) - return nil - }) - require.NoError(t, err) -} - -func TestError(t *testing.T) { - kvErrs := []*terror.Error{ - dbterror.ErrDDLJobNotFound, - dbterror.ErrCancelFinishedDDLJob, - dbterror.ErrCannotCancelDDLJob, - } - for _, err := range kvErrs { - code := terror.ToSQLError(err).Code - require.NotEqual(t, mysql.ErrUnknown, code) - require.Equal(t, uint16(err.Code()), code) - } -} diff --git a/ddl/fail_test.go b/ddl/fail_test.go deleted file mode 100644 index d79be651bde7f..0000000000000 --- a/ddl/fail_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. -package ddl_test - -import ( - "strconv" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestFailBeforeDecodeArgs(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t1 (c1 int, c2 int);") - tk.MustExec("insert t1 values (1, 2);") - - var tableID int64 - rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") - tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) - tableID = int64(tableIDi) - - d := dom.DDL() - tc := &callback.TestDDLCallback{Do: dom} - - first := true - stateCnt := 0 - tc.OnJobRunBeforeExported = func(job *model.Job) { - // It can be other schema states except failed schema state. - // This schema state can only appear once. - if job.SchemaState == model.StateWriteOnly { - stateCnt++ - } else if job.SchemaState == model.StateWriteReorganization { - if first { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/errorBeforeDecodeArgs", `return(true)`)) - first = false - } else { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/errorBeforeDecodeArgs")) - } - } - } - d.SetHook(tc) - defaultValue := int64(3) - jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c3", "", defaultValue, dom) - // Make sure the schema state only appears once. - require.Equal(t, 1, stateCnt) - testCheckJobDone(t, store, jobID, true) -} diff --git a/ddl/foreign_key.go b/ddl/foreign_key.go deleted file mode 100644 index f7a423f6d46e8..0000000000000 --- a/ddl/foreign_key.go +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/sqlexec" -) - -func (w *worker) onCreateForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return ver, errors.Trace(err) - } - - var fkInfo model.FKInfo - var fkCheck bool - err = job.DecodeArgs(&fkInfo, &fkCheck) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - if job.IsRollingback() { - return dropForeignKey(d, t, job, tblInfo, fkInfo.Name) - } - switch job.SchemaState { - case model.StateNone: - err = checkAddForeignKeyValidInOwner(d, t, job.SchemaName, tblInfo, &fkInfo, fkCheck) - if err != nil { - job.State = model.JobStateCancelled - return ver, err - } - fkInfo.State = model.StateWriteOnly - fkInfo.ID = allocateFKIndexID(tblInfo) - tblInfo.ForeignKeys = append(tblInfo.ForeignKeys, &fkInfo) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateWriteOnly - return ver, nil - case model.StateWriteOnly: - err = checkForeignKeyConstrain(w, job.SchemaName, tblInfo.Name.L, &fkInfo, fkCheck) - if err != nil { - job.State = model.JobStateRollingback - return ver, err - } - tblInfo.ForeignKeys[len(tblInfo.ForeignKeys)-1].State = model.StateWriteReorganization - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateWriteReorganization - case model.StateWriteReorganization: - tblInfo.ForeignKeys[len(tblInfo.ForeignKeys)-1].State = model.StatePublic - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.SchemaState = model.StatePublic - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - default: - return ver, dbterror.ErrInvalidDDLState.GenWithStack("foreign key", fkInfo.State) - } - return ver, nil -} - -func onDropForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return ver, errors.Trace(err) - } - - var fkName model.CIStr - err = job.DecodeArgs(&fkName) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - return dropForeignKey(d, t, job, tblInfo, fkName) -} - -func dropForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, fkName model.CIStr) (ver int64, err error) { - var fkInfo *model.FKInfo - for _, fk := range tblInfo.ForeignKeys { - if fk.Name.L == fkName.L { - fkInfo = fk - break - } - } - if fkInfo == nil { - job.State = model.JobStateCancelled - return ver, infoschema.ErrForeignKeyNotExists.GenWithStackByArgs(fkName) - } - nfks := tblInfo.ForeignKeys[:0] - for _, fk := range tblInfo.ForeignKeys { - if fk.Name.L != fkName.L { - nfks = append(nfks, fk) - } - } - tblInfo.ForeignKeys = nfks - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - if job.IsRollingback() { - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - } else { - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - } - job.SchemaState = model.StateNone - return ver, err -} - -func allocateFKIndexID(tblInfo *model.TableInfo) int64 { - tblInfo.MaxForeignKeyID++ - return tblInfo.MaxForeignKeyID -} - -func checkTableForeignKeysValid(sctx sessionctx.Context, is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo) error { - if !variable.EnableForeignKey.Load() { - return nil - } - fkCheck := sctx.GetSessionVars().ForeignKeyChecks - for _, fk := range tbInfo.ForeignKeys { - if fk.Version < model.FKVersion1 { - continue - } - err := checkTableForeignKeyValid(is, schema, tbInfo, fk, fkCheck) - if err != nil { - return err - } - } - - referredFKInfos := is.GetTableReferredForeignKeys(schema, tbInfo.Name.L) - for _, referredFK := range referredFKInfos { - childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - return err - } - fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) - if fk == nil { - continue - } - err = checkTableForeignKey(tbInfo, childTable.Meta(), fk) - if err != nil { - return err - } - } - return nil -} - -func checkTableForeignKeyValid(is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { - var referTblInfo *model.TableInfo - if fk.RefSchema.L == schema && fk.RefTable.L == tbInfo.Name.L { - same := true - for i, col := range fk.Cols { - if col.L != fk.RefCols[i].L { - same = false - break - } - } - if same { - // self-reference with same columns is not support. - return infoschema.ErrCannotAddForeign - } - referTblInfo = tbInfo - } else { - referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) - if err != nil { - if (infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err)) && !fkCheck { - return nil - } - return infoschema.ErrForeignKeyCannotOpenParent.GenWithStackByArgs(fk.RefTable.O) - } - referTblInfo = referTable.Meta() - } - return checkTableForeignKey(referTblInfo, tbInfo, fk) -} - -func getAndCheckLatestInfoSchema(d *ddlCtx, _ *meta.Meta) (infoschema.InfoSchema, error) { - // TODO(crazycs520): fix me, need to make sure the `d.infoCache` is the latest infoschema. - return d.infoCache.GetLatest(), nil -} - -func checkTableForeignKeyValidInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, fkCheck bool) (retryable bool, _ error) { - if !variable.EnableForeignKey.Load() { - return false, nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return true, err - } - for _, fk := range tbInfo.ForeignKeys { - if fk.Version < model.FKVersion1 { - continue - } - var referTableInfo *model.TableInfo - if fk.RefSchema.L == job.SchemaName && fk.RefTable.L == tbInfo.Name.L { - referTableInfo = tbInfo - } else { - referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) - if err != nil { - if !fkCheck && (infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err)) { - continue - } - return false, err - } - referTableInfo = referTable.Meta() - } - - err := checkTableForeignKey(referTableInfo, tbInfo, fk) - if err != nil { - return false, err - } - } - referredFKInfos := is.GetTableReferredForeignKeys(job.SchemaName, tbInfo.Name.L) - for _, referredFK := range referredFKInfos { - childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - return false, err - } - fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) - if fk == nil { - continue - } - err = checkTableForeignKey(tbInfo, childTable.Meta(), fk) - if err != nil { - return false, err - } - } - return false, nil -} - -func checkTableForeignKey(referTblInfo, tblInfo *model.TableInfo, fkInfo *model.FKInfo) error { - if referTblInfo.TempTableType != model.TempTableNone || tblInfo.TempTableType != model.TempTableNone { - return infoschema.ErrCannotAddForeign - } - if referTblInfo.TTLInfo != nil { - return dbterror.ErrUnsupportedTTLReferencedByFK - } - if referTblInfo.GetPartitionInfo() != nil || tblInfo.GetPartitionInfo() != nil { - return infoschema.ErrForeignKeyOnPartitioned - } - - // check refer columns in parent table. - for i := range fkInfo.RefCols { - refCol := model.FindColumnInfo(referTblInfo.Columns, fkInfo.RefCols[i].L) - if refCol == nil { - return infoschema.ErrForeignKeyNoColumnInParent.GenWithStackByArgs(fkInfo.RefCols[i], fkInfo.Name, fkInfo.RefTable) - } - if refCol.IsGenerated() && !refCol.GeneratedStored { - return infoschema.ErrForeignKeyCannotUseVirtualColumn.GenWithStackByArgs(fkInfo.Name, fkInfo.RefCols[i]) - } - col := model.FindColumnInfo(tblInfo.Columns, fkInfo.Cols[i].L) - if col == nil { - return dbterror.ErrKeyColumnDoesNotExits.GenWithStackByArgs(fkInfo.Cols[i]) - } - if col.GetType() != refCol.GetType() || - mysql.HasUnsignedFlag(col.GetFlag()) != mysql.HasUnsignedFlag(refCol.GetFlag()) || - col.GetCharset() != refCol.GetCharset() || - col.GetCollate() != refCol.GetCollate() { - return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(col.Name, refCol.Name, fkInfo.Name) - } - if len(fkInfo.RefCols) == 1 && mysql.HasPriKeyFlag(refCol.GetFlag()) && referTblInfo.PKIsHandle { - return nil - } - } - // check refer columns should have index. - if model.FindIndexByColumns(referTblInfo, referTblInfo.Indices, fkInfo.RefCols...) == nil { - return infoschema.ErrForeignKeyNoIndexInParent.GenWithStackByArgs(fkInfo.Name, fkInfo.RefTable) - } - return nil -} - -func checkModifyColumnWithForeignKeyConstraint(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, originalCol, newCol *model.ColumnInfo) error { - if newCol.GetType() == originalCol.GetType() && newCol.GetFlen() == originalCol.GetFlen() && newCol.GetDecimal() == originalCol.GetDecimal() { - return nil - } - // WARN: is maybe nil. - if is == nil { - return nil - } - for _, fkInfo := range tbInfo.ForeignKeys { - for i, col := range fkInfo.Cols { - if col.L == originalCol.Name.L { - if !is.TableExists(fkInfo.RefSchema, fkInfo.RefTable) { - continue - } - referTable, err := is.TableByName(fkInfo.RefSchema, fkInfo.RefTable) - if err != nil { - return err - } - referCol := model.FindColumnInfo(referTable.Meta().Columns, fkInfo.RefCols[i].L) - if referCol == nil { - continue - } - if newCol.GetType() != referCol.GetType() { - return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(originalCol.Name, fkInfo.RefCols[i], fkInfo.Name) - } - if newCol.GetFlen() < referCol.GetFlen() || newCol.GetFlen() < originalCol.GetFlen() || - (newCol.GetType() == mysql.TypeNewDecimal && (newCol.GetFlen() != originalCol.GetFlen() || newCol.GetDecimal() != originalCol.GetDecimal())) { - return dbterror.ErrForeignKeyColumnCannotChange.GenWithStackByArgs(originalCol.Name, fkInfo.Name) - } - } - } - } - referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) - for _, referredFK := range referredFKs { - for i, col := range referredFK.Cols { - if col.L == originalCol.Name.L { - if !is.TableExists(referredFK.ChildSchema, referredFK.ChildTable) { - continue - } - childTblInfo, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - return err - } - fk := model.FindFKInfoByName(childTblInfo.Meta().ForeignKeys, referredFK.ChildFKName.L) - childCol := model.FindColumnInfo(childTblInfo.Meta().Columns, fk.Cols[i].L) - if childCol == nil { - continue - } - if newCol.GetType() != childCol.GetType() { - return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(childCol.Name, originalCol.Name, referredFK.ChildFKName) - } - if newCol.GetFlen() < childCol.GetFlen() || newCol.GetFlen() < originalCol.GetFlen() || - (newCol.GetType() == mysql.TypeNewDecimal && (newCol.GetFlen() != childCol.GetFlen() || newCol.GetDecimal() != childCol.GetDecimal())) { - return dbterror.ErrForeignKeyColumnCannotChangeChild.GenWithStackByArgs(originalCol.Name, referredFK.ChildFKName, referredFK.ChildSchema.L+"."+referredFK.ChildTable.L) - } - } - } - } - - return nil -} - -func checkTableHasForeignKeyReferred(is infoschema.InfoSchema, schema, tbl string, ignoreTables []ast.Ident, fkCheck bool) *model.ReferredFKInfo { - if !fkCheck { - return nil - } - referredFKs := is.GetTableReferredForeignKeys(schema, tbl) - for _, referredFK := range referredFKs { - found := false - for _, tb := range ignoreTables { - if referredFK.ChildSchema.L == tb.Schema.L && referredFK.ChildTable.L == tb.Name.L { - found = true - break - } - } - if found { - continue - } - if is.TableExists(referredFK.ChildSchema, referredFK.ChildTable) { - return referredFK - } - } - return nil -} - -func checkDropTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job) error { - if !variable.EnableForeignKey.Load() { - return nil - } - var objectIdents []ast.Ident - var fkCheck bool - err := job.DecodeArgs(&objectIdents, &fkCheck) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - referredFK, err := checkTableHasForeignKeyReferredInOwner(d, t, job.SchemaName, job.TableName, objectIdents, fkCheck) - if err != nil { - return err - } - if referredFK != nil { - job.State = model.JobStateCancelled - msg := fmt.Sprintf("`%s`.`%s` CONSTRAINT `%s`", referredFK.ChildSchema, referredFK.ChildTable, referredFK.ChildFKName) - return errors.Trace(dbterror.ErrTruncateIllegalForeignKey.GenWithStackByArgs(msg)) - } - return nil -} - -func checkTruncateTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, fkCheck bool) error { - referredFK, err := checkTableHasForeignKeyReferredInOwner(d, t, job.SchemaName, job.TableName, []ast.Ident{{Name: tblInfo.Name, Schema: model.NewCIStr(job.SchemaName)}}, fkCheck) - if err != nil { - return err - } - if referredFK != nil { - job.State = model.JobStateCancelled - msg := fmt.Sprintf("`%s`.`%s` CONSTRAINT `%s`", referredFK.ChildSchema, referredFK.ChildTable, referredFK.ChildFKName) - return errors.Trace(dbterror.ErrTruncateIllegalForeignKey.GenWithStackByArgs(msg)) - } - return nil -} - -func checkTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, schema, tbl string, ignoreTables []ast.Ident, fkCheck bool) (_ *model.ReferredFKInfo, _ error) { - if !variable.EnableForeignKey.Load() { - return nil, nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return nil, err - } - referredFK := checkTableHasForeignKeyReferred(is, schema, tbl, ignoreTables, fkCheck) - return referredFK, nil -} - -func checkIndexNeededInForeignKey(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, idxInfo *model.IndexInfo) error { - referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) - if len(tbInfo.ForeignKeys) == 0 && len(referredFKs) == 0 { - return nil - } - remainIdxs := make([]*model.IndexInfo, 0, len(tbInfo.Indices)) - for _, idx := range tbInfo.Indices { - if idx.ID == idxInfo.ID { - continue - } - remainIdxs = append(remainIdxs, idx) - } - checkFn := func(cols []model.CIStr) error { - if !model.IsIndexPrefixCovered(tbInfo, idxInfo, cols...) { - return nil - } - if tbInfo.PKIsHandle && len(cols) == 1 { - refColInfo := model.FindColumnInfo(tbInfo.Columns, cols[0].L) - if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { - return nil - } - } - for _, index := range remainIdxs { - if model.IsIndexPrefixCovered(tbInfo, index, cols...) { - return nil - } - } - return dbterror.ErrDropIndexNeededInForeignKey.GenWithStackByArgs(idxInfo.Name) - } - for _, fk := range tbInfo.ForeignKeys { - if fk.Version < model.FKVersion1 { - continue - } - err := checkFn(fk.Cols) - if err != nil { - return err - } - } - for _, referredFK := range referredFKs { - err := checkFn(referredFK.Cols) - if err != nil { - return err - } - } - return nil -} - -func checkIndexNeededInForeignKeyInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, dbName string, tbInfo *model.TableInfo, idxInfo *model.IndexInfo) error { - if !variable.EnableForeignKey.Load() { - return nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return err - } - err = checkIndexNeededInForeignKey(is, dbName, tbInfo, idxInfo) - if err != nil { - job.State = model.JobStateCancelled - return err - } - return nil -} - -func checkDropColumnWithForeignKeyConstraint(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, colName string) error { - for _, fkInfo := range tbInfo.ForeignKeys { - for _, col := range fkInfo.Cols { - if col.L == colName { - return dbterror.ErrFkColumnCannotDrop.GenWithStackByArgs(colName, fkInfo.Name) - } - } - } - referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) - for _, referredFK := range referredFKs { - for _, col := range referredFK.Cols { - if col.L == colName { - return dbterror.ErrFkColumnCannotDropChild.GenWithStackByArgs(colName, referredFK.ChildFKName, referredFK.ChildTable) - } - } - } - return nil -} - -func checkDropColumnWithForeignKeyConstraintInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, colName string) error { - if !variable.EnableForeignKey.Load() { - return nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return errors.Trace(err) - } - err = checkDropColumnWithForeignKeyConstraint(is, job.SchemaName, tbInfo, colName) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - return nil -} - -type foreignKeyHelper struct { - loaded map[schemaAndTable]schemaIDAndTableInfo -} - -type schemaAndTable struct { - schema string - table string -} - -func newForeignKeyHelper() foreignKeyHelper { - return foreignKeyHelper{loaded: make(map[schemaAndTable]schemaIDAndTableInfo)} -} - -func (h *foreignKeyHelper) addLoadedTable(schemaName, tableName string, schemaID int64, tblInfo *model.TableInfo) { - k := schemaAndTable{schema: schemaName, table: tableName} - h.loaded[k] = schemaIDAndTableInfo{schemaID: schemaID, tblInfo: tblInfo} -} - -func (h *foreignKeyHelper) getLoadedTables() []schemaIDAndTableInfo { - tableList := make([]schemaIDAndTableInfo, 0, len(h.loaded)) - for _, info := range h.loaded { - tableList = append(tableList, info) - } - return tableList -} - -func (h *foreignKeyHelper) getTableFromStorage(is infoschema.InfoSchema, t *meta.Meta, schema, table model.CIStr) (result schemaIDAndTableInfo, _ error) { - k := schemaAndTable{schema: schema.L, table: table.L} - if info, ok := h.loaded[k]; ok { - return info, nil - } - db, ok := is.SchemaByName(schema) - if !ok { - return result, errors.Trace(infoschema.ErrDatabaseNotExists.GenWithStackByArgs(schema)) - } - tb, err := is.TableByName(schema, table) - if err != nil { - return result, errors.Trace(err) - } - tbInfo, err := getTableInfo(t, tb.Meta().ID, db.ID) - if err != nil { - return result, errors.Trace(err) - } - result.schemaID, result.tblInfo = db.ID, tbInfo - h.loaded[k] = result - return result, nil -} - -func checkDatabaseHasForeignKeyReferred(is infoschema.InfoSchema, schema model.CIStr, fkCheck bool) error { - if !fkCheck { - return nil - } - tables := is.SchemaTables(schema) - tableNames := make([]ast.Ident, len(tables)) - for i := range tables { - tableNames[i] = ast.Ident{Schema: schema, Name: tables[i].Meta().Name} - } - for _, tbl := range tables { - if referredFK := checkTableHasForeignKeyReferred(is, schema.L, tbl.Meta().Name.L, tableNames, fkCheck); referredFK != nil { - return errors.Trace(dbterror.ErrForeignKeyCannotDropParent.GenWithStackByArgs(tbl.Meta().Name, referredFK.ChildFKName, referredFK.ChildTable)) - } - } - return nil -} - -func checkDatabaseHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job) error { - if !variable.EnableForeignKey.Load() { - return nil - } - var fkCheck bool - err := job.DecodeArgs(&fkCheck) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - if !fkCheck { - return nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return errors.Trace(err) - } - err = checkDatabaseHasForeignKeyReferred(is, model.NewCIStr(job.SchemaName), fkCheck) - if err != nil { - job.State = model.JobStateCancelled - } - return errors.Trace(err) -} - -func checkFKDupName(tbInfo *model.TableInfo, fkName model.CIStr) error { - for _, fkInfo := range tbInfo.ForeignKeys { - if fkName.L == fkInfo.Name.L { - return dbterror.ErrFkDupName.GenWithStackByArgs(fkName.O) - } - } - return nil -} - -func checkAddForeignKeyValid(is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { - if !variable.EnableForeignKey.Load() { - return nil - } - err := checkTableForeignKeyValid(is, schema, tbInfo, fk, fkCheck) - if err != nil { - return err - } - return nil -} - -func checkAddForeignKeyValidInOwner(d *ddlCtx, t *meta.Meta, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { - err := checkFKDupName(tbInfo, fk.Name) - if err != nil { - return err - } - if !variable.EnableForeignKey.Load() { - return nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return errors.Trace(err) - } - err = checkAddForeignKeyValid(is, schema, tbInfo, fk, fkCheck) - if err != nil { - return errors.Trace(err) - } - // check foreign key columns should have index. - if len(fk.Cols) == 1 && tbInfo.PKIsHandle { - pkCol := tbInfo.GetPkColInfo() - if pkCol != nil && pkCol.Name.L == fk.Cols[0].L { - return nil - } - } - if model.FindIndexByColumns(tbInfo, tbInfo.Indices, fk.Cols...) == nil { - return errors.Errorf("Failed to add the foreign key constraint. Missing index for '%s' foreign key columns in the table '%s'", fk.Name, tbInfo.Name) - } - return nil -} - -func checkForeignKeyConstrain(w *worker, schema, table string, fkInfo *model.FKInfo, fkCheck bool) error { - if !fkCheck { - return nil - } - sctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - originValue := sctx.GetSessionVars().OptimizerEnableNAAJ - sctx.GetSessionVars().OptimizerEnableNAAJ = true - defer func() { - sctx.GetSessionVars().OptimizerEnableNAAJ = originValue - w.sessPool.Put(sctx) - }() - - var buf strings.Builder - buf.WriteString("select 1 from %n.%n where ") - paramsList := make([]interface{}, 0, 4+len(fkInfo.Cols)*2) - paramsList = append(paramsList, schema, table) - for i, col := range fkInfo.Cols { - if i == 0 { - buf.WriteString("%n is not null") - paramsList = append(paramsList, col.L) - } else { - buf.WriteString(" and %n is not null") - paramsList = append(paramsList, col.L) - } - } - buf.WriteString(" and (") - for i, col := range fkInfo.Cols { - if i == 0 { - buf.WriteString("%n") - } else { - buf.WriteString(",%n") - } - paramsList = append(paramsList, col.L) - } - buf.WriteString(") not in (select ") - for i, col := range fkInfo.RefCols { - if i == 0 { - buf.WriteString("%n") - } else { - buf.WriteString(",%n") - } - paramsList = append(paramsList, col.L) - } - buf.WriteString(" from %n.%n ) limit 1") - paramsList = append(paramsList, fkInfo.RefSchema.L, fkInfo.RefTable.L) - rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(w.ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, buf.String(), paramsList...) - if err != nil { - return errors.Trace(err) - } - rowCount := len(rows) - if rowCount != 0 { - return dbterror.ErrNoReferencedRow2.GenWithStackByArgs(fkInfo.String(schema, table)) - } - return nil -} diff --git a/ddl/foreign_key_test.go b/ddl/foreign_key_test.go deleted file mode 100644 index a50fb2de5af99..0000000000000 --- a/ddl/foreign_key_test.go +++ /dev/null @@ -1,432 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func testCreateForeignKey(t *testing.T, d ddl.DDL, ctx sessionctx.Context, dbInfo *model.DBInfo, tblInfo *model.TableInfo, fkName string, keys []string, refTable string, refKeys []string, onDelete model.ReferOptionType, onUpdate model.ReferOptionType) *model.Job { - FKName := model.NewCIStr(fkName) - Keys := make([]model.CIStr, len(keys)) - for i, key := range keys { - Keys[i] = model.NewCIStr(key) - } - - RefTable := model.NewCIStr(refTable) - RefKeys := make([]model.CIStr, len(refKeys)) - for i, key := range refKeys { - RefKeys[i] = model.NewCIStr(key) - } - - fkInfo := &model.FKInfo{ - Name: FKName, - RefTable: RefTable, - RefCols: RefKeys, - Cols: Keys, - OnDelete: int(onDelete), - OnUpdate: int(onUpdate), - State: model.StateNone, - } - - job := &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionAddForeignKey, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{fkInfo}, - } - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.NoError(t, err) - return job -} - -func testDropForeignKey(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, foreignKeyName string) *model.Job { - job := &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionDropForeignKey, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{model.NewCIStr(foreignKeyName)}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - return job -} - -func getForeignKey(t table.Table, name string) *model.FKInfo { - for _, fk := range t.Meta().ForeignKeys { - // only public foreign key can be read. - if fk.State != model.StatePublic { - continue - } - if fk.Name.L == strings.ToLower(name) { - return fk - } - } - return nil -} - -func TestForeignKey(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := dom.DDL() - dbInfo, err := testSchemaInfo(store, "test_foreign") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), dom.DDL(), dbInfo) - tblInfo, err := testTableInfo(store, "t", 3) - require.NoError(t, err) - tblInfo.Indices = append(tblInfo.Indices, &model.IndexInfo{ - ID: 1, - Name: model.NewCIStr("idx_fk"), - Table: model.NewCIStr("t"), - Columns: []*model.IndexColumn{{ - Name: model.NewCIStr("c1"), - Offset: 0, - Length: types.UnspecifiedLength, - }}, - State: model.StatePublic, - }) - testCreateTable(t, testkit.NewTestKit(t, store).Session(), d, dbInfo, tblInfo) - - // fix data race - var mu sync.Mutex - checkOK := false - var hookErr error - tc := &callback.TestDDLCallback{} - onJobUpdatedExportedFunc := func(job *model.Job) { - if job.State != model.JobStateDone { - return - } - mu.Lock() - defer mu.Unlock() - var t table.Table - t, err = testGetTableWithError(store, dbInfo.ID, tblInfo.ID) - if err != nil { - hookErr = errors.Trace(err) - return - } - fk := getForeignKey(t, "c1_fk") - if fk == nil { - hookErr = errors.New("foreign key not exists") - return - } - checkOK = true - } - tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - ctx := testkit.NewTestKit(t, store).Session() - job := testCreateForeignKey(t, d, ctx, dbInfo, tblInfo, "c1_fk", []string{"c1"}, "t2", []string{"c1"}, model.ReferOptionCascade, model.ReferOptionSetNull) - testCheckJobDone(t, store, job.ID, true) - require.NoError(t, err) - mu.Lock() - hErr := hookErr - ok := checkOK - mu.Unlock() - require.NoError(t, hErr) - require.True(t, ok) - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - - mu.Lock() - checkOK = false - mu.Unlock() - // fix data race pr/#9491 - tc2 := &callback.TestDDLCallback{} - onJobUpdatedExportedFunc2 := func(job *model.Job) { - if job.State != model.JobStateDone { - return - } - mu.Lock() - defer mu.Unlock() - var t table.Table - t, err = testGetTableWithError(store, dbInfo.ID, tblInfo.ID) - if err != nil { - hookErr = errors.Trace(err) - return - } - fk := getForeignKey(t, "c1_fk") - if fk != nil { - hookErr = errors.New("foreign key has not been dropped") - return - } - checkOK = true - } - tc2.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc2) - d.SetHook(tc2) - - job = testDropForeignKey(t, ctx, d, dbInfo, tblInfo, "c1_fk") - testCheckJobDone(t, store, job.ID, false) - mu.Lock() - hErr = hookErr - ok = checkOK - mu.Unlock() - require.NoError(t, hErr) - require.True(t, ok) - d.SetHook(originalHook) - - tk := testkit.NewTestKit(t, store) - jobID := testDropTable(tk, t, dbInfo.Name.L, tblInfo.Name.L, dom) - testCheckJobDone(t, store, jobID, false) - - require.NoError(t, err) -} - -func TestTruncateOrDropTableWithForeignKeyReferred2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := dom.DDL() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@global.tidb_enable_foreign_key=1") - tk2.MustExec("set @@foreign_key_checks=1;") - tk2.MustExec("use test") - - tk.MustExec("create table t1 (id int key, a int);") - - var wg sync.WaitGroup - var truncateErr, dropErr error - testTruncate := true - tc := &callback.TestDDLCallback{} - tc.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateNone { - return - } - if job.Type != model.ActionCreateTable { - return - } - wg.Add(1) - if testTruncate { - go func() { - defer wg.Done() - truncateErr = tk2.ExecToErr("truncate table t1") - }() - } else { - go func() { - defer wg.Done() - dropErr = tk2.ExecToErr("drop table t1") - }() - } - // make sure tk2's ddl job already put into ddl job queue. - time.Sleep(time.Millisecond * 100) - } - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - tk.MustExec("create table t2 (a int, b int, foreign key fk(b) references t1(id));") - wg.Wait() - require.Error(t, truncateErr) - require.Equal(t, "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk`)", truncateErr.Error()) - - tk.MustExec("drop table t2") - testTruncate = false - tk.MustExec("create table t2 (a int, b int, foreign key fk(b) references t1(id));") - wg.Wait() - require.Error(t, dropErr) - require.Equal(t, "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk`)", dropErr.Error()) -} - -func TestDropIndexNeededInForeignKey2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := dom.DDL() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@global.tidb_enable_foreign_key=1") - tk2.MustExec("set @@foreign_key_checks=1;") - tk2.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int)") - tk.MustExec("create table t2 (a int, b int, index idx1 (b),index idx2 (b), foreign key (b) references t1(id));") - - var wg sync.WaitGroup - var dropErr error - tc := &callback.TestDDLCallback{} - tc.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StatePublic || job.Type != model.ActionDropIndex { - return - } - wg.Add(1) - go func() { - defer wg.Done() - dropErr = tk2.ExecToErr("alter table t2 drop index idx2") - }() - // make sure tk2's ddl job already put into ddl job queue. - time.Sleep(time.Millisecond * 100) - } - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - tk.MustExec("alter table t2 drop index idx1") - wg.Wait() - require.Error(t, dropErr) - require.Equal(t, "[ddl:1553]Cannot drop index 'idx2': needed in a foreign key constraint", dropErr.Error()) -} - -func TestDropDatabaseWithForeignKeyReferred2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := dom.DDL() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@global.tidb_enable_foreign_key=1") - tk2.MustExec("set @@foreign_key_checks=1;") - tk2.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") - tk.MustExec("create database test2") - var wg sync.WaitGroup - var dropErr error - tc := &callback.TestDDLCallback{} - tc.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StateNone { - return - } - if job.Type != model.ActionCreateTable { - return - } - wg.Add(1) - go func() { - defer wg.Done() - dropErr = tk2.ExecToErr("drop database test") - }() - // make sure tk2's ddl job already put into ddl job queue. - time.Sleep(time.Millisecond * 100) - } - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - tk.MustExec("create table test2.t3 (id int key, b int, foreign key fk_b(b) references test.t2(id));") - wg.Wait() - require.Error(t, dropErr) - require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", dropErr.Error()) - tk.MustExec("drop table test2.t3") - tk.MustExec("drop database test") -} - -func TestAddForeignKey2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := dom.DDL() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int, index(b));") - var wg sync.WaitGroup - var addErr error - tc := &callback.TestDDLCallback{} - tc.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState != model.StatePublic || job.Type != model.ActionDropIndex { - return - } - wg.Add(1) - go func() { - defer wg.Done() - addErr = tk2.ExecToErr("alter table t2 add foreign key (b) references t1(id);") - }() - // make sure tk2's ddl job already put into ddl job queue. - time.Sleep(time.Millisecond * 100) - } - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - tk.MustExec("alter table t2 drop index b") - wg.Wait() - require.Error(t, addErr) - require.Equal(t, "[ddl:-1]Failed to add the foreign key constraint. Missing index for 'fk_1' foreign key columns in the table 't2'", addErr.Error()) -} - -func TestAddForeignKey3(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := dom.DDL() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int, b int, index(id), index(b));") - tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3)") - tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3)") - - var insertErrs []error - var deleteErrs []error - tc := &callback.TestDDLCallback{} - tc.OnJobRunBeforeExported = func(job *model.Job) { - if job.Type != model.ActionAddForeignKey { - return - } - if job.SchemaState == model.StateWriteOnly || job.SchemaState == model.StateWriteReorganization { - err := tk2.ExecToErr("insert into t2 values (10, 10)") - insertErrs = append(insertErrs, err) - err = tk2.ExecToErr("delete from t1 where id = 1") - deleteErrs = append(deleteErrs, err) - } - } - originalHook := d.GetHook() - defer d.SetHook(originalHook) - d.SetHook(tc) - - tk.MustExec("alter table t2 add foreign key (id) references t1(id) on delete cascade") - require.Equal(t, 2, len(insertErrs)) - for _, err := range insertErrs { - require.Error(t, err) - require.Equal(t, "[planner:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`) ON DELETE CASCADE)", err.Error()) - } - for _, err := range deleteErrs { - require.Error(t, err) - require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`) ON DELETE CASCADE)", err.Error()) - } - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1 1", "2 2", "3 3")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 1", "2 2", "3 3")) -} diff --git a/ddl/index.go b/ddl/index.go deleted file mode 100644 index c5f9ac7e131ce..0000000000000 --- a/ddl/index.go +++ /dev/null @@ -1,2515 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "bytes" - "cmp" - "context" - "encoding/hex" - "encoding/json" - "fmt" - "os" - "path/filepath" - "slices" - "strings" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/backoff" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/prometheus/client_golang/prometheus" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - kvutil "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" - "golang.org/x/sync/errgroup" -) - -const ( - // MaxCommentLength is exported for testing. - MaxCommentLength = 1024 -) - -var ( - telemetryAddIndexIngestUsage = metrics.TelemetryAddIndexIngestCnt -) - -func buildIndexColumns(ctx sessionctx.Context, columns []*model.ColumnInfo, indexPartSpecifications []*ast.IndexPartSpecification) ([]*model.IndexColumn, bool, error) { - // Build offsets. - idxParts := make([]*model.IndexColumn, 0, len(indexPartSpecifications)) - var col *model.ColumnInfo - var mvIndex bool - maxIndexLength := config.GetGlobalConfig().MaxIndexLength - // The sum of length of all index columns. - sumLength := 0 - for _, ip := range indexPartSpecifications { - col = model.FindColumnInfo(columns, ip.Column.Name.L) - if col == nil { - return nil, false, dbterror.ErrKeyColumnDoesNotExits.GenWithStack("column does not exist: %s", ip.Column.Name) - } - - if err := checkIndexColumn(ctx, col, ip.Length); err != nil { - return nil, false, err - } - if col.FieldType.IsArray() { - if mvIndex { - return nil, false, dbterror.ErrNotSupportedYet.GenWithStackByArgs("more than one multi-valued key part per index") - } - mvIndex = true - } - indexColLen := ip.Length - indexColumnLength, err := getIndexColumnLength(col, ip.Length) - if err != nil { - return nil, false, err - } - sumLength += indexColumnLength - - // The sum of all lengths must be shorter than the max length for prefix. - if sumLength > maxIndexLength { - // The multiple column index and the unique index in which the length sum exceeds the maximum size - // will return an error instead produce a warning. - if ctx == nil || ctx.GetSessionVars().StrictSQLMode || mysql.HasUniKeyFlag(col.GetFlag()) || len(indexPartSpecifications) > 1 { - return nil, false, dbterror.ErrTooLongKey.GenWithStackByArgs(sumLength, maxIndexLength) - } - // truncate index length and produce warning message in non-restrict sql mode. - colLenPerUint, err := getIndexColumnLength(col, 1) - if err != nil { - return nil, false, err - } - indexColLen = maxIndexLength / colLenPerUint - // produce warning message - ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrTooLongKey.FastGenByArgs(sumLength, maxIndexLength)) - } - - idxParts = append(idxParts, &model.IndexColumn{ - Name: col.Name, - Offset: col.Offset, - Length: indexColLen, - }) - } - - return idxParts, mvIndex, nil -} - -// CheckPKOnGeneratedColumn checks the specification of PK is valid. -func CheckPKOnGeneratedColumn(tblInfo *model.TableInfo, indexPartSpecifications []*ast.IndexPartSpecification) (*model.ColumnInfo, error) { - var lastCol *model.ColumnInfo - for _, colName := range indexPartSpecifications { - lastCol = tblInfo.FindPublicColumnByName(colName.Column.Name.L) - if lastCol == nil { - return nil, dbterror.ErrKeyColumnDoesNotExits.GenWithStackByArgs(colName.Column.Name) - } - // Virtual columns cannot be used in primary key. - if lastCol.IsGenerated() && !lastCol.GeneratedStored { - if lastCol.Hidden { - return nil, dbterror.ErrFunctionalIndexPrimaryKey - } - return nil, dbterror.ErrUnsupportedOnGeneratedColumn.GenWithStackByArgs("Defining a virtual generated column as primary key") - } - } - - return lastCol, nil -} - -func checkIndexPrefixLength(columns []*model.ColumnInfo, idxColumns []*model.IndexColumn) error { - idxLen, err := indexColumnsLen(columns, idxColumns) - if err != nil { - return err - } - if idxLen > config.GetGlobalConfig().MaxIndexLength { - return dbterror.ErrTooLongKey.GenWithStackByArgs(idxLen, config.GetGlobalConfig().MaxIndexLength) - } - return nil -} - -func checkIndexColumn(ctx sessionctx.Context, col *model.ColumnInfo, indexColumnLen int) error { - if col.GetFlen() == 0 && (types.IsTypeChar(col.FieldType.GetType()) || types.IsTypeVarchar(col.FieldType.GetType())) { - if col.Hidden { - return errors.Trace(dbterror.ErrWrongKeyColumnFunctionalIndex.GenWithStackByArgs(col.GeneratedExprString)) - } - return errors.Trace(dbterror.ErrWrongKeyColumn.GenWithStackByArgs(col.Name)) - } - - // JSON column cannot index. - if col.FieldType.GetType() == mysql.TypeJSON && !col.FieldType.IsArray() { - if col.Hidden { - return dbterror.ErrFunctionalIndexOnJSONOrGeometryFunction - } - return errors.Trace(dbterror.ErrJSONUsedAsKey.GenWithStackByArgs(col.Name.O)) - } - - // Length must be specified and non-zero for BLOB and TEXT column indexes. - if types.IsTypeBlob(col.FieldType.GetType()) { - if indexColumnLen == types.UnspecifiedLength { - if col.Hidden { - return dbterror.ErrFunctionalIndexOnBlob - } - return errors.Trace(dbterror.ErrBlobKeyWithoutLength.GenWithStackByArgs(col.Name.O)) - } - if indexColumnLen == types.ErrorLength { - return errors.Trace(dbterror.ErrKeyPart0.GenWithStackByArgs(col.Name.O)) - } - } - - // Length can only be specified for specifiable types. - if indexColumnLen != types.UnspecifiedLength && !types.IsTypePrefixable(col.FieldType.GetType()) { - return errors.Trace(dbterror.ErrIncorrectPrefixKey) - } - - // Key length must be shorter or equal to the column length. - if indexColumnLen != types.UnspecifiedLength && - types.IsTypeChar(col.FieldType.GetType()) { - if col.GetFlen() < indexColumnLen { - return errors.Trace(dbterror.ErrIncorrectPrefixKey) - } - // Length must be non-zero for char. - if indexColumnLen == types.ErrorLength { - return errors.Trace(dbterror.ErrKeyPart0.GenWithStackByArgs(col.Name.O)) - } - } - - if types.IsString(col.FieldType.GetType()) { - desc, err := charset.GetCharsetInfo(col.GetCharset()) - if err != nil { - return err - } - indexColumnLen *= desc.Maxlen - } - // Specified length must be shorter than the max length for prefix. - maxIndexLength := config.GetGlobalConfig().MaxIndexLength - if indexColumnLen > maxIndexLength && (ctx == nil || ctx.GetSessionVars().StrictSQLMode) { - // return error in strict sql mode - return dbterror.ErrTooLongKey.GenWithStackByArgs(indexColumnLen, maxIndexLength) - } - return nil -} - -// getIndexColumnLength calculate the bytes number required in an index column. -func getIndexColumnLength(col *model.ColumnInfo, colLen int) (int, error) { - length := types.UnspecifiedLength - if colLen != types.UnspecifiedLength { - length = colLen - } else if col.GetFlen() != types.UnspecifiedLength { - length = col.GetFlen() - } - - switch col.GetType() { - case mysql.TypeBit: - return (length + 7) >> 3, nil - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob: - // Different charsets occupy different numbers of bytes on each character. - desc, err := charset.GetCharsetInfo(col.GetCharset()) - if err != nil { - return 0, dbterror.ErrUnsupportedCharset.GenWithStackByArgs(col.GetCharset(), col.GetCollate()) - } - return desc.Maxlen * length, nil - case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeShort: - return mysql.DefaultLengthOfMysqlTypes[col.GetType()], nil - case mysql.TypeFloat: - if length <= mysql.MaxFloatPrecisionLength { - return mysql.DefaultLengthOfMysqlTypes[mysql.TypeFloat], nil - } - return mysql.DefaultLengthOfMysqlTypes[mysql.TypeDouble], nil - case mysql.TypeNewDecimal: - return calcBytesLengthForDecimal(length), nil - case mysql.TypeYear, mysql.TypeDate, mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeTimestamp: - return mysql.DefaultLengthOfMysqlTypes[col.GetType()], nil - default: - return length, nil - } -} - -// decimal using a binary format that packs nine decimal (base 10) digits into four bytes. -func calcBytesLengthForDecimal(m int) int { - return (m / 9 * 4) + ((m%9)+1)/2 -} - -// BuildIndexInfo builds a new IndexInfo according to the index information. -func BuildIndexInfo( - ctx sessionctx.Context, - allTableColumns []*model.ColumnInfo, - indexName model.CIStr, - isPrimary bool, - isUnique bool, - isGlobal bool, - indexPartSpecifications []*ast.IndexPartSpecification, - indexOption *ast.IndexOption, - state model.SchemaState, -) (*model.IndexInfo, error) { - if err := checkTooLongIndex(indexName); err != nil { - return nil, errors.Trace(err) - } - - idxColumns, mvIndex, err := buildIndexColumns(ctx, allTableColumns, indexPartSpecifications) - if err != nil { - return nil, errors.Trace(err) - } - - // Create index info. - idxInfo := &model.IndexInfo{ - Name: indexName, - Columns: idxColumns, - State: state, - Primary: isPrimary, - Unique: isUnique, - Global: isGlobal, - MVIndex: mvIndex, - } - - if indexOption != nil { - idxInfo.Comment = indexOption.Comment - if indexOption.Visibility == ast.IndexVisibilityInvisible { - idxInfo.Invisible = true - } - if indexOption.Tp == model.IndexTypeInvalid { - // Use btree as default index type. - idxInfo.Tp = model.IndexTypeBtree - } else { - idxInfo.Tp = indexOption.Tp - } - } else { - // Use btree as default index type. - idxInfo.Tp = model.IndexTypeBtree - } - - return idxInfo, nil -} - -// AddIndexColumnFlag aligns the column flags of columns in TableInfo to IndexInfo. -func AddIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { - if indexInfo.Primary { - for _, col := range indexInfo.Columns { - tblInfo.Columns[col.Offset].AddFlag(mysql.PriKeyFlag) - } - return - } - - col := indexInfo.Columns[0] - if indexInfo.Unique && len(indexInfo.Columns) == 1 { - tblInfo.Columns[col.Offset].AddFlag(mysql.UniqueKeyFlag) - } else { - tblInfo.Columns[col.Offset].AddFlag(mysql.MultipleKeyFlag) - } -} - -// DropIndexColumnFlag drops the column flag of columns in TableInfo according to the IndexInfo. -func DropIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { - if indexInfo.Primary { - for _, col := range indexInfo.Columns { - tblInfo.Columns[col.Offset].DelFlag(mysql.PriKeyFlag) - } - } else if indexInfo.Unique && len(indexInfo.Columns) == 1 { - tblInfo.Columns[indexInfo.Columns[0].Offset].DelFlag(mysql.UniqueKeyFlag) - } else { - tblInfo.Columns[indexInfo.Columns[0].Offset].DelFlag(mysql.MultipleKeyFlag) - } - - col := indexInfo.Columns[0] - // other index may still cover this col - for _, index := range tblInfo.Indices { - if index.Name.L == indexInfo.Name.L { - continue - } - - if index.Columns[0].Name.L != col.Name.L { - continue - } - - AddIndexColumnFlag(tblInfo, index) - } -} - -// ValidateRenameIndex checks if index name is ok to be renamed. -func ValidateRenameIndex(from, to model.CIStr, tbl *model.TableInfo) (ignore bool, err error) { - if fromIdx := tbl.FindIndexByName(from.L); fromIdx == nil { - return false, errors.Trace(infoschema.ErrKeyNotExists.GenWithStackByArgs(from.O, tbl.Name)) - } - // Take case-sensitivity into account, if `FromKey` and `ToKey` are the same, nothing need to be changed - if from.O == to.O { - return true, nil - } - // If spec.FromKey.L == spec.ToKey.L, we operate on the same index(case-insensitive) and change its name (case-sensitive) - // e.g: from `inDex` to `IndEX`. Otherwise, we try to rename an index to another different index which already exists, - // that's illegal by rule. - if toIdx := tbl.FindIndexByName(to.L); toIdx != nil && from.L != to.L { - return false, errors.Trace(infoschema.ErrKeyNameDuplicate.GenWithStackByArgs(toIdx.Name.O)) - } - return false, nil -} - -func onRenameIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, from, to, err := checkRenameIndex(t, job) - if err != nil || tblInfo == nil { - return ver, errors.Trace(err) - } - if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { - return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Rename Index")) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - // Store the mark and enter the next DDL handling loop. - return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) - } - - renameIndexes(tblInfo, from, to) - if ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func validateAlterIndexVisibility(ctx sessionctx.Context, indexName model.CIStr, invisible bool, tbl *model.TableInfo) (bool, error) { - var idx *model.IndexInfo - if idx = tbl.FindIndexByName(indexName.L); idx == nil || idx.State != model.StatePublic { - return false, errors.Trace(infoschema.ErrKeyNotExists.GenWithStackByArgs(indexName.O, tbl.Name)) - } - if ctx == nil || ctx.GetSessionVars() == nil || ctx.GetSessionVars().StmtCtx.MultiSchemaInfo == nil { - // Early return. - if idx.Invisible == invisible { - return true, nil - } - } - return false, nil -} - -func onAlterIndexVisibility(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, from, invisible, err := checkAlterIndexVisibility(t, job) - if err != nil || tblInfo == nil { - return ver, errors.Trace(err) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - return updateVersionAndTableInfo(d, t, job, tblInfo, false) - } - - setIndexVisibility(tblInfo, from, invisible) - if ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func setIndexVisibility(tblInfo *model.TableInfo, name model.CIStr, invisible bool) { - for _, idx := range tblInfo.Indices { - if idx.Name.L == name.L || (isTempIdxInfo(idx, tblInfo) && getChangingIndexOriginName(idx) == name.O) { - idx.Invisible = invisible - } - } -} - -func getNullColInfos(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) ([]*model.ColumnInfo, error) { - nullCols := make([]*model.ColumnInfo, 0, len(indexInfo.Columns)) - for _, colName := range indexInfo.Columns { - col := model.FindColumnInfo(tblInfo.Columns, colName.Name.L) - if !mysql.HasNotNullFlag(col.GetFlag()) || mysql.HasPreventNullInsertFlag(col.GetFlag()) { - nullCols = append(nullCols, col) - } - } - return nullCols, nil -} - -func checkPrimaryKeyNotNull(d *ddlCtx, w *worker, t *meta.Meta, job *model.Job, - tblInfo *model.TableInfo, indexInfo *model.IndexInfo) (warnings []string, err error) { - if !indexInfo.Primary { - return nil, nil - } - - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return nil, err - } - nullCols, err := getNullColInfos(tblInfo, indexInfo) - if err != nil { - return nil, err - } - if len(nullCols) == 0 { - return nil, nil - } - - err = modifyColsFromNull2NotNull(w, dbInfo, tblInfo, nullCols, &model.ColumnInfo{Name: model.NewCIStr("")}, false) - if err == nil { - return nil, nil - } - _, err = convertAddIdxJob2RollbackJob(d, t, job, tblInfo, []*model.IndexInfo{indexInfo}, err) - // TODO: Support non-strict mode. - // warnings = append(warnings, ErrWarnDataTruncated.GenWithStackByArgs(oldCol.Name.L, 0).Error()) - return nil, err -} - -// moveAndUpdateHiddenColumnsToPublic updates the hidden columns to public, and -// moves the hidden columns to proper offsets, so that Table.Columns' states meet the assumption of -// [public, public, ..., public, non-public, non-public, ..., non-public]. -func moveAndUpdateHiddenColumnsToPublic(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { - hiddenColOffset := make(map[int]struct{}, 0) - for _, col := range idxInfo.Columns { - if tblInfo.Columns[col.Offset].Hidden { - hiddenColOffset[col.Offset] = struct{}{} - } - } - if len(hiddenColOffset) == 0 { - return - } - // Find the first non-public column. - firstNonPublicPos := len(tblInfo.Columns) - 1 - for i, c := range tblInfo.Columns { - if c.State != model.StatePublic { - firstNonPublicPos = i - break - } - } - for _, col := range idxInfo.Columns { - tblInfo.Columns[col.Offset].State = model.StatePublic - if _, needMove := hiddenColOffset[col.Offset]; needMove { - tblInfo.MoveColumnInfo(col.Offset, firstNonPublicPos) - } - } -} - -func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK bool) (ver int64, err error) { - // Handle the rolling back job. - if job.IsRollingback() { - ver, err = onDropIndex(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - return ver, nil - } - - // Handle normal job. - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return ver, errors.Trace(err) - } - if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { - return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Create Index")) - } - - unique := make([]bool, 1) - global := make([]bool, 1) - indexNames := make([]model.CIStr, 1) - indexPartSpecifications := make([][]*ast.IndexPartSpecification, 1) - indexOption := make([]*ast.IndexOption, 1) - var sqlMode mysql.SQLMode - var warnings []string - hiddenCols := make([][]*model.ColumnInfo, 1) - - if isPK { - // Notice: sqlMode and warnings is used to support non-strict mode. - err = job.DecodeArgs(&unique[0], &indexNames[0], &indexPartSpecifications[0], &indexOption[0], &sqlMode, &warnings, &global[0]) - } else { - err = job.DecodeArgs(&unique[0], &indexNames[0], &indexPartSpecifications[0], &indexOption[0], &hiddenCols[0], &global[0]) - if err != nil { - err = job.DecodeArgs(&unique, &indexNames, &indexPartSpecifications, &indexOption, &hiddenCols, &global) - } - } - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - allIndexInfos := make([]*model.IndexInfo, 0, len(indexNames)) - for i, indexName := range indexNames { - indexInfo := tblInfo.FindIndexByName(indexName.L) - if indexInfo != nil && indexInfo.State == model.StatePublic { - job.State = model.JobStateCancelled - err = dbterror.ErrDupKeyName.GenWithStack("index already exist %s", indexName) - if isPK { - err = infoschema.ErrMultiplePriKey - } - return ver, err - } - if indexInfo == nil { - for _, hiddenCol := range hiddenCols[i] { - columnInfo := model.FindColumnInfo(tblInfo.Columns, hiddenCol.Name.L) - if columnInfo != nil && columnInfo.State == model.StatePublic { - // We already have a column with the same column name. - job.State = model.JobStateCancelled - // TODO: refine the error message - return ver, infoschema.ErrColumnExists.GenWithStackByArgs(hiddenCol.Name) - } - } - } - if indexInfo == nil { - if len(hiddenCols) > 0 { - for _, hiddenCol := range hiddenCols[i] { - InitAndAddColumnToTable(tblInfo, hiddenCol) - } - } - if err = checkAddColumnTooManyColumns(len(tblInfo.Columns)); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - indexInfo, err = BuildIndexInfo( - nil, - tblInfo.Columns, - indexName, - isPK, - unique[i], - global[i], - indexPartSpecifications[i], - indexOption[i], - model.StateNone, - ) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - if isPK { - if _, err = CheckPKOnGeneratedColumn(tblInfo, indexPartSpecifications[i]); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - } - indexInfo.ID = AllocateIndexID(tblInfo) - tblInfo.Indices = append(tblInfo.Indices, indexInfo) - if err = checkTooManyIndexes(tblInfo.Indices); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // Here we need do this check before set state to `DeleteOnly`, - // because if hidden columns has been set to `DeleteOnly`, - // the `DeleteOnly` columns are missing when we do this check. - if err := checkInvisibleIndexOnPK(tblInfo); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - logutil.BgLogger().Info("run add index job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Reflect("indexInfo", indexInfo)) - } - allIndexInfos = append(allIndexInfos, indexInfo) - } - - originalState := allIndexInfos[0].State - -SwitchIndexState: - switch allIndexInfos[0].State { - case model.StateNone: - // none -> delete only - var reorgTp model.ReorgType - reorgTp, err = pickBackfillType(w.ctx, job, allIndexInfos[0].Unique, d) - if err != nil { - if !errorIsRetryable(err, job) { - job.State = model.JobStateCancelled - } - return ver, err - } - if reorgTp.NeedMergeProcess() { - // Increase telemetryAddIndexIngestUsage - telemetryAddIndexIngestUsage.Inc() - for _, indexInfo := range allIndexInfos { - indexInfo.BackfillState = model.BackfillStateRunning - } - } - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateDeleteOnly - moveAndUpdateHiddenColumnsToPublic(tblInfo, indexInfo) - } - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != model.StateDeleteOnly) - if err != nil { - return ver, err - } - job.SchemaState = model.StateDeleteOnly - case model.StateDeleteOnly: - // delete only -> write only - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateWriteOnly - _, err = checkPrimaryKeyNotNull(d, w, t, job, tblInfo, indexInfo) - if err != nil { - break SwitchIndexState - } - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteOnly) - if err != nil { - return ver, err - } - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - // write only -> reorganization - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateWriteReorganization - _, err = checkPrimaryKeyNotNull(d, w, t, job, tblInfo, indexInfo) - if err != nil { - break SwitchIndexState - } - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteReorganization) - if err != nil { - return ver, err - } - // Initialize SnapshotVer to 0 for later reorganization check. - job.SnapshotVer = 0 - job.SchemaState = model.StateWriteReorganization - case model.StateWriteReorganization: - // reorganization -> public - tbl, err := getTable(d.store, schemaID, tblInfo) - if err != nil { - return ver, errors.Trace(err) - } - - var done bool - if job.MultiSchemaInfo != nil { - done, ver, err = doReorgWorkForCreateIndexMultiSchema(w, d, t, job, tbl, allIndexInfos) - } else { - done, ver, err = doReorgWorkForCreateIndex(w, d, t, job, tbl, allIndexInfos) - } - if !done { - return ver, err - } - - // Set column index flag. - for _, indexInfo := range allIndexInfos { - AddIndexColumnFlag(tblInfo, indexInfo) - if isPK { - if err = UpdateColsNull2NotNull(tblInfo, indexInfo); err != nil { - return ver, errors.Trace(err) - } - } - indexInfo.State = model.StatePublic - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StatePublic) - if err != nil { - return ver, errors.Trace(err) - } - - allIndexIDs := make([]int64, 0, len(allIndexInfos)) - ifExists := make([]bool, 0, len(allIndexInfos)) - for _, indexInfo := range allIndexInfos { - allIndexIDs = append(allIndexIDs, indexInfo.ID) - ifExists = append(ifExists, false) - } - job.Args = []interface{}{allIndexIDs, ifExists, getPartitionIDs(tbl.Meta())} - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - if !job.ReorgMeta.IsDistReorg && job.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { - ingest.LitBackCtxMgr.Unregister(job.ID) - } - logutil.BgLogger().Info("run add index job done", zap.String("category", "ddl"), - zap.String("charset", job.Charset), - zap.String("collation", job.Collate)) - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("index", tblInfo.State) - } - - return ver, errors.Trace(err) -} - -// pickBackfillType determines which backfill process will be used. -func pickBackfillType(ctx context.Context, job *model.Job, unique bool, d *ddlCtx) (model.ReorgType, error) { - if job.ReorgMeta.ReorgTp != model.ReorgTypeNone { - // The backfill task has been started. - // Don't change the backfill type. - return job.ReorgMeta.ReorgTp, nil - } - if !IsEnableFastReorg() { - job.ReorgMeta.ReorgTp = model.ReorgTypeTxn - return model.ReorgTypeTxn, nil - } - if ingest.LitInitialized { - available, err := ingest.LitBackCtxMgr.CheckAvailable() - if err != nil { - return model.ReorgTypeNone, err - } - if available { - ctx := logutil.WithCategory(ctx, "ddl-ingest") - err = cleanupSortPath(ctx, job.ID) - if err != nil { - return model.ReorgTypeNone, err - } - if variable.EnableDistTask.Load() { - _, err = ingest.LitBackCtxMgr.Register(ctx, unique, job.ID, d.etcdCli, job.ReorgMeta.ResourceGroupName) - } else { - _, err = ingest.LitBackCtxMgr.Register(ctx, unique, job.ID, nil, job.ReorgMeta.ResourceGroupName) - } - if err != nil { - return model.ReorgTypeNone, err - } - job.ReorgMeta.ReorgTp = model.ReorgTypeLitMerge - if variable.EnableDistTask.Load() { - job.ReorgMeta.IsDistReorg = true - } - return model.ReorgTypeLitMerge, nil - } - } - // The lightning environment is unavailable, but we can still use the txn-merge backfill. - logutil.BgLogger().Info("fallback to txn-merge backfill process", zap.String("category", "ddl"), - zap.Bool("lightning env initialized", ingest.LitInitialized)) - job.ReorgMeta.ReorgTp = model.ReorgTypeTxnMerge - return model.ReorgTypeTxnMerge, nil -} - -// cleanupSortPath is used to clean up the temp data of the previous jobs. -// Because we don't remove all the files after the support of checkpoint, -// there maybe some stale files in the sort path if TiDB is killed during the backfill process. -func cleanupSortPath(ctx context.Context, currentJobID int64) error { - sortPath := ingest.ConfigSortPath() - err := os.MkdirAll(sortPath, 0700) - if err != nil { - return errors.Trace(err) - } - entries, err := os.ReadDir(sortPath) - if err != nil { - logutil.Logger(ctx).Warn(ingest.LitErrReadSortPath, zap.Error(err)) - return errors.Trace(err) - } - for _, entry := range entries { - if !entry.IsDir() { - continue - } - jobID, err := ingest.DecodeBackendTag(entry.Name()) - if err != nil { - logutil.Logger(ctx).Warn(ingest.LitErrCleanSortPath, zap.Error(err)) - continue - } - if _, ok := ingest.LitBackCtxMgr.Load(jobID); ok { - // The job is still running, skip it. - logutil.Logger(ctx).Warn("the job is still running, skip removing it", - zap.Int64("running job ID", jobID)) - continue - } - // Remove all the temp data of the previous done jobs. - if jobID < currentJobID { - logutil.Logger(ctx).Info("remove stale temp index data", - zap.Int64("jobID", jobID), zap.Int64("currentJobID", currentJobID)) - err := os.RemoveAll(filepath.Join(sortPath, entry.Name())) - if err != nil { - logutil.Logger(ctx).Warn(ingest.LitErrCleanSortPath, zap.Error(err)) - return nil - } - } - } - return nil -} - -// IngestJobsNotExisted checks the ddl about `add index` with ingest method not existed. -func IngestJobsNotExisted(ctx sessionctx.Context) bool { - se := sess.NewSession(ctx) - template := "select job_meta from mysql.tidb_ddl_job where reorg and (type = %d or type = %d) and processing;" - sql := fmt.Sprintf(template, model.ActionAddIndex, model.ActionAddPrimaryKey) - rows, err := se.Execute(context.Background(), sql, "check-pitr") - if err != nil { - logutil.BgLogger().Warn("cannot check ingest job", zap.Error(err)) - return false - } - for _, row := range rows { - jobBinary := row.GetBytes(0) - runJob := model.Job{} - err := runJob.Decode(jobBinary) - if err != nil { - logutil.BgLogger().Warn("cannot check ingest job", zap.Error(err)) - return false - } - // Check whether this add index job is using lightning to do the backfill work. - if runJob.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { - return false - } - } - return true -} - -func doReorgWorkForCreateIndexMultiSchema(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, - tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { - if job.MultiSchemaInfo.Revertible { - done, ver, err = doReorgWorkForCreateIndex(w, d, t, job, tbl, allIndexInfos) - if done { - job.MarkNonRevertible() - if err == nil { - ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) - } - } - // We need another round to wait for all the others sub-jobs to finish. - return false, ver, err - } - return true, ver, err -} - -func doReorgWorkForCreateIndex(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, - tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { - var reorgTp model.ReorgType - reorgTp, err = pickBackfillType(w.ctx, job, allIndexInfos[0].Unique, d) - if err != nil { - return false, ver, err - } - if !reorgTp.NeedMergeProcess() { - return runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) - } - switch allIndexInfos[0].BackfillState { - case model.BackfillStateRunning: - logutil.BgLogger().Info("index backfill state running", zap.String("category", "ddl"), - zap.Int64("job ID", job.ID), zap.String("table", tbl.Meta().Name.O), - zap.Bool("ingest mode", reorgTp == model.ReorgTypeLitMerge), - zap.String("index", allIndexInfos[0].Name.O)) - switch reorgTp { - case model.ReorgTypeLitMerge: - if job.ReorgMeta.IsDistReorg { - done, ver, err = runIngestReorgJobDist(w, d, t, job, tbl, allIndexInfos) - } else { - done, ver, err = runIngestReorgJob(w, d, t, job, tbl, allIndexInfos) - } - case model.ReorgTypeTxnMerge: - done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) - } - if err != nil || !done { - return false, ver, errors.Trace(err) - } - for _, indexInfo := range allIndexInfos { - indexInfo.BackfillState = model.BackfillStateReadyToMerge - } - ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) - return false, ver, errors.Trace(err) - case model.BackfillStateReadyToMerge: - logutil.BgLogger().Info("index backfill state ready to merge", zap.String("category", "ddl"), zap.Int64("job ID", job.ID), - zap.String("table", tbl.Meta().Name.O), zap.String("index", allIndexInfos[0].Name.O)) - for _, indexInfo := range allIndexInfos { - indexInfo.BackfillState = model.BackfillStateMerging - } - if reorgTp == model.ReorgTypeLitMerge { - ingest.LitBackCtxMgr.Unregister(job.ID) - } - job.SnapshotVer = 0 // Reset the snapshot version for merge index reorg. - ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) - return false, ver, errors.Trace(err) - case model.BackfillStateMerging: - done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, true) - if !done { - return false, ver, err - } - for _, indexInfo := range allIndexInfos { - indexInfo.BackfillState = model.BackfillStateInapplicable // Prevent double-write on this index. - } - return true, ver, err - default: - return false, 0, dbterror.ErrInvalidDDLState.GenWithStackByArgs("backfill", allIndexInfos[0].BackfillState) - } -} - -func runIngestReorgJobDist(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, - tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { - done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) - if err != nil { - return false, ver, errors.Trace(err) - } - - if !done { - return false, ver, nil - } - - return true, ver, nil -} - -func runIngestReorgJob(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, - tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { - bc, ok := ingest.LitBackCtxMgr.Load(job.ID) - if ok && bc.Done() { - return true, 0, nil - } - ctx := logutil.WithCategory(w.ctx, "ddl-ingest") - bc, err = ingest.LitBackCtxMgr.Register(ctx, allIndexInfos[0].Unique, job.ID, nil, job.ReorgMeta.ResourceGroupName) - if err != nil { - ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) - return false, ver, errors.Trace(err) - } - done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) - if err != nil { - if !errorIsRetryable(err, job) { - logutil.BgLogger().Warn("run reorg job failed, convert job to rollback", zap.String("category", "ddl"), - zap.String("job", job.String()), zap.Error(err)) - ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) - } - return false, ver, errors.Trace(err) - } - if !done { - return false, ver, nil - } - for _, indexInfo := range allIndexInfos { - err = bc.FinishImport(indexInfo.ID, indexInfo.Unique, tbl) - if err != nil { - if common.ErrFoundDuplicateKeys.Equal(err) { - err = convertToKeyExistsErr(err, indexInfo, tbl.Meta()) - } - if kv.ErrKeyExists.Equal(err) { - logutil.BgLogger().Warn("import index duplicate key, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) - ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) - } else { - logutil.BgLogger().Warn("lightning import error", zap.String("category", "ddl"), zap.Error(err)) - if !errorIsRetryable(err, job) { - ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) - } - } - return false, ver, errors.Trace(err) - } - } - bc.SetDone() - return true, ver, nil -} - -func errorIsRetryable(err error, job *model.Job) bool { - if job.ErrorCount+1 >= variable.GetDDLErrorCountLimit() { - return false - } - originErr := errors.Cause(err) - if tErr, ok := originErr.(*terror.Error); ok { - sqlErr := terror.ToSQLError(tErr) - _, ok := dbterror.ReorgRetryableErrCodes[sqlErr.Code] - return ok - } - // For the unknown errors, we should retry. - return true -} - -func convertToKeyExistsErr(originErr error, idxInfo *model.IndexInfo, tblInfo *model.TableInfo) error { - tErr, ok := errors.Cause(originErr).(*terror.Error) - if !ok { - return originErr - } - if len(tErr.Args()) != 2 { - return originErr - } - key, keyIsByte := tErr.Args()[0].([]byte) - value, valIsByte := tErr.Args()[1].([]byte) - if !keyIsByte || !valIsByte { - return originErr - } - return genKeyExistsErr(key, value, idxInfo, tblInfo) -} - -func runReorgJobAndHandleErr(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, - tbl table.Table, allIndexInfos []*model.IndexInfo, mergingTmpIdx bool) (done bool, ver int64, err error) { - elements := make([]*meta.Element, 0, len(allIndexInfos)) - for _, indexInfo := range allIndexInfos { - elements = append(elements, &meta.Element{ID: indexInfo.ID, TypeKey: meta.IndexElementKey}) - } - - failpoint.Inject("mockDMLExecutionStateMerging", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) && allIndexInfos[0].BackfillState == model.BackfillStateMerging && - MockDMLExecutionStateMerging != nil { - MockDMLExecutionStateMerging() - } - }) - - sctx, err1 := w.sessPool.Get() - if err1 != nil { - err = err1 - return - } - defer w.sessPool.Put(sctx) - rh := newReorgHandler(sess.NewSession(sctx)) - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return false, ver, errors.Trace(err) - } - reorgInfo, err := getReorgInfo(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, tbl, elements, mergingTmpIdx) - if err != nil || reorgInfo == nil || reorgInfo.first { - // If we run reorg firstly, we should update the job snapshot version - // and then run the reorg next time. - return false, ver, errors.Trace(err) - } - err = overwriteReorgInfoFromGlobalCheckpoint(w, rh.s, job, reorgInfo) - if err != nil { - return false, ver, errors.Trace(err) - } - err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (addIndexErr error) { - defer util.Recover(metrics.LabelDDL, "onCreateIndex", - func() { - addIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("add table `%v` index `%v` panic", tbl.Meta().Name, allIndexInfos[0].Name) - }, false) - return w.addTableIndex(tbl, reorgInfo) - }) - if err != nil { - if dbterror.ErrPausedDDLJob.Equal(err) { - return false, ver, nil - } - if dbterror.ErrWaitReorgTimeout.Equal(err) { - // if timeout, we should return, check for the owner and re-wait job done. - return false, ver, nil - } - if common.ErrFoundDuplicateKeys.Equal(err) { - // TODO(tangenta): get duplicate column and match index. - err = convertToKeyExistsErr(err, allIndexInfos[0], tbl.Meta()) - } - if !errorIsRetryable(err, job) || - // TODO: Remove this check make it can be retry. Related test is TestModifyColumnReorgInfo. - job.ReorgMeta.IsDistReorg { - logutil.BgLogger().Warn("run add index job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) - ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) - if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { - logutil.BgLogger().Warn("run add index job failed, convert job to rollback, RemoveDDLReorgHandle failed", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err1)) - } - } - return false, ver, errors.Trace(err) - } - failpoint.Inject("mockDMLExecutionStateBeforeImport", func(_ failpoint.Value) { - if MockDMLExecutionStateBeforeImport != nil { - MockDMLExecutionStateBeforeImport() - } - }) - return true, ver, nil -} - -func onDropIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, allIndexInfos, ifExists, err := checkDropIndex(d, t, job) - if err != nil { - if ifExists && dbterror.ErrCantDropFieldOrKey.Equal(err) { - job.Warning = toTError(err) - job.State = model.JobStateDone - return ver, nil - } - return ver, errors.Trace(err) - } - if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { - return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Drop Index")) - } - - if job.MultiSchemaInfo != nil && !job.IsRollingback() && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - job.SchemaState = allIndexInfos[0].State - return updateVersionAndTableInfo(d, t, job, tblInfo, false) - } - - originalState := allIndexInfos[0].State - switch allIndexInfos[0].State { - case model.StatePublic: - // public -> write only - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateWriteOnly - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteOnly) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateWriteOnly: - // write only -> delete only - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateDeleteOnly - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateDeleteOnly) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateDeleteOnly: - // delete only -> reorganization - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateDeleteReorganization - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateDeleteReorganization) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateDeleteReorganization: - // reorganization -> absent - idxIds := make([]int64, 0, len(allIndexInfos)) - for _, indexInfo := range allIndexInfos { - indexInfo.State = model.StateNone - // Set column index flag. - DropIndexColumnFlag(tblInfo, indexInfo) - RemoveDependentHiddenColumns(tblInfo, indexInfo) - removeIndexInfo(tblInfo, indexInfo) - idxIds = append(idxIds, indexInfo.ID) - } - - failpoint.Inject("mockExceedErrorLimit", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - panic("panic test in cancelling add index") - } - }) - - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != model.StateNone) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - if job.IsRollingback() { - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - job.Args[0] = idxIds - } else { - // the partition ids were append by convertAddIdxJob2RollbackJob, it is weird, but for the compatibility, - // we should keep appending the partitions in the convertAddIdxJob2RollbackJob. - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - // Global index key has t{tableID}_ prefix. - // Assign partitionIDs empty to guarantee correct prefix in insertJobIntoDeleteRangeTable. - if allIndexInfos[0].Global { - job.Args = append(job.Args, idxIds[0], []int64{}) - } else { - job.Args = append(job.Args, idxIds[0], getPartitionIDs(tblInfo)) - } - } - default: - return ver, errors.Trace(dbterror.ErrInvalidDDLState.GenWithStackByArgs("index", allIndexInfos[0].State)) - } - job.SchemaState = allIndexInfos[0].State - return ver, errors.Trace(err) -} - -// RemoveDependentHiddenColumns removes hidden columns by the indexInfo. -func RemoveDependentHiddenColumns(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { - hiddenColOffs := make([]int, 0) - for _, indexColumn := range idxInfo.Columns { - col := tblInfo.Columns[indexColumn.Offset] - if col.Hidden { - hiddenColOffs = append(hiddenColOffs, col.Offset) - } - } - // Sort the offset in descending order. - slices.SortFunc(hiddenColOffs, func(a, b int) int { return cmp.Compare(b, a) }) - // Move all the dependent hidden columns to the end. - endOffset := len(tblInfo.Columns) - 1 - for _, offset := range hiddenColOffs { - tblInfo.MoveColumnInfo(offset, endOffset) - } - tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-len(hiddenColOffs)] -} - -func removeIndexInfo(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { - indices := tblInfo.Indices - offset := -1 - for i, idx := range indices { - if idxInfo.ID == idx.ID { - offset = i - break - } - } - if offset == -1 { - // The target index has been removed. - return - } - // Remove the target index. - tblInfo.Indices = append(tblInfo.Indices[:offset], tblInfo.Indices[offset+1:]...) -} - -func checkDropIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (*model.TableInfo, []*model.IndexInfo, bool /* ifExists */, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, false, errors.Trace(err) - } - - indexNames := make([]model.CIStr, 1) - ifExists := make([]bool, 1) - if err = job.DecodeArgs(&indexNames[0], &ifExists[0]); err != nil { - if err = job.DecodeArgs(&indexNames, &ifExists); err != nil { - job.State = model.JobStateCancelled - return nil, nil, false, errors.Trace(err) - } - } - - indexInfos := make([]*model.IndexInfo, 0, len(indexNames)) - for i, idxName := range indexNames { - indexInfo := tblInfo.FindIndexByName(idxName.L) - if indexInfo == nil { - job.State = model.JobStateCancelled - return nil, nil, ifExists[i], dbterror.ErrCantDropFieldOrKey.GenWithStack("index %s doesn't exist", idxName) - } - - // Check that drop primary index will not cause invisible implicit primary index. - if err := checkInvisibleIndexesOnPK(tblInfo, []*model.IndexInfo{indexInfo}, job); err != nil { - job.State = model.JobStateCancelled - return nil, nil, false, errors.Trace(err) - } - - // Double check for drop index needed in foreign key. - if err := checkIndexNeededInForeignKeyInOwner(d, t, job, job.SchemaName, tblInfo, indexInfo); err != nil { - return nil, nil, false, errors.Trace(err) - } - indexInfos = append(indexInfos, indexInfo) - } - return tblInfo, indexInfos, false, nil -} - -func checkInvisibleIndexesOnPK(tblInfo *model.TableInfo, indexInfos []*model.IndexInfo, job *model.Job) error { - newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) - for _, oidx := range tblInfo.Indices { - needAppend := true - for _, idx := range indexInfos { - if idx.Name.L == oidx.Name.L { - needAppend = false - break - } - } - if needAppend { - newIndices = append(newIndices, oidx) - } - } - newTbl := tblInfo.Clone() - newTbl.Indices = newIndices - if err := checkInvisibleIndexOnPK(newTbl); err != nil { - job.State = model.JobStateCancelled - return err - } - - return nil -} - -func checkRenameIndex(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIStr, model.CIStr, error) { - var from, to model.CIStr - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, from, to, errors.Trace(err) - } - - if err := job.DecodeArgs(&from, &to); err != nil { - job.State = model.JobStateCancelled - return nil, from, to, errors.Trace(err) - } - - // Double check. See function `RenameIndex` in ddl_api.go - duplicate, err := ValidateRenameIndex(from, to, tblInfo) - if duplicate { - return nil, from, to, nil - } - if err != nil { - job.State = model.JobStateCancelled - return nil, from, to, errors.Trace(err) - } - return tblInfo, from, to, errors.Trace(err) -} - -func checkAlterIndexVisibility(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIStr, bool, error) { - var ( - indexName model.CIStr - invisible bool - ) - - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, indexName, invisible, errors.Trace(err) - } - - if err := job.DecodeArgs(&indexName, &invisible); err != nil { - job.State = model.JobStateCancelled - return nil, indexName, invisible, errors.Trace(err) - } - - skip, err := validateAlterIndexVisibility(nil, indexName, invisible, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return nil, indexName, invisible, errors.Trace(err) - } - if skip { - job.State = model.JobStateDone - return nil, indexName, invisible, nil - } - return tblInfo, indexName, invisible, nil -} - -// indexRecord is the record information of an index. -type indexRecord struct { - handle kv.Handle - key []byte // It's used to lock a record. Record it to reduce the encoding time. - vals []types.Datum // It's the index values. - rsData []types.Datum // It's the restored data for handle. - skip bool // skip indicates that the index key is already exists, we should not add it. -} - -type baseIndexWorker struct { - *backfillCtx - indexes []table.Index - - tp backfillerType - // The following attributes are used to reduce memory allocation. - defaultVals []types.Datum - idxRecords []*indexRecord - rowMap map[int64]types.Datum - rowDecoder *decoder.RowDecoder -} - -type addIndexTxnWorker struct { - baseIndexWorker - - // The following attributes are used to reduce memory allocation. - idxKeyBufs [][]byte - batchCheckKeys []kv.Key - batchCheckValues [][]byte - distinctCheckFlags []bool - recordIdx []int -} - -func newAddIndexTxnWorker( - decodeColMap map[int64]decoder.Column, - t table.PhysicalTable, - bfCtx *backfillCtx, - jobID int64, - elements []*meta.Element, - eleTypeKey []byte, -) (*addIndexTxnWorker, error) { - if !bytes.Equal(eleTypeKey, meta.IndexElementKey) { - logutil.BgLogger().Error("Element type for addIndexTxnWorker incorrect", - zap.Int64("job ID", jobID), zap.ByteString("element type", eleTypeKey), zap.Int64("element ID", elements[0].ID)) - return nil, errors.Errorf("element type is not index, typeKey: %v", eleTypeKey) - } - - allIndexes := make([]table.Index, 0, len(elements)) - for _, elem := range elements { - if !bytes.Equal(elem.TypeKey, meta.IndexElementKey) { - continue - } - indexInfo := model.FindIndexInfoByID(t.Meta().Indices, elem.ID) - index := tables.NewIndex(t.GetPhysicalID(), t.Meta(), indexInfo) - allIndexes = append(allIndexes, index) - } - rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) - - return &addIndexTxnWorker{ - baseIndexWorker: baseIndexWorker{ - backfillCtx: bfCtx, - indexes: allIndexes, - rowDecoder: rowDecoder, - defaultVals: make([]types.Datum, len(t.WritableCols())), - rowMap: make(map[int64]types.Datum, len(decodeColMap)), - }, - }, nil -} - -func (w *baseIndexWorker) AddMetricInfo(cnt float64) { - w.metricCounter.Add(cnt) -} - -func (w *baseIndexWorker) String() string { - return w.tp.String() -} - -func (w *baseIndexWorker) GetCtx() *backfillCtx { - return w.backfillCtx -} - -// mockNotOwnerErrOnce uses to make sure `notOwnerErr` only mock error once. -var mockNotOwnerErrOnce uint32 - -// getIndexRecord gets index columns values use w.rowDecoder, and generate indexRecord. -func (w *baseIndexWorker) getIndexRecord(idxInfo *model.IndexInfo, handle kv.Handle, recordKey []byte) (*indexRecord, error) { - cols := w.table.WritableCols() - failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { - if valStr, ok := val.(string); ok { - switch valStr { - case "cantDecodeRecordErr": - failpoint.Return(nil, errors.Trace(dbterror.ErrCantDecodeRecord.GenWithStackByArgs("index", - errors.New("mock can't decode record error")))) - case "modifyColumnNotOwnerErr": - if idxInfo.Name.O == "_Idx$_idx_0" && handle.IntValue() == 7168 && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 0, 1) { - failpoint.Return(nil, errors.Trace(dbterror.ErrNotOwner)) - } - case "addIdxNotOwnerErr": - // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. - // First step, we need to exit "addPhysicalTableIndex". - if idxInfo.Name.O == "idx2" && handle.IntValue() == 6144 && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 1, 2) { - failpoint.Return(nil, errors.Trace(dbterror.ErrNotOwner)) - } - } - } - }) - idxVal := make([]types.Datum, len(idxInfo.Columns)) - var err error - for j, v := range idxInfo.Columns { - col := cols[v.Offset] - idxColumnVal, ok := w.rowMap[col.ID] - if ok { - idxVal[j] = idxColumnVal - continue - } - idxColumnVal, err = tables.GetColDefaultValue(w.sessCtx, col, w.defaultVals) - if err != nil { - return nil, errors.Trace(err) - } - - idxVal[j] = idxColumnVal - } - - rsData := tables.TryGetHandleRestoredDataWrapper(w.table.Meta(), nil, w.rowMap, idxInfo) - idxRecord := &indexRecord{handle: handle, key: recordKey, vals: idxVal, rsData: rsData} - return idxRecord, nil -} - -func (w *baseIndexWorker) cleanRowMap() { - for id := range w.rowMap { - delete(w.rowMap, id) - } -} - -// getNextKey gets next key of entry that we are going to process. -func (w *baseIndexWorker) getNextKey(taskRange reorgBackfillTask, taskDone bool) (nextKey kv.Key) { - if !taskDone { - // The task is not done. So we need to pick the last processed entry's handle and add one. - lastHandle := w.idxRecords[len(w.idxRecords)-1].handle - recordKey := tablecodec.EncodeRecordKey(taskRange.physicalTable.RecordPrefix(), lastHandle) - return recordKey.Next() - } - return taskRange.endKey -} - -func (w *baseIndexWorker) updateRowDecoder(handle kv.Handle, rawRecord []byte) error { - sysZone := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() - _, err := w.rowDecoder.DecodeAndEvalRowWithMap(w.sessCtx, handle, rawRecord, sysZone, w.rowMap) - return errors.Trace(err) -} - -// fetchRowColVals fetch w.batchCnt count records that need to reorganize indices, and build the corresponding indexRecord slice. -// fetchRowColVals returns: -// 1. The corresponding indexRecord slice. -// 2. Next handle of entry that we need to process. -// 3. Boolean indicates whether the task is done. -// 4. error occurs in fetchRowColVals. nil if no error occurs. -func (w *baseIndexWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*indexRecord, kv.Key, bool, error) { - // TODO: use tableScan to prune columns. - w.idxRecords = w.idxRecords[:0] - startTime := time.Now() - - // taskDone means that the reorged handle is out of taskRange.endHandle. - taskDone := false - oprStartTime := startTime - err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, taskRange.physicalTable.RecordPrefix(), txn.StartTS(), - taskRange.startKey, taskRange.endKey, func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { - oprEndTime := time.Now() - logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in baseIndexWorker fetchRowColVals", 0) - oprStartTime = oprEndTime - - taskDone = recordKey.Cmp(taskRange.endKey) >= 0 - - if taskDone || len(w.idxRecords) >= w.batchCnt { - return false, nil - } - - // Decode one row, generate records of this row. - err := w.updateRowDecoder(handle, rawRow) - if err != nil { - return false, err - } - for _, index := range w.indexes { - idxRecord, err1 := w.getIndexRecord(index.Meta(), handle, recordKey) - if err1 != nil { - return false, errors.Trace(err1) - } - w.idxRecords = append(w.idxRecords, idxRecord) - } - // If there are generated column, rowDecoder will use column value that not in idxInfo.Columns to calculate - // the generated value, so we need to clear up the reusing map. - w.cleanRowMap() - - if recordKey.Cmp(taskRange.endKey) == 0 { - taskDone = true - return false, nil - } - return true, nil - }) - - if len(w.idxRecords) == 0 { - taskDone = true - } - - logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Stringer("worker", w), zap.Uint64("txnStartTS", txn.StartTS()), - zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) - return w.idxRecords, w.getNextKey(taskRange, taskDone), taskDone, errors.Trace(err) -} - -func (w *addIndexTxnWorker) initBatchCheckBufs(batchCount int) { - if len(w.idxKeyBufs) < batchCount { - w.idxKeyBufs = make([][]byte, batchCount) - } - - w.batchCheckKeys = w.batchCheckKeys[:0] - w.batchCheckValues = w.batchCheckValues[:0] - w.distinctCheckFlags = w.distinctCheckFlags[:0] - w.recordIdx = w.recordIdx[:0] -} - -func (w *addIndexTxnWorker) checkHandleExists(idxInfo *model.IndexInfo, key kv.Key, value []byte, handle kv.Handle) error { - tblInfo := w.table.Meta() - idxColLen := len(idxInfo.Columns) - h, err := tablecodec.DecodeIndexHandle(key, value, idxColLen) - if err != nil { - return errors.Trace(err) - } - hasBeenBackFilled := h.Equal(handle) - if hasBeenBackFilled { - return nil - } - return genKeyExistsErr(key, value, idxInfo, tblInfo) -} - -func genKeyExistsErr(key, value []byte, idxInfo *model.IndexInfo, tblInfo *model.TableInfo) error { - idxColLen := len(idxInfo.Columns) - indexName := fmt.Sprintf("%s.%s", tblInfo.Name.String(), idxInfo.Name.String()) - colInfos := tables.BuildRowcodecColInfoForIndexColumns(idxInfo, tblInfo) - values, err := tablecodec.DecodeIndexKV(key, value, idxColLen, tablecodec.HandleNotNeeded, colInfos) - if err != nil { - logutil.BgLogger().Warn("decode index key value failed", zap.String("index", indexName), - zap.String("key", hex.EncodeToString(key)), zap.String("value", hex.EncodeToString(value)), zap.Error(err)) - return kv.ErrKeyExists.FastGenByArgs(key, indexName) - } - valueStr := make([]string, 0, idxColLen) - for i, val := range values[:idxColLen] { - d, err := tablecodec.DecodeColumnValue(val, colInfos[i].Ft, time.Local) - if err != nil { - logutil.BgLogger().Warn("decode column value failed", zap.String("index", indexName), - zap.String("key", hex.EncodeToString(key)), zap.String("value", hex.EncodeToString(value)), zap.Error(err)) - return kv.ErrKeyExists.FastGenByArgs(key, indexName) - } - str, err := d.ToString() - if err != nil { - str = string(val) - } - if types.IsBinaryStr(colInfos[i].Ft) || types.IsTypeBit(colInfos[i].Ft) { - str = util.FmtNonASCIIPrintableCharToHex(str) - } - valueStr = append(valueStr, str) - } - return kv.ErrKeyExists.FastGenByArgs(strings.Join(valueStr, "-"), indexName) -} - -// batchCheckUniqueKey checks the unique keys in the batch. -// Note that `idxRecords` may belong to multiple indexes. -func (w *addIndexTxnWorker) batchCheckUniqueKey(txn kv.Transaction, idxRecords []*indexRecord) error { - w.initBatchCheckBufs(len(idxRecords)) - stmtCtx := w.sessCtx.GetSessionVars().StmtCtx - uniqueBatchKeys := make([]kv.Key, 0, len(idxRecords)) - cnt := 0 - for i, record := range idxRecords { - idx := w.indexes[i%len(w.indexes)] - if !idx.Meta().Unique { - // non-unique key need not to check, use `nil` as a placeholder to keep - // `idxRecords[i]` belonging to `indexes[i%len(indexes)]`. - w.batchCheckKeys = append(w.batchCheckKeys, nil) - w.batchCheckValues = append(w.batchCheckValues, nil) - w.distinctCheckFlags = append(w.distinctCheckFlags, false) - w.recordIdx = append(w.recordIdx, 0) - continue - } - // skip by default. - idxRecords[i].skip = true - iter := idx.GenIndexKVIter(stmtCtx, record.vals, record.handle, idxRecords[i].rsData) - for iter.Valid() { - var buf []byte - if cnt < len(w.idxKeyBufs) { - buf = w.idxKeyBufs[cnt] - } - key, val, distinct, err := iter.Next(buf) - if err != nil { - return errors.Trace(err) - } - if cnt < len(w.idxKeyBufs) { - w.idxKeyBufs[cnt] = key - } else { - w.idxKeyBufs = append(w.idxKeyBufs, key) - } - cnt++ - w.batchCheckKeys = append(w.batchCheckKeys, key) - w.batchCheckValues = append(w.batchCheckValues, val) - w.distinctCheckFlags = append(w.distinctCheckFlags, distinct) - w.recordIdx = append(w.recordIdx, i) - uniqueBatchKeys = append(uniqueBatchKeys, key) - } - } - - if len(uniqueBatchKeys) == 0 { - return nil - } - - batchVals, err := txn.BatchGet(context.Background(), uniqueBatchKeys) - if err != nil { - return errors.Trace(err) - } - - // 1. unique-key/primary-key is duplicate and the handle is equal, skip it. - // 2. unique-key/primary-key is duplicate and the handle is not equal, return duplicate error. - // 3. non-unique-key is duplicate, skip it. - for i, key := range w.batchCheckKeys { - if len(key) == 0 { - continue - } - idx := w.indexes[i%len(w.indexes)] - val, found := batchVals[string(key)] - if found { - if w.distinctCheckFlags[i] { - if err := w.checkHandleExists(idx.Meta(), key, val, idxRecords[w.recordIdx[i]].handle); err != nil { - return errors.Trace(err) - } - } - } else if w.distinctCheckFlags[i] { - // The keys in w.batchCheckKeys also maybe duplicate, - // so we need to backfill the not found key into `batchVals` map. - batchVals[string(key)] = w.batchCheckValues[i] - } - idxRecords[w.recordIdx[i]].skip = found && idxRecords[w.recordIdx[i]].skip - } - // Constrains is already checked. - stmtCtx.BatchCheck = true - return nil -} - -type addIndexIngestWorker struct { - ctx context.Context - d *ddlCtx - metricCounter prometheus.Counter - sessCtx sessionctx.Context - - tbl table.PhysicalTable - indexes []table.Index - writers []ingest.Writer - copReqSenderPool *copReqSenderPool - checkpointMgr *ingest.CheckpointManager - - resultCh chan *backfillResult - jobID int64 - distribute bool -} - -func newAddIndexIngestWorker( - ctx context.Context, - t table.PhysicalTable, - d *ddlCtx, - engines []ingest.Engine, - resultCh chan *backfillResult, - jobID int64, - schemaName string, - indexIDs []int64, - writerID int, - copReqSenderPool *copReqSenderPool, - sessCtx sessionctx.Context, - checkpointMgr *ingest.CheckpointManager, - distribute bool, -) (*addIndexIngestWorker, error) { - indexes := make([]table.Index, 0, len(indexIDs)) - writers := make([]ingest.Writer, 0, len(indexIDs)) - for i, indexID := range indexIDs { - indexInfo := model.FindIndexInfoByID(t.Meta().Indices, indexID) - index := tables.NewIndex(t.GetPhysicalID(), t.Meta(), indexInfo) - lw, err := engines[i].CreateWriter(writerID) - if err != nil { - return nil, err - } - indexes = append(indexes, index) - writers = append(writers, lw) - } - - return &addIndexIngestWorker{ - ctx: ctx, - d: d, - sessCtx: sessCtx, - metricCounter: metrics.BackfillTotalCounter.WithLabelValues( - metrics.GenerateReorgLabel("add_idx_rate", schemaName, t.Meta().Name.O)), - tbl: t, - indexes: indexes, - writers: writers, - copReqSenderPool: copReqSenderPool, - resultCh: resultCh, - jobID: jobID, - checkpointMgr: checkpointMgr, - distribute: distribute, - }, nil -} - -// WriteLocal will write index records to lightning engine. -func (w *addIndexIngestWorker) WriteLocal(rs *IndexRecordChunk) (count int, nextKey kv.Key, err error) { - oprStartTime := time.Now() - copCtx := w.copReqSenderPool.copCtx - vars := w.sessCtx.GetSessionVars() - cnt, lastHandle, err := writeChunkToLocal( - w.ctx, w.writers, w.indexes, copCtx, vars, rs.Chunk) - if err != nil || cnt == 0 { - return 0, nil, err - } - w.metricCounter.Add(float64(cnt)) - logSlowOperations(time.Since(oprStartTime), "writeChunkToLocal", 3000) - nextKey = tablecodec.EncodeRecordKey(w.tbl.RecordPrefix(), lastHandle) - return cnt, nextKey, nil -} - -func writeChunkToLocal( - ctx context.Context, - writers []ingest.Writer, - indexes []table.Index, - copCtx copr.CopContext, - vars *variable.SessionVars, - copChunk *chunk.Chunk, -) (int, kv.Handle, error) { - sCtx, writeBufs := vars.StmtCtx, vars.GetWriteStmtBufs() - iter := chunk.NewIterator4Chunk(copChunk) - c := copCtx.GetBase() - - maxIdxColCnt := maxIndexColumnCount(indexes) - idxDataBuf := make([]types.Datum, maxIdxColCnt) - handleDataBuf := make([]types.Datum, len(c.HandleOutputOffsets)) - count := 0 - var lastHandle kv.Handle - - unlockFns := make([]func(), 0, len(writers)) - for _, w := range writers { - unlock := w.LockForWrite() - unlockFns = append(unlockFns, unlock) - } - defer func() { - for _, unlock := range unlockFns { - unlock() - } - }() - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - handleDataBuf = handleDataBuf[:0] - handleDataBuf := extractDatumByOffsets(row, c.HandleOutputOffsets, c.ExprColumnInfos, handleDataBuf) - handle, err := buildHandle(handleDataBuf, c.TableInfo, c.PrimaryKeyInfo, sCtx) - if err != nil { - return 0, nil, errors.Trace(err) - } - for i, index := range indexes { - idxID := index.Meta().ID - idxDataBuf = idxDataBuf[:0] - idxDataBuf = extractDatumByOffsets( - row, copCtx.IndexColumnOutputOffsets(idxID), c.ExprColumnInfos, idxDataBuf) - rsData := getRestoreData(c.TableInfo, copCtx.IndexInfo(idxID), c.PrimaryKeyInfo, handleDataBuf) - err = writeOneKVToLocal(ctx, writers[i], index, sCtx, writeBufs, idxDataBuf, rsData, handle) - if err != nil { - return 0, nil, errors.Trace(err) - } - } - count++ - lastHandle = handle - } - return count, lastHandle, nil -} - -func maxIndexColumnCount(indexes []table.Index) int { - maxCnt := 0 - for _, idx := range indexes { - colCnt := len(idx.Meta().Columns) - if colCnt > maxCnt { - maxCnt = colCnt - } - } - return maxCnt -} - -func writeOneKVToLocal( - ctx context.Context, - writer ingest.Writer, - index table.Index, - sCtx *stmtctx.StatementContext, - writeBufs *variable.WriteStmtBufs, - idxDt, rsData []types.Datum, - handle kv.Handle, -) error { - iter := index.GenIndexKVIter(sCtx, idxDt, handle, rsData) - for iter.Valid() { - key, idxVal, _, err := iter.Next(writeBufs.IndexKeyBuf) - if err != nil { - return errors.Trace(err) - } - failpoint.Inject("mockLocalWriterPanic", func() { - panic("mock panic") - }) - if !index.Meta().Unique { - handle = nil - } - err = writer.WriteRow(ctx, key, idxVal, handle) - if err != nil { - return errors.Trace(err) - } - failpoint.Inject("mockLocalWriterError", func() { - failpoint.Return(errors.New("mock engine error")) - }) - writeBufs.IndexKeyBuf = key - } - return nil -} - -// BackfillData will backfill table index in a transaction. A lock corresponds to a rowKey if the value of rowKey is changed, -// Note that index columns values may change, and an index is not allowed to be added, so the txn will rollback and retry. -// BackfillData will add w.batchCnt indices once, default value of w.batchCnt is 128. -func (w *addIndexTxnWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { - failpoint.Inject("errorMockPanic", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - panic("panic test") - } - }) - - oprStartTime := time.Now() - jobID := handleRange.getJobID() - ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) - errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) (err error) { - taskCtx.finishTS = txn.StartTS() - taskCtx.addedCount = 0 - taskCtx.scanCount = 0 - txn.SetOption(kv.Priority, handleRange.priority) - if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(jobID); tagger != nil { - txn.SetOption(kv.ResourceGroupTagger, tagger) - } - txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) - - idxRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) - if err != nil { - return errors.Trace(err) - } - taskCtx.nextKey = nextKey - taskCtx.done = taskDone - - err = w.batchCheckUniqueKey(txn, idxRecords) - if err != nil { - return errors.Trace(err) - } - - for i, idxRecord := range idxRecords { - taskCtx.scanCount++ - // The index is already exists, we skip it, no needs to backfill it. - // The following update, delete, insert on these rows, TiDB can handle it correctly. - if idxRecord.skip { - continue - } - - // We need to add this lock to make sure pessimistic transaction can realize this operation. - // For the normal pessimistic transaction, it's ok. But if async commit is used, it may lead to inconsistent data and index. - err := txn.LockKeys(context.Background(), new(kv.LockCtx), idxRecord.key) - if err != nil { - return errors.Trace(err) - } - - handle, err := w.indexes[i%len(w.indexes)].Create( - w.sessCtx, txn, idxRecord.vals, idxRecord.handle, idxRecord.rsData, table.WithIgnoreAssertion, table.FromBackfill) - if err != nil { - if kv.ErrKeyExists.Equal(err) && idxRecord.handle.Equal(handle) { - // Index already exists, skip it. - continue - } - return errors.Trace(err) - } - taskCtx.addedCount++ - } - - return nil - }) - logSlowOperations(time.Since(oprStartTime), "AddIndexBackfillData", 3000) - failpoint.Inject("mockDMLExecution", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) && MockDMLExecution != nil { - MockDMLExecution() - } - }) - return -} - -// MockDMLExecution is only used for test. -var MockDMLExecution func() - -// MockDMLExecutionMerging is only used for test. -var MockDMLExecutionMerging func() - -// MockDMLExecutionStateMerging is only used for test. -var MockDMLExecutionStateMerging func() - -// MockDMLExecutionStateBeforeImport is only used for test. -var MockDMLExecutionStateBeforeImport func() - -func (w *worker) addPhysicalTableIndex(t table.PhysicalTable, reorgInfo *reorgInfo) error { - if reorgInfo.mergingTmpIdx { - logutil.BgLogger().Info("start to merge temp index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) - return w.writePhysicalTableRecord(w.sessPool, t, typeAddIndexMergeTmpWorker, reorgInfo) - } - logutil.BgLogger().Info("start to add table index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) - return w.writePhysicalTableRecord(w.sessPool, t, typeAddIndexWorker, reorgInfo) -} - -// addTableIndex handles the add index reorganization state for a table. -func (w *worker) addTableIndex(t table.Table, reorgInfo *reorgInfo) error { - // TODO: Support typeAddIndexMergeTmpWorker. - if reorgInfo.ReorgMeta.IsDistReorg && !reorgInfo.mergingTmpIdx { - if reorgInfo.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { - err := w.executeDistGlobalTask(reorgInfo) - if err != nil { - return err - } - return checkDuplicateForUniqueIndex(w.ctx, t, reorgInfo) - } - } - - var err error - if tbl, ok := t.(table.PartitionedTable); ok { - var finish bool - for !finish { - p := tbl.GetPartition(reorgInfo.PhysicalTableID) - if p == nil { - return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, t.Meta().ID) - } - err = w.addPhysicalTableIndex(p, reorgInfo) - if err != nil { - break - } - w.ddlCtx.mu.RLock() - w.ddlCtx.mu.hook.OnUpdateReorgInfo(reorgInfo.Job, reorgInfo.PhysicalTableID) - w.ddlCtx.mu.RUnlock() - - finish, err = updateReorgInfo(w.sessPool, tbl, reorgInfo) - if err != nil { - return errors.Trace(err) - } - // Every time we finish a partition, we update the progress of the job. - if rc := w.getReorgCtx(reorgInfo.Job.ID); rc != nil { - reorgInfo.Job.SetRowCount(rc.getRowCount()) - } - } - } else { - //nolint:forcetypeassert - phyTbl := t.(table.PhysicalTable) - err = w.addPhysicalTableIndex(phyTbl, reorgInfo) - } - return errors.Trace(err) -} - -func checkDuplicateForUniqueIndex(ctx context.Context, t table.Table, reorgInfo *reorgInfo) error { - var bc ingest.BackendCtx - var err error - defer func() { - if bc != nil { - ingest.LitBackCtxMgr.Unregister(reorgInfo.ID) - } - }() - for _, elem := range reorgInfo.elements { - indexInfo := model.FindIndexInfoByID(t.Meta().Indices, elem.ID) - if indexInfo == nil { - return errors.New("unexpected error, can't find index info") - } - if indexInfo.Unique { - ctx := logutil.WithCategory(ctx, "ddl-ingest") - if bc == nil { - bc, err = ingest.LitBackCtxMgr.Register(ctx, indexInfo.Unique, reorgInfo.ID, nil, reorgInfo.ReorgMeta.ResourceGroupName) - if err != nil { - return err - } - } - err = bc.CollectRemoteDuplicateRows(indexInfo.ID, t) - if err != nil { - return err - } - } - } - return nil -} - -// MockDMLExecutionOnTaskFinished is used to mock DML execution when tasks finished. -var MockDMLExecutionOnTaskFinished func() - -// MockDMLExecutionOnDDLPaused is used to mock DML execution when ddl job paused. -var MockDMLExecutionOnDDLPaused func() - -func (w *worker) executeDistGlobalTask(reorgInfo *reorgInfo) error { - if reorgInfo.mergingTmpIdx { - return errors.New("do not support merge index") - } - - taskType := BackfillTaskType - taskKey := fmt.Sprintf("ddl/%s/%d", taskType, reorgInfo.Job.ID) - g, ctx := errgroup.WithContext(context.Background()) - done := make(chan struct{}) - - // generate taskKey for multi schema change. - if mInfo := reorgInfo.Job.MultiSchemaInfo; mInfo != nil { - taskKey = fmt.Sprintf("%s/%d", taskKey, mInfo.Seq) - } - - // for resuming add index task. - taskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - task, err := taskManager.GetGlobalTaskByKey(taskKey) - if err != nil { - return err - } - if task != nil { - // It's possible that the task state is succeed but the ddl job is paused. - // When task in succeed state, we can skip the dist task execution/scheduing process. - if task.State == proto.TaskStateSucceed { - return nil - } - g.Go(func() error { - defer close(done) - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logutil.BgLogger(), - func(ctx context.Context) (bool, error) { - return true, handle.ResumeTask(taskKey) - }, - ) - if err != nil { - return err - } - err = handle.WaitGlobalTask(ctx, task) - if w.isReorgPaused(reorgInfo.Job.ID) { - logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.Error(err)) - return dbterror.ErrPausedDDLJob.GenWithStackByArgs(reorgInfo.Job.ID) - } - return err - }) - } else { - elemIDs := make([]int64, 0, len(reorgInfo.elements)) - for _, elem := range reorgInfo.elements { - elemIDs = append(elemIDs, elem.ID) - } - - taskMeta := &BackfillGlobalMeta{ - Job: *reorgInfo.Job.Clone(), - EleIDs: elemIDs, - EleTypeKey: reorgInfo.currElement.TypeKey, - CloudStorageURI: variable.CloudStorageURI.Load(), - } - - metaData, err := json.Marshal(taskMeta) - if err != nil { - return err - } - - g.Go(func() error { - defer close(done) - err := handle.SubmitAndRunGlobalTask(ctx, taskKey, taskType, distPhysicalTableConcurrency, metaData) - failpoint.Inject("pauseAfterDistTaskSuccess", func() { - MockDMLExecutionOnTaskFinished() - }) - if w.isReorgPaused(reorgInfo.Job.ID) { - logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.Error(err)) - return dbterror.ErrPausedDDLJob.GenWithStackByArgs(reorgInfo.Job.ID) - } - return err - }) - } - - g.Go(func() error { - checkFinishTk := time.NewTicker(CheckBackfillJobFinishInterval) - defer checkFinishTk.Stop() - updateRowCntTk := time.NewTicker(UpdateBackfillJobRowCountInterval) - defer updateRowCntTk.Stop() - for { - select { - case <-done: - w.updateJobRowCount(taskKey, reorgInfo.Job.ID) - return nil - case <-checkFinishTk.C: - if err = w.isReorgRunnable(reorgInfo.Job.ID, true); err != nil { - if dbterror.ErrPausedDDLJob.Equal(err) { - if err = handle.PauseTask(taskKey); err != nil { - logutil.BgLogger().Error("pause global task error", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) - continue - } - } - if !dbterror.ErrCancelledDDLJob.Equal(err) { - return errors.Trace(err) - } - if err = handle.CancelGlobalTask(taskKey); err != nil { - logutil.BgLogger().Error("cancel global task error", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) - // continue to cancel global task - continue - } - } - case <-updateRowCntTk.C: - w.updateJobRowCount(taskKey, reorgInfo.Job.ID) - } - } - }) - err = g.Wait() - return err -} - -func (w *worker) updateJobRowCount(taskKey string, jobID int64) { - taskMgr, err := storage.GetTaskManager() - if err != nil { - logutil.BgLogger().Warn("cannot get task manager", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) - return - } - gTask, err := taskMgr.GetGlobalTaskByKey(taskKey) - if err != nil || gTask == nil { - logutil.BgLogger().Warn("cannot get global task", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) - return - } - rowCount, err := taskMgr.GetSubtaskRowCount(gTask.ID, proto.StepOne) - if err != nil { - logutil.BgLogger().Warn("cannot get subtask row count", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) - return - } - w.getReorgCtx(jobID).setRowCount(rowCount) -} - -func getNextPartitionInfo(reorg *reorgInfo, t table.PartitionedTable, currPhysicalTableID int64) (int64, kv.Key, kv.Key, error) { - pi := t.Meta().GetPartitionInfo() - if pi == nil { - return 0, nil, nil, nil - } - - // During data copying, copy data from partitions to be dropped - nextPartitionDefs := pi.DroppingDefinitions - if bytes.Equal(reorg.currElement.TypeKey, meta.IndexElementKey) { - // During index re-creation, process data from partitions to be added - nextPartitionDefs = pi.AddingDefinitions - } - if len(nextPartitionDefs) == 0 { - nextPartitionDefs = pi.Definitions - } - pid, err := findNextPartitionID(currPhysicalTableID, nextPartitionDefs) - if err != nil { - // Fatal error, should not run here. - logutil.BgLogger().Error("find next partition ID failed", zap.String("category", "ddl"), zap.Reflect("table", t), zap.Error(err)) - return 0, nil, nil, errors.Trace(err) - } - if pid == 0 { - // Next partition does not exist, all the job done. - return 0, nil, nil, nil - } - - failpoint.Inject("mockUpdateCachedSafePoint", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - ts := oracle.GoTimeToTS(time.Now()) - //nolint:forcetypeassert - s := reorg.d.store.(tikv.Storage) - s.UpdateSPCache(ts, time.Now()) - time.Sleep(time.Second * 3) - } - }) - - var startKey, endKey kv.Key - if reorg.mergingTmpIdx { - indexID := reorg.currElement.ID - startKey, endKey = tablecodec.GetTableIndexKeyRange(pid, tablecodec.TempIndexPrefix|indexID) - } else { - currentVer, err := getValidCurrentVersion(reorg.d.store) - if err != nil { - return 0, nil, nil, errors.Trace(err) - } - startKey, endKey, err = getTableRange(reorg.NewJobContext(), reorg.d, t.GetPartition(pid), currentVer.Ver, reorg.Job.Priority) - if err != nil { - return 0, nil, nil, errors.Trace(err) - } - } - return pid, startKey, endKey, nil -} - -// updateReorgInfo will find the next partition according to current reorgInfo. -// If no more partitions, or table t is not a partitioned table, returns true to -// indicate that the reorganize work is finished. -func updateReorgInfo(sessPool *sess.Pool, t table.PartitionedTable, reorg *reorgInfo) (bool, error) { - pid, startKey, endKey, err := getNextPartitionInfo(reorg, t, reorg.PhysicalTableID) - if err != nil { - return false, errors.Trace(err) - } - if pid == 0 { - // Next partition does not exist, all the job done. - return true, nil - } - reorg.PhysicalTableID, reorg.StartKey, reorg.EndKey = pid, startKey, endKey - - // Write the reorg info to store so the whole reorganize process can recover from panic. - err = reorg.UpdateReorgMeta(reorg.StartKey, sessPool) - logutil.BgLogger().Info("job update reorgInfo", zap.String("category", "ddl"), - zap.Int64("jobID", reorg.Job.ID), - zap.Stringer("element", reorg.currElement), - zap.Int64("partitionTableID", pid), - zap.String("startKey", hex.EncodeToString(reorg.StartKey)), - zap.String("endKey", hex.EncodeToString(reorg.EndKey)), zap.Error(err)) - return false, errors.Trace(err) -} - -// findNextPartitionID finds the next partition ID in the PartitionDefinition array. -// Returns 0 if current partition is already the last one. -func findNextPartitionID(currentPartition int64, defs []model.PartitionDefinition) (int64, error) { - for i, def := range defs { - if currentPartition == def.ID { - if i == len(defs)-1 { - return 0, nil - } - return defs[i+1].ID, nil - } - } - return 0, errors.Errorf("partition id not found %d", currentPartition) -} - -// AllocateIndexID allocates an index ID from TableInfo. -func AllocateIndexID(tblInfo *model.TableInfo) int64 { - tblInfo.MaxIndexID++ - return tblInfo.MaxIndexID -} - -func getIndexInfoByNameAndColumn(oldTableInfo *model.TableInfo, newOne *model.IndexInfo) *model.IndexInfo { - for _, oldOne := range oldTableInfo.Indices { - if newOne.Name.L == oldOne.Name.L && indexColumnSliceEqual(newOne.Columns, oldOne.Columns) { - return oldOne - } - } - return nil -} - -func indexColumnSliceEqual(a, b []*model.IndexColumn) bool { - if len(a) != len(b) { - return false - } - if len(a) == 0 { - logutil.BgLogger().Warn("admin repair table : index's columns length equal to 0", zap.String("category", "ddl")) - return true - } - // Accelerate the compare by eliminate index bound check. - b = b[:len(a)] - for i, v := range a { - if v.Name.L != b[i].Name.L { - return false - } - } - return true -} - -type cleanUpIndexWorker struct { - baseIndexWorker -} - -func newCleanUpIndexWorker(sessCtx sessionctx.Context, id int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) *cleanUpIndexWorker { - indexes := make([]table.Index, 0, len(t.Indices())) - rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) - for _, index := range t.Indices() { - if index.Meta().Global { - indexes = append(indexes, index) - } - } - return &cleanUpIndexWorker{ - baseIndexWorker: baseIndexWorker{ - backfillCtx: newBackfillCtx(reorgInfo.d, id, sessCtx, reorgInfo.SchemaName, t, jc, "cleanup_idx_rate", false), - indexes: indexes, - rowDecoder: rowDecoder, - defaultVals: make([]types.Datum, len(t.WritableCols())), - rowMap: make(map[int64]types.Datum, len(decodeColMap)), - }, - } -} - -func (w *cleanUpIndexWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { - failpoint.Inject("errorMockPanic", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - panic("panic test") - } - }) - - oprStartTime := time.Now() - ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) - errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - taskCtx.addedCount = 0 - taskCtx.scanCount = 0 - txn.SetOption(kv.Priority, handleRange.priority) - if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { - txn.SetOption(kv.ResourceGroupTagger, tagger) - } - txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) - - idxRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) - if err != nil { - return errors.Trace(err) - } - taskCtx.nextKey = nextKey - taskCtx.done = taskDone - - txn.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) - - n := len(w.indexes) - for i, idxRecord := range idxRecords { - taskCtx.scanCount++ - // we fetch records row by row, so records will belong to - // index[0], index[1] ... index[n-1], index[0], index[1] ... - // respectively. So indexes[i%n] is the index of idxRecords[i]. - err := w.indexes[i%n].Delete(w.sessCtx.GetSessionVars().StmtCtx, txn, idxRecord.vals, idxRecord.handle) - if err != nil { - return errors.Trace(err) - } - taskCtx.addedCount++ - } - return nil - }) - logSlowOperations(time.Since(oprStartTime), "cleanUpIndexBackfillDataInTxn", 3000) - - return -} - -// cleanupPhysicalTableIndex handles the drop partition reorganization state for a non-partitioned table or a partition. -func (w *worker) cleanupPhysicalTableIndex(t table.PhysicalTable, reorgInfo *reorgInfo) error { - logutil.BgLogger().Info("start to clean up index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) - return w.writePhysicalTableRecord(w.sessPool, t, typeCleanUpIndexWorker, reorgInfo) -} - -// cleanupGlobalIndex handles the drop partition reorganization state to clean up index entries of partitions. -func (w *worker) cleanupGlobalIndexes(tbl table.PartitionedTable, partitionIDs []int64, reorgInfo *reorgInfo) error { - var err error - var finish bool - for !finish { - p := tbl.GetPartition(reorgInfo.PhysicalTableID) - if p == nil { - return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, tbl.Meta().ID) - } - err = w.cleanupPhysicalTableIndex(p, reorgInfo) - if err != nil { - break - } - finish, err = w.updateReorgInfoForPartitions(tbl, reorgInfo, partitionIDs) - if err != nil { - return errors.Trace(err) - } - } - - return errors.Trace(err) -} - -// updateReorgInfoForPartitions will find the next partition in partitionIDs according to current reorgInfo. -// If no more partitions, or table t is not a partitioned table, returns true to -// indicate that the reorganize work is finished. -func (w *worker) updateReorgInfoForPartitions(t table.PartitionedTable, reorg *reorgInfo, partitionIDs []int64) (bool, error) { - pi := t.Meta().GetPartitionInfo() - if pi == nil { - return true, nil - } - - var pid int64 - for i, pi := range partitionIDs { - if pi == reorg.PhysicalTableID { - if i == len(partitionIDs)-1 { - return true, nil - } - pid = partitionIDs[i+1] - break - } - } - - currentVer, err := getValidCurrentVersion(reorg.d.store) - if err != nil { - return false, errors.Trace(err) - } - start, end, err := getTableRange(reorg.NewJobContext(), reorg.d, t.GetPartition(pid), currentVer.Ver, reorg.Job.Priority) - if err != nil { - return false, errors.Trace(err) - } - reorg.StartKey, reorg.EndKey, reorg.PhysicalTableID = start, end, pid - - // Write the reorg info to store so the whole reorganize process can recover from panic. - err = reorg.UpdateReorgMeta(reorg.StartKey, w.sessPool) - logutil.BgLogger().Info("job update reorg info", zap.String("category", "ddl"), zap.Int64("jobID", reorg.Job.ID), - zap.Stringer("element", reorg.currElement), - zap.Int64("partition table ID", pid), zap.String("start key", hex.EncodeToString(start)), - zap.String("end key", hex.EncodeToString(end)), zap.Error(err)) - return false, errors.Trace(err) -} - -// changingIndex is used to store the index that need to be changed during modifying column. -type changingIndex struct { - IndexInfo *model.IndexInfo - // Column offset in idxInfo.Columns. - Offset int - // When the modifying column is contained in the index, a temp index is created. - // isTemp indicates whether the indexInfo is a temp index created by a previous modify column job. - isTemp bool -} - -// FindRelatedIndexesToChange finds the indexes that covering the given column. -// The normal one will be overridden by the temp one. -func FindRelatedIndexesToChange(tblInfo *model.TableInfo, colName model.CIStr) []changingIndex { - // In multi-schema change jobs that contains several "modify column" sub-jobs, there may be temp indexes for another temp index. - // To prevent reorganizing too many indexes, we should create the temp indexes that are really necessary. - var normalIdxInfos, tempIdxInfos []changingIndex - for _, idxInfo := range tblInfo.Indices { - if pos := findIdxCol(idxInfo, colName); pos != -1 { - isTemp := isTempIdxInfo(idxInfo, tblInfo) - r := changingIndex{IndexInfo: idxInfo, Offset: pos, isTemp: isTemp} - if isTemp { - tempIdxInfos = append(tempIdxInfos, r) - } else { - normalIdxInfos = append(normalIdxInfos, r) - } - } - } - // Overwrite if the index has the corresponding temp index. For example, - // we try to find the indexes that contain the column `b` and there are two indexes, `i(a, b)` and `$i($a, b)`. - // Note that the symbol `$` means temporary. The index `$i($a, b)` is temporarily created by the previous "modify a" statement. - // In this case, we would create a temporary index like $$i($a, $b), so the latter should be chosen. - result := normalIdxInfos - for _, tmpIdx := range tempIdxInfos { - origName := getChangingIndexOriginName(tmpIdx.IndexInfo) - for i, normIdx := range normalIdxInfos { - if normIdx.IndexInfo.Name.O == origName { - result[i] = tmpIdx - } - } - } - return result -} - -func isTempIdxInfo(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) bool { - for _, idxCol := range idxInfo.Columns { - if tblInfo.Columns[idxCol.Offset].ChangeStateInfo != nil { - return true - } - } - return false -} - -func findIdxCol(idxInfo *model.IndexInfo, colName model.CIStr) int { - for offset, idxCol := range idxInfo.Columns { - if idxCol.Name.L == colName.L { - return offset - } - } - return -1 -} - -func renameIndexes(tblInfo *model.TableInfo, from, to model.CIStr) { - for _, idx := range tblInfo.Indices { - if idx.Name.L == from.L { - idx.Name = to - } else if isTempIdxInfo(idx, tblInfo) && getChangingIndexOriginName(idx) == from.O { - idx.Name.L = strings.Replace(idx.Name.L, from.L, to.L, 1) - idx.Name.O = strings.Replace(idx.Name.O, from.O, to.O, 1) - } - } -} diff --git a/ddl/ingest/BUILD.bazel b/ddl/ingest/BUILD.bazel deleted file mode 100644 index cfe680c3912c4..0000000000000 --- a/ddl/ingest/BUILD.bazel +++ /dev/null @@ -1,87 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ingest", - srcs = [ - "backend.go", - "backend_mgr.go", - "checkpoint.go", - "config.go", - "disk_root.go", - "engine.go", - "engine_mgr.go", - "env.go", - "mem_root.go", - "message.go", - "mock.go", - ], - importpath = "github.com/pingcap/tidb/ddl/ingest", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/backend", - "//br/pkg/lightning/backend/encode", - "//br/pkg/lightning/backend/kv", - "//br/pkg/lightning/backend/local", - "//br/pkg/lightning/checkpoints", - "//br/pkg/lightning/common", - "//br/pkg/lightning/config", - "//br/pkg/lightning/errormanager", - "//br/pkg/lightning/log", - "//config", - "//ddl/internal/session", - "//ddl/util", - "//kv", - "//meta", - "//parser/mysql", - "//sessionctx", - "//sessionctx/variable", - "//table", - "//util", - "//util/dbterror", - "//util/generic", - "//util/logutil", - "//util/size", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "ingest_test", - timeout = "short", - srcs = [ - "checkpoint_test.go", - "env_test.go", - "integration_test.go", - "main_test.go", - "mem_root_test.go", - ], - embed = [":ingest"], - flaky = True, - race = "on", - shard_count = 15, - deps = [ - "//config", - "//ddl", - "//ddl/ingest/testutil", - "//ddl/internal/session", - "//ddl/testutil", - "//ddl/util/callback", - "//domain", - "//errno", - "//parser/model", - "//testkit", - "//tests/realtikvtest", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/ingest/config.go b/ddl/ingest/config.go deleted file mode 100644 index 75f41cdd531e3..0000000000000 --- a/ddl/ingest/config.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ingest - -import ( - "context" - "math" - "path/filepath" - "sync/atomic" - - "github.com/pingcap/tidb/br/pkg/lightning/backend" - "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" - lightning "github.com/pingcap/tidb/br/pkg/lightning/config" - tidb "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" - "go.uber.org/zap" -) - -// ImporterRangeConcurrencyForTest is only used for test. -var ImporterRangeConcurrencyForTest *atomic.Int32 - -// Config is the configuration for the lightning local backend used in DDL. -type Config struct { - Lightning *lightning.Config - KeyspaceName string - IsRaftKV2 bool -} - -func genConfig(ctx context.Context, memRoot MemRoot, jobID int64, unique bool, isRaftKV2 bool) (*Config, error) { - tidbCfg := tidb.GetGlobalConfig() - cfg := lightning.NewConfig() - cfg.TikvImporter.Backend = lightning.BackendLocal - // Each backend will build a single dir in lightning dir. - cfg.TikvImporter.SortedKVDir = filepath.Join(LitSortPath, EncodeBackendTag(jobID)) - if ImporterRangeConcurrencyForTest != nil { - cfg.TikvImporter.RangeConcurrency = int(ImporterRangeConcurrencyForTest.Load()) - } - err := cfg.AdjustForDDL() - if err != nil { - logutil.Logger(ctx).Warn(LitWarnConfigError, zap.Error(err)) - return nil, err - } - adjustImportMemory(ctx, memRoot, cfg) - cfg.Checkpoint.Enable = true - if unique { - cfg.TikvImporter.DuplicateResolution = lightning.DupeResAlgErr - // TODO(lance6716): will introduce fail-fast for DDL usage later - cfg.Conflict.Threshold = math.MaxInt64 - } else { - cfg.TikvImporter.DuplicateResolution = lightning.DupeResAlgNone - } - cfg.TiDB.PdAddr = tidbCfg.Path - cfg.TiDB.Host = "127.0.0.1" - cfg.TiDB.StatusPort = int(tidbCfg.Status.StatusPort) - // Set TLS related information - cfg.Security.CAPath = tidbCfg.Security.ClusterSSLCA - cfg.Security.CertPath = tidbCfg.Security.ClusterSSLCert - cfg.Security.KeyPath = tidbCfg.Security.ClusterSSLKey - // in DDL scenario, we don't switch import mode - cfg.Cron.SwitchMode = lightning.Duration{Duration: 0} - - c := &Config{ - Lightning: cfg, - KeyspaceName: tidb.GetGlobalKeyspaceName(), - IsRaftKV2: isRaftKV2, - } - - return c, err -} - -var ( - compactMemory = 1 * size.GB - compactConcurrency = 4 -) - -func generateLocalEngineConfig(id int64, dbName, tbName string) *backend.EngineConfig { - return &backend.EngineConfig{ - Local: backend.LocalEngineConfig{ - Compact: true, - CompactThreshold: int64(compactMemory), - CompactConcurrency: compactConcurrency, - }, - TableInfo: &checkpoints.TidbTableInfo{ - ID: id, - DB: dbName, - Name: tbName, - }, - KeepSortDir: true, - } -} - -// adjustImportMemory adjusts the lightning memory parameters according to the memory root's max limitation. -func adjustImportMemory(ctx context.Context, memRoot MemRoot, cfg *lightning.Config) { - var scale int64 - // Try aggressive resource usage successful. - if tryAggressiveMemory(ctx, memRoot, cfg) { - return - } - - defaultMemSize := int64(cfg.TikvImporter.LocalWriterMemCacheSize) * int64(cfg.TikvImporter.RangeConcurrency) - defaultMemSize += 4 * int64(cfg.TikvImporter.EngineMemCacheSize) - logutil.Logger(ctx).Info(LitInfoInitMemSetting, - zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), - zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), - zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) - - maxLimit := memRoot.MaxMemoryQuota() - scale = defaultMemSize / maxLimit - - if scale == 1 || scale == 0 { - return - } - - cfg.TikvImporter.LocalWriterMemCacheSize /= lightning.ByteSize(scale) - cfg.TikvImporter.EngineMemCacheSize /= lightning.ByteSize(scale) - // TODO: adjust range concurrency number to control total concurrency in the future. - logutil.Logger(ctx).Info(LitInfoChgMemSetting, - zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), - zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), - zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) -} - -// tryAggressiveMemory lightning memory parameters according memory root's max limitation. -func tryAggressiveMemory(ctx context.Context, memRoot MemRoot, cfg *lightning.Config) bool { - var defaultMemSize int64 - defaultMemSize = int64(int(cfg.TikvImporter.LocalWriterMemCacheSize) * cfg.TikvImporter.RangeConcurrency) - defaultMemSize += int64(cfg.TikvImporter.EngineMemCacheSize) - - if (defaultMemSize + memRoot.CurrentUsage()) > memRoot.MaxMemoryQuota() { - return false - } - logutil.Logger(ctx).Info(LitInfoChgMemSetting, - zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), - zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), - zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) - return true -} - -// defaultImportantVariables is used in obtainImportantVariables to retrieve the system -// variables from downstream which may affect KV encode result. The values record the default -// values if missing. -var defaultImportantVariables = map[string]string{ - "max_allowed_packet": "67108864", // 64MB - "div_precision_increment": "4", - "time_zone": "SYSTEM", - "lc_time_names": "en_US", - "default_week_format": "0", - "block_encryption_mode": "aes-128-ecb", - "group_concat_max_len": "1024", - "tidb_row_format_version": "1", -} diff --git a/ddl/ingest/integration_test.go b/ddl/ingest/integration_test.go deleted file mode 100644 index 62e549299cd0a..0000000000000 --- a/ddl/ingest/integration_test.go +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package ingest_test - -import ( - "fmt" - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/ingest" - ingesttestutil "github.com/pingcap/tidb/ddl/ingest/testutil" - "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAddIndexIngestGeneratedColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - assertLastNDDLUseIngest := func(n int) { - tk.MustExec("admin check table t;") - rows := tk.MustQuery(fmt.Sprintf("admin show ddl jobs %d;", n)).Rows() - require.Len(t, rows, n) - for i := 0; i < n; i++ { - //nolint: forcetypeassert - jobTp := rows[i][3].(string) - require.True(t, strings.Contains(jobTp, "ingest"), jobTp) - } - } - tk.MustExec("create table t (a int, b int, c int as (b+10), d int as (b+c), primary key (a) clustered);") - tk.MustExec("insert into t (a, b) values (1, 1), (2, 2), (3, 3);") - tk.MustExec("alter table t add index idx(c);") - tk.MustExec("alter table t add index idx1(c, a);") - tk.MustExec("alter table t add index idx2(a);") - tk.MustExec("alter table t add index idx3(d);") - tk.MustExec("alter table t add index idx4(d, c);") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 11 12", "2 2 12 14", "3 3 13 16")) - assertLastNDDLUseIngest(5) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int, b char(10), c char(10) as (concat(b, 'x')), d int, e char(20) as (c));") - tk.MustExec("insert into t (a, b, d) values (1, '1', 1), (2, '2', 2), (3, '3', 3);") - tk.MustExec("alter table t add index idx(c);") - tk.MustExec("alter table t add index idx1(a, c);") - tk.MustExec("alter table t add index idx2(c(7));") - tk.MustExec("alter table t add index idx3(e(5));") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 1x 1 1x", "2 2 2x 2 2x", "3 3 3x 3 3x")) - assertLastNDDLUseIngest(4) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int, b char(10), c tinyint, d int as (a + c), e bigint as (d - a), primary key(b, a) clustered);") - tk.MustExec("insert into t (a, b, c) values (1, '1', 1), (2, '2', 2), (3, '3', 3);") - tk.MustExec("alter table t add index idx(d);") - tk.MustExec("alter table t add index idx1(b(2), d);") - tk.MustExec("alter table t add index idx2(d, c);") - tk.MustExec("alter table t add index idx3(e);") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 1 2 1", "2 2 2 4 2", "3 3 3 6 3")) - assertLastNDDLUseIngest(4) -} - -func TestIngestError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 1;") - tk.MustExec("create table t (a int primary key, b int);") - for i := 0; i < 4; i++ { - tk.MustExec(fmt.Sprintf("insert into t values (%d, %d);", i*10000, i*10000)) - } - tk.MustQuery("split table t between (0) and (50000) regions 5;").Check(testkit.Rows("4 1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockCopSenderError", "1*return")) - tk.MustExec("alter table t add index idx(a);") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockCopSenderError")) - tk.MustExec("admin check table t;") - rows := tk.MustQuery("admin show ddl jobs 1;").Rows() - //nolint: forcetypeassert - jobTp := rows[0][3].(string) - require.True(t, strings.Contains(jobTp, "ingest"), jobTp) - - tk.MustExec("drop table t;") - tk.MustExec("create table t (a int primary key, b int);") - for i := 0; i < 4; i++ { - tk.MustExec(fmt.Sprintf("insert into t values (%d, %d);", i*10000, i*10000)) - } - tk.MustQuery("split table t between (0) and (50000) regions 5;").Check(testkit.Rows("4 1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockLocalWriterError", "1*return")) - tk.MustExec("alter table t add index idx(a);") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockLocalWriterError")) - tk.MustExec("admin check table t;") - rows = tk.MustQuery("admin show ddl jobs 1;").Rows() - //nolint: forcetypeassert - jobTp = rows[0][3].(string) - require.True(t, strings.Contains(jobTp, "ingest"), jobTp) -} - -func TestAddIndexIngestPanic(t *testing.T) { - store := realtikvtest.CreateMockStoreAndSetup(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - // Mock panic on coprocessor request sender. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockCopSenderPanic", "return(true)")) - tk.MustExec("create table t (a int, b int, c int, d int, primary key (a) clustered);") - tk.MustExec("insert into t (a, b, c, d) values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") - tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrReorgPanic) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockCopSenderPanic")) - - // Mock panic on local engine writer. - tk.MustExec("drop table t;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockLocalWriterPanic", "return")) - tk.MustExec("create table t (a int, b int, c int, d int, primary key (a) clustered);") - tk.MustExec("insert into t (a, b, c, d) values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") - tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrReorgPanic) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockLocalWriterPanic")) -} - -func TestAddIndexIngestCancel(t *testing.T) { - store, dom := realtikvtest.CreateMockStoreAndDomainAndSetup(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("create table t (a int, b int);") - tk.MustExec("insert into t (a, b) values (1, 1), (2, 2), (3, 3);") - defHook := dom.DDL().GetHook() - customHook := newTestCallBack(t, dom) - cancelled := false - customHook.OnJobRunBeforeExported = func(job *model.Job) { - if cancelled { - return - } - if job.Type == model.ActionAddIndex && job.SchemaState == model.StateWriteReorganization { - idx := testutil.FindIdxInfo(dom, "test", "t", "idx") - if idx == nil { - return - } - if idx.BackfillState == model.BackfillStateRunning { - tk2 := testkit.NewTestKit(t, store) - rs, err := tk2.Exec(fmt.Sprintf("admin cancel ddl jobs %d", job.ID)) - assert.NoError(t, err) - assert.NoError(t, rs.Close()) - cancelled = true - } - } - } - dom.DDL().SetHook(customHook) - tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrCancelledDDLJob) - require.True(t, cancelled) - dom.DDL().SetHook(defHook) - ok, err := ingest.LitBackCtxMgr.CheckAvailable() - require.NoError(t, err) - require.True(t, ok) -} - -type testCallback struct { - ddl.Callback - OnJobRunBeforeExported func(job *model.Job) -} - -func newTestCallBack(t *testing.T, dom *domain.Domain) *testCallback { - defHookFactory, err := ddl.GetCustomizedHook("default_hook") - require.NoError(t, err) - return &testCallback{ - Callback: defHookFactory(dom), - } -} - -func (c *testCallback) OnJobRunBefore(job *model.Job) { - if c.OnJobRunBeforeExported != nil { - c.OnJobRunBeforeExported(job) - } -} - -func TestIngestPartitionRowCount(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec(`create table t (a int, b int, c int as (b+10), d int as (b+c), - primary key (a) clustered) partition by range (a) ( - partition p0 values less than (1), - partition p1 values less than (2), - partition p2 values less than MAXVALUE);`) - tk.MustExec("insert into t (a, b) values (0, 0), (1, 1), (2, 2);") - tk.MustExec("alter table t add index idx(d);") - rows := tk.MustQuery("admin show ddl jobs 1;").Rows() - require.Len(t, rows, 1) - //nolint: forcetypeassert - rowCount := rows[0][7].(string) - require.Equal(t, "3", rowCount) - tk.MustExec("admin check table t;") -} - -func TestAddIndexIngestClientError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("CREATE TABLE t1 (f1 json);") - tk.MustExec(`insert into t1(f1) values (cast("null" as json));`) - tk.MustGetErrCode("create index i1 on t1((cast(f1 as unsigned array)));", errno.ErrInvalidJSONValueForFuncIndex) -} - -func TestAddIndexCancelOnNoneState(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tkCancel := testkit.NewTestKit(t, store) - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("use test") - tk.MustExec(`create table t (c1 int, c2 int, c3 int)`) - tk.MustExec("insert into t values(1, 1, 1);") - - hook := &callback.TestDDLCallback{Do: dom} - first := true - hook.OnJobRunBeforeExported = func(job *model.Job) { - if job.SchemaState == model.StateNone && first { - _, err := tkCancel.Exec(fmt.Sprintf("admin cancel ddl jobs %d", job.ID)) - assert.NoError(t, err) - first = false - } - } - dom.DDL().SetHook(hook.Clone()) - tk.MustGetErrCode("alter table t add index idx1(c1)", errno.ErrCancelledDDLJob) - available, err := ingest.LitBackCtxMgr.CheckAvailable() - require.NoError(t, err) - require.True(t, available) -} - -func TestAddIndexIngestTimezone(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("SET time_zone = '-06:00';") - tk.MustExec("create table t (`src` varchar(48),`t` timestamp,`timezone` varchar(100));") - tk.MustExec("insert into t values('2000-07-29 23:15:30','2000-07-29 23:15:30','-6:00');") - // Test Daylight time. - tk.MustExec("insert into t values('1991-07-21 00:00:00','1991-07-21 00:00:00','-6:00');") - tk.MustExec("alter table t add index idx(t);") - tk.MustExec("admin check table t;") - - tk.MustExec("alter table t drop index idx;") - tk.MustExec("SET time_zone = 'Asia/Shanghai';") - tk.MustExec("insert into t values('2000-07-29 23:15:30','2000-07-29 23:15:30', '+8:00');") - tk.MustExec("insert into t values('1991-07-21 00:00:00','1991-07-21 00:00:00','+8:00');") - tk.MustExec("alter table t add index idx(t);") - tk.MustExec("admin check table t;") -} - -func TestAddIndexIngestMultiSchemaChange(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - defer ingesttestutil.InjectMockBackendMgr(t, store)() - - tk.MustExec("create table t (a int, b int);") - tk.MustExec("insert into t values(1, 1), (2, 2);") - tk.MustExec("alter table t add index idx(a), add index idx_2(b);") - tk.MustExec("admin check table t;") - tk.MustExec("alter table t drop index idx, drop index idx_2;") - tk.MustExec(`alter table t - add unique index idx(a), - add unique index idx_2(b, a), - add unique index idx_3(b);`) - tk.MustExec("admin check table t;") - - tk.MustExec("drop table t;") - tk.MustExec(`create table t (a int, b int, c int as (b+10), d int as (b+c), - primary key (a) clustered) partition by range (a) ( - partition p0 values less than (10), - partition p1 values less than (20), - partition p2 values less than MAXVALUE);`) - for i := 0; i < 30; i++ { - insertSQL := fmt.Sprintf("insert into t (a, b) values (%d, %d);", i, i) - tk.MustExec(insertSQL) - } - tk.MustExec("alter table t add index idx_a(a), add index idx_ab(a, b), add index idx_d(d);") - tk.MustExec("admin check table t;") -} diff --git a/ddl/ingest/mock.go b/ddl/ingest/mock.go deleted file mode 100644 index d3a14aa520be4..0000000000000 --- a/ddl/ingest/mock.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package ingest - -import ( - "context" - "encoding/hex" - "sync" - - "github.com/pingcap/tidb/br/pkg/lightning/backend/local" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" - clientv3 "go.etcd.io/etcd/client/v3" - "go.uber.org/zap" -) - -// MockBackendCtxMgr is a mock backend context manager. -type MockBackendCtxMgr struct { - sessCtxProvider func() sessionctx.Context - runningJobs map[int64]*MockBackendCtx -} - -// NewMockBackendCtxMgr creates a new mock backend context manager. -func NewMockBackendCtxMgr(sessCtxProvider func() sessionctx.Context) *MockBackendCtxMgr { - return &MockBackendCtxMgr{ - sessCtxProvider: sessCtxProvider, - runningJobs: make(map[int64]*MockBackendCtx), - } -} - -// CheckAvailable implements BackendCtxMgr.Available interface. -func (m *MockBackendCtxMgr) CheckAvailable() (bool, error) { - return len(m.runningJobs) == 0, nil -} - -// Register implements BackendCtxMgr.Register interface. -func (m *MockBackendCtxMgr) Register(_ context.Context, _ bool, jobID int64, _ *clientv3.Client, _ string) (BackendCtx, error) { - logutil.BgLogger().Info("mock backend mgr register", zap.Int64("jobID", jobID)) - if mockCtx, ok := m.runningJobs[jobID]; ok { - return mockCtx, nil - } - sessCtx := m.sessCtxProvider() - mockCtx := &MockBackendCtx{ - mu: sync.Mutex{}, - sessCtx: sessCtx, - } - m.runningJobs[jobID] = mockCtx - return mockCtx, nil -} - -// Unregister implements BackendCtxMgr.Unregister interface. -func (m *MockBackendCtxMgr) Unregister(jobID int64) { - if mCtx, ok := m.runningJobs[jobID]; ok { - mCtx.sessCtx.StmtCommit(context.Background()) - err := mCtx.sessCtx.CommitTxn(context.Background()) - logutil.BgLogger().Info("mock backend mgr unregister", zap.Int64("jobID", jobID), zap.Error(err)) - delete(m.runningJobs, jobID) - if mCtx.checkpointMgr != nil { - mCtx.checkpointMgr.Close() - } - } -} - -// Load implements BackendCtxMgr.Load interface. -func (m *MockBackendCtxMgr) Load(jobID int64) (BackendCtx, bool) { - logutil.BgLogger().Info("mock backend mgr load", zap.Int64("jobID", jobID)) - if mockCtx, ok := m.runningJobs[jobID]; ok { - return mockCtx, true - } - return nil, false -} - -// ResetSessCtx is only used for mocking test. -func (m *MockBackendCtxMgr) ResetSessCtx() { - for _, mockCtx := range m.runningJobs { - mockCtx.sessCtx = m.sessCtxProvider() - } -} - -// MockBackendCtx is a mock backend context. -type MockBackendCtx struct { - sessCtx sessionctx.Context - mu sync.Mutex - checkpointMgr *CheckpointManager -} - -// Register implements BackendCtx.Register interface. -func (m *MockBackendCtx) Register(jobID, indexID int64, _, _ string) (Engine, error) { - logutil.BgLogger().Info("mock backend ctx register", zap.Int64("jobID", jobID), zap.Int64("indexID", indexID)) - return &MockEngineInfo{sessCtx: m.sessCtx, mu: &m.mu}, nil -} - -// Unregister implements BackendCtx.Unregister interface. -func (*MockBackendCtx) Unregister(jobID, indexID int64) { - logutil.BgLogger().Info("mock backend ctx unregister", zap.Int64("jobID", jobID), zap.Int64("indexID", indexID)) -} - -// CollectRemoteDuplicateRows implements BackendCtx.CollectRemoteDuplicateRows interface. -func (*MockBackendCtx) CollectRemoteDuplicateRows(indexID int64, _ table.Table) error { - logutil.BgLogger().Info("mock backend ctx collect remote duplicate rows", zap.Int64("indexID", indexID)) - return nil -} - -// FinishImport implements BackendCtx.FinishImport interface. -func (*MockBackendCtx) FinishImport(indexID int64, _ bool, _ table.Table) error { - logutil.BgLogger().Info("mock backend ctx finish import", zap.Int64("indexID", indexID)) - return nil -} - -// ResetWorkers implements BackendCtx.ResetWorkers interface. -func (*MockBackendCtx) ResetWorkers(_ int64) { -} - -// Flush implements BackendCtx.Flush interface. -func (*MockBackendCtx) Flush(_ int64, _ FlushMode) (flushed bool, imported bool, err error) { - return false, false, nil -} - -// Done implements BackendCtx.Done interface. -func (*MockBackendCtx) Done() bool { - return false -} - -// SetDone implements BackendCtx.SetDone interface. -func (*MockBackendCtx) SetDone() { -} - -// AttachCheckpointManager attaches a checkpoint manager to the backend context. -func (m *MockBackendCtx) AttachCheckpointManager(mgr *CheckpointManager) { - m.checkpointMgr = mgr -} - -// GetCheckpointManager returns the checkpoint manager attached to the backend context. -func (m *MockBackendCtx) GetCheckpointManager() *CheckpointManager { - return m.checkpointMgr -} - -// GetLocalBackend returns the local backend. -func (*MockBackendCtx) GetLocalBackend() *local.Backend { - return nil -} - -// MockWriteHook the hook for write in mock engine. -type MockWriteHook func(key, val []byte) - -// MockEngineInfo is a mock engine info. -type MockEngineInfo struct { - sessCtx sessionctx.Context - mu *sync.Mutex - - onWrite MockWriteHook -} - -// NewMockEngineInfo creates a new mock engine info. -func NewMockEngineInfo(sessCtx sessionctx.Context) *MockEngineInfo { - return &MockEngineInfo{ - sessCtx: sessCtx, - mu: &sync.Mutex{}, - } -} - -// Flush implements Engine.Flush interface. -func (*MockEngineInfo) Flush() error { - return nil -} - -// ImportAndClean implements Engine.ImportAndClean interface. -func (*MockEngineInfo) ImportAndClean() error { - return nil -} - -// Clean implements Engine.Clean interface. -func (*MockEngineInfo) Clean() { -} - -// SetHook set the write hook. -func (m *MockEngineInfo) SetHook(onWrite func(key, val []byte)) { - m.onWrite = onWrite -} - -// CreateWriter implements Engine.CreateWriter interface. -func (m *MockEngineInfo) CreateWriter(id int) (Writer, error) { - logutil.BgLogger().Info("mock engine info create writer", zap.Int("id", id)) - return &MockWriter{sessCtx: m.sessCtx, mu: m.mu, onWrite: m.onWrite}, nil -} - -// MockWriter is a mock writer. -type MockWriter struct { - sessCtx sessionctx.Context - mu *sync.Mutex - onWrite MockWriteHook -} - -// WriteRow implements Writer.WriteRow interface. -func (m *MockWriter) WriteRow(_ context.Context, key, idxVal []byte, _ kv.Handle) error { - logutil.BgLogger().Info("mock writer write row", - zap.String("key", hex.EncodeToString(key)), - zap.String("idxVal", hex.EncodeToString(idxVal))) - m.mu.Lock() - defer m.mu.Unlock() - if m.onWrite != nil { - m.onWrite(key, idxVal) - return nil - } - txn, err := m.sessCtx.Txn(true) - if err != nil { - return err - } - return txn.Set(key, idxVal) -} - -// LockForWrite implements Writer.LockForWrite interface. -func (*MockWriter) LockForWrite() func() { - return func() {} -} - -// Close implements Writer.Close interface. -func (*MockWriter) Close(_ context.Context) error { - return nil -} diff --git a/ddl/ingest/tests/BUILD.bazel b/ddl/ingest/tests/BUILD.bazel deleted file mode 100644 index 79fc7a6f2614a..0000000000000 --- a/ddl/ingest/tests/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tests_test", - timeout = "short", - srcs = ["partition_table_test.go"], - flaky = True, - race = "off", - deps = [ - "//config", - "//ddl/ingest", - "//ddl/ingest/testutil", - "//ddl/util/callback", - "//parser/model", - "//testkit", - ], -) diff --git a/ddl/ingest/tests/partition_table_test.go b/ddl/ingest/tests/partition_table_test.go deleted file mode 100644 index d50f682dfa6e8..0000000000000 --- a/ddl/ingest/tests/partition_table_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/ingest" - ingesttestutil "github.com/pingcap/tidb/ddl/ingest/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" -) - -func TestAddIndexIngestRecoverPartition(t *testing.T) { - port := config.GetGlobalConfig().Port - tc := testkit.NewDistExecutionContext(t, 3) - defer tc.Close() - defer ingesttestutil.InjectMockBackendMgr(t, tc.Store)() - tk := testkit.NewTestKit(t, tc.Store) - tk.MustExec("use test;") - tk.MustExec("create table t (a int primary key, b int) partition by hash(a) partitions 8;") - tk.MustExec("insert into t values (2, 3), (3, 3), (5, 5);") - - partCnt := 0 - changeOwner0To1 := func(job *model.Job, _ int64) { - partCnt++ - if partCnt == 3 { - tc.SetOwner(1) - // TODO(tangenta): mock multiple backends in a better way. - //nolint: forcetypeassert - // TODO(tangenta): When owner changes, wait last ddl owner's DDL dispatching loop exits. - ingest.LitBackCtxMgr.(*ingest.MockBackendCtxMgr).ResetSessCtx() - bc, _ := ingest.LitBackCtxMgr.Load(job.ID) - bc.GetCheckpointManager().Close() - bc.AttachCheckpointManager(nil) - config.GetGlobalConfig().Port = port + 1 - } - } - changeOwner1To2 := func(job *model.Job, _ int64) { - partCnt++ - if partCnt == 6 { - tc.SetOwner(2) - //nolint: forcetypeassert - ingest.LitBackCtxMgr.(*ingest.MockBackendCtxMgr).ResetSessCtx() - bc, _ := ingest.LitBackCtxMgr.Load(job.ID) - bc.GetCheckpointManager().Close() - bc.AttachCheckpointManager(nil) - config.GetGlobalConfig().Port = port + 2 - } - } - tc.SetOwner(0) - hook0 := &callback.TestDDLCallback{} - hook0.OnUpdateReorgInfoExported = changeOwner0To1 - hook1 := &callback.TestDDLCallback{} - hook1.OnUpdateReorgInfoExported = changeOwner1To2 - tc.GetDomain(0).DDL().SetHook(hook0) - tc.GetDomain(1).DDL().SetHook(hook1) - tk.MustExec("alter table t add index idx(b);") - tk.MustExec("admin check table t;") -} diff --git a/ddl/ingest/testutil/BUILD.bazel b/ddl/ingest/testutil/BUILD.bazel deleted file mode 100644 index 4d1d61fdbb1ac..0000000000000 --- a/ddl/ingest/testutil/BUILD.bazel +++ /dev/null @@ -1,14 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testutil", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/ddl/ingest/testutil", - visibility = ["//visibility:public"], - deps = [ - "//ddl/ingest", - "//kv", - "//sessionctx", - "//testkit", - ], -) diff --git a/ddl/ingest/testutil/testutil.go b/ddl/ingest/testutil/testutil.go deleted file mode 100644 index e2b5b63237c76..0000000000000 --- a/ddl/ingest/testutil/testutil.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package testutil - -import ( - "testing" - - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" -) - -// InjectMockBackendMgr mock LitBackCtxMgr. -func InjectMockBackendMgr(t *testing.T, store kv.Storage) (restore func()) { - tk := testkit.NewTestKit(t, store) - oldLitBackendMgr := ingest.LitBackCtxMgr - oldInitialized := ingest.LitInitialized - - ingest.LitBackCtxMgr = ingest.NewMockBackendCtxMgr(func() sessionctx.Context { - tk.MustExec("rollback;") - tk.MustExec("begin;") - return tk.Session() - }) - ingest.LitInitialized = true - - return func() { - ingest.LitBackCtxMgr = oldLitBackendMgr - ingest.LitInitialized = oldInitialized - } -} diff --git a/ddl/integration_test.go b/ddl/integration_test.go deleted file mode 100644 index adb348b65d658..0000000000000 --- a/ddl/integration_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "testing" - - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestDDLStatementsBackFill(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - needReorg := false - callback := &callback.TestDDLCallback{ - Do: dom, - } - onJobUpdatedExportedFunc := func(job *model.Job) { - if job.SchemaState == model.StateWriteReorganization { - needReorg = true - } - } - callback.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - dom.DDL().SetHook(callback) - tk.MustExec("create table t (a int, b char(65));") - tk.MustExec("insert into t values (1, '123');") - testCases := []struct { - ddlSQL string - expectedNeedReorg bool - }{ - {"alter table t modify column a bigint;", false}, - {"alter table t modify column b char(255);", false}, - {"alter table t modify column a varchar(100);", true}, - {"create table t1 (a int, b int);", false}, - {"alter table t1 add index idx_a(a);", true}, - {"alter table t1 add primary key(b) nonclustered;", true}, - {"alter table t1 drop primary key;", false}, - } - for _, tc := range testCases { - needReorg = false - tk.MustExec(tc.ddlSQL) - require.Equal(t, tc.expectedNeedReorg, needReorg, tc) - } -} diff --git a/ddl/internal/session/BUILD.bazel b/ddl/internal/session/BUILD.bazel deleted file mode 100644 index 099ef69a3ac2e..0000000000000 --- a/ddl/internal/session/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "session", - srcs = [ - "session.go", - "session_pool.go", - ], - importpath = "github.com/pingcap/tidb/ddl/internal/session", - visibility = ["//ddl:__subpackages__"], - deps = [ - "//domain/infosync", - "//kv", - "//metrics", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessiontxn", - "//util/chunk", - "//util/logutil", - "//util/mock", - "//util/sqlexec", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "session_test", - timeout = "short", - srcs = ["session_pool_test.go"], - flaky = True, - deps = [ - ":session", - "//testkit", - "@com_github_ngaut_pools//:pools", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/internal/session/session.go b/ddl/internal/session/session.go deleted file mode 100644 index 224639e236021..0000000000000 --- a/ddl/internal/session/session.go +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" -) - -// Session wraps sessionctx.Context for transaction usage. -type Session struct { - sessionctx.Context -} - -// NewSession creates a new Session. -func NewSession(s sessionctx.Context) *Session { - return &Session{s} -} - -// Begin starts a transaction. -func (s *Session) Begin() error { - err := sessiontxn.NewTxn(context.Background(), s.Context) - if err != nil { - return err - } - s.GetSessionVars().SetInTxn(true) - return nil -} - -// Commit commits the transaction. -func (s *Session) Commit() error { - s.StmtCommit(context.Background()) - return s.CommitTxn(context.Background()) -} - -// Txn activate and returns the current transaction. -func (s *Session) Txn() (kv.Transaction, error) { - return s.Context.Txn(true) -} - -// Rollback aborts the transaction. -func (s *Session) Rollback() { - s.StmtRollback(context.Background(), false) - s.RollbackTxn(context.Background()) -} - -// Reset resets the session. -func (s *Session) Reset() { - s.StmtRollback(context.Background(), false) -} - -// Execute executes a query. -func (s *Session) Execute(ctx context.Context, query string, label string) ([]chunk.Row, error) { - startTime := time.Now() - var err error - defer func() { - metrics.DDLJobTableDuration.WithLabelValues(label + "-" + metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - }() - - if ctx.Value(kv.RequestSourceKey) == nil { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) - } - rs, err := s.Context.(sqlexec.SQLExecutor).ExecuteInternal(ctx, query) - if err != nil { - return nil, errors.Trace(err) - } - - if rs == nil { - return nil, nil - } - var rows []chunk.Row - defer terror.Call(rs.Close) - if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { - return nil, errors.Trace(err) - } - return rows, nil -} - -// Session returns the sessionctx.Context. -func (s *Session) Session() sessionctx.Context { - return s.Context -} - -// RunInTxn runs a function in a transaction. -func (s *Session) RunInTxn(f func(*Session) error) (err error) { - err = s.Begin() - if err != nil { - return err - } - failpoint.Inject("NotifyBeginTxnCh", func(val failpoint.Value) { - //nolint:forcetypeassert - v := val.(int) - if v == 1 { - MockDDLOnce = 1 - TestNotifyBeginTxnCh <- struct{}{} - } else if v == 2 && MockDDLOnce == 1 { - <-TestNotifyBeginTxnCh - MockDDLOnce = 0 - } - }) - - err = f(s) - if err != nil { - s.Rollback() - return - } - return errors.Trace(s.Commit()) -} - -var ( - // MockDDLOnce is only used for test. - MockDDLOnce = int64(0) - // TestNotifyBeginTxnCh is used for if the txn is beginning in RunInTxn. - TestNotifyBeginTxnCh = make(chan struct{}) -) diff --git a/ddl/label/BUILD.bazel b/ddl/label/BUILD.bazel deleted file mode 100644 index f534b24130e0f..0000000000000 --- a/ddl/label/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "label", - srcs = [ - "attributes.go", - "errors.go", - "rule.go", - ], - importpath = "github.com/pingcap/tidb/ddl/label", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//tablecodec", - "//util/codec", - "@in_gopkg_yaml_v2//:yaml_v2", - ], -) - -go_test( - name = "label_test", - timeout = "short", - srcs = [ - "attributes_test.go", - "main_test.go", - "rule_test.go", - ], - embed = [":label"], - flaky = True, - shard_count = 8, - deps = [ - "//parser/ast", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/label/main_test.go b/ddl/label/main_test.go deleted file mode 100644 index 2458b19b20e51..0000000000000 --- a/ddl/label/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package label - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/label/rule.go b/ddl/label/rule.go deleted file mode 100644 index 371f00aeb7e3a..0000000000000 --- a/ddl/label/rule.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package label - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "slices" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "gopkg.in/yaml.v2" -) - -const ( - // IDPrefix is the prefix for label rule ID. - IDPrefix = "schema" - ruleType = "key-range" -) - -const ( - // RuleIndexDefault is the default index for a rule. - RuleIndexDefault int = iota - // RuleIndexDatabase is the index for a rule of database. - RuleIndexDatabase - // RuleIndexTable is the index for a rule of table. - RuleIndexTable - // RuleIndexPartition is the index for a rule of partition. - RuleIndexPartition -) - -var ( - // TableIDFormat is the format of the label rule ID for a table. - // The format follows "schema/database_name/table_name". - TableIDFormat = "%s/%s/%s" - // PartitionIDFormat is the format of the label rule ID for a partition. - // The format follows "schema/database_name/table_name/partition_name". - PartitionIDFormat = "%s/%s/%s/%s" -) - -// Rule is used to establish the relationship between labels and a key range. -type Rule struct { - ID string `json:"id"` - Index int `json:"index"` - Labels Labels `json:"labels"` - RuleType string `json:"rule_type"` - Data []interface{} `json:"data"` -} - -// NewRule creates a rule. -func NewRule() *Rule { - return &Rule{} -} - -// ApplyAttributesSpec will transfer attributes defined in AttributesSpec to the labels. -func (r *Rule) ApplyAttributesSpec(spec *ast.AttributesSpec) error { - if spec.Default { - r.Labels = []Label{} - return nil - } - // construct a string list - attrBytes := []byte("[" + spec.Attributes + "]") - attributes := []string{} - err := yaml.UnmarshalStrict(attrBytes, &attributes) - if err != nil { - return err - } - r.Labels, err = NewLabels(attributes) - return err -} - -// String implements fmt.Stringer. -func (r *Rule) String() string { - t, err := json.Marshal(r) - if err != nil { - return "" - } - return string(t) -} - -// Clone clones a rule. -func (r *Rule) Clone() *Rule { - newRule := NewRule() - *newRule = *r - return newRule -} - -// Reset will reset the label rule for a table/partition with a given ID and names. -func (r *Rule) Reset(dbName, tableName, partName string, ids ...int64) *Rule { - isPartition := partName != "" - if isPartition { - r.ID = fmt.Sprintf(PartitionIDFormat, IDPrefix, dbName, tableName, partName) - } else { - r.ID = fmt.Sprintf(TableIDFormat, IDPrefix, dbName, tableName) - } - if len(r.Labels) == 0 { - return r - } - var hasDBKey, hasTableKey, hasPartitionKey bool - for i := range r.Labels { - switch r.Labels[i].Key { - case dbKey: - r.Labels[i].Value = dbName - hasDBKey = true - case tableKey: - r.Labels[i].Value = tableName - hasTableKey = true - case partitionKey: - if isPartition { - r.Labels[i].Value = partName - hasPartitionKey = true - } - default: - } - } - - if !hasDBKey { - r.Labels = append(r.Labels, Label{Key: dbKey, Value: dbName}) - } - - if !hasTableKey { - r.Labels = append(r.Labels, Label{Key: tableKey, Value: tableName}) - } - - if isPartition && !hasPartitionKey { - r.Labels = append(r.Labels, Label{Key: partitionKey, Value: partName}) - } - r.RuleType = ruleType - r.Data = []interface{}{} - slices.Sort(ids) - for i := 0; i < len(ids); i++ { - data := map[string]string{ - "start_key": hex.EncodeToString(codec.EncodeBytes(nil, tablecodec.GenTablePrefix(ids[i]))), - "end_key": hex.EncodeToString(codec.EncodeBytes(nil, tablecodec.GenTablePrefix(ids[i]+1))), - } - r.Data = append(r.Data, data) - } - // We may support more types later. - r.Index = RuleIndexTable - if isPartition { - r.Index = RuleIndexPartition - } - return r -} - -// RulePatch is the patch to update the label rules. -type RulePatch struct { - SetRules []*Rule `json:"sets"` - DeleteRules []string `json:"deletes"` -} - -// NewRulePatch returns a patch of rules which need to be set or deleted. -func NewRulePatch(setRules []*Rule, deleteRules []string) *RulePatch { - return &RulePatch{ - SetRules: setRules, - DeleteRules: deleteRules, - } -} diff --git a/ddl/main_test.go b/ddl/main_test.go deleted file mode 100644 index e58c92c71e138..0000000000000 --- a/ddl/main_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - - domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) - domain.SchemaOutOfDateRetryTimes.Store(50) - - autoid.SetStep(5000) - ddl.ReorgWaitTimeout = 30 * time.Millisecond - ddl.CheckBackfillJobFinishInterval = 50 * time.Millisecond - ddl.RunInGoTest = true - ddl.SetBatchInsertDeleteRangeSize(2) - - config.UpdateGlobal(func(conf *config.Config) { - // Test for table lock. - conf.EnableTableLock = true - conf.Instance.SlowThreshold = 10000 - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - - _, err := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "ddl: infosync.GlobalInfoSyncerInit: %v\n", err) - os.Exit(1) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/mock.go b/ddl/mock.go deleted file mode 100644 index 1c3e171db1e7c..0000000000000 --- a/ddl/mock.go +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - clientv3 "go.etcd.io/etcd/client/v3" - atomicutil "go.uber.org/atomic" -) - -// SetBatchInsertDeleteRangeSize sets the batch insert/delete range size in the test -func SetBatchInsertDeleteRangeSize(i int) { - batchInsertDeleteRangeSize = i -} - -var _ syncer.SchemaSyncer = &MockSchemaSyncer{} - -const mockCheckVersInterval = 2 * time.Millisecond - -// MockSchemaSyncer is a mock schema syncer, it is exported for testing. -type MockSchemaSyncer struct { - selfSchemaVersion int64 - mdlSchemaVersions sync.Map - globalVerCh chan clientv3.WatchResponse - mockSession chan struct{} -} - -// NewMockSchemaSyncer creates a new mock SchemaSyncer. -func NewMockSchemaSyncer() syncer.SchemaSyncer { - return &MockSchemaSyncer{} -} - -// Init implements SchemaSyncer.Init interface. -func (s *MockSchemaSyncer) Init(_ context.Context) error { - s.mdlSchemaVersions = sync.Map{} - s.globalVerCh = make(chan clientv3.WatchResponse, 1) - s.mockSession = make(chan struct{}, 1) - return nil -} - -// GlobalVersionCh implements SchemaSyncer.GlobalVersionCh interface. -func (s *MockSchemaSyncer) GlobalVersionCh() clientv3.WatchChan { - return s.globalVerCh -} - -// WatchGlobalSchemaVer implements SchemaSyncer.WatchGlobalSchemaVer interface. -func (*MockSchemaSyncer) WatchGlobalSchemaVer(context.Context) {} - -// UpdateSelfVersion implements SchemaSyncer.UpdateSelfVersion interface. -func (s *MockSchemaSyncer) UpdateSelfVersion(_ context.Context, jobID int64, version int64) error { - failpoint.Inject("mockUpdateMDLToETCDError", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(errors.New("mock update mdl to etcd error")) - } - }) - if variable.EnableMDL.Load() { - s.mdlSchemaVersions.Store(jobID, version) - } else { - atomic.StoreInt64(&s.selfSchemaVersion, version) - } - return nil -} - -// Done implements SchemaSyncer.Done interface. -func (s *MockSchemaSyncer) Done() <-chan struct{} { - return s.mockSession -} - -// CloseSession mockSession, it is exported for testing. -func (s *MockSchemaSyncer) CloseSession() { - close(s.mockSession) -} - -// Restart implements SchemaSyncer.Restart interface. -func (s *MockSchemaSyncer) Restart(_ context.Context) error { - s.mockSession = make(chan struct{}, 1) - return nil -} - -// OwnerUpdateGlobalVersion implements SchemaSyncer.OwnerUpdateGlobalVersion interface. -func (s *MockSchemaSyncer) OwnerUpdateGlobalVersion(_ context.Context, _ int64) error { - select { - case s.globalVerCh <- clientv3.WatchResponse{}: - default: - } - return nil -} - -// OwnerCheckAllVersions implements SchemaSyncer.OwnerCheckAllVersions interface. -func (s *MockSchemaSyncer) OwnerCheckAllVersions(ctx context.Context, jobID int64, latestVer int64) error { - ticker := time.NewTicker(mockCheckVersInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - failpoint.Inject("checkOwnerCheckAllVersionsWaitTime", func(v failpoint.Value) { - if v.(bool) { - panic("shouldn't happen") - } - }) - return errors.Trace(ctx.Err()) - case <-ticker.C: - if variable.EnableMDL.Load() { - ver, ok := s.mdlSchemaVersions.Load(jobID) - if ok && ver.(int64) >= latestVer { - return nil - } - } else { - ver := atomic.LoadInt64(&s.selfSchemaVersion) - if ver >= latestVer { - return nil - } - } - } - } -} - -// Close implements SchemaSyncer.Close interface. -func (*MockSchemaSyncer) Close() {} - -// NewMockStateSyncer creates a new mock StateSyncer. -func NewMockStateSyncer() syncer.StateSyncer { - return &MockStateSyncer{} -} - -// clusterState mocks cluster state. -// We move it from MockStateSyncer to here. Because we want to make it unaffected by ddl close. -var clusterState *atomicutil.Pointer[syncer.StateInfo] - -// MockStateSyncer is a mock state syncer, it is exported for testing. -type MockStateSyncer struct { - globalVerCh chan clientv3.WatchResponse - mockSession chan struct{} -} - -// Init implements StateSyncer.Init interface. -func (s *MockStateSyncer) Init(context.Context) error { - s.globalVerCh = make(chan clientv3.WatchResponse, 1) - s.mockSession = make(chan struct{}, 1) - state := syncer.NewStateInfo(syncer.StateNormalRunning) - if clusterState == nil { - clusterState = atomicutil.NewPointer(state) - } - return nil -} - -// UpdateGlobalState implements StateSyncer.UpdateGlobalState interface. -func (s *MockStateSyncer) UpdateGlobalState(_ context.Context, stateInfo *syncer.StateInfo) error { - failpoint.Inject("mockUpgradingState", func(val failpoint.Value) { - if val.(bool) { - clusterState.Store(stateInfo) - failpoint.Return(nil) - } - }) - s.globalVerCh <- clientv3.WatchResponse{} - clusterState.Store(stateInfo) - return nil -} - -// GetGlobalState implements StateSyncer.GetGlobalState interface. -func (*MockStateSyncer) GetGlobalState(context.Context) (*syncer.StateInfo, error) { - return clusterState.Load(), nil -} - -// IsUpgradingState implements StateSyncer.IsUpgradingState interface. -func (*MockStateSyncer) IsUpgradingState() bool { - return clusterState.Load().State == syncer.StateUpgrading -} - -// WatchChan implements StateSyncer.WatchChan interface. -func (s *MockStateSyncer) WatchChan() clientv3.WatchChan { - return s.globalVerCh -} - -// Rewatch implements StateSyncer.Rewatch interface. -func (*MockStateSyncer) Rewatch(context.Context) {} - -type mockDelRange struct { -} - -// newMockDelRangeManager creates a mock delRangeManager only used for test. -func newMockDelRangeManager() delRangeManager { - return &mockDelRange{} -} - -// addDelRangeJob implements delRangeManager interface. -func (*mockDelRange) addDelRangeJob(_ context.Context, _ *model.Job) error { - return nil -} - -// removeFromGCDeleteRange implements delRangeManager interface. -func (*mockDelRange) removeFromGCDeleteRange(_ context.Context, _ int64) error { - return nil -} - -// start implements delRangeManager interface. -func (*mockDelRange) start() {} - -// clear implements delRangeManager interface. -func (*mockDelRange) clear() {} - -// MockTableInfo mocks a table info by create table stmt ast and a specified table id. -func MockTableInfo(ctx sessionctx.Context, stmt *ast.CreateTableStmt, tableID int64) (*model.TableInfo, error) { - chs, coll := charset.GetDefaultCharsetAndCollate() - cols, newConstraints, err := buildColumnsAndConstraints(ctx, stmt.Cols, stmt.Constraints, chs, coll) - if err != nil { - return nil, errors.Trace(err) - } - tbl, err := BuildTableInfo(ctx, stmt.Table.Name, cols, newConstraints, "", "") - if err != nil { - return nil, errors.Trace(err) - } - tbl.ID = tableID - - if err = setTableAutoRandomBits(ctx, tbl, stmt.Cols); err != nil { - return nil, errors.Trace(err) - } - - // The specified charset will be handled in handleTableOptions - if err = handleTableOptions(stmt.Options, tbl); err != nil { - return nil, errors.Trace(err) - } - - return tbl, nil -} diff --git a/ddl/options.go b/ddl/options.go deleted file mode 100644 index e4c59e5d3b5b7..0000000000000 --- a/ddl/options.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "time" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - clientv3 "go.etcd.io/etcd/client/v3" -) - -// Option represents an option to initialize the DDL module -type Option func(*Options) - -// Options represents all the options of the DDL module needs -type Options struct { - EtcdCli *clientv3.Client - Store kv.Storage - InfoCache *infoschema.InfoCache - Hook Callback - Lease time.Duration -} - -// WithEtcdClient specifies the `clientv3.Client` of DDL used to request the etcd service -func WithEtcdClient(client *clientv3.Client) Option { - return func(options *Options) { - options.EtcdCli = client - } -} - -// WithStore specifies the `kv.Storage` of DDL used to request the KV service -func WithStore(store kv.Storage) Option { - return func(options *Options) { - options.Store = store - } -} - -// WithInfoCache specifies the `infoschema.InfoCache` -func WithInfoCache(ic *infoschema.InfoCache) Option { - return func(options *Options) { - options.InfoCache = ic - } -} - -// WithHook specifies the `Callback` of DDL used to notify the outer module when events are triggered -func WithHook(callback Callback) Option { - return func(options *Options) { - options.Hook = callback - } -} - -// WithLease specifies the schema lease duration -func WithLease(lease time.Duration) Option { - return func(options *Options) { - options.Lease = lease - } -} diff --git a/ddl/partition.go b/ddl/partition.go deleted file mode 100644 index 9d38a94745713..0000000000000 --- a/ddl/partition.go +++ /dev/null @@ -1,4253 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "math" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/mock" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/slice" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stringutil" - "github.com/tikv/client-go/v2/tikv" - kvutil "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -const ( - partitionMaxValue = "MAXVALUE" -) - -func checkAddPartition(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.PartitionInfo, []model.PartitionDefinition, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, errors.Trace(err) - } - partInfo := &model.PartitionInfo{} - err = job.DecodeArgs(&partInfo) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, errors.Trace(err) - } - if len(tblInfo.Partition.AddingDefinitions) > 0 { - return tblInfo, partInfo, tblInfo.Partition.AddingDefinitions, nil - } - return tblInfo, partInfo, []model.PartitionDefinition{}, nil -} - -// TODO: Move this into reorganize partition! -func (w *worker) onAddTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - // Handle the rolling back job - if job.IsRollingback() { - ver, err := w.onDropTablePartition(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - return ver, nil - } - - // notice: addingDefinitions is empty when job is in state model.StateNone - tblInfo, partInfo, addingDefinitions, err := checkAddPartition(t, job) - if err != nil { - return ver, err - } - - // In order to skip maintaining the state check in partitionDefinition, TiDB use addingDefinition instead of state field. - // So here using `job.SchemaState` to judge what the stage of this job is. - switch job.SchemaState { - case model.StateNone: - // job.SchemaState == model.StateNone means the job is in the initial state of add partition. - // Here should use partInfo from job directly and do some check action. - err = checkAddPartitionTooManyPartitions(uint64(len(tblInfo.Partition.Definitions) + len(partInfo.Definitions))) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkAddPartitionValue(tblInfo, partInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkAddPartitionNameUnique(tblInfo, partInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - // move the adding definition into tableInfo. - updateAddingPartitionInfo(partInfo, tblInfo) - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - // modify placement settings - for _, def := range tblInfo.Partition.AddingDefinitions { - if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, def.PlacementPolicyRef); err != nil { - return ver, errors.Trace(err) - } - } - - if tblInfo.TiFlashReplica != nil { - // Must set placement rule, and make sure it succeeds. - if err := infosync.ConfigureTiFlashPDForPartitions(true, &tblInfo.Partition.AddingDefinitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); err != nil { - logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) - return ver, errors.Trace(err) - } - } - - bundles, err := alterTablePartitionBundles(t, tblInfo, tblInfo.Partition.AddingDefinitions) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - ids := getIDs([]*model.TableInfo{tblInfo}) - for _, p := range tblInfo.Partition.AddingDefinitions { - ids = append(ids, p.ID) - } - if _, err := alterTableLabelRule(job.SchemaName, tblInfo, ids); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - // none -> replica only - job.SchemaState = model.StateReplicaOnly - case model.StateReplicaOnly: - // replica only -> public - failpoint.Inject("sleepBeforeReplicaOnly", func(val failpoint.Value) { - sleepSecond := val.(int) - time.Sleep(time.Duration(sleepSecond) * time.Second) - }) - // Here need do some tiflash replica complement check. - // TODO: If a table is with no TiFlashReplica or it is not available, the replica-only state can be eliminated. - if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { - // For available state, the new added partition should wait it's replica to - // be finished. Otherwise the query to this partition will be blocked. - needRetry, err := checkPartitionReplica(tblInfo.TiFlashReplica.Count, addingDefinitions, d) - if err != nil { - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - if needRetry { - // The new added partition hasn't been replicated. - // Do nothing to the job this time, wait next worker round. - time.Sleep(tiflashCheckTiDBHTTPAPIHalfInterval) - // Set the error here which will lead this job exit when it's retry times beyond the limitation. - return ver, errors.Errorf("[ddl] add partition wait for tiflash replica to complete") - } - } - - // When TiFlash Replica is ready, we must move them into `AvailablePartitionIDs`. - if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { - for _, d := range partInfo.Definitions { - tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, d.ID) - err = infosync.UpdateTiFlashProgressCache(d.ID, 1) - if err != nil { - // just print log, progress will be updated in `refreshTiFlashTicker` - logutil.BgLogger().Error("update tiflash sync progress cache failed", - zap.Error(err), - zap.Int64("tableID", tblInfo.ID), - zap.Int64("partitionID", d.ID), - ) - } - } - } - // For normal and replica finished table, move the `addingDefinitions` into `Definitions`. - updatePartitionInfo(tblInfo) - - preSplitAndScatter(w.sess.Context, d.store, tblInfo, addingDefinitions) - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionAddTablePartition, TableInfo: tblInfo, PartInfo: partInfo}) - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) - } - - return ver, errors.Trace(err) -} - -// alterTableLabelRule updates Label Rules if they exists -// returns true if changed. -func alterTableLabelRule(schemaName string, meta *model.TableInfo, ids []int64) (bool, error) { - tableRuleID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, schemaName, meta.Name.L) - oldRule, err := infosync.GetLabelRules(context.TODO(), []string{tableRuleID}) - if err != nil { - return false, errors.Trace(err) - } - if len(oldRule) == 0 { - return false, nil - } - - r, ok := oldRule[tableRuleID] - if ok { - rule := r.Reset(schemaName, meta.Name.L, "", ids...) - err = infosync.PutLabelRule(context.TODO(), rule) - if err != nil { - return false, errors.Wrapf(err, "failed to notify PD label rule") - } - return true, nil - } - return false, nil -} - -func alterTablePartitionBundles(t *meta.Meta, tblInfo *model.TableInfo, addingDefinitions []model.PartitionDefinition) ([]*placement.Bundle, error) { - var bundles []*placement.Bundle - - // tblInfo do not include added partitions, so we should add them first - tblInfo = tblInfo.Clone() - p := *tblInfo.Partition - p.Definitions = append([]model.PartitionDefinition{}, p.Definitions...) - p.Definitions = append(tblInfo.Partition.Definitions, addingDefinitions...) - tblInfo.Partition = &p - - // bundle for table should be recomputed because it includes some default configs for partitions - tblBundle, err := placement.NewTableBundle(t, tblInfo) - if err != nil { - return nil, errors.Trace(err) - } - - if tblBundle != nil { - bundles = append(bundles, tblBundle) - } - - partitionBundles, err := placement.NewPartitionListBundles(t, addingDefinitions) - if err != nil { - return nil, errors.Trace(err) - } - - bundles = append(bundles, partitionBundles...) - return bundles, nil -} - -// When drop/truncate a partition, we should still keep the dropped partition's placement settings to avoid unnecessary region schedules. -// When a partition is not configured with a placement policy directly, its rule is in the table's placement group which will be deleted after -// partition truncated/dropped. So it is necessary to create a standalone placement group with partition id after it. -func droppedPartitionBundles(t *meta.Meta, tblInfo *model.TableInfo, dropPartitions []model.PartitionDefinition) ([]*placement.Bundle, error) { - partitions := make([]model.PartitionDefinition, 0, len(dropPartitions)) - for _, def := range dropPartitions { - def = def.Clone() - if def.PlacementPolicyRef == nil { - def.PlacementPolicyRef = tblInfo.PlacementPolicyRef - } - - if def.PlacementPolicyRef != nil { - partitions = append(partitions, def) - } - } - - return placement.NewPartitionListBundles(t, partitions) -} - -// updatePartitionInfo merge `addingDefinitions` into `Definitions` in the tableInfo. -func updatePartitionInfo(tblInfo *model.TableInfo) { - parInfo := &model.PartitionInfo{} - oldDefs, newDefs := tblInfo.Partition.Definitions, tblInfo.Partition.AddingDefinitions - parInfo.Definitions = make([]model.PartitionDefinition, 0, len(newDefs)+len(oldDefs)) - parInfo.Definitions = append(parInfo.Definitions, oldDefs...) - parInfo.Definitions = append(parInfo.Definitions, newDefs...) - tblInfo.Partition.Definitions = parInfo.Definitions - tblInfo.Partition.AddingDefinitions = nil -} - -// updateAddingPartitionInfo write adding partitions into `addingDefinitions` field in the tableInfo. -func updateAddingPartitionInfo(partitionInfo *model.PartitionInfo, tblInfo *model.TableInfo) { - newDefs := partitionInfo.Definitions - tblInfo.Partition.AddingDefinitions = make([]model.PartitionDefinition, 0, len(newDefs)) - tblInfo.Partition.AddingDefinitions = append(tblInfo.Partition.AddingDefinitions, newDefs...) -} - -// rollbackAddingPartitionInfo remove the `addingDefinitions` in the tableInfo. -func rollbackAddingPartitionInfo(tblInfo *model.TableInfo) ([]int64, []string, []*placement.Bundle) { - physicalTableIDs := make([]int64, 0, len(tblInfo.Partition.AddingDefinitions)) - partNames := make([]string, 0, len(tblInfo.Partition.AddingDefinitions)) - rollbackBundles := make([]*placement.Bundle, 0, len(tblInfo.Partition.AddingDefinitions)) - for _, one := range tblInfo.Partition.AddingDefinitions { - physicalTableIDs = append(physicalTableIDs, one.ID) - partNames = append(partNames, one.Name.L) - if one.PlacementPolicyRef != nil { - rollbackBundles = append(rollbackBundles, placement.NewBundle(one.ID)) - } - } - tblInfo.Partition.AddingDefinitions = nil - return physicalTableIDs, partNames, rollbackBundles -} - -// Check if current table already contains DEFAULT list partition -func checkAddListPartitions(tblInfo *model.TableInfo) error { - for i := range tblInfo.Partition.Definitions { - for j := range tblInfo.Partition.Definitions[i].InValues { - for _, val := range tblInfo.Partition.Definitions[i].InValues[j] { - if val == "DEFAULT" { // should already be normalized - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("ADD List partition, already contains DEFAULT partition. Please use REORGANIZE PARTITION instead") - } - } - } - } - return nil -} - -// checkAddPartitionValue check add Partition Values, -// For Range: values less than value must be strictly increasing for each partition. -// For List: if a Default partition exists, -// -// no ADD partition can be allowed -// (needs reorganize partition instead). -func checkAddPartitionValue(meta *model.TableInfo, part *model.PartitionInfo) error { - switch meta.Partition.Type { - case model.PartitionTypeRange: - if len(meta.Partition.Columns) == 0 { - newDefs, oldDefs := part.Definitions, meta.Partition.Definitions - rangeValue := oldDefs[len(oldDefs)-1].LessThan[0] - if strings.EqualFold(rangeValue, "MAXVALUE") { - return errors.Trace(dbterror.ErrPartitionMaxvalue) - } - - currentRangeValue, err := strconv.Atoi(rangeValue) - if err != nil { - return errors.Trace(err) - } - - for i := 0; i < len(newDefs); i++ { - ifMaxvalue := strings.EqualFold(newDefs[i].LessThan[0], "MAXVALUE") - if ifMaxvalue && i == len(newDefs)-1 { - return nil - } else if ifMaxvalue && i != len(newDefs)-1 { - return errors.Trace(dbterror.ErrPartitionMaxvalue) - } - - nextRangeValue, err := strconv.Atoi(newDefs[i].LessThan[0]) - if err != nil { - return errors.Trace(err) - } - if nextRangeValue <= currentRangeValue { - return errors.Trace(dbterror.ErrRangeNotIncreasing) - } - currentRangeValue = nextRangeValue - } - } - case model.PartitionTypeList: - err := checkAddListPartitions(meta) - if err != nil { - return err - } - } - return nil -} - -func checkPartitionReplica(replicaCount uint64, addingDefinitions []model.PartitionDefinition, d *ddlCtx) (needWait bool, err error) { - failpoint.Inject("mockWaitTiFlashReplica", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(true, nil) - } - }) - failpoint.Inject("mockWaitTiFlashReplicaOK", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(false, nil) - } - }) - - ctx := context.Background() - pdCli := d.store.(tikv.Storage).GetRegionCache().PDClient() - stores, err := pdCli.GetAllStores(ctx) - if err != nil { - return needWait, errors.Trace(err) - } - // Check whether stores have `count` tiflash engines. - tiFlashStoreCount := uint64(0) - for _, store := range stores { - if storeHasEngineTiFlashLabel(store) { - tiFlashStoreCount++ - } - } - if replicaCount > tiFlashStoreCount { - return false, errors.Errorf("[ddl] the tiflash replica count: %d should be less than the total tiflash server count: %d", replicaCount, tiFlashStoreCount) - } - for _, pd := range addingDefinitions { - startKey, endKey := tablecodec.GetTableHandleKeyRange(pd.ID) - regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) - if err != nil { - return needWait, errors.Trace(err) - } - // For every region in the partition, if it has some corresponding peers and - // no pending peers, that means the replication has completed. - for _, region := range regions { - regionState, err := pdCli.GetRegionByID(ctx, region.Meta.Id) - if err != nil { - return needWait, errors.Trace(err) - } - tiflashPeerAtLeastOne := checkTiFlashPeerStoreAtLeastOne(stores, regionState.Meta.Peers) - failpoint.Inject("ForceTiflashNotAvailable", func(v failpoint.Value) { - tiflashPeerAtLeastOne = v.(bool) - }) - // It's unnecessary to wait all tiflash peer to be replicated. - // Here only make sure that tiflash peer count > 0 (at least one). - if tiflashPeerAtLeastOne { - continue - } - needWait = true - logutil.BgLogger().Info("partition replicas check failed in replica-only DDL state", zap.String("category", "ddl"), zap.Int64("pID", pd.ID), zap.Uint64("wait region ID", region.Meta.Id), zap.Bool("tiflash peer at least one", tiflashPeerAtLeastOne), zap.Time("check time", time.Now())) - return needWait, nil - } - } - logutil.BgLogger().Info("partition replicas check ok in replica-only DDL state", zap.String("category", "ddl")) - return needWait, nil -} - -func checkTiFlashPeerStoreAtLeastOne(stores []*metapb.Store, peers []*metapb.Peer) bool { - for _, peer := range peers { - for _, store := range stores { - if peer.StoreId == store.Id && storeHasEngineTiFlashLabel(store) { - return true - } - } - } - return false -} - -func storeHasEngineTiFlashLabel(store *metapb.Store) bool { - for _, label := range store.Labels { - if label.Key == placement.EngineLabelKey && label.Value == placement.EngineLabelTiFlash { - return true - } - } - return false -} - -func checkListPartitions(defs []*ast.PartitionDefinition) error { - for _, def := range defs { - _, ok := def.Clause.(*ast.PartitionDefinitionClauseIn) - if !ok { - switch def.Clause.(type) { - case *ast.PartitionDefinitionClauseLessThan: - return ast.ErrPartitionWrongValues.GenWithStackByArgs("RANGE", "LESS THAN") - case *ast.PartitionDefinitionClauseNone: - return ast.ErrPartitionRequiresValues.GenWithStackByArgs("LIST", "IN") - default: - return dbterror.ErrUnsupportedCreatePartition.GenWithStack("Only VALUES IN () is supported for LIST partitioning") - } - } - } - return nil -} - -// buildTablePartitionInfo builds partition info and checks for some errors. -func buildTablePartitionInfo(ctx sessionctx.Context, s *ast.PartitionOptions, tbInfo *model.TableInfo) error { - if s == nil { - return nil - } - - if strings.EqualFold(ctx.GetSessionVars().EnableTablePartition, "OFF") { - ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrTablePartitionDisabled) - return nil - } - - var enable bool - switch s.Tp { - case model.PartitionTypeRange: - enable = true - case model.PartitionTypeList: - // Partition by list is enabled only when tidb_enable_list_partition is 'ON'. - enable = ctx.GetSessionVars().EnableListTablePartition - if enable { - err := checkListPartitions(s.Definitions) - if err != nil { - return err - } - } - case model.PartitionTypeHash, model.PartitionTypeKey: - // Partition by hash and key is enabled by default. - if s.Sub != nil { - // Subpartitioning only allowed with Range or List - return ast.ErrSubpartition - } - // Note that linear hash is simply ignored, and creates non-linear hash/key. - if s.Linear { - ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("LINEAR %s is not supported, using non-linear %s instead", s.Tp.String(), s.Tp.String()))) - } - if s.Tp == model.PartitionTypeHash || len(s.ColumnNames) != 0 { - enable = true - } - } - - if !enable { - ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("Unsupported partition type %v, treat as normal table", s.Tp))) - return nil - } - if s.Sub != nil { - ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("Unsupported subpartitioning, only using %v partitioning", s.Tp))) - } - - pi := &model.PartitionInfo{ - Type: s.Tp, - Enable: enable, - Num: s.Num, - } - tbInfo.Partition = pi - if s.Expr != nil { - if err := checkPartitionFuncValid(ctx, tbInfo, s.Expr); err != nil { - return errors.Trace(err) - } - buf := new(bytes.Buffer) - restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags|format.RestoreBracketAroundBinaryOperation, buf) - if err := s.Expr.Restore(restoreCtx); err != nil { - return err - } - pi.Expr = buf.String() - } else if s.ColumnNames != nil { - pi.Columns = make([]model.CIStr, 0, len(s.ColumnNames)) - for _, cn := range s.ColumnNames { - pi.Columns = append(pi.Columns, cn.Name) - } - if err := checkColumnsPartitionType(tbInfo); err != nil { - return err - } - } - - err := generatePartitionDefinitionsFromInterval(ctx, s, tbInfo) - if err != nil { - return errors.Trace(err) - } - - defs, err := buildPartitionDefinitionsInfo(ctx, s.Definitions, tbInfo, s.Num) - if err != nil { - return errors.Trace(err) - } - - tbInfo.Partition.Definitions = defs - - if s.Interval != nil { - // Syntactic sugar for INTERVAL partitioning - // Generate the resulting CREATE TABLE as the query string - query, ok := ctx.Value(sessionctx.QueryString).(string) - if ok { - sqlMode := ctx.GetSessionVars().SQLMode - var buf bytes.Buffer - AppendPartitionDefs(tbInfo.Partition, &buf, sqlMode) - - syntacticSugar := s.Interval.OriginalText() - syntacticStart := s.Interval.OriginTextPosition() - newQuery := query[:syntacticStart] + "(" + buf.String() + ")" + query[syntacticStart+len(syntacticSugar):] - ctx.SetValue(sessionctx.QueryString, newQuery) - } - } - - partCols, err := getPartitionColSlices(ctx, tbInfo, s) - if err != nil { - return errors.Trace(err) - } - - for _, index := range tbInfo.Indices { - if index.Unique && !checkUniqueKeyIncludePartKey(partCols, index.Columns) { - index.Global = config.GetGlobalConfig().EnableGlobalIndex - } - } - return nil -} - -func getPartitionColSlices(sctx sessionctx.Context, tblInfo *model.TableInfo, s *ast.PartitionOptions) (partCols stringSlice, err error) { - if s.Expr != nil { - extractCols := newPartitionExprChecker(sctx, tblInfo) - s.Expr.Accept(extractCols) - partColumns, err := extractCols.columns, extractCols.err - if err != nil { - return nil, err - } - partCols = columnInfoSlice(partColumns) - } else if len(s.ColumnNames) > 0 { - partCols = columnNameSlice(s.ColumnNames) - } else { - return nil, errors.Errorf("Table partition metadata not correct, neither partition expression or list of partition columns") - } - return partCols, nil -} - -// getPartitionIntervalFromTable checks if a partitioned table matches a generated INTERVAL partitioned scheme -// will return nil if error occurs, i.e. not an INTERVAL partitioned table -func getPartitionIntervalFromTable(ctx sessionctx.Context, tbInfo *model.TableInfo) *ast.PartitionInterval { - if tbInfo.Partition == nil || - tbInfo.Partition.Type != model.PartitionTypeRange { - return nil - } - if len(tbInfo.Partition.Columns) > 1 { - // Multi-column RANGE COLUMNS is not supported with INTERVAL - return nil - } - if len(tbInfo.Partition.Definitions) < 2 { - // Must have at least two partitions to calculate an INTERVAL - return nil - } - - var ( - interval ast.PartitionInterval - startIdx = 0 - endIdx = len(tbInfo.Partition.Definitions) - 1 - isIntType = true - minVal = "0" - ) - if len(tbInfo.Partition.Columns) > 0 { - partCol := findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) - if partCol.FieldType.EvalType() == types.ETInt { - min := getLowerBoundInt(partCol) - minVal = strconv.FormatInt(min, 10) - } else if partCol.FieldType.EvalType() == types.ETDatetime { - isIntType = false - minVal = "0000-01-01" - } else { - // Only INT and Datetime columns are supported for INTERVAL partitioning - return nil - } - } else { - if !isPartExprUnsigned(tbInfo) { - minVal = "-9223372036854775808" - } - } - - // Check if possible null partition - firstPartLessThan := driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[0].LessThan[0]) - if strings.EqualFold(firstPartLessThan, minVal) { - interval.NullPart = true - startIdx++ - firstPartLessThan = driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[startIdx].LessThan[0]) - } - // flag if MAXVALUE partition - lastPartLessThan := driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[endIdx].LessThan[0]) - if strings.EqualFold(lastPartLessThan, partitionMaxValue) { - interval.MaxValPart = true - endIdx-- - lastPartLessThan = driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[endIdx].LessThan[0]) - } - // Guess the interval - if startIdx >= endIdx { - // Must have at least two partitions to calculate an INTERVAL - return nil - } - var firstExpr, lastExpr ast.ExprNode - if isIntType { - exprStr := fmt.Sprintf("((%s) - (%s)) DIV %d", lastPartLessThan, firstPartLessThan, endIdx-startIdx) - exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprStr, nil, nil) - if err != nil { - return nil - } - val, isNull, err := exprs[0].EvalInt(ctx, chunk.Row{}) - if isNull || err != nil || val < 1 { - // If NULL, error or interval < 1 then cannot be an INTERVAL partitioned table - return nil - } - interval.IntervalExpr.Expr = ast.NewValueExpr(val, "", "") - interval.IntervalExpr.TimeUnit = ast.TimeUnitInvalid - firstExpr, err = astIntValueExprFromStr(firstPartLessThan, minVal == "0") - if err != nil { - return nil - } - interval.FirstRangeEnd = &firstExpr - lastExpr, err = astIntValueExprFromStr(lastPartLessThan, minVal == "0") - if err != nil { - return nil - } - interval.LastRangeEnd = &lastExpr - } else { // types.ETDatetime - exprStr := fmt.Sprintf("TIMESTAMPDIFF(SECOND, '%s', '%s')", firstPartLessThan, lastPartLessThan) - exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprStr, nil, nil) - if err != nil { - return nil - } - val, isNull, err := exprs[0].EvalInt(ctx, chunk.Row{}) - if isNull || err != nil || val < 1 { - // If NULL, error or interval < 1 then cannot be an INTERVAL partitioned table - return nil - } - - // This will not find all matches > 28 days, since INTERVAL 1 MONTH can generate - // 2022-01-31, 2022-02-28, 2022-03-31 etc. so we just assume that if there is a - // diff >= 28 days, we will try with Month and not retry with something else... - i := val / int64(endIdx-startIdx) - if i < (28 * 24 * 60 * 60) { - // Since it is not stored or displayed, non need to try Minute..Week! - interval.IntervalExpr.Expr = ast.NewValueExpr(i, "", "") - interval.IntervalExpr.TimeUnit = ast.TimeUnitSecond - } else { - // Since it is not stored or displayed, non need to try to match Quarter or Year! - if (endIdx - startIdx) <= 3 { - // in case February is in the range - i = i / (28 * 24 * 60 * 60) - } else { - // This should be good for intervals up to 5 years - i = i / (30 * 24 * 60 * 60) - } - interval.IntervalExpr.Expr = ast.NewValueExpr(i, "", "") - interval.IntervalExpr.TimeUnit = ast.TimeUnitMonth - } - - firstExpr = ast.NewValueExpr(firstPartLessThan, "", "") - lastExpr = ast.NewValueExpr(lastPartLessThan, "", "") - interval.FirstRangeEnd = &firstExpr - interval.LastRangeEnd = &lastExpr - } - - partitionMethod := ast.PartitionMethod{ - Tp: model.PartitionTypeRange, - Interval: &interval, - } - partOption := &ast.PartitionOptions{PartitionMethod: partitionMethod} - // Generate the definitions from interval, first and last - err := generatePartitionDefinitionsFromInterval(ctx, partOption, tbInfo) - if err != nil { - return nil - } - - return &interval -} - -// comparePartitionAstAndModel compares a generated *ast.PartitionOptions and a *model.PartitionInfo -func comparePartitionAstAndModel(ctx sessionctx.Context, pAst *ast.PartitionOptions, pModel *model.PartitionInfo) error { - a := pAst.Definitions - m := pModel.Definitions - if len(pAst.Definitions) != len(pModel.Definitions) { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: number of partitions generated != partition defined (%d != %d)", len(a), len(m)) - } - for i := range pAst.Definitions { - // Allow options to differ! (like Placement Rules) - // Allow names to differ! - - // Check MAXVALUE - maxVD := false - if strings.EqualFold(m[i].LessThan[0], partitionMaxValue) { - maxVD = true - } - generatedExpr := a[i].Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs[0] - _, maxVG := generatedExpr.(*ast.MaxValueExpr) - if maxVG || maxVD { - if maxVG && maxVD { - continue - } - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("INTERVAL partitioning: MAXVALUE clause defined for partition %s differs between generated and defined", m[i].Name.O)) - } - - lessThan := m[i].LessThan[0] - if len(lessThan) > 1 && lessThan[:1] == "'" && lessThan[len(lessThan)-1:] == "'" { - lessThan = driver.UnwrapFromSingleQuotes(lessThan) - } - cmpExpr := &ast.BinaryOperationExpr{ - Op: opcode.EQ, - L: ast.NewValueExpr(lessThan, "", ""), - R: generatedExpr, - } - cmp, err := expression.EvalAstExpr(ctx, cmpExpr) - if err != nil { - return err - } - if cmp.GetInt64() != 1 { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("INTERVAL partitioning: LESS THAN for partition %s differs between generated and defined", m[i].Name.O)) - } - } - return nil -} - -// comparePartitionDefinitions check if generated definitions are the same as the given ones -// Allow names to differ -// returns error in case of error or non-accepted difference -func comparePartitionDefinitions(ctx sessionctx.Context, a, b []*ast.PartitionDefinition) error { - if len(a) != len(b) { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("number of partitions generated != partition defined (%d != %d)", len(a), len(b)) - } - for i := range a { - if len(b[i].Sub) > 0 { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does have unsupported subpartitions", b[i].Name.O)) - } - // TODO: We could extend the syntax to allow for table options too, like: - // CREATE TABLE t ... INTERVAL ... LAST PARTITION LESS THAN ('2015-01-01') PLACEMENT POLICY = 'cheapStorage' - // ALTER TABLE t LAST PARTITION LESS THAN ('2022-01-01') PLACEMENT POLICY 'defaultStorage' - // ALTER TABLE t LAST PARTITION LESS THAN ('2023-01-01') PLACEMENT POLICY 'fastStorage' - if len(b[i].Options) > 0 { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does have unsupported options", b[i].Name.O)) - } - lessThan, ok := b[i].Clause.(*ast.PartitionDefinitionClauseLessThan) - if !ok { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does not have the right type for LESS THAN", b[i].Name.O)) - } - definedExpr := lessThan.Exprs[0] - generatedExpr := a[i].Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs[0] - _, maxVD := definedExpr.(*ast.MaxValueExpr) - _, maxVG := generatedExpr.(*ast.MaxValueExpr) - if maxVG || maxVD { - if maxVG && maxVD { - continue - } - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s differs between generated and defined for MAXVALUE", b[i].Name.O)) - } - cmpExpr := &ast.BinaryOperationExpr{ - Op: opcode.EQ, - L: definedExpr, - R: generatedExpr, - } - cmp, err := expression.EvalAstExpr(ctx, cmpExpr) - if err != nil { - return err - } - if cmp.GetInt64() != 1 { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s differs between generated and defined for expression", b[i].Name.O)) - } - } - return nil -} - -func getLowerBoundInt(partCols ...*model.ColumnInfo) int64 { - ret := int64(0) - for _, col := range partCols { - if mysql.HasUnsignedFlag(col.FieldType.GetFlag()) { - return 0 - } - ret = mathutil.Min(ret, types.IntergerSignedLowerBound(col.GetType())) - } - return ret -} - -// generatePartitionDefinitionsFromInterval generates partition Definitions according to INTERVAL options on partOptions -func generatePartitionDefinitionsFromInterval(ctx sessionctx.Context, partOptions *ast.PartitionOptions, tbInfo *model.TableInfo) error { - if partOptions.Interval == nil { - return nil - } - if tbInfo.Partition.Type != model.PartitionTypeRange { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only allowed on RANGE partitioning") - } - if len(partOptions.ColumnNames) > 1 || len(tbInfo.Partition.Columns) > 1 { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, does not allow RANGE COLUMNS with more than one column") - } - var partCol *model.ColumnInfo - if len(tbInfo.Partition.Columns) > 0 { - partCol = findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) - if partCol == nil { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, could not find any RANGE COLUMNS") - } - // Only support Datetime, date and INT column types for RANGE INTERVAL! - switch partCol.FieldType.EvalType() { - case types.ETInt, types.ETDatetime: - default: - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only supports Date, Datetime and INT types") - } - } - // Allow given partition definitions, but check it later! - definedPartDefs := partOptions.Definitions - partOptions.Definitions = make([]*ast.PartitionDefinition, 0, 1) - if partOptions.Interval.FirstRangeEnd == nil || partOptions.Interval.LastRangeEnd == nil { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, currently requires FIRST and LAST partitions to be defined") - } - switch partOptions.Interval.IntervalExpr.TimeUnit { - case ast.TimeUnitInvalid, ast.TimeUnitYear, ast.TimeUnitQuarter, ast.TimeUnitMonth, ast.TimeUnitWeek, ast.TimeUnitDay, ast.TimeUnitHour, ast.TimeUnitDayMinute, ast.TimeUnitSecond: - default: - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only supports YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE and SECOND as time unit") - } - first := ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{*partOptions.Interval.FirstRangeEnd}, - } - last := ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{*partOptions.Interval.LastRangeEnd}, - } - if len(tbInfo.Partition.Columns) > 0 { - colTypes := collectColumnsType(tbInfo) - if len(colTypes) != len(tbInfo.Partition.Columns) { - return dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") - } - if _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, first.Exprs); err != nil { - return err - } - if _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, last.Exprs); err != nil { - return err - } - } else { - if err := checkPartitionValuesIsInt(ctx, "FIRST PARTITION", first.Exprs, tbInfo); err != nil { - return err - } - if err := checkPartitionValuesIsInt(ctx, "LAST PARTITION", last.Exprs, tbInfo); err != nil { - return err - } - } - if partOptions.Interval.NullPart { - var partExpr ast.ExprNode - if len(tbInfo.Partition.Columns) == 1 && partOptions.Interval.IntervalExpr.TimeUnit != ast.TimeUnitInvalid { - // Notice compatibility with MySQL, keyword here is 'supported range' but MySQL seems to work from 0000-01-01 too - // https://dev.mysql.com/doc/refman/8.0/en/datetime.html says range 1000-01-01 - 9999-12-31 - // https://docs.pingcap.com/tidb/dev/data-type-date-and-time says The supported range is '0000-01-01' to '9999-12-31' - // set LESS THAN to ZeroTime - partExpr = ast.NewValueExpr("0000-01-01", "", "") - } else { - var min int64 - if partCol != nil { - min = getLowerBoundInt(partCol) - } else { - if !isPartExprUnsigned(tbInfo) { - min = math.MinInt64 - } - } - partExpr = ast.NewValueExpr(min, "", "") - } - partOptions.Definitions = append(partOptions.Definitions, &ast.PartitionDefinition{ - Name: model.NewCIStr("P_NULL"), - Clause: &ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{partExpr}, - }, - }) - } - - err := GeneratePartDefsFromInterval(ctx, ast.AlterTablePartition, tbInfo, partOptions) - if err != nil { - return err - } - - if partOptions.Interval.MaxValPart { - partOptions.Definitions = append(partOptions.Definitions, &ast.PartitionDefinition{ - Name: model.NewCIStr("P_MAXVALUE"), - Clause: &ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{&ast.MaxValueExpr{}}, - }, - }) - } - - if len(definedPartDefs) > 0 { - err := comparePartitionDefinitions(ctx, partOptions.Definitions, definedPartDefs) - if err != nil { - return err - } - // Seems valid, so keep the defined so that the user defined names are kept etc. - partOptions.Definitions = definedPartDefs - } else if len(tbInfo.Partition.Definitions) > 0 { - err := comparePartitionAstAndModel(ctx, partOptions, tbInfo.Partition) - if err != nil { - return err - } - } - - return nil -} - -func astIntValueExprFromStr(s string, unsigned bool) (ast.ExprNode, error) { - if unsigned { - u, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, err - } - return ast.NewValueExpr(u, "", ""), nil - } - i, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return nil, err - } - return ast.NewValueExpr(i, "", ""), nil -} - -// GeneratePartDefsFromInterval generates range partitions from INTERVAL partitioning. -// Handles -// - CREATE TABLE: all partitions are generated -// - ALTER TABLE FIRST PARTITION (expr): Drops all partitions before the partition matching the expr (i.e. sets that partition as the new first partition) -// i.e. will return the partitions from old FIRST partition to (and including) new FIRST partition -// - ALTER TABLE LAST PARTITION (expr): Creates new partitions from (excluding) old LAST partition to (including) new LAST partition -// -// partition definitions will be set on partitionOptions -func GeneratePartDefsFromInterval(ctx sessionctx.Context, tp ast.AlterTableType, tbInfo *model.TableInfo, partitionOptions *ast.PartitionOptions) error { - if partitionOptions == nil { - return nil - } - var sb strings.Builder - err := partitionOptions.Interval.IntervalExpr.Expr.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) - if err != nil { - return err - } - intervalString := driver.UnwrapFromSingleQuotes(sb.String()) - if len(intervalString) < 1 || intervalString[:1] < "1" || intervalString[:1] > "9" { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL, should be a positive number") - } - var currVal types.Datum - var startExpr, lastExpr, currExpr ast.ExprNode - var timeUnit ast.TimeUnitType - var partCol *model.ColumnInfo - if len(tbInfo.Partition.Columns) == 1 { - partCol = findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) - if partCol == nil { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL COLUMNS partitioning: could not find partitioning column") - } - } - timeUnit = partitionOptions.Interval.IntervalExpr.TimeUnit - switch tp { - case ast.AlterTablePartition: - // CREATE TABLE - startExpr = *partitionOptions.Interval.FirstRangeEnd - lastExpr = *partitionOptions.Interval.LastRangeEnd - case ast.AlterTableDropFirstPartition: - startExpr = *partitionOptions.Interval.FirstRangeEnd - lastExpr = partitionOptions.Expr - case ast.AlterTableAddLastPartition: - startExpr = *partitionOptions.Interval.LastRangeEnd - lastExpr = partitionOptions.Expr - default: - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: Internal error during generating altered INTERVAL partitions, no known alter type") - } - lastVal, err := expression.EvalAstExpr(ctx, lastExpr) - if err != nil { - return err - } - var partDefs []*ast.PartitionDefinition - if len(partitionOptions.Definitions) != 0 { - partDefs = partitionOptions.Definitions - } else { - partDefs = make([]*ast.PartitionDefinition, 0, 1) - } - for i := 0; i < mysql.PartitionCountLimit; i++ { - if i == 0 { - currExpr = startExpr - // TODO: adjust the startExpr and have an offset for interval to handle - // Month/Quarters with start partition on day 28/29/30 - if tp == ast.AlterTableAddLastPartition { - // ALTER TABLE LAST PARTITION ... - // Current LAST PARTITION/start already exists, skip to next partition - continue - } - } else { - currExpr = &ast.BinaryOperationExpr{ - Op: opcode.Mul, - L: ast.NewValueExpr(i, "", ""), - R: partitionOptions.Interval.IntervalExpr.Expr, - } - if timeUnit == ast.TimeUnitInvalid { - currExpr = &ast.BinaryOperationExpr{ - Op: opcode.Plus, - L: startExpr, - R: currExpr, - } - } else { - currExpr = &ast.FuncCallExpr{ - FnName: model.NewCIStr("DATE_ADD"), - Args: []ast.ExprNode{ - startExpr, - currExpr, - &ast.TimeUnitExpr{Unit: timeUnit}, - }, - } - } - } - currVal, err = expression.EvalAstExpr(ctx, currExpr) - if err != nil { - return err - } - cmp, err := currVal.Compare(ctx.GetSessionVars().StmtCtx, &lastVal, collate.GetBinaryCollator()) - if err != nil { - return err - } - if cmp > 0 { - lastStr, err := lastVal.ToString() - if err != nil { - return err - } - sb.Reset() - err = startExpr.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) - if err != nil { - return err - } - startStr := sb.String() - errStr := fmt.Sprintf("INTERVAL: expr (%s) not matching FIRST + n INTERVALs (%s + n * %s", - lastStr, startStr, intervalString) - if timeUnit != ast.TimeUnitInvalid { - errStr = errStr + " " + timeUnit.String() - } - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(errStr + ")") - } - valStr, err := currVal.ToString() - if err != nil { - return err - } - if len(valStr) == 0 || valStr[0:1] == "'" { - return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: Error when generating partition values") - } - partName := "P_LT_" + valStr - if timeUnit != ast.TimeUnitInvalid { - currExpr = ast.NewValueExpr(valStr, "", "") - } else { - if valStr[:1] == "-" { - currExpr = ast.NewValueExpr(currVal.GetInt64(), "", "") - } else { - currExpr = ast.NewValueExpr(currVal.GetUint64(), "", "") - } - } - partDefs = append(partDefs, &ast.PartitionDefinition{ - Name: model.NewCIStr(partName), - Clause: &ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{currExpr}, - }, - }) - if cmp == 0 { - // Last partition! - break - } - } - if len(tbInfo.Partition.Definitions)+len(partDefs) > mysql.PartitionCountLimit { - return errors.Trace(dbterror.ErrTooManyPartitions) - } - partitionOptions.Definitions = partDefs - return nil -} - -// buildPartitionDefinitionsInfo build partition definitions info without assign partition id. tbInfo will be constant -func buildPartitionDefinitionsInfo(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo, numParts uint64) (partitions []model.PartitionDefinition, err error) { - switch tbInfo.Partition.Type { - case model.PartitionTypeNone: - if len(defs) != 1 { - return nil, dbterror.ErrUnsupportedPartitionType - } - partitions = []model.PartitionDefinition{{Name: defs[0].Name}} - if comment, set := defs[0].Comment(); set { - partitions[0].Comment = comment - } - case model.PartitionTypeRange: - partitions, err = buildRangePartitionDefinitions(ctx, defs, tbInfo) - case model.PartitionTypeHash, model.PartitionTypeKey: - partitions, err = buildHashPartitionDefinitions(ctx, defs, tbInfo, numParts) - case model.PartitionTypeList: - partitions, err = buildListPartitionDefinitions(ctx, defs, tbInfo) - default: - err = dbterror.ErrUnsupportedPartitionType - } - - if err != nil { - return nil, err - } - - return partitions, nil -} - -func setPartitionPlacementFromOptions(partition *model.PartitionDefinition, options []*ast.TableOption) error { - // the partition inheritance of placement rules don't have to copy the placement elements to themselves. - // For example: - // t placement policy x (p1 placement policy y, p2) - // p2 will share the same rule as table t does, but it won't copy the meta to itself. we will - // append p2 range to the coverage of table t's rules. This mechanism is good for cascading change - // when policy x is altered. - for _, opt := range options { - if opt.Tp == ast.TableOptionPlacementPolicy { - partition.PlacementPolicyRef = &model.PolicyRefInfo{ - Name: model.NewCIStr(opt.StrValue), - } - } - } - - return nil -} - -func isNonDefaultPartitionOptionsUsed(defs []model.PartitionDefinition) bool { - for i := range defs { - orgDef := defs[i] - if orgDef.Name.O != fmt.Sprintf("p%d", i) { - return true - } - if len(orgDef.Comment) > 0 { - return true - } - if orgDef.PlacementPolicyRef != nil { - return true - } - } - return false -} - -func buildHashPartitionDefinitions(_ sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo, numParts uint64) ([]model.PartitionDefinition, error) { - if err := checkAddPartitionTooManyPartitions(tbInfo.Partition.Num); err != nil { - return nil, err - } - - definitions := make([]model.PartitionDefinition, numParts) - oldParts := uint64(len(tbInfo.Partition.Definitions)) - for i := uint64(0); i < numParts; i++ { - if i < oldParts { - // Use the existing definitions - def := tbInfo.Partition.Definitions[i] - definitions[i].Name = def.Name - definitions[i].Comment = def.Comment - definitions[i].PlacementPolicyRef = def.PlacementPolicyRef - } else if i < oldParts+uint64(len(defs)) { - // Use the new defs - def := defs[i-oldParts] - definitions[i].Name = def.Name - definitions[i].Comment, _ = def.Comment() - if err := setPartitionPlacementFromOptions(&definitions[i], def.Options); err != nil { - return nil, err - } - } else { - // Use the default - definitions[i].Name = model.NewCIStr(fmt.Sprintf("p%d", i)) - } - } - return definitions, nil -} - -func buildListPartitionDefinitions(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo) ([]model.PartitionDefinition, error) { - definitions := make([]model.PartitionDefinition, 0, len(defs)) - exprChecker := newPartitionExprChecker(ctx, nil, checkPartitionExprAllowed) - colTypes := collectColumnsType(tbInfo) - if len(colTypes) != len(tbInfo.Partition.Columns) { - return nil, dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") - } - for _, def := range defs { - if err := def.Clause.Validate(model.PartitionTypeList, len(tbInfo.Partition.Columns)); err != nil { - return nil, err - } - clause := def.Clause.(*ast.PartitionDefinitionClauseIn) - if len(tbInfo.Partition.Columns) > 0 { - for _, vs := range clause.Values { - // TODO: use the generated strings / normalized partition values - _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, vs) - if err != nil { - return nil, err - } - } - } else { - for _, vs := range clause.Values { - if err := checkPartitionValuesIsInt(ctx, def.Name, vs, tbInfo); err != nil { - return nil, err - } - } - } - comment, _ := def.Comment() - err := checkTooLongTable(def.Name) - if err != nil { - return nil, err - } - piDef := model.PartitionDefinition{ - Name: def.Name, - Comment: comment, - } - - if err = setPartitionPlacementFromOptions(&piDef, def.Options); err != nil { - return nil, err - } - - buf := new(bytes.Buffer) - for _, vs := range clause.Values { - inValue := make([]string, 0, len(vs)) - for i := range vs { - vs[i].Accept(exprChecker) - if exprChecker.err != nil { - return nil, exprChecker.err - } - buf.Reset() - vs[i].Format(buf) - inValue = append(inValue, buf.String()) - } - piDef.InValues = append(piDef.InValues, inValue) - buf.Reset() - } - definitions = append(definitions, piDef) - } - return definitions, nil -} - -func collectColumnsType(tbInfo *model.TableInfo) []types.FieldType { - if len(tbInfo.Partition.Columns) > 0 { - colTypes := make([]types.FieldType, 0, len(tbInfo.Partition.Columns)) - for _, col := range tbInfo.Partition.Columns { - c := findColumnByName(col.L, tbInfo) - if c == nil { - return nil - } - colTypes = append(colTypes, c.FieldType) - } - - return colTypes - } - - return nil -} - -func buildRangePartitionDefinitions(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo) ([]model.PartitionDefinition, error) { - definitions := make([]model.PartitionDefinition, 0, len(defs)) - exprChecker := newPartitionExprChecker(ctx, nil, checkPartitionExprAllowed) - colTypes := collectColumnsType(tbInfo) - if len(colTypes) != len(tbInfo.Partition.Columns) { - return nil, dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") - } - for _, def := range defs { - if err := def.Clause.Validate(model.PartitionTypeRange, len(tbInfo.Partition.Columns)); err != nil { - return nil, err - } - clause := def.Clause.(*ast.PartitionDefinitionClauseLessThan) - var partValStrings []string - if len(tbInfo.Partition.Columns) > 0 { - var err error - if partValStrings, err = checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, clause.Exprs); err != nil { - return nil, err - } - } else { - if err := checkPartitionValuesIsInt(ctx, def.Name, clause.Exprs, tbInfo); err != nil { - return nil, err - } - } - comment, _ := def.Comment() - comment, err := validateCommentLength(ctx.GetSessionVars(), def.Name.L, &comment, dbterror.ErrTooLongTablePartitionComment) - if err != nil { - return nil, err - } - err = checkTooLongTable(def.Name) - if err != nil { - return nil, err - } - piDef := model.PartitionDefinition{ - Name: def.Name, - Comment: comment, - } - - if err = setPartitionPlacementFromOptions(&piDef, def.Options); err != nil { - return nil, err - } - - buf := new(bytes.Buffer) - // Range columns partitions support multi-column partitions. - for i, expr := range clause.Exprs { - expr.Accept(exprChecker) - if exprChecker.err != nil { - return nil, exprChecker.err - } - // If multi-column use new evaluated+normalized output, instead of just formatted expression - if len(partValStrings) > i && len(colTypes) > 1 { - partVal := partValStrings[i] - switch colTypes[i].EvalType() { - case types.ETInt: - // no wrapping - case types.ETDatetime, types.ETString, types.ETDuration: - if _, ok := clause.Exprs[i].(*ast.MaxValueExpr); !ok { - // Don't wrap MAXVALUE - partVal = driver.WrapInSingleQuotes(partVal) - } - default: - return nil, dbterror.ErrWrongTypeColumnValue.GenWithStackByArgs() - } - piDef.LessThan = append(piDef.LessThan, partVal) - } else { - expr.Format(buf) - piDef.LessThan = append(piDef.LessThan, buf.String()) - buf.Reset() - } - } - definitions = append(definitions, piDef) - } - return definitions, nil -} - -func checkPartitionValuesIsInt(ctx sessionctx.Context, defName interface{}, exprs []ast.ExprNode, tbInfo *model.TableInfo) error { - tp := types.NewFieldType(mysql.TypeLonglong) - if isPartExprUnsigned(tbInfo) { - tp.AddFlag(mysql.UnsignedFlag) - } - for _, exp := range exprs { - if _, ok := exp.(*ast.MaxValueExpr); ok { - continue - } - if d, ok := exp.(*ast.DefaultExpr); ok { - if d.Name != nil { - return dbterror.ErrPartitionConstDomain.GenWithStackByArgs() - } - continue - } - val, err := expression.EvalAstExpr(ctx, exp) - if err != nil { - return err - } - switch val.Kind() { - case types.KindUint64, types.KindNull: - case types.KindInt64: - if mysql.HasUnsignedFlag(tp.GetFlag()) && val.GetInt64() < 0 { - return dbterror.ErrPartitionConstDomain.GenWithStackByArgs() - } - default: - return dbterror.ErrValuesIsNotIntType.GenWithStackByArgs(defName) - } - - _, err = val.ConvertTo(ctx.GetSessionVars().StmtCtx, tp) - if err != nil && !types.ErrOverflow.Equal(err) { - return dbterror.ErrWrongTypeColumnValue.GenWithStackByArgs() - } - } - - return nil -} - -func checkPartitionNameUnique(pi *model.PartitionInfo) error { - newPars := pi.Definitions - partNames := make(map[string]struct{}, len(newPars)) - for _, newPar := range newPars { - if _, ok := partNames[newPar.Name.L]; ok { - return dbterror.ErrSameNamePartition.GenWithStackByArgs(newPar.Name) - } - partNames[newPar.Name.L] = struct{}{} - } - return nil -} - -func checkAddPartitionNameUnique(tbInfo *model.TableInfo, pi *model.PartitionInfo) error { - partNames := make(map[string]struct{}) - if tbInfo.Partition != nil { - oldPars := tbInfo.Partition.Definitions - for _, oldPar := range oldPars { - partNames[oldPar.Name.L] = struct{}{} - } - } - newPars := pi.Definitions - for _, newPar := range newPars { - if _, ok := partNames[newPar.Name.L]; ok { - return dbterror.ErrSameNamePartition.GenWithStackByArgs(newPar.Name) - } - partNames[newPar.Name.L] = struct{}{} - } - return nil -} - -func checkReorgPartitionNames(p *model.PartitionInfo, droppedNames []string, pi *model.PartitionInfo) error { - partNames := make(map[string]struct{}) - oldDefs := p.Definitions - for _, oldDef := range oldDefs { - partNames[oldDef.Name.L] = struct{}{} - } - for _, delName := range droppedNames { - droppedName := strings.ToLower(delName) - if _, ok := partNames[droppedName]; !ok { - return dbterror.ErrSameNamePartition.GenWithStackByArgs(delName) - } - delete(partNames, droppedName) - } - newDefs := pi.Definitions - for _, newDef := range newDefs { - if _, ok := partNames[newDef.Name.L]; ok { - return dbterror.ErrSameNamePartition.GenWithStackByArgs(newDef.Name) - } - partNames[newDef.Name.L] = struct{}{} - } - return nil -} - -func checkAndOverridePartitionID(newTableInfo, oldTableInfo *model.TableInfo) error { - // If any old partitionInfo has lost, that means the partition ID lost too, so did the data, repair failed. - if newTableInfo.Partition == nil { - return nil - } - if oldTableInfo.Partition == nil { - return dbterror.ErrRepairTableFail.GenWithStackByArgs("Old table doesn't have partitions") - } - if newTableInfo.Partition.Type != oldTableInfo.Partition.Type { - return dbterror.ErrRepairTableFail.GenWithStackByArgs("Partition type should be the same") - } - // Check whether partitionType is hash partition. - if newTableInfo.Partition.Type == model.PartitionTypeHash { - if newTableInfo.Partition.Num != oldTableInfo.Partition.Num { - return dbterror.ErrRepairTableFail.GenWithStackByArgs("Hash partition num should be the same") - } - } - for i, newOne := range newTableInfo.Partition.Definitions { - found := false - for _, oldOne := range oldTableInfo.Partition.Definitions { - // Fix issue 17952 which wanna substitute partition range expr. - // So eliminate stringSliceEqual(newOne.LessThan, oldOne.LessThan) here. - if newOne.Name.L == oldOne.Name.L { - newTableInfo.Partition.Definitions[i].ID = oldOne.ID - found = true - break - } - } - if !found { - return dbterror.ErrRepairTableFail.GenWithStackByArgs("Partition " + newOne.Name.L + " has lost") - } - } - return nil -} - -// checkPartitionFuncValid checks partition function validly. -func checkPartitionFuncValid(ctx sessionctx.Context, tblInfo *model.TableInfo, expr ast.ExprNode) error { - if expr == nil { - return nil - } - exprChecker := newPartitionExprChecker(ctx, tblInfo, checkPartitionExprArgs, checkPartitionExprAllowed) - expr.Accept(exprChecker) - if exprChecker.err != nil { - return errors.Trace(exprChecker.err) - } - if len(exprChecker.columns) == 0 { - return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) - } - return nil -} - -// checkResultOK derives from https://github.com/mysql/mysql-server/blob/5.7/sql/item_timefunc -// For partition tables, mysql do not support Constant, random or timezone-dependent expressions -// Based on mysql code to check whether field is valid, every time related type has check_valid_arguments_processor function. -func checkResultOK(ok bool) error { - if !ok { - return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) - } - - return nil -} - -// checkPartitionFuncType checks partition function return type. -func checkPartitionFuncType(ctx sessionctx.Context, expr ast.ExprNode, tblInfo *model.TableInfo) error { - if expr == nil { - return nil - } - - e, err := expression.RewriteSimpleExprWithTableInfo(ctx, tblInfo, expr, false) - if err != nil { - return errors.Trace(err) - } - if e.GetType().EvalType() == types.ETInt { - return nil - } - - if col, ok := expr.(*ast.ColumnNameExpr); ok { - return errors.Trace(dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(col.Name.Name.L)) - } - - return errors.Trace(dbterror.ErrPartitionFuncNotAllowed.GenWithStackByArgs("PARTITION")) -} - -// checkRangePartitionValue checks whether `less than value` is strictly increasing for each partition. -// Side effect: it may simplify the partition range definition from a constant expression to an integer. -func checkRangePartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) error { - pi := tblInfo.Partition - defs := pi.Definitions - if len(defs) == 0 { - return nil - } - - if strings.EqualFold(defs[len(defs)-1].LessThan[0], partitionMaxValue) { - defs = defs[:len(defs)-1] - } - isUnsigned := isPartExprUnsigned(tblInfo) - var prevRangeValue interface{} - for i := 0; i < len(defs); i++ { - if strings.EqualFold(defs[i].LessThan[0], partitionMaxValue) { - return errors.Trace(dbterror.ErrPartitionMaxvalue) - } - - currentRangeValue, fromExpr, err := getRangeValue(ctx, defs[i].LessThan[0], isUnsigned) - if err != nil { - return errors.Trace(err) - } - if fromExpr { - // Constant fold the expression. - defs[i].LessThan[0] = fmt.Sprintf("%d", currentRangeValue) - } - - if i == 0 { - prevRangeValue = currentRangeValue - continue - } - - if isUnsigned { - if currentRangeValue.(uint64) <= prevRangeValue.(uint64) { - return errors.Trace(dbterror.ErrRangeNotIncreasing) - } - } else { - if currentRangeValue.(int64) <= prevRangeValue.(int64) { - return errors.Trace(dbterror.ErrRangeNotIncreasing) - } - } - prevRangeValue = currentRangeValue - } - return nil -} - -func checkListPartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) error { - pi := tblInfo.Partition - if len(pi.Definitions) == 0 { - return ast.ErrPartitionsMustBeDefined.GenWithStackByArgs("LIST") - } - expStr, err := formatListPartitionValue(ctx, tblInfo) - if err != nil { - return errors.Trace(err) - } - - partitionsValuesMap := make(map[string]struct{}) - for _, s := range expStr { - if _, ok := partitionsValuesMap[s]; ok { - return errors.Trace(dbterror.ErrMultipleDefConstInListPart) - } - partitionsValuesMap[s] = struct{}{} - } - - return nil -} - -func formatListPartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) ([]string, error) { - defs := tblInfo.Partition.Definitions - pi := tblInfo.Partition - var colTps []*types.FieldType - cols := make([]*model.ColumnInfo, 0, len(pi.Columns)) - if len(pi.Columns) == 0 { - tp := types.NewFieldType(mysql.TypeLonglong) - if isPartExprUnsigned(tblInfo) { - tp.AddFlag(mysql.UnsignedFlag) - } - colTps = []*types.FieldType{tp} - } else { - colTps = make([]*types.FieldType, 0, len(pi.Columns)) - for _, colName := range pi.Columns { - colInfo := findColumnByName(colName.L, tblInfo) - if colInfo == nil { - return nil, errors.Trace(dbterror.ErrFieldNotFoundPart) - } - colTps = append(colTps, colInfo.FieldType.Clone()) - cols = append(cols, colInfo) - } - } - - haveDefault := false - exprStrs := make([]string, 0) - inValueStrs := make([]string, 0, mathutil.Max(len(pi.Columns), 1)) - for i := range defs { - inValuesLoop: - for j, vs := range defs[i].InValues { - inValueStrs = inValueStrs[:0] - for k, v := range vs { - // if DEFAULT would be given as string, like "DEFAULT", - // it would be stored as "'DEFAULT'", - if strings.EqualFold(v, "DEFAULT") && k == 0 && len(vs) == 1 { - if haveDefault { - return nil, dbterror.ErrMultipleDefConstInListPart - } - haveDefault = true - continue inValuesLoop - } - if strings.EqualFold(v, "MAXVALUE") { - return nil, errors.Trace(dbterror.ErrMaxvalueInValuesIn) - } - expr, err := expression.ParseSimpleExprCastWithTableInfo(ctx, v, &model.TableInfo{}, colTps[k]) - if err != nil { - return nil, errors.Trace(err) - } - eval, err := expr.Eval(chunk.Row{}) - if err != nil { - return nil, errors.Trace(err) - } - s, err := eval.ToString() - if err != nil { - return nil, errors.Trace(err) - } - if eval.IsNull() { - s = "NULL" - } else { - if colTps[k].EvalType() == types.ETInt { - defs[i].InValues[j][k] = s - } - if colTps[k].EvalType() == types.ETString { - s = string(hack.String(collate.GetCollator(cols[k].GetCollate()).Key(s))) - s = driver.WrapInSingleQuotes(s) - } - } - inValueStrs = append(inValueStrs, s) - } - exprStrs = append(exprStrs, strings.Join(inValueStrs, ",")) - } - } - return exprStrs, nil -} - -// getRangeValue gets an integer from the range value string. -// The returned boolean value indicates whether the input string is a constant expression. -func getRangeValue(ctx sessionctx.Context, str string, unsigned bool) (interface{}, bool, error) { - // Unsigned bigint was converted to uint64 handle. - if unsigned { - if value, err := strconv.ParseUint(str, 10, 64); err == nil { - return value, false, nil - } - - e, err1 := expression.ParseSimpleExprWithTableInfo(ctx, str, &model.TableInfo{}) - if err1 != nil { - return 0, false, err1 - } - res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) - if err2 == nil && !isNull { - return uint64(res), true, nil - } - } else { - if value, err := strconv.ParseInt(str, 10, 64); err == nil { - return value, false, nil - } - // The range value maybe not an integer, it could be a constant expression. - // For example, the following two cases are the same: - // PARTITION p0 VALUES LESS THAN (TO_SECONDS('2004-01-01')) - // PARTITION p0 VALUES LESS THAN (63340531200) - e, err1 := expression.ParseSimpleExprWithTableInfo(ctx, str, &model.TableInfo{}) - if err1 != nil { - return 0, false, err1 - } - res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) - if err2 == nil && !isNull { - return res, true, nil - } - } - return 0, false, dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(str) -} - -// CheckDropTablePartition checks if the partition exists and does not allow deleting the last existing partition in the table. -func CheckDropTablePartition(meta *model.TableInfo, partLowerNames []string) error { - pi := meta.Partition - if pi.Type != model.PartitionTypeRange && pi.Type != model.PartitionTypeList { - return dbterror.ErrOnlyOnRangeListPartition.GenWithStackByArgs("DROP") - } - - // To be error compatible with MySQL, we need to do this first! - // see https://github.com/pingcap/tidb/issues/31681#issuecomment-1015536214 - oldDefs := pi.Definitions - if len(oldDefs) <= len(partLowerNames) { - return errors.Trace(dbterror.ErrDropLastPartition) - } - - dupCheck := make(map[string]bool) - for _, pn := range partLowerNames { - found := false - for _, def := range oldDefs { - if def.Name.L == pn { - if _, ok := dupCheck[pn]; ok { - return errors.Trace(dbterror.ErrDropPartitionNonExistent.GenWithStackByArgs("DROP")) - } - dupCheck[pn] = true - found = true - break - } - } - if !found { - return errors.Trace(dbterror.ErrDropPartitionNonExistent.GenWithStackByArgs("DROP")) - } - } - return nil -} - -// updateDroppingPartitionInfo move dropping partitions to DroppingDefinitions, and return partitionIDs -func updateDroppingPartitionInfo(tblInfo *model.TableInfo, partLowerNames []string) []int64 { - oldDefs := tblInfo.Partition.Definitions - newDefs := make([]model.PartitionDefinition, 0, len(oldDefs)-len(partLowerNames)) - droppingDefs := make([]model.PartitionDefinition, 0, len(partLowerNames)) - pids := make([]int64, 0, len(partLowerNames)) - - // consider using a map to probe partLowerNames if too many partLowerNames - for i := range oldDefs { - found := false - for _, partName := range partLowerNames { - if oldDefs[i].Name.L == partName { - found = true - break - } - } - if found { - pids = append(pids, oldDefs[i].ID) - droppingDefs = append(droppingDefs, oldDefs[i]) - } else { - newDefs = append(newDefs, oldDefs[i]) - } - } - - tblInfo.Partition.Definitions = newDefs - tblInfo.Partition.DroppingDefinitions = droppingDefs - return pids -} - -func getPartitionDef(tblInfo *model.TableInfo, partName string) (index int, def *model.PartitionDefinition, _ error) { - defs := tblInfo.Partition.Definitions - for i := 0; i < len(defs); i++ { - if strings.EqualFold(defs[i].Name.L, strings.ToLower(partName)) { - return i, &(defs[i]), nil - } - } - return index, nil, table.ErrUnknownPartition.GenWithStackByArgs(partName, tblInfo.Name.O) -} - -func getPartitionIDsFromDefinitions(defs []model.PartitionDefinition) []int64 { - pids := make([]int64, 0, len(defs)) - for _, def := range defs { - pids = append(pids, def.ID) - } - return pids -} - -func hasGlobalIndex(tblInfo *model.TableInfo) bool { - for _, idxInfo := range tblInfo.Indices { - if idxInfo.Global { - return true - } - } - return false -} - -// getTableInfoWithDroppingPartitions builds oldTableInfo including dropping partitions, only used by onDropTablePartition. -func getTableInfoWithDroppingPartitions(t *model.TableInfo) *model.TableInfo { - p := t.Partition - nt := t.Clone() - np := *p - npd := make([]model.PartitionDefinition, 0, len(p.Definitions)+len(p.DroppingDefinitions)) - npd = append(npd, p.Definitions...) - npd = append(npd, p.DroppingDefinitions...) - np.Definitions = npd - np.DroppingDefinitions = nil - nt.Partition = &np - return nt -} - -// getTableInfoWithOriginalPartitions builds oldTableInfo including truncating partitions, only used by onTruncateTablePartition. -func getTableInfoWithOriginalPartitions(t *model.TableInfo, oldIDs []int64, newIDs []int64) *model.TableInfo { - nt := t.Clone() - np := nt.Partition - - // reconstruct original definitions - for _, oldDef := range np.DroppingDefinitions { - var newID int64 - for i := range newIDs { - if oldDef.ID == oldIDs[i] { - newID = newIDs[i] - break - } - } - for i := range np.Definitions { - newDef := &np.Definitions[i] - if newDef.ID == newID { - newDef.ID = oldDef.ID - break - } - } - } - - np.DroppingDefinitions = nil - np.NewPartitionIDs = nil - return nt -} - -func dropLabelRules(_ *ddlCtx, schemaName, tableName string, partNames []string) error { - deleteRules := make([]string, 0, len(partNames)) - for _, partName := range partNames { - deleteRules = append(deleteRules, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, schemaName, tableName, partName)) - } - // delete batch rules - patch := label.NewRulePatch([]*label.Rule{}, deleteRules) - return infosync.UpdateLabelRules(context.TODO(), patch) -} - -// onDropTablePartition deletes old partition meta. -func (w *worker) onDropTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var partNames []string - partInfo := model.PartitionInfo{} - if err := job.DecodeArgs(&partNames, &partInfo); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - if job.Type == model.ActionAddTablePartition || job.Type == model.ActionReorganizePartition || - job.Type == model.ActionRemovePartitioning || job.Type == model.ActionAlterTablePartitioning { - // It is rollback from reorganize partition, just remove DroppingDefinitions from tableInfo - tblInfo.Partition.DroppingDefinitions = nil - // It is rollbacked from adding table partition, just remove addingDefinitions from tableInfo. - physicalTableIDs, pNames, rollbackBundles := rollbackAddingPartitionInfo(tblInfo) - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), rollbackBundles) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - // TODO: Will this drop LabelRules for existing partitions, if the new partitions have the same name? - err = dropLabelRules(d, job.SchemaName, tblInfo.Name.L, pNames) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the label rules") - } - - if _, err := alterTableLabelRule(job.SchemaName, tblInfo, getIDs([]*model.TableInfo{tblInfo})); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - if partInfo.DDLType != model.PartitionTypeNone { - // Also remove anything with the new table id - physicalTableIDs = append(physicalTableIDs, tblInfo.Partition.NewTableID) - // Reset if it was normal table before - if tblInfo.Partition.Type == model.PartitionTypeNone { - tblInfo.Partition = nil - } else { - tblInfo.Partition.NewTableID = 0 - tblInfo.Partition.DDLExpr = "" - tblInfo.Partition.DDLColumns = nil - tblInfo.Partition.DDLType = model.PartitionTypeNone - } - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) - job.Args = []interface{}{physicalTableIDs} - return ver, nil - } - - var physicalTableIDs []int64 - // In order to skip maintaining the state check in partitionDefinition, TiDB use droppingDefinition instead of state field. - // So here using `job.SchemaState` to judge what the stage of this job is. - originalState := job.SchemaState - switch job.SchemaState { - case model.StatePublic: - // If an error occurs, it returns that it cannot delete all partitions or that the partition doesn't exist. - err = CheckDropTablePartition(tblInfo, partNames) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - physicalTableIDs = updateDroppingPartitionInfo(tblInfo, partNames) - err = dropLabelRules(d, job.SchemaName, tblInfo.Name.L, partNames) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the label rules") - } - - if _, err := alterTableLabelRule(job.SchemaName, tblInfo, getIDs([]*model.TableInfo{tblInfo})); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - var bundles []*placement.Bundle - // create placement groups for each dropped partition to keep the data's placement before GC - // These placements groups will be deleted after GC - bundles, err = droppedPartitionBundles(t, tblInfo, tblInfo.Partition.DroppingDefinitions) - if err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - var tableBundle *placement.Bundle - // Recompute table bundle to remove dropped partitions rules from its group - tableBundle, err = placement.NewTableBundle(t, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if tableBundle != nil { - bundles = append(bundles, tableBundle) - } - - if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - job.SchemaState = model.StateDeleteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != job.SchemaState) - case model.StateDeleteOnly: - // This state is not a real 'DeleteOnly' state, because tidb does not maintaining the state check in partitionDefinition. - // Insert this state to confirm all servers can not see the old partitions when reorg is running, - // so that no new data will be inserted into old partitions when reorganizing. - job.SchemaState = model.StateDeleteReorganization - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != job.SchemaState) - case model.StateDeleteReorganization: - oldTblInfo := getTableInfoWithDroppingPartitions(tblInfo) - physicalTableIDs = getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) - tbl, err := getTable(d.store, job.SchemaID, oldTblInfo) - if err != nil { - return ver, errors.Trace(err) - } - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - // If table has global indexes, we need reorg to clean up them. - if pt, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { - // Build elements for compatible with modify column type. elements will not be used when reorganizing. - elements := make([]*meta.Element, 0, len(tblInfo.Indices)) - for _, idxInfo := range tblInfo.Indices { - if idxInfo.Global { - elements = append(elements, &meta.Element{ID: idxInfo.ID, TypeKey: meta.IndexElementKey}) - } - } - sctx, err1 := w.sessPool.Get() - if err1 != nil { - return ver, err1 - } - defer w.sessPool.Put(sctx) - rh := newReorgHandler(sess.NewSession(sctx)) - reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, pt, physicalTableIDs, elements) - - if err != nil || reorgInfo.first { - // If we run reorg firstly, we should update the job snapshot version - // and then run the reorg next time. - return ver, errors.Trace(err) - } - err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (dropIndexErr error) { - defer tidbutil.Recover(metrics.LabelDDL, "onDropTablePartition", - func() { - dropIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("drop partition panic") - }, false) - return w.cleanupGlobalIndexes(pt, physicalTableIDs, reorgInfo) - }) - if err != nil { - if dbterror.ErrWaitReorgTimeout.Equal(err) { - // if timeout, we should return, check for the owner and re-wait job done. - return ver, nil - } - if dbterror.ErrPausedDDLJob.Equal(err) { - // if ErrPausedDDLJob, we should return, check for the owner and re-wait job done. - return ver, nil - } - return ver, errors.Trace(err) - } - } - if tblInfo.TiFlashReplica != nil { - removeTiFlashAvailablePartitionIDs(tblInfo, physicalTableIDs) - } - droppedDefs := tblInfo.Partition.DroppingDefinitions - tblInfo.Partition.DroppingDefinitions = nil - // used by ApplyDiff in updateSchemaVersion - job.CtxVars = []interface{}{physicalTableIDs} - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateNone - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: droppedDefs}}) - // A background job will be created to delete old partition data. - job.Args = []interface{}{physicalTableIDs} - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) - } - return ver, errors.Trace(err) -} - -func removeTiFlashAvailablePartitionIDs(tblInfo *model.TableInfo, pids []int64) { - // Remove the partitions - ids := tblInfo.TiFlashReplica.AvailablePartitionIDs - // Rarely called, so OK to take some time, to make it easy - for _, id := range pids { - for i, avail := range ids { - if id == avail { - tmp := ids[:i] - tmp = append(tmp, ids[i+1:]...) - ids = tmp - break - } - } - } - tblInfo.TiFlashReplica.AvailablePartitionIDs = ids -} - -// onTruncateTablePartition truncates old partition meta. -func (w *worker) onTruncateTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { - var ver int64 - var oldIDs, newIDs []int64 - if err := job.DecodeArgs(&oldIDs, &newIDs); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - if len(oldIDs) != len(newIDs) { - job.State = model.JobStateCancelled - return ver, errors.Trace(errors.New("len(oldIDs) must be the same as len(newIDs)")) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - pi := tblInfo.GetPartitionInfo() - if pi == nil { - return ver, errors.Trace(dbterror.ErrPartitionMgmtOnNonpartitioned) - } - - if !hasGlobalIndex(tblInfo) { - oldPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) - newPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) - for k, oldID := range oldIDs { - for i := 0; i < len(pi.Definitions); i++ { - def := &pi.Definitions[i] - if def.ID == oldID { - oldPartitions = append(oldPartitions, def.Clone()) - def.ID = newIDs[k] - // Shallow copy only use the def.ID in event handle. - newPartitions = append(newPartitions, *def) - break - } - } - } - if len(newPartitions) == 0 { - job.State = model.JobStateCancelled - return ver, table.ErrUnknownPartition.GenWithStackByArgs(fmt.Sprintf("pid:%v", oldIDs), tblInfo.Name.O) - } - - if err = clearTruncatePartitionTiflashStatus(tblInfo, newPartitions, oldIDs); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - if err = updateTruncatePartitionLabelRules(job, t, oldPartitions, newPartitions, tblInfo, oldIDs); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - preSplitAndScatter(w.sess.Context, d.store, tblInfo, newPartitions) - - job.CtxVars = []interface{}{oldIDs, newIDs} - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: newPartitions}}) - // A background job will be created to delete old partition data. - job.Args = []interface{}{oldIDs} - - return ver, err - } - - // When table has global index, public->deleteOnly->deleteReorg->none schema changes should be handled. - switch job.SchemaState { - case model.StatePublic: - // Step1: generate new partition ids - truncatingDefinitions := make([]model.PartitionDefinition, 0, len(oldIDs)) - for i, oldID := range oldIDs { - for j := 0; j < len(pi.Definitions); j++ { - def := &pi.Definitions[j] - if def.ID == oldID { - truncatingDefinitions = append(truncatingDefinitions, def.Clone()) - def.ID = newIDs[i] - break - } - } - } - pi.DroppingDefinitions = truncatingDefinitions - pi.NewPartitionIDs = newIDs[:] - - job.SchemaState = model.StateDeleteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - case model.StateDeleteOnly: - // This state is not a real 'DeleteOnly' state, because tidb does not maintaining the state check in partitionDefinition. - // Insert this state to confirm all servers can not see the old partitions when reorg is running, - // so that no new data will be inserted into old partitions when reorganizing. - job.SchemaState = model.StateDeleteReorganization - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - case model.StateDeleteReorganization: - // Step2: clear global index rows. - physicalTableIDs := oldIDs - oldTblInfo := getTableInfoWithOriginalPartitions(tblInfo, oldIDs, newIDs) - - tbl, err := getTable(d.store, job.SchemaID, oldTblInfo) - if err != nil { - return ver, errors.Trace(err) - } - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - // If table has global indexes, we need reorg to clean up them. - if pt, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { - // Build elements for compatible with modify column type. elements will not be used when reorganizing. - elements := make([]*meta.Element, 0, len(tblInfo.Indices)) - for _, idxInfo := range tblInfo.Indices { - if idxInfo.Global { - elements = append(elements, &meta.Element{ID: idxInfo.ID, TypeKey: meta.IndexElementKey}) - } - } - sctx, err1 := w.sessPool.Get() - if err1 != nil { - return ver, err1 - } - defer w.sessPool.Put(sctx) - rh := newReorgHandler(sess.NewSession(sctx)) - reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, pt, physicalTableIDs, elements) - - if err != nil || reorgInfo.first { - // If we run reorg firstly, we should update the job snapshot version - // and then run the reorg next time. - return ver, errors.Trace(err) - } - err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (dropIndexErr error) { - defer tidbutil.Recover(metrics.LabelDDL, "onDropTablePartition", - func() { - dropIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("drop partition panic") - }, false) - return w.cleanupGlobalIndexes(pt, physicalTableIDs, reorgInfo) - }) - if err != nil { - if dbterror.ErrWaitReorgTimeout.Equal(err) { - // if timeout, we should return, check for the owner and re-wait job done. - return ver, nil - } - return ver, errors.Trace(err) - } - } - - // Step3: generate new partition ids and finish rest works - oldPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) - newPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) - for _, oldDef := range pi.DroppingDefinitions { - var newID int64 - for i := range oldIDs { - if oldDef.ID == oldIDs[i] { - newID = newIDs[i] - break - } - } - for i := 0; i < len(pi.Definitions); i++ { - def := &pi.Definitions[i] - if newID == def.ID { - oldPartitions = append(oldPartitions, oldDef.Clone()) - newPartitions = append(newPartitions, def.Clone()) - break - } - } - } - if len(newPartitions) == 0 { - job.State = model.JobStateCancelled - return ver, table.ErrUnknownPartition.GenWithStackByArgs(fmt.Sprintf("pid:%v", oldIDs), tblInfo.Name.O) - } - - if err = clearTruncatePartitionTiflashStatus(tblInfo, newPartitions, oldIDs); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - if err = updateTruncatePartitionLabelRules(job, t, oldPartitions, newPartitions, tblInfo, oldIDs); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - // Step4: clear DroppingDefinitions and finish job. - tblInfo.Partition.DroppingDefinitions = nil - tblInfo.Partition.NewPartitionIDs = nil - - preSplitAndScatter(w.sess.Context, d.store, tblInfo, newPartitions) - - // used by ApplyDiff in updateSchemaVersion - job.CtxVars = []interface{}{oldIDs, newIDs} - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: newPartitions}}) - // A background job will be created to delete old partition data. - job.Args = []interface{}{oldIDs} - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) - } - - return ver, errors.Trace(err) -} - -func clearTruncatePartitionTiflashStatus(tblInfo *model.TableInfo, newPartitions []model.PartitionDefinition, oldIDs []int64) error { - // Clear the tiflash replica available status. - if tblInfo.TiFlashReplica != nil { - e := infosync.ConfigureTiFlashPDForPartitions(true, &newPartitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID) - failpoint.Inject("FailTiFlashTruncatePartition", func() { - e = errors.New("enforced error") - }) - if e != nil { - logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(e)) - return e - } - tblInfo.TiFlashReplica.Available = false - // Set partition replica become unavailable. - removeTiFlashAvailablePartitionIDs(tblInfo, oldIDs) - } - return nil -} - -func updateTruncatePartitionLabelRules(job *model.Job, t *meta.Meta, oldPartitions, newPartitions []model.PartitionDefinition, tblInfo *model.TableInfo, oldIDs []int64) error { - bundles, err := placement.NewPartitionListBundles(t, newPartitions) - if err != nil { - return errors.Trace(err) - } - - tableBundle, err := placement.NewTableBundle(t, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - - if tableBundle != nil { - bundles = append(bundles, tableBundle) - } - - // create placement groups for each dropped partition to keep the data's placement before GC - // These placements groups will be deleted after GC - keepDroppedBundles, err := droppedPartitionBundles(t, tblInfo, oldPartitions) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - bundles = append(bundles, keepDroppedBundles...) - - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) - if err != nil { - return errors.Wrapf(err, "failed to notify PD the placement rules") - } - - tableID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L) - oldPartRules := make([]string, 0, len(oldIDs)) - for _, newPartition := range newPartitions { - oldPartRuleID := fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L, newPartition.Name.L) - oldPartRules = append(oldPartRules, oldPartRuleID) - } - - rules, err := infosync.GetLabelRules(context.TODO(), append(oldPartRules, tableID)) - if err != nil { - return errors.Wrapf(err, "failed to get label rules from PD") - } - - newPartIDs := getPartitionIDs(tblInfo) - newRules := make([]*label.Rule, 0, len(oldIDs)+1) - if tr, ok := rules[tableID]; ok { - newRules = append(newRules, tr.Clone().Reset(job.SchemaName, tblInfo.Name.L, "", append(newPartIDs, tblInfo.ID)...)) - } - - for idx, newPartition := range newPartitions { - if pr, ok := rules[oldPartRules[idx]]; ok { - newRules = append(newRules, pr.Clone().Reset(job.SchemaName, tblInfo.Name.L, newPartition.Name.L, newPartition.ID)) - } - } - - patch := label.NewRulePatch(newRules, []string{}) - err = infosync.UpdateLabelRules(context.TODO(), patch) - if err != nil { - return errors.Wrapf(err, "failed to notify PD the label rules") - } - - return nil -} - -// onExchangeTablePartition exchange partition data -func (w *worker) onExchangeTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var ( - // defID only for updateSchemaVersion - defID int64 - ptSchemaID int64 - ptID int64 - partName string - withValidation bool - ) - - if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - ntDbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - ptDbInfo, err := t.GetDatabase(ptSchemaID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - nt, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - if job.IsRollingback() { - return rollbackExchangeTablePartition(d, t, job, nt) - } - pt, err := getTableInfo(t, ptID, ptSchemaID) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableNotExists.Equal(err) { - job.State = model.JobStateCancelled - } - return ver, errors.Trace(err) - } - - _, partDef, err := getPartitionDef(pt, partName) - if err != nil { - return ver, errors.Trace(err) - } - if job.SchemaState == model.StateNone { - if pt.State != model.StatePublic { - job.State = model.JobStateCancelled - return ver, dbterror.ErrInvalidDDLState.GenWithStack("table %s is not in public, but %s", pt.Name, pt.State) - } - err = checkExchangePartition(pt, nt) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkTableDefCompatible(pt, nt) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkExchangePartitionPlacementPolicy(t, nt.PlacementPolicyRef, pt.PlacementPolicyRef, partDef.PlacementPolicyRef) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if defID != partDef.ID { - logutil.BgLogger().Info("Exchange partition id changed, updating to actual id", zap.String("category", "ddl"), - zap.String("job", job.String()), zap.Int64("defID", defID), zap.Int64("partDef.ID", partDef.ID)) - job.Args[0] = partDef.ID - defID = partDef.ID - err = updateDDLJob2Table(w.sess, job, true) - if err != nil { - return ver, errors.Trace(err) - } - } - var ptInfo []schemaIDAndTableInfo - if len(nt.Constraints) > 0 { - pt.ExchangePartitionInfo = &model.ExchangePartitionInfo{ - ExchangePartitionTableID: nt.ID, - ExchangePartitionDefID: defID, - } - ptInfo = append(ptInfo, schemaIDAndTableInfo{ - schemaID: ptSchemaID, - tblInfo: pt, - }) - } - nt.ExchangePartitionInfo = &model.ExchangePartitionInfo{ - ExchangePartitionTableID: ptID, - ExchangePartitionDefID: defID, - } - // We need an interim schema version, - // so there are no non-matching rows inserted - // into the table using the schema version - // before the exchange is made. - job.SchemaState = model.StateWriteOnly - return updateVersionAndTableInfoWithCheck(d, t, job, nt, true, ptInfo...) - } - // From now on, nt (the non-partitioned table) has - // ExchangePartitionInfo set, meaning it is restricted - // to only allow writes that would match the - // partition to be exchange with. - // So we need to rollback that change, instead of just cancelling. - - if d.lease > 0 { - delayForAsyncCommit() - } - - if defID != partDef.ID { - // Should never happen, should have been updated above, in previous state! - logutil.BgLogger().Error("Exchange partition id changed, updating to actual id", zap.String("category", "ddl"), - zap.String("job", job.String()), zap.Int64("defID", defID), zap.Int64("partDef.ID", partDef.ID)) - job.Args[0] = partDef.ID - defID = partDef.ID - err = updateDDLJob2Table(w.sess, job, true) - if err != nil { - return ver, errors.Trace(err) - } - } - - if withValidation { - ntbl, err := getTable(d.store, job.SchemaID, nt) - if err != nil { - return ver, errors.Trace(err) - } - ptbl, err := getTable(d.store, ptSchemaID, pt) - if err != nil { - return ver, errors.Trace(err) - } - err = checkExchangePartitionRecordValidation(w, ptbl, ntbl, ptDbInfo.Name.L, ntDbInfo.Name.L, partName) - if err != nil { - job.State = model.JobStateRollingback - return ver, errors.Trace(err) - } - } - - // partition table auto IDs. - ptAutoIDs, err := t.GetAutoIDAccessors(ptSchemaID, ptID).Get() - if err != nil { - return ver, errors.Trace(err) - } - // non-partition table auto IDs. - ntAutoIDs, err := t.GetAutoIDAccessors(job.SchemaID, nt.ID).Get() - if err != nil { - return ver, errors.Trace(err) - } - - if pt.TiFlashReplica != nil { - for i, id := range pt.TiFlashReplica.AvailablePartitionIDs { - if id == partDef.ID { - pt.TiFlashReplica.AvailablePartitionIDs[i] = nt.ID - break - } - } - } - - // Recreate non-partition table meta info, - // by first delete it with the old table id - err = t.DropTableOrView(job.SchemaID, nt.ID) - if err != nil { - return ver, errors.Trace(err) - } - - // exchange table meta id - pt.ExchangePartitionInfo = nil - partDef.ID, nt.ID = nt.ID, partDef.ID - - err = t.UpdateTable(ptSchemaID, pt) - if err != nil { - return ver, errors.Trace(err) - } - - err = t.CreateTableOrView(job.SchemaID, nt) - if err != nil { - return ver, errors.Trace(err) - } - - failpoint.Inject("exchangePartitionErr", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(ver, errors.New("occur an error after updating partition id")) - } - }) - - // Set both tables to the maximum auto IDs between normal table and partitioned table. - newAutoIDs := meta.AutoIDGroup{ - RowID: mathutil.Max(ptAutoIDs.RowID, ntAutoIDs.RowID), - IncrementID: mathutil.Max(ptAutoIDs.IncrementID, ntAutoIDs.IncrementID), - RandomID: mathutil.Max(ptAutoIDs.RandomID, ntAutoIDs.RandomID), - } - err = t.GetAutoIDAccessors(ptSchemaID, pt.ID).Put(newAutoIDs) - if err != nil { - return ver, errors.Trace(err) - } - err = t.GetAutoIDAccessors(job.SchemaID, nt.ID).Put(newAutoIDs) - if err != nil { - return ver, errors.Trace(err) - } - - failpoint.Inject("exchangePartitionAutoID", func(val failpoint.Value) { - if val.(bool) { - seCtx, err := w.sessPool.Get() - defer w.sessPool.Put(seCtx) - if err != nil { - failpoint.Return(ver, err) - } - se := sess.NewSession(seCtx) - _, err = se.Execute(context.Background(), "insert ignore into test.pt values (40000000)", "exchange_partition_test") - if err != nil { - failpoint.Return(ver, err) - } - } - }) - - // the follow code is a swap function for rules of two partitions - // though partitions has exchanged their ID, swap still take effect - - bundles, err := bundlesForExchangeTablePartition(t, pt, partDef, nt) - if err != nil { - return ver, errors.Trace(err) - } - - if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - ntrID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, nt.Name.L) - ptrID := fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, job.SchemaName, pt.Name.L, partDef.Name.L) - - rules, err := infosync.GetLabelRules(context.TODO(), []string{ntrID, ptrID}) - if err != nil { - return 0, errors.Wrapf(err, "failed to get PD the label rules") - } - - ntr := rules[ntrID] - ptr := rules[ptrID] - - // This must be a bug, nt cannot be partitioned! - partIDs := getPartitionIDs(nt) - - var setRules []*label.Rule - var deleteRules []string - if ntr != nil && ptr != nil { - setRules = append(setRules, ntr.Clone().Reset(job.SchemaName, pt.Name.L, partDef.Name.L, partDef.ID)) - setRules = append(setRules, ptr.Clone().Reset(job.SchemaName, nt.Name.L, "", append(partIDs, nt.ID)...)) - } else if ptr != nil { - setRules = append(setRules, ptr.Clone().Reset(job.SchemaName, nt.Name.L, "", append(partIDs, nt.ID)...)) - // delete ptr - deleteRules = append(deleteRules, ptrID) - } else if ntr != nil { - setRules = append(setRules, ntr.Clone().Reset(job.SchemaName, pt.Name.L, partDef.Name.L, partDef.ID)) - // delete ntr - deleteRules = append(deleteRules, ntrID) - } - - patch := label.NewRulePatch(setRules, deleteRules) - err = infosync.UpdateLabelRules(context.TODO(), patch) - if err != nil { - return ver, errors.Wrapf(err, "failed to notify PD the label rules") - } - - job.SchemaState = model.StatePublic - nt.ExchangePartitionInfo = nil - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, nt, true) - if err != nil { - return ver, errors.Trace(err) - } - - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, pt) - return ver, nil -} - -func getReorgPartitionInfo(t *meta.Meta, job *model.Job) (*model.TableInfo, []string, *model.PartitionInfo, []model.PartitionDefinition, []model.PartitionDefinition, error) { - schemaID := job.SchemaID - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return nil, nil, nil, nil, nil, errors.Trace(err) - } - partInfo := &model.PartitionInfo{} - var partNames []string - err = job.DecodeArgs(&partNames, &partInfo) - if err != nil { - job.State = model.JobStateCancelled - return nil, nil, nil, nil, nil, errors.Trace(err) - } - var addingDefs, droppingDefs []model.PartitionDefinition - if tblInfo.Partition != nil { - addingDefs = tblInfo.Partition.AddingDefinitions - droppingDefs = tblInfo.Partition.DroppingDefinitions - tblInfo.Partition.NewTableID = partInfo.NewTableID - tblInfo.Partition.DDLType = partInfo.Type - tblInfo.Partition.DDLExpr = partInfo.Expr - tblInfo.Partition.DDLColumns = partInfo.Columns - } else { - tblInfo.Partition = getPartitionInfoTypeNone() - tblInfo.Partition.NewTableID = partInfo.NewTableID - tblInfo.Partition.Definitions[0].ID = tblInfo.ID - tblInfo.Partition.DDLType = partInfo.Type - tblInfo.Partition.DDLExpr = partInfo.Expr - tblInfo.Partition.DDLColumns = partInfo.Columns - } - if len(addingDefs) == 0 { - addingDefs = []model.PartitionDefinition{} - } - if len(droppingDefs) == 0 { - droppingDefs = []model.PartitionDefinition{} - } - return tblInfo, partNames, partInfo, droppingDefs, addingDefs, nil -} - -func (w *worker) onReorganizePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - // Handle the rolling back job - if job.IsRollingback() { - ver, err := w.onDropTablePartition(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - return ver, nil - } - - tblInfo, partNames, partInfo, _, addingDefinitions, err := getReorgPartitionInfo(t, job) - if err != nil { - return ver, err - } - - switch job.SchemaState { - case model.StateNone: - // job.SchemaState == model.StateNone means the job is in the initial state of reorg partition. - // Here should use partInfo from job directly and do some check action. - // In case there was a race for queueing different schema changes on the same - // table and the checks was not done on the current schema version. - // The partInfo may have been checked against an older schema version for example. - // If the check is done here, it does not need to be repeated, since no other - // DDL on the same table can be run concurrently. - num := len(partInfo.Definitions) - len(partNames) + len(tblInfo.Partition.Definitions) - err = checkAddPartitionTooManyPartitions(uint64(num)) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkReorgPartitionNames(tblInfo.Partition, partNames, partInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - // Re-check that the dropped/added partitions are compatible with current definition - firstPartIdx, lastPartIdx, idMap, err := getReplacedPartitionIDs(partNames, tblInfo.Partition) - if err != nil { - job.State = model.JobStateCancelled - return ver, err - } - sctx := w.sess.Context - if err = checkReorgPartitionDefs(sctx, job.Type, tblInfo, partInfo, firstPartIdx, lastPartIdx, idMap); err != nil { - job.State = model.JobStateCancelled - return ver, err - } - - // move the adding definition into tableInfo. - updateAddingPartitionInfo(partInfo, tblInfo) - orgDefs := tblInfo.Partition.Definitions - _ = updateDroppingPartitionInfo(tblInfo, partNames) - // Reset original partitions, and keep DroppedDefinitions - tblInfo.Partition.Definitions = orgDefs - - // modify placement settings - for _, def := range tblInfo.Partition.AddingDefinitions { - if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, def.PlacementPolicyRef); err != nil { - // job.State = model.JobStateCancelled may be set depending on error in function above. - return ver, errors.Trace(err) - } - } - - // From now on we cannot just cancel the DDL, we must roll back if changesMade! - changesMade := false - if tblInfo.TiFlashReplica != nil { - // Must set placement rule, and make sure it succeeds. - if err := infosync.ConfigureTiFlashPDForPartitions(true, &tblInfo.Partition.AddingDefinitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); err != nil { - logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - changesMade = true - // In the next step, StateDeleteOnly, wait to verify the TiFlash replicas are OK - } - - bundles, err := alterTablePartitionBundles(t, tblInfo, tblInfo.Partition.AddingDefinitions) - if err != nil { - if !changesMade { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - - if len(bundles) > 0 { - if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { - if !changesMade { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - changesMade = true - } - - ids := getIDs([]*model.TableInfo{tblInfo}) - for _, p := range tblInfo.Partition.AddingDefinitions { - ids = append(ids, p.ID) - } - changed, err := alterTableLabelRule(job.SchemaName, tblInfo, ids) - changesMade = changesMade || changed - if err != nil { - if !changesMade { - job.State = model.JobStateCancelled - return ver, err - } - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - - // Doing the preSplitAndScatter here, since all checks are completed, - // and we will soon start writing to the new partitions. - if s, ok := d.store.(kv.SplittableStore); ok && s != nil { - // partInfo only contains the AddingPartitions - splitPartitionTableRegion(w.sess.Context, s, tblInfo, partInfo.Definitions, true) - } - - // Assume we cannot have more than MaxUint64 rows, set the progress to 1/10 of that. - metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.1 / float64(math.MaxUint64)) - job.SchemaState = model.StateDeleteOnly - tblInfo.Partition.DDLState = model.StateDeleteOnly - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - // Is really both StateDeleteOnly AND StateWriteOnly needed? - // If transaction A in WriteOnly inserts row 1 (into both new and old partition set) - // and then transaction B in DeleteOnly deletes that row (in both new and old) - // does really transaction B need to do the delete in the new partition? - // Yes, otherwise it would still be there when the WriteReorg happens, - // and WriteReorg would only copy existing rows to the new table, so unless it is - // deleted it would result in a ghost row! - // What about update then? - // Updates also need to be handled for new partitions in DeleteOnly, - // since it would not be overwritten during Reorganize phase. - // BUT if the update results in adding in one partition and deleting in another, - // THEN only the delete must happen in the new partition set, not the insert! - case model.StateDeleteOnly: - // This state is to confirm all servers can not see the new partitions when reorg is running, - // so that all deletes will be done in both old and new partitions when in either DeleteOnly - // or WriteOnly state. - // Also using the state for checking that the optional TiFlash replica is available, making it - // in a state without (much) data and easy to retry without side effects. - - // Reason for having it here, is to make it easy for retry, and better to make sure it is in-sync - // as early as possible, to avoid a long wait after the data copying. - if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { - // For available state, the new added partition should wait its replica to - // be finished, otherwise the query to this partition will be blocked. - count := tblInfo.TiFlashReplica.Count - needRetry, err := checkPartitionReplica(count, addingDefinitions, d) - if err != nil { - // need to rollback, since we tried to register the new - // partitions before! - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - if needRetry { - // The new added partition hasn't been replicated. - // Do nothing to the job this time, wait next worker round. - time.Sleep(tiflashCheckTiDBHTTPAPIHalfInterval) - // Set the error here which will lead this job exit when it's retry times beyond the limitation. - return ver, errors.Errorf("[ddl] add partition wait for tiflash replica to complete") - } - - // When TiFlash Replica is ready, we must move them into `AvailablePartitionIDs`. - // Since onUpdateFlashReplicaStatus cannot see the partitions yet (not public) - for _, d := range addingDefinitions { - tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, d.ID) - } - } - - tblInfo.Partition.DDLState = model.StateWriteOnly - metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.2 / float64(math.MaxUint64)) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - // Insert this state to confirm all servers can see the new partitions when reorg is running, - // so that new data will be updated in both old and new partitions when reorganizing. - job.SnapshotVer = 0 - tblInfo.Partition.DDLState = model.StateWriteReorganization - metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.3 / float64(math.MaxUint64)) - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - job.SchemaState = model.StateWriteReorganization - case model.StateWriteReorganization: - physicalTableIDs := getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) - tbl, err2 := getTable(d.store, job.SchemaID, tblInfo) - if err2 != nil { - return ver, errors.Trace(err2) - } - // TODO: If table has global indexes, we need reorg to clean up them. - // and then add the new partition ids back... - if _, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { - err = errors.Trace(dbterror.ErrCancelledDDLJob.GenWithStack("global indexes is not supported yet for reorganize partition")) - return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) - } - var done bool - done, ver, err = doPartitionReorgWork(w, d, t, job, tbl, physicalTableIDs) - - if !done { - return ver, err - } - - firstPartIdx, lastPartIdx, idMap, err2 := getReplacedPartitionIDs(partNames, tblInfo.Partition) - failpoint.Inject("reorgPartWriteReorgReplacedPartIDsFail", func(val failpoint.Value) { - if val.(bool) { - err2 = errors.New("Injected error by reorgPartWriteReorgReplacedPartIDsFail") - } - }) - if err2 != nil { - return ver, err2 - } - newDefs := getReorganizedDefinitions(tblInfo.Partition, firstPartIdx, lastPartIdx, idMap) - - // From now on, use the new partitioning, but keep the Adding and Dropping for double write - tblInfo.Partition.Definitions = newDefs - tblInfo.Partition.Num = uint64(len(newDefs)) - if job.Type == model.ActionAlterTablePartitioning || - job.Type == model.ActionRemovePartitioning { - tblInfo.Partition.Type, tblInfo.Partition.DDLType = tblInfo.Partition.DDLType, tblInfo.Partition.Type - tblInfo.Partition.Expr, tblInfo.Partition.DDLExpr = tblInfo.Partition.DDLExpr, tblInfo.Partition.Expr - tblInfo.Partition.Columns, tblInfo.Partition.DDLColumns = tblInfo.Partition.DDLColumns, tblInfo.Partition.Columns - } - - // Now all the data copying is done, but we cannot simply remove the droppingDefinitions - // since they are a part of the normal Definitions that other nodes with - // the current schema version. So we need to double write for one more schema version - tblInfo.Partition.DDLState = model.StateDeleteReorganization - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - job.SchemaState = model.StateDeleteReorganization - - case model.StateDeleteReorganization: - // Drop the droppingDefinitions and finish the DDL - // This state is needed for the case where client A sees the schema - // with version of StateWriteReorg and would not see updates of - // client B that writes to the new partitions, previously - // addingDefinitions, since it would not double write to - // the droppingDefinitions during this time - // By adding StateDeleteReorg state, client B will write to both - // the new (previously addingDefinitions) AND droppingDefinitions - - // Register the droppingDefinitions ids for rangeDelete - // and the addingDefinitions for handling in the updateSchemaVersion - physicalTableIDs := getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) - newIDs := getPartitionIDsFromDefinitions(partInfo.Definitions) - statisticsPartInfo := &model.PartitionInfo{Definitions: tblInfo.Partition.AddingDefinitions} - - tblInfo.Partition.DroppingDefinitions = nil - tblInfo.Partition.AddingDefinitions = nil - tblInfo.Partition.DDLState = model.StateNone - - if job.Type != model.ActionReorganizePartition { - // ALTER TABLE ... PARTITION BY - // REMOVE PARTITIONING - // New Table ID, so needs to recreate the table by drop+create. - oldTblID := tblInfo.ID - // Overloading the NewTableID here with the oldTblID instead, - // for keeping the old global statistics - statisticsPartInfo.NewTableID = oldTblID - // TODO: Handle bundles? - // TODO: Add concurrent test! - // TODO: Will this result in big gaps? - // TODO: How to carrie over AUTO_INCREMENT etc.? - // Check if they are carried over in ApplyDiff?!? - autoIDs, err := t.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Get() - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - err = t.DropTableOrView(job.SchemaID, tblInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - tblInfo.ID = partInfo.NewTableID - if partInfo.DDLType != model.PartitionTypeNone { - // if partitioned before, then also add the old table ID, - // otherwise it will be the already included first partition - physicalTableIDs = append(physicalTableIDs, oldTblID) - } - if job.Type == model.ActionRemovePartitioning { - tblInfo.Partition = nil - } else { - // ALTER TABLE ... PARTITION BY - tblInfo.Partition.DDLType = model.PartitionTypeNone - tblInfo.Partition.DDLExpr = "" - tblInfo.Partition.DDLColumns = nil - tblInfo.Partition.NewTableID = 0 - } - err = t.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Put(autoIDs) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // TODO: Add failpoint here? - err = t.CreateTableOrView(job.SchemaID, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - job.CtxVars = []interface{}{physicalTableIDs, newIDs} - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - failpoint.Inject("reorgPartWriteReorgSchemaVersionUpdateFail", func(val failpoint.Value) { - if val.(bool) { - err = errors.New("Injected error by reorgPartWriteReorgSchemaVersionUpdateFail") - } - }) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - // How to handle this? - // Seems to only trigger asynchronous update of statistics. - // Should it actually be synchronous? - // Include the old table ID, if changed, which may contain global statistics, - // so it can be reused for the new (non)partitioned table. - asyncNotifyEvent(d, &util.Event{Tp: job.Type, TableInfo: tblInfo, PartInfo: statisticsPartInfo}) - // A background job will be created to delete old partition data. - job.Args = []interface{}{physicalTableIDs} - - default: - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) - } - - return ver, errors.Trace(err) -} - -func doPartitionReorgWork(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, physTblIDs []int64) (done bool, ver int64, err error) { - job.ReorgMeta.ReorgTp = model.ReorgTypeTxn - sctx, err1 := w.sessPool.Get() - if err1 != nil { - return done, ver, err1 - } - defer w.sessPool.Put(sctx) - rh := newReorgHandler(sess.NewSession(sctx)) - elements := BuildElements(tbl.Meta().Columns[0], tbl.Meta().Indices) - partTbl, ok := tbl.(table.PartitionedTable) - if !ok { - return false, ver, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() - } - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return false, ver, errors.Trace(err) - } - reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, partTbl, physTblIDs, elements) - err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (reorgErr error) { - defer tidbutil.Recover(metrics.LabelDDL, "doPartitionReorgWork", - func() { - reorgErr = dbterror.ErrCancelledDDLJob.GenWithStack("reorganize partition for table `%v` panic", tbl.Meta().Name) - }, false) - return w.reorgPartitionDataAndIndex(tbl, reorgInfo) - }) - if err != nil { - if dbterror.ErrPausedDDLJob.Equal(err) { - return false, ver, nil - } - - if dbterror.ErrWaitReorgTimeout.Equal(err) { - // If timeout, we should return, check for the owner and re-wait job done. - return false, ver, nil - } - if kv.IsTxnRetryableError(err) { - return false, ver, errors.Trace(err) - } - if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { - logutil.BgLogger().Warn("reorg partition job failed, RemoveDDLReorgHandle failed, can't convert job to rollback", zap.String("category", "ddl"), - zap.String("job", job.String()), zap.Error(err1)) - } - logutil.BgLogger().Warn("reorg partition job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) - ver, err = convertAddTablePartitionJob2RollbackJob(d, t, job, err, tbl.Meta()) - return false, ver, errors.Trace(err) - } - return true, ver, err -} - -type reorgPartitionWorker struct { - *backfillCtx - // Static allocated to limit memory allocations - rowRecords []*rowRecord - rowDecoder *decoder.RowDecoder - rowMap map[int64]types.Datum - writeColOffsetMap map[int64]int - maxOffset int - reorgedTbl table.PartitionedTable -} - -func newReorgPartitionWorker(sessCtx sessionctx.Context, i int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) (*reorgPartitionWorker, error) { - reorgedTbl, err := tables.GetReorganizedPartitionedTable(t) - if err != nil { - return nil, errors.Trace(err) - } - pt := t.GetPartitionedTable() - if pt == nil { - return nil, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() - } - partColIDs := reorgedTbl.GetPartitionColumnIDs() - writeColOffsetMap := make(map[int64]int, len(partColIDs)) - maxOffset := 0 - for _, id := range partColIDs { - var offset int - for _, col := range pt.Cols() { - if col.ID == id { - offset = col.Offset - break - } - } - writeColOffsetMap[id] = offset - maxOffset = mathutil.Max[int](maxOffset, offset) - } - return &reorgPartitionWorker{ - backfillCtx: newBackfillCtx(reorgInfo.d, i, sessCtx, reorgInfo.SchemaName, t, jc, "reorg_partition_rate", false), - rowDecoder: decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap), - rowMap: make(map[int64]types.Datum, len(decodeColMap)), - writeColOffsetMap: writeColOffsetMap, - maxOffset: maxOffset, - reorgedTbl: reorgedTbl, - }, nil -} - -func (w *reorgPartitionWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { - oprStartTime := time.Now() - ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) - errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - taskCtx.addedCount = 0 - taskCtx.scanCount = 0 - txn.SetOption(kv.Priority, handleRange.priority) - if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { - txn.SetOption(kv.ResourceGroupTagger, tagger) - } - txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) - - rowRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) - if err != nil { - return errors.Trace(err) - } - taskCtx.nextKey = nextKey - taskCtx.done = taskDone - - warningsMap := make(map[errors.ErrorID]*terror.Error) - warningsCountMap := make(map[errors.ErrorID]int64) - for _, prr := range rowRecords { - taskCtx.scanCount++ - - err = txn.Set(prr.key, prr.vals) - if err != nil { - return errors.Trace(err) - } - taskCtx.addedCount++ - if prr.warning != nil { - if _, ok := warningsCountMap[prr.warning.ID()]; ok { - warningsCountMap[prr.warning.ID()]++ - } else { - warningsCountMap[prr.warning.ID()] = 1 - warningsMap[prr.warning.ID()] = prr.warning - } - } - // TODO: Future optimization: also write the indexes here? - // What if the transaction limit is just enough for a single row, without index? - // Hmm, how could that be in the first place? - // For now, implement the batch-txn w.addTableIndex, - // since it already exists and is in use - } - - // Collect the warnings. - taskCtx.warnings, taskCtx.warningsCount = warningsMap, warningsCountMap - - // also add the index entries here? And make sure they are not added somewhere else - - return nil - }) - logSlowOperations(time.Since(oprStartTime), "BackfillData", 3000) - - return -} - -func (w *reorgPartitionWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*rowRecord, kv.Key, bool, error) { - w.rowRecords = w.rowRecords[:0] - startTime := time.Now() - - // taskDone means that the added handle is out of taskRange.endHandle. - taskDone := false - sysTZ := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() - - tmpRow := make([]types.Datum, w.maxOffset+1) - var lastAccessedHandle kv.Key - oprStartTime := startTime - err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, w.table.RecordPrefix(), txn.StartTS(), taskRange.startKey, taskRange.endKey, - func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { - oprEndTime := time.Now() - logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in reorgPartitionWorker fetchRowColVals", 0) - oprStartTime = oprEndTime - - taskDone = recordKey.Cmp(taskRange.endKey) >= 0 - - if taskDone || len(w.rowRecords) >= w.batchCnt { - return false, nil - } - - // TODO: Extend for normal tables - // TODO: Extend for REMOVE PARTITIONING - _, err := w.rowDecoder.DecodeTheExistedColumnMap(w.sessCtx, handle, rawRow, sysTZ, w.rowMap) - if err != nil { - return false, errors.Trace(err) - } - - // Set the partitioning columns and calculate which partition to write to - for colID, offset := range w.writeColOffsetMap { - d, ok := w.rowMap[colID] - if !ok { - return false, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() - } - tmpRow[offset] = d - } - p, err := w.reorgedTbl.GetPartitionByRow(w.sessCtx, tmpRow) - if err != nil { - return false, errors.Trace(err) - } - pid := p.GetPhysicalID() - newKey := tablecodec.EncodeTablePrefix(pid) - newKey = append(newKey, recordKey[len(newKey):]...) - w.rowRecords = append(w.rowRecords, &rowRecord{ - key: newKey, vals: rawRow, - }) - - w.cleanRowMap() - lastAccessedHandle = recordKey - if recordKey.Cmp(taskRange.endKey) == 0 { - taskDone = true - return false, nil - } - return true, nil - }) - - if len(w.rowRecords) == 0 { - taskDone = true - } - - logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) - return w.rowRecords, getNextHandleKey(taskRange, taskDone, lastAccessedHandle), taskDone, errors.Trace(err) -} - -func (w *reorgPartitionWorker) cleanRowMap() { - for id := range w.rowMap { - delete(w.rowMap, id) - } -} - -func (w *reorgPartitionWorker) AddMetricInfo(cnt float64) { - w.metricCounter.Add(cnt) -} - -func (*reorgPartitionWorker) String() string { - return typeReorgPartitionWorker.String() -} - -func (w *reorgPartitionWorker) GetCtx() *backfillCtx { - return w.backfillCtx -} - -func (w *worker) reorgPartitionDataAndIndex(t table.Table, reorgInfo *reorgInfo) error { - // First copy all table data to the new partitions - // from each of the DroppingDefinitions partitions. - // Then create all indexes on the AddingDefinitions partitions - // for each new index, one partition at a time. - - // Copy the data from the DroppingDefinitions to the AddingDefinitions - if bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { - err := w.updatePhysicalTableRow(t, reorgInfo) - if err != nil { - return errors.Trace(err) - } - } - - failpoint.Inject("reorgPartitionAfterDataCopy", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - panic("panic test in reorgPartitionAfterDataCopy") - } - }) - - // Rewrite this to do all indexes at once in addTableIndex - // instead of calling it once per index (meaning reading the table multiple times) - // But for now, try to understand how it works... - firstNewPartitionID := t.Meta().Partition.AddingDefinitions[0].ID - startElementOffset := 0 - //startElementOffsetToResetHandle := -1 - // This backfill job starts with backfilling index data, whose index ID is currElement.ID. - if !bytes.Equal(reorgInfo.currElement.TypeKey, meta.IndexElementKey) { - // First run, have not yet started backfilling index data - // Restart with the first new partition. - // TODO: handle remove partitioning - reorgInfo.PhysicalTableID = firstNewPartitionID - } else { - // The job was interrupted and has been restarted, - // reset and start from where it was done - for i, element := range reorgInfo.elements[1:] { - if reorgInfo.currElement.ID == element.ID { - startElementOffset = i - //startElementOffsetToResetHandle = i - break - } - } - } - - for i := startElementOffset; i < len(reorgInfo.elements[1:]); i++ { - // Now build the indexes in the new partitions - var physTbl table.PhysicalTable - if tbl, ok := t.(table.PartitionedTable); ok { - physTbl = tbl.GetPartition(reorgInfo.PhysicalTableID) - } else if tbl, ok := t.(table.PhysicalTable); ok { - // This may be used when partitioning a non-partitioned table - physTbl = tbl - } - // Get the original start handle and end handle. - currentVer, err := getValidCurrentVersion(reorgInfo.d.store) - if err != nil { - return errors.Trace(err) - } - // TODO: Can we improve this in case of a crash? - // like where the regInfo PhysicalTableID and element is the same, - // and the tableid in the key-prefix regInfo.StartKey and regInfo.EndKey matches with PhysicalTableID - // do not change the reorgInfo start/end key - startHandle, endHandle, err := getTableRange(reorgInfo.NewJobContext(), reorgInfo.d, physTbl, currentVer.Ver, reorgInfo.Job.Priority) - if err != nil { - return errors.Trace(err) - } - - // Always (re)start with the full PhysicalTable range - reorgInfo.StartKey, reorgInfo.EndKey = startHandle, endHandle - - // Update the element in the reorgInfo for updating the reorg meta below. - reorgInfo.currElement = reorgInfo.elements[i+1] - // Write the reorg info to store so the whole reorganize process can recover from panic. - err = reorgInfo.UpdateReorgMeta(reorgInfo.StartKey, w.sessPool) - logutil.BgLogger().Info("update column and indexes", zap.String("category", "ddl"), - zap.Int64("jobID", reorgInfo.Job.ID), - zap.ByteString("elementType", reorgInfo.currElement.TypeKey), - zap.Int64("elementID", reorgInfo.currElement.ID), - zap.Int64("partitionTableId", physTbl.GetPhysicalID()), - zap.String("startHandle", hex.EncodeToString(reorgInfo.StartKey)), - zap.String("endHandle", hex.EncodeToString(reorgInfo.EndKey))) - if err != nil { - return errors.Trace(err) - } - err = w.addTableIndex(t, reorgInfo) - if err != nil { - return errors.Trace(err) - } - reorgInfo.PhysicalTableID = firstNewPartitionID - } - failpoint.Inject("reorgPartitionAfterIndex", func(val failpoint.Value) { - //nolint:forcetypeassert - if val.(bool) { - panic("panic test in reorgPartitionAfterIndex") - } - }) - return nil -} - -func bundlesForExchangeTablePartition(t *meta.Meta, pt *model.TableInfo, newPar *model.PartitionDefinition, nt *model.TableInfo) ([]*placement.Bundle, error) { - bundles := make([]*placement.Bundle, 0, 3) - - ptBundle, err := placement.NewTableBundle(t, pt) - if err != nil { - return nil, errors.Trace(err) - } - if ptBundle != nil { - bundles = append(bundles, ptBundle) - } - - parBundle, err := placement.NewPartitionBundle(t, *newPar) - if err != nil { - return nil, errors.Trace(err) - } - if parBundle != nil { - bundles = append(bundles, parBundle) - } - - ntBundle, err := placement.NewTableBundle(t, nt) - if err != nil { - return nil, errors.Trace(err) - } - if ntBundle != nil { - bundles = append(bundles, ntBundle) - } - - if parBundle == nil && ntBundle != nil { - // newPar.ID is the ID of old table to exchange, so ntBundle != nil means it has some old placement settings. - // We should remove it in this situation - bundles = append(bundles, placement.NewBundle(newPar.ID)) - } - - if parBundle != nil && ntBundle == nil { - // nt.ID is the ID of old partition to exchange, so parBundle != nil means it has some old placement settings. - // We should remove it in this situation - bundles = append(bundles, placement.NewBundle(nt.ID)) - } - - return bundles, nil -} - -func checkExchangePartitionRecordValidation(w *worker, ptbl, ntbl table.Table, pschemaName, nschemaName, partitionName string) error { - verifyFunc := func(sql string, params ...interface{}) error { - var ctx sessionctx.Context - ctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - rows, _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(w.ctx, nil, sql, params...) - if err != nil { - return errors.Trace(err) - } - rowCount := len(rows) - if rowCount != 0 { - return errors.Trace(dbterror.ErrRowDoesNotMatchPartition) - } - // Check warnings! - // Is it possible to check how many rows where checked as well? - return nil - } - genConstraintCondition := func(constraints []*table.Constraint) string { - var buf strings.Builder - buf.WriteString("not (") - for i, cons := range constraints { - if i != 0 { - buf.WriteString(" and ") - } - buf.WriteString(fmt.Sprintf("(%s)", cons.ExprString)) - } - buf.WriteString(")") - return buf.String() - } - type CheckConstraintTable interface { - WritableConstraint() []*table.Constraint - } - - pt := ptbl.Meta() - index, _, err := getPartitionDef(pt, partitionName) - if err != nil { - return errors.Trace(err) - } - - var buf strings.Builder - buf.WriteString("select 1 from %n.%n where ") - paramList := []interface{}{nschemaName, ntbl.Meta().Name.L} - checkNt := true - - pi := pt.Partition - switch pi.Type { - case model.PartitionTypeHash: - if pi.Num == 1 { - checkNt = false - } else { - buf.WriteString("mod(") - buf.WriteString(pi.Expr) - buf.WriteString(", %?) != %?") - paramList = append(paramList, pi.Num, index) - } - case model.PartitionTypeRange: - // Table has only one partition and has the maximum value - if len(pi.Definitions) == 1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { - checkNt = false - } else { - // For range expression and range columns - if len(pi.Columns) == 0 { - conds, params := buildCheckSQLConditionForRangeExprPartition(pi, index) - buf.WriteString(conds) - paramList = append(paramList, params...) - } else { - conds, params := buildCheckSQLConditionForRangeColumnsPartition(pi, index) - buf.WriteString(conds) - paramList = append(paramList, params...) - } - } - case model.PartitionTypeList: - if len(pi.Columns) == 0 { - conds := buildCheckSQLConditionForListPartition(pi, index) - buf.WriteString(conds) - } else { - conds := buildCheckSQLConditionForListColumnsPartition(pi, index) - buf.WriteString(conds) - } - default: - return dbterror.ErrUnsupportedPartitionType.GenWithStackByArgs(pt.Name.O) - } - - if variable.EnableCheckConstraint.Load() { - pcc, ok := ptbl.(CheckConstraintTable) - if !ok { - return errors.Errorf("exchange partition process assert table partition failed") - } - pCons := pcc.WritableConstraint() - if len(pCons) > 0 { - if !checkNt { - checkNt = true - } else { - buf.WriteString(" or ") - } - buf.WriteString(genConstraintCondition(pCons)) - } - } - // Check non-partition table records. - if checkNt { - buf.WriteString(" limit 1") - err = verifyFunc(buf.String(), paramList...) - if err != nil { - return errors.Trace(err) - } - } - - // Check partition table records. - if variable.EnableCheckConstraint.Load() { - ncc, ok := ntbl.(CheckConstraintTable) - if !ok { - return errors.Errorf("exchange partition process assert table partition failed") - } - nCons := ncc.WritableConstraint() - if len(nCons) > 0 { - buf.Reset() - buf.WriteString("select 1 from %n.%n partition(%n) where ") - buf.WriteString(genConstraintCondition(nCons)) - buf.WriteString(" limit 1") - err = verifyFunc(buf.String(), pschemaName, pt.Name.L, partitionName) - if err != nil { - return errors.Trace(err) - } - } - } - return nil -} - -func checkExchangePartitionPlacementPolicy(t *meta.Meta, ntPPRef, ptPPRef, partPPRef *model.PolicyRefInfo) error { - partitionPPRef := partPPRef - if partitionPPRef == nil { - partitionPPRef = ptPPRef - } - - if ntPPRef == nil && partitionPPRef == nil { - return nil - } - if ntPPRef == nil || partitionPPRef == nil { - return dbterror.ErrTablesDifferentMetadata - } - - ptPlacementPolicyInfo, _ := getPolicyInfo(t, partitionPPRef.ID) - ntPlacementPolicyInfo, _ := getPolicyInfo(t, ntPPRef.ID) - if ntPlacementPolicyInfo == nil && ptPlacementPolicyInfo == nil { - return nil - } - if ntPlacementPolicyInfo == nil || ptPlacementPolicyInfo == nil { - return dbterror.ErrTablesDifferentMetadata - } - if ntPlacementPolicyInfo.Name.L != ptPlacementPolicyInfo.Name.L { - return dbterror.ErrTablesDifferentMetadata - } - - return nil -} - -func buildCheckSQLConditionForRangeExprPartition(pi *model.PartitionInfo, index int) (string, []interface{}) { - var buf strings.Builder - paramList := make([]interface{}, 0, 2) - // Since the pi.Expr string may contain the identifier, which couldn't be escaped in our ParseWithParams(...) - // So we write it to the origin sql string here. - if index == 0 { - buf.WriteString(pi.Expr) - buf.WriteString(" >= %?") - paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) - } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { - buf.WriteString(pi.Expr) - buf.WriteString(" < %?") - paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0])) - } else { - buf.WriteString(pi.Expr) - buf.WriteString(" < %? or ") - buf.WriteString(pi.Expr) - buf.WriteString(" >= %?") - paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0]), driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) - } - return buf.String(), paramList -} - -func buildCheckSQLConditionForRangeColumnsPartition(pi *model.PartitionInfo, index int) (string, []interface{}) { - paramList := make([]interface{}, 0, 2) - colName := pi.Columns[0].L - if index == 0 { - paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) - return "%n >= %?", paramList - } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { - paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0])) - return "%n < %?", paramList - } else { - paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0]), colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) - return "%n < %? or %n >= %?", paramList - } -} - -func buildCheckSQLConditionForListPartition(pi *model.PartitionInfo, index int) string { - var buf strings.Builder - buf.WriteString("not (") - for i, inValue := range pi.Definitions[index].InValues { - if i != 0 { - buf.WriteString(" OR ") - } - // AND has higher priority than OR, so no need for parentheses - for j, val := range inValue { - if j != 0 { - // Should never happen, since there should be no multi-columns, only a single expression :) - buf.WriteString(" AND ") - } - // null-safe compare '<=>' - buf.WriteString(fmt.Sprintf("(%s) <=> %s", pi.Expr, val)) - } - } - buf.WriteString(")") - return buf.String() -} - -func buildCheckSQLConditionForListColumnsPartition(pi *model.PartitionInfo, index int) string { - var buf strings.Builder - // How to find a match? - // (row <=> vals1) OR (row <=> vals2) - // How to find a non-matching row: - // NOT ( (row <=> vals1) OR (row <=> vals2) ... ) - buf.WriteString("not (") - colNames := make([]string, 0, len(pi.Columns)) - for i := range pi.Columns { - // TODO: check if there are no proper quoting function for this? - n := "`" + strings.ReplaceAll(pi.Columns[i].O, "`", "``") + "`" - colNames = append(colNames, n) - } - for i, colValues := range pi.Definitions[index].InValues { - if i != 0 { - buf.WriteString(" OR ") - } - // AND has higher priority than OR, so no need for parentheses - for j, val := range colValues { - if j != 0 { - buf.WriteString(" AND ") - } - // null-safe compare '<=>' - buf.WriteString(fmt.Sprintf("%s <=> %s", colNames[j], val)) - } - } - buf.WriteString(")") - return buf.String() -} - -func checkAddPartitionTooManyPartitions(piDefs uint64) error { - if piDefs > uint64(mysql.PartitionCountLimit) { - return errors.Trace(dbterror.ErrTooManyPartitions) - } - return nil -} - -func checkAddPartitionOnTemporaryMode(tbInfo *model.TableInfo) error { - if tbInfo.Partition != nil && tbInfo.TempTableType != model.TempTableNone { - return dbterror.ErrPartitionNoTemporary - } - return nil -} - -func checkPartitionColumnsUnique(tbInfo *model.TableInfo) error { - if len(tbInfo.Partition.Columns) <= 1 { - return nil - } - var columnsMap = make(map[string]struct{}) - for _, col := range tbInfo.Partition.Columns { - if _, ok := columnsMap[col.L]; ok { - return dbterror.ErrSameNamePartitionField.GenWithStackByArgs(col.L) - } - columnsMap[col.L] = struct{}{} - } - return nil -} - -func checkNoHashPartitions(_ sessionctx.Context, partitionNum uint64) error { - if partitionNum == 0 { - return ast.ErrNoParts.GenWithStackByArgs("partitions") - } - return nil -} - -func getPartitionIDs(table *model.TableInfo) []int64 { - if table.GetPartitionInfo() == nil { - return []int64{} - } - physicalTableIDs := make([]int64, 0, len(table.Partition.Definitions)) - for _, def := range table.Partition.Definitions { - physicalTableIDs = append(physicalTableIDs, def.ID) - } - return physicalTableIDs -} - -func getPartitionRuleIDs(dbName string, table *model.TableInfo) []string { - if table.GetPartitionInfo() == nil { - return []string{} - } - partRuleIDs := make([]string, 0, len(table.Partition.Definitions)) - for _, def := range table.Partition.Definitions { - partRuleIDs = append(partRuleIDs, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, dbName, table.Name.L, def.Name.L)) - } - return partRuleIDs -} - -// checkPartitioningKeysConstraints checks that the range partitioning key is included in the table constraint. -func checkPartitioningKeysConstraints(sctx sessionctx.Context, s *ast.CreateTableStmt, tblInfo *model.TableInfo) error { - // Returns directly if there are no unique keys in the table. - if len(tblInfo.Indices) == 0 && !tblInfo.PKIsHandle { - return nil - } - - partCols, err := getPartitionColSlices(sctx, tblInfo, s.Partition) - if err != nil { - return errors.Trace(err) - } - - // Checks that the partitioning key is included in the constraint. - // Every unique key on the table must use every column in the table's partitioning expression. - // See https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html - for _, index := range tblInfo.Indices { - if index.Unique && !checkUniqueKeyIncludePartKey(partCols, index.Columns) { - if index.Primary { - // not support global index with clustered index - if tblInfo.IsCommonHandle { - return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("CLUSTERED INDEX") - } - if !config.GetGlobalConfig().EnableGlobalIndex { - return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY KEY") - } - } - if !config.GetGlobalConfig().EnableGlobalIndex { - return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX") - } - } - } - // when PKIsHandle, tblInfo.Indices will not contain the primary key. - if tblInfo.PKIsHandle { - indexCols := []*model.IndexColumn{{ - Name: tblInfo.GetPkName(), - Length: types.UnspecifiedLength, - }} - if !checkUniqueKeyIncludePartKey(partCols, indexCols) { - return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("CLUSTERED INDEX") - } - } - return nil -} - -func checkPartitionKeysConstraint(pi *model.PartitionInfo, indexColumns []*model.IndexColumn, tblInfo *model.TableInfo) (bool, error) { - var ( - partCols []*model.ColumnInfo - err error - ) - // The expr will be an empty string if the partition is defined by: - // CREATE TABLE t (...) PARTITION BY RANGE COLUMNS(...) - if partExpr := pi.Expr; partExpr != "" { - // Parse partitioning key, extract the column names in the partitioning key to slice. - partCols, err = extractPartitionColumns(partExpr, tblInfo) - if err != nil { - return false, err - } - } else { - partCols = make([]*model.ColumnInfo, 0, len(pi.Columns)) - for _, col := range pi.Columns { - colInfo := tblInfo.FindPublicColumnByName(col.L) - if colInfo == nil { - return false, infoschema.ErrColumnNotExists.GenWithStackByArgs(col, tblInfo.Name) - } - partCols = append(partCols, colInfo) - } - } - - // In MySQL, every unique key on the table must use every column in the table's partitioning expression.(This - // also includes the table's primary key.) - // In TiDB, global index will be built when this constraint is not satisfied and EnableGlobalIndex is set. - // See https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html - return checkUniqueKeyIncludePartKey(columnInfoSlice(partCols), indexColumns), nil -} - -type columnNameExtractor struct { - extractedColumns []*model.ColumnInfo - tblInfo *model.TableInfo - err error -} - -func (*columnNameExtractor) Enter(node ast.Node) (ast.Node, bool) { - return node, false -} - -func (cne *columnNameExtractor) Leave(node ast.Node) (ast.Node, bool) { - if c, ok := node.(*ast.ColumnNameExpr); ok { - info := findColumnByName(c.Name.Name.L, cne.tblInfo) - if info != nil { - cne.extractedColumns = append(cne.extractedColumns, info) - return node, true - } - cne.err = dbterror.ErrBadField.GenWithStackByArgs(c.Name.Name.O, "expression") - return nil, false - } - return node, true -} - -func findColumnByName(colName string, tblInfo *model.TableInfo) *model.ColumnInfo { - if tblInfo == nil { - return nil - } - for _, info := range tblInfo.Columns { - if info.Name.L == colName { - return info - } - } - return nil -} - -func extractPartitionColumns(partExpr string, tblInfo *model.TableInfo) ([]*model.ColumnInfo, error) { - partExpr = "select " + partExpr - stmts, _, err := parser.New().ParseSQL(partExpr) - if err != nil { - return nil, errors.Trace(err) - } - extractor := &columnNameExtractor{ - tblInfo: tblInfo, - extractedColumns: make([]*model.ColumnInfo, 0), - } - stmts[0].Accept(extractor) - if extractor.err != nil { - return nil, errors.Trace(extractor.err) - } - return extractor.extractedColumns, nil -} - -// stringSlice is defined for checkUniqueKeyIncludePartKey. -// if Go supports covariance, the code shouldn't be so complex. -type stringSlice interface { - Len() int - At(i int) string -} - -// checkUniqueKeyIncludePartKey checks that the partitioning key is included in the constraint. -func checkUniqueKeyIncludePartKey(partCols stringSlice, idxCols []*model.IndexColumn) bool { - for i := 0; i < partCols.Len(); i++ { - partCol := partCols.At(i) - _, idxCol := model.FindIndexColumnByName(idxCols, partCol) - if idxCol == nil { - // Partition column is not found in the index columns. - return false - } - if idxCol.Length > 0 { - // The partition column is found in the index columns, but the index column is a prefix index - return false - } - } - return true -} - -// columnInfoSlice implements the stringSlice interface. -type columnInfoSlice []*model.ColumnInfo - -func (cis columnInfoSlice) Len() int { - return len(cis) -} - -func (cis columnInfoSlice) At(i int) string { - return cis[i].Name.L -} - -// columnNameSlice implements the stringSlice interface. -type columnNameSlice []*ast.ColumnName - -func (cns columnNameSlice) Len() int { - return len(cns) -} - -func (cns columnNameSlice) At(i int) string { - return cns[i].Name.L -} - -func isPartExprUnsigned(tbInfo *model.TableInfo) bool { - // We should not rely on any configuration, system or session variables, so use a mock ctx! - // Same as in tables.newPartitionExpr - ctx := mock.NewContext() - expr, err := expression.ParseSimpleExprWithTableInfo(ctx, tbInfo.Partition.Expr, tbInfo) - if err != nil { - logutil.BgLogger().Error("isPartExpr failed parsing expression!", zap.Error(err)) - return false - } - if mysql.HasUnsignedFlag(expr.GetType().GetFlag()) { - return true - } - return false -} - -// truncateTableByReassignPartitionIDs reassigns new partition ids. -func truncateTableByReassignPartitionIDs(t *meta.Meta, tblInfo *model.TableInfo, pids []int64) (err error) { - if len(pids) < len(tblInfo.Partition.Definitions) { - // To make it compatible with older versions when pids was not given - // and if there has been any add/reorganize partition increasing the number of partitions - morePids, err := t.GenGlobalIDs(len(tblInfo.Partition.Definitions) - len(pids)) - if err != nil { - return errors.Trace(err) - } - pids = append(pids, morePids...) - } - newDefs := make([]model.PartitionDefinition, 0, len(tblInfo.Partition.Definitions)) - for i, def := range tblInfo.Partition.Definitions { - newDef := def - newDef.ID = pids[i] - newDefs = append(newDefs, newDef) - } - tblInfo.Partition.Definitions = newDefs - return nil -} - -type partitionExprProcessor func(sessionctx.Context, *model.TableInfo, ast.ExprNode) error - -type partitionExprChecker struct { - processors []partitionExprProcessor - ctx sessionctx.Context - tbInfo *model.TableInfo - err error - - columns []*model.ColumnInfo -} - -func newPartitionExprChecker(ctx sessionctx.Context, tbInfo *model.TableInfo, processor ...partitionExprProcessor) *partitionExprChecker { - p := &partitionExprChecker{processors: processor, ctx: ctx, tbInfo: tbInfo} - p.processors = append(p.processors, p.extractColumns) - return p -} - -func (p *partitionExprChecker) Enter(n ast.Node) (node ast.Node, skipChildren bool) { - expr, ok := n.(ast.ExprNode) - if !ok { - return n, true - } - for _, processor := range p.processors { - if err := processor(p.ctx, p.tbInfo, expr); err != nil { - p.err = err - return n, true - } - } - - return n, false -} - -func (p *partitionExprChecker) Leave(n ast.Node) (node ast.Node, ok bool) { - return n, p.err == nil -} - -func (p *partitionExprChecker) extractColumns(_ sessionctx.Context, _ *model.TableInfo, expr ast.ExprNode) error { - columnNameExpr, ok := expr.(*ast.ColumnNameExpr) - if !ok { - return nil - } - colInfo := findColumnByName(columnNameExpr.Name.Name.L, p.tbInfo) - if colInfo == nil { - return errors.Trace(dbterror.ErrBadField.GenWithStackByArgs(columnNameExpr.Name.Name.L, "partition function")) - } - - p.columns = append(p.columns, colInfo) - return nil -} - -func checkPartitionExprAllowed(_ sessionctx.Context, tb *model.TableInfo, e ast.ExprNode) error { - switch v := e.(type) { - case *ast.FuncCallExpr: - if _, ok := expression.AllowedPartitionFuncMap[v.FnName.L]; ok { - return nil - } - case *ast.BinaryOperationExpr: - if _, ok := expression.AllowedPartition4BinaryOpMap[v.Op]; ok { - return errors.Trace(checkNoTimestampArgs(tb, v.L, v.R)) - } - case *ast.UnaryOperationExpr: - if _, ok := expression.AllowedPartition4UnaryOpMap[v.Op]; ok { - return errors.Trace(checkNoTimestampArgs(tb, v.V)) - } - case *ast.ColumnNameExpr, *ast.ParenthesesExpr, *driver.ValueExpr, *ast.MaxValueExpr, - *ast.DefaultExpr, *ast.TimeUnitExpr: - return nil - } - return errors.Trace(dbterror.ErrPartitionFunctionIsNotAllowed) -} - -func checkPartitionExprArgs(_ sessionctx.Context, tblInfo *model.TableInfo, e ast.ExprNode) error { - expr, ok := e.(*ast.FuncCallExpr) - if !ok { - return nil - } - argsType, err := collectArgsType(tblInfo, expr.Args...) - if err != nil { - return errors.Trace(err) - } - switch expr.FnName.L { - case ast.ToDays, ast.ToSeconds, ast.DayOfMonth, ast.Month, ast.DayOfYear, ast.Quarter, ast.YearWeek, - ast.Year, ast.Weekday, ast.DayOfWeek, ast.Day: - return errors.Trace(checkResultOK(hasDateArgs(argsType...))) - case ast.Hour, ast.Minute, ast.Second, ast.TimeToSec, ast.MicroSecond: - return errors.Trace(checkResultOK(hasTimeArgs(argsType...))) - case ast.UnixTimestamp: - return errors.Trace(checkResultOK(hasTimestampArgs(argsType...))) - case ast.FromDays: - return errors.Trace(checkResultOK(hasDateArgs(argsType...) || hasTimeArgs(argsType...))) - case ast.Extract: - switch expr.Args[0].(*ast.TimeUnitExpr).Unit { - case ast.TimeUnitYear, ast.TimeUnitYearMonth, ast.TimeUnitQuarter, ast.TimeUnitMonth, ast.TimeUnitDay: - return errors.Trace(checkResultOK(hasDateArgs(argsType...))) - case ast.TimeUnitDayMicrosecond, ast.TimeUnitDayHour, ast.TimeUnitDayMinute, ast.TimeUnitDaySecond: - return errors.Trace(checkResultOK(hasDatetimeArgs(argsType...))) - case ast.TimeUnitHour, ast.TimeUnitHourMinute, ast.TimeUnitHourSecond, ast.TimeUnitMinute, ast.TimeUnitMinuteSecond, - ast.TimeUnitSecond, ast.TimeUnitMicrosecond, ast.TimeUnitHourMicrosecond, ast.TimeUnitMinuteMicrosecond, ast.TimeUnitSecondMicrosecond: - return errors.Trace(checkResultOK(hasTimeArgs(argsType...))) - default: - return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) - } - case ast.DateDiff: - return errors.Trace(checkResultOK(slice.AllOf(argsType, func(i int) bool { - return hasDateArgs(argsType[i]) - }))) - - case ast.Abs, ast.Ceiling, ast.Floor, ast.Mod: - has := hasTimestampArgs(argsType...) - if has { - return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) - } - } - return nil -} - -func collectArgsType(tblInfo *model.TableInfo, exprs ...ast.ExprNode) ([]byte, error) { - ts := make([]byte, 0, len(exprs)) - for _, arg := range exprs { - col, ok := arg.(*ast.ColumnNameExpr) - if !ok { - continue - } - columnInfo := findColumnByName(col.Name.Name.L, tblInfo) - if columnInfo == nil { - return nil, errors.Trace(dbterror.ErrBadField.GenWithStackByArgs(col.Name.Name.L, "partition function")) - } - ts = append(ts, columnInfo.GetType()) - } - - return ts, nil -} - -func hasDateArgs(argsType ...byte) bool { - return slice.AnyOf(argsType, func(i int) bool { - return argsType[i] == mysql.TypeDate || argsType[i] == mysql.TypeDatetime - }) -} - -func hasTimeArgs(argsType ...byte) bool { - return slice.AnyOf(argsType, func(i int) bool { - return argsType[i] == mysql.TypeDuration || argsType[i] == mysql.TypeDatetime - }) -} - -func hasTimestampArgs(argsType ...byte) bool { - return slice.AnyOf(argsType, func(i int) bool { - return argsType[i] == mysql.TypeTimestamp - }) -} - -func hasDatetimeArgs(argsType ...byte) bool { - return slice.AnyOf(argsType, func(i int) bool { - return argsType[i] == mysql.TypeDatetime - }) -} - -func checkNoTimestampArgs(tbInfo *model.TableInfo, exprs ...ast.ExprNode) error { - argsType, err := collectArgsType(tbInfo, exprs...) - if err != nil { - return err - } - if hasTimestampArgs(argsType...) { - return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) - } - return nil -} - -// hexIfNonPrint checks if printable UTF-8 characters from a single quoted string, -// if so, just returns the string -// else returns a hex string of the binary string (i.e. actual encoding, not unicode code points!) -func hexIfNonPrint(s string) string { - isPrint := true - // https://go.dev/blog/strings `for range` of string converts to runes! - for _, runeVal := range s { - if !strconv.IsPrint(runeVal) { - isPrint = false - break - } - } - if isPrint { - return s - } - // To avoid 'simple' MySQL accepted escape characters, to be showed as hex, just escape them - // \0 \b \n \r \t \Z, see https://dev.mysql.com/doc/refman/8.0/en/string-literals.html - isPrint = true - res := "" - for _, runeVal := range s { - switch runeVal { - case 0: // Null - res += `\0` - case 7: // Bell - res += `\b` - case '\t': // 9 - res += `\t` - case '\n': // 10 - res += `\n` - case '\r': // 13 - res += `\r` - case 26: // ctrl-z / Substitute - res += `\Z` - default: - if !strconv.IsPrint(runeVal) { - isPrint = false - break - } - res += string(runeVal) - } - } - if isPrint { - return res - } - // Not possible to create an easy interpreted MySQL string, return as hex string - // Can be converted to string in MySQL like: CAST(UNHEX('') AS CHAR(255)) - return "0x" + hex.EncodeToString([]byte(driver.UnwrapFromSingleQuotes(s))) -} - -func writeColumnListToBuffer(partitionInfo *model.PartitionInfo, sqlMode mysql.SQLMode, buf *bytes.Buffer) { - for i, col := range partitionInfo.Columns { - buf.WriteString(stringutil.Escape(col.O, sqlMode)) - if i < len(partitionInfo.Columns)-1 { - buf.WriteString(",") - } - } -} - -// AppendPartitionInfo is used in SHOW CREATE TABLE as well as generation the SQL syntax -// for the PartitionInfo during validation of various DDL commands -func AppendPartitionInfo(partitionInfo *model.PartitionInfo, buf *bytes.Buffer, sqlMode mysql.SQLMode) { - if partitionInfo == nil { - return - } - // Since MySQL 5.1/5.5 is very old and TiDB aims for 5.7/8.0 compatibility, we will not - // include the /*!50100 or /*!50500 comments for TiDB. - // This also solves the issue with comments within comments that would happen for - // PLACEMENT POLICY options. - defaultPartitionDefinitions := true - if partitionInfo.Type == model.PartitionTypeHash || - partitionInfo.Type == model.PartitionTypeKey { - for i, def := range partitionInfo.Definitions { - if def.Name.O != fmt.Sprintf("p%d", i) { - defaultPartitionDefinitions = false - break - } - if len(def.Comment) > 0 || def.PlacementPolicyRef != nil { - defaultPartitionDefinitions = false - break - } - } - - if defaultPartitionDefinitions { - if partitionInfo.Type == model.PartitionTypeHash { - fmt.Fprintf(buf, "\nPARTITION BY HASH (%s) PARTITIONS %d", partitionInfo.Expr, partitionInfo.Num) - } else { - buf.WriteString("\nPARTITION BY KEY (") - writeColumnListToBuffer(partitionInfo, sqlMode, buf) - buf.WriteString(")") - fmt.Fprintf(buf, " PARTITIONS %d", partitionInfo.Num) - } - return - } - } - // this if statement takes care of lists/range/key columns case - if len(partitionInfo.Columns) > 0 { - // partitionInfo.Type == model.PartitionTypeRange || partitionInfo.Type == model.PartitionTypeList - // || partitionInfo.Type == model.PartitionTypeKey - // Notice that MySQL uses two spaces between LIST and COLUMNS... - if partitionInfo.Type == model.PartitionTypeKey { - fmt.Fprintf(buf, "\nPARTITION BY %s (", partitionInfo.Type.String()) - } else { - fmt.Fprintf(buf, "\nPARTITION BY %s COLUMNS(", partitionInfo.Type.String()) - } - writeColumnListToBuffer(partitionInfo, sqlMode, buf) - buf.WriteString(")\n(") - } else { - fmt.Fprintf(buf, "\nPARTITION BY %s (%s)\n(", partitionInfo.Type.String(), partitionInfo.Expr) - } - - AppendPartitionDefs(partitionInfo, buf, sqlMode) - buf.WriteString(")") -} - -// AppendPartitionDefs generates a list of partition definitions needed for SHOW CREATE TABLE (in executor/show.go) -// as well as needed for generating the ADD PARTITION query for INTERVAL partitioning of ALTER TABLE t LAST PARTITION -// and generating the CREATE TABLE query from CREATE TABLE ... INTERVAL -func AppendPartitionDefs(partitionInfo *model.PartitionInfo, buf *bytes.Buffer, sqlMode mysql.SQLMode) { - for i, def := range partitionInfo.Definitions { - if i > 0 { - fmt.Fprintf(buf, ",\n ") - } - fmt.Fprintf(buf, "PARTITION %s", stringutil.Escape(def.Name.O, sqlMode)) - // PartitionTypeHash and PartitionTypeKey do not have any VALUES definition - if partitionInfo.Type == model.PartitionTypeRange { - lessThans := make([]string, len(def.LessThan)) - for idx, v := range def.LessThan { - lessThans[idx] = hexIfNonPrint(v) - } - fmt.Fprintf(buf, " VALUES LESS THAN (%s)", strings.Join(lessThans, ",")) - } else if partitionInfo.Type == model.PartitionTypeList { - if len(def.InValues) == 0 { - fmt.Fprintf(buf, " DEFAULT") - } else if len(def.InValues) == 1 && - len(def.InValues[0]) == 1 && - strings.EqualFold(def.InValues[0][0], "DEFAULT") { - fmt.Fprintf(buf, " DEFAULT") - } else { - values := bytes.NewBuffer(nil) - for j, inValues := range def.InValues { - if j > 0 { - values.WriteString(",") - } - if len(inValues) > 1 { - values.WriteString("(") - tmpVals := make([]string, len(inValues)) - for idx, v := range inValues { - tmpVals[idx] = hexIfNonPrint(v) - } - values.WriteString(strings.Join(tmpVals, ",")) - values.WriteString(")") - } else if len(inValues) == 1 { - values.WriteString(hexIfNonPrint(inValues[0])) - } - } - fmt.Fprintf(buf, " VALUES IN (%s)", values.String()) - } - } - if len(def.Comment) > 0 { - fmt.Fprintf(buf, " COMMENT '%s'", format.OutputFormat(def.Comment)) - } - if def.PlacementPolicyRef != nil { - // add placement ref info here - fmt.Fprintf(buf, " /*T![placement] PLACEMENT POLICY=%s */", stringutil.Escape(def.PlacementPolicyRef.Name.O, sqlMode)) - } - } -} diff --git a/ddl/partition_test.go b/ddl/partition_test.go deleted file mode 100644 index 325fe6a1795da..0000000000000 --- a/ddl/partition_test.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestDropAndTruncatePartition(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - dbInfo, err := testSchemaInfo(store, "test_partition") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) - // generate 5 partition in tableInfo. - tblInfo, partIDs := buildTableInfoWithPartition(t, store) - ctx := testkit.NewTestKit(t, store).Session() - testCreateTable(t, ctx, d, dbInfo, tblInfo) - testDropPartition(t, ctx, d, dbInfo, tblInfo, []string{"p0", "p1"}) - - newIDs, err := genGlobalIDs(store, 2) - require.NoError(t, err) - testTruncatePartition(t, ctx, d, dbInfo, tblInfo, []int64{partIDs[3], partIDs[4]}, newIDs) -} - -func buildTableInfoWithPartition(t *testing.T, store kv.Storage) (*model.TableInfo, []int64) { - tbl := &model.TableInfo{ - Name: model.NewCIStr("t"), - } - tbl.MaxColumnID++ - col := &model.ColumnInfo{ - Name: model.NewCIStr("c"), - Offset: 0, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLong), - ID: tbl.MaxColumnID, - } - genIDs, err := genGlobalIDs(store, 1) - require.NoError(t, err) - tbl.ID = genIDs[0] - tbl.Columns = []*model.ColumnInfo{col} - tbl.Charset = "utf8" - tbl.Collate = "utf8_bin" - - partIDs, err := genGlobalIDs(store, 5) - require.NoError(t, err) - partInfo := &model.PartitionInfo{ - Type: model.PartitionTypeRange, - Expr: tbl.Columns[0].Name.L, - Enable: true, - Definitions: []model.PartitionDefinition{ - { - ID: partIDs[0], - Name: model.NewCIStr("p0"), - LessThan: []string{"100"}, - }, - { - ID: partIDs[1], - Name: model.NewCIStr("p1"), - LessThan: []string{"200"}, - }, - { - ID: partIDs[2], - Name: model.NewCIStr("p2"), - LessThan: []string{"300"}, - }, - { - ID: partIDs[3], - Name: model.NewCIStr("p3"), - LessThan: []string{"400"}, - }, - { - ID: partIDs[4], - Name: model.NewCIStr("p4"), - LessThan: []string{"500"}, - }, - }, - } - tbl.Partition = partInfo - return tbl, partIDs -} - -func buildDropPartitionJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, partNames []string) *model.Job { - return &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - SchemaState: model.StatePublic, - Type: model.ActionDropTablePartition, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{partNames}, - } -} - -func testDropPartition(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, partNames []string) *model.Job { - job := buildDropPartitionJob(dbInfo, tblInfo, partNames) - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - return job -} - -func buildTruncatePartitionJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, pids []int64, newIDs []int64) *model.Job { - return &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionTruncateTablePartition, - SchemaState: model.StatePublic, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{pids, newIDs}, - } -} - -func testTruncatePartition(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, pids []int64, newIDs []int64) *model.Job { - job := buildTruncatePartitionJob(dbInfo, tblInfo, pids, newIDs) - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - return job -} - -func TestReorganizePartitionRollback(t *testing.T) { - // See issue: https://github.com/pingcap/tidb/issues/42448 - store, do := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("CREATE TABLE `t1` (\n" + - " `id` bigint(20) NOT NULL AUTO_INCREMENT,\n" + - " `k` int(11) NOT NULL DEFAULT '0',\n" + - " `c` char(120) NOT NULL DEFAULT '',\n" + - " `pad` char(60) NOT NULL DEFAULT '',\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `k_1` (`k`)\n" + - " ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - " PARTITION BY RANGE (`id`)\n" + - " (PARTITION `p0` VALUES LESS THAN (2000000),\n" + - " PARTITION `p1` VALUES LESS THAN (4000000),\n" + - " PARTITION `p2` VALUES LESS THAN (6000000),\n" + - " PARTITION `p3` VALUES LESS THAN (8000000),\n" + - " PARTITION `p4` VALUES LESS THAN (10000000),\n" + - " PARTITION `p5` VALUES LESS THAN (MAXVALUE))") - tk.MustExec("insert into t1(k, c, pad) values (1, 'a', 'beijing'), (2, 'b', 'chengdu')") - - wait := make(chan struct{}) - defer close(wait) - ddlDone := make(chan error) - defer close(ddlDone) - hook := &callback.TestDDLCallback{Do: do} - hook.OnJobRunAfterExported = func(job *model.Job) { - if job.Type == model.ActionReorganizePartition && job.SchemaState == model.StateWriteReorganization { - <-wait - <-wait - } - } - do.DDL().SetHook(hook) - - go func() { - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - err := tk2.ExecToErr("alter table t1 reorganize partition p0, p1, p2, p3, p4 into( partition pnew values less than (10000000))") - ddlDone <- err - }() - - jobID := "" - - // wait DDL job reaches hook and then cancel - select { - case wait <- struct{}{}: - rows := tk.MustQuery("admin show ddl jobs where JOB_TYPE='alter table reorganize partition'").Rows() - require.Equal(t, 1, len(rows)) - jobID = rows[0][0].(string) - tk.MustExec("admin cancel ddl jobs " + jobID) - case <-time.After(time.Minute): - require.FailNow(t, "timeout") - } - - // continue to run DDL - select { - case wait <- struct{}{}: - case <-time.After(time.Minute): - require.FailNow(t, "timeout") - } - - // wait ddl done - select { - case err := <-ddlDone: - require.Error(t, err) - case <-time.After(time.Minute): - require.FailNow(t, "wait ddl cancelled timeout") - } - - // check job rollback finished - rows := tk.MustQuery("admin show ddl jobs where JOB_ID=" + jobID).Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "rollback done", rows[0][len(rows[0])-1]) - - // check table meta after rollback - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + - " `id` bigint(20) NOT NULL AUTO_INCREMENT,\n" + - " `k` int(11) NOT NULL DEFAULT '0',\n" + - " `c` char(120) NOT NULL DEFAULT '',\n" + - " `pad` char(60) NOT NULL DEFAULT '',\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `k_1` (`k`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=5001\n" + - "PARTITION BY RANGE (`id`)\n" + - "(PARTITION `p0` VALUES LESS THAN (2000000),\n" + - " PARTITION `p1` VALUES LESS THAN (4000000),\n" + - " PARTITION `p2` VALUES LESS THAN (6000000),\n" + - " PARTITION `p3` VALUES LESS THAN (8000000),\n" + - " PARTITION `p4` VALUES LESS THAN (10000000),\n" + - " PARTITION `p5` VALUES LESS THAN (MAXVALUE))")) - tbl, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - require.NotNil(t, tbl.Meta().Partition) - require.Nil(t, tbl.Meta().Partition.AddingDefinitions) - require.Nil(t, tbl.Meta().Partition.DroppingDefinitions) - - // test then add index should success - tk.MustExec("alter table t1 add index idx_kc (k, c)") -} diff --git a/ddl/placement/BUILD.bazel b/ddl/placement/BUILD.bazel deleted file mode 100644 index 5c2bb6833317b..0000000000000 --- a/ddl/placement/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "placement", - srcs = [ - "bundle.go", - "common.go", - "constraint.go", - "constraints.go", - "errors.go", - "rule.go", - ], - importpath = "github.com/pingcap/tidb/ddl/placement", - visibility = ["//visibility:public"], - deps = [ - "//parser/model", - "//tablecodec", - "//util/codec", - "@com_github_pingcap_failpoint//:failpoint", - "@in_gopkg_yaml_v2//:yaml_v2", - ], -) - -go_test( - name = "placement_test", - timeout = "short", - srcs = [ - "bundle_test.go", - "common_test.go", - "constraint_test.go", - "constraints_test.go", - "meta_bundle_test.go", - "rule_test.go", - ], - embed = [":placement"], - flaky = True, - race = "on", - shard_count = 25, - deps = [ - "//kv", - "//meta", - "//parser/model", - "//store/mockstore", - "//tablecodec", - "//util/codec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/placement/rule.go b/ddl/placement/rule.go deleted file mode 100644 index 711fc57c6d652..0000000000000 --- a/ddl/placement/rule.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package placement - -import ( - "encoding/hex" - "encoding/json" - "fmt" - "regexp" - "strings" - - "github.com/pingcap/tidb/util/codec" - "gopkg.in/yaml.v2" -) - -// PeerRoleType is the expected peer type of the placement rule. -type PeerRoleType string - -const ( - // Voter can either match a leader peer or follower peer. - Voter PeerRoleType = "voter" - // Leader matches a leader. - Leader PeerRoleType = "leader" - // Follower matches a follower. - Follower PeerRoleType = "follower" - // Learner matches a learner. - Learner PeerRoleType = "learner" -) - -const ( - attributePrefix = "#" - // AttributeEvictLeader is used to evict leader from a store. - attributeEvictLeader = "evict-leader" -) - -// RuleGroupConfig defines basic config of rule group -type RuleGroupConfig struct { - ID string `json:"id"` - Index int `json:"index"` - Override bool `json:"override"` -} - -// Rule is the core placement rule struct. Check https://github.com/tikv/pd/blob/master/server/schedule/placement/rule.go. -type Rule struct { - GroupID string `json:"group_id"` - ID string `json:"id"` - Index int `json:"index,omitempty"` - Override bool `json:"override,omitempty"` - StartKeyHex string `json:"start_key"` - EndKeyHex string `json:"end_key"` - Role PeerRoleType `json:"role"` - Count int `json:"count"` - Constraints Constraints `json:"label_constraints,omitempty"` - LocationLabels []string `json:"location_labels,omitempty"` -} - -var _ json.Marshaler = (*TiFlashRule)(nil) -var _ json.Unmarshaler = (*TiFlashRule)(nil) - -// TiFlashRule extends Rule with other necessary fields. -type TiFlashRule struct { - GroupID string - ID string - Index int - Override bool - Role PeerRoleType - Count int - Constraints Constraints - LocationLabels []string - IsolationLevel string - StartKey []byte - EndKey []byte -} - -type tiFlashRule struct { - GroupID string `json:"group_id"` - ID string `json:"id"` - Index int `json:"index,omitempty"` - Override bool `json:"override,omitempty"` - Role PeerRoleType `json:"role"` - Count int `json:"count"` - Constraints Constraints `json:"label_constraints,omitempty"` - LocationLabels []string `json:"location_labels,omitempty"` - IsolationLevel string `json:"isolation_level,omitempty"` - StartKeyHex string `json:"start_key"` - EndKeyHex string `json:"end_key"` -} - -// MarshalJSON implements json.Marshaler interface for TiFlashRule. -func (r *TiFlashRule) MarshalJSON() ([]byte, error) { - return json.Marshal(&tiFlashRule{ - GroupID: r.GroupID, - ID: r.ID, - Index: r.Index, - Override: r.Override, - Role: r.Role, - Count: r.Count, - Constraints: r.Constraints, - LocationLabels: r.LocationLabels, - IsolationLevel: r.IsolationLevel, - StartKeyHex: hex.EncodeToString(codec.EncodeBytes(nil, r.StartKey)), - EndKeyHex: hex.EncodeToString(codec.EncodeBytes(nil, r.EndKey)), - }) -} - -// UnmarshalJSON implements json.Unmarshaler interface for TiFlashRule. -func (r *TiFlashRule) UnmarshalJSON(bytes []byte) error { - var rule tiFlashRule - if err := json.Unmarshal(bytes, &rule); err != nil { - return err - } - *r = TiFlashRule{ - GroupID: rule.GroupID, - ID: rule.ID, - Index: rule.Index, - Override: rule.Override, - Role: rule.Role, - Count: rule.Count, - Constraints: rule.Constraints, - LocationLabels: rule.LocationLabels, - IsolationLevel: rule.IsolationLevel, - } - - startKey, err := hex.DecodeString(rule.StartKeyHex) - if err != nil { - return err - } - - endKey, err := hex.DecodeString(rule.EndKeyHex) - if err != nil { - return err - } - - _, r.StartKey, err = codec.DecodeBytes(startKey, nil) - if err != nil { - return err - } - - _, r.EndKey, err = codec.DecodeBytes(endKey, nil) - - return err -} - -// NewRule constructs *Rule from role, count, and constraints. It is here to -// consistent the behavior of creating new rules. -func NewRule(role PeerRoleType, replicas uint64, cnst Constraints) *Rule { - return &Rule{ - Role: role, - Count: int(replicas), - Constraints: cnst, - } -} - -var wrongSeparatorRegexp = regexp.MustCompile(`[^"':]+:\d`) - -func getYamlMapFormatError(str string) error { - if !strings.Contains(str, ":") { - return ErrInvalidConstraintsMappingNoColonFound - } - if wrongSeparatorRegexp.MatchString(str) { - return ErrInvalidConstraintsMappingWrongSeparator - } - return nil -} - -// NewRules constructs []*Rule from a yaml-compatible representation of -// 'array' or 'dict' constraints. -// Refer to https://github.com/pingcap/tidb/blob/master/docs/design/2020-06-24-placement-rules-in-sql.md. -func NewRules(role PeerRoleType, replicas uint64, cnstr string) (rules []*Rule, err error) { - cnstbytes := []byte(cnstr) - constraints1, err1 := NewConstraintsFromYaml(cnstbytes) - if err1 == nil { - if replicas == 0 { - if len(cnstr) > 0 { - return nil, fmt.Errorf("%w: count of replicas should be positive, but got %d, constraint %s", ErrInvalidConstraintsReplicas, replicas, cnstr) - } - return nil, nil - } - rules = append(rules, NewRule(role, replicas, constraints1)) - err = err1 - return - } - // check if is dict constraints - constraints2 := map[string]int{} - if err2 := yaml.UnmarshalStrict(cnstbytes, &constraints2); err2 != nil { - err = fmt.Errorf("%w: should be [constraint1, ...] (error %s), {constraint1: cnt1, ...} (error %s), or any yaml compatible representation", ErrInvalidConstraintsFormat, err1, err2) - return - } - - rules, err = NewRulesWithDictConstraints(role, cnstr) - // check if replicas is consistent - if err == nil { - totalCnt := 0 - for _, rule := range rules { - totalCnt += rule.Count - } - if replicas != 0 && replicas != uint64(totalCnt) { - err = fmt.Errorf("%w: count of replicas in dict constrains is %d, but got %d", ErrInvalidConstraintsReplicas, totalCnt, replicas) - } - } - return -} - -// NewRulesWithDictConstraints constructs []*Rule from a yaml-compatible representation of -// 'dict' constraints. -func NewRulesWithDictConstraints(role PeerRoleType, cnstr string) ([]*Rule, error) { - rules := []*Rule{} - cnstbytes := []byte(cnstr) - constraints2 := map[string]int{} - err2 := yaml.UnmarshalStrict(cnstbytes, &constraints2) - if err2 == nil { - for labels, cnt := range constraints2 { - if cnt <= 0 { - if err := getYamlMapFormatError(string(cnstbytes)); err != nil { - return rules, err - } - return rules, fmt.Errorf("%w: count of labels '%s' should be positive, but got %d", ErrInvalidConstraintsMapcnt, labels, cnt) - } - } - - for labels, cnt := range constraints2 { - lbs, overrideRole, err := preCheckDictConstraintStr(labels, role) - if err != nil { - return rules, err - } - labelConstraints, err := NewConstraints(lbs) - if err != nil { - return rules, err - } - if cnt == 0 { - return nil, fmt.Errorf("%w: count of replicas should be positive, but got %d", ErrInvalidConstraintsReplicas, cnt) - } - rules = append(rules, NewRule(overrideRole, uint64(cnt), labelConstraints)) - } - return rules, nil - } - - return nil, fmt.Errorf("%w: should be [constraint1, ...] or {constraint1: cnt1, ...}, error %s, or any yaml compatible representation", ErrInvalidConstraintsFormat, err2) -} - -// Clone is used to duplicate a RuleOp for safe modification. -// Note that it is a shallow copy: Constraints is not cloned. -func (r *Rule) Clone() *Rule { - n := &Rule{} - *n = *r - return n -} - -func (r *Rule) String() string { - return fmt.Sprintf("%+v", *r) -} diff --git a/ddl/reorg.go b/ddl/reorg.go deleted file mode 100644 index 1978c81c04932..0000000000000 --- a/ddl/reorg.go +++ /dev/null @@ -1,896 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "encoding/hex" - "fmt" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tipb/go-tipb" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -// reorgCtx is for reorganization. -type reorgCtx struct { - // doneCh is used to notify. - // If the reorganization job is done, we will use this channel to notify outer. - // TODO: Now we use goroutine to simulate reorganization jobs, later we may - // use a persistent job list. - doneCh chan error - // rowCount is used to simulate a job's row count. - rowCount int64 - jobState model.JobState - - mu struct { - sync.Mutex - // warnings are used to store the warnings when doing the reorg job under certain SQL modes. - warnings map[errors.ErrorID]*terror.Error - warningsCount map[errors.ErrorID]int64 - } - - references atomicutil.Int32 -} - -// newContext gets a context. It is only used for adding column in reorganization state. -func newContext(store kv.Storage) sessionctx.Context { - c := mock.NewContext() - c.Store = store - c.GetSessionVars().SetStatusFlag(mysql.ServerStatusAutocommit, false) - - tz := *time.UTC - c.GetSessionVars().TimeZone = &tz - c.GetSessionVars().StmtCtx.SetTimeZone(&tz) - return c -} - -const defaultWaitReorgTimeout = 10 * time.Second - -// ReorgWaitTimeout is the timeout that wait ddl in write reorganization stage. -var ReorgWaitTimeout = 5 * time.Second - -func (rc *reorgCtx) notifyJobState(state model.JobState) { - atomic.StoreInt32((*int32)(&rc.jobState), int32(state)) -} - -func (rc *reorgCtx) isReorgCanceled() bool { - return int32(model.JobStateCancelled) == atomic.LoadInt32((*int32)(&rc.jobState)) || - int32(model.JobStateCancelling) == atomic.LoadInt32((*int32)(&rc.jobState)) -} - -func (rc *reorgCtx) isReorgPaused() bool { - return int32(model.JobStatePaused) == atomic.LoadInt32((*int32)(&rc.jobState)) || - int32(model.JobStatePausing) == atomic.LoadInt32((*int32)(&rc.jobState)) -} - -func (rc *reorgCtx) setRowCount(count int64) { - atomic.StoreInt64(&rc.rowCount, count) -} - -func (rc *reorgCtx) mergeWarnings(warnings map[errors.ErrorID]*terror.Error, warningsCount map[errors.ErrorID]int64) { - if len(warnings) == 0 || len(warningsCount) == 0 { - return - } - rc.mu.Lock() - defer rc.mu.Unlock() - rc.mu.warnings, rc.mu.warningsCount = mergeWarningsAndWarningsCount(warnings, rc.mu.warnings, warningsCount, rc.mu.warningsCount) -} - -func (rc *reorgCtx) resetWarnings() { - rc.mu.Lock() - defer rc.mu.Unlock() - rc.mu.warnings = make(map[errors.ErrorID]*terror.Error) - rc.mu.warningsCount = make(map[errors.ErrorID]int64) -} - -func (rc *reorgCtx) increaseRowCount(count int64) { - atomic.AddInt64(&rc.rowCount, count) -} - -func (rc *reorgCtx) getRowCount() int64 { - row := atomic.LoadInt64(&rc.rowCount) - return row -} - -// runReorgJob is used as a portal to do the reorganization work. -// eg: -// 1: add index -// 2: alter column type -// 3: clean global index -// 4: reorganize partitions -/* - ddl goroutine >---------+ - ^ | - | | - | | - | | <---(doneCh)--- f() - HandleDDLQueue(...) | <---(regular timeout) - | | <---(ctx done) - | | - | | - A more ddl round <-----+ -*/ -// How can we cancel reorg job? -// -// The background reorg is continuously running except for several factors, for instances, ddl owner change, -// logic error (kv duplicate when insert index / cast error when alter column), ctx done, and cancel signal. -// -// When `admin cancel ddl jobs xxx` takes effect, we will give this kind of reorg ddl one more round. -// because we should pull the result from doneCh out, otherwise, the reorg worker will hang on `f()` logic, -// which is a kind of goroutine leak. -// -// That's why we couldn't set the job to rollingback state directly in `convertJob2RollbackJob`, which is a -// cancelling portal for admin cancel action. -// -// In other words, the cancelling signal is informed from the bottom up, we set the atomic cancel variable -// in the cancelling portal to notify the lower worker goroutine, and fetch the cancel error from them in -// the additional ddl round. -// -// After that, we can make sure that the worker goroutine is correctly shut down. -func (w *worker) runReorgJob(reorgInfo *reorgInfo, tblInfo *model.TableInfo, - lease time.Duration, f func() error) error { - job := reorgInfo.Job - d := reorgInfo.d - // This is for tests compatible, because most of the early tests try to build the reorg job manually - // without reorg meta info, which will cause nil pointer in here. - if job.ReorgMeta == nil { - job.ReorgMeta = &model.DDLReorgMeta{ - SQLMode: mysql.ModeNone, - Warnings: make(map[errors.ErrorID]*terror.Error), - WarningsCount: make(map[errors.ErrorID]int64), - Location: &model.TimeZoneLocation{Name: time.UTC.String(), Offset: 0}, - } - } - - rc := w.getReorgCtx(job.ID) - if rc == nil { - // This job is cancelling, we should return ErrCancelledDDLJob directly. - // - // Q: Is there any possibility that the job is cancelling and has no reorgCtx? - // A: Yes, consider the case that : - // - we cancel the job when backfilling the last batch of data, the cancel txn is commit first, - // - and then the backfill workers send signal to the `doneCh` of the reorgCtx, - // - and then the DDL worker will remove the reorgCtx - // - and update the DDL job to `done` - // - but at the commit time, the DDL txn will raise a "write conflict" error and retry, and it happens. - if job.IsCancelling() { - return dbterror.ErrCancelledDDLJob - } - - rc = w.newReorgCtx(reorgInfo.Job.ID, reorgInfo.Job.GetRowCount()) - w.wg.Add(1) - go func() { - defer w.wg.Done() - rc.doneCh <- f() - }() - } - - waitTimeout := defaultWaitReorgTimeout - // if lease is 0, we are using a local storage, - // and we can wait the reorganization to be done here. - // if lease > 0, we don't need to wait here because - // we should update some job's progress context and try checking again, - // so we use a very little timeout here. - if lease > 0 { - waitTimeout = ReorgWaitTimeout - } - - // wait reorganization job done or timeout - select { - case err := <-rc.doneCh: - // Since job is cancelled,we don't care about its partial counts. - if rc.isReorgCanceled() || terror.ErrorEqual(err, dbterror.ErrCancelledDDLJob) { - d.removeReorgCtx(job.ID) - return dbterror.ErrCancelledDDLJob - } - rowCount := rc.getRowCount() - job.SetRowCount(rowCount) - if err != nil { - logutil.BgLogger().Warn("run reorg job done", zap.String("category", "ddl"), zap.Int64("handled rows", rowCount), zap.Error(err)) - } else { - logutil.BgLogger().Info("run reorg job done", zap.String("category", "ddl"), zap.Int64("handled rows", rowCount)) - } - - // Update a job's warnings. - w.mergeWarningsIntoJob(job) - - d.removeReorgCtx(job.ID) - - updateBackfillProgress(w, reorgInfo, tblInfo, rowCount) - - // For other errors, even err is not nil here, we still wait the partial counts to be collected. - // since in the next round, the startKey is brand new which is stored by last time. - if err != nil { - return errors.Trace(err) - } - case <-w.ctx.Done(): - logutil.BgLogger().Info("run reorg job quit", zap.String("category", "ddl")) - d.removeReorgCtx(job.ID) - // We return dbterror.ErrWaitReorgTimeout here too, so that outer loop will break. - return dbterror.ErrWaitReorgTimeout - case <-time.After(waitTimeout): - rowCount := rc.getRowCount() - job.SetRowCount(rowCount) - updateBackfillProgress(w, reorgInfo, tblInfo, rowCount) - - // Update a job's warnings. - w.mergeWarningsIntoJob(job) - - rc.resetWarnings() - - logutil.BgLogger().Info("run reorg job wait timeout", zap.String("category", "ddl"), - zap.Duration("wait time", waitTimeout), - zap.Int64("total added row count", rowCount)) - // If timeout, we will return, check the owner and retry to wait job done again. - return dbterror.ErrWaitReorgTimeout - } - return nil -} - -func overwriteReorgInfoFromGlobalCheckpoint(w *worker, sess *sess.Session, job *model.Job, reorgInfo *reorgInfo) error { - if job.ReorgMeta.ReorgTp != model.ReorgTypeLitMerge { - // Only used for the ingest mode job. - return nil - } - if reorgInfo.mergingTmpIdx { - // Merging the temporary index uses txn mode, so we don't need to consider the checkpoint. - return nil - } - if job.ReorgMeta.IsDistReorg { - // The global checkpoint is not used in distributed tasks. - return nil - } - if w.getReorgCtx(job.ID) != nil { - // We only overwrite from checkpoint when the job runs for the first time on this TiDB instance. - return nil - } - bc, ok := ingest.LitBackCtxMgr.Load(job.ID) - if ok { - // We create the checkpoint manager here because we need to wait for the reorg meta to be initialized. - if bc.GetCheckpointManager() == nil { - mgr, err := ingest.NewCheckpointManager(w.ctx, bc, w.sessPool, job.ID, reorgInfo.currElement.ID) - if err != nil { - logutil.BgLogger().Warn("create checkpoint manager failed", zap.String("category", "ddl-ingest"), zap.Error(err)) - } - bc.AttachCheckpointManager(mgr) - } - } - start, end, pid, err := getCheckpointReorgHandle(sess, job) - if err != nil { - return errors.Trace(err) - } - if pid > 0 { - reorgInfo.StartKey = start - reorgInfo.EndKey = end - reorgInfo.PhysicalTableID = pid - } - return nil -} - -func (w *worker) mergeWarningsIntoJob(job *model.Job) { - rc := w.getReorgCtx(job.ID) - rc.mu.Lock() - partWarnings := rc.mu.warnings - partWarningsCount := rc.mu.warningsCount - rc.mu.Unlock() - warnings, warningsCount := job.GetWarnings() - warnings, warningsCount = mergeWarningsAndWarningsCount(partWarnings, warnings, partWarningsCount, warningsCount) - job.SetWarnings(warnings, warningsCount) -} - -func updateBackfillProgress(w *worker, reorgInfo *reorgInfo, tblInfo *model.TableInfo, - addedRowCount int64) { - if tblInfo == nil { - return - } - progress := float64(0) - if addedRowCount != 0 { - totalCount := getTableTotalCount(w, tblInfo) - if totalCount > 0 { - progress = float64(addedRowCount) / float64(totalCount) - } else { - progress = 0 - } - if progress > 1 { - progress = 1 - } - logutil.BgLogger().Debug("update progress", zap.String("category", "ddl"), - zap.Float64("progress", progress), - zap.Int64("addedRowCount", addedRowCount), - zap.Int64("totalCount", totalCount)) - } - switch reorgInfo.Type { - case model.ActionAddIndex, model.ActionAddPrimaryKey: - var label string - if reorgInfo.mergingTmpIdx { - label = metrics.LblAddIndexMerge - } else { - label = metrics.LblAddIndex - } - metrics.GetBackfillProgressByLabel(label, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) - case model.ActionModifyColumn: - metrics.GetBackfillProgressByLabel(metrics.LblModifyColumn, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) - case model.ActionReorganizePartition, model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning: - metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) - } -} - -func getTableTotalCount(w *worker, tblInfo *model.TableInfo) int64 { - var ctx sessionctx.Context - ctx, err := w.sessPool.Get() - if err != nil { - return statistics.PseudoRowCount - } - defer w.sessPool.Put(ctx) - - executor, ok := ctx.(sqlexec.RestrictedSQLExecutor) - // `mock.Context` is used in tests, which doesn't implement RestrictedSQLExecutor - if !ok { - return statistics.PseudoRowCount - } - var rows []chunk.Row - if tblInfo.Partition != nil && len(tblInfo.Partition.DroppingDefinitions) > 0 { - // if Reorganize Partition, only select number of rows from the selected partitions! - defs := tblInfo.Partition.DroppingDefinitions - partIDs := make([]string, 0, len(defs)) - for _, def := range defs { - partIDs = append(partIDs, strconv.FormatInt(def.ID, 10)) - } - sql := "select sum(table_rows) from information_schema.partitions where tidb_partition_id in (%?);" - rows, _, err = executor.ExecRestrictedSQL(w.ctx, nil, sql, strings.Join(partIDs, ",")) - } else { - sql := "select table_rows from information_schema.tables where tidb_table_id=%?;" - rows, _, err = executor.ExecRestrictedSQL(w.ctx, nil, sql, tblInfo.ID) - } - if err != nil { - return statistics.PseudoRowCount - } - if len(rows) != 1 { - return statistics.PseudoRowCount - } - return rows[0].GetInt64(0) -} - -func (dc *ddlCtx) isReorgCancelled(jobID int64) bool { - return dc.getReorgCtx(jobID).isReorgCanceled() -} -func (dc *ddlCtx) isReorgPaused(jobID int64) bool { - return dc.getReorgCtx(jobID).isReorgPaused() -} - -func (dc *ddlCtx) isReorgRunnable(jobID int64, isDistReorg bool) error { - if isChanClosed(dc.ctx.Done()) { - // Worker is closed. So it can't do the reorganization. - return dbterror.ErrInvalidWorker.GenWithStack("worker is closed") - } - - if dc.isReorgCancelled(jobID) { - // Job is cancelled. So it can't be done. - return dbterror.ErrCancelledDDLJob - } - - if dc.isReorgPaused(jobID) { - logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.String("ID", dc.uuid)) - return dbterror.ErrPausedDDLJob.GenWithStackByArgs(jobID) - } - - // If isDistReorg is true, we needn't check if it is owner. - if isDistReorg { - return nil - } - if !dc.isOwner() { - // If it's not the owner, we will try later, so here just returns an error. - logutil.BgLogger().Info("DDL is not the DDL owner", zap.String("category", "ddl"), zap.String("ID", dc.uuid)) - return errors.Trace(dbterror.ErrNotOwner) - } - return nil -} - -type reorgInfo struct { - *model.Job - - StartKey kv.Key - EndKey kv.Key - d *ddlCtx - first bool - mergingTmpIdx bool - // PhysicalTableID is used for partitioned table. - // DDL reorganize for a partitioned table will handle partitions one by one, - // PhysicalTableID is used to trace the current partition we are handling. - // If the table is not partitioned, PhysicalTableID would be TableID. - PhysicalTableID int64 - dbInfo *model.DBInfo - elements []*meta.Element - currElement *meta.Element -} - -func (r *reorgInfo) NewJobContext() *JobContext { - return r.d.jobContext(r.Job.ID, r.Job.ReorgMeta) -} - -func (r *reorgInfo) String() string { - var isEnabled bool - if ingest.LitInitialized { - _, isEnabled = ingest.LitBackCtxMgr.Load(r.Job.ID) - } - return "CurrElementType:" + string(r.currElement.TypeKey) + "," + - "CurrElementID:" + strconv.FormatInt(r.currElement.ID, 10) + "," + - "StartKey:" + hex.EncodeToString(r.StartKey) + "," + - "EndKey:" + hex.EncodeToString(r.EndKey) + "," + - "First:" + strconv.FormatBool(r.first) + "," + - "PhysicalTableID:" + strconv.FormatInt(r.PhysicalTableID, 10) + "," + - "Ingest mode:" + strconv.FormatBool(isEnabled) -} - -func constructDescTableScanPB(physicalTableID int64, tblInfo *model.TableInfo, handleCols []*model.ColumnInfo) *tipb.Executor { - tblScan := tables.BuildTableScanFromInfos(tblInfo, handleCols) - tblScan.TableId = physicalTableID - tblScan.Desc = true - return &tipb.Executor{Tp: tipb.ExecType_TypeTableScan, TblScan: tblScan} -} - -func constructLimitPB(count uint64) *tipb.Executor { - limitExec := &tipb.Limit{ - Limit: count, - } - return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} -} - -func buildDescTableScanDAG(ctx sessionctx.Context, tbl table.PhysicalTable, handleCols []*model.ColumnInfo, limit uint64) (*tipb.DAGRequest, error) { - dagReq := &tipb.DAGRequest{} - _, timeZoneOffset := time.Now().In(time.UTC).Zone() - dagReq.TimeZoneOffset = int64(timeZoneOffset) - for i := range handleCols { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) - } - dagReq.Flags |= model.FlagInSelectStmt - - tblScanExec := constructDescTableScanPB(tbl.GetPhysicalID(), tbl.Meta(), handleCols) - dagReq.Executors = append(dagReq.Executors, tblScanExec) - dagReq.Executors = append(dagReq.Executors, constructLimitPB(limit)) - distsql.SetEncodeType(ctx, dagReq) - return dagReq, nil -} - -func getColumnsTypes(columns []*model.ColumnInfo) []*types.FieldType { - colTypes := make([]*types.FieldType, 0, len(columns)) - for _, col := range columns { - colTypes = append(colTypes, &col.FieldType) - } - return colTypes -} - -// buildDescTableScan builds a desc table scan upon tblInfo. -func (dc *ddlCtx) buildDescTableScan(ctx *JobContext, startTS uint64, tbl table.PhysicalTable, - handleCols []*model.ColumnInfo, limit uint64) (distsql.SelectResult, error) { - sctx := newContext(dc.store) - dagPB, err := buildDescTableScanDAG(sctx, tbl, handleCols, limit) - if err != nil { - return nil, errors.Trace(err) - } - var b distsql.RequestBuilder - var builder *distsql.RequestBuilder - var ranges []*ranger.Range - if tbl.Meta().IsCommonHandle { - ranges = ranger.FullNotNullRange() - } else { - ranges = ranger.FullIntRange(false) - } - builder = b.SetHandleRanges(sctx.GetSessionVars().StmtCtx, tbl.GetPhysicalID(), tbl.Meta().IsCommonHandle, ranges) - builder.SetDAGRequest(dagPB). - SetStartTS(startTS). - SetKeepOrder(true). - SetConcurrency(1). - SetDesc(true). - SetResourceGroupTagger(ctx.getResourceGroupTaggerForTopSQL()). - SetResourceGroupName(ctx.resourceGroupName) - - builder.Request.NotFillCache = true - builder.Request.Priority = kv.PriorityLow - builder.RequestSource.RequestSourceInternal = true - builder.RequestSource.RequestSourceType = ctx.ddlJobSourceType() - - kvReq, err := builder.Build() - if err != nil { - return nil, errors.Trace(err) - } - - result, err := distsql.Select(ctx.ddlJobCtx, sctx, kvReq, getColumnsTypes(handleCols)) - if err != nil { - return nil, errors.Trace(err) - } - return result, nil -} - -// GetTableMaxHandle gets the max handle of a PhysicalTable. -func (dc *ddlCtx) GetTableMaxHandle(ctx *JobContext, startTS uint64, tbl table.PhysicalTable) (maxHandle kv.Handle, emptyTable bool, err error) { - var handleCols []*model.ColumnInfo - var pkIdx *model.IndexInfo - tblInfo := tbl.Meta() - switch { - case tblInfo.PKIsHandle: - for _, col := range tbl.Meta().Columns { - if mysql.HasPriKeyFlag(col.GetFlag()) { - handleCols = []*model.ColumnInfo{col} - break - } - } - case tblInfo.IsCommonHandle: - pkIdx = tables.FindPrimaryIndex(tblInfo) - cols := tblInfo.Cols() - for _, idxCol := range pkIdx.Columns { - handleCols = append(handleCols, cols[idxCol.Offset]) - } - default: - handleCols = []*model.ColumnInfo{model.NewExtraHandleColInfo()} - } - - // build a desc scan of tblInfo, which limit is 1, we can use it to retrieve the last handle of the table. - result, err := dc.buildDescTableScan(ctx, startTS, tbl, handleCols, 1) - if err != nil { - return nil, false, errors.Trace(err) - } - defer terror.Call(result.Close) - - chk := chunk.New(getColumnsTypes(handleCols), 1, 1) - err = result.Next(ctx.ddlJobCtx, chk) - if err != nil { - return nil, false, errors.Trace(err) - } - - if chk.NumRows() == 0 { - // empty table - return nil, true, nil - } - sessCtx := newContext(dc.store) - row := chk.GetRow(0) - if tblInfo.IsCommonHandle { - maxHandle, err = buildCommonHandleFromChunkRow(sessCtx.GetSessionVars().StmtCtx, tblInfo, pkIdx, handleCols, row) - return maxHandle, false, err - } - return kv.IntHandle(row.GetInt64(0)), false, nil -} - -func buildCommonHandleFromChunkRow(sctx *stmtctx.StatementContext, tblInfo *model.TableInfo, idxInfo *model.IndexInfo, - cols []*model.ColumnInfo, row chunk.Row) (kv.Handle, error) { - fieldTypes := make([]*types.FieldType, 0, len(cols)) - for _, col := range cols { - fieldTypes = append(fieldTypes, &col.FieldType) - } - datumRow := row.GetDatumRow(fieldTypes) - tablecodec.TruncateIndexValues(tblInfo, idxInfo, datumRow) - - var handleBytes []byte - handleBytes, err := codec.EncodeKey(sctx, nil, datumRow...) - if err != nil { - return nil, err - } - return kv.NewCommonHandle(handleBytes) -} - -// getTableRange gets the start and end handle of a table (or partition). -func getTableRange(ctx *JobContext, d *ddlCtx, tbl table.PhysicalTable, snapshotVer uint64, priority int) (startHandleKey, endHandleKey kv.Key, err error) { - // Get the start handle of this partition. - err = iterateSnapshotKeys(ctx, d.store, priority, tbl.RecordPrefix(), snapshotVer, nil, nil, - func(h kv.Handle, rowKey kv.Key, rawRecord []byte) (bool, error) { - startHandleKey = rowKey - return false, nil - }) - if err != nil { - return startHandleKey, endHandleKey, errors.Trace(err) - } - maxHandle, isEmptyTable, err := d.GetTableMaxHandle(ctx, snapshotVer, tbl) - if err != nil { - return startHandleKey, nil, errors.Trace(err) - } - if maxHandle != nil { - endHandleKey = tablecodec.EncodeRecordKey(tbl.RecordPrefix(), maxHandle).Next() - } - if isEmptyTable || endHandleKey.Cmp(startHandleKey) <= 0 { - logutil.BgLogger().Info("get noop table range", zap.String("category", "ddl"), - zap.String("table", fmt.Sprintf("%v", tbl.Meta())), - zap.Int64("table/partition ID", tbl.GetPhysicalID()), - zap.String("start key", hex.EncodeToString(startHandleKey)), - zap.String("end key", hex.EncodeToString(endHandleKey)), - zap.Bool("is empty table", isEmptyTable)) - if startHandleKey == nil { - endHandleKey = nil - } else { - endHandleKey = startHandleKey.Next() - } - } - return -} - -func getValidCurrentVersion(store kv.Storage) (ver kv.Version, err error) { - ver, err = store.CurrentVersion(kv.GlobalTxnScope) - if err != nil { - return ver, errors.Trace(err) - } else if ver.Ver <= 0 { - return ver, dbterror.ErrInvalidStoreVer.GenWithStack("invalid storage current version %d", ver.Ver) - } - return ver, nil -} - -func getReorgInfo(ctx *JobContext, d *ddlCtx, rh *reorgHandler, job *model.Job, dbInfo *model.DBInfo, - tbl table.Table, elements []*meta.Element, mergingTmpIdx bool) (*reorgInfo, error) { - var ( - element *meta.Element - start kv.Key - end kv.Key - pid int64 - info reorgInfo - ) - - if job.SnapshotVer == 0 { - // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. - // Third step, we need to remove the element information to make sure we can save the reorganized information to storage. - failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { - if val.(string) == "addIdxNotOwnerErr" && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 3, 4) { - if err := rh.RemoveReorgElementFailPoint(job); err != nil { - failpoint.Return(nil, errors.Trace(err)) - } - info.first = true - failpoint.Return(&info, nil) - } - }) - - info.first = true - if d.lease > 0 { // Only delay when it's not in test. - delayForAsyncCommit() - } - ver, err := getValidCurrentVersion(d.store) - if err != nil { - return nil, errors.Trace(err) - } - tblInfo := tbl.Meta() - pid = tblInfo.ID - var tb table.PhysicalTable - if pi := tblInfo.GetPartitionInfo(); pi != nil { - pid = pi.Definitions[0].ID - tb = tbl.(table.PartitionedTable).GetPartition(pid) - } else { - tb = tbl.(table.PhysicalTable) - } - if mergingTmpIdx { - start, end = tablecodec.GetTableIndexKeyRange(pid, tablecodec.TempIndexPrefix|elements[0].ID) - } else { - start, end, err = getTableRange(ctx, d, tb, ver.Ver, job.Priority) - if err != nil { - return nil, errors.Trace(err) - } - } - logutil.BgLogger().Info("job get table range", zap.String("category", "ddl"), - zap.Int64("jobID", job.ID), zap.Int64("physicalTableID", pid), - zap.String("startKey", hex.EncodeToString(start)), - zap.String("endKey", hex.EncodeToString(end))) - - failpoint.Inject("errorUpdateReorgHandle", func() (*reorgInfo, error) { - return &info, errors.New("occur an error when update reorg handle") - }) - err = rh.InitDDLReorgHandle(job, start, end, pid, elements[0]) - if err != nil { - return &info, errors.Trace(err) - } - // Update info should after data persistent. - job.SnapshotVer = ver.Ver - element = elements[0] - } else { - failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { - // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. - // Second step, we need to remove the element information to make sure we can get the error of "ErrDDLReorgElementNotExist". - // However, since "txn.Reset()" will be called later, the reorganized information cannot be saved to storage. - if val.(string) == "addIdxNotOwnerErr" && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 2, 3) { - if err := rh.RemoveReorgElementFailPoint(job); err != nil { - failpoint.Return(nil, errors.Trace(err)) - } - } - }) - - var err error - element, start, end, pid, err = rh.GetDDLReorgHandle(job) - if err != nil { - // If the reorg element doesn't exist, this reorg info should be saved by the older TiDB versions. - // It's compatible with the older TiDB versions. - // We'll try to remove it in the next major TiDB version. - if meta.ErrDDLReorgElementNotExist.Equal(err) { - job.SnapshotVer = 0 - logutil.BgLogger().Warn("get reorg info, the element does not exist", zap.String("category", "ddl"), zap.String("job", job.String())) - if job.IsCancelling() { - return nil, nil - } - } - return &info, errors.Trace(err) - } - } - info.Job = job - info.d = d - info.StartKey = start - info.EndKey = end - info.PhysicalTableID = pid - info.currElement = element - info.elements = elements - info.mergingTmpIdx = mergingTmpIdx - info.dbInfo = dbInfo - - return &info, nil -} - -func getReorgInfoFromPartitions(ctx *JobContext, d *ddlCtx, rh *reorgHandler, job *model.Job, dbInfo *model.DBInfo, tbl table.PartitionedTable, partitionIDs []int64, elements []*meta.Element) (*reorgInfo, error) { - var ( - element *meta.Element - start kv.Key - end kv.Key - pid int64 - info reorgInfo - ) - if job.SnapshotVer == 0 { - info.first = true - if d.lease > 0 { // Only delay when it's not in test. - delayForAsyncCommit() - } - ver, err := getValidCurrentVersion(d.store) - if err != nil { - return nil, errors.Trace(err) - } - pid = partitionIDs[0] - physTbl := tbl.GetPartition(pid) - - start, end, err = getTableRange(ctx, d, physTbl, ver.Ver, job.Priority) - if err != nil { - return nil, errors.Trace(err) - } - logutil.BgLogger().Info("job get table range", zap.String("category", "ddl"), - zap.Int64("job ID", job.ID), zap.Int64("physical table ID", pid), - zap.String("start key", hex.EncodeToString(start)), - zap.String("end key", hex.EncodeToString(end))) - - err = rh.InitDDLReorgHandle(job, start, end, pid, elements[0]) - if err != nil { - return &info, errors.Trace(err) - } - // Update info should after data persistent. - job.SnapshotVer = ver.Ver - element = elements[0] - } else { - var err error - element, start, end, pid, err = rh.GetDDLReorgHandle(job) - if err != nil { - // If the reorg element doesn't exist, this reorg info should be saved by the older TiDB versions. - // It's compatible with the older TiDB versions. - // We'll try to remove it in the next major TiDB version. - if meta.ErrDDLReorgElementNotExist.Equal(err) { - job.SnapshotVer = 0 - logutil.BgLogger().Warn("get reorg info, the element does not exist", zap.String("category", "ddl"), zap.String("job", job.String())) - } - return &info, errors.Trace(err) - } - } - info.Job = job - info.d = d - info.StartKey = start - info.EndKey = end - info.PhysicalTableID = pid - info.currElement = element - info.elements = elements - info.dbInfo = dbInfo - - return &info, nil -} - -// UpdateReorgMeta creates a new transaction and updates tidb_ddl_reorg table, -// so the reorg can restart in case of issues. -func (r *reorgInfo) UpdateReorgMeta(startKey kv.Key, pool *sess.Pool) (err error) { - if startKey == nil && r.EndKey == nil { - return nil - } - sctx, err := pool.Get() - if err != nil { - return - } - defer pool.Put(sctx) - - se := sess.NewSession(sctx) - err = se.Begin() - if err != nil { - return - } - rh := newReorgHandler(se) - err = updateDDLReorgHandle(rh.s, r.Job.ID, startKey, r.EndKey, r.PhysicalTableID, r.currElement) - err1 := se.Commit() - if err == nil { - err = err1 - } - return errors.Trace(err) -} - -// reorgHandler is used to handle the reorg information duration reorganization DDL job. -type reorgHandler struct { - s *sess.Session -} - -// NewReorgHandlerForTest creates a new reorgHandler, only used in test. -func NewReorgHandlerForTest(se sessionctx.Context) *reorgHandler { - return newReorgHandler(sess.NewSession(se)) -} - -func newReorgHandler(sess *sess.Session) *reorgHandler { - return &reorgHandler{s: sess} -} - -// InitDDLReorgHandle initializes the job reorganization information. -func (r *reorgHandler) InitDDLReorgHandle(job *model.Job, startKey, endKey kv.Key, physicalTableID int64, element *meta.Element) error { - return initDDLReorgHandle(r.s, job.ID, startKey, endKey, physicalTableID, element) -} - -// RemoveReorgElementFailPoint removes the element of the reorganization information. -func (r *reorgHandler) RemoveReorgElementFailPoint(job *model.Job) error { - return removeReorgElement(r.s, job) -} - -// RemoveDDLReorgHandle removes the job reorganization related handles. -func (r *reorgHandler) RemoveDDLReorgHandle(job *model.Job, elements []*meta.Element) error { - return removeDDLReorgHandle(r.s, job, elements) -} - -// CleanupDDLReorgHandles removes the job reorganization related handles. -func CleanupDDLReorgHandles(job *model.Job, s *sess.Session) { - if job != nil && !job.IsFinished() && !job.IsSynced() { - // Job is given, but it is neither finished nor synced; do nothing - return - } - - err := cleanDDLReorgHandles(s, job) - if err != nil { - // ignore error, cleanup is not that critical - logutil.BgLogger().Warn("Failed removing the DDL reorg entry in tidb_ddl_reorg", zap.String("job", job.String()), zap.Error(err)) - } -} - -// GetDDLReorgHandle gets the latest processed DDL reorganize position. -func (r *reorgHandler) GetDDLReorgHandle(job *model.Job) (element *meta.Element, startKey, endKey kv.Key, physicalTableID int64, err error) { - return getDDLReorgHandle(r.s, job) -} diff --git a/ddl/resourcegroup/BUILD.bazel b/ddl/resourcegroup/BUILD.bazel deleted file mode 100644 index 47f9367b626cb..0000000000000 --- a/ddl/resourcegroup/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "resourcegroup", - srcs = [ - "errors.go", - "group.go", - ], - importpath = "github.com/pingcap/tidb/ddl/resourcegroup", - visibility = ["//visibility:public"], - deps = [ - "//parser/model", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/resource_manager", - ], -) diff --git a/ddl/resourcegroup/group.go b/ddl/resourcegroup/group.go deleted file mode 100644 index 9f3a38fe4fb90..0000000000000 --- a/ddl/resourcegroup/group.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package resourcegroup - -import ( - rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/parser/model" -) - -// MaxGroupNameLength is max length of the name of a resource group -const MaxGroupNameLength = 32 - -// NewGroupFromOptions creates a new resource group from the given options. -func NewGroupFromOptions(groupName string, options *model.ResourceGroupSettings) (*rmpb.ResourceGroup, error) { - if options == nil { - return nil, ErrInvalidGroupSettings - } - if len(groupName) > MaxGroupNameLength { - return nil, ErrTooLongResourceGroupName - } - - group := &rmpb.ResourceGroup{ - Name: groupName, - } - - group.Priority = uint32(options.Priority) - if options.Runaway != nil { - runaway := &rmpb.RunawaySettings{ - Rule: &rmpb.RunawayRule{}, - } - if options.Runaway.ExecElapsedTimeMs == 0 { - return nil, ErrInvalidResourceGroupRunawayExecElapsedTime - } - runaway.Rule.ExecElapsedTimeMs = options.Runaway.ExecElapsedTimeMs - if options.Runaway.Action == model.RunawayActionNone { - return nil, ErrUnknownResourceGroupRunawayAction - } - runaway.Action = rmpb.RunawayAction(options.Runaway.Action) - if options.Runaway.WatchType != model.WatchNone { - runaway.Watch = &rmpb.RunawayWatch{} - runaway.Watch.Type = rmpb.RunawayWatchType(options.Runaway.WatchType) - runaway.Watch.LastingDurationMs = options.Runaway.WatchDurationMs - } - group.RunawaySettings = runaway - } - - if options.Background != nil { - group.BackgroundSettings = &rmpb.BackgroundSettings{ - JobTypes: options.Background.JobTypes, - } - } - - if options.RURate > 0 { - group.Mode = rmpb.GroupMode_RUMode - group.RUSettings = &rmpb.GroupRequestUnitSettings{ - RU: &rmpb.TokenBucket{ - Settings: &rmpb.TokenLimitSettings{ - FillRate: options.RURate, - BurstLimit: options.BurstLimit, - }, - }, - } - if len(options.CPULimiter) > 0 || len(options.IOReadBandwidth) > 0 || len(options.IOWriteBandwidth) > 0 { - return nil, ErrInvalidResourceGroupDuplicatedMode - } - return group, nil - } - - // Only support RU mode now - return nil, ErrUnknownResourceGroupMode -} diff --git a/ddl/schema.go b/ddl/schema.go deleted file mode 100644 index e9cb1e6579635..0000000000000 --- a/ddl/schema.go +++ /dev/null @@ -1,367 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "fmt" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" -) - -func onCreateSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - dbInfo := &model.DBInfo{} - if err := job.DecodeArgs(dbInfo); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - dbInfo.ID = schemaID - dbInfo.State = model.StateNone - - err := checkSchemaNotExists(d, t, schemaID, dbInfo) - if err != nil { - if infoschema.ErrDatabaseExists.Equal(err) { - // The database already exists, can't create it, we should cancel this job now. - job.State = model.JobStateCancelled - } - return ver, errors.Trace(err) - } - - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - - switch dbInfo.State { - case model.StateNone: - // none -> public - dbInfo.State = model.StatePublic - err = t.CreateDatabase(dbInfo) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) - return ver, nil - default: - // We can't enter here. - return ver, errors.Errorf("invalid db state %v", dbInfo.State) - } -} - -func checkSchemaNotExists(d *ddlCtx, t *meta.Meta, schemaID int64, dbInfo *model.DBInfo) error { - // Try to use memory schema info to check first. - currVer, err := t.GetSchemaVersion() - if err != nil { - return err - } - is := d.infoCache.GetLatest() - if is.SchemaMetaVersion() == currVer { - return checkSchemaNotExistsFromInfoSchema(is, schemaID, dbInfo) - } - return checkSchemaNotExistsFromStore(t, schemaID, dbInfo) -} - -func checkSchemaNotExistsFromInfoSchema(is infoschema.InfoSchema, schemaID int64, dbInfo *model.DBInfo) error { - // Check database exists by name. - if is.SchemaExists(dbInfo.Name) { - return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbInfo.Name) - } - // Check database exists by ID. - if _, ok := is.SchemaByID(schemaID); ok { - return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbInfo.Name) - } - return nil -} - -func checkSchemaNotExistsFromStore(t *meta.Meta, schemaID int64, dbInfo *model.DBInfo) error { - dbs, err := t.ListDatabases() - if err != nil { - return errors.Trace(err) - } - - for _, db := range dbs { - if db.Name.L == dbInfo.Name.L { - if db.ID != schemaID { - return infoschema.ErrDatabaseExists.GenWithStackByArgs(db.Name) - } - dbInfo = db - } - } - return nil -} - -func onModifySchemaCharsetAndCollate(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var toCharset, toCollate string - if err := job.DecodeArgs(&toCharset, &toCollate); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return ver, errors.Trace(err) - } - - if dbInfo.Charset == toCharset && dbInfo.Collate == toCollate { - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) - return ver, nil - } - - dbInfo.Charset = toCharset - dbInfo.Collate = toCollate - - if err = t.UpdateDatabase(dbInfo); err != nil { - return ver, errors.Trace(err) - } - if ver, err = updateSchemaVersion(d, t, job); err != nil { - return ver, errors.Trace(err) - } - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) - return ver, nil -} - -func onModifySchemaDefaultPlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var placementPolicyRef *model.PolicyRefInfo - if err := job.DecodeArgs(&placementPolicyRef); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return ver, errors.Trace(err) - } - // Double Check if policy exits while ddl executing - if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, placementPolicyRef); err != nil { - return ver, errors.Trace(err) - } - - // Notice: dbInfo.DirectPlacementOpts and dbInfo.PlacementPolicyRef can not be both not nil, which checked before constructing ddl job. - // So that we can just check the two situation that do not need ddl: 1. DB.DP == DDL.DP && nil == nil 2. nil == nil && DB.PP == DDL.PP - if placementPolicyRef != nil && dbInfo.PlacementPolicyRef != nil && *dbInfo.PlacementPolicyRef == *placementPolicyRef { - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) - return ver, nil - } - - // If placementPolicyRef and directPlacementOpts are both nil, And placement of dbInfo is not nil, it will remove all placement options. - dbInfo.PlacementPolicyRef = placementPolicyRef - - if err = t.UpdateDatabase(dbInfo); err != nil { - return ver, errors.Trace(err) - } - if ver, err = updateSchemaVersion(d, t, job); err != nil { - return ver, errors.Trace(err) - } - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) - return ver, nil -} - -func onDropSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return ver, errors.Trace(err) - } - if dbInfo.State == model.StatePublic { - err = checkDatabaseHasForeignKeyReferredInOwner(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - } - - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - switch dbInfo.State { - case model.StatePublic: - // public -> write only - dbInfo.State = model.StateWriteOnly - err = t.UpdateDatabase(dbInfo) - if err != nil { - return ver, errors.Trace(err) - } - var tables []*model.TableInfo - tables, err = t.ListTables(job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - var ruleIDs []string - for _, tblInfo := range tables { - rules := append(getPartitionRuleIDs(job.SchemaName, tblInfo), fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L)) - ruleIDs = append(ruleIDs, rules...) - } - patch := label.NewRulePatch([]*label.Rule{}, ruleIDs) - err = infosync.UpdateLabelRules(context.TODO(), patch) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - case model.StateWriteOnly: - // write only -> delete only - dbInfo.State = model.StateDeleteOnly - err = t.UpdateDatabase(dbInfo) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateDeleteOnly: - dbInfo.State = model.StateNone - var tables []*model.TableInfo - tables, err = t.ListTables(job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - err = t.UpdateDatabase(dbInfo) - if err != nil { - return ver, errors.Trace(err) - } - if err = t.DropDatabase(dbInfo.ID); err != nil { - break - } - - // Finish this job. - if len(tables) > 0 { - job.Args = append(job.Args, getIDs(tables)) - } - job.FinishDBJob(model.JobStateDone, model.StateNone, ver, dbInfo) - default: - // We can't enter here. - return ver, errors.Trace(errors.Errorf("invalid db state %v", dbInfo.State)) - } - job.SchemaState = dbInfo.State - return ver, errors.Trace(err) -} - -func (w *worker) onRecoverSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var ( - recoverSchemaInfo *RecoverSchemaInfo - recoverSchemaCheckFlag int64 - ) - if err := job.DecodeArgs(&recoverSchemaInfo, &recoverSchemaCheckFlag); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - schemaInfo := recoverSchemaInfo.DBInfo - // check GC and safe point - gcEnable, err := checkGCEnable(w) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - switch schemaInfo.State { - case model.StateNone: - // none -> write only - // check GC enable and update flag. - if gcEnable { - job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagEnableGC - } else { - job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagDisableGC - } - // Clear all placement when recover - for _, recoverTabInfo := range recoverSchemaInfo.RecoverTabsInfo { - err = clearTablePlacementAndBundles(recoverTabInfo.TableInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - } - schemaInfo.State = model.StateWriteOnly - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - // write only -> public - // do recover schema and tables. - if gcEnable { - err = disableGC(w) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Errorf("disable gc failed, try again later. err: %v", err) - } - } - dbInfo := schemaInfo.Clone() - dbInfo.State = model.StatePublic - err = t.CreateDatabase(dbInfo) - if err != nil { - return ver, errors.Trace(err) - } - // check GC safe point - err = checkSafePoint(w, recoverSchemaInfo.SnapshotTS) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - for _, recoverInfo := range recoverSchemaInfo.RecoverTabsInfo { - if recoverInfo.TableInfo.TTLInfo != nil { - // force disable TTL job schedule for recovered table - recoverInfo.TableInfo.TTLInfo.Enable = false - } - ver, err = w.recoverTable(t, job, recoverInfo) - if err != nil { - return ver, errors.Trace(err) - } - } - schemaInfo.State = model.StatePublic - for _, recoverInfo := range recoverSchemaInfo.RecoverTabsInfo { - recoverInfo.TableInfo.State = model.StatePublic - recoverInfo.TableInfo.UpdateTS = t.StartTS - } - // use to update InfoSchema - job.SchemaID = schemaInfo.ID - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, schemaInfo) - return ver, nil - default: - // We can't enter here. - return ver, errors.Errorf("invalid db state %v", schemaInfo.State) - } - return ver, errors.Trace(err) -} - -func checkSchemaExistAndCancelNotExistJob(t *meta.Meta, job *model.Job) (*model.DBInfo, error) { - dbInfo, err := t.GetDatabase(job.SchemaID) - if err != nil { - return nil, errors.Trace(err) - } - if dbInfo == nil { - job.State = model.JobStateCancelled - return nil, infoschema.ErrDatabaseDropExists.GenWithStackByArgs("") - } - return dbInfo, nil -} - -func getIDs(tables []*model.TableInfo) []int64 { - ids := make([]int64, 0, len(tables)) - for _, t := range tables { - ids = append(ids, t.ID) - if t.GetPartitionInfo() != nil { - ids = append(ids, getPartitionIDs(t)...) - } - } - - return ids -} diff --git a/ddl/schema_test.go b/ddl/schema_test.go deleted file mode 100644 index 84317bf9c9792..0000000000000 --- a/ddl/schema_test.go +++ /dev/null @@ -1,433 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -func testCreateTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo) *model.Job { - job := &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionCreateTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{tblInfo}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v := getSchemaVer(t, ctx) - tblInfo.State = model.StatePublic - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - tblInfo.State = model.StateNone - return job -} - -func testCheckTableState(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo, state model.SchemaState) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - info, err := m.GetTable(dbInfo.ID, tblInfo.ID) - require.NoError(t, err) - - if state == model.StateNone { - require.NoError(t, err) - return nil - } - - require.Equal(t, info.Name, tblInfo.Name) - require.Equal(t, info.State, state) - return nil - })) -} - -// testTableInfo creates a test table with num int columns and with no index. -func testTableInfo(store kv.Storage, name string, num int) (*model.TableInfo, error) { - tblInfo := &model.TableInfo{ - Name: model.NewCIStr(name), - } - genIDs, err := genGlobalIDs(store, 1) - - if err != nil { - return nil, err - } - tblInfo.ID = genIDs[0] - - cols := make([]*model.ColumnInfo, num) - for i := range cols { - col := &model.ColumnInfo{ - Name: model.NewCIStr(fmt.Sprintf("c%d", i+1)), - Offset: i, - DefaultValue: i + 1, - State: model.StatePublic, - } - - col.FieldType = *types.NewFieldType(mysql.TypeLong) - tblInfo.MaxColumnID++ - col.ID = tblInfo.MaxColumnID - cols[i] = col - } - tblInfo.Columns = cols - tblInfo.Charset = "utf8" - tblInfo.Collate = "utf8_bin" - return tblInfo, nil -} - -func genGlobalIDs(store kv.Storage, count int) ([]int64, error) { - var ret []int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - var err error - ret, err = m.GenGlobalIDs(count) - return err - }) - return ret, err -} - -func testSchemaInfo(store kv.Storage, name string) (*model.DBInfo, error) { - dbInfo := &model.DBInfo{ - Name: model.NewCIStr(name), - } - - genIDs, err := genGlobalIDs(store, 1) - if err != nil { - return nil, err - } - dbInfo.ID = genIDs[0] - return dbInfo, nil -} - -func testCreateSchema(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo) *model.Job { - job := &model.Job{ - SchemaID: dbInfo.ID, - Type: model.ActionCreateSchema, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{dbInfo}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v := getSchemaVer(t, ctx) - dbInfo.State = model.StatePublic - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, db: dbInfo}) - dbInfo.State = model.StateNone - return job -} - -func buildDropSchemaJob(dbInfo *model.DBInfo) *model.Job { - return &model.Job{ - SchemaID: dbInfo.ID, - Type: model.ActionDropSchema, - BinlogInfo: &model.HistoryInfo{}, - } -} - -func testDropSchema(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo) (*model.Job, int64) { - job := buildDropSchemaJob(dbInfo) - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - ver := getSchemaVer(t, ctx) - return job, ver -} - -func isDDLJobDone(test *testing.T, t *meta.Meta, store kv.Storage) bool { - tk := testkit.NewTestKit(test, store) - rows := tk.MustQuery("select * from mysql.tidb_ddl_job").Rows() - - if len(rows) == 0 { - return true - } - time.Sleep(testLease) - return false -} - -func testCheckSchemaState(test *testing.T, store kv.Storage, dbInfo *model.DBInfo, state model.SchemaState) { - isDropped := true - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - for { - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - info, err := t.GetDatabase(dbInfo.ID) - require.NoError(test, err) - - if state == model.StateNone { - isDropped = isDDLJobDone(test, t, store) - if !isDropped { - return nil - } - require.Nil(test, info) - return nil - } - - require.Equal(test, info.Name, dbInfo.Name) - require.Equal(test, info.State, state) - return nil - }) - require.NoError(test, err) - - if isDropped { - break - } - } -} - -func TestSchema(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - dbInfo, err := testSchemaInfo(store, "test_schema") - require.NoError(t, err) - - // create a database. - tk := testkit.NewTestKit(t, store) - d := domain.DDL() - job := testCreateSchema(t, tk.Session(), d, dbInfo) - testCheckSchemaState(t, store, dbInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - /*** to drop the schema with two tables. ***/ - // create table t with 100 records. - tblInfo1, err := testTableInfo(store, "t", 3) - require.NoError(t, err) - tJob1 := testCreateTable(t, tk.Session(), d, dbInfo, tblInfo1) - testCheckTableState(t, store, dbInfo, tblInfo1, model.StatePublic) - testCheckJobDone(t, store, tJob1.ID, true) - tbl1 := testGetTable(t, domain, tblInfo1.ID) - err = sessiontxn.NewTxn(context.Background(), tk.Session()) - require.NoError(t, err) - for i := 1; i <= 100; i++ { - _, err := tbl1.AddRecord(tk.Session(), types.MakeDatums(i, i, i)) - require.NoError(t, err) - } - // create table t1 with 1034 records. - tblInfo2, err := testTableInfo(store, "t1", 3) - require.NoError(t, err) - tk2 := testkit.NewTestKit(t, store) - tJob2 := testCreateTable(t, tk2.Session(), d, dbInfo, tblInfo2) - testCheckTableState(t, store, dbInfo, tblInfo2, model.StatePublic) - testCheckJobDone(t, store, tJob2.ID, true) - tbl2 := testGetTable(t, domain, tblInfo2.ID) - err = sessiontxn.NewTxn(context.Background(), tk2.Session()) - require.NoError(t, err) - for i := 1; i <= 1034; i++ { - _, err := tbl2.AddRecord(tk2.Session(), types.MakeDatums(i, i, i)) - require.NoError(t, err) - } - tk3 := testkit.NewTestKit(t, store) - job, v := testDropSchema(t, tk3.Session(), d, dbInfo) - testCheckSchemaState(t, store, dbInfo, model.StateNone) - ids := make(map[int64]struct{}) - ids[tblInfo1.ID] = struct{}{} - ids[tblInfo2.ID] = struct{}{} - checkHistoryJobArgs(t, tk3.Session(), job.ID, &historyJobArgs{ver: v, db: dbInfo, tblIDs: ids}) - - // Drop a non-existent database. - job = &model.Job{ - SchemaID: dbInfo.ID, - Type: model.ActionDropSchema, - BinlogInfo: &model.HistoryInfo{}, - } - ctx := testkit.NewTestKit(t, store).Session() - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.True(t, terror.ErrorEqual(err, infoschema.ErrDatabaseDropExists), "err %v", err) - - // Drop a database without a table. - dbInfo1, err := testSchemaInfo(store, "test1") - require.NoError(t, err) - job = testCreateSchema(t, ctx, d, dbInfo1) - testCheckSchemaState(t, store, dbInfo1, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - job, _ = testDropSchema(t, ctx, d, dbInfo1) - testCheckSchemaState(t, store, dbInfo1, model.StateNone) - testCheckJobDone(t, store, job.ID, false) -} - -func TestSchemaWaitJob(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - require.True(t, domain.DDL().OwnerManager().IsOwner()) - - d2 := ddl.NewDDL(context.Background(), - ddl.WithEtcdClient(domain.EtcdClient()), - ddl.WithStore(store), - ddl.WithInfoCache(domain.InfoCache()), - ddl.WithLease(testLease), - ) - err := d2.Start(pools.NewResourcePool(func() (pools.Resource, error) { - session := testkit.NewTestKit(t, store).Session() - session.GetSessionVars().CommonGlobalLoaded = true - return session, nil - }, 20, 20, 5)) - require.NoError(t, err) - defer func() { - err := d2.Stop() - require.NoError(t, err) - }() - - // d2 must not be owner. - d2.OwnerManager().RetireOwner() - // wait one-second makes d2 stop pick up jobs. - time.Sleep(1 * time.Second) - - dbInfo, err := testSchemaInfo(store, "test_schema") - require.NoError(t, err) - se := testkit.NewTestKit(t, store).Session() - testCreateSchema(t, se, d2, dbInfo) - testCheckSchemaState(t, store, dbInfo, model.StatePublic) - - // d2 must not be owner. - require.False(t, d2.OwnerManager().IsOwner()) - - genIDs, err := genGlobalIDs(store, 1) - require.NoError(t, err) - schemaID := genIDs[0] - doDDLJobErr(t, schemaID, 0, model.ActionCreateSchema, []interface{}{dbInfo}, testkit.NewTestKit(t, store).Session(), d2, store) -} - -func doDDLJobErr(t *testing.T, schemaID, tableID int64, tp model.ActionType, args []interface{}, ctx sessionctx.Context, d ddl.DDL, store kv.Storage) *model.Job { - job := &model.Job{ - SchemaID: schemaID, - TableID: tableID, - Type: tp, - Args: args, - BinlogInfo: &model.HistoryInfo{}, - } - // TODO: check error detail - ctx.SetValue(sessionctx.QueryString, "skip") - require.Error(t, d.DoDDLJob(ctx, job)) - testCheckJobCancelled(t, store, job, nil) - - return job -} - -func testCheckJobCancelled(t *testing.T, store kv.Storage, job *model.Job, state *model.SchemaState) { - se := testkit.NewTestKit(t, store).Session() - historyJob, err := ddl.GetHistoryJobByID(se, job.ID) - require.NoError(t, err) - require.True(t, historyJob.IsCancelled() || historyJob.IsRollbackDone(), "history job %s", historyJob) - if state != nil { - require.Equal(t, historyJob.SchemaState, *state) - } -} - -func TestRenameTableAutoIDs(t *testing.T) { - store := testkit.CreateMockStore(t) - tk1 := testkit.NewTestKit(t, store) - - dbName := "RenameTableAutoIDs" - tk1.MustExec(`create schema ` + dbName) - tk1.MustExec(`create schema ` + dbName + "2") - tk1.MustExec(`use ` + dbName) - tk1.MustExec(`CREATE TABLE t (a int auto_increment primary key nonclustered, b varchar(255), key (b))`) - tk1.MustExec(`insert into t values (11,11),(2,2),(null,12)`) - tk1.MustExec(`insert into t values (null,18)`) - tk1.MustQuery(`select _tidb_rowid, a, b from t`).Sort().Check(testkit.Rows("13 11 11", "14 2 2", "15 12 12", "17 16 18")) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec(`use ` + dbName) - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec(`use ` + dbName) - waitFor := func(col int, tableName, s string) { - for { - tk4 := testkit.NewTestKit(t, store) - tk4.MustExec(`use test`) - sql := `admin show ddl jobs where db_name like '` + strings.ToLower(dbName) + `%' and table_name like '` + tableName + `%' and job_type = 'rename table'` - res := tk4.MustQuery(sql).Rows() - if len(res) == 1 && res[0][col] == s { - break - } - for i := range res { - strs := make([]string, 0, len(res[i])) - for j := range res[i] { - strs = append(strs, res[i][j].(string)) - } - logutil.BgLogger().Info("ddl jobs", zap.Strings("jobs", strs)) - } - time.Sleep(10 * time.Millisecond) - } - } - alterChan := make(chan error) - tk2.MustExec(`set @@session.innodb_lock_wait_timeout = 0`) - tk2.MustExec(`BEGIN`) - tk2.MustExec(`insert into t values (null, 4)`) - go func() { - alterChan <- tk1.ExecToErr(`rename table t to ` + dbName + `2.t2`) - }() - waitFor(11, "t", "running") - waitFor(4, "t", "public") - tk3.MustExec(`BEGIN`) - tk3.MustExec(`insert into ` + dbName + `2.t2 values (20, 5)`) - - // TODO: Fix https://github.com/pingcap/tidb/issues/46904 - tk2.MustContainErrMsg(`insert into t values (null, 6)`, "[tikv:1205]Lock wait timeout exceeded; try restarting transaction") - tk2.MustExec(`rollback`) - tk3.MustExec(`rollback`) - /* - tk3.MustExec(`insert into ` + dbName + `2.t2 values (null, 7)`) - tk2.MustExec(`COMMIT`) - - waitFor(11, "t", "done") - tk2.MustExec(`BEGIN`) - tk2.MustExec(`insert into ` + dbName + `2.t2 values (null, 8)`) - - tk3.MustExec(`insert into ` + dbName + `2.t2 values (null, 9)`) - tk2.MustExec(`insert into ` + dbName + `2.t2 values (null, 10)`) - tk3.MustExec(`COMMIT`) - - waitFor(11, "t", "synced") - tk2.MustExec(`COMMIT`) - tk3.MustQuery(`select _tidb_rowid, a, b from ` + dbName + `2.t2`).Sort().Check(testkit.Rows(""+ - "13 11 11", - "14 2 2", - "15 12 12", - "17 16 18", - "19 18 4", - "21 20 6", - "5013 5012 5", - "5015 5014 7", - )) - - require.NoError(t, <-alterChan) - tk2.MustQuery(`select _tidb_rowid, a, b from ` + dbName + `2.t2`).Sort().Check(testkit.Rows( - "13 11 11", "14 2 2", "15 12 12", "17 16 18", - "19 18 4", "21 20 6", "5013 5012 5", "5015 5014 7")) - */ - require.NoError(t, <-alterChan) -} diff --git a/ddl/schematracker/BUILD.bazel b/ddl/schematracker/BUILD.bazel deleted file mode 100644 index 1b4c75cfea9b3..0000000000000 --- a/ddl/schematracker/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "schematracker", - srcs = [ - "checker.go", - "dm_tracker.go", - "info_store.go", - ], - importpath = "github.com/pingcap/tidb/ddl/schematracker", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//ddl/syncer", - "//infoschema", - "//kv", - "//meta/autoid", - "//owner", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//sessionctx", - "//sessionctx/variable", - "//statistics/handle", - "//store/mockstore", - "//table", - "//table/tables", - "//tidb-binlog/pump_client", - "//util/collate", - "//util/dbterror", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "schematracker_test", - timeout = "short", - srcs = [ - "dm_tracker_test.go", - "info_store_test.go", - ], - embed = [":schematracker"], - flaky = True, - shard_count = 14, - deps = [ - "//executor", - "//infoschema", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//util/chunk", - "//util/mock", - "//util/sqlexec", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/schematracker/checker.go b/ddl/schematracker/checker.go deleted file mode 100644 index 2919fee057f5a..0000000000000 --- a/ddl/schematracker/checker.go +++ /dev/null @@ -1,608 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schematracker - -import ( - "bytes" - "context" - "crypto/tls" - "fmt" - "strings" - "sync/atomic" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" -) - -var ( - // ConstructResultOfShowCreateDatabase should be assigned to executor.ConstructResultOfShowCreateDatabase. - // It is used to break cycle import. - ConstructResultOfShowCreateDatabase func(sessionctx.Context, *model.DBInfo, bool, *bytes.Buffer) error - // ConstructResultOfShowCreateTable should be assigned to executor.ConstructResultOfShowCreateTable. - // It is used to break cycle import. - ConstructResultOfShowCreateTable func(sessionctx.Context, *model.TableInfo, autoid.Allocators, *bytes.Buffer) error -) - -func init() { - mockstore.DDLCheckerInjector = NewStorageDDLInjector -} - -// Checker is used to check the result of SchemaTracker is same as real DDL. -type Checker struct { - realDDL ddl.DDL - tracker SchemaTracker - closed atomic.Bool -} - -// NewChecker creates a Checker. -func NewChecker(realDDL ddl.DDL) *Checker { - return &Checker{ - realDDL: realDDL, - tracker: NewSchemaTracker(2), - } -} - -// Disable turns off check. -func (d *Checker) Disable() { - d.closed.Store(true) -} - -// Enable turns on check. -func (d *Checker) Enable() { - d.closed.Store(false) -} - -// CreateTestDB creates a `test` database like the default behaviour of TiDB. -func (d *Checker) CreateTestDB(ctx sessionctx.Context) { - d.tracker.CreateTestDB(ctx) -} - -func (d *Checker) checkDBInfo(ctx sessionctx.Context, dbName model.CIStr) { - if d.closed.Load() { - return - } - dbInfo, _ := d.realDDL.GetInfoSchemaWithInterceptor(ctx).SchemaByName(dbName) - dbInfo2 := d.tracker.SchemaByName(dbName) - - if dbInfo == nil || dbInfo2 == nil { - if dbInfo == nil && dbInfo2 == nil { - return - } - errStr := fmt.Sprintf("inconsistent dbInfo, dbName: %s, real ddl: %p, schematracker:%p", dbName, dbInfo, dbInfo2) - panic(errStr) - } - - result := bytes.NewBuffer(make([]byte, 0, 512)) - err := ConstructResultOfShowCreateDatabase(ctx, dbInfo, false, result) - if err != nil { - panic(err) - } - result2 := bytes.NewBuffer(make([]byte, 0, 512)) - err = ConstructResultOfShowCreateDatabase(ctx, dbInfo2, false, result2) - if err != nil { - panic(err) - } - s1 := result.String() - s2 := result2.String() - if s1 != s2 { - errStr := fmt.Sprintf("%s != %s", s1, s2) - panic(errStr) - } -} - -func (d *Checker) checkTableInfo(ctx sessionctx.Context, dbName, tableName model.CIStr) { - if d.closed.Load() { - return - } - - if dbName.L == mysql.SystemDB { - // no need to check system tables. - return - } - - tableInfo, _ := d.realDDL.GetInfoSchemaWithInterceptor(ctx).TableByName(dbName, tableName) - tableInfo2, _ := d.tracker.TableByName(dbName, tableName) - - if tableInfo == nil || tableInfo2 == nil { - if tableInfo == nil && tableInfo2 == nil { - return - } - errStr := fmt.Sprintf("inconsistent tableInfo, dbName: %s, tableName: %s, real ddl: %p, schematracker:%p", - dbName, tableName, tableInfo, tableInfo2) - panic(errStr) - } - - result := bytes.NewBuffer(make([]byte, 0, 512)) - err := ConstructResultOfShowCreateTable(ctx, tableInfo.Meta(), autoid.Allocators{}, result) - if err != nil { - panic(err) - } - result2 := bytes.NewBuffer(make([]byte, 0, 512)) - err = ConstructResultOfShowCreateTable(ctx, tableInfo2, autoid.Allocators{}, result2) - if err != nil { - panic(err) - } - - // SchemaTracker will always use NONCLUSTERED so it can support more types of DDL. - removeClusteredIndexComment := func(s string) string { - ret := strings.ReplaceAll(s, " /*T![clustered_index] NONCLUSTERED */", "") - ret = strings.ReplaceAll(ret, " /*T![clustered_index] CLUSTERED */", "") - return ret - } - - s1 := removeClusteredIndexComment(result.String()) - s2 := removeClusteredIndexComment(result2.String()) - - if s1 != s2 { - errStr := fmt.Sprintf("%s != %s", s1, s2) - panic(errStr) - } -} - -// CreateSchema implements the DDL interface. -func (d *Checker) CreateSchema(ctx sessionctx.Context, stmt *ast.CreateDatabaseStmt) error { - err := d.realDDL.CreateSchema(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.CreateSchema(ctx, stmt) - if err != nil { - panic(err) - } - - d.checkDBInfo(ctx, stmt.Name) - return nil -} - -// AlterSchema implements the DDL interface. -func (d *Checker) AlterSchema(sctx sessionctx.Context, stmt *ast.AlterDatabaseStmt) error { - err := d.realDDL.AlterSchema(sctx, stmt) - if err != nil { - return err - } - err = d.tracker.AlterSchema(sctx, stmt) - if err != nil { - panic(err) - } - - d.checkDBInfo(sctx, stmt.Name) - return nil -} - -// DropSchema implements the DDL interface. -func (d *Checker) DropSchema(ctx sessionctx.Context, stmt *ast.DropDatabaseStmt) error { - err := d.realDDL.DropSchema(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.DropSchema(ctx, stmt) - if err != nil { - panic(err) - } - - d.checkDBInfo(ctx, stmt.Name) - return nil -} - -// RecoverSchema implements the DDL interface. -func (*Checker) RecoverSchema(_ sessionctx.Context, _ *ddl.RecoverSchemaInfo) (err error) { - return nil -} - -// CreateTable implements the DDL interface. -func (d *Checker) CreateTable(ctx sessionctx.Context, stmt *ast.CreateTableStmt) error { - err := d.realDDL.CreateTable(ctx, stmt) - if err != nil { - return err - } - - // some unit test will also check warnings, we reset the warnings after SchemaTracker use session context again. - count := ctx.GetSessionVars().StmtCtx.WarningCount() - // backup old session variables because CreateTable will change them. - strictSQLMode := ctx.GetSessionVars().StrictSQLMode - enableClusteredIndex := ctx.GetSessionVars().EnableClusteredIndex - - err = d.tracker.CreateTable(ctx, stmt) - if err != nil { - panic(err) - } - - ctx.GetSessionVars().StrictSQLMode = strictSQLMode - ctx.GetSessionVars().EnableClusteredIndex = enableClusteredIndex - ctx.GetSessionVars().StmtCtx.TruncateWarnings(int(count)) - - d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) - return nil -} - -// CreateView implements the DDL interface. -func (d *Checker) CreateView(ctx sessionctx.Context, stmt *ast.CreateViewStmt) error { - err := d.realDDL.CreateView(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.CreateView(ctx, stmt) - if err != nil { - panic(err) - } - - d.checkTableInfo(ctx, stmt.ViewName.Schema, stmt.ViewName.Name) - return nil -} - -// DropTable implements the DDL interface. -func (d *Checker) DropTable(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) { - err = d.realDDL.DropTable(ctx, stmt) - _ = d.tracker.DropTable(ctx, stmt) - - for _, tableName := range stmt.Tables { - d.checkTableInfo(ctx, tableName.Schema, tableName.Name) - } - return err -} - -// RecoverTable implements the DDL interface. -func (*Checker) RecoverTable(_ sessionctx.Context, _ *ddl.RecoverInfo) (err error) { - //TODO implement me - panic("implement me") -} - -// FlashbackCluster implements the DDL interface. -func (*Checker) FlashbackCluster(_ sessionctx.Context, _ uint64) (err error) { - //TODO implement me - panic("implement me") -} - -// DropView implements the DDL interface. -func (d *Checker) DropView(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) { - err = d.realDDL.DropView(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.DropView(ctx, stmt) - if err != nil { - panic(err) - } - - for _, tableName := range stmt.Tables { - d.checkTableInfo(ctx, tableName.Schema, tableName.Name) - } - return nil -} - -// CreateIndex implements the DDL interface. -func (d *Checker) CreateIndex(ctx sessionctx.Context, stmt *ast.CreateIndexStmt) error { - err := d.realDDL.CreateIndex(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.CreateIndex(ctx, stmt) - if err != nil { - panic(err) - } - - d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) - return nil -} - -// DropIndex implements the DDL interface. -func (d *Checker) DropIndex(ctx sessionctx.Context, stmt *ast.DropIndexStmt) error { - err := d.realDDL.DropIndex(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.DropIndex(ctx, stmt) - if err != nil { - panic(err) - } - - d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) - return nil -} - -// AlterTable implements the DDL interface. -func (d *Checker) AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast.AlterTableStmt) error { - err := d.realDDL.AlterTable(ctx, sctx, stmt) - if err != nil { - return err - } - - // some unit test will also check warnings, we reset the warnings after SchemaTracker use session context again. - count := sctx.GetSessionVars().StmtCtx.WarningCount() - err = d.tracker.AlterTable(ctx, sctx, stmt) - if err != nil { - panic(err) - } - sctx.GetSessionVars().StmtCtx.TruncateWarnings(int(count)) - - d.checkTableInfo(sctx, stmt.Table.Schema, stmt.Table.Name) - return nil -} - -// TruncateTable implements the DDL interface. -func (*Checker) TruncateTable(_ sessionctx.Context, _ ast.Ident) error { - //TODO implement me - panic("implement me") -} - -// RenameTable implements the DDL interface. -func (d *Checker) RenameTable(ctx sessionctx.Context, stmt *ast.RenameTableStmt) error { - err := d.realDDL.RenameTable(ctx, stmt) - if err != nil { - return err - } - err = d.tracker.RenameTable(ctx, stmt) - if err != nil { - panic(err) - } - - for _, tableName := range stmt.TableToTables { - d.checkTableInfo(ctx, tableName.OldTable.Schema, tableName.OldTable.Name) - d.checkTableInfo(ctx, tableName.NewTable.Schema, tableName.NewTable.Name) - } - return nil -} - -// LockTables implements the DDL interface. -func (d *Checker) LockTables(ctx sessionctx.Context, stmt *ast.LockTablesStmt) error { - return d.realDDL.LockTables(ctx, stmt) -} - -// UnlockTables implements the DDL interface. -func (d *Checker) UnlockTables(ctx sessionctx.Context, lockedTables []model.TableLockTpInfo) error { - return d.realDDL.UnlockTables(ctx, lockedTables) -} - -// CleanupTableLock implements the DDL interface. -func (d *Checker) CleanupTableLock(ctx sessionctx.Context, tables []*ast.TableName) error { - return d.realDDL.CleanupTableLock(ctx, tables) -} - -// UpdateTableReplicaInfo implements the DDL interface. -func (*Checker) UpdateTableReplicaInfo(_ sessionctx.Context, _ int64, _ bool) error { - //TODO implement me - panic("implement me") -} - -// RepairTable implements the DDL interface. -func (*Checker) RepairTable(_ sessionctx.Context, _ *ast.CreateTableStmt) error { - //TODO implement me - panic("implement me") -} - -// CreateSequence implements the DDL interface. -func (*Checker) CreateSequence(_ sessionctx.Context, _ *ast.CreateSequenceStmt) error { - //TODO implement me - panic("implement me") -} - -// DropSequence implements the DDL interface. -func (*Checker) DropSequence(_ sessionctx.Context, _ *ast.DropSequenceStmt) (err error) { - //TODO implement me - panic("implement me") -} - -// AlterSequence implements the DDL interface. -func (*Checker) AlterSequence(_ sessionctx.Context, _ *ast.AlterSequenceStmt) error { - //TODO implement me - panic("implement me") -} - -// CreatePlacementPolicy implements the DDL interface. -func (*Checker) CreatePlacementPolicy(_ sessionctx.Context, _ *ast.CreatePlacementPolicyStmt) error { - //TODO implement me - panic("implement me") -} - -// DropPlacementPolicy implements the DDL interface. -func (*Checker) DropPlacementPolicy(_ sessionctx.Context, _ *ast.DropPlacementPolicyStmt) error { - //TODO implement me - panic("implement me") -} - -// AlterPlacementPolicy implements the DDL interface. -func (*Checker) AlterPlacementPolicy(_ sessionctx.Context, _ *ast.AlterPlacementPolicyStmt) error { - //TODO implement me - panic("implement me") -} - -// AddResourceGroup implements the DDL interface. -// ResourceGroup do not affect the transaction. -func (*Checker) AddResourceGroup(_ sessionctx.Context, _ *ast.CreateResourceGroupStmt) error { - return nil -} - -// DropResourceGroup implements the DDL interface. -func (*Checker) DropResourceGroup(_ sessionctx.Context, _ *ast.DropResourceGroupStmt) error { - return nil -} - -// AlterResourceGroup implements the DDL interface. -func (*Checker) AlterResourceGroup(_ sessionctx.Context, _ *ast.AlterResourceGroupStmt) error { - return nil -} - -// CreateSchemaWithInfo implements the DDL interface. -func (d *Checker) CreateSchemaWithInfo(ctx sessionctx.Context, info *model.DBInfo, onExist ddl.OnExist) error { - err := d.realDDL.CreateSchemaWithInfo(ctx, info, onExist) - if err != nil { - return err - } - err = d.tracker.CreateSchemaWithInfo(ctx, info, onExist) - if err != nil { - panic(err) - } - - d.checkDBInfo(ctx, info.Name) - return nil -} - -// CreateTableWithInfo implements the DDL interface. -func (*Checker) CreateTableWithInfo(_ sessionctx.Context, _ model.CIStr, _ *model.TableInfo, _ ...ddl.CreateTableWithInfoConfigurier) error { - //TODO implement me - panic("implement me") -} - -// BatchCreateTableWithInfo implements the DDL interface. -func (*Checker) BatchCreateTableWithInfo(_ sessionctx.Context, _ model.CIStr, _ []*model.TableInfo, _ ...ddl.CreateTableWithInfoConfigurier) error { - //TODO implement me - panic("implement me") -} - -// CreatePlacementPolicyWithInfo implements the DDL interface. -func (*Checker) CreatePlacementPolicyWithInfo(_ sessionctx.Context, _ *model.PolicyInfo, _ ddl.OnExist) error { - //TODO implement me - panic("implement me") -} - -// Start implements the DDL interface. -func (d *Checker) Start(ctxPool *pools.ResourcePool) error { - return d.realDDL.Start(ctxPool) -} - -// GetLease implements the DDL interface. -func (d *Checker) GetLease() time.Duration { - return d.realDDL.GetLease() -} - -// Stats implements the DDL interface. -func (d *Checker) Stats(vars *variable.SessionVars) (map[string]interface{}, error) { - return d.realDDL.Stats(vars) -} - -// GetScope implements the DDL interface. -func (d *Checker) GetScope(status string) variable.ScopeFlag { - return d.realDDL.GetScope(status) -} - -// Stop implements the DDL interface. -func (d *Checker) Stop() error { - return d.realDDL.Stop() -} - -// RegisterStatsHandle implements the DDL interface. -func (d *Checker) RegisterStatsHandle(h *handle.Handle) { - d.realDDL.RegisterStatsHandle(h) -} - -// SchemaSyncer implements the DDL interface. -func (d *Checker) SchemaSyncer() syncer.SchemaSyncer { - return d.realDDL.SchemaSyncer() -} - -// StateSyncer implements the DDL interface. -func (d *Checker) StateSyncer() syncer.StateSyncer { - return d.realDDL.StateSyncer() -} - -// OwnerManager implements the DDL interface. -func (d *Checker) OwnerManager() owner.Manager { - return d.realDDL.OwnerManager() -} - -// GetID implements the DDL interface. -func (d *Checker) GetID() string { - return d.realDDL.GetID() -} - -// GetTableMaxHandle implements the DDL interface. -func (d *Checker) GetTableMaxHandle(ctx *ddl.JobContext, startTS uint64, tbl table.PhysicalTable) (kv.Handle, bool, error) { - return d.realDDL.GetTableMaxHandle(ctx, startTS, tbl) -} - -// SetBinlogClient implements the DDL interface. -func (d *Checker) SetBinlogClient(client *pumpcli.PumpsClient) { - d.realDDL.SetBinlogClient(client) -} - -// GetHook implements the DDL interface. -func (d *Checker) GetHook() ddl.Callback { - return d.realDDL.GetHook() -} - -// SetHook implements the DDL interface. -func (d *Checker) SetHook(h ddl.Callback) { - d.realDDL.SetHook(h) -} - -// GetInfoSchemaWithInterceptor implements the DDL interface. -func (d *Checker) GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema { - return d.realDDL.GetInfoSchemaWithInterceptor(ctx) -} - -// DoDDLJob implements the DDL interface. -func (d *Checker) DoDDLJob(ctx sessionctx.Context, job *model.Job) error { - return d.realDDL.DoDDLJob(ctx, job) -} - -// StorageDDLInjector wraps kv.Storage to inject checker to domain's DDL in bootstrap time. -type StorageDDLInjector struct { - kv.Storage - kv.EtcdBackend - Injector func(ddl.DDL) *Checker -} - -var _ kv.EtcdBackend = StorageDDLInjector{} - -// EtcdAddrs implements the kv.EtcdBackend interface. -func (s StorageDDLInjector) EtcdAddrs() ([]string, error) { - return s.EtcdBackend.EtcdAddrs() -} - -// TLSConfig implements the kv.EtcdBackend interface. -func (s StorageDDLInjector) TLSConfig() *tls.Config { - return s.EtcdBackend.TLSConfig() -} - -// StartGCWorker implements the kv.EtcdBackend interface. -func (s StorageDDLInjector) StartGCWorker() error { - return s.EtcdBackend.StartGCWorker() -} - -// NewStorageDDLInjector creates a new StorageDDLInjector to inject Checker. -func NewStorageDDLInjector(s kv.Storage) kv.Storage { - ret := StorageDDLInjector{ - Storage: s, - Injector: NewChecker, - } - if ebd, ok := s.(kv.EtcdBackend); ok { - ret.EtcdBackend = ebd - } - return ret -} - -// UnwrapStorage unwraps StorageDDLInjector for one level. -func UnwrapStorage(s kv.Storage) kv.Storage { - injector, ok := s.(StorageDDLInjector) - if !ok { - return s - } - return injector.Storage -} diff --git a/ddl/stat.go b/ddl/stat.go deleted file mode 100644 index cacce4e19167b..0000000000000 --- a/ddl/stat.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "encoding/hex" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/variable" -) - -var ( - serverID = "server_id" - ddlSchemaVersion = "ddl_schema_version" - ddlJobID = "ddl_job_id" - ddlJobAction = "ddl_job_action" - ddlJobStartTS = "ddl_job_start_ts" - ddlJobState = "ddl_job_state" - ddlJobError = "ddl_job_error" - ddlJobRows = "ddl_job_row_count" - ddlJobSchemaState = "ddl_job_schema_state" - ddlJobSchemaID = "ddl_job_schema_id" - ddlJobTableID = "ddl_job_table_id" - ddlJobSnapshotVer = "ddl_job_snapshot_ver" - ddlJobReorgHandle = "ddl_job_reorg_handle" - ddlJobArgs = "ddl_job_args" -) - -// GetScope gets the status variables scope. -func (*ddl) GetScope(_ string) variable.ScopeFlag { - // Now ddl status variables scope are all default scope. - return variable.DefaultStatusVarScopeFlag -} - -// Stats returns the DDL statistics. -func (d *ddl) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { - m := make(map[string]interface{}) - m[serverID] = d.uuid - var ddlInfo *Info - - s, err := d.sessPool.Get() - if err != nil { - return nil, errors.Trace(err) - } - defer d.sessPool.Put(s) - ddlInfo, err = GetDDLInfoWithNewTxn(s) - if err != nil { - return nil, errors.Trace(err) - } - - m[ddlSchemaVersion] = ddlInfo.SchemaVer - // TODO: Get the owner information. - if len(ddlInfo.Jobs) == 0 { - return m, nil - } - // TODO: Add all job information if needed. - job := ddlInfo.Jobs[0] - m[ddlJobID] = job.ID - m[ddlJobAction] = job.Type.String() - m[ddlJobStartTS] = job.StartTS - m[ddlJobState] = job.State.String() - m[ddlJobRows] = job.RowCount - if job.Error == nil { - m[ddlJobError] = "" - } else { - m[ddlJobError] = job.Error.Error() - } - m[ddlJobSchemaState] = job.SchemaState.String() - m[ddlJobSchemaID] = job.SchemaID - m[ddlJobTableID] = job.TableID - m[ddlJobSnapshotVer] = job.SnapshotVer - m[ddlJobReorgHandle] = hex.EncodeToString(ddlInfo.ReorgHandle) - m[ddlJobArgs] = job.Args - return m, nil -} diff --git a/ddl/stat_test.go b/ddl/stat_test.go deleted file mode 100644 index ea28f659b16b7..0000000000000 --- a/ddl/stat_test.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "encoding/hex" - "fmt" - "strconv" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestDDLStatsInfo(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - d := domain.DDL() - - tk := testkit.NewTestKit(t, store) - ctx := tk.Session() - dbInfo, err := testSchemaInfo(store, "test_stat") - require.NoError(t, err) - testCreateSchema(t, ctx, d, dbInfo) - tblInfo, err := testTableInfo(store, "t", 2) - require.NoError(t, err) - testCreateTable(t, ctx, d, dbInfo, tblInfo) - err = sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - - m := testGetTable(t, domain, tblInfo.ID) - // insert t values (1, 1), (2, 2), (3, 3) - _, err = m.AddRecord(ctx, types.MakeDatums(1, 1)) - require.NoError(t, err) - _, err = m.AddRecord(ctx, types.MakeDatums(2, 2)) - require.NoError(t, err) - _, err = m.AddRecord(ctx, types.MakeDatums(3, 3)) - require.NoError(t, err) - ctx.StmtCommit(context.Background()) - require.NoError(t, ctx.CommitTxn(context.Background())) - - job := buildCreateIdxJob(dbInfo, tblInfo, true, "idx", "c1") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum")) - }() - - ctx = testkit.NewTestKit(t, store).Session() - done := make(chan error, 1) - go func() { - ctx.SetValue(sessionctx.QueryString, "skip") - done <- d.DoDDLJob(ctx, job) - }() - - exit := false - // a copy of ddl.ddlJobReorgHandle - ddlJobReorgHandle := "ddl_job_reorg_handle" - for !exit { - select { - case err := <-done: - require.NoError(t, err) - exit = true - case wg := <-ddl.TestCheckWorkerNumCh: - varMap, err := d.Stats(nil) - wg.Done() - require.NoError(t, err) - _, err = hex.DecodeString(varMap[ddlJobReorgHandle].(string)) - require.NoError(t, err) - } - } -} - -func TestGetDDLInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - sess := tk.Session() - tk.MustExec("begin") - txn, err := sess.Txn(true) - require.NoError(t, err) - - dbInfo2 := &model.DBInfo{ - ID: 2, - Name: model.NewCIStr("b"), - State: model.StateNone, - } - job := &model.Job{ - ID: 1, - SchemaID: dbInfo2.ID, - Type: model.ActionCreateSchema, - RowCount: 0, - } - job1 := &model.Job{ - ID: 2, - SchemaID: dbInfo2.ID, - Type: model.ActionAddIndex, - RowCount: 0, - } - - err = addDDLJobs(sess, txn, job) - require.NoError(t, err) - - info, err := ddl.GetDDLInfo(sess) - require.NoError(t, err) - require.Len(t, info.Jobs, 1) - require.Equal(t, job, info.Jobs[0]) - require.Nil(t, info.ReorgHandle) - - // two jobs - err = addDDLJobs(sess, txn, job1) - require.NoError(t, err) - - info, err = ddl.GetDDLInfo(sess) - require.NoError(t, err) - require.Len(t, info.Jobs, 2) - require.Equal(t, job, info.Jobs[0]) - require.Equal(t, job1, info.Jobs[1]) - require.Nil(t, info.ReorgHandle) - - tk.MustExec("rollback") -} - -func addDDLJobs(sess session.Session, txn kv.Transaction, job *model.Job) error { - b, err := job.Encode(true) - if err != nil { - return err - } - _, err = sess.Execute(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), fmt.Sprintf("insert into mysql.tidb_ddl_job(job_id, reorg, schema_ids, table_ids, job_meta, type, processing) values (%d, %t, %s, %s, %s, %d, %t)", - job.ID, job.MayNeedReorg(), strconv.Quote(strconv.FormatInt(job.SchemaID, 10)), strconv.Quote(strconv.FormatInt(job.TableID, 10)), util.WrapKey2String(b), job.Type, false)) - return err -} - -func buildCreateIdxJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, unique bool, indexName string, colName string) *model.Job { - return &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionAddIndex, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{unique, model.NewCIStr(indexName), - []*ast.IndexPartSpecification{{ - Column: &ast.ColumnName{Name: model.NewCIStr(colName)}, - Length: types.UnspecifiedLength}}}, - ReorgMeta: &model.DDLReorgMeta{ // Add index job must have this field. - SQLMode: mysql.SQLMode(0), - Warnings: make(map[errors.ErrorID]*terror.Error), - WarningsCount: make(map[errors.ErrorID]int64), - }, - } -} - -func TestIssue42268(t *testing.T) { - // issue 42268 missing table name in 'admin show ddl' result during drop table - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_0") - tk.MustExec("create table t_0 (c1 int, c2 int)") - - tbl := external.GetTableByName(t, tk, "test", "t_0") - require.NotNil(t, tbl) - require.Equal(t, 2, len(tbl.Cols())) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - - hook := &callback.TestDDLCallback{Do: dom} - hook.OnJobRunBeforeExported = func(job *model.Job) { - if tbl.Meta().ID != job.TableID { - return - } - switch job.SchemaState { - case model.StateNone: - case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: - rs := tk1.MustQuery("admin show ddl jobs") - tblName := fmt.Sprintf("%s", rs.Rows()[0][2]) - require.Equal(t, tblName, "t_0") - } - } - dom.DDL().SetHook(hook) - - tk.MustExec("drop table t_0") -} diff --git a/ddl/syncer/BUILD.bazel b/ddl/syncer/BUILD.bazel deleted file mode 100644 index 08ef784bd79da..0000000000000 --- a/ddl/syncer/BUILD.bazel +++ /dev/null @@ -1,54 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "syncer", - srcs = [ - "state_syncer.go", - "syncer.go", - ], - importpath = "github.com/pingcap/tidb/ddl/syncer", - visibility = ["//visibility:public"], - deps = [ - "//ddl/util", - "//domain/infosync", - "//metrics", - "//sessionctx/variable", - "//util", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@io_etcd_go_etcd_api_v3//mvccpb", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "syncer_test", - timeout = "short", - srcs = [ - "state_syncer_test.go", - "syncer_test.go", - ], - flaky = True, - deps = [ - ":syncer", - "//ddl", - "//ddl/util", - "//infoschema", - "//parser/terror", - "//sessionctx/variable", - "//store/mockstore", - "//util", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_api_v3//mvccpb", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_server_v3//etcdserver", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_golang_google_grpc//codes", - "@org_golang_google_grpc//status", - ], -) diff --git a/ddl/table.go b/ddl/table.go deleted file mode 100644 index 539329771091d..0000000000000 --- a/ddl/table.go +++ /dev/null @@ -1,1934 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - field_types "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - tidb_util "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -const tiflashCheckTiDBHTTPAPIHalfInterval = 2500 * time.Millisecond - -// DANGER: it is an internal function used by onCreateTable and onCreateTables, for reusing code. Be careful. -// 1. it expects the argument of job has been deserialized. -// 2. it won't call updateSchemaVersion, FinishTableJob and asyncNotifyEvent. -func createTable(d *ddlCtx, t *meta.Meta, job *model.Job, fkCheck bool) (*model.TableInfo, error) { - schemaID := job.SchemaID - tbInfo := job.Args[0].(*model.TableInfo) - - tbInfo.State = model.StateNone - err := checkTableNotExists(d, t, schemaID, tbInfo.Name.L) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { - job.State = model.JobStateCancelled - } - return tbInfo, errors.Trace(err) - } - retryable, err := checkTableForeignKeyValidInOwner(d, t, job, tbInfo, fkCheck) - if err != nil { - if !retryable { - job.State = model.JobStateCancelled - } - return tbInfo, errors.Trace(err) - } - // Allocate foreign key ID. - for _, fkInfo := range tbInfo.ForeignKeys { - fkInfo.ID = allocateFKIndexID(tbInfo) - fkInfo.State = model.StatePublic - } - switch tbInfo.State { - case model.StateNone: - // none -> public - tbInfo.State = model.StatePublic - tbInfo.UpdateTS = t.StartTS - err = createTableOrViewWithCheck(t, job, schemaID, tbInfo) - if err != nil { - return tbInfo, errors.Trace(err) - } - - failpoint.Inject("checkOwnerCheckAllVersionsWaitTime", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(tbInfo, errors.New("mock create table error")) - } - }) - - // build table & partition bundles if any. - if err = checkAllTablePlacementPoliciesExistAndCancelNonExistJob(t, job, tbInfo); err != nil { - return tbInfo, errors.Trace(err) - } - - if tbInfo.TiFlashReplica != nil { - replicaInfo := tbInfo.TiFlashReplica - if pi := tbInfo.GetPartitionInfo(); pi != nil { - logutil.BgLogger().Info("Set TiFlash replica pd rule for partitioned table when creating", zap.Int64("tableID", tbInfo.ID)) - if e := infosync.ConfigureTiFlashPDForPartitions(false, &pi.Definitions, replicaInfo.Count, &replicaInfo.LocationLabels, tbInfo.ID); e != nil { - job.State = model.JobStateCancelled - return tbInfo, errors.Trace(e) - } - // Partitions that in adding mid-state. They have high priorities, so we should set accordingly pd rules. - if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.AddingDefinitions, replicaInfo.Count, &replicaInfo.LocationLabels, tbInfo.ID); e != nil { - job.State = model.JobStateCancelled - return tbInfo, errors.Trace(e) - } - } else { - logutil.BgLogger().Info("Set TiFlash replica pd rule when creating", zap.Int64("tableID", tbInfo.ID)) - if e := infosync.ConfigureTiFlashPDForTable(tbInfo.ID, replicaInfo.Count, &replicaInfo.LocationLabels); e != nil { - job.State = model.JobStateCancelled - return tbInfo, errors.Trace(e) - } - } - } - - bundles, err := placement.NewFullTableBundles(t, tbInfo) - if err != nil { - job.State = model.JobStateCancelled - return tbInfo, errors.Trace(err) - } - - // Send the placement bundle to PD. - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) - if err != nil { - job.State = model.JobStateCancelled - return tbInfo, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - return tbInfo, nil - default: - return tbInfo, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tbInfo.State) - } -} - -func onCreateTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - failpoint.Inject("mockExceedErrorLimit", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(ver, errors.New("mock do job error")) - } - }) - - // just decode, createTable will use it as Args[0] - tbInfo := &model.TableInfo{} - fkCheck := false - if err := job.DecodeArgs(tbInfo, &fkCheck); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if len(tbInfo.ForeignKeys) > 0 { - return createTableWithForeignKeys(d, t, job, tbInfo, fkCheck) - } - - tbInfo, err := createTable(d, t, job, fkCheck) - if err != nil { - return ver, errors.Trace(err) - } - - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateTable, TableInfo: tbInfo}) - return ver, errors.Trace(err) -} - -func createTableWithForeignKeys(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, fkCheck bool) (ver int64, err error) { - switch tbInfo.State { - case model.StateNone: - // create table in non-public state - tbInfo, err = createTable(d, t, job, fkCheck) - if err != nil { - return ver, errors.Trace(err) - } - tbInfo.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tbInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StateWriteOnly - case model.StateWriteOnly: - tbInfo.State = model.StatePublic - ver, err = updateVersionAndTableInfo(d, t, job, tbInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - return ver, nil - default: - return ver, errors.Trace(dbterror.ErrInvalidDDLJob.GenWithStackByArgs("table", tbInfo.State)) - } - return ver, errors.Trace(err) -} - -func onCreateTables(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { - var ver int64 - - var args []*model.TableInfo - fkCheck := false - err := job.DecodeArgs(&args, &fkCheck) - if err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - // We don't construct jobs for every table, but only tableInfo - // The following loop creates a stub job for every table - // - // &*job clones a stub job from the ActionCreateTables job - stubJob := &*job - stubJob.Args = make([]interface{}, 1) - for i := range args { - stubJob.TableID = args[i].ID - stubJob.Args[0] = args[i] - tbInfo, err := createTable(d, t, stubJob, fkCheck) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - args[i] = tbInfo - } - - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - - job.State = model.JobStateDone - job.SchemaState = model.StatePublic - job.BinlogInfo.SetTableInfos(ver, args) - - for i := range args { - asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateTable, TableInfo: args[i]}) - } - - return ver, errors.Trace(err) -} - -func createTableOrViewWithCheck(t *meta.Meta, job *model.Job, schemaID int64, tbInfo *model.TableInfo) error { - err := checkTableInfoValid(tbInfo) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - return t.CreateTableOrView(schemaID, tbInfo) -} - -func repairTableOrViewWithCheck(t *meta.Meta, job *model.Job, schemaID int64, tbInfo *model.TableInfo) error { - err := checkTableInfoValid(tbInfo) - if err != nil { - job.State = model.JobStateCancelled - return errors.Trace(err) - } - return t.UpdateTable(schemaID, tbInfo) -} - -func onCreateView(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - tbInfo := &model.TableInfo{} - var orReplace bool - var oldTbInfoID int64 - if err := job.DecodeArgs(tbInfo, &orReplace, &oldTbInfoID); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - tbInfo.State = model.StateNone - err := checkTableNotExists(d, t, schemaID, tbInfo.Name.L) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } else if !infoschema.ErrTableExists.Equal(err) { - return ver, errors.Trace(err) - } - if !orReplace { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - switch tbInfo.State { - case model.StateNone: - // none -> public - tbInfo.State = model.StatePublic - tbInfo.UpdateTS = t.StartTS - if oldTbInfoID > 0 && orReplace { - err = t.DropTableOrView(schemaID, oldTbInfoID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - err = t.GetAutoIDAccessors(schemaID, oldTbInfoID).Del() - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - err = createTableOrViewWithCheck(t, job, schemaID, tbInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateView, TableInfo: tbInfo}) - return ver, nil - default: - return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tbInfo.State) - } -} - -func onDropTableOrView(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - tblInfo, err := checkTableExistAndCancelNonExistJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - originalState := job.SchemaState - switch tblInfo.State { - case model.StatePublic: - // public -> write only - if job.Type == model.ActionDropTable { - err = checkDropTableHasForeignKeyReferredInOwner(d, t, job) - if err != nil { - return ver, err - } - } - tblInfo.State = model.StateWriteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateWriteOnly: - // write only -> delete only - tblInfo.State = model.StateDeleteOnly - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - case model.StateDeleteOnly: - tblInfo.State = model.StateNone - oldIDs := getPartitionIDs(tblInfo) - ruleIDs := append(getPartitionRuleIDs(job.SchemaName, tblInfo), fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L)) - job.CtxVars = []interface{}{oldIDs} - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) - if err != nil { - return ver, errors.Trace(err) - } - if tblInfo.IsSequence() { - if err = t.DropSequence(job.SchemaID, job.TableID); err != nil { - return ver, errors.Trace(err) - } - } else { - if err = t.DropTableOrView(job.SchemaID, job.TableID); err != nil { - return ver, errors.Trace(err) - } - if err = t.GetAutoIDAccessors(job.SchemaID, job.TableID).Del(); err != nil { - return ver, errors.Trace(err) - } - } - if tblInfo.TiFlashReplica != nil { - e := infosync.DeleteTiFlashTableSyncProgress(tblInfo) - if e != nil { - logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(e)) - } - } - // Placement rules cannot be removed immediately after drop table / truncate table, because the - // tables can be flashed back or recovered, therefore it moved to doGCPlacementRules in gc_worker.go. - - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) - startKey := tablecodec.EncodeTablePrefix(job.TableID) - job.Args = append(job.Args, startKey, oldIDs, ruleIDs) - if tblInfo.IsSequence() { - asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropSequence, TableInfo: tblInfo}) - } else if !tblInfo.IsView() { - asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropTable, TableInfo: tblInfo}) - } - default: - return ver, errors.Trace(dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State)) - } - job.SchemaState = tblInfo.State - return ver, errors.Trace(err) -} - -func (w *worker) onRecoverTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - var ( - recoverInfo *RecoverInfo - recoverTableCheckFlag int64 - ) - if err = job.DecodeArgs(&recoverInfo, &recoverTableCheckFlag); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - schemaID := recoverInfo.SchemaID - tblInfo := recoverInfo.TableInfo - if tblInfo.TTLInfo != nil { - // force disable TTL job schedule for recovered table - tblInfo.TTLInfo.Enable = false - } - - // check GC and safe point - gcEnable, err := checkGCEnable(w) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = checkTableNotExists(d, t, schemaID, tblInfo.Name.L) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { - job.State = model.JobStateCancelled - } - return ver, errors.Trace(err) - } - - err = checkTableIDNotExists(t, schemaID, tblInfo.ID) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { - job.State = model.JobStateCancelled - } - return ver, errors.Trace(err) - } - - // Recover table divide into 2 steps: - // 1. Check GC enable status, to decided whether enable GC after recover table. - // a. Why not disable GC before put the job to DDL job queue? - // Think about concurrency problem. If a recover job-1 is doing and already disabled GC, - // then, another recover table job-2 check GC enable will get disable before into the job queue. - // then, after recover table job-2 finished, the GC will be disabled. - // b. Why split into 2 steps? 1 step also can finish this job: check GC -> disable GC -> recover table -> finish job. - // What if the transaction commit failed? then, the job will retry, but the GC already disabled when first running. - // So, after this job retry succeed, the GC will be disabled. - // 2. Do recover table job. - // a. Check whether GC enabled, if enabled, disable GC first. - // b. Check GC safe point. If drop table time if after safe point time, then can do recover. - // otherwise, can't recover table, because the records of the table may already delete by gc. - // c. Remove GC task of the table from gc_delete_range table. - // d. Create table and rebase table auto ID. - // e. Finish. - switch tblInfo.State { - case model.StateNone: - // none -> write only - // check GC enable and update flag. - if gcEnable { - job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagEnableGC - } else { - job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagDisableGC - } - - // Clear all placement when recover - err = clearTablePlacementAndBundles(tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - job.SchemaState = model.StateWriteOnly - tblInfo.State = model.StateWriteOnly - case model.StateWriteOnly: - // write only -> public - // do recover table. - if gcEnable { - err = disableGC(w) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Errorf("disable gc failed, try again later. err: %v", err) - } - } - // check GC safe point - err = checkSafePoint(w, recoverInfo.SnapshotTS) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - ver, err = w.recoverTable(t, job, recoverInfo) - if err != nil { - return ver, errors.Trace(err) - } - tableInfo := tblInfo.Clone() - tableInfo.State = model.StatePublic - tableInfo.UpdateTS = t.StartTS - ver, err = updateVersionAndTableInfo(d, t, job, tableInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - tblInfo.State = model.StatePublic - tblInfo.UpdateTS = t.StartTS - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - default: - return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State) - } - return ver, nil -} - -func (w *worker) recoverTable(t *meta.Meta, job *model.Job, recoverInfo *RecoverInfo) (ver int64, err error) { - var tids []int64 - if recoverInfo.TableInfo.GetPartitionInfo() != nil { - tids = getPartitionIDs(recoverInfo.TableInfo) - tids = append(tids, recoverInfo.TableInfo.ID) - } else { - tids = []int64{recoverInfo.TableInfo.ID} - } - tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err := getOldLabelRules(recoverInfo.TableInfo, recoverInfo.OldSchemaName, recoverInfo.OldTableName) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to get old label rules from PD") - } - // Remove dropped table DDL job from gc_delete_range table. - err = w.delRangeManager.removeFromGCDeleteRange(w.ctx, recoverInfo.DropJobID) - if err != nil { - return ver, errors.Trace(err) - } - tableInfo := recoverInfo.TableInfo.Clone() - tableInfo.State = model.StatePublic - tableInfo.UpdateTS = t.StartTS - err = t.CreateTableAndSetAutoID(recoverInfo.SchemaID, tableInfo, recoverInfo.AutoIDs.RowID, recoverInfo.AutoIDs.RandomID) - if err != nil { - return ver, errors.Trace(err) - } - - failpoint.Inject("mockRecoverTableCommitErr", func(val failpoint.Value) { - if val.(bool) && atomic.CompareAndSwapUint32(&mockRecoverTableCommitErrOnce, 0, 1) { - err = failpoint.Enable(`tikvclient/mockCommitErrorOpt`, "return(true)") - if err != nil { - return - } - } - }) - - err = updateLabelRules(job, recoverInfo.TableInfo, oldRules, tableRuleID, partRuleIDs, oldRuleIDs, recoverInfo.TableInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to update the label rule to PD") - } - job.CtxVars = []interface{}{tids} - return ver, nil -} - -func clearTablePlacementAndBundles(tblInfo *model.TableInfo) error { - var bundles []*placement.Bundle - if tblInfo.PlacementPolicyRef != nil { - tblInfo.PlacementPolicyRef = nil - bundles = append(bundles, placement.NewBundle(tblInfo.ID)) - } - - if tblInfo.Partition != nil { - for i := range tblInfo.Partition.Definitions { - par := &tblInfo.Partition.Definitions[i] - if par.PlacementPolicyRef != nil { - par.PlacementPolicyRef = nil - bundles = append(bundles, placement.NewBundle(par.ID)) - } - } - } - - if len(bundles) == 0 { - return nil - } - - return infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) -} - -// mockRecoverTableCommitErrOnce uses to make sure -// `mockRecoverTableCommitErr` only mock error once. -var mockRecoverTableCommitErrOnce uint32 - -func enableGC(w *worker) error { - ctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - return gcutil.EnableGC(ctx) -} - -func disableGC(w *worker) error { - ctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - return gcutil.DisableGC(ctx) -} - -func checkGCEnable(w *worker) (enable bool, err error) { - ctx, err := w.sessPool.Get() - if err != nil { - return false, errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - return gcutil.CheckGCEnable(ctx) -} - -func checkSafePoint(w *worker, snapshotTS uint64) error { - ctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - return gcutil.ValidateSnapshot(ctx, snapshotTS) -} - -func getTable(store kv.Storage, schemaID int64, tblInfo *model.TableInfo) (table.Table, error) { - allocs := autoid.NewAllocatorsFromTblInfo(store, schemaID, tblInfo) - tbl, err := table.TableFromMeta(allocs, tblInfo) - return tbl, errors.Trace(err) -} - -// GetTableInfoAndCancelFaultJob is exported for test. -func GetTableInfoAndCancelFaultJob(t *meta.Meta, job *model.Job, schemaID int64) (*model.TableInfo, error) { - tblInfo, err := checkTableExistAndCancelNonExistJob(t, job, schemaID) - if err != nil { - return nil, errors.Trace(err) - } - - if tblInfo.State != model.StatePublic { - job.State = model.JobStateCancelled - return nil, dbterror.ErrInvalidDDLState.GenWithStack("table %s is not in public, but %s", tblInfo.Name, tblInfo.State) - } - - return tblInfo, nil -} - -func checkTableExistAndCancelNonExistJob(t *meta.Meta, job *model.Job, schemaID int64) (*model.TableInfo, error) { - tblInfo, err := getTableInfo(t, job.TableID, schemaID) - if err == nil { - // Check if table name is renamed. - if job.TableName != "" && tblInfo.Name.L != job.TableName && job.Type != model.ActionRepairTable { - job.State = model.JobStateCancelled - return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(job.SchemaName, job.TableName) - } - return tblInfo, nil - } - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableNotExists.Equal(err) { - job.State = model.JobStateCancelled - } - return nil, err -} - -func getTableInfo(t *meta.Meta, tableID, schemaID int64) (*model.TableInfo, error) { - // Check this table's database. - tblInfo, err := t.GetTable(schemaID, tableID) - if err != nil { - if meta.ErrDBNotExists.Equal(err) { - return nil, errors.Trace(infoschema.ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", schemaID), - )) - } - return nil, errors.Trace(err) - } - - // Check the table. - if tblInfo == nil { - return nil, errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", schemaID), - fmt.Sprintf("(Table ID %d)", tableID), - )) - } - return tblInfo, nil -} - -// onTruncateTable delete old table meta, and creates a new table identical to old table except for table ID. -// As all the old data is encoded with old table ID, it can not be accessed anymore. -// A background job will be created to delete old data. -func (w *worker) onTruncateTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - tableID := job.TableID - var newTableID int64 - var fkCheck bool - var newPartitionIDs []int64 - err := job.DecodeArgs(&newTableID, &fkCheck, &newPartitionIDs) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return ver, errors.Trace(err) - } - if tblInfo.IsView() || tblInfo.IsSequence() { - job.State = model.JobStateCancelled - return ver, infoschema.ErrTableNotExists.GenWithStackByArgs(job.SchemaName, tblInfo.Name.O) - } - err = checkTruncateTableHasForeignKeyReferredInOwner(d, t, job, tblInfo, fkCheck) - if err != nil { - return ver, err - } - err = t.DropTableOrView(schemaID, tblInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - err = t.GetAutoIDAccessors(schemaID, tblInfo.ID).Del() - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - failpoint.Inject("truncateTableErr", func(val failpoint.Value) { - if val.(bool) { - job.State = model.JobStateCancelled - failpoint.Return(ver, errors.New("occur an error after dropping table")) - } - }) - - // Clear the TiFlash replica progress from ETCD. - if tblInfo.TiFlashReplica != nil { - e := infosync.DeleteTiFlashTableSyncProgress(tblInfo) - if e != nil { - logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(e)) - } - } - - var oldPartitionIDs []int64 - if tblInfo.GetPartitionInfo() != nil { - oldPartitionIDs = getPartitionIDs(tblInfo) - // We use the new partition ID because all the old data is encoded with the old partition ID, it can not be accessed anymore. - err = truncateTableByReassignPartitionIDs(t, tblInfo, newPartitionIDs) - if err != nil { - return ver, errors.Trace(err) - } - } - - if pi := tblInfo.GetPartitionInfo(); pi != nil { - oldIDs := make([]int64, 0, len(oldPartitionIDs)) - newIDs := make([]int64, 0, len(oldPartitionIDs)) - newDefs := pi.Definitions - for i := range oldPartitionIDs { - newDef := &newDefs[i] - newID := newDef.ID - if newDef.PlacementPolicyRef != nil { - oldIDs = append(oldIDs, oldPartitionIDs[i]) - newIDs = append(newIDs, newID) - } - } - job.CtxVars = []interface{}{oldIDs, newIDs} - } - - tableRuleID, partRuleIDs, _, oldRules, err := getOldLabelRules(tblInfo, job.SchemaName, tblInfo.Name.L) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Wrapf(err, "failed to get old label rules from PD") - } - - err = updateLabelRules(job, tblInfo, oldRules, tableRuleID, partRuleIDs, []string{}, newTableID) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Wrapf(err, "failed to update the label rule to PD") - } - - // Clear the TiFlash replica available status. - if tblInfo.TiFlashReplica != nil { - // Set PD rules for TiFlash - if pi := tblInfo.GetPartitionInfo(); pi != nil { - if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.Definitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); e != nil { - logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) - job.State = model.JobStateCancelled - return ver, errors.Trace(e) - } - } else { - if e := infosync.ConfigureTiFlashPDForTable(newTableID, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels); e != nil { - logutil.BgLogger().Error("ConfigureTiFlashPDForTable fails", zap.Error(err)) - job.State = model.JobStateCancelled - return ver, errors.Trace(e) - } - } - tblInfo.TiFlashReplica.AvailablePartitionIDs = nil - tblInfo.TiFlashReplica.Available = false - } - - tblInfo.ID = newTableID - - // build table & partition bundles if any. - bundles, err := placement.NewFullTableBundles(t, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - err = t.CreateTableOrView(schemaID, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - failpoint.Inject("mockTruncateTableUpdateVersionError", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(ver, errors.New("mock update version error")) - } - }) - - var partitions []model.PartitionDefinition - if pi := tblInfo.GetPartitionInfo(); pi != nil { - partitions = tblInfo.GetPartitionInfo().Definitions - } - preSplitAndScatter(w.sess.Context, d.store, tblInfo, partitions) - - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTable, TableInfo: tblInfo}) - startKey := tablecodec.EncodeTablePrefix(tableID) - job.Args = []interface{}{startKey, oldPartitionIDs} - return ver, nil -} - -func onRebaseAutoIncrementIDType(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - return onRebaseAutoID(d, d.store, t, job, autoid.AutoIncrementType) -} - -func onRebaseAutoRandomType(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - return onRebaseAutoID(d, d.store, t, job, autoid.AutoRandomType) -} - -func onRebaseAutoID(d *ddlCtx, store kv.Storage, t *meta.Meta, job *model.Job, tp autoid.AllocatorType) (ver int64, _ error) { - schemaID := job.SchemaID - var ( - newBase int64 - force bool - ) - err := job.DecodeArgs(&newBase, &force) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - return ver, nil - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tbl, err := getTable(store, schemaID, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if !force { - newBaseTemp, err := adjustNewBaseToNextGlobalID(nil, tbl, tp, newBase) - if err != nil { - return ver, errors.Trace(err) - } - if newBase != newBaseTemp { - job.Warning = toTError(fmt.Errorf("Can't reset AUTO_INCREMENT to %d without FORCE option, using %d instead", - newBase, newBaseTemp, - )) - } - newBase = newBaseTemp - } - - if tp == autoid.AutoIncrementType { - tblInfo.AutoIncID = newBase - } else { - tblInfo.AutoRandID = newBase - } - - if alloc := tbl.Allocators(nil).Get(tp); alloc != nil { - // The next value to allocate is `newBase`. - newEnd := newBase - 1 - if force { - err = alloc.ForceRebase(newEnd) - } else { - err = alloc.Rebase(context.Background(), newEnd, false) - } - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func onModifyTableAutoIDCache(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { - var cache int64 - if err := job.DecodeArgs(&cache); err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, errors.Trace(err) - } - - tblInfo.AutoIdCache = cache - ver, err := updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func (w *worker) onShardRowID(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var shardRowIDBits uint64 - err := job.DecodeArgs(&shardRowIDBits) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - if shardRowIDBits < tblInfo.ShardRowIDBits { - tblInfo.ShardRowIDBits = shardRowIDBits - } else { - tbl, err := getTable(d.store, job.SchemaID, tblInfo) - if err != nil { - return ver, errors.Trace(err) - } - err = verifyNoOverflowShardBits(w.sessPool, tbl, shardRowIDBits) - if err != nil { - job.State = model.JobStateCancelled - return ver, err - } - tblInfo.ShardRowIDBits = shardRowIDBits - // MaxShardRowIDBits use to check the overflow of auto ID. - tblInfo.MaxShardRowIDBits = shardRowIDBits - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func verifyNoOverflowShardBits(s *sess.Pool, tbl table.Table, shardRowIDBits uint64) error { - if shardRowIDBits == 0 { - return nil - } - ctx, err := s.Get() - if err != nil { - return errors.Trace(err) - } - defer s.Put(ctx) - // Check next global max auto ID first. - autoIncID, err := tbl.Allocators(ctx).Get(autoid.RowIDAllocType).NextGlobalAutoID() - if err != nil { - return errors.Trace(err) - } - if tables.OverflowShardBits(autoIncID, shardRowIDBits, autoid.RowIDBitLength, true) { - return autoid.ErrAutoincReadFailed.GenWithStack("shard_row_id_bits %d will cause next global auto ID %v overflow", shardRowIDBits, autoIncID) - } - return nil -} - -func onRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var oldSchemaID int64 - var oldSchemaName model.CIStr - var tableName model.CIStr - if err := job.DecodeArgs(&oldSchemaID, &tableName, &oldSchemaName); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if job.SchemaState == model.StatePublic { - return finishJobRenameTable(d, t, job) - } - newSchemaID := job.SchemaID - err := checkTableNotExists(d, t, newSchemaID, tableName.L) - if err != nil { - if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { - job.State = model.JobStateCancelled - } - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, oldSchemaID) - if err != nil { - return ver, errors.Trace(err) - } - oldTableName := tblInfo.Name - ver, err = checkAndRenameTables(t, job, tblInfo, oldSchemaID, job.SchemaID, &oldSchemaName, &tableName) - if err != nil { - return ver, errors.Trace(err) - } - fkh := newForeignKeyHelper() - err = adjustForeignKeyChildTableInfoAfterRenameTable(d, t, job, &fkh, tblInfo, oldSchemaName, oldTableName, tableName, newSchemaID) - if err != nil { - return ver, errors.Trace(err) - } - ver, err = updateSchemaVersion(d, t, job, fkh.getLoadedTables()...) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StatePublic - return ver, nil -} - -func onRenameTables(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - oldSchemaIDs := []int64{} - newSchemaIDs := []int64{} - tableNames := []*model.CIStr{} - tableIDs := []int64{} - oldSchemaNames := []*model.CIStr{} - oldTableNames := []*model.CIStr{} - if err := job.DecodeArgs(&oldSchemaIDs, &newSchemaIDs, &tableNames, &tableIDs, &oldSchemaNames, &oldTableNames); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if job.SchemaState == model.StatePublic { - return finishJobRenameTables(d, t, job, tableNames, tableIDs, newSchemaIDs) - } - - var tblInfos = make([]*model.TableInfo, 0, len(tableNames)) - var err error - fkh := newForeignKeyHelper() - for i, oldSchemaID := range oldSchemaIDs { - job.TableID = tableIDs[i] - job.TableName = oldTableNames[i].L - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, oldSchemaID) - if err != nil { - return ver, errors.Trace(err) - } - ver, err := checkAndRenameTables(t, job, tblInfo, oldSchemaID, newSchemaIDs[i], oldSchemaNames[i], tableNames[i]) - if err != nil { - return ver, errors.Trace(err) - } - err = adjustForeignKeyChildTableInfoAfterRenameTable(d, t, job, &fkh, tblInfo, *oldSchemaNames[i], *oldTableNames[i], *tableNames[i], newSchemaIDs[i]) - if err != nil { - return ver, errors.Trace(err) - } - tblInfos = append(tblInfos, tblInfo) - } - - ver, err = updateSchemaVersion(d, t, job, fkh.getLoadedTables()...) - if err != nil { - return ver, errors.Trace(err) - } - job.SchemaState = model.StatePublic - return ver, nil -} - -func checkAndRenameTables(t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, oldSchemaID, newSchemaID int64, oldSchemaName, tableName *model.CIStr) (ver int64, _ error) { - err := t.DropTableOrView(oldSchemaID, tblInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - failpoint.Inject("renameTableErr", func(val failpoint.Value) { - if valStr, ok := val.(string); ok { - if tableName.L == valStr { - job.State = model.JobStateCancelled - failpoint.Return(ver, errors.New("occur an error after renaming table")) - } - } - }) - - oldTableName := tblInfo.Name - tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err := getOldLabelRules(tblInfo, oldSchemaName.L, oldTableName.L) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to get old label rules from PD") - } - - tblInfo.Name = *tableName - err = t.CreateTableOrView(newSchemaID, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if newSchemaID != oldSchemaID { - oldDBID := tblInfo.GetDBID(oldSchemaID) - err := meta.BackupAndRestoreAutoIDs(t, oldDBID, tblInfo.ID, newSchemaID, tblInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // It's compatible with old version. - // TODO: Remove it. - tblInfo.OldSchemaID = 0 - } - - err = updateLabelRules(job, tblInfo, oldRules, tableRuleID, partRuleIDs, oldRuleIDs, tblInfo.ID) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to update the label rule to PD") - } - - return ver, nil -} - -func adjustForeignKeyChildTableInfoAfterRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job, fkh *foreignKeyHelper, tblInfo *model.TableInfo, oldSchemaName, oldTableName, newTableName model.CIStr, newSchemaID int64) error { - if !variable.EnableForeignKey.Load() || newTableName.L == oldTableName.L { - return nil - } - is, err := getAndCheckLatestInfoSchema(d, t) - if err != nil { - return err - } - newDB, ok := is.SchemaByID(newSchemaID) - if !ok { - job.State = model.JobStateCancelled - return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(fmt.Sprintf("schema-ID: %v", newSchemaID)) - } - referredFKs := is.GetTableReferredForeignKeys(oldSchemaName.L, oldTableName.L) - if len(referredFKs) == 0 { - return nil - } - fkh.addLoadedTable(oldSchemaName.L, oldTableName.L, newDB.ID, tblInfo) - for _, referredFK := range referredFKs { - childTableInfo, err := fkh.getTableFromStorage(is, t, referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - if infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err) { - continue - } - return err - } - childFKInfo := model.FindFKInfoByName(childTableInfo.tblInfo.ForeignKeys, referredFK.ChildFKName.L) - if childFKInfo == nil { - continue - } - childFKInfo.RefSchema = newDB.Name - childFKInfo.RefTable = newTableName - } - for _, info := range fkh.loaded { - err = updateTable(t, info.schemaID, info.tblInfo) - if err != nil { - return err - } - } - return nil -} - -// We split the renaming table job into two steps: -// 1. rename table and update the schema version. -// 2. update the job state to JobStateDone. -// This is the requirement from TiCDC because -// - it uses the job state to check whether the DDL is finished. -// - there is a gap between schema reloading and job state updating: -// when the job state is updated to JobStateDone, before the new schema reloaded, -// there may be DMLs that use the old schema. -// - TiCDC cannot handle the DMLs that use the old schema, because -// the commit TS of the DMLs are greater than the job state updating TS. -func finishJobRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { - tblInfo, err := getTableInfo(t, job.TableID, job.SchemaID) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - // Before updating the schema version, we need to reset the old schema ID to new schema ID, so that - // the table info can be dropped normally in `ApplyDiff`. This is because renaming table requires two - // schema versions to complete. - oldRawArgs := job.RawArgs - job.Args[0] = job.SchemaID - job.RawArgs, err = json.Marshal(job.Args) - if err != nil { - return 0, errors.Trace(err) - } - ver, err := updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - job.RawArgs = oldRawArgs - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func finishJobRenameTables(d *ddlCtx, t *meta.Meta, job *model.Job, - tableNames []*model.CIStr, tableIDs, newSchemaIDs []int64) (int64, error) { - tblSchemaIDs := make(map[int64]int64, len(tableIDs)) - for i := range tableIDs { - tblSchemaIDs[tableIDs[i]] = newSchemaIDs[i] - } - tblInfos := make([]*model.TableInfo, 0, len(tableNames)) - for i := range tableIDs { - tblID := tableIDs[i] - tblInfo, err := getTableInfo(t, tblID, tblSchemaIDs[tblID]) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - tblInfos = append(tblInfos, tblInfo) - } - // Before updating the schema version, we need to reset the old schema ID to new schema ID, so that - // the table info can be dropped normally in `ApplyDiff`. This is because renaming table requires two - // schema versions to complete. - var err error - oldRawArgs := job.RawArgs - job.Args[0] = newSchemaIDs - job.RawArgs, err = json.Marshal(job.Args) - if err != nil { - return 0, errors.Trace(err) - } - ver, err := updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - job.RawArgs = oldRawArgs - job.FinishMultipleTableJob(model.JobStateDone, model.StatePublic, ver, tblInfos) - return ver, nil -} - -func onModifyTableComment(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var comment string - if err := job.DecodeArgs(&comment); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - return ver, nil - } - - tblInfo.Comment = comment - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func onModifyTableCharsetAndCollate(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var toCharset, toCollate string - var needsOverwriteCols bool - if err := job.DecodeArgs(&toCharset, &toCollate, &needsOverwriteCols); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) - if err != nil { - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - // double check. - _, err = checkAlterTableCharset(tblInfo, dbInfo, toCharset, toCollate, needsOverwriteCols) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { - job.MarkNonRevertible() - return ver, nil - } - - tblInfo.Charset = toCharset - tblInfo.Collate = toCollate - - if needsOverwriteCols { - // update column charset. - for _, col := range tblInfo.Columns { - if field_types.HasCharset(&col.FieldType) { - col.SetCharset(toCharset) - col.SetCollate(toCollate) - } else { - col.SetCharset(charset.CharsetBin) - col.SetCollate(charset.CharsetBin) - } - } - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func (w *worker) onSetTableFlashReplica(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var replicaInfo ast.TiFlashReplicaSpec - if err := job.DecodeArgs(&replicaInfo); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - // Ban setting replica count for tables in system database. - if tidb_util.IsMemOrSysDB(job.SchemaName) { - return ver, errors.Trace(dbterror.ErrUnsupportedTiFlashOperationForSysOrMemTable) - } - - err = w.checkTiFlashReplicaCount(replicaInfo.Count) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - // We should check this first, in order to avoid creating redundant DDL jobs. - if pi := tblInfo.GetPartitionInfo(); pi != nil { - logutil.BgLogger().Info("Set TiFlash replica pd rule for partitioned table", zap.Int64("tableID", tblInfo.ID)) - if e := infosync.ConfigureTiFlashPDForPartitions(false, &pi.Definitions, replicaInfo.Count, &replicaInfo.Labels, tblInfo.ID); e != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(e) - } - // Partitions that in adding mid-state. They have high priorities, so we should set accordingly pd rules. - if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.AddingDefinitions, replicaInfo.Count, &replicaInfo.Labels, tblInfo.ID); e != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(e) - } - } else { - logutil.BgLogger().Info("Set TiFlash replica pd rule", zap.Int64("tableID", tblInfo.ID)) - if e := infosync.ConfigureTiFlashPDForTable(tblInfo.ID, replicaInfo.Count, &replicaInfo.Labels); e != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(e) - } - } - - available := false - if tblInfo.TiFlashReplica != nil { - available = tblInfo.TiFlashReplica.Available - } - if replicaInfo.Count > 0 { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: replicaInfo.Count, - LocationLabels: replicaInfo.Labels, - Available: available, - } - } else { - if tblInfo.TiFlashReplica != nil { - err = infosync.DeleteTiFlashTableSyncProgress(tblInfo) - if err != nil { - logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(err)) - } - } - tblInfo.TiFlashReplica = nil - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func (w *worker) checkTiFlashReplicaCount(replicaCount uint64) error { - ctx, err := w.sessPool.Get() - if err != nil { - return errors.Trace(err) - } - defer w.sessPool.Put(ctx) - - return checkTiFlashReplicaCount(ctx, replicaCount) -} - -func onUpdateFlashReplicaStatus(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - var available bool - var physicalID int64 - if err := job.DecodeArgs(&available, &physicalID); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - if tblInfo.TiFlashReplica == nil || (tblInfo.ID == physicalID && tblInfo.TiFlashReplica.Available == available) || - (tblInfo.ID != physicalID && available == tblInfo.TiFlashReplica.IsPartitionAvailable(physicalID)) { - job.State = model.JobStateCancelled - return ver, errors.Errorf("the replica available status of table %s is already updated", tblInfo.Name.String()) - } - - if tblInfo.ID == physicalID { - tblInfo.TiFlashReplica.Available = available - } else if pi := tblInfo.GetPartitionInfo(); pi != nil { - // Partition replica become available. - if available { - allAvailable := true - for _, p := range pi.Definitions { - if p.ID == physicalID { - tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, physicalID) - } - allAvailable = allAvailable && tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID) - } - tblInfo.TiFlashReplica.Available = allAvailable - } else { - // Partition replica become unavailable. - for i, id := range tblInfo.TiFlashReplica.AvailablePartitionIDs { - if id == physicalID { - newIDs := tblInfo.TiFlashReplica.AvailablePartitionIDs[:i] - newIDs = append(newIDs, tblInfo.TiFlashReplica.AvailablePartitionIDs[i+1:]...) - tblInfo.TiFlashReplica.AvailablePartitionIDs = newIDs - tblInfo.TiFlashReplica.Available = false - logutil.BgLogger().Info("TiFlash replica become unavailable", zap.Int64("tableID", tblInfo.ID), zap.Int64("partitionID", id)) - break - } - } - } - } else { - job.State = model.JobStateCancelled - return ver, errors.Errorf("unknown physical ID %v in table %v", physicalID, tblInfo.Name.O) - } - - if tblInfo.TiFlashReplica.Available { - logutil.BgLogger().Info("TiFlash replica available", zap.Int64("tableID", tblInfo.ID)) - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func checkTableNotExists(d *ddlCtx, t *meta.Meta, schemaID int64, tableName string) error { - // Try to use memory schema info to check first. - currVer, err := t.GetSchemaVersion() - if err != nil { - return err - } - is := d.infoCache.GetLatest() - if is.SchemaMetaVersion() == currVer { - return checkTableNotExistsFromInfoSchema(is, schemaID, tableName) - } - - return checkTableNotExistsFromStore(t, schemaID, tableName) -} - -func checkTableIDNotExists(t *meta.Meta, schemaID, tableID int64) error { - tbl, err := t.GetTable(schemaID, tableID) - if err != nil { - if meta.ErrDBNotExists.Equal(err) { - return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") - } - return errors.Trace(err) - } - if tbl != nil { - return infoschema.ErrTableExists.GenWithStackByArgs(tbl.Name) - } - return nil -} - -func checkTableNotExistsFromInfoSchema(is infoschema.InfoSchema, schemaID int64, tableName string) error { - // Check this table's database. - schema, ok := is.SchemaByID(schemaID) - if !ok { - return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") - } - if is.TableExists(schema.Name, model.NewCIStr(tableName)) { - return infoschema.ErrTableExists.GenWithStackByArgs(tableName) - } - return nil -} - -func checkTableNotExistsFromStore(t *meta.Meta, schemaID int64, tableName string) error { - // Check this table's database. - tbls, err := t.ListTables(schemaID) - if err != nil { - if meta.ErrDBNotExists.Equal(err) { - return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") - } - return errors.Trace(err) - } - - // Check the table. - for _, tbl := range tbls { - if tbl.Name.L == tableName { - return infoschema.ErrTableExists.GenWithStackByArgs(tbl.Name) - } - } - - return nil -} - -// updateVersionAndTableInfoWithCheck checks table info validate and updates the schema version and the table information -func updateVersionAndTableInfoWithCheck(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, shouldUpdateVer bool, multiInfos ...schemaIDAndTableInfo) ( - ver int64, err error) { - err = checkTableInfoValid(tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - for _, info := range multiInfos { - err = checkTableInfoValid(info.tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - } - return updateVersionAndTableInfo(d, t, job, tblInfo, shouldUpdateVer, multiInfos...) -} - -// updateVersionAndTableInfo updates the schema version and the table information. -func updateVersionAndTableInfo(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, shouldUpdateVer bool, multiInfos ...schemaIDAndTableInfo) ( - ver int64, err error) { - failpoint.Inject("mockUpdateVersionAndTableInfoErr", func(val failpoint.Value) { - switch val.(int) { - case 1: - failpoint.Return(ver, errors.New("mock update version and tableInfo error")) - case 2: - // We change it cancelled directly here, because we want to get the original error with the job id appended. - // The job ID will be used to get the job from history queue and we will assert it's args. - job.State = model.JobStateCancelled - failpoint.Return(ver, errors.New("mock update version and tableInfo error, jobID="+strconv.Itoa(int(job.ID)))) - default: - } - }) - if shouldUpdateVer && (job.MultiSchemaInfo == nil || !job.MultiSchemaInfo.SkipVersion) { - ver, err = updateSchemaVersion(d, t, job, multiInfos...) - if err != nil { - return 0, errors.Trace(err) - } - } - - err = updateTable(t, job.SchemaID, tblInfo) - if err != nil { - return 0, errors.Trace(err) - } - for _, info := range multiInfos { - err = updateTable(t, info.schemaID, info.tblInfo) - if err != nil { - return 0, errors.Trace(err) - } - } - return ver, nil -} - -func updateTable(t *meta.Meta, schemaID int64, tblInfo *model.TableInfo) error { - if tblInfo.State == model.StatePublic { - tblInfo.UpdateTS = t.StartTS - } - return t.UpdateTable(schemaID, tblInfo) -} - -type schemaIDAndTableInfo struct { - schemaID int64 - tblInfo *model.TableInfo -} - -func onRepairTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { - schemaID := job.SchemaID - tblInfo := &model.TableInfo{} - - if err := job.DecodeArgs(tblInfo); err != nil { - // Invalid arguments, cancel this job. - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tblInfo.State = model.StateNone - - // Check the old DB and old table exist. - _, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) - if err != nil { - return ver, errors.Trace(err) - } - - // When in repair mode, the repaired table in a server is not access to user, - // the table after repairing will be removed from repair list. Other server left - // behind alive may need to restart to get the latest schema version. - ver, err = updateSchemaVersion(d, t, job) - if err != nil { - return ver, errors.Trace(err) - } - switch tblInfo.State { - case model.StateNone: - // none -> public - tblInfo.State = model.StatePublic - tblInfo.UpdateTS = t.StartTS - err = repairTableOrViewWithCheck(t, job, schemaID, tblInfo) - if err != nil { - return ver, errors.Trace(err) - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - asyncNotifyEvent(d, &util.Event{Tp: model.ActionRepairTable, TableInfo: tblInfo}) - return ver, nil - default: - return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State) - } -} - -func onAlterTableAttributes(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - rule := label.NewRule() - err = job.DecodeArgs(rule) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, err - } - - if len(rule.Labels) == 0 { - patch := label.NewRulePatch([]*label.Rule{}, []string{rule.ID}) - err = infosync.UpdateLabelRules(context.TODO(), patch) - } else { - err = infosync.PutLabelRule(context.TODO(), rule) - } - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Wrapf(err, "failed to notify PD the label rules") - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - - return ver, nil -} - -func onAlterTablePartitionAttributes(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - var partitionID int64 - rule := label.NewRule() - err = job.DecodeArgs(&partitionID, rule) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, err - } - - ptInfo := tblInfo.GetPartitionInfo() - if ptInfo.GetNameByID(partitionID) == "" { - job.State = model.JobStateCancelled - return 0, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O)) - } - - if len(rule.Labels) == 0 { - patch := label.NewRulePatch([]*label.Rule{}, []string{rule.ID}) - err = infosync.UpdateLabelRules(context.TODO(), patch) - } else { - err = infosync.PutLabelRule(context.TODO(), rule) - } - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Wrapf(err, "failed to notify PD the label rules") - } - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - - return ver, nil -} - -func onAlterTablePartitionPlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - var partitionID int64 - policyRefInfo := &model.PolicyRefInfo{} - err = job.DecodeArgs(&partitionID, &policyRefInfo) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, err - } - - ptInfo := tblInfo.GetPartitionInfo() - var partitionDef *model.PartitionDefinition - definitions := ptInfo.Definitions - oldPartitionEnablesPlacement := false - for i := range definitions { - if partitionID == definitions[i].ID { - def := &definitions[i] - oldPartitionEnablesPlacement = def.PlacementPolicyRef != nil - def.PlacementPolicyRef = policyRefInfo - partitionDef = &definitions[i] - break - } - } - - if partitionDef == nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O)) - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, partitionDef.PlacementPolicyRef); err != nil { - return ver, errors.Trace(err) - } - - bundle, err := placement.NewPartitionBundle(t, *partitionDef) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if bundle == nil && oldPartitionEnablesPlacement { - bundle = placement.NewBundle(partitionDef.ID) - } - - // Send the placement bundle to PD. - if bundle != nil { - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), []*placement.Bundle{bundle}) - } - - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Wrapf(err, "failed to notify PD the placement rules") - } - - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func onAlterTablePlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - policyRefInfo := &model.PolicyRefInfo{} - err = job.DecodeArgs(&policyRefInfo) - if err != nil { - job.State = model.JobStateCancelled - return 0, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, err - } - - if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, policyRefInfo); err != nil { - return 0, errors.Trace(err) - } - - oldTableEnablesPlacement := tblInfo.PlacementPolicyRef != nil - tblInfo.PlacementPolicyRef = policyRefInfo - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - - bundle, err := placement.NewTableBundle(t, tblInfo) - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - if bundle == nil && oldTableEnablesPlacement { - bundle = placement.NewBundle(tblInfo.ID) - } - - // Send the placement bundle to PD. - if bundle != nil { - err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), []*placement.Bundle{bundle}) - } - - if err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - - return ver, nil -} - -func getOldLabelRules(tblInfo *model.TableInfo, oldSchemaName, oldTableName string) (string, []string, []string, map[string]*label.Rule, error) { - tableRuleID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, oldSchemaName, oldTableName) - oldRuleIDs := []string{tableRuleID} - var partRuleIDs []string - if tblInfo.GetPartitionInfo() != nil { - for _, def := range tblInfo.GetPartitionInfo().Definitions { - partRuleIDs = append(partRuleIDs, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, oldSchemaName, oldTableName, def.Name.L)) - } - } - - oldRuleIDs = append(oldRuleIDs, partRuleIDs...) - oldRules, err := infosync.GetLabelRules(context.TODO(), oldRuleIDs) - return tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err -} - -func updateLabelRules(job *model.Job, tblInfo *model.TableInfo, oldRules map[string]*label.Rule, tableRuleID string, partRuleIDs, oldRuleIDs []string, tID int64) error { - if oldRules == nil { - return nil - } - var newRules []*label.Rule - if tblInfo.GetPartitionInfo() != nil { - for idx, def := range tblInfo.GetPartitionInfo().Definitions { - if r, ok := oldRules[partRuleIDs[idx]]; ok { - newRules = append(newRules, r.Clone().Reset(job.SchemaName, tblInfo.Name.L, def.Name.L, def.ID)) - } - } - } - ids := []int64{tID} - if r, ok := oldRules[tableRuleID]; ok { - if tblInfo.GetPartitionInfo() != nil { - for _, def := range tblInfo.GetPartitionInfo().Definitions { - ids = append(ids, def.ID) - } - } - newRules = append(newRules, r.Clone().Reset(job.SchemaName, tblInfo.Name.L, "", ids...)) - } - - patch := label.NewRulePatch(newRules, oldRuleIDs) - return infosync.UpdateLabelRules(context.TODO(), patch) -} - -func onAlterCacheTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - tbInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, errors.Trace(err) - } - // If the table is already in the cache state - if tbInfo.TableCacheStatusType == model.TableCacheStatusEnable { - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - return ver, nil - } - - if tbInfo.TempTableType != model.TempTableNone { - return ver, errors.Trace(dbterror.ErrOptOnTemporaryTable.GenWithStackByArgs("alter temporary table cache")) - } - - if tbInfo.Partition != nil { - return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("partition mode")) - } - - switch tbInfo.TableCacheStatusType { - case model.TableCacheStatusDisable: - // disable -> switching - tbInfo.TableCacheStatusType = model.TableCacheStatusSwitching - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) - if err != nil { - return ver, err - } - case model.TableCacheStatusSwitching: - // switching -> enable - tbInfo.TableCacheStatusType = model.TableCacheStatusEnable - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) - if err != nil { - return ver, err - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - default: - job.State = model.JobStateCancelled - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("alter table cache", tbInfo.TableCacheStatusType.String()) - } - return ver, err -} - -func onAlterNoCacheTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - tbInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return 0, errors.Trace(err) - } - // If the table is not in the cache state - if tbInfo.TableCacheStatusType == model.TableCacheStatusDisable { - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - return ver, nil - } - - switch tbInfo.TableCacheStatusType { - case model.TableCacheStatusEnable: - // enable -> switching - tbInfo.TableCacheStatusType = model.TableCacheStatusSwitching - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) - if err != nil { - return ver, err - } - case model.TableCacheStatusSwitching: - // switching -> disable - tbInfo.TableCacheStatusType = model.TableCacheStatusDisable - ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) - if err != nil { - return ver, err - } - // Finish this job. - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) - default: - job.State = model.JobStateCancelled - err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("alter table no cache", tbInfo.TableCacheStatusType.String()) - } - return ver, err -} diff --git a/ddl/table_test.go b/ddl/table_test.go deleted file mode 100644 index dce990b07828c..0000000000000 --- a/ddl/table_test.go +++ /dev/null @@ -1,580 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func testRenameTable( - t *testing.T, - ctx sessionctx.Context, - d ddl.DDL, - newSchemaID, oldSchemaID int64, - oldSchemaName model.CIStr, - tblInfo *model.TableInfo, -) *model.Job { - job := &model.Job{ - SchemaID: newSchemaID, - TableID: tblInfo.ID, - Type: model.ActionRenameTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{oldSchemaID, tblInfo.Name, oldSchemaName}, - CtxVars: []interface{}{[]int64{oldSchemaID, newSchemaID}, []int64{tblInfo.ID}}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v := getSchemaVer(t, ctx) - tblInfo.State = model.StatePublic - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - tblInfo.State = model.StateNone - return job -} - -func testRenameTables(t *testing.T, ctx sessionctx.Context, d ddl.DDL, oldSchemaIDs, newSchemaIDs []int64, newTableNames []*model.CIStr, oldTableIDs []int64, oldSchemaNames, oldTableNames []*model.CIStr) *model.Job { - job := &model.Job{ - Type: model.ActionRenameTables, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{oldSchemaIDs, newSchemaIDs, newTableNames, oldTableIDs, oldSchemaNames, oldTableNames}, - CtxVars: []interface{}{append(oldSchemaIDs, newSchemaIDs...), oldTableIDs}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) - return job -} - -func testLockTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo, lockTp model.TableLockType) *model.Job { - arg := &ddl.LockTablesArg{ - LockTables: []model.TableLockTpInfo{{SchemaID: newSchemaID, TableID: tblInfo.ID, Tp: lockTp}}, - SessionInfo: model.SessionInfo{ - ServerID: d.GetID(), - SessionID: ctx.GetSessionVars().ConnectionID, - }, - } - job := &model.Job{ - SchemaID: newSchemaID, - TableID: tblInfo.ID, - Type: model.ActionLockTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{arg}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) - return job -} - -func checkTableLockedTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo, serverID string, sessionID uint64, lockTp model.TableLockType) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - tt := meta.NewMeta(txn) - info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) - require.NoError(t, err) - - require.NotNil(t, info) - require.NotNil(t, info.Lock) - require.Len(t, info.Lock.Sessions, 1) - require.Equal(t, serverID, info.Lock.Sessions[0].ServerID) - require.Equal(t, sessionID, info.Lock.Sessions[0].SessionID) - require.Equal(t, lockTp, info.Lock.Tp) - require.Equal(t, lockTp, info.Lock.Tp) - require.Equal(t, model.TableLockStatePublic, info.Lock.State) - return nil - }) - require.NoError(t, err) -} - -func testTruncateTable(t *testing.T, ctx sessionctx.Context, store kv.Storage, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo) *model.Job { - genIDs, err := genGlobalIDs(store, 1) - require.NoError(t, err) - newTableID := genIDs[0] - job := &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionTruncateTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{newTableID}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v := getSchemaVer(t, ctx) - tblInfo.ID = newTableID - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) - return job -} - -func testGetTableWithError(store kv.Storage, schemaID, tableID int64) (table.Table, error) { - var tblInfo *model.TableInfo - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - var err1 error - tblInfo, err1 = t.GetTable(schemaID, tableID) - if err1 != nil { - return errors.Trace(err1) - } - return nil - }) - if err != nil { - return nil, errors.Trace(err) - } - if tblInfo == nil { - return nil, errors.New("table not found") - } - alloc := autoid.NewAllocator(store, schemaID, tblInfo.ID, false, autoid.RowIDAllocType) - tbl, err := table.TableFromMeta(autoid.NewAllocators(false, alloc), tblInfo) - if err != nil { - return nil, errors.Trace(err) - } - return tbl, nil -} - -func TestTable(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - dbInfo, err := testSchemaInfo(store, "test_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), domain.DDL(), dbInfo) - - ctx := testkit.NewTestKit(t, store).Session() - - tblInfo, err := testTableInfo(store, "t", 3) - require.NoError(t, err) - job := testCreateTable(t, ctx, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // Create an existing table. - newTblInfo, err := testTableInfo(store, "t", 3) - require.NoError(t, err) - doDDLJobErr(t, dbInfo.ID, newTblInfo.ID, model.ActionCreateTable, []interface{}{newTblInfo}, ctx, d, store) - - ctx = testkit.NewTestKit(t, store).Session() - require.NoError(t, sessiontxn.NewTxn(context.Background(), ctx)) - count := 2000 - tbl := testGetTable(t, domain, tblInfo.ID) - for i := 1; i <= count; i++ { - _, err := tbl.AddRecord(ctx, types.MakeDatums(i, i, i)) - require.NoError(t, err) - } - require.NoError(t, ctx.CommitTxn(context.Background())) - - jobID := testDropTable(testkit.NewTestKit(t, store), t, dbInfo.Name.L, tblInfo.Name.L, domain) - testCheckJobDone(t, store, jobID, false) - - // for truncate table - tblInfo, err = testTableInfo(store, "tt", 3) - require.NoError(t, err) - job = testCreateTable(t, ctx, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - job = testTruncateTable(t, ctx, store, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // for rename table - dbInfo1, err := testSchemaInfo(store, "test_rename_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo1) - job = testRenameTable(t, ctx, d, dbInfo1.ID, dbInfo.ID, dbInfo.Name, tblInfo) - testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - job = testLockTable(t, ctx, d, dbInfo1.ID, tblInfo, model.TableLockWrite) - testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - checkTableLockedTest(t, store, dbInfo1, tblInfo, d.GetID(), ctx.GetSessionVars().ConnectionID, model.TableLockWrite) - // for alter cache table - job = testAlterCacheTable(t, ctx, d, dbInfo1.ID, tblInfo) - testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - checkTableCacheTest(t, store, dbInfo1, tblInfo) - // for alter no cache table - job = testAlterNoCacheTable(t, ctx, d, dbInfo1.ID, tblInfo) - testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - checkTableNoCacheTest(t, store, dbInfo1, tblInfo) - - testDropSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) -} - -func TestCreateView(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - dbInfo, err := testSchemaInfo(store, "test_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), domain.DDL(), dbInfo) - - ctx := testkit.NewTestKit(t, store).Session() - - tblInfo, err := testTableInfo(store, "t", 3) - require.NoError(t, err) - job := testCreateTable(t, ctx, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // Create a view - newTblInfo0, err := testTableInfo(store, "v", 3) - require.NoError(t, err) - job = &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionCreateView, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{newTblInfo0}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v := getSchemaVer(t, ctx) - tblInfo.State = model.StatePublic - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: newTblInfo0}) - tblInfo.State = model.StateNone - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // Replace a view - newTblInfo1, err := testTableInfo(store, "v", 3) - require.NoError(t, err) - job = &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionCreateView, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{newTblInfo1, true, newTblInfo0.ID}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v = getSchemaVer(t, ctx) - tblInfo.State = model.StatePublic - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: newTblInfo1}) - tblInfo.State = model.StateNone - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // Replace a view with a non-existing table id - newTblInfo2, err := testTableInfo(store, "v", 3) - require.NoError(t, err) - job = &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionCreateView, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{newTblInfo2, true, newTblInfo0.ID}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.Error(t, err) -} - -func checkTableCacheTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - tt := meta.NewMeta(txn) - info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) - require.NoError(t, err) - require.NotNil(t, info) - require.NotNil(t, info.TableCacheStatusType) - require.Equal(t, model.TableCacheStatusEnable, info.TableCacheStatusType) - return nil - })) -} - -func checkTableNoCacheTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - tt := meta.NewMeta(txn) - info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) - require.NoError(t, err) - require.NotNil(t, info) - require.Equal(t, model.TableCacheStatusDisable, info.TableCacheStatusType) - return nil - })) -} - -func testAlterCacheTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { - job := &model.Job{ - SchemaID: newSchemaID, - TableID: tblInfo.ID, - Type: model.ActionAlterCacheTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err := d.DoDDLJob(ctx, job) - require.NoError(t, err) - - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) - return job -} - -func testAlterNoCacheTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { - job := &model.Job{ - SchemaID: newSchemaID, - TableID: tblInfo.ID, - Type: model.ActionAlterNoCacheTable, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) - return job -} - -func TestRenameTables(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - - dbInfo, err := testSchemaInfo(store, "test_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) - - ctx := testkit.NewTestKit(t, store).Session() - var tblInfos = make([]*model.TableInfo, 0, 2) - var newTblInfos = make([]*model.TableInfo, 0, 2) - for i := 1; i < 3; i++ { - tableName := fmt.Sprintf("t%d", i) - tblInfo, err := testTableInfo(store, tableName, 3) - require.NoError(t, err) - job := testCreateTable(t, ctx, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - tblInfos = append(tblInfos, tblInfo) - - newTableName := fmt.Sprintf("tt%d", i) - tblInfo, err = testTableInfo(store, newTableName, 3) - require.NoError(t, err) - newTblInfos = append(newTblInfos, tblInfo) - } - - job := testRenameTables(t, ctx, d, []int64{dbInfo.ID, dbInfo.ID}, []int64{dbInfo.ID, dbInfo.ID}, []*model.CIStr{&newTblInfos[0].Name, &newTblInfos[1].Name}, []int64{tblInfos[0].ID, tblInfos[1].ID}, []*model.CIStr{&dbInfo.Name, &dbInfo.Name}, []*model.CIStr{&tblInfos[0].Name, &tblInfos[1].Name}) - - historyJob, err := ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) - require.NoError(t, err) - wantTblInfos := historyJob.BinlogInfo.MultipleTableInfos - require.Equal(t, wantTblInfos[0].Name.L, "tt1") - require.Equal(t, wantTblInfos[1].Name.L, "tt2") -} - -func TestCreateTables(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - - dbInfo, err := testSchemaInfo(store, "test_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) - - ctx := testkit.NewTestKit(t, store).Session() - - var infos []*model.TableInfo - genIDs, err := genGlobalIDs(store, 3) - require.NoError(t, err) - - infos = append(infos, &model.TableInfo{ - ID: genIDs[0], - Name: model.NewCIStr("s1"), - }) - infos = append(infos, &model.TableInfo{ - ID: genIDs[1], - Name: model.NewCIStr("s2"), - }) - infos = append(infos, &model.TableInfo{ - ID: genIDs[2], - Name: model.NewCIStr("s3"), - }) - - job := &model.Job{ - SchemaID: dbInfo.ID, - Type: model.ActionCreateTables, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{infos}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - err = d.DoDDLJob(ctx, job) - require.NoError(t, err) - - testGetTable(t, domain, genIDs[0]) - testGetTable(t, domain, genIDs[1]) - testGetTable(t, domain, genIDs[2]) -} - -func TestAlterTTL(t *testing.T) { - store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) - - d := domain.DDL() - - dbInfo, err := testSchemaInfo(store, "test_table") - require.NoError(t, err) - testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) - - ctx := testkit.NewTestKit(t, store).Session() - - // initialize a table with ttlInfo - tableName := "t" - tblInfo, err := testTableInfo(store, tableName, 2) - require.NoError(t, err) - tblInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeDatetime) - tblInfo.Columns[1].FieldType = *types.NewFieldType(mysql.TypeDatetime) - tblInfo.TTLInfo = &model.TTLInfo{ - ColumnName: tblInfo.Columns[0].Name, - IntervalExprStr: "5", - IntervalTimeUnit: int(ast.TimeUnitDay), - } - - // create table - job := testCreateTable(t, ctx, d, dbInfo, tblInfo) - testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) - testCheckJobDone(t, store, job.ID, true) - - // submit ddl job to modify ttlInfo - tableInfoAfterAlterTTLInfo := tblInfo.Clone() - require.NoError(t, err) - tableInfoAfterAlterTTLInfo.TTLInfo = &model.TTLInfo{ - ColumnName: tblInfo.Columns[1].Name, - IntervalExprStr: "1", - IntervalTimeUnit: int(ast.TimeUnitYear), - } - - job = &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionAlterTTLInfo, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{&model.TTLInfo{ - ColumnName: tblInfo.Columns[1].Name, - IntervalExprStr: "1", - IntervalTimeUnit: int(ast.TimeUnitYear), - }}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v := getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) - - // assert the ddlInfo as expected - historyJob, err := ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) - require.NoError(t, err) - require.Equal(t, tableInfoAfterAlterTTLInfo.TTLInfo, historyJob.BinlogInfo.TableInfo.TTLInfo) - - // submit a ddl job to modify ttlEnabled - job = &model.Job{ - SchemaID: dbInfo.ID, - TableID: tblInfo.ID, - Type: model.ActionAlterTTLRemove, - BinlogInfo: &model.HistoryInfo{}, - Args: []interface{}{true}, - } - ctx.SetValue(sessionctx.QueryString, "skip") - require.NoError(t, d.DoDDLJob(ctx, job)) - - v = getSchemaVer(t, ctx) - checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) - - // assert the ddlInfo as expected - historyJob, err = ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) - require.NoError(t, err) - require.Empty(t, historyJob.BinlogInfo.TableInfo.TTLInfo) -} - -func TestRenameTableIntermediateState(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - originHook := dom.DDL().GetHook() - tk.MustExec("create database db1;") - tk.MustExec("create database db2;") - tk.MustExec("create table db1.t(a int);") - - testCases := []struct { - renameSQL string - insertSQL string - errMsg string - finalDB string - }{ - {"rename table db1.t to db1.t1;", "insert into db1.t values(1);", "[schema:1146]Table 'db1.t' doesn't exist", "db1.t1"}, - {"rename table db1.t1 to db1.t;", "insert into db1.t values(1);", "", "db1.t"}, - {"rename table db1.t to db2.t;", "insert into db1.t values(1);", "[schema:1146]Table 'db1.t' doesn't exist", "db2.t"}, - {"rename table db2.t to db1.t;", "insert into db1.t values(1);", "", "db1.t"}, - } - - for _, tc := range testCases { - hook := &callback.TestDDLCallback{Do: dom} - runInsert := false - fn := func(job *model.Job) { - if job.Type == model.ActionRenameTable && - job.SchemaState == model.StatePublic && !runInsert && !t.Failed() { - _, err := tk2.Exec(tc.insertSQL) - if len(tc.errMsg) > 0 { - assert.NotNil(t, err) - assert.Equal(t, tc.errMsg, err.Error()) - } else { - assert.NoError(t, err) - } - runInsert = true - } - } - hook.OnJobUpdatedExported.Store(&fn) - dom.DDL().SetHook(hook) - tk.MustExec(tc.renameSQL) - result := tk.MustQuery(fmt.Sprintf("select * from %s;", tc.finalDB)) - if len(tc.errMsg) > 0 { - result.Check(testkit.Rows()) - } else { - result.Check(testkit.Rows("1")) - } - tk.MustExec(fmt.Sprintf("delete from %s;", tc.finalDB)) - } - dom.DDL().SetHook(originHook) -} diff --git a/ddl/tests/adminpause/BUILD.bazel b/ddl/tests/adminpause/BUILD.bazel deleted file mode 100644 index 74887fa7df207..0000000000000 --- a/ddl/tests/adminpause/BUILD.bazel +++ /dev/null @@ -1,50 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "adminpause", - srcs = [ - "ddl_data_generation.go", - "ddl_stmt_cases.go", - "global.go", - ], - importpath = "github.com/pingcap/tidb/ddl/tests/adminpause", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//domain", - "//parser/model", - "//testkit", - "//util/logutil", - ], -) - -go_test( - name = "adminpause_test", - timeout = "moderate", - srcs = [ - "main_test.go", - "pause_cancel_test.go", - "pause_negative_test.go", - "pause_resume_test.go", - ], - embed = [":adminpause"], - flaky = True, - shard_count = 14, - deps = [ - "//config", - "//ddl", - "//ddl/testutil", - "//ddl/util/callback", - "//domain", - "//errno", - "//parser/model", - "//testkit", - "//testkit/testsetup", - "//util/sqlexec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ddl/tests/adminpause/main_test.go b/ddl/tests/adminpause/main_test.go deleted file mode 100644 index f625e38ea3724..0000000000000 --- a/ddl/tests/adminpause/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package adminpause - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/fail/BUILD.bazel b/ddl/tests/fail/BUILD.bazel deleted file mode 100644 index d5cfbf757e9aa..0000000000000 --- a/ddl/tests/fail/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "fail_test", - timeout = "short", - srcs = [ - "fail_db_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 12, - deps = [ - "//config", - "//ddl", - "//ddl/schematracker", - "//ddl/testutil", - "//ddl/util", - "//domain", - "//kv", - "//parser/model", - "//session", - "//sessionctx/variable", - "//store/mockstore", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/fail/main_test.go b/ddl/tests/fail/main_test.go deleted file mode 100644 index ea302666aeb02..0000000000000 --- a/ddl/tests/fail/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/fk/BUILD.bazel b/ddl/tests/fk/BUILD.bazel deleted file mode 100644 index aaf1316283d6d..0000000000000 --- a/ddl/tests/fk/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "fk_test", - timeout = "short", - srcs = [ - "foreign_key_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 25, - deps = [ - "//config", - "//ddl", - "//domain", - "//infoschema", - "//meta", - "//meta/autoid", - "//parser/auth", - "//parser/model", - "//planner/core", - "//sessiontxn", - "//testkit", - "//testkit/testsetup", - "//util/dbterror", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/fk/foreign_key_test.go b/ddl/tests/fk/foreign_key_test.go deleted file mode 100644 index f2744097cf096..0000000000000 --- a/ddl/tests/fk/foreign_key_test.go +++ /dev/null @@ -1,1837 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "bytes" - "context" - "fmt" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror" - "github.com/stretchr/testify/require" -) - -func TestCreateTableWithForeignKeyMetaInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int,b int as (a) virtual);") - tk.MustExec("create database test2") - tk.MustExec("use test2") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id) ON UPDATE RESTRICT ON DELETE CASCADE)") - tb1Info := getTableInfo(t, dom, "test", "t1") - tb2Info := getTableInfo(t, dom, "test2", "t2") - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test", "t1"))) - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) - require.Equal(t, 0, len(tb1Info.ForeignKeys)) - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t2"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb1ReferredFKs[0]) - tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tb2Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_b"), - RefSchema: model.NewCIStr("test"), - RefTable: model.NewCIStr("t1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("b")}, - OnDelete: 2, - OnUpdate: 1, - State: model.StatePublic, - Version: 1, - }, *tb2Info.ForeignKeys[0]) - // Auto create index for foreign key usage. - require.Equal(t, 1, len(tb2Info.Indices)) - require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) - require.Equal(t, "`test2`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb2Info.ForeignKeys[0].String("test2", "t2")) - - tk.MustExec("create table t3 (id int, b int, index idx_b(b), foreign key fk_b(b) references t2(id) ON UPDATE SET NULL ON DELETE NO ACTION)") - tb2Info = getTableInfo(t, dom, "test2", "t2") - tb3Info := getTableInfo(t, dom, "test2", "t3") - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) - require.Equal(t, 1, len(tb2Info.ForeignKeys)) - tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test2", "t2") - require.Equal(t, 1, len(tb2ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t3"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb2ReferredFKs[0]) - tb3ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t3") - require.Equal(t, 0, len(tb3ReferredFKs)) - require.Equal(t, 1, len(tb3Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_b"), - RefSchema: model.NewCIStr("test2"), - RefTable: model.NewCIStr("t2"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("b")}, - OnDelete: 4, - OnUpdate: 3, - State: model.StatePublic, - Version: 1, - }, *tb3Info.ForeignKeys[0]) - require.Equal(t, 1, len(tb3Info.Indices)) - require.Equal(t, "idx_b", tb3Info.Indices[0].Name.L) - require.Equal(t, "`test2`.`t3`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `t2` (`id`) ON DELETE NO ACTION ON UPDATE SET NULL", tb3Info.ForeignKeys[0].String("test2", "t3")) - - tk.MustExec("create table t5 (id int key, a int, b int, foreign key (a) references t5(id));") - tb5Info := getTableInfo(t, dom, "test2", "t5") - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) - require.Equal(t, 1, len(tb5Info.ForeignKeys)) - tb5ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t5") - require.Equal(t, 1, len(tb5ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t5"), - ChildFKName: model.NewCIStr("fk_1"), - }, *tb5ReferredFKs[0]) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_1"), - RefSchema: model.NewCIStr("test2"), - RefTable: model.NewCIStr("t5"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - State: model.StatePublic, - Version: 1, - }, *tb5Info.ForeignKeys[0]) - require.Equal(t, 1, len(tb5Info.Indices)) - require.Equal(t, "fk_1", tb5Info.Indices[0].Name.L) - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test", "t1"))) - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) - require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) - - tk.MustExec("set @@global.tidb_enable_foreign_key=0") - tk.MustExec("drop database test2") - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) - require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) -} - -func TestCreateTableWithForeignKeyMetaInfo2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("create database test2") - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec("use test2") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id) ON UPDATE RESTRICT ON DELETE CASCADE)") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") - tb1Info := getTableInfo(t, dom, "test", "t1") - tb2Info := getTableInfo(t, dom, "test2", "t2") - require.Equal(t, 0, len(tb1Info.ForeignKeys)) - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t2"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb1ReferredFKs[0]) - tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tb2Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_b"), - RefSchema: model.NewCIStr("test"), - RefTable: model.NewCIStr("t1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("b")}, - OnDelete: 2, - OnUpdate: 1, - State: model.StatePublic, - Version: 1, - }, *tb2Info.ForeignKeys[0]) - // Auto create index for foreign key usage. - require.Equal(t, 1, len(tb2Info.Indices)) - require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) - require.Equal(t, "`test2`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb2Info.ForeignKeys[0].String("test2", "t2")) - - tk.MustExec("create table t3 (id int key, a int, foreign key fk_a(a) references test.t1(id) ON DELETE CASCADE ON UPDATE RESTRICT, foreign key fk_a2(a) references test2.t2(id))") - tb1Info = getTableInfo(t, dom, "test", "t1") - tb3Info := getTableInfo(t, dom, "test", "t3") - require.Equal(t, 0, len(tb1Info.ForeignKeys)) - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 2, len(tb1ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test"), - ChildTable: model.NewCIStr("t3"), - ChildFKName: model.NewCIStr("fk_a"), - }, *tb1ReferredFKs[0]) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t2"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb1ReferredFKs[1]) - tb3ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t3") - require.Equal(t, 0, len(tb3ReferredFKs)) - require.Equal(t, 2, len(tb3Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_a"), - RefSchema: model.NewCIStr("test"), - RefTable: model.NewCIStr("t1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - OnDelete: 2, - OnUpdate: 1, - State: model.StatePublic, - Version: 1, - }, *tb3Info.ForeignKeys[0]) - require.Equal(t, model.FKInfo{ - ID: 2, - Name: model.NewCIStr("fk_a2"), - RefSchema: model.NewCIStr("test2"), - RefTable: model.NewCIStr("t2"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - State: model.StatePublic, - Version: 1, - }, *tb3Info.ForeignKeys[1]) - // Auto create index for foreign key usage. - require.Equal(t, 1, len(tb3Info.Indices)) - require.Equal(t, "fk_a", tb3Info.Indices[0].Name.L) - require.Equal(t, "`test`.`t3`, CONSTRAINT `fk_a` FOREIGN KEY (`a`) REFERENCES `t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb3Info.ForeignKeys[0].String("test", "t3")) - require.Equal(t, "`test`.`t3`, CONSTRAINT `fk_a2` FOREIGN KEY (`a`) REFERENCES `test2`.`t2` (`id`)", tb3Info.ForeignKeys[1].String("test", "t3")) - - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec("drop table test2.t2") - tb1Info = getTableInfo(t, dom, "test", "t1") - tb3Info = getTableInfo(t, dom, "test", "t3") - require.Equal(t, 0, len(tb1Info.ForeignKeys)) - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test"), - ChildTable: model.NewCIStr("t3"), - ChildFKName: model.NewCIStr("fk_a"), - }, *tb1ReferredFKs[0]) - tb3ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t3") - require.Equal(t, 0, len(tb3ReferredFKs)) - require.Equal(t, 2, len(tb3Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_a"), - RefSchema: model.NewCIStr("test"), - RefTable: model.NewCIStr("t1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - OnDelete: 2, - OnUpdate: 1, - State: model.StatePublic, - Version: 1, - }, *tb3Info.ForeignKeys[0]) - require.Equal(t, model.FKInfo{ - ID: 2, - Name: model.NewCIStr("fk_a2"), - RefSchema: model.NewCIStr("test2"), - RefTable: model.NewCIStr("t2"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - State: model.StatePublic, - Version: 1, - }, *tb3Info.ForeignKeys[1]) -} - -func TestCreateTableWithForeignKeyMetaInfo3(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - tk.MustExec("create table t3 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - tk.MustExec("create table t4 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - tk.MustExec("drop table t3") - tk.MustExec("create table t5 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - require.Equal(t, 3, len(tb1ReferredFKs)) - require.Equal(t, "t2", tb1ReferredFKs[0].ChildTable.L) - require.Equal(t, "t3", tb1ReferredFKs[1].ChildTable.L) - require.Equal(t, "t4", tb1ReferredFKs[2].ChildTable.L) -} - -func TestCreateTableWithForeignKeyPrivilegeCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create user 'u1'@'%' identified by '';") - tk.MustExec("grant create on *.* to 'u1'@'%';") - tk.MustExec("create table t1 (id int key);") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - err := tk2.ExecToErr("create table t2 (a int, foreign key fk(a) references t1(id));") - require.Error(t, err) - require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) - - tk.MustExec("grant references on test.t1 to 'u1'@'%';") - tk2.MustExec("create table t2 (a int, foreign key fk(a) references t1(id));") - tk2.MustExec("create table t3 (id int key)") - err = tk2.ExecToErr("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") - require.Error(t, err) - require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't3'", err.Error()) - - tk.MustExec("grant references on test.t3 to 'u1'@'%';") - tk2.MustExec("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") -} - -func TestAlterTableWithForeignKeyPrivilegeCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create user 'u1'@'%' identified by '';") - tk.MustExec("grant create,alter on *.* to 'u1'@'%';") - tk.MustExec("create table t1 (id int key);") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - tk2.MustExec("create table t2 (a int)") - err := tk2.ExecToErr("alter table t2 add foreign key (a) references t1 (id) on update cascade") - require.Error(t, err) - require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) - tk.MustExec("grant references on test.t1 to 'u1'@'%';") - tk2.MustExec("alter table t2 add foreign key (a) references t1 (id) on update cascade") -} - -func TestRenameTableWithForeignKeyMetaInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("create database test2") - tk.MustExec("create database test3") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))") - tk.MustExec("rename table test.t1 to test2.t2") - // check the schema diff - diff := getLatestSchemaDiff(t, tk) - require.Equal(t, model.ActionRenameTable, diff.Type) - require.Equal(t, 0, len(diff.AffectedOpts)) - tk.MustQuery("show create table test2.t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + - " `id` int(11) NOT NULL,\n" + - " `a` int(11) DEFAULT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `fk` (`a`),\n" + - " CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `test2`.`t2` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tblInfo := getTableInfo(t, dom, "test2", "t2") - tbReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") - require.Equal(t, 1, len(tblInfo.ForeignKeys)) - require.Equal(t, 1, len(tbReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("t2"), - ChildFKName: model.NewCIStr("fk"), - }, *tbReferredFKs[0]) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk"), - RefSchema: model.NewCIStr("test2"), - RefTable: model.NewCIStr("t2"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("a")}, - State: model.StatePublic, - Version: 1, - }, *tblInfo.ForeignKeys[0]) - - tk.MustExec("drop table test2.t2") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - tk.MustExec("use test2") - tk.MustExec("rename table test.t2 to test2.tt2") - // check the schema diff - diff = getLatestSchemaDiff(t, tk) - require.Equal(t, model.ActionRenameTable, diff.Type) - require.Equal(t, 0, len(diff.AffectedOpts)) - tb1Info := getTableInfo(t, dom, "test", "t1") - tb2Info := getTableInfo(t, dom, "test2", "tt2") - require.Equal(t, 0, len(tb1Info.ForeignKeys)) - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("tt2"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb1ReferredFKs[0]) - tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tb2Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_b"), - RefSchema: model.NewCIStr("test"), - RefTable: model.NewCIStr("t1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("b")}, - State: model.StatePublic, - Version: 1, - }, *tb2Info.ForeignKeys[0]) - // Auto create index for foreign key usage. - require.Equal(t, 1, len(tb2Info.Indices)) - require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) - require.Equal(t, "`test2`.`tt2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`)", tb2Info.ForeignKeys[0].String("test2", "tt2")) - - tk.MustExec("rename table test.t1 to test3.tt1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test3", "tt1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) - // check the schema diff - diff = getLatestSchemaDiff(t, tk) - require.Equal(t, model.ActionRenameTable, diff.Type) - require.Equal(t, 0, len(diff.AffectedOpts)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("tt2"), - ChildFKName: model.NewCIStr("fk_b"), - }, *tb1ReferredFKs[0]) - tbl2Info := getTableInfo(t, dom, "test2", "tt2") - tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys)) - require.Equal(t, model.FKInfo{ - ID: 1, - Name: model.NewCIStr("fk_b"), - RefSchema: model.NewCIStr("test3"), - RefTable: model.NewCIStr("tt1"), - RefCols: []model.CIStr{model.NewCIStr("id")}, - Cols: []model.CIStr{model.NewCIStr("b")}, - State: model.StatePublic, - Version: 1, - }, *tbl2Info.ForeignKeys[0]) - tk.MustQuery("show create table test2.tt2").Check(testkit.Rows("tt2 CREATE TABLE `tt2` (\n" + - " `id` int(11) NOT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `fk_b` (`b`),\n" + - " CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test3`.`tt1` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func TestCreateTableWithForeignKeyDML(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int);") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1)") - tk.MustExec("update t1 set a = 2 where id = 1") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") - - tk.MustExec("commit") -} - -func TestCreateTableWithForeignKeyError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - - cases := []struct { - prepare []string - refer string - create string - err string - }{ - { - refer: "create table t1 (id int, a int, b int);", - create: "create table t2 (a int, b int, foreign key fk_b(b) references T_unknown(b));", - err: "[schema:1824]Failed to open the referenced table 'T_unknown'", - }, - { - refer: "create table t1 (id int, a int, b int);", - create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(c_unknown));", - err: "[schema:3734]Failed to add the foreign key constraint. Missing column 'c_unknown' for constraint 'fk_b' in the referenced table 't1'", - }, - { - refer: "create table t1 (id int key, a int, b int);", - create: "create table t2 (a int, b int, foreign key fk(c_unknown) references t1(id));", - err: "[ddl:1072]Key column 'c_unknown' doesn't exist in table", - }, - { - refer: "create table t1 (id int, a int, b int);", - create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - refer: "create table t1 (id int, a int, b int not null, index(b));", - create: "create table t2 (a int, b int not null, foreign key fk_b(b) references t1(b) on update set null);", - err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", - }, - { - refer: "create table t1 (id int, a int, b int not null, index(b));", - create: "create table t2 (a int, b int not null, foreign key fk_b(b) references t1(b) on delete set null);", - err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", - }, - { - refer: "create table t1 (id int key, a int, b int as (a) virtual, index(b));", - create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", - err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", - }, - { - refer: "create table t1 (id int key, a int, b int, index(b));", - create: "create table t2 (a int, b int as (a) virtual, foreign key fk_b(b) references t1(b));", - err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", - }, - { - refer: "create table t1 (id int key, a int);", - create: "create table t2 (a int, b varchar(10), foreign key fk(b) references t1(id));", - err: "[ddl:3780]Referencing column 'b' and referenced column 'id' in foreign key constraint 'fk' are incompatible.", - }, - { - refer: "create table t1 (id int key, a int not null, index(a));", - create: "create table t2 (a int, b int unsigned, foreign key fk_b(b) references t1(a));", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - refer: "create table t1 (id int key, a bigint, index(a));", - create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(a));", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - refer: "create table t1 (id int key, a varchar(10) charset utf8, index(a));", - create: "create table t2 (a int, b varchar(10) charset utf8mb4, foreign key fk_b(b) references t1(a));", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - refer: "create table t1 (id int key, a varchar(10) collate utf8_bin, index(a));", - create: "create table t2 (a int, b varchar(10) collate utf8mb4_bin, foreign key fk_b(b) references t1(a));", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - refer: "create table t1 (id int key, a varchar(10));", - create: "create table t2 (a int, b varchar(10), foreign key fk_b(b) references t1(a));", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - refer: "create table t1 (id int key, a varchar(10), index (a(5)));", - create: "create table t2 (a int, b varchar(10), foreign key fk_b(b) references t1(a));", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - refer: "create table t1 (id int key, a int, index(a));", - create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(id, a));", - err: "[schema:1239]Incorrect foreign key definition for 'fk_b': Key reference and table reference don't match", - }, - { - create: "create table t2 (a int key, foreign key (a) references t2(a));", - err: "[schema:1215]Cannot add foreign key constraint", - }, - { - create: "create table t2 (a int, b int, index(a,b), index(b,a), foreign key (a,b) references t2(a,b));", - err: "[schema:1215]Cannot add foreign key constraint", - }, - { - create: "create table t2 (a int, b int, index(a,b), foreign key (a,b) references t2(b,a));", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't2'", - }, - { - prepare: []string{ - "set @@foreign_key_checks=0;", - "create table t2 (a int, b int, index(a), foreign key (a) references t1(id));", - }, - create: "create table t1 (id int, a int);", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't1'", - }, - { - prepare: []string{ - "set @@foreign_key_checks=0;", - "create table t2 (a int, b int, index(a), foreign key (a) references t1(id));", - }, - create: "create table t1 (id bigint key, a int);", - err: "[ddl:3780]Referencing column 'a' and referenced column 'id' in foreign key constraint 'fk_1' are incompatible.", - }, - { - // foreign key is not support in temporary table. - refer: "create temporary table t1 (id int key, b int, index(b))", - create: "create table t2 (a int, b int, foreign key fk(b) references t1(b))", - err: "[schema:1824]Failed to open the referenced table 't1'", - }, - { - // foreign key is not support in temporary table. - refer: "create global temporary table t1 (id int key, b int, index(b)) on commit delete rows", - create: "create table t2 (a int, b int, foreign key fk(b) references t1(b))", - err: "[schema:1215]Cannot add foreign key constraint", - }, - { - // foreign key is not support in temporary table. - refer: "create table t1 (id int key, b int, index(b))", - create: "create temporary table t2 (a int, b int, foreign key fk(b) references t1(b))", - err: "[schema:1215]Cannot add foreign key constraint", - }, - { - // foreign key is not support in temporary table. - refer: "create table t1 (id int key, b int, index(b))", - create: "create global temporary table t2 (a int, b int, foreign key fk(b) references t1(b)) on commit delete rows", - err: "[schema:1215]Cannot add foreign key constraint", - }, - { - create: "create table t1 (a int, foreign key ``(a) references t1(a));", - err: "[ddl:1280]Incorrect index name ''", - }, - { - create: "create table t1 (a int, constraint `` foreign key (a) references t1(a));", - err: "[ddl:1280]Incorrect index name ''", - }, - { - create: "create table t1 (a int, constraint `fk` foreign key (a,a) references t1(a, b));", - err: "[schema:1060]Duplicate column name 'a'", - }, - { - refer: "create table t1(a int, b int, index(a,b));", - create: "create table t2 (a int, b int, foreign key (a,b) references t1(a,a));", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't1'", - }, - { - refer: "create table t1 (id int key, b int, index(b))", - create: "create table t2 (a int, b int, index fk_1(a), foreign key (b) references t1(b));", - err: "[ddl:1061]duplicate key name fk_1", - }, - { - refer: "create table t1 (id int key);", - create: "create table t2 (id int key, foreign key name5678901234567890123456789012345678901234567890123456789012345(id) references t1(id));", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - { - refer: "create table t1 (id int key);", - create: "create table t2 (id int key, constraint name5678901234567890123456789012345678901234567890123456789012345 foreign key (id) references t1(id));", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - { - create: "create table t2 (id int key, constraint fk foreign key (id) references name5678901234567890123456789012345678901234567890123456789012345.t1(id));", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - { - prepare: []string{ - "set @@foreign_key_checks=0;", - }, - create: "create table t2 (id int key, constraint fk foreign key (id) references name5678901234567890123456789012345678901234567890123456789012345(id));", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - { - prepare: []string{ - "set @@foreign_key_checks=0;", - }, - create: "create table t2 (id int key, constraint fk foreign key (id) references t1(name5678901234567890123456789012345678901234567890123456789012345));", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - // Test foreign key with temporary table - { - refer: "create temporary table t1 (id int key);", - create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", - err: "[schema:1824]Failed to open the referenced table 't1'", - }, - { - refer: "create table t1 (id int key);", - create: "create temporary table t2 (id int key, constraint fk foreign key (id) references t1(id));", - err: "[schema:1215]Cannot add foreign key constraint", - }, - // Test foreign key with partition table - { - refer: "create table t1 (id int key) partition by hash(id) partitions 3;", - create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", - err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", - }, - { - refer: "create table t1 (id int key);", - create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id)) partition by hash(id) partitions 3;", - err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", - }, - } - for _, ca := range cases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - tk.MustExec("set @@foreign_key_checks=1") - for _, sql := range ca.prepare { - tk.MustExec(sql) - } - if ca.refer != "" { - tk.MustExec(ca.refer) - } - err := tk.ExecToErr(ca.create) - require.Error(t, err, ca.create) - require.Equal(t, ca.err, err.Error(), ca.create) - } - - passCases := [][]string{ - { - "create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))", - }, - { - "create table t1 (id int key, b int not null, index(b))", - "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", - }, - { - "create table t1 (id int key, a varchar(10), index(a));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - }, - { - "create table t1 (id int key, a decimal(10,5), index(a));", - "create table t2 (a int, b decimal(20, 10), foreign key fk_b(b) references t1(a));", - }, - { - "create table t1 (id int key, a varchar(10), index (a(10)));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - }, - { - "set @@foreign_key_checks=0;", - "create table t2 (a int, b int, foreign key fk_b(b) references t_unknown(b));", - "set @@foreign_key_checks=1;", - }, - { - "create table t2 (a int, b int, index(a,b), index(b,a), foreign key (a,b) references t2(b,a));", - }, - { - "create table t1 (a int key, b int, index(b))", - "create table t2 (a int, b int, foreign key (a) references t1(a), foreign key (b) references t1(b));", - }, - { - "create table t1 (id int key);", - "create table t2 (id int key, foreign key name567890123456789012345678901234567890123456789012345678901234(id) references t1(id));", - }, - } - for _, ca := range passCases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca { - tk.MustExec(sql) - } - } -} - -func TestModifyColumnWithForeignKey(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - - tk.MustExec("create table t1 (id int key, b varchar(10), index(b));") - tk.MustExec("create table t2 (a varchar(10), constraint fk foreign key (a) references t1(b));") - tk.MustExec("insert into t1 values (1, '123456789');") - tk.MustExec("insert into t2 values ('123456789');") - tk.MustGetErrMsg("alter table t1 modify column b varchar(5);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustGetErrMsg("alter table t1 modify column b bigint;", "[ddl:3780]Referencing column 'a' and referenced column 'b' in foreign key constraint 'fk' are incompatible.") - tk.MustExec("alter table t1 modify column b varchar(20);") - tk.MustGetErrMsg("alter table t1 modify column b varchar(10);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustExec("alter table t2 modify column a varchar(20);") - tk.MustExec("alter table t2 modify column a varchar(21);") - tk.MustGetErrMsg("alter table t2 modify column a varchar(5);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") - tk.MustGetErrMsg("alter table t2 modify column a bigint;", "[ddl:3780]Referencing column 'a' and referenced column 'b' in foreign key constraint 'fk' are incompatible.") - - tk.MustExec("drop table t2") - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key, b decimal(10, 5), index(b));") - tk.MustExec("create table t2 (a decimal(10, 5), constraint fk foreign key (a) references t1(b));") - tk.MustExec("insert into t1 values (1, 12345.67891);") - tk.MustExec("insert into t2 values (12345.67891);") - tk.MustGetErrMsg("alter table t1 modify column b decimal(10, 6);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustGetErrMsg("alter table t1 modify column b decimal(10, 3);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustGetErrMsg("alter table t1 modify column b decimal(5, 2);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustGetErrMsg("alter table t1 modify column b decimal(20, 10);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") - tk.MustGetErrMsg("alter table t2 modify column a decimal(30, 15);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") - tk.MustGetErrMsg("alter table t2 modify column a decimal(5, 2);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") -} - -func TestDropChildTableForeignKeyMetaInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int, CONSTRAINT fk foreign key (a) references t1(id))") - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - tk.MustExec("drop table t1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 0, len(tb1ReferredFKs)) - - tk.MustExec("create table t1 (id int key, b int, index(b))") - tk.MustExec("create table t2 (a int, b int, foreign key fk (a) references t1(b));") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - tk.MustExec("drop table t2") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 0, len(tb1ReferredFKs)) -} - -func TestDropForeignKeyMetaInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, a int, b int, CONSTRAINT fk foreign key (a) references t1(id))") - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - tk.MustExec("alter table t1 drop foreign key fk") - tbl1Info := getTableInfo(t, dom, "test", "t1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 0, len(tbl1Info.ForeignKeys)) - require.Equal(t, 0, len(tb1ReferredFKs)) - - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key, b int, index(b))") - tk.MustExec("create table t2 (a int, b int, foreign key fk (a) references t1(b));") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - tk.MustExec("alter table t2 drop foreign key fk") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 0, len(tb1ReferredFKs)) - tbl2Info := getTableInfo(t, dom, "test", "t2") - require.Equal(t, 0, len(tbl2Info.ForeignKeys)) -} - -func TestTruncateOrDropTableWithForeignKeyReferred(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("use test") - - cases := []struct { - prepares []string - tbl string - truncateErr string - dropErr string - }{ - { - prepares: []string{ - "create table t1 (id int key, b int not null, index(b))", - "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", - }, - tbl: "t1", - truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", - dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10), index(a));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - }, - tbl: "t1", - truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", - dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10), index (a(10)));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - }, - tbl: "t1", - truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", - dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", - }, - } - - for _, ca := range cases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca.prepares { - tk.MustExec(sql) - } - truncateSQL := fmt.Sprintf("truncate table %v", ca.tbl) - tk.MustExec("set @@foreign_key_checks=1;") - err := tk.ExecToErr(truncateSQL) - require.Error(t, err) - require.Equal(t, ca.truncateErr, err.Error()) - dropSQL := fmt.Sprintf("drop table %v", ca.tbl) - err = tk.ExecToErr(dropSQL) - require.Error(t, err) - require.Equal(t, ca.dropErr, err.Error()) - - tk.MustExec("set @@foreign_key_checks=0;") - tk.MustExec(truncateSQL) - } - passCases := [][]string{ - { - "create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))", - "truncate table t1", - "drop table t1", - }, - { - "create table t1 (id int key, a varchar(10), index (a(10)));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - "drop table t1, t2", - }, - { - "set @@foreign_key_checks=0;", - "create table t1 (id int key, a varchar(10), index (a(10)));", - "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", - "truncate table t1", - "drop table t1", - }, - } - for _, ca := range passCases { - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("set @@foreign_key_checks=1;") - for _, sql := range ca { - tk.MustExec(sql) - } - } -} - -func TestDropTableWithForeignKeyReferred(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") - tk.MustExec("create table t3 (id int key, b int, foreign key fk_b(b) references t2(id));") - err := tk.ExecToErr("drop table if exists t1,t2;") - require.Error(t, err) - require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) - tk.MustQuery("show tables").Check(testkit.Rows("t1", "t2", "t3")) -} - -func TestDropIndexNeededInForeignKey(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - cases := []struct { - prepares []string - drops []string - err string - }{ - { - prepares: []string{ - "create table t1 (id int key, b int, index idx (b))", - "create table t2 (a int, b int, index idx (b), foreign key fk_b(b) references t1(b));", - }, - drops: []string{ - "alter table t1 drop index idx", - "alter table t2 drop index idx", - }, - err: "[ddl:1553]Cannot drop index 'idx': needed in a foreign key constraint", - }, - { - prepares: []string{ - "create table t1 (id int, b int, index idx (id, b))", - "create table t2 (a int, b int, index idx (b, a), foreign key fk_b(b) references t1(id));", - }, - drops: []string{ - "alter table t1 drop index idx", - "alter table t2 drop index idx", - }, - err: "[ddl:1553]Cannot drop index 'idx': needed in a foreign key constraint", - }, - } - - for _, ca := range cases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca.prepares { - tk.MustExec(sql) - } - for _, drop := range ca.drops { - // even disable foreign key check, still can't drop the index used by foreign key. - tk.MustExec("set @@foreign_key_checks=0;") - err := tk.ExecToErr(drop) - require.Error(t, err) - require.Equal(t, ca.err, err.Error()) - tk.MustExec("set @@foreign_key_checks=1;") - err = tk.ExecToErr(drop) - require.Error(t, err) - require.Equal(t, ca.err, err.Error()) - } - } - passCases := [][]string{ - { - "create table t1 (id int key, b int, index idxb (b))", - "create table t2 (a int, b int key, index idxa (a),index idxb (b), foreign key fk_b(b) references t1(id));", - "alter table t1 drop index idxb", - "alter table t2 drop index idxa", - "alter table t2 drop index idxb", - }, - { - "create table t1 (id int key, b int, index idxb (b), unique index idx(b, id))", - "create table t2 (a int, b int key, index idx (b, a),index idxb (b), index idxab(a, b), foreign key fk_b(b) references t1(b));", - "alter table t1 drop index idxb", - "alter table t1 add index idxb (b)", - "alter table t1 drop index idx", - "alter table t2 drop index idx", - "alter table t2 add index idx (b, a)", - "alter table t2 drop index idxb", - "alter table t2 drop index idxab", - }, - } - tk.MustExec("set @@foreign_key_checks=1;") - for _, ca := range passCases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca { - tk.MustExec(sql) - } - } -} - -func getTableInfo(t *testing.T, dom *domain.Domain, db, tb string) *model.TableInfo { - err := dom.Reload() - require.NoError(t, err) - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr(db), model.NewCIStr(tb)) - require.NoError(t, err) - _, exist := is.TableByID(tbl.Meta().ID) - require.True(t, exist) - return tbl.Meta() -} - -func getTableInfoReferredForeignKeys(t *testing.T, dom *domain.Domain, db, tb string) []*model.ReferredFKInfo { - err := dom.Reload() - require.NoError(t, err) - return dom.InfoSchema().GetTableReferredForeignKeys(db, tb) -} - -func TestDropColumnWithForeignKey(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - - tk.MustExec("create table t1 (id int key, a int, b int, index(b), CONSTRAINT fk foreign key (a) references t1(b))") - tk.MustGetErrMsg("alter table t1 drop column a;", "[ddl:1828]Cannot drop column 'a': needed in a foreign key constraint 'fk'") - tk.MustGetErrMsg("alter table t1 drop column b;", "[ddl:1829]Cannot drop column 'b': needed in a foreign key constraint 'fk' of table 't1'") - - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (a int, b int, constraint fk foreign key (a) references t1(b));") - tk.MustGetErrMsg("alter table t1 drop column b;", "[ddl:1829]Cannot drop column 'b': needed in a foreign key constraint 'fk' of table 't2'") - tk.MustGetErrMsg("alter table t2 drop column a;", "[ddl:1828]Cannot drop column 'a': needed in a foreign key constraint 'fk'") -} - -func TestRenameColumnWithForeignKeyMetaInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - - tk.MustExec("create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))") - tk.MustExec("alter table t1 change id kid int") - tk.MustExec("alter table t1 rename column a to aa") - tbl1Info := getTableInfo(t, dom, "test", "t1") - tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tbl1Info.ForeignKeys)) - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, "kid", tb1ReferredFKs[0].Cols[0].L) - require.Equal(t, "kid", tbl1Info.ForeignKeys[0].RefCols[0].L) - require.Equal(t, "aa", tbl1Info.ForeignKeys[0].Cols[0].L) - - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key, b int, index(b))") - tk.MustExec("create table t2 (a int, b int, foreign key fk(a) references t1(b));") - tk.MustExec("alter table t2 change a aa int") - tbl1Info = getTableInfo(t, dom, "test", "t1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) - require.Equal(t, "b", tb1ReferredFKs[0].Cols[0].L) - tbl2Info := getTableInfo(t, dom, "test", "t2") - tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) - require.Equal(t, "aa", tbl2Info.ForeignKeys[0].Cols[0].L) - require.Equal(t, "b", tbl2Info.ForeignKeys[0].RefCols[0].L) - - tk.MustExec("alter table t1 change id kid int") - tk.MustExec("alter table t1 change b bb int") - tbl1Info = getTableInfo(t, dom, "test", "t1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 1, len(tb1ReferredFKs)) - require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) - require.Equal(t, "bb", tb1ReferredFKs[0].Cols[0].L) - tbl2Info = getTableInfo(t, dom, "test", "t2") - tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) - require.Equal(t, "aa", tbl2Info.ForeignKeys[0].Cols[0].L) - require.Equal(t, "bb", tbl2Info.ForeignKeys[0].RefCols[0].L) - - tk.MustExec("drop table t1, t2") - tk.MustExec("create table t1 (id int key, b int, index(b))") - tk.MustExec("create table t2 (a int, b int, foreign key (a) references t1(b), foreign key (b) references t1(b));") - tk.MustExec("alter table t1 change b bb int") - tbl1Info = getTableInfo(t, dom, "test", "t1") - tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") - require.Equal(t, 2, len(tb1ReferredFKs)) - require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) - require.Equal(t, 1, len(tb1ReferredFKs[1].Cols)) - require.Equal(t, "bb", tb1ReferredFKs[0].Cols[0].L) - require.Equal(t, "bb", tb1ReferredFKs[1].Cols[0].L) - tbl2Info = getTableInfo(t, dom, "test", "t2") - tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t2") - require.Equal(t, 0, len(tb2ReferredFKs)) - require.Equal(t, 2, len(tbl2Info.ForeignKeys)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) - require.Equal(t, "a", tbl2Info.ForeignKeys[0].Cols[0].L) - require.Equal(t, "bb", tbl2Info.ForeignKeys[0].RefCols[0].L) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[1].Cols)) - require.Equal(t, 1, len(tbl2Info.ForeignKeys[1].RefCols)) - require.Equal(t, "b", tbl2Info.ForeignKeys[1].Cols[0].L) - require.Equal(t, "bb", tbl2Info.ForeignKeys[1].RefCols[0].L) - tk.MustExec("alter table t2 rename column a to aa") - tk.MustExec("alter table t2 change b bb int") - tk.MustQuery("show create table t2"). - Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + - " `aa` int(11) DEFAULT NULL,\n" + - " `bb` int(11) DEFAULT NULL,\n" + - " KEY `fk_1` (`aa`),\n KEY `fk_2` (`bb`),\n" + - " CONSTRAINT `fk_1` FOREIGN KEY (`aa`) REFERENCES `test`.`t1` (`bb`),\n" + - " CONSTRAINT `fk_2` FOREIGN KEY (`bb`) REFERENCES `test`.`t1` (`bb`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func TestDropDatabaseWithForeignKeyReferred(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") - tk.MustExec("create database test2") - tk.MustExec("create table test2.t3 (id int key, b int, foreign key fk_b(b) references test.t2(id));") - err := tk.ExecToErr("drop database test;") - require.Error(t, err) - require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) - tk.MustExec("set @@foreign_key_checks=0;") - tk.MustExec("drop database test") - - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("create database test") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") - err = tk.ExecToErr("drop database test;") - require.Error(t, err) - require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) - tk.MustExec("drop table test2.t3") - tk.MustExec("drop database test") -} - -func TestAddForeignKey(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int);") - tk.MustExec("create table t2 (id int key, b int);") - tk.MustExec("alter table t2 add index(b)") - tk.MustExec("alter table t2 add foreign key (b) references t1(id);") - tbl2Info := getTableInfo(t, dom, "test", "t2") - require.Equal(t, int64(1), tbl2Info.MaxForeignKeyID) - tk.MustGetDBError("alter table t2 add foreign key (b) references t1(b);", infoschema.ErrForeignKeyNoIndexInParent) - tk.MustExec("alter table t1 add index(b)") - tk.MustExec("alter table t2 add foreign key (b) references t1(b);") - tk.MustGetDBError("alter table t2 add foreign key (b) references t2(b);", infoschema.ErrCannotAddForeign) - // Test auto-create index when create foreign key constraint. - tk.MustExec("drop table if exists t1,t2") - tk.MustExec("create table t1 (id int key, b int, index(b));") - tk.MustExec("create table t2 (id int key, b int);") - tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(b);") - tbl2Info = getTableInfo(t, dom, "test", "t2") - require.Equal(t, 1, len(tbl2Info.Indices)) - require.Equal(t, "fk", tbl2Info.Indices[0].Name.L) - require.Equal(t, model.StatePublic, tbl2Info.Indices[0].State) - tk.MustQuery("select b from t2 use index(fk)").Check(testkit.Rows()) - res := tk.MustQuery("explain select b from t2 use index(fk)") - plan := bytes.NewBuffer(nil) - rows := res.Rows() - for _, row := range rows { - for _, c := range row { - plan.WriteString(c.(string)) - plan.WriteString(" ") - } - } - require.Regexp(t, ".*IndexReader.*index:fk.*", plan.String()) - - // Test add multiple foreign key constraint in one statement. - tk.MustExec("alter table t2 add column c int, add column d int, add column e int;") - tk.MustExec("alter table t2 add index idx_c(c, d, e)") - tk.MustExec("alter table t2 add constraint fk_c foreign key (c) references t1(b), " + - "add constraint fk_d foreign key (d) references t1(b)," + - "add constraint fk_e foreign key (e) references t1(b)") - tbl2Info = getTableInfo(t, dom, "test", "t2") - require.Equal(t, 4, len(tbl2Info.Indices)) - names := []string{"fk", "idx_c", "fk_d", "fk_e"} - for i, idx := range tbl2Info.Indices { - require.Equal(t, names[i], idx.Name.L) - require.Equal(t, model.StatePublic, idx.State) - } - names = []string{"fk", "fk_c", "fk_d", "fk_e"} - for i, fkInfo := range tbl2Info.ForeignKeys { - require.Equal(t, names[i], fkInfo.Name.L) - require.Equal(t, model.StatePublic, fkInfo.State) - } - tk.MustGetDBError("insert into t2 (id, b) values (1,1)", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 (id, c) values (1,1)", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 (id, d) values (1,1)", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 (id, e) values (1,1)", plannercore.ErrNoReferencedRow2) - - // Test add multiple foreign key constraint in one statement but failed. - tk.MustExec("alter table t2 drop foreign key fk") - tk.MustExec("alter table t2 drop foreign key fk_c") - tk.MustExec("alter table t2 drop foreign key fk_d") - tk.MustExec("alter table t2 drop foreign key fk_e") - tk.MustGetDBError("alter table t2 add constraint fk_c foreign key (c) references t1(b), "+ - "add constraint fk_d foreign key (d) references t1(b),"+ - "add constraint fk_e foreign key (e) references t1(unknown_col)", infoschema.ErrForeignKeyNoColumnInParent) - tbl2Info = getTableInfo(t, dom, "test", "t2") - require.Equal(t, 0, len(tbl2Info.ForeignKeys)) - tk.MustGetDBError("alter table t2 drop index idx_c, add constraint fk_c foreign key (c) references t1(b)", dbterror.ErrDropIndexNeededInForeignKey) - - // Test circular dependency add foreign key failed. - tk.MustExec("drop table if exists t1,t2") - tk.MustExec("create table t1 (id int key,a int, index(a));") - tk.MustExec("create table t2 (id int key,a int, foreign key fk(a) references t1(id) ON DELETE CASCADE);") - tk.MustExec("insert into t1 values (1,1);") - err := tk.ExecToErr("ALTER TABLE t1 ADD foreign key fk(a) references t2(id) ON DELETE CASCADE;") - require.Error(t, err) - require.Equal(t, "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t2` (`id`) ON DELETE CASCADE)", err.Error()) - tbl1Info := getTableInfo(t, dom, "test", "t1") - require.Equal(t, 0, len(tbl1Info.ForeignKeys)) - referredFKs := dom.InfoSchema().GetTableReferredForeignKeys("test", "t2") - require.Equal(t, 0, len(referredFKs)) - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + - " `id` int(11) NOT NULL,\n" + - " `a` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `a` (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // Test add foreign key with auto-create index failed. - tk.MustExec("drop table if exists t1,t2") - tk.MustExec("create table t1 (id int key,a int);") - tk.MustExec("create table t2 (id int key);") - tk.MustExec("insert into t1 values (1,1);") - err = tk.ExecToErr("ALTER TABLE t1 ADD foreign key fk(a) references t2(id) ON DELETE CASCADE;") - require.Error(t, err) - require.Equal(t, "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t2` (`id`) ON DELETE CASCADE)", err.Error()) - tbl1Info = getTableInfo(t, dom, "test", "t1") - require.Equal(t, 0, len(tbl1Info.ForeignKeys)) - referredFKs = dom.InfoSchema().GetTableReferredForeignKeys("test", "t2") - require.Equal(t, 0, len(referredFKs)) - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + - " `id` int(11) NOT NULL,\n" + - " `a` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func TestAlterTableAddForeignKeyError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - cases := []struct { - prepares []string - alter string - err string - }{ - { - prepares: []string{ - "create table t1 (id int, a int, b int);", - "create table t2 (a int, b int);", - }, - alter: "alter table t2 add foreign key fk(b) references t_unknown(id)", - err: "[schema:1824]Failed to open the referenced table 't_unknown'", - }, - { - prepares: []string{ - "create table t1 (id int, a int, b int);", - "create table t2 (a int, b int);", - }, - alter: "alter table t2 add foreign key fk(b) references t1(c_unknown)", - err: "[schema:3734]Failed to add the foreign key constraint. Missing column 'c_unknown' for constraint 'fk' in the referenced table 't1'", - }, - { - prepares: []string{ - "create table t1 (id int, a int, b int);", - "create table t2 (a int, b int);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(b)", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - prepares: []string{ - "create table t1 (id int, a int, b int not null, index(b));", - "create table t2 (a int, b int not null);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(b) on update set null", - err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", - }, - { - prepares: []string{ - "create table t1 (id int, a int, b int not null, index(b));", - "create table t2 (a int, b int not null);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(b) on delete set null", - err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", - }, - { - prepares: []string{ - "create table t1 (id int key, a int, b int as (a) virtual, index(b));", - "create table t2 (a int, b int);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(b)", - err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", - }, - { - prepares: []string{ - "create table t1 (id int key, a int, b int, index(b));", - "create table t2 (a int, b int as (a) virtual);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(b)", - err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", - }, - { - prepares: []string{ - "create table t1 (id int key, a int);", - "create table t2 (a int, b varchar(10));", - }, - alter: "alter table t2 add foreign key fk(b) references t1(id)", - err: "[ddl:3780]Referencing column 'b' and referenced column 'id' in foreign key constraint 'fk' are incompatible.", - }, - { - prepares: []string{ - "create table t1 (id int key, a int not null, index(a));", - "create table t2 (a int, b int unsigned);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - prepares: []string{ - "create table t1 (id int key, a bigint, index(a));", - "create table t2 (a int, b int);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10) charset utf8, index(a));", - "create table t2 (a int, b varchar(10) charset utf8mb4);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10) collate utf8_bin, index(a));", - "create table t2 (a int, b varchar(10) collate utf8mb4_bin);", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10));", - "create table t2 (a int, b varchar(10));", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - prepares: []string{ - "create table t1 (id int key, a varchar(10), index (a(5)));", - "create table t2 (a int, b varchar(10));", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(a)", - err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", - }, - { - prepares: []string{ - "create table t1 (id int key, a int)", - "create table t2 (id int, b int, index(b))", - "insert into t2 values (1,1)", - }, - alter: "alter table t2 add foreign key fk_b(b) references t1(id)", - err: "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `t1` (`id`))", - }, - { - prepares: []string{ - "create table t1 (id int, a int, b int, index(a,b))", - "create table t2 (id int, a int, b int, index(a,b))", - "insert into t2 values (1, 1, null), (2, null, 1), (3, null, null), (4, 1, 1)", - }, - alter: "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", - err: "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`a`, `b`) REFERENCES `t1` (`a`, `b`))", - }, - { - prepares: []string{ - "create table t1 (id int key);", - "create table t2 (a int, b int unique);", - }, - alter: "alter table t2 add foreign key name5678901234567890123456789012345678901234567890123456789012345(b) references t1(id)", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - { - prepares: []string{ - "create table t1 (id int key);", - "create table t2 (a int, b int unique);", - }, - alter: "alter table t2 add constraint name5678901234567890123456789012345678901234567890123456789012345 foreign key (b) references t1(id)", - err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", - }, - // Test foreign key with temporary table. - { - prepares: []string{ - "create temporary table t1 (id int key);", - "create table t2 (a int, b int unique);", - }, - alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", - err: "[schema:1824]Failed to open the referenced table 't1'", - }, - { - prepares: []string{ - "create table t1 (id int key);", - "create temporary table t2 (a int, b int unique);", - }, - alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", - err: "[ddl:8200]TiDB doesn't support ALTER TABLE for local temporary table", - }, - // Test foreign key with partition table - { - prepares: []string{ - "create table t1 (id int key) partition by hash(id) partitions 3;", - "create table t2 (id int key);", - }, - alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", - err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", - }, - { - prepares: []string{ - "create table t1 (id int key);", - "create table t2 (id int key) partition by hash(id) partitions 3;;", - }, - alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", - err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", - }, - } - for i, ca := range cases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca.prepares { - tk.MustExec(sql) - } - err := tk.ExecToErr(ca.alter) - require.Error(t, err, fmt.Sprintf("%v, %v", i, ca.err)) - require.Equal(t, ca.err, err.Error()) - } - - passCases := [][]string{ - { - "create table t1 (id int key, a int, b int, index(a))", - "alter table t1 add foreign key fk(a) references t1(id)", - }, - { - "create table t1 (id int key, b int not null, index(b))", - "create table t2 (a int, b int, index(b));", - "alter table t2 add foreign key fk_b(b) references t1(b)", - }, - { - "create table t1 (id int key, a varchar(10), index(a));", - "create table t2 (a int, b varchar(20), index(b));", - "alter table t2 add foreign key fk_b(b) references t1(a)", - }, - { - "create table t1 (id int key, a decimal(10,5), index(a));", - "create table t2 (a int, b decimal(20, 10), index(b));", - "alter table t2 add foreign key fk_b(b) references t1(a)", - }, - { - "create table t1 (id int key, a varchar(10), index (a(10)));", - "create table t2 (a int, b varchar(20), index(b));", - "alter table t2 add foreign key fk_b(b) references t1(a)", - }, - { - "create table t1 (id int key, a int)", - "create table t2 (id int, b int, index(b))", - "insert into t2 values (1, null)", - "alter table t2 add foreign key fk_b(b) references t1(id)", - }, - { - "create table t1 (id int, a int, b int, index(a,b))", - "create table t2 (id int, a int, b int, index(a,b))", - "insert into t2 values (1, 1, null), (2, null, 1), (3, null, null)", - "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", - }, - { - "set @@foreign_key_checks=0;", - "create table t1 (id int, a int, b int, index(a,b))", - "create table t2 (id int, a int, b int, index(a,b))", - "insert into t2 values (1, 1, 1)", - "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", - "set @@foreign_key_checks=1;", - }, - { - "set @@foreign_key_checks=0;", - "create table t2 (a int, b int, index(b));", - "alter table t2 add foreign key fk_b(b) references t_unknown(a)", - "set @@foreign_key_checks=1;", - }, - { - "create table t1 (id int key);", - "create table t2 (a int, b int unique);", - "alter table t2 add foreign key name567890123456789012345678901234567890123456789012345678901234(b) references t1(id)", - }, - } - for _, ca := range passCases { - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t1") - for _, sql := range ca { - tk.MustExec(sql) - } - } -} - -func TestRenameTablesWithForeignKey(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=0;") - tk.MustExec("create database test1") - tk.MustExec("create database test2") - tk.MustExec("use test") - tk.MustExec("create table t0 (id int key, b int);") - tk.MustExec("create table t1 (id int key, b int, index(b), foreign key fk(b) references t2(id));") - tk.MustExec("create table t2 (id int key, b int, index(b), foreign key fk(b) references t1(id));") - tk.MustExec("rename table test.t1 to test1.tt1, test.t2 to test2.tt2, test.t0 to test.tt0") - - // check the schema diff - diff := getLatestSchemaDiff(t, tk) - require.Equal(t, model.ActionRenameTables, diff.Type) - require.Equal(t, 2, len(diff.AffectedOpts)) - - // check referred foreign key information. - t1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") - t2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t2") - require.Equal(t, 0, len(t1ReferredFKs)) - require.Equal(t, 0, len(t2ReferredFKs)) - tt1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test1", "tt1") - tt2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") - require.Equal(t, 1, len(tt1ReferredFKs)) - require.Equal(t, 1, len(tt2ReferredFKs)) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test2"), - ChildTable: model.NewCIStr("tt2"), - ChildFKName: model.NewCIStr("fk"), - }, *tt1ReferredFKs[0]) - require.Equal(t, model.ReferredFKInfo{ - Cols: []model.CIStr{model.NewCIStr("id")}, - ChildSchema: model.NewCIStr("test1"), - ChildTable: model.NewCIStr("tt1"), - ChildFKName: model.NewCIStr("fk"), - }, *tt2ReferredFKs[0]) - - // check show create table information - tk.MustQuery("show create table test1.tt1").Check(testkit.Rows("tt1 CREATE TABLE `tt1` (\n" + - " `id` int(11) NOT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`),\n" + - " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test2`.`tt2` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustQuery("show create table test2.tt2").Check(testkit.Rows("tt2 CREATE TABLE `tt2` (\n" + - " `id` int(11) NOT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`),\n" + - " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test1`.`tt1` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func getLatestSchemaDiff(t *testing.T, tk *testkit.TestKit) *model.SchemaDiff { - ctx := tk.Session() - err := sessiontxn.NewTxn(context.Background(), ctx) - require.NoError(t, err) - txn, err := ctx.Txn(true) - require.NoError(t, err) - m := meta.NewMeta(txn) - ver, err := m.GetSchemaVersion() - require.NoError(t, err) - diff, err := m.GetSchemaDiff(ver) - require.NoError(t, err) - return diff -} - -func TestMultiSchemaAddForeignKey(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (a int, b int);") - tk.MustExec("alter table t2 add foreign key (a) references t1(id), add foreign key (b) references t1(id)") - tk.MustExec("alter table t2 add column c int, add column d int") - tk.MustExec("alter table t2 add foreign key (c) references t1(id), add foreign key (d) references t1(id), add index(c), add index(d)") - tk.MustExec("drop table t2") - tk.MustExec("create table t2 (a int, b int, index idx1(a), index idx2(b));") - tk.MustGetErrMsg("alter table t2 drop index idx1, drop index idx2, add foreign key (a) references t1(id), add foreign key (b) references t1(id)", - "[ddl:1553]Cannot drop index 'idx1': needed in a foreign key constraint") - tk.MustExec("alter table t2 drop index idx1, drop index idx2") - tk.MustExec("alter table t2 add foreign key (a) references t1(id), add foreign key (b) references t1(id)") - tk.MustQuery("show create table t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + - " `a` int(11) DEFAULT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " KEY `fk_1` (`a`),\n" + - " KEY `fk_2` (`b`),\n" + - " CONSTRAINT `fk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t1` (`id`),\n" + - " CONSTRAINT `fk_2` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustExec("drop table t2") - tk.MustExec("create table t2 (a int, b int, index idx0(a,b), index idx1(a), index idx2(b));") - tk.MustExec("alter table t2 drop index idx1, add foreign key (a) references t1(id), add foreign key (b) references t1(id)") -} - -func TestAddForeignKeyInBigTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk.MustExec("create table employee (id bigint auto_increment key, pid bigint)") - tk.MustExec("insert into employee (id) values (1),(2),(3),(4),(5),(6),(7),(8)") - for i := 0; i < 14; i++ { - tk.MustExec("insert into employee (pid) select pid from employee") - } - tk.MustExec("update employee set pid=id-1 where id>1") - start := time.Now() - tk.MustExec("alter table employee add foreign key fk_1(pid) references employee(id)") - require.Less(t, time.Since(start), time.Minute) -} - -func TestForeignKeyWithCacheTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - // Test foreign key refer cache table. - tk.MustExec("create table t1 (id int key);") - tk.MustExec("insert into t1 values (1),(2),(3),(4)") - tk.MustExec("alter table t1 cache;") - tk.MustExec("create table t2 (b int);") - tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") - tk.MustExec("insert into t2 values (1),(2),(3),(4)") - tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) - tk.MustExec("update t1 set id = id+10 where id=1") - tk.MustExec("delete from t1 where id<10") - tk.MustQuery("select * from t1").Check(testkit.Rows("11")) - tk.MustQuery("select * from t2").Check(testkit.Rows("11")) - tk.MustExec("alter table t1 nocache;") - tk.MustExec("drop table t1,t2;") - - // Test add foreign key on cache table. - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (b int);") - tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") - tk.MustExec("alter table t2 cache;") - tk.MustExec("insert into t1 values (1),(2),(3),(4)") - tk.MustExec("insert into t2 values (1),(2),(3),(4)") - tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) - tk.MustExec("update t1 set id = id+10 where id=1") - tk.MustExec("delete from t1 where id<10") - tk.MustQuery("select * from t1").Check(testkit.Rows("11")) - tk.MustQuery("select * from t2").Check(testkit.Rows("11")) - tk.MustExec("alter table t2 nocache;") - tk.MustExec("drop table t1,t2;") -} - -func TestForeignKeyAndConcurrentDDL(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - // Test foreign key refer cache table. - tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") - tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@foreign_key_checks=1;") - tk2.MustExec("use test") - passCases := []struct { - prepare []string - ddl1 string - ddl2 string - }{ - { - ddl1: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", - ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", - }, - { - ddl1: "alter table t2 drop foreign key fk_1", - ddl2: "alter table t2 drop foreign key fk_2", - }, - { - prepare: []string{ - "alter table t2 drop index a", - }, - ddl1: "alter table t2 add index(a)", - ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", - }, - { - ddl1: "alter table t2 drop index c", - ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", - }, - } - for _, ca := range passCases { - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - tk.MustExec(ca.ddl1) - }() - go func() { - defer wg.Done() - tk2.MustExec(ca.ddl2) - }() - wg.Wait() - } - errorCases := []struct { - prepare []string - ddl1 string - err1 string - ddl2 string - err2 string - }{ - { - ddl1: "alter table t2 add constraint fk foreign key (a) references t1(a)", - err1: "[ddl:1826]Duplicate foreign key constraint name 'fk'", - ddl2: "alter table t2 add constraint fk foreign key (b) references t1(b)", - err2: "[ddl:1826]Duplicate foreign key constraint name 'fk'", - }, - { - prepare: []string{ - "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", - }, - ddl1: "alter table t2 drop foreign key fk_1", - err1: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", - ddl2: "alter table t2 drop foreign key fk_1", - err2: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", - }, - { - ddl1: "alter table t2 drop index a", - err1: "[ddl:1553]Cannot drop index 'a': needed in a foreign key constraint", - ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", - err2: "[ddl:-1]Failed to add the foreign key constraint. Missing index for 'fk_1' foreign key columns in the table 't2'", - }, - } - tk.MustExec("drop table t1,t2") - tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") - tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") - for i, ca := range errorCases { - for _, sql := range ca.prepare { - tk.MustExec(sql) - } - var wg sync.WaitGroup - var err1, err2 error - wg.Add(2) - go func() { - defer wg.Done() - err1 = tk.ExecToErr(ca.ddl1) - }() - go func() { - defer wg.Done() - err2 = tk2.ExecToErr(ca.ddl2) - }() - wg.Wait() - if (err1 == nil && err2 == nil) || (err1 != nil && err2 != nil) { - require.Failf(t, "both ddl1 and ddl2 execute success, but expect 1 error", fmt.Sprintf("idx: %v, err1: %v, err2: %v", i, err1, err2)) - } - if err1 != nil { - require.Equal(t, ca.err1, err1.Error()) - } - if err2 != nil { - require.Equal(t, ca.err2, err2.Error()) - } - } -} - -func TestForeignKeyAndRenameIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1;") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, b int, index idx1(b));") - tk.MustExec("create table t2 (id int key, b int, constraint fk foreign key (b) references t1(b));") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustExec("insert into t2 values (1,1),(2,2)") - tk.MustGetDBError("insert into t2 values (3,3)", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("delete from t1 where id=1", plannercore.ErrRowIsReferenced2) - tk.MustExec("alter table t1 rename index idx1 to idx2") - tk.MustExec("alter table t2 rename index fk to idx") - tk.MustGetDBError("insert into t2 values (3,3)", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("delete from t1 where id=1", plannercore.ErrRowIsReferenced2) - tk.MustExec("alter table t2 drop foreign key fk") - tk.MustExec("alter table t2 add foreign key fk (b) references t1(b) on delete cascade on update cascade") - tk.MustExec("alter table t1 rename index idx2 to idx3") - tk.MustExec("alter table t2 rename index idx to idx0") - tk.MustExec("delete from t1 where id=1") - tk.MustQuery("select * from t1").Check(testkit.Rows("2 2")) - tk.MustQuery("select * from t2").Check(testkit.Rows("2 2")) - tk.MustExec("admin check table t1,t2") -} diff --git a/ddl/tests/fk/main_test.go b/ddl/tests/fk/main_test.go deleted file mode 100644 index b81a384142d0e..0000000000000 --- a/ddl/tests/fk/main_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl_test - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - - domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) - domain.SchemaOutOfDateRetryTimes.Store(50) - - autoid.SetStep(5000) - ddl.RunInGoTest = true - - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 10000 - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/indexmerge/BUILD.bazel b/ddl/tests/indexmerge/BUILD.bazel deleted file mode 100644 index 722c7c9394f85..0000000000000 --- a/ddl/tests/indexmerge/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "indexmerge_test", - timeout = "short", - srcs = [ - "main_test.go", - "merge_test.go", - ], - flaky = True, - race = "on", - shard_count = 20, - deps = [ - "//config", - "//ddl", - "//ddl/ingest", - "//ddl/testutil", - "//ddl/util/callback", - "//domain", - "//errno", - "//kv", - "//meta/autoid", - "//parser/model", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/indexmerge/main_test.go b/ddl/tests/indexmerge/main_test.go deleted file mode 100644 index 84bf9bc166360..0000000000000 --- a/ddl/tests/indexmerge/main_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package indexmergetest - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - - domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) - domain.SchemaOutOfDateRetryTimes.Store(50) - - autoid.SetStep(5000) - ddl.RunInGoTest = true - - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 10000 - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/metadatalock/BUILD.bazel b/ddl/tests/metadatalock/BUILD.bazel deleted file mode 100644 index c3a87504d1da1..0000000000000 --- a/ddl/tests/metadatalock/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "metadatalock_test", - timeout = "short", - srcs = [ - "main_test.go", - "mdl_test.go", - ], - flaky = True, - shard_count = 32, - deps = [ - "//config", - "//ddl", - "//errno", - "//server", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/metadatalock/main_test.go b/ddl/tests/metadatalock/main_test.go deleted file mode 100644 index 93854188d63d8..0000000000000 --- a/ddl/tests/metadatalock/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package metadatalocktest - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/multivaluedindex/BUILD.bazel b/ddl/tests/multivaluedindex/BUILD.bazel deleted file mode 100644 index 25df7f9079d69..0000000000000 --- a/ddl/tests/multivaluedindex/BUILD.bazel +++ /dev/null @@ -1,19 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "multivaluedindex_test", - timeout = "short", - srcs = [ - "main_test.go", - "multi_valued_index_test.go", - ], - flaky = True, - deps = [ - "//infoschema", - "//parser/model", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/multivaluedindex/main_test.go b/ddl/tests/multivaluedindex/main_test.go deleted file mode 100644 index 17eda6ca0900b..0000000000000 --- a/ddl/tests/multivaluedindex/main_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package multivaluedindex - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/multivaluedindex/multi_valued_index_test.go b/ddl/tests/multivaluedindex/multi_valued_index_test.go deleted file mode 100644 index 6442c8df40445..0000000000000 --- a/ddl/tests/multivaluedindex/multi_valued_index_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package multivaluedindex - -import ( - "testing" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestCreateMultiValuedIndexHasBinaryCollation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create table test.t (pk varchar(4) primary key clustered, j json, str varchar(255), value int, key idx((cast(j as char(100) array)), str));") - is := tk.Session().GetDomainInfoSchema().(infoschema.InfoSchema) - require.NotNil(t, is) - - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - foundIndex := false - for _, c := range tbl.Cols() { - if c.Hidden { - foundIndex = true - require.True(t, c.FieldType.IsArray()) - require.Equal(t, c.FieldType.GetCharset(), "binary") - require.Equal(t, c.FieldType.GetCollate(), "binary") - } - } - require.True(t, foundIndex) -} diff --git a/ddl/tests/partition/BUILD.bazel b/ddl/tests/partition/BUILD.bazel deleted file mode 100644 index 8116f6070ae85..0000000000000 --- a/ddl/tests/partition/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "partition_test", - timeout = "short", - srcs = [ - "db_partition_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 47, - deps = [ - "//config", - "//ddl", - "//ddl/testutil", - "//ddl/util/callback", - "//domain", - "//errno", - "//kv", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn", - "//store/mockstore", - "//table", - "//table/tables", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ddl/tests/partition/main_test.go b/ddl/tests/partition/main_test.go deleted file mode 100644 index b9ce2882125fb..0000000000000 --- a/ddl/tests/partition/main_test.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package partition - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -const ( - // waitForCleanDataRound indicates how many times should we check data is cleaned or not. - waitForCleanDataRound = 150 - // waitForCleanDataInterval is a min duration between 2 check for data clean. - waitForCleanDataInterval = time.Millisecond * 100 -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} - -func backgroundExec(s kv.Storage, schema, sql string, done chan error) { - se, err := session.CreateSession4Test(s) - if err != nil { - done <- errors.Trace(err) - return - } - defer se.Close() - _, err = se.Execute(context.Background(), "use "+schema) - if err != nil { - done <- errors.Trace(err) - return - } - _, err = se.Execute(context.Background(), sql) - done <- errors.Trace(err) -} diff --git a/ddl/tests/resourcegroup/BUILD.bazel b/ddl/tests/resourcegroup/BUILD.bazel deleted file mode 100644 index 1c9950fc3a171..0000000000000 --- a/ddl/tests/resourcegroup/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "resourcegroup_test", - timeout = "short", - srcs = ["resource_group_test.go"], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//ddl/resourcegroup", - "//ddl/util/callback", - "//domain", - "//domain/infosync", - "//errno", - "//parser/auth", - "//parser/model", - "//sessionctx", - "//testkit", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/tests/resourcegroup/resource_group_test.go b/ddl/tests/resourcegroup/resource_group_test.go deleted file mode 100644 index c1796a3a6c72a..0000000000000 --- a/ddl/tests/resourcegroup/resource_group_test.go +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package resourcegrouptest_test - -import ( - "context" - "fmt" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/failpoint" - rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/ddl/resourcegroup" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestResourceGroupBasic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - re := require.New(t) - - hook := &callback.TestDDLCallback{Do: dom} - var groupID atomic.Int64 - onJobUpdatedExportedFunc := func(job *model.Job) { - // job.SchemaID will be assigned when the group is created. - if (job.SchemaName == "x" || job.SchemaName == "y") && job.Type == model.ActionCreateResourceGroup && job.SchemaID != 0 { - groupID.Store(job.SchemaID) - return - } - } - hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) - dom.DDL().SetHook(hook) - - tk.MustExec("set global tidb_enable_resource_control = 'off'") - tk.MustGetErrCode("create user usr1 resource group rg1", mysql.ErrResourceGroupSupportDisabled) - tk.MustExec("create user usr1") - tk.MustGetErrCode("alter user usr1 resource group rg1", mysql.ErrResourceGroupSupportDisabled) - tk.MustGetErrCode("create resource group x RU_PER_SEC=1000 ", mysql.ErrResourceGroupSupportDisabled) - - tk.MustExec("set global tidb_enable_resource_control = 'on'") - - // test default resource group. - tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default UNLIMITED MEDIUM YES ")) - tk.MustExec("alter resource group `default` PRIORITY=LOW") - tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default UNLIMITED LOW YES ")) - tk.MustExec("alter resource group `default` ru_per_sec=1000") - tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default 1000 LOW YES ")) - tk.MustContainErrMsg("drop resource group `default`", "can't drop reserved resource group") - - tk.MustExec("create resource group x RU_PER_SEC=1000") - checkFunc := func(groupInfo *model.ResourceGroupInfo) { - require.Equal(t, true, groupInfo.ID != 0) - require.Equal(t, "x", groupInfo.Name.L) - require.Equal(t, groupID.Load(), groupInfo.ID) - require.Equal(t, uint64(1000), groupInfo.RURate) - require.Nil(t, groupInfo.Runaway) - } - // Check the group is correctly reloaded in the information schema. - g := testResourceGroupNameFromIS(t, tk.Session(), "x") - checkFunc(g) - - // test create if not exists - tk.MustExec("create resource group if not exists x RU_PER_SEC=10000") - // Check the resource group is not changed - g = testResourceGroupNameFromIS(t, tk.Session(), "x") - checkFunc(g) - // Check warning message - res := tk.MustQuery("show warnings") - res.Check(testkit.Rows("Note 8248 Resource group 'x' already exists")) - - tk.MustExec("set global tidb_enable_resource_control = off") - tk.MustGetErrCode("alter resource group x RU_PER_SEC=2000 ", mysql.ErrResourceGroupSupportDisabled) - tk.MustGetErrCode("drop resource group x ", mysql.ErrResourceGroupSupportDisabled) - - tk.MustExec("set global tidb_enable_resource_control = DEFAULT") - - tk.MustGetErrCode("create resource group x RU_PER_SEC=1000 ", mysql.ErrResourceGroupExists) - - tk.MustExec("alter resource group x RU_PER_SEC=2000 BURSTABLE QUERY_LIMIT=(EXEC_ELAPSED='15s' ACTION DRYRUN WATCH SIMILAR DURATION '10m0s')") - g = testResourceGroupNameFromIS(t, tk.Session(), "x") - re.Equal(uint64(2000), g.RURate) - re.Equal(int64(-1), g.BurstLimit) - re.Equal(uint64(time.Second*15/time.Millisecond), g.Runaway.ExecElapsedTimeMs) - re.Equal(model.RunawayActionDryRun, g.Runaway.Action) - re.Equal(model.WatchSimilar, g.Runaway.WatchType) - re.Equal(int64(time.Minute*10/time.Millisecond), g.Runaway.WatchDurationMs) - - tk.MustExec("alter resource group x QUERY_LIMIT=(EXEC_ELAPSED='20s' ACTION DRYRUN WATCH SIMILAR)") - g = testResourceGroupNameFromIS(t, tk.Session(), "x") - re.Equal(uint64(2000), g.RURate) - re.Equal(int64(-1), g.BurstLimit) - re.Equal(uint64(time.Second*20/time.Millisecond), g.Runaway.ExecElapsedTimeMs) - re.Equal(model.RunawayActionDryRun, g.Runaway.Action) - re.Equal(model.WatchSimilar, g.Runaway.WatchType) - re.Equal(int64(0), g.Runaway.WatchDurationMs) - - tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 2000 MEDIUM YES EXEC_ELAPSED='20s', ACTION=DRYRUN, WATCH=SIMILAR DURATION=UNLIMITED ")) - - tk.MustExec("drop resource group x") - g = testResourceGroupNameFromIS(t, tk.Session(), "x") - re.Nil(g) - - tk.MustExec("alter resource group if exists not_exists RU_PER_SEC=2000") - // Check warning message - res = tk.MustQuery("show warnings") - res.Check(testkit.Rows("Note 8249 Unknown resource group 'not_exists'")) - - tk.MustExec("create resource group y RU_PER_SEC=4000") - checkFunc = func(groupInfo *model.ResourceGroupInfo) { - re.Equal(true, groupInfo.ID != 0) - re.Equal("y", groupInfo.Name.L) - re.Equal(groupID.Load(), groupInfo.ID) - re.Equal(uint64(4000), groupInfo.RURate) - re.Equal(int64(4000), groupInfo.BurstLimit) - } - g = testResourceGroupNameFromIS(t, tk.Session(), "y") - checkFunc(g) - tk.MustExec("alter resource group y BURSTABLE RU_PER_SEC=5000 QUERY_LIMIT=(EXEC_ELAPSED='15s' ACTION KILL)") - checkFunc = func(groupInfo *model.ResourceGroupInfo) { - re.Equal(true, groupInfo.ID != 0) - re.Equal("y", groupInfo.Name.L) - re.Equal(groupID.Load(), groupInfo.ID) - re.Equal(uint64(5000), groupInfo.RURate) - re.Equal(int64(-1), groupInfo.BurstLimit) - re.Equal(uint64(time.Second*15/time.Millisecond), groupInfo.Runaway.ExecElapsedTimeMs) - re.Equal(model.RunawayActionKill, groupInfo.Runaway.Action) - re.Equal(int64(0), groupInfo.Runaway.WatchDurationMs) - } - g = testResourceGroupNameFromIS(t, tk.Session(), "y") - checkFunc(g) - tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 5000 MEDIUM YES EXEC_ELAPSED='15s', ACTION=KILL ")) - tk.MustExec("drop resource group y") - g = testResourceGroupNameFromIS(t, tk.Session(), "y") - re.Nil(g) - - tk.MustGetErrCode("create resource group x ru_per_sec=1000 ru_per_sec=200", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 ru_per_sec=200, ru_per_sec=300", "Dupliated options specified") - tk.MustGetErrCode("create resource group x burstable, burstable", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x burstable, burstable", "Dupliated options specified") - tk.MustGetErrCode("create resource group x ru_per_sec=1000, burstable, burstable", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x ru_per_sec=1000, burstable, burstable", "Dupliated options specified") - tk.MustGetErrCode("create resource group x burstable, ru_per_sec=1000, burstable", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x burstable, ru_per_sec=1000, burstable", "Dupliated options specified") - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 burstable QUERY_LIMIT=(EXEC_ELAPSED='15s' action kill action cooldown)", "Dupliated runaway options specified") - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s') burstable priority=Low, QUERY_LIMIT=(EXEC_ELAPSED='15s')", "Dupliated options specified") - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s') QUERY_LIMIT=(EXEC_ELAPSED='15s')", "Dupliated options specified") - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(action kill)", "invalid exec elapsed time") - tk.MustGetErrCode("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s' action kil)", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s')", "unknown resource group runaway action") - tk.MustGetErrCode("create resource group x ru_per_sec=1000 EXEC_ELAPSED='15s' action kill", mysql.ErrParse) - tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15d' action kill)", "unknown unit \"d\"") - groups, err := infosync.ListResourceGroups(context.TODO()) - re.Equal(1, len(groups)) - re.NoError(err) - - // Check information schema table information_schema.resource_groups - tk.MustExec("create resource group x RU_PER_SEC=1000 PRIORITY=LOW") - tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 1000 LOW NO ")) - tk.MustExec("alter resource group x RU_PER_SEC=2000 BURSTABLE QUERY_LIMIT=(EXEC_ELAPSED='15s' action kill)") - tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 2000 LOW YES EXEC_ELAPSED='15s', ACTION=KILL ")) - tk.MustQuery("show create resource group x").Check(testkit.Rows("x CREATE RESOURCE GROUP `x` RU_PER_SEC=2000, PRIORITY=LOW, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)")) - tk.MustExec("CREATE RESOURCE GROUP `x_new` RU_PER_SEC=2000 PRIORITY=LOW BURSTABLE=true QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)") - tk.MustQuery("select * from information_schema.resource_groups where name = 'x_new'").Check(testkit.Rows("x_new 2000 LOW YES EXEC_ELAPSED='15s', ACTION=KILL ")) - tk.MustExec("alter resource group x BURSTABLE=false RU_PER_SEC=3000") - tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 3000 LOW NO EXEC_ELAPSED='15s', ACTION=KILL ")) - tk.MustQuery("show create resource group x").Check(testkit.Rows("x CREATE RESOURCE GROUP `x` RU_PER_SEC=3000, PRIORITY=LOW, QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)")) - - tk.MustExec("create resource group y BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='1s' action COOLDOWN WATCH EXACT duration '1h')") - tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 2000 MEDIUM YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) - tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=2000, PRIORITY=MEDIUM, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) - tk.MustExec("CREATE RESOURCE GROUP `y_new` RU_PER_SEC=2000 PRIORITY=MEDIUM QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH EXACT DURATION=\"1h0m0s\")") - tk.MustQuery("select * from information_schema.resource_groups where name = 'y_new'").Check(testkit.Rows("y_new 2000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) - tk.MustExec("alter resource group y_new RU_PER_SEC=3000") - tk.MustQuery("select * from information_schema.resource_groups where name = 'y_new'").Check(testkit.Rows("y_new 3000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) - - tk.MustExec("CREATE RESOURCE GROUP `z` RU_PER_SEC=2000 PRIORITY=MEDIUM QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH PLAN DURATION=\"1h0m0s\")") - tk.MustQuery("select * from information_schema.resource_groups where name = 'z'").Check(testkit.Rows("z 2000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=PLAN DURATION='1h0m0s' ")) - - tk.MustExec("alter resource group y RU_PER_SEC=4000") - tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 4000 MEDIUM YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) - tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=4000, PRIORITY=MEDIUM, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) - - tk.MustExec("alter resource group y RU_PER_SEC=4000 PRIORITY=HIGH BURSTABLE") - tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 4000 HIGH YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) - tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=4000, PRIORITY=HIGH, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) - - tk.MustQuery("select count(*) from information_schema.resource_groups").Check(testkit.Rows("6")) - tk.MustGetErrCode("create user usr_fail resource group nil_group", mysql.ErrResourceGroupNotExists) - tk.MustContainErrMsg("create user usr_fail resource group nil_group", "Unknown resource group 'nil_group'") - tk.MustExec("create user user2") - tk.MustGetErrCode("alter user user2 resource group nil_group", mysql.ErrResourceGroupNotExists) - tk.MustContainErrMsg("alter user user2 resource group nil_group", "Unknown resource group 'nil_group'") - - tk.MustExec("create resource group do_not_delete_rg ru_per_sec=100") - tk.MustExec("create user usr3 resource group do_not_delete_rg") - tk.MustQuery("select user_attributes from mysql.user where user = 'usr3'").Check(testkit.Rows(`{"resource_group": "do_not_delete_rg"}`)) - tk.MustContainErrMsg("drop resource group do_not_delete_rg", "user [usr3] depends on the resource group to drop") - tk.MustExec("alter user usr3 resource group `default`") - tk.MustExec("alter user usr3 resource group ``") - tk.MustExec("alter user usr3 resource group `DeFault`") - tk.MustQuery("select user_attributes from mysql.user where user = 'usr3'").Check(testkit.Rows(`{"resource_group": "default"}`)) - - tk.MustExec("alter resource group default ru_per_sec = 1000, priority = medium, background = (task_types = 'lightning, BR');") - tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default 1000 MEDIUM YES TASK_TYPES='lightning,br'")) - tk.MustQuery("show create resource group default").Check(testkit.Rows("default CREATE RESOURCE GROUP `default` RU_PER_SEC=1000, PRIORITY=MEDIUM, BURSTABLE, BACKGROUND=(TASK_TYPES='lightning,br')")) - g = testResourceGroupNameFromIS(t, tk.Session(), "default") - require.EqualValues(t, g.Background.JobTypes, []string{"lightning", "br"}) - - tk.MustContainErrMsg("create resource group bg ru_per_sec = 1000 background = (task_types = 'lightning')", "unsupported operation") - tk.MustContainErrMsg("alter resource group x background=(task_types='')", "unsupported operation") - tk.MustGetErrCode("alter resource group default background=(task_types='a,b,c')", mysql.ErrResourceGroupInvalidBackgroundTaskName) -} - -func testResourceGroupNameFromIS(t *testing.T, ctx sessionctx.Context, name string) *model.ResourceGroupInfo { - dom := domain.GetDomain(ctx) - // Make sure the table schema is the new schema. - err := dom.Reload() - require.NoError(t, err) - g, _ := dom.InfoSchema().ResourceGroupByName(model.NewCIStr(name)) - return g -} - -func TestResourceGroupRunaway(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/FastRunawayGC", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/FastRunawayGC")) - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1)") - - tk.MustExec("set global tidb_enable_resource_control='on'") - tk.MustExec("create resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=KILL)") - tk.MustExec("create resource group rg2 BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' action KILL WATCH EXACT duration '1s')") - tk.MustExec("create resource group rg3 BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' action KILL WATCH EXACT)") - tk.MustQuery("select * from information_schema.resource_groups where name = 'rg2'").Check(testkit.Rows("rg2 2000 MEDIUM YES EXEC_ELAPSED='50ms', ACTION=KILL, WATCH=EXACT DURATION='1s' ")) - tk.MustQuery("select * from information_schema.resource_groups where name = 'rg3'").Check(testkit.Rows("rg3 2000 MEDIUM YES EXEC_ELAPSED='50ms', ACTION=KILL, WATCH=EXACT DURATION=UNLIMITED ")) - tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) - tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) - tk.MustQuery("select /*+ resource_group(rg3) */ * from t").Check(testkit.Rows("1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/sleepCoprRequest", fmt.Sprintf("return(%d)", 60))) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/sleepCoprRequest")) - }() - err := tk.QueryToErr("select /*+ resource_group(rg1) */ * from t") - require.ErrorContains(t, err, "[executor:8253]Query execution was interrupted, identified as runaway query") - - tryInterval := time.Millisecond * 200 - maxWaitDuration := time.Second * 5 - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, match_type from mysql.tidb_runaway_queries", nil, - testkit.Rows("rg1 select /*+ resource_group(rg1) */ * from t identify"), maxWaitDuration, tryInterval) - // require.Len(t, tk.MustQuery("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries").Rows(), 0) - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries", nil, - nil, maxWaitDuration, tryInterval) - tk.MustExec("alter resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='100ms' ACTION=COOLDOWN)") - tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) - - tk.MustExec("alter resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='100ms' ACTION=DRYRUN)") - tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) - - err = tk.QueryToErr("select /*+ resource_group(rg2) */ * from t") - require.ErrorContains(t, err, "Query execution was interrupted, identified as runaway query") - tk.MustGetErrCode("select /*+ resource_group(rg2) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, match_type from mysql.tidb_runaway_queries", nil, - testkit.Rows("rg2 select /*+ resource_group(rg2) */ * from t identify", - "rg2 select /*+ resource_group(rg2) */ * from t watch"), maxWaitDuration, tryInterval) - tk.MustQuery("select SQL_NO_CACHE resource_group_name, watch_text from mysql.tidb_runaway_watch"). - Check(testkit.Rows("rg2 select /*+ resource_group(rg2) */ * from t")) - - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries", nil, - nil, maxWaitDuration, tryInterval) - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, watch_text, end_time from mysql.tidb_runaway_watch", nil, - nil, maxWaitDuration, tryInterval) - err = tk.QueryToErr("select /*+ resource_group(rg3) */ * from t") - require.ErrorContains(t, err, "Query execution was interrupted, identified as runaway query") - tk.MustGetErrCode("select /*+ resource_group(rg3) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) - tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, watch_text from mysql.tidb_runaway_watch", nil, - testkit.Rows("rg3 select /*+ resource_group(rg3) */ * from t"), maxWaitDuration, tryInterval) - - tk.MustExec("alter resource group rg2 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=COOLDOWN)") - tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) - tk.MustExec("alter resource group rg2 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=DRYRUN)") - tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) - tk.MustGetErrCode("select /*+ resource_group(rg3) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) -} - -func TestAlreadyExistsDefaultResourceGroup(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/managerAlreadyCreateSomeGroups", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/managerAlreadyCreateSomeGroups")) - }() - testkit.CreateMockStoreAndDomain(t) - groups, _ := infosync.ListResourceGroups(context.TODO()) - require.Equal(t, 2, len(groups)) -} - -func TestNewResourceGroupFromOptions(t *testing.T) { - type TestCase struct { - name string - groupName string - input *model.ResourceGroupSettings - output *rmpb.ResourceGroup - err error - } - var tests []TestCase - groupName := "test" - tests = append(tests, TestCase{ - name: "empty 1", - input: &model.ResourceGroupSettings{}, - err: resourcegroup.ErrUnknownResourceGroupMode, - }) - - tests = append(tests, TestCase{ - name: "empty 2", - input: nil, - err: resourcegroup.ErrInvalidGroupSettings, - }) - - tests = append(tests, TestCase{ - name: "normal case: ru case 1", - input: &model.ResourceGroupSettings{ - RURate: 2000, - Priority: 0, - }, - output: &rmpb.ResourceGroup{ - Name: groupName, - Mode: rmpb.GroupMode_RUMode, - Priority: 0, - RUSettings: &rmpb.GroupRequestUnitSettings{ - RU: &rmpb.TokenBucket{Settings: &rmpb.TokenLimitSettings{FillRate: 2000}}, - }, - }, - }) - - tests = append(tests, TestCase{ - name: "normal case: ru case 2", - input: &model.ResourceGroupSettings{ - RURate: 5000, - Priority: 8, - }, - output: &rmpb.ResourceGroup{ - Name: groupName, - Priority: 8, - Mode: rmpb.GroupMode_RUMode, - RUSettings: &rmpb.GroupRequestUnitSettings{ - RU: &rmpb.TokenBucket{Settings: &rmpb.TokenLimitSettings{FillRate: 5000}}, - }, - }, - }) - - tests = append(tests, TestCase{ - name: "error case: native case 1", - input: &model.ResourceGroupSettings{ - CPULimiter: "8", - IOReadBandwidth: "3000MB/s", - IOWriteBandwidth: "3000Mi", - }, - err: resourcegroup.ErrUnknownResourceGroupMode, - }) - - tests = append(tests, TestCase{ - name: "error case: native case 2", - input: &model.ResourceGroupSettings{ - CPULimiter: "8c", - IOReadBandwidth: "3000Mi", - IOWriteBandwidth: "3000Mi", - }, - err: resourcegroup.ErrUnknownResourceGroupMode, - }) - - tests = append(tests, TestCase{ - name: "error case: native case 3", - input: &model.ResourceGroupSettings{ - CPULimiter: "8", - IOReadBandwidth: "3000G", - IOWriteBandwidth: "3000MB", - }, - err: resourcegroup.ErrUnknownResourceGroupMode, - }) - - tests = append(tests, TestCase{ - name: "error case: duplicated mode", - input: &model.ResourceGroupSettings{ - CPULimiter: "8", - IOReadBandwidth: "3000Mi", - IOWriteBandwidth: "3000Mi", - RURate: 1000, - }, - err: resourcegroup.ErrInvalidResourceGroupDuplicatedMode, - }) - - tests = append(tests, TestCase{ - name: "error case: duplicated mode", - groupName: "test_group_too_looooooooooooooooooooooooooooooooooooooooooooooooong", - input: &model.ResourceGroupSettings{ - CPULimiter: "8", - IOReadBandwidth: "3000Mi", - IOWriteBandwidth: "3000Mi", - RURate: 1000, - }, - err: resourcegroup.ErrTooLongResourceGroupName, - }) - - for _, test := range tests { - name := groupName - if len(test.groupName) > 0 { - name = test.groupName - } - group, err := resourcegroup.NewGroupFromOptions(name, test.input) - comment := fmt.Sprintf("[%s]\nerr1 %s\nerr2 %s", test.name, err, test.err) - if test.err != nil { - require.ErrorIs(t, err, test.err, comment) - } else { - require.NoError(t, err, comment) - require.Equal(t, test.output, group) - } - } -} - -func TestBindHints(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - re := require.New(t) - - tk.MustExec("drop resource group if exists rg1") - tk.MustExec("create resource group rg1 RU_PER_SEC=1000") - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - - tk.MustExec("create global binding for select * from t using select /*+ resource_group(rg1) */ * from t") - tk.MustQuery("select * from t") - re.Equal("rg1", tk.Session().GetSessionVars().StmtCtx.ResourceGroup) - re.Equal("default", tk.Session().GetSessionVars().ResourceGroupName) - tk.MustQuery("select a, b from t") - re.Equal("", tk.Session().GetSessionVars().StmtCtx.ResourceGroup) - re.Equal("default", tk.Session().GetSessionVars().ResourceGroupName) -} diff --git a/ddl/tests/serial/BUILD.bazel b/ddl/tests/serial/BUILD.bazel deleted file mode 100644 index 0f08135411e03..0000000000000 --- a/ddl/tests/serial/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "serial_test", - timeout = "short", - srcs = [ - "main_test.go", - "serial_test.go", - ], - flaky = True, - shard_count = 20, - deps = [ - "//config", - "//ddl", - "//ddl/util", - "//ddl/util/callback", - "//domain", - "//domain/infosync", - "//errno", - "//infoschema", - "//keyspace", - "//kv", - "//meta", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//session", - "//sessionctx/variable", - "//sessiontxn", - "//store/mockstore", - "//table", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//util/dbterror", - "//util/gcutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/tests/serial/main_test.go b/ddl/tests/serial/main_test.go deleted file mode 100644 index 4fbd7468a8514..0000000000000 --- a/ddl/tests/serial/main_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package serial - -import ( - "context" - "fmt" - "os" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - - domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) - domain.SchemaOutOfDateRetryTimes.Store(50) - - autoid.SetStep(5000) - ddl.ReorgWaitTimeout = 30 * time.Millisecond - ddl.CheckBackfillJobFinishInterval = 50 * time.Millisecond - ddl.RunInGoTest = true - ddl.SetBatchInsertDeleteRangeSize(2) - - config.UpdateGlobal(func(conf *config.Config) { - // Test for table lock. - conf.EnableTableLock = true - conf.Instance.SlowThreshold = 10000 - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - - _, err := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) - if err != nil { - _, _ = fmt.Fprintf(os.Stderr, "ddl: infosync.GlobalInfoSyncerInit: %v\n", err) - os.Exit(1) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/tests/tiflash/BUILD.bazel b/ddl/tests/tiflash/BUILD.bazel deleted file mode 100644 index eaa2077dddc39..0000000000000 --- a/ddl/tests/tiflash/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tiflash_test", - timeout = "short", - srcs = [ - "ddl_tiflash_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 32, - deps = [ - "//config", - "//ddl", - "//ddl/placement", - "//ddl/util", - "//domain", - "//domain/infosync", - "//kv", - "//parser/model", - "//session", - "//sessionctx", - "//store/gcworker", - "//store/mockstore", - "//store/mockstore/unistore", - "//table", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//util", - "//util/logutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ddl/tests/tiflash/main_test.go b/ddl/tests/tiflash/main_test.go deleted file mode 100644 index 5cd548a1e6438..0000000000000 --- a/ddl/tests/tiflash/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package tiflashtest - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/testutil/BUILD.bazel b/ddl/testutil/BUILD.bazel deleted file mode 100644 index 4f45198778d09..0000000000000 --- a/ddl/testutil/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testutil", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/ddl/testutil", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//kv", - "//parser/model", - "//session", - "//sessiontxn", - "//table", - "//table/tables", - "//types", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ddl/testutil/testutil.go b/ddl/testutil/testutil.go deleted file mode 100644 index e8b3c5753cde0..0000000000000 --- a/ddl/testutil/testutil.go +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package testutil - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -// SessionExecInGoroutine export for testing. -func SessionExecInGoroutine(s kv.Storage, dbName, sql string, done chan error) { - ExecMultiSQLInGoroutine(s, dbName, []string{sql}, done) -} - -// ExecMultiSQLInGoroutine exports for testing. -func ExecMultiSQLInGoroutine(s kv.Storage, dbName string, multiSQL []string, done chan error) { - go func() { - se, err := session.CreateSession4Test(s) - if err != nil { - done <- errors.Trace(err) - return - } - defer se.Close() - _, err = se.Execute(context.Background(), "use "+dbName) - if err != nil { - done <- errors.Trace(err) - return - } - for _, sql := range multiSQL { - rs, err := se.Execute(context.Background(), sql) - if err != nil { - done <- errors.Trace(err) - return - } - if rs != nil { - done <- errors.Errorf("RecordSet should be empty") - return - } - done <- nil - } - }() -} - -// ExtractAllTableHandles extracts all handles of a given table. -func ExtractAllTableHandles(se session.Session, dbName, tbName string) ([]int64, error) { - dom := domain.GetDomain(se) - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tbName)) - if err != nil { - return nil, err - } - err = sessiontxn.NewTxn(context.Background(), se) - if err != nil { - return nil, err - } - - var allHandles []int64 - err = tables.IterRecords(tbl, se, nil, - func(h kv.Handle, _ []types.Datum, _ []*table.Column) (more bool, err error) { - allHandles = append(allHandles, h.IntValue()) - return true, nil - }) - return allHandles, err -} - -// FindIdxInfo is to get IndexInfo by index name. -func FindIdxInfo(dom *domain.Domain, dbName, tbName, idxName string) *model.IndexInfo { - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tbName)) - if err != nil { - logutil.BgLogger().Warn("cannot find table", zap.String("dbName", dbName), zap.String("tbName", tbName)) - return nil - } - return tbl.Meta().FindIndexByName(idxName) -} - -// SubStates is a slice of SchemaState. -type SubStates = []model.SchemaState - -// TestMatchCancelState is used to test whether the cancel state matches. -func TestMatchCancelState(t *testing.T, job *model.Job, cancelState interface{}, sql string) bool { - switch v := cancelState.(type) { - case model.SchemaState: - if job.Type == model.ActionMultiSchemaChange { - msg := fmt.Sprintf("unexpected multi-schema change(sql: %s, cancel state: %s)", sql, v) - require.Failf(t, msg, "use []model.SchemaState as cancel states instead") - return false - } - return job.SchemaState == v - case SubStates: // For multi-schema change sub-jobs. - if job.MultiSchemaInfo == nil { - msg := fmt.Sprintf("not multi-schema change(sql: %s, cancel state: %v)", sql, v) - require.Failf(t, msg, "use model.SchemaState as the cancel state instead") - return false - } - require.Equal(t, len(job.MultiSchemaInfo.SubJobs), len(v), sql) - for i, subJobSchemaState := range v { - if job.MultiSchemaInfo.SubJobs[i].SchemaState != subJobSchemaState { - return false - } - } - return true - default: - return false - } -} diff --git a/ddl/ttl.go b/ddl/ttl.go deleted file mode 100644 index 58011ee9e79f9..0000000000000 --- a/ddl/ttl.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" -) - -func onTTLInfoRemove(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - tblInfo.TTLInfo = nil - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func onTTLInfoChange(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { - // at least one for them is not nil - var ttlInfo *model.TTLInfo - var ttlInfoEnable *bool - var ttlInfoJobInterval *string - - if err := job.DecodeArgs(&ttlInfo, &ttlInfoEnable, &ttlInfoJobInterval); err != nil { - job.State = model.JobStateCancelled - return ver, errors.Trace(err) - } - - tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) - if err != nil { - return ver, errors.Trace(err) - } - - if ttlInfo != nil { - // if the TTL_ENABLE is not set explicitly, use the original value - if ttlInfoEnable == nil && tblInfo.TTLInfo != nil { - ttlInfo.Enable = tblInfo.TTLInfo.Enable - } - if ttlInfoJobInterval == nil && tblInfo.TTLInfo != nil { - ttlInfo.JobInterval = tblInfo.TTLInfo.JobInterval - } - tblInfo.TTLInfo = ttlInfo - } - if ttlInfoEnable != nil { - if tblInfo.TTLInfo == nil { - return ver, errors.Trace(dbterror.ErrSetTTLOptionForNonTTLTable.FastGenByArgs("TTL_ENABLE")) - } - - tblInfo.TTLInfo.Enable = *ttlInfoEnable - } - if ttlInfoJobInterval != nil { - if tblInfo.TTLInfo == nil { - return ver, errors.Trace(dbterror.ErrSetTTLOptionForNonTTLTable.FastGenByArgs("TTL_JOB_INTERVAL")) - } - - tblInfo.TTLInfo.JobInterval = *ttlInfoJobInterval - } - - ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) - if err != nil { - return ver, errors.Trace(err) - } - job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) - return ver, nil -} - -func checkTTLInfoValid(ctx sessionctx.Context, schema model.CIStr, tblInfo *model.TableInfo) error { - if err := checkTTLIntervalExpr(ctx, tblInfo.TTLInfo); err != nil { - return err - } - - if err := checkTTLTableSuitable(ctx, schema, tblInfo); err != nil { - return err - } - - return checkTTLInfoColumnType(tblInfo) -} - -func checkTTLIntervalExpr(ctx sessionctx.Context, ttlInfo *model.TTLInfo) error { - // FIXME: use a better way to validate the interval expression in ttl - var nowAddIntervalExpr ast.ExprNode - - unit := ast.TimeUnitType(ttlInfo.IntervalTimeUnit) - expr := fmt.Sprintf("select NOW() + INTERVAL %s %s", ttlInfo.IntervalExprStr, unit.String()) - stmts, _, err := parser.New().ParseSQL(expr) - if err != nil { - // FIXME: the error information can be wrong, as it could indicate an unknown position to user. - return errors.Trace(err) - } - nowAddIntervalExpr = stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr - _, err = expression.EvalAstExpr(ctx, nowAddIntervalExpr) - return err -} - -func checkTTLInfoColumnType(tblInfo *model.TableInfo) error { - colInfo := findColumnByName(tblInfo.TTLInfo.ColumnName.L, tblInfo) - if colInfo == nil { - return dbterror.ErrBadField.GenWithStackByArgs(tblInfo.TTLInfo.ColumnName.O, "TTL config") - } - if !types.IsTypeTime(colInfo.FieldType.GetType()) { - return dbterror.ErrUnsupportedColumnInTTLConfig.GenWithStackByArgs(tblInfo.TTLInfo.ColumnName.O) - } - - return nil -} - -// checkTTLTableSuitable returns whether this table is suitable to be a TTL table -// A temporary table or a parent table referenced by a foreign key cannot be TTL table -func checkTTLTableSuitable(ctx sessionctx.Context, schema model.CIStr, tblInfo *model.TableInfo) error { - if tblInfo.TempTableType != model.TempTableNone { - return dbterror.ErrTempTableNotAllowedWithTTL - } - - if err := checkPrimaryKeyForTTLTable(tblInfo); err != nil { - return err - } - - // checks even when the foreign key check is not enabled, to keep safe - is := sessiontxn.GetTxnManager(ctx).GetTxnInfoSchema() - if referredFK := checkTableHasForeignKeyReferred(is, schema.L, tblInfo.Name.L, nil, true); referredFK != nil { - return dbterror.ErrUnsupportedTTLReferencedByFK - } - - return nil -} - -func checkDropColumnWithTTLConfig(tblInfo *model.TableInfo, colName string) error { - if tblInfo.TTLInfo != nil { - if tblInfo.TTLInfo.ColumnName.L == colName { - return dbterror.ErrTTLColumnCannotDrop.GenWithStackByArgs(colName) - } - } - - return nil -} - -// We should forbid creating a TTL table with clustered primary key that contains a column with type float/double. -// This is because currently we are using SQL to delete expired rows and when the primary key contains float/double column, -// it is hard to use condition `WHERE PK in (...)` to delete specified rows because some precision will be lost when comparing. -func checkPrimaryKeyForTTLTable(tblInfo *model.TableInfo) error { - if !tblInfo.IsCommonHandle { - // only check the primary keys when it is common handle - return nil - } - - pk := tblInfo.GetPrimaryKey() - if pk == nil { - return nil - } - - for _, colDef := range pk.Columns { - col := tblInfo.Columns[colDef.Offset] - switch col.GetType() { - case mysql.TypeFloat, mysql.TypeDouble: - return dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL - } - } - - return nil -} - -// getTTLInfoInOptions returns the aggregated ttlInfo, the ttlEnable, or an error. -// if TTL, TTL_ENABLE or TTL_JOB_INTERVAL is not set in the config, the corresponding return value will be nil. -// if both of TTL and TTL_ENABLE are set, the `ttlInfo.Enable` will be equal with `ttlEnable`. -// if both of TTL and TTL_JOB_INTERVAL are set, the `ttlInfo.JobInterval` will be equal with `ttlCronJobSchedule`. -func getTTLInfoInOptions(options []*ast.TableOption) (ttlInfo *model.TTLInfo, ttlEnable *bool, ttlCronJobSchedule *string, err error) { - for _, op := range options { - switch op.Tp { - case ast.TableOptionTTL: - var sb strings.Builder - restoreFlags := format.RestoreStringSingleQuotes | format.RestoreNameBackQuotes - restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) - err := op.Value.Restore(restoreCtx) - if err != nil { - return nil, nil, nil, err - } - - intervalExpr := sb.String() - ttlInfo = &model.TTLInfo{ - ColumnName: op.ColumnName.Name, - IntervalExprStr: intervalExpr, - IntervalTimeUnit: int(op.TimeUnitValue.Unit), - Enable: true, - JobInterval: "1h", - } - case ast.TableOptionTTLEnable: - ttlEnable = &op.BoolValue - case ast.TableOptionTTLJobInterval: - ttlCronJobSchedule = &op.StrValue - } - } - - if ttlInfo != nil { - if ttlEnable != nil { - ttlInfo.Enable = *ttlEnable - } - if ttlCronJobSchedule != nil { - ttlInfo.JobInterval = *ttlCronJobSchedule - } - } - return ttlInfo, ttlEnable, ttlCronJobSchedule, nil -} diff --git a/ddl/util/BUILD.bazel b/ddl/util/BUILD.bazel deleted file mode 100644 index 3aa7b15ed75ef..0000000000000 --- a/ddl/util/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "util", - srcs = [ - "dead_table_lock_checker.go", - "event.go", - "mock.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/ddl/util", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/model", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//util/chunk", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//tikvrpc", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "util_test", - timeout = "short", - srcs = ["main_test.go"], - embed = [":util"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ddl/util/callback/BUILD.bazel b/ddl/util/callback/BUILD.bazel deleted file mode 100644 index 9eb53658313c2..0000000000000 --- a/ddl/util/callback/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "callback", - srcs = ["callback.go"], - importpath = "github.com/pingcap/tidb/ddl/util/callback", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//infoschema", - "//parser/model", - "//sessionctx", - "//util/logutil", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "callback_test", - timeout = "short", - srcs = ["callback_test.go"], - embed = [":callback"], - flaky = True, - deps = [ - "//ddl", - "@com_github_stretchr_testify//require", - ], -) diff --git a/ddl/util/callback/callback.go b/ddl/util/callback/callback.go deleted file mode 100644 index d795561b2d382..0000000000000 --- a/ddl/util/callback/callback.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package callback - -import ( - "context" - "sync/atomic" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// TestInterceptor is a test interceptor in the ddl -type TestInterceptor struct { - *ddl.BaseInterceptor - - OnGetInfoSchemaExported func(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema -} - -// OnGetInfoSchema is to run when to call GetInfoSchema -func (ti *TestInterceptor) OnGetInfoSchema(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { - if ti.OnGetInfoSchemaExported != nil { - return ti.OnGetInfoSchemaExported(ctx, is) - } - - return ti.BaseInterceptor.OnGetInfoSchema(ctx, is) -} - -// TestDDLCallback is used to customize user callback themselves. -type TestDDLCallback struct { - *ddl.BaseCallback - // We recommended to pass the domain parameter to the test ddl callback, it will ensure - // domain to reload schema before your ddl stepping into the next state change. - Do ddl.DomainReloader - - onJobRunBefore func(*model.Job) - OnJobRunBeforeExported func(*model.Job) - OnJobRunAfterExported func(*model.Job) - onJobUpdated func(*model.Job) - OnJobUpdatedExported atomic.Pointer[func(*model.Job)] - onWatched func(ctx context.Context) - OnGetJobBeforeExported func(string) - OnGetJobAfterExported func(string, *model.Job) - OnJobSchemaStateChanged func(int64) - - OnUpdateReorgInfoExported func(job *model.Job, pid int64) -} - -// OnChanged mock the same behavior with the main DDL hook. -func (tc *TestDDLCallback) OnChanged(err error) error { - if err != nil { - return err - } - logutil.BgLogger().Info("performing DDL change, must reload") - if tc.Do != nil { - err = tc.Do.Reload() - if err != nil { - logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) - } - } - return nil -} - -// OnSchemaStateChanged mock the same behavior with the main ddl hook. -func (tc *TestDDLCallback) OnSchemaStateChanged(schemaVer int64) { - if tc.Do != nil { - if err := tc.Do.Reload(); err != nil { - logutil.BgLogger().Warn("reload failed on schema state changed", zap.Error(err)) - } - } - - if tc.OnJobSchemaStateChanged != nil { - tc.OnJobSchemaStateChanged(schemaVer) - return - } -} - -// OnJobRunBefore is used to run the user customized logic of `onJobRunBefore` first. -func (tc *TestDDLCallback) OnJobRunBefore(job *model.Job) { - logutil.BgLogger().Info("on job run before", zap.String("job", job.String())) - if tc.OnJobRunBeforeExported != nil { - tc.OnJobRunBeforeExported(job) - return - } - if tc.onJobRunBefore != nil { - tc.onJobRunBefore(job) - return - } - - tc.BaseCallback.OnJobRunBefore(job) -} - -// OnJobRunAfter is used to run the user customized logic of `OnJobRunAfter` first. -func (tc *TestDDLCallback) OnJobRunAfter(job *model.Job) { - logutil.BgLogger().Info("on job run after", zap.String("job", job.String())) - if tc.OnJobRunAfterExported != nil { - tc.OnJobRunAfterExported(job) - return - } - - tc.BaseCallback.OnJobRunAfter(job) -} - -// OnJobUpdated is used to run the user customized logic of `OnJobUpdated` first. -func (tc *TestDDLCallback) OnJobUpdated(job *model.Job) { - logutil.BgLogger().Info("on job updated", zap.String("job", job.String())) - if onJobUpdatedExportedFunc := tc.OnJobUpdatedExported.Load(); onJobUpdatedExportedFunc != nil { - (*onJobUpdatedExportedFunc)(job) - return - } - if tc.onJobUpdated != nil { - tc.onJobUpdated(job) - return - } - - tc.BaseCallback.OnJobUpdated(job) -} - -// OnWatched is used to run the user customized logic of `OnWatched` first. -func (tc *TestDDLCallback) OnWatched(ctx context.Context) { - if tc.onWatched != nil { - tc.onWatched(ctx) - return - } - - tc.BaseCallback.OnWatched(ctx) -} - -// OnGetJobBefore implements Callback.OnGetJobBefore interface. -func (tc *TestDDLCallback) OnGetJobBefore(jobType string) { - if tc.OnGetJobBeforeExported != nil { - tc.OnGetJobBeforeExported(jobType) - return - } - tc.BaseCallback.OnGetJobBefore(jobType) -} - -// OnGetJobAfter implements Callback.OnGetJobAfter interface. -func (tc *TestDDLCallback) OnGetJobAfter(jobType string, job *model.Job) { - if tc.OnGetJobAfterExported != nil { - tc.OnGetJobAfterExported(jobType, job) - return - } - tc.BaseCallback.OnGetJobAfter(jobType, job) -} - -// Clone copies the callback and take its reference -func (tc *TestDDLCallback) Clone() *TestDDLCallback { - return &*tc -} - -// OnUpdateReorgInfo mock the same behavior with the main DDL reorg hook. -func (tc *TestDDLCallback) OnUpdateReorgInfo(job *model.Job, pid int64) { - if tc.OnUpdateReorgInfoExported != nil { - tc.OnUpdateReorgInfoExported(job, pid) - } -} diff --git a/ddl/util/main_test.go b/ddl/util/main_test.go deleted file mode 100644 index 59738797650c6..0000000000000 --- a/ddl/util/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package util - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ddl/util/util.go b/ddl/util/util.go deleted file mode 100644 index 70392debb8f06..0000000000000 --- a/ddl/util/util.go +++ /dev/null @@ -1,364 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package util - -import ( - "bytes" - "context" - "encoding/hex" - "fmt" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/tikvrpc" - clientv3 "go.etcd.io/etcd/client/v3" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -const ( - deleteRangesTable = `gc_delete_range` - doneDeleteRangesTable = `gc_delete_range_done` - loadDeleteRangeSQL = `SELECT HIGH_PRIORITY job_id, element_id, start_key, end_key FROM mysql.%n WHERE ts < %?` - recordDoneDeletedRangeSQL = `INSERT IGNORE INTO mysql.gc_delete_range_done SELECT * FROM mysql.gc_delete_range WHERE job_id = %? AND element_id = %?` - completeDeleteRangeSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %? AND element_id = %?` - completeDeleteMultiRangesSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %?` - updateDeleteRangeSQL = `UPDATE mysql.gc_delete_range SET start_key = %? WHERE job_id = %? AND element_id = %? AND start_key = %?` - deleteDoneRecordSQL = `DELETE FROM mysql.gc_delete_range_done WHERE job_id = %? AND element_id = %?` - loadGlobalVars = `SELECT HIGH_PRIORITY variable_name, variable_value from mysql.global_variables where variable_name in (` // + nameList + ")" - // KeyOpDefaultTimeout is the default timeout for each key operation. - KeyOpDefaultTimeout = 2 * time.Second - // KeyOpRetryInterval is the interval between two key operations. - KeyOpRetryInterval = 30 * time.Millisecond - // DDLAllSchemaVersions is the path on etcd that is used to store all servers current schema versions. - DDLAllSchemaVersions = "/tidb/ddl/all_schema_versions" - // DDLAllSchemaVersionsByJob is the path on etcd that is used to store all servers current schema versions. - DDLAllSchemaVersionsByJob = "/tidb/ddl/all_schema_by_job_versions" - // DDLGlobalSchemaVersion is the path on etcd that is used to store the latest schema versions. - DDLGlobalSchemaVersion = "/tidb/ddl/global_schema_version" - // ServerGlobalState is the path on etcd that is used to store the server global state. - ServerGlobalState = "/tidb/server/global_state" - // SessionTTL is the etcd session's TTL in seconds. - SessionTTL = 90 -) - -// DelRangeTask is for run delete-range command in gc_worker. -type DelRangeTask struct { - StartKey kv.Key - EndKey kv.Key - JobID int64 - ElementID int64 -} - -// Range returns the range [start, end) to delete. -func (t DelRangeTask) Range() (kv.Key, kv.Key) { - return t.StartKey, t.EndKey -} - -// LoadDeleteRanges loads delete range tasks from gc_delete_range table. -func LoadDeleteRanges(ctx context.Context, sctx sessionctx.Context, safePoint uint64) (ranges []DelRangeTask, _ error) { - return loadDeleteRangesFromTable(ctx, sctx, deleteRangesTable, safePoint) -} - -// LoadDoneDeleteRanges loads deleted ranges from gc_delete_range_done table. -func LoadDoneDeleteRanges(ctx context.Context, sctx sessionctx.Context, safePoint uint64) (ranges []DelRangeTask, _ error) { - return loadDeleteRangesFromTable(ctx, sctx, doneDeleteRangesTable, safePoint) -} - -func loadDeleteRangesFromTable(ctx context.Context, sctx sessionctx.Context, table string, safePoint uint64) (ranges []DelRangeTask, _ error) { - rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, loadDeleteRangeSQL, table, safePoint) - if rs != nil { - defer terror.Call(rs.Close) - } - if err != nil { - return nil, errors.Trace(err) - } - - req := rs.NewChunk(nil) - it := chunk.NewIterator4Chunk(req) - for { - err = rs.Next(context.TODO(), req) - if err != nil { - return nil, errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - - for row := it.Begin(); row != it.End(); row = it.Next() { - startKey, err := hex.DecodeString(row.GetString(2)) - if err != nil { - return nil, errors.Trace(err) - } - endKey, err := hex.DecodeString(row.GetString(3)) - if err != nil { - return nil, errors.Trace(err) - } - ranges = append(ranges, DelRangeTask{ - JobID: row.GetInt64(0), - ElementID: row.GetInt64(1), - StartKey: startKey, - EndKey: endKey, - }) - } - } - return ranges, nil -} - -// CompleteDeleteRange moves a record from gc_delete_range table to gc_delete_range_done table. -func CompleteDeleteRange(sctx sessionctx.Context, dr DelRangeTask, needToRecordDone bool) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "BEGIN") - if err != nil { - return errors.Trace(err) - } - - if needToRecordDone { - _, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, recordDoneDeletedRangeSQL, dr.JobID, dr.ElementID) - if err != nil { - return errors.Trace(err) - } - } - - err = RemoveFromGCDeleteRange(sctx, dr.JobID, dr.ElementID) - if err != nil { - return errors.Trace(err) - } - _, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "COMMIT") - return errors.Trace(err) -} - -// RemoveFromGCDeleteRange is exported for ddl pkg to use. -func RemoveFromGCDeleteRange(sctx sessionctx.Context, jobID, elementID int64) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, completeDeleteRangeSQL, jobID, elementID) - return errors.Trace(err) -} - -// RemoveMultiFromGCDeleteRange is exported for ddl pkg to use. -func RemoveMultiFromGCDeleteRange(ctx context.Context, sctx sessionctx.Context, jobID int64) error { - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, completeDeleteMultiRangesSQL, jobID) - return errors.Trace(err) -} - -// DeleteDoneRecord removes a record from gc_delete_range_done table. -func DeleteDoneRecord(sctx sessionctx.Context, dr DelRangeTask) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, deleteDoneRecordSQL, dr.JobID, dr.ElementID) - return errors.Trace(err) -} - -// UpdateDeleteRange is only for emulator. -func UpdateDeleteRange(sctx sessionctx.Context, dr DelRangeTask, newStartKey, oldStartKey kv.Key) error { - newStartKeyHex := hex.EncodeToString(newStartKey) - oldStartKeyHex := hex.EncodeToString(oldStartKey) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, updateDeleteRangeSQL, newStartKeyHex, dr.JobID, dr.ElementID, oldStartKeyHex) - return errors.Trace(err) -} - -// LoadDDLReorgVars loads ddl reorg variable from mysql.global_variables. -func LoadDDLReorgVars(ctx context.Context, sctx sessionctx.Context) error { - // close issue #21391 - // variable.TiDBRowFormatVersion is used to encode the new row for column type change. - return LoadGlobalVars(ctx, sctx, []string{variable.TiDBDDLReorgWorkerCount, variable.TiDBDDLReorgBatchSize, variable.TiDBRowFormatVersion}) -} - -// LoadDDLVars loads ddl variable from mysql.global_variables. -func LoadDDLVars(ctx sessionctx.Context) error { - return LoadGlobalVars(context.Background(), ctx, []string{variable.TiDBDDLErrorCountLimit}) -} - -// LoadGlobalVars loads global variable from mysql.global_variables. -func LoadGlobalVars(ctx context.Context, sctx sessionctx.Context, varNames []string) error { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) - if e, ok := sctx.(sqlexec.RestrictedSQLExecutor); ok { - var buf strings.Builder - buf.WriteString(loadGlobalVars) - paramNames := make([]interface{}, 0, len(varNames)) - for i, name := range varNames { - if i > 0 { - buf.WriteString(", ") - } - buf.WriteString("%?") - paramNames = append(paramNames, name) - } - buf.WriteString(")") - rows, _, err := e.ExecRestrictedSQL(ctx, nil, buf.String(), paramNames...) - if err != nil { - return errors.Trace(err) - } - for _, row := range rows { - varName := row.GetString(0) - varValue := row.GetString(1) - if err = sctx.GetSessionVars().SetSystemVarWithoutValidation(varName, varValue); err != nil { - return err - } - } - } - return nil -} - -// GetTimeZone gets the session location's zone name and offset. -func GetTimeZone(sctx sessionctx.Context) (string, int) { - loc := sctx.GetSessionVars().Location() - name := loc.String() - if name != "" { - _, err := time.LoadLocation(name) - if err == nil { - return name, 0 - } - } - _, offset := time.Now().In(loc).Zone() - return "", offset -} - -// enableEmulatorGC means whether to enable emulator GC. The default is enable. -// In some unit tests, we want to stop emulator GC, then wen can set enableEmulatorGC to 0. -var emulatorGCEnable = atomicutil.NewInt32(1) - -// EmulatorGCEnable enables emulator gc. It exports for testing. -func EmulatorGCEnable() { - emulatorGCEnable.Store(1) -} - -// EmulatorGCDisable disables emulator gc. It exports for testing. -func EmulatorGCDisable() { - emulatorGCEnable.Store(0) -} - -// IsEmulatorGCEnable indicates whether emulator GC enabled. It exports for testing. -func IsEmulatorGCEnable() bool { - return emulatorGCEnable.Load() == 1 -} - -var internalResourceGroupTag = []byte{0} - -// GetInternalResourceGroupTaggerForTopSQL only use for testing. -func GetInternalResourceGroupTaggerForTopSQL() tikvrpc.ResourceGroupTagger { - tagger := func(req *tikvrpc.Request) { - req.ResourceGroupTag = internalResourceGroupTag - } - return tagger -} - -// IsInternalResourceGroupTaggerForTopSQL use for testing. -func IsInternalResourceGroupTaggerForTopSQL(tag []byte) bool { - return bytes.Equal(tag, internalResourceGroupTag) -} - -// DeleteKeyFromEtcd deletes key value from etcd. -func DeleteKeyFromEtcd(key string, etcdCli *clientv3.Client, retryCnt int, timeout time.Duration) error { - var err error - ctx := context.Background() - for i := 0; i < retryCnt; i++ { - childCtx, cancel := context.WithTimeout(ctx, timeout) - _, err = etcdCli.Delete(childCtx, key) - cancel() - if err == nil { - return nil - } - logutil.BgLogger().Warn("etcd-cli delete key failed", zap.String("category", "ddl"), zap.String("key", key), zap.Error(err), zap.Int("retryCnt", i)) - } - return errors.Trace(err) -} - -// PutKVToEtcd puts key value to etcd. -// etcdCli is client of etcd. -// retryCnt is retry time when an error occurs. -// opts are configures of etcd Operations. -func PutKVToEtcd(ctx context.Context, etcdCli *clientv3.Client, retryCnt int, key, val string, - opts ...clientv3.OpOption) error { - var err error - for i := 0; i < retryCnt; i++ { - if IsContextDone(ctx) { - return errors.Trace(ctx.Err()) - } - - childCtx, cancel := context.WithTimeout(ctx, KeyOpDefaultTimeout) - _, err = etcdCli.Put(childCtx, key, val, opts...) - cancel() - if err == nil { - return nil - } - logutil.BgLogger().Warn("etcd-cli put kv failed", zap.String("category", "ddl"), zap.String("key", key), zap.String("value", val), zap.Error(err), zap.Int("retryCnt", i)) - time.Sleep(KeyOpRetryInterval) - } - return errors.Trace(err) -} - -// IsContextDone checks if context is done. -func IsContextDone(ctx context.Context) bool { - select { - case <-ctx.Done(): - return true - default: - } - return false -} - -// WrapKey2String wraps the key to a string. -func WrapKey2String(key []byte) string { - if len(key) == 0 { - return "''" - } - return fmt.Sprintf("0x%x", key) -} - -const ( - getRaftKvVersionSQL = "show config where type = 'tikv' and name = 'storage.engine'" - raftKv2 = "raft-kv2" -) - -// IsRaftKv2 checks whether the raft-kv2 is enabled -func IsRaftKv2(ctx context.Context, sctx sessionctx.Context) (bool, error) { - // Mock store does not support `show config` now, so we use failpoint here - // to control whether we are in raft-kv2 - failpoint.Inject("IsRaftKv2", func(v failpoint.Value) (bool, error) { - v2, _ := v.(bool) - return v2, nil - }) - - rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, getRaftKvVersionSQL) - if err != nil { - return false, err - } - if rs == nil { - return false, nil - } - - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, sctx.GetSessionVars().MaxChunkSize) - if err != nil { - return false, errors.Trace(err) - } - if len(rows) == 0 { - return false, nil - } - - // All nodes should have the same type of engine - raftVersion := rows[0].GetString(3) - return raftVersion == raftKv2, nil -} diff --git a/distsql/BUILD.bazel b/distsql/BUILD.bazel deleted file mode 100644 index 2601df8d0d0ce..0000000000000 --- a/distsql/BUILD.bazel +++ /dev/null @@ -1,98 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "distsql", - srcs = [ - "distsql.go", - "request_builder.go", - "select_result.go", - ], - importpath = "github.com/pingcap/tidb/distsql", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl/placement", - "//errno", - "//expression", - "//infoschema", - "//kv", - "//metrics", - "//parser/mysql", - "//parser/terror", - "//planner/util", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//store/copr", - "//tablecodec", - "//telemetry", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/execdetails", - "//util/logutil", - "//util/memory", - "//util/ranger", - "//util/tracing", - "//util/trxevents", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//metrics", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@org_golang_google_grpc//metadata", - "@org_golang_x_exp//maps", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "distsql_test", - timeout = "short", - srcs = [ - "bench_test.go", - "distsql_test.go", - "main_test.go", - "request_builder_test.go", - "select_result_test.go", - ], - embed = [":distsql"], - flaky = True, - race = "on", - shard_count = 24, - deps = [ - "//domain/resourcegroup", - "//kv", - "//parser/charset", - "//parser/mysql", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//store/copr", - "//tablecodec", - "//testkit/testsetup", - "//types", - "//util/benchdaily", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/disk", - "//util/execdetails", - "//util/mathutil", - "//util/memory", - "//util/mock", - "//util/paging", - "//util/ranger", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/distsql/bench_test.go b/distsql/bench_test.go deleted file mode 100644 index 3855547f2083f..0000000000000 --- a/distsql/bench_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package distsql - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/chunk" -) - -func BenchmarkSelectResponseChunk_BigResponse(b *testing.B) { - for i := 0; i < b.N; i++ { - b.StopTimer() - sctx := newMockSessionContext() - sctx.GetSessionVars().InitChunkSize = 32 - sctx.GetSessionVars().MaxChunkSize = 1024 - selectResult, colTypes := createSelectNormalByBenchmarkTest(4000, 20000, sctx) - chk := chunk.NewChunkWithCapacity(colTypes, 1024) - b.StartTimer() - for { - err := selectResult.Next(context.TODO(), chk) - if err != nil { - panic(err) - } - if chk.NumRows() == 0 { - break - } - chk.Reset() - } - } -} - -func BenchmarkSelectResponseChunk_SmallResponse(b *testing.B) { - for i := 0; i < b.N; i++ { - b.StopTimer() - sctx := newMockSessionContext() - sctx.GetSessionVars().InitChunkSize = 32 - sctx.GetSessionVars().MaxChunkSize = 1024 - selectResult, colTypes := createSelectNormalByBenchmarkTest(32, 3200, sctx) - chk := chunk.NewChunkWithCapacity(colTypes, 1024) - b.StartTimer() - for { - err := selectResult.Next(context.TODO(), chk) - if err != nil { - panic(err) - } - if chk.NumRows() == 0 { - break - } - chk.Reset() - } - } -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkSelectResponseChunk_BigResponse, - BenchmarkSelectResponseChunk_SmallResponse, - ) -} diff --git a/distsql/distsql.go b/distsql/distsql.go deleted file mode 100644 index 375664d4a2783..0000000000000 --- a/distsql/distsql.go +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package distsql - -import ( - "context" - "strconv" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tidb/util/trxevents" - "github.com/pingcap/tipb/go-tipb" - "github.com/tikv/client-go/v2/tikvrpc/interceptor" - "go.uber.org/zap" - "google.golang.org/grpc/metadata" -) - -// GenSelectResultFromResponse generates an iterator from response. -func GenSelectResultFromResponse(sctx sessionctx.Context, fieldTypes []*types.FieldType, planIDs []int, rootID int, resp kv.Response) SelectResult { - // TODO: Add metric label and set open tracing. - return &selectResult{ - label: "mpp", - resp: resp, - rowLen: len(fieldTypes), - fieldTypes: fieldTypes, - ctx: sctx, - copPlanIDs: planIDs, - rootPlanID: rootID, - storeType: kv.TiFlash, - } -} - -// Select sends a DAG request, returns SelectResult. -// In kvReq, KeyRanges is required, Concurrency/KeepOrder/Desc/IsolationLevel/Priority are optional. -func Select(ctx context.Context, sctx sessionctx.Context, kvReq *kv.Request, fieldTypes []*types.FieldType) (SelectResult, error) { - r, ctx := tracing.StartRegionEx(ctx, "distsql.Select") - defer r.End() - - // For testing purpose. - if hook := ctx.Value("CheckSelectRequestHook"); hook != nil { - hook.(func(*kv.Request))(kvReq) - } - - enabledRateLimitAction := sctx.GetSessionVars().EnabledRateLimitAction - originalSQL := sctx.GetSessionVars().StmtCtx.OriginalSQL - eventCb := func(event trxevents.TransactionEvent) { - // Note: Do not assume this callback will be invoked within the same goroutine. - if copMeetLock := event.GetCopMeetLock(); copMeetLock != nil { - logutil.Logger(ctx).Debug("coprocessor encounters lock", - zap.Uint64("startTS", kvReq.StartTs), - zap.Stringer("lock", copMeetLock.LockInfo), - zap.String("stmt", originalSQL)) - } - } - - ctx = WithSQLKvExecCounterInterceptor(ctx, sctx.GetSessionVars().StmtCtx) - option := &kv.ClientSendOption{ - SessionMemTracker: sctx.GetSessionVars().MemTracker, - EnabledRateLimitAction: enabledRateLimitAction, - EventCb: eventCb, - EnableCollectExecutionInfo: config.GetGlobalConfig().Instance.EnableCollectExecutionInfo.Load(), - } - - if kvReq.StoreType == kv.TiFlash { - ctx = SetTiFlashConfVarsInContext(ctx, sctx) - option.TiFlashReplicaRead = sctx.GetSessionVars().TiFlashReplicaRead - option.AppendWarning = sctx.GetSessionVars().StmtCtx.AppendWarning - } - - resp := sctx.GetClient().Send(ctx, kvReq, sctx.GetSessionVars().KVVars, option) - if resp == nil { - return nil, errors.New("client returns nil response") - } - - label := metrics.LblGeneral - if sctx.GetSessionVars().InRestrictedSQL { - label = metrics.LblInternal - } - - // kvReq.MemTracker is used to trace and control memory usage in DistSQL layer; - // for selectResult, we just use the kvReq.MemTracker prepared for co-processor - // instead of creating a new one for simplification. - return &selectResult{ - label: "dag", - resp: resp, - rowLen: len(fieldTypes), - fieldTypes: fieldTypes, - ctx: sctx, - sqlType: label, - memTracker: kvReq.MemTracker, - storeType: kvReq.StoreType, - paging: kvReq.Paging.Enable, - distSQLConcurrency: kvReq.Concurrency, - }, nil -} - -// SetTiFlashConfVarsInContext set some TiFlash config variables in context. -func SetTiFlashConfVarsInContext(ctx context.Context, sctx sessionctx.Context) context.Context { - if sctx.GetSessionVars().TiFlashMaxThreads != -1 { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxTiFlashThreads, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxThreads, 10)) - } - if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalJoin != -1 { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalJoin, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalJoin, 10)) - } - if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalGroupBy != -1 { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalGroupBy, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalGroupBy, 10)) - } - if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalSort != -1 { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalSort, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalSort, 10)) - } - if sctx.GetSessionVars().TiFlashMaxQueryMemoryPerNode <= 0 { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashMemQuotaQueryPerNode, "0") - } else { - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashMemQuotaQueryPerNode, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxQueryMemoryPerNode, 10)) - } - ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashQuerySpillRatio, strconv.FormatFloat(sctx.GetSessionVars().TiFlashQuerySpillRatio, 'f', -1, 64)) - return ctx -} - -// SelectWithRuntimeStats sends a DAG request, returns SelectResult. -// The difference from Select is that SelectWithRuntimeStats will set copPlanIDs into selectResult, -// which can help selectResult to collect runtime stats. -func SelectWithRuntimeStats(ctx context.Context, sctx sessionctx.Context, kvReq *kv.Request, - fieldTypes []*types.FieldType, copPlanIDs []int, rootPlanID int) (SelectResult, error) { - sr, err := Select(ctx, sctx, kvReq, fieldTypes) - if err != nil { - return nil, err - } - if selectResult, ok := sr.(*selectResult); ok { - selectResult.copPlanIDs = copPlanIDs - selectResult.rootPlanID = rootPlanID - } - return sr, nil -} - -// Analyze do a analyze request. -func Analyze(ctx context.Context, client kv.Client, kvReq *kv.Request, vars interface{}, - isRestrict bool, stmtCtx *stmtctx.StatementContext) (SelectResult, error) { - ctx = WithSQLKvExecCounterInterceptor(ctx, stmtCtx) - kvReq.RequestSource.RequestSourceInternal = true - kvReq.RequestSource.RequestSourceType = kv.InternalTxnStats - resp := client.Send(ctx, kvReq, vars, &kv.ClientSendOption{}) - if resp == nil { - return nil, errors.New("client returns nil response") - } - label := metrics.LblGeneral - if isRestrict { - label = metrics.LblInternal - } - result := &selectResult{ - label: "analyze", - resp: resp, - sqlType: label, - storeType: kvReq.StoreType, - } - return result, nil -} - -// Checksum sends a checksum request. -func Checksum(ctx context.Context, client kv.Client, kvReq *kv.Request, vars interface{}) (SelectResult, error) { - // FIXME: As BR have dependency of `Checksum` and TiDB also introduced BR as dependency, Currently we can't edit - // Checksum function signature. The two-way dependence should be removed in the future. - resp := client.Send(ctx, kvReq, vars, &kv.ClientSendOption{}) - if resp == nil { - return nil, errors.New("client returns nil response") - } - result := &selectResult{ - label: "checksum", - resp: resp, - sqlType: metrics.LblGeneral, - storeType: kvReq.StoreType, - } - return result, nil -} - -// SetEncodeType sets the encoding method for the DAGRequest. The supported encoding -// methods are: -// 1. TypeChunk: the result is encoded using the Chunk format, refer util/chunk/chunk.go -// 2. TypeDefault: the result is encoded row by row -func SetEncodeType(ctx sessionctx.Context, dagReq *tipb.DAGRequest) { - if canUseChunkRPC(ctx) { - dagReq.EncodeType = tipb.EncodeType_TypeChunk - setChunkMemoryLayout(dagReq) - } else { - dagReq.EncodeType = tipb.EncodeType_TypeDefault - } -} - -func canUseChunkRPC(ctx sessionctx.Context) bool { - if !ctx.GetSessionVars().EnableChunkRPC { - return false - } - if !checkAlignment() { - return false - } - return true -} - -var supportedAlignment = unsafe.Sizeof(types.MyDecimal{}) == 40 - -// checkAlignment checks the alignment in current system environment. -// The alignment is influenced by system, machine and Golang version. -// Using this function can guarantee the alignment is we want. -func checkAlignment() bool { - return supportedAlignment -} - -var systemEndian tipb.Endian - -// setChunkMemoryLayout sets the chunk memory layout for the DAGRequest. -func setChunkMemoryLayout(dagReq *tipb.DAGRequest) { - dagReq.ChunkMemoryLayout = &tipb.ChunkMemoryLayout{Endian: GetSystemEndian()} -} - -// GetSystemEndian gets the system endian. -func GetSystemEndian() tipb.Endian { - return systemEndian -} - -func init() { - i := 0x0100 - ptr := unsafe.Pointer(&i) - if 0x01 == *(*byte)(ptr) { - systemEndian = tipb.Endian_BigEndian - } else { - systemEndian = tipb.Endian_LittleEndian - } -} - -// WithSQLKvExecCounterInterceptor binds an interceptor for client-go to count the -// number of SQL executions of each TiKV (if any). -func WithSQLKvExecCounterInterceptor(ctx context.Context, stmtCtx *stmtctx.StatementContext) context.Context { - if stmtCtx.KvExecCounter != nil { - // Unlike calling Transaction or Snapshot interface, in distsql package we directly - // face tikv Request. So we need to manually bind RPCInterceptor to ctx. Instead of - // calling SetRPCInterceptor on Transaction or Snapshot. - return interceptor.WithRPCInterceptor(ctx, stmtCtx.KvExecCounter.RPCInterceptor()) - } - return ctx -} diff --git a/distsql/distsql_test.go b/distsql/distsql_test.go deleted file mode 100644 index 660b7f36f5cc7..0000000000000 --- a/distsql/distsql_test.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package distsql - -import ( - "context" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tipb/go-tipb" - "github.com/stretchr/testify/require" - tikvstore "github.com/tikv/client-go/v2/kv" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" -) - -func TestSelectNormal(t *testing.T) { - response, colTypes := createSelectNormal(t, 1, 2, nil, nil) - - // Test Next. - chk := chunk.New(colTypes, 32, 32) - numAllRows := 0 - for { - err := response.Next(context.TODO(), chk) - require.NoError(t, err) - numAllRows += chk.NumRows() - if chk.NumRows() == 0 { - break - } - } - require.Equal(t, 2, numAllRows) - require.NoError(t, response.Close()) - require.Equal(t, int64(0), response.memTracker.BytesConsumed()) -} - -func TestSelectMemTracker(t *testing.T) { - response, colTypes := createSelectNormal(t, 2, 6, nil, nil) - - // Test Next. - chk := chunk.New(colTypes, 3, 3) - err := response.Next(context.TODO(), chk) - require.NoError(t, err) - require.True(t, chk.IsFull()) - require.NoError(t, response.Close()) - require.Equal(t, int64(0), response.memTracker.BytesConsumed()) -} - -func TestSelectNormalChunkSize(t *testing.T) { - sctx := newMockSessionContext() - sctx.GetSessionVars().EnableChunkRPC = false - response, colTypes := createSelectNormal(t, 100, 1000000, nil, sctx) - testChunkSize(t, response, colTypes) - require.NoError(t, response.Close()) - require.Equal(t, int64(0), response.memTracker.BytesConsumed()) -} - -func TestSelectWithRuntimeStats(t *testing.T) { - planIDs := []int{1, 2, 3} - response, colTypes := createSelectNormal(t, 1, 2, planIDs, nil) - - require.Equal(t, len(planIDs), len(response.copPlanIDs), "invalid copPlanIDs") - for i := range planIDs { - require.Equal(t, planIDs[i], response.copPlanIDs[i], "invalid copPlanIDs") - } - - // Test Next. - chk := chunk.New(colTypes, 32, 32) - numAllRows := 0 - for { - err := response.Next(context.TODO(), chk) - require.NoError(t, err) - numAllRows += chk.NumRows() - if chk.NumRows() == 0 { - break - } - } - require.Equal(t, 2, numAllRows) - require.NoError(t, response.Close()) -} - -func TestSelectResultRuntimeStats(t *testing.T) { - stmtStats := execdetails.NewRuntimeStatsColl(nil) - basic := stmtStats.GetBasicRuntimeStats(1) - basic.Record(time.Second, 20) - s1 := &selectResultRuntimeStats{ - backoffSleep: map[string]time.Duration{"RegionMiss": time.Millisecond}, - totalProcessTime: time.Second, - totalWaitTime: time.Second, - rpcStat: tikv.NewRegionRequestRuntimeStats(), - distSQLConcurrency: 15, - } - s1.copRespTime.Add(execdetails.Duration(time.Second)) - s1.copRespTime.Add(execdetails.Duration(time.Millisecond)) - s1.procKeys.Add(100) - s1.procKeys.Add(200) - - s2 := *s1 - stmtStats.RegisterStats(1, s1) - stmtStats.RegisterStats(1, &s2) - stats := stmtStats.GetRootStats(1) - expect := "time:1s, loops:1, cop_task: {num: 4, max: 1s, min: 1ms, avg: 500.5ms, p95: 1s, max_proc_keys: 200, p95_proc_keys: 200, tot_proc: 2s, tot_wait: 2s, copr_cache_hit_ratio: 0.00, max_distsql_concurrency: 15}, backoff{RegionMiss: 2ms}" - require.Equal(t, expect, stats.String()) - // Test for idempotence. - require.Equal(t, expect, stats.String()) - - s1.rpcStat.Stats[tikvrpc.CmdCop] = &tikv.RPCRuntimeStats{ - Count: 1, - Consume: int64(time.Second), - } - stmtStats.RegisterStats(2, s1) - stats = stmtStats.GetRootStats(2) - expect = "cop_task: {num: 2, max: 1s, min: 1ms, avg: 500.5ms, p95: 1s, max_proc_keys: 200, p95_proc_keys: 200, tot_proc: 1s, tot_wait: 1s, rpc_num: 1, rpc_time: 1s, copr_cache_hit_ratio: 0.00, max_distsql_concurrency: 15}, backoff{RegionMiss: 1ms}" - require.Equal(t, expect, stats.String()) - // Test for idempotence. - require.Equal(t, expect, stats.String()) - - s1 = &selectResultRuntimeStats{ - backoffSleep: map[string]time.Duration{"RegionMiss": time.Millisecond}, - totalProcessTime: time.Second, - totalWaitTime: time.Second, - rpcStat: tikv.NewRegionRequestRuntimeStats(), - } - s1.copRespTime.Add(execdetails.Duration(time.Second)) - s1.procKeys.Add(100) - expect = "cop_task: {num: 1, max: 1s, proc_keys: 100, tot_proc: 1s, tot_wait: 1s, copr_cache_hit_ratio: 0.00}, backoff{RegionMiss: 1ms}" - require.Equal(t, expect, s1.String()) -} - -func TestAnalyze(t *testing.T) { - sctx := newMockSessionContext() - sctx.GetSessionVars().EnableChunkRPC = false - request, err := (&RequestBuilder{}).SetKeyRanges(nil). - SetAnalyzeRequest(&tipb.AnalyzeReq{}, kv.RC). - SetKeepOrder(true). - Build() - require.NoError(t, err) - - response, err := Analyze(context.TODO(), sctx.GetClient(), request, tikvstore.DefaultVars, true, sctx.GetSessionVars().StmtCtx) - require.NoError(t, err) - - result, ok := response.(*selectResult) - require.True(t, ok) - - require.Equal(t, "analyze", result.label) - require.Equal(t, "internal", result.sqlType) - - bytes, err := response.NextRaw(context.TODO()) - require.NoError(t, err) - require.Len(t, bytes, 16) - - require.NoError(t, response.Close()) -} - -func TestChecksum(t *testing.T) { - sctx := newMockSessionContext() - sctx.GetSessionVars().EnableChunkRPC = false - request, err := (&RequestBuilder{}).SetKeyRanges(nil). - SetChecksumRequest(&tipb.ChecksumRequest{}). - Build() - require.NoError(t, err) - - response, err := Checksum(context.TODO(), sctx.GetClient(), request, tikvstore.DefaultVars) - require.NoError(t, err) - - result, ok := response.(*selectResult) - require.True(t, ok) - require.Equal(t, "checksum", result.label) - require.Equal(t, "general", result.sqlType) - - bytes, err := response.NextRaw(context.TODO()) - require.NoError(t, err) - require.Len(t, bytes, 16) - - require.NoError(t, response.Close()) -} - -// mockResponse implements kv.Response interface. -// Used only for test. -type mockResponse struct { - count int - total int - batch int - ctx sessionctx.Context - sync.Mutex -} - -// Close implements kv.Response interface. -func (resp *mockResponse) Close() error { - resp.Lock() - defer resp.Unlock() - - resp.count = 0 - return nil -} - -// Next implements kv.Response interface. -func (resp *mockResponse) Next(context.Context) (kv.ResultSubset, error) { - resp.Lock() - defer resp.Unlock() - - if resp.count >= resp.total { - return nil, nil - } - numRows := mathutil.Min(resp.batch, resp.total-resp.count) - resp.count += numRows - - var chunks []tipb.Chunk - if !canUseChunkRPC(resp.ctx) { - datum := types.NewIntDatum(1) - bytes := make([]byte, 0, 100) - bytes, _ = codec.EncodeValue(nil, bytes, datum, datum, datum, datum) - chunks = make([]tipb.Chunk, numRows) - for i := range chunks { - chkData := make([]byte, len(bytes)) - copy(chkData, bytes) - chunks[i] = tipb.Chunk{RowsData: chkData} - } - } else { - chunks = make([]tipb.Chunk, 0) - for numRows > 0 { - rows := mathutil.Min(numRows, 1024) - numRows -= rows - - colTypes := make([]*types.FieldType, 4) - for i := 0; i < 4; i++ { - colTypes[i] = types.NewFieldTypeBuilder().SetType(mysql.TypeLonglong).BuildP() - } - chk := chunk.New(colTypes, numRows, numRows) - - for rowOrdinal := 0; rowOrdinal < rows; rowOrdinal++ { - for colOrdinal := 0; colOrdinal < 4; colOrdinal++ { - chk.AppendInt64(colOrdinal, 123) - } - } - - codec := chunk.NewCodec(colTypes) - buffer := codec.Encode(chk) - chunks = append(chunks, tipb.Chunk{RowsData: buffer}) - } - } - - respPB := &tipb.SelectResponse{ - Chunks: chunks, - OutputCounts: []int64{1}, - } - if canUseChunkRPC(resp.ctx) { - respPB.EncodeType = tipb.EncodeType_TypeChunk - } else { - respPB.EncodeType = tipb.EncodeType_TypeDefault - } - respBytes, err := respPB.Marshal() - if err != nil { - panic(err) - } - return &mockResultSubset{respBytes}, nil -} - -// mockResultSubset implements kv.ResultSubset interface. -// Used only for test. -type mockResultSubset struct{ data []byte } - -// GetData implements kv.ResultSubset interface. -func (r *mockResultSubset) GetData() []byte { return r.data } - -// GetStartKey implements kv.ResultSubset interface. -func (r *mockResultSubset) GetStartKey() kv.Key { return nil } - -// MemSize implements kv.ResultSubset interface. -func (r *mockResultSubset) MemSize() int64 { return int64(cap(r.data)) } - -// RespTime implements kv.ResultSubset interface. -func (r *mockResultSubset) RespTime() time.Duration { return 0 } - -func newMockSessionContext() sessionctx.Context { - ctx := mock.NewContext() - ctx.GetSessionVars().StmtCtx = stmtctx.NewStmtCtx() - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) - - ctx.Store = &mock.Store{ - Client: &mock.Client{ - MockResponse: &mockResponse{ - ctx: ctx, - batch: 1, - total: 2, - }, - }, - } - return ctx -} - -func createSelectNormalByBenchmarkTest(batch, totalRows int, ctx sessionctx.Context) (*selectResult, []*types.FieldType) { - request, _ := (&RequestBuilder{}).SetKeyRanges(nil). - SetDAGRequest(&tipb.DAGRequest{}). - SetDesc(false). - SetKeepOrder(false). - SetFromSessionVars(variable.NewSessionVars(nil)). - SetMemTracker(memory.NewTracker(-1, -1)). - Build() - - // 4 int64 types. - ftb := types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) - colTypes := []*types.FieldType{ - ftb.BuildP(), - } - colTypes = append(colTypes, colTypes[0]) - colTypes = append(colTypes, colTypes[0]) - colTypes = append(colTypes, colTypes[0]) - - // Test Next. - var response SelectResult - response, _ = Select(context.TODO(), ctx, request, colTypes) - - result, _ := response.(*selectResult) - resp, _ := result.resp.(*mockResponse) - resp.total = totalRows - resp.batch = batch - - return result, colTypes -} - -func testChunkSize(t *testing.T, response SelectResult, colTypes []*types.FieldType) { - chk := chunk.New(colTypes, 32, 32) - - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) - - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) - - chk.SetRequiredRows(1, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 1, chk.NumRows()) - - chk.SetRequiredRows(2, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 2, chk.NumRows()) - - chk.SetRequiredRows(17, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 17, chk.NumRows()) - - chk.SetRequiredRows(170, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) - - chk.SetRequiredRows(32, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) - - chk.SetRequiredRows(0, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) - - chk.SetRequiredRows(-1, 32) - require.NoError(t, response.Next(context.TODO(), chk)) - require.Equal(t, 32, chk.NumRows()) -} - -func createSelectNormal(t *testing.T, batch, totalRows int, planIDs []int, sctx sessionctx.Context) (*selectResult, []*types.FieldType) { - request, err := (&RequestBuilder{}).SetKeyRanges(nil). - SetDAGRequest(&tipb.DAGRequest{}). - SetDesc(false). - SetKeepOrder(false). - SetFromSessionVars(variable.NewSessionVars(nil)). - SetMemTracker(memory.NewTracker(-1, -1)). - Build() - require.NoError(t, err) - - // 4 int64 types. - ftb := types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) - colTypes := []*types.FieldType{ - ftb.BuildP(), - } - colTypes = append(colTypes, colTypes[0]) - colTypes = append(colTypes, colTypes[0]) - colTypes = append(colTypes, colTypes[0]) - - if sctx == nil { - sctx = newMockSessionContext() - } - - // Test Next. - var response SelectResult - if planIDs == nil { - response, err = Select(context.TODO(), sctx, request, colTypes) - } else { - response, err = SelectWithRuntimeStats(context.TODO(), sctx, request, colTypes, planIDs, 1) - } - - require.NoError(t, err) - result, ok := response.(*selectResult) - - require.True(t, ok) - require.Equal(t, "general", result.sqlType) - require.Equal(t, "dag", result.label) - require.Len(t, colTypes, result.rowLen) - - resp, ok := result.resp.(*mockResponse) - require.True(t, ok) - - resp.total = totalRows - resp.batch = batch - - return result, colTypes -} diff --git a/distsql/main_test.go b/distsql/main_test.go deleted file mode 100644 index 33aa940acde49..0000000000000 --- a/distsql/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package distsql - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/disttask/framework/BUILD.bazel b/disttask/framework/BUILD.bazel deleted file mode 100644 index a974d5bb41ff3..0000000000000 --- a/disttask/framework/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "framework_test", - timeout = "short", - srcs = [ - "framework_dynamic_dispatch_test.go", - "framework_err_handling_test.go", - "framework_ha_test.go", - "framework_pause_and_resume_test.go", - "framework_rollback_test.go", - "framework_test.go", - ], - flaky = True, - race = "off", - shard_count = 31, - deps = [ - "//disttask/framework/dispatcher", - "//disttask/framework/handle", - "//disttask/framework/mock", - "//disttask/framework/mock/execute", - "//disttask/framework/proto", - "//disttask/framework/scheduler", - "//disttask/framework/storage", - "//domain/infosync", - "//testkit", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/dispatcher/BUILD.bazel b/disttask/framework/dispatcher/BUILD.bazel deleted file mode 100644 index 254745ffe845f..0000000000000 --- a/disttask/framework/dispatcher/BUILD.bazel +++ /dev/null @@ -1,59 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "dispatcher", - srcs = [ - "dispatcher.go", - "dispatcher_manager.go", - "interface.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/dispatcher", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/proto", - "//disttask/framework/storage", - "//domain/infosync", - "//metrics", - "//resourcemanager/pool/spool", - "//resourcemanager/util", - "//sessionctx", - "//util", - "//util/disttask", - "//util/intest", - "//util/logutil", - "//util/syncutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "dispatcher_test", - timeout = "short", - srcs = [ - "dispatcher_test.go", - "main_test.go", - ], - embed = [":dispatcher"], - flaky = True, - race = "off", - shard_count = 14, - deps = [ - "//disttask/framework/mock", - "//disttask/framework/proto", - "//disttask/framework/storage", - "//domain/infosync", - "//kv", - "//testkit", - "//testkit/testsetup", - "//util/logutil", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/dispatcher/dispatcher.go b/disttask/framework/dispatcher/dispatcher.go deleted file mode 100644 index 35d7fe30d5b2b..0000000000000 --- a/disttask/framework/dispatcher/dispatcher.go +++ /dev/null @@ -1,767 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package dispatcher - -import ( - "context" - "math/rand" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/sessionctx" - disttaskutil "github.com/pingcap/tidb/util/disttask" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -const ( - // DefaultSubtaskConcurrency is the default concurrency for handling subtask. - DefaultSubtaskConcurrency = 16 - // MaxSubtaskConcurrency is the maximum concurrency for handling subtask. - MaxSubtaskConcurrency = 256 - // DefaultLiveNodesCheckInterval is the tick interval of fetching all server infos from etcd. - DefaultLiveNodesCheckInterval = 2 -) - -var ( - checkTaskFinishedInterval = 500 * time.Millisecond - nonRetrySQLTime = 1 - // RetrySQLTimes is the max retry times when executing SQL. - RetrySQLTimes = 30 - // RetrySQLInterval is the initial interval between two SQL retries. - RetrySQLInterval = 3 * time.Second - // RetrySQLMaxInterval is the max interval between two SQL retries. - RetrySQLMaxInterval = 30 * time.Second -) - -// TaskHandle provides the interface for operations needed by Dispatcher. -// Then we can use dispatcher's function in Dispatcher interface. -type TaskHandle interface { - // GetPreviousSchedulerIDs gets previous scheduler IDs. - GetPreviousSchedulerIDs(_ context.Context, taskID int64, step int64) ([]string, error) - // GetPreviousSubtaskMetas gets previous subtask metas. - GetPreviousSubtaskMetas(taskID int64, step int64) ([][]byte, error) - storage.SessionExecutor -} - -// Dispatcher manages the lifetime of a task -// including submitting subtasks and updating the status of a task. -type Dispatcher interface { - // Init initializes the dispatcher, should be called before ExecuteTask. - // if Init returns error, dispatcher manager will fail the task directly, - // so the returned error should be a fatal error. - Init() error - // ExecuteTask start to schedule a task. - ExecuteTask() - // Close closes the dispatcher, should be called if Init returns nil. - Close() -} - -// BaseDispatcher is the base struct for Dispatcher. -// each task type embed this struct and implement the Extension interface. -type BaseDispatcher struct { - ctx context.Context - taskMgr *storage.TaskManager - Task *proto.Task - logCtx context.Context - // serverID, it's value is ip:port now. - serverID string - // when RegisterDispatcherFactory, the factory MUST initialize this field. - Extension - - // for HA - // liveNodes will fetch and store all live nodes every liveNodeInterval ticks. - liveNodes []*infosync.ServerInfo - liveNodeFetchInterval int - // liveNodeFetchTick is the tick variable. - liveNodeFetchTick int - // taskNodes stores the id of current scheduler nodes. - taskNodes []string - // rand is for generating random selection of nodes. - rand *rand.Rand -} - -// MockOwnerChange mock owner change in tests. -var MockOwnerChange func() - -// NewBaseDispatcher creates a new BaseDispatcher. -func NewBaseDispatcher(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) *BaseDispatcher { - logCtx := logutil.WithFields(context.Background(), zap.Int64("task-id", task.ID), - zap.String("task-type", task.Type)) - return &BaseDispatcher{ - ctx: ctx, - taskMgr: taskMgr, - Task: task, - logCtx: logCtx, - serverID: serverID, - liveNodes: nil, - liveNodeFetchInterval: DefaultLiveNodesCheckInterval, - liveNodeFetchTick: 0, - taskNodes: nil, - rand: rand.New(rand.NewSource(time.Now().UnixNano())), - } -} - -// Init implements the Dispatcher interface. -func (*BaseDispatcher) Init() error { - return nil -} - -// ExecuteTask implements the Dispatcher interface. -func (d *BaseDispatcher) ExecuteTask() { - logutil.Logger(d.logCtx).Info("execute one task", - zap.String("state", d.Task.State), zap.Uint64("concurrency", d.Task.Concurrency)) - d.scheduleTask() -} - -// Close closes the dispatcher. -func (*BaseDispatcher) Close() { -} - -// refreshTask fetch task state from tidb_global_task table. -func (d *BaseDispatcher) refreshTask() error { - newTask, err := d.taskMgr.GetGlobalTaskByID(d.Task.ID) - if err != nil { - logutil.Logger(d.logCtx).Error("refresh task failed", zap.Error(err)) - return err - } - // newTask might be nil when GC routine move the task into history table. - if newTask != nil { - d.Task = newTask - } - return nil -} - -// scheduleTask schedule the task execution step by step. -func (d *BaseDispatcher) scheduleTask() { - ticker := time.NewTicker(checkTaskFinishedInterval) - defer ticker.Stop() - for { - select { - case <-d.ctx.Done(): - logutil.Logger(d.logCtx).Info("schedule task exits", zap.Error(d.ctx.Err())) - return - case <-ticker.C: - err := d.refreshTask() - if err != nil { - continue - } - failpoint.Inject("cancelTaskAfterRefreshTask", func(val failpoint.Value) { - if val.(bool) && d.Task.State == proto.TaskStateRunning { - err := d.taskMgr.CancelGlobalTask(d.Task.ID) - if err != nil { - logutil.Logger(d.logCtx).Error("cancel task failed", zap.Error(err)) - } - } - }) - - failpoint.Inject("pausePendingTask", func(val failpoint.Value) { - if val.(bool) && d.Task.State == proto.TaskStatePending { - _, err := d.taskMgr.PauseTask(d.Task.Key) - if err != nil { - logutil.Logger(d.logCtx).Error("pause task failed", zap.Error(err)) - } - d.Task.State = proto.TaskStatePausing - } - }) - - failpoint.Inject("pauseTaskAfterRefreshTask", func(val failpoint.Value) { - if val.(bool) && d.Task.State == proto.TaskStateRunning { - _, err := d.taskMgr.PauseTask(d.Task.Key) - if err != nil { - logutil.Logger(d.logCtx).Error("pause task failed", zap.Error(err)) - } - d.Task.State = proto.TaskStatePausing - } - }) - - switch d.Task.State { - case proto.TaskStateCancelling: - err = d.onCancelling() - case proto.TaskStatePausing: - err = d.onPausing() - case proto.TaskStatePaused: - err = d.onPaused() - // close the dispatcher. - if err == nil { - return - } - case proto.TaskStateResuming: - err = d.onResuming() - case proto.TaskStateReverting: - err = d.onReverting() - case proto.TaskStatePending: - err = d.onPending() - case proto.TaskStateRunning: - err = d.onRunning() - case proto.TaskStateSucceed, proto.TaskStateReverted, proto.TaskStateFailed: - if err := d.onFinished(); err != nil { - logutil.Logger(d.logCtx).Error("schedule task meet error", zap.String("state", d.Task.State), zap.Error(err)) - } - return - } - if err != nil { - logutil.Logger(d.logCtx).Info("schedule task meet err, reschedule it", zap.Error(err)) - } - - failpoint.Inject("mockOwnerChange", func(val failpoint.Value) { - if val.(bool) { - logutil.Logger(d.logCtx).Info("mockOwnerChange called") - MockOwnerChange() - time.Sleep(time.Second) - } - }) - } - } -} - -// handle task in cancelling state, dispatch revert subtasks. -func (d *BaseDispatcher) onCancelling() error { - logutil.Logger(d.logCtx).Info("on cancelling state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - errs := []error{errors.New("cancel")} - return d.onErrHandlingStage(errs) -} - -// handle task in pausing state, cancel all running subtasks. -func (d *BaseDispatcher) onPausing() error { - logutil.Logger(d.logCtx).Info("on pausing state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStateRunning, proto.TaskStatePending) - if err != nil { - logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) - return err - } - if cnt == 0 { - logutil.Logger(d.logCtx).Info("all running subtasks paused, update the task to paused state") - return d.updateTask(proto.TaskStatePaused, nil, RetrySQLTimes) - } - logutil.Logger(d.logCtx).Debug("on pausing state, this task keeps current state", zap.String("state", d.Task.State)) - return nil -} - -// MockDMLExecutionOnPausedState is used to mock DML execution when tasks paused. -var MockDMLExecutionOnPausedState func(task *proto.Task) - -// handle task in paused state -func (d *BaseDispatcher) onPaused() error { - logutil.Logger(d.logCtx).Info("on paused state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - failpoint.Inject("mockDMLExecutionOnPausedState", func(val failpoint.Value) { - if val.(bool) { - MockDMLExecutionOnPausedState(d.Task) - } - }) - return nil -} - -// TestSyncChan is used to sync the test. -var TestSyncChan = make(chan struct{}) - -// handle task in resuming state -func (d *BaseDispatcher) onResuming() error { - logutil.Logger(d.logCtx).Info("on resuming state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStatePaused) - if err != nil { - logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) - return err - } - if cnt == 0 { - // Finish the resuming process. - logutil.Logger(d.logCtx).Info("all paused tasks converted to pending state, update the task to running state") - err := d.updateTask(proto.TaskStateRunning, nil, RetrySQLTimes) - failpoint.Inject("syncAfterResume", func() { - TestSyncChan <- struct{}{} - }) - return err - } - - return d.taskMgr.ResumeSubtasks(d.Task.ID) -} - -// handle task in reverting state, check all revert subtasks finished. -func (d *BaseDispatcher) onReverting() error { - logutil.Logger(d.logCtx).Debug("on reverting state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStateRevertPending, proto.TaskStateReverting) - if err != nil { - logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) - return err - } - if cnt == 0 { - // Finish the rollback step. - logutil.Logger(d.logCtx).Info("all reverting tasks finished, update the task to reverted state") - return d.updateTask(proto.TaskStateReverted, nil, RetrySQLTimes) - } - // Wait all subtasks in this stage finished. - d.OnTick(d.ctx, d.Task) - logutil.Logger(d.logCtx).Debug("on reverting state, this task keeps current state", zap.String("state", d.Task.State)) - return nil -} - -// handle task in pending state, dispatch subtasks. -func (d *BaseDispatcher) onPending() error { - logutil.Logger(d.logCtx).Debug("on pending state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - return d.onNextStage() -} - -// handle task in running state, check all running subtasks finished. -// If subtasks finished, run into the next stage. -func (d *BaseDispatcher) onRunning() error { - logutil.Logger(d.logCtx).Debug("on running state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) - subTaskErrs, err := d.taskMgr.CollectSubTaskError(d.Task.ID) - if err != nil { - logutil.Logger(d.logCtx).Warn("collect subtask error failed", zap.Error(err)) - return err - } - if len(subTaskErrs) > 0 { - logutil.Logger(d.logCtx).Warn("subtasks encounter errors") - return d.onErrHandlingStage(subTaskErrs) - } - // check current stage finished. - cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStatePending, proto.TaskStateRunning) - if err != nil { - logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) - return err - } - - if cnt == 0 { - return d.onNextStage() - } - // Check if any node are down. - if err := d.replaceDeadNodesIfAny(); err != nil { - return err - } - // Wait all subtasks in this stage finished. - d.OnTick(d.ctx, d.Task) - logutil.Logger(d.logCtx).Debug("on running state, this task keeps current state", zap.String("state", d.Task.State)) - return nil -} - -func (d *BaseDispatcher) onFinished() error { - metrics.UpdateMetricsForFinishTask(d.Task) - logutil.Logger(d.logCtx).Debug("schedule task, task is finished", zap.String("state", d.Task.State)) - return d.taskMgr.TransferSubTasks2History(d.Task.ID) -} - -func (d *BaseDispatcher) replaceDeadNodesIfAny() error { - if len(d.taskNodes) == 0 { - var err error - d.taskNodes, err = d.taskMgr.GetSchedulerIDsByTaskIDAndStep(d.Task.ID, d.Task.Step) - if err != nil { - return err - } - } - d.liveNodeFetchTick++ - if d.liveNodeFetchTick == d.liveNodeFetchInterval { - d.liveNodeFetchTick = 0 - serverInfos, err := GenerateSchedulerNodes(d.ctx) - if err != nil { - return err - } - eligibleServerInfos, err := d.GetEligibleInstances(d.ctx, d.Task) - if err != nil { - return err - } - newInfos := serverInfos[:0] - for _, m := range serverInfos { - found := false - for _, n := range eligibleServerInfos { - if m.ID == n.ID { - found = true - break - } - } - if found { - newInfos = append(newInfos, m) - } - } - d.liveNodes = newInfos - } - if len(d.liveNodes) > 0 { - replaceNodes := make(map[string]string) - for _, nodeID := range d.taskNodes { - if ok := disttaskutil.MatchServerInfo(d.liveNodes, nodeID); !ok { - n := d.liveNodes[d.rand.Int()%len(d.liveNodes)] //nolint:gosec - replaceNodes[nodeID] = disttaskutil.GenerateExecID(n.IP, n.Port) - } - } - if err := d.taskMgr.UpdateFailedSchedulerIDs(d.Task.ID, replaceNodes); err != nil { - return err - } - // replace local cache. - for k, v := range replaceNodes { - for m, n := range d.taskNodes { - if n == k { - d.taskNodes[m] = v - break - } - } - } - } - return nil -} - -// updateTask update the task in tidb_global_task table. -func (d *BaseDispatcher) updateTask(taskState string, newSubTasks []*proto.Subtask, retryTimes int) (err error) { - prevState := d.Task.State - d.Task.State = taskState - if !VerifyTaskStateTransform(prevState, taskState) { - return errors.Errorf("invalid task state transform, from %s to %s", prevState, taskState) - } - - failpoint.Inject("cancelBeforeUpdate", func() { - err := d.taskMgr.CancelGlobalTask(d.Task.ID) - if err != nil { - logutil.Logger(d.logCtx).Error("cancel task failed", zap.Error(err)) - } - }) - - var retryable bool - for i := 0; i < retryTimes; i++ { - retryable, err = d.taskMgr.UpdateGlobalTaskAndAddSubTasks(d.Task, newSubTasks, prevState) - if err == nil || !retryable { - break - } - if i%10 == 0 { - logutil.Logger(d.logCtx).Warn("updateTask first failed", zap.String("from", prevState), zap.String("to", d.Task.State), - zap.Int("retry times", i), zap.Error(err)) - } - time.Sleep(RetrySQLInterval) - } - if err != nil && retryTimes != nonRetrySQLTime { - logutil.Logger(d.logCtx).Warn("updateTask failed", - zap.String("from", prevState), zap.String("to", d.Task.State), zap.Int("retry times", retryTimes), zap.Error(err)) - } - return err -} - -func (d *BaseDispatcher) onErrHandlingStage(receiveErr []error) error { - // 1. generate the needed task meta and subTask meta (dist-plan). - meta, err := d.OnErrStage(d.ctx, d, d.Task, receiveErr) - if err != nil { - // OnErrStage must be retryable, if not, there will have resource leak for tasks. - logutil.Logger(d.logCtx).Warn("handle error failed", zap.Error(err)) - return err - } - - // 2. dispatch revert dist-plan to EligibleInstances. - return d.dispatchSubTask4Revert(meta) -} - -func (d *BaseDispatcher) dispatchSubTask4Revert(meta []byte) error { - instanceIDs, err := d.GetAllSchedulerIDs(d.ctx, d.Task) - if err != nil { - logutil.Logger(d.logCtx).Warn("get task's all instances failed", zap.Error(err)) - return err - } - - subTasks := make([]*proto.Subtask, 0, len(instanceIDs)) - for _, id := range instanceIDs { - // reverting subtasks belong to the same step as current active step. - subTasks = append(subTasks, proto.NewSubtask(d.Task.Step, d.Task.ID, d.Task.Type, id, meta)) - } - return d.updateTask(proto.TaskStateReverting, subTasks, RetrySQLTimes) -} - -func (*BaseDispatcher) nextStepSubtaskDispatched(*proto.Task) bool { - // TODO: will implement it when we we support dispatch subtask by batch. - // since subtask meta might be too large to save in one transaction. - return true -} - -func (d *BaseDispatcher) onNextStage() (err error) { - /// dynamic dispatch subtasks. - failpoint.Inject("mockDynamicDispatchErr", func() { - failpoint.Return(errors.New("mockDynamicDispatchErr")) - }) - - nextStep := d.GetNextStep(d, d.Task) - logutil.Logger(d.logCtx).Info("onNextStage", - zap.Int64("current-step", d.Task.Step), - zap.Int64("next-step", nextStep)) - - // 1. Adjust the global task's concurrency. - if d.Task.State == proto.TaskStatePending { - if d.Task.Concurrency == 0 { - d.Task.Concurrency = DefaultSubtaskConcurrency - } - if d.Task.Concurrency > MaxSubtaskConcurrency { - d.Task.Concurrency = MaxSubtaskConcurrency - } - } - defer func() { - if err != nil { - return - } - // invariant: task.Step always means the most recent step that all - // corresponding subtasks have been saved to system table. - // - // when all subtasks of task.Step is finished, we call OnNextSubtasksBatch - // to generate subtasks of next step. after all subtasks of next step are - // saved to system table, we will update task.Step to next step, so the - // invariant hold. - // see nextStepSubtaskDispatched for why we don't update task and subtasks - // in a single transaction. - if d.nextStepSubtaskDispatched(d.Task) { - currStep := d.Task.Step - d.Task.Step = nextStep - // When all subtasks dispatched and processed, mark task as succeed. - taskState := proto.TaskStateRunning - if d.Task.Step == proto.StepDone { - taskState = proto.TaskStateSucceed - logutil.Logger(d.logCtx).Info("all subtasks dispatched and processed, finish the task") - } else { - logutil.Logger(d.logCtx).Info("move to next stage", - zap.Int64("from", currStep), zap.Int64("to", d.Task.Step)) - } - d.Task.StateUpdateTime = time.Now().UTC() - err = d.updateTask(taskState, nil, RetrySQLTimes) - } - }() - - for { - // 3. generate a batch of subtasks. - metas, err := d.OnNextSubtasksBatch(d.ctx, d, d.Task, nextStep) - if err != nil { - logutil.Logger(d.logCtx).Warn("generate part of subtasks failed", zap.Error(err)) - return d.handlePlanErr(err) - } - - failpoint.Inject("mockDynamicDispatchErr1", func() { - failpoint.Return(errors.New("mockDynamicDispatchErr1")) - }) - - // 4. dispatch batch of subtasks to EligibleInstances. - err = d.dispatchSubTask(nextStep, metas) - if err != nil { - return err - } - - if d.nextStepSubtaskDispatched(d.Task) { - break - } - - failpoint.Inject("mockDynamicDispatchErr2", func() { - failpoint.Return(errors.New("mockDynamicDispatchErr2")) - }) - } - return nil -} - -func (d *BaseDispatcher) dispatchSubTask(subtaskStep int64, metas [][]byte) error { - logutil.Logger(d.logCtx).Info("dispatch subtasks", zap.String("state", d.Task.State), zap.Int64("step", d.Task.Step), zap.Uint64("concurrency", d.Task.Concurrency), zap.Int("subtasks", len(metas))) - - // select all available TiDB nodes for task. - serverNodes, err := d.GetEligibleInstances(d.ctx, d.Task) - logutil.Logger(d.logCtx).Debug("eligible instances", zap.Int("num", len(serverNodes))) - - if err != nil { - return err - } - // 4. filter by role. - serverNodes, err = d.filterByRole(serverNodes) - if err != nil { - return err - } - - logutil.Logger(d.logCtx).Info("eligible instances", zap.Int("num", len(serverNodes))) - - if len(serverNodes) == 0 { - return errors.New("no available TiDB node to dispatch subtasks") - } - d.taskNodes = make([]string, len(serverNodes)) - for i := range serverNodes { - d.taskNodes[i] = disttaskutil.GenerateExecID(serverNodes[i].IP, serverNodes[i].Port) - } - subTasks := make([]*proto.Subtask, 0, len(metas)) - for i, meta := range metas { - // we assign the subtask to the instance in a round-robin way. - // TODO: assign the subtask to the instance according to the system load of each nodes - pos := i % len(serverNodes) - instanceID := disttaskutil.GenerateExecID(serverNodes[pos].IP, serverNodes[pos].Port) - logutil.Logger(d.logCtx).Debug("create subtasks", zap.String("instanceID", instanceID)) - subTasks = append(subTasks, proto.NewSubtask(subtaskStep, d.Task.ID, d.Task.Type, instanceID, meta)) - } - return d.updateTask(d.Task.State, subTasks, RetrySQLTimes) -} - -func (d *BaseDispatcher) handlePlanErr(err error) error { - logutil.Logger(d.logCtx).Warn("generate plan failed", zap.Error(err), zap.String("state", d.Task.State)) - if d.IsRetryableErr(err) { - return err - } - d.Task.Error = err - // state transform: pending -> failed. - return d.updateTask(proto.TaskStateFailed, nil, RetrySQLTimes) -} - -// GenerateSchedulerNodes generate a eligible TiDB nodes. -func GenerateSchedulerNodes(ctx context.Context) (serverNodes []*infosync.ServerInfo, err error) { - var serverInfos map[string]*infosync.ServerInfo - _, etcd := ctx.Value("etcd").(bool) - if intest.InTest && !etcd { - serverInfos = infosync.MockGlobalServerInfoManagerEntry.GetAllServerInfo() - } else { - serverInfos, err = infosync.GetAllServerInfo(ctx) - } - if err != nil { - return nil, err - } - if len(serverInfos) == 0 { - return nil, errors.New("not found instance") - } - - serverNodes = make([]*infosync.ServerInfo, 0, len(serverInfos)) - for _, serverInfo := range serverInfos { - serverNodes = append(serverNodes, serverInfo) - } - return serverNodes, nil -} - -func (d *BaseDispatcher) filterByRole(infos []*infosync.ServerInfo) ([]*infosync.ServerInfo, error) { - nodes, err := d.taskMgr.GetNodesByRole("background") - if err != nil { - return nil, err - } - - if len(nodes) == 0 { - nodes, err = d.taskMgr.GetNodesByRole("") - } - - if err != nil { - return nil, err - } - - res := make([]*infosync.ServerInfo, 0, len(nodes)) - for _, info := range infos { - _, ok := nodes[disttaskutil.GenerateExecID(info.IP, info.Port)] - if ok { - res = append(res, info) - } - } - return res, nil -} - -// GetAllSchedulerIDs gets all the scheduler IDs. -func (d *BaseDispatcher) GetAllSchedulerIDs(ctx context.Context, task *proto.Task) ([]string, error) { - serverInfos, err := d.GetEligibleInstances(ctx, task) - if err != nil { - return nil, err - } - if len(serverInfos) == 0 { - return nil, nil - } - - schedulerIDs, err := d.taskMgr.GetSchedulerIDsByTaskID(task.ID) - if err != nil { - return nil, err - } - ids := make([]string, 0, len(schedulerIDs)) - for _, id := range schedulerIDs { - if ok := disttaskutil.MatchServerInfo(serverInfos, id); ok { - ids = append(ids, id) - } - } - return ids, nil -} - -// GetPreviousSubtaskMetas get subtask metas from specific step. -func (d *BaseDispatcher) GetPreviousSubtaskMetas(taskID int64, step int64) ([][]byte, error) { - previousSubtasks, err := d.taskMgr.GetSucceedSubtasksByStep(taskID, step) - if err != nil { - logutil.Logger(d.logCtx).Warn("get previous succeed subtask failed", zap.Int64("step", step)) - return nil, err - } - previousSubtaskMetas := make([][]byte, 0, len(previousSubtasks)) - for _, subtask := range previousSubtasks { - previousSubtaskMetas = append(previousSubtaskMetas, subtask.Meta) - } - return previousSubtaskMetas, nil -} - -// GetPreviousSchedulerIDs gets scheduler IDs that run previous step. -func (d *BaseDispatcher) GetPreviousSchedulerIDs(_ context.Context, taskID int64, step int64) ([]string, error) { - return d.taskMgr.GetSchedulerIDsByTaskIDAndStep(taskID, step) -} - -// WithNewSession executes the function with a new session. -func (d *BaseDispatcher) WithNewSession(fn func(se sessionctx.Context) error) error { - return d.taskMgr.WithNewSession(fn) -} - -// WithNewTxn executes the fn in a new transaction. -func (d *BaseDispatcher) WithNewTxn(ctx context.Context, fn func(se sessionctx.Context) error) error { - return d.taskMgr.WithNewTxn(ctx, fn) -} - -// VerifyTaskStateTransform verifies whether the task state transform is valid. -func VerifyTaskStateTransform(from, to string) bool { - rules := map[string][]string{ - proto.TaskStatePending: { - proto.TaskStateRunning, - proto.TaskStateCancelling, - proto.TaskStatePausing, - proto.TaskStateSucceed, - proto.TaskStateFailed, - }, - proto.TaskStateRunning: { - proto.TaskStateSucceed, - proto.TaskStateReverting, - proto.TaskStateFailed, - proto.TaskStateCancelling, - proto.TaskStatePausing, - }, - proto.TaskStateSucceed: {}, - proto.TaskStateReverting: { - proto.TaskStateReverted, - // no revert_failed now - // proto.TaskStateRevertFailed, - }, - proto.TaskStateFailed: {}, - proto.TaskStateRevertFailed: {}, - proto.TaskStateCancelling: { - proto.TaskStateReverting, - // no canceled now - // proto.TaskStateCanceled, - }, - proto.TaskStateCanceled: {}, - proto.TaskStatePausing: { - proto.TaskStatePaused, - }, - proto.TaskStatePaused: { - proto.TaskStateResuming, - }, - proto.TaskStateResuming: { - proto.TaskStateRunning, - }, - proto.TaskStateRevertPending: {}, - proto.TaskStateReverted: {}, - } - logutil.BgLogger().Info("task state transform", zap.String("from", from), zap.String("to", to)) - - if from == to { - return true - } - - for _, state := range rules[from] { - if state == to { - return true - } - } - return false -} diff --git a/disttask/framework/dispatcher/dispatcher_test.go b/disttask/framework/dispatcher/dispatcher_test.go deleted file mode 100644 index b3f7ef9e62de7..0000000000000 --- a/disttask/framework/dispatcher/dispatcher_test.go +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package dispatcher_test - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/mock" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/util" - "go.uber.org/mock/gomock" -) - -var ( - _ dispatcher.Extension = (*testDispatcherExt)(nil) - _ dispatcher.Extension = (*numberExampleDispatcherExt)(nil) -) - -const ( - subtaskCnt = 3 -) - -type testDispatcherExt struct{} - -func (*testDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { -} - -func (*testDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ int64) (metas [][]byte, err error) { - return nil, nil -} - -func (*testDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { - return nil, nil -} - -var mockedAllServerInfos = []*infosync.ServerInfo{} - -func (*testDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { - return mockedAllServerInfos, nil -} - -func (*testDispatcherExt) IsRetryableErr(error) bool { - return true -} - -func (*testDispatcherExt) GetNextStep(dispatcher.TaskHandle, *proto.Task) int64 { - return proto.StepDone -} - -type numberExampleDispatcherExt struct{} - -func (*numberExampleDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { -} - -func (n *numberExampleDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, task *proto.Task, _ int64) (metas [][]byte, err error) { - switch task.Step { - case proto.StepInit: - for i := 0; i < subtaskCnt; i++ { - metas = append(metas, []byte{'1'}) - } - logutil.BgLogger().Info("progress step init") - case proto.StepOne: - logutil.BgLogger().Info("progress step one") - return nil, nil - default: - return nil, errors.New("unknown step") - } - return metas, nil -} - -func (n *numberExampleDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { - // Don't handle not. - return nil, nil -} - -func (*numberExampleDispatcherExt) GetEligibleInstances(ctx context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { - return dispatcher.GenerateSchedulerNodes(ctx) -} - -func (*numberExampleDispatcherExt) IsRetryableErr(error) bool { - return true -} - -func (*numberExampleDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { - switch task.Step { - case proto.StepInit: - return proto.StepOne - default: - return proto.StepDone - } -} - -func MockDispatcherManager(t *testing.T, pool *pools.ResourcePool) (*dispatcher.Manager, *storage.TaskManager) { - ctx := context.WithValue(context.Background(), "etcd", true) - mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) - storage.SetTaskManager(mgr) - dsp, err := dispatcher.NewManager(util.WithInternalSourceType(ctx, "dispatcher"), mgr, "host:port") - require.NoError(t, err) - dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, - func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { - mockDispatcher := dsp.MockDispatcher(task) - mockDispatcher.Extension = &testDispatcherExt{} - return mockDispatcher - }) - return dsp, mgr -} - -func deleteTasks(t *testing.T, store kv.Storage, taskID int64) { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf("delete from mysql.tidb_global_task where id = %d", taskID)) -} - -func TestGetInstance(t *testing.T) { - ctx := context.Background() - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - - dspManager, mgr := MockDispatcherManager(t, pool) - // test no server - task := &proto.Task{ID: 1, Type: proto.TaskTypeExample} - dsp := dspManager.MockDispatcher(task) - dsp.Extension = &testDispatcherExt{} - instanceIDs, err := dsp.GetAllSchedulerIDs(ctx, task) - require.Lenf(t, instanceIDs, 0, "GetAllSchedulerIDs when there's no subtask") - require.NoError(t, err) - - // test 2 servers - // server ids: uuid0, uuid1 - // subtask instance ids: nil - uuids := []string{"ddl_id_1", "ddl_id_2"} - serverIDs := []string{"10.123.124.10:32457", "[ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]:65535"} - - mockedAllServerInfos = []*infosync.ServerInfo{ - { - ID: uuids[0], - IP: "10.123.124.10", - Port: 32457, - }, - { - ID: uuids[1], - IP: "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789", - Port: 65535, - }, - } - instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) - require.Lenf(t, instanceIDs, 0, "GetAllSchedulerIDs") - require.NoError(t, err) - - // server ids: uuid0, uuid1 - // subtask instance ids: uuid1 - subtask := &proto.Subtask{ - Type: proto.TaskTypeExample, - TaskID: task.ID, - SchedulerID: serverIDs[1], - } - err = mgr.AddNewSubTask(task.ID, proto.StepInit, subtask.SchedulerID, nil, subtask.Type, true) - require.NoError(t, err) - instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) - require.NoError(t, err) - require.Equal(t, []string{serverIDs[1]}, instanceIDs) - // server ids: uuid0, uuid1 - // subtask instance ids: uuid0, uuid1 - subtask = &proto.Subtask{ - Type: proto.TaskTypeExample, - TaskID: task.ID, - SchedulerID: serverIDs[0], - } - err = mgr.AddNewSubTask(task.ID, proto.StepInit, subtask.SchedulerID, nil, subtask.Type, true) - require.NoError(t, err) - instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) - require.NoError(t, err) - require.Len(t, instanceIDs, len(serverIDs)) - require.ElementsMatch(t, instanceIDs, serverIDs) -} - -func TestTaskFailInManager(t *testing.T) { - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockDispatcher := mock.NewMockDispatcher(ctrl) - mockDispatcher.EXPECT().Init().Return(errors.New("mock dispatcher init error")) - - dspManager, mgr := MockDispatcherManager(t, pool) - dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, - func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { - return mockDispatcher - }) - dspManager.Start() - defer dspManager.Stop() - - // unknown task type - taskID, err := mgr.AddNewGlobalTask("test", "test-type", 1, nil) - require.NoError(t, err) - require.Eventually(t, func() bool { - task, err := mgr.GetGlobalTaskByID(taskID) - require.NoError(t, err) - return task.State == proto.TaskStateFailed && - strings.Contains(task.Error.Error(), "unknown task type") - }, time.Second*10, time.Millisecond*300) - - // dispatcher init error - taskID, err = mgr.AddNewGlobalTask("test2", proto.TaskTypeExample, 1, nil) - require.NoError(t, err) - require.Eventually(t, func() bool { - task, err := mgr.GetGlobalTaskByID(taskID) - require.NoError(t, err) - return task.State == proto.TaskStateFailed && - strings.Contains(task.Error.Error(), "mock dispatcher init error") - }, time.Second*10, time.Millisecond*300) -} - -func checkDispatch(t *testing.T, taskCnt int, isSucc, isCancel, isSubtaskCancel, isPauseAndResume bool) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/MockDisableDistTask", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/MockDisableDistTask")) - }() - // test DispatchTaskLoop - // test parallelism control - var originalConcurrency int - if taskCnt == 1 { - originalConcurrency = dispatcher.DefaultDispatchConcurrency - dispatcher.DefaultDispatchConcurrency = 1 - } - - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - - dsp, mgr := MockDispatcherManager(t, pool) - dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, - func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { - mockDispatcher := dsp.MockDispatcher(task) - mockDispatcher.Extension = &numberExampleDispatcherExt{} - return mockDispatcher - }) - dsp.Start() - defer func() { - dsp.Stop() - // make data race happy - if taskCnt == 1 { - dispatcher.DefaultDispatchConcurrency = originalConcurrency - } - }() - - require.NoError(t, mgr.StartManager(":4000", "background")) - - // 3s - cnt := 60 - checkGetRunningTaskCnt := func(expected int) { - require.Eventually(t, func() bool { - return dsp.GetRunningTaskCnt() == expected - }, time.Second, 50*time.Millisecond) - } - - checkTaskRunningCnt := func() []*proto.Task { - var tasks []*proto.Task - require.Eventually(t, func() bool { - var err error - tasks, err = mgr.GetGlobalTasksInStates(proto.TaskStateRunning) - require.NoError(t, err) - return len(tasks) == taskCnt - }, time.Second, 50*time.Millisecond) - return tasks - } - - checkSubtaskCnt := func(tasks []*proto.Task, taskIDs []int64) { - for i, taskID := range taskIDs { - require.Equal(t, int64(i+1), tasks[i].ID) - require.Eventually(t, func() bool { - cnt, err := mgr.GetSubtaskInStatesCnt(taskID, proto.TaskStatePending) - require.NoError(t, err) - return int64(subtaskCnt) == cnt - }, time.Second, 50*time.Millisecond) - } - } - - // Mock add tasks. - taskIDs := make([]int64, 0, taskCnt) - for i := 0; i < taskCnt; i++ { - taskID, err := mgr.AddNewGlobalTask(fmt.Sprintf("%d", i), proto.TaskTypeExample, 0, nil) - require.NoError(t, err) - taskIDs = append(taskIDs, taskID) - } - // test OnNextSubtasksBatch. - checkGetRunningTaskCnt(taskCnt) - tasks := checkTaskRunningCnt() - checkSubtaskCnt(tasks, taskIDs) - // test parallelism control - if taskCnt == 1 { - taskID, err := mgr.AddNewGlobalTask(fmt.Sprintf("%d", taskCnt), proto.TaskTypeExample, 0, nil) - require.NoError(t, err) - checkGetRunningTaskCnt(taskCnt) - // Clean the task. - deleteTasks(t, store, taskID) - dsp.DelRunningTask(taskID) - } - - // test DetectTaskLoop - checkGetTaskState := func(expectedState string) { - i := 0 - for ; i < cnt; i++ { - tasks, err := mgr.GetGlobalTasksInStates(expectedState) - require.NoError(t, err) - if len(tasks) == taskCnt { - break - } - historyTasks, err := mgr.GetGlobalTasksFromHistoryInStates(expectedState) - require.NoError(t, err) - if len(tasks)+len(historyTasks) == taskCnt { - break - } - time.Sleep(time.Millisecond * 50) - } - require.Less(t, i, cnt) - } - // Test all subtasks are successful. - var err error - if isSucc { - // Mock subtasks succeed. - for i := 1; i <= subtaskCnt*taskCnt; i++ { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) - require.NoError(t, err) - } - checkGetTaskState(proto.TaskStateSucceed) - require.Len(t, tasks, taskCnt) - - checkGetRunningTaskCnt(0) - return - } - - if isCancel { - for i := 1; i <= taskCnt; i++ { - err = mgr.CancelGlobalTask(int64(i)) - require.NoError(t, err) - } - } else if isPauseAndResume { - for i := 0; i < taskCnt; i++ { - found, err := mgr.PauseTask(fmt.Sprintf("%d", i)) - require.Equal(t, true, found) - require.NoError(t, err) - } - for i := 1; i <= subtaskCnt*taskCnt; i++ { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStatePaused, nil) - require.NoError(t, err) - } - checkGetTaskState(proto.TaskStatePaused) - for i := 0; i < taskCnt; i++ { - found, err := mgr.ResumeTask(fmt.Sprintf("%d", i)) - require.Equal(t, true, found) - require.NoError(t, err) - } - - // Mock subtasks succeed. - for i := 1; i <= subtaskCnt*taskCnt; i++ { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) - require.NoError(t, err) - } - checkGetTaskState(proto.TaskStateSucceed) - return - } else { - // Test each task has a subtask failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/storage/MockUpdateTaskErr", "1*return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/storage/MockUpdateTaskErr")) - }() - - if isSubtaskCancel { - // Mock a subtask canceled - for i := 1; i <= subtaskCnt*taskCnt; i += subtaskCnt { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateCanceled, nil) - require.NoError(t, err) - } - } else { - // Mock a subtask fails. - for i := 1; i <= subtaskCnt*taskCnt; i += subtaskCnt { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateFailed, nil) - require.NoError(t, err) - } - } - } - - checkGetTaskState(proto.TaskStateReverting) - require.Len(t, tasks, taskCnt) - // Mock all subtask reverted. - start := subtaskCnt * taskCnt - for i := start; i <= start+subtaskCnt*taskCnt; i++ { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateReverted, nil) - require.NoError(t, err) - } - checkGetTaskState(proto.TaskStateReverted) - require.Len(t, tasks, taskCnt) -} - -func TestSimple(t *testing.T) { - checkDispatch(t, 1, true, false, false, false) -} - -func TestSimpleErrStage(t *testing.T) { - checkDispatch(t, 1, false, false, false, false) -} - -func TestSimpleCancel(t *testing.T) { - checkDispatch(t, 1, false, true, false, false) -} - -func TestSimpleSubtaskCancel(t *testing.T) { - checkDispatch(t, 1, false, false, true, false) -} - -func TestParallel(t *testing.T) { - checkDispatch(t, 3, true, false, false, false) -} - -func TestParallelErrStage(t *testing.T) { - checkDispatch(t, 3, false, false, false, false) -} - -func TestParallelCancel(t *testing.T) { - checkDispatch(t, 3, false, true, false, false) -} - -func TestParallelSubtaskCancel(t *testing.T) { - checkDispatch(t, 3, false, false, true, false) -} - -func TestPause(t *testing.T) { - checkDispatch(t, 1, false, false, false, true) -} - -func TestParallelPause(t *testing.T) { - checkDispatch(t, 3, false, false, false, true) -} - -func TestVerifyTaskStateTransform(t *testing.T) { - testCases := []struct { - oldState string - newState string - expect bool - }{ - {proto.TaskStateRunning, proto.TaskStateRunning, true}, - {proto.TaskStatePending, proto.TaskStateRunning, true}, - {proto.TaskStatePending, proto.TaskStateReverting, false}, - {proto.TaskStateRunning, proto.TaskStateReverting, true}, - {proto.TaskStateReverting, proto.TaskStateReverted, true}, - {proto.TaskStateReverting, proto.TaskStateSucceed, false}, - {proto.TaskStateRunning, proto.TaskStatePausing, true}, - {proto.TaskStateRunning, proto.TaskStateResuming, false}, - {proto.TaskStateCancelling, proto.TaskStateRunning, false}, - {proto.TaskStateCanceled, proto.TaskStateRunning, false}, - } - for _, tc := range testCases { - require.Equal(t, tc.expect, dispatcher.VerifyTaskStateTransform(tc.oldState, tc.newState)) - } -} - -func TestCleanUpRoutine(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/MockDisableDistTask", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/MockDisableDistTask")) - }() - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - dsp, mgr := MockDispatcherManager(t, pool) - mockCleanupRountine := mock.NewMockCleanUpRoutine(ctrl) - mockCleanupRountine.EXPECT().CleanUp(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() - dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, - func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { - mockDispatcher := dsp.MockDispatcher(task) - mockDispatcher.Extension = &numberExampleDispatcherExt{} - return mockDispatcher - }) - dispatcher.RegisterDispatcherCleanUpFactory(proto.TaskTypeExample, - func() dispatcher.CleanUpRoutine { - return mockCleanupRountine - }) - dsp.Start() - defer dsp.Stop() - require.NoError(t, mgr.StartManager(":4000", "background")) - - taskID, err := mgr.AddNewGlobalTask("test", proto.TaskTypeExample, 1, nil) - require.NoError(t, err) - - checkTaskRunningCnt := func() []*proto.Task { - var tasks []*proto.Task - require.Eventually(t, func() bool { - var err error - tasks, err = mgr.GetGlobalTasksInStates(proto.TaskStateRunning) - require.NoError(t, err) - return len(tasks) == 1 - }, time.Second, 50*time.Millisecond) - return tasks - } - - checkSubtaskCnt := func(tasks []*proto.Task, taskID int64) { - require.Eventually(t, func() bool { - cnt, err := mgr.GetSubtaskInStatesCnt(taskID, proto.TaskStatePending) - require.NoError(t, err) - return int64(subtaskCnt) == cnt - }, time.Second, 50*time.Millisecond) - } - - tasks := checkTaskRunningCnt() - checkSubtaskCnt(tasks, taskID) - for i := 1; i <= subtaskCnt; i++ { - err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) - require.NoError(t, err) - } - dsp.DoCleanUpRoutine() - require.Eventually(t, func() bool { - tasks, err := mgr.GetGlobalTasksFromHistoryInStates(proto.TaskStateSucceed) - require.NoError(t, err) - return len(tasks) != 0 - }, time.Second*10, time.Millisecond*300) -} diff --git a/disttask/framework/dispatcher/interface.go b/disttask/framework/dispatcher/interface.go deleted file mode 100644 index 7a9b627ae257a..0000000000000 --- a/disttask/framework/dispatcher/interface.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package dispatcher - -import ( - "context" - - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/util/syncutil" -) - -// Extension is used to control the process operations for each task. -// it's used to extend functions of BaseDispatcher. -// as golang doesn't support inheritance, we embed this interface in Dispatcher -// to simulate abstract method as in other OO languages. -type Extension interface { - // OnTick is used to handle the ticker event, if business impl need to do some periodical work, you can - // do it here, but don't do too much work here, because the ticker interval is small, and it will block - // the event is generated every checkTaskRunningInterval, and only when the task NOT FINISHED and NO ERROR. - OnTick(ctx context.Context, task *proto.Task) - - // OnNextSubtasksBatch is used to generate batch of subtasks for next stage - // NOTE: don't change gTask.State inside, framework will manage it. - // it's called when: - // 1. task is pending and entering it's first step. - // 2. subtasks dispatched has all finished with no error. - // when next step is StepDone, it should return nil, nil. - OnNextSubtasksBatch(ctx context.Context, h TaskHandle, task *proto.Task, step int64) (subtaskMetas [][]byte, err error) - - // OnErrStage is called when: - // 1. subtask is finished with error. - // 2. task is cancelled after we have dispatched some subtasks. - OnErrStage(ctx context.Context, h TaskHandle, task *proto.Task, receiveErr []error) (subtaskMeta []byte, err error) - - // GetEligibleInstances is used to get the eligible instances for the task. - // on certain condition we may want to use some instances to do the task, such as instances with more disk. - GetEligibleInstances(ctx context.Context, task *proto.Task) ([]*infosync.ServerInfo, error) - - // IsRetryableErr is used to check whether the error occurred in dispatcher is retryable. - IsRetryableErr(err error) bool - - // GetNextStep is used to get the next step for the task. - // if task runs successfully, it should go from StepInit to business steps, - // then to StepDone, then dispatcher will mark it as finished. - GetNextStep(h TaskHandle, task *proto.Task) int64 -} - -// dispatcherFactoryFn is used to create a dispatcher. -type dispatcherFactoryFn func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) Dispatcher - -var dispatcherFactoryMap = struct { - syncutil.RWMutex - m map[string]dispatcherFactoryFn -}{ - m: make(map[string]dispatcherFactoryFn), -} - -// RegisterDispatcherFactory is used to register the dispatcher factory. -// normally dispatcher ctor should be registered before the server start. -// and should be called in a single routine, such as in init(). -// after the server start, there's should be no write to the map. -// but for index backfill, the register call stack is so deep, not sure -// if it's safe to do so, so we use a lock here. -func RegisterDispatcherFactory(taskType string, ctor dispatcherFactoryFn) { - dispatcherFactoryMap.Lock() - defer dispatcherFactoryMap.Unlock() - dispatcherFactoryMap.m[taskType] = ctor -} - -// getDispatcherFactory is used to get the dispatcher factory. -func getDispatcherFactory(taskType string) dispatcherFactoryFn { - dispatcherFactoryMap.RLock() - defer dispatcherFactoryMap.RUnlock() - return dispatcherFactoryMap.m[taskType] -} - -// ClearDispatcherFactory is only used in test. -func ClearDispatcherFactory() { - dispatcherFactoryMap.Lock() - defer dispatcherFactoryMap.Unlock() - dispatcherFactoryMap.m = make(map[string]dispatcherFactoryFn) -} - -// CleanUpRoutine is used for the framework to do some clean up work if the task is finished. -type CleanUpRoutine interface { - // CleanUp do the clean up work. - CleanUp(ctx context.Context, task *proto.Task) error -} -type cleanUpFactoryFn func() CleanUpRoutine - -var cleanUpFactoryMap = struct { - syncutil.RWMutex - m map[string]cleanUpFactoryFn -}{ - m: make(map[string]cleanUpFactoryFn), -} - -// RegisterDispatcherCleanUpFactory is used to register the dispatcher clean up factory. -// normally dispatcher cleanup is used in the dispatcher_manager gcTaskLoop to do clean up -// works when tasks are finished. -func RegisterDispatcherCleanUpFactory(taskType string, ctor cleanUpFactoryFn) { - cleanUpFactoryMap.Lock() - defer cleanUpFactoryMap.Unlock() - cleanUpFactoryMap.m[taskType] = ctor -} - -// getDispatcherCleanUpFactory is used to get the dispatcher factory. -func getDispatcherCleanUpFactory(taskType string) cleanUpFactoryFn { - cleanUpFactoryMap.RLock() - defer cleanUpFactoryMap.RUnlock() - return cleanUpFactoryMap.m[taskType] -} - -// ClearDispatcherCleanUpFactory is only used in test. -func ClearDispatcherCleanUpFactory() { - cleanUpFactoryMap.Lock() - defer cleanUpFactoryMap.Unlock() - cleanUpFactoryMap.m = make(map[string]cleanUpFactoryFn) -} diff --git a/disttask/framework/dispatcher/main_test.go b/disttask/framework/dispatcher/main_test.go deleted file mode 100644 index 4811add10ca95..0000000000000 --- a/disttask/framework/dispatcher/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package dispatcher - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -// DispatcherForTest exports for testing. -type DispatcherManagerForTest interface { - GetRunningTaskCnt() int - DelRunningTask(globalTaskID int64) - DoCleanUpRoutine() -} - -// GetRunningGTaskCnt implements Dispatcher.GetRunningGTaskCnt interface. -func (dm *Manager) GetRunningTaskCnt() int { - return dm.getRunningTaskCnt() -} - -// DelRunningGTask implements Dispatcher.DelRunningGTask interface. -func (dm *Manager) DelRunningTask(globalTaskID int64) { - dm.delRunningTask(globalTaskID) -} - -// DoCleanUpRoutine implements Dispatcher.DoCleanUpRoutine interface. -func (dm *Manager) DoCleanUpRoutine() { - dm.doCleanUpRoutine() -} - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - // Make test more fast. - checkTaskRunningInterval = checkTaskRunningInterval / 10 - checkTaskFinishedInterval = checkTaskFinishedInterval / 10 - RetrySQLInterval = RetrySQLInterval / 20 - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("syscall.syscall"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/disttask/framework/framework_dynamic_dispatch_test.go b/disttask/framework/framework_dynamic_dispatch_test.go deleted file mode 100644 index 42960c1e0985e..0000000000000 --- a/disttask/framework/framework_dynamic_dispatch_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package framework_test - -import ( - "context" - "fmt" - "sync" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" -) - -type testDynamicDispatcherExt struct { - cnt int -} - -var _ dispatcher.Extension = (*testDynamicDispatcherExt)(nil) - -func (*testDynamicDispatcherExt) OnTick(_ context.Context, _ *proto.Task) {} - -func (dsp *testDynamicDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, gTask *proto.Task, _ int64) (metas [][]byte, err error) { - // step1 - if gTask.Step == proto.StepInit { - dsp.cnt++ - return [][]byte{ - []byte(fmt.Sprintf("task%d", dsp.cnt)), - []byte(fmt.Sprintf("task%d", dsp.cnt)), - }, nil - } - - // step2 - if gTask.Step == proto.StepOne { - dsp.cnt++ - return [][]byte{ - []byte(fmt.Sprintf("task%d", dsp.cnt)), - }, nil - } - return nil, nil -} - -func (*testDynamicDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { - return nil, nil -} - -func (dsp *testDynamicDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { - switch task.Step { - case proto.StepInit: - return proto.StepOne - case proto.StepOne: - return proto.StepTwo - default: - return proto.StepDone - } -} - -func (*testDynamicDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { - return generateSchedulerNodes4Test() -} - -func (*testDynamicDispatcherExt) IsRetryableErr(error) bool { - return true -} - -func TestFrameworkDynamicBasic(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &testDynamicDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 3) - DispatchTaskAndCheckSuccess("key1", t, &m) - distContext.Close() -} - -func TestFrameworkDynamicHA(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &testDynamicDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 3) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr", "5*return()")) - DispatchTaskAndCheckSuccess("key1", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr1", "5*return()")) - DispatchTaskAndCheckSuccess("key2", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr2", "5*return()")) - DispatchTaskAndCheckSuccess("key3", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDynamicDispatchErr2")) - distContext.Close() -} diff --git a/disttask/framework/framework_ha_test.go b/disttask/framework/framework_ha_test.go deleted file mode 100644 index 29487f13d01c5..0000000000000 --- a/disttask/framework/framework_ha_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package framework_test - -import ( - "context" - "sync" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" -) - -type haTestDispatcherExt struct { - cnt int -} - -var _ dispatcher.Extension = (*haTestDispatcherExt)(nil) - -func (*haTestDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { -} - -func (dsp *haTestDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, gTask *proto.Task, _ int64) (metas [][]byte, err error) { - if gTask.Step == proto.StepInit { - dsp.cnt = 10 - return [][]byte{ - []byte("task1"), - []byte("task2"), - []byte("task3"), - []byte("task4"), - []byte("task5"), - []byte("task6"), - []byte("task7"), - []byte("task8"), - []byte("task9"), - []byte("task10"), - }, nil - } - if gTask.Step == proto.StepOne { - dsp.cnt = 15 - return [][]byte{ - []byte("task11"), - []byte("task12"), - []byte("task13"), - []byte("task14"), - []byte("task15"), - }, nil - } - return nil, nil -} - -func (*haTestDispatcherExt) OnErrStage(ctx context.Context, h dispatcher.TaskHandle, gTask *proto.Task, receiveErr []error) (subtaskMeta []byte, err error) { - return nil, nil -} - -func (*haTestDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { - return generateSchedulerNodes4Test() -} - -func (*haTestDispatcherExt) IsRetryableErr(error) bool { - return true -} - -func (dsp *haTestDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { - switch task.Step { - case proto.StepInit: - return proto.StepOne - case proto.StepOne: - return proto.StepTwo - default: - return proto.StepDone - } -} - -func TestHABasic(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 4) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "4*return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - distContext.Close() -} - -func TestHAManyNodes(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 30) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "30*return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - distContext.Close() -} - -func TestHAFailInDifferentStage(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 6) - // stage1 : server num from 6 to 3. - // stage2 : server num from 3 to 2. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "6*return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown2", "return()")) - - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown2")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - distContext.Close() -} - -func TestHAFailInDifferentStageManyNodes(t *testing.T) { - var m sync.Map - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 30) - // stage1 : server num from 30 to 27. - // stage2 : server num from 27 to 26. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "30*return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown2", "return()")) - - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown2")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - distContext.Close() -} - -func TestHAReplacedButRunning(t *testing.T) { - var m sync.Map - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 4) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBPartitionThenResume", "10*return(true)")) - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBPartitionThenResume")) - distContext.Close() -} - -func TestHAReplacedButRunningManyNodes(t *testing.T) { - var m sync.Map - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 30) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBPartitionThenResume", "30*return(true)")) - DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBPartitionThenResume")) - distContext.Close() -} diff --git a/disttask/framework/framework_pause_and_resume_test.go b/disttask/framework/framework_pause_and_resume_test.go deleted file mode 100644 index 525942e6fcfb7..0000000000000 --- a/disttask/framework/framework_pause_and_resume_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package framework_test - -import ( - "sync" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" -) - -func CheckSubtasksState(t *testing.T, taskID int64, state string, expectedCnt int64) { - mgr, err := storage.GetTaskManager() - require.NoError(t, err) - mgr.PrintSubtaskInfo(taskID) - cnt, err := mgr.GetSubtaskInStatesCnt(taskID, state) - require.NoError(t, err) - historySubTasksCnt, err := storage.GetSubtasksFromHistoryByTaskIDForTest(mgr, taskID) - require.NoError(t, err) - require.Equal(t, expectedCnt, cnt+int64(historySubTasksCnt)) -} - -func TestFrameworkPauseAndResume(t *testing.T) { - var m sync.Map - ctrl := gomock.NewController(t) - defer ctrl.Finish() - RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) - distContext := testkit.NewDistExecutionContext(t, 3) - // 1. dispatch and pause one running task. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/pauseTaskAfterRefreshTask", "2*return(true)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/syncAfterResume", "return()")) - DispatchTaskAndCheckState("key1", t, &m, proto.TaskStatePaused) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/pauseTaskAfterRefreshTask")) - // 4 subtask dispatched. - require.NoError(t, handle.ResumeTask("key1")) - <-dispatcher.TestSyncChan - WaitTaskExit(t, "key1") - CheckSubtasksState(t, 1, proto.TaskStateSucceed, 4) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/syncAfterResume")) - - mgr, err := storage.GetTaskManager() - require.NoError(t, err) - errs, err := mgr.CollectSubTaskError(1) - require.NoError(t, err) - require.Empty(t, errs) - - // 2. pause pending task. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/pausePendingTask", "2*return(true)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/syncAfterResume", "1*return()")) - DispatchTaskAndCheckState("key2", t, &m, proto.TaskStatePaused) - - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/pausePendingTask")) - // 4 subtask dispatched. - require.NoError(t, handle.ResumeTask("key2")) - <-dispatcher.TestSyncChan - WaitTaskExit(t, "key2") - CheckSubtasksState(t, 1, proto.TaskStateSucceed, 4) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/syncAfterResume")) - - errs, err = mgr.CollectSubTaskError(1) - require.NoError(t, err) - require.Empty(t, errs) - distContext.Close() -} diff --git a/disttask/framework/handle/BUILD.bazel b/disttask/framework/handle/BUILD.bazel deleted file mode 100644 index 5d0928ab3582a..0000000000000 --- a/disttask/framework/handle/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "handle", - srcs = ["handle.go"], - importpath = "github.com/pingcap/tidb/disttask/framework/handle", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/proto", - "//disttask/framework/storage", - "//metrics", - "//util/backoff", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "handle_test", - timeout = "short", - srcs = ["handle_test.go"], - flaky = True, - deps = [ - ":handle", - "//disttask/framework/proto", - "//disttask/framework/storage", - "//testkit", - "//util/backoff", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - ], -) diff --git a/disttask/framework/handle/handle.go b/disttask/framework/handle/handle.go deleted file mode 100644 index a9c9ce96f1ec5..0000000000000 --- a/disttask/framework/handle/handle.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "context" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/backoff" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -var ( - checkTaskFinishInterval = 300 * time.Millisecond -) - -// SubmitGlobalTask submits a global task. -func SubmitGlobalTask(taskKey, taskType string, concurrency int, taskMeta []byte) (*proto.Task, error) { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return nil, err - } - globalTask, err := globalTaskManager.GetGlobalTaskByKey(taskKey) - if err != nil { - return nil, err - } - - if globalTask == nil { - taskID, err := globalTaskManager.AddNewGlobalTask(taskKey, taskType, concurrency, taskMeta) - if err != nil { - return nil, err - } - - globalTask, err = globalTaskManager.GetGlobalTaskByID(taskID) - if err != nil { - return nil, err - } - - if globalTask == nil { - return nil, errors.Errorf("cannot find global task with ID %d", taskID) - } - metrics.UpdateMetricsForAddTask(globalTask) - } - return globalTask, nil -} - -// WaitGlobalTask waits for a global task to finish. -func WaitGlobalTask(ctx context.Context, globalTask *proto.Task) error { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - ticker := time.NewTicker(checkTaskFinishInterval) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return ctx.Err() - case <-ticker.C: - found, err := globalTaskManager.GetTaskByIDWithHistory(globalTask.ID) - if err != nil { - logutil.Logger(ctx).Error("cannot get global task during waiting", - zap.Int64("task-id", globalTask.ID), - zap.Error(err)) - continue - } - if found == nil { - return errors.Errorf("cannot find global task with ID %d", globalTask.ID) - } - - switch found.State { - case proto.TaskStateSucceed: - return nil - case proto.TaskStateReverted: - logutil.BgLogger().Error("global task reverted", zap.Int64("task-id", globalTask.ID), zap.Error(found.Error)) - return found.Error - case proto.TaskStatePaused: - logutil.BgLogger().Error("global task paused", zap.Int64("task-id", globalTask.ID)) - return nil - case proto.TaskStateFailed, proto.TaskStateCanceled: - return errors.Errorf("task stopped with state %s, err %v", found.State, found.Error) - } - } - } -} - -// SubmitAndRunGlobalTask submits a global task and wait for it to finish. -func SubmitAndRunGlobalTask(ctx context.Context, taskKey, taskType string, concurrency int, taskMeta []byte) error { - globalTask, err := SubmitGlobalTask(taskKey, taskType, concurrency, taskMeta) - if err != nil { - return err - } - return WaitGlobalTask(ctx, globalTask) -} - -// CancelGlobalTask cancels a global task. -func CancelGlobalTask(taskKey string) error { - taskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - task, err := taskManager.GetGlobalTaskByKey(taskKey) - if err != nil { - return err - } - if task == nil { - logutil.BgLogger().Info("task not exist", zap.String("taskKey", taskKey)) - - return nil - } - return taskManager.CancelGlobalTask(task.ID) -} - -// PauseTask pauses a task. -func PauseTask(taskKey string) error { - taskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - found, err := taskManager.PauseTask(taskKey) - if !found { - logutil.BgLogger().Info("task not pausable", zap.String("taskKey", taskKey)) - return nil - } - return err -} - -// ResumeTask resumes a task. -func ResumeTask(taskKey string) error { - taskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - found, err := taskManager.ResumeTask(taskKey) - if !found { - logutil.BgLogger().Info("task not resumable", zap.String("taskKey", taskKey)) - return nil - } - return err -} - -// RunWithRetry runs a function with retry, when retry exceed max retry time, it -// returns the last error met. -// if the function fails with err, it should return a bool to indicate whether -// the error is retryable. -// if context done, it will stop early and return ctx.Err(). -func RunWithRetry( - ctx context.Context, - maxRetry int, - backoffer backoff.Backoffer, - logger *zap.Logger, - f func(context.Context) (bool, error), -) error { - var lastErr error - for i := 0; i < maxRetry; i++ { - retryable, err := f(ctx) - if err == nil || !retryable { - return err - } - lastErr = err - logger.Warn("met retryable error", zap.Int("retry-count", i), - zap.Int("max-retry", maxRetry), zap.Error(err)) - select { - case <-ctx.Done(): - return ctx.Err() - case <-time.After(backoffer.Backoff(i)): - } - } - return lastErr -} diff --git a/disttask/framework/handle/handle_test.go b/disttask/framework/handle/handle_test.go deleted file mode 100644 index 0aefeefe27dad..0000000000000 --- a/disttask/framework/handle/handle_test.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package handle_test - -import ( - "context" - "math" - "sync/atomic" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/backoff" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/util" -) - -func TestHandle(t *testing.T) { - ctx := context.Background() - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) - storage.SetTaskManager(mgr) - - // no dispatcher registered - err := handle.SubmitAndRunGlobalTask(ctx, "1", proto.TaskTypeExample, 2, []byte("byte")) - require.Error(t, err) - - task, err := mgr.GetGlobalTaskByID(1) - require.NoError(t, err) - require.Equal(t, int64(1), task.ID) - require.Equal(t, "1", task.Key) - require.Equal(t, proto.TaskTypeExample, task.Type) - // no dispatcher registered - require.Equal(t, proto.TaskStateFailed, task.State) - require.Equal(t, proto.StepInit, task.Step) - require.Equal(t, uint64(2), task.Concurrency) - require.Equal(t, []byte("byte"), task.Meta) - - require.NoError(t, handle.CancelGlobalTask("1")) - - task, err = handle.SubmitGlobalTask("2", proto.TaskTypeExample, 2, []byte("byte")) - require.NoError(t, err) - require.NoError(t, handle.PauseTask("2")) - require.NoError(t, handle.ResumeTask("2")) -} - -func TestRunWithRetry(t *testing.T) { - ctx := context.Background() - - // retry count exceed - backoffer := backoff.NewExponential(100*time.Millisecond, 1, time.Second) - err := handle.RunWithRetry(ctx, 3, backoffer, log.L(), - func(ctx context.Context) (bool, error) { - return true, errors.New("mock error") - }, - ) - require.ErrorContains(t, err, "mock error") - - // non-retryable error - var end atomic.Bool - go func() { - defer end.Store(true) - backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) - err = handle.RunWithRetry(ctx, math.MaxInt, backoffer, log.L(), - func(ctx context.Context) (bool, error) { - return false, errors.New("mock error") - }, - ) - require.Error(t, err) - }() - require.Eventually(t, func() bool { - return end.Load() - }, 5*time.Second, 100*time.Millisecond) - - // fail with retryable error once, then success - end.Store(false) - go func() { - defer end.Store(true) - backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) - var i int - err = handle.RunWithRetry(ctx, math.MaxInt, backoffer, log.L(), - func(ctx context.Context) (bool, error) { - if i == 0 { - i++ - return true, errors.New("mock error") - } - return false, nil - }, - ) - require.NoError(t, err) - }() - require.Eventually(t, func() bool { - return end.Load() - }, 5*time.Second, 100*time.Millisecond) - - // context done - subctx, cancel := context.WithCancel(ctx) - cancel() - backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) - err = handle.RunWithRetry(subctx, math.MaxInt, backoffer, log.L(), - func(ctx context.Context) (bool, error) { - return true, errors.New("mock error") - }, - ) - require.ErrorIs(t, err, context.Canceled) -} diff --git a/disttask/framework/mock/BUILD.bazel b/disttask/framework/mock/BUILD.bazel deleted file mode 100644 index 1e02ac8e55499..0000000000000 --- a/disttask/framework/mock/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mock", - srcs = [ - "dispatcher_mock.go", - "plan_mock.go", - "scheduler_mock.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/mock", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/planner", - "//disttask/framework/proto", - "//disttask/framework/scheduler/execute", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/mock/execute/BUILD.bazel b/disttask/framework/mock/execute/BUILD.bazel deleted file mode 100644 index a326fcabcc61e..0000000000000 --- a/disttask/framework/mock/execute/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "execute", - srcs = ["execute_mock.go"], - importpath = "github.com/pingcap/tidb/disttask/framework/mock/execute", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/proto", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/planner/BUILD.bazel b/disttask/framework/planner/BUILD.bazel deleted file mode 100644 index b878b73ba2f72..0000000000000 --- a/disttask/framework/planner/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "planner", - srcs = [ - "plan.go", - "planner.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/planner", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/storage", - "//sessionctx", - ], -) - -go_test( - name = "planner_test", - timeout = "short", - srcs = [ - "plan_test.go", - "planner_test.go", - ], - flaky = True, - deps = [ - ":planner", - "//disttask/framework/mock", - "//disttask/framework/storage", - "//testkit", - "@com_github_ngaut_pools//:pools", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/planner/plan.go b/disttask/framework/planner/plan.go deleted file mode 100644 index 08b8e1695d98e..0000000000000 --- a/disttask/framework/planner/plan.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package planner - -import ( - "context" - - "github.com/pingcap/tidb/sessionctx" -) - -// PlanCtx is the context for planning. -type PlanCtx struct { - Ctx context.Context - - // integrate with current distribute framework - SessionCtx sessionctx.Context - TaskID int64 - TaskKey string - TaskType string - ThreadCnt int - - // PreviousSubtaskMetas is subtask metas of previous steps. - // We can remove this field if we find a better way to pass the result between steps. - PreviousSubtaskMetas map[int64][][]byte - GlobalSort bool - NextTaskStep int64 -} - -// LogicalPlan represents a logical plan in distribute framework. -// A normal flow of distribute framework is: logical plan -> physical plan -> pipelines. -// To integrate with current distribute framework, the flow becomes: -// logical plan -> task meta -> physical plan -> subtaskmetas -> pipelines. -type LogicalPlan interface { - ToTaskMeta() ([]byte, error) - FromTaskMeta([]byte) error - ToPhysicalPlan(PlanCtx) (*PhysicalPlan, error) -} - -// PhysicalPlan is a DAG of processors in distribute framework. -// Each processor is a node process the task with a pipeline, -// and receive/pass the result to other processors via input and output links. -type PhysicalPlan struct { - Processors []ProcessorSpec -} - -// AddProcessor adds a node to the DAG. -func (p *PhysicalPlan) AddProcessor(processor ProcessorSpec) { - p.Processors = append(p.Processors, processor) -} - -// ToSubtaskMetas converts the physical plan to a list of subtask metas. -func (p *PhysicalPlan) ToSubtaskMetas(ctx PlanCtx, step int64) ([][]byte, error) { - subtaskMetas := make([][]byte, 0, len(p.Processors)) - for _, processor := range p.Processors { - if processor.Step != step { - continue - } - subtaskMeta, err := processor.Pipeline.ToSubtaskMeta(ctx) - if err != nil { - return nil, err - } - subtaskMetas = append(subtaskMetas, subtaskMeta) - } - return subtaskMetas, nil -} - -// ProcessorSpec is the specification of a processor. -// A processor is a node in the DAG. -// It contains input links from other processors, as well as output links to other processors. -// It also contains an pipeline which is the actual logic of the processor. -type ProcessorSpec struct { - ID int - Input InputSpec - Pipeline PipelineSpec - Output OutputSpec - // We can remove this field if we find a better way to pass the result between steps. - Step int64 -} - -// InputSpec is the specification of an input. -type InputSpec struct { - ColumnTypes []byte - Links []LinkSpec -} - -// OutputSpec is the specification of an output. -type OutputSpec struct { - Links []LinkSpec -} - -// LinkSpec is the specification of a link. -// Link connects pipelines between different nodes. -type LinkSpec struct { - ProcessorID int - // Support endpoint for communication between processors. - // Endpoint string -} - -// PipelineSpec is the specification of an pipeline. -type PipelineSpec interface { - // ToSubtaskMeta converts the pipeline to a subtask meta - ToSubtaskMeta(PlanCtx) ([]byte, error) -} diff --git a/disttask/framework/planner/plan_test.go b/disttask/framework/planner/plan_test.go deleted file mode 100644 index 565a6a6d0ec7e..0000000000000 --- a/disttask/framework/planner/plan_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package planner_test - -import ( - "testing" - - "github.com/pingcap/tidb/disttask/framework/mock" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" -) - -func TestPhysicalPlan(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockPipelineSpec := mock.NewMockPipelineSpec(ctrl) - - plan := &planner.PhysicalPlan{} - planCtx := planner.PlanCtx{} - plan.AddProcessor(planner.ProcessorSpec{Pipeline: mockPipelineSpec, Step: 1}) - mockPipelineSpec.EXPECT().ToSubtaskMeta(gomock.Any()).Return([]byte("mock"), nil) - subtaskMetas, err := plan.ToSubtaskMetas(planCtx, 1) - require.NoError(t, err) - require.Equal(t, [][]byte{[]byte("mock")}, subtaskMetas) -} diff --git a/disttask/framework/planner/planner.go b/disttask/framework/planner/planner.go deleted file mode 100644 index 03fd9f5448a23..0000000000000 --- a/disttask/framework/planner/planner.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package planner - -import "github.com/pingcap/tidb/disttask/framework/storage" - -// Planner represents a distribute plan planner. -type Planner struct{} - -// NewPlanner creates a new planer instance. -func NewPlanner() *Planner { - return &Planner{} -} - -// Run runs the distribute plan. -func (*Planner) Run(planCtx PlanCtx, plan LogicalPlan) (int64, error) { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return 0, err - } - - taskMeta, err := plan.ToTaskMeta() - if err != nil { - return 0, err - } - - return globalTaskManager.AddGlobalTaskWithSession( - planCtx.SessionCtx, - planCtx.TaskKey, - planCtx.TaskType, - planCtx.ThreadCnt, - taskMeta, - ) -} diff --git a/disttask/framework/planner/planner_test.go b/disttask/framework/planner/planner_test.go deleted file mode 100644 index ff95d506fd6b4..0000000000000 --- a/disttask/framework/planner/planner_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package planner_test - -import ( - "context" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/tidb/disttask/framework/mock" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/util" - "go.uber.org/mock/gomock" -) - -func TestPlanner(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - ctx := context.Background() - store := testkit.CreateMockStore(t) - gtk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return gtk.Session(), nil - }, 1, 1, time.Second) - defer pool.Close() - mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) - storage.SetTaskManager(mgr) - - p := &planner.Planner{} - pCtx := planner.PlanCtx{ - Ctx: ctx, - SessionCtx: gtk.Session(), - TaskKey: "1", - TaskType: "example", - ThreadCnt: 1, - } - mockLogicalPlan := mock.NewMockLogicalPlan(ctrl) - mockLogicalPlan.EXPECT().ToTaskMeta().Return([]byte("mock"), nil) - taskID, err := p.Run(pCtx, mockLogicalPlan) - require.NoError(t, err) - require.Equal(t, int64(1), taskID) -} diff --git a/disttask/framework/proto/BUILD.bazel b/disttask/framework/proto/BUILD.bazel deleted file mode 100644 index c79a7260ad2c3..0000000000000 --- a/disttask/framework/proto/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "proto", - srcs = ["task.go"], - importpath = "github.com/pingcap/tidb/disttask/framework/proto", - visibility = ["//visibility:public"], -) - -go_test( - name = "proto_test", - timeout = "short", - srcs = ["task_test.go"], - embed = [":proto"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/disttask/framework/scheduler/BUILD.bazel b/disttask/framework/scheduler/BUILD.bazel deleted file mode 100644 index c92d7043ae39e..0000000000000 --- a/disttask/framework/scheduler/BUILD.bazel +++ /dev/null @@ -1,55 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "scheduler", - srcs = [ - "interface.go", - "manager.go", - "register.go", - "scheduler.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/scheduler", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/common", - "//config", - "//disttask/framework/dispatcher", - "//disttask/framework/handle", - "//disttask/framework/proto", - "//disttask/framework/scheduler/execute", - "//disttask/framework/storage", - "//domain/infosync", - "//metrics", - "//resourcemanager/pool/spool", - "//resourcemanager/util", - "//util/backoff", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "scheduler_test", - timeout = "short", - srcs = [ - "manager_test.go", - "register_test.go", - "scheduler_test.go", - ], - embed = [":scheduler"], - flaky = True, - race = "on", - shard_count = 8, - deps = [ - "//disttask/framework/mock", - "//disttask/framework/mock/execute", - "//disttask/framework/proto", - "//resourcemanager/pool/spool", - "//resourcemanager/util", - "@com_github_pkg_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/framework/scheduler/execute/BUILD.bazel b/disttask/framework/scheduler/execute/BUILD.bazel deleted file mode 100644 index 3ac7d5c2e1e7e..0000000000000 --- a/disttask/framework/scheduler/execute/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "execute", - srcs = [ - "interface.go", - "summary.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/scheduler/execute", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/proto", - "//disttask/framework/storage", - "//util/logutil", - "@org_uber_go_zap//:zap", - ], -) diff --git a/disttask/framework/scheduler/execute/interface.go b/disttask/framework/scheduler/execute/interface.go deleted file mode 100644 index 68ff01221365c..0000000000000 --- a/disttask/framework/scheduler/execute/interface.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package execute - -import ( - "context" - - "github.com/pingcap/tidb/disttask/framework/proto" -) - -// SubtaskExecutor defines the executor of a subtask. -type SubtaskExecutor interface { - // Init is used to initialize the environment for the subtask executor. - Init(context.Context) error - // RunSubtask is used to run the subtask. - RunSubtask(ctx context.Context, subtask *proto.Subtask) error - // Cleanup is used to clean up the environment for the subtask executor. - Cleanup(context.Context) error - // OnFinished is used to handle the subtask when it is finished. - // The subtask meta can be updated in place. - OnFinished(ctx context.Context, subtask *proto.Subtask) error - // Rollback is used to roll back all subtasks. - // TODO: right now all impl of Rollback is empty, maybe we can remove it. - Rollback(context.Context) error -} diff --git a/disttask/framework/scheduler/execute/summary.go b/disttask/framework/scheduler/execute/summary.go deleted file mode 100644 index fe50fc02f38b6..0000000000000 --- a/disttask/framework/scheduler/execute/summary.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package execute - -import ( - "context" - "sync" - "time" - - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// Summary is used to collect the summary of subtasks execution. -type Summary struct { - mu struct { - sync.Mutex - RowCount map[int64]int64 // subtask ID -> row count - } -} - -// NewSummary creates a new Summary. -func NewSummary() *Summary { - return &Summary{ - mu: struct { - sync.Mutex - RowCount map[int64]int64 - }{ - RowCount: map[int64]int64{}, - }, - } -} - -// UpdateRowCount updates the row count of the subtask. -func (s *Summary) UpdateRowCount(subtaskID int64, rowCount int64) { - s.mu.Lock() - defer s.mu.Unlock() - s.mu.RowCount[subtaskID] = rowCount -} - -// UpdateRowCountLoop updates the row count of the subtask periodically. -func (s *Summary) UpdateRowCountLoop(ctx context.Context, taskMgr *storage.TaskManager) { - ticker := time.NewTicker(3 * time.Second) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - return - case <-ticker.C: - s.PersistRowCount(ctx, taskMgr) - } - } -} - -// PersistRowCount persists the row count of the subtask to the storage. -func (s *Summary) PersistRowCount(ctx context.Context, taskMgr *storage.TaskManager) { - var copiedRowCount map[int64]int64 - s.mu.Lock() - if len(s.mu.RowCount) == 0 { - s.mu.Unlock() - return - } - copiedRowCount = make(map[int64]int64, len(s.mu.RowCount)) - for subtaskID, rowCount := range s.mu.RowCount { - copiedRowCount[subtaskID] = rowCount - } - s.mu.Unlock() - - for subtaskID, rowCount := range copiedRowCount { - err := taskMgr.UpdateSubtaskRowCount(subtaskID, rowCount) - if err != nil { - logutil.Logger(ctx).Warn("update subtask row count failed", zap.Error(err)) - } - } - s.mu.Lock() - for subtaskID := range copiedRowCount { - delete(s.mu.RowCount, subtaskID) - } - s.mu.Unlock() -} diff --git a/disttask/framework/scheduler/interface.go b/disttask/framework/scheduler/interface.go deleted file mode 100644 index 8e8bbcfded298..0000000000000 --- a/disttask/framework/scheduler/interface.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package scheduler - -import ( - "context" - - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" -) - -// TaskTable defines the interface to access task table. -type TaskTable interface { - GetGlobalTasksInStates(states ...interface{}) (task []*proto.Task, err error) - GetGlobalTaskByID(taskID int64) (task *proto.Task, err error) - - GetSubtasksInStates(tidbID string, taskID int64, step int64, states ...interface{}) ([]*proto.Subtask, error) - GetFirstSubtaskInStates(instanceID string, taskID int64, step int64, states ...interface{}) (*proto.Subtask, error) - StartManager(tidbID string, role string) error - StartSubtask(subtaskID int64) error - UpdateSubtaskStateAndError(subtaskID int64, state string, err error) error - FinishSubtask(subtaskID int64, meta []byte) error - - HasSubtasksInStates(tidbID string, taskID int64, step int64, states ...interface{}) (bool, error) - UpdateErrorToSubtask(tidbID string, taskID int64, err error) error - IsSchedulerCanceled(tidbID string, taskID int64) (bool, error) - PauseSubtasks(tidbID string, taskID int64) error -} - -// Pool defines the interface of a pool. -type Pool interface { - Run(func()) error - RunWithConcurrency(chan func(), uint32) error - ReleaseAndWait() -} - -// Scheduler is the subtask scheduler for a task. -// Each task type should implement this interface. -type Scheduler interface { - Init(context.Context) error - Run(context.Context, *proto.Task) error - Rollback(context.Context, *proto.Task) error - Pause(context.Context, *proto.Task) error - Close() -} - -// Extension extends the scheduler. -// each task type should implement this interface. -type Extension interface { - // IsIdempotent returns whether the subtask is idempotent. - // when tidb restart, the subtask might be left in the running state. - // if it's idempotent, the scheduler can rerun the subtask, else - // the scheduler will mark the subtask as failed. - IsIdempotent(subtask *proto.Subtask) bool - // GetSubtaskExecutor returns the subtask executor for the subtask. - // Note: summary is the summary manager of all subtask of the same type now. - GetSubtaskExecutor(ctx context.Context, task *proto.Task, summary *execute.Summary) (execute.SubtaskExecutor, error) -} - -// EmptySubtaskExecutor is an empty scheduler. -// it can be used for the task that does not need to split into subtasks. -type EmptySubtaskExecutor struct { -} - -var _ execute.SubtaskExecutor = &EmptySubtaskExecutor{} - -// Init implements the SubtaskExecutor interface. -func (*EmptySubtaskExecutor) Init(context.Context) error { - return nil -} - -// RunSubtask implements the SubtaskExecutor interface. -func (*EmptySubtaskExecutor) RunSubtask(context.Context, *proto.Subtask) error { - return nil -} - -// Cleanup implements the SubtaskExecutor interface. -func (*EmptySubtaskExecutor) Cleanup(context.Context) error { - return nil -} - -// OnFinished implements the SubtaskExecutor interface. -func (*EmptySubtaskExecutor) OnFinished(_ context.Context, _ *proto.Subtask) error { - return nil -} - -// Rollback implements the SubtaskExecutor interface. -func (*EmptySubtaskExecutor) Rollback(context.Context) error { - return nil -} diff --git a/disttask/framework/scheduler/manager.go b/disttask/framework/scheduler/manager.go deleted file mode 100644 index ff7682c99790c..0000000000000 --- a/disttask/framework/scheduler/manager.go +++ /dev/null @@ -1,394 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package scheduler - -import ( - "context" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/resourcemanager/pool/spool" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -var ( - schedulerPoolSize int32 = 4 - // same as dispatcher - checkTime = 300 * time.Millisecond - retrySQLTimes = 3 - retrySQLInterval = 500 * time.Millisecond -) - -// ManagerBuilder is used to build a Manager. -type ManagerBuilder struct { - newPool func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) -} - -// NewManagerBuilder creates a new ManagerBuilder. -func NewManagerBuilder() *ManagerBuilder { - return &ManagerBuilder{ - newPool: func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { - return spool.NewPool(name, size, component, options...) - }, - } -} - -// setPoolFactory sets the poolFactory to mock the Pool in unit test. -func (b *ManagerBuilder) setPoolFactory(poolFactory func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error)) { - b.newPool = poolFactory -} - -// Manager monitors the global task table and manages the schedulers. -type Manager struct { - taskTable TaskTable - schedulerPool Pool - mu struct { - sync.RWMutex - // taskID -> CancelCauseFunc. - // CancelCauseFunc is used to fast cancel the scheduler.Run. - handlingTasks map[int64]context.CancelCauseFunc - } - // id, it's the same as server id now, i.e. host:port. - id string - wg sync.WaitGroup - ctx context.Context - cancel context.CancelFunc - logCtx context.Context - newPool func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) -} - -// BuildManager builds a Manager. -func (b *ManagerBuilder) BuildManager(ctx context.Context, id string, taskTable TaskTable) (*Manager, error) { - m := &Manager{ - id: id, - taskTable: taskTable, - logCtx: logutil.WithFields(context.Background()), - newPool: b.newPool, - } - m.ctx, m.cancel = context.WithCancel(ctx) - m.mu.handlingTasks = make(map[int64]context.CancelCauseFunc) - - schedulerPool, err := m.newPool("scheduler_pool", schedulerPoolSize, util.DistTask) - if err != nil { - return nil, err - } - m.schedulerPool = schedulerPool - - return m, nil -} - -// Start starts the Manager. -func (m *Manager) Start() error { - logutil.Logger(m.logCtx).Debug("manager start") - var err error - for i := 0; i < retrySQLTimes; i++ { - err = m.taskTable.StartManager(m.id, config.GetGlobalConfig().Instance.TiDBServiceScope) - if err == nil { - break - } - if i%10 == 0 { - logutil.Logger(m.logCtx).Warn("start manager failed", zap.String("scope", config.GetGlobalConfig().Instance.TiDBServiceScope), - zap.Int("retry times", retrySQLTimes), zap.Error(err)) - } - time.Sleep(retrySQLInterval) - } - if err != nil { - return err - } - - m.wg.Add(1) - go func() { - defer m.wg.Done() - m.fetchAndHandleRunnableTasks(m.ctx) - }() - - m.wg.Add(1) - go func() { - defer m.wg.Done() - m.fetchAndFastCancelTasks(m.ctx) - }() - return nil -} - -// Stop stops the Manager. -func (m *Manager) Stop() { - m.cancel() - m.schedulerPool.ReleaseAndWait() - m.wg.Wait() -} - -// fetchAndHandleRunnableTasks fetches the runnable tasks from the global task table and handles them. -func (m *Manager) fetchAndHandleRunnableTasks(ctx context.Context) { - ticker := time.NewTicker(checkTime) - for { - select { - case <-ctx.Done(): - logutil.Logger(m.logCtx).Info("fetchAndHandleRunnableTasks done") - return - case <-ticker.C: - tasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStateRunning, proto.TaskStateReverting) - if err != nil { - m.logErr(err) - continue - } - m.onRunnableTasks(ctx, tasks) - } - } -} - -// fetchAndFastCancelTasks fetches the reverting/pausing tasks from the global task table and fast cancels them. -func (m *Manager) fetchAndFastCancelTasks(ctx context.Context) { - ticker := time.NewTicker(checkTime) - for { - select { - case <-ctx.Done(): - m.cancelAllRunningTasks() - logutil.Logger(m.logCtx).Info("fetchAndFastCancelTasks done") - return - case <-ticker.C: - tasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStateReverting) - if err != nil { - m.logErr(err) - continue - } - m.onCanceledTasks(ctx, tasks) - - // cancel pending/running subtasks, and mark them as paused. - pausingTasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStatePausing) - if err != nil { - m.logErr(err) - continue - } - if err := m.onPausingTasks(pausingTasks); err != nil { - m.logErr(err) - continue - } - } - } -} - -// onRunnableTasks handles runnable tasks. -func (m *Manager) onRunnableTasks(ctx context.Context, tasks []*proto.Task) { - tasks = m.filterAlreadyHandlingTasks(tasks) - for _, task := range tasks { - exist, err := m.taskTable.HasSubtasksInStates(m.id, task.ID, task.Step, - proto.TaskStatePending, proto.TaskStateRevertPending, - // for the case that the tidb is restarted when the subtask is running. - proto.TaskStateRunning, proto.TaskStateReverting) - if err != nil { - logutil.Logger(m.logCtx).Error("check subtask exist failed", zap.Error(err)) - m.logErr(err) - continue - } - if !exist { - continue - } - logutil.Logger(m.logCtx).Info("detect new subtask", zap.Int64("task-id", task.ID)) - m.addHandlingTask(task.ID) - t := task - err = m.schedulerPool.Run(func() { - m.onRunnableTask(ctx, t) - m.removeHandlingTask(t.ID) - }) - // pool closed. - if err != nil { - m.removeHandlingTask(task.ID) - m.logErr(err) - return - } - } -} - -// onCanceledTasks cancels the running subtasks. -func (m *Manager) onCanceledTasks(_ context.Context, tasks []*proto.Task) { - m.mu.RLock() - defer m.mu.RUnlock() - for _, task := range tasks { - logutil.Logger(m.logCtx).Info("onCanceledTasks", zap.Int64("task-id", task.ID)) - if cancel, ok := m.mu.handlingTasks[task.ID]; ok && cancel != nil { - // subtask needs to change its state to canceled. - cancel(ErrCancelSubtask) - } - } -} - -// onPausingTasks pauses/cancels the pending/running subtasks. -func (m *Manager) onPausingTasks(tasks []*proto.Task) error { - m.mu.RLock() - defer m.mu.RUnlock() - for _, task := range tasks { - logutil.Logger(m.logCtx).Info("onPausingTasks", zap.Any("task_id", task.ID)) - if cancel, ok := m.mu.handlingTasks[task.ID]; ok && cancel != nil { - // Pause all running subtasks, don't mark subtasks as canceled. - // Should not change the subtask's state. - cancel(nil) - } - if err := m.taskTable.PauseSubtasks(m.id, task.ID); err != nil { - return err - } - } - return nil -} - -// cancelAllRunningTasks cancels all running tasks. -func (m *Manager) cancelAllRunningTasks() { - m.mu.RLock() - defer m.mu.RUnlock() - for id, cancel := range m.mu.handlingTasks { - logutil.Logger(m.logCtx).Info("cancelAllRunningTasks", zap.Int64("task-id", id)) - if cancel != nil { - // tidb shutdown, don't mark subtask as canceled. - // Should not change the subtask's state. - cancel(nil) - } - } -} - -// filterAlreadyHandlingTasks filters the tasks that are already handled. -func (m *Manager) filterAlreadyHandlingTasks(tasks []*proto.Task) []*proto.Task { - m.mu.RLock() - defer m.mu.RUnlock() - - var i int - for _, task := range tasks { - if _, ok := m.mu.handlingTasks[task.ID]; !ok { - tasks[i] = task - i++ - } - } - return tasks[:i] -} - -// TestContext only used in tests. -type TestContext struct { - TestSyncSubtaskRun chan struct{} - mockDown atomic.Bool -} - -var testContexts sync.Map - -// onRunnableTask handles a runnable task. -func (m *Manager) onRunnableTask(ctx context.Context, task *proto.Task) { - logutil.Logger(m.logCtx).Info("onRunnableTask", zap.Int64("task-id", task.ID), zap.String("type", task.Type)) - // runCtx only used in scheduler.Run, cancel in m.fetchAndFastCancelTasks. - factory := getSchedulerFactory(task.Type) - if factory == nil { - err := errors.Errorf("task type %s not found", task.Type) - m.logErrAndPersist(err, task.ID) - return - } - scheduler := factory(ctx, m.id, task, m.taskTable) - err := scheduler.Init(ctx) - if err != nil { - m.logErrAndPersist(err, task.ID) - return - } - defer scheduler.Close() - for { - select { - case <-ctx.Done(): - return - case <-time.After(checkTime): - } - failpoint.Inject("mockStopManager", func() { - testContexts.Store(m.id, &TestContext{make(chan struct{}), atomic.Bool{}}) - go func() { - v, ok := testContexts.Load(m.id) - if ok { - <-v.(*TestContext).TestSyncSubtaskRun - _ = infosync.MockGlobalServerInfoManagerEntry.DeleteByID(m.id) - m.Stop() - } - }() - }) - task, err := m.taskTable.GetGlobalTaskByID(task.ID) - if err != nil { - m.logErr(err) - return - } - if task == nil { - return - } - if task.State != proto.TaskStateRunning && task.State != proto.TaskStateReverting { - logutil.Logger(m.logCtx).Info("onRunnableTask exit", - zap.Int64("task-id", task.ID), zap.Int64("step", task.Step), zap.String("state", task.State)) - return - } - if exist, err := m.taskTable.HasSubtasksInStates(m.id, task.ID, task.Step, - proto.TaskStatePending, proto.TaskStateRevertPending, - // for the case that the tidb is restarted when the subtask is running. - proto.TaskStateRunning, proto.TaskStateReverting); err != nil { - m.logErr(err) - return - } else if !exist { - continue - } - switch task.State { - case proto.TaskStateRunning: - runCtx, runCancel := context.WithCancelCause(ctx) - m.registerCancelFunc(task.ID, runCancel) - err = scheduler.Run(runCtx, task) - runCancel(nil) - case proto.TaskStatePausing: - err = scheduler.Pause(ctx, task) - case proto.TaskStateReverting: - err = scheduler.Rollback(ctx, task) - } - if err != nil { - logutil.Logger(m.logCtx).Error("failed to handle task", zap.Error(err)) - } - } -} - -// addHandlingTask adds a task to the handling task set. -func (m *Manager) addHandlingTask(id int64) { - m.mu.Lock() - defer m.mu.Unlock() - m.mu.handlingTasks[id] = nil -} - -// registerCancelFunc registers a cancel function for a task. -func (m *Manager) registerCancelFunc(id int64, cancel context.CancelCauseFunc) { - m.mu.Lock() - defer m.mu.Unlock() - m.mu.handlingTasks[id] = cancel -} - -// removeHandlingTask removes a task from the handling task set. -func (m *Manager) removeHandlingTask(id int64) { - m.mu.Lock() - defer m.mu.Unlock() - delete(m.mu.handlingTasks, id) -} - -func (m *Manager) logErr(err error) { - logutil.Logger(m.logCtx).Error("task manager error", zap.Error(err), zap.Stack("stack")) -} - -func (m *Manager) logErrAndPersist(err error, taskID int64) { - m.logErr(err) - err1 := m.taskTable.UpdateErrorToSubtask(m.id, taskID, err) - if err1 != nil { - logutil.Logger(m.logCtx).Error("update to subtask failed", zap.Error(err1), zap.Stack("stack")) - } -} diff --git a/disttask/framework/scheduler/manager_test.go b/disttask/framework/scheduler/manager_test.go deleted file mode 100644 index 323b1a01aded4..0000000000000 --- a/disttask/framework/scheduler/manager_test.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package scheduler - -import ( - "context" - "errors" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/disttask/framework/mock" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/resourcemanager/pool/spool" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" -) - -var unfinishedSubtaskStates = []interface{}{ - proto.TaskStatePending, proto.TaskStateRevertPending, - proto.TaskStateRunning, proto.TaskStateReverting, -} - -func getPoolRunFn() (*sync.WaitGroup, func(f func()) error) { - wg := &sync.WaitGroup{} - return wg, func(f func()) error { - wg.Add(1) - go func() { - defer wg.Done() - f() - }() - return nil - } -} - -func TestManageTask(t *testing.T) { - b := NewManagerBuilder() - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockTaskTable := mock.NewMockTaskTable(ctrl) - m, err := b.BuildManager(context.Background(), "test", mockTaskTable) - require.NoError(t, err) - tasks := []*proto.Task{{ID: 1}, {ID: 2}} - newTasks := m.filterAlreadyHandlingTasks(tasks) - require.Equal(t, tasks, newTasks) - - m.addHandlingTask(1) - tasks = []*proto.Task{{ID: 1}, {ID: 2}} - newTasks = m.filterAlreadyHandlingTasks(tasks) - require.Equal(t, []*proto.Task{{ID: 2}}, newTasks) - - m.addHandlingTask(2) - tasks = []*proto.Task{{ID: 1}, {ID: 2}} - newTasks = m.filterAlreadyHandlingTasks(tasks) - require.Equal(t, []*proto.Task{}, newTasks) - - m.removeHandlingTask(1) - tasks = []*proto.Task{{ID: 1}, {ID: 2}} - newTasks = m.filterAlreadyHandlingTasks(tasks) - require.Equal(t, []*proto.Task{{ID: 1}}, newTasks) - - ctx1, cancel1 := context.WithCancelCause(context.Background()) - m.registerCancelFunc(2, cancel1) - m.cancelAllRunningTasks() - require.Equal(t, context.Canceled, ctx1.Err()) - - // test cancel. - m.addHandlingTask(1) - ctx2, cancel2 := context.WithCancelCause(context.Background()) - m.registerCancelFunc(1, cancel2) - ctx3, cancel3 := context.WithCancelCause(context.Background()) - m.registerCancelFunc(2, cancel3) - m.onCanceledTasks(context.Background(), []*proto.Task{{ID: 1}}) - require.Equal(t, context.Canceled, ctx2.Err()) - require.NoError(t, ctx3.Err()) - - // test pause. - m.addHandlingTask(3) - ctx4, cancel4 := context.WithCancelCause(context.Background()) - m.registerCancelFunc(1, cancel4) - mockTaskTable.EXPECT().PauseSubtasks("test", int64(1)).Return(nil) - m.onPausingTasks([]*proto.Task{{ID: 1}}) - require.Equal(t, context.Canceled, ctx4.Err()) -} - -func TestOnRunnableTasks(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockTaskTable := mock.NewMockTaskTable(ctrl) - mockInternalScheduler := mock.NewMockScheduler(ctrl) - mockPool := mock.NewMockPool(ctrl) - - b := NewManagerBuilder() - b.setPoolFactory(func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { - return mockPool, nil - }) - id := "test" - taskID := int64(1) - task := &proto.Task{ID: taskID, State: proto.TaskStateRunning, Step: proto.StepOne, Type: "type"} - - m, err := b.BuildManager(context.Background(), id, mockTaskTable) - require.NoError(t, err) - - // no task - m.onRunnableTasks(context.Background(), nil) - - RegisterTaskType("type", - func(ctx context.Context, id string, task *proto.Task, taskTable TaskTable) Scheduler { - return mockInternalScheduler - }) - - // get subtask failed - mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, - unfinishedSubtaskStates...). - Return(false, errors.New("get subtask failed")) - mockInternalScheduler.EXPECT().Close() - m.onRunnableTasks(context.Background(), []*proto.Task{task}) - - // no subtask - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, - unfinishedSubtaskStates...).Return(false, nil) - m.onRunnableTasks(context.Background(), []*proto.Task{task}) - - // pool error - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, - unfinishedSubtaskStates...).Return(true, nil) - mockPool.EXPECT().Run(gomock.Any()).Return(errors.New("pool error")) - m.onRunnableTasks(context.Background(), []*proto.Task{task}) - - // StepOne succeed - wg, runFn := getPoolRunFn() - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, - unfinishedSubtaskStates...).Return(true, nil) - mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task, nil) - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, - unfinishedSubtaskStates...).Return(true, nil) - mockInternalScheduler.EXPECT().Run(gomock.Any(), task).Return(nil) - - // StepTwo failed - task1 := &proto.Task{ID: taskID, State: proto.TaskStateRunning, Step: proto.StepTwo} - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task1, nil) - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepTwo, - unfinishedSubtaskStates...).Return(true, nil) - mockInternalScheduler.EXPECT().Run(gomock.Any(), task1).Return(errors.New("run err")) - - task2 := &proto.Task{ID: taskID, State: proto.TaskStateReverting, Step: proto.StepTwo} - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task2, nil) - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepTwo, - unfinishedSubtaskStates...).Return(true, nil) - mockInternalScheduler.EXPECT().Rollback(gomock.Any(), task2).Return(nil) - - task3 := &proto.Task{ID: taskID, State: proto.TaskStateReverted, Step: proto.StepTwo} - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task3, nil) - - m.onRunnableTasks(context.Background(), []*proto.Task{task}) - - wg.Wait() -} - -func TestManager(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockTaskTable := mock.NewMockTaskTable(ctrl) - mockInternalScheduler := mock.NewMockScheduler(ctrl) - mockPool := mock.NewMockPool(ctrl) - b := NewManagerBuilder() - b.setPoolFactory(func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { - return mockPool, nil - }) - RegisterTaskType("type", - func(ctx context.Context, id string, task *proto.Task, taskTable TaskTable) Scheduler { - return mockInternalScheduler - }) - id := "test" - taskID1 := int64(1) - taskID2 := int64(2) - taskID3 := int64(3) - task1 := &proto.Task{ID: taskID1, State: proto.TaskStateRunning, Step: proto.StepOne, Type: "type"} - task2 := &proto.Task{ID: taskID2, State: proto.TaskStateReverting, Step: proto.StepOne, Type: "type"} - task3 := &proto.Task{ID: taskID3, State: proto.TaskStatePausing, Step: proto.StepOne, Type: "type"} - - mockTaskTable.EXPECT().StartManager("test", "").Return(nil).Times(1) - mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStateRunning, proto.TaskStateReverting). - Return([]*proto.Task{task1, task2}, nil).AnyTimes() - mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStateReverting). - Return([]*proto.Task{task2}, nil).AnyTimes() - mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStatePausing). - Return([]*proto.Task{task3}, nil).AnyTimes() - mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) - // task1 - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, - unfinishedSubtaskStates...). - Return(true, nil) - wg, runFn := getPoolRunFn() - mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID1).Return(task1, nil).AnyTimes() - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, - unfinishedSubtaskStates...). - Return(true, nil) - mockInternalScheduler.EXPECT().Run(gomock.Any(), task1).Return(nil) - - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, - unfinishedSubtaskStates...). - Return(false, nil).AnyTimes() - mockInternalScheduler.EXPECT().Close() - // task2 - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, - unfinishedSubtaskStates...). - Return(true, nil) - mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) - mockTaskTable.EXPECT().GetGlobalTaskByID(taskID2).Return(task2, nil).AnyTimes() - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, - unfinishedSubtaskStates...). - Return(true, nil) - mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) - mockInternalScheduler.EXPECT().Rollback(gomock.Any(), task2).Return(nil) - mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, - unfinishedSubtaskStates...). - Return(false, nil).AnyTimes() - mockInternalScheduler.EXPECT().Close() - // task3 - mockTaskTable.EXPECT().PauseSubtasks(id, taskID3).Return(nil).AnyTimes() - - // for scheduler pool - mockPool.EXPECT().ReleaseAndWait().Do(func() { - wg.Wait() - }) - m, err := b.BuildManager(context.Background(), id, mockTaskTable) - require.NoError(t, err) - require.NoError(t, m.Start()) - time.Sleep(5 * time.Second) - m.Stop() -} diff --git a/disttask/framework/scheduler/scheduler.go b/disttask/framework/scheduler/scheduler.go deleted file mode 100644 index d6f2074119a53..0000000000000 --- a/disttask/framework/scheduler/scheduler.go +++ /dev/null @@ -1,609 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package scheduler - -import ( - "context" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/backoff" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -const ( - // DefaultCheckSubtaskCanceledInterval is the default check interval for cancel cancelled subtasks. - DefaultCheckSubtaskCanceledInterval = 2 * time.Second -) - -var ( - // ErrCancelSubtask is the cancel cause when cancelling subtasks. - ErrCancelSubtask = errors.New("cancel subtasks") - // ErrFinishSubtask is the cancel cause when scheduler successfully processed subtasks. - ErrFinishSubtask = errors.New("finish subtasks") - // ErrFinishRollback is the cancel cause when scheduler rollback successfully. - ErrFinishRollback = errors.New("finish rollback") - - // TestSyncChan is used to sync the test. - TestSyncChan = make(chan struct{}) -) - -// BaseScheduler is the base implementation of Scheduler. -type BaseScheduler struct { - // id, it's the same as server id now, i.e. host:port. - id string - taskID int64 - taskTable TaskTable - logCtx context.Context - Extension - - mu struct { - sync.RWMutex - err error - // handled indicates whether the error has been updated to one of the subtask. - handled bool - // runtimeCancel is used to cancel the Run/Rollback when error occurs. - runtimeCancel context.CancelCauseFunc - } -} - -// NewBaseScheduler creates a new BaseScheduler. -func NewBaseScheduler(_ context.Context, id string, taskID int64, taskTable TaskTable) *BaseScheduler { - schedulerImpl := &BaseScheduler{ - id: id, - taskID: taskID, - taskTable: taskTable, - logCtx: logutil.WithFields(context.Background(), zap.Int64("task-id", taskID)), - } - return schedulerImpl -} - -func (s *BaseScheduler) startCancelCheck(ctx context.Context, wg *sync.WaitGroup, cancelFn context.CancelCauseFunc) { - wg.Add(1) - go func() { - defer wg.Done() - ticker := time.NewTicker(DefaultCheckSubtaskCanceledInterval) - defer ticker.Stop() - for { - select { - case <-ctx.Done(): - logutil.Logger(s.logCtx).Info("scheduler exits", zap.Error(ctx.Err())) - return - case <-ticker.C: - canceled, err := s.taskTable.IsSchedulerCanceled(s.id, s.taskID) - if err != nil { - continue - } - if canceled { - logutil.Logger(s.logCtx).Info("scheduler canceled") - if cancelFn != nil { - // subtask transferred to other tidb, don't mark subtask as canceled. - // Should not change the subtask's state. - cancelFn(nil) - } - } - } - } - }() -} - -// Init implements the Scheduler interface. -func (*BaseScheduler) Init(_ context.Context) error { - return nil -} - -// Run runs the scheduler task. -func (s *BaseScheduler) Run(ctx context.Context, task *proto.Task) (err error) { - defer func() { - if r := recover(); r != nil { - logutil.Logger(ctx).Error("BaseScheduler panicked", zap.Any("recover", r), zap.Stack("stack")) - err4Panic := errors.Errorf("%v", r) - err1 := s.updateErrorToSubtask(ctx, task.ID, err4Panic) - if err == nil { - err = err1 - } - } - }() - err = s.run(ctx, task) - if s.mu.handled { - return err - } - if err == nil { - return nil - } - return s.updateErrorToSubtask(ctx, task.ID, err) -} - -func (s *BaseScheduler) run(ctx context.Context, task *proto.Task) error { - if ctx.Err() != nil { - s.onError(ctx.Err()) - return s.getError() - } - runCtx, runCancel := context.WithCancelCause(ctx) - defer runCancel(ErrFinishSubtask) - s.registerCancelFunc(runCancel) - s.resetError() - logutil.Logger(s.logCtx).Info("scheduler run a step", zap.Any("step", task.Step), zap.Any("concurrency", task.Concurrency)) - - summary, cleanup, err := runSummaryCollectLoop(ctx, task, s.taskTable) - if err != nil { - s.onError(err) - return s.getError() - } - defer cleanup() - - executor, err := s.GetSubtaskExecutor(ctx, task, summary) - if err != nil { - s.onError(err) - return s.getError() - } - - failpoint.Inject("mockExecSubtaskInitEnvErr", func() { - failpoint.Return(errors.New("mockExecSubtaskInitEnvErr")) - }) - if err := executor.Init(runCtx); err != nil { - s.onError(err) - return s.getError() - } - - var wg sync.WaitGroup - cancelCtx, checkCancel := context.WithCancel(ctx) - s.startCancelCheck(cancelCtx, &wg, runCancel) - - defer func() { - err := executor.Cleanup(runCtx) - if err != nil { - logutil.Logger(s.logCtx).Error("cleanup subtask exec env failed", zap.Error(err)) - } - checkCancel() - wg.Wait() - }() - - subtasks, err := s.taskTable.GetSubtasksInStates(s.id, task.ID, task.Step, - proto.TaskStatePending, proto.TaskStateRunning) - if err != nil { - s.onError(err) - if common.IsRetryableError(err) { - logutil.Logger(s.logCtx).Warn("met retryable error", zap.Error(err)) - return nil - } - return s.getError() - } - for _, subtask := range subtasks { - metrics.IncDistTaskSubTaskCnt(subtask) - metrics.StartDistTaskSubTask(subtask) - } - - for { - // check if any error occurs. - if err := s.getError(); err != nil { - break - } - - subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, - proto.TaskStatePending, proto.TaskStateRunning) - if err != nil { - logutil.Logger(s.logCtx).Warn("GetFirstSubtaskInStates meets error", zap.Error(err)) - continue - } - if subtask == nil { - newTask, err := s.taskTable.GetGlobalTaskByID(task.ID) - if err != nil { - logutil.Logger(s.logCtx).Warn("GetGlobalTaskByID meets error", zap.Error(err)) - continue - } - // When the task move to next step or task state changes, the scheduler should exit. - if newTask.Step != task.Step || newTask.State != task.State { - break - } - continue - } - - if subtask.State == proto.TaskStateRunning { - if !s.IsIdempotent(subtask) { - logutil.Logger(s.logCtx).Info("subtask in running state and is not idempotent, fail it", - zap.Int64("subtask-id", subtask.ID)) - subtaskErr := errors.New("subtask in running state and is not idempotent") - s.onError(subtaskErr) - s.updateSubtaskStateAndError(subtask, proto.TaskStateFailed, subtaskErr) - s.markErrorHandled() - break - } - } else { - // subtask.State == proto.TaskStatePending - s.startSubtaskAndUpdateState(ctx, subtask) - if err := s.getError(); err != nil { - logutil.Logger(s.logCtx).Warn("startSubtaskAndUpdateState meets error", zap.Error(err)) - continue - } - } - - failpoint.Inject("mockCleanScheduler", func() { - v, ok := testContexts.Load(s.id) - if ok { - if v.(*TestContext).mockDown.Load() { - failpoint.Break() - } - } - }) - - s.runSubtask(runCtx, executor, subtask) - } - return s.getError() -} - -func (s *BaseScheduler) runSubtask(ctx context.Context, executor execute.SubtaskExecutor, subtask *proto.Subtask) { - err := executor.RunSubtask(ctx, subtask) - failpoint.Inject("MockRunSubtaskCancel", func(val failpoint.Value) { - if val.(bool) { - err = ErrCancelSubtask - } - }) - - failpoint.Inject("MockRunSubtaskContextCanceled", func(val failpoint.Value) { - if val.(bool) { - err = context.Canceled - } - }) - - if err != nil { - s.onError(err) - } - - finished := s.markSubTaskCanceledOrFailed(ctx, subtask) - if finished { - return - } - - failpoint.Inject("mockTiDBDown", func(val failpoint.Value) { - logutil.Logger(s.logCtx).Info("trigger mockTiDBDown") - if s.id == val.(string) || s.id == ":4001" || s.id == ":4002" { - v, ok := testContexts.Load(s.id) - if ok { - v.(*TestContext).TestSyncSubtaskRun <- struct{}{} - v.(*TestContext).mockDown.Store(true) - logutil.Logger(s.logCtx).Info("mockTiDBDown") - time.Sleep(2 * time.Second) - failpoint.Return() - } - } - }) - failpoint.Inject("mockTiDBDown2", func() { - if s.id == ":4003" && subtask.Step == proto.StepTwo { - v, ok := testContexts.Load(s.id) - if ok { - v.(*TestContext).TestSyncSubtaskRun <- struct{}{} - v.(*TestContext).mockDown.Store(true) - time.Sleep(2 * time.Second) - return - } - } - }) - - failpoint.Inject("mockTiDBPartitionThenResume", func(val failpoint.Value) { - if val.(bool) && (s.id == ":4000" || s.id == ":4001" || s.id == ":4002") { - _ = infosync.MockGlobalServerInfoManagerEntry.DeleteByID(s.id) - time.Sleep(20 * time.Second) - } - }) - - failpoint.Inject("MockExecutorRunErr", func(val failpoint.Value) { - if val.(bool) { - s.onError(errors.New("MockExecutorRunErr")) - } - }) - failpoint.Inject("MockExecutorRunCancel", func(val failpoint.Value) { - if taskID, ok := val.(int); ok { - mgr, err := storage.GetTaskManager() - if err != nil { - logutil.BgLogger().Error("get task manager failed", zap.Error(err)) - } else { - err = mgr.CancelGlobalTask(int64(taskID)) - if err != nil { - logutil.BgLogger().Error("cancel global task failed", zap.Error(err)) - } - } - } - }) - s.onSubtaskFinished(ctx, executor, subtask) -} - -func (s *BaseScheduler) onSubtaskFinished(ctx context.Context, executor execute.SubtaskExecutor, subtask *proto.Subtask) { - if err := s.getError(); err == nil { - if err = executor.OnFinished(ctx, subtask); err != nil { - s.onError(err) - } - } - failpoint.Inject("MockSubtaskFinishedCancel", func(val failpoint.Value) { - if val.(bool) { - s.onError(ErrCancelSubtask) - } - }) - - finished := s.markSubTaskCanceledOrFailed(ctx, subtask) - if finished { - return - } - - s.finishSubtaskAndUpdateState(ctx, subtask) - - finished = s.markSubTaskCanceledOrFailed(ctx, subtask) - if finished { - return - } - - failpoint.Inject("syncAfterSubtaskFinish", func() { - TestSyncChan <- struct{}{} - <-TestSyncChan - }) -} - -// Rollback rollbacks the scheduler task. -func (s *BaseScheduler) Rollback(ctx context.Context, task *proto.Task) error { - rollbackCtx, rollbackCancel := context.WithCancelCause(ctx) - defer rollbackCancel(ErrFinishRollback) - s.registerCancelFunc(rollbackCancel) - - s.resetError() - logutil.Logger(s.logCtx).Info("scheduler rollback a step", zap.Any("step", task.Step)) - - // We should cancel all subtasks before rolling back - for { - subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, - proto.TaskStatePending, proto.TaskStateRunning) - if err != nil { - s.onError(err) - return s.getError() - } - - if subtask == nil { - break - } - - s.updateSubtaskStateAndError(subtask, proto.TaskStateCanceled, nil) - if err = s.getError(); err != nil { - return err - } - } - - executor, err := s.GetSubtaskExecutor(ctx, task, nil) - if err != nil { - s.onError(err) - return s.getError() - } - subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, - proto.TaskStateRevertPending, proto.TaskStateReverting) - if err != nil { - s.onError(err) - return s.getError() - } - if subtask == nil { - logutil.BgLogger().Warn("scheduler rollback a step, but no subtask in revert_pending state", zap.Any("step", task.Step)) - return nil - } - if subtask.State == proto.TaskStateRevertPending { - s.updateSubtaskStateAndError(subtask, proto.TaskStateReverting, nil) - } - if err := s.getError(); err != nil { - return err - } - - // right now all impl of Rollback is empty, so we don't check idempotent here. - // will try to remove this rollback completely in the future. - err = executor.Rollback(rollbackCtx) - if err != nil { - s.updateSubtaskStateAndError(subtask, proto.TaskStateRevertFailed, nil) - s.onError(err) - } else { - s.updateSubtaskStateAndError(subtask, proto.TaskStateReverted, nil) - } - return s.getError() -} - -// Pause pause the scheduler task. -func (s *BaseScheduler) Pause(_ context.Context, task *proto.Task) error { - logutil.Logger(s.logCtx).Info("scheduler pause subtasks") - // pause all running subtasks. - if err := s.taskTable.PauseSubtasks(s.id, task.ID); err != nil { - s.onError(err) - return s.getError() - } - return nil -} - -// Close closes the scheduler when all the subtasks are complete. -func (*BaseScheduler) Close() { -} - -func runSummaryCollectLoop( - ctx context.Context, - task *proto.Task, - taskTable TaskTable, -) (summary *execute.Summary, cleanup func(), err error) { - taskMgr, ok := taskTable.(*storage.TaskManager) - if !ok { - return nil, func() {}, nil - } - opt, ok := taskTypes[task.Type] - if !ok { - return nil, func() {}, errors.Errorf("scheduler option for type %s not found", task.Type) - } - if opt.Summary != nil { - go opt.Summary.UpdateRowCountLoop(ctx, taskMgr) - return opt.Summary, func() { - opt.Summary.PersistRowCount(ctx, taskMgr) - }, nil - } - return nil, func() {}, nil -} - -func (s *BaseScheduler) registerCancelFunc(cancel context.CancelCauseFunc) { - s.mu.Lock() - defer s.mu.Unlock() - s.mu.runtimeCancel = cancel -} - -func (s *BaseScheduler) onError(err error) { - if err == nil { - return - } - err = errors.Trace(err) - logutil.Logger(s.logCtx).Error("onError", zap.Error(err)) - s.mu.Lock() - defer s.mu.Unlock() - - if s.mu.err == nil { - s.mu.err = err - logutil.Logger(s.logCtx).Error("scheduler error", zap.Error(err)) - } - - if s.mu.runtimeCancel != nil { - s.mu.runtimeCancel(err) - } -} - -func (s *BaseScheduler) markErrorHandled() { - s.mu.Lock() - defer s.mu.Unlock() - s.mu.handled = true -} - -func (s *BaseScheduler) getError() error { - s.mu.RLock() - defer s.mu.RUnlock() - return s.mu.err -} - -func (s *BaseScheduler) resetError() { - s.mu.Lock() - defer s.mu.Unlock() - s.mu.err = nil - s.mu.handled = false -} - -func (s *BaseScheduler) startSubtaskAndUpdateState(ctx context.Context, subtask *proto.Subtask) { - metrics.DecDistTaskSubTaskCnt(subtask) - metrics.EndDistTaskSubTask(subtask) - s.startSubtask(ctx, subtask.ID) - subtask.State = proto.TaskStateRunning - metrics.IncDistTaskSubTaskCnt(subtask) - metrics.StartDistTaskSubTask(subtask) -} - -func (s *BaseScheduler) updateSubtaskStateAndErrorImpl(subtaskID int64, state string, subTaskErr error) { - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - logger := logutil.Logger(s.logCtx) - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - ctx := context.Background() - err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, s.taskTable.UpdateSubtaskStateAndError(subtaskID, state, subTaskErr) - }, - ) - if err != nil { - s.onError(err) - } -} - -func (s *BaseScheduler) startSubtask(ctx context.Context, subtaskID int64) { - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - logger := logutil.Logger(s.logCtx) - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, s.taskTable.StartSubtask(subtaskID) - }, - ) - if err != nil { - s.onError(err) - } -} - -func (s *BaseScheduler) finishSubtask(ctx context.Context, subtask *proto.Subtask) { - logger := logutil.Logger(s.logCtx) - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, s.taskTable.FinishSubtask(subtask.ID, subtask.Meta) - }, - ) - if err != nil { - s.onError(err) - } -} - -func (s *BaseScheduler) updateSubtaskStateAndError(subtask *proto.Subtask, state string, subTaskErr error) { - metrics.DecDistTaskSubTaskCnt(subtask) - metrics.EndDistTaskSubTask(subtask) - s.updateSubtaskStateAndErrorImpl(subtask.ID, state, subTaskErr) - subtask.State = state - metrics.IncDistTaskSubTaskCnt(subtask) - if !subtask.IsFinished() { - metrics.StartDistTaskSubTask(subtask) - } -} - -func (s *BaseScheduler) finishSubtaskAndUpdateState(ctx context.Context, subtask *proto.Subtask) { - metrics.DecDistTaskSubTaskCnt(subtask) - metrics.EndDistTaskSubTask(subtask) - s.finishSubtask(ctx, subtask) - subtask.State = proto.TaskStateSucceed - metrics.IncDistTaskSubTaskCnt(subtask) -} - -// markSubTaskCanceledOrFailed check the error type and decide the subtasks' state. -// 1. Only cancel subtasks when meet ErrCancelSubtask. -// 2. Only fail subtasks when meet non retryable error. -// 3. When meet other errors, don't change subtasks' state. -func (s *BaseScheduler) markSubTaskCanceledOrFailed(ctx context.Context, subtask *proto.Subtask) bool { - if err := s.getError(); err != nil { - if ctx.Err() != nil && context.Cause(ctx) == ErrCancelSubtask { - logutil.Logger(s.logCtx).Warn("subtask canceled", zap.Error(err)) - s.updateSubtaskStateAndError(subtask, proto.TaskStateCanceled, nil) - } else if common.IsRetryableError(err) { - logutil.Logger(s.logCtx).Warn("met retryable error", zap.Error(err)) - } else if errors.Cause(err) != context.Canceled { - logutil.Logger(s.logCtx).Warn("subtask failed", zap.Error(err)) - s.updateSubtaskStateAndError(subtask, proto.TaskStateFailed, err) - } else { - logutil.Logger(s.logCtx).Info("met context canceled for gracefully shutdown", zap.Error(err)) - } - s.markErrorHandled() - return true - } - return false -} - -func (s *BaseScheduler) updateErrorToSubtask(ctx context.Context, taskID int64, err error) error { - logger := logutil.Logger(s.logCtx) - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - err1 := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, s.taskTable.UpdateErrorToSubtask(s.id, taskID, err) - }, - ) - return err1 -} diff --git a/disttask/framework/storage/BUILD.bazel b/disttask/framework/storage/BUILD.bazel deleted file mode 100644 index 6a65604959789..0000000000000 --- a/disttask/framework/storage/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "storage", - srcs = [ - "task_table.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/disttask/framework/storage", - visibility = ["//visibility:public"], - deps = [ - "//disttask/framework/proto", - "//kv", - "//parser/terror", - "//sessionctx", - "//util/chunk", - "//util/intest", - "//util/logutil", - "//util/sqlexec", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "storage_test", - timeout = "short", - srcs = ["table_test.go"], - flaky = True, - race = "on", - shard_count = 7, - deps = [ - ":storage", - "//disttask/framework/proto", - "//testkit", - "//testkit/testsetup", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/disttask/framework/storage/table_test.go b/disttask/framework/storage/table_test.go deleted file mode 100644 index 2671cc9e2e9b2..0000000000000 --- a/disttask/framework/storage/table_test.go +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package storage_test - -import ( - "context" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} - -func GetResourcePool(t *testing.T) *pools.ResourcePool { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - pool := pools.NewResourcePool(func() (pools.Resource, error) { - return tk.Session(), nil - }, 1, 1, time.Second) - return pool -} - -func GetTaskManager(t *testing.T, pool *pools.ResourcePool) *storage.TaskManager { - manager := storage.NewTaskManager(context.Background(), pool) - storage.SetTaskManager(manager) - manager, err := storage.GetTaskManager() - require.NoError(t, err) - return manager -} - -func TestGlobalTaskTable(t *testing.T) { - pool := GetResourcePool(t) - gm := GetTaskManager(t, pool) - defer pool.Close() - id, err := gm.AddNewGlobalTask("key1", "test", 4, []byte("test")) - require.NoError(t, err) - require.Equal(t, int64(1), id) - - task, err := gm.GetNewGlobalTask() - require.NoError(t, err) - require.Equal(t, int64(1), task.ID) - require.Equal(t, "key1", task.Key) - require.Equal(t, "test", task.Type) - require.Equal(t, proto.TaskStatePending, task.State) - require.Equal(t, uint64(4), task.Concurrency) - require.Equal(t, []byte("test"), task.Meta) - - task2, err := gm.GetGlobalTaskByID(1) - require.NoError(t, err) - require.Equal(t, task, task2) - - task3, err := gm.GetGlobalTasksInStates(proto.TaskStatePending) - require.NoError(t, err) - require.Len(t, task3, 1) - require.Equal(t, task, task3[0]) - - task4, err := gm.GetGlobalTasksInStates(proto.TaskStatePending, proto.TaskStateRunning) - require.NoError(t, err) - require.Len(t, task4, 1) - require.Equal(t, task, task4[0]) - require.GreaterOrEqual(t, task4[0].StateUpdateTime, task.StateUpdateTime) - - prevState := task.State - task.State = proto.TaskStateRunning - retryable, err := gm.UpdateGlobalTaskAndAddSubTasks(task, nil, prevState) - require.NoError(t, err) - require.Equal(t, true, retryable) - - task5, err := gm.GetGlobalTasksInStates(proto.TaskStateRunning) - require.NoError(t, err) - require.Len(t, task5, 1) - require.Equal(t, task.State, task5[0].State) - - task6, err := gm.GetGlobalTaskByKey("key1") - require.NoError(t, err) - require.Len(t, task5, 1) - require.Equal(t, task.State, task6.State) - - // test cannot insert task with dup key - _, err = gm.AddNewGlobalTask("key1", "test2", 4, []byte("test2")) - require.EqualError(t, err, "[kv:1062]Duplicate entry 'key1' for key 'tidb_global_task.task_key'") - - // test cancel global task - id, err = gm.AddNewGlobalTask("key2", "test", 4, []byte("test")) - require.NoError(t, err) - - cancelling, err := gm.IsGlobalTaskCancelling(id) - require.NoError(t, err) - require.False(t, cancelling) - - require.NoError(t, gm.CancelGlobalTask(id)) - cancelling, err = gm.IsGlobalTaskCancelling(id) - require.NoError(t, err) - require.True(t, cancelling) -} - -func TestSubTaskTable(t *testing.T) { - pool := GetResourcePool(t) - sm := GetTaskManager(t, pool) - defer pool.Close() - - err := sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false) - require.NoError(t, err) - - nilTask, err := sm.GetFirstSubtaskInStates("tidb2", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.Nil(t, nilTask) - - subtask, err := sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, proto.TaskTypeExample, subtask.Type) - require.Equal(t, int64(1), subtask.TaskID) - require.Equal(t, proto.TaskStatePending, subtask.State) - require.Equal(t, "tidb1", subtask.SchedulerID) - require.Equal(t, []byte("test"), subtask.Meta) - require.Zero(t, subtask.StartTime) - require.Zero(t, subtask.UpdateTime) - - subtask2, err := sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending, proto.TaskStateReverted) - require.NoError(t, err) - require.Equal(t, subtask, subtask2) - - ids, err := sm.GetSchedulerIDsByTaskID(1) - require.NoError(t, err) - require.Len(t, ids, 1) - require.Equal(t, "tidb1", ids[0]) - - ids, err = sm.GetSchedulerIDsByTaskID(3) - require.NoError(t, err) - require.Len(t, ids, 0) - - cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(1), cnt) - - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(1), cnt) - - ok, err := sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.True(t, ok) - - ts := time.Now() - time.Sleep(time.Second) - require.NoError(t, sm.StartSubtask(1)) - - subtask, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.Nil(t, subtask) - - subtask, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStateRunning) - require.NoError(t, err) - require.Equal(t, proto.TaskTypeExample, subtask.Type) - require.Equal(t, int64(1), subtask.TaskID) - require.Equal(t, proto.TaskStateRunning, subtask.State) - require.Equal(t, "tidb1", subtask.SchedulerID) - require.Equal(t, []byte("test"), subtask.Meta) - require.GreaterOrEqual(t, subtask.StartTime, ts) - require.GreaterOrEqual(t, subtask.UpdateTime, ts) - - // check update time after state change to cancel - time.Sleep(time.Second) - require.NoError(t, sm.UpdateSubtaskStateAndError(1, proto.TaskStateCancelling, nil)) - subtask2, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStateCancelling) - require.NoError(t, err) - require.Equal(t, proto.TaskStateCancelling, subtask2.State) - require.Greater(t, subtask2.UpdateTime, subtask.UpdateTime) - - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(0), cnt) - - ok, err = sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.False(t, ok) - - err = sm.DeleteSubtasksByTaskID(1) - require.NoError(t, err) - - ok, err = sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending, proto.TaskStateRunning) - require.NoError(t, err) - require.False(t, ok) - - err = sm.AddNewSubTask(2, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, true) - require.NoError(t, err) - - cnt, err = sm.GetSubtaskInStatesCnt(2, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(1), cnt) - - subtasks, err := sm.GetSucceedSubtasksByStep(2, proto.StepInit) - require.NoError(t, err) - require.Len(t, subtasks, 0) - - err = sm.FinishSubtask(2, []byte{}) - require.NoError(t, err) - - subtasks, err = sm.GetSucceedSubtasksByStep(2, proto.StepInit) - require.NoError(t, err) - require.Len(t, subtasks, 1) - - rowCount, err := sm.GetSubtaskRowCount(2, proto.StepInit) - require.NoError(t, err) - require.Equal(t, int64(0), rowCount) - err = sm.UpdateSubtaskRowCount(2, 100) - require.NoError(t, err) - rowCount, err = sm.GetSubtaskRowCount(2, proto.StepInit) - require.NoError(t, err) - require.Equal(t, int64(100), rowCount) - - // test UpdateErrorToSubtask do update start/update time - err = sm.AddNewSubTask(3, proto.StepInit, "for_test", []byte("test"), proto.TaskTypeExample, false) - require.NoError(t, err) - require.NoError(t, sm.UpdateErrorToSubtask("for_test", 3, errors.New("fail"))) - subtask, err = sm.GetFirstSubtaskInStates("for_test", 3, proto.StepInit, proto.TaskStateFailed) - require.NoError(t, err) - require.Equal(t, proto.TaskStateFailed, subtask.State) - require.Greater(t, subtask.StartTime, ts) - require.Greater(t, subtask.UpdateTime, ts) - - // test FinishSubtask do update update time - err = sm.AddNewSubTask(4, proto.StepInit, "for_test1", []byte("test"), proto.TaskTypeExample, false) - require.NoError(t, err) - subtask, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.NoError(t, sm.StartSubtask(subtask.ID)) - subtask, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStateRunning) - require.NoError(t, err) - require.Greater(t, subtask.StartTime, ts) - require.Greater(t, subtask.UpdateTime, ts) - time.Sleep(time.Second) - require.NoError(t, sm.FinishSubtask(subtask.ID, []byte{})) - subtask2, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStateSucceed) - require.NoError(t, err) - require.Equal(t, subtask2.StartTime, subtask.StartTime) - require.Greater(t, subtask2.UpdateTime, subtask.UpdateTime) - - // test UpdateFailedSchedulerIDs and IsSchedulerCanceled - canceled, err := sm.IsSchedulerCanceled("for_test999", 4) - require.NoError(t, err) - require.True(t, canceled) - canceled, err = sm.IsSchedulerCanceled("for_test1", 4) - require.NoError(t, err) - require.False(t, canceled) - canceled, err = sm.IsSchedulerCanceled("for_test2", 4) - require.NoError(t, err) - require.True(t, canceled) - - require.NoError(t, sm.UpdateSubtaskStateAndError(4, proto.TaskStateRunning, nil)) - require.NoError(t, sm.UpdateFailedSchedulerIDs(4, map[string]string{ - "for_test1": "for_test999", - "for_test2": "for_test999", - })) - - canceled, err = sm.IsSchedulerCanceled("for_test1", 4) - require.NoError(t, err) - require.True(t, canceled) - canceled, err = sm.IsSchedulerCanceled("for_test2", 4) - require.NoError(t, err) - require.True(t, canceled) - canceled, err = sm.IsSchedulerCanceled("for_test999", 4) - require.NoError(t, err) - require.False(t, canceled) -} - -func TestBothGlobalAndSubTaskTable(t *testing.T) { - pool := GetResourcePool(t) - sm := GetTaskManager(t, pool) - defer pool.Close() - - id, err := sm.AddNewGlobalTask("key1", "test", 4, []byte("test")) - require.NoError(t, err) - require.Equal(t, int64(1), id) - - task, err := sm.GetNewGlobalTask() - require.NoError(t, err) - require.Equal(t, proto.TaskStatePending, task.State) - - // isSubTaskRevert: false - prevState := task.State - task.State = proto.TaskStateRunning - subTasks := []*proto.Subtask{ - { - Step: proto.StepInit, - Type: proto.TaskTypeExample, - SchedulerID: "instance1", - Meta: []byte("m1"), - }, - { - Step: proto.StepInit, - Type: proto.TaskTypeExample, - SchedulerID: "instance2", - Meta: []byte("m2"), - }, - } - retryable, err := sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) - require.NoError(t, err) - require.Equal(t, true, retryable) - - task, err = sm.GetGlobalTaskByID(1) - require.NoError(t, err) - require.Equal(t, proto.TaskStateRunning, task.State) - - subtask1, err := sm.GetFirstSubtaskInStates("instance1", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(1), subtask1.ID) - require.Equal(t, proto.TaskTypeExample, subtask1.Type) - require.Equal(t, []byte("m1"), subtask1.Meta) - - subtask2, err := sm.GetFirstSubtaskInStates("instance2", 1, proto.StepInit, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(2), subtask2.ID) - require.Equal(t, proto.TaskTypeExample, subtask2.Type) - require.Equal(t, []byte("m2"), subtask2.Meta) - - cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(2), cnt) - - // isSubTaskRevert: true - prevState = task.State - task.State = proto.TaskStateReverting - subTasks = []*proto.Subtask{ - { - Step: proto.StepInit, - Type: proto.TaskTypeExample, - SchedulerID: "instance3", - Meta: []byte("m3"), - }, - { - Step: proto.StepInit, - Type: proto.TaskTypeExample, - SchedulerID: "instance4", - Meta: []byte("m4"), - }, - } - retryable, err = sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) - require.NoError(t, err) - require.Equal(t, true, retryable) - - task, err = sm.GetGlobalTaskByID(1) - require.NoError(t, err) - require.Equal(t, proto.TaskStateReverting, task.State) - - subtask1, err = sm.GetFirstSubtaskInStates("instance3", 1, proto.StepInit, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(3), subtask1.ID) - require.Equal(t, proto.TaskTypeExample, subtask1.Type) - require.Equal(t, []byte("m3"), subtask1.Meta) - - subtask2, err = sm.GetFirstSubtaskInStates("instance4", 1, proto.StepInit, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(4), subtask2.ID) - require.Equal(t, proto.TaskTypeExample, subtask2.Type) - require.Equal(t, []byte("m4"), subtask2.Meta) - - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(2), cnt) - - // test transactional - require.NoError(t, sm.DeleteSubtasksByTaskID(1)) - failpoint.Enable("github.com/pingcap/tidb/disttask/framework/storage/MockUpdateTaskErr", "1*return(true)") - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/storage/MockUpdateTaskErr")) - }() - prevState = task.State - task.State = proto.TaskStateFailed - retryable, err = sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) - require.EqualError(t, err, "updateTaskErr") - require.Equal(t, true, retryable) - - task, err = sm.GetGlobalTaskByID(1) - require.NoError(t, err) - require.Equal(t, proto.TaskStateReverting, task.State) - - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStateRevertPending) - require.NoError(t, err) - require.Equal(t, int64(0), cnt) -} - -func TestDistFrameworkMeta(t *testing.T) { - pool := GetResourcePool(t) - sm := GetTaskManager(t, pool) - defer pool.Close() - - require.NoError(t, sm.StartManager(":4000", "background")) - require.NoError(t, sm.StartManager(":4001", "")) - require.NoError(t, sm.StartManager(":4002", "background")) - nodes, err := sm.GetNodesByRole("background") - require.NoError(t, err) - require.Equal(t, map[string]bool{ - ":4000": true, - ":4002": true, - }, nodes) - - nodes, err = sm.GetNodesByRole("") - require.NoError(t, err) - require.Equal(t, map[string]bool{ - ":4001": true, - }, nodes) -} - -func TestSubtaskHistoryTable(t *testing.T) { - pool := GetResourcePool(t) - sm := GetTaskManager(t, pool) - defer pool.Close() - - const ( - taskID = 1 - taskID2 = 2 - subTask1 = 1 - subTask2 = 2 - subTask3 = 3 - subTask4 = 4 // taskID2 - tidb1 = "tidb1" - tidb2 = "tidb2" - tidb3 = "tidb3" - meta = "test" - finishedMeta = "finished" - ) - - require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb1, []byte(meta), proto.TaskTypeExample, false)) - require.NoError(t, sm.FinishSubtask(subTask1, []byte(finishedMeta))) - require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb2, []byte(meta), proto.TaskTypeExample, false)) - require.NoError(t, sm.UpdateSubtaskStateAndError(subTask2, proto.TaskStateCanceled, nil)) - require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb3, []byte(meta), proto.TaskTypeExample, false)) - require.NoError(t, sm.UpdateSubtaskStateAndError(subTask3, proto.TaskStateFailed, nil)) - - subTasks, err := storage.GetSubtasksByTaskIDForTest(sm, taskID) - require.NoError(t, err) - require.Len(t, subTasks, 3) - historySubTasksCnt, err := storage.GetSubtasksFromHistoryForTest(sm) - require.NoError(t, err) - require.Equal(t, 0, historySubTasksCnt) - subTasks, err = sm.GetSubtasksForImportInto(taskID, proto.StepInit) - require.NoError(t, err) - require.Len(t, subTasks, 3) - - // test TransferSubTasks2History - require.NoError(t, sm.TransferSubTasks2History(taskID)) - - subTasks, err = storage.GetSubtasksByTaskIDForTest(sm, taskID) - require.NoError(t, err) - require.Len(t, subTasks, 0) - historySubTasksCnt, err = storage.GetSubtasksFromHistoryForTest(sm) - require.NoError(t, err) - require.Equal(t, 3, historySubTasksCnt) - subTasks, err = sm.GetSubtasksForImportInto(taskID, proto.StepInit) - require.NoError(t, err) - require.Len(t, subTasks, 3) - - // test GC history table. - failpoint.Enable("github.com/pingcap/tidb/disttask/framework/storage/subtaskHistoryKeepSeconds", "return(1)") - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/storage/subtaskHistoryKeepSeconds")) - }() - time.Sleep(2 * time.Second) - - require.NoError(t, sm.AddNewSubTask(taskID2, proto.StepInit, tidb1, []byte(meta), proto.TaskTypeExample, false)) - require.NoError(t, sm.UpdateSubtaskStateAndError(subTask4, proto.TaskStateFailed, nil)) - require.NoError(t, sm.TransferSubTasks2History(taskID2)) - - require.NoError(t, sm.GCSubtasks()) - - historySubTasksCnt, err = storage.GetSubtasksFromHistoryForTest(sm) - require.NoError(t, err) - require.Equal(t, 1, historySubTasksCnt) -} - -func TestTaskHistoryTable(t *testing.T) { - pool := GetResourcePool(t) - gm := GetTaskManager(t, pool) - defer pool.Close() - - _, err := gm.AddNewGlobalTask("1", proto.TaskTypeExample, 1, nil) - require.NoError(t, err) - taskID, err := gm.AddNewGlobalTask("2", proto.TaskTypeExample, 1, nil) - require.NoError(t, err) - - tasks, err := gm.GetGlobalTasksInStates(proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, 2, len(tasks)) - - require.NoError(t, gm.TransferTasks2History(tasks)) - - tasks, err = gm.GetGlobalTasksInStates(proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, 0, len(tasks)) - num, err := storage.GetTasksFromHistoryForTest(gm) - require.NoError(t, err) - require.Equal(t, 2, num) - - task, err := gm.GetTaskByIDWithHistory(taskID) - require.NoError(t, err) - require.NotNil(t, task) - - task, err = gm.GetGlobalTaskByKeyWithHistory("1") - require.NoError(t, err) - require.NotNil(t, task) - - // task with fail transfer - _, err = gm.AddNewGlobalTask("3", proto.TaskTypeExample, 1, nil) - require.NoError(t, err) - tasks, err = gm.GetGlobalTasksInStates(proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, 1, len(tasks)) - tasks[0].Error = errors.New("mock err") - require.NoError(t, gm.TransferTasks2History(tasks)) - num, err = storage.GetTasksFromHistoryForTest(gm) - require.NoError(t, err) - require.Equal(t, 3, num) -} - -func TestPauseAndResume(t *testing.T) { - pool := GetResourcePool(t) - sm := GetTaskManager(t, pool) - defer pool.Close() - require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) - require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) - require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) - // 1.1 pause all subtasks. - require.NoError(t, sm.PauseSubtasks("tidb1", 1)) - cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePaused) - require.NoError(t, err) - require.Equal(t, int64(3), cnt) - // 1.2 resume all subtasks. - require.NoError(t, sm.ResumeSubtasks(1)) - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(3), cnt) - - // 2.1 pause 2 subtasks. - sm.UpdateSubtaskStateAndError(1, proto.TaskStateSucceed, nil) - require.NoError(t, sm.PauseSubtasks("tidb1", 1)) - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePaused) - require.NoError(t, err) - require.Equal(t, int64(2), cnt) - // 2.2 resume 2 subtasks. - require.NoError(t, sm.ResumeSubtasks(1)) - cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) - require.NoError(t, err) - require.Equal(t, int64(2), cnt) -} diff --git a/disttask/framework/storage/util.go b/disttask/framework/storage/util.go deleted file mode 100644 index c9feff2ce73be..0000000000000 --- a/disttask/framework/storage/util.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package storage - -import "github.com/pingcap/tidb/disttask/framework/proto" - -// GetSubtasksFromHistoryForTest gets subtasks from history table for test. -func GetSubtasksFromHistoryForTest(stm *TaskManager) (int, error) { - rs, err := stm.executeSQLWithNewSession(stm.ctx, - "select * from mysql.tidb_background_subtask_history") - if err != nil { - return 0, err - } - return len(rs), nil -} - -// GetSubtasksFromHistoryByTaskIDForTest gets subtasks by taskID from history table for test. -func GetSubtasksFromHistoryByTaskIDForTest(stm *TaskManager, taskID int64) (int, error) { - rs, err := stm.executeSQLWithNewSession(stm.ctx, - "select * from mysql.tidb_background_subtask_history where task_key = %?", taskID) - if err != nil { - return 0, err - } - return len(rs), nil -} - -// GetSubtasksByTaskIDForTest gets subtasks by taskID for test. -func GetSubtasksByTaskIDForTest(stm *TaskManager, taskID int64) ([]*proto.Subtask, error) { - rs, err := stm.executeSQLWithNewSession(stm.ctx, - "select * from mysql.tidb_background_subtask where task_key = %?", taskID) - if err != nil { - return nil, err - } - if len(rs) == 0 { - return nil, nil - } - subtasks := make([]*proto.Subtask, 0, len(rs)) - for _, r := range rs { - subtasks = append(subtasks, row2SubTask(r)) - } - return subtasks, nil -} - -// GetTasksFromHistoryForTest gets tasks from history table for test. -func GetTasksFromHistoryForTest(stm *TaskManager) (int, error) { - rs, err := stm.executeSQLWithNewSession(stm.ctx, - "select * from mysql.tidb_global_task_history") - if err != nil { - return 0, err - } - return len(rs), nil -} diff --git a/disttask/importinto/BUILD.bazel b/disttask/importinto/BUILD.bazel deleted file mode 100644 index 0ee8c7f99d65e..0000000000000 --- a/disttask/importinto/BUILD.bazel +++ /dev/null @@ -1,129 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "importinto", - srcs = [ - "clean_s3.go", - "dispatcher.go", - "encode_and_sort_operator.go", - "job.go", - "metrics.go", - "planner.go", - "proto.go", - "scheduler.go", - "subtask_executor.go", - "wrapper.go", - ], - importpath = "github.com/pingcap/tidb/disttask/importinto", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/backend", - "//br/pkg/lightning/backend/external", - "//br/pkg/lightning/backend/kv", - "//br/pkg/lightning/backend/local", - "//br/pkg/lightning/checkpoints", - "//br/pkg/lightning/common", - "//br/pkg/lightning/config", - "//br/pkg/lightning/log", - "//br/pkg/lightning/metric", - "//br/pkg/lightning/mydump", - "//br/pkg/lightning/verification", - "//br/pkg/storage", - "//br/pkg/utils", - "//config", - "//disttask/framework/dispatcher", - "//disttask/framework/handle", - "//disttask/framework/planner", - "//disttask/framework/proto", - "//disttask/framework/scheduler", - "//disttask/framework/scheduler/execute", - "//disttask/framework/storage", - "//disttask/operator", - "//domain/infosync", - "//errno", - "//executor/asyncloaddata", - "//executor/importer", - "//kv", - "//meta/autoid", - "//metrics", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//resourcemanager/pool/workerpool", - "//resourcemanager/util", - "//sessionctx", - "//sessionctx/variable", - "//table/tables", - "//util", - "//util/backoff", - "//util/dbterror/exeerrors", - "//util/etcd", - "//util/logutil", - "//util/mathutil", - "//util/promutil", - "//util/size", - "//util/sqlexec", - "@com_github_docker_go_units//:go-units", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "importinto_test", - timeout = "short", - srcs = [ - "dispatcher_test.go", - "dispatcher_testkit_test.go", - "encode_and_sort_operator_test.go", - "job_testkit_test.go", - "metrics_test.go", - "planner_test.go", - "subtask_executor_test.go", - "wrapper_test.go", - ], - embed = [":importinto"], - flaky = True, - race = "on", - shard_count = 14, - deps = [ - "//br/pkg/lightning/backend", - "//br/pkg/lightning/backend/external", - "//br/pkg/lightning/checkpoints", - "//br/pkg/lightning/mydump", - "//br/pkg/lightning/verification", - "//ddl", - "//disttask/framework/dispatcher", - "//disttask/framework/planner", - "//disttask/framework/proto", - "//disttask/framework/storage", - "//disttask/importinto/mock", - "//disttask/operator", - "//domain/infosync", - "//executor/importer", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//testkit", - "//util/logutil", - "//util/mock", - "//util/sqlexec", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_stretchr_testify//require", - "@com_github_stretchr_testify//suite", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_mock//gomock", - "@org_uber_go_zap//:zap", - ], -) diff --git a/disttask/importinto/dispatcher.go b/disttask/importinto/dispatcher.go deleted file mode 100644 index d4e96788e913e..0000000000000 --- a/disttask/importinto/dispatcher.go +++ /dev/null @@ -1,757 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/json" - "strconv" - "strings" - "sync" - "time" - - dmysql "github.com/go-sql-driver/mysql" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/br/pkg/lightning/config" - "github.com/pingcap/tidb/br/pkg/lightning/metric" - "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/backoff" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/atomic" - "go.uber.org/zap" -) - -const ( - registerTaskTTL = 10 * time.Minute - refreshTaskTTLInterval = 3 * time.Minute - registerTimeout = 5 * time.Second -) - -// NewTaskRegisterWithTTL is the ctor for TaskRegister. -// It is exported for testing. -var NewTaskRegisterWithTTL = utils.NewTaskRegisterWithTTL - -type taskInfo struct { - taskID int64 - - // operation on taskInfo is run inside detect-task goroutine, so no need to synchronize. - lastRegisterTime time.Time - - // initialized lazily in register() - etcdClient *etcd.Client - taskRegister utils.TaskRegister -} - -func (t *taskInfo) register(ctx context.Context) { - if time.Since(t.lastRegisterTime) < refreshTaskTTLInterval { - return - } - - if time.Since(t.lastRegisterTime) < refreshTaskTTLInterval { - return - } - logger := logutil.BgLogger().With(zap.Int64("task-id", t.taskID)) - if t.taskRegister == nil { - client, err := importer.GetEtcdClient() - if err != nil { - logger.Warn("get etcd client failed", zap.Error(err)) - return - } - t.etcdClient = client - t.taskRegister = NewTaskRegisterWithTTL(client.GetClient(), registerTaskTTL, - utils.RegisterImportInto, strconv.FormatInt(t.taskID, 10)) - } - timeoutCtx, cancel := context.WithTimeout(ctx, registerTimeout) - defer cancel() - if err := t.taskRegister.RegisterTaskOnce(timeoutCtx); err != nil { - logger.Warn("register task failed", zap.Error(err)) - } else { - logger.Info("register task to pd or refresh lease success") - } - // we set it even if register failed, TTL is 10min, refresh interval is 3min, - // we can try 2 times before the lease is expired. - t.lastRegisterTime = time.Now() -} - -func (t *taskInfo) close(ctx context.Context) { - logger := logutil.BgLogger().With(zap.Int64("task-id", t.taskID)) - if t.taskRegister != nil { - timeoutCtx, cancel := context.WithTimeout(ctx, registerTimeout) - defer cancel() - if err := t.taskRegister.Close(timeoutCtx); err != nil { - logger.Warn("unregister task failed", zap.Error(err)) - } else { - logger.Info("unregister task success") - } - t.taskRegister = nil - } - if t.etcdClient != nil { - if err := t.etcdClient.Close(); err != nil { - logger.Warn("close etcd client failed", zap.Error(err)) - } - t.etcdClient = nil - } -} - -// ImportDispatcherExt is an extension of ImportDispatcher, exported for test. -type ImportDispatcherExt struct { - GlobalSort bool - mu sync.RWMutex - // NOTE: there's no need to sync for below 2 fields actually, since we add a restriction that only one - // task can be running at a time. but we might support task queuing in the future, leave it for now. - // the last time we switch TiKV into IMPORT mode, this is a global operation, do it for one task makes - // no difference to do it for all tasks. So we do not need to record the switch time for each task. - lastSwitchTime atomic.Time - // taskInfoMap is a map from taskID to taskInfo - taskInfoMap sync.Map - - // currTaskID is the taskID of the current running task. - // It may be changed when we switch to a new task or switch to a new owner. - currTaskID atomic.Int64 - disableTiKVImportMode atomic.Bool -} - -var _ dispatcher.Extension = (*ImportDispatcherExt)(nil) - -// OnTick implements dispatcher.Extension interface. -func (dsp *ImportDispatcherExt) OnTick(ctx context.Context, task *proto.Task) { - // only switch TiKV mode or register task when task is running - if task.State != proto.TaskStateRunning { - return - } - dsp.switchTiKVMode(ctx, task) - dsp.registerTask(ctx, task) -} - -func (*ImportDispatcherExt) isImporting2TiKV(task *proto.Task) bool { - return task.Step == StepImport || task.Step == StepWriteAndIngest -} - -func (dsp *ImportDispatcherExt) switchTiKVMode(ctx context.Context, task *proto.Task) { - dsp.updateCurrentTask(task) - // only import step need to switch to IMPORT mode, - // If TiKV is in IMPORT mode during checksum, coprocessor will time out. - if dsp.disableTiKVImportMode.Load() || !dsp.isImporting2TiKV(task) { - return - } - - if time.Since(dsp.lastSwitchTime.Load()) < config.DefaultSwitchTiKVModeInterval { - return - } - - dsp.mu.Lock() - defer dsp.mu.Unlock() - if time.Since(dsp.lastSwitchTime.Load()) < config.DefaultSwitchTiKVModeInterval { - return - } - - logger := logutil.BgLogger().With(zap.Int64("task-id", task.ID)) - pdCli, switcher, err := importer.GetTiKVModeSwitcherWithPDClient(ctx, logger) - if err != nil { - logger.Warn("get tikv mode switcher failed", zap.Error(err)) - return - } - switcher.ToImportMode(ctx) - pdCli.Close() - dsp.lastSwitchTime.Store(time.Now()) -} - -func (dsp *ImportDispatcherExt) registerTask(ctx context.Context, task *proto.Task) { - val, _ := dsp.taskInfoMap.LoadOrStore(task.ID, &taskInfo{taskID: task.ID}) - info := val.(*taskInfo) - info.register(ctx) -} - -func (dsp *ImportDispatcherExt) unregisterTask(ctx context.Context, task *proto.Task) { - if val, loaded := dsp.taskInfoMap.LoadAndDelete(task.ID); loaded { - info := val.(*taskInfo) - info.close(ctx) - } -} - -// OnNextSubtasksBatch generate batch of next stage's plan. -func (dsp *ImportDispatcherExt) OnNextSubtasksBatch( - ctx context.Context, - taskHandle dispatcher.TaskHandle, - gTask *proto.Task, - nextStep int64, -) ( - resSubtaskMeta [][]byte, err error) { - logger := logutil.BgLogger().With( - zap.String("type", gTask.Type), - zap.Int64("task-id", gTask.ID), - zap.String("curr-step", stepStr(gTask.Step)), - zap.String("next-step", stepStr(nextStep)), - ) - taskMeta := &TaskMeta{} - err = json.Unmarshal(gTask.Meta, taskMeta) - if err != nil { - return nil, errors.Trace(err) - } - logger.Info("on next subtasks batch") - - defer func() { - taskFinished := err == nil && nextStep == proto.StepDone - if taskFinished { - // todo: we're not running in a transaction with task update - if err2 := dsp.finishJob(ctx, logger, taskHandle, gTask, taskMeta); err2 != nil { - err = err2 - } - } else if err != nil && !dsp.IsRetryableErr(err) { - if err2 := dsp.failJob(ctx, taskHandle, gTask, taskMeta, logger, err.Error()); err2 != nil { - // todo: we're not running in a transaction with task update, there might be case - // failJob return error, but task update succeed. - logger.Error("call failJob failed", zap.Error(err2)) - } - } - }() - - previousSubtaskMetas := make(map[int64][][]byte, 1) - switch nextStep { - case StepImport, StepEncodeAndSort: - if metrics, ok := metric.GetCommonMetric(ctx); ok { - metrics.BytesCounter.WithLabelValues(metric.StateTotalRestore).Add(float64(taskMeta.Plan.TotalFileSize)) - } - jobStep := importer.JobStepImporting - if dsp.GlobalSort { - jobStep = importer.JobStepGlobalSorting - } - if err = startJob(ctx, logger, taskHandle, taskMeta, jobStep); err != nil { - return nil, err - } - case StepMergeSort: - sortAndEncodeMeta, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepEncodeAndSort) - if err != nil { - return nil, err - } - previousSubtaskMetas[StepEncodeAndSort] = sortAndEncodeMeta - case StepWriteAndIngest: - failpoint.Inject("failWhenDispatchWriteIngestSubtask", func() { - failpoint.Return(nil, errors.New("injected error")) - }) - // merge sort might be skipped for some kv groups, so we need to get all - // subtask metas of StepEncodeAndSort step too. - encodeAndSortMetas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepEncodeAndSort) - if err != nil { - return nil, err - } - mergeSortMetas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepMergeSort) - if err != nil { - return nil, err - } - previousSubtaskMetas[StepEncodeAndSort] = encodeAndSortMetas - previousSubtaskMetas[StepMergeSort] = mergeSortMetas - if err = job2Step(ctx, logger, taskMeta, importer.JobStepImporting); err != nil { - return nil, err - } - case StepPostProcess: - dsp.switchTiKV2NormalMode(ctx, gTask, logger) - failpoint.Inject("clearLastSwitchTime", func() { - dsp.lastSwitchTime.Store(time.Time{}) - }) - if err = job2Step(ctx, logger, taskMeta, importer.JobStepValidating); err != nil { - return nil, err - } - failpoint.Inject("failWhenDispatchPostProcessSubtask", func() { - failpoint.Return(nil, errors.New("injected error after StepImport")) - }) - // we need get metas where checksum is stored. - if err := updateResult(taskHandle, gTask, taskMeta, dsp.GlobalSort); err != nil { - return nil, err - } - step := getStepOfEncode(dsp.GlobalSort) - metas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, step) - if err != nil { - return nil, err - } - previousSubtaskMetas[step] = metas - logger.Info("move to post-process step ", zap.Any("result", taskMeta.Result)) - case proto.StepDone: - return nil, nil - default: - return nil, errors.Errorf("unknown step %d", gTask.Step) - } - - planCtx := planner.PlanCtx{ - Ctx: ctx, - TaskID: gTask.ID, - PreviousSubtaskMetas: previousSubtaskMetas, - GlobalSort: dsp.GlobalSort, - NextTaskStep: nextStep, - } - logicalPlan := &LogicalPlan{} - if err := logicalPlan.FromTaskMeta(gTask.Meta); err != nil { - return nil, err - } - physicalPlan, err := logicalPlan.ToPhysicalPlan(planCtx) - if err != nil { - return nil, err - } - metaBytes, err := physicalPlan.ToSubtaskMetas(planCtx, nextStep) - if err != nil { - return nil, err - } - logger.Info("generate subtasks", zap.Int("subtask-count", len(metaBytes))) - return metaBytes, nil -} - -// OnErrStage implements dispatcher.Extension interface. -func (dsp *ImportDispatcherExt) OnErrStage(ctx context.Context, handle dispatcher.TaskHandle, gTask *proto.Task, receiveErrs []error) ([]byte, error) { - logger := logutil.BgLogger().With( - zap.String("type", gTask.Type), - zap.Int64("task-id", gTask.ID), - zap.String("step", stepStr(gTask.Step)), - ) - logger.Info("on error stage", zap.Errors("errors", receiveErrs)) - taskMeta := &TaskMeta{} - err := json.Unmarshal(gTask.Meta, taskMeta) - if err != nil { - return nil, errors.Trace(err) - } - errStrs := make([]string, 0, len(receiveErrs)) - for _, receiveErr := range receiveErrs { - errStrs = append(errStrs, receiveErr.Error()) - } - if err = dsp.failJob(ctx, handle, gTask, taskMeta, logger, strings.Join(errStrs, "; ")); err != nil { - return nil, err - } - - gTask.Error = receiveErrs[0] - - errStr := receiveErrs[0].Error() - // do nothing if the error is resumable - if isResumableErr(errStr) { - return nil, nil - } - - if gTask.Step == StepImport { - err = rollback(ctx, handle, gTask, logger) - if err != nil { - // TODO: add error code according to spec. - gTask.Error = errors.New(errStr + ", " + err.Error()) - } - } - return nil, err -} - -// GetEligibleInstances implements dispatcher.Extension interface. -func (*ImportDispatcherExt) GetEligibleInstances(ctx context.Context, gTask *proto.Task) ([]*infosync.ServerInfo, error) { - taskMeta := &TaskMeta{} - err := json.Unmarshal(gTask.Meta, taskMeta) - if err != nil { - return nil, errors.Trace(err) - } - if len(taskMeta.EligibleInstances) > 0 { - return taskMeta.EligibleInstances, nil - } - return dispatcher.GenerateSchedulerNodes(ctx) -} - -// IsRetryableErr implements dispatcher.Extension interface. -func (*ImportDispatcherExt) IsRetryableErr(error) bool { - // TODO: check whether the error is retryable. - return false -} - -// GetNextStep implements dispatcher.Extension interface. -func (dsp *ImportDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { - switch task.Step { - case proto.StepInit: - if dsp.GlobalSort { - return StepEncodeAndSort - } - return StepImport - case StepEncodeAndSort: - return StepMergeSort - case StepMergeSort: - return StepWriteAndIngest - case StepImport, StepWriteAndIngest: - return StepPostProcess - default: - // current step must be StepPostProcess - return proto.StepDone - } -} - -func (dsp *ImportDispatcherExt) switchTiKV2NormalMode(ctx context.Context, task *proto.Task, logger *zap.Logger) { - dsp.updateCurrentTask(task) - if dsp.disableTiKVImportMode.Load() { - return - } - - dsp.mu.Lock() - defer dsp.mu.Unlock() - - pdCli, switcher, err := importer.GetTiKVModeSwitcherWithPDClient(ctx, logger) - if err != nil { - logger.Warn("get tikv mode switcher failed", zap.Error(err)) - return - } - switcher.ToNormalMode(ctx) - pdCli.Close() - - // clear it, so next task can switch TiKV mode again. - dsp.lastSwitchTime.Store(time.Time{}) -} - -func (dsp *ImportDispatcherExt) updateCurrentTask(task *proto.Task) { - if dsp.currTaskID.Swap(task.ID) != task.ID { - taskMeta := &TaskMeta{} - if err := json.Unmarshal(task.Meta, taskMeta); err == nil { - // for raftkv2, switch mode in local backend - dsp.disableTiKVImportMode.Store(taskMeta.Plan.DisableTiKVImportMode || taskMeta.Plan.IsRaftKV2) - } - } -} - -type importDispatcher struct { - *dispatcher.BaseDispatcher -} - -func newImportDispatcher(ctx context.Context, taskMgr *storage.TaskManager, - serverID string, task *proto.Task) dispatcher.Dispatcher { - metrics := metricsManager.getOrCreateMetrics(task.ID) - subCtx := metric.WithCommonMetric(ctx, metrics) - dsp := importDispatcher{ - BaseDispatcher: dispatcher.NewBaseDispatcher(subCtx, taskMgr, serverID, task), - } - return &dsp -} - -func (dsp *importDispatcher) Init() (err error) { - defer func() { - if err != nil { - // if init failed, close is not called, so we need to unregister here. - metricsManager.unregister(dsp.Task.ID) - } - }() - taskMeta := &TaskMeta{} - if err = json.Unmarshal(dsp.BaseDispatcher.Task.Meta, taskMeta); err != nil { - return errors.Annotate(err, "unmarshal task meta failed") - } - - dsp.BaseDispatcher.Extension = &ImportDispatcherExt{ - GlobalSort: taskMeta.Plan.CloudStorageURI != "", - } - return dsp.BaseDispatcher.Init() -} - -func (dsp *importDispatcher) Close() { - metricsManager.unregister(dsp.Task.ID) - dsp.BaseDispatcher.Close() -} - -// nolint:deadcode -func dropTableIndexes(ctx context.Context, handle dispatcher.TaskHandle, taskMeta *TaskMeta, logger *zap.Logger) error { - tblInfo := taskMeta.Plan.TableInfo - tableName := common.UniqueTable(taskMeta.Plan.DBName, tblInfo.Name.L) - - remainIndexes, dropIndexes := common.GetDropIndexInfos(tblInfo) - for _, idxInfo := range dropIndexes { - sqlStr := common.BuildDropIndexSQL(tableName, idxInfo) - if err := executeSQL(ctx, handle, logger, sqlStr); err != nil { - if merr, ok := errors.Cause(err).(*dmysql.MySQLError); ok { - switch merr.Number { - case errno.ErrCantDropFieldOrKey, errno.ErrDropIndexNeededInForeignKey: - remainIndexes = append(remainIndexes, idxInfo) - logger.Warn("can't drop index, skip", zap.String("index", idxInfo.Name.O), zap.Error(err)) - continue - } - } - return err - } - } - if len(remainIndexes) < len(tblInfo.Indices) { - taskMeta.Plan.TableInfo = taskMeta.Plan.TableInfo.Clone() - taskMeta.Plan.TableInfo.Indices = remainIndexes - } - return nil -} - -// nolint:deadcode -func createTableIndexes(ctx context.Context, executor storage.SessionExecutor, taskMeta *TaskMeta, logger *zap.Logger) error { - tableName := common.UniqueTable(taskMeta.Plan.DBName, taskMeta.Plan.TableInfo.Name.L) - singleSQL, multiSQLs := common.BuildAddIndexSQL(tableName, taskMeta.Plan.TableInfo, taskMeta.Plan.DesiredTableInfo) - logger.Info("build add index sql", zap.String("singleSQL", singleSQL), zap.Strings("multiSQLs", multiSQLs)) - if len(multiSQLs) == 0 { - return nil - } - - err := executeSQL(ctx, executor, logger, singleSQL) - if err == nil { - return nil - } - if !common.IsDupKeyError(err) { - // TODO: refine err msg and error code according to spec. - return errors.Errorf("Failed to create index: %v, please execute the SQL manually, sql: %s", err, singleSQL) - } - if len(multiSQLs) == 1 { - return nil - } - logger.Warn("cannot add all indexes in one statement, try to add them one by one", zap.Strings("sqls", multiSQLs), zap.Error(err)) - - for i, ddl := range multiSQLs { - err := executeSQL(ctx, executor, logger, ddl) - if err != nil && !common.IsDupKeyError(err) { - // TODO: refine err msg and error code according to spec. - return errors.Errorf("Failed to create index: %v, please execute the SQLs manually, sqls: %s", err, strings.Join(multiSQLs[i:], ";")) - } - } - return nil -} - -// TODO: return the result of sql. -func executeSQL(ctx context.Context, executor storage.SessionExecutor, logger *zap.Logger, sql string, args ...interface{}) (err error) { - logger.Info("execute sql", zap.String("sql", sql), zap.Any("args", args)) - return executor.WithNewSession(func(se sessionctx.Context) error { - _, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql, args...) - return err - }) -} - -func updateMeta(gTask *proto.Task, taskMeta *TaskMeta) error { - bs, err := json.Marshal(taskMeta) - if err != nil { - return errors.Trace(err) - } - gTask.Meta = bs - - return nil -} - -// todo: converting back and forth, we should unify struct and remove this function later. -func toChunkMap(engineCheckpoints map[int32]*checkpoints.EngineCheckpoint) map[int32][]Chunk { - chunkMap := make(map[int32][]Chunk, len(engineCheckpoints)) - for id, ecp := range engineCheckpoints { - chunkMap[id] = make([]Chunk, 0, len(ecp.Chunks)) - for _, chunkCheckpoint := range ecp.Chunks { - chunkMap[id] = append(chunkMap[id], toChunk(*chunkCheckpoint)) - } - } - return chunkMap -} - -func getStepOfEncode(globalSort bool) int64 { - if globalSort { - return StepEncodeAndSort - } - return StepImport -} - -// we will update taskMeta in place and make gTask.Meta point to the new taskMeta. -func updateResult(handle dispatcher.TaskHandle, gTask *proto.Task, taskMeta *TaskMeta, globalSort bool) error { - stepOfEncode := getStepOfEncode(globalSort) - metas, err := handle.GetPreviousSubtaskMetas(gTask.ID, stepOfEncode) - if err != nil { - return err - } - - subtaskMetas := make([]*ImportStepMeta, 0, len(metas)) - for _, bs := range metas { - var subtaskMeta ImportStepMeta - if err := json.Unmarshal(bs, &subtaskMeta); err != nil { - return errors.Trace(err) - } - subtaskMetas = append(subtaskMetas, &subtaskMeta) - } - columnSizeMap := make(map[int64]int64) - for _, subtaskMeta := range subtaskMetas { - taskMeta.Result.LoadedRowCnt += subtaskMeta.Result.LoadedRowCnt - for key, val := range subtaskMeta.Result.ColSizeMap { - columnSizeMap[key] += val - } - } - taskMeta.Result.ColSizeMap = columnSizeMap - - if globalSort { - taskMeta.Result.LoadedRowCnt, err = getLoadedRowCountOnGlobalSort(handle, gTask) - if err != nil { - return err - } - } - - return updateMeta(gTask, taskMeta) -} - -func getLoadedRowCountOnGlobalSort(handle dispatcher.TaskHandle, gTask *proto.Task) (uint64, error) { - metas, err := handle.GetPreviousSubtaskMetas(gTask.ID, StepWriteAndIngest) - if err != nil { - return 0, err - } - - var loadedRowCount uint64 - for _, bs := range metas { - var subtaskMeta WriteIngestStepMeta - if err = json.Unmarshal(bs, &subtaskMeta); err != nil { - return 0, errors.Trace(err) - } - loadedRowCount += subtaskMeta.Result.LoadedRowCnt - } - return loadedRowCount, nil -} - -func startJob(ctx context.Context, logger *zap.Logger, taskHandle dispatcher.TaskHandle, taskMeta *TaskMeta, jobStep string) error { - failpoint.Inject("syncBeforeJobStarted", func() { - TestSyncChan <- struct{}{} - <-TestSyncChan - }) - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - // we consider all errors as retryable errors, except context done. - // the errors include errors happened when communicate with PD and TiKV. - // we didn't consider system corrupt cases like system table dropped/altered. - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { - exec := se.(sqlexec.SQLExecutor) - return importer.StartJob(ctx, exec, taskMeta.JobID, jobStep) - }) - }, - ) - failpoint.Inject("syncAfterJobStarted", func() { - TestSyncChan <- struct{}{} - }) - return err -} - -func job2Step(ctx context.Context, logger *zap.Logger, taskMeta *TaskMeta, step string) error { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return err - } - // todo: use dispatcher.TaskHandle - // we might call this in scheduler later, there's no dispatcher.TaskHandle, so we use globalTaskManager here. - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, globalTaskManager.WithNewSession(func(se sessionctx.Context) error { - exec := se.(sqlexec.SQLExecutor) - return importer.Job2Step(ctx, exec, taskMeta.JobID, step) - }) - }, - ) -} - -func (dsp *ImportDispatcherExt) finishJob(ctx context.Context, logger *zap.Logger, - taskHandle dispatcher.TaskHandle, gTask *proto.Task, taskMeta *TaskMeta) error { - dsp.unregisterTask(ctx, gTask) - summary := &importer.JobSummary{ImportedRows: taskMeta.Result.LoadedRowCnt} - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { - exec := se.(sqlexec.SQLExecutor) - return importer.FinishJob(ctx, exec, taskMeta.JobID, summary) - }) - }, - ) -} - -func (dsp *ImportDispatcherExt) failJob(ctx context.Context, taskHandle dispatcher.TaskHandle, gTask *proto.Task, - taskMeta *TaskMeta, logger *zap.Logger, errorMsg string) error { - dsp.switchTiKV2NormalMode(ctx, gTask, logger) - dsp.unregisterTask(ctx, gTask) - // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes - backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) - return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, - func(ctx context.Context) (bool, error) { - return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { - exec := se.(sqlexec.SQLExecutor) - return importer.FailJob(ctx, exec, taskMeta.JobID, errorMsg) - }) - }, - ) -} - -func redactSensitiveInfo(gTask *proto.Task, taskMeta *TaskMeta) { - taskMeta.Stmt = "" - taskMeta.Plan.Path = ast.RedactURL(taskMeta.Plan.Path) - if taskMeta.Plan.CloudStorageURI != "" { - taskMeta.Plan.CloudStorageURI = ast.RedactURL(taskMeta.Plan.CloudStorageURI) - } - if err := updateMeta(gTask, taskMeta); err != nil { - // marshal failed, should not happen - logutil.BgLogger().Warn("failed to update task meta", zap.Error(err)) - } -} - -// isResumableErr checks whether it's possible to rely on checkpoint to re-import data after the error has been fixed. -func isResumableErr(string) bool { - // TODO: add more cases - return false -} - -func rollback(ctx context.Context, handle dispatcher.TaskHandle, gTask *proto.Task, logger *zap.Logger) (err error) { - taskMeta := &TaskMeta{} - err = json.Unmarshal(gTask.Meta, taskMeta) - if err != nil { - return errors.Trace(err) - } - - logger.Info("rollback") - - // // TODO: create table indexes depends on the option. - // // create table indexes even if the rollback is failed. - // defer func() { - // err2 := createTableIndexes(ctx, handle, taskMeta, logger) - // err = multierr.Append(err, err2) - // }() - - tableName := common.UniqueTable(taskMeta.Plan.DBName, taskMeta.Plan.TableInfo.Name.L) - // truncate the table - return executeSQL(ctx, handle, logger, "TRUNCATE "+tableName) -} - -func stepStr(step int64) string { - switch step { - case proto.StepInit: - return "init" - case StepImport: - return "import" - case StepPostProcess: - return "post-process" - case StepEncodeAndSort: - return "encode&sort" - case StepMergeSort: - return "merge-sort" - case StepWriteAndIngest: - return "write&ingest" - case proto.StepDone: - return "done" - default: - return "unknown" - } -} - -func init() { - dispatcher.RegisterDispatcherFactory(proto.ImportInto, newImportDispatcher) -} diff --git a/disttask/importinto/dispatcher_test.go b/disttask/importinto/dispatcher_test.go deleted file mode 100644 index 9d5c9dcee7102..0000000000000 --- a/disttask/importinto/dispatcher_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" -) - -type importIntoSuite struct { - suite.Suite -} - -func TestImportInto(t *testing.T) { - suite.Run(t, &importIntoSuite{}) -} - -func (s *importIntoSuite) enableFailPoint(path, term string) { - require.NoError(s.T(), failpoint.Enable(path, term)) - s.T().Cleanup(func() { - _ = failpoint.Disable(path) - }) -} - -func (s *importIntoSuite) TestDispatcherGetEligibleInstances() { - makeFailpointRes := func(v interface{}) string { - bytes, err := json.Marshal(v) - s.NoError(err) - return fmt.Sprintf("return(`%s`)", string(bytes)) - } - uuids := []string{"ddl_id_1", "ddl_id_2"} - serverInfoMap := map[string]*infosync.ServerInfo{ - uuids[0]: { - ID: uuids[0], - }, - uuids[1]: { - ID: uuids[1], - }, - } - mockedAllServerInfos := makeFailpointRes(serverInfoMap) - - dsp := ImportDispatcherExt{} - gTask := &proto.Task{Meta: []byte("{}")} - ctx := context.WithValue(context.Background(), "etcd", true) - s.enableFailPoint("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo", mockedAllServerInfos) - eligibleInstances, err := dsp.GetEligibleInstances(ctx, gTask) - s.NoError(err) - // order of slice is not stable, change to map - resultMap := map[string]*infosync.ServerInfo{} - for _, ins := range eligibleInstances { - resultMap[ins.ID] = ins - } - s.Equal(serverInfoMap, resultMap) - - gTask.Meta = []byte(`{"EligibleInstances":[{"ip": "1.1.1.1", "listening_port": 4000}]}`) - eligibleInstances, err = dsp.GetEligibleInstances(ctx, gTask) - s.NoError(err) - s.Equal([]*infosync.ServerInfo{{IP: "1.1.1.1", Port: 4000}}, eligibleInstances) -} - -func (s *importIntoSuite) TestUpdateCurrentTask() { - taskMeta := TaskMeta{ - Plan: importer.Plan{ - DisableTiKVImportMode: true, - }, - } - bs, err := json.Marshal(taskMeta) - require.NoError(s.T(), err) - - dsp := ImportDispatcherExt{} - require.Equal(s.T(), int64(0), dsp.currTaskID.Load()) - require.False(s.T(), dsp.disableTiKVImportMode.Load()) - - dsp.updateCurrentTask(&proto.Task{ - ID: 1, - Meta: bs, - }) - require.Equal(s.T(), int64(1), dsp.currTaskID.Load()) - require.True(s.T(), dsp.disableTiKVImportMode.Load()) - - dsp.updateCurrentTask(&proto.Task{ - ID: 1, - Meta: bs, - }) - require.Equal(s.T(), int64(1), dsp.currTaskID.Load()) - require.True(s.T(), dsp.disableTiKVImportMode.Load()) -} - -func (s *importIntoSuite) TestDispatcherInit() { - meta := TaskMeta{ - Plan: importer.Plan{ - CloudStorageURI: "", - }, - } - bytes, err := json.Marshal(meta) - s.NoError(err) - dsp := importDispatcher{ - BaseDispatcher: &dispatcher.BaseDispatcher{ - Task: &proto.Task{ - Meta: bytes, - }, - }, - } - s.NoError(dsp.Init()) - s.False(dsp.Extension.(*ImportDispatcherExt).GlobalSort) - - meta.Plan.CloudStorageURI = "s3://test" - bytes, err = json.Marshal(meta) - s.NoError(err) - dsp = importDispatcher{ - BaseDispatcher: &dispatcher.BaseDispatcher{ - Task: &proto.Task{ - Meta: bytes, - }, - }, - } - s.NoError(dsp.Init()) - s.True(dsp.Extension.(*ImportDispatcherExt).GlobalSort) -} - -func (s *importIntoSuite) TestGetNextStep() { - task := &proto.Task{ - Step: proto.StepInit, - } - ext := &ImportDispatcherExt{} - for _, nextStep := range []int64{StepImport, StepPostProcess, proto.StepDone} { - s.Equal(nextStep, ext.GetNextStep(nil, task)) - task.Step = nextStep - } - - task.Step = proto.StepInit - ext = &ImportDispatcherExt{GlobalSort: true} - for _, nextStep := range []int64{StepEncodeAndSort, StepMergeSort, - StepWriteAndIngest, StepPostProcess, proto.StepDone} { - s.Equal(nextStep, ext.GetNextStep(nil, task)) - task.Step = nextStep - } -} - -func (s *importIntoSuite) TestStr() { - s.Equal("init", stepStr(proto.StepInit)) - s.Equal("import", stepStr(StepImport)) - s.Equal("post-process", stepStr(StepPostProcess)) - s.Equal("merge-sort", stepStr(StepMergeSort)) - s.Equal("encode&sort", stepStr(StepEncodeAndSort)) - s.Equal("write&ingest", stepStr(StepWriteAndIngest)) - s.Equal("done", stepStr(proto.StepDone)) - s.Equal("unknown", stepStr(111)) -} - -func (s *importIntoSuite) TestGetStepOfEncode() { - s.Equal(StepImport, getStepOfEncode(false)) - s.Equal(StepEncodeAndSort, getStepOfEncode(true)) -} - -func TestIsImporting2TiKV(t *testing.T) { - ext := &ImportDispatcherExt{} - require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepEncodeAndSort})) - require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepMergeSort})) - require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepPostProcess})) - require.True(t, ext.isImporting2TiKV(&proto.Task{Step: StepImport})) - require.True(t, ext.isImporting2TiKV(&proto.Task{Step: StepWriteAndIngest})) -} diff --git a/disttask/importinto/job.go b/disttask/importinto/job.go deleted file mode 100644 index 7691e969fbc88..0000000000000 --- a/disttask/importinto/job.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/google/uuid" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" - "github.com/pingcap/tidb/disttask/framework/handle" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// DistImporter is a JobImporter for distributed IMPORT INTO. -type DistImporter struct { - *importer.JobImportParam - plan *importer.Plan - stmt string - logger *zap.Logger - // the instance to import data, used for single-node import, nil means import data on all instances. - instance *infosync.ServerInfo - // the files to import, when import from server file, we need to pass those file to the framework. - chunkMap map[int32][]Chunk - sourceFileSize int64 - // only set after submit task - jobID int64 - taskID int64 -} - -// NewDistImporter creates a new DistImporter. -func NewDistImporter(param *importer.JobImportParam, plan *importer.Plan, stmt string, sourceFileSize int64) (*DistImporter, error) { - return &DistImporter{ - JobImportParam: param, - plan: plan, - stmt: stmt, - logger: logutil.BgLogger(), - sourceFileSize: sourceFileSize, - }, nil -} - -// NewDistImporterCurrNode creates a new DistImporter to import data on current node. -func NewDistImporterCurrNode(param *importer.JobImportParam, plan *importer.Plan, stmt string, sourceFileSize int64) (*DistImporter, error) { - serverInfo, err := infosync.GetServerInfo() - if err != nil { - return nil, err - } - return &DistImporter{ - JobImportParam: param, - plan: plan, - stmt: stmt, - logger: logutil.BgLogger(), - instance: serverInfo, - sourceFileSize: sourceFileSize, - }, nil -} - -// NewDistImporterServerFile creates a new DistImporter to import given files on current node. -// we also run import on current node. -// todo: merge all 3 ctor into one. -func NewDistImporterServerFile(param *importer.JobImportParam, plan *importer.Plan, stmt string, ecp map[int32]*checkpoints.EngineCheckpoint, sourceFileSize int64) (*DistImporter, error) { - distImporter, err := NewDistImporterCurrNode(param, plan, stmt, sourceFileSize) - if err != nil { - return nil, err - } - distImporter.chunkMap = toChunkMap(ecp) - return distImporter, nil -} - -// Param implements JobImporter.Param. -func (ti *DistImporter) Param() *importer.JobImportParam { - return ti.JobImportParam -} - -// Import implements JobImporter.Import. -func (*DistImporter) Import() { - // todo: remove it -} - -// ImportTask import task. -func (ti *DistImporter) ImportTask(task *proto.Task) { - ti.logger.Info("start distribute IMPORT INTO") - ti.Group.Go(func() error { - defer close(ti.Done) - // task is run using distribute framework, so we only wait for the task to finish. - return handle.WaitGlobalTask(ti.GroupCtx, task) - }) -} - -// Result implements JobImporter.Result. -func (ti *DistImporter) Result() importer.JobImportResult { - var result importer.JobImportResult - taskMeta, err := getTaskMeta(ti.jobID) - if err != nil { - return result - } - - return importer.JobImportResult{ - Affected: taskMeta.Result.LoadedRowCnt, - ColSizeMap: taskMeta.Result.ColSizeMap, - } -} - -// Close implements the io.Closer interface. -func (*DistImporter) Close() error { - return nil -} - -// SubmitTask submits a task to the distribute framework. -func (ti *DistImporter) SubmitTask(ctx context.Context) (int64, *proto.Task, error) { - var instances []*infosync.ServerInfo - if ti.instance != nil { - instances = append(instances, ti.instance) - } - // we use globalTaskManager to submit task, user might not have the privilege to system tables. - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return 0, nil, err - } - - var jobID, taskID int64 - plan := ti.plan - if err = globalTaskManager.WithNewTxn(ctx, func(se sessionctx.Context) error { - var err2 error - exec := se.(sqlexec.SQLExecutor) - // If 2 client try to execute IMPORT INTO concurrently, there's chance that both of them will pass the check. - // We can enforce ONLY one import job running by: - // - using LOCK TABLES, but it requires enable-table-lock=true, it's not enabled by default. - // - add a key to PD as a distributed lock, but it's a little complex, and we might support job queuing later. - // So we only add this simple soft check here and doc it. - activeJobCnt, err2 := importer.GetActiveJobCnt(ctx, exec) - if err2 != nil { - return err2 - } - if activeJobCnt > 0 { - return exeerrors.ErrLoadDataPreCheckFailed.FastGenByArgs("there's pending or running jobs") - } - jobID, err2 = importer.CreateJob(ctx, exec, plan.DBName, plan.TableInfo.Name.L, plan.TableInfo.ID, - plan.User, plan.Parameters, ti.sourceFileSize) - if err2 != nil { - return err2 - } - - // TODO: use planner.Run to run the logical plan - // now creating import job and submitting distributed task should be in the same transaction. - logicalPlan := &LogicalPlan{ - JobID: jobID, - Plan: *plan, - Stmt: ti.stmt, - EligibleInstances: instances, - ChunkMap: ti.chunkMap, - } - planCtx := planner.PlanCtx{ - Ctx: ctx, - SessionCtx: se, - TaskKey: TaskKey(jobID), - TaskType: proto.ImportInto, - ThreadCnt: int(plan.ThreadCnt), - } - p := planner.NewPlanner() - taskID, err2 = p.Run(planCtx, logicalPlan) - if err2 != nil { - return err2 - } - return nil - }); err != nil { - return 0, nil, err - } - - globalTask, err := globalTaskManager.GetGlobalTaskByID(taskID) - if err != nil { - return 0, nil, err - } - if globalTask == nil { - return 0, nil, errors.Errorf("cannot find global task with ID %d", taskID) - } - // update logger with task id. - ti.jobID = jobID - ti.taskID = taskID - ti.logger = ti.logger.With(zap.Int64("task-id", globalTask.ID)) - - ti.logger.Info("job submitted to global task queue", - zap.Int64("job-id", jobID), zap.Int64("thread-cnt", plan.ThreadCnt)) - - return jobID, globalTask, nil -} - -func (*DistImporter) taskKey() string { - // task key is meaningless to IMPORT INTO, so we use a random uuid. - return fmt.Sprintf("%s/%s", proto.ImportInto, uuid.New().String()) -} - -// JobID returns the job id. -func (ti *DistImporter) JobID() int64 { - return ti.jobID -} - -func getTaskMeta(jobID int64) (*TaskMeta, error) { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return nil, err - } - taskKey := TaskKey(jobID) - globalTask, err := globalTaskManager.GetGlobalTaskByKey(taskKey) - if err != nil { - return nil, err - } - if globalTask == nil { - return nil, errors.Errorf("cannot find global task with key %s", taskKey) - } - var taskMeta TaskMeta - if err := json.Unmarshal(globalTask.Meta, &taskMeta); err != nil { - return nil, errors.Trace(err) - } - return &taskMeta, nil -} - -// GetTaskImportedRows gets the number of imported rows of a job. -// Note: for finished job, we can get the number of imported rows from task meta. -func GetTaskImportedRows(jobID int64) (uint64, error) { - globalTaskManager, err := storage.GetTaskManager() - if err != nil { - return 0, err - } - taskKey := TaskKey(jobID) - task, err := globalTaskManager.GetGlobalTaskByKey(taskKey) - if err != nil { - return 0, err - } - if task == nil { - return 0, errors.Errorf("cannot find global task with key %s", taskKey) - } - taskMeta := TaskMeta{} - if err = json.Unmarshal(task.Meta, &taskMeta); err != nil { - return 0, errors.Trace(err) - } - var importedRows uint64 - if taskMeta.Plan.CloudStorageURI == "" { - subtasks, err := globalTaskManager.GetSubtasksForImportInto(task.ID, StepImport) - if err != nil { - return 0, err - } - for _, subtask := range subtasks { - var subtaskMeta ImportStepMeta - if err2 := json.Unmarshal(subtask.Meta, &subtaskMeta); err2 != nil { - return 0, errors.Trace(err2) - } - importedRows += subtaskMeta.Result.LoadedRowCnt - } - } else { - subtasks, err := globalTaskManager.GetSubtasksForImportInto(task.ID, StepWriteAndIngest) - if err != nil { - return 0, err - } - for _, subtask := range subtasks { - var subtaskMeta WriteIngestStepMeta - if err2 := json.Unmarshal(subtask.Meta, &subtaskMeta); err2 != nil { - return 0, errors.Trace(err2) - } - importedRows += subtaskMeta.Result.LoadedRowCnt - } - } - return importedRows, nil -} - -// TaskKey returns the task key for a job. -func TaskKey(jobID int64) string { - return fmt.Sprintf("%s/%d", proto.ImportInto, jobID) -} diff --git a/disttask/importinto/metrics.go b/disttask/importinto/metrics.go deleted file mode 100644 index d2ba554722362..0000000000000 --- a/disttask/importinto/metrics.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "strconv" - "sync" - - "github.com/pingcap/tidb/br/pkg/lightning/metric" - "github.com/pingcap/tidb/disttask/framework/proto" - tidbmetrics "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/promutil" - "github.com/prometheus/client_golang/prometheus" -) - -type taskMetrics struct { - metrics *metric.Common - counter int -} - -// taskMetricManager manages the metrics of IMPORT INTO tasks. -// we have a set of metrics for each task, with different task_id const label. -// metrics is passed by context value to avoid passing parameters everywhere. -// both dispatcher and scheduler might use it, to avoid registered again, -// we add a manager to manage lifecycle of metrics for tasks. -type taskMetricManager struct { - sync.RWMutex - metricsMap map[int64]*taskMetrics -} - -var metricsManager = &taskMetricManager{ - metricsMap: make(map[int64]*taskMetrics), -} - -// getOrCreateMetrics gets or creates the metrics for the task. -// if the metrics has been created, the counter will be increased. -func (m *taskMetricManager) getOrCreateMetrics(taskID int64) *metric.Common { - m.Lock() - defer m.Unlock() - tm, ok := m.metricsMap[taskID] - if !ok { - metrics := tidbmetrics.GetRegisteredImportMetrics(promutil.NewDefaultFactory(), - prometheus.Labels{ - proto.TaskIDLabelName: strconv.FormatInt(taskID, 10), - }) - tm = &taskMetrics{ - metrics: metrics, - } - m.metricsMap[taskID] = tm - } - - tm.counter++ - - return tm.metrics -} - -// unregister count down the metrics for the task. -// if the counter is 0, the metrics will be unregistered. -func (m *taskMetricManager) unregister(taskID int64) { - m.Lock() - defer m.Unlock() - if tm, ok := m.metricsMap[taskID]; ok { - tm.counter-- - if tm.counter == 0 { - tidbmetrics.UnregisterImportMetrics(tm.metrics) - delete(m.metricsMap, taskID) - } - } -} diff --git a/disttask/importinto/mock/BUILD.bazel b/disttask/importinto/mock/BUILD.bazel deleted file mode 100644 index 902ed4332b31a..0000000000000 --- a/disttask/importinto/mock/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mock", - srcs = ["import_mock.go"], - importpath = "github.com/pingcap/tidb/disttask/importinto/mock", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/backend", - "@org_uber_go_mock//gomock", - ], -) diff --git a/disttask/importinto/planner.go b/disttask/importinto/planner.go deleted file mode 100644 index b88fbd27a7883..0000000000000 --- a/disttask/importinto/planner.go +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/hex" - "encoding/json" - "math" - "strconv" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/br/pkg/lightning/config" - verify "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -var ( - _ planner.LogicalPlan = &LogicalPlan{} - _ planner.PipelineSpec = &ImportSpec{} - _ planner.PipelineSpec = &PostProcessSpec{} -) - -// LogicalPlan represents a logical plan for import into. -type LogicalPlan struct { - JobID int64 - Plan importer.Plan - Stmt string - EligibleInstances []*infosync.ServerInfo - ChunkMap map[int32][]Chunk -} - -// ToTaskMeta converts the logical plan to task meta. -func (p *LogicalPlan) ToTaskMeta() ([]byte, error) { - taskMeta := TaskMeta{ - JobID: p.JobID, - Plan: p.Plan, - Stmt: p.Stmt, - EligibleInstances: p.EligibleInstances, - ChunkMap: p.ChunkMap, - } - return json.Marshal(taskMeta) -} - -// FromTaskMeta converts the task meta to logical plan. -func (p *LogicalPlan) FromTaskMeta(bs []byte) error { - var taskMeta TaskMeta - if err := json.Unmarshal(bs, &taskMeta); err != nil { - return errors.Trace(err) - } - p.JobID = taskMeta.JobID - p.Plan = taskMeta.Plan - p.Stmt = taskMeta.Stmt - p.EligibleInstances = taskMeta.EligibleInstances - p.ChunkMap = taskMeta.ChunkMap - return nil -} - -// ToPhysicalPlan converts the logical plan to physical plan. -func (p *LogicalPlan) ToPhysicalPlan(planCtx planner.PlanCtx) (*planner.PhysicalPlan, error) { - physicalPlan := &planner.PhysicalPlan{} - inputLinks := make([]planner.LinkSpec, 0) - addSpecs := func(specs []planner.PipelineSpec) { - for i, spec := range specs { - physicalPlan.AddProcessor(planner.ProcessorSpec{ - ID: i, - Pipeline: spec, - Output: planner.OutputSpec{ - Links: []planner.LinkSpec{ - { - ProcessorID: len(specs), - }, - }, - }, - Step: planCtx.NextTaskStep, - }) - inputLinks = append(inputLinks, planner.LinkSpec{ - ProcessorID: i, - }) - } - } - // physical plan only needs to be generated once. - // However, our current implementation requires generating it for each step. - // we only generate needed plans for the next step. - switch planCtx.NextTaskStep { - case StepImport, StepEncodeAndSort: - specs, err := generateImportSpecs(planCtx.Ctx, p) - if err != nil { - return nil, err - } - - addSpecs(specs) - case StepMergeSort: - specs, err := generateMergeSortSpecs(planCtx) - if err != nil { - return nil, err - } - - addSpecs(specs) - case StepWriteAndIngest: - specs, err := generateWriteIngestSpecs(planCtx, p) - if err != nil { - return nil, err - } - - addSpecs(specs) - case StepPostProcess: - physicalPlan.AddProcessor(planner.ProcessorSpec{ - ID: len(inputLinks), - Input: planner.InputSpec{ - ColumnTypes: []byte{ - // Checksum_crc64_xor, Total_kvs, Total_bytes, ReadRowCnt, LoadedRowCnt, ColSizeMap - mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeJSON, - }, - Links: inputLinks, - }, - Pipeline: &PostProcessSpec{ - Schema: p.Plan.DBName, - Table: p.Plan.TableInfo.Name.L, - }, - Step: planCtx.NextTaskStep, - }) - } - - return physicalPlan, nil -} - -// ImportSpec is the specification of an import pipeline. -type ImportSpec struct { - ID int32 - Plan importer.Plan - Chunks []Chunk -} - -// ToSubtaskMeta converts the import spec to subtask meta. -func (s *ImportSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { - importStepMeta := ImportStepMeta{ - ID: s.ID, - Chunks: s.Chunks, - } - return json.Marshal(importStepMeta) -} - -// WriteIngestSpec is the specification of a write-ingest pipeline. -type WriteIngestSpec struct { - *WriteIngestStepMeta -} - -// ToSubtaskMeta converts the write-ingest spec to subtask meta. -func (s *WriteIngestSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { - return json.Marshal(s.WriteIngestStepMeta) -} - -// MergeSortSpec is the specification of a merge-sort pipeline. -type MergeSortSpec struct { - *MergeSortStepMeta -} - -// ToSubtaskMeta converts the merge-sort spec to subtask meta. -func (s *MergeSortSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { - return json.Marshal(s.MergeSortStepMeta) -} - -// PostProcessSpec is the specification of a post process pipeline. -type PostProcessSpec struct { - // for checksum request - Schema string - Table string -} - -// ToSubtaskMeta converts the post process spec to subtask meta. -func (*PostProcessSpec) ToSubtaskMeta(planCtx planner.PlanCtx) ([]byte, error) { - encodeStep := getStepOfEncode(planCtx.GlobalSort) - subtaskMetas := make([]*ImportStepMeta, 0, len(planCtx.PreviousSubtaskMetas)) - for _, bs := range planCtx.PreviousSubtaskMetas[encodeStep] { - var subtaskMeta ImportStepMeta - if err := json.Unmarshal(bs, &subtaskMeta); err != nil { - return nil, errors.Trace(err) - } - subtaskMetas = append(subtaskMetas, &subtaskMeta) - } - var localChecksum verify.KVChecksum - maxIDs := make(map[autoid.AllocatorType]int64, 3) - for _, subtaskMeta := range subtaskMetas { - checksum := verify.MakeKVChecksum(subtaskMeta.Checksum.Size, subtaskMeta.Checksum.KVs, subtaskMeta.Checksum.Sum) - localChecksum.Add(&checksum) - - for key, val := range subtaskMeta.MaxIDs { - if maxIDs[key] < val { - maxIDs[key] = val - } - } - } - postProcessStepMeta := &PostProcessStepMeta{ - Checksum: Checksum{ - Size: localChecksum.SumSize(), - KVs: localChecksum.SumKVS(), - Sum: localChecksum.Sum(), - }, - MaxIDs: maxIDs, - } - return json.Marshal(postProcessStepMeta) -} - -func buildControllerForPlan(p *LogicalPlan) (*importer.LoadDataController, error) { - return buildController(&p.Plan, p.Stmt) -} - -func buildController(plan *importer.Plan, stmt string) (*importer.LoadDataController, error) { - idAlloc := kv.NewPanickingAllocators(0) - tbl, err := tables.TableFromMeta(idAlloc, plan.TableInfo) - if err != nil { - return nil, err - } - - astArgs, err := importer.ASTArgsFromStmt(stmt) - if err != nil { - return nil, err - } - controller, err := importer.NewLoadDataController(plan, tbl, astArgs) - if err != nil { - return nil, err - } - return controller, nil -} - -func generateImportSpecs(ctx context.Context, p *LogicalPlan) ([]planner.PipelineSpec, error) { - var chunkMap map[int32][]Chunk - if len(p.ChunkMap) > 0 { - chunkMap = p.ChunkMap - } else { - controller, err2 := buildControllerForPlan(p) - if err2 != nil { - return nil, err2 - } - if err2 = controller.InitDataFiles(ctx); err2 != nil { - return nil, err2 - } - - engineCheckpoints, err2 := controller.PopulateChunks(ctx) - if err2 != nil { - return nil, err2 - } - chunkMap = toChunkMap(engineCheckpoints) - } - importSpecs := make([]planner.PipelineSpec, 0, len(chunkMap)) - for id := range chunkMap { - if id == common.IndexEngineID { - continue - } - importSpec := &ImportSpec{ - ID: id, - Plan: p.Plan, - Chunks: chunkMap[id], - } - importSpecs = append(importSpecs, importSpec) - } - return importSpecs, nil -} - -func skipMergeSort(kvGroup string, stats []external.MultipleFilesStat) bool { - failpoint.Inject("forceMergeSort", func(val failpoint.Value) { - in := val.(string) - if in == kvGroup || in == "*" { - failpoint.Return(false) - } - }) - return external.GetMaxOverlappingTotal(stats) <= external.MergeSortOverlapThreshold -} - -func generateMergeSortSpecs(planCtx planner.PlanCtx) ([]planner.PipelineSpec, error) { - step := external.MergeSortFileCountStep - result := make([]planner.PipelineSpec, 0, 16) - kvMetas, err := getSortedKVMetasOfEncodeStep(planCtx.PreviousSubtaskMetas[StepEncodeAndSort]) - if err != nil { - return nil, err - } - for kvGroup, kvMeta := range kvMetas { - length := len(kvMeta.DataFiles) - if skipMergeSort(kvGroup, kvMeta.MultipleFilesStats) { - logutil.Logger(planCtx.Ctx).Info("skip merge sort for kv group", - zap.Int64("task-id", planCtx.TaskID), - zap.String("kv-group", kvGroup)) - continue - } - for start := 0; start < length; start += step { - end := start + step - if end > length { - end = length - } - result = append(result, &MergeSortSpec{ - MergeSortStepMeta: &MergeSortStepMeta{ - KVGroup: kvGroup, - DataFiles: kvMeta.DataFiles[start:end], - }, - }) - } - } - return result, nil -} - -func generateWriteIngestSpecs(planCtx planner.PlanCtx, p *LogicalPlan) ([]planner.PipelineSpec, error) { - ctx := planCtx.Ctx - controller, err2 := buildControllerForPlan(p) - if err2 != nil { - return nil, err2 - } - if err2 = controller.InitDataStore(ctx); err2 != nil { - return nil, err2 - } - // kvMetas contains data kv meta and all index kv metas. - // each kvMeta will be split into multiple range group individually, - // i.e. data and index kv will NOT be in the same subtask. - kvMetas, err := getSortedKVMetasForIngest(planCtx) - if err != nil { - return nil, err - } - failpoint.Inject("mockWriteIngestSpecs", func() { - failpoint.Return([]planner.PipelineSpec{ - &WriteIngestSpec{ - WriteIngestStepMeta: &WriteIngestStepMeta{ - KVGroup: dataKVGroup, - }, - }, - &WriteIngestSpec{ - WriteIngestStepMeta: &WriteIngestStepMeta{ - KVGroup: "1", - }, - }, - }, nil) - }) - specs := make([]planner.PipelineSpec, 0, 16) - for kvGroup, kvMeta := range kvMetas { - splitter, err1 := getRangeSplitter(ctx, controller.GlobalSortStore, kvMeta) - if err1 != nil { - return nil, err1 - } - - err1 = func() error { - defer func() { - err2 := splitter.Close() - if err2 != nil { - logutil.Logger(ctx).Warn("close range splitter failed", zap.Error(err2)) - } - }() - startKey := tidbkv.Key(kvMeta.MinKey) - var endKey tidbkv.Key - for { - endKeyOfGroup, dataFiles, statFiles, rangeSplitKeys, err2 := splitter.SplitOneRangesGroup() - if err2 != nil { - return err2 - } - if len(endKeyOfGroup) == 0 { - endKey = tidbkv.Key(kvMeta.MaxKey).Next() - } else { - endKey = tidbkv.Key(endKeyOfGroup).Clone() - } - logutil.Logger(ctx).Info("kv range as subtask", - zap.String("startKey", hex.EncodeToString(startKey)), - zap.String("endKey", hex.EncodeToString(endKey))) - if startKey.Cmp(endKey) >= 0 { - return errors.Errorf("invalid kv range, startKey: %s, endKey: %s", - hex.EncodeToString(startKey), hex.EncodeToString(endKey)) - } - // each subtask will write and ingest one range group - m := &WriteIngestStepMeta{ - KVGroup: kvGroup, - SortedKVMeta: external.SortedKVMeta{ - MinKey: startKey, - MaxKey: endKey, - DataFiles: dataFiles, - StatFiles: statFiles, - // this is actually an estimate, we don't know the exact size of the data - TotalKVSize: uint64(config.DefaultBatchSize), - }, - RangeSplitKeys: rangeSplitKeys, - RangeSplitSize: splitter.GetRangeSplitSize(), - } - specs = append(specs, &WriteIngestSpec{m}) - - startKey = endKey - if len(endKeyOfGroup) == 0 { - break - } - } - return nil - }() - if err1 != nil { - return nil, err1 - } - } - return specs, nil -} - -func getSortedKVMetasOfEncodeStep(subTaskMetas [][]byte) (map[string]*external.SortedKVMeta, error) { - dataKVMeta := &external.SortedKVMeta{} - indexKVMetas := make(map[int64]*external.SortedKVMeta) - for _, subTaskMeta := range subTaskMetas { - var stepMeta ImportStepMeta - err := json.Unmarshal(subTaskMeta, &stepMeta) - if err != nil { - return nil, errors.Trace(err) - } - dataKVMeta.Merge(stepMeta.SortedDataMeta) - for indexID, sortedIndexMeta := range stepMeta.SortedIndexMetas { - if item, ok := indexKVMetas[indexID]; !ok { - indexKVMetas[indexID] = sortedIndexMeta - } else { - item.Merge(sortedIndexMeta) - } - } - } - res := make(map[string]*external.SortedKVMeta, 1+len(indexKVMetas)) - res[dataKVGroup] = dataKVMeta - for indexID, item := range indexKVMetas { - res[strconv.Itoa(int(indexID))] = item - } - return res, nil -} - -func getSortedKVMetasOfMergeStep(subTaskMetas [][]byte) (map[string]*external.SortedKVMeta, error) { - result := make(map[string]*external.SortedKVMeta, len(subTaskMetas)) - for _, subTaskMeta := range subTaskMetas { - var stepMeta MergeSortStepMeta - err := json.Unmarshal(subTaskMeta, &stepMeta) - if err != nil { - return nil, errors.Trace(err) - } - meta, ok := result[stepMeta.KVGroup] - if !ok { - result[stepMeta.KVGroup] = &stepMeta.SortedKVMeta - continue - } - meta.Merge(&stepMeta.SortedKVMeta) - } - return result, nil -} - -func getSortedKVMetasForIngest(planCtx planner.PlanCtx) (map[string]*external.SortedKVMeta, error) { - kvMetasOfMergeSort, err := getSortedKVMetasOfMergeStep(planCtx.PreviousSubtaskMetas[StepMergeSort]) - if err != nil { - return nil, err - } - kvMetasOfEncodeStep, err := getSortedKVMetasOfEncodeStep(planCtx.PreviousSubtaskMetas[StepEncodeAndSort]) - if err != nil { - return nil, err - } - for kvGroup, kvMeta := range kvMetasOfEncodeStep { - // only part of kv files are merge sorted. we need to merge kv metas that - // are not merged into the kvMetasOfMergeSort. - if skipMergeSort(kvGroup, kvMeta.MultipleFilesStats) { - if _, ok := kvMetasOfMergeSort[kvGroup]; ok { - // this should not happen, because we only generate merge sort - // subtasks for those kv groups with MaxOverlappingTotal > MergeSortOverlapThreshold - logutil.Logger(planCtx.Ctx).Error("kv group of encode step conflict with merge sort step") - return nil, errors.New("kv group of encode step conflict with merge sort step") - } - kvMetasOfMergeSort[kvGroup] = kvMeta - } - } - return kvMetasOfMergeSort, nil -} - -func getRangeSplitter(ctx context.Context, store storage.ExternalStorage, kvMeta *external.SortedKVMeta) ( - *external.RangeSplitter, error) { - regionSplitSize, regionSplitKeys, err := importer.GetRegionSplitSizeKeys(ctx) - if err != nil { - logutil.Logger(ctx).Warn("fail to get region split size and keys", zap.Error(err)) - } - regionSplitSize = max(regionSplitSize, int64(config.SplitRegionSize)) - regionSplitKeys = max(regionSplitKeys, int64(config.SplitRegionKeys)) - logutil.Logger(ctx).Info("split kv range with split size and keys", - zap.Int64("region-split-size", regionSplitSize), - zap.Int64("region-split-keys", regionSplitKeys)) - - return external.NewRangeSplitter( - ctx, kvMeta.DataFiles, kvMeta.StatFiles, store, - int64(config.DefaultBatchSize), int64(math.MaxInt64), - regionSplitSize, regionSplitKeys, - ) -} diff --git a/disttask/importinto/planner_test.go b/disttask/importinto/planner_test.go deleted file mode 100644 index 95655b8d794bd..0000000000000 --- a/disttask/importinto/planner_test.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/json" - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/disttask/framework/planner" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/stretchr/testify/require" -) - -func TestLogicalPlan(t *testing.T) { - logicalPlan := &LogicalPlan{ - JobID: 1, - Plan: importer.Plan{}, - Stmt: `IMPORT INTO db.tb FROM 'gs://test-load/*.csv?endpoint=xxx'`, - EligibleInstances: []*infosync.ServerInfo{{ID: "1"}}, - ChunkMap: map[int32][]Chunk{1: {{Path: "gs://test-load/1.csv"}}}, - } - bs, err := logicalPlan.ToTaskMeta() - require.NoError(t, err) - plan := &LogicalPlan{} - require.NoError(t, plan.FromTaskMeta(bs)) - require.Equal(t, logicalPlan, plan) -} - -func TestToPhysicalPlan(t *testing.T) { - chunkID := int32(1) - logicalPlan := &LogicalPlan{ - JobID: 1, - Plan: importer.Plan{ - DBName: "db", - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("tb"), - }, - }, - Stmt: `IMPORT INTO db.tb FROM 'gs://test-load/*.csv?endpoint=xxx'`, - EligibleInstances: []*infosync.ServerInfo{{ID: "1"}}, - ChunkMap: map[int32][]Chunk{chunkID: {{Path: "gs://test-load/1.csv"}}}, - } - planCtx := planner.PlanCtx{ - NextTaskStep: StepImport, - } - physicalPlan, err := logicalPlan.ToPhysicalPlan(planCtx) - require.NoError(t, err) - plan := &planner.PhysicalPlan{ - Processors: []planner.ProcessorSpec{ - { - ID: 0, - Pipeline: &ImportSpec{ - ID: chunkID, - Plan: logicalPlan.Plan, - Chunks: logicalPlan.ChunkMap[chunkID], - }, - Output: planner.OutputSpec{ - Links: []planner.LinkSpec{ - { - ProcessorID: 1, - }, - }, - }, - Step: StepImport, - }, - }, - } - require.Equal(t, plan, physicalPlan) - - subtaskMetas1, err := physicalPlan.ToSubtaskMetas(planCtx, StepImport) - require.NoError(t, err) - subtaskMeta1 := ImportStepMeta{ - ID: chunkID, - Chunks: logicalPlan.ChunkMap[chunkID], - } - bs, err := json.Marshal(subtaskMeta1) - require.NoError(t, err) - require.Equal(t, [][]byte{bs}, subtaskMetas1) - - subtaskMeta1.Checksum = Checksum{Size: 1, KVs: 2, Sum: 3} - bs, err = json.Marshal(subtaskMeta1) - require.NoError(t, err) - planCtx = planner.PlanCtx{ - NextTaskStep: StepPostProcess, - } - physicalPlan, err = logicalPlan.ToPhysicalPlan(planCtx) - require.NoError(t, err) - subtaskMetas2, err := physicalPlan.ToSubtaskMetas(planner.PlanCtx{ - PreviousSubtaskMetas: map[int64][][]byte{ - StepImport: {bs}, - }, - }, StepPostProcess) - require.NoError(t, err) - subtaskMeta2 := PostProcessStepMeta{ - Checksum: Checksum{Size: 1, KVs: 2, Sum: 3}, - MaxIDs: map[autoid.AllocatorType]int64{}, - } - bs, err = json.Marshal(subtaskMeta2) - require.NoError(t, err) - require.Equal(t, [][]byte{bs}, subtaskMetas2) -} - -func genEncodeStepMetas(t *testing.T, cnt int) [][]byte { - stepMetaBytes := make([][]byte, 0, cnt) - for i := 0; i < cnt; i++ { - prefix := fmt.Sprintf("d_%d_", i) - idxPrefix := fmt.Sprintf("i1_%d_", i) - meta := &ImportStepMeta{ - SortedDataMeta: &external.SortedKVMeta{ - MinKey: []byte(prefix + "a"), - MaxKey: []byte(prefix + "c"), - TotalKVSize: 12, - DataFiles: []string{prefix + "/1"}, - StatFiles: []string{prefix + "/1.stat"}, - MultipleFilesStats: []external.MultipleFilesStat{ - { - Filenames: [][2]string{ - {prefix + "/1", prefix + "/1.stat"}, - }, - }, - }, - }, - SortedIndexMetas: map[int64]*external.SortedKVMeta{ - 1: { - MinKey: []byte(idxPrefix + "a"), - MaxKey: []byte(idxPrefix + "c"), - TotalKVSize: 12, - DataFiles: []string{idxPrefix + "/1"}, - StatFiles: []string{idxPrefix + "/1.stat"}, - MultipleFilesStats: []external.MultipleFilesStat{ - { - Filenames: [][2]string{ - {idxPrefix + "/1", idxPrefix + "/1.stat"}, - }, - }, - }, - }, - }, - } - bytes, err := json.Marshal(meta) - require.NoError(t, err) - stepMetaBytes = append(stepMetaBytes, bytes) - } - return stepMetaBytes -} - -func TestGenerateMergeSortSpecs(t *testing.T) { - stepBak := external.MergeSortFileCountStep - external.MergeSortFileCountStep = 2 - t.Cleanup(func() { - external.MergeSortFileCountStep = stepBak - }) - // force merge sort for data kv - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort", `return("data")`)) - t.Cleanup(func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort")) - }) - encodeStepMetaBytes := genEncodeStepMetas(t, 3) - planCtx := planner.PlanCtx{ - Ctx: context.Background(), - TaskID: 1, - PreviousSubtaskMetas: map[int64][][]byte{ - StepEncodeAndSort: encodeStepMetaBytes, - }, - } - specs, err := generateMergeSortSpecs(planCtx) - require.NoError(t, err) - require.Len(t, specs, 2) - require.Len(t, specs[0].(*MergeSortSpec).DataFiles, 2) - require.Equal(t, "data", specs[0].(*MergeSortSpec).KVGroup) - require.Equal(t, "d_0_/1", specs[0].(*MergeSortSpec).DataFiles[0]) - require.Equal(t, "d_1_/1", specs[0].(*MergeSortSpec).DataFiles[1]) - require.Equal(t, "data", specs[1].(*MergeSortSpec).KVGroup) - require.Len(t, specs[1].(*MergeSortSpec).DataFiles, 1) - require.Equal(t, "d_2_/1", specs[1].(*MergeSortSpec).DataFiles[0]) -} - -func genMergeStepMetas(t *testing.T, cnt int) [][]byte { - stepMetaBytes := make([][]byte, 0, cnt) - for i := 0; i < cnt; i++ { - prefix := fmt.Sprintf("x_%d_", i) - meta := &MergeSortStepMeta{ - KVGroup: "data", - SortedKVMeta: external.SortedKVMeta{ - MinKey: []byte(prefix + "a"), - MaxKey: []byte(prefix + "c"), - TotalKVSize: 12, - DataFiles: []string{prefix + "/1"}, - StatFiles: []string{prefix + "/1.stat"}, - MultipleFilesStats: []external.MultipleFilesStat{ - { - Filenames: [][2]string{ - {prefix + "/1", prefix + "/1.stat"}, - }, - }, - }, - }, - } - bytes, err := json.Marshal(meta) - require.NoError(t, err) - stepMetaBytes = append(stepMetaBytes, bytes) - } - return stepMetaBytes -} - -func TestGetSortedKVMetas(t *testing.T) { - encodeStepMetaBytes := genEncodeStepMetas(t, 3) - kvMetas, err := getSortedKVMetasOfEncodeStep(encodeStepMetaBytes) - require.NoError(t, err) - require.Len(t, kvMetas, 2) - require.Contains(t, kvMetas, "data") - require.Contains(t, kvMetas, "1") - // just check meta is merged, won't check all fields - require.Equal(t, []byte("d_0_a"), kvMetas["data"].MinKey) - require.Equal(t, []byte("d_2_c"), kvMetas["data"].MaxKey) - require.Equal(t, []byte("i1_0_a"), kvMetas["1"].MinKey) - require.Equal(t, []byte("i1_2_c"), kvMetas["1"].MaxKey) - - mergeStepMetas := genMergeStepMetas(t, 3) - kvMetas2, err := getSortedKVMetasOfMergeStep(mergeStepMetas) - require.NoError(t, err) - require.Len(t, kvMetas2, 1) - require.Equal(t, []byte("x_0_a"), kvMetas2["data"].MinKey) - require.Equal(t, []byte("x_2_c"), kvMetas2["data"].MaxKey) - - // force merge sort for data kv - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort", `return("data")`)) - t.Cleanup(func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort")) - }) - allKVMetas, err := getSortedKVMetasForIngest(planner.PlanCtx{ - PreviousSubtaskMetas: map[int64][][]byte{ - StepEncodeAndSort: encodeStepMetaBytes, - StepMergeSort: mergeStepMetas, - }, - }) - require.NoError(t, err) - require.Len(t, allKVMetas, 2) - require.Equal(t, []byte("x_0_a"), allKVMetas["data"].MinKey) - require.Equal(t, []byte("x_2_c"), allKVMetas["data"].MaxKey) - require.Equal(t, []byte("i1_0_a"), allKVMetas["1"].MinKey) - require.Equal(t, []byte("i1_2_c"), allKVMetas["1"].MaxKey) -} diff --git a/disttask/importinto/scheduler.go b/disttask/importinto/scheduler.go deleted file mode 100644 index 61d84dd35331d..0000000000000 --- a/disttask/importinto/scheduler.go +++ /dev/null @@ -1,519 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importinto - -import ( - "context" - "encoding/json" - "sync" - "time" - - "github.com/docker/go-units" - "github.com/google/uuid" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/br/pkg/lightning/backend" - "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/br/pkg/lightning/config" - "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/br/pkg/lightning/metric" - "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -// importStepExecutor is a executor for import step. -// SubtaskExecutor is equivalent to a Lightning instance. -type importStepExecutor struct { - taskID int64 - taskMeta *TaskMeta - tableImporter *importer.TableImporter - sharedVars sync.Map - logger *zap.Logger - - indexMemorySizeLimit uint64 - - importCtx context.Context - importCancel context.CancelFunc - wg sync.WaitGroup -} - -func getTableImporter(ctx context.Context, taskID int64, taskMeta *TaskMeta) (*importer.TableImporter, error) { - idAlloc := kv.NewPanickingAllocators(0) - tbl, err := tables.TableFromMeta(idAlloc, taskMeta.Plan.TableInfo) - if err != nil { - return nil, err - } - astArgs, err := importer.ASTArgsFromStmt(taskMeta.Stmt) - if err != nil { - return nil, err - } - controller, err := importer.NewLoadDataController(&taskMeta.Plan, tbl, astArgs) - if err != nil { - return nil, err - } - if err = controller.InitDataStore(ctx); err != nil { - return nil, err - } - - return importer.NewTableImporter(&importer.JobImportParam{ - GroupCtx: ctx, - Progress: asyncloaddata.NewProgress(false), - Job: &asyncloaddata.Job{}, - }, controller, taskID) -} - -func (s *importStepExecutor) Init(ctx context.Context) error { - s.logger.Info("init subtask env") - tableImporter, err := getTableImporter(ctx, s.taskID, s.taskMeta) - if err != nil { - return err - } - s.tableImporter = tableImporter - - // we need this sub context since Cleanup which wait on this routine is called - // before parent context is canceled in normal flow. - s.importCtx, s.importCancel = context.WithCancel(ctx) - // only need to check disk quota when we are using local sort. - if s.tableImporter.IsLocalSort() { - s.wg.Add(1) - go func() { - defer s.wg.Done() - s.tableImporter.CheckDiskQuota(s.importCtx) - }() - } - s.indexMemorySizeLimit = getWriterMemorySizeLimit(s.tableImporter.Plan) - s.logger.Info("memory size limit per index writer per concurrency", - zap.String("limit", units.BytesSize(float64(s.indexMemorySizeLimit)))) - return nil -} - -func (s *importStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { - logger := s.logger.With(zap.Int64("subtask-id", subtask.ID)) - task := log.BeginTask(logger, "run subtask") - defer func() { - task.End(zapcore.ErrorLevel, err) - }() - bs := subtask.Meta - var subtaskMeta ImportStepMeta - err = json.Unmarshal(bs, &subtaskMeta) - if err != nil { - return errors.Trace(err) - } - - var dataEngine, indexEngine *backend.OpenedEngine - if s.tableImporter.IsLocalSort() { - dataEngine, err = s.tableImporter.OpenDataEngine(ctx, subtaskMeta.ID) - if err != nil { - return err - } - // Unlike in Lightning, we start an index engine for each subtask, - // whereas previously there was only a single index engine globally. - // This is because the scheduler currently does not have a post-processing mechanism. - // If we import the index in `cleanupSubtaskEnv`, the dispatcher will not wait for the import to complete. - // Multiple index engines may suffer performance degradation due to range overlap. - // These issues will be alleviated after we integrate s3 sorter. - // engineID = -1, -2, -3, ... - indexEngine, err = s.tableImporter.OpenIndexEngine(ctx, common.IndexEngineID-subtaskMeta.ID) - if err != nil { - return err - } - } - sharedVars := &SharedVars{ - TableImporter: s.tableImporter, - DataEngine: dataEngine, - IndexEngine: indexEngine, - Progress: asyncloaddata.NewProgress(false), - Checksum: &verification.KVChecksum{}, - SortedDataMeta: &external.SortedKVMeta{}, - SortedIndexMetas: make(map[int64]*external.SortedKVMeta), - } - s.sharedVars.Store(subtaskMeta.ID, sharedVars) - - source := operator.NewSimpleDataChannel(make(chan *importStepMinimalTask)) - op := newEncodeAndSortOperator(ctx, s, sharedVars, subtask.ID, s.indexMemorySizeLimit) - op.SetSource(source) - pipeline := operator.NewAsyncPipeline(op) - if err = pipeline.Execute(); err != nil { - return err - } - -outer: - for _, chunk := range subtaskMeta.Chunks { - // TODO: current workpool impl doesn't drain the input channel, it will - // just return on context cancel(error happened), so we add this select. - select { - case source.Channel() <- &importStepMinimalTask{ - Plan: s.taskMeta.Plan, - Chunk: chunk, - SharedVars: sharedVars, - }: - case <-op.Done(): - break outer - } - } - source.Finish() - - return pipeline.Close() -} - -func (s *importStepExecutor) OnFinished(ctx context.Context, subtask *proto.Subtask) error { - var subtaskMeta ImportStepMeta - if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { - return errors.Trace(err) - } - s.logger.Info("on subtask finished", zap.Int32("engine-id", subtaskMeta.ID)) - - val, ok := s.sharedVars.Load(subtaskMeta.ID) - if !ok { - return errors.Errorf("sharedVars %d not found", subtaskMeta.ID) - } - sharedVars, ok := val.(*SharedVars) - if !ok { - return errors.Errorf("sharedVars %d not found", subtaskMeta.ID) - } - - var dataKVCount int64 - if s.tableImporter.IsLocalSort() { - // TODO: we should close and cleanup engine in all case, since there's no checkpoint. - s.logger.Info("import data engine", zap.Int32("engine-id", subtaskMeta.ID)) - closedDataEngine, err := sharedVars.DataEngine.Close(ctx) - if err != nil { - return err - } - dataKVCount, err = s.tableImporter.ImportAndCleanup(ctx, closedDataEngine) - if err != nil { - return err - } - - s.logger.Info("import index engine", zap.Int32("engine-id", subtaskMeta.ID)) - if closedEngine, err := sharedVars.IndexEngine.Close(ctx); err != nil { - return err - } else if _, err := s.tableImporter.ImportAndCleanup(ctx, closedEngine); err != nil { - return err - } - } - // there's no imported dataKVCount on this stage when using global sort. - - sharedVars.mu.Lock() - defer sharedVars.mu.Unlock() - subtaskMeta.Checksum.Sum = sharedVars.Checksum.Sum() - subtaskMeta.Checksum.KVs = sharedVars.Checksum.SumKVS() - subtaskMeta.Checksum.Size = sharedVars.Checksum.SumSize() - subtaskMeta.Result = Result{ - LoadedRowCnt: uint64(dataKVCount), - ColSizeMap: sharedVars.Progress.GetColSize(), - } - allocators := sharedVars.TableImporter.Allocators() - subtaskMeta.MaxIDs = map[autoid.AllocatorType]int64{ - autoid.RowIDAllocType: allocators.Get(autoid.RowIDAllocType).Base(), - autoid.AutoIncrementType: allocators.Get(autoid.AutoIncrementType).Base(), - autoid.AutoRandomType: allocators.Get(autoid.AutoRandomType).Base(), - } - subtaskMeta.SortedDataMeta = sharedVars.SortedDataMeta - subtaskMeta.SortedIndexMetas = sharedVars.SortedIndexMetas - s.sharedVars.Delete(subtaskMeta.ID) - newMeta, err := json.Marshal(subtaskMeta) - if err != nil { - return errors.Trace(err) - } - subtask.Meta = newMeta - return nil -} - -func (s *importStepExecutor) Cleanup(_ context.Context) (err error) { - s.logger.Info("cleanup subtask env") - s.importCancel() - s.wg.Wait() - return s.tableImporter.Close() -} - -func (s *importStepExecutor) Rollback(context.Context) error { - // TODO: add rollback - s.logger.Info("rollback") - return nil -} - -type mergeSortStepExecutor struct { - scheduler.EmptySubtaskExecutor - taskID int64 - taskMeta *TaskMeta - logger *zap.Logger - controller *importer.LoadDataController - // subtask of a task is run in serial now, so we don't need lock here. - // change to SyncMap when we support parallel subtask in the future. - subtaskSortedKVMeta *external.SortedKVMeta -} - -var _ execute.SubtaskExecutor = &mergeSortStepExecutor{} - -func (m *mergeSortStepExecutor) Init(ctx context.Context) error { - controller, err := buildController(&m.taskMeta.Plan, m.taskMeta.Stmt) - if err != nil { - return err - } - if err = controller.InitDataStore(ctx); err != nil { - return err - } - m.controller = controller - return nil -} - -func (m *mergeSortStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { - logger := m.logger.With(zap.Int64("subtask-id", subtask.ID)) - task := log.BeginTask(logger, "run subtask") - defer func() { - task.End(zapcore.ErrorLevel, err) - }() - - sm := &MergeSortStepMeta{} - err = json.Unmarshal(subtask.Meta, sm) - if err != nil { - return errors.Trace(err) - } - - var mu sync.Mutex - m.subtaskSortedKVMeta = &external.SortedKVMeta{} - onClose := func(summary *external.WriterSummary) { - mu.Lock() - defer mu.Unlock() - m.subtaskSortedKVMeta.MergeSummary(summary) - } - - writerID := uuid.New().String() - prefix := subtaskPrefix(m.taskID, subtask.ID) - - return external.MergeOverlappingFiles( - ctx, - sm.DataFiles, - m.controller.GlobalSortStore, - 64*1024, - prefix, - writerID, - 256*size.MB, - 8*1024, - 1*size.MB, - 8*1024, - onClose) -} - -func (m *mergeSortStepExecutor) OnFinished(_ context.Context, subtask *proto.Subtask) error { - var subtaskMeta MergeSortStepMeta - if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { - return errors.Trace(err) - } - subtaskMeta.SortedKVMeta = *m.subtaskSortedKVMeta - m.subtaskSortedKVMeta = nil - newMeta, err := json.Marshal(subtaskMeta) - if err != nil { - return errors.Trace(err) - } - subtask.Meta = newMeta - return nil -} - -type writeAndIngestStepExecutor struct { - taskID int64 - taskMeta *TaskMeta - logger *zap.Logger - tableImporter *importer.TableImporter -} - -var _ execute.SubtaskExecutor = &writeAndIngestStepExecutor{} - -func (e *writeAndIngestStepExecutor) Init(ctx context.Context) error { - tableImporter, err := getTableImporter(ctx, e.taskID, e.taskMeta) - if err != nil { - return err - } - e.tableImporter = tableImporter - return nil -} - -func (e *writeAndIngestStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { - sm := &WriteIngestStepMeta{} - err = json.Unmarshal(subtask.Meta, sm) - if err != nil { - return errors.Trace(err) - } - - logger := e.logger.With(zap.Int64("subtask-id", subtask.ID), - zap.String("kv-group", sm.KVGroup)) - task := log.BeginTask(logger, "run subtask") - defer func() { - task.End(zapcore.ErrorLevel, err) - }() - - _, engineUUID := backend.MakeUUID("", subtask.ID) - localBackend := e.tableImporter.Backend() - err = localBackend.CloseEngine(ctx, &backend.EngineConfig{ - External: &backend.ExternalEngineConfig{ - StorageURI: e.taskMeta.Plan.CloudStorageURI, - DataFiles: sm.DataFiles, - StatFiles: sm.StatFiles, - MinKey: sm.MinKey, - MaxKey: sm.MaxKey, - SplitKeys: sm.RangeSplitKeys, - RegionSplitSize: sm.RangeSplitSize, - TotalFileSize: int64(sm.TotalKVSize), - TotalKVCount: 0, - }, - }, engineUUID) - if err != nil { - return err - } - return localBackend.ImportEngine(ctx, engineUUID, int64(config.SplitRegionSize), int64(config.SplitRegionKeys)) -} - -func (e *writeAndIngestStepExecutor) OnFinished(_ context.Context, subtask *proto.Subtask) error { - var subtaskMeta WriteIngestStepMeta - if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { - return errors.Trace(err) - } - if subtaskMeta.KVGroup != dataKVGroup { - return nil - } - - // only data kv group has loaded row count - _, engineUUID := backend.MakeUUID("", subtask.ID) - localBackend := e.tableImporter.Backend() - _, kvCount := localBackend.GetExternalEngineKVStatistics(engineUUID) - subtaskMeta.Result.LoadedRowCnt = uint64(kvCount) - - newMeta, err := json.Marshal(subtaskMeta) - if err != nil { - return errors.Trace(err) - } - subtask.Meta = newMeta - return nil -} - -func (e *writeAndIngestStepExecutor) Cleanup(_ context.Context) (err error) { - e.logger.Info("cleanup subtask env") - return e.tableImporter.Close() -} - -func (e *writeAndIngestStepExecutor) Rollback(context.Context) error { - e.logger.Info("rollback") - return nil -} - -type postStepExecutor struct { - scheduler.EmptySubtaskExecutor - taskID int64 - taskMeta *TaskMeta - logger *zap.Logger -} - -var _ execute.SubtaskExecutor = &postStepExecutor{} - -func (p *postStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { - logger := p.logger.With(zap.Int64("subtask-id", subtask.ID)) - task := log.BeginTask(logger, "run subtask") - defer func() { - task.End(zapcore.ErrorLevel, err) - }() - stepMeta := PostProcessStepMeta{} - if err = json.Unmarshal(subtask.Meta, &stepMeta); err != nil { - return errors.Trace(err) - } - failpoint.Inject("waitBeforePostProcess", func() { - time.Sleep(5 * time.Second) - }) - return postProcess(ctx, p.taskMeta, &stepMeta, logger) -} - -type importScheduler struct { - *scheduler.BaseScheduler -} - -func newImportScheduler(ctx context.Context, id string, task *proto.Task, taskTable scheduler.TaskTable) scheduler.Scheduler { - s := &importScheduler{ - BaseScheduler: scheduler.NewBaseScheduler(ctx, id, task.ID, taskTable), - } - s.BaseScheduler.Extension = s - return s -} - -func (s *importScheduler) Run(ctx context.Context, task *proto.Task) error { - metrics := metricsManager.getOrCreateMetrics(task.ID) - defer metricsManager.unregister(task.ID) - subCtx := metric.WithCommonMetric(ctx, metrics) - return s.BaseScheduler.Run(subCtx, task) -} - -func (*importScheduler) IsIdempotent(*proto.Subtask) bool { - // import don't have conflict detection and resolution now, so it's ok - // to import data twice. - return true -} - -func (*importScheduler) GetSubtaskExecutor(_ context.Context, task *proto.Task, _ *execute.Summary) (execute.SubtaskExecutor, error) { - taskMeta := TaskMeta{} - if err := json.Unmarshal(task.Meta, &taskMeta); err != nil { - return nil, errors.Trace(err) - } - logger := logutil.BgLogger().With( - zap.String("type", proto.ImportInto), - zap.Int64("task-id", task.ID), - zap.String("step", stepStr(task.Step)), - ) - logger.Info("create step scheduler") - - switch task.Step { - case StepImport, StepEncodeAndSort: - return &importStepExecutor{ - taskID: task.ID, - taskMeta: &taskMeta, - logger: logger, - }, nil - case StepMergeSort: - return &mergeSortStepExecutor{ - taskID: task.ID, - taskMeta: &taskMeta, - logger: logger, - }, nil - case StepWriteAndIngest: - return &writeAndIngestStepExecutor{ - taskID: task.ID, - taskMeta: &taskMeta, - logger: logger, - }, nil - case StepPostProcess: - return &postStepExecutor{ - taskID: task.ID, - taskMeta: &taskMeta, - logger: logger, - }, nil - default: - return nil, errors.Errorf("unknown step %d for import task %d", task.Step, task.ID) - } -} - -func init() { - scheduler.RegisterTaskType(proto.ImportInto, newImportScheduler) -} diff --git a/disttask/operator/BUILD.bazel b/disttask/operator/BUILD.bazel deleted file mode 100644 index dc2a180e2da93..0000000000000 --- a/disttask/operator/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "operator", - srcs = [ - "compose.go", - "operator.go", - "pipeline.go", - "wrapper.go", - ], - importpath = "github.com/pingcap/tidb/disttask/operator", - visibility = ["//visibility:public"], - deps = [ - "//resourcemanager/pool/workerpool", - "//resourcemanager/util", - "@org_golang_x_sync//errgroup", - ], -) - -go_test( - name = "operator_test", - timeout = "short", - srcs = ["pipeline_test.go"], - embed = [":operator"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/domain/BUILD.bazel b/domain/BUILD.bazel deleted file mode 100644 index 4bdce47695e19..0000000000000 --- a/domain/BUILD.bazel +++ /dev/null @@ -1,166 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "domain", - srcs = [ - "domain.go", - "domain_sysvars.go", - "domainctx.go", - "extract.go", - "historical_stats.go", - "optimize_trace.go", - "plan_replayer.go", - "plan_replayer_dump.go", - "runaway.go", - "schema_checker.go", - "schema_validator.go", - "sysvar_cache.go", - "test_helper.go", - "topn_slow_query.go", - ], - importpath = "github.com/pingcap/tidb/domain", - visibility = ["//visibility:public"], - deps = [ - "//bindinfo", - "//br/pkg/streamhelper", - "//br/pkg/streamhelper/daemon", - "//config", - "//ddl", - "//ddl/placement", - "//ddl/schematracker", - "//ddl/util", - "//disttask/framework/dispatcher", - "//disttask/framework/scheduler", - "//disttask/framework/storage", - "//domain/globalconfigsync", - "//domain/infosync", - "//domain/metrics", - "//domain/resourcegroup", - "//errno", - "//infoschema", - "//infoschema/metrics", - "//infoschema/perfschema", - "//keyspace", - "//kv", - "//meta", - "//metrics", - "//owner", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//privilege/privileges", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/variable", - "//statistics/handle", - "//statistics/handle/storage", - "//store/helper", - "//telemetry", - "//ttl/cache", - "//ttl/sqlbuilder", - "//ttl/ttlworker", - "//types", - "//util", - "//util/chunk", - "//util/dbterror", - "//util/disttask", - "//util/domainutil", - "//util/engine", - "//util/etcd", - "//util/execdetails", - "//util/expensivequery", - "//util/gctuner", - "//util/globalconn", - "//util/intest", - "//util/logutil", - "//util/memory", - "//util/memoryusagealarm", - "//util/printer", - "//util/replayer", - "//util/servermemorylimit", - "//util/sqlexec", - "//util/syncutil", - "@com_github_burntsushi_toml//:toml", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/pdpb", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//txnkv/transaction", - "@com_github_tikv_pd_client//:client", - "@com_github_tikv_pd_client//resource_group/controller", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//backoff", - "@org_golang_google_grpc//keepalive", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "domain_test", - timeout = "short", - srcs = [ - "db_test.go", - "domain_test.go", - "domain_utils_test.go", - "domainctx_test.go", - "extract_test.go", - "main_test.go", - "plan_replayer_handle_test.go", - "plan_replayer_test.go", - "schema_checker_test.go", - "schema_validator_test.go", - "session_pool_test.go", - "topn_slow_query_test.go", - ], - embed = [":domain"], - flaky = True, - shard_count = 23, - deps = [ - "//config", - "//ddl", - "//domain/infosync", - "//errno", - "//keyspace", - "//kv", - "//metrics", - "//parser/ast", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//server", - "//session", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//store/mockstore", - "//testkit", - "//testkit/testsetup", - "//types", - "//util", - "//util/mock", - "//util/replayer", - "//util/stmtsummary/v2:stmtsummary", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//txnkv/transaction", - "@com_github_tikv_pd_client//:client", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/domain/db_test.go b/domain/db_test.go deleted file mode 100644 index 06b82ea2f9fd5..0000000000000 --- a/domain/db_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package domain_test - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/stretchr/testify/require" -) - -func TestDomainSession(t *testing.T) { - lease := 50 * time.Millisecond - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - session.SetSchemaLease(lease) - domain, err := session.BootstrapSession(store) - require.NoError(t, err) - ddl.DisableTiFlashPoll(domain.DDL()) - defer domain.Close() - - // for NotifyUpdatePrivilege - createRoleSQL := `CREATE ROLE 'test'@'localhost';` - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - _, err = se.Execute(context.Background(), createRoleSQL) - require.NoError(t, err) - - // for BindHandle - _, err = se.Execute(context.Background(), "use test") - require.NoError(t, err) - _, err = se.Execute(context.Background(), "drop table if exists t") - require.NoError(t, err) - _, err = se.Execute(context.Background(), "create table t(i int, s varchar(20), index index_t(i, s))") - require.NoError(t, err) - _, err = se.Execute(context.Background(), "create global binding for select * from t where i>100 using select * from t use index(index_t) where i>100") - require.NoError(t, err) -} - -func TestNormalSessionPool(t *testing.T) { - lease := 100 * time.Millisecond - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - session.SetSchemaLease(lease) - domain, err := session.BootstrapSession(store) - require.NoError(t, err) - defer domain.Close() - info, err1 := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) - require.NoError(t, err1) - conf := config.GetGlobalConfig() - conf.Socket = "" - conf.Port = 0 - conf.Status.ReportStatus = false - svr, err := server.NewServer(conf, nil) - require.NoError(t, err) - svr.SetDomain(domain) - info.SetSessionManager(svr) - - pool := domain.SysSessionPool() - se, err := pool.Get() - require.NoError(t, err) - require.NotEmpty(t, se) - require.Equal(t, svr.InternalSessionExists(se), true) - - pool.Put(se) - require.Equal(t, svr.InternalSessionExists(se), false) -} - -func TestAbnormalSessionPool(t *testing.T) { - lease := 100 * time.Millisecond - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - session.SetSchemaLease(lease) - domain, err := session.BootstrapSession(store) - require.NoError(t, err) - defer domain.Close() - info, err1 := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) - require.NoError(t, err1) - conf := config.GetGlobalConfig() - conf.Socket = "" - conf.Port = 0 - conf.Status.ReportStatus = false - svr, err := server.NewServer(conf, nil) - require.NoError(t, err) - svr.SetDomain(domain) - info.SetSessionManager(svr) - - pool := domain.SysSessionPool() - failpoint.Enable("github.com/pingcap/tidb/domain/mockSessionPoolReturnError", "return") - se, err := pool.Get() - require.Error(t, err) - failpoint.Disable("github.com/pingcap/tidb/domain/mockSessionPoolReturnError") - require.Equal(t, svr.InternalSessionExists(se), false) -} diff --git a/domain/extract.go b/domain/extract.go deleted file mode 100644 index 915e13a8d6d20..0000000000000 --- a/domain/extract.go +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package domain - -import ( - "archive/zip" - "context" - "encoding/base64" - "encoding/json" - "fmt" - "math/rand" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - "github.com/BurntSushi/toml" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -const ( - // ExtractMetaFile indicates meta file for extract - ExtractMetaFile = "extract_meta.txt" -) - -const ( - // ExtractTaskType indicates type of extract task - ExtractTaskType = "taskType" - // ExtractPlanTaskSkipStats indicates skip stats for extract plan task - ExtractPlanTaskSkipStats = "SkipStats" -) - -// ExtractType indicates type -type ExtractType uint8 - -const ( - // ExtractPlanType indicates extract plan task - ExtractPlanType ExtractType = iota -) - -func taskTypeToString(t ExtractType) string { - switch t { - case ExtractPlanType: - return "Plan" - } - return "Unknown" -} - -// ExtractHandle handles the extractWorker to run extract the information task like Plan or any others. -// extractHandle will provide 2 mode for extractWorker: -// 1. submit a background extract task, the response will be returned after the task is started to be solved -// 2. submit a task and wait until the task is solved, the result will be returned to the response. -type ExtractHandle struct { - worker *extractWorker -} - -// NewExtractHandler new extract handler -func NewExtractHandler(sctxs []sessionctx.Context) *ExtractHandle { - h := &ExtractHandle{} - h.worker = newExtractWorker(sctxs[0], false) - return h -} - -// ExtractTask extract tasks -func (h *ExtractHandle) ExtractTask(ctx context.Context, task *ExtractTask) (string, error) { - // TODO: support background job later - if task.IsBackgroundJob { - return "", nil - } - return h.worker.extractTask(ctx, task) -} - -type extractWorker struct { - ctx context.Context - sctx sessionctx.Context - isBackgroundWorker bool - sync.Mutex -} - -// ExtractTask indicates task -type ExtractTask struct { - ExtractType ExtractType - IsBackgroundJob bool - - // Param for Extract Plan - SkipStats bool - UseHistoryView bool - - // variables for plan task type - Begin time.Time - End time.Time -} - -// NewExtractPlanTask returns extract plan task -func NewExtractPlanTask(begin, end time.Time) *ExtractTask { - return &ExtractTask{ - Begin: begin, - End: end, - ExtractType: ExtractPlanType, - } -} - -func newExtractWorker(sctx sessionctx.Context, isBackgroundWorker bool) *extractWorker { - return &extractWorker{ - sctx: sctx, - isBackgroundWorker: isBackgroundWorker, - } -} - -func (w *extractWorker) extractTask(ctx context.Context, task *ExtractTask) (string, error) { - switch task.ExtractType { - case ExtractPlanType: - return w.extractPlanTask(ctx, task) - } - return "", errors.New("unknown extract task") -} - -func (w *extractWorker) extractPlanTask(ctx context.Context, task *ExtractTask) (string, error) { - if task.UseHistoryView && !config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return "", errors.New("tidb_stmt_summary_enable_persistent should be enabled for extract task") - } - records, err := w.collectRecords(ctx, task) - if err != nil { - logutil.BgLogger().Error("collect stmt summary records failed for extract plan task", zap.Error(err)) - return "", err - } - p, err := w.packageExtractPlanRecords(ctx, records) - if err != nil { - logutil.BgLogger().Error("package stmt summary records failed for extract plan task", zap.Error(err)) - return "", err - } - return w.dumpExtractPlanPackage(task, p) -} - -func (w *extractWorker) collectRecords(ctx context.Context, task *ExtractTask) (map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, error) { - w.Lock() - defer w.Unlock() - exec := w.sctx.(sqlexec.RestrictedSQLExecutor) - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - sourceTable := "STATEMENTS_SUMMARY_HISTORY" - if !task.UseHistoryView { - sourceTable = "STATEMENTS_SUMMARY" - } - rows, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("SELECT STMT_TYPE, DIGEST, PLAN_DIGEST,QUERY_SAMPLE_TEXT, BINARY_PLAN, TABLE_NAMES, SAMPLE_USER FROM INFORMATION_SCHEMA.%s WHERE SUMMARY_END_TIME > '%s' AND SUMMARY_BEGIN_TIME < '%s'", - sourceTable, task.Begin.Format(types.TimeFormat), task.End.Format(types.TimeFormat))) - if err != nil { - return nil, err - } - collectMap := make(map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, 0) - for _, row := range rows { - record := &stmtSummaryHistoryRecord{} - record.stmtType = row.GetString(0) - record.digest = row.GetString(1) - record.planDigest = row.GetString(2) - record.sql = row.GetString(3) - record.binaryPlan = row.GetString(4) - tableNames := row.GetString(5) - key := stmtSummaryHistoryKey{ - digest: record.digest, - planDigest: record.planDigest, - } - record.userName = row.GetString(6) - record.tables = make([]tableNamePair, 0) - setRecord, err := w.handleTableNames(tableNames, record) - if err != nil { - return nil, err - } - if setRecord && checkRecordValid(record) { - collectMap[key] = record - } - } - return collectMap, nil -} - -func (w *extractWorker) handleTableNames(tableNames string, record *stmtSummaryHistoryRecord) (bool, error) { - is := GetDomain(w.sctx).InfoSchema() - for _, t := range strings.Split(tableNames, ",") { - names := strings.Split(t, ".") - if len(names) != 2 { - return false, nil - } - dbName := names[0] - tblName := names[1] - record.schemaName = dbName - // skip internal schema record - switch strings.ToLower(record.schemaName) { - case util.PerformanceSchemaName.L, util.InformationSchemaName.L, util.MetricSchemaName.L, "mysql": - return false, nil - } - exists := is.TableExists(model.NewCIStr(dbName), model.NewCIStr(tblName)) - if !exists { - return false, nil - } - t, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) - if err != nil { - return false, err - } - record.tables = append(record.tables, tableNamePair{DBName: dbName, TableName: tblName, IsView: t.Meta().IsView()}) - } - return true, nil -} - -func checkRecordValid(r *stmtSummaryHistoryRecord) bool { - if r.stmtType != "Select" { - return false - } - if r.schemaName == "" { - return false - } - if r.planDigest == "" { - return false - } - return true -} - -func (w *extractWorker) packageExtractPlanRecords(ctx context.Context, records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord) (*extractPlanPackage, error) { - p := &extractPlanPackage{} - p.records = records - p.tables = make(map[tableNamePair]struct{}, 0) - for _, record := range records { - // skip the sql which has been cut off - if strings.Contains(record.sql, "(len:") { - record.skip = true - continue - } - plan, err := w.decodeBinaryPlan(ctx, record.binaryPlan) - if err != nil { - return nil, err - } - record.plan = plan - for _, tbl := range record.tables { - p.tables[tbl] = struct{}{} - } - } - if err := w.handleIsView(ctx, p); err != nil { - return nil, err - } - return p, nil -} - -func (w *extractWorker) handleIsView(ctx context.Context, p *extractPlanPackage) error { - is := GetDomain(w.sctx).InfoSchema() - tne := &tableNameExtractor{ - ctx: ctx, - executor: w.sctx.(sqlexec.RestrictedSQLExecutor), - is: is, - curDB: model.NewCIStr(""), - names: make(map[tableNamePair]struct{}), - cteNames: make(map[string]struct{}), - } - for v := range p.tables { - if v.IsView { - v, err := is.TableByName(model.NewCIStr(v.DBName), model.NewCIStr(v.TableName)) - if err != nil { - return err - } - sql := v.Meta().View.SelectStmt - node, err := tne.executor.ParseWithParams(tne.ctx, sql) - if err != nil { - return err - } - node.Accept(tne) - } - } - if tne.err != nil { - return tne.err - } - r := tne.getTablesAndViews() - for t := range r { - p.tables[t] = struct{}{} - } - return nil -} - -func (w *extractWorker) decodeBinaryPlan(ctx context.Context, bPlan string) (string, error) { - exec := w.sctx.(sqlexec.RestrictedSQLExecutor) - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - rows, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("SELECT tidb_decode_binary_plan('%s')", bPlan)) - if err != nil { - return "", err - } - plan := rows[0].GetString(0) - return strings.Trim(plan, "\n"), nil -} - -// dumpExtractPlanPackage will dump the information about sqls collected in stmt_summary_history -// The files will be organized into the following format: -/* - |-extract_meta.txt - |-meta.txt - |-config.toml - |-variables.toml - |-bindings.sql - |-schema - | |-schema_meta.txt - | |-db1.table1.schema.txt - | |-db2.table2.schema.txt - | |-.... - |-view - | |-db1.view1.view.txt - | |-db2.view2.view.txt - | |-.... - |-stats - | |-stats1.json - | |-stats2.json - | |-.... - |-table_tiflash_replica.txt - |-sql - | |-digest1.sql - | |-digest2.sql - | |-.... - |-skippedSQLs - | |-digest1.sql - | |-... -*/ -func (w *extractWorker) dumpExtractPlanPackage(task *ExtractTask, p *extractPlanPackage) (name string, err error) { - f, name, err := GenerateExtractFile() - if err != nil { - return "", err - } - zw := zip.NewWriter(f) - defer func() { - if err != nil { - logutil.BgLogger().Error("dump extract plan task failed", zap.Error(err)) - } - if err1 := zw.Close(); err1 != nil { - logutil.BgLogger().Warn("close zip file failed", zap.String("file", name), zap.Error(err)) - } - if err1 := f.Close(); err1 != nil { - logutil.BgLogger().Warn("close file failed", zap.String("file", name), zap.Error(err)) - } - }() - - // Dump config - if err = dumpConfig(zw); err != nil { - return "", err - } - // Dump meta - if err = dumpMeta(zw); err != nil { - return "", err - } - // dump extract plan task meta - if err = dumpExtractMeta(task, zw); err != nil { - return "", err - } - // Dump Schema and View - if err = dumpSchemas(w.sctx, zw, p.tables); err != nil { - return "", err - } - // Dump tables tiflash replicas - if err = dumpTiFlashReplica(w.sctx, zw, p.tables); err != nil { - return "", err - } - // Dump variables - if err = dumpVariables(w.sctx, w.sctx.GetSessionVars(), zw); err != nil { - return "", err - } - // Dump global bindings - if err = dumpGlobalBindings(w.sctx, zw); err != nil { - return "", err - } - // Dump stats - if !task.SkipStats { - if _, err = dumpStats(zw, p.tables, GetDomain(w.sctx), 0); err != nil { - return "", err - } - } - // Dump sqls and plan - if err = dumpSQLRecords(p.records, zw); err != nil { - return "", err - } - return name, nil -} - -func dumpSQLRecords(records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, zw *zip.Writer) error { - for key, record := range records { - if record.skip { - err := dumpSQLRecord(record, fmt.Sprintf("skippedSQLs/%v.json", key.digest), zw) - if err != nil { - return err - } - } else { - err := dumpSQLRecord(record, fmt.Sprintf("SQLs/%v.json", key.digest), zw) - if err != nil { - return err - } - } - } - return nil -} - -type singleSQLRecord struct { - Schema string `json:"schema"` - Plan string `json:"plan"` - SQL string `json:"sql"` - Digest string `json:"digest"` - BinaryPlan string `json:"binaryPlan"` - UserName string `json:"userName"` -} - -// dumpSQLRecord dumps sql records into one file for each record, the format is in json. -func dumpSQLRecord(record *stmtSummaryHistoryRecord, path string, zw *zip.Writer) error { - zf, err := zw.Create(path) - if err != nil { - return err - } - singleSQLRecord := &singleSQLRecord{ - Schema: record.schemaName, - Plan: record.plan, - SQL: record.sql, - Digest: record.digest, - BinaryPlan: record.binaryPlan, - UserName: record.userName, - } - content, err := json.Marshal(singleSQLRecord) - if err != nil { - return err - } - _, err = zf.Write(content) - if err != nil { - return err - } - return nil -} - -func dumpExtractMeta(task *ExtractTask, zw *zip.Writer) error { - cf, err := zw.Create(ExtractMetaFile) - if err != nil { - return errors.AddStack(err) - } - varMap := make(map[string]string) - varMap[ExtractTaskType] = taskTypeToString(task.ExtractType) - switch task.ExtractType { - case ExtractPlanType: - varMap[ExtractPlanTaskSkipStats] = strconv.FormatBool(task.SkipStats) - } - - if err := toml.NewEncoder(cf).Encode(varMap); err != nil { - return errors.AddStack(err) - } - return nil -} - -type extractPlanPackage struct { - tables map[tableNamePair]struct{} - records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord -} - -type stmtSummaryHistoryKey struct { - digest string - planDigest string -} - -type stmtSummaryHistoryRecord struct { - stmtType string - schemaName string - tables []tableNamePair - digest string - planDigest string - sql string - binaryPlan string - userName string - - plan string - skip bool -} - -// GenerateExtractFile generates extract stmt file -func GenerateExtractFile() (*os.File, string, error) { - path := GetExtractTaskDirName() - err := os.MkdirAll(path, os.ModePerm) - if err != nil { - return nil, "", errors.AddStack(err) - } - fileName, err := generateExtractStmtFile() - if err != nil { - return nil, "", errors.AddStack(err) - } - zf, err := os.Create(filepath.Join(path, fileName)) - if err != nil { - return nil, "", errors.AddStack(err) - } - return zf, fileName, err -} - -func generateExtractStmtFile() (string, error) { - // Generate key and create zip file - time := time.Now().UnixNano() - b := make([]byte, 16) - //nolint: gosec - _, err := rand.Read(b) - if err != nil { - return "", err - } - key := base64.URLEncoding.EncodeToString(b) - return fmt.Sprintf("extract_%v_%v.zip", key, time), nil -} - -// GetExtractTaskDirName get extract dir name -func GetExtractTaskDirName() string { - tidbLogDir := filepath.Dir(config.GetGlobalConfig().Log.File.Filename) - return filepath.Join(tidbLogDir, "extract") -} diff --git a/domain/extract_test.go b/domain/extract_test.go deleted file mode 100644 index c543972ef0c6f..0000000000000 --- a/domain/extract_test.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package domain_test - -import ( - "context" - "os" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/testkit" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - "github.com/stretchr/testify/require" -) - -func TestExtractPlanWithoutHistoryView(t *testing.T) { - _, dom := testkit.CreateMockStoreAndDomain(t) - extractHandler := dom.GetExtractHandle() - task := domain.NewExtractPlanTask(time.Now(), time.Now()) - task.UseHistoryView = false - _, err := extractHandler.ExtractTask(context.Background(), task) - require.NoError(t, err) -} - -func TestExtractWithoutStmtSummaryPersistedEnabled(t *testing.T) { - setupStmtSummary() - closeStmtSummary() - _, dom := testkit.CreateMockStoreAndDomain(t) - extractHandler := dom.GetExtractHandle() - task := domain.NewExtractPlanTask(time.Now(), time.Now()) - task.UseHistoryView = true - _, err := extractHandler.ExtractTask(context.Background(), task) - require.Error(t, err) -} - -func TestExtractHandlePlanTask(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := newTestKitWithRoot(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id int);") - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) - - // new testkit - tk = newTestKitWithRoot(t, store) - tk.MustExec("use test") - tk.MustQuery("select * from t") - startTime := time.Now() - time.Sleep(time.Second) - tk.MustQuery("select COUNT(*) FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY_HISTORY WHERE STMT_TYPE = 'Select' AND SCHEMA_NAME = 'test' AND TABLE_NAMES = 'test.t'").Check(testkit.Rows("1")) - time.Sleep(time.Second) - end := time.Now() - extractHandler := dom.GetExtractHandle() - task := domain.NewExtractPlanTask(startTime, end) - task.UseHistoryView = true - name, err := extractHandler.ExtractTask(context.Background(), task) - require.NoError(t, err) - require.True(t, len(name) > 0) -} - -func setupStmtSummary() { - stmtsummaryv2.Setup(&stmtsummaryv2.Config{ - Filename: "tidb-statements.log", - }) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = true - }) -} - -func closeStmtSummary() { - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = false - }) - stmtsummaryv2.GlobalStmtSummary.Close() - _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) -} - -func newTestKit(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - return tk -} - -func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := newTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - return tk -} diff --git a/domain/globalconfigsync/BUILD.bazel b/domain/globalconfigsync/BUILD.bazel deleted file mode 100644 index 50ac507d54e1a..0000000000000 --- a/domain/globalconfigsync/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "globalconfigsync", - srcs = ["globalconfig.go"], - importpath = "github.com/pingcap/tidb/domain/globalconfigsync", - visibility = ["//visibility:public"], - deps = [ - "//util/logutil", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "globalconfigsync_test", - timeout = "short", - srcs = ["globalconfig_test.go"], - flaky = True, - deps = [ - ":globalconfigsync", - "//kv", - "//session", - "//store/mockstore", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_pd_client//:client", - "@io_etcd_go_etcd_tests_v3//integration", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/domain/infosync/BUILD.bazel b/domain/infosync/BUILD.bazel deleted file mode 100644 index 2eed57e867ac8..0000000000000 --- a/domain/infosync/BUILD.bazel +++ /dev/null @@ -1,78 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "infosync", - srcs = [ - "error.go", - "info.go", - "label_manager.go", - "mock_info.go", - "placement_manager.go", - "region.go", - "resource_manager_client.go", - "schedule_manager.go", - "tiflash_manager.go", - ], - importpath = "github.com/pingcap/tidb/domain/infosync", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl/label", - "//ddl/placement", - "//ddl/util", - "//domain/resourcegroup", - "//errno", - "//kv", - "//metrics", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx/binloginfo", - "//sessionctx/variable", - "//store/helper", - "//tablecodec", - "//util", - "//util/codec", - "//util/dbterror", - "//util/hack", - "//util/logutil", - "//util/pdapi", - "//util/syncutil", - "//util/versioninfo", - "@com_github_golang_protobuf//proto", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/meta_storagepb", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_pd_client//:client", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "infosync_test", - timeout = "short", - srcs = ["info_test.go"], - embed = [":infosync"], - flaky = True, - shard_count = 4, - deps = [ - "//ddl/placement", - "//ddl/util", - "//keyspace", - "//parser/model", - "//testkit/testsetup", - "//util", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/domain/infosync/error.go b/domain/infosync/error.go deleted file mode 100644 index 53c1fedcd9d2b..0000000000000 --- a/domain/infosync/error.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package infosync - -import ( - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbterror" -) - -var ( - // ErrHTTPServiceError means we got a http response with a status code which is not '2xx' - ErrHTTPServiceError = dbterror.ClassDomain.NewStdErr( - errno.ErrHTTPServiceError, mysql.Message("HTTP request failed with status %s", nil), - ) -) diff --git a/domain/infosync/region.go b/domain/infosync/region.go deleted file mode 100644 index cdda3e1a73d68..0000000000000 --- a/domain/infosync/region.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package infosync - -import ( - "context" - "encoding/hex" - "encoding/json" - "fmt" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/pdapi" -) - -// PlacementScheduleState is the returned third-valued state from GetReplicationState(). For convenience, the string of PD is deserialized into an enum first. -type PlacementScheduleState int - -const ( - // PlacementScheduleStatePending corresponds to "PENDING" from PD. - PlacementScheduleStatePending PlacementScheduleState = iota - // PlacementScheduleStateInProgress corresponds to "INPROGRESS" from PD. - PlacementScheduleStateInProgress - // PlacementScheduleStateScheduled corresponds to "REPLICATED" from PD. - PlacementScheduleStateScheduled -) - -func (t PlacementScheduleState) String() string { - switch t { - case PlacementScheduleStateScheduled: - return "SCHEDULED" - case PlacementScheduleStateInProgress: - return "INPROGRESS" - case PlacementScheduleStatePending: - return "PENDING" - default: - return "PENDING" - } -} - -// GetReplicationState is used to check if regions in the given keyranges are replicated from PD. -func GetReplicationState(ctx context.Context, startKey []byte, endKey []byte) (PlacementScheduleState, error) { - is, err := getGlobalInfoSyncer() - if err != nil { - return PlacementScheduleStatePending, err - } - - if is.etcdCli == nil { - return PlacementScheduleStatePending, nil - } - - addrs := is.etcdCli.Endpoints() - - if len(addrs) == 0 { - return PlacementScheduleStatePending, errors.Errorf("pd unavailable") - } - - res, err := doRequest(ctx, "GetReplicationState", addrs, fmt.Sprintf("%s/replicated?startKey=%s&endKey=%s", pdapi.Regions, hex.EncodeToString(startKey), hex.EncodeToString(endKey)), "GET", nil) - if err == nil && res != nil { - st := PlacementScheduleStatePending - // it should not fail - var state string - _ = json.Unmarshal(res, &state) - switch state { - case "REPLICATED": - st = PlacementScheduleStateScheduled - case "INPROGRESS": - st = PlacementScheduleStateInProgress - case "PENDING": - st = PlacementScheduleStatePending - } - return st, nil - } - return PlacementScheduleStatePending, err -} diff --git a/domain/main_test.go b/domain/main_test.go deleted file mode 100644 index 6172387e5a67c..0000000000000 --- a/domain/main_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package domain_test - -import ( - "testing" - - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - server.RunInGoTest = true - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/domain/metrics/BUILD.bazel b/domain/metrics/BUILD.bazel deleted file mode 100644 index c0d3843193791..0000000000000 --- a/domain/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/domain/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/domain/metrics/metrics.go b/domain/metrics/metrics.go deleted file mode 100644 index 08ddabdaeae9a..0000000000000 --- a/domain/metrics/metrics.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// domain metrics vars -var ( - GenerateHistoricalStatsSuccessCounter prometheus.Counter - GenerateHistoricalStatsFailedCounter prometheus.Counter - - PlanReplayerDumpTaskSuccess prometheus.Counter - PlanReplayerDumpTaskFailed prometheus.Counter - - PlanReplayerCaptureTaskSendCounter prometheus.Counter - PlanReplayerCaptureTaskDiscardCounter prometheus.Counter - - PlanReplayerRegisterTaskGauge prometheus.Gauge -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init domain metrics vars. -func InitMetricsVars() { - GenerateHistoricalStatsSuccessCounter = metrics.HistoricalStatsCounter.WithLabelValues("generate", "success") - GenerateHistoricalStatsFailedCounter = metrics.HistoricalStatsCounter.WithLabelValues("generate", "fail") - - PlanReplayerDumpTaskSuccess = metrics.PlanReplayerTaskCounter.WithLabelValues("dump", "success") - PlanReplayerDumpTaskFailed = metrics.PlanReplayerTaskCounter.WithLabelValues("dump", "fail") - - PlanReplayerCaptureTaskSendCounter = metrics.PlanReplayerTaskCounter.WithLabelValues("capture", "send") - PlanReplayerCaptureTaskDiscardCounter = metrics.PlanReplayerTaskCounter.WithLabelValues("capture", "discard") - - PlanReplayerRegisterTaskGauge = metrics.PlanReplayerRegisterTaskGauge -} diff --git a/domain/plan_replayer.go b/domain/plan_replayer.go deleted file mode 100644 index 9e7f2f586cffc..0000000000000 --- a/domain/plan_replayer.go +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package domain - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/domain/infosync" - domain_metrics "github.com/pingcap/tidb/domain/metrics" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// dumpFileGcChecker is used to gc dump file in circle -// For now it is used by `plan replayer` and `trace plan` statement -type dumpFileGcChecker struct { - sync.Mutex - gcLease time.Duration - paths []string - sctx sessionctx.Context - planReplayerTaskStatus *planReplayerDumpTaskStatus -} - -func parseType(s string) string { - return strings.Split(s, "_")[0] -} - -func parseTime(s string) (time.Time, error) { - startIdx := strings.LastIndex(s, "_") - if startIdx == -1 { - return time.Time{}, errors.New("failed to parse the file :" + s) - } - endIdx := strings.LastIndex(s, ".") - if endIdx == -1 || endIdx <= startIdx+1 { - return time.Time{}, errors.New("failed to parse the file :" + s) - } - i, err := strconv.ParseInt(s[startIdx+1:endIdx], 10, 64) - if err != nil { - return time.Time{}, errors.New("failed to parse the file :" + s) - } - return time.Unix(0, i), nil -} - -// GCDumpFiles periodically cleans the outdated files for plan replayer and plan trace. -func (p *dumpFileGcChecker) GCDumpFiles(gcDurationDefault, gcDurationForCapture time.Duration) { - p.Lock() - defer p.Unlock() - for _, path := range p.paths { - p.gcDumpFilesByPath(path, gcDurationDefault, gcDurationForCapture) - } -} - -func (p *dumpFileGcChecker) setupSctx(sctx sessionctx.Context) { - p.sctx = sctx -} - -func (p *dumpFileGcChecker) gcDumpFilesByPath(path string, gcDurationDefault, gcDurationForCapture time.Duration) { - entries, err := os.ReadDir(path) - if err != nil { - if !os.IsNotExist(err) { - absPath, err2 := filepath.Abs(path) - if err2 != nil { - logutil.BgLogger().Warn("failed to get absolute path", - zap.Error(err2), zap.String("category", "dumpFileGcChecker")) - absPath = path - } - logutil.BgLogger().Warn("open plan replayer directory failed", - zap.Error(err), zap.String("category", "dumpFileGcChecker"), - zap.String("path", absPath)) - } - } - - gcTargetTimeDefault := time.Now().Add(-gcDurationDefault) - gcTargetTimeForCapture := time.Now().Add(-gcDurationForCapture) - for _, entry := range entries { - f, err := entry.Info() - if err != nil { - logutil.BgLogger().Warn("open plan replayer directory failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err)) - } - fileName := f.Name() - createTime, err := parseTime(fileName) - if err != nil { - logutil.BgLogger().Error("parseTime failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err), zap.String("filename", fileName)) - continue - } - isPlanReplayer := strings.Contains(fileName, "replayer") - isPlanReplayerCapture := strings.Contains(fileName, "capture") - canGC := false - if isPlanReplayer && isPlanReplayerCapture { - canGC = !createTime.After(gcTargetTimeForCapture) - } else { - canGC = !createTime.After(gcTargetTimeDefault) - } - if canGC { - err := os.Remove(filepath.Join(path, f.Name())) - if err != nil { - logutil.BgLogger().Warn("remove file failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err), zap.String("filename", fileName)) - continue - } - logutil.BgLogger().Info("dumpFileGcChecker successful", zap.String("filename", fileName)) - if isPlanReplayer && p.sctx != nil { - deletePlanReplayerStatus(context.Background(), p.sctx, fileName) - p.planReplayerTaskStatus.clearFinishedTask() - } - } - } -} - -func deletePlanReplayerStatus(ctx context.Context, sctx sessionctx.Context, token string) { - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - exec := sctx.(sqlexec.RestrictedSQLExecutor) - _, _, err := exec.ExecRestrictedSQL(ctx1, nil, "delete from mysql.plan_replayer_status where token = %?", token) - if err != nil { - logutil.BgLogger().Warn("delete mysql.plan_replayer_status record failed", zap.String("token", token), zap.Error(err)) - } -} - -// insertPlanReplayerStatus insert mysql.plan_replayer_status record -func insertPlanReplayerStatus(ctx context.Context, sctx sessionctx.Context, records []PlanReplayerStatusRecord) { - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - var instance string - serverInfo, err := infosync.GetServerInfo() - if err != nil { - logutil.BgLogger().Error("failed to get server info", zap.Error(err)) - instance = "unknown" - } else { - instance = fmt.Sprintf("%s:%d", serverInfo.IP, serverInfo.Port) - } - for _, record := range records { - if len(record.FailedReason) > 0 { - insertPlanReplayerErrorStatusRecord(ctx1, sctx, instance, record) - } else { - insertPlanReplayerSuccessStatusRecord(ctx1, sctx, instance, record) - } - } -} - -func insertPlanReplayerErrorStatusRecord(ctx context.Context, sctx sessionctx.Context, instance string, record PlanReplayerStatusRecord) { - exec := sctx.(sqlexec.RestrictedSQLExecutor) - _, _, err := exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( - "insert into mysql.plan_replayer_status (sql_digest, plan_digest, origin_sql, fail_reason, instance) values ('%s','%s','%s','%s','%s')", - record.SQLDigest, record.PlanDigest, record.OriginSQL, record.FailedReason, instance)) - if err != nil { - logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", - zap.Error(err)) - } -} - -func insertPlanReplayerSuccessStatusRecord(ctx context.Context, sctx sessionctx.Context, instance string, record PlanReplayerStatusRecord) { - exec := sctx.(sqlexec.RestrictedSQLExecutor) - _, _, err := exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( - "insert into mysql.plan_replayer_status (sql_digest, plan_digest, origin_sql, token, instance) values ('%s','%s','%s','%s','%s')", - record.SQLDigest, record.PlanDigest, record.OriginSQL, record.Token, instance)) - if err != nil { - logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", - zap.String("sql", record.OriginSQL), - zap.Error(err)) - // try insert record without original sql - _, _, err = exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( - "insert into mysql.plan_replayer_status (sql_digest, plan_digest, token, instance) values ('%s','%s','%s','%s')", - record.SQLDigest, record.PlanDigest, record.Token, instance)) - if err != nil { - logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", - zap.String("sqlDigest", record.SQLDigest), - zap.String("planDigest", record.PlanDigest), - zap.Error(err)) - } - } -} - -type planReplayerHandle struct { - *planReplayerTaskCollectorHandle - *planReplayerTaskDumpHandle -} - -// SendTask send dumpTask in background task handler -func (h *planReplayerHandle) SendTask(task *PlanReplayerDumpTask) bool { - select { - case h.planReplayerTaskDumpHandle.taskCH <- task: - // we directly remove the task key if we put task in channel successfully, if the task was failed to dump, - // the task handle will re-add the task in next loop - if !task.IsContinuesCapture { - h.planReplayerTaskCollectorHandle.removeTask(task.PlanReplayerTaskKey) - } - domain_metrics.PlanReplayerCaptureTaskSendCounter.Inc() - return true - default: - domain_metrics.PlanReplayerCaptureTaskDiscardCounter.Inc() - // directly discard the task if the task channel is full in order not to block the query process - logutil.BgLogger().Warn("discard one plan replayer dump task", - zap.String("sql-digest", task.SQLDigest), zap.String("plan-digest", task.PlanDigest)) - return false - } -} - -type planReplayerTaskCollectorHandle struct { - taskMu struct { - sync.RWMutex - tasks map[replayer.PlanReplayerTaskKey]struct{} - } - ctx context.Context - sctx sessionctx.Context -} - -// CollectPlanReplayerTask collects all unhandled plan replayer task -func (h *planReplayerTaskCollectorHandle) CollectPlanReplayerTask() error { - allKeys, err := h.collectAllPlanReplayerTask(h.ctx) - if err != nil { - return err - } - tasks := make([]replayer.PlanReplayerTaskKey, 0) - for _, key := range allKeys { - unhandled, err := checkUnHandledReplayerTask(h.ctx, h.sctx, key) - if err != nil { - logutil.BgLogger().Warn("collect plan replayer task failed", zap.String("category", "plan-replayer-task"), zap.Error(err)) - return err - } - if unhandled { - logutil.BgLogger().Debug("collect plan replayer task success", zap.String("category", "plan-replayer-task"), - zap.String("sql-digest", key.SQLDigest), - zap.String("plan-digest", key.PlanDigest)) - tasks = append(tasks, key) - } - } - h.setupTasks(tasks) - domain_metrics.PlanReplayerRegisterTaskGauge.Set(float64(len(tasks))) - return nil -} - -// GetTasks get all tasks -func (h *planReplayerTaskCollectorHandle) GetTasks() []replayer.PlanReplayerTaskKey { - tasks := make([]replayer.PlanReplayerTaskKey, 0) - h.taskMu.RLock() - defer h.taskMu.RUnlock() - for taskKey := range h.taskMu.tasks { - tasks = append(tasks, taskKey) - } - return tasks -} - -func (h *planReplayerTaskCollectorHandle) setupTasks(tasks []replayer.PlanReplayerTaskKey) { - r := make(map[replayer.PlanReplayerTaskKey]struct{}) - for _, task := range tasks { - r[task] = struct{}{} - } - h.taskMu.Lock() - defer h.taskMu.Unlock() - h.taskMu.tasks = r -} - -func (h *planReplayerTaskCollectorHandle) removeTask(taskKey replayer.PlanReplayerTaskKey) { - h.taskMu.Lock() - defer h.taskMu.Unlock() - delete(h.taskMu.tasks, taskKey) -} - -func (h *planReplayerTaskCollectorHandle) collectAllPlanReplayerTask(ctx context.Context) ([]replayer.PlanReplayerTaskKey, error) { - exec := h.sctx.(sqlexec.SQLExecutor) - rs, err := exec.ExecuteInternal(ctx, "select sql_digest, plan_digest from mysql.plan_replayer_task") - if err != nil { - return nil, err - } - if rs == nil { - return nil, nil - } - var rows []chunk.Row - defer terror.Call(rs.Close) - if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { - return nil, errors.Trace(err) - } - allKeys := make([]replayer.PlanReplayerTaskKey, 0, len(rows)) - for _, row := range rows { - sqlDigest, planDigest := row.GetString(0), row.GetString(1) - allKeys = append(allKeys, replayer.PlanReplayerTaskKey{ - SQLDigest: sqlDigest, - PlanDigest: planDigest, - }) - } - return allKeys, nil -} - -type planReplayerDumpTaskStatus struct { - // running task records the task running by all workers in order to avoid multi workers running the same task key - runningTaskMu struct { - sync.RWMutex - runningTasks map[replayer.PlanReplayerTaskKey]struct{} - } - - // finished task records the finished task in order to avoid running finished task key - finishedTaskMu struct { - sync.RWMutex - finishedTask map[replayer.PlanReplayerTaskKey]struct{} - } -} - -// GetRunningTaskStatusLen used for unit test -func (r *planReplayerDumpTaskStatus) GetRunningTaskStatusLen() int { - r.runningTaskMu.RLock() - defer r.runningTaskMu.RUnlock() - return len(r.runningTaskMu.runningTasks) -} - -// CleanFinishedTaskStatus clean then finished tasks, only used for unit test -func (r *planReplayerDumpTaskStatus) CleanFinishedTaskStatus() { - r.finishedTaskMu.Lock() - defer r.finishedTaskMu.Unlock() - r.finishedTaskMu.finishedTask = map[replayer.PlanReplayerTaskKey]struct{}{} -} - -// GetFinishedTaskStatusLen used for unit test -func (r *planReplayerDumpTaskStatus) GetFinishedTaskStatusLen() int { - r.finishedTaskMu.RLock() - defer r.finishedTaskMu.RUnlock() - return len(r.finishedTaskMu.finishedTask) -} - -func (r *planReplayerDumpTaskStatus) occupyRunningTaskKey(task *PlanReplayerDumpTask) bool { - r.runningTaskMu.Lock() - defer r.runningTaskMu.Unlock() - _, ok := r.runningTaskMu.runningTasks[task.PlanReplayerTaskKey] - if ok { - return false - } - r.runningTaskMu.runningTasks[task.PlanReplayerTaskKey] = struct{}{} - return true -} - -func (r *planReplayerDumpTaskStatus) releaseRunningTaskKey(task *PlanReplayerDumpTask) { - r.runningTaskMu.Lock() - defer r.runningTaskMu.Unlock() - delete(r.runningTaskMu.runningTasks, task.PlanReplayerTaskKey) -} - -func (r *planReplayerDumpTaskStatus) checkTaskKeyFinishedBefore(task *PlanReplayerDumpTask) bool { - r.finishedTaskMu.RLock() - defer r.finishedTaskMu.RUnlock() - _, ok := r.finishedTaskMu.finishedTask[task.PlanReplayerTaskKey] - return ok -} - -func (r *planReplayerDumpTaskStatus) setTaskFinished(task *PlanReplayerDumpTask) { - r.finishedTaskMu.Lock() - defer r.finishedTaskMu.Unlock() - r.finishedTaskMu.finishedTask[task.PlanReplayerTaskKey] = struct{}{} -} - -func (r *planReplayerDumpTaskStatus) clearFinishedTask() { - r.finishedTaskMu.Lock() - defer r.finishedTaskMu.Unlock() - r.finishedTaskMu.finishedTask = map[replayer.PlanReplayerTaskKey]struct{}{} -} - -type planReplayerTaskDumpWorker struct { - ctx context.Context - sctx sessionctx.Context - taskCH <-chan *PlanReplayerDumpTask - status *planReplayerDumpTaskStatus -} - -func (w *planReplayerTaskDumpWorker) run() { - logutil.BgLogger().Info("planReplayerTaskDumpWorker started.") - for task := range w.taskCH { - w.handleTask(task) - } - logutil.BgLogger().Info("planReplayerTaskDumpWorker exited.") -} - -func (w *planReplayerTaskDumpWorker) handleTask(task *PlanReplayerDumpTask) { - sqlDigest := task.SQLDigest - planDigest := task.PlanDigest - check := true - occupy := true - handleTask := true - defer func() { - logutil.BgLogger().Debug("handle task", zap.String("category", "plan-replayer-capture"), - zap.String("sql-digest", sqlDigest), - zap.String("plan-digest", planDigest), - zap.Bool("check", check), - zap.Bool("occupy", occupy), - zap.Bool("handle", handleTask)) - }() - defer util.Recover(metrics.LabelDomain, "PlanReplayerTaskDumpWorker", nil, false) - - if task.IsContinuesCapture { - if w.status.checkTaskKeyFinishedBefore(task) { - check = false - return - } - } - occupy = w.status.occupyRunningTaskKey(task) - if !occupy { - return - } - handleTask = w.HandleTask(task) - w.status.releaseRunningTaskKey(task) -} - -// HandleTask handled task -func (w *planReplayerTaskDumpWorker) HandleTask(task *PlanReplayerDumpTask) (success bool) { - defer func() { - if success && task.IsContinuesCapture { - w.status.setTaskFinished(task) - } - }() - taskKey := task.PlanReplayerTaskKey - unhandled, err := checkUnHandledReplayerTask(w.ctx, w.sctx, taskKey) - if err != nil { - logutil.BgLogger().Warn("check task failed", zap.String("category", "plan-replayer-capture"), - zap.String("sqlDigest", taskKey.SQLDigest), - zap.String("planDigest", taskKey.PlanDigest), - zap.Error(err)) - return false - } - // the task is processed, thus we directly skip it. - if !unhandled { - return true - } - - file, fileName, err := replayer.GeneratePlanReplayerFile(task.IsCapture, task.IsContinuesCapture, variable.EnableHistoricalStatsForCapture.Load()) - if err != nil { - logutil.BgLogger().Warn("generate task file failed", zap.String("category", "plan-replayer-capture"), - zap.String("sqlDigest", taskKey.SQLDigest), - zap.String("planDigest", taskKey.PlanDigest), - zap.Error(err)) - return false - } - task.Zf = file - task.FileName = fileName - err = DumpPlanReplayerInfo(w.ctx, w.sctx, task) - if err != nil { - logutil.BgLogger().Warn("dump task result failed", zap.String("category", "plan-replayer-capture"), - zap.String("sqlDigest", taskKey.SQLDigest), - zap.String("planDigest", taskKey.PlanDigest), - zap.Error(err)) - return false - } - return true -} - -type planReplayerTaskDumpHandle struct { - taskCH chan *PlanReplayerDumpTask - status *planReplayerDumpTaskStatus - workers []*planReplayerTaskDumpWorker -} - -// GetTaskStatus used for test -func (h *planReplayerTaskDumpHandle) GetTaskStatus() *planReplayerDumpTaskStatus { - return h.status -} - -// GetWorker used for test -func (h *planReplayerTaskDumpHandle) GetWorker() *planReplayerTaskDumpWorker { - return h.workers[0] -} - -// Close make finished flag ture -func (h *planReplayerTaskDumpHandle) Close() { - close(h.taskCH) -} - -// DrainTask drain a task for unit test -func (h *planReplayerTaskDumpHandle) DrainTask() *PlanReplayerDumpTask { - return <-h.taskCH -} - -func checkUnHandledReplayerTask(ctx context.Context, sctx sessionctx.Context, task replayer.PlanReplayerTaskKey) (bool, error) { - exec := sctx.(sqlexec.SQLExecutor) - rs, err := exec.ExecuteInternal(ctx, fmt.Sprintf("select * from mysql.plan_replayer_status where sql_digest = '%v' and plan_digest = '%v' and fail_reason is null", task.SQLDigest, task.PlanDigest)) - if err != nil { - return false, err - } - if rs == nil { - return true, nil - } - var rows []chunk.Row - defer terror.Call(rs.Close) - if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { - return false, errors.Trace(err) - } - if len(rows) > 0 { - return false, nil - } - return true, nil -} - -// CheckPlanReplayerTaskExists checks whether plan replayer capture task exists already -func CheckPlanReplayerTaskExists(ctx context.Context, sctx sessionctx.Context, sqlDigest, planDigest string) (bool, error) { - exec := sctx.(sqlexec.SQLExecutor) - rs, err := exec.ExecuteInternal(ctx, fmt.Sprintf("select * from mysql.plan_replayer_task where sql_digest = '%v' and plan_digest = '%v'", - sqlDigest, planDigest)) - if err != nil { - return false, err - } - if rs == nil { - return false, nil - } - var rows []chunk.Row - defer terror.Call(rs.Close) - if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { - return false, errors.Trace(err) - } - if len(rows) > 0 { - return true, nil - } - return false, nil -} - -// PlanReplayerStatusRecord indicates record in mysql.plan_replayer_status -type PlanReplayerStatusRecord struct { - SQLDigest string - PlanDigest string - OriginSQL string - Token string - FailedReason string -} - -// PlanReplayerDumpTask wrap the params for plan replayer dump -type PlanReplayerDumpTask struct { - replayer.PlanReplayerTaskKey - - // tmp variables stored during the query - TblStats map[int64]interface{} - - // variables used to dump the plan - StartTS uint64 - SessionBindings []*bindinfo.BindRecord - EncodedPlan string - SessionVars *variable.SessionVars - ExecStmts []ast.StmtNode - Analyze bool - HistoricalStatsTS uint64 - DebugTrace []interface{} - - FileName string - Zf *os.File - - // IsCapture indicates whether the task is from capture - IsCapture bool - // IsContinuesCapture indicates whether the task is from continues capture - IsContinuesCapture bool -} diff --git a/domain/plan_replayer_test.go b/domain/plan_replayer_test.go deleted file mode 100644 index 026c3d753a075..0000000000000 --- a/domain/plan_replayer_test.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package domain - -import ( - "fmt" - "path/filepath" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util/replayer" - "github.com/stretchr/testify/require" -) - -func TestPlanReplayerDifferentGC(t *testing.T) { - dirName := replayer.GetPlanReplayerDirName() - - time1 := time.Now().Add(-7 * 25 * time.Hour).UnixNano() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time1))) - file1, fileName1, err := replayer.GeneratePlanReplayerFile(true, false, false) - require.NoError(t, err) - require.NoError(t, file1.Close()) - filePath1 := filepath.Join(dirName, fileName1) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField")) - - time2 := time.Now().Add(-7 * 23 * time.Hour).UnixNano() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time2))) - file2, fileName2, err := replayer.GeneratePlanReplayerFile(true, false, false) - require.NoError(t, err) - require.NoError(t, file2.Close()) - filePath2 := filepath.Join(dirName, fileName2) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField")) - - time3 := time.Now().Add(-2 * time.Hour).UnixNano() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time3))) - file3, fileName3, err := replayer.GeneratePlanReplayerFile(false, false, false) - require.NoError(t, err) - require.NoError(t, file3.Close()) - filePath3 := filepath.Join(dirName, fileName3) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField")) - - time4 := time.Now().UnixNano() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time4))) - file4, fileName4, err := replayer.GeneratePlanReplayerFile(false, false, false) - require.NoError(t, err) - require.NoError(t, file4.Close()) - filePath4 := filepath.Join(dirName, fileName4) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/replayer/InjectPlanReplayerFileNameTimeField")) - - handler := &dumpFileGcChecker{ - paths: []string{dirName}, - } - handler.GCDumpFiles(time.Hour, time.Hour*24*7) - require.NoFileExists(t, filePath1) - require.FileExists(t, filePath2) - require.NoFileExists(t, filePath3) - require.FileExists(t, filePath4) - - handler.GCDumpFiles(0, 0) - require.NoFileExists(t, filePath2) - require.NoFileExists(t, filePath4) -} - -func TestDumpGCFileParseTime(t *testing.T) { - nowTime := time.Now() - name1 := fmt.Sprintf("replayer_single_xxxxxx_%v.zip", nowTime.UnixNano()) - pt, err := parseTime(name1) - require.NoError(t, err) - require.True(t, pt.Equal(nowTime)) - - name2 := fmt.Sprintf("replayer_single_xxxxxx_%v1.zip", nowTime.UnixNano()) - _, err = parseTime(name2) - require.NotNil(t, err) - - name3 := fmt.Sprintf("replayer_single_xxxxxx_%v._zip", nowTime.UnixNano()) - _, err = parseTime(name3) - require.NotNil(t, err) - - name4 := "extract_-brq6zKMarD9ayaifkHc4A==_1678168728477502000.zip" - _, err = parseTime(name4) - require.NoError(t, err) - - var pName string - pName, err = replayer.GeneratePlanReplayerFileName(false, false, false) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(true, false, false) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(false, true, false) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(true, true, false) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(false, false, true) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(true, false, true) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(false, true, true) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) - - pName, err = replayer.GeneratePlanReplayerFileName(true, true, true) - require.NoError(t, err) - _, err = parseTime(pName) - require.NoError(t, err) -} diff --git a/domain/resourcegroup/BUILD.bazel b/domain/resourcegroup/BUILD.bazel deleted file mode 100644 index 00ce1438f19aa..0000000000000 --- a/domain/resourcegroup/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "resourcegroup", - srcs = ["runaway.go"], - importpath = "github.com/pingcap/tidb/domain/resourcegroup", - visibility = ["//visibility:public"], - deps = [ - "//util/dbterror/exeerrors", - "//util/logutil", - "@com_github_jellydator_ttlcache_v3//:ttlcache", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_pd_client//resource_group/controller", - "@org_uber_go_zap//:zap", - ], -) diff --git a/domain/resourcegroup/runaway.go b/domain/resourcegroup/runaway.go deleted file mode 100644 index 2454796be0d07..0000000000000 --- a/domain/resourcegroup/runaway.go +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package resourcegroup - -import ( - "context" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/jellydator/ttlcache/v3" - rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - rmclient "github.com/tikv/pd/client/resource_group/controller" - "go.uber.org/zap" -) - -const ( - // DefaultResourceGroupName is the default resource group name. - DefaultResourceGroupName = "default" - // ManualSource shows the item added manually. - ManualSource = "manual" - // RunawayWatchTableName is the name of system table which save runaway watch items. - RunawayWatchTableName = "mysql.tidb_runaway_watch" - // RunawayWatchDoneTableName is the name of system table which save done runaway watch items. - RunawayWatchDoneTableName = "mysql.tidb_runaway_watch_done" - - // MaxWaitDuration is the max duration to wait for acquiring token buckets. - MaxWaitDuration = time.Second * 30 - maxWatchListCap = 10000 - maxWatchRecordChannelSize = 1024 -) - -// NullTime is a zero time.Time. -var NullTime time.Time - -// RunawayMatchType is used to indicate whether query was interrupted by runaway identification or quarantine watch. -type RunawayMatchType uint - -const ( - // RunawayMatchTypeWatch shows quarantine watch. - RunawayMatchTypeWatch RunawayMatchType = iota - // RunawayMatchTypeIdentify shows identification. - RunawayMatchTypeIdentify -) - -func (t RunawayMatchType) String() string { - switch t { - case RunawayMatchTypeWatch: - return "watch" - case RunawayMatchTypeIdentify: - return "identify" - default: - panic("unknown type") - } -} - -// RunawayRecord is used to save records which will be insert into mysql.tidb_runaway_queries. -type RunawayRecord struct { - ResourceGroupName string - Time time.Time - Match string - Action string - SQLText string - PlanDigest string - Source string -} - -// GenRunawayQueriesStmt generates statement with given RunawayRecords. -func GenRunawayQueriesStmt(records []*RunawayRecord) (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, len(records)*7) - builder.WriteString("insert into mysql.tidb_runaway_queries VALUES ") - for count, r := range records { - if count > 0 { - builder.WriteByte(',') - } - builder.WriteString("(%?, %?, %?, %?, %?, %?, %?)") - params = append(params, r.ResourceGroupName) - params = append(params, r.Time) - params = append(params, r.Match) - params = append(params, r.Action) - params = append(params, r.SQLText) - params = append(params, r.PlanDigest) - params = append(params, r.Source) - } - return builder.String(), params -} - -// QuarantineRecord is used to save records which will be insert into mysql.tidb_runaway_watch. -type QuarantineRecord struct { - ID int64 - ResourceGroupName string - StartTime time.Time - EndTime time.Time - Watch rmpb.RunawayWatchType - WatchText string - Source string - Action rmpb.RunawayAction -} - -// GetRecordKey is used to get the key in ttl cache. -func (r *QuarantineRecord) GetRecordKey() string { - return r.ResourceGroupName + "/" + r.WatchText -} - -func writeInsert(builder *strings.Builder, tableName string) { - builder.WriteString("insert into ") - builder.WriteString(tableName) - builder.WriteString(" VALUES ") -} - -// GenInsertionStmt is used to generate insertion sql. -func (r *QuarantineRecord) GenInsertionStmt() (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, 6) - writeInsert(&builder, RunawayWatchTableName) - builder.WriteString("(null, %?, %?, %?, %?, %?, %?, %?)") - params = append(params, r.ResourceGroupName) - params = append(params, r.StartTime) - if r.EndTime.Equal(NullTime) { - params = append(params, nil) - } else { - params = append(params, r.EndTime) - } - params = append(params, r.Watch) - params = append(params, r.WatchText) - params = append(params, r.Source) - params = append(params, r.Action) - return builder.String(), params -} - -// GenInsertionDoneStmt is used to generate insertion sql for runaway watch done record. -func (r *QuarantineRecord) GenInsertionDoneStmt() (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, 9) - writeInsert(&builder, RunawayWatchDoneTableName) - builder.WriteString("(null, %?, %?, %?, %?, %?, %?, %?, %?, %?)") - params = append(params, r.ID) - params = append(params, r.ResourceGroupName) - params = append(params, r.StartTime) - if r.EndTime.Equal(NullTime) { - params = append(params, nil) - } else { - params = append(params, r.EndTime) - } - params = append(params, r.Watch) - params = append(params, r.WatchText) - params = append(params, r.Source) - params = append(params, r.Action) - params = append(params, time.Now().UTC()) - return builder.String(), params -} - -// GenDeletionStmt is used to generate deletion sql. -func (r *QuarantineRecord) GenDeletionStmt() (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, 1) - builder.WriteString("delete from ") - builder.WriteString(RunawayWatchTableName) - builder.WriteString(" where id = %?") - params = append(params, r.ID) - return builder.String(), params -} - -// RunawayManager is used to detect and record runaway queries. -type RunawayManager struct { - // queryLock is used to avoid repeated additions. Since we will add new items to the system table, - // in order to avoid repeated additions, we need a lock to ensure that - // action "judging whether there is this record in the current watch list and adding records" have atomicity. - queryLock sync.Mutex - watchList *ttlcache.Cache[string, *QuarantineRecord] - // activeGroup is used to manage the active runaway watches of resource group - activeGroup map[string]int64 - activeLock sync.RWMutex - - resourceGroupCtl *rmclient.ResourceGroupsController - serverID string - runawayQueriesChan chan *RunawayRecord - quarantineChan chan *QuarantineRecord - // staleQuarantineRecord is used to clean outdated record. There are three scenarios: - // 1. Record is expired in watch list. - // 2. The record that will be added is itself out of date. - // Like that tidb cluster is paused, and record is expired when restarting. - // 3. Duplicate added records. - // It replaces clean up loop. - staleQuarantineRecord chan *QuarantineRecord - evictionCancel func() - insertionCancel func() -} - -// NewRunawayManager creates a new RunawayManager. -func NewRunawayManager(resourceGroupCtl *rmclient.ResourceGroupsController, serverAddr string) *RunawayManager { - watchList := ttlcache.New[string, *QuarantineRecord]( - ttlcache.WithTTL[string, *QuarantineRecord](ttlcache.NoTTL), - ttlcache.WithCapacity[string, *QuarantineRecord](maxWatchListCap), - ttlcache.WithDisableTouchOnHit[string, *QuarantineRecord](), - ) - go watchList.Start() - staleQuarantineChan := make(chan *QuarantineRecord, maxWatchRecordChannelSize) - m := &RunawayManager{ - resourceGroupCtl: resourceGroupCtl, - watchList: watchList, - serverID: serverAddr, - runawayQueriesChan: make(chan *RunawayRecord, maxWatchRecordChannelSize), - quarantineChan: make(chan *QuarantineRecord, maxWatchRecordChannelSize), - staleQuarantineRecord: staleQuarantineChan, - activeGroup: make(map[string]int64), - } - m.insertionCancel = watchList.OnInsertion(func(ctx context.Context, i *ttlcache.Item[string, *QuarantineRecord]) { - m.activeLock.Lock() - m.activeGroup[i.Value().ResourceGroupName]++ - m.activeLock.Unlock() - }) - m.evictionCancel = watchList.OnEviction(func(ctx context.Context, er ttlcache.EvictionReason, i *ttlcache.Item[string, *QuarantineRecord]) { - m.activeLock.Lock() - m.activeGroup[i.Value().ResourceGroupName]-- - m.activeLock.Unlock() - if i.Value().ID == 0 { - return - } - staleQuarantineChan <- i.Value() - }) - return m -} - -// DeriveChecker derives a RunawayChecker from the given resource group -func (rm *RunawayManager) DeriveChecker(resourceGroupName, originalSQL, sqlDigest, planDigest string) *RunawayChecker { - group, err := rm.resourceGroupCtl.GetResourceGroup(resourceGroupName) - if err != nil || group == nil { - logutil.BgLogger().Warn("cannot setup up runaway checker", zap.Error(err)) - return nil - } - rm.activeLock.RLock() - defer rm.activeLock.RUnlock() - if group.RunawaySettings == nil && rm.activeGroup[resourceGroupName] == 0 { - return nil - } - return newRunawayChecker(rm, resourceGroupName, group.RunawaySettings, originalSQL, sqlDigest, planDigest) -} - -func (rm *RunawayManager) markQuarantine(resourceGroupName, convict string, watchType rmpb.RunawayWatchType, action rmpb.RunawayAction, ttl time.Duration, now *time.Time) { - var endTime time.Time - if ttl > 0 { - endTime = now.UTC().Add(ttl) - } - record := &QuarantineRecord{ - ResourceGroupName: resourceGroupName, - StartTime: now.UTC(), - EndTime: endTime, - Watch: watchType, - WatchText: convict, - Source: rm.serverID, - Action: action, - } - // Add record without ID into watch list in this TiDB right now. - rm.addWatchList(record, ttl, false) - select { - case rm.quarantineChan <- record: - default: - // TODO: add warning for discard flush records - } -} - -func (rm *RunawayManager) addWatchList(record *QuarantineRecord, ttl time.Duration, force bool) { - key := record.GetRecordKey() - // This is a pre-check, because we generally believe that in most cases, we will not add a watch list to a key repeatedly. - item := rm.getWatchFromWatchList(key) - if force { - rm.queryLock.Lock() - defer rm.queryLock.Unlock() - if item != nil { - // check the ID because of the eariler scan. - if item.ID == record.ID { - return - } - rm.watchList.Delete(key) - } - rm.watchList.Set(key, record, ttl) - } else { - if item == nil { - rm.queryLock.Lock() - // When watchlist get record, it will check whether the record is stale, so add new record if returns nil. - if rm.watchList.Get(key) == nil { - rm.watchList.Set(key, record, ttl) - } else { - rm.staleQuarantineRecord <- record - } - rm.queryLock.Unlock() - } else if item.ID == 0 { - // to replace the record without ID. - rm.queryLock.Lock() - defer rm.queryLock.Unlock() - rm.watchList.Set(key, record, ttl) - } else if item.ID != record.ID { - // check the ID because of the eariler scan. - rm.staleQuarantineRecord <- record - } - } -} - -// GetWatchByKey is used to get a watch item by given key. -func (rm *RunawayManager) GetWatchByKey(key string) *QuarantineRecord { - return rm.getWatchFromWatchList(key) -} - -// GetWatchList is used to get all watch items. -func (rm *RunawayManager) GetWatchList() []*QuarantineRecord { - items := rm.watchList.Items() - ret := make([]*QuarantineRecord, 0, len(items)) - for _, item := range items { - ret = append(ret, item.Value()) - } - return ret -} - -// AddWatch is used to add watch items from system table. -func (rm *RunawayManager) AddWatch(record *QuarantineRecord) { - ttl := time.Until(record.EndTime) - if record.EndTime.Equal(NullTime) { - ttl = 0 - } else if ttl <= 0 { - rm.staleQuarantineRecord <- record - return - } - - force := false - // The manual record replaces the old record. - force = record.Source == ManualSource - rm.addWatchList(record, ttl, force) -} - -// RemoveWatch is used to remove watch item, and this action is triggered by reading done watch system table. -func (rm *RunawayManager) RemoveWatch(record *QuarantineRecord) { - // we should check whether the cached record is not the same as the removing record. - rm.queryLock.Lock() - defer rm.queryLock.Unlock() - item := rm.getWatchFromWatchList(record.GetRecordKey()) - if item == nil { - return - } - if item.ID == record.ID { - rm.watchList.Delete(record.GetRecordKey()) - } -} -func (rm *RunawayManager) getWatchFromWatchList(key string) *QuarantineRecord { - item := rm.watchList.Get(key) - if item != nil { - return item.Value() - } - return nil -} - -func (rm *RunawayManager) markRunaway(resourceGroupName, originalSQL, planDigest string, action string, matchType RunawayMatchType, now *time.Time) { - source := rm.serverID - select { - case rm.runawayQueriesChan <- &RunawayRecord{ - ResourceGroupName: resourceGroupName, - Time: *now, - Match: matchType.String(), - Action: action, - SQLText: originalSQL, - PlanDigest: planDigest, - Source: source, - }: - default: - // TODO: add warning for discard flush records - } -} - -// FlushThreshold specifies the threshold for the number of records in trigger flush -func (rm *RunawayManager) FlushThreshold() int { - return maxWatchRecordChannelSize / 2 -} - -// RunawayRecordChan returns the channel of RunawayRecord -func (rm *RunawayManager) RunawayRecordChan() <-chan *RunawayRecord { - return rm.runawayQueriesChan -} - -// QuarantineRecordChan returns the channel of QuarantineRecord -func (rm *RunawayManager) QuarantineRecordChan() <-chan *QuarantineRecord { - return rm.quarantineChan -} - -// StaleQuarantineRecordChan returns the channel of staleQuarantineRecord -func (rm *RunawayManager) StaleQuarantineRecordChan() <-chan *QuarantineRecord { - return rm.staleQuarantineRecord -} - -// examineWatchList check whether the query is in watch list. -func (rm *RunawayManager) examineWatchList(resourceGroupName string, convict string) (bool, rmpb.RunawayAction) { - item := rm.getWatchFromWatchList(resourceGroupName + "/" + convict) - if item == nil { - return false, 0 - } - return true, item.Action -} - -// Stop stops the watchList which is a ttlcache. -func (rm *RunawayManager) Stop() { - if rm.watchList != nil { - rm.watchList.Stop() - } -} - -// RunawayChecker is used to check if the query is runaway. -type RunawayChecker struct { - manager *RunawayManager - resourceGroupName string - originalSQL string - sqlDigest string - planDigest string - - deadline time.Time - setting *rmpb.RunawaySettings - - marked atomic.Bool -} - -func newRunawayChecker(manager *RunawayManager, resourceGroupName string, setting *rmpb.RunawaySettings, originalSQL, sqlDigest, planDigest string) *RunawayChecker { - c := &RunawayChecker{ - manager: manager, - resourceGroupName: resourceGroupName, - originalSQL: originalSQL, - sqlDigest: sqlDigest, - planDigest: planDigest, - setting: setting, - marked: atomic.Bool{}, - } - if setting != nil { - c.deadline = time.Now().Add(time.Duration(setting.Rule.ExecElapsedTimeMs) * time.Millisecond) - } - return c -} - -// BeforeExecutor checks whether query is in watch list before executing and after compiling. -func (r *RunawayChecker) BeforeExecutor() error { - if r == nil { - return nil - } - for _, convict := range r.getConvictIdentifiers() { - watched, action := r.manager.examineWatchList(r.resourceGroupName, convict) - if watched { - if action == rmpb.RunawayAction_NoneAction && r.setting != nil { - action = r.setting.Action - } - if r.marked.CompareAndSwap(false, true) { - now := time.Now() - r.markRunaway(RunawayMatchTypeWatch, action, &now) - } - // If no match action, it will do nothing. - switch action { - case rmpb.RunawayAction_Kill: - return exeerrors.ErrResourceGroupQueryRunawayQuarantine - case rmpb.RunawayAction_CoolDown: - return nil - case rmpb.RunawayAction_DryRun: - return nil - default: - } - } - } - return nil -} - -// BeforeCopRequest checks runaway and modifies the request if necessary before sending coprocessor request. -func (r *RunawayChecker) BeforeCopRequest(req *tikvrpc.Request) error { - if r.setting == nil { - return nil - } - marked := r.marked.Load() - if !marked { - // note: now we don't check whether query is in watch list again. - until := time.Until(r.deadline) - if until > 0 { - if r.setting.Action == rmpb.RunawayAction_Kill { - // if the execution time is close to the threshold, set a timeout - if until < tikv.ReadTimeoutMedium { - req.Context.MaxExecutionDurationMs = uint64(until.Milliseconds()) - } - } - return nil - } - // execution time exceeds the threshold, mark the query as runaway - if r.marked.CompareAndSwap(false, true) { - now := time.Now() - r.markRunaway(RunawayMatchTypeIdentify, r.setting.Action, &now) - r.markQuarantine(&now) - } - } - switch r.setting.Action { - case rmpb.RunawayAction_Kill: - return exeerrors.ErrResourceGroupQueryRunawayInterrupted - case rmpb.RunawayAction_CoolDown: - req.ResourceControlContext.OverridePriority = 1 // set priority to lowest - return nil - case rmpb.RunawayAction_DryRun: - return nil - default: - return nil - } -} - -// AfterCopRequest checks runaway after receiving coprocessor response. -func (r *RunawayChecker) AfterCopRequest() { - if r.setting == nil { - return - } - // Do not perform action here as it may be the last cop request and just let it finish. If it's not the last cop request, action would be performed in `BeforeCopRequest` when handling the next cop request. - // Here only marks the query as runaway - if !r.marked.Load() && r.deadline.Before(time.Now()) { - if r.marked.CompareAndSwap(false, true) { - now := time.Now() - r.markRunaway(RunawayMatchTypeIdentify, r.setting.Action, &now) - r.markQuarantine(&now) - } - } -} - -func (r *RunawayChecker) markQuarantine(now *time.Time) { - if r.setting.Watch == nil { - return - } - ttl := time.Duration(r.setting.Watch.LastingDurationMs) * time.Millisecond - - r.manager.markQuarantine(r.resourceGroupName, r.getSettingConvictIdentifier(), r.setting.Watch.Type, r.setting.Action, ttl, now) -} - -func (r *RunawayChecker) markRunaway(matchType RunawayMatchType, action rmpb.RunawayAction, now *time.Time) { - r.manager.markRunaway(r.resourceGroupName, r.originalSQL, r.planDigest, strings.ToLower(rmpb.RunawayAction_name[int32(action)]), matchType, now) -} - -func (r *RunawayChecker) getSettingConvictIdentifier() string { - if r.setting.Watch == nil { - return "" - } - switch r.setting.Watch.Type { - case rmpb.RunawayWatchType_Plan: - return r.planDigest - case rmpb.RunawayWatchType_Similar: - return r.sqlDigest - case rmpb.RunawayWatchType_Exact: - return r.originalSQL - default: - return "" - } -} - -func (r *RunawayChecker) getConvictIdentifiers() []string { - return []string{r.originalSQL, r.sqlDigest, r.planDigest} -} diff --git a/domain/runaway.go b/domain/runaway.go deleted file mode 100644 index 0840b51f1fdce..0000000000000 --- a/domain/runaway.go +++ /dev/null @@ -1,592 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package domain - -import ( - "context" - "net" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/sqlbuilder" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/tikv" - pd "github.com/tikv/pd/client" - rmclient "github.com/tikv/pd/client/resource_group/controller" - "go.uber.org/zap" -) - -const ( - runawayRecordFlushInterval = time.Second - runawayRecordGCInterval = time.Hour * 24 - runawayRecordExpiredDuration = time.Hour * 24 * 7 - runawayWatchSyncInterval = time.Second - - runawayRecordGCBatchSize = 100 - runawayRecordGCSelectBatchSize = runawayRecordGCBatchSize * 5 -) - -var systemSchemaCIStr = model.NewCIStr("mysql") - -func (do *Domain) deleteExpiredRows(tableName, colName string, expiredDuration time.Duration) { - if !do.DDL().OwnerManager().IsOwner() { - return - } - failpoint.Inject("FastRunawayGC", func() { - expiredDuration = time.Second * 1 - }) - expiredTime := time.Now().Add(-expiredDuration) - tbCIStr := model.NewCIStr(tableName) - tbl, err := do.InfoSchema().TableByName(systemSchemaCIStr, tbCIStr) - if err != nil { - logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) - return - } - tbInfo := tbl.Meta() - col := tbInfo.FindPublicColumnByName(colName) - if col == nil { - logutil.BgLogger().Error("time column is not public in table", zap.String("table", tableName), zap.String("column", colName)) - return - } - tb, err := cache.NewBasePhysicalTable(systemSchemaCIStr, tbInfo, model.NewCIStr(""), col) - if err != nil { - logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) - return - } - generator, err := sqlbuilder.NewScanQueryGenerator(tb, expiredTime, nil, nil) - if err != nil { - logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) - return - } - var leftRows [][]types.Datum - for { - sql := "" - if sql, err = generator.NextSQL(leftRows, runawayRecordGCSelectBatchSize); err != nil { - logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) - return - } - // to remove - if len(sql) == 0 { - return - } - - rows, sqlErr := do.execRestrictedSQL(sql, nil) - if sqlErr != nil { - logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) - return - } - leftRows = make([][]types.Datum, len(rows)) - for i, row := range rows { - leftRows[i] = row.GetDatumRow(tb.KeyColumnTypes) - } - - for len(leftRows) > 0 { - var delBatch [][]types.Datum - if len(leftRows) < runawayRecordGCBatchSize { - delBatch = leftRows - leftRows = nil - } else { - delBatch = leftRows[0:runawayRecordGCBatchSize] - leftRows = leftRows[runawayRecordGCBatchSize:] - } - sql, err := sqlbuilder.BuildDeleteSQL(tb, delBatch, expiredTime) - if err != nil { - logutil.BgLogger().Error( - "build delete SQL failed when deleting system table", - zap.Error(err), - zap.String("table", tb.Schema.O+"."+tb.Name.O), - ) - return - } - - _, err = do.execRestrictedSQL(sql, nil) - if err != nil { - logutil.BgLogger().Error( - "delete SQL failed when deleting system table", zap.Error(err), zap.String("SQL", sql), - ) - } - } - } -} - -func (do *Domain) updateNewAndDoneWatch() error { - do.runawaySyncer.mu.Lock() - defer do.runawaySyncer.mu.Unlock() - records, err := do.runawaySyncer.getNewWatchRecords() - if err != nil { - logutil.BgLogger().Error("try to get new runaway watch", zap.Error(err)) - return err - } - for _, r := range records { - do.runawayManager.AddWatch(r) - } - doneRecords, err := do.runawaySyncer.getNewWatchDoneRecords() - if err != nil { - logutil.BgLogger().Error("try to get done runaway watch", zap.Error(err)) - return err - } - for _, r := range doneRecords { - do.runawayManager.RemoveWatch(r) - } - return nil -} - -func (do *Domain) runawayWatchSyncLoop() { - defer util.Recover(metrics.LabelDomain, "runawayWatchSyncLoop", nil, false) - runawayWatchSyncTicker := time.NewTicker(runawayWatchSyncInterval) - for { - select { - case <-do.exit: - return - case <-runawayWatchSyncTicker.C: - err := do.updateNewAndDoneWatch() - if err != nil { - logutil.BgLogger().Warn("get runaway watch record failed", zap.Error(err)) - } - } - } -} - -// AddRunawayWatch is used to add runaway watch item manually. -func (do *Domain) AddRunawayWatch(record *resourcegroup.QuarantineRecord) error { - return do.handleRunawayWatch(record) -} - -// GetRunawayWatchList is used to get all items from runaway watch list. -func (do *Domain) GetRunawayWatchList() []*resourcegroup.QuarantineRecord { - return do.runawayManager.GetWatchList() -} - -// TryToUpdateRunawayWatch is used to to update watch list including -// creation and deletion by manual trigger. -func (do *Domain) TryToUpdateRunawayWatch() error { - return do.updateNewAndDoneWatch() -} - -// RemoveRunawayWatch is used to remove runaway watch item manually. -func (do *Domain) RemoveRunawayWatch(recordID int64) error { - do.runawaySyncer.mu.Lock() - defer do.runawaySyncer.mu.Unlock() - records, err := do.runawaySyncer.getWatchRecordByID(recordID) - if err != nil { - return err - } - if len(records) != 1 { - return errors.Errorf("no runaway watch with the specific ID") - } - err = do.handleRunawayWatchDone(records[0]) - return err -} - -func (do *Domain) runawayRecordFlushLoop() { - defer util.Recover(metrics.LabelDomain, "runawayRecordFlushLoop", nil, false) - - // this times is used to batch flushing rocords, with 1s duration, - // we can guarantee a watch record can be seen by the user within 1s. - runawayRecordFluashTimer := time.NewTimer(runawayRecordFlushInterval) - runawayRecordGCTicker := time.NewTicker(runawayRecordGCInterval) - failpoint.Inject("FastRunawayGC", func() { - runawayRecordFluashTimer.Stop() - runawayRecordGCTicker.Stop() - runawayRecordFluashTimer = time.NewTimer(time.Millisecond * 50) - runawayRecordGCTicker = time.NewTicker(time.Millisecond * 200) - }) - - fired := false - recordCh := do.RunawayManager().RunawayRecordChan() - quarantineRecordCh := do.RunawayManager().QuarantineRecordChan() - staleQuarantineRecordCh := do.RunawayManager().StaleQuarantineRecordChan() - flushThrehold := do.runawayManager.FlushThreshold() - records := make([]*resourcegroup.RunawayRecord, 0, flushThrehold) - - flushRunawayRecords := func() { - if len(records) == 0 { - return - } - sql, params := resourcegroup.GenRunawayQueriesStmt(records) - if _, err := do.execRestrictedSQL(sql, params); err != nil { - logutil.BgLogger().Error("flush runaway records failed", zap.Error(err), zap.Int("count", len(records))) - } - records = records[:0] - } - - for { - select { - case <-do.exit: - return - case <-runawayRecordFluashTimer.C: - flushRunawayRecords() - fired = true - case r := <-recordCh: - records = append(records, r) - failpoint.Inject("FastRunawayGC", func() { - flushRunawayRecords() - }) - if len(records) >= flushThrehold { - flushRunawayRecords() - } else if fired { - fired = false - // meet a new record, reset the timer. - runawayRecordFluashTimer.Reset(runawayRecordFlushInterval) - } - case <-runawayRecordGCTicker.C: - go do.deleteExpiredRows("tidb_runaway_queries", "time", runawayRecordExpiredDuration) - case r := <-quarantineRecordCh: - go func() { - err := do.handleRunawayWatch(r) - if err != nil { - logutil.BgLogger().Error("add runaway watch", zap.Error(err)) - } - }() - case r := <-staleQuarantineRecordCh: - go func() { - for i := 0; i < 3; i++ { - err := do.handleRemoveStaleRunawayWatch(r) - if err == nil { - break - } - logutil.BgLogger().Error("remove stale runaway watch", zap.Error(err)) - time.Sleep(time.Second) - } - }() - } - } -} - -func (do *Domain) handleRunawayWatch(record *resourcegroup.QuarantineRecord) error { - se, err := do.sysSessionPool.Get() - defer func() { - do.sysSessionPool.Put(se) - }() - if err != nil { - return errors.Annotate(err, "get session failed") - } - exec, _ := se.(sqlexec.SQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - _, err = exec.ExecuteInternal(ctx, "BEGIN") - if err != nil { - return errors.Trace(err) - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - }() - sql, params := record.GenInsertionStmt() - _, err = exec.ExecuteInternal(ctx, sql, params...) - return err -} - -func (do *Domain) handleRunawayWatchDone(record *resourcegroup.QuarantineRecord) error { - se, err := do.sysSessionPool.Get() - defer func() { - do.sysSessionPool.Put(se) - }() - if err != nil { - return errors.Annotate(err, "get session failed") - } - exec, _ := se.(sqlexec.SQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - _, err = exec.ExecuteInternal(ctx, "BEGIN") - if err != nil { - return errors.Trace(err) - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - }() - sql, params := record.GenInsertionDoneStmt() - _, err = exec.ExecuteInternal(ctx, sql, params...) - if err != nil { - return err - } - sql, params = record.GenDeletionStmt() - _, err = exec.ExecuteInternal(ctx, sql, params...) - return err -} - -func (do *Domain) handleRemoveStaleRunawayWatch(record *resourcegroup.QuarantineRecord) error { - se, err := do.sysSessionPool.Get() - defer func() { - do.sysSessionPool.Put(se) - }() - if err != nil { - return errors.Annotate(err, "get session failed") - } - exec, _ := se.(sqlexec.SQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - _, err = exec.ExecuteInternal(ctx, "BEGIN") - if err != nil { - return errors.Trace(err) - } - defer func() { - if err != nil { - _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") - terror.Log(err1) - return - } - _, err = exec.ExecuteInternal(ctx, "COMMIT") - if err != nil { - return - } - }() - sql, params := record.GenDeletionStmt() - _, err = exec.ExecuteInternal(ctx, sql, params...) - return err -} - -func (do *Domain) execRestrictedSQL(sql string, params []interface{}) ([]chunk.Row, error) { - se, err := do.sysSessionPool.Get() - defer func() { - do.sysSessionPool.Put(se) - }() - if err != nil { - return nil, errors.Annotate(err, "get session failed") - } - exec := se.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - r, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, - sql, params..., - ) - return r, err -} - -func (do *Domain) initResourceGroupsController(ctx context.Context, pdClient pd.Client, uniqueID uint64) error { - if pdClient == nil { - logutil.BgLogger().Warn("cannot setup up resource controller, not using tikv storage") - // return nil as unistore doesn't support it - return nil - } - - control, err := rmclient.NewResourceGroupController(ctx, uniqueID, pdClient, nil, rmclient.WithMaxWaitDuration(resourcegroup.MaxWaitDuration)) - if err != nil { - return err - } - control.Start(ctx) - serverInfo, err := infosync.GetServerInfo() - if err != nil { - return err - } - serverAddr := net.JoinHostPort(serverInfo.IP, strconv.Itoa(int(serverInfo.Port))) - do.runawayManager = resourcegroup.NewRunawayManager(control, serverAddr) - do.runawaySyncer = newRunawaySyncer(do.sysSessionPool) - do.resourceGroupsController = control - tikv.SetResourceControlInterceptor(control) - return nil -} - -type runawaySyncer struct { - newWatchReader *SystemTableReader - deletionWatchReader *SystemTableReader - sysSessionPool *sessionPool - mu sync.Mutex -} - -func newRunawaySyncer(sysSessionPool *sessionPool) *runawaySyncer { - return &runawaySyncer{ - sysSessionPool: sysSessionPool, - newWatchReader: &SystemTableReader{ - resourcegroup.RunawayWatchTableName, - "start_time", - resourcegroup.NullTime}, - deletionWatchReader: &SystemTableReader{resourcegroup.RunawayWatchDoneTableName, - "done_time", - resourcegroup.NullTime}, - } -} - -func (s *runawaySyncer) getWatchRecordByID(id int64) ([]*resourcegroup.QuarantineRecord, error) { - return s.getWatchRecord(s.newWatchReader, s.newWatchReader.genSelectByIDStmt(id), false) -} - -func (s *runawaySyncer) getNewWatchRecords() ([]*resourcegroup.QuarantineRecord, error) { - return s.getWatchRecord(s.newWatchReader, s.newWatchReader.genSelectStmt, true) -} - -func (s *runawaySyncer) getNewWatchDoneRecords() ([]*resourcegroup.QuarantineRecord, error) { - return s.getWatchDoneRecord(s.deletionWatchReader, s.deletionWatchReader.genSelectStmt, true) -} - -func (s *runawaySyncer) getWatchRecord(reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { - se, err := s.sysSessionPool.Get() - defer func() { - s.sysSessionPool.Put(se) - }() - if err != nil { - return nil, errors.Annotate(err, "get session failed") - } - exec := se.(sqlexec.RestrictedSQLExecutor) - return getRunawayWatchRecord(exec, reader, sqlGenFn, push) -} - -func (s *runawaySyncer) getWatchDoneRecord(reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { - se, err := s.sysSessionPool.Get() - defer func() { - s.sysSessionPool.Put(se) - }() - if err != nil { - return nil, errors.Annotate(err, "get session failed") - } - exec := se.(sqlexec.RestrictedSQLExecutor) - return getRunawayWatchDoneRecord(exec, reader, sqlGenFn, push) -} - -func getRunawayWatchRecord(exec sqlexec.RestrictedSQLExecutor, reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { - rs, err := reader.Read(exec, sqlGenFn) - if err != nil { - return nil, err - } - ret := make([]*resourcegroup.QuarantineRecord, 0, len(rs)) - now := time.Now().UTC() - for _, r := range rs { - startTime, err := r.GetTime(2).GoTime(time.UTC) - if err != nil { - continue - } - var endTime time.Time - if !r.IsNull(3) { - endTime, err = r.GetTime(3).GoTime(time.UTC) - if err != nil { - continue - } - } - qr := &resourcegroup.QuarantineRecord{ - ID: r.GetInt64(0), - ResourceGroupName: r.GetString(1), - StartTime: startTime, - EndTime: endTime, - Watch: rmpb.RunawayWatchType(r.GetInt64(4)), - WatchText: r.GetString(5), - Source: r.GetString(6), - Action: rmpb.RunawayAction(r.GetInt64(7)), - } - // If a TiDB write record slow, it will occur that the record which has earlier start time is inserted later than others. - // So we start the scan a little earlier. - if push { - reader.CheckPoint = now.Add(-3 * runawayWatchSyncInterval) - } - ret = append(ret, qr) - } - return ret, nil -} - -func getRunawayWatchDoneRecord(exec sqlexec.RestrictedSQLExecutor, reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { - rs, err := reader.Read(exec, sqlGenFn) - if err != nil { - return nil, err - } - length := len(rs) - ret := make([]*resourcegroup.QuarantineRecord, 0, length) - now := time.Now().UTC() - for _, r := range rs { - startTime, err := r.GetTime(3).GoTime(time.UTC) - if err != nil { - continue - } - var endTime time.Time - if !r.IsNull(4) { - endTime, err = r.GetTime(4).GoTime(time.UTC) - if err != nil { - continue - } - } - qr := &resourcegroup.QuarantineRecord{ - ID: r.GetInt64(1), - ResourceGroupName: r.GetString(2), - StartTime: startTime, - EndTime: endTime, - Watch: rmpb.RunawayWatchType(r.GetInt64(5)), - WatchText: r.GetString(6), - Source: r.GetString(7), - Action: rmpb.RunawayAction(r.GetInt64(8)), - } - // Ditto as getRunawayWatchRecord. - if push { - reader.CheckPoint = now.Add(-3 * runawayWatchSyncInterval) - } - ret = append(ret, qr) - } - return ret, nil -} - -// SystemTableReader is used to read table `runaway_watch` and `runaway_watch_done`. -type SystemTableReader struct { - TableName string - KeyCol string - CheckPoint time.Time -} - -func (r *SystemTableReader) genSelectByIDStmt(id int64) func() (string, []interface{}) { - return func() (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, 1) - builder.WriteString("select * from ") - builder.WriteString(r.TableName) - builder.WriteString(" where id = %?") - params = append(params, id) - return builder.String(), params - } -} - -func (r *SystemTableReader) genSelectStmt() (string, []interface{}) { - var builder strings.Builder - params := make([]interface{}, 0, 1) - builder.WriteString("select * from ") - builder.WriteString(r.TableName) - builder.WriteString(" where ") - builder.WriteString(r.KeyCol) - builder.WriteString(" > %? order by ") - builder.WriteString(r.KeyCol) - params = append(params, r.CheckPoint) - return builder.String(), params -} - -func (r *SystemTableReader) Read(exec sqlexec.RestrictedSQLExecutor, genFn func() (string, []interface{})) ([]chunk.Row, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - sql, params := genFn() - rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, - sql, params..., - ) - return rows, err -} diff --git a/dumpling/export/BUILD.bazel b/dumpling/export/BUILD.bazel index 9c25f0f480ac0..ca5e92fbdcdf1 100644 --- a/dumpling/export/BUILD.bazel +++ b/dumpling/export/BUILD.bazel @@ -30,22 +30,22 @@ go_library( "//br/pkg/summary", "//br/pkg/utils", "//br/pkg/version", - "//config", "//dumpling/cli", "//dumpling/context", "//dumpling/log", - "//errno", - "//parser", - "//parser/ast", - "//parser/format", - "//parser/model", - "//store/helper", - "//tablecodec", - "//util", - "//util/codec", - "//util/dbutil", - "//util/promutil", - "//util/table-filter", + "//pkg/config", + "//pkg/errno", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/store/helper", + "//pkg/tablecodec", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/dbutil", + "//pkg/util/promutil", + "//pkg/util/table-filter", "@com_github_coreos_go_semver//semver", "@com_github_docker_go_units//:go-units", "@com_github_go_sql_driver_mysql//:mysql", @@ -95,14 +95,14 @@ go_test( deps = [ "//br/pkg/storage", "//br/pkg/version", - "//config", "//dumpling/context", "//dumpling/log", - "//errno", - "//parser", - "//util/filter", - "//util/promutil", - "//util/table-filter", + "//pkg/config", + "//pkg/errno", + "//pkg/parser", + "//pkg/util/filter", + "//pkg/util/promutil", + "//pkg/util/table-filter", "@com_github_coreos_go_semver//semver", "@com_github_data_dog_go_sqlmock//:go-sqlmock", "@com_github_go_sql_driver_mysql//:mysql", diff --git a/dumpling/export/block_allow_list_test.go b/dumpling/export/block_allow_list_test.go index 28faa4e95f261..cf5c0a68da03e 100644 --- a/dumpling/export/block_allow_list_test.go +++ b/dumpling/export/block_allow_list_test.go @@ -8,8 +8,8 @@ import ( "github.com/pingcap/tidb/br/pkg/version" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/util/filter" - tf "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/util/filter" + tf "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" ) diff --git a/dumpling/export/config.go b/dumpling/export/config.go index b22e11ed1798c..f1dd093375f31 100644 --- a/dumpling/export/config.go +++ b/dumpling/export/config.go @@ -20,9 +20,9 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/version" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/promutil" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/promutil" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/pflag" "go.uber.org/atomic" diff --git a/dumpling/export/consistency_test.go b/dumpling/export/consistency_test.go index 555338803d40d..3af8306ece1e2 100644 --- a/dumpling/export/consistency_test.go +++ b/dumpling/export/consistency_test.go @@ -11,9 +11,9 @@ import ( "github.com/DATA-DOG/go-sqlmock" "github.com/go-sql-driver/mysql" "github.com/pingcap/tidb/br/pkg/version" - dbconfig "github.com/pingcap/tidb/config" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/errno" + dbconfig "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" "github.com/stretchr/testify/require" ) diff --git a/dumpling/export/dump.go b/dumpling/export/dump.go index 3cf5a50be19eb..dd0770bb5707c 100644 --- a/dumpling/export/dump.go +++ b/dumpling/export/dump.go @@ -30,13 +30,13 @@ import ( "github.com/pingcap/tidb/dumpling/cli" tcontext "github.com/pingcap/tidb/dumpling/context" "github.com/pingcap/tidb/dumpling/log" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/codec" pd "github.com/tikv/pd/client" gatomic "go.uber.org/atomic" "go.uber.org/zap" diff --git a/dumpling/export/dump_test.go b/dumpling/export/dump_test.go index f77171b1cfa8c..dfd9a1d334930 100644 --- a/dumpling/export/dump_test.go +++ b/dumpling/export/dump_test.go @@ -15,8 +15,8 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/version" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) diff --git a/dumpling/export/ir_impl_test.go b/dumpling/export/ir_impl_test.go index b614f0ae9912f..c66aa54be20fb 100644 --- a/dumpling/export/ir_impl_test.go +++ b/dumpling/export/ir_impl_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" ) diff --git a/dumpling/export/main_test.go b/dumpling/export/main_test.go index 1049fda523cf1..9e99bb3f9ad95 100644 --- a/dumpling/export/main_test.go +++ b/dumpling/export/main_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/pingcap/tidb/dumpling/log" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/stretchr/testify/require" "go.uber.org/goleak" diff --git a/dumpling/export/metrics.go b/dumpling/export/metrics.go index b4f9aa66ac4d1..dbbdb045d88a5 100644 --- a/dumpling/export/metrics.go +++ b/dumpling/export/metrics.go @@ -5,7 +5,7 @@ package export import ( "math" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "go.uber.org/atomic" diff --git a/dumpling/export/metrics_test.go b/dumpling/export/metrics_test.go index 569933150faea..f41230b8d9407 100644 --- a/dumpling/export/metrics_test.go +++ b/dumpling/export/metrics_test.go @@ -5,7 +5,7 @@ package export import ( "testing" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" ) func TestMetricsRegistration(t *testing.T) { diff --git a/dumpling/export/retry.go b/dumpling/export/retry.go index 4121cada1875b..118ad97aa7a79 100644 --- a/dumpling/export/retry.go +++ b/dumpling/export/retry.go @@ -10,7 +10,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/utils" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/util/dbutil" + "github.com/pingcap/tidb/pkg/util/dbutil" "go.uber.org/zap" ) diff --git a/dumpling/export/sql.go b/dumpling/export/sql.go index 60d14ac49e14c..84355b797bccc 100644 --- a/dumpling/export/sql.go +++ b/dumpling/export/sql.go @@ -17,12 +17,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/version" - dbconfig "github.com/pingcap/tidb/config" tcontext "github.com/pingcap/tidb/dumpling/context" "github.com/pingcap/tidb/dumpling/log" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/helper" + dbconfig "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/helper" "go.uber.org/multierr" "go.uber.org/zap" ) diff --git a/dumpling/export/sql_test.go b/dumpling/export/sql_test.go index 5ae4c7278efa4..97b40ee7b2e1c 100644 --- a/dumpling/export/sql_test.go +++ b/dumpling/export/sql_test.go @@ -19,9 +19,9 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/version" - dbconfig "github.com/pingcap/tidb/config" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/util/promutil" + dbconfig "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" ) diff --git a/dumpling/export/writer_serial_test.go b/dumpling/export/writer_serial_test.go index 4029b08ef9ce3..586fa8e8d794d 100644 --- a/dumpling/export/writer_serial_test.go +++ b/dumpling/export/writer_serial_test.go @@ -11,7 +11,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/storage" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" ) diff --git a/dumpling/export/writer_test.go b/dumpling/export/writer_test.go index 698ac48d0219a..50e617493a4a5 100644 --- a/dumpling/export/writer_test.go +++ b/dumpling/export/writer_test.go @@ -14,7 +14,7 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/version" tcontext "github.com/pingcap/tidb/dumpling/context" - "github.com/pingcap/tidb/util/promutil" + "github.com/pingcap/tidb/pkg/util/promutil" "github.com/stretchr/testify/require" ) diff --git a/errno/BUILD.bazel b/errno/BUILD.bazel deleted file mode 100644 index e322518799a5d..0000000000000 --- a/errno/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "errno", - srcs = [ - "errcode.go", - "errname.go", - "infoschema.go", - ], - importpath = "github.com/pingcap/tidb/errno", - visibility = ["//visibility:public"], - deps = ["//parser/mysql"], -) - -go_test( - name = "errno_test", - timeout = "short", - srcs = [ - "infoschema_test.go", - "main_test.go", - ], - embed = [":errno"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - ], -) diff --git a/errno/main_test.go b/errno/main_test.go deleted file mode 100644 index c50dc3cdbaa0e..0000000000000 --- a/errno/main_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package errno - -import ( - "os" - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - os.Exit(m.Run()) -} diff --git a/executor/BUILD.bazel b/executor/BUILD.bazel deleted file mode 100644 index 083666a0c820b..0000000000000 --- a/executor/BUILD.bazel +++ /dev/null @@ -1,478 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "executor", - srcs = [ - "adapter.go", - "admin.go", - "admin_plugins.go", - "admin_telemetry.go", - "analyze.go", - "analyze_col.go", - "analyze_col_v2.go", - "analyze_global_stats.go", - "analyze_idx.go", - "analyze_utils.go", - "analyze_worker.go", - "batch_checker.go", - "batch_point_get.go", - "bind.go", - "brie.go", - "builder.go", - "change.go", - "checksum.go", - "compact_table.go", - "compiler.go", - "concurrent_map.go", - "coprocessor.go", - "cte.go", - "cte_table_reader.go", - "ddl.go", - "delete.go", - "distsql.go", - "executor.go", - "explain.go", - "foreign_key.go", - "grant.go", - "hash_table.go", - "import_into.go", - "index_advise.go", - "index_lookup_hash_join.go", - "index_lookup_join.go", - "index_lookup_merge_join.go", - "index_merge_reader.go", - "infoschema_reader.go", - "insert.go", - "insert_common.go", - "inspection_common.go", - "inspection_profile.go", - "inspection_result.go", - "inspection_summary.go", - "join.go", - "joiner.go", - "load_data.go", - "load_stats.go", - "mem_reader.go", - "memtable_reader.go", - "merge_join.go", - "metrics_reader.go", - "mpp_gather.go", - "opt_rule_blacklist.go", - "parallel_apply.go", - "pipelined_window.go", - "plan_replayer.go", - "point_get.go", - "prepared.go", - "projection.go", - "reload_expr_pushdown_blacklist.go", - "replace.go", - "revoke.go", - "sample.go", - "select_into.go", - "set.go", - "set_config.go", - "show.go", - "show_placement.go", - "show_stats.go", - "shuffle.go", - "simple.go", - "slow_query.go", - "sort.go", - "split.go", - "stmtsummary.go", - "table_reader.go", - "trace.go", - "union_scan.go", - "update.go", - "utils.go", - "window.go", - "write.go", - ], - importpath = "github.com/pingcap/tidb/executor", - visibility = ["//visibility:public"], - deps = [ - "//bindinfo", - "//br/pkg/glue", - "//br/pkg/lightning/mydump", - "//br/pkg/storage", - "//br/pkg/task", - "//br/pkg/task/show", - "//br/pkg/utils", - "//config", - "//ddl", - "//ddl/label", - "//ddl/placement", - "//ddl/schematracker", - "//distsql", - "//disttask/framework/proto", - "//disttask/framework/storage", - "//disttask/importinto", - "//domain", - "//domain/infosync", - "//domain/resourcegroup", - "//errno", - "//executor/aggfuncs", - "//executor/aggregate", - "//executor/asyncloaddata", - "//executor/importer", - "//executor/internal/applycache", - "//executor/internal/builder", - "//executor/internal/calibrateresource", - "//executor/internal/exec", - "//executor/internal/mpp", - "//executor/internal/pdhelper", - "//executor/internal/querywatch", - "//executor/internal/util", - "//executor/internal/vecgroupchecker", - "//executor/lockstats", - "//executor/metrics", - "//executor/mppcoordmanager", - "//expression", - "//expression/aggregation", - "//infoschema", - "//keyspace", - "//kv", - "//meta", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//parser/tidb", - "//parser/types", - "//planner", - "//planner/cardinality", - "//planner/core", - "//planner/util", - "//plugin", - "//privilege", - "//privilege/privileges", - "//resourcemanager/pool/workerpool", - "//resourcemanager/util", - "//session/txninfo", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/staleread", - "//statistics", - "//statistics/handle", - "//statistics/handle/cache", - "//statistics/handle/storage", - "//store/driver/backoff", - "//store/driver/txn", - "//store/helper", - "//table", - "//table/tables", - "//table/temptable", - "//tablecodec", - "//telemetry", - "//tidb-binlog/node", - "//types", - "//types/parser_driver", - "//util", - "//util/admin", - "//util/bitmap", - "//util/breakpoint", - "//util/channel", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/cteutil", - "//util/dbterror", - "//util/dbterror/exeerrors", - "//util/deadlockhistory", - "//util/disk", - "//util/disttask", - "//util/etcd", - "//util/execdetails", - "//util/filter", - "//util/format", - "//util/gcutil", - "//util/globalconn", - "//util/hack", - "//util/hint", - "//util/intest", - "//util/keydecoder", - "//util/logutil", - "//util/logutil/consistency", - "//util/mathutil", - "//util/memory", - "//util/mvmap", - "//util/password-validation", - "//util/pdapi", - "//util/plancodec", - "//util/printer", - "//util/ranger", - "//util/replayer", - "//util/resourcegrouptag", - "//util/rowDecoder", - "//util/rowcodec", - "//util/sem", - "//util/servermemorylimit", - "//util/set", - "//util/size", - "//util/sqlexec", - "//util/stmtsummary", - "//util/stmtsummary/v2:stmtsummary", - "//util/stringutil", - "//util/syncutil", - "//util/table-filter", - "//util/tiflash", - "//util/timeutil", - "//util/tls", - "//util/topsql", - "//util/topsql/state", - "//util/tracing", - "@com_github_burntsushi_toml//:toml", - "@com_github_docker_go_units//:go-units", - "@com_github_gogo_protobuf//proto", - "@com_github_ngaut_pools//:pools", - "@com_github_opentracing_basictracer_go//:basictracer-go", - "@com_github_opentracing_opentracing_go//:opentracing-go", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/brpb", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/diagnosticspb", - "@com_github_pingcap_kvproto//pkg/encryptionpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_pingcap_kvproto//pkg/tikvpb", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_sysutil//:sysutil", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_prometheus_client_golang//api", - "@com_github_prometheus_client_golang//api/prometheus/v1:prometheus", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_common//model", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//txnkv", - "@com_github_tikv_client_go_v2//txnkv/txnlock", - "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@com_github_twmb_murmur3//:murmur3", - "@com_sourcegraph_sourcegraph_appdash//:appdash", - "@com_sourcegraph_sourcegraph_appdash//opentracing", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//codes", - "@org_golang_google_grpc//credentials", - "@org_golang_google_grpc//credentials/insecure", - "@org_golang_google_grpc//status", - "@org_golang_x_exp//maps", - "@org_golang_x_sync//errgroup", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "executor_test", - timeout = "moderate", - srcs = [ - "adapter_test.go", - "analyze_test.go", - "analyze_utils_test.go", - "batch_point_get_test.go", - "benchmark_test.go", - "brie_test.go", - "charset_test.go", - "chunk_size_control_test.go", - "cluster_table_test.go", - "compact_table_test.go", - "concurrent_map_test.go", - "copr_cache_test.go", - "cte_test.go", - "delete_test.go", - "distsql_test.go", - "executor_failpoint_test.go", - "executor_pkg_test.go", - "executor_required_rows_test.go", - "executor_test.go", - "executor_txn_test.go", - "explain_test.go", - "explain_unit_test.go", - "explainfor_test.go", - "grant_test.go", - "hash_table_test.go", - "historical_stats_test.go", - "hot_regions_history_table_test.go", - "import_into_test.go", - "index_advise_test.go", - "index_lookup_join_test.go", - "index_lookup_merge_join_test.go", - "infoschema_cluster_table_test.go", - "infoschema_reader_internal_test.go", - "infoschema_reader_test.go", - "insert_test.go", - "inspection_common_test.go", - "inspection_result_test.go", - "inspection_summary_test.go", - "join_pkg_test.go", - "join_test.go", - "joiner_test.go", - "main_test.go", - "memtable_reader_test.go", - "merge_join_test.go", - "metrics_reader_test.go", - "parallel_apply_test.go", - "partition_table_test.go", - "pkg_test.go", - "point_get_test.go", - "prepared_test.go", - "recover_test.go", - "resource_tag_test.go", - "revoke_test.go", - "rowid_test.go", - "sample_test.go", - "select_into_test.go", - "set_test.go", - "show_placement_labels_test.go", - "show_placement_test.go", - "show_stats_test.go", - "show_test.go", - "shuffle_test.go", - "simple_test.go", - "slow_query_sql_test.go", - "slow_query_test.go", - "sort_test.go", - "split_test.go", - "stale_txn_test.go", - "statement_context_test.go", - "stmtsummary_test.go", - "table_readers_required_rows_test.go", - "temporary_table_test.go", - "tikv_regions_peers_table_test.go", - "trace_test.go", - "union_scan_test.go", - "update_test.go", - "utils_test.go", - "window_test.go", - "write_concurrent_test.go", - ], - data = glob(["testdata/**"]), - embed = [":executor"], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//ddl", - "//ddl/placement", - "//ddl/util", - "//distsql", - "//domain", - "//domain/infosync", - "//errno", - "//executor/aggfuncs", - "//executor/aggregate", - "//executor/importer", - "//executor/internal/builder", - "//executor/internal/exec", - "//expression", - "//expression/aggregation", - "//infoschema", - "//kv", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner", - "//planner/core", - "//planner/property", - "//planner/util", - "//server", - "//session", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/staleread", - "//statistics", - "//statistics/handle/globalstats", - "//statistics/handle/storage", - "//store/copr", - "//store/driver/error", - "//store/helper", - "//store/mockstore", - "//store/mockstore/unistore", - "//table/tables", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util", - "//util/benchdaily", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/dbterror/exeerrors", - "//util/deadlockhistory", - "//util/disk", - "//util/execdetails", - "//util/gcutil", - "//util/globalconn", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/mock", - "//util/paging", - "//util/pdapi", - "//util/plancodec", - "//util/ranger", - "//util/sem", - "//util/set", - "//util/stmtsummary/v2:stmtsummary", - "//util/stringutil", - "//util/syncutil", - "//util/tableutil", - "//util/topsql/state", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_fn//:fn", - "@com_github_pingcap_kvproto//pkg/diagnosticspb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_sysutil//:sysutil", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_model//go", - "@com_github_prometheus_common//model", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//util", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//zapcore", - ], -) diff --git a/executor/admin.go b/executor/admin.go deleted file mode 100644 index 8d04108e5354a..0000000000000 --- a/executor/admin.go +++ /dev/null @@ -1,890 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "math" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -var ( - _ exec.Executor = &CheckIndexRangeExec{} - _ exec.Executor = &RecoverIndexExec{} - _ exec.Executor = &CleanupIndexExec{} -) - -// CheckIndexRangeExec outputs the index values which has handle between begin and end. -type CheckIndexRangeExec struct { - exec.BaseExecutor - - table *model.TableInfo - index *model.IndexInfo - is infoschema.InfoSchema - startKey []types.Datum - - handleRanges []ast.HandleRange - srcChunk *chunk.Chunk - - result distsql.SelectResult - cols []*model.ColumnInfo -} - -// Next implements the Executor Next interface. -func (e *CheckIndexRangeExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - handleIdx := e.GetSchema().Len() - 1 - for { - err := e.result.Next(ctx, e.srcChunk) - if err != nil { - return err - } - if e.srcChunk.NumRows() == 0 { - return nil - } - iter := chunk.NewIterator4Chunk(e.srcChunk) - appendRows := make([]chunk.Row, 0, e.srcChunk.NumRows()) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - handle := row.GetInt64(handleIdx) - for _, hr := range e.handleRanges { - if handle >= hr.Begin && handle < hr.End { - appendRows = append(appendRows, row) - break - } - } - } - if len(appendRows) > 0 { - req.AppendRows(appendRows) - } - if req.NumRows() > 0 { - return nil - } - } -} - -// Open implements the Executor Open interface. -func (e *CheckIndexRangeExec) Open(ctx context.Context) error { - tCols := e.table.Cols() - for _, ic := range e.index.Columns { - col := tCols[ic.Offset] - e.cols = append(e.cols, col) - } - - colTypeForHandle := e.GetSchema().Columns[len(e.cols)].RetType - e.cols = append(e.cols, &model.ColumnInfo{ - ID: model.ExtraHandleID, - Name: model.ExtraHandleName, - FieldType: *colTypeForHandle, - }) - - e.srcChunk = exec.TryNewCacheChunk(e) - dagPB, err := e.buildDAGPB() - if err != nil { - return err - } - sc := e.Ctx().GetSessionVars().StmtCtx - txn, err := e.Ctx().Txn(true) - if err != nil { - return nil - } - var builder distsql.RequestBuilder - kvReq, err := builder.SetIndexRanges(sc, e.table.ID, e.index.ID, ranger.FullRange()). - SetDAGRequest(dagPB). - SetStartTS(txn.StartTS()). - SetKeepOrder(true). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetConnID(e.Ctx().GetSessionVars().ConnectionID). - Build() - if err != nil { - return err - } - - e.result, err = distsql.Select(ctx, e.Ctx(), kvReq, e.RetFieldTypes()) - if err != nil { - return err - } - return nil -} - -func (e *CheckIndexRangeExec) buildDAGPB() (*tipb.DAGRequest, error) { - dagReq := &tipb.DAGRequest{} - dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) - sc := e.Ctx().GetSessionVars().StmtCtx - dagReq.Flags = sc.PushDownFlags() - for i := range e.Schema().Columns { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) - } - execPB := e.constructIndexScanPB() - dagReq.Executors = append(dagReq.Executors, execPB) - - err := tables.SetPBColumnsDefaultValue(e.Ctx(), dagReq.Executors[0].IdxScan.Columns, e.cols) - if err != nil { - return nil, err - } - distsql.SetEncodeType(e.Ctx(), dagReq) - return dagReq, nil -} - -func (e *CheckIndexRangeExec) constructIndexScanPB() *tipb.Executor { - idxExec := &tipb.IndexScan{ - TableId: e.table.ID, - IndexId: e.index.ID, - Columns: util.ColumnsToProto(e.cols, e.table.PKIsHandle, true), - } - return &tipb.Executor{Tp: tipb.ExecType_TypeIndexScan, IdxScan: idxExec} -} - -// Close implements the Executor Close interface. -func (*CheckIndexRangeExec) Close() error { - return nil -} - -// RecoverIndexExec represents a recover index executor. -// It is built from "admin recover index" statement, is used to backfill -// corrupted index. -type RecoverIndexExec struct { - exec.BaseExecutor - - done bool - - index table.Index - table table.Table - physicalID int64 - batchSize int - - columns []*model.ColumnInfo - colFieldTypes []*types.FieldType - srcChunk *chunk.Chunk - handleCols plannercore.HandleCols - - containsGenedCol bool - cols []*expression.Column - - // below buf is used to reduce allocations. - recoverRows []recoverRows - idxValsBufs [][]types.Datum - idxKeyBufs [][]byte - batchKeys []kv.Key -} - -func (e *RecoverIndexExec) columnsTypes() []*types.FieldType { - if e.colFieldTypes != nil { - return e.colFieldTypes - } - - e.colFieldTypes = make([]*types.FieldType, 0, len(e.columns)) - for _, col := range e.columns { - e.colFieldTypes = append(e.colFieldTypes, &col.FieldType) - } - return e.colFieldTypes -} - -// Open implements the Executor Open interface. -func (e *RecoverIndexExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - - e.srcChunk = chunk.New(e.columnsTypes(), e.InitCap(), e.MaxChunkSize()) - e.batchSize = 2048 - e.recoverRows = make([]recoverRows, 0, e.batchSize) - e.idxValsBufs = make([][]types.Datum, e.batchSize) - e.idxKeyBufs = make([][]byte, e.batchSize) - return nil -} - -func (e *RecoverIndexExec) constructTableScanPB(tblInfo *model.TableInfo, colInfos []*model.ColumnInfo) (*tipb.Executor, error) { - tblScan := tables.BuildTableScanFromInfos(tblInfo, colInfos) - tblScan.TableId = e.physicalID - err := tables.SetPBColumnsDefaultValue(e.Ctx(), tblScan.Columns, colInfos) - return &tipb.Executor{Tp: tipb.ExecType_TypeTableScan, TblScan: tblScan}, err -} - -func (*RecoverIndexExec) constructLimitPB(count uint64) *tipb.Executor { - limitExec := &tipb.Limit{ - Limit: count, - } - return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} -} - -func (e *RecoverIndexExec) buildDAGPB(_ kv.Transaction, limitCnt uint64) (*tipb.DAGRequest, error) { - dagReq := &tipb.DAGRequest{} - dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) - sc := e.Ctx().GetSessionVars().StmtCtx - dagReq.Flags = sc.PushDownFlags() - for i := range e.columns { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) - } - - tblScanExec, err := e.constructTableScanPB(e.table.Meta(), e.columns) - if err != nil { - return nil, err - } - dagReq.Executors = append(dagReq.Executors, tblScanExec) - - limitExec := e.constructLimitPB(limitCnt) - dagReq.Executors = append(dagReq.Executors, limitExec) - distsql.SetEncodeType(e.Ctx(), dagReq) - return dagReq, nil -} - -func (e *RecoverIndexExec) buildTableScan(ctx context.Context, txn kv.Transaction, startHandle kv.Handle, limitCnt uint64) (distsql.SelectResult, error) { - dagPB, err := e.buildDAGPB(txn, limitCnt) - if err != nil { - return nil, err - } - var builder distsql.RequestBuilder - keyRanges, err := buildRecoverIndexKeyRanges(e.physicalID, startHandle) - if err != nil { - return nil, err - } - builder.KeyRanges = kv.NewNonParitionedKeyRanges(keyRanges) - kvReq, err := builder. - SetDAGRequest(dagPB). - SetStartTS(txn.StartTS()). - SetKeepOrder(true). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetConnID(e.Ctx().GetSessionVars().ConnectionID). - Build() - if err != nil { - return nil, err - } - - // Actually, with limitCnt, the match datas maybe only in one region, so let the concurrency to be 1, - // avoid unnecessary region scan. - kvReq.Concurrency = 1 - result, err := distsql.Select(ctx, e.Ctx(), kvReq, e.columnsTypes()) - if err != nil { - return nil, err - } - return result, nil -} - -// buildRecoverIndexKeyRanges build a KeyRange: (startHandle, unlimited). -func buildRecoverIndexKeyRanges(tid int64, startHandle kv.Handle) ([]kv.KeyRange, error) { - var startKey []byte - if startHandle == nil { - startKey = tablecodec.GenTableRecordPrefix(tid).Next() - } else { - startKey = tablecodec.EncodeRowKey(tid, startHandle.Encoded()).PrefixNext() - } - endKey := tablecodec.GenTableRecordPrefix(tid).PrefixNext() - return []kv.KeyRange{{StartKey: startKey, EndKey: endKey}}, nil -} - -type backfillResult struct { - currentHandle kv.Handle - addedCount int64 - scanRowCount int64 -} - -func (e *RecoverIndexExec) backfillIndex(ctx context.Context) (totalAddedCnt, totalScanCnt int64, err error) { - var ( - currentHandle kv.Handle - lastLogCnt int64 - result backfillResult - ) - for { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) - errInTxn := kv.RunInNewTxn(ctx, e.Ctx().GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - setOptionForTopSQL(e.Ctx().GetSessionVars().StmtCtx, txn) - var err error - result, err = e.backfillIndexInTxn(ctx, txn, currentHandle) - return err - }) - if errInTxn != nil { - return totalAddedCnt, totalScanCnt, errInTxn - } - totalAddedCnt += result.addedCount - totalScanCnt += result.scanRowCount - if totalScanCnt-lastLogCnt >= 50000 { - lastLogCnt = totalScanCnt - logutil.Logger(ctx).Info("recover index", zap.String("table", e.table.Meta().Name.O), - zap.String("index", e.index.Meta().Name.O), zap.Int64("totalAddedCnt", totalAddedCnt), - zap.Int64("totalScanCnt", totalScanCnt), zap.Stringer("currentHandle", result.currentHandle)) - } - - // no more rows - if result.scanRowCount == 0 { - break - } - currentHandle = result.currentHandle - if currentHandle.Next().Compare(result.currentHandle) <= 0 { - break // There is no more handles in the table. - } - } - return totalAddedCnt, totalScanCnt, nil -} - -type recoverRows struct { - handle kv.Handle - idxVals []types.Datum - rsData []types.Datum - skip bool -} - -func (e *RecoverIndexExec) fetchRecoverRows(ctx context.Context, srcResult distsql.SelectResult, result *backfillResult) ([]recoverRows, error) { - e.recoverRows = e.recoverRows[:0] - idxValLen := len(e.index.Meta().Columns) - result.scanRowCount = 0 - - for { - err := srcResult.Next(ctx, e.srcChunk) - if err != nil { - return nil, err - } - - if e.srcChunk.NumRows() == 0 { - break - } - iter := chunk.NewIterator4Chunk(e.srcChunk) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - if result.scanRowCount >= int64(e.batchSize) { - return e.recoverRows, nil - } - handle, err := e.handleCols.BuildHandle(row) - if err != nil { - return nil, err - } - idxVals, err := e.buildIndexedValues(row, e.idxValsBufs[result.scanRowCount], e.colFieldTypes, idxValLen) - if err != nil { - return nil, err - } - e.idxValsBufs[result.scanRowCount] = idxVals - rsData := tables.TryGetHandleRestoredDataWrapper(e.table.Meta(), plannercore.GetCommonHandleDatum(e.handleCols, row), nil, e.index.Meta()) - e.recoverRows = append(e.recoverRows, recoverRows{handle: handle, idxVals: idxVals, rsData: rsData, skip: true}) - result.scanRowCount++ - result.currentHandle = handle - } - } - - return e.recoverRows, nil -} - -func (e *RecoverIndexExec) buildIndexedValues(row chunk.Row, idxVals []types.Datum, fieldTypes []*types.FieldType, idxValLen int) ([]types.Datum, error) { - if !e.containsGenedCol { - return extractIdxVals(row, idxVals, fieldTypes, idxValLen), nil - } - - if e.cols == nil { - columns, _, err := expression.ColumnInfos2ColumnsAndNames(e.Ctx(), model.NewCIStr("mock"), e.table.Meta().Name, e.table.Meta().Columns, e.table.Meta()) - if err != nil { - return nil, err - } - e.cols = columns - } - - if cap(idxVals) < idxValLen { - idxVals = make([]types.Datum, idxValLen) - } else { - idxVals = idxVals[:idxValLen] - } - - for i, col := range e.index.Meta().Columns { - if e.table.Meta().Columns[col.Offset].IsGenerated() { - val, err := e.cols[col.Offset].EvalVirtualColumn(row) - if err != nil { - return nil, err - } - val.Copy(&idxVals[i]) - } else { - val := row.GetDatum(col.Offset, &(e.table.Meta().Columns[col.Offset].FieldType)) - val.Copy(&idxVals[i]) - } - } - return idxVals, nil -} - -func (e *RecoverIndexExec) batchMarkDup(txn kv.Transaction, rows []recoverRows) error { - if len(rows) == 0 { - return nil - } - e.batchKeys = e.batchKeys[:0] - sc := e.Ctx().GetSessionVars().StmtCtx - distinctFlags := make([]bool, 0, len(rows)) - rowIdx := make([]int, 0, len(rows)) - cnt := 0 - for i, row := range rows { - iter := e.index.GenIndexKVIter(sc, row.idxVals, row.handle, nil) - for iter.Valid() { - var buf []byte - if cnt < len(e.idxKeyBufs) { - buf = e.idxKeyBufs[cnt] - } - key, _, distinct, err := iter.Next(buf) - if err != nil { - return err - } - if cnt < len(e.idxKeyBufs) { - e.idxKeyBufs[cnt] = key - } else { - e.idxKeyBufs = append(e.idxKeyBufs, key) - } - - cnt++ - e.batchKeys = append(e.batchKeys, key) - distinctFlags = append(distinctFlags, distinct) - rowIdx = append(rowIdx, i) - } - } - - values, err := txn.BatchGet(context.Background(), e.batchKeys) - if err != nil { - return err - } - - // 1. unique-key is duplicate and the handle is equal, skip it. - // 2. unique-key is duplicate and the handle is not equal, data is not consistent, log it and skip it. - // 3. non-unique-key is duplicate, skip it. - isCommonHandle := e.table.Meta().IsCommonHandle - for i, key := range e.batchKeys { - val, found := values[string(key)] - if found { - if distinctFlags[i] { - handle, err1 := tablecodec.DecodeHandleInUniqueIndexValue(val, isCommonHandle) - if err1 != nil { - return err1 - } - - if handle.Compare(rows[rowIdx[i]].handle) != 0 { - logutil.BgLogger().Warn("recover index: the constraint of unique index is broken, handle in index is not equal to handle in table", - zap.String("index", e.index.Meta().Name.O), zap.ByteString("indexKey", key), - zap.Stringer("handleInTable", rows[rowIdx[i]].handle), zap.Stringer("handleInIndex", handle)) - } - } - } - rows[rowIdx[i]].skip = found && rows[rowIdx[i]].skip - } - return nil -} - -func (e *RecoverIndexExec) backfillIndexInTxn(ctx context.Context, txn kv.Transaction, currentHandle kv.Handle) (result backfillResult, err error) { - srcResult, err := e.buildTableScan(ctx, txn, currentHandle, uint64(e.batchSize)) - if err != nil { - return result, err - } - defer terror.Call(srcResult.Close) - - rows, err := e.fetchRecoverRows(ctx, srcResult, &result) - if err != nil { - return result, err - } - - err = e.batchMarkDup(txn, rows) - if err != nil { - return result, err - } - - // Constrains is already checked. - e.Ctx().GetSessionVars().StmtCtx.BatchCheck = true - for _, row := range rows { - if row.skip { - continue - } - - recordKey := tablecodec.EncodeRecordKey(e.table.RecordPrefix(), row.handle) - err := txn.LockKeys(ctx, new(kv.LockCtx), recordKey) - if err != nil { - return result, err - } - - _, err = e.index.Create(e.Ctx(), txn, row.idxVals, row.handle, row.rsData, table.WithIgnoreAssertion) - if err != nil { - return result, err - } - result.addedCount++ - } - return result, nil -} - -// Next implements the Executor Next interface. -func (e *RecoverIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - - recoveringClusteredIndex := e.index.Meta().Primary && e.table.Meta().IsCommonHandle - if recoveringClusteredIndex { - req.AppendInt64(0, 0) - req.AppendInt64(1, 0) - e.done = true - return nil - } - var totalAddedCnt, totalScanCnt int64 - var err error - if tbl, ok := e.table.(table.PartitionedTable); ok { - pi := e.table.Meta().GetPartitionInfo() - for _, p := range pi.Definitions { - e.table = tbl.GetPartition(p.ID) - e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) - e.physicalID = p.ID - addedCnt, scanCnt, err := e.backfillIndex(ctx) - totalAddedCnt += addedCnt - totalScanCnt += scanCnt - if err != nil { - return err - } - } - } else { - totalAddedCnt, totalScanCnt, err = e.backfillIndex(ctx) - if err != nil { - return err - } - } - - req.AppendInt64(0, totalAddedCnt) - req.AppendInt64(1, totalScanCnt) - e.done = true - return nil -} - -// CleanupIndexExec represents a cleanup index executor. -// It is built from "admin cleanup index" statement, is used to delete -// dangling index data. -type CleanupIndexExec struct { - exec.BaseExecutor - - done bool - removeCnt uint64 - - index table.Index - table table.Table - physicalID int64 - - columns []*model.ColumnInfo - idxColFieldTypes []*types.FieldType - idxChunk *chunk.Chunk - handleCols plannercore.HandleCols - - idxValues *kv.HandleMap // kv.Handle -> [][]types.Datum - batchSize uint64 - batchKeys []kv.Key - idxValsBufs [][]types.Datum - lastIdxKey []byte - scanRowCnt uint64 -} - -func (e *CleanupIndexExec) getIdxColTypes() []*types.FieldType { - if e.idxColFieldTypes != nil { - return e.idxColFieldTypes - } - e.idxColFieldTypes = make([]*types.FieldType, 0, len(e.columns)) - for _, col := range e.columns { - e.idxColFieldTypes = append(e.idxColFieldTypes, col.FieldType.ArrayType()) - } - return e.idxColFieldTypes -} - -func (e *CleanupIndexExec) batchGetRecord(txn kv.Transaction) (map[string][]byte, error) { - e.idxValues.Range(func(h kv.Handle, _ interface{}) bool { - e.batchKeys = append(e.batchKeys, tablecodec.EncodeRecordKey(e.table.RecordPrefix(), h)) - return true - }) - values, err := txn.BatchGet(context.Background(), e.batchKeys) - if err != nil { - return nil, err - } - return values, nil -} - -func (e *CleanupIndexExec) deleteDanglingIdx(txn kv.Transaction, values map[string][]byte) error { - for _, k := range e.batchKeys { - if _, found := values[string(k)]; !found { - _, handle, err := tablecodec.DecodeRecordKey(k) - if err != nil { - return err - } - handleIdxValsGroup, ok := e.idxValues.Get(handle) - if !ok { - return errors.Trace(errors.Errorf("batch keys are inconsistent with handles")) - } - for _, handleIdxVals := range handleIdxValsGroup.([][]types.Datum) { - if err := e.index.Delete(e.Ctx().GetSessionVars().StmtCtx, txn, handleIdxVals, handle); err != nil { - return err - } - e.removeCnt++ - if e.removeCnt%e.batchSize == 0 { - logutil.BgLogger().Info("clean up dangling index", zap.String("table", e.table.Meta().Name.String()), - zap.String("index", e.index.Meta().Name.String()), zap.Uint64("count", e.removeCnt)) - } - } - } - } - return nil -} - -func extractIdxVals(row chunk.Row, idxVals []types.Datum, - fieldTypes []*types.FieldType, idxValLen int) []types.Datum { - if cap(idxVals) < idxValLen { - idxVals = make([]types.Datum, idxValLen) - } else { - idxVals = idxVals[:idxValLen] - } - - for i := 0; i < idxValLen; i++ { - colVal := row.GetDatum(i, fieldTypes[i]) - colVal.Copy(&idxVals[i]) - } - return idxVals -} - -func (e *CleanupIndexExec) fetchIndex(ctx context.Context, txn kv.Transaction) error { - result, err := e.buildIndexScan(ctx, txn) - if err != nil { - return err - } - defer terror.Call(result.Close) - - sc := e.Ctx().GetSessionVars().StmtCtx - idxColLen := len(e.index.Meta().Columns) - for { - err := result.Next(ctx, e.idxChunk) - if err != nil { - return err - } - if e.idxChunk.NumRows() == 0 { - return nil - } - iter := chunk.NewIterator4Chunk(e.idxChunk) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - handle, err := e.handleCols.BuildHandle(row) - if err != nil { - return err - } - idxVals := extractIdxVals(row, e.idxValsBufs[e.scanRowCnt], e.idxColFieldTypes, idxColLen) - e.idxValsBufs[e.scanRowCnt] = idxVals - existingIdxVals, ok := e.idxValues.Get(handle) - if ok { - updatedIdxVals := append(existingIdxVals.([][]types.Datum), idxVals) - e.idxValues.Set(handle, updatedIdxVals) - } else { - e.idxValues.Set(handle, [][]types.Datum{idxVals}) - } - idxKey, _, err := e.index.GenIndexKey(sc, idxVals, handle, nil) - if err != nil { - return err - } - e.scanRowCnt++ - e.lastIdxKey = idxKey - if e.scanRowCnt >= e.batchSize { - return nil - } - } - } -} - -// Next implements the Executor Next interface. -func (e *CleanupIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - cleaningClusteredPrimaryKey := e.table.Meta().IsCommonHandle && e.index.Meta().Primary - if cleaningClusteredPrimaryKey { - e.done = true - req.AppendUint64(0, 0) - return nil - } - - var err error - if tbl, ok := e.table.(table.PartitionedTable); ok { - pi := e.table.Meta().GetPartitionInfo() - for _, p := range pi.Definitions { - e.table = tbl.GetPartition(p.ID) - e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) - e.physicalID = p.ID - err = e.init() - if err != nil { - return err - } - err = e.cleanTableIndex(ctx) - if err != nil { - return err - } - } - } else { - err = e.cleanTableIndex(ctx) - if err != nil { - return err - } - } - e.done = true - req.AppendUint64(0, e.removeCnt) - return nil -} - -func (e *CleanupIndexExec) cleanTableIndex(ctx context.Context) error { - for { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) - errInTxn := kv.RunInNewTxn(ctx, e.Ctx().GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - txn.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) - setOptionForTopSQL(e.Ctx().GetSessionVars().StmtCtx, txn) - err := e.fetchIndex(ctx, txn) - if err != nil { - return err - } - values, err := e.batchGetRecord(txn) - if err != nil { - return err - } - err = e.deleteDanglingIdx(txn, values) - if err != nil { - return err - } - return nil - }) - if errInTxn != nil { - return errInTxn - } - if e.scanRowCnt == 0 { - break - } - e.scanRowCnt = 0 - e.batchKeys = e.batchKeys[:0] - e.idxValues.Range(func(h kv.Handle, val interface{}) bool { - e.idxValues.Delete(h) - return true - }) - } - return nil -} - -func (e *CleanupIndexExec) buildIndexScan(ctx context.Context, txn kv.Transaction) (distsql.SelectResult, error) { - dagPB, err := e.buildIdxDAGPB() - if err != nil { - return nil, err - } - sc := e.Ctx().GetSessionVars().StmtCtx - var builder distsql.RequestBuilder - ranges := ranger.FullRange() - keyRanges, err := distsql.IndexRangesToKVRanges(sc, e.physicalID, e.index.Meta().ID, ranges) - if err != nil { - return nil, err - } - err = keyRanges.SetToNonPartitioned() - if err != nil { - return nil, err - } - keyRanges.FirstPartitionRange()[0].StartKey = kv.Key(e.lastIdxKey).PrefixNext() - kvReq, err := builder.SetWrappedKeyRanges(keyRanges). - SetDAGRequest(dagPB). - SetStartTS(txn.StartTS()). - SetKeepOrder(true). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetConnID(e.Ctx().GetSessionVars().ConnectionID). - Build() - if err != nil { - return nil, err - } - - kvReq.Concurrency = 1 - result, err := distsql.Select(ctx, e.Ctx(), kvReq, e.getIdxColTypes()) - if err != nil { - return nil, err - } - return result, nil -} - -// Open implements the Executor Open interface. -func (e *CleanupIndexExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - return e.init() -} - -func (e *CleanupIndexExec) init() error { - e.idxChunk = chunk.New(e.getIdxColTypes(), e.InitCap(), e.MaxChunkSize()) - e.idxValues = kv.NewHandleMap() - e.batchKeys = make([]kv.Key, 0, e.batchSize) - e.idxValsBufs = make([][]types.Datum, e.batchSize) - sc := e.Ctx().GetSessionVars().StmtCtx - idxKey, _, err := e.index.GenIndexKey(sc, []types.Datum{{}}, kv.IntHandle(math.MinInt64), nil) - if err != nil { - return err - } - e.lastIdxKey = idxKey - return nil -} - -func (e *CleanupIndexExec) buildIdxDAGPB() (*tipb.DAGRequest, error) { - dagReq := &tipb.DAGRequest{} - dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) - sc := e.Ctx().GetSessionVars().StmtCtx - dagReq.Flags = sc.PushDownFlags() - for i := range e.columns { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) - } - - execPB := e.constructIndexScanPB() - dagReq.Executors = append(dagReq.Executors, execPB) - err := tables.SetPBColumnsDefaultValue(e.Ctx(), dagReq.Executors[0].IdxScan.Columns, e.columns) - if err != nil { - return nil, err - } - - limitExec := e.constructLimitPB() - dagReq.Executors = append(dagReq.Executors, limitExec) - distsql.SetEncodeType(e.Ctx(), dagReq) - return dagReq, nil -} - -func (e *CleanupIndexExec) constructIndexScanPB() *tipb.Executor { - idxExec := &tipb.IndexScan{ - TableId: e.physicalID, - IndexId: e.index.Meta().ID, - Columns: util.ColumnsToProto(e.columns, e.table.Meta().PKIsHandle, true), - PrimaryColumnIds: tables.TryGetCommonPkColumnIds(e.table.Meta()), - } - return &tipb.Executor{Tp: tipb.ExecType_TypeIndexScan, IdxScan: idxExec} -} - -func (e *CleanupIndexExec) constructLimitPB() *tipb.Executor { - limitExec := &tipb.Limit{ - Limit: e.batchSize, - } - return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} -} - -// Close implements the Executor Close interface. -func (*CleanupIndexExec) Close() error { - return nil -} diff --git a/executor/aggfuncs/BUILD.bazel b/executor/aggfuncs/BUILD.bazel deleted file mode 100644 index 5b34257948f23..0000000000000 --- a/executor/aggfuncs/BUILD.bazel +++ /dev/null @@ -1,117 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "aggfuncs", - srcs = [ - "aggfuncs.go", - "builder.go", - "func_avg.go", - "func_bitfuncs.go", - "func_count.go", - "func_count_distinct.go", - "func_cume_dist.go", - "func_first_row.go", - "func_group_concat.go", - "func_json_arrayagg.go", - "func_json_objectagg.go", - "func_lead_lag.go", - "func_max_min.go", - "func_ntile.go", - "func_percent_rank.go", - "func_percentile.go", - "func_rank.go", - "func_stddevpop.go", - "func_stddevsamp.go", - "func_sum.go", - "func_value.go", - "func_varpop.go", - "func_varsamp.go", - "row_number.go", - ], - importpath = "github.com/pingcap/tidb/executor/aggfuncs", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//expression/aggregation", - "//parser/ast", - "//parser/charset", - "//parser/mysql", - "//planner/core", - "//planner/util", - "//sessionctx", - "//sessionctx/variable", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/selection", - "//util/set", - "//util/stringutil", - "@com_github_dgryski_go_farm//:go-farm", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "aggfuncs_test", - timeout = "moderate", - srcs = [ - "aggfunc_test.go", - "export_test.go", - "func_avg_test.go", - "func_bitfuncs_test.go", - "func_count_test.go", - "func_cume_dist_test.go", - "func_first_row_test.go", - "func_group_concat_test.go", - "func_json_arrayagg_test.go", - "func_json_objectagg_test.go", - "func_lead_lag_test.go", - "func_max_min_test.go", - "func_ntile_test.go", - "func_percent_rank_test.go", - "func_percentile_test.go", - "func_rank_test.go", - "func_stddevpop_test.go", - "func_stddevsamp_test.go", - "func_sum_test.go", - "func_value_test.go", - "func_varpop_test.go", - "func_varsamp_test.go", - "main_test.go", - "row_number_test.go", - "window_func_test.go", - ], - embed = [":aggfuncs"], - flaky = True, - race = "on", - shard_count = 48, - deps = [ - "//expression", - "//expression/aggregation", - "//parser/ast", - "//parser/charset", - "//parser/mysql", - "//planner/util", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/hack", - "//util/mock", - "//util/set", - "@com_github_dgryski_go_farm//:go-farm", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/aggfuncs/builder.go b/executor/aggfuncs/builder.go deleted file mode 100644 index 53bf87bcaa2e9..0000000000000 --- a/executor/aggfuncs/builder.go +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package aggfuncs - -import ( - "context" - "fmt" - "strconv" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// Build is used to build a specific AggFunc implementation according to the -// input aggFuncDesc. -func Build(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - switch aggFuncDesc.Name { - case ast.AggFuncCount: - return buildCount(aggFuncDesc, ordinal) - case ast.AggFuncSum: - return buildSum(ctx, aggFuncDesc, ordinal) - case ast.AggFuncAvg: - return buildAvg(ctx, aggFuncDesc, ordinal) - case ast.AggFuncFirstRow: - return buildFirstRow(aggFuncDesc, ordinal) - case ast.AggFuncMax: - return buildMaxMin(aggFuncDesc, ordinal, true) - case ast.AggFuncMin: - return buildMaxMin(aggFuncDesc, ordinal, false) - case ast.AggFuncGroupConcat: - return buildGroupConcat(ctx, aggFuncDesc, ordinal) - case ast.AggFuncBitOr: - return buildBitOr(aggFuncDesc, ordinal) - case ast.AggFuncBitXor: - return buildBitXor(aggFuncDesc, ordinal) - case ast.AggFuncBitAnd: - return buildBitAnd(aggFuncDesc, ordinal) - case ast.AggFuncVarPop: - return buildVarPop(aggFuncDesc, ordinal) - case ast.AggFuncStddevPop: - return buildStdDevPop(aggFuncDesc, ordinal) - case ast.AggFuncJsonArrayagg: - return buildJSONArrayagg(aggFuncDesc, ordinal) - case ast.AggFuncJsonObjectAgg: - return buildJSONObjectAgg(aggFuncDesc, ordinal) - case ast.AggFuncApproxCountDistinct: - return buildApproxCountDistinct(aggFuncDesc, ordinal) - case ast.AggFuncApproxPercentile: - return buildApproxPercentile(ctx, aggFuncDesc, ordinal) - case ast.AggFuncVarSamp: - return buildVarSamp(aggFuncDesc, ordinal) - case ast.AggFuncStddevSamp: - return buildStddevSamp(aggFuncDesc, ordinal) - } - return nil -} - -// BuildWindowFunctions builds specific window function according to function description and order by columns. -func BuildWindowFunctions(ctx sessionctx.Context, windowFuncDesc *aggregation.AggFuncDesc, ordinal int, orderByCols []*expression.Column) AggFunc { - switch windowFuncDesc.Name { - case ast.WindowFuncRank: - return buildRank(ordinal, orderByCols, false) - case ast.WindowFuncDenseRank: - return buildRank(ordinal, orderByCols, true) - case ast.WindowFuncRowNumber: - return buildRowNumber(windowFuncDesc, ordinal) - case ast.WindowFuncFirstValue: - return buildFirstValue(windowFuncDesc, ordinal) - case ast.WindowFuncLastValue: - return buildLastValue(windowFuncDesc, ordinal) - case ast.WindowFuncCumeDist: - return buildCumeDist(ordinal, orderByCols) - case ast.WindowFuncNthValue: - return buildNthValue(windowFuncDesc, ordinal) - case ast.WindowFuncNtile: - return buildNtile(windowFuncDesc, ordinal) - case ast.WindowFuncPercentRank: - return buildPercentRank(ordinal, orderByCols) - case ast.WindowFuncLead: - return buildLead(ctx, windowFuncDesc, ordinal) - case ast.WindowFuncLag: - return buildLag(ctx, windowFuncDesc, ordinal) - case ast.AggFuncMax: - // The max/min aggFunc using in the window function will using the sliding window algo. - return buildMaxMinInWindowFunction(windowFuncDesc, ordinal, true) - case ast.AggFuncMin: - return buildMaxMinInWindowFunction(windowFuncDesc, ordinal, false) - default: - return Build(ctx, windowFuncDesc, ordinal) - } -} - -func buildApproxCountDistinct(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseApproxCountDistinct{baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - }} - - // In partition table, union need to compute partial result into partial result. - // We can detect and handle this case by checking whether return type is string. - - switch aggFuncDesc.RetTp.GetType() { - case mysql.TypeLonglong: - switch aggFuncDesc.Mode { - case aggregation.CompleteMode: - return &approxCountDistinctOriginal{base} - case aggregation.Partial1Mode: - return &approxCountDistinctPartial1{approxCountDistinctOriginal{base}} - case aggregation.Partial2Mode: - return &approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}} - case aggregation.FinalMode: - return &approxCountDistinctFinal{approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}}} - } - case mysql.TypeString: - switch aggFuncDesc.Mode { - case aggregation.CompleteMode, aggregation.Partial1Mode: - return &approxCountDistinctPartial1{approxCountDistinctOriginal{base}} - case aggregation.Partial2Mode, aggregation.FinalMode: - return &approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}} - } - } - - return nil -} - -func buildApproxPercentile(sctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - if aggFuncDesc.Mode == aggregation.DedupMode { - return nil - } - - // Checked while building descriptor - percent, _, err := aggFuncDesc.Args[1].EvalInt(sctx, chunk.Row{}) - if err != nil { - // Should not reach here - logutil.BgLogger().Error("Error happened when buildApproxPercentile", zap.Error(err)) - return nil - } - - base := basePercentile{percent: int(percent), baseAggFunc: baseAggFunc{args: aggFuncDesc.Args, ordinal: ordinal}} - - evalType := aggFuncDesc.Args[0].GetType().EvalType() - if aggFuncDesc.Args[0].GetType().GetType() == mysql.TypeBit { - evalType = types.ETString // same as other aggregate function - } - switch aggFuncDesc.Mode { - case aggregation.CompleteMode, aggregation.Partial1Mode, aggregation.FinalMode: - switch evalType { - case types.ETInt: - return &percentileOriginal4Int{base} - case types.ETReal: - return &percentileOriginal4Real{base} - case types.ETDecimal: - return &percentileOriginal4Decimal{base} - case types.ETDatetime, types.ETTimestamp: - return &percentileOriginal4Time{base} - case types.ETDuration: - return &percentileOriginal4Duration{base} - default: - // Return NULL in any case - return &base - } - } - - return nil -} - -// buildCount builds the AggFunc implementation for function "COUNT". -func buildCount(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - // If mode is DedupMode, we return nil for not implemented. - if aggFuncDesc.Mode == aggregation.DedupMode { - return nil // not implemented yet. - } - - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - retTp: aggFuncDesc.RetTp, - } - - // If HasDistinct and mode is CompleteMode or Partial1Mode, we should - // use countOriginalWithDistinct. - if aggFuncDesc.HasDistinct && - (aggFuncDesc.Mode == aggregation.CompleteMode || aggFuncDesc.Mode == aggregation.Partial1Mode) { - if len(base.args) == 1 { - // optimize with single column - // TODO: because Time and JSON does not have `hashcode()` or similar method - // so they're in exception for now. - // TODO: add hashCode method for all evaluate types (decimal, Time, Duration, JSON). - // https://github.com/pingcap/tidb/issues/15857 - switch aggFuncDesc.Args[0].GetType().EvalType() { - case types.ETInt: - return &countOriginalWithDistinct4Int{baseCount{base}} - case types.ETReal: - return &countOriginalWithDistinct4Real{baseCount{base}} - case types.ETDecimal: - return &countOriginalWithDistinct4Decimal{baseCount{base}} - case types.ETDuration: - return &countOriginalWithDistinct4Duration{baseCount{base}} - case types.ETString: - return &countOriginalWithDistinct4String{baseCount{base}} - } - } - return &countOriginalWithDistinct{baseCount{base}} - } - - switch aggFuncDesc.Mode { - case aggregation.CompleteMode, aggregation.Partial1Mode: - switch aggFuncDesc.Args[0].GetType().EvalType() { - case types.ETInt: - return &countOriginal4Int{baseCount{base}} - case types.ETReal: - return &countOriginal4Real{baseCount{base}} - case types.ETDecimal: - return &countOriginal4Decimal{baseCount{base}} - case types.ETTimestamp, types.ETDatetime: - return &countOriginal4Time{baseCount{base}} - case types.ETDuration: - return &countOriginal4Duration{baseCount{base}} - case types.ETJson: - return &countOriginal4JSON{baseCount{base}} - case types.ETString: - return &countOriginal4String{baseCount{base}} - } - case aggregation.Partial2Mode, aggregation.FinalMode: - return &countPartial{baseCount{base}} - } - - return nil -} - -// buildSum builds the AggFunc implementation for function "SUM". -func buildSum(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseSumAggFunc{ - baseAggFunc: baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - retTp: aggFuncDesc.RetTp, - }, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - switch aggFuncDesc.RetTp.EvalType() { - case types.ETDecimal: - if aggFuncDesc.HasDistinct { - return &sum4DistinctDecimal{base} - } - return &sum4Decimal{base} - default: - if aggFuncDesc.HasDistinct { - return &sum4DistinctFloat64{base} - } - if ctx.GetSessionVars().WindowingUseHighPrecision { - return &sum4Float64HighPrecision{baseSum4Float64{base}} - } - return &sum4Float64{baseSum4Float64{base}} - } - } -} - -// buildAvg builds the AggFunc implementation for function "AVG". -func buildAvg(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - retTp: aggFuncDesc.RetTp, - } - switch aggFuncDesc.Mode { - // Build avg functions which consume the original data and remove the - // duplicated input of the same group. - case aggregation.DedupMode: - return nil // not implemented yet. - - // Build avg functions which consume the original data and update their - // partial results. - case aggregation.CompleteMode, aggregation.Partial1Mode: - switch aggFuncDesc.RetTp.EvalType() { - case types.ETDecimal: - if aggFuncDesc.HasDistinct { - return &avgOriginal4DistinctDecimal{base} - } - return &avgOriginal4Decimal{baseAvgDecimal{base}} - default: - if aggFuncDesc.HasDistinct { - return &avgOriginal4DistinctFloat64{base} - } - if ctx.GetSessionVars().WindowingUseHighPrecision { - return &avgOriginal4Float64HighPrecision{baseAvgFloat64{base}} - } - return &avgOriginal4Float64{avgOriginal4Float64HighPrecision{baseAvgFloat64{base}}} - } - - // Build avg functions which consume the partial result of other avg - // functions and update their partial results. - case aggregation.Partial2Mode, aggregation.FinalMode: - switch aggFuncDesc.RetTp.GetType() { - case mysql.TypeNewDecimal: - return &avgPartial4Decimal{baseAvgDecimal{base}} - case mysql.TypeDouble: - return &avgPartial4Float64{baseAvgFloat64{base}} - } - } - return nil -} - -// buildFirstRow builds the AggFunc implementation for function "FIRST_ROW". -func buildFirstRow(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - retTp: aggFuncDesc.RetTp, - } - evalType, fieldType := aggFuncDesc.RetTp.EvalType(), aggFuncDesc.RetTp - if fieldType.GetType() == mysql.TypeBit { - evalType = types.ETString - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - default: - switch fieldType.GetType() { - case mysql.TypeEnum: - return &firstRow4Enum{base} - case mysql.TypeSet: - return &firstRow4Set{base} - } - - switch evalType { - case types.ETInt: - return &firstRow4Int{base} - case types.ETReal: - switch fieldType.GetType() { - case mysql.TypeFloat: - return &firstRow4Float32{base} - case mysql.TypeDouble: - return &firstRow4Float64{base} - } - case types.ETDecimal: - return &firstRow4Decimal{base} - case types.ETDatetime, types.ETTimestamp: - return &firstRow4Time{base} - case types.ETDuration: - return &firstRow4Duration{base} - case types.ETString: - return &firstRow4String{base} - case types.ETJson: - return &firstRow4JSON{base} - } - } - return nil -} - -// buildMaxMin builds the AggFunc implementation for function "MAX" and "MIN". -func buildMaxMin(aggFuncDesc *aggregation.AggFuncDesc, ordinal int, isMax bool) AggFunc { - base := baseMaxMinAggFunc{ - baseAggFunc: baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - retTp: aggFuncDesc.RetTp, - }, - isMax: isMax, - collator: collate.GetCollator(aggFuncDesc.RetTp.GetCollate()), - } - evalType, fieldType := aggFuncDesc.RetTp.EvalType(), aggFuncDesc.RetTp - if fieldType.GetType() == mysql.TypeBit { - evalType = types.ETString - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - default: - switch fieldType.GetType() { - case mysql.TypeEnum: - return &maxMin4Enum{base} - case mysql.TypeSet: - return &maxMin4Set{base} - } - - switch evalType { - case types.ETInt: - if mysql.HasUnsignedFlag(fieldType.GetFlag()) { - return &maxMin4Uint{base} - } - return &maxMin4Int{base} - case types.ETReal: - switch fieldType.GetType() { - case mysql.TypeFloat: - return &maxMin4Float32{base} - case mysql.TypeDouble: - return &maxMin4Float64{base} - } - case types.ETDecimal: - return &maxMin4Decimal{base} - case types.ETString: - return &maxMin4String{baseMaxMinAggFunc: base, retTp: aggFuncDesc.RetTp} - case types.ETDatetime, types.ETTimestamp: - return &maxMin4Time{base} - case types.ETDuration: - return &maxMin4Duration{base} - case types.ETJson: - return &maxMin4JSON{base} - } - } - return nil -} - -// buildMaxMin builds the AggFunc implementation for function "MAX" and "MIN" using by window function. -func buildMaxMinInWindowFunction(aggFuncDesc *aggregation.AggFuncDesc, ordinal int, isMax bool) AggFunc { - base := buildMaxMin(aggFuncDesc, ordinal, isMax) - // build max/min aggFunc for window function using sliding window - switch baseAggFunc := base.(type) { - case *maxMin4Int: - return &maxMin4IntSliding{*baseAggFunc, windowInfo{}} - case *maxMin4Uint: - return &maxMin4UintSliding{*baseAggFunc, windowInfo{}} - case *maxMin4Float32: - return &maxMin4Float32Sliding{*baseAggFunc, windowInfo{}} - case *maxMin4Float64: - return &maxMin4Float64Sliding{*baseAggFunc, windowInfo{}} - case *maxMin4Decimal: - return &maxMin4DecimalSliding{*baseAggFunc, windowInfo{}} - case *maxMin4String: - return &maxMin4StringSliding{*baseAggFunc, windowInfo{}} - case *maxMin4Time: - return &maxMin4TimeSliding{*baseAggFunc, windowInfo{}} - case *maxMin4Duration: - return &maxMin4DurationSliding{*baseAggFunc, windowInfo{}} - } - return base -} - -// buildGroupConcat builds the AggFunc implementation for function "GROUP_CONCAT". -func buildGroupConcat(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - // The last arg is promised to be a not-null string constant, so the error can be ignored. - c, _ := aggFuncDesc.Args[len(aggFuncDesc.Args)-1].(*expression.Constant) - sep, _, err := c.EvalString(nil, chunk.Row{}) - // This err should never happen. - if err != nil { - panic(fmt.Sprintf("Error happened when buildGroupConcat: %s", err.Error())) - } - var s string - s, err = ctx.GetSessionVars().GetSessionOrGlobalSystemVar(context.Background(), variable.GroupConcatMaxLen) - if err != nil { - panic(fmt.Sprintf("Error happened when buildGroupConcat: no system variable named '%s'", variable.GroupConcatMaxLen)) - } - maxLen, err := strconv.ParseUint(s, 10, 64) - // Should never happen - if err != nil { - panic(fmt.Sprintf("Error happened when buildGroupConcat: %s", err.Error())) - } - var truncated int32 - base := baseGroupConcat4String{ - baseAggFunc: baseAggFunc{ - args: aggFuncDesc.Args[:len(aggFuncDesc.Args)-1], - ordinal: ordinal, - }, - byItems: aggFuncDesc.OrderByItems, - sep: sep, - maxLen: maxLen, - truncated: &truncated, - } - if aggFuncDesc.HasDistinct { - if len(aggFuncDesc.OrderByItems) > 0 { - return &groupConcatDistinctOrder{base} - } - return &groupConcatDistinct{base} - } - if len(aggFuncDesc.OrderByItems) > 0 { - return &groupConcatOrder{base} - } - return &groupConcat{base} - } -} - -// buildBitOr builds the AggFunc implementation for function "BIT_OR". -func buildBitOr(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &bitOrUint64{baseBitAggFunc{base}} -} - -// buildBitXor builds the AggFunc implementation for function "BIT_XOR". -func buildBitXor(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &bitXorUint64{baseBitAggFunc{base}} -} - -// buildBitAnd builds the AggFunc implementation for function "BIT_AND". -func buildBitAnd(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &bitAndUint64{baseBitAggFunc{base}} -} - -// buildVarPop builds the AggFunc implementation for function "VAR_POP". -func buildVarPop(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseVarPopAggFunc{ - baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - }, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - if aggFuncDesc.HasDistinct { - return &varPop4DistinctFloat64{base} - } - return &varPop4Float64{base} - } -} - -// buildStdDevPop builds the AggFunc implementation for function "STD()/STDDEV()/STDDEV_POP()" -func buildStdDevPop(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseVarPopAggFunc{ - baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - }, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - if aggFuncDesc.HasDistinct { - return &stdDevPop4DistinctFloat64{varPop4DistinctFloat64{base}} - } - return &stdDevPop4Float64{varPop4Float64{base}} - } -} - -// buildVarSamp builds the AggFunc implementation for function "VAR_SAMP()" -func buildVarSamp(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseVarPopAggFunc{ - baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - }, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - if aggFuncDesc.HasDistinct { - return &varSamp4DistinctFloat64{varPop4DistinctFloat64{base}} - } - return &varSamp4Float64{varPop4Float64{base}} - } -} - -// buildStddevSamp builds the AggFunc implementation for function "STDDEV_SAMP()" -func buildStddevSamp(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseVarPopAggFunc{ - baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - }, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - if aggFuncDesc.HasDistinct { - return &stddevSamp4DistinctFloat64{varPop4DistinctFloat64{base}} - } - return &stddevSamp4Float64{varPop4Float64{base}} - } -} - -// buildJSONArrayagg builds the AggFunc implementation for function "json_arrayagg". -func buildJSONArrayagg(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - return &jsonArrayagg{base} - } -} - -// buildJSONObjectAgg builds the AggFunc implementation for function "json_objectagg". -func buildJSONObjectAgg(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - switch aggFuncDesc.Mode { - case aggregation.DedupMode: - return nil - default: - return &jsonObjectAgg{base} - } -} - -// buildRowNumber builds the AggFunc implementation for function "ROW_NUMBER". -func buildRowNumber(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &rowNumber{base} -} - -func buildRank(ordinal int, orderByCols []*expression.Column, isDense bool) AggFunc { - base := baseAggFunc{ - ordinal: ordinal, - } - r := &rank{baseAggFunc: base, isDense: isDense, rowComparer: buildRowComparer(orderByCols)} - return r -} - -func buildFirstValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &firstValue{baseAggFunc: base, tp: aggFuncDesc.RetTp} -} - -func buildLastValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - return &lastValue{baseAggFunc: base, tp: aggFuncDesc.RetTp} -} - -func buildCumeDist(ordinal int, orderByCols []*expression.Column) AggFunc { - base := baseAggFunc{ - ordinal: ordinal, - } - r := &cumeDist{baseAggFunc: base, rowComparer: buildRowComparer(orderByCols)} - return r -} - -func buildNthValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - // Already checked when building the function description. - nth, _, _ := expression.GetUint64FromConstant(aggFuncDesc.Args[1]) - return &nthValue{baseAggFunc: base, tp: aggFuncDesc.RetTp, nth: nth} -} - -func buildNtile(aggFuncDes *aggregation.AggFuncDesc, ordinal int) AggFunc { - base := baseAggFunc{ - args: aggFuncDes.Args, - ordinal: ordinal, - } - n, _, _ := expression.GetUint64FromConstant(aggFuncDes.Args[0]) - return &ntile{baseAggFunc: base, n: n} -} - -func buildPercentRank(ordinal int, orderByCols []*expression.Column) AggFunc { - base := baseAggFunc{ - ordinal: ordinal, - } - return &percentRank{baseAggFunc: base, rowComparer: buildRowComparer(orderByCols)} -} - -func buildLeadLag(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) baseLeadLag { - offset := uint64(1) - if len(aggFuncDesc.Args) >= 2 { - offset, _, _ = expression.GetUint64FromConstant(aggFuncDesc.Args[1]) - } - var defaultExpr expression.Expression - defaultExpr = expression.NewNull() - if len(aggFuncDesc.Args) == 3 { - defaultExpr = aggFuncDesc.Args[2] - if et, ok := defaultExpr.(*expression.Constant); ok { - res, err1 := et.Value.ConvertTo(ctx.GetSessionVars().StmtCtx, aggFuncDesc.RetTp) - if err1 == nil { - defaultExpr = &expression.Constant{Value: res, RetType: aggFuncDesc.RetTp} - } - } - } - base := baseAggFunc{ - args: aggFuncDesc.Args, - ordinal: ordinal, - } - ve, _ := buildValueEvaluator(aggFuncDesc.RetTp) - return baseLeadLag{baseAggFunc: base, offset: offset, defaultExpr: defaultExpr, valueEvaluator: ve} -} - -func buildLead(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - return &lead{buildLeadLag(ctx, aggFuncDesc, ordinal)} -} - -func buildLag(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { - return &lag{buildLeadLag(ctx, aggFuncDesc, ordinal)} -} diff --git a/executor/aggfuncs/main_test.go b/executor/aggfuncs/main_test.go deleted file mode 100644 index 0c0ed25104cda..0000000000000 --- a/executor/aggfuncs/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package aggfuncs - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/aggregate/BUILD.bazel b/executor/aggregate/BUILD.bazel deleted file mode 100644 index 09fa6b3b1296f..0000000000000 --- a/executor/aggregate/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "aggregate", - srcs = [ - "agg_hash_base_worker.go", - "agg_hash_executor.go", - "agg_hash_final_worker.go", - "agg_hash_partial_worker.go", - "agg_spill.go", - "agg_stream_executor.go", - "agg_util.go", - ], - importpath = "github.com/pingcap/tidb/executor/aggregate", - visibility = ["//visibility:public"], - deps = [ - "//executor/aggfuncs", - "//executor/internal/exec", - "//executor/internal/vecgroupchecker", - "//expression", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//types", - "//util/channel", - "//util/chunk", - "//util/codec", - "//util/disk", - "//util/execdetails", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/set", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_twmb_murmur3//:murmur3", - "@org_uber_go_zap//:zap", - ], -) diff --git a/executor/analyze.go b/executor/analyze.go deleted file mode 100644 index e813d67567459..0000000000000 --- a/executor/analyze.go +++ /dev/null @@ -1,721 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "fmt" - "math" - "net" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" - "golang.org/x/sync/errgroup" -) - -var _ exec.Executor = &AnalyzeExec{} - -// AnalyzeExec represents Analyze executor. -type AnalyzeExec struct { - exec.BaseExecutor - tasks []*analyzeTask - wg util.WaitGroupWrapper - opts map[ast.AnalyzeOptionType]uint64 - OptionsMap map[int64]core.V2AnalyzeOptions -} - -var ( - // RandSeed is the seed for randing package. - // It's public for test. - RandSeed = int64(1) - - // MaxRegionSampleSize is the max sample size for one region when analyze v1 collects samples from table. - // It's public for test. - MaxRegionSampleSize = int64(1000) -) - -const ( - maxSketchSize = 10000 -) - -type taskType int - -const ( - colTask taskType = iota - idxTask -) - -// Next implements the Executor Next interface. -// It will collect all the sample task and run them concurrently. -func (e *AnalyzeExec) Next(ctx context.Context, _ *chunk.Chunk) error { - statsHandle := domain.GetDomain(e.Ctx()).StatsHandle() - infoSchema := sessiontxn.GetTxnManager(e.Ctx()).GetTxnInfoSchema() - sessionVars := e.Ctx().GetSessionVars() - - // Filter the locked tables. - tasks, needAnalyzeTableCnt, skippedTables, err := filterAndCollectTasks(e.tasks, statsHandle, infoSchema) - if err != nil { - return err - } - warnLockedTableMsg(sessionVars, needAnalyzeTableCnt, skippedTables) - - if len(tasks) == 0 { - return nil - } - - // Get the min number of goroutines for parallel execution. - concurrency, err := getBuildStatsConcurrency(e.Ctx()) - if err != nil { - return err - } - concurrency = min(len(tasks), concurrency) - - // Start workers with channel to collect results. - taskCh := make(chan *analyzeTask, concurrency) - resultChLen := min(concurrency*2, len(tasks)) - resultsCh := make(chan *statistics.AnalyzeResults, resultChLen) - for i := 0; i < concurrency; i++ { - e.wg.Run(func() { e.analyzeWorker(taskCh, resultsCh) }) - } - pruneMode := variable.PartitionPruneMode(sessionVars.PartitionPruneMode.Load()) - // needGlobalStats used to indicate whether we should merge the partition-level stats to global-level stats. - needGlobalStats := pruneMode == variable.Dynamic - globalStatsMap := make(map[globalStatsKey]globalStatsInfo) - g, _ := errgroup.WithContext(ctx) - g.Go(func() error { - return e.handleResultsError(ctx, concurrency, needGlobalStats, globalStatsMap, resultsCh, len(tasks)) - }) - - for _, task := range tasks { - prepareV2AnalyzeJobInfo(task.colExec, false) - AddNewAnalyzeJob(e.Ctx(), task.job) - } - failpoint.Inject("mockKillPendingAnalyzeJob", func() { - dom := domain.GetDomain(e.Ctx()) - dom.SysProcTracker().KillSysProcess(dom.GetAutoAnalyzeProcID()) - }) - - for _, task := range tasks { - taskCh <- task - } - close(taskCh) - - // Wait all workers done and close the results channel. - e.wg.Wait() - close(resultsCh) - err = g.Wait() - for _, task := range tasks { - if task.colExec != nil && task.colExec.memTracker != nil { - task.colExec.memTracker.Detach() - } - } - if err != nil { - return err - } - failpoint.Inject("mockKillFinishedAnalyzeJob", func() { - dom := domain.GetDomain(e.Ctx()) - dom.SysProcTracker().KillSysProcess(dom.GetAutoAnalyzeProcID()) - }) - // If we enabled dynamic prune mode, then we need to generate global stats here for partition tables. - if needGlobalStats { - err = e.handleGlobalStats(ctx, globalStatsMap) - if err != nil { - return err - } - } - - // Update analyze options to mysql.analyze_options for auto analyze. - err = e.saveV2AnalyzeOpts() - if err != nil { - sessionVars.StmtCtx.AppendWarning(err) - } - return statsHandle.Update(infoSchema) -} - -// filterAndCollectTasks filters the tasks that are not locked and collects the table IDs. -func filterAndCollectTasks(tasks []*analyzeTask, statsHandle *handle.Handle, infoSchema infoschema.InfoSchema) ([]*analyzeTask, uint, []string, error) { - var ( - filteredTasks []*analyzeTask - skippedTables []string - needAnalyzeTableCnt uint - // tidMap is used to deduplicate table IDs. - // In stats v1, analyze for each index is a single task, and they have the same table id. - tidAndPidsMap = make(map[int64]struct{}, len(tasks)) - ) - - lockedTableAndPartitionIDs, err := getLockedTableAndPartitionIDs(statsHandle, tasks) - if err != nil { - return nil, 0, nil, err - } - - for _, task := range tasks { - // Check if the table or partition is locked. - tableID := getTableIDFromTask(task) - _, isLocked := lockedTableAndPartitionIDs[tableID.TableID] - // If the whole table is not locked, we should check whether the partition is locked. - if !isLocked && tableID.IsPartitionTable() { - _, isLocked = lockedTableAndPartitionIDs[tableID.PartitionID] - } - - // Only analyze the table that is not locked. - if !isLocked { - filteredTasks = append(filteredTasks, task) - } - - // Get the physical table ID. - physicalTableID := tableID.TableID - if tableID.IsPartitionTable() { - physicalTableID = tableID.PartitionID - } - if _, ok := tidAndPidsMap[physicalTableID]; !ok { - if isLocked { - if tableID.IsPartitionTable() { - tbl, _, def := infoSchema.FindTableByPartitionID(tableID.PartitionID) - if def == nil { - logutil.BgLogger().Warn("Unknown partition ID in analyze task", zap.Int64("pid", tableID.PartitionID)) - } else { - schema, _ := infoSchema.SchemaByTable(tbl.Meta()) - skippedTables = append(skippedTables, fmt.Sprintf("%s.%s partition (%s)", schema.Name, tbl.Meta().Name.O, def.Name.O)) - } - } else { - tbl, ok := infoSchema.TableByID(physicalTableID) - if !ok { - logutil.BgLogger().Warn("Unknown table ID in analyze task", zap.Int64("tid", physicalTableID)) - } else { - schema, _ := infoSchema.SchemaByTable(tbl.Meta()) - skippedTables = append(skippedTables, fmt.Sprintf("%s.%s", schema.Name, tbl.Meta().Name.O)) - } - } - } else { - needAnalyzeTableCnt++ - } - tidAndPidsMap[physicalTableID] = struct{}{} - } - } - - return filteredTasks, needAnalyzeTableCnt, skippedTables, nil -} - -// getLockedTableAndPartitionIDs queries the locked tables and partitions. -func getLockedTableAndPartitionIDs(statsHandle *handle.Handle, tasks []*analyzeTask) (map[int64]struct{}, error) { - tidAndPids := make([]int64, 0, len(tasks)) - // Check the locked tables in one transaction. - // We need to check all tables and its partitions. - // Because if the whole table is locked, we should skip all partitions. - for _, task := range tasks { - tableID := getTableIDFromTask(task) - tidAndPids = append(tidAndPids, tableID.TableID) - if tableID.IsPartitionTable() { - tidAndPids = append(tidAndPids, tableID.PartitionID) - } - } - return statsHandle.GetLockedTables(tidAndPids...) -} - -// warnLockedTableMsg warns the locked table IDs. -func warnLockedTableMsg(sessionVars *variable.SessionVars, needAnalyzeTableCnt uint, skippedTables []string) { - if len(skippedTables) > 0 { - tables := strings.Join(skippedTables, ", ") - var msg string - if len(skippedTables) > 1 { - msg = "skip analyze locked tables: %s" - if needAnalyzeTableCnt > 0 { - msg = "skip analyze locked tables: %s, other tables will be analyzed" - } - } else { - msg = "skip analyze locked table: %s" - } - sessionVars.StmtCtx.AppendWarning(errors.Errorf(msg, tables)) - } -} - -func getTableIDFromTask(task *analyzeTask) statistics.AnalyzeTableID { - switch task.taskType { - case colTask: - return task.colExec.tableID - case idxTask: - return task.idxExec.tableID - } - - panic("unreachable") -} - -func (e *AnalyzeExec) saveV2AnalyzeOpts() error { - if !variable.PersistAnalyzeOptions.Load() || len(e.OptionsMap) == 0 { - return nil - } - // only to save table options if dynamic prune mode - dynamicPrune := variable.PartitionPruneMode(e.Ctx().GetSessionVars().PartitionPruneMode.Load()) == variable.Dynamic - toSaveMap := make(map[int64]core.V2AnalyzeOptions) - for id, opts := range e.OptionsMap { - if !opts.IsPartition || !dynamicPrune { - toSaveMap[id] = opts - } - } - sql := new(strings.Builder) - sqlexec.MustFormatSQL(sql, "REPLACE INTO mysql.analyze_options (table_id,sample_num,sample_rate,buckets,topn,column_choice,column_ids) VALUES ") - idx := 0 - for _, opts := range toSaveMap { - sampleNum := opts.RawOpts[ast.AnalyzeOptNumSamples] - sampleRate := float64(0) - if val, ok := opts.RawOpts[ast.AnalyzeOptSampleRate]; ok { - sampleRate = math.Float64frombits(val) - } - buckets := opts.RawOpts[ast.AnalyzeOptNumBuckets] - topn := int64(-1) - if val, ok := opts.RawOpts[ast.AnalyzeOptNumTopN]; ok { - topn = int64(val) - } - colChoice := opts.ColChoice.String() - colIDs := make([]string, len(opts.ColumnList)) - for i, colInfo := range opts.ColumnList { - colIDs[i] = strconv.FormatInt(colInfo.ID, 10) - } - colIDStrs := strings.Join(colIDs, ",") - sqlexec.MustFormatSQL(sql, "(%?,%?,%?,%?,%?,%?,%?)", opts.PhyTableID, sampleNum, sampleRate, buckets, topn, colChoice, colIDStrs) - if idx < len(toSaveMap)-1 { - sqlexec.MustFormatSQL(sql, ",") - } - idx++ - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) - _, _, err := exec.ExecRestrictedSQL(ctx, nil, sql.String()) - if err != nil { - return err - } - return nil -} - -func recordHistoricalStats(sctx sessionctx.Context, tableID int64) error { - statsHandle := domain.GetDomain(sctx).StatsHandle() - historicalStatsEnabled, err := statsHandle.CheckHistoricalStatsEnable() - if err != nil { - return errors.Errorf("check tidb_enable_historical_stats failed: %v", err) - } - if !historicalStatsEnabled { - return nil - } - historicalStatsWorker := domain.GetDomain(sctx).GetHistoricalStatsWorker() - historicalStatsWorker.SendTblToDumpHistoricalStats(tableID) - return nil -} - -// handleResultsError will handle the error fetch from resultsCh and record it in log -func (e *AnalyzeExec) handleResultsError( - ctx context.Context, - concurrency int, - needGlobalStats bool, - globalStatsMap globalStatsMap, - resultsCh <-chan *statistics.AnalyzeResults, - taskNum int, -) error { - partitionStatsConcurrency := e.Ctx().GetSessionVars().AnalyzePartitionConcurrency - // the concurrency of handleResultsError cannot be more than partitionStatsConcurrency - partitionStatsConcurrency = min(taskNum, partitionStatsConcurrency) - // If partitionStatsConcurrency > 1, we will try to demand extra session from Domain to save Analyze results in concurrency. - // If there is no extra session we can use, we will save analyze results in single-thread. - if partitionStatsConcurrency > 1 { - dom := domain.GetDomain(e.Ctx()) - subSctxs := dom.FetchAnalyzeExec(partitionStatsConcurrency) - if len(subSctxs) > 0 { - defer func() { - dom.ReleaseAnalyzeExec(subSctxs) - }() - internalCtx := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - err := e.handleResultsErrorWithConcurrency(internalCtx, concurrency, needGlobalStats, subSctxs, globalStatsMap, resultsCh) - return err - } - } - - tableIDs := map[int64]struct{}{} - - // save analyze results in single-thread. - statsHandle := domain.GetDomain(e.Ctx()).StatsHandle() - panicCnt := 0 - var err error - for panicCnt < concurrency { - results, ok := <-resultsCh - if !ok { - break - } - if results.Err != nil { - err = results.Err - if isAnalyzeWorkerPanic(err) { - panicCnt++ - } else { - logutil.Logger(ctx).Error("analyze failed", zap.Error(err)) - } - finishJobWithLog(e.Ctx(), results.Job, err) - continue - } - handleGlobalStats(needGlobalStats, globalStatsMap, results) - tableIDs[results.TableID.GetStatisticsID()] = struct{}{} - - if err1 := statsHandle.SaveTableStatsToStorage(results, e.Ctx().GetSessionVars().EnableAnalyzeSnapshot, handle.StatsMetaHistorySourceAnalyze); err1 != nil { - tableID := results.TableID.TableID - err = err1 - logutil.Logger(ctx).Error("save table stats to storage failed", zap.Error(err), zap.Int64("tableID", tableID)) - finishJobWithLog(e.Ctx(), results.Job, err) - } else { - finishJobWithLog(e.Ctx(), results.Job, nil) - } - if atomic.LoadUint32(&e.Ctx().GetSessionVars().Killed) == 1 { - finishJobWithLog(e.Ctx(), results.Job, exeerrors.ErrQueryInterrupted) - return errors.Trace(exeerrors.ErrQueryInterrupted) - } - } - // Dump stats to historical storage. - for tableID := range tableIDs { - if err := recordHistoricalStats(e.Ctx(), tableID); err != nil { - logutil.BgLogger().Error("record historical stats failed", zap.Error(err)) - } - } - - return err -} - -func (e *AnalyzeExec) handleResultsErrorWithConcurrency(ctx context.Context, statsConcurrency int, needGlobalStats bool, - subSctxs []sessionctx.Context, - globalStatsMap globalStatsMap, resultsCh <-chan *statistics.AnalyzeResults) error { - partitionStatsConcurrency := len(subSctxs) - - var wg util.WaitGroupWrapper - saveResultsCh := make(chan *statistics.AnalyzeResults, partitionStatsConcurrency) - errCh := make(chan error, partitionStatsConcurrency) - for i := 0; i < partitionStatsConcurrency; i++ { - worker := newAnalyzeSaveStatsWorker(saveResultsCh, subSctxs[i], errCh, &e.Ctx().GetSessionVars().Killed) - ctx1 := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - wg.Run(func() { - worker.run(ctx1, e.Ctx().GetSessionVars().EnableAnalyzeSnapshot) - }) - } - tableIDs := map[int64]struct{}{} - panicCnt := 0 - var err error - for panicCnt < statsConcurrency { - if atomic.LoadUint32(&e.Ctx().GetSessionVars().Killed) == 1 { - close(saveResultsCh) - return errors.Trace(exeerrors.ErrQueryInterrupted) - } - results, ok := <-resultsCh - if !ok { - break - } - if results.Err != nil { - err = results.Err - if isAnalyzeWorkerPanic(err) { - panicCnt++ - } else { - logutil.Logger(ctx).Error("analyze failed", zap.Error(err)) - } - finishJobWithLog(e.Ctx(), results.Job, err) - continue - } - handleGlobalStats(needGlobalStats, globalStatsMap, results) - tableIDs[results.TableID.GetStatisticsID()] = struct{}{} - saveResultsCh <- results - } - close(saveResultsCh) - wg.Wait() - close(errCh) - if len(errCh) > 0 { - errMsg := make([]string, 0) - for err1 := range errCh { - errMsg = append(errMsg, err1.Error()) - } - err = errors.New(strings.Join(errMsg, ",")) - } - for tableID := range tableIDs { - // Dump stats to historical storage. - if err := recordHistoricalStats(e.Ctx(), tableID); err != nil { - logutil.BgLogger().Error("record historical stats failed", zap.Error(err)) - } - } - return err -} - -func (e *AnalyzeExec) analyzeWorker(taskCh <-chan *analyzeTask, resultsCh chan<- *statistics.AnalyzeResults) { - var task *analyzeTask - defer func() { - if r := recover(); r != nil { - logutil.BgLogger().Error("analyze worker panicked", zap.Any("recover", r), zap.Stack("stack")) - metrics.PanicCounter.WithLabelValues(metrics.LabelAnalyze).Inc() - resultsCh <- &statistics.AnalyzeResults{ - Err: getAnalyzePanicErr(r), - Job: task.job, - } - } - }() - for { - var ok bool - task, ok = <-taskCh - if !ok { - break - } - StartAnalyzeJob(e.Ctx(), task.job) - switch task.taskType { - case colTask: - resultsCh <- analyzeColumnsPushDownEntry(task.colExec) - case idxTask: - resultsCh <- analyzeIndexPushdown(task.idxExec) - } - } -} - -type analyzeTask struct { - taskType taskType - idxExec *AnalyzeIndexExec - colExec *AnalyzeColumnsExec - job *statistics.AnalyzeJob -} - -type baseAnalyzeExec struct { - ctx sessionctx.Context - tableID statistics.AnalyzeTableID - concurrency int - analyzePB *tipb.AnalyzeReq - opts map[ast.AnalyzeOptionType]uint64 - job *statistics.AnalyzeJob - snapshot uint64 -} - -// AddNewAnalyzeJob records the new analyze job. -func AddNewAnalyzeJob(ctx sessionctx.Context, job *statistics.AnalyzeJob) { - if job == nil { - return - } - var instance string - serverInfo, err := infosync.GetServerInfo() - if err != nil { - logutil.BgLogger().Error("failed to get server info", zap.Error(err)) - instance = "unknown" - } else { - instance = net.JoinHostPort(serverInfo.IP, strconv.Itoa(int(serverInfo.Port))) - } - statsHandle := domain.GetDomain(ctx).StatsHandle() - err = statsHandle.InsertAnalyzeJob(job, instance, ctx.GetSessionVars().ConnectionID) - if err != nil { - logutil.BgLogger().Error("failed to insert analyze job", zap.Error(err)) - } -} - -// StartAnalyzeJob marks the state of the analyze job as running and sets the start time. -func StartAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob) { - if job == nil || job.ID == nil { - return - } - job.StartTime = time.Now() - job.Progress.SetLastDumpTime(job.StartTime) - exec := sctx.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - const sql = "UPDATE mysql.analyze_jobs SET start_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %? WHERE id = %?" - _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, job.StartTime.UTC().Format(types.TimeFormat), statistics.AnalyzeRunning, *job.ID) - if err != nil { - logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzePending, statistics.AnalyzeRunning)), zap.Error(err)) - } - failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { - if val.(bool) { - logutil.BgLogger().Info("StartAnalyzeJob", - zap.Time("start_time", job.StartTime), - zap.Uint64("job id", *job.ID), - ) - } - }) -} - -// UpdateAnalyzeJob updates count of the processed rows when increment reaches a threshold. -func UpdateAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, rowCount int64) { - if job == nil || job.ID == nil { - return - } - delta := job.Progress.Update(rowCount) - if delta == 0 { - return - } - exec := sctx.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - const sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %? WHERE id = %?" - _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, delta, *job.ID) - if err != nil { - logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("process %v rows", delta)), zap.Error(err)) - } - failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { - if val.(bool) { - logutil.BgLogger().Info("UpdateAnalyzeJob", - zap.Int64("increase processed_rows", delta), - zap.Uint64("job id", *job.ID), - ) - } - }) -} - -// FinishAnalyzeMergeJob finishes analyze merge job -func FinishAnalyzeMergeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { - if job == nil || job.ID == nil { - return - } - - job.EndTime = time.Now() - var sql string - var args []interface{} - if analyzeErr != nil { - failReason := analyzeErr.Error() - const textMaxLength = 65535 - if len(failReason) > textMaxLength { - failReason = failReason[:textMaxLength] - } - sql = "UPDATE mysql.analyze_jobs SET end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, fail_reason = %?, process_id = NULL WHERE id = %?" - args = []interface{}{job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFailed, failReason, *job.ID} - } else { - sql = "UPDATE mysql.analyze_jobs SET end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, process_id = NULL WHERE id = %?" - args = []interface{}{job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFinished, *job.ID} - } - exec := sctx.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, args...) - if err != nil { - var state string - if analyzeErr != nil { - state = statistics.AnalyzeFailed - } else { - state = statistics.AnalyzeFinished - } - logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzeRunning, state)), zap.Error(err)) - } - failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { - if val.(bool) { - logutil.BgLogger().Info("FinishAnalyzeMergeJob", - zap.Time("end_time", job.EndTime), - zap.Uint64("job id", *job.ID), - ) - } - }) -} - -// FinishAnalyzeJob updates the state of the analyze job to finished/failed according to `meetError` and sets the end time. -func FinishAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { - if job == nil || job.ID == nil { - return - } - job.EndTime = time.Now() - var sql string - var args []interface{} - // process_id is used to see which process is running the analyze job and kill the analyze job. After the analyze job - // is finished(or failed), process_id is useless and we set it to NULL to avoid `kill tidb process_id` wrongly. - if analyzeErr != nil { - failReason := analyzeErr.Error() - const textMaxLength = 65535 - if len(failReason) > textMaxLength { - failReason = failReason[:textMaxLength] - } - sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %?, end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, fail_reason = %?, process_id = NULL WHERE id = %?" - args = []interface{}{job.Progress.GetDeltaCount(), job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFailed, failReason, *job.ID} - } else { - sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %?, end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, process_id = NULL WHERE id = %?" - args = []interface{}{job.Progress.GetDeltaCount(), job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFinished, *job.ID} - } - exec := sctx.(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, args...) - if err != nil { - var state string - if analyzeErr != nil { - state = statistics.AnalyzeFailed - } else { - state = statistics.AnalyzeFinished - } - logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzeRunning, state)), zap.Error(err)) - } - failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { - if val.(bool) { - logutil.BgLogger().Info("FinishAnalyzeJob", - zap.Int64("increase processed_rows", job.Progress.GetDeltaCount()), - zap.Time("end_time", job.EndTime), - zap.Uint64("job id", *job.ID), - zap.Error(analyzeErr), - ) - } - }) -} - -func finishJobWithLog(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { - FinishAnalyzeJob(sctx, job, analyzeErr) - if job != nil { - var state string - if analyzeErr != nil { - state = statistics.AnalyzeFailed - } else { - state = statistics.AnalyzeFinished - } - logutil.BgLogger().Info(fmt.Sprintf("analyze table `%s`.`%s` has %s", job.DBName, job.TableName, state), - zap.String("partition", job.PartitionName), - zap.String("job info", job.JobInfo), - zap.Time("start time", job.StartTime), - zap.Time("end time", job.EndTime), - zap.String("cost", job.EndTime.Sub(job.StartTime).String()), - zap.String("sample rate reason", job.SampleRateReason)) - } -} - -func handleGlobalStats(needGlobalStats bool, globalStatsMap globalStatsMap, results *statistics.AnalyzeResults) { - if results.TableID.IsPartitionTable() && needGlobalStats { - for _, result := range results.Ars { - if result.IsIndex == 0 { - // If it does not belong to the statistics of index, we need to set it to -1 to distinguish. - globalStatsID := globalStatsKey{tableID: results.TableID.TableID, indexID: int64(-1)} - histIDs := make([]int64, 0, len(result.Hist)) - for _, hg := range result.Hist { - // It's normal virtual column, skip. - if hg == nil { - continue - } - histIDs = append(histIDs, hg.ID) - } - globalStatsMap[globalStatsID] = globalStatsInfo{isIndex: result.IsIndex, histIDs: histIDs, statsVersion: results.StatsVer} - } else { - for _, hg := range result.Hist { - globalStatsID := globalStatsKey{tableID: results.TableID.TableID, indexID: hg.ID} - globalStatsMap[globalStatsID] = globalStatsInfo{isIndex: result.IsIndex, histIDs: []int64{hg.ID}, statsVersion: results.StatsVer} - } - } - } - } -} diff --git a/executor/analyze_test.go b/executor/analyze_test.go deleted file mode 100644 index 78c50a18308b4..0000000000000 --- a/executor/analyze_test.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "os" - "strconv" - "strings" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/stretchr/testify/require" -) - -func checkHistogram(sc *stmtctx.StatementContext, hg *statistics.Histogram) (bool, error) { - for i := 0; i < len(hg.Buckets); i++ { - lower, upper := hg.GetLower(i), hg.GetUpper(i) - cmp, err := upper.Compare(sc, lower, collate.GetBinaryCollator()) - if cmp < 0 || err != nil { - return false, err - } - if i == 0 { - continue - } - previousUpper := hg.GetUpper(i - 1) - cmp, err = lower.Compare(sc, previousUpper, collate.GetBinaryCollator()) - if cmp <= 0 || err != nil { - return false, err - } - } - return true, nil -} - -func TestAnalyzeIndexExtractTopN(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - var dom *domain.Domain - session.DisableStats4Test() - session.SetSchemaLease(0) - dom, err = session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create database test_index_extract_topn") - tk.MustExec("use test_index_extract_topn") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx(a, b))") - tk.MustExec("insert into t values(1, 1), (1, 1), (1, 2), (1, 2)") - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("analyze table t") - - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - table, err := is.TableByName(model.NewCIStr("test_index_extract_topn"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - tbl := dom.StatsHandle().GetTableStats(tableInfo) - - // Construct TopN, should be (1, 1) -> 2 and (1, 2) -> 2 - topn := statistics.NewTopN(2) - { - key1, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, types.NewIntDatum(1), types.NewIntDatum(1)) - require.NoError(t, err) - topn.AppendTopN(key1, 2) - key2, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, types.NewIntDatum(1), types.NewIntDatum(2)) - require.NoError(t, err) - topn.AppendTopN(key2, 2) - } - for _, idx := range tbl.Indices { - ok, err := checkHistogram(tk.Session().GetSessionVars().StmtCtx, &idx.Histogram) - require.NoError(t, err) - require.True(t, ok) - require.True(t, idx.TopN.Equal(topn)) - } -} - -func TestAnalyzePartitionTableForFloat(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk.MustExec("use test") - tk.MustExec("CREATE TABLE t1 ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, num float(9,8) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH (id) PARTITIONS 128;") - // To reproduce the error we meet in https://github.com/pingcap/tidb/issues/35910, we should use the data provided in this issue - b, err := os.ReadFile("testdata/analyze_test_data.sql") - require.NoError(t, err) - sqls := strings.Split(string(b), ";") - for _, sql := range sqls { - if len(sql) < 1 { - continue - } - tk.MustExec(sql) - } - tk.MustExec("analyze table t1") -} - -func TestAnalyzePartitionTableByConcurrencyInDynamic(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk.MustExec("use test") - tk.MustExec("create table t(id int) partition by hash(id) partitions 4") - testcases := []struct { - concurrency string - }{ - { - concurrency: "1", - }, - { - concurrency: "2", - }, - { - concurrency: "3", - }, - { - concurrency: "4", - }, - { - concurrency: "5", - }, - } - // assert empty table - for _, tc := range testcases { - concurrency := tc.concurrency - fmt.Println("testcase ", concurrency) - tk.MustExec(fmt.Sprintf("set @@global.tidb_merge_partition_stats_concurrency=%v", concurrency)) - tk.MustQuery("select @@global.tidb_merge_partition_stats_concurrency").Check(testkit.Rows(concurrency)) - tk.MustExec(fmt.Sprintf("set @@tidb_analyze_partition_concurrency=%v", concurrency)) - tk.MustQuery("select @@tidb_analyze_partition_concurrency").Check(testkit.Rows(concurrency)) - - tk.MustExec("analyze table t") - tk.MustQuery("show stats_topn where partition_name = 'global' and table_name = 't'") - } - - for i := 1; i <= 500; i++ { - for j := 1; j <= 20; j++ { - tk.MustExec(fmt.Sprintf("insert into t (id) values (%v)", j)) - } - } - var expected [][]interface{} - for i := 1; i <= 20; i++ { - expected = append(expected, []interface{}{ - strconv.FormatInt(int64(i), 10), "500", - }) - } - testcases = []struct { - concurrency string - }{ - { - concurrency: "1", - }, - { - concurrency: "2", - }, - { - concurrency: "3", - }, - { - concurrency: "4", - }, - { - concurrency: "5", - }, - } - for _, tc := range testcases { - concurrency := tc.concurrency - fmt.Println("testcase ", concurrency) - tk.MustExec(fmt.Sprintf("set @@tidb_merge_partition_stats_concurrency=%v", concurrency)) - tk.MustQuery("select @@tidb_merge_partition_stats_concurrency").Check(testkit.Rows(concurrency)) - tk.MustExec("analyze table t") - tk.MustQuery("show stats_topn where partition_name = 'global' and table_name = 't'").CheckAt([]int{5, 6}, expected) - } -} - -func TestMergeGlobalStatsWithUnAnalyzedPartition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_partition_prune_mode=dynamic;") - tk.MustExec("CREATE TABLE `t` ( `id` int(11) DEFAULT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL ) PARTITION BY RANGE (`id`) (PARTITION `p0` VALUES LESS THAN (3), PARTITION `p1` VALUES LESS THAN (7), PARTITION `p2` VALUES LESS THAN (11));") - tk.MustExec("insert into t values (1,1,1,1),(2,2,2,2),(4,4,4,4),(5,5,5,5),(6,6,6,6),(8,8,8,8),(9,9,9,9);") - tk.MustExec("create index idxa on t (a);") - tk.MustExec("create index idxb on t (b);") - tk.MustExec("create index idxc on t (c);") - tk.MustExec("analyze table t partition p0 index idxa;") - tk.MustExec("analyze table t partition p1 index idxb;") - tk.MustExec("analyze table t partition p2 index idxc;") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1105 The version 2 would collect all statistics not only the selected indexes", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p2, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"")) - tk.MustExec("analyze table t partition p0;") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/2) as the sample-rate=1\"")) -} - -func TestSetFastAnalyzeSystemVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@session.tidb_enable_fast_analyze=1") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1105 the fast analyze feature has already been removed in TiDB v7.5.0, so this will have no effect")) -} - -func TestIncrementalAnalyze(t *testing.T) { - msg := "the incremental analyze feature has already been removed in TiDB v7.5.0, so this will have no effect" - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, primary key(a), index idx(b))") - tk.MustMatchErrMsg("analyze incremental table t index", msg) - // Create a partition table. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, primary key(a), index idx(b)) partition by range(a) (partition p0 values less than (10), partition p1 values less than (20))") - tk.MustMatchErrMsg("analyze incremental table t partition p0 index idx", msg) -} diff --git a/executor/asyncloaddata/BUILD.bazel b/executor/asyncloaddata/BUILD.bazel deleted file mode 100644 index e536255e97bc8..0000000000000 --- a/executor/asyncloaddata/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "asyncloaddata", - srcs = [ - "progress.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/executor/asyncloaddata", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/terror", - "//types", - "//util/chunk", - "//util/dbterror/exeerrors", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//util", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "asyncloaddata_test", - timeout = "short", - srcs = [ - "main_test.go", - "progress_test.go", - "util_test.go", - ], - embed = [":asyncloaddata"], - flaky = True, - race = "on", - shard_count = 6, - deps = [ - "//testkit", - "//util/sqlexec", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/asyncloaddata/util.go b/executor/asyncloaddata/util.go deleted file mode 100644 index f7fd903357b24..0000000000000 --- a/executor/asyncloaddata/util.go +++ /dev/null @@ -1,595 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package asyncloaddata - -import ( - "context" - "fmt" - "net/url" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -// vars used for test. -var ( - // TestSyncCh is used in unit test to synchronize the execution of LOAD DATA. - TestSyncCh = make(chan struct{}) - // TestLastLoadDataJobID last created job id, used in unit test. - TestLastLoadDataJobID atomic.Int64 -) - -// Job import job. -type Job struct { - ID int64 - // Job don't manage the life cycle of the connection. - Conn sqlexec.SQLExecutor - User string -} - -// NewJob returns new Job. -func NewJob(id int64, conn sqlexec.SQLExecutor, user string) *Job { - return &Job{ID: id, Conn: conn, User: user} -} - -// CreateLoadDataJob creates a load data job by insert a record to system table. -// The AUTO_INCREMENT value will be returned as jobID. -func CreateLoadDataJob( - ctx context.Context, - conn sqlexec.SQLExecutor, - dataSource, db, table string, - importMode string, - user string, -) (*Job, error) { - // remove the params in data source URI because it may contains AK/SK - u, err := url.Parse(dataSource) - if err == nil && u.Scheme != "" { - u.RawQuery = "" - u.Fragment = "" - dataSource = u.String() - } - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err = conn.ExecuteInternal(ctx, - `INSERT INTO mysql.load_data_jobs - (data_source, table_schema, table_name, import_mode, create_user) - VALUES (%?, %?, %?, %?, %?);`, - dataSource, db, table, importMode, user) - if err != nil { - return nil, err - } - rs, err := conn.ExecuteInternal(ctx, `SELECT LAST_INSERT_ID();`) - if err != nil { - return nil, err - } - //nolint: errcheck - defer rs.Close() - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return nil, err - } - if len(rows) != 1 { - return nil, errors.Errorf("unexpected result length: %d", len(rows)) - } - - failpoint.Inject("SaveLastLoadDataJobID", func() { - TestLastLoadDataJobID.Store(rows[0].GetInt64(0)) - }) - return NewJob(rows[0].GetInt64(0), conn, user), nil -} - -// StartJob tries to start a not-yet-started job with jobID. It will not return -// error when there's no matched job. -func (j *Job) StartJob(ctx context.Context) error { - failpoint.Inject("AfterCreateLoadDataJob", nil) - failpoint.Inject("SyncAfterCreateLoadDataJob", func() { - TestSyncCh <- struct{}{} - <-TestSyncCh - }) - - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err := j.Conn.ExecuteInternal(ctx, - `UPDATE mysql.load_data_jobs - SET start_time = CURRENT_TIMESTAMP(6), update_time = CURRENT_TIMESTAMP(6) - WHERE job_id = %? AND start_time IS NULL AND end_time IS NULL;`, - j.ID) - if err != nil { - return err - } - - failpoint.Inject("AfterStartJob", nil) - failpoint.Inject("SyncAfterStartJob", func() { - TestSyncCh <- struct{}{} - <-TestSyncCh - }) - return nil -} - -var ( - // HeartBeatInSec is the interval of heartbeat. - HeartBeatInSec = 5 - // OfflineThresholdInSec means after failing to update heartbeat for 3 times, - // we treat the worker of the job as offline. - OfflineThresholdInSec = HeartBeatInSec * 3 -) - -// UpdateJobProgress updates the progress of a load data job. It should be called -// periodically as heartbeat after StartJob. -// The returned bool indicates whether the keepalive is succeeded. If not, the -// caller should call FailJob soon. -// TODO: Currently if the node is crashed after CreateLoadDataJob and before StartJob, -// it will always be in the status of pending. Maybe we should unify CreateLoadDataJob -// and StartJob. -func (j *Job) UpdateJobProgress(ctx context.Context, progress string) (bool, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - // let TiDB handle heartbeat check for concurrent SQL - // we tolerate 2 times of failure/timeout when updating heartbeat - _, err := j.Conn.ExecuteInternal(ctx, - `UPDATE mysql.load_data_jobs - SET progress = %?, update_time = CURRENT_TIMESTAMP(6) - WHERE job_id = %? - AND end_time IS NULL - AND (update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) - OR update_time IS NULL);`, - progress, j.ID, OfflineThresholdInSec) - if err != nil { - return false, err - } - return j.Conn.GetSessionVars().StmtCtx.AffectedRows() == 1, nil -} - -// FinishJob finishes a load data job. A job can only be finished once. -func (j *Job) FinishJob(ctx context.Context, result string) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err := j.Conn.ExecuteInternal(ctx, - `UPDATE mysql.load_data_jobs - SET end_time = CURRENT_TIMESTAMP(6), result_message = %? - WHERE job_id = %? AND result_message IS NULL AND error_message IS NULL;`, - result, j.ID) - return err -} - -// FailJob fails a load data job. A job can only be failed once. -func (j *Job) FailJob(ctx context.Context, result string) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err := j.Conn.ExecuteInternal(ctx, - `UPDATE mysql.load_data_jobs - SET end_time = CURRENT_TIMESTAMP(6), error_message = %? - WHERE job_id = %? AND result_message IS NULL AND error_message IS NULL;`, - result, j.ID) - return err -} - -// CancelJob cancels a load data job. Only a running/paused job can be canceled. -func (j *Job) CancelJob(ctx context.Context) (err error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err = j.Conn.ExecuteInternal(ctx, "BEGIN PESSIMISTIC;") - if err != nil { - return err - } - defer func() { - if err != nil { - _, err1 := j.Conn.ExecuteInternal(ctx, "ROLLBACK;") - terror.Log(err1) - return - } - - _, err = j.Conn.ExecuteInternal(ctx, "COMMIT;") - if err != nil { - return - } - }() - - var ( - rs sqlexec.RecordSet - rows []chunk.Row - ) - rs, err = j.Conn.ExecuteInternal(ctx, - `SELECT expected_status, end_time, error_message FROM mysql.load_data_jobs - WHERE job_id = %? AND create_user = %?;`, - j.ID, j.User) - if err != nil { - return err - } - defer terror.Call(rs.Close) - rows, err = sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return err - } - - if len(rows) < 1 { - return exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) - } - status := rows[0].GetEnum(0).String() - if status != "running" && status != "paused" { - return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs(fmt.Sprintf("need status running or paused, but got %s", status)) - } - endTimeIsNull := rows[0].IsNull(1) - if !endTimeIsNull { - hasError := !rows[0].IsNull(2) - if hasError { - return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs("need status running or paused, but got failed") - } - return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs("need status running or paused, but got finished") - } - - _, err = j.Conn.ExecuteInternal(ctx, - `UPDATE mysql.load_data_jobs - SET expected_status = 'canceled', - end_time = CURRENT_TIMESTAMP(6), - error_message = 'canceled by user' - WHERE job_id = %?;`, - j.ID) - return err -} - -// DropJob drops a load data job. -func (j *Job) DropJob(ctx context.Context) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - _, err := j.Conn.ExecuteInternal(ctx, - `DELETE FROM mysql.load_data_jobs - WHERE job_id = %? AND create_user = %?;`, - j.ID, j.User) - if err == nil { - return err - } - if j.Conn.GetSessionVars().StmtCtx.AffectedRows() < 1 { - return exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) - } - return nil -} - -// OnComplete is called when a job is finished or failed. -func (j *Job) OnComplete(inErr error, msg string) { - // write the ending status even if user context is canceled. - ctx2 := context.Background() - ctx2 = kv.WithInternalSourceType(ctx2, kv.InternalLoadData) - if inErr == nil { - err2 := j.FinishJob(ctx2, msg) - terror.Log(err2) - return - } - errMsg := inErr.Error() - if errImpl, ok := errors.Cause(inErr).(*errors.Error); ok { - b, marshalErr := errImpl.MarshalJSON() - if marshalErr == nil { - errMsg = string(b) - } - } - - err2 := j.FailJob(ctx2, errMsg) - terror.Log(err2) -} - -// ProgressUpdateRoutineFn job progress update routine. -func (j *Job) ProgressUpdateRoutineFn(ctx context.Context, finishCh chan struct{}, errCh <-chan struct{}, progress *Progress) error { - ticker := time.NewTicker(time.Duration(HeartBeatInSec) * time.Second) - defer ticker.Stop() - - for { - select { - case <-finishCh: - // When done, try to update progress to reach 100% - ok, err2 := j.UpdateJobProgress(ctx, progress.String()) - if !ok || err2 != nil { - logutil.Logger(ctx).Warn("failed to update job progress when finished", - zap.Bool("ok", ok), zap.Error(err2)) - } - return nil - case <-errCh: - return nil - case <-ticker.C: - ok, err2 := j.UpdateJobProgress(ctx, progress.String()) - if err2 != nil { - return err2 - } - if !ok { - return errors.Errorf("failed to update job progress, the job %d is interrupted by user or failed to keepalive", j.ID) - } - } - } -} - -// JobExpectedStatus is the expected status of a load data job. User can set the -// expected status of a job and worker will respect it. -type JobExpectedStatus int - -const ( - // JobExpectedRunning means the job is expected to be running. - JobExpectedRunning JobExpectedStatus = iota - // JobExpectedPaused means the job is expected to be paused. - JobExpectedPaused - // JobExpectedCanceled means the job is expected to be canceled. - JobExpectedCanceled -) - -// UpdateJobExpectedStatus updates the expected status of a load data job. -// TODO: remove it? -func UpdateJobExpectedStatus( - ctx context.Context, - conn sqlexec.SQLExecutor, - jobID int64, - status JobExpectedStatus, -) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - var sql string - switch status { - case JobExpectedRunning: - sql = `UPDATE mysql.load_data_jobs - SET expected_status = 'running' - WHERE job_id = %? AND expected_status = 'paused';` - case JobExpectedPaused: - sql = `UPDATE mysql.load_data_jobs - SET expected_status = 'paused' - WHERE job_id = %? AND expected_status = 'running';` - case JobExpectedCanceled: - sql = `UPDATE mysql.load_data_jobs - SET expected_status = 'canceled' - WHERE job_id = %? AND expected_status != 'canceled';` - } - _, err := conn.ExecuteInternal(ctx, sql, jobID) - return err -} - -// JobStatus represents the status of a load data job. -type JobStatus int - -const ( - // JobFailed means the job is failed and can't be resumed. - JobFailed JobStatus = iota - // JobCanceled means the job is canceled by user and can't be resumed. It - // will finally convert to JobFailed with a message indicating the reason - // is canceled. - JobCanceled - // JobPaused means the job is paused by user and can be resumed. - JobPaused - // JobFinished means the job is finished. - JobFinished - // JobPending means the job is pending to be started. - JobPending - // JobRunning means the job is running. - JobRunning -) - -func (s JobStatus) String() string { - switch s { - case JobFailed: - return "failed" - case JobCanceled: - return "canceled" - case JobPaused: - return "paused" - case JobFinished: - return "finished" - case JobPending: - return "pending" - case JobRunning: - return "running" - default: - return "unknown JobStatus" - } -} - -// GetJobStatus gets the status of a load data job. The returned error means -// something wrong when querying the database. Other business logic errors are -// returned as JobFailed with message. -func (j *Job) GetJobStatus(ctx context.Context) (JobStatus, string, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - rs, err := j.Conn.ExecuteInternal(ctx, - `SELECT - expected_status, - update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, - end_time, - result_message, - error_message, - start_time - FROM mysql.load_data_jobs - WHERE job_id = %?;`, - OfflineThresholdInSec, j.ID) - if err != nil { - return JobFailed, "", err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return JobFailed, "", err - } - if len(rows) != 1 { - return JobFailed, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID).Error(), nil - } - - return getJobStatus(rows[0]) -} - -// getJobStatus expected the first 6 columns of input row is (expected_status, -// is_alive (derived from update_time), end_time, result_message, error_message, -// start_time). -func getJobStatus(row chunk.Row) (JobStatus, string, error) { - // ending status has the highest priority - expectedStatus := row.GetEnum(0).String() - endTimeIsNull := row.IsNull(2) - if !endTimeIsNull { - resultMsgIsNull := row.IsNull(3) - if !resultMsgIsNull { - resultMessage := row.GetString(3) - return JobFinished, resultMessage, nil - } - - errorMessage := row.GetString(4) - if expectedStatus == "canceled" { - return JobCanceled, errorMessage, nil - } - return JobFailed, errorMessage, nil - } - - isAlive := row.GetInt64(1) == 1 - startTimeIsNull := row.IsNull(5) - - switch expectedStatus { - case "canceled": - return JobCanceled, "", nil - case "paused": - if startTimeIsNull || isAlive { - return JobPaused, "", nil - } - return JobFailed, "job expected paused but the node is timeout", nil - case "running": - if startTimeIsNull { - return JobPending, "", nil - } - if isAlive { - return JobRunning, "", nil - } - return JobFailed, "job expected running but the node is timeout", nil - default: - return JobFailed, fmt.Sprintf("unexpected job status %s", expectedStatus), nil - } -} - -// JobInfo is the information of a load data job. -type JobInfo struct { - JobID int64 - User string - DataSource string - TableSchema string - TableName string - ImportMode string - Progress string - Status JobStatus - StatusMessage string - CreateTime types.Time - StartTime types.Time - EndTime types.Time -} - -// GetJobInfo gets all needed information of a load data job. -func (j *Job) GetJobInfo(ctx context.Context) (*JobInfo, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - rs, err := j.Conn.ExecuteInternal(ctx, - `SELECT - expected_status, - update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, - end_time, - result_message, - error_message, - start_time, - - job_id, - data_source, - table_schema, - table_name, - import_mode, - progress, - create_user, - create_time - FROM mysql.load_data_jobs - WHERE job_id = %? AND create_user = %?;`, - OfflineThresholdInSec, j.ID, j.User) - if err != nil { - return nil, err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return nil, err - } - if len(rows) != 1 { - return nil, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) - } - - return getJobInfo(rows[0]) -} - -// getJobInfo expected the columns of input row is (expected_status, -// is_alive (derived from update_time), end_time, result_message, error_message, -// start_time, job_id, data_source, table_schema, table_name, import_mode, -// progress, create_user). -func getJobInfo(row chunk.Row) (*JobInfo, error) { - var err error - jobInfo := JobInfo{ - JobID: row.GetInt64(6), - DataSource: row.GetString(7), - TableSchema: row.GetString(8), - TableName: row.GetString(9), - ImportMode: row.GetString(10), - Progress: row.GetString(11), - User: row.GetString(12), - CreateTime: row.GetTime(13), - StartTime: row.GetTime(5), - EndTime: row.GetTime(2), - } - jobInfo.Status, jobInfo.StatusMessage, err = getJobStatus(row) - if err != nil { - return nil, err - } - return &jobInfo, nil -} - -// GetAllJobInfo gets all jobs status of a user. -func GetAllJobInfo( - ctx context.Context, - conn sqlexec.SQLExecutor, - user string, -) ([]*JobInfo, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) - rs, err := conn.ExecuteInternal(ctx, - `SELECT - expected_status, - update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, - end_time, - result_message, - error_message, - start_time, - - job_id, - data_source, - table_schema, - table_name, - import_mode, - progress, - create_user, - create_time - FROM mysql.load_data_jobs - WHERE create_user = %?;`, - OfflineThresholdInSec, user) - if err != nil { - return nil, err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return nil, err - } - ret := make([]*JobInfo, 0, len(rows)) - for _, row := range rows { - jobInfo, err := getJobInfo(row) - if err != nil { - return nil, err - } - ret = append(ret, jobInfo) - } - - return ret, nil -} diff --git a/executor/asyncloaddata/util_test.go b/executor/asyncloaddata/util_test.go deleted file mode 100644 index c58519adc222e..0000000000000 --- a/executor/asyncloaddata/util_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package asyncloaddata_test - -import ( - "context" - "testing" - "time" - - . "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" -) - -func checkEqualIgnoreTimes(t *testing.T, expected, got *JobInfo) { - cloned := *expected - cloned.CreateTime = got.CreateTime - cloned.StartTime = got.StartTime - cloned.EndTime = got.EndTime - require.Equal(t, &cloned, got) -} - -func createJob(t *testing.T, conn sqlexec.SQLExecutor, user string) (*Job, *JobInfo) { - job, err := CreateLoadDataJob(context.Background(), conn, "/tmp/test.csv", "test", "t", "logical", user) - require.NoError(t, err) - info, err := job.GetJobInfo(context.Background()) - require.NoError(t, err) - expected := &JobInfo{ - JobID: job.ID, - User: user, - DataSource: "/tmp/test.csv", - TableSchema: "test", - TableName: "t", - ImportMode: "logical", - Progress: "", - Status: JobPending, - StatusMessage: "", - } - checkEqualIgnoreTimes(t, expected, info) - return job, info -} - -func TestHappyPath(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - ctx := context.Background() - - // job is created - - job, expected := createJob(t, tk.Session(), "user") - - // job is started by a worker - - backup := OfflineThresholdInSec - OfflineThresholdInSec = 1000 - t.Cleanup(func() { - OfflineThresholdInSec = backup - }) - err := job.StartJob(ctx) - require.NoError(t, err) - info, err := job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobRunning - checkEqualIgnoreTimes(t, expected, info) - - // job is periodically updated by worker - - ok, err := job.UpdateJobProgress(ctx, "imported 10%") - require.NoError(t, err) - require.True(t, ok) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Progress = "imported 10%" - checkEqualIgnoreTimes(t, expected, info) - - // job is paused - - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobPaused - checkEqualIgnoreTimes(t, expected, info) - - // worker still can update progress, maybe response to pausing is delayed - - ok, err = job.UpdateJobProgress(ctx, "imported 20%") - require.NoError(t, err) - require.True(t, ok) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Progress = "imported 20%" - checkEqualIgnoreTimes(t, expected, info) - - // job is resumed - - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobRunning - checkEqualIgnoreTimes(t, expected, info) - - // job is finished - - err = job.FinishJob(ctx, "finished message") - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobFinished - expected.StatusMessage = "finished message" - checkEqualIgnoreTimes(t, expected, info) -} - -func TestKeepAlive(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - ctx := context.Background() - - // job is created - - job, expected := createJob(t, tk.Session(), "user") - - backup := OfflineThresholdInSec - OfflineThresholdInSec = 1 - t.Cleanup(func() { - OfflineThresholdInSec = backup - }) - - // before job is started, worker don't need to keepalive - // TODO:👆not correct! - - time.Sleep(2 * time.Second) - info, err := job.GetJobInfo(ctx) - require.NoError(t, err) - checkEqualIgnoreTimes(t, expected, info) - - err = job.StartJob(ctx) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobRunning - checkEqualIgnoreTimes(t, expected, info) - - // if worker failed to keepalive, job will fail - - time.Sleep(2 * time.Second) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobFailed - expected.StatusMessage = "job expected running but the node is timeout" - checkEqualIgnoreTimes(t, expected, info) - - // after the worker is failed to keepalive, further keepalive will fail - - ok, err := job.UpdateJobProgress(ctx, "imported 20%") - require.NoError(t, err) - require.False(t, ok) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - checkEqualIgnoreTimes(t, expected, info) - - // when worker fails to keepalive, before it calls FailJob, it still can - // change expected status to some extent. - - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.StatusMessage = "job expected paused but the node is timeout" - checkEqualIgnoreTimes(t, expected, info) - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.StatusMessage = "job expected running but the node is timeout" - checkEqualIgnoreTimes(t, expected, info) - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedCanceled) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobCanceled - expected.StatusMessage = "" - checkEqualIgnoreTimes(t, expected, info) - - // Now the worker calls FailJob, but the status should still be canceled, - // that's more friendly. - - err = job.FailJob(ctx, "failed to keepalive") - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobCanceled - expected.StatusMessage = "failed to keepalive" - checkEqualIgnoreTimes(t, expected, info) -} - -func TestJobIsFailedAndGetAllJobs(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - ctx := context.Background() - - // job is created - - job, expected := createJob(t, tk.Session(), "user") - - // job can be failed directly when it's pending - - err := job.FailJob(ctx, "failed message") - require.NoError(t, err) - info, err := job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobFailed - expected.StatusMessage = "failed message" - checkEqualIgnoreTimes(t, expected, info) - - // create another job and fail it - - job, expected = createJob(t, tk.Session(), "user") - - err = job.StartJob(ctx) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobRunning - checkEqualIgnoreTimes(t, expected, info) - - err = job.FailJob(ctx, "failed message") - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - expected.Status = JobFailed - expected.StatusMessage = "failed message" - checkEqualIgnoreTimes(t, expected, info) - - // test change expected status of a failed job. - - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - checkEqualIgnoreTimes(t, expected, info) - err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) - require.NoError(t, err) - info, err = job.GetJobInfo(ctx) - require.NoError(t, err) - checkEqualIgnoreTimes(t, expected, info) - err = job.CancelJob(ctx) - require.ErrorContains(t, err, "The current job status cannot perform the operation. need status running or paused, but got failed") - - // add job of another user and test GetAllJobInfo - - job, _ = createJob(t, tk.Session(), "user2") - - jobs, err := GetAllJobInfo(ctx, tk.Session(), "user") - require.NoError(t, err) - require.Equal(t, 2, len(jobs)) - require.Equal(t, JobFailed, jobs[0].Status) - require.Equal(t, JobFailed, jobs[1].Status) - - jobs, err = GetAllJobInfo(ctx, tk.Session(), "user2") - require.NoError(t, err) - require.Equal(t, 1, len(jobs)) - require.Equal(t, JobPending, jobs[0].Status) - require.Equal(t, job.ID, jobs[0].JobID) - - job.User = "wrong_user" - _, err = job.GetJobInfo(ctx) - require.ErrorContains(t, err, "doesn't exist") -} - -func TestGetJobStatus(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - ctx := context.Background() - - // job is created - - job, _ := createJob(t, tk.Session(), "user") - - // job is pending - - status, msg, err := job.GetJobStatus(ctx) - require.NoError(t, err) - require.Equal(t, JobPending, status) - require.Equal(t, "", msg) - - // job is running - - backup := OfflineThresholdInSec - OfflineThresholdInSec = 1000 - t.Cleanup(func() { - OfflineThresholdInSec = backup - }) - err = job.StartJob(ctx) - require.NoError(t, err) - status, msg, err = job.GetJobStatus(ctx) - require.NoError(t, err) - require.Equal(t, JobRunning, status) - require.Equal(t, "", msg) - - // job is finished - - err = job.FinishJob(ctx, "finished message") - require.NoError(t, err) - status, msg, err = job.GetJobStatus(ctx) - require.NoError(t, err) - require.Equal(t, JobFinished, status) - require.Equal(t, "finished message", msg) - - // wrong ID - - job.ID += 1 - status, msg, err = job.GetJobStatus(ctx) - require.NoError(t, err) - require.Equal(t, JobFailed, status) - require.Contains(t, msg, "doesn't exist") -} - -func TestCreateLoadDataJobRedact(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - ctx := context.Background() - - _, err := CreateLoadDataJob(ctx, tk.Session(), - "s3://bucket/a.csv?access-key=hideme&secret-access-key=hideme", - "db", "table", "mode", "user") - require.NoError(t, err) - result := tk.MustQuery("SELECT * FROM mysql.load_data_jobs;") - result.CheckContain("a.csv") - result.CheckNotContain("hideme") -} diff --git a/executor/benchmark_test.go b/executor/benchmark_test.go deleted file mode 100644 index dacc4bd243fe8..0000000000000 --- a/executor/benchmark_test.go +++ /dev/null @@ -1,2188 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "encoding/base64" - "fmt" - "math/rand" - "os" - "sort" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/log" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/stringutil" - "go.uber.org/zap/zapcore" -) - -var ( - _ exec.Executor = &mockDataSource{} - _ core.PhysicalPlan = &mockDataPhysicalPlan{} - wideString = strings.Repeat("x", 5*1024) -) - -type mockDataSourceParameters struct { - schema *expression.Schema - genDataFunc func(row int, typ *types.FieldType) interface{} - ndvs []int // number of distinct values on columns[i] and zero represents no limit - orders []bool // columns[i] should be ordered if orders[i] is true - rows int // number of rows the DataSource should output - ctx sessionctx.Context -} - -type mockDataSource struct { - exec.BaseExecutor - p mockDataSourceParameters - genData []*chunk.Chunk - chunks []*chunk.Chunk - chunkPtr int -} - -type mockDataPhysicalPlan struct { - MockPhysicalPlan - schema *expression.Schema - exec exec.Executor -} - -func (mp *mockDataPhysicalPlan) GetExecutor() exec.Executor { - return mp.exec -} - -func (mp *mockDataPhysicalPlan) Schema() *expression.Schema { - return mp.schema -} - -func (mp *mockDataPhysicalPlan) ExplainID() fmt.Stringer { - return stringutil.MemoizeStr(func() string { - return "mockData_0" - }) -} - -func (mp *mockDataPhysicalPlan) ID() int { - return 0 -} - -func (mp *mockDataPhysicalPlan) Stats() *property.StatsInfo { - return nil -} - -func (mp *mockDataPhysicalPlan) SelectBlockOffset() int { - return 0 -} - -// MemoryUsage of mockDataPhysicalPlan is only for testing -func (mp *mockDataPhysicalPlan) MemoryUsage() (sum int64) { - return -} - -func buildMockDataPhysicalPlan(ctx sessionctx.Context, srcExec exec.Executor) *mockDataPhysicalPlan { - return &mockDataPhysicalPlan{ - schema: srcExec.Schema(), - exec: srcExec, - } -} - -func (mds *mockDataSource) genColDatums(col int) (results []interface{}) { - typ := mds.RetFieldTypes()[col] - order := false - if col < len(mds.p.orders) { - order = mds.p.orders[col] - } - rows := mds.p.rows - NDV := 0 - if col < len(mds.p.ndvs) { - NDV = mds.p.ndvs[col] - } - results = make([]interface{}, 0, rows) - if NDV == 0 { - if mds.p.genDataFunc == nil { - for i := 0; i < rows; i++ { - results = append(results, mds.randDatum(typ)) - } - } else { - for i := 0; i < rows; i++ { - results = append(results, mds.p.genDataFunc(i, typ)) - } - } - } else { - datumSet := make(map[string]bool, NDV) - datums := make([]interface{}, 0, NDV) - for len(datums) < NDV { - d := mds.randDatum(typ) - str := fmt.Sprintf("%v", d) - if datumSet[str] { - continue - } - datumSet[str] = true - datums = append(datums, d) - } - - for i := 0; i < rows; i++ { - results = append(results, datums[rand.Intn(NDV)]) - } - } - - if order { - sort.Slice(results, func(i, j int) bool { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return results[i].(int64) < results[j].(int64) - case mysql.TypeDouble: - return results[i].(float64) < results[j].(float64) - case mysql.TypeVarString: - return results[i].(string) < results[j].(string) - default: - panic("not implement") - } - }) - } - - return -} - -func (mds *mockDataSource) randDatum(typ *types.FieldType) interface{} { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(rand.Int()) - case mysql.TypeFloat: - return rand.Float32() - case mysql.TypeDouble: - return rand.Float64() - case mysql.TypeNewDecimal: - var d types.MyDecimal - return d.FromInt(int64(rand.Int())) - case mysql.TypeVarString: - buff := make([]byte, 10) - rand.Read(buff) - return base64.RawURLEncoding.EncodeToString(buff) - default: - panic("not implement") - } -} - -func (mds *mockDataSource) prepareChunks() { - mds.chunks = make([]*chunk.Chunk, len(mds.genData)) - for i := range mds.chunks { - mds.chunks[i] = mds.genData[i].CopyConstruct() - } - mds.chunkPtr = 0 -} - -func (mds *mockDataSource) Next(ctx context.Context, req *chunk.Chunk) error { - if mds.chunkPtr >= len(mds.chunks) { - req.Reset() - return nil - } - dataChk := mds.chunks[mds.chunkPtr] - dataChk.SwapColumns(req) - mds.chunkPtr++ - return nil -} - -func buildMockDataSource(opt mockDataSourceParameters) *mockDataSource { - baseExec := exec.NewBaseExecutor(opt.ctx, opt.schema, 0) - m := &mockDataSource{baseExec, opt, nil, nil, 0} - rTypes := exec.RetTypes(m) - colData := make([][]interface{}, len(rTypes)) - for i := 0; i < len(rTypes); i++ { - colData[i] = m.genColDatums(i) - } - - m.genData = make([]*chunk.Chunk, (m.p.rows+m.MaxChunkSize()-1)/m.MaxChunkSize()) - for i := range m.genData { - m.genData[i] = chunk.NewChunkWithCapacity(exec.RetTypes(m), m.MaxChunkSize()) - } - - for i := 0; i < m.p.rows; i++ { - idx := i / m.MaxChunkSize() - retTypes := exec.RetTypes(m) - for colIdx := 0; colIdx < len(rTypes); colIdx++ { - switch retTypes[colIdx].GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - m.genData[idx].AppendInt64(colIdx, colData[colIdx][i].(int64)) - case mysql.TypeFloat: - m.genData[idx].AppendFloat32(colIdx, colData[colIdx][i].(float32)) - case mysql.TypeDouble: - m.genData[idx].AppendFloat64(colIdx, colData[colIdx][i].(float64)) - case mysql.TypeNewDecimal: - m.genData[idx].AppendMyDecimal(colIdx, colData[colIdx][i].(*types.MyDecimal)) - case mysql.TypeVarString: - m.genData[idx].AppendString(colIdx, colData[colIdx][i].(string)) - default: - panic("not implement") - } - } - } - return m -} - -func buildMockDataSourceWithIndex(opt mockDataSourceParameters, index []int) *mockDataSource { - opt.orders = make([]bool, len(opt.schema.Columns)) - for _, idx := range index { - opt.orders[idx] = true - } - return buildMockDataSource(opt) -} - -// aggTestCase has a fixed schema (aggCol Double, groupBy LongLong). -type aggTestCase struct { - execType string // "hash" or "stream" - aggFunc string // sum, avg, count .... - groupByNDV int // the number of distinct group-by keys - hasDistinct bool - rows int - concurrency int - dataSourceSorted bool - ctx sessionctx.Context -} - -func (a aggTestCase) columns() []*expression.Column { - return []*expression.Column{ - {Index: 0, RetType: types.NewFieldType(mysql.TypeDouble)}, - {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, - } -} - -func (a aggTestCase) String() string { - return fmt.Sprintf("(execType:%v, aggFunc:%v, ndv:%v, hasDistinct:%v, rows:%v, concurrency:%v, sorted:%v)", - a.execType, a.aggFunc, a.groupByNDV, a.hasDistinct, a.rows, a.concurrency, a.dataSourceSorted) -} - -func defaultAggTestCase(exec string) *aggTestCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - return &aggTestCase{exec, ast.AggFuncSum, 1000, false, 10000000, 4, true, ctx} -} - -func buildHashAggExecutor(ctx sessionctx.Context, src exec.Executor, schema *expression.Schema, - aggFuncs []*aggregation.AggFuncDesc, groupItems []expression.Expression) exec.Executor { - plan := new(core.PhysicalHashAgg) - plan.AggFuncs = aggFuncs - plan.GroupByItems = groupItems - plan.SetSchema(schema) - plan.Init(ctx, nil, 0) - plan.SetChildren(nil) - b := newExecutorBuilder(ctx, nil, nil) - exec := b.build(plan) - hashAgg := exec.(*aggregate.HashAggExec) - hashAgg.SetChildren(0, src) - return exec -} - -func buildStreamAggExecutor(ctx sessionctx.Context, srcExec exec.Executor, schema *expression.Schema, - aggFuncs []*aggregation.AggFuncDesc, groupItems []expression.Expression, concurrency int, dataSourceSorted bool) exec.Executor { - src := buildMockDataPhysicalPlan(ctx, srcExec) - - sg := new(core.PhysicalStreamAgg) - sg.AggFuncs = aggFuncs - sg.GroupByItems = groupItems - sg.SetSchema(schema) - sg.Init(ctx, nil, 0) - - var tail core.PhysicalPlan = sg - // if data source is not sorted, we have to attach sort, to make the input of stream-agg sorted - if !dataSourceSorted { - byItems := make([]*util.ByItems, 0, len(sg.GroupByItems)) - for _, col := range sg.GroupByItems { - byItems = append(byItems, &util.ByItems{Expr: col, Desc: false}) - } - sortPP := &core.PhysicalSort{ByItems: byItems} - sortPP.SetChildren(src) - sg.SetChildren(sortPP) - tail = sortPP - } else { - sg.SetChildren(src) - } - - var ( - plan core.PhysicalPlan - splitter core.PartitionSplitterType = core.PartitionHashSplitterType - ) - if concurrency > 1 { - if dataSourceSorted { - splitter = core.PartitionRangeSplitterType - } - plan = core.PhysicalShuffle{ - Concurrency: concurrency, - Tails: []core.PhysicalPlan{tail}, - DataSources: []core.PhysicalPlan{src}, - SplitterType: splitter, - ByItemArrays: [][]expression.Expression{sg.GroupByItems}, - }.Init(ctx, nil, 0) - plan.SetChildren(sg) - } else { - plan = sg - } - - b := newExecutorBuilder(ctx, nil, nil) - return b.build(plan) -} - -func buildAggExecutor(b *testing.B, testCase *aggTestCase, child exec.Executor) exec.Executor { - ctx := testCase.ctx - if testCase.execType == "stream" { - if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBStreamAggConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { - b.Fatal(err) - } - } else { - if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBHashAggFinalConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { - b.Fatal(err) - } - if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBHashAggPartialConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { - b.Fatal(err) - } - } - - childCols := testCase.columns() - schema := expression.NewSchema(childCols...) - groupBy := []expression.Expression{childCols[1]} - aggFunc, err := aggregation.NewAggFuncDesc(testCase.ctx, testCase.aggFunc, []expression.Expression{childCols[0]}, testCase.hasDistinct) - if err != nil { - b.Fatal(err) - } - aggFuncs := []*aggregation.AggFuncDesc{aggFunc} - - var aggExec exec.Executor - switch testCase.execType { - case "hash": - aggExec = buildHashAggExecutor(testCase.ctx, child, schema, aggFuncs, groupBy) - case "stream": - aggExec = buildStreamAggExecutor(testCase.ctx, child, schema, aggFuncs, groupBy, testCase.concurrency, testCase.dataSourceSorted) - default: - b.Fatal("not implement") - } - return aggExec -} - -func benchmarkAggExecWithCase(b *testing.B, casTest *aggTestCase) { - if err := casTest.ctx.GetSessionVars().SetSystemVar(variable.TiDBStreamAggConcurrency, fmt.Sprintf("%v", casTest.concurrency)); err != nil { - b.Fatal(err) - } - - cols := casTest.columns() - dataSource := buildMockDataSource(mockDataSourceParameters{ - schema: expression.NewSchema(cols...), - ndvs: []int{0, casTest.groupByNDV}, - orders: []bool{false, casTest.dataSourceSorted}, - rows: casTest.rows, - ctx: casTest.ctx, - }) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() // prepare a new agg-executor - aggExec := buildAggExecutor(b, casTest, dataSource) - tmpCtx := context.Background() - chk := exec.NewFirstChunk(aggExec) - dataSource.prepareChunks() - - b.StartTimer() - if err := aggExec.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := aggExec.Next(tmpCtx, chk); err != nil { - b.Fatal(b) - } - if chk.NumRows() == 0 { - break - } - } - - if err := aggExec.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func BenchmarkShuffleStreamAggRows(b *testing.B) { - b.ReportAllocs() - sortTypes := []bool{false, true} - rows := []int{10000, 100000, 1000000, 10000000} - concurrencies := []int{1, 2, 4, 8} - for _, row := range rows { - for _, con := range concurrencies { - for _, sorted := range sortTypes { - cas := defaultAggTestCase("stream") - cas.rows = row - cas.dataSourceSorted = sorted - cas.concurrency = con - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkAggExecWithCase(b, cas) - }) - } - } - } -} - -func BenchmarkHashAggRows(b *testing.B) { - rows := []int{100000, 1000000, 10000000} - concurrencies := []int{1, 4, 8, 15, 20, 30, 40} - for _, row := range rows { - for _, con := range concurrencies { - cas := defaultAggTestCase("hash") - cas.rows = row - cas.concurrency = con - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkAggExecWithCase(b, cas) - }) - } - } -} - -func BenchmarkAggGroupByNDV(b *testing.B) { - NDVs := []int{10, 100, 1000, 10000, 100000, 1000000, 10000000} - for _, NDV := range NDVs { - for _, exec := range []string{"hash", "stream"} { - cas := defaultAggTestCase(exec) - cas.groupByNDV = NDV - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkAggExecWithCase(b, cas) - }) - } - } -} - -func BenchmarkAggConcurrency(b *testing.B) { - concs := []int{1, 4, 8, 15, 20, 30, 40} - for _, con := range concs { - for _, exec := range []string{"hash", "stream"} { - cas := defaultAggTestCase(exec) - cas.concurrency = con - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkAggExecWithCase(b, cas) - }) - } - } -} - -func BenchmarkAggDistinct(b *testing.B) { - rows := []int{100000, 1000000, 10000000} - distincts := []bool{false, true} - for _, row := range rows { - for _, exec := range []string{"hash", "stream"} { - for _, distinct := range distincts { - cas := defaultAggTestCase(exec) - cas.rows = row - cas.hasDistinct = distinct - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkAggExecWithCase(b, cas) - }) - } - } - } -} - -func buildWindowExecutor(ctx sessionctx.Context, windowFunc string, funcs int, frame *core.WindowFrame, srcExec exec.Executor, schema *expression.Schema, partitionBy []*expression.Column, concurrency int, dataSourceSorted bool) exec.Executor { - src := buildMockDataPhysicalPlan(ctx, srcExec) - win := new(core.PhysicalWindow) - win.WindowFuncDescs = make([]*aggregation.WindowFuncDesc, 0) - winSchema := schema.Clone() - for i := 0; i < funcs; i++ { - var args []expression.Expression - switch windowFunc { - case ast.WindowFuncNtile: - args = append(args, &expression.Constant{Value: types.NewUintDatum(2)}) - case ast.WindowFuncNthValue: - args = append(args, partitionBy[0], &expression.Constant{Value: types.NewUintDatum(2)}) - case ast.AggFuncSum: - args = append(args, src.Schema().Columns[0]) - case ast.AggFuncAvg: - args = append(args, src.Schema().Columns[0]) - case ast.AggFuncBitXor: - args = append(args, src.Schema().Columns[0]) - case ast.AggFuncMax, ast.AggFuncMin: - args = append(args, src.Schema().Columns[0]) - default: - args = append(args, partitionBy[0]) - } - desc, _ := aggregation.NewWindowFuncDesc(ctx, windowFunc, args, false) - - win.WindowFuncDescs = append(win.WindowFuncDescs, desc) - winSchema.Append(&expression.Column{ - UniqueID: 10 + (int64)(i), - RetType: types.NewFieldType(mysql.TypeLonglong), - }) - } - for _, col := range partitionBy { - win.PartitionBy = append(win.PartitionBy, property.SortItem{Col: col}) - } - win.Frame = frame - win.OrderBy = nil - - win.SetSchema(winSchema) - win.Init(ctx, nil, 0) - - var tail core.PhysicalPlan = win - if !dataSourceSorted { - byItems := make([]*util.ByItems, 0, len(partitionBy)) - for _, col := range partitionBy { - byItems = append(byItems, &util.ByItems{Expr: col, Desc: false}) - } - sort := &core.PhysicalSort{ByItems: byItems} - sort.SetChildren(src) - win.SetChildren(sort) - tail = sort - } else { - win.SetChildren(src) - } - - var plan core.PhysicalPlan - if concurrency > 1 { - byItems := make([]expression.Expression, 0, len(win.PartitionBy)) - for _, item := range win.PartitionBy { - byItems = append(byItems, item.Col) - } - - plan = core.PhysicalShuffle{ - Concurrency: concurrency, - Tails: []core.PhysicalPlan{tail}, - DataSources: []core.PhysicalPlan{src}, - SplitterType: core.PartitionHashSplitterType, - ByItemArrays: [][]expression.Expression{byItems}, - }.Init(ctx, nil, 0) - plan.SetChildren(win) - } else { - plan = win - } - - b := newExecutorBuilder(ctx, nil, nil) - exec := b.build(plan) - return exec -} - -// windowTestCase has a fixed schema (col Double, partitionBy LongLong, rawData VarString(16), col LongLong). -type windowTestCase struct { - windowFunc string - numFunc int // The number of windowFuncs. Default: 1. - frame *core.WindowFrame - ndv int // the number of distinct group-by keys - rows int - concurrency int - pipelined int - dataSourceSorted bool - ctx sessionctx.Context - rawDataSmall string - columns []*expression.Column // the columns of mock schema -} - -func (a windowTestCase) String() string { - return fmt.Sprintf("(func:%v, aggColType:%s, numFunc:%v, ndv:%v, rows:%v, sorted:%v, concurrency:%v, pipelined:%v)", - a.windowFunc, a.columns[0].RetType, a.numFunc, a.ndv, a.rows, a.dataSourceSorted, a.concurrency, a.pipelined) -} - -func defaultWindowTestCase() *windowTestCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - return &windowTestCase{ast.WindowFuncRowNumber, 1, nil, 1000, 10000000, 1, 0, true, ctx, strings.Repeat("x", 16), - []*expression.Column{ - {Index: 0, RetType: types.NewFieldType(mysql.TypeDouble)}, - {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, - {Index: 2, RetType: types.NewFieldType(mysql.TypeVarString)}, - {Index: 3, RetType: types.NewFieldType(mysql.TypeLonglong)}, - }} -} - -func benchmarkWindowExecWithCase(b *testing.B, casTest *windowTestCase) { - ctx := casTest.ctx - if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBWindowConcurrency, fmt.Sprintf("%v", casTest.concurrency)); err != nil { - b.Fatal(err) - } - if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBEnablePipelinedWindowFunction, fmt.Sprintf("%v", casTest.pipelined)); err != nil { - b.Fatal(err) - } - - cols := casTest.columns - dataSource := buildMockDataSource(mockDataSourceParameters{ - schema: expression.NewSchema(cols...), - ndvs: []int{0, casTest.ndv, 0, 0}, - orders: []bool{false, casTest.dataSourceSorted, false, false}, - rows: casTest.rows, - ctx: casTest.ctx, - }) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() // prepare a new window-executor - childCols := casTest.columns - schema := expression.NewSchema(childCols...) - windowExec := buildWindowExecutor(casTest.ctx, casTest.windowFunc, casTest.numFunc, casTest.frame, dataSource, schema, childCols[1:2], casTest.concurrency, casTest.dataSourceSorted) - tmpCtx := context.Background() - chk := exec.NewFirstChunk(windowExec) - dataSource.prepareChunks() - - b.StartTimer() - if err := windowExec.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := windowExec.Next(tmpCtx, chk); err != nil { - b.Fatal(b) - } - if chk.NumRows() == 0 { - break - } - } - - if err := windowExec.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func baseBenchmarkWindowRows(b *testing.B, pipelined int) { - b.ReportAllocs() - rows := []int{1000, 100000} - ndvs := []int{1, 10, 1000} - concs := []int{1, 2, 4} - for _, row := range rows { - for _, ndv := range ndvs { - for _, con := range concs { - cas := defaultWindowTestCase() - cas.rows = row - cas.ndv = ndv - cas.concurrency = con - cas.dataSourceSorted = false - cas.windowFunc = ast.WindowFuncRowNumber // cheapest - cas.pipelined = pipelined - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkWindowExecWithCase(b, cas) - }) - } - } - } -} - -func BenchmarkWindowRows(b *testing.B) { - baseBenchmarkWindowRows(b, 0) - baseBenchmarkWindowRows(b, 1) -} - -func baseBenchmarkWindowFunctions(b *testing.B, pipelined int) { - b.ReportAllocs() - windowFuncs := []string{ - // ast.WindowFuncRowNumber, - // ast.WindowFuncRank, - // ast.WindowFuncDenseRank, - // ast.WindowFuncCumeDist, - // ast.WindowFuncPercentRank, - // ast.WindowFuncNtile, - // ast.WindowFuncLead, - ast.WindowFuncLag, - // ast.WindowFuncFirstValue, - // ast.WindowFuncLastValue, - // ast.WindowFuncNthValue, - } - concs := []int{1, 4} - for _, windowFunc := range windowFuncs { - for _, con := range concs { - cas := defaultWindowTestCase() - cas.rows = 100000 - cas.ndv = 1000 - cas.concurrency = con - cas.dataSourceSorted = false - cas.windowFunc = windowFunc - cas.pipelined = pipelined - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkWindowExecWithCase(b, cas) - }) - } - } -} - -func BenchmarkWindowFunctions(b *testing.B) { - baseBenchmarkWindowFunctions(b, 0) - baseBenchmarkWindowFunctions(b, 1) -} - -func baseBenchmarkWindowFunctionsWithFrame(b *testing.B, pipelined int) { - b.ReportAllocs() - windowFuncs := []string{ - ast.WindowFuncRowNumber, - ast.AggFuncBitXor, - } - numFuncs := []int{1, 5} - frames := []*core.WindowFrame{ - {Type: ast.Rows, Start: &core.FrameBound{UnBounded: true}, End: &core.FrameBound{Type: ast.CurrentRow}}, - } - sortTypes := []bool{false, true} - concs := []int{1, 2, 3, 4, 5, 6} - for i, windowFunc := range windowFuncs { - for _, sorted := range sortTypes { - for _, numFunc := range numFuncs { - for _, con := range concs { - cas := defaultWindowTestCase() - cas.rows = 100000 - cas.ndv = 1000 - cas.concurrency = con - cas.dataSourceSorted = sorted - cas.windowFunc = windowFunc - cas.numFunc = numFunc - if i < len(frames) { - cas.frame = frames[i] - } - cas.pipelined = pipelined - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkWindowExecWithCase(b, cas) - }) - } - } - } - } -} - -func BenchmarkWindowFunctionsWithFrame(b *testing.B) { - baseBenchmarkWindowFunctionsWithFrame(b, 0) - baseBenchmarkWindowFunctionsWithFrame(b, 1) -} - -func baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b *testing.B, pipelined int) { - b.ReportAllocs() - windowFunc := ast.AggFuncMax - frame := &core.WindowFrame{Type: ast.Rows, Start: &core.FrameBound{UnBounded: true}, End: &core.FrameBound{UnBounded: true}} - cas := defaultWindowTestCase() - cas.rows = 10000 - cas.ndv = 10 - cas.concurrency = 1 - cas.dataSourceSorted = false - cas.windowFunc = windowFunc - cas.numFunc = 1 - cas.frame = frame - cas.pipelined = pipelined - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkWindowExecWithCase(b, cas) - }) -} - -func BenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b *testing.B) { - baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b, 0) - baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b, 1) -} - -func baseBenchmarkWindowFunctionsWithSlidingWindow(b *testing.B, frameType ast.FrameType, pipelined int) { - b.ReportAllocs() - windowFuncs := []struct { - aggFunc string - aggColTypes byte - }{ - {ast.AggFuncSum, mysql.TypeFloat}, - {ast.AggFuncSum, mysql.TypeNewDecimal}, - {ast.AggFuncCount, mysql.TypeLong}, - {ast.AggFuncAvg, mysql.TypeFloat}, - {ast.AggFuncAvg, mysql.TypeNewDecimal}, - {ast.AggFuncBitXor, mysql.TypeLong}, - {ast.AggFuncMax, mysql.TypeLong}, - {ast.AggFuncMax, mysql.TypeFloat}, - {ast.AggFuncMin, mysql.TypeLong}, - {ast.AggFuncMin, mysql.TypeFloat}, - } - row := 100000 - ndv := 100 - frame := &core.WindowFrame{ - Type: frameType, - Start: &core.FrameBound{Type: ast.Preceding, Num: 10}, - End: &core.FrameBound{Type: ast.Following, Num: 10}, - } - for _, windowFunc := range windowFuncs { - cas := defaultWindowTestCase() - cas.ctx.GetSessionVars().WindowingUseHighPrecision = false - cas.rows = row - cas.ndv = ndv - cas.windowFunc = windowFunc.aggFunc - cas.frame = frame - cas.columns[0].RetType.SetType(windowFunc.aggColTypes) - cas.pipelined = pipelined - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkWindowExecWithCase(b, cas) - }) - } -} - -func BenchmarkWindowFunctionsWithSlidingWindow(b *testing.B) { - baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Rows, 0) - baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Ranges, 0) - baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Rows, 1) - baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Ranges, 1) -} - -type hashJoinTestCase struct { - rows int - cols []*types.FieldType - concurrency int - ctx sessionctx.Context - keyIdx []int - joinType core.JoinType - disk bool - useOuterToBuild bool - rawData string - childrenUsedSchema [][]bool -} - -func (tc hashJoinTestCase) columns() []*expression.Column { - ret := make([]*expression.Column, 0) - for i, t := range tc.cols { - column := &expression.Column{Index: i, RetType: t, UniqueID: tc.ctx.GetSessionVars().AllocPlanColumnID()} - ret = append(ret, column) - } - return ret -} - -func (tc hashJoinTestCase) String() string { - return fmt.Sprintf("(rows:%v, cols:%v, concurency:%v, joinKeyIdx: %v, disk:%v)", - tc.rows, tc.cols, tc.concurrency, tc.keyIdx, tc.disk) -} - -func defaultHashJoinTestCase(cols []*types.FieldType, joinType core.JoinType, useOuterToBuild bool) *hashJoinTestCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) - ctx.GetSessionVars().SetIndexLookupJoinConcurrency(4) - tc := &hashJoinTestCase{rows: 100000, concurrency: 4, ctx: ctx, keyIdx: []int{0, 1}, rawData: wideString} - tc.cols = cols - tc.useOuterToBuild = useOuterToBuild - tc.joinType = joinType - return tc -} - -func prepare4HashJoin(testCase *hashJoinTestCase, innerExec, outerExec exec.Executor) *HashJoinExec { - if testCase.useOuterToBuild { - innerExec, outerExec = outerExec, innerExec - } - cols0 := innerExec.Schema().Columns - cols1 := outerExec.Schema().Columns - - joinSchema := expression.NewSchema() - if testCase.childrenUsedSchema != nil { - for i, used := range testCase.childrenUsedSchema[0] { - if used { - joinSchema.Append(cols0[i]) - } - } - for i, used := range testCase.childrenUsedSchema[1] { - if used { - joinSchema.Append(cols1[i]) - } - } - } else { - joinSchema.Append(cols0...) - joinSchema.Append(cols1...) - } - - joinKeysColIdx := make([]int, 0, len(testCase.keyIdx)) - joinKeysColIdx = append(joinKeysColIdx, testCase.keyIdx...) - probeKeysColIdx := make([]int, 0, len(testCase.keyIdx)) - probeKeysColIdx = append(probeKeysColIdx, testCase.keyIdx...) - e := &HashJoinExec{ - BaseExecutor: exec.NewBaseExecutor(testCase.ctx, joinSchema, 5, innerExec, outerExec), - hashJoinCtx: &hashJoinCtx{ - sessCtx: testCase.ctx, - joinType: testCase.joinType, // 0 for InnerJoin, 1 for LeftOutersJoin, 2 for RightOuterJoin - isOuterJoin: false, - useOuterToBuild: testCase.useOuterToBuild, - concurrency: uint(testCase.concurrency), - probeTypes: exec.RetTypes(outerExec), - buildTypes: exec.RetTypes(innerExec), - }, - probeSideTupleFetcher: &probeSideTupleFetcher{ - probeSideExec: outerExec, - }, - probeWorkers: make([]*probeWorker, testCase.concurrency), - buildWorker: &buildWorker{ - buildKeyColIdx: joinKeysColIdx, - buildSideExec: innerExec, - }, - } - - childrenUsedSchema := markChildrenUsedColsForTest(e.Schema(), e.Children(0).Schema(), e.Children(1).Schema()) - defaultValues := make([]types.Datum, e.buildWorker.buildSideExec.Schema().Len()) - lhsTypes, rhsTypes := exec.RetTypes(innerExec), exec.RetTypes(outerExec) - for i := uint(0); i < e.concurrency; i++ { - e.probeWorkers[i] = &probeWorker{ - workerID: i, - hashJoinCtx: e.hashJoinCtx, - joiner: newJoiner(testCase.ctx, e.joinType, true, defaultValues, - nil, lhsTypes, rhsTypes, childrenUsedSchema, false), - probeKeyColIdx: probeKeysColIdx, - } - } - e.buildWorker.hashJoinCtx = e.hashJoinCtx - memLimit := int64(-1) - if testCase.disk { - memLimit = 1 - } - t := memory.NewTracker(-1, memLimit) - t.SetActionOnExceed(nil) - t2 := disk.NewTracker(-1, -1) - e.Ctx().GetSessionVars().MemTracker = t - e.Ctx().GetSessionVars().StmtCtx.MemTracker.AttachTo(t) - e.Ctx().GetSessionVars().DiskTracker = t2 - e.Ctx().GetSessionVars().StmtCtx.DiskTracker.AttachTo(t2) - return e -} - -// markChildrenUsedColsForTest compares each child with the output schema, and mark -// each column of the child is used by output or not. -func markChildrenUsedColsForTest(outputSchema *expression.Schema, childSchemas ...*expression.Schema) (childrenUsed [][]bool) { - childrenUsed = make([][]bool, 0, len(childSchemas)) - markedOffsets := make(map[int]struct{}) - for _, col := range outputSchema.Columns { - markedOffsets[col.Index] = struct{}{} - } - prefixLen := 0 - for _, childSchema := range childSchemas { - used := make([]bool, len(childSchema.Columns)) - for i := range childSchema.Columns { - if _, ok := markedOffsets[prefixLen+i]; ok { - used[i] = true - } - } - childrenUsed = append(childrenUsed, used) - } - for _, child := range childSchemas { - used := expression.GetUsedList(outputSchema.Columns, child) - childrenUsed = append(childrenUsed, used) - } - return -} - -func benchmarkHashJoinExecWithCase(b *testing.B, casTest *hashJoinTestCase) { - opt1 := mockDataSourceParameters{ - rows: casTest.rows, - ctx: casTest.ctx, - genDataFunc: func(row int, typ *types.FieldType) interface{} { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(row) - case mysql.TypeVarString: - return casTest.rawData - case mysql.TypeDouble: - return float64(row) - default: - panic("not implement") - } - }, - } - opt2 := opt1 - opt1.schema = expression.NewSchema(casTest.columns()...) - opt2.schema = expression.NewSchema(casTest.columns()...) - dataSource1 := buildMockDataSource(opt1) - dataSource2 := buildMockDataSource(opt2) - // Test spill result. - benchmarkHashJoinExec(b, casTest, dataSource1, dataSource2, true) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchmarkHashJoinExec(b, casTest, dataSource1, dataSource2, false) - } -} - -func benchmarkHashJoinExec(b *testing.B, casTest *hashJoinTestCase, opt1, opt2 *mockDataSource, testResult bool) { - b.StopTimer() - executor := prepare4HashJoin(casTest, opt1, opt2) - tmpCtx := context.Background() - chk := exec.NewFirstChunk(executor) - opt1.prepareChunks() - opt2.prepareChunks() - - totalRow := 0 - b.StartTimer() - if err := executor.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := executor.Next(tmpCtx, chk); err != nil { - b.Fatal(err) - } - if chk.NumRows() == 0 { - break - } - totalRow += chk.NumRows() - } - - if testResult { - time.Sleep(200 * time.Millisecond) - if spilled := executor.rowContainer.alreadySpilledSafeForTest(); spilled != casTest.disk { - b.Fatal("wrong usage with disk:", spilled, casTest.disk) - } - } - - if err := executor.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - if totalRow == 0 { - b.Fatal("totalRow == 0") - } -} - -func BenchmarkHashJoinInlineProjection(b *testing.B) { - cols := []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeVarString), - } - - b.ReportAllocs() - - { - cas := defaultHashJoinTestCase(cols, 0, false) - cas.keyIdx = []int{0} - cas.childrenUsedSchema = [][]bool{ - {false, true}, - {false, false}, - } - b.Run("InlineProjection:ON", func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - } - - { - cas := defaultHashJoinTestCase(cols, 0, false) - cas.keyIdx = []int{0} - b.Run("InlineProjection:OFF", func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - } -} - -func BenchmarkHashJoinExec(b *testing.B) { - lvl := log.GetLevel() - log.SetLevel(zapcore.ErrorLevel) - defer log.SetLevel(lvl) - - cols := []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeVarString), - } - - b.ReportAllocs() - cas := defaultHashJoinTestCase(cols, 0, false) - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - cas.disk = true - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - // Replace the wide string column with double column - cols = []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeDouble), - } - - cas = defaultHashJoinTestCase(cols, 0, false) - cas.keyIdx = []int{0} - cas.rows = 5 - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas = defaultHashJoinTestCase(cols, 0, false) - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) -} - -func BenchmarkOuterHashJoinExec(b *testing.B) { - lvl := log.GetLevel() - log.SetLevel(zapcore.ErrorLevel) - defer log.SetLevel(lvl) - - cols := []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeVarString), - } - - b.ReportAllocs() - cas := defaultHashJoinTestCase(cols, 2, true) - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - cas.disk = true - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - // Replace the wide string column with double column - cols = []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeDouble), - } - - cas = defaultHashJoinTestCase(cols, 2, true) - cas.keyIdx = []int{0} - cas.rows = 5 - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas = defaultHashJoinTestCase(cols, 2, true) - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) - - cas.keyIdx = []int{0} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkHashJoinExecWithCase(b, cas) - }) -} - -func benchmarkBuildHashTableForList(b *testing.B, casTest *hashJoinTestCase) { - opt := mockDataSourceParameters{ - schema: expression.NewSchema(casTest.columns()...), - rows: casTest.rows, - ctx: casTest.ctx, - genDataFunc: func(row int, typ *types.FieldType) interface{} { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(row) - case mysql.TypeVarString: - return casTest.rawData - default: - panic("not implement") - } - }, - } - dataSource1 := buildMockDataSource(opt) - dataSource2 := buildMockDataSource(opt) - - dataSource1.prepareChunks() - benchmarkBuildHashTable(b, casTest, dataSource1, dataSource2, true) - b.ResetTimer() - for i := 0; i < b.N; i++ { - benchmarkBuildHashTable(b, casTest, dataSource1, dataSource2, false) - } -} - -func benchmarkBuildHashTable(b *testing.B, casTest *hashJoinTestCase, dataSource1, dataSource2 *mockDataSource, testResult bool) { - b.StopTimer() - exec := prepare4HashJoin(casTest, dataSource1, dataSource2) - tmpCtx := context.Background() - if err := exec.Open(tmpCtx); err != nil { - b.Fatal(err) - } - exec.prepared = true - - innerResultCh := make(chan *chunk.Chunk, len(dataSource1.chunks)) - for _, chk := range dataSource1.chunks { - innerResultCh <- chk - } - close(innerResultCh) - - b.StartTimer() - if err := exec.buildWorker.buildHashTableForList(innerResultCh); err != nil { - b.Fatal(err) - } - - if testResult { - time.Sleep(200 * time.Millisecond) - if exec.rowContainer.alreadySpilledSafeForTest() != casTest.disk { - b.Fatal("wrong usage with disk") - } - } - - if err := exec.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() -} - -func BenchmarkBuildHashTableForList(b *testing.B) { - lvl := log.GetLevel() - log.SetLevel(zapcore.ErrorLevel) - defer log.SetLevel(lvl) - - cols := []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeVarString), - } - - b.ReportAllocs() - cas := defaultHashJoinTestCase(cols, 0, false) - rows := []int{10, 100000} - keyIdxs := [][]int{{0, 1}, {0}} - disks := []bool{false, true} - for _, row := range rows { - for _, keyIdx := range keyIdxs { - for _, disk := range disks { - cas.rows = row - cas.keyIdx = keyIdx - cas.disk = disk - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkBuildHashTableForList(b, cas) - }) - } - } - } -} - -type indexJoinTestCase struct { - outerRows int - innerRows int - concurrency int - ctx sessionctx.Context - outerJoinKeyIdx []int - innerJoinKeyIdx []int - outerHashKeyIdx []int - innerHashKeyIdx []int - innerIdx []int - needOuterSort bool - rawData string -} - -func (tc indexJoinTestCase) columns() []*expression.Column { - return []*expression.Column{ - {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, - {Index: 1, RetType: types.NewFieldType(mysql.TypeDouble)}, - {Index: 2, RetType: types.NewFieldType(mysql.TypeVarString)}, - } -} - -func defaultIndexJoinTestCase() *indexJoinTestCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().SnapshotTS = 1 - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) - tc := &indexJoinTestCase{ - outerRows: 100000, - innerRows: variable.DefMaxChunkSize * 100, - concurrency: 4, - ctx: ctx, - outerJoinKeyIdx: []int{0, 1}, - innerJoinKeyIdx: []int{0, 1}, - outerHashKeyIdx: []int{0, 1}, - innerHashKeyIdx: []int{0, 1}, - innerIdx: []int{0, 1}, - rawData: wideString, - } - return tc -} - -func (tc indexJoinTestCase) String() string { - return fmt.Sprintf("(outerRows:%v, innerRows:%v, concurency:%v, outerJoinKeyIdx: %v, innerJoinKeyIdx: %v, NeedOuterSort:%v)", - tc.outerRows, tc.innerRows, tc.concurrency, tc.outerJoinKeyIdx, tc.innerJoinKeyIdx, tc.needOuterSort) -} -func (tc indexJoinTestCase) getMockDataSourceOptByRows(rows int) mockDataSourceParameters { - return mockDataSourceParameters{ - schema: expression.NewSchema(tc.columns()...), - rows: rows, - ctx: tc.ctx, - genDataFunc: func(row int, typ *types.FieldType) interface{} { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(row) - case mysql.TypeDouble: - return float64(row) - case mysql.TypeVarString: - return tc.rawData - default: - panic("not implement") - } - }, - } -} - -func prepare4IndexInnerHashJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { - outerCols, innerCols := tc.columns(), tc.columns() - joinSchema := expression.NewSchema(outerCols...) - joinSchema.Append(innerCols...) - leftTypes, rightTypes := exec.RetTypes(outerDS), exec.RetTypes(innerDS) - defaultValues := make([]types.Datum, len(innerCols)) - colLens := make([]int, len(innerCols)) - for i := range colLens { - colLens[i] = types.UnspecifiedLength - } - keyOff2IdxOff := make([]int, len(tc.outerJoinKeyIdx)) - for i := range keyOff2IdxOff { - keyOff2IdxOff[i] = i - } - - readerBuilder, err := newExecutorBuilder(tc.ctx, nil, nil). - newDataReaderBuilder(&mockPhysicalIndexReader{e: innerDS}) - if err != nil { - return nil, err - } - - e := &IndexLookUpJoin{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 1, outerDS), - outerCtx: outerCtx{ - rowTypes: leftTypes, - keyCols: tc.outerJoinKeyIdx, - hashCols: tc.outerHashKeyIdx, - }, - innerCtx: innerCtx{ - readerBuilder: readerBuilder, - rowTypes: rightTypes, - colLens: colLens, - keyCols: tc.innerJoinKeyIdx, - hashCols: tc.innerHashKeyIdx, - }, - workerWg: new(sync.WaitGroup), - joiner: newJoiner(tc.ctx, 0, false, defaultValues, nil, leftTypes, rightTypes, nil, false), - isOuterJoin: false, - keyOff2IdxOff: keyOff2IdxOff, - lastColHelper: nil, - } - e.joinResult = exec.NewFirstChunk(e) - return e, nil -} - -func prepare4IndexOuterHashJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { - e, err := prepare4IndexInnerHashJoin(tc, outerDS, innerDS) - if err != nil { - return nil, err - } - idxHash := &IndexNestedLoopHashJoin{IndexLookUpJoin: *e.(*IndexLookUpJoin)} - concurrency := tc.concurrency - idxHash.joiners = make([]joiner, concurrency) - for i := 0; i < concurrency; i++ { - idxHash.joiners[i] = e.(*IndexLookUpJoin).joiner.Clone() - } - return idxHash, nil -} - -func prepare4IndexMergeJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { - outerCols, innerCols := tc.columns(), tc.columns() - joinSchema := expression.NewSchema(outerCols...) - joinSchema.Append(innerCols...) - outerJoinKeys := make([]*expression.Column, 0, len(tc.outerJoinKeyIdx)) - innerJoinKeys := make([]*expression.Column, 0, len(tc.innerJoinKeyIdx)) - for _, keyIdx := range tc.outerJoinKeyIdx { - outerJoinKeys = append(outerJoinKeys, outerCols[keyIdx]) - } - for _, keyIdx := range tc.innerJoinKeyIdx { - innerJoinKeys = append(innerJoinKeys, innerCols[keyIdx]) - } - leftTypes, rightTypes := exec.RetTypes(outerDS), exec.RetTypes(innerDS) - defaultValues := make([]types.Datum, len(innerCols)) - colLens := make([]int, len(innerCols)) - for i := range colLens { - colLens[i] = types.UnspecifiedLength - } - keyOff2IdxOff := make([]int, len(outerJoinKeys)) - for i := range keyOff2IdxOff { - keyOff2IdxOff[i] = i - } - - compareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) - outerCompareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) - for i := range outerJoinKeys { - compareFuncs = append(compareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], innerJoinKeys[i])) - outerCompareFuncs = append(outerCompareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], outerJoinKeys[i])) - } - - readerBuilder, err := newExecutorBuilder(tc.ctx, nil, nil). - newDataReaderBuilder(&mockPhysicalIndexReader{e: innerDS}) - if err != nil { - return nil, err - } - - e := &IndexLookUpMergeJoin{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 2, outerDS), - outerMergeCtx: outerMergeCtx{ - rowTypes: leftTypes, - keyCols: tc.outerJoinKeyIdx, - joinKeys: outerJoinKeys, - needOuterSort: tc.needOuterSort, - compareFuncs: outerCompareFuncs, - }, - innerMergeCtx: innerMergeCtx{ - readerBuilder: readerBuilder, - rowTypes: rightTypes, - joinKeys: innerJoinKeys, - colLens: colLens, - keyCols: tc.innerJoinKeyIdx, - compareFuncs: compareFuncs, - }, - workerWg: new(sync.WaitGroup), - isOuterJoin: false, - keyOff2IdxOff: keyOff2IdxOff, - lastColHelper: nil, - } - concurrency := e.Ctx().GetSessionVars().IndexLookupJoinConcurrency() - joiners := make([]joiner, concurrency) - for i := 0; i < concurrency; i++ { - joiners[i] = newJoiner(tc.ctx, 0, false, defaultValues, nil, leftTypes, rightTypes, nil, false) - } - e.joiners = joiners - return e, nil -} - -type indexJoinType int8 - -const ( - indexInnerHashJoin indexJoinType = iota - indexOuterHashJoin - indexMergeJoin -) - -func benchmarkIndexJoinExecWithCase( - b *testing.B, - tc *indexJoinTestCase, - outerDS *mockDataSource, - innerDS *mockDataSource, - execType indexJoinType, -) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() - var executor exec.Executor - var err error - switch execType { - case indexInnerHashJoin: - executor, err = prepare4IndexInnerHashJoin(tc, outerDS, innerDS) - case indexOuterHashJoin: - executor, err = prepare4IndexOuterHashJoin(tc, outerDS, innerDS) - case indexMergeJoin: - executor, err = prepare4IndexMergeJoin(tc, outerDS, innerDS) - } - - if err != nil { - b.Fatal(err) - } - - tmpCtx := context.Background() - chk := exec.NewFirstChunk(executor) - outerDS.prepareChunks() - innerDS.prepareChunks() - - b.StartTimer() - if err = executor.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := executor.Next(tmpCtx, chk); err != nil { - b.Fatal(err) - } - if chk.NumRows() == 0 { - break - } - } - - if err := executor.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func BenchmarkIndexJoinExec(b *testing.B) { - lvl := log.GetLevel() - log.SetLevel(zapcore.ErrorLevel) - defer log.SetLevel(lvl) - - b.ReportAllocs() - tc := defaultIndexJoinTestCase() - outerOpt := tc.getMockDataSourceOptByRows(tc.outerRows) - innerOpt := tc.getMockDataSourceOptByRows(tc.innerRows) - outerDS := buildMockDataSourceWithIndex(outerOpt, tc.innerIdx) - innerDS := buildMockDataSourceWithIndex(innerOpt, tc.innerIdx) - - tc.needOuterSort = true - b.Run(fmt.Sprintf("index merge join need outer sort %v", tc), func(b *testing.B) { - benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexMergeJoin) - }) - - tc.needOuterSort = false - b.Run(fmt.Sprintf("index merge join %v", tc), func(b *testing.B) { - benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexMergeJoin) - }) - - b.Run(fmt.Sprintf("index inner hash join %v", tc), func(b *testing.B) { - benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexInnerHashJoin) - }) - - b.Run(fmt.Sprintf("index outer hash join %v", tc), func(b *testing.B) { - benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexOuterHashJoin) - }) -} - -type mergeJoinTestCase struct { - indexJoinTestCase - childrenUsedSchema [][]bool -} - -func prepareMergeJoinExec(tc *mergeJoinTestCase, joinSchema *expression.Schema, leftExec, rightExec exec.Executor, defaultValues []types.Datum, - compareFuncs []expression.CompareFunc, innerJoinKeys []*expression.Column, outerJoinKeys []*expression.Column) *MergeJoinExec { - // only benchmark inner join - mergeJoinExec := &MergeJoinExec{ - stmtCtx: tc.ctx.GetSessionVars().StmtCtx, - BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 3, leftExec, rightExec), - compareFuncs: compareFuncs, - isOuterJoin: false, - } - - mergeJoinExec.joiner = newJoiner( - tc.ctx, - 0, - false, - defaultValues, - nil, - exec.RetTypes(leftExec), - exec.RetTypes(rightExec), - tc.childrenUsedSchema, - false, - ) - - mergeJoinExec.innerTable = &mergeJoinTable{ - isInner: true, - childIndex: 1, - joinKeys: innerJoinKeys, - } - - mergeJoinExec.outerTable = &mergeJoinTable{ - childIndex: 0, - filters: nil, - joinKeys: outerJoinKeys, - } - - return mergeJoinExec -} - -func prepare4MergeJoin(tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource, sorted bool, concurrency int) exec.Executor { - outerCols, innerCols := tc.columns(), tc.columns() - - joinSchema := expression.NewSchema() - if tc.childrenUsedSchema != nil { - for i, used := range tc.childrenUsedSchema[0] { - if used { - joinSchema.Append(outerCols[i]) - } - } - for i, used := range tc.childrenUsedSchema[1] { - if used { - joinSchema.Append(innerCols[i]) - } - } - } else { - joinSchema.Append(outerCols...) - joinSchema.Append(innerCols...) - } - - outerJoinKeys := make([]*expression.Column, 0, len(tc.outerJoinKeyIdx)) - innerJoinKeys := make([]*expression.Column, 0, len(tc.innerJoinKeyIdx)) - for _, keyIdx := range tc.outerJoinKeyIdx { - outerJoinKeys = append(outerJoinKeys, outerCols[keyIdx]) - } - for _, keyIdx := range tc.innerJoinKeyIdx { - innerJoinKeys = append(innerJoinKeys, innerCols[keyIdx]) - } - compareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) - for i := range outerJoinKeys { - compareFuncs = append(compareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], innerJoinKeys[i])) - } - - defaultValues := make([]types.Datum, len(innerCols)) - - var leftExec, rightExec exec.Executor - if sorted { - leftSortExec := &SortExec{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, innerDS.Schema(), 3, innerDS), - ByItems: make([]*util.ByItems, 0, len(tc.innerJoinKeyIdx)), - schema: innerDS.Schema(), - } - for _, key := range innerJoinKeys { - leftSortExec.ByItems = append(leftSortExec.ByItems, &util.ByItems{Expr: key}) - } - leftExec = leftSortExec - - rightSortExec := &SortExec{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, outerDS.Schema(), 4, outerDS), - ByItems: make([]*util.ByItems, 0, len(tc.outerJoinKeyIdx)), - schema: outerDS.Schema(), - } - for _, key := range outerJoinKeys { - rightSortExec.ByItems = append(rightSortExec.ByItems, &util.ByItems{Expr: key}) - } - rightExec = rightSortExec - } else { - leftExec = innerDS - rightExec = outerDS - } - - var e exec.Executor - if concurrency == 1 { - e = prepareMergeJoinExec(tc, joinSchema, leftExec, rightExec, defaultValues, compareFuncs, innerJoinKeys, outerJoinKeys) - } else { - // build dataSources - dataSources := []exec.Executor{leftExec, rightExec} - // build splitters - innerByItems := make([]expression.Expression, 0, len(innerJoinKeys)) - for _, innerJoinKey := range innerJoinKeys { - innerByItems = append(innerByItems, innerJoinKey) - } - outerByItems := make([]expression.Expression, 0, len(outerJoinKeys)) - for _, outerJoinKey := range outerJoinKeys { - outerByItems = append(outerByItems, outerJoinKey) - } - splitters := []partitionSplitter{ - &partitionHashSplitter{ - byItems: innerByItems, - numWorkers: concurrency, - }, - &partitionHashSplitter{ - byItems: outerByItems, - numWorkers: concurrency, - }, - } - // build ShuffleMergeJoinExec - shuffle := &ShuffleExec{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 4), - concurrency: concurrency, - dataSources: dataSources, - splitters: splitters, - } - - // build workers, only benchmark inner join - shuffle.workers = make([]*shuffleWorker, shuffle.concurrency) - for i := range shuffle.workers { - leftReceiver := shuffleReceiver{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, leftExec.Schema(), 0), - } - rightReceiver := shuffleReceiver{ - BaseExecutor: exec.NewBaseExecutor(tc.ctx, rightExec.Schema(), 0), - } - w := &shuffleWorker{ - receivers: []*shuffleReceiver{&leftReceiver, &rightReceiver}, - } - w.childExec = prepareMergeJoinExec(tc, joinSchema, &leftReceiver, &rightReceiver, defaultValues, compareFuncs, innerJoinKeys, outerJoinKeys) - - shuffle.workers[i] = w - } - e = shuffle - } - - return e -} - -func newMergeJoinBenchmark(numOuterRows, numInnerDup, numInnerRedundant int) (tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource) { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().SnapshotTS = 1 - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) - - numInnerRows := numOuterRows*numInnerDup + numInnerRedundant - itc := &indexJoinTestCase{ - outerRows: numOuterRows, - innerRows: numInnerRows, - concurrency: 4, - ctx: ctx, - outerJoinKeyIdx: []int{0, 1}, - innerJoinKeyIdx: []int{0, 1}, - outerHashKeyIdx: []int{0, 1}, - innerHashKeyIdx: []int{0, 1}, - innerIdx: []int{0, 1}, - rawData: wideString, - } - tc = &mergeJoinTestCase{*itc, nil} - outerOpt := mockDataSourceParameters{ - schema: expression.NewSchema(tc.columns()...), - rows: numOuterRows, - ctx: tc.ctx, - genDataFunc: func(row int, typ *types.FieldType) interface{} { - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(row) - case mysql.TypeDouble: - return float64(row) - case mysql.TypeVarString: - return tc.rawData - default: - panic("not implement") - } - }, - } - - innerOpt := mockDataSourceParameters{ - schema: expression.NewSchema(tc.columns()...), - rows: numInnerRows, - ctx: tc.ctx, - genDataFunc: func(row int, typ *types.FieldType) interface{} { - row = row / numInnerDup - switch typ.GetType() { - case mysql.TypeLong, mysql.TypeLonglong: - return int64(row) - case mysql.TypeDouble: - return float64(row) - case mysql.TypeVarString: - return tc.rawData - default: - panic("not implement") - } - }, - } - - innerDS = buildMockDataSource(innerOpt) - outerDS = buildMockDataSource(outerOpt) - - return -} - -type mergeJoinType int8 - -const ( - innerMergeJoin mergeJoinType = iota -) - -func benchmarkMergeJoinExecWithCase(b *testing.B, tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource, joinType mergeJoinType) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() - var executor exec.Executor - switch joinType { - case innerMergeJoin: - executor = prepare4MergeJoin(tc, innerDS, outerDS, true, 2) - } - - tmpCtx := context.Background() - chk := exec.NewFirstChunk(executor) - outerDS.prepareChunks() - innerDS.prepareChunks() - - b.StartTimer() - if err := executor.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := executor.Next(tmpCtx, chk); err != nil { - b.Fatal(err) - } - if chk.NumRows() == 0 { - break - } - } - - if err := executor.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func BenchmarkMergeJoinExec(b *testing.B) { - lvl := log.GetLevel() - log.SetLevel(zapcore.ErrorLevel) - defer log.SetLevel(lvl) - b.ReportAllocs() - - totalRows := 300000 - - innerDupAndRedundant := [][]int{ - {1, 0}, - {100, 0}, - {10000, 0}, - {1, 30000}, - } - - childrenUsedSchemas := [][][]bool{ - nil, - { - {true, false, false}, - {false, true, false}, - }, - } - - for _, params := range innerDupAndRedundant { - numInnerDup, numInnerRedundant := params[0], params[1] - for _, childrenUsedSchema := range childrenUsedSchemas { - tc, innerDS, outerDS := newMergeJoinBenchmark(totalRows/numInnerDup, numInnerDup, numInnerRedundant) - inlineProj := false - if childrenUsedSchema != nil { - inlineProj = true - tc.childrenUsedSchema = childrenUsedSchema - } - - b.Run(fmt.Sprintf("merge join %v InlineProj:%v", tc, inlineProj), func(b *testing.B) { - benchmarkMergeJoinExecWithCase(b, tc, outerDS, innerDS, innerMergeJoin) - }) - } - } -} - -type sortCase struct { - rows int - orderByIdx []int - ndvs []int - ctx sessionctx.Context -} - -func (tc sortCase) columns() []*expression.Column { - return []*expression.Column{ - {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, - {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, - } -} - -func (tc sortCase) String() string { - return fmt.Sprintf("(rows:%v, orderBy:%v, ndvs: %v)", tc.rows, tc.orderByIdx, tc.ndvs) -} - -func defaultSortTestCase() *sortCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - tc := &sortCase{rows: 300000, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx} - return tc -} - -func sortTestCaseWithMemoryLimit(bytesLimit int64) *sortCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().MemTracker = memory.NewTracker(-1, bytesLimit) - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, bytesLimit) - ctx.GetSessionVars().StmtCtx.MemTracker.AttachTo(ctx.GetSessionVars().MemTracker) - tc := &sortCase{rows: 300000, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx} - return tc -} - -func benchmarkSortExec(b *testing.B, cas *sortCase) { - opt := mockDataSourceParameters{ - schema: expression.NewSchema(cas.columns()...), - rows: cas.rows, - ctx: cas.ctx, - ndvs: cas.ndvs, - } - dataSource := buildMockDataSource(opt) - executor := &SortExec{ - BaseExecutor: exec.NewBaseExecutor(cas.ctx, dataSource.Schema(), 4, dataSource), - ByItems: make([]*util.ByItems, 0, len(cas.orderByIdx)), - schema: dataSource.Schema(), - } - for _, idx := range cas.orderByIdx { - executor.ByItems = append(executor.ByItems, &util.ByItems{Expr: cas.columns()[idx]}) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() - tmpCtx := context.Background() - chk := exec.NewFirstChunk(executor) - dataSource.prepareChunks() - - b.StartTimer() - if err := executor.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := executor.Next(tmpCtx, chk); err != nil { - b.Fatal(err) - } - if chk.NumRows() == 0 { - break - } - } - - if err := executor.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func BenchmarkSortExec(b *testing.B) { - b.ReportAllocs() - cas := defaultSortTestCase() - benchmarkSortExecDerivateCases(b, cas) -} - -func BenchmarkSortExecSpillToDisk(b *testing.B) { - enableTmpStorageOnOOMCurrentVal := variable.EnableTmpStorageOnOOM.Load() - variable.EnableTmpStorageOnOOM.Store(true) - defer variable.EnableTmpStorageOnOOM.Store(enableTmpStorageOnOOMCurrentVal) - - b.ReportAllocs() - cas := sortTestCaseWithMemoryLimit(1) - benchmarkSortExecDerivateCases(b, cas) -} - -func benchmarkSortExecDerivateCases(b *testing.B, cas *sortCase) { - cas.ndvs = []int{0, 0} - cas.orderByIdx = []int{0, 1} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkSortExec(b, cas) - }) - - ndvs := []int{1, 10000} - for _, ndv := range ndvs { - cas.ndvs = []int{ndv, 0} - cas.orderByIdx = []int{0, 1} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkSortExec(b, cas) - }) - - cas.ndvs = []int{ndv, 0} - cas.orderByIdx = []int{0} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkSortExec(b, cas) - }) - - cas.ndvs = []int{ndv, 0} - cas.orderByIdx = []int{1} - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkSortExec(b, cas) - }) - } -} - -type limitCase struct { - rows int - offset int - count int - childUsedSchema []bool - usingInlineProjection bool - ctx sessionctx.Context -} - -func (tc limitCase) columns() []*expression.Column { - return []*expression.Column{ - {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, - {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, - } -} - -func (tc limitCase) String() string { - return fmt.Sprintf("(rows:%v, offset:%v, count:%v, inline_projection:%v)", - tc.rows, tc.offset, tc.count, tc.usingInlineProjection) -} - -func defaultLimitTestCase() *limitCase { - ctx := mock.NewContext() - ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize - ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize - ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) - tc := &limitCase{ - rows: 30000, - offset: 10000, - count: 10000, - childUsedSchema: []bool{false, true}, - usingInlineProjection: false, - ctx: ctx, - } - return tc -} - -func benchmarkLimitExec(b *testing.B, cas *limitCase) { - opt := mockDataSourceParameters{ - schema: expression.NewSchema(cas.columns()...), - rows: cas.rows, - ctx: cas.ctx, - } - dataSource := buildMockDataSource(opt) - var exe exec.Executor - limit := &LimitExec{ - BaseExecutor: exec.NewBaseExecutor(cas.ctx, dataSource.Schema(), 4, dataSource), - begin: uint64(cas.offset), - end: uint64(cas.offset + cas.count), - } - if cas.usingInlineProjection { - if len(cas.childUsedSchema) > 0 { - limit.columnIdxsUsedByChild = make([]int, 0, len(cas.childUsedSchema)) - for i, used := range cas.childUsedSchema { - if used { - limit.columnIdxsUsedByChild = append(limit.columnIdxsUsedByChild, i) - } - } - } - exe = limit - } else { - columns := cas.columns() - usedCols := make([]*expression.Column, 0, len(columns)) - exprs := make([]expression.Expression, 0, len(columns)) - for i, used := range cas.childUsedSchema { - if used { - usedCols = append(usedCols, columns[i]) - exprs = append(exprs, columns[i]) - } - } - proj := &ProjectionExec{ - BaseExecutor: exec.NewBaseExecutor(cas.ctx, expression.NewSchema(usedCols...), 0, limit), - numWorkers: 1, - evaluatorSuit: expression.NewEvaluatorSuite(exprs, false), - } - exe = proj - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - b.StopTimer() - tmpCtx := context.Background() - chk := exec.NewFirstChunk(exe) - dataSource.prepareChunks() - - b.StartTimer() - if err := exe.Open(tmpCtx); err != nil { - b.Fatal(err) - } - for { - if err := exe.Next(tmpCtx, chk); err != nil { - b.Fatal(err) - } - if chk.NumRows() == 0 { - break - } - } - - if err := exe.Close(); err != nil { - b.Fatal(err) - } - b.StopTimer() - } -} - -func BenchmarkLimitExec(b *testing.B) { - b.ReportAllocs() - cas := defaultLimitTestCase() - usingInlineProjection := []bool{false, true} - for _, inlineProjection := range usingInlineProjection { - cas.usingInlineProjection = inlineProjection - b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { - benchmarkLimitExec(b, cas) - }) - } -} - -func BenchmarkReadLastLinesOfHugeLine(b *testing.B) { - // step 1. initial a huge line log file - hugeLine := make([]byte, 1024*1024*10) - for i := range hugeLine { - hugeLine[i] = 'a' + byte(i%26) - } - fileName := "tidb.log" - err := os.WriteFile(fileName, hugeLine, 0644) - if err != nil { - b.Fatal(err) - } - file, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm) - if err != nil { - b.Fatal(err) - } - defer func() { - file.Close() - os.Remove(fileName) - }() - stat, _ := file.Stat() - filesize := stat.Size() - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, n, err := readLastLines(context.Background(), file, filesize) - if err != nil { - b.Fatal(err) - } - if n != len(hugeLine) { - b.Fatalf("len %v, expected: %v", n, len(hugeLine)) - } - } -} - -func BenchmarkAggPartialResultMapperMemoryUsage(b *testing.B) { - b.ReportAllocs() - type testCase struct { - rowNum int - } - cases := []testCase{ - { - rowNum: 0, - }, - { - rowNum: 100, - }, - { - rowNum: 10000, - }, - { - rowNum: 1000000, - }, - { - rowNum: 851968, // 6.5 * (1 << 17) - }, - { - rowNum: 851969, // 6.5 * (1 << 17) + 1 - }, - { - rowNum: 425984, // 6.5 * (1 << 16) - }, - { - rowNum: 425985, // 6.5 * (1 << 16) + 1 - }, - } - - for _, c := range cases { - b.Run(fmt.Sprintf("MapRows %v", c.rowNum), func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - aggMap := make(aggregate.AggPartialResultMapper) - tempSlice := make([]aggfuncs.PartialResult, 10) - for num := 0; num < c.rowNum; num++ { - aggMap[strconv.Itoa(num)] = tempSlice - } - } - }) - } -} - -func BenchmarkPipelinedRowNumberWindowFunctionExecution(b *testing.B) { - b.ReportAllocs() -} diff --git a/executor/builder.go b/executor/builder.go deleted file mode 100644 index 86be073d02ea8..0000000000000 --- a/executor/builder.go +++ /dev/null @@ -1,5481 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "cmp" - "context" - "fmt" - "math" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/diagnosticspb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/calibrateresource" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/pdhelper" - "github.com/pingcap/tidb/executor/internal/querywatch" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/executor/lockstats" - executor_metrics "github.com/pingcap/tidb/executor/metrics" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - plannerutil "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/cteutil" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tipb/go-tipb" - clientkv "github.com/tikv/client-go/v2/kv" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/txnkv" - "github.com/tikv/client-go/v2/txnkv/txnsnapshot" - clientutil "github.com/tikv/client-go/v2/util" -) - -// executorBuilder builds an Executor from a Plan. -// The InfoSchema must not change during execution. -type executorBuilder struct { - ctx sessionctx.Context - is infoschema.InfoSchema - err error // err is set when there is error happened during Executor building process. - hasLock bool - Ti *TelemetryInfo - // isStaleness means whether this statement use stale read. - isStaleness bool - txnScope string - readReplicaScope string - inUpdateStmt bool - inDeleteStmt bool - inInsertStmt bool - inSelectLockStmt bool - - // forDataReaderBuilder indicates whether the builder is used by a dataReaderBuilder. - // When forDataReader is true, the builder should use the dataReaderTS as the executor read ts. This is because - // dataReaderBuilder can be used in concurrent goroutines, so we must ensure that getting the ts should be thread safe and - // can return a correct value even if the session context has already been destroyed - forDataReaderBuilder bool - dataReaderTS uint64 - - // Used when building MPPGather. - encounterUnionScan bool -} - -// CTEStorages stores resTbl and iterInTbl for CTEExec. -// There will be a map[CTEStorageID]*CTEStorages in StmtCtx, -// which will store all CTEStorages to make all shared CTEs use same the CTEStorages. -type CTEStorages struct { - ResTbl cteutil.Storage - IterInTbl cteutil.Storage - Producer *cteProducer -} - -func newExecutorBuilder(ctx sessionctx.Context, is infoschema.InfoSchema, ti *TelemetryInfo) *executorBuilder { - txnManager := sessiontxn.GetTxnManager(ctx) - return &executorBuilder{ - ctx: ctx, - is: is, - Ti: ti, - isStaleness: staleread.IsStmtStaleness(ctx), - txnScope: txnManager.GetTxnScope(), - readReplicaScope: txnManager.GetReadReplicaScope(), - } -} - -// MockPhysicalPlan is used to return a specified executor in when build. -// It is mainly used for testing. -type MockPhysicalPlan interface { - plannercore.PhysicalPlan - GetExecutor() exec.Executor -} - -// MockExecutorBuilder is a wrapper for executorBuilder. -// ONLY used in test. -type MockExecutorBuilder struct { - *executorBuilder -} - -// NewMockExecutorBuilderForTest is ONLY used in test. -func NewMockExecutorBuilderForTest(ctx sessionctx.Context, is infoschema.InfoSchema, ti *TelemetryInfo) *MockExecutorBuilder { - return &MockExecutorBuilder{ - executorBuilder: newExecutorBuilder(ctx, is, ti)} -} - -// Build builds an executor tree according to `p`. -func (b *MockExecutorBuilder) Build(p plannercore.Plan) exec.Executor { - return b.build(p) -} - -func (b *executorBuilder) build(p plannercore.Plan) exec.Executor { - switch v := p.(type) { - case nil: - return nil - case *plannercore.Change: - return b.buildChange(v) - case *plannercore.CheckTable: - return b.buildCheckTable(v) - case *plannercore.RecoverIndex: - return b.buildRecoverIndex(v) - case *plannercore.CleanupIndex: - return b.buildCleanupIndex(v) - case *plannercore.CheckIndexRange: - return b.buildCheckIndexRange(v) - case *plannercore.ChecksumTable: - return b.buildChecksumTable(v) - case *plannercore.ReloadExprPushdownBlacklist: - return b.buildReloadExprPushdownBlacklist(v) - case *plannercore.ReloadOptRuleBlacklist: - return b.buildReloadOptRuleBlacklist(v) - case *plannercore.AdminPlugins: - return b.buildAdminPlugins(v) - case *plannercore.DDL: - return b.buildDDL(v) - case *plannercore.Deallocate: - return b.buildDeallocate(v) - case *plannercore.Delete: - return b.buildDelete(v) - case *plannercore.Execute: - return b.buildExecute(v) - case *plannercore.Trace: - return b.buildTrace(v) - case *plannercore.Explain: - return b.buildExplain(v) - case *plannercore.PointGetPlan: - return b.buildPointGet(v) - case *plannercore.BatchPointGetPlan: - return b.buildBatchPointGet(v) - case *plannercore.Insert: - return b.buildInsert(v) - case *plannercore.ImportInto: - return b.buildImportInto(v) - case *plannercore.LoadData: - return b.buildLoadData(v) - case *plannercore.LoadStats: - return b.buildLoadStats(v) - case *plannercore.LockStats: - return b.buildLockStats(v) - case *plannercore.UnlockStats: - return b.buildUnlockStats(v) - case *plannercore.IndexAdvise: - return b.buildIndexAdvise(v) - case *plannercore.PlanReplayer: - return b.buildPlanReplayer(v) - case *plannercore.PhysicalLimit: - return b.buildLimit(v) - case *plannercore.Prepare: - return b.buildPrepare(v) - case *plannercore.PhysicalLock: - return b.buildSelectLock(v) - case *plannercore.CancelDDLJobs: - return b.buildCancelDDLJobs(v) - case *plannercore.PauseDDLJobs: - return b.buildPauseDDLJobs(v) - case *plannercore.ResumeDDLJobs: - return b.buildResumeDDLJobs(v) - case *plannercore.ShowNextRowID: - return b.buildShowNextRowID(v) - case *plannercore.ShowDDL: - return b.buildShowDDL(v) - case *plannercore.PhysicalShowDDLJobs: - return b.buildShowDDLJobs(v) - case *plannercore.ShowDDLJobQueries: - return b.buildShowDDLJobQueries(v) - case *plannercore.ShowDDLJobQueriesWithRange: - return b.buildShowDDLJobQueriesWithRange(v) - case *plannercore.ShowSlow: - return b.buildShowSlow(v) - case *plannercore.PhysicalShow: - return b.buildShow(v) - case *plannercore.Simple: - return b.buildSimple(v) - case *plannercore.PhysicalSimpleWrapper: - return b.buildSimple(&v.Inner) - case *plannercore.Set: - return b.buildSet(v) - case *plannercore.SetConfig: - return b.buildSetConfig(v) - case *plannercore.PhysicalSort: - return b.buildSort(v) - case *plannercore.PhysicalTopN: - return b.buildTopN(v) - case *plannercore.PhysicalUnionAll: - return b.buildUnionAll(v) - case *plannercore.Update: - return b.buildUpdate(v) - case *plannercore.PhysicalUnionScan: - return b.buildUnionScanExec(v) - case *plannercore.PhysicalHashJoin: - return b.buildHashJoin(v) - case *plannercore.PhysicalMergeJoin: - return b.buildMergeJoin(v) - case *plannercore.PhysicalIndexJoin: - return b.buildIndexLookUpJoin(v) - case *plannercore.PhysicalIndexMergeJoin: - return b.buildIndexLookUpMergeJoin(v) - case *plannercore.PhysicalIndexHashJoin: - return b.buildIndexNestedLoopHashJoin(v) - case *plannercore.PhysicalSelection: - return b.buildSelection(v) - case *plannercore.PhysicalHashAgg: - return b.buildHashAgg(v) - case *plannercore.PhysicalStreamAgg: - return b.buildStreamAgg(v) - case *plannercore.PhysicalProjection: - return b.buildProjection(v) - case *plannercore.PhysicalMemTable: - return b.buildMemTable(v) - case *plannercore.PhysicalTableDual: - return b.buildTableDual(v) - case *plannercore.PhysicalApply: - return b.buildApply(v) - case *plannercore.PhysicalMaxOneRow: - return b.buildMaxOneRow(v) - case *plannercore.Analyze: - return b.buildAnalyze(v) - case *plannercore.PhysicalTableReader: - return b.buildTableReader(v) - case *plannercore.PhysicalTableSample: - return b.buildTableSample(v) - case *plannercore.PhysicalIndexReader: - return b.buildIndexReader(v) - case *plannercore.PhysicalIndexLookUpReader: - return b.buildIndexLookUpReader(v) - case *plannercore.PhysicalWindow: - return b.buildWindow(v) - case *plannercore.PhysicalShuffle: - return b.buildShuffle(v) - case *plannercore.PhysicalShuffleReceiverStub: - return b.buildShuffleReceiverStub(v) - case *plannercore.SQLBindPlan: - return b.buildSQLBindExec(v) - case *plannercore.SplitRegion: - return b.buildSplitRegion(v) - case *plannercore.PhysicalIndexMergeReader: - return b.buildIndexMergeReader(v) - case *plannercore.SelectInto: - return b.buildSelectInto(v) - case *plannercore.AdminShowTelemetry: - return b.buildAdminShowTelemetry(v) - case *plannercore.AdminResetTelemetryID: - return b.buildAdminResetTelemetryID(v) - case *plannercore.PhysicalCTE: - return b.buildCTE(v) - case *plannercore.PhysicalCTETable: - return b.buildCTETableReader(v) - case *plannercore.CompactTable: - return b.buildCompactTable(v) - default: - if mp, ok := p.(MockPhysicalPlan); ok { - return mp.GetExecutor() - } - - b.err = exeerrors.ErrUnknownPlan.GenWithStack("Unknown Plan %T", p) - return nil - } -} - -func (b *executorBuilder) buildCancelDDLJobs(v *plannercore.CancelDDLJobs) exec.Executor { - e := &CancelDDLJobsExec{ - CommandDDLJobsExec: &CommandDDLJobsExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - jobIDs: v.JobIDs, - execute: ddl.CancelJobs, - }, - } - return e -} - -func (b *executorBuilder) buildPauseDDLJobs(v *plannercore.PauseDDLJobs) exec.Executor { - e := &PauseDDLJobsExec{ - CommandDDLJobsExec: &CommandDDLJobsExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - jobIDs: v.JobIDs, - execute: ddl.PauseJobs, - }, - } - return e -} - -func (b *executorBuilder) buildResumeDDLJobs(v *plannercore.ResumeDDLJobs) exec.Executor { - e := &ResumeDDLJobsExec{ - CommandDDLJobsExec: &CommandDDLJobsExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - jobIDs: v.JobIDs, - execute: ddl.ResumeJobs, - }, - } - return e -} - -func (b *executorBuilder) buildChange(v *plannercore.Change) exec.Executor { - return &ChangeExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - ChangeStmt: v.ChangeStmt, - } -} - -func (b *executorBuilder) buildShowNextRowID(v *plannercore.ShowNextRowID) exec.Executor { - e := &ShowNextRowIDExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - tblName: v.TableName, - } - return e -} - -func (b *executorBuilder) buildShowDDL(v *plannercore.ShowDDL) exec.Executor { - // We get Info here because for Executors that returns result set, - // next will be called after transaction has been committed. - // We need the transaction to get Info. - e := &ShowDDLExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - } - - var err error - ownerManager := domain.GetDomain(e.Ctx()).DDL().OwnerManager() - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - e.ddlOwnerID, err = ownerManager.GetOwnerID(ctx) - cancel() - if err != nil { - b.err = err - return nil - } - - session, err := e.GetSysSession() - if err != nil { - b.err = err - return nil - } - ddlInfo, err := ddl.GetDDLInfoWithNewTxn(session) - e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) - if err != nil { - b.err = err - return nil - } - e.ddlInfo = ddlInfo - e.selfID = ownerManager.ID() - return e -} - -func (b *executorBuilder) buildShowDDLJobs(v *plannercore.PhysicalShowDDLJobs) exec.Executor { - loc := b.ctx.GetSessionVars().Location() - ddlJobRetriever := DDLJobRetriever{TZLoc: loc} - e := &ShowDDLJobsExec{ - jobNumber: int(v.JobNumber), - is: b.is, - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - DDLJobRetriever: ddlJobRetriever, - } - return e -} - -func (b *executorBuilder) buildShowDDLJobQueries(v *plannercore.ShowDDLJobQueries) exec.Executor { - e := &ShowDDLJobQueriesExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - jobIDs: v.JobIDs, - } - return e -} - -func (b *executorBuilder) buildShowDDLJobQueriesWithRange(v *plannercore.ShowDDLJobQueriesWithRange) exec.Executor { - e := &ShowDDLJobQueriesWithRangeExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - offset: v.Offset, - limit: v.Limit, - } - return e -} - -func (b *executorBuilder) buildShowSlow(v *plannercore.ShowSlow) exec.Executor { - e := &ShowSlowExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - ShowSlow: v.ShowSlow, - } - return e -} - -// buildIndexLookUpChecker builds check information to IndexLookUpReader. -func buildIndexLookUpChecker(b *executorBuilder, p *plannercore.PhysicalIndexLookUpReader, - e *IndexLookUpExecutor) { - is := p.IndexPlans[0].(*plannercore.PhysicalIndexScan) - fullColLen := len(is.Index.Columns) + len(p.CommonHandleCols) - if !e.isCommonHandle() { - fullColLen++ - } - e.dagPB.OutputOffsets = make([]uint32, fullColLen) - for i := 0; i < fullColLen; i++ { - e.dagPB.OutputOffsets[i] = uint32(i) - } - - ts := p.TablePlans[0].(*plannercore.PhysicalTableScan) - e.handleIdx = ts.HandleIdx - - e.ranges = ranger.FullRange() - - tps := make([]*types.FieldType, 0, fullColLen) - for _, col := range is.Columns { - // tps is used to decode the index, we should use the element type of the array if any. - tps = append(tps, col.FieldType.ArrayType()) - } - - if !e.isCommonHandle() { - tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) - } - - e.checkIndexValue = &checkIndexValue{idxColTps: tps} - - colNames := make([]string, 0, len(is.IdxCols)) - for i := range is.IdxCols { - colNames = append(colNames, is.Columns[i].Name.L) - } - if cols, missingColOffset := table.FindColumns(e.table.Cols(), colNames, true); missingColOffset >= 0 { - b.err = plannercore.ErrUnknownColumn.GenWithStack("Unknown column %s", is.Columns[missingColOffset].Name.O) - } else { - e.idxTblCols = cols - } -} - -func (b *executorBuilder) buildCheckTable(v *plannercore.CheckTable) exec.Executor { - noMVIndexOrPrefixIndex := true - for _, idx := range v.IndexInfos { - if idx.MVIndex { - noMVIndexOrPrefixIndex = false - break - } - for _, col := range idx.Columns { - if col.Length != types.UnspecifiedLength { - noMVIndexOrPrefixIndex = false - break - } - } - if !noMVIndexOrPrefixIndex { - break - } - } - if b.ctx.GetSessionVars().FastCheckTable && noMVIndexOrPrefixIndex { - e := &FastCheckTableExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dbName: v.DBName, - table: v.Table, - indexInfos: v.IndexInfos, - is: b.is, - err: &atomic.Pointer[error]{}, - } - return e - } - - readerExecs := make([]*IndexLookUpExecutor, 0, len(v.IndexLookUpReaders)) - for _, readerPlan := range v.IndexLookUpReaders { - readerExec, err := buildNoRangeIndexLookUpReader(b, readerPlan) - if err != nil { - b.err = errors.Trace(err) - return nil - } - buildIndexLookUpChecker(b, readerPlan, readerExec) - - readerExecs = append(readerExecs, readerExec) - } - - e := &CheckTableExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dbName: v.DBName, - table: v.Table, - indexInfos: v.IndexInfos, - is: b.is, - srcs: readerExecs, - exitCh: make(chan struct{}), - retCh: make(chan error, len(readerExecs)), - checkIndex: v.CheckIndex, - } - return e -} - -func buildIdxColsConcatHandleCols(tblInfo *model.TableInfo, indexInfo *model.IndexInfo, hasGenedCol bool) []*model.ColumnInfo { - var pkCols []*model.IndexColumn - if tblInfo.IsCommonHandle { - pkIdx := tables.FindPrimaryIndex(tblInfo) - pkCols = pkIdx.Columns - } - - columns := make([]*model.ColumnInfo, 0, len(indexInfo.Columns)+len(pkCols)) - if hasGenedCol { - columns = tblInfo.Columns - } else { - for _, idxCol := range indexInfo.Columns { - if tblInfo.PKIsHandle && tblInfo.GetPkColInfo().Offset == idxCol.Offset { - continue - } - columns = append(columns, tblInfo.Columns[idxCol.Offset]) - } - } - - if tblInfo.IsCommonHandle { - for _, c := range pkCols { - columns = append(columns, tblInfo.Columns[c.Offset]) - } - return columns - } - if tblInfo.PKIsHandle { - columns = append(columns, tblInfo.Columns[tblInfo.GetPkColInfo().Offset]) - return columns - } - handleOffset := len(columns) - handleColsInfo := &model.ColumnInfo{ - ID: model.ExtraHandleID, - Name: model.ExtraHandleName, - Offset: handleOffset, - } - handleColsInfo.FieldType = *types.NewFieldType(mysql.TypeLonglong) - columns = append(columns, handleColsInfo) - return columns -} - -func (b *executorBuilder) buildRecoverIndex(v *plannercore.RecoverIndex) exec.Executor { - tblInfo := v.Table.TableInfo - t, err := b.is.TableByName(v.Table.Schema, tblInfo.Name) - if err != nil { - b.err = err - return nil - } - idxName := strings.ToLower(v.IndexName) - index := tables.GetWritableIndexByName(idxName, t) - if index == nil { - b.err = errors.Errorf("secondary index `%v` is not found in table `%v`", v.IndexName, v.Table.Name.O) - return nil - } - var hasGenedCol bool - for _, iCol := range index.Meta().Columns { - if tblInfo.Columns[iCol.Offset].IsGenerated() { - hasGenedCol = true - } - } - cols := buildIdxColsConcatHandleCols(tblInfo, index.Meta(), hasGenedCol) - e := &RecoverIndexExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - columns: cols, - containsGenedCol: hasGenedCol, - index: index, - table: t, - physicalID: t.Meta().ID, - } - sessCtx := e.Ctx().GetSessionVars().StmtCtx - e.handleCols = buildHandleColsForExec(sessCtx, tblInfo, index.Meta(), e.columns) - return e -} - -func buildHandleColsForExec(sctx *stmtctx.StatementContext, tblInfo *model.TableInfo, - idxInfo *model.IndexInfo, allColInfo []*model.ColumnInfo) plannercore.HandleCols { - if !tblInfo.IsCommonHandle { - extraColPos := len(allColInfo) - 1 - intCol := &expression.Column{ - Index: extraColPos, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - return plannercore.NewIntHandleCols(intCol) - } - tblCols := make([]*expression.Column, len(tblInfo.Columns)) - for i := 0; i < len(tblInfo.Columns); i++ { - c := tblInfo.Columns[i] - tblCols[i] = &expression.Column{ - RetType: &c.FieldType, - ID: c.ID, - } - } - pkIdx := tables.FindPrimaryIndex(tblInfo) - for i, c := range pkIdx.Columns { - tblCols[c.Offset].Index = len(idxInfo.Columns) + i - } - return plannercore.NewCommonHandleCols(sctx, tblInfo, pkIdx, tblCols) -} - -func (b *executorBuilder) buildCleanupIndex(v *plannercore.CleanupIndex) exec.Executor { - tblInfo := v.Table.TableInfo - t, err := b.is.TableByName(v.Table.Schema, tblInfo.Name) - if err != nil { - b.err = err - return nil - } - idxName := strings.ToLower(v.IndexName) - var index table.Index - for _, idx := range t.Indices() { - if idx.Meta().State != model.StatePublic { - continue - } - if idxName == idx.Meta().Name.L { - index = idx - break - } - } - - if index == nil { - b.err = errors.Errorf("secondary index `%v` is not found in table `%v`", v.IndexName, v.Table.Name.O) - return nil - } - e := &CleanupIndexExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - columns: buildIdxColsConcatHandleCols(tblInfo, index.Meta(), false), - index: index, - table: t, - physicalID: t.Meta().ID, - batchSize: 20000, - } - sessCtx := e.Ctx().GetSessionVars().StmtCtx - e.handleCols = buildHandleColsForExec(sessCtx, tblInfo, index.Meta(), e.columns) - return e -} - -func (b *executorBuilder) buildCheckIndexRange(v *plannercore.CheckIndexRange) exec.Executor { - tb, err := b.is.TableByName(v.Table.Schema, v.Table.Name) - if err != nil { - b.err = err - return nil - } - e := &CheckIndexRangeExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - handleRanges: v.HandleRanges, - table: tb.Meta(), - is: b.is, - } - idxName := strings.ToLower(v.IndexName) - for _, idx := range tb.Indices() { - if idx.Meta().Name.L == idxName { - e.index = idx.Meta() - e.startKey = make([]types.Datum, len(e.index.Columns)) - break - } - } - return e -} - -func (b *executorBuilder) buildChecksumTable(v *plannercore.ChecksumTable) exec.Executor { - e := &ChecksumTableExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - tables: make(map[int64]*checksumContext), - done: false, - } - startTs, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - for _, t := range v.Tables { - e.tables[t.TableInfo.ID] = newChecksumContext(t.DBInfo, t.TableInfo, startTs) - } - return e -} - -func (b *executorBuilder) buildReloadExprPushdownBlacklist(_ *plannercore.ReloadExprPushdownBlacklist) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, nil, 0) - return &ReloadExprPushdownBlacklistExec{base} -} - -func (b *executorBuilder) buildReloadOptRuleBlacklist(_ *plannercore.ReloadOptRuleBlacklist) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, nil, 0) - return &ReloadOptRuleBlacklistExec{BaseExecutor: base} -} - -func (b *executorBuilder) buildAdminPlugins(v *plannercore.AdminPlugins) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, nil, 0) - return &AdminPluginsExec{BaseExecutor: base, Action: v.Action, Plugins: v.Plugins} -} - -func (b *executorBuilder) buildDeallocate(v *plannercore.Deallocate) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, nil, v.ID()) - base.SetInitCap(chunk.ZeroCapacity) - e := &DeallocateExec{ - BaseExecutor: base, - Name: v.Name, - } - return e -} - -func (b *executorBuilder) buildSelectLock(v *plannercore.PhysicalLock) exec.Executor { - if !b.inSelectLockStmt { - b.inSelectLockStmt = true - defer func() { b.inSelectLockStmt = false }() - } - b.hasLock = true - if b.err = b.updateForUpdateTS(); b.err != nil { - return nil - } - - src := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - if !b.ctx.GetSessionVars().InTxn() { - // Locking of rows for update using SELECT FOR UPDATE only applies when autocommit - // is disabled (either by beginning transaction with START TRANSACTION or by setting - // autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked. - // See https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html - return src - } - e := &SelectLockExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), - Lock: v.Lock, - tblID2Handle: v.TblID2Handle, - tblID2PhysTblIDCol: v.TblID2PhysTblIDCol, - } - - // filter out temporary tables because they do not store any record in tikv and should not write any lock - is := e.Ctx().GetInfoSchema().(infoschema.InfoSchema) - for tblID := range e.tblID2Handle { - tblInfo, ok := is.TableByID(tblID) - if !ok { - b.err = errors.Errorf("Can not get table %d", tblID) - } - - if tblInfo.Meta().TempTableType != model.TempTableNone { - delete(e.tblID2Handle, tblID) - } - } - - return e -} - -func (b *executorBuilder) buildLimit(v *plannercore.PhysicalLimit) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - n := int(mathutil.Min(v.Count, uint64(b.ctx.GetSessionVars().MaxChunkSize))) - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) - base.SetInitCap(n) - e := &LimitExec{ - BaseExecutor: base, - begin: v.Offset, - end: v.Offset + v.Count, - } - - childUsedSchema := markChildrenUsedCols(v.Schema().Columns, v.Children()[0].Schema())[0] - e.columnIdxsUsedByChild = make([]int, 0, len(childUsedSchema)) - for i, used := range childUsedSchema { - if used { - e.columnIdxsUsedByChild = append(e.columnIdxsUsedByChild, i) - } - } - if len(e.columnIdxsUsedByChild) == len(childUsedSchema) { - e.columnIdxsUsedByChild = nil // indicates that all columns are used. LimitExec will improve performance for this condition. - } - return e -} - -func (b *executorBuilder) buildPrepare(v *plannercore.Prepare) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(chunk.ZeroCapacity) - return &PrepareExec{ - BaseExecutor: base, - name: v.Name, - sqlText: v.SQLText, - } -} - -func (b *executorBuilder) buildExecute(v *plannercore.Execute) exec.Executor { - e := &ExecuteExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - is: b.is, - name: v.Name, - usingVars: v.Params, - stmt: v.Stmt, - plan: v.Plan, - outputNames: v.OutputNames(), - } - - failpoint.Inject("assertExecutePrepareStatementStalenessOption", func(val failpoint.Value) { - vs := strings.Split(val.(string), "_") - assertTS, assertReadReplicaScope := vs[0], vs[1] - staleread.AssertStmtStaleness(b.ctx, true) - ts, err := sessiontxn.GetTxnManager(b.ctx).GetStmtReadTS() - if err != nil { - panic(e) - } - - if strconv.FormatUint(ts, 10) != assertTS || - assertReadReplicaScope != b.readReplicaScope { - panic("execute prepare statement have wrong staleness option") - } - }) - - return e -} - -func (b *executorBuilder) buildShow(v *plannercore.PhysicalShow) exec.Executor { - e := &ShowExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - Tp: v.Tp, - CountWarningsOrErrors: v.CountWarningsOrErrors, - DBName: model.NewCIStr(v.DBName), - Table: v.Table, - Partition: v.Partition, - Column: v.Column, - IndexName: v.IndexName, - ResourceGroupName: model.NewCIStr(v.ResourceGroupName), - Flag: v.Flag, - Roles: v.Roles, - User: v.User, - is: b.is, - Full: v.Full, - IfNotExists: v.IfNotExists, - GlobalScope: v.GlobalScope, - Extended: v.Extended, - Extractor: v.Extractor, - ImportJobID: v.ImportJobID, - } - if e.Tp == ast.ShowMasterStatus { - // show master status need start ts. - if _, err := e.Ctx().Txn(true); err != nil { - b.err = err - } - } - return e -} - -func (b *executorBuilder) buildSimple(v *plannercore.Simple) exec.Executor { - switch s := v.Statement.(type) { - case *ast.GrantStmt: - return b.buildGrant(s) - case *ast.RevokeStmt: - return b.buildRevoke(s) - case *ast.BRIEStmt: - return b.buildBRIE(s, v.Schema()) - case *ast.CreateUserStmt, *ast.AlterUserStmt: - var lockOptions []*ast.PasswordOrLockOption - if b.Ti.AccountLockTelemetry == nil { - b.Ti.AccountLockTelemetry = &AccountLockTelemetryInfo{} - } - b.Ti.AccountLockTelemetry.CreateOrAlterUser++ - if stmt, ok := v.Statement.(*ast.CreateUserStmt); ok { - lockOptions = stmt.PasswordOrLockOptions - } else if stmt, ok := v.Statement.(*ast.AlterUserStmt); ok { - lockOptions = stmt.PasswordOrLockOptions - } - if len(lockOptions) > 0 { - // Multiple lock options are supported for the parser, but only the last one option takes effect. - for i := len(lockOptions) - 1; i >= 0; i-- { - if lockOptions[i].Type == ast.Lock { - b.Ti.AccountLockTelemetry.LockUser++ - break - } else if lockOptions[i].Type == ast.Unlock { - b.Ti.AccountLockTelemetry.UnlockUser++ - break - } - } - } - case *ast.CalibrateResourceStmt: - return &calibrateresource.Executor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), 0), - WorkloadType: s.Tp, - OptionList: s.DynamicCalibrateResourceOptionList, - } - case *ast.AddQueryWatchStmt: - return &querywatch.AddExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), 0), - QueryWatchOptionList: s.QueryWatchOptionList, - } - case *ast.LoadDataActionStmt: - return &LoadDataActionExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), - tp: s.Tp, - jobID: s.JobID, - } - case *ast.ImportIntoActionStmt: - return &ImportIntoActionExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), - tp: s.Tp, - jobID: s.JobID, - } - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(chunk.ZeroCapacity) - e := &SimpleExec{ - BaseExecutor: base, - Statement: v.Statement, - IsFromRemote: v.IsFromRemote, - is: b.is, - staleTxnStartTS: v.StaleTxnStartTS, - } - return e -} - -func (b *executorBuilder) buildSet(v *plannercore.Set) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(chunk.ZeroCapacity) - e := &SetExecutor{ - BaseExecutor: base, - vars: v.VarAssigns, - } - return e -} - -func (b *executorBuilder) buildSetConfig(v *plannercore.SetConfig) exec.Executor { - return &SetConfigExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - p: v, - } -} - -func (b *executorBuilder) buildInsert(v *plannercore.Insert) exec.Executor { - b.inInsertStmt = true - if b.err = b.updateForUpdateTS(); b.err != nil { - return nil - } - - selectExec := b.build(v.SelectPlan) - if b.err != nil { - return nil - } - var baseExec exec.BaseExecutor - if selectExec != nil { - baseExec = exec.NewBaseExecutor(b.ctx, nil, v.ID(), selectExec) - } else { - baseExec = exec.NewBaseExecutor(b.ctx, nil, v.ID()) - } - baseExec.SetInitCap(chunk.ZeroCapacity) - - ivs := &InsertValues{ - BaseExecutor: baseExec, - Table: v.Table, - Columns: v.Columns, - Lists: v.Lists, - GenExprs: v.GenCols.Exprs, - allAssignmentsAreConstant: v.AllAssignmentsAreConstant, - hasRefCols: v.NeedFillDefaultValue, - SelectExec: selectExec, - rowLen: v.RowLen, - } - err := ivs.initInsertColumns() - if err != nil { - b.err = err - return nil - } - ivs.fkChecks, b.err = buildFKCheckExecs(b.ctx, ivs.Table, v.FKChecks) - if b.err != nil { - return nil - } - ivs.fkCascades, b.err = b.buildFKCascadeExecs(ivs.Table, v.FKCascades) - if b.err != nil { - return nil - } - - if v.IsReplace { - return b.buildReplace(ivs) - } - insert := &InsertExec{ - InsertValues: ivs, - OnDuplicate: append(v.OnDuplicate, v.GenCols.OnDuplicates...), - } - return insert -} - -func (b *executorBuilder) buildImportInto(v *plannercore.ImportInto) exec.Executor { - tbl, ok := b.is.TableByID(v.Table.TableInfo.ID) - if !ok { - b.err = errors.Errorf("Can not get table %d", v.Table.TableInfo.ID) - return nil - } - if !tbl.Meta().IsBaseTable() { - b.err = plannercore.ErrNonUpdatableTable.GenWithStackByArgs(tbl.Meta().Name.O, "LOAD") - return nil - } - - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - exec, err := newImportIntoExec(base, b.ctx, v, tbl) - if err != nil { - b.err = err - return nil - } - - return exec -} - -func (b *executorBuilder) buildLoadData(v *plannercore.LoadData) exec.Executor { - tbl, ok := b.is.TableByID(v.Table.TableInfo.ID) - if !ok { - b.err = errors.Errorf("Can not get table %d", v.Table.TableInfo.ID) - return nil - } - if !tbl.Meta().IsBaseTable() { - b.err = plannercore.ErrNonUpdatableTable.GenWithStackByArgs(tbl.Meta().Name.O, "LOAD") - return nil - } - - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - worker, err := NewLoadDataWorker(b.ctx, v, tbl) - if err != nil { - b.err = err - return nil - } - - return &LoadDataExec{ - BaseExecutor: base, - loadDataWorker: worker, - FileLocRef: v.FileLocRef, - } -} - -func (b *executorBuilder) buildLoadStats(v *plannercore.LoadStats) exec.Executor { - e := &LoadStatsExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - info: &LoadStatsInfo{v.Path, b.ctx}, - } - return e -} - -func (b *executorBuilder) buildLockStats(v *plannercore.LockStats) exec.Executor { - e := &lockstats.LockExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - Tables: v.Tables, - } - return e -} - -func (b *executorBuilder) buildUnlockStats(v *plannercore.UnlockStats) exec.Executor { - e := &lockstats.UnlockExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - Tables: v.Tables, - } - return e -} - -func (b *executorBuilder) buildIndexAdvise(v *plannercore.IndexAdvise) exec.Executor { - e := &IndexAdviseExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - IsLocal: v.IsLocal, - indexAdviseInfo: &IndexAdviseInfo{ - Path: v.Path, - MaxMinutes: v.MaxMinutes, - MaxIndexNum: v.MaxIndexNum, - LineFieldsInfo: v.LineFieldsInfo, - Ctx: b.ctx, - }, - } - return e -} - -func (b *executorBuilder) buildPlanReplayer(v *plannercore.PlanReplayer) exec.Executor { - if v.Load { - e := &PlanReplayerLoadExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - info: &PlanReplayerLoadInfo{Path: v.File, Ctx: b.ctx}, - } - return e - } - if v.Capture { - e := &PlanReplayerExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - CaptureInfo: &PlanReplayerCaptureInfo{ - SQLDigest: v.SQLDigest, - PlanDigest: v.PlanDigest, - }, - } - return e - } - if v.Remove { - e := &PlanReplayerExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), - CaptureInfo: &PlanReplayerCaptureInfo{ - SQLDigest: v.SQLDigest, - PlanDigest: v.PlanDigest, - Remove: true, - }, - } - return e - } - - e := &PlanReplayerExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - DumpInfo: &PlanReplayerDumpInfo{ - Analyze: v.Analyze, - Path: v.File, - ctx: b.ctx, - HistoricalStatsTS: v.HistoricalStatsTS, - }, - } - if v.ExecStmt != nil { - e.DumpInfo.ExecStmts = []ast.StmtNode{v.ExecStmt} - } else { - e.BaseExecutor = exec.NewBaseExecutor(b.ctx, nil, v.ID()) - } - return e -} - -func (*executorBuilder) buildReplace(vals *InsertValues) exec.Executor { - replaceExec := &ReplaceExec{ - InsertValues: vals, - } - return replaceExec -} - -func (b *executorBuilder) buildGrant(grant *ast.GrantStmt) exec.Executor { - e := &GrantExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), - Privs: grant.Privs, - ObjectType: grant.ObjectType, - Level: grant.Level, - Users: grant.Users, - WithGrant: grant.WithGrant, - AuthTokenOrTLSOptions: grant.AuthTokenOrTLSOptions, - is: b.is, - } - return e -} - -func (b *executorBuilder) buildRevoke(revoke *ast.RevokeStmt) exec.Executor { - e := &RevokeExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), - ctx: b.ctx, - Privs: revoke.Privs, - ObjectType: revoke.ObjectType, - Level: revoke.Level, - Users: revoke.Users, - is: b.is, - } - return e -} - -func (b *executorBuilder) setTelemetryInfo(v *plannercore.DDL) { - if v == nil || b.Ti == nil { - return - } - switch s := v.Statement.(type) { - case *ast.AlterTableStmt: - if len(s.Specs) > 1 { - b.Ti.UseMultiSchemaChange = true - } - for _, spec := range s.Specs { - switch spec.Tp { - case ast.AlterTableDropFirstPartition: - if b.Ti.PartitionTelemetry == nil { - b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} - } - b.Ti.PartitionTelemetry.UseDropIntervalPartition = true - case ast.AlterTableAddLastPartition: - if b.Ti.PartitionTelemetry == nil { - b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} - } - b.Ti.PartitionTelemetry.UseAddIntervalPartition = true - case ast.AlterTableExchangePartition: - b.Ti.UseExchangePartition = true - case ast.AlterTableReorganizePartition: - if b.Ti.PartitionTelemetry == nil { - b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} - } - b.Ti.PartitionTelemetry.UseReorganizePartition = true - } - } - case *ast.CreateTableStmt: - if s.Partition == nil || strings.EqualFold(b.ctx.GetSessionVars().EnableTablePartition, "OFF") { - break - } - - p := s.Partition - if b.Ti.PartitionTelemetry == nil { - b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} - } - b.Ti.PartitionTelemetry.TablePartitionMaxPartitionsNum = mathutil.Max(p.Num, uint64(len(p.Definitions))) - b.Ti.PartitionTelemetry.UseTablePartition = true - - switch p.Tp { - case model.PartitionTypeRange: - if p.Sub == nil { - if len(p.ColumnNames) > 0 { - b.Ti.PartitionTelemetry.UseTablePartitionRangeColumns = true - if len(p.ColumnNames) > 1 { - b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt1 = true - } - if len(p.ColumnNames) > 2 { - b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt2 = true - } - if len(p.ColumnNames) > 3 { - b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt3 = true - } - } else { - b.Ti.PartitionTelemetry.UseTablePartitionRange = true - } - if p.Interval != nil { - b.Ti.PartitionTelemetry.UseCreateIntervalPartition = true - } - } - case model.PartitionTypeHash: - if p.Sub == nil { - b.Ti.PartitionTelemetry.UseTablePartitionHash = true - } - case model.PartitionTypeList: - enable := b.ctx.GetSessionVars().EnableListTablePartition - if p.Sub == nil && enable { - if len(p.ColumnNames) > 0 { - b.Ti.PartitionTelemetry.UseTablePartitionListColumns = true - } else { - b.Ti.PartitionTelemetry.UseTablePartitionList = true - } - } - } - case *ast.FlashBackToTimestampStmt: - b.Ti.UseFlashbackToCluster = true - } -} - -func (b *executorBuilder) buildDDL(v *plannercore.DDL) exec.Executor { - b.setTelemetryInfo(v) - - e := &DDLExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - stmt: v.Statement, - is: b.is, - tempTableDDL: temptable.GetTemporaryTableDDL(b.ctx), - } - return e -} - -// buildTrace builds a TraceExec for future executing. This method will be called -// at build(). -func (b *executorBuilder) buildTrace(v *plannercore.Trace) exec.Executor { - t := &TraceExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - stmtNode: v.StmtNode, - builder: b, - format: v.Format, - - optimizerTrace: v.OptimizerTrace, - optimizerTraceTarget: v.OptimizerTraceTarget, - } - if t.format == plannercore.TraceFormatLog && !t.optimizerTrace { - return &SortExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), t), - ByItems: []*plannerutil.ByItems{ - {Expr: &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeTimestamp), - }}, - }, - schema: v.Schema(), - } - } - return t -} - -// buildExplain builds a explain executor. `e.rows` collects final result to `ExplainExec`. -func (b *executorBuilder) buildExplain(v *plannercore.Explain) exec.Executor { - explainExec := &ExplainExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - explain: v, - } - if v.Analyze { - if b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl == nil { - b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(nil) - } - // If the resource group name is not empty, we could collect and display the RU - // runtime stats for analyze executor. - resourceGroupName := b.ctx.GetSessionVars().ResourceGroupName - // Try to register the RU runtime stats for analyze executor. - if store, ok := b.ctx.GetStore().(interface { - CreateRURuntimeStats(uint64) *clientutil.RURuntimeStats - }); len(resourceGroupName) > 0 && ok { - // StartTS will be used to identify this SQL, so that the runtime stats could - // aggregate the RU stats beneath the KV storage client. - startTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - explainExec.ruRuntimeStats = store.CreateRURuntimeStats(startTS) - } - explainExec.analyzeExec = b.build(v.TargetPlan) - } - return explainExec -} - -func (b *executorBuilder) buildSelectInto(v *plannercore.SelectInto) exec.Executor { - child := b.build(v.TargetPlan) - if b.err != nil { - return nil - } - return &SelectIntoExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), child), - intoOpt: v.IntoOpt, - LineFieldsInfo: v.LineFieldsInfo, - } -} - -func (b *executorBuilder) buildUnionScanExec(v *plannercore.PhysicalUnionScan) exec.Executor { - oriEncounterUnionScan := b.encounterUnionScan - b.encounterUnionScan = true - defer func() { - b.encounterUnionScan = oriEncounterUnionScan - }() - reader := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - - return b.buildUnionScanFromReader(reader, v) -} - -// buildUnionScanFromReader builds union scan executor from child executor. -// Note that this function may be called by inner workers of index lookup join concurrently. -// Be careful to avoid data race. -func (b *executorBuilder) buildUnionScanFromReader(reader exec.Executor, v *plannercore.PhysicalUnionScan) exec.Executor { - // If reader is union, it means a partition table and we should transfer as above. - if x, ok := reader.(*UnionExec); ok { - for i, child := range x.AllChildren() { - x.SetChildren(i, b.buildUnionScanFromReader(child, v)) - if b.err != nil { - return nil - } - } - return x - } - us := &UnionScanExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), reader)} - // Get the handle column index of the below Plan. - us.handleCols = v.HandleCols - us.mutableRow = chunk.MutRowFromTypes(exec.RetTypes(us)) - - // If the push-downed condition contains virtual column, we may build a selection upon reader - originReader := reader - if sel, ok := reader.(*SelectionExec); ok { - reader = sel.Children(0) - } - - us.collators = make([]collate.Collator, 0, len(us.columns)) - for _, tp := range exec.RetTypes(us) { - us.collators = append(us.collators, collate.GetCollator(tp.GetCollate())) - } - - startTS, err := b.getSnapshotTS() - sessionVars := b.ctx.GetSessionVars() - if err != nil { - b.err = err - return nil - } - - switch x := reader.(type) { - case *MPPGather: - us.desc = false - us.keepOrder = false - us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) - us.columns = x.columns - us.table = x.table - us.virtualColumnIndex = x.virtualColumnIndex - us.handleCachedTable(b, x, sessionVars, startTS) - case *TableReaderExecutor: - us.desc = x.desc - us.keepOrder = x.keepOrder - us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) - us.columns = x.columns - us.table = x.table - us.virtualColumnIndex = x.virtualColumnIndex - us.handleCachedTable(b, x, sessionVars, startTS) - case *IndexReaderExecutor: - us.desc = x.desc - us.keepOrder = x.keepOrder - for _, ic := range x.index.Columns { - for i, col := range x.columns { - if col.Name.L == ic.Name.L { - us.usedIndex = append(us.usedIndex, i) - break - } - } - } - us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) - us.columns = x.columns - us.table = x.table - us.handleCachedTable(b, x, sessionVars, startTS) - case *IndexLookUpExecutor: - us.desc = x.desc - us.keepOrder = x.keepOrder - for _, ic := range x.index.Columns { - for i, col := range x.columns { - if col.Name.L == ic.Name.L { - us.usedIndex = append(us.usedIndex, i) - break - } - } - } - us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) - us.columns = x.columns - us.table = x.table - us.virtualColumnIndex = buildVirtualColumnIndex(us.Schema(), us.columns) - us.handleCachedTable(b, x, sessionVars, startTS) - case *IndexMergeReaderExecutor: - if len(x.byItems) != 0 { - us.keepOrder = x.keepOrder - us.desc = x.byItems[0].Desc - for _, item := range x.byItems { - c, ok := item.Expr.(*expression.Column) - if !ok { - b.err = errors.Errorf("Not support non-column in orderBy pushed down") - return nil - } - for i, col := range x.columns { - if col.ID == c.ID { - us.usedIndex = append(us.usedIndex, i) - break - } - } - } - } - us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) - us.columns = x.columns - us.table = x.table - us.virtualColumnIndex = buildVirtualColumnIndex(us.Schema(), us.columns) - default: - // The mem table will not be written by sql directly, so we can omit the union scan to avoid err reporting. - return originReader - } - return us -} - -type bypassDataSourceExecutor interface { - dataSourceExecutor - setDummy() -} - -func (us *UnionScanExec) handleCachedTable(b *executorBuilder, x bypassDataSourceExecutor, vars *variable.SessionVars, startTS uint64) { - tbl := x.Table() - if tbl.Meta().TableCacheStatusType == model.TableCacheStatusEnable { - cachedTable := tbl.(table.CachedTable) - // Determine whether the cache can be used. - leaseDuration := time.Duration(variable.TableCacheLease.Load()) * time.Second - cacheData, loading := cachedTable.TryReadFromCache(startTS, leaseDuration) - if cacheData != nil { - vars.StmtCtx.ReadFromTableCache = true - x.setDummy() - us.cacheTable = cacheData - } else if loading { - return - } else { - if !b.inUpdateStmt && !b.inDeleteStmt && !b.inInsertStmt && !vars.StmtCtx.InExplainStmt { - store := b.ctx.GetStore() - cachedTable.UpdateLockForRead(context.Background(), store, startTS, leaseDuration) - } - } - } -} - -// buildMergeJoin builds MergeJoinExec executor. -func (b *executorBuilder) buildMergeJoin(v *plannercore.PhysicalMergeJoin) exec.Executor { - leftExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - - rightExec := b.build(v.Children()[1]) - if b.err != nil { - return nil - } - - defaultValues := v.DefaultValues - if defaultValues == nil { - if v.JoinType == plannercore.RightOuterJoin { - defaultValues = make([]types.Datum, leftExec.Schema().Len()) - } else { - defaultValues = make([]types.Datum, rightExec.Schema().Len()) - } - } - - colsFromChildren := v.Schema().Columns - if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { - colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] - } - - e := &MergeJoinExec{ - stmtCtx: b.ctx.GetSessionVars().StmtCtx, - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), leftExec, rightExec), - compareFuncs: v.CompareFuncs, - joiner: newJoiner( - b.ctx, - v.JoinType, - v.JoinType == plannercore.RightOuterJoin, - defaultValues, - v.OtherConditions, - exec.RetTypes(leftExec), - exec.RetTypes(rightExec), - markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()), - false, - ), - isOuterJoin: v.JoinType.IsOuterJoin(), - desc: v.Desc, - } - - leftTable := &mergeJoinTable{ - childIndex: 0, - joinKeys: v.LeftJoinKeys, - filters: v.LeftConditions, - } - rightTable := &mergeJoinTable{ - childIndex: 1, - joinKeys: v.RightJoinKeys, - filters: v.RightConditions, - } - - if v.JoinType == plannercore.RightOuterJoin { - e.innerTable = leftTable - e.outerTable = rightTable - } else { - e.innerTable = rightTable - e.outerTable = leftTable - } - e.innerTable.isInner = true - - // optimizer should guarantee that filters on inner table are pushed down - // to tikv or extracted to a Selection. - if len(e.innerTable.filters) != 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "merge join's inner filter should be empty.") - return nil - } - - executor_metrics.ExecutorCounterMergeJoinExec.Inc() - return e -} - -func (b *executorBuilder) buildHashJoin(v *plannercore.PhysicalHashJoin) exec.Executor { - leftExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - - rightExec := b.build(v.Children()[1]) - if b.err != nil { - return nil - } - - e := &HashJoinExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), leftExec, rightExec), - probeSideTupleFetcher: &probeSideTupleFetcher{}, - probeWorkers: make([]*probeWorker, v.Concurrency), - buildWorker: &buildWorker{}, - hashJoinCtx: &hashJoinCtx{ - sessCtx: b.ctx, - isOuterJoin: v.JoinType.IsOuterJoin(), - useOuterToBuild: v.UseOuterToBuild, - joinType: v.JoinType, - concurrency: v.Concurrency, - }, - } - e.hashJoinCtx.allocPool = e.AllocPool - defaultValues := v.DefaultValues - lhsTypes, rhsTypes := exec.RetTypes(leftExec), exec.RetTypes(rightExec) - if v.InnerChildIdx == 1 { - if len(v.RightConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } else { - if len(v.LeftConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } - - leftIsBuildSide := true - - e.isNullEQ = v.IsNullEQ - var probeKeys, probeNAKeys, buildKeys, buildNAKeys []*expression.Column - var buildSideExec exec.Executor - if v.UseOuterToBuild { - // update the buildSideEstCount due to changing the build side - if v.InnerChildIdx == 1 { - buildSideExec, buildKeys, buildNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys - e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys - e.outerFilter = v.LeftConditions - } else { - buildSideExec, buildKeys, buildNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys - e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys - e.outerFilter = v.RightConditions - leftIsBuildSide = false - } - if defaultValues == nil { - defaultValues = make([]types.Datum, e.probeSideTupleFetcher.probeSideExec.Schema().Len()) - } - } else { - if v.InnerChildIdx == 0 { - buildSideExec, buildKeys, buildNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys - e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys - e.outerFilter = v.RightConditions - } else { - buildSideExec, buildKeys, buildNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys - e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys - e.outerFilter = v.LeftConditions - leftIsBuildSide = false - } - if defaultValues == nil { - defaultValues = make([]types.Datum, buildSideExec.Schema().Len()) - } - } - probeKeyColIdx := make([]int, len(probeKeys)) - probeNAKeColIdx := make([]int, len(probeNAKeys)) - buildKeyColIdx := make([]int, len(buildKeys)) - buildNAKeyColIdx := make([]int, len(buildNAKeys)) - for i := range buildKeys { - buildKeyColIdx[i] = buildKeys[i].Index - } - for i := range buildNAKeys { - buildNAKeyColIdx[i] = buildNAKeys[i].Index - } - for i := range probeKeys { - probeKeyColIdx[i] = probeKeys[i].Index - } - for i := range probeNAKeys { - probeNAKeColIdx[i] = probeNAKeys[i].Index - } - isNAJoin := len(v.LeftNAJoinKeys) > 0 - colsFromChildren := v.Schema().Columns - if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { - colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] - } - childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) - for i := uint(0); i < e.concurrency; i++ { - e.probeWorkers[i] = &probeWorker{ - hashJoinCtx: e.hashJoinCtx, - workerID: i, - joiner: newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, lhsTypes, rhsTypes, childrenUsedSchema, isNAJoin), - probeKeyColIdx: probeKeyColIdx, - probeNAKeyColIdx: probeNAKeColIdx, - } - } - e.buildWorker.buildKeyColIdx, e.buildWorker.buildNAKeyColIdx, e.buildWorker.buildSideExec, e.buildWorker.hashJoinCtx = buildKeyColIdx, buildNAKeyColIdx, buildSideExec, e.hashJoinCtx - e.hashJoinCtx.isNullAware = isNAJoin - executor_metrics.ExecutorCountHashJoinExec.Inc() - - // We should use JoinKey to construct the type information using by hashing, instead of using the child's schema directly. - // When a hybrid type column is hashed multiple times, we need to distinguish what field types are used. - // For example, the condition `enum = int and enum = string`, we should use ETInt to hash the first column, - // and use ETString to hash the second column, although they may be the same column. - leftExecTypes, rightExecTypes := exec.RetTypes(leftExec), exec.RetTypes(rightExec) - leftTypes, rightTypes := make([]*types.FieldType, 0, len(v.LeftJoinKeys)+len(v.LeftNAJoinKeys)), make([]*types.FieldType, 0, len(v.RightJoinKeys)+len(v.RightNAJoinKeys)) - // set left types and right types for joiner. - for i, col := range v.LeftJoinKeys { - leftTypes = append(leftTypes, leftExecTypes[col.Index].Clone()) - leftTypes[i].SetFlag(col.RetType.GetFlag()) - } - offset := len(v.LeftJoinKeys) - for i, col := range v.LeftNAJoinKeys { - leftTypes = append(leftTypes, leftExecTypes[col.Index].Clone()) - leftTypes[i+offset].SetFlag(col.RetType.GetFlag()) - } - for i, col := range v.RightJoinKeys { - rightTypes = append(rightTypes, rightExecTypes[col.Index].Clone()) - rightTypes[i].SetFlag(col.RetType.GetFlag()) - } - offset = len(v.RightJoinKeys) - for i, col := range v.RightNAJoinKeys { - rightTypes = append(rightTypes, rightExecTypes[col.Index].Clone()) - rightTypes[i+offset].SetFlag(col.RetType.GetFlag()) - } - - // consider collations - for i := range v.EqualConditions { - chs, coll := v.EqualConditions[i].CharsetAndCollation() - leftTypes[i].SetCharset(chs) - leftTypes[i].SetCollate(coll) - rightTypes[i].SetCharset(chs) - rightTypes[i].SetCollate(coll) - } - offset = len(v.EqualConditions) - for i := range v.NAEqualConditions { - chs, coll := v.NAEqualConditions[i].CharsetAndCollation() - leftTypes[i+offset].SetCharset(chs) - leftTypes[i+offset].SetCollate(coll) - rightTypes[i+offset].SetCharset(chs) - rightTypes[i+offset].SetCollate(coll) - } - if leftIsBuildSide { - e.buildTypes, e.probeTypes = leftTypes, rightTypes - } else { - e.buildTypes, e.probeTypes = rightTypes, leftTypes - } - return e -} - -func (b *executorBuilder) buildHashAgg(v *plannercore.PhysicalHashAgg) exec.Executor { - src := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - sessionVars := b.ctx.GetSessionVars() - e := &aggregate.HashAggExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), - Sc: sessionVars.StmtCtx, - PartialAggFuncs: make([]aggfuncs.AggFunc, 0, len(v.AggFuncs)), - GroupByItems: v.GroupByItems, - } - // We take `create table t(a int, b int);` as example. - // - // 1. If all the aggregation functions are FIRST_ROW, we do not need to set the defaultVal for them: - // e.g. - // mysql> select distinct a, b from t; - // 0 rows in set (0.00 sec) - // - // 2. If there exists group by items, we do not need to set the defaultVal for them either: - // e.g. - // mysql> select avg(a) from t group by b; - // Empty set (0.00 sec) - // - // mysql> select avg(a) from t group by a; - // +--------+ - // | avg(a) | - // +--------+ - // | NULL | - // +--------+ - // 1 row in set (0.00 sec) - if len(v.GroupByItems) != 0 || aggregation.IsAllFirstRow(v.AggFuncs) { - e.DefaultVal = nil - } else { - if v.IsFinalAgg() { - e.DefaultVal = e.Ctx().GetSessionVars().GetNewChunkWithCapacity(exec.RetTypes(e), 1, 1, e.AllocPool) - } - } - for _, aggDesc := range v.AggFuncs { - if aggDesc.HasDistinct || len(aggDesc.OrderByItems) > 0 { - e.IsUnparallelExec = true - } - } - // When we set both tidb_hashagg_final_concurrency and tidb_hashagg_partial_concurrency to 1, - // we do not need to parallelly execute hash agg, - // and this action can be a workaround when meeting some unexpected situation using parallelExec. - if finalCon, partialCon := sessionVars.HashAggFinalConcurrency(), sessionVars.HashAggPartialConcurrency(); finalCon <= 0 || partialCon <= 0 || finalCon == 1 && partialCon == 1 { - e.IsUnparallelExec = true - } - partialOrdinal := 0 - for i, aggDesc := range v.AggFuncs { - if e.IsUnparallelExec { - e.PartialAggFuncs = append(e.PartialAggFuncs, aggfuncs.Build(b.ctx, aggDesc, i)) - } else { - ordinal := []int{partialOrdinal} - partialOrdinal++ - if aggDesc.Name == ast.AggFuncAvg { - ordinal = append(ordinal, partialOrdinal+1) - partialOrdinal++ - } - partialAggDesc, finalDesc := aggDesc.Split(ordinal) - partialAggFunc := aggfuncs.Build(b.ctx, partialAggDesc, i) - finalAggFunc := aggfuncs.Build(b.ctx, finalDesc, i) - e.PartialAggFuncs = append(e.PartialAggFuncs, partialAggFunc) - e.FinalAggFuncs = append(e.FinalAggFuncs, finalAggFunc) - if partialAggDesc.Name == ast.AggFuncGroupConcat { - // For group_concat, finalAggFunc and partialAggFunc need shared `truncate` flag to do duplicate. - finalAggFunc.(interface{ SetTruncated(t *int32) }).SetTruncated( - partialAggFunc.(interface{ GetTruncated() *int32 }).GetTruncated(), - ) - } - } - if e.DefaultVal != nil { - value := aggDesc.GetDefaultValue() - e.DefaultVal.AppendDatum(i, &value) - } - } - - executor_metrics.ExecutorCounterHashAggExec.Inc() - return e -} - -func (b *executorBuilder) buildStreamAgg(v *plannercore.PhysicalStreamAgg) exec.Executor { - src := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - e := &aggregate.StreamAggExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), - GroupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, v.GroupByItems), - AggFuncs: make([]aggfuncs.AggFunc, 0, len(v.AggFuncs)), - } - - if len(v.GroupByItems) != 0 || aggregation.IsAllFirstRow(v.AggFuncs) { - e.DefaultVal = nil - } else { - // Only do this for final agg, see issue #35295, #30923 - if v.IsFinalAgg() { - e.DefaultVal = e.Ctx().GetSessionVars().GetNewChunkWithCapacity(exec.RetTypes(e), 1, 1, e.AllocPool) - } - } - for i, aggDesc := range v.AggFuncs { - aggFunc := aggfuncs.Build(b.ctx, aggDesc, i) - e.AggFuncs = append(e.AggFuncs, aggFunc) - if e.DefaultVal != nil { - value := aggDesc.GetDefaultValue() - e.DefaultVal.AppendDatum(i, &value) - } - } - - executor_metrics.ExecutorStreamAggExec.Inc() - return e -} - -func (b *executorBuilder) buildSelection(v *plannercore.PhysicalSelection) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - e := &SelectionExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), - filters: v.Conditions, - } - return e -} - -func (b *executorBuilder) buildProjection(v *plannercore.PhysicalProjection) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - e := &ProjectionExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), - numWorkers: int64(b.ctx.GetSessionVars().ProjectionConcurrency()), - evaluatorSuit: expression.NewEvaluatorSuite(v.Exprs, v.AvoidColumnEvaluator), - calculateNoDelay: v.CalculateNoDelay, - } - - // If the calculation row count for this Projection operator is smaller - // than a Chunk size, we turn back to the un-parallel Projection - // implementation to reduce the goroutine overhead. - if int64(v.StatsCount()) < int64(b.ctx.GetSessionVars().MaxChunkSize) { - e.numWorkers = 0 - } - - // Use un-parallel projection for query that write on memdb to avoid data race. - // See also https://github.com/pingcap/tidb/issues/26832 - if b.inUpdateStmt || b.inDeleteStmt || b.inInsertStmt || b.hasLock { - e.numWorkers = 0 - } - return e -} - -func (b *executorBuilder) buildTableDual(v *plannercore.PhysicalTableDual) exec.Executor { - if v.RowCount != 0 && v.RowCount != 1 { - b.err = errors.Errorf("buildTableDual failed, invalid row count for dual table: %v", v.RowCount) - return nil - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(v.RowCount) - e := &TableDualExec{ - BaseExecutor: base, - numDualRows: v.RowCount, - } - return e -} - -// `getSnapshotTS` returns for-update-ts if in insert/update/delete/lock statement otherwise the isolation read ts -// Please notice that in RC isolation, the above two ts are the same -func (b *executorBuilder) getSnapshotTS() (ts uint64, err error) { - if b.forDataReaderBuilder { - return b.dataReaderTS, nil - } - - txnManager := sessiontxn.GetTxnManager(b.ctx) - if b.inInsertStmt || b.inUpdateStmt || b.inDeleteStmt || b.inSelectLockStmt { - return txnManager.GetStmtForUpdateTS() - } - return txnManager.GetStmtReadTS() -} - -// getSnapshot get the appropriate snapshot from txnManager and set -// the relevant snapshot options before return. -func (b *executorBuilder) getSnapshot() (kv.Snapshot, error) { - var snapshot kv.Snapshot - var err error - - txnManager := sessiontxn.GetTxnManager(b.ctx) - if b.inInsertStmt || b.inUpdateStmt || b.inDeleteStmt || b.inSelectLockStmt { - snapshot, err = txnManager.GetSnapshotWithStmtForUpdateTS() - } else { - snapshot, err = txnManager.GetSnapshotWithStmtReadTS() - } - if err != nil { - return nil, err - } - - sessVars := b.ctx.GetSessionVars() - replicaReadType := sessVars.GetReplicaRead() - snapshot.SetOption(kv.ReadReplicaScope, b.readReplicaScope) - snapshot.SetOption(kv.TaskID, sessVars.StmtCtx.TaskID) - snapshot.SetOption(kv.TiKVClientReadTimeout, sessVars.GetTiKVClientReadTimeout()) - snapshot.SetOption(kv.ResourceGroupName, sessVars.ResourceGroupName) - snapshot.SetOption(kv.ExplicitRequestSourceType, sessVars.ExplicitRequestSourceType) - - if replicaReadType.IsClosestRead() && b.readReplicaScope != kv.GlobalTxnScope { - snapshot.SetOption(kv.MatchStoreLabels, []*metapb.StoreLabel{ - { - Key: placement.DCLabelKey, - Value: b.readReplicaScope, - }, - }) - } - - return snapshot, nil -} - -func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) exec.Executor { - switch v.DBName.L { - case util.MetricSchemaName.L: - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &MetricRetriever{ - table: v.Table, - extractor: v.Extractor.(*plannercore.MetricTableExtractor), - }, - } - case util.InformationSchemaName.L: - switch v.Table.Name.L { - case strings.ToLower(infoschema.TableClusterConfig): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &clusterConfigRetriever{ - extractor: v.Extractor.(*plannercore.ClusterTableExtractor), - }, - } - case strings.ToLower(infoschema.TableClusterLoad): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &clusterServerInfoRetriever{ - extractor: v.Extractor.(*plannercore.ClusterTableExtractor), - serverInfoType: diagnosticspb.ServerInfoType_LoadInfo, - }, - } - case strings.ToLower(infoschema.TableClusterHardware): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &clusterServerInfoRetriever{ - extractor: v.Extractor.(*plannercore.ClusterTableExtractor), - serverInfoType: diagnosticspb.ServerInfoType_HardwareInfo, - }, - } - case strings.ToLower(infoschema.TableClusterSystemInfo): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &clusterServerInfoRetriever{ - extractor: v.Extractor.(*plannercore.ClusterTableExtractor), - serverInfoType: diagnosticspb.ServerInfoType_SystemInfo, - }, - } - case strings.ToLower(infoschema.TableClusterLog): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &clusterLogRetriever{ - extractor: v.Extractor.(*plannercore.ClusterLogTableExtractor), - }, - } - case strings.ToLower(infoschema.TableTiDBHotRegionsHistory): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &hotRegionsHistoryRetriver{ - extractor: v.Extractor.(*plannercore.HotRegionsHistoryTableExtractor), - }, - } - case strings.ToLower(infoschema.TableInspectionResult): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &inspectionResultRetriever{ - extractor: v.Extractor.(*plannercore.InspectionResultTableExtractor), - timeRange: v.QueryTimeRange, - }, - } - case strings.ToLower(infoschema.TableInspectionSummary): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &inspectionSummaryRetriever{ - table: v.Table, - extractor: v.Extractor.(*plannercore.InspectionSummaryTableExtractor), - timeRange: v.QueryTimeRange, - }, - } - case strings.ToLower(infoschema.TableInspectionRules): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &inspectionRuleRetriever{ - extractor: v.Extractor.(*plannercore.InspectionRuleTableExtractor), - }, - } - case strings.ToLower(infoschema.TableMetricSummary): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &MetricsSummaryRetriever{ - table: v.Table, - extractor: v.Extractor.(*plannercore.MetricSummaryTableExtractor), - timeRange: v.QueryTimeRange, - }, - } - case strings.ToLower(infoschema.TableMetricSummaryByLabel): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &MetricsSummaryByLabelRetriever{ - table: v.Table, - extractor: v.Extractor.(*plannercore.MetricSummaryTableExtractor), - timeRange: v.QueryTimeRange, - }, - } - case strings.ToLower(infoschema.TableTiKVRegionPeers): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &tikvRegionPeersRetriever{ - extractor: v.Extractor.(*plannercore.TikvRegionPeersExtractor), - }, - } - case strings.ToLower(infoschema.TableSchemata), - strings.ToLower(infoschema.TableStatistics), - strings.ToLower(infoschema.TableTiDBIndexes), - strings.ToLower(infoschema.TableViews), - strings.ToLower(infoschema.TableTables), - strings.ToLower(infoschema.TableReferConst), - strings.ToLower(infoschema.TableSequences), - strings.ToLower(infoschema.TablePartitions), - strings.ToLower(infoschema.TableEngines), - strings.ToLower(infoschema.TableCollations), - strings.ToLower(infoschema.TableAnalyzeStatus), - strings.ToLower(infoschema.TableClusterInfo), - strings.ToLower(infoschema.TableProfiling), - strings.ToLower(infoschema.TableCharacterSets), - strings.ToLower(infoschema.TableKeyColumn), - strings.ToLower(infoschema.TableUserPrivileges), - strings.ToLower(infoschema.TableMetricTables), - strings.ToLower(infoschema.TableCollationCharacterSetApplicability), - strings.ToLower(infoschema.TableProcesslist), - strings.ToLower(infoschema.ClusterTableProcesslist), - strings.ToLower(infoschema.TableTiKVRegionStatus), - strings.ToLower(infoschema.TableTiDBHotRegions), - strings.ToLower(infoschema.TableSessionVar), - strings.ToLower(infoschema.TableConstraints), - strings.ToLower(infoschema.TableTiFlashReplica), - strings.ToLower(infoschema.TableTiDBServersInfo), - strings.ToLower(infoschema.TableTiKVStoreStatus), - strings.ToLower(infoschema.TableClientErrorsSummaryGlobal), - strings.ToLower(infoschema.TableClientErrorsSummaryByUser), - strings.ToLower(infoschema.TableClientErrorsSummaryByHost), - strings.ToLower(infoschema.TableAttributes), - strings.ToLower(infoschema.TablePlacementPolicies), - strings.ToLower(infoschema.TableTrxSummary), - strings.ToLower(infoschema.TableVariablesInfo), - strings.ToLower(infoschema.TableUserAttributes), - strings.ToLower(infoschema.ClusterTableTrxSummary), - strings.ToLower(infoschema.TableMemoryUsage), - strings.ToLower(infoschema.TableMemoryUsageOpsHistory), - strings.ToLower(infoschema.ClusterTableMemoryUsage), - strings.ToLower(infoschema.ClusterTableMemoryUsageOpsHistory), - strings.ToLower(infoschema.TableResourceGroups), - strings.ToLower(infoschema.TableRunawayWatches), - strings.ToLower(infoschema.TableCheckConstraints): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &memtableRetriever{ - table: v.Table, - columns: v.Columns, - extractor: v.Extractor, - }, - } - case strings.ToLower(infoschema.TableTiDBTrx), - strings.ToLower(infoschema.ClusterTableTiDBTrx): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &tidbTrxTableRetriever{ - table: v.Table, - columns: v.Columns, - }, - } - case strings.ToLower(infoschema.TableDataLockWaits): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &dataLockWaitsTableRetriever{ - table: v.Table, - columns: v.Columns, - }, - } - case strings.ToLower(infoschema.TableDeadlocks), - strings.ToLower(infoschema.ClusterTableDeadlocks): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &deadlocksTableRetriever{ - table: v.Table, - columns: v.Columns, - }, - } - case strings.ToLower(infoschema.TableStatementsSummary), - strings.ToLower(infoschema.TableStatementsSummaryHistory), - strings.ToLower(infoschema.TableStatementsSummaryEvicted), - strings.ToLower(infoschema.ClusterTableStatementsSummary), - strings.ToLower(infoschema.ClusterTableStatementsSummaryHistory), - strings.ToLower(infoschema.ClusterTableStatementsSummaryEvicted): - var extractor *plannercore.StatementsSummaryExtractor - if v.Extractor != nil { - extractor = v.Extractor.(*plannercore.StatementsSummaryExtractor) - } - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: buildStmtSummaryRetriever(v.Table, v.Columns, extractor), - } - case strings.ToLower(infoschema.TableColumns): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &hugeMemTableRetriever{ - table: v.Table, - columns: v.Columns, - extractor: v.Extractor.(*plannercore.ColumnsTableExtractor), - viewSchemaMap: make(map[int64]*expression.Schema), - viewOutputNamesMap: make(map[int64]types.NameSlice), - }, - } - case strings.ToLower(infoschema.TableSlowQuery), strings.ToLower(infoschema.ClusterTableSlowLog): - memTracker := memory.NewTracker(v.ID(), -1) - memTracker.AttachTo(b.ctx.GetSessionVars().StmtCtx.MemTracker) - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &slowQueryRetriever{ - table: v.Table, - outputCols: v.Columns, - extractor: v.Extractor.(*plannercore.SlowQueryExtractor), - memTracker: memTracker, - }, - } - case strings.ToLower(infoschema.TableStorageStats): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &tableStorageStatsRetriever{ - table: v.Table, - outputCols: v.Columns, - extractor: v.Extractor.(*plannercore.TableStorageStatsExtractor), - }, - } - case strings.ToLower(infoschema.TableDDLJobs): - loc := b.ctx.GetSessionVars().Location() - ddlJobRetriever := DDLJobRetriever{TZLoc: loc} - return &DDLJobsReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - is: b.is, - DDLJobRetriever: ddlJobRetriever, - } - case strings.ToLower(infoschema.TableTiFlashTables), - strings.ToLower(infoschema.TableTiFlashSegments): - return &MemTableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.Table, - retriever: &TiFlashSystemTableRetriever{ - table: v.Table, - outputCols: v.Columns, - extractor: v.Extractor.(*plannercore.TiFlashSystemTableExtractor), - }, - } - } - } - tb, _ := b.is.TableByID(v.Table.ID) - return &TableScanExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - t: tb, - columns: v.Columns, - } -} - -func (b *executorBuilder) buildSort(v *plannercore.PhysicalSort) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - sortExec := SortExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), - ByItems: v.ByItems, - schema: v.Schema(), - } - executor_metrics.ExecutorCounterSortExec.Inc() - return &sortExec -} - -func (b *executorBuilder) buildTopN(v *plannercore.PhysicalTopN) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - sortExec := SortExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), - ByItems: v.ByItems, - schema: v.Schema(), - } - executor_metrics.ExecutorCounterTopNExec.Inc() - return &TopNExec{ - SortExec: sortExec, - limit: &plannercore.PhysicalLimit{Count: v.Count, Offset: v.Offset}, - } -} - -func (b *executorBuilder) buildApply(v *plannercore.PhysicalApply) exec.Executor { - var ( - innerPlan plannercore.PhysicalPlan - outerPlan plannercore.PhysicalPlan - ) - if v.InnerChildIdx == 0 { - innerPlan = v.Children()[0] - outerPlan = v.Children()[1] - } else { - innerPlan = v.Children()[1] - outerPlan = v.Children()[0] - } - v.OuterSchema = plannercore.ExtractCorColumnsBySchema4PhysicalPlan(innerPlan, outerPlan.Schema()) - leftChild := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - rightChild := b.build(v.Children()[1]) - if b.err != nil { - return nil - } - // test is in the explain/naaj.test#part5. - // although we prepared the NAEqualConditions, but for Apply mode, we still need move it to other conditions like eq condition did here. - otherConditions := append(expression.ScalarFuncs2Exprs(v.EqualConditions), expression.ScalarFuncs2Exprs(v.NAEqualConditions)...) - otherConditions = append(otherConditions, v.OtherConditions...) - defaultValues := v.DefaultValues - if defaultValues == nil { - defaultValues = make([]types.Datum, v.Children()[v.InnerChildIdx].Schema().Len()) - } - outerExec, innerExec := leftChild, rightChild - outerFilter, innerFilter := v.LeftConditions, v.RightConditions - if v.InnerChildIdx == 0 { - outerExec, innerExec = rightChild, leftChild - outerFilter, innerFilter = v.RightConditions, v.LeftConditions - } - tupleJoiner := newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, - defaultValues, otherConditions, exec.RetTypes(leftChild), exec.RetTypes(rightChild), nil, false) - serialExec := &NestedLoopApplyExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec, innerExec), - innerExec: innerExec, - outerExec: outerExec, - outerFilter: outerFilter, - innerFilter: innerFilter, - outer: v.JoinType != plannercore.InnerJoin, - joiner: tupleJoiner, - outerSchema: v.OuterSchema, - ctx: b.ctx, - canUseCache: v.CanUseCache, - } - executor_metrics.ExecutorCounterNestedLoopApplyExec.Inc() - - // try parallel mode - if v.Concurrency > 1 { - innerExecs := make([]exec.Executor, 0, v.Concurrency) - innerFilters := make([]expression.CNFExprs, 0, v.Concurrency) - corCols := make([][]*expression.CorrelatedColumn, 0, v.Concurrency) - joiners := make([]joiner, 0, v.Concurrency) - for i := 0; i < v.Concurrency; i++ { - clonedInnerPlan, err := plannercore.SafeClone(innerPlan) - if err != nil { - b.err = nil - return serialExec - } - corCol := plannercore.ExtractCorColumnsBySchema4PhysicalPlan(clonedInnerPlan, outerPlan.Schema()) - clonedInnerExec := b.build(clonedInnerPlan) - if b.err != nil { - b.err = nil - return serialExec - } - innerExecs = append(innerExecs, clonedInnerExec) - corCols = append(corCols, corCol) - innerFilters = append(innerFilters, innerFilter.Clone()) - joiners = append(joiners, newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, - defaultValues, otherConditions, exec.RetTypes(leftChild), exec.RetTypes(rightChild), nil, false)) - } - - allExecs := append([]exec.Executor{outerExec}, innerExecs...) - - return &ParallelNestedLoopApplyExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), allExecs...), - innerExecs: innerExecs, - outerExec: outerExec, - outerFilter: outerFilter, - innerFilter: innerFilters, - outer: v.JoinType != plannercore.InnerJoin, - joiners: joiners, - corCols: corCols, - concurrency: v.Concurrency, - useCache: v.CanUseCache, - } - } - return serialExec -} - -func (b *executorBuilder) buildMaxOneRow(v *plannercore.PhysicalMaxOneRow) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) - base.SetInitCap(2) - base.SetMaxChunkSize(2) - e := &MaxOneRowExec{BaseExecutor: base} - return e -} - -func (b *executorBuilder) buildUnionAll(v *plannercore.PhysicalUnionAll) exec.Executor { - childExecs := make([]exec.Executor, len(v.Children())) - for i, child := range v.Children() { - childExecs[i] = b.build(child) - if b.err != nil { - return nil - } - } - e := &UnionExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExecs...), - concurrency: b.ctx.GetSessionVars().UnionConcurrency(), - } - return e -} - -func buildHandleColsForSplit(sc *stmtctx.StatementContext, tbInfo *model.TableInfo) plannercore.HandleCols { - if tbInfo.IsCommonHandle { - primaryIdx := tables.FindPrimaryIndex(tbInfo) - tableCols := make([]*expression.Column, len(tbInfo.Columns)) - for i, col := range tbInfo.Columns { - tableCols[i] = &expression.Column{ - ID: col.ID, - RetType: &col.FieldType, - } - } - for i, pkCol := range primaryIdx.Columns { - tableCols[pkCol.Offset].Index = i - } - return plannercore.NewCommonHandleCols(sc, tbInfo, primaryIdx, tableCols) - } - intCol := &expression.Column{ - RetType: types.NewFieldType(mysql.TypeLonglong), - } - return plannercore.NewIntHandleCols(intCol) -} - -func (b *executorBuilder) buildSplitRegion(v *plannercore.SplitRegion) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(1) - base.SetMaxChunkSize(1) - if v.IndexInfo != nil { - return &SplitIndexRegionExec{ - BaseExecutor: base, - tableInfo: v.TableInfo, - partitionNames: v.PartitionNames, - indexInfo: v.IndexInfo, - lower: v.Lower, - upper: v.Upper, - num: v.Num, - valueLists: v.ValueLists, - } - } - handleCols := buildHandleColsForSplit(b.ctx.GetSessionVars().StmtCtx, v.TableInfo) - if len(v.ValueLists) > 0 { - return &SplitTableRegionExec{ - BaseExecutor: base, - tableInfo: v.TableInfo, - partitionNames: v.PartitionNames, - handleCols: handleCols, - valueLists: v.ValueLists, - } - } - return &SplitTableRegionExec{ - BaseExecutor: base, - tableInfo: v.TableInfo, - partitionNames: v.PartitionNames, - handleCols: handleCols, - lower: v.Lower, - upper: v.Upper, - num: v.Num, - } -} - -func (b *executorBuilder) buildUpdate(v *plannercore.Update) exec.Executor { - b.inUpdateStmt = true - tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) - multiUpdateOnSameTable := make(map[int64]bool) - for _, info := range v.TblColPosInfos { - tbl, _ := b.is.TableByID(info.TblID) - if _, ok := tblID2table[info.TblID]; ok { - multiUpdateOnSameTable[info.TblID] = true - } - tblID2table[info.TblID] = tbl - if len(v.PartitionedTable) > 0 { - // The v.PartitionedTable collects the partitioned table. - // Replace the original table with the partitioned table to support partition selection. - // e.g. update t partition (p0, p1), the new values are not belong to the given set p0, p1 - // Using the table in v.PartitionedTable returns a proper error, while using the original table can't. - for _, p := range v.PartitionedTable { - if info.TblID == p.Meta().ID { - tblID2table[info.TblID] = p - } - } - } - } - if b.err = b.updateForUpdateTS(); b.err != nil { - return nil - } - - selExec := b.build(v.SelectPlan) - if b.err != nil { - return nil - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), selExec) - base.SetInitCap(chunk.ZeroCapacity) - var assignFlag []int - assignFlag, b.err = getAssignFlag(b.ctx, v, selExec.Schema().Len()) - if b.err != nil { - return nil - } - // should use the new tblID2table, since the update's schema may have been changed in Execstmt. - b.err = plannercore.CheckUpdateList(assignFlag, v, tblID2table) - if b.err != nil { - return nil - } - updateExec := &UpdateExec{ - BaseExecutor: base, - OrderedList: v.OrderedList, - allAssignmentsAreConstant: v.AllAssignmentsAreConstant, - virtualAssignmentsOffset: v.VirtualAssignmentsOffset, - multiUpdateOnSameTable: multiUpdateOnSameTable, - tblID2table: tblID2table, - tblColPosInfos: v.TblColPosInfos, - assignFlag: assignFlag, - } - updateExec.fkChecks, b.err = buildTblID2FKCheckExecs(b.ctx, tblID2table, v.FKChecks) - if b.err != nil { - return nil - } - updateExec.fkCascades, b.err = b.buildTblID2FKCascadeExecs(tblID2table, v.FKCascades) - if b.err != nil { - return nil - } - return updateExec -} - -func getAssignFlag(ctx sessionctx.Context, v *plannercore.Update, schemaLen int) ([]int, error) { - assignFlag := make([]int, schemaLen) - for i := range assignFlag { - assignFlag[i] = -1 - } - for _, assign := range v.OrderedList { - if !ctx.GetSessionVars().AllowWriteRowID && assign.Col.ID == model.ExtraHandleID { - return nil, errors.Errorf("insert, update and replace statements for _tidb_rowid are not supported") - } - tblIdx, found := v.TblColPosInfos.FindTblIdx(assign.Col.Index) - if found { - colIdx := assign.Col.Index - assignFlag[colIdx] = tblIdx - } - } - return assignFlag, nil -} - -func (b *executorBuilder) buildDelete(v *plannercore.Delete) exec.Executor { - b.inDeleteStmt = true - tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) - for _, info := range v.TblColPosInfos { - tblID2table[info.TblID], _ = b.is.TableByID(info.TblID) - } - - if b.err = b.updateForUpdateTS(); b.err != nil { - return nil - } - - selExec := b.build(v.SelectPlan) - if b.err != nil { - return nil - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), selExec) - base.SetInitCap(chunk.ZeroCapacity) - deleteExec := &DeleteExec{ - BaseExecutor: base, - tblID2Table: tblID2table, - IsMultiTable: v.IsMultiTable, - tblColPosInfos: v.TblColPosInfos, - } - deleteExec.fkChecks, b.err = buildTblID2FKCheckExecs(b.ctx, tblID2table, v.FKChecks) - if b.err != nil { - return nil - } - deleteExec.fkCascades, b.err = b.buildTblID2FKCascadeExecs(tblID2table, v.FKCascades) - if b.err != nil { - return nil - } - return deleteExec -} - -func (b *executorBuilder) updateForUpdateTS() error { - // GetStmtForUpdateTS will auto update the for update ts if it is necessary - _, err := sessiontxn.GetTxnManager(b.ctx).GetStmtForUpdateTS() - return err -} - -func (b *executorBuilder) buildAnalyzeIndexPushdown(task plannercore.AnalyzeIndexTask, opts map[ast.AnalyzeOptionType]uint64, autoAnalyze string) *analyzeTask { - job := &statistics.AnalyzeJob{DBName: task.DBName, TableName: task.TableName, PartitionName: task.PartitionName, JobInfo: autoAnalyze + "analyze index " + task.IndexInfo.Name.O} - _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) - sc := b.ctx.GetSessionVars().StmtCtx - startTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { - startTS = uint64(val.(int)) - }) - - base := baseAnalyzeExec{ - ctx: b.ctx, - tableID: task.TableID, - concurrency: b.ctx.GetSessionVars().IndexSerialScanConcurrency(), - analyzePB: &tipb.AnalyzeReq{ - Tp: tipb.AnalyzeType_TypeIndex, - Flags: sc.PushDownFlags(), - TimeZoneOffset: offset, - }, - opts: opts, - job: job, - snapshot: startTS, - } - e := &AnalyzeIndexExec{ - baseAnalyzeExec: base, - isCommonHandle: task.TblInfo.IsCommonHandle, - idxInfo: task.IndexInfo, - } - topNSize := new(int32) - *topNSize = int32(opts[ast.AnalyzeOptNumTopN]) - statsVersion := new(int32) - *statsVersion = int32(task.StatsVersion) - e.analyzePB.IdxReq = &tipb.AnalyzeIndexReq{ - BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), - NumColumns: int32(len(task.IndexInfo.Columns)), - TopNSize: topNSize, - Version: statsVersion, - SketchSize: maxSketchSize, - } - if e.isCommonHandle && e.idxInfo.Primary { - e.analyzePB.Tp = tipb.AnalyzeType_TypeCommonHandle - } - depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) - width := int32(opts[ast.AnalyzeOptCMSketchWidth]) - e.analyzePB.IdxReq.CmsketchDepth = &depth - e.analyzePB.IdxReq.CmsketchWidth = &width - return &analyzeTask{taskType: idxTask, idxExec: e, job: job} -} - -func (b *executorBuilder) buildAnalyzeSamplingPushdown( - task plannercore.AnalyzeColumnsTask, - opts map[ast.AnalyzeOptionType]uint64, - schemaForVirtualColEval *expression.Schema, -) *analyzeTask { - if task.V2Options != nil { - opts = task.V2Options.FilledOpts - } - availableIdx := make([]*model.IndexInfo, 0, len(task.Indexes)) - colGroups := make([]*tipb.AnalyzeColumnGroup, 0, len(task.Indexes)) - if len(task.Indexes) > 0 { - for _, idx := range task.Indexes { - availableIdx = append(availableIdx, idx) - colGroup := &tipb.AnalyzeColumnGroup{ - ColumnOffsets: make([]int64, 0, len(idx.Columns)), - } - for _, col := range idx.Columns { - colGroup.ColumnOffsets = append(colGroup.ColumnOffsets, int64(col.Offset)) - } - colGroups = append(colGroups, colGroup) - } - } - - _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) - sc := b.ctx.GetSessionVars().StmtCtx - startTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { - startTS = uint64(val.(int)) - }) - statsHandle := domain.GetDomain(b.ctx).StatsHandle() - count, modifyCount, err := statsHandle.StatsMetaCountAndModifyCount(task.TableID.GetStatisticsID()) - if err != nil { - b.err = err - return nil - } - failpoint.Inject("injectBaseCount", func(val failpoint.Value) { - count = int64(val.(int)) - }) - failpoint.Inject("injectBaseModifyCount", func(val failpoint.Value) { - modifyCount = int64(val.(int)) - }) - sampleRate := new(float64) - var sampleRateReason string - if opts[ast.AnalyzeOptNumSamples] == 0 { - *sampleRate = math.Float64frombits(opts[ast.AnalyzeOptSampleRate]) - if *sampleRate < 0 { - *sampleRate, sampleRateReason = b.getAdjustedSampleRate(task) - if task.PartitionName != "" { - sc.AppendNote(errors.Errorf( - `Analyze use auto adjusted sample rate %f for table %s.%s's partition %s, reason to use this rate is "%s"`, - *sampleRate, - task.DBName, - task.TableName, - task.PartitionName, - sampleRateReason, - )) - } else { - sc.AppendNote(errors.Errorf( - `Analyze use auto adjusted sample rate %f for table %s.%s, reason to use this rate is "%s"`, - *sampleRate, - task.DBName, - task.TableName, - sampleRateReason, - )) - } - } - } - job := &statistics.AnalyzeJob{ - DBName: task.DBName, - TableName: task.TableName, - PartitionName: task.PartitionName, - SampleRateReason: sampleRateReason, - } - - base := baseAnalyzeExec{ - ctx: b.ctx, - tableID: task.TableID, - concurrency: b.ctx.GetSessionVars().DistSQLScanConcurrency(), - analyzePB: &tipb.AnalyzeReq{ - Tp: tipb.AnalyzeType_TypeFullSampling, - Flags: sc.PushDownFlags(), - TimeZoneOffset: offset, - }, - opts: opts, - job: job, - snapshot: startTS, - } - e := &AnalyzeColumnsExec{ - baseAnalyzeExec: base, - tableInfo: task.TblInfo, - colsInfo: task.ColsInfo, - handleCols: task.HandleCols, - indexes: availableIdx, - AnalyzeInfo: task.AnalyzeInfo, - schemaForVirtualColEval: schemaForVirtualColEval, - baseCount: count, - baseModifyCnt: modifyCount, - } - e.analyzePB.ColReq = &tipb.AnalyzeColumnsReq{ - BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), - SampleSize: int64(opts[ast.AnalyzeOptNumSamples]), - SampleRate: sampleRate, - SketchSize: maxSketchSize, - ColumnsInfo: util.ColumnsToProto(task.ColsInfo, task.TblInfo.PKIsHandle, false), - ColumnGroups: colGroups, - } - if task.TblInfo != nil { - e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) - if task.TblInfo.IsCommonHandle { - e.analyzePB.ColReq.PrimaryPrefixColumnIds = tables.PrimaryPrefixColumnIDs(task.TblInfo) - } - } - b.err = tables.SetPBColumnsDefaultValue(b.ctx, e.analyzePB.ColReq.ColumnsInfo, task.ColsInfo) - return &analyzeTask{taskType: colTask, colExec: e, job: job} -} - -// getAdjustedSampleRate calculate the sample rate by the table size. If we cannot get the table size. We use the 0.001 as the default sample rate. -// From the paper "Random sampling for histogram construction: how much is enough?"'s Corollary 1 to Theorem 5, -// for a table size n, histogram size k, maximum relative error in bin size f, and error probability gamma, -// the minimum random sample size is -// -// r = 4 * k * ln(2*n/gamma) / f^2 -// -// If we take f = 0.5, gamma = 0.01, n =1e6, we would got r = 305.82* k. -// Since the there's log function over the table size n, the r grows slowly when the n increases. -// If we take n = 1e12, a 300*k sample still gives <= 0.66 bin size error with probability 0.99. -// So if we don't consider the top-n values, we can keep the sample size at 300*256. -// But we may take some top-n before building the histogram, so we increase the sample a little. -func (b *executorBuilder) getAdjustedSampleRate(task plannercore.AnalyzeColumnsTask) (sampleRate float64, reason string) { - statsHandle := domain.GetDomain(b.ctx).StatsHandle() - defaultRate := 0.001 - if statsHandle == nil { - return defaultRate, fmt.Sprintf("statsHandler is nil, use the default-rate=%v", defaultRate) - } - var statsTbl *statistics.Table - tid := task.TableID.GetStatisticsID() - if tid == task.TblInfo.ID { - statsTbl = statsHandle.GetTableStats(task.TblInfo) - } else { - statsTbl = statsHandle.GetPartitionStats(task.TblInfo, tid) - } - approxiCount, hasPD := b.getApproximateTableCountFromStorage(tid, task) - // If there's no stats meta and no pd, return the default rate. - if statsTbl == nil && !hasPD { - return defaultRate, fmt.Sprintf("TiDB cannot get the row count of the table, use the default-rate=%v", defaultRate) - } - // If the count in stats_meta is still 0 and there's no information from pd side, we scan all rows. - if statsTbl.RealtimeCount == 0 && !hasPD { - return 1, "TiDB assumes that the table is empty and cannot get row count from PD, use sample-rate=1" - } - // we have issue https://github.com/pingcap/tidb/issues/29216. - // To do a workaround for this issue, we check the approxiCount from the pd side to do a comparison. - // If the count from the stats_meta is extremely smaller than the approximate count from the pd, - // we think that we meet this issue and use the approximate count to calculate the sample rate. - if float64(statsTbl.RealtimeCount*5) < approxiCount { - // Confirmed by TiKV side, the experience error rate of the approximate count is about 20%. - // So we increase the number to 150000 to reduce this error rate. - sampleRate = math.Min(1, 150000/approxiCount) - return sampleRate, fmt.Sprintf("Row count in stats_meta is much smaller compared with the row count got by PD, use min(1, 15000/%v) as the sample-rate=%v", approxiCount, sampleRate) - } - // If we don't go into the above if branch and we still detect the count is zero. Return 1 to prevent the dividing zero. - if statsTbl.RealtimeCount == 0 { - return 1, "TiDB assumes that the table is empty, use sample-rate=1" - } - // We are expected to scan about 100000 rows or so. - // Since there's tiny error rate around the count from the stats meta, we use 110000 to get a little big result - sampleRate = math.Min(1, config.DefRowsForSampleRate/float64(statsTbl.RealtimeCount)) - return sampleRate, fmt.Sprintf("use min(1, %v/%v) as the sample-rate=%v", config.DefRowsForSampleRate, statsTbl.RealtimeCount, sampleRate) -} - -func (b *executorBuilder) getApproximateTableCountFromStorage(tid int64, task plannercore.AnalyzeColumnsTask) (float64, bool) { - return pdhelper.GlobalPDHelper.GetApproximateTableCountFromStorage(b.ctx, tid, task.DBName, task.TableName, task.PartitionName) -} - -func (b *executorBuilder) buildAnalyzeColumnsPushdown( - task plannercore.AnalyzeColumnsTask, - opts map[ast.AnalyzeOptionType]uint64, - autoAnalyze string, - schemaForVirtualColEval *expression.Schema, -) *analyzeTask { - if task.StatsVersion == statistics.Version2 { - return b.buildAnalyzeSamplingPushdown(task, opts, schemaForVirtualColEval) - } - job := &statistics.AnalyzeJob{DBName: task.DBName, TableName: task.TableName, PartitionName: task.PartitionName, JobInfo: autoAnalyze + "analyze columns"} - cols := task.ColsInfo - if hasPkHist(task.HandleCols) { - colInfo := task.TblInfo.Columns[task.HandleCols.GetCol(0).Index] - cols = append([]*model.ColumnInfo{colInfo}, cols...) - } else if task.HandleCols != nil && !task.HandleCols.IsInt() { - cols = make([]*model.ColumnInfo, 0, len(task.ColsInfo)+task.HandleCols.NumCols()) - for i := 0; i < task.HandleCols.NumCols(); i++ { - cols = append(cols, task.TblInfo.Columns[task.HandleCols.GetCol(i).Index]) - } - cols = append(cols, task.ColsInfo...) - task.ColsInfo = cols - } - - _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) - sc := b.ctx.GetSessionVars().StmtCtx - startTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { - startTS = uint64(val.(int)) - }) - - base := baseAnalyzeExec{ - ctx: b.ctx, - tableID: task.TableID, - concurrency: b.ctx.GetSessionVars().DistSQLScanConcurrency(), - analyzePB: &tipb.AnalyzeReq{ - Tp: tipb.AnalyzeType_TypeColumn, - Flags: sc.PushDownFlags(), - TimeZoneOffset: offset, - }, - opts: opts, - job: job, - snapshot: startTS, - } - e := &AnalyzeColumnsExec{ - baseAnalyzeExec: base, - colsInfo: task.ColsInfo, - handleCols: task.HandleCols, - AnalyzeInfo: task.AnalyzeInfo, - } - depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) - width := int32(opts[ast.AnalyzeOptCMSketchWidth]) - e.analyzePB.ColReq = &tipb.AnalyzeColumnsReq{ - BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), - SampleSize: MaxRegionSampleSize, - SketchSize: maxSketchSize, - ColumnsInfo: util.ColumnsToProto(cols, task.HandleCols != nil && task.HandleCols.IsInt(), false), - CmsketchDepth: &depth, - CmsketchWidth: &width, - } - if task.TblInfo != nil { - e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) - if task.TblInfo.IsCommonHandle { - e.analyzePB.ColReq.PrimaryPrefixColumnIds = tables.PrimaryPrefixColumnIDs(task.TblInfo) - } - } - if task.CommonHandleInfo != nil { - topNSize := new(int32) - *topNSize = int32(opts[ast.AnalyzeOptNumTopN]) - statsVersion := new(int32) - *statsVersion = int32(task.StatsVersion) - e.analyzePB.IdxReq = &tipb.AnalyzeIndexReq{ - BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), - NumColumns: int32(len(task.CommonHandleInfo.Columns)), - TopNSize: topNSize, - Version: statsVersion, - } - depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) - width := int32(opts[ast.AnalyzeOptCMSketchWidth]) - e.analyzePB.IdxReq.CmsketchDepth = &depth - e.analyzePB.IdxReq.CmsketchWidth = &width - e.analyzePB.IdxReq.SketchSize = maxSketchSize - e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) - e.analyzePB.Tp = tipb.AnalyzeType_TypeMixed - e.commonHandle = task.CommonHandleInfo - } - b.err = tables.SetPBColumnsDefaultValue(b.ctx, e.analyzePB.ColReq.ColumnsInfo, cols) - return &analyzeTask{taskType: colTask, colExec: e, job: job} -} - -func (b *executorBuilder) buildAnalyze(v *plannercore.Analyze) exec.Executor { - e := &AnalyzeExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - tasks: make([]*analyzeTask, 0, len(v.ColTasks)+len(v.IdxTasks)), - opts: v.Opts, - OptionsMap: v.OptionsMap, - } - autoAnalyze := "" - if b.ctx.GetSessionVars().InRestrictedSQL { - autoAnalyze = "auto " - } - for _, task := range v.ColTasks { - columns, _, err := expression.ColumnInfos2ColumnsAndNames( - b.ctx, - model.NewCIStr(task.AnalyzeInfo.DBName), - task.TblInfo.Name, - task.ColsInfo, - task.TblInfo, - ) - if err != nil { - b.err = err - return nil - } - schema := expression.NewSchema(columns...) - e.tasks = append(e.tasks, b.buildAnalyzeColumnsPushdown(task, v.Opts, autoAnalyze, schema)) - // Other functions may set b.err, so we need to check it here. - if b.err != nil { - return nil - } - } - for _, task := range v.IdxTasks { - e.tasks = append(e.tasks, b.buildAnalyzeIndexPushdown(task, v.Opts, autoAnalyze)) - if b.err != nil { - return nil - } - } - return e -} - -// markChildrenUsedCols compares each child with the output schema, and mark -// each column of the child is used by output or not. -func markChildrenUsedCols(outputCols []*expression.Column, childSchemas ...*expression.Schema) (childrenUsed [][]bool) { - childrenUsed = make([][]bool, 0, len(childSchemas)) - markedOffsets := make(map[int]struct{}) - for _, col := range outputCols { - markedOffsets[col.Index] = struct{}{} - } - prefixLen := 0 - for _, childSchema := range childSchemas { - used := make([]bool, len(childSchema.Columns)) - for i := range childSchema.Columns { - if _, ok := markedOffsets[prefixLen+i]; ok { - used[i] = true - } - } - childrenUsed = append(childrenUsed, used) - prefixLen += childSchema.Len() - } - return -} - -func (*executorBuilder) corColInDistPlan(plans []plannercore.PhysicalPlan) bool { - for _, p := range plans { - x, ok := p.(*plannercore.PhysicalSelection) - if !ok { - continue - } - for _, cond := range x.Conditions { - if len(expression.ExtractCorColumns(cond)) > 0 { - return true - } - } - } - return false -} - -// corColInAccess checks whether there's correlated column in access conditions. -func (*executorBuilder) corColInAccess(p plannercore.PhysicalPlan) bool { - var access []expression.Expression - switch x := p.(type) { - case *plannercore.PhysicalTableScan: - access = x.AccessCondition - case *plannercore.PhysicalIndexScan: - access = x.AccessCondition - } - for _, cond := range access { - if len(expression.ExtractCorColumns(cond)) > 0 { - return true - } - } - return false -} - -func (b *executorBuilder) newDataReaderBuilder(p plannercore.PhysicalPlan) (*dataReaderBuilder, error) { - ts, err := b.getSnapshotTS() - if err != nil { - return nil, err - } - - builderForDataReader := *b - builderForDataReader.forDataReaderBuilder = true - builderForDataReader.dataReaderTS = ts - - return &dataReaderBuilder{ - Plan: p, - executorBuilder: &builderForDataReader, - }, nil -} - -func (b *executorBuilder) buildIndexLookUpJoin(v *plannercore.PhysicalIndexJoin) exec.Executor { - outerExec := b.build(v.Children()[1-v.InnerChildIdx]) - if b.err != nil { - return nil - } - outerTypes := exec.RetTypes(outerExec) - innerPlan := v.Children()[v.InnerChildIdx] - innerTypes := make([]*types.FieldType, innerPlan.Schema().Len()) - for i, col := range innerPlan.Schema().Columns { - innerTypes[i] = col.RetType.Clone() - // The `innerTypes` would be called for `Datum.ConvertTo` when converting the columns from outer table - // to build hash map or construct lookup keys. So we need to modify its flen otherwise there would be - // truncate error. See issue https://github.com/pingcap/tidb/issues/21232 for example. - if innerTypes[i].EvalType() == types.ETString { - innerTypes[i].SetFlen(types.UnspecifiedLength) - } - } - - // Use the probe table's collation. - for i, col := range v.OuterHashKeys { - outerTypes[col.Index] = outerTypes[col.Index].Clone() - outerTypes[col.Index].SetCollate(innerTypes[v.InnerHashKeys[i].Index].GetCollate()) - outerTypes[col.Index].SetFlag(col.RetType.GetFlag()) - } - - // We should use JoinKey to construct the type information using by hashing, instead of using the child's schema directly. - // When a hybrid type column is hashed multiple times, we need to distinguish what field types are used. - // For example, the condition `enum = int and enum = string`, we should use ETInt to hash the first column, - // and use ETString to hash the second column, although they may be the same column. - innerHashTypes := make([]*types.FieldType, len(v.InnerHashKeys)) - outerHashTypes := make([]*types.FieldType, len(v.OuterHashKeys)) - for i, col := range v.InnerHashKeys { - innerHashTypes[i] = innerTypes[col.Index].Clone() - innerHashTypes[i].SetFlag(col.RetType.GetFlag()) - } - for i, col := range v.OuterHashKeys { - outerHashTypes[i] = outerTypes[col.Index].Clone() - outerHashTypes[i].SetFlag(col.RetType.GetFlag()) - } - - var ( - outerFilter []expression.Expression - leftTypes, rightTypes []*types.FieldType - ) - - if v.InnerChildIdx == 0 { - leftTypes, rightTypes = innerTypes, outerTypes - outerFilter = v.RightConditions - if len(v.LeftConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } else { - leftTypes, rightTypes = outerTypes, innerTypes - outerFilter = v.LeftConditions - if len(v.RightConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } - defaultValues := v.DefaultValues - if defaultValues == nil { - defaultValues = make([]types.Datum, len(innerTypes)) - } - hasPrefixCol := false - for _, l := range v.IdxColLens { - if l != types.UnspecifiedLength { - hasPrefixCol = true - break - } - } - - readerBuilder, err := b.newDataReaderBuilder(innerPlan) - if err != nil { - b.err = err - return nil - } - - e := &IndexLookUpJoin{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec), - outerCtx: outerCtx{ - rowTypes: outerTypes, - hashTypes: outerHashTypes, - filter: outerFilter, - }, - innerCtx: innerCtx{ - readerBuilder: readerBuilder, - rowTypes: innerTypes, - hashTypes: innerHashTypes, - colLens: v.IdxColLens, - hasPrefixCol: hasPrefixCol, - }, - workerWg: new(sync.WaitGroup), - isOuterJoin: v.JoinType.IsOuterJoin(), - indexRanges: v.Ranges, - keyOff2IdxOff: v.KeyOff2IdxOff, - lastColHelper: v.CompareFilters, - finished: &atomic.Value{}, - } - colsFromChildren := v.Schema().Columns - if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { - colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] - } - childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) - e.joiner = newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, leftTypes, rightTypes, childrenUsedSchema, false) - outerKeyCols := make([]int, len(v.OuterJoinKeys)) - for i := 0; i < len(v.OuterJoinKeys); i++ { - outerKeyCols[i] = v.OuterJoinKeys[i].Index - } - innerKeyCols := make([]int, len(v.InnerJoinKeys)) - innerKeyColIDs := make([]int64, len(v.InnerJoinKeys)) - keyCollators := make([]collate.Collator, 0, len(v.InnerJoinKeys)) - for i := 0; i < len(v.InnerJoinKeys); i++ { - innerKeyCols[i] = v.InnerJoinKeys[i].Index - innerKeyColIDs[i] = v.InnerJoinKeys[i].ID - keyCollators = append(keyCollators, collate.GetCollator(v.InnerJoinKeys[i].RetType.GetCollate())) - } - e.outerCtx.keyCols = outerKeyCols - e.innerCtx.keyCols = innerKeyCols - e.innerCtx.keyColIDs = innerKeyColIDs - e.innerCtx.keyCollators = keyCollators - - outerHashCols, innerHashCols := make([]int, len(v.OuterHashKeys)), make([]int, len(v.InnerHashKeys)) - hashCollators := make([]collate.Collator, 0, len(v.InnerHashKeys)) - for i := 0; i < len(v.OuterHashKeys); i++ { - outerHashCols[i] = v.OuterHashKeys[i].Index - } - for i := 0; i < len(v.InnerHashKeys); i++ { - innerHashCols[i] = v.InnerHashKeys[i].Index - hashCollators = append(hashCollators, collate.GetCollator(v.InnerHashKeys[i].RetType.GetCollate())) - } - e.outerCtx.hashCols = outerHashCols - e.innerCtx.hashCols = innerHashCols - e.innerCtx.hashCollators = hashCollators - - e.joinResult = exec.TryNewCacheChunk(e) - executor_metrics.ExecutorCounterIndexLookUpJoin.Inc() - return e -} - -func (b *executorBuilder) buildIndexLookUpMergeJoin(v *plannercore.PhysicalIndexMergeJoin) exec.Executor { - outerExec := b.build(v.Children()[1-v.InnerChildIdx]) - if b.err != nil { - return nil - } - outerTypes := exec.RetTypes(outerExec) - innerPlan := v.Children()[v.InnerChildIdx] - innerTypes := make([]*types.FieldType, innerPlan.Schema().Len()) - for i, col := range innerPlan.Schema().Columns { - innerTypes[i] = col.RetType.Clone() - // The `innerTypes` would be called for `Datum.ConvertTo` when converting the columns from outer table - // to build hash map or construct lookup keys. So we need to modify its flen otherwise there would be - // truncate error. See issue https://github.com/pingcap/tidb/issues/21232 for example. - if innerTypes[i].EvalType() == types.ETString { - innerTypes[i].SetFlen(types.UnspecifiedLength) - } - } - var ( - outerFilter []expression.Expression - leftTypes, rightTypes []*types.FieldType - ) - if v.InnerChildIdx == 0 { - leftTypes, rightTypes = innerTypes, outerTypes - outerFilter = v.RightConditions - if len(v.LeftConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } else { - leftTypes, rightTypes = outerTypes, innerTypes - outerFilter = v.LeftConditions - if len(v.RightConditions) > 0 { - b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") - return nil - } - } - defaultValues := v.DefaultValues - if defaultValues == nil { - defaultValues = make([]types.Datum, len(innerTypes)) - } - outerKeyCols := make([]int, len(v.OuterJoinKeys)) - for i := 0; i < len(v.OuterJoinKeys); i++ { - outerKeyCols[i] = v.OuterJoinKeys[i].Index - } - innerKeyCols := make([]int, len(v.InnerJoinKeys)) - keyCollators := make([]collate.Collator, 0, len(v.InnerJoinKeys)) - for i := 0; i < len(v.InnerJoinKeys); i++ { - innerKeyCols[i] = v.InnerJoinKeys[i].Index - keyCollators = append(keyCollators, collate.GetCollator(v.InnerJoinKeys[i].RetType.GetCollate())) - } - executor_metrics.ExecutorCounterIndexLookUpJoin.Inc() - - readerBuilder, err := b.newDataReaderBuilder(innerPlan) - if err != nil { - b.err = err - return nil - } - - e := &IndexLookUpMergeJoin{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec), - outerMergeCtx: outerMergeCtx{ - rowTypes: outerTypes, - filter: outerFilter, - joinKeys: v.OuterJoinKeys, - keyCols: outerKeyCols, - needOuterSort: v.NeedOuterSort, - compareFuncs: v.OuterCompareFuncs, - }, - innerMergeCtx: innerMergeCtx{ - readerBuilder: readerBuilder, - rowTypes: innerTypes, - joinKeys: v.InnerJoinKeys, - keyCols: innerKeyCols, - keyCollators: keyCollators, - compareFuncs: v.CompareFuncs, - colLens: v.IdxColLens, - desc: v.Desc, - keyOff2KeyOffOrderByIdx: v.KeyOff2KeyOffOrderByIdx, - }, - workerWg: new(sync.WaitGroup), - isOuterJoin: v.JoinType.IsOuterJoin(), - indexRanges: v.Ranges, - keyOff2IdxOff: v.KeyOff2IdxOff, - lastColHelper: v.CompareFilters, - } - colsFromChildren := v.Schema().Columns - if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { - colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] - } - childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) - joiners := make([]joiner, e.Ctx().GetSessionVars().IndexLookupJoinConcurrency()) - for i := 0; i < len(joiners); i++ { - joiners[i] = newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, leftTypes, rightTypes, childrenUsedSchema, false) - } - e.joiners = joiners - return e -} - -func (b *executorBuilder) buildIndexNestedLoopHashJoin(v *plannercore.PhysicalIndexHashJoin) exec.Executor { - join := b.buildIndexLookUpJoin(&(v.PhysicalIndexJoin)) - if b.err != nil { - return nil - } - e := join.(*IndexLookUpJoin) - idxHash := &IndexNestedLoopHashJoin{ - IndexLookUpJoin: *e, - keepOuterOrder: v.KeepOuterOrder, - } - concurrency := e.Ctx().GetSessionVars().IndexLookupJoinConcurrency() - idxHash.joiners = make([]joiner, concurrency) - for i := 0; i < concurrency; i++ { - idxHash.joiners[i] = e.joiner.Clone() - } - return idxHash -} - -func buildNoRangeTableReader(b *executorBuilder, v *plannercore.PhysicalTableReader) (*TableReaderExecutor, error) { - tablePlans := v.TablePlans - if v.StoreType == kv.TiFlash { - tablePlans = []plannercore.PhysicalPlan{v.GetTablePlan()} - } - dagReq, err := builder.ConstructDAGReq(b.ctx, tablePlans, v.StoreType) - if err != nil { - return nil, err - } - ts, err := v.GetTableScan() - if err != nil { - return nil, err - } - if err = b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { - return nil, err - } - - tbl, _ := b.is.TableByID(ts.Table.ID) - isPartition, physicalTableID := ts.IsPartition() - if isPartition { - pt := tbl.(table.PartitionedTable) - tbl = pt.GetPartition(physicalTableID) - } - startTS, err := b.getSnapshotTS() - if err != nil { - return nil, err - } - paging := b.ctx.GetSessionVars().EnablePaging - e := &TableReaderExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dagPB: dagReq, - startTS: startTS, - txnScope: b.txnScope, - readReplicaScope: b.readReplicaScope, - isStaleness: b.isStaleness, - netDataSize: v.GetNetDataSize(), - table: tbl, - keepOrder: ts.KeepOrder, - desc: ts.Desc, - byItems: ts.ByItems, - columns: ts.Columns, - paging: paging, - corColInFilter: b.corColInDistPlan(v.TablePlans), - corColInAccess: b.corColInAccess(v.TablePlans[0]), - plans: v.TablePlans, - tablePlan: v.GetTablePlan(), - storeType: v.StoreType, - batchCop: v.ReadReqType == plannercore.BatchCop, - } - e.buildVirtualColumnInfo() - - if v.StoreType == kv.TiDB && b.ctx.GetSessionVars().User != nil { - // User info is used to do privilege check. It is only used in TiDB cluster memory table. - e.dagPB.User = &tipb.UserIdentity{ - UserName: b.ctx.GetSessionVars().User.Username, - UserHost: b.ctx.GetSessionVars().User.Hostname, - } - } - - for i := range v.Schema().Columns { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) - } - - return e, nil -} - -func (b *executorBuilder) buildMPPGather(v *plannercore.PhysicalTableReader) exec.Executor { - startTs, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - - gather := &MPPGather{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - is: b.is, - originalPlan: v.GetTablePlan(), - startTS: startTs, - mppQueryID: kv.MPPQueryID{QueryTs: getMPPQueryTS(b.ctx), LocalQueryID: getMPPQueryID(b.ctx), ServerID: domain.GetDomain(b.ctx).ServerID()}, - memTracker: memory.NewTracker(v.ID(), -1), - - columns: []*model.ColumnInfo{}, - virtualColumnIndex: []int{}, - virtualColumnRetFieldTypes: []*types.FieldType{}, - } - - gather.memTracker.AttachTo(b.ctx.GetSessionVars().StmtCtx.MemTracker) - - var hasVirtualCol bool - for _, col := range v.Schema().Columns { - if col.VirtualExpr != nil { - hasVirtualCol = true - break - } - } - - var isSingleDataSource bool - tableScans := v.GetTableScans() - if len(tableScans) == 1 { - isSingleDataSource = true - } - - // 1. hasVirtualCol: when got virtual column in TableScan, will generate plan like the following, - // and there will be no other operators in the MPP fragment. - // MPPGather - // ExchangeSender - // PhysicalTableScan - // 2. UnionScan: there won't be any operators like Join between UnionScan and TableScan. - // and UnionScan cannot push down to tiflash. - if !isSingleDataSource { - if hasVirtualCol || b.encounterUnionScan { - b.err = errors.Errorf("should only have one TableScan in MPP fragment(hasVirtualCol: %v, encounterUnionScan: %v)", hasVirtualCol, b.encounterUnionScan) - return nil - } - return gather - } - - // Setup MPPGather.table if isSingleDataSource. - // Virtual Column or UnionScan need to use it. - ts := tableScans[0] - gather.columns = ts.Columns - if hasVirtualCol { - gather.virtualColumnIndex, gather.virtualColumnRetFieldTypes = buildVirtualColumnInfo(gather.Schema(), gather.columns) - } - tbl, _ := b.is.TableByID(ts.Table.ID) - isPartition, physicalTableID := ts.IsPartition() - if isPartition { - // Only for static pruning partition table. - pt := tbl.(table.PartitionedTable) - tbl = pt.GetPartition(physicalTableID) - } - gather.table = tbl - return gather -} - -// buildTableReader builds a table reader executor. It first build a no range table reader, -// and then update it ranges from table scan plan. -func (b *executorBuilder) buildTableReader(v *plannercore.PhysicalTableReader) exec.Executor { - failpoint.Inject("checkUseMPP", func(val failpoint.Value) { - if !b.ctx.GetSessionVars().InRestrictedSQL && val.(bool) != useMPPExecution(b.ctx, v) { - if val.(bool) { - b.err = errors.New("expect mpp but not used") - } else { - b.err = errors.New("don't expect mpp but we used it") - } - failpoint.Return(nil) - } - }) - useMPP := useMPPExecution(b.ctx, v) - useTiFlashBatchCop := v.ReadReqType == plannercore.BatchCop - useTiFlash := useMPP || useTiFlashBatchCop - if useTiFlash { - if _, isTiDBZoneLabelSet := config.GetGlobalConfig().Labels[placement.DCLabelKey]; b.ctx.GetSessionVars().TiFlashReplicaRead != tiflash.AllReplicas && !isTiDBZoneLabelSet { - b.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("the variable tiflash_replica_read is ignored, because the entry TiDB[%s] does not set the zone attribute and tiflash_replica_read is '%s'", config.GetGlobalConfig().AdvertiseAddress, tiflash.GetTiFlashReplicaRead(b.ctx.GetSessionVars().TiFlashReplicaRead))) - } - } - if useMPP { - return b.buildMPPGather(v) - } - ts, err := v.GetTableScan() - if err != nil { - b.err = err - return nil - } - ret, err := buildNoRangeTableReader(b, v) - if err != nil { - b.err = err - return nil - } - if err = b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { - b.err = err - return nil - } - - if ret.table.Meta().TempTableType != model.TempTableNone { - ret.dummy = true - } - - ret.ranges = ts.Ranges - sctx := b.ctx.GetSessionVars().StmtCtx - sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) - - if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - return ret - } - // When isPartition is set, it means the union rewriting is done, so a partition reader is preferred. - if ok, _ := ts.IsPartition(); ok { - return ret - } - - pi := ts.Table.GetPartitionInfo() - if pi == nil { - return ret - } - - tmp, _ := b.is.TableByID(ts.Table.ID) - tbl := tmp.(table.PartitionedTable) - partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) - if err != nil { - b.err = err - return nil - } - if v.StoreType == kv.TiFlash { - sctx.IsTiFlash.Store(true) - } - - if len(partitions) == 0 { - return &TableDualExec{BaseExecutor: *ret.Base()} - } - - // Sort the partition is necessary to make the final multiple partition key ranges ordered. - slices.SortFunc(partitions, func(i, j table.PhysicalTable) int { - return cmp.Compare(i.GetPhysicalID(), j.GetPhysicalID()) - }) - ret.kvRangeBuilder = kvRangeBuilderFromRangeAndPartition{ - sctx: b.ctx, - partitions: partitions, - } - - return ret -} - -func buildIndexRangeForEachPartition(ctx sessionctx.Context, usedPartitions []table.PhysicalTable, contentPos []int64, - lookUpContent []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager) (map[int64][]*ranger.Range, error) { - contentBucket := make(map[int64][]*indexJoinLookUpContent) - for _, p := range usedPartitions { - contentBucket[p.GetPhysicalID()] = make([]*indexJoinLookUpContent, 0, 8) - } - for i, pos := range contentPos { - if _, ok := contentBucket[pos]; ok { - contentBucket[pos] = append(contentBucket[pos], lookUpContent[i]) - } - } - nextRange := make(map[int64][]*ranger.Range) - for _, p := range usedPartitions { - ranges, err := buildRangesForIndexJoin(ctx, contentBucket[p.GetPhysicalID()], indexRanges, keyOff2IdxOff, cwc) - if err != nil { - return nil, err - } - nextRange[p.GetPhysicalID()] = ranges - } - return nextRange, nil -} - -func getPartitionKeyColOffsets(keyColIDs []int64, pt table.PartitionedTable) []int { - keyColOffsets := make([]int, len(keyColIDs)) - for i, colID := range keyColIDs { - offset := -1 - for j, col := range pt.Cols() { - if colID == col.ID { - offset = j - break - } - } - if offset == -1 { - return nil - } - keyColOffsets[i] = offset - } - - t, ok := pt.(interface { - PartitionExpr() *tables.PartitionExpr - }) - if !ok { - return nil - } - pe := t.PartitionExpr() - if pe == nil { - return nil - } - - offsetMap := make(map[int]struct{}) - for _, offset := range keyColOffsets { - offsetMap[offset] = struct{}{} - } - for _, offset := range pe.ColumnOffset { - if _, ok := offsetMap[offset]; !ok { - return nil - } - } - return keyColOffsets -} - -func (builder *dataReaderBuilder) prunePartitionForInnerExecutor(tbl table.Table, partitionInfo *plannercore.PartitionInfo, - lookUpContent []*indexJoinLookUpContent) (usedPartition []table.PhysicalTable, canPrune bool, contentPos []int64, err error) { - partitionTbl := tbl.(table.PartitionedTable) - - // In index join, this is called by multiple goroutines simultaneously, but partitionPruning is not thread-safe. - // Use once.Do to avoid DATA RACE here. - // TODO: condition based pruning can be do in advance. - condPruneResult, err := builder.partitionPruning(partitionTbl, partitionInfo.PruningConds, partitionInfo.PartitionNames, partitionInfo.Columns, partitionInfo.ColumnNames) - if err != nil { - return nil, false, nil, err - } - - // recalculate key column offsets - if len(lookUpContent) == 0 { - return nil, false, nil, nil - } - if lookUpContent[0].keyColIDs == nil { - return nil, false, nil, plannercore.ErrInternal.GenWithStack("cannot get column IDs when dynamic pruning") - } - keyColOffsets := getPartitionKeyColOffsets(lookUpContent[0].keyColIDs, partitionTbl) - if len(keyColOffsets) == 0 { - return condPruneResult, false, nil, nil - } - - locateKey := make([]types.Datum, len(partitionTbl.Cols())) - partitions := make(map[int64]table.PhysicalTable) - contentPos = make([]int64, len(lookUpContent)) - for idx, content := range lookUpContent { - for i, data := range content.keys { - locateKey[keyColOffsets[i]] = data - } - p, err := partitionTbl.GetPartitionByRow(builder.ctx, locateKey) - if table.ErrNoPartitionForGivenValue.Equal(err) { - continue - } - if err != nil { - return nil, false, nil, err - } - if _, ok := partitions[p.GetPhysicalID()]; !ok { - partitions[p.GetPhysicalID()] = p - } - contentPos[idx] = p.GetPhysicalID() - } - - usedPartition = make([]table.PhysicalTable, 0, len(partitions)) - for _, p := range condPruneResult { - if _, ok := partitions[p.GetPhysicalID()]; ok { - usedPartition = append(usedPartition, p) - } - } - - // To make the final key ranges involving multiple partitions ordered. - slices.SortFunc(usedPartition, func(i, j table.PhysicalTable) int { - return cmp.Compare(i.GetPhysicalID(), j.GetPhysicalID()) - }) - return usedPartition, true, contentPos, nil -} - -func buildNoRangeIndexReader(b *executorBuilder, v *plannercore.PhysicalIndexReader) (*IndexReaderExecutor, error) { - dagReq, err := builder.ConstructDAGReq(b.ctx, v.IndexPlans, kv.TiKV) - if err != nil { - return nil, err - } - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - tbl, _ := b.is.TableByID(is.Table.ID) - isPartition, physicalTableID := is.IsPartition() - if isPartition { - pt := tbl.(table.PartitionedTable) - tbl = pt.GetPartition(physicalTableID) - } else { - physicalTableID = is.Table.ID - } - startTS, err := b.getSnapshotTS() - if err != nil { - return nil, err - } - paging := b.ctx.GetSessionVars().EnablePaging - e := &IndexReaderExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dagPB: dagReq, - startTS: startTS, - txnScope: b.txnScope, - readReplicaScope: b.readReplicaScope, - isStaleness: b.isStaleness, - netDataSize: v.GetNetDataSize(), - physicalTableID: physicalTableID, - table: tbl, - index: is.Index, - keepOrder: is.KeepOrder, - desc: is.Desc, - columns: is.Columns, - byItems: is.ByItems, - paging: paging, - corColInFilter: b.corColInDistPlan(v.IndexPlans), - corColInAccess: b.corColInAccess(v.IndexPlans[0]), - idxCols: is.IdxCols, - colLens: is.IdxColLens, - plans: v.IndexPlans, - outputColumns: v.OutputColumns, - } - - for _, col := range v.OutputColumns { - dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(col.Index)) - } - - return e, nil -} - -func (b *executorBuilder) buildIndexReader(v *plannercore.PhysicalIndexReader) exec.Executor { - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - if err := b.validCanReadTemporaryOrCacheTable(is.Table); err != nil { - b.err = err - return nil - } - - ret, err := buildNoRangeIndexReader(b, v) - if err != nil { - b.err = err - return nil - } - - if ret.table.Meta().TempTableType != model.TempTableNone { - ret.dummy = true - } - - ret.ranges = is.Ranges - sctx := b.ctx.GetSessionVars().StmtCtx - sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) - - if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - return ret - } - // When isPartition is set, it means the union rewriting is done, so a partition reader is preferred. - if ok, _ := is.IsPartition(); ok { - return ret - } - - pi := is.Table.GetPartitionInfo() - if pi == nil { - return ret - } - - if is.Index.Global { - tmp, ok := b.is.TableByID(ret.table.Meta().ID) - if !ok { - b.err = infoschema.ErrTableNotExists - return nil - } - tbl, ok := tmp.(table.PartitionedTable) - if !ok { - b.err = exeerrors.ErrBuildExecutor - return nil - } - ret.partitionIDMap, err = getPartitionIdsAfterPruning(b.ctx, tbl, &v.PartitionInfo) - if err != nil { - b.err = err - return nil - } - return ret - } - - tmp, _ := b.is.TableByID(is.Table.ID) - tbl := tmp.(table.PartitionedTable) - partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) - if err != nil { - b.err = err - return nil - } - ret.partitions = partitions - return ret -} - -func buildTableReq(b *executorBuilder, schemaLen int, plans []plannercore.PhysicalPlan) (dagReq *tipb.DAGRequest, val table.Table, err error) { - tableReq, err := builder.ConstructDAGReq(b.ctx, plans, kv.TiKV) - if err != nil { - return nil, nil, err - } - for i := 0; i < schemaLen; i++ { - tableReq.OutputOffsets = append(tableReq.OutputOffsets, uint32(i)) - } - ts := plans[0].(*plannercore.PhysicalTableScan) - tbl, _ := b.is.TableByID(ts.Table.ID) - isPartition, physicalTableID := ts.IsPartition() - if isPartition { - pt := tbl.(table.PartitionedTable) - tbl = pt.GetPartition(physicalTableID) - } - return tableReq, tbl, err -} - -// buildIndexReq is designed to create a DAG for index request. -// If len(ByItems) != 0 means index request should return related columns -// to sort result rows in TiDB side for parition tables. -func buildIndexReq(ctx sessionctx.Context, columns []*model.IndexColumn, handleLen int, plans []plannercore.PhysicalPlan) (dagReq *tipb.DAGRequest, err error) { - indexReq, err := builder.ConstructDAGReq(ctx, plans, kv.TiKV) - if err != nil { - return nil, err - } - - indexReq.OutputOffsets = []uint32{} - idxScan := plans[0].(*plannercore.PhysicalIndexScan) - if len(idxScan.ByItems) != 0 { - schema := idxScan.Schema() - for _, item := range idxScan.ByItems { - c, ok := item.Expr.(*expression.Column) - if !ok { - return nil, errors.Errorf("Not support non-column in orderBy pushed down") - } - find := false - for i, schemaColumn := range schema.Columns { - if schemaColumn.ID == c.ID { - indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(i)) - find = true - break - } - } - if !find { - return nil, errors.Errorf("Not found order by related columns in indexScan.schema") - } - } - } - - for i := 0; i < handleLen; i++ { - indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(len(columns)+i)) - } - - if idxScan.NeedExtraOutputCol() { - // need add one more column for pid or physical table id - indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(len(columns)+handleLen)) - } - return indexReq, err -} - -func buildNoRangeIndexLookUpReader(b *executorBuilder, v *plannercore.PhysicalIndexLookUpReader) (*IndexLookUpExecutor, error) { - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - var handleLen int - if len(v.CommonHandleCols) != 0 { - handleLen = len(v.CommonHandleCols) - } else { - handleLen = 1 - } - indexReq, err := buildIndexReq(b.ctx, is.Index.Columns, handleLen, v.IndexPlans) - if err != nil { - return nil, err - } - indexPaging := false - if v.Paging { - indexPaging = true - } - tableReq, tbl, err := buildTableReq(b, v.Schema().Len(), v.TablePlans) - if err != nil { - return nil, err - } - ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) - startTS, err := b.getSnapshotTS() - if err != nil { - return nil, err - } - - readerBuilder, err := b.newDataReaderBuilder(nil) - if err != nil { - return nil, err - } - - e := &IndexLookUpExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dagPB: indexReq, - startTS: startTS, - table: tbl, - index: is.Index, - keepOrder: is.KeepOrder, - byItems: is.ByItems, - desc: is.Desc, - tableRequest: tableReq, - columns: ts.Columns, - indexPaging: indexPaging, - dataReaderBuilder: readerBuilder, - corColInIdxSide: b.corColInDistPlan(v.IndexPlans), - corColInTblSide: b.corColInDistPlan(v.TablePlans), - corColInAccess: b.corColInAccess(v.IndexPlans[0]), - idxCols: is.IdxCols, - colLens: is.IdxColLens, - idxPlans: v.IndexPlans, - tblPlans: v.TablePlans, - PushedLimit: v.PushedLimit, - idxNetDataSize: v.GetAvgTableRowSize(), - avgRowSize: v.GetAvgTableRowSize(), - } - - if v.ExtraHandleCol != nil { - e.handleIdx = append(e.handleIdx, v.ExtraHandleCol.Index) - e.handleCols = []*expression.Column{v.ExtraHandleCol} - } else { - for _, handleCol := range v.CommonHandleCols { - e.handleIdx = append(e.handleIdx, handleCol.Index) - } - e.handleCols = v.CommonHandleCols - e.primaryKeyIndex = tables.FindPrimaryIndex(tbl.Meta()) - } - return e, nil -} - -func (b *executorBuilder) buildIndexLookUpReader(v *plannercore.PhysicalIndexLookUpReader) exec.Executor { - if b.Ti != nil { - b.Ti.UseTableLookUp.Store(true) - } - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - if err := b.validCanReadTemporaryOrCacheTable(is.Table); err != nil { - b.err = err - return nil - } - - ret, err := buildNoRangeIndexLookUpReader(b, v) - if err != nil { - b.err = err - return nil - } - - if ret.table.Meta().TempTableType != model.TempTableNone { - ret.dummy = true - } - - ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) - - ret.ranges = is.Ranges - executor_metrics.ExecutorCounterIndexLookUpExecutor.Inc() - - sctx := b.ctx.GetSessionVars().StmtCtx - sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) - sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) - - if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - return ret - } - - if pi := is.Table.GetPartitionInfo(); pi == nil { - return ret - } - - if is.Index.Global || len(is.ByItems) != 0 { - tmp, ok := b.is.TableByID(ts.Table.ID) - if !ok { - b.err = err - return nil - } - tbl, ok := tmp.(table.PartitionedTable) - if !ok { - b.err = exeerrors.ErrBuildExecutor - return nil - } - ret.partitionIDMap, err = getPartitionIdsAfterPruning(b.ctx, tbl, &v.PartitionInfo) - if err != nil { - b.err = err - return nil - } - - if is.Index.Global { - return ret - } - } - if ok, _ := is.IsPartition(); ok { - // Already pruned when translated to logical union. - return ret - } - - tmp, _ := b.is.TableByID(is.Table.ID) - tbl := tmp.(table.PartitionedTable) - partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) - if err != nil { - b.err = err - return nil - } - ret.partitionTableMode = true - ret.prunedPartitions = partitions - return ret -} - -func buildNoRangeIndexMergeReader(b *executorBuilder, v *plannercore.PhysicalIndexMergeReader) (*IndexMergeReaderExecutor, error) { - partialPlanCount := len(v.PartialPlans) - partialReqs := make([]*tipb.DAGRequest, 0, partialPlanCount) - partialDataSizes := make([]float64, 0, partialPlanCount) - indexes := make([]*model.IndexInfo, 0, partialPlanCount) - descs := make([]bool, 0, partialPlanCount) - ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) - isCorColInPartialFilters := make([]bool, 0, partialPlanCount) - isCorColInPartialAccess := make([]bool, 0, partialPlanCount) - for i := 0; i < partialPlanCount; i++ { - var tempReq *tipb.DAGRequest - var err error - - if is, ok := v.PartialPlans[i][0].(*plannercore.PhysicalIndexScan); ok { - tempReq, err = buildIndexReq(b.ctx, is.Index.Columns, ts.HandleCols.NumCols(), v.PartialPlans[i]) - descs = append(descs, is.Desc) - indexes = append(indexes, is.Index) - } else { - ts := v.PartialPlans[i][0].(*plannercore.PhysicalTableScan) - tempReq, _, err = buildTableReq(b, len(ts.Columns), v.PartialPlans[i]) - descs = append(descs, ts.Desc) - indexes = append(indexes, nil) - } - if err != nil { - return nil, err - } - collect := false - tempReq.CollectRangeCounts = &collect - partialReqs = append(partialReqs, tempReq) - isCorColInPartialFilters = append(isCorColInPartialFilters, b.corColInDistPlan(v.PartialPlans[i])) - isCorColInPartialAccess = append(isCorColInPartialAccess, b.corColInAccess(v.PartialPlans[i][0])) - partialDataSizes = append(partialDataSizes, v.GetPartialReaderNetDataSize(v.PartialPlans[i][0])) - } - tableReq, tblInfo, err := buildTableReq(b, v.Schema().Len(), v.TablePlans) - isCorColInTableFilter := b.corColInDistPlan(v.TablePlans) - if err != nil { - return nil, err - } - startTS, err := b.getSnapshotTS() - if err != nil { - return nil, err - } - - readerBuilder, err := b.newDataReaderBuilder(nil) - if err != nil { - return nil, err - } - - paging := b.ctx.GetSessionVars().EnablePaging - e := &IndexMergeReaderExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - dagPBs: partialReqs, - startTS: startTS, - table: tblInfo, - indexes: indexes, - descs: descs, - tableRequest: tableReq, - columns: ts.Columns, - partialPlans: v.PartialPlans, - tblPlans: v.TablePlans, - partialNetDataSizes: partialDataSizes, - dataAvgRowSize: v.GetAvgTableRowSize(), - dataReaderBuilder: readerBuilder, - paging: paging, - handleCols: v.HandleCols, - isCorColInPartialFilters: isCorColInPartialFilters, - isCorColInTableFilter: isCorColInTableFilter, - isCorColInPartialAccess: isCorColInPartialAccess, - isIntersection: v.IsIntersectionType, - byItems: v.ByItems, - pushedLimit: v.PushedLimit, - keepOrder: v.KeepOrder, - } - collectTable := false - e.tableRequest.CollectRangeCounts = &collectTable - return e, nil -} - -func (b *executorBuilder) buildIndexMergeReader(v *plannercore.PhysicalIndexMergeReader) exec.Executor { - if b.Ti != nil { - b.Ti.UseIndexMerge = true - b.Ti.UseTableLookUp.Store(true) - } - ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) - if err := b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { - b.err = err - return nil - } - - ret, err := buildNoRangeIndexMergeReader(b, v) - if err != nil { - b.err = err - return nil - } - ret.ranges = make([][]*ranger.Range, 0, len(v.PartialPlans)) - sctx := b.ctx.GetSessionVars().StmtCtx - for i := 0; i < len(v.PartialPlans); i++ { - if is, ok := v.PartialPlans[i][0].(*plannercore.PhysicalIndexScan); ok { - ret.ranges = append(ret.ranges, is.Ranges) - sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) - } else { - ret.ranges = append(ret.ranges, v.PartialPlans[i][0].(*plannercore.PhysicalTableScan).Ranges) - if ret.table.Meta().IsCommonHandle { - tblInfo := ret.table.Meta() - sctx.IndexNames = append(sctx.IndexNames, tblInfo.Name.O+":"+tables.FindPrimaryIndex(tblInfo).Name.O) - } - } - } - sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) - executor_metrics.ExecutorCounterIndexMergeReaderExecutor.Inc() - - if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - return ret - } - - if pi := ts.Table.GetPartitionInfo(); pi == nil { - return ret - } - - tmp, _ := b.is.TableByID(ts.Table.ID) - partitions, err := partitionPruning(b.ctx, tmp.(table.PartitionedTable), v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) - if err != nil { - b.err = err - return nil - } - ret.partitionTableMode, ret.prunedPartitions = true, partitions - return ret -} - -// dataReaderBuilder build an executor. -// The executor can be used to read data in the ranges which are constructed by datums. -// Differences from executorBuilder: -// 1. dataReaderBuilder calculate data range from argument, rather than plan. -// 2. the result executor is already opened. -type dataReaderBuilder struct { - plannercore.Plan - *executorBuilder - - selectResultHook // for testing - once struct { - sync.Once - condPruneResult []table.PhysicalTable - err error - } -} - -type mockPhysicalIndexReader struct { - plannercore.PhysicalPlan - - e exec.Executor -} - -// MemoryUsage of mockPhysicalIndexReader is only for testing -func (*mockPhysicalIndexReader) MemoryUsage() (sum int64) { - return -} - -func (builder *dataReaderBuilder) buildExecutorForIndexJoin(ctx context.Context, lookUpContents []*indexJoinLookUpContent, - indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - return builder.buildExecutorForIndexJoinInternal(ctx, builder.Plan, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) -} - -func (builder *dataReaderBuilder) buildExecutorForIndexJoinInternal(ctx context.Context, plan plannercore.Plan, lookUpContents []*indexJoinLookUpContent, - indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - switch v := plan.(type) { - case *plannercore.PhysicalTableReader: - return builder.buildTableReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) - case *plannercore.PhysicalIndexReader: - return builder.buildIndexReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - case *plannercore.PhysicalIndexLookUpReader: - return builder.buildIndexLookUpReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - case *plannercore.PhysicalUnionScan: - return builder.buildUnionScanForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) - // The inner child of IndexJoin might be Projection when a combination of the following conditions is true: - // 1. The inner child fetch data using indexLookupReader - // 2. PK is not handle - // 3. The inner child needs to keep order - // In this case, an extra column tidb_rowid will be appended in the output result of IndexLookupReader(see copTask.doubleReadNeedProj). - // Then we need a Projection upon IndexLookupReader to prune the redundant column. - case *plannercore.PhysicalProjection: - return builder.buildProjectionForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - // Need to support physical selection because after PR 16389, TiDB will push down all the expr supported by TiKV or TiFlash - // in predicate push down stage, so if there is an expr which only supported by TiFlash, a physical selection will be added after index read - case *plannercore.PhysicalSelection: - childExec, err := builder.buildExecutorForIndexJoinInternal(ctx, v.Children()[0], lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) - if err != nil { - return nil, err - } - exec := &SelectionExec{ - BaseExecutor: exec.NewBaseExecutor(builder.ctx, v.Schema(), v.ID(), childExec), - filters: v.Conditions, - } - err = exec.open(ctx) - return exec, err - case *mockPhysicalIndexReader: - return v.e, nil - } - return nil, errors.New("Wrong plan type for dataReaderBuilder") -} - -func (builder *dataReaderBuilder) buildUnionScanForIndexJoin(ctx context.Context, v *plannercore.PhysicalUnionScan, - values []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, - cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - childBuilder, err := builder.newDataReaderBuilder(v.Children()[0]) - if err != nil { - return nil, err - } - - reader, err := childBuilder.buildExecutorForIndexJoin(ctx, values, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) - if err != nil { - return nil, err - } - - ret := builder.buildUnionScanFromReader(reader, v) - if us, ok := ret.(*UnionScanExec); ok { - err = us.open(ctx) - } - return ret, err -} - -func (builder *dataReaderBuilder) buildTableReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalTableReader, - lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, - cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - e, err := buildNoRangeTableReader(builder.executorBuilder, v) - if !canReorderHandles { - // `canReorderHandles` is set to false only in IndexMergeJoin. IndexMergeJoin will trigger a dead loop problem - // when enabling paging(tidb/issues/35831). But IndexMergeJoin is not visible to the user and is deprecated - // for now. Thus, we disable paging here. - e.paging = false - } - if err != nil { - return nil, err - } - tbInfo := e.table.Meta() - if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - if v.IsCommonHandle { - kvRanges, err := buildKvRangesForIndexJoin(e.Ctx(), getPhysicalTableID(e.table), -1, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - if err != nil { - return nil, err - } - return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) - } - handles, _ := dedupHandles(lookUpContents) - return builder.buildTableReaderFromHandles(ctx, e, handles, canReorderHandles) - } - tbl, _ := builder.is.TableByID(tbInfo.ID) - pt := tbl.(table.PartitionedTable) - partitionInfo := &v.PartitionInfo - usedPartitionList, err := builder.partitionPruning(pt, partitionInfo.PruningConds, partitionInfo.PartitionNames, partitionInfo.Columns, partitionInfo.ColumnNames) - if err != nil { - return nil, err - } - usedPartitions := make(map[int64]table.PhysicalTable, len(usedPartitionList)) - for _, p := range usedPartitionList { - usedPartitions[p.GetPhysicalID()] = p - } - var kvRanges []kv.KeyRange - var keyColOffsets []int - if len(lookUpContents) > 0 { - keyColOffsets = getPartitionKeyColOffsets(lookUpContents[0].keyColIDs, pt) - } - if v.IsCommonHandle { - if len(keyColOffsets) > 0 { - locateKey := make([]types.Datum, len(pt.Cols())) - kvRanges = make([]kv.KeyRange, 0, len(lookUpContents)) - // lookUpContentsByPID groups lookUpContents by pid(partition) so that kv ranges for same partition can be merged. - lookUpContentsByPID := make(map[int64][]*indexJoinLookUpContent) - for _, content := range lookUpContents { - for i, data := range content.keys { - locateKey[keyColOffsets[i]] = data - } - p, err := pt.GetPartitionByRow(e.Ctx(), locateKey) - if table.ErrNoPartitionForGivenValue.Equal(err) { - continue - } - if err != nil { - return nil, err - } - pid := p.GetPhysicalID() - if _, ok := usedPartitions[pid]; !ok { - continue - } - lookUpContentsByPID[pid] = append(lookUpContentsByPID[pid], content) - } - for pid, contents := range lookUpContentsByPID { - // buildKvRanges for each partition. - tmp, err := buildKvRangesForIndexJoin(e.Ctx(), pid, -1, contents, indexRanges, keyOff2IdxOff, cwc, nil, interruptSignal) - if err != nil { - return nil, err - } - kvRanges = append(kvRanges, tmp...) - } - } else { - kvRanges = make([]kv.KeyRange, 0, len(usedPartitions)*len(lookUpContents)) - for _, p := range usedPartitionList { - tmp, err := buildKvRangesForIndexJoin(e.Ctx(), p.GetPhysicalID(), -1, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - if err != nil { - return nil, err - } - kvRanges = append(tmp, kvRanges...) - } - } - // The key ranges should be ordered. - slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { - return bytes.Compare(i.StartKey, j.StartKey) - }) - return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) - } - - handles, lookUpContents := dedupHandles(lookUpContents) - - if len(keyColOffsets) > 0 { - locateKey := make([]types.Datum, len(pt.Cols())) - kvRanges = make([]kv.KeyRange, 0, len(lookUpContents)) - for _, content := range lookUpContents { - for i, data := range content.keys { - locateKey[keyColOffsets[i]] = data - } - p, err := pt.GetPartitionByRow(e.Ctx(), locateKey) - if table.ErrNoPartitionForGivenValue.Equal(err) { - continue - } - if err != nil { - return nil, err - } - pid := p.GetPhysicalID() - if _, ok := usedPartitions[pid]; !ok { - continue - } - handle := kv.IntHandle(content.keys[0].GetInt64()) - ranges, _ := distsql.TableHandlesToKVRanges(pid, []kv.Handle{handle}) - kvRanges = append(kvRanges, ranges...) - } - } else { - for _, p := range usedPartitionList { - ranges, _ := distsql.TableHandlesToKVRanges(p.GetPhysicalID(), handles) - kvRanges = append(kvRanges, ranges...) - } - } - - // The key ranges should be ordered. - slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { - return bytes.Compare(i.StartKey, j.StartKey) - }) - return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) -} - -func dedupHandles(lookUpContents []*indexJoinLookUpContent) ([]kv.Handle, []*indexJoinLookUpContent) { - handles := make([]kv.Handle, 0, len(lookUpContents)) - validLookUpContents := make([]*indexJoinLookUpContent, 0, len(lookUpContents)) - for _, content := range lookUpContents { - isValidHandle := true - handle := kv.IntHandle(content.keys[0].GetInt64()) - for _, key := range content.keys { - if handle.IntValue() != key.GetInt64() { - isValidHandle = false - break - } - } - if isValidHandle { - handles = append(handles, handle) - validLookUpContents = append(validLookUpContents, content) - } - } - return handles, validLookUpContents -} - -type kvRangeBuilderFromRangeAndPartition struct { - sctx sessionctx.Context - partitions []table.PhysicalTable -} - -func (h kvRangeBuilderFromRangeAndPartition) buildKeyRangeSeparately(ranges []*ranger.Range) ([]int64, [][]kv.KeyRange, error) { - ret := make([][]kv.KeyRange, len(h.partitions)) - pids := make([]int64, 0, len(h.partitions)) - for i, p := range h.partitions { - pid := p.GetPhysicalID() - pids = append(pids, pid) - meta := p.Meta() - if len(ranges) == 0 { - continue - } - kvRange, err := distsql.TableHandleRangesToKVRanges(h.sctx.GetSessionVars().StmtCtx, []int64{pid}, meta != nil && meta.IsCommonHandle, ranges) - if err != nil { - return nil, nil, err - } - ret[i] = kvRange.AppendSelfTo(ret[i]) - } - return pids, ret, nil -} - -func (h kvRangeBuilderFromRangeAndPartition) buildKeyRange(ranges []*ranger.Range) ([][]kv.KeyRange, error) { - ret := make([][]kv.KeyRange, len(h.partitions)) - if len(ranges) == 0 { - return ret, nil - } - for i, p := range h.partitions { - pid := p.GetPhysicalID() - meta := p.Meta() - kvRange, err := distsql.TableHandleRangesToKVRanges(h.sctx.GetSessionVars().StmtCtx, []int64{pid}, meta != nil && meta.IsCommonHandle, ranges) - if err != nil { - return nil, err - } - ret[i] = kvRange.AppendSelfTo(ret[i]) - } - return ret, nil -} - -// newClosestReadAdjuster let the request be sent to closest replica(within the same zone) -// if response size exceeds certain threshold. -func newClosestReadAdjuster(ctx sessionctx.Context, req *kv.Request, netDataSize float64) kv.CoprRequestAdjuster { - if req.ReplicaRead != kv.ReplicaReadClosestAdaptive { - return nil - } - return func(req *kv.Request, copTaskCount int) bool { - // copTaskCount is the number of coprocessor requests - if int64(netDataSize/float64(copTaskCount)) >= ctx.GetSessionVars().ReplicaClosestReadThreshold { - req.MatchStoreLabels = append(req.MatchStoreLabels, &metapb.StoreLabel{ - Key: placement.DCLabelKey, - Value: config.GetTxnScopeFromConfig(), - }) - return true - } - // reset to read from leader when the data size is small. - req.ReplicaRead = kv.ReplicaReadLeader - return false - } -} - -func (builder *dataReaderBuilder) buildTableReaderBase(ctx context.Context, e *TableReaderExecutor, reqBuilderWithRange distsql.RequestBuilder) (*TableReaderExecutor, error) { - startTS, err := builder.getSnapshotTS() - if err != nil { - return nil, err - } - kvReq, err := reqBuilderWithRange. - SetDAGRequest(e.dagPB). - SetStartTS(startTS). - SetDesc(e.desc). - SetKeepOrder(e.keepOrder). - SetTxnScope(e.txnScope). - SetReadReplicaScope(e.readReplicaScope). - SetIsStaleness(e.isStaleness). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &reqBuilderWithRange.Request, e.netDataSize)). - SetPaging(e.paging). - SetConnID(e.Ctx().GetSessionVars().ConnectionID). - Build() - if err != nil { - return nil, err - } - e.kvRanges = kvReq.KeyRanges.AppendSelfTo(e.kvRanges) - e.resultHandler = &tableResultHandler{} - result, err := builder.SelectResult(ctx, builder.ctx, kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) - if err != nil { - return nil, err - } - e.resultHandler.open(nil, result) - return e, nil -} - -func (builder *dataReaderBuilder) buildTableReaderFromHandles(ctx context.Context, e *TableReaderExecutor, handles []kv.Handle, canReorderHandles bool) (*TableReaderExecutor, error) { - if canReorderHandles { - slices.SortFunc(handles, func(i, j kv.Handle) int { - return i.Compare(j) - }) - } - var b distsql.RequestBuilder - if len(handles) > 0 { - if _, ok := handles[0].(kv.PartitionHandle); ok { - b.SetPartitionsAndHandles(handles) - } else { - b.SetTableHandles(getPhysicalTableID(e.table), handles) - } - } else { - b.SetKeyRanges(nil) - } - return builder.buildTableReaderBase(ctx, e, b) -} - -func (builder *dataReaderBuilder) buildTableReaderFromKvRanges(ctx context.Context, e *TableReaderExecutor, ranges []kv.KeyRange) (exec.Executor, error) { - var b distsql.RequestBuilder - b.SetKeyRanges(ranges) - return builder.buildTableReaderBase(ctx, e, b) -} - -func (builder *dataReaderBuilder) buildIndexReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalIndexReader, - lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memoryTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - e, err := buildNoRangeIndexReader(builder.executorBuilder, v) - if err != nil { - return nil, err - } - tbInfo := e.table.Meta() - if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - kvRanges, err := buildKvRangesForIndexJoin(e.Ctx(), e.physicalTableID, e.index.ID, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memoryTracker, interruptSignal) - if err != nil { - return nil, err - } - err = e.open(ctx, kvRanges) - return e, err - } - - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - if is.Index.Global { - tmp, ok := builder.is.TableByID(tbInfo.ID) - if !ok { - return nil, infoschema.ErrTableNotExists - } - tbl, ok := tmp.(table.PartitionedTable) - if !ok { - return nil, exeerrors.ErrBuildExecutor - } - e.partitionIDMap, err = getPartitionIdsAfterPruning(builder.ctx, tbl, &v.PartitionInfo) - if err != nil { - return nil, err - } - - if e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc); err != nil { - return nil, err - } - if err := e.Open(ctx); err != nil { - return nil, err - } - return e, nil - } - - tbl, _ := builder.executorBuilder.is.TableByID(tbInfo.ID) - usedPartition, canPrune, contentPos, err := builder.prunePartitionForInnerExecutor(tbl, &v.PartitionInfo, lookUpContents) - if err != nil { - return nil, err - } - if len(usedPartition) != 0 { - if canPrune { - rangeMap, err := buildIndexRangeForEachPartition(e.Ctx(), usedPartition, contentPos, lookUpContents, indexRanges, keyOff2IdxOff, cwc) - if err != nil { - return nil, err - } - e.partitions = usedPartition - e.ranges = indexRanges - e.partRangeMap = rangeMap - } else { - e.partitions = usedPartition - if e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc); err != nil { - return nil, err - } - } - if err := e.Open(ctx); err != nil { - return nil, err - } - return e, nil - } - ret := &TableDualExec{BaseExecutor: *e.Base()} - err = ret.Open(ctx) - return ret, err -} - -func (builder *dataReaderBuilder) buildIndexLookUpReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalIndexLookUpReader, - lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - if builder.Ti != nil { - builder.Ti.UseTableLookUp.Store(true) - } - e, err := buildNoRangeIndexLookUpReader(builder.executorBuilder, v) - if err != nil { - return nil, err - } - - tbInfo := e.table.Meta() - if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - e.kvRanges, err = buildKvRangesForIndexJoin(e.Ctx(), getPhysicalTableID(e.table), e.index.ID, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) - if err != nil { - return nil, err - } - err = e.open(ctx) - return e, err - } - - is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) - ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) - if is.Index.Global { - tmp, ok := builder.is.TableByID(ts.Table.ID) - if !ok { - return nil, infoschema.ErrTableNotExists - } - tbl, ok := tmp.(table.PartitionedTable) - if !ok { - return nil, exeerrors.ErrBuildExecutor - } - e.partitionIDMap, err = getPartitionIdsAfterPruning(builder.ctx, tbl, &v.PartitionInfo) - if err != nil { - return nil, err - } - e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc) - if err != nil { - return nil, err - } - if err := e.Open(ctx); err != nil { - return nil, err - } - return e, err - } - - tbl, _ := builder.executorBuilder.is.TableByID(tbInfo.ID) - usedPartition, canPrune, contentPos, err := builder.prunePartitionForInnerExecutor(tbl, &v.PartitionInfo, lookUpContents) - if err != nil { - return nil, err - } - if len(usedPartition) != 0 { - if canPrune { - rangeMap, err := buildIndexRangeForEachPartition(e.Ctx(), usedPartition, contentPos, lookUpContents, indexRanges, keyOff2IdxOff, cwc) - if err != nil { - return nil, err - } - e.prunedPartitions = usedPartition - e.ranges = indexRanges - e.partitionRangeMap = rangeMap - } else { - e.prunedPartitions = usedPartition - e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc) - if err != nil { - return nil, err - } - } - e.partitionTableMode = true - if err := e.Open(ctx); err != nil { - return nil, err - } - return e, err - } - ret := &TableDualExec{BaseExecutor: *e.Base()} - err = ret.Open(ctx) - return ret, err -} - -func (builder *dataReaderBuilder) buildProjectionForIndexJoin(ctx context.Context, v *plannercore.PhysicalProjection, - lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { - var ( - childExec exec.Executor - err error - ) - switch op := v.Children()[0].(type) { - case *plannercore.PhysicalIndexLookUpReader: - if childExec, err = builder.buildIndexLookUpReaderForIndexJoin(ctx, op, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal); err != nil { - return nil, err - } - case *plannercore.PhysicalTableReader: - if childExec, err = builder.buildTableReaderForIndexJoin(ctx, op, lookUpContents, indexRanges, keyOff2IdxOff, cwc, true, memTracker, interruptSignal); err != nil { - return nil, err - } - default: - return nil, errors.Errorf("inner child of Projection should be IndexLookupReader/TableReader, but got %T", v.Children()[0]) - } - - e := &ProjectionExec{ - BaseExecutor: exec.NewBaseExecutor(builder.ctx, v.Schema(), v.ID(), childExec), - numWorkers: int64(builder.ctx.GetSessionVars().ProjectionConcurrency()), - evaluatorSuit: expression.NewEvaluatorSuite(v.Exprs, v.AvoidColumnEvaluator), - calculateNoDelay: v.CalculateNoDelay, - } - - // If the calculation row count for this Projection operator is smaller - // than a Chunk size, we turn back to the un-parallel Projection - // implementation to reduce the goroutine overhead. - if int64(v.StatsCount()) < int64(builder.ctx.GetSessionVars().MaxChunkSize) { - e.numWorkers = 0 - } - err = e.open(ctx) - - return e, err -} - -// buildRangesForIndexJoin builds kv ranges for index join when the inner plan is index scan plan. -func buildRangesForIndexJoin(ctx sessionctx.Context, lookUpContents []*indexJoinLookUpContent, - ranges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager) ([]*ranger.Range, error) { - retRanges := make([]*ranger.Range, 0, len(ranges)*len(lookUpContents)) - lastPos := len(ranges[0].LowVal) - 1 - tmpDatumRanges := make([]*ranger.Range, 0, len(lookUpContents)) - for _, content := range lookUpContents { - for _, ran := range ranges { - for keyOff, idxOff := range keyOff2IdxOff { - ran.LowVal[idxOff] = content.keys[keyOff] - ran.HighVal[idxOff] = content.keys[keyOff] - } - } - if cwc == nil { - // A deep copy is need here because the old []*range.Range is overwriten - for _, ran := range ranges { - retRanges = append(retRanges, ran.Clone()) - } - continue - } - nextColRanges, err := cwc.BuildRangesByRow(ctx, content.row) - if err != nil { - return nil, err - } - for _, nextColRan := range nextColRanges { - for _, ran := range ranges { - ran.LowVal[lastPos] = nextColRan.LowVal[0] - ran.HighVal[lastPos] = nextColRan.HighVal[0] - ran.LowExclude = nextColRan.LowExclude - ran.HighExclude = nextColRan.HighExclude - ran.Collators = nextColRan.Collators - tmpDatumRanges = append(tmpDatumRanges, ran.Clone()) - } - } - } - - if cwc == nil { - return retRanges, nil - } - - return ranger.UnionRanges(ctx, tmpDatumRanges, true) -} - -// buildKvRangesForIndexJoin builds kv ranges for index join when the inner plan is index scan plan. -func buildKvRangesForIndexJoin(ctx sessionctx.Context, tableID, indexID int64, lookUpContents []*indexJoinLookUpContent, - ranges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (_ []kv.KeyRange, err error) { - kvRanges := make([]kv.KeyRange, 0, len(ranges)*len(lookUpContents)) - if len(ranges) == 0 { - return []kv.KeyRange{}, nil - } - lastPos := len(ranges[0].LowVal) - 1 - sc := ctx.GetSessionVars().StmtCtx - tmpDatumRanges := make([]*ranger.Range, 0, len(lookUpContents)) - for _, content := range lookUpContents { - for _, ran := range ranges { - for keyOff, idxOff := range keyOff2IdxOff { - ran.LowVal[idxOff] = content.keys[keyOff] - ran.HighVal[idxOff] = content.keys[keyOff] - } - } - if cwc == nil { - // Index id is -1 means it's a common handle. - var tmpKvRanges *kv.KeyRanges - var err error - if indexID == -1 { - tmpKvRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{tableID}, ranges) - } else { - tmpKvRanges, err = distsql.IndexRangesToKVRangesWithInterruptSignal(sc, tableID, indexID, ranges, memTracker, interruptSignal) - } - if err != nil { - return nil, err - } - kvRanges = tmpKvRanges.AppendSelfTo(kvRanges) - continue - } - nextColRanges, err := cwc.BuildRangesByRow(ctx, content.row) - if err != nil { - return nil, err - } - for _, nextColRan := range nextColRanges { - for _, ran := range ranges { - ran.LowVal[lastPos] = nextColRan.LowVal[0] - ran.HighVal[lastPos] = nextColRan.HighVal[0] - ran.LowExclude = nextColRan.LowExclude - ran.HighExclude = nextColRan.HighExclude - ran.Collators = nextColRan.Collators - tmpDatumRanges = append(tmpDatumRanges, ran.Clone()) - } - } - } - if len(kvRanges) != 0 && memTracker != nil { - memTracker.Consume(int64(2 * cap(kvRanges[0].StartKey) * len(kvRanges))) - } - if len(tmpDatumRanges) != 0 && memTracker != nil { - memTracker.Consume(2 * int64(len(tmpDatumRanges)) * types.EstimatedMemUsage(tmpDatumRanges[0].LowVal, len(tmpDatumRanges))) - } - if cwc == nil { - slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { - return bytes.Compare(i.StartKey, j.StartKey) - }) - return kvRanges, nil - } - - tmpDatumRanges, err = ranger.UnionRanges(ctx, tmpDatumRanges, true) - if err != nil { - return nil, err - } - // Index id is -1 means it's a common handle. - if indexID == -1 { - tmpKeyRanges, err := distsql.CommonHandleRangesToKVRanges(ctx.GetSessionVars().StmtCtx, []int64{tableID}, tmpDatumRanges) - return tmpKeyRanges.FirstPartitionRange(), err - } - tmpKeyRanges, err := distsql.IndexRangesToKVRangesWithInterruptSignal(ctx.GetSessionVars().StmtCtx, tableID, indexID, tmpDatumRanges, memTracker, interruptSignal) - return tmpKeyRanges.FirstPartitionRange(), err -} - -func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) exec.Executor { - childExec := b.build(v.Children()[0]) - if b.err != nil { - return nil - } - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) - groupByItems := make([]expression.Expression, 0, len(v.PartitionBy)) - for _, item := range v.PartitionBy { - groupByItems = append(groupByItems, item.Col) - } - orderByCols := make([]*expression.Column, 0, len(v.OrderBy)) - for _, item := range v.OrderBy { - orderByCols = append(orderByCols, item.Col) - } - windowFuncs := make([]aggfuncs.AggFunc, 0, len(v.WindowFuncDescs)) - partialResults := make([]aggfuncs.PartialResult, 0, len(v.WindowFuncDescs)) - resultColIdx := v.Schema().Len() - len(v.WindowFuncDescs) - for _, desc := range v.WindowFuncDescs { - aggDesc, err := aggregation.NewAggFuncDescForWindowFunc(b.ctx, desc, false) - if err != nil { - b.err = err - return nil - } - agg := aggfuncs.BuildWindowFunctions(b.ctx, aggDesc, resultColIdx, orderByCols) - windowFuncs = append(windowFuncs, agg) - partialResult, _ := agg.AllocPartialResult() - partialResults = append(partialResults, partialResult) - resultColIdx++ - } - - var err error - if b.ctx.GetSessionVars().EnablePipelinedWindowExec { - exec := &PipelinedWindowExec{ - BaseExecutor: base, - groupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, groupByItems), - numWindowFuncs: len(v.WindowFuncDescs), - } - - exec.windowFuncs = windowFuncs - exec.partialResults = partialResults - if v.Frame == nil { - exec.start = &plannercore.FrameBound{ - Type: ast.Preceding, - UnBounded: true, - } - exec.end = &plannercore.FrameBound{ - Type: ast.Following, - UnBounded: true, - } - } else { - exec.start = v.Frame.Start - exec.end = v.Frame.End - if v.Frame.Type == ast.Ranges { - cmpResult := int64(-1) - if len(v.OrderBy) > 0 && v.OrderBy[0].Desc { - cmpResult = 1 - } - exec.orderByCols = orderByCols - exec.expectedCmpResult = cmpResult - exec.isRangeFrame = true - err = exec.start.UpdateCompareCols(b.ctx, exec.orderByCols) - if err != nil { - return nil - } - err = exec.end.UpdateCompareCols(b.ctx, exec.orderByCols) - if err != nil { - return nil - } - } - } - return exec - } - var processor windowProcessor - if v.Frame == nil { - processor = &aggWindowProcessor{ - windowFuncs: windowFuncs, - partialResults: partialResults, - } - } else if v.Frame.Type == ast.Rows { - processor = &rowFrameWindowProcessor{ - windowFuncs: windowFuncs, - partialResults: partialResults, - start: v.Frame.Start, - end: v.Frame.End, - } - } else { - cmpResult := int64(-1) - if len(v.OrderBy) > 0 && v.OrderBy[0].Desc { - cmpResult = 1 - } - tmpProcessor := &rangeFrameWindowProcessor{ - windowFuncs: windowFuncs, - partialResults: partialResults, - start: v.Frame.Start, - end: v.Frame.End, - orderByCols: orderByCols, - expectedCmpResult: cmpResult, - } - - err = tmpProcessor.start.UpdateCompareCols(b.ctx, orderByCols) - if err != nil { - return nil - } - err = tmpProcessor.end.UpdateCompareCols(b.ctx, orderByCols) - if err != nil { - return nil - } - - processor = tmpProcessor - } - return &WindowExec{BaseExecutor: base, - processor: processor, - groupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, groupByItems), - numWindowFuncs: len(v.WindowFuncDescs), - } -} - -func (b *executorBuilder) buildShuffle(v *plannercore.PhysicalShuffle) *ShuffleExec { - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - shuffle := &ShuffleExec{ - BaseExecutor: base, - concurrency: v.Concurrency, - } - - // 1. initialize the splitters - splitters := make([]partitionSplitter, len(v.ByItemArrays)) - switch v.SplitterType { - case plannercore.PartitionHashSplitterType: - for i, byItems := range v.ByItemArrays { - splitters[i] = buildPartitionHashSplitter(shuffle.concurrency, byItems) - } - case plannercore.PartitionRangeSplitterType: - for i, byItems := range v.ByItemArrays { - splitters[i] = buildPartitionRangeSplitter(b.ctx, shuffle.concurrency, byItems) - } - default: - panic("Not implemented. Should not reach here.") - } - shuffle.splitters = splitters - - // 2. initialize the data sources (build the data sources from physical plan to executors) - shuffle.dataSources = make([]exec.Executor, len(v.DataSources)) - for i, dataSource := range v.DataSources { - shuffle.dataSources[i] = b.build(dataSource) - if b.err != nil { - return nil - } - } - - // 3. initialize the workers - head := v.Children()[0] - // A `PhysicalShuffleReceiverStub` for every worker have the same `DataSource` but different `Receiver`. - // We preallocate `PhysicalShuffleReceiverStub`s here and reuse them below. - stubs := make([]*plannercore.PhysicalShuffleReceiverStub, 0, len(v.DataSources)) - for _, dataSource := range v.DataSources { - stub := plannercore.PhysicalShuffleReceiverStub{ - DataSource: dataSource, - }.Init(b.ctx, dataSource.StatsInfo(), dataSource.SelectBlockOffset(), nil) - stub.SetSchema(dataSource.Schema()) - stubs = append(stubs, stub) - } - shuffle.workers = make([]*shuffleWorker, shuffle.concurrency) - for i := range shuffle.workers { - receivers := make([]*shuffleReceiver, len(v.DataSources)) - for j, dataSource := range v.DataSources { - receivers[j] = &shuffleReceiver{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, dataSource.Schema(), stubs[j].ID()), - } - } - - w := &shuffleWorker{ - receivers: receivers, - } - - for j := range v.DataSources { - stub := stubs[j] - stub.Receiver = (unsafe.Pointer)(receivers[j]) - v.Tails[j].SetChildren(stub) - } - - w.childExec = b.build(head) - if b.err != nil { - return nil - } - - shuffle.workers[i] = w - } - - return shuffle -} - -func (*executorBuilder) buildShuffleReceiverStub(v *plannercore.PhysicalShuffleReceiverStub) *shuffleReceiver { - return (*shuffleReceiver)(v.Receiver) -} - -func (b *executorBuilder) buildSQLBindExec(v *plannercore.SQLBindPlan) exec.Executor { - base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) - base.SetInitCap(chunk.ZeroCapacity) - - e := &SQLBindExec{ - BaseExecutor: base, - sqlBindOp: v.SQLBindOp, - normdOrigSQL: v.NormdOrigSQL, - bindSQL: v.BindSQL, - charset: v.Charset, - collation: v.Collation, - db: v.Db, - isGlobal: v.IsGlobal, - bindAst: v.BindStmt, - newStatus: v.NewStatus, - source: v.Source, - sqlDigest: v.SQLDigest, - planDigest: v.PlanDigest, - } - return e -} - -// NewRowDecoder creates a chunk decoder for new row format row value decode. -func NewRowDecoder(ctx sessionctx.Context, schema *expression.Schema, tbl *model.TableInfo) *rowcodec.ChunkDecoder { - getColInfoByID := func(tbl *model.TableInfo, colID int64) *model.ColumnInfo { - for _, col := range tbl.Columns { - if col.ID == colID { - return col - } - } - return nil - } - var pkCols []int64 - reqCols := make([]rowcodec.ColInfo, len(schema.Columns)) - for i := range schema.Columns { - idx, col := i, schema.Columns[i] - isPK := (tbl.PKIsHandle && mysql.HasPriKeyFlag(col.RetType.GetFlag())) || col.ID == model.ExtraHandleID - if isPK { - pkCols = append(pkCols, col.ID) - } - isGeneratedCol := false - if col.VirtualExpr != nil { - isGeneratedCol = true - } - reqCols[idx] = rowcodec.ColInfo{ - ID: col.ID, - VirtualGenCol: isGeneratedCol, - Ft: col.RetType, - } - } - if len(pkCols) == 0 { - pkCols = tables.TryGetCommonPkColumnIds(tbl) - if len(pkCols) == 0 { - pkCols = []int64{-1} - } - } - defVal := func(i int, chk *chunk.Chunk) error { - if reqCols[i].ID < 0 { - // model.ExtraHandleID, ExtraPidColID, ExtraPhysTblID... etc - // Don't set the default value for that column. - chk.AppendNull(i) - return nil - } - - ci := getColInfoByID(tbl, reqCols[i].ID) - d, err := table.GetColOriginDefaultValue(ctx, ci) - if err != nil { - return err - } - chk.AppendDatum(i, &d) - return nil - } - return rowcodec.NewChunkDecoder(reqCols, pkCols, defVal, ctx.GetSessionVars().Location()) -} - -func (b *executorBuilder) buildBatchPointGet(plan *plannercore.BatchPointGetPlan) exec.Executor { - var err error - if err = b.validCanReadTemporaryOrCacheTable(plan.TblInfo); err != nil { - b.err = err - return nil - } - - if plan.Lock && !b.inSelectLockStmt { - b.inSelectLockStmt = true - defer func() { - b.inSelectLockStmt = false - }() - } - - decoder := NewRowDecoder(b.ctx, plan.Schema(), plan.TblInfo) - e := &BatchPointGetExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, plan.Schema(), plan.ID()), - tblInfo: plan.TblInfo, - idxInfo: plan.IndexInfo, - rowDecoder: decoder, - keepOrder: plan.KeepOrder, - desc: plan.Desc, - lock: plan.Lock, - waitTime: plan.LockWaitTime, - partExpr: plan.PartitionExpr, - partPos: plan.PartitionColPos, - planPhysIDs: plan.PartitionIDs, - singlePart: plan.SinglePart, - partTblID: plan.PartTblID, - columns: plan.Columns, - } - - e.snapshot, err = b.getSnapshot() - if err != nil { - b.err = err - return nil - } - if e.Ctx().GetSessionVars().IsReplicaReadClosestAdaptive() { - e.snapshot.SetOption(kv.ReplicaReadAdjuster, newReplicaReadAdjuster(e.Ctx(), plan.GetAvgRowSize())) - } - if e.RuntimeStats() != nil { - snapshotStats := &txnsnapshot.SnapshotRuntimeStats{} - e.stats = &runtimeStatsWithSnapshot{ - SnapshotRuntimeStats: snapshotStats, - } - e.snapshot.SetOption(kv.CollectRuntimeStats, snapshotStats) - } - - if plan.IndexInfo != nil { - sctx := b.ctx.GetSessionVars().StmtCtx - sctx.IndexNames = append(sctx.IndexNames, plan.TblInfo.Name.O+":"+plan.IndexInfo.Name.O) - } - - failpoint.Inject("assertBatchPointReplicaOption", func(val failpoint.Value) { - assertScope := val.(string) - if e.Ctx().GetSessionVars().GetReplicaRead().IsClosestRead() && assertScope != b.readReplicaScope { - panic("batch point get replica option fail") - } - }) - - snapshotTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - if plan.TblInfo.TableCacheStatusType == model.TableCacheStatusEnable { - if cacheTable := b.getCacheTable(plan.TblInfo, snapshotTS); cacheTable != nil { - e.snapshot = cacheTableSnapshot{e.snapshot, cacheTable} - } - } - - if plan.TblInfo.TempTableType != model.TempTableNone { - // Temporary table should not do any lock operations - e.lock = false - e.waitTime = 0 - } - - if e.lock { - b.hasLock = true - } - - var capacity int - if plan.IndexInfo != nil && !isCommonHandleRead(plan.TblInfo, plan.IndexInfo) { - e.idxVals = plan.IndexValues - capacity = len(e.idxVals) - } else { - // `SELECT a FROM t WHERE a IN (1, 1, 2, 1, 2)` should not return duplicated rows - handles := make([]kv.Handle, 0, len(plan.Handles)) - dedup := kv.NewHandleMap() - // Used for clear paritionIDs of duplicated rows. - dupPartPos := 0 - if plan.IndexInfo == nil { - for idx, handle := range plan.Handles { - if _, found := dedup.Get(handle); found { - continue - } - dedup.Set(handle, true) - handles = append(handles, handle) - if len(plan.PartitionIDs) > 0 { - e.planPhysIDs[dupPartPos] = e.planPhysIDs[idx] - dupPartPos++ - } - } - } else { - for idx, value := range plan.IndexValues { - if datumsContainNull(value) { - continue - } - handleBytes, err := EncodeUniqueIndexValuesForKey(e.Ctx(), e.tblInfo, plan.IndexInfo, value) - if err != nil { - if kv.ErrNotExist.Equal(err) { - continue - } - b.err = err - return nil - } - handle, err := kv.NewCommonHandle(handleBytes) - if err != nil { - b.err = err - return nil - } - if _, found := dedup.Get(handle); found { - continue - } - dedup.Set(handle, true) - handles = append(handles, handle) - if len(plan.PartitionIDs) > 0 { - e.planPhysIDs[dupPartPos] = e.planPhysIDs[idx] - dupPartPos++ - } - } - } - e.handles = handles - if dupPartPos > 0 { - e.planPhysIDs = e.planPhysIDs[:dupPartPos] - } - capacity = len(e.handles) - } - e.Base().SetInitCap(capacity) - e.Base().SetMaxChunkSize(capacity) - e.buildVirtualColumnInfo() - return e -} - -func newReplicaReadAdjuster(ctx sessionctx.Context, avgRowSize float64) txnkv.ReplicaReadAdjuster { - return func(count int) (tikv.StoreSelectorOption, clientkv.ReplicaReadType) { - if int64(avgRowSize*float64(count)) >= ctx.GetSessionVars().ReplicaClosestReadThreshold { - return tikv.WithMatchLabels([]*metapb.StoreLabel{ - { - Key: placement.DCLabelKey, - Value: config.GetTxnScopeFromConfig(), - }, - }), clientkv.ReplicaReadMixed - } - // fallback to read from leader if the request is small - return nil, clientkv.ReplicaReadLeader - } -} - -func isCommonHandleRead(tbl *model.TableInfo, idx *model.IndexInfo) bool { - return tbl.IsCommonHandle && idx.Primary -} - -func getPhysicalTableID(t table.Table) int64 { - if p, ok := t.(table.PhysicalTable); ok { - return p.GetPhysicalID() - } - return t.Meta().ID -} - -func (b *executorBuilder) buildAdminShowTelemetry(v *plannercore.AdminShowTelemetry) exec.Executor { - return &AdminShowTelemetryExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID())} -} - -func (b *executorBuilder) buildAdminResetTelemetryID(v *plannercore.AdminResetTelemetryID) exec.Executor { - return &AdminResetTelemetryIDExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID())} -} - -func (builder *dataReaderBuilder) partitionPruning(tbl table.PartitionedTable, conds []expression.Expression, partitionNames []model.CIStr, - columns []*expression.Column, columnNames types.NameSlice) ([]table.PhysicalTable, error) { - builder.once.Do(func() { - condPruneResult, err := partitionPruning(builder.executorBuilder.ctx, tbl, conds, partitionNames, columns, columnNames) - builder.once.condPruneResult = condPruneResult - builder.once.err = err - }) - return builder.once.condPruneResult, builder.once.err -} - -func partitionPruning(ctx sessionctx.Context, tbl table.PartitionedTable, conds []expression.Expression, partitionNames []model.CIStr, - columns []*expression.Column, columnNames types.NameSlice) ([]table.PhysicalTable, error) { - idxArr, err := plannercore.PartitionPruning(ctx, tbl, conds, partitionNames, columns, columnNames) - if err != nil { - return nil, err - } - - pi := tbl.Meta().GetPartitionInfo() - var ret []table.PhysicalTable - if fullRangePartition(idxArr) { - ret = make([]table.PhysicalTable, 0, len(pi.Definitions)) - for _, def := range pi.Definitions { - p := tbl.GetPartition(def.ID) - ret = append(ret, p) - } - } else { - ret = make([]table.PhysicalTable, 0, len(idxArr)) - for _, idx := range idxArr { - pid := pi.Definitions[idx].ID - p := tbl.GetPartition(pid) - ret = append(ret, p) - } - } - return ret, nil -} - -func getPartitionIdsAfterPruning(ctx sessionctx.Context, tbl table.PartitionedTable, partInfo *plannercore.PartitionInfo) (map[int64]struct{}, error) { - if partInfo == nil { - return nil, errors.New("partInfo in getPartitionIdsAfterPruning must not be nil") - } - idxArr, err := plannercore.PartitionPruning(ctx, tbl, partInfo.PruningConds, partInfo.PartitionNames, partInfo.Columns, partInfo.ColumnNames) - if err != nil { - return nil, err - } - - var ret map[int64]struct{} - - pi := tbl.Meta().GetPartitionInfo() - if fullRangePartition(idxArr) { - ret = make(map[int64]struct{}, len(pi.Definitions)) - for _, def := range pi.Definitions { - ret[def.ID] = struct{}{} - } - } else { - ret = make(map[int64]struct{}, len(idxArr)) - for _, idx := range idxArr { - pid := pi.Definitions[idx].ID - ret[pid] = struct{}{} - } - } - return ret, nil -} - -func fullRangePartition(idxArr []int) bool { - return len(idxArr) == 1 && idxArr[0] == plannercore.FullRange -} - -type emptySampler struct{} - -func (*emptySampler) writeChunk(_ *chunk.Chunk) error { - return nil -} - -func (*emptySampler) finished() bool { - return true -} - -func (b *executorBuilder) buildTableSample(v *plannercore.PhysicalTableSample) *TableSampleExecutor { - startTS, err := b.getSnapshotTS() - if err != nil { - b.err = err - return nil - } - e := &TableSampleExecutor{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - table: v.TableInfo, - startTS: startTS, - } - - tblInfo := v.TableInfo.Meta() - if tblInfo.TempTableType != model.TempTableNone { - if tblInfo.TempTableType != model.TempTableGlobal { - b.err = errors.New("TABLESAMPLE clause can not be applied to local temporary tables") - return nil - } - e.sampler = &emptySampler{} - } else if v.TableSampleInfo.AstNode.SampleMethod == ast.SampleMethodTypeTiDBRegion { - e.sampler = newTableRegionSampler( - b.ctx, v.TableInfo, startTS, v.TableSampleInfo.Partitions, v.Schema(), - v.TableSampleInfo.FullSchema, e.RetFieldTypes(), v.Desc) - } - - return e -} - -func (b *executorBuilder) buildCTE(v *plannercore.PhysicalCTE) exec.Executor { - if b.Ti != nil { - b.Ti.UseNonRecursive = true - } - if v.RecurPlan != nil && b.Ti != nil { - b.Ti.UseRecursive = true - } - - storageMap, ok := b.ctx.GetSessionVars().StmtCtx.CTEStorageMap.(map[int]*CTEStorages) - if !ok { - b.err = errors.New("type assertion for CTEStorageMap failed") - return nil - } - - chkSize := b.ctx.GetSessionVars().MaxChunkSize - // iterOutTbl will be constructed in CTEExec.Open(). - var resTbl cteutil.Storage - var iterInTbl cteutil.Storage - var producer *cteProducer - storages, ok := storageMap[v.CTE.IDForStorage] - if ok { - // Storage already setup. - resTbl = storages.ResTbl - iterInTbl = storages.IterInTbl - producer = storages.Producer - } else { - if v.SeedPlan == nil { - b.err = errors.New("cte.seedPlan cannot be nil") - return nil - } - // Build seed part. - corCols := plannercore.ExtractOuterApplyCorrelatedCols(v.SeedPlan) - seedExec := b.build(v.SeedPlan) - if b.err != nil { - return nil - } - - // Setup storages. - tps := seedExec.Base().RetFieldTypes() - resTbl = cteutil.NewStorageRowContainer(tps, chkSize) - if err := resTbl.OpenAndRef(); err != nil { - b.err = err - return nil - } - iterInTbl = cteutil.NewStorageRowContainer(tps, chkSize) - if err := iterInTbl.OpenAndRef(); err != nil { - b.err = err - return nil - } - storageMap[v.CTE.IDForStorage] = &CTEStorages{ResTbl: resTbl, IterInTbl: iterInTbl} - - // Build recursive part. - var recursiveExec exec.Executor - if v.RecurPlan != nil { - recursiveExec = b.build(v.RecurPlan) - if b.err != nil { - return nil - } - corCols = append(corCols, plannercore.ExtractOuterApplyCorrelatedCols(v.RecurPlan)...) - } - - var sel []int - if v.CTE.IsDistinct { - sel = make([]int, chkSize) - for i := 0; i < chkSize; i++ { - sel[i] = i - } - } - - var corColHashCodes [][]byte - for _, corCol := range corCols { - corColHashCodes = append(corColHashCodes, getCorColHashCode(corCol)) - } - - producer = &cteProducer{ - ctx: b.ctx, - seedExec: seedExec, - recursiveExec: recursiveExec, - resTbl: resTbl, - iterInTbl: iterInTbl, - isDistinct: v.CTE.IsDistinct, - sel: sel, - hasLimit: v.CTE.HasLimit, - limitBeg: v.CTE.LimitBeg, - limitEnd: v.CTE.LimitEnd, - corCols: corCols, - corColHashCodes: corColHashCodes, - } - storageMap[v.CTE.IDForStorage].Producer = producer - } - - return &CTEExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - producer: producer, - } -} - -func (b *executorBuilder) buildCTETableReader(v *plannercore.PhysicalCTETable) exec.Executor { - storageMap, ok := b.ctx.GetSessionVars().StmtCtx.CTEStorageMap.(map[int]*CTEStorages) - if !ok { - b.err = errors.New("type assertion for CTEStorageMap failed") - return nil - } - storages, ok := storageMap[v.IDForStorage] - if !ok { - b.err = errors.Errorf("iterInTbl should already be set up by CTEExec(id: %d)", v.IDForStorage) - return nil - } - return &CTETableReaderExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - iterInTbl: storages.IterInTbl, - chkIdx: 0, - } -} -func (b *executorBuilder) validCanReadTemporaryOrCacheTable(tbl *model.TableInfo) error { - err := b.validCanReadTemporaryTable(tbl) - if err != nil { - return err - } - return b.validCanReadCacheTable(tbl) -} - -func (b *executorBuilder) validCanReadCacheTable(tbl *model.TableInfo) error { - if tbl.TableCacheStatusType == model.TableCacheStatusDisable { - return nil - } - - sessionVars := b.ctx.GetSessionVars() - - // Temporary table can't switch into cache table. so the following code will not cause confusion - if sessionVars.TxnCtx.IsStaleness || b.isStaleness { - return errors.Trace(errors.New("can not stale read cache table")) - } - - return nil -} - -func (b *executorBuilder) validCanReadTemporaryTable(tbl *model.TableInfo) error { - if tbl.TempTableType == model.TempTableNone { - return nil - } - - // Some tools like dumpling use history read to dump all table's records and will be fail if we return an error. - // So we do not check SnapshotTS here - - sessionVars := b.ctx.GetSessionVars() - - if tbl.TempTableType == model.TempTableLocal && sessionVars.SnapshotTS != 0 { - return errors.New("can not read local temporary table when 'tidb_snapshot' is set") - } - - if sessionVars.TxnCtx.IsStaleness || b.isStaleness { - return errors.New("can not stale read temporary table") - } - - return nil -} - -func (b *executorBuilder) getCacheTable(tblInfo *model.TableInfo, startTS uint64) kv.MemBuffer { - tbl, ok := b.is.TableByID(tblInfo.ID) - if !ok { - b.err = errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs(b.ctx.GetSessionVars().CurrentDB, tblInfo.Name)) - return nil - } - sessVars := b.ctx.GetSessionVars() - leaseDuration := time.Duration(variable.TableCacheLease.Load()) * time.Second - cacheData, loading := tbl.(table.CachedTable).TryReadFromCache(startTS, leaseDuration) - if cacheData != nil { - sessVars.StmtCtx.ReadFromTableCache = true - return cacheData - } else if loading { - return nil - } else { - if !b.ctx.GetSessionVars().StmtCtx.InExplainStmt && !b.inDeleteStmt && !b.inUpdateStmt { - tbl.(table.CachedTable).UpdateLockForRead(context.Background(), b.ctx.GetStore(), startTS, leaseDuration) - } - } - return nil -} - -func (b *executorBuilder) buildCompactTable(v *plannercore.CompactTable) exec.Executor { - if v.ReplicaKind != ast.CompactReplicaKindTiFlash && v.ReplicaKind != ast.CompactReplicaKindAll { - b.err = errors.Errorf("compact %v replica is not supported", strings.ToLower(string(v.ReplicaKind))) - return nil - } - - store := b.ctx.GetStore() - tikvStore, ok := store.(tikv.Storage) - if !ok { - b.err = errors.New("compact tiflash replica can only run with tikv compatible storage") - return nil - } - - var partitionIDs []int64 - if v.PartitionNames != nil { - if v.TableInfo.Partition == nil { - b.err = errors.Errorf("table:%s is not a partition table, but user specify partition name list:%+v", v.TableInfo.Name.O, v.PartitionNames) - return nil - } - // use map to avoid FindPartitionDefinitionByName - partitionMap := map[string]int64{} - for _, partition := range v.TableInfo.Partition.Definitions { - partitionMap[partition.Name.L] = partition.ID - } - - for _, partitionName := range v.PartitionNames { - partitionID, ok := partitionMap[partitionName.L] - if !ok { - b.err = table.ErrUnknownPartition.GenWithStackByArgs(partitionName.O, v.TableInfo.Name.O) - return nil - } - partitionIDs = append(partitionIDs, partitionID) - } - if b.Ti.PartitionTelemetry == nil { - b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} - } - b.Ti.PartitionTelemetry.UseCompactTablePartition = true - } - - return &CompactTableTiFlashExec{ - BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), - tableInfo: v.TableInfo, - partitionIDs: partitionIDs, - tikvStore: tikvStore, - } -} diff --git a/executor/checksum.go b/executor/checksum.go deleted file mode 100644 index e367ab7862776..0000000000000 --- a/executor/checksum.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "strconv" - - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -var _ exec.Executor = &ChecksumTableExec{} - -// ChecksumTableExec represents ChecksumTable executor. -type ChecksumTableExec struct { - exec.BaseExecutor - - tables map[int64]*checksumContext - done bool -} - -// Open implements the Executor Open interface. -func (e *ChecksumTableExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - - concurrency, err := getChecksumTableConcurrency(e.Ctx()) - if err != nil { - return err - } - - tasks, err := e.buildTasks() - if err != nil { - return err - } - - taskCh := make(chan *checksumTask, len(tasks)) - resultCh := make(chan *checksumResult, len(tasks)) - for i := 0; i < concurrency; i++ { - go e.checksumWorker(taskCh, resultCh) - } - - for _, task := range tasks { - taskCh <- task - } - close(taskCh) - - for i := 0; i < len(tasks); i++ { - result := <-resultCh - if result.Error != nil { - err = result.Error - logutil.Logger(ctx).Error("checksum failed", zap.Error(err)) - continue - } - e.handleResult(result) - } - if err != nil { - return err - } - - return nil -} - -// Next implements the Executor Next interface. -func (e *ChecksumTableExec) Next(_ context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - for _, t := range e.tables { - req.AppendString(0, t.DBInfo.Name.O) - req.AppendString(1, t.TableInfo.Name.O) - req.AppendUint64(2, t.Response.Checksum) - req.AppendUint64(3, t.Response.TotalKvs) - req.AppendUint64(4, t.Response.TotalBytes) - } - e.done = true - return nil -} - -func (e *ChecksumTableExec) buildTasks() ([]*checksumTask, error) { - var tasks []*checksumTask - for id, t := range e.tables { - reqs, err := t.BuildRequests(e.Ctx()) - if err != nil { - return nil, err - } - for _, req := range reqs { - tasks = append(tasks, &checksumTask{id, req}) - } - } - return tasks, nil -} - -func (e *ChecksumTableExec) handleResult(result *checksumResult) { - table := e.tables[result.TableID] - table.HandleResponse(result.Response) -} - -func (e *ChecksumTableExec) checksumWorker(taskCh <-chan *checksumTask, resultCh chan<- *checksumResult) { - for task := range taskCh { - result := &checksumResult{TableID: task.TableID} - result.Response, result.Error = e.handleChecksumRequest(task.Request) - resultCh <- result - } -} - -func (e *ChecksumTableExec) handleChecksumRequest(req *kv.Request) (resp *tipb.ChecksumResponse, err error) { - ctx := distsql.WithSQLKvExecCounterInterceptor(context.TODO(), e.Ctx().GetSessionVars().StmtCtx) - res, err := distsql.Checksum(ctx, e.Ctx().GetClient(), req, e.Ctx().GetSessionVars().KVVars) - if err != nil { - return nil, err - } - defer func() { - if err1 := res.Close(); err1 != nil { - err = err1 - } - }() - - resp = &tipb.ChecksumResponse{} - - for { - data, err := res.NextRaw(ctx) - if err != nil { - return nil, err - } - if data == nil { - break - } - checksum := &tipb.ChecksumResponse{} - if err = checksum.Unmarshal(data); err != nil { - return nil, err - } - updateChecksumResponse(resp, checksum) - } - - return resp, nil -} - -type checksumTask struct { - TableID int64 - Request *kv.Request -} - -type checksumResult struct { - Error error - TableID int64 - Response *tipb.ChecksumResponse -} - -type checksumContext struct { - DBInfo *model.DBInfo - TableInfo *model.TableInfo - StartTs uint64 - Response *tipb.ChecksumResponse -} - -func newChecksumContext(db *model.DBInfo, table *model.TableInfo, startTs uint64) *checksumContext { - return &checksumContext{ - DBInfo: db, - TableInfo: table, - StartTs: startTs, - Response: &tipb.ChecksumResponse{}, - } -} - -func (c *checksumContext) BuildRequests(ctx sessionctx.Context) ([]*kv.Request, error) { - var partDefs []model.PartitionDefinition - if part := c.TableInfo.Partition; part != nil { - partDefs = part.Definitions - } - - reqs := make([]*kv.Request, 0, (len(c.TableInfo.Indices)+1)*(len(partDefs)+1)) - if err := c.appendRequest(ctx, c.TableInfo.ID, &reqs); err != nil { - return nil, err - } - - for _, partDef := range partDefs { - if err := c.appendRequest(ctx, partDef.ID, &reqs); err != nil { - return nil, err - } - } - - return reqs, nil -} - -func (c *checksumContext) appendRequest(ctx sessionctx.Context, tableID int64, reqs *[]*kv.Request) error { - req, err := c.buildTableRequest(ctx, tableID) - if err != nil { - return err - } - - *reqs = append(*reqs, req) - for _, indexInfo := range c.TableInfo.Indices { - if indexInfo.State != model.StatePublic { - continue - } - req, err = c.buildIndexRequest(ctx, tableID, indexInfo) - if err != nil { - return err - } - *reqs = append(*reqs, req) - } - - return nil -} - -func (c *checksumContext) buildTableRequest(ctx sessionctx.Context, tableID int64) (*kv.Request, error) { - checksum := &tipb.ChecksumRequest{ - ScanOn: tipb.ChecksumScanOn_Table, - Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, - } - - var ranges []*ranger.Range - if c.TableInfo.IsCommonHandle { - ranges = ranger.FullNotNullRange() - } else { - ranges = ranger.FullIntRange(false) - } - - var builder distsql.RequestBuilder - builder.SetResourceGroupTagger(ctx.GetSessionVars().StmtCtx.GetResourceGroupTagger()) - return builder.SetHandleRanges(ctx.GetSessionVars().StmtCtx, tableID, c.TableInfo.IsCommonHandle, ranges). - SetChecksumRequest(checksum). - SetStartTS(c.StartTs). - SetConcurrency(ctx.GetSessionVars().DistSQLScanConcurrency()). - SetResourceGroupName(ctx.GetSessionVars().ResourceGroupName). - SetExplicitRequestSourceType(ctx.GetSessionVars().ExplicitRequestSourceType). - Build() -} - -func (c *checksumContext) buildIndexRequest(ctx sessionctx.Context, tableID int64, indexInfo *model.IndexInfo) (*kv.Request, error) { - checksum := &tipb.ChecksumRequest{ - ScanOn: tipb.ChecksumScanOn_Index, - Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, - } - - ranges := ranger.FullRange() - - var builder distsql.RequestBuilder - builder.SetResourceGroupTagger(ctx.GetSessionVars().StmtCtx.GetResourceGroupTagger()) - return builder.SetIndexRanges(ctx.GetSessionVars().StmtCtx, tableID, indexInfo.ID, ranges). - SetChecksumRequest(checksum). - SetStartTS(c.StartTs). - SetConcurrency(ctx.GetSessionVars().DistSQLScanConcurrency()). - SetResourceGroupName(ctx.GetSessionVars().ResourceGroupName). - SetExplicitRequestSourceType(ctx.GetSessionVars().ExplicitRequestSourceType). - Build() -} - -func (c *checksumContext) HandleResponse(update *tipb.ChecksumResponse) { - updateChecksumResponse(c.Response, update) -} - -func getChecksumTableConcurrency(ctx sessionctx.Context) (int, error) { - sessionVars := ctx.GetSessionVars() - concurrency, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), variable.TiDBChecksumTableConcurrency) - if err != nil { - return 0, err - } - c, err := strconv.ParseInt(concurrency, 10, 64) - return int(c), err -} - -func updateChecksumResponse(resp, update *tipb.ChecksumResponse) { - resp.Checksum ^= update.Checksum - resp.TotalKvs += update.TotalKvs - resp.TotalBytes += update.TotalBytes -} diff --git a/executor/coprocessor.go b/executor/coprocessor.go deleted file mode 100644 index e5879334d7404..0000000000000 --- a/executor/coprocessor.go +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - - "github.com/gogo/protobuf/proto" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/kvproto/pkg/tikvpb" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tipb/go-tipb" -) - -func copHandlerCtx(ctx context.Context, req *coprocessor.Request) context.Context { - source := req.Context.SourceStmt - if source == nil { - return ctx - } - - traceInfo := &model.TraceInfo{ - ConnectionID: source.ConnectionId, - SessionAlias: source.SessionAlias, - } - - ctx = tracing.ContextWithTraceInfo(ctx, traceInfo) - ctx = logutil.WithTraceFields(ctx, traceInfo) - return ctx -} - -// CoprocessorDAGHandler uses to handle cop dag request. -type CoprocessorDAGHandler struct { - sctx sessionctx.Context - dagReq *tipb.DAGRequest -} - -// NewCoprocessorDAGHandler creates a new CoprocessorDAGHandler. -func NewCoprocessorDAGHandler(sctx sessionctx.Context) *CoprocessorDAGHandler { - return &CoprocessorDAGHandler{ - sctx: sctx, - } -} - -// HandleRequest handles the coprocessor request. -func (h *CoprocessorDAGHandler) HandleRequest(ctx context.Context, req *coprocessor.Request) *coprocessor.Response { - ctx = copHandlerCtx(ctx, req) - - e, err := h.buildDAGExecutor(req) - if err != nil { - return h.buildErrorResponse(err) - } - - err = e.Open(ctx) - if err != nil { - return h.buildErrorResponse(err) - } - - chk := exec.TryNewCacheChunk(e) - tps := e.Base().RetFieldTypes() - var totalChunks, partChunks []tipb.Chunk - memTracker := h.sctx.GetSessionVars().StmtCtx.MemTracker - for { - chk.Reset() - err = exec.Next(ctx, e, chk) - if err != nil { - return h.buildErrorResponse(err) - } - if chk.NumRows() == 0 { - break - } - partChunks, err = h.buildChunk(chk, tps) - if err != nil { - return h.buildErrorResponse(err) - } - for _, ch := range partChunks { - memTracker.Consume(int64(ch.Size())) - } - totalChunks = append(totalChunks, partChunks...) - } - if err := e.Close(); err != nil { - return h.buildErrorResponse(err) - } - return h.buildUnaryResponse(totalChunks) -} - -// HandleStreamRequest handles the coprocessor stream request. -func (h *CoprocessorDAGHandler) HandleStreamRequest(ctx context.Context, req *coprocessor.Request, stream tikvpb.Tikv_CoprocessorStreamServer) error { - ctx = copHandlerCtx(ctx, req) - logutil.Logger(ctx).Debug("handle coprocessor stream request") - - e, err := h.buildDAGExecutor(req) - if err != nil { - return stream.Send(h.buildErrorResponse(err)) - } - - err = e.Open(ctx) - if err != nil { - return stream.Send(h.buildErrorResponse(err)) - } - - chk := exec.TryNewCacheChunk(e) - tps := e.Base().RetFieldTypes() - for { - chk.Reset() - if err = exec.Next(ctx, e, chk); err != nil { - return stream.Send(h.buildErrorResponse(err)) - } - if chk.NumRows() == 0 { - return h.buildResponseAndSendToStream(chk, tps, stream) - } - if err = h.buildResponseAndSendToStream(chk, tps, stream); err != nil { - return stream.Send(h.buildErrorResponse(err)) - } - } -} - -func (h *CoprocessorDAGHandler) buildResponseAndSendToStream(chk *chunk.Chunk, tps []*types.FieldType, stream tikvpb.Tikv_CoprocessorStreamServer) error { - chunks, err := h.buildChunk(chk, tps) - if err != nil { - return stream.Send(h.buildErrorResponse(err)) - } - - for i := range chunks { - resp := h.buildStreamResponse(&chunks[i]) - if err = stream.Send(resp); err != nil { - return err - } - } - return nil -} - -func (h *CoprocessorDAGHandler) buildDAGExecutor(req *coprocessor.Request) (exec.Executor, error) { - if req.GetTp() != kv.ReqTypeDAG { - return nil, errors.Errorf("unsupported request type %d", req.GetTp()) - } - dagReq := new(tipb.DAGRequest) - err := proto.Unmarshal(req.Data, dagReq) - if err != nil { - return nil, errors.Trace(err) - } - - if dagReq.User != nil { - pm := privilege.GetPrivilegeManager(h.sctx) - if pm != nil { - h.sctx.GetSessionVars().User = &auth.UserIdentity{ - Username: dagReq.User.UserName, - Hostname: dagReq.User.UserHost, - } - authName, authHost, success := pm.MatchIdentity(dagReq.User.UserName, dagReq.User.UserHost, false) - if success && pm.GetAuthWithoutVerification(authName, authHost) { - h.sctx.GetSessionVars().User.AuthUsername = authName - h.sctx.GetSessionVars().User.AuthHostname = authHost - h.sctx.GetSessionVars().ActiveRoles = pm.GetDefaultRoles(authName, authHost) - } - } - } - - stmtCtx := h.sctx.GetSessionVars().StmtCtx - stmtCtx.SetFlagsFromPBFlag(dagReq.Flags) - tz, err := timeutil.ConstructTimeZone(dagReq.TimeZoneName, int(dagReq.TimeZoneOffset)) - if err != nil { - return nil, errors.Trace(err) - } - - stmtCtx.SetTimeZone(tz) - h.sctx.GetSessionVars().TimeZone = tz - h.dagReq = dagReq - is := h.sctx.GetInfoSchema().(infoschema.InfoSchema) - // Build physical plan. - bp := core.NewPBPlanBuilder(h.sctx, is, req.Ranges) - plan, err := bp.Build(dagReq.Executors) - if err != nil { - return nil, errors.Trace(err) - } - plan = core.InjectExtraProjection(plan) - // Build executor. - b := newExecutorBuilder(h.sctx, is, nil) - return b.build(plan), nil -} - -func (h *CoprocessorDAGHandler) buildChunk(chk *chunk.Chunk, tps []*types.FieldType) (chunks []tipb.Chunk, err error) { - switch h.dagReq.EncodeType { - case tipb.EncodeType_TypeDefault: - chunks, err = h.encodeDefault(chk, tps) - case tipb.EncodeType_TypeChunk: - chunks, err = h.encodeChunk(chk, tps) - default: - return nil, errors.Errorf("unknown DAG encode type: %v", h.dagReq.EncodeType) - } - return chunks, err -} - -func (h *CoprocessorDAGHandler) buildUnaryResponse(chunks []tipb.Chunk) *coprocessor.Response { - selResp := tipb.SelectResponse{ - Chunks: chunks, - EncodeType: h.dagReq.EncodeType, - } - if h.dagReq.CollectExecutionSummaries != nil && *h.dagReq.CollectExecutionSummaries { - execSummary := make([]*tipb.ExecutorExecutionSummary, len(h.dagReq.Executors)) - for i := range execSummary { - // TODO: Add real executor execution summary information. - execSummary[i] = &tipb.ExecutorExecutionSummary{} - } - selResp.ExecutionSummaries = execSummary - } - data, err := proto.Marshal(&selResp) - if err != nil { - return h.buildErrorResponse(err) - } - return &coprocessor.Response{ - Data: data, - } -} - -func (h *CoprocessorDAGHandler) buildStreamResponse(chunk *tipb.Chunk) *coprocessor.Response { - data, err := chunk.Marshal() - if err != nil { - return h.buildErrorResponse(err) - } - streamResponse := tipb.StreamResponse{ - Data: data, - } - var resp = &coprocessor.Response{} - resp.Data, err = proto.Marshal(&streamResponse) - if err != nil { - resp.OtherError = err.Error() - } - return resp -} - -func (*CoprocessorDAGHandler) buildErrorResponse(err error) *coprocessor.Response { - return &coprocessor.Response{ - OtherError: err.Error(), - } -} - -func (h *CoprocessorDAGHandler) encodeChunk(chk *chunk.Chunk, colTypes []*types.FieldType) ([]tipb.Chunk, error) { - colOrdinal := h.dagReq.OutputOffsets - respColTypes := make([]*types.FieldType, 0, len(colOrdinal)) - for _, ordinal := range colOrdinal { - respColTypes = append(respColTypes, colTypes[ordinal]) - } - encoder := chunk.NewCodec(respColTypes) - cur := tipb.Chunk{} - cur.RowsData = append(cur.RowsData, encoder.Encode(chk)...) - return []tipb.Chunk{cur}, nil -} - -func (h *CoprocessorDAGHandler) encodeDefault(chk *chunk.Chunk, tps []*types.FieldType) ([]tipb.Chunk, error) { - colOrdinal := h.dagReq.OutputOffsets - stmtCtx := h.sctx.GetSessionVars().StmtCtx - requestedRow := make([]byte, 0) - chunks := []tipb.Chunk{} - for i := 0; i < chk.NumRows(); i++ { - requestedRow = requestedRow[:0] - row := chk.GetRow(i) - for _, ordinal := range colOrdinal { - data, err := codec.EncodeValue(stmtCtx, nil, row.GetDatum(int(ordinal), tps[ordinal])) - if err != nil { - return nil, err - } - requestedRow = append(requestedRow, data...) - } - chunks = h.appendRow(chunks, requestedRow, i) - } - return chunks, nil -} - -const rowsPerChunk = 64 - -func (*CoprocessorDAGHandler) appendRow(chunks []tipb.Chunk, data []byte, rowCnt int) []tipb.Chunk { - if rowCnt%rowsPerChunk == 0 { - chunks = append(chunks, tipb.Chunk{}) - } - cur := &chunks[len(chunks)-1] - cur.RowsData = append(cur.RowsData, data...) - return chunks -} diff --git a/executor/cte_test.go b/executor/cte_test.go deleted file mode 100644 index fc8805d07b7dc..0000000000000 --- a/executor/cte_test.go +++ /dev/null @@ -1,507 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "math/rand" - "slices" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestBasicCTE(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - rows := tk.MustQuery("with recursive cte1 as (" + - "select 1 c1 " + - "union all " + - "select c1 + 1 c1 from cte1 where c1 < 5) " + - "select * from cte1") - rows.Check(testkit.Rows("1", "2", "3", "4", "5")) - - // Two seed parts. - rows = tk.MustQuery("with recursive cte1 as (" + - "select 1 c1 " + - "union all " + - "select 2 c1 " + - "union all " + - "select c1 + 1 c1 from cte1 where c1 < 10) " + - "select * from cte1 order by c1") - rows.Check(testkit.Rows("1", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "9", "10", "10")) - - // Two recursive parts. - rows = tk.MustQuery("with recursive cte1 as (" + - "select 1 c1 " + - "union all " + - "select 2 c1 " + - "union all " + - "select c1 + 1 c1 from cte1 where c1 < 3 " + - "union all " + - "select c1 + 2 c1 from cte1 where c1 < 5) " + - "select * from cte1 order by c1") - rows.Check(testkit.Rows("1", "2", "2", "3", "3", "3", "4", "4", "5", "5", "5", "6", "6")) - - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(a int);") - tk.MustExec("insert into t1 values(1);") - tk.MustExec("insert into t1 values(2);") - rows = tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS(WITH RECURSIVE qn AS (SELECT a*0 AS b UNION ALL SELECT b+1 FROM qn WHERE b=0) SELECT * FROM qn WHERE b=a);") - rows.Check(testkit.Rows("1")) - rows = tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS( WITH RECURSIVE qn AS (SELECT a*0 AS b UNION ALL SELECT b+1 FROM qn WHERE b=0 or b = 1) SELECT * FROM qn WHERE b=a );") - rows.Check(testkit.Rows("1", "2")) - - rows = tk.MustQuery("with recursive c(p) as (select 1), cte(a, b) as (select 1, 1 union select a+1, 1 from cte, c where a < 5) select * from cte order by 1, 2;") - rows.Check(testkit.Rows("1 1", "2 1", "3 1", "4 1", "5 1")) -} - -func TestUnionDistinct(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - // Basic test. UNION/UNION ALL intersects. - rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select 1 union select 1 union all select c1 + 1 from cte1 where c1 < 3) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2", "3")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union all select 1 union select 1 union all select c1 + 1 from cte1 where c1 < 3) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int, c2 int);") - tk.MustExec("insert into t1 values(1, 1), (1, 2), (2, 2);") - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from t1) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec("insert into t1 values(1), (1), (1), (2), (2), (2);") - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 4) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2", "3", "4")) -} - -func TestCTEMaxRecursionDepth(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - tk.MustExec("set @@cte_max_recursion_depth = -1;") - err := tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - // If there is no recursive part, query runs ok. - rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) - rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) - - tk.MustExec("set @@cte_max_recursion_depth = 0;") - err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 0) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 1) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - // If there is no recursive part, query runs ok. - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) - rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) - - tk.MustExec("set @@cte_max_recursion_depth = 1;") - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 0) select * from cte1;") - rows.Check(testkit.Rows("1")) - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 1) select * from cte1;") - rows.Check(testkit.Rows("1")) - err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 2) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 2 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - // If there is no recursive part, query runs ok. - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) - rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") - rows.Check(testkit.Rows("1", "2")) -} - -func TestCTEWithLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - // Basic recursive tests. - rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 0) select * from cte1") - rows.Check(testkit.Rows("1", "2", "3", "4", "5")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 1) select * from cte1") - rows.Check(testkit.Rows("2", "3", "4", "5", "6")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 10) select * from cte1") - rows.Check(testkit.Rows("11", "12", "13", "14", "15")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 995) select * from cte1") - rows.Check(testkit.Rows("996", "997", "998", "999", "1000")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 6) select * from cte1;") - rows.Check(testkit.Rows("7", "8", "9", "10", "11")) - - // Test with cte_max_recursion_depth - tk.MustExec("set cte_max_recursion_depth=2;") - rows = tk.MustQuery("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") - rows.Check(testkit.Rows("2")) - - err := tk.QueryToErr("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 3 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - - tk.MustExec("set cte_max_recursion_depth=1000;") - rows = tk.MustQuery("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 5 offset 996) select * from cte1;") - rows.Check(testkit.Rows("996", "997", "998", "999", "1000")) - - err = tk.QueryToErr("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 5 offset 997) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 0 offset 1) select * from cte1") - rows.Check(testkit.Rows()) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 0 offset 10) select * from cte1") - rows.Check(testkit.Rows()) - - // Test join. - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 order by dt1.c1, dt2.c1;") - rows.Check(testkit.Rows("2 2", "2 3", "3 2", "3 3")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1 order by dt1.c1, dt1.c1;") - rows.Check(testkit.Rows("2 2", "3 3")) - - // Test subquery. - // Different with mysql, maybe it's mysql bug?(https://bugs.mysql.com/bug.php?id=103890&thanks=4) - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 where c1 in (select 2);") - rows.Check(testkit.Rows("2")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 dt where c1 in (select c1 from cte1 where 1 = dt.c1 - 1);") - rows.Check(testkit.Rows("2")) - - // Test Apply. - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 where cte1.c1 = (select dt1.c1 from cte1 dt1 where dt1.c1 = cte1.c1);") - rows.Check(testkit.Rows("2", "3")) - - // Recursive tests with table. - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec("insert into t1 values(1), (2), (3);") - - // Error: ERROR 1221 (HY000): Incorrect usage of UNION and LIMIT. - // Limit can only be at the end of SQL stmt. - err = tk.ExecToErr("with recursive cte1(c1) as (select c1 from t1 limit 1 offset 1 union select c1 + 1 from cte1 limit 0 offset 1) select * from cte1") - require.EqualError(t, err, "[planner:1221]Incorrect usage of UNION and LIMIT") - - // Basic non-recusive tests. - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 1 offset 1) select * from cte1") - rows.Check(testkit.Rows("2")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 0 offset 1) select * from cte1") - rows.Check(testkit.Rows()) - - rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 2 offset 0) select * from cte1") - rows.Check(testkit.Rows("1", "2")) - - // Test with table. - tk.MustExec("drop table if exists t1;") - insertStr := "insert into t1 values(0)" - for i := 1; i < 300; i++ { - insertStr += fmt.Sprintf(", (%d)", i) - } - - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec(insertStr) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 1) select * from cte1") - rows.Check(testkit.Rows("0")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 1 offset 100) select * from cte1") - rows.Check(testkit.Rows("100")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 5 offset 100) select * from cte1") - rows.Check(testkit.Rows("100", "101", "102", "103", "104")) - - // Basic non-recursive tests. - rows = tk.MustQuery("with cte1 as (select c1 from t1 limit 2 offset 1) select * from cte1") - rows.Check(testkit.Rows("1", "2")) - - rows = tk.MustQuery("with cte1 as (select c1 from t1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") - rows.Check(testkit.Rows("1 1", "2 2")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 0 offset 1) select * from cte1") - rows.Check(testkit.Rows()) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 0 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") - rows.Check(testkit.Rows()) - - // rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 5 offset 100) select * from cte1") - // rows.Check(testkit.Rows("100", "101", "102", "103", "104")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 limit 3 offset 100) select * from cte1") - rows.Check(testkit.Rows("100", "101", "102")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 limit 3 offset 100) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") - rows.Check(testkit.Rows("100 100", "101 101", "102 102")) - - // Test limit 0. - tk.MustExec("set cte_max_recursion_depth = 0;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec("insert into t1 values(0);") - rows = tk.MustQuery("with recursive cte1 as (select 1/c1 c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 2 limit 0) select * from cte1;") - rows.Check(testkit.Rows()) - // MySQL err: ERROR 1365 (22012): Division by 0. Because it gives error when computing 1/c1. - err = tk.QueryToErr("with recursive cte1 as (select 1/c1 c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 2 limit 1) select * from cte1;") - require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") - - tk.MustExec("set cte_max_recursion_depth = 1000;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec("insert into t1 values(1), (2), (3);") - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 2) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "4")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "4", "5")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "4", "5", "6")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 3) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") - rows.Check(testkit.Rows("4")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 3) select * from cte1;") - rows.Check(testkit.Rows("4", "5")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 3) select * from cte1;") - rows.Check(testkit.Rows("4", "5", "6")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 3) select * from cte1;") - rows.Check(testkit.Rows("4", "5", "6", "7")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 4) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 4) select * from cte1;") - rows.Check(testkit.Rows("5")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 4) select * from cte1;") - rows.Check(testkit.Rows("5", "6")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 4) select * from cte1;") - rows.Check(testkit.Rows("5", "6", "7")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 4) select * from cte1;") - rows.Check(testkit.Rows("5", "6", "7", "8")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 2) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "2")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "2", "3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 2) select * from cte1;") - rows.Check(testkit.Rows("3", "2", "3", "4")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 3) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") - rows.Check(testkit.Rows("2")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 3) select * from cte1;") - rows.Check(testkit.Rows("2", "3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 3) select * from cte1;") - rows.Check(testkit.Rows("2", "3", "4")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 3) select * from cte1;") - rows.Check(testkit.Rows("2", "3", "4", "3")) - - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 4) select * from cte1;") - rows.Check(testkit.Rows()) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 4) select * from cte1;") - rows.Check(testkit.Rows("3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 4) select * from cte1;") - rows.Check(testkit.Rows("3", "4")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 4) select * from cte1;") - rows.Check(testkit.Rows("3", "4", "3")) - rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 4) select * from cte1;") - rows.Check(testkit.Rows("3", "4", "3", "4")) -} - -func TestSpillToDisk(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("SET GLOBAL tidb_enable_tmp_storage_on_oom = 1") - defer tk.MustExec("SET GLOBAL tidb_enable_tmp_storage_on_oom = 0") - tk.MustExec("use test;") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testCTEStorageSpill", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testCTEStorageSpill")) - tk.MustExec("set tidb_mem_quota_query = 1073741824;") - }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill")) - }() - - // Use duplicated rows to test UNION DISTINCT. - tk.MustExec("set tidb_mem_quota_query = 1073741824;") - insertStr := "insert into t1 values(0)" - rowNum := 1000 - vals := make([]int, rowNum) - vals[0] = 0 - for i := 1; i < rowNum; i++ { - v := rand.Intn(100) - vals[i] = v - insertStr += fmt.Sprintf(", (%d)", v) - } - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int);") - tk.MustExec(insertStr) - tk.MustExec("set tidb_mem_quota_query = 40000;") - tk.MustExec("set cte_max_recursion_depth = 500000;") - sql := fmt.Sprintf("with recursive cte1 as ( "+ - "select c1 from t1 "+ - "union "+ - "select c1 + 1 c1 from cte1 where c1 < %d) "+ - "select c1 from cte1 order by c1;", rowNum) - rows := tk.MustQuery(sql) - - memTracker := tk.Session().GetSessionVars().StmtCtx.MemTracker - diskTracker := tk.Session().GetSessionVars().StmtCtx.DiskTracker - require.Greater(t, memTracker.MaxConsumed(), int64(0)) - require.Greater(t, diskTracker.MaxConsumed(), int64(0)) - - slices.Sort(vals) - resRows := make([]string, 0, rowNum) - for i := vals[0]; i <= rowNum; i++ { - resRows = append(resRows, fmt.Sprintf("%d", i)) - } - rows.Check(testkit.Rows(resRows...)) -} - -func TestCTEExecError(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists src;") - tk.MustExec("create table src(first int, second int);") - - insertStr := fmt.Sprintf("insert into src values (%d, %d)", rand.Intn(1000), rand.Intn(1000)) - for i := 0; i < 1000; i++ { - insertStr += fmt.Sprintf(",(%d, %d)", rand.Intn(1000), rand.Intn(1000)) - } - insertStr += ";" - tk.MustExec(insertStr) - - // Increase projection concurrency and decrease chunk size - // to increase the probability of reproducing the problem. - tk.MustExec("set tidb_max_chunk_size = 32") - tk.MustExec("set tidb_projection_concurrency = 20") - for i := 0; i < 10; i++ { - err := tk.QueryToErr("with recursive cte(iter, first, second, result) as " + - "(select 1, first, second, first+second from src " + - " union all " + - "select iter+1, second, result, second+result from cte where iter < 80 )" + - "select * from cte") - require.True(t, terror.ErrorEqual(err, types.ErrOverflow)) - } -} - -// https://github.com/pingcap/tidb/issues/33965. -func TestCTEsInView(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - tk.MustExec("create database if not exists test1;") - tk.MustExec("create table test.t (a int);") - tk.MustExec("create table test1.t (a int);") - tk.MustExec("insert into test.t values (1);") - tk.MustExec("insert into test1.t values (2);") - - tk.MustExec("use test;") - tk.MustExec("create definer='root'@'localhost' view test.v as with tt as (select * from t) select * from tt;") - tk.MustQuery("select * from test.v;").Check(testkit.Rows("1")) - tk.MustExec("use test1;") - tk.MustQuery("select * from test.v;").Check(testkit.Rows("1")) -} - -func TestCTEPanic(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("create table t1(c1 int)") - tk.MustExec("insert into t1 values(1), (2), (3)") - - fpPathPrefix := "github.com/pingcap/tidb/executor/" - fp := "testCTESeedPanic" - require.NoError(t, failpoint.Enable(fpPathPrefix+fp, fmt.Sprintf(`panic("%s")`, fp))) - err := tk.QueryToErr("with recursive cte1 as (select c1 from t1 union all select c1 + 1 from cte1 where c1 < 5) select t_alias_1.c1 from cte1 as t_alias_1 inner join cte1 as t_alias_2 on t_alias_1.c1 = t_alias_2.c1 order by c1") - require.Contains(t, err.Error(), fp) - require.NoError(t, failpoint.Disable(fpPathPrefix+fp)) - - fp = "testCTERecursivePanic" - require.NoError(t, failpoint.Enable(fpPathPrefix+fp, fmt.Sprintf(`panic("%s")`, fp))) - err = tk.QueryToErr("with recursive cte1 as (select c1 from t1 union all select c1 + 1 from cte1 where c1 < 5) select t_alias_1.c1 from cte1 as t_alias_1 inner join cte1 as t_alias_2 on t_alias_1.c1 = t_alias_2.c1 order by c1") - require.Contains(t, err.Error(), fp) - require.NoError(t, failpoint.Disable(fpPathPrefix+fp)) -} - -func TestCTEDelSpillFile(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t1, t2;") - tk.MustExec("create table t1(c1 int, c2 int);") - tk.MustExec("create table t2(c1 int);") - tk.MustExec("set @@cte_max_recursion_depth = 1000000;") - tk.MustExec("set global tidb_mem_oom_action = 'log';") - tk.MustExec("set @@tidb_mem_quota_query = 100;") - tk.MustExec("insert into t2 values(1);") - tk.MustExec("insert into t1 (c1, c2) with recursive cte1 as (select c1 from t2 union select cte1.c1 + 1 from cte1 where cte1.c1 < 100000) select cte1.c1, cte1.c1+1 from cte1;") - require.Nil(t, tk.Session().GetSessionVars().StmtCtx.CTEStorageMap) -} - -func TestCTEShareCorColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t1, t2;") - tk.MustExec("create table t1(c1 int, c2 varchar(100));") - tk.MustExec("insert into t1 values(1, '2020-10-10');") - tk.MustExec("create table t2(c1 int, c2 date);") - tk.MustExec("insert into t2 values(1, '2020-10-10');") - for i := 0; i < 100; i++ { - tk.MustQuery("with cte1 as (select t1.c1, (select t2.c2 from t2 where t2.c2 = str_to_date(t1.c2, '%Y-%m-%d')) from t1 inner join t2 on t1.c1 = t2.c1) select /*+ hash_join_build(alias1) */ * from cte1 alias1 inner join cte1 alias2 on alias1.c1 = alias2.c1;").Check(testkit.Rows("1 2020-10-10 1 2020-10-10")) - tk.MustQuery("with cte1 as (select t1.c1, (select t2.c2 from t2 where t2.c2 = str_to_date(t1.c2, '%Y-%m-%d')) from t1 inner join t2 on t1.c1 = t2.c1) select /*+ hash_join_build(alias2) */ * from cte1 alias1 inner join cte1 alias2 on alias1.c1 = alias2.c1;").Check(testkit.Rows("1 2020-10-10 1 2020-10-10")) - } - - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(a int);") - tk.MustExec("insert into t1 values(1), (2);") - tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS( WITH RECURSIVE qn AS (SELECT a AS b UNION ALL SELECT b+1 FROM qn WHERE b=0 or b = 1) SELECT * FROM qn dtqn1 where exists (select /*+ NO_DECORRELATE() */ b from qn where dtqn1.b+1));").Check(testkit.Rows("1", "2")) -} diff --git a/executor/ddl.go b/executor/ddl.go deleted file mode 100644 index 556f11f22bddb..0000000000000 --- a/executor/ddl.go +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// DDLExec represents a DDL executor. -// It grabs a DDL instance from Domain, calling the DDL methods to do the work. -type DDLExec struct { - exec.BaseExecutor - - stmt ast.StmtNode - is infoschema.InfoSchema - tempTableDDL temptable.TemporaryTableDDL - done bool -} - -// toErr converts the error to the ErrInfoSchemaChanged when the schema is outdated. -func (e *DDLExec) toErr(err error) error { - // The err may be cause by schema changed, here we distinguish the ErrInfoSchemaChanged error from other errors. - dom := domain.GetDomain(e.Ctx()) - checker := domain.NewSchemaChecker(dom, e.is.SchemaMetaVersion(), nil, true) - txn, err1 := e.Ctx().Txn(true) - if err1 != nil { - logutil.BgLogger().Error("active txn failed", zap.Error(err1)) - return err - } - _, schemaInfoErr := checker.Check(txn.StartTS()) - if schemaInfoErr != nil { - return errors.Trace(schemaInfoErr) - } - return err -} - -func (e *DDLExec) getLocalTemporaryTable(schema model.CIStr, table model.CIStr) (table.Table, bool) { - tbl, err := e.Ctx().GetInfoSchema().(infoschema.InfoSchema).TableByName(schema, table) - if infoschema.ErrTableNotExists.Equal(err) { - return nil, false - } - - if tbl.Meta().TempTableType != model.TempTableLocal { - return nil, false - } - - return tbl, true -} - -// Next implements the Executor Next interface. -func (e *DDLExec) Next(ctx context.Context, _ *chunk.Chunk) (err error) { - if e.done { - return nil - } - e.done = true - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) - // For each DDL, we should commit the previous transaction and create a new transaction. - // Following cases are exceptions - var localTempTablesToDrop []*ast.TableName - switch s := e.stmt.(type) { - case *ast.CreateTableStmt: - if s.TemporaryKeyword == ast.TemporaryLocal { - return e.createSessionTemporaryTable(s) - } - case *ast.DropTableStmt: - if s.IsView { - break - } - - for tbIdx := len(s.Tables) - 1; tbIdx >= 0; tbIdx-- { - if _, ok := e.getLocalTemporaryTable(s.Tables[tbIdx].Schema, s.Tables[tbIdx].Name); ok { - localTempTablesToDrop = append(localTempTablesToDrop, s.Tables[tbIdx]) - s.Tables = append(s.Tables[:tbIdx], s.Tables[tbIdx+1:]...) - } - } - - // Statement `DROP TEMPORARY TABLE ...` should not have non-local temporary tables - if s.TemporaryKeyword == ast.TemporaryLocal && len(s.Tables) > 0 { - nonExistsTables := make([]string, 0, len(s.Tables)) - for _, tn := range s.Tables { - nonExistsTables = append(nonExistsTables, ast.Ident{Schema: tn.Schema, Name: tn.Name}.String()) - } - err = infoschema.ErrTableDropExists.GenWithStackByArgs(strings.Join(nonExistsTables, ",")) - if s.IfExists { - e.Ctx().GetSessionVars().StmtCtx.AppendNote(err) - return nil - } - return err - } - - // if all tables are local temporary, directly drop those tables. - if len(s.Tables) == 0 { - return e.dropLocalTemporaryTables(localTempTablesToDrop) - } - } - - if err = sessiontxn.NewTxnInStmt(ctx, e.Ctx()); err != nil { - return err - } - - defer func() { - e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue = false - e.Ctx().GetSessionVars().StmtCtx.DDLJobID = 0 - }() - - switch x := e.stmt.(type) { - case *ast.AlterDatabaseStmt: - err = e.executeAlterDatabase(x) - case *ast.AlterTableStmt: - err = e.executeAlterTable(ctx, x) - case *ast.CreateIndexStmt: - err = e.executeCreateIndex(x) - case *ast.CreateDatabaseStmt: - err = e.executeCreateDatabase(x) - case *ast.FlashBackDatabaseStmt: - err = e.executeFlashbackDatabase(x) - case *ast.CreateTableStmt: - err = e.executeCreateTable(x) - case *ast.CreateViewStmt: - err = e.executeCreateView(ctx, x) - case *ast.DropIndexStmt: - err = e.executeDropIndex(x) - case *ast.DropDatabaseStmt: - err = e.executeDropDatabase(x) - case *ast.DropTableStmt: - if x.IsView { - err = e.executeDropView(x) - } else { - err = e.executeDropTable(x) - if err == nil { - err = e.dropLocalTemporaryTables(localTempTablesToDrop) - } - } - case *ast.RecoverTableStmt: - err = e.executeRecoverTable(x) - case *ast.FlashBackTableStmt: - err = e.executeFlashbackTable(x) - case *ast.FlashBackToTimestampStmt: - if len(x.Tables) != 0 { - err = dbterror.ErrGeneralUnsupportedDDL.GenWithStack("Unsupported FLASHBACK table TO TIMESTAMP") - } else if x.DBName.O != "" { - err = dbterror.ErrGeneralUnsupportedDDL.GenWithStack("Unsupported FLASHBACK database TO TIMESTAMP") - } else { - err = e.executeFlashBackCluster(x) - } - case *ast.RenameTableStmt: - err = e.executeRenameTable(x) - case *ast.TruncateTableStmt: - err = e.executeTruncateTable(x) - case *ast.LockTablesStmt: - err = e.executeLockTables(x) - case *ast.UnlockTablesStmt: - err = e.executeUnlockTables(x) - case *ast.CleanupTableLockStmt: - err = e.executeCleanupTableLock(x) - case *ast.RepairTableStmt: - err = e.executeRepairTable(x) - case *ast.CreateSequenceStmt: - err = e.executeCreateSequence(x) - case *ast.DropSequenceStmt: - err = e.executeDropSequence(x) - case *ast.AlterSequenceStmt: - err = e.executeAlterSequence(x) - case *ast.CreatePlacementPolicyStmt: - err = e.executeCreatePlacementPolicy(x) - case *ast.DropPlacementPolicyStmt: - err = e.executeDropPlacementPolicy(x) - case *ast.AlterPlacementPolicyStmt: - err = e.executeAlterPlacementPolicy(x) - case *ast.CreateResourceGroupStmt: - err = e.executeCreateResourceGroup(x) - case *ast.DropResourceGroupStmt: - err = e.executeDropResourceGroup(x) - case *ast.AlterResourceGroupStmt: - err = e.executeAlterResourceGroup(x) - } - if err != nil { - // If the owner return ErrTableNotExists error when running this DDL, it may be caused by schema changed, - // otherwise, ErrTableNotExists can be returned before putting this DDL job to the job queue. - if (e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue && infoschema.ErrTableNotExists.Equal(err)) || - !e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue { - return e.toErr(err) - } - return err - } - - dom := domain.GetDomain(e.Ctx()) - // Update InfoSchema in TxnCtx, so it will pass schema check. - is := dom.InfoSchema() - txnCtx := e.Ctx().GetSessionVars().TxnCtx - txnCtx.InfoSchema = is - // DDL will force commit old transaction, after DDL, in transaction status should be false. - e.Ctx().GetSessionVars().SetInTxn(false) - return nil -} - -func (e *DDLExec) executeTruncateTable(s *ast.TruncateTableStmt) error { - ident := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name} - if _, exist := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); exist { - return e.tempTableDDL.TruncateLocalTemporaryTable(s.Table.Schema, s.Table.Name) - } - err := domain.GetDomain(e.Ctx()).DDL().TruncateTable(e.Ctx(), ident) - return err -} - -func (e *DDLExec) executeRenameTable(s *ast.RenameTableStmt) error { - for _, tables := range s.TableToTables { - if _, ok := e.getLocalTemporaryTable(tables.OldTable.Schema, tables.OldTable.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("RENAME TABLE") - } - } - return domain.GetDomain(e.Ctx()).DDL().RenameTable(e.Ctx(), s) -} - -func (e *DDLExec) executeCreateDatabase(s *ast.CreateDatabaseStmt) error { - err := domain.GetDomain(e.Ctx()).DDL().CreateSchema(e.Ctx(), s) - return err -} - -func (e *DDLExec) executeAlterDatabase(s *ast.AlterDatabaseStmt) error { - err := domain.GetDomain(e.Ctx()).DDL().AlterSchema(e.Ctx(), s) - return err -} - -func (e *DDLExec) executeCreateTable(s *ast.CreateTableStmt) error { - err := domain.GetDomain(e.Ctx()).DDL().CreateTable(e.Ctx(), s) - return err -} - -func (e *DDLExec) createSessionTemporaryTable(s *ast.CreateTableStmt) error { - is := e.Ctx().GetInfoSchema().(infoschema.InfoSchema) - dbInfo, ok := is.SchemaByName(s.Table.Schema) - if !ok { - return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(s.Table.Schema.O) - } - - _, exists := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name) - if exists { - err := infoschema.ErrTableExists.GenWithStackByArgs(ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}) - if s.IfNotExists { - e.Ctx().GetSessionVars().StmtCtx.AppendNote(err) - return nil - } - return err - } - - tbInfo, err := ddl.BuildSessionTemporaryTableInfo(e.Ctx(), is, s, dbInfo.Charset, dbInfo.Collate, dbInfo.PlacementPolicyRef) - if err != nil { - return err - } - - if err = e.tempTableDDL.CreateLocalTemporaryTable(dbInfo, tbInfo); err != nil { - return err - } - - sessiontxn.GetTxnManager(e.Ctx()).OnLocalTemporaryTableCreated() - return nil -} - -func (e *DDLExec) executeCreateView(ctx context.Context, s *ast.CreateViewStmt) error { - ret := &core.PreprocessorReturn{} - err := core.Preprocess(ctx, e.Ctx(), s.Select, core.WithPreprocessorReturn(ret)) - if err != nil { - return errors.Trace(err) - } - if ret.IsStaleness { - return exeerrors.ErrViewInvalid.GenWithStackByArgs(s.ViewName.Schema.L, s.ViewName.Name.L) - } - - return domain.GetDomain(e.Ctx()).DDL().CreateView(e.Ctx(), s) -} - -func (e *DDLExec) executeCreateIndex(s *ast.CreateIndexStmt) error { - if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("CREATE INDEX") - } - - return domain.GetDomain(e.Ctx()).DDL().CreateIndex(e.Ctx(), s) -} - -func (e *DDLExec) executeDropDatabase(s *ast.DropDatabaseStmt) error { - dbName := s.Name - - // Protect important system table from been dropped by a mistake. - // I can hardly find a case that a user really need to do this. - if dbName.L == "mysql" { - return errors.New("Drop 'mysql' database is forbidden") - } - - err := domain.GetDomain(e.Ctx()).DDL().DropSchema(e.Ctx(), s) - sessionVars := e.Ctx().GetSessionVars() - if err == nil && strings.ToLower(sessionVars.CurrentDB) == dbName.L { - sessionVars.CurrentDB = "" - err = sessionVars.SetSystemVar(variable.CharsetDatabase, mysql.DefaultCharset) - if err != nil { - return err - } - err = sessionVars.SetSystemVar(variable.CollationDatabase, mysql.DefaultCollationName) - if err != nil { - return err - } - } - return err -} - -func (e *DDLExec) executeDropTable(s *ast.DropTableStmt) error { - return domain.GetDomain(e.Ctx()).DDL().DropTable(e.Ctx(), s) -} - -func (e *DDLExec) executeDropView(s *ast.DropTableStmt) error { - return domain.GetDomain(e.Ctx()).DDL().DropView(e.Ctx(), s) -} - -func (e *DDLExec) executeDropSequence(s *ast.DropSequenceStmt) error { - return domain.GetDomain(e.Ctx()).DDL().DropSequence(e.Ctx(), s) -} - -func (e *DDLExec) dropLocalTemporaryTables(localTempTables []*ast.TableName) error { - if len(localTempTables) == 0 { - return nil - } - - for _, tb := range localTempTables { - err := e.tempTableDDL.DropLocalTemporaryTable(tb.Schema, tb.Name) - if err != nil { - return err - } - } - - return nil -} - -func (e *DDLExec) executeDropIndex(s *ast.DropIndexStmt) error { - if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("DROP INDEX") - } - - return domain.GetDomain(e.Ctx()).DDL().DropIndex(e.Ctx(), s) -} - -func (e *DDLExec) executeAlterTable(ctx context.Context, s *ast.AlterTableStmt) error { - if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("ALTER TABLE") - } - - return domain.GetDomain(e.Ctx()).DDL().AlterTable(ctx, e.Ctx(), s) -} - -// executeRecoverTable represents a recover table executor. -// It is built from "recover table" statement, -// is used to recover the table that deleted by mistake. -func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { - dom := domain.GetDomain(e.Ctx()) - var job *model.Job - var err error - var tblInfo *model.TableInfo - if s.JobID != 0 { - job, tblInfo, err = e.getRecoverTableByJobID(s, dom) - } else { - job, tblInfo, err = e.getRecoverTableByTableName(s.Table) - } - if err != nil { - return err - } - // Check the table ID was not exists. - tbl, ok := dom.InfoSchema().TableByID(tblInfo.ID) - if ok { - return infoschema.ErrTableExists.GenWithStack("Table '%-.192s' already been recover to '%-.192s', can't be recover repeatedly", s.Table.Name.O, tbl.Meta().Name.O) - } - - m, err := domain.GetDomain(e.Ctx()).GetSnapshotMeta(job.StartTS) - if err != nil { - return err - } - autoIDs, err := m.GetAutoIDAccessors(job.SchemaID, job.TableID).Get() - if err != nil { - return err - } - - recoverInfo := &ddl.RecoverInfo{ - SchemaID: job.SchemaID, - TableInfo: tblInfo, - DropJobID: job.ID, - SnapshotTS: job.StartTS, - AutoIDs: autoIDs, - OldSchemaName: job.SchemaName, - OldTableName: tblInfo.Name.L, - } - // Call DDL RecoverTable. - err = domain.GetDomain(e.Ctx()).DDL().RecoverTable(e.Ctx(), recoverInfo) - return err -} - -func (e *DDLExec) getRecoverTableByJobID(s *ast.RecoverTableStmt, dom *domain.Domain) (*model.Job, *model.TableInfo, error) { - se, err := e.GetSysSession() - if err != nil { - return nil, nil, err - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - defer e.ReleaseSysSession(ctx, se) - job, err := ddl.GetHistoryJobByID(se, s.JobID) - if err != nil { - return nil, nil, err - } - if job == nil { - return nil, nil, dbterror.ErrDDLJobNotFound.GenWithStackByArgs(s.JobID) - } - if job.Type != model.ActionDropTable && job.Type != model.ActionTruncateTable { - return nil, nil, errors.Errorf("Job %v type is %v, not dropped/truncated table", job.ID, job.Type) - } - - // Check GC safe point for getting snapshot infoSchema. - err = gcutil.ValidateSnapshot(e.Ctx(), job.StartTS) - if err != nil { - return nil, nil, err - } - - // Get the snapshot infoSchema before drop table. - snapInfo, err := dom.GetSnapshotInfoSchema(job.StartTS) - if err != nil { - return nil, nil, err - } - // Get table meta from snapshot infoSchema. - table, ok := snapInfo.TableByID(job.TableID) - if !ok { - return nil, nil, infoschema.ErrTableNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", job.SchemaID), - fmt.Sprintf("(Table ID %d)", job.TableID), - ) - } - return job, table.Meta(), nil -} - -// GetDropOrTruncateTableInfoFromJobs gets the dropped/truncated table information from DDL jobs, -// it will use the `start_ts` of DDL job as snapshot to get the dropped/truncated table information. -func GetDropOrTruncateTableInfoFromJobs(jobs []*model.Job, gcSafePoint uint64, dom *domain.Domain, fn func(*model.Job, *model.TableInfo) (bool, error)) (bool, error) { - getTable := func(startTS uint64, schemaID int64, tableID int64) (*model.TableInfo, error) { - snapMeta, err := dom.GetSnapshotMeta(startTS) - if err != nil { - return nil, err - } - tbl, err := snapMeta.GetTable(schemaID, tableID) - return tbl, err - } - return ddl.GetDropOrTruncateTableInfoFromJobsByStore(jobs, gcSafePoint, getTable, fn) -} - -func (e *DDLExec) getRecoverTableByTableName(tableName *ast.TableName) (*model.Job, *model.TableInfo, error) { - txn, err := e.Ctx().Txn(true) - if err != nil { - return nil, nil, err - } - schemaName := tableName.Schema.L - if schemaName == "" { - schemaName = strings.ToLower(e.Ctx().GetSessionVars().CurrentDB) - } - if schemaName == "" { - return nil, nil, errors.Trace(core.ErrNoDB) - } - gcSafePoint, err := gcutil.GetGCSafePoint(e.Ctx()) - if err != nil { - return nil, nil, err - } - var jobInfo *model.Job - var tableInfo *model.TableInfo - dom := domain.GetDomain(e.Ctx()) - handleJobAndTableInfo := func(job *model.Job, tblInfo *model.TableInfo) (bool, error) { - if tblInfo.Name.L != tableName.Name.L { - return false, nil - } - schema, ok := dom.InfoSchema().SchemaByID(job.SchemaID) - if !ok { - return false, nil - } - if schema.Name.L == schemaName { - tableInfo = tblInfo - jobInfo = job - return true, nil - } - return false, nil - } - fn := func(jobs []*model.Job) (bool, error) { - return GetDropOrTruncateTableInfoFromJobs(jobs, gcSafePoint, dom, handleJobAndTableInfo) - } - err = ddl.IterHistoryDDLJobs(txn, fn) - if err != nil { - if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { - return nil, nil, errors.Errorf("Can't find dropped/truncated table '%s' in GC safe point %s", tableName.Name.O, model.TSConvert2Time(gcSafePoint).String()) - } - return nil, nil, err - } - if tableInfo == nil || jobInfo == nil { - return nil, nil, errors.Errorf("Can't find localTemporary/dropped/truncated table: %v in DDL history jobs", tableName.Name) - } - // Dropping local temporary tables won't appear in DDL jobs. - if tableInfo.TempTableType == model.TempTableGlobal { - return nil, nil, exeerrors.ErrUnsupportedFlashbackTmpTable - } - return jobInfo, tableInfo, nil -} - -func (e *DDLExec) executeFlashBackCluster(s *ast.FlashBackToTimestampStmt) error { - flashbackTS, err := staleread.CalculateAsOfTsExpr(context.Background(), e.Ctx(), s.FlashbackTS) - if err != nil { - return err - } - - return domain.GetDomain(e.Ctx()).DDL().FlashbackCluster(e.Ctx(), flashbackTS) -} - -func (e *DDLExec) executeFlashbackTable(s *ast.FlashBackTableStmt) error { - job, tblInfo, err := e.getRecoverTableByTableName(s.Table) - if err != nil { - return err - } - if len(s.NewName) != 0 { - tblInfo.Name = model.NewCIStr(s.NewName) - } - // Check the table ID was not exists. - is := domain.GetDomain(e.Ctx()).InfoSchema() - tbl, ok := is.TableByID(tblInfo.ID) - if ok { - return infoschema.ErrTableExists.GenWithStack("Table '%-.192s' already been flashback to '%-.192s', can't be flashback repeatedly", s.Table.Name.O, tbl.Meta().Name.O) - } - - m, err := domain.GetDomain(e.Ctx()).GetSnapshotMeta(job.StartTS) - if err != nil { - return err - } - autoIDs, err := m.GetAutoIDAccessors(job.SchemaID, job.TableID).Get() - if err != nil { - return err - } - - recoverInfo := &ddl.RecoverInfo{ - SchemaID: job.SchemaID, - TableInfo: tblInfo, - DropJobID: job.ID, - SnapshotTS: job.StartTS, - AutoIDs: autoIDs, - OldSchemaName: job.SchemaName, - OldTableName: s.Table.Name.L, - } - // Call DDL RecoverTable. - err = domain.GetDomain(e.Ctx()).DDL().RecoverTable(e.Ctx(), recoverInfo) - return err -} - -// executeFlashbackDatabase represents a restore schema executor. -// It is built from "flashback schema" statement, -// is used to recover the schema that deleted by mistake. -func (e *DDLExec) executeFlashbackDatabase(s *ast.FlashBackDatabaseStmt) error { - dbName := s.DBName - if len(s.NewName) > 0 { - dbName = model.NewCIStr(s.NewName) - } - // Check the Schema Name was not exists. - is := domain.GetDomain(e.Ctx()).InfoSchema() - if is.SchemaExists(dbName) { - return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbName) - } - recoverSchemaInfo, err := e.getRecoverDBByName(s.DBName) - if err != nil { - return err - } - // Check the Schema ID was not exists. - if schema, ok := is.SchemaByID(recoverSchemaInfo.ID); ok { - return infoschema.ErrDatabaseExists.GenWithStack("Schema '%-.192s' already been recover to '%-.192s', can't be recover repeatedly", s.DBName, schema.Name.O) - } - recoverSchemaInfo.Name = dbName - // Call DDL RecoverSchema. - err = domain.GetDomain(e.Ctx()).DDL().RecoverSchema(e.Ctx(), recoverSchemaInfo) - return err -} - -func (e *DDLExec) getRecoverDBByName(schemaName model.CIStr) (recoverSchemaInfo *ddl.RecoverSchemaInfo, err error) { - txn, err := e.Ctx().Txn(true) - if err != nil { - return nil, err - } - gcSafePoint, err := gcutil.GetGCSafePoint(e.Ctx()) - if err != nil { - return nil, err - } - dom := domain.GetDomain(e.Ctx()) - fn := func(jobs []*model.Job) (bool, error) { - for _, job := range jobs { - // Check GC safe point for getting snapshot infoSchema. - err = gcutil.ValidateSnapshotWithGCSafePoint(job.StartTS, gcSafePoint) - if err != nil { - return false, err - } - if job.Type != model.ActionDropSchema { - continue - } - snapMeta, err := dom.GetSnapshotMeta(job.StartTS) - if err != nil { - return false, err - } - schemaInfo, err := snapMeta.GetDatabase(job.SchemaID) - if err != nil { - return false, err - } - if schemaInfo == nil { - // The dropped DDL maybe execute failed that caused by the parallel DDL execution, - // then can't find the schema from the snapshot info-schema. Should just ignore error here, - // see more in TestParallelDropSchemaAndDropTable. - continue - } - if schemaInfo.Name.L != schemaName.L { - continue - } - tables, err := snapMeta.ListTables(job.SchemaID) - if err != nil { - return false, err - } - recoverTabsInfo := make([]*ddl.RecoverInfo, 0) - for _, tblInfo := range tables { - autoIDs, err := snapMeta.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Get() - if err != nil { - return false, err - } - recoverTabsInfo = append(recoverTabsInfo, &ddl.RecoverInfo{ - SchemaID: job.SchemaID, - TableInfo: tblInfo, - DropJobID: job.ID, - SnapshotTS: job.StartTS, - AutoIDs: autoIDs, - OldSchemaName: schemaName.L, - OldTableName: tblInfo.Name.L, - }) - } - recoverSchemaInfo = &ddl.RecoverSchemaInfo{DBInfo: schemaInfo, RecoverTabsInfo: recoverTabsInfo, DropJobID: job.ID, SnapshotTS: job.StartTS, OldSchemaName: schemaName} - return true, nil - } - return false, nil - } - err = ddl.IterHistoryDDLJobs(txn, fn) - if err != nil { - if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { - return nil, errors.Errorf("Can't find dropped database '%s' in GC safe point %s", schemaName.O, model.TSConvert2Time(gcSafePoint).String()) - } - return nil, err - } - if recoverSchemaInfo == nil { - return nil, errors.Errorf("Can't find dropped database: %v in DDL history jobs", schemaName.O) - } - return -} - -func (e *DDLExec) executeLockTables(s *ast.LockTablesStmt) error { - if !config.TableLockEnabled() { - e.Ctx().GetSessionVars().StmtCtx.AppendWarning(exeerrors.ErrFuncNotEnabled.GenWithStackByArgs("LOCK TABLES", "enable-table-lock")) - return nil - } - - for _, tb := range s.TableLocks { - if _, ok := e.getLocalTemporaryTable(tb.Table.Schema, tb.Table.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("LOCK TABLES") - } - } - - return domain.GetDomain(e.Ctx()).DDL().LockTables(e.Ctx(), s) -} - -func (e *DDLExec) executeUnlockTables(_ *ast.UnlockTablesStmt) error { - if !config.TableLockEnabled() { - e.Ctx().GetSessionVars().StmtCtx.AppendWarning(exeerrors.ErrFuncNotEnabled.GenWithStackByArgs("UNLOCK TABLES", "enable-table-lock")) - return nil - } - lockedTables := e.Ctx().GetAllTableLocks() - return domain.GetDomain(e.Ctx()).DDL().UnlockTables(e.Ctx(), lockedTables) -} - -func (e *DDLExec) executeCleanupTableLock(s *ast.CleanupTableLockStmt) error { - for _, tb := range s.Tables { - if _, ok := e.getLocalTemporaryTable(tb.Schema, tb.Name); ok { - return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("ADMIN CLEANUP TABLE LOCK") - } - } - return domain.GetDomain(e.Ctx()).DDL().CleanupTableLock(e.Ctx(), s.Tables) -} - -func (e *DDLExec) executeRepairTable(s *ast.RepairTableStmt) error { - return domain.GetDomain(e.Ctx()).DDL().RepairTable(e.Ctx(), s.CreateStmt) -} - -func (e *DDLExec) executeCreateSequence(s *ast.CreateSequenceStmt) error { - return domain.GetDomain(e.Ctx()).DDL().CreateSequence(e.Ctx(), s) -} - -func (e *DDLExec) executeAlterSequence(s *ast.AlterSequenceStmt) error { - return domain.GetDomain(e.Ctx()).DDL().AlterSequence(e.Ctx(), s) -} - -func (e *DDLExec) executeCreatePlacementPolicy(s *ast.CreatePlacementPolicyStmt) error { - return domain.GetDomain(e.Ctx()).DDL().CreatePlacementPolicy(e.Ctx(), s) -} - -func (e *DDLExec) executeDropPlacementPolicy(s *ast.DropPlacementPolicyStmt) error { - return domain.GetDomain(e.Ctx()).DDL().DropPlacementPolicy(e.Ctx(), s) -} - -func (e *DDLExec) executeAlterPlacementPolicy(s *ast.AlterPlacementPolicyStmt) error { - return domain.GetDomain(e.Ctx()).DDL().AlterPlacementPolicy(e.Ctx(), s) -} - -func (e *DDLExec) executeCreateResourceGroup(s *ast.CreateResourceGroupStmt) error { - if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { - return infoschema.ErrResourceGroupSupportDisabled - } - return domain.GetDomain(e.Ctx()).DDL().AddResourceGroup(e.Ctx(), s) -} - -func (e *DDLExec) executeAlterResourceGroup(s *ast.AlterResourceGroupStmt) error { - if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { - return infoschema.ErrResourceGroupSupportDisabled - } - return domain.GetDomain(e.Ctx()).DDL().AlterResourceGroup(e.Ctx(), s) -} - -func (e *DDLExec) executeDropResourceGroup(s *ast.DropResourceGroupStmt) error { - if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { - return infoschema.ErrResourceGroupSupportDisabled - } - return domain.GetDomain(e.Ctx()).DDL().DropResourceGroup(e.Ctx(), s) -} diff --git a/executor/distsql.go b/executor/distsql.go deleted file mode 100644 index 86ff558349d99..0000000000000 --- a/executor/distsql.go +++ /dev/null @@ -1,1570 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "context" - "fmt" - "runtime/trace" - "slices" - "sort" - "strings" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - plannerutil "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -var ( - _ exec.Executor = &TableReaderExecutor{} - _ exec.Executor = &IndexReaderExecutor{} - _ exec.Executor = &IndexLookUpExecutor{} -) - -// LookupTableTaskChannelSize represents the channel size of the index double read taskChan. -var LookupTableTaskChannelSize int32 = 50 - -// lookupTableTask is created from a partial result of an index request which -// contains the handles in those index keys. -type lookupTableTask struct { - handles []kv.Handle - rowIdx []int // rowIdx represents the handle index for every row. Only used when keep order. - rows []chunk.Row - idxRows *chunk.Chunk - cursor int - - // after the cop task is built, buildDone will be set to the current instant, for Next wait duration statistic. - buildDoneTime time.Time - doneCh chan error - - // indexOrder map is used to save the original index order for the handles. - // Without this map, the original index order might be lost. - // The handles fetched from index is originally ordered by index, but we need handles to be ordered by itself - // to do table request. - indexOrder *kv.HandleMap - // duplicatedIndexOrder map likes indexOrder. But it's used when checkIndexValue isn't nil and - // the same handle of index has multiple values. - duplicatedIndexOrder *kv.HandleMap - - // partitionTable indicates whether this task belongs to a partition table and which partition table it is. - partitionTable table.PhysicalTable - - // memUsage records the memory usage of this task calculated by table worker. - // memTracker is used to release memUsage after task is done and unused. - // - // The sequence of function calls are: - // 1. calculate task.memUsage. - // 2. task.memTracker = tableWorker.memTracker - // 3. task.memTracker.Consume(task.memUsage) - // 4. task.memTracker.Consume(-task.memUsage) - // - // Step 1~3 are completed in "tableWorker.executeTask". - // Step 4 is completed in "IndexLookUpExecutor.Next". - memUsage int64 - memTracker *memory.Tracker -} - -func (task *lookupTableTask) Len() int { - return len(task.rows) -} - -func (task *lookupTableTask) Less(i, j int) bool { - return task.rowIdx[i] < task.rowIdx[j] -} - -func (task *lookupTableTask) Swap(i, j int) { - task.rowIdx[i], task.rowIdx[j] = task.rowIdx[j], task.rowIdx[i] - task.rows[i], task.rows[j] = task.rows[j], task.rows[i] -} - -// Closeable is a interface for closeable structures. -type Closeable interface { - // Close closes the object. - Close() error -} - -// closeAll closes all objects even if an object returns an error. -// If multiple objects returns error, the first error will be returned. -func closeAll(objs ...Closeable) error { - var err error - for _, obj := range objs { - if obj != nil { - err1 := obj.Close() - if err == nil && err1 != nil { - err = err1 - } - } - } - if err != nil { - return errors.Trace(err) - } - return nil -} - -// rebuildIndexRanges will be called if there's correlated column in access conditions. We will rebuild the range -// by substituting correlated column with the constant. -func rebuildIndexRanges(ctx sessionctx.Context, is *plannercore.PhysicalIndexScan, idxCols []*expression.Column, colLens []int) (ranges []*ranger.Range, err error) { - access := make([]expression.Expression, 0, len(is.AccessCondition)) - for _, cond := range is.AccessCondition { - newCond, err1 := expression.SubstituteCorCol2Constant(cond) - if err1 != nil { - return nil, err1 - } - access = append(access, newCond) - } - // All of access conditions must be used to build ranges, so we don't limit range memory usage. - ranges, _, err = ranger.DetachSimpleCondAndBuildRangeForIndex(ctx, access, idxCols, colLens, 0) - return ranges, err -} - -// IndexReaderExecutor sends dag request and reads index data from kv layer. -type IndexReaderExecutor struct { - exec.BaseExecutor - - // For a partitioned table, the IndexReaderExecutor works on a partition, so - // the type of this table field is actually `table.PhysicalTable`. - table table.Table - index *model.IndexInfo - physicalTableID int64 - ranges []*ranger.Range - partitions []table.PhysicalTable - partRangeMap map[int64][]*ranger.Range // each partition may have different ranges - partitionIDMap map[int64]struct{} // partitionIDs that global index access - - // kvRanges are only used for union scan. - kvRanges []kv.KeyRange - dagPB *tipb.DAGRequest - startTS uint64 - txnScope string - readReplicaScope string - isStaleness bool - netDataSize float64 - // result returns one or more distsql.PartialResult and each PartialResult is returned by one region. - result distsql.SelectResult - // columns are only required by union scan. - columns []*model.ColumnInfo - // outputColumns are only required by union scan. - outputColumns []*expression.Column - - paging bool - - keepOrder bool - desc bool - // byItems only for partition table with orderBy + pushedLimit - byItems []*plannerutil.ByItems - - corColInFilter bool - corColInAccess bool - idxCols []*expression.Column - colLens []int - plans []plannercore.PhysicalPlan - - memTracker *memory.Tracker - - selectResultHook // for testing - - // If dummy flag is set, this is not a real IndexReader, it just provides the KV ranges for UnionScan. - // Used by the temporary table, cached table. - dummy bool -} - -// Table implements the dataSourceExecutor interface. -func (e *IndexReaderExecutor) Table() table.Table { - return e.table -} - -func (e *IndexReaderExecutor) setDummy() { - e.dummy = true -} - -// Close clears all resources hold by current object. -func (e *IndexReaderExecutor) Close() (err error) { - if e.result != nil { - err = e.result.Close() - } - e.result = nil - e.kvRanges = e.kvRanges[:0] - if e.dummy { - return nil - } - return err -} - -// Next implements the Executor Next interface. -func (e *IndexReaderExecutor) Next(ctx context.Context, req *chunk.Chunk) error { - if e.dummy { - req.Reset() - return nil - } - - return e.result.Next(ctx, req) -} - -// TODO: cleanup this method. -func (e *IndexReaderExecutor) buildKeyRanges(sc *stmtctx.StatementContext, ranges []*ranger.Range, physicalID int64) ([]kv.KeyRange, error) { - var ( - rRanges *kv.KeyRanges - err error - ) - if e.index.ID == -1 { - rRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, ranges) - } else { - rRanges, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, ranges) - } - return rRanges.FirstPartitionRange(), err -} - -// Open implements the Executor Open interface. -func (e *IndexReaderExecutor) Open(ctx context.Context) error { - var err error - if e.corColInAccess { - e.ranges, err = rebuildIndexRanges(e.Ctx(), e.plans[0].(*plannercore.PhysicalIndexScan), e.idxCols, e.colLens) - if err != nil { - return err - } - } - - sc := e.Ctx().GetSessionVars().StmtCtx - var kvRanges []kv.KeyRange - if len(e.partitions) > 0 { - for _, p := range e.partitions { - partRange := e.ranges - if pRange, ok := e.partRangeMap[p.GetPhysicalID()]; ok { - partRange = pRange - } - kvRange, err := e.buildKeyRanges(sc, partRange, p.GetPhysicalID()) - if err != nil { - return err - } - kvRanges = append(kvRanges, kvRange...) - } - } else { - kvRanges, err = e.buildKeyRanges(sc, e.ranges, e.physicalTableID) - } - if err != nil { - return err - } - - return e.open(ctx, kvRanges) -} - -func (e *IndexReaderExecutor) buildKVReq(r []kv.KeyRange) (*kv.Request, error) { - var builder distsql.RequestBuilder - builder.SetKeyRanges(r). - SetDAGRequest(e.dagPB). - SetStartTS(e.startTS). - SetDesc(e.desc). - SetKeepOrder(e.keepOrder). - SetTxnScope(e.txnScope). - SetReadReplicaScope(e.readReplicaScope). - SetIsStaleness(e.isStaleness). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetMemTracker(e.memTracker). - SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &builder.Request, e.netDataSize)). - SetConnID(e.Ctx().GetSessionVars().ConnectionID) - kvReq, err := builder.Build() - return kvReq, err -} - -func (e *IndexReaderExecutor) open(ctx context.Context, kvRanges []kv.KeyRange) error { - var err error - if e.corColInFilter { - e.dagPB.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.plans) - if err != nil { - return err - } - } - - if e.index.Global { - idxScanExec := e.dagPB.Executors[0] - args := make([]expression.Expression, 0, len(e.partitionIDMap)) - column := &expression.Column{ - UniqueID: model.ExtraPidColID, - RetType: types.NewFieldType(mysql.TypeLonglong), - Index: len(idxScanExec.IdxScan.Columns) - 1, - OrigName: model.ExtraPartitionIdName.L, - } - args = append(args, column) - for pid := range e.partitionIDMap { - args = append(args, expression.NewInt64Const(pid)) - } - - inCondition, err := expression.NewFunction(e.Ctx(), ast.In, types.NewFieldType(mysql.TypeLonglong), args...) - if err != nil { - return err - } - pbConditions, err := expression.ExpressionsToPBList(e.Ctx().GetSessionVars().StmtCtx, []expression.Expression{inCondition}, e.Ctx().GetClient()) - if err != nil { - return err - } - if len(e.dagPB.Executors) > 1 && e.dagPB.Executors[1].Tp == tipb.ExecType_TypeSelection { - e.dagPB.Executors[1].Selection.Conditions = append(e.dagPB.Executors[1].Selection.Conditions, pbConditions...) - } else { - selExec := &tipb.Selection{ - Conditions: pbConditions, - } - executors := make([]*tipb.Executor, 0, len(e.dagPB.Executors)+1) - executors = append(executors, e.dagPB.Executors[0]) - executors = append(executors, &tipb.Executor{Tp: tipb.ExecType_TypeSelection, Selection: selExec}) - executors = append(executors, e.dagPB.Executors[1:]...) - e.dagPB.Executors = executors - } - } - - if e.RuntimeStats() != nil { - collExec := true - e.dagPB.CollectExecutionSummaries = &collExec - } - e.kvRanges = kvRanges - // Treat temporary table as dummy table, avoid sending distsql request to TiKV. - // In a test case IndexReaderExecutor is mocked and e.table is nil. - // Avoid sending distsql request to TIKV. - if e.dummy { - return nil - } - - if e.memTracker != nil { - e.memTracker.Reset() - } else { - e.memTracker = memory.NewTracker(e.ID(), -1) - } - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { - return bytes.Compare(i.StartKey, j.StartKey) - }) - // use sortedSelectResults only when byItems pushed down and partition numbers > 1 - if e.byItems == nil || len(e.partitions) <= 1 { - kvReq, err := e.buildKVReq(kvRanges) - if err != nil { - return err - } - e.result, err = e.SelectResult(ctx, e.Ctx(), kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) - if err != nil { - return err - } - } else { - kvReqs := make([]*kv.Request, 0, len(kvRanges)) - for _, kvRange := range kvRanges { - kvReq, err := e.buildKVReq([]kv.KeyRange{kvRange}) - if err != nil { - return err - } - kvReqs = append(kvReqs, kvReq) - } - var results []distsql.SelectResult - for _, kvReq := range kvReqs { - result, err := e.SelectResult(ctx, e.Ctx(), kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) - if err != nil { - return err - } - results = append(results, result) - } - e.result = distsql.NewSortedSelectResults(results, e.Schema(), e.byItems, e.memTracker) - } - return nil -} - -// IndexLookUpExecutor implements double read for index scan. -type IndexLookUpExecutor struct { - exec.BaseExecutor - - table table.Table - index *model.IndexInfo - ranges []*ranger.Range - dagPB *tipb.DAGRequest - startTS uint64 - // handleIdx is the index of handle, which is only used for case of keeping order. - handleIdx []int - handleCols []*expression.Column - primaryKeyIndex *model.IndexInfo - tableRequest *tipb.DAGRequest - // columns are only required by union scan. - columns []*model.ColumnInfo - *dataReaderBuilder - idxNetDataSize float64 - avgRowSize float64 - - // fields about accessing partition tables - partitionTableMode bool // if this executor is accessing a partition table - prunedPartitions []table.PhysicalTable // partition tables need to access - partitionIDMap map[int64]struct{} // partitionIDs that global index access - partitionRangeMap map[int64][]*ranger.Range - partitionKVRanges [][]kv.KeyRange // kvRanges of each prunedPartitions - - // All fields above are immutable. - - idxWorkerWg sync.WaitGroup - tblWorkerWg sync.WaitGroup - finished chan struct{} - - resultCh chan *lookupTableTask - resultCurr *lookupTableTask - - // memTracker is used to track the memory usage of this executor. - memTracker *memory.Tracker - - // checkIndexValue is used to check the consistency of the index data. - *checkIndexValue - - kvRanges []kv.KeyRange - workerStarted bool - - byItems []*plannerutil.ByItems - keepOrder bool - desc bool - - indexPaging bool - - corColInIdxSide bool - corColInTblSide bool - corColInAccess bool - idxPlans []plannercore.PhysicalPlan - tblPlans []plannercore.PhysicalPlan - idxCols []*expression.Column - colLens []int - // PushedLimit is used to skip the preceding and tailing handles when Limit is sunk into IndexLookUpReader. - PushedLimit *plannercore.PushedDownLimit - - stats *IndexLookUpRunTimeStats - - // cancelFunc is called when close the executor - cancelFunc context.CancelFunc - - // If dummy flag is set, this is not a real IndexLookUpReader, it just provides the KV ranges for UnionScan. - // Used by the temporary table, cached table. - dummy bool -} - -type getHandleType int8 - -const ( - getHandleFromIndex getHandleType = iota - getHandleFromTable -) - -// nolint:structcheck -type checkIndexValue struct { - idxColTps []*types.FieldType - idxTblCols []*table.Column -} - -// Table implements the dataSourceExecutor interface. -func (e *IndexLookUpExecutor) Table() table.Table { - return e.table -} - -func (e *IndexLookUpExecutor) setDummy() { - e.dummy = true -} - -// Open implements the Executor Open interface. -func (e *IndexLookUpExecutor) Open(ctx context.Context) error { - var err error - if e.corColInAccess { - e.ranges, err = rebuildIndexRanges(e.Ctx(), e.idxPlans[0].(*plannercore.PhysicalIndexScan), e.idxCols, e.colLens) - if err != nil { - return err - } - } - err = e.buildTableKeyRanges() - if err != nil { - return err - } - - // Treat temporary table as dummy table, avoid sending distsql request to TiKV. - if e.dummy { - return nil - } - - return e.open(ctx) -} - -func (e *IndexLookUpExecutor) buildTableKeyRanges() (err error) { - sc := e.Ctx().GetSessionVars().StmtCtx - if e.partitionTableMode { - e.partitionKVRanges = make([][]kv.KeyRange, 0, len(e.prunedPartitions)) - for _, p := range e.prunedPartitions { - // TODO: prune and adjust e.ranges for each partition again, since not all e.ranges are suitable for all e.prunedPartitions. - // For example, a table partitioned by range(a), and p0=(1, 10), p1=(11, 20), for the condition "(a>1 and a<10) or (a>11 and a<20)", - // the first range is only suitable to p0 and the second is to p1, but now we'll also build kvRange for range0+p1 and range1+p0. - physicalID := p.GetPhysicalID() - ranges := e.ranges - if e.partitionRangeMap != nil && e.partitionRangeMap[physicalID] != nil { - ranges = e.partitionRangeMap[physicalID] - } - var kvRange *kv.KeyRanges - if e.index.ID == -1 { - kvRange, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, ranges) - } else { - kvRange, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, ranges) - } - if err != nil { - return err - } - e.partitionKVRanges = append(e.partitionKVRanges, kvRange.FirstPartitionRange()) - } - } else { - physicalID := getPhysicalTableID(e.table) - var kvRanges *kv.KeyRanges - if e.index.ID == -1 { - kvRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, e.ranges) - } else { - kvRanges, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, e.ranges) - } - e.kvRanges = kvRanges.FirstPartitionRange() - } - return err -} - -func (e *IndexLookUpExecutor) open(_ context.Context) error { - // We have to initialize "memTracker" and other execution resources in here - // instead of in function "Open", because this "IndexLookUpExecutor" may be - // constructed by a "IndexLookUpJoin" and "Open" will not be called in that - // situation. - e.initRuntimeStats() - e.memTracker = memory.NewTracker(e.ID(), -1) - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - - e.finished = make(chan struct{}) - e.resultCh = make(chan *lookupTableTask, atomic.LoadInt32(&LookupTableTaskChannelSize)) - - var err error - if e.corColInIdxSide { - e.dagPB.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.idxPlans) - if err != nil { - return err - } - } - - if e.corColInTblSide { - e.tableRequest.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.tblPlans) - if err != nil { - return err - } - } - return nil -} - -func (e *IndexLookUpExecutor) startWorkers(ctx context.Context, initBatchSize int) error { - // indexWorker will write to workCh and tableWorker will read from workCh, - // so fetching index and getting table data can run concurrently. - ctx, cancel := context.WithCancel(ctx) - e.cancelFunc = cancel - workCh := make(chan *lookupTableTask, 1) - if err := e.startIndexWorker(ctx, workCh, initBatchSize); err != nil { - return err - } - e.startTableWorker(ctx, workCh) - e.workerStarted = true - return nil -} - -func (e *IndexLookUpExecutor) needPartitionHandle(tp getHandleType) (bool, error) { - var col *expression.Column - var needPartitionHandle, hasExtraCol bool - if tp == getHandleFromIndex { - cols := e.idxPlans[0].Schema().Columns - outputOffsets := e.dagPB.OutputOffsets - col = cols[outputOffsets[len(outputOffsets)-1]] - // For indexScan, need partitionHandle when global index or keepOrder with partitionTable - needPartitionHandle = e.index.Global || e.partitionTableMode && e.keepOrder - hasExtraCol = col.ID == model.ExtraPhysTblID || col.ID == model.ExtraPidColID - } else { - cols := e.tblPlans[0].Schema().Columns - outputOffsets := e.tableRequest.OutputOffsets - col = cols[outputOffsets[len(outputOffsets)-1]] - - // For TableScan, need partitionHandle in `indexOrder` when e.keepOrder == true - needPartitionHandle = (e.index.Global || e.partitionTableMode) && e.keepOrder - // no ExtraPidColID here, because TableScan shouldn't contain them. - hasExtraCol = col.ID == model.ExtraPhysTblID - } - - // TODO: fix global index related bugs later - // There will be two needPartitionHandle != hasExtraCol situations. - // Only `needPartitionHandle` == true and `hasExtraCol` == false are not allowed. - // `ExtraPhysTblID` will be used in `SelectLock` when `needPartitionHandle` == false and `hasExtraCol` == true. - if needPartitionHandle && !hasExtraCol && !e.index.Global { - return needPartitionHandle, errors.Errorf("Internal error, needPartitionHandle != ret, tp(%d)", tp) - } - return needPartitionHandle, nil -} - -func (e *IndexLookUpExecutor) isCommonHandle() bool { - return !(len(e.handleCols) == 1 && e.handleCols[0].ID == model.ExtraHandleID) && e.table.Meta() != nil && e.table.Meta().IsCommonHandle -} - -func (e *IndexLookUpExecutor) getRetTpsForIndexReader() []*types.FieldType { - if e.checkIndexValue != nil { - return e.idxColTps - } - var tps []*types.FieldType - if len(e.byItems) != 0 { - for _, item := range e.byItems { - tps = append(tps, item.Expr.GetType()) - } - } - if e.isCommonHandle() { - for _, handleCol := range e.handleCols { - tps = append(tps, handleCol.RetType) - } - } else { - tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) - } - if ok, _ := e.needPartitionHandle(getHandleFromIndex); ok { - tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) - } - return tps -} - -// startIndexWorker launch a background goroutine to fetch handles, send the results to workCh. -func (e *IndexLookUpExecutor) startIndexWorker(ctx context.Context, workCh chan<- *lookupTableTask, initBatchSize int) error { - if e.RuntimeStats() != nil { - collExec := true - e.dagPB.CollectExecutionSummaries = &collExec - } - tracker := memory.NewTracker(memory.LabelForIndexWorker, -1) - tracker.AttachTo(e.memTracker) - - kvRanges := [][]kv.KeyRange{e.kvRanges} - if e.partitionTableMode { - kvRanges = e.partitionKVRanges - } - // When len(kvrange) = 1, no sorting is required, - // so remove byItems and non-necessary output colums - if len(kvRanges) == 1 { - e.dagPB.OutputOffsets = e.dagPB.OutputOffsets[len(e.byItems):] - e.byItems = nil - } - tps := e.getRetTpsForIndexReader() - idxID := e.getIndexPlanRootID() - e.idxWorkerWg.Add(1) - go func() { - defer trace.StartRegion(ctx, "IndexLookUpIndexWorker").End() - worker := &indexWorker{ - idxLookup: e, - workCh: workCh, - finished: e.finished, - resultCh: e.resultCh, - keepOrder: e.keepOrder, - checkIndexValue: e.checkIndexValue, - maxBatchSize: e.Ctx().GetSessionVars().IndexLookupSize, - maxChunkSize: e.MaxChunkSize(), - PushedLimit: e.PushedLimit, - } - var builder distsql.RequestBuilder - builder.SetDAGRequest(e.dagPB). - SetStartTS(e.startTS). - SetDesc(e.desc). - SetKeepOrder(e.keepOrder). - SetPaging(e.indexPaging). - SetTxnScope(e.txnScope). - SetReadReplicaScope(e.readReplicaScope). - SetIsStaleness(e.isStaleness). - SetFromSessionVars(e.Ctx().GetSessionVars()). - SetFromInfoSchema(e.Ctx().GetInfoSchema()). - SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &builder.Request, e.idxNetDataSize/float64(len(kvRanges)))). - SetMemTracker(tracker). - SetConnID(e.Ctx().GetSessionVars().ConnectionID) - - results := make([]distsql.SelectResult, 0, len(kvRanges)) - for _, kvRange := range kvRanges { - // check if executor is closed - finished := false - select { - case <-e.finished: - finished = true - default: - } - if finished { - break - } - - // init kvReq, result and worker for this partition - // The key ranges should be ordered. - slices.SortFunc(kvRange, func(i, j kv.KeyRange) int { - return bytes.Compare(i.StartKey, j.StartKey) - }) - kvReq, err := builder.SetKeyRanges(kvRange).Build() - if err != nil { - worker.syncErr(err) - break - } - result, err := distsql.SelectWithRuntimeStats(ctx, e.Ctx(), kvReq, tps, getPhysicalPlanIDs(e.idxPlans), idxID) - if err != nil { - worker.syncErr(err) - break - } - results = append(results, result) - } - worker.batchSize = mathutil.Min(initBatchSize, worker.maxBatchSize) - if len(results) > 1 && len(e.byItems) != 0 { - // e.Schema() not the output schema for indexReader, and we put byItems related column at first in `buildIndexReq`, so use nil here. - ssr := distsql.NewSortedSelectResults(results, nil, e.byItems, e.memTracker) - results = []distsql.SelectResult{ssr} - } - ctx1, cancel := context.WithCancel(ctx) - // this error is synced in fetchHandles(), don't sync it again - _ = worker.fetchHandles(ctx1, results) - cancel() - for _, result := range results { - if err := result.Close(); err != nil { - logutil.Logger(ctx).Error("close Select result failed", zap.Error(err)) - } - } - close(workCh) - close(e.resultCh) - e.idxWorkerWg.Done() - }() - return nil -} - -// startTableWorker launchs some background goroutines which pick tasks from workCh and execute the task. -func (e *IndexLookUpExecutor) startTableWorker(ctx context.Context, workCh <-chan *lookupTableTask) { - lookupConcurrencyLimit := e.Ctx().GetSessionVars().IndexLookupConcurrency() - e.tblWorkerWg.Add(lookupConcurrencyLimit) - for i := 0; i < lookupConcurrencyLimit; i++ { - workerID := i - worker := &tableWorker{ - idxLookup: e, - workCh: workCh, - finished: e.finished, - keepOrder: e.keepOrder, - handleIdx: e.handleIdx, - checkIndexValue: e.checkIndexValue, - memTracker: memory.NewTracker(workerID, -1), - } - worker.memTracker.AttachTo(e.memTracker) - ctx1, cancel := context.WithCancel(ctx) - go func() { - defer trace.StartRegion(ctx1, "IndexLookUpTableWorker").End() - worker.pickAndExecTask(ctx1) - cancel() - e.tblWorkerWg.Done() - }() - } -} - -func (e *IndexLookUpExecutor) buildTableReader(ctx context.Context, task *lookupTableTask) (exec.Executor, error) { - table := e.table - if e.partitionTableMode && task.partitionTable != nil { - table = task.partitionTable - } - tableReaderExec := &TableReaderExecutor{ - BaseExecutor: exec.NewBaseExecutor(e.Ctx(), e.Schema(), e.getTableRootPlanID()), - table: table, - dagPB: e.tableRequest, - startTS: e.startTS, - txnScope: e.txnScope, - readReplicaScope: e.readReplicaScope, - isStaleness: e.isStaleness, - columns: e.columns, - corColInFilter: e.corColInTblSide, - plans: e.tblPlans, - netDataSize: e.avgRowSize * float64(len(task.handles)), - byItems: e.byItems, - } - tableReaderExec.buildVirtualColumnInfo() - tableReader, err := e.dataReaderBuilder.buildTableReaderFromHandles(ctx, tableReaderExec, task.handles, true) - if err != nil { - logutil.Logger(ctx).Error("build table reader from handles failed", zap.Error(err)) - return nil, err - } - return tableReader, nil -} - -// Close implements Exec Close interface. -func (e *IndexLookUpExecutor) Close() error { - if e.stats != nil { - defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) - } - e.kvRanges = e.kvRanges[:0] - if e.dummy { - return nil - } - - if !e.workerStarted || e.finished == nil { - return nil - } - - if e.cancelFunc != nil { - e.cancelFunc() - e.cancelFunc = nil - } - close(e.finished) - // Drain the resultCh and discard the result, in case that Next() doesn't fully - // consume the data, background worker still writing to resultCh and block forever. - channel.Clear(e.resultCh) - e.idxWorkerWg.Wait() - e.tblWorkerWg.Wait() - e.finished = nil - e.workerStarted = false - e.memTracker = nil - e.resultCurr = nil - return nil -} - -// Next implements Exec Next interface. -func (e *IndexLookUpExecutor) Next(ctx context.Context, req *chunk.Chunk) error { - if e.dummy { - req.Reset() - return nil - } - - if !e.workerStarted { - if err := e.startWorkers(ctx, req.RequiredRows()); err != nil { - return err - } - } - req.Reset() - for { - resultTask, err := e.getResultTask() - if err != nil { - return err - } - if resultTask == nil { - return nil - } - if resultTask.cursor < len(resultTask.rows) { - numToAppend := mathutil.Min(len(resultTask.rows)-resultTask.cursor, req.RequiredRows()-req.NumRows()) - req.AppendRows(resultTask.rows[resultTask.cursor : resultTask.cursor+numToAppend]) - resultTask.cursor += numToAppend - if req.IsFull() { - return nil - } - } - } -} - -func (e *IndexLookUpExecutor) getResultTask() (*lookupTableTask, error) { - if e.resultCurr != nil && e.resultCurr.cursor < len(e.resultCurr.rows) { - return e.resultCurr, nil - } - var ( - enableStats = e.stats != nil - start time.Time - indexFetchedInstant time.Time - ) - if enableStats { - start = time.Now() - } - task, ok := <-e.resultCh - if !ok { - return nil, nil - } - if enableStats { - indexFetchedInstant = time.Now() - } - if err := <-task.doneCh; err != nil { - return nil, err - } - if enableStats { - e.stats.NextWaitIndexScan += indexFetchedInstant.Sub(start) - if task.buildDoneTime.After(indexFetchedInstant) { - e.stats.NextWaitTableLookUpBuild += task.buildDoneTime.Sub(indexFetchedInstant) - indexFetchedInstant = task.buildDoneTime - } - e.stats.NextWaitTableLookUpResp += time.Since(indexFetchedInstant) - } - - // Release the memory usage of last task before we handle a new task. - if e.resultCurr != nil { - e.resultCurr.memTracker.Consume(-e.resultCurr.memUsage) - } - e.resultCurr = task - return e.resultCurr, nil -} - -func (e *IndexLookUpExecutor) initRuntimeStats() { - if e.RuntimeStats() != nil { - e.stats = &IndexLookUpRunTimeStats{ - indexScanBasicStats: &execdetails.BasicRuntimeStats{}, - Concurrency: e.Ctx().GetSessionVars().IndexLookupConcurrency(), - } - } -} - -func (e *IndexLookUpExecutor) getIndexPlanRootID() int { - if len(e.idxPlans) > 0 { - return e.idxPlans[len(e.idxPlans)-1].ID() - } - return e.ID() -} - -func (e *IndexLookUpExecutor) getTableRootPlanID() int { - if len(e.tblPlans) > 0 { - return e.tblPlans[len(e.tblPlans)-1].ID() - } - return e.ID() -} - -// indexWorker is used by IndexLookUpExecutor to maintain index lookup background goroutines. -type indexWorker struct { - idxLookup *IndexLookUpExecutor - workCh chan<- *lookupTableTask - finished <-chan struct{} - resultCh chan<- *lookupTableTask - keepOrder bool - - // batchSize is for lightweight startup. It will be increased exponentially until reaches the max batch size value. - batchSize int - maxBatchSize int - maxChunkSize int - - // checkIndexValue is used to check the consistency of the index data. - *checkIndexValue - // PushedLimit is used to skip the preceding and tailing handles when Limit is sunk into IndexLookUpReader. - PushedLimit *plannercore.PushedDownLimit - // scannedKeys indicates how many keys be scanned - scannedKeys uint64 -} - -func (w *indexWorker) syncErr(err error) { - doneCh := make(chan error, 1) - doneCh <- err - w.resultCh <- &lookupTableTask{ - doneCh: doneCh, - } -} - -// fetchHandles fetches a batch of handles from index data and builds the index lookup tasks. -// The tasks are sent to workCh to be further processed by tableWorker, and sent to e.resultCh -// at the same time to keep data ordered. -func (w *indexWorker) fetchHandles(ctx context.Context, results []distsql.SelectResult) (err error) { - defer func() { - if r := recover(); r != nil { - logutil.Logger(ctx).Error("indexWorker in IndexLookupExecutor panicked", zap.Any("recover", r), zap.Stack("stack")) - err4Panic := errors.Errorf("%v", r) - w.syncErr(err4Panic) - if err != nil { - err = errors.Trace(err4Panic) - } - } - }() - chk := w.idxLookup.Ctx().GetSessionVars().GetNewChunkWithCapacity(w.idxLookup.getRetTpsForIndexReader(), w.idxLookup.MaxChunkSize(), w.idxLookup.MaxChunkSize(), w.idxLookup.AllocPool) - idxID := w.idxLookup.getIndexPlanRootID() - if w.idxLookup.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl != nil { - if idxID != w.idxLookup.ID() && w.idxLookup.stats != nil { - w.idxLookup.stats.indexScanBasicStats = w.idxLookup.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.GetBasicRuntimeStats(idxID) - } - } - for i := 0; i < len(results); { - result := results[i] - if w.PushedLimit != nil && w.scannedKeys >= w.PushedLimit.Count+w.PushedLimit.Offset { - break - } - startTime := time.Now() - handles, retChunk, err := w.extractTaskHandles(ctx, chk, result) - finishFetch := time.Now() - if err != nil { - w.syncErr(err) - return err - } - if len(handles) == 0 { - i++ - continue - } - task := w.buildTableTask(handles, retChunk) - finishBuild := time.Now() - if w.idxLookup.partitionTableMode { - task.partitionTable = w.idxLookup.prunedPartitions[i] - } - select { - case <-ctx.Done(): - return nil - case <-w.finished: - return nil - case w.workCh <- task: - w.resultCh <- task - } - if w.idxLookup.stats != nil { - atomic.AddInt64(&w.idxLookup.stats.FetchHandle, int64(finishFetch.Sub(startTime))) - atomic.AddInt64(&w.idxLookup.stats.TaskWait, int64(time.Since(finishBuild))) - atomic.AddInt64(&w.idxLookup.stats.FetchHandleTotal, int64(time.Since(startTime))) - } - } - return nil -} - -func (w *indexWorker) extractTaskHandles(ctx context.Context, chk *chunk.Chunk, idxResult distsql.SelectResult) ( - handles []kv.Handle, retChk *chunk.Chunk, err error) { - numColsWithoutPid := chk.NumCols() - ok, err := w.idxLookup.needPartitionHandle(getHandleFromIndex) - if err != nil { - return nil, nil, err - } - if ok { - numColsWithoutPid = numColsWithoutPid - 1 - } - handleOffset := make([]int, 0, len(w.idxLookup.handleCols)) - for i := range w.idxLookup.handleCols { - handleOffset = append(handleOffset, numColsWithoutPid-len(w.idxLookup.handleCols)+i) - } - if len(handleOffset) == 0 { - handleOffset = []int{numColsWithoutPid - 1} - } - handles = make([]kv.Handle, 0, w.batchSize) - // PushedLimit would always be nil for CheckIndex or CheckTable, we add this check just for insurance. - checkLimit := (w.PushedLimit != nil) && (w.checkIndexValue == nil) - for len(handles) < w.batchSize { - requiredRows := w.batchSize - len(handles) - if checkLimit { - if w.PushedLimit.Offset+w.PushedLimit.Count <= w.scannedKeys { - return handles, nil, nil - } - leftCnt := w.PushedLimit.Offset + w.PushedLimit.Count - w.scannedKeys - if uint64(requiredRows) > leftCnt { - requiredRows = int(leftCnt) - } - } - chk.SetRequiredRows(requiredRows, w.maxChunkSize) - startTime := time.Now() - err = errors.Trace(idxResult.Next(ctx, chk)) - if err != nil { - return handles, nil, err - } - if w.idxLookup.stats != nil { - w.idxLookup.stats.indexScanBasicStats.Record(time.Since(startTime), chk.NumRows()) - } - if chk.NumRows() == 0 { - return handles, retChk, nil - } - for i := 0; i < chk.NumRows(); i++ { - w.scannedKeys++ - if checkLimit { - if w.scannedKeys <= w.PushedLimit.Offset { - continue - } - if w.scannedKeys > (w.PushedLimit.Offset + w.PushedLimit.Count) { - // Skip the handles after Offset+Count. - return handles, nil, nil - } - } - h, err := w.idxLookup.getHandle(chk.GetRow(i), handleOffset, w.idxLookup.isCommonHandle(), getHandleFromIndex) - if err != nil { - return handles, retChk, err - } - if ph, ok := h.(kv.PartitionHandle); ok { - if _, exist := w.idxLookup.partitionIDMap[ph.PartitionID]; !exist { - continue - } - } - handles = append(handles, h) - } - if w.checkIndexValue != nil { - if retChk == nil { - retChk = chunk.NewChunkWithCapacity(w.idxColTps, w.batchSize) - } - retChk.Append(chk, 0, chk.NumRows()) - } - } - w.batchSize *= 2 - if w.batchSize > w.maxBatchSize { - w.batchSize = w.maxBatchSize - } - return handles, retChk, nil -} - -func (w *indexWorker) buildTableTask(handles []kv.Handle, retChk *chunk.Chunk) *lookupTableTask { - var indexOrder *kv.HandleMap - var duplicatedIndexOrder *kv.HandleMap - if w.keepOrder { - // Save the index order. - indexOrder = kv.NewHandleMap() - for i, h := range handles { - indexOrder.Set(h, i) - } - } - - if w.checkIndexValue != nil { - // Save the index order. - indexOrder = kv.NewHandleMap() - duplicatedIndexOrder = kv.NewHandleMap() - for i, h := range handles { - if _, ok := indexOrder.Get(h); ok { - duplicatedIndexOrder.Set(h, i) - } else { - indexOrder.Set(h, i) - } - } - } - - task := &lookupTableTask{ - handles: handles, - indexOrder: indexOrder, - duplicatedIndexOrder: duplicatedIndexOrder, - idxRows: retChk, - } - - task.doneCh = make(chan error, 1) - return task -} - -// tableWorker is used by IndexLookUpExecutor to maintain table lookup background goroutines. -type tableWorker struct { - idxLookup *IndexLookUpExecutor - workCh <-chan *lookupTableTask - finished <-chan struct{} - keepOrder bool - handleIdx []int - - // memTracker is used to track the memory usage of this executor. - memTracker *memory.Tracker - - // checkIndexValue is used to check the consistency of the index data. - *checkIndexValue -} - -// pickAndExecTask picks tasks from workCh, and execute them. -func (w *tableWorker) pickAndExecTask(ctx context.Context) { - var task *lookupTableTask - var ok bool - defer func() { - if r := recover(); r != nil { - logutil.Logger(ctx).Error("tableWorker in IndexLookUpExecutor panicked", zap.Any("recover", r), zap.Stack("stack")) - task.doneCh <- errors.Errorf("%v", r) - } - }() - for { - // Don't check ctx.Done() on purpose. If background worker get the signal and all - // exit immediately, session's goroutine doesn't know this and still calling Next(), - // it may block reading task.doneCh forever. - select { - case task, ok = <-w.workCh: - if !ok { - return - } - case <-w.finished: - return - } - startTime := time.Now() - err := w.executeTask(ctx, task) - if w.idxLookup.stats != nil { - atomic.AddInt64(&w.idxLookup.stats.TableRowScan, int64(time.Since(startTime))) - atomic.AddInt64(&w.idxLookup.stats.TableTaskNum, 1) - } - task.doneCh <- err - } -} - -func (e *IndexLookUpExecutor) getHandle(row chunk.Row, handleIdx []int, - isCommonHandle bool, tp getHandleType) (handle kv.Handle, err error) { - if isCommonHandle { - var handleEncoded []byte - var datums []types.Datum - for i, idx := range handleIdx { - // If the new collation is enabled and the handle contains non-binary string, - // the handle in the index is encoded as "sortKey". So we cannot restore its - // original value(the primary key) here. - // We use a trick to avoid encoding the "sortKey" again by changing the charset - // collation to `binary`. - rtp := e.handleCols[i].RetType - if collate.NewCollationEnabled() && e.table.Meta().CommonHandleVersion == 0 && rtp.EvalType() == types.ETString && - !mysql.HasBinaryFlag(rtp.GetFlag()) && tp == getHandleFromIndex { - rtp = rtp.Clone() - rtp.SetCollate(charset.CollationBin) - datums = append(datums, row.GetDatum(idx, rtp)) - continue - } - datums = append(datums, row.GetDatum(idx, e.handleCols[i].RetType)) - } - tablecodec.TruncateIndexValues(e.table.Meta(), e.primaryKeyIndex, datums) - handleEncoded, err = codec.EncodeKey(e.Ctx().GetSessionVars().StmtCtx, nil, datums...) - if err != nil { - return nil, err - } - handle, err = kv.NewCommonHandle(handleEncoded) - if err != nil { - return nil, err - } - } else { - if len(handleIdx) == 0 { - handle = kv.IntHandle(row.GetInt64(0)) - } else { - handle = kv.IntHandle(row.GetInt64(handleIdx[0])) - } - } - ok, err := e.needPartitionHandle(tp) - if err != nil { - return nil, err - } - if ok { - pid := row.GetInt64(row.Len() - 1) - handle = kv.NewPartitionHandle(pid, handle) - } - return -} - -// IndexLookUpRunTimeStats record the indexlookup runtime stat -type IndexLookUpRunTimeStats struct { - // indexScanBasicStats uses to record basic runtime stats for index scan. - indexScanBasicStats *execdetails.BasicRuntimeStats - FetchHandleTotal int64 - FetchHandle int64 - TaskWait int64 - TableRowScan int64 - TableTaskNum int64 - Concurrency int - // Record the `Next` call affected wait duration details. - NextWaitIndexScan time.Duration - NextWaitTableLookUpBuild time.Duration - NextWaitTableLookUpResp time.Duration -} - -func (e *IndexLookUpRunTimeStats) String() string { - var buf bytes.Buffer - fetchHandle := atomic.LoadInt64(&e.FetchHandleTotal) - indexScan := atomic.LoadInt64(&e.FetchHandle) - taskWait := atomic.LoadInt64(&e.TaskWait) - tableScan := atomic.LoadInt64(&e.TableRowScan) - tableTaskNum := atomic.LoadInt64(&e.TableTaskNum) - concurrency := e.Concurrency - if indexScan != 0 { - buf.WriteString(fmt.Sprintf("index_task: {total_time: %s, fetch_handle: %s, build: %s, wait: %s}", - execdetails.FormatDuration(time.Duration(fetchHandle)), - execdetails.FormatDuration(time.Duration(indexScan)), - execdetails.FormatDuration(time.Duration(fetchHandle-indexScan-taskWait)), - execdetails.FormatDuration(time.Duration(taskWait)))) - } - if tableScan != 0 { - if buf.Len() > 0 { - buf.WriteByte(',') - } - buf.WriteString(fmt.Sprintf(" table_task: {total_time: %v, num: %d, concurrency: %d}", execdetails.FormatDuration(time.Duration(tableScan)), tableTaskNum, concurrency)) - } - if e.NextWaitIndexScan > 0 || e.NextWaitTableLookUpBuild > 0 || e.NextWaitTableLookUpResp > 0 { - if buf.Len() > 0 { - buf.WriteByte(',') - fmt.Fprintf(&buf, " next: {wait_index: %s, wait_table_lookup_build: %s, wait_table_lookup_resp: %s}", - execdetails.FormatDuration(e.NextWaitIndexScan), - execdetails.FormatDuration(e.NextWaitTableLookUpBuild), - execdetails.FormatDuration(e.NextWaitTableLookUpResp)) - } - } - return buf.String() -} - -// Clone implements the RuntimeStats interface. -func (e *IndexLookUpRunTimeStats) Clone() execdetails.RuntimeStats { - newRs := *e - return &newRs -} - -// Merge implements the RuntimeStats interface. -func (e *IndexLookUpRunTimeStats) Merge(other execdetails.RuntimeStats) { - tmp, ok := other.(*IndexLookUpRunTimeStats) - if !ok { - return - } - e.FetchHandleTotal += tmp.FetchHandleTotal - e.FetchHandle += tmp.FetchHandle - e.TaskWait += tmp.TaskWait - e.TableRowScan += tmp.TableRowScan - e.TableTaskNum += tmp.TableTaskNum - e.NextWaitIndexScan += tmp.NextWaitIndexScan - e.NextWaitTableLookUpBuild += tmp.NextWaitTableLookUpBuild - e.NextWaitTableLookUpResp += tmp.NextWaitTableLookUpResp -} - -// Tp implements the RuntimeStats interface. -func (*IndexLookUpRunTimeStats) Tp() int { - return execdetails.TpIndexLookUpRunTimeStats -} - -func (w *tableWorker) compareData(ctx context.Context, task *lookupTableTask, tableReader exec.Executor) error { - chk := exec.TryNewCacheChunk(tableReader) - tblInfo := w.idxLookup.table.Meta() - vals := make([]types.Datum, 0, len(w.idxTblCols)) - - // Prepare collator for compare. - collators := make([]collate.Collator, 0, len(w.idxColTps)) - for _, tp := range w.idxColTps { - collators = append(collators, collate.GetCollator(tp.GetCollate())) - } - - ir := func() *consistency.Reporter { - return &consistency.Reporter{ - HandleEncode: func(handle kv.Handle) kv.Key { - return tablecodec.EncodeRecordKey(w.idxLookup.table.RecordPrefix(), handle) - }, - IndexEncode: func(idxRow *consistency.RecordData) kv.Key { - var idx table.Index - for _, v := range w.idxLookup.table.Indices() { - if strings.EqualFold(v.Meta().Name.String(), w.idxLookup.index.Name.O) { - idx = v - break - } - } - if idx == nil { - return nil - } - k, _, err := idx.GenIndexKey(w.idxLookup.Ctx().GetSessionVars().StmtCtx, idxRow.Values[:len(idx.Meta().Columns)], idxRow.Handle, nil) - if err != nil { - return nil - } - return k - }, - Tbl: tblInfo, - Idx: w.idxLookup.index, - Sctx: w.idxLookup.Ctx(), - } - } - - for { - err := exec.Next(ctx, tableReader, chk) - if err != nil { - return errors.Trace(err) - } - - // If ctx is cancelled, `Next` may return empty result when the actual data is not empty. To avoid producing - // false-positive error logs that cause confusion, exit in this case. - select { - case <-ctx.Done(): - return nil - default: - } - - if chk.NumRows() == 0 { - task.indexOrder.Range(func(h kv.Handle, val interface{}) bool { - idxRow := task.idxRows.GetRow(val.(int)) - err = ir().ReportAdminCheckInconsistent(ctx, h, &consistency.RecordData{Handle: h, Values: getDatumRow(&idxRow, w.idxColTps)}, nil) - return false - }) - if err != nil { - return err - } - break - } - - iter := chunk.NewIterator4Chunk(chk) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - handle, err := w.idxLookup.getHandle(row, w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) - if err != nil { - return err - } - v, ok := task.indexOrder.Get(handle) - if !ok { - v, _ = task.duplicatedIndexOrder.Get(handle) - } - offset, _ := v.(int) - task.indexOrder.Delete(handle) - idxRow := task.idxRows.GetRow(offset) - vals = vals[:0] - for i, col := range w.idxTblCols { - vals = append(vals, row.GetDatum(i, &col.FieldType)) - } - tablecodec.TruncateIndexValues(tblInfo, w.idxLookup.index, vals) - sctx := w.idxLookup.Ctx().GetSessionVars().StmtCtx - for i := range vals { - col := w.idxTblCols[i] - idxVal := idxRow.GetDatum(i, w.idxColTps[i]) - tablecodec.TruncateIndexValue(&idxVal, w.idxLookup.index.Columns[i], col.ColumnInfo) - cmpRes, err := tables.CompareIndexAndVal(sctx, vals[i], idxVal, collators[i], col.FieldType.IsArray() && vals[i].Kind() == types.KindMysqlJSON) - if err != nil { - return ir().ReportAdminCheckInconsistentWithColInfo(ctx, - handle, - col.Name.O, - idxVal, - vals[i], - err, - &consistency.RecordData{Handle: handle, Values: getDatumRow(&idxRow, w.idxColTps)}, - ) - } - if cmpRes != 0 { - return ir().ReportAdminCheckInconsistentWithColInfo(ctx, - handle, - col.Name.O, - idxRow.GetDatum(i, w.idxColTps[i]), - vals[i], - err, - &consistency.RecordData{Handle: handle, Values: getDatumRow(&idxRow, w.idxColTps)}, - ) - } - } - } - } - return nil -} - -func getDatumRow(r *chunk.Row, fields []*types.FieldType) []types.Datum { - datumRow := make([]types.Datum, 0, r.Chunk().NumCols()) - for colIdx := 0; colIdx < r.Chunk().NumCols(); colIdx++ { - if colIdx >= len(fields) { - break - } - datum := r.GetDatum(colIdx, fields[colIdx]) - datumRow = append(datumRow, datum) - } - return datumRow -} - -// executeTask executes the table look up tasks. We will construct a table reader and send request by handles. -// Then we hold the returning rows and finish this task. -func (w *tableWorker) executeTask(ctx context.Context, task *lookupTableTask) error { - tableReader, err := w.idxLookup.buildTableReader(ctx, task) - task.buildDoneTime = time.Now() - if err != nil { - logutil.Logger(ctx).Error("build table reader failed", zap.Error(err)) - return err - } - defer terror.Call(tableReader.Close) - - if w.checkIndexValue != nil { - return w.compareData(ctx, task, tableReader) - } - - task.memTracker = w.memTracker - memUsage := int64(cap(task.handles) * 8) - task.memUsage = memUsage - task.memTracker.Consume(memUsage) - handleCnt := len(task.handles) - task.rows = make([]chunk.Row, 0, handleCnt) - for { - chk := exec.TryNewCacheChunk(tableReader) - err = exec.Next(ctx, tableReader, chk) - if err != nil { - logutil.Logger(ctx).Error("table reader fetch next chunk failed", zap.Error(err)) - return err - } - if chk.NumRows() == 0 { - break - } - memUsage = chk.MemoryUsage() - task.memUsage += memUsage - task.memTracker.Consume(memUsage) - iter := chunk.NewIterator4Chunk(chk) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - task.rows = append(task.rows, row) - } - } - - defer trace.StartRegion(ctx, "IndexLookUpTableCompute").End() - memUsage = int64(cap(task.rows)) * int64(unsafe.Sizeof(chunk.Row{})) - task.memUsage += memUsage - task.memTracker.Consume(memUsage) - if w.keepOrder { - task.rowIdx = make([]int, 0, len(task.rows)) - for i := range task.rows { - handle, err := w.idxLookup.getHandle(task.rows[i], w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) - if err != nil { - return err - } - rowIdx, _ := task.indexOrder.Get(handle) - task.rowIdx = append(task.rowIdx, rowIdx.(int)) - } - memUsage = int64(cap(task.rowIdx) * 4) - task.memUsage += memUsage - task.memTracker.Consume(memUsage) - sort.Sort(task) - } - - if handleCnt != len(task.rows) && !util.HasCancelled(ctx) && - !w.idxLookup.Ctx().GetSessionVars().StmtCtx.WeakConsistency { - if len(w.idxLookup.tblPlans) == 1 { - obtainedHandlesMap := kv.NewHandleMap() - for _, row := range task.rows { - handle, err := w.idxLookup.getHandle(row, w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) - if err != nil { - return err - } - obtainedHandlesMap.Set(handle, true) - } - missHds := GetLackHandles(task.handles, obtainedHandlesMap) - return (&consistency.Reporter{ - HandleEncode: func(hd kv.Handle) kv.Key { - return tablecodec.EncodeRecordKey(w.idxLookup.table.RecordPrefix(), hd) - }, - Tbl: w.idxLookup.table.Meta(), - Idx: w.idxLookup.index, - Sctx: w.idxLookup.Ctx(), - }).ReportLookupInconsistent(ctx, - handleCnt, - len(task.rows), - missHds, - task.handles, - nil, - //missRecords, - ) - } - } - - return nil -} - -// GetLackHandles gets the handles in expectedHandles but not in obtainedHandlesMap. -func GetLackHandles(expectedHandles []kv.Handle, obtainedHandlesMap *kv.HandleMap) []kv.Handle { - diffCnt := len(expectedHandles) - obtainedHandlesMap.Len() - diffHandles := make([]kv.Handle, 0, diffCnt) - var cnt int - for _, handle := range expectedHandles { - isExist := false - if _, ok := obtainedHandlesMap.Get(handle); ok { - obtainedHandlesMap.Delete(handle) - isExist = true - } - if !isExist { - diffHandles = append(diffHandles, handle) - cnt++ - if cnt == diffCnt { - break - } - } - } - - return diffHandles -} - -func getPhysicalPlanIDs(plans []plannercore.PhysicalPlan) []int { - planIDs := make([]int, 0, len(plans)) - for _, p := range plans { - planIDs = append(planIDs, p.ID()) - } - return planIDs -} diff --git a/executor/distsql_test.go b/executor/distsql_test.go deleted file mode 100644 index ce02b2c1b3369..0000000000000 --- a/executor/distsql_test.go +++ /dev/null @@ -1,700 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "bytes" - "context" - "fmt" - "math/rand" - "regexp" - "runtime/pprof" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/paging" - "github.com/prometheus/client_golang/prometheus" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" -) - -// checkGoroutineExists -// nolint:unused -func checkGoroutineExists(keyword string) bool { - buf := new(bytes.Buffer) - profile := pprof.Lookup("goroutine") - err := profile.WriteTo(buf, 1) - if err != nil { - panic(err) - } - str := buf.String() - return strings.Contains(str, keyword) -} - -func TestCopClientSend(t *testing.T) { - t.Skip("not stable") - var cluster testutils.Cluster - store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithSingleStore(c) - cluster = c - })) - if _, ok := store.GetClient().(*copr.CopClient); !ok { - // Make sure the store is tikv store. - return - } - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table copclient (id int primary key)") - - // Insert 1000 rows. - var values []string - for i := 0; i < 1000; i++ { - values = append(values, fmt.Sprintf("(%d)", i)) - } - tk.MustExec("insert copclient values " + strings.Join(values, ",")) - - // Get table ID for split. - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("copclient")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - // Split the table. - tableStart := tablecodec.GenTableRecordPrefix(tblID) - cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 100) - - ctx := context.Background() - // Send coprocessor request when the table split. - rs, err := tk.Exec("select sum(id) from copclient") - require.NoError(t, err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, "499500", req.GetRow(0).GetMyDecimal(0).String()) - require.NoError(t, rs.Close()) - - // Split one region. - key := tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(500)) - region, _, _ := cluster.GetRegionByKey(key) - peerID := cluster.AllocID() - cluster.Split(region.GetId(), cluster.AllocID(), key, []uint64{peerID}, peerID) - - // Check again. - rs, err = tk.Exec("select sum(id) from copclient") - require.NoError(t, err) - req = rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, "499500", req.GetRow(0).GetMyDecimal(0).String()) - require.NoError(t, rs.Close()) - - // Check there is no goroutine leak. - rs, err = tk.Exec("select * from copclient order by id") - require.NoError(t, err) - req = rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - require.NoError(t, rs.Close()) - keyword := "(*copIterator).work" - require.False(t, checkGoroutineExists(keyword)) -} - -func TestGetLackHandles(t *testing.T) { - expectedHandles := []kv.Handle{kv.IntHandle(1), kv.IntHandle(2), kv.IntHandle(3), kv.IntHandle(4), - kv.IntHandle(5), kv.IntHandle(6), kv.IntHandle(7), kv.IntHandle(8), kv.IntHandle(9), kv.IntHandle(10)} - handlesMap := kv.NewHandleMap() - for _, h := range expectedHandles { - handlesMap.Set(h, true) - } - - // expected handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - // obtained handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - diffHandles := executor.GetLackHandles(expectedHandles, handlesMap) - require.Len(t, diffHandles, 0) - require.Equal(t, 0, handlesMap.Len()) - - // expected handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - // obtained handles 2, 3, 4, 6, 7, 8, 9 - retHandles := []kv.Handle{kv.IntHandle(2), kv.IntHandle(3), kv.IntHandle(4), kv.IntHandle(6), - kv.IntHandle(7), kv.IntHandle(8), kv.IntHandle(9)} - handlesMap = kv.NewHandleMap() - handlesMap.Set(kv.IntHandle(1), true) - handlesMap.Set(kv.IntHandle(5), true) - handlesMap.Set(kv.IntHandle(10), true) - diffHandles = executor.GetLackHandles(expectedHandles, handlesMap) - require.Equal(t, diffHandles, retHandles) // deep equal -} - -func TestBigIntPK(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(a bigint unsigned primary key, b int, c int, index idx(a, b))") - tk.MustExec("insert into t values(1, 1, 1), (9223372036854775807, 2, 2)") - tk.MustQuery("select * from t use index(idx) order by a").Check(testkit.Rows("1 1 1", "9223372036854775807 2 2")) -} - -func TestCorColToRanges(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only-full-group-by - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b int, c int, index idx(b))") - tk.MustExec("insert into t values(1, 1, 1), (2, 2 ,2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9)") - tk.MustExec("analyze table t") - // Test single read on table. - tk.MustQuery("select t.c in (select count(*) from t s ignore index(idx), t t1 where s.a = t.a and s.a = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) - // Test single read on index. - tk.MustQuery("select t.c in (select count(*) from t s use index(idx), t t1 where s.b = t.a and s.a = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) - // Test IndexLookUpReader. - tk.MustQuery("select t.c in (select count(*) from t s use index(idx), t t1 where s.b = t.a and s.c = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) -} - -func TestUniqueKeyNullValueSelect(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - // test null in unique-key - tk.MustExec("create table t (id int default null, c varchar(20), unique id (id));") - tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") - res := tk.MustQuery("select * from t where id is null;") - res.Check(testkit.Rows(" a", " b", " c")) - - // test null in mul unique-key - tk.MustExec("drop table t") - tk.MustExec("create table t (id int default null, b int default 1, c varchar(20), unique id_c(id, b));") - tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") - res = tk.MustQuery("select * from t where id is null and b = 1;") - res.Check(testkit.Rows(" 1 a", " 1 b", " 1 c")) - - tk.MustExec("drop table t") - // test null in non-unique-key - tk.MustExec("create table t (id int default null, c varchar(20), key id (id));") - tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") - res = tk.MustQuery("select * from t where id is null;") - res.Check(testkit.Rows(" a", " b", " c")) -} - -// TestIssue10178 contains tests for https://github.com/pingcap/tidb/issues/10178 . -func TestIssue10178(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a bigint unsigned primary key)") - tk.MustExec("insert into t values(9223372036854775807), (18446744073709551615)") - tk.MustQuery("select max(a) from t").Check(testkit.Rows("18446744073709551615")) - tk.MustQuery("select * from t where a > 9223372036854775807").Check(testkit.Rows("18446744073709551615")) - tk.MustQuery("select * from t where a < 9223372036854775808").Check(testkit.Rows("9223372036854775807")) -} - -func TestInconsistentIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx_a(a))") - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - idx := tbl.Meta().FindIndexByName("idx_a") - idxOp := tables.NewIndex(tbl.Meta().ID, tbl.Meta(), idx) - ctx := mock.NewContext() - ctx.Store = store - - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", i+10, i)) - require.NoError(t, tk.QueryToErr("select * from t where a>=0")) - } - - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("update t set a=%d where a=%d", i, i+10)) - require.NoError(t, tk.QueryToErr("select * from t where a>=0")) - } - - for i := 0; i < 10; i++ { - txn, err := store.Begin() - require.NoError(t, err) - _, err = idxOp.Create(ctx, txn, types.MakeDatums(i+10), kv.IntHandle(100+i), nil) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - err = tk.QueryToErr("select * from t use index(idx_a) where a >= 0") - require.Equal(t, fmt.Sprintf("[executor:8133]data inconsistency in table: t, index: idx_a, index-count:%d != record-count:10", i+11), err.Error()) - // if has other conditions, the inconsistent index check doesn't work. - err = tk.QueryToErr("select * from t where a>=0 and b<10") - require.NoError(t, err) - } - - // fix inconsistent problem to pass CI - for i := 0; i < 10; i++ { - txn, err := store.Begin() - require.NoError(t, err) - err = idxOp.Delete(ctx.GetSessionVars().StmtCtx, txn, types.MakeDatums(i+10), kv.IntHandle(100+i)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - } -} - -func TestPartitionTableIndexLookUpReader(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(`create table t (a int, b int, key(a)) - partition by range (a) ( - partition p1 values less than (10), - partition p2 values less than (20), - partition p3 values less than (30), - partition p4 values less than (40))`) - tk.MustExec(`insert into t values (1, 1), (2, 2), (11, 11), (12, 12), (21, 21), (22, 22), (31, 31), (32, 32)`) - tk.MustExec(`set tidb_partition_prune_mode='dynamic'`) - - tk.MustQuery("select * from t where a>=1 and a<=1").Sort().Check(testkit.Rows("1 1")) - tk.MustQuery("select * from t where a>=1 and a<=2").Sort().Check(testkit.Rows("1 1", "2 2")) - tk.MustQuery("select * from t where a>=1 and a<12").Sort().Check(testkit.Rows("1 1", "11 11", "2 2")) - tk.MustQuery("select * from t where a>=1 and a<15").Sort().Check(testkit.Rows("1 1", "11 11", "12 12", "2 2")) - tk.MustQuery("select * from t where a>15 and a<32").Sort().Check(testkit.Rows("21 21", "22 22", "31 31")) - tk.MustQuery("select * from t where a>30").Sort().Check(testkit.Rows("31 31", "32 32")) - tk.MustQuery("select * from t where a>=1 and a<15 order by a").Check(testkit.Rows("1 1", "2 2", "11 11", "12 12")) - tk.MustQuery("select * from t where a>=1 and a<15 order by a limit 1").Check(testkit.Rows("1 1")) - tk.MustQuery("select * from t where a>=1 and a<15 order by a limit 3").Check(testkit.Rows("1 1", "2 2", "11 11")) - tk.MustQuery("select * from t where a between 1 and 15 order by a limit 3").Check(testkit.Rows("1 1", "2 2", "11 11")) - tk.MustQuery("select * from t where a between 1 and 15 order by a limit 3 offset 1").Check(testkit.Rows("2 2", "11 11", "12 12")) -} - -func TestPartitionTableRandomlyIndexLookUpReader(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(`create table t (a int, b int, key(a)) - partition by range (a) ( - partition p1 values less than (10), - partition p2 values less than (20), - partition p3 values less than (30), - partition p4 values less than (40))`) - tk.MustExec("create table tnormal (a int, b int, key(a))") - values := make([]string, 0, 128) - for i := 0; i < 128; i++ { - values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(40), rand.Intn(40))) - } - tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) - tk.MustExec(fmt.Sprintf("insert into tnormal values %v", strings.Join(values, ", "))) - - randRange := func() (int, int) { - a, b := rand.Intn(40), rand.Intn(40) - if a > b { - return b, a - } - return a, b - } - for i := 0; i < 256; i++ { - la, ra := randRange() - lb, rb := randRange() - cond := fmt.Sprintf("(a between %v and %v) or (b between %v and %v)", la, ra, lb, rb) - tk.MustQuery("select * from t use index(a) where " + cond).Sort().Check( - tk.MustQuery("select * from tnormal where " + cond).Sort().Rows()) - } -} - -func TestIndexLookUpStats(t *testing.T) { - stats := &executor.IndexLookUpRunTimeStats{ - FetchHandleTotal: int64(5 * time.Second), - FetchHandle: int64(2 * time.Second), - TaskWait: int64(2 * time.Second), - TableRowScan: int64(2 * time.Second), - TableTaskNum: 2, - Concurrency: 1, - NextWaitIndexScan: time.Second, - NextWaitTableLookUpBuild: 2 * time.Second, - NextWaitTableLookUpResp: 3 * time.Second, - } - require.Equal(t, "index_task: {total_time: 5s, fetch_handle: 2s, build: 1s, wait: 2s}"+ - ", table_task: {total_time: 2s, num: 2, concurrency: 1}"+ - ", next: {wait_index: 1s, wait_table_lookup_build: 2s, wait_table_lookup_resp: 3s}", stats.String()) - require.Equal(t, stats.Clone().String(), stats.String()) - stats.Merge(stats.Clone()) - require.Equal(t, "index_task: {total_time: 10s, fetch_handle: 4s, build: 2s, wait: 4s}"+ - ", table_task: {total_time: 4s, num: 4, concurrency: 1}"+ - ", next: {wait_index: 2s, wait_table_lookup_build: 4s, wait_table_lookup_resp: 6s}", stats.String()) -} - -func TestIndexLookUpGetResultChunk(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists tbl") - tk.MustExec("create table tbl(a int, b int, c int, key idx_a(a))") - for i := 0; i < 101; i++ { - tk.MustExec(fmt.Sprintf("insert into tbl values(%d,%d,%d)", i, i, i)) - } - tk.MustQuery("select * from tbl use index(idx_a) where a > 99 order by a asc limit 1").Check(testkit.Rows("100 100 100")) - tk.MustQuery("select * from tbl use index(idx_a) where a > 10 order by a asc limit 4,1").Check(testkit.Rows("15 15 15")) -} - -func TestPartitionTableIndexJoinIndexLookUp(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk.MustExec(`create table t (a int, b int, key(a)) partition by hash(a) partitions 4`) - tk.MustExec("create table tnormal (a int, b int, key(a), key(b))") - nRows := 512 - values := make([]string, 0, nRows) - for i := 0; i < nRows; i++ { - values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(nRows), rand.Intn(nRows))) - } - tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) - tk.MustExec(fmt.Sprintf("insert into tnormal values %v", strings.Join(values, ", "))) - - randRange := func() (int, int) { - a, b := rand.Intn(nRows), rand.Intn(nRows) - if a > b { - return b, a - } - return a, b - } - for i := 0; i < nRows; i++ { - lb, rb := randRange() - cond := fmt.Sprintf("(t2.b between %v and %v)", lb, rb) - result := tk.MustQuery("select t1.* from tnormal t1, tnormal t2 use index(a) where t1.a=t2.b and " + cond).Sort().Rows() - tk.MustQuery("select /*+ TIDB_INLJ(t1, t2) */ t1.* from t t1, t t2 use index(a) where t1.a=t2.b and " + cond).Sort().Check(result) - } -} - -func TestCoprocessorPagingSize(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t_paging (a int, b int, key(a), key(b))") - nRows := 512 - values := make([]string, 0, nRows) - for i := 0; i < nRows; i++ { - values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(nRows), rand.Intn(nRows))) - } - tk.MustExec(fmt.Sprintf("insert into t_paging values %v", strings.Join(values, ", "))) - tk.MustQuery("select @@tidb_min_paging_size").Check(testkit.Rows(strconv.FormatUint(paging.MinPagingSize, 10))) - - // Enable the coprocessor paging protocol. - tk.MustExec("set @@tidb_enable_paging = on") - - // When the min paging size is small, we need more RPC roundtrip! - // Check 'rpc_num' in the execution information - // - // mysql> explain analyze select * from t_paging; - // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - // | id |task | execution info | - // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - // | TableReader_5 |root | time:7.27ms, loops:2, cop_task: {num: 10, max: 1.57ms, min: 313.3µs, avg: 675.9µs, p95: 1.57ms, tot_proc: 2ms, rpc_num: 10, rpc_time: 6.69ms, copr_cache_hit_ratio: 0.00, distsql_concurrency: 15} | - // | └─TableFullScan_4 |cop[tikv] | tikv_task:{proc max:1.48ms, min:294µs, avg: 629µs, p80:1.21ms, p95:1.48ms, iters:0, tasks:10} | - // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ - // 2 rows in set (0.01 sec) - - getRPCNumFromExplain := func(rows [][]interface{}) (res uint64) { - re := regexp.MustCompile("rpc_num: ([0-9]+)") - for _, row := range rows { - buf := bytes.NewBufferString("") - _, _ = fmt.Fprintf(buf, "%s\n", row) - if matched := re.FindStringSubmatch(buf.String()); matched != nil { - require.Equal(t, len(matched), 2) - c, err := strconv.ParseUint(matched[1], 10, 64) - require.NoError(t, err) - return c - } - } - return res - } - - // This is required here because only the chunk encoding collect the execution information and contains 'rpc_num'. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - tk.MustExec("set @@tidb_min_paging_size = 1") - rows := tk.MustQuery("explain analyze select * from t_paging").Rows() - rpcNum := getRPCNumFromExplain(rows) - require.Greater(t, rpcNum, uint64(2)) - - tk.MustExec("set @@tidb_min_paging_size = 1000") - rows = tk.MustQuery("explain analyze select * from t_paging").Rows() - rpcNum = getRPCNumFromExplain(rows) - require.Equal(t, rpcNum, uint64(1)) -} - -func TestAdaptiveClosestRead(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect this UT - tk.MustExec("drop table if exists t") - // the avg row size is more accurate in check_rpc mode when unistre is used. - // See: https://github.com/pingcap/tidb/issues/31744#issuecomment-1016309883 - tk.MustExec("set @@tidb_enable_chunk_rpc = '1'") - - readCounter := func(counter prometheus.Counter) float64 { - var metric dto.Metric - require.Nil(t, counter.Write(&metric)) - return metric.Counter.GetValue() - } - - checkMetrics := func(q string, hit, miss int) { - beforeHit := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("hit")) - beforeMiss := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("miss")) - tk.MustQuery(q) - afterHit := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("hit")) - afterMiss := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("miss")) - require.Equal(t, hit, int(afterHit-beforeHit), "exec query '%s' check hit failed", q) - require.Equal(t, miss, int(afterMiss-beforeMiss), "exec query '%s' check miss failed", q) - } - - tk.MustExec("create table t(id int primary key, s varchar(8), p varchar(16));") - tk.MustExec("insert into t values (1, '00000001', '0000000000000001'), (2, '00000003', '0000000000000002'), (3, '00000011', '0000000000000003');") - tk.MustExec("analyze table t;") - - tk.MustExec("set @@tidb_partition_prune_mode ='static';") - tk.MustExec("set tidb_replica_read = 'closest-adaptive';") - tk.MustExec("set tidb_adaptive_closest_read_threshold = 25;") - - // table reader - // estimate cost is 19 - checkMetrics("select s from t where id >= 1 and id < 2;", 0, 1) - // estimate cost is 37 - checkMetrics("select * from t where id >= 1 and id < 2;", 1, 0) - tk.MustExec("set tidb_adaptive_closest_read_threshold = 50;") - checkMetrics("select * from t where id >= 1 and id < 2;", 0, 1) - // estimate cost is 74 - checkMetrics("select * from t where id >= 1 and id <= 2;", 1, 0) - - partitionDef := "PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (3), PARTITION p3 VALUES LESS THAN MAXVALUE);" - - // test TableReader with partition - tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int primary key, s varchar(8), p varchar(16)) " + partitionDef) - tk.MustExec("insert into t values (1, '00000001', '0000000000000001'), (2, '00000003', '0000000000000002'), (3, '00000011', '0000000000000003'), (4, '00000044', '0000000000000004');") - tk.MustExec("analyze table t;") - // estimate cost is 38 - checkMetrics("select s from t where id >= 1 and id < 3;", 1, 0) - // estimate cost is 39 with 2 cop request - checkMetrics("select s from t where id >= 2 and id < 4;", 0, 2) - - // index reader - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (id int, s varchar(8), p varchar(8), key `idx_s_p`(`s`, `p`));") - tk.MustExec("insert into t values (1, 'test1000', '11111111'), (2, 'test2000', '11111111');") - tk.MustExec("analyze table t;") - // avg row size = 27.91 - checkMetrics("select p from t where s >= 'test' and s < 'test11'", 0, 1) - checkMetrics("select p from t where s >= 'test' and s < 'test22'", 1, 0) - - // index reader with partitions - tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (v int, id int, p varchar(8), key `idx_id_p`(`id`, `p`)) " + partitionDef) - tk.MustExec("insert into t values (1, 1, '11111111'), (2, 2, '22222222'), (3, 3, '33333333'), (4, 4, '44444444');") - tk.MustExec("analyze table t;") - // avg row size = 19 - checkMetrics("select p from t where id >= 1 and id < 3", 1, 0) - checkMetrics("select p from t where id >= 2 and id < 4", 0, 2) - checkMetrics("select p from t where id >= 1 and id < 4", 1, 1) - - // index lookup reader - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (id int, s varchar(8), p varchar(50), key `idx_s`(`s`));") - str := "this_is_a_string_with_length_of_50________________" - tk.MustExec(fmt.Sprintf("insert into t values (1, 'test1000', '%s'), (2, 'test2000', '%s');", str, str)) - tk.MustExec("analyze table t;") - tk.MustExec("set tidb_adaptive_closest_read_threshold = 80;") - // IndexReader cost is 22, TableReader cost (1 row) is 67 - checkMetrics("select/*+ FORCE_INDEX(t, idx_s) */ p from t where s >= 'test' and s < 'test11'", 0, 2) - tk.MustExec("set tidb_adaptive_closest_read_threshold = 100;") - checkMetrics("select/*+ FORCE_INDEX(t, idx_s) */ p from t where s >= 'test' and s < 'test22'", 1, 1) - - // index merge reader - tk.MustExec("drop table if exists t;") - // use int field to avoid the planer estimation with big random fluctuation. - tk.MustExec("create table t (id int, v bigint not null, s1 int not null, s2 int not null, key `idx_v_s1`(`s1`, `v`), key `idx_s2`(`s2`));") - tk.MustExec("insert into t values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") - tk.MustExec("analyze table t;") - tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") - // 2 IndexScan with cost 19/56, 2 TableReader with cost 32.5/65. - checkMetrics("select/* +USE_INDEX_MERGE(t) */ id from t use index(`idx_v_s1`) use index(idx_s2) where (s1 < 3 and v > 0) or s2 = 3;", 3, 1) -} - -func TestCoprocessorPagingReqKeyRangeSorted(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/checkKeyRangeSortedForPaging", "return")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/checkKeyRangeSortedForPaging")) - }() - - tk.MustExec("use test") - tk.MustExec("CREATE TABLE `UK_COLLATION19523` (" + - "`COL1` binary(1) DEFAULT NULL," + - "`COL2` varchar(20) COLLATE utf8_general_ci DEFAULT NULL," + - "`COL4` datetime DEFAULT NULL," + - "`COL3` bigint(20) DEFAULT NULL," + - "`COL5` float DEFAULT NULL," + - "UNIQUE KEY `U_COL1` (`COL1`)" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci") - - tk.MustExec("prepare stmt from 'SELECT/*+ HASH_JOIN(t1, t2) */ * FROM UK_COLLATION19523 t1 JOIN UK_COLLATION19523 t2 ON t1.col1 > t2.col1 WHERE t1.col1 IN (?, ?, ?) AND t2.col1 < ?;';") - tk.MustExec("set @a=0x4F, @b=0xF8, @c=NULL, @d=0xBF;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - tk.MustExec("set @a=0x00, @b=0xD2, @c=9179987834981541375, @d=0xF8;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - - tk.MustExec("CREATE TABLE `IDT_COLLATION26873` (" + - "`COL1` varbinary(20) DEFAULT NULL," + - "`COL2` varchar(20) COLLATE utf8_general_ci DEFAULT NULL," + - "`COL4` datetime DEFAULT NULL," + - "`COL3` bigint(20) DEFAULT NULL," + - "`COL5` float DEFAULT NULL," + - "KEY `U_COL1` (`COL1`))") - tk.MustExec("prepare stmt from 'SELECT/*+ INL_JOIN(t1, t2) */ t2.* FROM IDT_COLLATION26873 t1 LEFT JOIN IDT_COLLATION26873 t2 ON t1.col1 = t2.col1 WHERE t1.col1 < ? AND t1.col1 IN (?, ?, ?);';") - tk.MustExec("set @a=NULL, @b=NULL, @c=NULL, @d=NULL;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - tk.MustExec("set @a=0xE3253A6AC72A3A168EAF0E34A4779A947872CCCD, @b=0xD67BB26504EE152C2C356D7F6CAD897F03462963, @c=NULL, @d=0xDE735FEB375A4CF33479A39CA925470BFB229DB4;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - tk.MustExec("set @a=2606738829406840179, @b=1468233589368287363, @c=5174008984061521089, @d=7727946571160309462;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - tk.MustExec("set @a=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @b=NULL, @c=6864108002939154648, @d=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - tk.MustExec("set @a=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @b=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @c=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @d=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE;") - tk.MustExec("execute stmt using @a,@b,@c,@d;") - - tk.MustExec("CREATE TABLE `PK_SNPRE10114` (" + - "`COL1` varbinary(10) NOT NULL DEFAULT 'S'," + - "`COL2` varchar(20) DEFAULT NULL," + - "`COL4` datetime DEFAULT NULL," + - "`COL3` bigint(20) DEFAULT NULL," + - "`COL5` float DEFAULT NULL," + - "PRIMARY KEY (`COL1`) CLUSTERED)") - tk.MustExec(`prepare stmt from 'SELECT * FROM PK_SNPRE10114 WHERE col1 IN (?, ?, ?) AND (col2 IS NULL OR col2 IN (?, ?)) AND (col3 IS NULL OR col4 IS NULL);';`) - tk.MustExec(`set @a=0x0D5BDAEB79074756F203, @b=NULL, @c=0x6A911AAAC728F1ED3B4F, @d="鏖秿垙麜濇凗辯Ũ卮伄幖轒ƀ漭蝏雓轊恿磔徵", @e="訇廵纹髺釖寒近槩靏詗膦潳陒錃粓悧闒摔)乀";`) - tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) - tk.MustExec(`set @a=7775448739068993371, @b=5641728652098016210, @c=6774432238941172824, @d="HqpP5rN", @e="8Fy";`) - tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) - tk.MustExec(`set @a=0x61219F79C90D3541F70E, @b=5501707547099269248, @c=0xEC43EFD30131DEA2CB8B, @d="呣丼蒢咿卻鹻铴础湜僂頃dž縍套衞陀碵碼幓9", @e="鹹楞睕堚尛鉌翡佾搁紟精廬姆燵藝潐楻翇慸嵊";`) - tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) -} - -func TestCoprocessorBatchByStore(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t(id int primary key, c1 int, c2 int, key i(c1))") - tk.MustExec(`create table t1(id int primary key, c1 int, c2 int, key i(c1)) partition by range(id) ( - partition p0 values less than(10000), - partition p1 values less than (50000), - partition p2 values less than (100000))`) - for i := 0; i < 10; i++ { - tk.MustExec("insert into t values(?, ?, ?)", i*10000, i*10000, i%2) - tk.MustExec("insert into t1 values(?, ?, ?)", i*10000, i*10000, i%2) - } - tk.MustQuery("split table t between (0) and (100000) regions 20").Check(testkit.Rows("20 1")) - tk.MustQuery("split table t1 between (0) and (100000) regions 20").Check(testkit.Rows("60 1")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/setRangesPerTask", "return(1)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/setRangesPerTask")) - }() - ranges := []string{ - "(c1 >= 0 and c1 < 5000)", - "(c1 >= 10000 and c1 < 15000)", - "(c1 >= 20000 and c1 < 25000)", - "(c1 >= 30000 and c1 < 35000)", - "(c1 >= 40000 and c1 < 45000)", - "(c1 >= 50000 and c1 < 55000)", - "(c1 >= 60000 and c1 < 65000)", - "(c1 >= 70000 and c1 < 75000)", - "(c1 >= 80000 and c1 < 85000)", - "(c1 >= 90000 and c1 < 95000)", - } - evenRows := testkit.Rows("0 0 0", "20000 20000 0", "40000 40000 0", "60000 60000 0", "80000 80000 0") - oddRows := testkit.Rows("10000 10000 1", "30000 30000 1", "50000 50000 1", "70000 70000 1", "90000 90000 1") - reverseOddRows := testkit.Rows("90000 90000 1", "70000 70000 1", "50000 50000 1", "30000 30000 1", "10000 10000 1") - for _, table := range []string{"t", "t1"} { - baseSQL := fmt.Sprintf("select * from %s force index(i) where id < 100000 and (%s)", table, strings.Join(ranges, " or ")) - for _, paging := range []string{"on", "off"} { - tk.MustExec("set session tidb_enable_paging=?", paging) - for size := 0; size < 10; size++ { - tk.MustExec("set session tidb_store_batch_size=?", size) - tk.MustQuery(baseSQL + " and c2 = 0").Sort().Check(evenRows) - tk.MustQuery(baseSQL + " and c2 = 1").Sort().Check(oddRows) - tk.MustQuery(baseSQL + " and c2 = 0 order by c1 asc").Check(evenRows) - tk.MustQuery(baseSQL + " and c2 = 1 order by c1 desc").Check(reverseOddRows) - // every batched task will get region error and fallback. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/batchCopRegionError", "return")) - tk.MustQuery(baseSQL + " and c2 = 0").Sort().Check(evenRows) - tk.MustQuery(baseSQL + " and c2 = 1").Sort().Check(oddRows) - tk.MustQuery(baseSQL + " and c2 = 0 order by c1 asc").Check(evenRows) - tk.MustQuery(baseSQL + " and c2 = 1 order by c1 desc").Check(reverseOddRows) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/batchCopRegionError")) - } - } - } -} - -func TestIndexLookUpWithSelectForUpdateOnPartitionTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index k(b)) PARTITION BY HASH(a) partitions 4") - tk.MustExec("insert into t(a, b) values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8)") - tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "PartitionUnion") - tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "IndexLookUp") - tk.MustQuery("select b from t use index(k) where b > 2 order by b limit 1 for update").Check(testkit.Rows("3")) - - tk.MustExec("analyze table t") - tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "IndexLookUp") - tk.MustQuery("select b from t use index(k) where b > 2 order by b limit 1 for update").Check(testkit.Rows("3")) -} diff --git a/executor/executor.go b/executor/executor.go deleted file mode 100644 index 73d1c4a4a553a..0000000000000 --- a/executor/executor.go +++ /dev/null @@ -1,2693 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "cmp" - "context" - "fmt" - "math" - "runtime/pprof" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/opentracing/opentracing-go" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/pdhelper" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/resourcemanager/pool/workerpool" - poolutil "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/admin" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/tracing" - tikverr "github.com/tikv/client-go/v2/error" - tikvstore "github.com/tikv/client-go/v2/kv" - tikvutil "github.com/tikv/client-go/v2/util" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -var ( - _ exec.Executor = &CheckTableExec{} - _ exec.Executor = &aggregate.HashAggExec{} - _ exec.Executor = &HashJoinExec{} - _ exec.Executor = &IndexLookUpExecutor{} - _ exec.Executor = &IndexReaderExecutor{} - _ exec.Executor = &LimitExec{} - _ exec.Executor = &MaxOneRowExec{} - _ exec.Executor = &MergeJoinExec{} - _ exec.Executor = &ProjectionExec{} - _ exec.Executor = &SelectionExec{} - _ exec.Executor = &SelectLockExec{} - _ exec.Executor = &ShowNextRowIDExec{} - _ exec.Executor = &ShowDDLExec{} - _ exec.Executor = &ShowDDLJobsExec{} - _ exec.Executor = &ShowDDLJobQueriesExec{} - _ exec.Executor = &SortExec{} - _ exec.Executor = &aggregate.StreamAggExec{} - _ exec.Executor = &TableDualExec{} - _ exec.Executor = &TableReaderExecutor{} - _ exec.Executor = &TableScanExec{} - _ exec.Executor = &TopNExec{} - _ exec.Executor = &UnionExec{} - _ exec.Executor = &FastCheckTableExec{} - - // GlobalMemoryUsageTracker is the ancestor of all the Executors' memory tracker and GlobalMemory Tracker - GlobalMemoryUsageTracker *memory.Tracker - // GlobalDiskUsageTracker is the ancestor of all the Executors' disk tracker - GlobalDiskUsageTracker *disk.Tracker - // GlobalAnalyzeMemoryTracker is the ancestor of all the Analyze jobs' memory tracker and child of global Tracker - GlobalAnalyzeMemoryTracker *memory.Tracker -) - -var ( - _ dataSourceExecutor = &TableReaderExecutor{} - _ dataSourceExecutor = &IndexReaderExecutor{} - _ dataSourceExecutor = &IndexLookUpExecutor{} - _ dataSourceExecutor = &IndexMergeReaderExecutor{} - - // CheckTableFastBucketSize is the bucket size of fast check table. - CheckTableFastBucketSize = atomic.Int64{} -) - -// dataSourceExecutor is a table DataSource converted Executor. -// Currently, there are TableReader/IndexReader/IndexLookUp/IndexMergeReader. -// Note, partition reader is special and the caller should handle it carefully. -type dataSourceExecutor interface { - exec.Executor - Table() table.Table -} - -const ( - // globalPanicStorageExceed represents the panic message when out of storage quota. - globalPanicStorageExceed string = "Out Of Quota For Local Temporary Space!" - // globalPanicMemoryExceed represents the panic message when out of memory limit. - globalPanicMemoryExceed string = "Out Of Global Memory Limit!" - // globalPanicAnalyzeMemoryExceed represents the panic message when out of analyze memory limit. - globalPanicAnalyzeMemoryExceed string = "Out Of Global Analyze Memory Limit!" -) - -// globalPanicOnExceed panics when GlobalDisTracker storage usage exceeds storage quota. -type globalPanicOnExceed struct { - memory.BaseOOMAction - mutex syncutil.Mutex // For synchronization. -} - -func init() { - action := &globalPanicOnExceed{} - GlobalMemoryUsageTracker = memory.NewGlobalTracker(memory.LabelForGlobalMemory, -1) - GlobalMemoryUsageTracker.SetActionOnExceed(action) - GlobalDiskUsageTracker = disk.NewGlobalTrcaker(memory.LabelForGlobalStorage, -1) - GlobalDiskUsageTracker.SetActionOnExceed(action) - GlobalAnalyzeMemoryTracker = memory.NewTracker(memory.LabelForGlobalAnalyzeMemory, -1) - GlobalAnalyzeMemoryTracker.SetActionOnExceed(action) - // register quota funcs - variable.SetMemQuotaAnalyze = GlobalAnalyzeMemoryTracker.SetBytesLimit - variable.GetMemQuotaAnalyze = GlobalAnalyzeMemoryTracker.GetBytesLimit - // TODO: do not attach now to avoid impact to global, will attach later when analyze memory track is stable - //GlobalAnalyzeMemoryTracker.AttachToGlobalTracker(GlobalMemoryUsageTracker) - - schematracker.ConstructResultOfShowCreateDatabase = ConstructResultOfShowCreateDatabase - schematracker.ConstructResultOfShowCreateTable = ConstructResultOfShowCreateTable - - // CheckTableFastBucketSize is used to set the fast analyze bucket size for check table. - CheckTableFastBucketSize.Store(1024) -} - -// Start the backend components -func Start() { - pdhelper.GlobalPDHelper.Start() -} - -// Stop the backend components -func Stop() { - pdhelper.GlobalPDHelper.Stop() -} - -// Action panics when storage usage exceeds storage quota. -func (a *globalPanicOnExceed) Action(t *memory.Tracker) { - a.mutex.Lock() - defer a.mutex.Unlock() - msg := "" - switch t.Label() { - case memory.LabelForGlobalStorage: - msg = globalPanicStorageExceed - case memory.LabelForGlobalMemory: - msg = globalPanicMemoryExceed - case memory.LabelForGlobalAnalyzeMemory: - msg = globalPanicAnalyzeMemoryExceed - default: - msg = "Out of Unknown Resource Quota!" - } - panic(msg) -} - -// GetPriority get the priority of the Action -func (*globalPanicOnExceed) GetPriority() int64 { - return memory.DefPanicPriority -} - -// newList creates a new List to buffer current executor's result. -func newList(e exec.Executor) *chunk.List { - base := e.Base() - return chunk.NewList(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize()) -} - -// CommandDDLJobsExec is the general struct for Cancel/Pause/Resume commands on -// DDL jobs. These command currently by admin have the very similar struct and -// operations, it should be a better idea to have them in the same struct. -type CommandDDLJobsExec struct { - exec.BaseExecutor - - cursor int - jobIDs []int64 - errs []error - - execute func(se sessionctx.Context, ids []int64) (errs []error, err error) -} - -// Open implements the Executor for all Cancel/Pause/Resume command on DDL jobs -// just with different processes. And, it should not be called directly by the -// Executor. -func (e *CommandDDLJobsExec) Open(context.Context) error { - // We want to use a global transaction to execute the admin command, so we don't use e.Ctx() here. - newSess, err := e.GetSysSession() - if err != nil { - return err - } - e.errs, err = e.execute(newSess, e.jobIDs) - e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), newSess) - return err -} - -// Next implements the Executor Next interface for Cancel/Pause/Resume -func (e *CommandDDLJobsExec) Next(_ context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if e.cursor >= len(e.jobIDs) { - return nil - } - numCurBatch := mathutil.Min(req.Capacity(), len(e.jobIDs)-e.cursor) - for i := e.cursor; i < e.cursor+numCurBatch; i++ { - req.AppendString(0, strconv.FormatInt(e.jobIDs[i], 10)) - if e.errs != nil && e.errs[i] != nil { - req.AppendString(1, fmt.Sprintf("error: %v", e.errs[i])) - } else { - req.AppendString(1, "successful") - } - } - e.cursor += numCurBatch - return nil -} - -// CancelDDLJobsExec represents a cancel DDL jobs executor. -type CancelDDLJobsExec struct { - *CommandDDLJobsExec -} - -// PauseDDLJobsExec indicates an Executor for Pause a DDL Job. -type PauseDDLJobsExec struct { - *CommandDDLJobsExec -} - -// ResumeDDLJobsExec indicates an Executor for Resume a DDL Job. -type ResumeDDLJobsExec struct { - *CommandDDLJobsExec -} - -// ShowNextRowIDExec represents a show the next row ID executor. -type ShowNextRowIDExec struct { - exec.BaseExecutor - tblName *ast.TableName - done bool -} - -// Next implements the Executor Next interface. -func (e *ShowNextRowIDExec) Next(_ context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - is := domain.GetDomain(e.Ctx()).InfoSchema() - tbl, err := is.TableByName(e.tblName.Schema, e.tblName.Name) - if err != nil { - return err - } - tblMeta := tbl.Meta() - - allocators := tbl.Allocators(e.Ctx()) - for _, alloc := range allocators.Allocs { - nextGlobalID, err := alloc.NextGlobalAutoID() - if err != nil { - return err - } - - var colName, idType string - switch alloc.GetType() { - case autoid.RowIDAllocType: - idType = "_TIDB_ROWID" - if tblMeta.PKIsHandle { - if col := tblMeta.GetAutoIncrementColInfo(); col != nil { - colName = col.Name.O - } - } else { - colName = model.ExtraHandleName.O - } - case autoid.AutoIncrementType: - idType = "AUTO_INCREMENT" - if tblMeta.PKIsHandle { - if col := tblMeta.GetAutoIncrementColInfo(); col != nil { - colName = col.Name.O - } - } else { - colName = model.ExtraHandleName.O - } - case autoid.AutoRandomType: - idType = "AUTO_RANDOM" - colName = tblMeta.GetPkName().O - case autoid.SequenceType: - idType = "SEQUENCE" - colName = "" - default: - return autoid.ErrInvalidAllocatorType.GenWithStackByArgs() - } - - req.AppendString(0, e.tblName.Schema.O) - req.AppendString(1, e.tblName.Name.O) - req.AppendString(2, colName) - req.AppendInt64(3, nextGlobalID) - req.AppendString(4, idType) - } - - e.done = true - return nil -} - -// ShowDDLExec represents a show DDL executor. -type ShowDDLExec struct { - exec.BaseExecutor - - ddlOwnerID string - selfID string - ddlInfo *ddl.Info - done bool -} - -// Next implements the Executor Next interface. -func (e *ShowDDLExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - - ddlJobs := "" - query := "" - l := len(e.ddlInfo.Jobs) - for i, job := range e.ddlInfo.Jobs { - ddlJobs += job.String() - query += job.Query - if i != l-1 { - ddlJobs += "\n" - query += "\n" - } - } - - serverInfo, err := infosync.GetServerInfoByID(ctx, e.ddlOwnerID) - if err != nil { - return err - } - - serverAddress := serverInfo.IP + ":" + - strconv.FormatUint(uint64(serverInfo.Port), 10) - - req.AppendInt64(0, e.ddlInfo.SchemaVer) - req.AppendString(1, e.ddlOwnerID) - req.AppendString(2, serverAddress) - req.AppendString(3, ddlJobs) - req.AppendString(4, e.selfID) - req.AppendString(5, query) - - e.done = true - return nil -} - -// ShowDDLJobsExec represent a show DDL jobs executor. -type ShowDDLJobsExec struct { - exec.BaseExecutor - DDLJobRetriever - - jobNumber int - is infoschema.InfoSchema - sess sessionctx.Context -} - -// DDLJobRetriever retrieve the DDLJobs. -// nolint:structcheck -type DDLJobRetriever struct { - runningJobs []*model.Job - historyJobIter meta.LastJobIterator - cursor int - is infoschema.InfoSchema - activeRoles []*auth.RoleIdentity - cacheJobs []*model.Job - TZLoc *time.Location -} - -func (e *DDLJobRetriever) initial(txn kv.Transaction, sess sessionctx.Context) error { - m := meta.NewMeta(txn) - jobs, err := ddl.GetAllDDLJobs(sess) - if err != nil { - return err - } - e.historyJobIter, err = ddl.GetLastHistoryDDLJobsIterator(m) - if err != nil { - return err - } - e.runningJobs = jobs - e.cursor = 0 - return nil -} - -func (e *DDLJobRetriever) appendJobToChunk(req *chunk.Chunk, job *model.Job, checker privilege.Manager) { - schemaName := job.SchemaName - tableName := "" - finishTS := uint64(0) - if job.BinlogInfo != nil { - finishTS = job.BinlogInfo.FinishedTS - if job.BinlogInfo.TableInfo != nil { - tableName = job.BinlogInfo.TableInfo.Name.L - } - if job.BinlogInfo.MultipleTableInfos != nil { - tablenames := new(strings.Builder) - for i, affect := range job.BinlogInfo.MultipleTableInfos { - if i > 0 { - fmt.Fprintf(tablenames, ",") - } - fmt.Fprintf(tablenames, "%s", affect.Name.L) - } - tableName = tablenames.String() - } - if len(schemaName) == 0 && job.BinlogInfo.DBInfo != nil { - schemaName = job.BinlogInfo.DBInfo.Name.L - } - } - if len(tableName) == 0 { - tableName = job.TableName - } - // For compatibility, the old version of DDL Job wasn't store the schema name and table name. - if len(schemaName) == 0 { - schemaName = getSchemaName(e.is, job.SchemaID) - } - if len(tableName) == 0 { - tableName = getTableName(e.is, job.TableID) - } - - createTime := ts2Time(job.StartTS, e.TZLoc) - startTime := ts2Time(job.RealStartTS, e.TZLoc) - finishTime := ts2Time(finishTS, e.TZLoc) - - // Check the privilege. - if checker != nil && !checker.RequestVerification(e.activeRoles, strings.ToLower(schemaName), strings.ToLower(tableName), "", mysql.AllPrivMask) { - return - } - - req.AppendInt64(0, job.ID) - req.AppendString(1, schemaName) - req.AppendString(2, tableName) - req.AppendString(3, job.Type.String()+showAddIdxReorgTp(job)) - req.AppendString(4, job.SchemaState.String()) - req.AppendInt64(5, job.SchemaID) - req.AppendInt64(6, job.TableID) - req.AppendInt64(7, job.RowCount) - req.AppendTime(8, createTime) - if job.RealStartTS > 0 { - req.AppendTime(9, startTime) - } else { - req.AppendNull(9) - } - if finishTS > 0 { - req.AppendTime(10, finishTime) - } else { - req.AppendNull(10) - } - req.AppendString(11, job.State.String()) - if job.Type == model.ActionMultiSchemaChange { - for _, subJob := range job.MultiSchemaInfo.SubJobs { - req.AppendInt64(0, job.ID) - req.AppendString(1, schemaName) - req.AppendString(2, tableName) - req.AppendString(3, subJob.Type.String()+" /* subjob */"+showAddIdxReorgTpInSubJob(subJob)) - req.AppendString(4, subJob.SchemaState.String()) - req.AppendInt64(5, job.SchemaID) - req.AppendInt64(6, job.TableID) - req.AppendInt64(7, subJob.RowCount) - req.AppendTime(8, createTime) - if subJob.RealStartTS > 0 { - realStartTS := ts2Time(subJob.RealStartTS, e.TZLoc) - req.AppendTime(9, realStartTS) - } else { - req.AppendNull(9) - } - if finishTS > 0 { - req.AppendTime(10, finishTime) - } else { - req.AppendNull(10) - } - req.AppendString(11, subJob.State.String()) - } - } -} - -func showAddIdxReorgTp(job *model.Job) string { - if job.Type == model.ActionAddIndex || job.Type == model.ActionAddPrimaryKey { - if job.ReorgMeta != nil { - tp := job.ReorgMeta.ReorgTp.String() - if len(tp) > 0 { - return " /* " + tp + " */" - } - } - } - return "" -} - -func showAddIdxReorgTpInSubJob(subJob *model.SubJob) string { - if subJob.Type == model.ActionAddIndex || subJob.Type == model.ActionAddPrimaryKey { - tp := subJob.ReorgTp.String() - if len(tp) > 0 { - return " /* " + tp + " */" - } - } - return "" -} - -func ts2Time(timestamp uint64, loc *time.Location) types.Time { - duration := time.Duration(math.Pow10(9-types.DefaultFsp)) * time.Nanosecond - t := model.TSConvert2Time(timestamp) - t.Truncate(duration) - return types.NewTime(types.FromGoTime(t.In(loc)), mysql.TypeDatetime, types.DefaultFsp) -} - -// ShowDDLJobQueriesExec represents a show DDL job queries executor. -// The jobs id that is given by 'admin show ddl job queries' statement, -// only be searched in the latest 10 history jobs. -type ShowDDLJobQueriesExec struct { - exec.BaseExecutor - - cursor int - jobs []*model.Job - jobIDs []int64 -} - -// Open implements the Executor Open interface. -func (e *ShowDDLJobQueriesExec) Open(ctx context.Context) error { - var err error - var jobs []*model.Job - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - session, err := e.GetSysSession() - if err != nil { - return err - } - err = sessiontxn.NewTxn(context.Background(), session) - if err != nil { - return err - } - defer func() { - // ReleaseSysSession will rollbacks txn automatically. - e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) - }() - txn, err := session.Txn(true) - if err != nil { - return err - } - session.GetSessionVars().SetInTxn(true) - - m := meta.NewMeta(txn) - jobs, err = ddl.GetAllDDLJobs(session) - if err != nil { - return err - } - - historyJobs, err := ddl.GetLastNHistoryDDLJobs(m, ddl.DefNumHistoryJobs) - if err != nil { - return err - } - - appendedJobID := make(map[int64]struct{}) - // deduplicate job results - // for situations when this operation happens at the same time with new DDLs being executed - for _, job := range jobs { - if _, ok := appendedJobID[job.ID]; !ok { - appendedJobID[job.ID] = struct{}{} - e.jobs = append(e.jobs, job) - } - } - for _, historyJob := range historyJobs { - if _, ok := appendedJobID[historyJob.ID]; !ok { - appendedJobID[historyJob.ID] = struct{}{} - e.jobs = append(e.jobs, historyJob) - } - } - - return nil -} - -// Next implements the Executor Next interface. -func (e *ShowDDLJobQueriesExec) Next(_ context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if e.cursor >= len(e.jobs) { - return nil - } - if len(e.jobIDs) >= len(e.jobs) { - return nil - } - numCurBatch := mathutil.Min(req.Capacity(), len(e.jobs)-e.cursor) - for _, id := range e.jobIDs { - for i := e.cursor; i < e.cursor+numCurBatch; i++ { - if id == e.jobs[i].ID { - req.AppendString(0, e.jobs[i].Query) - } - } - } - e.cursor += numCurBatch - return nil -} - -// ShowDDLJobQueriesWithRangeExec represents a show DDL job queries with range executor. -// The jobs id that is given by 'admin show ddl job queries' statement, -// can be searched within a specified range in history jobs using offset and limit. -type ShowDDLJobQueriesWithRangeExec struct { - exec.BaseExecutor - - cursor int - jobs []*model.Job - offset uint64 - limit uint64 -} - -// Open implements the Executor Open interface. -func (e *ShowDDLJobQueriesWithRangeExec) Open(ctx context.Context) error { - var err error - var jobs []*model.Job - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - session, err := e.GetSysSession() - if err != nil { - return err - } - err = sessiontxn.NewTxn(context.Background(), session) - if err != nil { - return err - } - defer func() { - // ReleaseSysSession will rollbacks txn automatically. - e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) - }() - txn, err := session.Txn(true) - if err != nil { - return err - } - session.GetSessionVars().SetInTxn(true) - - m := meta.NewMeta(txn) - jobs, err = ddl.GetAllDDLJobs(session) - if err != nil { - return err - } - - historyJobs, err := ddl.GetLastNHistoryDDLJobs(m, int(e.offset+e.limit)) - if err != nil { - return err - } - - appendedJobID := make(map[int64]struct{}) - // deduplicate job results - // for situations when this operation happens at the same time with new DDLs being executed - for _, job := range jobs { - if _, ok := appendedJobID[job.ID]; !ok { - appendedJobID[job.ID] = struct{}{} - e.jobs = append(e.jobs, job) - } - } - for _, historyJob := range historyJobs { - if _, ok := appendedJobID[historyJob.ID]; !ok { - appendedJobID[historyJob.ID] = struct{}{} - e.jobs = append(e.jobs, historyJob) - } - } - - if e.cursor < int(e.offset) { - e.cursor = int(e.offset) - } - - return nil -} - -// Next implements the Executor Next interface. -func (e *ShowDDLJobQueriesWithRangeExec) Next(_ context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if e.cursor >= len(e.jobs) { - return nil - } - if int(e.offset) > len(e.jobs) { - return nil - } - numCurBatch := mathutil.Min(req.Capacity(), len(e.jobs)-e.cursor) - for i := e.cursor; i < e.cursor+numCurBatch; i++ { - // i is make true to be >= int(e.offset) - if i >= int(e.offset+e.limit) { - break - } - req.AppendString(0, strconv.FormatInt(e.jobs[i].ID, 10)) - req.AppendString(1, e.jobs[i].Query) - } - e.cursor += numCurBatch - return nil -} - -// Open implements the Executor Open interface. -func (e *ShowDDLJobsExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - e.DDLJobRetriever.is = e.is - if e.jobNumber == 0 { - e.jobNumber = ddl.DefNumHistoryJobs - } - sess, err := e.GetSysSession() - if err != nil { - return err - } - e.sess = sess - err = sessiontxn.NewTxn(context.Background(), sess) - if err != nil { - return err - } - txn, err := sess.Txn(true) - if err != nil { - return err - } - sess.GetSessionVars().SetInTxn(true) - err = e.DDLJobRetriever.initial(txn, sess) - return err -} - -// Next implements the Executor Next interface. -func (e *ShowDDLJobsExec) Next(_ context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if (e.cursor - len(e.runningJobs)) >= e.jobNumber { - return nil - } - count := 0 - - // Append running ddl jobs. - if e.cursor < len(e.runningJobs) { - numCurBatch := mathutil.Min(req.Capacity(), len(e.runningJobs)-e.cursor) - for i := e.cursor; i < e.cursor+numCurBatch; i++ { - e.appendJobToChunk(req, e.runningJobs[i], nil) - } - e.cursor += numCurBatch - count += numCurBatch - } - - // Append history ddl jobs. - var err error - if count < req.Capacity() { - num := req.Capacity() - count - remainNum := e.jobNumber - (e.cursor - len(e.runningJobs)) - num = mathutil.Min(num, remainNum) - e.cacheJobs, err = e.historyJobIter.GetLastJobs(num, e.cacheJobs) - if err != nil { - return err - } - for _, job := range e.cacheJobs { - e.appendJobToChunk(req, job, nil) - } - e.cursor += len(e.cacheJobs) - } - return nil -} - -// Close implements the Executor Close interface. -func (e *ShowDDLJobsExec) Close() error { - e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), e.sess) - return e.BaseExecutor.Close() -} - -func getSchemaName(is infoschema.InfoSchema, id int64) string { - var schemaName string - dbInfo, ok := is.SchemaByID(id) - if ok { - schemaName = dbInfo.Name.O - return schemaName - } - - return schemaName -} - -func getTableName(is infoschema.InfoSchema, id int64) string { - var tableName string - table, ok := is.TableByID(id) - if ok { - tableName = table.Meta().Name.O - return tableName - } - - return tableName -} - -// CheckTableExec represents a check table executor. -// It is built from the "admin check table" statement, and it checks if the -// index matches the records in the table. -type CheckTableExec struct { - exec.BaseExecutor - - dbName string - table table.Table - indexInfos []*model.IndexInfo - srcs []*IndexLookUpExecutor - done bool - is infoschema.InfoSchema - exitCh chan struct{} - retCh chan error - checkIndex bool -} - -// Open implements the Executor Open interface. -func (e *CheckTableExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - for _, src := range e.srcs { - if err := src.Open(ctx); err != nil { - return errors.Trace(err) - } - } - e.done = false - return nil -} - -// Close implements the Executor Close interface. -func (e *CheckTableExec) Close() error { - var firstErr error - close(e.exitCh) - for _, src := range e.srcs { - if err := src.Close(); err != nil && firstErr == nil { - firstErr = err - } - } - return firstErr -} - -func (e *CheckTableExec) checkTableIndexHandle(ctx context.Context, idxInfo *model.IndexInfo) error { - // For partition table, there will be multi same index indexLookUpReaders on different partitions. - for _, src := range e.srcs { - if src.index.Name.L == idxInfo.Name.L { - err := e.checkIndexHandle(ctx, src) - if err != nil { - return err - } - } - } - return nil -} - -func (e *CheckTableExec) checkIndexHandle(ctx context.Context, src *IndexLookUpExecutor) error { - cols := src.Schema().Columns - retFieldTypes := make([]*types.FieldType, len(cols)) - for i := range cols { - retFieldTypes[i] = cols[i].RetType - } - chk := chunk.New(retFieldTypes, e.InitCap(), e.MaxChunkSize()) - - var err error - for { - err = exec.Next(ctx, src, chk) - if err != nil { - e.retCh <- errors.Trace(err) - break - } - if chk.NumRows() == 0 { - break - } - } - return errors.Trace(err) -} - -func (e *CheckTableExec) handlePanic(r interface{}) { - if r != nil { - e.retCh <- errors.Errorf("%v", r) - } -} - -// Next implements the Executor Next interface. -func (e *CheckTableExec) Next(ctx context.Context, _ *chunk.Chunk) error { - if e.done || len(e.srcs) == 0 { - return nil - } - defer func() { e.done = true }() - - idxNames := make([]string, 0, len(e.indexInfos)) - for _, idx := range e.indexInfos { - if idx.MVIndex { - continue - } - idxNames = append(idxNames, idx.Name.O) - } - greater, idxOffset, err := admin.CheckIndicesCount(e.Ctx(), e.dbName, e.table.Meta().Name.O, idxNames) - if err != nil { - // For admin check index statement, for speed up and compatibility, doesn't do below checks. - if e.checkIndex { - return errors.Trace(err) - } - if greater == admin.IdxCntGreater { - err = e.checkTableIndexHandle(ctx, e.indexInfos[idxOffset]) - } else if greater == admin.TblCntGreater { - err = e.checkTableRecord(ctx, idxOffset) - } - return errors.Trace(err) - } - - // The number of table rows is equal to the number of index rows. - // TODO: Make the value of concurrency adjustable. And we can consider the number of records. - if len(e.srcs) == 1 { - err = e.checkIndexHandle(ctx, e.srcs[0]) - if err == nil && e.srcs[0].index.MVIndex { - err = e.checkTableRecord(ctx, 0) - } - if err != nil { - return err - } - } - taskCh := make(chan *IndexLookUpExecutor, len(e.srcs)) - failure := atomicutil.NewBool(false) - concurrency := mathutil.Min(3, len(e.srcs)) - var wg util.WaitGroupWrapper - for _, src := range e.srcs { - taskCh <- src - } - for i := 0; i < concurrency; i++ { - wg.Run(func() { - util.WithRecovery(func() { - for { - if fail := failure.Load(); fail { - return - } - select { - case src := <-taskCh: - err1 := e.checkIndexHandle(ctx, src) - if err1 == nil && src.index.MVIndex { - for offset, idx := range e.indexInfos { - if idx.ID == src.index.ID { - err1 = e.checkTableRecord(ctx, offset) - break - } - } - } - if err1 != nil { - failure.Store(true) - logutil.Logger(ctx).Info("check index handle failed", zap.Error(err1)) - return - } - case <-e.exitCh: - return - default: - return - } - } - }, e.handlePanic) - }) - } - wg.Wait() - select { - case err := <-e.retCh: - return errors.Trace(err) - default: - return nil - } -} - -func (e *CheckTableExec) checkTableRecord(ctx context.Context, idxOffset int) error { - idxInfo := e.indexInfos[idxOffset] - txn, err := e.Ctx().Txn(true) - if err != nil { - return err - } - if e.table.Meta().GetPartitionInfo() == nil { - idx := tables.NewIndex(e.table.Meta().ID, e.table.Meta(), idxInfo) - return admin.CheckRecordAndIndex(ctx, e.Ctx(), txn, e.table, idx) - } - - info := e.table.Meta().GetPartitionInfo() - for _, def := range info.Definitions { - pid := def.ID - partition := e.table.(table.PartitionedTable).GetPartition(pid) - idx := tables.NewIndex(def.ID, e.table.Meta(), idxInfo) - if err := admin.CheckRecordAndIndex(ctx, e.Ctx(), txn, partition, idx); err != nil { - return errors.Trace(err) - } - } - return nil -} - -// ShowSlowExec represents the executor of showing the slow queries. -// It is build from the "admin show slow" statement: -// -// admin show slow top [internal | all] N -// admin show slow recent N -type ShowSlowExec struct { - exec.BaseExecutor - - ShowSlow *ast.ShowSlow - result []*domain.SlowQueryInfo - cursor int -} - -// Open implements the Executor Open interface. -func (e *ShowSlowExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - - dom := domain.GetDomain(e.Ctx()) - e.result = dom.ShowSlowQuery(e.ShowSlow) - return nil -} - -// Next implements the Executor Next interface. -func (e *ShowSlowExec) Next(_ context.Context, req *chunk.Chunk) error { - req.Reset() - if e.cursor >= len(e.result) { - return nil - } - - for e.cursor < len(e.result) && req.NumRows() < e.MaxChunkSize() { - slow := e.result[e.cursor] - req.AppendString(0, slow.SQL) - req.AppendTime(1, types.NewTime(types.FromGoTime(slow.Start), mysql.TypeTimestamp, types.MaxFsp)) - req.AppendDuration(2, types.Duration{Duration: slow.Duration, Fsp: types.MaxFsp}) - req.AppendString(3, slow.Detail.String()) - if slow.Succ { - req.AppendInt64(4, 1) - } else { - req.AppendInt64(4, 0) - } - req.AppendUint64(5, slow.ConnID) - req.AppendUint64(6, slow.TxnTS) - req.AppendString(7, slow.User) - req.AppendString(8, slow.DB) - req.AppendString(9, slow.TableIDs) - req.AppendString(10, slow.IndexNames) - if slow.Internal { - req.AppendInt64(11, 1) - } else { - req.AppendInt64(11, 0) - } - req.AppendString(12, slow.Digest) - req.AppendString(13, slow.SessAlias) - e.cursor++ - } - return nil -} - -// SelectLockExec represents a select lock executor. -// It is built from the "SELECT .. FOR UPDATE" or the "SELECT .. LOCK IN SHARE MODE" statement. -// For "SELECT .. FOR UPDATE" statement, it locks every row key from source Executor. -// After the execution, the keys are buffered in transaction, and will be sent to KV -// when doing commit. If there is any key already locked by another transaction, -// the transaction will rollback and retry. -type SelectLockExec struct { - exec.BaseExecutor - - Lock *ast.SelectLockInfo - keys []kv.Key - - // The children may be a join of multiple tables, so we need a map. - tblID2Handle map[int64][]plannercore.HandleCols - - // When SelectLock work on a partition table, we need the partition ID - // (Physical Table ID) instead of the 'logical' table ID to calculate - // the lock KV. In that case, the Physical Table ID is extracted - // from the row key in the store and as an extra column in the chunk row. - - // tblID2PhyTblIDCol is used for partitioned tables. - // The child executor need to return an extra column containing - // the Physical Table ID (i.e. from which partition the row came from) - // Used during building - tblID2PhysTblIDCol map[int64]*expression.Column - - // Used during execution - // Map from logic tableID to column index where the physical table id is stored - // For dynamic prune mode, model.ExtraPhysTblID columns are requested from - // storage and used for physical table id - // For static prune mode, model.ExtraPhysTblID is still sent to storage/Protobuf - // but could be filled in by the partitions TableReaderExecutor - // due to issues with chunk handling between the TableReaderExecutor and the - // SelectReader result. - tblID2PhysTblIDColIdx map[int64]int -} - -// Open implements the Executor Open interface. -func (e *SelectLockExec) Open(ctx context.Context) error { - if len(e.tblID2PhysTblIDCol) > 0 { - e.tblID2PhysTblIDColIdx = make(map[int64]int) - cols := e.Schema().Columns - for i := len(cols) - 1; i >= 0; i-- { - if cols[i].ID == model.ExtraPhysTblID { - for tblID, col := range e.tblID2PhysTblIDCol { - if cols[i].UniqueID == col.UniqueID { - e.tblID2PhysTblIDColIdx[tblID] = i - break - } - } - } - } - } - return e.BaseExecutor.Open(ctx) -} - -// Next implements the Executor Next interface. -func (e *SelectLockExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - err := exec.Next(ctx, e.Children(0), req) - if err != nil { - return err - } - // If there's no handle or it's not a `SELECT FOR UPDATE` statement. - if len(e.tblID2Handle) == 0 || (!plannercore.IsSelectForUpdateLockType(e.Lock.LockType)) { - return nil - } - - if req.NumRows() > 0 { - iter := chunk.NewIterator4Chunk(req) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - for tblID, cols := range e.tblID2Handle { - for _, col := range cols { - handle, err := col.BuildHandle(row) - if err != nil { - return err - } - physTblID := tblID - if physTblColIdx, ok := e.tblID2PhysTblIDColIdx[tblID]; ok { - physTblID = row.GetInt64(physTblColIdx) - if physTblID == 0 { - // select * from t1 left join t2 on t1.c = t2.c for update - // The join right side might be added NULL in left join - // In that case, physTblID is 0, so skip adding the lock. - // - // Note, we can't distinguish whether it's the left join case, - // or a bug that TiKV return without correct physical ID column. - continue - } - } - e.keys = append(e.keys, tablecodec.EncodeRowKeyWithHandle(physTblID, handle)) - } - } - } - return nil - } - lockWaitTime := e.Ctx().GetSessionVars().LockWaitTimeout - if e.Lock.LockType == ast.SelectLockForUpdateNoWait { - lockWaitTime = tikvstore.LockNoWait - } else if e.Lock.LockType == ast.SelectLockForUpdateWaitN { - lockWaitTime = int64(e.Lock.WaitSec) * 1000 - } - - for id := range e.tblID2Handle { - e.UpdateDeltaForTableID(id) - } - lockCtx, err := newLockCtx(e.Ctx(), lockWaitTime, len(e.keys)) - if err != nil { - return err - } - return doLockKeys(ctx, e.Ctx(), lockCtx, e.keys...) -} - -func newLockCtx(sctx sessionctx.Context, lockWaitTime int64, numKeys int) (*tikvstore.LockCtx, error) { - seVars := sctx.GetSessionVars() - forUpdateTS, err := sessiontxn.GetTxnManager(sctx).GetStmtForUpdateTS() - if err != nil { - return nil, err - } - lockCtx := tikvstore.NewLockCtx(forUpdateTS, lockWaitTime, seVars.StmtCtx.GetLockWaitStartTime()) - lockCtx.Killed = &seVars.Killed - lockCtx.PessimisticLockWaited = &seVars.StmtCtx.PessimisticLockWaited - lockCtx.LockKeysDuration = &seVars.StmtCtx.LockKeysDuration - lockCtx.LockKeysCount = &seVars.StmtCtx.LockKeysCount - lockCtx.LockExpired = &seVars.TxnCtx.LockExpire - lockCtx.ResourceGroupTagger = func(req *kvrpcpb.PessimisticLockRequest) []byte { - if req == nil { - return nil - } - if len(req.Mutations) == 0 { - return nil - } - if mutation := req.Mutations[0]; mutation != nil { - label := resourcegrouptag.GetResourceGroupLabelByKey(mutation.Key) - normalized, digest := seVars.StmtCtx.SQLDigest() - if len(normalized) == 0 { - return nil - } - _, planDigest := seVars.StmtCtx.GetPlanDigest() - return resourcegrouptag.EncodeResourceGroupTag(digest, planDigest, label) - } - return nil - } - lockCtx.OnDeadlock = func(deadlock *tikverr.ErrDeadlock) { - cfg := config.GetGlobalConfig() - if deadlock.IsRetryable && !cfg.PessimisticTxn.DeadlockHistoryCollectRetryable { - return - } - rec := deadlockhistory.ErrDeadlockToDeadlockRecord(deadlock) - deadlockhistory.GlobalDeadlockHistory.Push(rec) - } - if lockCtx.ForUpdateTS > 0 && seVars.AssertionLevel != variable.AssertionLevelOff { - lockCtx.InitCheckExistence(numKeys) - } - return lockCtx, nil -} - -// doLockKeys is the main entry for pessimistic lock keys -// waitTime means the lock operation will wait in milliseconds if target key is already -// locked by others. used for (select for update nowait) situation -func doLockKeys(ctx context.Context, se sessionctx.Context, lockCtx *tikvstore.LockCtx, keys ...kv.Key) error { - sessVars := se.GetSessionVars() - sctx := sessVars.StmtCtx - if !sctx.InUpdateStmt && !sctx.InDeleteStmt { - atomic.StoreUint32(&se.GetSessionVars().TxnCtx.ForUpdate, 1) - } - // Lock keys only once when finished fetching all results. - txn, err := se.Txn(true) - if err != nil { - return err - } - - // Skip the temporary table keys. - keys = filterTemporaryTableKeys(sessVars, keys) - - keys = filterLockTableKeys(sessVars.StmtCtx, keys) - var lockKeyStats *tikvutil.LockKeysDetails - ctx = context.WithValue(ctx, tikvutil.LockKeysDetailCtxKey, &lockKeyStats) - err = txn.LockKeys(tikvutil.SetSessionID(ctx, se.GetSessionVars().ConnectionID), lockCtx, keys...) - if lockKeyStats != nil { - sctx.MergeLockKeysExecDetails(lockKeyStats) - } - return err -} - -func filterTemporaryTableKeys(vars *variable.SessionVars, keys []kv.Key) []kv.Key { - txnCtx := vars.TxnCtx - if txnCtx == nil || txnCtx.TemporaryTables == nil { - return keys - } - - newKeys := keys[:0:len(keys)] - for _, key := range keys { - tblID := tablecodec.DecodeTableID(key) - if _, ok := txnCtx.TemporaryTables[tblID]; !ok { - newKeys = append(newKeys, key) - } - } - return newKeys -} - -func filterLockTableKeys(stmtCtx *stmtctx.StatementContext, keys []kv.Key) []kv.Key { - if len(stmtCtx.LockTableIDs) == 0 { - return keys - } - newKeys := keys[:0:len(keys)] - for _, key := range keys { - tblID := tablecodec.DecodeTableID(key) - if _, ok := stmtCtx.LockTableIDs[tblID]; ok { - newKeys = append(newKeys, key) - } - } - return newKeys -} - -// LimitExec represents limit executor -// It ignores 'Offset' rows from src, then returns 'Count' rows at maximum. -type LimitExec struct { - exec.BaseExecutor - - begin uint64 - end uint64 - cursor uint64 - - // meetFirstBatch represents whether we have met the first valid Chunk from child. - meetFirstBatch bool - - childResult *chunk.Chunk - - // columnIdxsUsedByChild keep column indexes of child executor used for inline projection - columnIdxsUsedByChild []int - - // Log the close time when opentracing is enabled. - span opentracing.Span -} - -// Next implements the Executor Next interface. -func (e *LimitExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.cursor >= e.end { - return nil - } - for !e.meetFirstBatch { - // transfer req's requiredRows to childResult and then adjust it in childResult - e.childResult = e.childResult.SetRequiredRows(req.RequiredRows(), e.MaxChunkSize()) - err := exec.Next(ctx, e.Children(0), e.adjustRequiredRows(e.childResult)) - if err != nil { - return err - } - batchSize := uint64(e.childResult.NumRows()) - // no more data. - if batchSize == 0 { - return nil - } - if newCursor := e.cursor + batchSize; newCursor >= e.begin { - e.meetFirstBatch = true - begin, end := e.begin-e.cursor, batchSize - if newCursor > e.end { - end = e.end - e.cursor - } - e.cursor += end - if begin == end { - break - } - if e.columnIdxsUsedByChild != nil { - req.Append(e.childResult.Prune(e.columnIdxsUsedByChild), int(begin), int(end)) - } else { - req.Append(e.childResult, int(begin), int(end)) - } - return nil - } - e.cursor += batchSize - } - e.childResult.Reset() - e.childResult = e.childResult.SetRequiredRows(req.RequiredRows(), e.MaxChunkSize()) - e.adjustRequiredRows(e.childResult) - err := exec.Next(ctx, e.Children(0), e.childResult) - if err != nil { - return err - } - batchSize := uint64(e.childResult.NumRows()) - // no more data. - if batchSize == 0 { - return nil - } - if e.cursor+batchSize > e.end { - e.childResult.TruncateTo(int(e.end - e.cursor)) - batchSize = e.end - e.cursor - } - e.cursor += batchSize - - if e.columnIdxsUsedByChild != nil { - for i, childIdx := range e.columnIdxsUsedByChild { - if err = req.SwapColumn(i, e.childResult, childIdx); err != nil { - return err - } - } - } else { - req.SwapColumns(e.childResult) - } - return nil -} - -// Open implements the Executor Open interface. -func (e *LimitExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - e.childResult = exec.TryNewCacheChunk(e.Children(0)) - e.cursor = 0 - e.meetFirstBatch = e.begin == 0 - if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { - e.span = span - } - return nil -} - -// Close implements the Executor Close interface. -func (e *LimitExec) Close() error { - start := time.Now() - - e.childResult = nil - err := e.BaseExecutor.Close() - - elapsed := time.Since(start) - if elapsed > time.Millisecond { - logutil.BgLogger().Info("limit executor close takes a long time", - zap.Duration("elapsed", elapsed)) - if e.span != nil { - span1 := e.span.Tracer().StartSpan("limitExec.Close", opentracing.ChildOf(e.span.Context()), opentracing.StartTime(start)) - defer span1.Finish() - } - } - return err -} - -func (e *LimitExec) adjustRequiredRows(chk *chunk.Chunk) *chunk.Chunk { - // the limit of maximum number of rows the LimitExec should read - limitTotal := int(e.end - e.cursor) - - var limitRequired int - if e.cursor < e.begin { - // if cursor is less than begin, it have to read (begin-cursor) rows to ignore - // and then read chk.RequiredRows() rows to return, - // so the limit is (begin-cursor)+chk.RequiredRows(). - limitRequired = int(e.begin) - int(e.cursor) + chk.RequiredRows() - } else { - // if cursor is equal or larger than begin, just read chk.RequiredRows() rows to return. - limitRequired = chk.RequiredRows() - } - - return chk.SetRequiredRows(mathutil.Min(limitTotal, limitRequired), e.MaxChunkSize()) -} - -func init() { - // While doing optimization in the plan package, we need to execute uncorrelated subquery, - // but the plan package cannot import the executor package because of the dependency cycle. - // So we assign a function implemented in the executor package to the plan package to avoid the dependency cycle. - plannercore.EvalSubqueryFirstRow = func(ctx context.Context, p plannercore.PhysicalPlan, is infoschema.InfoSchema, sctx sessionctx.Context) ([]types.Datum, error) { - defer func(begin time.Time) { - s := sctx.GetSessionVars() - s.StmtCtx.SetSkipPlanCache(errors.New("query has uncorrelated sub-queries is un-cacheable")) - s.RewritePhaseInfo.PreprocessSubQueries++ - s.RewritePhaseInfo.DurationPreprocessSubQuery += time.Since(begin) - }(time.Now()) - - r, ctx := tracing.StartRegionEx(ctx, "executor.EvalSubQuery") - defer r.End() - - e := newExecutorBuilder(sctx, is, nil) - executor := e.build(p) - if e.err != nil { - return nil, e.err - } - err := executor.Open(ctx) - defer terror.Call(executor.Close) - if err != nil { - return nil, err - } - if pi, ok := sctx.(processinfoSetter); ok { - // Before executing the sub-query, we need update the processinfo to make the progress bar more accurate. - // because the sub-query may take a long time. - pi.UpdateProcessInfo() - } - chk := exec.TryNewCacheChunk(executor) - err = exec.Next(ctx, executor, chk) - if err != nil { - return nil, err - } - if chk.NumRows() == 0 { - return nil, nil - } - row := chk.GetRow(0).GetDatumRow(exec.RetTypes(executor)) - return row, err - } -} - -// TableDualExec represents a dual table executor. -type TableDualExec struct { - exec.BaseExecutor - - // numDualRows can only be 0 or 1. - numDualRows int - numReturned int -} - -// Open implements the Executor Open interface. -func (e *TableDualExec) Open(context.Context) error { - e.numReturned = 0 - return nil -} - -// Next implements the Executor Next interface. -func (e *TableDualExec) Next(_ context.Context, req *chunk.Chunk) error { - req.Reset() - if e.numReturned >= e.numDualRows { - return nil - } - if e.Schema().Len() == 0 { - req.SetNumVirtualRows(1) - } else { - for i := range e.Schema().Columns { - req.AppendNull(i) - } - } - e.numReturned = e.numDualRows - return nil -} - -// SelectionExec represents a filter executor. -type SelectionExec struct { - exec.BaseExecutor - - batched bool - filters []expression.Expression - selected []bool - inputIter *chunk.Iterator4Chunk - inputRow chunk.Row - childResult *chunk.Chunk - - memTracker *memory.Tracker -} - -// Open implements the Executor Open interface. -func (e *SelectionExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - failpoint.Inject("mockSelectionExecBaseExecutorOpenReturnedError", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(errors.New("mock SelectionExec.baseExecutor.Open returned error")) - } - }) - return e.open(ctx) -} - -func (e *SelectionExec) open(context.Context) error { - if e.memTracker != nil { - e.memTracker.Reset() - } else { - e.memTracker = memory.NewTracker(e.ID(), -1) - } - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - e.childResult = exec.TryNewCacheChunk(e.Children(0)) - e.memTracker.Consume(e.childResult.MemoryUsage()) - e.batched = expression.Vectorizable(e.filters) - if e.batched { - e.selected = make([]bool, 0, chunk.InitialCapacity) - } - e.inputIter = chunk.NewIterator4Chunk(e.childResult) - e.inputRow = e.inputIter.End() - return nil -} - -// Close implements plannercore.Plan Close interface. -func (e *SelectionExec) Close() error { - if e.childResult != nil { - e.memTracker.Consume(-e.childResult.MemoryUsage()) - e.childResult = nil - } - e.selected = nil - return e.BaseExecutor.Close() -} - -// Next implements the Executor Next interface. -func (e *SelectionExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - - if !e.batched { - return e.unBatchedNext(ctx, req) - } - - for { - for ; e.inputRow != e.inputIter.End(); e.inputRow = e.inputIter.Next() { - if req.IsFull() { - return nil - } - - if !e.selected[e.inputRow.Idx()] { - continue - } - - req.AppendRow(e.inputRow) - } - mSize := e.childResult.MemoryUsage() - err := exec.Next(ctx, e.Children(0), e.childResult) - e.memTracker.Consume(e.childResult.MemoryUsage() - mSize) - if err != nil { - return err - } - // no more data. - if e.childResult.NumRows() == 0 { - return nil - } - e.selected, err = expression.VectorizedFilter(e.Ctx(), e.filters, e.inputIter, e.selected) - if err != nil { - return err - } - e.inputRow = e.inputIter.Begin() - } -} - -// unBatchedNext filters input rows one by one and returns once an input row is selected. -// For sql with "SETVAR" in filter and "GETVAR" in projection, for example: "SELECT @a FROM t WHERE (@a := 2) > 0", -// we have to set batch size to 1 to do the evaluation of filter and projection. -func (e *SelectionExec) unBatchedNext(ctx context.Context, chk *chunk.Chunk) error { - for { - for ; e.inputRow != e.inputIter.End(); e.inputRow = e.inputIter.Next() { - selected, _, err := expression.EvalBool(e.Ctx(), e.filters, e.inputRow) - if err != nil { - return err - } - if selected { - chk.AppendRow(e.inputRow) - e.inputRow = e.inputIter.Next() - return nil - } - } - mSize := e.childResult.MemoryUsage() - err := exec.Next(ctx, e.Children(0), e.childResult) - e.memTracker.Consume(e.childResult.MemoryUsage() - mSize) - if err != nil { - return err - } - e.inputRow = e.inputIter.Begin() - // no more data. - if e.childResult.NumRows() == 0 { - return nil - } - } -} - -// TableScanExec is a table scan executor without result fields. -type TableScanExec struct { - exec.BaseExecutor - - t table.Table - columns []*model.ColumnInfo - virtualTableChunkList *chunk.List - virtualTableChunkIdx int -} - -// Next implements the Executor Next interface. -func (e *TableScanExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - return e.nextChunk4InfoSchema(ctx, req) -} - -func (e *TableScanExec) nextChunk4InfoSchema(ctx context.Context, chk *chunk.Chunk) error { - chk.GrowAndReset(e.MaxChunkSize()) - if e.virtualTableChunkList == nil { - e.virtualTableChunkList = chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) - columns := make([]*table.Column, e.Schema().Len()) - for i, colInfo := range e.columns { - columns[i] = table.ToColumn(colInfo) - } - mutableRow := chunk.MutRowFromTypes(exec.RetTypes(e)) - type tableIter interface { - IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error - } - err := (e.t.(tableIter)).IterRecords(ctx, e.Ctx(), columns, func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { - mutableRow.SetDatums(rec...) - e.virtualTableChunkList.AppendRow(mutableRow.ToRow()) - return true, nil - }) - if err != nil { - return err - } - } - // no more data. - if e.virtualTableChunkIdx >= e.virtualTableChunkList.NumChunks() { - return nil - } - virtualTableChunk := e.virtualTableChunkList.GetChunk(e.virtualTableChunkIdx) - e.virtualTableChunkIdx++ - chk.SwapColumns(virtualTableChunk) - return nil -} - -// Open implements the Executor Open interface. -func (e *TableScanExec) Open(context.Context) error { - e.virtualTableChunkList = nil - return nil -} - -// MaxOneRowExec checks if the number of rows that a query returns is at maximum one. -// It's built from subquery expression. -type MaxOneRowExec struct { - exec.BaseExecutor - - evaluated bool -} - -// Open implements the Executor Open interface. -func (e *MaxOneRowExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - e.evaluated = false - return nil -} - -// Next implements the Executor Next interface. -func (e *MaxOneRowExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.evaluated { - return nil - } - e.evaluated = true - err := exec.Next(ctx, e.Children(0), req) - if err != nil { - return err - } - - if num := req.NumRows(); num == 0 { - for i := range e.Schema().Columns { - req.AppendNull(i) - } - return nil - } else if num != 1 { - return exeerrors.ErrSubqueryMoreThan1Row - } - - childChunk := exec.TryNewCacheChunk(e.Children(0)) - err = exec.Next(ctx, e.Children(0), childChunk) - if err != nil { - return err - } - if childChunk.NumRows() != 0 { - return exeerrors.ErrSubqueryMoreThan1Row - } - - return nil -} - -// UnionExec pulls all it's children's result and returns to its parent directly. -// A "resultPuller" is started for every child to pull result from that child and push it to the "resultPool", the used -// "Chunk" is obtained from the corresponding "resourcePool". All resultPullers are running concurrently. -// -// +----------------+ -// +---> resourcePool 1 ---> | resultPuller 1 |-----+ -// | +----------------+ | -// | | -// | +----------------+ v -// +---> resourcePool 2 ---> | resultPuller 2 |-----> resultPool ---+ -// | +----------------+ ^ | -// | ...... | | -// | +----------------+ | | -// +---> resourcePool n ---> | resultPuller n |-----+ | -// | +----------------+ | -// | | -// | +-------------+ | -// |--------------------------| main thread | <---------------------+ -// +-------------+ -type UnionExec struct { - exec.BaseExecutor - concurrency int - childIDChan chan int - - stopFetchData atomic.Value - - finished chan struct{} - resourcePools []chan *chunk.Chunk - resultPool chan *unionWorkerResult - - results []*chunk.Chunk - wg sync.WaitGroup - initialized bool - mu struct { - *syncutil.Mutex - maxOpenedChildID int - } - - childInFlightForTest int32 -} - -// unionWorkerResult stores the result for a union worker. -// A "resultPuller" is started for every child to pull result from that child, unionWorkerResult is used to store that pulled result. -// "src" is used for Chunk reuse: after pulling result from "resultPool", main-thread must push a valid unused Chunk to "src" to -// enable the corresponding "resultPuller" continue to work. -type unionWorkerResult struct { - chk *chunk.Chunk - err error - src chan<- *chunk.Chunk -} - -func (e *UnionExec) waitAllFinished() { - e.wg.Wait() - close(e.resultPool) -} - -// Open implements the Executor Open interface. -func (e *UnionExec) Open(context.Context) error { - e.stopFetchData.Store(false) - e.initialized = false - e.finished = make(chan struct{}) - e.mu.Mutex = &syncutil.Mutex{} - e.mu.maxOpenedChildID = -1 - return nil -} - -func (e *UnionExec) initialize(ctx context.Context) { - if e.concurrency > e.ChildrenLen() { - e.concurrency = e.ChildrenLen() - } - for i := 0; i < e.concurrency; i++ { - e.results = append(e.results, exec.NewFirstChunk(e.Children(0))) - } - e.resultPool = make(chan *unionWorkerResult, e.concurrency) - e.resourcePools = make([]chan *chunk.Chunk, e.concurrency) - e.childIDChan = make(chan int, e.ChildrenLen()) - for i := 0; i < e.concurrency; i++ { - e.resourcePools[i] = make(chan *chunk.Chunk, 1) - e.resourcePools[i] <- e.results[i] - e.wg.Add(1) - go e.resultPuller(ctx, i) - } - for i := 0; i < e.ChildrenLen(); i++ { - e.childIDChan <- i - } - close(e.childIDChan) - go e.waitAllFinished() -} - -func (e *UnionExec) resultPuller(ctx context.Context, workerID int) { - result := &unionWorkerResult{ - err: nil, - chk: nil, - src: e.resourcePools[workerID], - } - defer func() { - if r := recover(); r != nil { - logutil.Logger(ctx).Error("resultPuller panicked", zap.Any("recover", r), zap.Stack("stack")) - result.err = errors.Errorf("%v", r) - e.resultPool <- result - e.stopFetchData.Store(true) - } - e.wg.Done() - }() - for childID := range e.childIDChan { - e.mu.Lock() - if childID > e.mu.maxOpenedChildID { - e.mu.maxOpenedChildID = childID - } - e.mu.Unlock() - if err := e.Children(childID).Open(ctx); err != nil { - result.err = err - e.stopFetchData.Store(true) - e.resultPool <- result - } - failpoint.Inject("issue21441", func() { - atomic.AddInt32(&e.childInFlightForTest, 1) - }) - for { - if e.stopFetchData.Load().(bool) { - return - } - select { - case <-e.finished: - return - case result.chk = <-e.resourcePools[workerID]: - } - result.err = exec.Next(ctx, e.Children(childID), result.chk) - if result.err == nil && result.chk.NumRows() == 0 { - e.resourcePools[workerID] <- result.chk - break - } - failpoint.Inject("issue21441", func() { - if int(atomic.LoadInt32(&e.childInFlightForTest)) > e.concurrency { - panic("the count of child in flight is larger than e.concurrency unexpectedly") - } - }) - e.resultPool <- result - if result.err != nil { - e.stopFetchData.Store(true) - return - } - } - failpoint.Inject("issue21441", func() { - atomic.AddInt32(&e.childInFlightForTest, -1) - }) - } -} - -// Next implements the Executor Next interface. -func (e *UnionExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if !e.initialized { - e.initialize(ctx) - e.initialized = true - } - result, ok := <-e.resultPool - if !ok { - return nil - } - if result.err != nil { - return errors.Trace(result.err) - } - - if result.chk.NumCols() != req.NumCols() { - return errors.Errorf("Internal error: UnionExec chunk column count mismatch, req: %d, result: %d", - req.NumCols(), result.chk.NumCols()) - } - req.SwapColumns(result.chk) - result.src <- result.chk - return nil -} - -// Close implements the Executor Close interface. -func (e *UnionExec) Close() error { - if e.finished != nil { - close(e.finished) - } - e.results = nil - if e.resultPool != nil { - channel.Clear(e.resultPool) - } - e.resourcePools = nil - if e.childIDChan != nil { - channel.Clear(e.childIDChan) - } - // We do not need to acquire the e.mu.Lock since all the resultPuller can be - // promised to exit when reaching here (e.childIDChan been closed). - var firstErr error - for i := 0; i <= e.mu.maxOpenedChildID; i++ { - if err := e.Children(i).Close(); err != nil && firstErr == nil { - firstErr = err - } - } - return firstErr -} - -// ResetContextOfStmt resets the StmtContext and session variables. -// Before every execution, we must clear statement context. -func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { - vars := ctx.GetSessionVars() - for name, val := range vars.StmtCtx.SetVarHintRestore { - err := vars.SetSystemVar(name, val) - if err != nil { - logutil.BgLogger().Warn("Failed to restore the variable after SET_VAR hint", zap.String("variable name", name), zap.String("expected value", val)) - } - } - vars.StmtCtx.SetVarHintRestore = nil - var sc *stmtctx.StatementContext - if vars.TxnCtx.CouldRetry || mysql.HasCursorExistsFlag(vars.Status) { - // Must construct new statement context object, the retry history need context for every statement. - // TODO: Maybe one day we can get rid of transaction retry, then this logic can be deleted. - sc = stmtctx.NewStmtCtx() - } else { - sc = vars.InitStatementContext() - } - sc.SetTimeZone(vars.Location()) - sc.TaskID = stmtctx.AllocateTaskID() - sc.CTEStorageMap = map[int]*CTEStorages{} - sc.IsStaleness = false - sc.LockTableIDs = make(map[int64]struct{}) - sc.EnableOptimizeTrace = false - sc.OptimizeTracer = nil - sc.OptimizerCETrace = nil - sc.IsSyncStatsFailed = false - sc.IsExplainAnalyzeDML = false - // Firstly we assume that UseDynamicPruneMode can be enabled according session variable, then we will check other conditions - // in PlanBuilder.buildDataSource - if ctx.GetSessionVars().IsDynamicPartitionPruneEnabled() { - sc.UseDynamicPruneMode = true - } else { - sc.UseDynamicPruneMode = false - } - - sc.StatsLoad.Timeout = 0 - sc.StatsLoad.NeededItems = nil - sc.StatsLoad.ResultCh = nil - - sc.SysdateIsNow = ctx.GetSessionVars().SysdateIsNow - - vars.MemTracker.Detach() - vars.MemTracker.UnbindActions() - vars.MemTracker.SetBytesLimit(vars.MemQuotaQuery) - vars.MemTracker.ResetMaxConsumed() - vars.DiskTracker.Detach() - vars.DiskTracker.ResetMaxConsumed() - vars.MemTracker.SessionID.Store(vars.ConnectionID) - vars.StmtCtx.TableStats = make(map[int64]interface{}) - - isAnalyze := false - if execStmt, ok := s.(*ast.ExecuteStmt); ok { - prepareStmt, err := plannercore.GetPreparedStmt(execStmt, vars) - if err != nil { - return err - } - _, isAnalyze = prepareStmt.PreparedAst.Stmt.(*ast.AnalyzeTableStmt) - } else if _, ok := s.(*ast.AnalyzeTableStmt); ok { - isAnalyze = true - } - if isAnalyze { - sc.InitMemTracker(memory.LabelForAnalyzeMemory, -1) - vars.MemTracker.SetBytesLimit(-1) - vars.MemTracker.AttachTo(GlobalAnalyzeMemoryTracker) - } else { - sc.InitMemTracker(memory.LabelForSQLText, -1) - } - logOnQueryExceedMemQuota := domain.GetDomain(ctx).ExpensiveQueryHandle().LogOnQueryExceedMemQuota - switch variable.OOMAction.Load() { - case variable.OOMActionCancel: - action := &memory.PanicOnExceed{ConnID: vars.ConnectionID} - action.SetLogHook(logOnQueryExceedMemQuota) - vars.MemTracker.SetActionOnExceed(action) - case variable.OOMActionLog: - fallthrough - default: - action := &memory.LogOnExceed{ConnID: vars.ConnectionID} - action.SetLogHook(logOnQueryExceedMemQuota) - vars.MemTracker.SetActionOnExceed(action) - } - sc.MemTracker.SessionID.Store(vars.ConnectionID) - sc.MemTracker.AttachTo(vars.MemTracker) - sc.InitDiskTracker(memory.LabelForSQLText, -1) - globalConfig := config.GetGlobalConfig() - if variable.EnableTmpStorageOnOOM.Load() && sc.DiskTracker != nil { - sc.DiskTracker.AttachTo(vars.DiskTracker) - if GlobalDiskUsageTracker != nil { - vars.DiskTracker.AttachTo(GlobalDiskUsageTracker) - } - } - if execStmt, ok := s.(*ast.ExecuteStmt); ok { - prepareStmt, err := plannercore.GetPreparedStmt(execStmt, vars) - if err != nil { - return err - } - s = prepareStmt.PreparedAst.Stmt - sc.InitSQLDigest(prepareStmt.NormalizedSQL, prepareStmt.SQLDigest) - // For `execute stmt` SQL, should reset the SQL digest with the prepare SQL digest. - goCtx := context.Background() - if variable.EnablePProfSQLCPU.Load() && len(prepareStmt.NormalizedSQL) > 0 { - goCtx = pprof.WithLabels(goCtx, pprof.Labels("sql", FormatSQL(prepareStmt.NormalizedSQL).String())) - pprof.SetGoroutineLabels(goCtx) - } - if topsqlstate.TopSQLEnabled() && prepareStmt.SQLDigest != nil { - sc.IsSQLRegistered.Store(true) - topsql.AttachAndRegisterSQLInfo(goCtx, prepareStmt.NormalizedSQL, prepareStmt.SQLDigest, vars.InRestrictedSQL) - } - if s, ok := prepareStmt.PreparedAst.Stmt.(*ast.SelectStmt); ok { - if s.LockInfo == nil { - sc.WeakConsistency = isWeakConsistencyRead(ctx, execStmt) - } - } - } - // execute missed stmtID uses empty sql - sc.OriginalSQL = s.Text() - if explainStmt, ok := s.(*ast.ExplainStmt); ok { - sc.InExplainStmt = true - sc.ExplainFormat = explainStmt.Format - sc.InExplainAnalyzeStmt = explainStmt.Analyze - sc.IgnoreExplainIDSuffix = strings.ToLower(explainStmt.Format) == types.ExplainFormatBrief - sc.InVerboseExplain = strings.ToLower(explainStmt.Format) == types.ExplainFormatVerbose - s = explainStmt.Stmt - } else { - sc.ExplainFormat = "" - } - if explainForStmt, ok := s.(*ast.ExplainForStmt); ok { - sc.InExplainStmt = true - sc.InExplainAnalyzeStmt = true - sc.InVerboseExplain = strings.ToLower(explainForStmt.Format) == types.ExplainFormatVerbose - } - - // TODO: Many same bool variables here. - // We should set only two variables ( - // IgnoreErr and StrictSQLMode) to avoid setting the same bool variables and - // pushing them down to TiKV as flags. - - sc.InRestrictedSQL = vars.InRestrictedSQL - switch stmt := s.(type) { - case *ast.UpdateStmt: - ResetUpdateStmtCtx(sc, stmt, vars) - case *ast.DeleteStmt: - ResetDeleteStmtCtx(sc, stmt, vars) - case *ast.InsertStmt: - sc.InInsertStmt = true - // For insert statement (not for update statement), disabling the StrictSQLMode - // should make TruncateAsWarning and DividedByZeroAsWarning, - // but should not make DupKeyAsWarning. - sc.DupKeyAsWarning = stmt.IgnoreErr - sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.IgnoreNoPartition = stmt.IgnoreErr - sc.ErrAutoincReadFailedAsWarning = stmt.IgnoreErr - sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate - sc.Priority = stmt.Priority - case *ast.CreateTableStmt, *ast.AlterTableStmt: - sc.InCreateOrAlterStmt = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.StrictSQLMode || sc.AllowInvalidDate - sc.NoZeroDate = vars.SQLMode.HasNoZeroDateMode() - sc.TruncateAsWarning = !vars.StrictSQLMode - case *ast.LoadDataStmt: - sc.InLoadDataStmt = true - // return warning instead of error when load data meet no partition for value - sc.IgnoreNoPartition = true - case *ast.SelectStmt: - sc.InSelectStmt = true - - // see https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict - // said "For statements such as SELECT that do not change data, invalid values - // generate a warning in strict mode, not an error." - // and https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html - sc.OverflowAsWarning = true - - // Return warning for truncate error in selection. - sc.TruncateAsWarning = true - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - if opts := stmt.SelectStmtOpts; opts != nil { - sc.Priority = opts.Priority - sc.NotFillCache = !opts.SQLCache - } - sc.WeakConsistency = isWeakConsistencyRead(ctx, stmt) - case *ast.SetOprStmt: - sc.InSelectStmt = true - sc.OverflowAsWarning = true - sc.TruncateAsWarning = true - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - case *ast.ShowStmt: - sc.IgnoreTruncate.Store(true) - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - if stmt.Tp == ast.ShowWarnings || stmt.Tp == ast.ShowErrors || stmt.Tp == ast.ShowSessionStates { - sc.InShowWarning = true - sc.SetWarnings(vars.StmtCtx.GetWarnings()) - } - case *ast.SplitRegionStmt: - sc.IgnoreTruncate.Store(false) - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - case *ast.SetSessionStatesStmt: - sc.InSetSessionStatesStmt = true - sc.IgnoreTruncate.Store(true) - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - default: - sc.IgnoreTruncate.Store(true) - sc.IgnoreZeroInDate = true - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - } - - sc.UpdateTypeFlags(func(flags types.Flags) types.Flags { - return flags. - WithSkipUTF8Check(vars.SkipUTF8Check). - WithSkipSACIICheck(vars.SkipASCIICheck). - WithSkipUTF8MB4Check(!globalConfig.Instance.CheckMb4ValueInUTF8.Load()) - }) - - vars.PlanCacheParams.Reset() - if priority := mysql.PriorityEnum(atomic.LoadInt32(&variable.ForcePriority)); priority != mysql.NoPriority { - sc.Priority = priority - } - if vars.StmtCtx.LastInsertID > 0 { - sc.PrevLastInsertID = vars.StmtCtx.LastInsertID - } else { - sc.PrevLastInsertID = vars.StmtCtx.PrevLastInsertID - } - sc.PrevAffectedRows = 0 - if vars.StmtCtx.InUpdateStmt || vars.StmtCtx.InDeleteStmt || vars.StmtCtx.InInsertStmt || vars.StmtCtx.InSetSessionStatesStmt { - sc.PrevAffectedRows = int64(vars.StmtCtx.AffectedRows()) - } else if vars.StmtCtx.InSelectStmt { - sc.PrevAffectedRows = -1 - } - if globalConfig.Instance.EnableCollectExecutionInfo.Load() { - // In ExplainFor case, RuntimeStatsColl should not be reset for reuse, - // because ExplainFor need to display the last statement information. - reuseObj := vars.StmtCtx.RuntimeStatsColl - if _, ok := s.(*ast.ExplainForStmt); ok { - reuseObj = nil - } - sc.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(reuseObj) - } - - sc.TblInfo2UnionScan = make(map[*model.TableInfo]bool) - errCount, warnCount := vars.StmtCtx.NumErrorWarnings() - vars.SysErrorCount = errCount - vars.SysWarningCount = warnCount - vars.ExchangeChunkStatus() - vars.StmtCtx = sc - vars.PrevFoundInPlanCache = vars.FoundInPlanCache - vars.FoundInPlanCache = false - vars.ClearStmtVars() - vars.PrevFoundInBinding = vars.FoundInBinding - vars.FoundInBinding = false - vars.DurationWaitTS = 0 - vars.CurrInsertBatchExtraCols = nil - vars.CurrInsertValues = chunk.Row{} - - return -} - -// ResetUpdateStmtCtx resets statement context for UpdateStmt. -func ResetUpdateStmtCtx(sc *stmtctx.StatementContext, stmt *ast.UpdateStmt, vars *variable.SessionVars) { - sc.InUpdateStmt = true - sc.DupKeyAsWarning = stmt.IgnoreErr - sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate - sc.Priority = stmt.Priority - sc.IgnoreNoPartition = stmt.IgnoreErr -} - -// ResetDeleteStmtCtx resets statement context for DeleteStmt. -func ResetDeleteStmtCtx(sc *stmtctx.StatementContext, stmt *ast.DeleteStmt, vars *variable.SessionVars) { - sc.InDeleteStmt = true - sc.DupKeyAsWarning = stmt.IgnoreErr - sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr - sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() - sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate - sc.Priority = stmt.Priority -} - -func setOptionForTopSQL(sc *stmtctx.StatementContext, snapshot kv.Snapshot) { - if snapshot == nil { - return - } - snapshot.SetOption(kv.ResourceGroupTagger, sc.GetResourceGroupTagger()) - if sc.KvExecCounter != nil { - snapshot.SetOption(kv.RPCInterceptor, sc.KvExecCounter.RPCInterceptor()) - } -} - -func isWeakConsistencyRead(ctx sessionctx.Context, node ast.Node) bool { - sessionVars := ctx.GetSessionVars() - return sessionVars.ConnectionID > 0 && sessionVars.ReadConsistency.IsWeak() && - plannercore.IsAutoCommitTxn(ctx) && plannercore.IsReadOnly(node, sessionVars) -} - -// FastCheckTableExec represents a check table executor. -// It is built from the "admin check table" statement, and it checks if the -// index matches the records in the table. -// It uses a new algorithms to check table data, which is faster than the old one(CheckTableExec). -type FastCheckTableExec struct { - exec.BaseExecutor - - dbName string - table table.Table - indexInfos []*model.IndexInfo - done bool - is infoschema.InfoSchema - err *atomic.Pointer[error] - wg sync.WaitGroup - contextCtx context.Context -} - -// Open implements the Executor Open interface. -func (e *FastCheckTableExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - return err - } - - e.done = false - e.contextCtx = ctx - return nil -} - -type checkIndexTask struct { - indexOffset int -} - -type checkIndexWorker struct { - sctx sessionctx.Context - dbName string - table table.Table - indexInfos []*model.IndexInfo - e *FastCheckTableExec -} - -type groupByChecksum struct { - bucket uint64 - checksum uint64 - count int64 -} - -func getCheckSum(ctx context.Context, se sessionctx.Context, sql string) ([]groupByChecksum, error) { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) - rs, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) - if err != nil { - return nil, err - } - defer func(rs sqlexec.RecordSet) { - err := rs.Close() - if err != nil { - logutil.BgLogger().Error("close record set failed", zap.Error(err)) - } - }(rs) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 256) - if err != nil { - return nil, err - } - checksums := make([]groupByChecksum, 0, len(rows)) - for _, row := range rows { - checksums = append(checksums, groupByChecksum{bucket: row.GetUint64(1), checksum: row.GetUint64(0), count: row.GetInt64(2)}) - } - return checksums, nil -} - -// HandleTask implements the Worker interface. -func (w *checkIndexWorker) HandleTask(task checkIndexTask, _ func(workerpool.None)) { - defer w.e.wg.Done() - idxInfo := w.indexInfos[task.indexOffset] - bucketSize := int(CheckTableFastBucketSize.Load()) - - ctx := kv.WithInternalSourceType(w.e.contextCtx, kv.InternalTxnAdmin) - - trySaveErr := func(err error) { - w.e.err.CompareAndSwap(nil, &err) - } - - se, err := w.e.Base().GetSysSession() - if err != nil { - trySaveErr(err) - return - } - se.GetSessionVars().OptimizerUseInvisibleIndexes = true - defer func() { - se.GetSessionVars().OptimizerUseInvisibleIndexes = false - w.e.Base().ReleaseSysSession(ctx, se) - }() - - var pkCols []string - var pkTypes []*types.FieldType - switch { - case w.e.table.Meta().IsCommonHandle: - pkColsInfo := w.e.table.Meta().GetPrimaryKey().Columns - for _, colInfo := range pkColsInfo { - colStr := colInfo.Name.O - pkCols = append(pkCols, colStr) - pkTypes = append(pkTypes, &w.e.table.Meta().Columns[colInfo.Offset].FieldType) - } - case w.e.table.Meta().PKIsHandle: - pkCols = append(pkCols, w.e.table.Meta().GetPkName().O) - default: // support decoding _tidb_rowid. - pkCols = append(pkCols, model.ExtraHandleName.O) - } - - // CheckSum of (handle + index columns). - var md5HandleAndIndexCol strings.Builder - md5HandleAndIndexCol.WriteString("crc32(md5(concat_ws(0x2, ") - for _, col := range pkCols { - md5HandleAndIndexCol.WriteString(ColumnName(col)) - md5HandleAndIndexCol.WriteString(", ") - } - for offset, col := range idxInfo.Columns { - tblCol := w.table.Meta().Columns[col.Offset] - if tblCol.IsGenerated() && !tblCol.GeneratedStored { - md5HandleAndIndexCol.WriteString(tblCol.GeneratedExprString) - } else { - md5HandleAndIndexCol.WriteString(ColumnName(col.Name.O)) - } - if offset != len(idxInfo.Columns)-1 { - md5HandleAndIndexCol.WriteString(", ") - } - } - md5HandleAndIndexCol.WriteString(")))") - - // Used to group by and order. - var md5Handle strings.Builder - md5Handle.WriteString("crc32(md5(concat_ws(0x2, ") - for i, col := range pkCols { - md5Handle.WriteString(ColumnName(col)) - if i != len(pkCols)-1 { - md5Handle.WriteString(", ") - } - } - md5Handle.WriteString(")))") - - handleColumnField := strings.Join(pkCols, ", ") - var indexColumnField strings.Builder - for offset, col := range idxInfo.Columns { - indexColumnField.WriteString(ColumnName(col.Name.O)) - if offset != len(idxInfo.Columns)-1 { - indexColumnField.WriteString(", ") - } - } - - tableRowCntToCheck := int64(0) - - offset := 0 - mod := 1 - meetError := false - - lookupCheckThreshold := int64(100) - checkOnce := false - - if w.e.Ctx().GetSessionVars().SnapshotTS != 0 { - se.GetSessionVars().SnapshotTS = w.e.Ctx().GetSessionVars().SnapshotTS - defer func() { - se.GetSessionVars().SnapshotTS = 0 - }() - } - _, err = se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "begin") - if err != nil { - trySaveErr(err) - return - } - - times := 0 - const maxTimes = 10 - for tableRowCntToCheck > lookupCheckThreshold || !checkOnce { - times++ - if times == maxTimes { - logutil.BgLogger().Warn("compare checksum by group reaches time limit", zap.Int("times", times)) - break - } - whereKey := fmt.Sprintf("((cast(%s as signed) - %d) %% %d)", md5Handle.String(), offset, mod) - groupByKey := fmt.Sprintf("((cast(%s as signed) - %d) div %d %% %d)", md5Handle.String(), offset, mod, bucketSize) - if !checkOnce { - whereKey = "0" - } - checkOnce = true - - tblQuery := fmt.Sprintf("select /*+ read_from_storage(tikv[%s]) */ bit_xor(%s), %s, count(*) from %s use index() where %s = 0 group by %s", TableName(w.e.dbName, w.e.table.Meta().Name.String()), md5HandleAndIndexCol.String(), groupByKey, TableName(w.e.dbName, w.e.table.Meta().Name.String()), whereKey, groupByKey) - idxQuery := fmt.Sprintf("select bit_xor(%s), %s, count(*) from %s use index(`%s`) where %s = 0 group by %s", md5HandleAndIndexCol.String(), groupByKey, TableName(w.e.dbName, w.e.table.Meta().Name.String()), idxInfo.Name, whereKey, groupByKey) - - logutil.BgLogger().Info("fast check table by group", zap.String("table name", w.table.Meta().Name.String()), zap.String("index name", idxInfo.Name.String()), zap.Int("times", times), zap.Int("current offset", offset), zap.Int("current mod", mod), zap.String("table sql", tblQuery), zap.String("index sql", idxQuery)) - - // compute table side checksum. - tableChecksum, err := getCheckSum(w.e.contextCtx, se, tblQuery) - if err != nil { - trySaveErr(err) - return - } - slices.SortFunc(tableChecksum, func(i, j groupByChecksum) int { - return cmp.Compare(i.bucket, j.bucket) - }) - - // compute index side checksum. - indexChecksum, err := getCheckSum(w.e.contextCtx, se, idxQuery) - if err != nil { - trySaveErr(err) - return - } - slices.SortFunc(indexChecksum, func(i, j groupByChecksum) int { - return cmp.Compare(i.bucket, j.bucket) - }) - - currentOffset := 0 - - // Every checksum in table side should be the same as the index side. - i := 0 - for i < len(tableChecksum) && i < len(indexChecksum) { - if tableChecksum[i].bucket != indexChecksum[i].bucket || tableChecksum[i].checksum != indexChecksum[i].checksum { - if tableChecksum[i].bucket <= indexChecksum[i].bucket { - currentOffset = int(tableChecksum[i].bucket) - tableRowCntToCheck = tableChecksum[i].count - } else { - currentOffset = int(indexChecksum[i].bucket) - tableRowCntToCheck = indexChecksum[i].count - } - meetError = true - break - } - i++ - } - - if !meetError && i < len(indexChecksum) && i == len(tableChecksum) { - // Table side has fewer buckets. - currentOffset = int(indexChecksum[i].bucket) - tableRowCntToCheck = indexChecksum[i].count - meetError = true - } else if !meetError && i < len(tableChecksum) && i == len(indexChecksum) { - // Index side has fewer buckets. - currentOffset = int(tableChecksum[i].bucket) - tableRowCntToCheck = tableChecksum[i].count - meetError = true - } - - if !meetError { - if times != 1 { - logutil.BgLogger().Error("unexpected result, no error detected in this round, but an error is detected in the previous round", zap.Int("times", times), zap.Int("offset", offset), zap.Int("mod", mod)) - } - break - } - - offset += currentOffset * mod - mod *= bucketSize - } - - queryToRow := func(se sessionctx.Context, sql string) ([]chunk.Row, error) { - rs, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) - if err != nil { - return nil, err - } - row, err := sqlexec.DrainRecordSet(ctx, rs, 4096) - if err != nil { - return nil, err - } - err = rs.Close() - if err != nil { - logutil.BgLogger().Warn("close result set failed", zap.Error(err)) - } - return row, nil - } - - if meetError { - groupByKey := fmt.Sprintf("((cast(%s as signed) - %d) %% %d)", md5Handle.String(), offset, mod) - indexSQL := fmt.Sprintf("select %s, %s, %s from %s use index(`%s`) where %s = 0 order by %s", handleColumnField, indexColumnField.String(), md5HandleAndIndexCol.String(), TableName(w.e.dbName, w.e.table.Meta().Name.String()), idxInfo.Name, groupByKey, handleColumnField) - tableSQL := fmt.Sprintf("select /*+ read_from_storage(tikv[%s]) */ %s, %s, %s from %s use index() where %s = 0 order by %s", TableName(w.e.dbName, w.e.table.Meta().Name.String()), handleColumnField, indexColumnField.String(), md5HandleAndIndexCol.String(), TableName(w.e.dbName, w.e.table.Meta().Name.String()), groupByKey, handleColumnField) - - idxRow, err := queryToRow(se, indexSQL) - if err != nil { - trySaveErr(err) - return - } - tblRow, err := queryToRow(se, tableSQL) - if err != nil { - trySaveErr(err) - return - } - - getHandleFromRow := func(row chunk.Row) (kv.Handle, error) { - handleDatum := make([]types.Datum, 0) - for i, t := range pkTypes { - handleDatum = append(handleDatum, row.GetDatum(i, t)) - } - if w.table.Meta().IsCommonHandle { - handleBytes, err := codec.EncodeKey(w.sctx.GetSessionVars().StmtCtx, nil, handleDatum...) - if err != nil { - return nil, err - } - return kv.NewCommonHandle(handleBytes) - } - return kv.IntHandle(row.GetInt64(0)), nil - } - getValueFromRow := func(row chunk.Row) ([]types.Datum, error) { - valueDatum := make([]types.Datum, 0) - for i, t := range idxInfo.Columns { - valueDatum = append(valueDatum, row.GetDatum(i+len(pkCols), &w.table.Meta().Columns[t.Offset].FieldType)) - } - return valueDatum, nil - } - - ir := func() *consistency.Reporter { - return &consistency.Reporter{ - HandleEncode: func(handle kv.Handle) kv.Key { - return tablecodec.EncodeRecordKey(w.table.RecordPrefix(), handle) - }, - IndexEncode: func(idxRow *consistency.RecordData) kv.Key { - var idx table.Index - for _, v := range w.table.Indices() { - if strings.EqualFold(v.Meta().Name.String(), idxInfo.Name.O) { - idx = v - break - } - } - if idx == nil { - return nil - } - k, _, err := idx.GenIndexKey(w.sctx.GetSessionVars().StmtCtx, idxRow.Values[:len(idx.Meta().Columns)], idxRow.Handle, nil) - if err != nil { - return nil - } - return k - }, - Tbl: w.table.Meta(), - Idx: idxInfo, - Sctx: w.sctx, - } - } - - getCheckSum := func(row chunk.Row) uint64 { - return row.GetUint64(len(pkCols) + len(idxInfo.Columns)) - } - - var handle kv.Handle - var tableRecord *consistency.RecordData - var lastTableRecord *consistency.RecordData - var indexRecord *consistency.RecordData - i := 0 - for i < len(tblRow) || i < len(idxRow) { - if i == len(tblRow) { - // No more rows in table side. - tableRecord = nil - } else { - handle, err = getHandleFromRow(tblRow[i]) - if err != nil { - trySaveErr(err) - return - } - value, err := getValueFromRow(tblRow[i]) - if err != nil { - trySaveErr(err) - return - } - tableRecord = &consistency.RecordData{Handle: handle, Values: value} - } - if i == len(idxRow) { - // No more rows in index side. - indexRecord = nil - } else { - indexHandle, err := getHandleFromRow(idxRow[i]) - if err != nil { - trySaveErr(err) - return - } - indexValue, err := getValueFromRow(idxRow[i]) - if err != nil { - trySaveErr(err) - return - } - indexRecord = &consistency.RecordData{Handle: indexHandle, Values: indexValue} - } - - if tableRecord == nil { - if lastTableRecord != nil && lastTableRecord.Handle.Equal(indexRecord.Handle) { - tableRecord = lastTableRecord - } - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, tableRecord) - } else if indexRecord == nil { - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, indexRecord, tableRecord) - } else if tableRecord.Handle.Equal(indexRecord.Handle) && getCheckSum(tblRow[i]) != getCheckSum(idxRow[i]) { - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, indexRecord, tableRecord) - } else if !tableRecord.Handle.Equal(indexRecord.Handle) { - if tableRecord.Handle.Compare(indexRecord.Handle) < 0 { - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, nil, tableRecord) - } else { - if lastTableRecord != nil && lastTableRecord.Handle.Equal(indexRecord.Handle) { - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, lastTableRecord) - } else { - err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, nil) - } - } - } - if err != nil { - trySaveErr(err) - return - } - i++ - if tableRecord != nil { - lastTableRecord = &consistency.RecordData{Handle: tableRecord.Handle, Values: tableRecord.Values} - } else { - lastTableRecord = nil - } - } - } -} - -// Close implements the Worker interface. -func (*checkIndexWorker) Close() {} - -func (e *FastCheckTableExec) createWorker() workerpool.Worker[checkIndexTask, workerpool.None] { - return &checkIndexWorker{sctx: e.Ctx(), dbName: e.dbName, table: e.table, indexInfos: e.indexInfos, e: e} -} - -// Next implements the Executor Next interface. -func (e *FastCheckTableExec) Next(ctx context.Context, _ *chunk.Chunk) error { - if e.done || len(e.indexInfos) == 0 { - return nil - } - defer func() { e.done = true }() - - // Here we need check all indexes, includes invisible index - e.Ctx().GetSessionVars().OptimizerUseInvisibleIndexes = true - defer func() { - e.Ctx().GetSessionVars().OptimizerUseInvisibleIndexes = false - }() - - workerPool := workerpool.NewWorkerPool[checkIndexTask]("checkIndex", - poolutil.CheckTable, 3, e.createWorker) - workerPool.Start(ctx) - - e.wg.Add(len(e.indexInfos)) - for i := range e.indexInfos { - workerPool.AddTask(checkIndexTask{indexOffset: i}) - } - - e.wg.Wait() - workerPool.ReleaseAndWait() - - p := e.err.Load() - if p == nil { - return nil - } - return *p -} - -// TableName returns `schema`.`table` -func TableName(schema, table string) string { - return fmt.Sprintf("`%s`.`%s`", escapeName(schema), escapeName(table)) -} - -// ColumnName returns `column` -func ColumnName(column string) string { - return fmt.Sprintf("`%s`", escapeName(column)) -} - -func escapeName(name string) string { - return strings.ReplaceAll(name, "`", "``") -} diff --git a/executor/executor_test.go b/executor/executor_test.go deleted file mode 100644 index ff5402d5d77e5..0000000000000 --- a/executor/executor_test.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func TestSetOperationOnDiffColType(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists t1, t2, t3`) - tk.MustExec(`create table t1(a int, b int)`) - tk.MustExec(`create table t2(a int, b varchar(20))`) - tk.MustExec(`create table t3(a int, b decimal(30,10))`) - tk.MustExec(`insert into t1 values (1,1),(1,1),(2,2),(3,3),(null,null)`) - tk.MustExec(`insert into t2 values (1,'1'),(2,'2'),(null,null),(null,'3')`) - tk.MustExec(`insert into t3 values (2,2.1),(3,3)`) - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - executorSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } -} - -// issue-23038: wrong key range of index scan for year column -func TestIndexScanWithYearCol(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (c1 year(4), c2 int, key(c1));") - tk.MustExec("insert into t values(2001, 1);") - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - executorSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } -} - -func TestSetOperation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`drop table if exists t1, t2, t3`) - tk.MustExec(`create table t1(a int)`) - tk.MustExec(`create table t2 like t1`) - tk.MustExec(`create table t3 like t1`) - tk.MustExec(`insert into t1 values (1),(1),(2),(3),(null)`) - tk.MustExec(`insert into t2 values (1),(2),(null),(null)`) - tk.MustExec(`insert into t3 values (2),(3)`) - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - executorSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } - - // from https://github.com/pingcap/tidb/issues/40279 - tk.MustExec("CREATE TABLE `issue40279` (`a` char(155) NOT NULL DEFAULT 'on1unvbxp5sko6mbetn3ku26tuiyju7w3wc0olzto9ew7gsrx',`b` mediumint(9) NOT NULL DEFAULT '2525518',PRIMARY KEY (`b`,`a`) /*T![clustered_index] CLUSTERED */);") - tk.MustExec("insert into `issue40279` values ();") - tk.MustQuery("( select `issue40279`.`b` as r0 , from_base64( `issue40279`.`a` ) as r1 from `issue40279` ) " + - "except ( " + - "select `issue40279`.`a` as r0 , elt(2, `issue40279`.`a` , `issue40279`.`a` ) as r1 from `issue40279`);"). - Check(testkit.Rows("2525518 ")) - tk.MustExec("drop table if exists t2") - - tk.MustExec("CREATE TABLE `t2` ( `a` varchar(20) CHARACTER SET gbk COLLATE gbk_chinese_ci DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin") - tk.MustExec("insert into t2 values(0xCED2)") - result := tk.MustQuery("(select elt(2,t2.a,t2.a) from t2) except (select 0xCED2 from t2)") - rows := result.Rows() - require.Len(t, rows, 0) -} - -func TestCompareIssue38361(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("drop database if exists TEST1") - tk.MustExec("create database TEST1") - tk.MustExec("use TEST1") - tk.MustExec("create table t(a datetime, b bigint, c bigint)") - tk.MustExec("insert into t values(cast('2023-08-09 00:00:00' as datetime), 20230809, 20231310)") - - tk.MustQuery("select a > 20230809 from t").Check(testkit.Rows("0")) - tk.MustQuery("select a = 20230809 from t").Check(testkit.Rows("1")) - tk.MustQuery("select a < 20230810 from t").Check(testkit.Rows("1")) - // 20231310 can't be converted to valid datetime, thus should be compared using real date type,and datetime will be - // converted to something like 'YYYYMMDDHHMMSS', bigger than 20231310 - tk.MustQuery("select a < 20231310 from t").Check(testkit.Rows("0")) - tk.MustQuery("select 20230809 < a from t").Check(testkit.Rows("0")) - tk.MustQuery("select 20230809 = a from t").Check(testkit.Rows("1")) - tk.MustQuery("select 20230810 > a from t").Check(testkit.Rows("1")) - tk.MustQuery("select 20231310 > a from t").Check(testkit.Rows("0")) - - // constant datetime cmp numeric constant should be compared as real data type - tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) > 20230809 from t").Check(testkit.Rows("1")) - tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) = 20230809 from t").Check(testkit.Rows("0")) - tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) < 20230810 from t").Check(testkit.Rows("0")) - tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) < 20231310 from t").Check(testkit.Rows("0")) - tk.MustQuery("select 20230809 < cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("1")) - tk.MustQuery("select 20230809 = cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) - tk.MustQuery("select 20230810 > cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) - tk.MustQuery("select 20231310 > cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) - - // datetime column cmp numeric column should be compared as real data type - tk.MustQuery("select a > b from t").Check(testkit.Rows("1")) - tk.MustQuery("select a = b from t").Check(testkit.Rows("0")) - tk.MustQuery("select a < b + 1 from t").Check(testkit.Rows("0")) - tk.MustQuery("select a < c from t").Check(testkit.Rows("0")) - tk.MustQuery("select b < a from t").Check(testkit.Rows("1")) - tk.MustQuery("select b = a from t").Check(testkit.Rows("0")) - tk.MustQuery("select b > a from t").Check(testkit.Rows("0")) - tk.MustQuery("select c > a from t").Check(testkit.Rows("0")) -} diff --git a/executor/explain.go b/executor/explain.go deleted file mode 100644 index cb268fb3b6e51..0000000000000 --- a/executor/explain.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "fmt" - "os" - "path/filepath" - "runtime" - rpprof "runtime/pprof" - "sort" - "strconv" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/size" - clientutil "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -// ExplainExec represents an explain executor. -type ExplainExec struct { - exec.BaseExecutor - - explain *core.Explain - analyzeExec exec.Executor - executed bool - ruRuntimeStats *clientutil.RURuntimeStats - rows [][]string - cursor int -} - -// Open implements the Executor Open interface. -func (e *ExplainExec) Open(ctx context.Context) error { - if e.analyzeExec != nil { - return e.analyzeExec.Open(ctx) - } - return nil -} - -// Close implements the Executor Close interface. -func (e *ExplainExec) Close() error { - e.rows = nil - if e.analyzeExec != nil && !e.executed { - // Open(), but Next() is not called. - return e.analyzeExec.Close() - } - return nil -} - -// Next implements the Executor Next interface. -func (e *ExplainExec) Next(ctx context.Context, req *chunk.Chunk) error { - if e.rows == nil { - var err error - e.rows, err = e.generateExplainInfo(ctx) - if err != nil { - return err - } - } - - req.GrowAndReset(e.MaxChunkSize()) - if e.cursor >= len(e.rows) { - return nil - } - - numCurRows := mathutil.Min(req.Capacity(), len(e.rows)-e.cursor) - for i := e.cursor; i < e.cursor+numCurRows; i++ { - for j := range e.rows[i] { - req.AppendString(j, e.rows[i][j]) - } - } - e.cursor += numCurRows - return nil -} - -func (e *ExplainExec) executeAnalyzeExec(ctx context.Context) (err error) { - if e.analyzeExec != nil && !e.executed { - defer func() { - err1 := e.analyzeExec.Close() - if err1 != nil { - if err != nil { - err = errors.New(err.Error() + ", " + err1.Error()) - } else { - err = err1 - } - } - }() - if minHeapInUse, alarmRatio := e.Ctx().GetSessionVars().MemoryDebugModeMinHeapInUse, e.Ctx().GetSessionVars().MemoryDebugModeAlarmRatio; minHeapInUse != 0 && alarmRatio != 0 { - memoryDebugModeCtx, cancel := context.WithCancel(ctx) - waitGroup := sync.WaitGroup{} - waitGroup.Add(1) - defer func() { - // Notify and wait debug goroutine exit. - cancel() - waitGroup.Wait() - }() - go (&memoryDebugModeHandler{ - ctx: memoryDebugModeCtx, - minHeapInUse: mathutil.Abs(minHeapInUse), - alarmRatio: alarmRatio, - autoGC: minHeapInUse > 0, - memTracker: e.Ctx().GetSessionVars().MemTracker, - wg: &waitGroup, - }).run() - } - e.executed = true - chk := exec.TryNewCacheChunk(e.analyzeExec) - for { - err = exec.Next(ctx, e.analyzeExec, chk) - if err != nil || chk.NumRows() == 0 { - break - } - } - } - // Register the RU runtime stats to the runtime stats collection after the analyze executor has been executed. - if e.analyzeExec != nil && e.executed { - if coll := e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl; coll != nil { - coll.RegisterStats(e.explain.TargetPlan.ID(), &ruRuntimeStats{e.ruRuntimeStats}) - } - } - return err -} - -func (e *ExplainExec) generateExplainInfo(ctx context.Context) (rows [][]string, err error) { - if err = e.executeAnalyzeExec(ctx); err != nil { - return nil, err - } - if err = e.explain.RenderResult(); err != nil { - return nil, err - } - return e.explain.Rows, nil -} - -// getAnalyzeExecToExecutedNoDelay gets the analyze DML executor to execute in handleNoDelay function. -// For explain analyze insert/update/delete statement, the analyze executor should be executed in handleNoDelay -// function and then commit transaction if needed. -// Otherwise, in autocommit transaction, the table record change of analyze executor(insert/update/delete...) -// will not be committed. -func (e *ExplainExec) getAnalyzeExecToExecutedNoDelay() exec.Executor { - if e.analyzeExec != nil && !e.executed && e.analyzeExec.Schema().Len() == 0 { - e.executed = true - return e.analyzeExec - } - return nil -} - -type memoryDebugModeHandler struct { - ctx context.Context - minHeapInUse int64 - alarmRatio int64 - autoGC bool - wg *sync.WaitGroup - memTracker *memory.Tracker - - infoField []zap.Field -} - -func (h *memoryDebugModeHandler) fetchCurrentMemoryUsage(gc bool) (heapInUse, trackedMem uint64) { - if gc { - runtime.GC() //nolint: revive - } - instanceStats := memory.ForceReadMemStats() - heapInUse = instanceStats.HeapInuse - trackedMem = uint64(h.memTracker.BytesConsumed()) - return -} - -func (h *memoryDebugModeHandler) genInfo(status string, needProfile bool, heapInUse, trackedMem int64) (fields []zap.Field, err error) { - var fileName string - h.infoField = h.infoField[:0] - h.infoField = append(h.infoField, zap.String("sql", status)) - h.infoField = append(h.infoField, zap.String("heap in use", memory.FormatBytes(heapInUse))) - h.infoField = append(h.infoField, zap.String("tracked memory", memory.FormatBytes(trackedMem))) - if needProfile { - fileName, err = getHeapProfile() - h.infoField = append(h.infoField, zap.String("heap profile", fileName)) - } - return h.infoField, err -} - -func (h *memoryDebugModeHandler) getTrackerTreeMemUseLogs() []zap.Field { - trackerMemUseMap := h.memTracker.CountAllChildrenMemUse() - logs := make([]zap.Field, 0, len(trackerMemUseMap)) - keys := make([]string, 0, len(trackerMemUseMap)) - for k := range trackerMemUseMap { - keys = append(keys, k) - } - sort.Strings(keys) - for _, k := range keys { - logs = append(logs, zap.String("TrackerTree "+k, memory.FormatBytes(trackerMemUseMap[k]))) - } - return logs -} - -func updateTriggerIntervalByHeapInUse(heapInUse uint64) (time.Duration, int) { - if heapInUse < 30*size.GB { - return 5 * time.Second, 6 - } else if heapInUse < 40*size.GB { - return 15 * time.Second, 2 - } else { - return 30 * time.Second, 1 - } -} - -func (h *memoryDebugModeHandler) run() { - var err error - var fields []zap.Field - defer func() { - heapInUse, trackedMem := h.fetchCurrentMemoryUsage(true) - if err == nil { - fields, err := h.genInfo("finished", true, int64(heapInUse), int64(trackedMem)) - logutil.BgLogger().Info("Memory Debug Mode", fields...) - if err != nil { - logutil.BgLogger().Error("Memory Debug Mode Exit", zap.Error(err)) - } - } else { - fields, err := h.genInfo("debug_mode_error", false, int64(heapInUse), int64(trackedMem)) - logutil.BgLogger().Error("Memory Debug Mode", fields...) - logutil.BgLogger().Error("Memory Debug Mode Exit", zap.Error(err)) - } - h.wg.Done() - }() - - logutil.BgLogger().Info("Memory Debug Mode", - zap.String("sql", "started"), - zap.Bool("autoGC", h.autoGC), - zap.String("minHeapInUse", memory.FormatBytes(h.minHeapInUse)), - zap.Int64("alarmRatio", h.alarmRatio), - ) - triggerInterval := 5 * time.Second - printMod := 6 - ticker, loop := time.NewTicker(triggerInterval), 0 - for { - select { - case <-h.ctx.Done(): - return - case <-ticker.C: - heapInUse, trackedMem := h.fetchCurrentMemoryUsage(h.autoGC) - loop++ - if loop%printMod == 0 { - fields, err = h.genInfo("running", false, int64(heapInUse), int64(trackedMem)) - logutil.BgLogger().Info("Memory Debug Mode", fields...) - if err != nil { - return - } - } - triggerInterval, printMod = updateTriggerIntervalByHeapInUse(heapInUse) - ticker.Reset(triggerInterval) - - if !h.autoGC { - if heapInUse > uint64(h.minHeapInUse) && trackedMem/100*uint64(100+h.alarmRatio) < heapInUse { - fields, err = h.genInfo("warning", true, int64(heapInUse), int64(trackedMem)) - logutil.BgLogger().Warn("Memory Debug Mode", fields...) - if err != nil { - return - } - } - } else { - if heapInUse > uint64(h.minHeapInUse) && trackedMem/100*uint64(100+h.alarmRatio) < heapInUse { - fields, err = h.genInfo("warning", true, int64(heapInUse), int64(trackedMem)) - logutil.BgLogger().Warn("Memory Debug Mode", fields...) - if err != nil { - return - } - ts := h.memTracker.SearchTrackerConsumedMoreThanNBytes(h.minHeapInUse / 5) - logs := make([]zap.Field, 0, len(ts)) - for _, t := range ts { - logs = append(logs, zap.String("Executor_"+strconv.Itoa(t.Label()), memory.FormatBytes(t.BytesConsumed()))) - } - logutil.BgLogger().Warn("Memory Debug Mode, Log all executors that consumes more than threshold * 20%", logs...) - logutil.BgLogger().Warn("Memory Debug Mode, Log the tracker tree", h.getTrackerTreeMemUseLogs()...) - } - } - } - } -} - -func getHeapProfile() (fileName string, err error) { - tempDir := filepath.Join(config.GetGlobalConfig().TempStoragePath, "record") - timeString := time.Now().Format(time.RFC3339) - fileName = filepath.Join(tempDir, "heapGC"+timeString) - f, err := os.Create(fileName) - if err != nil { - return "", err - } - p := rpprof.Lookup("heap") - err = p.WriteTo(f, 0) - if err != nil { - return "", err - } - err = f.Close() - if err != nil { - return "", err - } - return fileName, nil -} - -// ruRuntimeStats is a wrapper of clientutil.RURuntimeStats, -// which implements the RuntimeStats interface. -type ruRuntimeStats struct { - *clientutil.RURuntimeStats -} - -// String implements the RuntimeStats interface. -func (e *ruRuntimeStats) String() string { - if e.RURuntimeStats != nil { - return fmt.Sprintf("RU:%f", e.RURuntimeStats.RRU()+e.RURuntimeStats.WRU()) - } - return "" -} - -// Clone implements the RuntimeStats interface. -func (e *ruRuntimeStats) Clone() execdetails.RuntimeStats { - newRs := &ruRuntimeStats{} - if e.RURuntimeStats != nil { - newRs.RURuntimeStats = e.RURuntimeStats.Clone() - } - return newRs -} - -// Merge implements the RuntimeStats interface. -func (e *ruRuntimeStats) Merge(other execdetails.RuntimeStats) { - tmp, ok := other.(*ruRuntimeStats) - if !ok { - return - } - if tmp.RURuntimeStats != nil { - if e.RURuntimeStats == nil { - e.RURuntimeStats = tmp.RURuntimeStats.Clone() - return - } - e.RURuntimeStats.Merge(tmp.RURuntimeStats) - } -} - -// Tp implements the RuntimeStats interface. -func (*ruRuntimeStats) Tp() int { - return execdetails.TpRURuntimeStats -} diff --git a/executor/foreign_key.go b/executor/foreign_key.go deleted file mode 100644 index 6b6f27624b9c6..0000000000000 --- a/executor/foreign_key.go +++ /dev/null @@ -1,974 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "context" - "strconv" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/set" - "github.com/tikv/client-go/v2/txnkv/txnsnapshot" -) - -// WithForeignKeyTrigger indicates the executor has foreign key check or cascade. -type WithForeignKeyTrigger interface { - GetFKChecks() []*FKCheckExec - GetFKCascades() []*FKCascadeExec - HasFKCascades() bool -} - -// FKCheckExec uses to check foreign key constraint. -// When insert/update child table, need to check the row has related row exists in refer table. -// When insert/update parent table, need to check the row doesn't have related row exists in refer table. -type FKCheckExec struct { - *plannercore.FKCheck - *fkValueHelper - ctx sessionctx.Context - - toBeCheckedKeys []kv.Key - toBeCheckedPrefixKeys []kv.Key - toBeLockedKeys []kv.Key - - checkRowsCache map[string]bool - stats *FKCheckRuntimeStats -} - -// FKCheckRuntimeStats contains the FKCheckExec runtime stats. -type FKCheckRuntimeStats struct { - Total time.Duration - Check time.Duration - Lock time.Duration - Keys int -} - -// FKCascadeExec uses to execute foreign key cascade behaviour. -type FKCascadeExec struct { - *fkValueHelper - plan *plannercore.FKCascade - b *executorBuilder - tp plannercore.FKCascadeType - referredFK *model.ReferredFKInfo - childTable *model.TableInfo - fk *model.FKInfo - fkCols []*model.ColumnInfo - fkIdx *model.IndexInfo - // On delete statement, fkValues stores the delete foreign key values. - // On update statement and the foreign key cascade is `SET NULL`, fkValues stores the old foreign key values. - fkValues [][]types.Datum - // new-value-key => UpdatedValuesCouple - fkUpdatedValuesMap map[string]*UpdatedValuesCouple - - stats *FKCascadeRuntimeStats -} - -// UpdatedValuesCouple contains the updated new row the old rows, exporting for test. -type UpdatedValuesCouple struct { - NewValues []types.Datum - OldValuesList [][]types.Datum -} - -// FKCascadeRuntimeStats contains the FKCascadeExec runtime stats. -type FKCascadeRuntimeStats struct { - Total time.Duration - Keys int -} - -func buildTblID2FKCheckExecs(sctx sessionctx.Context, tblID2Table map[int64]table.Table, tblID2FKChecks map[int64][]*plannercore.FKCheck) (map[int64][]*FKCheckExec, error) { - fkChecksMap := make(map[int64][]*FKCheckExec) - for tid, tbl := range tblID2Table { - fkChecks, err := buildFKCheckExecs(sctx, tbl, tblID2FKChecks[tid]) - if err != nil { - return nil, err - } - if len(fkChecks) > 0 { - fkChecksMap[tid] = fkChecks - } - } - return fkChecksMap, nil -} - -func buildFKCheckExecs(sctx sessionctx.Context, tbl table.Table, fkChecks []*plannercore.FKCheck) ([]*FKCheckExec, error) { - fkCheckExecs := make([]*FKCheckExec, 0, len(fkChecks)) - for _, fkCheck := range fkChecks { - fkCheckExec, err := buildFKCheckExec(sctx, tbl, fkCheck) - if err != nil { - return nil, err - } - if fkCheckExec != nil { - fkCheckExecs = append(fkCheckExecs, fkCheckExec) - } - } - return fkCheckExecs, nil -} - -func buildFKCheckExec(sctx sessionctx.Context, tbl table.Table, fkCheck *plannercore.FKCheck) (*FKCheckExec, error) { - var cols []model.CIStr - if fkCheck.FK != nil { - cols = fkCheck.FK.Cols - } else if fkCheck.ReferredFK != nil { - cols = fkCheck.ReferredFK.Cols - } - colsOffsets, err := getFKColumnsOffsets(tbl.Meta(), cols) - if err != nil { - return nil, err - } - helper := &fkValueHelper{ - colsOffsets: colsOffsets, - fkValuesSet: set.NewStringSet(), - } - return &FKCheckExec{ - ctx: sctx, - FKCheck: fkCheck, - fkValueHelper: helper, - }, nil -} - -func (fkc *FKCheckExec) insertRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { - if fkc.ReferredFK != nil { - // Insert into parent table doesn't need to do foreign key check. - return nil - } - return fkc.addRowNeedToCheck(sc, row) -} - -func (fkc *FKCheckExec) updateRowNeedToCheck(sc *stmtctx.StatementContext, oldRow, newRow []types.Datum) error { - newVals, err := fkc.fetchFKValues(newRow) - if err != nil { - return err - } - oldVals, err := fkc.fetchFKValues(oldRow) - if err != nil { - return err - } - if len(oldVals) == len(newVals) { - isSameValue := true - for i := range oldVals { - cmp, err := oldVals[i].Compare(sc, &newVals[i], collate.GetCollator(oldVals[i].Collation())) - if err != nil || cmp != 0 { - isSameValue = false - break - } - } - if isSameValue { - // If the old fk value and the new fk value are the same, no need to check. - return nil - } - } - - if fkc.FK != nil { - return fkc.addRowNeedToCheck(sc, newRow) - } else if fkc.ReferredFK != nil { - return fkc.addRowNeedToCheck(sc, oldRow) - } - return nil -} - -func (fkc *FKCheckExec) deleteRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { - return fkc.addRowNeedToCheck(sc, row) -} - -func (fkc *FKCheckExec) addRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { - vals, err := fkc.fetchFKValuesWithCheck(sc, row) - if err != nil || len(vals) == 0 { - return err - } - key, isPrefix, err := fkc.buildCheckKeyFromFKValue(sc, vals) - if err != nil { - return err - } - if isPrefix { - fkc.toBeCheckedPrefixKeys = append(fkc.toBeCheckedPrefixKeys, key) - } else { - fkc.toBeCheckedKeys = append(fkc.toBeCheckedKeys, key) - } - return nil -} - -func (fkc *FKCheckExec) doCheck(ctx context.Context) error { - if fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { - fkc.stats = &FKCheckRuntimeStats{} - defer fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(fkc.ID(), fkc.stats) - } - if len(fkc.toBeCheckedKeys) == 0 && len(fkc.toBeCheckedPrefixKeys) == 0 { - return nil - } - start := time.Now() - if fkc.stats != nil { - defer func() { - fkc.stats.Keys = len(fkc.toBeCheckedKeys) + len(fkc.toBeCheckedPrefixKeys) - fkc.stats.Total = time.Since(start) - }() - } - txn, err := fkc.ctx.Txn(false) - if err != nil { - return err - } - err = fkc.checkKeys(ctx, txn) - if err != nil { - return err - } - err = fkc.checkIndexKeys(ctx, txn) - if err != nil { - return err - } - if fkc.stats != nil { - fkc.stats.Check = time.Since(start) - } - if len(fkc.toBeLockedKeys) == 0 { - return nil - } - sessVars := fkc.ctx.GetSessionVars() - lockCtx, err := newLockCtx(fkc.ctx, sessVars.LockWaitTimeout, len(fkc.toBeLockedKeys)) - if err != nil { - return err - } - // WARN: Since tidb current doesn't support `LOCK IN SHARE MODE`, therefore, performance will be very poor in concurrency cases. - // TODO(crazycs520):After TiDB support `LOCK IN SHARE MODE`, use `LOCK IN SHARE MODE` here. - forUpdate := atomic.LoadUint32(&sessVars.TxnCtx.ForUpdate) - err = doLockKeys(ctx, fkc.ctx, lockCtx, fkc.toBeLockedKeys...) - // doLockKeys may set TxnCtx.ForUpdate to 1, then if the lock meet write conflict, TiDB can't retry for update. - // So reset TxnCtx.ForUpdate to 0 then can be retry if meet write conflict. - atomic.StoreUint32(&sessVars.TxnCtx.ForUpdate, forUpdate) - if fkc.stats != nil { - fkc.stats.Lock = time.Since(start) - fkc.stats.Check - } - return err -} - -func (fkc *FKCheckExec) buildCheckKeyFromFKValue(sc *stmtctx.StatementContext, vals []types.Datum) (key kv.Key, isPrefix bool, err error) { - if fkc.IdxIsPrimaryKey { - handleKey, err := fkc.buildHandleFromFKValues(sc, vals) - if err != nil { - return nil, false, err - } - key := tablecodec.EncodeRecordKey(fkc.Tbl.RecordPrefix(), handleKey) - if fkc.IdxIsExclusive { - return key, false, nil - } - return key, true, nil - } - key, distinct, err := fkc.Idx.GenIndexKey(sc, vals, nil, nil) - if err != nil { - return nil, false, err - } - if distinct && fkc.IdxIsExclusive { - return key, false, nil - } - return key, true, nil -} - -func (fkc *FKCheckExec) buildHandleFromFKValues(sc *stmtctx.StatementContext, vals []types.Datum) (kv.Handle, error) { - if len(vals) == 1 && fkc.Idx == nil { - return kv.IntHandle(vals[0].GetInt64()), nil - } - handleBytes, err := codec.EncodeKey(sc, nil, vals...) - if err != nil { - return nil, err - } - return kv.NewCommonHandle(handleBytes) -} - -func (fkc *FKCheckExec) checkKeys(ctx context.Context, txn kv.Transaction) error { - if len(fkc.toBeCheckedKeys) == 0 { - return nil - } - err := fkc.prefetchKeys(ctx, txn, fkc.toBeCheckedKeys) - if err != nil { - return err - } - for _, k := range fkc.toBeCheckedKeys { - err = fkc.checkKey(ctx, txn, k) - if err != nil { - return err - } - } - return nil -} - -func (*FKCheckExec) prefetchKeys(ctx context.Context, txn kv.Transaction, keys []kv.Key) error { - // Fill cache using BatchGet - _, err := txn.BatchGet(ctx, keys) - if err != nil { - return err - } - return nil -} - -func (fkc *FKCheckExec) checkKey(ctx context.Context, txn kv.Transaction, k kv.Key) error { - if fkc.CheckExist { - return fkc.checkKeyExist(ctx, txn, k) - } - return fkc.checkKeyNotExist(ctx, txn, k) -} - -func (fkc *FKCheckExec) checkKeyExist(ctx context.Context, txn kv.Transaction, k kv.Key) error { - _, err := txn.Get(ctx, k) - if err == nil { - fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, k) - return nil - } - if kv.IsErrNotFound(err) { - return fkc.FailedErr - } - return err -} - -func (fkc *FKCheckExec) checkKeyNotExist(ctx context.Context, txn kv.Transaction, k kv.Key) error { - _, err := txn.Get(ctx, k) - if err == nil { - return fkc.FailedErr - } - if kv.IsErrNotFound(err) { - return nil - } - return err -} - -func (fkc *FKCheckExec) checkIndexKeys(ctx context.Context, txn kv.Transaction) error { - if len(fkc.toBeCheckedPrefixKeys) == 0 { - return nil - } - memBuffer := txn.GetMemBuffer() - snap := txn.GetSnapshot() - snap.SetOption(kv.ScanBatchSize, 2) - defer func() { - snap.SetOption(kv.ScanBatchSize, txnsnapshot.DefaultScanBatchSize) - }() - for _, key := range fkc.toBeCheckedPrefixKeys { - err := fkc.checkPrefixKey(ctx, memBuffer, snap, key) - if err != nil { - return err - } - } - return nil -} - -func (fkc *FKCheckExec) checkPrefixKey(ctx context.Context, memBuffer kv.MemBuffer, snap kv.Snapshot, key kv.Key) error { - key, value, err := fkc.getIndexKeyValueInTable(ctx, memBuffer, snap, key) - if err != nil { - return err - } - if fkc.CheckExist { - return fkc.checkPrefixKeyExist(key, value) - } - if len(value) > 0 { - // If check not exist, but the key is exist, return failedErr. - return fkc.FailedErr - } - return nil -} - -func (fkc *FKCheckExec) checkPrefixKeyExist(key kv.Key, value []byte) error { - exist := len(value) > 0 - if !exist { - return fkc.FailedErr - } - if fkc.Idx != nil && fkc.Idx.Meta().Primary && fkc.Tbl.Meta().IsCommonHandle { - fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, key) - } else { - handle, err := tablecodec.DecodeIndexHandle(key, value, len(fkc.Idx.Meta().Columns)) - if err != nil { - return err - } - handleKey := tablecodec.EncodeRecordKey(fkc.Tbl.RecordPrefix(), handle) - fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, handleKey) - } - return nil -} - -func (*FKCheckExec) getIndexKeyValueInTable(ctx context.Context, memBuffer kv.MemBuffer, snap kv.Snapshot, key kv.Key) (k []byte, v []byte, _ error) { - select { - case <-ctx.Done(): - return nil, nil, ctx.Err() - default: - } - memIter, err := memBuffer.Iter(key, key.PrefixNext()) - if err != nil { - return nil, nil, err - } - deletedKeys := set.NewStringSet() - defer memIter.Close() - for ; memIter.Valid(); err = memIter.Next() { - if err != nil { - return nil, nil, err - } - k := memIter.Key() - if !k.HasPrefix(key) { - break - } - // check whether the key was been deleted. - if len(memIter.Value()) > 0 { - return k, memIter.Value(), nil - } - deletedKeys.Insert(string(k)) - } - - it, err := snap.Iter(key, key.PrefixNext()) - if err != nil { - return nil, nil, err - } - defer it.Close() - for ; it.Valid(); err = it.Next() { - if err != nil { - return nil, nil, err - } - k := it.Key() - if !k.HasPrefix(key) { - break - } - if !deletedKeys.Exist(string(k)) { - return k, it.Value(), nil - } - } - return nil, nil, nil -} - -type fkValueHelper struct { - colsOffsets []int - fkValuesSet set.StringSet -} - -func (h *fkValueHelper) fetchFKValuesWithCheck(sc *stmtctx.StatementContext, row []types.Datum) ([]types.Datum, error) { - vals, err := h.fetchFKValues(row) - if err != nil || h.hasNullValue(vals) { - return nil, err - } - keyBuf, err := codec.EncodeKey(sc, nil, vals...) - if err != nil { - return nil, err - } - key := string(keyBuf) - if h.fkValuesSet.Exist(key) { - return nil, nil - } - h.fkValuesSet.Insert(key) - return vals, nil -} - -func (h *fkValueHelper) fetchFKValues(row []types.Datum) ([]types.Datum, error) { - vals := make([]types.Datum, len(h.colsOffsets)) - for i, offset := range h.colsOffsets { - if offset >= len(row) { - return nil, table.ErrIndexOutBound.GenWithStackByArgs("", offset, row) - } - vals[i] = row[offset] - } - return vals, nil -} - -func (*fkValueHelper) hasNullValue(vals []types.Datum) bool { - // If any foreign key column value is null, no need to check this row. - // test case: - // create table t1 (id int key,a int, b int, index(a, b)); - // create table t2 (id int key,a int, b int, foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE); - // > insert into t2 values (2, null, 1); - // Query OK, 1 row affected - // > insert into t2 values (3, 1, null); - // Query OK, 1 row affected - // > insert into t2 values (4, null, null); - // Query OK, 1 row affected - // > select * from t2; - // +----+--------+--------+ - // | id | a | b | - // +----+--------+--------+ - // | 4 | | | - // | 2 | | 1 | - // | 3 | 1 | | - // +----+--------+--------+ - for _, val := range vals { - if val.IsNull() { - return true - } - } - return false -} - -func getFKColumnsOffsets(tbInfo *model.TableInfo, cols []model.CIStr) ([]int, error) { - colsOffsets := make([]int, len(cols)) - for i, col := range cols { - offset := -1 - for i := range tbInfo.Columns { - if tbInfo.Columns[i].Name.L == col.L { - offset = tbInfo.Columns[i].Offset - break - } - } - if offset < 0 { - return nil, table.ErrUnknownColumn.GenWithStackByArgs(col.L) - } - colsOffsets[i] = offset - } - return colsOffsets, nil -} - -type fkCheckKey struct { - k kv.Key - isPrefix bool -} - -func (fkc FKCheckExec) checkRows(ctx context.Context, sc *stmtctx.StatementContext, txn kv.Transaction, rows []toBeCheckedRow) error { - if fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { - fkc.stats = &FKCheckRuntimeStats{} - defer fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(fkc.ID(), fkc.stats) - } - if len(rows) == 0 { - return nil - } - if fkc.checkRowsCache == nil { - fkc.checkRowsCache = map[string]bool{} - } - fkCheckKeys := make([]*fkCheckKey, len(rows)) - prefetchKeys := make([]kv.Key, 0, len(rows)) - for i, r := range rows { - if r.ignored { - continue - } - vals, err := fkc.fetchFKValues(r.row) - if err != nil { - return err - } - if fkc.hasNullValue(vals) { - continue - } - key, isPrefix, err := fkc.buildCheckKeyFromFKValue(sc, vals) - if err != nil { - return err - } - fkCheckKeys[i] = &fkCheckKey{key, isPrefix} - if !isPrefix { - prefetchKeys = append(prefetchKeys, key) - } - } - if len(prefetchKeys) > 0 { - err := fkc.prefetchKeys(ctx, txn, prefetchKeys) - if err != nil { - return err - } - } - memBuffer := txn.GetMemBuffer() - snap := txn.GetSnapshot() - snap.SetOption(kv.ScanBatchSize, 2) - defer func() { - snap.SetOption(kv.ScanBatchSize, 256) - }() - for i, fkCheckKey := range fkCheckKeys { - if fkCheckKey == nil { - continue - } - k := fkCheckKey.k - if ignore, ok := fkc.checkRowsCache[string(k)]; ok { - if ignore { - rows[i].ignored = true - sc.AppendWarning(fkc.FailedErr) - } - continue - } - var err error - if fkCheckKey.isPrefix { - err = fkc.checkPrefixKey(ctx, memBuffer, snap, k) - } else { - err = fkc.checkKey(ctx, txn, k) - } - if err != nil { - rows[i].ignored = true - sc.AppendWarning(fkc.FailedErr) - fkc.checkRowsCache[string(k)] = true - } else { - fkc.checkRowsCache[string(k)] = false - } - if fkc.stats != nil { - fkc.stats.Keys++ - } - } - return nil -} - -func (b *executorBuilder) buildTblID2FKCascadeExecs(tblID2Table map[int64]table.Table, tblID2FKCascades map[int64][]*plannercore.FKCascade) (map[int64][]*FKCascadeExec, error) { - fkCascadesMap := make(map[int64][]*FKCascadeExec) - for tid, tbl := range tblID2Table { - fkCascades, err := b.buildFKCascadeExecs(tbl, tblID2FKCascades[tid]) - if err != nil { - return nil, err - } - if len(fkCascades) > 0 { - fkCascadesMap[tid] = fkCascades - } - } - return fkCascadesMap, nil -} - -func (b *executorBuilder) buildFKCascadeExecs(tbl table.Table, fkCascades []*plannercore.FKCascade) ([]*FKCascadeExec, error) { - fkCascadeExecs := make([]*FKCascadeExec, 0, len(fkCascades)) - for _, fkCascade := range fkCascades { - fkCascadeExec, err := b.buildFKCascadeExec(tbl, fkCascade) - if err != nil { - return nil, err - } - if fkCascadeExec != nil { - fkCascadeExecs = append(fkCascadeExecs, fkCascadeExec) - } - } - return fkCascadeExecs, nil -} - -func (b *executorBuilder) buildFKCascadeExec(tbl table.Table, fkCascade *plannercore.FKCascade) (*FKCascadeExec, error) { - colsOffsets, err := getFKColumnsOffsets(tbl.Meta(), fkCascade.ReferredFK.Cols) - if err != nil { - return nil, err - } - helper := &fkValueHelper{ - colsOffsets: colsOffsets, - fkValuesSet: set.NewStringSet(), - } - return &FKCascadeExec{ - b: b, - fkValueHelper: helper, - plan: fkCascade, - tp: fkCascade.Tp, - referredFK: fkCascade.ReferredFK, - childTable: fkCascade.ChildTable.Meta(), - fk: fkCascade.FK, - fkCols: fkCascade.FKCols, - fkIdx: fkCascade.FKIdx, - fkUpdatedValuesMap: make(map[string]*UpdatedValuesCouple), - }, nil -} - -func (fkc *FKCascadeExec) onDeleteRow(sc *stmtctx.StatementContext, row []types.Datum) error { - vals, err := fkc.fetchFKValuesWithCheck(sc, row) - if err != nil || len(vals) == 0 { - return err - } - fkc.fkValues = append(fkc.fkValues, vals) - return nil -} - -func (fkc *FKCascadeExec) onUpdateRow(sc *stmtctx.StatementContext, oldRow, newRow []types.Datum) error { - oldVals, err := fkc.fetchFKValuesWithCheck(sc, oldRow) - if err != nil || len(oldVals) == 0 { - return err - } - if model.ReferOptionType(fkc.fk.OnUpdate) == model.ReferOptionSetNull { - fkc.fkValues = append(fkc.fkValues, oldVals) - return nil - } - newVals, err := fkc.fetchFKValues(newRow) - if err != nil { - return err - } - newValsKey, err := codec.EncodeKey(sc, nil, newVals...) - if err != nil { - return err - } - couple := fkc.fkUpdatedValuesMap[string(newValsKey)] - if couple == nil { - couple = &UpdatedValuesCouple{ - NewValues: newVals, - } - } - couple.OldValuesList = append(couple.OldValuesList, oldVals) - fkc.fkUpdatedValuesMap[string(newValsKey)] = couple - return nil -} - -func (fkc *FKCascadeExec) buildExecutor(ctx context.Context) (exec.Executor, error) { - p, err := fkc.buildFKCascadePlan(ctx) - if err != nil || p == nil { - return nil, err - } - fkc.plan.CascadePlans = append(fkc.plan.CascadePlans, p) - e := fkc.b.build(p) - return e, fkc.b.err -} - -// maxHandleFKValueInOneCascade uses to limit the max handle fk value in one cascade executor, -// this is to avoid performance issue, see: https://github.com/pingcap/tidb/issues/38631 -var maxHandleFKValueInOneCascade = 1024 - -func (fkc *FKCascadeExec) buildFKCascadePlan(ctx context.Context) (plannercore.Plan, error) { - if len(fkc.fkValues) == 0 && len(fkc.fkUpdatedValuesMap) == 0 { - return nil, nil - } - var indexName model.CIStr - if fkc.fkIdx != nil { - indexName = fkc.fkIdx.Name - } - var stmtNode ast.StmtNode - switch fkc.tp { - case plannercore.FKCascadeOnDelete: - fkValues := fkc.fetchOnDeleteOrUpdateFKValues() - switch model.ReferOptionType(fkc.fk.OnDelete) { - case model.ReferOptionCascade: - stmtNode = GenCascadeDeleteAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) - case model.ReferOptionSetNull: - stmtNode = GenCascadeSetNullAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) - } - case plannercore.FKCascadeOnUpdate: - switch model.ReferOptionType(fkc.fk.OnUpdate) { - case model.ReferOptionCascade: - couple := fkc.fetchUpdatedValuesCouple() - if couple != nil && len(couple.NewValues) != 0 { - if fkc.stats != nil { - fkc.stats.Keys += len(couple.OldValuesList) - } - stmtNode = GenCascadeUpdateAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, couple) - } - case model.ReferOptionSetNull: - fkValues := fkc.fetchOnDeleteOrUpdateFKValues() - stmtNode = GenCascadeSetNullAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) - } - } - if stmtNode == nil { - return nil, errors.Errorf("generate foreign key cascade ast failed, %v", fkc.tp) - } - sctx := fkc.b.ctx - err := plannercore.Preprocess(ctx, sctx, stmtNode) - if err != nil { - return nil, err - } - finalPlan, err := planner.OptimizeForForeignKeyCascade(ctx, sctx, stmtNode, fkc.b.is) - if err != nil { - return nil, err - } - return finalPlan, err -} - -func (fkc *FKCascadeExec) fetchOnDeleteOrUpdateFKValues() [][]types.Datum { - var fkValues [][]types.Datum - if len(fkc.fkValues) <= maxHandleFKValueInOneCascade { - fkValues = fkc.fkValues - fkc.fkValues = nil - } else { - fkValues = fkc.fkValues[:maxHandleFKValueInOneCascade] - fkc.fkValues = fkc.fkValues[maxHandleFKValueInOneCascade:] - } - if fkc.stats != nil { - fkc.stats.Keys += len(fkValues) - } - return fkValues -} - -func (fkc *FKCascadeExec) fetchUpdatedValuesCouple() *UpdatedValuesCouple { - for k, couple := range fkc.fkUpdatedValuesMap { - if len(couple.OldValuesList) <= maxHandleFKValueInOneCascade { - delete(fkc.fkUpdatedValuesMap, k) - return couple - } - result := &UpdatedValuesCouple{ - NewValues: couple.NewValues, - OldValuesList: couple.OldValuesList[:maxHandleFKValueInOneCascade], - } - couple.OldValuesList = couple.OldValuesList[maxHandleFKValueInOneCascade:] - return result - } - return nil -} - -// GenCascadeDeleteAST uses to generate cascade delete ast, export for test. -func GenCascadeDeleteAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, fkValues [][]types.Datum) *ast.DeleteStmt { - deleteStmt := &ast.DeleteStmt{ - TableRefs: genTableRefsAST(schema, table, idx), - Where: genWhereConditionAst(cols, fkValues), - } - return deleteStmt -} - -// GenCascadeSetNullAST uses to generate foreign key `SET NULL` ast, export for test. -func GenCascadeSetNullAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, fkValues [][]types.Datum) *ast.UpdateStmt { - newValues := make([]types.Datum, len(cols)) - for i := range cols { - newValues[i] = types.NewDatum(nil) - } - couple := &UpdatedValuesCouple{ - NewValues: newValues, - OldValuesList: fkValues, - } - return GenCascadeUpdateAST(schema, table, idx, cols, couple) -} - -// GenCascadeUpdateAST uses to generate cascade update ast, export for test. -func GenCascadeUpdateAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, couple *UpdatedValuesCouple) *ast.UpdateStmt { - list := make([]*ast.Assignment, 0, len(cols)) - for i, col := range cols { - v := &driver.ValueExpr{Datum: couple.NewValues[i]} - v.Type = col.FieldType - assignment := &ast.Assignment{ - Column: &ast.ColumnName{Name: col.Name}, - Expr: v, - } - list = append(list, assignment) - } - updateStmt := &ast.UpdateStmt{ - TableRefs: genTableRefsAST(schema, table, idx), - Where: genWhereConditionAst(cols, couple.OldValuesList), - List: list, - } - return updateStmt -} - -func genTableRefsAST(schema, table, idx model.CIStr) *ast.TableRefsClause { - tn := &ast.TableName{Schema: schema, Name: table} - if idx.L != "" { - tn.IndexHints = []*ast.IndexHint{{ - IndexNames: []model.CIStr{idx}, - HintType: ast.HintUse, - HintScope: ast.HintForScan, - }} - } - join := &ast.Join{Left: &ast.TableSource{Source: tn}} - return &ast.TableRefsClause{TableRefs: join} -} - -func genWhereConditionAst(cols []*model.ColumnInfo, fkValues [][]types.Datum) ast.ExprNode { - if len(cols) > 1 { - return genWhereConditionAstForMultiColumn(cols, fkValues) - } - valueList := make([]ast.ExprNode, 0, len(fkValues)) - for _, fkVals := range fkValues { - v := &driver.ValueExpr{Datum: fkVals[0]} - v.Type = cols[0].FieldType - valueList = append(valueList, v) - } - return &ast.PatternInExpr{ - Expr: &ast.ColumnNameExpr{Name: &ast.ColumnName{Name: cols[0].Name}}, - List: valueList, - } -} - -func genWhereConditionAstForMultiColumn(cols []*model.ColumnInfo, fkValues [][]types.Datum) ast.ExprNode { - colValues := make([]ast.ExprNode, len(cols)) - for i := range cols { - col := &ast.ColumnNameExpr{Name: &ast.ColumnName{Name: cols[i].Name}} - colValues[i] = col - } - valueList := make([]ast.ExprNode, 0, len(fkValues)) - for _, fkVals := range fkValues { - values := make([]ast.ExprNode, len(fkVals)) - for i, v := range fkVals { - val := &driver.ValueExpr{Datum: v} - val.Type = cols[i].FieldType - values[i] = val - } - row := &ast.RowExpr{Values: values} - valueList = append(valueList, row) - } - return &ast.PatternInExpr{ - Expr: &ast.RowExpr{Values: colValues}, - List: valueList, - } -} - -// String implements the RuntimeStats interface. -func (s *FKCheckRuntimeStats) String() string { - buf := bytes.NewBuffer(make([]byte, 0, 32)) - buf.WriteString("total:") - buf.WriteString(execdetails.FormatDuration(s.Total)) - if s.Check > 0 { - buf.WriteString(", check:") - buf.WriteString(execdetails.FormatDuration(s.Check)) - } - if s.Lock > 0 { - buf.WriteString(", lock:") - buf.WriteString(execdetails.FormatDuration(s.Lock)) - } - if s.Keys > 0 { - buf.WriteString(", foreign_keys:") - buf.WriteString(strconv.Itoa(s.Keys)) - } - return buf.String() -} - -// Clone implements the RuntimeStats interface. -func (s *FKCheckRuntimeStats) Clone() execdetails.RuntimeStats { - newRs := &FKCheckRuntimeStats{ - Total: s.Total, - Check: s.Check, - Lock: s.Lock, - Keys: s.Keys, - } - return newRs -} - -// Merge implements the RuntimeStats interface. -func (s *FKCheckRuntimeStats) Merge(other execdetails.RuntimeStats) { - tmp, ok := other.(*FKCheckRuntimeStats) - if !ok { - return - } - s.Total += tmp.Total - s.Check += tmp.Check - s.Lock += tmp.Lock - s.Keys += tmp.Keys -} - -// Tp implements the RuntimeStats interface. -func (*FKCheckRuntimeStats) Tp() int { - return execdetails.TpFKCheckRuntimeStats -} - -// String implements the RuntimeStats interface. -func (s *FKCascadeRuntimeStats) String() string { - buf := bytes.NewBuffer(make([]byte, 0, 32)) - buf.WriteString("total:") - buf.WriteString(execdetails.FormatDuration(s.Total)) - if s.Keys > 0 { - buf.WriteString(", foreign_keys:") - buf.WriteString(strconv.Itoa(s.Keys)) - } - return buf.String() -} - -// Clone implements the RuntimeStats interface. -func (s *FKCascadeRuntimeStats) Clone() execdetails.RuntimeStats { - newRs := &FKCascadeRuntimeStats{ - Total: s.Total, - Keys: s.Keys, - } - return newRs -} - -// Merge implements the RuntimeStats interface. -func (s *FKCascadeRuntimeStats) Merge(other execdetails.RuntimeStats) { - tmp, ok := other.(*FKCascadeRuntimeStats) - if !ok { - return - } - s.Total += tmp.Total - s.Keys += tmp.Keys -} - -// Tp implements the RuntimeStats interface. -func (*FKCascadeRuntimeStats) Tp() int { - return execdetails.TpFKCascadeRuntimeStats -} diff --git a/executor/importer/BUILD.bazel b/executor/importer/BUILD.bazel deleted file mode 100644 index 9a83a7a134056..0000000000000 --- a/executor/importer/BUILD.bazel +++ /dev/null @@ -1,136 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "importer", - srcs = [ - "chunk_process.go", - "engine_process.go", - "import.go", - "job.go", - "kv_encode.go", - "precheck.go", - "table_import.go", - ], - importpath = "github.com/pingcap/tidb/executor/importer", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/backend", - "//br/pkg/lightning/backend/encode", - "//br/pkg/lightning/backend/external", - "//br/pkg/lightning/backend/kv", - "//br/pkg/lightning/backend/local", - "//br/pkg/lightning/checkpoints", - "//br/pkg/lightning/common", - "//br/pkg/lightning/config", - "//br/pkg/lightning/log", - "//br/pkg/lightning/metric", - "//br/pkg/lightning/mydump", - "//br/pkg/lightning/verification", - "//br/pkg/storage", - "//br/pkg/streamhelper", - "//br/pkg/utils", - "//config", - "//ddl/util", - "//executor/asyncloaddata", - "//expression", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util", - "//util/chunk", - "//util/dbterror", - "//util/dbterror/exeerrors", - "//util/etcd", - "//util/filter", - "//util/intest", - "//util/logutil", - "//util/sqlexec", - "//util/stringutil", - "//util/syncutil", - "@com_github_docker_go_units//:go-units", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@org_golang_x_sync//errgroup", - "@org_uber_go_multierr//:multierr", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "importer_test", - timeout = "short", - srcs = [ - "chunk_process_test.go", - "chunk_process_testkit_test.go", - "import_test.go", - "job_test.go", - "precheck_test.go", - "table_import_test.go", - ], - embed = [":importer"], - flaky = True, - race = "on", - shard_count = 18, - deps = [ - "//br/pkg/errors", - "//br/pkg/lightning/backend/encode", - "//br/pkg/lightning/backend/external", - "//br/pkg/lightning/checkpoints", - "//br/pkg/lightning/config", - "//br/pkg/lightning/log", - "//br/pkg/lightning/mydump", - "//br/pkg/mock", - "//br/pkg/streamhelper", - "//br/pkg/utils", - "//config", - "//expression", - "//infoschema", - "//kv", - "//parser", - "//parser/ast", - "//parser/model", - "//planner/core", - "//session", - "//sessionctx/variable", - "//testkit", - "//types", - "//util", - "//util/dbterror/exeerrors", - "//util/etcd", - "//util/logutil", - "//util/mock", - "//util/sqlexec", - "//util/syncutil", - "@com_github_johannesboyne_gofakes3//:gofakes3", - "@com_github_johannesboyne_gofakes3//backend/s3mem", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_server_v3//embed", - "@org_uber_go_mock//gomock", - "@org_uber_go_zap//:zap", - ], -) diff --git a/executor/importer/import.go b/executor/importer/import.go deleted file mode 100644 index 8696d0fa7be89..0000000000000 --- a/executor/importer/import.go +++ /dev/null @@ -1,1324 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importer - -import ( - "context" - "io" - "math" - "net/url" - "os" - "path/filepath" - "runtime" - "slices" - "strings" - "sync" - "unicode/utf8" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/log" - "github.com/pingcap/tidb/br/pkg/lightning/backend/local" - "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/br/pkg/lightning/config" - litlog "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/br/pkg/storage" - tidb "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/expression" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - pformat "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/filter" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/stringutil" - kvconfig "github.com/tikv/client-go/v2/config" - "go.uber.org/zap" - "golang.org/x/sync/errgroup" -) - -const ( - // DataFormatCSV represents the data source file of IMPORT INTO is csv. - DataFormatCSV = "csv" - // DataFormatDelimitedData delimited data. - DataFormatDelimitedData = "delimited data" - // DataFormatSQL represents the data source file of IMPORT INTO is mydumper-format DML file. - DataFormatSQL = "sql" - // DataFormatParquet represents the data source file of IMPORT INTO is parquet. - DataFormatParquet = "parquet" - - // DefaultDiskQuota is the default disk quota for IMPORT INTO - DefaultDiskQuota = config.ByteSize(50 << 30) // 50GiB - - // 0 means no limit - unlimitedWriteSpeed = config.ByteSize(0) - - characterSetOption = "character_set" - fieldsTerminatedByOption = "fields_terminated_by" - fieldsEnclosedByOption = "fields_enclosed_by" - fieldsEscapedByOption = "fields_escaped_by" - fieldsDefinedNullByOption = "fields_defined_null_by" - linesTerminatedByOption = "lines_terminated_by" - skipRowsOption = "skip_rows" - splitFileOption = "split_file" - diskQuotaOption = "disk_quota" - threadOption = "thread" - maxWriteSpeedOption = "max_write_speed" - checksumTableOption = "checksum_table" - recordErrorsOption = "record_errors" - detachedOption = "detached" - disableTiKVImportModeOption = "disable_tikv_import_mode" - cloudStorageURIOption = "cloud_storage_uri" - // used for test - maxEngineSizeOption = "__max_engine_size" -) - -var ( - // name -> whether the option has value - supportedOptions = map[string]bool{ - characterSetOption: true, - fieldsTerminatedByOption: true, - fieldsEnclosedByOption: true, - fieldsEscapedByOption: true, - fieldsDefinedNullByOption: true, - linesTerminatedByOption: true, - skipRowsOption: true, - splitFileOption: false, - diskQuotaOption: true, - threadOption: true, - maxWriteSpeedOption: true, - checksumTableOption: true, - recordErrorsOption: true, - detachedOption: false, - disableTiKVImportModeOption: false, - maxEngineSizeOption: true, - cloudStorageURIOption: true, - } - - csvOnlyOptions = map[string]struct{}{ - characterSetOption: {}, - fieldsTerminatedByOption: {}, - fieldsEnclosedByOption: {}, - fieldsEscapedByOption: {}, - fieldsDefinedNullByOption: {}, - linesTerminatedByOption: {}, - skipRowsOption: {}, - splitFileOption: {}, - } - - // LoadDataReadBlockSize is exposed for test. - LoadDataReadBlockSize = int64(config.ReadBlockSize) -) - -// GetKVStore returns a kv.Storage. -// kv encoder of physical mode needs it. -var GetKVStore func(path string, tls kvconfig.Security) (tidbkv.Storage, error) - -// FieldMapping indicates the relationship between input field and table column or user variable -type FieldMapping struct { - Column *table.Column - UserVar *ast.VariableExpr -} - -// LoadDataReaderInfo provides information for a data reader of LOAD DATA. -type LoadDataReaderInfo struct { - // Opener can be called at needed to get a io.ReadSeekCloser. It will only - // be called once. - Opener func(ctx context.Context) (io.ReadSeekCloser, error) - // Remote is not nil only if load from cloud storage. - Remote *mydump.SourceFileMeta -} - -// Plan describes the plan of LOAD DATA and IMPORT INTO. -type Plan struct { - DBName string - DBID int64 - // TableInfo is the table info we used during import, we might change it - // if add index by SQL is enabled(it's disabled now). - TableInfo *model.TableInfo - // DesiredTableInfo is the table info before import, and the desired table info - // after import. - DesiredTableInfo *model.TableInfo - - Path string - Format string - // Data interpretation is restrictive if the SQL mode is restrictive and neither - // the IGNORE nor the LOCAL modifier is specified. Errors terminate the load - // operation. - // ref https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-column-assignments - Restrictive bool - - SQLMode mysql.SQLMode - // Charset is the charset of the data file when file is CSV or TSV. - // it might be nil when using LOAD DATA and no charset is specified. - // for IMPORT INTO, it is always non-nil. - Charset *string - ImportantSysVars map[string]string - - // used for LOAD DATA and CSV format of IMPORT INTO - FieldNullDef []string - // this is not used in IMPORT INTO - NullValueOptEnclosed bool - // LinesStartingBy is not used in IMPORT INTO - // FieldsOptEnclosed is not used in either IMPORT INTO or LOAD DATA - plannercore.LineFieldsInfo - IgnoreLines uint64 - - DiskQuota config.ByteSize - Checksum config.PostOpLevel - ThreadCnt int64 - MaxWriteSpeed config.ByteSize - SplitFile bool - MaxRecordedErrors int64 - Detached bool - DisableTiKVImportMode bool - MaxEngineSize config.ByteSize - CloudStorageURI string - - // used for checksum in physical mode - DistSQLScanConcurrency int - - // todo: remove it when load data code is reverted. - InImportInto bool - // only initialized for IMPORT INTO, used when creating job. - Parameters *ImportParameters `json:"-"` - // the user who executes the statement, in the form of user@host - // only initialized for IMPORT INTO - User string `json:"-"` - - IsRaftKV2 bool - // total data file size in bytes. - TotalFileSize int64 -} - -// ASTArgs is the arguments for ast.LoadDataStmt. -// TODO: remove this struct and use the struct which can be serialized. -type ASTArgs struct { - FileLocRef ast.FileLocRefTp - ColumnsAndUserVars []*ast.ColumnNameOrUserVar - ColumnAssignments []*ast.Assignment - OnDuplicate ast.OnDuplicateKeyHandlingType - FieldsInfo *ast.FieldsClause - LinesInfo *ast.LinesClause -} - -// LoadDataController load data controller. -// todo: need a better name -type LoadDataController struct { - *Plan - *ASTArgs - - // used for sync column assignment expression generation. - colAssignMu sync.Mutex - - Table table.Table - - // how input field(or input column) from data file is mapped, either to a column or variable. - // if there's NO column list clause in SQL statement, then it's table's columns - // else it's user defined list. - FieldMappings []*FieldMapping - // see InsertValues.InsertColumns - // Note: our behavior is different with mysql. such as for table t(a,b) - // - "...(a,a) set a=100" is allowed in mysql, but not in tidb - // - "...(a,b) set b=100" will set b=100 in mysql, but in tidb the set is ignored. - // - ref columns in set clause is allowed in mysql, but not in tidb - InsertColumns []*table.Column - - logger *zap.Logger - dataStore storage.ExternalStorage - dataFiles []*mydump.SourceFileMeta - // GlobalSortStore is used to store sorted data when using global sort. - GlobalSortStore storage.ExternalStorage -} - -func getImportantSysVars(sctx sessionctx.Context) map[string]string { - res := map[string]string{} - for k, defVal := range common.DefaultImportantVariables { - if val, ok := sctx.GetSessionVars().GetSystemVar(k); ok { - res[k] = val - } else { - res[k] = defVal - } - } - for k, defVal := range common.DefaultImportVariablesTiDB { - if val, ok := sctx.GetSessionVars().GetSystemVar(k); ok { - res[k] = val - } else { - res[k] = defVal - } - } - return res -} - -// NewPlanFromLoadDataPlan creates a import plan from LOAD DATA. -func NewPlanFromLoadDataPlan(userSctx sessionctx.Context, plan *plannercore.LoadData) (*Plan, error) { - fullTableName := common.UniqueTable(plan.Table.Schema.L, plan.Table.Name.L) - logger := log.L().With(zap.String("table", fullTableName)) - charset := plan.Charset - if charset == nil { - // https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-character-set - d, err2 := userSctx.GetSessionVars().GetSessionOrGlobalSystemVar( - context.Background(), variable.CharsetDatabase) - if err2 != nil { - logger.Error("LOAD DATA get charset failed", zap.Error(err2)) - } else { - charset = &d - } - } - restrictive := userSctx.GetSessionVars().SQLMode.HasStrictMode() && - plan.OnDuplicate != ast.OnDuplicateKeyHandlingIgnore - - var ignoreLines uint64 - if plan.IgnoreLines != nil { - ignoreLines = *plan.IgnoreLines - } - - var ( - nullDef []string - nullValueOptEnclosed = false - ) - - lineFieldsInfo := plannercore.NewLineFieldsInfo(plan.FieldsInfo, plan.LinesInfo) - // todo: move null defined into plannercore.LineFieldsInfo - // in load data, there maybe multiple null def, but in SELECT ... INTO OUTFILE there's only one - if plan.FieldsInfo != nil && plan.FieldsInfo.DefinedNullBy != nil { - nullDef = append(nullDef, *plan.FieldsInfo.DefinedNullBy) - nullValueOptEnclosed = plan.FieldsInfo.NullValueOptEnclosed - } else if len(lineFieldsInfo.FieldsEnclosedBy) != 0 { - nullDef = append(nullDef, "NULL") - } - if len(lineFieldsInfo.FieldsEscapedBy) != 0 { - nullDef = append(nullDef, string([]byte{lineFieldsInfo.FieldsEscapedBy[0], 'N'})) - } - - return &Plan{ - DBName: plan.Table.Schema.O, - DBID: plan.Table.DBInfo.ID, - - Path: plan.Path, - Format: DataFormatDelimitedData, - Restrictive: restrictive, - FieldNullDef: nullDef, - NullValueOptEnclosed: nullValueOptEnclosed, - LineFieldsInfo: lineFieldsInfo, - IgnoreLines: ignoreLines, - - SQLMode: userSctx.GetSessionVars().SQLMode, - Charset: charset, - ImportantSysVars: getImportantSysVars(userSctx), - - DistSQLScanConcurrency: userSctx.GetSessionVars().DistSQLScanConcurrency(), - }, nil -} - -// NewImportPlan creates a new import into plan. -func NewImportPlan(userSctx sessionctx.Context, plan *plannercore.ImportInto, tbl table.Table) (*Plan, error) { - var format string - if plan.Format != nil { - format = strings.ToLower(*plan.Format) - } else { - // without FORMAT 'xxx' clause, default to CSV - format = DataFormatCSV - } - restrictive := userSctx.GetSessionVars().SQLMode.HasStrictMode() - // those are the default values for lightning CSV format too - lineFieldsInfo := plannercore.LineFieldsInfo{ - FieldsTerminatedBy: `,`, - FieldsEnclosedBy: `"`, - FieldsEscapedBy: `\`, - LinesStartingBy: ``, - // csv_parser will determine it automatically(either '\r' or '\n' or '\r\n') - // But user cannot set this to empty explicitly. - LinesTerminatedBy: ``, - } - - p := &Plan{ - TableInfo: tbl.Meta(), - DesiredTableInfo: tbl.Meta(), - DBName: plan.Table.Schema.O, - DBID: plan.Table.DBInfo.ID, - - Path: plan.Path, - Format: format, - Restrictive: restrictive, - FieldNullDef: []string{`\N`}, - LineFieldsInfo: lineFieldsInfo, - - SQLMode: userSctx.GetSessionVars().SQLMode, - ImportantSysVars: getImportantSysVars(userSctx), - - DistSQLScanConcurrency: userSctx.GetSessionVars().DistSQLScanConcurrency(), - InImportInto: true, - User: userSctx.GetSessionVars().User.String(), - } - if err := p.initOptions(userSctx, plan.Options); err != nil { - return nil, err - } - if err := p.initParameters(plan); err != nil { - return nil, err - } - return p, nil -} - -// InitTiKVConfigs initializes some TiKV related configs. -func (p *Plan) InitTiKVConfigs(ctx context.Context, sctx sessionctx.Context) error { - isRaftKV2, err := util.IsRaftKv2(ctx, sctx) - if err != nil { - return err - } - p.IsRaftKV2 = isRaftKV2 - return nil -} - -// ASTArgsFromPlan creates ASTArgs from plan. -func ASTArgsFromPlan(plan *plannercore.LoadData) *ASTArgs { - return &ASTArgs{ - FileLocRef: plan.FileLocRef, - ColumnsAndUserVars: plan.ColumnsAndUserVars, - ColumnAssignments: plan.ColumnAssignments, - OnDuplicate: plan.OnDuplicate, - FieldsInfo: plan.FieldsInfo, - LinesInfo: plan.LinesInfo, - } -} - -// ASTArgsFromImportPlan creates ASTArgs from plan. -func ASTArgsFromImportPlan(plan *plannercore.ImportInto) *ASTArgs { - // FileLocRef are not used in ImportIntoStmt, OnDuplicate not used now. - return &ASTArgs{ - FileLocRef: ast.FileLocServerOrRemote, - ColumnsAndUserVars: plan.ColumnsAndUserVars, - ColumnAssignments: plan.ColumnAssignments, - OnDuplicate: ast.OnDuplicateKeyHandlingReplace, - } -} - -// ASTArgsFromStmt creates ASTArgs from statement. -func ASTArgsFromStmt(stmt string) (*ASTArgs, error) { - stmtNode, err := parser.New().ParseOneStmt(stmt, "", "") - if err != nil { - return nil, err - } - importIntoStmt, ok := stmtNode.(*ast.ImportIntoStmt) - if !ok { - return nil, errors.Errorf("stmt %s is not import into stmt", stmt) - } - // FileLocRef are not used in ImportIntoStmt, OnDuplicate not used now. - return &ASTArgs{ - FileLocRef: ast.FileLocServerOrRemote, - ColumnsAndUserVars: importIntoStmt.ColumnsAndUserVars, - ColumnAssignments: importIntoStmt.ColumnAssignments, - OnDuplicate: ast.OnDuplicateKeyHandlingReplace, - }, nil -} - -// NewLoadDataController create new controller. -func NewLoadDataController(plan *Plan, tbl table.Table, astArgs *ASTArgs) (*LoadDataController, error) { - fullTableName := tbl.Meta().Name.String() - logger := log.L().With(zap.String("table", fullTableName)) - c := &LoadDataController{ - Plan: plan, - ASTArgs: astArgs, - Table: tbl, - logger: logger, - } - if err := c.checkFieldParams(); err != nil { - return nil, err - } - - columnNames := c.initFieldMappings() - if err := c.initLoadColumns(columnNames); err != nil { - return nil, err - } - return c, nil -} - -func (e *LoadDataController) checkFieldParams() error { - if e.Path == "" { - return exeerrors.ErrLoadDataEmptyPath - } - if e.InImportInto { - if e.Format != DataFormatCSV && e.Format != DataFormatParquet && e.Format != DataFormatSQL { - return exeerrors.ErrLoadDataUnsupportedFormat.GenWithStackByArgs(e.Format) - } - } else { - if e.NullValueOptEnclosed && len(e.FieldsEnclosedBy) == 0 { - return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("must specify FIELDS [OPTIONALLY] ENCLOSED BY when use NULL DEFINED BY OPTIONALLY ENCLOSED") - } - // NOTE: IMPORT INTO also don't support user set empty LinesTerminatedBy or FieldsTerminatedBy, - // but it's check in initOptions. - if len(e.LinesTerminatedBy) == 0 { - return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("LINES TERMINATED BY is empty") - } - // see https://github.com/pingcap/tidb/issues/33298 - if len(e.FieldsTerminatedBy) == 0 { - return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("load data with empty field terminator") - } - } - if len(e.FieldsEnclosedBy) > 0 && - (strings.HasPrefix(e.FieldsEnclosedBy, e.FieldsTerminatedBy) || strings.HasPrefix(e.FieldsTerminatedBy, e.FieldsEnclosedBy)) { - return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("FIELDS ENCLOSED BY and TERMINATED BY must not be prefix of each other") - } - - return nil -} - -func (p *Plan) initDefaultOptions() { - threadCnt := runtime.GOMAXPROCS(0) - failpoint.Inject("mockNumCpu", func(val failpoint.Value) { - threadCnt = val.(int) - }) - threadCnt = int(math.Max(1, float64(threadCnt)*0.5)) - - p.Checksum = config.OpLevelRequired - p.ThreadCnt = int64(threadCnt) - p.MaxWriteSpeed = unlimitedWriteSpeed - p.SplitFile = false - p.MaxRecordedErrors = 100 - p.Detached = false - p.DisableTiKVImportMode = false - p.MaxEngineSize = config.ByteSize(defaultMaxEngineSize) - p.CloudStorageURI = variable.CloudStorageURI.Load() - - v := "utf8mb4" - p.Charset = &v -} - -func (p *Plan) initOptions(seCtx sessionctx.Context, options []*plannercore.LoadDataOpt) error { - p.initDefaultOptions() - - specifiedOptions := map[string]*plannercore.LoadDataOpt{} - for _, opt := range options { - hasValue, ok := supportedOptions[opt.Name] - if !ok { - return exeerrors.ErrUnknownOption.FastGenByArgs(opt.Name) - } - if hasValue && opt.Value == nil || !hasValue && opt.Value != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - if _, ok = specifiedOptions[opt.Name]; ok { - return exeerrors.ErrDuplicateOption.FastGenByArgs(opt.Name) - } - specifiedOptions[opt.Name] = opt - } - - if p.Format != DataFormatCSV { - for k := range csvOnlyOptions { - if _, ok := specifiedOptions[k]; ok { - return exeerrors.ErrLoadDataUnsupportedOption.FastGenByArgs(k, "non-CSV format") - } - } - } - - optAsString := func(opt *plannercore.LoadDataOpt) (string, error) { - if opt.Value.GetType().GetType() != mysql.TypeVarString { - return "", exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - val, isNull, err2 := opt.Value.EvalString(seCtx, chunk.Row{}) - if err2 != nil || isNull { - return "", exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - return val, nil - } - optAsInt64 := func(opt *plannercore.LoadDataOpt) (int64, error) { - // current parser takes integer and bool as mysql.TypeLonglong - if opt.Value.GetType().GetType() != mysql.TypeLonglong || mysql.HasIsBooleanFlag(opt.Value.GetType().GetFlag()) { - return 0, exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - val, isNull, err2 := opt.Value.EvalInt(seCtx, chunk.Row{}) - if err2 != nil || isNull { - return 0, exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - return val, nil - } - if opt, ok := specifiedOptions[characterSetOption]; ok { - v, err := optAsString(opt) - if err != nil || v == "" { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - _, err = config.ParseCharset(v) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.Charset = &v - } - if opt, ok := specifiedOptions[fieldsTerminatedByOption]; ok { - v, err := optAsString(opt) - if err != nil || v == "" { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.FieldsTerminatedBy = v - } - if opt, ok := specifiedOptions[fieldsEnclosedByOption]; ok { - v, err := optAsString(opt) - if err != nil || len(v) > 1 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.FieldsEnclosedBy = v - } - if opt, ok := specifiedOptions[fieldsEscapedByOption]; ok { - v, err := optAsString(opt) - if err != nil || len(v) > 1 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.FieldsEscapedBy = v - } - if opt, ok := specifiedOptions[fieldsDefinedNullByOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.FieldNullDef = []string{v} - } - if opt, ok := specifiedOptions[linesTerminatedByOption]; ok { - v, err := optAsString(opt) - // cannot set terminator to empty string explicitly - if err != nil || v == "" { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.LinesTerminatedBy = v - } - if opt, ok := specifiedOptions[skipRowsOption]; ok { - vInt, err := optAsInt64(opt) - if err != nil || vInt < 0 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.IgnoreLines = uint64(vInt) - } - if _, ok := specifiedOptions[splitFileOption]; ok { - p.SplitFile = true - } - if opt, ok := specifiedOptions[diskQuotaOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - if err = p.DiskQuota.UnmarshalText([]byte(v)); err != nil || p.DiskQuota <= 0 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - } - if opt, ok := specifiedOptions[threadOption]; ok { - vInt, err := optAsInt64(opt) - if err != nil || vInt <= 0 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.ThreadCnt = vInt - } - if opt, ok := specifiedOptions[maxWriteSpeedOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - if err = p.MaxWriteSpeed.UnmarshalText([]byte(v)); err != nil || p.MaxWriteSpeed < 0 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - } - if opt, ok := specifiedOptions[checksumTableOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - if err = p.Checksum.FromStringValue(v); err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - } - if opt, ok := specifiedOptions[recordErrorsOption]; ok { - vInt, err := optAsInt64(opt) - if err != nil || vInt < -1 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - p.MaxRecordedErrors = vInt - // todo: set a max value for this param? - } - if _, ok := specifiedOptions[detachedOption]; ok { - p.Detached = true - } - if _, ok := specifiedOptions[disableTiKVImportModeOption]; ok { - p.DisableTiKVImportMode = true - } - if opt, ok := specifiedOptions[cloudStorageURIOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - // set cloud storage uri to empty string to force uses local sort when - // the global variable is set. - if v != "" { - b, err := storage.ParseBackend(v, nil) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - // only support s3 and gcs now. - if b.GetS3() == nil && b.GetGcs() == nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - } - p.CloudStorageURI = v - } - if opt, ok := specifiedOptions[maxEngineSizeOption]; ok { - v, err := optAsString(opt) - if err != nil { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - if err = p.MaxEngineSize.UnmarshalText([]byte(v)); err != nil || p.MaxEngineSize < 0 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) - } - } - - // when split-file is set, data file will be split into chunks of 256 MiB. - // skip_rows should be 0 or 1, we add this restriction to simplify skip_rows - // logic, so we only need to skip on the first chunk for each data file. - // CSV parser limit each row size to LargestEntryLimit(120M), the first row - // will NOT cross file chunk. - if p.SplitFile && p.IgnoreLines > 1 { - return exeerrors.ErrInvalidOptionVal.FastGenByArgs("skip_rows, should be <= 1 when split-file is enabled") - } - - p.adjustOptions() - return nil -} - -func (p *Plan) adjustOptions() { - // max value is cpu-count - numCPU := int64(runtime.GOMAXPROCS(0)) - if p.ThreadCnt > numCPU { - log.L().Info("IMPORT INTO thread count is larger than cpu-count, set to cpu-count") - p.ThreadCnt = numCPU - } -} - -func (p *Plan) initParameters(plan *plannercore.ImportInto) error { - redactURL := ast.RedactURL(p.Path) - var columnsAndVars, setClause string - var sb strings.Builder - formatCtx := pformat.NewRestoreCtx(pformat.DefaultRestoreFlags, &sb) - if len(plan.ColumnsAndUserVars) > 0 { - sb.WriteString("(") - for i, col := range plan.ColumnsAndUserVars { - if i > 0 { - sb.WriteString(", ") - } - _ = col.Restore(formatCtx) - } - sb.WriteString(")") - columnsAndVars = sb.String() - } - if len(plan.ColumnAssignments) > 0 { - sb.Reset() - for i, assign := range plan.ColumnAssignments { - if i > 0 { - sb.WriteString(", ") - } - _ = assign.Restore(formatCtx) - } - setClause = sb.String() - } - optionMap := make(map[string]interface{}, len(plan.Options)) - for _, opt := range plan.Options { - if opt.Value != nil { - val := opt.Value.String() - if opt.Name == cloudStorageURIOption { - val = ast.RedactURL(val) - } - optionMap[opt.Name] = val - } else { - optionMap[opt.Name] = nil - } - } - p.Parameters = &ImportParameters{ - ColumnsAndVars: columnsAndVars, - SetClause: setClause, - FileLocation: redactURL, - Format: p.Format, - Options: optionMap, - } - return nil -} - -// initFieldMappings make a field mapping slice to implicitly map input field to table column or user defined variable -// the slice's order is the same as the order of the input fields. -// Returns a slice of same ordered column names without user defined variable names. -func (e *LoadDataController) initFieldMappings() []string { - columns := make([]string, 0, len(e.ColumnsAndUserVars)+len(e.ColumnAssignments)) - tableCols := e.Table.VisibleCols() - - if len(e.ColumnsAndUserVars) == 0 { - for _, v := range tableCols { - // Data for generated column is generated from the other rows rather than from the parsed data. - fieldMapping := &FieldMapping{ - Column: v, - } - e.FieldMappings = append(e.FieldMappings, fieldMapping) - columns = append(columns, v.Name.O) - } - - return columns - } - - var column *table.Column - - for _, v := range e.ColumnsAndUserVars { - if v.ColumnName != nil { - column = table.FindCol(tableCols, v.ColumnName.Name.O) - columns = append(columns, v.ColumnName.Name.O) - } else { - column = nil - } - - fieldMapping := &FieldMapping{ - Column: column, - UserVar: v.UserVar, - } - e.FieldMappings = append(e.FieldMappings, fieldMapping) - } - - return columns -} - -// initLoadColumns sets columns which the input fields loaded to. -func (e *LoadDataController) initLoadColumns(columnNames []string) error { - var cols []*table.Column - var missingColName string - var err error - tableCols := e.Table.VisibleCols() - - if len(columnNames) != len(tableCols) { - for _, v := range e.ColumnAssignments { - columnNames = append(columnNames, v.Column.Name.O) - } - } - - cols, missingColName = table.FindCols(tableCols, columnNames, e.Table.Meta().PKIsHandle) - if missingColName != "" { - return dbterror.ErrBadField.GenWithStackByArgs(missingColName, "field list") - } - - e.InsertColumns = append(e.InsertColumns, cols...) - - // e.InsertColumns is appended according to the original tables' column sequence. - // We have to reorder it to follow the use-specified column order which is shown in the columnNames. - if err = e.reorderColumns(columnNames); err != nil { - return err - } - - // Check column whether is specified only once. - err = table.CheckOnce(cols) - if err != nil { - return err - } - - return nil -} - -// reorderColumns reorder the e.InsertColumns according to the order of columnNames -// Note: We must ensure there must be one-to-one mapping between e.InsertColumns and columnNames in terms of column name. -func (e *LoadDataController) reorderColumns(columnNames []string) error { - cols := e.InsertColumns - - if len(cols) != len(columnNames) { - return exeerrors.ErrColumnsNotMatched - } - - reorderedColumns := make([]*table.Column, len(cols)) - - if columnNames == nil { - return nil - } - - mapping := make(map[string]int) - for idx, colName := range columnNames { - mapping[strings.ToLower(colName)] = idx - } - - for _, col := range cols { - idx := mapping[col.Name.L] - reorderedColumns[idx] = col - } - - e.InsertColumns = reorderedColumns - - return nil -} - -// GetFieldCount get field count. -func (e *LoadDataController) GetFieldCount() int { - return len(e.FieldMappings) -} - -// GenerateCSVConfig generates a CSV config for parser from LoadDataWorker. -func (e *LoadDataController) GenerateCSVConfig() *config.CSVConfig { - csvConfig := &config.CSVConfig{ - Separator: e.FieldsTerminatedBy, - // ignore optionally enclosed - Delimiter: e.FieldsEnclosedBy, - Terminator: e.LinesTerminatedBy, - NotNull: false, - Null: e.FieldNullDef, - Header: false, - TrimLastSep: false, - EscapedBy: e.FieldsEscapedBy, - StartingBy: e.LinesStartingBy, - } - if !e.InImportInto { - // for load data - csvConfig.AllowEmptyLine = true - csvConfig.QuotedNullIsText = !e.NullValueOptEnclosed - csvConfig.UnescapedQuote = true - } - return csvConfig -} - -// InitDataStore initializes the data store. -func (e *LoadDataController) InitDataStore(ctx context.Context) error { - u, err2 := storage.ParseRawURL(e.Path) - if err2 != nil { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - err2.Error()) - } - - if storage.IsLocal(u) { - u.Path = filepath.Dir(e.Path) - } else { - u.Path = "" - } - s, err := e.initExternalStore(ctx, u, plannercore.ImportIntoDataSource) - if err != nil { - return err - } - e.dataStore = s - - if e.IsGlobalSort() { - target := "cloud storage" - cloudStorageURL, err3 := storage.ParseRawURL(e.Plan.CloudStorageURI) - if err3 != nil { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(target, - err3.Error()) - } - s, err = e.initExternalStore(ctx, cloudStorageURL, target) - if err != nil { - return err - } - e.GlobalSortStore = s - } - return nil -} -func (*LoadDataController) initExternalStore(ctx context.Context, u *url.URL, target string) (storage.ExternalStorage, error) { - b, err2 := storage.ParseBackendFromURL(u, nil) - if err2 != nil { - return nil, exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(target, GetMsgFromBRError(err2)) - } - - opt := &storage.ExternalStorageOptions{} - if intest.InTest { - opt.NoCredentials = true - } - s, err := storage.New(ctx, b, opt) - if err != nil { - return nil, exeerrors.ErrLoadDataCantAccess.GenWithStackByArgs(target, GetMsgFromBRError(err)) - } - return s, nil -} - -// InitDataFiles initializes the data store and files. -// it will call InitDataStore internally. -func (e *LoadDataController) InitDataFiles(ctx context.Context) error { - u, err2 := storage.ParseRawURL(e.Path) - if err2 != nil { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - err2.Error()) - } - - var fileNameKey string - if storage.IsLocal(u) { - // LOAD DATA don't support server file. - if !e.InImportInto { - return exeerrors.ErrLoadDataFromServerDisk.GenWithStackByArgs(e.Path) - } - - if !filepath.IsAbs(e.Path) { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - "file location should be absolute path when import from server disk") - } - // we add this check for security, we don't want user import any sensitive system files, - // most of which is readable text file and don't have a suffix, such as /etc/passwd - if !slices.Contains([]string{".csv", ".sql", ".parquet"}, strings.ToLower(filepath.Ext(e.Path))) { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - "the file suffix is not supported when import from server disk") - } - dir := filepath.Dir(e.Path) - _, err := os.Stat(dir) - if err != nil { - // permission denied / file not exist error, etc. - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - err.Error()) - } - - fileNameKey = filepath.Base(e.Path) - } else { - fileNameKey = strings.Trim(u.Path, "/") - } - // try to find pattern error in advance - _, err2 = filepath.Match(stringutil.EscapeGlobExceptAsterisk(fileNameKey), "") - if err2 != nil { - return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, - "Glob pattern error: "+err2.Error()) - } - - if err2 = e.InitDataStore(ctx); err2 != nil { - return err2 - } - - s := e.dataStore - var totalSize int64 - dataFiles := []*mydump.SourceFileMeta{} - idx := strings.IndexByte(fileNameKey, '*') - // simple path when the path represent one file - sourceType := e.getSourceType() - if idx == -1 { - fileReader, err2 := s.Open(ctx, fileNameKey, nil) - if err2 != nil { - return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "Please check the file location is correct") - } - defer func() { - terror.Log(fileReader.Close()) - }() - size, err3 := fileReader.Seek(0, io.SeekEnd) - if err3 != nil { - return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "failed to read file size by seek") - } - compressTp := mydump.ParseCompressionOnFileExtension(fileNameKey) - fileMeta := mydump.SourceFileMeta{ - Path: fileNameKey, - FileSize: size, - Compression: compressTp, - Type: sourceType, - } - fileMeta.RealSize = e.getFileRealSize(ctx, fileMeta, s) - dataFiles = append(dataFiles, &fileMeta) - totalSize = size - } else { - var commonPrefix string - if !storage.IsLocal(u) { - // for local directory, we're walking the parent directory, - // so we don't have a common prefix as cloud storage do. - commonPrefix = fileNameKey[:idx] - } - // when import from server disk, all entries in parent directory should have READ - // access, else walkDir will fail - // we only support '*', in order to reuse glob library manually escape the path - escapedPath := stringutil.EscapeGlobExceptAsterisk(fileNameKey) - err := s.WalkDir(ctx, &storage.WalkOption{ObjPrefix: commonPrefix, SkipSubDir: true}, - func(remotePath string, size int64) error { - // we have checked in LoadDataExec.Next - //nolint: errcheck - match, _ := filepath.Match(escapedPath, remotePath) - if !match { - return nil - } - compressTp := mydump.ParseCompressionOnFileExtension(remotePath) - fileMeta := mydump.SourceFileMeta{ - Path: remotePath, - FileSize: size, - Compression: compressTp, - Type: sourceType, - } - fileMeta.RealSize = e.getFileRealSize(ctx, fileMeta, s) - dataFiles = append(dataFiles, &fileMeta) - totalSize += size - return nil - }) - if err != nil { - return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err), "failed to walk dir") - } - } - - e.dataFiles = dataFiles - e.TotalFileSize = totalSize - return nil -} - -func (e *LoadDataController) getFileRealSize(ctx context.Context, - fileMeta mydump.SourceFileMeta, store storage.ExternalStorage) int64 { - if fileMeta.Compression == mydump.CompressionNone { - return fileMeta.FileSize - } - compressRatio, err := mydump.SampleFileCompressRatio(ctx, fileMeta, store) - if err != nil { - e.logger.Warn("failed to get compress ratio", zap.String("file", fileMeta.Path), zap.Error(err)) - return fileMeta.FileSize - } - return int64(compressRatio * float64(fileMeta.FileSize)) -} - -func (e *LoadDataController) getSourceType() mydump.SourceType { - switch e.Format { - case DataFormatParquet: - return mydump.SourceTypeParquet - case DataFormatDelimitedData, DataFormatCSV: - return mydump.SourceTypeCSV - default: - // DataFormatSQL - return mydump.SourceTypeSQL - } -} - -// GetLoadDataReaderInfos returns the LoadDataReaderInfo for each data file. -func (e *LoadDataController) GetLoadDataReaderInfos() []LoadDataReaderInfo { - result := make([]LoadDataReaderInfo, 0, len(e.dataFiles)) - for i := range e.dataFiles { - f := e.dataFiles[i] - result = append(result, LoadDataReaderInfo{ - Opener: func(ctx context.Context) (io.ReadSeekCloser, error) { - fileReader, err2 := mydump.OpenReader(ctx, f, e.dataStore, storage.DecompressConfig{}) - if err2 != nil { - return nil, exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "Please check the INFILE path is correct") - } - return fileReader, nil - }, - Remote: f, - }) - } - return result -} - -// GetParser returns a parser for the data file. -func (e *LoadDataController) GetParser( - ctx context.Context, - dataFileInfo LoadDataReaderInfo, -) (parser mydump.Parser, err error) { - reader, err2 := dataFileInfo.Opener(ctx) - if err2 != nil { - return nil, err2 - } - defer func() { - if err != nil { - if err3 := reader.Close(); err3 != nil { - e.logger.Warn("failed to close reader", zap.Error(err3)) - } - } - }() - switch e.Format { - case DataFormatDelimitedData, DataFormatCSV: - var charsetConvertor *mydump.CharsetConvertor - if e.Charset != nil { - charsetConvertor, err = mydump.NewCharsetConvertor(*e.Charset, string(utf8.RuneError)) - if err != nil { - return nil, err - } - } - if err != nil { - return nil, err - } - parser, err = mydump.NewCSVParser( - ctx, - e.GenerateCSVConfig(), - reader, - LoadDataReadBlockSize, - nil, - false, - charsetConvertor) - case DataFormatSQL: - parser = mydump.NewChunkParser( - ctx, - e.SQLMode, - reader, - LoadDataReadBlockSize, - nil, - ) - case DataFormatParquet: - parser, err = mydump.NewParquetParser( - ctx, - e.dataStore, - reader, - dataFileInfo.Remote.Path, - ) - } - if err != nil { - return nil, exeerrors.ErrLoadDataWrongFormatConfig.GenWithStack(err.Error()) - } - parser.SetLogger(litlog.Logger{Logger: logutil.Logger(ctx)}) - - return parser, nil -} - -// HandleSkipNRows skips the first N rows of the data file. -func (e *LoadDataController) HandleSkipNRows(parser mydump.Parser) error { - // handle IGNORE N LINES - ignoreOneLineFn := parser.ReadRow - if csvParser, ok := parser.(*mydump.CSVParser); ok { - ignoreOneLineFn = func() error { - _, _, err3 := csvParser.ReadUntilTerminator() - return err3 - } - } - - ignoreLineCnt := e.IgnoreLines - for ignoreLineCnt > 0 { - err := ignoreOneLineFn() - if err != nil { - if errors.Cause(err) == io.EOF { - return nil - } - return err - } - - ignoreLineCnt-- - } - return nil -} - -func (e *LoadDataController) toMyDumpFiles() []mydump.FileInfo { - tbl := filter.Table{ - Schema: e.DBName, - Name: e.Table.Meta().Name.O, - } - res := []mydump.FileInfo{} - for _, f := range e.dataFiles { - res = append(res, mydump.FileInfo{ - TableName: tbl, - FileMeta: *f, - }) - } - return res -} - -// IsLocalSort returns true if we sort data on local disk. -func (e *LoadDataController) IsLocalSort() bool { - return e.Plan.CloudStorageURI == "" -} - -// IsGlobalSort returns true if we sort data on global storage. -func (e *LoadDataController) IsGlobalSort() bool { - return !e.IsLocalSort() -} - -// CreateColAssignExprs creates the column assignment expressions using session context. -// RewriteAstExpr will write ast node in place(due to xxNode.Accept), but it doesn't change node content, -// so we sync it. -func (e *LoadDataController) CreateColAssignExprs(sctx sessionctx.Context) ([]expression.Expression, []stmtctx.SQLWarn, error) { - e.colAssignMu.Lock() - defer e.colAssignMu.Unlock() - res := make([]expression.Expression, 0, len(e.ColumnAssignments)) - allWarnings := []stmtctx.SQLWarn{} - for _, assign := range e.ColumnAssignments { - newExpr, err := expression.RewriteAstExpr(sctx, assign.Expr, nil, nil, false) - // col assign expr warnings is static, we should generate it for each row processed. - // so we save it and clear it here. - allWarnings = append(allWarnings, sctx.GetSessionVars().StmtCtx.GetWarnings()...) - sctx.GetSessionVars().StmtCtx.SetWarnings(nil) - if err != nil { - return nil, nil, err - } - res = append(res, newExpr) - } - return res, allWarnings, nil -} - -func (e *LoadDataController) getLocalBackendCfg(pdAddr, dataDir string) local.BackendConfig { - backendConfig := local.BackendConfig{ - PDAddr: pdAddr, - LocalStoreDir: dataDir, - MaxConnPerStore: config.DefaultRangeConcurrency, - ConnCompressType: config.CompressionNone, - WorkerConcurrency: config.DefaultRangeConcurrency * 2, - KVWriteBatchSize: config.KVWriteBatchSize, - RegionSplitBatchSize: config.DefaultRegionSplitBatchSize, - RegionSplitConcurrency: runtime.GOMAXPROCS(0), - // enable after we support checkpoint - CheckpointEnabled: false, - MemTableSize: config.DefaultEngineMemCacheSize, - LocalWriterMemCacheSize: int64(config.DefaultLocalWriterMemCacheSize), - ShouldCheckTiKV: true, - DupeDetectEnabled: false, - DuplicateDetectOpt: common.DupDetectOpt{ReportErrOnDup: false}, - StoreWriteBWLimit: int(e.MaxWriteSpeed), - MaxOpenFiles: int(tidbutil.GenRLimit("table_import")), - KeyspaceName: tidb.GetGlobalKeyspaceName(), - PausePDSchedulerScope: config.PausePDSchedulerScopeTable, - DisableAutomaticCompactions: true, - } - if e.IsRaftKV2 { - backendConfig.RaftKV2SwitchModeDuration = config.DefaultSwitchTiKVModeInterval - } - return backendConfig -} - -// JobImportParam is the param of the job import. -type JobImportParam struct { - Job *asyncloaddata.Job - Group *errgroup.Group - GroupCtx context.Context - // should be closed in the end of the job. - Done chan struct{} - - Progress *asyncloaddata.Progress -} - -// JobImportResult is the result of the job import. -type JobImportResult struct { - Affected uint64 - Warnings []stmtctx.SQLWarn - ColSizeMap map[int64]int64 -} - -// JobImporter is the interface for importing a job. -type JobImporter interface { - // Param returns the param of the job import. - Param() *JobImportParam - // Import imports the job. - // import should run in routines using param.Group, when import finished, it should close param.Done. - // during import, we should use param.GroupCtx, so this method has no context param. - Import() - // Result returns the result of the job import. - Result() JobImportResult - io.Closer -} - -// GetMsgFromBRError get msg from BR error. -// TODO: add GetMsg() to errors package to replace this function. -// see TestGetMsgFromBRError for more details. -func GetMsgFromBRError(err error) string { - if err == nil { - return "" - } - if berr, ok := err.(*errors.Error); ok { - return berr.GetMsg() - } - raw := err.Error() - berrMsg := errors.Cause(err).Error() - if len(raw) <= len(berrMsg)+len(": ") { - return raw - } - return raw[:len(raw)-len(berrMsg)-len(": ")] -} - -// TestSyncCh is used in unit test to synchronize the execution. -var TestSyncCh = make(chan struct{}) diff --git a/executor/importer/job.go b/executor/importer/job.go deleted file mode 100644 index b658d42efb067..0000000000000 --- a/executor/importer/job.go +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package importer - -import ( - "context" - "encoding/json" - "fmt" - "sync/atomic" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/util" -) - -// vars used for test. -var ( - // TestLastImportJobID last created job id, used in unit test. - TestLastImportJobID atomic.Int64 -) - -// constants for job status and step. -const ( - // JobStatus - // ┌───────┐ ┌───────┐ ┌────────┐ - // │pending├────►│running├───►│finished│ - // └────┬──┘ └────┬──┘ └────────┘ - // │ │ ┌──────┐ - // │ ├──────►│failed│ - // │ │ └──────┘ - // │ │ ┌─────────┐ - // └─────────────┴──────►│cancelled│ - // └─────────┘ - jobStatusPending = "pending" - // JobStatusRunning exported since it's used in show import jobs - JobStatusRunning = "running" - jogStatusCancelled = "cancelled" - jobStatusFailed = "failed" - jobStatusFinished = "finished" - - // when the job is finished, step will be set to none. - jobStepNone = "" - // JobStepGlobalSorting is the first step when using global sort, - // step goes from none -> global-sorting -> importing -> validating -> none. - JobStepGlobalSorting = "global-sorting" - // JobStepImporting is the first step when using local sort, - // step goes from none -> importing -> validating -> none. - // when used in global sort, it means importing the sorted data. - // when used in local sort, it means encode&sort data and then importing the data. - JobStepImporting = "importing" - JobStepValidating = "validating" - - baseQuerySQL = `SELECT - id, create_time, start_time, end_time, - table_schema, table_name, table_id, created_by, parameters, source_file_size, - status, step, summary, error_message - FROM mysql.tidb_import_jobs` -) - -// ImportParameters is the parameters for import into statement. -// it's a minimal meta info to store in tidb_import_jobs for diagnose. -// for detailed info, see tidb_global_tasks. -type ImportParameters struct { - ColumnsAndVars string `json:"columns-and-vars,omitempty"` - SetClause string `json:"set-clause,omitempty"` - // for s3 URL, AK/SK is redacted for security - FileLocation string `json:"file-location"` - Format string `json:"format"` - // only include what user specified, not include default value. - Options map[string]interface{} `json:"options,omitempty"` -} - -var _ fmt.Stringer = &ImportParameters{} - -// String implements fmt.Stringer interface. -func (ip *ImportParameters) String() string { - b, _ := json.Marshal(ip) - return string(b) -} - -// JobSummary is the summary info of import into job. -type JobSummary struct { - // ImportedRows is the number of rows imported into TiKV. - ImportedRows uint64 `json:"imported-rows,omitempty"` -} - -// JobInfo is the information of import into job. -type JobInfo struct { - ID int64 - CreateTime types.Time - StartTime types.Time - EndTime types.Time - TableSchema string - TableName string - TableID int64 - CreatedBy string - Parameters ImportParameters - SourceFileSize int64 - Status string - // in SHOW IMPORT JOB, we name it as phase. - // here, we use the same name as in distributed framework. - Step string - // the summary info of the job, it's updated only when the job is finished. - // for running job, we should query the progress from the distributed framework. - Summary *JobSummary - ErrorMessage string -} - -// CanCancel returns whether the job can be cancelled. -func (j *JobInfo) CanCancel() bool { - return j.Status == jobStatusPending || j.Status == JobStatusRunning -} - -// GetJob returns the job with the given id if the user has privilege. -// hasSuperPriv: whether the user has super privilege. -// If the user has super privilege, the user can show or operate all jobs, -// else the user can only show or operate his own jobs. -func GetJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, user string, hasSuperPriv bool) (*JobInfo, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - - sql := baseQuerySQL + ` WHERE id = %?` - rs, err := conn.ExecuteInternal(ctx, sql, jobID) - if err != nil { - return nil, err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return nil, err - } - if len(rows) != 1 { - return nil, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(jobID) - } - - info, err := convert2JobInfo(rows[0]) - if err != nil { - return nil, err - } - if !hasSuperPriv && info.CreatedBy != user { - return nil, core.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER") - } - return info, nil -} - -// GetActiveJobCnt returns the count of active import jobs. -// Active import jobs include pending and running jobs. -func GetActiveJobCnt(ctx context.Context, conn sqlexec.SQLExecutor) (int64, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - - sql := `select count(1) from mysql.tidb_import_jobs where status in (%?, %?)` - rs, err := conn.ExecuteInternal(ctx, sql, jobStatusPending, JobStatusRunning) - if err != nil { - return 0, err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return 0, err - } - cnt := rows[0].GetInt64(0) - return cnt, nil -} - -// CreateJob creates import into job by insert a record to system table. -// The AUTO_INCREMENT value will be returned as jobID. -func CreateJob( - ctx context.Context, - conn sqlexec.SQLExecutor, - db, table string, - tableID int64, - user string, - parameters *ImportParameters, - sourceFileSize int64, -) (int64, error) { - bytes, err := json.Marshal(parameters) - if err != nil { - return 0, err - } - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - _, err = conn.ExecuteInternal(ctx, `INSERT INTO mysql.tidb_import_jobs - (table_schema, table_name, table_id, created_by, parameters, source_file_size, status, step) - VALUES (%?, %?, %?, %?, %?, %?, %?, %?);`, - db, table, tableID, user, bytes, sourceFileSize, jobStatusPending, jobStepNone) - if err != nil { - return 0, err - } - rs, err := conn.ExecuteInternal(ctx, `SELECT LAST_INSERT_ID();`) - if err != nil { - return 0, err - } - defer terror.Call(rs.Close) - - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return 0, err - } - if len(rows) != 1 { - return 0, errors.Errorf("unexpected result length: %d", len(rows)) - } - - failpoint.Inject("setLastImportJobID", func() { - TestLastImportJobID.Store(rows[0].GetInt64(0)) - }) - return rows[0].GetInt64(0), nil -} - -// StartJob tries to start a pending job with jobID, change its status/step to running/input step. -// It will not return error when there's no matched job or the job has already started. -func StartJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, step string) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs - SET update_time = CURRENT_TIMESTAMP(6), start_time = CURRENT_TIMESTAMP(6), status = %?, step = %? - WHERE id = %? AND status = %?;`, - JobStatusRunning, step, jobID, jobStatusPending) - - return err -} - -// Job2Step tries to change the step of a running job with jobID. -// It will not return error when there's no matched job. -func Job2Step(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, step string) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs - SET update_time = CURRENT_TIMESTAMP(6), step = %? - WHERE id = %? AND status = %?;`, - step, jobID, JobStatusRunning) - - return err -} - -// FinishJob tries to finish a running job with jobID, change its status to finished, clear its step. -// It will not return error when there's no matched job. -func FinishJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, summary *JobSummary) error { - bytes, err := json.Marshal(summary) - if err != nil { - return err - } - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - _, err = conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs - SET update_time = CURRENT_TIMESTAMP(6), end_time = CURRENT_TIMESTAMP(6), status = %?, step = %?, summary = %? - WHERE id = %? AND status = %?;`, - jobStatusFinished, jobStepNone, bytes, jobID, JobStatusRunning) - return err -} - -// FailJob fails import into job. A job can only be failed once. -// It will not return error when there's no matched job. -func FailJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, errorMsg string) error { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs - SET update_time = CURRENT_TIMESTAMP(6), end_time = CURRENT_TIMESTAMP(6), status = %?, error_message = %? - WHERE id = %? AND status = %?;`, - jobStatusFailed, errorMsg, jobID, JobStatusRunning) - return err -} - -func convert2JobInfo(row chunk.Row) (*JobInfo, error) { - // start_time, end_time, summary, error_message can be NULL, need to use row.IsNull() to check. - startTime, endTime := types.ZeroTime, types.ZeroTime - if !row.IsNull(2) { - startTime = row.GetTime(2) - } - if !row.IsNull(3) { - endTime = row.GetTime(3) - } - - parameters := ImportParameters{} - parametersStr := row.GetString(8) - if err := json.Unmarshal([]byte(parametersStr), ¶meters); err != nil { - return nil, errors.Trace(err) - } - - var summary *JobSummary - var summaryStr string - if !row.IsNull(12) { - summaryStr = row.GetString(12) - } - if len(summaryStr) > 0 { - summary = &JobSummary{} - if err := json.Unmarshal([]byte(summaryStr), summary); err != nil { - return nil, errors.Trace(err) - } - } - - var errMsg string - if !row.IsNull(13) { - errMsg = row.GetString(13) - } - return &JobInfo{ - ID: row.GetInt64(0), - CreateTime: row.GetTime(1), - StartTime: startTime, - EndTime: endTime, - TableSchema: row.GetString(4), - TableName: row.GetString(5), - TableID: row.GetInt64(6), - CreatedBy: row.GetString(7), - Parameters: parameters, - SourceFileSize: row.GetInt64(9), - Status: row.GetString(10), - Step: row.GetString(11), - Summary: summary, - ErrorMessage: errMsg, - }, nil -} - -// GetAllViewableJobs gets all viewable jobs. -func GetAllViewableJobs(ctx context.Context, conn sqlexec.SQLExecutor, user string, hasSuperPriv bool) ([]*JobInfo, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - sql := baseQuerySQL - args := []interface{}{} - if !hasSuperPriv { - sql += " WHERE created_by = %?" - args = append(args, user) - } - rs, err := conn.ExecuteInternal(ctx, sql, args...) - if err != nil { - return nil, err - } - defer terror.Call(rs.Close) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - if err != nil { - return nil, err - } - ret := make([]*JobInfo, 0, len(rows)) - for _, row := range rows { - jobInfo, err2 := convert2JobInfo(row) - if err2 != nil { - return nil, err2 - } - ret = append(ret, jobInfo) - } - - return ret, nil -} - -// CancelJob cancels import into job. Only a running/paused job can be canceled. -// check privileges using get before calling this method. -func CancelJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64) (err error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) - sql := `UPDATE mysql.tidb_import_jobs - SET update_time = CURRENT_TIMESTAMP(6), status = %?, error_message = 'cancelled by user' - WHERE id = %? AND status IN (%?, %?);` - args := []interface{}{jogStatusCancelled, jobID, jobStatusPending, JobStatusRunning} - _, err = conn.ExecuteInternal(ctx, sql, args...) - return err -} diff --git a/executor/internal/BUILD.bazel b/executor/internal/BUILD.bazel deleted file mode 100644 index 5eff32ca29ebe..0000000000000 --- a/executor/internal/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = ["testkit.go"], - importpath = "github.com/pingcap/tidb/executor/internal", - visibility = ["//executor:__subpackages__"], - deps = ["//testkit"], -) diff --git a/executor/internal/applycache/BUILD.bazel b/executor/internal/applycache/BUILD.bazel deleted file mode 100644 index d504c72b21540..0000000000000 --- a/executor/internal/applycache/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "applycache", - srcs = ["apply_cache.go"], - importpath = "github.com/pingcap/tidb/executor/internal/applycache", - visibility = ["//executor:__subpackages__"], - deps = [ - "//sessionctx", - "//util/chunk", - "//util/kvcache", - "//util/mathutil", - "//util/memory", - "//util/syncutil", - ], -) - -go_test( - name = "applycache_test", - timeout = "short", - srcs = [ - "apply_cache_test.go", - "main_test.go", - ], - embed = [":applycache"], - flaky = True, - deps = [ - "//config", - "//meta/autoid", - "//parser/mysql", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/mock", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/internal/applycache/main_test.go b/executor/internal/applycache/main_test.go deleted file mode 100644 index 68798f0e215c4..0000000000000 --- a/executor/internal/applycache/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package applycache - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/internal/builder/BUILD.bazel b/executor/internal/builder/BUILD.bazel deleted file mode 100644 index cef7a1cfd7013..0000000000000 --- a/executor/internal/builder/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "builder", - srcs = ["builder_utils.go"], - importpath = "github.com/pingcap/tidb/executor/internal/builder", - visibility = ["//executor:__subpackages__"], - deps = [ - "//distsql", - "//kv", - "//planner/core", - "//sessionctx", - "//util/timeutil", - "@com_github_pingcap_tipb//go-tipb", - ], -) diff --git a/executor/internal/calibrateresource/BUILD.bazel b/executor/internal/calibrateresource/BUILD.bazel deleted file mode 100644 index 59582d1ed38ce..0000000000000 --- a/executor/internal/calibrateresource/BUILD.bazel +++ /dev/null @@ -1,55 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "calibrateresource", - srcs = ["calibrate_resource.go"], - importpath = "github.com/pingcap/tidb/executor/internal/calibrateresource", - visibility = ["//executor:__subpackages__"], - deps = [ - "//domain", - "//executor/internal/exec", - "//infoschema", - "//kv", - "//parser/ast", - "//parser/duration", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn/staleread", - "//util/chunk", - "//util/mathutil", - "//util/sqlexec", - "@com_github_docker_go_units//:go-units", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_pd_client//resource_group/controller", - ], -) - -go_test( - name = "calibrateresource_test", - timeout = "short", - srcs = [ - "calibrate_resource_test.go", - "main_test.go", - ], - embed = [":calibrateresource"], - flaky = True, - deps = [ - "//config", - "//domain", - "//meta/autoid", - "//parser/mysql", - "//testkit", - "//testkit/testsetup", - "//types", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_pd_client//:client", - "@com_github_tikv_pd_client//resource_group/controller", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/internal/calibrateresource/main_test.go b/executor/internal/calibrateresource/main_test.go deleted file mode 100644 index 53ca420880a99..0000000000000 --- a/executor/internal/calibrateresource/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package calibrateresource - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/internal/exec/BUILD.bazel b/executor/internal/exec/BUILD.bazel deleted file mode 100644 index 0be250a2072dc..0000000000000 --- a/executor/internal/exec/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "exec", - srcs = ["executor.go"], - importpath = "github.com/pingcap/tidb/executor/internal/exec", - visibility = ["//executor:__subpackages__"], - deps = [ - "//domain", - "//expression", - "//sessionctx", - "//sessionctx/variable", - "//types", - "//util/chunk", - "//util/dbterror/exeerrors", - "//util/execdetails", - "//util/sqlexec", - "//util/topsql", - "//util/topsql/state", - "//util/tracing", - "@com_github_ngaut_pools//:pools", - ], -) diff --git a/executor/internal/exec/executor.go b/executor/internal/exec/executor.go deleted file mode 100644 index 68f5403b498d3..0000000000000 --- a/executor/internal/exec/executor.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package exec - -import ( - "context" - "fmt" - "sync/atomic" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/tracing" -) - -// Executor is the physical implementation of an algebra operator. -// -// In TiDB, all algebra operators are implemented as iterators, i.e., they -// support a simple Open-Next-Close protocol. See this paper for more details: -// -// "Volcano-An Extensible and Parallel Query Evaluation System" -// -// Different from Volcano's execution model, a "Next" function call in TiDB will -// return a batch of rows, other than a single row in Volcano. -// NOTE: Executors must call "chk.Reset()" before appending their results to it. -type Executor interface { - Base() *BaseExecutor - Open(context.Context) error - Next(ctx context.Context, req *chunk.Chunk) error - Close() error - Schema() *expression.Schema - RetFieldTypes() []*types.FieldType - InitCap() int - MaxChunkSize() int -} - -var _ Executor = &BaseExecutor{} - -// BaseExecutor holds common information for executors. -type BaseExecutor struct { - ctx sessionctx.Context - AllocPool chunk.Allocator - schema *expression.Schema // output schema - runtimeStats *execdetails.BasicRuntimeStats - children []Executor - retFieldTypes []*types.FieldType - id int - initCap int - maxChunkSize int -} - -// NewBaseExecutor creates a new BaseExecutor instance. -func NewBaseExecutor(ctx sessionctx.Context, schema *expression.Schema, id int, children ...Executor) BaseExecutor { - e := BaseExecutor{ - children: children, - ctx: ctx, - id: id, - schema: schema, - initCap: ctx.GetSessionVars().InitChunkSize, - maxChunkSize: ctx.GetSessionVars().MaxChunkSize, - AllocPool: ctx.GetSessionVars().ChunkPool.Alloc, - } - if ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { - if e.id > 0 { - e.runtimeStats = e.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.GetBasicRuntimeStats(id) - } - } - if schema != nil { - cols := schema.Columns - e.retFieldTypes = make([]*types.FieldType, len(cols)) - for i := range cols { - e.retFieldTypes[i] = cols[i].RetType - } - } - return e -} - -// RuntimeStats returns the runtime stats of an executor. -func (e *BaseExecutor) RuntimeStats() *execdetails.BasicRuntimeStats { - return e.runtimeStats -} - -// ID returns the id of an executor. -func (e *BaseExecutor) ID() int { - return e.id -} - -// AllChildren returns all children. -func (e *BaseExecutor) AllChildren() []Executor { - return e.children -} - -// ChildrenLen returns the length of children. -func (e *BaseExecutor) ChildrenLen() int { - return len(e.children) -} - -// EmptyChildren judges whether the children is empty. -func (e *BaseExecutor) EmptyChildren() bool { - return len(e.children) == 0 -} - -// SetChildren sets the children for an executor. -func (e *BaseExecutor) SetChildren(idx int, ex Executor) { - e.children[idx] = ex -} - -// Children returns the children for an executor. -func (e *BaseExecutor) Children(idx int) Executor { - return e.children[idx] -} - -// RetFieldTypes returns the return field types of an executor. -func (e *BaseExecutor) RetFieldTypes() []*types.FieldType { - return e.retFieldTypes -} - -// InitCap returns the initial capacity for chunk -func (e *BaseExecutor) InitCap() int { - return e.initCap -} - -// SetInitCap sets the initial capacity for chunk -func (e *BaseExecutor) SetInitCap(c int) { - e.initCap = c -} - -// MaxChunkSize returns the max chunk size. -func (e *BaseExecutor) MaxChunkSize() int { - return e.maxChunkSize -} - -// SetMaxChunkSize sets the max chunk size. -func (e *BaseExecutor) SetMaxChunkSize(size int) { - e.maxChunkSize = size -} - -// Base returns the BaseExecutor of an executor, don't override this method! -func (e *BaseExecutor) Base() *BaseExecutor { - return e -} - -// Open initializes children recursively and "childrenResults" according to children's schemas. -func (e *BaseExecutor) Open(ctx context.Context) error { - for _, child := range e.children { - err := child.Open(ctx) - if err != nil { - return err - } - } - return nil -} - -// Close closes all executors and release all resources. -func (e *BaseExecutor) Close() error { - var firstErr error - for _, src := range e.children { - if err := src.Close(); err != nil && firstErr == nil { - firstErr = err - } - } - return firstErr -} - -// Schema returns the current BaseExecutor's schema. If it is nil, then create and return a new one. -func (e *BaseExecutor) Schema() *expression.Schema { - if e.schema == nil { - return expression.NewSchema() - } - return e.schema -} - -// Next fills multiple rows into a chunk. -func (*BaseExecutor) Next(_ context.Context, _ *chunk.Chunk) error { - return nil -} - -// Ctx return ```sessionctx.Context``` of Executor -func (e *BaseExecutor) Ctx() sessionctx.Context { - return e.ctx -} - -// GetSchema gets the schema. -func (e *BaseExecutor) GetSchema() *expression.Schema { - return e.schema -} - -// UpdateDeltaForTableID updates the delta info for the table with tableID. -func (e *BaseExecutor) UpdateDeltaForTableID(id int64) { - txnCtx := e.ctx.GetSessionVars().TxnCtx - txnCtx.UpdateDeltaForTable(id, 0, 0, map[int64]int64{}) -} - -// GetSysSession gets a system session context from executor. -func (e *BaseExecutor) GetSysSession() (sessionctx.Context, error) { - dom := domain.GetDomain(e.Ctx()) - sysSessionPool := dom.SysSessionPool() - ctx, err := sysSessionPool.Get() - if err != nil { - return nil, err - } - restrictedCtx := ctx.(sessionctx.Context) - restrictedCtx.GetSessionVars().InRestrictedSQL = true - return restrictedCtx, nil -} - -// ReleaseSysSession releases a system session context to executor. -func (e *BaseExecutor) ReleaseSysSession(ctx context.Context, sctx sessionctx.Context) { - if sctx == nil { - return - } - dom := domain.GetDomain(e.Ctx()) - sysSessionPool := dom.SysSessionPool() - if _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "rollback"); err != nil { - sctx.(pools.Resource).Close() - return - } - sysSessionPool.Put(sctx.(pools.Resource)) -} - -// TryNewCacheChunk tries to get a cached chunk -func TryNewCacheChunk(e Executor) *chunk.Chunk { - base := e.Base() - s := base.Ctx().GetSessionVars() - return s.GetNewChunkWithCapacity(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize(), base.AllocPool) -} - -// RetTypes returns all output column types. -func RetTypes(e Executor) []*types.FieldType { - base := e.Base() - return base.RetFieldTypes() -} - -// NewFirstChunk creates a new chunk to buffer current executor's result. -func NewFirstChunk(e Executor) *chunk.Chunk { - base := e.Base() - return chunk.New(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize()) -} - -// Next is a wrapper function on e.Next(), it handles some common codes. -func Next(ctx context.Context, e Executor, req *chunk.Chunk) error { - base := e.Base() - if base.RuntimeStats() != nil { - start := time.Now() - defer func() { base.RuntimeStats().Record(time.Since(start), req.NumRows()) }() - } - sessVars := base.Ctx().GetSessionVars() - if atomic.LoadUint32(&sessVars.Killed) == 2 { - return exeerrors.ErrMaxExecTimeExceeded - } - if atomic.LoadUint32(&sessVars.Killed) == 1 { - return exeerrors.ErrQueryInterrupted - } - - r, ctx := tracing.StartRegionEx(ctx, fmt.Sprintf("%T.Next", e)) - defer r.End() - - if topsqlstate.TopSQLEnabled() && sessVars.StmtCtx.IsSQLAndPlanRegistered.CompareAndSwap(false, true) { - RegisterSQLAndPlanInExecForTopSQL(sessVars) - } - err := e.Next(ctx, req) - - if err != nil { - return err - } - // recheck whether the session/query is killed during the Next() - if atomic.LoadUint32(&sessVars.Killed) == 2 { - err = exeerrors.ErrMaxExecTimeExceeded - } - if atomic.LoadUint32(&sessVars.Killed) == 1 { - err = exeerrors.ErrQueryInterrupted - } - return err -} - -// RegisterSQLAndPlanInExecForTopSQL register the sql and plan information if it doesn't register before execution. -// This uses to catch the running SQL when Top SQL is enabled in execution. -func RegisterSQLAndPlanInExecForTopSQL(sessVars *variable.SessionVars) { - stmtCtx := sessVars.StmtCtx - normalizedSQL, sqlDigest := stmtCtx.SQLDigest() - topsql.RegisterSQL(normalizedSQL, sqlDigest, sessVars.InRestrictedSQL) - normalizedPlan, planDigest := stmtCtx.GetPlanDigest() - if len(normalizedPlan) > 0 { - topsql.RegisterPlan(normalizedPlan, planDigest) - } -} diff --git a/executor/internal/mpp/BUILD.bazel b/executor/internal/mpp/BUILD.bazel deleted file mode 100644 index f9a0c83e381a2..0000000000000 --- a/executor/internal/mpp/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mpp", - srcs = ["local_mpp_coordinator.go"], - importpath = "github.com/pingcap/tidb/executor/internal/mpp", - visibility = ["//executor:__subpackages__"], - deps = [ - "//config", - "//distsql", - "//executor/internal/builder", - "//executor/internal/util", - "//executor/metrics", - "//infoschema", - "//kv", - "//planner/core", - "//sessionctx", - "//sessionctx/variable", - "//store/copr", - "//store/driver/backoff", - "//store/driver/error", - "//util/execdetails", - "//util/logutil", - "//util/memory", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "mpp_test", - timeout = "short", - srcs = ["local_mpp_coordinator_test.go"], - embed = [":mpp"], - flaky = True, - deps = [ - "//planner/core", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - ], -) diff --git a/executor/internal/pdhelper/BUILD.bazel b/executor/internal/pdhelper/BUILD.bazel deleted file mode 100644 index 2dd165090b3f8..0000000000000 --- a/executor/internal/pdhelper/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "pdhelper", - srcs = ["pd.go"], - importpath = "github.com/pingcap/tidb/executor/internal/pdhelper", - visibility = ["//executor:__subpackages__"], - deps = [ - "//kv", - "//sessionctx", - "//store/helper", - "//util", - "//util/sqlexec", - "@com_github_jellydator_ttlcache_v3//:ttlcache", - "@com_github_pingcap_failpoint//:failpoint", - ], -) - -go_test( - name = "pdhelper_test", - timeout = "short", - srcs = [ - "main_test.go", - "pd_test.go", - ], - embed = [":pdhelper"], - flaky = True, - deps = [ - "//config", - "//meta/autoid", - "//sessionctx", - "//testkit/testsetup", - "@com_github_jellydator_ttlcache_v3//:ttlcache", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/internal/pdhelper/main_test.go b/executor/internal/pdhelper/main_test.go deleted file mode 100644 index e4aecc8f9bffa..0000000000000 --- a/executor/internal/pdhelper/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package pdhelper - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/internal/pdhelper/pd.go b/executor/internal/pdhelper/pd.go deleted file mode 100644 index 0e985ccccb8bd..0000000000000 --- a/executor/internal/pdhelper/pd.go +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package pdhelper - -import ( - "context" - "strconv" - "strings" - "sync" - "time" - - "github.com/jellydator/ttlcache/v3" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" -) - -// GlobalPDHelper is the global variable for PDHelper. -var GlobalPDHelper = defaultPDHelper() -var globalPDHelperOnce sync.Once - -// PDHelper is used to get some information from PD. -type PDHelper struct { - cacheForApproximateTableCountFromStorage *ttlcache.Cache[string, float64] - - getApproximateTableCountFromStorageFunc func(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) - wg util.WaitGroupWrapper -} - -func defaultPDHelper() *PDHelper { - cache := ttlcache.New[string, float64]( - ttlcache.WithTTL[string, float64](30*time.Second), - ttlcache.WithCapacity[string, float64](1024*1024), - ) - return &PDHelper{ - cacheForApproximateTableCountFromStorage: cache, - getApproximateTableCountFromStorageFunc: getApproximateTableCountFromStorage, - } -} - -// Start is used to start the background task of PDHelper. Currently, the background task is used to clean up TTL cache. -func (p *PDHelper) Start() { - globalPDHelperOnce.Do(func() { - p.wg.Run(p.cacheForApproximateTableCountFromStorage.Start) - }) -} - -// Stop stops the background task of PDHelper. -func (p *PDHelper) Stop() { - p.cacheForApproximateTableCountFromStorage.Stop() - p.wg.Wait() -} - -func approximateTableCountKey(tid int64, dbName, tableName, partitionName string) string { - return strings.Join([]string{strconv.FormatInt(tid, 10), dbName, tableName, partitionName}, "_") -} - -// GetApproximateTableCountFromStorage gets the approximate count of the table. -func (p *PDHelper) GetApproximateTableCountFromStorage(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) { - key := approximateTableCountKey(tid, dbName, tableName, partitionName) - if item := p.cacheForApproximateTableCountFromStorage.Get(key); item != nil { - return item.Value(), true - } - result, hasPD := p.getApproximateTableCountFromStorageFunc(sctx, tid, dbName, tableName, partitionName) - p.cacheForApproximateTableCountFromStorage.Set(key, result, ttlcache.DefaultTTL) - return result, hasPD -} - -func getApproximateTableCountFromStorage(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) { - tikvStore, ok := sctx.GetStore().(helper.Storage) - if !ok { - return 0, false - } - regionStats := &helper.PDRegionStats{} - pdHelper := helper.NewHelper(tikvStore) - err := pdHelper.GetPDRegionStats(tid, regionStats, true) - failpoint.Inject("calcSampleRateByStorageCount", func() { - // Force the TiDB thinking that there's PD and the count of region is small. - err = nil - regionStats.Count = 1 - // Set a very large approximate count. - regionStats.StorageKeys = 1000000 - }) - if err != nil { - return 0, false - } - // If this table is not small, we directly use the count from PD, - // since for a small table, it's possible that it's data is in the same region with part of another large table. - // Thus, we use the number of the regions of the table's table KV to decide whether the table is small. - if regionStats.Count > 2 { - return float64(regionStats.StorageKeys), true - } - // Otherwise, we use count(*) to calc it's size, since it's very small, the table data can be filled in no more than 2 regions. - sql := new(strings.Builder) - sqlexec.MustFormatSQL(sql, "select count(*) from %n.%n", dbName, tableName) - if partitionName != "" { - sqlexec.MustFormatSQL(sql, " partition(%n)", partitionName) - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, sql.String()) - if err != nil { - return 0, false - } - // If the record set is nil, there's something wrong with the execution. The COUNT(*) would always return one row. - if len(rows) == 0 || rows[0].Len() == 0 { - return 0, false - } - return float64(rows[0].GetInt64(0)), true -} diff --git a/executor/internal/querywatch/BUILD.bazel b/executor/internal/querywatch/BUILD.bazel deleted file mode 100644 index 76baf3c72794a..0000000000000 --- a/executor/internal/querywatch/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "querywatch", - srcs = ["query_watch.go"], - importpath = "github.com/pingcap/tidb/executor/internal/querywatch", - visibility = ["//executor:__subpackages__"], - deps = [ - "//domain", - "//domain/resourcegroup", - "//executor/internal/exec", - "//expression", - "//infoschema", - "//parser", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//util/chunk", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_tikv_pd_client//resource_group/controller", - ], -) - -go_test( - name = "querywatch_test", - timeout = "short", - srcs = [ - "main_test.go", - "query_watch_test.go", - ], - embed = [":querywatch"], - flaky = True, - deps = [ - "//config", - "//errno", - "//kv", - "//meta/autoid", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/internal/querywatch/main_test.go b/executor/internal/querywatch/main_test.go deleted file mode 100644 index bc73101fc1a44..0000000000000 --- a/executor/internal/querywatch/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package querywatch - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/internal/testkit.go b/executor/internal/testkit.go deleted file mode 100644 index 0f11735e44158..0000000000000 --- a/executor/internal/testkit.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "fmt" - - "github.com/pingcap/tidb/testkit" -) - -// FillData fill data into table -func FillData(tk *testkit.TestKit, table string) { - tk.MustExec("use test") - tk.MustExec(fmt.Sprintf("create table %s(id int not null default 1, name varchar(255), PRIMARY KEY(id));", table)) - - // insert data - tk.MustExec(fmt.Sprintf("insert INTO %s VALUES (1, \"hello\");", table)) - tk.MustExec(fmt.Sprintf("insert into %s values (2, \"hello\");", table)) -} diff --git a/executor/internal/util/BUILD.bazel b/executor/internal/util/BUILD.bazel deleted file mode 100644 index 94dc9d923c2b9..0000000000000 --- a/executor/internal/util/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "util", - srcs = [ - "partition_table.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/executor/internal/util", - visibility = ["//executor:__subpackages__"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_tipb//go-tipb", - ], -) diff --git a/executor/internal/vecgroupchecker/BUILD.bazel b/executor/internal/vecgroupchecker/BUILD.bazel deleted file mode 100644 index 4b2085263884a..0000000000000 --- a/executor/internal/vecgroupchecker/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "vecgroupchecker", - srcs = ["vec_group_checker.go"], - importpath = "github.com/pingcap/tidb/executor/internal/vecgroupchecker", - visibility = ["//executor:__subpackages__"], - deps = [ - "//expression", - "//sessionctx", - "//types", - "//util/chunk", - "//util/codec", - ], -) - -go_test( - name = "vecgroupchecker_test", - timeout = "short", - srcs = [ - "main_test.go", - "vec_group_checker_test.go", - ], - embed = [":vecgroupchecker"], - flaky = True, - shard_count = 3, - deps = [ - "//config", - "//expression", - "//meta/autoid", - "//parser/mysql", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/mock", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/internal/vecgroupchecker/main_test.go b/executor/internal/vecgroupchecker/main_test.go deleted file mode 100644 index 115de76703aaa..0000000000000 --- a/executor/internal/vecgroupchecker/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package vecgroupchecker - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/join.go b/executor/join.go deleted file mode 100644 index b49cbab10fc21..0000000000000 --- a/executor/join.go +++ /dev/null @@ -1,1660 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "context" - "fmt" - "runtime/trace" - "strconv" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/applycache" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/bitmap" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/memory" -) - -var ( - _ exec.Executor = &HashJoinExec{} - _ exec.Executor = &NestedLoopApplyExec{} -) - -type hashJoinCtx struct { - sessCtx sessionctx.Context - allocPool chunk.Allocator - // concurrency is the number of partition, build and join workers. - concurrency uint - joinResultCh chan *hashjoinWorkerResult - // closeCh add a lock for closing executor. - closeCh chan struct{} - finished atomic.Bool - useOuterToBuild bool - isOuterJoin bool - isNullEQ []bool - buildFinished chan error - rowContainer *hashRowContainer - joinType plannercore.JoinType - outerMatchedStatus []*bitmap.ConcurrentBitmap - stats *hashJoinRuntimeStats - probeTypes []*types.FieldType - buildTypes []*types.FieldType - outerFilter expression.CNFExprs - isNullAware bool - memTracker *memory.Tracker // track memory usage. - diskTracker *disk.Tracker // track disk usage. -} - -// probeSideTupleFetcher reads tuples from probeSideExec and send them to probeWorkers. -type probeSideTupleFetcher struct { - *hashJoinCtx - - probeSideExec exec.Executor - probeChkResourceCh chan *probeChkResource - probeResultChs []chan *chunk.Chunk - requiredRows int64 -} - -type probeWorker struct { - hashJoinCtx *hashJoinCtx - workerID uint - - probeKeyColIdx []int - probeNAKeyColIdx []int - // We pre-alloc and reuse the Rows and RowPtrs for each probe goroutine, to avoid allocation frequently - buildSideRows []chunk.Row - buildSideRowPtrs []chunk.RowPtr - - // We build individual joiner for each join worker when use chunk-based - // execution, to avoid the concurrency of joiner.chk and joiner.selected. - joiner joiner - rowIters *chunk.Iterator4Slice - rowContainerForProbe *hashRowContainer - // for every naaj probe worker, pre-allocate the int slice for store the join column index to check. - needCheckBuildColPos []int - needCheckProbeColPos []int - needCheckBuildTypes []*types.FieldType - needCheckProbeTypes []*types.FieldType - probeChkResourceCh chan *probeChkResource - joinChkResourceCh chan *chunk.Chunk - probeResultCh chan *chunk.Chunk -} - -type buildWorker struct { - hashJoinCtx *hashJoinCtx - buildSideExec exec.Executor - buildKeyColIdx []int - buildNAKeyColIdx []int -} - -// HashJoinExec implements the hash join algorithm. -type HashJoinExec struct { - exec.BaseExecutor - *hashJoinCtx - - probeSideTupleFetcher *probeSideTupleFetcher - probeWorkers []*probeWorker - buildWorker *buildWorker - - workerWg util.WaitGroupWrapper - waiterWg util.WaitGroupWrapper - - prepared bool -} - -// probeChkResource stores the result of the join probe side fetch worker, -// `dest` is for Chunk reuse: after join workers process the probe side chunk which is read from `dest`, -// they'll store the used chunk as `chk`, and then the probe side fetch worker will put new data into `chk` and write `chk` into dest. -type probeChkResource struct { - chk *chunk.Chunk - dest chan<- *chunk.Chunk -} - -// hashjoinWorkerResult stores the result of join workers, -// `src` is for Chunk reuse: the main goroutine will get the join result chunk `chk`, -// and push `chk` into `src` after processing, join worker goroutines get the empty chunk from `src` -// and push new data into this chunk. -type hashjoinWorkerResult struct { - chk *chunk.Chunk - err error - src chan<- *chunk.Chunk -} - -// Close implements the Executor Close interface. -func (e *HashJoinExec) Close() error { - if e.closeCh != nil { - close(e.closeCh) - } - e.finished.Store(true) - if e.prepared { - if e.buildFinished != nil { - channel.Clear(e.buildFinished) - } - if e.joinResultCh != nil { - channel.Clear(e.joinResultCh) - } - if e.probeSideTupleFetcher.probeChkResourceCh != nil { - close(e.probeSideTupleFetcher.probeChkResourceCh) - channel.Clear(e.probeSideTupleFetcher.probeChkResourceCh) - } - for i := range e.probeSideTupleFetcher.probeResultChs { - channel.Clear(e.probeSideTupleFetcher.probeResultChs[i]) - } - for i := range e.probeWorkers { - close(e.probeWorkers[i].joinChkResourceCh) - channel.Clear(e.probeWorkers[i].joinChkResourceCh) - } - e.probeSideTupleFetcher.probeChkResourceCh = nil - terror.Call(e.rowContainer.Close) - e.waiterWg.Wait() - } - e.outerMatchedStatus = e.outerMatchedStatus[:0] - for _, w := range e.probeWorkers { - w.buildSideRows = nil - w.buildSideRowPtrs = nil - w.needCheckBuildColPos = nil - w.needCheckProbeColPos = nil - w.needCheckBuildTypes = nil - w.needCheckProbeTypes = nil - w.joinChkResourceCh = nil - } - - if e.stats != nil && e.rowContainer != nil { - e.stats.hashStat = *e.rowContainer.stat - } - if e.stats != nil { - defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) - } - err := e.BaseExecutor.Close() - return err -} - -// Open implements the Executor Open interface. -func (e *HashJoinExec) Open(ctx context.Context) error { - if err := e.BaseExecutor.Open(ctx); err != nil { - e.closeCh = nil - e.prepared = false - return err - } - e.prepared = false - if e.hashJoinCtx.memTracker != nil { - e.hashJoinCtx.memTracker.Reset() - } else { - e.hashJoinCtx.memTracker = memory.NewTracker(e.ID(), -1) - } - e.hashJoinCtx.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - - e.diskTracker = disk.NewTracker(e.ID(), -1) - e.diskTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.DiskTracker) - - e.workerWg = util.WaitGroupWrapper{} - e.waiterWg = util.WaitGroupWrapper{} - e.closeCh = make(chan struct{}) - e.finished.Store(false) - - if e.RuntimeStats() != nil { - e.stats = &hashJoinRuntimeStats{ - concurrent: int(e.concurrency), - } - } - return nil -} - -// fetchProbeSideChunks get chunks from fetches chunks from the big table in a background goroutine -// and sends the chunks to multiple channels which will be read by multiple join workers. -func (fetcher *probeSideTupleFetcher) fetchProbeSideChunks(ctx context.Context, maxChunkSize int) { - hasWaitedForBuild := false - for { - if fetcher.finished.Load() { - return - } - - var probeSideResource *probeChkResource - var ok bool - select { - case <-fetcher.closeCh: - return - case probeSideResource, ok = <-fetcher.probeChkResourceCh: - if !ok { - return - } - } - probeSideResult := probeSideResource.chk - if fetcher.isOuterJoin { - required := int(atomic.LoadInt64(&fetcher.requiredRows)) - probeSideResult.SetRequiredRows(required, maxChunkSize) - } - err := exec.Next(ctx, fetcher.probeSideExec, probeSideResult) - failpoint.Inject("ConsumeRandomPanic", nil) - if err != nil { - fetcher.joinResultCh <- &hashjoinWorkerResult{ - err: err, - } - return - } - if !hasWaitedForBuild { - failpoint.Inject("issue30289", func(val failpoint.Value) { - if val.(bool) { - probeSideResult.Reset() - } - }) - if probeSideResult.NumRows() == 0 && !fetcher.useOuterToBuild { - fetcher.finished.Store(true) - } - emptyBuild, buildErr := fetcher.wait4BuildSide() - if buildErr != nil { - fetcher.joinResultCh <- &hashjoinWorkerResult{ - err: buildErr, - } - return - } else if emptyBuild { - return - } - hasWaitedForBuild = true - } - - if probeSideResult.NumRows() == 0 { - return - } - - probeSideResource.dest <- probeSideResult - } -} - -func (fetcher *probeSideTupleFetcher) wait4BuildSide() (emptyBuild bool, err error) { - select { - case <-fetcher.closeCh: - return true, nil - case err := <-fetcher.buildFinished: - if err != nil { - return false, err - } - } - if fetcher.rowContainer.Len() == uint64(0) && (fetcher.joinType == plannercore.InnerJoin || fetcher.joinType == plannercore.SemiJoin) { - return true, nil - } - return false, nil -} - -// fetchBuildSideRows fetches all rows from build side executor, and append them -// to e.buildSideResult. -func (w *buildWorker) fetchBuildSideRows(ctx context.Context, chkCh chan<- *chunk.Chunk, errCh chan<- error, doneCh <-chan struct{}) { - defer close(chkCh) - var err error - failpoint.Inject("issue30289", func(val failpoint.Value) { - if val.(bool) { - err = errors.Errorf("issue30289 build return error") - errCh <- errors.Trace(err) - return - } - }) - failpoint.Inject("issue42662_1", func(val failpoint.Value) { - if val.(bool) { - if w.hashJoinCtx.sessCtx.GetSessionVars().ConnectionID != 0 { - // consume 170MB memory, this sql should be tracked into MemoryTop1Tracker - w.hashJoinCtx.memTracker.Consume(170 * 1024 * 1024) - } - return - } - }) - sessVars := w.hashJoinCtx.sessCtx.GetSessionVars() - for { - if w.hashJoinCtx.finished.Load() { - return - } - chk := sessVars.GetNewChunkWithCapacity(w.buildSideExec.Base().RetFieldTypes(), sessVars.MaxChunkSize, sessVars.MaxChunkSize, w.hashJoinCtx.allocPool) - err = exec.Next(ctx, w.buildSideExec, chk) - if err != nil { - errCh <- errors.Trace(err) - return - } - failpoint.Inject("errorFetchBuildSideRowsMockOOMPanic", nil) - failpoint.Inject("ConsumeRandomPanic", nil) - if chk.NumRows() == 0 { - return - } - select { - case <-doneCh: - return - case <-w.hashJoinCtx.closeCh: - return - case chkCh <- chk: - } - } -} - -func (e *HashJoinExec) initializeForProbe() { - // e.joinResultCh is for transmitting the join result chunks to the main - // thread. - e.joinResultCh = make(chan *hashjoinWorkerResult, e.concurrency+1) - - e.probeSideTupleFetcher.hashJoinCtx = e.hashJoinCtx - // e.probeSideTupleFetcher.probeResultChs is for transmitting the chunks which store the data of - // probeSideExec, it'll be written by probe side worker goroutine, and read by join - // workers. - e.probeSideTupleFetcher.probeResultChs = make([]chan *chunk.Chunk, e.concurrency) - for i := uint(0); i < e.concurrency; i++ { - e.probeSideTupleFetcher.probeResultChs[i] = make(chan *chunk.Chunk, 1) - e.probeWorkers[i].probeResultCh = e.probeSideTupleFetcher.probeResultChs[i] - } - - // e.probeChkResourceCh is for transmitting the used probeSideExec chunks from - // join workers to probeSideExec worker. - e.probeSideTupleFetcher.probeChkResourceCh = make(chan *probeChkResource, e.concurrency) - for i := uint(0); i < e.concurrency; i++ { - e.probeSideTupleFetcher.probeChkResourceCh <- &probeChkResource{ - chk: exec.NewFirstChunk(e.probeSideTupleFetcher.probeSideExec), - dest: e.probeSideTupleFetcher.probeResultChs[i], - } - } - - // e.probeWorker.joinChkResourceCh is for transmitting the reused join result chunks - // from the main thread to probe worker goroutines. - for i := uint(0); i < e.concurrency; i++ { - e.probeWorkers[i].joinChkResourceCh = make(chan *chunk.Chunk, 1) - e.probeWorkers[i].joinChkResourceCh <- exec.NewFirstChunk(e) - e.probeWorkers[i].probeChkResourceCh = e.probeSideTupleFetcher.probeChkResourceCh - } -} - -func (e *HashJoinExec) fetchAndProbeHashTable(ctx context.Context) { - e.initializeForProbe() - e.workerWg.RunWithRecover(func() { - defer trace.StartRegion(ctx, "HashJoinProbeSideFetcher").End() - e.probeSideTupleFetcher.fetchProbeSideChunks(ctx, e.MaxChunkSize()) - }, e.probeSideTupleFetcher.handleProbeSideFetcherPanic) - - for i := uint(0); i < e.concurrency; i++ { - workerID := i - e.workerWg.RunWithRecover(func() { - defer trace.StartRegion(ctx, "HashJoinWorker").End() - e.probeWorkers[workerID].runJoinWorker() - }, e.probeWorkers[workerID].handleProbeWorkerPanic) - } - e.waiterWg.RunWithRecover(e.waitJoinWorkersAndCloseResultChan, nil) -} - -func (fetcher *probeSideTupleFetcher) handleProbeSideFetcherPanic(r interface{}) { - for i := range fetcher.probeResultChs { - close(fetcher.probeResultChs[i]) - } - if r != nil { - fetcher.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("%v", r)} - } -} - -func (w *probeWorker) handleProbeWorkerPanic(r interface{}) { - if r != nil { - w.hashJoinCtx.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("probeWorker[%d] meets error: %v", w.workerID, r)} - } -} - -func (e *HashJoinExec) handleJoinWorkerPanic(r interface{}) { - if r != nil { - e.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("%v", r)} - } -} - -// Concurrently handling unmatched rows from the hash table -func (w *probeWorker) handleUnmatchedRowsFromHashTable() { - ok, joinResult := w.getNewJoinResult() - if !ok { - return - } - numChks := w.rowContainerForProbe.NumChunks() - for i := int(w.workerID); i < numChks; i += int(w.hashJoinCtx.concurrency) { - chk, err := w.rowContainerForProbe.GetChunk(i) - if err != nil { - // Catching the error and send it - joinResult.err = err - w.hashJoinCtx.joinResultCh <- joinResult - return - } - for j := 0; j < chk.NumRows(); j++ { - if !w.hashJoinCtx.outerMatchedStatus[i].UnsafeIsSet(j) { // process unmatched outer rows - w.joiner.onMissMatch(false, chk.GetRow(j), joinResult.chk) - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return - } - } - } - } - - if joinResult == nil { - return - } else if joinResult.err != nil || (joinResult.chk != nil && joinResult.chk.NumRows() > 0) { - w.hashJoinCtx.joinResultCh <- joinResult - } -} - -func (e *HashJoinExec) waitJoinWorkersAndCloseResultChan() { - e.workerWg.Wait() - if e.useOuterToBuild { - // Concurrently handling unmatched rows from the hash table at the tail - for i := uint(0); i < e.concurrency; i++ { - var workerID = i - e.workerWg.RunWithRecover(func() { e.probeWorkers[workerID].handleUnmatchedRowsFromHashTable() }, e.handleJoinWorkerPanic) - } - e.workerWg.Wait() - } - close(e.joinResultCh) -} - -func (w *probeWorker) runJoinWorker() { - probeTime := int64(0) - if w.hashJoinCtx.stats != nil { - start := time.Now() - defer func() { - t := time.Since(start) - atomic.AddInt64(&w.hashJoinCtx.stats.probe, probeTime) - atomic.AddInt64(&w.hashJoinCtx.stats.fetchAndProbe, int64(t)) - w.hashJoinCtx.stats.setMaxFetchAndProbeTime(int64(t)) - }() - } - - var ( - probeSideResult *chunk.Chunk - selected = make([]bool, 0, chunk.InitialCapacity) - ) - ok, joinResult := w.getNewJoinResult() - if !ok { - return - } - - // Read and filter probeSideResult, and join the probeSideResult with the build side rows. - emptyProbeSideResult := &probeChkResource{ - dest: w.probeResultCh, - } - hCtx := &hashContext{ - allTypes: w.hashJoinCtx.probeTypes, - keyColIdx: w.probeKeyColIdx, - naKeyColIdx: w.probeNAKeyColIdx, - } - for ok := true; ok; { - if w.hashJoinCtx.finished.Load() { - break - } - select { - case <-w.hashJoinCtx.closeCh: - return - case probeSideResult, ok = <-w.probeResultCh: - } - failpoint.Inject("ConsumeRandomPanic", nil) - if !ok { - break - } - start := time.Now() - if w.hashJoinCtx.useOuterToBuild { - ok, joinResult = w.join2ChunkForOuterHashJoin(probeSideResult, hCtx, joinResult) - } else { - ok, joinResult = w.join2Chunk(probeSideResult, hCtx, joinResult, selected) - } - probeTime += int64(time.Since(start)) - if !ok { - break - } - probeSideResult.Reset() - emptyProbeSideResult.chk = probeSideResult - w.probeChkResourceCh <- emptyProbeSideResult - } - // note joinResult.chk may be nil when getNewJoinResult fails in loops - if joinResult == nil { - return - } else if joinResult.err != nil || (joinResult.chk != nil && joinResult.chk.NumRows() > 0) { - w.hashJoinCtx.joinResultCh <- joinResult - } else if joinResult.chk != nil && joinResult.chk.NumRows() == 0 { - w.joinChkResourceCh <- joinResult.chk - } -} - -func (w *probeWorker) joinMatchedProbeSideRow2ChunkForOuterHashJoin(probeKey uint64, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { - var err error - w.buildSideRows, w.buildSideRowPtrs, err = w.rowContainerForProbe.GetMatchedRowsAndPtrs(probeKey, probeSideRow, hCtx, w.buildSideRows, w.buildSideRowPtrs, true) - buildSideRows, rowsPtrs := w.buildSideRows, w.buildSideRowPtrs - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - return true, joinResult - } - - iter := w.rowIters - iter.Reset(buildSideRows) - var outerMatchStatus []outerRowStatusFlag - rowIdx, ok := 0, false - for iter.Begin(); iter.Current() != iter.End(); { - outerMatchStatus, err = w.joiner.tryToMatchOuters(iter, probeSideRow, joinResult.chk, outerMatchStatus) - if err != nil { - joinResult.err = err - return false, joinResult - } - for i := range outerMatchStatus { - if outerMatchStatus[i] == outerRowMatched { - w.hashJoinCtx.outerMatchedStatus[rowsPtrs[rowIdx+i].ChkIdx].Set(int(rowsPtrs[rowIdx+i].RowIdx)) - } - } - rowIdx += len(outerMatchStatus) - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - return true, joinResult -} - -// joinNAALOSJMatchProbeSideRow2Chunk implement the matching logic for NA-AntiLeftOuterSemiJoin -func (w *probeWorker) joinNAALOSJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { - var ( - err error - ok bool - ) - if probeKeyNullBits == nil { - // step1: match the same key bucket first. - // because AntiLeftOuterSemiJoin cares about the scalar value. If we both have a match from null - // bucket and same key bucket, we should return the result as from same-key bucket - // rather than from null bucket. - w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) - buildSideRows := w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) != 0 { - iter1 := w.rowIters - iter1.Reset(buildSideRows) - for iter1.Begin(); iter1.Current() != iter1.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk, LeftNotNullRightNotNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid same-key bucket row from right side. - // as said in the comment, once we meet a same key (NOT IN semantic) in CNF, we can determine the result as . - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - } - // step2: match the null bucket secondly. - w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows = w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - // when reach here, it means we couldn't find a valid same key match from same-key bucket yet - // and the null bucket is empty. so the result should be . - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - iter2 := w.rowIters - iter2.Reset(buildSideRows) - for iter2.Begin(); iter2.Current() != iter2.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk, LeftNotNullRightHasNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid null bucket row from right side. - // as said in the comment, once we meet a null in CNF, we can determine the result as . - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - // step3: if we couldn't return it quickly in null bucket and same key bucket, here means two cases: - // case1: x NOT IN (empty set): if other key bucket don't have the valid rows yet. - // case2: x NOT IN (l,m,n...): if other key bucket do have the valid rows. - // both cases mean the result should be - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - // when left side has null values, all we want is to find a valid build side rows (past other condition) - // so we can return it as soon as possible. here means two cases: - // case1: NOT IN (empty set): ----------------------> result is . - // case2: NOT IN (at least a valid inner row) ------------------> result is . - // Step1: match null bucket (assumption that null bucket is quite smaller than all hash table bucket rows) - w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows := w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) != 0 { - iter1 := w.rowIters - iter1.Reset(buildSideRows) - for iter1.Begin(); iter1.Current() != iter1.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk, LeftHasNullRightHasNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid null bucket row from right side. (not empty) - // as said in the comment, once we found at least a valid row, we can determine the result as . - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - } - // Step2: match all hash table bucket build rows (use probeKeyNullBits to filter if any). - w.buildSideRows, err = w.rowContainerForProbe.GetAllMatchedRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows = w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, - // which means x NOT IN (empty set) or x NOT IN (l,m,n), the result should be - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - iter2 := w.rowIters - iter2.Reset(buildSideRows) - for iter2.Begin(); iter2.Current() != iter2.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk, LeftHasNullRightNotNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid same key bucket row from right side. (not empty) - // as said in the comment, once we found at least a valid row, we can determine the result as . - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - // step3: if we couldn't return it quickly in null bucket and all hash bucket, here means only one cases: - // case1: NOT IN (empty set): - // empty set comes from no rows from all bucket can pass other condition. the result should be - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult -} - -// joinNAASJMatchProbeSideRow2Chunk implement the matching logic for NA-AntiSemiJoin -func (w *probeWorker) joinNAASJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { - var ( - err error - ok bool - ) - if probeKeyNullBits == nil { - // step1: match null bucket first. - // need fetch the "valid" rows every time. (nullBits map check is necessary) - w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows := w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) != 0 { - iter1 := w.rowIters - iter1.Reset(buildSideRows) - for iter1.Begin(); iter1.Current() != iter1.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid null bucket row from right side. - // as said in the comment, once we meet a rhs null in CNF, we can determine the reject of lhs row. - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - } - // step2: then same key bucket. - w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) - buildSideRows = w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, - // which means x NOT IN (empty set), accept the rhs row. - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - iter2 := w.rowIters - iter2.Reset(buildSideRows) - for iter2.Begin(); iter2.Current() != iter2.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid same key bucket row from right side. - // as said in the comment, once we meet a false in CNF, we can determine the reject of lhs row. - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - // step3: if we couldn't return it quickly in null bucket and same key bucket, here means two cases: - // case1: x NOT IN (empty set): if other key bucket don't have the valid rows yet. - // case2: x NOT IN (l,m,n...): if other key bucket do have the valid rows. - // both cases should accept the rhs row. - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - // when left side has null values, all we want is to find a valid build side rows (passed from other condition) - // so we can return it as soon as possible. here means two cases: - // case1: NOT IN (empty set): ----------------------> accept rhs row. - // case2: NOT IN (at least a valid inner row) ------------------> unknown result, refuse rhs row. - // Step1: match null bucket (assumption that null bucket is quite smaller than all hash table bucket rows) - w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows := w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) != 0 { - iter1 := w.rowIters - iter1.Reset(buildSideRows) - for iter1.Begin(); iter1.Current() != iter1.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid null bucket row from right side. (not empty) - // as said in the comment, once we found at least a valid row, we can determine the reject of lhs row. - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - } - // Step2: match all hash table bucket build rows. - w.buildSideRows, err = w.rowContainerForProbe.GetAllMatchedRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) - buildSideRows = w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, - // which means NOT IN (empty set) or NOT IN (no valid rows) accept the rhs row. - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - iter2 := w.rowIters - iter2.Reset(buildSideRows) - for iter2.Begin(); iter2.Current() != iter2.End(); { - matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk) - if err != nil { - joinResult.err = err - return false, joinResult - } - // here matched means: there is a valid key row from right side. (not empty) - // as said in the comment, once we found at least a valid row, we can determine the reject of lhs row. - if matched { - return true, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - // step3: if we couldn't return it quickly in null bucket and all hash bucket, here means only one cases: - // case1: NOT IN (empty set): - // empty set comes from no rows from all bucket can pass other condition. we should accept the rhs row. - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult -} - -// joinNAAJMatchProbeSideRow2Chunk implement the matching priority logic for NA-AntiSemiJoin and NA-AntiLeftOuterSemiJoin -// there are some bucket-matching priority difference between them. -// -// Since NA-AntiSemiJoin don't need to append the scalar value with the left side row, there is a quick matching path. -// 1: lhs row has null: -// lhs row has null can't determine its result in advance, we should judge whether the right valid set is empty -// or not. For semantic like x NOT IN(y set), If y set is empty, the scalar result is 1; Otherwise, the result -// is 0. Since NA-AntiSemiJoin don't care about the scalar value, we just try to find a valid row from right side, -// once we found it then just return the left side row instantly. (same as NA-AntiLeftOuterSemiJoin) -// -// 2: lhs row without null: -// same-key bucket and null-bucket which should be the first to match? For semantic like x NOT IN(y set), once y -// set has a same key x, the scalar value is 0; else if y set has a null key, then the scalar value is null. Both -// of them lead the refuse of the lhs row without any difference. Since NA-AntiSemiJoin don't care about the scalar -// value, we can just match the null bucket first and refuse the lhs row as quickly as possible, because a null of -// yi in the CNF (x NA-EQ yi) can always determine a negative value (refuse lhs row) in advance here. -// -// For NA-AntiLeftOuterSemiJoin, we couldn't match null-bucket first, because once y set has a same key x and null -// key, we should return the result as left side row appended with a scalar value 0 which is from same key matching failure. -func (w *probeWorker) joinNAAJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { - naAntiSemiJoin := w.hashJoinCtx.joinType == plannercore.AntiSemiJoin && w.hashJoinCtx.isNullAware - naAntiLeftOuterSemiJoin := w.hashJoinCtx.joinType == plannercore.AntiLeftOuterSemiJoin && w.hashJoinCtx.isNullAware - if naAntiSemiJoin { - return w.joinNAASJMatchProbeSideRow2Chunk(probeKey, probeKeyNullBits, probeSideRow, hCtx, joinResult) - } - if naAntiLeftOuterSemiJoin { - return w.joinNAALOSJMatchProbeSideRow2Chunk(probeKey, probeKeyNullBits, probeSideRow, hCtx, joinResult) - } - // shouldn't be here, not a valid NAAJ. - return false, joinResult -} - -func (w *probeWorker) joinMatchedProbeSideRow2Chunk(probeKey uint64, probeSideRow chunk.Row, hCtx *hashContext, - joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { - var err error - w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) - buildSideRows := w.buildSideRows - if err != nil { - joinResult.err = err - return false, joinResult - } - if len(buildSideRows) == 0 { - w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) - return true, joinResult - } - iter := w.rowIters - iter.Reset(buildSideRows) - hasMatch, hasNull, ok := false, false, false - for iter.Begin(); iter.Current() != iter.End(); { - matched, isNull, err := w.joiner.tryToMatchInners(probeSideRow, iter, joinResult.chk) - if err != nil { - joinResult.err = err - return false, joinResult - } - hasMatch = hasMatch || matched - hasNull = hasNull || isNull - - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - if !hasMatch { - w.joiner.onMissMatch(hasNull, probeSideRow, joinResult.chk) - } - return true, joinResult -} - -func (w *probeWorker) getNewJoinResult() (bool, *hashjoinWorkerResult) { - joinResult := &hashjoinWorkerResult{ - src: w.joinChkResourceCh, - } - ok := true - select { - case <-w.hashJoinCtx.closeCh: - ok = false - case joinResult.chk, ok = <-w.joinChkResourceCh: - } - return ok, joinResult -} - -func (w *probeWorker) join2Chunk(probeSideChk *chunk.Chunk, hCtx *hashContext, joinResult *hashjoinWorkerResult, - selected []bool) (ok bool, _ *hashjoinWorkerResult) { - var err error - selected, err = expression.VectorizedFilter(w.hashJoinCtx.sessCtx, w.hashJoinCtx.outerFilter, chunk.NewIterator4Chunk(probeSideChk), selected) - if err != nil { - joinResult.err = err - return false, joinResult - } - - numRows := probeSideChk.NumRows() - hCtx.initHash(numRows) - // By now, path 1 and 2 won't be conducted at the same time. - // 1: write the row data of join key to hashVals. (normal EQ key should ignore the null values.) null-EQ for Except statement is an exception. - for keyIdx, i := range hCtx.keyColIdx { - ignoreNull := len(w.hashJoinCtx.isNullEQ) > keyIdx && w.hashJoinCtx.isNullEQ[keyIdx] - err = codec.HashChunkSelected(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull, selected, ignoreNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - } - // 2: write the row data of NA join key to hashVals. (NA EQ key should collect all row including null value, store null value in a special position) - isNAAJ := len(hCtx.naKeyColIdx) > 0 - for keyIdx, i := range hCtx.naKeyColIdx { - // NAAJ won't ignore any null values, but collect them up to probe. - err = codec.HashChunkSelected(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull, selected, false) - if err != nil { - joinResult.err = err - return false, joinResult - } - // after fetch one NA column, collect the null value to null bitmap for every row. (use hasNull flag to accelerate) - // eg: if a NA Join cols is (a, b, c), for every build row here we maintained a 3-bit map to mark which column is null for them. - for rowIdx := 0; rowIdx < numRows; rowIdx++ { - if hCtx.hasNull[rowIdx] { - hCtx.naColNullBitMap[rowIdx].UnsafeSet(keyIdx) - // clean and try fetch next NA join col. - hCtx.hasNull[rowIdx] = false - hCtx.naHasNull[rowIdx] = true - } - } - } - - for i := range selected { - killed := atomic.LoadUint32(&w.hashJoinCtx.sessCtx.GetSessionVars().Killed) == 1 - failpoint.Inject("killedInJoin2Chunk", func(val failpoint.Value) { - if val.(bool) { - killed = true - } - }) - if killed { - joinResult.err = exeerrors.ErrQueryInterrupted - return false, joinResult - } - if isNAAJ { - if !selected[i] { - // since this is the case of using inner to build, so for an outer row unselected, we should fill the result when it's outer join. - w.joiner.onMissMatch(false, probeSideChk.GetRow(i), joinResult.chk) - } - if hCtx.naHasNull[i] { - // here means the probe join connecting column has null value in it and this is special for matching all the hash buckets - // for it. (probeKey is not necessary here) - probeRow := probeSideChk.GetRow(i) - ok, joinResult = w.joinNAAJMatchProbeSideRow2Chunk(0, hCtx.naColNullBitMap[i].Clone(), probeRow, hCtx, joinResult) - if !ok { - return false, joinResult - } - } else { - // here means the probe join connecting column without null values, where we should match same key bucket and null bucket for it at its order. - // step1: process same key matched probe side rows - probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) - ok, joinResult = w.joinNAAJMatchProbeSideRow2Chunk(probeKey, nil, probeRow, hCtx, joinResult) - if !ok { - return false, joinResult - } - } - } else { - // since this is the case of using inner to build, so for an outer row unselected, we should fill the result when it's outer join. - if !selected[i] || hCtx.hasNull[i] { // process unmatched probe side rows - w.joiner.onMissMatch(false, probeSideChk.GetRow(i), joinResult.chk) - } else { // process matched probe side rows - probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) - ok, joinResult = w.joinMatchedProbeSideRow2Chunk(probeKey, probeRow, hCtx, joinResult) - if !ok { - return false, joinResult - } - } - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - return true, joinResult -} - -// join2ChunkForOuterHashJoin joins chunks when using the outer to build a hash table (refer to outer hash join) -func (w *probeWorker) join2ChunkForOuterHashJoin(probeSideChk *chunk.Chunk, hCtx *hashContext, joinResult *hashjoinWorkerResult) (ok bool, _ *hashjoinWorkerResult) { - hCtx.initHash(probeSideChk.NumRows()) - for keyIdx, i := range hCtx.keyColIdx { - err := codec.HashChunkColumns(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull) - if err != nil { - joinResult.err = err - return false, joinResult - } - } - for i := 0; i < probeSideChk.NumRows(); i++ { - killed := atomic.LoadUint32(&w.hashJoinCtx.sessCtx.GetSessionVars().Killed) == 1 - failpoint.Inject("killedInJoin2ChunkForOuterHashJoin", func(val failpoint.Value) { - if val.(bool) { - killed = true - } - }) - if killed { - joinResult.err = exeerrors.ErrQueryInterrupted - return false, joinResult - } - probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) - ok, joinResult = w.joinMatchedProbeSideRow2ChunkForOuterHashJoin(probeKey, probeRow, hCtx, joinResult) - if !ok { - return false, joinResult - } - if joinResult.chk.IsFull() { - w.hashJoinCtx.joinResultCh <- joinResult - ok, joinResult = w.getNewJoinResult() - if !ok { - return false, joinResult - } - } - } - return true, joinResult -} - -// Next implements the Executor Next interface. -// hash join constructs the result following these steps: -// step 1. fetch data from build side child and build a hash table; -// step 2. fetch data from probe child in a background goroutine and probe the hash table in multiple join workers. -func (e *HashJoinExec) Next(ctx context.Context, req *chunk.Chunk) (err error) { - if !e.prepared { - e.buildFinished = make(chan error, 1) - hCtx := &hashContext{ - allTypes: e.buildTypes, - keyColIdx: e.buildWorker.buildKeyColIdx, - naKeyColIdx: e.buildWorker.buildNAKeyColIdx, - } - e.rowContainer = newHashRowContainer(e.Ctx(), hCtx, exec.RetTypes(e.buildWorker.buildSideExec)) - // we shallow copies rowContainer for each probe worker to avoid lock contention - for i := uint(0); i < e.concurrency; i++ { - if i == 0 { - e.probeWorkers[i].rowContainerForProbe = e.rowContainer - } else { - e.probeWorkers[i].rowContainerForProbe = e.rowContainer.ShallowCopy() - } - } - for i := uint(0); i < e.concurrency; i++ { - e.probeWorkers[i].rowIters = chunk.NewIterator4Slice([]chunk.Row{}).(*chunk.Iterator4Slice) - } - e.workerWg.RunWithRecover(func() { - defer trace.StartRegion(ctx, "HashJoinHashTableBuilder").End() - e.fetchAndBuildHashTable(ctx) - }, e.handleFetchAndBuildHashTablePanic) - e.fetchAndProbeHashTable(ctx) - e.prepared = true - } - if e.isOuterJoin { - atomic.StoreInt64(&e.probeSideTupleFetcher.requiredRows, int64(req.RequiredRows())) - } - req.Reset() - - result, ok := <-e.joinResultCh - if !ok { - return nil - } - if result.err != nil { - e.finished.Store(true) - return result.err - } - req.SwapColumns(result.chk) - result.src <- result.chk - return nil -} - -func (e *HashJoinExec) handleFetchAndBuildHashTablePanic(r interface{}) { - if r != nil { - e.buildFinished <- errors.Errorf("%v", r) - } - close(e.buildFinished) -} - -func (e *HashJoinExec) fetchAndBuildHashTable(ctx context.Context) { - if e.stats != nil { - start := time.Now() - defer func() { - e.stats.fetchAndBuildHashTable = time.Since(start) - }() - } - // buildSideResultCh transfers build side chunk from build side fetch to build hash table. - buildSideResultCh := make(chan *chunk.Chunk, 1) - doneCh := make(chan struct{}) - fetchBuildSideRowsOk := make(chan error, 1) - e.workerWg.RunWithRecover( - func() { - defer trace.StartRegion(ctx, "HashJoinBuildSideFetcher").End() - e.buildWorker.fetchBuildSideRows(ctx, buildSideResultCh, fetchBuildSideRowsOk, doneCh) - }, - func(r interface{}) { - if r != nil { - fetchBuildSideRowsOk <- errors.Errorf("%v", r) - } - close(fetchBuildSideRowsOk) - }, - ) - - // TODO: Parallel build hash table. Currently not support because `unsafeHashTable` is not thread-safe. - err := e.buildWorker.buildHashTableForList(buildSideResultCh) - if err != nil { - e.buildFinished <- errors.Trace(err) - close(doneCh) - } - // Wait fetchBuildSideRows be finished. - // 1. if buildHashTableForList fails - // 2. if probeSideResult.NumRows() == 0, fetchProbeSideChunks will not wait for the build side. - channel.Clear(buildSideResultCh) - // Check whether err is nil to avoid sending redundant error into buildFinished. - if err == nil { - if err = <-fetchBuildSideRowsOk; err != nil { - e.buildFinished <- err - } - } -} - -// buildHashTableForList builds hash table from `list`. -func (w *buildWorker) buildHashTableForList(buildSideResultCh <-chan *chunk.Chunk) error { - var err error - var selected []bool - rowContainer := w.hashJoinCtx.rowContainer - rowContainer.GetMemTracker().AttachTo(w.hashJoinCtx.memTracker) - rowContainer.GetMemTracker().SetLabel(memory.LabelForBuildSideResult) - rowContainer.GetDiskTracker().AttachTo(w.hashJoinCtx.diskTracker) - rowContainer.GetDiskTracker().SetLabel(memory.LabelForBuildSideResult) - if variable.EnableTmpStorageOnOOM.Load() { - actionSpill := rowContainer.ActionSpill() - failpoint.Inject("testRowContainerSpill", func(val failpoint.Value) { - if val.(bool) { - actionSpill = rowContainer.rowContainer.ActionSpillForTest() - defer actionSpill.(*chunk.SpillDiskAction).WaitForTest() - } - }) - w.hashJoinCtx.sessCtx.GetSessionVars().MemTracker.FallbackOldAndSetNewAction(actionSpill) - } - for chk := range buildSideResultCh { - if w.hashJoinCtx.finished.Load() { - return nil - } - if !w.hashJoinCtx.useOuterToBuild { - err = rowContainer.PutChunk(chk, w.hashJoinCtx.isNullEQ) - } else { - var bitMap = bitmap.NewConcurrentBitmap(chk.NumRows()) - w.hashJoinCtx.outerMatchedStatus = append(w.hashJoinCtx.outerMatchedStatus, bitMap) - w.hashJoinCtx.memTracker.Consume(bitMap.BytesConsumed()) - if len(w.hashJoinCtx.outerFilter) == 0 { - err = w.hashJoinCtx.rowContainer.PutChunk(chk, w.hashJoinCtx.isNullEQ) - } else { - selected, err = expression.VectorizedFilter(w.hashJoinCtx.sessCtx, w.hashJoinCtx.outerFilter, chunk.NewIterator4Chunk(chk), selected) - if err != nil { - return err - } - err = rowContainer.PutChunkSelected(chk, selected, w.hashJoinCtx.isNullEQ) - } - } - failpoint.Inject("ConsumeRandomPanic", nil) - if err != nil { - return err - } - } - return nil -} - -// NestedLoopApplyExec is the executor for apply. -type NestedLoopApplyExec struct { - exec.BaseExecutor - - ctx sessionctx.Context - innerRows []chunk.Row - cursor int - innerExec exec.Executor - outerExec exec.Executor - innerFilter expression.CNFExprs - outerFilter expression.CNFExprs - - joiner joiner - - cache *applycache.ApplyCache - canUseCache bool - cacheHitCounter int - cacheAccessCounter int - - outerSchema []*expression.CorrelatedColumn - - outerChunk *chunk.Chunk - outerChunkCursor int - outerSelected []bool - innerList *chunk.List - innerChunk *chunk.Chunk - innerSelected []bool - innerIter chunk.Iterator - outerRow *chunk.Row - hasMatch bool - hasNull bool - - outer bool - - memTracker *memory.Tracker // track memory usage. -} - -// Close implements the Executor interface. -func (e *NestedLoopApplyExec) Close() error { - e.innerRows = nil - e.memTracker = nil - if e.RuntimeStats() != nil { - runtimeStats := newJoinRuntimeStats() - if e.canUseCache { - var hitRatio float64 - if e.cacheAccessCounter > 0 { - hitRatio = float64(e.cacheHitCounter) / float64(e.cacheAccessCounter) - } - runtimeStats.setCacheInfo(true, hitRatio) - } else { - runtimeStats.setCacheInfo(false, 0) - } - runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", 0)) - defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), runtimeStats) - } - return e.outerExec.Close() -} - -// Open implements the Executor interface. -func (e *NestedLoopApplyExec) Open(ctx context.Context) error { - err := e.outerExec.Open(ctx) - if err != nil { - return err - } - e.cursor = 0 - e.innerRows = e.innerRows[:0] - e.outerChunk = exec.TryNewCacheChunk(e.outerExec) - e.innerChunk = exec.TryNewCacheChunk(e.innerExec) - e.innerList = chunk.NewList(exec.RetTypes(e.innerExec), e.InitCap(), e.MaxChunkSize()) - - e.memTracker = memory.NewTracker(e.ID(), -1) - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - - e.innerList.GetMemTracker().SetLabel(memory.LabelForInnerList) - e.innerList.GetMemTracker().AttachTo(e.memTracker) - - if e.canUseCache { - e.cache, err = applycache.NewApplyCache(e.ctx) - if err != nil { - return err - } - e.cacheHitCounter = 0 - e.cacheAccessCounter = 0 - e.cache.GetMemTracker().AttachTo(e.memTracker) - } - return nil -} - -// aggExecutorTreeInputEmpty checks whether the executor tree returns empty if without aggregate operators. -// Note that, the prerequisite is that this executor tree has been executed already and it returns one row. -func aggExecutorTreeInputEmpty(e exec.Executor) bool { - children := e.Base().AllChildren() - if len(children) == 0 { - return false - } - if len(children) > 1 { - _, ok := e.(*UnionExec) - if !ok { - // It is a Join executor. - return false - } - for _, child := range children { - if !aggExecutorTreeInputEmpty(child) { - return false - } - } - return true - } - // Single child executors. - if aggExecutorTreeInputEmpty(children[0]) { - return true - } - if hashAgg, ok := e.(*aggregate.HashAggExec); ok { - return hashAgg.IsChildReturnEmpty - } - if streamAgg, ok := e.(*aggregate.StreamAggExec); ok { - return streamAgg.IsChildReturnEmpty - } - return false -} - -func (e *NestedLoopApplyExec) fetchSelectedOuterRow(ctx context.Context, chk *chunk.Chunk) (*chunk.Row, error) { - outerIter := chunk.NewIterator4Chunk(e.outerChunk) - for { - if e.outerChunkCursor >= e.outerChunk.NumRows() { - err := exec.Next(ctx, e.outerExec, e.outerChunk) - if err != nil { - return nil, err - } - if e.outerChunk.NumRows() == 0 { - return nil, nil - } - e.outerSelected, err = expression.VectorizedFilter(e.ctx, e.outerFilter, outerIter, e.outerSelected) - if err != nil { - return nil, err - } - // For cases like `select count(1), (select count(1) from s where s.a > t.a) as sub from t where t.a = 1`, - // if outer child has no row satisfying `t.a = 1`, `sub` should be `null` instead of `0` theoretically; however, the - // outer `count(1)` produces one row <0, null> over the empty input, we should specially mark this outer row - // as not selected, to trigger the mismatch join procedure. - if e.outerChunkCursor == 0 && e.outerChunk.NumRows() == 1 && e.outerSelected[0] && aggExecutorTreeInputEmpty(e.outerExec) { - e.outerSelected[0] = false - } - e.outerChunkCursor = 0 - } - outerRow := e.outerChunk.GetRow(e.outerChunkCursor) - selected := e.outerSelected[e.outerChunkCursor] - e.outerChunkCursor++ - if selected { - return &outerRow, nil - } else if e.outer { - e.joiner.onMissMatch(false, outerRow, chk) - if chk.IsFull() { - return nil, nil - } - } - } -} - -// fetchAllInners reads all data from the inner table and stores them in a List. -func (e *NestedLoopApplyExec) fetchAllInners(ctx context.Context) error { - err := e.innerExec.Open(ctx) - defer terror.Call(e.innerExec.Close) - if err != nil { - return err - } - - if e.canUseCache { - // create a new one since it may be in the cache - e.innerList = chunk.NewList(exec.RetTypes(e.innerExec), e.InitCap(), e.MaxChunkSize()) - } else { - e.innerList.Reset() - } - innerIter := chunk.NewIterator4Chunk(e.innerChunk) - for { - err := exec.Next(ctx, e.innerExec, e.innerChunk) - if err != nil { - return err - } - if e.innerChunk.NumRows() == 0 { - return nil - } - - e.innerSelected, err = expression.VectorizedFilter(e.ctx, e.innerFilter, innerIter, e.innerSelected) - if err != nil { - return err - } - for row := innerIter.Begin(); row != innerIter.End(); row = innerIter.Next() { - if e.innerSelected[row.Idx()] { - e.innerList.AppendRow(row) - } - } - } -} - -// Next implements the Executor interface. -func (e *NestedLoopApplyExec) Next(ctx context.Context, req *chunk.Chunk) (err error) { - req.Reset() - for { - if e.innerIter == nil || e.innerIter.Current() == e.innerIter.End() { - if e.outerRow != nil && !e.hasMatch { - e.joiner.onMissMatch(e.hasNull, *e.outerRow, req) - } - e.outerRow, err = e.fetchSelectedOuterRow(ctx, req) - if e.outerRow == nil || err != nil { - return err - } - e.hasMatch = false - e.hasNull = false - - if e.canUseCache { - var key []byte - for _, col := range e.outerSchema { - *col.Data = e.outerRow.GetDatum(col.Index, col.RetType) - key, err = codec.EncodeKey(e.Ctx().GetSessionVars().StmtCtx, key, *col.Data) - if err != nil { - return err - } - } - e.cacheAccessCounter++ - value, err := e.cache.Get(key) - if err != nil { - return err - } - if value != nil { - e.innerList = value - e.cacheHitCounter++ - } else { - err = e.fetchAllInners(ctx) - if err != nil { - return err - } - if _, err := e.cache.Set(key, e.innerList); err != nil { - return err - } - } - } else { - for _, col := range e.outerSchema { - *col.Data = e.outerRow.GetDatum(col.Index, col.RetType) - } - err = e.fetchAllInners(ctx) - if err != nil { - return err - } - } - e.innerIter = chunk.NewIterator4List(e.innerList) - e.innerIter.Begin() - } - - matched, isNull, err := e.joiner.tryToMatchInners(*e.outerRow, e.innerIter, req) - e.hasMatch = e.hasMatch || matched - e.hasNull = e.hasNull || isNull - - if err != nil || req.IsFull() { - return err - } - } -} - -// cacheInfo is used to save the concurrency information of the executor operator -type cacheInfo struct { - hitRatio float64 - useCache bool -} - -type joinRuntimeStats struct { - *execdetails.RuntimeStatsWithConcurrencyInfo - - applyCache bool - cache cacheInfo - hasHashStat bool - hashStat hashStatistic -} - -func newJoinRuntimeStats() *joinRuntimeStats { - stats := &joinRuntimeStats{ - RuntimeStatsWithConcurrencyInfo: &execdetails.RuntimeStatsWithConcurrencyInfo{}, - } - return stats -} - -// setCacheInfo sets the cache information. Only used for apply executor. -func (e *joinRuntimeStats) setCacheInfo(useCache bool, hitRatio float64) { - e.Lock() - e.applyCache = true - e.cache.useCache = useCache - e.cache.hitRatio = hitRatio - e.Unlock() -} - -func (e *joinRuntimeStats) String() string { - buf := bytes.NewBuffer(make([]byte, 0, 16)) - buf.WriteString(e.RuntimeStatsWithConcurrencyInfo.String()) - if e.applyCache { - if e.cache.useCache { - fmt.Fprintf(buf, ", cache:ON, cacheHitRatio:%.3f%%", e.cache.hitRatio*100) - } else { - buf.WriteString(", cache:OFF") - } - } - if e.hasHashStat { - buf.WriteString(", " + e.hashStat.String()) - } - return buf.String() -} - -// Tp implements the RuntimeStats interface. -func (*joinRuntimeStats) Tp() int { - return execdetails.TpJoinRuntimeStats -} - -func (e *joinRuntimeStats) Clone() execdetails.RuntimeStats { - newJRS := &joinRuntimeStats{ - RuntimeStatsWithConcurrencyInfo: e.RuntimeStatsWithConcurrencyInfo, - applyCache: e.applyCache, - cache: e.cache, - hasHashStat: e.hasHashStat, - hashStat: e.hashStat, - } - return newJRS -} - -type hashJoinRuntimeStats struct { - fetchAndBuildHashTable time.Duration - hashStat hashStatistic - fetchAndProbe int64 - probe int64 - concurrent int - maxFetchAndProbe int64 -} - -func (e *hashJoinRuntimeStats) setMaxFetchAndProbeTime(t int64) { - for { - value := atomic.LoadInt64(&e.maxFetchAndProbe) - if t <= value { - return - } - if atomic.CompareAndSwapInt64(&e.maxFetchAndProbe, value, t) { - return - } - } -} - -// Tp implements the RuntimeStats interface. -func (*hashJoinRuntimeStats) Tp() int { - return execdetails.TpHashJoinRuntimeStats -} - -func (e *hashJoinRuntimeStats) String() string { - buf := bytes.NewBuffer(make([]byte, 0, 128)) - if e.fetchAndBuildHashTable > 0 { - buf.WriteString("build_hash_table:{total:") - buf.WriteString(execdetails.FormatDuration(e.fetchAndBuildHashTable)) - buf.WriteString(", fetch:") - buf.WriteString(execdetails.FormatDuration(e.fetchAndBuildHashTable - e.hashStat.buildTableElapse)) - buf.WriteString(", build:") - buf.WriteString(execdetails.FormatDuration(e.hashStat.buildTableElapse)) - buf.WriteString("}") - } - if e.probe > 0 { - buf.WriteString(", probe:{concurrency:") - buf.WriteString(strconv.Itoa(e.concurrent)) - buf.WriteString(", total:") - buf.WriteString(execdetails.FormatDuration(time.Duration(e.fetchAndProbe))) - buf.WriteString(", max:") - buf.WriteString(execdetails.FormatDuration(time.Duration(atomic.LoadInt64(&e.maxFetchAndProbe)))) - buf.WriteString(", probe:") - buf.WriteString(execdetails.FormatDuration(time.Duration(e.probe))) - buf.WriteString(", fetch:") - buf.WriteString(execdetails.FormatDuration(time.Duration(e.fetchAndProbe - e.probe))) - if e.hashStat.probeCollision > 0 { - buf.WriteString(", probe_collision:") - buf.WriteString(strconv.FormatInt(e.hashStat.probeCollision, 10)) - } - buf.WriteString("}") - } - return buf.String() -} - -func (e *hashJoinRuntimeStats) Clone() execdetails.RuntimeStats { - return &hashJoinRuntimeStats{ - fetchAndBuildHashTable: e.fetchAndBuildHashTable, - hashStat: e.hashStat, - fetchAndProbe: e.fetchAndProbe, - probe: e.probe, - concurrent: e.concurrent, - maxFetchAndProbe: e.maxFetchAndProbe, - } -} - -func (e *hashJoinRuntimeStats) Merge(rs execdetails.RuntimeStats) { - tmp, ok := rs.(*hashJoinRuntimeStats) - if !ok { - return - } - e.fetchAndBuildHashTable += tmp.fetchAndBuildHashTable - e.hashStat.buildTableElapse += tmp.hashStat.buildTableElapse - e.hashStat.probeCollision += tmp.hashStat.probeCollision - e.fetchAndProbe += tmp.fetchAndProbe - e.probe += tmp.probe - if e.maxFetchAndProbe < tmp.maxFetchAndProbe { - e.maxFetchAndProbe = tmp.maxFetchAndProbe - } -} diff --git a/executor/join_test.go b/executor/join_test.go deleted file mode 100644 index 3f71a03a4cd6c..0000000000000 --- a/executor/join_test.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" -) - -func TestNaturalJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (a int, b int)") - tk.MustExec("create table t2 (a int, c int)") - tk.MustExec("insert t1 values (1,2), (10,20), (0,0)") - tk.MustExec("insert t2 values (1,3), (100,200), (0,0)") - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - executorSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } -} - -func TestUsingAndNaturalJoinSchema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3, t4") - tk.MustExec("create table t1 (c int, b int);") - tk.MustExec("create table t2 (a int, b int);") - tk.MustExec("create table t3 (b int, c int);") - tk.MustExec("create table t4 (y int, c int);") - - tk.MustExec("insert into t1 values (10,1);") - tk.MustExec("insert into t1 values (3 ,1);") - tk.MustExec("insert into t1 values (3 ,2);") - tk.MustExec("insert into t2 values (2, 1);") - tk.MustExec("insert into t3 values (1, 3);") - tk.MustExec("insert into t3 values (1,10);") - tk.MustExec("insert into t4 values (11,3);") - tk.MustExec("insert into t4 values (2, 3);") - - var input []string - var output []struct { - SQL string - Res []string - } - executorSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } -} - -func TestTiDBNAAJ(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@session.tidb_enable_null_aware_anti_join=0;") - tk.MustExec("create table t(a decimal(40,0), b bigint(20) not null);") - tk.MustExec("insert into t values(7,8),(7,8),(3,4),(3,4),(9,2),(9,2),(2,0),(2,0),(0,4),(0,4),(8,8),(8,8),(6,1),(6,1),(NULL, 0),(NULL,0);") - tk.MustQuery("select ( table1 . a , table1 . b ) NOT IN ( SELECT 3 , 2 UNION SELECT 9, 2 ) AS field2 from t as table1 order by field2;").Check(testkit.Rows( - "0", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")) - tk.MustExec("set @@session.tidb_enable_null_aware_anti_join=1;") - tk.MustQuery("select ( table1 . a , table1 . b ) NOT IN ( SELECT 3 , 2 UNION SELECT 9, 2 ) AS field2 from t as table1 order by field2;").Check(testkit.Rows( - "0", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")) -} diff --git a/executor/lockstats/BUILD.bazel b/executor/lockstats/BUILD.bazel deleted file mode 100644 index 4d05fa72d6c75..0000000000000 --- a/executor/lockstats/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "lockstats", - srcs = [ - "lock_stats_executor.go", - "unlock_stats_executor.go", - ], - importpath = "github.com/pingcap/tidb/executor/lockstats", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//executor/internal/exec", - "//infoschema", - "//parser/ast", - "//parser/model", - "//statistics/handle/util", - "//table/tables", - "//util/chunk", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "lockstats_test", - timeout = "short", - srcs = ["lock_stats_executor_test.go"], - embed = [":lockstats"], - flaky = True, - deps = [ - "//infoschema", - "//parser/ast", - "//parser/model", - "@com_github_stretchr_testify//require", - ], -) diff --git a/executor/main_test.go b/executor/main_test.go deleted file mode 100644 index 4e1e3fedf1f83..0000000000000 --- a/executor/main_test.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) -var prepareMergeSuiteData testdata.TestData -var executorSuiteData testdata.TestData -var pointGetSuiteData testdata.TestData -var slowQuerySuiteData testdata.TestData - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - testDataMap.LoadTestSuiteData("testdata", "executor_suite") - testDataMap.LoadTestSuiteData("testdata", "prepare_suite") - testDataMap.LoadTestSuiteData("testdata", "point_get_suite") - testDataMap.LoadTestSuiteData("testdata", "slow_query_suite") - executorSuiteData = testDataMap["executor_suite"] - prepareMergeSuiteData = testDataMap["prepare_suite"] - pointGetSuiteData = testDataMap["point_get_suite"] - slowQuerySuiteData = testDataMap["slow_query_suite"] - - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - variable.StatsCacheMemQuota.Store(5000) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/executor/metrics/BUILD.bazel b/executor/metrics/BUILD.bazel deleted file mode 100644 index 8fd4ab43550a7..0000000000000 --- a/executor/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/executor/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/executor/metrics/metrics.go b/executor/metrics/metrics.go deleted file mode 100644 index d4775f23a0031..0000000000000 --- a/executor/metrics/metrics.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// phases -const ( - PhaseBuildLocking = "build:locking" - PhaseOpenLocking = "open:locking" - PhaseNextLocking = "next:locking" - PhaseLockLocking = "lock:locking" - PhaseBuildFinal = "build:final" - PhaseOpenFinal = "open:final" - PhaseNextFinal = "next:final" - PhaseLockFinal = "lock:final" - PhaseCommitPrewrite = "commit:prewrite" - PhaseCommitCommit = "commit:commit" - PhaseCommitWaitCommitTS = "commit:wait:commit-ts" - PhaseCommitWaitLatestTS = "commit:wait:latest-ts" - PhaseCommitWaitLatch = "commit:wait:local-latch" - PhaseCommitWaitBinlog = "commit:wait:prewrite-binlog" - PhaseWriteResponse = "write-response" -) - -// executor metrics vars -var ( - TotalQueryProcHistogramGeneral prometheus.Observer - TotalCopProcHistogramGeneral prometheus.Observer - TotalCopWaitHistogramGeneral prometheus.Observer - CopMVCCRatioHistogramGeneral prometheus.Observer - TotalQueryProcHistogramInternal prometheus.Observer - TotalCopProcHistogramInternal prometheus.Observer - TotalCopWaitHistogramInternal prometheus.Observer - - SelectForUpdateFirstAttemptDuration prometheus.Observer - SelectForUpdateRetryDuration prometheus.Observer - DmlFirstAttemptDuration prometheus.Observer - DmlRetryDuration prometheus.Observer - - // FairLockingTxnUsedCount counts transactions where at least one statement has fair locking enabled. - FairLockingTxnUsedCount prometheus.Counter - // FairLockingStmtUsedCount counts statements that have fair locking enabled. - FairLockingStmtUsedCount prometheus.Counter - // FairLockingTxnEffectiveCount counts transactions where at least one statement has fair locking enabled, - // and it takes effect (which is determined according to whether lock-with-conflict has occurred during execution). - FairLockingTxnEffectiveCount prometheus.Counter - // FairLockingStmtEffectiveCount counts statements where at least one statement has fair locking enabled, - // and it takes effect (which is determined according to whether lock-with-conflict has occurred during execution). - FairLockingStmtEffectiveCount prometheus.Counter - - ExecutorCounterMergeJoinExec prometheus.Counter - ExecutorCountHashJoinExec prometheus.Counter - ExecutorCounterHashAggExec prometheus.Counter - ExecutorStreamAggExec prometheus.Counter - ExecutorCounterSortExec prometheus.Counter - ExecutorCounterTopNExec prometheus.Counter - ExecutorCounterNestedLoopApplyExec prometheus.Counter - ExecutorCounterIndexLookUpJoin prometheus.Counter - ExecutorCounterIndexLookUpExecutor prometheus.Counter - ExecutorCounterIndexMergeReaderExecutor prometheus.Counter - - SessionExecuteRunDurationInternal prometheus.Observer - SessionExecuteRunDurationGeneral prometheus.Observer - TotalTiFlashQuerySuccCounter prometheus.Counter - - // pre-define observers for non-internal queries - ExecBuildLocking prometheus.Observer - ExecOpenLocking prometheus.Observer - ExecNextLocking prometheus.Observer - ExecLockLocking prometheus.Observer - ExecBuildFinal prometheus.Observer - ExecOpenFinal prometheus.Observer - ExecNextFinal prometheus.Observer - ExecLockFinal prometheus.Observer - ExecCommitPrewrite prometheus.Observer - ExecCommitCommit prometheus.Observer - ExecCommitWaitCommitTS prometheus.Observer - ExecCommitWaitLatestTS prometheus.Observer - ExecCommitWaitLatch prometheus.Observer - ExecCommitWaitBinlog prometheus.Observer - ExecWriteResponse prometheus.Observer - ExecUnknown prometheus.Observer - - // pre-define observers for internal queries - ExecBuildLockingInternal prometheus.Observer - ExecOpenLockingInternal prometheus.Observer - ExecNextLockingInternal prometheus.Observer - ExecLockLockingInternal prometheus.Observer - ExecBuildFinalInternal prometheus.Observer - ExecOpenFinalInternal prometheus.Observer - ExecNextFinalInternal prometheus.Observer - ExecLockFinalInternal prometheus.Observer - ExecCommitPrewriteInternal prometheus.Observer - ExecCommitCommitInternal prometheus.Observer - ExecCommitWaitCommitTSInternal prometheus.Observer - ExecCommitWaitLatestTSInternal prometheus.Observer - ExecCommitWaitLatchInternal prometheus.Observer - ExecCommitWaitBinlogInternal prometheus.Observer - ExecWriteResponseInternal prometheus.Observer - ExecUnknownInternal prometheus.Observer - - TransactionDurationPessimisticRollbackInternal prometheus.Observer - TransactionDurationPessimisticRollbackGeneral prometheus.Observer - TransactionDurationOptimisticRollbackInternal prometheus.Observer - TransactionDurationOptimisticRollbackGeneral prometheus.Observer - - PhaseDurationObserverMap map[string]prometheus.Observer - PhaseDurationObserverMapInternal map[string]prometheus.Observer - - MppCoordinatorStatsTotalRegisteredNumber prometheus.Gauge - MppCoordinatorStatsActiveNumber prometheus.Gauge - MppCoordinatorStatsOverTimeNumber prometheus.Gauge - MppCoordinatorStatsReportNotReceived prometheus.Gauge - - MppCoordinatorLatencyRcvReport prometheus.Observer -) - -func init() { - InitMetricsVars() - InitPhaseDurationObserverMap() -} - -// InitMetricsVars init executor metrics vars. -func InitMetricsVars() { - TotalQueryProcHistogramGeneral = metrics.TotalQueryProcHistogram.WithLabelValues(metrics.LblGeneral) - TotalCopProcHistogramGeneral = metrics.TotalCopProcHistogram.WithLabelValues(metrics.LblGeneral) - TotalCopWaitHistogramGeneral = metrics.TotalCopWaitHistogram.WithLabelValues(metrics.LblGeneral) - CopMVCCRatioHistogramGeneral = metrics.CopMVCCRatioHistogram.WithLabelValues(metrics.LblGeneral) - TotalQueryProcHistogramInternal = metrics.TotalQueryProcHistogram.WithLabelValues(metrics.LblInternal) - TotalCopProcHistogramInternal = metrics.TotalCopProcHistogram.WithLabelValues(metrics.LblInternal) - TotalCopWaitHistogramInternal = metrics.TotalCopWaitHistogram.WithLabelValues(metrics.LblInternal) - - SelectForUpdateFirstAttemptDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("select-for-update", "first-attempt") - SelectForUpdateRetryDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("select-for-update", "retry") - DmlFirstAttemptDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("dml", "first-attempt") - DmlRetryDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("dml", "retry") - - FairLockingTxnUsedCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingTxnUsed) - FairLockingStmtUsedCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingStmtUsed) - FairLockingTxnEffectiveCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingTxnEffective) - FairLockingStmtEffectiveCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingStmtEffective) - - ExecutorCounterMergeJoinExec = metrics.ExecutorCounter.WithLabelValues("MergeJoinExec") - ExecutorCountHashJoinExec = metrics.ExecutorCounter.WithLabelValues("HashJoinExec") - ExecutorCounterHashAggExec = metrics.ExecutorCounter.WithLabelValues("HashAggExec") - ExecutorStreamAggExec = metrics.ExecutorCounter.WithLabelValues("StreamAggExec") - ExecutorCounterSortExec = metrics.ExecutorCounter.WithLabelValues("SortExec") - ExecutorCounterTopNExec = metrics.ExecutorCounter.WithLabelValues("TopNExec") - ExecutorCounterNestedLoopApplyExec = metrics.ExecutorCounter.WithLabelValues("NestedLoopApplyExec") - ExecutorCounterIndexLookUpJoin = metrics.ExecutorCounter.WithLabelValues("IndexLookUpJoin") - ExecutorCounterIndexLookUpExecutor = metrics.ExecutorCounter.WithLabelValues("IndexLookUpExecutor") - ExecutorCounterIndexMergeReaderExecutor = metrics.ExecutorCounter.WithLabelValues("IndexMergeReaderExecutor") - - SessionExecuteRunDurationInternal = metrics.SessionExecuteRunDuration.WithLabelValues(metrics.LblInternal) - SessionExecuteRunDurationGeneral = metrics.SessionExecuteRunDuration.WithLabelValues(metrics.LblGeneral) - TotalTiFlashQuerySuccCounter = metrics.TiFlashQueryTotalCounter.WithLabelValues("", metrics.LblOK) - - ExecBuildLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildLocking, "0") - ExecOpenLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenLocking, "0") - ExecNextLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextLocking, "0") - ExecLockLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockLocking, "0") - ExecBuildFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildFinal, "0") - ExecOpenFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenFinal, "0") - ExecNextFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextFinal, "0") - ExecLockFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockFinal, "0") - ExecCommitPrewrite = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitPrewrite, "0") - ExecCommitCommit = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitCommit, "0") - ExecCommitWaitCommitTS = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitCommitTS, "0") - ExecCommitWaitLatestTS = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatestTS, "0") - ExecCommitWaitLatch = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatch, "0") - ExecCommitWaitBinlog = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitBinlog, "0") - ExecWriteResponse = metrics.ExecPhaseDuration.WithLabelValues(PhaseWriteResponse, "0") - ExecUnknown = metrics.ExecPhaseDuration.WithLabelValues("unknown", "0") - - ExecBuildLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildLocking, "1") - ExecOpenLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenLocking, "1") - ExecNextLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextLocking, "1") - ExecLockLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockLocking, "1") - ExecBuildFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildFinal, "1") - ExecOpenFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenFinal, "1") - ExecNextFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextFinal, "1") - ExecLockFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockFinal, "1") - ExecCommitPrewriteInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitPrewrite, "1") - ExecCommitCommitInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitCommit, "1") - ExecCommitWaitCommitTSInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitCommitTS, "1") - ExecCommitWaitLatestTSInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatestTS, "1") - ExecCommitWaitLatchInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatch, "1") - ExecCommitWaitBinlogInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitBinlog, "1") - ExecWriteResponseInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseWriteResponse, "1") - ExecUnknownInternal = metrics.ExecPhaseDuration.WithLabelValues("unknown", "1") - - TransactionDurationPessimisticRollbackInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblRollback, metrics.LblInternal) - TransactionDurationPessimisticRollbackGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblRollback, metrics.LblGeneral) - TransactionDurationOptimisticRollbackInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblRollback, metrics.LblInternal) - TransactionDurationOptimisticRollbackGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblRollback, metrics.LblGeneral) - - MppCoordinatorStatsTotalRegisteredNumber = metrics.MppCoordinatorStats.WithLabelValues("total") - MppCoordinatorStatsActiveNumber = metrics.MppCoordinatorStats.WithLabelValues("active") - MppCoordinatorStatsOverTimeNumber = metrics.MppCoordinatorStats.WithLabelValues("overTime") - MppCoordinatorStatsReportNotReceived = metrics.MppCoordinatorStats.WithLabelValues("reportNotRcv") - - MppCoordinatorLatencyRcvReport = metrics.MppCoordinatorLatency.WithLabelValues("rcvReports") -} - -// InitPhaseDurationObserverMap init observer map -func InitPhaseDurationObserverMap() { - PhaseDurationObserverMap = map[string]prometheus.Observer{ - PhaseBuildLocking: ExecBuildLocking, - PhaseOpenLocking: ExecOpenLocking, - PhaseNextLocking: ExecNextLocking, - PhaseLockLocking: ExecLockLocking, - PhaseBuildFinal: ExecBuildFinal, - PhaseOpenFinal: ExecOpenFinal, - PhaseNextFinal: ExecNextFinal, - PhaseLockFinal: ExecLockFinal, - PhaseCommitPrewrite: ExecCommitPrewrite, - PhaseCommitCommit: ExecCommitCommit, - PhaseCommitWaitCommitTS: ExecCommitWaitCommitTS, - PhaseCommitWaitLatestTS: ExecCommitWaitLatestTS, - PhaseCommitWaitLatch: ExecCommitWaitLatch, - PhaseCommitWaitBinlog: ExecCommitWaitBinlog, - PhaseWriteResponse: ExecWriteResponse, - } - PhaseDurationObserverMapInternal = map[string]prometheus.Observer{ - PhaseBuildLocking: ExecBuildLockingInternal, - PhaseOpenLocking: ExecOpenLockingInternal, - PhaseNextLocking: ExecNextLockingInternal, - PhaseLockLocking: ExecLockLockingInternal, - PhaseBuildFinal: ExecBuildFinalInternal, - PhaseOpenFinal: ExecOpenFinalInternal, - PhaseNextFinal: ExecNextFinalInternal, - PhaseLockFinal: ExecLockFinalInternal, - PhaseCommitPrewrite: ExecCommitPrewriteInternal, - PhaseCommitCommit: ExecCommitCommitInternal, - PhaseCommitWaitCommitTS: ExecCommitWaitCommitTSInternal, - PhaseCommitWaitLatestTS: ExecCommitWaitLatestTSInternal, - PhaseCommitWaitLatch: ExecCommitWaitLatchInternal, - PhaseCommitWaitBinlog: ExecCommitWaitBinlogInternal, - PhaseWriteResponse: ExecWriteResponseInternal, - } -} diff --git a/executor/mppcoordmanager/BUILD.bazel b/executor/mppcoordmanager/BUILD.bazel deleted file mode 100644 index 5ac9def4ad73a..0000000000000 --- a/executor/mppcoordmanager/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mppcoordmanager", - srcs = ["mpp_coordinator_manager.go"], - importpath = "github.com/pingcap/tidb/executor/mppcoordmanager", - visibility = ["//visibility:public"], - deps = [ - "//executor/metrics", - "//kv", - "//store/copr", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/mpp", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "mppcoordmanager_test", - timeout = "short", - srcs = ["mpp_coordinator_manager_test.go"], - embed = [":mppcoordmanager"], - flaky = True, - race = "on", - deps = [ - "//kv", - "//store/copr", - "@com_github_stretchr_testify//require", - ], -) diff --git a/executor/partition_table_test.go b/executor/partition_table_test.go deleted file mode 100644 index 18d7683b55327..0000000000000 --- a/executor/partition_table_test.go +++ /dev/null @@ -1,4296 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "math/rand" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/require" -) - -func TestSetPartitionPruneMode(t *testing.T) { - store := testkit.CreateMockStore(t) - - tkInit := testkit.NewTestKit(t, store) - tkInit.MustExec(`set @@session.tidb_partition_prune_mode = DEFAULT`) - tkInit.MustQuery("show warnings").Check(testkit.Rows()) - tkInit.MustExec(`set @@global.tidb_partition_prune_mode = DEFAULT`) - tkInit.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats")) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) - tk.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) - tk.MustExec(`set @@session.tidb_partition_prune_mode = "static"`) - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec(`set @@global.tidb_partition_prune_mode = "static"`) - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk2 := testkit.NewTestKit(t, store) - tk2.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("static")) - tk2.MustQuery("show warnings").Check(testkit.Rows()) - tk2.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("static")) - tk2.MustExec(`set @@session.tidb_partition_prune_mode = "dynamic"`) - tk2.MustQuery("show warnings").Sort().Check(testkit.Rows( - `Warning 1105 Please analyze all partition tables again for consistency between partition and global stats`, - `Warning 1105 Please avoid setting partition prune mode to dynamic at session level and set partition prune mode to dynamic at global level`)) - tk2.MustExec(`set @@global.tidb_partition_prune_mode = "dynamic"`) - tk2.MustQuery("show warnings").Check(testkit.Rows(`Warning 1105 Please analyze all partition tables again for consistency between partition and global stats`)) - tk3 := testkit.NewTestKit(t, store) - tk3.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) - tk3.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) -} - -func TestFourReader(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists pt") - tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("insert into pt values (0, 0), (2, 2), (4, 4), (6, 6), (7, 7), (9, 9), (null, null)") - - // Table reader - tk.MustQuery("select * from pt").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6", "7 7", "9 9", " ")) - // Table reader: table dual - tk.MustQuery("select * from pt where c > 10").Check(testkit.Rows()) - // Table reader: one partition - tk.MustQuery("select * from pt where c > 8").Check(testkit.Rows("9 9")) - // Table reader: more than one partition - tk.MustQuery("select * from pt where c < 2 or c >= 9").Sort().Check(testkit.Rows("0 0", "9 9")) - - // Index reader - tk.MustQuery("select c from pt").Sort().Check(testkit.Rows("0", "2", "4", "6", "7", "9", "")) - tk.MustQuery("select c from pt where c > 10").Check(testkit.Rows()) - tk.MustQuery("select c from pt where c > 8").Check(testkit.Rows("9")) - tk.MustQuery("select c from pt where c < 2 or c >= 9").Sort().Check(testkit.Rows("0", "9")) - - // Index lookup - tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6", "7 7", "9 9", " ")) - tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 4 and c > 10").Check(testkit.Rows()) - tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 10 and c > 8").Check(testkit.Rows("9 9")) - tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 10 and c < 2 or c >= 9").Sort().Check(testkit.Rows("0 0", "9 9")) - - // Index Merge - tk.MustExec("set @@tidb_enable_index_merge = 1") - tk.MustQuery("select /*+ use_index(i_c, i_id) */ * from pt where id = 4 or c < 7").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6")) -} - -func TestPartitionIndexJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_enable_table_partition = 1") - tk.MustExec("set @@session.tidb_enable_list_partition = 1") - for i := 0; i < 3; i++ { - tk.MustExec("drop table if exists p, t") - if i == 0 { - // Test for range partition - tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( - partition p0 values less than (4), - partition p1 values less than (7), - partition p2 values less than (10))`) - } else if i == 1 { - // Test for list partition - tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by list (c) ( - partition p0 values in (1,2,3,4), - partition p1 values in (5,6,7), - partition p2 values in (8, 9,10))`) - } else { - // Test for hash partition - tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by hash(c) partitions 5;`) - } - - tk.MustExec("create table t (id int)") - tk.MustExec("insert into p values (3,3), (4,4), (6,6), (9,9)") - tk.MustExec("insert into t values (4), (9)") - - // Build indexLookUp in index join - tk.MustQuery("select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id").Sort().Check(testkit.Rows("4 4 4", "9 9 9")) - // Build index reader in index join - tk.MustQuery("select /*+ INL_JOIN(p) */ p.id from p, t where p.id = t.id").Sort().Check(testkit.Rows("4", "9")) - } -} - -func TestPartitionUnionScanIndexJoin(t *testing.T) { - // For issue https://github.com/pingcap/tidb/issues/19152 - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (c_int int, c_str varchar(40), primary key (c_int)) partition by range (c_int) ( partition p0 values less than (10), partition p1 values less than maxvalue)") - tk.MustExec("create table t2 (c_int int, c_str varchar(40), primary key (c_int, c_str)) partition by hash (c_int) partitions 4") - tk.MustExec("insert into t1 values (10, 'interesting neumann')") - tk.MustExec("insert into t2 select * from t1") - tk.MustExec("begin") - tk.MustExec("insert into t2 values (11, 'hopeful hoover');") - tk.MustQuery("select /*+ INL_JOIN(t1,t2) */ * from t1 join t2 on t1.c_int = t2.c_int and t1.c_str = t2.c_str where t1.c_int in (10, 11)").Check(testkit.Rows("10 interesting neumann 10 interesting neumann")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t1,t2) */ * from t1 join t2 on t1.c_int = t2.c_int and t1.c_str = t2.c_str where t1.c_int in (10, 11)").Check(testkit.Rows("10 interesting neumann 10 interesting neumann")) - tk.MustExec("commit") -} - -func TestPointGetwithRangeAndListPartitionTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set @@session.tidb_enable_list_partition = ON") - - // list partition table - tk.MustExec(`create table tlist(a int, b int, unique index idx_a(a), index idx_b(b)) partition by list(a)( - partition p0 values in (NULL, 1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8), - partition p2 values in (9, 10, 11, 12));`) - - // range partition table - tk.MustExec(`create table trange1(a int, unique key(a)) partition by range(a) ( - partition p0 values less than (30), - partition p1 values less than (60), - partition p2 values less than (90), - partition p3 values less than (120));`) - - // range partition table + unsigned int - tk.MustExec(`create table trange2(a int unsigned, unique key(a)) partition by range(a) ( - partition p0 values less than (30), - partition p1 values less than (60), - partition p2 values less than (90), - partition p3 values less than (120));`) - - // insert data into list partition table - tk.MustExec("insert into tlist values(1,1), (2,2), (3, 3), (4, 4), (5,5), (6, 6), (7,7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (NULL, NULL);") - - vals := make([]string, 0, 100) - // insert data into range partition table and hash partition table - for i := 0; i < 100; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i+1)) - } - tk.MustExec("insert into trange1 values " + strings.Join(vals, ",")) - tk.MustExec("insert into trange2 values " + strings.Join(vals, ",")) - - // test PointGet - for i := 0; i < 100; i++ { - // explain select a from t where a = {x}; // x >= 1 and x <= 100 Check if PointGet is used - // select a from t where a={x}; // the result is {x} - x := rand.Intn(100) + 1 - queryRange1 := fmt.Sprintf("select a from trange1 where a=%v", x) - tk.MustHavePlan(queryRange1, "Point_Get") // check if PointGet is used - tk.MustQuery(queryRange1).Check(testkit.Rows(fmt.Sprintf("%v", x))) - - queryRange2 := fmt.Sprintf("select a from trange1 where a=%v", x) - tk.MustHavePlan(queryRange2, "Point_Get") // check if PointGet is used - tk.MustQuery(queryRange2).Check(testkit.Rows(fmt.Sprintf("%v", x))) - - y := rand.Intn(12) + 1 - queryList := fmt.Sprintf("select a from tlist where a=%v", y) - tk.MustHavePlan(queryList, "Point_Get") // check if PointGet is used - tk.MustQuery(queryList).Check(testkit.Rows(fmt.Sprintf("%v", y))) - } - - // test table dual - queryRange1 := "select a from trange1 where a=200" - tk.MustHavePlan(queryRange1, "TableDual") // check if TableDual is used - tk.MustQuery(queryRange1).Check(testkit.Rows()) - - queryRange2 := "select a from trange2 where a=200" - tk.MustHavePlan(queryRange2, "TableDual") // check if TableDual is used - tk.MustQuery(queryRange2).Check(testkit.Rows()) - - queryList := "select a from tlist where a=200" - tk.MustHavePlan(queryList, "TableDual") // check if TableDual is used - tk.MustQuery(queryList).Check(testkit.Rows()) - - // test PointGet for one partition - queryOnePartition := "select a from t where a = -1" - tk.MustExec("create table t(a int primary key, b int) PARTITION BY RANGE (a) (partition p0 values less than(1))") - tk.MustExec("insert into t values (-1, 1), (-2, 1)") - tk.MustExec("analyze table t") - tk.MustHavePlan(queryOnePartition, "Point_Get") - tk.MustQuery(queryOnePartition).Check(testkit.Rows(fmt.Sprintf("%v", -1))) - - tk.MustExec("drop table t") - tk.MustExec("create table t(a int primary key, b int) PARTITION BY list (a) (partition p0 values in (-1, -2))") - tk.MustExec("insert into t values (-1, 1), (-2, 1)") - tk.MustExec("analyze table t") - tk.MustHavePlan(queryOnePartition, "Point_Get") - tk.MustQuery(queryOnePartition).Check(testkit.Rows(fmt.Sprintf("%v", -1))) -} - -func TestPartitionReaderUnderApply(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // For issue 19458. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(c_int int)") - tk.MustExec("insert into t values(1), (2), (3), (4), (5), (6), (7), (8), (9)") - tk.MustExec("DROP TABLE IF EXISTS `t1`") - tk.MustExec(`CREATE TABLE t1 ( - c_int int NOT NULL, - c_str varchar(40) NOT NULL, - c_datetime datetime NOT NULL, - c_timestamp timestamp NULL DEFAULT NULL, - c_double double DEFAULT NULL, - c_decimal decimal(12,6) DEFAULT NULL, - PRIMARY KEY (c_int,c_str,c_datetime) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci - PARTITION BY RANGE (c_int) - (PARTITION p0 VALUES LESS THAN (2) ENGINE = InnoDB, - PARTITION p1 VALUES LESS THAN (4) ENGINE = InnoDB, - PARTITION p2 VALUES LESS THAN (6) ENGINE = InnoDB, - PARTITION p3 VALUES LESS THAN (8) ENGINE = InnoDB, - PARTITION p4 VALUES LESS THAN (10) ENGINE = InnoDB, - PARTITION p5 VALUES LESS THAN (20) ENGINE = InnoDB, - PARTITION p6 VALUES LESS THAN (50) ENGINE = InnoDB, - PARTITION p7 VALUES LESS THAN (1000000000) ENGINE = InnoDB)`) - tk.MustExec("INSERT INTO `t1` VALUES (19,'nifty feistel','2020-02-28 04:01:28','2020-02-04 06:11:57',32.430079,1.284000),(20,'objective snyder','2020-04-15 17:55:04','2020-05-30 22:04:13',37.690874,9.372000)") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (22, 'wizardly saha', '2020-05-03 16:35:22', '2020-05-03 02:18:42', 96.534810, 0.088)") - tk.MustQuery("select c_int from t where (select min(t1.c_int) from t1 where t1.c_int > t.c_int) > (select count(*) from t1 where t1.c_int > t.c_int) order by c_int").Check(testkit.Rows( - "1", "2", "3", "4", "5", "6", "7", "8", "9")) - tk.MustExec("rollback") - - // For issue 19450. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (c_int int, c_str varchar(40), c_decimal decimal(12, 6), primary key (c_int))") - tk.MustExec("create table t2 (c_int int, c_str varchar(40), c_decimal decimal(12, 6), primary key (c_int)) partition by hash (c_int) partitions 4") - tk.MustExec("insert into t1 values (1, 'romantic robinson', 4.436), (2, 'stoic chaplygin', 9.826), (3, 'vibrant shamir', 6.300), (4, 'hungry wilson', 4.900), (5, 'naughty swartz', 9.524)") - tk.MustExec("insert into t2 select * from t1") - tk.MustQuery("select * from t1 where c_decimal in (select c_decimal from t2 where t1.c_int = t2.c_int or t1.c_int = t2.c_int and t1.c_str > t2.c_str)").Check(testkit.Rows( - "1 romantic robinson 4.436000", - "2 stoic chaplygin 9.826000", - "3 vibrant shamir 6.300000", - "4 hungry wilson 4.900000", - "5 naughty swartz 9.524000")) - - // For issue 19450 release-4.0 - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - tk.MustQuery("select * from t1 where c_decimal in (select c_decimal from t2 where t1.c_int = t2.c_int or t1.c_int = t2.c_int and t1.c_str > t2.c_str)").Check(testkit.Rows( - "1 romantic robinson 4.436000", - "2 stoic chaplygin 9.826000", - "3 vibrant shamir 6.300000", - "4 hungry wilson 4.900000", - "5 naughty swartz 9.524000")) -} - -func TestImproveCoverage(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table coverage_rr ( -pk1 varchar(35) NOT NULL, -pk2 int NOT NULL, -c int, -PRIMARY KEY (pk1,pk2)) partition by hash(pk2) partitions 4;`) - tk.MustExec("create table coverage_dt (pk1 varchar(35), pk2 int)") - tk.MustExec("insert into coverage_rr values ('ios', 3, 2),('android', 4, 7),('linux',5,1)") - tk.MustExec("insert into coverage_dt values ('apple',3),('ios',3),('linux',5)") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery("select /*+ INL_JOIN(dt, rr) */ * from coverage_dt dt join coverage_rr rr on (dt.pk1 = rr.pk1 and dt.pk2 = rr.pk2);").Sort().Check(testkit.Rows("ios 3 ios 3 2", "linux 5 linux 5 1")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(dt, rr) */ * from coverage_dt dt join coverage_rr rr on (dt.pk1 = rr.pk1 and dt.pk2 = rr.pk2);").Sort().Check(testkit.Rows("ios 3 ios 3 2", "linux 5 linux 5 1")) -} - -func TestPartitionInfoDisable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_info_null") - tk.MustExec(`CREATE TABLE t_info_null ( - id bigint(20) unsigned NOT NULL AUTO_INCREMENT, - date date NOT NULL, - media varchar(32) NOT NULL DEFAULT '0', - app varchar(32) NOT NULL DEFAULT '', - xxx bigint(20) NOT NULL DEFAULT '0', - PRIMARY KEY (id, date), - UNIQUE KEY idx_media_id (media, date, app) -) PARTITION BY RANGE COLUMNS(date) ( - PARTITION p201912 VALUES LESS THAN ("2020-01-01"), - PARTITION p202001 VALUES LESS THAN ("2020-02-01"), - PARTITION p202002 VALUES LESS THAN ("2020-03-01"), - PARTITION p202003 VALUES LESS THAN ("2020-04-01"), - PARTITION p202004 VALUES LESS THAN ("2020-05-01"), - PARTITION p202005 VALUES LESS THAN ("2020-06-01"), - PARTITION p202006 VALUES LESS THAN ("2020-07-01"), - PARTITION p202007 VALUES LESS THAN ("2020-08-01"), - PARTITION p202008 VALUES LESS THAN ("2020-09-01"), - PARTITION p202009 VALUES LESS THAN ("2020-10-01"), - PARTITION p202010 VALUES LESS THAN ("2020-11-01"), - PARTITION p202011 VALUES LESS THAN ("2020-12-01") -)`) - is := tk.Session().GetInfoSchema().(infoschema.InfoSchema) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t_info_null")) - require.NoError(t, err) - - tbInfo := tbl.Meta() - // Mock for a case that the tableInfo.Partition is not nil, but tableInfo.Partition.Enable is false. - // That may happen when upgrading from a old version TiDB. - tbInfo.Partition.Enable = false - tbInfo.Partition.Num = 0 - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustQuery("explain select * from t_info_null where (date = '2020-10-02' or date = '2020-10-06') and app = 'xxx' and media = '19003006'").Check(testkit.Rows("Batch_Point_Get_5 2.00 root table:t_info_null, index:idx_media_id(media, date, app) keep order:false, desc:false")) - tk.MustQuery("explain select * from t_info_null").Check(testkit.Rows("TableReader_5 10000.00 root data:TableFullScan_4", - "└─TableFullScan_4 10000.00 cop[tikv] table:t_info_null keep order:false, stats:pseudo")) - // No panic. - tk.MustQuery("select * from t_info_null where (date = '2020-10-02' or date = '2020-10-06') and app = 'xxx' and media = '19003006'").Check(testkit.Rows()) -} - -func TestOrderByAndLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_orderby_limit") - tk.MustExec("use test_orderby_limit") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // range partition table - tk.MustExec(`create table trange(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than (500), - partition p2 values less than(1100));`) - - // hash partition table - tk.MustExec("create table thash(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by hash(a) partitions 4;") - - // regular table - tk.MustExec("create table tregular(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b))") - - // range partition table with int pk - tk.MustExec(`create table trange_intpk(a int primary key, b int) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than (500), - partition p2 values less than(1100));`) - - // hash partition table with int pk - tk.MustExec("create table thash_intpk(a int primary key, b int) partition by hash(a) partitions 4;") - - // regular table with int pk - tk.MustExec("create table tregular_intpk(a int primary key, b int)") - - // range partition table with clustered index - tk.MustExec(`create table trange_clustered(a int, b int, primary key(a, b) clustered) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than (500), - partition p2 values less than(1100));`) - - // hash partition table with clustered index - tk.MustExec("create table thash_clustered(a int, b int, primary key(a, b) clustered) partition by hash(a) partitions 4;") - - // regular table with clustered index - tk.MustExec("create table tregular_clustered(a int, b int, primary key(a, b) clustered)") - - listVals := make([]int, 0, 1000) - - for i := 0; i < 1000; i++ { - listVals = append(listVals, i) - } - rand.Shuffle(len(listVals), func(i, j int) { - listVals[i], listVals[j] = listVals[j], listVals[i] - }) - - var listVals1, listVals2, listVals3 string - - for i := 0; i <= 300; i++ { - listVals1 += strconv.Itoa(listVals[i]) - if i != 300 { - listVals1 += "," - } - } - for i := 301; i <= 600; i++ { - listVals2 += strconv.Itoa(listVals[i]) - if i != 600 { - listVals2 += "," - } - } - for i := 601; i <= 999; i++ { - listVals3 += strconv.Itoa(listVals[i]) - if i != 999 { - listVals3 += "," - } - } - - tk.MustExec(fmt.Sprintf(`create table tlist_intpk(a int primary key, b int) partition by list(a)( - partition p1 values in (%s), - partition p2 values in (%s), - partition p3 values in (%s) - )`, listVals1, listVals2, listVals3)) - tk.MustExec(fmt.Sprintf(`create table tlist(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by list(a)( - partition p1 values in (%s), - partition p2 values in (%s), - partition p3 values in (%s) - )`, listVals1, listVals2, listVals3)) - tk.MustExec(fmt.Sprintf(`create table tlist_clustered(a int, b int, primary key(a, b)) partition by list(a)( - partition p1 values in (%s), - partition p2 values in (%s), - partition p3 values in (%s) - )`, listVals1, listVals2, listVals3)) - - // generate some random data to be inserted - vals := make([]string, 0, 1000) - for i := 0; i < 1000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(550), rand.Intn(1000))) - } - - dedupValsA := make([]string, 0, 1000) - dedupMapA := make(map[int]struct{}, 1000) - for i := 0; i < 1000; i++ { - valA := rand.Intn(550) - if _, ok := dedupMapA[valA]; ok { - continue - } - dedupValsA = append(dedupValsA, fmt.Sprintf("(%v, %v)", valA, rand.Intn(1000))) - dedupMapA[valA] = struct{}{} - } - - dedupValsAB := make([]string, 0, 1000) - dedupMapAB := make(map[string]struct{}, 1000) - for i := 0; i < 1000; i++ { - val := fmt.Sprintf("(%v, %v)", rand.Intn(550), rand.Intn(1000)) - if _, ok := dedupMapAB[val]; ok { - continue - } - dedupValsAB = append(dedupValsAB, val) - dedupMapAB[val] = struct{}{} - } - - valInserted := strings.Join(vals, ",") - valDedupAInserted := strings.Join(dedupValsA, ",") - valDedupABInserted := strings.Join(dedupValsAB, ",") - - tk.MustExec("insert into trange values " + valInserted) - tk.MustExec("insert into thash values " + valInserted) - tk.MustExec("insert into tlist values" + valInserted) - tk.MustExec("insert into tregular values " + valInserted) - tk.MustExec("insert into trange_intpk values " + valDedupAInserted) - tk.MustExec("insert into thash_intpk values " + valDedupAInserted) - tk.MustExec("insert into tlist_intpk values " + valDedupAInserted) - tk.MustExec("insert into tregular_intpk values " + valDedupAInserted) - tk.MustExec("insert into trange_clustered values " + valDedupABInserted) - tk.MustExec("insert into thash_clustered values " + valDedupABInserted) - tk.MustExec("insert into tlist_clustered values " + valDedupABInserted) - tk.MustExec("insert into tregular_clustered values " + valDedupABInserted) - - tk.MustExec("analyze table trange") - tk.MustExec("analyze table trange_intpk") - tk.MustExec("analyze table trange_clustered") - tk.MustExec("analyze table thash") - tk.MustExec("analyze table thash_intpk") - tk.MustExec("analyze table thash_clustered") - tk.MustExec("analyze table tregular") - tk.MustExec("analyze table tregular_intpk") - tk.MustExec("analyze table tregular_clustered") - tk.MustExec("analyze table tlist") - tk.MustExec("analyze table tlist_intpk") - tk.MustExec("analyze table tlist_clustered") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test_orderby_limit")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if strings.HasPrefix(tblInfo.Name.L, "tr") || strings.HasPrefix(tblInfo.Name.L, "thash") || strings.HasPrefix(tblInfo.Name.L, "tlist") { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tikv\"") - - // test indexLookUp - for i := 0; i < 50; i++ { - // explain select * from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select * from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryPartition := fmt.Sprintf("select * from trange use index(idx_a) where a > %v order by a, b limit %v;", x, y) - queryRegular := fmt.Sprintf("select * from tregular use index(idx_a) where a > %v order by a, b limit %v;", x, y) - tk.MustHavePlan(queryPartition, "IndexLookUp") // check if IndexLookUp is used - tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) - } - - // test indexLookUp with order property pushed down. - for i := 0; i < 50; i++ { - if i%2 == 0 { - tk.MustExec("set tidb_partition_prune_mode = `static-only`") - } else { - tk.MustExec("set tidb_partition_prune_mode = `dynamic-only`") - } - // explain select * from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select * from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(1000) + 1 - // Since we only use order by a not order by a, b, the result is not stable when we read both a and b. - // We cut the max element so that the result can be stable. - maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(a), 1100) from (select * from tregular use index(idx_a) where a > %v order by a limit %v) t", x, y)).Rows()[0][0] - queryRangePartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) - queryHashPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) - queryListPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) - queryRegular := fmt.Sprintf("select * from tregular use index(idx_a) where a > %v and a < %v order by a limit %v;", x, maxEle, y) - - regularResult := tk.MustQuery(queryRegular).Sort().Rows() - if len(regularResult) > 0 { - tk.MustHavePlan(queryRangePartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryRangePartitionWithLimitHint, "IndexLookUp") - tk.MustHavePlan(queryHashPartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryHashPartitionWithLimitHint, "IndexLookUp") - tk.MustHavePlan(queryListPartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryListPartitionWithLimitHint, "IndexLookUp") - } - if i%2 != 0 { - tk.MustNotHavePlan(queryRangePartitionWithLimitHint, "TopN") // fully pushed - tk.MustNotHavePlan(queryHashPartitionWithLimitHint, "TopN") - tk.MustNotHavePlan(queryListPartitionWithLimitHint, "TopN") - } - tk.MustQuery(queryRangePartitionWithLimitHint).Sort().Check(regularResult) - tk.MustQuery(queryHashPartitionWithLimitHint).Sort().Check(regularResult) - tk.MustQuery(queryListPartitionWithLimitHint).Sort().Check(regularResult) - } - - // test indexLookUp with order property pushed down. - for i := 0; i < 50; i++ { - if i%2 == 0 { - tk.MustExec("set tidb_partition_prune_mode = `static-only`") - } else { - tk.MustExec("set tidb_partition_prune_mode = `dynamic-only`") - } - // explain select * from t where b > {y} use index(idx_b) order by b limit {x}; // check if IndexLookUp is used - // select * from t where b > {y} use index(idx_b) order by b limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(b), 2000) from (select * from tregular use index(idx_b) where b > %v order by b limit %v) t", x, y)).Rows()[0][0] - queryRangePartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) - queryHashPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) - queryListPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) - queryRegular := fmt.Sprintf("select * from tregular use index(idx_b) where b > %v and b < %v order by b limit %v;", x, maxEle, y) - - regularResult := tk.MustQuery(queryRegular).Sort().Rows() - if len(regularResult) > 0 { - tk.MustHavePlan(queryRangePartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryRangePartitionWithLimitHint, "IndexLookUp") - tk.MustHavePlan(queryHashPartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryHashPartitionWithLimitHint, "IndexLookUp") - tk.MustHavePlan(queryListPartitionWithLimitHint, "Limit") - tk.MustHavePlan(queryListPartitionWithLimitHint, "IndexLookUp") - } - if i%2 != 0 { - tk.MustNotHavePlan(queryRangePartitionWithLimitHint, "TopN") // fully pushed - tk.MustNotHavePlan(queryHashPartitionWithLimitHint, "TopN") - tk.MustNotHavePlan(queryListPartitionWithLimitHint, "TopN") - } - tk.MustQuery(queryRangePartitionWithLimitHint).Sort().Check(regularResult) - tk.MustQuery(queryHashPartitionWithLimitHint).Sort().Check(regularResult) - tk.MustQuery(queryListPartitionWithLimitHint).Sort().Check(regularResult) - } - - tk.MustExec("set tidb_partition_prune_mode = default") - - // test tableReader - for i := 0; i < 50; i++ { - // explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryPartition := fmt.Sprintf("select * from trange ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) - queryRegular := fmt.Sprintf("select * from tregular ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) - tk.MustHavePlan(queryPartition, "TableReader") // check if tableReader is used - tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) - } - - // test tableReader with order property pushed down. - for i := 0; i < 50; i++ { - // explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) - queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) - queryListPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) - queryRegular := fmt.Sprintf("select * from tregular ignore index(idx_a) where a > %v order by a, b limit %v;", x, y) - tk.MustHavePlan(queryRangePartition, "TableReader") // check if tableReader is used - tk.MustHavePlan(queryHashPartition, "TableReader") - tk.MustHavePlan(queryListPartition, "TableReader") - tk.MustNotHavePlan(queryRangePartition, "Limit") // check if order property is not pushed - tk.MustNotHavePlan(queryHashPartition, "Limit") - tk.MustNotHavePlan(queryListPartition, "Limit") - regularResult := tk.MustQuery(queryRegular).Rows() - tk.MustQuery(queryRangePartition).Check(regularResult) - tk.MustQuery(queryHashPartition).Check(regularResult) - tk.MustQuery(queryListPartition).Check(regularResult) - - // test int pk - // To be simplified, we only read column a. - queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange_intpk use index(primary) where a > %v order by a limit %v", x, y) - queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash_intpk use index(primary) where a > %v order by a limit %v", x, y) - queryListPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from tlist_intpk use index(primary) where a > %v order by a limit %v", x, y) - queryRegular = fmt.Sprintf("select a from tregular_intpk where a > %v order by a limit %v", x, y) - tk.MustHavePlan(queryRangePartition, "TableReader") - tk.MustHavePlan(queryHashPartition, "TableReader") - tk.MustHavePlan(queryListPartition, "TableReader") - tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed - tk.MustNotHavePlan(queryRangePartition, "TopN") // and is fully pushed - tk.MustHavePlan(queryHashPartition, "Limit") - tk.MustNotHavePlan(queryHashPartition, "TopN") - tk.MustHavePlan(queryListPartition, "Limit") - tk.MustNotHavePlan(queryListPartition, "TopN") - regularResult = tk.MustQuery(queryRegular).Rows() - tk.MustQuery(queryRangePartition).Check(regularResult) - tk.MustQuery(queryHashPartition).Check(regularResult) - tk.MustQuery(queryListPartition).Check(regularResult) - - // test clustered index - queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) - queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) - queryListPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) - queryRegular = fmt.Sprintf("select * from tregular_clustered where a > %v order by a, b limit %v;", x, y) - tk.MustHavePlan(queryRangePartition, "TableReader") // check if tableReader is used - tk.MustHavePlan(queryHashPartition, "TableReader") - tk.MustHavePlan(queryListPartition, "TableReader") - tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed - tk.MustHavePlan(queryHashPartition, "Limit") - tk.MustHavePlan(queryListPartition, "Limit") - tk.MustNotHavePlan(queryRangePartition, "TopN") // could fully pushed for TableScan executor - tk.MustNotHavePlan(queryHashPartition, "TopN") - tk.MustNotHavePlan(queryListPartition, "TopN") - regularResult = tk.MustQuery(queryRegular).Rows() - tk.MustQuery(queryRangePartition).Check(regularResult) - tk.MustQuery(queryHashPartition).Check(regularResult) - tk.MustQuery(queryListPartition).Check(regularResult) - - tk.MustExec(" set @@tidb_allow_mpp=1;") - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash,tikv\"") - queryPartitionWithTiFlash := fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_intpk]) */ * from trange_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_intpk]) */ /*+ LIMIT_TO_COP() */ * from trange_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_clustered]) */ * from trange_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_clustered]) */ /*+ LIMIT_TO_COP() */ * from trange_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_intpk]) */ * from thash_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_intpk]) */ /*+ LIMIT_TO_COP() */ * from thash_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_clustered]) */ * from thash_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_clustered]) */ /*+ LIMIT_TO_COP() */ * from thash_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_intpk]) */ * from tlist_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_intpk]) */ /*+ LIMIT_TO_COP() */ * from tlist_intpk where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_clustered]) */ * from tlist_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) - queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_clustered]) */ /*+ LIMIT_TO_COP() */ * from tlist_clustered where a > %v order by a limit %v", x, y) - // check if tiflash is used - require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) - // but order is not pushed - tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") - tk.MustExec(" set @@tidb_allow_mpp=0;") - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tikv\"") - } - - // test indexReader - for i := 0; i < 50; i++ { - // explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select a from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryPartition := fmt.Sprintf("select a from trange use index(idx_a) where a > %v order by a limit %v;", x, y) - queryRegular := fmt.Sprintf("select a from tregular use index(idx_a) where a > %v order by a limit %v;", x, y) - tk.MustHavePlan(queryPartition, "IndexReader") // check if indexReader is used - tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) - } - - // test indexReader with order property pushed down. - for i := 0; i < 50; i++ { - // explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used - // select a from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > %v order by a limit %v;", x, y) - queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash use index(idx_a) where a > %v order by a limit %v;", x, y) - queryRegular := fmt.Sprintf("select a from tregular use index(idx_a) where a > %v order by a limit %v;", x, y) - tk.MustHavePlan(queryRangePartition, "IndexReader") // check if indexReader is used - tk.MustHavePlan(queryHashPartition, "IndexReader") - tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed - tk.MustHavePlan(queryHashPartition, "Limit") - tk.MustNotHavePlan(queryRangePartition, "TopN") // fully pushed limit - tk.MustNotHavePlan(queryHashPartition, "TopN") - regularResult := tk.MustQuery(queryRegular).Rows() - tk.MustQuery(queryRangePartition).Check(regularResult) - tk.MustQuery(queryHashPartition).Check(regularResult) - } - - // test indexReader use idx_ab(a, b) with a = {x} order by b limit {y} - for i := 0; i < 50; i++ { - x := rand.Intn(549) - y := rand.Intn(500) + 1 - queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_ab) where a = %v order by b limit %v;", x, y) - queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash use index(idx_ab) where a = %v order by b limit %v;", x, y) - queryListPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from tlist use index(idx_ab) where a = %v order by b limit %v;", x, y) - queryRegular := fmt.Sprintf("select a from tregular use index(idx_ab) where a = %v order by b limit %v;", x, y) - tk.MustHavePlan(queryRangePartition, "IndexReader") // check if indexReader is used - tk.MustHavePlan(queryHashPartition, "IndexReader") - tk.MustHavePlan(queryListPartition, "IndexReader") - tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed - tk.MustHavePlan(queryHashPartition, "Limit") - tk.MustHavePlan(queryListPartition, "Limit") - tk.MustNotHavePlan(queryRangePartition, "TopN") // fully pushed limit - tk.MustNotHavePlan(queryHashPartition, "TopN") - tk.MustNotHavePlan(queryListPartition, "TopN") - regularResult := tk.MustQuery(queryRegular).Rows() - tk.MustQuery(queryRangePartition).Check(regularResult) - tk.MustQuery(queryHashPartition).Check(regularResult) - tk.MustQuery(queryListPartition).Check(regularResult) - } - - // test indexMerge - for i := 0; i < 50; i++ { - // explain select /*+ use_index_merge(t) */ * from t where a > 2 or b < 5 order by a, b limit {x}; // check if IndexMerge is used - // select /*+ use_index_merge(t) */ * from t where a > 2 or b < 5 order by a, b limit {x}; // can return the correct value - y := rand.Intn(500) + 1 - queryHashPartition := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > 2 or b < 5 order by a, b limit %v;", y) - queryRegular := fmt.Sprintf("select * from tregular where a > 2 or b < 5 order by a, b limit %v;", y) - tk.MustHavePlan(queryHashPartition, "IndexMerge") // check if indexMerge is used - tk.MustQuery(queryHashPartition).Check(tk.MustQuery(queryRegular).Rows()) - } - - // test sql killed when memory exceed `tidb_mem_quota_query` - originMemQuota := tk.MustQuery("show variables like 'tidb_mem_quota_query'").Rows()[0][1].(string) - originOOMAction := tk.MustQuery("show variables like 'tidb_mem_oom_action'").Rows()[0][1].(string) - tk.MustExec("set session tidb_mem_quota_query=128") - tk.MustExec("set global tidb_mem_oom_action=CANCEL") - err := tk.QueryToErr("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > 1 order by a limit 2000") - require.Error(t, err) - require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) - tk.MustExec(fmt.Sprintf("set session tidb_mem_quota_query=%s", originMemQuota)) - tk.MustExec(fmt.Sprintf("set global tidb_mem_oom_action=%s", originOOMAction)) -} - -func TestOrderByOnUnsignedPk(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table tunsigned_hash(a bigint unsigned primary key) partition by hash(a) partitions 6") - tk.MustExec("insert into tunsigned_hash values(25), (9279808998424041135)") - tk.MustQuery("select min(a) from tunsigned_hash").Check(testkit.Rows("25")) - tk.MustQuery("select max(a) from tunsigned_hash").Check(testkit.Rows("9279808998424041135")) -} - -func TestPartitionHandleWithKeepOrder(t *testing.T) { - // https://github.com/pingcap/tidb/issues/44312 - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (id int not null, store_id int not null )" + - "partition by range (store_id)" + - "(partition p0 values less than (6)," + - "partition p1 values less than (11)," + - "partition p2 values less than (16)," + - "partition p3 values less than (21))") - tk.MustExec("create table t1(id int not null, store_id int not null)") - tk.MustExec("insert into t values (1, 1)") - tk.MustExec("insert into t values (2, 17)") - tk.MustExec("insert into t1 values (0, 18)") - tk.MustExec("alter table t exchange partition p3 with table t1") - tk.MustExec("alter table t add index idx(id)") - tk.MustExec("analyze table t") - tk.MustQuery("select *,_tidb_rowid from t use index(idx) order by id limit 2").Check(testkit.Rows("0 18 1", "1 1 1")) - - tk.MustExec("drop table t, t1") - tk.MustExec("create table t (a int, b int, c int, key `idx_ac`(a, c), key `idx_bc`(b, c))" + - "partition by range (b)" + - "(partition p0 values less than (6)," + - "partition p1 values less than (11)," + - "partition p2 values less than (16)," + - "partition p3 values less than (21))") - tk.MustExec("create table t1 (a int, b int, c int, key `idx_ac`(a, c), key `idx_bc`(b, c))") - tk.MustExec("insert into t values (1,2,3), (2,3,4), (3,4,5)") - tk.MustExec("insert into t1 values (1,18,3)") - tk.MustExec("alter table t exchange partition p3 with table t1") - tk.MustExec("analyze table t") - tk.MustQuery("select * from t where a = 1 or b = 5 order by c limit 2").Sort().Check(testkit.Rows("1 18 3", "1 2 3")) -} - -func TestOrderByOnHandle(t *testing.T) { - // https://github.com/pingcap/tidb/issues/44266 - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - for i := 0; i < 2; i++ { - // indexLookUp + _tidb_rowid - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t`(" + - "`a` int(11) NOT NULL," + - "`b` int(11) DEFAULT NULL," + - "`c` int(11) DEFAULT NULL," + - "KEY `idx_b` (`b`)) PARTITION BY HASH (`a`) PARTITIONS 2;") - tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") - if i == 1 { - tk.MustExec("analyze table t") - } - tk.MustQuery("select * from t use index(idx_b) order by b, _tidb_rowid limit 10;").Check(testkit.Rows("2 -1 3", "1 1 1", "3 2 2")) - - // indexLookUp + pkIsHandle - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t`(" + - "`a` int(11) NOT NULL," + - "`b` int(11) DEFAULT NULL," + - "`c` int(11) DEFAULT NULL," + - "primary key(`a`)," + - "KEY `idx_b` (`b`)) PARTITION BY HASH (`a`) PARTITIONS 2;") - tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") - if i == 1 { - tk.MustExec("analyze table t") - } - tk.MustQuery("select * from t use index(idx_b) order by b, a limit 10;").Check(testkit.Rows("2 -1 3", "1 1 1", "3 2 2")) - - // indexMerge + _tidb_rowid - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t`(" + - "`a` int(11) NOT NULL," + - "`b` int(11) DEFAULT NULL," + - "`c` int(11) DEFAULT NULL," + - "KEY `idx_b` (`b`)," + - "KEY `idx_c` (`c`)) PARTITION BY HASH (`a`) PARTITIONS 2;") - tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") - if i == 1 { - tk.MustExec("analyze table t") - } - tk.MustQuery("select * from t use index(idx_b, idx_c) where b = 1 or c = 2 order by _tidb_rowid limit 10;").Check(testkit.Rows("3 2 2", "1 1 1")) - - // indexMerge + pkIsHandle - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t`(" + - "`a` int(11) NOT NULL," + - "`b` int(11) DEFAULT NULL," + - "`c` int(11) DEFAULT NULL," + - "KEY `idx_b` (`b`)," + - "KEY `idx_c` (`c`)," + - "PRIMARY KEY (`a`)) PARTITION BY HASH (`a`) PARTITIONS 2;") - tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") - if i == 1 { - tk.MustExec("analyze table t") - } - tk.MustQuery("select * from t use index(idx_b, idx_c) where b = 1 or c = 2 order by a limit 10;").Check(testkit.Rows("1 1 1", "3 2 2")) - } -} - -func TestBatchGetandPointGetwithHashPartition(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_batchget_pointget") - tk.MustExec("use test_batchget_pointget") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // hash partition table - tk.MustExec("create table thash(a int, unique key(a)) partition by hash(a) partitions 4;") - - // regular partition table - tk.MustExec("create table tregular(a int, unique key(a));") - - vals := make([]string, 0, 100) - // insert data into range partition table and hash partition table - for i := 0; i < 100; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i+1)) - } - tk.MustExec("insert into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular values " + strings.Join(vals, ",")) - - // test PointGet - for i := 0; i < 100; i++ { - // explain select a from t where a = {x}; // x >= 1 and x <= 100 Check if PointGet is used - // select a from t where a={x}; // the result is {x} - x := rand.Intn(100) + 1 - queryHash := fmt.Sprintf("select a from thash where a=%v", x) - queryRegular := fmt.Sprintf("select a from tregular where a=%v", x) - tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used - tk.MustQuery(queryHash).Check(tk.MustQuery(queryRegular).Rows()) - } - - // test empty PointGet - queryHash := "select a from thash where a=200" - tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used - tk.MustQuery(queryHash).Check(testkit.Rows()) - - // test BatchGet - for i := 0; i < 100; i++ { - // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used - // select a from t where where a in ({x1}, {x2}, ... {x10}); - points := make([]string, 0, 10) - for i := 0; i < 10; i++ { - x := rand.Intn(100) + 1 - points = append(points, fmt.Sprintf("%v", x)) - } - - queryHash := fmt.Sprintf("select a from thash where a in (%v)", strings.Join(points, ",")) - queryRegular := fmt.Sprintf("select a from tregular where a in (%v)", strings.Join(points, ",")) - tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - } -} - -func TestView(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_view") - tk.MustExec("use test_view") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a varchar(10), b varchar(10), key(a)) partition by range columns(a) ( - partition p0 values less than ('300'), - partition p1 values less than ('600'), - partition p2 values less than ('900'), - partition p3 values less than ('9999'))`) - tk.MustExec(`create table t1 (a int, b int, key(a))`) - tk.MustExec(`create table t2 (a varchar(10), b varchar(10), key(a))`) - - // insert the same data into thash and t1 - vals := make([]string, 0, 3000) - for i := 0; i < 3000; i++ { - vals = append(vals, fmt.Sprintf(`(%v, %v)`, rand.Intn(10000), rand.Intn(10000))) - } - tk.MustExec(fmt.Sprintf(`insert into thash values %v`, strings.Join(vals, ", "))) - tk.MustExec(fmt.Sprintf(`insert into t1 values %v`, strings.Join(vals, ", "))) - - // insert the same data into trange and t2 - vals = vals[:0] - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf(`("%v", "%v")`, rand.Intn(1000), rand.Intn(1000))) - } - tk.MustExec(fmt.Sprintf(`insert into trange values %v`, strings.Join(vals, ", "))) - tk.MustExec(fmt.Sprintf(`insert into t2 values %v`, strings.Join(vals, ", "))) - - // test views on a single table - tk.MustExec(`create definer='root'@'localhost' view vhash as select a*2 as a, a+b as b from thash`) - tk.MustExec(`create definer='root'@'localhost' view v1 as select a*2 as a, a+b as b from t1`) - tk.MustExec(`create definer='root'@'localhost' view vrange as select concat(a, b) as a, a+b as b from trange`) - tk.MustExec(`create definer='root'@'localhost' view v2 as select concat(a, b) as a, a+b as b from t2`) - for i := 0; i < 100; i++ { - xhash := rand.Intn(10000) - tk.MustQuery(fmt.Sprintf(`select * from vhash where a>=%v`, xhash)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v1 where a>=%v`, xhash)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vhash where b>=%v`, xhash)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v1 where b>=%v`, xhash)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vhash where a>=%v and b>=%v`, xhash, xhash)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v1 where a>=%v and b>=%v`, xhash, xhash)).Sort().Rows()) - - xrange := fmt.Sprintf(`"%v"`, rand.Intn(1000)) - tk.MustQuery(fmt.Sprintf(`select * from vrange where a>=%v`, xrange)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v2 where a>=%v`, xrange)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vrange where b>=%v`, xrange)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v2 where b>=%v`, xrange)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vrange where a>=%v and b<=%v`, xrange, xrange)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from v2 where a>=%v and b<=%v`, xrange, xrange)).Sort().Rows()) - } - - // test views on both tables - tk.MustExec(`create definer='root'@'localhost' view vboth as select thash.a+trange.a as a, thash.b+trange.b as b from thash, trange where thash.a=trange.a`) - tk.MustExec(`create definer='root'@'localhost' view vt as select t1.a+t2.a as a, t1.b+t2.b as b from t1, t2 where t1.a=t2.a`) - for i := 0; i < 100; i++ { - x := rand.Intn(10000) - tk.MustQuery(fmt.Sprintf(`select * from vboth where a>=%v`, x)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from vt where a>=%v`, x)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vboth where b>=%v`, x)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from vt where b>=%v`, x)).Sort().Rows()) - tk.MustQuery(fmt.Sprintf(`select * from vboth where a>=%v and b>=%v`, x, x)).Sort().Check( - tk.MustQuery(fmt.Sprintf(`select * from vt where a>=%v and b>=%v`, x, x)).Sort().Rows()) - } -} - -func TestDirectReadingwithIndexJoin(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_dr_join") - tk.MustExec("use test_dr_join") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // hash and range partition - tk.MustExec("create table thash (a int, b int, c int, primary key(a), index idx_b(b)) partition by hash(a) partitions 4;") - tk.MustExec(`create table trange (a int, b int, c int, primary key(a), index idx_b(b)) partition by range(a) ( - partition p0 values less than(1000), - partition p1 values less than(2000), - partition p2 values less than(3000), - partition p3 values less than(4000));`) - - // regualr table - tk.MustExec(`create table tnormal (a int, b int, c int, primary key(a), index idx_b(b));`) - tk.MustExec(`create table touter (a int, b int, c int);`) - - // generate some random data to be inserted - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v, %v)", rand.Intn(4000), rand.Intn(4000), rand.Intn(4000))) - } - tk.MustExec("insert ignore into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into tnormal values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into touter values " + strings.Join(vals, ",")) - - // test indexLookUp + hash - queryPartition := "select /*+ INL_JOIN(touter, thash) */ * from touter join thash use index(idx_b) on touter.b = thash.b" - queryRegular := "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal use index(idx_b) on touter.b = tnormal.b" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:test_dr_join.touter.b, inner key:test_dr_join.thash.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.thash.b)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─IndexLookUp(Probe) 12487.50 root partition:all ", - " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test_dr_join.thash.b))", - " │ └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(test_dr_join.thash.b, test_dr_join.touter.b)], keep order:false, stats:pseudo", - " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:thash keep order:false, stats:pseudo")) // check if IndexLookUp is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // test tableReader + hash - queryPartition = "select /*+ INL_JOIN(touter, thash) */ * from touter join thash on touter.a = thash.a" - queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal on touter.a = tnormal.a" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:TableReader, outer key:test_dr_join.touter.a, inner key:test_dr_join.thash.a, equal cond:eq(test_dr_join.touter.a, test_dr_join.thash.a)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.a))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan", - " └─TableRangeScan 9990.00 cop[tikv] table:thash range: decided by [test_dr_join.touter.a], keep order:false, stats:pseudo")) // check if tableReader is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // test indexReader + hash - queryPartition = "select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b;" - queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test_dr_join.touter.b, inner key:test_dr_join.thash.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.thash.b)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─IndexReader(Probe) 12487.50 root partition:all index:Selection", - " └─Selection 12487.50 cop[tikv] not(isnull(test_dr_join.thash.b))", - " └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(test_dr_join.thash.b, test_dr_join.touter.b)], keep order:false, stats:pseudo")) // check if indexReader is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // test indexLookUp + range - // explain select /*+ INL_JOIN(touter, tinner) */ * from touter join tinner use index(a) on touter.a = tinner.a; - queryPartition = "select /*+ INL_JOIN(touter, trange) */ * from touter join trange use index(idx_b) on touter.b = trange.b;" - queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:test_dr_join.touter.b, inner key:test_dr_join.trange.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.trange.b)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─IndexLookUp(Probe) 12487.50 root partition:all ", - " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test_dr_join.trange.b))", - " │ └─IndexRangeScan 12500.00 cop[tikv] table:trange, index:idx_b(b) range: decided by [eq(test_dr_join.trange.b, test_dr_join.touter.b)], keep order:false, stats:pseudo", - " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:trange keep order:false, stats:pseudo")) // check if IndexLookUp is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // test tableReader + range - queryPartition = "select /*+ INL_JOIN(touter, trange) */ * from touter join trange on touter.a = trange.a;" - queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal on touter.a = tnormal.a;" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:TableReader, outer key:test_dr_join.touter.a, inner key:test_dr_join.trange.a, equal cond:eq(test_dr_join.touter.a, test_dr_join.trange.a)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.a))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan", - " └─TableRangeScan 9990.00 cop[tikv] table:trange range: decided by [test_dr_join.touter.a], keep order:false, stats:pseudo")) // check if tableReader is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // test indexReader + range - // explain select /*+ INL_JOIN(touter, tinner) */ tinner.a from touter join tinner on touter.a = tinner.a; - queryPartition = "select /*+ INL_JOIN(touter, trange) */ trange.b from touter join trange use index(idx_b) on touter.b = trange.b;" - queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" - tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( - "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test_dr_join.touter.b, inner key:test_dr_join.trange.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.trange.b)", - "├─TableReader(Build) 9990.00 root data:Selection", - "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", - "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", - "└─IndexReader(Probe) 12487.50 root partition:all index:Selection", - " └─Selection 12487.50 cop[tikv] not(isnull(test_dr_join.trange.b))", - " └─IndexRangeScan 12500.00 cop[tikv] table:trange, index:idx_b(b) range: decided by [eq(test_dr_join.trange.b, test_dr_join.touter.b)], keep order:false, stats:pseudo")) // check if indexReader is used - tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) -} - -func TestDynamicPruningUnderIndexJoin(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create database pruing_under_index_join") - tk.MustExec("use pruing_under_index_join") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table tnormal (a int, b int, c int, primary key(a), index idx_b(b))`) - tk.MustExec(`create table thash (a int, b int, c int, primary key(a), index idx_b(b)) partition by hash(a) partitions 4`) - tk.MustExec(`create table touter (a int, b int, c int)`) - - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v, %v)", i, rand.Intn(10000), rand.Intn(10000))) - } - tk.MustExec(`insert into tnormal values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into touter values ` + strings.Join(vals, ", ")) - - // case 1: IndexReader in the inner side - tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b`).Check(testkit.Rows( - `IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.b, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.b)`, - `├─TableReader(Build) 9990.00 root data:Selection`, - `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, - `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - `└─IndexReader(Probe) 12487.50 root partition:all index:Selection`, - ` └─Selection 12487.50 cop[tikv] not(isnull(pruing_under_index_join.thash.b))`, - ` └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(pruing_under_index_join.thash.b, pruing_under_index_join.touter.b)], keep order:false, stats:pseudo`)) - tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b`).Sort().Check( - tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b`).Sort().Rows()) - - // case 2: TableReader in the inner side - tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(primary) on touter.b = thash.a`).Check(testkit.Rows( - `IndexJoin 12487.50 root inner join, inner:TableReader, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.a, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.a)`, - `├─TableReader(Build) 9990.00 root data:Selection`, - `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, - `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - `└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan`, - ` └─TableRangeScan 9990.00 cop[tikv] table:thash range: decided by [pruing_under_index_join.touter.b], keep order:false, stats:pseudo`)) - tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(primary) on touter.b = thash.a`).Sort().Check( - tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.* from touter join tnormal use index(primary) on touter.b = tnormal.a`).Sort().Rows()) - - // case 3: IndexLookUp in the inner side + read all inner columns - tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(idx_b) on touter.b = thash.b`).Check(testkit.Rows( - `IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.b, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.b)`, - `├─TableReader(Build) 9990.00 root data:Selection`, - `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, - `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - `└─IndexLookUp(Probe) 12487.50 root partition:all `, - ` ├─Selection(Build) 12487.50 cop[tikv] not(isnull(pruing_under_index_join.thash.b))`, - ` │ └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(pruing_under_index_join.thash.b, pruing_under_index_join.touter.b)], keep order:false, stats:pseudo`, - ` └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:thash keep order:false, stats:pseudo`)) - tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(idx_b) on touter.b = thash.b`).Sort().Check( - tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.* from touter join tnormal use index(idx_b) on touter.b = tnormal.b`).Sort().Rows()) -} - -func TestIssue25527(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_issue_25527") - tk.MustExec("use test_issue_25527") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set @@session.tidb_enable_list_partition = ON") - - // the original case - tk.MustExec(`CREATE TABLE t ( - col1 tinyint(4) primary key - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH( COL1 DIV 80 ) - PARTITIONS 6`) - tk.MustExec(`insert into t values(-128), (107)`) - tk.MustExec(`prepare stmt from 'select col1 from t where col1 in (?, ?, ?)'`) - tk.MustExec(`set @a=-128, @b=107, @c=-128`) - tk.MustQuery(`execute stmt using @a,@b,@c`).Sort().Check(testkit.Rows("-128", "107")) - - // the minimal reproducible case for hash partitioning - tk.MustExec(`CREATE TABLE t0 (a int primary key) PARTITION BY HASH( a DIV 80 ) PARTITIONS 2`) - tk.MustExec(`insert into t0 values (1)`) - tk.MustQuery(`select a from t0 where a in (1)`).Check(testkit.Rows("1")) - - // the minimal reproducible case for range partitioning - tk.MustExec(`create table t1 (a int primary key) partition by range (a+5) ( - partition p0 values less than(10), partition p1 values less than(20))`) - tk.MustExec(`insert into t1 values (5)`) - tk.MustQuery(`select a from t1 where a in (5)`).Check(testkit.Rows("5")) - - // the minimal reproducible case for list partitioning - tk.MustExec(`create table t2 (a int primary key) partition by list (a+5) ( - partition p0 values in (5, 6, 7, 8), partition p1 values in (9, 10, 11, 12))`) - tk.MustExec(`insert into t2 values (5)`) - tk.MustQuery(`select a from t2 where a in (5)`).Check(testkit.Rows("5")) -} - -func TestIssue25598(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_issue_25598") - tk.MustExec("use test_issue_25598") - tk.MustExec(`CREATE TABLE UK_HP16726 ( - COL1 bigint(16) DEFAULT NULL, - COL2 varchar(20) DEFAULT NULL, - COL4 datetime DEFAULT NULL, - COL3 bigint(20) DEFAULT NULL, - COL5 float DEFAULT NULL, - UNIQUE KEY UK_COL1 (COL1) /*!80000 INVISIBLE */ - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin - PARTITION BY HASH( COL1 ) - PARTITIONS 25`) - - tk.MustQuery(`select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`).Check(testkit.Rows()) - tk.MustExec(`explain select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`) - - tk.MustExec(`set @@tidb_partition_prune_mode = 'dynamic'`) - tk.MustQuery(`select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`).Check(testkit.Rows()) - tk.MustExec(`explain select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`) -} - -func TestBatchGetforRangeandListPartitionTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_pointget") - tk.MustExec("use test_pointget") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set @@session.tidb_enable_list_partition = ON") - - // list partition table - tk.MustExec(`create table tlist(a int, b int, unique index idx_a(a), index idx_b(b)) partition by list(a)( - partition p0 values in (1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8), - partition p2 values in (9, 10, 11, 12));`) - - // range partition table - tk.MustExec(`create table trange(a int, unique key(a)) partition by range(a) ( - partition p0 values less than (30), - partition p1 values less than (60), - partition p2 values less than (90), - partition p3 values less than (120));`) - - // hash partition table - tk.MustExec("create table thash(a int unsigned, unique key(a)) partition by hash(a) partitions 4;") - - // insert data into list partition table - tk.MustExec("insert into tlist values(1,1), (2,2), (3, 3), (4, 4), (5,5), (6, 6), (7,7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12);") - // regular partition table - tk.MustExec("create table tregular1(a int, unique key(a));") - tk.MustExec("create table tregular2(a int, unique key(a));") - - vals := make([]string, 0, 100) - // insert data into range partition table and hash partition table - for i := 0; i < 100; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i+1)) - } - tk.MustExec("insert into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular2 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)") - - // test BatchGet - for i := 0; i < 100; i++ { - // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used - // select a from t where where a in ({x1}, {x2}, ... {x10}); - points := make([]string, 0, 10) - for i := 0; i < 10; i++ { - x := rand.Intn(100) + 1 - points = append(points, fmt.Sprintf("%v", x)) - } - queryRegular1 := fmt.Sprintf("select a from tregular1 where a in (%v)", strings.Join(points, ",")) - - queryHash := fmt.Sprintf("select a from thash where a in (%v)", strings.Join(points, ",")) - tk.MustHavePlan(queryHash, "Batch_Point_Get") // check if BatchGet is used - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryRange := fmt.Sprintf("select a from trange where a in (%v)", strings.Join(points, ",")) - tk.MustHavePlan(queryRange, "Batch_Point_Get") // check if BatchGet is used - tk.MustQuery(queryRange).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - points = make([]string, 0, 10) - for i := 0; i < 10; i++ { - x := rand.Intn(12) + 1 - points = append(points, fmt.Sprintf("%v", x)) - } - queryRegular2 := fmt.Sprintf("select a from tregular2 where a in (%v)", strings.Join(points, ",")) - queryList := fmt.Sprintf("select a from tlist where a in (%v)", strings.Join(points, ",")) - tk.MustHavePlan(queryList, "Batch_Point_Get") // check if BatchGet is used - tk.MustQuery(queryList).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - } - - // test different data type - // unsigned flag - // partition table and reguar table pair - tk.MustExec(`create table trange3(a int unsigned, unique key(a)) partition by range(a) ( - partition p0 values less than (30), - partition p1 values less than (60), - partition p2 values less than (90), - partition p3 values less than (120));`) - tk.MustExec("create table tregular3(a int unsigned, unique key(a));") - vals = make([]string, 0, 100) - // insert data into range partition table and hash partition table - for i := 0; i < 100; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i+1)) - } - tk.MustExec("insert into trange3 values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular3 values " + strings.Join(vals, ",")) - // test BatchGet - // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used - // select a from t where where a in ({x1}, {x2}, ... {x10}); - points := make([]string, 0, 10) - for i := 0; i < 10; i++ { - x := rand.Intn(100) + 1 - points = append(points, fmt.Sprintf("%v", x)) - } - queryRegular := fmt.Sprintf("select a from tregular3 where a in (%v)", strings.Join(points, ",")) - queryRange := fmt.Sprintf("select a from trange3 where a in (%v)", strings.Join(points, ",")) - tk.MustHavePlan(queryRange, "Batch_Point_Get") // check if BatchGet is used - tk.MustQuery(queryRange).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) -} - -func TestGlobalStatsAndSQLBinding(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_global_stats") - tk.MustExec("use test_global_stats") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set tidb_cost_model_version=2") - - // hash and range and list partition - tk.MustExec("create table thash(a int, b int, key(a)) partition by hash(a) partitions 4") - tk.MustExec(`create table trange(a int, b int, key(a)) partition by range(a) ( - partition p0 values less than (200), - partition p1 values less than (400), - partition p2 values less than (600), - partition p3 values less than (800), - partition p4 values less than (1001))`) - tk.MustExec(`create table tlist (a int, b int, key(a)) partition by list (a) ( - partition p0 values in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), - partition p1 values in (10, 11, 12, 13, 14, 15, 16, 17, 18, 19), - partition p2 values in (20, 21, 22, 23, 24, 25, 26, 27, 28, 29), - partition p3 values in (30, 31, 32, 33, 34, 35, 36, 37, 38, 39), - partition p4 values in (40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50))`) - - // construct some special data distribution - vals := make([]string, 0, 1000) - listVals := make([]string, 0, 1000) - for i := 0; i < 1000; i++ { - if i < 10 { - // for hash and range partition, 1% of records are in [0, 100) - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100))) - // for list partition, 1% of records are equal to 0 - listVals = append(listVals, "(0, 0)") - } else { - vals = append(vals, fmt.Sprintf("(%v, %v)", 100+rand.Intn(900), 100+rand.Intn(900))) - listVals = append(listVals, fmt.Sprintf("(%v, %v)", 1+rand.Intn(50), 1+rand.Intn(50))) - } - } - tk.MustExec("insert into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert into tlist values " + strings.Join(listVals, ",")) - - // before analyzing, the planner will choose TableScan to access the 1% of records - tk.MustHavePlan("select * from thash where a<100", "TableFullScan") - tk.MustHavePlan("select * from trange where a<100", "TableFullScan") - tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") - - tk.MustExec("analyze table thash") - tk.MustExec("analyze table trange") - tk.MustExec("analyze table tlist") - - tk.MustHavePlan("select * from thash where a<100", "TableFullScan") - tk.MustHavePlan("select * from trange where a<100", "TableFullScan") - tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") - - // create SQL bindings - tk.MustExec("create session binding for select * from thash where a<100 using select * from thash ignore index(a) where a<100") - tk.MustExec("create session binding for select * from trange where a<100 using select * from trange ignore index(a) where a<100") - tk.MustExec("create session binding for select * from tlist where a<100 using select * from tlist ignore index(a) where a<100") - - // use TableScan again since the Index(a) is ignored - tk.MustHavePlan("select * from thash where a<100", "TableFullScan") - tk.MustHavePlan("select * from trange where a<100", "TableFullScan") - tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") - - // drop SQL bindings - tk.MustExec("drop session binding for select * from thash where a<100") - tk.MustExec("drop session binding for select * from trange where a<100") - tk.MustExec("drop session binding for select * from tlist where a<100") - - tk.MustHavePlan("select * from thash where a<100", "TableFullScan") - tk.MustHavePlan("select * from trange where a<100", "TableFullScan") - tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") -} - -func TestPartitionTableWithDifferentJoin(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_partition_joins") - tk.MustExec("use test_partition_joins") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // hash and range partition - tk.MustExec("create table thash(a int, b int, key(a)) partition by hash(a) partitions 4") - tk.MustExec("create table tregular1(a int, b int, key(a))") - - tk.MustExec(`create table trange(a int, b int, key(a)) partition by range(a) ( - partition p0 values less than (200), - partition p1 values less than (400), - partition p2 values less than (600), - partition p3 values less than (800), - partition p4 values less than (1001))`) - tk.MustExec("create table tregular2(a int, b int, key(a))") - - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) - } - tk.MustExec("insert into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) - - vals = make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) - } - tk.MustExec("insert into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular2 values " + strings.Join(vals, ",")) - - // random params - x1 := rand.Intn(1000) - x2 := rand.Intn(1000) - x3 := rand.Intn(1000) - x4 := rand.Intn(1000) - - // group 1 - // hash_join range partition and hash partition - queryHash := fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.b=thash.b and thash.a = %v and trange.a > %v;", x1, x2) - queryRegular := fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.b=tregular1.b and tregular1.a = %v and tregular2.a > %v;", x1, x2) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a > %v;", x1) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a > %v;", x1) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and trange.b = thash.b and thash.a > %v;", x1) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.b = tregular2.b and tregular1.a > %v;", x1) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a = %v;", x1) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a = %v;", x1) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // group 2 - // hash_join range partition and regular table - queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a >= %v and tregular1.a > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a >= %v and tregular1.a > %v;", x1, x2) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a in (%v, %v, %v);", x1, x2, x3) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a in (%v, %v, %v);", x1, x2, x3) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and tregular1.a >= %v;", x1) - queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular1.a >= %v;", x1) - tk.MustHavePlan(queryHash, "HashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // group 3 - // merge_join range partition and hash partition - queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.b=thash.b and thash.a = %v and trange.a > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.b=tregular1.b and tregular1.a = %v and tregular2.a > %v;", x1, x2) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a > %v;", x1) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a > %v;", x1) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and trange.b = thash.b and thash.a > %v;", x1) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.b = tregular2.b and tregular1.a > %v;", x1) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a = %v;", x1) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a = %v;", x1) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // group 4 - // merge_join range partition and regular table - queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a >= %v and tregular1.a > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a >= %v and tregular1.a > %v;", x1, x2) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a in (%v, %v, %v);", x1, x2, x3) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a in (%v, %v, %v);", x1, x2, x3) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and tregular1.a >= %v;", x1) - queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular1.a >= %v;", x1) - tk.MustHavePlan(queryHash, "MergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // new table instances - tk.MustExec("create table thash2(a int, b int, index idx(a)) partition by hash(a) partitions 4") - tk.MustExec("create table tregular3(a int, b int, index idx(a))") - - tk.MustExec(`create table trange2(a int, b int, index idx(a)) partition by range(a) ( - partition p0 values less than (200), - partition p1 values less than (400), - partition p2 values less than (600), - partition p3 values less than (800), - partition p4 values less than (1001))`) - tk.MustExec("create table tregular4(a int, b int, index idx(a))") - - vals = make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) - } - tk.MustExec("insert into thash2 values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular3 values " + strings.Join(vals, ",")) - - vals = make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) - } - tk.MustExec("insert into trange2 values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular4 values " + strings.Join(vals, ",")) - - // group 5 - // index_merge_join range partition and range partition - // Currently don't support index merge join on two partition tables. Only test warning. - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v;", x1) - // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v;", x1) - // tk.MustHavePlan(queryHash, "IndexMergeJoin") - // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - tk.MustQuery(queryHash) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange2.a > %v;", x1, x2) - // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.a > %v;", x1, x2) - // tk.MustHavePlan(queryHash, "IndexMergeJoin") - // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - tk.MustQuery(queryHash) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange.b > %v;", x1, x2) - // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular2.b > %v;", x1, x2) - // tk.MustHavePlan(queryHash, "IndexMergeJoin") - // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - tk.MustQuery(queryHash) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange2.b > %v;", x1, x2) - // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.b > %v;", x1, x2) - // tk.MustHavePlan(queryHash, "IndexMergeJoin") - // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - tk.MustQuery(queryHash) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) - - // group 6 - // index_merge_join range partition and regualr table - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v;", x1) - queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v;", x1) - tk.MustHavePlan(queryHash, "IndexMergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and tregular4.a > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.a > %v;", x1, x2) - tk.MustHavePlan(queryHash, "IndexMergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and trange.b > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular2.b > %v;", x1, x2) - tk.MustHavePlan(queryHash, "IndexMergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and tregular4.b > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.b > %v;", x1, x2) - tk.MustHavePlan(queryHash, "IndexMergeJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // group 7 - // index_hash_join hash partition and hash partition - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a in (%v, %v);", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v);", x1, x2) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a in (%v, %v) and thash2.a in (%v, %v);", x1, x2, x3, x4) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a > %v and thash2.b > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a > %v and tregular3.b > %v;", x1, x2) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - // group 8 - // index_hash_join hash partition and hash partition - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a in (%v, %v);", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v);", x1, x2) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) - - queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a > %v and tregular3.b > %v;", x1, x2) - queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a > %v and tregular3.b > %v;", x1, x2) - tk.MustHavePlan(queryHash, "IndexHashJoin") - tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) -} - -func createTable4DynamicPruneModeTestWithExpression(tk *testkit.TestKit) { - tk.MustExec("create table trange(a int, b int) partition by range(a) (partition p0 values less than(3), partition p1 values less than (5), partition p2 values less than(11));") - tk.MustExec("create table thash(a int, b int) partition by hash(a) partitions 4;") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into trange values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") - tk.MustExec("insert into thash values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") - tk.MustExec("insert into t values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") - tk.MustExec("set session tidb_partition_prune_mode='dynamic'") - tk.MustExec("analyze table trange") - tk.MustExec("analyze table thash") - tk.MustExec("analyze table t") -} - -type testData4Expression struct { - sql string - partitions []string -} - -func TestDateColWithUnequalExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_datetime_unequal_expression") - tk.MustExec("create database db_datetime_unequal_expression") - tk.MustExec("use db_datetime_unequal_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec(`create table tp(a datetime, b int) partition by range columns (a) (partition p0 values less than("2012-12-10 00:00:00"), partition p1 values less than("2022-12-30 00:00:00"), partition p2 values less than("2025-12-12 00:00:00"))`) - tk.MustExec(`create table t(a datetime, b int) partition by range columns (a) (partition p0 values less than("2012-12-10 00:00:00"), partition p1 values less than("2022-12-30 00:00:00"), partition p2 values less than("2025-12-12 00:00:00"))`) - tk.MustExec(`insert into tp values("2015-09-09 00:00:00", 1), ("2020-08-08 19:00:01", 2), ("2024-01-01 01:01:01", 3)`) - tk.MustExec(`insert into t values("2015-09-09 00:00:00", 1), ("2020-08-08 19:00:01", 2), ("2024-01-01 01:01:01", 3)`) - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a != '2024-01-01 01:01:01'", - partitions: []string{"all"}, - }, - { - sql: "select * from %s where a != '2024-01-01 01:01:01' and a > '2015-09-09 00:00:00'", - partitions: []string{"p1,p2"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestToDaysColWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_to_days_expression") - tk.MustExec("create database db_to_days_expression") - tk.MustExec("use db_to_days_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a date, b int) partition by range(to_days(a)) (partition p0 values less than (737822), partition p1 values less than (738019), partition p2 values less than (738154))") - tk.MustExec("create table t(a date, b int)") - tk.MustExec("insert into tp values('2020-01-01', 1), ('2020-03-02', 2), ('2020-05-05', 3), ('2020-11-11', 4)") - tk.MustExec("insert into t values('2020-01-01', 1), ('2020-03-02', 2), ('2020-05-05', 3), ('2020-11-11', 4)") - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a < '2020-08-16'", - partitions: []string{"p0,p1"}, - }, - { - sql: "select * from %s where a between '2020-05-01' and '2020-10-01'", - partitions: []string{"p1,p2"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestWeekdayWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_weekday_expression") - tk.MustExec("create database db_weekday_expression") - tk.MustExec("use db_weekday_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a datetime, b int) partition by range(weekday(a)) (partition p0 values less than(3), partition p1 values less than(5), partition p2 values less than(8))") - tk.MustExec("create table t(a datetime, b int)") - tk.MustExec(`insert into tp values("2020-08-17 00:00:00", 1), ("2020-08-18 00:00:00", 2), ("2020-08-19 00:00:00", 4), ("2020-08-20 00:00:00", 5), ("2020-08-21 00:00:00", 6), ("2020-08-22 00:00:00", 0)`) - tk.MustExec(`insert into t values("2020-08-17 00:00:00", 1), ("2020-08-18 00:00:00", 2), ("2020-08-19 00:00:00", 4), ("2020-08-20 00:00:00", 5), ("2020-08-21 00:00:00", 6), ("2020-08-22 00:00:00", 0)`) - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a = '2020-08-17 00:00:00'", - partitions: []string{"p0"}, - }, - { - sql: "select * from %s where a= '2020-08-20 00:00:00' and a < '2020-08-22 00:00:00'", - partitions: []string{"p1"}, - }, - { - sql: " select * from %s where a < '2020-08-19 00:00:00'", - partitions: []string{"all"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestFloorUnixTimestampAndIntColWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_floor_unix_timestamp_int_expression") - tk.MustExec("create database db_floor_unix_timestamp_int_expression") - tk.MustExec("use db_floor_unix_timestamp_int_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a timestamp, b int) partition by range(floor(unix_timestamp(a))) (partition p0 values less than(1580670000), partition p1 values less than(1597622400), partition p2 values less than(1629158400))") - tk.MustExec("create table t(a timestamp, b int)") - tk.MustExec("insert into tp values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") - tk.MustExec("insert into t values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a > '2020-09-11 00:00:00'", - partitions: []string{"p2"}, - }, - { - sql: "select * from %s where a < '2020-07-07 01:00:00'", - partitions: []string{"p0,p1"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestUnixTimestampAndIntColWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_unix_timestamp_int_expression") - tk.MustExec("create database db_unix_timestamp_int_expression") - tk.MustExec("use db_unix_timestamp_int_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a timestamp, b int) partition by range(unix_timestamp(a)) (partition p0 values less than(1580670000), partition p1 values less than(1597622400), partition p2 values less than(1629158400))") - tk.MustExec("create table t(a timestamp, b int)") - tk.MustExec("insert into tp values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") - tk.MustExec("insert into t values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a > '2020-09-11 00:00:00'", - partitions: []string{"p2"}, - }, - { - sql: "select * from %s where a < '2020-07-07 01:00:00'", - partitions: []string{"p0,p1"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestDatetimeColAndIntColWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_datetime_int_expression") - tk.MustExec("create database db_datetime_int_expression") - tk.MustExec("use db_datetime_int_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a datetime, b int) partition by range columns(a) (partition p0 values less than('2020-02-02 00:00:00'), partition p1 values less than('2020-09-01 00:00:00'), partition p2 values less than('2020-12-20 00:00:00'))") - tk.MustExec("create table t(a datetime, b int)") - tk.MustExec("insert into tp values('2020-01-01 12:00:00', 1), ('2020-08-22 10:00:00', 2), ('2020-09-09 11:00:00', 3), ('2020-10-01 00:00:00', 4)") - tk.MustExec("insert into t values('2020-01-01 12:00:00', 1), ('2020-08-22 10:00:00', 2), ('2020-09-09 11:00:00', 3), ('2020-10-01 00:00:00', 4)") - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a < '2020-09-01 00:00:00'", - partitions: []string{"p0,p1"}, - }, - { - sql: "select * from %s where a > '2020-07-07 01:00:00'", - partitions: []string{"p1,p2"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestVarcharColAndIntColWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_varchar_int_expression") - tk.MustExec("create database db_varchar_int_expression") - tk.MustExec("use db_varchar_int_expression") - tk.MustExec("set tidb_partition_prune_mode='dynamic'") - tk.MustExec("create table tp(a varchar(255), b int) partition by range columns(a) (partition p0 values less than('ddd'), partition p1 values less than('ggggg'), partition p2 values less than('mmmmmm'))") - tk.MustExec("create table t(a varchar(255), b int)") - tk.MustExec("insert into tp values('aaa', 1), ('bbbb', 2), ('ccc', 3), ('dfg', 4), ('kkkk', 5), ('10', 6)") - tk.MustExec("insert into t values('aaa', 1), ('bbbb', 2), ('ccc', 3), ('dfg', 4), ('kkkk', 5), ('10', 6)") - tk.MustExec("analyze table tp") - tk.MustExec("analyze table t") - - tests := []testData4Expression{ - { - sql: "select * from %s where a < '10'", - partitions: []string{"p0"}, - }, - { - sql: "select * from %s where a > 0", - partitions: []string{"all"}, - }, - { - sql: "select * from %s where a < 0", - partitions: []string{"all"}, - }, - } - - for _, t := range tests { - tpSQL := fmt.Sprintf(t.sql, "tp") - tSQL := fmt.Sprintf(t.sql, "t") - tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) - } -} - -func TestDynamicPruneModeWithExpression(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists db_equal_expression") - tk.MustExec("create database db_equal_expression") - tk.MustExec("use db_equal_expression") - createTable4DynamicPruneModeTestWithExpression(tk) - - tables := []string{"trange", "thash"} - tests := []testData4Expression{ - { - sql: "select * from %s where a = 2", - partitions: []string{ - "p0", - "p2", - }, - }, - { - sql: "select * from %s where a = 4 or a = 1", - partitions: []string{ - "p0,p1", - "p0,p1", - }, - }, - { - sql: "select * from %s where a = -1", - partitions: []string{ - "p0", - "p1", - }, - }, - { - sql: "select * from %s where a is NULL", - partitions: []string{ - "p0", - "p0", - }, - }, - { - sql: "select * from %s where b is NULL", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a > -1", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a >= 4 and a <= 5", - partitions: []string{ - "p1,p2", - "p0,p1", - }, - }, - { - sql: "select * from %s where a > 10", - partitions: []string{ - "dual", - "all", - }, - }, - { - sql: "select * from %s where a >=2 and a <= 3", - partitions: []string{ - "p0,p1", - "p2,p3", - }, - }, - { - sql: "select * from %s where a between 2 and 3", - partitions: []string{ - "p0,p1", - "p2,p3", - }, - }, - { - sql: "select * from %s where a < 2", - partitions: []string{ - "p0", - "all", - }, - }, - { - sql: "select * from %s where a <= 3", - partitions: []string{ - "p0,p1", - "all", - }, - }, - { - sql: "select * from %s where a in (2, 3)", - partitions: []string{ - "p0,p1", - "p2,p3", - }, - }, - { - sql: "select * from %s where a in (1, 5)", - partitions: []string{ - "p0,p2", - "p1", - }, - }, - { - sql: "select * from %s where a not in (1, 5)", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a = 2 and a = 2", - partitions: []string{ - "p0", - "p2", - }, - }, - { - sql: "select * from %s where a = 2 and a = 3", - partitions: []string{ - // This means that we have no partition-read plan - "", - "", - }, - }, - { - sql: "select * from %s where a < 2 and a > 0", - partitions: []string{ - "p0", - "p1", - }, - }, - { - sql: "select * from %s where a < 2 and a < 3", - partitions: []string{ - "p0", - "all", - }, - }, - { - sql: "select * from %s where a > 1 and a > 2", - partitions: []string{ - "p1,p2", - "all", - }, - }, - { - sql: "select * from %s where a = 2 or a = 3", - partitions: []string{ - "p0,p1", - "p2,p3", - }, - }, - { - sql: "select * from %s where a = 2 or a in (3)", - partitions: []string{ - "p0,p1", - "p2,p3", - }, - }, - { - sql: "select * from %s where a = 2 or a > 3", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a = 2 or a <= 1", - partitions: []string{ - "p0", - "all", - }, - }, - { - sql: "select * from %s where a = 2 or a between 2 and 2", - partitions: []string{ - "p0", - "p2", - }, - }, - { - sql: "select * from %s where a != 2", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a != 2 and a > 4", - partitions: []string{ - "p2", - "all", - }, - }, - { - sql: "select * from %s where a != 2 and a != 3", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a != 2 and a = 3", - partitions: []string{ - "p1", - "p3", - }, - }, - { - sql: "select * from %s where not (a = 2)", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where not (a > 2)", - partitions: []string{ - "p0", - "all", - }, - }, - { - sql: "select * from %s where not (a < 2)", - partitions: []string{ - "all", - "all", - }, - }, - // cases that partition pruning can not work - { - sql: "select * from %s where a + 1 > 4", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a - 1 > 0", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a * 2 < 0", - partitions: []string{ - "all", - "all", - }, - }, - { - sql: "select * from %s where a << 1 < 0", - partitions: []string{ - "all", - "all", - }, - }, - // comparison between int column and string column - { - sql: "select * from %s where a > '10'", - partitions: []string{ - "dual", - "all", - }, - }, - { - sql: "select * from %s where a > '10ab'", - partitions: []string{ - "dual", - "all", - }, - }, - } - - for _, t := range tests { - for i := range t.partitions { - sql := fmt.Sprintf(t.sql, tables[i]) - tk.MustPartition(sql, t.partitions[i]).Sort().Check(tk.MustQuery(fmt.Sprintf(t.sql, "t")).Sort().Rows()) - } - } -} - -func TestAddDropPartitions(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_add_drop_partition") - tk.MustExec("use test_add_drop_partition") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table t(a int) partition by range(a) ( - partition p0 values less than (5), - partition p1 values less than (10), - partition p2 values less than (15))`) - tk.MustExec(`insert into t values (2), (7), (12)`) - tk.MustPartition(`select * from t where a < 3`, "p0").Sort().Check(testkit.Rows("2")) - tk.MustPartition(`select * from t where a < 8`, "p0,p1").Sort().Check(testkit.Rows("2", "7")) - tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "2", "7")) - - // remove p0 - tk.MustExec(`alter table t drop partition p0`) - tk.MustPartition(`select * from t where a < 3`, "p1").Sort().Check(testkit.Rows()) - tk.MustPartition(`select * from t where a < 8`, "p1").Sort().Check(testkit.Rows("7")) - tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "7")) - - // add 2 more partitions - tk.MustExec(`alter table t add partition (partition p3 values less than (20))`) - tk.MustExec(`alter table t add partition (partition p4 values less than (40))`) - tk.MustExec(`insert into t values (15), (25)`) - tk.MustPartition(`select * from t where a < 3`, "p1").Sort().Check(testkit.Rows()) - tk.MustPartition(`select * from t where a < 8`, "p1").Sort().Check(testkit.Rows("7")) - tk.MustPartition(`select * from t where a < 20`, "p1,p2,p3").Sort().Check(testkit.Rows("12", "15", "7")) -} - -func TestMPPQueryExplainInfo(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database tiflash_partition_test") - tk.MustExec("use tiflash_partition_test") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table t(a int) partition by range(a) ( - partition p0 values less than (5), - partition p1 values less than (10), - partition p2 values less than (15))`) - tb := external.GetTableByName(t, tk, "tiflash_partition_test", "t") - for _, partition := range tb.Meta().GetPartitionInfo().Definitions { - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), partition.ID, true) - require.NoError(t, err) - } - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - tk.MustExec(`insert into t values (2), (7), (12)`) - tk.MustExec("set tidb_enforce_mpp=1") - tk.MustPartition(`select * from t where a < 3`, "p0").Sort().Check(testkit.Rows("2")) - tk.MustPartition(`select * from t where a < 8`, "p0,p1").Sort().Check(testkit.Rows("2", "7")) - tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "2", "7")) - tk.MustPartition(`select * from t where a < 5 union all select * from t where a > 10`, "p0").Sort().Check(testkit.Rows("12", "2")) - tk.MustPartition(`select * from t where a < 5 union all select * from t where a > 10`, "p2").Sort().Check(testkit.Rows("12", "2")) -} - -func TestPartitionPruningInTransaction(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_pruning_transaction") - defer tk.MustExec(`drop database test_pruning_transaction`) - tk.MustExec("use test_pruning_transaction") - tk.MustExec(`create table t(a int, b int) partition by range(a) (partition p0 values less than(3), partition p1 values less than (5), partition p2 values less than(11))`) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustExec(`begin`) - tk.MustPartitionByList(`select * from t`, []string{"p0", "p1", "p2"}) - tk.MustPartitionByList(`select * from t where a > 3`, []string{"p1", "p2"}) // partition pruning can work in transactions - tk.MustPartitionByList(`select * from t where a > 7`, []string{"p2"}) - tk.MustExec(`rollback`) - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec(`begin`) - tk.MustPartition(`select * from t`, "all") - tk.MustPartition(`select * from t where a > 3`, "p1,p2") // partition pruning can work in transactions - tk.MustPartition(`select * from t where a > 7`, "p2") - tk.MustExec(`rollback`) - tk.MustExec("set @@tidb_partition_prune_mode = default") -} - -func TestIssue25253(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database issue25253") - defer tk.MustExec("drop database issue25253") - tk.MustExec("use issue25253") - - tk.MustExec(`CREATE TABLE IDT_HP23902 ( - COL1 smallint DEFAULT NULL, - COL2 varchar(20) DEFAULT NULL, - COL4 datetime DEFAULT NULL, - COL3 bigint DEFAULT NULL, - COL5 float DEFAULT NULL, - KEY UK_COL1 (COL1) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin - PARTITION BY HASH( COL1+30 ) - PARTITIONS 6`) - tk.MustExec(`insert ignore into IDT_HP23902 partition(p0, p1)(col1, col3) values(-10355, 1930590137900568573), (13810, -1332233145730692137)`) - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1748 Found a row not matching the given partition set", - "Warning 1748 Found a row not matching the given partition set")) - tk.MustQuery(`select * from IDT_HP23902`).Check(testkit.Rows()) - - tk.MustExec(`create table t ( - a int - ) partition by range(a) ( - partition p0 values less than (10), - partition p1 values less than (20))`) - tk.MustExec(`insert ignore into t partition(p0)(a) values(12)`) - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1748 Found a row not matching the given partition set")) - tk.MustQuery(`select * from t`).Check(testkit.Rows()) -} - -func TestDML(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_DML") - defer tk.MustExec(`drop database test_DML`) - tk.MustExec("use test_DML") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table tinner (a int, b int)`) - tk.MustExec(`create table thash (a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a int, b int) partition by range(a) ( - partition p0 values less than(10000), - partition p1 values less than(20000), - partition p2 values less than(30000), - partition p3 values less than(40000), - partition p4 values less than MAXVALUE)`) - - vals := make([]string, 0, 50) - for i := 0; i < 50; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec(`insert into tinner values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) - - // delete, insert, replace, update - for i := 0; i < 200; i++ { - var pattern string - switch rand.Intn(4) { - case 0: // delete - col := []string{"a", "b"}[rand.Intn(2)] - l := rand.Intn(40000) - r := l + rand.Intn(5000) - pattern = fmt.Sprintf(`delete from %%v where %v>%v and %v<%v`, col, l, col, r) - case 1: // insert - a, b := rand.Intn(40000), rand.Intn(40000) - pattern = fmt.Sprintf(`insert into %%v values (%v, %v)`, a, b) - case 2: // replace - a, b := rand.Intn(40000), rand.Intn(40000) - pattern = fmt.Sprintf(`replace into %%v(a, b) values (%v, %v)`, a, b) - case 3: // update - col := []string{"a", "b"}[rand.Intn(2)] - l := rand.Intn(40000) - r := l + rand.Intn(5000) - x := rand.Intn(1000) - 500 - pattern = fmt.Sprintf(`update %%v set %v=%v+%v where %v>%v and %v<%v`, col, col, x, col, l, col, r) - } - for _, tbl := range []string{"tinner", "thash", "trange"} { - tk.MustExec(fmt.Sprintf(pattern, tbl)) - } - - // check - r := tk.MustQuery(`select * from tinner`).Sort().Rows() - tk.MustQuery(`select * from thash`).Sort().Check(r) - tk.MustQuery(`select * from trange`).Sort().Check(r) - } -} - -func TestUnion(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_union") - defer tk.MustExec(`drop database test_union`) - tk.MustExec("use test_union") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table t(a int, b int, key(a))`) - tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a int, b int, key(a)) partition by range(a) ( - partition p0 values less than (10000), - partition p1 values less than (20000), - partition p2 values less than (30000), - partition p3 values less than (40000))`) - - vals := make([]string, 0, 1000) - for i := 0; i < 1000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec(`insert into t values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) - - randRange := func() (int, int) { - l, r := rand.Intn(40000), rand.Intn(40000) - if l > r { - l, r = r, l - } - return l, r - } - - for i := 0; i < 100; i++ { - a1l, a1r := randRange() - a2l, a2r := randRange() - b1l, b1r := randRange() - b2l, b2r := randRange() - for _, utype := range []string{"union all", "union distinct"} { - pattern := fmt.Sprintf(`select * from %%v where a>=%v and a<=%v and b>=%v and b<=%v - %v select * from %%v where a>=%v and a<=%v and b>=%v and b<=%v`, a1l, a1r, b1l, b1r, utype, a2l, a2r, b2l, b2r) - r := tk.MustQuery(fmt.Sprintf(pattern, "t", "t")).Sort().Rows() - tk.MustQuery(fmt.Sprintf(pattern, "thash", "thash")).Sort().Check(r) // hash + hash - tk.MustQuery(fmt.Sprintf(pattern, "trange", "trange")).Sort().Check(r) // range + range - tk.MustQuery(fmt.Sprintf(pattern, "trange", "thash")).Sort().Check(r) // range + hash - } - } -} - -func TestSubqueries(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_subquery") - defer tk.MustExec(`drop database test_subquery`) - tk.MustExec("use test_subquery") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table touter (a int, b int, index(a))`) - tk.MustExec(`create table tinner (a int, b int, c int, index(a))`) - tk.MustExec(`create table thash (a int, b int, c int, index(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a int, b int, c int, index(a)) partition by range(a) ( - partition p0 values less than(10000), - partition p1 values less than(20000), - partition p2 values less than(30000), - partition p3 values less than(40000))`) - - outerVals := make([]string, 0, 100) - for i := 0; i < 100; i++ { - outerVals = append(outerVals, fmt.Sprintf(`(%v, %v)`, rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec(`insert into touter values ` + strings.Join(outerVals, ", ")) - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf(`(%v, %v, %v)`, rand.Intn(40000), rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec(`insert into tinner values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) - - // in - for i := 0; i < 50; i++ { - for _, op := range []string{"in", "not in"} { - x := rand.Intn(40000) - var r [][]interface{} - for _, t := range []string{"tinner", "thash", "trange"} { - q := fmt.Sprintf(`select * from touter where touter.a %v (select %v.b from %v where %v.a > touter.b and %v.c > %v)`, op, t, t, t, t, x) - if r == nil { - r = tk.MustQuery(q).Sort().Rows() - } else { - tk.MustQuery(q).Sort().Check(r) - } - } - } - } - - // exist - for i := 0; i < 50; i++ { - for _, op := range []string{"exists", "not exists"} { - x := rand.Intn(40000) - var r [][]interface{} - for _, t := range []string{"tinner", "thash", "trange"} { - q := fmt.Sprintf(`select * from touter where %v (select %v.b from %v where %v.a > touter.b and %v.c > %v)`, op, t, t, t, t, x) - if r == nil { - r = tk.MustQuery(q).Sort().Rows() - } else { - tk.MustQuery(q).Sort().Check(r) - } - } - } - } -} - -func TestSplitRegion(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_split_region") - tk.MustExec("use test_split_region") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table tnormal (a int, b int)`) - tk.MustExec(`create table thash (a int, b int, index(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a int, b int, index(a)) partition by range(a) ( - partition p0 values less than (10000), - partition p1 values less than (20000), - partition p2 values less than (30000), - partition p3 values less than (40000))`) - vals := make([]string, 0, 1000) - for i := 0; i < 1000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec(`insert into tnormal values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) - tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) - - tk.MustExec(`SPLIT TABLE thash INDEX a BETWEEN (1) AND (25000) REGIONS 10`) - tk.MustExec(`SPLIT TABLE trange INDEX a BETWEEN (1) AND (25000) REGIONS 10`) - - result := tk.MustQuery(`select * from tnormal where a>=1 and a<=15000`).Sort().Rows() - tk.MustPartition(`select * from trange where a>=1 and a<=15000`, "p0,p1").Sort().Check(result) - tk.MustPartition(`select * from thash where a>=1 and a<=15000`, "all").Sort().Check(result) - - result = tk.MustQuery(`select * from tnormal where a in (1, 10001, 20001)`).Sort().Rows() - tk.MustPartition(`select * from trange where a in (1, 10001, 20001)`, "p0,p1,p2").Sort().Check(result) - tk.MustPartition(`select * from thash where a in (1, 10001, 20001)`, "p1").Sort().Check(result) -} - -func TestParallelApply(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create database test_parallel_apply") - tk.MustExec("use test_parallel_apply") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set tidb_enable_parallel_apply=true") - - tk.MustExec(`create table touter (a int, b int)`) - tk.MustExec(`create table tinner (a int, b int, key(a))`) - tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table trange (a int, b int, key(a)) partition by range(a) ( - partition p0 values less than(10000), - partition p1 values less than(20000), - partition p2 values less than(30000), - partition p3 values less than(40000))`) - - vouter := make([]string, 0, 100) - for i := 0; i < 100; i++ { - vouter = append(vouter, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec("insert into touter values " + strings.Join(vouter, ", ")) - - vals := make([]string, 0, 2000) - for i := 0; i < 100; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) - } - tk.MustExec("insert into tinner values " + strings.Join(vals, ", ")) - tk.MustExec("insert into thash values " + strings.Join(vals, ", ")) - tk.MustExec("insert into trange values " + strings.Join(vals, ", ")) - - // parallel apply + hash partition + IndexReader as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(thash.a) from thash use index(a) where thash.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, - ` └─IndexReader 10000.00 root partition:all index:HashAgg`, // IndexReader is a inner child of Apply - ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.thash.a)->Column#8`, - ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.thash.a, test_parallel_apply.touter.b)`, - ` └─IndexFullScan 100000000.00 cop[tikv] table:thash, index:a(a) keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(thash.a) from thash use index(a) where thash.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.a) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // parallel apply + hash partition + TableReader as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(thash.b) from thash ignore index(a) where thash.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, - ` └─TableReader 10000.00 root partition:all data:HashAgg`, // TableReader is a inner child of Apply - ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.thash.b)->Column#8`, - ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.thash.a, test_parallel_apply.touter.b)`, - ` └─TableFullScan 100000000.00 cop[tikv] table:thash keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(thash.b) from thash ignore index(a) where thash.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner ignore index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // parallel apply + hash partition + IndexLookUp as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#9)->Column#7`, - ` └─IndexLookUp 10000.00 root `, // IndexLookUp is a inner child of Apply - ` ├─Selection(Build) 80000000.00 cop[tikv] gt(test_parallel_apply.tinner.a, test_parallel_apply.touter.b)`, - ` │ └─IndexFullScan 100000000.00 cop[tikv] table:tinner, index:a(a) keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 cop[tikv] funcs:sum(test_parallel_apply.tinner.b)->Column#9`, - ` └─TableRowIDScan 80000000.00 cop[tikv] table:tinner keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(thash.b) from thash use index(a) where thash.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // parallel apply + range partition + IndexReader as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(trange.a) from trange use index(a) where trange.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, - ` └─IndexReader 10000.00 root partition:all index:HashAgg`, // IndexReader is a inner child of Apply - ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.trange.a)->Column#8`, - ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.trange.a, test_parallel_apply.touter.b)`, - ` └─IndexFullScan 100000000.00 cop[tikv] table:trange, index:a(a) keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(trange.a) from trange use index(a) where trange.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.a) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // parallel apply + range partition + TableReader as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(trange.b) from trange ignore index(a) where trange.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, - ` └─TableReader 10000.00 root partition:all data:HashAgg`, // TableReader is a inner child of Apply - ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.trange.b)->Column#8`, - ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.trange.a, test_parallel_apply.touter.b)`, - ` └─TableFullScan 100000000.00 cop[tikv] table:trange keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(trange.b) from trange ignore index(a) where trange.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner ignore index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // parallel apply + range partition + IndexLookUp as its inner child - tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Check(testkit.Rows( - `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, - `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, - ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, - ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#9)->Column#7`, - ` └─IndexLookUp 10000.00 root `, // IndexLookUp is a inner child of Apply - ` ├─Selection(Build) 80000000.00 cop[tikv] gt(test_parallel_apply.tinner.a, test_parallel_apply.touter.b)`, - ` │ └─IndexFullScan 100000000.00 cop[tikv] table:tinner, index:a(a) keep order:false, stats:pseudo`, - ` └─HashAgg(Probe) 10000.00 cop[tikv] funcs:sum(test_parallel_apply.tinner.b)->Column#9`, - ` └─TableRowIDScan 80000000.00 cop[tikv] table:tinner keep order:false, stats:pseudo`)) - tk.MustQuery(`select * from touter where touter.a > (select sum(trange.b) from trange use index(a) where trange.a>touter.b)`).Sort().Check( - tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) - - // random queries - ops := []string{"!=", ">", "<", ">=", "<="} - aggFuncs := []string{"sum", "count", "max", "min"} - tbls := []string{"tinner", "thash", "trange"} - for i := 0; i < 50; i++ { - var r [][]interface{} - op := ops[rand.Intn(len(ops))] - agg := aggFuncs[rand.Intn(len(aggFuncs))] - x := rand.Intn(10000) - for _, tbl := range tbls { - q := fmt.Sprintf(`select * from touter where touter.a > (select %v(%v.b) from %v where %v.a%vtouter.b-%v)`, agg, tbl, tbl, tbl, op, x) - if r == nil { - r = tk.MustQuery(q).Sort().Rows() - } else { - tk.MustQuery(q).Sort().Check(r) - } - } - } -} - -func TestDirectReadingWithUnionScan(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_unionscan") - defer tk.MustExec(`drop database test_unionscan`) - tk.MustExec("use test_unionscan") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table trange(a int, b int, index idx_a(a)) partition by range(a) ( - partition p0 values less than (10), - partition p1 values less than (30), - partition p2 values less than (50))`) - tk.MustExec(`create table thash(a int, b int, index idx_a(a)) partition by hash(a) partitions 4`) - tk.MustExec(`create table tnormal(a int, b int, index idx_a(a))`) - - vals := make([]string, 0, 1000) - for i := 0; i < 1000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(50), rand.Intn(50))) - } - for _, tb := range []string{`trange`, `tnormal`, `thash`} { - sql := fmt.Sprintf(`insert into %v values `+strings.Join(vals, ", "), tb) - tk.MustExec(sql) - } - - randCond := func(col string) string { - la, ra := rand.Intn(50), rand.Intn(50) - if la > ra { - la, ra = ra, la - } - return fmt.Sprintf(`%v>=%v and %v<=%v`, col, la, col, ra) - } - - tk.MustExec(`begin`) - for i := 0; i < 1000; i++ { - if i == 0 || rand.Intn(2) == 0 { // insert some inflight rows - val := fmt.Sprintf("(%v, %v)", rand.Intn(50), rand.Intn(50)) - for _, tb := range []string{`trange`, `tnormal`, `thash`} { - sql := fmt.Sprintf(`insert into %v values `+val, tb) - tk.MustExec(sql) - } - } else { - var sql string - switch rand.Intn(3) { - case 0: // table scan - sql = `select * from %v ignore index(idx_a) where ` + randCond(`b`) - case 1: // index reader - sql = `select a from %v use index(idx_a) where ` + randCond(`a`) - case 2: // index lookup - sql = `select * from %v use index(idx_a) where ` + randCond(`a`) + ` and ` + randCond(`b`) - } - switch rand.Intn(2) { - case 0: // order by a - sql += ` order by a` - case 1: // order by b - sql += ` order by b` - } - - var result [][]interface{} - for _, tb := range []string{`trange`, `tnormal`, `thash`} { - q := fmt.Sprintf(sql, tb) - tk.MustHavePlan(q, `UnionScan`) - if result == nil { - result = tk.MustQuery(q).Sort().Rows() - } else { - tk.MustQuery(q).Sort().Check(result) - } - } - } - } - tk.MustExec(`rollback`) -} - -func TestIssue25030(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_issue_25030") - tk.MustExec("use test_issue_25030") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`CREATE TABLE tbl_936 ( - col_5410 smallint NOT NULL, - col_5411 double, - col_5412 boolean NOT NULL DEFAULT 1, - col_5413 set('Alice', 'Bob', 'Charlie', 'David') NOT NULL DEFAULT 'Charlie', - col_5414 varbinary(147) COLLATE 'binary' DEFAULT 'bvpKgYWLfyuTiOYSkj', - col_5415 timestamp NOT NULL DEFAULT '2021-07-06', - col_5416 decimal(6, 6) DEFAULT 0.49, - col_5417 text COLLATE utf8_bin, - col_5418 float DEFAULT 2048.0762299371554, - col_5419 int UNSIGNED NOT NULL DEFAULT 3152326370, - PRIMARY KEY (col_5419) ) - PARTITION BY HASH (col_5419) PARTITIONS 3`) - tk.MustQuery(`SELECT last_value(col_5414) OVER w FROM tbl_936 - WINDOW w AS (ORDER BY col_5410, col_5411, col_5412, col_5413, col_5414, col_5415, col_5416, col_5417, col_5418, col_5419) - ORDER BY col_5410, col_5411, col_5412, col_5413, col_5414, col_5415, col_5416, col_5417, col_5418, col_5419, nth_value(col_5412, 5) OVER w`). - Check(testkit.Rows()) // can work properly without any error or panic -} - -func TestUnsignedPartitionColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_unsigned_partition") - tk.MustExec("use test_unsigned_partition") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`create table thash_pk (a int unsigned, b int, primary key(a)) partition by hash (a) partitions 3`) - tk.MustExec(`create table trange_pk (a int unsigned, b int, primary key(a)) partition by range (a) ( - partition p1 values less than (100000), - partition p2 values less than (200000), - partition p3 values less than (300000), - partition p4 values less than (400000))`) - tk.MustExec(`create table tnormal_pk (a int unsigned, b int, primary key(a))`) - tk.MustExec(`create table thash_uniq (a int unsigned, b int, unique key(a)) partition by hash (a) partitions 3`) - tk.MustExec(`create table trange_uniq (a int unsigned, b int, unique key(a)) partition by range (a) ( - partition p1 values less than (100000), - partition p2 values less than (200000), - partition p3 values less than (300000), - partition p4 values less than (400000))`) - tk.MustExec(`create table tnormal_uniq (a int unsigned, b int, unique key(a))`) - - valColA := make(map[int]struct{}, 1000) - vals := make([]string, 0, 1000) - for len(vals) < 1000 { - a := rand.Intn(400000) - if _, ok := valColA[a]; ok { - continue - } - valColA[a] = struct{}{} - vals = append(vals, fmt.Sprintf("(%v, %v)", a, rand.Intn(400000))) - } - valStr := strings.Join(vals, ", ") - for _, tbl := range []string{"thash_pk", "trange_pk", "tnormal_pk", "thash_uniq", "trange_uniq", "tnormal_uniq"} { - tk.MustExec(fmt.Sprintf("insert into %v values %v", tbl, valStr)) - } - - for i := 0; i < 100; i++ { - scanCond := fmt.Sprintf("a %v %v", []string{">", "<"}[rand.Intn(2)], rand.Intn(400000)) - pointCond := fmt.Sprintf("a = %v", rand.Intn(400000)) - batchCond := fmt.Sprintf("a in (%v, %v, %v)", rand.Intn(400000), rand.Intn(400000), rand.Intn(400000)) - - var rScan, rPoint, rBatch [][]interface{} - for tid, tbl := range []string{"tnormal_pk", "trange_pk", "thash_pk"} { - // unsigned + TableReader - scanSQL := fmt.Sprintf("select * from %v use index(primary) where %v", tbl, scanCond) - tk.MustHavePlan(scanSQL, "TableReader") - r := tk.MustQuery(scanSQL).Sort() - if tid == 0 { - rScan = r.Rows() - } else { - r.Check(rScan) - } - - // unsigned + PointGet on PK - pointSQL := fmt.Sprintf("select * from %v use index(primary) where %v", tbl, pointCond) - tk.MustPointGet(pointSQL) - r = tk.MustQuery(pointSQL).Sort() - if tid == 0 { - rPoint = r.Rows() - } else { - r.Check(rPoint) - } - - // unsigned + BatchGet on PK - batchSQL := fmt.Sprintf("select * from %v where %v", tbl, batchCond) - tk.MustHavePlan(batchSQL, "Batch_Point_Get") - r = tk.MustQuery(batchSQL).Sort() - if tid == 0 { - rBatch = r.Rows() - } else { - r.Check(rBatch) - } - } - - lookupCond := fmt.Sprintf("a %v %v", []string{">", "<"}[rand.Intn(2)], rand.Intn(400000)) - var rLookup [][]interface{} - for tid, tbl := range []string{"tnormal_uniq", "trange_uniq", "thash_uniq"} { - // unsigned + IndexReader - scanSQL := fmt.Sprintf("select a from %v use index(a) where %v", tbl, scanCond) - tk.MustHavePlan(scanSQL, "IndexReader") - r := tk.MustQuery(scanSQL).Sort() - if tid == 0 { - rScan = r.Rows() - } else { - r.Check(rScan) - } - - // unsigned + IndexLookUp - lookupSQL := fmt.Sprintf("select * from %v use index(a) where %v", tbl, lookupCond) - tk.MustIndexLookup(lookupSQL) - r = tk.MustQuery(lookupSQL).Sort() - if tid == 0 { - rLookup = r.Rows() - } else { - r.Check(rLookup) - } - - // unsigned + PointGet on UniqueIndex - pointSQL := fmt.Sprintf("select * from %v use index(a) where %v", tbl, pointCond) - tk.MustPointGet(pointSQL) - r = tk.MustQuery(pointSQL).Sort() - if tid == 0 { - rPoint = r.Rows() - } else { - r.Check(rPoint) - } - - // unsigned + BatchGet on UniqueIndex - batchSQL := fmt.Sprintf("select * from %v where %v", tbl, batchCond) - tk.MustHavePlan(batchSQL, "Batch_Point_Get") - r = tk.MustQuery(batchSQL).Sort() - if tid == 0 { - rBatch = r.Rows() - } else { - r.Check(rBatch) - } - } - } -} - -func TestDirectReadingWithAgg(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_dr_agg") - tk.MustExec("use test_dr_agg") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // list partition table - tk.MustExec(`create table tlist(a int, b int, index idx_a(a), index idx_b(b)) partition by list(a)( - partition p0 values in (1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8), - partition p2 values in (9, 10, 11, 12));`) - - // range partition table - tk.MustExec(`create table trange(a int, b int, index idx_a(a), index idx_b(b)) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than (500), - partition p2 values less than(1100));`) - - // hash partition table - tk.MustExec(`create table thash(a int, b int) partition by hash(a) partitions 4;`) - - // regular table - tk.MustExec("create table tregular1(a int, b int, index idx_a(a))") - tk.MustExec("create table tregular2(a int, b int, index idx_a(a))") - - // generate some random data to be inserted - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000))) - } - - tk.MustExec("insert into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) - - vals = make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(12)+1, rand.Intn(20))) - } - - tk.MustExec("insert into tlist values " + strings.Join(vals, ",")) - tk.MustExec("insert into tregular2 values " + strings.Join(vals, ",")) - - // test range partition - for i := 0; i < 200; i++ { - // select /*+ stream_agg() */ a from t where a > ? group by a; - // select /*+ hash_agg() */ a from t where a > ? group by a; - // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; - // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; - x := rand.Intn(1099) - - queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from trange where a > %v group by a;", x) - queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from trange where a > %v group by a;", x) - queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - - y := rand.Intn(1099) - z := rand.Intn(1099) - - queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from trange where a in(%v, %v, %v) group by a;", x, y, z) - queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a in(%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) - - queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from trange where a in (%v, %v, %v) group by a;", x, y, z) - queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a in (%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) - } - - // test hash partition - for i := 0; i < 200; i++ { - // select /*+ stream_agg() */ a from t where a > ? group by a; - // select /*+ hash_agg() */ a from t where a > ? group by a; - // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; - // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; - x := rand.Intn(1099) - - queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from thash where a > %v group by a;", x) - queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from thash where a > %v group by a;", x) - queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - - y := rand.Intn(1099) - z := rand.Intn(1099) - - queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from thash where a in(%v, %v, %v) group by a;", x, y, z) - queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a in(%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) - - queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from thash where a in (%v, %v, %v) group by a;", x, y, z) - queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a in (%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) - } - - // test list partition - for i := 0; i < 200; i++ { - // select /*+ stream_agg() */ a from t where a > ? group by a; - // select /*+ hash_agg() */ a from t where a > ? group by a; - // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; - // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; - x := rand.Intn(12) + 1 - - queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tlist where a > %v group by a;", x) - queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular2 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tlist where a > %v group by a;", x) - queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular2 where a > %v group by a;", x) - tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - - y := rand.Intn(12) + 1 - z := rand.Intn(12) + 1 - - queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tlist where a in(%v, %v, %v) group by a;", x, y, z) - queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular2 where a in(%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) - - queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tlist where a in (%v, %v, %v) group by a;", x, y, z) - queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular2 where a in (%v, %v, %v) group by a;", x, y, z) - tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used - tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) - } -} - -func TestDynamicModeByDefault(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_dynamic_by_default") - - tk.MustExec(`create table trange(a int, b int, primary key(a) clustered, index idx_b(b)) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than(500), - partition p2 values less than(1100));`) - tk.MustExec(`create table thash(a int, b int, primary key(a) clustered, index idx_b(b)) partition by hash(a) partitions 4;`) - - for _, q := range []string{ - "explain select * from trange where a>400", - "explain select * from thash where a>=100", - } { - for _, r := range tk.MustQuery(q).Rows() { - require.NotContains(t, strings.ToLower(r[0].(string)), "partitionunion") - } - } -} - -func TestIssue24636(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_issue_24636") - tk.MustExec("use test_issue_24636") - - tk.MustExec(`CREATE TABLE t (a int, b date, c int, PRIMARY KEY (a,b)) - PARTITION BY RANGE ( TO_DAYS(b) ) ( - PARTITION p0 VALUES LESS THAN (737821), - PARTITION p1 VALUES LESS THAN (738289) - )`) - tk.MustExec(`INSERT INTO t (a, b, c) VALUES(0, '2021-05-05', 0)`) - tk.MustQuery(`select c from t use index(primary) where a=0 limit 1`).Check(testkit.Rows("0")) - - tk.MustExec(` - CREATE TABLE test_partition ( - a varchar(100) NOT NULL, - b date NOT NULL, - c varchar(100) NOT NULL, - d datetime DEFAULT NULL, - e datetime DEFAULT NULL, - f bigint(20) DEFAULT NULL, - g bigint(20) DEFAULT NULL, - h bigint(20) DEFAULT NULL, - i bigint(20) DEFAULT NULL, - j bigint(20) DEFAULT NULL, - k bigint(20) DEFAULT NULL, - l bigint(20) DEFAULT NULL, - PRIMARY KEY (a,b,c) /*T![clustered_index] NONCLUSTERED */ - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin - PARTITION BY RANGE ( TO_DAYS(b) ) ( - PARTITION pmin VALUES LESS THAN (737821), - PARTITION p20200601 VALUES LESS THAN (738289))`) - tk.MustExec(`INSERT INTO test_partition (a, b, c, d, e, f, g, h, i, j, k, l) VALUES('aaa', '2021-05-05', '428ff6a1-bb37-42ac-9883-33d7a29961e6', '2021-05-06 08:13:38', '2021-05-06 13:28:08', 0, 8, 3, 0, 9, 1, 0)`) - tk.MustQuery(`select c,j,l from test_partition where c='428ff6a1-bb37-42ac-9883-33d7a29961e6' and a='aaa' limit 0, 200`).Check(testkit.Rows("428ff6a1-bb37-42ac-9883-33d7a29961e6 9 0")) -} - -func TestIdexMerge(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_idx_merge") - tk.MustExec("use test_idx_merge") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - // list partition table - tk.MustExec(`create table tlist(a int, b int, primary key(a) clustered, index idx_b(b)) partition by list(a)( - partition p0 values in (1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8), - partition p2 values in (9, 10, 11, 12));`) - - // range partition table - tk.MustExec(`create table trange(a int, b int, primary key(a) clustered, index idx_b(b)) partition by range(a) ( - partition p0 values less than(300), - partition p1 values less than (500), - partition p2 values less than(1100));`) - - // hash partition table - tk.MustExec(`create table thash(a int, b int, primary key(a) clustered, index idx_b(b)) partition by hash(a) partitions 4;`) - - // regular table - tk.MustExec("create table tregular1(a int, b int, primary key(a) clustered)") - tk.MustExec("create table tregular2(a int, b int, primary key(a) clustered)") - - // generate some random data to be inserted - vals := make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000))) - } - - tk.MustExec("insert ignore into trange values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into thash values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into tregular1 values " + strings.Join(vals, ",")) - - vals = make([]string, 0, 2000) - for i := 0; i < 2000; i++ { - vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(12)+1, rand.Intn(20))) - } - - tk.MustExec("insert ignore into tlist values " + strings.Join(vals, ",")) - tk.MustExec("insert ignore into tregular2 values " + strings.Join(vals, ",")) - - // test range partition - for i := 0; i < 100; i++ { - x1 := rand.Intn(1099) - x2 := rand.Intn(1099) - - queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(trange) */ * from trange where a > %v or b < %v;", x1, x2) - queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b < %v;", x1, x2) - tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(trange) */ * from trange where a > %v or b > %v;", x1, x2) - queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b > %v;", x1, x2) - tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - } - - // test hash partition - for i := 0; i < 100; i++ { - x1 := rand.Intn(1099) - x2 := rand.Intn(1099) - - queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > %v or b < %v;", x1, x2) - queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregualr1) */ * from tregular1 where a > %v or b < %v;", x1, x2) - tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > %v or b > %v;", x1, x2) - queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b > %v;", x1, x2) - tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - } - - // test list partition - for i := 0; i < 100; i++ { - x1 := rand.Intn(12) + 1 - x2 := rand.Intn(12) + 1 - queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(tlist) */ * from tlist where a > %v or b < %v;", x1, x2) - queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregular2) */ * from tregular2 where a > %v or b < %v;", x1, x2) - tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) - - queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(tlist) */ * from tlist where a > %v or b > %v;", x1, x2) - queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular2) */ * from tregular2 where a > %v or b > %v;", x1, x2) - tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used - tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) - } -} - -func TestIssue25309(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create database test_issue_25309") - tk.MustExec("use test_issue_25309") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - tk.MustExec(`CREATE TABLE tbl_500 ( - col_20 tinyint(4) NOT NULL, - col_21 varchar(399) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, - col_22 json DEFAULT NULL, - col_23 blob DEFAULT NULL, - col_24 mediumint(9) NOT NULL, - col_25 float NOT NULL DEFAULT '7306.384497585912', - col_26 binary(196) NOT NULL, - col_27 timestamp DEFAULT '1976-12-08 00:00:00', - col_28 bigint(20) NOT NULL, - col_29 tinyint(1) NOT NULL DEFAULT '1', - PRIMARY KEY (col_29,col_20) /*T![clustered_index] NONCLUSTERED */, - KEY idx_7 (col_28,col_20,col_26,col_27,col_21,col_24), - KEY idx_8 (col_25,col_29,col_24) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`) - - tk.MustExec(`CREATE TABLE tbl_600 ( - col_60 int(11) NOT NULL DEFAULT '-776833487', - col_61 tinyint(1) NOT NULL DEFAULT '1', - col_62 tinyint(4) NOT NULL DEFAULT '-125', - PRIMARY KEY (col_62,col_60,col_61) /*T![clustered_index] NONCLUSTERED */, - KEY idx_19 (col_60) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci - PARTITION BY HASH( col_60 ) - PARTITIONS 1`) - - tk.MustExec(`insert into tbl_500 select -34, 'lrfGPPPUuZjtT', '{"obj1": {"sub_obj0": 100}}', 0x6C47636D, 1325624, 7306.3843, 'abc', '1976-12-08', 4757891479624162031, 0`) - tk.MustQuery(`select tbl_5.* from tbl_500 tbl_5 where col_24 in ( select col_62 from tbl_600 where tbl_5.col_26 < 'hSvHLdQeGBNIyOFXStV' )`).Check(testkit.Rows()) -} - -func TestGlobalIndexScan(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") - tk.MustExec("analyze table p") - tk.MustQuery("select id from p use index (idx) order by id").Check(testkit.Rows("1", "3", "5", "7")) -} - -func TestAggWithGlobalIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") - tk.MustExec("analyze table p") - tk.MustQuery("select count(*) from p use index (idx)").Check(testkit.Rows("4")) - tk.MustQuery("select count(*) from p partition(p0) use index (idx)").Check(testkit.Rows("1")) -} - -func TestGlobalIndexDoubleRead(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") - tk.MustQuery("select * from p use index (idx)").Sort().Check(testkit.Rows("1 3", "3 4", "5 6", "7 9")) -} - -func TestDropGlobalIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - - failpoint.Enable("github.com/pingcap/tidb/ddl/checkDropGlobalIndex", `return(true)`) - tk.MustExec("alter table p drop index idx") - failpoint.Disable("github.com/pingcap/tidb/ddl/checkDropGlobalIndex") -} - -func TestIssue20028(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("set @@tidb_partition_prune_mode='static-only'") - tk.MustExec(`create table t1 (c_datetime datetime, primary key (c_datetime)) -partition by range (to_days(c_datetime)) ( partition p0 values less than (to_days('2020-02-01')), -partition p1 values less than (to_days('2020-04-01')), -partition p2 values less than (to_days('2020-06-01')), -partition p3 values less than maxvalue)`) - tk.MustExec("create table t2 (c_datetime datetime, unique key(c_datetime))") - tk.MustExec("insert into t1 values ('2020-06-26 03:24:00'), ('2020-02-21 07:15:33'), ('2020-04-27 13:50:58')") - tk.MustExec("insert into t2 values ('2020-01-10 09:36:00'), ('2020-02-04 06:00:00'), ('2020-06-12 03:45:18')") - tk.MustExec("begin") - tk.MustQuery("select * from t1 join t2 on t1.c_datetime >= t2.c_datetime for update"). - Sort(). - Check(testkit.Rows( - "2020-02-21 07:15:33 2020-01-10 09:36:00", - "2020-02-21 07:15:33 2020-02-04 06:00:00", - "2020-04-27 13:50:58 2020-01-10 09:36:00", - "2020-04-27 13:50:58 2020-02-04 06:00:00", - "2020-06-26 03:24:00 2020-01-10 09:36:00", - "2020-06-26 03:24:00 2020-02-04 06:00:00", - "2020-06-26 03:24:00 2020-06-12 03:45:18")) - tk.MustExec("rollback") -} - -func TestSelectLockOnPartitionTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists pt") - tk.MustExec(`create table pt (id int primary key, k int, c int, index(k)) -partition by range (id) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (11))`) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - optimisticTableReader := func() { - tk.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk.MustExec("begin") - tk.MustQuery("select id, k from pt ignore index (k) where k = 5 for update").Check(testkit.Rows("5 5")) - tk2.MustExec("update pt set c = c + 1 where k = 5") - _, err := tk.Exec("commit") - require.Error(t, err) // Write conflict - } - - optimisticIndexReader := func() { - tk.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk.MustExec("begin") - // This is not index reader actually. - tk.MustQuery("select k from pt where k = 5 for update").Check(testkit.Rows("5")) - tk2.MustExec("update pt set c = c + 1 where k = 5") - _, err := tk.Exec("commit") - require.Error(t, err) - } - - optimisticIndexLookUp := func() { - tk.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk.MustExec("begin") - tk.MustQuery("select c, k from pt use index (k) where k = 5 for update").Check(testkit.Rows("5 5")) - tk2.MustExec("update pt set c = c + 1 where k = 5") - _, err := tk.Exec("commit") - require.Error(t, err) - } - - pessimisticTableReader := func() { - tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk.MustExec("begin") - tk.MustQuery("select id, k from pt ignore index (k) where k = 5 for update").Check(testkit.Rows("5 5")) - ch := make(chan int, 2) - go func() { - tk2.MustExec("update pt set c = c + 1 where k = 5") - ch <- 1 - }() - time.Sleep(100 * time.Millisecond) - ch <- 2 - - // Check the operation in the goroutine is blocked, if not the first result in - // the channel should be 1. - require.Equal(t, 2, <-ch) - - tk.MustExec("commit") - <-ch - tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) - } - - pessimisticIndexReader := func() { - tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk.MustExec("begin") - // This is not index reader actually. - tk.MustQuery("select k from pt where k = 5 for update").Check(testkit.Rows("5")) - ch := make(chan int, 2) - go func() { - tk2.MustExec("update pt set c = c + 1 where k = 5") - ch <- 1 - }() - time.Sleep(100 * time.Millisecond) - ch <- 2 - - // Check the operation in the goroutine is blocked, - require.Equal(t, 2, <-ch) - - tk.MustExec("commit") - <-ch - tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) - } - - pessimisticIndexLookUp := func() { - tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk.MustExec("begin") - tk.MustQuery("select c, k from pt use index (k) where k = 5 for update").Check(testkit.Rows("5 5")) - ch := make(chan int, 2) - go func() { - tk2.MustExec("update pt set c = c + 1 where k = 5") - ch <- 1 - }() - time.Sleep(100 * time.Millisecond) - ch <- 2 - - // Check the operation in the goroutine is blocked, - require.Equal(t, 2, <-ch) - - tk.MustExec("commit") - <-ch - tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) - } - - partitionModes := []string{ - "'dynamic'", - "'static'", - } - testCases := []func(){ - optimisticTableReader, - optimisticIndexLookUp, - optimisticIndexReader, - pessimisticTableReader, - pessimisticIndexReader, - pessimisticIndexLookUp, - } - - for _, mode := range partitionModes { - tk.MustExec("set @@tidb_partition_prune_mode=" + mode) - for _, c := range testCases { - tk.MustExec("replace into pt values (5, 5, 5)") - c() - } - } -} - -func TestIssue21731(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p, t") - tk.MustExec("set @@tidb_enable_list_partition = OFF") - // Notice that this does not really test the issue #21731 - tk.MustExec("create table t (a int, b int, unique index idx(a)) partition by list columns(b) (partition p0 values in (1), partition p1 values in (2));") -} - -type testOutput struct { - SQL string - Plan []string - Res []string -} - -func verifyPartitionResult(tk *testkit.TestKit, input []string, output []testOutput) { - for i, tt := range input { - var isSelect = false - if strings.HasPrefix(strings.ToLower(tt), "select ") { - isSelect = true - } - testdata.OnRecord(func() { - output[i].SQL = tt - if isSelect { - output[i].Plan = testdata.ConvertRowsToStrings(tk.UsedPartitions(tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - } else { - // Just verify SELECT (also avoid double INSERTs during record) - output[i].Res = nil - output[i].Plan = nil - } - }) - if isSelect { - tk.UsedPartitions(tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } else { - tk.MustExec(tt) - } - } -} - -func TestRangePartitionBoundariesEq(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("SET @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("CREATE DATABASE TestRangePartitionBoundaries") - defer tk.MustExec("DROP DATABASE TestRangePartitionBoundaries") - tk.MustExec("USE TestRangePartitionBoundaries") - tk.MustExec("DROP TABLE IF EXISTS t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1000000), - PARTITION p1 VALUES LESS THAN (2000000), - PARTITION p2 VALUES LESS THAN (3000000)); -`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestRangePartitionBoundariesNe(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("SET @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("CREATE DATABASE TestRangePartitionBoundariesNe") - defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesNe") - tk.MustExec("USE TestRangePartitionBoundariesNe") - tk.MustExec("DROP TABLE IF EXISTS t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1), - PARTITION p1 VALUES LESS THAN (2), - PARTITION p2 VALUES LESS THAN (3), - PARTITION p3 VALUES LESS THAN (4), - PARTITION p4 VALUES LESS THAN (5), - PARTITION p5 VALUES LESS THAN (6), - PARTITION p6 VALUES LESS THAN (7))`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestRangePartitionBoundariesBetweenM(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("CREATE DATABASE IF NOT EXISTS TestRangePartitionBoundariesBetweenM") - defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesBetweenM") - tk.MustExec("USE TestRangePartitionBoundariesBetweenM") - tk.MustExec("DROP TABLE IF EXISTS t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1000000), - PARTITION p1 VALUES LESS THAN (2000000), - PARTITION p2 VALUES LESS THAN (3000000))`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestRangePartitionBoundariesBetweenS(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("CREATE DATABASE IF NOT EXISTS TestRangePartitionBoundariesBetweenS") - defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesBetweenS") - tk.MustExec("USE TestRangePartitionBoundariesBetweenS") - tk.MustExec("DROP TABLE IF EXISTS t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1), - PARTITION p1 VALUES LESS THAN (2), - PARTITION p2 VALUES LESS THAN (3), - PARTITION p3 VALUES LESS THAN (4), - PARTITION p4 VALUES LESS THAN (5), - PARTITION p5 VALUES LESS THAN (6), - PARTITION p6 VALUES LESS THAN (7))`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestRangePartitionBoundariesLtM(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("create database TestRangePartitionBoundariesLtM") - defer tk.MustExec("drop database TestRangePartitionBoundariesLtM") - tk.MustExec("use TestRangePartitionBoundariesLtM") - tk.MustExec("drop table if exists t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1000000), - PARTITION p1 VALUES LESS THAN (2000000), - PARTITION p2 VALUES LESS THAN (3000000))`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestRangePartitionBoundariesLtS(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("create database TestRangePartitionBoundariesLtS") - defer tk.MustExec("drop database TestRangePartitionBoundariesLtS") - tk.MustExec("use TestRangePartitionBoundariesLtS") - tk.MustExec("drop table if exists t") - tk.MustExec(`CREATE TABLE t -(a INT, b varchar(255)) -PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (1), - PARTITION p1 VALUES LESS THAN (2), - PARTITION p2 VALUES LESS THAN (3), - PARTITION p3 VALUES LESS THAN (4), - PARTITION p4 VALUES LESS THAN (5), - PARTITION p5 VALUES LESS THAN (6), - PARTITION p6 VALUES LESS THAN (7))`) - - var input []string - var output []testOutput - executorSuiteData.LoadTestCases(t, &input, &output) - verifyPartitionResult(tk, input, output) -} - -func TestIssue25528(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustExec("use test") - tk.MustExec("create table issue25528 (id int primary key, balance DECIMAL(10, 2), balance2 DECIMAL(10, 2) GENERATED ALWAYS AS (-balance) VIRTUAL, created_at TIMESTAMP) PARTITION BY HASH(id) PARTITIONS 8") - tk.MustExec("insert into issue25528 (id, balance, created_at) values(1, 100, '2021-06-17 22:35:20')") - tk.MustExec("begin pessimistic") - tk.MustQuery("select * from issue25528 where id = 1 for update").Check(testkit.Rows("1 100.00 -100.00 2021-06-17 22:35:20")) - - tk.MustExec("drop table if exists issue25528") - tk.MustExec("CREATE TABLE `issue25528` ( `c1` int(11) NOT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, `c4` int(11) DEFAULT NULL, PRIMARY KEY (`c1`) /*T![clustered_index] CLUSTERED */, KEY `k2` (`c2`), KEY `k3` (`c3`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH( `c1` ) PARTITIONS 10;") - tk.MustExec("INSERT INTO issue25528 (`c1`, `c2`, `c3`, `c4`) VALUES (1, 1, 1, 1) , (3, 3, 3, 3) , (2, 2, 2, 2) , (4, 4, 4, 4);") - tk.MustQuery("select * from issue25528 where c1 in (3, 4) order by c2 for update;").Check(testkit.Rows("3 3 3 3", "4 4 4 4")) -} - -func TestIssue26251(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1 := testkit.NewTestKit(t, store) - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - tk1.MustExec("use test") - tk1.MustExec("create table tp (id int primary key) partition by range (id) (partition p0 values less than (100));") - tk1.MustExec("create table tn (id int primary key);") - tk1.MustExec("insert into tp values(1),(2);") - tk1.MustExec("insert into tn values(1),(2);") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - tk1.MustExec("begin pessimistic") - tk1.MustQuery("select * from tp,tn where tp.id=tn.id and tn.id<=1 for update;").Check(testkit.Rows("1 1")) - - ch := make(chan struct{}, 1) - tk2.MustExec("begin pessimistic") - go func() { - // This query should block. - tk2.MustQuery("select * from tn where id=1 for update;").Check(testkit.Rows("1")) - ch <- struct{}{} - }() - - select { - case <-time.After(100 * time.Millisecond): - // Expected, query blocked, not finish within 100ms. - tk1.MustExec("rollback") - case <-ch: - // Unexpected, test fail. - t.Fail() - } - - // Clean up - <-ch - tk2.MustExec("rollback") -} - -func TestLeftJoinForUpdate(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("create database TestLeftJoinForUpdate") - defer tk1.MustExec("drop database TestLeftJoinForUpdate") - tk1.MustExec("use TestLeftJoinForUpdate") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use TestLeftJoinForUpdate") - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec("use TestLeftJoinForUpdate") - - tk1.MustExec("drop table if exists nt, pt") - tk1.MustExec("create table nt (id int, col varchar(32), primary key (id))") - tk1.MustExec("create table pt (id int, col varchar(32), primary key (id)) partition by hash(id) partitions 4") - - resetData := func() { - tk1.MustExec("truncate table nt") - tk1.MustExec("truncate table pt") - tk1.MustExec("insert into nt values (1, 'hello')") - tk1.MustExec("insert into pt values (2, 'test')") - } - - // ========================== First round of test ================== - // partition table left join normal table. - // ================================================================= - resetData() - ch := make(chan int, 10) - tk1.MustExec("begin pessimistic") - // No union scan - tk1.MustQuery("select * from pt left join nt on pt.id = nt.id for update").Check(testkit.Rows("2 test ")) - go func() { - // Check the key is locked. - tk2.MustExec("update pt set col = 'xxx' where id = 2") - ch <- 2 - }() - - // Union scan - tk1.MustExec("insert into pt values (1, 'world')") - tk1.MustQuery("select * from pt left join nt on pt.id = nt.id for update").Sort().Check(testkit.Rows("1 world 1 hello", "2 test ")) - go func() { - // Check the key is locked. - tk3.MustExec("update nt set col = 'yyy' where id = 1") - ch <- 3 - }() - - // Give chance for the goroutines to run first. - time.Sleep(80 * time.Millisecond) - ch <- 1 - tk1.MustExec("rollback") - - checkOrder := func() { - require.Equal(t, <-ch, 1) - v1 := <-ch - v2 := <-ch - require.True(t, (v1 == 2 && v2 == 3) || (v1 == 3 && v2 == 2)) - } - checkOrder() - - // ========================== Another round of test ================== - // normal table left join partition table. - // =================================================================== - resetData() - tk1.MustExec("begin pessimistic") - // No union scan - tk1.MustQuery("select * from nt left join pt on pt.id = nt.id for update").Check(testkit.Rows("1 hello ")) - - // Union scan - tk1.MustExec("insert into pt values (1, 'world')") - tk1.MustQuery("select * from nt left join pt on pt.id = nt.id for update").Check(testkit.Rows("1 hello 1 world")) - go func() { - tk2.MustExec("replace into pt values (1, 'aaa')") - ch <- 2 - }() - go func() { - tk3.MustExec("update nt set col = 'bbb' where id = 1") - ch <- 3 - }() - time.Sleep(80 * time.Millisecond) - ch <- 1 - tk1.MustExec("rollback") - checkOrder() -} - -func TestIssue31024(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("create database TestIssue31024") - defer tk1.MustExec("drop database TestIssue31024") - tk1.MustExec("use TestIssue31024") - tk1.MustExec("create table t1 (c_datetime datetime, c1 int, c2 int, primary key (c_datetime), key(c1), key(c2))" + - " partition by range (to_days(c_datetime)) " + - "( partition p0 values less than (to_days('2020-02-01'))," + - " partition p1 values less than (to_days('2020-04-01'))," + - " partition p2 values less than (to_days('2020-06-01'))," + - " partition p3 values less than maxvalue)") - tk1.MustExec("create table t2 (c_datetime datetime, unique key(c_datetime))") - tk1.MustExec("insert into t1 values ('2020-06-26 03:24:00', 1, 1), ('2020-02-21 07:15:33', 2, 2), ('2020-04-27 13:50:58', 3, 3)") - tk1.MustExec("insert into t2 values ('2020-01-10 09:36:00'), ('2020-02-04 06:00:00'), ('2020-06-12 03:45:18')") - tk1.MustExec("SET GLOBAL tidb_txn_mode = 'pessimistic'") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use TestIssue31024") - - ch := make(chan int, 10) - tk1.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk1.MustExec("begin pessimistic") - tk1.MustQuery("select /*+ use_index_merge(t1) */ * from t1 join t2 on t1.c_datetime >= t2.c_datetime where t1.c1 < 10 or t1.c2 < 10 for update") - - go func() { - // Check the key is locked. - tk2.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk2.MustExec("begin pessimistic") - tk2.MustExec("update t1 set c_datetime = '2020-06-26 03:24:00' where c1 = 1") - ch <- 2 - }() - - // Give chance for the goroutines to run first. - time.Sleep(80 * time.Millisecond) - ch <- 1 - tk1.MustExec("rollback") - - require.Equal(t, <-ch, 1) - require.Equal(t, <-ch, 2) - - tk2.MustExec("rollback") -} - -func TestIssue27346(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("create database TestIssue27346") - defer tk1.MustExec("drop database TestIssue27346") - tk1.MustExec("use TestIssue27346") - - tk1.MustExec("set @@tidb_enable_index_merge=1,@@tidb_partition_prune_mode='dynamic'") - - tk1.MustExec("DROP TABLE IF EXISTS `tbl_18`") - tk1.MustExec("CREATE TABLE `tbl_18` (`col_119` binary(16) NOT NULL DEFAULT 'skPoKiwYUi',`col_120` int(10) unsigned NOT NULL,`col_121` timestamp NOT NULL,`col_122` double NOT NULL DEFAULT '3937.1887880628115',`col_123` bigint(20) NOT NULL DEFAULT '3550098074891542725',PRIMARY KEY (`col_123`,`col_121`,`col_122`,`col_120`) CLUSTERED,UNIQUE KEY `idx_103` (`col_123`,`col_119`,`col_120`),UNIQUE KEY `idx_104` (`col_122`,`col_120`),UNIQUE KEY `idx_105` (`col_119`,`col_120`),KEY `idx_106` (`col_121`,`col_120`,`col_122`,`col_119`),KEY `idx_107` (`col_121`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci PARTITION BY HASH( `col_120` ) PARTITIONS 3") - tk1.MustExec("INSERT INTO tbl_18 (`col_119`, `col_120`, `col_121`, `col_122`, `col_123`) VALUES (X'736b506f4b6977595569000000000000', 672436701, '1974-02-24 00:00:00', 3937.1887880628115e0, -7373106839136381229), (X'736b506f4b6977595569000000000000', 2637316689, '1993-10-29 00:00:00', 3937.1887880628115e0, -4522626077860026631), (X'736b506f4b6977595569000000000000', 831809724, '1995-11-20 00:00:00', 3937.1887880628115e0, -4426441253940231780), (X'736b506f4b6977595569000000000000', 1588592628, '2001-03-28 00:00:00', 3937.1887880628115e0, 1329207475772244999), (X'736b506f4b6977595569000000000000', 3908038471, '2031-06-06 00:00:00', 3937.1887880628115e0, -6562815696723135786), (X'736b506f4b6977595569000000000000', 1674237178, '2001-10-24 00:00:00', 3937.1887880628115e0, -6459065549188938772), (X'736b506f4b6977595569000000000000', 3507075493, '2010-03-25 00:00:00', 3937.1887880628115e0, -4329597025765326929), (X'736b506f4b6977595569000000000000', 1276461709, '2019-07-20 00:00:00', 3937.1887880628115e0, 3550098074891542725)") - - tk1.MustQuery("select col_120,col_122,col_123 from tbl_18 where tbl_18.col_122 = 4763.320888074281 and not( tbl_18.col_121 in ( '2032-11-01' , '1975-05-21' , '1994-05-16' , '1984-01-15' ) ) or not( tbl_18.col_121 >= '2008-10-24' ) order by tbl_18.col_119,tbl_18.col_120,tbl_18.col_121,tbl_18.col_122,tbl_18.col_123 limit 919 for update").Sort().Check(testkit.Rows( - "1588592628 3937.1887880628115 1329207475772244999", - "1674237178 3937.1887880628115 -6459065549188938772", - "2637316689 3937.1887880628115 -4522626077860026631", - "672436701 3937.1887880628115 -7373106839136381229", - "831809724 3937.1887880628115 -4426441253940231780")) - tk1.MustQuery("select /*+ use_index_merge( tbl_18 ) */ col_120,col_122,col_123 from tbl_18 where tbl_18.col_122 = 4763.320888074281 and not( tbl_18.col_121 in ( '2032-11-01' , '1975-05-21' , '1994-05-16' , '1984-01-15' ) ) or not( tbl_18.col_121 >= '2008-10-24' ) order by tbl_18.col_119,tbl_18.col_120,tbl_18.col_121,tbl_18.col_122,tbl_18.col_123 limit 919 for update").Sort().Check(testkit.Rows( - "1588592628 3937.1887880628115 1329207475772244999", - "1674237178 3937.1887880628115 -6459065549188938772", - "2637316689 3937.1887880628115 -4522626077860026631", - "672436701 3937.1887880628115 -7373106839136381229", - "831809724 3937.1887880628115 -4426441253940231780")) -} - -func TestIssue35181(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database TestIssue35181") - tk.MustExec("use TestIssue35181") - tk.MustExec("CREATE TABLE `t` (`a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (2021), PARTITION `p1` VALUES LESS THAN (3000))") - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustExec(`insert into t select * from t where a=3000`) - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec(`insert into t select * from t where a=3000`) -} - -func TestIssue21732(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database TestIssue21732") - tk.MustExec("use TestIssue21732") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (a int, b int GENERATED ALWAYS AS (3*a-2*a) VIRTUAL) partition by hash(b) partitions 2;`) - tk.MustExec("alter table p add unique index idx (a);") - tk.MustExec("insert into p (a) values (1),(2),(3);") - tk.MustQuery("select * from p use index (idx)").Sort().Check(testkit.Rows("1 1", "2 2", "3 3")) - tk.MustExec("drop database TestIssue21732") -} - -func TestGlobalIndexSelectSpecifiedPartition(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") - tk.MustQuery("select * from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1 3")) -} - -func TestGlobalIndexScanSpecifiedPartition(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") - tk.MustExec("analyze table p") - tk.MustQuery("select id from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1")) -} - -func TestGlobalIndexScanForClusteredSpecifiedPartition(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table p (id int, c int, d int, e int, primary key(d, c) clustered) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table p add unique idx(id)") - tk.MustExec("insert into p values (1,3,1,1), (3,4,3,3), (5,6,5,5), (7,9,7,7)") - tk.MustExec("analyze table p") - tk.MustQuery("select id from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1")) -} - -func TestGlobalIndexJoin(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table t1 (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table t1 add unique idx(id)") - tk.MustExec("insert into t1 values (1,3), (3,4), (5,6), (7,9)") - - tk.MustExec(`create table t2 (id int, c int)`) - tk.MustExec("insert into t2 values (1, 3)") - - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 3")) - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) -} - -func TestGlobalIndexJoinSpecifiedPartition(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table t1 (id int, c int) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table t1 add unique idx(id)") - tk.MustExec("insert into t1 values (1,3), (3,4), (5,6), (7,9)") - - tk.MustExec(`create table t2 (id int, c int)`) - tk.MustExec("insert into t2 values (1, 3)") - - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 3")) - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) -} - -func TestGlobalIndexJoinForClusteredSpecifiedPartition(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists p") - tk.MustExec(`create table t1 (id int, c int, d int, e int, primary key(d, c) clustered) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - tk.MustExec("alter table t1 add unique idx(id)") - tk.MustExec("insert into t1 values (1,3,1,1), (3,4,3,3), (5,6,5,5), (7,9,7,7)") - - tk.MustExec(`create table t2 (id int, c int)`) - tk.MustExec("insert into t2 values (1, 3)") - - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 1 1 3")) - tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) -} - -func TestGlobalIndexForIssue40149(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - for _, opt := range []string{"true", "false"} { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(`+opt+`)`) - tk.MustExec("use test") - tk.MustExec("drop table if exists test_t1") - tk.MustExec(`CREATE TABLE test_t1 ( - a int(11) NOT NULL, - b int(11) DEFAULT NULL, - c int(11) DEFAULT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY RANGE (c) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (MAXVALUE));`) - tk.MustExec("alter table test_t1 add unique p_a (a);") - tk.MustExec("insert into test_t1 values (1,1,1);") - tk.MustQuery("select * from test_t1 where a = 1;").Sort().Check(testkit.Rows("1 1 1")) - failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - } -} - -func TestGlobalIndexMerge(t *testing.T) { - restoreConfig := config.RestoreFunc() - defer restoreConfig() - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableGlobalIndex = true - }) - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("SET session tidb_enable_index_merge = ON;") - tk.MustExec("drop table if exists t") - tk.MustExec(`CREATE TABLE t ( - a int(11) NOT NULL, - b int(11) DEFAULT NULL, - c int(11) DEFAULT NULL, - d int(11) NOT NULL AUTO_INCREMENT, - KEY idx_bd (b, c), - UNIQUE KEY uidx_ac(a) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY RANGE (c) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (MAXVALUE));`) - tk.MustExec("insert into t values (1,1,1,1),(2,2,2,2),(3,3,3,3),(4,4,4,4),(5,5,5,5),(6,6,6,6),(7,7,7,7),(8,8,8,8);") - tk.MustExec("analyze table t") - // when index_merge has global index as its partial path, ignore it. - require.False(t, tk.MustUseIndex("select /*+ use_index_merge(t, uidx_ac, idx_bc) */ * from t where a=1 or b=2", "uidx_ac")) - tk.MustQuery("select /*+ use_index_merge(t, uidx_ac, idx_bc) */ * from t where a=1 or b=2").Sort().Check( - testkit.Rows("1 1 1 1", "2 2 2 2")) -} - -func TestIssue39999(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec(`create schema test39999`) - tk.MustExec(`use test39999`) - tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) - tk.MustExec(`drop table if exists c, t`) - tk.MustExec("CREATE TABLE `c` (" + - "`serial_id` varchar(24)," + - "`occur_trade_date` date," + - "`txt_account_id` varchar(24)," + - "`capital_sub_class` varchar(10)," + - "`occur_amount` decimal(16,2)," + - "`broker` varchar(10)," + - "PRIMARY KEY (`txt_account_id`,`occur_trade_date`,`serial_id`) /*T![clustered_index] CLUSTERED */," + - "KEY `idx_serial_id` (`serial_id`)" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci " + - "PARTITION BY RANGE COLUMNS(`serial_id`) (" + - "PARTITION `p202209` VALUES LESS THAN ('20221001')," + - "PARTITION `p202210` VALUES LESS THAN ('20221101')," + - "PARTITION `p202211` VALUES LESS THAN ('20221201')" + - ")") - - tk.MustExec("CREATE TABLE `t` ( " + - "`txn_account_id` varchar(24), " + - "`account_id` varchar(32), " + - "`broker` varchar(10), " + - "PRIMARY KEY (`txn_account_id`) /*T![clustered_index] CLUSTERED */ " + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci") - - tk.MustExec("INSERT INTO `c` (serial_id, txt_account_id, capital_sub_class, occur_trade_date, occur_amount, broker) VALUES ('2022111700196920','04482786','CUST','2022-11-17',-2.01,'0009')") - tk.MustExec("INSERT INTO `t` VALUES ('04482786','1142927','0009')") - - tk.MustExec(`set tidb_partition_prune_mode='dynamic'`) - tk.MustExec(`analyze table c`) - tk.MustExec(`analyze table t`) - query := `select - /*+ inl_join(c) */ - c.occur_amount -from - c - join t on c.txt_account_id = t.txn_account_id - and t.broker = '0009' - and c.occur_trade_date = '2022-11-17'` - tk.MustQuery("explain " + query).Check(testkit.Rows(""+ - "IndexJoin_22 1.00 root inner join, inner:TableReader_21, outer key:test39999.t.txn_account_id, inner key:test39999.c.txt_account_id, equal cond:eq(test39999.t.txn_account_id, test39999.c.txt_account_id)", - "├─TableReader_27(Build) 1.00 root data:Selection_26", - "│ └─Selection_26 1.00 cop[tikv] eq(test39999.t.broker, \"0009\")", - "│ └─TableFullScan_25 1.00 cop[tikv] table:t keep order:false", - "└─TableReader_21(Probe) 1.00 root partition:all data:Selection_20", - " └─Selection_20 1.00 cop[tikv] eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)", - " └─TableRangeScan_19 1.00 cop[tikv] table:c range: decided by [eq(test39999.c.txt_account_id, test39999.t.txn_account_id) eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)], keep order:false")) - tk.MustQuery(query).Check(testkit.Rows("-2.01")) - - // Add the missing partition key part. - tk.MustExec(`alter table t add column serial_id varchar(24) default '2022111700196920'`) - query += ` and c.serial_id = t.serial_id` - tk.MustQuery(query).Check(testkit.Rows("-2.01")) - tk.MustQuery("explain " + query).Check(testkit.Rows(""+ - `IndexJoin_20 0.80 root inner join, inner:TableReader_19, outer key:test39999.t.txn_account_id, test39999.t.serial_id, inner key:test39999.c.txt_account_id, test39999.c.serial_id, equal cond:eq(test39999.t.serial_id, test39999.c.serial_id), eq(test39999.t.txn_account_id, test39999.c.txt_account_id)`, - `├─TableReader_25(Build) 0.80 root data:Selection_24`, - `│ └─Selection_24 0.80 cop[tikv] eq(test39999.t.broker, "0009"), not(isnull(test39999.t.serial_id))`, - `│ └─TableFullScan_23 1.00 cop[tikv] table:t keep order:false`, - `└─TableReader_19(Probe) 0.80 root partition:all data:Selection_18`, - ` └─Selection_18 0.80 cop[tikv] eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)`, - ` └─TableRangeScan_17 0.80 cop[tikv] table:c range: decided by [eq(test39999.c.txt_account_id, test39999.t.txn_account_id) eq(test39999.c.serial_id, test39999.t.serial_id) eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)], keep order:false`)) -} diff --git a/executor/plan_replayer.go b/executor/plan_replayer.go deleted file mode 100644 index a02238243be6e..0000000000000 --- a/executor/plan_replayer.go +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "archive/zip" - "bytes" - "context" - "encoding/json" - "fmt" - "os" - "strings" - - "github.com/BurntSushi/toml" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -var _ exec.Executor = &PlanReplayerExec{} -var _ exec.Executor = &PlanReplayerLoadExec{} - -// PlanReplayerExec represents a plan replayer executor. -type PlanReplayerExec struct { - exec.BaseExecutor - CaptureInfo *PlanReplayerCaptureInfo - DumpInfo *PlanReplayerDumpInfo - endFlag bool -} - -// PlanReplayerCaptureInfo indicates capture info -type PlanReplayerCaptureInfo struct { - SQLDigest string - PlanDigest string - Remove bool -} - -// PlanReplayerDumpInfo indicates dump info -type PlanReplayerDumpInfo struct { - ExecStmts []ast.StmtNode - Analyze bool - HistoricalStatsTS uint64 - StartTS uint64 - Path string - File *os.File - FileName string - ctx sessionctx.Context -} - -// Next implements the Executor Next interface. -func (e *PlanReplayerExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if e.endFlag { - return nil - } - if e.CaptureInfo != nil { - if e.CaptureInfo.Remove { - return e.removeCaptureTask(ctx) - } - return e.registerCaptureTask(ctx) - } - err := e.createFile() - if err != nil { - return err - } - // Note: - // For the dumping for SQL file case (len(e.DumpInfo.Path) > 0), the DumpInfo.dump() is called in - // handleFileTransInConn(), which is after TxnManager.OnTxnEnd(), where we can't access the TxnManager anymore. - // So we must fetch the startTS now. - startTS, err := sessiontxn.GetTxnManager(e.Ctx()).GetStmtReadTS() - if err != nil { - return err - } - e.DumpInfo.StartTS = startTS - if len(e.DumpInfo.Path) > 0 { - err = e.prepare() - if err != nil { - return err - } - // As we can only read file from handleSpecialQuery, thus we store the file token in the session var during `dump` - // and return nil here. - e.endFlag = true - return nil - } - if e.DumpInfo.ExecStmts == nil { - return errors.New("plan replayer: sql is empty") - } - err = e.DumpInfo.dump(ctx) - if err != nil { - return err - } - req.AppendString(0, e.DumpInfo.FileName) - e.endFlag = true - return nil -} - -func (e *PlanReplayerExec) removeCaptureTask(ctx context.Context) error { - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) - _, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("delete from mysql.plan_replayer_task where sql_digest = '%s' and plan_digest = '%s'", - e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest)) - if err != nil { - logutil.BgLogger().Warn("remove mysql.plan_replayer_status record failed", - zap.Error(err)) - return err - } - err = domain.GetDomain(e.Ctx()).GetPlanReplayerHandle().CollectPlanReplayerTask() - if err != nil { - logutil.BgLogger().Warn("collect task failed", zap.Error(err)) - } - logutil.BgLogger().Info("collect plan replayer task success") - e.endFlag = true - return nil -} - -func (e *PlanReplayerExec) registerCaptureTask(ctx context.Context) error { - ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) - exists, err := domain.CheckPlanReplayerTaskExists(ctx1, e.Ctx(), e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest) - if err != nil { - return err - } - if exists { - return errors.New("plan replayer capture task already exists") - } - exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) - _, _, err = exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("insert into mysql.plan_replayer_task (sql_digest, plan_digest) values ('%s','%s')", - e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest)) - if err != nil { - logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", - zap.Error(err)) - return err - } - err = domain.GetDomain(e.Ctx()).GetPlanReplayerHandle().CollectPlanReplayerTask() - if err != nil { - logutil.BgLogger().Warn("collect task failed", zap.Error(err)) - } - logutil.BgLogger().Info("collect plan replayer task success") - e.endFlag = true - return nil -} - -func (e *PlanReplayerExec) createFile() error { - var err error - e.DumpInfo.File, e.DumpInfo.FileName, err = replayer.GeneratePlanReplayerFile(false, false, false) - if err != nil { - return err - } - return nil -} - -func (e *PlanReplayerDumpInfo) dump(ctx context.Context) (err error) { - fileName := e.FileName - zf := e.File - task := &domain.PlanReplayerDumpTask{ - StartTS: e.StartTS, - FileName: fileName, - Zf: zf, - SessionVars: e.ctx.GetSessionVars(), - TblStats: nil, - ExecStmts: e.ExecStmts, - Analyze: e.Analyze, - HistoricalStatsTS: e.HistoricalStatsTS, - } - err = domain.DumpPlanReplayerInfo(ctx, e.ctx, task) - if err != nil { - return err - } - e.ctx.GetSessionVars().LastPlanReplayerToken = e.FileName - return nil -} - -func (e *PlanReplayerExec) prepare() error { - val := e.Ctx().Value(PlanReplayerDumpVarKey) - if val != nil { - e.Ctx().SetValue(PlanReplayerDumpVarKey, nil) - return errors.New("plan replayer: previous plan replayer dump option isn't closed normally, please try again") - } - e.Ctx().SetValue(PlanReplayerDumpVarKey, e.DumpInfo) - return nil -} - -// DumpSQLsFromFile dumps plan replayer results for sqls from file -func (e *PlanReplayerDumpInfo) DumpSQLsFromFile(ctx context.Context, b []byte) error { - sqls := strings.Split(string(b), ";") - e.ExecStmts = make([]ast.StmtNode, 0) - for _, sql := range sqls { - s := strings.Trim(sql, "\n") - if len(s) < 1 { - continue - } - node, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ParseWithParams(ctx, s) - if err != nil { - return fmt.Errorf("parse sql error, sql:%v, err:%v", s, err) - } - e.ExecStmts = append(e.ExecStmts, node) - } - return e.dump(ctx) -} - -// PlanReplayerLoadExec represents a plan replayer load executor. -type PlanReplayerLoadExec struct { - exec.BaseExecutor - info *PlanReplayerLoadInfo -} - -// PlanReplayerLoadInfo contains file path and session context. -type PlanReplayerLoadInfo struct { - Path string - Ctx sessionctx.Context -} - -type planReplayerDumpKeyType int - -func (planReplayerDumpKeyType) String() string { - return "plan_replayer_dump_var" -} - -type planReplayerLoadKeyType int - -func (planReplayerLoadKeyType) String() string { - return "plan_replayer_load_var" -} - -// PlanReplayerLoadVarKey is a variable key for plan replayer load. -const PlanReplayerLoadVarKey planReplayerLoadKeyType = 0 - -// PlanReplayerDumpVarKey is a variable key for plan replayer dump. -const PlanReplayerDumpVarKey planReplayerDumpKeyType = 1 - -// Next implements the Executor Next interface. -func (e *PlanReplayerLoadExec) Next(_ context.Context, req *chunk.Chunk) error { - req.GrowAndReset(e.MaxChunkSize()) - if len(e.info.Path) == 0 { - return errors.New("plan replayer: file path is empty") - } - val := e.Ctx().Value(PlanReplayerLoadVarKey) - if val != nil { - e.Ctx().SetValue(PlanReplayerLoadVarKey, nil) - return errors.New("plan replayer: previous plan replayer load option isn't closed normally, please try again") - } - e.Ctx().SetValue(PlanReplayerLoadVarKey, e.info) - return nil -} - -func loadSetTiFlashReplica(ctx sessionctx.Context, z *zip.Reader) error { - for _, zipFile := range z.File { - if strings.Compare(zipFile.Name, domain.PlanReplayerTiFlashReplicasFile) == 0 { - v, err := zipFile.Open() - if err != nil { - return errors.AddStack(err) - } - //nolint: errcheck,all_revive,revive - defer v.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(v) - if err != nil { - return errors.AddStack(err) - } - rows := strings.Split(buf.String(), "\n") - for _, row := range rows { - if len(row) < 1 { - continue - } - r := strings.Split(row, "\t") - if len(r) < 3 { - logutil.BgLogger().Debug("plan replayer: skip error", - zap.Error(errors.New("setting tiflash replicas failed"))) - continue - } - dbName := r[0] - tableName := r[1] - c := context.Background() - // Though we record tiflash replica in txt, we only set 1 tiflash replica as it's enough for reproduce the plan - sql := fmt.Sprintf("alter table %s.%s set tiflash replica 1", dbName, tableName) - _, err = ctx.(sqlexec.SQLExecutor).Execute(c, sql) - logutil.BgLogger().Debug("plan replayer: skip error", zap.Error(err)) - } - } - } - return nil -} - -func loadAllBindings(ctx sessionctx.Context, z *zip.Reader) error { - for _, f := range z.File { - if strings.Compare(f.Name, domain.PlanReplayerSessionBindingFile) == 0 { - err := loadBindings(ctx, f, true) - if err != nil { - return err - } - } else if strings.Compare(f.Name, domain.PlanReplayerGlobalBindingFile) == 0 { - err := loadBindings(ctx, f, false) - if err != nil { - return err - } - } - } - return nil -} - -func loadBindings(ctx sessionctx.Context, f *zip.File, isSession bool) error { - r, err := f.Open() - if err != nil { - return errors.AddStack(err) - } - //nolint: errcheck - defer r.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(r) - if err != nil { - return errors.AddStack(err) - } - if len(buf.String()) < 1 { - return nil - } - bindings := strings.Split(buf.String(), "\n") - for _, binding := range bindings { - cols := strings.Split(binding, "\t") - if len(cols) < 3 { - continue - } - originSQL := cols[0] - bindingSQL := cols[1] - enabled := cols[3] - if strings.Compare(enabled, "enabled") == 0 { - sql := fmt.Sprintf("CREATE %s BINDING FOR %s USING %s", func() string { - if isSession { - return "SESSION" - } - return "GLOBAL" - }(), originSQL, bindingSQL) - c := context.Background() - _, err = ctx.(sqlexec.SQLExecutor).Execute(c, sql) - if err != nil { - return err - } - } - } - return nil -} - -func loadVariables(ctx sessionctx.Context, z *zip.Reader) error { - unLoadVars := make([]string, 0) - for _, zipFile := range z.File { - if strings.Compare(zipFile.Name, domain.PlanReplayerVariablesFile) == 0 { - varMap := make(map[string]string) - v, err := zipFile.Open() - if err != nil { - return errors.AddStack(err) - } - //nolint: errcheck,all_revive,revive - defer v.Close() - _, err = toml.NewDecoder(v).Decode(&varMap) - if err != nil { - return errors.AddStack(err) - } - vars := ctx.GetSessionVars() - for name, value := range varMap { - sysVar := variable.GetSysVar(name) - if sysVar == nil { - unLoadVars = append(unLoadVars, name) - logutil.BgLogger().Warn(fmt.Sprintf("skip set variable %s:%s", name, value), zap.Error(err)) - continue - } - sVal, err := sysVar.Validate(vars, value, variable.ScopeSession) - if err != nil { - unLoadVars = append(unLoadVars, name) - logutil.BgLogger().Warn(fmt.Sprintf("skip variable %s:%s", name, value), zap.Error(err)) - continue - } - err = vars.SetSystemVar(name, sVal) - if err != nil { - unLoadVars = append(unLoadVars, name) - logutil.BgLogger().Warn(fmt.Sprintf("skip set variable %s:%s", name, value), zap.Error(err)) - continue - } - } - } - } - if len(unLoadVars) > 0 { - ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("variables set failed:%s", strings.Join(unLoadVars, ","))) - } - return nil -} - -// createSchemaAndItems creates schema and tables or views -func createSchemaAndItems(ctx sessionctx.Context, f *zip.File) error { - r, err := f.Open() - if err != nil { - return errors.AddStack(err) - } - //nolint: errcheck - defer r.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(r) - if err != nil { - return errors.AddStack(err) - } - originText := buf.String() - index1 := strings.Index(originText, ";") - createDatabaseSQL := originText[:index1+1] - index2 := strings.Index(originText[index1+1:], ";") - useDatabaseSQL := originText[index1+1:][:index2+1] - createTableSQL := originText[index1+1:][index2+1:] - c := context.Background() - // create database if not exists - _, err = ctx.(sqlexec.SQLExecutor).Execute(c, createDatabaseSQL) - logutil.BgLogger().Debug("plan replayer: skip error", zap.Error(err)) - // use database - _, err = ctx.(sqlexec.SQLExecutor).Execute(c, useDatabaseSQL) - if err != nil { - return err - } - // create table or view - _, err = ctx.(sqlexec.SQLExecutor).Execute(c, createTableSQL) - if err != nil { - return err - } - return nil -} - -func loadStats(ctx sessionctx.Context, f *zip.File) error { - jsonTbl := &storage.JSONTable{} - r, err := f.Open() - if err != nil { - return errors.AddStack(err) - } - //nolint: errcheck - defer r.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(r) - if err != nil { - return errors.AddStack(err) - } - if err := json.Unmarshal(buf.Bytes(), jsonTbl); err != nil { - return errors.AddStack(err) - } - do := domain.GetDomain(ctx) - h := do.StatsHandle() - if h == nil { - return errors.New("plan replayer: hanlde is nil") - } - return h.LoadStatsFromJSON(context.Background(), ctx.GetInfoSchema().(infoschema.InfoSchema), jsonTbl, 0) -} - -// Update updates the data of the corresponding table. -func (e *PlanReplayerLoadInfo) Update(data []byte) error { - b := bytes.NewReader(data) - z, err := zip.NewReader(b, int64(len(data))) - if err != nil { - return errors.AddStack(err) - } - - // load variable - err = loadVariables(e.Ctx, z) - if err != nil { - return err - } - - // build schema and table first - for _, zipFile := range z.File { - if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) { - continue - } - path := strings.Split(zipFile.Name, "/") - if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() { - err = createSchemaAndItems(e.Ctx, zipFile) - if err != nil { - return err - } - } - } - - // set tiflash replica if exists - err = loadSetTiFlashReplica(e.Ctx, z) - if err != nil { - return err - } - - // build view next - for _, zipFile := range z.File { - path := strings.Split(zipFile.Name, "/") - if len(path) == 2 && strings.Compare(path[0], "view") == 0 && zipFile.Mode().IsRegular() { - err = createSchemaAndItems(e.Ctx, zipFile) - if err != nil { - return err - } - } - } - - // load stats - for _, zipFile := range z.File { - path := strings.Split(zipFile.Name, "/") - if len(path) == 2 && strings.Compare(path[0], "stats") == 0 && zipFile.Mode().IsRegular() { - err = loadStats(e.Ctx, zipFile) - if err != nil { - return err - } - } - } - - err = loadAllBindings(e.Ctx, z) - if err != nil { - logutil.BgLogger().Warn("load bindings failed", zap.Error(err)) - e.Ctx.GetSessionVars().StmtCtx.AppendWarning(fmt.Errorf("load bindings failed, err:%v", err)) - } - return nil -} diff --git a/executor/prepared_test.go b/executor/prepared_test.go deleted file mode 100644 index 7a9772a52d231..0000000000000 --- a/executor/prepared_test.go +++ /dev/null @@ -1,1275 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "strconv" - "strings" - "sync/atomic" - "testing" - - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestPreparedNameResolver(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int, KEY id (id))") - tk.MustExec("prepare stmt from 'select * from t limit ? offset ?'") - tk.MustGetErrMsg("prepare stmt from 'select b from t'", - "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("prepare stmt from '(select * FROM t) union all (select * FROM t) order by a limit ?'", - "[planner:1054]Unknown column 'a' in 'order clause'") -} - -// a 'create table' DDL statement should be accepted if it has no parameters. -func TestPreparedDDL(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("prepare stmt from 'create table t (id int, KEY id (id))'") -} - -// TestUnsupportedStmtForPrepare is related to https://github.com/pingcap/tidb/issues/17412 -func TestUnsupportedStmtForPrepare(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`prepare stmt0 from "create table t0(a int primary key)"`) - tk.MustGetErrCode(`prepare stmt1 from "execute stmt0"`, mysql.ErrUnsupportedPs) - tk.MustGetErrCode(`prepare stmt2 from "deallocate prepare stmt0"`, mysql.ErrUnsupportedPs) - tk.MustGetErrCode(`prepare stmt4 from "prepare stmt3 from 'create table t1(a int, b int)'"`, mysql.ErrUnsupportedPs) -} - -func TestIgnorePlanCache(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - - tk.MustExec("create table t (id int primary key, num int)") - tk.MustExec("insert into t values (1, 1)") - tk.MustExec("insert into t values (2, 2)") - tk.MustExec("insert into t values (3, 3)") - tk.MustExec("prepare stmt from 'select /*+ IGNORE_PLAN_CACHE() */ * from t where id=?'") - tk.MustExec("set @ignore_plan_doma = 1") - tk.MustExec("execute stmt using @ignore_plan_doma") - require.False(t, tk.Session().GetSessionVars().StmtCtx.UseCache) -} - -func TestPreparedStmtWithHint(t *testing.T) { - // see https://github.com/pingcap/tidb/issues/18535 - store, dom := testkit.CreateMockStoreAndDomain(t) - sv := server.CreateMockServer(t, store) - sv.SetDomain(dom) - defer sv.Close() - - conn1 := server.CreateMockConn(t, sv) - tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) - - go dom.ExpensiveQueryHandle().SetSessionManager(sv).Run() - tk.MustExec("prepare stmt from \"select /*+ max_execution_time(100) */ sleep(10)\"") - tk.MustQuery("execute stmt").Check(testkit.Rows("1")) - - // see https://github.com/pingcap/tidb/issues/46817 - tk.MustExec("use test") - tk.MustExec("create table if not exists t (i int)") - tk.MustExec("prepare stmt from 'with a as (select /*+ qb_name(qb1) */ * from t) select /*+ leading(@qb1)*/ * from a;'") -} - -func TestPreparedNullParam(t *testing.T) { - store := testkit.CreateMockStore(t) - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set tidb_enable_prepared_plan_cache=%v`, flag)) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_collect_execution_info=0") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int not null, KEY id (id))") - tk.MustExec("insert into t values (1), (2), (3)") - - tk.MustExec("prepare stmt from 'select * from t where id = ?'") - tk.MustExec("set @a= null") - tk.MustQuery("execute stmt using @a").Check(testkit.Rows()) - - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( - "TableDual_5 0.00 root rows:0")) - } -} - -func TestIssue29850(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec(`set tidb_enable_clustered_index=on`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0") - tk.MustExec(`use test`) - tk.MustExec(`CREATE TABLE customer ( - c_id int(11) NOT NULL, - c_d_id int(11) NOT NULL, - c_first varchar(16) DEFAULT NULL, - c_w_id int(11) NOT NULL, - c_last varchar(16) DEFAULT NULL, - c_credit char(2) DEFAULT NULL, - c_discount decimal(4,4) DEFAULT NULL, - PRIMARY KEY (c_w_id,c_d_id,c_id), - KEY idx_customer (c_w_id,c_d_id,c_last,c_first))`) - tk.MustExec(`CREATE TABLE warehouse ( - w_id int(11) NOT NULL, - w_tax decimal(4,4) DEFAULT NULL, - PRIMARY KEY (w_id))`) - tk.MustExec(`prepare stmt from 'SELECT c_discount, c_last, c_credit, w_tax - FROM customer, warehouse - WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?'`) - tk.MustExec(`set @w_id=1262`) - tk.MustExec(`set @c_d_id=7`) - tk.MustExec(`set @c_id=1549`) - tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows()) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use PointGet - `Projection_7 0.00 root test.customer.c_discount, test.customer.c_last, test.customer.c_credit, test.warehouse.w_tax`, - `└─HashJoin_8 0.00 root CARTESIAN inner join`, - ` ├─Point_Get_11(Build) 1.00 root table:warehouse handle:1262`, - ` └─Point_Get_10(Probe) 1.00 root table:customer, clustered index:PRIMARY(c_w_id, c_d_id, c_id) `)) - tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows()) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the cached plan - - tk.MustExec(`create table t (a int primary key)`) - tk.MustExec(`insert into t values (1), (2)`) - tk.MustExec(`prepare stmt from 'select * from t where a>=? and a<=?'`) - tk.MustExec(`set @a1=1, @a2=2`) - tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( - `Point_Get_5 1.00 root table:t handle:1`)) - tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) - - tk.MustExec(`prepare stmt from 'select * from t where a=? or a=?'`) - tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // cannot use PointGet since it contains a or condition - `Point_Get_5 1.00 root table:t handle:1`)) - tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2")) -} - -func TestIssue28064(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("drop table if exists t28064") - tk.MustExec("CREATE TABLE `t28064` (" + - "`a` decimal(10,0) DEFAULT NULL," + - "`b` decimal(10,0) DEFAULT NULL," + - "`c` decimal(10,0) DEFAULT NULL," + - "`d` decimal(10,0) DEFAULT NULL," + - "KEY `iabc` (`a`,`b`,`c`));") - tk.MustExec("set @a='123', @b='234', @c='345';") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("prepare stmt1 from 'select * from t28064 use index (iabc) where a = ? and b = ? and c = ?';") - - tk.MustExec("execute stmt1 using @a, @b, @c;") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - rows.Check(testkit.Rows( - "IndexLookUp_7 0.00 root ", - "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", - "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) - - tk.MustExec("execute stmt1 using @a, @b, @c;") - rows = tk.MustQuery("select @@last_plan_from_cache") - rows.Check(testkit.Rows("1")) - - tk.MustExec("execute stmt1 using @a, @b, @c;") - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - rows.Check(testkit.Rows( - "IndexLookUp_7 0.00 root ", - "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", - "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) -} - -func TestPreparePlanCache4Blacklist(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - - // test the blacklist of optimization rules - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("prepare stmt from 'select min(a) from t;';") - tk.MustExec("execute stmt;") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - require.Contains(t, res.Rows()[1][0], "TopN") - - res = tk.MustQuery("explain format = 'brief' select min(a) from t") - require.Contains(t, res.Rows()[1][0], "TopN") - - tk.MustExec("INSERT INTO mysql.opt_rule_blacklist VALUES('max_min_eliminate');") - tk.MustExec("ADMIN reload opt_rule_blacklist;") - - tk.MustExec("execute stmt;") - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) - tk.MustExec("execute stmt;") - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - // Plans that have been cached will not be affected by the blacklist. - require.Contains(t, res.Rows()[1][0], "TopN") - - res = tk.MustQuery("explain format = 'brief' select min(a) from t") - require.Contains(t, res.Rows()[0][0], "HashAgg") - - // test the blacklist of Expression Pushdown - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("prepare stmt from 'SELECT * FROM t WHERE a < 2 and a > 2;';") - tk.MustExec("execute stmt;") - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - require.Equal(t, 3, len(res.Rows())) - require.Contains(t, res.Rows()[1][0], "Selection") - require.Equal(t, "gt(test.t.a, 2), lt(test.t.a, 2)", res.Rows()[1][4]) - - res = tk.MustQuery("explain format = 'brief' SELECT * FROM t WHERE a < 2 and a > 2;") - require.Equal(t, 3, len(res.Rows())) - require.Equal(t, "gt(test.t.a, 2), lt(test.t.a, 2)", res.Rows()[1][4]) - - tk.MustExec("INSERT INTO mysql.expr_pushdown_blacklist VALUES('<','tikv','');") - tk.MustExec("ADMIN reload expr_pushdown_blacklist;") - - tk.MustExec("execute stmt;") - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - tk.MustExec("execute stmt;") - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - // The expressions can not be pushed down to tikv. - require.Equal(t, 4, len(res.Rows())) - - res = tk.MustQuery("explain format = 'brief' SELECT * FROM t WHERE a < 2 and a > 2;") - require.Equal(t, 4, len(res.Rows())) - require.Contains(t, res.Rows()[0][0], "Selection") - require.Equal(t, "lt(test.t.a, 2)", res.Rows()[0][4]) - require.Contains(t, res.Rows()[2][0], "Selection") - require.Equal(t, "gt(test.t.a, 2)", res.Rows()[2][4]) - - tk.MustExec("DELETE FROM mysql.expr_pushdown_blacklist;") - tk.MustExec("ADMIN reload expr_pushdown_blacklist;") -} - -func TestPlanCacheClusterIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("create table t1(a varchar(20), b varchar(20), c varchar(20), primary key(a, b))") - tk.MustExec("insert into t1 values('1','1','111'),('2','2','222'),('3','3','333')") - - // For table scan - tk.MustExec(`prepare stmt1 from "select * from t1 where t1.a = ? and t1.b > ?"`) - tk.MustExec("set @v1 = '1'") - tk.MustExec("set @v2 = '0'") - tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("1 1 111")) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) - tk.MustExec("set @v1 = '2'") - tk.MustExec("set @v2 = '1'") - tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("2 2 222")) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustExec("set @v1 = '3'") - tk.MustExec("set @v2 = '2'") - tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("3 3 333")) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - require.Equal(t, 0, strings.Index(rows[len(rows)-1][4].(string), `range:("3" "2","3" +inf]`)) - // For point get - tk.MustExec(`prepare stmt2 from "select * from t1 where t1.a = ? and t1.b = ?"`) - tk.MustExec("set @v1 = '1'") - tk.MustExec("set @v2 = '1'") - tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("1 1 111")) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) - tk.MustExec("set @v1 = '2'") - tk.MustExec("set @v2 = '2'") - tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("2 2 222")) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustExec("set @v1 = '3'") - tk.MustExec("set @v2 = '3'") - tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("3 3 333")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - require.Equal(t, 0, strings.Index(rows[len(rows)-1][0].(string), `Point_Get`)) - // For CBO point get and batch point get - // case 1: - tk.MustExec(`drop table if exists ta, tb`) - tk.MustExec(`create table ta (a varchar(8) primary key, b int)`) - tk.MustExec(`insert ta values ('a', 1), ('b', 2)`) - tk.MustExec(`create table tb (a varchar(8) primary key, b int)`) - tk.MustExec(`insert tb values ('a', 1), ('b', 2)`) - tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.a = tb.a and ta.a = ?"`) - tk.MustExec(`set @v1 = 'a', @v2 = 'b'`) - tk.MustQuery(`execute stmt1 using @v1`).Check(testkit.Rows("a 1 a 1")) - tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 b 2")) - - // case 2: - tk.MustExec(`drop table if exists ta, tb`) - tk.MustExec(`create table ta (a varchar(10) primary key, b int not null)`) - tk.MustExec(`insert ta values ('a', 1), ('b', 2)`) - tk.MustExec(`create table tb (b int primary key, c int)`) - tk.MustExec(`insert tb values (1, 1), (2, 2)`) - tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.b = tb.b and ta.a = ?"`) - tk.MustExec(`set @v1 = 'a', @v2 = 'b'`) - tk.MustQuery(`execute stmt1 using @v1`).Check(testkit.Rows("a 1 1 1")) - tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 2 2")) - tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 2 2")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - require.True(t, strings.Contains(rows[3][0].(string), `TableRangeScan`)) - - // case 3: - tk.MustExec(`drop table if exists ta, tb`) - tk.MustExec(`create table ta (a varchar(10), b varchar(10), c int, primary key (a, b))`) - tk.MustExec(`insert ta values ('a', 'a', 1), ('b', 'b', 2), ('c', 'c', 3)`) - tk.MustExec(`create table tb (b int primary key, c int)`) - tk.MustExec(`insert tb values (1, 1), (2, 2), (3,3)`) - tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.c = tb.b and ta.a = ? and ta.b = ?"`) - tk.MustExec(`set @v1 = 'a', @v2 = 'b', @v3 = 'c'`) - tk.MustQuery(`execute stmt1 using @v1, @v1`).Check(testkit.Rows("a a 1 1 1")) - tk.MustQuery(`execute stmt1 using @v2, @v2`).Check(testkit.Rows("b b 2 2 2")) - tk.MustExec(`prepare stmt2 from "select * from ta, tb where ta.c = tb.b and (ta.a, ta.b) in ((?, ?), (?, ?))"`) - tk.MustQuery(`execute stmt2 using @v1, @v1, @v2, @v2`).Check(testkit.Rows("a a 1 1 1", "b b 2 2 2")) - tk.MustQuery(`execute stmt2 using @v2, @v2, @v3, @v3`).Check(testkit.Rows("b b 2 2 2", "c c 3 3 3")) - - // For issue 19002 - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec(`drop table if exists t1`) - tk.MustExec(`create table t1(a int, b int, c int, primary key(a, b))`) - tk.MustExec(`insert into t1 values(1,1,111),(2,2,222),(3,3,333)`) - // Point Get: - tk.MustExec(`prepare stmt1 from "select * from t1 where t1.a = ? and t1.b = ?"`) - tk.MustExec(`set @v1=1, @v2=1`) - tk.MustQuery(`execute stmt1 using @v1,@v2`).Check(testkit.Rows("1 1 111")) - tk.MustExec(`set @v1=2, @v2=2`) - tk.MustQuery(`execute stmt1 using @v1,@v2`).Check(testkit.Rows("2 2 222")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) - // Batch Point Get: - tk.MustExec(`prepare stmt2 from "select * from t1 where (t1.a,t1.b) in ((?,?),(?,?))"`) - tk.MustExec(`set @v1=1, @v2=1, @v3=2, @v4=2`) - tk.MustQuery(`execute stmt2 using @v1,@v2,@v3,@v4`).Check(testkit.Rows("1 1 111", "2 2 222")) - tk.MustExec(`set @v1=2, @v2=2, @v3=3, @v4=3`) - tk.MustQuery(`execute stmt2 using @v1,@v2,@v3,@v4`).Check(testkit.Rows("2 2 222", "3 3 333")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) -} - -func TestPlanCacheWithDifferentVariableTypes(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("create table t1(a varchar(20), b int, c float, key(b, a))") - tk.MustExec("insert into t1 values('1',1,1.1),('2',2,222),('3',3,333)") - tk.MustExec("create table t2(a varchar(20), b int, c float, key(b, a))") - tk.MustExec("insert into t2 values('3',3,3.3),('2',2,222),('3',3,333)") - - var input []struct { - PrepareStmt string - Executes []struct { - Vars []struct { - Name string - Value string - } - ExecuteSQL string - } - } - var output []struct { - PrepareStmt string - Executes []struct { - SQL string - Vars []struct { - Name string - Value string - } - Plan []string - LastPlanUseCache string - Result []string - } - } - prepareMergeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - tk.MustExec(tt.PrepareStmt) - testdata.OnRecord(func() { - output[i].PrepareStmt = tt.PrepareStmt - output[i].Executes = make([]struct { - SQL string - Vars []struct { - Name string - Value string - } - Plan []string - LastPlanUseCache string - Result []string - }, len(tt.Executes)) - }) - require.Equal(t, tt.PrepareStmt, output[i].PrepareStmt) - for j, exec := range tt.Executes { - for _, v := range exec.Vars { - tk.MustExec(fmt.Sprintf(`set @%s = %s`, v.Name, v.Value)) - } - res := tk.MustQuery(exec.ExecuteSQL) - lastPlanUseCache := tk.MustQuery("select @@last_plan_from_cache").Rows()[0][0] - tk.MustQuery(exec.ExecuteSQL) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - plan := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - testdata.OnRecord(func() { - output[i].Executes[j].SQL = exec.ExecuteSQL - output[i].Executes[j].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - output[i].Executes[j].Vars = exec.Vars - output[i].Executes[j].LastPlanUseCache = lastPlanUseCache.(string) - output[i].Executes[j].Result = testdata.ConvertRowsToStrings(res.Rows()) - }) - - require.Equal(t, exec.ExecuteSQL, output[i].Executes[j].SQL) - plan.Check(testkit.Rows(output[i].Executes[j].Plan...)) - require.Equal(t, exec.Vars, output[i].Executes[j].Vars) - require.Equal(t, lastPlanUseCache.(string), output[i].Executes[j].LastPlanUseCache) - res.Check(testkit.Rows(output[i].Executes[j].Result...)) - } - } -} - -func TestPlanCacheOperators(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - type ExecCase struct { - Parameters []string - UseCache bool - } - type PrepCase struct { - PrepStmt string - ExecCases []ExecCase - } - - cases := []PrepCase{ - {"use test", nil}, - - // cases for TableReader on PK - {"create table t (a int, b int, primary key(a))", nil}, - {"insert into t values (1,1), (2,2), (3,3), (4,4), (5,5), (6,null)", nil}, - {"select a from t where a=?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, true}, - {[]string{"3"}, true}, - }}, - {"select a from t where a in (?,?,?)", []ExecCase{ - {[]string{"1", "1", "1"}, false}, - {[]string{"2", "3", "4"}, true}, - {[]string{"3", "5", "7"}, true}, - }}, - {"select a from t where a>? and a? and a? and a? and a? and a?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ HASH_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ HASH_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ MERGE_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ MERGE_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ INL_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, true}, - {[]string{"5"}, true}, - }}, - {"select /*+ INL_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b? and t1.a > (select min(t2.a) from t t2 where t2.b < t1.b)", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, false}, // plans with sub-queries cannot be cached, but the result must be correct - {[]string{"5"}, false}, - }}, - {"select * from t t1 where t1.a > (select min(t2.a) from t t2 where t2.b < t1.b+?)", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"3"}, false}, - {[]string{"5"}, false}, - }}, - {"select * from t t1 where t1.b>? and t1.a > (select min(t2.a) from t t2 where t2.b < t1.b+?)", []ExecCase{ - {[]string{"1", "1"}, false}, - {[]string{"3", "2"}, false}, - {[]string{"5", "3"}, false}, - }}, - {"drop table t", nil}, - - // cases for Window - {"create table t (name varchar(50), y int, sale decimal(14,2))", nil}, - {"insert into t values ('Bob',2016,2.4), ('Bob',2017,3.2), ('Bob',2018,2.1), ('Alice',2016,1.4), ('Alice',2017,2), ('Alice',2018,3.3), ('John',2016,4), ('John',2017,2.1), ('John',2018,5)", nil}, - {"select *, sum(sale) over (partition by y order by sale) total from t where sale>? order by y", []ExecCase{ - {[]string{"0.1"}, false}, - {[]string{"0.5"}, true}, - {[]string{"1.5"}, true}, - {[]string{"3.5"}, true}, - }}, - {"select *, sum(sale) over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ - {[]string{"0.1"}, false}, - {[]string{"0.5"}, true}, - {[]string{"1.5"}, true}, - {[]string{"3.5"}, true}, - }}, - {"select *, rank() over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ - {[]string{"0.1"}, false}, - {[]string{"0.5"}, true}, - {[]string{"1.5"}, true}, - {[]string{"3.5"}, true}, - }}, - {"select *, first_value(sale) over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ - {[]string{"0.1"}, false}, - {[]string{"0.5"}, true}, - {[]string{"1.5"}, true}, - {[]string{"3.5"}, true}, - }}, - {"select *, first_value(sale) over (partition by y order by sale rows ? preceding) total from t order by y", []ExecCase{ - {[]string{"1"}, false}, // window plans with parameters in frame cannot be cached - {[]string{"2"}, false}, - {[]string{"3"}, false}, - {[]string{"4"}, false}, - }}, - {"drop table t", nil}, - - // cases for Limit - {"create table t (a int)", nil}, - {"insert into t values (1), (1), (2), (2), (3), (4), (5), (6), (7), (8), (9), (0), (0)", nil}, - {"select * from t limit ?", []ExecCase{ - {[]string{"20"}, false}, - {[]string{"30"}, false}, - }}, - {"select * from t limit 40, ?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, false}, - }}, - {"select * from t limit ?, 10", []ExecCase{ - {[]string{"20"}, false}, - {[]string{"30"}, false}, - }}, - {"select * from t limit ?, ?", []ExecCase{ - {[]string{"20", "20"}, false}, - {[]string{"20", "40"}, false}, - }}, - {"select * from t where a? order by mod(a, 3)", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, true}, - {[]string{"3"}, true}, - }}, - - // cases for topN - {"select * from t order by b limit ?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, false}, - }}, - {"select * from t order by b limit 10, ?", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, false}, - }}, - {"select * from t order by ? limit 10", []ExecCase{ - {[]string{"1"}, false}, - {[]string{"2"}, false}, - }}, - {"select * from t order by ? limit ?", []ExecCase{ - {[]string{"1", "10"}, false}, - {[]string{"2", "20"}, false}, - }}, - } - - for _, prepCase := range cases { - isQuery := strings.Contains(prepCase.PrepStmt, "select") - if !isQuery { - tk.MustExec(prepCase.PrepStmt) - continue - } - - tk.MustExec(fmt.Sprintf(`prepare stmt from '%v'`, prepCase.PrepStmt)) - for _, execCase := range prepCase.ExecCases { - // set all parameters - usingStmt := "" - if len(execCase.Parameters) > 0 { - setStmt := "set " - usingStmt = "using " - for i, parameter := range execCase.Parameters { - if i > 0 { - setStmt += ", " - usingStmt += ", " - } - setStmt += fmt.Sprintf("@x%v=%v", i, parameter) - usingStmt += fmt.Sprintf("@x%v", i) - } - tk.MustExec(setStmt) - } - - // execute this statement and check whether it uses a cached plan - results := tk.MustQuery("execute stmt " + usingStmt).Sort().Rows() - - // check whether the result is correct - tmp := strings.Split(prepCase.PrepStmt, "?") - require.Equal(t, len(execCase.Parameters)+1, len(tmp)) - query := "" - for i := range tmp { - query += tmp[i] - if i < len(execCase.Parameters) { - query += execCase.Parameters[i] - } - } - tk.MustQuery(query).Sort().Check(results) - } - } -} - -func TestIssue28782(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("prepare stmt from 'SELECT IF(?, 1, 0);';") - tk.MustExec("set @a=1, @b=null, @c=0") - - tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1")) - tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("0")) - // TODO(Reminiscent): Support cache more tableDual plan. - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - tk.MustQuery("execute stmt using @c;").Check(testkit.Rows("0")) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) -} - -func TestIssue29101(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) - tk.MustExec(`use test`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec(`CREATE TABLE customer ( - c_id int(11) NOT NULL, - c_d_id int(11) NOT NULL, - c_w_id int(11) NOT NULL, - c_first varchar(16) DEFAULT NULL, - c_last varchar(16) DEFAULT NULL, - c_credit char(2) DEFAULT NULL, - c_discount decimal(4,4) DEFAULT NULL, - PRIMARY KEY (c_w_id,c_d_id,c_id) NONCLUSTERED, - KEY idx_customer (c_w_id,c_d_id,c_last,c_first) - )`) - tk.MustExec(`CREATE TABLE warehouse ( - w_id int(11) NOT NULL, - w_tax decimal(4,4) DEFAULT NULL, - PRIMARY KEY (w_id) - )`) - tk.MustExec(`prepare s1 from 'SELECT /*+ TIDB_INLJ(customer,warehouse) */ c_discount, c_last, c_credit, w_tax FROM customer, warehouse WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?'`) - tk.MustExec(`set @a=936,@b=7,@c=158`) - tk.MustQuery(`execute s1 using @a,@b,@c`).Check(testkit.Rows()) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use PK - `Projection_6 1.00 root test.customer.c_discount, test.customer.c_last, test.customer.c_credit, test.warehouse.w_tax`, - `└─HashJoin_7 1.00 root CARTESIAN inner join`, - ` ├─Point_Get_10(Build) 1.00 root table:warehouse handle:936`, - ` └─Point_Get_9(Probe) 1.00 root table:customer, index:PRIMARY(c_w_id, c_d_id, c_id) `)) - tk.MustQuery(`execute s1 using @a,@b,@c`).Check(testkit.Rows()) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the plan-cache - - tk.MustExec(`CREATE TABLE order_line ( - ol_o_id int(11) NOT NULL, - ol_d_id int(11) NOT NULL, - ol_w_id int(11) NOT NULL, - ol_number int(11) NOT NULL, - ol_i_id int(11) NOT NULL, - PRIMARY KEY (ol_w_id,ol_d_id,ol_o_id,ol_number) NONCLUSTERED)`) - tk.MustExec(`CREATE TABLE stock ( - s_i_id int(11) NOT NULL, - s_w_id int(11) NOT NULL, - s_quantity int(11) DEFAULT NULL, - PRIMARY KEY (s_w_id,s_i_id) NONCLUSTERED)`) - tk.MustExec(`prepare s1 from 'SELECT /*+ TIDB_INLJ(order_line,stock) */ COUNT(DISTINCT (s_i_id)) stock_count FROM order_line, stock WHERE ol_w_id = ? AND ol_d_id = ? AND ol_o_id < ? AND ol_o_id >= ? - 20 AND s_w_id = ? AND s_i_id = ol_i_id AND s_quantity < ?'`) - tk.MustExec(`set @a=391,@b=1,@c=3058,@d=18`) - tk.MustExec(`execute s1 using @a,@b,@c,@c,@a,@d`) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use index-join - `StreamAgg_9 1.00 root funcs:count(distinct test.stock.s_i_id)->Column#11`, - `└─IndexJoin_14 0.03 root inner join, inner:IndexLookUp_13, outer key:test.order_line.ol_i_id, inner key:test.stock.s_i_id, equal cond:eq(test.order_line.ol_i_id, test.stock.s_i_id)`, - ` ├─IndexLookUp_28(Build) 0.03 root `, - ` │ ├─IndexRangeScan_26(Build) 0.03 cop[tikv] table:order_line, index:PRIMARY(ol_w_id, ol_d_id, ol_o_id, ol_number) range:[391 1 3038,391 1 3058), keep order:false, stats:pseudo`, - ` │ └─TableRowIDScan_27(Probe) 0.03 cop[tikv] table:order_line keep order:false, stats:pseudo`, - ` └─IndexLookUp_13(Probe) 0.03 root `, - ` ├─IndexRangeScan_10(Build) 0.03 cop[tikv] table:stock, index:PRIMARY(s_w_id, s_i_id) range: decided by [eq(test.stock.s_i_id, test.order_line.ol_i_id) eq(test.stock.s_w_id, 391)], keep order:false, stats:pseudo`, - ` └─Selection_12(Probe) 0.03 cop[tikv] lt(test.stock.s_quantity, 18)`, - ` └─TableRowIDScan_11 0.03 cop[tikv] table:stock keep order:false, stats:pseudo`)) - tk.MustExec(`execute s1 using @a,@b,@c,@c,@a,@d`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the plan-cache -} - -func TestIssue28087And28162(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - // issue 28087 - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists IDT_26207`) - tk.MustExec(`CREATE TABLE IDT_26207 (col1 bit(1))`) - tk.MustExec(`insert into IDT_26207 values(0x0), (0x1)`) - tk.MustExec(`prepare stmt from 'select t1.col1 from IDT_26207 as t1 left join IDT_26207 as t2 on t1.col1 = t2.col1 where t1.col1 in (?, ?, ?)'`) - tk.MustExec(`set @a=0x01, @b=0x01, @c=0x01`) - tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x01")) - tk.MustExec(`set @a=0x00, @b=0x00, @c=0x01`) - tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x00", "\x01")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) - - // issue 28162 - tk.MustExec(`drop table if exists IDT_MC21780`) - tk.MustExec(`CREATE TABLE IDT_MC21780 ( - COL1 timestamp NULL DEFAULT NULL, - COL2 timestamp NULL DEFAULT NULL, - COL3 timestamp NULL DEFAULT NULL, - KEY U_M_COL (COL1,COL2) - )`) - tk.MustExec(`insert into IDT_MC21780 values("1970-12-18 10:53:28", "1970-12-18 10:53:28", "1970-12-18 10:53:28")`) - tk.MustExec(`prepare stmt from 'select/*+ hash_join(t1) */ * from IDT_MC21780 t1 join IDT_MC21780 t2 on t1.col1 = t2.col1 where t1. col1 < ? and t2. col1 in (?, ?, ?);'`) - tk.MustExec(`set @a="2038-01-19 03:14:07", @b="2038-01-19 03:14:07", @c="2038-01-19 03:14:07", @d="2038-01-19 03:14:07"`) - tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows()) - tk.MustExec(`set @a="1976-09-09 20:21:11", @b="2021-07-14 09:28:16", @c="1982-01-09 03:36:39", @d="1970-12-18 10:53:28"`) - tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows("1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) -} - -func TestParameterPushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t (a int, b int, c int, key(a))`) - tk.MustExec(`insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6)`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec(`set @x1=1,@x5=5,@x10=10,@x20=20`) - - var input []struct { - SQL string - } - var output []struct { - Result []string - Plan []string - FromCache string - } - prepareMergeSuiteData.LoadTestCases(t, &input, &output) - - for i, tt := range input { - if strings.HasPrefix(tt.SQL, "execute") { - res := tk.MustQuery(tt.SQL).Sort() - fromCache := tk.MustQuery("select @@last_plan_from_cache") - tk.MustQuery(tt.SQL) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - plan := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) - - testdata.OnRecord(func() { - output[i].Result = testdata.ConvertRowsToStrings(res.Rows()) - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - output[i].FromCache = fromCache.Rows()[0][0].(string) - }) - - res.Check(testkit.Rows(output[i].Result...)) - plan.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, fromCache.Rows()[0][0].(string), output[i].FromCache) - } else { - tk.MustExec(tt.SQL) - testdata.OnRecord(func() { - output[i].Result = nil - }) - } - } -} - -func TestPreparePlanCache4Function(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - - // Testing for non-deterministic functions - tk.MustExec("prepare stmt from 'select rand()';") - res := tk.MustQuery("execute stmt;") - require.Equal(t, 1, len(res.Rows())) - - res1 := tk.MustQuery("execute stmt;") - require.Equal(t, 1, len(res1.Rows())) - require.NotEqual(t, res.Rows()[0][0], res1.Rows()[0][0]) - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - - // Testing for control functions - tk.MustExec("prepare stmt from 'SELECT IFNULL(?,0);';") - tk.MustExec("set @a = 1, @b = null;") - tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1")) - tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("0")) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("prepare stmt from 'select a, case when a = ? then 0 when a <=> ? then 1 else 2 end b from t order by a;';") - tk.MustExec("insert into t values(0), (1), (2), (null);") - tk.MustExec("set @a = 0, @b = 1, @c = 2, @d = null;") - tk.MustQuery("execute stmt using @a, @b;").Check(testkit.Rows(" 2", "0 0", "1 1", "2 2")) - tk.MustQuery("execute stmt using @c, @d;").Check(testkit.Rows(" 1", "0 2", "1 2", "2 0")) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) -} - -func TestPreparePlanCache4DifferentSystemVars(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - - // Testing for 'sql_select_limit' - tk.MustExec("set @@sql_select_limit = 1") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("insert into t values(0), (1), (null);") - tk.MustExec("prepare stmt from 'select a from t order by a;';") - tk.MustQuery("execute stmt;").Check(testkit.Rows("")) - - tk.MustExec("set @@sql_select_limit = 2") - tk.MustQuery("execute stmt;").Check(testkit.Rows("", "0")) - // The 'sql_select_limit' will be stored in the cache key. So if the `sql_select_limit` - // have been changed, the plan cache can not be reused. - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - - tk.MustExec("set @@sql_select_limit = 18446744073709551615") - tk.MustQuery("execute stmt;").Check(testkit.Rows("", "0", "1")) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - - // test for 'tidb_enable_index_merge' - tk.MustExec("set @@tidb_enable_index_merge = 1;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b int, index idx_a(a), index idx_b(b));") - tk.MustExec("prepare stmt from 'select * from t use index(idx_a, idx_b) where a > 1 or b > 1;';") - tk.MustExec("execute stmt;") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res := tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Equal(t, 4, len(res.Rows())) - require.Contains(t, res.Rows()[0][0], "IndexMerge") - - tk.MustExec("set @@tidb_enable_index_merge = 0;") - tk.MustExec("execute stmt;") - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Equal(t, 4, len(res.Rows())) - require.Contains(t, res.Rows()[0][0], "IndexMerge") - tk.MustExec("execute stmt;") - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) - - // test for 'tidb_enable_parallel_apply' - tk.MustExec("set @@tidb_enable_collect_execution_info=1;") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (null, null)") - - tk.MustExec("set tidb_enable_parallel_apply=true") - tk.MustExec("prepare stmt from 'select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a);';") - tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Contains(t, res.Rows()[1][0], "Apply") - require.Contains(t, res.Rows()[1][5], "Concurrency") - - tk.MustExec("set tidb_enable_parallel_apply=false") - tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Contains(t, res.Rows()[1][0], "Apply") - executionInfo := fmt.Sprintf("%v", res.Rows()[1][4]) - // Do not use the parallel apply. - require.False(t, strings.Contains(executionInfo, "Concurrency")) - tk.MustExec("execute stmt;") - // The subquery plan with PhysicalApply can't be cached. - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - tk.MustExec("execute stmt;") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: PhysicalApply plan is un-cacheable")) - - // test for apply cache - tk.MustExec("set @@tidb_enable_collect_execution_info=1;") - tk.MustExec("set tidb_mem_quota_apply_cache=33554432") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (null, null)") - - tk.MustExec("prepare stmt from 'select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a);';") - tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Contains(t, res.Rows()[1][0], "Apply") - require.Contains(t, res.Rows()[1][5], "cache:ON") - - tk.MustExec("set tidb_mem_quota_apply_cache=0") - tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) - require.Contains(t, res.Rows()[1][0], "Apply") - executionInfo = fmt.Sprintf("%v", res.Rows()[1][5]) - // Do not use the apply cache. - require.True(t, strings.Contains(executionInfo, "cache:OFF")) - tk.MustExec("execute stmt;") - // The subquery plan can not be cached. - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) -} - -func TestTemporaryTable4PlanCache(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("drop table if exists tmp2") - tk.MustExec("create temporary table tmp2 (a int, b int, key(a), key(b));") - tk.MustExec("prepare stmt from 'select * from tmp2;';") - tk.MustQuery("execute stmt;").Check(testkit.Rows()) - tk.MustQuery("execute stmt;").Check(testkit.Rows()) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - - tk.MustExec("drop table if exists tmp_t;") - tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") - tk.MustExec("prepare stmt from 'select * from tmp_t;';") - tk.MustQuery("execute stmt;").Check(testkit.Rows()) - tk.MustQuery("execute stmt;").Check(testkit.Rows()) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) -} - -func TestPrepareStmtAfterIsolationReadChange(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=0`) - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // create virtual tiflash replica. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@session.tidb_isolation_read_engines='tikv'") - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustExec("prepare stmt from \"select * from t\"") - tk.MustQuery("execute stmt") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - require.Equal(t, "cop[tikv]", rows[len(rows)-1][2]) - - tk.MustExec("set @@session.tidb_isolation_read_engines='tiflash'") - // allowing mpp will generate mpp[tiflash] plan, the test framework will time out due to - // "retry for TiFlash peer with region missing", so disable mpp mode to use cop mode instead. - tk.MustExec("set @@session.tidb_allow_mpp=0") - tk.MustExec("execute stmt") - tkProcess = tk.Session().ShowProcess() - ps = []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - require.Equal(t, rows[len(rows)-1][2], "cop[tiflash]") - - require.Equal(t, 1, len(tk.Session().GetSessionVars().PreparedStmts)) - require.Equal(t, "select * from `t`", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedSQL) - require.Equal(t, "", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedPlan) -} - -func TestPreparePC4Binding(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - - tk.MustExec("prepare stmt from \"select * from t\"") - require.Equal(t, 1, len(tk.Session().GetSessionVars().PreparedStmts)) - require.Equal(t, "select * from `test` . `t`", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedSQL4PC) - - tk.MustQuery("execute stmt") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustQuery("execute stmt") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustExec("create binding for select * from t using select * from t") - res := tk.MustQuery("show session bindings") - require.Equal(t, 1, len(res.Rows())) - - tk.MustQuery("execute stmt") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) - tk.MustQuery("execute stmt") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustQuery("execute stmt") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) -} - -func TestIssue31141(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") - - // No panic here. - tk.MustExec("prepare stmt1 from 'do 1'") - - tk.MustExec("set @@tidb_txn_mode = 'optimistic'") - tk.MustExec("prepare stmt1 from 'do 1'") -} - -func TestMaxPreparedStmtCount(t *testing.T) { - oldVal := atomic.LoadInt64(&variable.PreparedStmtCount) - atomic.StoreInt64(&variable.PreparedStmtCount, 0) - defer func() { - atomic.StoreInt64(&variable.PreparedStmtCount, oldVal) - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.max_prepared_stmt_count = 2") - tk.MustExec("prepare stmt1 from 'select ? as num from dual'") - tk.MustExec("prepare stmt2 from 'select ? as num from dual'") - err := tk.ExecToErr("prepare stmt3 from 'select ? as num from dual'") - require.True(t, terror.ErrorEqual(err, variable.ErrMaxPreparedStmtCountReached)) -} diff --git a/executor/sample.go b/executor/sample.go deleted file mode 100644 index 54c81208e0994..0000000000000 --- a/executor/sample.go +++ /dev/null @@ -1,400 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "slices" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/tracing" - "github.com/tikv/client-go/v2/tikv" -) - -var _ exec.Executor = &TableSampleExecutor{} - -// TableSampleExecutor fetches a few rows through kv.Scan -// according to the specific sample method. -type TableSampleExecutor struct { - exec.BaseExecutor - - table table.Table - startTS uint64 - - sampler rowSampler -} - -// Open initializes necessary variables for using this executor. -func (*TableSampleExecutor) Open(ctx context.Context) error { - defer tracing.StartRegion(ctx, "TableSampleExecutor.Open").End() - return nil -} - -// Next fills data into the chunk passed by its caller. -// The task was actually done by sampler. -func (e *TableSampleExecutor) Next(_ context.Context, req *chunk.Chunk) error { - req.Reset() - if e.sampler.finished() { - return nil - } - // TODO(tangenta): add runtime stat & memory tracing - return e.sampler.writeChunk(req) -} - -// Close implements the Executor Close interface. -func (*TableSampleExecutor) Close() error { - return nil -} - -type rowSampler interface { - writeChunk(req *chunk.Chunk) error - finished() bool -} - -type tableRegionSampler struct { - ctx sessionctx.Context - table table.Table - startTS uint64 - partTables []table.PartitionedTable - schema *expression.Schema - fullSchema *expression.Schema - isDesc bool - retTypes []*types.FieldType - - rowMap map[int64]types.Datum - restKVRanges []kv.KeyRange - isFinished bool -} - -func newTableRegionSampler(ctx sessionctx.Context, t table.Table, startTs uint64, partTables []table.PartitionedTable, - schema *expression.Schema, fullSchema *expression.Schema, retTypes []*types.FieldType, desc bool) *tableRegionSampler { - return &tableRegionSampler{ - ctx: ctx, - table: t, - startTS: startTs, - partTables: partTables, - schema: schema, - fullSchema: fullSchema, - isDesc: desc, - retTypes: retTypes, - rowMap: make(map[int64]types.Datum), - } -} - -func (s *tableRegionSampler) writeChunk(req *chunk.Chunk) error { - err := s.initRanges() - if err != nil { - return err - } - expectedRowCount := req.RequiredRows() - for expectedRowCount > 0 && len(s.restKVRanges) > 0 { - ranges, err := s.pickRanges(expectedRowCount) - if err != nil { - return err - } - err = s.writeChunkFromRanges(ranges, req) - if err != nil { - return err - } - expectedRowCount = req.RequiredRows() - req.NumRows() - } - if len(s.restKVRanges) == 0 { - s.isFinished = true - } - return nil -} - -func (s *tableRegionSampler) initRanges() error { - if s.restKVRanges == nil { - var err error - s.restKVRanges, err = s.splitTableRanges() - if err != nil { - return err - } - sortRanges(s.restKVRanges, s.isDesc) - } - return nil -} - -func (s *tableRegionSampler) pickRanges(count int) ([]kv.KeyRange, error) { - var regionKeyRanges []kv.KeyRange - cutPoint := count - if len(s.restKVRanges) < cutPoint { - cutPoint = len(s.restKVRanges) - } - regionKeyRanges, s.restKVRanges = s.restKVRanges[:cutPoint], s.restKVRanges[cutPoint:] - return regionKeyRanges, nil -} - -func (s *tableRegionSampler) writeChunkFromRanges(ranges []kv.KeyRange, req *chunk.Chunk) error { - decLoc := s.ctx.GetSessionVars().Location() - cols, decColMap, err := s.buildSampleColAndDecodeColMap() - if err != nil { - return err - } - rowDecoder := decoder.NewRowDecoder(s.table, cols, decColMap) - err = s.scanFirstKVForEachRange(ranges, func(handle kv.Handle, value []byte) error { - _, err := rowDecoder.DecodeAndEvalRowWithMap(s.ctx, handle, value, decLoc, s.rowMap) - if err != nil { - return err - } - currentRow := rowDecoder.CurrentRowWithDefaultVal() - mutRow := chunk.MutRowFromTypes(s.retTypes) - for i, col := range s.schema.Columns { - offset := decColMap[col.ID].Col.Offset - target := currentRow.GetDatum(offset, s.retTypes[i]) - mutRow.SetDatum(i, target) - } - req.AppendRow(mutRow.ToRow()) - s.resetRowMap() - return nil - }) - return err -} - -func (s *tableRegionSampler) splitTableRanges() ([]kv.KeyRange, error) { - if len(s.partTables) != 0 { - var ranges []kv.KeyRange - for _, t := range s.partTables { - for _, pid := range t.GetAllPartitionIDs() { - start := tablecodec.GenTableRecordPrefix(pid) - end := start.PrefixNext() - rs, err := splitIntoMultiRanges(s.ctx.GetStore(), start, end) - if err != nil { - return nil, err - } - ranges = append(ranges, rs...) - } - } - return ranges, nil - } - startKey, endKey := s.table.RecordPrefix(), s.table.RecordPrefix().PrefixNext() - return splitIntoMultiRanges(s.ctx.GetStore(), startKey, endKey) -} - -func splitIntoMultiRanges(store kv.Storage, startKey, endKey kv.Key) ([]kv.KeyRange, error) { - kvRange := kv.KeyRange{StartKey: startKey, EndKey: endKey} - - s, ok := store.(tikv.Storage) - if !ok { - return []kv.KeyRange{kvRange}, nil - } - - maxSleep := 10000 // ms - bo := tikv.NewBackofferWithVars(context.Background(), maxSleep, nil) - regions, err := s.GetRegionCache().LoadRegionsInKeyRange(bo, startKey, endKey) - if err != nil { - return nil, errors.Trace(err) - } - var ranges = make([]kv.KeyRange, 0, len(regions)) - for _, r := range regions { - start, end := r.StartKey(), r.EndKey() - if kv.Key(start).Cmp(startKey) < 0 { - start = startKey - } - if end == nil || kv.Key(end).Cmp(endKey) > 0 { - end = endKey - } - ranges = append(ranges, kv.KeyRange{StartKey: start, EndKey: end}) - } - if len(ranges) == 0 { - return nil, errors.Trace(errors.Errorf("no regions found")) - } - return ranges, nil -} - -func sortRanges(ranges []kv.KeyRange, isDesc bool) { - slices.SortFunc(ranges, func(i, j kv.KeyRange) int { - ir, jr := i.StartKey, j.StartKey - if !isDesc { - return ir.Cmp(jr) - } - return -ir.Cmp(jr) - }) -} - -func (s *tableRegionSampler) buildSampleColAndDecodeColMap() ([]*table.Column, map[int64]decoder.Column, error) { - schemaCols := s.schema.Columns - cols := make([]*table.Column, 0, len(schemaCols)) - colMap := make(map[int64]decoder.Column, len(schemaCols)) - tableCols := s.table.Cols() - - for _, schemaCol := range schemaCols { - for _, tableCol := range tableCols { - if tableCol.ID != schemaCol.ID { - continue - } - // The `MutRow` produced by `DecodeAndEvalRowWithMap` used `ColumnInfo.Offset` as indices. - // To evaluate the columns in virtual generated expression properly, - // indices of column(Column.Index) needs to be resolved against full column's schema. - if schemaCol.VirtualExpr != nil { - var err error - schemaCol.VirtualExpr, err = schemaCol.VirtualExpr.ResolveIndices(s.fullSchema) - if err != nil { - return nil, nil, err - } - } - colMap[tableCol.ID] = decoder.Column{ - Col: tableCol, - GenExpr: schemaCol.VirtualExpr, - } - cols = append(cols, tableCol) - } - } - // Schema columns contain _tidb_rowid, append extra handle column info. - if len(cols) < len(schemaCols) && schemaCols[len(schemaCols)-1].ID == model.ExtraHandleID { - extraHandle := model.NewExtraHandleColInfo() - extraHandle.Offset = len(cols) - tableCol := &table.Column{ColumnInfo: extraHandle} - colMap[model.ExtraHandleID] = decoder.Column{ - Col: tableCol, - } - cols = append(cols, tableCol) - } - return cols, colMap, nil -} - -func (s *tableRegionSampler) scanFirstKVForEachRange(ranges []kv.KeyRange, - fn func(handle kv.Handle, value []byte) error) error { - ver := kv.Version{Ver: s.startTS} - snap := s.ctx.GetStore().GetSnapshot(ver) - setOptionForTopSQL(s.ctx.GetSessionVars().StmtCtx, snap) - concurrency := s.ctx.GetSessionVars().ExecutorConcurrency - if len(ranges) < concurrency { - concurrency = len(ranges) - } - - fetchers := make([]*sampleFetcher, concurrency) - for i := 0; i < concurrency; i++ { - fetchers[i] = &sampleFetcher{ - workerID: i, - concurrency: concurrency, - kvChan: make(chan *sampleKV), - snapshot: snap, - ranges: ranges, - } - go fetchers[i].run() - } - syncer := sampleSyncer{ - fetchers: fetchers, - totalCount: len(ranges), - consumeFn: fn, - } - return syncer.sync() -} - -func (s *tableRegionSampler) resetRowMap() { - if s.rowMap == nil { - colLen := len(s.schema.Columns) - s.rowMap = make(map[int64]types.Datum, colLen) - return - } - for id := range s.rowMap { - delete(s.rowMap, id) - } -} - -func (s *tableRegionSampler) finished() bool { - return s.isFinished -} - -type sampleKV struct { - handle kv.Handle - value []byte -} - -type sampleFetcher struct { - workerID int - concurrency int - kvChan chan *sampleKV - err error - snapshot kv.Snapshot - ranges []kv.KeyRange -} - -func (s *sampleFetcher) run() { - defer close(s.kvChan) - for i, r := range s.ranges { - if i%s.concurrency != s.workerID { - continue - } - it, err := s.snapshot.Iter(r.StartKey, r.EndKey) - if err != nil { - s.err = err - return - } - hasValue := false - for it.Valid() { - if !tablecodec.IsRecordKey(it.Key()) { - if err = it.Next(); err != nil { - s.err = err - return - } - continue - } - handle, err := tablecodec.DecodeRowKey(it.Key()) - if err != nil { - s.err = err - return - } - hasValue = true - s.kvChan <- &sampleKV{handle: handle, value: it.Value()} - break - } - if !hasValue { - s.kvChan <- nil - } - } -} - -type sampleSyncer struct { - fetchers []*sampleFetcher - totalCount int - consumeFn func(handle kv.Handle, value []byte) error -} - -func (s *sampleSyncer) sync() error { - defer func() { - for _, f := range s.fetchers { - // Cleanup channels to terminate fetcher goroutines. - channel.Clear(f.kvChan) - } - }() - for i := 0; i < s.totalCount; i++ { - f := s.fetchers[i%len(s.fetchers)] - v, ok := <-f.kvChan - if f.err != nil { - return f.err - } - if ok && v != nil { - err := s.consumeFn(v.handle, v.value) - if err != nil { - return err - } - } - } - return nil -} diff --git a/executor/sample_test.go b/executor/sample_test.go deleted file mode 100644 index 2dd347534924d..0000000000000 --- a/executor/sample_test.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "sync/atomic" - "testing" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func createSampleTestkit(t *testing.T, store kv.Storage) *testkit.TestKit { - atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) - tk := testkit.NewTestKit(t, store) - tk.MustExec("drop database if exists test_table_sample;") - tk.MustExec("create database test_table_sample;") - tk.MustExec("use test_table_sample;") - tk.MustExec("set @@global.tidb_scatter_region=1;") - return tk -} - -func TestTableSampleBasic(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int);") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows()) - - tk.MustExec("insert into t values (0), (1000), (2000);") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("0")) - tk.MustExec("alter table t add column b varchar(255) not null default 'abc';") - tk.MustQuery("select b from t tablesample regions();").Check(testkit.Rows("abc")) - tk.MustExec("alter table t add column c int as (a + 1);") - tk.MustQuery("select c from t tablesample regions();").Check(testkit.Rows("1")) - tk.MustQuery("select c, _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1 1")) - tk.MustHavePlan("select * from t tablesample regions();", "TableSample") - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a BIGINT PRIMARY KEY AUTO_RANDOM(3), b int auto_increment, key(b)) pre_split_regions=8;") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows()) - for i := 0; i < 1000; i++ { - tk.MustExec("insert into t values();") - } - tk.MustQuery("select count(*) from t tablesample regions();").Check(testkit.Rows("8")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a varchar(30) collate utf8mb4_general_ci primary key);") - tk.MustQuery("split table t between ('a') and ('z') regions 100;").Check(testkit.Rows("99 1")) - tk.MustExec("insert into t values ('a'), ('b'), ('c'), ('d'), ('e');") - tk.MustQuery("select a from t tablesample regions() limit 2;").Check(testkit.Rows("a", "b")) -} - -func TestTableSampleMultiRegions(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int) shard_row_id_bits = 2 pre_split_regions = 2;") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?);", i) - } - rows := tk.MustQuery("select * from t tablesample regions();").Rows() - require.Len(t, rows, 4) - tk.MustQuery("select a from t tablesample regions() order by a limit 1;").Check(testkit.Rows("0")) - tk.MustQuery("select a from t tablesample regions() where a = 0;").Check(testkit.Rows("0")) - - tk.MustExec("create table t2 (a int) shard_row_id_bits = 2 pre_split_regions = 2;") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t2 values (?);", i) - } - rows = tk.MustQuery("select * from t tablesample regions(), t2 tablesample regions();").Rows() - require.Len(t, rows, 16) - tk.MustQuery("select count(*) from t tablesample regions();").Check(testkit.Rows("4")) - tk.MustExec("drop table t2;") -} - -func TestTableSampleNoSplitTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - atomic.StoreUint32(&ddl.EnableSplitTableRegion, 0) - tk.MustExec("drop table if exists t1;") - tk.MustExec("drop table if exists t2;") - tk.MustExec("create table t1 (id int primary key);") - tk.MustExec("create table t2 (id int primary key);") - tk.MustExec("insert into t2 values(1);") - rows := tk.MustQuery("select * from t1 tablesample regions();").Rows() - rows2 := tk.MustQuery("select * from t2 tablesample regions();").Rows() - require.Len(t, rows, 0) - require.Len(t, rows2, 1) -} - -func TestTableSamplePlan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a bigint, b int default 10);") - tk.MustExec("split table t between (0) and (100000) regions 4;") - tk.MustExec("insert into t(a) values (1), (2), (3);") - rows := tk.MustQuery("explain analyze select a from t tablesample regions();").Rows() - require.Len(t, rows, 2) - tableSample := fmt.Sprintf("%v", rows[1]) - require.Regexp(t, ".*TableSample.*", tableSample) -} - -func TestTableSampleSchema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - // Clustered index - tk.MustExec("create table t (a varchar(255) primary key, b bigint);") - tk.MustExec("insert into t values ('b', 100), ('y', 100);") - tk.MustQuery("split table t between ('a') and ('z') regions 2;").Check(testkit.Rows("1 1")) - tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("b", "y")) - - tk.MustExec("drop table t;") - tk.MustExec("create table t (a varchar(255), b int, c decimal, primary key (a, b, c));") - tk.MustQuery("split table t between ('a', 0, 0) and ('z', 100, 100) regions 2;").Check(testkit.Rows("1 1")) - tk.MustExec("insert into t values ('b', 10, 100), ('y', 100, 10);") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("b 10 100", "y 100 10")) - - // PKIsHandle - tk.MustExec("drop table t;") - tk.MustExec("create table t (a bigint primary key, b int default 10);") - tk.MustQuery("split table t between (1) and (100000) regions 4;").Check(testkit.Rows("3 1")) - tk.MustExec("insert into t(a) values (200), (25600), (50300), (99900), (99901)") - tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("200", "25600", "50300", "99900")) - - // _tidb_rowid - tk.MustExec("drop table t;") - tk.MustExec("create table t (a bigint, b int default 10);") - tk.MustQuery("split table t between (0) and (100000) regions 4;").Check(testkit.Rows("3 1")) - tk.MustExec("insert into t(a) values (1), (2), (3);") - tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("1")) -} - -func TestTableSampleInvalid(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int, b varchar(255));") - tk.MustExec("insert into t values (1, 'abc');") - tk.MustExec("create view v as select * from t;") - tk.MustGetErrCode("select * from v tablesample regions();", errno.ErrInvalidTableSample) - tk.MustGetErrCode("select * from information_schema.tables tablesample regions();", errno.ErrInvalidTableSample) - - tk.MustGetErrCode("select a from t tablesample system();", errno.ErrInvalidTableSample) - tk.MustGetErrCode("select a from t tablesample bernoulli(10 percent);", errno.ErrInvalidTableSample) - tk.MustGetErrCode("select a from t as t1 tablesample regions(), t as t2 tablesample system();", errno.ErrInvalidTableSample) - tk.MustGetErrCode("select a from t tablesample ();", errno.ErrInvalidTableSample) -} - -func TestTableSampleWithTiDBRowID(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int, b varchar(255));") - tk.MustExec("insert into t values (1, 'abc');") - tk.MustQuery("select _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1")) - tk.MustQuery("select a, _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1 1")) - tk.MustQuery("select _tidb_rowid, b from t tablesample regions();").Check(testkit.Rows("1 abc")) - tk.MustQuery("select b, _tidb_rowid, a from t tablesample regions();").Check(testkit.Rows("abc 1 1")) -} - -func TestTableSampleWithPartition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int, b varchar(255), primary key (a)) partition by hash(a) partitions 2;") - tk.MustExec("insert into t values (1, '1'), (2, '2'), (3, '3');") - rows := tk.MustQuery("select * from t tablesample regions();").Rows() - require.Len(t, rows, 2) - - tk.MustExec("delete from t;") - tk.MustExec("insert into t values (1, '1');") - rows = tk.MustQuery("select * from t partition (p0) tablesample regions();").Rows() - require.Len(t, rows, 0) - rows = tk.MustQuery("select * from t partition (p1) tablesample regions();").Rows() - require.Len(t, rows, 1) - - // Test https://github.com/pingcap/tidb/issues/27349. - tk.MustExec("drop table if exists t;") - tk.MustExec(`create table t (a int, b int, unique key idx(a)) partition by range (a) ( - partition p0 values less than (0), - partition p1 values less than (10), - partition p2 values less than (30), - partition p3 values less than (maxvalue));`) - tk.MustExec("insert into t values (2, 2), (31, 31), (12, 12);") - tk.MustQuery("select _tidb_rowid from t tablesample regions() order by _tidb_rowid;"). - Check(testkit.Rows("1", "2", "3")) // The order of _tidb_rowid should be correct. -} - -func TestTableSampleGeneratedColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int primary key, b int as (a + 1), c int as (b + 1), d int as (c + 1));") - tk.MustQuery("split table t between (0) and (10000) regions 4;").Check(testkit.Rows("3 1")) - tk.MustExec("insert into t(a) values (1), (2), (2999), (4999), (9999);") - tk.MustQuery("select a from t tablesample regions()").Check(testkit.Rows("1", "2999", "9999")) - tk.MustQuery("select c from t tablesample regions()").Check(testkit.Rows("3", "3001", "10001")) - tk.MustQuery("select a, b from t tablesample regions()").Check( - testkit.Rows("1 2", "2999 3000", "9999 10000")) - tk.MustQuery("select d, c from t tablesample regions()").Check( - testkit.Rows("4 3", "3002 3001", "10002 10001")) - tk.MustQuery("select a, d from t tablesample regions()").Check( - testkit.Rows("1 4", "2999 3002", "9999 10002")) -} - -func TestTableSampleUnionScanIgnorePendingKV(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int primary key);") - tk.MustQuery("split table t between (0) and (40000) regions 4;").Check(testkit.Rows("3 1")) - tk.MustExec("insert into t values (1), (1000), (10002);") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) - - tk.MustExec("begin;") - tk.MustExec("insert into t values (20006), (50000);") - // The memory DB values in transactions are ignored. - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) - tk.MustExec("delete from t where a = 1;") - // The memory DB values in transactions are ignored. - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) - tk.MustExec("commit;") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1000", "10002", "20006", "50000")) -} - -func TestTableSampleTransactionConsistency(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk2 := createSampleTestkit(t, store) - - tk.MustExec("create table t (a int primary key);") - tk.MustQuery("split table t between (0) and (40000) regions 4;").Check(testkit.Rows("3 1")) - tk.MustExec("insert into t values (1), (1000), (10002);") - - tk.MustExec("begin;") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) - tk2.MustExec("insert into t values (20006), (50000);") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) - tk.MustExec("commit;") - tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002", "20006", "50000")) -} - -func TestTableSampleNotSupportedPlanWarning(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int primary key, b int, c varchar(255));") - tk.MustQuery("split table t between (0) and (10000) regions 5;").Check(testkit.Rows("4 1")) - tk.MustExec("insert into t values (1000, 1, '1'), (1001, 1, '1'), (2100, 2, '2'), (4500, 3, '3');") - - tk.MustExec("create index idx_0 on t (b);") - tk.MustQuery("select a from t tablesample regions() order by a;").Check( - testkit.Rows("1000", "2100", "4500")) - tk.MustQuery("select a from t use index (idx_0) tablesample regions() order by a;").Check( - testkit.Rows("1000", "1001", "2100", "4500")) - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 8128 Invalid TABLESAMPLE: plan not supported")) -} - -func TestMaxChunkSize(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := createSampleTestkit(t, store) - tk.MustExec("create table t (a int) shard_row_id_bits = 2 pre_split_regions = 2;") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?);", i) - } - tk.Session().GetSessionVars().MaxChunkSize = 1 - rows := tk.MustQuery("select * from t tablesample regions();").Rows() - require.Len(t, rows, 4) -} diff --git a/executor/set.go b/executor/set.go deleted file mode 100644 index fbc22f2319869..0000000000000 --- a/executor/set.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - disttaskutil "github.com/pingcap/tidb/util/disttask" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// SetExecutor executes set statement. -type SetExecutor struct { - exec.BaseExecutor - - vars []*expression.VarAssignment - done bool -} - -// Next implements the Executor Next interface. -func (e *SetExecutor) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.done { - return nil - } - e.done = true - sessionVars := e.Ctx().GetSessionVars() - for _, v := range e.vars { - // Variable is case insensitive, we use lower case. - if v.Name == ast.SetNames || v.Name == ast.SetCharset { - // This is set charset stmt. - if v.IsDefault { - err := e.setCharset(mysql.DefaultCharset, "", v.Name == ast.SetNames) - if err != nil { - return err - } - continue - } - dt, err := v.Expr.(*expression.Constant).Eval(chunk.Row{}) - if err != nil { - return err - } - cs := dt.GetString() - var co string - if v.ExtendValue != nil { - co = v.ExtendValue.Value.GetString() - } - err = e.setCharset(cs, co, v.Name == ast.SetNames) - if err != nil { - return err - } - continue - } - name := strings.ToLower(v.Name) - if !v.IsSystem { - // Set user variable. - value, err := v.Expr.Eval(chunk.Row{}) - if err != nil { - return err - } - if value.IsNull() { - sessionVars.UnsetUserVar(name) - } else { - sessionVars.SetUserVarVal(name, value) - sessionVars.SetUserVarType(name, v.Expr.GetType()) - } - continue - } - - if err := e.setSysVariable(ctx, name, v); err != nil { - return err - } - } - return nil -} - -func (e *SetExecutor) setSysVariable(ctx context.Context, name string, v *expression.VarAssignment) error { - sessionVars := e.Ctx().GetSessionVars() - sysVar := variable.GetSysVar(name) - if sysVar == nil { - if variable.IsRemovedSysVar(name) { - return nil // removed vars permit parse-but-ignore - } - return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - - if sysVar.RequireDynamicPrivileges != nil { - semEnabled := sem.IsEnabled() - pm := privilege.GetPrivilegeManager(e.Ctx()) - privs := sysVar.RequireDynamicPrivileges(v.IsGlobal, semEnabled) - for _, priv := range privs { - if !pm.RequestDynamicVerification(sessionVars.ActiveRoles, priv, false) { - msg := priv - if !semEnabled { - msg = "SUPER or " + msg - } - return core.ErrSpecificAccessDenied.GenWithStackByArgs(msg) - } - } - } - - if sysVar.IsNoop && !variable.EnableNoopVariables.Load() { - // The variable is a noop. For compatibility we allow it to still - // be changed, but we append a warning since users might be expecting - // something that's not going to happen. - sessionVars.StmtCtx.AppendWarning(exeerrors.ErrSettingNoopVariable.GenWithStackByArgs(sysVar.Name)) - } - if sysVar.HasInstanceScope() && !v.IsGlobal && sessionVars.EnableLegacyInstanceScope { - // For backward compatibility we will change the v.IsGlobal to true, - // and append a warning saying this will not be supported in future. - v.IsGlobal = true - sessionVars.StmtCtx.AppendWarning(exeerrors.ErrInstanceScope.GenWithStackByArgs(sysVar.Name)) - } - - if v.IsGlobal { - valStr, err := e.getVarValue(ctx, v, sysVar) - if err != nil { - return err - } - err = sessionVars.GlobalVarsAccessor.SetGlobalSysVar(ctx, name, valStr) - if err != nil { - return err - } - err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { - auditPlugin := plugin.DeclareAuditManifest(p.Manifest) - if auditPlugin.OnGlobalVariableEvent != nil { - auditPlugin.OnGlobalVariableEvent(context.Background(), e.Ctx().GetSessionVars(), name, valStr) - } - return nil - }) - logutil.BgLogger().Info("set global var", zap.Uint64("conn", sessionVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) - if name == variable.TiDBServiceScope { - dom := domain.GetDomain(e.Ctx()) - serverID := disttaskutil.GenerateSubtaskExecID(ctx, dom.DDL().GetID()) - _, err = e.Ctx().(sqlexec.SQLExecutor).ExecuteInternal(ctx, - `update mysql.dist_framework_meta - set role = %? - where host = %?`, valStr, serverID) - } - return err - } - // Set session variable - valStr, err := e.getVarValue(ctx, v, nil) - if err != nil { - return err - } - getSnapshotTSByName := func() uint64 { - if name == variable.TiDBSnapshot { - return sessionVars.SnapshotTS - } else if name == variable.TiDBTxnReadTS { - return sessionVars.TxnReadTS.PeakTxnReadTS() - } - return 0 - } - oldSnapshotTS := getSnapshotTSByName() - fallbackOldSnapshotTS := func() { - if name == variable.TiDBSnapshot { - sessionVars.SnapshotTS = oldSnapshotTS - } else if name == variable.TiDBTxnReadTS { - sessionVars.TxnReadTS.SetTxnReadTS(oldSnapshotTS) - } - } - if sessionVars.InTxn() { - if name == variable.TxnIsolationOneShot || - name == variable.TiDBTxnReadTS { - return errors.Trace(exeerrors.ErrCantChangeTxCharacteristics) - } - if name == variable.TiDBSnapshot && sessionVars.TxnCtx.IsStaleness { - return errors.Trace(exeerrors.ErrCantChangeTxCharacteristics) - } - } - err = sessionVars.SetSystemVar(name, valStr) - if err != nil { - return err - } - newSnapshotTS := getSnapshotTSByName() - newSnapshotIsSet := newSnapshotTS > 0 && newSnapshotTS != oldSnapshotTS - if newSnapshotIsSet { - if name == variable.TiDBTxnReadTS { - err = sessionctx.ValidateStaleReadTS(ctx, e.Ctx(), newSnapshotTS) - } else { - err = sessionctx.ValidateSnapshotReadTS(ctx, e.Ctx(), newSnapshotTS) - // Also check gc safe point for snapshot read. - // We don't check snapshot with gc safe point for read_ts - // Client-go will automatically check the snapshotTS with gc safe point. It's unnecessary to check gc safe point during set executor. - if err == nil { - err = gcutil.ValidateSnapshot(e.Ctx(), newSnapshotTS) - } - } - if err != nil { - fallbackOldSnapshotTS() - return err - } - } - - err = e.loadSnapshotInfoSchemaIfNeeded(name, newSnapshotTS) - if err != nil { - fallbackOldSnapshotTS() - return err - } - // Clients are often noisy in setting session variables such as - // autocommit, timezone, query cache - logutil.BgLogger().Debug("set session var", zap.Uint64("conn", sessionVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) - return nil -} - -func (e *SetExecutor) setCharset(cs, co string, isSetName bool) error { - var err error - sessionVars := e.Ctx().GetSessionVars() - if co == "" { - if co, err = charset.GetDefaultCollation(cs); err != nil { - return err - } - } else { - var coll *charset.Collation - if coll, err = collate.GetCollationByName(co); err != nil { - return err - } - if coll.CharsetName != cs { - return charset.ErrCollationCharsetMismatch.GenWithStackByArgs(coll.Name, cs) - } - } - if isSetName { - for _, v := range variable.SetNamesVariables { - if err = sessionVars.SetSystemVar(v, cs); err != nil { - return errors.Trace(err) - } - } - return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, co)) - } - // Set charset statement, see also https://dev.mysql.com/doc/refman/8.0/en/set-character-set.html. - for _, v := range variable.SetCharsetVariables { - if err = sessionVars.SetSystemVar(v, cs); err != nil { - return errors.Trace(err) - } - } - csDB, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CharsetDatabase) - if err != nil { - return err - } - coDB, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CollationDatabase) - if err != nil { - return err - } - err = sessionVars.SetSystemVar(variable.CharacterSetConnection, csDB) - if err != nil { - return errors.Trace(err) - } - return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, coDB)) -} - -func (e *SetExecutor) getVarValue(ctx context.Context, v *expression.VarAssignment, sysVar *variable.SysVar) (value string, err error) { - if v.IsDefault { - // To set a SESSION variable to the GLOBAL value or a GLOBAL value - // to the compiled-in MySQL default value, use the DEFAULT keyword. - // See http://dev.mysql.com/doc/refman/5.7/en/set-statement.html - if sysVar != nil { - return sysVar.Value, nil - } - return e.Ctx().GetSessionVars().GetGlobalSystemVar(ctx, v.Name) - } - nativeVal, err := v.Expr.Eval(chunk.Row{}) - if err != nil || nativeVal.IsNull() { - return "", err - } - - value, err = nativeVal.ToString() - if err != nil { - return "", err - } - - // We need to clone the string because the value is constructed by `hack.String` in Datum which reuses the under layer `[]byte` - // instead of allocating some new spaces. The `[]byte` in Datum will be reused in `chunk.Chunk` by different statements in session. - // If we do not clone the value, the system variable will have a risk to be modified by other statements. - return strings.Clone(value), nil -} - -func (e *SetExecutor) loadSnapshotInfoSchemaIfNeeded(name string, snapshotTS uint64) error { - if name != variable.TiDBSnapshot && name != variable.TiDBTxnReadTS { - return nil - } - vars := e.Ctx().GetSessionVars() - if snapshotTS == 0 { - vars.SnapshotInfoschema = nil - return nil - } - logutil.BgLogger().Info("load snapshot info schema", - zap.Uint64("conn", vars.ConnectionID), - zap.Uint64("SnapshotTS", snapshotTS)) - dom := domain.GetDomain(e.Ctx()) - snapInfo, err := dom.GetSnapshotInfoSchema(snapshotTS) - if err != nil { - return err - } - - vars.SnapshotInfoschema = temptable.AttachLocalTemporaryTableInfoSchema(e.Ctx(), snapInfo) - return nil -} diff --git a/executor/set_test.go b/executor/set_test.go deleted file mode 100644 index 9e04db43ad1e2..0000000000000 --- a/executor/set_test.go +++ /dev/null @@ -1,2171 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "bytes" - "context" - "errors" - "fmt" - "io" - "net/http" - "strconv" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/stretchr/testify/require" -) - -func TestSetVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("SET @a = 1;") - tk.MustExec(`SET @a = "1";`) - tk.MustExec("SET @a = null;") - tk.MustExec("SET @@global.autocommit = 1;") - - // TODO: this test case should returns error. - // err := tk.ExecToErr("SET @@global.autocommit = null;") - // c.Assert(err, NotNil) - - tk.MustExec("SET @@autocommit = 1;") - require.Error(t, tk.ExecToErr("SET @@autocommit = null;")) - require.Error(t, tk.ExecToErr("SET @@date_format = 1;")) - require.Error(t, tk.ExecToErr("SET @@rewriter_enabled = 1;")) - require.Error(t, tk.ExecToErr("SET xxx = abcd;")) - require.Error(t, tk.ExecToErr("SET @@global.a = 1;")) - require.Error(t, tk.ExecToErr("SET @@global.timestamp = 1;")) - - // For issue 998 - tk.MustExec("SET @issue998a=1, @issue998b=5;") - tk.MustQuery(`select @issue998a, @issue998b;`).Check(testkit.Rows("1 5")) - tk.MustExec("SET @@autocommit=0, @issue998a=2;") - tk.MustQuery(`select @issue998a, @@autocommit;`).Check(testkit.Rows("2 0")) - tk.MustExec("SET @@global.autocommit=1, @issue998b=6;") - tk.MustQuery(`select @issue998b, @@global.autocommit;`).Check(testkit.Rows("6 1")) - - // For issue 4302 - tk.MustExec("use test;drop table if exists x;create table x(a int);insert into x value(1);") - tk.MustExec("SET @issue4302=(select a from x limit 1);") - tk.MustQuery(`select @issue4302;`).Check(testkit.Rows("1")) - - // Set default - // {ScopeGlobal | ScopeSession, "low_priority_updates", "OFF"}, - // For global var - tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("0")) - tk.MustExec(`set @@global.low_priority_updates="ON";`) - tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("1")) - tk.MustExec(`set @@global.low_priority_updates=DEFAULT;`) // It will be set to default var value. - tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("0")) - // For session - tk.MustQuery(`select @@session.low_priority_updates;`).Check(testkit.Rows("0")) - tk.MustExec(`set @@global.low_priority_updates="ON";`) - tk.MustExec(`set @@session.low_priority_updates=DEFAULT;`) // It will be set to global var value. - tk.MustQuery(`select @@session.low_priority_updates;`).Check(testkit.Rows("1")) - - // For mysql jdbc driver issue. - tk.MustQuery(`select @@session.tx_read_only;`).Check(testkit.Rows("0")) - - // Test session variable states. - vars := tk.Session().(sessionctx.Context).GetSessionVars() - require.NoError(t, tk.Session().CommitTxn(context.TODO())) - tk.MustExec("set @@autocommit = 1") - require.False(t, vars.InTxn()) - require.True(t, vars.IsAutocommit()) - tk.MustExec("set @@autocommit = 0") - require.False(t, vars.IsAutocommit()) - - tk.MustExec("set @@sql_mode = 'strict_trans_tables'") - require.True(t, vars.StrictSQLMode) - tk.MustExec("set @@sql_mode = ''") - require.False(t, vars.StrictSQLMode) - - tk.MustExec("set names utf8") - charset, collation := vars.GetCharsetInfo() - require.Equal(t, "utf8", charset) - require.Equal(t, "utf8_bin", collation) - - tk.MustExec("set names latin1 collate latin1_bin") - charset, collation = vars.GetCharsetInfo() - require.Equal(t, "latin1", charset) - require.Equal(t, "latin1_bin", collation) - - tk.MustExec("set names utf8 collate default") - charset, collation = vars.GetCharsetInfo() - require.Equal(t, "utf8", charset) - require.Equal(t, "utf8_bin", collation) - - expectErrMsg := "[ddl:1273]Unknown collation: 'non_exist_collation'" - tk.MustGetErrMsg("set names utf8 collate non_exist_collation", expectErrMsg) - tk.MustGetErrMsg("set @@session.collation_server='non_exist_collation'", expectErrMsg) - tk.MustGetErrMsg("set @@session.collation_database='non_exist_collation'", expectErrMsg) - tk.MustGetErrMsg("set @@session.collation_connection='non_exist_collation'", expectErrMsg) - tk.MustGetErrMsg("set @@global.collation_server='non_exist_collation'", expectErrMsg) - tk.MustGetErrMsg("set @@global.collation_database='non_exist_collation'", expectErrMsg) - tk.MustGetErrMsg("set @@global.collation_connection='non_exist_collation'", expectErrMsg) - - expectErrMsg = "[parser:1115]Unknown character set: 'boguscharsetname'" - tk.MustGetErrMsg("set names boguscharsetname", expectErrMsg) - - tk.MustExec("set character_set_results = NULL") - tk.MustQuery("select @@character_set_results").Check(testkit.Rows("")) - - tk.MustExec("set @@global.ddl_slow_threshold=12345") - tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows("12345")) - require.Equal(t, uint32(12345), variable.DDLSlowOprThreshold) - tk.MustExec("set session ddl_slow_threshold=\"54321\"") - tk.MustQuery("show variables like 'ddl_slow_threshold'").Check(testkit.Rows("ddl_slow_threshold 54321")) - require.Equal(t, uint32(54321), variable.DDLSlowOprThreshold) - tk.MustExec("set @@global.ddl_slow_threshold=-1") - tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBDDLSlowOprThreshold))) - require.Equal(t, uint32(variable.DefTiDBDDLSlowOprThreshold), variable.DDLSlowOprThreshold) - require.Error(t, tk.ExecToErr("set @@global.ddl_slow_threshold=abc")) - tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBDDLSlowOprThreshold))) - require.Equal(t, uint32(variable.DefTiDBDDLSlowOprThreshold), variable.DDLSlowOprThreshold) - - // Test set transaction isolation level, which is equivalent to setting variable "tx_isolation". - tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - // error - err := tk.ExecToErr("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - // Fails - err = tk.ExecToErr("SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) - - // test synonyms variables - tk.MustExec("SET SESSION tx_isolation = 'READ-COMMITTED'") - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - err = tk.ExecToErr("SET SESSION tx_isolation = 'READ-UNCOMMITTED'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - // fails - err = tk.ExecToErr("SET SESSION transaction_isolation = 'SERIALIZABLE'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - // fails - err = tk.ExecToErr("SET GLOBAL transaction_isolation = 'SERIALIZABLE'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) - - err = tk.ExecToErr("SET GLOBAL transaction_isolation = 'READ-UNCOMMITTED'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) - - err = tk.ExecToErr("SET GLOBAL tx_isolation = 'SERIALIZABLE'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) - - // Even the transaction fail, set session variable would success. - tk.MustExec("BEGIN") - tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") - require.Error(t, tk.ExecToErr(`INSERT INTO t VALUES ("sdfsdf")`)) - tk.MustExec("COMMIT") - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - - tk.MustExec("set global avoid_temporal_upgrade = on") - tk.MustQuery(`select @@global.avoid_temporal_upgrade;`).Check(testkit.Rows("1")) - tk.MustExec("set @@global.avoid_temporal_upgrade = off") - tk.MustQuery(`select @@global.avoid_temporal_upgrade;`).Check(testkit.Rows("0")) - tk.MustExec("set session sql_log_bin = on") - tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("1")) - tk.MustExec("set sql_log_bin = off") - tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("0")) - tk.MustExec("set @@sql_log_bin = on") - tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("1")) - - binlogValue := "0" - if config.GetGlobalConfig().Binlog.Enable { - binlogValue = "1" - } - tk.MustQuery(`select @@global.log_bin;`).Check(testkit.Rows(binlogValue)) - tk.MustQuery(`select @@log_bin;`).Check(testkit.Rows(binlogValue)) - - tk.MustExec("set @@tidb_general_log = 1") - tk.MustExec("set @@tidb_general_log = 0") - - tk.MustExec("set @@tidb_pprof_sql_cpu = 1") - tk.MustExec("set @@tidb_pprof_sql_cpu = 0") - - tk.MustExec(`set @@block_encryption_mode = "aes-128-ecb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-ecb")) - tk.MustExec(`set @@block_encryption_mode = "aes-192-ecb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-ecb")) - tk.MustExec(`set @@block_encryption_mode = "aes-256-ecb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-ecb")) - tk.MustExec(`set @@block_encryption_mode = "aes-128-cbc"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-cbc")) - tk.MustExec(`set @@block_encryption_mode = "aes-192-cbc"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-cbc")) - tk.MustExec(`set @@block_encryption_mode = "aes-256-cbc"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cbc")) - tk.MustExec(`set @@block_encryption_mode = "aes-128-ofb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-ofb")) - tk.MustExec(`set @@block_encryption_mode = "aes-192-ofb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-ofb")) - tk.MustExec(`set @@block_encryption_mode = "aes-256-ofb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-ofb")) - tk.MustExec(`set @@block_encryption_mode = "aes-128-cfb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-cfb")) - tk.MustExec(`set @@block_encryption_mode = "aes-192-cfb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-cfb")) - tk.MustExec(`set @@block_encryption_mode = "aes-256-cfb"`) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cfb")) - require.Error(t, tk.ExecToErr("set @@block_encryption_mode = 'abc'")) - tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cfb")) - - tk.MustExec(`set @@global.tidb_force_priority = "no_priority"`) - tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("NO_PRIORITY")) - tk.MustExec(`set @@global.tidb_force_priority = "low_priority"`) - tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("LOW_PRIORITY")) - tk.MustExec(`set @@global.tidb_force_priority = "high_priority"`) - tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("HIGH_PRIORITY")) - tk.MustExec(`set @@global.tidb_force_priority = "delayed"`) - tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("DELAYED")) - require.Error(t, tk.ExecToErr("set global tidb_force_priority = 'abc'")) - tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("DELAYED")) - - tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_low"`) - tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_LOW")) - tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_normal"`) - tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_NORMAL")) - tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_high"`) - tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_HIGH")) - require.Error(t, tk.ExecToErr("set session tidb_ddl_reorg_priority = 'abc'")) - tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_HIGH")) - - tk.MustExec("set tidb_opt_write_row_id = 1") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_opt_write_row_id = 0") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) - tk.MustExec("set tidb_opt_write_row_id = true") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_opt_write_row_id = false") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) - tk.MustExec("set tidb_opt_write_row_id = On") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_opt_write_row_id = Off") - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) - require.Error(t, tk.ExecToErr("set tidb_opt_write_row_id = 'abc'")) - tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) - - tk.MustExec("set tidb_checksum_table_concurrency = 42") - tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows("42")) - require.Error(t, tk.ExecToErr("set tidb_checksum_table_concurrency = 'abc'")) - tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows("42")) - tk.MustExec("set tidb_checksum_table_concurrency = 257") - tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) - - tk.MustExec("set tidb_build_stats_concurrency = 42") - tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows("42")) - tk.MustExec("set tidb_build_sampling_stats_concurrency = 42") - tk.MustQuery(`select @@tidb_build_sampling_stats_concurrency;`).Check(testkit.Rows("42")) - require.Error(t, tk.ExecToErr("set tidb_build_sampling_stats_concurrency = 'abc'")) - require.Error(t, tk.ExecToErr("set tidb_build_stats_concurrency = 'abc'")) - tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows("42")) - tk.MustExec("set tidb_build_stats_concurrency = 257") - tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) - tk.MustExec("set tidb_build_sampling_stats_concurrency = 257") - tk.MustQuery(`select @@tidb_build_sampling_stats_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) - - tk.MustExec(`set tidb_partition_prune_mode = "static"`) - tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("static")) - tk.MustExec(`set tidb_partition_prune_mode = "dynamic"`) - tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) - tk.MustExec(`set tidb_partition_prune_mode = "static-only"`) - tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("static")) - tk.MustExec(`set tidb_partition_prune_mode = "dynamic-only"`) - tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) - require.Error(t, tk.ExecToErr("set tidb_partition_prune_mode = 'abc'")) - tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) - - tk.MustExec("set tidb_constraint_check_in_place = 1") - tk.MustQuery(`select @@session.tidb_constraint_check_in_place;`).Check(testkit.Rows("1")) - tk.MustExec("set global tidb_constraint_check_in_place = 0") - tk.MustQuery(`select @@global.tidb_constraint_check_in_place;`).Check(testkit.Rows("0")) - - tk.MustExec("set tidb_batch_commit = 0") - tk.MustQuery("select @@session.tidb_batch_commit;").Check(testkit.Rows("0")) - tk.MustExec("set tidb_batch_commit = 1") - tk.MustQuery("select @@session.tidb_batch_commit;").Check(testkit.Rows("1")) - require.Error(t, tk.ExecToErr("set global tidb_batch_commit = 0")) - require.Error(t, tk.ExecToErr("set global tidb_batch_commit = 2")) - - // test skip isolation level check: init - tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") - tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") - tk.MustExec("SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED") - tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - // test skip isolation level check: error - err = tk.ExecToErr("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - err = tk.ExecToErr("SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) - - // test skip isolation level check: success - tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 1") - tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 1") - tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 8048 The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error")) - tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("SERIALIZABLE")) - tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("SERIALIZABLE")) - - // test skip isolation level check: success - tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") - tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 1") - tk.MustExec("SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 8048 The isolation level 'READ-UNCOMMITTED' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error")) - tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-UNCOMMITTED")) - tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-UNCOMMITTED")) - - // test skip isolation level check: reset - tk.MustExec("SET GLOBAL transaction_isolation='REPEATABLE-READ'") // should reset tx_isolation back to rr before reset tidb_skip_isolation_level_check - tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") - tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") - - // test for tidb_wait_split_region_finish - tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_wait_split_region_finish = 1") - tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_wait_split_region_finish = 0") - tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("0")) - - // test for tidb_scatter_region - tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("0")) - tk.MustExec("set global tidb_scatter_region = 1") - tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("1")) - tk.MustExec("set global tidb_scatter_region = 0") - tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("0")) - require.Error(t, tk.ExecToErr("set session tidb_scatter_region = 0")) - require.Error(t, tk.ExecToErr(`select @@session.tidb_scatter_region;`)) - - // test for tidb_wait_split_region_timeout - tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows(strconv.Itoa(variable.DefWaitSplitRegionTimeout))) - tk.MustExec("set tidb_wait_split_region_timeout = 1") - tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows("1")) - tk.MustExec("set tidb_wait_split_region_timeout = 0") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_wait_split_region_timeout value: '0'")) - tk.MustQuery(`select @@tidb_wait_split_region_timeout`).Check(testkit.Rows("1")) - - tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows("1")) - - tk.MustExec("set session tidb_backoff_weight = 3") - tk.MustQuery("select @@session.tidb_backoff_weight;").Check(testkit.Rows("3")) - tk.MustExec("set session tidb_backoff_weight = 20") - tk.MustQuery("select @@session.tidb_backoff_weight;").Check(testkit.Rows("20")) - tk.MustExec("set session tidb_backoff_weight = -1") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_backoff_weight value: '-1'")) - tk.MustExec("set global tidb_backoff_weight = 0") - tk.MustQuery("select @@global.tidb_backoff_weight;").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_backoff_weight = 10") - tk.MustQuery("select @@global.tidb_backoff_weight;").Check(testkit.Rows("10")) - - tk.MustExec("set @@tidb_expensive_query_time_threshold=70") - tk.MustQuery("select @@tidb_expensive_query_time_threshold;").Check(testkit.Rows("70")) - - tk.MustExec("set @@tidb_expensive_txn_time_threshold=120") - tk.MustQuery("select @@tidb_expensive_txn_time_threshold;").Check(testkit.Rows("120")) - - tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("0")) - tk.MustExec("set @@global.tidb_store_limit = 100") - tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("100")) - tk.MustExec("set @@global.tidb_store_limit = 0") - tk.MustExec("set global tidb_store_limit = 10000") - tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("10000")) - - tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("16384")) - tk.MustExec("set @@global.tidb_txn_commit_batch_size = 100") - tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("100")) - tk.MustExec("set @@global.tidb_txn_commit_batch_size = 0") - tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_txn_commit_batch_size = 100") - tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("100")) - - tk.MustQuery("select @@session.tidb_metric_query_step;").Check(testkit.Rows("60")) - tk.MustExec("set @@session.tidb_metric_query_step = 120") - tk.MustExec("set @@session.tidb_metric_query_step = 9") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_metric_query_step value: '9'")) - tk.MustQuery("select @@session.tidb_metric_query_step;").Check(testkit.Rows("10")) - - tk.MustQuery("select @@session.tidb_metric_query_range_duration;").Check(testkit.Rows("60")) - tk.MustExec("set @@session.tidb_metric_query_range_duration = 120") - tk.MustExec("set @@session.tidb_metric_query_range_duration = 9") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_metric_query_range_duration value: '9'")) - tk.MustQuery("select @@session.tidb_metric_query_range_duration;").Check(testkit.Rows("10")) - - tk.MustExec("set @@cte_max_recursion_depth=100") - tk.MustQuery("select @@cte_max_recursion_depth").Check(testkit.Rows("100")) - tk.MustExec("set @@global.cte_max_recursion_depth=100") - tk.MustQuery("select @@global.cte_max_recursion_depth").Check(testkit.Rows("100")) - tk.MustExec("set @@cte_max_recursion_depth=-1") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect cte_max_recursion_depth value: '-1'")) - tk.MustQuery("select @@cte_max_recursion_depth").Check(testkit.Rows("0")) - - // test for tidb_redact_log - tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("0")) - tk.MustExec("set global tidb_redact_log = 1") - tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("1")) - tk.MustExec("set global tidb_redact_log = 0") - tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("0")) - tk.MustExec("set session tidb_redact_log = 0") - tk.MustQuery(`select @@session.tidb_redact_log;`).Check(testkit.Rows("0")) - tk.MustExec("set session tidb_redact_log = 1") - tk.MustQuery(`select @@session.tidb_redact_log;`).Check(testkit.Rows("1")) - - tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("0")) - tk.MustExec("set @@session.tidb_dml_batch_size = 120") - tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("120")) - tk.MustExec("set @@session.tidb_dml_batch_size = -120") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_dml_batch_size value: '?'")) // redacted because of tidb_redact_log = 1 above - tk.MustQuery("select @@session.tidb_dml_batch_size").Check(testkit.Rows("0")) - tk.MustExec("set session tidb_redact_log = 0") - tk.MustExec("set session tidb_dml_batch_size = -120") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_dml_batch_size value: '-120'")) // without redaction - - tk.MustExec("set @@session.tidb_dml_batch_size = 120") - tk.MustExec("set @@global.tidb_dml_batch_size = 200") // now permitted due to TiDB #19809 - tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("120")) // global only applies to new sessions - - err = tk.ExecToErr("set tidb_enable_parallel_apply=-1") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar)) - - // test for tidb_mem_quota_apply_cache - defVal := fmt.Sprintf("%v", variable.DefTiDBMemQuotaApplyCache) - tk.MustQuery(`select @@tidb_mem_quota_apply_cache`).Check(testkit.Rows(defVal)) - tk.MustExec(`set global tidb_mem_quota_apply_cache = 1`) - tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("1")) - tk.MustExec(`set global tidb_mem_quota_apply_cache = 0`) - tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("0")) - tk.MustExec(`set tidb_mem_quota_apply_cache = 123`) - tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("0")) - tk.MustQuery(`select @@tidb_mem_quota_apply_cache`).Check(testkit.Rows("123")) - - // test for tidb_mem_quota_bind_cache - defVal = fmt.Sprintf("%v", variable.DefTiDBMemQuotaBindingCache) - tk.MustQuery(`select @@tidb_mem_quota_binding_cache`).Check(testkit.Rows(defVal)) - tk.MustExec(`set global tidb_mem_quota_binding_cache = 1`) - tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("1")) - tk.MustExec(`set global tidb_mem_quota_binding_cache = 0`) - tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("0")) - tk.MustExec(`set global tidb_mem_quota_binding_cache = 123`) - tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("123")) - tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("123")) - - // test for tidb_enable_parallel_apply - tk.MustQuery(`select @@tidb_enable_parallel_apply`).Check(testkit.Rows("0")) - tk.MustExec(`set global tidb_enable_parallel_apply = 1`) - tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("1")) - tk.MustExec(`set global tidb_enable_parallel_apply = 0`) - tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("0")) - tk.MustExec(`set tidb_enable_parallel_apply=1`) - tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("0")) - tk.MustQuery(`select @@tidb_enable_parallel_apply`).Check(testkit.Rows("1")) - - tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) - tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) - tk.MustExec("set tidb_general_log = 1") - tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("1")) - tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log ON")) - tk.MustExec("set tidb_general_log = 0") - tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) - tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) - tk.MustExec("set tidb_general_log = on") - tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("1")) - tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log ON")) - tk.MustExec("set tidb_general_log = off") - tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) - tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) - require.Error(t, tk.ExecToErr("set tidb_general_log = abc")) - require.Error(t, tk.ExecToErr("set tidb_general_log = 123")) - - tk.MustExec(`SET @@character_set_results = NULL;`) - tk.MustQuery(`select @@character_set_results;`).Check(testkit.Rows("")) - - varList := []string{"character_set_server", "character_set_client", "character_set_filesystem", "character_set_database"} - for _, v := range varList { - tk.MustGetErrCode(fmt.Sprintf("SET @@global.%s = @global_start_value;", v), mysql.ErrWrongValueForVar) - tk.MustGetErrCode(fmt.Sprintf("SET @@%s = @global_start_value;", v), mysql.ErrWrongValueForVar) - tk.MustGetErrCode(fmt.Sprintf("SET @@%s = NULL;", v), mysql.ErrWrongValueForVar) - tk.MustGetErrCode(fmt.Sprintf("SET @@%s = \"\";", v), mysql.ErrWrongValueForVar) - tk.MustGetErrMsg(fmt.Sprintf("SET @@%s = \"somecharset\";", v), "Unknown charset somecharset") - // we do not support set character_set_xxx or collation_xxx to a collation id. - tk.MustGetErrMsg(fmt.Sprintf("SET @@global.%s = 46;", v), "Unknown charset 46") - tk.MustGetErrMsg(fmt.Sprintf("SET @@%s = 46;", v), "Unknown charset 46") - } - - tk.MustExec("SET SESSION tidb_enable_extended_stats = on") - tk.MustQuery("select @@session.tidb_enable_extended_stats").Check(testkit.Rows("1")) - tk.MustExec("SET SESSION tidb_enable_extended_stats = off") - tk.MustQuery("select @@session.tidb_enable_extended_stats").Check(testkit.Rows("0")) - tk.MustExec("SET GLOBAL tidb_enable_extended_stats = on") - tk.MustQuery("select @@global.tidb_enable_extended_stats").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL tidb_enable_extended_stats = off") - tk.MustQuery("select @@global.tidb_enable_extended_stats").Check(testkit.Rows("0")) - - tk.MustExec("SET SESSION tidb_allow_fallback_to_tikv = 'tiflash'") - tk.MustQuery("select @@session.tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) - tk.MustExec("SET SESSION tidb_allow_fallback_to_tikv = ''") - tk.MustQuery("select @@session.tidb_allow_fallback_to_tikv").Check(testkit.Rows("")) - tk.MustExec("SET GLOBAL tidb_allow_fallback_to_tikv = 'tiflash'") - tk.MustQuery("select @@global.tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) - tk.MustExec("SET GLOBAL tidb_allow_fallback_to_tikv = ''") - tk.MustQuery("select @@global.tidb_allow_fallback_to_tikv").Check(testkit.Rows("")) - tk.MustExec("set @@tidb_allow_fallback_to_tikv = 'tiflash, tiflash, tiflash'") - tk.MustQuery("select @@tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) - - tk.MustGetErrMsg("SET SESSION tidb_allow_fallback_to_tikv = 'tikv,tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tikv,tiflash'") - tk.MustGetErrMsg("SET GLOBAL tidb_allow_fallback_to_tikv = 'tikv,tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tikv,tiflash'") - tk.MustGetErrMsg("set @@tidb_allow_fallback_to_tikv = 'tidb, tiflash, tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tidb, tiflash, tiflash'") - tk.MustGetErrMsg("set @@tidb_allow_fallback_to_tikv = 'unknown, tiflash, tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'unknown, tiflash, tiflash'") - - // Test issue #22145 - tk.MustExec(`set global sync_relay_log = "'"`) - - tk.MustExec(`set @@global.tidb_enable_clustered_index = 'int_only'`) - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1287 'INT_ONLY' is deprecated and will be removed in a future release. Please use 'ON' or 'OFF' instead")) - tk.MustExec(`set @@global.tidb_enable_clustered_index = 'off'`) - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec("set @@tidb_enable_clustered_index = 'off'") - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec("set @@tidb_enable_clustered_index = 'on'") - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec("set @@tidb_enable_clustered_index = 'int_only'") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1287 'INT_ONLY' is deprecated and will be removed in a future release. Please use 'ON' or 'OFF' instead")) - - // test for tidb_enable_ordered_result_mode - tk.MustQuery(`select @@tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) - tk.MustExec(`set global tidb_enable_ordered_result_mode = 1`) - tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("1")) - tk.MustExec(`set global tidb_enable_ordered_result_mode = 0`) - tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) - tk.MustExec(`set tidb_enable_ordered_result_mode=1`) - tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) - tk.MustQuery(`select @@tidb_enable_ordered_result_mode`).Check(testkit.Rows("1")) - - // test for tidb_opt_enable_correlation_adjustment - tk.MustQuery(`select @@tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) - tk.MustExec(`set global tidb_opt_enable_correlation_adjustment = 0`) - tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("0")) - tk.MustExec(`set global tidb_opt_enable_correlation_adjustment = 1`) - tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) - tk.MustExec(`set tidb_opt_enable_correlation_adjustment=0`) - tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) - tk.MustQuery(`select @@tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("0")) - - // test for tidb_opt_limit_push_down_threshold - tk.MustQuery(`select @@tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) - tk.MustExec(`set global tidb_opt_limit_push_down_threshold = 20`) - tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("20")) - tk.MustExec(`set global tidb_opt_limit_push_down_threshold = 100`) - tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) - tk.MustExec(`set tidb_opt_limit_push_down_threshold = 20`) - tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) - tk.MustQuery(`select @@tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("20")) - - tk.MustQuery("select @@tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_opt_prefer_range_scan = 1") - tk.MustQuery("select @@global.tidb_opt_prefer_range_scan").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_opt_prefer_range_scan = 0") - tk.MustQuery("select @@global.tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) - tk.MustExec("set session tidb_opt_prefer_range_scan = 1") - tk.MustQuery("select @@session.tidb_opt_prefer_range_scan").Check(testkit.Rows("1")) - tk.MustExec("set session tidb_opt_prefer_range_scan = 0") - tk.MustQuery("select @@session.tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) - - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 0.5") - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0.5")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 1") - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 1.5") - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("1.5")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10") - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = -1") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '-1'")) - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = -0.01") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '-0.01'")) - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10.01") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '10.01'")) - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) - tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10.1") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '10.1'")) - tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) - require.Error(t, tk.ExecToErr("set tidb_tso_client_batch_max_wait_time = 1")) - - tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_enable_tso_follower_proxy = 1") - tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_tso_follower_proxy = 0") - tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("0")) - require.Error(t, tk.ExecToErr("set tidb_enable_tso_follower_proxy = 1")) - - tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_historical_stats = 1") - tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_historical_stats = 0") - tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("0")) - - // test for tidb_enable_column_tracking - tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_column_tracking = 0") - tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("0")) - // When set tidb_enable_column_tracking off, we record the time of the setting operation. - tk.MustQuery("select count(1) from mysql.tidb where variable_name = 'tidb_disable_column_tracking_time' and variable_value is not null").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("1")) - require.Error(t, tk.ExecToErr("select @@session.tidb_enable_column_tracking")) - require.Error(t, tk.ExecToErr("set tidb_enable_column_tracking = 0")) - require.Error(t, tk.ExecToErr("set global tidb_enable_column_tracking = -1")) - - // test for tidb_ignore_prepared_cache_close_stmt - tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("0")) // default value is 0 - tk.MustExec("set global tidb_ignore_prepared_cache_close_stmt=1") - tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("1")) - tk.MustQuery("show global variables like 'tidb_ignore_prepared_cache_close_stmt'").Check(testkit.Rows("tidb_ignore_prepared_cache_close_stmt ON")) - tk.MustExec("set global tidb_ignore_prepared_cache_close_stmt=0") - tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("0")) - tk.MustQuery("show global variables like 'tidb_ignore_prepared_cache_close_stmt'").Check(testkit.Rows("tidb_ignore_prepared_cache_close_stmt OFF")) - - // test for tidb_enable_new_cost_interface - tk.MustQuery("select @@global.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set global tidb_enable_new_cost_interface=0") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1287 'OFF' is deprecated and will be removed in a future release. Please use ON instead")) - tk.MustQuery("select @@global.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) - tk.MustExec("set tidb_enable_new_cost_interface=0") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1287 'OFF' is deprecated and will be removed in a future release. Please use ON instead")) - tk.MustQuery("select @@session.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) - - // test for tidb_remove_orderby_in_subquery - tk.MustQuery("select @@session.tidb_remove_orderby_in_subquery").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set session tidb_remove_orderby_in_subquery=0") - tk.MustQuery("select @@session.tidb_remove_orderby_in_subquery").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.tidb_remove_orderby_in_subquery").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set global tidb_remove_orderby_in_subquery=0") - tk.MustQuery("select @@global.tidb_remove_orderby_in_subquery").Check(testkit.Rows("0")) - - // test for tidb_opt_skew_distinct_agg - tk.MustQuery("select @@session.tidb_opt_skew_distinct_agg").Check(testkit.Rows("0")) // default value is 0 - tk.MustExec("set session tidb_opt_skew_distinct_agg=1") - tk.MustQuery("select @@session.tidb_opt_skew_distinct_agg").Check(testkit.Rows("1")) - tk.MustQuery("select @@global.tidb_opt_skew_distinct_agg").Check(testkit.Rows("0")) // default value is 0 - tk.MustExec("set global tidb_opt_skew_distinct_agg=1") - tk.MustQuery("select @@global.tidb_opt_skew_distinct_agg").Check(testkit.Rows("1")) - - // test for tidb_opt_three_stage_distinct_agg - tk.MustQuery("select @@session.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set session tidb_opt_three_stage_distinct_agg=0") - tk.MustQuery("select @@session.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set global tidb_opt_three_stage_distinct_agg=0") - tk.MustQuery("select @@global.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("0")) - - // the value of max_allowed_packet should be a multiple of 1024 - tk.MustExec("set @@global.max_allowed_packet=16385") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '16385'")) - result := tk.MustQuery("select @@global.max_allowed_packet;") - result.Check(testkit.Rows("16384")) - tk.MustExec("set @@global.max_allowed_packet=2047") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '2047'")) - result = tk.MustQuery("select @@global.max_allowed_packet;") - result.Check(testkit.Rows("1024")) - tk.MustExec("set @@global.max_allowed_packet=0") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '0'")) - result = tk.MustQuery("select @@global.max_allowed_packet;") - result.Check(testkit.Rows("1024")) - - // test value of tidb_stats_cache_mem_quota - tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_stats_cache_mem_quota = 200") - tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("200")) - // assert quota must larger than -1 - tk.MustExec("set global tidb_stats_cache_mem_quota = -1") - tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("0")) - // assert quota muster smaller than 1TB - tk.MustExec("set global tidb_stats_cache_mem_quota = 1099511627777") - tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("1099511627776")) - // for read-only instance scoped system variables. - tk.MustGetErrCode("set @@global.plugin_load = ''", errno.ErrIncorrectGlobalLocalVar) - tk.MustGetErrCode("set @@global.plugin_dir = ''", errno.ErrIncorrectGlobalLocalVar) - - // test for tidb_max_auto_analyze_time - tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBMaxAutoAnalyzeTime))) - tk.MustExec("set global tidb_max_auto_analyze_time = 60") - tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows("60")) - tk.MustExec("set global tidb_max_auto_analyze_time = -1") - tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows("0")) - - // test variables for cost model ver2 - tk.MustQuery("select @@tidb_cost_model_version").Check(testkit.Rows(fmt.Sprintf("%v", variable.DefTiDBCostModelVer))) - tk.MustExec("set tidb_cost_model_version=3") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_cost_model_version value: '3'")) - tk.MustExec("set tidb_cost_model_version=0") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_cost_model_version value: '0'")) - tk.MustExec("set tidb_cost_model_version=2") - tk.MustQuery("select @@tidb_cost_model_version").Check(testkit.Rows("2")) - - tk.MustQuery("select @@tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_enable_analyze_snapshot = 1") - tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("1")) - tk.MustExec("set global tidb_enable_analyze_snapshot = 0") - tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) - tk.MustExec("set session tidb_enable_analyze_snapshot = 1") - tk.MustQuery("select @@session.tidb_enable_analyze_snapshot").Check(testkit.Rows("1")) - tk.MustExec("set session tidb_enable_analyze_snapshot = 0") - tk.MustQuery("select @@session.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) - - // test variables `init_connect' - tk.MustGetErrCode("set global init_connect = '-1'", mysql.ErrWrongTypeForVar) - tk.MustGetErrCode("set global init_connect = 'invalidstring'", mysql.ErrWrongTypeForVar) - tk.MustExec("set global init_connect = 'select now(); select timestamp()'") - - // test variable 'tidb_session_plan_cache_size' - // global scope - tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value - tk.MustExec("set global tidb_session_plan_cache_size = 1") - tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("1")) - // session scope - tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value - tk.MustExec("set session tidb_session_plan_cache_size = 1") - tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("1")) - - // test variable 'foreign_key_checks' - // global scope - tk.MustQuery("select @@global.foreign_key_checks").Check(testkit.Rows("1")) // default value - tk.MustExec("set global foreign_key_checks = 0") - tk.MustQuery("select @@global.foreign_key_checks").Check(testkit.Rows("0")) - // session scope - tk.MustQuery("select @@session.foreign_key_checks").Check(testkit.Rows("1")) // default value - tk.MustExec("set session foreign_key_checks = 0") - tk.MustQuery("select @@session.foreign_key_checks").Check(testkit.Rows("0")) - - // test variable 'tidb_enable_foreign_key' - // global scope - tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("1")) // default value - tk.MustExec("set global tidb_enable_foreign_key = 0") - tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("0")) - - // test variable 'tidb_opt_force_inline_cte' - tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0 - tk.MustExec("set session tidb_opt_force_inline_cte=1") - tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("1")) - tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0 - tk.MustExec("set global tidb_opt_force_inline_cte=1") - tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("1")) - - // test tidb_auto_analyze_partition_batch_size - tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1")) // default value is 1 - tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 2") - tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("2")) - tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 0") - tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1")) // min value is 1 - tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 9999") - tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1024")) // max value is 1024 - - // test variable 'tidb_opt_prefix_index_single_scan' - // global scope - tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) // default value - tk.MustExec("set global tidb_opt_prefix_index_single_scan = 0") - tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_opt_prefix_index_single_scan = 1") - tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) - // session scope - tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) // default value - tk.MustExec("set session tidb_opt_prefix_index_single_scan = 0") - tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("0")) - tk.MustExec("set session tidb_opt_prefix_index_single_scan = 1") - tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) - - // test tidb_opt_range_max_size - tk.MustQuery("select @@tidb_opt_range_max_size").Check(testkit.Rows("67108864")) - tk.MustExec("set global tidb_opt_range_max_size = -1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_opt_range_max_size value: '-1'")) - tk.MustQuery("select @@global.tidb_opt_range_max_size").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_opt_range_max_size = 1048576") - tk.MustQuery("select @@global.tidb_opt_range_max_size").Check(testkit.Rows("1048576")) - tk.MustExec("set session tidb_opt_range_max_size = 2097152") - tk.MustQuery("select @@session.tidb_opt_range_max_size").Check(testkit.Rows("2097152")) - - // test for password validation - tk.MustQuery("SELECT @@GLOBAL.validate_password.enable").Check(testkit.Rows("0")) - tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("8")) - tk.MustExec("SET GLOBAL validate_password.length = 3") - tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("4")) - tk.MustExec("SET GLOBAL validate_password.mixed_case_count = 2") - tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("6")) - - // test tidb_cdc_write_source - require.Equal(t, uint64(0), tk.Session().GetSessionVars().CDCWriteSource) - tk.MustQuery("select @@tidb_cdc_write_source").Check(testkit.Rows("0")) - tk.MustExec("set @@session.tidb_cdc_write_source = 2") - tk.MustQuery("select @@tidb_cdc_write_source").Check(testkit.Rows("2")) - require.Equal(t, uint64(2), tk.Session().GetSessionVars().CDCWriteSource) - tk.MustExec("set @@session.tidb_cdc_write_source = 0") - require.Equal(t, uint64(0), tk.Session().GetSessionVars().CDCWriteSource) - - tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("json,blob,mediumblob,longblob")) - tk.MustExec("set @@session.tidb_analyze_skip_column_types = 'json, text, blob'") - tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("json,text,blob")) - tk.MustExec("set @@session.tidb_analyze_skip_column_types = ''") - tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("")) - tk.MustGetErrMsg("set @@session.tidb_analyze_skip_column_types = 'int,json'", "[variable:1231]Variable 'tidb_analyze_skip_column_types' can't be set to the value of 'int,json'") - - tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("json,blob,mediumblob,longblob")) - tk.MustExec("set @@global.tidb_analyze_skip_column_types = 'json, text, blob'") - tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("json,text,blob")) - tk.MustExec("set @@global.tidb_analyze_skip_column_types = ''") - tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("")) - tk.MustGetErrMsg("set @@global.tidb_analyze_skip_column_types = 'int,json'", "[variable:1231]Variable 'tidb_analyze_skip_column_types' can't be set to the value of 'int,json'") - - // test tidb_skip_missing_partition_stats - // global scope - tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) // default value - tk.MustExec("set global tidb_skip_missing_partition_stats = 0") - tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_skip_missing_partition_stats = 1") - tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) - // session scope - tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) // default value - tk.MustExec("set session tidb_skip_missing_partition_stats = 0") - tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("0")) - tk.MustExec("set session tidb_skip_missing_partition_stats = 1") - tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) - - // test tidb_schema_version_cache_limit - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("16")) - tk.MustExec("set @@global.tidb_schema_version_cache_limit=64;") - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("64")) - tk.MustExec("set @@global.tidb_schema_version_cache_limit=2;") - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) - tk.MustExec("set @@global.tidb_schema_version_cache_limit=256;") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_schema_version_cache_limit value: '256'")) - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("255")) - tk.MustExec("set @@global.tidb_schema_version_cache_limit=0;") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_schema_version_cache_limit value: '0'")) - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) - tk.MustGetErrMsg("set @@global.tidb_schema_version_cache_limit='x';", "[variable:1232]Incorrect argument type to variable 'tidb_schema_version_cache_limit'") - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) - tk.MustExec("set @@global.tidb_schema_version_cache_limit=64;") - tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("64")) -} - -func TestGetSetNoopVars(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // By default you can get/set noop sysvars without issue. - tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("OFF")) - tk.MustQuery("SHOW VARIABLES LIKE 'query_cache_type'").Check(testkit.Rows("query_cache_type OFF")) - tk.MustExec("SET query_cache_type=2") - tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("DEMAND")) - // When tidb_enable_noop_variables is OFF, you can GET in @@ context - // and always SET. But you can't see in SHOW VARIABLES. - // Warnings are also returned. - tk.MustExec("SET GLOBAL tidb_enable_noop_variables = OFF") - defer tk.MustExec("SET GLOBAL tidb_enable_noop_variables = ON") - tk.MustQuery("SELECT @@global.tidb_enable_noop_variables").Check(testkit.Rows("OFF")) - tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("DEMAND")) - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 8145 variable query_cache_type has no effect in TiDB")) - tk.MustQuery("SHOW VARIABLES LIKE 'query_cache_type'").Check(testkit.Rows()) - tk.MustExec("SET query_cache_type = OFF") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 8144 setting query_cache_type has no effect in TiDB")) - // but the change is still effective. - tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("OFF")) - - // Only ON and OFF supported - err := tk.ExecToErr("SET GLOBAL tidb_enable_noop_variables = 2") - require.Error(t, err) - require.Equal(t, "[variable:1231]Variable 'tidb_enable_noop_variables' can't be set to the value of '2'", err.Error()) - - err = tk.ExecToErr("SET GLOBAL tidb_enable_noop_variables = 'warn'") - require.Error(t, err) - require.Equal(t, "[variable:1231]Variable 'tidb_enable_noop_variables' can't be set to the value of 'warn'", err.Error()) -} - -func TestTruncateIncorrectIntSessionVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - testCases := []struct { - sessionVarName string - minValue int - maxValue int - }{ - {"auto_increment_increment", 1, 65535}, - {"auto_increment_offset", 1, 65535}, - } - - for _, tc := range testCases { - name := tc.sessionVarName - selectSQL := fmt.Sprintf("select @@%s;", name) - validValue := tc.minValue + (tc.maxValue-tc.minValue)/2 - tk.MustExec(fmt.Sprintf("set @@%s = %d", name, validValue)) - tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", validValue))) - - tk.MustExec(fmt.Sprintf("set @@%s = %d", name, tc.minValue-1)) - warnMsg := fmt.Sprintf("Warning 1292 Truncated incorrect %s value: '%d'", name, tc.minValue-1) - tk.MustQuery("show warnings").Check(testkit.Rows(warnMsg)) - tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", tc.minValue))) - - tk.MustExec(fmt.Sprintf("set @@%s = %d", name, tc.maxValue+1)) - warnMsg = fmt.Sprintf("Warning 1292 Truncated incorrect %s value: '%d'", name, tc.maxValue+1) - tk.MustQuery("show warnings").Check(testkit.Rows(warnMsg)) - tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", tc.maxValue))) - } -} - -func TestSetCharset(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - sessionVars := tk.Session().GetSessionVars() - - var characterSetVariables = []string{ - "character_set_client", - "character_set_connection", - "character_set_results", - "character_set_server", - "character_set_database", - "character_set_system", - "character_set_filesystem", - } - - check := func(args ...string) { - for i, v := range characterSetVariables { - sVar, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), v) - require.NoError(t, err) - require.Equal(t, args[i], sVar, fmt.Sprintf("%d: %s", i, characterSetVariables[i])) - } - } - - check( - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - tk.MustExec(`SET NAMES latin1`) - check( - "latin1", - "latin1", - "latin1", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - tk.MustExec(`SET NAMES default`) - check( - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - // Issue #1523 - tk.MustExec(`SET NAMES binary`) - check( - "binary", - "binary", - "binary", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - tk.MustExec(`SET NAMES utf8`) - check( - "utf8", - "utf8", - "utf8", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - tk.MustExec(`SET CHARACTER SET latin1`) - check( - "latin1", - "utf8mb4", - "latin1", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) - - tk.MustExec(`SET CHARACTER SET default`) - check( - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8mb4", - "utf8", - "binary", - ) -} - -func TestSetCollationAndCharset(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - ctx := tk.Session().(sessionctx.Context) - sessionVars := ctx.GetSessionVars() - - cases := []struct { - charset string - collation string - expectCharset string - expectCollation string - }{ - {variable.CharacterSetConnection, variable.CollationConnection, "utf8", "utf8_bin"}, - {variable.CharsetDatabase, variable.CollationDatabase, "utf8", "utf8_bin"}, - {variable.CharacterSetServer, variable.CollationServer, "utf8", "utf8_bin"}, - } - - for _, c := range cases { - tk.MustExec(fmt.Sprintf("set %s = %s;", c.charset, c.expectCharset)) - sVar, ok := sessionVars.GetSystemVar(c.charset) - require.True(t, ok) - require.Equal(t, c.expectCharset, sVar) - sVar, ok = sessionVars.GetSystemVar(c.collation) - require.True(t, ok) - require.Equal(t, c.expectCollation, sVar) - } - - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - ctx = tk.Session().(sessionctx.Context) - sessionVars = ctx.GetSessionVars() - - for _, c := range cases { - tk.MustExec(fmt.Sprintf("set %s = %s;", c.collation, c.expectCollation)) - sVar, ok := sessionVars.GetSystemVar(c.charset) - require.True(t, ok) - require.Equal(t, c.expectCharset, sVar) - sVar, ok = sessionVars.GetSystemVar(c.collation) - require.True(t, ok) - require.Equal(t, c.expectCollation, sVar) - } -} - -func TestValidateSetVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - err := tk.ExecToErr("set global tidb_distsql_scan_concurrency='fff';") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set global tidb_distsql_scan_concurrency=-2;") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_distsql_scan_concurrency value: '-2'")) - - err = tk.ExecToErr("set @@tidb_distsql_scan_concurrency='fff';") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@tidb_distsql_scan_concurrency=-2;") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_distsql_scan_concurrency value: '-2'")) - - err = tk.ExecToErr("set @@tidb_batch_delete='ok';") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@tidb_batch_delete='On';") - tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("1")) - tk.MustExec("set @@tidb_batch_delete='oFf';") - tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("0")) - tk.MustExec("set @@tidb_batch_delete=1;") - tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("1")) - tk.MustExec("set @@tidb_batch_delete=0;") - tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("0")) - - tk.MustExec("set @@tidb_opt_agg_push_down=off;") - tk.MustQuery("select @@tidb_opt_agg_push_down;").Check(testkit.Rows("0")) - - tk.MustExec("set @@tidb_constraint_check_in_place=on;") - tk.MustQuery("select @@tidb_constraint_check_in_place;").Check(testkit.Rows("1")) - - tk.MustExec("set @@tidb_general_log=0;") - tk.MustQuery(`show warnings`).Check(testkit.Rows(fmt.Sprintf("Warning %d modifying tidb_general_log will require SET GLOBAL in a future version of TiDB", errno.ErrInstanceScope))) - tk.MustQuery("select @@tidb_general_log;").Check(testkit.Rows("0")) - - tk.MustExec("set @@tidb_pprof_sql_cpu=1;") - tk.MustQuery("select @@tidb_pprof_sql_cpu;").Check(testkit.Rows("1")) - tk.MustExec("set @@tidb_pprof_sql_cpu=0;") - tk.MustQuery("select @@tidb_pprof_sql_cpu;").Check(testkit.Rows("0")) - - err = tk.ExecToErr("set @@tidb_batch_delete=3;") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@group_concat_max_len=1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect group_concat_max_len value: '1'")) - result := tk.MustQuery("select @@group_concat_max_len;") - result.Check(testkit.Rows("4")) - - err = tk.ExecToErr("set @@group_concat_max_len = 18446744073709551616") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) - - // Test illegal type - err = tk.ExecToErr("set @@group_concat_max_len='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@default_week_format=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect default_week_format value: '-1'")) - result = tk.MustQuery("select @@default_week_format;") - result.Check(testkit.Rows("0")) - - tk.MustExec("set @@default_week_format=9") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect default_week_format value: '9'")) - result = tk.MustQuery("select @@default_week_format;") - result.Check(testkit.Rows("7")) - - err = tk.ExecToErr("set @@error_count = 0") - require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err %v", err)) - - err = tk.ExecToErr("set @@warning_count = 0") - require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err %v", err)) - - tk.MustExec("set time_zone='SySTeM'") - result = tk.MustQuery("select @@time_zone;") - result.Check(testkit.Rows("SYSTEM")) - - // The following cases test value out of range and illegal type when setting system variables. - // See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html for more details. - tk.MustExec("set @@global.max_connections=100001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '100001'")) - result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("100000")) - - // "max_connections == 0" means there is no limitation on the number of connections. - tk.MustExec("set @@global.max_connections=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) - result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("0")) - - err = tk.ExecToErr("set @@global.max_connections='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@global.thread_pool_size=65") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect thread_pool_size value: '65'")) - result = tk.MustQuery("select @@global.thread_pool_size;") - result.Check(testkit.Rows("64")) - - tk.MustExec("set @@global.thread_pool_size=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect thread_pool_size value: '-1'")) - result = tk.MustQuery("select @@global.thread_pool_size;") - result.Check(testkit.Rows("1")) - - err = tk.ExecToErr("set @@global.thread_pool_size='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@global.max_allowed_packet=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '-1'")) - result = tk.MustQuery("select @@global.max_allowed_packet;") - result.Check(testkit.Rows("1024")) - - err = tk.ExecToErr("set @@global.max_allowed_packet='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - err = tk.ExecToErr("set @@max_allowed_packet=default") - require.True(t, terror.ErrorEqual(err, variable.ErrReadOnly)) - - tk.MustExec("set @@global.max_connect_errors=18446744073709551615") - - tk.MustExec("set @@global.max_connect_errors=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connect_errors value: '-1'")) - result = tk.MustQuery("select @@global.max_connect_errors;") - result.Check(testkit.Rows("1")) - - err = tk.ExecToErr("set @@global.max_connect_errors=18446744073709551616") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@global.max_connections=100001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '100001'")) - result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("100000")) - - tk.MustExec("set @@global.max_connections=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) - result = tk.MustQuery("select @@global.max_connections;") - result.Check(testkit.Rows("0")) - - err = tk.ExecToErr("set @@global.max_connections='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@max_sort_length=1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '1'")) - result = tk.MustQuery("select @@max_sort_length;") - result.Check(testkit.Rows("4")) - - tk.MustExec("set @@max_sort_length=-100") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '-100'")) - result = tk.MustQuery("select @@max_sort_length;") - result.Check(testkit.Rows("4")) - - tk.MustExec("set @@max_sort_length=8388609") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '8388609'")) - result = tk.MustQuery("select @@max_sort_length;") - result.Check(testkit.Rows("8388608")) - - err = tk.ExecToErr("set @@max_sort_length='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@global.table_definition_cache=399") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '399'")) - result = tk.MustQuery("select @@global.table_definition_cache;") - result.Check(testkit.Rows("400")) - - tk.MustExec("set @@global.table_definition_cache=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '-1'")) - result = tk.MustQuery("select @@global.table_definition_cache;") - result.Check(testkit.Rows("400")) - - tk.MustExec("set @@global.table_definition_cache=524289") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '524289'")) - result = tk.MustQuery("select @@global.table_definition_cache;") - result.Check(testkit.Rows("524288")) - - err = tk.ExecToErr("set @@global.table_definition_cache='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@old_passwords=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect old_passwords value: '-1'")) - result = tk.MustQuery("select @@old_passwords;") - result.Check(testkit.Rows("0")) - - tk.MustExec("set @@old_passwords=3") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect old_passwords value: '3'")) - result = tk.MustQuery("select @@old_passwords;") - result.Check(testkit.Rows("2")) - - err = tk.ExecToErr("set @@old_passwords='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@tmp_table_size=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tmp_table_size value: '-1'")) - result = tk.MustQuery("select @@tmp_table_size;") - result.Check(testkit.Rows("1024")) - - tk.MustExec("set @@tmp_table_size=1020") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tmp_table_size value: '1020'")) - result = tk.MustQuery("select @@tmp_table_size;") - result.Check(testkit.Rows("1024")) - - tk.MustExec("set @@tmp_table_size=167772161") - result = tk.MustQuery("select @@tmp_table_size;") - result.Check(testkit.Rows("167772161")) - - tk.MustExec("set @@tmp_table_size=18446744073709551615") - result = tk.MustQuery("select @@tmp_table_size;") - result.Check(testkit.Rows("18446744073709551615")) - - err = tk.ExecToErr("set @@tmp_table_size=18446744073709551616") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - err = tk.ExecToErr("set @@tmp_table_size='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@tidb_tmp_table_max_size=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '-1'")) - result = tk.MustQuery("select @@tidb_tmp_table_max_size;") - result.Check(testkit.Rows("1048576")) - - tk.MustExec("set @@tidb_tmp_table_max_size=1048575") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '1048575'")) - result = tk.MustQuery("select @@tidb_tmp_table_max_size;") - result.Check(testkit.Rows("1048576")) - - tk.MustExec("set @@tidb_tmp_table_max_size=167772161") - result = tk.MustQuery("select @@tidb_tmp_table_max_size;") - result.Check(testkit.Rows("167772161")) - - tk.MustExec("set @@tidb_tmp_table_max_size=137438953472") - result = tk.MustQuery("select @@tidb_tmp_table_max_size;") - result.Check(testkit.Rows("137438953472")) - - tk.MustExec("set @@tidb_tmp_table_max_size=137438953473") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '137438953473'")) - result = tk.MustQuery("select @@tidb_tmp_table_max_size;") - result.Check(testkit.Rows("137438953472")) - - err = tk.ExecToErr("set @@tidb_tmp_table_max_size='hello'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) - - tk.MustExec("set @@global.connect_timeout=1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect connect_timeout value: '1'")) - result = tk.MustQuery("select @@global.connect_timeout;") - result.Check(testkit.Rows("2")) - - tk.MustExec("set @@global.connect_timeout=31536000") - result = tk.MustQuery("select @@global.connect_timeout;") - result.Check(testkit.Rows("31536000")) - - tk.MustExec("set @@global.connect_timeout=31536001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect connect_timeout value: '31536001'")) - result = tk.MustQuery("select @@global.connect_timeout;") - result.Check(testkit.Rows("31536000")) - - result = tk.MustQuery("select @@sql_select_limit;") - result.Check(testkit.Rows("18446744073709551615")) - tk.MustExec("set @@sql_select_limit=default") - result = tk.MustQuery("select @@sql_select_limit;") - result.Check(testkit.Rows("18446744073709551615")) - - tk.MustExec("set @@sql_auto_is_null=00") - result = tk.MustQuery("select @@sql_auto_is_null;") - result.Check(testkit.Rows("0")) - - tk.MustExec("set @@sql_warnings=001") - result = tk.MustQuery("select @@sql_warnings;") - result.Check(testkit.Rows("1")) - - tk.MustExec("set @@sql_warnings=000") - result = tk.MustQuery("select @@sql_warnings;") - result.Check(testkit.Rows("0")) - - tk.MustExec("set @@global.super_read_only=-0") - result = tk.MustQuery("select @@global.super_read_only;") - result.Check(testkit.Rows("0")) - - err = tk.ExecToErr("set @@global.super_read_only=-1") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@global.innodb_status_output_locks=-1") - result = tk.MustQuery("select @@global.innodb_status_output_locks;") - result.Check(testkit.Rows("1")) - - tk.MustExec("set @@global.innodb_ft_enable_stopword=0000000") - result = tk.MustQuery("select @@global.innodb_ft_enable_stopword;") - result.Check(testkit.Rows("0")) - - tk.MustExec("set @@global.innodb_stats_on_metadata=1") - result = tk.MustQuery("select @@global.innodb_stats_on_metadata;") - result.Check(testkit.Rows("1")) - - tk.MustExec("set @@global.innodb_file_per_table=-50") - result = tk.MustQuery("select @@global.innodb_file_per_table;") - result.Check(testkit.Rows("1")) - - err = tk.ExecToErr("set @@global.innodb_ft_enable_stopword=2") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@query_cache_type=0") - result = tk.MustQuery("select @@query_cache_type;") - result.Check(testkit.Rows("OFF")) - - tk.MustExec("set @@query_cache_type=2") - result = tk.MustQuery("select @@query_cache_type;") - result.Check(testkit.Rows("DEMAND")) - - tk.MustExec("set @@global.sync_binlog=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect sync_binlog value: '-1'")) - - tk.MustExec("set @@global.sync_binlog=4294967299") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect sync_binlog value: '4294967299'")) - - tk.MustExec("set @@global.flush_time=31536001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect flush_time value: '31536001'")) - - tk.MustExec("set @@global.interactive_timeout=31536001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect interactive_timeout value: '31536001'")) - - tk.MustExec("set @@global.innodb_commit_concurrency = -1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_commit_concurrency value: '-1'")) - - tk.MustExec("set @@global.innodb_commit_concurrency = 1001") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_commit_concurrency value: '1001'")) - - tk.MustExec("set @@global.innodb_fast_shutdown = -1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_fast_shutdown value: '-1'")) - - tk.MustExec("set @@global.innodb_fast_shutdown = 3") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_fast_shutdown value: '3'")) - - tk.MustExec("set @@global.innodb_lock_wait_timeout = 0") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '0'")) - - tk.MustExec("set @@global.innodb_lock_wait_timeout = 1073741825") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '1073741825'")) - - tk.MustExec("set @@innodb_lock_wait_timeout = 0") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '0'")) - - tk.MustExec("set @@innodb_lock_wait_timeout = 1073741825") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '1073741825'")) - - tk.MustExec("set @@global.validate_password.number_count=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect validate_password.number_count value: '-1'")) - - tk.MustExec("set @@global.validate_password.length=-1") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect validate_password.length value: '-1'")) - - err = tk.ExecToErr("set @@tx_isolation=''") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - err = tk.ExecToErr("set global tx_isolation=''") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - err = tk.ExecToErr("set @@transaction_isolation=''") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - err = tk.ExecToErr("set global transaction_isolation=''") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - err = tk.ExecToErr("set global tx_isolation='REPEATABLE-READ1'") - require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) - - tk.MustExec("set @@tx_isolation='READ-COMMITTED'") - result = tk.MustQuery("select @@tx_isolation;") - result.Check(testkit.Rows("READ-COMMITTED")) - - tk.MustExec("set @@tx_isolation='read-COMMITTED'") - result = tk.MustQuery("select @@tx_isolation;") - result.Check(testkit.Rows("READ-COMMITTED")) - - tk.MustExec("set @@tx_isolation='REPEATABLE-READ'") - result = tk.MustQuery("select @@tx_isolation;") - result.Check(testkit.Rows("REPEATABLE-READ")) - - tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") - tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") - err = tk.ExecToErr("set @@tx_isolation='SERIALIZABLE'") - require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) - - tk.MustExec("set global allow_auto_random_explicit_insert=on;") - tk.MustQuery("select @@global.allow_auto_random_explicit_insert;").Check(testkit.Rows("1")) -} - -func TestSelectGlobalVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("0")) - tk.MustQuery("select @@max_connections;").Check(testkit.Rows("0")) - - tk.MustExec("set @@global.max_connections=100;") - - tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("100")) - tk.MustQuery("select @@max_connections;").Check(testkit.Rows("100")) - - tk.MustExec("set @@global.max_connections=0;") - - // test for unknown variable. - err := tk.ExecToErr("select @@invalid") - require.True(t, terror.ErrorEqual(err, variable.ErrUnknownSystemVar), fmt.Sprintf("err %v", err)) - err = tk.ExecToErr("select @@global.invalid") - require.True(t, terror.ErrorEqual(err, variable.ErrUnknownSystemVar), fmt.Sprintf("err %v", err)) -} - -func TestSetConcurrency(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test default value - tk.MustQuery("select @@tidb_executor_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefExecutorConcurrency))) - - tk.MustQuery("select @@tidb_index_lookup_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_index_lookup_join_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_hash_join_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_hashagg_partial_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_hashagg_final_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_window_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_streamagg_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBStreamAggConcurrency))) - tk.MustQuery("select @@tidb_projection_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) - tk.MustQuery("select @@tidb_distsql_scan_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefDistSQLScanConcurrency))) - - tk.MustQuery("select @@tidb_index_serial_scan_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefIndexSerialScanConcurrency))) - - vars := tk.Session().GetSessionVars() - require.Equal(t, variable.DefExecutorConcurrency, vars.ExecutorConcurrency) - require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupJoinConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashJoinConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggPartialConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggFinalConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.WindowConcurrency()) - require.Equal(t, variable.DefTiDBStreamAggConcurrency, vars.StreamAggConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.ProjectionConcurrency()) - require.Equal(t, variable.DefDistSQLScanConcurrency, vars.DistSQLScanConcurrency()) - - require.Equal(t, variable.DefIndexSerialScanConcurrency, vars.IndexSerialScanConcurrency()) - - // test setting deprecated variables - warnTpl := "Warning 1287 '%s' is deprecated and will be removed in a future release. Please use tidb_executor_concurrency instead" - - checkSet := func(v string) { - tk.MustExec(fmt.Sprintf("set @@%s=1;", v)) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", fmt.Sprintf(warnTpl, v))) - tk.MustQuery(fmt.Sprintf("select @@%s;", v)).Check(testkit.Rows("1")) - } - - checkSet(variable.TiDBIndexLookupConcurrency) - require.Equal(t, 1, vars.IndexLookupConcurrency()) - - checkSet(variable.TiDBIndexLookupJoinConcurrency) - require.Equal(t, 1, vars.IndexLookupJoinConcurrency()) - - checkSet(variable.TiDBHashJoinConcurrency) - require.Equal(t, 1, vars.HashJoinConcurrency()) - - checkSet(variable.TiDBHashAggPartialConcurrency) - require.Equal(t, 1, vars.HashAggPartialConcurrency()) - - checkSet(variable.TiDBHashAggFinalConcurrency) - require.Equal(t, 1, vars.HashAggFinalConcurrency()) - - checkSet(variable.TiDBProjectionConcurrency) - require.Equal(t, 1, vars.ProjectionConcurrency()) - - checkSet(variable.TiDBWindowConcurrency) - require.Equal(t, 1, vars.WindowConcurrency()) - - checkSet(variable.TiDBStreamAggConcurrency) - require.Equal(t, 1, vars.StreamAggConcurrency()) - - tk.MustExec(fmt.Sprintf("set @@%s=1;", variable.TiDBDistSQLScanConcurrency)) - tk.MustQuery(fmt.Sprintf("select @@%s;", variable.TiDBDistSQLScanConcurrency)).Check(testkit.Rows("1")) - require.Equal(t, 1, vars.DistSQLScanConcurrency()) - - tk.MustExec("set @@tidb_index_serial_scan_concurrency=4") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustQuery("select @@tidb_index_serial_scan_concurrency;").Check(testkit.Rows("4")) - require.Equal(t, 4, vars.IndexSerialScanConcurrency()) - - // test setting deprecated value unset - tk.MustExec("set @@tidb_index_lookup_concurrency=-1;") - tk.MustExec("set @@tidb_index_lookup_join_concurrency=-1;") - tk.MustExec("set @@tidb_hash_join_concurrency=-1;") - tk.MustExec("set @@tidb_hashagg_partial_concurrency=-1;") - tk.MustExec("set @@tidb_hashagg_final_concurrency=-1;") - tk.MustExec("set @@tidb_window_concurrency=-1;") - tk.MustExec("set @@tidb_streamagg_concurrency=-1;") - tk.MustExec("set @@tidb_projection_concurrency=-1;") - - require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupJoinConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashJoinConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggPartialConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggFinalConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.WindowConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.StreamAggConcurrency()) - require.Equal(t, variable.DefExecutorConcurrency, vars.ProjectionConcurrency()) - - tk.MustExec("set @@tidb_executor_concurrency=-1;") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_executor_concurrency value: '-1'")) - require.Equal(t, 1, vars.ExecutorConcurrency) -} - -func TestEnableNoopFunctionsVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - defer func() { - // Ensure global settings are reset. - tk.MustExec("SET GLOBAL tx_read_only = 0") - tk.MustExec("SET GLOBAL transaction_read_only = 0") - tk.MustExec("SET GLOBAL read_only = 0") - tk.MustExec("SET GLOBAL super_read_only = 0") - tk.MustExec("SET GLOBAL offline_mode = 0") - tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") - }() - - // test for tidb_enable_noop_functions - tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - - // change session var to 1 - tk.MustExec(`set tidb_enable_noop_functions=1;`) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("ON")) - tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - - // restore to 0 - tk.MustExec(`set tidb_enable_noop_functions=0;`) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - - // set test - require.Error(t, tk.ExecToErr(`set tidb_enable_noop_functions='abc'`)) - require.Error(t, tk.ExecToErr(`set tidb_enable_noop_functions=11`)) - tk.MustExec(`set tidb_enable_noop_functions="off";`) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - tk.MustExec(`set tidb_enable_noop_functions="on";`) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("ON")) - tk.MustExec(`set tidb_enable_noop_functions=0;`) - tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) - - err := tk.ExecToErr("SET SESSION tx_read_only = 1") - require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) - - tk.MustExec("SET SESSION tx_read_only = 0") - tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("0")) - tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("0")) - - err = tk.ExecToErr("SET GLOBAL tx_read_only = 1") // should fail. - require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) - tk.MustExec("SET GLOBAL tx_read_only = 0") - tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("0")) - - err = tk.ExecToErr("SET SESSION transaction_read_only = 1") - require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) - tk.MustExec("SET SESSION transaction_read_only = 0") - tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("0")) - tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("0")) - - // works on SESSION because SESSION tidb_enable_noop_functions=1 - tk.MustExec("SET tidb_enable_noop_functions = 1") - tk.MustExec("SET SESSION transaction_read_only = 1") - tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("1")) - tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("1")) - - // fails on GLOBAL because GLOBAL.tidb_enable_noop_functions still=0 - err = tk.ExecToErr("SET GLOBAL transaction_read_only = 1") - require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) - tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 1") - // now works - tk.MustExec("SET GLOBAL transaction_read_only = 1") - tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("1")) - tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL transaction_read_only = 0") - tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("0")) - - require.Error(t, tk.ExecToErr("SET tidb_enable_noop_functions = 0")) // fails because transaction_read_only/tx_read_only = 1 - - tk.MustExec("SET transaction_read_only = 0") - tk.MustExec("SET tidb_enable_noop_functions = 0") // now works. - - // setting session doesn't change global, which succeeds because global.transaction_read_only/tx_read_only = 0 - tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") - - // but if global.transaction_read_only=1, it would fail - tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 1") - tk.MustExec("SET GLOBAL transaction_read_only = 1") - // fails - require.Error(t, tk.ExecToErr("SET GLOBAL tidb_enable_noop_functions = 0")) - - // reset for rest of tests. - tk.MustExec("SET GLOBAL transaction_read_only = 0") - tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") - - tk.MustExec("set global read_only = 0") - tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("0")) - tk.MustExec("set global read_only = off") - tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("0")) - tk.MustExec("SET global tidb_enable_noop_functions = 1") - tk.MustExec("set global read_only = 1") - tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("1")) - tk.MustExec("set global read_only = on") - tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("1")) - require.Error(t, tk.ExecToErr("set global read_only = abc")) -} - -// https://github.com/pingcap/tidb/issues/29670 -func TestDefaultBehavior(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("InnoDB")) - tk.MustExec("SET GLOBAL default_storage_engine = 'somethingweird'") - tk.MustExec("SET default_storage_engine = 'MyISAM'") - tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("MyISAM")) - tk.MustExec("SET default_storage_engine = DEFAULT") // reads from global value - tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird")) - tk.MustExec("SET @@SESSION.default_storage_engine = @@GLOBAL.default_storage_engine") // example from MySQL manual - tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird")) - tk.MustExec("SET GLOBAL default_storage_engine = 'somethingweird2'") - tk.MustExec("SET default_storage_engine = @@GLOBAL.default_storage_engine") // variation of example - tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird2")) - tk.MustExec("SET default_storage_engine = DEFAULT") // restore default again for session global - tk.MustExec("SET GLOBAL default_storage_engine = DEFAULT") // restore default for global - tk.MustQuery("SELECT @@SESSION.default_storage_engine, @@GLOBAL.default_storage_engine").Check(testkit.Rows("somethingweird2 InnoDB")) - - // Try sql_mode option which has validation - err := tk.ExecToErr("SET GLOBAL sql_mode = 'DEFAULT'") // illegal now - require.EqualError(t, err, `ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of 'DEFAULT'`) - tk.MustExec("SET GLOBAL sql_mode = DEFAULT") -} - -func TestTiDBReadOnly(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // turn on tidb_restricted_read_only should turn on tidb_super_read_only - tk.MustExec("SET GLOBAL tidb_restricted_read_only = ON") - tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("1")) - - // can't turn off tidb_super_read_only if tidb_restricted_read_only is on - err := tk.ExecToErr("SET GLOBAL tidb_super_read_only = OFF") - require.Error(t, err) - require.Equal(t, "can't turn off tidb_super_read_only when tidb_restricted_read_only is on", err.Error()) - - // turn off tidb_restricted_read_only won't affect tidb_super_read_only - tk.MustExec("SET GLOBAL tidb_restricted_read_only = OFF") - tk.MustQuery("SELECT @@GLOBAL.tidb_restricted_read_only").Check(testkit.Rows("0")) - tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("1")) - - // it is ok to turn off tidb_super_read_only now - tk.MustExec("SET GLOBAL tidb_super_read_only = OFF") - tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("0")) -} - -func TestRemovedSysVars(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test for tidb_enable_noop_functions - // In SET context, it just noops: - tk.MustExec(`SET tidb_enable_global_temporary_table = 1`) - tk.MustExec(`SET tidb_slow_log_masking = 1`) - tk.MustExec(`SET GLOBAL tidb_enable_global_temporary_table = 1`) - tk.MustExec(`SET GLOBAL tidb_slow_log_masking = 1`) - - // In SELECT context it returns a specifc error - // (to avoid presenting dummy data) - tk.MustGetErrCode("SELECT @@tidb_slow_log_masking", errno.ErrVariableNoLongerSupported) - tk.MustGetErrCode("SELECT @@tidb_enable_global_temporary_table", errno.ErrVariableNoLongerSupported) -} - -func TestSetClusterConfig(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - serversInfo := []infoschema.ServerInfo{ - {ServerType: "tidb", Address: "127.0.0.1:1111", StatusAddr: "127.0.0.1:1111"}, - {ServerType: "tidb", Address: "127.0.0.1:2222", StatusAddr: "127.0.0.1:2222"}, - {ServerType: "pd", Address: "127.0.0.1:3333", StatusAddr: "127.0.0.1:3333"}, - {ServerType: "pd", Address: "127.0.0.1:4444", StatusAddr: "127.0.0.1:4444"}, - {ServerType: "tikv", Address: "127.0.0.1:5555", StatusAddr: "127.0.0.1:5555"}, - {ServerType: "tikv", Address: "127.0.0.1:6666", StatusAddr: "127.0.0.1:6666"}, - {ServerType: "tiflash", Address: "127.0.0.1:3933", StatusAddr: "127.0.0.1:7777"}, - } - var serverInfoErr error - serverInfoFunc := func(sessionctx.Context) ([]infoschema.ServerInfo, error) { - return serversInfo, serverInfoErr - } - tk.Session().SetValue(executor.TestSetConfigServerInfoKey, serverInfoFunc) - - require.EqualError(t, tk.ExecToErr("set config xxx log.level='info'"), "unknown type xxx") - require.EqualError(t, tk.ExecToErr("set config tidb log.level='info'"), "TiDB doesn't support to change configs online, please use SQL variables") - require.EqualError(t, tk.ExecToErr("set config '127.0.0.1:1111' log.level='info'"), "TiDB doesn't support to change configs online, please use SQL variables") - require.EqualError(t, tk.ExecToErr("set config '127.a.b.c:1234' log.level='info'"), "invalid instance 127.a.b.c:1234") // name doesn't resolve. - require.EqualError(t, tk.ExecToErr("set config 'example.com:1111' log.level='info'"), "instance example.com:1111 is not found in this cluster") // name resolves. - require.EqualError(t, tk.ExecToErr("set config tikv log.level=null"), "can't set config to null") - require.EqualError(t, tk.ExecToErr("set config '1.1.1.1:1111' log.level='info'"), "instance 1.1.1.1:1111 is not found in this cluster") - require.EqualError(t, tk.ExecToErr("set config tikv `raftstore.max-peer-down-duration`=DEFAULT"), "Unknown DEFAULT for SET CONFIG") - require.ErrorContains(t, tk.ExecToErr("set config tiflash `server.snap-max-write-bytes-per-sec`='500MB'"), "This command can only change config items begin with 'raftstore-proxy'") - - httpCnt := 0 - tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { - httpCnt++ - return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil - }) - tk.MustExec("set config tikv log.level='info'") - require.Equal(t, 2, httpCnt) - - httpCnt = 0 - tk.MustExec("set config '127.0.0.1:5555' log.level='info'") - require.Equal(t, 1, httpCnt) - - httpCnt = 0 - tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(req *http.Request) (*http.Response, error) { - httpCnt++ - body, err := io.ReadAll(req.Body) - require.NoError(t, err) - // The `raftstore.` prefix is stripped. - require.JSONEq(t, `{"server.snap-max-write-bytes-per-sec":"500MB"}`, string(body)) - return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil - }) - tk.MustExec("set config tiflash `raftstore-proxy.server.snap-max-write-bytes-per-sec`='500MB'") - require.Equal(t, 1, httpCnt) - - httpCnt = 0 - tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { - return nil, errors.New("something wrong") - }) - tk.MustExec("set config tikv log.level='info'") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1105 something wrong", "Warning 1105 something wrong")) - - tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { - return &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewBufferString("WRONG"))}, nil - }) - tk.MustExec("set config tikv log.level='info'") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1105 bad request to http://127.0.0.1:5555/config: WRONG", "Warning 1105 bad request to http://127.0.0.1:6666/config: WRONG")) -} - -func TestSetClusterConfigJSONData(t *testing.T) { - var d types.MyDecimal - require.NoError(t, d.FromFloat64(123.456)) - tyBool := types.NewFieldType(mysql.TypeTiny) - tyBool.AddFlag(mysql.IsBooleanFlag) - cases := []struct { - val expression.Expression - result string - succ bool - }{ - {&expression.Constant{Value: types.NewIntDatum(1), RetType: tyBool}, `{"k":true}`, true}, - {&expression.Constant{Value: types.NewIntDatum(0), RetType: tyBool}, `{"k":false}`, true}, - {&expression.Constant{Value: types.NewIntDatum(2333), RetType: types.NewFieldType(mysql.TypeLong)}, `{"k":2333}`, true}, - {&expression.Constant{Value: types.NewFloat64Datum(23.33), RetType: types.NewFieldType(mysql.TypeDouble)}, `{"k":23.33}`, true}, - {&expression.Constant{Value: types.NewStringDatum("abcd"), RetType: types.NewFieldType(mysql.TypeString)}, `{"k":"abcd"}`, true}, - {&expression.Constant{Value: types.NewDecimalDatum(&d), RetType: types.NewFieldType(mysql.TypeNewDecimal)}, `{"k":123.456}`, true}, - {&expression.Constant{Value: types.NewDatum(nil), RetType: types.NewFieldType(mysql.TypeLonglong)}, "", false}, - {&expression.Constant{RetType: types.NewFieldType(mysql.TypeJSON)}, "", false}, // unsupported type - {nil, "", false}, - {&expression.Constant{Value: types.NewDatum(`["no","no","lz4","lz4","lz4","zstd","zstd"]`), RetType: types.NewFieldType(mysql.TypeString)}, `{"k":"[\"no\",\"no\",\"lz4\",\"lz4\",\"lz4\",\"zstd\",\"zstd\"]"}`, true}, - } - - ctx := mock.NewContext() - for _, c := range cases { - result, err := executor.ConvertConfigItem2JSON(ctx, "k", c.val) - if c.succ { - require.Equal(t, result, c.result) - } else { - require.Error(t, err) - } - } -} - -func TestSetTopSQLVariables(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop")) - }() - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_top_sql='On';") - tk.MustQuery("select @@global.tidb_enable_top_sql;").Check(testkit.Rows("1")) - tk.MustExec("set @@global.tidb_enable_top_sql='off';") - tk.MustQuery("select @@global.tidb_enable_top_sql;").Check(testkit.Rows("0")) - - tk.MustExec("set @@global.tidb_top_sql_max_time_series_count=20;") - tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("20")) - require.Equal(t, int64(20), topsqlstate.GlobalState.MaxStatementCount.Load()) - err := tk.ExecToErr("set @@global.tidb_top_sql_max_time_series_count='abc';") - require.EqualError(t, err, "[variable:1232]Incorrect argument type to variable 'tidb_top_sql_max_time_series_count'") - tk.MustExec("set @@global.tidb_top_sql_max_time_series_count='-1';") - tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("1")) - tk.MustExec("set @@global.tidb_top_sql_max_time_series_count='5001';") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_time_series_count value: '5001'")) - tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("5000")) - - tk.MustExec("set @@global.tidb_top_sql_max_time_series_count=20;") - tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("20")) - require.Equal(t, int64(20), topsqlstate.GlobalState.MaxStatementCount.Load()) - - tk.MustExec("set @@global.tidb_top_sql_max_meta_count=10000;") - tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("10000")) - require.Equal(t, int64(10000), topsqlstate.GlobalState.MaxCollect.Load()) - err = tk.ExecToErr("set @@global.tidb_top_sql_max_meta_count='abc';") - require.EqualError(t, err, "[variable:1232]Incorrect argument type to variable 'tidb_top_sql_max_meta_count'") - tk.MustExec("set @@global.tidb_top_sql_max_meta_count='-1';") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_meta_count value: '-1'")) - tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("1")) - - tk.MustExec("set @@global.tidb_top_sql_max_meta_count='10001';") - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_meta_count value: '10001'")) - tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("10000")) - - tk.MustExec("set @@global.tidb_top_sql_max_meta_count=5000;") - tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("5000")) - require.Equal(t, int64(5000), topsqlstate.GlobalState.MaxCollect.Load()) - - tk.MustQuery("show variables like '%top_sql%'").Check(testkit.Rows("tidb_enable_top_sql OFF", "tidb_top_sql_max_meta_count 5000", "tidb_top_sql_max_time_series_count 20")) - tk.MustQuery("show global variables like '%top_sql%'").Check(testkit.Rows("tidb_enable_top_sql OFF", "tidb_top_sql_max_meta_count 5000", "tidb_top_sql_max_time_series_count 20")) -} - -func TestPreparePlanCacheValid(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // global scope - tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value - tk.MustExec("SET GLOBAL tidb_session_plan_cache_size = 0") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1292 Truncated incorrect tidb_session_plan_cache_size value: '0'")) - tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL tidb_session_plan_cache_size = 2") - tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("2")) - // session scope - tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value - tk.MustExec("SET SESSION tidb_session_plan_cache_size = 0") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1292 Truncated incorrect tidb_session_plan_cache_size value: '0'")) - tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("1")) - tk.MustExec("SET SESSION tidb_session_plan_cache_size = 2") - tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("2")) - - tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = -0.1") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1292 Truncated incorrect tidb_prepared_plan_cache_memory_guard_ratio value: '-0.1'")) - tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("0")) - tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = 2.2") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1292 Truncated incorrect tidb_prepared_plan_cache_memory_guard_ratio value: '2.2'")) - tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = 0.5") - tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("0.5")) - - tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 0") - tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("0")) - tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 1") - tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 0") - tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("0")) -} - -func TestInstanceScopeSwitching(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // enable 'switching' to SESSION variables - tk.MustExec("set tidb_enable_legacy_instance_scope = 1") - tk.MustExec("set tidb_general_log = 1") - tk.MustQuery(`show warnings`).Check(testkit.Rows(fmt.Sprintf("Warning %d modifying tidb_general_log will require SET GLOBAL in a future version of TiDB", errno.ErrInstanceScope))) - - // disable 'switching' to SESSION variables - tk.MustExec("set tidb_enable_legacy_instance_scope = 0") - tk.MustGetErrCode("set tidb_general_log = 1", errno.ErrGlobalVariable) -} - -func TestGcMaxWaitTime(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("set global tidb_gc_max_wait_time = 1000") - tk.MustExec("set global tidb_gc_life_time = \"72h\"") - tk.MustExec("set global tidb_gc_life_time = \"24h\"") - tk.MustExec("set global tidb_gc_life_time = \"10m\"") - - tk.MustExec("set global tidb_gc_max_wait_time = 86400") - tk.MustExec("set global tidb_gc_life_time = \"72h\"") - tk.MustExec("set global tidb_gc_max_wait_time = 1000") -} - -func TestTiFlashFineGrainedShuffle(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // Default is 0. - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) - - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) - // Min val is -1. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = -2") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("-1")) - - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) - - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 1024") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("1024")) - // Max val is 1024. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 1025") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("1024")) - - // Default is 8192. - tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("8192")) - - // Min is 1. - tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = 0") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("1")) - tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = -1") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("1")) - - // Max is uint64_max. - tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = 18446744073709551615") - tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("18446744073709551615")) - - // Test set global. - tk.MustExec("set global tiflash_fine_grained_shuffle_stream_count = -1") - tk.MustExec("set global tiflash_fine_grained_shuffle_batch_size = 8192") -} - -func TestSetTiFlashFastScanVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int);") - tk.MustExec("insert into t values(1);") - - // check the default tiflash read mode - tk.MustQuery("select @@session.tiflash_fastscan").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.tiflash_fastscan").Check(testkit.Rows("0")) - - tk.MustExec("set @@tiflash_fastscan=ON;") - tk.MustQuery("select @@session.tiflash_fastscan").Check(testkit.Rows("1")) - - tk.MustExec("set GLOBAL tiflash_fastscan=OFF;") - tk.MustQuery("select @@global.tiflash_fastscan").Check(testkit.Rows("0")) -} - -func TestSetPlanCacheMemoryMonitor(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) - tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) - - tk.MustExec("set @@session.tidb_enable_prepared_plan_cache_memory_monitor=OFF;") - tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("0")) - - tk.MustExec("set @@session.tidb_enable_prepared_plan_cache_memory_monitor=1;") - tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) - - tk.MustExec("set @@global.tidb_enable_prepared_plan_cache_memory_monitor=off;") - tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("0")) -} - -func TestSetChunkReuseVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_enable_reuse_chunk=ON;") - tk.MustQuery("select @@session.tidb_enable_reuse_chunk").Check(testkit.Rows("1")) - tk.MustExec("set GLOBAL tidb_enable_reuse_chunk=ON;") - tk.MustQuery("select @@global.tidb_enable_reuse_chunk").Check(testkit.Rows("1")) - - tk.MustExec("set @@tidb_enable_reuse_chunk=OFF;") - tk.MustQuery("select @@session.tidb_enable_reuse_chunk").Check(testkit.Rows("0")) - tk.MustExec("set GLOBAL tidb_enable_reuse_chunk=OFF;") - tk.MustQuery("select @@global.tidb_enable_reuse_chunk").Check(testkit.Rows("0")) - - // error value - tk.MustGetErrCode("set @@tidb_enable_reuse_chunk=s;", errno.ErrWrongValueForVar) -} - -func TestSetMppVersionVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("UNSPECIFIED")) - tk.MustExec("SET SESSION mpp_version = -1") - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("-1")) - tk.MustExec("SET SESSION mpp_version = 0") - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("0")) - tk.MustExec("SET SESSION mpp_version = 1") - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("1")) - tk.MustExec("SET SESSION mpp_version = 2") - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("2")) - tk.MustExec("SET SESSION mpp_version = unspecified") - tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("unspecified")) - { - tk.MustGetErrMsg("SET SESSION mpp_version = 3", "incorrect value: 3. mpp_version options: -1 (unspecified), 0, 1, 2") - } - { - tk.MustExec("SET GLOBAL mpp_version = 1") - tk.MustQuery("select @@global.mpp_version").Check(testkit.Rows("1")) - tk.MustExec("SET GLOBAL mpp_version = -1") - tk.MustQuery("select @@global.mpp_version").Check(testkit.Rows("-1")) - } -} - -func TestSetMppExchangeCompressionModeVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustGetErrMsg( - "SET SESSION mpp_exchange_compression_mode = 123", - "incorrect value: `123`. mpp_exchange_compression_mode options: NONE, FAST, HIGH_COMPRESSION, UNSPECIFIED") - tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("UNSPECIFIED")) - - tk.MustExec("SET SESSION mpp_exchange_compression_mode = none") - tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("none")) - tk.MustExec("SET SESSION mpp_exchange_compression_mode = fast") - tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("fast")) - tk.MustExec("SET SESSION mpp_exchange_compression_mode = HIGH_COMPRESSION") - tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("HIGH_COMPRESSION")) - - { - tk.MustExec("SET GLOBAL mpp_exchange_compression_mode = none") - tk.MustQuery("select @@global.mpp_exchange_compression_mode").Check(testkit.Rows("none")) - } - { - tk.MustExec("SET mpp_version = 0") - tk.MustExec("SET mpp_exchange_compression_mode = unspecified") - require.Equal(t, len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()), 0) - } - { - tk.MustExec("SET mpp_version = 0") - tk.MustExec("SET mpp_exchange_compression_mode = HIGH_COMPRESSION") - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Equal(t, len(warnings), 1) - require.Equal(t, warnings[0].Err.Error(), "mpp exchange compression won't work under current mpp version 0") - } -} - -func TestDeprecateEnableTiFlashPipelineModel(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@global.tidb_enable_tiflash_pipeline_model = 1`) - tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1681 tidb_enable_tiflash_pipeline_model is deprecated and will be removed in a future release.")) -} diff --git a/executor/show_test.go b/executor/show_test.go deleted file mode 100644 index 286180f093cbe..0000000000000 --- a/executor/show_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/stretchr/testify/require" -) - -func Test_fillOneImportJobInfo(t *testing.T) { - typeBytes := []byte{mysql.TypeLonglong, mysql.TypeString, mysql.TypeString, mysql.TypeLonglong, - mysql.TypeString, mysql.TypeString, mysql.TypeString, mysql.TypeLonglong, - mysql.TypeString, mysql.TypeTimestamp, mysql.TypeTimestamp, mysql.TypeTimestamp, mysql.TypeString} - fieldTypes := make([]*types.FieldType, 0, len(typeBytes)) - for _, tp := range typeBytes { - fieldType := types.NewFieldType(tp) - flen, decimal := mysql.GetDefaultFieldLengthAndDecimal(tp) - fieldType.SetFlen(flen) - fieldType.SetDecimal(decimal) - charset, collate := types.DefaultCharsetForType(tp) - fieldType.SetCharset(charset) - fieldType.SetCollate(collate) - fieldTypes = append(fieldTypes, fieldType) - } - c := chunk.New(fieldTypes, 10, 10) - jobInfo := &importer.JobInfo{ - Parameters: importer.ImportParameters{}, - } - fillOneImportJobInfo(jobInfo, c, -1) - require.True(t, c.GetRow(0).IsNull(7)) - require.True(t, c.GetRow(0).IsNull(10)) - require.True(t, c.GetRow(0).IsNull(11)) - - fillOneImportJobInfo(jobInfo, c, 0) - require.False(t, c.GetRow(1).IsNull(7)) - require.Equal(t, uint64(0), c.GetRow(1).GetUint64(7)) - require.True(t, c.GetRow(1).IsNull(10)) - require.True(t, c.GetRow(1).IsNull(11)) - - jobInfo.Summary = &importer.JobSummary{ImportedRows: 123} - jobInfo.StartTime = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 0) - jobInfo.EndTime = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 0) - fillOneImportJobInfo(jobInfo, c, 0) - require.False(t, c.GetRow(2).IsNull(7)) - require.Equal(t, uint64(123), c.GetRow(2).GetUint64(7)) - require.False(t, c.GetRow(2).IsNull(10)) - require.False(t, c.GetRow(2).IsNull(11)) -} diff --git a/executor/simple_test.go b/executor/simple_test.go deleted file mode 100644 index 196c92b27e9bc..0000000000000 --- a/executor/simple_test.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "context" - "fmt" - "strconv" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/globalconn" - "github.com/stretchr/testify/require" -) - -func TestKillStmt(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - sv := server.CreateMockServer(t, store) - sv.SetDomain(dom) - defer sv.Close() - - conn1 := server.CreateMockConn(t, sv) - tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) - - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - newCfg.EnableGlobalKill = false - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - }() - - connID := conn1.ID() - - tk.MustExec("use test") - tk.MustExec(fmt.Sprintf("kill %d", connID)) - result := tk.MustQuery("show warnings") - result.Check(testkit.Rows("Warning 1105 Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] [connectionID | CONNECTION_ID()]' instead")) - - newCfg2 := *originCfg - newCfg2.EnableGlobalKill = true - config.StoreGlobalConfig(&newCfg2) - - // ZERO serverID, treated as truncated. - tk.MustExec("kill 1") - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows("Warning 1105 Kill failed: Received a 32bits truncated ConnectionID, expect 64bits. Please execute 'KILL [CONNECTION | QUERY] ConnectionID' to send a Kill without truncating ConnectionID.")) - - // truncated - tk.MustExec("kill 101") - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows("Warning 1105 Kill failed: Received a 32bits truncated ConnectionID, expect 64bits. Please execute 'KILL [CONNECTION | QUERY] ConnectionID' to send a Kill without truncating ConnectionID.")) - - // excceed int64 - tk.MustExec("kill 9223372036854775808") // 9223372036854775808 == 2^63 - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows("Warning 1105 Parse ConnectionID failed: unexpected connectionID exceeds int64")) - - // local kill - connIDAllocator := globalconn.NewGlobalAllocator(dom.ServerID, false) - killConnID := connIDAllocator.NextID() - tk.MustExec("kill " + strconv.FormatUint(killConnID, 10)) - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows()) - - tk.MustExecToErr("kill rand()", "Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] [connectionID | CONNECTION_ID()]' instead") - // remote kill is tested in `tests/globalkilltest` -} - -func TestUserAttributes(t *testing.T) { - store := testkit.CreateMockStore(t) - rootTK := testkit.NewTestKit(t, store) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - - // https://dev.mysql.com/doc/refman/8.0/en/create-user.html#create-user-comments-attributes - rootTK.MustExec(`CREATE USER testuser COMMENT '1234'`) - rootTK.MustExec(`CREATE USER testuser1 ATTRIBUTE '{"name": "Tom", "age": 19}'`) - _, err := rootTK.Exec(`CREATE USER testuser2 ATTRIBUTE '{"name": "Tom", age: 19}'`) - rootTK.MustExec(`CREATE USER testuser2`) - require.Error(t, err) - rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser'`).Check(testkit.Rows(`{"metadata": {"comment": "1234"}}`)) - rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser1'`).Check(testkit.Rows(`{"metadata": {"age": 19, "name": "Tom"}}`)) - rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser2'`).Check(testkit.Rows(`{}`)) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser'`).Check(testkit.Rows(`{"comment": "1234"}`)) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 19, "name": "Tom"}`)) - rootTK.MustQueryWithContext(ctx, `SELECT attribute->>"$.age" AS age, attribute->>"$.name" AS name FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`19 Tom`)) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser2'`).Check(testkit.Rows(``)) - - // https://dev.mysql.com/doc/refman/8.0/en/alter-user.html#alter-user-comments-attributes - rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"age": 20, "sex": "male"}'`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom", "sex": "male"}`)) - rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"hobby": "soccer"}'`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "hobby": "soccer", "name": "Tom", "sex": "male"}`)) - rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"sex": null, "hobby": null}'`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`)) - rootTK.MustExec(`ALTER USER testuser1 COMMENT '5678'`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "5678", "name": "Tom"}`)) - rootTK.MustExec(`ALTER USER testuser1 COMMENT ''`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "", "name": "Tom"}`)) - rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"comment": null}'`) - rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`)) - - // Non-root users could access COMMENT or ATTRIBUTE of all users via the view, - // but not via the mysql.user table. - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "testuser1"}, nil, nil, nil)) - tk.MustQueryWithContext(ctx, `SELECT user, host, attribute FROM information_schema.user_attributes ORDER BY user`).Check( - testkit.Rows("root % ", "testuser % {\"comment\": \"1234\"}", "testuser1 % {\"age\": 20, \"name\": \"Tom\"}", "testuser2 % ")) - tk.MustGetErrCode(`SELECT user, host, user_attributes FROM mysql.user ORDER BY user`, mysql.ErrTableaccessDenied) - - // https://github.com/pingcap/tidb/issues/39207 - rootTK.MustExec("create user usr1@'%' identified by 'passord'") - rootTK.MustExec("alter user usr1 comment 'comment1'") - rootTK.MustQuery("select user_attributes from mysql.user where user = 'usr1'").Check(testkit.Rows(`{"metadata": {"comment": "comment1"}}`)) - rootTK.MustExec("set global tidb_enable_resource_control = 'on'") - rootTK.MustExec("CREATE RESOURCE GROUP rg1 ru_per_sec = 100") - rootTK.MustExec("alter user usr1 resource group rg1") - rootTK.MustQuery("select user_attributes from mysql.user where user = 'usr1'").Check(testkit.Rows(`{"metadata": {"comment": "comment1"}, "resource_group": "rg1"}`)) -} - -func TestSetResourceGroup(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("SET GLOBAL tidb_enable_resource_control='on'") - - tk.MustContainErrMsg("SET RESOURCE GROUP rg1", "Unknown resource group 'rg1'") - - tk.MustExec("CREATE RESOURCE GROUP rg1 ru_per_sec = 100") - tk.MustExec("ALTER USER `root` RESOURCE GROUP `rg1`") - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg1")) - - tk.MustExec("CREATE RESOURCE GROUP rg2 ru_per_sec = 200") - tk.MustExec("SET RESOURCE GROUP `rg2`") - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg2")) - tk.MustExec("SET RESOURCE GROUP ``") - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) - tk.MustExec("SET RESOURCE GROUP default") - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) - - tk.RefreshSession() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg1")) -} diff --git a/executor/sort.go b/executor/sort.go deleted file mode 100644 index 0f87e6444fff7..0000000000000 --- a/executor/sort.go +++ /dev/null @@ -1,550 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "container/heap" - "context" - "errors" - "slices" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" -) - -// SortExec represents sorting executor. -type SortExec struct { - exec.BaseExecutor - - ByItems []*util.ByItems - Idx int - fetched bool - schema *expression.Schema - - // keyColumns is the column index of the by items. - keyColumns []int - // keyCmpFuncs is used to compare each ByItem. - keyCmpFuncs []chunk.CompareFunc - // rowChunks is the chunks to store row values. - rowChunks *chunk.SortedRowContainer - - memTracker *memory.Tracker - diskTracker *disk.Tracker - - // partitionList is the chunks to store row values for partitions. Every partition is a sorted list. - partitionList []*chunk.SortedRowContainer - - // multiWayMerge uses multi-way merge for spill disk. - // The multi-way merge algorithm can refer to https://en.wikipedia.org/wiki/K-way_merge_algorithm - multiWayMerge *multiWayMerge - // spillAction save the Action for spill disk. - spillAction *chunk.SortAndSpillDiskAction -} - -// Close implements the Executor Close interface. -func (e *SortExec) Close() error { - for _, container := range e.partitionList { - err := container.Close() - if err != nil { - return err - } - } - e.partitionList = e.partitionList[:0] - - if e.rowChunks != nil { - e.memTracker.Consume(-e.rowChunks.GetMemTracker().BytesConsumed()) - e.rowChunks = nil - } - e.memTracker = nil - e.diskTracker = nil - e.multiWayMerge = nil - if e.spillAction != nil { - e.spillAction.SetFinished() - } - e.spillAction = nil - return e.Children(0).Close() -} - -// Open implements the Executor Open interface. -func (e *SortExec) Open(ctx context.Context) error { - e.fetched = false - e.Idx = 0 - - // To avoid duplicated initialization for TopNExec. - if e.memTracker == nil { - e.memTracker = memory.NewTracker(e.ID(), -1) - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - e.diskTracker = memory.NewTracker(e.ID(), -1) - e.diskTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.DiskTracker) - } - e.partitionList = e.partitionList[:0] - return e.Children(0).Open(ctx) -} - -// Next implements the Executor Next interface. -// Sort constructs the result following these step: -// 1. Read as mush as rows into memory. -// 2. If memory quota is triggered, sort these rows in memory and put them into disk as partition 1, then reset -// the memory quota trigger and return to step 1 -// 3. If memory quota is not triggered and child is consumed, sort these rows in memory as partition N. -// 4. Merge sort if the count of partitions is larger than 1. If there is only one partition in step 4, it works -// just like in-memory sort before. -func (e *SortExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if !e.fetched { - e.initCompareFuncs() - e.buildKeyColumns() - err := e.fetchRowChunks(ctx) - if err != nil { - return err - } - e.fetched = true - } - - if len(e.partitionList) == 0 { - return nil - } - if len(e.partitionList) > 1 { - if err := e.externalSorting(req); err != nil { - return err - } - } else { - for !req.IsFull() && e.Idx < e.partitionList[0].NumRow() { - _, _, err := e.partitionList[0].GetSortedRowAndAlwaysAppendToChunk(e.Idx, req) - if err != nil { - return err - } - e.Idx++ - } - } - return nil -} - -func (e *SortExec) externalSorting(req *chunk.Chunk) (err error) { - if e.multiWayMerge == nil { - e.multiWayMerge = &multiWayMerge{e.lessRow, e.compressRow, make([]partitionPointer, 0, len(e.partitionList))} - for i := 0; i < len(e.partitionList); i++ { - chk := chunk.New(exec.RetTypes(e), 1, 1) - - row, _, err := e.partitionList[i].GetSortedRowAndAlwaysAppendToChunk(0, chk) - if err != nil { - return err - } - e.multiWayMerge.elements = append(e.multiWayMerge.elements, partitionPointer{chk: chk, row: row, partitionID: i, consumed: 0}) - } - heap.Init(e.multiWayMerge) - } - - for !req.IsFull() && e.multiWayMerge.Len() > 0 { - partitionPtr := e.multiWayMerge.elements[0] - req.AppendRow(partitionPtr.row) - partitionPtr.consumed++ - partitionPtr.chk.Reset() - if partitionPtr.consumed >= e.partitionList[partitionPtr.partitionID].NumRow() { - heap.Remove(e.multiWayMerge, 0) - continue - } - - partitionPtr.row, _, err = e.partitionList[partitionPtr.partitionID]. - GetSortedRowAndAlwaysAppendToChunk(partitionPtr.consumed, partitionPtr.chk) - if err != nil { - return err - } - e.multiWayMerge.elements[0] = partitionPtr - heap.Fix(e.multiWayMerge, 0) - } - return nil -} - -func (e *SortExec) fetchRowChunks(ctx context.Context) error { - fields := exec.RetTypes(e) - byItemsDesc := make([]bool, len(e.ByItems)) - for i, byItem := range e.ByItems { - byItemsDesc[i] = byItem.Desc - } - e.rowChunks = chunk.NewSortedRowContainer(fields, e.MaxChunkSize(), byItemsDesc, e.keyColumns, e.keyCmpFuncs) - e.rowChunks.GetMemTracker().AttachTo(e.memTracker) - e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) - if variable.EnableTmpStorageOnOOM.Load() { - e.spillAction = e.rowChunks.ActionSpill() - failpoint.Inject("testSortedRowContainerSpill", func(val failpoint.Value) { - if val.(bool) { - e.spillAction = e.rowChunks.ActionSpillForTest() - defer e.spillAction.WaitForTest() - } - }) - e.Ctx().GetSessionVars().MemTracker.FallbackOldAndSetNewAction(e.spillAction) - e.rowChunks.GetDiskTracker().AttachTo(e.diskTracker) - e.rowChunks.GetDiskTracker().SetLabel(memory.LabelForRowChunks) - } - for { - chk := exec.TryNewCacheChunk(e.Children(0)) - err := exec.Next(ctx, e.Children(0), chk) - if err != nil { - return err - } - rowCount := chk.NumRows() - if rowCount == 0 { - break - } - if err := e.rowChunks.Add(chk); err != nil { - if errors.Is(err, chunk.ErrCannotAddBecauseSorted) { - e.partitionList = append(e.partitionList, e.rowChunks) - e.rowChunks = chunk.NewSortedRowContainer(fields, e.MaxChunkSize(), byItemsDesc, e.keyColumns, e.keyCmpFuncs) - e.rowChunks.GetMemTracker().AttachTo(e.memTracker) - e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) - e.rowChunks.GetDiskTracker().AttachTo(e.diskTracker) - e.rowChunks.GetDiskTracker().SetLabel(memory.LabelForRowChunks) - e.spillAction = e.rowChunks.ActionSpill() - failpoint.Inject("testSortedRowContainerSpill", func(val failpoint.Value) { - if val.(bool) { - e.spillAction = e.rowChunks.ActionSpillForTest() - defer e.spillAction.WaitForTest() - } - }) - e.Ctx().GetSessionVars().MemTracker.FallbackOldAndSetNewAction(e.spillAction) - err = e.rowChunks.Add(chk) - } - if err != nil { - return err - } - } - } - failpoint.Inject("SignalCheckpointForSort", func(val failpoint.Value) { - if val.(bool) { - if e.Ctx().GetSessionVars().ConnectionID == 123456 { - e.Ctx().GetSessionVars().MemTracker.NeedKill.Store(true) - } - } - }) - if e.rowChunks.NumRow() > 0 { - err := e.rowChunks.Sort() - if err != nil { - return err - } - e.partitionList = append(e.partitionList, e.rowChunks) - } - return nil -} - -func (e *SortExec) initCompareFuncs() { - e.keyCmpFuncs = make([]chunk.CompareFunc, len(e.ByItems)) - for i := range e.ByItems { - keyType := e.ByItems[i].Expr.GetType() - e.keyCmpFuncs[i] = chunk.GetCompareFunc(keyType) - } -} - -func (e *SortExec) buildKeyColumns() { - e.keyColumns = make([]int, 0, len(e.ByItems)) - for _, by := range e.ByItems { - col := by.Expr.(*expression.Column) - e.keyColumns = append(e.keyColumns, col.Index) - } -} - -func (e *SortExec) lessRow(rowI, rowJ chunk.Row) bool { - for i, colIdx := range e.keyColumns { - cmpFunc := e.keyCmpFuncs[i] - cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) - if e.ByItems[i].Desc { - cmp = -cmp - } - if cmp < 0 { - return true - } else if cmp > 0 { - return false - } - } - return false -} - -func (e *SortExec) compressRow(rowI, rowJ chunk.Row) int { - for i, colIdx := range e.keyColumns { - cmpFunc := e.keyCmpFuncs[i] - cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) - if e.ByItems[i].Desc { - cmp = -cmp - } - if cmp != 0 { - return cmp - } - } - return 0 -} - -type partitionPointer struct { - chk *chunk.Chunk - row chunk.Row - partitionID int - consumed int -} - -type multiWayMerge struct { - lessRowFunction func(rowI chunk.Row, rowJ chunk.Row) bool - compressRowFunction func(rowI chunk.Row, rowJ chunk.Row) int - elements []partitionPointer -} - -func (h *multiWayMerge) Less(i, j int) bool { - rowI := h.elements[i].row - rowJ := h.elements[j].row - return h.lessRowFunction(rowI, rowJ) -} - -func (h *multiWayMerge) Len() int { - return len(h.elements) -} - -func (*multiWayMerge) Push(interface{}) { - // Should never be called. -} - -func (h *multiWayMerge) Pop() interface{} { - h.elements = h.elements[:len(h.elements)-1] - return nil -} - -func (h *multiWayMerge) Swap(i, j int) { - h.elements[i], h.elements[j] = h.elements[j], h.elements[i] -} - -// TopNExec implements a Top-N algorithm and it is built from a SELECT statement with ORDER BY and LIMIT. -// Instead of sorting all the rows fetched from the table, it keeps the Top-N elements only in a heap to reduce memory usage. -type TopNExec struct { - SortExec - limit *plannercore.PhysicalLimit - totalLimit uint64 - - // rowChunks is the chunks to store row values. - rowChunks *chunk.List - // rowPointer store the chunk index and row index for each row. - rowPtrs []chunk.RowPtr - - chkHeap *topNChunkHeap -} - -// topNChunkHeap implements heap.Interface. -type topNChunkHeap struct { - *TopNExec -} - -// Less implement heap.Interface, but since we mantains a max heap, -// this function returns true if row i is greater than row j. -func (h *topNChunkHeap) Less(i, j int) bool { - rowI := h.rowChunks.GetRow(h.rowPtrs[i]) - rowJ := h.rowChunks.GetRow(h.rowPtrs[j]) - return h.greaterRow(rowI, rowJ) -} - -func (h *topNChunkHeap) greaterRow(rowI, rowJ chunk.Row) bool { - for i, colIdx := range h.keyColumns { - cmpFunc := h.keyCmpFuncs[i] - cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) - if h.ByItems[i].Desc { - cmp = -cmp - } - if cmp > 0 { - return true - } else if cmp < 0 { - return false - } - } - return false -} - -func (h *topNChunkHeap) Len() int { - return len(h.rowPtrs) -} - -func (*topNChunkHeap) Push(interface{}) { - // Should never be called. -} - -func (h *topNChunkHeap) Pop() interface{} { - h.rowPtrs = h.rowPtrs[:len(h.rowPtrs)-1] - // We don't need the popped value, return nil to avoid memory allocation. - return nil -} - -func (h *topNChunkHeap) Swap(i, j int) { - h.rowPtrs[i], h.rowPtrs[j] = h.rowPtrs[j], h.rowPtrs[i] -} - -// keyColumnsLess is the less function for key columns. -func (e *TopNExec) keyColumnsLess(i, j chunk.RowPtr) bool { - rowI := e.rowChunks.GetRow(i) - rowJ := e.rowChunks.GetRow(j) - return e.lessRow(rowI, rowJ) -} - -func (e *TopNExec) keyColumnsCompare(i, j chunk.RowPtr) int { - rowI := e.rowChunks.GetRow(i) - rowJ := e.rowChunks.GetRow(j) - return e.compressRow(rowI, rowJ) -} - -func (e *TopNExec) initPointers() { - e.rowPtrs = make([]chunk.RowPtr, 0, e.rowChunks.Len()) - e.memTracker.Consume(int64(8 * e.rowChunks.Len())) - for chkIdx := 0; chkIdx < e.rowChunks.NumChunks(); chkIdx++ { - rowChk := e.rowChunks.GetChunk(chkIdx) - for rowIdx := 0; rowIdx < rowChk.NumRows(); rowIdx++ { - e.rowPtrs = append(e.rowPtrs, chunk.RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)}) - } - } -} - -// Open implements the Executor Open interface. -func (e *TopNExec) Open(ctx context.Context) error { - e.memTracker = memory.NewTracker(e.ID(), -1) - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - - e.fetched = false - e.Idx = 0 - - return e.Children(0).Open(ctx) -} - -// Next implements the Executor Next interface. -func (e *TopNExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if !e.fetched { - e.totalLimit = e.limit.Offset + e.limit.Count - e.Idx = int(e.limit.Offset) - err := e.loadChunksUntilTotalLimit(ctx) - if err != nil { - return err - } - err = e.executeTopN(ctx) - if err != nil { - return err - } - e.fetched = true - } - if e.Idx >= len(e.rowPtrs) { - return nil - } - if !req.IsFull() { - numToAppend := mathutil.Min(len(e.rowPtrs)-e.Idx, req.RequiredRows()-req.NumRows()) - rows := make([]chunk.Row, numToAppend) - for index := 0; index < numToAppend; index++ { - rows[index] = e.rowChunks.GetRow(e.rowPtrs[e.Idx]) - e.Idx++ - } - req.AppendRows(rows) - } - return nil -} - -func (e *TopNExec) loadChunksUntilTotalLimit(ctx context.Context) error { - e.chkHeap = &topNChunkHeap{e} - e.rowChunks = chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) - e.rowChunks.GetMemTracker().AttachTo(e.memTracker) - e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) - for uint64(e.rowChunks.Len()) < e.totalLimit { - srcChk := exec.TryNewCacheChunk(e.Children(0)) - // adjust required rows by total limit - srcChk.SetRequiredRows(int(e.totalLimit-uint64(e.rowChunks.Len())), e.MaxChunkSize()) - err := exec.Next(ctx, e.Children(0), srcChk) - if err != nil { - return err - } - if srcChk.NumRows() == 0 { - break - } - e.rowChunks.Add(srcChk) - } - e.initPointers() - e.initCompareFuncs() - e.buildKeyColumns() - return nil -} - -const topNCompactionFactor = 4 - -func (e *TopNExec) executeTopN(ctx context.Context) error { - heap.Init(e.chkHeap) - for uint64(len(e.rowPtrs)) > e.totalLimit { - // The number of rows we loaded may exceeds total limit, remove greatest rows by Pop. - heap.Pop(e.chkHeap) - } - childRowChk := exec.TryNewCacheChunk(e.Children(0)) - for { - err := exec.Next(ctx, e.Children(0), childRowChk) - if err != nil { - return err - } - if childRowChk.NumRows() == 0 { - break - } - err = e.processChildChk(childRowChk) - if err != nil { - return err - } - if e.rowChunks.Len() > len(e.rowPtrs)*topNCompactionFactor { - err = e.doCompaction() - if err != nil { - return err - } - } - } - slices.SortFunc(e.rowPtrs, e.keyColumnsCompare) - return nil -} - -func (e *TopNExec) processChildChk(childRowChk *chunk.Chunk) error { - for i := 0; i < childRowChk.NumRows(); i++ { - heapMaxPtr := e.rowPtrs[0] - var heapMax, next chunk.Row - heapMax = e.rowChunks.GetRow(heapMaxPtr) - next = childRowChk.GetRow(i) - if e.chkHeap.greaterRow(heapMax, next) { - // Evict heap max, keep the next row. - e.rowPtrs[0] = e.rowChunks.AppendRow(childRowChk.GetRow(i)) - heap.Fix(e.chkHeap, 0) - } - } - return nil -} - -// doCompaction rebuild the chunks and row pointers to release memory. -// If we don't do compaction, in a extreme case like the child data is already ascending sorted -// but we want descending top N, then we will keep all data in memory. -// But if data is distributed randomly, this function will be called log(n) times. -func (e *TopNExec) doCompaction() error { - newRowChunks := chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) - newRowPtrs := make([]chunk.RowPtr, 0, e.rowChunks.Len()) - for _, rowPtr := range e.rowPtrs { - newRowPtr := newRowChunks.AppendRow(e.rowChunks.GetRow(rowPtr)) - newRowPtrs = append(newRowPtrs, newRowPtr) - } - newRowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) - e.memTracker.ReplaceChild(e.rowChunks.GetMemTracker(), newRowChunks.GetMemTracker()) - e.rowChunks = newRowChunks - - e.memTracker.Consume(int64(-8 * len(e.rowPtrs))) - e.memTracker.Consume(int64(8 * len(newRowPtrs))) - e.rowPtrs = newRowPtrs - return nil -} diff --git a/executor/split_test.go b/executor/split_test.go deleted file mode 100644 index c2c58e8547c78..0000000000000 --- a/executor/split_test.go +++ /dev/null @@ -1,493 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "encoding/binary" - "math" - "math/rand" - "sort" - "testing" - "time" - - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestLongestCommonPrefixLen(t *testing.T) { - cases := []struct { - s1 string - s2 string - l int - }{ - {"", "", 0}, - {"", "a", 0}, - {"a", "", 0}, - {"a", "a", 1}, - {"ab", "a", 1}, - {"a", "ab", 1}, - {"b", "ab", 0}, - {"ba", "ab", 0}, - } - - for _, ca := range cases { - re := longestCommonPrefixLen([]byte(ca.s1), []byte(ca.s2)) - require.Equal(t, ca.l, re) - } -} - -func TestGetStepValue(t *testing.T) { - cases := []struct { - lower []byte - upper []byte - l int - v uint64 - }{ - {[]byte{}, []byte{}, 0, math.MaxUint64}, - {[]byte{0}, []byte{128}, 0, binary.BigEndian.Uint64([]byte{128, 255, 255, 255, 255, 255, 255, 255})}, - {[]byte{'a'}, []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255, 255, 255, 255, 255, 255, 255})}, - {[]byte("abc"), []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255 - 'b', 255 - 'c', 255, 255, 255, 255, 255})}, - {[]byte("abc"), []byte("xyz"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 255, 255, 255, 255, 255})}, - {[]byte("abc"), []byte("axyz"), 1, binary.BigEndian.Uint64([]byte{'x' - 'b', 'y' - 'c', 'z', 255, 255, 255, 255, 255})}, - {[]byte("abc0123456"), []byte("xyz01234"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 0, 0, 0, 0, 0})}, - } - - for _, ca := range cases { - l := longestCommonPrefixLen(ca.lower, ca.upper) - require.Equal(t, ca.l, l) - v0 := getStepValue(ca.lower[l:], ca.upper[l:], 1) - require.Equal(t, v0, ca.v) - } -} - -func TestSplitIndex(t *testing.T) { - tbInfo := &model.TableInfo{ - Name: model.NewCIStr("t1"), - ID: rand.Int63(), - Columns: []*model.ColumnInfo{ - { - Name: model.NewCIStr("c0"), - ID: 1, - Offset: 1, - DefaultValue: 0, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLong), - }, - }, - } - idxCols := []*model.IndexColumn{{Name: tbInfo.Columns[0].Name, Offset: 0, Length: types.UnspecifiedLength}} - idxInfo := &model.IndexInfo{ - ID: 2, - Name: model.NewCIStr("idx1"), - Table: model.NewCIStr("t1"), - Columns: idxCols, - State: model.StatePublic, - } - firstIdxInfo0 := idxInfo.Clone() - firstIdxInfo0.ID = 1 - firstIdxInfo0.Name = model.NewCIStr("idx") - tbInfo.Indices = []*model.IndexInfo{firstIdxInfo0, idxInfo} - - // Test for int index. - // range is 0 ~ 100, and split into 10 region. - // So 10 regions range is like below, left close right open interval: - // region1: [-inf ~ 10) - // region2: [10 ~ 20) - // region3: [20 ~ 30) - // region4: [30 ~ 40) - // region5: [40 ~ 50) - // region6: [50 ~ 60) - // region7: [60 ~ 70) - // region8: [70 ~ 80) - // region9: [80 ~ 90) - // region10: [90 ~ +inf) - ctx := mock.NewContext() - e := &SplitIndexRegionExec{ - BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), - tableInfo: tbInfo, - indexInfo: idxInfo, - lower: []types.Datum{types.NewDatum(0)}, - upper: []types.Datum{types.NewDatum(100)}, - num: 10, - } - valueList, err := e.getSplitIdxKeys() - sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) - require.NoError(t, err) - require.Len(t, valueList, e.num+1) - - cases := []struct { - value int - lessEqualIdx int - }{ - {-1, 0}, - {0, 0}, - {1, 0}, - {10, 1}, - {11, 1}, - {20, 2}, - {21, 2}, - {31, 3}, - {41, 4}, - {51, 5}, - {61, 6}, - {71, 7}, - {81, 8}, - {91, 9}, - {100, 9}, - {1000, 9}, - } - - index := tables.NewIndex(tbInfo.ID, tbInfo, idxInfo) - for _, ca := range cases { - // test for minInt64 handle - idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MinInt64), nil) - require.NoError(t, err) - idx := searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - - // Test for max int64 handle. - idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MaxInt64), nil) - require.NoError(t, err) - idx = searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - } - // Test for varchar index. - // range is a ~ z, and split into 26 region. - // So 26 regions range is like below: - // region1: [-inf ~ b) - // region2: [b ~ c) - // . - // . - // . - // region26: [y ~ +inf) - e.lower = []types.Datum{types.NewDatum("a")} - e.upper = []types.Datum{types.NewDatum("z")} - e.num = 26 - // change index column type to varchar - tbInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeVarchar) - - valueList, err = e.getSplitIdxKeys() - sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) - require.NoError(t, err) - require.Len(t, valueList, e.num+1) - - cases2 := []struct { - value string - lessEqualIdx int - }{ - {"", 0}, - {"a", 0}, - {"abcde", 0}, - {"b", 1}, - {"bzzzz", 1}, - {"c", 2}, - {"czzzz", 2}, - {"z", 25}, - {"zabcd", 25}, - } - - for _, ca := range cases2 { - // test for minInt64 handle - idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MinInt64), nil) - require.NoError(t, err) - idx := searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - - // Test for max int64 handle. - idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MaxInt64), nil) - require.NoError(t, err) - idx = searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - } - - // Test for timestamp index. - // range is 2010-01-01 00:00:00 ~ 2020-01-01 00:00:00, and split into 10 region. - // So 10 regions range is like below: - // region1: [-inf ~ 2011-01-01 00:00:00) - // region2: [2011-01-01 00:00:00 ~ 2012-01-01 00:00:00) - // . - // . - // . - // region10: [2019-01-01 00:00:00 ~ +inf) - lowerTime := types.NewTime(types.FromDate(2010, 1, 1, 0, 0, 0, 0), mysql.TypeTimestamp, types.DefaultFsp) - upperTime := types.NewTime(types.FromDate(2020, 1, 1, 0, 0, 0, 0), mysql.TypeTimestamp, types.DefaultFsp) - e.lower = []types.Datum{types.NewDatum(lowerTime)} - e.upper = []types.Datum{types.NewDatum(upperTime)} - e.num = 10 - - // change index column type to timestamp - tbInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeTimestamp) - - valueList, err = e.getSplitIdxKeys() - sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) - require.NoError(t, err) - require.Len(t, valueList, e.num+1) - - cases3 := []struct { - value types.CoreTime - lessEqualIdx int - }{ - {types.FromDate(2009, 11, 20, 12, 50, 59, 0), 0}, - {types.FromDate(2010, 1, 1, 0, 0, 0, 0), 0}, - {types.FromDate(2011, 12, 31, 23, 59, 59, 0), 1}, - {types.FromDate(2011, 2, 1, 0, 0, 0, 0), 1}, - {types.FromDate(2012, 3, 1, 0, 0, 0, 0), 2}, - {types.FromDate(2013, 4, 1, 0, 0, 0, 0), 3}, - {types.FromDate(2014, 5, 1, 0, 0, 0, 0), 4}, - {types.FromDate(2015, 6, 1, 0, 0, 0, 0), 5}, - {types.FromDate(2016, 8, 1, 0, 0, 0, 0), 6}, - {types.FromDate(2017, 9, 1, 0, 0, 0, 0), 7}, - {types.FromDate(2018, 10, 1, 0, 0, 0, 0), 8}, - {types.FromDate(2019, 11, 1, 0, 0, 0, 0), 9}, - {types.FromDate(2020, 12, 1, 0, 0, 0, 0), 9}, - {types.FromDate(2030, 12, 1, 0, 0, 0, 0), 9}, - } - - for _, ca := range cases3 { - value := types.NewTime(ca.value, mysql.TypeTimestamp, types.DefaultFsp) - // test for min int64 handle - idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(value)}, kv.IntHandle(math.MinInt64), nil) - require.NoError(t, err) - idx := searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - - // Test for max int64 handle. - idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(value)}, kv.IntHandle(math.MaxInt64), nil) - require.NoError(t, err) - idx = searchLessEqualIdx(valueList, idxValue) - require.Equal(t, idx, ca.lessEqualIdx) - } -} - -func TestSplitTable(t *testing.T) { - tbInfo := &model.TableInfo{ - Name: model.NewCIStr("t1"), - ID: rand.Int63(), - Columns: []*model.ColumnInfo{ - { - Name: model.NewCIStr("c0"), - ID: 1, - Offset: 1, - DefaultValue: 0, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLong), - }, - }, - } - defer func(originValue int64) { - minRegionStepValue = originValue - }(minRegionStepValue) - minRegionStepValue = 10 - // range is 0 ~ 100, and split into 10 region. - // So 10 regions range is like below: - // region1: [-inf ~ 10) - // region2: [10 ~ 20) - // region3: [20 ~ 30) - // region4: [30 ~ 40) - // region5: [40 ~ 50) - // region6: [50 ~ 60) - // region7: [60 ~ 70) - // region8: [70 ~ 80) - // region9: [80 ~ 90 ) - // region10: [90 ~ +inf) - ctx := mock.NewContext() - e := &SplitTableRegionExec{ - BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), - tableInfo: tbInfo, - handleCols: core.NewIntHandleCols(&expression.Column{RetType: types.NewFieldType(mysql.TypeLonglong)}), - lower: []types.Datum{types.NewDatum(0)}, - upper: []types.Datum{types.NewDatum(100)}, - num: 10, - } - valueList, err := e.getSplitTableKeys() - require.NoError(t, err) - require.Len(t, valueList, e.num-1) - - cases := []struct { - value int - lessEqualIdx int - }{ - {-1, -1}, - {0, -1}, - {1, -1}, - {10, 0}, - {11, 0}, - {20, 1}, - {21, 1}, - {31, 2}, - {41, 3}, - {51, 4}, - {61, 5}, - {71, 6}, - {81, 7}, - {91, 8}, - {100, 8}, - {1000, 8}, - } - - recordPrefix := tablecodec.GenTableRecordPrefix(e.tableInfo.ID) - for _, ca := range cases { - // test for minInt64 handle - key := tablecodec.EncodeRecordKey(recordPrefix, kv.IntHandle(ca.value)) - require.NoError(t, err) - idx := searchLessEqualIdx(valueList, key) - require.Equal(t, idx, ca.lessEqualIdx) - } -} - -func TestStepShouldLargeThanMinStep(t *testing.T) { - ctx := mock.NewContext() - tbInfo := &model.TableInfo{ - Name: model.NewCIStr("t1"), - ID: rand.Int63(), - Columns: []*model.ColumnInfo{ - { - Name: model.NewCIStr("c0"), - ID: 1, - Offset: 1, - DefaultValue: 0, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLong), - }, - }, - } - e1 := &SplitTableRegionExec{ - BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), - tableInfo: tbInfo, - handleCols: core.NewIntHandleCols(&expression.Column{RetType: types.NewFieldType(mysql.TypeLonglong)}), - lower: []types.Datum{types.NewDatum(0)}, - upper: []types.Datum{types.NewDatum(1000)}, - num: 10, - } - _, err := e1.getSplitTableKeys() - require.Equal(t, "[executor:8212]Failed to split region ranges: the region size is too small, expected at least 1000, but got 100", err.Error()) -} - -func TestClusterIndexSplitTable(t *testing.T) { - tbInfo := &model.TableInfo{ - Name: model.NewCIStr("t"), - ID: 1, - IsCommonHandle: true, - CommonHandleVersion: 1, - Indices: []*model.IndexInfo{ - { - ID: 1, - Primary: true, - State: model.StatePublic, - Columns: []*model.IndexColumn{ - {Offset: 1}, - {Offset: 2}, - }, - }, - }, - Columns: []*model.ColumnInfo{ - { - Name: model.NewCIStr("c0"), - ID: 1, - Offset: 0, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeDouble), - }, - { - Name: model.NewCIStr("c1"), - ID: 2, - Offset: 1, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLonglong), - }, - { - Name: model.NewCIStr("c2"), - ID: 3, - Offset: 2, - State: model.StatePublic, - FieldType: *types.NewFieldType(mysql.TypeLonglong), - }, - }, - } - defer func(originValue int64) { - minRegionStepValue = originValue - }(minRegionStepValue) - minRegionStepValue = 3 - ctx := mock.NewContext() - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - e := &SplitTableRegionExec{ - BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), - tableInfo: tbInfo, - handleCols: buildHandleColsForSplit(sc, tbInfo), - lower: types.MakeDatums(1, 0), - upper: types.MakeDatums(1, 100), - num: 10, - } - valueList, err := e.getSplitTableKeys() - require.NoError(t, err) - require.Len(t, valueList, e.num-1) - - cases := []struct { - value []types.Datum - lessEqualIdx int - }{ - // For lower-bound and upper-bound, because 0 and 100 are padding with 7 zeros, - // the split points are not (i * 10) but approximation. - {types.MakeDatums(1, -1), -1}, - {types.MakeDatums(1, 0), -1}, - {types.MakeDatums(1, 10), -1}, - {types.MakeDatums(1, 11), 0}, - {types.MakeDatums(1, 20), 0}, - {types.MakeDatums(1, 21), 1}, - - {types.MakeDatums(1, 31), 2}, - {types.MakeDatums(1, 41), 3}, - {types.MakeDatums(1, 51), 4}, - {types.MakeDatums(1, 61), 5}, - {types.MakeDatums(1, 71), 6}, - {types.MakeDatums(1, 81), 7}, - {types.MakeDatums(1, 91), 8}, - {types.MakeDatums(1, 100), 8}, - {types.MakeDatums(1, 101), 8}, - } - - recordPrefix := tablecodec.GenTableRecordPrefix(e.tableInfo.ID) - for _, ca := range cases { - h, err := e.handleCols.BuildHandleByDatums(ca.value) - require.NoError(t, err) - key := tablecodec.EncodeRecordKey(recordPrefix, h) - require.NoError(t, err) - idx := searchLessEqualIdx(valueList, key) - require.Equal(t, idx, ca.lessEqualIdx) - } -} - -func searchLessEqualIdx(valueList [][]byte, value []byte) int { - idx := -1 - for i, v := range valueList { - if bytes.Compare(value, v) >= 0 { - idx = i - continue - } - break - } - return idx -} diff --git a/executor/stmtsummary.go b/executor/stmtsummary.go deleted file mode 100644 index e5a13c12c8294..0000000000000 --- a/executor/stmtsummary.go +++ /dev/null @@ -1,407 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stmtsummary" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" -) - -const ( - defaultRetrieveCount = 1024 -) - -func buildStmtSummaryRetriever( - table *model.TableInfo, - columns []*model.ColumnInfo, - extractor *plannercore.StatementsSummaryExtractor, -) memTableRetriever { - if extractor == nil { - extractor = &plannercore.StatementsSummaryExtractor{} - } - if extractor.Digests.Empty() { - extractor.Digests = nil - } - - var retriever memTableRetriever - if extractor.SkipRequest { - retriever = &dummyRetriever{} - } else if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - retriever = &stmtSummaryRetrieverV2{ - stmtSummary: stmtsummaryv2.GlobalStmtSummary, - table: table, - columns: columns, - digests: extractor.Digests, - timeRanges: buildTimeRanges(extractor.CoarseTimeRange), - } - } else { - retriever = &stmtSummaryRetriever{ - table: table, - columns: columns, - digests: extractor.Digests, - } - } - - return retriever -} - -type dummyRetriever struct { - dummyCloser -} - -func (*dummyRetriever) retrieve(_ context.Context, _ sessionctx.Context) ([][]types.Datum, error) { - return nil, nil -} - -// stmtSummaryRetriever is used to retrieve statements summary. -type stmtSummaryRetriever struct { - table *model.TableInfo - columns []*model.ColumnInfo - digests set.StringSet - - // lazily initialized - rowsReader *rowsReader -} - -func (e *stmtSummaryRetriever) retrieve(_ context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { - if err := e.ensureRowsReader(sctx); err != nil { - return nil, err - } - return e.rowsReader.read(defaultRetrieveCount) -} - -func (e *stmtSummaryRetriever) close() error { - if e.rowsReader != nil { - return e.rowsReader.close() - } - return nil -} - -func (*stmtSummaryRetriever) getRuntimeStats() execdetails.RuntimeStats { - return nil -} - -func (e *stmtSummaryRetriever) ensureRowsReader(sctx sessionctx.Context) error { - if e.rowsReader != nil { - return nil - } - - var err error - if isEvictedTable(e.table.Name.O) { - e.rowsReader, err = e.initEvictedRowsReader(sctx) - } else { - e.rowsReader, err = e.initSummaryRowsReader(sctx) - } - - return err -} - -func (e *stmtSummaryRetriever) initEvictedRowsReader(sctx sessionctx.Context) (*rowsReader, error) { - if err := checkPrivilege(sctx); err != nil { - return nil, err - } - - rows := stmtsummary.StmtSummaryByDigestMap.ToEvictedCountDatum() - if !isClusterTable(e.table.Name.O) { - // rows are full-columned, so we need to adjust them to the required columns. - return newSimpleRowsReader(adjustColumns(rows, e.columns, e.table)), nil - } - - // Additional column `INSTANCE` for cluster table - rows, err := infoschema.AppendHostInfoToRows(sctx, rows) - if err != nil { - return nil, err - } - // rows are full-columned, so we need to adjust them to the required columns. - return newSimpleRowsReader(adjustColumns(rows, e.columns, e.table)), nil -} - -func (e *stmtSummaryRetriever) initSummaryRowsReader(sctx sessionctx.Context) (*rowsReader, error) { - vars := sctx.GetSessionVars() - user := vars.User - tz := vars.StmtCtx.TimeZone() - columns := e.columns - priv := hasPriv(sctx, mysql.ProcessPriv) - instanceAddr, err := clusterTableInstanceAddr(sctx, e.table.Name.O) - if err != nil { - return nil, err - } - - reader := stmtsummary.NewStmtSummaryReader(user, priv, columns, instanceAddr, tz) - if e.digests != nil { - // set checker to filter out statements not matching the given digests - checker := stmtsummary.NewStmtSummaryChecker(e.digests) - reader.SetChecker(checker) - } - - var rows [][]types.Datum - if isCurrentTable(e.table.Name.O) { - rows = reader.GetStmtSummaryCurrentRows() - } - if isHistoryTable(e.table.Name.O) { - rows = reader.GetStmtSummaryHistoryRows() - } - return newSimpleRowsReader(rows), nil -} - -// stmtSummaryRetriever is used to retrieve statements summary when -// config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent is true -type stmtSummaryRetrieverV2 struct { - stmtSummary *stmtsummaryv2.StmtSummary - table *model.TableInfo - columns []*model.ColumnInfo - digests set.StringSet - timeRanges []*stmtsummaryv2.StmtTimeRange - - // lazily initialized - rowsReader *rowsReader -} - -func (r *stmtSummaryRetrieverV2) retrieve(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { - if err := r.ensureRowsReader(ctx, sctx); err != nil { - return nil, err - } - return r.rowsReader.read(defaultRetrieveCount) -} - -func (r *stmtSummaryRetrieverV2) close() error { - if r.rowsReader != nil { - return r.rowsReader.close() - } - return nil -} - -func (*stmtSummaryRetrieverV2) getRuntimeStats() execdetails.RuntimeStats { - return nil -} - -func (r *stmtSummaryRetrieverV2) ensureRowsReader(ctx context.Context, sctx sessionctx.Context) error { - if r.rowsReader != nil { - return nil - } - - var err error - if isEvictedTable(r.table.Name.O) { - r.rowsReader, err = r.initEvictedRowsReader(sctx) - } else { - r.rowsReader, err = r.initSummaryRowsReader(ctx, sctx) - } - - return err -} - -func (r *stmtSummaryRetrieverV2) initEvictedRowsReader(sctx sessionctx.Context) (*rowsReader, error) { - if err := checkPrivilege(sctx); err != nil { - return nil, err - } - - var rows [][]types.Datum - - row := r.stmtSummary.Evicted() - if row != nil { - rows = append(rows, row) - } - if !isClusterTable(r.table.Name.O) { - // rows are full-columned, so we need to adjust them to the required columns. - return newSimpleRowsReader(adjustColumns(rows, r.columns, r.table)), nil - } - - // Additional column `INSTANCE` for cluster table - rows, err := infoschema.AppendHostInfoToRows(sctx, rows) - if err != nil { - return nil, err - } - // rows are full-columned, so we need to adjust them to the required columns. - return newSimpleRowsReader(adjustColumns(rows, r.columns, r.table)), nil -} - -func (r *stmtSummaryRetrieverV2) initSummaryRowsReader(ctx context.Context, sctx sessionctx.Context) (*rowsReader, error) { - vars := sctx.GetSessionVars() - user := vars.User - tz := vars.StmtCtx.TimeZone() - stmtSummary := r.stmtSummary - columns := r.columns - timeRanges := r.timeRanges - digests := r.digests - priv := hasPriv(sctx, mysql.ProcessPriv) - instanceAddr, err := clusterTableInstanceAddr(sctx, r.table.Name.O) - if err != nil { - return nil, err - } - - mem := stmtsummaryv2.NewMemReader(stmtSummary, columns, instanceAddr, tz, user, priv, digests, timeRanges) - memRows := mem.Rows() - - var rowsReader *rowsReader - if isCurrentTable(r.table.Name.O) { - rowsReader = newSimpleRowsReader(memRows) - } - if isHistoryTable(r.table.Name.O) { - // history table should return all rows including mem and disk - concurrent := sctx.GetSessionVars().Concurrency.DistSQLScanConcurrency() - history, err := stmtsummaryv2.NewHistoryReader(ctx, columns, instanceAddr, tz, user, priv, digests, timeRanges, concurrent) - if err != nil { - return nil, err - } - rowsReader = newRowsReader(memRows, history) - } - - return rowsReader, nil -} - -type rowsPuller interface { - Closeable - Rows() ([][]types.Datum, error) -} - -type rowsReader struct { - puller rowsPuller - rows [][]types.Datum -} - -func newSimpleRowsReader(rows [][]types.Datum) *rowsReader { - return &rowsReader{rows: rows} -} - -func newRowsReader(rows [][]types.Datum, puller rowsPuller) *rowsReader { - return &rowsReader{puller: puller, rows: rows} -} - -func (r *rowsReader) read(maxCount int) ([][]types.Datum, error) { - if err := r.pull(); err != nil { - return nil, err - } - - if maxCount >= len(r.rows) { - ret := r.rows - r.rows = nil - return ret, nil - } - ret := r.rows[:maxCount] - r.rows = r.rows[maxCount:] - return ret, nil -} - -func (r *rowsReader) pull() error { - if r.puller == nil { - return nil - } - // there are remaining rows - if len(r.rows) > 0 { - return nil - } - - rows, err := r.puller.Rows() - if err != nil { - return err - } - // pulled new rows from the puller - if len(rows) != 0 { - r.rows = rows - return nil - } - - // reach the end of the puller - err = r.puller.Close() - if err != nil { - return err - } - r.puller = nil - return nil -} - -func (r *rowsReader) close() error { - if r.puller != nil { - return r.puller.Close() - } - return nil -} - -func isClusterTable(originalTableName string) bool { - switch originalTableName { - case infoschema.ClusterTableStatementsSummary, - infoschema.ClusterTableStatementsSummaryHistory, - infoschema.ClusterTableStatementsSummaryEvicted: - return true - } - - return false -} - -func isCurrentTable(originalTableName string) bool { - switch originalTableName { - case infoschema.TableStatementsSummary, - infoschema.ClusterTableStatementsSummary: - return true - } - - return false -} - -func isHistoryTable(originalTableName string) bool { - switch originalTableName { - case infoschema.TableStatementsSummaryHistory, - infoschema.ClusterTableStatementsSummaryHistory: - return true - } - - return false -} - -func isEvictedTable(originalTableName string) bool { - switch originalTableName { - case infoschema.TableStatementsSummaryEvicted, - infoschema.ClusterTableStatementsSummaryEvicted: - return true - } - - return false -} - -func checkPrivilege(sctx sessionctx.Context) error { - if !hasPriv(sctx, mysql.ProcessPriv) { - return plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("PROCESS") - } - return nil -} - -func clusterTableInstanceAddr(sctx sessionctx.Context, originalTableName string) (string, error) { - if isClusterTable(originalTableName) { - return infoschema.GetInstanceAddr(sctx) - } - return "", nil -} - -func buildTimeRanges(tr *plannercore.TimeRange) []*stmtsummaryv2.StmtTimeRange { - if tr == nil { - return nil - } - - return []*stmtsummaryv2.StmtTimeRange{{ - Begin: tr.StartTime.Unix(), - End: tr.EndTime.Unix(), - }} -} diff --git a/executor/temporary_table_test.go b/executor/temporary_table_test.go deleted file mode 100644 index 43ca00905690e..0000000000000 --- a/executor/temporary_table_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "context" - "sync" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestNormalGlobalTemporaryTableNoNetwork(t *testing.T) { - assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { - tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") - tk.MustExec("begin") - }) -} - -func TestGlobalTemporaryTableNoNetworkWithCreateAndTruncate(t *testing.T) { - assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { - tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") - tk.MustExec("truncate table tmp_t") - tk.MustExec("begin") - }) -} - -func TestGlobalTemporaryTableNoNetworkWithCreateAndThenCreateNormalTable(t *testing.T) { - assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { - tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") - tk.MustExec("create table txx(a int)") - tk.MustExec("begin") - }) -} - -func TestLocalTemporaryTableNoNetworkWithCreateOutsideTxn(t *testing.T) { - assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { - tk.MustExec("create temporary table tmp_t (id int primary key, a int, b int, index(a))") - tk.MustExec("begin") - }) -} - -func TestLocalTemporaryTableNoNetworkWithInsideTxn(t *testing.T) { - assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { - tk.MustExec("begin") - tk.MustExec("create temporary table tmp_t (id int primary key, a int, b int, index(a))") - }) -} - -func assertTemporaryTableNoNetwork(t *testing.T, createTable func(*testkit.TestKit)) { - var done sync.WaitGroup - defer done.Wait() - - store := testkit.CreateMockStore(t) - - // Test that table reader/index reader/index lookup on the temporary table do not need to visit TiKV. - tk := testkit.NewTestKit(t, store) - tk1 := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk1.MustExec("use test") - tk.MustExec("drop table if exists normal, tmp_t") - tk.MustExec("create table normal (id int, a int, index(a))") - createTable(tk) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy")) - }() - - tk.MustExec("insert into tmp_t values (1, 1, 1)") - tk.MustExec("insert into tmp_t values (2, 2, 2)") - - // Make sure the fail point works. - // With that failpoint, all requests to the TiKV is discard. - rs, err := tk1.Exec("select * from normal") - require.NoError(t, err) - - blocked := make(chan struct{}, 1) - ctx, cancelFunc := context.WithCancel(context.Background()) - done.Add(1) - go func() { - defer done.Done() - _, _ = session.ResultSetToStringSlice(ctx, tk1.Session(), rs) - blocked <- struct{}{} - }() - - select { - case <-blocked: - cancelFunc() - require.FailNow(t, "The query should block when the failpoint is enabled.") - case <-time.After(200 * time.Millisecond): - cancelFunc() - } - - // Check the temporary table do not send request to TiKV. - // PointGet - tk.MustHavePlan("select * from tmp_t where id=1", "Point_Get") - tk.MustQuery("select * from tmp_t where id=1").Check(testkit.Rows("1 1 1")) - - // BatchPointGet - tk.MustHavePlan("select * from tmp_t where id in (1, 2)", "Batch_Point_Get") - tk.MustQuery("select * from tmp_t where id in (1, 2)").Check(testkit.Rows("1 1 1", "2 2 2")) - - // Table reader - tk.MustHavePlan("select * from tmp_t", "TableReader") - tk.MustQuery("select * from tmp_t").Check(testkit.Rows("1 1 1", "2 2 2")) - - // Index reader - tk.MustHavePlan("select /*+ USE_INDEX(tmp_t, a) */ a from tmp_t", "IndexReader") - tk.MustQuery("select /*+ USE_INDEX(tmp_t, a) */ a from tmp_t").Check(testkit.Rows("1", "2")) - - // Index lookup - tk.MustHavePlan("select /*+ USE_INDEX(tmp_t, a) */ b from tmp_t where a = 1", "IndexLookUp") - tk.MustQuery("select /*+ USE_INDEX(tmp_t, a) */ b from tmp_t where a = 1").Check(testkit.Rows("1")) - tk.MustExec("rollback") - - // prepare some data for local temporary table, when for global temporary table, the below operations have no effect. - tk.MustExec("insert into tmp_t value(10, 10, 10)") - tk.MustExec("insert into tmp_t value(11, 11, 11)") - - // Pessimistic lock - tk.MustExec("begin pessimistic") - tk.MustExec("insert into tmp_t values (3, 3, 3)") - tk.MustExec("insert ignore into tmp_t values (4, 4, 4)") - tk.MustExec("insert into tmp_t values (5, 5, 5) on duplicate key update a=100") - tk.MustExec("insert into tmp_t values (10, 10, 10) on duplicate key update a=100") - tk.MustExec("insert ignore into tmp_t values (10, 10, 10) on duplicate key update id=11") - tk.MustExec("replace into tmp_t values(6, 6, 6)") - tk.MustExec("replace into tmp_t values(11, 100, 100)") - tk.MustExec("update tmp_t set id = id + 1 where a = 1") - tk.MustExec("delete from tmp_t where a > 1") - tk.MustQuery("select count(*) from tmp_t where a >= 1 for update") - tk.MustExec("rollback") - - // Check 'for update' will not write any lock too when table is unmodified - tk.MustExec("begin pessimistic") - tk.MustExec("select * from tmp_t where id=1 for update") - tk.MustExec("select * from tmp_t where id in (1, 2, 3) for update") - tk.MustExec("select * from tmp_t where id > 1 for update") - tk.MustExec("rollback") -} diff --git a/executor/test/admintest/BUILD.bazel b/executor/test/admintest/BUILD.bazel deleted file mode 100644 index d79b63ce09e31..0000000000000 --- a/executor/test/admintest/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "admintest_test", - timeout = "short", - srcs = [ - "admin_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 21, - deps = [ - "//config", - "//domain", - "//errno", - "//executor", - "//kv", - "//meta/autoid", - "//parser/model", - "//session", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//testkit", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util/codec", - "//util/logutil", - "//util/logutil/consistency", - "//util/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/executor/test/admintest/main_test.go b/executor/test/admintest/main_test.go deleted file mode 100644 index ceaae5253e07c..0000000000000 --- a/executor/test/admintest/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package admintest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - conf.Performance.EnableStatsCacheMemQuota = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/aggregate/BUILD.bazel b/executor/test/aggregate/BUILD.bazel deleted file mode 100644 index 39edd0c6aae96..0000000000000 --- a/executor/test/aggregate/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "aggregate_test", - timeout = "short", - srcs = [ - "aggregate_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 9, - deps = [ - "//config", - "//executor/aggregate", - "//executor/internal", - "//session", - "//testkit", - "//testkit/testdata", - "//testkit/testsetup", - "//util/sqlexec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/aggregate/main_test.go b/executor/test/aggregate/main_test.go deleted file mode 100644 index d6b56a55b9c63..0000000000000 --- a/executor/test/aggregate/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package aggregate - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var aggMergeSuiteData testdata.TestData -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - testDataMap.LoadTestSuiteData("testdata", "agg_suite") - aggMergeSuiteData = testDataMap["agg_suite"] - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/analyzetest/BUILD.bazel b/executor/test/analyzetest/BUILD.bazel deleted file mode 100644 index 680c07a163d89..0000000000000 --- a/executor/test/analyzetest/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "analyzetest_test", - timeout = "short", - srcs = [ - "analyze_bench_test.go", - "analyze_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//domain", - "//domain/infosync", - "//errno", - "//executor", - "//infoschema", - "//kv", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/autoanalyze", - "//testkit", - "//util/dbterror/exeerrors", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/analyzetest/analyze_test.go b/executor/test/analyzetest/analyze_test.go deleted file mode 100644 index 67e6a28ef35ad..0000000000000 --- a/executor/test/analyzetest/analyze_test.go +++ /dev/null @@ -1,3216 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package analyzetest - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/stretchr/testify/require" -) - -func TestAnalyzePartition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - testkit.WithPruneMode(tk, variable.Static, func() { - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version=2") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21) -)` - tk.MustExec(createTable) - for i := 1; i < 21; i++ { - tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d, "hello")`, i, i)) - } - tk.MustExec("analyze table t") - - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - pi := table.Meta().GetPartitionInfo() - require.NotNil(t, pi) - do, err := session.GetDomain(store) - require.NoError(t, err) - handle := do.StatsHandle() - for _, def := range pi.Definitions { - statsTbl := handle.GetPartitionStats(table.Meta(), def.ID) - require.False(t, statsTbl.Pseudo) - require.Len(t, statsTbl.Columns, 3) - require.Len(t, statsTbl.Indices, 1) - for _, col := range statsTbl.Columns { - require.Greater(t, col.Len()+col.TopN.Num(), 0) - } - for _, idx := range statsTbl.Indices { - require.Greater(t, idx.Len()+idx.TopN.Num(), 0) - } - } - - tk.MustExec("drop table t") - tk.MustExec(createTable) - for i := 1; i < 21; i++ { - tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d, "hello")`, i, i)) - } - tk.MustExec("alter table t analyze partition p0") - is = tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - table, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - pi = table.Meta().GetPartitionInfo() - require.NotNil(t, pi) - - for i, def := range pi.Definitions { - statsTbl := handle.GetPartitionStats(table.Meta(), def.ID) - if i == 0 { - require.False(t, statsTbl.Pseudo) - require.Len(t, statsTbl.Columns, 3) - require.Len(t, statsTbl.Indices, 1) - } else { - require.True(t, statsTbl.Pseudo) - } - } - }) -} - -func TestAnalyzeReplicaReadFollower(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - ctx := tk.Session().(sessionctx.Context) - ctx.GetSessionVars().SetReplicaRead(kv.ReplicaReadFollower) - tk.MustExec("analyze table t") -} - -func TestClusterIndexAnalyze(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("drop database if exists test_cluster_index_analyze;") - tk.MustExec("create database test_cluster_index_analyze;") - tk.MustExec("use test_cluster_index_analyze;") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - - tk.MustExec("create table t (a int, b int, c int, primary key(a, b));") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?, ?, ?)", i, i, i) - } - tk.MustExec("analyze table t;") - tk.MustExec("drop table t;") - - tk.MustExec("create table t (a varchar(255), b int, c float, primary key(c, a));") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?, ?, ?)", strconv.Itoa(i), i, i) - } - tk.MustExec("analyze table t;") - tk.MustExec("drop table t;") - - tk.MustExec("create table t (a char(10), b decimal(5, 3), c int, primary key(a, c, b));") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?, ?, ?)", strconv.Itoa(i), i, i) - } - tk.MustExec("analyze table t;") - tk.MustExec("drop table t;") -} - -func TestAnalyzeRestrict(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - rs, err := tk.Session().ExecuteInternal(ctx, "analyze table t") - require.Nil(t, err) - require.Nil(t, rs) -} - -func TestAnalyzeParameters(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - for i := 0; i < 20; i++ { - tk.MustExec(fmt.Sprintf("insert into t values (%d)", i)) - } - tk.MustExec("insert into t values (19), (19), (19)") - - tk.MustExec("set @@tidb_analyze_version = 1") - tk.MustExec("analyze table t with 30 samples") - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - tbl := dom.StatsHandle().GetTableStats(tableInfo) - col := tbl.Columns[1] - require.Equal(t, 20, col.Len()) - require.Len(t, col.TopN.TopN, 1) - width, depth := col.CMSketch.GetWidthAndDepth() - require.Equal(t, int32(5), depth) - require.Equal(t, int32(2048), width) - - tk.MustExec("analyze table t with 4 buckets, 0 topn, 4 cmsketch width, 4 cmsketch depth") - tbl = dom.StatsHandle().GetTableStats(tableInfo) - col = tbl.Columns[1] - require.Equal(t, 4, col.Len()) - require.Nil(t, col.TopN) - width, depth = col.CMSketch.GetWidthAndDepth() - require.Equal(t, int32(4), depth) - require.Equal(t, int32(4), width) - - // Test very large cmsketch - tk.MustExec(fmt.Sprintf("analyze table t with %d cmsketch width, %d cmsketch depth", core.CMSketchSizeLimit, 1)) - tbl = dom.StatsHandle().GetTableStats(tableInfo) - col = tbl.Columns[1] - require.Equal(t, 20, col.Len()) - - require.Len(t, col.TopN.TopN, 1) - width, depth = col.CMSketch.GetWidthAndDepth() - require.Equal(t, int32(1), depth) - require.Equal(t, int32(core.CMSketchSizeLimit), width) - - // Test very large cmsketch - tk.MustExec("analyze table t with 20480 cmsketch width, 50 cmsketch depth") - tbl = dom.StatsHandle().GetTableStats(tableInfo) - col = tbl.Columns[1] - require.Equal(t, 20, col.Len()) - require.Len(t, col.TopN.TopN, 1) - width, depth = col.CMSketch.GetWidthAndDepth() - require.Equal(t, int32(50), depth) - require.Equal(t, int32(20480), width) -} - -func TestAnalyzeTooLongColumns(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a json)") - value := fmt.Sprintf(`{"x":"%s"}`, strings.Repeat("x", mysql.MaxFieldVarCharLength)) - tk.MustExec(fmt.Sprintf("insert into t values ('%s')", value)) - - tk.MustExec("set @@session.tidb_analyze_skip_column_types = ''") - tk.MustExec("analyze table t") - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - tbl := dom.StatsHandle().GetTableStats(tableInfo) - require.Equal(t, 0, tbl.Columns[1].Len()) - require.Equal(t, 0, tbl.Columns[1].TopN.Num()) - require.Equal(t, int64(65559), tbl.Columns[1].TotColSize) -} - -func TestAnlyzeIssue(t *testing.T) { - // Issue15993 - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t0") - tk.MustExec("CREATE TABLE t0(c0 INT PRIMARY KEY);") - tk.MustExec("ANALYZE TABLE t0 INDEX PRIMARY;") - // Issue15751 - tk.MustExec("drop table if exists t0") - tk.MustExec("CREATE TABLE t0(c0 INT, c1 INT, PRIMARY KEY(c0, c1))") - tk.MustExec("INSERT INTO t0 VALUES (0, 0)") - tk.MustExec("ANALYZE TABLE t0") - // Issue15752 - tk.MustExec("drop table if exists t0") - tk.MustExec("CREATE TABLE t0(c0 INT)") - tk.MustExec("INSERT INTO t0 VALUES (0)") - tk.MustExec("CREATE INDEX i0 ON t0(c0)") - tk.MustExec("ANALYZE TABLE t0 INDEX i0") -} - -func TestFailedAnalyzeRequest(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b int, index index_b(b))") - tk.MustExec("set @@tidb_analyze_version = 1") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/buildStatsFromResult", `return(true)`)) - _, err := tk.Exec("analyze table t") - require.NotNil(t, err) - require.Equal(t, "mock buildStatsFromResult error", err.Error()) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/buildStatsFromResult")) -} - -func TestExtractTopN(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database if not exists test_extract_topn") - tk.MustExec("use test_extract_topn") - tk.MustExec("drop table if exists test_extract_topn") - tk.MustExec("create table test_extract_topn(a int primary key, b int, index index_b(b))") - tk.MustExec("set @@session.tidb_analyze_version=2") - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("insert into test_extract_topn values (%d, %d)", i, i)) - } - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("insert into test_extract_topn values (%d, 0)", i+10)) - } - tk.MustExec("analyze table test_extract_topn") - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test_extract_topn"), model.NewCIStr("test_extract_topn")) - require.NoError(t, err) - tblInfo := table.Meta() - tblStats := dom.StatsHandle().GetTableStats(tblInfo) - colStats := tblStats.Columns[tblInfo.Columns[1].ID] - require.Len(t, colStats.TopN.TopN, 10) - item := colStats.TopN.TopN[0] - require.Equal(t, uint64(11), item.Count) - idxStats := tblStats.Indices[tblInfo.Indices[0].ID] - require.Len(t, idxStats.TopN.TopN, 10) - idxItem := idxStats.TopN.TopN[0] - require.Equal(t, uint64(11), idxItem.Count) - // The columns are: DBName, table name, column name, is index, value, count. - tk.MustQuery("show stats_topn where column_name in ('b', 'index_b')").Sort().Check(testkit.Rows("test_extract_topn test_extract_topn b 0 0 11", - "test_extract_topn test_extract_topn b 0 1 1", - "test_extract_topn test_extract_topn b 0 2 1", - "test_extract_topn test_extract_topn b 0 3 1", - "test_extract_topn test_extract_topn b 0 4 1", - "test_extract_topn test_extract_topn b 0 5 1", - "test_extract_topn test_extract_topn b 0 6 1", - "test_extract_topn test_extract_topn b 0 7 1", - "test_extract_topn test_extract_topn b 0 8 1", - "test_extract_topn test_extract_topn b 0 9 1", - "test_extract_topn test_extract_topn index_b 1 0 11", - "test_extract_topn test_extract_topn index_b 1 1 1", - "test_extract_topn test_extract_topn index_b 1 2 1", - "test_extract_topn test_extract_topn index_b 1 3 1", - "test_extract_topn test_extract_topn index_b 1 4 1", - "test_extract_topn test_extract_topn index_b 1 5 1", - "test_extract_topn test_extract_topn index_b 1 6 1", - "test_extract_topn test_extract_topn index_b 1 7 1", - "test_extract_topn test_extract_topn index_b 1 8 1", - "test_extract_topn test_extract_topn index_b 1 9 1", - )) -} - -func TestNormalAnalyzeOnCommonHandle(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3, t4") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("CREATE TABLE t1 (a int primary key, b int)") - tk.MustExec("insert into t1 values(1,1), (2,2), (3,3)") - tk.MustExec("CREATE TABLE t2 (a varchar(255) primary key, b int)") - tk.MustExec("insert into t2 values(\"111\",1), (\"222\",2), (\"333\",3)") - tk.MustExec("CREATE TABLE t3 (a int, b int, c int, primary key (a, b), key(c))") - tk.MustExec("insert into t3 values(1,1,1), (2,2,2), (3,3,3)") - - // Version2 is tested in TestStatsVer2. - tk.MustExec("set@@tidb_analyze_version=1") - tk.MustExec("analyze table t1, t2, t3") - - tk.MustQuery(`show stats_buckets where table_name in ("t1", "t2", "t3")`).Sort().Check(testkit.Rows( - "test t1 a 0 0 1 1 1 1 0", - "test t1 a 0 1 2 1 2 2 0", - "test t1 a 0 2 3 1 3 3 0", - "test t1 b 0 0 1 1 1 1 0", - "test t1 b 0 1 2 1 2 2 0", - "test t1 b 0 2 3 1 3 3 0", - "test t2 PRIMARY 1 0 1 1 111 111 0", - "test t2 PRIMARY 1 1 2 1 222 222 0", - "test t2 PRIMARY 1 2 3 1 333 333 0", - "test t2 a 0 0 1 1 111 111 0", - "test t2 a 0 1 2 1 222 222 0", - "test t2 a 0 2 3 1 333 333 0", - "test t2 b 0 0 1 1 1 1 0", - "test t2 b 0 1 2 1 2 2 0", - "test t2 b 0 2 3 1 3 3 0", - "test t3 PRIMARY 1 0 1 1 (1, 1) (1, 1) 0", - "test t3 PRIMARY 1 1 2 1 (2, 2) (2, 2) 0", - "test t3 PRIMARY 1 2 3 1 (3, 3) (3, 3) 0", - "test t3 a 0 0 1 1 1 1 0", - "test t3 a 0 1 2 1 2 2 0", - "test t3 a 0 2 3 1 3 3 0", - "test t3 b 0 0 1 1 1 1 0", - "test t3 b 0 1 2 1 2 2 0", - "test t3 b 0 2 3 1 3 3 0", - "test t3 c 0 0 1 1 1 1 0", - "test t3 c 0 1 2 1 2 2 0", - "test t3 c 0 2 3 1 3 3 0", - "test t3 c 1 0 1 1 1 1 0", - "test t3 c 1 1 2 1 2 2 0", - "test t3 c 1 2 3 1 3 3 0")) -} - -func TestDefaultValForAnalyze(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version=1") - defer tk.MustExec("set @@tidb_analyze_version=2") - originalSampleSize := executor.MaxRegionSampleSize - // Increase MaxRegionSampleSize to ensure all samples are collected for building histogram, otherwise the test will be unstable. - executor.MaxRegionSampleSize = 10000 - defer func() { - executor.MaxRegionSampleSize = originalSampleSize - }() - tk.MustExec("drop database if exists test_default_val_for_analyze;") - tk.MustExec("create database test_default_val_for_analyze;") - tk.MustExec("use test_default_val_for_analyze") - - tk.MustExec("create table t (a int, key(a));") - for i := 0; i < 256; i++ { - tk.MustExec("insert into t values (0),(0),(0),(0),(0),(0),(0),(0)") - } - for i := 1; i < 4; i++ { - tk.MustExec("insert into t values (?)", i) - } - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - tk.MustQuery("select @@tidb_enable_fast_analyze").Check(testkit.Rows("0")) - tk.MustQuery("select @@session.tidb_enable_fast_analyze").Check(testkit.Rows("0")) - tk.MustExec("analyze table t with 0 topn, 2 buckets, 10000 samples") - tk.MustQuery("explain format = 'brief' select * from t where a = 1").Check(testkit.Rows("IndexReader 512.00 root index:IndexRangeScan", - "└─IndexRangeScan 512.00 cop[tikv] table:t, index:a(a) range:[1,1], keep order:false")) - tk.MustQuery("explain format = 'brief' select * from t where a = 999").Check(testkit.Rows("IndexReader 0.00 root index:IndexRangeScan", - "└─IndexRangeScan 0.00 cop[tikv] table:t, index:a(a) range:[999,999], keep order:false")) - - tk.MustExec("drop table t;") - tk.MustExec("create table t (a int, key(a));") - for i := 0; i < 256; i++ { - tk.MustExec("insert into t values (0),(0),(0),(0),(0),(0),(0),(0)") - } - for i := 1; i < 2049; i += 8 { - vals := make([]string, 0, 8) - for j := i; j < i+8; j += 1 { - vals = append(vals, fmt.Sprintf("(%v)", j)) - } - tk.MustExec("insert into t values " + strings.Join(vals, ",")) - } - tk.MustExec("analyze table t with 0 topn;") - tk.MustQuery("explain format = 'brief' select * from t where a = 1").Check(testkit.Rows("IndexReader 1.00 root index:IndexRangeScan", - "└─IndexRangeScan 1.00 cop[tikv] table:t, index:a(a) range:[1,1], keep order:false")) -} - -func TestAnalyzeFullSamplingOnIndexWithVirtualColumnOrPrefixColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists sampling_index_virtual_col") - tk.MustExec("create table sampling_index_virtual_col(a int, b int as (a+1), index idx(b))") - tk.MustExec("insert into sampling_index_virtual_col (a) values (1), (2), (null), (3), (4), (null), (5), (5), (5), (5)") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("analyze table sampling_index_virtual_col with 1 topn") - tk.MustQuery("show stats_buckets where table_name = 'sampling_index_virtual_col' and column_name = 'idx'").Check(testkit.Rows( - "test sampling_index_virtual_col idx 1 0 1 1 2 2 0", - "test sampling_index_virtual_col idx 1 1 2 1 3 3 0", - "test sampling_index_virtual_col idx 1 2 3 1 4 4 0", - "test sampling_index_virtual_col idx 1 3 4 1 5 5 0")) - tk.MustQuery("show stats_topn where table_name = 'sampling_index_virtual_col' and column_name = 'idx'").Check(testkit.Rows("test sampling_index_virtual_col idx 1 6 4")) - row := tk.MustQuery(`show stats_histograms where db_name = "test" and table_name = "sampling_index_virtual_col"`).Rows()[0] - // The NDV. - require.Equal(t, "5", row[6]) - // The NULLs. - require.Equal(t, "2", row[7]) - tk.MustExec("drop table if exists sampling_index_prefix_col") - tk.MustExec("create table sampling_index_prefix_col(a varchar(3), index idx(a(1)))") - tk.MustExec("insert into sampling_index_prefix_col (a) values ('aa'), ('ab'), ('ac'), ('bb')") - tk.MustExec("analyze table sampling_index_prefix_col with 1 topn") - tk.MustQuery("show stats_buckets where table_name = 'sampling_index_prefix_col' and column_name = 'idx'").Check(testkit.Rows( - "test sampling_index_prefix_col idx 1 0 1 1 b b 0", - )) - tk.MustQuery("show stats_topn where table_name = 'sampling_index_prefix_col' and column_name = 'idx'").Check(testkit.Rows("test sampling_index_prefix_col idx 1 a 3")) -} - -func testSnapshotAnalyzeAndMaxTSAnalyzeHelper(analyzeSnapshot bool) func(t *testing.T) { - return func(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - if analyzeSnapshot { - tk.MustExec("set @@session.tidb_enable_analyze_snapshot = on") - } else { - tk.MustExec("set @@session.tidb_enable_analyze_snapshot = off") - } - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index index_a(a))") - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - tid := tblInfo.ID - tk.MustExec("insert into t values(1),(1),(1)") - tk.MustExec("begin") - txn, err := tk.Session().Txn(false) - require.NoError(t, err) - startTS1 := txn.StartTS() - tk.MustExec("commit") - tk.MustExec("insert into t values(2),(2),(2)") - tk.MustExec("begin") - txn, err = tk.Session().Txn(false) - require.NoError(t, err) - startTS2 := txn.StartTS() - tk.MustExec("commit") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS1))) - tk.MustExec("analyze table t") - rows := tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() - require.Len(t, rows, 1) - if analyzeSnapshot { - // Analyze cannot see the second insert if it reads the snapshot. - require.Equal(t, "3", rows[0][0]) - } else { - // Analyze can see the second insert if it reads the latest data. - require.Equal(t, "6", rows[0][0]) - } - s1Str := rows[0][1].(string) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS2))) - tk.MustExec("analyze table t") - rows = tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() - require.Len(t, rows, 1) - require.Equal(t, "6", rows[0][0]) - s2Str := rows[0][1].(string) - require.True(t, s1Str != s2Str) - tk.MustExec("set @@session.tidb_analyze_version = 2") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS1))) - tk.MustExec("analyze table t") - rows = tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() - require.Len(t, rows, 1) - require.Equal(t, "6", rows[0][0]) - s3Str := rows[0][1].(string) - // The third analyze doesn't write results into mysql.stats_xxx because its snapshot is smaller than the second analyze. - require.Equal(t, s2Str, s3Str) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot")) - } -} - -func TestSnapshotAnalyzeAndMaxTSAnalyze(t *testing.T) { - for _, analyzeSnapshot := range []bool{true, false} { - t.Run(fmt.Sprintf("%s-%t", t.Name(), analyzeSnapshot), testSnapshotAnalyzeAndMaxTSAnalyzeHelper(analyzeSnapshot)) - } -} - -func TestAdjustSampleRateNote(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - statsHandle := domain.GetDomain(tk.Session().(sessionctx.Context)).StatsHandle() - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index index_a(a))") - require.NoError(t, statsHandle.HandleDDLEvent(<-statsHandle.DDLEventCh())) - is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - tid := tblInfo.ID - tk.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 220000 where table_id=%d", tid)) - require.NoError(t, statsHandle.Update(is)) - result := tk.MustQuery("show stats_meta where table_name = 't'") - require.Equal(t, "220000", result.Rows()[0][5]) - tk.MustExec("analyze table t") - tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 0.500000 for table test.t, reason to use this rate is \"use min(1, 110000/220000) as the sample-rate=0.5\"")) - tk.MustExec("insert into t values(1),(1),(1)") - require.NoError(t, statsHandle.DumpStatsDeltaToKV(true)) - require.NoError(t, statsHandle.Update(is)) - result = tk.MustQuery("show stats_meta where table_name = 't'") - require.Equal(t, "3", result.Rows()[0][5]) - tk.MustExec("analyze table t") - tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) -} - -func TestAnalyzeIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (id int, v int, primary key(id), index k(v))") - tk.MustExec("insert into t1(id, v) values(1, 2), (2, 2), (3, 2), (4, 2), (5, 1), (6, 3), (7, 4)") - tk.MustExec("set @@tidb_analyze_version=1") - tk.MustExec("analyze table t1 index k") - require.Greater(t, len(tk.MustQuery("show stats_buckets where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 0) - tk.MustExec("set @@tidb_analyze_version=default") - tk.MustExec("analyze table t1") - require.Greater(t, len(tk.MustQuery("show stats_topn where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 0) - - tk.MustExec("drop stats t1") - tk.MustExec("set @@tidb_analyze_version=1") - tk.MustExec("analyze table t1 index k") - require.Greater(t, len(tk.MustQuery("show stats_buckets where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 1) -} - -func TestIssue20874(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("delete from mysql.stats_histograms") - tk.MustExec("create table t (a char(10) collate utf8mb4_unicode_ci not null, b char(20) collate utf8mb4_general_ci not null, key idxa(a), key idxb(b))") - tk.MustExec("insert into t values ('#', 'C'), ('$', 'c'), ('a', 'a')") - tk.MustExec("set @@tidb_analyze_version=1") - tk.MustExec("analyze table t") - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check(testkit.Rows( - "test t a 0 0 1 1 \x02\xd2 \x02\xd2 0", - "test t a 0 1 2 1 \x0e\x0f \x0e\x0f 0", - "test t a 0 2 3 1 \x0e3 \x0e3 0", - "test t b 0 0 1 1 \x00A \x00A 0", - "test t b 0 1 3 2 \x00C \x00C 0", - "test t idxa 1 0 1 1 \x02\xd2 \x02\xd2 0", - "test t idxa 1 1 2 1 \x0e\x0f \x0e\x0f 0", - "test t idxa 1 2 3 1 \x0e3 \x0e3 0", - "test t idxb 1 0 1 1 \x00A \x00A 0", - "test t idxb 1 1 3 2 \x00C \x00C 0", - )) - tk.MustQuery("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, correlation from mysql.stats_histograms").Sort().Check(testkit.Rows( - "0 1 3 0 9 1 1", - "0 2 2 0 9 1 -0.5", - "1 1 3 0 0 1 0", - "1 2 2 0 0 1 0", - )) - tk.MustExec("set @@tidb_analyze_version=2") - tk.MustExec("analyze table t") - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check(testkit.Rows( - "test t a 0 \x02\xd2 1", - "test t a 0 \x0e\x0f 1", - "test t a 0 \x0e3 1", - "test t b 0 \x00A 1", - "test t b 0 \x00C 2", - "test t idxa 1 \x02\xd2 1", - "test t idxa 1 \x0e\x0f 1", - "test t idxa 1 \x0e3 1", - "test t idxb 1 \x00A 1", - "test t idxb 1 \x00C 2", - )) - tk.MustQuery("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, correlation from mysql.stats_histograms").Sort().Check(testkit.Rows( - "0 1 3 0 6 2 1", - "0 2 2 0 6 2 -0.5", - "1 1 3 0 6 2 0", - "1 2 2 0 6 2 0", - )) -} - -func TestAnalyzeClusteredIndexPrimary(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t0") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t0(a varchar(20), primary key(a) clustered)") - tk.MustExec("create table t1(a varchar(20), primary key(a))") - tk.MustExec("insert into t0 values('1111')") - tk.MustExec("insert into t1 values('1111')") - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("analyze table t0 index primary") - tk.MustExec("analyze table t1 index primary") - tk.MustQuery("show stats_buckets").Check(testkit.Rows( - "test t0 PRIMARY 1 0 1 1 1111 1111 0", - "test t1 PRIMARY 1 0 1 1 1111 1111 0")) - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("analyze table t0") - tk.MustExec("analyze table t1") - tk.MustQuery("show stats_topn").Sort().Check(testkit.Rows(""+ - "test t0 PRIMARY 1 1111 1", - "test t0 a 0 1111 1", - "test t1 PRIMARY 1 1111 1", - "test t1 a 0 1111 1")) -} - -func TestAnalyzeSamplingWorkPanic(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)") - tk.MustExec("split table t between (-9223372036854775808) and (9223372036854775807) regions 12") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockAnalyzeSamplingBuildWorkerPanic", "return(1)")) - err := tk.ExecToErr("analyze table t") - require.NotNil(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockAnalyzeSamplingBuildWorkerPanic")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockAnalyzeSamplingMergeWorkerPanic", "return(1)")) - err = tk.ExecToErr("analyze table t") - require.NotNil(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockAnalyzeSamplingMergeWorkerPanic")) -} - -func TestSmallTableAnalyzeV2(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/calcSampleRateByStorageCount", "return(1)")) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("create table small_table_inject_pd(a int)") - tk.MustExec("insert into small_table_inject_pd values(1), (2), (3), (4), (5)") - tk.MustExec("analyze table small_table_inject_pd") - tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"")) - tk.MustExec(` -create table small_table_inject_pd_with_partition( - a int -) partition by range(a) ( - partition p0 values less than (5), - partition p1 values less than (10), - partition p2 values less than (15) -)`) - tk.MustExec("insert into small_table_inject_pd_with_partition values(1), (6), (11)") - tk.MustExec("analyze table small_table_inject_pd_with_partition") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p2, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - )) - rows := [][]interface{}{ - {"global", "a"}, - {"p0", "a"}, - {"p1", "a"}, - {"p2", "a"}, - } - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 'small_table_inject_pd_with_partition' and last_analyzed_at is not null").Sort().CheckAt([]int{2, 3}, rows) - rows = [][]interface{}{ - {"global", "0", "3"}, - {"p0", "0", "1"}, - {"p1", "0", "1"}, - {"p2", "0", "1"}, - } - tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 'small_table_inject_pd_with_partition'").Sort().CheckAt([]int{2, 4, 5}, rows) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/calcSampleRateByStorageCount")) -} - -func TestSavedAnalyzeOptions(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - originalVal2 := tk.MustQuery("select @@tidb_auto_analyze_ratio").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) - }() - tk.MustExec("set global tidb_auto_analyze_ratio = 0.01") - originalVal3 := autoanalyze.AutoAnalyzeMinCnt - defer func() { - autoanalyze.AutoAnalyzeMinCnt = originalVal3 - }() - autoanalyze.AutoAnalyzeMinCnt = 0 - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("create table t(a int, b int, c int, primary key(a), key idx(b))") - tk.MustExec("insert into t values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") - - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - tk.MustExec("analyze table t with 1 topn, 2 buckets") - is := dom.InfoSchema() - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - tbl := h.GetTableStats(tableInfo) - lastVersion := tbl.Version - col0 := tbl.Columns[tableInfo.Columns[0].ID] - require.Equal(t, 2, len(col0.Buckets)) - col1 := tbl.Columns[tableInfo.Columns[1].ID] - require.Equal(t, 1, len(col1.TopN.TopN)) - require.Equal(t, 2, len(col1.Buckets)) - col2 := tbl.Columns[tableInfo.Columns[2].ID] - require.Equal(t, 2, len(col2.Buckets)) - rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - - // auto-analyze uses the table-level options - tk.MustExec("insert into t values (10,10,10)") - require.Nil(t, h.DumpStatsDeltaToKV(true)) - require.Nil(t, h.Update(is)) - h.HandleAutoAnalyze(is) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - col0 = tbl.Columns[tableInfo.Columns[0].ID] - require.Equal(t, 2, len(col0.Buckets)) - - // manual analyze uses the table-level persisted options by merging the new options - tk.MustExec("analyze table t columns a,b with 1 samplerate, 3 buckets") - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - col0 = tbl.Columns[tableInfo.Columns[0].ID] - require.Equal(t, 3, len(col0.Buckets)) - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - col1 = tbl.Columns[tableInfo.Columns[1].ID] - require.Equal(t, 1, len(col1.TopN.TopN)) - col2 = tbl.Columns[tableInfo.Columns[2].ID] - require.Less(t, col2.LastUpdateVersion, col0.LastUpdateVersion) // not updated since removed from list - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "1", rs.Rows()[0][0]) - require.Equal(t, "3", rs.Rows()[0][1]) - require.Equal(t, "1", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - colIDStrs := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10)}, ",") - require.Equal(t, colIDStrs, rs.Rows()[0][4]) - - // disable option persistence - tk.MustExec("set global tidb_persist_analyze_options = false") - // manual analyze will neither use the pre-persisted options nor persist new options - tk.MustExec("analyze table t with 2 topn") - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - col0 = tbl.Columns[tableInfo.Columns[0].ID] - require.NotEqual(t, 3, len(col0.Buckets)) - rs = tk.MustQuery("select topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.NotEqual(t, "2", rs.Rows()[0][0]) -} - -func TestSavedPartitionAnalyzeOptions(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9),(10,10,10),(11,11,11),(12,12,12),(13,13,13),(14,14,14)") - - h := dom.StatsHandle() - - // analyze partition only sets options of partition - tk.MustExec("analyze table t partition p0 with 1 topn, 3 buckets") - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - lastVersion := p0.Version - require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "-1", rs.Rows()[0][1]) - - // merge partition & table level options - tk.MustExec("analyze table t columns a,b with 0 topn, 2 buckets") - p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - p1 := h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) - require.Greater(t, p0.Version, lastVersion) - lastVersion = p0.Version - require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 2, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) - // check column c is not analyzed - require.Less(t, p0.Columns[tableInfo.Columns[2].ID].LastUpdateVersion, p0.Columns[tableInfo.Columns[0].ID].LastUpdateVersion) - require.Less(t, p1.Columns[tableInfo.Columns[2].ID].LastUpdateVersion, p1.Columns[tableInfo.Columns[0].ID].LastUpdateVersion) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - colIDStrsAB := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10)}, ",") - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - - // analyze partition only updates this partition, and set different collist - tk.MustExec("analyze table t partition p1 columns a,c with 1 buckets") - p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - p1 = h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) - require.Equal(t, p0.Version, lastVersion) - require.Greater(t, p1.Version, lastVersion) - lastVersion = p1.Version - require.Equal(t, 1, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - // only column c of p1 is re-analyzed - require.Equal(t, 1, len(p1.Columns[tableInfo.Columns[2].ID].Buckets)) - require.NotEqual(t, 1, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) - colIDStrsABC := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10), strconv.FormatInt(tableInfo.Columns[2].ID, 10)}, ",") - rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "1", rs.Rows()[0][0]) - require.Equal(t, colIDStrsABC, rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][1]) - - // analyze partition without options uses saved partition options - tk.MustExec("analyze table t partition p0") - p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Greater(t, p0.Version, lastVersion) - lastVersion = p0.Version - require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - rs = tk.MustQuery("select buckets from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - rs = tk.MustQuery("select buckets from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - - // merge options of statement's, partition's and table's - tk.MustExec("analyze table t partition p0 with 3 buckets") - p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Greater(t, p0.Version, lastVersion) - require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "3", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - - // add new partitions, use table options as default - tk.MustExec("ALTER TABLE t ADD PARTITION (PARTITION p2 VALUES LESS THAN (30))") - tk.MustExec("insert into t values (21,21,21),(22,22,22),(23,23,23),(24,24,24)") - tk.MustExec("analyze table t partition p2") - is = dom.InfoSchema() - table, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = table.Meta() - pi = tableInfo.GetPartitionInfo() - p2 := h.GetPartitionStats(tableInfo, pi.Definitions[2].ID) - require.Equal(t, 2, len(p2.Columns[tableInfo.Columns[0].ID].Buckets)) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p2.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - require.Equal(t, "0", rs.Rows()[0][2]) - require.Equal(t, "LIST", rs.Rows()[0][3]) - require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) - - // set analyze version back to 1, will not use persisted - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("analyze table t partition p2") - pi = tableInfo.GetPartitionInfo() - p2 = h.GetPartitionStats(tableInfo, pi.Definitions[2].ID) - require.NotEqual(t, 2, len(p2.Columns[tableInfo.Columns[0].ID].Buckets)) - - // drop column - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("alter table t drop column b") - tk.MustExec("analyze table t") - colIDStrsA := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10)}, ",") - colIDStrsAC := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[2].ID, 10)}, ",") - rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, colIDStrsA, rs.Rows()[0][0]) - rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, colIDStrsA, rs.Rows()[0][0]) - rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, colIDStrsAC, rs.Rows()[0][0]) - - // drop partition - tk.MustExec("alter table t drop partition p1") - is = dom.InfoSchema() // refresh infoschema - require.Nil(t, h.GCStats(is, time.Duration(0))) - rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 1, len(rs.Rows())) - rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) - require.Equal(t, 0, len(rs.Rows())) - - // drop table - tk.MustExec("drop table t") - is = dom.InfoSchema() // refresh infoschema - require.Nil(t, h.GCStats(is, time.Duration(0))) - rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - //require.Equal(t, len(rs.Rows()), 0) TODO - rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) - require.Equal(t, 0, len(rs.Rows())) -} - -func TestSavedAnalyzeOptionsForMultipleTables(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - tk.MustExec("create table t1(a int, b int, c int, primary key(a), key idx(b))") - tk.MustExec("insert into t1 values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") - tk.MustExec("create table t2(a int, b int, c int, primary key(a), key idx(b))") - tk.MustExec("insert into t2 values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") - - h := dom.StatsHandle() - - tk.MustExec("analyze table t1 with 1 topn, 3 buckets") - tk.MustExec("analyze table t2 with 0 topn, 2 buckets") - tk.MustExec("analyze table t1,t2 with 2 topn") - is := dom.InfoSchema() - table1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - table2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tableInfo1 := table1.Meta() - tableInfo2 := table2.Meta() - tblStats1 := h.GetTableStats(tableInfo1) - tblStats2 := h.GetTableStats(tableInfo2) - tbl1Col0 := tblStats1.Columns[tableInfo1.Columns[0].ID] - tbl2Col0 := tblStats2.Columns[tableInfo2.Columns[0].ID] - require.Equal(t, 3, len(tbl1Col0.Buckets)) - require.Equal(t, 2, len(tbl2Col0.Buckets)) - rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo1.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo2.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) -} - -func TestSavedAnalyzeColumnOptions(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - originalVal2 := tk.MustQuery("select @@tidb_auto_analyze_ratio").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) - }() - tk.MustExec("set global tidb_auto_analyze_ratio = 0.01") - originalVal3 := autoanalyze.AutoAnalyzeMinCnt - defer func() { - autoanalyze.AutoAnalyzeMinCnt = originalVal3 - }() - autoanalyze.AutoAnalyzeMinCnt = 0 - originalVal4 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal4)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4)") - - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - tk.MustExec("select * from t where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - tk.MustExec("analyze table t predicate columns") - require.NoError(t, h.LoadNeededHistograms()) - tblStats := h.GetTableStats(tblInfo) - lastVersion := tblStats.Version - // column b is analyzed - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) - tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows("PREDICATE ")) - - tk.MustExec("select * from t where c > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - // manually analyze uses the saved option(predicate columns). - tk.MustExec("analyze table t") - require.NoError(t, h.LoadNeededHistograms()) - tblStats = h.GetTableStats(tblInfo) - require.Less(t, lastVersion, tblStats.Version) - lastVersion = tblStats.Version - // column b, c are analyzed - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) - - tk.MustExec("insert into t values (5,5,5),(6,6,6)") - require.Nil(t, h.DumpStatsDeltaToKV(true)) - require.Nil(t, h.Update(is)) - // auto analyze uses the saved option(predicate columns). - h.HandleAutoAnalyze(is) - tblStats = h.GetTableStats(tblInfo) - require.Less(t, lastVersion, tblStats.Version) - lastVersion = tblStats.Version - // column b, c are analyzed - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) - - tk.MustExec("analyze table t columns a") - tblStats = h.GetTableStats(tblInfo) - require.Less(t, lastVersion, tblStats.Version) - lastVersion = tblStats.Version - // column a is analyzed - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) - require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) - tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows(fmt.Sprintf("LIST %v", tblInfo.Columns[0].ID))) - - tk.MustExec("analyze table t all columns") - tblStats = h.GetTableStats(tblInfo) - require.Less(t, lastVersion, tblStats.Version) - lastVersion = tblStats.Version - // column a, b, c are analyzed - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) - require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) - tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows("ALL ")) -} - -func TestAnalyzeColumnsWithPrimaryKey(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int, c int primary key)") - tk.MustExec("insert into t values (1,1,1), (1,1,2), (2,2,3), (2,2,4), (3,3,5), (4,3,6), (5,4,7), (6,4,8), (null,null,9)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where a > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "a", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 2, len(rows)) - require.Equal(t, "a", rows[0][3]) - require.Equal(t, "c", rows[1][3]) - - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 9")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t a 0 1 2", - "test t a 0 2 2", - "test t c 0 1 1", - "test t c 0 2 1")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 6 1 8 2 1", - "0 2 0 0 8 0 0", // column b is not analyzed - "0 3 9 0 9 2 1", - )) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t a 0 0 3 1 3 5 0", - "test t a 0 1 4 1 6 6 0", - "test t c 0 0 4 1 3 6 0", - "test t c 0 1 7 1 7 9 0")) - }(val) - } -} - -func TestAnalyzeColumnsWithIndex(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int, c int, d int, index idx_b_d(b, d))") - tk.MustExec("insert into t values (1,1,null,1), (2,1,9,1), (1,1,8,1), (2,2,7,2), (1,3,7,3), (2,4,6,4), (1,4,6,5), (2,4,6,5), (1,5,6,5)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns b,d are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where c > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "c", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 3, len(rows)) - require.Equal(t, "b", rows[0][3]) - require.Equal(t, "c", rows[1][3]) - require.Equal(t, "d", rows[2][3]) - - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 9")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t b 0 1 3", - "test t b 0 4 3", - "test t c 0 6 4", - "test t c 0 7 2", - "test t d 0 1 3", - "test t d 0 5 3", - "test t idx_b_d 1 (1, 1) 3", - "test t idx_b_d 1 (4, 5) 2")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 0 0 9 0 0", // column a is not analyzed - "0 2 5 0 9 2 1", - "0 3 4 1 8 2 -0.07", - "0 4 5 0 9 2 1", - "1 1 6 0 18 2 0")) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t b 0 0 2 1 2 3 0", - "test t b 0 1 3 1 5 5 0", - "test t c 0 0 2 1 8 9 0", - "test t d 0 0 2 1 2 3 0", - "test t d 0 1 3 1 4 4 0", - "test t idx_b_d 1 0 3 1 (2, 2) (4, 4) 0", - "test t idx_b_d 1 1 4 1 (5, 5) (5, 5) 0")) - }(val) - } -} - -func TestAnalyzeColumnsWithClusteredIndex(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int, c int, d int, primary key(b, d) clustered)") - tk.MustExec("insert into t values (1,1,null,1), (2,2,9,2), (1,3,8,3), (2,4,7,4), (1,5,7,5), (2,6,6,6), (1,7,6,7), (2,8,6,8), (1,9,6,9)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns b,d are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where c > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "c", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 3, len(rows)) - require.Equal(t, "b", rows[0][3]) - require.Equal(t, "c", rows[1][3]) - require.Equal(t, "d", rows[2][3]) - - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 9")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t PRIMARY 1 (1, 1) 1", - "test t PRIMARY 1 (2, 2) 1", - "test t b 0 1 1", - "test t b 0 2 1", - "test t c 0 6 4", - "test t c 0 7 2", - "test t d 0 1 1", - "test t d 0 2 1")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 0 0 9 0 0", // column a is not analyzed - "0 2 9 0 9 2 1", - "0 3 4 1 8 2 -0.07", - "0 4 9 0 9 2 1", - "1 1 9 0 18 2 0")) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t PRIMARY 1 0 4 1 (3, 3) (6, 6) 0", - "test t PRIMARY 1 1 7 1 (7, 7) (9, 9) 0", - "test t b 0 0 4 1 3 6 0", - "test t b 0 1 7 1 7 9 0", - "test t c 0 0 2 1 8 9 0", - "test t d 0 0 4 1 3 6 0", - "test t d 0 1 7 1 7 9 0")) - }(val) - } -} - -func TestAnalyzeColumnsWithDynamicPartitionTable(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("create table t (a int, b int, c int, index idx(c)) partition by range (a) (partition p0 values less than (10), partition p1 values less than maxvalue)") - tk.MustExec("insert into t values (1,2,1), (2,4,1), (3,6,1), (4,8,2), (4,8,2), (5,10,3), (5,10,4), (5,10,5), (null,null,6), (11,22,7), (12,24,8), (13,26,9), (14,28,10), (15,30,11), (16,32,12), (16,32,13), (16,32,13), (16,32,14), (17,34,14), (17,34,14)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - defs := tbl.Meta().Partition.Definitions - p0ID := defs[0].ID - p1ID := defs[1].ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where a < 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 6, len(rows)) - require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) - require.Equal(t, []interface{}{"test", "t", "global", "c"}, rows[1][:4]) - require.Equal(t, []interface{}{"test", "t", "p0", "a"}, rows[2][:4]) - require.Equal(t, []interface{}{"test", "t", "p0", "c"}, rows[3][:4]) - require.Equal(t, []interface{}{"test", "t", "p1", "a"}, rows[4][:4]) - require.Equal(t, []interface{}{"test", "t", "p1", "c"}, rows[5][:4]) - - rows = tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 't'").Sort().Rows() - require.Equal(t, 3, len(rows)) - require.Equal(t, []interface{}{"test", "t", "global", "0", "20"}, append(rows[0][:3], rows[0][4:]...)) - require.Equal(t, []interface{}{"test", "t", "p0", "0", "9"}, append(rows[1][:3], rows[1][4:]...)) - require.Equal(t, []interface{}{"test", "t", "p1", "0", "11"}, append(rows[2][:3], rows[2][4:]...)) - - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t global a 0 16 4", - "test t global a 0 5 3", - "test t global c 0 1 3", - "test t global c 0 14 3", - "test t p0 a 0 4 2", - "test t p0 a 0 5 3", - "test t p0 c 0 1 3", - "test t p0 c 0 2 2", - "test t p1 a 0 16 4", - "test t p1 a 0 17 2", - "test t p1 c 0 13 2", - "test t p1 c 0 14 3")) - - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t global idx 1 1 3", - "test t global idx 1 14 3", - "test t p0 idx 1 1 3", - "test t p0 idx 1 2 2", - "test t p1 idx 1 13 2", - "test t p1 idx 1 14 3")) - - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t global a 0 0 5 2 1 4 0", - "test t global a 0 1 12 2 17 17 0", - "test t global c 0 0 6 1 2 6 0", - "test t global c 0 1 14 2 13 13 0", - "test t p0 a 0 0 2 1 1 2 0", - "test t p0 a 0 1 3 1 3 3 0", - "test t p0 c 0 0 3 1 3 5 0", - "test t p0 c 0 1 4 1 6 6 0", - "test t p1 a 0 0 3 1 11 13 0", - "test t p1 a 0 1 5 1 14 15 0", - "test t p1 c 0 0 4 1 7 10 0", - "test t p1 c 0 1 6 1 11 12 0")) - - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t global idx 1 0 6 1 2 6 0", - "test t global idx 1 1 14 2 13 13 0", - "test t p0 idx 1 0 3 1 3 5 0", - "test t p0 idx 1 1 4 1 6 6 0", - "test t p1 idx 1 0 4 1 7 10 0", - "test t p1 idx 1 1 6 1 11 12 0")) - - tk.MustQuery("select table_id, is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms order by table_id, is_index, hist_id asc").Check( - testkit.Rows(fmt.Sprintf("%d 0 1 12 1 19 2 0", tblID), // global, a - fmt.Sprintf("%d 0 3 14 0 20 2 0", tblID), // global, c - fmt.Sprintf("%d 1 1 14 0 0 2 0", tblID), // global, idx - fmt.Sprintf("%d 0 1 5 1 8 2 1", p0ID), // p0, a - fmt.Sprintf("%d 0 2 0 0 8 0 0", p0ID), // p0, b, not analyzed - fmt.Sprintf("%d 0 3 6 0 9 2 1", p0ID), // p0, c - fmt.Sprintf("%d 1 1 6 0 9 2 0", p0ID), // p0, idx - fmt.Sprintf("%d 0 1 7 0 11 2 1", p1ID), // p1, a - fmt.Sprintf("%d 0 2 0 0 11 0 0", p1ID), // p1, b, not analyzed - fmt.Sprintf("%d 0 3 8 0 11 2 1", p1ID), // p1, c - fmt.Sprintf("%d 1 1 8 0 11 2 0", p1ID), // p1, idx - )) - }(val) - } -} - -func TestIssue34228(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`USE test`) - tk.MustExec(`DROP TABLE IF EXISTS Issue34228`) - tk.MustExec(`CREATE TABLE Issue34228 (id bigint NOT NULL, dt datetime NOT NULL) PARTITION BY RANGE COLUMNS(dt) (PARTITION p202201 VALUES LESS THAN ("2022-02-01"), PARTITION p202202 VALUES LESS THAN ("2022-03-01"))`) - tk.MustExec(`INSERT INTO Issue34228 VALUES (1, '2022-02-01 00:00:02'), (2, '2022-02-01 00:00:02')`) - tk.MustExec(`SET @@global.tidb_analyze_version = 1`) - tk.MustExec(`SET @@session.tidb_partition_prune_mode = 'static'`) - tk.MustExec(`ANALYZE TABLE Issue34228`) - tk.MustExec(`SET @@session.tidb_partition_prune_mode = 'dynamic'`) - tk.MustExec(`ANALYZE TABLE Issue34228`) - tk.MustQuery(`SELECT * FROM Issue34228`).Sort().Check(testkit.Rows("1 2022-02-01 00:00:02", "2 2022-02-01 00:00:02")) - // Needs a second run to hit the issue - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec(`USE test`) - tk2.MustExec(`DROP TABLE IF EXISTS Issue34228`) - tk2.MustExec(`CREATE TABLE Issue34228 (id bigint NOT NULL, dt datetime NOT NULL) PARTITION BY RANGE COLUMNS(dt) (PARTITION p202201 VALUES LESS THAN ("2022-02-01"), PARTITION p202202 VALUES LESS THAN ("2022-03-01"))`) - tk2.MustExec(`INSERT INTO Issue34228 VALUES (1, '2022-02-01 00:00:02'), (2, '2022-02-01 00:00:02')`) - tk2.MustExec(`SET @@global.tidb_analyze_version = 1`) - tk2.MustExec(`SET @@session.tidb_partition_prune_mode = 'static'`) - tk2.MustExec(`ANALYZE TABLE Issue34228`) - tk2.MustExec(`SET @@session.tidb_partition_prune_mode = 'dynamic'`) - tk2.MustExec(`ANALYZE TABLE Issue34228`) - tk2.MustQuery(`SELECT * FROM Issue34228`).Sort().Check(testkit.Rows("1 2022-02-01 00:00:02", "2 2022-02-01 00:00:02")) -} - -func TestAnalyzeColumnsWithStaticPartitionTable(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustExec("create table t (a int, b int, c int, index idx(c)) partition by range (a) (partition p0 values less than (10), partition p1 values less than maxvalue)") - tk.MustExec("insert into t values (1,2,1), (2,4,1), (3,6,1), (4,8,2), (4,8,2), (5,10,3), (5,10,4), (5,10,5), (null,null,6), (11,22,7), (12,24,8), (13,26,9), (14,28,10), (15,30,11), (16,32,12), (16,32,13), (16,32,13), (16,32,14), (17,34,14), (17,34,14)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - defs := tbl.Meta().Partition.Definitions - p0ID := defs[0].ID - p1ID := defs[1].ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where a < 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 4, len(rows)) - require.Equal(t, []interface{}{"test", "t", "p0", "a"}, rows[0][:4]) - require.Equal(t, []interface{}{"test", "t", "p0", "c"}, rows[1][:4]) - require.Equal(t, []interface{}{"test", "t", "p1", "a"}, rows[2][:4]) - require.Equal(t, []interface{}{"test", "t", "p1", "c"}, rows[3][:4]) - - rows = tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 't'").Sort().Rows() - require.Equal(t, 2, len(rows)) - require.Equal(t, []interface{}{"test", "t", "p0", "0", "9"}, append(rows[0][:3], rows[0][4:]...)) - require.Equal(t, []interface{}{"test", "t", "p1", "0", "11"}, append(rows[1][:3], rows[1][4:]...)) - - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t p0 a 0 4 2", - "test t p0 a 0 5 3", - "test t p0 c 0 1 3", - "test t p0 c 0 2 2", - "test t p1 a 0 16 4", - "test t p1 a 0 17 2", - "test t p1 c 0 13 2", - "test t p1 c 0 14 3")) - - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t p0 idx 1 1 3", - "test t p0 idx 1 2 2", - "test t p1 idx 1 13 2", - "test t p1 idx 1 14 3")) - - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t p0 a 0 0 2 1 1 2 0", - "test t p0 a 0 1 3 1 3 3 0", - "test t p0 c 0 0 3 1 3 5 0", - "test t p0 c 0 1 4 1 6 6 0", - "test t p1 a 0 0 3 1 11 13 0", - "test t p1 a 0 1 5 1 14 15 0", - "test t p1 c 0 0 4 1 7 10 0", - "test t p1 c 0 1 6 1 11 12 0")) - - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t p0 idx 1 0 3 1 3 5 0", - "test t p0 idx 1 1 4 1 6 6 0", - "test t p1 idx 1 0 4 1 7 10 0", - "test t p1 idx 1 1 6 1 11 12 0")) - - tk.MustQuery("select table_id, is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms order by table_id, is_index, hist_id asc").Check( - testkit.Rows(fmt.Sprintf("%d 0 1 5 1 8 2 1", p0ID), // p0, a - fmt.Sprintf("%d 0 2 0 0 8 0 0", p0ID), // p0, b, not analyzed - fmt.Sprintf("%d 0 3 6 0 9 2 1", p0ID), // p0, c - fmt.Sprintf("%d 1 1 6 0 9 2 0", p0ID), // p0, idx - fmt.Sprintf("%d 0 1 7 0 11 2 1", p1ID), // p1, a - fmt.Sprintf("%d 0 2 0 0 11 0 0", p1ID), // p1, b, not analyzed - fmt.Sprintf("%d 0 3 8 0 11 2 1", p1ID), // p1, c - fmt.Sprintf("%d 1 1 8 0 11 2 0", p1ID), // p1, idx - )) - }(val) - } -} - -func TestAnalyzeColumnsWithExtendedStats(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_enable_extended_stats = on") - tk.MustExec("create table t (a int, b int, c int)") - tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") - tk.MustExec("insert into t values (5,1,1), (4,2,2), (3,3,3), (2,4,4), (1,5,5)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "b", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() - require.Equal(t, 2, len(rows)) - require.Equal(t, "b", rows[0][3]) - require.Equal(t, "c", rows[1][3]) - - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 5")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t b 0 1 1", - "test t b 0 2 1", - "test t c 0 1 1", - "test t c 0 2 1")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 0 0 5 0 0", // column a is not analyzed - "0 2 5 0 5 2 1", - "0 3 5 0 5 2 1", - )) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t b 0 0 2 1 3 4 0", - "test t b 0 1 3 1 5 5 0", - "test t c 0 0 2 1 3 4 0", - "test t c 0 1 3 1 5 5 0")) - rows = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, []interface{}{"test", "t", "s1", "[b,c]", "correlation", "1.000000"}, rows[0][:len(rows[0])-1]) - }(val) - } -} - -func TestAnalyzeColumnsWithVirtualColumnIndex(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int, c int as (b+1), index idx(c))") - tk.MustExec("insert into t (a,b) values (1,1), (2,2), (3,3), (4,4), (5,4), (6,5), (7,5), (8,5), (null,null)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - )) - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "b", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - // virtual column c is skipped when dumping stats into disk, so only the stats of column b are updated - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "b", rows[0][3]) - - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 9")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t b 0 4 2", - "test t b 0 5 3", - "test t idx 1 5 2", - "test t idx 1 6 3")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 0 0 0 0", // column a is not analyzed - "0 2 5 1 2 1", - "0 3 0 0 0 0", // column c is not analyzed - "1 1 5 1 2 0")) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t b 0 0 2 1 1 2 0", - "test t b 0 1 3 1 3 3 0", - "test t idx 1 0 2 1 2 3 0", - "test t idx 1 1 3 1 4 4 0")) - }(val) - } -} - -func TestAnalyzeColumnsAfterAnalyzeAll(t *testing.T) { - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t (a,b) values (1,1), (1,1), (2,2), (2,2), (3,3), (4,4)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblID := tbl.Meta().ID - - tk.MustExec("analyze table t with 2 topn, 2 buckets") - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 6")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t a 0 1 2", - "test t a 0 2 2", - "test t b 0 1 2", - "test t b 0 2 2")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 4 0 6 2 1", - "0 2 4 0 6 2 1")) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t a 0 0 2 1 3 4 0", - "test t b 0 0 2 1 3 4 0")) - - tk.MustExec("insert into t (a,b) values (1,1), (6,6)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, "b", rows[0][3]) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - } - - // Column a is not analyzed in second ANALYZE. We keep the outdated stats of column a rather than delete them. - tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 8")) - tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_idx, value, count - testkit.Rows("test t a 0 1 2", - "test t a 0 2 2", - "test t b 0 1 3", - "test t b 0 2 2")) - tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( - testkit.Rows("0 1 4 0 8 2 1", // tot_col_size of column a is updated to 8 by DumpStatsDeltaToKV - "0 2 5 0 8 2 0.76")) - tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( - // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv - testkit.Rows("test t a 0 0 2 1 3 4 0", - "test t b 0 0 2 1 3 4 0", - "test t b 0 1 3 1 6 6 0")) - tk.MustQuery(fmt.Sprintf("select hist_id from mysql.stats_histograms where version = (select version from mysql.stats_meta where table_id = %d)", tblID)).Check(testkit.Rows("2")) - }(val) - } -} - -func TestAnalyzeSampleRateReason(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) - - tk.MustExec(`analyze table t`) - tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows( - `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "use min(1, 110000/10000) as the sample-rate=1"`)) - - tk.MustExec(`insert into t values (1, 1), (2, 2), (3, 3)`) - require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) - tk.MustExec(`analyze table t`) - tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows( - `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "TiDB assumes that the table is empty, use sample-rate=1"`)) -} - -func TestAnalyzeColumnsErrorAndWarning(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - - // analyze version 1 doesn't support `ANALYZE COLUMNS c1, ..., cn`/`ANALYZE PREDICATE COLUMNS` currently - tk.MustExec("set @@tidb_analyze_version = 1") - err := tk.ExecToErr("analyze table t columns a") - require.Equal(t, "Only the version 2 of analyze supports analyzing the specified columns", err.Error()) - err = tk.ExecToErr("analyze table t predicate columns") - require.Equal(t, "Only the version 2 of analyze supports analyzing predicate columns", err.Error()) - - tk.MustExec("set @@tidb_analyze_version = 2") - // invalid column - err = tk.ExecToErr("analyze table t columns c") - terr := errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrAnalyzeMissColumn), terr.Code()) - - // If no predicate column is collected, analyze predicate columns gives a warning and falls back to analyze all columns. - tk.MustExec("analyze table t predicate columns") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "use min(1, 110000/10000) as the sample-rate=1"`, - "Warning 1105 No predicate column has been collected yet for table test.t so all columns are analyzed", - )) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Rows() - require.Equal(t, 2, len(rows)) - - for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { - func(choice model.ColumnChoice) { - tk.MustExec("set @@tidb_analyze_version = 1") - tk.MustExec("analyze table t") - tk.MustExec("set @@tidb_analyze_version = 2") - switch choice { - case model.ColumnList: - tk.MustExec("analyze table t columns b") - case model.PredicateColumns: - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b > 1") - require.NoError(t, dom.StatsHandle().DumpColStatsUsageToKV()) - tk.MustExec("analyze table t predicate columns") - } - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "TiDB assumes that the table is empty, use sample-rate=1"`, - "Warning 1105 Table test.t has version 1 statistics so all the columns must be analyzed to overwrite the current statistics", - )) - }(val) - } -} - -func checkAnalyzeStatus(t *testing.T, tk *testkit.TestKit, jobInfo, status, failReason, comment string, timeLimit int64) { - rows := tk.MustQuery("show analyze status where table_schema = 'test' and table_name = 't' and partition_name = ''").Rows() - require.Equal(t, 1, len(rows), comment) - require.Equal(t, jobInfo, rows[0][3], comment) - require.Equal(t, status, rows[0][7], comment) - require.Equal(t, failReason, rows[0][8], comment) - if timeLimit <= 0 { - return - } - const layout = time.DateTime - startTime, err := time.Parse(layout, rows[0][5].(string)) - require.NoError(t, err, comment) - endTime, err := time.Parse(layout, rows[0][6].(string)) - require.NoError(t, err, comment) - require.Less(t, endTime.Sub(startTime), time.Duration(timeLimit)*time.Second, comment) -} - -func testKillAutoAnalyze(t *testing.T, ver int) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - tk.MustExec(fmt.Sprintf("set @@tidb_analyze_version = %v", ver)) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (1,2), (3,4)") - is := dom.InfoSchema() - h := dom.StatsHandle() - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t") - tk.MustExec("insert into t values (5,6), (7,8), (9, 10)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - lastVersion := h.GetTableStats(tableInfo).Version - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - jobInfo := "auto analyze " - if ver == 1 { - jobInfo += "columns" - } else { - jobInfo += "table all columns with 256 buckets, 500 topn, 1 samplerate" - } - // kill auto analyze when it is pending/running/finished - for _, status := range []string{ - "pending", - "running", - "finished", - } { - func() { - comment := fmt.Sprintf("kill %v analyze job", status) - tk.MustExec("delete from mysql.analyze_jobs") - mockAnalyzeStatus := "github.com/pingcap/tidb/executor/mockKill" + strings.Title(status) - if status == "running" { - mockAnalyzeStatus += "V" + strconv.Itoa(ver) - } - mockAnalyzeStatus += "AnalyzeJob" - require.NoError(t, failpoint.Enable(mockAnalyzeStatus, "return")) - defer func() { - require.NoError(t, failpoint.Disable(mockAnalyzeStatus)) - }() - if status == "pending" || status == "running" { - mockSlowAnalyze := "github.com/pingcap/tidb/executor/mockSlowAnalyzeV" + strconv.Itoa(ver) - require.NoError(t, failpoint.Enable(mockSlowAnalyze, "return")) - defer func() { - require.NoError(t, failpoint.Disable(mockSlowAnalyze)) - }() - } - require.True(t, h.HandleAutoAnalyze(is), comment) - currentVersion := h.GetTableStats(tableInfo).Version - if status == "finished" { - // If we kill a finished job, after kill command the status is still finished and the table stats are updated. - checkAnalyzeStatus(t, tk, jobInfo, "finished", "", comment, -1) - require.Greater(t, currentVersion, lastVersion, comment) - } else { - // If we kill a pending/running job, after kill command the status is failed and the table stats are not updated. - // We expect the killed analyze stops quickly. Specifically, end_time - start_time < 10s. - checkAnalyzeStatus(t, tk, jobInfo, "failed", exeerrors.ErrQueryInterrupted.Error(), comment, 10) - require.Equal(t, currentVersion, lastVersion, comment) - } - }() - } -} - -func TestKillAutoAnalyzeV1(t *testing.T) { - testKillAutoAnalyze(t, 1) -} - -func TestKillAutoAnalyzeV2(t *testing.T) { - testKillAutoAnalyze(t, 2) -} - -func TestKillAutoAnalyzeIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - tk.MustExec("set @@tidb_analyze_version = 1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (1,2), (3,4)") - is := dom.InfoSchema() - h := dom.StatsHandle() - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t") - tk.MustExec("alter table t add index idx(b)") - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - lastVersion := h.GetTableStats(tblInfo).Version - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - const jobInfo = "auto analyze index idx" - // kill auto analyze when it is pending/running/finished - for _, status := range []string{"pending", "running", "finished"} { - func() { - comment := fmt.Sprintf("kill %v analyze job", status) - tk.MustExec("delete from mysql.analyze_jobs") - mockAnalyzeStatus := "github.com/pingcap/tidb/executor/mockKill" + strings.Title(status) - if status == "running" { - mockAnalyzeStatus += "AnalyzeIndexJob" - } else { - mockAnalyzeStatus += "AnalyzeJob" - } - require.NoError(t, failpoint.Enable(mockAnalyzeStatus, "return")) - defer func() { - require.NoError(t, failpoint.Disable(mockAnalyzeStatus)) - }() - if status == "pending" || status == "running" { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockSlowAnalyzeIndex", "return")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockSlowAnalyzeIndex")) - }() - } - require.True(t, h.HandleAutoAnalyze(dom.InfoSchema()), comment) - currentVersion := h.GetTableStats(tblInfo).Version - if status == "finished" { - // If we kill a finished job, after kill command the status is still finished and the index stats are updated. - checkAnalyzeStatus(t, tk, jobInfo, "finished", "", comment, -1) - require.Greater(t, currentVersion, lastVersion, comment) - } else { - // If we kill a pending/running job, after kill command the status is failed and the index stats are not updated. - // We expect the killed analyze stops quickly. Specifically, end_time - start_time < 10s. - checkAnalyzeStatus(t, tk, jobInfo, "failed", exeerrors.ErrQueryInterrupted.Error(), comment, 10) - require.Equal(t, currentVersion, lastVersion, comment) - } - }() - } -} - -func TestAnalyzeJob(t *testing.T) { - store := testkit.CreateMockStore(t) - for _, result := range []string{statistics.AnalyzeFinished, statistics.AnalyzeFailed} { - tk := testkit.NewTestKit(t, store) - tk.MustExec("delete from mysql.analyze_jobs") - se := tk.Session() - job := &statistics.AnalyzeJob{ - DBName: "test", - TableName: "t", - PartitionName: "", - JobInfo: "table all columns with 256 buckets, 500 topn, 1 samplerate", - } - executor.AddNewAnalyzeJob(se, job) - require.NotNil(t, job.ID) - rows := tk.MustQuery("show analyze status").Rows() - require.Len(t, rows, 1) - require.Equal(t, job.DBName, rows[0][0]) - require.Equal(t, job.TableName, rows[0][1]) - require.Equal(t, job.PartitionName, rows[0][2]) - require.Equal(t, job.JobInfo, rows[0][3]) - require.Equal(t, "0", rows[0][4]) - require.Equal(t, "", rows[0][5]) - require.Equal(t, "", rows[0][6]) - require.Equal(t, statistics.AnalyzePending, rows[0][7]) - require.Equal(t, "", rows[0][8]) - serverInfo, err := infosync.GetServerInfo() - require.NoError(t, err) - addr := fmt.Sprintf("%s:%d", serverInfo.IP, serverInfo.Port) - require.Equal(t, addr, rows[0][9]) - connID := strconv.FormatUint(tk.Session().GetSessionVars().ConnectionID, 10) - require.Equal(t, connID, rows[0][10]) - - executor.StartAnalyzeJob(se, job) - ctx := context.WithValue(context.Background(), executor.AnalyzeProgressTest, 100) - rows = tk.MustQueryWithContext(ctx, "show analyze status").Rows() - checkTime := func(val interface{}) { - str, ok := val.(string) - require.True(t, ok) - _, err := time.Parse(time.DateTime, str) - require.NoError(t, err) - } - checkTime(rows[0][5]) - require.Equal(t, statistics.AnalyzeRunning, rows[0][7]) - require.Equal(t, "9m0s", rows[0][11]) // REMAINING_SECONDS - require.Equal(t, "0.1", rows[0][12]) // PROGRESS - require.Equal(t, "0", rows[0][13]) // ESTIMATED_TOTAL_ROWS - - // UpdateAnalyzeJob requires the interval between two updates to mysql.analyze_jobs is more than 5 second. - // Hence we fake last dump time as 10 second ago in order to make update to mysql.analyze_jobs happen. - lastDumpTime := time.Now().Add(-10 * time.Second) - job.Progress.SetLastDumpTime(lastDumpTime) - const smallCount int64 = 100 - executor.UpdateAnalyzeJob(se, job, smallCount) - // Delta count doesn't reach threshold so we don't dump it to mysql.analyze_jobs - require.Equal(t, smallCount, job.Progress.GetDeltaCount()) - require.Equal(t, lastDumpTime, job.Progress.GetLastDumpTime()) - rows = tk.MustQuery("show analyze status").Rows() - require.Equal(t, "0", rows[0][4]) - - const largeCount int64 = 15000000 - executor.UpdateAnalyzeJob(se, job, largeCount) - // Delta count reaches threshold so we dump it to mysql.analyze_jobs and update last dump time. - require.Equal(t, int64(0), job.Progress.GetDeltaCount()) - require.True(t, job.Progress.GetLastDumpTime().After(lastDumpTime)) - lastDumpTime = job.Progress.GetLastDumpTime() - rows = tk.MustQuery("show analyze status").Rows() - require.Equal(t, strconv.FormatInt(smallCount+largeCount, 10), rows[0][4]) - - executor.UpdateAnalyzeJob(se, job, largeCount) - // We have just updated mysql.analyze_jobs in the previous step so we don't update it until 5 second passes or the analyze job is over. - require.Equal(t, largeCount, job.Progress.GetDeltaCount()) - require.Equal(t, lastDumpTime, job.Progress.GetLastDumpTime()) - rows = tk.MustQuery("show analyze status").Rows() - require.Equal(t, strconv.FormatInt(smallCount+largeCount, 10), rows[0][4]) - - var analyzeErr error - if result == statistics.AnalyzeFailed { - analyzeErr = errors.Errorf("analyze meets error") - } - executor.FinishAnalyzeJob(se, job, analyzeErr) - rows = tk.MustQuery("show analyze status").Rows() - require.Equal(t, strconv.FormatInt(smallCount+2*largeCount, 10), rows[0][4]) - checkTime(rows[0][6]) - require.Equal(t, result, rows[0][7]) - if result == statistics.AnalyzeFailed { - require.Equal(t, analyzeErr.Error(), rows[0][8]) - } else { - require.Equal(t, "", rows[0][8]) - } - // process_id is set to NULL after the analyze job is finished/failed. - require.Equal(t, "", rows[0][10]) - } -} - -func TestInsertAnalyzeJobWithLongInstance(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("delete from mysql.analyze_jobs") - job := &statistics.AnalyzeJob{ - DBName: "test", - TableName: "t", - PartitionName: "", - JobInfo: "table all columns with 256 buckets, 500 topn, 1 samplerate", - } - h := dom.StatsHandle() - instance := "xxxtidb-tidb-0.xxxtidb-tidb-peer.xxxx-xx-1234-xxx-123456-1-321.xyz:4000" - require.NoError(t, h.InsertAnalyzeJob(job, instance, 1)) - rows := tk.MustQuery("show analyze status").Rows() - require.Len(t, rows, 1) - require.Equal(t, instance, rows[0][9]) -} - -func TestShowAanalyzeStatusJobInfo(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - originalVal2 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal2)) - }() - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set global tidb_persist_analyze_options = 0") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, c int, d int, index idx_b_d(b, d))") - tk.MustExec("insert into t values (1,1,null,1), (2,1,9,1), (1,1,8,1), (2,2,7,2), (1,3,7,3), (2,4,6,4), (1,4,6,5), (2,4,6,5), (1,5,6,5)") - tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") - checkJobInfo := func(expected string) { - rows := tk.MustQuery("show analyze status where table_schema = 'test' and table_name = 't'").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, expected, rows[0][3]) - tk.MustExec("delete from mysql.analyze_jobs") - } - checkJobInfo("analyze table columns b, c, d with 2 buckets, 2 topn, 1 samplerate") - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where c > 1") - h := dom.StatsHandle() - require.NoError(t, h.DumpColStatsUsageToKV()) - tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") - checkJobInfo("analyze table columns b, c, d with 2 buckets, 2 topn, 1 samplerate") - tk.MustExec("analyze table t") - checkJobInfo("analyze table all columns with 256 buckets, 500 topn, 1 samplerate") - tk.MustExec("set global tidb_persist_analyze_options = 1") - tk.MustExec("analyze table t columns a with 1 topn, 3 buckets") - checkJobInfo("analyze table columns a, b, d with 3 buckets, 1 topn, 1 samplerate") - tk.MustExec("analyze table t") - checkJobInfo("analyze table columns a, b, d with 3 buckets, 1 topn, 1 samplerate") -} - -func TestAnalyzePartitionTableWithDynamicMode(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") - tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - - // analyze table only sets table options and gen globalStats - tk.MustExec("analyze table t columns a,c with 1 topn, 3 buckets") - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl := h.GetTableStats(tableInfo) - lastVersion := tbl.Version - // both globalStats and partition stats generated and options saved for column a,c - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) - rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - - // analyze table with persisted table-level options - tk.MustExec("analyze table t") - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - - // analyze table with merged table-level options - tk.MustExec("analyze table t with 2 topn, 2 buckets") - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) -} - -func TestAnalyzePartitionTableStaticToDynamic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") - tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - - // analyze partition under static mode with options - tk.MustExec("analyze table t partition p0 columns a,c with 1 topn, 3 buckets") - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl := h.GetTableStats(tableInfo) - p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - p1 := h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) - lastVersion := tbl.Version - require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) - require.Equal(t, 0, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, 0, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) - rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - // The columns are: table_id, sample_num, sample_rate, buckets, topn, column_choice, column_ids. - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - - // analyze table in dynamic mode will ignore partition-level options and use default - tk.MustExec("analyze table t") - tk.MustQuery("select * from t where b > 1 and c > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - p1 = h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) - require.NotEqual(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets), len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) - require.Equal(t, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets), len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "0", rs.Rows()[0][0]) - - // analyze table under dynamic mode with specified options with old partition-level options - tk.MustExec("analyze table t columns b,d with 2 topn, 2 buckets") - tk.MustQuery("select * from t where b > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[1].ID].Buckets)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, "2", rs.Rows()[0][1]) - - // analyze table under dynamic mode without options with old table-level & partition-level options - tk.MustExec("analyze table t") - tk.MustQuery("select * from t where b > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - lastVersion = tbl.Version - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].TopN.TopN)) - - // analyze table under dynamic mode with specified options with old table-level & partition-level options - tk.MustExec("analyze table t with 1 topn") - tk.MustQuery("select * from t where b > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[1].ID].Buckets)) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[1].ID].TopN.TopN)) - require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[3].ID].TopN.TopN)) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "3", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) - require.Equal(t, 0, len(rs.Rows())) - rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) - require.Equal(t, 1, len(rs.Rows())) - require.Equal(t, "2", rs.Rows()[0][0]) - require.Equal(t, "1", rs.Rows()[0][1]) -} - -func TestAnalyzePartitionUnderDynamic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - tk.MustExec("set global tidb_persist_analyze_options = true") - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") - tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - - // analyze partition with options under dynamic mode - tk.MustExec("analyze table t partition p0 columns a,b,c with 1 topn, 3 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", - )) - tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl := h.GetTableStats(tableInfo) - lastVersion := tbl.Version - require.NotEqual(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) - require.NotEqual(t, 3, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) - - tk.MustExec("analyze table t partition p0") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/9) as the sample-rate=1\"", - )) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) // global stats updated -} - -func TestAnalyzePartitionStaticToDynamic(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("set @@session.tidb_skip_missing_partition_stats = 0") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") - tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - - // generate old partition stats - tk.MustExec("set global tidb_persist_analyze_options = false") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - tk.MustExec("analyze table t partition p0 columns a,c with 1 topn, 3 buckets") - tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) - - // analyze partition with existing stats of other partitions under dynamic - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", - )) - - // analyze partition with existing table-level options and existing partition stats under dynamic - tk.MustExec("insert into mysql.analyze_options values (?,?,?,?,?,?,?)", tableInfo.ID, 0, 0, 2, 2, "DEFAULT", "") - tk.MustExec("set global tidb_persist_analyze_options = true") - tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/5) as the sample-rate=1\"", - "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", - "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", - )) - - // analyze partition with existing table-level & partition-level options and existing partition stats under dynamic - tk.MustExec("insert into mysql.analyze_options values (?,?,?,?,?,?,?)", pi.Definitions[1].ID, 0, 0, 1, 1, "DEFAULT", "") - tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/5) as the sample-rate=1\"", - "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", - "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", - )) - // flaky test, fix it later - //tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") - //require.NoError(t, h.LoadNeededHistograms()) - //tbl := h.GetTableStats(tableInfo) - //require.Equal(t, 0, len(tbl.Columns)) - - // ignore both p0's 3 buckets, persisted-partition-options' 1 bucket, just use table-level 2 buckets - tk.MustExec("analyze table t partition p0") - tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl := h.GetTableStats(tableInfo) - require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) -} - -func TestAnalyzePartitionUnderV1Dynamic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) - }() - - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") - tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - - // analyze partition with index and with options are allowed under dynamic V1 - tk.MustExec("analyze table t partition p0 with 1 topn, 3 buckets") - rows := tk.MustQuery("show warnings").Rows() - require.Len(t, rows, 0) - tk.MustExec("analyze table t partition p1 with 1 topn, 3 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows()) - tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") - require.NoError(t, h.LoadNeededHistograms()) - tbl := h.GetTableStats(tableInfo) - lastVersion := tbl.Version - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) - require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) - - tk.MustExec("analyze table t partition p1 index idx with 1 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows()) - tbl = h.GetTableStats(tableInfo) - require.Greater(t, tbl.Version, lastVersion) - require.Equal(t, 2, len(tbl.Indices[tableInfo.Indices[0].ID].Buckets)) -} - -func TestIssue35056(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 1") - createTable := `CREATE TABLE t (id int, a int, b varchar(10)) -PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(7,7,7),(9,9,9)") - tk.MustExec("insert into t values (11,11,11),(12,12,12),(14,14,14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - h.HandleAutoAnalyze(is) - tk.MustExec("create index idxa on t (a)") - tk.MustExec("create index idxb on t (b)") - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - tk.MustExec("analyze table t partition p0 index idxa") - tk.MustExec("analyze table t partition p1 index idxb") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t partition p0") // no panic -} - -func TestIssue35056Related(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - createTable := `CREATE TABLE t (id int) -PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - tk.MustExec("insert into t values (1),(2),(3),(4),(7),(9)") - tk.MustExec("insert into t values (11),(12),(14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - h.HandleAutoAnalyze(is) - tk.MustExec("alter table t add column a int") - tk.MustExec("alter table t add column b int") - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - tk.MustExec("analyze table t partition p0 columns id,a") - tk.MustExec("analyze table t partition p1 columns id,b") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t partition p0") // no panic -} - -func TestIssue35044(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") - createTable := `CREATE TABLE t (a int) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (20) -)` - tk.MustExec(createTable) - tk.MustExec("insert into t values (1),(2),(3)") - tk.MustExec("insert into t values (11),(12),(14)") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - is := dom.InfoSchema() - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - require.NotNil(t, pi) - tk.MustExec("analyze table t partition p0 columns a") - tk.MustExec("analyze table t partition p1 columns a") - tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t partition p0") - tbl := h.GetTableStats(tableInfo) - require.Equal(t, int64(6), tbl.Columns[tableInfo.Columns[0].ID].Histogram.NDV) -} - -func TestAutoAnalyzeAwareGlobalVariableChange(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) - // We want to test that HandleAutoAnalyze is aware of setting @@global.tidb_enable_analyze_snapshot to 1 and reads data from snapshot. - tk.MustExec("set @@global.tidb_enable_analyze_snapshot = 1") - tk.MustExec("set @@global.tidb_analyze_version = 2") - tk.MustExec("create table t(a int)") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tid := tbl.Meta().ID - tk.MustExec("insert into t values(1),(2),(3)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - err = h.Update(dom.InfoSchema()) - require.NoError(t, err) - tk.MustExec("analyze table t") - tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( - "3 0", - )) - - originalVal1 := autoanalyze.AutoAnalyzeMinCnt - originalVal2 := tk.MustQuery("select @@global.tidb_auto_analyze_ratio").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - tk.MustExec("set global tidb_auto_analyze_ratio = 0.001") - defer func() { - autoanalyze.AutoAnalyzeMinCnt = originalVal1 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) - }() - - tk.MustExec("begin") - txn, err := tk.Session().Txn(false) - require.NoError(t, err) - startTS := txn.StartTS() - tk.MustExec("commit") - - tk.MustExec("insert into t values(4),(5),(6)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - err = h.Update(dom.InfoSchema()) - require.NoError(t, err) - - // Simulate that the analyze would start before and finish after the second insert. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectBaseCount", "return(3)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectBaseModifyCount", "return(0)")) - require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) - // Check the count / modify_count changes during the analyze are not lost. - tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( - "6 3", - )) - // Check the histogram is correct for the snapshot analyze. - tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( - "3", - )) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectBaseCount")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectBaseModifyCount")) -} - -func TestAnalyzeColumnsSkipMVIndexJsonCol(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("create table t (a int, b int, c json, index idx_b(b), index idx_c((cast(json_extract(c, _utf8mb4'$') as char(32) array))))") - tk.MustExec(`insert into t values (1, 1, '["a1", "a2"]'), (2, 2, '["b1", "b2"]'), (3, 3, '["c1", "c2"]'), (2, 2, '["c1", "c2"]')`) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - tk.MustExec("analyze table t columns a") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows(""+ - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Columns b are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", - "Warning 1105 analyzing multi-valued indexes is not supported, skip idx_c")) - tk.MustQuery("select job_info from mysql.analyze_jobs where table_schema = 'test' and table_name = 't'").Check(testkit.Rows( - "analyze table columns a, b with 256 buckets, 500 topn, 1 samplerate")) - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - stats := h.GetTableStats(tblInfo) - require.True(t, stats.Columns[tblInfo.Columns[0].ID].IsStatsInitialized()) - require.True(t, stats.Columns[tblInfo.Columns[1].ID].IsStatsInitialized()) - require.False(t, stats.Columns[tblInfo.Columns[2].ID].IsStatsInitialized()) - require.True(t, stats.Indices[tblInfo.Indices[0].ID].IsStatsInitialized()) - require.False(t, stats.Indices[tblInfo.Indices[1].ID].IsStatsInitialized()) -} - -func TestManualAnalyzeSkipColumnTypes(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c json, d text, e mediumtext, f blob, g mediumblob, index idx(d(10)))") - tk.MustExec("set @@session.tidb_analyze_skip_column_types = 'json,blob,mediumblob,text,mediumtext'") - tk.MustExec("analyze table t") - tk.MustQuery("select job_info from mysql.analyze_jobs where job_info like '%analyze table%'").Check(testkit.Rows("analyze table columns a, b, d with 256 buckets, 500 topn, 1 samplerate")) - tk.MustExec("delete from mysql.analyze_jobs") - tk.MustExec("analyze table t columns a, e") - tk.MustQuery("select job_info from mysql.analyze_jobs where job_info like '%analyze table%'").Check(testkit.Rows("analyze table columns a, d with 256 buckets, 500 topn, 1 samplerate")) -} - -// TestAnalyzeMVIndex tests analyzing the mv index use some real data in the table. -// It checks the analyze jobs, async loading and the stats content in the memory. -func TestAnalyzeMVIndex(t *testing.T) { - t.Skip() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/DebugAnalyzeJobOperations", "return(true)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/statistics/handle/DebugAnalyzeJobOperations", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/DebugAnalyzeJobOperations")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/statistics/handle/DebugAnalyzeJobOperations")) - }() - // 1. prepare the table and insert data - store, dom := testkit.CreateMockStoreAndDomain(t) - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { - h.SetLease(oriLease) - }() - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, j json, index ia(a)," + - "index ij_signed((cast(j->'$.signed' as signed array)))," + - "index ij_unsigned((cast(j->'$.unsigned' as unsigned array)))," + - // date currently incompatible with mysql - //"index ij_date((cast(j->'$.dt' as date array)))," + - // datetime currently incompatible with mysql - //"index ij_datetime((cast(j->'$.dttm' as datetime(6) array)))," + - // time currently incompatible with mysql - //"index ij_time((cast(j->'$.tm' as time(6) array)))," + - "index ij_double((cast(j->'$.dbl' as double array)))," + - // decimal not supported yet - //"index ij_decimal((cast(j->'$.dcm' as decimal(15,5) array)))," + - "index ij_binary((cast(j->'$.bin' as binary(50) array)))," + - "index ij_char((cast(j->'$.char' as char(50) array)))" + - ")") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - jsonData := []map[string]interface{}{ - { - "signed": []int64{1, 2, 300, 300, 0, 4, 5, -40000}, - "unsigned": []uint64{0, 3, 4, 600, 12}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16"}, - "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, -1000.56789}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "yuiop", "1234", "5678", "0000", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, - }, - { - "signed": []int64{1, 2, 300, 300, 0, 4, 5, -40000}, - "unsigned": []uint64{0, 3, 4, 600, 12}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10"}, - "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "ghjk", "0000", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, - }, - { - "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, - "unsigned": []uint64{0, 3, 4, 600, 3112}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2340-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, - "dbl": []float64{-21.5, 2.15, 10.555555, -12.000005, 0.00, 10.9876}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "1234", "0000", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, - }, - { - "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, - "unsigned": []uint64{0, 3, 4, 600, 3112}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2110-01-01 18:17:16", "2340-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, - "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, -123.654}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "0000", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "k!@cvd", "yuiop", "%*$%#@qwe"}, - }, - { - "signed": []int64{1, 2, 300, -300, 0, 100, -5, 13245}, - "unsigned": []uint64{0, 3, 4, 600, 3112}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2110-01-01 22:17:16", "2340-01-22 15:16:17", "1950-01-01 00:12:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:5.10", "12:4:43"}, - "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "3796", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "kicvd", "yuiop", "%*asdf@"}, - }, - { - "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, - "unsigned": []uint64{0, 3, 4, 600, 3112}, - "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, - "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, - "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, - "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, - "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, - "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "0000", "zzzz"}, - "char": []string{"aaa", "cccccc", "eee", "asdf", "k!@cvd", "yuiop", "%*$%#@qwe"}, - }, - } - for i := 0; i < 3; i++ { - jsonValue := jsonData[i] - jsonValueStr, err := json.Marshal(jsonValue) - require.NoError(t, err) - tk.MustExec(fmt.Sprintf("insert into t values (%d, '%s')", 1, jsonValueStr)) - } - tk.MustExec("insert into t select * from t") - tk.MustExec("insert into t select * from t") - tk.MustExec("insert into t select * from t") - for i := 3; i < 6; i++ { - jsonValue := jsonData[i] - jsonValueStr, err := json.Marshal(jsonValue) - require.NoError(t, err) - tk.MustExec(fmt.Sprintf("insert into t values (%d, '%s')", 1, jsonValueStr)) - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - - // 2. analyze and check analyze jobs - tk.MustExec("analyze table t with 1 samplerate, 3 topn") - tk.MustQuery("select id, table_schema, table_name, partition_name, job_info, processed_rows, state from mysql.analyze_jobs order by id"). - Check(testkit.Rows("1 test t analyze table columns a with 256 buckets, 3 topn, 1 samplerate 27 finished", - "2 test t analyze index ij_signed 190 finished", - "3 test t analyze index ij_unsigned 135 finished", - "4 test t analyze index ij_double 154 finished", - "5 test t analyze index ij_binary 259 finished", - "6 test t analyze index ij_char 189 finished", - )) - - // 3. test stats loading - // 3.1. turn off sync loading, stats on all indexes should be allEvicted, but these queries should trigger async loading - tk.MustExec("set session tidb_stats_load_sync_wait = 0") - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, ij_signed:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_signed:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, ij_unsigned:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_unsigned:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where 10.01 member of (j->'$.dbl')").Check(testkit.Rows( - "TableReader 21.60 root data:Selection", - "└─Selection 21.60 cop[tikv] json_memberof(cast(10.01, json BINARY), json_extract(test.t.j, \"$.dbl\"))", - " └─TableFullScan 27.00 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, ij_binary:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_binary:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, ij_char:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_char:allEvicted, j:unInitialized]", - )) - // 3.2. emulate the background async loading - require.NoError(t, h.LoadNeededHistograms()) - // 3.3. now, stats on all indexes should be loaded - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where 10.01 member of (j->'$.dbl')").Check(testkit.Rows( - "TableReader 21.60 root data:Selection", - "└─Selection 21.60 cop[tikv] json_memberof(cast(10.01, json BINARY), json_extract(test.t.j, \"$.dbl\"))", - " └─TableFullScan 27.00 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", - )) - - // 3.4. clean up the stats and re-analyze the table - tk.MustExec("drop stats t") - tk.MustExec("analyze table t with 1 samplerate, 3 topn") - // 3.5. turn on the sync loading, stats on mv indexes should be loaded - tk.MustExec("set session tidb_stats_load_sync_wait = 1000") - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - )) - tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( - "IndexMerge 0.03 root type: union", - "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", - )) - - // 4. check stats content in the memory - require.NoError(t, h.LoadNeededHistograms()) - tk.MustQuery("show stats_meta").CheckAt([]int{0, 1, 4, 5}, testkit.Rows("test t 0 27")) - tk.MustQuery("show stats_histograms").CheckAt([]int{0, 1, 3, 4, 6, 7, 8, 9, 10}, testkit.Rows( - // db_name, table_name, column_name, is_index, distinct_count, null_count, avg_col_size, correlation, load_status - "test t a 0 1 0 1 1 allEvicted", - "test t ia 1 1 0 0 0 allLoaded", - "test t ij_signed 1 11 0 0 0 allLoaded", - "test t ij_unsigned 1 6 0 0 0 allLoaded", - "test t ij_double 1 7 0 0 0 allLoaded", - "test t ij_binary 1 15 0 0 0 allLoaded", - "test t ij_char 1 11 0 0 0 allLoaded", - )) - tk.MustQuery("show stats_topn").Check(testkit.Rows( - // db_name, table_name, partition_name, column_name, is_index, value, count - "test t ia 1 1 27", - "test t ij_signed 1 -40000 16", - "test t ij_signed 1 -300 1", - "test t ij_signed 1 -5 11", - "test t ij_unsigned 1 0 27", - "test t ij_unsigned 1 3 27", - "test t ij_unsigned 1 4 27", - "test t ij_double 1 -21.5 27", - "test t ij_double 1 -12.000005 8", - "test t ij_double 1 0 27", - "test t ij_binary 1 0000 26", - "test t ij_binary 1 1234 19", - "test t ij_binary 1 3796 1", - "test t ij_char 1 !@#$ 24", - "test t ij_char 1 %*$%#@qwe 2", - "test t ij_char 1 %*asdf@ 1", - )) - tk.MustQuery("show stats_buckets").Check(testkit.Rows( - // db_name, table_name, partition_name, column_name, is_index, bucket_id, count, repeats, lower_bound, upper_bound, ndv - "test t ij_signed 1 0 27 27 0 0 0", - "test t ij_signed 1 1 54 27 1 1 0", - "test t ij_signed 1 2 81 27 2 2 0", - "test t ij_signed 1 3 107 26 4 4 0", - "test t ij_signed 1 4 123 16 5 5 0", - "test t ij_signed 1 5 124 1 100 100 0", - "test t ij_signed 1 6 151 27 300 300 0", - "test t ij_signed 1 7 162 11 13245 13245 0", - "test t ij_unsigned 1 0 16 16 12 12 0", - "test t ij_unsigned 1 1 43 27 600 600 0", - "test t ij_unsigned 1 2 54 11 3112 3112 0", - "test t ij_double 1 0 19 19 0.000005 0.000005 0", - "test t ij_double 1 1 46 27 2.15 2.15 0", - "test t ij_double 1 2 73 27 10.555555 10.555555 0", - "test t ij_double 1 3 92 19 10.9876 10.9876 0", - "test t ij_binary 1 0 8 8 5678 5678 0", - "test t ij_binary 1 1 35 27 aaaaaa aaaaaa 0", - "test t ij_binary 1 2 59 24 asdf asdf 0", - "test t ij_binary 1 3 86 27 bbbb bbbb 0", - "test t ij_binary 1 4 113 27 ccc ccc 0", - "test t ij_binary 1 5 116 3 egfb egfb 0", - "test t ij_binary 1 6 124 8 ghjk ghjk 0", - "test t ij_binary 1 7 127 3 nfre nfre 0", - "test t ij_binary 1 8 154 27 ppp ppp 0", - "test t ij_binary 1 9 178 24 qwer qwer 0", - "test t ij_binary 1 10 186 8 yuiop yuiop 0", - "test t ij_binary 1 11 213 27 zzzz zzzz 0", - "test t ij_char 1 0 27 27 aaa aaa 0", - "test t ij_char 1 1 54 27 asdf asdf 0", - "test t ij_char 1 2 81 27 cccccc cccccc 0", - "test t ij_char 1 3 108 27 eee eee 0", - "test t ij_char 1 4 110 2 k!@cvd k!@cvd 0", - "test t ij_char 1 5 111 1 kicvd kicvd 0", - "test t ij_char 1 6 135 24 qwer qwer 0", - "test t ij_char 1 7 162 27 yuiop yuiop 0", - )) -} - -func TestAnalyzePartitionVerify(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - sql := "create table t(a int,b varchar(100),c int,INDEX idx_c(c)) PARTITION BY RANGE ( a ) (" - for n := 100; n < 1000; n = n + 100 { - sql += "PARTITION p" + fmt.Sprint(n) + " VALUES LESS THAN (" + fmt.Sprint(n) + ")," - } - sql += "PARTITION p" + fmt.Sprint(1000) + " VALUES LESS THAN MAXVALUE)" - tk.MustExec(sql) - // insert random data into table t - insertStr := "insert into t (a,b,c) values(0, 'abc', 0)" - for i := 1; i < 1000; i++ { - insertStr += fmt.Sprintf(" ,(%d, '%s', %d)", i, "abc", i) - } - insertStr += ";" - tk.MustExec(insertStr) - tk.MustExec("analyze table t") - - result := tk.MustQuery("show stats_histograms where Db_name='test'").Sort() - require.NotNil(t, result) - require.Len(t, result.Rows(), 4+4*10) // 4 columns * 10 partiion+ 4 global columns - for _, row := range result.Rows() { - if row[2] == "global" { - if row[3] == "b" { - // global column b has 1 distinct value - require.Equal(t, "1", row[6]) - } else { - require.Equal(t, "1000", row[6]) - } - } else { - if row[3] == "b" { - require.Equal(t, "1", row[6]) - } else { - require.Equal(t, "100", row[6]) - } - } - } -} diff --git a/executor/test/analyzetest/main_test.go b/executor/test/analyzetest/main_test.go deleted file mode 100644 index 1141e30df2af8..0000000000000 --- a/executor/test/analyzetest/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package analyzetest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/analyzetest/memorycontrol/BUILD.bazel b/executor/test/analyzetest/memorycontrol/BUILD.bazel deleted file mode 100644 index ee4e779892b7a..0000000000000 --- a/executor/test/analyzetest/memorycontrol/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "memorycontrol_test", - timeout = "short", - srcs = [ - "main_test.go", - "memory_control_test.go", - ], - flaky = True, - shard_count = 3, - deps = [ - "//config", - "//executor", - "//sessionctx/variable", - "//statistics/handle/autoanalyze", - "//testkit", - "//util", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/analyzetest/memorycontrol/main_test.go b/executor/test/analyzetest/memorycontrol/main_test.go deleted file mode 100644 index 8efcfe4ba08b1..0000000000000 --- a/executor/test/analyzetest/memorycontrol/main_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package memorycontrol - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx/variable" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - variable.StatsCacheMemQuota.Store(1000000) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/autoidtest/BUILD.bazel b/executor/test/autoidtest/BUILD.bazel deleted file mode 100644 index e6d04505022cb..0000000000000 --- a/executor/test/autoidtest/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "autoidtest_test", - timeout = "moderate", - srcs = [ - "autoid_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 10, - deps = [ - "//autoid_service", - "//config", - "//ddl/testutil", - "//meta/autoid", - "//parser/mysql", - "//session", - "//sessionctx/variable", - "//testkit", - "//testkit/testutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/autoidtest/autoid_test.go b/executor/test/autoidtest/autoid_test.go deleted file mode 100644 index ce94f8e8656df..0000000000000 --- a/executor/test/autoidtest/autoid_test.go +++ /dev/null @@ -1,808 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package autoid_test - -import ( - "context" - "fmt" - "strconv" - "strings" - "testing" - - "github.com/pingcap/failpoint" - _ "github.com/pingcap/tidb/autoid_service" - ddltestutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/stretchr/testify/require" -) - -// Test filter different kind of allocators. -// In special ddl type, for example: -// 1: ActionRenameTable : it will abandon all the old allocators. -// 2: ActionRebaseAutoID : it will drop row-id-type allocator. -// 3: ActionModifyTableAutoIdCache : it will drop row-id-type allocator. -// 3: ActionRebaseAutoRandomBase : it will drop auto-rand-type allocator. -func TestFilterDifferentAllocators(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - - for _, str := range []string{"", " AUTO_ID_CACHE 1"} { - tk.MustExec("create table t(a bigint auto_random(5) key, b int auto_increment unique)" + str) - tk.MustExec("insert into t values()") - tk.MustQuery("select b from t").Check(testkit.Rows("1")) - allHandles, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") - require.NoError(t, err) - require.Equal(t, 1, len(allHandles)) - orderedHandles := testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - require.Equal(t, int64(1), orderedHandles[0]) - tk.MustExec("delete from t") - - // Test rebase auto_increment. - tk.MustExec("alter table t auto_increment 3000000") - tk.MustExec("insert into t values()") - tk.MustQuery("select b from t").Check(testkit.Rows("3000000")) - allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") - require.NoError(t, err) - require.Equal(t, 1, len(allHandles)) - orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - require.Equal(t, int64(2), orderedHandles[0]) - tk.MustExec("delete from t") - - // Test rebase auto_random. - tk.MustExec("alter table t auto_random_base 3000000") - tk.MustExec("insert into t values()") - tk.MustQuery("select b from t").Check(testkit.Rows("3000001")) - allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") - require.NoError(t, err) - require.Equal(t, 1, len(allHandles)) - orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - require.Equal(t, int64(3000000), orderedHandles[0]) - tk.MustExec("delete from t") - - // Test rename table. - tk.MustExec("rename table t to t1") - tk.MustExec("insert into t1 values()") - res := tk.MustQuery("select b from t1") - strInt64, err := strconv.ParseInt(res.Rows()[0][0].(string), 10, 64) - require.NoError(t, err) - require.GreaterOrEqual(t, strInt64, int64(3000002)) - allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t1") - require.NoError(t, err) - require.Equal(t, 1, len(allHandles)) - orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - require.Greater(t, orderedHandles[0], int64(3000001)) - - tk.MustExec("drop table t1") - } -} - -func TestAutoIncrementInsertMinMax(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - cases := []struct { - t string - s string - vals []int64 - expect [][]interface{} - }{ - {"tinyint", "signed", []int64{-128, 0, 127}, testkit.Rows("-128", "1", "2", "3", "127")}, - {"tinyint", "unsigned", []int64{0, 127, 255}, testkit.Rows("1", "2", "127", "128", "255")}, - {"smallint", "signed", []int64{-32768, 0, 32767}, testkit.Rows("-32768", "1", "2", "3", "32767")}, - {"smallint", "unsigned", []int64{0, 32767, 65535}, testkit.Rows("1", "2", "32767", "32768", "65535")}, - {"mediumint", "signed", []int64{-8388608, 0, 8388607}, testkit.Rows("-8388608", "1", "2", "3", "8388607")}, - {"mediumint", "unsigned", []int64{0, 8388607, 16777215}, testkit.Rows("1", "2", "8388607", "8388608", "16777215")}, - {"integer", "signed", []int64{-2147483648, 0, 2147483647}, testkit.Rows("-2147483648", "1", "2", "3", "2147483647")}, - {"integer", "unsigned", []int64{0, 2147483647, 4294967295}, testkit.Rows("1", "2", "2147483647", "2147483648", "4294967295")}, - {"bigint", "signed", []int64{-9223372036854775808, 0, 9223372036854775807}, testkit.Rows("-9223372036854775808", "1", "2", "3", "9223372036854775807")}, - {"bigint", "unsigned", []int64{0, 9223372036854775807}, testkit.Rows("1", "2", "9223372036854775807", "9223372036854775808")}, - } - - for _, option := range []string{"", "auto_id_cache 1", "auto_id_cache 100"} { - for idx, c := range cases { - sql := fmt.Sprintf("create table t%d (a %s %s key auto_increment) %s", idx, c.t, c.s, option) - tk.MustExec(sql) - - for _, val := range c.vals { - tk.MustExec(fmt.Sprintf("insert into t%d values (%d)", idx, val)) - tk.Exec(fmt.Sprintf("insert into t%d values ()", idx)) // ignore error - } - - tk.MustQuery(fmt.Sprintf("select * from t%d order by a", idx)).Check(c.expect) - - tk.MustExec(fmt.Sprintf("drop table t%d", idx)) - } - } - - tk.MustExec("create table t10 (a integer key auto_increment) auto_id_cache 1") - err := tk.ExecToErr("insert into t10 values (2147483648)") - require.Error(t, err) - err = tk.ExecToErr("insert into t10 values (-2147483649)") - require.Error(t, err) -} - -func TestInsertWithAutoidSchema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`create table t1(id int primary key auto_increment, n int);`) - tk.MustExec(`create table t2(id int unsigned primary key auto_increment, n int);`) - tk.MustExec(`create table t3(id tinyint primary key auto_increment, n int);`) - tk.MustExec(`create table t4(id int primary key, n float auto_increment, key I_n(n));`) - tk.MustExec(`create table t5(id int primary key, n float unsigned auto_increment, key I_n(n));`) - tk.MustExec(`create table t6(id int primary key, n double auto_increment, key I_n(n));`) - tk.MustExec(`create table t7(id int primary key, n double unsigned auto_increment, key I_n(n));`) - // test for inserting multiple values - tk.MustExec(`create table t8(id int primary key auto_increment, n int);`) - - testInsertWithAutoidSchema(t, tk) -} - -func TestInsertWithAutoidSchemaCache(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`create table t1(id int primary key auto_increment, n int) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t2(id int unsigned primary key auto_increment, n int) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t3(id tinyint primary key auto_increment, n int) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t4(id int primary key, n float auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t5(id int primary key, n float unsigned auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t6(id int primary key, n double auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) - tk.MustExec(`create table t7(id int primary key, n double unsigned auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) - // test for inserting multiple values - tk.MustExec(`create table t8(id int primary key auto_increment, n int);`) - - testInsertWithAutoidSchema(t, tk) -} - -func testInsertWithAutoidSchema(t *testing.T, tk *testkit.TestKit) { - tests := []struct { - insert string - query string - result [][]interface{} - }{ - { - `insert into t1(id, n) values(1, 1)`, - `select * from t1 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t1(n) values(2)`, - `select * from t1 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t1(n) values(3)`, - `select * from t1 where id = 3`, - testkit.Rows(`3 3`), - }, - { - `insert into t1(id, n) values(-1, 4)`, - `select * from t1 where id = -1`, - testkit.Rows(`-1 4`), - }, - { - `insert into t1(n) values(5)`, - `select * from t1 where id = 4`, - testkit.Rows(`4 5`), - }, - { - `insert into t1(id, n) values('5', 6)`, - `select * from t1 where id = 5`, - testkit.Rows(`5 6`), - }, - { - `insert into t1(n) values(7)`, - `select * from t1 where id = 6`, - testkit.Rows(`6 7`), - }, - { - `insert into t1(id, n) values(7.4, 8)`, - `select * from t1 where id = 7`, - testkit.Rows(`7 8`), - }, - { - `insert into t1(id, n) values(7.5, 9)`, - `select * from t1 where id = 8`, - testkit.Rows(`8 9`), - }, - { - `insert into t1(n) values(9)`, - `select * from t1 where id = 9`, - testkit.Rows(`9 9`), - }, - // test last insert id - { - `insert into t1 values(3000, -1), (null, -2)`, - `select * from t1 where id = 3000`, - testkit.Rows(`3000 -1`), - }, - { - `;`, - `select * from t1 where id = 3001`, - testkit.Rows(`3001 -2`), - }, - { - `;`, - `select last_insert_id()`, - testkit.Rows(`3001`), - }, - { - `insert into t2(id, n) values(1, 1)`, - `select * from t2 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t2(n) values(2)`, - `select * from t2 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t2(n) values(3)`, - `select * from t2 where id = 3`, - testkit.Rows(`3 3`), - }, - { - `insert into t3(id, n) values(1, 1)`, - `select * from t3 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t3(n) values(2)`, - `select * from t3 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t3(n) values(3)`, - `select * from t3 where id = 3`, - testkit.Rows(`3 3`), - }, - { - `insert into t3(id, n) values(-1, 4)`, - `select * from t3 where id = -1`, - testkit.Rows(`-1 4`), - }, - { - `insert into t3(n) values(5)`, - `select * from t3 where id = 4`, - testkit.Rows(`4 5`), - }, - { - `insert into t4(id, n) values(1, 1)`, - `select * from t4 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t4(id) values(2)`, - `select * from t4 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t4(id, n) values(3, -1)`, - `select * from t4 where id = 3`, - testkit.Rows(`3 -1`), - }, - { - `insert into t4(id) values(4)`, - `select * from t4 where id = 4`, - testkit.Rows(`4 3`), - }, - { - `insert into t4(id, n) values(5, 5.5)`, - `select * from t4 where id = 5`, - testkit.Rows(`5 5.5`), - }, - { - `insert into t4(id) values(6)`, - `select * from t4 where id = 6`, - testkit.Rows(`6 7`), - }, - { - `insert into t4(id, n) values(7, '7.7')`, - `select * from t4 where id = 7`, - testkit.Rows(`7 7.7`), - }, - { - `insert into t4(id) values(8)`, - `select * from t4 where id = 8`, - testkit.Rows(`8 9`), - }, - { - `insert into t4(id, n) values(9, 10.4)`, - `select * from t4 where id = 9`, - testkit.Rows(`9 10.4`), - }, - { - `insert into t4(id) values(10)`, - `select * from t4 where id = 10`, - testkit.Rows(`10 11`), - }, - { - `insert into t5(id, n) values(1, 1)`, - `select * from t5 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t5(id) values(2)`, - `select * from t5 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t5(id) values(3)`, - `select * from t5 where id = 3`, - testkit.Rows(`3 3`), - }, - { - `insert into t6(id, n) values(1, 1)`, - `select * from t6 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t6(id) values(2)`, - `select * from t6 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t6(id, n) values(3, -1)`, - `select * from t6 where id = 3`, - testkit.Rows(`3 -1`), - }, - { - `insert into t6(id) values(4)`, - `select * from t6 where id = 4`, - testkit.Rows(`4 3`), - }, - { - `insert into t6(id, n) values(5, 5.5)`, - `select * from t6 where id = 5`, - testkit.Rows(`5 5.5`), - }, - { - `insert into t6(id) values(6)`, - `select * from t6 where id = 6`, - testkit.Rows(`6 7`), - }, - { - `insert into t6(id, n) values(7, '7.7')`, - `select * from t4 where id = 7`, - testkit.Rows(`7 7.7`), - }, - { - `insert into t6(id) values(8)`, - `select * from t4 where id = 8`, - testkit.Rows(`8 9`), - }, - { - `insert into t6(id, n) values(9, 10.4)`, - `select * from t6 where id = 9`, - testkit.Rows(`9 10.4`), - }, - { - `insert into t6(id) values(10)`, - `select * from t6 where id = 10`, - testkit.Rows(`10 11`), - }, - { - `insert into t7(id, n) values(1, 1)`, - `select * from t7 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `insert into t7(id) values(2)`, - `select * from t7 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `insert into t7(id) values(3)`, - `select * from t7 where id = 3`, - testkit.Rows(`3 3`), - }, - - // the following is test for insert multiple values. - { - `insert into t8(n) values(1),(2)`, - `select * from t8 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `;`, - `select * from t8 where id = 2`, - testkit.Rows(`2 2`), - }, - { - `;`, - `select last_insert_id();`, - testkit.Rows(`1`), - }, - // test user rebase and auto alloc mixture. - { - `insert into t8 values(null, 3),(-1, -1),(null,4),(null, 5)`, - `select * from t8 where id = 3`, - testkit.Rows(`3 3`), - }, - // -1 won't rebase allocator here cause -1 < base. - { - `;`, - `select * from t8 where id = -1`, - testkit.Rows(`-1 -1`), - }, - { - `;`, - `select * from t8 where id = 4`, - testkit.Rows(`4 4`), - }, - { - `;`, - `select * from t8 where id = 5`, - testkit.Rows(`5 5`), - }, - { - `;`, - `select last_insert_id();`, - testkit.Rows(`3`), - }, - { - `insert into t8 values(null, 6),(10, 7),(null, 8)`, - `select * from t8 where id = 6`, - testkit.Rows(`6 6`), - }, - // 10 will rebase allocator here. - { - `;`, - `select * from t8 where id = 10`, - testkit.Rows(`10 7`), - }, - { - `;`, - `select * from t8 where id = 11`, - testkit.Rows(`11 8`), - }, - { - `;`, - `select last_insert_id()`, - testkit.Rows(`6`), - }, - // fix bug for last_insert_id should be first allocated id in insert rows (skip the rebase id). - { - `insert into t8 values(100, 9),(null,10),(null,11)`, - `select * from t8 where id = 100`, - testkit.Rows(`100 9`), - }, - { - `;`, - `select * from t8 where id = 101`, - testkit.Rows(`101 10`), - }, - { - `;`, - `select * from t8 where id = 102`, - testkit.Rows(`102 11`), - }, - { - `;`, - `select last_insert_id()`, - testkit.Rows(`101`), - }, - // test with sql_mode: NO_AUTO_VALUE_ON_ZERO. - { - `;`, - `select @@sql_mode`, - testkit.Rows(`ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`), - }, - { - `;`, - "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO`", - nil, - }, - { - `insert into t8 values (0, 12), (null, 13)`, - `select * from t8 where id = 0`, - testkit.Rows(`0 12`), - }, - { - `;`, - `select * from t8 where id = 103`, - testkit.Rows(`103 13`), - }, - { - `;`, - `select last_insert_id()`, - testkit.Rows(`103`), - }, - // test without sql_mode: NO_AUTO_VALUE_ON_ZERO. - { - `;`, - "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`", - nil, - }, - // value 0 will be substitute by autoid. - { - `insert into t8 values (0, 14), (null, 15)`, - `select * from t8 where id = 104`, - testkit.Rows(`104 14`), - }, - { - `;`, - `select * from t8 where id = 105`, - testkit.Rows(`105 15`), - }, - { - `;`, - `select last_insert_id()`, - testkit.Rows(`104`), - }, - // last test : auto increment allocation can find in retryInfo. - { - `retry : insert into t8 values (null, 16), (null, 17)`, - `select * from t8 where id = 1000`, - testkit.Rows(`1000 16`), - }, - { - `;`, - `select * from t8 where id = 1001`, - testkit.Rows(`1001 17`), - }, - { - `;`, - `select last_insert_id()`, - // this insert doesn't has the last_insert_id, should be same as the last insert case. - testkit.Rows(`104`), - }, - } - - for _, tt := range tests { - if strings.HasPrefix(tt.insert, "retry : ") { - // it's the last retry insert case, change the sessionVars. - retryInfo := &variable.RetryInfo{Retrying: true} - retryInfo.AddAutoIncrementID(1000) - retryInfo.AddAutoIncrementID(1001) - tk.Session().GetSessionVars().RetryInfo = retryInfo - tk.MustExec(tt.insert[8:]) - tk.Session().GetSessionVars().RetryInfo = &variable.RetryInfo{} - } else { - tk.MustExec(tt.insert) - } - if tt.query == "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO`" || - tt.query == "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`" { - tk.MustExec(tt.query) - } else { - tk.MustQuery(tt.query).Check(tt.result) - } - } -} - -// TestAutoIDIncrementAndOffset There is a potential issue in MySQL: when the value of auto_increment_offset is greater -// than that of auto_increment_increment, the value of auto_increment_offset is ignored -// (https://dev.mysql.com/doc/refman/8.0/en/replication-options-master.html#sysvar_auto_increment_increment), -// This issue is a flaw of the implementation of MySQL and it doesn't exist in TiDB. -func TestAutoIDIncrementAndOffset(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - // Test for offset is larger than increment. - tk.Session().GetSessionVars().AutoIncrementIncrement = 5 - tk.Session().GetSessionVars().AutoIncrementOffset = 10 - - for _, str := range []string{"", " AUTO_ID_CACHE 1"} { - tk.MustExec(`create table io (a int key auto_increment)` + str) - tk.MustExec(`insert into io values (null),(null),(null)`) - tk.MustQuery(`select * from io`).Check(testkit.Rows("10", "15", "20")) - tk.MustExec(`drop table io`) - } - - // Test handle is PK. - for _, str := range []string{"", " AUTO_ID_CACHE 1"} { - tk.MustExec(`create table io (a int key auto_increment)` + str) - tk.Session().GetSessionVars().AutoIncrementOffset = 10 - tk.Session().GetSessionVars().AutoIncrementIncrement = 2 - tk.MustExec(`insert into io values (),(),()`) - tk.MustQuery(`select * from io`).Check(testkit.Rows("10", "12", "14")) - tk.MustExec(`delete from io`) - - // Test reset the increment. - tk.Session().GetSessionVars().AutoIncrementIncrement = 5 - tk.MustExec(`insert into io values (),(),()`) - tk.MustQuery(`select * from io`).Check(testkit.Rows("15", "20", "25")) - tk.MustExec(`delete from io`) - - tk.Session().GetSessionVars().AutoIncrementIncrement = 10 - tk.MustExec(`insert into io values (),(),()`) - tk.MustQuery(`select * from io`).Check(testkit.Rows("30", "40", "50")) - tk.MustExec(`delete from io`) - - tk.Session().GetSessionVars().AutoIncrementIncrement = 5 - tk.MustExec(`insert into io values (),(),()`) - tk.MustQuery(`select * from io`).Check(testkit.Rows("55", "60", "65")) - tk.MustExec(`drop table io`) - } - - // Test handle is not PK. - for _, str := range []string{"", " AUTO_ID_CACHE 1"} { - tk.Session().GetSessionVars().AutoIncrementIncrement = 2 - tk.Session().GetSessionVars().AutoIncrementOffset = 10 - tk.MustExec(`create table io (a int, b int auto_increment, key(b))` + str) - tk.MustExec(`insert into io(b) values (null),(null),(null)`) - // AutoID allocation will take increment and offset into consideration. - tk.MustQuery(`select b from io`).Check(testkit.Rows("10", "12", "14")) - if str == "" { - // HandleID allocation will ignore the increment and offset. - tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("15", "16", "17")) - } else { - // Separate row id and auto inc id, increment and offset works on auto inc id - tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("1", "2", "3")) - } - tk.MustExec(`delete from io`) - - tk.Session().GetSessionVars().AutoIncrementIncrement = 10 - tk.MustExec(`insert into io(b) values (null),(null),(null)`) - tk.MustQuery(`select b from io`).Check(testkit.Rows("20", "30", "40")) - if str == "" { - tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("41", "42", "43")) - } else { - tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("4", "5", "6")) - } - - // Test invalid value. - tk.Session().GetSessionVars().AutoIncrementIncrement = -1 - tk.Session().GetSessionVars().AutoIncrementOffset = -2 - tk.MustGetErrMsg(`insert into io(b) values (null),(null),(null)`, - "[autoid:8060]Invalid auto_increment settings: auto_increment_increment: -1, auto_increment_offset: -2, both of them must be in range [1..65535]") - tk.MustExec(`delete from io`) - - tk.Session().GetSessionVars().AutoIncrementIncrement = 65536 - tk.Session().GetSessionVars().AutoIncrementOffset = 65536 - tk.MustGetErrMsg(`insert into io(b) values (null),(null),(null)`, - "[autoid:8060]Invalid auto_increment settings: auto_increment_increment: 65536, auto_increment_offset: 65536, both of them must be in range [1..65535]") - - tk.MustExec(`drop table io`) - } -} - -func TestRenameTableForAutoIncrement(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - tk.MustExec("drop table if exists t1, t2, t3;") - tk.MustExec("create table t1 (id int key auto_increment);") - tk.MustExec("insert into t1 values ()") - tk.MustExec("rename table t1 to t11") - tk.MustExec("insert into t11 values ()") - // TODO(tiancaiamao): fix bug and uncomment here, rename table should not discard the cached AUTO_ID. - // tk.MustQuery("select * from t11").Check(testkit.Rows("1", "2")) - - // auto_id_cache 1 use another implementation and do not have such bug. - tk.MustExec("create table t2 (id int key auto_increment) auto_id_cache 1;") - tk.MustExec("insert into t2 values ()") - tk.MustExec("rename table t2 to t22") - tk.MustExec("insert into t22 values ()") - tk.MustQuery("select * from t22").Check(testkit.Rows("1", "2")) - - tk.MustExec("create table t3 (id int key auto_increment) auto_id_cache 100;") - tk.MustExec("insert into t3 values ()") - tk.MustExec("rename table t3 to t33") - tk.MustExec("insert into t33 values ()") - // TODO(tiancaiamao): fix bug and uncomment here, rename table should not discard the cached AUTO_ID. - // tk.MustQuery("select * from t33").Check(testkit.Rows("1", "2")) -} - -func TestAlterTableAutoIDCache(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - tk.MustExec("drop table if exists t_473;") - tk.MustExec("create table t_473 (id int key auto_increment)") - tk.MustExec("insert into t_473 values ()") - tk.MustQuery("select * from t_473").Check(testkit.Rows("1")) - rs, err := tk.Exec("show table t_473 next_row_id") - require.NoError(t, err) - rows, err1 := session.ResultSetToStringSlice(context.Background(), tk.Session(), rs) - require.NoError(t, err1) - // "test t_473 id 1013608 AUTO_INCREMENT" - val, err2 := strconv.ParseUint(rows[0][3], 10, 64) - require.NoError(t, err2) - - tk.MustExec("alter table t_473 auto_id_cache = 100") - tk.MustQuery("show table t_473 next_row_id").Check(testkit.Rows( - fmt.Sprintf("test t_473 id %d _TIDB_ROWID", val), - "test t_473 id 1 AUTO_INCREMENT", - )) - tk.MustExec("insert into t_473 values ()") - tk.MustQuery("select * from t_473").Check(testkit.Rows("1", fmt.Sprintf("%d", val))) - tk.MustQuery("show table t_473 next_row_id").Check(testkit.Rows( - fmt.Sprintf("test t_473 id %d _TIDB_ROWID", val+100), - "test t_473 id 1 AUTO_INCREMENT", - )) - - // Note that auto_id_cache=1 use a different implementation, switch between them is not allowed. - // TODO: relax this restriction and update the test case. - tk.MustExecToErr("alter table t_473 auto_id_cache = 1") -} - -func TestMockAutoIDServiceError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - tk.MustExec("create table t_mock_err (id int key auto_increment) auto_id_cache 1") - - failpoint.Enable("github.com/pingcap/tidb/autoid_service/mockErr", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/autoid_service/mockErr") - // Cover a bug that the autoid client retry non-retryable errors forever cause dead loop. - tk.MustExecToErr("insert into t_mock_err values (),()") // mock error, instead of dead loop -} - -func TestIssue39528(t *testing.T) { - // When AUTO_ID_CACHE is 1, it should not affect row id setting when autoid and rowid are separated. - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("create table issue39528 (id int unsigned key nonclustered auto_increment) shard_row_id_bits=4 auto_id_cache 1;") - tk.MustExec("insert into issue39528 values ()") - tk.MustExec("insert into issue39528 values ()") - - ctx := context.Background() - var codeRun bool - ctx = context.WithValue(ctx, "testIssue39528", &codeRun) - _, err := tk.ExecWithContext(ctx, "insert into issue39528 values ()") - require.NoError(t, err) - // Make sure the code does not visit tikv on allocate path. - require.False(t, codeRun) -} - -func TestAutoIDConstraint(t *testing.T) { - // Remove the constraint that auto id column must be defined as a key - // See https://github.com/pingcap/tidb/issues/40580 - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - // Cover: create table with/without key constraint - template := `create table t%d (id int auto_increment,k int,c char(120)%s) %s` - keyDefs := []string{"", - ",PRIMARY KEY(k, id)", - ",key idx_1(id)", - ",PRIMARY KEY(`k`, `id`), key idx_1(id)", - } - engineDefs := []string{"", - "engine = MyISAM", - "engine = InnoDB", - "auto_id_cache 1", - "auto_id_cache 100", - } - i := 0 - for _, keyDef := range keyDefs { - for _, engineDef := range engineDefs { - tk.MustExec(fmt.Sprintf("drop table if exists t%d", i)) - sql := fmt.Sprintf(template, i, keyDef, engineDef) - tk.MustExec(sql) - i++ - } - } - - // alter table add auto id column is not supported, but cover it here to prevent regression - tk.MustExec("create table tt1 (id int)") - tk.MustExecToErr("alter table tt1 add column (c int auto_increment)") - - // Cover case: create table with auto id column as key, and remove it later - tk.MustExec("create table tt2 (id int, c int auto_increment, key c_idx(c))") - tk.MustExec("alter table tt2 drop index c_idx") -} diff --git a/executor/test/autoidtest/main_test.go b/executor/test/autoidtest/main_test.go deleted file mode 100644 index edde1b16dc012..0000000000000 --- a/executor/test/autoidtest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package autoid_test - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/ddl/BUILD.bazel b/executor/test/ddl/BUILD.bazel deleted file mode 100644 index b4e59f0f2bbb3..0000000000000 --- a/executor/test/ddl/BUILD.bazel +++ /dev/null @@ -1,44 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "ddl_test", - timeout = "short", - srcs = [ - "ddl_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 27, - deps = [ - "//config", - "//ddl/schematracker", - "//ddl/testutil", - "//ddl/util", - "//domain", - "//errno", - "//infoschema", - "//kv", - "//meta", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//sessionctx/variable", - "//sessionctx/variable/featuretag/disttask", - "//sessiontxn", - "//store/mockstore", - "//table", - "//table/tables", - "//testkit", - "//testkit/testutil", - "//types", - "//util/chunk", - "//util/dbterror", - "//util/dbterror/exeerrors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/ddl/ddl_test.go b/executor/test/ddl/ddl_test.go deleted file mode 100644 index 7ac08a2600aba..0000000000000 --- a/executor/test/ddl/ddl_test.go +++ /dev/null @@ -1,1378 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "context" - "fmt" - "math" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/schematracker" - ddltestutil "github.com/pingcap/tidb/ddl/testutil" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessionctx/variable/featuretag/disttask" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/stretchr/testify/require" -) - -// TestInTxnExecDDLFail tests the following case: -// 1. Execute the SQL of "begin"; -// 2. A SQL that will fail to execute; -// 3. Execute DDL. -func TestInTxnExecDDLFail(t *testing.T) { - store := testkit.CreateMockStore(t) - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (i int key);") - tk.MustExec("insert into t values (1);") - tk.MustExec("begin;") - tk.MustExec("insert into t values (1);") - tk.MustGetErrMsg("truncate table t;", "[kv:1062]Duplicate entry '1' for key 't.PRIMARY'") - tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) -} - -func TestInTxnExecDDLInvalid(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (c_int int, c_str varchar(40));") - tk.MustExec("insert into t values (1, 'quizzical hofstadter');") - tk.MustExec("begin;") - _ = tk.MustQuery("select c_int from t where c_str is not null for update;") - tk.MustExec("alter table t add index idx_4 (c_str);") -} - -func TestCreateTable(t *testing.T) { - store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // Test create an exist database - tk.MustExecToErr("CREATE database test") - - // Test create an exist table - tk.MustExec("CREATE TABLE create_test (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") - tk.MustExecToErr("CREATE TABLE create_test (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") - - // Test "if not exist" - tk.MustExec("CREATE TABLE if not exists test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") - - // Testcase for https://github.com/pingcap/tidb/issues/312 - tk.MustExec(`create table issue312_1 (c float(24));`) - tk.MustExec(`create table issue312_2 (c float(25));`) - rs, err := tk.Exec(`desc issue312_1`) - require.NoError(t, err) - ctx := context.Background() - req := rs.NewChunk(nil) - it := chunk.NewIterator4Chunk(req) - for { - err1 := rs.Next(ctx, req) - require.NoError(t, err1) - if req.NumRows() == 0 { - break - } - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, "float", row.GetString(1)) - } - } - rs, err = tk.Exec(`desc issue312_2`) - require.NoError(t, err) - req = rs.NewChunk(nil) - it = chunk.NewIterator4Chunk(req) - for { - err1 := rs.Next(ctx, req) - require.NoError(t, err1) - if req.NumRows() == 0 { - break - } - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, "double", req.GetRow(0).GetString(1)) - } - } - require.NoError(t, rs.Close()) - - // test multiple collate specified in column when create. - tk.MustExec("drop table if exists test_multiple_column_collate;") - tk.MustExec("create table test_multiple_column_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("test_multiple_column_collate")) - require.NoError(t, err) - require.Equal(t, "utf8", tt.Cols()[0].GetCharset()) - require.Equal(t, "utf8_general_ci", tt.Cols()[0].GetCollate()) - require.Equal(t, "utf8mb4", tt.Meta().Charset) - require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) - - tk.MustExec("drop table if exists test_multiple_column_collate;") - tk.MustExec("create table test_multiple_column_collate (a char(1) charset utf8 collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("test_multiple_column_collate")) - require.NoError(t, err) - require.Equal(t, "utf8", tt.Cols()[0].GetCharset()) - require.Equal(t, "utf8_general_ci", tt.Cols()[0].GetCollate()) - require.Equal(t, "utf8mb4", tt.Meta().Charset) - require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) - - // test Err case for multiple collate specified in column when create. - tk.MustExec("drop table if exists test_err_multiple_collate;") - tk.MustGetErrMsg("create table test_err_multiple_collate (a char(1) charset utf8mb4 collate utf8_unicode_ci collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin", - dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8_unicode_ci", "utf8mb4").Error()) - - tk.MustExec("drop table if exists test_err_multiple_collate;") - tk.MustGetErrMsg("create table test_err_multiple_collate (a char(1) collate utf8_unicode_ci collate utf8mb4_general_ci) charset utf8mb4 collate utf8mb4_bin", - dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8mb4_general_ci", "utf8").Error()) - - // table option is auto-increment - tk.MustExec("drop table if exists create_auto_increment_test;") - tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), primary key(id)) auto_increment = 999;") - tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") - tk.MustExec("insert into create_auto_increment_test (name) values ('bb')") - tk.MustExec("insert into create_auto_increment_test (name) values ('cc')") - r := tk.MustQuery("select * from create_auto_increment_test;") - r.Check(testkit.Rows("999 aa", "1000 bb", "1001 cc")) - tk.MustExec("drop table create_auto_increment_test") - tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), primary key(id)) auto_increment = 1999;") - tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") - tk.MustExec("insert into create_auto_increment_test (name) values ('bb')") - tk.MustExec("insert into create_auto_increment_test (name) values ('cc')") - r = tk.MustQuery("select * from create_auto_increment_test;") - r.Check(testkit.Rows("1999 aa", "2000 bb", "2001 cc")) - tk.MustExec("drop table create_auto_increment_test") - tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), key(id)) auto_increment = 1000;") - tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") - r = tk.MustQuery("select * from create_auto_increment_test;") - r.Check(testkit.Rows("1000 aa")) - - // Test for `drop table if exists`. - tk.MustExec("drop table if exists t_if_exists;") - tk.MustQuery("show warnings;").Check(testkit.Rows("Note 1051 Unknown table 'test.t_if_exists'")) - tk.MustExec("create table if not exists t1_if_exists(c int)") - tk.MustExec("drop table if exists t1_if_exists,t2_if_exists,t3_if_exists") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1051|Unknown table 'test.t2_if_exists'", "Note|1051|Unknown table 'test.t3_if_exists'")) -} - -func TestCreateView(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // create an source table - tk.MustExec("CREATE TABLE source_table (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") - // test create a exist view - tk.MustExec("CREATE VIEW view_t AS select id , name from source_table") - defer tk.MustExec("DROP VIEW IF EXISTS view_t") - tk.MustGetErrMsg("CREATE VIEW view_t AS select id , name from source_table", "[schema:1050]Table 'test.view_t' already exists") - // create view on nonexistent table - tk.MustGetErrMsg("create view v1 (c,d) as select a,b from t1", "[schema:1146]Table 'test.t1' doesn't exist") - // simple view - tk.MustExec("create table t1 (a int ,b int)") - tk.MustExec("insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)") - // view with colList and SelectFieldExpr - tk.MustExec("create view v1 (c) as select b+1 from t1") - // view with SelectFieldExpr - tk.MustExec("create view v2 as select b+1 from t1") - // view with SelectFieldExpr and AsName - tk.MustExec("create view v3 as select b+1 as c from t1") - // view with colList , SelectField and AsName - tk.MustExec("create view v4 (c) as select b+1 as d from t1") - // view with select wild card - tk.MustExec("create view v5 as select * from t1") - tk.MustExec("create view v6 (c,d) as select * from t1") - tk.MustGetErrCode("create view v7 (c,d,e) as select * from t1", errno.ErrViewWrongList) - // drop multiple views in a statement - tk.MustExec("drop view v1,v2,v3,v4,v5,v6") - // view with variable - tk.MustExec("create view v1 (c,d) as select a,b+@@global.max_user_connections from t1") - tk.MustGetErrMsg("create view v1 (c,d) as select a,b from t1 where a = @@global.max_user_connections", "[schema:1050]Table 'test.v1' already exists") - tk.MustExec("drop view v1") - // view with different col counts - tk.MustGetErrCode("create view v1 (c,d,e) as select a,b from t1 ", errno.ErrViewWrongList) - tk.MustGetErrCode("create view v1 (c) as select a,b from t1 ", errno.ErrViewWrongList) - // view with or_replace flag - tk.MustExec("drop view if exists v1") - tk.MustExec("create view v1 (c,d) as select a,b from t1") - tk.MustExec("create or replace view v1 (c,d) as select a,b from t1 ") - tk.MustExec("create table if not exists t1 (a int ,b int)") - err := tk.ExecToErr("create or replace view t1 as select * from t1") - require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "t1", "VIEW").Error(), err.Error()) - // create view using prepare - tk.MustExec(`prepare stmt from "create view v10 (x) as select 1";`) - tk.MustExec("execute stmt") - - // create view on union - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("drop view if exists v") - tk.MustGetDBError("create view v as select * from t1 union select * from t2", infoschema.ErrTableNotExists) - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int, b int)") - tk.MustExec("insert into t1 values(1,2), (1,1), (1,2)") - tk.MustExec("insert into t2 values(1,1),(1,3)") - tk.MustExec("create definer='root'@'localhost' view v as select * from t1 union select * from t2") - tk.MustQuery("select * from v").Sort().Check(testkit.Rows("1 1", "1 2", "1 3")) - tk.MustExec("alter table t1 drop column a") - tk.MustGetDBError("select * from v", plannercore.ErrViewInvalid) - tk.MustExec("alter table t1 add column a int") - tk.MustQuery("select * from v").Sort().Check(testkit.Rows("1 1", "1 3", " 1", " 2")) - tk.MustExec("alter table t1 drop column a") - tk.MustExec("alter table t2 drop column b") - tk.MustGetDBError("select * from v", plannercore.ErrViewInvalid) - tk.MustExec("drop view v") - - tk.MustExec("create view v as (select * from t1)") - tk.MustExec("drop view v") - tk.MustExec("create view v as (select * from t1 union select * from t2)") - tk.MustExec("drop view v") - - // Test for `drop view if exists`. - tk.MustExec("drop view if exists v_if_exists;") - tk.MustQuery("show warnings;").Check(testkit.Rows("Note 1051 Unknown table 'test.v_if_exists'")) - tk.MustExec("create view v1_if_exists as (select * from t1)") - tk.MustExec("drop view if exists v1_if_exists,v2_if_exists,v3_if_exists") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1051|Unknown table 'test.v2_if_exists'", "Note|1051|Unknown table 'test.v3_if_exists'")) - - // Test for create nested view. - tk.MustExec("create table test_v_nested(a int)") - tk.MustExec("create definer='root'@'localhost' view v_nested as select * from test_v_nested") - tk.MustExec("create definer='root'@'localhost' view v_nested2 as select * from v_nested") - tk.MustGetDBError("create or replace definer='root'@'localhost' view v_nested as select * from v_nested2", plannercore.ErrNoSuchTable) - tk.MustExec("drop table test_v_nested") - tk.MustExec("drop view v_nested, v_nested2") - - // Refer https://github.com/pingcap/tidb/issues/25876 - err = tk.ExecToErr("create view v_stale as select * from source_table as of timestamp current_timestamp(3)") - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrViewInvalid), "err %s", err) - - // Refer https://github.com/pingcap/tidb/issues/32682 - tk.MustExec("drop view if exists v1,v2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("CREATE TABLE t1(a INT, b INT);") - err = tk.ExecToErr("CREATE DEFINER=1234567890abcdefGHIKL1234567890abcdefGHIKL@localhost VIEW v1 AS SELECT a FROM t1;") - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrWrongStringLength), "ERROR 1470 (HY000): String '1234567890abcdefGHIKL1234567890abcdefGHIKL' is too long for user name (should be no longer than 32)") - err = tk.ExecToErr("CREATE DEFINER=some_user_name@host_1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890X VIEW v2 AS SELECT b FROM t1;") - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrWrongStringLength), "ERROR 1470 (HY000): String 'host_1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij12345' is too long for host name (should be no longer than 255)") -} - -func TestCreateViewWithOverlongColName(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - defer tk.MustExec("drop table t") - tk.MustExec("create view v as select distinct'" + strings.Repeat("a", 65) + "', " + - "max('" + strings.Repeat("b", 65) + "'), " + - "'cccccccccc', '" + strings.Repeat("d", 65) + "';") - resultCreateStmt := "CREATE ALGORITHM=UNDEFINED DEFINER=``@`` SQL SECURITY DEFINER VIEW `v` (`name_exp_1`, `name_exp_2`, `cccccccccc`, `name_exp_4`) AS " + - "SELECT DISTINCT _UTF8MB4'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' AS `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`," + - "MAX(_UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') AS `max('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')`," + - "_UTF8MB4'cccccccccc' AS `cccccccccc`,_UTF8MB4'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd' AS `ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd`" - tk.MustQuery("select * from v") - tk.MustQuery("select name_exp_1, name_exp_2, cccccccccc, name_exp_4 from v") - tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) - tk.MustExec("drop view v;") - tk.MustExec(resultCreateStmt) - - tk.MustExec("drop view v ") - tk.MustExec("create definer='root'@'localhost' view v as select 'a', '" + strings.Repeat("b", 65) + "' from t " + - "union select '" + strings.Repeat("c", 65) + "', " + - "count(distinct '" + strings.Repeat("b", 65) + "', " + - "'c');") - resultCreateStmt = "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`, `name_exp_2`) AS " + - "SELECT _UTF8MB4'a' AS `a`,_UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' AS `bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb` FROM `test`.`t` " + - "UNION SELECT _UTF8MB4'ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' AS `ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc`," + - "COUNT(DISTINCT _UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', _UTF8MB4'c') AS `count(distinct 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'c')`" - tk.MustQuery("select * from v") - tk.MustQuery("select a, name_exp_2 from v") - tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) - tk.MustExec("drop view v;") - tk.MustExec(resultCreateStmt) - - tk.MustExec("drop view v ") - tk.MustExec("create definer='root'@'localhost' view v as select 'a' as '" + strings.Repeat("b", 65) + "' from t;") - tk.MustQuery("select * from v") - tk.MustQuery("select name_exp_1 from v") - resultCreateStmt = "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`name_exp_1`) AS SELECT _UTF8MB4'a' AS `" + strings.Repeat("b", 65) + "` FROM `test`.`t`" - tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) - tk.MustExec("drop view v;") - tk.MustExec(resultCreateStmt) - - tk.MustExec("drop view v ") - err := tk.ExecToErr("create view v(`" + strings.Repeat("b", 65) + "`) as select a from t;") - require.EqualError(t, err, "[ddl:1059]Identifier name 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' is too long") -} - -func TestCreateDropDatabase(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) - - ddlChecker := dom.DDL().(*schematracker.Checker) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database if not exists drop_test;") - tk.MustExec("drop database if exists drop_test;") - tk.MustExec("create database drop_test;") - tk.MustExec("use drop_test;") - tk.MustExec("drop database drop_test;") - tk.MustGetDBError("drop table t;", plannercore.ErrNoDB) - tk.MustGetDBError("select * from t;", plannercore.ErrNoDB) - - tk.MustExecToErr("drop database mysql") - - tk.MustExec("create database charset_test charset ascii;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET ascii */", - )) - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test charset binary;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET binary */", - )) - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test collate utf8_general_ci;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci */", - )) - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test charset utf8 collate utf8_general_ci;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci */", - )) - tk.MustGetErrMsg("create database charset_test charset utf8 collate utf8mb4_unicode_ci;", "[ddl:1253]COLLATION 'utf8mb4_unicode_ci' is not valid for CHARACTER SET 'utf8'") - - // ddl.SchemaTracker will not respect session charset - ddlChecker.Disable() - - tk.MustExec("SET SESSION character_set_server='ascii'") - tk.MustExec("SET SESSION collation_server='ascii_bin'") - - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET ascii */", - )) - - ddlChecker.Enable() - - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test collate utf8mb4_general_ci;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */", - )) - - tk.MustExec("drop database charset_test;") - tk.MustExec("create database charset_test charset utf8mb4;") - tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", - "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */", - )) -} - -func TestCreateDropTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table if not exists drop_test (a int)") - tk.MustExec("drop table if exists drop_test") - tk.MustExec("create table drop_test (a int)") - tk.MustExec("drop table drop_test") - tk.MustExecToErr("drop table mysql.gc_delete_range") -} - -func TestCreateDropView(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create or replace view drop_test as select 1,2") - tk.MustGetErrMsg("drop table drop_test", "[schema:1051]Unknown table 'test.drop_test'") - - tk.MustExec("drop view if exists drop_test") - - tk.MustGetErrMsg("drop view mysql.gc_delete_range", "Drop tidb system table 'mysql.gc_delete_range' is forbidden") - tk.MustGetErrMsg("drop view drop_test", "[schema:1051]Unknown table 'test.drop_test'") - tk.MustExec("create table t_v(a int)") - tk.MustGetErrMsg("drop view t_v", "[ddl:1347]'test.t_v' is not VIEW") - - tk.MustExec("create table t_v1(a int, b int);") - tk.MustExec("create table t_v2(a int, b int);") - tk.MustExec("create view v as select * from t_v1;") - tk.MustExec("create or replace view v as select * from t_v2;") - tk.MustQuery("select * from information_schema.views where table_name ='v';").Check( - testkit.Rows("def test v SELECT `test`.`t_v2`.`a` AS `a`,`test`.`t_v2`.`b` AS `b` FROM `test`.`t_v2` CASCADED NO @ DEFINER utf8mb4 utf8mb4_bin")) -} - -func TestAlterTableAddColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table if not exists alter_test (c1 int)") - tk.MustExec("insert into alter_test values(1)") - tk.MustExec("alter table alter_test add column c2 timestamp default current_timestamp") - time.Sleep(1 * time.Millisecond) - now := time.Now().Add(-1 * time.Millisecond).Format(types.TimeFormat) - r, err := tk.Exec("select c2 from alter_test") - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(context.Background(), req) - require.NoError(t, err) - row := req.GetRow(0) - require.Equal(t, 1, row.Len()) - require.GreaterOrEqual(t, now, row.GetTime(0).String()) - require.Nil(t, r.Close()) - tk.MustExec("alter table alter_test add column c3 varchar(50) default 'CURRENT_TIMESTAMP'") - tk.MustQuery("select c3 from alter_test").Check(testkit.Rows("CURRENT_TIMESTAMP")) - tk.MustExec("create or replace view alter_view as select c1,c2 from alter_test") - err = tk.ExecToErr("alter table alter_view add column c4 varchar(50)") - require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error(), err.Error()) - tk.MustExec("drop view alter_view") - tk.MustExec("create sequence alter_seq") - err = tk.ExecToErr("alter table alter_seq add column c int") - require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error(), err.Error()) - tk.MustExec("alter table alter_test add column c4 date default current_date") - now = time.Now().Format(types.DateFormat) - r, err = tk.Exec("select c4 from alter_test") - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(context.Background(), req) - require.NoError(t, err) - row = req.GetRow(0) - require.Equal(t, 1, row.Len()) - require.GreaterOrEqual(t, now, row.GetTime(0).String()) - require.Nil(t, r.Close()) - tk.MustExec("drop sequence alter_seq") -} - -func TestAlterTableAddColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table if not exists alter_test (c1 int)") - tk.MustExec("insert into alter_test values(1)") - tk.MustExec("alter table alter_test add column c2 timestamp default current_timestamp, add column c8 varchar(50) default 'CURRENT_TIMESTAMP'") - tk.MustExec("alter table alter_test add column (c7 timestamp default current_timestamp, c3 varchar(50) default 'CURRENT_TIMESTAMP')") - r, err := tk.Exec("select c2 from alter_test") - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(context.Background(), req) - require.NoError(t, err) - row := req.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Nil(t, r.Close()) - tk.MustQuery("select c3 from alter_test").Check(testkit.Rows("CURRENT_TIMESTAMP")) - tk.MustExec("create or replace view alter_view as select c1,c2 from alter_test") - err = tk.ExecToErr("alter table alter_view add column (c4 varchar(50), c5 varchar(50))") - require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error(), err.Error()) - tk.MustExec("drop view alter_view") - tk.MustExec("create sequence alter_seq") - err = tk.ExecToErr("alter table alter_seq add column (c1 int, c2 varchar(10))") - require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error(), err.Error()) - tk.MustExec("drop sequence alter_seq") -} - -func TestAddNotNullColumnNoDefault(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table nn (c1 int)") - tk.MustExec("insert nn values (1), (2)") - tk.MustExec("alter table nn add column c2 int not null") - - tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("nn")) - require.NoError(t, err) - col2 := tbl.Meta().Columns[1] - require.Nil(t, col2.DefaultValue) - require.Equal(t, "0", col2.OriginDefaultValue) - - tk.MustQuery("select * from nn").Check(testkit.Rows("1 0", "2 0")) - tk.MustExecToErr("insert nn (c1) values (3)") - tk.MustExec("set sql_mode=''") - tk.MustExec("insert nn (c1) values (3)") - tk.MustQuery("select * from nn").Check(testkit.Rows("1 0", "2 0", "3 0")) -} - -func TestAlterTableModifyColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists mc") - tk.MustExec("create table mc(c1 int, c2 varchar(10), c3 bit)") - tk.MustExecToErr("alter table mc modify column c1 short") - tk.MustExec("alter table mc modify column c1 bigint") - - tk.MustExecToErr("alter table mc modify column c2 blob") - tk.MustExec("alter table mc modify column c2 varchar(8)") - tk.MustExec("alter table mc modify column c2 varchar(11)") - tk.MustExec("alter table mc modify column c2 text(13)") - tk.MustExec("alter table mc modify column c2 text") - tk.MustExec("alter table mc modify column c3 bit") - result := tk.MustQuery("show create table mc") - createSQL := result.Rows()[0][1] - expected := "CREATE TABLE `mc` (\n `c1` bigint(20) DEFAULT NULL,\n `c2` text DEFAULT NULL,\n `c3` bit(1) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" - require.Equal(t, expected, createSQL) - tk.MustExec("create or replace view alter_view as select c1,c2 from mc") - tk.MustGetErrMsg("alter table alter_view modify column c2 text", - dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error()) - tk.MustExec("drop view alter_view") - tk.MustExec("create sequence alter_seq") - tk.MustGetErrMsg("alter table alter_seq modify column c int", - dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error()) - tk.MustExec("drop sequence alter_seq") - - // test multiple collate modification in column. - tk.MustExec("drop table if exists modify_column_multiple_collate") - tk.MustExec("create table modify_column_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tk.MustExec("alter table modify_column_multiple_collate modify column a char(1) collate utf8mb4_bin;") - tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("modify_column_multiple_collate")) - require.NoError(t, err) - require.Equal(t, "utf8mb4", tt.Cols()[0].GetCharset()) - require.Equal(t, "utf8mb4_bin", tt.Cols()[0].GetCollate()) - require.Equal(t, "utf8mb4", tt.Meta().Charset) - require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) - - tk.MustExec("drop table if exists modify_column_multiple_collate;") - tk.MustExec("create table modify_column_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tk.MustExec("alter table modify_column_multiple_collate modify column a char(1) charset utf8mb4 collate utf8mb4_bin;") - tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("modify_column_multiple_collate")) - require.NoError(t, err) - require.Equal(t, "utf8mb4", tt.Cols()[0].GetCharset()) - require.Equal(t, "utf8mb4_bin", tt.Cols()[0].GetCollate()) - require.Equal(t, "utf8mb4", tt.Meta().Charset) - require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) - - // test Err case for multiple collate modification in column. - tk.MustExec("drop table if exists err_modify_multiple_collate;") - tk.MustExec("create table err_modify_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tk.MustGetErrMsg("alter table err_modify_multiple_collate modify column a char(1) charset utf8mb4 collate utf8_bin;", dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8_bin", "utf8mb4").Error()) - - tk.MustExec("drop table if exists err_modify_multiple_collate;") - tk.MustExec("create table err_modify_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") - tk.MustGetErrMsg("alter table err_modify_multiple_collate modify column a char(1) collate utf8_bin collate utf8mb4_bin;", dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8mb4_bin", "utf8").Error()) -} - -func TestColumnCharsetAndCollate(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - dbName := "col_charset_collate" - tk.MustExec("create database " + dbName) - tk.MustExec("use " + dbName) - tests := []struct { - colType string - charset string - collates string - exptCharset string - exptCollate string - errMsg string - }{ - { - colType: "varchar(10)", - charset: "charset utf8", - collates: "collate utf8_bin", - exptCharset: "utf8", - exptCollate: "utf8_bin", - errMsg: "", - }, - { - colType: "varchar(10)", - charset: "charset utf8mb4", - collates: "", - exptCharset: "utf8mb4", - exptCollate: "utf8mb4_bin", - errMsg: "", - }, - { - colType: "varchar(10)", - charset: "charset utf16", - collates: "", - exptCharset: "", - exptCollate: "", - errMsg: "Unknown charset utf16", - }, - { - colType: "varchar(10)", - charset: "charset latin1", - collates: "", - exptCharset: "latin1", - exptCollate: "latin1_bin", - errMsg: "", - }, - { - colType: "varchar(10)", - charset: "charset binary", - collates: "", - exptCharset: "binary", - exptCollate: "binary", - errMsg: "", - }, - { - colType: "varchar(10)", - charset: "charset ascii", - collates: "", - exptCharset: "ascii", - exptCollate: "ascii_bin", - errMsg: "", - }, - } - sctx := tk.Session() - dm := domain.GetDomain(sctx) - for i, tt := range tests { - tblName := fmt.Sprintf("t%d", i) - sql := fmt.Sprintf("create table %s (a %s %s %s)", tblName, tt.colType, tt.charset, tt.collates) - if tt.errMsg == "" { - tk.MustExec(sql) - is := dm.InfoSchema() - require.NotNil(t, is) - - tb, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) - require.NoError(t, err) - require.Equalf(t, tt.exptCharset, tb.Meta().Columns[0].GetCharset(), sql) - require.Equalf(t, tt.exptCollate, tb.Meta().Columns[0].GetCollate(), sql) - } else { - err := tk.ExecToErr(sql) - require.Errorf(t, err, sql) - } - } - tk.MustExec("drop database " + dbName) -} - -func TestTooLargeIdentifierLength(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // for database. - dbName1, dbName2 := strings.Repeat("a", mysql.MaxDatabaseNameLength), strings.Repeat("a", mysql.MaxDatabaseNameLength+1) - tk.MustExec(fmt.Sprintf("create database %s", dbName1)) - tk.MustExec(fmt.Sprintf("drop database %s", dbName1)) - tk.MustGetErrMsg(fmt.Sprintf("create database %s", dbName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", dbName2)) - - // for table. - tk.MustExec("use test") - tableName1, tableName2 := strings.Repeat("b", mysql.MaxTableNameLength), strings.Repeat("b", mysql.MaxTableNameLength+1) - tk.MustExec(fmt.Sprintf("create table %s(c int)", tableName1)) - tk.MustExec(fmt.Sprintf("drop table %s", tableName1)) - tk.MustGetErrMsg(fmt.Sprintf("create table %s(c int)", tableName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", tableName2)) - - // for column. - tk.MustExec("drop table if exists t;") - columnName1, columnName2 := strings.Repeat("c", mysql.MaxColumnNameLength), strings.Repeat("c", mysql.MaxColumnNameLength+1) - tk.MustExec(fmt.Sprintf("create table t(%s int)", columnName1)) - tk.MustExec("drop table t") - tk.MustGetErrMsg(fmt.Sprintf("create table t(%s int)", columnName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", columnName2)) - - // for index. - tk.MustExec("create table t(c int);") - indexName1, indexName2 := strings.Repeat("d", mysql.MaxIndexIdentifierLen), strings.Repeat("d", mysql.MaxIndexIdentifierLen+1) - tk.MustExec(fmt.Sprintf("create index %s on t(c)", indexName1)) - tk.MustExec(fmt.Sprintf("drop index %s on t", indexName1)) - tk.MustGetErrMsg(fmt.Sprintf("create index %s on t(c)", indexName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", indexName2)) - - // for create table with index. - tk.MustExec("drop table t;") - tk.MustGetErrMsg(fmt.Sprintf("create table t(c int, index %s(c));", indexName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", indexName2)) -} - -func TestShardRowIDBits(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t (a int) shard_row_id_bits = 15") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t values (?)", i) - } - - dom := domain.GetDomain(tk.Session()) - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - assertCountAndShard := func(tt table.Table, expectCount int) { - var hasShardedID bool - var count int - require.NoError(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - err = tables.IterRecords(tt, tk.Session(), nil, func(h kv.Handle, rec []types.Datum, cols []*table.Column) (more bool, err error) { - require.GreaterOrEqual(t, h.IntValue(), int64(0)) - first8bits := h.IntValue() >> 56 - if first8bits > 0 { - hasShardedID = true - } - count++ - return true, nil - }) - require.NoError(t, err) - require.Equal(t, expectCount, count) - require.True(t, hasShardedID) - } - - assertCountAndShard(tbl, 100) - - // After PR 10759, shard_row_id_bits is supported with tables with auto_increment column. - tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 4") - tk.MustExec("alter table auto shard_row_id_bits = 5") - tk.MustExec("drop table auto") - tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 0") - tk.MustExec("alter table auto shard_row_id_bits = 5") - tk.MustExec("drop table auto") - tk.MustExec("create table auto (id int not null auto_increment unique)") - tk.MustExec("alter table auto shard_row_id_bits = 5") - tk.MustExec("drop table auto") - tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 4") - tk.MustExec("alter table auto shard_row_id_bits = 0") - tk.MustExec("drop table auto") - - errMsg := "[ddl:8200]Unsupported shard_row_id_bits for table with primary key as row id" - tk.MustGetErrMsg("create table auto (id varchar(255) primary key clustered, b int) shard_row_id_bits = 4;", errMsg) - tk.MustExec("create table auto (id varchar(255) primary key clustered, b int) shard_row_id_bits = 0;") - tk.MustGetErrMsg("alter table auto shard_row_id_bits = 5;", errMsg) - tk.MustExec("alter table auto shard_row_id_bits = 0;") - tk.MustExec("drop table if exists auto;") - - // After PR 10759, shard_row_id_bits is not supported with pk_is_handle tables. - tk.MustGetErrMsg("create table auto (id int not null auto_increment primary key, b int) shard_row_id_bits = 4", errMsg) - tk.MustExec("create table auto (id int not null auto_increment primary key, b int) shard_row_id_bits = 0") - tk.MustGetErrMsg("alter table auto shard_row_id_bits = 5", errMsg) - tk.MustExec("alter table auto shard_row_id_bits = 0") - - // Hack an existing table with shard_row_id_bits and primary key as handle - db, ok := dom.InfoSchema().SchemaByName(model.NewCIStr("test")) - require.True(t, ok) - tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto")) - tblInfo := tbl.Meta() - tblInfo.ShardRowIDBits = 5 - tblInfo.MaxShardRowIDBits = 5 - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - _, err = m.GenSchemaVersion() - require.NoError(t, err) - require.Nil(t, m.UpdateTable(db.ID, tblInfo)) - return nil - }) - require.NoError(t, err) - err = dom.Reload() - require.NoError(t, err) - - tk.MustExec("insert auto(b) values (1), (3), (5)") - tk.MustQuery("select id from auto order by id").Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("alter table auto shard_row_id_bits = 0") - tk.MustExec("drop table auto") - - // Test shard_row_id_bits with auto_increment column - tk.MustExec("create table auto (a int, b int auto_increment unique) shard_row_id_bits = 15") - for i := 0; i < 100; i++ { - tk.MustExec("insert into auto(a) values (?)", i) - } - tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto")) - assertCountAndShard(tbl, 100) - prevB, err := strconv.Atoi(tk.MustQuery("select b from auto where a=0").Rows()[0][0].(string)) - require.NoError(t, err) - for i := 1; i < 100; i++ { - b, err := strconv.Atoi(tk.MustQuery(fmt.Sprintf("select b from auto where a=%d", i)).Rows()[0][0].(string)) - require.NoError(t, err) - require.Greater(t, b, prevB) - prevB = b - } - - // Test overflow - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int) shard_row_id_bits = 15") - defer tk.MustExec("drop table if exists t1") - - tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - maxID := 1<<(64-15-1) - 1 - alloc := tbl.Allocators(tk.Session()).Get(autoid.RowIDAllocType) - err = alloc.Rebase(context.Background(), int64(maxID)-1, false) - require.NoError(t, err) - tk.MustExec("insert into t1 values(1)") - - // continue inserting will fail. - tk.MustGetDBError("insert into t1 values(2)", autoid.ErrAutoincReadFailed) - tk.MustGetDBError("insert into t1 values(3)", autoid.ErrAutoincReadFailed) -} - -func TestAutoRandomBitsData(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create database if not exists test_auto_random_bits") - defer tk.MustExec("drop database if exists test_auto_random_bits") - tk.MustExec("use test_auto_random_bits") - tk.MustExec("drop table if exists t") - - extractAllHandles := func() []int64 { - allHds, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test_auto_random_bits", "t") - require.NoError(t, err) - return allHds - } - - tk.MustExec("set @@allow_auto_random_explicit_insert = true") - - tk.MustExec("create table t (a bigint primary key clustered auto_random(15), b int)") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t(b) values (?)", i) - } - allHandles := extractAllHandles() - tk.MustExec("drop table t") - - // Test auto random id number. - require.Equal(t, 100, len(allHandles)) - // Test the handles are not all zero. - allZero := true - for _, h := range allHandles { - allZero = allZero && (h>>(64-16)) == 0 - } - require.False(t, allZero) - // Test non-shard-bits part of auto random id is monotonic increasing and continuous. - orderedHandles := testutil.MaskSortHandles(allHandles, 15, mysql.TypeLonglong) - size := int64(len(allHandles)) - for i := int64(1); i <= size; i++ { - require.Equal(t, orderedHandles[i-1], i) - } - - // Test explicit insert. - autoRandBitsUpperBound := 2<<47 - 1 - tk.MustExec("create table t (a bigint primary key clustered auto_random(15), b int)") - for i := -10; i < 10; i++ { - tk.MustExec(fmt.Sprintf("insert into t values(%d, %d)", i+autoRandBitsUpperBound, i)) - } - tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) - tk.MustExec("drop table t") - - // Test overflow. - tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") - // Here we cannot fill the all values for a `bigint` column, - // so firstly we rebase auto_rand to the position before overflow. - tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", autoRandBitsUpperBound, 1)) - tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) - tk.MustExec("drop table t") - - tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") - tk.MustExec("insert into t values (1, 2)") - tk.MustExec(fmt.Sprintf("update t set a = %d where a = 1", autoRandBitsUpperBound)) - tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) - tk.MustExec("drop table t") - - // Test insert negative integers explicitly won't trigger rebase. - tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") - for i := 1; i <= 100; i++ { - tk.MustExec("insert into t(b) values (?)", i) - tk.MustExec("insert into t(a, b) values (?, ?)", -i, i) - } - // orderedHandles should be [-100, -99, ..., -2, -1, 1, 2, ..., 99, 100] - orderedHandles = testutil.MaskSortHandles(extractAllHandles(), 15, mysql.TypeLonglong) - size = int64(len(allHandles)) - for i := int64(0); i < 100; i++ { - require.Equal(t, i-100, orderedHandles[i]) - } - for i := int64(100); i < size; i++ { - require.Equal(t, i-99, orderedHandles[i]) - } - tk.MustExec("drop table t") - - // Test signed/unsigned types. - tk.MustExec("create table t (a bigint primary key auto_random(10), b int)") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t (b) values(?)", i) - } - for _, h := range extractAllHandles() { - // Sign bit should be reserved. - require.True(t, h > 0) - } - tk.MustExec("drop table t") - - tk.MustExec("create table t (a bigint unsigned primary key auto_random(10), b int)") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t (b) values(?)", i) - } - signBitUnused := true - for _, h := range extractAllHandles() { - signBitUnused = signBitUnused && (h > 0) - } - // Sign bit should be used for shard. - require.False(t, signBitUnused) - tk.MustExec("drop table t;") - - // Test rename table does not affect incremental part of auto_random ID. - tk.MustExec("create database test_auto_random_bits_rename;") - tk.MustExec("create table t (a bigint auto_random primary key);") - for i := 0; i < 10; i++ { - tk.MustExec("insert into t values ();") - } - tk.MustExec("alter table t rename to test_auto_random_bits_rename.t1;") - for i := 0; i < 10; i++ { - tk.MustExec("insert into test_auto_random_bits_rename.t1 values ();") - } - tk.MustExec("alter table test_auto_random_bits_rename.t1 rename to t;") - for i := 0; i < 10; i++ { - tk.MustExec("insert into t values ();") - } - uniqueHandles := make(map[int64]struct{}) - for _, h := range extractAllHandles() { - uniqueHandles[h&((1<<(63-5))-1)] = struct{}{} - } - require.Equal(t, 30, len(uniqueHandles)) - tk.MustExec("drop database test_auto_random_bits_rename;") - tk.MustExec("drop table t;") -} - -func TestAutoRandomTableOption(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // test table option is auto-random - tk.MustExec("drop table if exists auto_random_table_option") - tk.MustExec("create table auto_random_table_option (a bigint auto_random(5) key) auto_random_base = 1000") - tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto_random_table_option")) - require.NoError(t, err) - require.Equal(t, int64(1000), tt.Meta().AutoRandID) - tk.MustExec("insert into auto_random_table_option values (),(),(),(),()") - allHandles, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "auto_random_table_option") - require.NoError(t, err) - require.Equal(t, 5, len(allHandles)) - // Test non-shard-bits part of auto random id is monotonic increasing and continuous. - orderedHandles := testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - size := int64(len(allHandles)) - for i := int64(0); i < size; i++ { - require.Equal(t, orderedHandles[i], i+1000) - } - - tk.MustExec("drop table if exists alter_table_auto_random_option") - tk.MustExec("create table alter_table_auto_random_option (a bigint primary key auto_random(4), b int)") - tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("alter_table_auto_random_option")) - require.NoError(t, err) - require.Equal(t, int64(0), tt.Meta().AutoRandID) - tk.MustExec("insert into alter_table_auto_random_option values(),(),(),(),()") - allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "alter_table_auto_random_option") - require.NoError(t, err) - orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - size = int64(len(allHandles)) - for i := int64(0); i < size; i++ { - require.Equal(t, i+1, orderedHandles[i]) - } - tk.MustExec("delete from alter_table_auto_random_option") - - // alter table to change the auto_random option (it will dismiss the local allocator cache) - // To avoid the new base is in the range of local cache, which will leading the next - // value is not what we rebased, because the local cache is dropped, here we choose - // a quite big value to do this. - tk.MustExec("alter table alter_table_auto_random_option auto_random_base = 3000000") - tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("alter_table_auto_random_option")) - require.NoError(t, err) - require.Equal(t, int64(3000000), tt.Meta().AutoRandID) - tk.MustExec("insert into alter_table_auto_random_option values(),(),(),(),()") - allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "alter_table_auto_random_option") - require.NoError(t, err) - orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) - size = int64(len(allHandles)) - for i := int64(0); i < size; i++ { - require.Equal(t, i+3000000, orderedHandles[i]) - } - tk.MustExec("drop table alter_table_auto_random_option") - - // Alter auto_random_base on non auto_random table. - tk.MustExec("create table alter_auto_random_normal (a int)") - err = tk.ExecToErr("alter table alter_auto_random_normal auto_random_base = 100") - require.Error(t, err) - require.Contains(t, err.Error(), autoid.AutoRandomRebaseNotApplicable) -} - -func TestSetDDLReorgWorkerCnt(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - err := ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(variable.DefTiDBDDLReorgWorkerCount), variable.GetDDLReorgWorkerCounter()) - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 1") - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(1), variable.GetDDLReorgWorkerCounter()) - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(100), variable.GetDDLReorgWorkerCounter()) - tk.MustGetDBError("set @@global.tidb_ddl_reorg_worker_cnt = invalid_val", variable.ErrWrongTypeForVar) - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(100), variable.GetDDLReorgWorkerCounter()) - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = -1") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_worker_cnt value: '-1'")) - tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt").Check(testkit.Rows("1")) - - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") - res := tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") - res.Check(testkit.Rows("100")) - - res = tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") - res.Check(testkit.Rows("100")) - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") - res = tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") - res.Check(testkit.Rows("100")) - - tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 257") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_worker_cnt value: '257'")) - tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt").Check(testkit.Rows("256")) -} - -func TestSetDDLReorgBatchSize(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - err := ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(variable.DefTiDBDDLReorgBatchSize), variable.GetDDLReorgBatchSize()) - - tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 1") - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '1'")) - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, variable.MinDDLReorgBatchSize, variable.GetDDLReorgBatchSize()) - tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_reorg_batch_size = %v", variable.MaxDDLReorgBatchSize+1)) - tk.MustQuery("show warnings;").Check(testkit.Rows(fmt.Sprintf("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '%d'", variable.MaxDDLReorgBatchSize+1))) - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, variable.MaxDDLReorgBatchSize, variable.GetDDLReorgBatchSize()) - tk.MustGetDBError("set @@global.tidb_ddl_reorg_batch_size = invalid_val", variable.ErrWrongTypeForVar) - tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 100") - err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) - require.NoError(t, err) - require.Equal(t, int32(100), variable.GetDDLReorgBatchSize()) - tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = -1") - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '-1'")) - - tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 100") - res := tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") - res.Check(testkit.Rows("100")) - - res = tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") - res.Check(testkit.Rows(fmt.Sprintf("%v", 100))) - tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 1000") - res = tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") - res.Check(testkit.Rows("1000")) -} - -func TestIllegalFunctionCall4GeneratedColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // Test create an exist database - tk.MustExecToErr("CREATE database test") - - tk.MustGetErrMsg("create table t1 (b double generated always as (rand()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) - tk.MustGetErrMsg("create table t1 (a varchar(64), b varchar(1024) generated always as (load_file(a)) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) - tk.MustGetErrMsg("create table t1 (a datetime generated always as (curdate()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) - tk.MustGetErrMsg("create table t1 (a datetime generated always as (current_time()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) - tk.MustGetErrMsg("create table t1 (a datetime generated always as (current_timestamp()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) - tk.MustGetErrMsg("create table t1 (a datetime, b varchar(10) generated always as (localtime()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) - tk.MustGetErrMsg("create table t1 (a varchar(1024) generated always as (uuid()) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) - tk.MustGetErrMsg("create table t1 (a varchar(1024), b varchar(1024) generated always as (is_free_lock(a)) virtual);", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) - - tk.MustExec("create table t1 (a bigint not null primary key auto_increment, b bigint, c bigint as (b + 1));") - - tk.MustGetErrMsg("alter table t1 add column d varchar(1024) generated always as (database());", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("d").Error()) - - tk.MustExec("alter table t1 add column d bigint generated always as (b + 1); ") - - tk.MustGetErrMsg("alter table t1 modify column d bigint generated always as (connection_id());", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("d").Error()) - tk.MustGetErrMsg("alter table t1 change column c cc bigint generated always as (connection_id());", - dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("cc").Error()) -} - -func TestGeneratedColumnRelatedDDL(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // Test create an exist database - err := tk.ExecToErr("CREATE database test") - require.Error(t, err) - - tk.MustGetErrMsg("create table t1 (a bigint not null primary key auto_increment, b bigint as (a + 1));", - dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("b").Error()) - tk.MustExec("create table t1 (a bigint not null primary key auto_increment, b bigint, c bigint as (b + 1));") - tk.MustGetErrMsg("alter table t1 add column d bigint generated always as (a + 1);", - dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("d").Error()) - tk.MustExec("alter table t1 add column d bigint generated always as (b + 1);") - tk.MustGetErrMsg("alter table t1 modify column d bigint generated always as (a + 1);", - dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("d").Error()) - - // This mysql compatibility check can be disabled using tidb_enable_auto_increment_in_generated - tk.MustExec("set session tidb_enable_auto_increment_in_generated = 1;") - tk.MustExec("alter table t1 modify column d bigint generated always as (a + 1);") - - tk.MustGetErrMsg("alter table t1 add column e bigint as (z + 1);", - dbterror.ErrBadField.GenWithStackByArgs("z", "generated column function").Error()) - - tk.MustExec("drop table t1;") - - tk.MustExec("create table t1(a int, b int as (a+1), c int as (b+1));") - tk.MustExec("insert into t1 (a) values (1);") - tk.MustGetErrCode("alter table t1 modify column c int as (b+1) first;", mysql.ErrGeneratedColumnNonPrior) - tk.MustGetErrCode("alter table t1 modify column b int as (a+1) after c;", mysql.ErrGeneratedColumnNonPrior) - tk.MustQuery("select * from t1").Check(testkit.Rows("1 2 3")) -} - -func TestSetDDLErrorCountLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - err := ddlutil.LoadDDLVars(tk.Session()) - require.NoError(t, err) - require.Equal(t, int64(variable.DefTiDBDDLErrorCountLimit), variable.GetDDLErrorCountLimit()) - - tk.MustExec("set @@global.tidb_ddl_error_count_limit = -1") - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_error_count_limit value: '-1'")) - err = ddlutil.LoadDDLVars(tk.Session()) - require.NoError(t, err) - require.Equal(t, int64(0), variable.GetDDLErrorCountLimit()) - tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_error_count_limit = %v", uint64(math.MaxInt64)+1)) - tk.MustQuery("show warnings;").Check(testkit.Rows(fmt.Sprintf("Warning 1292 Truncated incorrect tidb_ddl_error_count_limit value: '%d'", uint64(math.MaxInt64)+1))) - err = ddlutil.LoadDDLVars(tk.Session()) - require.NoError(t, err) - require.Equal(t, int64(math.MaxInt64), variable.GetDDLErrorCountLimit()) - tk.MustGetDBError("set @@global.tidb_ddl_error_count_limit = invalid_val", variable.ErrWrongTypeForVar) - tk.MustExec("set @@global.tidb_ddl_error_count_limit = 100") - err = ddlutil.LoadDDLVars(tk.Session()) - require.NoError(t, err) - require.Equal(t, int64(100), variable.GetDDLErrorCountLimit()) - res := tk.MustQuery("select @@global.tidb_ddl_error_count_limit") - res.Check(testkit.Rows("100")) -} - -func TestLoadDDLDistributeVars(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.Equal(t, variable.DefTiDBEnableDistTask, disttask.TiDBEnableDistTask) - - tk.MustGetDBError("set @@global.tidb_enable_dist_task = invalid_val", variable.ErrWrongValueForVar) - require.Equal(t, disttask.TiDBEnableDistTask, variable.EnableDistTask.Load()) - tk.MustExec("set @@global.tidb_enable_dist_task = 'on'") - require.Equal(t, true, variable.EnableDistTask.Load()) - tk.MustExec(fmt.Sprintf("set @@global.tidb_enable_dist_task = %v", disttask.TiDBEnableDistTask)) - require.Equal(t, disttask.TiDBEnableDistTask, variable.EnableDistTask.Load()) -} - -// this test will change the fail-point `mockAutoIDChange`, so we move it to the `testRecoverTable` suite -func TestRenameTable(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("drop database if exists rename1") - tk.MustExec("drop database if exists rename2") - tk.MustExec("drop database if exists rename3") - - tk.MustExec("create database rename1") - tk.MustExec("create database rename2") - tk.MustExec("create database rename3") - tk.MustExec("create table rename1.t (a int primary key auto_increment)") - tk.MustExec("insert rename1.t values ()") - tk.MustExec("rename table rename1.t to rename2.t") - // Make sure the drop old database doesn't affect the rename3.t's operations. - tk.MustExec("drop database rename1") - tk.MustExec("insert rename2.t values ()") - tk.MustExec("rename table rename2.t to rename3.t") - tk.MustExec("insert rename3.t values ()") - tk.MustQuery("select * from rename3.t").Check(testkit.Rows("1", "5001", "10001")) - // Make sure the drop old database doesn't affect the rename3.t's operations. - tk.MustExec("drop database rename2") - tk.MustExec("insert rename3.t values ()") - tk.MustQuery("select * from rename3.t").Check(testkit.Rows("1", "5001", "10001", "10002")) - tk.MustExec("drop database rename3") - - tk.MustExec("create database rename1") - tk.MustExec("create database rename2") - tk.MustExec("create table rename1.t (a int primary key auto_increment)") - tk.MustExec("rename table rename1.t to rename2.t1") - tk.MustExec("insert rename2.t1 values ()") - result := tk.MustQuery("select * from rename2.t1") - result.Check(testkit.Rows("1")) - // Make sure the drop old database doesn't affect the t1's operations. - tk.MustExec("drop database rename1") - tk.MustExec("insert rename2.t1 values ()") - result = tk.MustQuery("select * from rename2.t1") - result.Check(testkit.Rows("1", "2")) - // Rename a table to another table in the same database. - tk.MustExec("rename table rename2.t1 to rename2.t2") - tk.MustExec("insert rename2.t2 values ()") - result = tk.MustQuery("select * from rename2.t2") - result.Check(testkit.Rows("1", "2", "5001")) - tk.MustExec("drop database rename2") - - tk.MustExec("create database rename1") - tk.MustExec("create database rename2") - tk.MustExec("create table rename1.t (a int primary key auto_increment)") - tk.MustExec("insert rename1.t values ()") - tk.MustExec("rename table rename1.t to rename2.t1") - // Make sure the value is greater than autoid.step. - tk.MustExec("insert rename2.t1 values (100000)") - tk.MustExec("insert rename2.t1 values ()") - result = tk.MustQuery("select * from rename2.t1") - result.Check(testkit.Rows("1", "100000", "100001")) - tk.MustExecToErr("insert rename1.t values ()") - tk.MustExec("drop database rename1") - tk.MustExec("drop database rename2") -} - -func TestAutoIncrementColumnErrorMessage(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // Test create an exist database - tk.MustExecToErr("CREATE database test") - - tk.MustExec("CREATE TABLE t1 (t1_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY);") - - tk.MustGetErrMsg("CREATE INDEX idx1 ON t1 ((t1_id + t1_id));", - dbterror.ErrExpressionIndexCanNotRefer.GenWithStackByArgs("idx1").Error()) - - // This mysql compatibility check can be disabled using tidb_enable_auto_increment_in_generated - tk.MustExec("SET SESSION tidb_enable_auto_increment_in_generated = 1;") - tk.MustExec("CREATE INDEX idx1 ON t1 ((t1_id + t1_id));") -} - -func TestRenameMultiTables(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("drop database if exists rename1") - tk.MustExec("drop database if exists rename2") - tk.MustExec("drop database if exists rename3") - tk.MustExec("drop database if exists rename4") - - tk.MustExec("create database rename1") - tk.MustExec("create database rename2") - tk.MustExec("create database rename3") - tk.MustExec("create database rename4") - tk.MustExec("create table rename1.t1 (a int primary key auto_increment)") - tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") - tk.MustExec("insert rename1.t1 values ()") - tk.MustExec("insert rename3.t3 values ()") - tk.MustExec("rename table rename1.t1 to rename2.t2, rename3.t3 to rename4.t4") - // Make sure the drop old database doesn't affect t2,t4's operations. - tk.MustExec("drop database rename1") - tk.MustExec("insert rename2.t2 values ()") - tk.MustExec("drop database rename3") - tk.MustExec("insert rename4.t4 values ()") - tk.MustQuery("select * from rename2.t2").Check(testkit.Rows("1", "5001")) - tk.MustQuery("select * from rename4.t4").Check(testkit.Rows("1", "5001")) - // Rename a table to another table in the same database. - tk.MustExec("rename table rename2.t2 to rename2.t1, rename4.t4 to rename4.t3") - tk.MustExec("insert rename2.t1 values ()") - tk.MustQuery("select * from rename2.t1").Check(testkit.Rows("1", "5001", "10001")) - tk.MustExec("insert rename4.t3 values ()") - tk.MustQuery("select * from rename4.t3").Check(testkit.Rows("1", "5001", "10001")) - tk.MustExec("drop database rename2") - tk.MustExec("drop database rename4") - - tk.MustExec("create database rename1") - tk.MustExec("create database rename2") - tk.MustExec("create database rename3") - tk.MustExec("create table rename1.t1 (a int primary key auto_increment)") - tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") - tk.MustGetErrCode("rename table rename1.t1 to rename2.t2, rename3.t3 to rename2.t2", errno.ErrTableExists) - tk.MustExec("rename table rename1.t1 to rename2.t2, rename2.t2 to rename1.t1") - tk.MustExec("rename table rename1.t1 to rename2.t2, rename3.t3 to rename1.t1") - tk.MustExec("use rename1") - tk.MustQuery("show tables").Check(testkit.Rows("t1")) - tk.MustExec("use rename2") - tk.MustQuery("show tables").Check(testkit.Rows("t2")) - tk.MustExec("use rename3") - tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") - tk.MustGetErrCode("rename table rename1.t1 to rename1.t2, rename1.t1 to rename3.t3", errno.ErrTableExists) - tk.MustGetErrCode("rename table rename1.t1 to rename1.t2, rename1.t1 to rename3.t4", errno.ErrNoSuchTable) - tk.MustExec("drop database rename1") - tk.MustExec("drop database rename2") - tk.MustExec("drop database rename3") -} - -func TestCheckPrimaryKeyForTTLTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // create table should fail when pk contains double/float - tk.MustGetDBError("create table t1(id float primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("create table t1(id float(10,2) primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("create table t1(id double primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("create table t1(id float(10,2) primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("create table t1(id1 int, id2 float, t timestamp, primary key(id1, id2)) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("create table t1(id1 int, id2 double, t timestamp, primary key(id1, id2)) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - - // alter table should fail when pk contains double/float - tk.MustExec("create table t1(id float primary key, t timestamp)") - tk.MustExec("create table t2(id double primary key, t timestamp)") - tk.MustExec("create table t3(id1 int, id2 float, primary key(id1, id2), t timestamp)") - tk.MustExec("create table t4(id1 int, id2 double, primary key(id1, id2), t timestamp)") - tk.MustGetDBError("alter table t1 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("alter table t2 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("alter table t3 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - tk.MustGetDBError("alter table t4 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) - - // create table should not fail when the pk is not clustered - tk.MustExec("create table t11(id float primary key nonclustered, t timestamp) TTL=`t`+INTERVAL 1 DAY") - tk.MustExec("create table t12(id double primary key nonclustered, t timestamp) TTL=`t`+INTERVAL 1 DAY") - tk.MustExec("create table t13(id1 int, id2 float, t timestamp, primary key(id1, id2) nonclustered) TTL=`t`+INTERVAL 1 DAY") - - // alter table should not fail when the pk is not clustered - tk.MustExec("create table t21(id float primary key nonclustered, t timestamp)") - tk.MustExec("create table t22(id double primary key nonclustered, t timestamp)") - tk.MustExec("create table t23(id1 int, id2 float, t timestamp, primary key(id1, id2) nonclustered)") - tk.MustExec("alter table t21 TTL=`t`+INTERVAL 1 DAY") - tk.MustExec("alter table t22 TTL=`t`+INTERVAL 1 DAY") - tk.MustExec("alter table t23 TTL=`t`+INTERVAL 1 DAY") -} diff --git a/executor/test/ddl/main_test.go b/executor/test/ddl/main_test.go deleted file mode 100644 index 454579cc724bc..0000000000000 --- a/executor/test/ddl/main_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package ddl - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/distsqltest/BUILD.bazel b/executor/test/distsqltest/BUILD.bazel deleted file mode 100644 index 72908a9279fcc..0000000000000 --- a/executor/test/distsqltest/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "distsqltest_test", - timeout = "short", - srcs = [ - "distsql_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - deps = [ - "//config", - "//kv", - "//meta/autoid", - "//sessionctx/variable", - "//testkit", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/distsqltest/distsql_test.go b/executor/test/distsqltest/distsql_test.go deleted file mode 100644 index 6d634b12f6a7f..0000000000000 --- a/executor/test/distsqltest/distsql_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package distsql_test - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestDistsqlPartitionTableConcurrency(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1(id int primary key , val int)") - partitions := make([]string, 0, 20) - for i := 0; i < 20; i++ { - pid := i + 1 - partitions = append(partitions, fmt.Sprintf("PARTITION p%d VALUES LESS THAN (%d00)", pid, pid)) - } - tk.MustExec("create table t2(id int primary key, val int)" + - "partition by range(id)" + - "(" + strings.Join(partitions[:10], ",") + ")") - tk.MustExec("create table t3(id int primary key, val int)" + - "partition by range(id)" + - "(" + strings.Join(partitions, ",") + ")") - for i := 0; i < 20; i++ { - for _, tbl := range []string{"t1", "t2", "t3"} { - tk.MustExec(fmt.Sprintf("insert into %s values(%d, %d)", tbl, i*50, i*50)) - } - } - tk.MustExec("analyze table t1, t2, t3") - // non-partitioned table checker - ctx1 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { - require.Equal(t, req.KeyRanges.PartitionNum(), 1) - require.Equal(t, req.Concurrency, 1) - }) - // 10-ranges-partitioned table checker - ctx2 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { - require.Equal(t, req.KeyRanges.PartitionNum(), 10) - require.Equal(t, req.Concurrency, 10) - }) - // 20-ranges-partitioned table checker - ctx3 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { - require.Equal(t, req.KeyRanges.PartitionNum(), 20) - require.Equal(t, req.Concurrency, variable.DefDistSQLScanConcurrency) - }) - ctxs := []context.Context{ctx1, ctx2, ctx3} - for i, tbl := range []string{"t1", "t2", "t3"} { - ctx := ctxs[i] - // If order by is added here, the concurrency is always equal to 1. - // Because we will use different kv.Request for each partition in TableReader. - tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 1", tbl)) - tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 5", tbl)) - tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 1", tbl)) - tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 5", tbl)) - } -} diff --git a/executor/test/distsqltest/main_test.go b/executor/test/distsqltest/main_test.go deleted file mode 100644 index 1ef29dda80cce..0000000000000 --- a/executor/test/distsqltest/main_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package distsql_test - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/executor/BUILD.bazel b/executor/test/executor/BUILD.bazel deleted file mode 100644 index 2d9a52b367388..0000000000000 --- a/executor/test/executor/BUILD.bazel +++ /dev/null @@ -1,60 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "executor_test", - timeout = "short", - srcs = [ - "executor_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//ddl", - "//domain", - "//domain/infosync", - "//executor", - "//expression", - "//infoschema", - "//kv", - "//meta", - "//meta/autoid", - "//parser", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner", - "//planner/core", - "//session", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//store/driver/error", - "//store/mockstore", - "//table/tables", - "//tablecodec", - "//testkit", - "//testkit/testdata", - "//types", - "//util", - "//util/memory", - "//util/mock", - "//util/replayer", - "//util/rowcodec", - "//util/sqlexec", - "//util/timeutil", - "@com_github_golang_protobuf//proto", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/executor/executor_test.go b/executor/test/executor/executor_test.go deleted file mode 100644 index 3e1f5eaf14354..0000000000000 --- a/executor/test/executor/executor_test.go +++ /dev/null @@ -1,4301 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "archive/zip" - "context" - "fmt" - "math" - "path/filepath" - "reflect" - "runtime" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/golang/protobuf/proto" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - error2 "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tipb/go-tipb" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/testutils" -) - -func checkFileName(s string) bool { - files := []string{ - "config.toml", - "debug_trace/debug_trace0.json", - "meta.txt", - "stats/test.t_dump_single.json", - "schema/test.t_dump_single.schema.txt", - "schema/schema_meta.txt", - "table_tiflash_replica.txt", - "variables.toml", - "session_bindings.sql", - "global_bindings.sql", - "sql/sql0.sql", - "explain.txt", - "statsMem/test.t_dump_single.txt", - "sql_meta.toml", - } - for _, f := range files { - if strings.Compare(f, s) == 0 { - return true - } - } - return false -} - -func TestBind(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists testbind") - - tk.MustExec("create table testbind(i int, s varchar(20))") - tk.MustExec("create index index_t on testbind(i,s)") - tk.MustExec("create global binding for select * from testbind using select * from testbind use index for join(index_t)") - require.Len(t, tk.MustQuery("show global bindings").Rows(), 1) - - tk.MustExec("create session binding for select * from testbind using select * from testbind use index for join(index_t)") - require.Len(t, tk.MustQuery("show session bindings").Rows(), 1) - tk.MustExec("drop session binding for select * from testbind") -} - -func TestLoadStats(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.Error(t, tk.ExecToErr("load stats")) - require.Error(t, tk.ExecToErr("load stats ./xxx.json")) -} - -func TestPlanReplayer(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx_a(a))") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustQuery("plan replayer dump explain select * from t where a=10") - tk.MustQuery("plan replayer dump explain select /*+ read_from_storage(tiflash[t]) */ * from t") - - tk.MustExec("create table t1 (a int)") - tk.MustExec("create table t2 (a int)") - tk.MustExec("create definer=`root`@`127.0.0.1` view v1 as select * from t1") - tk.MustExec("create definer=`root`@`127.0.0.1` view v2 as select * from v1") - tk.MustQuery("plan replayer dump explain with tmp as (select a from t1 group by t1.a) select * from tmp, t2 where t2.a=tmp.a;") - tk.MustQuery("plan replayer dump explain select * from t1 where t1.a > (with cte1 as (select 1) select count(1) from cte1);") - tk.MustQuery("plan replayer dump explain select * from v1") - tk.MustQuery("plan replayer dump explain select * from v2") - require.True(t, len(tk.Session().GetSessionVars().LastPlanReplayerToken) > 0) - - // clear the status table and assert - tk.MustExec("delete from mysql.plan_replayer_status") - tk.MustQuery("plan replayer dump explain select * from v2") - token := tk.Session().GetSessionVars().LastPlanReplayerToken - rows := tk.MustQuery(fmt.Sprintf("select * from mysql.plan_replayer_status where token = '%v'", token)).Rows() - require.Len(t, rows, 1) -} - -func TestPlanReplayerCaptureSEM(t *testing.T) { - originSEM := config.GetGlobalConfig().Security.EnableSEM - defer func() { - config.GetGlobalConfig().Security.EnableSEM = originSEM - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("plan replayer capture '123' '123';") - tk.MustExec("create table t(id int)") - tk.MustQuery("plan replayer dump explain select * from t") - tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1")) -} - -func TestPlanReplayerCapture(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("plan replayer capture '123' '123';") - tk.MustQuery("select sql_digest, plan_digest from mysql.plan_replayer_task;").Check(testkit.Rows("123 123")) - tk.MustGetErrMsg("plan replayer capture '123' '123';", "plan replayer capture task already exists") - tk.MustExec("plan replayer capture remove '123' '123'") - tk.MustQuery("select count(*) from mysql.plan_replayer_task;").Check(testkit.Rows("0")) - tk.MustExec("create table t(id int)") - tk.MustExec("prepare stmt from 'update t set id = ? where id = ? + 1';") - tk.MustExec("SET @number = 5;") - tk.MustExec("execute stmt using @number,@number") - _, sqlDigest := tk.Session().GetSessionVars().StmtCtx.SQLDigest() - _, planDigest := tk.Session().GetSessionVars().StmtCtx.GetPlanDigest() - tk.MustExec("SET @@tidb_enable_plan_replayer_capture = ON;") - tk.MustExec("SET @@global.tidb_enable_historical_stats_for_capture='ON'") - tk.MustExec(fmt.Sprintf("plan replayer capture '%v' '%v'", sqlDigest.String(), planDigest.String())) - err := dom.GetPlanReplayerHandle().CollectPlanReplayerTask() - require.NoError(t, err) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/shouldDumpStats", "return(true)")) - defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/shouldDumpStats")) - tk.MustExec("execute stmt using @number,@number") - task := dom.GetPlanReplayerHandle().DrainTask() - require.NotNil(t, task) -} - -func TestPlanReplayerContinuesCapture(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set @@global.tidb_enable_historical_stats='OFF'") - _, err := tk.Exec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'") - require.Error(t, err) - require.Equal(t, err.Error(), "tidb_enable_historical_stats should be enabled before enabling tidb_enable_plan_replayer_continuous_capture") - - tk.MustExec("set @@global.tidb_enable_historical_stats='ON'") - tk.MustExec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'") - - prHandle := dom.GetPlanReplayerHandle() - tk.MustExec("delete from mysql.plan_replayer_status;") - tk.MustExec("use test") - tk.MustExec("create table t(id int);") - tk.MustExec("set @@tidb_enable_plan_replayer_continuous_capture = 'ON'") - tk.MustQuery("select * from t;") - task := prHandle.DrainTask() - require.NotNil(t, task) - worker := prHandle.GetWorker() - success := worker.HandleTask(task) - require.True(t, success) - tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1")) -} - -func TestShow(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database test_show;") - tk.MustExec("use test_show") - - tk.MustQuery("show engines") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key)") - require.Len(t, tk.MustQuery("show index in t").Rows(), 1) - require.Len(t, tk.MustQuery("show index from t").Rows(), 1) - require.Len(t, tk.MustQuery("show master status").Rows(), 1) - - tk.MustQuery("show create database test_show").Check(testkit.Rows("test_show CREATE DATABASE `test_show` /*!40100 DEFAULT CHARACTER SET utf8mb4 */")) - tk.MustQuery("show privileges").Check(testkit.Rows("Alter Tables To alter the table", - "Alter routine Functions,Procedures To alter or drop stored functions/procedures", - "Config Server Admin To use SHOW CONFIG and SET CONFIG statements", - "Create Databases,Tables,Indexes To create new databases and tables", - "Create routine Databases To use CREATE FUNCTION/PROCEDURE", - "Create role Server Admin To create new roles", - "Create temporary tables Databases To use CREATE TEMPORARY TABLE", - "Create view Tables To create new views", - "Create user Server Admin To create new users", - "Delete Tables To delete existing rows", - "Drop Databases,Tables To drop databases, tables, and views", - "Drop role Server Admin To drop roles", - "Event Server Admin To create, alter, drop and execute events", - "Execute Functions,Procedures To execute stored routines", - "File File access on server To read and write files on the server", - "Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess", - "Index Tables To create or drop indexes", - "Insert Tables To insert data into tables", - "Lock tables Databases To use LOCK TABLES (together with SELECT privilege)", - "Process Server Admin To view the plain text of currently executing queries", - "Proxy Server Admin To make proxy user possible", - "References Databases,Tables To have references on tables", - "Reload Server Admin To reload or refresh tables, logs and privileges", - "Replication client Server Admin To ask where the slave or master servers are", - "Replication slave Server Admin To read binary log events from the master", - "Select Tables To retrieve rows from table", - "Show databases Server Admin To see all databases with SHOW DATABASES", - "Show view Tables To see views with SHOW CREATE VIEW", - "Shutdown Server Admin To shut down the server", - "Super Server Admin To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.", - "Trigger Tables To use triggers", - "Create tablespace Server Admin To create/alter/drop tablespaces", - "Update Tables To update existing rows", - "Usage Server Admin No privileges - allow connect only", - "BACKUP_ADMIN Server Admin ", - "RESTORE_ADMIN Server Admin ", - "SYSTEM_USER Server Admin ", - "SYSTEM_VARIABLES_ADMIN Server Admin ", - "ROLE_ADMIN Server Admin ", - "CONNECTION_ADMIN Server Admin ", - "PLACEMENT_ADMIN Server Admin ", - "DASHBOARD_CLIENT Server Admin ", - "RESTRICTED_TABLES_ADMIN Server Admin ", - "RESTRICTED_STATUS_ADMIN Server Admin ", - "RESTRICTED_VARIABLES_ADMIN Server Admin ", - "RESTRICTED_USER_ADMIN Server Admin ", - "RESTRICTED_CONNECTION_ADMIN Server Admin ", - "RESTRICTED_REPLICA_WRITER_ADMIN Server Admin ", - "RESOURCE_GROUP_ADMIN Server Admin ", - )) - require.Len(t, tk.MustQuery("show table status").Rows(), 1) -} - -// TestSelectBackslashN Issue 3685. -func TestSelectBackslashN(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - sql := `select \N;` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err := tk.Exec(sql) - require.NoError(t, err) - fields := rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "NULL", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select "\N";` - tk.MustQuery(sql).Check(testkit.Rows("N")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - tk.MustExec("use test;") - tk.MustExec("create table test (`\\N` int);") - tk.MustExec("insert into test values (1);") - tk.CheckExecResult(1, 0) - sql = "select * from test;" - tk.MustQuery(sql).Check(testkit.Rows("1")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `\N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select \N from test;` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.NoError(t, err) - require.Len(t, fields, 1) - require.Equal(t, `NULL`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select (\N) from test;` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `NULL`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select `\\N` from test;" - tk.MustQuery(sql).Check(testkit.Rows("1")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `\N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select (`\\N`) from test;" - tk.MustQuery(sql).Check(testkit.Rows("1")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `\N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select '\N' from test;` - tk.MustQuery(sql).Check(testkit.Rows("N")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select ('\N') from test;` - tk.MustQuery(sql).Check(testkit.Rows("N")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `N`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) -} - -// TestSelectNull Issue #4053. -func TestSelectNull(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - sql := `select nUll;` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err := tk.Exec(sql) - require.NoError(t, err) - fields := rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `NULL`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select (null);` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `NULL`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select null+NULL;` - tk.MustQuery(sql).Check(testkit.Rows("")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.NoError(t, err) - require.Len(t, fields, 1) - require.Equal(t, `null+NULL`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) -} - -// TestSelectStringLiteral Issue #3686. -func TestSelectStringLiteral(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - sql := `select 'abc';` - tk.MustQuery(sql).Check(testkit.Rows("abc")) - rs, err := tk.Exec(sql) - require.NoError(t, err) - fields := rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `abc`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select (('abc'));` - tk.MustQuery(sql).Check(testkit.Rows("abc")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `abc`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select 'abc'+'def';` - tk.MustQuery(sql).Check(testkit.Rows("0")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, `'abc'+'def'`, fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - // Below checks whether leading invalid chars are trimmed. - sql = "select '\n';" - tk.MustQuery(sql).Check(testkit.Rows("\n")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select '\t col';" // Lowercased letter is a valid char. - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "col", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select '\t Col';" // Uppercased letter is a valid char. - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "Col", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select '\n\t 中文 col';" // Chinese char is a valid char. - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "中文 col", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select ' \r\n .col';" // Punctuation is a valid char. - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, ".col", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = "select ' 😆col';" // Emoji is a valid char. - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "😆col", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - // Below checks whether trailing invalid chars are preserved. - sql = `select 'abc ';` - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "abc ", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select ' abc 123 ';` - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "abc 123 ", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - // Issue #4239. - sql = `select 'a' ' ' 'string';` - tk.MustQuery(sql).Check(testkit.Rows("a string")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "a", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select 'a' " " "string";` - tk.MustQuery(sql).Check(testkit.Rows("a string")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "a", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select 'string' 'string';` - tk.MustQuery(sql).Check(testkit.Rows("stringstring")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "string", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select "ss" "a";` - tk.MustQuery(sql).Check(testkit.Rows("ssa")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "ss", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select "ss" "a" "b";` - tk.MustQuery(sql).Check(testkit.Rows("ssab")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "ss", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select "ss" "a" ' ' "b";` - tk.MustQuery(sql).Check(testkit.Rows("ssa b")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "ss", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) - - sql = `select "ss" "a" ' ' "b" ' ' "d";` - tk.MustQuery(sql).Check(testkit.Rows("ssa b d")) - rs, err = tk.Exec(sql) - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "ss", fields[0].Column.Name.O) - require.NoError(t, rs.Close()) -} - -func TestUpdateClustered(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - type resultChecker struct { - check string - assert []string - } - - for _, clustered := range []string{"", "clustered"} { - tests := []struct { - initSchema []string - initData []string - dml string - resultCheck []resultChecker - }{ - { // left join + update both + match & unmatched + pk - []string{ - "drop table if exists a, b", - "create table a (k1 int, k2 int, v int)", - fmt.Sprintf("create table b (a int not null, k1 int, k2 int, v int, primary key(k1, k2) %s)", clustered), - }, - []string{ - "insert into a values (1, 1, 1), (2, 2, 2)", // unmatched + matched - "insert into b values (2, 2, 2, 2)", - }, - "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.v = 20, b.v = 100, a.k1 = a.k1 + 1, b.k1 = b.k1 + 1, a.k2 = a.k2 + 2, b.k2 = b.k2 + 2", - []resultChecker{ - { - "select * from b", - []string{"2 3 4 100"}, - }, - { - "select * from a", - []string{"2 3 20", "3 4 20"}, - }, - }, - }, - { // left join + update both + match & unmatched + pk - []string{ - "drop table if exists a, b", - "create table a (k1 int, k2 int, v int)", - fmt.Sprintf("create table b (a int not null, k1 int, k2 int, v int, primary key(k1, k2) %s)", clustered), - }, - []string{ - "insert into a values (1, 1, 1), (2, 2, 2)", // unmatched + matched - "insert into b values (2, 2, 2, 2)", - }, - "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", - []resultChecker{ - { - "select * from b", - []string{"2 3 4 100"}, - }, - { - "select * from a", - []string{"2 3 20", "3 4 20"}, - }, - }, - }, - { // left join + update both + match & unmatched + prefix pk - []string{ - "drop table if exists a, b", - "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", - fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), - }, - []string{ - "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched - "insert into b values ('22', '22', '22', '22')", - }, - "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", - []resultChecker{ - { - "select * from b", - []string{"22 23 24 100"}, - }, - { - "select * from a", - []string{"12 13 20", "23 24 20"}, - }, - }, - }, - { // right join + update both + match & unmatched + prefix pk - []string{ - "drop table if exists a, b", - "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", - fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), - }, - []string{ - "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched - "insert into b values ('22', '22', '22', '22')", - }, - "update b right join a on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", - []resultChecker{ - { - "select * from b", - []string{"22 23 24 100"}, - }, - { - "select * from a", - []string{"12 13 20", "23 24 20"}, - }, - }, - }, - { // inner join + update both + match & unmatched + prefix pk - []string{ - "drop table if exists a, b", - "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", - fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), - }, - []string{ - "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched - "insert into b values ('22', '22', '22', '22')", - }, - "update b join a on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", - []resultChecker{ - { - "select * from b", - []string{"22 23 24 100"}, - }, - { - "select * from a", - []string{"11 11 11", "23 24 20"}, - }, - }, - }, - { - []string{ - "drop table if exists a, b", - "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", - fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), - }, - []string{ - "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched - "insert into b values ('22', '22', '22', '22')", - }, - "update a set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, a.v = 20 where exists (select 1 from b where a.k1 = b.k1 and a.k2 = b.k2)", - []resultChecker{ - { - "select * from b", - []string{"22 22 22 22"}, - }, - { - "select * from a", - []string{"11 11 11", "23 24 20"}, - }, - }, - }, - } - - for _, test := range tests { - for _, s := range test.initSchema { - tk.MustExec(s) - } - for _, s := range test.initData { - tk.MustExec(s) - } - tk.MustExec(test.dml) - for _, checker := range test.resultCheck { - tk.MustQuery(checker.check).Check(testkit.Rows(checker.assert...)) - } - tk.MustExec("admin check table a") - tk.MustExec("admin check table b") - } - } -} - -func TestClusterIndexOuterJoinElimination(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("create table t (a int, b int, c int, primary key(a,b))") - rows := tk.MustQuery(`explain format = 'brief' select t1.a from t t1 left join t t2 on t1.a = t2.a and t1.b = t2.b`).Rows() - rowStrs := testdata.ConvertRowsToStrings(rows) - for _, row := range rowStrs { - // outer join has been eliminated. - require.NotContains(t, row, "Join") - } -} - -func TestPlanReplayerDumpSingle(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_dump_single") - tk.MustExec("create table t_dump_single(a int)") - res := tk.MustQuery("plan replayer dump explain select * from t_dump_single") - path := testdata.ConvertRowsToStrings(res.Rows()) - - reader, err := zip.OpenReader(filepath.Join(replayer.GetPlanReplayerDirName(), path[0])) - require.NoError(t, err) - defer func() { require.NoError(t, reader.Close()) }() - for _, file := range reader.File { - require.True(t, checkFileName(file.Name), file.Name) - } -} - -func TestTimezonePushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (ts timestamp)") - defer tk.MustExec("drop table t") - tk.MustExec(`insert into t values ("2018-09-13 10:02:06")`) - - systemTZ := timeutil.SystemLocation() - require.NotEqual(t, "System", systemTZ.String()) - require.NotEqual(t, "Local", systemTZ.String()) - ctx := context.Background() - count := 0 - ctx1 := context.WithValue(ctx, "CheckSelectRequestHook", func(req *kv.Request) { - count++ - dagReq := new(tipb.DAGRequest) - require.NoError(t, proto.Unmarshal(req.Data, dagReq)) - require.Equal(t, systemTZ.String(), dagReq.GetTimeZoneName()) - }) - rs, err := tk.Session().Execute(ctx1, `select * from t where ts = "2018-09-13 10:02:06"`) - require.NoError(t, err) - rs[0].Close() - - tk.MustExec(`set time_zone="System"`) - rs, err = tk.Session().Execute(ctx1, `select * from t where ts = "2018-09-13 10:02:06"`) - require.NoError(t, err) - rs[0].Close() - - require.Equal(t, 2, count) // Make sure the hook function is called. -} - -func TestNotFillCacheFlag(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (id int primary key)") - tk.MustExec("insert into t values (1)") - - tests := []struct { - sql string - expect bool - }{ - {"select SQL_NO_CACHE * from t", true}, - {"select SQL_CACHE * from t", false}, - {"select * from t", false}, - } - count := 0 - ctx := context.Background() - for _, test := range tests { - ctx1 := context.WithValue(ctx, "CheckSelectRequestHook", func(req *kv.Request) { - count++ - comment := fmt.Sprintf("sql=%s, expect=%v, get=%v", test.sql, test.expect, req.NotFillCache) - require.Equal(t, test.expect, req.NotFillCache, comment) - }) - rs, err := tk.Session().Execute(ctx1, test.sql) - require.NoError(t, err) - tk.ResultSetToResult(rs[0], fmt.Sprintf("sql: %v", test.sql)) - } - require.Equal(t, len(tests), count) // Make sure the hook function is called. -} - -func TestExecutorBit(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (c1 bit(2))") - tk.MustExec("insert into t values (0), (1), (2), (3)") - err := tk.ExecToErr("insert into t values (4)") - require.Error(t, err) - err = tk.ExecToErr("insert into t values ('a')") - require.Error(t, err) - r, err := tk.Exec("select * from t where c1 = 2") - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(context.Background(), req) - require.NoError(t, err) - require.Equal(t, types.NewBinaryLiteralFromUint(2, -1), types.BinaryLiteral(req.GetRow(0).GetBytes(0))) - require.NoError(t, r.Close()) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(31))") - tk.MustExec("insert into t values (0x7fffffff)") - err = tk.ExecToErr("insert into t values (0x80000000)") - require.Error(t, err) - err = tk.ExecToErr("insert into t values (0xffffffff)") - require.Error(t, err) - tk.MustExec("insert into t values ('123')") - tk.MustExec("insert into t values ('1234')") - err = tk.ExecToErr("insert into t values ('12345)") - require.Error(t, err) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(62))") - tk.MustExec("insert into t values ('12345678')") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(61))") - err = tk.ExecToErr("insert into t values ('12345678')") - require.Error(t, err) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(32))") - tk.MustExec("insert into t values (0x7fffffff)") - tk.MustExec("insert into t values (0xffffffff)") - err = tk.ExecToErr("insert into t values (0x1ffffffff)") - require.Error(t, err) - tk.MustExec("insert into t values ('1234')") - err = tk.ExecToErr("insert into t values ('12345')") - require.Error(t, err) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(64))") - tk.MustExec("insert into t values (0xffffffffffffffff)") - tk.MustExec("insert into t values ('12345678')") - err = tk.ExecToErr("insert into t values ('123456789')") - require.Error(t, err) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 bit(64))") - tk.MustExec("insert into t values (0xffffffffffffffff)") - tk.MustExec("insert into t values ('12345678')") - tk.MustQuery("select * from t where c1").Check(testkit.Rows("\xff\xff\xff\xff\xff\xff\xff\xff", "12345678")) -} - -func TestCheckIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - ctx := mock.NewContext() - ctx.Store = store - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - - _, err = se.Execute(context.Background(), "create database test_admin") - require.NoError(t, err) - _, err = se.Execute(context.Background(), "use test_admin") - require.NoError(t, err) - _, err = se.Execute(context.Background(), "create table t (pk int primary key, c int default 1, c1 int default 1, unique key c(c))") - require.NoError(t, err) - - is := dom.InfoSchema() - db := model.NewCIStr("test_admin") - dbInfo, ok := is.SchemaByName(db) - require.True(t, ok) - - tblName := model.NewCIStr("t") - tbl, err := is.TableByName(db, tblName) - require.NoError(t, err) - tbInfo := tbl.Meta() - - alloc := autoid.NewAllocator(store, dbInfo.ID, tbInfo.ID, false, autoid.RowIDAllocType) - tb, err := tables.TableFromMeta(autoid.NewAllocators(false, alloc), tbInfo) - require.NoError(t, err) - - _, err = se.Execute(context.Background(), "admin check index t c") - require.NoError(t, err) - - _, err = se.Execute(context.Background(), "admin check index t C") - require.NoError(t, err) - - // set data to: - // index data (handle, data): (1, 10), (2, 20) - // table data (handle, data): (1, 10), (2, 20) - recordVal1 := types.MakeDatums(int64(1), int64(10), int64(11)) - recordVal2 := types.MakeDatums(int64(2), int64(20), int64(21)) - require.NoError(t, sessiontxn.NewTxn(context.Background(), ctx)) - _, err = tb.AddRecord(ctx, recordVal1) - require.NoError(t, err) - _, err = tb.AddRecord(ctx, recordVal2) - require.NoError(t, err) - txn, err := ctx.Txn(true) - require.NoError(t, err) - require.NoError(t, txn.Commit(context.Background())) - - mockCtx := mock.NewContext() - idx := tb.Indices()[0] - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - - _, err = se.Execute(context.Background(), "admin check index t idx_inexistent") - require.Error(t, err) - require.Contains(t, err.Error(), "not exist") - - // set data to: - // index data (handle, data): (1, 10), (2, 20), (3, 30) - // table data (handle, data): (1, 10), (2, 20), (4, 40) - txn, err = store.Begin() - require.NoError(t, err) - _, err = idx.Create(mockCtx, txn, types.MakeDatums(int64(30)), kv.IntHandle(3), nil) - require.NoError(t, err) - key := tablecodec.EncodeRowKey(tb.Meta().ID, kv.IntHandle(4).Encoded()) - setColValue(t, txn, key, types.NewDatum(int64(40))) - err = txn.Commit(context.Background()) - require.NoError(t, err) - _, err = se.Execute(context.Background(), "admin check index t c") - require.Error(t, err) - require.Equal(t, "[admin:8223]data inconsistency in table: t, index: c, handle: 3, index-values:\"handle: 3, values: [KindInt64 30]\" != record-values:\"\"", err.Error()) - - // set data to: - // index data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) - // table data (handle, data): (1, 10), (2, 20), (4, 40) - txn, err = store.Begin() - require.NoError(t, err) - _, err = idx.Create(mockCtx, txn, types.MakeDatums(int64(40)), kv.IntHandle(4), nil) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - _, err = se.Execute(context.Background(), "admin check index t c") - require.Error(t, err) - require.EqualError(t, err, "[admin:8223]data inconsistency in table: t, index: c, handle: 3, index-values:\"handle: 3, values: [KindInt64 30]\" != record-values:\"\"") - - // set data to: - // index data (handle, data): (1, 10), (4, 40) - // table data (handle, data): (1, 10), (2, 20), (4, 40) - txn, err = store.Begin() - require.NoError(t, err) - err = idx.Delete(sc, txn, types.MakeDatums(int64(30)), kv.IntHandle(3)) - require.NoError(t, err) - err = idx.Delete(sc, txn, types.MakeDatums(int64(20)), kv.IntHandle(2)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - _, err = se.Execute(context.Background(), "admin check index t c") - require.Error(t, err) - require.EqualError(t, err, "[admin:8223]data inconsistency in table: t, index: c, handle: 2, index-values:\"\" != record-values:\"handle: 2, values: [KindInt64 20]\"") - - // TODO: pass the case below: - // set data to: - // index data (handle, data): (1, 10), (4, 40), (2, 30) - // table data (handle, data): (1, 10), (2, 20), (4, 40) -} - -func setColValue(t *testing.T, txn kv.Transaction, key kv.Key, v types.Datum) { - row := []types.Datum{v, {}} - colIDs := []int64{2, 3} - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - rd := rowcodec.Encoder{Enable: true} - value, err := tablecodec.EncodeRow(sc, row, colIDs, nil, nil, &rd) - require.NoError(t, err) - err = txn.Set(key, value) - require.NoError(t, err) -} - -func TestTimestampTimeZone(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (ts timestamp)") - tk.MustExec("set time_zone = '+00:00'") - tk.MustExec("insert into t values ('2017-04-27 22:40:42')") - // The timestamp will get different value if time_zone session variable changes. - tests := []struct { - timezone string - expect string - }{ - {"+10:00", "2017-04-28 08:40:42"}, - {"-6:00", "2017-04-27 16:40:42"}, - } - for _, tt := range tests { - tk.MustExec(fmt.Sprintf("set time_zone = '%s'", tt.timezone)) - tk.MustQuery("select * from t").Check(testkit.Rows(tt.expect)) - } - - // For issue https://github.com/pingcap/tidb/issues/3467 - tk.MustExec("drop table if exists t1") - tk.MustExec(`CREATE TABLE t1 ( - id bigint(20) NOT NULL AUTO_INCREMENT, - uid int(11) DEFAULT NULL, - datetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - ip varchar(128) DEFAULT NULL, - PRIMARY KEY (id), - KEY i_datetime (datetime), - KEY i_userid (uid) - );`) - tk.MustExec(`INSERT INTO t1 VALUES (123381351,1734,"2014-03-31 08:57:10","127.0.0.1");`) - r := tk.MustQuery("select datetime from t1;") // Cover TableReaderExec - r.Check(testkit.Rows("2014-03-31 08:57:10")) - r = tk.MustQuery("select datetime from t1 where datetime='2014-03-31 08:57:10';") - r.Check(testkit.Rows("2014-03-31 08:57:10")) // Cover IndexReaderExec - r = tk.MustQuery("select * from t1 where datetime='2014-03-31 08:57:10';") - r.Check(testkit.Rows("123381351 1734 2014-03-31 08:57:10 127.0.0.1")) // Cover IndexLookupExec - - // For issue https://github.com/pingcap/tidb/issues/3485 - tk.MustExec("set time_zone = 'Asia/Shanghai'") - tk.MustExec("drop table if exists t1") - tk.MustExec(`CREATE TABLE t1 ( - id bigint(20) NOT NULL AUTO_INCREMENT, - datetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (id) - );`) - tk.MustExec(`INSERT INTO t1 VALUES (123381351,"2014-03-31 08:57:10");`) - r = tk.MustQuery(`select * from t1 where datetime="2014-03-31 08:57:10";`) - r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) - tk.MustExec(`alter table t1 add key i_datetime (datetime);`) - r = tk.MustQuery(`select * from t1 where datetime="2014-03-31 08:57:10";`) - r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) - r = tk.MustQuery(`select * from t1;`) - r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) - r = tk.MustQuery("select datetime from t1 where datetime='2014-03-31 08:57:10';") - r.Check(testkit.Rows("2014-03-31 08:57:10")) -} - -func TestTimestampDefaultValueTimeZone(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set time_zone = '+08:00'") - tk.MustExec(`create table t (a int, b timestamp default "2019-01-17 14:46:14")`) - tk.MustExec("insert into t set a=1") - r := tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 14:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustExec("set time_zone = '+00:00'") - tk.MustExec("insert into t set a=2") - r = tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 06:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - r = tk.MustQuery(`select a,b from t order by a`) - r.Check(testkit.Rows("1 2019-01-17 06:46:14", "2 2019-01-17 06:46:14")) - // Test the column's version is greater than ColumnInfoVersion1. - is := domain.GetDomain(tk.Session()).InfoSchema() - require.NotNil(t, is) - tb, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tb.Cols()[1].Version = model.ColumnInfoVersion1 + 1 - tk.MustExec("insert into t set a=3") - r = tk.MustQuery(`select a,b from t order by a`) - r.Check(testkit.Rows("1 2019-01-17 06:46:14", "2 2019-01-17 06:46:14", "3 2019-01-17 06:46:14")) - tk.MustExec("delete from t where a=3") - // Change time zone back. - tk.MustExec("set time_zone = '+08:00'") - r = tk.MustQuery(`select a,b from t order by a`) - r.Check(testkit.Rows("1 2019-01-17 14:46:14", "2 2019-01-17 14:46:14")) - tk.MustExec("set time_zone = '-08:00'") - r = tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-16 22:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // test zero default value in multiple time zone. - defer tk.MustExec(fmt.Sprintf("set @@sql_mode='%s'", tk.MustQuery("select @@sql_mode").Rows()[0][0])) - tk.MustExec("set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';") - tk.MustExec("drop table if exists t") - tk.MustExec("set time_zone = '+08:00'") - tk.MustExec(`create table t (a int, b timestamp default "0000-00-00 00")`) - tk.MustExec("insert into t set a=1") - r = tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustExec("set time_zone = '+00:00'") - tk.MustExec("insert into t set a=2") - r = tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustExec("set time_zone = '-08:00'") - tk.MustExec("insert into t set a=3") - r = tk.MustQuery(`show create table t`) - r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - r = tk.MustQuery(`select a,b from t order by a`) - r.Check(testkit.Rows("1 0000-00-00 00:00:00", "2 0000-00-00 00:00:00", "3 0000-00-00 00:00:00")) - - // test add timestamp column default current_timestamp. - tk.MustExec(`drop table if exists t`) - tk.MustExec(`set time_zone = 'Asia/Shanghai'`) - tk.MustExec(`create table t (a int)`) - tk.MustExec(`insert into t set a=1`) - tk.MustExec(`alter table t add column b timestamp not null default current_timestamp;`) - timeIn8 := tk.MustQuery("select b from t").Rows()[0][0] - tk.MustExec(`set time_zone = '+00:00'`) - timeIn0 := tk.MustQuery("select b from t").Rows()[0][0] - require.NotEqual(t, timeIn8, timeIn0) - datumTimeIn8, err := expression.GetTimeValue(tk.Session(), timeIn8, mysql.TypeTimestamp, 0, nil) - require.NoError(t, err) - tIn8To0 := datumTimeIn8.GetMysqlTime() - timeZoneIn8, err := time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - err = tIn8To0.ConvertTimeZone(timeZoneIn8, time.UTC) - require.NoError(t, err) - require.Equal(t, tIn8To0.String(), timeIn0) - - // test add index. - tk.MustExec(`alter table t add index(b);`) - tk.MustExec("admin check table t") - tk.MustExec(`set time_zone = '+05:00'`) - tk.MustExec("admin check table t") - - // 1. add a timestamp general column - // 2. add the index - tk.MustExec(`drop table if exists t`) - // change timezone - tk.MustExec(`set time_zone = 'Asia/Shanghai'`) - tk.MustExec(`create table t(a timestamp default current_timestamp)`) - tk.MustExec(`insert into t set a="20220413154712"`) - tk.MustExec(`alter table t add column b timestamp as (a+1) virtual;`) - // change timezone - tk.MustExec(`set time_zone = '+05:00'`) - tk.MustExec(`insert into t set a="20220413154840"`) - tk.MustExec(`alter table t add index(b);`) - tk.MustExec("admin check table t") - tk.MustExec(`set time_zone = '-03:00'`) - tk.MustExec("admin check table t") -} - -func TestTiDBCurrentTS(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - tk.MustExec("begin") - rows := tk.MustQuery("select @@tidb_current_ts").Rows() - tsStr := rows[0][0].(string) - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, fmt.Sprintf("%d", txn.StartTS()), tsStr) - tk.MustExec("begin") - rows = tk.MustQuery("select @@tidb_current_ts").Rows() - newTsStr := rows[0][0].(string) - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, fmt.Sprintf("%d", txn.StartTS()), newTsStr) - require.NotEqual(t, tsStr, newTsStr) - tk.MustExec("commit") - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - - err = tk.ExecToErr("set @@tidb_current_ts = '1'") - require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err: %v", err)) -} - -func TestTiDBLastTxnInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int primary key)") - tk.MustQuery("select @@tidb_last_txn_info").Check(testkit.Rows("")) - - tk.MustExec("insert into t values (1)") - rows1 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() - require.Greater(t, rows1[0][0].(string), "0") - require.Less(t, rows1[0][0].(string), rows1[0][1].(string)) - - tk.MustExec("begin") - tk.MustQuery("select a from t where a = 1").Check(testkit.Rows("1")) - rows2 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), @@tidb_current_ts").Rows() - tk.MustExec("commit") - rows3 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() - require.Equal(t, rows1[0][0], rows2[0][0]) - require.Equal(t, rows1[0][1], rows2[0][1]) - require.Equal(t, rows1[0][0], rows3[0][0]) - require.Equal(t, rows1[0][1], rows3[0][1]) - require.Less(t, rows2[0][1], rows2[0][2]) - - tk.MustExec("begin") - tk.MustExec("update t set a = a + 1 where a = 1") - rows4 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), @@tidb_current_ts").Rows() - tk.MustExec("commit") - rows5 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() - require.Equal(t, rows1[0][0], rows4[0][0]) - require.Equal(t, rows1[0][1], rows4[0][1]) - require.Equal(t, rows5[0][0], rows4[0][2]) - require.Less(t, rows4[0][1], rows4[0][2]) - require.Less(t, rows4[0][2], rows5[0][1]) - - tk.MustExec("begin") - tk.MustExec("update t set a = a + 1 where a = 2") - tk.MustExec("rollback") - rows6 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() - require.Equal(t, rows5[0][0], rows6[0][0]) - require.Equal(t, rows5[0][1], rows6[0][1]) - - tk.MustExec("begin optimistic") - tk.MustExec("insert into t values (2)") - err := tk.ExecToErr("commit") - require.Error(t, err) - rows7 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), json_extract(@@tidb_last_txn_info, '$.error')").Rows() - require.Greater(t, rows7[0][0], rows5[0][0]) - require.Equal(t, "0", rows7[0][1]) - require.Contains(t, err.Error(), rows7[0][1]) - - err = tk.ExecToErr("set @@tidb_last_txn_info = '{}'") - require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err: %v", err)) -} - -func TestTiDBLastQueryInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int primary key, v int)") - tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.start_ts')").Check(testkit.Rows("0 0")) - - toUint64 := func(str interface{}) uint64 { - res, err := strconv.ParseUint(str.(string), 10, 64) - require.NoError(t, err) - return res - } - - tk.MustExec("select * from t") - rows := tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Equal(t, rows[0][1], rows[0][0]) - - tk.MustExec("insert into t values (1, 10)") - rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Equal(t, rows[0][1], rows[0][0]) - // tidb_last_txn_info is still valid after checking query info. - rows = tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Less(t, rows[0][0].(string), rows[0][1].(string)) - - tk.MustExec("begin pessimistic") - tk.MustExec("select * from t") - rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Equal(t, rows[0][1], rows[0][0]) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("update t set v = 11 where a = 1") - - tk.MustExec("select * from t") - rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Equal(t, rows[0][1], rows[0][0]) - - tk.MustExec("update t set v = 12 where a = 1") - rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Less(t, toUint64(rows[0][0]), toUint64(rows[0][1])) - - tk.MustExec("commit") - - tk.MustExec("set transaction isolation level read committed") - tk.MustExec("begin pessimistic") - tk.MustExec("select * from t") - rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() - require.Greater(t, toUint64(rows[0][0]), uint64(0)) - require.Less(t, toUint64(rows[0][0]), toUint64(rows[0][1])) - - tk.MustExec("rollback") -} - -func TestSelectForUpdate(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - tk.MustExec("drop table if exists t, t1") - - txn, err := tk.Session().Txn(true) - require.True(t, kv.ErrInvalidTxn.Equal(err)) - require.False(t, txn.Valid()) - tk.MustExec("create table t (c1 int, c2 int, c3 int)") - tk.MustExec("insert t values (11, 2, 3)") - tk.MustExec("insert t values (12, 2, 3)") - tk.MustExec("insert t values (13, 2, 3)") - - tk.MustExec("create table t1 (c1 int)") - tk.MustExec("insert t1 values (11)") - - // conflict - tk1.MustExec("begin") - tk1.MustQuery("select * from t where c1=11 for update") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=211 where c1=11") - tk2.MustExec("commit") - - err = tk1.ExecToErr("commit") - require.Error(t, err) - - // no conflict for subquery. - tk1.MustExec("begin") - tk1.MustQuery("select * from t where exists(select null from t1 where t1.c1=t.c1) for update") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=211 where c1=12") - tk2.MustExec("commit") - - tk1.MustExec("commit") - - // not conflict - tk1.MustExec("begin") - tk1.MustQuery("select * from t where c1=11 for update") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=22 where c1=12") - tk2.MustExec("commit") - - tk1.MustExec("commit") - - // not conflict, auto commit - tk1.MustExec("set @@autocommit=1;") - tk1.MustQuery("select * from t where c1=11 for update") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=211 where c1=11") - tk2.MustExec("commit") - - tk1.MustExec("commit") - - // conflict - tk1.MustExec("begin") - tk1.MustQuery("select * from (select * from t for update) t join t1 for update") - - tk2.MustExec("begin") - tk2.MustExec("update t1 set c1 = 13") - tk2.MustExec("commit") - - err = tk1.ExecToErr("commit") - require.Error(t, err) -} - -func TestSelectForUpdateOf(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t (i int)") - tk.MustExec("create table t1 (i int)") - tk.MustExec("insert t values (1)") - tk.MustExec("insert t1 values (1)") - - tk.MustExec("begin pessimistic") - tk.MustQuery("select * from t, t1 where t.i = t1.i for update of t").Check(testkit.Rows("1 1")) - - tk1.MustExec("begin pessimistic") - - // no lock for t - tk1.MustQuery("select * from t1 for update").Check(testkit.Rows("1")) - - // meet lock for t1 - err := tk1.ExecToErr("select * from t for update nowait") - require.True(t, terror.ErrorEqual(err, error2.ErrLockAcquireFailAndNoWaitSet), fmt.Sprintf("err: %v", err)) - - // t1 rolled back, tk1 acquire the lock - tk.MustExec("rollback") - tk1.MustQuery("select * from t for update nowait").Check(testkit.Rows("1")) - - tk1.MustExec("rollback") -} - -func TestPartitionHashCode(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table t(c1 bigint, c2 bigint, c3 bigint, primary key(c1)) partition by hash (c1) partitions 4;`) - var wg util.WaitGroupWrapper - for i := 0; i < 5; i++ { - wg.Run(func() { - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - for i := 0; i < 5; i++ { - tk1.MustExec("select * from t") - } - }) - } - wg.Wait() -} - -// this is from jira issue #5856 -func TestInsertValuesWithSubQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2(a int, b int, c int)") - defer tk.MustExec("drop table if exists t2") - - // should not reference upper scope - require.Error(t, tk.ExecToErr("insert into t2 values (11, 8, (select not b))")) - require.Error(t, tk.ExecToErr("insert into t2 set a = 11, b = 8, c = (select b))")) - - // subquery reference target table is allowed - tk.MustExec("insert into t2 values(1, 1, (select b from t2))") - tk.MustQuery("select * from t2").Check(testkit.Rows("1 1 ")) - tk.MustExec("insert into t2 set a = 1, b = 1, c = (select b+1 from t2)") - tk.MustQuery("select * from t2").Check(testkit.Rows("1 1 ", "1 1 2")) - - // insert using column should work normally - tk.MustExec("delete from t2") - tk.MustExec("insert into t2 values(2, 4, a)") - tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2")) - tk.MustExec("insert into t2 set a = 3, b = 5, c = b") - tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2", "3 5 5")) - - // issue #30626 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - // TODO: should insert success and get (81,1) from the table - tk.MustGetErrMsg( - "insert into t values ( 81, ( select ( SELECT '1' AS `c0` WHERE '1' >= `subq_0`.`c0` ) as `c1` FROM ( SELECT '1' AS `c0` ) AS `subq_0` ) );", - "Insert's SET operation or VALUES_LIST doesn't support complex subqueries now") - tk.MustGetErrMsg( - "insert into t set a = 81, b = (select ( SELECT '1' AS `c0` WHERE '1' >= `subq_0`.`c0` ) as `c1` FROM ( SELECT '1' AS `c0` ) AS `subq_0` );", - "Insert's SET operation or VALUES_LIST doesn't support complex subqueries now") -} - -// fix issue https://github.com/pingcap/tidb/issues/32871 -func TestBitColumnIn(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (id bit(16), key id(id))") - tk.MustExec("insert into t values (65)") - tk.MustQuery("select * from t where id not in (-1,2)").Check(testkit.Rows("\x00A")) - tk.MustGetErrMsg( - "select * from t where id in (-1, -2)", - "[expression:1582]Incorrect parameter count in the call to native function 'in'") -} - -func TestIndexLookupRuntimeStats(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a int, b int, index(a))") - tk.MustExec("insert into t1 values (1,2),(2,3),(3,4)") - rows := tk.MustQuery("explain analyze select * from t1 use index(a) where a > 1").Rows() - require.Len(t, rows, 3) - explain := fmt.Sprintf("%v", rows[0]) - require.Regexp(t, ".*time:.*loops:.*index_task:.*table_task: {total_time.*num.*concurrency.*}.*", explain) - indexExplain := fmt.Sprintf("%v", rows[1]) - tableExplain := fmt.Sprintf("%v", rows[2]) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) -} - -func TestHashAggRuntimeStats(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a int, b int)") - tk.MustExec("insert into t1 values (1,2),(2,3),(3,4)") - rows := tk.MustQuery("explain analyze SELECT /*+ HASH_AGG() */ count(*) FROM t1 WHERE a < 10;").Rows() - require.Len(t, rows, 5) - explain := fmt.Sprintf("%v", rows[0]) - pattern := ".*time:.*loops:.*partial_worker:{wall_time:.*concurrency:.*task_num:.*tot_wait:.*tot_exec:.*tot_time:.*max:.*p95:.*}.*final_worker:{wall_time:.*concurrency:.*task_num:.*tot_wait:.*tot_exec:.*tot_time:.*max:.*p95:.*}.*" - require.Regexp(t, pattern, explain) -} - -func TestIndexMergeRuntimeStats(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_index_merge = 1") - tk.MustExec("create table t1(id int primary key, a int, b int, c int, d int)") - tk.MustExec("create index t1a on t1(a)") - tk.MustExec("create index t1b on t1(b)") - tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") - rows := tk.MustQuery("explain analyze select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4;").Rows() - require.Len(t, rows, 4) - explain := fmt.Sprintf("%v", rows[0]) - pattern := ".*time:.*loops:.*index_task:{fetch_handle:.*, merge:.*}.*table_task:{num.*concurrency.*fetch_row.*wait_time.*}.*" - require.Regexp(t, pattern, explain) - tableRangeExplain := fmt.Sprintf("%v", rows[1]) - indexExplain := fmt.Sprintf("%v", rows[2]) - tableExplain := fmt.Sprintf("%v", rows[3]) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableRangeExplain) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustQuery("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by a").Check(testkit.Rows("1 1 1 1 1", "5 5 5 5 5")) -} - -func TestPrevStmtDesensitization(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec(fmt.Sprintf("set @@session.%v=1", variable.TiDBRedactLog)) - defer tk.MustExec(fmt.Sprintf("set @@session.%v=0", variable.TiDBRedactLog)) - tk.MustExec("create table t (a int, unique key (a))") - tk.MustExec("begin") - tk.MustExec("insert into t values (1),(2)") - require.Equal(t, "insert into `t` values ( ... )", tk.Session().GetSessionVars().PrevStmt.String()) - tk.MustGetErrMsg("insert into t values (1)", `[kv:1062]Duplicate entry '?' for key 't.a'`) -} - -func TestIssue19148(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a decimal(16, 2));") - tk.MustExec("select * from t where a > any_value(a);") - is := domain.GetDomain(tk.Session()).InfoSchema() - tblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - require.Zero(t, tblInfo.Meta().Columns[0].GetFlag()) -} - -func TestOOMActionPriority(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t0") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t3") - tk.MustExec("drop table if exists t4") - tk.MustExec("create table t0(a int)") - tk.MustExec("insert into t0 values(1)") - tk.MustExec("create table t1(a int)") - tk.MustExec("insert into t1 values(1)") - tk.MustExec("create table t2(a int)") - tk.MustExec("insert into t2 values(1)") - tk.MustExec("create table t3(a int)") - tk.MustExec("insert into t3 values(1)") - tk.MustExec("create table t4(a int)") - tk.MustExec("insert into t4 values(1)") - tk.MustQuery("select * from t0 join t1 join t2 join t3 join t4 order by t0.a").Check(testkit.Rows("1 1 1 1 1")) - action := tk.Session().GetSessionVars().StmtCtx.MemTracker.GetFallbackForTest(true) - // All actions are finished and removed. - require.Equal(t, action.GetPriority(), int64(memory.DefLogPriority)) -} - -func TestTrackAggMemoryUsage(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1)") - tk.MustExec("set tidb_track_aggregate_memory_usage = off;") - rows := tk.MustQuery("explain analyze select /*+ HASH_AGG() */ sum(a) from t").Rows() - require.Equal(t, "N/A", rows[0][7]) - rows = tk.MustQuery("explain analyze select /*+ STREAM_AGG() */ sum(a) from t").Rows() - require.Equal(t, "N/A", rows[0][7]) - tk.MustExec("set tidb_track_aggregate_memory_usage = on;") - rows = tk.MustQuery("explain analyze select /*+ HASH_AGG() */ sum(a) from t").Rows() - require.NotEqual(t, "N/A", rows[0][7]) - rows = tk.MustQuery("explain analyze select /*+ STREAM_AGG() */ sum(a) from t").Rows() - require.NotEqual(t, "N/A", rows[0][7]) -} - -func TestProjectionBitType(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(k1 int, v bit(34) DEFAULT b'111010101111001001100111101111111', primary key(k1) clustered);") - tk.MustExec("create table t1(k1 int, v bit(34) DEFAULT b'111010101111001001100111101111111', primary key(k1) nonclustered);") - tk.MustExec("insert into t(k1) select 1;") - tk.MustExec("insert into t1(k1) select 1;") - - tk.MustExec("set @@tidb_enable_vectorized_expression = 0;") - // following SQL should returns same result - tk.MustQuery("(select * from t where false) union(select * from t for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) - tk.MustQuery("(select * from t1 where false) union(select * from t1 for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) - - tk.MustExec("set @@tidb_enable_vectorized_expression = 1;") - tk.MustQuery("(select * from t where false) union(select * from t for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) - tk.MustQuery("(select * from t1 where false) union(select * from t1 for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) -} - -func TestExprBlackListForEnum(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a enum('a','b','c'), b enum('a','b','c'), c int, index idx(b,a));") - tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3);") - - checkFuncPushDown := func(rows [][]interface{}, keyWord string) bool { - for _, line := range rows { - // Agg/Expr push down - if line[2].(string) == "cop[tikv]" && strings.Contains(line[4].(string), keyWord) { - return true - } - // access index - if line[2].(string) == "cop[tikv]" && strings.Contains(line[3].(string), keyWord) { - return true - } - } - return false - } - - // Test agg(enum) push down - tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows := tk.MustQuery("desc format='brief' select /*+ HASH_AGG() */ max(a) from t;").Rows() - require.False(t, checkFuncPushDown(rows, "max")) - rows = tk.MustQuery("desc format='brief' select /*+ STREAM_AGG() */ max(a) from t;").Rows() - require.False(t, checkFuncPushDown(rows, "max")) - - tk.MustExec("delete from mysql.expr_pushdown_blacklist;") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = tk.MustQuery("desc format='brief' select /*+ HASH_AGG() */ max(a) from t;").Rows() - require.True(t, checkFuncPushDown(rows, "max")) - rows = tk.MustQuery("desc format='brief' select /*+ STREAM_AGG() */ max(a) from t;").Rows() - require.True(t, checkFuncPushDown(rows, "max")) - - // Test expr(enum) push down - tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() - require.False(t, checkFuncPushDown(rows, "plus")) - rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() - require.False(t, checkFuncPushDown(rows, "plus")) - - tk.MustExec("delete from mysql.expr_pushdown_blacklist;") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() - require.True(t, checkFuncPushDown(rows, "plus")) - rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() - require.True(t, checkFuncPushDown(rows, "plus")) - - // Test enum index - tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = tk.MustQuery("desc format='brief' select * from t where b = 1;").Rows() - require.False(t, checkFuncPushDown(rows, "index:idx(b)")) - rows = tk.MustQuery("desc format='brief' select * from t where b = 'a';").Rows() - require.False(t, checkFuncPushDown(rows, "index:idx(b)")) - rows = tk.MustQuery("desc format='brief' select * from t where b > 1;").Rows() - require.False(t, checkFuncPushDown(rows, "index:idx(b)")) - rows = tk.MustQuery("desc format='brief' select * from t where b > 'a';").Rows() - require.False(t, checkFuncPushDown(rows, "index:idx(b)")) - - tk.MustExec("delete from mysql.expr_pushdown_blacklist;") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a = 1;").Rows() - require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) - rows = tk.MustQuery("desc format='brief' select * from t where b = 'a' and a = 'a';").Rows() - require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) - rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a > 1;").Rows() - require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) - rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a > 'a'").Rows() - require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) -} - -// Test invoke Close without invoking Open before for each operators. -func TestUnreasonablyClose(t *testing.T) { - store := testkit.CreateMockStore(t) - - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable(), plannercore.MockUnsignedTable()}) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - // To enable the shuffleExec operator. - tk.MustExec("set @@tidb_merge_join_concurrency=4") - - var opsNeedsCovered = []plannercore.PhysicalPlan{ - &plannercore.PhysicalHashJoin{}, - &plannercore.PhysicalMergeJoin{}, - &plannercore.PhysicalIndexJoin{}, - &plannercore.PhysicalIndexHashJoin{}, - &plannercore.PhysicalTableReader{}, - &plannercore.PhysicalIndexReader{}, - &plannercore.PhysicalIndexLookUpReader{}, - &plannercore.PhysicalIndexMergeReader{}, - &plannercore.PhysicalApply{}, - &plannercore.PhysicalHashAgg{}, - &plannercore.PhysicalStreamAgg{}, - &plannercore.PhysicalLimit{}, - &plannercore.PhysicalSort{}, - &plannercore.PhysicalTopN{}, - &plannercore.PhysicalCTE{}, - &plannercore.PhysicalCTETable{}, - &plannercore.PhysicalMaxOneRow{}, - &plannercore.PhysicalProjection{}, - &plannercore.PhysicalSelection{}, - &plannercore.PhysicalTableDual{}, - &plannercore.PhysicalWindow{}, - &plannercore.PhysicalShuffle{}, - &plannercore.PhysicalUnionAll{}, - } - - opsNeedsCoveredMask := uint64(1< t1.a) AS a from t as t1) t", - "select /*+ hash_agg() */ count(f) from t group by a", - "select /*+ stream_agg() */ count(f) from t", - "select * from t order by a, f", - "select * from t order by a, f limit 1", - "select * from t limit 1", - "select (select t1.a from t t1 where t1.a > t2.a) as a from t t2;", - "select a + 1 from t", - "select count(*) a from t having a > 1", - "select * from t where a = 1.1", - "with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 0) select * from cte1", - "select /*+use_index_merge(t, c_d_e, f)*/ * from t where c < 1 or f > 2", - "select sum(f) over (partition by f) from t", - "select /*+ merge_join(t1)*/ * from t t1 join t t2 on t1.d = t2.d", - "select a from t union all select a from t", - } { - comment := fmt.Sprintf("case:%v sql:%s", i, tc) - stmt, err := p.ParseOneStmt(tc, "", "") - require.NoError(t, err, comment) - err = sessiontxn.NewTxn(context.Background(), tk.Session()) - require.NoError(t, err, comment) - - err = sessiontxn.GetTxnManager(tk.Session()).OnStmtStart(context.TODO(), stmt) - require.NoError(t, err, comment) - - executorBuilder := executor.NewMockExecutorBuilderForTest(tk.Session(), is, nil) - - p, _, _ := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NotNil(t, p) - - // This for loop level traverses the plan tree to get which operators are covered. - var hasCTE bool - for child := []plannercore.PhysicalPlan{p.(plannercore.PhysicalPlan)}; len(child) != 0; { - newChild := make([]plannercore.PhysicalPlan, 0, len(child)) - for _, ch := range child { - found := false - for k, t := range opsNeedsCovered { - if reflect.TypeOf(t) == reflect.TypeOf(ch) { - opsAlreadyCoveredMask |= 1 << k - found = true - break - } - } - require.True(t, found, fmt.Sprintf("case: %v sql: %s operator %v is not registered in opsNeedsCoveredMask", i, tc, reflect.TypeOf(ch))) - switch x := ch.(type) { - case *plannercore.PhysicalCTE: - newChild = append(newChild, x.RecurPlan) - newChild = append(newChild, x.SeedPlan) - hasCTE = true - continue - case *plannercore.PhysicalShuffle: - newChild = append(newChild, x.DataSources...) - newChild = append(newChild, x.Tails...) - continue - } - newChild = append(newChild, ch.Children()...) - } - child = newChild - } - - if hasCTE { - // Normally CTEStorages will be setup in ResetContextOfStmt. - // But the following case call e.Close() directly, instead of calling session.ExecStmt(), which calls ResetContextOfStmt. - // So need to setup CTEStorages manually. - tk.Session().GetSessionVars().StmtCtx.CTEStorageMap = map[int]*executor.CTEStorages{} - } - e := executorBuilder.Build(p) - - func() { - defer func() { - r := recover() - buf := make([]byte, 4096) - stackSize := runtime.Stack(buf, false) - buf = buf[:stackSize] - require.Nil(t, r, fmt.Sprintf("case: %v\n sql: %s\n error stack: %v", i, tc, string(buf))) - }() - require.NoError(t, e.Close(), comment) - }() - } - // The following code is used to make sure all the operators registered - // in opsNeedsCoveredMask are covered. - commentBuf := strings.Builder{} - if opsAlreadyCoveredMask != opsNeedsCoveredMask { - for i := range opsNeedsCovered { - if opsAlreadyCoveredMask&(1< t1.a) AS a from t as t1) t;") - require.Contains(t, result.Rows()[1][0], "Apply") - var ( - ind int - flag bool - ) - value := (result.Rows()[1][5]).(string) - for ind = 0; ind < len(value)-5; ind++ { - if value[ind:ind+5] == "cache" { - flag = true - break - } - } - require.True(t, flag) - require.Equal(t, "cache:ON, cacheHitRatio:88.889%", value[ind:]) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9);") - tk.MustExec("analyze table t;") - result = tk.MustQuery("explain analyze SELECT count(1) FROM (SELECT (SELECT min(a) FROM t as t2 WHERE t2.a > t1.a) AS a from t as t1) t;") - require.Contains(t, result.Rows()[1][0], "Apply") - flag = false - value = (result.Rows()[1][5]).(string) - for ind = 0; ind < len(value)-5; ind++ { - if value[ind:ind+5] == "cache" { - flag = true - break - } - } - require.True(t, flag) - require.Equal(t, "cache:OFF", value[ind:]) -} - -func TestCollectDMLRuntimeStats(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int, b int, unique index (a))") - - testSQLs := []string{ - "insert ignore into t1 values (5,5);", - "insert into t1 values (5,5) on duplicate key update a=a+1;", - "replace into t1 values (5,6),(6,7)", - "update t1 set a=a+1 where a=6;", - } - - getRootStats := func() string { - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(plannercore.Plan) - require.True(t, ok) - stats := tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl.GetRootStats(p.ID()) - return stats.String() - } - for _, sql := range testSQLs { - tk.MustExec(sql) - require.Regexp(t, "time.*loops.*Get.*num_rpc.*total_time.*", getRootStats()) - } - - // Test for lock keys stats. - tk.MustExec("begin pessimistic") - tk.MustExec("update t1 set b=b+1") - require.Regexp(t, "time.*lock_keys.*time.* region.* keys.* lock_rpc:.* rpc_count.*", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustQuery("select * from t1 for update").Check(testkit.Rows("5 6", "7 7")) - require.Regexp(t, "time.*lock_keys.*time.* region.* keys.* lock_rpc:.* rpc_count.*", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustExec("insert ignore into t1 values (9,9)") - require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:{BatchGet:{num_rpc:.*, total_time:.*}}}.*", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t1 values (10,10) on duplicate key update a=a+1") - require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:{BatchGet:{num_rpc:.*, total_time:.*}.*", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t1 values (1,2)") - require.Regexp(t, "time:.*, loops:.*, prepare:.*, insert:.*", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustExec("insert ignore into t1 values(11,11) on duplicate key update `a`=`a`+1") - require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:.*}", getRootStats()) - tk.MustExec("rollback") - - tk.MustExec("begin pessimistic") - tk.MustExec("replace into t1 values (1,4)") - require.Regexp(t, "time:.*, loops:.*, prefetch:.*, rpc:.*", getRootStats()) - tk.MustExec("rollback") -} - -func TestIssue24933(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("drop view if exists v;") - tk.MustExec("create table t(a int);") - tk.MustExec("insert into t values(1), (2), (3);") - - tk.MustExec("create definer='root'@'localhost' view v as select count(*) as c1 from t;") - tk.MustQuery("select * from v;").Check(testkit.Rows("3")) - - // Test subquery and outer field is wildcard. - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(*) from t) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select avg(a) from t group by a) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1.0000", "2.0000", "3.0000")) - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select sum(a) from t group by a) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select group_concat(a) from t group by a) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) - - // Test alias names. - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(0) as c1 from t) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(*) as c1 from t) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select group_concat(a) as `concat(a)` from t group by a) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) - - // Test firstrow. - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select a from t group by a) s;") - tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) - - // Test direct select. - tk.MustGetErrMsg("SELECT `s`.`count(a)` FROM (SELECT COUNT(`a`) FROM `test`.`t`) AS `s`", "[planner:1054]Unknown column 's.count(a)' in 'field list'") - - tk.MustExec("drop view v;") - tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(a) from t) s;") - tk.MustQuery("select * from v").Check(testkit.Rows("3")) - - // Test window function. - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(c1 int);") - tk.MustExec("insert into t values(111), (222), (333);") - tk.MustExec("drop view if exists v;") - tk.MustExec("create definer='root'@'localhost' view v as (select * from (select row_number() over (order by c1) from t) s);") - tk.MustQuery("select * from v;").Check(testkit.Rows("1", "2", "3")) - tk.MustExec("drop view if exists v;") - tk.MustExec("create definer='root'@'localhost' view v as (select * from (select c1, row_number() over (order by c1) from t) s);") - tk.MustQuery("select * from v;").Check(testkit.Rows("111 1", "222 2", "333 3")) - - // Test simple expr. - tk.MustExec("drop view if exists v;") - tk.MustExec("create definer='root'@'localhost' view v as (select * from (select c1 or 0 from t) s)") - tk.MustQuery("select * from v;").Check(testkit.Rows("1", "1", "1")) - tk.MustQuery("select `c1 or 0` from v;").Check(testkit.Rows("1", "1", "1")) - - tk.MustExec("drop view v;") -} - -func TestTableSampleTemporaryTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. - safePointName := "tikv_gc_safe_point" - safePointValue := "20160102-15:04:05 -0700" - safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" - updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') - ON DUPLICATE KEY - UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) - tk.MustExec(updateSafePoint) - - tk.MustExec("use test") - tk.MustExec("drop table if exists tmp1") - tk.MustExec("create global temporary table tmp1 " + - "(id int not null primary key, code int not null, value int default null, unique key code(code))" + - "on commit delete rows") - - tk.MustExec("use test") - tk.MustExec("drop table if exists tmp2") - tk.MustExec("create temporary table tmp2 (id int not null primary key, code int not null, value int default null, unique key code(code));") - - // sleep 1us to make test stale - time.Sleep(time.Microsecond) - - // test tablesample return empty for global temporary table - tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) - - tk.MustExec("begin") - tk.MustExec("insert into tmp1 values (1, 1, 1)") - tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) - tk.MustExec("commit") - - // tablesample for global temporary table should not return error for compatibility of tools like dumpling - tk.MustExec("set @@tidb_snapshot=NOW(6)") - tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) - - tk.MustExec("begin") - tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) - tk.MustExec("commit") - tk.MustExec("set @@tidb_snapshot=''") - - // test tablesample returns error for local temporary table - tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") - - tk.MustExec("begin") - tk.MustExec("insert into tmp2 values (1, 1, 1)") - tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") - tk.MustExec("commit") - tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") -} - -func TestCTEWithIndexLookupJoinDeadLock(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int(11) default null,b int(11) default null,key b (b),key ba (b))") - tk.MustExec("create table t1 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") - tk.MustExec("create table t2 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") - // It's easy to reproduce this problem in 30 times execution of IndexLookUpJoin. - for i := 0; i < 30; i++ { - tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") - } -} - -func TestGetResultRowsCount(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - for i := 1; i <= 10; i++ { - tk.MustExec(fmt.Sprintf("insert into t values (%v)", i)) - } - cases := []struct { - sql string - row int64 - }{ - {"select * from t", 10}, - {"select * from t where a < 0", 0}, - {"select * from t where a <= 3", 3}, - {"insert into t values (11)", 0}, - {"replace into t values (12)", 0}, - {"update t set a=13 where a=12", 0}, - } - - for _, ca := range cases { - if strings.HasPrefix(ca.sql, "select") { - tk.MustQuery(ca.sql) - } else { - tk.MustExec(ca.sql) - } - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(plannercore.Plan) - require.True(t, ok) - cnt := executor.GetResultRowsCount(tk.Session().GetSessionVars().StmtCtx, p) - require.Equal(t, ca.row, cnt, fmt.Sprintf("sql: %v", ca.sql)) - } -} - -func TestAdminShowDDLJobs(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database if not exists test_admin_show_ddl_jobs") - tk.MustExec("use test_admin_show_ddl_jobs") - tk.MustExec("create table t (a int);") - - re := tk.MustQuery("admin show ddl jobs 1") - row := re.Rows()[0] - require.Equal(t, "test_admin_show_ddl_jobs", row[1]) - jobID, err := strconv.Atoi(row[0].(string)) - require.NoError(t, err) - - job, err := ddl.GetHistoryJobByID(tk.Session(), int64(jobID)) - require.NoError(t, err) - require.NotNil(t, job) - // Test for compatibility. Old TiDB version doesn't have SchemaName field, and the BinlogInfo maybe nil. - // See PR: 11561. - job.BinlogInfo = nil - job.SchemaName = "" - err = sessiontxn.NewTxnInStmt(context.Background(), tk.Session()) - require.NoError(t, err) - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - err = meta.NewMeta(txn).AddHistoryDDLJob(job, true) - require.NoError(t, err) - tk.Session().StmtCommit(context.Background()) - - re = tk.MustQuery("admin show ddl jobs 1") - row = re.Rows()[0] - require.Equal(t, "test_admin_show_ddl_jobs", row[1]) - - re = tk.MustQuery("admin show ddl jobs 1 where job_type='create table'") - row = re.Rows()[0] - require.Equal(t, "test_admin_show_ddl_jobs", row[1]) - require.Equal(t, "", row[10]) - - // Test the START_TIME and END_TIME field. - tk.MustExec(`set @@time_zone = 'Asia/Shanghai'`) - re = tk.MustQuery("admin show ddl jobs where end_time is not NULL") - row = re.Rows()[0] - createTime, err := types.ParseDatetime(nil, row[8].(string)) - require.NoError(t, err) - startTime, err := types.ParseDatetime(nil, row[9].(string)) - require.NoError(t, err) - endTime, err := types.ParseDatetime(nil, row[10].(string)) - require.NoError(t, err) - tk.MustExec(`set @@time_zone = 'Europe/Amsterdam'`) - re = tk.MustQuery("admin show ddl jobs where end_time is not NULL") - row2 := re.Rows()[0] - require.NotEqual(t, row[8], row2[8]) - require.NotEqual(t, row[9], row2[9]) - require.NotEqual(t, row[10], row2[10]) - createTime2, err := types.ParseDatetime(nil, row2[8].(string)) - require.NoError(t, err) - startTime2, err := types.ParseDatetime(nil, row2[9].(string)) - require.NoError(t, err) - endTime2, err := types.ParseDatetime(nil, row2[10].(string)) - require.NoError(t, err) - loc, err := time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - loc2, err := time.LoadLocation("Europe/Amsterdam") - require.NoError(t, err) - tt, err := createTime.GoTime(loc) - require.NoError(t, err) - t2, err := createTime2.GoTime(loc2) - require.NoError(t, err) - require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) - tt, err = startTime.GoTime(loc) - require.NoError(t, err) - t2, err = startTime2.GoTime(loc2) - require.NoError(t, err) - require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) - tt, err = endTime.GoTime(loc) - require.NoError(t, err) - t2, err = endTime2.GoTime(loc2) - require.NoError(t, err) - require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) -} - -func TestAdminShowDDLJobsRowCount(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // Test for issue: https://github.com/pingcap/tidb/issues/25968 - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (id bigint key,b int);") - tk.MustExec("split table t by (10),(20),(30);") - tk.MustExec("insert into t values (0,0),(10,10),(20,20),(30,30);") - tk.MustExec("alter table t add index idx1(b);") - require.Equal(t, "4", tk.MustQuery("admin show ddl jobs 1").Rows()[0][7]) - - tk.MustExec("insert into t values (1,0),(2,10),(3,20),(4,30);") - tk.MustExec("alter table t add index idx2(b);") - require.Equal(t, "8", tk.MustQuery("admin show ddl jobs 1").Rows()[0][7]) -} - -func TestAdminShowDDLJobsInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // Test for issue: https://github.com/pingcap/tidb/issues/29915 - tk.MustExec("create placement policy x followers=4;") - tk.MustExec("create placement policy y " + - "PRIMARY_REGION=\"cn-east-1\" " + - "REGIONS=\"cn-east-1, cn-east-2\" " + - "FOLLOWERS=2") - tk.MustExec("create database if not exists test_admin_show_ddl_jobs") - tk.MustExec("use test_admin_show_ddl_jobs") - - tk.MustExec("create table t (a int);") - tk.MustExec("create table t1 (a int);") - - tk.MustExec("alter table t placement policy x;") - require.Equal(t, "alter table placement", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) - - tk.MustExec("rename table t to tt, t1 to tt1") - require.Equal(t, "rename tables", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) - - tk.MustExec("create table tt2 (c int) PARTITION BY RANGE (c) " + - "(PARTITION p0 VALUES LESS THAN (6)," + - "PARTITION p1 VALUES LESS THAN (11)," + - "PARTITION p2 VALUES LESS THAN (16)," + - "PARTITION p3 VALUES LESS THAN (21));") - tk.MustExec("alter table tt2 partition p0 placement policy y") - require.Equal(t, "alter table partition placement", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) - - tk.MustExec("alter table tt1 cache") - require.Equal(t, "alter table cache", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) - tk.MustExec("alter table tt1 nocache") - require.Equal(t, "alter table nocache", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) -} - -func TestAdminChecksumOfPartitionedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - tk.MustExec("DROP TABLE IF EXISTS admin_checksum_partition_test;") - tk.MustExec("CREATE TABLE admin_checksum_partition_test (a INT) PARTITION BY HASH(a) PARTITIONS 4;") - tk.MustExec("INSERT INTO admin_checksum_partition_test VALUES (1), (2);") - - r := tk.MustQuery("ADMIN CHECKSUM TABLE admin_checksum_partition_test;") - r.Check(testkit.Rows("test admin_checksum_partition_test 1 5 5")) -} - -func TestUnion2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - testSQL := `drop table if exists union_test; create table union_test(id int);` - tk.MustExec(testSQL) - - testSQL = `drop table if exists union_test;` - tk.MustExec(testSQL) - testSQL = `create table union_test(id int);` - tk.MustExec(testSQL) - testSQL = `insert union_test values (1),(2)` - tk.MustExec(testSQL) - - testSQL = `select * from (select id from union_test union select id from union_test) t order by id;` - r := tk.MustQuery(testSQL) - r.Check(testkit.Rows("1", "2")) - - r = tk.MustQuery("select 1 union all select 1") - r.Check(testkit.Rows("1", "1")) - - r = tk.MustQuery("select 1 union all select 1 union select 1") - r.Check(testkit.Rows("1")) - - r = tk.MustQuery("select 1 as a union (select 2) order by a limit 1") - r.Check(testkit.Rows("1")) - - r = tk.MustQuery("select 1 as a union (select 2) order by a limit 1, 1") - r.Check(testkit.Rows("2")) - - r = tk.MustQuery("select id from union_test union all (select 1) order by id desc") - r.Check(testkit.Rows("2", "1", "1")) - - r = tk.MustQuery("select id as a from union_test union (select 1) order by a desc") - r.Check(testkit.Rows("2", "1")) - - r = tk.MustQuery(`select null as a union (select "abc") order by a`) - r.Check(testkit.Rows("", "abc")) - - r = tk.MustQuery(`select "abc" as a union (select 1) order by a`) - r.Check(testkit.Rows("1", "abc")) - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (c int, d int)") - tk.MustExec("insert t1 values (NULL, 1)") - tk.MustExec("insert t1 values (1, 1)") - tk.MustExec("insert t1 values (1, 2)") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2 (c int, d int)") - tk.MustExec("insert t2 values (1, 3)") - tk.MustExec("insert t2 values (1, 1)") - tk.MustExec("drop table if exists t3") - tk.MustExec("create table t3 (c int, d int)") - tk.MustExec("insert t3 values (3, 2)") - tk.MustExec("insert t3 values (4, 3)") - r = tk.MustQuery(`select sum(c1), c2 from (select c c1, d c2 from t1 union all select d c1, c c2 from t2 union all select c c1, d c2 from t3) x group by c2 order by c2`) - r.Check(testkit.Rows("5 1", "4 2", "4 3")) - - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1 (a int primary key)") - tk.MustExec("create table t2 (a int primary key)") - tk.MustExec("create table t3 (a int primary key)") - tk.MustExec("insert t1 values (7), (8)") - tk.MustExec("insert t2 values (1), (9)") - tk.MustExec("insert t3 values (2), (3)") - r = tk.MustQuery("select * from t1 union all select * from t2 union all (select * from t3) order by a limit 2") - r.Check(testkit.Rows("1", "2")) - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (a int)") - tk.MustExec("create table t2 (a int)") - tk.MustExec("insert t1 values (2), (1)") - tk.MustExec("insert t2 values (3), (4)") - r = tk.MustQuery("select * from t1 union all (select * from t2) order by a limit 1") - r.Check(testkit.Rows("1")) - r = tk.MustQuery("select (select * from t1 where a != t.a union all (select * from t2 where a != t.a) order by a limit 1) from t1 t") - r.Check(testkit.Rows("1", "2")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int unsigned primary key auto_increment, c1 int, c2 int, index c1_c2 (c1, c2))") - tk.MustExec("insert into t (c1, c2) values (1, 1)") - tk.MustExec("insert into t (c1, c2) values (1, 2)") - tk.MustExec("insert into t (c1, c2) values (2, 3)") - r = tk.MustQuery("select * from (select * from t where t.c1 = 1 union select * from t where t.id = 1) s order by s.id") - r.Check(testkit.Rows("1 1 1", "2 1 2")) - - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (f1 DATE)") - tk.MustExec("INSERT INTO t VALUES ('1978-11-26')") - r = tk.MustQuery("SELECT f1+0 FROM t UNION SELECT f1+0 FROM t") - r.Check(testkit.Rows("19781126")) - - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (a int, b int)") - tk.MustExec("INSERT INTO t VALUES ('1', '1')") - r = tk.MustQuery("select b from (SELECT * FROM t UNION ALL SELECT a, b FROM t order by a) t") - r.Check(testkit.Rows("1", "1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (a DECIMAL(4,2))") - tk.MustExec("INSERT INTO t VALUE(12.34)") - r = tk.MustQuery("SELECT 1 AS c UNION select a FROM t") - r.Sort().Check(testkit.Rows("1.00", "12.34")) - - // #issue3771 - r = tk.MustQuery("SELECT 'a' UNION SELECT CONCAT('a', -4)") - r.Sort().Check(testkit.Rows("a", "a-4")) - - // test race - tk.MustQuery("SELECT @x:=0 UNION ALL SELECT @x:=0 UNION ALL SELECT @x") - - // test field tp - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("CREATE TABLE t1 (a date)") - tk.MustExec("CREATE TABLE t2 (a date)") - tk.MustExec("SELECT a from t1 UNION select a FROM t2") - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + " `a` date DEFAULT NULL\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // Move from session test. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (c double);") - tk.MustExec("create table t2 (c double);") - tk.MustExec("insert into t1 value (73);") - tk.MustExec("insert into t2 value (930);") - // If set unspecified column flen to 0, it will cause bug in union. - // This test is used to prevent the bug reappear. - tk.MustQuery("select c from t1 union (select c from t2) order by c").Check(testkit.Rows("73", "930")) - - // issue 5703 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a date)") - tk.MustExec("insert into t value ('2017-01-01'), ('2017-01-02')") - r = tk.MustQuery("(select a from t where a < 0) union (select a from t where a > 0) order by a") - r.Check(testkit.Rows("2017-01-01", "2017-01-02")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t value(0),(0)") - tk.MustQuery("select 1 from (select a from t union all select a from t) tmp").Check(testkit.Rows("1", "1", "1", "1")) - tk.MustQuery("select 10 as a from dual union select a from t order by a desc limit 1 ").Check(testkit.Rows("10")) - tk.MustQuery("select -10 as a from dual union select a from t order by a limit 1 ").Check(testkit.Rows("-10")) - tk.MustQuery("select count(1) from (select a from t union all select a from t) tmp").Check(testkit.Rows("4")) - - err := tk.ExecToErr("select 1 from (select a from t limit 1 union all select a from t limit 1) tmp") - require.Error(t, err) - terr := errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(mysql.ErrWrongUsage), terr.Code()) - - err = tk.ExecToErr("select 1 from (select a from t order by a union all select a from t limit 1) tmp") - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(mysql.ErrWrongUsage), terr.Code()) - - tk.MustGetDBError("(select a from t order by a) union all select a from t limit 1 union all select a from t limit 1", plannercore.ErrWrongUsage) - - tk.MustExec("(select a from t limit 1) union all select a from t limit 1") - tk.MustExec("(select a from t order by a) union all select a from t order by a") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t value(1),(2),(3)") - - tk.MustQuery("(select a from t order by a limit 2) union all (select a from t order by a desc limit 2) order by a desc limit 1,2").Check(testkit.Rows("2", "2")) - tk.MustQuery("select a from t union all select a from t order by a desc limit 5").Check(testkit.Rows("3", "3", "2", "2", "1")) - tk.MustQuery("(select a from t order by a desc limit 2) union all select a from t group by a order by a").Check(testkit.Rows("1", "2", "2", "3", "3")) - tk.MustQuery("(select a from t order by a desc limit 2) union all select 33 as a order by a desc limit 2").Check(testkit.Rows("33", "3")) - - tk.MustQuery("select 1 union select 1 union all select 1").Check(testkit.Rows("1", "1")) - tk.MustQuery("select 1 union all select 1 union select 1").Check(testkit.Rows("1")) - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec(`create table t1(a bigint, b bigint);`) - tk.MustExec(`create table t2(a bigint, b bigint);`) - tk.MustExec(`insert into t1 values(1, 1);`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t1 select * from t1;`) - tk.MustExec(`insert into t2 values(1, 1);`) - tk.MustExec(`set @@tidb_init_chunk_size=2;`) - tk.MustExec(`set @@sql_mode="";`) - tk.MustQuery(`select count(*) from (select t1.a, t1.b from t1 left join t2 on t1.a=t2.a union all select t1.a, t1.a from t1 left join t2 on t1.a=t2.a) tmp;`).Check(testkit.Rows("128")) - tk.MustQuery(`select tmp.a, count(*) from (select t1.a, t1.b from t1 left join t2 on t1.a=t2.a union all select t1.a, t1.a from t1 left join t2 on t1.a=t2.a) tmp;`).Check(testkit.Rows("1 128")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t value(1 ,2)") - tk.MustQuery("select a, b from (select a, 0 as d, b from t union all select a, 0 as d, b from t) test;").Check(testkit.Rows("1 2", "1 2")) - - // #issue 8141 - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("insert into t1 value(1,2),(1,1),(2,2),(2,2),(3,2),(3,2)") - tk.MustExec("set @@tidb_init_chunk_size=2;") - tk.MustQuery("select count(*) from (select a as c, a as d from t1 union all select a, b from t1) t;").Check(testkit.Rows("12")) - - // #issue 8189 and #issue 8199 - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("CREATE TABLE t1 (a int not null, b char (10) not null)") - tk.MustExec("insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") - tk.MustExec("CREATE TABLE t2 (a int not null, b char (10) not null)") - tk.MustExec("insert into t2 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") - tk.MustQuery("select a from t1 union select a from t1 order by (select a+1);").Check(testkit.Rows("1", "2", "3")) - - // #issue 8201 - for i := 0; i < 4; i++ { - tk.MustQuery("SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev").Check(testkit.Rows("0")) - } - - // #issue 8231 - tk.MustExec("drop table if exists t1") - tk.MustExec("CREATE TABLE t1 (uid int(1))") - tk.MustExec("INSERT INTO t1 SELECT 150") - tk.MustQuery("SELECT 'a' UNION SELECT uid FROM t1 order by 1 desc;").Check(testkit.Rows("a", "150")) - - // #issue 8196 - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("CREATE TABLE t1 (a int not null, b char (10) not null)") - tk.MustExec("insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") - tk.MustExec("CREATE TABLE t2 (a int not null, b char (10) not null)") - tk.MustExec("insert into t2 values(3,'c'),(4,'d'),(5,'f'),(6,'e')") - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustGetErrMsg("(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by t1.b", - "[planner:1250]Table 't1' from one of the SELECTs cannot be used in global ORDER clause") - - // #issue 9900 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b decimal(6, 3))") - tk.MustExec("insert into t values(1, 1.000)") - tk.MustQuery("select count(distinct a), sum(distinct a), avg(distinct a) from (select a from t union all select b from t) tmp;").Check(testkit.Rows("1 1.000 1.0000000")) - - // #issue 23832 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a bit(20), b float, c double, d int)") - tk.MustExec("insert into t values(10, 10, 10, 10), (1, -1, 2, -2), (2, -2, 1, 1), (2, 1.1, 2.1, 10.1)") - tk.MustQuery("select a from t union select 10 order by a").Check(testkit.Rows("1", "2", "10")) -} - -func TestUnionLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists union_limit") - tk.MustExec("create table union_limit (id int) partition by hash(id) partitions 30") - for i := 0; i < 60; i++ { - tk.MustExec(fmt.Sprintf("insert into union_limit values (%d)", i)) - } - // Cover the code for worker count limit in the union executor. - tk.MustQuery("select * from union_limit limit 10") -} - -func TestLowResolutionTSORead(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@autocommit=1") - tk.MustExec("use test") - tk.MustExec("create table low_resolution_tso(a int)") - tk.MustExec("insert low_resolution_tso values (1)") - - // enable low resolution tso - require.False(t, tk.Session().GetSessionVars().LowResolutionTSO) - tk.MustExec("set @@tidb_low_resolution_tso = 'on'") - require.True(t, tk.Session().GetSessionVars().LowResolutionTSO) - - time.Sleep(3 * time.Second) - tk.MustQuery("select * from low_resolution_tso").Check(testkit.Rows("1")) - err := tk.ExecToErr("update low_resolution_tso set a = 2") - require.Error(t, err) - tk.MustExec("set @@tidb_low_resolution_tso = 'off'") - tk.MustExec("update low_resolution_tso set a = 2") - tk.MustQuery("select * from low_resolution_tso").Check(testkit.Rows("2")) -} - -func TestStaleReadAtFutureTime(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - // Setting tx_read_ts to a time in the future will fail. (One day before the 2038 problem) - tk.MustGetErrMsg("set @@tx_read_ts = '2038-01-18 03:14:07'", "cannot set read timestamp to a future time") - // TxnReadTS Is not updated if check failed. - require.Zero(t, tk.Session().GetSessionVars().TxnReadTS.PeakTxnReadTS()) -} - -func TestSQLMode(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a tinyint not null)") - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - tk.ExecToErr("insert t values ()") - tk.ExecToErr("insert t values ('1000')") - - tk.MustExec("create table if not exists tdouble (a double(3,2))") - tk.ExecToErr("insert tdouble values (10.23)") - - tk.MustExec("set sql_mode = ''") - tk.MustExec("insert t values ()") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value")) - tk.MustExec("insert t values (null)") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) - tk.MustExec("insert ignore t values (null)") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) - tk.MustExec("insert t select null") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) - tk.MustExec("insert t values (1000)") - tk.MustQuery("select * from t order by a").Check(testkit.Rows("0", "0", "0", "0", "127")) - - tk.MustExec("insert tdouble values (10.23)") - tk.MustQuery("select * from tdouble").Check(testkit.Rows("9.99")) - - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - tk.MustExec("set @@global.sql_mode = ''") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("drop table if exists t2") - tk2.MustExec("create table t2 (a varchar(3))") - tk2.MustExec("insert t2 values ('abcd')") - tk2.MustQuery("select * from t2").Check(testkit.Rows("abc")) - - // session1 is still in strict mode. - tk.ExecToErr("insert t2 values ('abcd')") - // Restore original global strict mode. - tk.MustExec("set @@global.sql_mode = 'STRICT_TRANS_TABLES'") -} - -func TestTableScan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use information_schema") - result := tk.MustQuery("select * from schemata") - // There must be these tables: information_schema, mysql, performance_schema and test. - require.GreaterOrEqual(t, len(result.Rows()), 4) - tk.MustExec("use test") - tk.MustExec("create database mytest") - rowStr1 := fmt.Sprintf("%s %s %s %s %v %v", "def", "mysql", "utf8mb4", "utf8mb4_bin", nil, nil) - rowStr2 := fmt.Sprintf("%s %s %s %s %v %v", "def", "mytest", "utf8mb4", "utf8mb4_bin", nil, nil) - tk.MustExec("use information_schema") - result = tk.MustQuery("select * from schemata where schema_name = 'mysql'") - result.Check(testkit.Rows(rowStr1)) - result = tk.MustQuery("select * from schemata where schema_name like 'my%'") - result.Check(testkit.Rows(rowStr1, rowStr2)) - result = tk.MustQuery("select 1 from tables limit 1") - result.Check(testkit.Rows("1")) -} - -func TestAdapterStatement(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.Session().GetSessionVars().TxnCtx.InfoSchema = domain.GetDomain(tk.Session()).InfoSchema() - compiler := &executor.Compiler{Ctx: tk.Session()} - s := parser.New() - stmtNode, err := s.ParseOneStmt("select 1", "", "") - require.NoError(t, err) - stmt, err := compiler.Compile(context.TODO(), stmtNode) - require.NoError(t, err) - require.Equal(t, "select 1", stmt.OriginText()) - - stmtNode, err = s.ParseOneStmt("create table test.t (a int)", "", "") - require.NoError(t, err) - stmt, err = compiler.Compile(context.TODO(), stmtNode) - require.NoError(t, err) - require.Equal(t, "create table test.t (a int)", stmt.OriginText()) -} - -func TestIsPointGet(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql") - ctx := tk.Session().(sessionctx.Context) - tests := map[string]bool{ - "select * from help_topic where name='aaa'": false, - "select 1 from help_topic where name='aaa'": false, - "select * from help_topic where help_topic_id=1": true, - "select * from help_topic where help_category_id=1": false, - } - s := parser.New() - for sqlStr, result := range tests { - stmtNode, err := s.ParseOneStmt(sqlStr, "", "") - require.NoError(t, err) - preprocessorReturn := &plannercore.PreprocessorReturn{} - err = plannercore.Preprocess(context.Background(), ctx, stmtNode, plannercore.WithPreprocessorReturn(preprocessorReturn)) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), ctx, stmtNode, preprocessorReturn.InfoSchema) - require.NoError(t, err) - ret, err := plannercore.IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, p) - require.NoError(t, err) - require.Equal(t, result, ret) - } -} - -func TestClusteredIndexIsPointGet(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("drop database if exists test_cluster_index_is_point_get;") - tk.MustExec("create database test_cluster_index_is_point_get;") - tk.MustExec("use test_cluster_index_is_point_get;") - - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a varchar(255), b int, c char(10), primary key (c, a));") - ctx := tk.Session().(sessionctx.Context) - - tests := map[string]bool{ - "select 1 from t where a='x'": false, - "select * from t where c='x'": false, - "select * from t where a='x' and c='x'": true, - "select * from t where a='x' and c='x' and b=1": false, - } - s := parser.New() - for sqlStr, result := range tests { - stmtNode, err := s.ParseOneStmt(sqlStr, "", "") - require.NoError(t, err) - preprocessorReturn := &plannercore.PreprocessorReturn{} - err = plannercore.Preprocess(context.Background(), ctx, stmtNode, plannercore.WithPreprocessorReturn(preprocessorReturn)) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), ctx, stmtNode, preprocessorReturn.InfoSchema) - require.NoError(t, err) - ret, err := plannercore.IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, p) - require.NoError(t, err) - require.Equal(t, result, ret) - } -} - -func TestColumnName(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c int, d int)") - // disable only full group by - tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") - rs, err := tk.Exec("select 1 + c, count(*) from t") - require.NoError(t, err) - fields := rs.Fields() - require.Len(t, fields, 2) - require.Equal(t, "1 + c", fields[0].Column.Name.L) - require.Equal(t, "1 + c", fields[0].ColumnAsName.L) - require.Equal(t, "count(*)", fields[1].Column.Name.L) - require.Equal(t, "count(*)", fields[1].ColumnAsName.L) - require.NoError(t, rs.Close()) - rs, err = tk.Exec("select (c) > all (select c from t) from t") - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 1) - require.Equal(t, "(c) > all (select c from t)", fields[0].Column.Name.L) - require.Equal(t, "(c) > all (select c from t)", fields[0].ColumnAsName.L) - require.NoError(t, rs.Close()) - tk.MustExec("begin") - tk.MustExec("insert t values(1,1)") - rs, err = tk.Exec("select c d, d c from t") - require.NoError(t, err) - fields = rs.Fields() - require.Len(t, fields, 2) - require.Equal(t, "c", fields[0].Column.Name.L) - require.Equal(t, "d", fields[0].ColumnAsName.L) - require.Equal(t, "d", fields[1].Column.Name.L) - require.Equal(t, "c", fields[1].ColumnAsName.L) - require.NoError(t, rs.Close()) - // Test case for query a column of a table. - // In this case, all attributes have values. - rs, err = tk.Exec("select c as a from t as t2") - require.NoError(t, err) - fields = rs.Fields() - require.Equal(t, "c", fields[0].Column.Name.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - require.Equal(t, "t", fields[0].Table.Name.L) - require.Equal(t, "t2", fields[0].TableAsName.L) - require.Equal(t, "test", fields[0].DBName.L) - require.Nil(t, rs.Close()) - // Test case for query a expression which only using constant inputs. - // In this case, the table, org_table and database attributes will all be empty. - rs, err = tk.Exec("select hour(1) as a from t as t2") - require.NoError(t, err) - fields = rs.Fields() - require.Equal(t, "a", fields[0].Column.Name.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - require.Equal(t, "", fields[0].Table.Name.L) - require.Equal(t, "", fields[0].TableAsName.L) - require.Equal(t, "", fields[0].DBName.L) - require.Nil(t, rs.Close()) - // Test case for query a column wrapped with parentheses and unary plus. - // In this case, the column name should be its original name. - rs, err = tk.Exec("select (c), (+c), +(c), +(+(c)), ++c from t") - require.NoError(t, err) - fields = rs.Fields() - for i := 0; i < 5; i++ { - require.Equal(t, "c", fields[i].Column.Name.L) - require.Equal(t, "c", fields[i].ColumnAsName.L) - } - require.Nil(t, rs.Close()) - - // Test issue https://github.com/pingcap/tidb/issues/9639 . - // Both window function and expression appear in final result field. - tk.MustExec("set @@tidb_enable_window_function = 1") - rs, err = tk.Exec("select 1+1, row_number() over() num from t") - require.NoError(t, err) - fields = rs.Fields() - require.Equal(t, "1+1", fields[0].Column.Name.L) - require.Equal(t, "1+1", fields[0].ColumnAsName.L) - require.Equal(t, "num", fields[1].Column.Name.L) - require.Equal(t, "num", fields[1].ColumnAsName.L) - tk.MustExec("set @@tidb_enable_window_function = 0") - require.Nil(t, rs.Close()) - - rs, err = tk.Exec("select if(1,c,c) from t;") - require.NoError(t, err) - fields = rs.Fields() - require.Equal(t, "if(1,c,c)", fields[0].Column.Name.L) - // It's a compatibility issue. Should be empty instead. - require.Equal(t, "if(1,c,c)", fields[0].ColumnAsName.L) - require.Nil(t, rs.Close()) -} - -func TestSelectVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (d int)") - tk.MustExec("insert into t values(1), (2), (1)") - // This behavior is different from MySQL. - result := tk.MustQuery("select @a, @a := d+1 from t") - result.Check(testkit.Rows(" 2", "2 3", "3 2")) - // Test for PR #10658. - tk.MustExec("select SQL_BIG_RESULT d from t group by d") - tk.MustExec("select SQL_SMALL_RESULT d from t group by d") - tk.MustExec("select SQL_BUFFER_RESULT d from t group by d") -} - -func TestHistoryRead(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists history_read") - tk.MustExec("create table history_read (a int)") - tk.MustExec("insert history_read values (1)") - - // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. - safePointName := "tikv_gc_safe_point" - safePointValue := "20060102-15:04:05 -0700" - safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" - updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') - ON DUPLICATE KEY - UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) - tk.MustExec(updateSafePoint) - - // Set snapshot to a time before save point will fail. - _, err := tk.Exec("set @@tidb_snapshot = '2006-01-01 15:04:05.999999'") - require.True(t, terror.ErrorEqual(err, variable.ErrSnapshotTooOld), "err %v", err) - // SnapshotTS Is not updated if check failed. - require.Equal(t, uint64(0), tk.Session().GetSessionVars().SnapshotTS) - - // Setting snapshot to a time in the future will fail. (One day before the 2038 problem) - _, err = tk.Exec("set @@tidb_snapshot = '2038-01-18 03:14:07'") - require.Regexp(t, "cannot set read timestamp to a future time", err) - // SnapshotTS Is not updated if check failed. - require.Equal(t, uint64(0), tk.Session().GetSessionVars().SnapshotTS) - - curVer1, _ := store.CurrentVersion(kv.GlobalTxnScope) - time.Sleep(time.Millisecond) - snapshotTime := time.Now() - time.Sleep(time.Millisecond) - curVer2, _ := store.CurrentVersion(kv.GlobalTxnScope) - tk.MustExec("insert history_read values (2)") - tk.MustQuery("select * from history_read").Check(testkit.Rows("1", "2")) - tk.MustExec("set @@tidb_snapshot = '" + snapshotTime.Format("2006-01-02 15:04:05.999999") + "'") - ctx := tk.Session().(sessionctx.Context) - snapshotTS := ctx.GetSessionVars().SnapshotTS - require.Greater(t, snapshotTS, curVer1.Ver) - require.Less(t, snapshotTS, curVer2.Ver) - tk.MustQuery("select * from history_read").Check(testkit.Rows("1")) - tk.MustExecToErr("insert history_read values (2)") - tk.MustExecToErr("update history_read set a = 3 where a = 1") - tk.MustExecToErr("delete from history_read where a = 1") - tk.MustExec("set @@tidb_snapshot = ''") - tk.MustQuery("select * from history_read").Check(testkit.Rows("1", "2")) - tk.MustExec("insert history_read values (3)") - tk.MustExec("update history_read set a = 4 where a = 3") - tk.MustExec("delete from history_read where a = 1") - - time.Sleep(time.Millisecond) - snapshotTime = time.Now() - time.Sleep(time.Millisecond) - tk.MustExec("alter table history_read add column b int") - tk.MustExec("insert history_read values (8, 8), (9, 9)") - tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2 ", "4 ", "8 8", "9 9")) - tk.MustExec("set @@tidb_snapshot = '" + snapshotTime.Format("2006-01-02 15:04:05.999999") + "'") - tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2", "4")) - tsoStr := strconv.FormatUint(oracle.GoTimeToTS(snapshotTime), 10) - - tk.MustExec("set @@tidb_snapshot = '" + tsoStr + "'") - tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2", "4")) - - tk.MustExec("set @@tidb_snapshot = ''") - tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2 ", "4 ", "8 8", "9 9")) -} - -func TestHistoryReadInTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. - safePointName := "tikv_gc_safe_point" - safePointValue := "20060102-15:04:05 -0700" - safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" - updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') - ON DUPLICATE KEY - UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) - tk.MustExec(updateSafePoint) - - tk.MustExec("drop table if exists his_t0, his_t1") - tk.MustExec("create table his_t0(id int primary key, v int)") - tk.MustExec("insert into his_t0 values(1, 10)") - - time.Sleep(time.Millisecond) - tk.MustExec("set @a=now(6)") - time.Sleep(time.Millisecond) - tk.MustExec("create table his_t1(id int primary key, v int)") - tk.MustExec("update his_t0 set v=v+1") - time.Sleep(time.Millisecond) - tk.MustExec("set tidb_snapshot=now(6)") - ts2 := tk.Session().GetSessionVars().SnapshotTS - tk.MustExec("set tidb_snapshot=''") - time.Sleep(time.Millisecond) - tk.MustExec("update his_t0 set v=v+1") - tk.MustExec("insert into his_t1 values(10, 100)") - - init := func(isolation string, setSnapshotBeforeTxn bool) { - if isolation == "none" { - tk.MustExec("set @@tidb_snapshot=@a") - return - } - - if setSnapshotBeforeTxn { - tk.MustExec("set @@tidb_snapshot=@a") - } - - if isolation == "optimistic" { - tk.MustExec("begin optimistic") - } else { - tk.MustExec(fmt.Sprintf("set @@tx_isolation='%s'", isolation)) - tk.MustExec("begin pessimistic") - } - - if !setSnapshotBeforeTxn { - tk.MustExec("set @@tidb_snapshot=@a") - } - } - - for _, isolation := range []string{ - "none", // not start an explicit txn - "optimistic", - "REPEATABLE-READ", - "READ-COMMITTED", - } { - for _, setSnapshotBeforeTxn := range []bool{false, true} { - t.Run(fmt.Sprintf("[%s] setSnapshotBeforeTxn[%v]", isolation, setSnapshotBeforeTxn), func(t *testing.T) { - tk.MustExec("rollback") - tk.MustExec("set @@tidb_snapshot=''") - - init(isolation, setSnapshotBeforeTxn) - // When tidb_snapshot is set, should use the snapshot info schema - tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0")) - - // When tidb_snapshot is set, select should use select ts - tk.MustQuery("select * from his_t0").Check(testkit.Rows("1 10")) - tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 10")) - - // When tidb_snapshot is set, write statements should not be allowed - if isolation != "none" && isolation != "optimistic" { - notAllowedSQLs := []string{ - "insert into his_t0 values(5, 1)", - "delete from his_t0 where id=1", - "update his_t0 set v=v+1", - "select * from his_t0 for update", - "select * from his_t0 where id=1 for update", - "create table his_t2(id int)", - } - - for _, sql := range notAllowedSQLs { - err := tk.ExecToErr(sql) - require.Errorf(t, err, "can not execute write statement when 'tidb_snapshot' is set") - } - } - - // After `ExecRestrictedSQL` with a specified snapshot and use current session, the original snapshot ts should not be reset - // See issue: https://github.com/pingcap/tidb/issues/34529 - exec := tk.Session().(sqlexec.RestrictedSQLExecutor) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionWithSnapshot(ts2), sqlexec.ExecOptionUseCurSession}, "select * from his_t0 where id=1") - require.NoError(t, err) - require.Equal(t, 1, len(rows)) - require.Equal(t, int64(1), rows[0].GetInt64(0)) - require.Equal(t, int64(11), rows[0].GetInt64(1)) - tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 10")) - tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0")) - - // CLEAR - tk.MustExec("set @@tidb_snapshot=''") - - // When tidb_snapshot is not set, should use the transaction's info schema - tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0", "his_t1")) - - // When tidb_snapshot is not set, select should use the transaction's ts - tk.MustQuery("select * from his_t0").Check(testkit.Rows("1 12")) - tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 12")) - tk.MustQuery("select * from his_t1").Check(testkit.Rows("10 100")) - tk.MustQuery("select * from his_t1 where id=10").Check(testkit.Rows("10 100")) - - // When tidb_snapshot is not set, select ... for update should not be effected - tk.MustQuery("select * from his_t0 for update").Check(testkit.Rows("1 12")) - tk.MustQuery("select * from his_t0 where id=1 for update").Check(testkit.Rows("1 12")) - tk.MustQuery("select * from his_t1 for update").Check(testkit.Rows("10 100")) - tk.MustQuery("select * from his_t1 where id=10 for update").Check(testkit.Rows("10 100")) - - tk.MustExec("rollback") - }) - } - } -} - -func TestCurrentTimestampValueSelection(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t,t1") - - tk.MustExec("create table t (id int, t0 timestamp null default current_timestamp, t1 timestamp(1) null default current_timestamp(1), t2 timestamp(2) null default current_timestamp(2) on update current_timestamp(2))") - tk.MustExec("insert into t (id) values (1)") - rs := tk.MustQuery("select t0, t1, t2 from t where id = 1") - t0 := rs.Rows()[0][0].(string) - t1 := rs.Rows()[0][1].(string) - t2 := rs.Rows()[0][2].(string) - require.Equal(t, 1, len(strings.Split(t0, "."))) - require.Equal(t, 1, len(strings.Split(t1, ".")[1])) - require.Equal(t, 2, len(strings.Split(t2, ".")[1])) - tk.MustQuery("select id from t where t0 = ?", t0).Check(testkit.Rows("1")) - tk.MustQuery("select id from t where t1 = ?", t1).Check(testkit.Rows("1")) - tk.MustQuery("select id from t where t2 = ?", t2).Check(testkit.Rows("1")) - time.Sleep(time.Second) - tk.MustExec("update t set t0 = now() where id = 1") - rs = tk.MustQuery("select t2 from t where id = 1") - newT2 := rs.Rows()[0][0].(string) - require.True(t, newT2 != t2) - - tk.MustExec("create table t1 (id int, a timestamp, b timestamp(2), c timestamp(3))") - tk.MustExec("insert into t1 (id, a, b, c) values (1, current_timestamp(2), current_timestamp, current_timestamp(3))") - rs = tk.MustQuery("select a, b, c from t1 where id = 1") - a := rs.Rows()[0][0].(string) - b := rs.Rows()[0][1].(string) - d := rs.Rows()[0][2].(string) - require.Equal(t, 1, len(strings.Split(a, "."))) - require.Equal(t, "00", strings.Split(b, ".")[1]) - require.Equal(t, 3, len(strings.Split(d, ".")[1])) -} - -func TestAddDateBuiltinWithWarnings(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@sql_mode='NO_ZERO_DATE'") - result := tk.MustQuery(`select date_add('2001-01-00', interval -2 hour);`) - result.Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '2001-01-00'")) -} - -func TestStrToDateBuiltinWithWarnings(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@sql_mode='NO_ZERO_DATE'") - tk.MustExec("use test") - tk.MustQuery(`SELECT STR_TO_DATE('0000-1-01', '%Y-%m-%d');`).Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1411 Incorrect datetime value: '0000-1-01' for function str_to_date")) - tk.MustQuery("SELECT CAST('4#,8?Q' AS DATE);").Check(testkit.Rows("")) - tk.MustQuery(`show warnings;`).Check(testkit.Rows( - `Warning 8034 Incorrect datetime value: '4#,8?Q'`, - )) - tk.MustExec("CREATE TABLE t1 (c1 INT, c2 TEXT);") - tk.MustExec("INSERT INTO t1 VALUES (1833458842, '0.3503490908550797');") - tk.MustQuery(`SELECT CAST(t1.c2 AS DATE) FROM t1`).Check(testkit.Rows("")) - tk.MustQuery(`show warnings;`).Check(testkit.Rows( - `Warning 1292 Incorrect datetime value: '0.3503490908550797'`, - )) -} - -func TestAdmin(t *testing.T) { - var cluster testutils.Cluster - store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithSingleStore(c) - cluster = c - })) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk.MustExec("drop table if exists admin_test") - tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("insert admin_test (c1) values (1),(2),(NULL)") - - ctx := context.Background() - // cancel DDL jobs test - r, err := tk.Exec("admin cancel ddl jobs 1") - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - row := req.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "1", row.GetString(0)) - require.Regexp(t, ".*DDL Job:1 not found", row.GetString(1)) - - // show ddl test; - r, err = tk.Exec("admin show ddl") - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - row = req.GetRow(0) - require.Equal(t, 6, row.Len()) - tk = testkit.NewTestKit(t, store) - tk.MustExec("begin") - sess := tk.Session() - ddlInfo, err := ddl.GetDDLInfo(sess) - require.NoError(t, err) - require.Equal(t, ddlInfo.SchemaVer, row.GetInt64(0)) - // TODO: Pass this test. - // rowOwnerInfos := strings.Split(row.Data[1].GetString(), ",") - // ownerInfos := strings.Split(ddlInfo.Owner.String(), ",") - // c.Assert(rowOwnerInfos[0], Equals, ownerInfos[0]) - serverInfo, err := infosync.GetServerInfoByID(ctx, row.GetString(1)) - require.NoError(t, err) - require.Equal(t, serverInfo.IP+":"+strconv.FormatUint(uint64(serverInfo.Port), 10), row.GetString(2)) - require.Equal(t, "", row.GetString(3)) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Zero(t, req.NumRows()) - tk.MustExec("rollback") - - // show DDL jobs test - r, err = tk.Exec("admin show ddl jobs") - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - row = req.GetRow(0) - require.Equal(t, 12, row.Len()) - txn, err := store.Begin() - require.NoError(t, err) - historyJobs, err := ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) - require.Greater(t, len(historyJobs), 1) - require.Greater(t, len(row.GetString(1)), 0) - require.NoError(t, err) - require.Equal(t, historyJobs[0].ID, row.GetInt64(0)) - require.NoError(t, err) - - r, err = tk.Exec("admin show ddl jobs 20") - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - row = req.GetRow(0) - require.Equal(t, 12, row.Len()) - require.Equal(t, historyJobs[0].ID, row.GetInt64(0)) - require.NoError(t, err) - - // show DDL job queries test - tk.MustExec("use test") - tk.MustExec("drop table if exists admin_test2") - tk.MustExec("create table admin_test2 (c1 int, c2 int, c3 int default 1, index (c1))") - result := tk.MustQuery(`admin show ddl job queries 1, 1, 1`) - result.Check(testkit.Rows()) - result = tk.MustQuery(`admin show ddl job queries 1, 2, 3, 4`) - result.Check(testkit.Rows()) - historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) - result = tk.MustQuery(fmt.Sprintf("admin show ddl job queries %d", historyJobs[0].ID)) - result.Check(testkit.Rows(historyJobs[0].Query)) - require.NoError(t, err) - - // show DDL job queries with range test - tk.MustExec("use test") - tk.MustExec("drop table if exists admin_test2") - tk.MustExec("create table admin_test2 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test3") - tk.MustExec("create table admin_test3 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test4") - tk.MustExec("create table admin_test4 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test5") - tk.MustExec("create table admin_test5 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test6") - tk.MustExec("create table admin_test6 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test7") - tk.MustExec("create table admin_test7 (c1 int, c2 int, c3 int default 1, index (c1))") - tk.MustExec("drop table if exists admin_test8") - tk.MustExec("create table admin_test8 (c1 int, c2 int, c3 int default 1, index (c1))") - historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) - result = tk.MustQuery(`admin show ddl job queries limit 3`) - result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[0].ID, historyJobs[0].Query), fmt.Sprintf("%d %s", historyJobs[1].ID, historyJobs[1].Query), fmt.Sprintf("%d %s", historyJobs[2].ID, historyJobs[2].Query))) - result = tk.MustQuery(`admin show ddl job queries limit 3, 2`) - result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[3].ID, historyJobs[3].Query), fmt.Sprintf("%d %s", historyJobs[4].ID, historyJobs[4].Query))) - result = tk.MustQuery(`admin show ddl job queries limit 3 offset 2`) - result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[2].ID, historyJobs[2].Query), fmt.Sprintf("%d %s", historyJobs[3].ID, historyJobs[3].Query), fmt.Sprintf("%d %s", historyJobs[4].ID, historyJobs[4].Query))) - require.NoError(t, err) - - // check situations when `admin show ddl job 20` happens at the same time with new DDLs being executed - var wg sync.WaitGroup - wg.Add(2) - flag := true - go func() { - defer wg.Done() - for i := 0; i < 10; i++ { - tk.MustExec("drop table if exists admin_test9") - tk.MustExec("create table admin_test9 (c1 int, c2 int, c3 int default 1, index (c1))") - } - }() - go func() { - // check that the result set has no duplication - defer wg.Done() - for i := 0; i < 10; i++ { - result := tk2.MustQuery(`admin show ddl job queries 20`) - rows := result.Rows() - rowIDs := make(map[string]struct{}) - for _, row := range rows { - rowID := fmt.Sprintf("%v", row[0]) - if _, ok := rowIDs[rowID]; ok { - flag = false - return - } - rowIDs[rowID] = struct{}{} - } - } - }() - wg.Wait() - require.True(t, flag) - - // check situations when `admin show ddl job queries limit 3 offset 2` happens at the same time with new DDLs being executed - var wg2 sync.WaitGroup - wg2.Add(2) - flag = true - go func() { - defer wg2.Done() - for i := 0; i < 10; i++ { - tk.MustExec("drop table if exists admin_test9") - tk.MustExec("create table admin_test9 (c1 int, c2 int, c3 int default 1, index (c1))") - } - }() - go func() { - // check that the result set has no duplication - defer wg2.Done() - for i := 0; i < 10; i++ { - result := tk2.MustQuery(`admin show ddl job queries limit 3 offset 2`) - rows := result.Rows() - rowIDs := make(map[string]struct{}) - for _, row := range rows { - rowID := fmt.Sprintf("%v", row[0]) - if _, ok := rowIDs[rowID]; ok { - flag = false - return - } - rowIDs[rowID] = struct{}{} - } - } - }() - wg2.Wait() - require.True(t, flag) - - // check table test - tk.MustExec("create table admin_test1 (c1 int, c2 int default 1, index (c1))") - tk.MustExec("insert admin_test1 (c1) values (21),(22)") - r, err = tk.Exec("admin check table admin_test, admin_test1") - require.NoError(t, err) - require.Nil(t, r) - // error table name - require.Error(t, tk.ExecToErr("admin check table admin_test_error")) - // different index values - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - require.NotNil(t, is) - tb, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("admin_test")) - require.NoError(t, err) - require.Len(t, tb.Indices(), 1) - _, err = tb.Indices()[0].Create(mock.NewContext(), txn, types.MakeDatums(int64(10)), kv.IntHandle(1), nil) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - errAdmin := tk.ExecToErr("admin check table admin_test") - require.Error(t, errAdmin) - - if config.CheckTableBeforeDrop { - tk.MustGetErrMsg("drop table admin_test", errAdmin.Error()) - - // Drop inconsistency index. - tk.MustExec("alter table admin_test drop index c1") - tk.MustExec("admin check table admin_test") - } - // checksum table test - tk.MustExec("create table checksum_with_index (id int, count int, PRIMARY KEY(id), KEY(count))") - tk.MustExec("create table checksum_without_index (id int, count int, PRIMARY KEY(id))") - r, err = tk.Exec("admin checksum table checksum_with_index, checksum_without_index") - require.NoError(t, err) - res := tk.ResultSetToResult(r, "admin checksum table") - // Mocktikv returns 1 for every table/index scan, then we will xor the checksums of a table. - // For "checksum_with_index", we have two checksums, so the result will be 1^1 = 0. - // For "checksum_without_index", we only have one checksum, so the result will be 1. - res.Sort().Check(testkit.Rows("test checksum_with_index 0 2 2", "test checksum_without_index 1 1 1")) - - tk.MustExec("drop table if exists t1;") - tk.MustExec("CREATE TABLE t1 (c2 BOOL, PRIMARY KEY (c2));") - tk.MustExec("INSERT INTO t1 SET c2 = '0';") - tk.MustExec("ALTER TABLE t1 ADD COLUMN c3 DATETIME NULL DEFAULT '2668-02-03 17:19:31';") - tk.MustExec("ALTER TABLE t1 ADD INDEX idx2 (c3);") - tk.MustExec("ALTER TABLE t1 ADD COLUMN c4 bit(10) default 127;") - tk.MustExec("ALTER TABLE t1 ADD INDEX idx3 (c4);") - tk.MustExec("admin check table t1;") - - // Test admin show ddl jobs table name after table has been droped. - tk.MustExec("drop table if exists t1;") - re := tk.MustQuery("admin show ddl jobs 1") - rows := re.Rows() - require.Len(t, rows, 1) - require.Equal(t, "t1", rows[0][2]) - - // Test for reverse scan get history ddl jobs when ddl history jobs queue has multiple regions. - txn, err = store.Begin() - require.NoError(t, err) - historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), 20) - require.NoError(t, err) - - // Split region for history ddl job queues. - m := meta.NewMeta(txn) - startKey := meta.DDLJobHistoryKey(m, 0) - endKey := meta.DDLJobHistoryKey(m, historyJobs[0].ID) - cluster.SplitKeys(startKey, endKey, int(historyJobs[0].ID/5)) - - historyJobs2, err := ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), 20) - require.NoError(t, err) - require.Equal(t, historyJobs2, historyJobs) -} - -func TestForSelectScopeInUnion(t *testing.T) { - store := testkit.CreateMockStore(t) - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - // A union B for update, the "for update" option belongs to union statement, so - // it should works on both A and B. - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("drop table if exists t") - tk1.MustExec("create table t(a int)") - tk1.MustExec("insert into t values (1)") - - tk1.MustExec("begin") - // 'For update' would act on the second select. - tk1.MustQuery("select 1 as a union select a from t for update") - - tk2.MustExec("use test") - tk2.MustExec("update t set a = a + 1") - - // As tk1 use select 'for update', it should detect conflict and fail. - _, err := tk1.Exec("commit") - require.Error(t, err) - - tk1.MustExec("begin") - tk1.MustQuery("select 1 as a union select a from t limit 5 for update") - tk1.MustQuery("select 1 as a union select a from t order by a for update") - - tk2.MustExec("update t set a = a + 1") - - _, err = tk1.Exec("commit") - require.Error(t, err) -} - -func TestUnsignedDecimalOverflow(t *testing.T) { - store := testkit.CreateMockStore(t) - - tests := []struct { - input interface{} - hasErr bool - err string - }{{ - -1, - true, - "Out of range value for column", - }, { - "-1.1e-1", - true, - "Out of range value for column", - }, { - -1.1, - true, - "Out of range value for column", - }, { - -0, - false, - "", - }, - } - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a decimal(10,2) unsigned)") - for _, test := range tests { - err := tk.ExecToErr("insert into t values (?)", test.input) - if test.hasErr { - require.Error(t, err) - require.Contains(t, err.Error(), test.err) - } else { - require.NoError(t, err) - } - } - - tk.MustExec("set sql_mode=''") - tk.MustExec("delete from t") - tk.MustExec("insert into t values (?)", -1) - tk.MustQuery("select a from t limit 1").Check(testkit.Rows("0.00")) -} - -func TestMaxOneRow(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists t1`) - tk.MustExec(`drop table if exists t2`) - tk.MustExec(`create table t1(a double, b double);`) - tk.MustExec(`create table t2(a double, b double);`) - tk.MustExec(`insert into t1 values(1, 1), (2, 2), (3, 3);`) - tk.MustExec(`insert into t2 values(0, 0);`) - tk.MustExec(`set @@tidb_init_chunk_size=1;`) - rs, err := tk.Exec(`select (select t1.a from t1 where t1.a > t2.a) as a from t2;`) - require.NoError(t, err) - - err = rs.Next(context.TODO(), rs.NewChunk(nil)) - require.Error(t, err) - require.Equal(t, "[executor:1242]Subquery returns more than 1 row", err.Error()) - require.NoError(t, rs.Close()) -} - -func TestDoSubquery(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a int)`) - tk.MustExec(`do 1 in (select * from t)`) - tk.MustExec(`insert into t values(1)`) - r, err := tk.Exec(`do 1 in (select * from t)`) - require.NoError(t, err) - require.Nil(t, r) -} - -func TestSummaryFailedUpdate(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int as(-a))") - tk.MustExec("insert into t(a) values(1), (3), (7)") - sm := &testkit.MockSessionManager{ - PS: make([]*util.ProcessInfo, 0), - } - tk.Session().SetSessionManager(sm) - dom.ExpensiveQueryHandle().SetSessionManager(sm) - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") - tk.MustQuery("select variable_value from mysql.GLOBAL_VARIABLES where variable_name = 'tidb_mem_oom_action'").Check(testkit.Rows("LOG")) - - tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("set @@tidb_mem_quota_query=1") - tk.MustMatchErrMsg("update t set t.a = t.a - 1 where t.a in (select a from t where a < 4)", memory.PanicMemoryExceedWarnMsg) - tk.MustExec("set @@tidb_mem_quota_query=1000000000") - tk.MustQuery("select stmt_type from information_schema.statements_summary where digest_text = 'update `t` set `t` . `a` = `t` . `a` - ? where `t` . `a` in ( select `a` from `t` where `a` < ? )'").Check(testkit.Rows("Update")) -} - -func TestIsFastPlan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id int primary key, a int)") - - cases := []struct { - sql string - isFastPlan bool - }{ - {"select a from t where id=1", true}, - {"select a+id from t where id=1", true}, - {"select 1", true}, - {"select @@autocommit", true}, - {"set @@autocommit=1", true}, - {"set @a=1", true}, - {"select * from t where a=1", false}, - {"select * from t", false}, - } - - for _, ca := range cases { - if strings.HasPrefix(ca.sql, "select") { - tk.MustQuery(ca.sql) - } else { - tk.MustExec(ca.sql) - } - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(plannercore.Plan) - require.True(t, ok) - ok = executor.IsFastPlan(p) - require.Equal(t, ca.isFastPlan, ok) - } -} - -func TestCountDistinctJSON(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(j JSON)") - tk.MustExec("insert into t values('2010')") - tk.MustExec("insert into t values('2011')") - tk.MustExec("insert into t values('2012')") - tk.MustExec("insert into t values('2010.000')") - tk.MustExec("insert into t values(cast(? as JSON))", uint64(math.MaxUint64)) - tk.MustExec("insert into t values(cast(? as JSON))", float64(math.MaxUint64)) - - tk.MustQuery("select count(distinct j) from t").Check(testkit.Rows("5")) -} - -func TestHashJoinJSON(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int(11), j JSON, d DOUBLE)") - tk.MustExec("insert into t values(0, '2010', 2010)") - tk.MustExec("insert into t values(1, '2011', 2011)") - tk.MustExec("insert into t values(2, '2012', 2012)") - tk.MustExec("insert into t values(3, cast(? as JSON), ?)", uint64(math.MaxUint64), float64(math.MaxUint64)) - - tk.MustQuery("select /*+inl_hash_join(t2)*/ t1.id, t2.id from t t1 join t t2 on t1.j = t2.d;").Check(testkit.Rows("0 0", "1 1", "2 2")) -} - -func TestTableLockPrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tk.MustExec("create user 'testuser'@'localhost'") - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "testuser", Hostname: "localhost"}, nil, nil, nil)) - tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test'") - tk.MustExec("GRANT LOCK TABLES ON test.* to 'testuser'@'localhost'") - tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1142]SELECT command denied to user 'testuser'@'localhost' for table 't'") - tk.MustExec("REVOKE ALL ON test.* FROM 'testuser'@'localhost'") - tk.MustExec("GRANT SELECT ON test.* to 'testuser'@'localhost'") - tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test'") - tk.MustExec("GRANT LOCK TABLES ON test.* to 'testuser'@'localhost'") - tk2.MustExec("LOCK TABLE test.t WRITE") - - tk.MustExec("create database test2") - tk.MustExec("create table test2.t2(a int)") - tk2.MustGetErrMsg("LOCK TABLE test.t WRITE, test2.t2 WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test2'") - tk.MustExec("GRANT LOCK TABLES ON test2.* to 'testuser'@'localhost'") - tk2.MustGetErrMsg("LOCK TABLE test.t WRITE, test2.t2 WRITE", "[planner:1142]SELECT command denied to user 'testuser'@'localhost' for table 't2'") - tk.MustExec("GRANT SELECT ON test2.* to 'testuser'@'localhost'") - tk2.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") - tk.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") -} - -func TestGlobalMemoryControl2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk0 := testkit.NewTestKit(t, store) - tk0.MustExec("set global tidb_mem_oom_action = 'cancel'") - tk0.MustExec("set global tidb_server_memory_limit = 1 << 30") - tk0.MustExec("set global tidb_server_memory_limit_sess_min_size = 128") - - sm := &testkit.MockSessionManager{ - PS: []*util.ProcessInfo{tk0.Session().ShowProcess()}, - } - dom.ServerMemoryLimitHandle().SetSessionManager(sm) - go dom.ServerMemoryLimitHandle().Run() - - tk0.MustExec("use test") - tk0.MustExec("create table t(a int)") - tk0.MustExec("insert into t select 1") - for i := 1; i <= 8; i++ { - tk0.MustExec("insert into t select * from t") // 256 Lines - } - - var test []int - wg := sync.WaitGroup{} - wg.Add(1) - go func() { - time.Sleep(100 * time.Millisecond) // Make sure the sql is running. - test = make([]int, 128<<20) // Keep 1GB HeapInuse - wg.Done() - }() - sql := "select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a;" // Need 500MB - require.True(t, strings.Contains(tk0.QueryToErr(sql).Error(), memory.PanicMemoryExceedWarnMsg)) - require.Equal(t, tk0.Session().GetSessionVars().DiskTracker.MaxConsumed(), int64(0)) - wg.Wait() - test[0] = 0 - runtime.GC() -} - -func TestCompileOutOfMemoryQuota(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // Test for issue: https://github.com/pingcap/tidb/issues/38322 - defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") - tk.MustExec("set global tidb_mem_oom_action='CANCEL'") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create table t1(a int, c int, index idx(a))") - tk.MustExec("set tidb_mem_quota_query=10") - err := tk.ExecToErr("select t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a") - require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg) -} - -func TestSignalCheckpointForSort(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/SignalCheckpointForSort", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/SignalCheckpointForSort")) - }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/chunk/SignalCheckpointForSort", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/chunk/SignalCheckpointForSort")) - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") - tk.MustExec("set global tidb_mem_oom_action='CANCEL'") - tk.MustExec("set tidb_mem_quota_query = 100000000") - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - for i := 0; i < 20; i++ { - tk.MustExec(fmt.Sprintf("insert into t values(%d)", i)) - } - tk.Session().GetSessionVars().ConnectionID = 123456 - - err := tk.QueryToErr("select * from t order by a") - require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg) -} - -func TestSessionRootTrackerDetach(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") - tk.MustExec("set global tidb_mem_oom_action='CANCEL'") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("create table t1(a int, c int, index idx(a))") - tk.MustExec("set tidb_mem_quota_query=10") - tk.MustContainErrMsg("select /*+hash_join(t1)*/ t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a", memory.PanicMemoryExceedWarnMsg) - tk.MustExec("set tidb_mem_quota_query=1000") - rs, err := tk.Exec("select /*+hash_join(t1)*/ t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a") - require.NoError(t, err) - require.NotNil(t, tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false)) - err = rs.Close() - require.NoError(t, err) - require.Nil(t, tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false)) -} - -func TestPlanReplayerDumpTPCDS(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table catalog_sales -( - cs_sold_date_sk int , - cs_sold_time_sk int , - cs_ship_date_sk int , - cs_bill_customer_sk int , - cs_bill_cdemo_sk int , - cs_bill_hdemo_sk int , - cs_bill_addr_sk int , - cs_ship_customer_sk int , - cs_ship_cdemo_sk int , - cs_ship_hdemo_sk int , - cs_ship_addr_sk int , - cs_call_center_sk int , - cs_catalog_page_sk int , - cs_ship_mode_sk int , - cs_warehouse_sk int , - cs_item_sk int not null, - cs_promo_sk int , - cs_order_number int not null, - cs_quantity int , - cs_wholesale_cost decimal(7,2) , - cs_list_price decimal(7,2) , - cs_sales_price decimal(7,2) , - cs_ext_discount_amt decimal(7,2) , - cs_ext_sales_price decimal(7,2) , - cs_ext_wholesale_cost decimal(7,2) , - cs_ext_list_price decimal(7,2) , - cs_ext_tax decimal(7,2) , - cs_coupon_amt decimal(7,2) , - cs_ext_ship_cost decimal(7,2) , - cs_net_paid decimal(7,2) , - cs_net_paid_inc_tax decimal(7,2) , - cs_net_paid_inc_ship decimal(7,2) , - cs_net_paid_inc_ship_tax decimal(7,2) , - cs_net_profit decimal(7,2) , - primary key (cs_item_sk, cs_order_number) -);`) - tk.MustExec(`create table store_sales -( - ss_sold_date_sk int , - ss_sold_time_sk int , - ss_item_sk int not null, - ss_customer_sk int , - ss_cdemo_sk int , - ss_hdemo_sk int , - ss_addr_sk int , - ss_store_sk int , - ss_promo_sk int , - ss_ticket_number int not null, - ss_quantity int , - ss_wholesale_cost decimal(7,2) , - ss_list_price decimal(7,2) , - ss_sales_price decimal(7,2) , - ss_ext_discount_amt decimal(7,2) , - ss_ext_sales_price decimal(7,2) , - ss_ext_wholesale_cost decimal(7,2) , - ss_ext_list_price decimal(7,2) , - ss_ext_tax decimal(7,2) , - ss_coupon_amt decimal(7,2) , - ss_net_paid decimal(7,2) , - ss_net_paid_inc_tax decimal(7,2) , - ss_net_profit decimal(7,2) , - primary key (ss_item_sk, ss_ticket_number) -);`) - tk.MustExec(`create table date_dim -( - d_date_sk int not null, - d_date_id char(16) not null, - d_date date , - d_month_seq int , - d_week_seq int , - d_quarter_seq int , - d_year int , - d_dow int , - d_moy int , - d_dom int , - d_qoy int , - d_fy_year int , - d_fy_quarter_seq int , - d_fy_week_seq int , - d_day_name char(9) , - d_quarter_name char(6) , - d_holiday char(1) , - d_weekend char(1) , - d_following_holiday char(1) , - d_first_dom int , - d_last_dom int , - d_same_day_ly int , - d_same_day_lq int , - d_current_day char(1) , - d_current_week char(1) , - d_current_month char(1) , - d_current_quarter char(1) , - d_current_year char(1) , - primary key (d_date_sk) -);`) - tk.MustQuery(`plan replayer dump explain with ssci as ( -select ss_customer_sk customer_sk - ,ss_item_sk item_sk -from store_sales,date_dim -where ss_sold_date_sk = d_date_sk - and d_month_seq between 1212 and 1212 + 11 -group by ss_customer_sk - ,ss_item_sk), -csci as( - select cs_bill_customer_sk customer_sk - ,cs_item_sk item_sk -from catalog_sales,date_dim -where cs_sold_date_sk = d_date_sk - and d_month_seq between 1212 and 1212 + 11 -group by cs_bill_customer_sk - ,cs_item_sk) - select sum(case when ssci.customer_sk is not null and csci.customer_sk is null then 1 else 0 end) store_only - ,sum(case when ssci.customer_sk is null and csci.customer_sk is not null then 1 else 0 end) catalog_only - ,sum(case when ssci.customer_sk is not null and csci.customer_sk is not null then 1 else 0 end) store_and_catalog -from ssci left join csci on (ssci.customer_sk=csci.customer_sk - and ssci.item_sk = csci.item_sk) -UNION - select sum(case when ssci.customer_sk is not null and csci.customer_sk is null then 1 else 0 end) store_only - ,sum(case when ssci.customer_sk is null and csci.customer_sk is not null then 1 else 0 end) catalog_only - ,sum(case when ssci.customer_sk is not null and csci.customer_sk is not null then 1 else 0 end) store_and_catalog -from ssci right join csci on (ssci.customer_sk=csci.customer_sk - and ssci.item_sk = csci.item_sk) -limit 100;`) -} - -func TestProcessInfoOfSubQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (i int, j int);") - var wg sync.WaitGroup - wg.Add(1) - go func() { - tk.MustQuery("select 1, (select sleep(count(1) + 2) from t);") - wg.Done() - }() - time.Sleep(time.Second) - tk2.MustQuery("select 1 from information_schema.processlist where TxnStart != '' and info like 'select%sleep% from t%'").Check(testkit.Rows("1")) - wg.Wait() -} diff --git a/executor/test/executor/main_test.go b/executor/test/executor/main_test.go deleted file mode 100644 index f1314d947a514..0000000000000 --- a/executor/test/executor/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.Cleanup(func(_ int) { - view.Stop() - }), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/fktest/BUILD.bazel b/executor/test/fktest/BUILD.bazel deleted file mode 100644 index 6af0ab32d2c65..0000000000000 --- a/executor/test/fktest/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "fktest_test", - timeout = "short", - srcs = [ - "foreign_key_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 26, - deps = [ - "//config", - "//executor", - "//infoschema", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//testkit", - "//tests/realtikvtest", - "//types", - "//util/dbterror/exeerrors", - "//util/memory", - "//util/sqlexec", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/fktest/foreign_key_test.go b/executor/test/fktest/foreign_key_test.go deleted file mode 100644 index 852a6d895ecc7..0000000000000 --- a/executor/test/fktest/foreign_key_test.go +++ /dev/null @@ -1,2539 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package fk_test - -import ( - "bytes" - "context" - "fmt" - "strconv" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" -) - -var foreignKeyTestCase1 = []struct { - prepareSQLs []string - notNull bool -}{ - // Case-1: test unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, a int, b int, unique index(id), unique index(a, b));", - "create table t2 (b int, name varchar(10), a int, id int, unique index(id), unique index (a,b), foreign key fk(a, b) references t1(a, b));", - }, - }, - // Case-2: test unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key, a int, b int, unique index(id), unique index(a, b, id));", - "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b));", - }, - }, - // Case-3: test non-unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, unique index(id), index(a, b));", - "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b));", - }, - }, - // Case-4: test non-unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, unique index(id), index(a, b, id));", - "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b));", - }, - }, - //Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, unique index(id), primary key (a, b));", - "create table t2 (b int, name varchar(10), a int, id int, unique index(id), primary key (a, b), foreign key fk(a, b) references t1(a, b));", - }, - notNull: true, - }, - // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, unique index(id), primary key (a, b));", - "create table t2 (b int, a int, name varchar(10), id int, unique index(id), primary key (a, b), foreign key fk(a, b) references t1(a, b));", - }, - notNull: true, - }, - // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, unique index(id), primary key (a, b, id));", - "create table t2 (b int, a int, id int, name varchar(10), unique index(id), primary key (a, b, id), foreign key fk(a, b) references t1(a, b));", - }, - notNull: true, - }, - // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, unique index(id), primary key (a, b, id));", - "create table t2 (name varchar(10), b int, a int, id int, unique index(id), primary key (a, b, id), foreign key fk(a, b) references t1(a, b));", - }, - notNull: true, - }, -} - -func TestForeignKeyOnInsertChildTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - tk.MustExec("create table t_data (id int, a int, b int)") - tk.MustExec("insert into t_data (id, a, b) values (1, 1, 1), (2, 2, 2);") - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1);") - tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1)") - if !ca.notNull { - tk.MustExec("insert into t2 (id, a, b) values (2, null, 1)") - tk.MustExec("insert into t2 (id, a, b) values (3, 1, null)") - tk.MustExec("insert into t2 (id, a, b) values (4, null, null)") - } - tk.MustGetDBError("insert into t2 (id, a, b) values (5, 1, 0);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 (id, a, b) values (6, 0, 1);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 (id, a, b) values (7, 2, 2);", plannercore.ErrNoReferencedRow2) - // Test insert from select. - tk.MustExec("delete from t2") - tk.MustExec("insert into t2 (id, a, b) select id, a, b from t_data where t_data.id=1") - tk.MustGetDBError("insert into t2 (id, a, b) select id, a, b from t_data where t_data.id=2", plannercore.ErrNoReferencedRow2) - - // Test in txn - tk.MustExec("delete from t2") - tk.MustExec("begin") - tk.MustExec("delete from t1 where a=1") - tk.MustGetDBError("insert into t2 (id, a, b) values (1, 1, 1)", plannercore.ErrNoReferencedRow2) - tk.MustExec("insert into t1 (id, a, b) values (2, 2, 2)") - tk.MustExec("insert into t2 (id, a, b) values (2, 2, 2)") - tk.MustExec("rollback") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) - } - - // Case-10: test primary key is handle and contain foreign key column, and foreign key column has default value. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1 (id int,a int, primary key(id));") - tk.MustExec("create table t2 (id int key,a int not null default 0, index (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 values (1, 1);") - tk.MustExec("insert into t2 values (1, 1);") - tk.MustGetDBError("insert into t2 (id) values (10);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("insert into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) - - // Case-11: test primary key is handle and contain foreign key column, and foreign key column doesn't have default value. - tk.MustExec("drop table if exists t2;") - tk.MustExec("create table t2 (id int key,a int, index (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t2 values (1, 1);") - tk.MustExec("insert into t2 (id) values (10);") - tk.MustGetDBError("insert into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) -} - -func TestForeignKeyOnInsertDuplicateUpdateChildTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") - - sqls := []string{ - "insert into t2 (id, a, b, name) values (1, 12, 22, 'b') on duplicate key update a = 100", - "insert into t2 (id, a, b, name) values (1, 13, 23, 'c') on duplicate key update a = a+10", - "insert into t2 (id, a, b, name) values (1, 14, 24, 'd') on duplicate key update a = a + 100", - "insert into t2 (id, a, b, name) values (1, 14, 24, 'd') on duplicate key update a = 12, b = 23", - } - for _, sqlStr := range sqls { - tk.MustGetDBError(sqlStr, plannercore.ErrNoReferencedRow2) - } - tk.MustExec("insert into t2 (id, a, b, name) values (1, 14, 26, 'b') on duplicate key update a = 12, b = 22, name = 'x'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 12 22 x")) - if !ca.notNull { - tk.MustExec("insert into t2 (id, a, b, name) values (1, 14, 26, 'b') on duplicate key update a = null, b = 22, name = 'y'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 22 y")) - tk.MustExec("insert into t2 (id, a, b, name) values (1, 15, 26, 'b') on duplicate key update b = null, name = 'z'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 z")) - } - tk.MustExec("insert into t2 (id, a, b, name) values (1, 15, 26, 'b') on duplicate key update a=13,b=23, name = 'c'") - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 13 23 c")) - - // Test In txn. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (2, 11, 21, 'a')") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (2, 14, 26, 'b') on duplicate key update a = 12, b = 22, name = 'x'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 12 22 x")) - tk.MustExec("rollback") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 11 21 a")) - - tk.MustExec("begin") - tk.MustExec("delete from t1 where id=3") - tk.MustGetDBError("insert into t2 (id, a, b, name) values (2, 13, 23, 'y') on duplicate key update a = 13, b = 23, name = 'y'", plannercore.ErrNoReferencedRow2) - tk.MustExec("insert into t2 (id, a, b, name) values (2, 14, 24, 'z') on duplicate key update a = 14, b = 24, name = 'z'") - tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") - tk.MustExec("insert into t2 (id, a, b, name) values (2, 15, 25, 'o') on duplicate key update a = 15, b = 25, name = 'o'") - tk.MustExec("delete from t1 where id=1") - tk.MustGetDBError("insert into t2 (id, a, b, name) values (2, 11, 21, 'y') on duplicate key update a = 11, b = 21, name = 'p'", plannercore.ErrNoReferencedRow2) - tk.MustExec("commit") - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("2 15 25 o")) - } - - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") - tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") - - tk.MustExec("insert into t2 (id, a) values (11, 1) on duplicate key update a = 2, name = 'b'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 2 21 b")) - tk.MustExec("insert into t2 (id, a, b) values (11, 2, 22) on duplicate key update a = 3, name = 'c'") - tk.MustExec("insert into t2 (id, a, name) values (11, 3, 'b') on duplicate key update b = b+10, name = 'd'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 3 31 d")) - tk.MustExec("insert into t2 (id, a, name) values (11, 3, 'b') on duplicate key update id = 1, name = 'f'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 31 f")) - tk.MustGetDBError("insert into t2 (id, a, name) values (1, 3, 'b') on duplicate key update a = 10", plannercore.ErrNoReferencedRow2) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 31 f")) - - // Test In txn. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 21, 'a')") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a) values (11, 1) on duplicate key update a = 2, name = 'b'") - tk.MustExec("rollback") - - tk.MustExec("begin") - tk.MustExec("delete from t1 where id=2") - tk.MustGetDBError("insert into t2 (id, a) values (1, 1) on duplicate key update a = 2, name = 'b'", plannercore.ErrNoReferencedRow2) - tk.MustExec("insert into t2 (id, a) values (1, 1) on duplicate key update a = 3, name = 'c'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 21 c")) - tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") - tk.MustExec("insert into t2 (id, a) values (3, 3) on duplicate key update a = 5, name = 'd'") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 5 21 d")) - tk.MustExec("delete from t1 where id=1") - tk.MustGetDBError("insert into t2 (id, a) values (1, 5) on duplicate key update a = 1, name = 'e'", plannercore.ErrNoReferencedRow2) - tk.MustExec("commit") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 5 21 d")) -} - -func TestForeignKeyCheckAndLock(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@foreign_key_checks=1") - tk2.MustExec("use test") - - if !*realtikvtest.WithRealTiKV { - // Unistore doesn't write lock records on secondary keys with value unchanged, causing it incorrectly ignores - // conflicts between transactions on these kinds of keys. This may make the test fail if fair locking is - // enabled. So disable it if it's not running with real tikv. - tk.MustExec("set @@tidb_pessimistic_txn_fair_locking = 0") - tk2.MustExec("set @@tidb_pessimistic_txn_fair_locking = 0") - } - - cases := []struct { - prepareSQLs []string - }{ - // Case-1: test unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, name varchar(10), unique index (id))", - "create table t2 (a int, name varchar(10), unique index (a), foreign key fk(a) references t1(id))", - }, - }, - //Case-2: test unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int, name varchar(10), unique index (id, name))", - "create table t2 (name varchar(10), a int, unique index (a, name), foreign key fk(a) references t1(id))", - }, - }, - //Case-3: test non-unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, name varchar(10), index (id))", - "create table t2 (a int, name varchar(10), index (a), foreign key fk(a) references t1(id))", - }, - }, - //Case-4: test non-unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int, name varchar(10), index (id, name))", - "create table t2 (name varchar(10), a int, index (a, name), foreign key fk(a) references t1(id))", - }, - }, - //Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, name varchar(10), primary key (id))", - "create table t2 (a int, name varchar(10), primary key (a), foreign key fk(a) references t1(id))", - }, - }, - //Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, name varchar(10), primary key (id))", - "create table t2 (a int, name varchar(10), primary key (a), foreign key fk(a) references t1(id))", - }, - }, - //Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, name varchar(10), primary key (id, name))", - "create table t2 (a int, name varchar(10), primary key (a , name), foreign key fk(a) references t1(id))", - }, - }, - // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, name varchar(10), primary key (id, name))", - "create table t2 (a int, name varchar(10), primary key (a , name), foreign key fk(a) references t1(id))", - }, - }, - } - - for _, ca := range cases { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - // Test delete in optimistic txn - tk.MustExec("insert into t1 (id, name) values (1, 'a');") - // Test insert child table - tk.MustExec("begin optimistic") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - tk2.MustExec("delete from t1 where id = 1") - err := tk.ExecToErr("commit") - require.NotNil(t, err) - require.Contains(t, err.Error(), "Write conflict") - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows()) - tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows()) - - // Test update in optimistic txn - tk.MustExec("insert into t1 (id, name) values (1, 'a');") - tk.MustExec("begin optimistic") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - tk2.MustExec("update t1 set id=2 where id = 1") - err = tk.ExecToErr("commit") - require.NotNil(t, err) - require.Contains(t, err.Error(), "Write conflict") - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("2 a")) - tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows()) - - // Test update child table - tk.MustExec("delete from t1") - tk.MustExec("delete from t2") - tk.MustExec("insert into t1 (id, name) values (1, 'a'), (2, 'b');") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - tk.MustExec("begin optimistic") - tk.MustExec("update t2 set a=2 where a = 1") - tk2.MustExec("delete from t1 where id = 2") - err = tk.ExecToErr("commit") - require.Error(t, err) - require.Contains(t, err.Error(), "Write conflict") - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a")) - tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("1 a")) - - // Test in pessimistic txn - tk.MustExec("delete from t2") - // Test insert child table - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustExec("begin pessimistic") - err := tk2.ExecToErr("update t1 set id = 2 where id = 1") - require.NotNil(t, err) - require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) - tk2.MustExec("commit") - }() - time.Sleep(time.Millisecond * 50) - tk.MustExec("commit") - wg.Wait() - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a")) - tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("1 a")) - - // Test update child table - tk.MustExec("insert into t1 (id, name) values (2, 'b');") - tk.MustExec("begin pessimistic") - tk.MustExec("update t2 set a=2 where a = 1") - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustExec("begin pessimistic") - err := tk2.ExecToErr("update t1 set id = 3 where id = 2") - require.NotNil(t, err) - require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) - tk2.MustExec("commit") - }() - time.Sleep(time.Millisecond * 50) - tk.MustExec("commit") - wg.Wait() - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) - tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("2 a")) - - // Test delete parent table in pessimistic txn - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustExec("begin pessimistic") - err := tk2.ExecToErr("delete from t1 where id = 1") - require.NotNil(t, err) - require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) - tk2.MustExec("commit") - }() - time.Sleep(time.Millisecond * 50) - tk.MustExec("commit") - wg.Wait() - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) - tk.MustQuery("select a, name from t2 order by a").Check(testkit.Rows("1 a", "2 a")) - - tk.MustExec("delete from t2") - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustExec("begin pessimistic") - err := tk2.ExecToErr("delete from t1 where id < 5") // Also test the non-fast path - require.NotNil(t, err) - require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) - tk2.MustExec("commit") - }() - time.Sleep(time.Millisecond * 50) - tk.MustExec("commit") - wg.Wait() - tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) - tk.MustQuery("select a, name from t2 order by a").Check(testkit.Rows("1 a")) - - // Test delete parent table in auto-commit txn - // TODO(crazycs520): fix following test. - /* - tk.MustExec("delete from t2") - tk.MustExec("begin pessimistic") - tk.MustExec("delete from t2;") // active txn - tk.MustExec("insert into t2 (a, name) values (1, 'a');") - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - }() - time.Sleep(time.Millisecond * 50) - tk.MustExec("commit") - wg.Wait() - */ - } -} - -func TestForeignKeyOnInsertOnDuplicateParentTableCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - if !ca.notNull { - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") - - tk.MustExec("insert into t1 (id, a) values (2, 12) on duplicate key update a=a+100, b=b+200") - tk.MustExec("insert into t1 (id, a) values (3, 13), (2, 12) on duplicate key update a=a+1000, b=b+2000") - tk.MustExec("insert into t1 (id) values (5), (6), (7) on duplicate key update a=a+10000, b=b+20000") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) - - tk.MustGetDBError("insert into t1 (id, a) values (1, 11) on duplicate key update a=a+10, b=b+20", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) - } else { - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a');") - - tk.MustExec("insert into t1 (id, a, b) values (2, 12, 22) on duplicate key update a=a+100, b=b+200") - tk.MustExec("insert into t1 (id, a, b) values (3, 13, 23), (2, 12, 22) on duplicate key update a=a+1000, b=b+2000") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) - - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21) on duplicate key update id=11") - tk.MustGetDBError("insert into t1 (id, a, b) values (11, 11, 21) on duplicate key update a=a+10, b=b+20", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1112 2222", "3 1013 2023", "4 14 24", "11 11 21")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) - } - } - - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") - tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") - - tk.MustExec("insert into t1 (id, a, b) values (2, 0, 0), (3, 0, 0) on duplicate key update id=id+100") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) - - tk.MustExec("insert into t1 (id, a, b) values (1, 0, 0) on duplicate key update a=a+100") - tk.MustGetDBError("insert into t1 (id, a, b) values (1, 0, 0) on duplicate key update id=100+id", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 111 21", "4 14 24", "102 12 22", "103 13 23")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 1 21 a")) - - // Case-10: Test insert into parent table failed cause by foreign key check, see https://github.com/pingcap/tidb/issues/39200. - tk.MustExec("drop table if exists t1,t2;") - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (id int, foreign key fk(id) references t1(id));") - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec("insert into t2 values (1)") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("insert into t1 values (1) on duplicate key update id=2") -} - -func TestForeignKeyConcurrentInsertChildTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int, a int, primary key (id));") - tk.MustExec("create table t2 (id int, a int, index(a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 (id, a) values (1, 11),(2, 12), (3, 13), (4, 14)") - var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) - go func() { - defer wg.Done() - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - for cnt := 0; cnt < 20; cnt++ { - id := cnt%4 + 1 - sql := fmt.Sprintf("insert into t2 (id, a) values (%v, %v)", cnt, id) - tk.MustExec(sql) - } - }() - } - wg.Wait() -} - -func TestForeignKeyOnUpdateChildTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") - - sqls := []string{ - "update t2 set a=100, b = 200 where id = 1", - "update t2 set a=a+10, b = b+20 where a = 11", - "update t2 set a=a+100, b = b+200", - "update t2 set a=12, b = 23 where id = 1", - } - for _, sqlStr := range sqls { - tk.MustGetDBError(sqlStr, plannercore.ErrNoReferencedRow2) - } - tk.MustExec("update t2 set a=12, b = 22 where id = 1") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 12 22 a")) - if !ca.notNull { - tk.MustExec("update t2 set a=null, b = 22 where a = 12 ") - tk.MustExec("update t2 set b = null where b = 22 ") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a")) - } - tk.MustExec("update t2 set a=13, b=23 where id = 1") - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 13 23 a")) - - // Test In txn. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") - tk.MustExec("begin") - tk.MustExec("update t2 set a=12, b=22 where id=1") - tk.MustExec("rollback") - - tk.MustExec("begin") - tk.MustExec("delete from t1 where id=2") - tk.MustGetDBError("update t2 set a=12, b=22 where id=1", plannercore.ErrNoReferencedRow2) - tk.MustExec("update t2 set a=13, b=23 where id=1") - tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") - tk.MustExec("update t2 set a=15, b=25 where id=1") - tk.MustExec("delete from t1 where id=1") - tk.MustGetDBError("update t2 set a=11, b=21 where id=1", plannercore.ErrNoReferencedRow2) - tk.MustExec("commit") - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 15 25 a")) - } - - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") - tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") - tk.MustExec("update t2 set a = 2 where id = 11") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 2 21 a")) - tk.MustExec("update t2 set a = 3 where id = 11") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 3 21 a")) - tk.MustExec("update t2 set b=b+1 where id = 11") - tk.MustQuery("select id, a, b , name from t2 order by id").Check(testkit.Rows("11 3 22 a")) - tk.MustExec("update t2 set id = 1 where id = 11") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 22 a")) - tk.MustGetDBError("update t2 set a = 10 where id = 1", plannercore.ErrNoReferencedRow2) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 22 a")) - - // Test In txn. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 21, 'a')") - tk.MustExec("begin") - tk.MustExec("update t2 set a=2, b=22 where id=1") - tk.MustExec("rollback") - - tk.MustExec("begin") - tk.MustExec("delete from t1 where id=2") - tk.MustGetDBError("update t2 set a=2, b=22 where id=1", plannercore.ErrNoReferencedRow2) - tk.MustExec("update t2 set a=3, b=23 where id=1") - tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") - tk.MustExec("update t2 set a=5, b=25 where id=1") - tk.MustExec("delete from t1 where id=1") - tk.MustGetDBError("update t2 set a=1, b=21 where id=1", plannercore.ErrNoReferencedRow2) - tk.MustExec("commit") - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 5 25 a")) -} - -func TestForeignKeyOnUpdateParentTableCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - if !ca.notNull { - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") - - tk.MustExec("update t1 set a=a+100, b = b+200 where id = 2") - tk.MustExec("update t1 set a=a+1000, b = b+2000 where a = 13 or b=222") - tk.MustExec("update t1 set a=a+10000, b = b+20000 where id = 5 or a is null or b is null") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) - tk.MustGetDBError("update t1 set a=a+10, b = b+20 where id = 1 or a = 1112 or b = 24", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) - } else { - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a');") - tk.MustExec("update t1 set a=a+100, b = b+200 where id = 2") - tk.MustExec("update t1 set a=a+1000, b = b+2000 where a = 13 or b=222") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) - tk.MustGetDBError("update t1 set a=a+10, b = b+20 where id = 1 or a = 1112 or b = 24", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) - } - } - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") - tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") - tk.MustExec("update t1 set id = id + 100 where id =2 or a = 13") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) - tk.MustGetDBError("update t1 set id = id+10 where id = 1 or b = 24", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 1 21 a")) -} - -func TestForeignKeyOnDeleteParentTableCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - if !ca.notNull { - tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1), (5, 5, null), (6, null, 6), (7, null, null);;") - - tk.MustExec("delete from t1 where id = 2") - tk.MustExec("delete from t1 where a = 3 or b = 4") - tk.MustExec("delete from t1 where a = 5 or b = 6 or a is null or b is null;") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) - } else { - tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);") - tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1);") - - tk.MustExec("delete from t1 where id = 2") - tk.MustExec("delete from t1 where a = 3 or b = 4") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) - } - models := []string{"pessimistic", "optimistic"} - for _, model := range models { - // Test in transaction. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin " + model) - tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2);") - tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1);") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - tk.MustExec("delete from t1 where id = 2") - tk.MustExec("delete from t2 where id = 1") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows()) - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) - } - } - - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1 (id int,a int, primary key(id));") - tk.MustExec("create table t2 (id int,a int, primary key(a), foreign key fk(a) references t1(id));") - tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4);") - tk.MustExec("insert into t2 values (1, 1);") - tk.MustExec("delete from t1 where id = 2;") - tk.MustExec("delete from t1 where a = 3 or a = 4;") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select id, a from t1 order by id").Check(testkit.Rows("1 1")) -} - -func TestForeignKeyOnDeleteCascade(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - cases := []struct { - prepareSQLs []string - }{ - // Case-1: test unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, a int, b int, unique index(a, b));", - "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-2: test unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key, a int, b int, unique index(a, b, id));", - "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-3: test non-unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b));", - "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-4: test non-unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b, id));", - "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - } - - for idx, ca := range cases { - tk.MustExec("drop table if exists t1, t2;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("delete from t1 where id = 2 or a = 2") - tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7) or id=7") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("5 5 ")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("5 5 e", "6 6 f", "7 g")) - - // Test in transaction. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") - tk.MustExec("delete from t1 where id = 1 or a = 2") - tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("5 5 e", "6 6 f", "7 g")) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 2 2 b")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (1, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'c')") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 1 c", "2 2 2 b")) - tk.MustExec("delete from t1") - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - // only test in non-unique index - if idx >= 2 { - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("begin") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - tk.MustExec("delete from t1 where id = 2") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (3, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("3 1 1 e")) - - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows()) - } - } - - cases = []struct { - prepareSQLs []string - }{ - // Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, primary key (a, b));", - "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, primary key (a, b));", - "create table t2 (name varchar(10), b int, a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, primary key (a, b, id));", - "create table t2 (b int, a int, name varchar(10), id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, primary key (a, b, id));", - "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", - }, - }, - // Case-9: test primary key is handle and contain foreign key column. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, primary key (id));", - "create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id) ON DELETE CASCADE);", - }, - }, - } - for _, ca := range cases { - tk.MustExec("drop table if exists t1, t2;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") - tk.MustExec("delete from t1 where id = 1 or a = 2") - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("3 3 3", "4 4 4")) - tk.MustExec("delete from t1 where a in (2,3) or b < 5") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - // test in transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") - tk.MustExec("delete from t1 where id = 1 or a = 2") - tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows()) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 2 2 b")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (1, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'c')") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 1 c", "2 2 2 b")) - tk.MustExec("delete from t1") - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - } -} - -func TestForeignKeyOnDeleteCascade2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - // Test cascade delete in self table. - tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") - tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("delete from t1 where id=11") - tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1", "10", "12", "100", "101", "102", "120", "121", "122", "1000")) - tk.MustExec("delete from t1 where id=1") - // The affect rows doesn't contain the cascade deleted rows, the behavior is compatible with MySQL. - require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) - tk.MustQuery("select id from t1 order by id").Check(testkit.Rows()) - - // Test explain analyze with foreign key cascade. - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("explain analyze delete from t1 where id=1") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - - // Test string type foreign key. - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id varchar(10) key, name varchar(10), leader varchar(10), index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") - tk.MustExec("insert into t1 values (1, 'boss', null)") - tk.MustExec("insert into t1 values (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") - tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("delete from t1 where id=11") - tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1", "10", "100", "1000", "101", "102", "12", "120", "121", "122")) - tk.MustExec("delete from t1 where id=1") - require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) - tk.MustQuery("select id from t1 order by id").Check(testkit.Rows()) - - // Test cascade delete depth. - tk.MustExec("drop table t1") - tk.MustExec("create table t1(id int primary key, pid int, index(pid), foreign key(pid) references t1(id) on delete cascade);") - tk.MustExec("insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);") - tk.MustGetDBError("delete from t1 where id=0;", exeerrors.ErrForeignKeyCascadeDepthExceeded) - tk.MustExec("delete from t1 where id=15;") - tk.MustExec("delete from t1 where id=0;") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustExec("insert into t1 values(0,0)") - tk.MustExec("delete from t1 where id=0;") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - - // Test for cascade delete failed. - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key, foreign key (id) references t1 (id) on delete cascade)") - tk.MustExec("create table t3 (id int key, foreign key (id) references t2(id))") - tk.MustExec("insert into t1 values (1)") - tk.MustExec("insert into t2 values (1)") - tk.MustExec("insert into t3 values (1)") - // test in autocommit transaction - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1")) - // Test in transaction and commit transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (2),(3),(4)") - tk.MustExec("insert into t2 values (2),(3)") - tk.MustExec("insert into t3 values (3)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 2") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - // Test in transaction and rollback transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (5), (6)") - tk.MustExec("insert into t2 values (4), (5), (6)") - tk.MustExec("insert into t3 values (5)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 4") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "5", "6")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3", "5", "6")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3", "5")) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - tk.MustExec("delete from t3 where id = 1") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select * from t1").Check(testkit.Rows("3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("3")) - // Test in autocommit=0 transaction - tk.MustExec("set autocommit=0") - tk.MustExec("insert into t1 values (1), (2)") - tk.MustExec("insert into t2 values (1), (2)") - tk.MustExec("insert into t3 values (1)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 2") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - tk.MustExec("set autocommit=1") - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - - // Test StmtCommit after fk cascade executor execute finish. - tk.MustExec("drop table if exists t1,t2,t3") - tk.MustExec("create table t0(id int primary key);") - tk.MustExec("create table t1(id int primary key, pid int, index(pid), a int, foreign key(pid) references t1(id) on delete cascade, foreign key(a) references t0(id) on delete cascade);") - tk.MustExec("insert into t0 values (0)") - tk.MustExec("insert into t1 values (0, 0, 0)") - tk.MustExec("insert into t1 (id, pid) values(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);") - tk.MustGetDBError("delete from t0 where id=0;", exeerrors.ErrForeignKeyCascadeDepthExceeded) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id=14;") - tk.MustExec("delete from t0 where id=0;") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t0").Check(testkit.Rows()) - tk.MustQuery("select * from t1").Check(testkit.Rows()) - - // Test multi-foreign key cascade in one table. - tk.MustExec("drop table if exists t1,t2,t3") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key)") - tk.MustExec("create table t3 (id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete cascade, " + - "constraint fk_id2 foreign key (id2) references t2 (id) on delete cascade)") - tk.MustExec("insert into t1 values (1), (2), (3)") - tk.MustExec("insert into t2 values (1), (2), (3)") - tk.MustExec("insert into t3 values (1,1), (1, 2), (1, 3), (2, 1), (2, 2)") - tk.MustExec("delete from t1 where id=1") - tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "2", "3")) - tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows("2 1", "2 2")) - tk.MustExec("create table t4 (id3 int key, constraint fk_id3 foreign key (id3) references t3 (id2))") - tk.MustExec("insert into t4 values (2)") - tk.MustGetDBError("delete from t1 where id = 2", plannercore.ErrRowIsReferenced2) - tk.MustGetDBError("delete from t2 where id = 2", plannercore.ErrRowIsReferenced2) - tk.MustExec("delete from t2 where id=1") - tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) - tk.MustQuery("select * from t2").Check(testkit.Rows("2", "3")) - tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows("2 2")) - - // Test multi-foreign key cascade in one table. - tk.MustExec("drop table if exists t1,t2,t3, t4") - tk.MustExec(`create table t1 (c0 int, index(c0))`) - cnt := 20 - for i := 1; i < cnt; i++ { - tk.MustExec(fmt.Sprintf("alter table t1 add column c%v int", i)) - tk.MustExec(fmt.Sprintf("alter table t1 add index idx_%v (c%v) ", i, i)) - tk.MustExec(fmt.Sprintf("alter table t1 add foreign key (c%v) references t1 (c%v) on delete cascade", i, i-1)) - } - for i := 0; i < cnt; i++ { - vals := strings.Repeat(strconv.Itoa(i)+",", 20) - tk.MustExec(fmt.Sprintf("insert into t1 values (%v)", vals[:len(vals)-1])) - } - tk.MustExec("delete from t1 where c0 in (0, 1, 2, 3, 4)") - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("15")) - - // Test foreign key cascade execution meet lock and do retry. - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@global.tidb_enable_foreign_key=1") - tk2.MustExec("set @@foreign_key_checks=1") - tk2.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (id int key, name varchar(10), pid int, index(pid), constraint fk foreign key (pid) references t1 (id) on delete cascade)") - tk.MustExec("insert into t1 values (1, 'boss', null), (2, 'a', 1), (3, 'b', 1), (4, 'c', '2')") - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t1 values (5, 'd', 3)") - tk2.MustExec("begin pessimistic") - tk2.MustExec("insert into t1 values (6, 'e', 4)") - tk2.MustExec("delete from t1 where id=2") - tk2.MustExec("commit") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - - // Test handle many foreign key value in one cascade. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (id int auto_increment key, b int);") - tk.MustExec("create table t2 (id int, b int, foreign key fk(id) references t1(id) on delete cascade)") - tk.MustExec("insert into t1 (b) values (1),(1),(1),(1),(1),(1),(1),(1);") - for i := 0; i < 12; i++ { - tk.MustExec("insert into t1 (b) select b from t1") - } - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) - tk.MustExec("insert into t2 select * from t1") - tk.MustExec("delete from t1") - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("0")) - tk.MustQuery("select count(*) from t2").Check(testkit.Rows("0")) -} - -func TestForeignKeyGenerateCascadeAST(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - fkValues := [][]types.Datum{ - {types.NewDatum(1), types.NewDatum("a")}, - {types.NewDatum(2), types.NewDatum("b")}, - } - cols := []*model.ColumnInfo{ - {ID: 1, Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeLonglong)}, - {ID: 2, Name: model.NewCIStr("name"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, - } - restoreFn := func(stmt ast.StmtNode) string { - var sb strings.Builder - fctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) - err := stmt.Restore(fctx) - require.NoError(t, err) - return sb.String() - } - checkStmtFn := func(stmt ast.StmtNode, sql string) { - exec, ok := tk.Session().(sqlexec.RestrictedSQLExecutor) - require.True(t, ok) - expectedStmt, err := exec.ParseWithParams(context.Background(), sql) - require.NoError(t, err) - require.Equal(t, restoreFn(expectedStmt), restoreFn(stmt)) - } - var stmt ast.StmtNode - stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) - checkStmtFn(stmt, "delete from test.t2 where (a,name) in ((1,'a'), (2,'b'))") - stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) - checkStmtFn(stmt, "delete from test.t2 use index(idx) where (a,name) in ((1,'a'), (2,'b'))") - stmt = executor.GenCascadeSetNullAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) - checkStmtFn(stmt, "update test.t2 set a = null, name = null where (a,name) in ((1,'a'), (2,'b'))") - stmt = executor.GenCascadeSetNullAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) - checkStmtFn(stmt, "update test.t2 use index(idx) set a = null, name = null where (a,name) in ((1,'a'), (2,'b'))") - newValue1 := []types.Datum{types.NewDatum(10), types.NewDatum("aa")} - couple := &executor.UpdatedValuesCouple{ - NewValues: newValue1, - OldValuesList: fkValues, - } - stmt = executor.GenCascadeUpdateAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, couple) - checkStmtFn(stmt, "update test.t2 set a = 10, name = 'aa' where (a,name) in ((1,'a'), (2,'b'))") - stmt = executor.GenCascadeUpdateAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, couple) - checkStmtFn(stmt, "update test.t2 use index(idx) set a = 10, name = 'aa' where (a,name) in ((1,'a'), (2,'b'))") - // Test for 1 fk column. - fkValues = [][]types.Datum{{types.NewDatum(1)}, {types.NewDatum(2)}} - cols = []*model.ColumnInfo{{ID: 1, Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeLonglong)}} - stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) - checkStmtFn(stmt, "delete from test.t2 where a in (1,2)") - stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) - checkStmtFn(stmt, "delete from test.t2 use index(idx) where a in (1,2)") -} - -func TestForeignKeyOnDeleteSetNull(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - cases := []struct { - prepareSQLs []string - }{ - // Case-1: test unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, a int, b int, unique index(a, b));", - "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", - }, - }, - // Case-2: test unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key, a int, b int, unique index(a, b, id));", - "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", - }, - }, - // Case-3: test non-unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b));", - "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", - }, - }, - // Case-4: test non-unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b, id));", - "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", - }, - }, - } - - for idx, ca := range cases { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") - tk.MustExec("delete from t1 where id = 1 or a = 2") - tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "3 c", "4 d", "5 5 e", "6 6 f", "7 g")) - - // Test in transaction. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") - tk.MustExec("delete from t1 where id = 1 or a = 2") - tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "3 c", "4 d", "5 5 e", "6 6 f", "7 g")) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 2 2 b")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (11, 1, 1, 'c')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (1, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 1, 'c')") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 2 2 b", "11 1 1 c")) - tk.MustExec("delete from t1") - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "11 c")) - - // only test in non-unique index - if idx >= 2 { - tk.MustExec("delete from t2") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("begin") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - tk.MustExec("delete from t1 where id = 2") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 a")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (2, 1, 1, 'b')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (3, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "3 1 1 e")) - - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b")) - } - } -} - -func TestForeignKeyOnDeleteSetNull2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - // Test cascade delete in self table. - tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE SET NULL);") - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") - tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("delete from t1 where id=11") - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) - tk.MustExec("delete from t1 where id=1") - // The affect rows doesn't contain the cascade deleted rows, the behavior is compatible with MySQL. - require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("10 l1_a ", "12 l1_c ", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) - - // Test explain analyze with foreign key cascade. - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("explain analyze delete from t1 where id=1") - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("10 l1_a ", "11 l1_b ", "12 l1_c ")) - - // Test string type foreign key. - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id varchar(10) key, name varchar(10), leader varchar(10), index(leader), foreign key (leader) references t1(id) ON DELETE SET NULL);") - tk.MustExec("insert into t1 values (1, 'boss', null)") - tk.MustExec("insert into t1 values (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") - tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("delete from t1 where id=11") - tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) - tk.MustExec("delete from t1 where id=1") - require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) - tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("10 l1_a ", "12 l1_c ", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) - - // Test cascade set null depth. - tk.MustExec("drop table t1") - tk.MustExec("create table t1(id int primary key, pid int, index(pid), foreign key(pid) references t1(id) on delete set null);") - tk.MustExec("insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);") - tk.MustExec("delete from t1 where id=0;") - tk.MustQuery("select id, pid from t1").Check(testkit.Rows("1 ", "2 1", "3 2", "4 3", "5 4", "6 5", "7 6", "8 7", "9 8", "10 9", "11 10", "12 11", "13 12", "14 13", "15 14")) - - // Test for cascade delete failed. - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int, foreign key (id) references t1 (id) on delete set null)") - tk.MustExec("create table t3 (id int, foreign key (id) references t2(id))") - tk.MustExec("insert into t1 values (1)") - tk.MustExec("insert into t2 values (1)") - tk.MustExec("insert into t3 values (1)") - // test in autocommit transaction - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1")) - // Test in transaction and commit transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (2),(3),(4)") - tk.MustExec("insert into t2 values (2),(3)") - tk.MustExec("insert into t3 values (3)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 2") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - tk.MustExec("commit") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) - tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) - // Test in transaction and rollback transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (5), (6)") - tk.MustExec("insert into t2 values (4), (5), (6)") - tk.MustExec("insert into t3 values (5)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 4") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "5", "6")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "1", "3", "5", "6")) - tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3", "5")) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) - tk.MustExec("delete from t3 where id = 1") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "3")) - tk.MustQuery("select * from t3").Check(testkit.Rows("3")) - - // Test in autocommit=0 transaction - tk.MustExec("set autocommit=0") - tk.MustExec("insert into t1 values (1), (2)") - tk.MustExec("insert into t2 values (1), (2)") - tk.MustExec("insert into t3 values (1)") - tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("delete from t1 where id = 2") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "", "1", "3")) - tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) - tk.MustExec("set autocommit=1") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "", "1", "3")) - tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) - - // Test StmtCommit after fk cascade executor execute finish. - tk.MustExec("drop table if exists t1,t2,t3") - tk.MustExec("create table t0(id int primary key);") - tk.MustExec("create table t1(id int primary key, pid int, index(pid), a int, foreign key(pid) references t1(id) on delete set null, foreign key(a) references t0(id) on delete set null);") - tk.MustExec("insert into t0 values (0), (1)") - tk.MustExec("insert into t1 values (0, 0, 0)") - tk.MustExec("insert into t1 (id, pid) values(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);") - tk.MustExec("update t1 set a=1 where a is null") - tk.MustExec("delete from t0 where id=0;") - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustQuery("select * from t0").Check(testkit.Rows("1")) - tk.MustQuery("select id, pid, a from t1 order by id").Check(testkit.Rows("0 0 ", "1 0 1", "2 1 1", "3 2 1", "4 3 1", "5 4 1", "6 5 1", "7 6 1", "8 7 1", "9 8 1", "10 9 1", "11 10 1", "12 11 1", "13 12 1", "14 13 1")) - - // Test multi-foreign key set null in one table. - tk.MustExec("drop table if exists t1,t2,t3") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key)") - tk.MustExec("create table t3 (id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete set null, " + - "constraint fk_id2 foreign key (id2) references t2 (id) on delete set null)") - tk.MustExec("insert into t1 values (1), (2), (3)") - tk.MustExec("insert into t2 values (1), (2), (3)") - tk.MustExec("insert into t3 values (1,1), (1, 2), (1, 3), (2, 1), (2, 2)") - tk.MustExec("delete from t1 where id=1") - tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1", "2", "3")) - tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows(" 1", " 2", " 3", "2 1", "2 2")) - tk.MustExec("create table t4 (id3 int key, constraint fk_id3 foreign key (id3) references t3 (id2))") - tk.MustExec("insert into t4 values (2)") - tk.MustExec("delete from t1 where id=2") - tk.MustGetDBError("delete from t2 where id = 2", plannercore.ErrRowIsReferenced2) - tk.MustQuery("select * from t1").Check(testkit.Rows("3")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1", "2", "3")) - tk.MustQuery("select * from t3 order by id1, id2").Check(testkit.Rows(" 1", " 1", " 2", " 2", " 3")) - - // Test foreign key set null execution meet lock and do retry. - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@global.tidb_enable_foreign_key=1") - tk2.MustExec("set @@foreign_key_checks=1") - tk2.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3, t4") - tk.MustExec("create table t1 (id int key, name varchar(10), pid int, index(pid), constraint fk foreign key (pid) references t1 (id) on delete set null)") - tk.MustExec("insert into t1 values (1, 'boss', null), (2, 'a', 1), (3, 'b', 1), (4, 'c', '2')") - tk.MustExec("begin pessimistic") - tk.MustExec("insert into t1 values (5, 'd', 3)") - tk2.MustExec("begin pessimistic") - tk2.MustExec("insert into t1 values (6, 'e', 4)") - tk2.MustExec("delete from t1 where id=2") - tk2.MustExec("commit") - tk.MustExec("delete from t1 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3 b ", "4 c ", "5 d 3", "6 e 4")) - - // Test foreign key cascade delete and set null in one row. - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (id int key, name varchar(10), pid int, ppid int, index(pid), index(ppid) , constraint fk_pid foreign key (pid) references t1 (id) on delete cascade, " + - "constraint fk_ppid foreign key (ppid) references t1 (id) on delete set null)") - tk.MustExec("insert into t1 values (1, 'boss', null, null), (2, 'a', 1, 1), (3, 'b', 1, 1), (4, 'c', '2', 1)") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows()) - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (id int key, name varchar(10), pid int, oid int, poid int, index(pid), index (oid), index(poid) , constraint fk_pid foreign key (pid) references t1 (id) on delete cascade, " + - "constraint fk_poid foreign key (poid) references t1 (oid) on delete set null)") - tk.MustExec("insert into t1 values (1, 'boss', null, 0, 0), (2, 'a', 1, 1, 0), (3, 'b', null, 2, 1), (4, 'c', 2, 3, 2)") - tk.MustExec("delete from t1 where id = 1") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3 b 2 ")) - - // Test handle many foreign key value in one cascade. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (id int auto_increment key, b int);") - tk.MustExec("create table t2 (id int, b int, foreign key fk(id) references t1(id) on delete set null)") - tk.MustExec("insert into t1 (b) values (1),(1),(1),(1),(1),(1),(1),(1);") - for i := 0; i < 12; i++ { - tk.MustExec("insert into t1 (b) select b from t1") - } - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) - tk.MustExec("insert into t2 select * from t1") - tk.MustExec("delete from t1") - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("0")) - tk.MustQuery("select count(*) from t2 where id is null").Check(testkit.Rows("32768")) -} - -func TestForeignKeyOnUpdateCascade(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - cases := []struct { - prepareSQLs []string - }{ - // Case-1: test unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int, a int, b int, unique index(a, b));", - "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-2: test unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key, a int, b int, unique index(a, b, id));", - "create table t2 (b int, name varchar(10), a int, id int key, unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-3: test non-unique index only contain foreign key columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b));", - "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-4: test non-unique index contain foreign key columns and other columns. - { - prepareSQLs: []string{ - "create table t1 (id int key,a int, b int, index(a, b, id));", - "create table t2 (name varchar(10), b int, id int key, a int, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - } - - for idx, ca := range cases { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'),(2, 12, 22, 'b'), (3, 13, 23, 'c'), (4, 14, 24, 'd'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") - tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") - tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 111 221", "2 112 222")) - tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 111 221 a", "2 112 222 b", "3 13 23 c")) - // Test update fk column to null - tk.MustExec("update t1 set a=101, b=null where id = 1 or b = 222") - tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 101 ", "2 101 ")) - tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 101 a", "2 101 b", "3 13 23 c")) - tk.MustExec("update t1 set a=null where b is null") - tk.MustQuery("select id, a, b from t1 where b is null order by id").Check(testkit.Rows("1 ", "2 ", "5 ", "7 ")) - tk.MustQuery("select id, a, b, name from t2 where b is null order by id").Check(testkit.Rows("1 101 a", "2 101 b", "5 15 e", "7 g")) - // Test update fk column from null to not-null value - tk.MustExec("update t1 set a=0, b = 0 where id = 7") - tk.MustQuery("select id, a, b from t1 where a=0 and b=0 order by id").Check(testkit.Rows("7 0 0")) - tk.MustQuery("select id, a, b from t2 where a=0 and b=0 order by id").Check(testkit.Rows()) - - // Test in transaction. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") - tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 102 202 b", "3 3 3 c", "4 4 4 d", "5 5 e", "6 6 f", "7 g")) - tk.MustExec("rollback") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") - tk.MustExec("update t1 set a=101 where a = 1") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 1 a", "2 2 2 b")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (3, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 2 2", "3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id, a").Check(testkit.Rows("1 101 1 a", "2 2 2 b", "3 1 1 c")) - tk.MustExec("update t1 set a=null, b=2000 where id in (1, 2)") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 2000", "2 2000", "3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 2000 a", "2 2000 b", "3 1 1 c")) - - // only test in non-unique index - if idx >= 2 { - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("begin") - tk.MustExec("update t1 set a=101 where id = 1") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") - tk.MustExec("update t1 set b=102 where id = 2") - tk.MustQuery("select * from t1").Check(testkit.Rows("1 101 1", "2 1 102")) - tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 1 102 a")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (3, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 1 102", "3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 102 a", "3 1 1 e")) - - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") - tk.MustExec("update t1 set a=101, b=102 where id = 1") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 102", "2 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 102 a", "2 101 102 b")) - } - } - - cases = []struct { - prepareSQLs []string - }{ - // Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, primary key (a, b));", - "create table t2 (b int, a int, name varchar(10), id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, primary key (a, b));", - "create table t2 (name varchar(10), b int, a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=0;", - "create table t1 (id int, a int, b int, primary key (a, b, id));", - "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. - { - prepareSQLs: []string{ - "set @@tidb_enable_clustered_index=1;", - "create table t1 (id int, a int, b int, primary key (a, b, id));", - "create table t2 (b int, a int, id int, name varchar(10), primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", - }, - }, - } - for idx, ca := range cases { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'),(2, 12, 22, 'b'), (3, 13, 23, 'c'), (4, 14, 24, 'd')") - tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") - tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 111 221", "2 112 222")) - tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 111 221 a", "2 112 222 b", "3 13 23 c")) - tk.MustExec("update t1 set a=101 where id = 1 or b = 222") - tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 101 221", "2 101 222")) - tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 101 221 a", "2 101 222 b", "3 13 23 c")) - - if idx < 2 { - tk.MustGetDBError("update t1 set b=200 where id in (1,2);", kv.ErrKeyExists) - } - - // test in transaction. - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") - tk.MustExec("update t1 set a=a+100, b=b+200 where id = 1 or a = 2") - tk.MustExec("update t1 set a=a+1000, b=b+2000 where a in (2,3,4) or b in (5,6,7) or id=2") - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("1 101 201", "2 1102 2202", "3 1003 2003", "4 1004 2004")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 1102 2202 b", "3 1003 2003 c", "4 1004 2004 d")) - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("1 101 201", "2 1102 2202", "3 1003 2003", "4 1004 2004")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 1102 2202 b", "3 1003 2003 c", "4 1004 2004 d")) - - tk.MustExec("delete from t2") - tk.MustExec("delete from t1") - tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") - tk.MustExec("begin") - tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") - tk.MustExec("update t1 set a=a+100, b=b+200 where id = 1") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 201", "2 2 2")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 2 2 b")) - err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") - require.Error(t, err) - require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) - tk.MustExec("insert into t1 values (3, 1, 1);") - tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 201", "2 2 2", "3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 2 2 b", "3 1 1 c")) - tk.MustExec("update t1 set a=a+1000, b=b+2000 where a>1") - tk.MustExec("commit") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1101 2201", "2 1002 2002", "3 1 1")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1101 2201 a", "2 1002 2002 b", "3 1 1 c")) - } - - // Case-9: test primary key is handle and contain foreign key column. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") - tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id) ON UPDATE CASCADE);") - tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") - tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a'),(12, 2, 22, 'b'), (13, 3, 23, 'c'), (14, 4, 24, 'd')") - tk.MustExec("update t1 set id = id + 100 where id in (1, 2, 3)") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("4 14 24", "101 11 21", "102 12 22", "103 13 23")) - tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 101 21 a", "12 102 22 b", "13 103 23 c", "14 4 24 d")) -} - -func TestForeignKeyOnUpdateCascade2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - // Test update same old row in parent, but only the first old row do cascade update - tk.MustExec("create table t1 (id int key, a int, index (a));") - tk.MustExec("create table t2 (id int key, pid int, constraint fk_pid foreign key (pid) references t1(a) ON UPDATE CASCADE);") - tk.MustExec("insert into t1 (id, a) values (1,1), (2, 1)") - tk.MustExec("insert into t2 (id, pid) values (1,1), (2, 1)") - tk.MustExec("update t1 set a=id+1") - tk.MustQuery("select id, a from t1 order by id").Check(testkit.Rows("1 2", "2 3")) - tk.MustQuery("select id, pid from t2 order by id").Check(testkit.Rows("1 2", "2 2")) - - // Test cascade delete in self table. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON UPDATE CASCADE);") - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("update t1 set id=id+10000 where id=11") - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 1")) - tk.MustExec("update t1 set id=0 where id=1") - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("0 boss ", "10 l1_a 0", "12 l1_c 0", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 0")) - - // Test explain analyze with foreign key cascade. - tk.MustExec("explain analyze update t1 set id=1 where id=10") - tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("0 boss ", "1 l1_a 0", "12 l1_c 0", "100 l2_a1 1", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 0")) - - // Test cascade delete in self table with string type foreign key. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (id varchar(100) key, name varchar(10), leader varchar(100), index(leader), foreign key (leader) references t1(id) ON UPDATE CASCADE);") - tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") - tk.MustExec("insert into t1 values (100, 'l2_a1', 10)") - tk.MustExec("insert into t1 values (110, 'l2_b1', 11)") - tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") - tk.MustExec("update t1 set id=id+10000 where id=11") - tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("1 boss ", "10 l1_a 1", "10011 l1_b 1", "12 l1_c 1", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100")) - tk.MustExec("update t1 set id=0 where id=1") - tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("0 boss ", "10 l1_a 0", "10011 l1_b 0", "12 l1_c 0", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100")) - - // Test cascade delete depth error. - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t0 (id int, unique index(id))") - tk.MustExec("insert into t0 values (1)") - for i := 1; i < 17; i++ { - tk.MustExec(fmt.Sprintf("create table t%v (id int, unique index(id), foreign key (id) references t%v(id) on update cascade)", i, i-1)) - tk.MustExec(fmt.Sprintf("insert into t%v values (1)", i)) - } - tk.MustGetDBError("update t0 set id=10 where id=1;", exeerrors.ErrForeignKeyCascadeDepthExceeded) - tk.MustQuery("select id from t0").Check(testkit.Rows("1")) - tk.MustQuery("select id from t15").Check(testkit.Rows("1")) - tk.MustExec("drop table if exists t16") - tk.MustExec("update t0 set id=10 where id=1;") - tk.MustQuery("select id from t0").Check(testkit.Rows("10")) - tk.MustQuery("select id from t15").Check(testkit.Rows("10")) - for i := 16; i > -1; i-- { - tk.MustExec("drop table if exists t" + strconv.Itoa(i)) - } - - // Test handle many foreign key value in one cascade. - tk.MustExec("create table t1 (id int auto_increment key, b int, index(b));") - tk.MustExec("create table t2 (id int, b int, foreign key fk(b) references t1(b) on update cascade)") - tk.MustExec("insert into t1 (b) values (1),(2),(3),(4),(5),(6),(7),(8);") - for i := 0; i < 12; i++ { - tk.MustExec("insert into t1 (b) select id from t1") - } - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) - tk.MustExec("insert into t2 select * from t1") - tk.MustExec("update t1 set b=2") - tk.MustQuery("select count(*) from t1 join t2 where t1.id=t2.id and t1.b=t2.b").Check(testkit.Rows("32768")) -} - -func TestDMLExplainAnalyzeFKInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - - // Test for Insert ignore foreign check runtime stats. - tk.MustExec("drop table if exists t1,t2,t3") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key)") - tk.MustExec("create table t3 (id int key, id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete cascade, " + - "constraint fk_id2 foreign key (id2) references t2 (id) on delete cascade)") - tk.MustExec("insert into t1 values (1), (2)") - tk.MustExec("insert into t2 values (1)") - res := tk.MustQuery("explain analyze insert ignore into t3 values (1, 1, 1), (2, 1, 1), (3, 2, 1), (4, 1, 1), (5, 2, 1), (6, 2, 1)") - explain := getExplainResult(res) - require.Regexpf(t, "time:.* loops:.* prepare:.* check_insert: {total_time:.* mem_insert_time:.* prefetch:.* fk_check:.*", explain, "") - res = tk.MustQuery("explain analyze insert ignore into t3 values (7, null, null), (8, null, null)") - explain = getExplainResult(res) - require.Regexpf(t, "time:.* loops:.* prepare:.* check_insert: {total_time:.* mem_insert_time:.* prefetch:.* fk_check:.*", explain, "") -} - -func getExplainResult(res *testkit.Result) string { - resBuff := bytes.NewBufferString("") - for _, row := range res.Rows() { - _, _ = fmt.Fprintf(resBuff, "%s\t", row) - } - return resBuff.String() -} - -func TestForeignKeyOnInsertOnDuplicateUpdate(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key, name varchar(10));") - tk.MustExec("create table t2 (id int key, pid int, foreign key fk(pid) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);") - tk.MustExec("insert into t1 values (1, 'a'), (2, 'b')") - tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 1), (4, 2), (5, null)") - tk.MustExec("insert into t1 values (1, 'aa') on duplicate key update name = 'aa'") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1 aa", "2 b")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 1", "2 2", "3 1", "4 2", "5 ")) - tk.MustExec("insert into t1 values (1, 'aaa') on duplicate key update id = 10") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("2 b", "10 aa")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 2", "3 10", "4 2", "5 ")) - // Test in transaction. - tk.MustExec("begin") - tk.MustExec("insert into t1 values (3, 'c')") - tk.MustExec("insert into t2 values (6, 3)") - tk.MustExec("insert into t1 values (2, 'bb'), (3, 'cc') on duplicate key update id =id*10") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("10 aa", "20 b", "30 c")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 20", "3 10", "4 20", "5 ", "6 30")) - tk.MustExec("commit") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("10 aa", "20 b", "30 c")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 20", "3 10", "4 20", "5 ", "6 30")) - tk.MustExec("delete from t1") - tk.MustQuery("select * from t2").Check(testkit.Rows("5 ")) - // Test for cascade update failed. - tk.MustExec("drop table t1, t2") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key, foreign key (id) references t1 (id) on update cascade)") - tk.MustExec("create table t3 (id int key, foreign key (id) references t2(id))") - tk.MustExec("begin") - tk.MustExec("insert into t1 values (1)") - tk.MustExec("insert into t2 values (1)") - tk.MustExec("insert into t3 values (1)") - tk.MustGetDBError("insert into t1 values (1) on duplicate key update id = 2", plannercore.ErrRowIsReferenced2) - require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) - tk.MustExec("commit") - tk.MustQuery("select * from t1").Check(testkit.Rows("1")) - tk.MustQuery("select * from t2").Check(testkit.Rows("1")) - tk.MustQuery("select * from t3").Check(testkit.Rows("1")) -} - -func TestExplainAnalyzeDMLWithFKInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (id int key, foreign key fk(id) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);") - tk.MustExec("create table t3 (id int, unique index idx(id));") - tk.MustExec("create table t4 (id int, index idx_id(id),foreign key fk(id) references t3(id));") - tk.MustExec("create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));") - tk.MustExec("create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), " + - "foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE SET NULL, " + - "foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, " + - "foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);") - tk.MustExec("create table t7(id int primary key, pid int, index(pid), foreign key(pid) references t7(id) on delete cascade);") - - cases := []struct { - prepare []string - sql string - plan string - }{ - // Test foreign key use primary key. - { - prepare: []string{ - "insert into t1 values (1),(2),(3),(4),(5)", - }, - sql: "explain analyze insert into t2 values (1),(2),(3);", - plan: "Insert_. N/A 0 root time:.*, loops:1, prepare:.*, insert:.*" + - "└─Foreign_Key_Check_. 0.00 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", - }, - { - sql: "explain analyze insert ignore into t2 values (10),(11),(12);", - plan: "Insert_.* fk_check.*" + - "└─Foreign_Key_Check_.* 0 root table:t1 total:0s, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", - }, - { - sql: "explain analyze update t2 set id=id+2 where id >1", - plan: "Update_.* 0 root time:.*, loops:1.*" + - "├─TableReader_.*" + - "│ └─TableRangeScan.*" + - "└─Foreign_Key_Check_.* 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:2 foreign_key:fk, check_exist N/A N/A", - }, - { - sql: "explain analyze delete from t1 where id>1", - plan: "Delete_.*" + - "├─TableReader_.*" + - "│ └─TableRangeScan_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:4 foreign_key:fk, on_delete:CASCADE N/A N/A.*" + - " └─Delete_.*" + - " └─Batch_Point_Get_.*", - }, - { - sql: "explain analyze update t1 set id=id+1 where id = 1", - plan: "Update_.*" + - "├─Point_Get_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:1 foreign_key:fk, on_update:CASCADE N/A N/A.*" + - " └─Update_.*" + - " ├─Point_Get_.*" + - " └─Foreign_Key_Check_.*", - }, - { - sql: "explain analyze insert into t1 values (1) on duplicate key update id = 100", - plan: "Insert_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t2 total:0s foreign_key:fk, on_update:CASCADE N/A N/A", - }, - { - sql: "explain analyze insert into t1 values (2) on duplicate key update id = 100", - plan: "Insert_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:1 foreign_key:fk, on_update:CASCADE N/A N/A.*" + - " └─Update_.*" + - " ├─Point_Get_.*" + - " └─Foreign_Key_Check_.* 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk, check_exist N/A N/A", - }, - // Test foreign key use index. - { - prepare: []string{ - "insert into t3 values (1),(2),(3),(4),(5)", - }, - sql: "explain analyze insert into t4 values (1),(2),(3);", - plan: "Insert_.*" + - "└─Foreign_Key_Check_.* 0 root table:t3, index:idx total:.*, check:.*, lock:.*, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", - }, - { - sql: "explain analyze update t4 set id=id+2 where id >1", - plan: "Update_.*" + - "├─IndexReader_.*" + - "│ └─IndexRangeScan_.*" + - "└─Foreign_Key_Check_.* 0 root table:t3, index:idx total:.*, check:.*, lock:.*, foreign_keys:2 foreign_key:fk, check_exist N/A N/A", - }, - { - sql: "explain analyze delete from t3 where id in (2,3)", - plan: "Delete_.*" + - "├─Batch_Point_Get_.*" + - "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:2 foreign_key:fk, check_not_exist N/A N/A", - }, - { - prepare: []string{ - "insert into t3 values (2)", - }, - sql: "explain analyze update t3 set id=id+1 where id = 2", - plan: "Update_.*" + - "├─Point_Get_.*" + - "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:1 foreign_key:fk, check_not_exist N/A N/A", - }, - - { - sql: "explain analyze insert into t3 values (2) on duplicate key update id = 100", - plan: "Insert_.*" + - "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:0s foreign_key:fk, check_not_exist N/A N/A", - }, - { - sql: "explain analyze insert into t3 values (3) on duplicate key update id = 100", - plan: "Insert_.*" + - "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:1 foreign_key:fk, check_not_exist N/A N/A", - }, - // Test multi-foreign keys in on table. - { - prepare: []string{ - "insert into t5 values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5)", - }, - sql: "explain analyze insert into t6 values (1,1,1)", - plan: "Insert_.*" + - "├─Foreign_Key_Check_.* 0 root table:t5 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + - "├─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_2, check_exist N/A N/A.*" + - "└─Foreign_Key_Check_.* 0 root table:t5, index:idx3 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", - }, - { - sql: "explain analyze insert ignore into t6 values (1,1,10)", - plan: "Insert_.* root time:.* loops:.* prepare:.* check_insert.* fk_check:.*" + - "├─Foreign_Key_Check.* 0 root table:t5 total:0s, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + - "├─Foreign_Key_Check.* 0 root table:t5, index:idx2 total:0s, foreign_keys:1 foreign_key:fk_2, check_exist N/A N/A.*" + - "└─Foreign_Key_Check.* 0 root table:t5, index:idx3 total:0s, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", - }, - { - sql: "explain analyze update t6 set id=id+1, id3=id2+1 where id = 1", - plan: "Update_.*" + - "├─IndexLookUp_.*" + - "│ ├─IndexRangeScan_.*" + - "│ └─TableRowIDScan_.*" + - "├─Foreign_Key_Check_.* 0 root table:t5 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + - "└─Foreign_Key_Check_.* 0 root table:t5, index:idx3 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", - }, - { - sql: "explain analyze delete from t5 where id in (4,5)", - plan: "Delete_.*" + - "├─Batch_Point_Get_.*" + - "├─Foreign_Key_Check_.* 0 root table:t6, index:idx_id2 total:.*, check:.*, foreign_keys:2 foreign_key:fk_2, check_not_exist N/A N/A.*" + - "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:2 foreign_key:fk_1, on_delete:SET NULL N/A N/A.*" + - "│ └─Update_.*" + - "│ │ ├─IndexRangeScan_.*" + - "│ │ └─TableRowIDScan_.*" + - "│ └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t6, index:fk_3 total:.*, foreign_keys:2 foreign_key:fk_3, on_delete:CASCADE N/A N/A.*" + - " └─Delete_.*" + - " └─IndexLookUp_.*" + - " ├─IndexRangeScan_.*" + - " └─TableRowIDScan_.*", - }, - { - sql: "explain analyze update t5 set id=id+1, id2=id2+1 where id = 3", - plan: "Update_.*" + - "├─Point_Get_.*" + - "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + - "│ └─Update_.*" + - "│ ├─IndexLookUp_.*" + - "│ │ ├─IndexRangeScan_.*" + - "│ │ └─TableRowIDScan_.*" + - "│ └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id2 total:.*, foreign_keys:1 foreign_key:fk_2, on_update:CASCADE N/A N/A.*" + - " └─Update_.*" + - " ├─IndexLookUp_.*" + - " │ ├─IndexRangeScan_.*" + - " │ └─TableRowIDScan_.*" + - " └─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:0s foreign_key:fk_2, check_exist N/A N/A", - }, - { - prepare: []string{ - "insert into t5 values (10,10,10)", - }, - sql: "explain analyze update t5 set id=id+1, id2=id2+1, id3=id3+1 where id = 10", - plan: "Update_.*" + - "├─Point_Get_.*" + - "├─Foreign_Key_Check_.* 0 root table:t6, index:fk_3 total:.*, check:.*, foreign_keys:1 foreign_key:.*, check_not_exist N/A N/A.*" + - "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + - "│ └─Update_.*" + - "│ ├─IndexLookUp_.*" + - "│ │ ├─IndexRangeScan_.*" + - "│ │ └─TableRowIDScan_.*" + - "│ └─Foreign_Key_Check_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id2 total:.*, foreign_keys:1 foreign_key:fk_2, on_update:CASCADE N/A N/A.*" + - " └─Update_.*" + - " ├─IndexLookUp_.*" + - " │ ├─IndexRangeScan_.*" + - " │ └─TableRowIDScan_.*" + - " └─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:0s foreign_key:fk_2, check_exist N/A N/A", - }, - { - sql: "explain analyze insert into t5 values (1,1,1) on duplicate key update id = 100, id3=100", - plan: "Insert_.*" + - "├─Foreign_Key_Check_.* 0 root table:t6, index:fk_3 total:.*, check:.*, foreign_keys:1 foreign_key:fk_3, check_not_exist N/A N/A.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + - " └─Update_.*" + - " ├─IndexLookUp_.*" + - " │ ├─IndexRangeScan_.*" + - " │ └─TableRowIDScan_.*" + - " └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A", - }, - { - prepare: []string{ - "insert into t7 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);", - }, - sql: "explain analyze delete from t7 where id = 0;", - plan: "Delete_.*" + - "├─Point_Get_.*" + - "└─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.* foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.* foreign_keys:2 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + - " └─Delete_.*" + - " ├─UnionScan_.*" + - " │ └─IndexReader_.*" + - " │ └─IndexRangeScan_.*" + - " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:0s foreign_key:fk_1, on_delete:CASCADE.*", - }, - } - for _, ca := range cases { - for _, sql := range ca.prepare { - tk.MustExec(sql) - } - res := tk.MustQuery(ca.sql) - explain := getExplainResult(res) - require.Regexp(t, ca.plan, explain) - } -} - -func TestForeignKeyRuntimeStats(t *testing.T) { - checkStats := executor.FKCheckRuntimeStats{ - Total: time.Second * 3, - Check: time.Second * 2, - Lock: time.Second, - Keys: 10, - } - require.Equal(t, "total:3s, check:2s, lock:1s, foreign_keys:10", checkStats.String()) - checkStats.Merge(checkStats.Clone()) - require.Equal(t, "total:6s, check:4s, lock:2s, foreign_keys:20", checkStats.String()) - cascadeStats := executor.FKCascadeRuntimeStats{ - Total: time.Second, - Keys: 10, - } - require.Equal(t, "total:1s, foreign_keys:10", cascadeStats.String()) - cascadeStats.Merge(cascadeStats.Clone()) - require.Equal(t, "total:2s, foreign_keys:20", cascadeStats.String()) -} - -func TestPrivilegeCheckInForeignKeyCascade(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (id int key, foreign key fk (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE);") - tk.MustExec("insert into t1 values (1), (2), (3);") - cases := []struct { - prepares []string - sql string - err error - t1Rows []string - t2Rows []string - }{ - { - prepares: []string{"grant insert on test.t2 to 'u1'@'%';"}, - sql: "insert into t2 values (1), (2), (3);", - t1Rows: []string{"1", "2", "3"}, - t2Rows: []string{"1", "2", "3"}, - }, - { - prepares: []string{"grant select, delete on test.t1 to 'u1'@'%';"}, - sql: "delete from t1 where id=1;", - t1Rows: []string{"2", "3"}, - t2Rows: []string{"2", "3"}, - }, - { - prepares: []string{"grant select, update on test.t1 to 'u1'@'%';"}, - sql: "update t1 set id=id+10 where id=2;", - t1Rows: []string{"3", "12"}, - t2Rows: []string{"3", "12"}, - }, - } - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("set @@foreign_key_checks=1") - for _, ca := range cases { - tk.MustExec("drop user if exists 'u1'@'%'") - tk.MustExec("create user 'u1'@'%' identified by '';") - for _, sql := range ca.prepares { - tk.MustExec(sql) - } - err := tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - require.NoError(t, err) - if ca.err == nil { - tk2.MustExec(ca.sql) - } else { - err = tk2.ExecToErr(ca.sql) - require.Error(t, err) - } - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows(ca.t1Rows...)) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows(ca.t2Rows...)) - } -} - -func TestTableLockInForeignKeyCascade(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("set @@foreign_key_checks=1") - // enable table lock - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableTableLock = true - }) - defer func() { - config.UpdateGlobal(func(conf *config.Config) { - conf.EnableTableLock = false - }) - }() - tk.MustExec("create table t1 (id int key);") - tk.MustExec("create table t2 (id int key, foreign key fk (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE);") - tk.MustExec("insert into t1 values (1), (2), (3);") - tk.MustExec("insert into t2 values (1), (2), (3);") - tk.MustExec("lock table t2 read;") - tk2.MustGetDBError("delete from t1 where id = 1", infoschema.ErrTableLocked) - tk.MustExec("unlock tables;") - tk2.MustExec("delete from t1 where id = 1") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("2", "3")) - tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("2", "3")) -} - -func TestForeignKeyIssue39732(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_stmt_summary=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create user 'u1'@'%' identified by '';") - tk.MustExec("GRANT ALL PRIVILEGES ON *.* TO 'u1'@'%'") - err := tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - require.NoError(t, err) - tk.MustExec("create table t1 (id int key, leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") - tk.MustExec("insert into t1 values (1, null), (10, 1), (11, 1), (20, 10)") - tk.MustExec(`prepare stmt1 from 'delete from t1 where id = ?';`) - tk.MustExec(`set @a = 1;`) - tk.MustExec("execute stmt1 using @a;") - tk.MustQuery("select * from t1 order by id").Check(testkit.Rows()) -} - -func TestForeignKeyOnReplaceIntoChildTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t_data (id int, a int, b int)") - tk.MustExec("insert into t_data (id, a, b) values (1, 1, 1), (2, 2, 2);") - for _, ca := range foreignKeyTestCase1 { - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - for _, sql := range ca.prepareSQLs { - tk.MustExec(sql) - } - tk.MustExec("replace into t1 (id, a, b) values (1, 1, 1);") - tk.MustExec("replace into t2 (id, a, b) values (1, 1, 1)") - tk.MustGetDBError("replace into t1 (id, a, b) values (1, 2, 3);", plannercore.ErrRowIsReferenced2) - if !ca.notNull { - tk.MustExec("replace into t2 (id, a, b) values (2, null, 1)") - tk.MustExec("replace into t2 (id, a, b) values (3, 1, null)") - tk.MustExec("replace into t2 (id, a, b) values (4, null, null)") - } - tk.MustGetDBError("replace into t2 (id, a, b) values (5, 1, 0);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("replace into t2 (id, a, b) values (6, 0, 1);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("replace into t2 (id, a, b) values (7, 2, 2);", plannercore.ErrNoReferencedRow2) - // Test replace into from select. - tk.MustExec("delete from t2") - tk.MustExec("replace into t2 (id, a, b) select id, a, b from t_data where t_data.id=1") - tk.MustGetDBError("replace into t2 (id, a, b) select id, a, b from t_data where t_data.id=2", plannercore.ErrNoReferencedRow2) - - // Test in txn - tk.MustExec("delete from t2") - tk.MustExec("begin") - tk.MustExec("delete from t1 where a=1") - tk.MustGetDBError("replace into t2 (id, a, b) values (1, 1, 1)", plannercore.ErrNoReferencedRow2) - tk.MustExec("replace into t1 (id, a, b) values (2, 2, 2)") - tk.MustExec("replace into t2 (id, a, b) values (2, 2, 2)") - tk.MustGetDBError("replace into t1 (id, a, b) values (2, 2, 3);", plannercore.ErrRowIsReferenced2) - tk.MustExec("rollback") - tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) - tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) - } - - // Case-10: test primary key is handle and contain foreign key column, and foreign key column has default value. - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("set @@tidb_enable_clustered_index=0;") - tk.MustExec("create table t1 (id int,a int, primary key(id));") - tk.MustExec("create table t2 (id int key,a int not null default 0, index (a), foreign key fk(a) references t1(id));") - tk.MustExec("replace into t1 values (1, 1);") - tk.MustExec("replace into t2 values (1, 1);") - tk.MustGetDBError("replace into t2 (id) values (10);", plannercore.ErrNoReferencedRow2) - tk.MustGetDBError("replace into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) - - // Case-11: test primary key is handle and contain foreign key column, and foreign key column doesn't have default value. - tk.MustExec("drop table if exists t2;") - tk.MustExec("create table t2 (id int key,a int, index (a), foreign key fk(a) references t1(id));") - tk.MustExec("replace into t2 values (1, 1);") - tk.MustExec("replace into t2 (id) values (10);") - tk.MustGetDBError("replace into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) -} - -func TestForeignKeyLargeTxnErr(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") - tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890');") - for i := 0; i < 8; i++ { - tk.MustExec("insert into t1 (name) select name from t1;") - } - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) - tk.MustExec("update t1 set pid=1 where id>1") - tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") - originLimit := kv.TxnTotalSizeLimit.Load() - defer func() { - kv.TxnTotalSizeLimit.Store(originLimit) - }() - // Set the limitation to a small value, make it easier to reach the limitation. - kv.TxnTotalSizeLimit.Store(10240) - tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) - // foreign key cascade behaviour will cause ErrTxnTooLarge. - tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) - tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) - tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) - tk.MustQuery("select id,pid from t1 where id<3 order by id").Check(testkit.Rows("1 ", "2 1")) - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec("update t1 set id=id+100000 where id=1") - tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) -} - -func TestForeignKeyAndLockView(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (id int key)") - tk.MustExec("create table t2 (id int key, foreign key (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE)") - tk.MustExec("insert into t1 values (1)") - tk.MustExec("insert into t2 values (1)") - tk.MustExec("begin pessimistic") - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec("update t2 set id=2") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("set @@foreign_key_checks=1") - tk2.MustExec("use test") - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - tk2.MustExec("begin pessimistic") - tk2.MustExec("update t1 set id=2 where id=1") - tk2.MustExec("commit") - }() - time.Sleep(time.Millisecond * 200) - _, digest := parser.NormalizeDigest("update t1 set id=2 where id=1") - tk.MustQuery("select CURRENT_SQL_DIGEST from information_schema.tidb_trx where state='LockWaiting' and db='test'").Check(testkit.Rows(digest.String())) - tk.MustGetErrMsg("update t1 set id=2", "[executor:1213]Deadlock found when trying to get lock; try restarting transaction") - wg.Wait() -} - -func TestForeignKeyAndMemoryTracker(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("use test") - tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") - tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz');") - for i := 0; i < 8; i++ { - tk.MustExec("insert into t1 (name) select name from t1;") - } - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) - tk.MustExec("update t1 set pid=1 where id>1") - tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") - tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") - tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") - tk.MustExec("set @@tidb_mem_quota_query=40960;") - // foreign key cascade behaviour will exceed memory quota. - err := tk.ExecToErr("update t1 set id=id+100000 where id=1") - require.Error(t, err) - require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) - tk.MustQuery("select id,pid from t1 where id = 1").Check(testkit.Rows("1 ")) - tk.MustExec("set @@foreign_key_checks=0") - // After disable foreign_key_checks, following DML will execute successful. - tk.MustExec("update t1 set id=id+100000 where id=1") - tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) -} diff --git a/executor/test/fktest/main_test.go b/executor/test/fktest/main_test.go deleted file mode 100644 index 0c90a74931b12..0000000000000 --- a/executor/test/fktest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package fk_test - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/indexmergereadtest/BUILD.bazel b/executor/test/indexmergereadtest/BUILD.bazel deleted file mode 100644 index 1d5d2ee88bc01..0000000000000 --- a/executor/test/indexmergereadtest/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "indexmergereadtest_test", - timeout = "short", - srcs = [ - "index_merge_reader_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 36, - deps = [ - "//config", - "//executor", - "//meta/autoid", - "//session", - "//testkit", - "//testkit/testutil", - "//util", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/indexmergereadtest/main_test.go b/executor/test/indexmergereadtest/main_test.go deleted file mode 100644 index d312e1a4f6558..0000000000000 --- a/executor/test/indexmergereadtest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package indexmergereadtest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/issuetest/BUILD.bazel b/executor/test/issuetest/BUILD.bazel deleted file mode 100644 index 6e3778ff441c9..0000000000000 --- a/executor/test/issuetest/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "issuetest_test", - timeout = "short", - srcs = [ - "executor_issue_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 17, - deps = [ - "//autoid_service", - "//config", - "//kv", - "//meta/autoid", - "//parser/auth", - "//parser/charset", - "//parser/mysql", - "//session", - "//testkit", - "//util", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/issuetest/main_test.go b/executor/test/issuetest/main_test.go deleted file mode 100644 index 25d37b71f892e..0000000000000 --- a/executor/test/issuetest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package issuetest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/jointest/BUILD.bazel b/executor/test/jointest/BUILD.bazel deleted file mode 100644 index 9f313448718e3..0000000000000 --- a/executor/test/jointest/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "jointest_test", - timeout = "moderate", - srcs = [ - "join_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 11, - deps = [ - "//config", - "//meta/autoid", - "//session", - "//testkit", - "//util", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/jointest/hashjoin/BUILD.bazel b/executor/test/jointest/hashjoin/BUILD.bazel deleted file mode 100644 index bb2d662ddffe4..0000000000000 --- a/executor/test/jointest/hashjoin/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "hashjoin_test", - timeout = "short", - srcs = [ - "hash_join_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 14, - deps = [ - "//config", - "//meta/autoid", - "//planner/core", - "//session", - "//sessionctx/variable", - "//testkit", - "//util/dbterror/exeerrors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/jointest/hashjoin/main_test.go b/executor/test/jointest/hashjoin/main_test.go deleted file mode 100644 index cb8498d35d4dc..0000000000000 --- a/executor/test/jointest/hashjoin/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package hashjoin - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/jointest/join_test.go b/executor/test/jointest/join_test.go deleted file mode 100644 index d0158f811ce4d..0000000000000 --- a/executor/test/jointest/join_test.go +++ /dev/null @@ -1,1437 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package jointest - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/require" -) - -func TestJoinInDisk(t *testing.T) { - origin := config.RestoreFunc() - defer origin() - - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") - tk.MustExec("SET GLOBAL tidb_mem_oom_action='LOG'") - tk.MustExec("use test") - - sm := &testkit.MockSessionManager{ - PS: make([]*util.ProcessInfo, 0), - } - tk.Session().SetSessionManager(sm) - dom.ExpensiveQueryHandle().SetSessionManager(sm) - - // TODO(fengliyuan): how to ensure that it is using disk really? - tk.MustExec("set @@tidb_mem_quota_query=1;") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int, c2 int)") - tk.MustExec("create table t1(c1 int, c2 int)") - tk.MustExec("insert into t values(1,1),(2,2)") - tk.MustExec("insert into t1 values(2,3),(4,4)") - result := tk.MustQuery("select /*+ TIDB_HJ(t, t2) */ * from t, t1 where t.c1 = t1.c1") - result.Check(testkit.Rows("2 2 2 3")) -} - -func TestJoin2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set @@tidb_index_lookup_join_concurrency = 200") - require.Equal(t, 200, tk.Session().GetSessionVars().IndexLookupJoinConcurrency()) - - tk.MustExec("set @@tidb_index_lookup_join_concurrency = 4") - require.Equal(t, 4, tk.Session().GetSessionVars().IndexLookupJoinConcurrency()) - - tk.MustExec("set @@tidb_index_lookup_size = 2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c int)") - tk.MustExec("insert t values (1)") - tests := []struct { - sql string - result [][]interface{} - }{ - { - "select 1 from t as a left join t as b on 0", - testkit.Rows("1"), - }, - { - "select 1 from t as a join t as b on 1", - testkit.Rows("1"), - }, - } - for _, tt := range tests { - result := tk.MustQuery(tt.sql) - result.Check(tt.result) - } - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int, c2 int)") - tk.MustExec("create table t1(c1 int, c2 int)") - tk.MustExec("insert into t values(1,1),(2,2)") - tk.MustExec("insert into t1 values(2,3),(4,4)") - result := tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") - result.Check(testkit.Rows("1 1 ")) - result = tk.MustQuery("select * from t1 right outer join t on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") - result.Check(testkit.Rows(" 1 1")) - result = tk.MustQuery("select * from t right outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") - result.Check(testkit.Rows()) - result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t1.c1 = 3 or false") - result.Check(testkit.Rows()) - result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 and t.c1 != 1 order by t1.c1") - result.Check(testkit.Rows("1 1 ", "2 2 2 3")) - result = tk.MustQuery("select t.c1, t1.c1 from t left outer join t1 on t.c1 = t1.c1 and t.c2 + t1.c2 <= 5") - result.Check(testkit.Rows("1 ", "2 2")) - - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t3") - - tk.MustExec("create table t1 (c1 int, c2 int)") - tk.MustExec("create table t2 (c1 int, c2 int)") - tk.MustExec("create table t3 (c1 int, c2 int)") - - tk.MustExec("insert into t1 values (1,1), (2,2), (3,3)") - tk.MustExec("insert into t2 values (1,1), (3,3), (5,5)") - tk.MustExec("insert into t3 values (1,1), (5,5), (9,9)") - - result = tk.MustQuery("select * from t1 left join t2 on t1.c1 = t2.c1 right join t3 on t2.c1 = t3.c1 order by t1.c1, t1.c2, t2.c1, t2.c2, t3.c1, t3.c2;") - result.Check(testkit.Rows(" 5 5", " 9 9", "1 1 1 1 1 1")) - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (c1 int)") - tk.MustExec("insert into t1 values (1), (1), (1)") - result = tk.MustQuery("select * from t1 a join t1 b on a.c1 = b.c1;") - result.Check(testkit.Rows("1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int, index k(c1))") - tk.MustExec("create table t1(c1 int)") - tk.MustExec("insert into t values (1),(2),(3),(4),(5),(6),(7)") - tk.MustExec("insert into t1 values (1),(2),(3),(4),(5),(6),(7)") - result = tk.MustQuery("select a.c1 from t a , t1 b where a.c1 = b.c1 order by a.c1;") - result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7")) - // Test race. - result = tk.MustQuery("select a.c1 from t a , t1 b where a.c1 = b.c1 and a.c1 + b.c1 > 5 order by b.c1") - result.Check(testkit.Rows("3", "4", "5", "6", "7")) - result = tk.MustQuery("select a.c1 from t a , (select * from t1 limit 3) b where a.c1 = b.c1 order by b.c1;") - result.Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("drop table if exists t,t2,t1") - tk.MustExec("create table t(c1 int)") - tk.MustExec("create table t1(c1 int, c2 int)") - tk.MustExec("create table t2(c1 int, c2 int)") - tk.MustExec("insert into t1 values(1,2),(2,3),(3,4)") - tk.MustExec("insert into t2 values(1,0),(2,0),(3,0)") - tk.MustExec("insert into t values(1),(2),(3)") - result = tk.MustQuery("select * from t1 , t2 where t2.c1 = t1.c1 and t2.c2 = 0 and t1.c2 in (select * from t)") - result.Sort().Check(testkit.Rows("1 2 1 0", "2 3 2 0")) - result = tk.MustQuery("select * from t1 , t2 where t2.c1 = t1.c1 and t2.c2 = 0 and t1.c1 = 1 order by t1.c2 limit 1") - result.Sort().Check(testkit.Rows("1 2 1 0")) - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t(a int primary key, b int)") - tk.MustExec("create table t1(a int, b int, key s(b))") - tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") - tk.MustExec("insert into t1 values(1, 2), (1, 3), (1, 4), (3, 4), (4, 5)") - - // The physical plans of the two sql are tested at physical_plan_test.go - tk.MustQuery("select /*+ INL_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) - tk.MustQuery("select /*+ INL_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Sort().Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) - // Test single index reader. - tk.MustQuery("select /*+ INL_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Check(testkit.Rows("2", "3")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Sort().Check(testkit.Rows("2", "3")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Check(testkit.Rows("2", "3")) - tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) - tk.MustQuery("select /*+ INL_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) - - // Test that two conflict hints will return warning. - tk.MustExec("select /*+ TIDB_INLJ(t) TIDB_SMJ(t) */ * from t join t1 on t.a=t1.a") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - tk.MustExec("select /*+ TIDB_INLJ(t) TIDB_HJ(t) */ * from t join t1 on t.a=t1.a") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - tk.MustExec("select /*+ TIDB_SMJ(t) TIDB_HJ(t) */ * from t join t1 on t.a=t1.a") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1),(2), (3)") - tk.MustQuery("select @a := @a + 1 from t, (select @a := 0) b;").Check(testkit.Rows("1", "2", "3")) - - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t(a int primary key, b int, key s(b))") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("insert into t values(1, 3), (2, 2), (3, 1)") - tk.MustExec("insert into t1 values(0, 0), (1, 2), (1, 3), (3, 4)") - tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) - tk.MustQuery("select /*+ INL_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) - tk.MustQuery("select /*+ INL_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) - - // join reorder will disorganize the resulting schema - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("insert into t values(1,2)") - tk.MustExec("insert into t1 values(3,4)") - tk.MustQuery("select (select t1.a from t1 , t where t.a = s.a limit 2) from t as s").Check(testkit.Rows("3")) - - // test index join bug - tk.MustExec("drop table if exists t, t1") - tk.MustExec("create table t(a int, b int, key s1(a,b), key s2(b))") - tk.MustExec("create table t1(a int)") - tk.MustExec("insert into t values(1,2), (5,3), (6,4)") - tk.MustExec("insert into t1 values(1), (2), (3)") - tk.MustQuery("select /*+ INL_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) - tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) - tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) - - // test issue#4997 - tk.MustExec("drop table if exists t1, t2") - tk.MustExec(` - CREATE TABLE t1 ( - pk int(11) NOT NULL AUTO_INCREMENT primary key, - a int(11) DEFAULT NULL, - b date DEFAULT NULL, - c varchar(1) DEFAULT NULL, - KEY a (a), - KEY b (b), - KEY c (c,a) - )`) - tk.MustExec(` - CREATE TABLE t2 ( - pk int(11) NOT NULL AUTO_INCREMENT primary key, - a int(11) DEFAULT NULL, - b date DEFAULT NULL, - c varchar(1) DEFAULT NULL, - KEY a (a), - KEY b (b), - KEY c (c,a) - )`) - tk.MustExec(`insert into t1 value(1,1,"2000-11-11", null);`) - result = tk.MustQuery(` - SELECT table2.b AS field2 FROM - ( - t1 AS table1 LEFT OUTER JOIN - (SELECT tmp_t2.* FROM ( t2 AS tmp_t1 RIGHT JOIN t1 AS tmp_t2 ON (tmp_t2.a = tmp_t1.a))) AS table2 - ON (table2.c = table1.c) - ) `) - result.Check(testkit.Rows("")) - - // test virtual rows are included (issue#5771) - result = tk.MustQuery(`SELECT 1 FROM (SELECT 1) t1, (SELECT 1) t2`) - result.Check(testkit.Rows("1")) - - result = tk.MustQuery(` - SELECT @NUM := @NUM + 1 as NUM FROM - ( SELECT 1 UNION ALL - SELECT 2 UNION ALL - SELECT 3 - ) a - INNER JOIN - ( SELECT 1 UNION ALL - SELECT 2 UNION ALL - SELECT 3 - ) b, - (SELECT @NUM := 0) d; - `) - result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) - - // This case is for testing: - // when the main thread calls Executor.Close() while the out data fetch worker and join workers are still working, - // we need to stop the goroutines as soon as possible to avoid unexpected error. - tk.MustExec("set @@tidb_hash_join_concurrency=5") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int)") - for i := 0; i < 100; i++ { - tk.MustExec("insert into t value(1)") - } - result = tk.MustQuery("select /*+ TIDB_HJ(s, r) */ * from t as s join t as r on s.a = r.a limit 1;") - result.Check(testkit.Rows("1 1")) - - tk.MustExec("drop table if exists user, aa, bb") - tk.MustExec("create table aa(id int)") - tk.MustExec("insert into aa values(1)") - tk.MustExec("create table bb(id int)") - tk.MustExec("insert into bb values(1)") - tk.MustExec("create table user(id int, name varchar(20))") - tk.MustExec("insert into user values(1, 'a'), (2, 'b')") - tk.MustQuery("select user.id,user.name from user left join aa on aa.id = user.id left join bb on aa.id = bb.id where bb.id < 10;").Check(testkit.Rows("1 a")) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t (a bigint);`) - tk.MustExec(`insert into t values (1);`) - tk.MustQuery(`select t2.a, t1.a from t t1 inner join (select "1" as a) t2 on t2.a = t1.a;`).Check(testkit.Rows("1 1")) - tk.MustQuery(`select t2.a, t1.a from t t1 inner join (select "2" as b, "1" as a) t2 on t2.a = t1.a;`).Check(testkit.Rows("1 1")) - - tk.MustExec("drop table if exists t1, t2, t3, t4") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int, b int)") - tk.MustExec("create table t3(a int, b int)") - tk.MustExec("create table t4(a int, b int)") - tk.MustExec("insert into t1 values(1, 1)") - tk.MustExec("insert into t2 values(1, 1)") - tk.MustExec("insert into t3 values(1, 1)") - tk.MustExec("insert into t4 values(1, 1)") - tk.MustQuery("select min(t2.b) from t1 right join t2 on t2.a=t1.a right join t3 on t2.a=t3.a left join t4 on t3.a=t4.a").Check(testkit.Rows("1")) -} - -func TestJoinCast(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - var result *testkit.Result - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int)") - tk.MustExec("create table t1(c1 int unsigned)") - tk.MustExec("insert into t values (1)") - tk.MustExec("insert into t1 values (1)") - result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") - result.Check(testkit.Rows("1")) - - // int64(-1) != uint64(18446744073709551615) - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 bigint)") - tk.MustExec("create table t1(c1 bigint unsigned)") - tk.MustExec("insert into t values (-1)") - tk.MustExec("insert into t1 values (18446744073709551615)") - result = tk.MustQuery("select * from t , t1 where t.c1 = t1.c1") - result.Check(testkit.Rows()) - - // float(1) == double(1) - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 float)") - tk.MustExec("create table t1(c1 double)") - tk.MustExec("insert into t values (1.0)") - tk.MustExec("insert into t1 values (1.00)") - result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") - result.Check(testkit.Rows("1")) - - // varchar("x") == char("x") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 varchar(1))") - tk.MustExec("create table t1(c1 char(1))") - tk.MustExec(`insert into t values ("x")`) - tk.MustExec(`insert into t1 values ("x")`) - result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") - result.Check(testkit.Rows("x")) - - // varchar("x") != char("y") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 varchar(1))") - tk.MustExec("create table t1(c1 char(1))") - tk.MustExec(`insert into t values ("x")`) - tk.MustExec(`insert into t1 values ("y")`) - result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") - result.Check(testkit.Rows()) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int,c2 double)") - tk.MustExec("create table t1(c1 double,c2 int)") - tk.MustExec("insert into t values (1, 2), (1, NULL)") - tk.MustExec("insert into t1 values (1, 2), (1, NULL)") - result = tk.MustQuery("select * from t a , t1 b where (a.c1, a.c2) = (b.c1, b.c2);") - result.Check(testkit.Rows("1 2 1 2")) - - /* Issue 11895 */ - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t(c1 bigint unsigned);") - tk.MustExec("create table t1(c1 bit(64));") - tk.MustExec("insert into t value(18446744073709551615);") - tk.MustExec("insert into t1 value(-1);") - result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") - require.Len(t, result.Rows(), 1) - - /* Issues 11896 */ - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t(c1 bigint);") - tk.MustExec("create table t1(c1 bit(64));") - tk.MustExec("insert into t value(1);") - tk.MustExec("insert into t1 value(1);") - result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") - require.Len(t, result.Rows(), 1) - - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t(c1 bigint);") - tk.MustExec("create table t1(c1 bit(64));") - tk.MustExec("insert into t value(-1);") - tk.MustExec("insert into t1 value(18446744073709551615);") - result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") - // TODO: MySQL will return one row, because c1 in t1 is 0xffffffff, which equals to -1. - require.Len(t, result.Rows(), 0) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t(c1 bigint)") - tk.MustExec("create table t1(c1 bigint unsigned)") - tk.MustExec("create table t2(c1 Date)") - tk.MustExec("insert into t value(20191111)") - tk.MustExec("insert into t1 value(20191111)") - tk.MustExec("insert into t2 value('2019-11-11')") - result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1") - result.Check(testkit.Rows("20191111 20191111 2019-11-11")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2;") - tk.MustExec("create table t(c1 bigint);") - tk.MustExec("create table t1(c1 bigint unsigned);") - tk.MustExec("create table t2(c1 enum('a', 'b', 'c', 'd'));") - tk.MustExec("insert into t value(3);") - tk.MustExec("insert into t1 value(3);") - tk.MustExec("insert into t2 value('c');") - result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1;") - result.Check(testkit.Rows("3 3 c")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("drop table if exists t2;") - tk.MustExec("create table t(c1 bigint);") - tk.MustExec("create table t1(c1 bigint unsigned);") - tk.MustExec("create table t2 (c1 SET('a', 'b', 'c', 'd'));") - tk.MustExec("insert into t value(9);") - tk.MustExec("insert into t1 value(9);") - tk.MustExec("insert into t2 value('a,d');") - result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1;") - result.Check(testkit.Rows("9 9 a,d")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 int)") - tk.MustExec("create table t1(c1 decimal(4,2))") - tk.MustExec("insert into t values(0), (2)") - tk.MustExec("insert into t1 values(0), (9)") - result = tk.MustQuery("select * from t left join t1 on t1.c1 = t.c1") - result.Sort().Check(testkit.Rows("0 0.00", "2 ")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 decimal(4,1))") - tk.MustExec("create table t1(c1 decimal(4,2))") - tk.MustExec("insert into t values(0), (2)") - tk.MustExec("insert into t1 values(0), (9)") - result = tk.MustQuery("select * from t left join t1 on t1.c1 = t.c1") - result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t(c1 decimal(4,1))") - tk.MustExec("create table t1(c1 decimal(4,2))") - tk.MustExec("create index k1 on t1(c1)") - tk.MustExec("insert into t values(0), (2)") - tk.MustExec("insert into t1 values(0), (9)") - result = tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") - result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) - result = tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") - result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) - result = tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") - result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t(c1 char(10))") - tk.MustExec("create table t1(c1 char(10))") - tk.MustExec("create table t2(c1 char(10))") - tk.MustExec("insert into t values('abd')") - tk.MustExec("insert into t1 values('abc')") - tk.MustExec("insert into t2 values('abc')") - result = tk.MustQuery("select * from (select * from t union all select * from t1) t1 join t2 on t1.c1 = t2.c1") - result.Sort().Check(testkit.Rows("abc abc")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a varchar(10), index idx(a))") - tk.MustExec("insert into t values('1'), ('2'), ('3')") - tk.MustExec("set @@tidb_init_chunk_size=1") - result = tk.MustQuery("select a from (select /*+ INL_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") - result.Sort().Check(testkit.Rows("1", "2", "3")) - result = tk.MustQuery("select a from (select /*+ INL_HASH_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") - result.Sort().Check(testkit.Rows("1", "2", "3")) - result = tk.MustQuery("select a from (select /*+ INL_MERGE_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") - result.Sort().Check(testkit.Rows("1", "2", "3")) - tk.MustExec("set @@tidb_init_chunk_size=32") -} - -func TestUsing(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3, t4") - tk.MustExec("create table t1 (a int, c int)") - tk.MustExec("create table t2 (a int, d int)") - tk.MustExec("create table t3 (a int)") - tk.MustExec("create table t4 (a int)") - tk.MustExec("insert t1 values (2, 4), (1, 3)") - tk.MustExec("insert t2 values (2, 5), (3, 6)") - tk.MustExec("insert t3 values (1)") - - tk.MustQuery("select * from t1 join t2 using (a)").Check(testkit.Rows("2 4 5")) - tk.MustQuery("select t1.a, t2.a from t1 join t2 using (a)").Check(testkit.Rows("2 2")) - - tk.MustQuery("select * from t1 right join t2 using (a) order by a").Check(testkit.Rows("2 5 4", "3 6 ")) - tk.MustQuery("select t1.a, t2.a from t1 right join t2 using (a) order by t2.a").Check(testkit.Rows("2 2", " 3")) - - tk.MustQuery("select * from t1 left join t2 using (a) order by a").Check(testkit.Rows("1 3 ", "2 4 5")) - tk.MustQuery("select t1.a, t2.a from t1 left join t2 using (a) order by t1.a").Check(testkit.Rows("1 ", "2 2")) - - tk.MustQuery("select * from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows("1 ")) - tk.MustQuery("select * from t1 join t2 using (a) right join t3 on (t2.a = t3.a)").Check(testkit.Rows(" 1")) - tk.MustQuery("select t2.a from t1 join t2 using (a) right join t3 on (t1.a = t3.a)").Check(testkit.Rows("")) - tk.MustQuery("select t1.a, t2.a, t3.a from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows(" 1")) - tk.MustQuery("select t1.c, t2.d from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows(" ")) - - tk.MustExec("alter table t1 add column b int default 1 after a") - tk.MustExec("alter table t2 add column b int default 1 after a") - tk.MustQuery("select * from t1 join t2 using (b, a)").Check(testkit.Rows("2 1 4 5")) - - tk.MustExec("select * from (t1 join t2 using (a)) join (t3 join t4 using (a)) on (t2.a = t4.a and t1.a = t3.a)") - - tk.MustExec("drop table if exists t, tt") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("create table tt(b int, a int)") - tk.MustExec("insert into t (a, b) values(1, 1)") - tk.MustExec("insert into tt (a, b) values(1, 2)") - tk.MustQuery("select * from t join tt using(a)").Check(testkit.Rows("1 1 2")) - - tk.MustExec("drop table if exists t, tt") - tk.MustExec("create table t(a float, b int)") - tk.MustExec("create table tt(b bigint, a int)") - // Check whether this sql can execute successfully. - tk.MustExec("select * from t join tt using(a)") - - tk.MustExec("drop table if exists t, s") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("create table s(b int, a int)") - tk.MustExec("insert into t values(1,1), (2,2), (3,3), (null,null)") - tk.MustExec("insert into s values(1,1), (3,3), (null,null)") - - // For issue 20477 - tk.MustQuery("select t.*, s.* from t join s using(a)").Check(testkit.Rows("1 1 1 1", "3 3 3 3")) - tk.MustQuery("select s.a from t join s using(a)").Check(testkit.Rows("1", "3")) - tk.MustQuery("select s.a from t join s using(a) where s.a > 1").Check(testkit.Rows("3")) - tk.MustQuery("select s.a from t join s using(a) order by s.a").Check(testkit.Rows("1", "3")) - tk.MustQuery("select s.a from t join s using(a) where s.a > 1 order by s.a").Check(testkit.Rows("3")) - tk.MustQuery("select s.a from t join s using(a) where s.a > 1 order by s.a limit 2").Check(testkit.Rows("3")) - - // For issue 20441 - tk.MustExec(`DROP TABLE if exists t1, t2, t3`) - tk.MustExec(`create table t1 (i int)`) - tk.MustExec(`create table t2 (i int)`) - tk.MustExec(`create table t3 (i int)`) - tk.MustExec(`select * from t1,t2 natural left join t3 order by t1.i,t2.i,t3.i`) - tk.MustExec(`select t1.i,t2.i,t3.i from t2 natural left join t3,t1 order by t1.i,t2.i,t3.i`) - tk.MustExec(`select * from t1,t2 natural right join t3 order by t1.i,t2.i,t3.i`) - tk.MustExec(`select t1.i,t2.i,t3.i from t2 natural right join t3,t1 order by t1.i,t2.i,t3.i`) - - // For issue 15844 - tk.MustExec(`DROP TABLE if exists t0, t1`) - tk.MustExec(`CREATE TABLE t0(c0 INT)`) - tk.MustExec(`CREATE TABLE t1(c0 INT)`) - tk.MustExec(`SELECT t0.c0 FROM t0 NATURAL RIGHT JOIN t1 WHERE t1.c0`) - - // For issue 20958 - tk.MustExec(`DROP TABLE if exists t1, t2`) - tk.MustExec(`create table t1(id int, name varchar(20));`) - tk.MustExec(`create table t2(id int, address varchar(30));`) - tk.MustExec(`insert into t1 values(1,'gangshen');`) - tk.MustExec(`insert into t2 values(1,'HangZhou');`) - tk.MustQuery(`select t2.* from t1 inner join t2 using (id) limit 1;`).Check(testkit.Rows("1 HangZhou")) - tk.MustQuery(`select t2.* from t1 inner join t2 on t1.id = t2.id limit 1;`).Check(testkit.Rows("1 HangZhou")) - - // For issue 20476 - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int)") - tk.MustExec("insert into t1 (a) values(1)") - tk.MustQuery("select t1.*, t2.* from t1 join t1 t2 using(a)").Check(testkit.Rows("1 1")) - tk.MustQuery("select * from t1 join t1 t2 using(a)").Check(testkit.Rows("1")) - - // For issue 18992 - tk.MustExec("drop table t") - tk.MustExec("CREATE TABLE t ( a varchar(55) NOT NULL, b varchar(55) NOT NULL, c int(11) DEFAULT NULL, d int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustExec("update t t1 join t t2 using(a,b) set t1.c=t2.d;") - - // For issue 20467 - tk.MustExec(`DROP TABLE if exists t1,t2,t3,t4,t5`) - tk.MustExec(`CREATE TABLE t1 (a INT, b INT)`) - tk.MustExec(`CREATE TABLE t2 (a INT, b INT)`) - tk.MustExec(`CREATE TABLE t3 (a INT, b INT)`) - tk.MustExec(`INSERT INTO t1 VALUES (1,1)`) - tk.MustExec(`INSERT INTO t2 VALUES (1,1)`) - tk.MustExec(`INSERT INTO t3 VALUES (1,1)`) - tk.MustGetErrMsg(`SELECT * FROM t1 JOIN (t2 JOIN t3 USING (b)) USING (a)`, "[planner:1052]Column 'a' in from clause is ambiguous") - - // For issue 6712 - tk.MustExec("drop table if exists t1,t2") - tk.MustExec("create table t1 (t1 int , t0 int)") - tk.MustExec("create table t2 (t2 int, t0 int)") - tk.MustExec("insert into t1 select 11, 1") - tk.MustExec("insert into t2 select 22, 1") - tk.MustQuery("select t1.t0, t2.t0 from t1 join t2 using(t0) group by t1.t0").Check(testkit.Rows("1 1")) - tk.MustQuery("select t1.t0, t2.t0 from t1 join t2 using(t0) having t1.t0 > 0").Check(testkit.Rows("1 1")) -} - -func TestSubquery(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_hash_join_concurrency=1") - tk.MustExec("set @@tidb_hashagg_partial_concurrency=1") - tk.MustExec("set @@tidb_hashagg_final_concurrency=1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c int, d int)") - tk.MustExec("insert t values (1, 1)") - tk.MustExec("insert t values (2, 2)") - tk.MustExec("insert t values (3, 4)") - tk.MustExec("commit") - - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - - result := tk.MustQuery("select * from t where exists(select * from t k where t.c = k.c having sum(c) = 1)") - result.Check(testkit.Rows("1 1")) - result = tk.MustQuery("select * from t where exists(select k.c, k.d from t k, t p where t.c = k.d)") - result.Check(testkit.Rows("1 1", "2 2")) - result = tk.MustQuery("select 1 = (select count(*) from t where t.c = k.d) from t k") - result.Check(testkit.Rows("1", "1", "0")) - result = tk.MustQuery("select 1 = (select count(*) from t where exists( select * from t m where t.c = k.d)) from t k") - result.Sort().Check(testkit.Rows("0", "1", "1")) - result = tk.MustQuery("select t.c = any (select count(*) from t) from t") - result.Sort().Check(testkit.Rows("0", "0", "1")) - result = tk.MustQuery("select * from t where (t.c, 6) = any (select count(*), sum(t.c) from t)") - result.Check(testkit.Rows("3 4")) - result = tk.MustQuery("select t.c from t where (t.c) < all (select count(*) from t)") - result.Check(testkit.Rows("1", "2")) - result = tk.MustQuery("select t.c from t where (t.c, t.d) = any (select * from t)") - result.Sort().Check(testkit.Rows("1", "2", "3")) - result = tk.MustQuery("select t.c from t where (t.c, t.d) != all (select * from t)") - result.Check(testkit.Rows()) - result = tk.MustQuery("select (select count(*) from t where t.c = k.d) from t k") - result.Sort().Check(testkit.Rows("0", "1", "1")) - result = tk.MustQuery("select t.c from t where (t.c, t.d) in (select * from t)") - result.Sort().Check(testkit.Rows("1", "2", "3")) - result = tk.MustQuery("select t.c from t where (t.c, t.d) not in (select * from t)") - result.Check(testkit.Rows()) - result = tk.MustQuery("select * from t A inner join t B on A.c = B.c and A.c > 100") - result.Check(testkit.Rows()) - // = all empty set is true - result = tk.MustQuery("select t.c from t where (t.c, t.d) != all (select * from t where d > 1000)") - result.Sort().Check(testkit.Rows("1", "2", "3")) - result = tk.MustQuery("select t.c from t where (t.c) < any (select c from t where d > 1000)") - result.Check(testkit.Rows()) - tk.MustExec("insert t values (NULL, NULL)") - result = tk.MustQuery("select (t.c) < any (select c from t) from t") - result.Sort().Check(testkit.Rows("1", "1", "", "")) - result = tk.MustQuery("select (10) > all (select c from t) from t") - result.Check(testkit.Rows("", "", "", "")) - result = tk.MustQuery("select (c) > all (select c from t) from t") - result.Check(testkit.Rows("0", "0", "0", "")) - - tk.MustExec("drop table if exists a") - tk.MustExec("create table a (c int, d int)") - tk.MustExec("insert a values (1, 2)") - tk.MustExec("drop table if exists b") - tk.MustExec("create table b (c int, d int)") - tk.MustExec("insert b values (2, 1)") - - result = tk.MustQuery("select * from a b where c = (select d from b a where a.c = 2 and b.c = 1)") - result.Check(testkit.Rows("1 2")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(c int)") - tk.MustExec("insert t values(10), (8), (7), (9), (11)") - result = tk.MustQuery("select * from t where 9 in (select c from t s where s.c < t.c limit 3)") - result.Check(testkit.Rows("10")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int, v int)") - tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") - result = tk.MustQuery("select * from t where v=(select min(t1.v) from t t1, t t2, t t3 where t1.id=t2.id and t2.id=t3.id and t1.id=t.id)") - result.Check(testkit.Rows("1 1", "2 2", "3 3")) - - result = tk.MustQuery("select exists (select t.id from t where s.id < 2 and t.id = s.id) from t s") - result.Sort().Check(testkit.Rows("0", "0", "1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(c int)") - result = tk.MustQuery("select exists(select count(*) from t)") - result.Check(testkit.Rows("1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int primary key, v int)") - tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") - result = tk.MustQuery("select (select t.id from t where s.id < 2 and t.id = s.id) from t s") - result.Sort().Check(testkit.Rows("1", "", "")) - rs, err := tk.Exec("select (select t.id from t where t.id = t.v and t.v != s.id) from t s") - require.NoError(t, err) - _, err = session.GetRows4Test(context.Background(), tk.Session(), rs) - require.Error(t, err) - require.NoError(t, rs.Close()) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists s") - tk.MustExec("create table t(id int)") - tk.MustExec("create table s(id int)") - tk.MustExec("insert into t values(1), (2)") - tk.MustExec("insert into s values(2), (2)") - result = tk.MustQuery("select id from t where(select count(*) from s where s.id = t.id) > 0") - result.Check(testkit.Rows("2")) - result = tk.MustQuery("select *, (select count(*) from s where id = t.id limit 1, 1) from t") - result.Check(testkit.Rows("1 ", "2 ")) - - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists s") - tk.MustExec("create table t(id int primary key)") - tk.MustExec("create table s(id int)") - tk.MustExec("insert into t values(1), (2)") - tk.MustExec("insert into s values(2), (2)") - result = tk.MustQuery("select *, (select count(id) from s where id = t.id) from t") - result.Check(testkit.Rows("1 0", "2 2")) - result = tk.MustQuery("select *, 0 < any (select count(id) from s where id = t.id) from t") - result.Check(testkit.Rows("1 0", "2 1")) - result = tk.MustQuery("select (select count(*) from t k where t.id = id) from s, t where t.id = s.id limit 1") - result.Check(testkit.Rows("1")) - - tk.MustExec("drop table if exists t, s") - tk.MustExec("create table t(id int primary key)") - tk.MustExec("create table s(id int, index k(id))") - tk.MustExec("insert into t values(1), (2)") - tk.MustExec("insert into s values(2), (2)") - result = tk.MustQuery("select (select id from s where s.id = t.id order by s.id limit 1) from t") - result.Check(testkit.Rows("", "2")) - - tk.MustExec("drop table if exists t, s") - tk.MustExec("create table t(id int)") - tk.MustExec("create table s(id int)") - tk.MustExec("insert into t values(2), (2)") - tk.MustExec("insert into s values(2)") - result = tk.MustQuery("select (select id from s where s.id = t.id order by s.id) from t") - result.Check(testkit.Rows("2", "2")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(dt datetime)") - result = tk.MustQuery("select (select 1 from t where DATE_FORMAT(o.dt,'%Y-%m')) from t o") - result.Check(testkit.Rows()) - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(f1 int, f2 int)") - tk.MustExec("create table t2(fa int, fb int)") - tk.MustExec("insert into t1 values (1,1),(1,1),(1,2),(1,2),(1,2),(1,3)") - tk.MustExec("insert into t2 values (1,1),(1,2),(1,3)") - result = tk.MustQuery("select f1,f2 from t1 group by f1,f2 having count(1) >= all (select fb from t2 where fa = f1)") - result.Check(testkit.Rows("1 2")) - - tk.MustExec("DROP TABLE IF EXISTS t1, t2") - tk.MustExec("CREATE TABLE t1(a INT)") - tk.MustExec("CREATE TABLE t2 (d BINARY(2), PRIMARY KEY (d(1)), UNIQUE KEY (d))") - tk.MustExec("INSERT INTO t1 values(1)") - result = tk.MustQuery("SELECT 1 FROM test.t1, test.t2 WHERE 1 = (SELECT test.t2.d FROM test.t2 WHERE test.t1.a >= 1) and test.t2.d = 1;") - result.Check(testkit.Rows()) - - tk.MustExec("DROP TABLE IF EXISTS t1") - tk.MustExec("CREATE TABLE t1(a int, b int default 0)") - tk.MustExec("create index k1 on t1(a)") - tk.MustExec("INSERT INTO t1 (a) values(1), (2), (3), (4), (5)") - result = tk.MustQuery("select (select /*+ INL_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") - result.Check(testkit.Rows("1", "2", "3", "4", "5")) - result = tk.MustQuery("select (select /*+ INL_HASH_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") - result.Check(testkit.Rows("1", "2", "3", "4", "5")) - result = tk.MustQuery("select (select /*+ INL_MERGE_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") - result.Check(testkit.Rows("1", "2", "3", "4", "5")) - - // test left outer semi join & anti left outer semi join - tk.MustQuery("select 1 from (select t1.a in (select t1.a from t1) from t1) x;").Check(testkit.Rows("1", "1", "1", "1", "1")) - tk.MustQuery("select 1 from (select t1.a not in (select t1.a from t1) from t1) x;").Check(testkit.Rows("1", "1", "1", "1", "1")) - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int)") - tk.MustExec("create table t2(b int)") - tk.MustExec("insert into t1 values(1)") - tk.MustExec("insert into t2 values(1)") - tk.MustQuery("select * from t1 where a in (select a from t2)").Check(testkit.Rows("1")) - - tk.MustExec("insert into t2 value(null)") - tk.MustQuery("select * from t1 where 1 in (select b from t2)").Check(testkit.Rows("1")) - tk.MustQuery("select * from t1 where 1 not in (select b from t2)").Check(testkit.Rows()) - tk.MustQuery("select * from t1 where 2 not in (select b from t2)").Check(testkit.Rows()) - tk.MustQuery("select * from t1 where 2 in (select b from t2)").Check(testkit.Rows()) - tk.MustQuery("select 1 in (select b from t2) from t1").Check(testkit.Rows("1")) - tk.MustQuery("select 1 in (select 1 from t2) from t1").Check(testkit.Rows("1")) - tk.MustQuery("select 1 not in (select b from t2) from t1").Check(testkit.Rows("0")) - tk.MustQuery("select 1 not in (select 1 from t2) from t1").Check(testkit.Rows("0")) - - tk.MustExec("delete from t2 where b=1") - tk.MustQuery("select 1 in (select b from t2) from t1").Check(testkit.Rows("")) - tk.MustQuery("select 1 not in (select b from t2) from t1").Check(testkit.Rows("")) - tk.MustQuery("select 1 not in (select 1 from t2) from t1").Check(testkit.Rows("0")) - tk.MustQuery("select 1 in (select 1 from t2) from t1").Check(testkit.Rows("1")) - tk.MustQuery("select 1 not in (select null from t1) from t2").Check(testkit.Rows("")) - tk.MustQuery("select 1 in (select null from t1) from t2").Check(testkit.Rows("")) - - tk.MustExec("drop table if exists s") - tk.MustExec("create table s(a int not null, b int)") - tk.MustExec("set sql_mode = ''") - tk.MustQuery("select (2,0) in (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) not in (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) = any (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) != all (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) in (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) not in (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) = any (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustQuery("select (2,0) != all (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) - tk.MustExec("insert into s values(1,null)") - tk.MustQuery("select 1 in (select b from s)").Check(testkit.Rows("")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1),(null)") - tk.MustQuery("select a not in (select 1) from t").Sort().Check(testkit.Rows( - "0", - "", - )) - tk.MustQuery("select 1 not in (select null from t t1) from t").Check(testkit.Rows( - "", - "", - )) - tk.MustQuery("select 1 in (select null from t t1) from t").Check(testkit.Rows( - "", - "", - )) - tk.MustQuery("select a in (select 0) xx from (select null as a) x").Check(testkit.Rows("")) - - tk.MustExec("drop table t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,null),(null, null),(null, 2)") - tk.MustQuery("select * from t t1 where (2 in (select a from t t2 where (t2.b=t1.b) is null))").Check(testkit.Rows()) - tk.MustQuery("select (t2.a in (select t1.a from t t1)) is true from t t2").Sort().Check(testkit.Rows( - "0", - "0", - "1", - )) - - tk.MustExec("set @@tidb_hash_join_concurrency=5") -} - -func TestJoinLeak(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_hash_join_concurrency=1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (d int)") - tk.MustExec("begin") - for i := 0; i < 1002; i++ { - tk.MustExec("insert t values (1)") - } - tk.MustExec("commit") - result, err := tk.Exec("select * from t t1 left join (select 1) t2 on 1") - require.NoError(t, err) - req := result.NewChunk(nil) - err = result.Next(context.Background(), req) - require.NoError(t, err) - time.Sleep(time.Millisecond) - require.NoError(t, result.Close()) - - tk.MustExec("set @@tidb_hash_join_concurrency=5") -} - -func TestNullEmptyAwareSemiJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, index idx_a(a), index idb_b(b), index idx_c(c))") - tk.MustExec("insert into t values(null, 1, 0), (1, 2, 0)") - tests := []struct { - sql string - }{ - { - "a, b from t t1 where a not in (select b from t t2)", - }, - { - "a, b from t t1 where a not in (select b from t t2 where t1.b = t2.a)", - }, - { - "a, b from t t1 where a not in (select a from t t2)", - }, - { - "a, b from t t1 where a not in (select a from t t2 where t1.b = t2.b)", - }, - { - "a, b from t t1 where a != all (select b from t t2)", - }, - { - "a, b from t t1 where a != all (select b from t t2 where t1.b = t2.a)", - }, - { - "a, b from t t1 where a != all (select a from t t2)", - }, - { - "a, b from t t1 where a != all (select a from t t2 where t1.b = t2.b)", - }, - { - "a, b from t t1 where not exists (select * from t t2 where t1.a = t2.b)", - }, - { - "a, b from t t1 where not exists (select * from t t2 where t1.a = t2.a)", - }, - } - results := []struct { - result [][]interface{} - }{ - { - testkit.Rows(), - }, - { - testkit.Rows("1 2"), - }, - { - testkit.Rows(), - }, - { - testkit.Rows(), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 2"), - }, - { - testkit.Rows(), - }, - { - testkit.Rows(), - }, - { - testkit.Rows(" 1"), - }, - { - testkit.Rows(" 1"), - }, - } - hints := [5]string{ - "/*+ HASH_JOIN(t1, t2) */", - "/*+ MERGE_JOIN(t1, t2) */", - "/*+ INL_JOIN(t1, t2) */", - "/*+ INL_HASH_JOIN(t1, t2) */", - "/*+ INL_MERGE_JOIN(t1, t2) */", - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(1, null, 0), (2, 1, 0)") - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows("2 1"), - }, - { - testkit.Rows(), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(1, null, 0), (2, 1, 0), (null, 2, 0)") - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(), - }, - { - testkit.Rows("1 "), - }, - { - testkit.Rows(" 2"), - }, - { - testkit.Rows(" 2"), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(1, null, 0), (2, null, 0)") - tests = []struct { - sql string - }{ - { - "a, b from t t1 where b not in (select a from t t2)", - }, - } - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows(), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(null, 1, 1), (2, 2, 2), (3, null, 3), (4, 4, 3)") - tests = []struct { - sql string - }{ - { - "a, b, a not in (select b from t t2) from t t1 order by a", - }, - { - "a, c, a not in (select c from t t2) from t t1 order by a", - }, - { - "a, b, a in (select b from t t2) from t t1 order by a", - }, - { - "a, c, a in (select c from t t2) from t t1 order by a", - }, - } - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows( - " 1 ", - "2 2 0", - "3 ", - "4 4 0", - ), - }, - { - testkit.Rows( - " 1 ", - "2 2 0", - "3 3 0", - "4 3 1", - ), - }, - { - testkit.Rows( - " 1 ", - "2 2 1", - "3 ", - "4 4 1", - ), - }, - { - testkit.Rows( - " 1 ", - "2 2 1", - "3 3 1", - "4 3 0", - ), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("drop table if exists s") - tk.MustExec("create table s(a int, b int)") - tk.MustExec("insert into s values(1, 2)") - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(null, null, 0)") - tests = []struct { - sql string - }{ - { - "a in (select b from t t2 where t2.a = t1.b) from s t1", - }, - { - "a in (select b from s t2 where t2.a = t1.b) from t t1", - }, - } - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows("0"), - }, - { - testkit.Rows("0"), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table s") - tk.MustExec("insert into s values(2, 2)") - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(null, 1, 0)") - tests = []struct { - sql string - }{ - { - "a in (select a from s t2 where t2.b = t1.b) from t t1", - }, - { - "a in (select a from s t2 where t2.b < t1.b) from t t1", - }, - } - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows("0"), - }, - { - testkit.Rows("0"), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("truncate table s") - tk.MustExec("insert into s values(null, 2)") - tk.MustExec("truncate table t") - tk.MustExec("insert into t values(1, 1, 0)") - tests = []struct { - sql string - }{ - { - "a in (select a from s t2 where t2.b = t1.b) from t t1", - }, - { - "b in (select a from s t2) from t t1", - }, - { - "* from t t1 where a not in (select a from s t2 where t2.b = t1.b)", - }, - { - "* from t t1 where a not in (select a from s t2)", - }, - { - "* from s t1 where a not in (select a from t t2)", - }, - } - results = []struct { - result [][]interface{} - }{ - { - testkit.Rows("0"), - }, - { - testkit.Rows(""), - }, - { - testkit.Rows("1 1 0"), - }, - { - testkit.Rows(), - }, - { - testkit.Rows(), - }, - } - for i, tt := range tests { - for _, hint := range hints { - sql := fmt.Sprintf("select %s %s", hint, tt.sql) - result := tk.MustQuery(sql) - result.Check(results[i].result) - } - } - - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int)") - tk.MustExec("create table t2(a int)") - tk.MustExec("insert into t1 values(1),(2)") - tk.MustExec("insert into t2 values(1),(null)") - tk.MustQuery("select * from t1 where a not in (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( - "2", - )) - tk.MustQuery("select * from t1 where a != all (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( - "2", - )) - tk.MustQuery("select * from t1 where a <> all (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( - "2", - )) -} - -func TestIssue18070(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") - tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, index(a))") - tk.MustExec("create table t2(a int, index(a))") - tk.MustExec("insert into t1 values(1),(2)") - tk.MustExec("insert into t2 values(1),(1),(2),(2)") - tk.MustExec("set @@tidb_mem_quota_query=1000") - tk.MustContainErrMsg("select /*+ inl_hash_join(t1)*/ * from t1 join t2 on t1.a = t2.a;", memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) - - fpName := "github.com/pingcap/tidb/executor/mockIndexMergeJoinOOMPanic" - require.NoError(t, failpoint.Enable(fpName, `panic("ERROR 1105 (HY000): Out Of Memory Quota![conn=1]")`)) - defer func() { - require.NoError(t, failpoint.Disable(fpName)) - }() - tk.MustContainErrMsg("select /*+ inl_merge_join(t1)*/ * from t1 join t2 on t1.a = t2.a;", memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) -} - -func TestIssue20779(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b int, index idx(b));") - tk.MustExec("insert into t1 values(1, 1);") - tk.MustExec("insert into t1 select * from t1;") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIssue20779", "return")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIssue20779")) - }() - - rs, err := tk.Exec("select /*+ inl_hash_join(t2) */ t1.b from t1 left join t1 t2 on t1.b=t2.b order by t1.b;") - require.NoError(t, err) - _, err = session.GetRows4Test(context.Background(), nil, rs) - require.EqualError(t, err, "testIssue20779") - require.NoError(t, rs.Close()) -} - -func TestIssue30211(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2;") - tk.MustExec("create table t1(a int, index(a));") - tk.MustExec("create table t2(a int, index(a));") - func() { - fpName := "github.com/pingcap/tidb/executor/TestIssue30211" - require.NoError(t, failpoint.Enable(fpName, `panic("TestIssue30211 IndexJoinPanic")`)) - defer func() { - require.NoError(t, failpoint.Disable(fpName)) - }() - err := tk.QueryToErr("select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.a;") - require.EqualError(t, err, "failpoint panic: TestIssue30211 IndexJoinPanic") - - err = tk.QueryToErr("select /*+ inl_hash_join(t1) */ * from t1 join t2 on t1.a = t2.a;") - require.EqualError(t, err, "failpoint panic: TestIssue30211 IndexJoinPanic") - }() - tk.MustExec("insert into t1 values(1),(2);") - tk.MustExec("insert into t2 values(1),(1),(2),(2);") - tk.MustExec("set @@tidb_mem_quota_query=8000;") - tk.MustExec("set tidb_index_join_batch_size = 1;") - tk.MustExec("SET GLOBAL tidb_mem_oom_action = 'CANCEL'") - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action='LOG'") - err := tk.QueryToErr("select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.a;").Error() - require.True(t, strings.Contains(err, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery)) - err = tk.QueryToErr("select /*+ inl_hash_join(t1) */ * from t1 join t2 on t1.a = t2.a;").Error() - require.True(t, strings.Contains(err, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery)) -} - -func TestIssue37932(t *testing.T) { - store := testkit.CreateMockStore(t) - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - tk1.MustExec("create table tbl_1 ( col_1 set ( 'Alice','Bob','Charlie','David' ) not null default 'Alice' ,col_2 tinyint unsigned ,col_3 decimal ( 34 , 3 ) not null default 79 ,col_4 bigint unsigned not null ,col_5 bit ( 12 ) not null , unique key idx_1 ( col_2 ) ,unique key idx_2 ( col_2 ) ) charset utf8mb4 collate utf8mb4_bin ;") - tk1.MustExec("create table tbl_2 ( col_6 text ( 52 ) collate utf8_unicode_ci not null ,col_7 int unsigned not null ,col_8 blob ( 369 ) ,col_9 bit ( 51 ) ,col_10 decimal ( 38 , 16 ) , unique key idx_3 ( col_7 ) ,unique key idx_4 ( col_7 ) ) charset utf8 collate utf8_unicode_ci ;") - tk1.MustExec("create table tbl_3 ( col_11 set ( 'Alice','Bob','Charlie','David' ) not null ,col_12 bigint unsigned not null default 1678891638492596595 ,col_13 text ( 18 ) ,col_14 set ( 'Alice','Bob','Charlie','David' ) not null default 'Alice' ,col_15 mediumint , key idx_5 ( col_12 ) ,unique key idx_6 ( col_12 ) ) charset utf8mb4 collate utf8mb4_general_ci ;") - tk1.MustExec("create table tbl_4 ( col_16 set ( 'Alice','Bob','Charlie','David' ) not null ,col_17 tinyint unsigned ,col_18 int unsigned not null default 4279145838 ,col_19 varbinary ( 210 ) not null ,col_20 timestamp , primary key ( col_18 ) /*T![clustered_index] nonclustered */ ,key idx_8 ( col_19 ) ) charset utf8mb4 collate utf8mb4_unicode_ci ;") - tk1.MustExec("create table tbl_5 ( col_21 bigint ,col_22 set ( 'Alice','Bob','Charlie','David' ) ,col_23 blob ( 311 ) ,col_24 bigint unsigned not null default 3415443099312152509 ,col_25 time , unique key idx_9 ( col_21 ) ,unique key idx_10 ( col_21 ) ) charset gbk collate gbk_bin ;") - tk1.MustExec("insert into tbl_1 values ( 'Bob',null,0.04,2650749963804575036,4044 );") - tk1.MustExec("insert into tbl_1 values ( 'Alice',171,1838.2,6452757231340518222,1190 );") - tk1.MustExec("insert into tbl_1 values ( 'Bob',202,2.962,4304284252076747481,2112 );") - tk1.MustExec("insert into tbl_1 values ( 'David',155,32610.05,5899651588546531414,104 );") - tk1.MustExec("insert into tbl_1 values ( 'Charlie',52,4219.7,6151233689319516187,1246 );") - tk1.MustExec("insert into tbl_1 values ( 'Bob',55,3963.11,3614977408465893392,1188 );") - tk1.MustExec("insert into tbl_1 values ( 'Alice',203,72.01,1553550133494908281,1658 );") - tk1.MustExec("insert into tbl_1 values ( 'Bob',40,871.569,8114062926218465773,1397 );") - tk1.MustExec("insert into tbl_1 values ( 'Alice',165,7765,4481202107781982005,2089 );") - tk1.MustExec("insert into tbl_1 values ( 'David',79,7.02,993594504887208796,514 );") - tk1.MustExec("insert into tbl_2 values ( 'iB_%7c&q!6-gY4bkvg',2064909882,'dLN52t1YZSdJ',2251679806445488,32 );") - tk1.MustExec("insert into tbl_2 values ( 'h_',1478443689,'EqP+iN=',180492371752598,0.1 );") - tk1.MustExec("insert into tbl_2 values ( 'U@U&*WKfPzil=6YaDxp',4271201457,'QWuo24qkSSo',823931105457505,88514 );") - tk1.MustExec("insert into tbl_2 values ( 'FR4GA=',505128825,'RpEmV6ph5Z7',568030123046798,609381 );") - tk1.MustExec("insert into tbl_2 values ( '3GsU',166660047,'',1061132816887762,6.4605 );") - tk1.MustExec("insert into tbl_2 values ( 'BA4hPRD0lm*pbg#NE',3440634757,'7gUPe2',288001159469205,6664.9 );") - tk1.MustExec("insert into tbl_2 values ( '+z',2117152318,'WTkD(N',215697667226264,7.88 );") - tk1.MustExec("insert into tbl_2 values ( 'x@SPhy9lOomPa4LF',2881759652,'ETUXQQ0b4HnBSKgTWIU',153379720424625,null );") - tk1.MustExec("insert into tbl_2 values ( '',2075177391,'MPae!9%ufd',115899580476733,341.23 );") - tk1.MustExec("insert into tbl_2 values ( '~udi',1839363347,'iQj$$YsZc5ULTxG)yH',111454353417190,6.6 );") - tk1.MustExec("insert into tbl_3 values ( 'Alice',7032411265967085555,'P7*KBZ159','Alice',7516989 );") - tk1.MustExec("insert into tbl_3 values ( 'David',486417871670147038,'','Charlie',-2135446 );") - tk1.MustExec("insert into tbl_3 values ( 'Charlie',5784081664185069254,'7V_&YzKM~Q','Charlie',5583839 );") - tk1.MustExec("insert into tbl_3 values ( 'David',6346366522897598558,')Lp&$2)SC@','Bob',2522913 );") - tk1.MustExec("insert into tbl_3 values ( 'Charlie',224922711063053272,'gY','David',6624398 );") - tk1.MustExec("insert into tbl_3 values ( 'Alice',4678579167560495958,'fPIXY%R8WyY(=u&O','David',-3267160 );") - tk1.MustExec("insert into tbl_3 values ( 'David',8817108026311573677,'Cs0dZW*SPnKhV1','Alice',2359718 );") - tk1.MustExec("insert into tbl_3 values ( 'Bob',3177426155683033662,'o2=@zv2qQDhKUs)4y','Bob',-8091802 );") - tk1.MustExec("insert into tbl_3 values ( 'Bob',2543586640437235142,'hDa*CsOUzxmjf2m','Charlie',-8091935 );") - tk1.MustExec("insert into tbl_3 values ( 'Charlie',6204182067887668945,'DX-!=)dbGPQO','David',-1954600 );") - tk1.MustExec("insert into tbl_4 values ( 'David',167,576262750,'lX&x04W','2035-09-28' );") - tk1.MustExec("insert into tbl_4 values ( 'Charlie',236,2637776757,'92OhsL!w%7','2036-02-08' );") - tk1.MustExec("insert into tbl_4 values ( 'Bob',68,1077999933,'M0l','1997-09-16' );") - tk1.MustExec("insert into tbl_4 values ( 'Charlie',184,1280264753,'FhjkfeXsK1Q(','2030-03-16' );") - tk1.MustExec("insert into tbl_4 values ( 'Alice',10,2150711295,'Eqip)^tr*MoL','2032-07-02' );") - tk1.MustExec("insert into tbl_4 values ( 'Bob',108,2421602476,'Eul~~Df_Q8s&I3Y-7','2019-06-10' );") - tk1.MustExec("insert into tbl_4 values ( 'Alice',36,2811198561,'%XgRou0#iKtn*','2022-06-13' );") - tk1.MustExec("insert into tbl_4 values ( 'Charlie',115,330972286,'hKeJS','2000-11-15' );") - tk1.MustExec("insert into tbl_4 values ( 'Alice',6,2958326555,'c6+=1','2001-02-11' );") - tk1.MustExec("insert into tbl_4 values ( 'Alice',99,387404826,'figc(@9R*k3!QM_Vve','2036-02-17' );") - tk1.MustExec("insert into tbl_5 values ( -401358236474313609,'Charlie','4J$',701059766304691317,'08:19:10.00' );") - tk1.MustExec("insert into tbl_5 values ( 2759837898825557143,'Bob','E',5158554038674310466,'11:04:03.00' );") - tk1.MustExec("insert into tbl_5 values ( 273910054423832204,'Alice',null,8944547065167499612,'08:02:30.00' );") - tk1.MustExec("insert into tbl_5 values ( 2875669873527090798,'Alice','4^SpR84',4072881341903432150,'18:24:55.00' );") - tk1.MustExec("insert into tbl_5 values ( -8446590100588981557,'David','yBj8',8760380566452862549,'09:01:10.00' );") - tk1.MustExec("insert into tbl_5 values ( -1075861460175889441,'Charlie','ti11Pl0lJ',9139997565676405627,'08:30:14.00' );") - tk1.MustExec("insert into tbl_5 values ( 95663565223131772,'Alice','6$',8467839300407531400,'23:31:42.00' );") - tk1.MustExec("insert into tbl_5 values ( -5661709703968335255,'Charlie','',8122758569495329946,'19:36:24.00' );") - tk1.MustExec("insert into tbl_5 values ( 3338588216091909518,'Bob','',6558557574025196860,'15:22:56.00' );") - tk1.MustExec("insert into tbl_5 values ( 8918630521194612922,'David','I$w',5981981639362947650,'22:03:24.00' );") - tk1.MustExec("begin pessimistic;") - tk1.MustExec("insert ignore into tbl_1 set col_1 = 'David', col_2 = 110, col_3 = 37065, col_4 = 8164500960513474805, col_5 = 1264 on duplicate key update col_3 = 22151.5, col_4 = 6266058887081523571, col_5 = 3254, col_2 = 59, col_1 = 'Bob';") - tk1.MustExec("insert into tbl_4 (col_16,col_17,col_18,col_19,col_20) values ( 'Charlie',34,2499970462,'Z','1978-10-27' ) ,( 'David',217,1732485689,'*)~@@Q8ryi','2004-12-01' ) ,( 'Charlie',40,1360558255,'H(Y','1998-06-25' ) ,( 'Alice',108,2973455447,'%CcP4$','1979-03-28' ) ,( 'David',9,3835209932,'tdKXUzLmAzwFf$','2009-03-03' ) ,( 'David',68,163270003,'uimsclz@FQJN','1988-09-11' ) ,( 'Alice',76,297067264,'BzFF','1989-01-05' ) on duplicate key update col_16 = 'Charlie', col_17 = 14, col_18 = 4062155275, col_20 = '2002-03-07', col_19 = 'tmvchLzp*o8';") - tk2.MustExec("delete from tbl_3 where tbl_3.col_13 in ( null ,'' ,'g8EEzUU7LQ' ,'~fC3&B*cnOOx_' ,'%RF~AFto&x' ,'NlWkMWG^00' ,'e^4o2Ji^q_*Fa52Z' ) ;") - tk2.MustExec("delete from tbl_5 where not( tbl_5.col_21 between -1075861460175889441 and 3338588216091909518 ) ;") - tk1.MustExec("replace into tbl_1 (col_1,col_2,col_3,col_4,col_5) values ( 'Alice',83,8.33,4070808626051569664,455 ) ,( 'Alice',53,2.8,2763362085715461014,1912 ) ,( 'David',178,4242.8,962727993466011464,1844 ) ,( 'Alice',16,650054,5638988670318229867,565 ) ,( 'Alice',76,89783.1,3968605744540056024,2563 ) ,( 'Bob',120,0.89,1003144931151245839,2670 );") - tk1.MustExec("delete from tbl_5 where col_24 is null ;") - tk1.MustExec("delete from tbl_3 where tbl_3.col_11 in ( 'Alice' ,'Bob' ,'Alice' ) ;") - tk2.MustExec("insert into tbl_3 set col_11 = 'Bob', col_12 = 5701982550256146475, col_13 = 'Hhl)yCsQ2K3cfc^', col_14 = 'Alice', col_15 = -3718868 on duplicate key update col_15 = 7210750, col_12 = 6133680876296985245, col_14 = 'Alice', col_11 = 'David', col_13 = 'F+RMGE!_2^Cfr3Fw';") - tk2.MustExec("insert ignore into tbl_5 set col_21 = 2439343116426563397, col_22 = 'Charlie', col_23 = '~Spa2YzRFFom16XD', col_24 = 5571575017340582365, col_25 = '13:24:38.00' ;") - err := tk1.ExecToErr("update tbl_4 set tbl_4.col_20 = '2006-01-24' where tbl_4.col_18 in ( select col_11 from tbl_3 where IsNull( tbl_4.col_16 ) or not( tbl_4.col_19 in ( select col_3 from tbl_1 where tbl_4.col_16 between 'Alice' and 'David' and tbl_4.col_19 <= '%XgRou0#iKtn*' ) ) ) ;") - if err != nil { - print(err.Error()) - if strings.Contains(err.Error(), "Truncated incorrect DOUBLE value") { - t.Log("Truncated incorrect DOUBLE value is within expectations, skipping") - return - } - } - require.NoError(t, err) -} diff --git a/executor/test/jointest/main_test.go b/executor/test/jointest/main_test.go deleted file mode 100644 index b97030790cc88..0000000000000 --- a/executor/test/jointest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package jointest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/kvtest/BUILD.bazel b/executor/test/kvtest/BUILD.bazel deleted file mode 100644 index c746c6013029f..0000000000000 --- a/executor/test/kvtest/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "kvtest_test", - timeout = "short", - srcs = [ - "kv_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - deps = [ - "//config", - "//meta/autoid", - "//testkit", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/kvtest/main_test.go b/executor/test/kvtest/main_test.go deleted file mode 100644 index 20ca5537d3627..0000000000000 --- a/executor/test/kvtest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package kvtest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/loaddatatest/BUILD.bazel b/executor/test/loaddatatest/BUILD.bazel deleted file mode 100644 index 0f2a18a7e83f6..0000000000000 --- a/executor/test/loaddatatest/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "loaddatatest_test", - timeout = "short", - srcs = [ - "load_data_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 10, - deps = [ - "//br/pkg/lightning/mydump", - "//config", - "//executor", - "//meta/autoid", - "//sessionctx", - "//testkit", - "//util/dbterror/exeerrors", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/loaddatatest/main_test.go b/executor/test/loaddatatest/main_test.go deleted file mode 100644 index 8fcabff4d980f..0000000000000 --- a/executor/test/loaddatatest/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package loaddatatest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.Cleanup(func(_ int) { - view.Stop() - }), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/loadremotetest/BUILD.bazel b/executor/test/loadremotetest/BUILD.bazel deleted file mode 100644 index 5a592dfce5d21..0000000000000 --- a/executor/test/loadremotetest/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "loadremotetest_test", - timeout = "short", - srcs = [ - "error_test.go", - "main_test.go", - "multi_file_test.go", - "one_csv_test.go", - "util_test.go", - ], - flaky = True, - deps = [ - "//kv", - "//parser/terror", - "//testkit", - "@com_github_fsouza_fake_gcs_server//fakestorage", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@com_github_stretchr_testify//suite", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/loadremotetest/error_test.go b/executor/test/loadremotetest/error_test.go deleted file mode 100644 index beb941ed96470..0000000000000 --- a/executor/test/loadremotetest/error_test.go +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package loadremotetest - -import ( - "fmt" - "testing" - - "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func checkClientErrorMessage(t *testing.T, err error, msg string) { - require.Error(t, err) - cause := errors.Cause(err) - terr, ok := cause.(*errors.Error) - require.True(t, ok, "%T", cause) - require.Contains(t, terror.ToSQLError(terr).Error(), msg) -} - -func (s *mockGCSSuite) TestErrorMessage() { - s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") - - err := s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t") - checkClientErrorMessage(s.T(), err, "ERROR 1046 (3D000): No database selected") - err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE wrongdb.t") - checkClientErrorMessage(s.T(), err, "ERROR 1146 (42S02): Table 'wrongdb.t' doesn't exist") - - s.tk.MustExec("CREATE DATABASE load_csv;") - s.tk.MustExec("USE load_csv;") - s.tk.MustExec("CREATE TABLE t (i INT PRIMARY KEY, s varchar(32));") - - err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (wrong)") - checkClientErrorMessage(s.T(), err, "ERROR 1054 (42S22): Unknown column 'wrong' in 'field list'") - // This behaviour is different from MySQL - err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (i,i)") - checkClientErrorMessage(s.T(), err, "ERROR 1110 (42000): Column 'i' specified twice") - err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (@v) SET wrong=@v") - checkClientErrorMessage(s.T(), err, "ERROR 1054 (42S22): Unknown column 'wrong' in 'field list'") - err = s.tk.ExecToErr("LOAD DATA INFILE 'abc://1' INTO TABLE t;") - checkClientErrorMessage(s.T(), err, - "ERROR 8158 (HY000): The URI of data source is invalid. Reason: storage abc not support yet. Please provide a valid URI, such as 's3://import/test.csv?access_key_id={your_access_key_id ID}&secret_access_key={your_secret_access_key}&session_token={your_session_token}'") - err = s.tk.ExecToErr("LOAD DATA INFILE 's3://no-network' INTO TABLE t;") - checkClientErrorMessage(s.T(), err, - "ERROR 8159 (HY000): Access to the data source has been denied. Reason: failed to get region of bucket no-network. Please check the URI, access key and secret access key are correct") - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://wrong-bucket/p?endpoint=%s' - INTO TABLE t;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 8160 (HY000): Failed to read source files. Reason: the object doesn't exist, file info: input.bucket='wrong-bucket', input.key='p'. Please check the file location is correct") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "t.tsv", - }, - Content: []byte("1\t2\n" + - "1\t4\n"), - }) - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t.tsv?endpoint=%s' - INTO TABLE t LINES STARTING BY '\n';`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - `ERROR 8162 (HY000): STARTING BY ' -' cannot contain LINES TERMINATED BY ' -'`) -} - -func (s *mockGCSSuite) TestColumnNumMismatch() { - s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "t2.tsv", - }, - Content: []byte("1\t2\n" + - "1\t4\n"), - }) - - s.tk.MustExec("CREATE DATABASE load_csv;") - s.tk.MustExec("USE load_csv;") - - // table has fewer columns than data - - s.tk.MustExec("CREATE TABLE t (c INT);") - s.tk.MustExec("SET SESSION sql_mode = ''") - err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - INTO TABLE t;`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1262 Row 1 was truncated; it contained more data than there were input columns", - "Warning 1262 Row 2 was truncated; it contained more data than there were input columns")) - - s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - INTO TABLE t;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1262 (01000): Row 1 was truncated; it contained more data than there were input columns") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - REPLACE INTO TABLE t;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1262 (01000): Row 1 was truncated; it contained more data than there were input columns") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - IGNORE INTO TABLE t;`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1262 Row 1 was truncated; it contained more data than there were input columns", - "Warning 1262 Row 2 was truncated; it contained more data than there were input columns")) - - // table has more columns than data - - s.tk.MustExec("CREATE TABLE t2 (c1 INT, c2 INT, c3 INT);") - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - INTO TABLE t2;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1261 (01000): Row 1 doesn't contain data for all columns") - - // fill default value for missing columns - - s.tk.MustExec(`CREATE TABLE t3 ( - c1 INT NOT NULL, - c2 INT NOT NULL, - c3 INT NOT NULL DEFAULT 1);`) - s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' - INTO TABLE t3 (c1, c2);`, gcsEndpoint)) - s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( - "1 2 1", - "1 4 1")) -} - -func (s *mockGCSSuite) TestEvalError() { - s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "t3.tsv", - }, - Content: []byte("1\t2\n" + - "1\t4\n"), - }) - - s.tk.MustExec("CREATE DATABASE load_csv;") - s.tk.MustExec("USE load_csv;") - - s.tk.MustExec("CREATE TABLE t (c INT, c2 INT UNIQUE);") - s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' - INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'asd'") - - // REPLACE does not help - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' - REPLACE INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'asd'") - - // IGNORE helps - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' - IGNORE INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1292 Truncated incorrect DOUBLE value: 'asd'", - "Warning 1292 Truncated incorrect DOUBLE value: 'asd'")) - s.tk.MustQuery("SELECT * FROM t;").Check(testkit.Rows( - "1 2", - "1 4")) -} - -func (s *mockGCSSuite) TestDataError() { - s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "null.tsv", - }, - Content: []byte("1\t\\N\n" + - "1\t4\n"), - }) - - s.tk.MustExec("CREATE DATABASE load_csv;") - s.tk.MustExec("USE load_csv;") - - s.tk.MustExec("CREATE TABLE t (c INT NOT NULL, c2 INT NOT NULL);") - s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/null.tsv?endpoint=%s' - INTO TABLE t;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, - "ERROR 1263 (22004): Column set to default value; NULL supplied to NOT NULL column 'c2' at row 1") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/null.tsv?endpoint=%s' - IGNORE INTO TABLE t;`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 1", s.tk.Session().LastMessage()) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1263 Column set to default value; NULL supplied to NOT NULL column 'c2' at row 1")) - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "t4.tsv", - }, - Content: []byte("1\t2\n" + - "1\t2\n"), - }) - - s.tk.MustExec("CREATE TABLE t2 (c INT PRIMARY KEY, c2 INT NOT NULL);") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t4.tsv?endpoint=%s' - INTO TABLE t2;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, "ERROR 1062 (23000): Duplicate entry '1' for key 't2.PRIMARY'") - - s.tk.MustExec("CREATE TABLE t3 (c INT, c2 INT UNIQUE);") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t4.tsv?endpoint=%s' - INTO TABLE t3;`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, "ERROR 1062 (23000): Duplicate entry '2' for key 't3.c2'") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-tsv", - Name: "t5.tsv", - }, - Content: []byte("1\t100\n" + - "2\t100\n"), - }) - s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t5.tsv?endpoint=%s' - REPLACE INTO TABLE t3;`, gcsEndpoint)) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows()) - s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( - "2 100")) - - s.tk.MustExec("UPDATE t3 SET c = 3;") - s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t5.tsv?endpoint=%s' - IGNORE INTO TABLE t3;`, gcsEndpoint)) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1062 Duplicate entry '100' for key 't3.c2'", - "Warning 1062 Duplicate entry '100' for key 't3.c2'")) - s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( - "3 100")) -} - -func (s *mockGCSSuite) TestIssue43555() { - s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") - - s.server.CreateObject(fakestorage.Object{ - ObjectAttrs: fakestorage.ObjectAttrs{ - BucketName: "test-csv", - Name: "43555.csv", - }, - Content: []byte("6\n" + - "7.1\n"), - }) - - s.tk.MustExec("CREATE DATABASE load_csv;") - s.tk.MustExec("USE load_csv;") - - s.tk.MustExec("CREATE TABLE t (id CHAR(1), id1 INT);") - s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - - err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' - IGNORE INTO TABLE t;`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 3", s.tk.Session().LastMessage()) - - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1261 Row 1 doesn't contain data for all columns", - "Warning 1261 Row 2 doesn't contain data for all columns", - "Warning 1265 Data truncated for column 'id' at row 2")) - s.tk.MustQuery("SELECT * FROM t;").Check(testkit.Rows( - "6 ", - "7 ")) - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' - INTO TABLE t (id);`, gcsEndpoint)) - checkClientErrorMessage(s.T(), err, "ERROR 1265 (01000): Data truncated for column 'id' at row 2") - - err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' - IGNORE INTO TABLE t (id1) SET id='7.1';`, gcsEndpoint)) - require.NoError(s.T(), err) - require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) - s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( - "Warning 1265 Data truncated for column 'id' at row 1", - "Warning 1265 Data truncated for column 'id' at row 2")) -} diff --git a/executor/test/loadremotetest/util_test.go b/executor/test/loadremotetest/util_test.go deleted file mode 100644 index 99cd49185f6d1..0000000000000 --- a/executor/test/loadremotetest/util_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package loadremotetest - -import ( - "fmt" - "testing" - - "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/suite" -) - -type mockGCSSuite struct { - suite.Suite - - server *fakestorage.Server - store kv.Storage - tk *testkit.TestKit -} - -var ( - gcsHost = "127.0.0.1" - gcsPort = uint16(4443) - // for fake gcs server, we must use this endpoint format - // NOTE: must end with '/' - gcsEndpointFormat = "http://%s:%d/storage/v1/" - gcsEndpoint = fmt.Sprintf(gcsEndpointFormat, gcsHost, gcsPort) -) - -func TestLoadRemote(t *testing.T) { - suite.Run(t, &mockGCSSuite{}) -} - -func (s *mockGCSSuite) SetupSuite() { - var err error - opt := fakestorage.Options{ - Scheme: "http", - Host: gcsHost, - Port: gcsPort, - PublicHost: gcsHost, - } - s.server, err = fakestorage.NewServerWithOptions(opt) - s.Require().NoError(err) - s.store = testkit.CreateMockStore(s.T()) - s.tk = testkit.NewTestKit(s.T(), s.store) -} - -func (s *mockGCSSuite) TearDownSuite() { - s.server.Stop() -} diff --git a/executor/test/memtest/BUILD.bazel b/executor/test/memtest/BUILD.bazel deleted file mode 100644 index b3199abc81e33..0000000000000 --- a/executor/test/memtest/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "memtest_test", - timeout = "short", - srcs = [ - "main_test.go", - "mem_test.go", - ], - flaky = True, - race = "on", - deps = [ - "//config", - "//meta/autoid", - "//testkit", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/memtest/main_test.go b/executor/test/memtest/main_test.go deleted file mode 100644 index d3818ceadab95..0000000000000 --- a/executor/test/memtest/main_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package memtest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/oomtest/BUILD.bazel b/executor/test/oomtest/BUILD.bazel deleted file mode 100644 index ea66329264da9..0000000000000 --- a/executor/test/oomtest/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "oomtest_test", - timeout = "short", - srcs = ["oom_test.go"], - flaky = True, - race = "on", - shard_count = 3, - deps = [ - "//testkit", - "//testkit/testsetup", - "//util/set", - "//util/syncutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) diff --git a/executor/test/partitiontest/BUILD.bazel b/executor/test/partitiontest/BUILD.bazel deleted file mode 100644 index c552e0ed55be7..0000000000000 --- a/executor/test/partitiontest/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "partitiontest_test", - timeout = "short", - srcs = [ - "main_test.go", - "partition_test.go", - ], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//testkit", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - ], -) diff --git a/executor/test/partitiontest/partition_test.go b/executor/test/partitiontest/partition_test.go deleted file mode 100644 index 72bc11269ae8e..0000000000000 --- a/executor/test/partitiontest/partition_test.go +++ /dev/null @@ -1,503 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package partitiontest - -import ( - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestPartitionedTableReplace(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - testSQL := `drop table if exists replace_test; - create table replace_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1) - partition by range (id) ( - PARTITION p0 VALUES LESS THAN (3), - PARTITION p1 VALUES LESS THAN (5), - PARTITION p2 VALUES LESS THAN (7), - PARTITION p3 VALUES LESS THAN (9));` - tk.MustExec(testSQL) - testSQL = `replace replace_test (c1) values (1),(2),(NULL);` - tk.MustExec(testSQL) - require.Equal(t, tk.Session().LastMessage(), "Records: 3 Duplicates: 0 Warnings: 0") - - errReplaceSQL := `replace replace_test (c1) values ();` - tk.MustExec("begin") - err := tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test (c1, c2) values (1,2),(1);` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test (xxx) values (3);` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test_xxx (c1) values ();` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - replaceSetSQL := `replace replace_test set c1 = 3;` - tk.MustExec(replaceSetSQL) - require.Empty(t, tk.Session().LastMessage()) - - errReplaceSetSQL := `replace replace_test set c1 = 4, c1 = 5;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSetSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSetSQL = `replace replace_test set xxx = 6;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSetSQL) - require.Error(t, err) - tk.MustExec("rollback") - - tk.MustExec(`drop table if exists replace_test_1`) - tk.MustExec(`create table replace_test_1 (id int, c1 int) partition by range (id) ( - PARTITION p0 VALUES LESS THAN (4), - PARTITION p1 VALUES LESS THAN (6), - PARTITION p2 VALUES LESS THAN (8), - PARTITION p3 VALUES LESS THAN (10), - PARTITION p4 VALUES LESS THAN (100))`) - tk.MustExec(`replace replace_test_1 select id, c1 from replace_test;`) - require.Equal(t, tk.Session().LastMessage(), "Records: 4 Duplicates: 0 Warnings: 0") - - tk.MustExec(`drop table if exists replace_test_2`) - tk.MustExec(`create table replace_test_2 (id int, c1 int) partition by range (id) ( - PARTITION p0 VALUES LESS THAN (10), - PARTITION p1 VALUES LESS THAN (50), - PARTITION p2 VALUES LESS THAN (100), - PARTITION p3 VALUES LESS THAN (300))`) - tk.MustExec(`replace replace_test_1 select id, c1 from replace_test union select id * 10, c1 * 10 from replace_test;`) - require.Equal(t, tk.Session().LastMessage(), "Records: 8 Duplicates: 0 Warnings: 0") - - errReplaceSelectSQL := `replace replace_test_1 select c1 from replace_test;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSelectSQL) - require.Error(t, err) - tk.MustExec("rollback") - - tk.MustExec(`drop table if exists replace_test_3`) - replaceUniqueIndexSQL := `create table replace_test_3 (c1 int, c2 int, UNIQUE INDEX (c2)) partition by range (c2) ( - PARTITION p0 VALUES LESS THAN (4), - PARTITION p1 VALUES LESS THAN (7), - PARTITION p2 VALUES LESS THAN (11))` - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_3 set c2=8;` - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_3 set c2=8;` - tk.MustExec(replaceUniqueIndexSQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - require.Empty(t, tk.Session().LastMessage()) - replaceUniqueIndexSQL = `replace into replace_test_3 set c1=8, c2=8;` - tk.MustExec(replaceUniqueIndexSQL) - require.Equal(t, int64(2), int64(tk.Session().AffectedRows())) - require.Empty(t, tk.Session().LastMessage()) - - replaceUniqueIndexSQL = `replace into replace_test_3 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_3 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - require.Empty(t, tk.Session().LastMessage()) - - replaceUniqueIndexSQL = `create table replace_test_4 (c1 int, c2 int, c3 int, UNIQUE INDEX (c1, c2)) partition by range (c1) ( - PARTITION p0 VALUES LESS THAN (4), - PARTITION p1 VALUES LESS THAN (7), - PARTITION p2 VALUES LESS THAN (11));` - tk.MustExec(`drop table if exists replace_test_4`) - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - - replacePrimaryKeySQL := `create table replace_test_5 (c1 int, c2 int, c3 int, PRIMARY KEY (c1, c2)) partition by range (c2) ( - PARTITION p0 VALUES LESS THAN (4), - PARTITION p1 VALUES LESS THAN (7), - PARTITION p2 VALUES LESS THAN (11));` - tk.MustExec(replacePrimaryKeySQL) - replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` - tk.MustExec(replacePrimaryKeySQL) - replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` - tk.MustExec(replacePrimaryKeySQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - - issue989SQL := `CREATE TABLE tIssue989 (a int, b int, KEY(a), UNIQUE KEY(b)) partition by range (b) ( - PARTITION p1 VALUES LESS THAN (100), - PARTITION p2 VALUES LESS THAN (200))` - tk.MustExec(issue989SQL) - issue989SQL = `insert into tIssue989 (a, b) values (1, 2);` - tk.MustExec(issue989SQL) - issue989SQL = `replace into tIssue989(a, b) values (111, 2);` - tk.MustExec(issue989SQL) - r := tk.MustQuery("select * from tIssue989;") - r.Check(testkit.Rows("111 2")) -} - -func TestHashPartitionedTableReplace(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_enable_table_partition = '1';") - tk.MustExec("drop table if exists replace_test;") - testSQL := `create table replace_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1) - partition by hash(id) partitions 4;` - tk.MustExec(testSQL) - - testSQL = `replace replace_test (c1) values (1),(2),(NULL);` - tk.MustExec(testSQL) - - errReplaceSQL := `replace replace_test (c1) values ();` - tk.MustExec("begin") - err := tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test (c1, c2) values (1,2),(1);` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test (xxx) values (3);` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSQL = `replace replace_test_xxx (c1) values ();` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSetSQL := `replace replace_test set c1 = 4, c1 = 5;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSetSQL) - require.Error(t, err) - tk.MustExec("rollback") - - errReplaceSetSQL = `replace replace_test set xxx = 6;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSetSQL) - require.Error(t, err) - tk.MustExec("rollback") - - tk.MustExec(`replace replace_test set c1 = 3;`) - tk.MustExec(`replace replace_test set c1 = 4;`) - tk.MustExec(`replace replace_test set c1 = 5;`) - tk.MustExec(`replace replace_test set c1 = 6;`) - tk.MustExec(`replace replace_test set c1 = 7;`) - - tk.MustExec(`drop table if exists replace_test_1`) - tk.MustExec(`create table replace_test_1 (id int, c1 int) partition by hash(id) partitions 5;`) - tk.MustExec(`replace replace_test_1 select id, c1 from replace_test;`) - - tk.MustExec(`drop table if exists replace_test_2`) - tk.MustExec(`create table replace_test_2 (id int, c1 int) partition by hash(id) partitions 6;`) - - tk.MustExec(`replace replace_test_1 select id, c1 from replace_test union select id * 10, c1 * 10 from replace_test;`) - - errReplaceSelectSQL := `replace replace_test_1 select c1 from replace_test;` - tk.MustExec("begin") - err = tk.ExecToErr(errReplaceSelectSQL) - require.Error(t, err) - tk.MustExec("rollback") - - tk.MustExec(`drop table if exists replace_test_3`) - replaceUniqueIndexSQL := `create table replace_test_3 (c1 int, c2 int, UNIQUE INDEX (c2)) partition by hash(c2) partitions 7;` - tk.MustExec(replaceUniqueIndexSQL) - - tk.MustExec(`replace into replace_test_3 set c2=8;`) - tk.MustExec(`replace into replace_test_3 set c2=8;`) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - tk.MustExec(`replace into replace_test_3 set c1=8, c2=8;`) - require.Equal(t, int64(2), int64(tk.Session().AffectedRows())) - - tk.MustExec(`replace into replace_test_3 set c2=NULL;`) - tk.MustExec(`replace into replace_test_3 set c2=NULL;`) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - - for i := 0; i < 100; i++ { - sql := fmt.Sprintf("replace into replace_test_3 set c2=%d;", i) - tk.MustExec(sql) - } - result := tk.MustQuery("select count(*) from replace_test_3") - result.Check(testkit.Rows("102")) - - replaceUniqueIndexSQL = `create table replace_test_4 (c1 int, c2 int, c3 int, UNIQUE INDEX (c1, c2)) partition by hash(c1) partitions 8;` - tk.MustExec(`drop table if exists replace_test_4`) - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` - tk.MustExec(replaceUniqueIndexSQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - - replacePrimaryKeySQL := `create table replace_test_5 (c1 int, c2 int, c3 int, PRIMARY KEY (c1, c2)) partition by hash (c2) partitions 9;` - tk.MustExec(replacePrimaryKeySQL) - replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` - tk.MustExec(replacePrimaryKeySQL) - replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` - tk.MustExec(replacePrimaryKeySQL) - require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) - - issue989SQL := `CREATE TABLE tIssue989 (a int, b int, KEY(a), UNIQUE KEY(b)) partition by hash (b) partitions 10;` - tk.MustExec(issue989SQL) - issue989SQL = `insert into tIssue989 (a, b) values (1, 2);` - tk.MustExec(issue989SQL) - issue989SQL = `replace into tIssue989(a, b) values (111, 2);` - tk.MustExec(issue989SQL) - r := tk.MustQuery("select * from tIssue989;") - r.Check(testkit.Rows("111 2")) -} - -func TestPartitionedTableUpdate(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(`create table t (id int not null default 1, name varchar(255)) - PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21))`) - - tk.MustExec(`insert INTO t VALUES (1, "hello");`) - tk.CheckExecResult(1, 0) - tk.MustExec(`insert INTO t VALUES (7, "hello");`) - tk.CheckExecResult(1, 0) - - // update non partition column - tk.MustExec(`UPDATE t SET name = "abc" where id > 0;`) - tk.CheckExecResult(2, 0) - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 2 Changed: 2 Warnings: 0") - r := tk.MustQuery(`SELECT * from t order by id limit 2;`) - r.Check(testkit.Rows("1 abc", "7 abc")) - - // update partition column - tk.MustExec(`update t set id = id + 1`) - tk.CheckExecResult(2, 0) - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 2 Changed: 2 Warnings: 0") - r = tk.MustQuery(`SELECT * from t order by id limit 2;`) - r.Check(testkit.Rows("2 abc", "8 abc")) - - // update partition column, old and new record locates on different partitions - tk.MustExec(`update t set id = 20 where id = 8`) - tk.CheckExecResult(1, 0) - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 1 Warnings: 0") - r = tk.MustQuery(`SELECT * from t order by id limit 2;`) - r.Check(testkit.Rows("2 abc", "20 abc")) - - // table option is auto-increment - tk.MustExec("drop table if exists t;") - tk.MustExec(`create table t (id int not null auto_increment, name varchar(255), primary key(id)) - PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21))`) - - tk.MustExec("insert into t(name) values ('aa')") - tk.MustExec("update t set id = 8 where name = 'aa'") - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 1 Warnings: 0") - tk.MustExec("insert into t(name) values ('bb')") - r = tk.MustQuery("select * from t;") - r.Check(testkit.Rows("8 aa", "9 bb")) - - err := tk.ExecToErr("update t set id = null where name = 'aa'") - require.EqualError(t, err, "[table:1048]Column 'id' cannot be null") - - // Test that in a transaction, when a constraint failed in an update statement, the record is not inserted. - tk.MustExec("drop table if exists t;") - tk.MustExec(`create table t (id int, name int unique) - PARTITION BY RANGE ( name ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21))`) - tk.MustExec("insert t values (1, 1), (2, 2);") - err = tk.ExecToErr("update t set name = 1 where id = 2") - require.Error(t, err) - tk.MustQuery("select * from t").Check(testkit.Rows("1 1", "2 2")) - - // test update ignore for pimary key - tk.MustExec("drop table if exists t;") - tk.MustExec(`create table t(a bigint, primary key (a)) - PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11))`) - tk.MustExec("insert into t values (5)") - tk.MustExec("insert into t values (7)") - err = tk.ExecToErr("update ignore t set a = 5 where a = 7;") - require.NoError(t, err) - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 0 Warnings: 1") - r = tk.MustQuery("SHOW WARNINGS;") - r.Check(testkit.Rows("Warning 1062 Duplicate entry '5' for key 't.PRIMARY'")) - tk.MustQuery("select * from t order by a").Check(testkit.Rows("5", "7")) - - // test update ignore for truncate as warning - err = tk.ExecToErr("update ignore t set a = 1 where a = (select '2a')") - require.NoError(t, err) - r = tk.MustQuery("SHOW WARNINGS;") - r.Check(testkit.Rows("Warning 1292 Truncated incorrect DOUBLE value: '2a'", "Warning 1292 Truncated incorrect DOUBLE value: '2a'")) - - // test update ignore for unique key - tk.MustExec("drop table if exists t;") - tk.MustExec(`create table t(a bigint, unique key I_uniq (a)) - PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11))`) - tk.MustExec("insert into t values (5)") - tk.MustExec("insert into t values (7)") - err = tk.ExecToErr("update ignore t set a = 5 where a = 7;") - require.NoError(t, err) - require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 0 Warnings: 1") - r = tk.MustQuery("SHOW WARNINGS;") - r.Check(testkit.Rows("Warning 1062 Duplicate entry '5' for key 't.I_uniq'")) - tk.MustQuery("select * from t order by a").Check(testkit.Rows("5", "7")) -} - -func TestPartitionedTableDelete(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - createTable := `CREATE TABLE test.t (id int not null default 1, name varchar(255), index(id)) - PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21))` - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(createTable) - for i := 1; i < 21; i++ { - tk.MustExec(fmt.Sprintf(`insert into t values (%d, "hello")`, i)) - } - - tk.MustExec(`delete from t where id = 2 limit 1;`) - tk.CheckExecResult(1, 0) - - // Test delete with false condition - tk.MustExec(`delete from t where 0;`) - tk.CheckExecResult(0, 0) - - tk.MustExec("insert into t values (2, 'abc')") - tk.MustExec(`delete from t where t.id = 2 limit 1`) - tk.CheckExecResult(1, 0) - - // Test delete ignore - tk.MustExec("insert into t values (2, 'abc')") - err := tk.ExecToErr("delete from t where id = (select '2a')") - require.Error(t, err) - err = tk.ExecToErr("delete ignore from t where id = (select '2a')") - require.NoError(t, err) - tk.CheckExecResult(1, 0) - r := tk.MustQuery("SHOW WARNINGS;") - r.Check(testkit.Rows("Warning 1292 Truncated incorrect DOUBLE value: '2a'", "Warning 1292 Truncated incorrect DOUBLE value: '2a'")) - - // Test delete without using index, involve multiple partitions. - tk.MustExec("delete from t ignore index(id) where id >= 13 and id <= 17") - tk.CheckExecResult(5, 0) - - tk.MustExec("admin check table t") - tk.MustExec(`delete from t;`) - tk.CheckExecResult(14, 0) - - // Fix that partitioned table should not use PointGetPlan. - tk.MustExec(`create table t1 (c1 bigint, c2 bigint, c3 bigint, primary key(c1)) partition by range (c1) (partition p0 values less than (3440))`) - tk.MustExec("insert into t1 values (379, 379, 379)") - tk.MustExec("delete from t1 where c1 = 379") - tk.CheckExecResult(1, 0) - tk.MustExec(`drop table t1;`) -} - -func TestPartitionOnMissing(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create schema OnMissing") - tk.MustExec("use OnMissing") - tk.MustExec(`set global tidb_partition_prune_mode='dynamic'`) - tk.MustExec(`set session tidb_partition_prune_mode='dynamic'`) - - tk.MustExec(`CREATE TABLE tt1 ( - id INT NOT NULL, - listid INT, - name varchar(10), - primary key (listid) clustered - ) - PARTITION BY LIST (listid) ( - PARTITION p1 VALUES IN (1), - PARTITION p2 VALUES IN (2), - PARTITION p3 VALUES IN (3), - PARTITION p4 VALUES IN (4) - )`) - - tk.MustExec(`CREATE TABLE tt2 ( - id INT NOT NULL, - listid INT - )`) - - tk.MustExec(`create index idx_listid on tt1(id,listid)`) - tk.MustExec(`create index idx_listid on tt2(listid)`) - - tk.MustExec(`insert into tt1 values(1,1,1)`) - tk.MustExec(`insert into tt1 values(2,2,2)`) - tk.MustExec(`insert into tt1 values(3,3,3)`) - tk.MustExec(`insert into tt1 values(4,4,4)`) - tk.MustExec(`insert into tt2 values(1,1)`) - tk.MustExec(`insert into tt2 values(2,2)`) - tk.MustExec(`insert into tt2 values(3,3)`) - tk.MustExec(`insert into tt2 values(4,4)`) - tk.MustExec(`insert into tt2 values(5,5)`) - - tk.MustExec(`analyze table tt1`) - tk.MustExec(`analyze table tt2`) - - tk.MustQuery(`select /*+ inl_join(tt1)*/ count(*) from tt2 - left join tt1 on tt1.listid=tt2.listid and tt1.id=tt2.id`).Check(testkit.Rows("5")) - tk.MustQuery(`select /*+ inl_join(tt1)*/ count(*) from tt2 - left join tt1 on tt1.listid=tt2.listid`).Check(testkit.Rows("5")) - tk.MustQuery(`explain format = 'brief' select /*+ inl_join(tt1)*/ count(*) from tt2 - left join tt1 on tt1.listid=tt2.listid`).Check(testkit.Rows(""+ - "StreamAgg 1.00 root funcs:count(Column#13)->Column#7", - "└─IndexReader 1.00 root index:StreamAgg", - " └─StreamAgg 1.00 cop[tikv] funcs:count(1)->Column#13", - " └─IndexFullScan 5.00 cop[tikv] table:tt2, index:idx_listid(listid) keep order:false")) -} diff --git a/executor/test/passwordtest/BUILD.bazel b/executor/test/passwordtest/BUILD.bazel deleted file mode 100644 index 52ba94935a5f6..0000000000000 --- a/executor/test/passwordtest/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "passwordtest_test", - timeout = "short", - srcs = [ - "main_test.go", - "password_management_test.go", - ], - flaky = True, - shard_count = 16, - deps = [ - "//domain", - "//errno", - "//kv", - "//parser/auth", - "//parser/mysql", - "//privilege/privileges", - "//sessionctx/variable", - "//testkit", - "//util/sqlexec", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/seqtest/BUILD.bazel b/executor/test/seqtest/BUILD.bazel deleted file mode 100644 index 1af49d0d6f9bf..0000000000000 --- a/executor/test/seqtest/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "seqtest_test", - timeout = "moderate", - srcs = [ - "main_test.go", - "prepared_test.go", - "seq_executor_test.go", - ], - flaky = True, - race = "on", - shard_count = 37, - deps = [ - "//config", - "//ddl/testutil", - "//ddl/util", - "//errno", - "//executor", - "//expression", - "//kv", - "//meta/autoid", - "//metrics", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//server", - "//session", - "//sessionctx/variable", - "//store/copr", - "//store/mockstore", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "//testkit/testutil", - "//util/gcutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/seqtest/main_test.go b/executor/test/seqtest/main_test.go deleted file mode 100644 index af6852b2ad954..0000000000000 --- a/executor/test/seqtest/main_test.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/config" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/executor.readProjection[...]"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/seqtest/prepared_test.go b/executor/test/seqtest/prepared_test.go deleted file mode 100644 index b963fd40a0deb..0000000000000 --- a/executor/test/seqtest/prepared_test.go +++ /dev/null @@ -1,697 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "context" - "fmt" - "math" - "testing" - "time" - - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" -) - -func TestPrepared(t *testing.T) { - store := testkit.CreateMockStore(t) - flags := []bool{false, true} - ctx := context.Background() - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - var err error - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1)") - tk.MustExec("insert prepare_test (c1) values (1),(2),(NULL)") - - tk.MustExec(`prepare stmt_test_1 from 'select id from prepare_test where id > ?';`) - tk.MustExec(`set @a = 1;`) - tk.MustExec(`execute stmt_test_1 using @a;`) - tk.MustExec(`prepare stmt_test_2 from 'select 1'`) - // Prepare multiple statement is not allowed. - tk.MustGetErrCode(`prepare stmt_test_3 from 'select id from prepare_test where id > ?;select id from prepare_test where id > ?;'`, errno.ErrPrepareMulti) - - // The variable count does not match. - tk.MustExec(`prepare stmt_test_4 from 'select id from prepare_test where id > ? and id < ?';`) - tk.MustExec(`set @a = 1;`) - tk.MustGetErrCode(`execute stmt_test_4 using @a;`, errno.ErrWrongParamCount) - // Prepare and deallocate prepared statement immediately. - tk.MustExec(`prepare stmt_test_5 from 'select id from prepare_test where id > ?';`) - tk.MustExec(`deallocate prepare stmt_test_5;`) - - // Statement not found. - err = tk.ExecToErr("deallocate prepare stmt_test_5") - require.True(t, plannercore.ErrStmtNotFound.Equal(err)) - - // incorrect SQLs in prepare. issue #3738, SQL in prepare stmt is parsed in DoPrepare. - tk.MustGetErrMsg(`prepare p from "delete from t where a = 7 or 1=1/*' and b = 'p'";`, - `[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use near '/*' and b = 'p'' at line 1`) - - // The `stmt_test5` should not be found. - err = tk.ExecToErr(`set @a = 1; execute stmt_test_5 using @a;`) - require.True(t, plannercore.ErrStmtNotFound.Equal(err)) - - // Use parameter marker with argument will run prepared statement. - result := tk.MustQuery("select distinct c1, c2 from prepare_test where c1 = ?", 1) - result.Check(testkit.Rows("1 ")) - - // Call Session PrepareStmt directly to get stmtID. - query := "select c1, c2 from prepare_test where c1 = ?" - stmtID, _, _, err := tk.Session().PrepareStmt(query) - require.NoError(t, err) - rs, err := tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 ")) - - tk.MustExec("delete from prepare_test") - query = "select c1 from prepare_test where c1 = (select c1 from prepare_test where c1 = ?)" - stmtID, _, _, err = tk.Session().PrepareStmt(query) - require.NoError(t, err) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec(`set @@tidb_enable_prepared_plan_cache=true`) - tk1.MustExec("use test") - tk1.MustExec("insert prepare_test (c1) values (3)") - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) - require.NoError(t, err) - tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) - - tk.MustExec("delete from prepare_test") - query = "select c1 from prepare_test where c1 = (select c1 from prepare_test where c1 = ?)" - stmtID, _, _, err = tk.Session().PrepareStmt(query) - require.NoError(t, err) - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) - require.NoError(t, err) - require.NoError(t, rs.Close()) - tk1.MustExec("insert prepare_test (c1) values (3)") - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) - require.NoError(t, err) - tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) - - tk.MustExec("delete from prepare_test") - query = "select c1 from prepare_test where c1 in (select c1 from prepare_test where c1 = ?)" - stmtID, _, _, err = tk.Session().PrepareStmt(query) - require.NoError(t, err) - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) - require.NoError(t, err) - require.NoError(t, rs.Close()) - tk1.MustExec("insert prepare_test (c1) values (3)") - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) - require.NoError(t, err) - tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) - - tk.MustExec("begin") - tk.MustExec("insert prepare_test (c1) values (4)") - query = "select c1, c2 from prepare_test where c1 = ?" - stmtID, _, _, err = tk.Session().PrepareStmt(query) - require.NoError(t, err) - tk.MustExec("rollback") - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(4)) - require.NoError(t, err) - tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows()) - - prepStmt, err := tk.Session().GetSessionVars().GetPreparedStmtByID(stmtID) - require.NoError(t, err) - execStmt := &ast.ExecuteStmt{PrepStmt: prepStmt, BinaryArgs: expression.Args2Expressions4Test(1)} - // Check that ast.Statement created by compiler.Compile has query text. - compiler := executor.Compiler{Ctx: tk.Session()} - stmt, err := compiler.Compile(context.TODO(), execStmt) - require.NoError(t, err) - - // Check that rebuild plan works. - err = tk.Session().PrepareTxnCtx(ctx) - require.NoError(t, err) - _, err = stmt.RebuildPlan(ctx) - require.NoError(t, err) - rs, err = stmt.Exec(ctx) - require.NoError(t, err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - require.NoError(t, rs.Close()) - - // Make schema change. - tk.MustExec("drop table if exists prepare2") - tk.MustExec("create table prepare2 (a int)") - - // Should success as the changed schema do not affect the prepared statement. - rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - if rs != nil { - require.NoError(t, rs.Close()) - } - - // Drop a column so the prepared statement become invalid. - query = "select c1, c2 from prepare_test where c1 = ?" - stmtID, _, _, err = tk.Session().PrepareStmt(query) - require.NoError(t, err) - tk.MustExec("alter table prepare_test drop column c2") - - _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.True(t, plannercore.ErrUnknownColumn.Equal(err)) - - tk.MustExec("drop table prepare_test") - _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.True(t, plannercore.ErrSchemaChanged.Equal(err)) - - // issue 3381 - tk.MustExec("drop table if exists prepare3") - tk.MustExec("create table prepare3 (a decimal(1))") - tk.MustExec("prepare stmt from 'insert into prepare3 value(123)'") - tk.MustExecToErr("execute stmt") - - _, _, fields, err := tk.Session().PrepareStmt("select a from prepare3") - require.NoError(t, err) - require.Equal(t, "test", fields[0].DBName.L) - require.Equal(t, "prepare3", fields[0].TableAsName.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - - _, _, fields, err = tk.Session().PrepareStmt("select a from prepare3 where ?") - require.NoError(t, err) - require.Equal(t, "test", fields[0].DBName.L) - require.Equal(t, "prepare3", fields[0].TableAsName.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - - _, _, fields, err = tk.Session().PrepareStmt("select (1,1) in (select 1,1)") - require.NoError(t, err) - require.Equal(t, "", fields[0].DBName.L) - require.Equal(t, "", fields[0].TableAsName.L) - require.Equal(t, "(1,1) in (select 1,1)", fields[0].ColumnAsName.L) - - _, _, fields, err = tk.Session().PrepareStmt("select a from prepare3 where a = (" + - "select a from prepare2 where a = ?)") - require.NoError(t, err) - require.Equal(t, "test", fields[0].DBName.L) - require.Equal(t, "prepare3", fields[0].TableAsName.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - - _, _, fields, err = tk.Session().PrepareStmt("select * from prepare3 as t1 join prepare3 as t2") - require.NoError(t, err) - require.Equal(t, "test", fields[0].DBName.L) - require.Equal(t, "t1", fields[0].TableAsName.L) - require.Equal(t, "a", fields[0].ColumnAsName.L) - require.Equal(t, "test", fields[1].DBName.L) - require.Equal(t, "t2", fields[1].TableAsName.L) - require.Equal(t, "a", fields[1].ColumnAsName.L) - - _, _, fields, err = tk.Session().PrepareStmt("update prepare3 set a = ?") - require.NoError(t, err) - require.Len(t, fields, 0) - - // issue 8074 - tk.MustExec("drop table if exists prepare1;") - tk.MustExec("create table prepare1 (a decimal(1))") - tk.MustExec("insert into prepare1 values(1);") - tk.MustGetErrMsg("prepare stmt FROM @sql1", - "[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 4 near \"NULL\" ") - tk.MustExec("SET @sql = 'update prepare1 set a=5 where a=?';") - tk.MustExec("prepare stmt FROM @sql") - tk.MustExec("set @var=1;") - tk.MustExec("execute stmt using @var") - tk.MustQuery("select a from prepare1;").Check(testkit.Rows("5")) - - // issue 19371 - tk.MustExec("SET @sql = 'update prepare1 set a=a+1';") - tk.MustExec("prepare stmt FROM @SQL") - tk.MustExec("execute stmt") - tk.MustQuery("select a from prepare1;").Check(testkit.Rows("6")) - tk.MustExec("prepare stmt FROM @Sql") - tk.MustExec("execute stmt") - tk.MustQuery("select a from prepare1;").Check(testkit.Rows("7")) - - // Coverage. - exec := &executor.ExecuteExec{} - err = exec.Next(ctx, nil) - require.NoError(t, err) - err = exec.Close() - require.NoError(t, err) - - // issue 8065 - stmtID, _, _, err = tk.Session().PrepareStmt("select ? from dual") - require.NoError(t, err) - _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - stmtID, _, _, err = tk.Session().PrepareStmt("update prepare1 set a = ? where a = ?") - require.NoError(t, err) - _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1, 1)) - require.NoError(t, err) - } -} - -func TestPreparedLimitOffset(t *testing.T) { - store := testkit.CreateMockStore(t) - flags := []bool{false, true} - ctx := context.Background() - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1)") - tk.MustExec("insert prepare_test (c1) values (1),(2),(NULL)") - tk.MustExec(`prepare stmt_test_1 from 'select id from prepare_test limit ? offset ?'; set @a = 1, @b=1;`) - r := tk.MustQuery(`execute stmt_test_1 using @a, @b;`) - r.Check(testkit.Rows("2")) - - tk.MustExec(`set @a=1.1`) - _, err := tk.Exec(`execute stmt_test_1 using @a, @b;`) - require.True(t, plannercore.ErrWrongArguments.Equal(err)) - - tk.MustExec(`set @c="-1"`) - _, err = tk.Exec("execute stmt_test_1 using @c, @c") - require.True(t, plannercore.ErrWrongArguments.Equal(err)) - - stmtID, _, _, err := tk.Session().PrepareStmt("select id from prepare_test limit ?") - require.NoError(t, err) - rs, err := tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - rs.Close() - } -} - -func TestPrepareWithAggregation(t *testing.T) { - store := testkit.CreateMockStore(t) - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - - se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ - PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), - }) - require.NoError(t, err) - tk.SetSession(se) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int primary key)") - tk.MustExec("insert into t values (1), (2), (3)") - tk.MustExec(`prepare stmt from 'select sum(id) from t where id = ?'`) - - tk.MustExec(`set @id="1"`) - r := tk.MustQuery(`execute stmt using @id;`) - r.Check(testkit.Rows("1")) - - r = tk.MustQuery(`execute stmt using @id;`) - r.Check(testkit.Rows("1")) - } -} - -func TestPreparedInsert(t *testing.T) { - store := testkit.CreateMockStore(t) - metrics.ResettablePlanCacheCounterFortTest = true - metrics.PlanCacheCounter.Reset() - counter := metrics.PlanCacheCounter.WithLabelValues("prepare") - pb := &dto.Metric{} - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - var err error - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") - tk.MustExec(`prepare stmt_insert from 'insert into prepare_test values (?, ?)'`) - tk.MustExec(`set @a=1,@b=1; execute stmt_insert using @a, @b;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(0), hit) - } - tk.MustExec(`set @a=2,@b=2; execute stmt_insert using @a, @b;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(1), hit) - } - tk.MustExec(`set @a=3,@b=3; execute stmt_insert using @a, @b;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(2), hit) - } - - result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) - result.Check(testkit.Rows("1 1")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) - result.Check(testkit.Rows("2 2")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) - result.Check(testkit.Rows("3 3")) - - tk.MustExec(`prepare stmt_insert_select from 'insert into prepare_test (id, c1) select id + 100, c1 + 100 from prepare_test where id = ?'`) - tk.MustExec(`set @a=1; execute stmt_insert_select using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(2), hit) - } - tk.MustExec(`set @a=2; execute stmt_insert_select using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(3), hit) - } - tk.MustExec(`set @a=3; execute stmt_insert_select using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(4), hit) - } - - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 101) - result.Check(testkit.Rows("101 101")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 102) - result.Check(testkit.Rows("102 102")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 103) - result.Check(testkit.Rows("103 103")) - } -} - -func TestPreparedUpdate(t *testing.T) { - store := testkit.CreateMockStore(t) - metrics.ResettablePlanCacheCounterFortTest = true - metrics.PlanCacheCounter.Reset() - counter := metrics.PlanCacheCounter.WithLabelValues("prepare") - pb := &dto.Metric{} - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect hit counter in this UT. - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - var err error - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") - tk.MustExec(`insert into prepare_test values (1, 1)`) - tk.MustExec(`insert into prepare_test values (2, 2)`) - tk.MustExec(`insert into prepare_test values (3, 3)`) - - tk.MustExec(`prepare stmt_update from 'update prepare_test set c1 = c1 + ? where id = ?'`) - tk.MustExec(`set @a=1,@b=100; execute stmt_update using @b,@a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(0), hit) - } - tk.MustExec(`set @a=2,@b=200; execute stmt_update using @b,@a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(1), hit) - } - tk.MustExec(`set @a=3,@b=300; execute stmt_update using @b,@a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(2), hit) - } - - result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) - result.Check(testkit.Rows("1 101")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) - result.Check(testkit.Rows("2 202")) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) - result.Check(testkit.Rows("3 303")) - } -} - -func TestIssue21884(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@tidb_enable_prepared_plan_cache=false`) - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test(a bigint primary key, status bigint, last_update_time datetime)") - tk.MustExec("insert into prepare_test values (100, 0, '2020-12-18 20:00:00')") - tk.MustExec("prepare stmt from 'update prepare_test set status = ?, last_update_time = now() where a = 100'") - tk.MustExec("set @status = 1") - tk.MustExec("execute stmt using @status") - updateTime := tk.MustQuery("select last_update_time from prepare_test").Rows()[0][0] - // Sleep 1 second to make sure `last_update_time` is updated. - time.Sleep(1 * time.Second) - tk.MustExec("execute stmt using @status") - newUpdateTime := tk.MustQuery("select last_update_time from prepare_test").Rows()[0][0] - require.NotEqual(t, newUpdateTime, updateTime) -} - -func TestPreparedDelete(t *testing.T) { - store := testkit.CreateMockStore(t) - metrics.ResettablePlanCacheCounterFortTest = true - metrics.PlanCacheCounter.Reset() - counter := metrics.PlanCacheCounter.WithLabelValues("prepare") - pb := &dto.Metric{} - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect hit counter in this UT. - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - var err error - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") - tk.MustExec(`insert into prepare_test values (1, 1)`) - tk.MustExec(`insert into prepare_test values (2, 2)`) - tk.MustExec(`insert into prepare_test values (3, 3)`) - - tk.MustExec(`prepare stmt_delete from 'delete from prepare_test where id = ?'`) - tk.MustExec(`set @a=1; execute stmt_delete using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(0), hit) - } - tk.MustExec(`set @a=2; execute stmt_delete using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(1), hit) - } - tk.MustExec(`set @a=3; execute stmt_delete using @a;`) - if flag { - err = counter.Write(pb) - require.NoError(t, err) - hit := pb.GetCounter().GetValue() - require.Equal(t, float64(2), hit) - } - - result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) - result.Check(nil) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) - result.Check(nil) - result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) - result.Check(nil) - } -} - -func TestPrepareDealloc(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@tidb_enable_prepared_plan_cache=true`) - - se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ - PreparedPlanCache: plannercore.NewLRUPlanCache(3, 0.1, math.MaxUint64, tk.Session(), false), - }) - require.NoError(t, err) - tk.SetSession(se) - - tk.MustExec("use test") - tk.MustExec("drop table if exists prepare_test") - tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") - - require.Equal(t, 0, tk.Session().GetSessionPlanCache().Size()) - tk.MustExec(`prepare stmt1 from 'select id from prepare_test'`) - tk.MustExec("execute stmt1") - tk.MustExec(`prepare stmt2 from 'select c1 from prepare_test'`) - tk.MustExec("execute stmt2") - tk.MustExec(`prepare stmt3 from 'select id, c1 from prepare_test'`) - tk.MustExec("execute stmt3") - tk.MustExec(`prepare stmt4 from 'select * from prepare_test'`) - tk.MustExec("execute stmt4") - require.Equal(t, 3, tk.Session().GetSessionPlanCache().Size()) - - tk.MustExec("deallocate prepare stmt1") - require.Equal(t, 3, tk.Session().GetSessionPlanCache().Size()) - tk.MustExec("deallocate prepare stmt2") - tk.MustExec("deallocate prepare stmt3") - tk.MustExec("deallocate prepare stmt4") - require.Equal(t, 0, tk.Session().GetSessionPlanCache().Size()) - - tk.MustExec(`prepare stmt1 from 'select * from prepare_test'`) - tk.MustExec(`execute stmt1`) - tk.MustExec(`prepare stmt2 from 'select * from prepare_test'`) - tk.MustExec(`execute stmt2`) - require.Equal(t, 1, tk.Session().GetSessionPlanCache().Size()) // use the same cached plan since they have the same statement - - tk.MustExec(`drop database if exists plan_cache`) - tk.MustExec(`create database plan_cache`) - tk.MustExec(`use plan_cache`) - tk.MustExec(`create table prepare_test (id int PRIMARY KEY, c1 int)`) - tk.MustExec(`prepare stmt3 from 'select * from prepare_test'`) - tk.MustExec(`execute stmt3`) - require.Equal(t, 2, tk.Session().GetSessionPlanCache().Size()) // stmt3 has different DB -} - -func TestPreparedIssue8153(t *testing.T) { - store := testkit.CreateMockStore(t) - flags := []bool{false, true} - for _, flag := range flags { - tk := testkit.NewTestKit(t, store) - tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) - var err error - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t (a, b) values (1,3), (2,2), (3,1)") - - tk.MustExec(`prepare stmt from 'select * from t order by ? asc'`) - r := tk.MustQuery(`execute stmt using @param;`) - r.Check(testkit.Rows("1 3", "2 2", "3 1")) - - tk.MustExec(`set @param = 1`) - r = tk.MustQuery(`execute stmt using @param;`) - r.Check(testkit.Rows("1 3", "2 2", "3 1")) - - tk.MustExec(`set @param = 2`) - r = tk.MustQuery(`execute stmt using @param;`) - r.Check(testkit.Rows("3 1", "2 2", "1 3")) - - tk.MustExec(`set @param = 3`) - _, err = tk.Exec(`execute stmt using @param;`) - require.EqualError(t, err, "[planner:1054]Unknown column '?' in 'order clause'") - - tk.MustExec(`set @param = '##'`) - r = tk.MustQuery(`execute stmt using @param;`) - r.Check(testkit.Rows("1 3", "2 2", "3 1")) - - tk.MustExec("insert into t (a, b) values (1,1), (1,2), (2,1), (2,3), (3,2), (3,3)") - tk.MustExec(`prepare stmt from 'select ?, sum(a) from t group by ?'`) - - tk.MustExec(`set @a=1,@b=1`) - r = tk.MustQuery(`execute stmt using @a,@b;`) - r.Check(testkit.Rows("1 18")) - - tk.MustExec(`set @a=1,@b=2`) - _, err = tk.Exec(`execute stmt using @a,@b;`) - require.EqualError(t, err, "[planner:1056]Can't group on 'sum(a)'") - } -} - -func TestPreparedIssue17419(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - sv := server.CreateMockServer(t, store) - sv.SetDomain(dom) - defer sv.Close() - - conn1 := server.CreateMockConn(t, sv) - tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) - ctx := context.Background() - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int)") - tk.MustExec("insert into t (a) values (1), (2), (3)") - - conn2 := server.CreateMockConn(t, sv) - tk1 := testkit.NewTestKitWithSession(t, store, conn2.Context().Session) - - query := "select * from test.t" - stmtID, _, _, err := tk1.Session().PrepareStmt(query) - require.NoError(t, err) - - dom.ExpensiveQueryHandle().SetSessionManager(sv) - - rs, err := tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test()) - require.NoError(t, err) - tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1", "2", "3")) - tk1.Session().SetProcessInfo("", time.Now(), mysql.ComStmtExecute, 0) - - dom.ExpensiveQueryHandle().LogOnQueryExceedMemQuota(tk.Session().GetSessionVars().ConnectionID) - - // After entirely fixing https://github.com/pingcap/tidb/issues/17419 - // require.NotNil(t, tk1.Session().ShowProcess().Plan) - // _, ok := tk1.Session().ShowProcess().Plan.(*plannercore.Execute) - // require.True(t, ok) -} - -func TestLimitUnsupportedCase(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, key(a))") - tk.MustExec("prepare stmt from 'select * from t limit ?'") - - tk.MustExec("set @a = 1.2") - tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") - tk.MustExec("set @a = 1.") - tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") - tk.MustExec("set @a = '0'") - tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") - tk.MustExec("set @a = '1'") - tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") - tk.MustExec("set @a = 1_2") - tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") -} - -func TestIssue38323(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int, k int);") - - tk.MustExec("prepare stmt from 'explain select * from t where id = ? and k = ? group by id, k';") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: not a SELECT/UPDATE/INSERT/DELETE/SET statement")) - tk.MustExec("set @a = 1;") - tk.MustExec("execute stmt using @a, @a") - tk.MustQuery("execute stmt using @a, @a").Check(tk.MustQuery("explain select * from t where id = 1 and k = 1 group by id, k").Rows()) - - tk.MustExec("prepare stmt from 'explain select * from t where ? = id and ? = k group by id, k';") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: not a SELECT/UPDATE/INSERT/DELETE/SET statement")) - tk.MustExec("set @a = 1;") - tk.MustQuery("execute stmt using @a, @a").Check(tk.MustQuery("explain select * from t where 1 = id and 1 = k group by id, k").Rows()) -} diff --git a/executor/test/showtest/BUILD.bazel b/executor/test/showtest/BUILD.bazel deleted file mode 100644 index 1a3310521bb50..0000000000000 --- a/executor/test/showtest/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "showtest_test", - timeout = "moderate", - srcs = [ - "main_test.go", - "show_test.go", - ], - flaky = True, - shard_count = 45, - deps = [ - "//autoid_service", - "//config", - "//executor", - "//infoschema", - "//meta/autoid", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//planner/core", - "//privilege/privileges", - "//session", - "//sessionctx/variable", - "//testkit", - "//types", - "//util/dbterror/exeerrors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/showtest/main_test.go b/executor/test/showtest/main_test.go deleted file mode 100644 index c37cfd90b5d5a..0000000000000 --- a/executor/test/showtest/main_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package showtest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/showtest/show_test.go b/executor/test/showtest/show_test.go deleted file mode 100644 index 1cc71ca8b6fd5..0000000000000 --- a/executor/test/showtest/show_test.go +++ /dev/null @@ -1,1981 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package showtest - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - parsertypes "github.com/pingcap/tidb/parser/types" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/stretchr/testify/require" -) - -func TestShowHistogramsInFlight(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - result := tk.MustQuery("show histograms_in_flight") - rows := result.Rows() - require.Len(t, rows, 1) - require.Equal(t, rows[0][0], "0") -} - -func TestShowOpenTables(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("show open tables") - tk.MustQuery("show open tables in test") -} - -func TestShowCreateViewDefiner(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%", AuthUsername: "root", AuthHostname: "%"}, nil, nil, nil)) - - tk.MustExec("use test") - tk.MustExec("create or replace view v1 as select 1") - tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `v1` (`1`) AS SELECT 1 AS `1`|utf8mb4|utf8mb4_bin")) - tk.MustExec("drop view v1") -} - -func TestShowCreateTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int,b int)") - tk.MustExec("drop view if exists v1") - tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select * from t1") - tk.MustQuery("show create table v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) - tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) - tk.MustExec("drop view v1") - tk.MustExec("drop table t1") - - tk.MustExec("drop view if exists v") - tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v as select JSON_MERGE('{}', '{}') as col;") - tk.MustQuery("show create view v").Check(testkit.RowsWithSep("|", "v|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v` (`col`) AS SELECT JSON_MERGE(_UTF8MB4'{}', _UTF8MB4'{}') AS `col`|utf8mb4|utf8mb4_bin")) - tk.MustExec("drop view if exists v") - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int,b int)") - tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select avg(a),t1.* from t1 group by a") - tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`avg(a)`, `a`, `b`) AS SELECT AVG(`a`) AS `avg(a)`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1` GROUP BY `a`|utf8mb4|utf8mb4_bin")) - tk.MustExec("drop view v1") - tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select a+b, t1.* , a as c from t1") - tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a+b`, `a`, `b`, `c`) AS SELECT `a`+`b` AS `a+b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`a` AS `c` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) - tk.MustExec("drop table t1") - tk.MustExec("drop view v1") - - // For issue #9211 - tk.MustExec("create table t(c int, b int as (c + 1))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `c` int(11) DEFAULT NULL,\n"+ - " `b` int(11) GENERATED ALWAYS AS (`c` + 1) VIRTUAL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - tk.MustExec("drop table t") - tk.MustExec("create table t(c int, b int as (c + 1) not null)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `c` int(11) DEFAULT NULL,\n"+ - " `b` int(11) GENERATED ALWAYS AS (`c` + 1) VIRTUAL NOT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec("drop table t") - tk.MustExec("create table t ( a char(10) charset utf8 collate utf8_bin, b char(10) as (rtrim(a)));") - tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` char(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,\n"+ - " `b` char(10) GENERATED ALWAYS AS (rtrim(`a`)) VIRTUAL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec("drop table t") - - tk.MustExec(`drop table if exists different_charset`) - tk.MustExec(`create table different_charset(ch1 varchar(10) charset utf8, ch2 varchar(10) charset binary);`) - tk.MustQuery(`show create table different_charset`).Check(testkit.RowsWithSep("|", - ""+ - "different_charset CREATE TABLE `different_charset` (\n"+ - " `ch1` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,\n"+ - " `ch2` varbinary(10) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table `t` (\n" + - "`a` timestamp not null default current_timestamp,\n" + - "`b` timestamp(3) default current_timestamp(3),\n" + - "`c` datetime default current_timestamp,\n" + - "`d` datetime(4) default current_timestamp(4),\n" + - "`e` varchar(20) default 'cUrrent_tImestamp',\n" + - "`f` datetime(2) default current_timestamp(2) on update current_timestamp(2),\n" + - "`g` timestamp(2) default current_timestamp(2) on update current_timestamp(2),\n" + - "`h` date default current_date )") - tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"+ - " `b` timestamp(3) DEFAULT CURRENT_TIMESTAMP(3),\n"+ - " `c` datetime DEFAULT CURRENT_TIMESTAMP,\n"+ - " `d` datetime(4) DEFAULT CURRENT_TIMESTAMP(4),\n"+ - " `e` varchar(20) DEFAULT 'cUrrent_tImestamp',\n"+ - " `f` datetime(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2),\n"+ - " `g` timestamp(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2),\n"+ - " `h` date DEFAULT CURRENT_DATE\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec("drop table t") - - tk.MustExec("create table t (a int, b int) shard_row_id_bits = 4 pre_split_regions=3;") - tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T! SHARD_ROW_ID_BITS=4 PRE_SPLIT_REGIONS=3 */", - )) - tk.MustExec("drop table t") - - // for issue #20446 - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c int unsigned default 0);") - tk.MustQuery("show create table `t1`").Check(testkit.RowsWithSep("|", - ""+ - "t1 CREATE TABLE `t1` (\n"+ - " `c` int(10) unsigned DEFAULT '0'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec("drop table t1") - - tk.MustExec("CREATE TABLE `log` (" + - "`LOG_ID` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT," + - "`ROUND_ID` bigint(20) UNSIGNED NOT NULL," + - "`USER_ID` int(10) UNSIGNED NOT NULL," + - "`USER_IP` int(10) UNSIGNED DEFAULT NULL," + - "`END_TIME` datetime NOT NULL," + - "`USER_TYPE` int(11) DEFAULT NULL," + - "`APP_ID` int(11) DEFAULT NULL," + - "PRIMARY KEY (`LOG_ID`,`END_TIME`) NONCLUSTERED," + - "KEY `IDX_EndTime` (`END_TIME`)," + - "KEY `IDX_RoundId` (`ROUND_ID`)," + - "KEY `IDX_UserId_EndTime` (`USER_ID`,`END_TIME`)" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=505488 " + - "PARTITION BY RANGE ( month(`end_time`) ) (" + - "PARTITION `p1` VALUES LESS THAN (2)," + - "PARTITION `p2` VALUES LESS THAN (3)," + - "PARTITION `p3` VALUES LESS THAN (4)," + - "PARTITION `p4` VALUES LESS THAN (5)," + - "PARTITION `p5` VALUES LESS THAN (6)," + - "PARTITION `p6` VALUES LESS THAN (7)," + - "PARTITION `p7` VALUES LESS THAN (8)," + - "PARTITION `p8` VALUES LESS THAN (9)," + - "PARTITION `p9` VALUES LESS THAN (10)," + - "PARTITION `p10` VALUES LESS THAN (11)," + - "PARTITION `p11` VALUES LESS THAN (12)," + - "PARTITION `p12` VALUES LESS THAN (MAXVALUE))") - tk.MustQuery("show create table log").Check(testkit.RowsWithSep("|", - "log CREATE TABLE `log` (\n"+ - " `LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n"+ - " `ROUND_ID` bigint(20) unsigned NOT NULL,\n"+ - " `USER_ID` int(10) unsigned NOT NULL,\n"+ - " `USER_IP` int(10) unsigned DEFAULT NULL,\n"+ - " `END_TIME` datetime NOT NULL,\n"+ - " `USER_TYPE` int(11) DEFAULT NULL,\n"+ - " `APP_ID` int(11) DEFAULT NULL,\n"+ - " PRIMARY KEY (`LOG_ID`,`END_TIME`) /*T![clustered_index] NONCLUSTERED */,\n"+ - " KEY `IDX_EndTime` (`END_TIME`),\n"+ - " KEY `IDX_RoundId` (`ROUND_ID`),\n"+ - " KEY `IDX_UserId_EndTime` (`USER_ID`,`END_TIME`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=505488\n"+ - "PARTITION BY RANGE (MONTH(`end_time`))\n"+ - "(PARTITION `p1` VALUES LESS THAN (2),\n"+ - " PARTITION `p2` VALUES LESS THAN (3),\n"+ - " PARTITION `p3` VALUES LESS THAN (4),\n"+ - " PARTITION `p4` VALUES LESS THAN (5),\n"+ - " PARTITION `p5` VALUES LESS THAN (6),\n"+ - " PARTITION `p6` VALUES LESS THAN (7),\n"+ - " PARTITION `p7` VALUES LESS THAN (8),\n"+ - " PARTITION `p8` VALUES LESS THAN (9),\n"+ - " PARTITION `p9` VALUES LESS THAN (10),\n"+ - " PARTITION `p10` VALUES LESS THAN (11),\n"+ - " PARTITION `p11` VALUES LESS THAN (12),\n"+ - " PARTITION `p12` VALUES LESS THAN (MAXVALUE))")) - - // for issue #11831 - tk.MustExec("create table ttt4(a varchar(123) default null collate utf8mb4_unicode_ci)engine=innodb default charset=utf8mb4 collate=utf8mb4_unicode_ci;") - tk.MustQuery("show create table `ttt4`").Check(testkit.RowsWithSep("|", - ""+ - "ttt4 CREATE TABLE `ttt4` (\n"+ - " `a` varchar(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", - )) - tk.MustExec("create table ttt5(a varchar(123) default null)engine=innodb default charset=utf8mb4 collate=utf8mb4_bin;") - tk.MustQuery("show create table `ttt5`").Check(testkit.RowsWithSep("|", - ""+ - "ttt5 CREATE TABLE `ttt5` (\n"+ - " `a` varchar(123) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // for expression index - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b real);") - tk.MustExec("alter table t add index expr_idx((a*b+1));") - tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` double DEFAULT NULL,\n"+ - " KEY `expr_idx` ((`a` * `b` + 1))\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // Fix issue #15175, show create table sequence_name. - tk.MustExec("drop sequence if exists seq") - tk.MustExec("create sequence seq") - tk.MustQuery("show create table seq;").Check(testkit.Rows("seq CREATE SEQUENCE `seq` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB")) - - // Test for issue #15633, 'binary' collation should be ignored in the result of 'show create table'. - tk.MustExec(`drop table if exists binary_collate`) - tk.MustExec(`create table binary_collate(a varchar(10)) default collate=binary;`) - tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", - ""+ - "binary_collate CREATE TABLE `binary_collate` (\n"+ - " `a` varbinary(10) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=binary", // binary collate is ignored - )) - tk.MustExec(`drop table if exists binary_collate`) - tk.MustExec(`create table binary_collate(a varchar(10)) default charset=binary collate=binary;`) - tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", - ""+ - "binary_collate CREATE TABLE `binary_collate` (\n"+ - " `a` varbinary(10) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=binary", // binary collate is ignored - )) - tk.MustExec(`drop table if exists binary_collate`) - tk.MustExec(`create table binary_collate(a varchar(10)) default charset=utf8mb4 collate=utf8mb4_bin;`) - tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", - ""+ - "binary_collate CREATE TABLE `binary_collate` (\n"+ - " `a` varchar(10) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", // non-binary collate is kept. - )) - // Test for issue #17 in bug competition, default num and sequence should be shown without quote. - tk.MustExec(`drop table if exists default_num`) - tk.MustExec("create table default_num(a int default 11)") - tk.MustQuery("show create table default_num").Check(testkit.RowsWithSep("|", - ""+ - "default_num CREATE TABLE `default_num` (\n"+ - " `a` int(11) DEFAULT '11'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec(`drop table if exists default_varchar`) - tk.MustExec("create table default_varchar(a varchar(10) default \"haha\")") - tk.MustQuery("show create table default_varchar").Check(testkit.RowsWithSep("|", - ""+ - "default_varchar CREATE TABLE `default_varchar` (\n"+ - " `a` varchar(10) DEFAULT 'haha'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec(`drop table if exists default_sequence`) - tk.MustExec("create table default_sequence(a int default nextval(seq))") - tk.MustQuery("show create table default_sequence").Check(testkit.RowsWithSep("|", - ""+ - "default_sequence CREATE TABLE `default_sequence` (\n"+ - " `a` int(11) DEFAULT nextval(`test`.`seq`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // set @@foreign_key_checks=0, - // This means that the child table can be created before the parent table. - // This behavior is required for mysqldump restores. - tk.MustExec("set @@foreign_key_checks=0") - tk.MustExec(`DROP TABLE IF EXISTS parent, child`) - tk.MustExec(`CREATE TABLE child (id INT NOT NULL PRIMARY KEY auto_increment, parent_id INT NOT NULL, INDEX par_ind (parent_id), CONSTRAINT child_ibfk_1 FOREIGN KEY (parent_id) REFERENCES parent(id))`) - tk.MustExec(`CREATE TABLE parent ( id INT NOT NULL PRIMARY KEY auto_increment )`) - tk.MustQuery(`show create table child`).Check(testkit.RowsWithSep("|", - ""+ - "child CREATE TABLE `child` (\n"+ - " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ - " `parent_id` int(11) NOT NULL,\n"+ - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ - " KEY `par_ind` (`parent_id`),\n"+ - " CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `test`.`parent` (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // Test Foreign keys + ON DELETE / ON UPDATE - tk.MustExec(`DROP TABLE child`) - tk.MustExec(`CREATE TABLE child (id INT NOT NULL PRIMARY KEY auto_increment, parent_id INT NOT NULL, INDEX par_ind (parent_id), CONSTRAINT child_ibfk_1 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE RESTRICT ON UPDATE CASCADE)`) - tk.MustQuery(`show create table child`).Check(testkit.RowsWithSep("|", - ""+ - "child CREATE TABLE `child` (\n"+ - " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ - " `parent_id` int(11) NOT NULL,\n"+ - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ - " KEY `par_ind` (`parent_id`),\n"+ - " CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `test`.`parent` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // Test Foreign key refer other database table. - tk.MustExec("create database test1") - tk.MustExec("create database test2") - tk.MustExec("create table test1.t1 (id int key, b int, index(b));") - tk.MustExec("create table test2.t2 (id int key, b int, foreign key fk(b) references test1.t1(id));") - tk.MustQuery("show create table test2.t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + - " `id` int(11) NOT NULL,\n" + - " `b` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `fk` (`b`),\n" + - " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test1`.`t1` (`id`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // Test issue #20327 - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b char(10) as ('a'));") - result := tk.MustQuery("show create table t;").Rows()[0][1] - require.Regexp(t, `(?s).*GENERATED ALWAYS AS \(_utf8mb4'a'\).*`, result) - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b char(10) as (_utf8'a'));") - result = tk.MustQuery("show create table t;").Rows()[0][1] - require.Regexp(t, `(?s).*GENERATED ALWAYS AS \(_utf8'a'\).*`, result) - // Test show list partition table - tk.MustExec("set @@session.tidb_enable_list_partition = ON") - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id)) partition by list (id) ( - partition p0 values in (3,5,6,9,17), - partition p1 values in (1,2,10,11,19,20), - partition p2 values in (4,12,13,14,18), - partition p3 values in (7,8,15,16,null) - );`) - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `id` int(11) DEFAULT NULL,\n"+ - " `name` varchar(10) DEFAULT NULL,\n"+ - " UNIQUE KEY `idx` (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY LIST (`id`)\n"+ - "(PARTITION `p0` VALUES IN (3,5,6,9,17),\n"+ - " PARTITION `p1` VALUES IN (1,2,10,11,19,20),\n"+ - " PARTITION `p2` VALUES IN (4,12,13,14,18),\n"+ - " PARTITION `p3` VALUES IN (7,8,15,16,NULL))")) - // Test show list column partition table - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id)) partition by list columns (id) ( - partition p0 values in (3,5,6,9,17), - partition p1 values in (1,2,10,11,19,20), - partition p2 values in (4,12,13,14,18), - partition p3 values in (7,8,15,16,null) - );`) - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `id` int(11) DEFAULT NULL,\n"+ - " `name` varchar(10) DEFAULT NULL,\n"+ - " UNIQUE KEY `idx` (`id`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY LIST COLUMNS(`id`)\n"+ - "(PARTITION `p0` VALUES IN (3,5,6,9,17),\n"+ - " PARTITION `p1` VALUES IN (1,2,10,11,19,20),\n"+ - " PARTITION `p2` VALUES IN (4,12,13,14,18),\n"+ - " PARTITION `p3` VALUES IN (7,8,15,16,NULL))")) - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id, name)) partition by list columns (id, name) ( - partition p0 values in ((3, '1'), (5, '5')), - partition p1 values in ((1, '1')));`) - // The strings are single quoted in MySQL even if sql_mode doesn't contain ANSI_QUOTES. - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `id` int(11) DEFAULT NULL,\n"+ - " `name` varchar(10) DEFAULT NULL,\n"+ - " UNIQUE KEY `idx` (`id`,`name`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY LIST COLUMNS(`id`,`name`)\n"+ - "(PARTITION `p0` VALUES IN ((3,'1'),(5,'5')),\n"+ - " PARTITION `p1` VALUES IN ((1,'1')))")) - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec(`create table t (id int primary key, v varchar(255) not null, key idx_v (v) comment 'foo\'bar')`) - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `id` int(11) NOT NULL,\n"+ - " `v` varchar(255) NOT NULL,\n"+ - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ - " KEY `idx_v` (`v`) COMMENT 'foo''bar'\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // For issue #29922 - tk.MustExec("CREATE TABLE `thash` (\n `id` bigint unsigned NOT NULL,\n `data` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n)\nPARTITION BY HASH (`id`)\n(PARTITION pEven COMMENT = \"Even ids\",\n PARTITION pOdd COMMENT = \"Odd ids\");") - tk.MustQuery("show create table `thash`").Check(testkit.RowsWithSep("|", ""+ - "thash CREATE TABLE `thash` (\n"+ - " `id` bigint(20) unsigned NOT NULL,\n"+ - " `data` varchar(255) DEFAULT NULL,\n"+ - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY HASH (`id`)\n"+ - "(PARTITION `pEven` COMMENT 'Even ids',\n"+ - " PARTITION `pOdd` COMMENT 'Odd ids')", - )) - // empty edge case - tk.MustExec("drop table if exists `thash`") - tk.MustExec("CREATE TABLE `thash` (\n `id` bigint unsigned NOT NULL,\n `data` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n)\nPARTITION BY HASH (`id`);") - tk.MustQuery("show create table `thash`").Check(testkit.RowsWithSep("|", ""+ - "thash CREATE TABLE `thash` (\n"+ - " `id` bigint(20) unsigned NOT NULL,\n"+ - " `data` varchar(255) DEFAULT NULL,\n"+ - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY HASH (`id`) PARTITIONS 1", - )) - - // default value escape character '\\' display case - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int primary key, b varchar(20) default '\\\\');") - tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) NOT NULL,\n"+ - " `b` varchar(20) DEFAULT '\\\\',\n"+ - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(" + - "a set('a', 'b') charset binary," + - "b enum('a', 'b') charset ascii);") - tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` set('a','b') CHARACTER SET binary COLLATE binary DEFAULT NULL,\n"+ - " `b` enum('a','b') CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a bit default (rand()))`) - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` bit(1) DEFAULT rand()\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec(`drop table if exists t`) - err := tk.ExecToErr(`create table t (a varchar(255) character set ascii) partition by range columns (a) (partition p values less than (0xff))`) - require.ErrorContains(t, err, "[ddl:1654]Partition column values of incorrect type") - tk.MustExec(`create table t (a varchar(255) character set ascii) partition by range columns (a) (partition p values less than (0x7f))`) - tk.MustQuery(`show create table t`).Check(testkit.Rows( - "t CREATE TABLE `t` (\n" + - " `a` varchar(255) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - "PARTITION BY RANGE COLUMNS(`a`)\n" + - "(PARTITION `p` VALUES LESS THAN (x'7f'))")) -} - -func TestShowCreateTablePlacement(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - defer tk.MustExec(`DROP TABLE IF EXISTS t`) - - // case for policy - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create placement policy x " + - "FOLLOWERS=2 " + - "CONSTRAINTS=\"[+disk=ssd]\" ") - defer tk.MustExec(`DROP PLACEMENT POLICY IF EXISTS x`) - tk.MustExec("create table t(a int)" + - "PLACEMENT POLICY=\"x\"") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin "+ - "/*T![placement] PLACEMENT POLICY=`x` */", - )) - - // case for policy with quotes - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int)" + - "/*T![placement] PLACEMENT POLICY=\"x\" */") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin "+ - "/*T![placement] PLACEMENT POLICY=`x` */", - )) - - // Partitioned tables - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("set @old_list_part = @@tidb_enable_list_partition") - defer tk.MustExec("set @@tidb_enable_list_partition = @old_list_part") - tk.MustExec("set tidb_enable_list_partition = 1") - tk.MustExec("create table t(a int, b varchar(255))" + - "/*T![placement] PLACEMENT POLICY=\"x\" */" + - "PARTITION BY LIST (a)\n" + - "(PARTITION pLow VALUES in (1,2,3,5,8) COMMENT 'a comment' placement policy 'x'," + - " PARTITION pMid VALUES in (9) COMMENT 'another comment'," + - "partition pMax values IN (10,11,12))") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n"+ - "PARTITION BY LIST (`a`)\n"+ - "(PARTITION `pLow` VALUES IN (1,2,3,5,8) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMid` VALUES IN (9) COMMENT 'another comment',\n"+ - " PARTITION `pMax` VALUES IN (10,11,12))", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "PARTITION BY LIST COLUMNS (b)\n" + - "(PARTITION pLow VALUES in ('1','2','3','5','8') COMMENT 'a comment' placement policy 'x'," + - "partition pMax values IN ('10','11','12'))") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY LIST COLUMNS(`b`)\n"+ - "(PARTITION `pLow` VALUES IN ('1','2','3','5','8') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMax` VALUES IN ('10','11','12'))", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "PARTITION BY LIST COLUMNS (a,b)\n" + - "(PARTITION pLow VALUES in ((1,'1'),(2,'2'),(3,'3'),(5,'5'),(8,'8')) COMMENT 'a comment' placement policy 'x'," + - "partition pMax values IN ((10,'10'),(11,'11'),(12,'12')))") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY LIST COLUMNS(`a`,`b`)\n"+ - "(PARTITION `pLow` VALUES IN ((1,'1'),(2,'2'),(3,'3'),(5,'5'),(8,'8')) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMax` VALUES IN ((10,'10'),(11,'11'),(12,'12')))", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "PARTITION BY RANGE (a)\n" + - "(PARTITION pLow VALUES less than (1000000) COMMENT 'a comment' placement policy 'x'," + - "partition pMax values LESS THAN (MAXVALUE))") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY RANGE (`a`)\n"+ - "(PARTITION `pLow` VALUES LESS THAN (1000000) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMax` VALUES LESS THAN (MAXVALUE))", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "PARTITION BY RANGE COLUMNS (b)\n" + - "(PARTITION pLow VALUES less than ('1000000') COMMENT 'a comment' placement policy 'x'," + - "partition pMax values LESS THAN (MAXVALUE))") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY RANGE COLUMNS(`b`)\n"+ - "(PARTITION `pLow` VALUES LESS THAN ('1000000') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMax` VALUES LESS THAN (MAXVALUE))", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - - tk.MustExec("create table t(a int, b varchar(255))" + - "/*T![placement] PLACEMENT POLICY=\"x\" */" + - "PARTITION BY RANGE COLUMNS (a,b)\n" + - "(PARTITION pLow VALUES less than (1000000,'1000000') COMMENT 'a comment' placement policy 'x'," + - " PARTITION pMidLow VALUES less than (1000000,MAXVALUE) COMMENT 'another comment' placement policy 'x'," + - " PARTITION pMadMax VALUES less than (9000000,'1000000') COMMENT ='Not a comment' placement policy 'x'," + - "partition pMax values LESS THAN (MAXVALUE, 'Does not matter...'))") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec(`insert into t values (1,'1')`) - tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) - tk.MustQuery(`show create table t`).Check(testkit.Rows( - "t CREATE TABLE `t` (\n" + - " `a` int(11) DEFAULT NULL,\n" + - " `b` varchar(255) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n" + - "PARTITION BY RANGE COLUMNS(`a`,`b`)\n" + - "(PARTITION `pLow` VALUES LESS THAN (1000000,'1000000') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + - " PARTITION `pMidLow` VALUES LESS THAN (1000000,MAXVALUE) COMMENT 'another comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + - " PARTITION `pMadMax` VALUES LESS THAN (9000000,'1000000') COMMENT 'Not a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + - " PARTITION `pMax` VALUES LESS THAN (MAXVALUE,'Does not matter...'))")) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "/*T![placement] PLACEMENT POLICY=\"x\" */" + - "PARTITION BY HASH (a) PARTITIONS 2") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n"+ - "PARTITION BY HASH (`a`) PARTITIONS 2", - )) - - tk.MustExec(`DROP TABLE IF EXISTS t`) - tk.MustExec("create table t(a int, b varchar(255))" + - "PARTITION BY HASH (a)\n" + - "(PARTITION pLow COMMENT 'a comment' placement policy 'x'," + - "partition pMax)") - tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ - "t CREATE TABLE `t` (\n"+ - " `a` int(11) DEFAULT NULL,\n"+ - " `b` varchar(255) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ - "PARTITION BY HASH (`a`)\n"+ - "(PARTITION `pLow` COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ - " PARTITION `pMax`)", - )) - tk.MustExec(`DROP TABLE t`) -} - -func TestShowVisibility(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database showdatabase") - tk.MustExec("use showdatabase") - tk.MustExec("create table t1 (id int)") - tk.MustExec("create table t2 (id int)") - tk.MustExec(`create user 'show'@'%'`) - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show", Hostname: "%"}, nil, nil, nil)) - - // No ShowDatabases privilege, this user would see nothing except INFORMATION_SCHEMA. - tk.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) - - // After grant, the user can see the database. - tk.MustExec(`grant select on showdatabase.t1 to 'show'@'%'`) - tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA", "showdatabase")) - - // The user can see t1 but not t2. - tk1.MustExec("use showdatabase") - tk1.MustQuery("show tables").Check(testkit.Rows("t1")) - - // After revoke, show database result should be just except INFORMATION_SCHEMA. - tk.MustExec(`revoke select on showdatabase.t1 from 'show'@'%'`) - tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) - - // Grant any global privilege would make show databases available. - tk.MustExec(`grant CREATE on *.* to 'show'@'%'`) - rows := tk1.MustQuery("show databases").Rows() - require.GreaterOrEqual(t, len(rows), 2) - - tk.MustExec(`drop user 'show'@'%'`) - tk.MustExec("drop database showdatabase") -} - -func TestShowDatabasesInfoSchemaFirst(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) - tk.MustExec(`create user 'show'@'%'`) - - tk.MustExec(`create database AAAA`) - tk.MustExec(`create database BBBB`) - tk.MustExec(`grant select on AAAA.* to 'show'@'%'`) - tk.MustExec(`grant select on BBBB.* to 'show'@'%'`) - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show", Hostname: "%"}, nil, nil, nil)) - tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA", "AAAA", "BBBB")) - - tk.MustExec(`drop user 'show'@'%'`) - tk.MustExec(`drop database AAAA`) - tk.MustExec(`drop database BBBB`) -} - -func TestShowWarnings(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - testSQL := `create table if not exists show_warnings (a int)` - tk.MustExec(testSQL) - tk.MustExec("set @@sql_mode=''") - tk.MustExec("insert show_warnings values ('a')") - require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1366|Incorrect int value: 'a' for column 'a' at row 1")) - require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1366|Incorrect int value: 'a' for column 'a' at row 1")) - require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - - // Test Warning level 'Error' - testSQL = `create table show_warnings (a int)` - _, _ = tk.Exec(testSQL) - // FIXME: Table 'test.show_warnings' already exists - require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Error|1050|Table 'test.show_warnings' already exists")) - tk.MustQuery("select @@error_count").Check(testkit.RowsWithSep("|", "1")) - - // Test Warning level 'Note' - testSQL = `create table show_warnings_2 (a int)` - tk.MustExec(testSQL) - testSQL = `create table if not exists show_warnings_2 like show_warnings` - _, err := tk.Exec(testSQL) - require.NoError(t, err) - require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1050|Table 'test.show_warnings_2' already exists")) - tk.MustQuery("select @@warning_count").Check(testkit.RowsWithSep("|", "1")) - tk.MustQuery("select @@warning_count").Check(testkit.RowsWithSep("|", "0")) -} - -func TestShowErrors(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - testSQL := `create table if not exists show_errors (a int)` - tk.MustExec(testSQL) - testSQL = `create table show_errors (a int)` - // FIXME: 'test.show_errors' already exists - _, _ = tk.Exec(testSQL) - - tk.MustQuery("show errors").Check(testkit.RowsWithSep("|", "Error|1050|Table 'test.show_errors' already exists")) - - // eliminate previous errors - tk.MustExec("select 1") - _, _ = tk.Exec("create invalid") - tk.MustQuery("show errors").Check(testkit.RowsWithSep("|", "Error|1064|You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 14 near \"invalid\" ")) -} - -func TestShowWarningsForExprPushdown(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set tidb_cost_model_version=2`) - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - testSQL := `create table if not exists show_warnings_expr_pushdown (a int, value date)` - tk.MustExec(testSQL) - - // create tiflash replica - { - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "show_warnings_expr_pushdown" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - } - tk.MustExec("set tidb_allow_mpp=0") - tk.MustExec("explain select * from show_warnings_expr_pushdown t where md5(value) = '2020-01-01'") - require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.")) - tk.MustExec("explain select /*+ read_from_storage(tiflash[show_warnings_expr_pushdown]) */ max(md5(value)) from show_warnings_expr_pushdown group by a") - require.Equal(t, uint16(2), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because arguments of AggFunc `max` contains unsupported exprs")) - tk.MustExec("explain select /*+ read_from_storage(tiflash[show_warnings_expr_pushdown]) */ max(a) from show_warnings_expr_pushdown group by md5(value)") - require.Equal(t, uint16(2), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs")) - tk.MustExec("set tidb_opt_distinct_agg_push_down=0") - tk.MustExec("explain select max(distinct a) from show_warnings_expr_pushdown group by value") - require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) - // tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Aggregation can not be pushed to storage layer in non-mpp mode because it contains agg function with distinct")) -} - -func TestShowGrantsPrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user show_grants") - tk.MustExec("show grants for show_grants") - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show_grants", Hostname: "%"}, nil, nil, nil)) - err := tk1.QueryToErr("show grants for root") - require.EqualError(t, exeerrors.ErrDBaccessDenied.GenWithStackByArgs("show_grants", "%", mysql.SystemDB), err.Error()) - // Test show grants for user with auth host name `%`. - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "show_grants", Hostname: "127.0.0.1", AuthUsername: "show_grants", AuthHostname: "%"}, nil, nil, nil)) - tk2.MustQuery("show grants") -} - -func TestShowStatsPrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user show_stats") - tk1 := testkit.NewTestKit(t, store) - - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show_stats", Hostname: "%"}, nil, nil, nil)) - e := "[planner:1142]SHOW command denied to user 'show_stats'@'%' for table" - err := tk1.ExecToErr("show stats_meta") - require.ErrorContains(t, err, e) - err = tk1.ExecToErr("SHOW STATS_BUCKETS") - require.ErrorContains(t, err, e) - err = tk1.ExecToErr("SHOW STATS_HISTOGRAMS") - require.ErrorContains(t, err, e) - - eqErr := plannercore.ErrDBaccessDenied.GenWithStackByArgs("show_stats", "%", mysql.SystemDB) - err = tk1.ExecToErr("SHOW STATS_HEALTHY") - require.EqualError(t, err, eqErr.Error()) - tk.MustExec("grant select on mysql.* to show_stats") - tk1.MustExec("show stats_meta") - tk1.MustExec("SHOW STATS_BUCKETS") - tk1.MustExec("SHOW STATS_HEALTHY") - tk1.MustExec("SHOW STATS_HISTOGRAMS") - - tk.MustExec("create user a@'%' identified by '';") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "a", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("grant select on mysql.stats_meta to a@'%';") - tk.MustExec("grant select on mysql.stats_buckets to a@'%';") - tk.MustExec("grant select on mysql.stats_histograms to a@'%';") - tk1.MustExec("show stats_meta") - tk1.MustExec("SHOW STATS_BUCKETS") - tk1.MustExec("SHOW STATS_HISTOGRAMS") -} - -func TestIssue18878(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "127.0.0.1", AuthHostname: "%"}, nil, nil, nil)) - tk.MustQuery("select user()").Check(testkit.Rows("root@127.0.0.1")) - tk.MustQuery("show grants") - tk.MustQuery("select user()").Check(testkit.Rows("root@127.0.0.1")) - err := tk.QueryToErr("show grants for root@127.0.0.1") - require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "127.0.0.1").Error(), err.Error()) - err = tk.QueryToErr("show grants for root@localhost") - require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "localhost").Error(), err.Error()) - err = tk.QueryToErr("show grants for root@1.1.1.1") - require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "1.1.1.1").Error(), err.Error()) - tk.MustExec("create user `show_grants`@`127.0.%`") - err = tk.QueryToErr("show grants for `show_grants`@`127.0.0.1`") - require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("show_grants", "127.0.0.1").Error(), err.Error()) - tk.MustQuery("show grants for `show_grants`@`127.0.%`") -} - -func TestIssue17794(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER 'root'@'8.8.%'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "9.9.9.9", AuthHostname: "%"}, nil, nil, nil)) - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "8.8.8.8", AuthHostname: "8.8.%"}, nil, nil, nil)) - tk.MustQuery("show grants").Check(testkit.Rows("GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION")) - tk1.MustQuery("show grants").Check(testkit.Rows("GRANT USAGE ON *.* TO 'root'@'8.8.%'")) -} - -func TestIssue3641(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustGetErrCode("show tables;", mysql.ErrNoDB) - tk.MustGetErrCode("show tables;", mysql.ErrNoDB) -} - -func TestIssue10549(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE newdb;") - tk.MustExec("CREATE ROLE 'app_developer';") - tk.MustExec("GRANT ALL ON newdb.* TO 'app_developer';") - tk.MustExec("CREATE USER 'dev';") - tk.MustExec("GRANT 'app_developer' TO 'dev';") - tk.MustExec("SET DEFAULT ROLE app_developer TO 'dev';") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dev", Hostname: "%", AuthUsername: "dev", AuthHostname: "%"}, nil, nil, nil)) - tk.MustQuery("SHOW DATABASES;").Check(testkit.Rows("INFORMATION_SCHEMA", "newdb")) - tk.MustQuery("SHOW GRANTS;").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT ALL PRIVILEGES ON `newdb`.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) - tk.MustQuery("SHOW GRANTS FOR CURRENT_USER").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT ALL PRIVILEGES ON `newdb`.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) - tk.MustQuery("SHOW GRANTS FOR dev").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) -} - -func TestIssue11165(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE ROLE 'r_manager';") - tk.MustExec("CREATE USER 'manager'@'localhost';") - tk.MustExec("GRANT 'r_manager' TO 'manager'@'localhost';") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "manager", Hostname: "localhost", AuthUsername: "manager", AuthHostname: "localhost"}, nil, nil, nil)) - tk.MustExec("SET DEFAULT ROLE ALL TO 'manager'@'localhost';") - tk.MustExec("SET DEFAULT ROLE NONE TO 'manager'@'localhost';") - tk.MustExec("SET DEFAULT ROLE 'r_manager' TO 'manager'@'localhost';") -} - -// TestShow2 is moved from session_test -func TestShow2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("set global autocommit=0") - tk1 := testkit.NewTestKit(t, store) - tk1.MustQuery("show global variables where variable_name = 'autocommit'").Check(testkit.Rows("autocommit OFF")) - tk.MustExec("set global autocommit = 1") - tk2 := testkit.NewTestKit(t, store) - tk2.MustQuery("show global variables where variable_name = 'autocommit'").Check(testkit.Rows("autocommit ON")) - - // TODO: Specifying the charset for national char/varchar should not be supported. - tk.MustExec("drop table if exists test_full_column") - tk.MustExec(`create table test_full_column( - c_int int, - c_float float, - c_bit bit, - c_bool bool, - c_char char(1) charset ascii collate ascii_bin, - c_nchar national char(1) charset ascii collate ascii_bin, - c_binary binary, - c_varchar varchar(1) charset ascii collate ascii_bin, - c_varchar_default varchar(20) charset ascii collate ascii_bin default 'cUrrent_tImestamp', - c_nvarchar national varchar(1) charset ascii collate ascii_bin, - c_varbinary varbinary(1), - c_year year, - c_date date, - c_time time, - c_datetime datetime, - c_datetime_default datetime default current_timestamp, - c_datetime_default_2 datetime(2) default current_timestamp(2), - c_timestamp timestamp, - c_timestamp_default timestamp default current_timestamp, - c_timestamp_default_3 timestamp(3) default current_timestamp(3), - c_timestamp_default_4 timestamp(3) default current_timestamp(3) on update current_timestamp(3), - c_date_default date default current_date, - c_date_default_2 date default (curdate()), - c_blob blob, - c_tinyblob tinyblob, - c_mediumblob mediumblob, - c_longblob longblob, - c_text text charset ascii collate ascii_bin, - c_tinytext tinytext charset ascii collate ascii_bin, - c_mediumtext mediumtext charset ascii collate ascii_bin, - c_longtext longtext charset ascii collate ascii_bin, - c_json json, - c_enum enum('1') charset ascii collate ascii_bin, - c_set set('1') charset ascii collate ascii_bin - );`) - - tk.MustQuery(`show full columns from test_full_column`).Check(testkit.Rows( - "" + - "c_int int(11) YES select,insert,update,references ]\n" + - "[c_float float YES select,insert,update,references ]\n" + - "[c_bit bit(1) YES select,insert,update,references ]\n" + - "[c_bool tinyint(1) YES select,insert,update,references ]\n" + - "[c_char char(1) ascii_bin YES select,insert,update,references ]\n" + - "[c_nchar char(1) ascii_bin YES select,insert,update,references ]\n" + - "[c_binary binary(1) YES select,insert,update,references ]\n" + - "[c_varchar varchar(1) ascii_bin YES select,insert,update,references ]\n" + - "[c_varchar_default varchar(20) ascii_bin YES cUrrent_tImestamp select,insert,update,references ]\n" + - "[c_nvarchar varchar(1) ascii_bin YES select,insert,update,references ]\n" + - "[c_varbinary varbinary(1) YES select,insert,update,references ]\n" + - "[c_year year(4) YES select,insert,update,references ]\n" + - "[c_date date YES select,insert,update,references ]\n" + - "[c_time time YES select,insert,update,references ]\n" + - "[c_datetime datetime YES select,insert,update,references ]\n" + - "[c_datetime_default datetime YES CURRENT_TIMESTAMP select,insert,update,references ]\n" + - "[c_datetime_default_2 datetime(2) YES CURRENT_TIMESTAMP(2) select,insert,update,references ]\n" + - "[c_timestamp timestamp YES select,insert,update,references ]\n" + - "[c_timestamp_default timestamp YES CURRENT_TIMESTAMP select,insert,update,references ]\n" + - "[c_timestamp_default_3 timestamp(3) YES CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" + - "[c_timestamp_default_4 timestamp(3) YES CURRENT_TIMESTAMP(3) DEFAULT_GENERATED on update CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" + - "[c_date_default date YES CURRENT_DATE select,insert,update,references ]\n" + - "[c_date_default_2 date YES CURRENT_DATE select,insert,update,references ]\n" + - "[c_blob blob YES select,insert,update,references ]\n" + - "[c_tinyblob tinyblob YES select,insert,update,references ]\n" + - "[c_mediumblob mediumblob YES select,insert,update,references ]\n" + - "[c_longblob longblob YES select,insert,update,references ]\n" + - "[c_text text ascii_bin YES select,insert,update,references ]\n" + - "[c_tinytext tinytext ascii_bin YES select,insert,update,references ]\n" + - "[c_mediumtext mediumtext ascii_bin YES select,insert,update,references ]\n" + - "[c_longtext longtext ascii_bin YES select,insert,update,references ]\n" + - "[c_json json YES select,insert,update,references ]\n" + - "[c_enum enum('1') ascii_bin YES select,insert,update,references ]\n" + - "[c_set set('1') ascii_bin YES select,insert,update,references ")) - - tk.MustExec("drop table if exists test_full_column") - - tk.MustExec("drop table if exists t") - tk.MustExec(`create table if not exists t (c int) comment '注释'`) - tk.MustExec("create or replace definer='root'@'localhost' view v as select * from t") - tk.MustQuery(`show columns from t`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) - tk.MustQuery(`describe t`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) - tk.MustQuery(`show columns from v`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) - tk.MustQuery(`describe v`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) - tk.MustQuery("show collation where Charset = 'utf8' and Collation = 'utf8_bin'").Check(testkit.RowsWithSep(",", "utf8_bin,utf8,83,Yes,Yes,1")) - tk.MustExec(`drop sequence if exists seq`) - tk.MustExec(`create sequence seq`) - tk.MustQuery("show tables").Check(testkit.Rows("seq", "t", "v")) - tk.MustQuery("show full tables").Check(testkit.Rows("seq SEQUENCE", "t BASE TABLE", "v VIEW")) - - // Bug 19427 - tk.MustQuery("SHOW FULL TABLES in INFORMATION_SCHEMA like 'VIEWS'").Check(testkit.Rows("VIEWS SYSTEM VIEW")) - tk.MustQuery("SHOW FULL TABLES in information_schema like 'VIEWS'").Check(testkit.Rows("VIEWS SYSTEM VIEW")) - tk.MustQuery("SHOW FULL TABLES in metrics_schema like 'uptime'").Check(testkit.Rows("uptime SYSTEM VIEW")) - - is := dom.InfoSchema() - tblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - createTime := model.TSConvert2Time(tblInfo.Meta().UpdateTS).Format(time.DateTime) - - // The Hostname is the actual host - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - - r := tk.MustQuery("show table status from test like 't'") - r.Check(testkit.Rows(fmt.Sprintf("t InnoDB 10 Compact 0 0 0 0 0 0 %s utf8mb4_bin 注释", createTime))) - - tk.MustQuery("show databases like 'test'").Check(testkit.Rows("test")) - - tk.MustExec(`grant all on *.* to 'root'@'%'`) - tk.MustQuery("show grants").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) - - tk.MustQuery("show grants for current_user()").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) - tk.MustQuery("show grants for current_user").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) -} - -func TestShowCreateUser(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // Create a new user. - tk.MustExec(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED BY 'root';`) - tk.MustQuery("show create user 'test_show_create_user'@'%'"). - Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - - tk.MustExec(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED BY 'test';`) - tk.MustQuery("show create user 'test_show_create_user'@'localhost';"). - Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - - // Case: the user exists but the host portion doesn't match - err := tk.QueryToErr("show create user 'test_show_create_user'@'asdf';") - require.Equal(t, exeerrors.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'test_show_create_user'@'asdf'").Error(), err.Error()) - - // Case: a user that doesn't exist - err = tk.QueryToErr("show create user 'aaa'@'localhost';") - require.Equal(t, exeerrors.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'aaa'@'localhost'").Error(), err.Error()) - - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "127.0.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, nil, nil) - tk.MustQuery("show create user current_user"). - Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) - - tk.MustQuery("show create user current_user()"). - Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) - - tk.MustExec("create user 'check_priv'") - - // "show create user" for other user requires the SELECT privilege on mysql database. - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use mysql") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "check_priv", Hostname: "127.0.0.1", AuthUsername: "test_show", AuthHostname: "asdf"}, nil, nil, nil)) - err = tk1.QueryToErr("show create user 'root'@'%'") - require.Error(t, err) - - // "show create user" for current user doesn't check privileges. - tk1.MustQuery("show create user current_user"). - Check(testkit.Rows("CREATE USER 'check_priv'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) - - // Creating users with `IDENTIFIED WITH 'caching_sha2_password'`. - tk.MustExec("CREATE USER 'sha_test'@'%' IDENTIFIED WITH 'caching_sha2_password' BY 'temp_passwd'") - - // Compare only the start of the output as the salt changes every time. - rows := tk.MustQuery("SHOW CREATE USER 'sha_test'@'%'") - require.Equal(t, "CREATE USER 'sha_test'@'%' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$", rows.Rows()[0][0].(string)[:78]) - // Creating users with `IDENTIFIED WITH 'auth-socket'` - tk.MustExec("CREATE USER 'sock'@'%' IDENTIFIED WITH 'auth_socket'") - - // Compare only the start of the output as the salt changes every time. - rows = tk.MustQuery("SHOW CREATE USER 'sock'@'%'") - require.Equal(t, "CREATE USER 'sock'@'%' IDENTIFIED WITH 'auth_socket' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) - tk.MustExec("CREATE USER 'sock2'@'%' IDENTIFIED WITH 'auth_socket' AS 'sock3'") - - // Compare only the start of the output as the salt changes every time. - rows = tk.MustQuery("SHOW CREATE USER 'sock2'@'%'") - require.Equal(t, "CREATE USER 'sock2'@'%' IDENTIFIED WITH 'auth_socket' AS 'sock3' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) - - // Test ACCOUNT LOCK/UNLOCK. - tk.MustExec("CREATE USER 'lockness'@'%' IDENTIFIED BY 'monster' ACCOUNT LOCK") - rows = tk.MustQuery("SHOW CREATE USER 'lockness'@'%'") - require.Equal(t, "CREATE USER 'lockness'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*BC05309E7FE12AFD4EBB9FFE7E488A6320F12FF3' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) - - // Test COMMENT and ATTRIBUTE. - tk.MustExec("CREATE USER commentUser COMMENT '1234'") - tk.MustQuery("SHOW CREATE USER commentUser").Check(testkit.Rows(`CREATE USER 'commentUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"comment": "1234"}'`)) - tk.MustExec(`CREATE USER attributeUser attribute '{"name": "Tom", "age": 19}'`) - tk.MustQuery("SHOW CREATE USER attributeUser").Check(testkit.Rows(`CREATE USER 'attributeUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"age": 19, "name": "Tom"}'`)) - - // Creating users with IDENTIFIED WITH 'tidb_auth_token'. - tk.MustExec(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' ATTRIBUTE '{"email": "user@pingcap.com"}'`) - tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"email": "user@pingcap.com"}'`)) - tk.MustExec(`ALTER USER 'token_user'@'%' REQUIRE token_issuer 'issuer-ABC'`) - tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE token_issuer issuer-ABC PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"email": "user@pingcap.com"}'`)) - - // create users with password reuse. - tk.MustExec(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 3 DAY`) - tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 3 DAY`)) - tk.MustExec(`ALTER USER 'reuse_user'@'%' PASSWORD HISTORY 50`) - tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 3 DAY`)) - tk.MustExec(`ALTER USER 'reuse_user'@'%' PASSWORD REUSE INTERVAL 31 DAY`) - tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 31 DAY`)) - - tk.MustExec("CREATE USER 'jeffrey1'@'localhost' PASSWORD EXPIRE") - tk.MustQuery("SHOW CREATE USER 'jeffrey1'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey1'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - tk.MustExec("CREATE USER 'jeffrey2'@'localhost' PASSWORD EXPIRE DEFAULT") - tk.MustQuery("SHOW CREATE USER 'jeffrey2'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey2'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - tk.MustExec("CREATE USER 'jeffrey3'@'localhost' PASSWORD EXPIRE NEVER") - tk.MustQuery("SHOW CREATE USER 'jeffrey3'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey3'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE NEVER ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - tk.MustExec("CREATE USER 'jeffrey4'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY") - tk.MustQuery("SHOW CREATE USER 'jeffrey4'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey4'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE INTERVAL 180 DAY ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - - tk.MustExec("CREATE USER failed_login_user") - tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) - tk.MustExec("ALTER USER failed_login_user FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME 2") - tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME 2`)) - tk.MustExec("ALTER USER failed_login_user PASSWORD_LOCK_TIME UNBOUNDED") - tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED`)) - tk.MustExec("ALTER USER failed_login_user comment 'testcomment'") - tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED ATTRIBUTE '{"comment": "testcomment"}'`)) - tk.MustExec("ALTER USER failed_login_user ATTRIBUTE '{\"attribute\": \"testattribute\"}'") - tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED ATTRIBUTE '{"attribute": "testattribute", "comment": "testcomment"}'`)) -} - -func TestUnprivilegedShow(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE testshow") - tk.MustExec("USE testshow") - tk.MustExec("CREATE TABLE t1 (a int)") - tk.MustExec("CREATE TABLE t2 (a int)") - - tk.MustExec(`CREATE USER 'lowprivuser'`) // no grants - - tk.Session().Auth(&auth.UserIdentity{Username: "lowprivuser", Hostname: "192.168.0.1", AuthUsername: "lowprivuser", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - rs, err := tk.Exec("SHOW TABLE STATUS FROM testshow") - require.NoError(t, err) - require.NotNil(t, rs) - - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - tk.MustExec("GRANT ALL ON testshow.t1 TO 'lowprivuser'") - tk.Session().Auth(&auth.UserIdentity{Username: "lowprivuser", Hostname: "192.168.0.1", AuthUsername: "lowprivuser", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - - is := dom.InfoSchema() - tblInfo, err := is.TableByName(model.NewCIStr("testshow"), model.NewCIStr("t1")) - require.NoError(t, err) - createTime := model.TSConvert2Time(tblInfo.Meta().UpdateTS).Format(time.DateTime) - - tk.MustQuery("show table status from testshow").Check(testkit.Rows(fmt.Sprintf("t1 InnoDB 10 Compact 0 0 0 0 0 0 %s utf8mb4_bin ", createTime))) -} - -func TestCollation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - rs, err := tk.Exec("show collation;") - require.NoError(t, err) - fields := rs.Fields() - require.Equal(t, mysql.TypeVarchar, fields[0].Column.GetType()) - require.Equal(t, mysql.TypeVarchar, fields[1].Column.GetType()) - require.Equal(t, mysql.TypeLonglong, fields[2].Column.GetType()) - require.Equal(t, mysql.TypeVarchar, fields[3].Column.GetType()) - require.Equal(t, mysql.TypeVarchar, fields[4].Column.GetType()) - require.Equal(t, mysql.TypeLonglong, fields[5].Column.GetType()) -} - -func TestShowTableStatus(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a bigint);`) - - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - - // It's not easy to test the result contents because every time the test runs, "Create_time" changed. - tk.MustExec("show table status;") - rs, err := tk.Exec("show table status;") - require.NoError(t, err) - require.NotNil(t, rs) - rows, err := session.GetRows4Test(context.Background(), tk.Session(), rs) - require.NoError(t, err) - err = rs.Close() - require.NoError(t, err) - require.Equal(t, 1, len(rows)) - - for i := range rows { - row := rows[i] - require.Equal(t, "t", row.GetString(0)) - require.Equal(t, "InnoDB", row.GetString(1)) - require.Equal(t, int64(10), row.GetInt64(2)) - require.Equal(t, "Compact", row.GetString(3)) - } - tk.MustExec(`drop table if exists tp;`) - tk.MustExec(`create table tp (a int) - partition by range(a) - ( partition p0 values less than (10), - partition p1 values less than (20), - partition p2 values less than (maxvalue) - );`) - rs, err = tk.Exec("show table status from test like 'tp';") - require.NoError(t, err) - rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) - require.NoError(t, err) - require.Equal(t, "partitioned", rows[0].GetString(16)) - - tk.MustExec("create database UPPER_CASE") - tk.MustExec("use UPPER_CASE") - tk.MustExec("create table t (i int)") - rs, err = tk.Exec("show table status") - require.NoError(t, err) - require.NotNil(t, rs) - rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) - require.NoError(t, err) - err = rs.Close() - require.NoError(t, err) - require.Equal(t, 1, len(rows)) - - tk.MustExec("use upper_case") - rs, err = tk.Exec("show table status") - require.NoError(t, err) - require.NotNil(t, rs) - rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) - require.NoError(t, err) - err = rs.Close() - require.NoError(t, err) - require.Equal(t, 1, len(rows)) - - tk.MustExec("drop database UPPER_CASE") -} - -func TestShowSlow(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // The test result is volatile, because - // 1. Slow queries is stored in domain, which may be affected by other tests. - // 2. Collecting slow queries is a asynchronous process, check immediately may not get the expected result. - // 3. Make slow query like "select sleep(1)" would slow the CI. - // So, we just cover the code but do not check the result. - tk.MustQuery(`admin show slow recent 3`) - tk.MustQuery(`admin show slow top 3`) - tk.MustQuery(`admin show slow top internal 3`) - tk.MustQuery(`admin show slow top all 3`) -} - -func TestShowCreateStmtIgnoreLocalTemporaryTables(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // SHOW CREATE VIEW ignores local temporary table with the same name - tk.MustExec("drop view if exists v1") - tk.MustExec("create view v1 as select 1") - tk.MustExec("create temporary table v1 (a int)") - tk.MustQuery("show create table v1").Check(testkit.RowsWithSep("|", - ""+ - "v1 CREATE TEMPORARY TABLE `v1` (\n"+ - " `a` int(11) DEFAULT NULL\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - tk.MustExec("drop view v1") - err := tk.ExecToErr("show create view v1") - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - - // SHOW CREATE SEQUENCE ignores local temporary table with the same name - tk.MustExec("drop view if exists seq1") - tk.MustExec("create sequence seq1") - tk.MustExec("create temporary table seq1 (a int)") - tk.MustQuery("show create sequence seq1").Check(testkit.RowsWithSep("|", - "seq1 CREATE SEQUENCE `seq1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB", - )) - tk.MustExec("drop sequence seq1") - err = tk.ExecToErr("show create sequence seq1") - require.True(t, infoschema.ErrTableNotExists.Equal(err)) -} - -func TestAutoRandomBase(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@allow_auto_random_explicit_insert = true") - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a bigint primary key auto_random(5), b int unique key auto_increment) auto_random_base = 100, auto_increment = 100") - tk.MustQuery("show create table t").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` bigint(20) NOT NULL /*T![auto_rand] AUTO_RANDOM(5) */,\n"+ - " `b` int(11) NOT NULL AUTO_INCREMENT,\n"+ - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n"+ - " UNIQUE KEY `b` (`b`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=100 /*T![auto_rand_base] AUTO_RANDOM_BASE=100 */", - )) - - tk.MustExec("insert into t(`a`) values (1000)") - tk.MustQuery("show create table t").Check(testkit.RowsWithSep("|", - ""+ - "t CREATE TABLE `t` (\n"+ - " `a` bigint(20) NOT NULL /*T![auto_rand] AUTO_RANDOM(5) */,\n"+ - " `b` int(11) NOT NULL AUTO_INCREMENT,\n"+ - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n"+ - " UNIQUE KEY `b` (`b`)\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=5100 /*T![auto_rand_base] AUTO_RANDOM_BASE=6001 */", - )) -} - -func TestAutoRandomWithLargeSignedShowTableRegions(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database if not exists auto_random_db;") - defer tk.MustExec("drop database if exists auto_random_db;") - tk.MustExec("use auto_random_db;") - tk.MustExec("drop table if exists t;") - - tk.MustExec("create table t (a bigint unsigned auto_random primary key clustered);") - tk.MustExec("set @@global.tidb_scatter_region=1;") - // 18446744073709541615 is MaxUint64 - 10000. - // 18446744073709551615 is the MaxUint64. - tk.MustQuery("split table t between (18446744073709541615) and (18446744073709551615) regions 2;"). - Check(testkit.Rows("1 1")) - startKey := tk.MustQuery("show table t regions;").Rows()[1][1].(string) - idx := strings.Index(startKey, "_r_") - require.False(t, idx == -1) - require.Falsef(t, startKey[idx+3] == '-', "actual key: %s", startKey) -} - -func TestShowEscape(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists `t``abl\"e`") - tk.MustExec("create table `t``abl\"e`(`c``olum\"n` int(11) primary key)") - tk.MustQuery("show create table `t``abl\"e`").Check(testkit.RowsWithSep("|", - ""+ - "t`abl\"e CREATE TABLE `t``abl\"e` (\n"+ - " `c``olum\"n` int(11) NOT NULL,\n"+ - " PRIMARY KEY (`c``olum\"n`) /*T![clustered_index] CLUSTERED */\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - // ANSI_QUOTES will change the SHOW output - tk.MustExec("set @old_sql_mode=@@sql_mode") - tk.MustExec("set sql_mode=ansi_quotes") - tk.MustQuery("show create table \"t`abl\"\"e\"").Check(testkit.RowsWithSep("|", - ""+ - "t`abl\"e CREATE TABLE \"t`abl\"\"e\" (\n"+ - " \"c`olum\"\"n\" int(11) NOT NULL,\n"+ - " PRIMARY KEY (\"c`olum\"\"n\") /*T![clustered_index] CLUSTERED */\n"+ - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", - )) - - tk.MustExec("rename table \"t`abl\"\"e\" to t") - tk.MustExec("set sql_mode=@old_sql_mode") -} - -func TestShowBuiltin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - res := tk.MustQuery("show builtins;") - require.NotNil(t, res) - rows := res.Rows() - const builtinFuncNum = 291 - require.Equal(t, builtinFuncNum, len(rows)) - require.Equal(t, rows[0][0].(string), "abs") - require.Equal(t, rows[builtinFuncNum-1][0].(string), "yearweek") -} - -func TestShowClusterConfig(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - var confItems [][]types.Datum - var confErr error - var confFunc executor.TestShowClusterConfigFunc = func() ([][]types.Datum, error) { - return confItems, confErr - } - tk.Session().SetValue(executor.TestShowClusterConfigKey, confFunc) - strs2Items := func(strs ...string) []types.Datum { - items := make([]types.Datum, 0, len(strs)) - for _, s := range strs { - items = append(items, types.NewStringDatum(s)) - } - return items - } - confItems = append(confItems, strs2Items("tidb", "127.0.0.1:1111", "log.level", "info")) - confItems = append(confItems, strs2Items("pd", "127.0.0.1:2222", "log.level", "info")) - confItems = append(confItems, strs2Items("tikv", "127.0.0.1:3333", "log.level", "info")) - tk.MustQuery("show config").Check(testkit.Rows( - "tidb 127.0.0.1:1111 log.level info", - "pd 127.0.0.1:2222 log.level info", - "tikv 127.0.0.1:3333 log.level info")) - tk.MustQuery("show config where type='tidb'").Check(testkit.Rows( - "tidb 127.0.0.1:1111 log.level info")) - tk.MustQuery("show config where type like '%ti%'").Check(testkit.Rows( - "tidb 127.0.0.1:1111 log.level info", - "tikv 127.0.0.1:3333 log.level info")) - - confErr = fmt.Errorf("something unknown error") - require.EqualError(t, tk.QueryToErr("show config"), confErr.Error()) -} - -func TestInvisibleCoprCacheConfig(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - rows := tk.MustQuery("show variables like '%config%'").Rows() - require.Equal(t, 1, len(rows)) - configValue := rows[0][1].(string) - coprCacheVal := - "\t\t\"copr-cache\": {\n" + - "\t\t\t\"capacity-mb\": 1000\n" + - "\t\t},\n" - require.Equal(t, true, strings.Contains(configValue, coprCacheVal)) -} - -func TestEnableGlobalKillConfig(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - rows := tk.MustQuery("show variables like '%config%'").Rows() - require.Equal(t, 1, len(rows)) - configValue := rows[0][1].(string) - globalKillVal := "\"enable-global-kill\": true" - require.True(t, strings.Contains(configValue, globalKillVal)) -} - -func TestShowCreateTableWithIntegerDisplayLengthWarnings(t *testing.T) { - parsertypes.TiDBStrictIntegerDisplayWidth = true - defer func() { - parsertypes.TiDBStrictIntegerDisplayWidth = false - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int(2), b varchar(2))") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` int DEFAULT NULL,\n" + - " `b` varchar(2) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a bigint(10), b bigint)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` bigint DEFAULT NULL,\n" + - " `b` bigint DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a tinyint(5), b tinyint(2), c tinyint)") - // Here it will occur 2 warnings. - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` tinyint DEFAULT NULL,\n" + - " `b` tinyint DEFAULT NULL,\n" + - " `c` tinyint DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a smallint(5), b smallint)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` smallint DEFAULT NULL,\n" + - " `b` smallint DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a mediumint(5), b mediumint)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` mediumint DEFAULT NULL,\n" + - " `b` mediumint DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int1(1), b int2(2), c int3, d int4, e int8)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `a` tinyint(1) DEFAULT NULL,\n" + - " `b` smallint DEFAULT NULL,\n" + - " `c` mediumint DEFAULT NULL,\n" + - " `d` int DEFAULT NULL,\n" + - " `e` bigint DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int primary key, c1 bool, c2 int(10) zerofill)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1681 Integer display width is deprecated and will be removed in a future release.", - "Warning 1681 The ZEROFILL attribute is deprecated and will be removed in a future release. Use the LPAD function to zero-pad numbers, or store the formatted numbers in a CHAR column.", - )) - tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + - " `id` int NOT NULL,\n" + - " `c1` tinyint(1) DEFAULT NULL,\n" + - " `c2` int(10) unsigned zerofill DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func TestShowVar(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - var showSQL string - sessionVars := make([]string, 0, len(variable.GetSysVars())) - globalVars := make([]string, 0, len(variable.GetSysVars())) - for _, v := range variable.GetSysVars() { - if v.Scope == variable.ScopeSession { - sessionVars = append(sessionVars, v.Name) - } else { - globalVars = append(globalVars, v.Name) - } - } - - // When ScopeSession only. `show global variables` must return empty. - sessionVarsStr := strings.Join(sessionVars, "','") - showSQL = "show variables where variable_name in('" + sessionVarsStr + "')" - res := tk.MustQuery(showSQL) - require.Len(t, res.Rows(), len(sessionVars)) - showSQL = "show global variables where variable_name in('" + sessionVarsStr + "')" - res = tk.MustQuery(showSQL) - require.Len(t, res.Rows(), 0) - - globalVarsStr := strings.Join(globalVars, "','") - showSQL = "show variables where variable_name in('" + globalVarsStr + "')" - res = tk.MustQuery(showSQL) - require.Len(t, res.Rows(), len(globalVars)) - showSQL = "show global variables where variable_name in('" + globalVarsStr + "')" - res = tk.MustQuery(showSQL) - require.Len(t, res.Rows(), len(globalVars)) - - // Test versions' related variables - res = tk.MustQuery("show variables like 'version%'") - for _, row := range res.Rows() { - line := fmt.Sprint(row) - if strings.HasPrefix(line, "version ") { - require.Equal(t, mysql.ServerVersion, line[len("version "):]) - } else if strings.HasPrefix(line, "version_comment ") { - require.Equal(t, variable.GetSysVar(variable.VersionComment), line[len("version_comment "):]) - } - } - - // Test case insensitive case for show session variables - tk.MustExec("SET @@SQL_MODE='NO_BACKSLASH_ESCAPES'") - tk.MustQuery("SHOW SESSION VARIABLES like 'sql_mode'").Check( - testkit.RowsWithSep("|", "sql_mode|NO_BACKSLASH_ESCAPES")) - tk.MustQuery("SHOW SESSION VARIABLES like 'SQL_MODE'").Check( - testkit.RowsWithSep("|", "sql_mode|NO_BACKSLASH_ESCAPES")) -} - -// TestShowPerformanceSchema tests for Issue 19231 -func TestShowPerformanceSchema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // Ideally we should create a new performance_schema table here with indices that we run the tests on. - // However, its not possible to create a new performance_schema table since its a special in memory table. - // Instead the test below uses the default index on the table. - tk.MustQuery("SHOW INDEX FROM performance_schema.events_statements_summary_by_digest").Check( - testkit.Rows("events_statements_summary_by_digest 0 SCHEMA_NAME 1 SCHEMA_NAME A 0 YES BTREE YES NO", - "events_statements_summary_by_digest 0 SCHEMA_NAME 2 DIGEST A 0 YES BTREE YES NO")) -} - -func TestShowCreatePlacementPolicy(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE PLACEMENT POLICY xyz PRIMARY_REGION='us-east-1' REGIONS='us-east-1,us-east-2' FOLLOWERS=4") - tk.MustQuery("SHOW CREATE PLACEMENT POLICY xyz").Check(testkit.Rows("xyz CREATE PLACEMENT POLICY `xyz` PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-east-2\" FOLLOWERS=4")) - // non existent policy - err := tk.QueryToErr("SHOW CREATE PLACEMENT POLICY doesnotexist") - require.Equal(t, infoschema.ErrPlacementPolicyNotExists.GenWithStackByArgs("doesnotexist").Error(), err.Error()) - // alter and try second example - tk.MustExec("ALTER PLACEMENT POLICY xyz FOLLOWERS=4") - tk.MustQuery("SHOW CREATE PLACEMENT POLICY xyz").Check(testkit.Rows("xyz CREATE PLACEMENT POLICY `xyz` FOLLOWERS=4")) - tk.MustExec("DROP PLACEMENT POLICY xyz") -} - -func TestShowTemporaryTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create global temporary table t1 (id int) on commit delete rows") - tk.MustExec("create global temporary table t3 (i int primary key, j int) on commit delete rows") - // For issue https://github.com/pingcap/tidb/issues/24752 - tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE GLOBAL TEMPORARY TABLE `t1` (\n" + - " `id` int(11) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS")) - // No panic, fix issue https://github.com/pingcap/tidb/issues/24788 - expect := "CREATE GLOBAL TEMPORARY TABLE `t3` (\n" + - " `i` int(11) NOT NULL,\n" + - " `j` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS" - tk.MustQuery("show create table t3").Check(testkit.Rows("t3 " + expect)) - - // Verify that the `show create table` result can be used to build the table. - createTable := strings.ReplaceAll(expect, "t3", "t4") - tk.MustExec(createTable) - - // Cover auto increment column. - tk.MustExec(`CREATE GLOBAL TEMPORARY TABLE t5 ( - id int(11) NOT NULL AUTO_INCREMENT, - b int(11) NOT NULL, - pad varbinary(255) DEFAULT NULL, - PRIMARY KEY (id), - KEY b (b)) ON COMMIT DELETE ROWS`) - expect = "CREATE GLOBAL TEMPORARY TABLE `t5` (\n" + - " `id` int(11) NOT NULL AUTO_INCREMENT,\n" + - " `b` int(11) NOT NULL,\n" + - " `pad` varbinary(255) DEFAULT NULL,\n" + - " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS" - tk.MustQuery("show create table t5").Check(testkit.Rows("t5 " + expect)) - - tk.MustExec("create temporary table t6 (i int primary key, j int)") - expect = "CREATE TEMPORARY TABLE `t6` (\n" + - " `i` int(11) NOT NULL,\n" + - " `j` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" - tk.MustQuery("show create table t6").Check(testkit.Rows("t6 " + expect)) - tk.MustExec("create temporary table t7 (i int primary key auto_increment, j int)") - defer func() { - tk.MustExec("commit;") - }() - tk.MustExec("begin;") - tk.MustExec("insert into t7 (j) values (14)") - tk.MustExec("insert into t7 (j) values (24)") - tk.MustQuery("select * from t7").Check(testkit.Rows("1 14", "2 24")) - expect = "CREATE TEMPORARY TABLE `t7` (\n" + - " `i` int(11) NOT NULL AUTO_INCREMENT,\n" + - " `j` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=3" - tk.MustQuery("show create table t7").Check(testkit.Rows("t7 " + expect)) -} - -func TestShowCachedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (id int)") - tk.MustExec("alter table t1 cache") - tk.MustQuery("show create table t1").Check( - testkit.Rows("t1 CREATE TABLE `t1` (\n" + - " `id` int(11) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /* CACHED ON */")) - tk.MustQuery("select create_options from information_schema.tables where table_schema = 'test' and table_name = 't1'").Check( - testkit.Rows("cached=on")) - - tk.MustExec("alter table t1 nocache") - tk.MustQuery("show create table t1").Check( - testkit.Rows("t1 CREATE TABLE `t1` (\n" + - " `id` int(11) DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustQuery("select create_options from information_schema.tables where table_schema = 'test' and table_name = 't1'").Check( - testkit.Rows("")) -} - -func TestShowBindingCache(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b int)") - tk.MustExec(`set global tidb_mem_quota_binding_cache = 1`) - tk.MustQuery("select @@global.tidb_mem_quota_binding_cache").Check(testkit.Rows("1")) - tk.MustExec("admin reload bindings;") - res := tk.MustQuery("show global bindings") - require.Equal(t, 0, len(res.Rows())) - - tk.MustExec("create global binding for select * from t using select * from t") - res = tk.MustQuery("show global bindings") - require.Equal(t, 0, len(res.Rows())) - - tk.MustExec(`set global tidb_mem_quota_binding_cache = default`) - tk.MustQuery("select @@global.tidb_mem_quota_binding_cache").Check(testkit.Rows("67108864")) - tk.MustExec("admin reload bindings") - res = tk.MustQuery("show global bindings") - require.Equal(t, 1, len(res.Rows())) - - tk.MustExec("create global binding for select * from t where a > 1 using select * from t where a > 1") - res = tk.MustQuery("show global bindings") - require.Equal(t, 2, len(res.Rows())) -} - -func TestShowBindingCacheStatus(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "0 0 0 Bytes 64 MB")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx_a(a), index idx_b(b))") - result := tk.MustQuery("show global bindings") - rows := result.Rows() - require.Equal(t, len(rows), 0) - tk.MustExec("create global binding for select * from t using select * from t") - - result = tk.MustQuery("show global bindings") - rows = result.Rows() - require.Equal(t, len(rows), 1) - - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "1 1 159 Bytes 64 MB")) - - tk.MustExec(`set global tidb_mem_quota_binding_cache = 250`) - tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("250")) - tk.MustExec("admin reload bindings;") - tk.MustExec("create global binding for select * from t where a > 1 using select * from t where a > 1") - result = tk.MustQuery("show global bindings") - rows = result.Rows() - require.Equal(t, len(rows), 1) - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "1 2 187 Bytes 250 Bytes")) - - tk.MustExec("drop global binding for select * from t where a > 1") - result = tk.MustQuery("show global bindings") - rows = result.Rows() - require.Equal(t, len(rows), 0) - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "0 1 0 Bytes 250 Bytes")) - - tk.MustExec("admin reload bindings") - result = tk.MustQuery("show global bindings") - rows = result.Rows() - require.Equal(t, len(rows), 1) - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "1 1 159 Bytes 250 Bytes")) - - tk.MustExec("create global binding for select * from t using select * from t use index(idx_a)") - - result = tk.MustQuery("show global bindings") - rows = result.Rows() - require.Equal(t, len(rows), 1) - - tk.MustQuery("show binding_cache status").Check(testkit.Rows( - "1 1 198 Bytes 250 Bytes")) -} - -func TestShowDatabasesLike(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{ - Username: "root", Hostname: "%"}, nil, nil, nil)) - - tk.MustExec("DROP DATABASE IF EXISTS `TEST_$1`") - tk.MustExec("DROP DATABASE IF EXISTS `test_$2`") - tk.MustExec("CREATE DATABASE `TEST_$1`;") - tk.MustExec("CREATE DATABASE `test_$2`;") - - tk.MustQuery("SHOW DATABASES LIKE 'TEST_%'").Check(testkit.Rows("TEST_$1", "test_$2")) - tk.MustQuery("SHOW DATABASES LIKE 'test_%'").Check(testkit.Rows("TEST_$1", "test_$2")) -} - -func TestShowTableStatusLike(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("DROP table IF EXISTS `T1`") - tk.MustExec("CREATE table `T1` (a int);") - rows := tk.MustQuery("SHOW table status LIKE 't1'").Rows() - require.Equal(t, "T1", rows[0][0]) - - tk.MustExec("DROP table IF EXISTS `Li_1`") - tk.MustExec("DROP table IF EXISTS `li_2`") - - tk.MustExec("CREATE table `Li_1` (a int);") - tk.MustExec("CREATE table `li_2` (a int);") - - rows = tk.MustQuery("SHOW table status LIKE 'li%'").Rows() - require.Equal(t, "Li_1", rows[0][0]) - require.Equal(t, "li_2", rows[1][0]) -} - -func TestShowCollationsLike(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{ - Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustQuery("SHOW COLLATION LIKE 'UTF8MB4_BI%'").Check(testkit.Rows("utf8mb4_bin utf8mb4 46 Yes Yes 1")) - tk.MustQuery("SHOW COLLATION LIKE 'utf8mb4_bi%'").Check(testkit.Rows("utf8mb4_bin utf8mb4 46 Yes Yes 1")) -} - -func TestShowLimitReturnRow(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t1(a int, b int, c int, d int, index idx_a(a), index idx_b(b))") - tk.MustExec("create table t2(a int, b int, c int, d int, index idx_a(a), index idx_b(b))") - tk.MustExec("INSERT INTO t1 VALUES(1,2,3,4)") - tk.MustExec("INSERT INTO t1 VALUES(4,3,1,2)") - tk.MustExec("SET @@sql_select_limit=1") - tk.MustExec("PREPARE stmt FROM \"SHOW COLUMNS FROM t1\"") - result := tk.MustQuery("EXECUTE stmt") - rows := result.Rows() - require.Equal(t, len(rows), 1) - - tk.MustExec("PREPARE stmt FROM \"select * FROM t1\"") - result = tk.MustQuery("EXECUTE stmt") - rows = result.Rows() - require.Equal(t, len(rows), 1) - - // Test case for other scenarios. - result = tk.MustQuery("SHOW ENGINES") - rows = result.Rows() - require.Equal(t, len(rows), 1) - - tk.MustQuery("SHOW DATABASES like '%SCHEMA'").Check(testkit.RowsWithSep("|", "INFORMATION_SCHEMA")) - - tk.MustQuery("SHOW TABLES where tables_in_test='t2'").Check(testkit.RowsWithSep("|", "t2")) - - result = tk.MustQuery("SHOW TABLE STATUS where name='t2'") - rows = result.Rows() - require.Equal(t, rows[0][0], "t2") - - tk.MustQuery("SHOW COLUMNS FROM t1 where Field ='d'").Check(testkit.RowsWithSep("|", ""+ - "d int(11) YES ")) - - tk.MustQuery("Show Charset where charset='gbk'").Check(testkit.RowsWithSep("|", ""+ - "gbk Chinese Internal Code Specification gbk_chinese_ci 2")) - - tk.MustQuery("Show Variables where variable_name ='max_allowed_packet'").Check(testkit.RowsWithSep("|", ""+ - "max_allowed_packet 67108864")) - - result = tk.MustQuery("SHOW status where variable_name ='server_id'") - rows = result.Rows() - require.Equal(t, rows[0][0], "server_id") - - tk.MustQuery("Show Collation where collation='utf8_bin'").Check(testkit.RowsWithSep("|", ""+ - "utf8_bin utf8 83 Yes Yes 1")) - - result = tk.MustQuery("show index from t1 where key_name='idx_b'") - rows = result.Rows() - require.Equal(t, rows[0][2], "idx_b") -} - -func TestShowBindingDigestField(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(id int, key(id))") - tk.MustExec("create table t2(id int, key(id))") - tk.MustExec("create binding for select * from t1, t2 where t1.id = t2.id using select /*+ merge_join(t1, t2)*/ * from t1, t2 where t1.id = t2.id") - result := tk.MustQuery("show bindings;") - rows := result.Rows()[0] - require.Equal(t, len(rows), 11) - require.Equal(t, rows[9], "ac1ceb4eb5c01f7c03e29b7d0d6ab567e563f4c93164184cde218f20d07fd77c") - tk.MustExec("drop binding for select * from t1, t2 where t1.id = t2.id") - result = tk.MustQuery("show bindings;") - require.Equal(t, len(result.Rows()), 0) - - tk.MustExec("create global binding for select * from t1, t2 where t1.id = t2.id using select /*+ merge_join(t1, t2)*/ * from t1, t2 where t1.id = t2.id") - result = tk.MustQuery("show global bindings;") - rows = result.Rows()[0] - require.Equal(t, len(rows), 11) - require.Equal(t, rows[9], "ac1ceb4eb5c01f7c03e29b7d0d6ab567e563f4c93164184cde218f20d07fd77c") - tk.MustExec("drop global binding for select * from t1, t2 where t1.id = t2.id") - result = tk.MustQuery("show global bindings;") - require.Equal(t, len(result.Rows()), 0) -} - -func TestShowPasswordVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = ''") - rs, err := tk.Exec("show variables like 'authentication_ldap_sasl_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], "") - rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_sasl_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], "") - - tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = password") - defer func() { - tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = ''") - }() - rs, err = tk.Exec("show variables like 'authentication_ldap_sasl_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.MaskPwd) - rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_sasl_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], variable.MaskPwd) - - tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = ''") - rs, err = tk.Exec("show variables like 'authentication_ldap_simple_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], "") - rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_simple_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], "") - - tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = password") - defer func() { - tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = ''") - }() - - rs, err = tk.Exec("show variables like 'authentication_ldap_simple_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.MaskPwd) - rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_simple_bind_root_pwd'") - require.NoError(t, err) - require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], variable.MaskPwd) -} diff --git a/executor/test/simpletest/BUILD.bazel b/executor/test/simpletest/BUILD.bazel deleted file mode 100644 index f2f4c275aad58..0000000000000 --- a/executor/test/simpletest/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "simpletest_test", - timeout = "short", - srcs = [ - "chunk_reuse_test.go", - "main_test.go", - "simple_test.go", - ], - flaky = True, - race = "on", - shard_count = 36, - deps = [ - "//config", - "//errno", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//session", - "//sessionctx", - "//store/mockstore", - "//testkit", - "//util/dbterror/exeerrors", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/simpletest/simple_test.go b/executor/test/simpletest/simple_test.go deleted file mode 100644 index e89a431091ebe..0000000000000 --- a/executor/test/simpletest/simple_test.go +++ /dev/null @@ -1,1152 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package simpletest - -import ( - "context" - "strconv" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" -) - -func TestFlushTables(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("FLUSH TABLES") - err := tk.ExecToErr("FLUSH TABLES WITH READ LOCK") - require.Error(t, err) -} - -func TestUseDB(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test") - err := tk.ExecToErr("USE ``") - require.Truef(t, terror.ErrorEqual(core.ErrNoDB, err), "err %v", err) -} - -func TestStmtAutoNewTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - // Some statements are like DDL, they commit the previous txn automically. - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // Fix issue https://github.com/pingcap/tidb/issues/10705 - tk.MustExec("begin") - tk.MustExec("create user 'xxx'@'%';") - tk.MustExec("grant all privileges on *.* to 'xxx'@'%';") - - tk.MustExec("create table auto_new (id int)") - tk.MustExec("begin") - tk.MustExec("insert into auto_new values (1)") - tk.MustExec("revoke all privileges on *.* from 'xxx'@'%'") - tk.MustExec("rollback") // insert statement has already committed - tk.MustQuery("select * from auto_new").Check(testkit.Rows("1")) - - // Test the behavior when autocommit is false. - tk.MustExec("set autocommit = 0") - tk.MustExec("insert into auto_new values (2)") - tk.MustExec("create user 'yyy'@'%'") - tk.MustExec("rollback") - tk.MustQuery("select * from auto_new").Check(testkit.Rows("1", "2")) - - tk.MustExec("drop user 'yyy'@'%'") - tk.MustExec("insert into auto_new values (3)") - tk.MustExec("rollback") - tk.MustQuery("select * from auto_new").Check(testkit.Rows("1", "2")) -} - -func TestIssue9111(t *testing.T) { - store := testkit.CreateMockStore(t) - // CREATE USER / DROP USER fails if admin doesn't have insert privilege on `mysql.user` table. - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user 'user_admin'@'localhost';") - tk.MustExec("grant create user on *.* to 'user_admin'@'localhost';") - - // Create a new session. - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "user_admin", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - _, err = se.Execute(ctx, `create user test_create_user`) - require.NoError(t, err) - _, err = se.Execute(ctx, `drop user test_create_user`) - require.NoError(t, err) - - tk.MustExec("revoke create user on *.* from 'user_admin'@'localhost';") - tk.MustExec("grant insert, delete on mysql.user to 'user_admin'@'localhost';") - - _, err = se.Execute(ctx, `create user test_create_user`) - require.NoError(t, err) - _, err = se.Execute(ctx, `drop user test_create_user`) - require.NoError(t, err) - - _, err = se.Execute(ctx, `create role test_create_user`) - require.NoError(t, err) - _, err = se.Execute(ctx, `drop role test_create_user`) - require.NoError(t, err) - - tk.MustExec("drop user 'user_admin'@'localhost';") -} - -func TestRoleAtomic(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create role r2;") - err := tk.ExecToErr("create role r1, r2, r3") - require.Error(t, err) - // Check atomic create role. - result := tk.MustQuery(`SELECT user FROM mysql.User WHERE user in ('r1', 'r2', 'r3')`) - result.Check(testkit.Rows("r2")) - // Check atomic drop role. - err = tk.ExecToErr("drop role r1, r2, r3") - require.Error(t, err) - result = tk.MustQuery(`SELECT user FROM mysql.User WHERE user in ('r1', 'r2', 'r3')`) - result.Check(testkit.Rows("r2")) - tk.MustExec("drop role r2;") -} - -func TestExtendedStatsPrivileges(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("create user 'u1'@'%'") - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "u1", Hostname: "%"}, nil, nil, nil)) - ctx := context.Background() - _, err = se.Execute(ctx, "set session tidb_enable_extended_stats = on") - require.NoError(t, err) - _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") - require.Error(t, err) - require.Equal(t, "[planner:1142]ALTER command denied to user 'u1'@'%' for table 't'", err.Error()) - tk.MustExec("grant alter on test.* to 'u1'@'%'") - _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") - require.Error(t, err) - require.Equal(t, "[planner:1142]ADD STATS_EXTENDED command denied to user 'u1'@'%' for table 't'", err.Error()) - tk.MustExec("grant select on test.* to 'u1'@'%'") - _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") - require.Error(t, err) - require.Equal(t, "[planner:1142]ADD STATS_EXTENDED command denied to user 'u1'@'%' for table 'stats_extended'", err.Error()) - tk.MustExec("grant insert on mysql.stats_extended to 'u1'@'%'") - _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") - require.NoError(t, err) - - _, err = se.Execute(ctx, "use test") - require.NoError(t, err) - _, err = se.Execute(ctx, "alter table t drop stats_extended s1") - require.Error(t, err) - require.Equal(t, "[planner:1142]DROP STATS_EXTENDED command denied to user 'u1'@'%' for table 'stats_extended'", err.Error()) - tk.MustExec("grant update on mysql.stats_extended to 'u1'@'%'") - _, err = se.Execute(ctx, "alter table t drop stats_extended s1") - require.NoError(t, err) - tk.MustExec("drop user 'u1'@'%'") -} - -func TestIssue17247(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user 'issue17247'") - tk.MustExec("grant CREATE USER on *.* to 'issue17247'") - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "issue17247", Hostname: "%"}, nil, nil, nil)) - tk1.MustExec("ALTER USER USER() IDENTIFIED BY 'xxx'") - tk1.MustExec("ALTER USER CURRENT_USER() IDENTIFIED BY 'yyy'") - tk1.MustExec("ALTER USER CURRENT_USER IDENTIFIED BY 'zzz'") - tk.MustExec("ALTER USER 'issue17247'@'%' IDENTIFIED BY 'kkk'") - tk.MustExec("ALTER USER 'issue17247'@'%' IDENTIFIED BY PASSWORD '*B50FBDB37F1256824274912F2A1CE648082C3F1F'") - // Wrong grammar - _, err := tk1.Exec("ALTER USER USER() IDENTIFIED BY PASSWORD '*B50FBDB37F1256824274912F2A1CE648082C3F1F'") - require.Error(t, err) -} - -// Close issue #23649. -// See https://github.com/pingcap/tidb/issues/23649 -func TestIssue23649(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("DROP USER IF EXISTS issue23649;") - tk.MustExec("CREATE USER issue23649;") - err := tk.ExecToErr("GRANT bogusrole to issue23649;") - require.Equal(t, "[executor:3523]Unknown authorization ID `bogusrole`@`%`", err.Error()) - err = tk.ExecToErr("GRANT bogusrole to nonexisting;") - require.Equal(t, "[executor:3523]Unknown authorization ID `bogusrole`@`%`", err.Error()) -} - -func TestSetCurrentUserPwd(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER issue28534;") - defer func() { - tk.MustExec("DROP USER IF EXISTS issue28534;") - }() - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "issue28534", Hostname: "localhost", CurrentUser: true, AuthUsername: "issue28534", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec(`SET PASSWORD FOR CURRENT_USER() = "43582eussi"`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="issue28534"`) - result.Check(testkit.Rows(auth.EncodePassword("43582eussi"))) -} - -func TestShowGrantsAfterDropRole(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER u29473") - defer tk.MustExec("DROP USER IF EXISTS u29473") - - tk.MustExec("CREATE ROLE r29473") - tk.MustExec("GRANT r29473 TO u29473") - tk.MustExec("GRANT CREATE USER ON *.* TO u29473") - - tk.Session().Auth(&auth.UserIdentity{Username: "u29473", Hostname: "%"}, nil, nil, nil) - tk.MustExec("SET ROLE r29473") - tk.MustExec("DROP ROLE r29473") - tk.MustQuery("SHOW GRANTS").Check(testkit.Rows("GRANT CREATE USER ON *.* TO 'u29473'@'%'")) -} - -func TestPrivilegesAfterDropUser(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1(id int, v int)") - defer tk.MustExec("drop table t1") - - tk.MustExec("CREATE USER u1 require ssl") - defer tk.MustExec("DROP USER IF EXISTS u1") - - tk.MustExec("GRANT CREATE ON test.* TO u1") - tk.MustExec("GRANT UPDATE ON test.t1 TO u1") - tk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO u1") - tk.MustExec("GRANT SELECT(v), UPDATE(v) on test.t1 TO u1") - - tk.MustQuery("SELECT COUNT(1) FROM mysql.global_grants WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) - tk.MustQuery("SELECT COUNT(1) FROM mysql.global_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) - tk.MustQuery("SELECT COUNT(1) FROM mysql.tables_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) - tk.MustQuery("SELECT COUNT(1) FROM mysql.columns_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) - tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'u1'@'%'", - "GRANT CREATE ON `test`.* TO 'u1'@'%'", - "GRANT UPDATE ON `test`.`t1` TO 'u1'@'%'", - "GRANT SELECT(v), UPDATE(v) ON `test`.`t1` TO 'u1'@'%'", - "GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'u1'@'%'", - )) - - tk.MustExec("DROP USER u1") - err := tk.QueryToErr("SHOW GRANTS FOR u1") - require.Equal(t, "[privilege:1141]There is no such grant defined for user 'u1' on host '%'", err.Error()) - tk.MustQuery("SELECT * FROM mysql.global_grants WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) - tk.MustQuery("SELECT * FROM mysql.global_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) - tk.MustQuery("SELECT * FROM mysql.tables_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) - tk.MustQuery("SELECT * FROM mysql.columns_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) -} - -func TestDropRoleAfterRevoke(t *testing.T) { - store := testkit.CreateMockStore(t) - // issue 29781 - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) - - tk.MustExec("create role r1, r2, r3;") - defer tk.MustExec("drop role if exists r1, r2, r3;") - tk.MustExec("grant r1,r2,r3 to current_user();") - tk.MustExec("set role all;") - tk.MustExec("revoke r1, r3 from root;") - tk.MustExec("drop role r1;") -} - -func TestUserWithSetNames(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("set names gbk;") - - tk.MustExec("drop user if exists '\xd2\xbb'@'localhost';") - tk.MustExec("create user '\xd2\xbb'@'localhost' IDENTIFIED BY '\xd2\xbb';") - - result := tk.MustQuery("SELECT authentication_string FROM mysql.User WHERE User='\xd2\xbb' and Host='localhost';") - result.Check(testkit.Rows(auth.EncodePassword("一"))) - - tk.MustExec("ALTER USER '\xd2\xbb'@'localhost' IDENTIFIED BY '\xd2\xbb\xd2\xbb';") - result = tk.MustQuery("SELECT authentication_string FROM mysql.User WHERE User='\xd2\xbb' and Host='localhost';") - result.Check(testkit.Rows(auth.EncodePassword("一一"))) - - tk.MustExec("RENAME USER '\xd2\xbb'@'localhost' to '\xd2\xbb'") - - tk.MustExec("drop user '\xd2\xbb';") -} - -func TestStatementsCauseImplicitCommit(t *testing.T) { - // Test some of the implicit commit statements. - // See https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("create table ic (id int primary key)") - - cases := []string{ - "create table xx (id int)", - "create user 'xx'@'127.0.0.1'", - "grant SELECT on test.ic to 'xx'@'127.0.0.1'", - "flush privileges", - "analyze table ic", - } - for i, sql := range cases { - tk.MustExec("begin") - tk.MustExec("insert into ic values (?)", i) - tk.MustExec(sql) - tk.MustQuery("select * from ic where id = ?", i).Check(testkit.Rows(strconv.FormatInt(int64(i), 10))) - // Clean up data - tk.MustExec("delete from ic") - } -} - -func TestDo(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("do 1, @a:=1") - tk.MustQuery("select @a").Check(testkit.Rows("1")) - - tk.MustExec("use test") - tk.MustExec("create table t (i int)") - tk.MustExec("insert into t values (1)") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk.MustQuery("select * from t").Check(testkit.Rows("1")) - tk.MustExec("do @a := (select * from t where i = 1)") - tk2.MustExec("insert into t values (2)") - tk.MustQuery("select * from t").Check(testkit.Rows("1", "2")) -} - -func TestDoWithAggFunc(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("DO sum(1)") - tk.MustExec("DO avg(@e+@f)") - tk.MustExec("DO GROUP_CONCAT(NULLIF(ELT(1, @e), 2.0) ORDER BY 1)") -} - -func TestSetRoleAllCorner(t *testing.T) { - store := testkit.CreateMockStore(t) - // For user with no role, `SET ROLE ALL` should active - // a empty slice, rather than nil. - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user set_role_all") - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "set_role_all", Hostname: "localhost"}, nil, nil, nil)) - ctx := context.Background() - _, err = se.Execute(ctx, `set role all`) - require.NoError(t, err) - _, err = se.Execute(ctx, `select current_role`) - require.NoError(t, err) -} - -func TestCreateRole(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user testCreateRole;") - tk.MustExec("grant CREATE USER on *.* to testCreateRole;") - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testCreateRole", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - _, err = se.Execute(ctx, `create role test_create_role;`) - require.NoError(t, err) - tk.MustExec("revoke CREATE USER on *.* from testCreateRole;") - tk.MustExec("drop role test_create_role;") - tk.MustExec("grant CREATE ROLE on *.* to testCreateRole;") - _, err = se.Execute(ctx, `create role test_create_role;`) - require.NoError(t, err) - tk.MustExec("drop role test_create_role;") - _, err = se.Execute(ctx, `create user test_create_role;`) - require.Error(t, err) - tk.MustExec("drop user testCreateRole;") -} - -func TestDropRole(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user testCreateRole;") - tk.MustExec("create user test_create_role;") - tk.MustExec("grant CREATE USER on *.* to testCreateRole;") - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testCreateRole", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - _, err = se.Execute(ctx, `drop role test_create_role;`) - require.NoError(t, err) - tk.MustExec("revoke CREATE USER on *.* from testCreateRole;") - tk.MustExec("create role test_create_role;") - tk.MustExec("grant DROP ROLE on *.* to testCreateRole;") - _, err = se.Execute(ctx, `drop role test_create_role;`) - require.NoError(t, err) - tk.MustExec("create user test_create_role;") - _, err = se.Execute(ctx, `drop user test_create_role;`) - require.Error(t, err) - tk.MustExec("drop user testCreateRole;") - tk.MustExec("drop user test_create_role;") -} - -func TestTransaction(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("begin") - ctx := tk.Session() - require.True(t, inTxn(ctx)) - tk.MustExec("commit") - require.False(t, inTxn(ctx)) - tk.MustExec("begin") - require.True(t, inTxn(ctx)) - tk.MustExec("rollback") - require.False(t, inTxn(ctx)) - - // Test that begin implicitly commits previous transaction. - tk.MustExec("use test") - tk.MustExec("create table txn (a int)") - tk.MustExec("begin") - tk.MustExec("insert txn values (1)") - tk.MustExec("begin") - tk.MustExec("rollback") - tk.MustQuery("select * from txn").Check(testkit.Rows("1")) - - // Test that DDL implicitly commits previous transaction. - tk.MustExec("begin") - tk.MustExec("insert txn values (2)") - tk.MustExec("create table txn2 (a int)") - tk.MustExec("rollback") - tk.MustQuery("select * from txn").Check(testkit.Rows("1", "2")) -} - -func inTxn(ctx sessionctx.Context) bool { - return (ctx.GetSessionVars().Status & mysql.ServerStatusInTrans) > 0 -} - -func TestIssue33144(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - //Create role - tk.MustExec("create role 'r1' ;") - - sessionVars := tk.Session().GetSessionVars() - sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "%"} - - //Grant role to current_user() - tk.MustExec("grant 'r1' to current_user();") - //Revoke role from current_user() - tk.MustExec("revoke 'r1' from current_user();") - - //Grant role to current_user(),current_user() - tk.MustExec("grant 'r1' to current_user(),current_user();") - //Revoke role from current_user(),current_user() - tk.MustExec("revoke 'r1' from current_user(),current_user();") - - //Drop role - tk.MustExec("drop role 'r1' ;") -} - -func TestRole(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // Make sure user test not in mysql.User. - result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) - result.Check(nil) - - // Test for DROP ROLE. - createRoleSQL := `CREATE ROLE 'test'@'localhost';` - tk.MustExec(createRoleSQL) - // Make sure user test in mysql.User. - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword(""))) - // Insert relation into mysql.role_edges - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test','%','root')") - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test1','localhost','test1')") - // Insert relation into mysql.default_roles - tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('%','root','localhost','test')") - tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('localhost','test','%','test1')") - - dropUserSQL := `DROP ROLE IF EXISTS 'test'@'localhost' ;` - err := tk.ExecToErr(dropUserSQL) - require.NoError(t, err) - - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) - result.Check(nil) - result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE TO_USER="test" and TO_HOST="localhost"`) - result.Check(nil) - result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE FROM_USER="test" and FROM_HOST="localhost"`) - result.Check(nil) - result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE USER="test" and HOST="localhost"`) - result.Check(nil) - result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE DEFAULT_ROLE_USER="test" and DEFAULT_ROLE_HOST="localhost"`) - result.Check(nil) - - // Test for GRANT ROLE - createRoleSQL = `CREATE ROLE 'r_1'@'localhost', 'r_2'@'localhost', 'r_3'@'localhost';` - tk.MustExec(createRoleSQL) - grantRoleSQL := `GRANT 'r_1'@'localhost' TO 'r_2'@'localhost';` - tk.MustExec(grantRoleSQL) - result = tk.MustQuery(`SELECT TO_USER FROM mysql.role_edges WHERE FROM_USER="r_1" and FROM_HOST="localhost"`) - result.Check(testkit.Rows("r_2")) - - grantRoleSQL = `GRANT 'r_1'@'localhost' TO 'r_3'@'localhost', 'r_4'@'localhost';` - err = tk.ExecToErr(grantRoleSQL) - require.Error(t, err) - - // Test grant role for current_user(); - sessionVars := tk.Session().GetSessionVars() - originUser := sessionVars.User - sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "%"} - tk.MustExec("grant 'r_1'@'localhost' to current_user();") - tk.MustExec("revoke 'r_1'@'localhost' from 'root'@'%';") - sessionVars.User = originUser - - result = tk.MustQuery(`SELECT FROM_USER FROM mysql.role_edges WHERE TO_USER="r_3" and TO_HOST="localhost"`) - result.Check(nil) - - dropRoleSQL := `DROP ROLE IF EXISTS 'r_1'@'localhost' ;` - tk.MustExec(dropRoleSQL) - dropRoleSQL = `DROP ROLE IF EXISTS 'r_2'@'localhost' ;` - tk.MustExec(dropRoleSQL) - dropRoleSQL = `DROP ROLE IF EXISTS 'r_3'@'localhost' ;` - tk.MustExec(dropRoleSQL) - - // Test for revoke role - createRoleSQL = `CREATE ROLE 'test'@'localhost', r_1, r_2;` - tk.MustExec(createRoleSQL) - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test','%','root')") - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_1','%','root')") - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_2','%','root')") - tk.MustExec("flush privileges") - tk.MustExec("SET DEFAULT ROLE r_1, r_2 TO root") - err = tk.ExecToErr("revoke test@localhost, r_1 from root;") - require.NoError(t, err) - err = tk.ExecToErr("revoke `r_2`@`%` from root, u_2;") - require.Error(t, err) - err = tk.ExecToErr("revoke `r_2`@`%` from root;") - require.NoError(t, err) - err = tk.ExecToErr("revoke `r_1`@`%` from root;") - require.NoError(t, err) - result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE DEFAULT_ROLE_USER="test" and DEFAULT_ROLE_HOST="localhost"`) - result.Check(nil) - result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE USER="root" and HOST="%"`) - result.Check(nil) - dropRoleSQL = `DROP ROLE 'test'@'localhost', r_1, r_2;` - tk.MustExec(dropRoleSQL) - - ctx := tk.Session().(sessionctx.Context) - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "test1", Hostname: "localhost"} - require.NotNil(t, tk.ExecToErr("SET ROLE role1, role2")) - tk.MustExec("SET ROLE ALL") - tk.MustExec("SET ROLE ALL EXCEPT role1, role2") - tk.MustExec("SET ROLE DEFAULT") - tk.MustExec("SET ROLE NONE") -} - -func TestRoleAdmin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER 'testRoleAdmin';") - tk.MustExec("CREATE ROLE 'targetRole';") - - // Create a new session. - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testRoleAdmin", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - _, err = se.Execute(ctx, "GRANT `targetRole` TO `testRoleAdmin`;") - require.Error(t, err) - - tk.MustExec("GRANT SUPER ON *.* TO `testRoleAdmin`;") - _, err = se.Execute(ctx, "GRANT `targetRole` TO `testRoleAdmin`;") - require.NoError(t, err) - _, err = se.Execute(ctx, "REVOKE `targetRole` FROM `testRoleAdmin`;") - require.NoError(t, err) - - tk.MustExec("DROP USER 'testRoleAdmin';") - tk.MustExec("DROP ROLE 'targetRole';") -} - -func TestDefaultRole(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - createRoleSQL := `CREATE ROLE r_1, r_2, r_3, u_1;` - tk.MustExec(createRoleSQL) - - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_1','%','u_1')") - tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_2','%','u_1')") - - tk.MustExec("flush privileges;") - - setRoleSQL := `SET DEFAULT ROLE r_3 TO u_1;` - err := tk.ExecToErr(setRoleSQL) - require.Error(t, err) - - setRoleSQL = `SET DEFAULT ROLE r_1 TO u_1000;` - err = tk.ExecToErr(setRoleSQL) - require.Error(t, err) - - setRoleSQL = `SET DEFAULT ROLE r_1, r_3 TO u_1;` - err = tk.ExecToErr(setRoleSQL) - require.Error(t, err) - - setRoleSQL = `SET DEFAULT ROLE r_1 TO u_1;` - err = tk.ExecToErr(setRoleSQL) - require.NoError(t, err) - result := tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) - result.Check(testkit.Rows("r_1")) - setRoleSQL = `SET DEFAULT ROLE r_2 TO u_1;` - err = tk.ExecToErr(setRoleSQL) - require.NoError(t, err) - result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) - result.Check(testkit.Rows("r_2")) - - setRoleSQL = `SET DEFAULT ROLE ALL TO u_1;` - err = tk.ExecToErr(setRoleSQL) - require.NoError(t, err) - result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) - result.Check(testkit.Rows("r_1", "r_2")) - - setRoleSQL = `SET DEFAULT ROLE NONE TO u_1;` - err = tk.ExecToErr(setRoleSQL) - require.NoError(t, err) - result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) - result.Check(nil) - - dropRoleSQL := `DROP USER r_1, r_2, r_3, u_1;` - tk.MustExec(dropRoleSQL) -} - -func TestSetDefaultRoleAll(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user test_all;") - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "test_all", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - _, err = se.Execute(ctx, "set default role all to test_all;") - require.NoError(t, err) -} - -func TestUser(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - // Make sure user test not in mysql.User. - result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) - result.Check(nil) - // Create user test. - createUserSQL := `CREATE USER 'test'@'localhost' IDENTIFIED BY '123';` - tk.MustExec(createUserSQL) - // Make sure user test in mysql.User. - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("123"))) - // Create duplicate user with IfNotExists will be success. - createUserSQL = `CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY '123';` - tk.MustExec(createUserSQL) - - // Create duplicate user without IfNotExists will cause error. - createUserSQL = `CREATE USER 'test'@'localhost' IDENTIFIED BY '123';` - tk.MustGetErrCode(createUserSQL, mysql.ErrCannotUser) - createUserSQL = `CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY '123';` - tk.MustExec(createUserSQL) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3163|User 'test'@'localhost' already exists.")) - dropUserSQL := `DROP USER IF EXISTS 'test'@'localhost' ;` - tk.MustExec(dropUserSQL) - // Create user test. - createUserSQL = `CREATE USER 'test1'@'localhost';` - tk.MustExec(createUserSQL) - // Make sure user test in mysql.User. - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword(""))) - dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost' ;` - tk.MustExec(dropUserSQL) - - // Test create/alter user with `tidb_auth_token` - tk.MustExec(`CREATE USER token_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) - tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-abc")) - tk.MustExec(`ALTER USER token_user REQUIRE token_issuer 'issuer-123'`) - tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-123")) - tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'tidb_auth_token'`) - tk.MustExec(`CREATE USER token_user1 IDENTIFIED WITH 'tidb_auth_token'`) - tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is needed for 'tidb_auth_token' user, please use 'alter user' to declare it")) - tk.MustExec(`CREATE USER temp_user IDENTIFIED WITH 'mysql_native_password' BY '1234' REQUIRE token_issuer 'issuer-abc'`) - tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is not needed for 'mysql_native_password' user")) - tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'mysql_native_password' REQUIRE token_issuer 'issuer-abc'`) - tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is not needed for the auth plugin")) - tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'tidb_auth_token'`) - tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|Auth plugin 'tidb_auth_plugin' needs TOKEN_ISSUER")) - tk.MustExec(`ALTER USER token_user REQUIRE SSL`) - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'mysql_native_password' BY '1234'`) - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) - tk.MustQuery(`show warnings`).Check(testkit.Rows()) - - // Test alter user. - createUserSQL = `CREATE USER 'test1'@'localhost' IDENTIFIED BY '123', 'test2'@'localhost' IDENTIFIED BY '123', 'test3'@'localhost' IDENTIFIED BY '123', 'test4'@'localhost' IDENTIFIED BY '123';` - tk.MustExec(createUserSQL) - alterUserSQL := `ALTER USER 'test1'@'localhost' IDENTIFIED BY '111';` - tk.MustExec(alterUserSQL) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("111"))) - alterUserSQL = `ALTER USER 'test_not_exist'@'localhost' IDENTIFIED BY '111';` - tk.MustGetErrCode(alterUserSQL, mysql.ErrCannotUser) - alterUserSQL = `ALTER USER 'test1'@'localhost' IDENTIFIED BY '222', 'test_not_exist'@'localhost' IDENTIFIED BY '111';` - tk.MustGetErrCode(alterUserSQL, mysql.ErrCannotUser) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("111"))) - alterUserSQL = `ALTER USER 'test4'@'localhost' IDENTIFIED WITH 'auth_socket';` - tk.MustExec(alterUserSQL) - result = tk.MustQuery(`SELECT plugin FROM mysql.User WHERE User="test4" and Host="localhost"`) - result.Check(testkit.Rows("auth_socket")) - - alterUserSQL = `ALTER USER IF EXISTS 'test2'@'localhost' IDENTIFIED BY '222', 'test_not_exist'@'localhost' IDENTIFIED BY '1';` - tk.MustExec(alterUserSQL) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User 'test_not_exist'@'localhost' does not exist.")) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test2" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("222"))) - alterUserSQL = `ALTER USER IF EXISTS'test_not_exist'@'localhost' IDENTIFIED BY '1', 'test3'@'localhost' IDENTIFIED BY '333';` - tk.MustExec(alterUserSQL) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User 'test_not_exist'@'localhost' does not exist.")) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test3" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("333"))) - - // Test alter user user(). - alterUserSQL = `ALTER USER USER() IDENTIFIED BY '1';` - err := tk.ExecToErr(alterUserSQL) - require.Truef(t, terror.ErrorEqual(err, errors.New("Session user is empty")), "err %v", err) - sess, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(sess) - ctx := tk.Session().(sessionctx.Context) - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "test1", Hostname: "localhost", AuthHostname: "localhost"} - tk.MustExec(alterUserSQL) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("1"))) - dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` - tk.MustExec(dropUserSQL) - - // Test drop user if exists. - createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` - tk.MustExec(createUserSQL) - dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost' ;` - tk.MustExec(dropUserSQL) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User test2@localhost does not exist.")) - - // Test negative cases without IF EXISTS. - createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` - tk.MustExec(createUserSQL) - dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` - tk.MustGetErrCode(dropUserSQL, mysql.ErrCannotUser) - dropUserSQL = `DROP USER 'test3'@'localhost';` - tk.MustExec(dropUserSQL) - dropUserSQL = `DROP USER 'test1'@'localhost';` - tk.MustExec(dropUserSQL) - // Test positive cases without IF EXISTS. - createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` - tk.MustExec(createUserSQL) - dropUserSQL = `DROP USER 'test1'@'localhost', 'test3'@'localhost';` - tk.MustExec(dropUserSQL) - - // Test 'identified by password' - createUserSQL = `CREATE USER 'test1'@'localhost' identified by password 'xxx';` - err = tk.ExecToErr(createUserSQL) - require.Truef(t, terror.ErrorEqual(exeerrors.ErrPasswordFormat, err), "err %v", err) - createUserSQL = `CREATE USER 'test1'@'localhost' identified by password '*3D56A309CD04FA2EEF181462E59011F075C89548';` - tk.MustExec(createUserSQL) - dropUserSQL = `DROP USER 'test1'@'localhost';` - tk.MustExec(dropUserSQL) - - // Test drop user meet error - err = tk.ExecToErr(dropUserSQL) - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrCannotUser.GenWithStackByArgs("DROP USER", "")), "err %v", err) - - createUserSQL = `CREATE USER 'test1'@'localhost'` - tk.MustExec(createUserSQL) - createUserSQL = `CREATE USER 'test2'@'localhost'` - tk.MustExec(createUserSQL) - - dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` - err = tk.ExecToErr(dropUserSQL) - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrCannotUser.GenWithStackByArgs("DROP USER", "")), "err %v", err) - - // Close issue #17639 - dropUserSQL = `DROP USER if exists test3@'%'` - tk.MustExec(dropUserSQL) - createUserSQL = `create user test3@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';` - tk.MustExec(createUserSQL) - querySQL := `select authentication_string from mysql.user where user="test3" ;` - tk.MustQuery(querySQL).Check(testkit.Rows("*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9")) - alterUserSQL = `alter user test3@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';` - tk.MustExec(alterUserSQL) - tk.MustQuery(querySQL).Check(testkit.Rows("*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9")) - - createUserSQL = `create user userA@LOCALHOST;` - tk.MustExec(createUserSQL) - querySQL = `select user,host from mysql.user where user = 'userA';` - tk.MustQuery(querySQL).Check(testkit.Rows("userA localhost")) - - createUserSQL = `create user userB@DEMO.com;` - tk.MustExec(createUserSQL) - querySQL = `select user,host from mysql.user where user = 'userB';` - tk.MustQuery(querySQL).Check(testkit.Rows("userB demo.com")) - - createUserSQL = `create user userC@localhost;` - tk.MustExec(createUserSQL) - renameUserSQL := `rename user 'userC'@'localhost' to 'userD'@'Demo.com';` - tk.MustExec(renameUserSQL) - querySQL = `select user,host from mysql.user where user = 'userD';` - tk.MustQuery(querySQL).Check(testkit.Rows("userD demo.com")) - - createUserSQL = `create user foo@localhost identified with 'foobar';` - err = tk.ExecToErr(createUserSQL) - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrPluginIsNotLoaded), "err %v", err) - - tk.MustExec(`create user joan;`) - tk.MustExec(`create user sally;`) - tk.MustExec(`create role engineering;`) - tk.MustExec(`create role consultants;`) - tk.MustExec(`create role qa;`) - tk.MustExec(`grant engineering to joan;`) - tk.MustExec(`grant engineering to sally;`) - tk.MustExec(`grant engineering, consultants to joan, sally;`) - tk.MustExec(`grant qa to consultants;`) - tk.MustExec("CREATE ROLE `engineering`@`US`;") - tk.MustExec("create role `engineering`@`INDIA`;") - tk.MustExec("grant `engineering`@`US` TO `engineering`@`INDIA`;") - - tk.MustQuery("select user,host from mysql.user where user='engineering' and host = 'india'"). - Check(testkit.Rows("engineering india")) - tk.MustQuery("select user,host from mysql.user where user='engineering' and host = 'us'"). - Check(testkit.Rows("engineering us")) - - tk.MustExec("drop role engineering@INDIA;") - tk.MustExec("drop role engineering@US;") - - tk.MustQuery("select user from mysql.user where user='engineering' and host = 'india'").Check(testkit.Rows()) - tk.MustQuery("select user from mysql.user where user='engineering' and host = 'us'").Check(testkit.Rows()) -} - -func TestSetPwd(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - createUserSQL := `CREATE USER 'testpwd'@'localhost' IDENTIFIED BY '';` - tk.MustExec(createUserSQL) - result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) - result.Check(testkit.Rows("")) - - // set password for - tk.MustExec(`SET PASSWORD FOR 'testpwd'@'localhost' = 'password';`) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("password"))) - - tk.MustExec(`CREATE USER 'testpwdsock'@'localhost' IDENTIFIED WITH 'auth_socket';`) - tk.MustExec(`SET PASSWORD FOR 'testpwdsock'@'localhost' = 'password';`) - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows("Note 1699 SET PASSWORD has no significance for user 'testpwdsock'@'localhost' as authentication plugin does not support it.")) - - // set password - setPwdSQL := `SET PASSWORD = 'pwd'` - // Session user is empty. - err := tk.ExecToErr(setPwdSQL) - require.Error(t, err) - sess, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(sess) - ctx := tk.Session().(sessionctx.Context) - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "testpwd1", Hostname: "localhost", AuthUsername: "testpwd1", AuthHostname: "localhost"} - // Session user doesn't exist. - err = tk.ExecToErr(setPwdSQL) - require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrPasswordNoMatch), "err %v", err) - // normal - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "testpwd", Hostname: "localhost", AuthUsername: "testpwd", AuthHostname: "localhost"} - tk.MustExec(setPwdSQL) - result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) - result.Check(testkit.Rows(auth.EncodePassword("pwd"))) -} - -func TestFlushPrivileges(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec(`CREATE USER 'testflush'@'localhost' IDENTIFIED BY '';`) - tk.MustExec(`UPDATE mysql.User SET Select_priv='Y' WHERE User="testflush" and Host="localhost"`) - - // Create a new session. - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - defer se.Close() - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testflush", Hostname: "localhost"}, nil, nil, nil)) - - ctx := context.Background() - // Before flush. - _, err = se.Execute(ctx, `SELECT authentication_string FROM mysql.User WHERE User="testflush" and Host="localhost"`) - require.Error(t, err) - - tk.MustExec("FLUSH PRIVILEGES") - - // After flush. - _, err = se.Execute(ctx, `SELECT authentication_string FROM mysql.User WHERE User="testflush" and Host="localhost"`) - require.NoError(t, err) -} - -func TestFlushPrivilegesPanic(t *testing.T) { - defer view.Stop() - // Run in a separate suite because this test need to set SkipGrantTable config. - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Security.SkipGrantTable = true - }) - - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("FLUSH PRIVILEGES") -} - -func TestDropPartitionStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - // Use the testSerialSuite to fix the unstable test - tk := testkit.NewTestKit(t, store) - tk.MustExec(`create database if not exists test_drop_gstats`) - tk.MustExec("use test_drop_gstats") - tk.MustExec("drop table if exists test_drop_gstats;") - tk.MustExec(`create table test_drop_gstats ( - a int, - key(a) -) -partition by range (a) ( - partition p0 values less than (10), - partition p1 values less than (20), - partition global values less than (30) -)`) - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk.MustExec("insert into test_drop_gstats values (1), (5), (11), (15), (21), (25)") - require.Nil(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) - - checkPartitionStats := func(names ...string) { - rs := tk.MustQuery("show stats_meta").Rows() - require.Equal(t, len(names), len(rs)) - for i := range names { - require.Equal(t, names[i], rs[i][2].(string)) - } - } - - tk.MustExec("analyze table test_drop_gstats") - checkPartitionStats("global", "p0", "p1", "global") - - tk.MustExec("drop stats test_drop_gstats partition p0") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1681|'DROP STATS ... PARTITION ...' is deprecated and will be removed in a future release.")) - checkPartitionStats("global", "p1", "global") - - err := tk.ExecToErr("drop stats test_drop_gstats partition abcde") - require.Error(t, err) - require.Equal(t, "can not found the specified partition name abcde in the table definition", err.Error()) - - tk.MustExec("drop stats test_drop_gstats partition global") - checkPartitionStats("global", "p1") - - tk.MustExec("drop stats test_drop_gstats global") - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1287|'DROP STATS ... GLOBAL' is deprecated and will be removed in a future release. Please use DROP STATS ... instead")) - checkPartitionStats("p1") - - tk.MustExec("analyze table test_drop_gstats") - checkPartitionStats("global", "p0", "p1", "global") - - tk.MustExec("drop stats test_drop_gstats partition p0, p1, global") - checkPartitionStats("global") - - tk.MustExec("analyze table test_drop_gstats") - checkPartitionStats("global", "p0", "p1", "global") - - tk.MustExec("drop stats test_drop_gstats") - checkPartitionStats() -} - -func TestDropStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := dom.StatsHandle() - h.Clear() - testKit.MustExec("analyze table t") - statsTbl := h.GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - testKit.MustExec("drop stats t") - require.Nil(t, h.Update(is)) - statsTbl = h.GetTableStats(tableInfo) - require.True(t, statsTbl.Pseudo) - - testKit.MustExec("analyze table t") - statsTbl = h.GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - h.SetLease(1) - testKit.MustExec("drop stats t") - require.Nil(t, h.Update(is)) - statsTbl = h.GetTableStats(tableInfo) - require.True(t, statsTbl.Pseudo) - h.SetLease(0) -} - -func TestDropStatsForMultipleTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t1 (c1 int, c2 int)") - testKit.MustExec("create table t2 (c1 int, c2 int)") - - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tableInfo2 := tbl2.Meta() - - h := dom.StatsHandle() - h.Clear() - testKit.MustExec("analyze table t1, t2") - statsTbl1 := h.GetTableStats(tableInfo1) - require.False(t, statsTbl1.Pseudo) - statsTbl2 := h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - - testKit.MustExec("drop stats t1, t2") - require.Nil(t, h.Update(is)) - statsTbl1 = h.GetTableStats(tableInfo1) - require.True(t, statsTbl1.Pseudo) - statsTbl2 = h.GetTableStats(tableInfo2) - require.True(t, statsTbl2.Pseudo) - - testKit.MustExec("analyze table t1, t2") - statsTbl1 = h.GetTableStats(tableInfo1) - require.False(t, statsTbl1.Pseudo) - statsTbl2 = h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - - h.SetLease(1) - testKit.MustExec("drop stats t1, t2") - require.Nil(t, h.Update(is)) - statsTbl1 = h.GetTableStats(tableInfo1) - require.True(t, statsTbl1.Pseudo) - statsTbl2 = h.GetTableStats(tableInfo2) - require.True(t, statsTbl2.Pseudo) - h.SetLease(0) -} - -func TestCreateUserWithLDAP(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("CREATE USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_simple AS 'uid=bob,ou=People,dc=example,dc=com'") - tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=People,dc=example,dc=com authentication_ldap_simple")) - - tk.MustExec("CREATE USER 'bob2'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob2,ou=People,dc=example,dc=com'") - tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob2'").Check(testkit.Rows("localhost bob2 uid=bob2,ou=People,dc=example,dc=com authentication_ldap_sasl")) -} - -func TestAlterUserWithLDAP(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // case 1: alter from a LDAP user to LDAP user - tk.MustExec("CREATE USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_simple AS 'uid=bob,ou=People,dc=example,dc=com'") - tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=People,dc=example,dc=com authentication_ldap_simple")) - tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") - tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=Manager,dc=example,dc=com authentication_ldap_sasl")) - - // case 2: should ignore the password history - tk.MustExec("ALTER USER 'bob'@'localhost' PASSWORD HISTORY 5\n") - tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=People,dc=example,dc=com'") - tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") - tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=People,dc=example,dc=com'") - tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") -} - -func TestIssue44098(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set global validate_password.enable = 1") - tk.MustExec("create user u1 identified with 'tidb_auth_token'") - tk.MustExec("create user u2 identified with 'auth_socket'") - tk.MustExec("create user u3 identified with 'authentication_ldap_simple'") - tk.MustExec("create user u4 identified with 'authentication_ldap_sasl'") - tk.MustGetErrCode("create user u5 identified with 'mysql_native_password'", errno.ErrNotValidPassword) - tk.MustGetErrCode("create user u5 identified with 'caching_sha2_password'", errno.ErrNotValidPassword) - tk.MustGetErrCode("create user u5 identified with 'tidb_sm3_password'", errno.ErrNotValidPassword) - tk.MustGetErrCode("create user u5 identified with 'mysql_clear_password'", errno.ErrPluginIsNotLoaded) - tk.MustGetErrCode("create user u5 identified with 'tidb_session_token'", errno.ErrPluginIsNotLoaded) -} diff --git a/executor/test/splittest/BUILD.bazel b/executor/test/splittest/BUILD.bazel deleted file mode 100644 index 9991491295297..0000000000000 --- a/executor/test/splittest/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "splittest_test", - timeout = "moderate", - srcs = [ - "main_test.go", - "split_table_test.go", - ], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//ddl", - "//domain/infosync", - "//errno", - "//kv", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//sessionctx/variable", - "//store/copr", - "//store/driver/backoff", - "//store/helper", - "//table", - "//testkit", - "//testkit/external", - "//util/benchdaily", - "//util/dbterror", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/tiflashtest/BUILD.bazel b/executor/test/tiflashtest/BUILD.bazel deleted file mode 100644 index 2f6658a8fa74f..0000000000000 --- a/executor/test/tiflashtest/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tiflashtest_test", - timeout = "moderate", - srcs = [ - "main_test.go", - "tiflash_test.go", - ], - flaky = True, - race = "on", - shard_count = 39, - deps = [ - "//config", - "//domain", - "//kv", - "//meta/autoid", - "//parser/terror", - "//planner/core", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit", - "//testkit/external", - "//util/dbterror/exeerrors", - "//util/memory", - "//util/tiflashcompute", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/tiflashtest/main_test.go b/executor/test/tiflashtest/main_test.go deleted file mode 100644 index 96a8fc14b388c..0000000000000 --- a/executor/test/tiflashtest/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tiflashtest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.Cleanup(func(_ int) { - view.Stop() - }), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/unstabletest/BUILD.bazel b/executor/test/unstabletest/BUILD.bazel deleted file mode 100644 index c9a73c83eb2d7..0000000000000 --- a/executor/test/unstabletest/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "unstabletest_test", - timeout = "short", - srcs = [ - "main_test.go", - "memory_test.go", - "unstable_test.go", - ], - flaky = True, - shard_count = 4, - deps = [ - "//config", - "//meta/autoid", - "//testkit", - "//util", - "//util/memory", - "//util/skip", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/unstabletest/main_test.go b/executor/test/unstabletest/main_test.go deleted file mode 100644 index bd803b07370a9..0000000000000 --- a/executor/test/unstabletest/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package unstabletest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.Cleanup(func(_ int) { - view.Stop() - }), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/test/writetest/BUILD.bazel b/executor/test/writetest/BUILD.bazel deleted file mode 100644 index fc1b09d703dfc..0000000000000 --- a/executor/test/writetest/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "writetest_test", - timeout = "short", - srcs = [ - "main_test.go", - "write_test.go", - ], - flaky = True, - shard_count = 30, - deps = [ - "//br/pkg/lightning/mydump", - "//config", - "//executor", - "//executor/internal", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//session", - "//sessionctx", - "//sessiontxn", - "//store/mockstore", - "//table/tables", - "//testkit", - "//types", - "//util", - "//util/mock", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/executor/test/writetest/main_test.go b/executor/test/writetest/main_test.go deleted file mode 100644 index 61ddeb0febce4..0000000000000 --- a/executor/test/writetest/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package writetest - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/meta/autoid" - "github.com/tikv/client-go/v2/tikv" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - autoid.SetStep(5000) - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowThreshold = 30000 // 30s - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - opts := []goleak.Option{ - goleak.Cleanup(func(_ int) { - view.Stop() - }), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/executor/trace.go b/executor/trace.go deleted file mode 100644 index 3798559d4a356..0000000000000 --- a/executor/trace.go +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "archive/zip" - "context" - "crypto/rand" - "encoding/base64" - "encoding/json" - "fmt" - "os" - "path/filepath" - "slices" - "strings" - "time" - - "github.com/opentracing/basictracer-go" - "github.com/opentracing/opentracing-go" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" - "sourcegraph.com/sourcegraph/appdash" - traceImpl "sourcegraph.com/sourcegraph/appdash/opentracing" -) - -// TraceExec represents a root executor of trace query. -type TraceExec struct { - exec.BaseExecutor - // CollectedSpans collects all span during execution. Span is appended via - // callback method which passes into tracer implementation. - CollectedSpans []basictracer.RawSpan - // exhausted being true means there is no more result. - exhausted bool - // stmtNode is the real query ast tree and it is used for building real query's plan. - stmtNode ast.StmtNode - - builder *executorBuilder - format string - - // optimizerTrace indicates 'trace plan statement' - optimizerTrace bool - optimizerTraceTarget string -} - -// Next executes real query and collects span later. -func (e *TraceExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if e.exhausted { - return nil - } - se, ok := e.Ctx().(sqlexec.SQLExecutor) - if !ok { - e.exhausted = true - return nil - } - - // For audit log plugin to set the correct statement. - stmtCtx := e.Ctx().GetSessionVars().StmtCtx - defer func() { - e.Ctx().GetSessionVars().StmtCtx = stmtCtx - }() - - if e.optimizerTrace { - switch e.optimizerTraceTarget { - case core.TracePlanTargetEstimation: - return e.nextOptimizerCEPlanTrace(ctx, e.Ctx(), req) - case core.TracePlanTargetDebug: - return e.nextOptimizerDebugPlanTrace(ctx, e.Ctx(), req) - default: - return e.nextOptimizerPlanTrace(ctx, e.Ctx(), req) - } - } - - ctx = util.ContextWithTraceExecDetails(ctx) - switch e.format { - case core.TraceFormatLog: - return e.nextTraceLog(ctx, se, req) - default: - return e.nextRowJSON(ctx, se, req) - } -} - -func (e *TraceExec) nextOptimizerCEPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { - stmtCtx := se.GetSessionVars().StmtCtx - origin := stmtCtx.EnableOptimizerCETrace - stmtCtx.EnableOptimizerCETrace = true - defer func() { - stmtCtx.EnableOptimizerCETrace = origin - }() - - _, _, err := core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) - if err != nil { - return err - } - - writer := strings.Builder{} - jsonEncoder := json.NewEncoder(&writer) - // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... - jsonEncoder.SetEscapeHTML(false) - err = jsonEncoder.Encode(stmtCtx.OptimizerCETrace) - if err != nil { - return errors.AddStack(err) - } - res := []byte(writer.String()) - - req.AppendBytes(0, res) - e.exhausted = true - return nil -} - -func (e *TraceExec) nextOptimizerDebugPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { - stmtCtx := se.GetSessionVars().StmtCtx - origin := stmtCtx.EnableOptimizerDebugTrace - stmtCtx.EnableOptimizerDebugTrace = true - defer func() { - stmtCtx.EnableOptimizerDebugTrace = origin - }() - - _, _, err := core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) - if err != nil { - return err - } - - writer := strings.Builder{} - jsonEncoder := json.NewEncoder(&writer) - // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... - jsonEncoder.SetEscapeHTML(false) - err = jsonEncoder.Encode(stmtCtx.OptimizerDebugTrace) - if err != nil { - return errors.AddStack(err) - } - res := []byte(writer.String()) - - req.AppendBytes(0, res) - e.exhausted = true - return nil -} - -func (e *TraceExec) nextOptimizerPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { - zf, fileName, err := generateOptimizerTraceFile() - if err != nil { - return err - } - zw := zip.NewWriter(zf) - defer func() { - err := zw.Close() - if err != nil { - logutil.BgLogger().Warn("Closing zip writer failed", zap.Error(err)) - } - err = zf.Close() - if err != nil { - logutil.BgLogger().Warn("Closing zip file failed", zap.Error(err)) - } - }() - traceZW, err := zw.Create("trace.json") - if err != nil { - return errors.AddStack(err) - } - stmtCtx := se.GetSessionVars().StmtCtx - origin := stmtCtx.EnableOptimizeTrace - stmtCtx.EnableOptimizeTrace = true - defer func() { - stmtCtx.EnableOptimizeTrace = origin - }() - _, _, err = core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) - if err != nil { - return err - } - - writer := strings.Builder{} - jsonEncoder := json.NewEncoder(&writer) - // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... - jsonEncoder.SetEscapeHTML(false) - err = jsonEncoder.Encode(se.GetSessionVars().StmtCtx.OptimizeTracer) - if err != nil { - return errors.AddStack(err) - } - res := []byte(writer.String()) - - _, err = traceZW.Write(res) - if err != nil { - return errors.AddStack(err) - } - req.AppendString(0, fileName) - e.exhausted = true - return nil -} - -func (e *TraceExec) nextTraceLog(ctx context.Context, se sqlexec.SQLExecutor, req *chunk.Chunk) error { - recorder := basictracer.NewInMemoryRecorder() - tracer := basictracer.New(recorder) - span := tracer.StartSpan("trace") - ctx = opentracing.ContextWithSpan(ctx, span) - - e.executeChild(ctx, se) - span.Finish() - - generateLogResult(recorder.GetSpans(), req) - e.exhausted = true - return nil -} - -func (e *TraceExec) nextRowJSON(ctx context.Context, se sqlexec.SQLExecutor, req *chunk.Chunk) error { - store := appdash.NewMemoryStore() - tracer := traceImpl.NewTracer(store) - span := tracer.StartSpan("trace") - ctx = opentracing.ContextWithSpan(ctx, span) - - e.executeChild(ctx, se) - span.Finish() - - traces, err := store.Traces(appdash.TracesOpts{}) - if err != nil { - return errors.Trace(err) - } - - // Row format. - if e.format != core.TraceFormatJSON { - if len(traces) < 1 { - e.exhausted = true - return nil - } - trace := traces[0] - dfsTree(trace, "", false, req) - e.exhausted = true - return nil - } - - // Json format. - data, err := json.Marshal(traces) - if err != nil { - return errors.Trace(err) - } - - // Split json data into rows to avoid the max packet size limitation. - const maxRowLen = 4096 - for len(data) > maxRowLen { - req.AppendString(0, string(data[:maxRowLen])) - data = data[maxRowLen:] - } - req.AppendString(0, string(data)) - e.exhausted = true - return nil -} - -func (e *TraceExec) executeChild(ctx context.Context, se sqlexec.SQLExecutor) { - // For audit log plugin to log the statement correctly. - // Should be logged as 'explain ...', instead of the executed SQL. - vars := e.Ctx().GetSessionVars() - origin := vars.InRestrictedSQL - vars.InRestrictedSQL = true - defer func() { - vars.InRestrictedSQL = origin - }() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnTrace) - rs, err := se.ExecuteStmt(ctx, e.stmtNode) - if err != nil { - var errCode uint16 - if te, ok := err.(*terror.Error); ok { - errCode = terror.ToSQLError(te).Code - } - logutil.Eventf(ctx, "execute with error(%d): %s", errCode, err.Error()) - } - if rs != nil { - drainRecordSet(ctx, e.Ctx(), rs) - if err = rs.Close(); err != nil { - logutil.Logger(ctx).Error("run trace close result with error", zap.Error(err)) - } - } - logutil.Eventf(ctx, "execute done, modify row: %d", e.Ctx().GetSessionVars().StmtCtx.AffectedRows()) -} - -func drainRecordSet(ctx context.Context, sctx sessionctx.Context, rs sqlexec.RecordSet) { - req := rs.NewChunk(nil) - var rowCount int - for { - err := rs.Next(ctx, req) - if err != nil || req.NumRows() == 0 { - if err != nil { - var errCode uint16 - if te, ok := err.(*terror.Error); ok { - errCode = terror.ToSQLError(te).Code - } - logutil.Eventf(ctx, "execute with error(%d): %s", errCode, err.Error()) - } else { - logutil.Eventf(ctx, "execute done, ReturnRow: %d, ModifyRow: %d", rowCount, sctx.GetSessionVars().StmtCtx.AffectedRows()) - } - return - } - rowCount += req.NumRows() - req.Reset() - } -} - -func dfsTree(t *appdash.Trace, prefix string, isLast bool, chk *chunk.Chunk) { - var newPrefix, suffix string - if prefix == "" { - newPrefix = prefix + " " - } else { - if !isLast { - suffix = "├─" - newPrefix = prefix + "│ " - } else { - suffix = "└─" - newPrefix = prefix + " " - } - } - - var start time.Time - var duration time.Duration - if e, err := t.TimespanEvent(); err == nil { - start = e.Start() - end := e.End() - duration = end.Sub(start) - } - - chk.AppendString(0, prefix+suffix+t.Span.Name()) - chk.AppendString(1, start.Format("15:04:05.000000")) - chk.AppendString(2, duration.String()) - - // Sort events by their start time - slices.SortFunc(t.Sub, func(i, j *appdash.Trace) int { - var istart, jstart time.Time - if ievent, err := i.TimespanEvent(); err == nil { - istart = ievent.Start() - } - if jevent, err := j.TimespanEvent(); err == nil { - jstart = jevent.Start() - } - return istart.Compare(jstart) - }) - - for i, sp := range t.Sub { - dfsTree(sp, newPrefix, i == (len(t.Sub))-1 /*last element of array*/, chk) - } -} - -func generateLogResult(allSpans []basictracer.RawSpan, chk *chunk.Chunk) { - for rIdx := range allSpans { - span := &allSpans[rIdx] - - chk.AppendTime(0, types.NewTime(types.FromGoTime(span.Start), mysql.TypeTimestamp, 6)) - chk.AppendString(1, "--- start span "+span.Operation+" ----") - chk.AppendString(2, "") - chk.AppendString(3, span.Operation) - - var tags string - if len(span.Tags) > 0 { - tags = fmt.Sprintf("%v", span.Tags) - } - for _, l := range span.Logs { - for _, field := range l.Fields { - if field.Key() == logutil.TraceEventKey { - chk.AppendTime(0, types.NewTime(types.FromGoTime(l.Timestamp), mysql.TypeTimestamp, 6)) - chk.AppendString(1, field.Value().(string)) - chk.AppendString(2, tags) - chk.AppendString(3, span.Operation) - } - } - } - } -} - -func generateOptimizerTraceFile() (*os.File, string, error) { - dirPath := domain.GetOptimizerTraceDirName() - // Create path - err := os.MkdirAll(dirPath, os.ModePerm) - if err != nil { - return nil, "", errors.AddStack(err) - } - // Generate key and create zip file - time := time.Now().UnixNano() - b := make([]byte, 16) - //nolint: gosec - _, err = rand.Read(b) - if err != nil { - return nil, "", errors.AddStack(err) - } - key := base64.URLEncoding.EncodeToString(b) - fileName := fmt.Sprintf("optimizer_trace_%v_%v.zip", key, time) - zf, err := os.Create(filepath.Join(dirPath, fileName)) - if err != nil { - return nil, "", errors.AddStack(err) - } - return zf, fileName, nil -} diff --git a/executor/trace_test.go b/executor/trace_test.go deleted file mode 100644 index 79e7a13402ce1..0000000000000 --- a/executor/trace_test.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestTraceExec(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - testSQL := `create table trace (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1);` - tk.MustExec(testSQL) - tk.MustExec("trace insert into trace (c1, c2, c3) values (1, 2, 3)") - rows := tk.MustQuery("trace select * from trace where id = 0;").Rows() - require.GreaterOrEqual(t, len(rows), 1) - - // +---------------------------+-----------------+------------+ - // | operation | snapshotTS | duration | - // +---------------------------+-----------------+------------+ - // | session.getTxnFuture | 22:08:38.247834 | 78.909µs | - // | ├─session.Execute | 22:08:38.247829 | 1.478487ms | - // | ├─session.ParseSQL | 22:08:38.248457 | 71.159µs | - // | ├─executor.Compile | 22:08:38.248578 | 45.329µs | - // | ├─session.runStmt | 22:08:38.248661 | 75.13µs | - // | ├─session.CommitTxn | 22:08:38.248699 | 13.213µs | - // | └─recordSet.Next | 22:08:38.249340 | 155.317µs | - // +---------------------------+-----------------+------------+ - rows = tk.MustQuery("trace format='row' select * from trace where id = 0;").Rows() - require.Greater(t, len(rows), 1) - require.True(t, rowsOrdered(rows)) - - rows = tk.MustQuery("trace format='row' delete from trace where id = 0").Rows() - require.Greater(t, len(rows), 1) - require.True(t, rowsOrdered(rows)) - - rows = tk.MustQuery("trace format='row' analyze table trace").Rows() - require.Greater(t, len(rows), 1) - require.True(t, rowsOrdered(rows)) - - tk.MustExec("trace format='log' insert into trace (c1, c2, c3) values (1, 2, 3)") - rows = tk.MustQuery("trace format='log' select * from trace where id = 0;").Rows() - require.GreaterOrEqual(t, len(rows), 1) -} - -func rowsOrdered(rows [][]interface{}) bool { - for idx := range rows { - if _, ok := rows[idx][1].(string); !ok { - return false - } - if idx == 0 { - continue - } - if rows[idx-1][1].(string) > rows[idx][1].(string) { - return false - } - } - return true -} - -func TestTracePlanStmt(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table tp1(id int);") - tk.MustExec("create table tp2(id int);") - tk.MustExec("set @@tidb_cost_model_version=2") - rows := tk.MustQuery("trace plan select * from tp1 t1, tp2 t2 where t1.id = t2.id").Rows() - require.Len(t, rows, 1) - require.Len(t, rows[0], 1) - require.Regexp(t, ".*zip", rows[0][0]) -} diff --git a/executor/update.go b/executor/update.go deleted file mode 100644 index c4362cd57f2f1..0000000000000 --- a/executor/update.go +++ /dev/null @@ -1,563 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "bytes" - "context" - "fmt" - "runtime/trace" - - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/memory" - "github.com/tikv/client-go/v2/txnkv/txnsnapshot" -) - -// UpdateExec represents a new update executor. -type UpdateExec struct { - exec.BaseExecutor - - OrderedList []*expression.Assignment - - // updatedRowKeys is a map for unique (TableAlias, handle) pair. - // The value is true if the row is changed, or false otherwise - updatedRowKeys map[int]*kv.MemAwareHandleMap[bool] - tblID2table map[int64]table.Table - // mergedRowData is a map for unique (Table, handle) pair. - // The value is cached table row - mergedRowData map[int64]*kv.MemAwareHandleMap[[]types.Datum] - multiUpdateOnSameTable map[int64]bool - - matched uint64 // a counter of matched rows during update - // tblColPosInfos stores relationship between column ordinal to its table handle. - // the columns ordinals is present in ordinal range format, @see plannercore.TblColPosInfos - tblColPosInfos plannercore.TblColPosInfoSlice - assignFlag []int - evalBuffer chunk.MutRow - allAssignmentsAreConstant bool - virtualAssignmentsOffset int - drained bool - memTracker *memory.Tracker - - stats *updateRuntimeStats - - handles []kv.Handle - tableUpdatable []bool - changed []bool - matches []bool - // fkChecks contains the foreign key checkers. the map is tableID -> []*FKCheckExec - fkChecks map[int64][]*FKCheckExec - // fkCascades contains the foreign key cascade. the map is tableID -> []*FKCascadeExec - fkCascades map[int64][]*FKCascadeExec -} - -// prepare `handles`, `tableUpdatable`, `changed` to avoid re-computations. -func (e *UpdateExec) prepare(row []types.Datum) (err error) { - if e.updatedRowKeys == nil { - e.updatedRowKeys = make(map[int]*kv.MemAwareHandleMap[bool]) - } - e.handles = e.handles[:0] - e.tableUpdatable = e.tableUpdatable[:0] - e.changed = e.changed[:0] - e.matches = e.matches[:0] - for _, content := range e.tblColPosInfos { - if e.updatedRowKeys[content.Start] == nil { - e.updatedRowKeys[content.Start] = kv.NewMemAwareHandleMap[bool]() - } - handle, err := content.HandleCols.BuildHandleByDatums(row) - if err != nil { - return err - } - e.handles = append(e.handles, handle) - - updatable := false - flags := e.assignFlag[content.Start:content.End] - for _, flag := range flags { - if flag >= 0 { - updatable = true - break - } - } - if unmatchedOuterRow(content, row) { - updatable = false - } - e.tableUpdatable = append(e.tableUpdatable, updatable) - - changed, ok := e.updatedRowKeys[content.Start].Get(handle) - if ok { - e.changed = append(e.changed, changed) - e.matches = append(e.matches, false) - } else { - e.changed = append(e.changed, false) - e.matches = append(e.matches, true) - } - } - return nil -} - -func (e *UpdateExec) merge(row, newData []types.Datum, mergeGenerated bool) error { - if e.mergedRowData == nil { - e.mergedRowData = make(map[int64]*kv.MemAwareHandleMap[[]types.Datum]) - } - var mergedData []types.Datum - // merge updates from and into mergedRowData - for i, content := range e.tblColPosInfos { - if !e.multiUpdateOnSameTable[content.TblID] { - // No need to merge if not multi-updated - continue - } - if !e.tableUpdatable[i] { - // If there's nothing to update, we can just skip current row - continue - } - if e.changed[i] { - // Each matched row is updated once, even if it matches the conditions multiple times. - continue - } - handle := e.handles[i] - flags := e.assignFlag[content.Start:content.End] - - if e.mergedRowData[content.TblID] == nil { - e.mergedRowData[content.TblID] = kv.NewMemAwareHandleMap[[]types.Datum]() - } - tbl := e.tblID2table[content.TblID] - oldData := row[content.Start:content.End] - newTableData := newData[content.Start:content.End] - if v, ok := e.mergedRowData[content.TblID].Get(handle); ok { - mergedData = v - for i, flag := range flags { - if tbl.WritableCols()[i].IsGenerated() != mergeGenerated { - continue - } - mergedData[i].Copy(&oldData[i]) - if flag >= 0 { - newTableData[i].Copy(&mergedData[i]) - } else { - mergedData[i].Copy(&newTableData[i]) - } - } - } else { - mergedData = append([]types.Datum{}, newTableData...) - } - - memDelta := e.mergedRowData[content.TblID].Set(handle, mergedData) - memDelta += types.EstimatedMemUsage(mergedData, 1) + int64(handle.ExtraMemSize()) - e.memTracker.Consume(memDelta) - } - return nil -} - -func (e *UpdateExec) exec(ctx context.Context, _ *expression.Schema, row, newData []types.Datum) error { - defer trace.StartRegion(ctx, "UpdateExec").End() - bAssignFlag := make([]bool, len(e.assignFlag)) - for i, flag := range e.assignFlag { - bAssignFlag[i] = flag >= 0 - } - for i, content := range e.tblColPosInfos { - if !e.tableUpdatable[i] { - // If there's nothing to update, we can just skip current row - continue - } - if e.changed[i] { - // Each matched row is updated once, even if it matches the conditions multiple times. - continue - } - if e.matches[i] { - // Row is matched for the first time, increment `matched` counter - e.matched++ - } - tbl := e.tblID2table[content.TblID] - handle := e.handles[i] - - oldData := row[content.Start:content.End] - newTableData := newData[content.Start:content.End] - flags := bAssignFlag[content.Start:content.End] - - // Update row - fkChecks := e.fkChecks[content.TblID] - fkCascades := e.fkCascades[content.TblID] - changed, err1 := updateRecord(ctx, e.Ctx(), handle, oldData, newTableData, flags, tbl, false, e.memTracker, fkChecks, fkCascades) - if err1 == nil { - _, exist := e.updatedRowKeys[content.Start].Get(handle) - memDelta := e.updatedRowKeys[content.Start].Set(handle, changed) - if !exist { - memDelta += int64(handle.ExtraMemSize()) - } - e.memTracker.Consume(memDelta) - continue - } - - sc := e.Ctx().GetSessionVars().StmtCtx - if (kv.ErrKeyExists.Equal(err1) || table.ErrCheckConstraintViolated.Equal(err1)) && sc.DupKeyAsWarning { - sc.AppendWarning(err1) - continue - } - return err1 - } - return nil -} - -// unmatchedOuterRow checks the tableCols of a record to decide whether that record -// can not be updated. The handle is NULL only when it is the inner side of an -// outer join: the outer row can not match any inner rows, and in this scenario -// the inner handle field is filled with a NULL value. -// -// This fixes: https://github.com/pingcap/tidb/issues/7176. -func unmatchedOuterRow(tblPos plannercore.TblColPosInfo, waitUpdateRow []types.Datum) bool { - firstHandleIdx := tblPos.HandleCols.GetCol(0) - return waitUpdateRow[firstHandleIdx.Index].IsNull() -} - -// Next implements the Executor Next interface. -func (e *UpdateExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - if !e.drained { - if e.collectRuntimeStatsEnabled() { - ctx = context.WithValue(ctx, autoid.AllocatorRuntimeStatsCtxKey, e.stats.AllocatorRuntimeStats) - } - numRows, err := e.updateRows(ctx) - if err != nil { - return err - } - e.drained = true - e.Ctx().GetSessionVars().StmtCtx.AddRecordRows(uint64(numRows)) - } - return nil -} - -func (e *UpdateExec) updateRows(ctx context.Context) (int, error) { - fields := exec.RetTypes(e.Children(0)) - colsInfo := plannercore.GetUpdateColumnsInfo(e.tblID2table, e.tblColPosInfos, len(fields)) - globalRowIdx := 0 - chk := exec.TryNewCacheChunk(e.Children(0)) - if !e.allAssignmentsAreConstant { - e.evalBuffer = chunk.MutRowFromTypes(fields) - } - composeFunc := e.fastComposeNewRow - if !e.allAssignmentsAreConstant { - composeFunc = e.composeNewRow - } - memUsageOfChk := int64(0) - totalNumRows := 0 - for { - e.memTracker.Consume(-memUsageOfChk) - err := exec.Next(ctx, e.Children(0), chk) - if err != nil { - return 0, err - } - - if chk.NumRows() == 0 { - break - } - memUsageOfChk = chk.MemoryUsage() - e.memTracker.Consume(memUsageOfChk) - if e.collectRuntimeStatsEnabled() { - txn, err := e.Ctx().Txn(true) - if err == nil && txn.GetSnapshot() != nil { - txn.GetSnapshot().SetOption(kv.CollectRuntimeStats, e.stats.SnapshotRuntimeStats) - } - } - txn, err := e.Ctx().Txn(true) - if err == nil { - sc := e.Ctx().GetSessionVars().StmtCtx - txn.SetOption(kv.ResourceGroupTagger, sc.GetResourceGroupTagger()) - if sc.KvExecCounter != nil { - // Bind an interceptor for client-go to count the number of SQL executions of each TiKV. - txn.SetOption(kv.RPCInterceptor, sc.KvExecCounter.RPCInterceptor()) - } - } - for rowIdx := 0; rowIdx < chk.NumRows(); rowIdx++ { - chunkRow := chk.GetRow(rowIdx) - datumRow := chunkRow.GetDatumRow(fields) - // precomputes handles - if err := e.prepare(datumRow); err != nil { - return 0, err - } - // compose non-generated columns - newRow, err := composeFunc(globalRowIdx, datumRow, colsInfo) - if err != nil { - return 0, err - } - // merge non-generated columns - if err := e.merge(datumRow, newRow, false); err != nil { - return 0, err - } - if e.virtualAssignmentsOffset < len(e.OrderedList) { - // compose generated columns - newRow, err = e.composeGeneratedColumns(globalRowIdx, newRow, colsInfo) - if err != nil { - return 0, err - } - // merge generated columns - if err := e.merge(datumRow, newRow, true); err != nil { - return 0, err - } - } - // write to table - if err := e.exec(ctx, e.Children(0).Schema(), datumRow, newRow); err != nil { - return 0, err - } - } - totalNumRows += chk.NumRows() - chk = chunk.Renew(chk, e.MaxChunkSize()) - } - return totalNumRows, nil -} - -func (*UpdateExec) handleErr(colName model.CIStr, rowIdx int, err error) error { - if err == nil { - return nil - } - - if types.ErrDataTooLong.Equal(err) { - return resetErrDataTooLong(colName.O, rowIdx+1, err) - } - - if types.ErrOverflow.Equal(err) { - return types.ErrWarnDataOutOfRange.GenWithStackByArgs(colName.O, rowIdx+1) - } - - return err -} - -func (e *UpdateExec) fastComposeNewRow(rowIdx int, oldRow []types.Datum, cols []*table.Column) ([]types.Datum, error) { - newRowData := types.CloneRow(oldRow) - for _, assign := range e.OrderedList { - tblIdx := e.assignFlag[assign.Col.Index] - if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { - continue - } - con := assign.Expr.(*expression.Constant) - val, err := con.Eval(emptyRow) - if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { - return nil, err - } - - // info of `_tidb_rowid` column is nil. - // No need to cast `_tidb_rowid` column value. - if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) - if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { - return nil, err - } - } - - val.Copy(&newRowData[assign.Col.Index]) - } - return newRowData, nil -} - -func (e *UpdateExec) composeNewRow(rowIdx int, oldRow []types.Datum, cols []*table.Column) ([]types.Datum, error) { - newRowData := types.CloneRow(oldRow) - e.evalBuffer.SetDatums(newRowData...) - for _, assign := range e.OrderedList[:e.virtualAssignmentsOffset] { - tblIdx := e.assignFlag[assign.Col.Index] - if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { - continue - } - val, err := assign.Expr.Eval(e.evalBuffer.ToRow()) - if err != nil { - return nil, err - } - - // info of `_tidb_rowid` column is nil. - // No need to cast `_tidb_rowid` column value. - if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) - if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { - return nil, err - } - } - - val.Copy(&newRowData[assign.Col.Index]) - } - return newRowData, nil -} - -func (e *UpdateExec) composeGeneratedColumns(rowIdx int, newRowData []types.Datum, cols []*table.Column) ([]types.Datum, error) { - if e.allAssignmentsAreConstant { - return newRowData, nil - } - e.evalBuffer.SetDatums(newRowData...) - for _, assign := range e.OrderedList[e.virtualAssignmentsOffset:] { - tblIdx := e.assignFlag[assign.Col.Index] - if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { - continue - } - val, err := assign.Expr.Eval(e.evalBuffer.ToRow()) - if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { - return nil, err - } - - // info of `_tidb_rowid` column is nil. - // No need to cast `_tidb_rowid` column value. - if cols[assign.Col.Index] != nil { - val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) - if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { - return nil, err - } - } - - val.Copy(&newRowData[assign.Col.Index]) - e.evalBuffer.SetDatum(assign.Col.Index, val) - } - return newRowData, nil -} - -// Close implements the Executor Close interface. -func (e *UpdateExec) Close() error { - defer e.memTracker.ReplaceBytesUsed(0) - e.setMessage() - if e.RuntimeStats() != nil && e.stats != nil { - txn, err := e.Ctx().Txn(false) - if err == nil && txn.Valid() && txn.GetSnapshot() != nil { - txn.GetSnapshot().SetOption(kv.CollectRuntimeStats, nil) - } - defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) - } - return e.Children(0).Close() -} - -// Open implements the Executor Open interface. -func (e *UpdateExec) Open(ctx context.Context) error { - e.memTracker = memory.NewTracker(e.ID(), -1) - e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) - - return e.Children(0).Open(ctx) -} - -// setMessage sets info message(ERR_UPDATE_INFO) generated by UPDATE statement -func (e *UpdateExec) setMessage() { - stmtCtx := e.Ctx().GetSessionVars().StmtCtx - numMatched := e.matched - numChanged := stmtCtx.UpdatedRows() - numWarnings := stmtCtx.WarningCount() - msg := fmt.Sprintf(mysql.MySQLErrName[mysql.ErrUpdateInfo].Raw, numMatched, numChanged, numWarnings) - stmtCtx.SetMessage(msg) -} - -func (e *UpdateExec) collectRuntimeStatsEnabled() bool { - if e.RuntimeStats() != nil { - if e.stats == nil { - e.stats = &updateRuntimeStats{ - SnapshotRuntimeStats: &txnsnapshot.SnapshotRuntimeStats{}, - AllocatorRuntimeStats: autoid.NewAllocatorRuntimeStats(), - } - } - return true - } - return false -} - -// updateRuntimeStats is the execution stats about update statements. -type updateRuntimeStats struct { - *txnsnapshot.SnapshotRuntimeStats - *autoid.AllocatorRuntimeStats -} - -func (e *updateRuntimeStats) String() string { - if e.SnapshotRuntimeStats == nil && e.AllocatorRuntimeStats == nil { - return "" - } - buf := bytes.NewBuffer(make([]byte, 0, 16)) - if e.SnapshotRuntimeStats != nil { - stats := e.SnapshotRuntimeStats.String() - if stats != "" { - buf.WriteString(stats) - } - } - if e.AllocatorRuntimeStats != nil { - stats := e.AllocatorRuntimeStats.String() - if stats != "" { - if buf.Len() > 0 { - buf.WriteString(", ") - } - buf.WriteString(stats) - } - } - return buf.String() -} - -// Clone implements the RuntimeStats interface. -func (e *updateRuntimeStats) Clone() execdetails.RuntimeStats { - newRs := &updateRuntimeStats{} - if e.SnapshotRuntimeStats != nil { - snapshotStats := e.SnapshotRuntimeStats.Clone() - newRs.SnapshotRuntimeStats = snapshotStats - } - if e.AllocatorRuntimeStats != nil { - newRs.AllocatorRuntimeStats = e.AllocatorRuntimeStats.Clone() - } - return newRs -} - -// Merge implements the RuntimeStats interface. -func (e *updateRuntimeStats) Merge(other execdetails.RuntimeStats) { - tmp, ok := other.(*updateRuntimeStats) - if !ok { - return - } - if tmp.SnapshotRuntimeStats != nil { - if e.SnapshotRuntimeStats == nil { - snapshotStats := tmp.SnapshotRuntimeStats.Clone() - e.SnapshotRuntimeStats = snapshotStats - } else { - e.SnapshotRuntimeStats.Merge(tmp.SnapshotRuntimeStats) - } - } - if tmp.AllocatorRuntimeStats != nil { - if e.AllocatorRuntimeStats == nil { - e.AllocatorRuntimeStats = tmp.AllocatorRuntimeStats.Clone() - } - } -} - -// Tp implements the RuntimeStats interface. -func (*updateRuntimeStats) Tp() int { - return execdetails.TpUpdateRuntimeStats -} - -// GetFKChecks implements WithForeignKeyTrigger interface. -func (e *UpdateExec) GetFKChecks() []*FKCheckExec { - fkChecks := make([]*FKCheckExec, 0, len(e.fkChecks)) - for _, fkc := range e.fkChecks { - fkChecks = append(fkChecks, fkc...) - } - return fkChecks -} - -// GetFKCascades implements WithForeignKeyTrigger interface. -func (e *UpdateExec) GetFKCascades() []*FKCascadeExec { - fkCascades := make([]*FKCascadeExec, 0, len(e.fkChecks)) - for _, fkc := range e.fkCascades { - fkCascades = append(fkCascades, fkc...) - } - return fkCascades -} - -// HasFKCascades implements WithForeignKeyTrigger interface. -func (e *UpdateExec) HasFKCascades() bool { - return len(e.fkCascades) > 0 -} diff --git a/executor/update_test.go b/executor/update_test.go deleted file mode 100644 index 10f1b5cb87578..0000000000000 --- a/executor/update_test.go +++ /dev/null @@ -1,644 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package executor_test - -import ( - "fmt" - "strconv" - "testing" - "time" - - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestUpdateGenColInTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table t(a bigint, b bigint as (a+1));`) - tk.MustExec(`begin;`) - tk.MustExec(`insert into t(a) values(1);`) - err := tk.ExecToErr(`update t set b=6 where b=2;`) - require.Equal( - t, - "[planner:3105]The value specified for generated column 'b' in table 't' is not allowed.", - err.Error(), - ) - tk.MustExec(`commit;`) - tk.MustQuery(`select * from t;`).Check( - testkit.Rows( - `1 2`, - ), - ) -} - -func TestUpdateWithAutoidSchema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`create table t1(id int primary key auto_increment, n int);`) - tk.MustExec(`create table t2(id int primary key, n float auto_increment, key I_n(n));`) - tk.MustExec(`create table t3(id int primary key, n double auto_increment, key I_n(n));`) - - tests := []struct { - exec string - query string - result [][]interface{} - }{ - { - `insert into t1 set n = 1`, - `select * from t1 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `update t1 set id = id+1`, - `select * from t1 where id = 2`, - testkit.Rows(`2 1`), - }, - { - `insert into t1 set n = 2`, - `select * from t1 where id = 3`, - testkit.Rows(`3 2`), - }, - { - `update t1 set id = id + '1.1' where id = 3`, - `select * from t1 where id = 4`, - testkit.Rows(`4 2`), - }, - { - `insert into t1 set n = 3`, - `select * from t1 where id = 5`, - testkit.Rows(`5 3`), - }, - { - `update t1 set id = id + '0.5' where id = 5`, - `select * from t1 where id = 6`, - testkit.Rows(`6 3`), - }, - { - `insert into t1 set n = 4`, - `select * from t1 where id = 7`, - testkit.Rows(`7 4`), - }, - { - `insert into t2 set id = 1`, - `select * from t2 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `update t2 set n = n+1`, - `select * from t2 where id = 1`, - testkit.Rows(`1 2`), - }, - { - `insert into t2 set id = 2`, - `select * from t2 where id = 2`, - testkit.Rows(`2 3`), - }, - { - `update t2 set n = n + '2.2'`, - `select * from t2 where id = 2`, - testkit.Rows(`2 5.2`), - }, - { - `insert into t2 set id = 3`, - `select * from t2 where id = 3`, - testkit.Rows(`3 6`), - }, - { - `update t2 set n = n + '0.5' where id = 3`, - `select * from t2 where id = 3`, - testkit.Rows(`3 6.5`), - }, - { - `insert into t2 set id = 4`, - `select * from t2 where id = 4`, - testkit.Rows(`4 7`), - }, - { - `insert into t3 set id = 1`, - `select * from t3 where id = 1`, - testkit.Rows(`1 1`), - }, - { - `update t3 set n = n+1`, - `select * from t3 where id = 1`, - testkit.Rows(`1 2`), - }, - { - `insert into t3 set id = 2`, - `select * from t3 where id = 2`, - testkit.Rows(`2 3`), - }, - { - `update t3 set n = n + '3.3'`, - `select * from t3 where id = 2`, - testkit.Rows(`2 6.3`), - }, - { - `insert into t3 set id = 3`, - `select * from t3 where id = 3`, - testkit.Rows(`3 7`), - }, - { - `update t3 set n = n + '0.5' where id = 3`, - `select * from t3 where id = 3`, - testkit.Rows(`3 7.5`), - }, - { - `insert into t3 set id = 4`, - `select * from t3 where id = 4`, - testkit.Rows(`4 8`), - }, - } - - for _, tt := range tests { - tk.MustExec(tt.exec) - tk.MustQuery(tt.query).Check(tt.result) - } -} - -func TestUpdateSchemaChange(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table t(a bigint, b bigint as (a+1));`) - tk.MustExec(`begin;`) - tk.MustExec(`insert into t(a) values(1);`) - err := tk.ExecToErr(`update t set b=6 where b=2;`) - require.Equal( - t, - "[planner:3105]The value specified for generated column 'b' in table 't' is not allowed.", - err.Error(), - ) - tk.MustExec(`commit;`) - tk.MustQuery(`select * from t;`).Check( - testkit.Rows( - `1 2`, - ), - ) -} - -func TestUpdateMultiDatabaseTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop database if exists test2") - tk.MustExec("create database test2") - tk.MustExec("create table t(a int, b int generated always as (a+1) virtual)") - tk.MustExec("create table test2.t(a int, b int generated always as (a+1) virtual)") - tk.MustExec("update t, test2.t set test.t.a=1") -} - -func TestUpdateSwapColumnValues(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1 (c_str varchar(40))") - tk.MustExec("create table t2 (c_str varchar(40))") - tk.MustExec("insert into t1 values ('Alice')") - tk.MustExec("insert into t2 values ('Bob')") - tk.MustQuery("select t1.c_str, t2.c_str from t1, t2 where t1.c_str <= t2.c_str").Check(testkit.Rows("Alice Bob")) - tk.MustExec("update t1, t2 set t1.c_str = t2.c_str, t2.c_str = t1.c_str where t1.c_str <= t2.c_str") - tk.MustQuery("select t1.c_str, t2.c_str from t1, t2 where t1.c_str <= t2.c_str").Check(testkit.Rows()) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values(1, 2)") - tk.MustQuery("select * from t").Check(testkit.Rows("1 2")) - tk.MustExec("update t set a=b, b=a") - tk.MustQuery("select * from t").Check(testkit.Rows("2 1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (1,3)") - tk.MustQuery("select * from t").Check(testkit.Rows("1 3")) - tk.MustExec("update t set a=b, b=a") - tk.MustQuery("select * from t").Check(testkit.Rows("3 1")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, c int as (-a) virtual, d int as (-b) stored)") - tk.MustExec("insert into t(a, b) values (10, 11), (20, 22)") - tk.MustQuery("select * from t").Check(testkit.Rows("10 11 -10 -11", "20 22 -20 -22")) - tk.MustExec("update t set a=b, b=a") - tk.MustQuery("select * from t").Check(testkit.Rows("11 10 -11 -10", "22 20 -22 -20")) - tk.MustExec("update t set b=30, a=b") - tk.MustQuery("select * from t").Check(testkit.Rows("10 30 -10 -30", "20 30 -20 -30")) -} - -func TestMultiUpdateOnSameTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(x int, y int)") - tk.MustExec("insert into t values()") - tk.MustExec("update t t1, t t2 set t2.y=1, t1.x=2") - tk.MustQuery("select * from t").Check(testkit.Rows("2 1")) - tk.MustExec("update t t1, t t2 set t1.x=t2.y, t2.y=t1.x") - tk.MustQuery("select * from t").Check(testkit.Rows("1 2")) - - // Update generated columns - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(x int, y int, z int as (x+10) stored, w int as (y-10) virtual)") - tk.MustExec("insert into t(x, y) values(1, 2), (3, 4)") - tk.MustExec("update t t1, t t2 set t2.y=1, t1.x=2 where t1.x=1") - tk.MustQuery("select * from t").Check(testkit.Rows("2 1 12 -9", "3 1 13 -9")) - - tk.MustExec("update t t1, t t2 set t1.x=5, t2.y=t1.x where t1.x=3") - tk.MustQuery("select * from t").Check(testkit.Rows("2 3 12 -7", "5 3 15 -7")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int as (a+b) stored)") - tk.MustExec("insert into t(a, b) values (1, 2)") - tk.MustExec("update t t1, t t2 set t2.a=3") - tk.MustQuery("select * from t").Check(testkit.Rows("3 2 5")) - - tk.MustExec("update t t1, t t2 set t1.a=4, t2.b=5") - tk.MustQuery("select * from t").Check(testkit.Rows("4 5 9")) - - // Update primary keys - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int primary key)") - tk.MustExec("insert into t values (1), (2)") - tk.MustExec("update t set a=a+2") - tk.MustQuery("select * from t").Check(testkit.Rows("3", "4")) - tk.MustExec("update t m, t n set m.a = n.a+10 where m.a=n.a") - tk.MustQuery("select * from t").Check(testkit.Rows("13", "14")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int primary key, b int)") - tk.MustExec("insert into t values (1,3), (2,4)") - tk.MustGetErrMsg( - "update t m, t n set m.a = n.a+10, n.b = m.b+1 where m.a=n.a", - `[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`, - ) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, c int, primary key(a, b))") - tk.MustExec("insert into t values (1,3,5), (2,4,6)") - tk.MustExec("update t m, t n set m.a = n.a+10, m.b = n.b+10 where m.a=n.a") - tk.MustQuery("select * from t").Check(testkit.Rows("11 13 5", "12 14 6")) - tk.MustExec("update t m, t n, t q set q.c=m.a+n.b, n.c = m.a+1, m.c = n.b+1 where m.b=n.b AND m.a=q.a") - tk.MustQuery("select * from t").Check(testkit.Rows("11 13 24", "12 14 26")) - tk.MustGetErrMsg( - "update t m, t n, t q set m.a = m.a+1, n.c = n.c-1, q.c = q.a+q.b where m.b=n.b and n.b=q.b", - `[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`, - ) -} - -func TestUpdateClusterIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(id varchar(200) primary key, v int)`) - tk.MustExec(`insert into t(id, v) values ('abc', 233)`) - tk.MustQuery(`select id, v from t where id = 'abc'`).Check(testkit.Rows("abc 233")) - tk.MustExec(`update t set id = 'dfg' where id = 'abc'`) - tk.MustQuery(`select * from t`).Check(testkit.Rows("dfg 233")) - tk.MustExec(`update t set id = 'aaa', v = 333 where id = 'dfg'`) - tk.MustQuery(`select * from t where id = 'aaa'`).Check(testkit.Rows("aaa 333")) - tk.MustExec(`update t set v = 222 where id = 'aaa'`) - tk.MustQuery(`select * from t where id = 'aaa'`).Check(testkit.Rows("aaa 222")) - tk.MustExec(`insert into t(id, v) values ('bbb', 111)`) - tk.MustGetErrCode(`update t set id = 'bbb' where id = 'aaa'`, errno.ErrDupEntry) - - tk.MustExec(`drop table if exists ut3pk`) - tk.MustExec(`create table ut3pk(id1 varchar(200), id2 varchar(200), v int, id3 int, primary key(id1, id2, id3))`) - tk.MustExec(`insert into ut3pk(id1, id2, v, id3) values ('aaa', 'bbb', 233, 111)`) - tk.MustQuery(`select id1, id2, id3, v from ut3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows("aaa bbb 111 233")) - tk.MustExec(`update ut3pk set id1 = 'abc', id2 = 'bbb2', id3 = 222, v = 555 where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`) - tk.MustQuery(`select id1, id2, id3, v from ut3pk where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`).Check(testkit.Rows("abc bbb2 222 555")) - tk.MustQuery(`select id1, id2, id3, v from ut3pk`).Check(testkit.Rows("abc bbb2 222 555")) - tk.MustExec(`update ut3pk set v = 666 where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`) - tk.MustQuery(`select id1, id2, id3, v from ut3pk`).Check(testkit.Rows("abc bbb2 222 666")) - tk.MustExec(`insert into ut3pk(id1, id2, id3, v) values ('abc', 'bbb3', 222, 777)`) - tk.MustGetErrCode( - `update ut3pk set id2 = 'bbb3' where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`, - errno.ErrDupEntry, - ) - - tk.MustExec(`drop table if exists ut1pku`) - tk.MustExec(`create table ut1pku(id varchar(200) primary key, uk int, v int, unique key ukk(uk))`) - tk.MustExec(`insert into ut1pku(id, uk, v) values('a', 1, 2), ('b', 2, 3)`) - tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 1 2", "b 2 3")) - tk.MustExec(`update ut1pku set uk = 3 where id = 'a'`) - tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 3 2", "b 2 3")) - tk.MustGetErrCode(`update ut1pku set uk = 2 where id = 'a'`, errno.ErrDupEntry) - tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 3 2", "b 2 3")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a char(10) primary key, b char(10));") - tk.MustExec("insert into t values('a', 'b');") - tk.MustExec("update t set a='c' where t.a='a' and b='b';") - tk.MustQuery("select * from t").Check(testkit.Rows("c b")) - - tk.MustExec("drop table if exists s") - tk.MustExec("create table s (a int, b int, c int, primary key (a, b))") - tk.MustExec("insert s values (3, 3, 3), (5, 5, 5)") - tk.MustExec("update s set c = 10 where a = 3") - tk.MustQuery("select * from s").Check(testkit.Rows("3 3 10", "5 5 5")) -} - -func TestDeleteClusterIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(id varchar(200) primary key, v int)`) - tk.MustExec(`insert into t(id, v) values ('abc', 233)`) - tk.MustExec(`delete from t where id = 'abc'`) - tk.MustQuery(`select * from t`).Check(testkit.Rows()) - tk.MustQuery(`select * from t where id = 'abc'`).Check(testkit.Rows()) - - tk.MustExec(`drop table if exists it3pk`) - tk.MustExec(`create table it3pk(id1 varchar(200), id2 varchar(200), v int, id3 int, primary key(id1, id2, id3))`) - tk.MustExec(`insert into it3pk(id1, id2, v, id3) values ('aaa', 'bbb', 233, 111)`) - tk.MustExec(`delete from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`) - tk.MustQuery(`select * from it3pk`).Check(testkit.Rows()) - tk.MustQuery(`select * from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows()) - tk.MustExec(`insert into it3pk(id1, id2, v, id3) values ('aaa', 'bbb', 433, 111)`) - tk.MustQuery(`select * from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows("aaa bbb 433 111")) - - tk.MustExec(`drop table if exists dt3pku`) - tk.MustExec(`create table dt3pku(id varchar(200) primary key, uk int, v int, unique key uuk(uk))`) - tk.MustExec(`insert into dt3pku(id, uk, v) values('a', 1, 2)`) - tk.MustExec(`delete from dt3pku where id = 'a'`) - tk.MustQuery(`select * from dt3pku`).Check(testkit.Rows()) - tk.MustExec(`insert into dt3pku(id, uk, v) values('a', 1, 2)`) - - tk.MustExec("drop table if exists s1") - tk.MustExec("create table s1 (a int, b int, c int, primary key (a, b))") - tk.MustExec("insert s1 values (3, 3, 3), (5, 5, 5)") - tk.MustExec("delete from s1 where a = 3") - tk.MustQuery("select * from s1").Check(testkit.Rows("5 5 5")) -} - -func TestReplaceClusterIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - - tk.MustExec(`drop table if exists rt1pk`) - tk.MustExec(`create table rt1pk(id varchar(200) primary key, v int)`) - tk.MustExec(`replace into rt1pk(id, v) values('abc', 1)`) - tk.MustQuery(`select * from rt1pk`).Check(testkit.Rows("abc 1")) - tk.MustExec(`replace into rt1pk(id, v) values('bbb', 233), ('abc', 2)`) - tk.MustQuery(`select * from rt1pk`).Check(testkit.Rows("abc 2", "bbb 233")) - - tk.MustExec(`drop table if exists rt3pk`) - tk.MustExec(`create table rt3pk(id1 timestamp, id2 time, v int, id3 year, primary key(id1, id2, id3))`) - tk.MustExec(`replace into rt3pk(id1, id2,id3, v) values('2018-01-01 11:11:11', '22:22:22', '2019', 1)`) - tk.MustQuery(`select * from rt3pk`).Check(testkit.Rows("2018-01-01 11:11:11 22:22:22 1 2019")) - tk.MustExec(`replace into rt3pk(id1, id2, id3, v) values('2018-01-01 11:11:11', '22:22:22', '2019', 2)`) - tk.MustQuery(`select * from rt3pk`).Check(testkit.Rows("2018-01-01 11:11:11 22:22:22 2 2019")) - - tk.MustExec(`drop table if exists rt1pk1u`) - tk.MustExec(`create table rt1pk1u(id varchar(200) primary key, uk int, v int, unique key uuk(uk))`) - tk.MustExec(`replace into rt1pk1u(id, uk, v) values("abc", 2, 1)`) - tk.MustQuery(`select * from rt1pk1u`).Check(testkit.Rows("abc 2 1")) - tk.MustExec(`replace into rt1pk1u(id, uk, v) values("aaa", 2, 11)`) - tk.MustQuery(`select * from rt1pk1u`).Check(testkit.Rows("aaa 2 11")) -} - -func TestPessimisticUpdatePKLazyCheck(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOn) - testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOff) - testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeIntOnly) -} - -func testUpdatePKLazyCheck(t *testing.T, tk *testkit.TestKit, clusteredIndex variable.ClusteredIndexDefMode) { - tk.Session().GetSessionVars().EnableClusteredIndex = clusteredIndex - tk.MustExec(`drop table if exists upk`) - tk.MustExec(`create table upk (a int, b int, c int, primary key (a, b))`) - tk.MustExec(`insert upk values (1, 1, 1), (2, 2, 2), (3, 3, 3)`) - tk.MustExec("begin pessimistic") - tk.MustExec("update upk set b = b + 1 where a between 1 and 2") - require.Equal(t, 2, getPresumeExistsCount(t, tk.Session())) - err := tk.ExecToErr("update upk set a = 3, b = 3 where a between 1 and 2") - require.True(t, kv.ErrKeyExists.Equal(err)) - tk.MustExec("commit") -} - -func getPresumeExistsCount(t *testing.T, se session.Session) int { - txn, err := se.Txn(false) - require.NoError(t, err) - buf := txn.GetMemBuffer() - it, err := buf.Iter(nil, nil) - require.NoError(t, err) - presumeNotExistsCnt := 0 - for it.Valid() { - flags, err1 := buf.GetFlags(it.Key()) - require.Nil(t, err1) - err = it.Next() - require.NoError(t, err) - if flags.HasPresumeKeyNotExists() { - presumeNotExistsCnt++ - } - } - return presumeNotExistsCnt -} - -func TestOutOfRangeWithUnsigned(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(ts int(10) unsigned NULL DEFAULT NULL)`) - tk.MustExec(`insert into t values(1)`) - tk.MustGetErrMsg( - "update t set ts = IF(ts < (0 - ts), 1,1) where ts>0", - "[types:1690]BIGINT UNSIGNED value is out of range in '(0 - test.t.ts)'", - ) -} - -func TestIssue21447(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1, tk2 := testkit.NewTestKit(t, store), testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("drop table if exists t1") - tk1.MustExec("create table t1(id int primary key, name varchar(40))") - tk1.MustExec("insert into t1 values(1, 'abc')") - - tk1.MustExec("begin pessimistic") - tk2.MustExec("begin pessimistic") - tk2.MustExec("update t1 set name='xyz' where id=1") - tk2.CheckExecResult(1, 0) - tk2.MustQuery("select * from t1 where id = 1").Check(testkit.Rows("1 xyz")) - tk2.MustExec("commit") - tk1.MustExec("update t1 set name='xyz' where id=1") - tk1.CheckExecResult(0, 0) - tk1.MustQuery("select * from t1 where id = 1").Check(testkit.Rows("1 abc")) - tk1.MustQuery("select * from t1 where id = 1 for update").Check(testkit.Rows("1 xyz")) - tk1.MustQuery("select * from t1 where id in (1, 2)").Check(testkit.Rows("1 abc")) - tk1.MustQuery("select * from t1 where id in (1, 2) for update").Check(testkit.Rows("1 xyz")) - tk1.MustExec("commit") -} - -func TestIssue23553(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`drop table if exists tt`) - tk.MustExec(`create table tt (m0 varchar(64), status tinyint not null)`) - tk.MustExec(`insert into tt values('1',0),('1',0),('1',0)`) - tk.MustExec(`update tt a inner join (select m0 from tt where status!=1 group by m0 having count(*)>1) b on a.m0=b.m0 set a.status=1`) -} - -func TestLockUnchangedUniqueKeys(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - - for _, shouldLock := range []bool{true, false} { - for _, tt := range []struct { - name string - create string - insert string - update string - isClusteredPK bool - }{ - { - // ref https://github.com/pingcap/tidb/issues/36438 - "Issue36438", - "create table t (i varchar(10), unique key(i))", - "insert into t values ('a')", - "update t set i = 'a'", - false, - }, - { - "ClusteredAndRowUnchanged", - "create table t (k int, v int, primary key(k) clustered, key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - true, - }, - { - "ClusteredAndRowUnchangedAndParted", - "create table t (k int, v int, primary key(k) clustered, key sk(k)) partition by hash(k) partitions 4", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - true, - }, - { - "ClusteredAndRowChanged", - "create table t (k int, v int, primary key(k) clustered, key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 11 where k = 1", - true, - }, - { - "NonClusteredAndRowUnchanged", - "create table t (k int, v int, primary key(k) nonclustered, key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - false, - }, - { - "NonClusteredAndRowUnchangedAndParted", - "create table t (k int, v int, primary key(k) nonclustered, key sk(k)) partition by hash(k) partitions 4", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - false, - }, - { - "NonClusteredAndRowChanged", - "create table t (k int, v int, primary key(k) nonclustered, key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 11 where k = 1", - false, - }, - { - "UniqueAndRowUnchanged", - "create table t (k int, v int, unique key uk(k), key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - false, - }, - { - "UniqueAndRowUnchangedAndParted", - "create table t (k int, v int, unique key uk(k), key sk(k)) partition by hash(k) partitions 4", - "insert into t values (1, 10)", - "update t force index(sk) set v = 10 where k = 1", - false, - }, - { - "UniqueAndRowChanged", - "create table t (k int, v int, unique key uk(k), key sk(k))", - "insert into t values (1, 10)", - "update t force index(sk) set v = 11 where k = 1", - false, - }, - } { - t.Run( - tt.name+"-"+strconv.FormatBool(shouldLock), func(t *testing.T) { - tk1.MustExec(fmt.Sprintf("set @@tidb_lock_unchanged_keys = %v", shouldLock)) - tk1.MustExec("drop table if exists t") - tk1.MustExec(tt.create) - tk1.MustExec(tt.insert) - tk1.MustExec("begin pessimistic") - - tk1.MustExec(tt.update) - - errCh := make(chan error, 1) - go func() { - _, err := tk2.Exec(tt.insert) - errCh <- err - }() - - select { - case <-time.After(100 * time.Millisecond): - if !shouldLock && !tt.isClusteredPK { - require.Fail(t, "insert is blocked by update") - } - tk1.MustExec("rollback") - require.Error(t, <-errCh) - case err := <-errCh: - require.Error(t, err) - if shouldLock { - require.Fail(t, "insert is not blocked by update") - } - } - }, - ) - } - } -} diff --git a/executor/write.go b/executor/write.go deleted file mode 100644 index 87ae63a0b230a..0000000000000 --- a/executor/write.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package executor - -import ( - "context" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/tracing" -) - -var ( - _ exec.Executor = &UpdateExec{} - _ exec.Executor = &DeleteExec{} - _ exec.Executor = &InsertExec{} - _ exec.Executor = &ReplaceExec{} - _ exec.Executor = &LoadDataExec{} -) - -// updateRecord updates the row specified by the handle `h`, from `oldData` to `newData`. -// `modified` means which columns are really modified. It's used for secondary indices. -// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. -// The return values: -// 1. changed (bool) : does the update really change the row values. e.g. update set i = 1 where i = 1; -// 2. err (error) : error in the update. -func updateRecord( - ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, modified []bool, - t table.Table, - onDup bool, _ *memory.Tracker, fkChecks []*FKCheckExec, fkCascades []*FKCascadeExec, -) (bool, error) { - r, ctx := tracing.StartRegionEx(ctx, "executor.updateRecord") - defer r.End() - - sc := sctx.GetSessionVars().StmtCtx - changed, handleChanged := false, false - // onUpdateSpecified is for "UPDATE SET ts_field = old_value", the - // timestamp field is explicitly set, but not changed in fact. - onUpdateSpecified := make(map[int]bool) - - // We can iterate on public columns not writable columns, - // because all of them are sorted by their `Offset`, which - // causes all writable columns are after public columns. - - // Handle the bad null error. - for i, col := range t.Cols() { - var err error - if err = col.HandleBadNull(&newData[i], sc, 0); err != nil { - return false, err - } - } - - // Handle exchange partition - tbl := t.Meta() - if tbl.ExchangePartitionInfo != nil && tbl.GetPartitionInfo() == nil { - if err := checkRowForExchangePartition(sctx, newData, tbl); err != nil { - return false, err - } - } - - // Compare datum, then handle some flags. - for i, col := range t.Cols() { - // We should use binary collation to compare datum, otherwise the result will be incorrect. - cmp, err := newData[i].Compare(sc, &oldData[i], collate.GetBinaryCollator()) - if err != nil { - return false, err - } - if cmp != 0 { - changed = true - modified[i] = true - // Rebase auto increment id if the field is changed. - if mysql.HasAutoIncrementFlag(col.GetFlag()) { - recordID, err := getAutoRecordID(newData[i], &col.FieldType, false) - if err != nil { - return false, err - } - if err = t.Allocators(sctx).Get(autoid.AutoIncrementType).Rebase(ctx, recordID, true); err != nil { - return false, err - } - } - if col.IsPKHandleColumn(t.Meta()) { - handleChanged = true - // Rebase auto random id if the field is changed. - if err := rebaseAutoRandomValue(ctx, sctx, t, &newData[i], col); err != nil { - return false, err - } - } - if col.IsCommonHandleColumn(t.Meta()) { - handleChanged = true - } - } else { - if mysql.HasOnUpdateNowFlag(col.GetFlag()) && modified[i] { - // It's for "UPDATE t SET ts = ts" and ts is a timestamp. - onUpdateSpecified[i] = true - } - modified[i] = false - } - } - - sc.AddTouchedRows(1) - // If no changes, nothing to do, return directly. - if !changed { - // See https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS - if sctx.GetSessionVars().ClientCapability&mysql.ClientFoundRows > 0 { - sc.AddAffectedRows(1) - } - keySet := lockRowKey - if sctx.GetSessionVars().LockUnchangedKeys { - keySet |= lockUniqueKeys - } - _, err := addUnchangedKeysForLockByRow(sctx, t, h, oldData, keySet) - return false, err - } - - // Fill values into on-update-now fields, only if they are really changed. - for i, col := range t.Cols() { - if mysql.HasOnUpdateNowFlag(col.GetFlag()) && !modified[i] && !onUpdateSpecified[i] { - v, err := expression.GetTimeValue(sctx, strings.ToUpper(ast.CurrentTimestamp), col.GetType(), col.GetDecimal(), nil) - if err != nil { - return false, err - } - newData[i] = v - modified[i] = true - // Only TIMESTAMP and DATETIME columns can be automatically updated, so it cannot be PKIsHandle. - // Ref: https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html - if col.IsPKHandleColumn(t.Meta()) { - return false, errors.Errorf("on-update-now column should never be pk-is-handle") - } - if col.IsCommonHandleColumn(t.Meta()) { - handleChanged = true - } - } - } - - // If handle changed, remove the old then add the new record, otherwise update the record. - if handleChanged { - // For `UPDATE IGNORE`/`INSERT IGNORE ON DUPLICATE KEY UPDATE` - // we use the staging buffer so that we don't need to precheck the existence of handle or unique keys by sending - // extra kv requests, and the remove action will not take effect if there are conflicts. - if updated, err := func() (bool, error) { - txn, err := sctx.Txn(true) - if err != nil { - return false, err - } - memBuffer := txn.GetMemBuffer() - sh := memBuffer.Staging() - defer memBuffer.Cleanup(sh) - - if err = t.RemoveRecord(sctx, h, oldData); err != nil { - return false, err - } - - _, err = t.AddRecord(sctx, newData, table.IsUpdate, table.WithCtx(ctx)) - if err != nil { - return false, err - } - memBuffer.Release(sh) - return true, nil - }(); err != nil { - if terr, ok := errors.Cause(err).(*terror.Error); sctx.GetSessionVars().StmtCtx.IgnoreNoPartition && ok && terr.Code() == errno.ErrNoPartitionForGivenValue { - return false, nil - } - return updated, err - } - } else { - // Update record to new value and update index. - if err := t.UpdateRecord(ctx, sctx, h, oldData, newData, modified); err != nil { - if terr, ok := errors.Cause(err).(*terror.Error); sctx.GetSessionVars().StmtCtx.IgnoreNoPartition && ok && terr.Code() == errno.ErrNoPartitionForGivenValue { - return false, nil - } - return false, err - } - if sctx.GetSessionVars().LockUnchangedKeys { - // Lock unique keys when handle unchanged - if _, err := addUnchangedKeysForLockByRow(sctx, t, h, oldData, lockUniqueKeys); err != nil { - return false, err - } - } - } - for _, fkt := range fkChecks { - err := fkt.updateRowNeedToCheck(sc, oldData, newData) - if err != nil { - return false, err - } - } - for _, fkc := range fkCascades { - err := fkc.onUpdateRow(sc, oldData, newData) - if err != nil { - return false, err - } - } - if onDup { - sc.AddAffectedRows(2) - } else { - sc.AddAffectedRows(1) - } - sc.AddUpdatedRows(1) - sc.AddCopiedRows(1) - - return true, nil -} - -const ( - lockRowKey = 1 << iota - lockUniqueKeys -) - -func addUnchangedKeysForLockByRow( - sctx sessionctx.Context, t table.Table, h kv.Handle, row []types.Datum, keySet int, -) (int, error) { - txnCtx := sctx.GetSessionVars().TxnCtx - if !txnCtx.IsPessimistic || keySet == 0 { - return 0, nil - } - count := 0 - physicalID := t.Meta().ID - if pt, ok := t.(table.PartitionedTable); ok { - p, err := pt.GetPartitionByRow(sctx, row) - if err != nil { - return 0, err - } - physicalID = p.GetPhysicalID() - } - if keySet&lockRowKey > 0 { - unchangedRowKey := tablecodec.EncodeRowKeyWithHandle(physicalID, h) - txnCtx.AddUnchangedKeyForLock(unchangedRowKey) - count++ - } - if keySet&lockUniqueKeys > 0 { - stmtCtx := sctx.GetSessionVars().StmtCtx - clustered := t.Meta().HasClusteredIndex() - for _, idx := range t.Indices() { - meta := idx.Meta() - if !meta.Unique || !meta.IsPublic() || (meta.Primary && clustered) { - continue - } - ukVals, err := idx.FetchValues(row, nil) - if err != nil { - return count, err - } - unchangedUniqueKey, _, err := tablecodec.GenIndexKey( - stmtCtx, - idx.TableMeta(), - meta, - physicalID, - ukVals, - h, - nil, - ) - if err != nil { - return count, err - } - txnCtx.AddUnchangedKeyForLock(unchangedUniqueKey) - count++ - } - } - return count, nil -} - -func rebaseAutoRandomValue( - ctx context.Context, sctx sessionctx.Context, t table.Table, newData *types.Datum, col *table.Column, -) error { - tableInfo := t.Meta() - if !tableInfo.ContainsAutoRandomBits() { - return nil - } - recordID, err := getAutoRecordID(*newData, &col.FieldType, false) - if err != nil { - return err - } - if recordID < 0 { - return nil - } - shardFmt := autoid.NewShardIDFormat(&col.FieldType, tableInfo.AutoRandomBits, tableInfo.AutoRandomRangeBits) - // Set bits except incremental_bits to zero. - recordID = recordID & shardFmt.IncrementalMask() - return t.Allocators(sctx).Get(autoid.AutoRandomType).Rebase(ctx, recordID, true) -} - -// resetErrDataTooLong reset ErrDataTooLong error msg. -// types.ErrDataTooLong is produced in types.ProduceStrWithSpecifiedTp, there is no column info in there, -// so we reset the error msg here, and wrap old err with errors.Wrap. -func resetErrDataTooLong(colName string, rowIdx int, _ error) error { - newErr := types.ErrDataTooLong.GenWithStack("Data too long for column '%v' at row %v", colName, rowIdx) - return newErr -} - -// checkRowForExchangePartition is only used for ExchangePartition by non-partitionTable during write only state. -// It check if rowData inserted or updated violate partition definition or checkConstraints of partitionTable. -func checkRowForExchangePartition(sctx sessionctx.Context, row []types.Datum, tbl *model.TableInfo) error { - is := sctx.GetDomainInfoSchema().(infoschema.InfoSchema) - pt, tableFound := is.TableByID(tbl.ExchangePartitionInfo.ExchangePartitionTableID) - if !tableFound { - return errors.Errorf("exchange partition process table by id failed") - } - p, ok := pt.(table.PartitionedTable) - if !ok { - return errors.Errorf("exchange partition process assert table partition failed") - } - err := p.CheckForExchangePartition( - sctx, - pt.Meta().Partition, - row, - tbl.ExchangePartitionInfo.ExchangePartitionDefID, - tbl.ID, - ) - if err != nil { - return err - } - if variable.EnableCheckConstraint.Load() { - type CheckConstraintTable interface { - CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error - } - cc, ok := pt.(CheckConstraintTable) - if !ok { - return errors.Errorf("exchange partition process assert check constraint failed") - } - err := cc.CheckRowConstraint(sctx, row) - if err != nil { - // TODO: make error include ExchangePartition info. - return err - } - } - return nil -} diff --git a/expression/BUILD.bazel b/expression/BUILD.bazel deleted file mode 100644 index 6cffb6112fca0..0000000000000 --- a/expression/BUILD.bazel +++ /dev/null @@ -1,238 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "expression", - srcs = [ - "builtin.go", - "builtin_arithmetic.go", - "builtin_arithmetic_vec.go", - "builtin_cast.go", - "builtin_cast_vec.go", - "builtin_compare.go", - "builtin_compare_vec.go", - "builtin_compare_vec_generated.go", - "builtin_control.go", - "builtin_control_vec_generated.go", - "builtin_convert_charset.go", - "builtin_encryption.go", - "builtin_encryption_vec.go", - "builtin_func_param.go", - "builtin_grouping.go", - "builtin_ilike.go", - "builtin_ilike_vec.go", - "builtin_info.go", - "builtin_info_vec.go", - "builtin_json.go", - "builtin_json_vec.go", - "builtin_like.go", - "builtin_like_vec.go", - "builtin_math.go", - "builtin_math_vec.go", - "builtin_miscellaneous.go", - "builtin_miscellaneous_vec.go", - "builtin_op.go", - "builtin_op_vec.go", - "builtin_other.go", - "builtin_other_vec.go", - "builtin_other_vec_generated.go", - "builtin_regexp.go", - "builtin_regexp_util.go", - "builtin_string.go", - "builtin_string_vec.go", - "builtin_string_vec_generated.go", - "builtin_time.go", - "builtin_time_vec.go", - "builtin_time_vec_generated.go", - "builtin_vectorized.go", - "chunk_executor.go", - "collation.go", - "column.go", - "constant.go", - "constant_fold.go", - "constant_propagation.go", - "distsql_builtin.go", - "errors.go", - "evaluator.go", - "explain.go", - "expr_to_pb.go", - "expression.go", - "extension.go", - "function_traits.go", - "grouping_sets.go", - "helper.go", - "scalar_function.go", - "schema.go", - "simple_rewriter.go", - "util.go", - "vectorized.go", - ], - importpath = "github.com/pingcap/tidb/expression", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//errno", - "//extension", - "//kv", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/types", - "//planner/funcdep", - "//privilege", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//types", - "//types/parser_driver", - "//util", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/disjointset", - "//util/encrypt", - "//util/generatedexpr", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/mock", - "//util/parser", - "//util/password-validation", - "//util/plancodec", - "//util/printer", - "//util/sem", - "//util/set", - "//util/size", - "//util/sqlexec", - "//util/stringutil", - "//util/vitess", - "//util/zeropool", - "@com_github_gogo_protobuf//proto", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_pkg_errors//:errors", - "@com_github_tikv_client_go_v2//oracle", - "@org_golang_x_tools//container/intsets", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "expression_test", - timeout = "moderate", - srcs = [ - "bench_test.go", - "builtin_arithmetic_test.go", - "builtin_arithmetic_vec_test.go", - "builtin_cast_bench_test.go", - "builtin_cast_test.go", - "builtin_cast_vec_test.go", - "builtin_compare_test.go", - "builtin_compare_vec_generated_test.go", - "builtin_compare_vec_test.go", - "builtin_control_test.go", - "builtin_control_vec_generated_test.go", - "builtin_encryption_test.go", - "builtin_encryption_vec_test.go", - "builtin_grouping_test.go", - "builtin_ilike_test.go", - "builtin_info_test.go", - "builtin_info_vec_test.go", - "builtin_json_test.go", - "builtin_json_vec_test.go", - "builtin_like_test.go", - "builtin_like_vec_test.go", - "builtin_math_test.go", - "builtin_math_vec_test.go", - "builtin_miscellaneous_test.go", - "builtin_miscellaneous_vec_test.go", - "builtin_op_test.go", - "builtin_op_vec_test.go", - "builtin_other_test.go", - "builtin_other_vec_generated_test.go", - "builtin_other_vec_test.go", - "builtin_regexp_test.go", - "builtin_regexp_vec_const_test.go", - "builtin_string_test.go", - "builtin_string_vec_generated_test.go", - "builtin_string_vec_test.go", - "builtin_test.go", - "builtin_time_test.go", - "builtin_time_vec_generated_test.go", - "builtin_time_vec_test.go", - "builtin_vectorized_test.go", - "collation_test.go", - "column_test.go", - "constant_test.go", - "distsql_builtin_test.go", - "evaluator_test.go", - "expr_to_pb_test.go", - "expression_test.go", - "function_traits_test.go", - "grouping_sets_test.go", - "helper_test.go", - "main_test.go", - "scalar_function_test.go", - "schema_test.go", - "typeinfer_test.go", - "util_test.go", - ], - data = glob(["testdata/**"]), - embed = [":expression"], - flaky = True, - race = "on", - shard_count = 50, - deps = [ - "//config", - "//errno", - "//kv", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//session", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//tablecodec", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//types/parser_driver", - "//util", - "//util/benchdaily", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/hack", - "//util/mathutil", - "//util/mock", - "//util/printer", - "//util/timeutil", - "@com_github_gogo_protobuf//proto", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/expression/aggregation/BUILD.bazel b/expression/aggregation/BUILD.bazel deleted file mode 100644 index b66495b0c2217..0000000000000 --- a/expression/aggregation/BUILD.bazel +++ /dev/null @@ -1,77 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "aggregation", - srcs = [ - "agg_to_pb.go", - "aggregation.go", - "avg.go", - "base_func.go", - "bit_and.go", - "bit_or.go", - "bit_xor.go", - "concat.go", - "count.go", - "descriptor.go", - "explain.go", - "first_row.go", - "max_min.go", - "sum.go", - "util.go", - "window_func.go", - ], - importpath = "github.com/pingcap/tidb/expression/aggregation", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/ast", - "//parser/charset", - "//parser/mysql", - "//parser/terror", - "//planner/util", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/mathutil", - "//util/mvmap", - "//util/size", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_tipb//go-tipb", - ], -) - -go_test( - name = "aggregation_test", - timeout = "short", - srcs = [ - "agg_to_pb_test.go", - "aggregation_test.go", - "base_func_test.go", - "bench_test.go", - "main_test.go", - "util_test.go", - ], - embed = [":aggregation"], - flaky = True, - shard_count = 14, - deps = [ - "//expression", - "//kv", - "//parser/ast", - "//parser/mysql", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/mock", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/expression/aggregation/bench_test.go b/expression/aggregation/bench_test.go deleted file mode 100644 index c03e9d1287a24..0000000000000 --- a/expression/aggregation/bench_test.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package aggregation - -import ( - "testing" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" -) - -func BenchmarkCreateContext(b *testing.B) { - col := &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - ctx := mock.NewContext() - desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, false) - if err != nil { - b.Fatal(err) - } - fun := desc.GetAggFunc(ctx) - b.StartTimer() - for i := 0; i < b.N; i++ { - fun.CreateContext(ctx.GetSessionVars().StmtCtx) - } - b.ReportAllocs() -} - -func BenchmarkResetContext(b *testing.B) { - col := &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - ctx := mock.NewContext() - desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, false) - if err != nil { - b.Fatal(err) - } - fun := desc.GetAggFunc(ctx) - evalCtx := fun.CreateContext(ctx.GetSessionVars().StmtCtx) - b.StartTimer() - for i := 0; i < b.N; i++ { - fun.ResetContext(ctx.GetSessionVars().StmtCtx, evalCtx) - } - b.ReportAllocs() -} - -func BenchmarkCreateDistinctContext(b *testing.B) { - col := &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - ctx := mock.NewContext() - desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, true) - if err != nil { - b.Fatal(err) - } - fun := desc.GetAggFunc(ctx) - b.StartTimer() - for i := 0; i < b.N; i++ { - fun.CreateContext(ctx.GetSessionVars().StmtCtx) - } - b.ReportAllocs() -} - -func BenchmarkResetDistinctContext(b *testing.B) { - col := &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - ctx := mock.NewContext() - desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, true) - if err != nil { - b.Fatal(err) - } - fun := desc.GetAggFunc(ctx) - evalCtx := fun.CreateContext(ctx.GetSessionVars().StmtCtx) - b.StartTimer() - for i := 0; i < b.N; i++ { - fun.ResetContext(ctx.GetSessionVars().StmtCtx, evalCtx) - } - b.ReportAllocs() -} diff --git a/expression/aggregation/explain.go b/expression/aggregation/explain.go deleted file mode 100644 index 3e0eb4e6b9655..0000000000000 --- a/expression/aggregation/explain.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package aggregation - -import ( - "bytes" - "fmt" - - "github.com/pingcap/tidb/parser/ast" -) - -// ExplainAggFunc generates explain information for a aggregation function. -func ExplainAggFunc(agg *AggFuncDesc, normalized bool) string { - var buffer bytes.Buffer - fmt.Fprintf(&buffer, "%s(", agg.Name) - if agg.HasDistinct { - buffer.WriteString("distinct ") - } - for i, arg := range agg.Args { - if agg.Name == ast.AggFuncGroupConcat && i == len(agg.Args)-1 { - if len(agg.OrderByItems) > 0 { - buffer.WriteString(" order by ") - for i, item := range agg.OrderByItems { - if item.Desc { - if normalized { - fmt.Fprintf(&buffer, "%s desc", item.Expr.ExplainNormalizedInfo()) - } else { - fmt.Fprintf(&buffer, "%s desc", item.Expr.ExplainInfo()) - } - } else { - if normalized { - fmt.Fprintf(&buffer, "%s", item.Expr.ExplainNormalizedInfo()) - } else { - fmt.Fprintf(&buffer, "%s", item.Expr.ExplainInfo()) - } - } - - if i+1 < len(agg.OrderByItems) { - buffer.WriteString(", ") - } - } - } - buffer.WriteString(" separator ") - } else if i != 0 { - buffer.WriteString(", ") - } - if normalized { - buffer.WriteString(arg.ExplainNormalizedInfo()) - } else { - buffer.WriteString(arg.ExplainInfo()) - } - } - buffer.WriteString(")") - return buffer.String() -} diff --git a/expression/aggregation/main_test.go b/expression/aggregation/main_test.go deleted file mode 100644 index 44a7ad54767d6..0000000000000 --- a/expression/aggregation/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package aggregation - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/expression/aggregation/util.go b/expression/aggregation/util.go deleted file mode 100644 index 9bcd03c7b2f53..0000000000000 --- a/expression/aggregation/util.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package aggregation - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mvmap" -) - -// distinctChecker stores existing keys and checks if given data is distinct. -type distinctChecker struct { - existingKeys *mvmap.MVMap - key []byte - vals [][]byte - sc *stmtctx.StatementContext -} - -// createDistinctChecker creates a new distinct checker. -func createDistinctChecker(sc *stmtctx.StatementContext) *distinctChecker { - return &distinctChecker{ - existingKeys: mvmap.NewMVMap(), - sc: sc, - } -} - -// Check checks if values is distinct. -func (d *distinctChecker) Check(values []types.Datum) (bool, error) { - d.key = d.key[:0] - var err error - d.key, err = codec.EncodeValue(d.sc, d.key, values...) - if err != nil { - return false, err - } - d.vals = d.existingKeys.Get(d.key, d.vals[:0]) - if len(d.vals) > 0 { - return false, nil - } - d.existingKeys.Put(d.key, []byte{}) - return true, nil -} - -// calculateSum adds v to sum. -func calculateSum(sc *stmtctx.StatementContext, sum, v types.Datum) (data types.Datum, err error) { - // for avg and sum calculation - // avg and sum use decimal for integer and decimal type, use float for others - // see https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html - - switch v.Kind() { - case types.KindNull: - case types.KindInt64, types.KindUint64: - var d *types.MyDecimal - d, err = v.ToDecimal(sc) - if err == nil { - data = types.NewDecimalDatum(d) - } - case types.KindMysqlDecimal: - v.Copy(&data) - default: - var f float64 - f, err = v.ToFloat64(sc) - if err == nil { - data = types.NewFloat64Datum(f) - } - } - - if err != nil { - return data, err - } - if data.IsNull() { - return sum, nil - } - switch sum.Kind() { - case types.KindNull: - return data, nil - case types.KindFloat64, types.KindMysqlDecimal: - return types.ComputePlus(sum, data) - default: - return data, errors.Errorf("invalid value %v for aggregate", sum.Kind()) - } -} diff --git a/expression/aggregation/util_test.go b/expression/aggregation/util_test.go deleted file mode 100644 index 2c476d19cd136..0000000000000 --- a/expression/aggregation/util_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package aggregation - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestDistinct(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - dc := createDistinctChecker(sc) - testCases := []struct { - vals []interface{} - expect bool - }{ - {[]interface{}{1, 1}, true}, - {[]interface{}{1, 1}, false}, - {[]interface{}{1, 2}, true}, - {[]interface{}{1, 2}, false}, - {[]interface{}{1, nil}, true}, - {[]interface{}{1, nil}, false}, - } - for _, tc := range testCases { - d, err := dc.Check(types.MakeDatums(tc.vals...)) - require.NoError(t, err) - require.Equal(t, tc.expect, d) - } -} diff --git a/expression/bench_test.go b/expression/bench_test.go deleted file mode 100644 index ecf23f07f604a..0000000000000 --- a/expression/bench_test.go +++ /dev/null @@ -1,2143 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package expression - -// This file contains benchmarks of our expression evaluation. - -import ( - "flag" - "fmt" - "math" - "math/rand" - "net" - "reflect" - "strings" - "sync" - "testing" - "time" - - "github.com/google/uuid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -type benchHelper struct { - ctx sessionctx.Context - exprs []Expression - - inputTypes []*types.FieldType - outputTypes []*types.FieldType - inputChunk *chunk.Chunk - outputChunk *chunk.Chunk -} - -func (h *benchHelper) init() { - numRows := 4 * 1024 - - h.ctx = mock.NewContext() - h.ctx.GetSessionVars().StmtCtx.SetTimeZone(time.Local) - h.ctx.GetSessionVars().InitChunkSize = 32 - h.ctx.GetSessionVars().MaxChunkSize = numRows - - h.inputTypes = make([]*types.FieldType, 0, 10) - ftb := types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) - h.inputTypes = append(h.inputTypes, ftb.BuildP()) - - ftb = types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeDouble).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxRealWidth).SetDecimal(types.UnspecifiedLength).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) - h.inputTypes = append(h.inputTypes, ftb.BuildP()) - - ftb = types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeNewDecimal).SetFlag(mysql.BinaryFlag).SetFlen(11).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) - h.inputTypes = append(h.inputTypes, ftb.BuildP()) - - // Use 20 string columns to show the cache performance. - for i := 0; i < 20; i++ { - ftb = types.NewFieldTypeBuilder() - ftb.SetType(mysql.TypeVarString).SetDecimal(types.UnspecifiedLength).SetCharset(charset.CharsetUTF8).SetCollate(charset.CollationUTF8) - h.inputTypes = append(h.inputTypes, ftb.BuildP()) - } - - h.inputChunk = chunk.NewChunkWithCapacity(h.inputTypes, numRows) - for rowIdx := 0; rowIdx < numRows; rowIdx++ { - h.inputChunk.AppendInt64(0, 4) - h.inputChunk.AppendFloat64(1, 2.019) - h.inputChunk.AppendMyDecimal(2, types.NewDecFromFloatForTest(5.9101)) - for i := 0; i < 20; i++ { - h.inputChunk.AppendString(3+i, `abcdefughasfjsaljal1321798273528791!&(*#&@&^%&%^&!)sadfashqwer`) - } - } - - cols := make([]*Column, 0, len(h.inputTypes)) - for i := 0; i < len(h.inputTypes); i++ { - cols = append(cols, &Column{ - UniqueID: int64(i), - RetType: h.inputTypes[i], - Index: i, - }) - } - - h.exprs = make([]Expression, 0, 10) - if expr, err := NewFunction(h.ctx, ast.Substr, h.inputTypes[3], []Expression{cols[3], cols[2]}...); err != nil { - panic("create SUBSTR function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.Plus, h.inputTypes[0], []Expression{cols[1], cols[2]}...); err != nil { - panic("create PLUS function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[11], cols[8]}...); err != nil { - panic("create GT function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[19], cols[10]}...); err != nil { - panic("create GT function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[17], cols[4]}...); err != nil { - panic("create GT function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[18], cols[5]}...); err != nil { - panic("create GT function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.LE, h.inputTypes[2], []Expression{cols[19], cols[4]}...); err != nil { - panic("create LE function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - - if expr, err := NewFunction(h.ctx, ast.EQ, h.inputTypes[2], []Expression{cols[20], cols[3]}...); err != nil { - panic("create EQ function failed.") - } else { - h.exprs = append(h.exprs, expr) - } - h.exprs = append(h.exprs, cols[2]) - h.exprs = append(h.exprs, cols[2]) - - h.outputTypes = make([]*types.FieldType, 0, len(h.exprs)) - for i := 0; i < len(h.exprs); i++ { - h.outputTypes = append(h.outputTypes, h.exprs[i].GetType()) - } - - h.outputChunk = chunk.NewChunkWithCapacity(h.outputTypes, numRows) -} - -func BenchmarkVectorizedExecute(b *testing.B) { - h := benchHelper{} - h.init() - inputIter := chunk.NewIterator4Chunk(h.inputChunk) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - h.outputChunk.Reset() - if err := VectorizedExecute(h.ctx, h.exprs, inputIter, h.outputChunk); err != nil { - panic("errors happened during \"VectorizedExecute\"") - } - } -} - -func BenchmarkScalarFunctionClone(b *testing.B) { - col := &Column{RetType: types.NewFieldType(mysql.TypeLonglong)} - con1 := NewOne() - con2 := NewZero() - add := NewFunctionInternal(mock.NewContext(), ast.Plus, types.NewFieldType(mysql.TypeLonglong), col, con1) - sub := NewFunctionInternal(mock.NewContext(), ast.Plus, types.NewFieldType(mysql.TypeLonglong), add, con2) - b.ResetTimer() - for i := 0; i < b.N; i++ { - sub.Clone() - } - b.ReportAllocs() -} - -func getRandomTime(r *rand.Rand) types.CoreTime { - return types.FromDate(r.Intn(2200), r.Intn(10)+1, r.Intn(20)+1, - r.Intn(12), r.Intn(60), r.Intn(60), r.Intn(1000000)) -} - -// dataGenerator is used to generate data for test. -type dataGenerator interface { - gen() interface{} -} - -type defaultRandGen struct { - *rand.Rand -} - -type lockedSource struct { - lk sync.Mutex - src rand.Source -} - -func (r *lockedSource) Int63() (n int64) { - r.lk.Lock() - n = r.src.Int63() - r.lk.Unlock() - return -} - -func (r *lockedSource) Seed(seed int64) { - r.lk.Lock() - r.src.Seed(seed) - r.lk.Unlock() -} - -func newDefaultRandGen() *defaultRandGen { - return &defaultRandGen{rand.New(&lockedSource{src: rand.NewSource(int64(rand.Uint64()))})} -} - -type defaultGener struct { - nullRation float64 - eType types.EvalType - randGen *defaultRandGen -} - -func newDefaultGener(nullRation float64, eType types.EvalType) *defaultGener { - return &defaultGener{ - nullRation: nullRation, - eType: eType, - randGen: newDefaultRandGen(), - } -} - -func (g *defaultGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - switch g.eType { - case types.ETInt: - if g.randGen.Float64() < 0.5 { - return -g.randGen.Int63() - } - return g.randGen.Int63() - case types.ETReal: - if g.randGen.Float64() < 0.5 { - return -g.randGen.Float64() * 1000000 - } - return g.randGen.Float64() * 1000000 - case types.ETDecimal: - d := new(types.MyDecimal) - var f float64 - if g.randGen.Float64() < 0.5 { - f = g.randGen.Float64() * 100000 - } else { - f = -g.randGen.Float64() * 100000 - } - if err := d.FromFloat64(f); err != nil { - panic(err) - } - return d - case types.ETDatetime, types.ETTimestamp: - gt := getRandomTime(g.randGen.Rand) - t := types.NewTime(gt, convertETType(g.eType), 0) - // TiDB has DST time problem, and it causes ErrWrongValue. - // We should ignore ambiguous Time. See https://timezonedb.com/time-zones/Asia/Shanghai. - for _, err := t.GoTime(time.Local); err != nil; { - gt = getRandomTime(g.randGen.Rand) - t = types.NewTime(gt, convertETType(g.eType), 0) - _, err = t.GoTime(time.Local) - } - return t - case types.ETDuration: - d := types.Duration{ - // use rand.Int32() to make it not overflow when AddDuration - Duration: time.Duration(g.randGen.Int31()), - } - return d - case types.ETJson: - j := new(types.BinaryJSON) - if err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"key":%v}`, g.randGen.Int()))); err != nil { - panic(err) - } - return *j - case types.ETString: - return randString(g.randGen.Rand) - } - return nil -} - -// charInt64Gener is used to generate int which is equal to char's ascii -type charInt64Gener struct{} - -func (g *charInt64Gener) gen() interface{} { - nanosecond := time.Now().Nanosecond() - nanosecond = nanosecond % 1024 - return int64(nanosecond) -} - -// selectStringGener select one string randomly from the candidates array -type selectStringGener struct { - candidates []string - randGen *defaultRandGen -} - -func newSelectStringGener(candidates []string) *selectStringGener { - return &selectStringGener{candidates, newDefaultRandGen()} -} - -func (g *selectStringGener) gen() interface{} { - if len(g.candidates) == 0 { - return nil - } - return g.candidates[g.randGen.Intn(len(g.candidates))] -} - -// selectRealGener select one real number randomly from the candidates array -type selectRealGener struct { - candidates []float64 - randGen *defaultRandGen -} - -func newSelectRealGener(candidates []float64) *selectRealGener { - return &selectRealGener{candidates, newDefaultRandGen()} -} - -func (g *selectRealGener) gen() interface{} { - if len(g.candidates) == 0 { - return nil - } - return g.candidates[g.randGen.Intn(len(g.candidates))] -} - -type constJSONGener struct { - jsonStr string -} - -func (g *constJSONGener) gen() interface{} { - j := new(types.BinaryJSON) - if err := j.UnmarshalJSON([]byte(g.jsonStr)); err != nil { - panic(err) - } - return *j -} - -type decimalJSONGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func newDecimalJSONGener(nullRation float64) *decimalJSONGener { - return &decimalJSONGener{nullRation, newDefaultRandGen()} -} - -func (g *decimalJSONGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - - var f float64 - if g.randGen.Float64() < 0.5 { - f = g.randGen.Float64() * 100000 - } else { - f = -g.randGen.Float64() * 100000 - } - if err := (&types.MyDecimal{}).FromFloat64(f); err != nil { - panic(err) - } - return types.CreateBinaryJSON(f) -} - -type jsonStringGener struct { - randGen *defaultRandGen -} - -func newJSONStringGener() *jsonStringGener { - return &jsonStringGener{newDefaultRandGen()} -} - -func (g *jsonStringGener) gen() interface{} { - j := new(types.BinaryJSON) - if err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"key":%v}`, g.randGen.Int()))); err != nil { - panic(err) - } - return j.String() -} - -type decimalStringGener struct { - randGen *defaultRandGen -} - -func newDecimalStringGener() *decimalStringGener { - return &decimalStringGener{newDefaultRandGen()} -} - -func (g *decimalStringGener) gen() interface{} { - tempDecimal := new(types.MyDecimal) - if err := tempDecimal.FromFloat64(g.randGen.Float64()); err != nil { - panic(err) - } - return tempDecimal.String() -} - -type realStringGener struct { - randGen *defaultRandGen -} - -func newRealStringGener() *realStringGener { - return &realStringGener{newDefaultRandGen()} -} - -func (g *realStringGener) gen() interface{} { - return fmt.Sprintf("%f", g.randGen.Float64()) -} - -type jsonTimeGener struct { - randGen *defaultRandGen -} - -func newJSONTimeGener() *jsonTimeGener { - return &jsonTimeGener{newDefaultRandGen()} -} - -func (g *jsonTimeGener) gen() interface{} { - tm := types.NewTime(getRandomTime(g.randGen.Rand), mysql.TypeDatetime, types.DefaultFsp) - return types.CreateBinaryJSON(tm) -} - -type rangeDurationGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func newRangeDurationGener(nullRation float64) *rangeDurationGener { - return &rangeDurationGener{nullRation, newDefaultRandGen()} -} - -func (g *rangeDurationGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - tm := (mathutil.Abs(g.randGen.Int63n(12))*3600 + mathutil.Abs(g.randGen.Int63n(60))*60 + mathutil.Abs(g.randGen.Int63n(60))) * 1000 - tu := (tm + mathutil.Abs(g.randGen.Int63n(1000))) * 1000 - return types.Duration{ - Duration: time.Duration(tu * 1000)} -} - -type timeFormatGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func newTimeFormatGener(nullRation float64) *timeFormatGener { - return &timeFormatGener{nullRation, newDefaultRandGen()} -} - -func (g *timeFormatGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - switch g.randGen.Uint32() % 4 { - case 0: - return "%H %i %S" - case 1: - return "%l %i %s" - case 2: - return "%p %i %s" - case 3: - return "%I %i %S %f" - case 4: - return "%T" - default: - return nil - } -} - -// rangeRealGener is used to generate float64 items in [begin, end]. -type rangeRealGener struct { - begin float64 - end float64 - - nullRation float64 - randGen *defaultRandGen -} - -func newRangeRealGener(begin, end, nullRation float64) *rangeRealGener { - return &rangeRealGener{begin, end, nullRation, newDefaultRandGen()} -} - -func (g *rangeRealGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - if g.end < g.begin { - g.begin = -100 - g.end = 100 - } - return g.randGen.Float64()*(g.end-g.begin) + g.begin -} - -// rangeDecimalGener is used to generate decimal items in [begin, end]. -type rangeDecimalGener struct { - begin float64 - end float64 - - nullRation float64 - randGen *defaultRandGen -} - -func newRangeDecimalGener(begin, end, nullRation float64) *rangeDecimalGener { - return &rangeDecimalGener{begin, end, nullRation, newDefaultRandGen()} -} - -func (g *rangeDecimalGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - if g.end < g.begin { - g.begin = -100000 - g.end = 100000 - } - d := new(types.MyDecimal) - f := g.randGen.Float64()*(g.end-g.begin) + g.begin - if err := d.FromFloat64(f); err != nil { - panic(err) - } - return d -} - -// rangeInt64Gener is used to generate int64 items in [begin, end). -type rangeInt64Gener struct { - begin int - end int - randGen *defaultRandGen -} - -func newRangeInt64Gener(begin, end int) *rangeInt64Gener { - return &rangeInt64Gener{begin, end, newDefaultRandGen()} -} - -func (rig *rangeInt64Gener) gen() interface{} { - return int64(rig.randGen.Intn(rig.end-rig.begin) + rig.begin) -} - -// numStrGener is used to generate number strings. -type numStrGener struct { - rangeInt64Gener -} - -func (g *numStrGener) gen() interface{} { - return fmt.Sprintf("%v", g.rangeInt64Gener.gen()) -} - -// ipv6StrGener is used to generate ipv6 strings. -type ipv6StrGener struct { - randGen *defaultRandGen -} - -func (g *ipv6StrGener) gen() interface{} { - var ip net.IP = make([]byte, net.IPv6len) - for i := range ip { - ip[i] = uint8(g.randGen.Intn(256)) - } - return ip.String() -} - -// ipv4StrGener is used to generate ipv4 strings. For example 111.111.111.111 -type ipv4StrGener struct { - randGen *defaultRandGen -} - -func (g *ipv4StrGener) gen() interface{} { - var ip net.IP = make([]byte, net.IPv4len) - for i := range ip { - ip[i] = uint8(g.randGen.Intn(256)) - } - return ip.String() -} - -// ipv6ByteGener is used to generate ipv6 address in 16 bytes string. -type ipv6ByteGener struct { - randGen *defaultRandGen -} - -func (g *ipv6ByteGener) gen() interface{} { - var ip = make([]byte, net.IPv6len) - for i := range ip { - ip[i] = uint8(g.randGen.Intn(256)) - } - return string(ip[:net.IPv6len]) -} - -// ipv4ByteGener is used to generate ipv4 address in 4 bytes string. -type ipv4ByteGener struct { - randGen *defaultRandGen -} - -func (g *ipv4ByteGener) gen() interface{} { - var ip = make([]byte, net.IPv4len) - for i := range ip { - ip[i] = uint8(g.randGen.Intn(256)) - } - return string(ip[:net.IPv4len]) -} - -// ipv4Compat is used to generate ipv4 compatible ipv6 strings -type ipv4CompatByteGener struct { - randGen *defaultRandGen -} - -func (g *ipv4CompatByteGener) gen() interface{} { - var ip = make([]byte, net.IPv6len) - for i := range ip { - if i < 12 { - ip[i] = 0 - } else { - ip[i] = uint8(g.randGen.Intn(256)) - } - } - return string(ip[:net.IPv6len]) -} - -// ipv4MappedByteGener is used to generate ipv4-mapped ipv6 bytes. -type ipv4MappedByteGener struct { - randGen *defaultRandGen -} - -func (g *ipv4MappedByteGener) gen() interface{} { - var ip = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0} - for i := 12; i < 16; i++ { - ip[i] = uint8(g.randGen.Intn(256)) // reset the last 4 bytes - } - return string(ip[:net.IPv6len]) -} - -// uuidStrGener is used to generate uuid strings. -type uuidStrGener struct { - randGen *defaultRandGen -} - -func (g *uuidStrGener) gen() interface{} { - u, _ := uuid.NewUUID() - return u.String() -} - -// uuidBinGener is used to generate uuid binarys. -type uuidBinGener struct { - randGen *defaultRandGen -} - -func (g *uuidBinGener) gen() interface{} { - u, _ := uuid.NewUUID() - bin, _ := u.MarshalBinary() - return string(bin) -} - -// randLenStrGener is used to generate strings whose lengths are in [lenBegin, lenEnd). -type randLenStrGener struct { - lenBegin int - lenEnd int - randGen *defaultRandGen -} - -func newRandLenStrGener(lenBegin, lenEnd int) *randLenStrGener { - return &randLenStrGener{lenBegin, lenEnd, newDefaultRandGen()} -} - -func (g *randLenStrGener) gen() interface{} { - n := g.randGen.Intn(g.lenEnd-g.lenBegin) + g.lenBegin - buf := make([]byte, n) - for i := range buf { - x := g.randGen.Intn(62) - if x < 10 { - buf[i] = byte('0' + x) - } else if x-10 < 26 { - buf[i] = byte('a' + x - 10) - } else { - buf[i] = byte('A' + x - 10 - 26) - } - } - return string(buf) -} - -type randHexStrGener struct { - lenBegin int - lenEnd int - randGen *defaultRandGen -} - -func newRandHexStrGener(lenBegin, lenEnd int) *randHexStrGener { - return &randHexStrGener{lenBegin, lenEnd, newDefaultRandGen()} -} - -func (g *randHexStrGener) gen() interface{} { - n := g.randGen.Intn(g.lenEnd-g.lenBegin) + g.lenBegin - buf := make([]byte, n) - for i := range buf { - x := g.randGen.Intn(16) - if x < 10 { - buf[i] = byte('0' + x) - } else { - if x%2 == 0 { - buf[i] = byte('a' + x - 10) - } else { - buf[i] = byte('A' + x - 10) - } - } - } - return string(buf) -} - -// dateGener is used to generate a date -type dateGener struct { - randGen *defaultRandGen -} - -func (g dateGener) gen() interface{} { - year := 1970 + g.randGen.Intn(100) - month := g.randGen.Intn(10) + 1 - day := g.randGen.Intn(20) + 1 - gt := types.FromDate(year, month, day, 0, 0, 0, 0) - d := types.NewTime(gt, mysql.TypeDate, types.DefaultFsp) - return d -} - -// dateTimeGener is used to generate a dataTime -type dateTimeGener struct { - Fsp int - Year int - Month int - Day int - randGen *defaultRandGen -} - -func (g *dateTimeGener) gen() interface{} { - if g.Year == 0 { - g.Year = 1970 + g.randGen.Intn(100) - } - if g.Month == 0 { - g.Month = g.randGen.Intn(10) + 1 - } - if g.Day == 0 { - g.Day = g.randGen.Intn(20) + 1 - } - var gt types.CoreTime - if g.Fsp > 0 && g.Fsp <= 6 { - gt = types.FromDate(g.Year, g.Month, g.Day, g.randGen.Intn(12), g.randGen.Intn(60), g.randGen.Intn(60), g.randGen.Intn(1000000)) - } else { - gt = types.FromDate(g.Year, g.Month, g.Day, g.randGen.Intn(12), g.randGen.Intn(60), g.randGen.Intn(60), 0) - } - t := types.NewTime(gt, mysql.TypeDatetime, types.DefaultFsp) - return t -} - -// dateTimeStrGener is used to generate strings which are dateTime format. -// Fsp must be -1 to 9 otherwise will be ignored. -1 will generate a 0 to 9 random length fsp part, otherwise the fsp part will be of fixed length. -// Fsp more than 6 is to test robustness of fsp part parsing. -type dateTimeStrGener struct { - Fsp int - Year int - Month int - Day int - randGen *defaultRandGen -} - -func (g *dateTimeStrGener) gen() interface{} { - if g.Year == 0 { - g.Year = 1970 + g.randGen.Intn(100) - } - if g.Month == 0 { - g.Month = g.randGen.Intn(10) + 1 - } - if g.Day == 0 { - g.Day = g.randGen.Intn(20) + 1 - } - if g.Fsp == -1 { - g.Fsp = g.randGen.Intn(10) - } - hour := g.randGen.Intn(12) - minute := g.randGen.Intn(60) - second := g.randGen.Intn(60) - dataTimeStr := fmt.Sprintf("%d-%d-%d %d:%d:%d", - g.Year, g.Month, g.Day, hour, minute, second) - if g.Fsp > 0 && g.Fsp <= 9 { - microFmt := fmt.Sprintf(".%%0%dd", g.Fsp) - return dataTimeStr + fmt.Sprintf(microFmt, g.randGen.Int()%int(math.Pow10(g.Fsp))) - } - - return dataTimeStr -} - -// dateStrGener is used to generate strings which are date format -type dateStrGener struct { - Year int - Month int - Day int - NullRation float64 - randGen *defaultRandGen -} - -func (g *dateStrGener) gen() interface{} { - if g.NullRation > 1e-6 && g.randGen.Float64() < g.NullRation { - return nil - } - - if g.Year == 0 { - g.Year = 1970 + g.randGen.Intn(100) - } - if g.Month == 0 { - g.Month = g.randGen.Intn(10) - } - if g.Day == 0 { - g.Day = g.randGen.Intn(20) - } - - return fmt.Sprintf("%d-%d-%d", g.Year, g.Month, g.Day) -} - -// dateOrDatetimeStrGener is used to generate strings which are date or datetime format. -type dateOrDatetimeStrGener struct { - dateRatio float64 - dateStrGener - dateTimeStrGener -} - -func (g dateOrDatetimeStrGener) gen() interface{} { - if g.dateRatio > 1e-6 && g.dateStrGener.randGen.Float64() < g.dateRatio { - return g.dateStrGener.gen() - } - - return g.dateTimeStrGener.gen() -} - -// timeStrGener is used to generate strings which are time format -type timeStrGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func (g *timeStrGener) gen() interface{} { - if g.nullRation > 1e-6 && g.randGen.Float64() < g.nullRation { - return nil - } - hour := g.randGen.Intn(12) - minute := g.randGen.Intn(60) - second := g.randGen.Intn(60) - - return fmt.Sprintf("%d:%d:%d", hour, minute, second) -} - -// dateIntGener is used to generate int values which are date format. -type dateIntGener struct { - dateGener -} - -func (g dateIntGener) gen() interface{} { - t := g.dateGener.gen().(types.Time) - num, err := t.ToNumber().ToInt() - if err != nil { - panic(err) - } - return num -} - -// dateTimeIntGener is used to generate int values which are dateTime format. -type dateTimeIntGener struct { - dateTimeGener -} - -func (g dateTimeIntGener) gen() interface{} { - t := g.dateTimeGener.gen().(types.Time) - num, err := t.ToNumber().ToInt() - if err != nil { - panic(err) - } - return num -} - -// dateOrDatetimeIntGener is used to generate int values which are date or datetime format. -type dateOrDatetimeIntGener struct { - dateRatio float64 - dateIntGener - dateTimeIntGener -} - -func (g dateOrDatetimeIntGener) gen() interface{} { - if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { - return g.dateIntGener.gen() - } - - return g.dateTimeIntGener.gen() -} - -// dateRealGener is used to generate floating point values which are date format. -// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date. -type dateRealGener struct { - fspRatio float64 - dateGener -} - -func (g dateRealGener) gen() interface{} { - t := g.dateGener.gen().(types.Time) - num, err := t.ToNumber().ToFloat64() - if err != nil { - panic(err) - } - - if g.randGen.Float64() >= g.fspRatio { - return num - } - - num += g.randGen.Float64() - return num -} - -// dateTimeRealGener is used to generate floating point values which are dateTime format. -// `fspRatio` is used to control the ratio of values with fractional part. -type dateTimeRealGener struct { - fspRatio float64 - dateTimeGener -} - -func (g dateTimeRealGener) gen() interface{} { - t := g.dateTimeGener.gen().(types.Time) - tmp, err := t.ToNumber().ToInt() - if err != nil { - panic(err) - } - num := float64(tmp) - - if g.randGen.Float64() >= g.fspRatio { - return num - } - - // Not using `t`'s us part since it's too regular. - // Instead, generating a more arbitrary fractional part, e.g. with more than 6 digits. - // We want the parsing logic to be strong enough to deal with this arbitrary fractional number. - num += g.randGen.Float64() - return num -} - -// dateOrDatetimeRealGener is used to generate floating point values which are date or datetime format. -type dateOrDatetimeRealGener struct { - dateRatio float64 - dateRealGener - dateTimeRealGener -} - -func (g dateOrDatetimeRealGener) gen() interface{} { - if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { - return g.dateRealGener.gen() - } - - return g.dateTimeRealGener.gen() -} - -// dateDecimalGener is used to generate decimals which are date format. -// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date. -type dateDecimalGener struct { - fspRatio float64 - dateGener -} - -func (g dateDecimalGener) gen() interface{} { - t := g.dateGener.gen().(types.Time) - intPart := t.ToNumber() - - if g.randGen.Float64() >= g.fspRatio { - return intPart - } - - // Generate a fractional part that is at most 9 digits. - fracDigits := g.randGen.Intn(1000000000) - fracPart := new(types.MyDecimal).FromInt(int64(fracDigits)) - if err := fracPart.Shift(-9); err != nil { - panic(err) - } - - res := new(types.MyDecimal) - err := types.DecimalAdd(intPart, fracPart, res) - if err != nil { - panic(err) - } - return res -} - -// dateTimeDecimalGener is used to generate decimals which are dateTime format. -type dateTimeDecimalGener struct { - fspRatio float64 - dateTimeGener -} - -func (g dateTimeDecimalGener) gen() interface{} { - t := g.dateTimeGener.gen().(types.Time) - num := t.ToNumber() - // Not using `num`'s fractional part so that we can: - // 1. Return early for non-fsp values. - // 2. Generate a more arbitrary fractional part if needed. - i, err := num.ToInt() - if err != nil { - panic(err) - } - intPart := new(types.MyDecimal).FromInt(i) - - if g.randGen.Float64() >= g.fspRatio { - return intPart - } - - // Generate a fractional part that is at most 9 digits. - fracDigits := g.randGen.Intn(1000000000) - fracPart := new(types.MyDecimal).FromInt(int64(fracDigits)) - if err := fracPart.Shift(-9); err != nil { - panic(err) - } - - res := new(types.MyDecimal) - err = types.DecimalAdd(intPart, fracPart, res) - if err != nil { - panic(err) - } - return res -} - -// dateOrDatetimeDecimalGener is used to generate decimals which are date or datetime format. -type dateOrDatetimeDecimalGener struct { - dateRatio float64 - dateDecimalGener - dateTimeDecimalGener -} - -func (g dateOrDatetimeDecimalGener) gen() interface{} { - if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { - return g.dateDecimalGener.gen() - } - - return g.dateTimeDecimalGener.gen() -} - -// constStrGener always returns the given string -type constStrGener struct { - s string -} - -func (g *constStrGener) gen() interface{} { - return g.s -} - -type randDurInt struct { - randGen *defaultRandGen -} - -func newRandDurInt() *randDurInt { - return &randDurInt{newDefaultRandGen()} -} - -func (g *randDurInt) gen() interface{} { - return int64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60)) -} - -type randDurReal struct { - randGen *defaultRandGen -} - -func newRandDurReal() *randDurReal { - return &randDurReal{newDefaultRandGen()} -} - -func (g *randDurReal) gen() interface{} { - return float64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60)) -} - -type randDurDecimal struct { - randGen *defaultRandGen -} - -func newRandDurDecimal() *randDurDecimal { - return &randDurDecimal{newDefaultRandGen()} -} - -func (g *randDurDecimal) gen() interface{} { - d := new(types.MyDecimal) - return d.FromFloat64(float64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60))) -} - -// locationGener is used to generate location for the built-in function GetFormat. -type locationGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func newLocationGener(nullRation float64) *locationGener { - return &locationGener{nullRation, newDefaultRandGen()} -} - -func (g *locationGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - switch g.randGen.Uint32() % 5 { - case 0: - return usaLocation - case 1: - return jisLocation - case 2: - return isoLocation - case 3: - return eurLocation - case 4: - return internalLocation - default: - return nil - } -} - -// formatGener is used to generate a format for the built-in function GetFormat. -type formatGener struct { - nullRation float64 - randGen *defaultRandGen -} - -func newFormatGener(nullRation float64) *formatGener { - return &formatGener{nullRation, newDefaultRandGen()} -} - -func (g *formatGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - switch g.randGen.Uint32() % 4 { - case 0: - return dateFormat - case 1: - return datetimeFormat - case 2: - return timestampFormat - case 3: - return timeFormat - default: - return nil - } -} - -type nullWrappedGener struct { - nullRation float64 - inner dataGenerator - randGen *defaultRandGen -} - -func newNullWrappedGener(nullRation float64, inner dataGenerator) *nullWrappedGener { - return &nullWrappedGener{nullRation, inner, newDefaultRandGen()} -} - -func (g *nullWrappedGener) gen() interface{} { - if g.randGen.Float64() < g.nullRation { - return nil - } - return g.inner.gen() -} - -type vecExprBenchCase struct { - // retEvalType is the EvalType of the expression result. - // This field is required. - retEvalType types.EvalType - // childrenTypes is the EvalTypes of the expression children(arguments). - // This field is required. - childrenTypes []types.EvalType - // childrenFieldTypes is the field types of the expression children(arguments). - // If childrenFieldTypes is not set, it will be converted from childrenTypes. - // This field is optional. - childrenFieldTypes []*types.FieldType - // geners are used to generate data for children and geners[i] generates data for children[i]. - // If geners[i] is nil, the default dataGenerator will be used for its corresponding child. - // The geners slice can be shorter than the children slice, if it has 3 children, then - // geners[gen1, gen2] will be regarded as geners[gen1, gen2, nil]. - // This field is optional. - geners []dataGenerator - // aesModeAttr information, needed by encryption functions - aesModes string - // constants are used to generate constant data for children[i]. - constants []*Constant - // chunkSize is used to specify the chunk size of children, the maximum is 1024. - // This field is optional, 1024 by default. - chunkSize int -} - -type vecExprBenchCases map[string][]vecExprBenchCase - -func fillColumn(eType types.EvalType, chk *chunk.Chunk, colIdx int, testCase vecExprBenchCase) { - var gen dataGenerator - if len(testCase.geners) > colIdx && testCase.geners[colIdx] != nil { - gen = testCase.geners[colIdx] - } - fillColumnWithGener(eType, chk, colIdx, gen) -} - -func fillColumnWithGener(eType types.EvalType, chk *chunk.Chunk, colIdx int, gen dataGenerator) { - batchSize := chk.Capacity() - if gen == nil { - gen = newDefaultGener(0.2, eType) - } - - col := chk.Column(colIdx) - col.Reset(eType) - for i := 0; i < batchSize; i++ { - v := gen.gen() - if v == nil { - col.AppendNull() - continue - } - switch eType { - case types.ETInt: - col.AppendInt64(v.(int64)) - case types.ETReal: - col.AppendFloat64(v.(float64)) - case types.ETDecimal: - col.AppendMyDecimal(v.(*types.MyDecimal)) - case types.ETDatetime, types.ETTimestamp: - col.AppendTime(v.(types.Time)) - case types.ETDuration: - col.AppendDuration(v.(types.Duration)) - case types.ETJson: - col.AppendJSON(v.(types.BinaryJSON)) - case types.ETString: - col.AppendString(v.(string)) - } - } -} - -func randString(r *rand.Rand) string { - n := 10 + r.Intn(10) - buf := make([]byte, n) - for i := range buf { - x := r.Intn(62) - if x < 10 { - buf[i] = byte('0' + x) - } else if x-10 < 26 { - buf[i] = byte('a' + x - 10) - } else { - buf[i] = byte('A' + x - 10 - 26) - } - } - return string(buf) -} - -func eType2FieldType(eType types.EvalType) *types.FieldType { - switch eType { - case types.ETInt: - return types.NewFieldType(mysql.TypeLonglong) - case types.ETReal: - return types.NewFieldType(mysql.TypeDouble) - case types.ETDecimal: - return types.NewFieldType(mysql.TypeNewDecimal) - case types.ETDatetime, types.ETTimestamp: - return types.NewFieldType(mysql.TypeDatetime) - case types.ETDuration: - return types.NewFieldType(mysql.TypeDuration) - case types.ETJson: - return types.NewFieldType(mysql.TypeJSON) - case types.ETString: - return types.NewFieldType(mysql.TypeVarString) - default: - panic(fmt.Sprintf("EvalType=%v is not supported.", eType)) - } -} - -func genVecExprBenchCase(ctx sessionctx.Context, funcName string, testCase vecExprBenchCase) (expr Expression, fts []*types.FieldType, input *chunk.Chunk, output *chunk.Chunk) { - fts = make([]*types.FieldType, len(testCase.childrenTypes)) - for i := range fts { - if i < len(testCase.childrenFieldTypes) && testCase.childrenFieldTypes[i] != nil { - fts[i] = testCase.childrenFieldTypes[i] - } else { - fts[i] = eType2FieldType(testCase.childrenTypes[i]) - } - } - if testCase.chunkSize <= 0 || testCase.chunkSize > 1024 { - testCase.chunkSize = 1024 - } - cols := make([]Expression, len(testCase.childrenTypes)) - input = chunk.New(fts, testCase.chunkSize, testCase.chunkSize) - input.NumRows() - for i, eType := range testCase.childrenTypes { - fillColumn(eType, input, i, testCase) - if i < len(testCase.constants) && testCase.constants[i] != nil { - cols[i] = testCase.constants[i] - } else { - cols[i] = &Column{Index: i, RetType: fts[i]} - } - } - - expr, err := NewFunction(ctx, funcName, eType2FieldType(testCase.retEvalType), cols...) - if err != nil { - panic(err) - } - - output = chunk.New([]*types.FieldType{eType2FieldType(expr.GetType().EvalType())}, testCase.chunkSize, testCase.chunkSize) - return expr, fts, input, output -} - -// testVectorizedEvalOneVec is used to verify that the vectorized -// expression is evaluated correctly during projection -func testVectorizedEvalOneVec(t *testing.T, vecExprCases vecExprBenchCases) { - ctx := mock.NewContext() - for funcName, testCases := range vecExprCases { - for _, testCase := range testCases { - expr, fts, input, output := genVecExprBenchCase(ctx, funcName, testCase) - commentf := func(row int) string { - return fmt.Sprintf("func: %v, case %+v, row: %v, rowData: %v", funcName, testCase, row, input.GetRow(row).GetDatumRow(fts)) - } - output2 := output.CopyConstruct() - require.NoErrorf(t, evalOneVec(ctx, expr, input, output, 0), "func: %v, case: %+v", funcName, testCase) - it := chunk.NewIterator4Chunk(input) - require.NoErrorf(t, evalOneColumn(ctx, expr, it, output2, 0), "func: %v, case: %+v", funcName, testCase) - - c1, c2 := output.Column(0), output2.Column(0) - switch expr.GetType().EvalType() { - case types.ETInt: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetInt64(i), c2.GetInt64(i), commentf(i)) - } - } - case types.ETReal: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetFloat64(i), c2.GetFloat64(i), commentf(i)) - } - } - case types.ETDecimal: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetDecimal(i), c2.GetDecimal(i), commentf(i)) - } - } - case types.ETDatetime, types.ETTimestamp: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetTime(i), c2.GetTime(i), commentf(i)) - } - } - case types.ETDuration: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetDuration(i, 0), c2.GetDuration(i, 0), commentf(i)) - } - } - case types.ETJson: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetJSON(i), c2.GetJSON(i), commentf(i)) - } - } - case types.ETString: - for i := 0; i < input.NumRows(); i++ { - require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) - if !c1.IsNull(i) { - require.Equal(t, c1.GetString(i), c2.GetString(i), commentf(i)) - } - } - } - } - } -} - -// benchmarkVectorizedEvalOneVec is used to get the effect of -// using the vectorized expression evaluations during projection -func benchmarkVectorizedEvalOneVec(b *testing.B, vecExprCases vecExprBenchCases) { - ctx := mock.NewContext() - for funcName, testCases := range vecExprCases { - for _, testCase := range testCases { - expr, _, input, output := genVecExprBenchCase(ctx, funcName, testCase) - exprName := expr.String() - if sf, ok := expr.(*ScalarFunction); ok { - exprName = fmt.Sprintf("%v", reflect.TypeOf(sf.Function)) - tmp := strings.Split(exprName, ".") - exprName = tmp[len(tmp)-1] - } - - b.Run(exprName+"-EvalOneVec", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - if err := evalOneVec(ctx, expr, input, output, 0); err != nil { - b.Fatal(err) - } - } - }) - b.Run(exprName+"-EvalOneCol", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - it := chunk.NewIterator4Chunk(input) - if err := evalOneColumn(ctx, expr, it, output, 0); err != nil { - b.Fatal(err) - } - } - }) - } - } -} - -func genVecBuiltinFuncBenchCase(ctx sessionctx.Context, funcName string, testCase vecExprBenchCase) (baseFunc builtinFunc, fts []*types.FieldType, input *chunk.Chunk, result *chunk.Column) { - childrenNumber := len(testCase.childrenTypes) - fts = make([]*types.FieldType, childrenNumber) - for i := range fts { - if i < len(testCase.childrenFieldTypes) && testCase.childrenFieldTypes[i] != nil { - fts[i] = testCase.childrenFieldTypes[i] - } else { - fts[i] = eType2FieldType(testCase.childrenTypes[i]) - } - } - cols := make([]Expression, childrenNumber) - if testCase.chunkSize <= 0 || testCase.chunkSize > 1024 { - testCase.chunkSize = 1024 - } - input = chunk.New(fts, testCase.chunkSize, testCase.chunkSize) - for i, eType := range testCase.childrenTypes { - fillColumn(eType, input, i, testCase) - if i < len(testCase.constants) && testCase.constants[i] != nil { - cols[i] = testCase.constants[i] - } else { - cols[i] = &Column{Index: i, RetType: fts[i]} - } - } - if len(cols) == 0 { - input.SetNumVirtualRows(testCase.chunkSize) - } - - var err error - if funcName == ast.Cast { - var fc functionClass - tp := eType2FieldType(testCase.retEvalType) - switch testCase.retEvalType { - case types.ETInt: - fc = &castAsIntFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETDecimal: - fc = &castAsDecimalFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETReal: - fc = &castAsRealFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETDatetime, types.ETTimestamp: - fc = &castAsTimeFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETDuration: - fc = &castAsDurationFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETJson: - fc = &castAsJSONFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - case types.ETString: - fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} - } - baseFunc, err = fc.getFunction(ctx, cols) - } else if funcName == ast.GetVar { - var fc functionClass - tp := eType2FieldType(testCase.retEvalType) - switch testCase.retEvalType { - case types.ETInt: - fc = &getIntVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} - case types.ETDecimal: - fc = &getDecimalVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} - case types.ETReal: - fc = &getRealVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} - default: - fc = &getStringVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} - } - baseFunc, err = fc.getFunction(ctx, cols) - } else { - baseFunc, err = funcs[funcName].getFunction(ctx, cols) - } - if err != nil { - panic(err) - } - result = chunk.NewColumn(eType2FieldType(testCase.retEvalType), testCase.chunkSize) - // Mess up the output to make sure vecEvalXXX to call ResizeXXX/ReserveXXX itself. - result.AppendNull() - return baseFunc, fts, input, result -} - -// a hack way to calculate length of a chunk.Column. -func getColumnLen(col *chunk.Column, eType types.EvalType) int { - chk := chunk.New([]*types.FieldType{eType2FieldType(eType)}, 1024, 1024) - chk.SetCol(0, col) - return chk.NumRows() -} - -// removeTestOptions removes all not needed options like '-test.timeout=' from argument list -func removeTestOptions(args []string) []string { - argList := args[:0] - - // args contains '-test.timeout=' option for example - // excluding it to be able to run all tests - for _, arg := range args { - if strings.HasPrefix(arg, "builtin") || IsFunctionSupported(arg) { - argList = append(argList, arg) - } - } - return argList -} - -// testVectorizedBuiltinFunc is used to verify that the vectorized -// expression is evaluated correctly -func testVectorizedBuiltinFunc(t *testing.T, vecExprCases vecExprBenchCases) { - testFunc := make(map[string]bool) - argList := removeTestOptions(flag.Args()) - testAll := len(argList) == 0 - for _, arg := range argList { - testFunc[arg] = true - } - for funcName, testCases := range vecExprCases { - for _, testCase := range testCases { - ctx := mock.NewContext() - if testCase.aesModes == "" { - testCase.aesModes = "aes-128-ecb" - } - err := ctx.GetSessionVars().SetSystemVar(variable.BlockEncryptionMode, testCase.aesModes) - require.NoError(t, err) - if funcName == ast.CurrentUser || funcName == ast.User { - ctx.GetSessionVars().User = &auth.UserIdentity{ - Username: "tidb", - Hostname: "localhost", - CurrentUser: true, - AuthHostname: "localhost", - AuthUsername: "tidb", - } - } - if funcName == ast.GetParam { - testTime := time.Now() - ctx.GetSessionVars().PlanCacheParams.Append( - types.NewIntDatum(1), - types.NewDecimalDatum(types.NewDecFromStringForTest("20170118123950.123")), - types.NewTimeDatum(types.NewTime(types.FromGoTime(testTime), mysql.TypeTimestamp, 6)), - types.NewDurationDatum(types.ZeroDuration), - types.NewStringDatum("{}"), - types.NewBinaryLiteralDatum([]byte{1}), - types.NewBytesDatum([]byte{'b'}), - types.NewFloat32Datum(1.1), - types.NewFloat64Datum(2.1), - types.NewUintDatum(100), - types.NewMysqlBitDatum([]byte{1}), - types.NewMysqlEnumDatum(types.Enum{Name: "n", Value: 2})) - } - baseFunc, fts, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) - baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) - tmp := strings.Split(baseFuncName, ".") - baseFuncName = tmp[len(tmp)-1] - - if !testAll && (!testFunc[baseFuncName] && !testFunc[funcName]) { - continue - } - // do not forget to implement the vectorized method. - require.Truef(t, baseFunc.vectorized(), "func: %v, case: %+v", baseFuncName, testCase) - commentf := func(row int) string { - return fmt.Sprintf("func: %v, case %+v, row: %v, rowData: %v", baseFuncName, testCase, row, input.GetRow(row).GetDatumRow(fts)) - } - it := chunk.NewIterator4Chunk(input) - i := 0 - var vecWarnCnt uint16 - switch testCase.retEvalType { - case types.ETInt: - err := baseFunc.vecEvalInt(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - i64s := output.Int64s() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalInt(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, i64s[i], val, commentf(i)) - } - i++ - } - case types.ETReal: - err := baseFunc.vecEvalReal(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - f64s := output.Float64s() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalReal(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, f64s[i], val, commentf(i)) - } - i++ - } - case types.ETDecimal: - err := baseFunc.vecEvalDecimal(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - d64s := output.Decimals() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalDecimal(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, d64s[i], *val, commentf(i)) - } - i++ - } - case types.ETDatetime, types.ETTimestamp: - err := baseFunc.vecEvalTime(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - t64s := output.Times() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalTime(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, t64s[i], val, commentf(i)) - } - i++ - } - case types.ETDuration: - err := baseFunc.vecEvalDuration(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - d64s := output.GoDurations() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalDuration(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, d64s[i], val.Duration, commentf(i)) - } - i++ - } - case types.ETJson: - err := baseFunc.vecEvalJSON(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalJSON(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - cmp := types.CompareBinaryJSON(val, output.GetJSON(i)) - require.Zero(t, cmp, commentf(i)) - } - i++ - } - case types.ETString: - err := baseFunc.vecEvalString(input, output) - require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() - for row := it.Begin(); row != it.End(); row = it.Next() { - val, isNull, err := baseFunc.evalString(row) - require.NoErrorf(t, err, commentf(i)) - require.Equal(t, output.IsNull(i), isNull, commentf(i)) - if !isNull { - require.Equal(t, output.GetString(i), val, commentf(i)) - } - i++ - } - default: - t.Fatalf("evalType=%v is not supported", testCase.retEvalType) - } - - // check warnings - totalWarns := ctx.GetSessionVars().StmtCtx.WarningCount() - require.Equal(t, totalWarns, 2*vecWarnCnt) - warns := ctx.GetSessionVars().StmtCtx.GetWarnings() - for i := 0; i < int(vecWarnCnt); i++ { - require.True(t, terror.ErrorEqual(warns[i].Err, warns[i+int(vecWarnCnt)].Err)) - } - } - } -} - -// testVectorizedBuiltinFuncForRand is used to verify that the vectorized -// expression is evaluated correctly -func testVectorizedBuiltinFuncForRand(t *testing.T, vecExprCases vecExprBenchCases) { - for funcName, testCases := range vecExprCases { - require.True(t, strings.EqualFold("rand", funcName)) - - for _, testCase := range testCases { - require.Len(t, testCase.childrenTypes, 0) - - ctx := mock.NewContext() - baseFunc, _, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) - baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) - tmp := strings.Split(baseFuncName, ".") - baseFuncName = tmp[len(tmp)-1] - // do not forget to implement the vectorized method. - require.Truef(t, baseFunc.vectorized(), "func: %v", baseFuncName) - switch testCase.retEvalType { - case types.ETReal: - err := baseFunc.vecEvalReal(input, output) - require.NoError(t, err) - // do not forget to call ResizeXXX/ReserveXXX - require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) - // check result - res := output.Float64s() - for _, v := range res { - require.True(t, (0 <= v) && (v < 1)) - } - default: - t.Fatalf("evalType=%v is not supported", testCase.retEvalType) - } - } - } -} - -// benchmarkVectorizedBuiltinFunc is used to get the effect of -// using the vectorized expression evaluations -func benchmarkVectorizedBuiltinFunc(b *testing.B, vecExprCases vecExprBenchCases) { - ctx := mock.NewContext() - testFunc := make(map[string]bool) - argList := removeTestOptions(flag.Args()) - testAll := len(argList) == 0 - for _, arg := range argList { - testFunc[arg] = true - } - for funcName, testCases := range vecExprCases { - for _, testCase := range testCases { - if testCase.aesModes == "" { - testCase.aesModes = "aes-128-ecb" - } - err := ctx.GetSessionVars().SetSystemVar(variable.BlockEncryptionMode, testCase.aesModes) - if err != nil { - panic(err) - } - if funcName == ast.CurrentUser || funcName == ast.User { - ctx.GetSessionVars().User = &auth.UserIdentity{ - Username: "tidb", - Hostname: "localhost", - CurrentUser: true, - AuthHostname: "localhost", - AuthUsername: "tidb", - } - } - if funcName == ast.GetParam { - testTime := time.Now() - ctx.GetSessionVars().PlanCacheParams.Append( - types.NewIntDatum(1), - types.NewDecimalDatum(types.NewDecFromStringForTest("20170118123950.123")), - types.NewTimeDatum(types.NewTime(types.FromGoTime(testTime), mysql.TypeTimestamp, 6)), - types.NewDurationDatum(types.ZeroDuration), - types.NewStringDatum("{}"), - types.NewBinaryLiteralDatum([]byte{1}), - types.NewBytesDatum([]byte{'b'}), - types.NewFloat32Datum(1.1), - types.NewFloat64Datum(2.1), - types.NewUintDatum(100), - types.NewMysqlBitDatum([]byte{1}), - types.NewMysqlEnumDatum(types.Enum{Name: "n", Value: 2})) - } - baseFunc, _, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) - baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) - tmp := strings.Split(baseFuncName, ".") - baseFuncName = tmp[len(tmp)-1] - - if !testAll && !testFunc[baseFuncName] && !testFunc[funcName] { - continue - } - - b.Run(baseFuncName+"-VecBuiltinFunc", func(b *testing.B) { - b.ResetTimer() - switch testCase.retEvalType { - case types.ETInt: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalInt(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETReal: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalReal(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETDecimal: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalDecimal(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETDatetime, types.ETTimestamp: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalTime(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETDuration: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalDuration(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETJson: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalJSON(input, output); err != nil { - b.Fatal(err) - } - } - case types.ETString: - for i := 0; i < b.N; i++ { - if err := baseFunc.vecEvalString(input, output); err != nil { - b.Fatal(err) - } - } - default: - b.Fatalf("evalType=%v is not supported", testCase.retEvalType) - } - }) - b.Run(baseFuncName+"-NonVecBuiltinFunc", func(b *testing.B) { - b.ResetTimer() - it := chunk.NewIterator4Chunk(input) - switch testCase.retEvalType { - case types.ETInt: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalInt(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendInt64(v) - } - } - } - case types.ETReal: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalReal(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendFloat64(v) - } - } - } - case types.ETDecimal: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalDecimal(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendMyDecimal(v) - } - } - } - case types.ETDatetime, types.ETTimestamp: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalTime(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendTime(v) - } - } - } - case types.ETDuration: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalDuration(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendDuration(v) - } - } - } - case types.ETJson: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalJSON(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendJSON(v) - } - } - } - case types.ETString: - for i := 0; i < b.N; i++ { - output.Reset(testCase.retEvalType) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, isNull, err := baseFunc.evalString(row) - if err != nil { - b.Fatal(err) - } - if isNull { - output.AppendNull() - } else { - output.AppendString(v) - } - } - } - default: - b.Fatalf("evalType=%v is not supported", testCase.retEvalType) - } - }) - } - } -} - -func genVecEvalBool(numCols int, colTypes, eTypes []types.EvalType) (CNFExprs, *chunk.Chunk) { - gens := make([]dataGenerator, 0, len(eTypes)) - for _, eType := range eTypes { - if eType == types.ETString { - gens = append(gens, &numStrGener{*newRangeInt64Gener(0, 10)}) - } else { - gens = append(gens, newDefaultGener(0.05, eType)) - } - } - - ts := make([]types.EvalType, 0, numCols) - gs := make([]dataGenerator, 0, numCols) - fts := make([]*types.FieldType, 0, numCols) - randGen := newDefaultRandGen() - for i := 0; i < numCols; i++ { - idx := randGen.Intn(len(eTypes)) - if colTypes != nil { - for j := range eTypes { - if colTypes[i] == eTypes[j] { - idx = j - break - } - } - } - ts = append(ts, eTypes[idx]) - gs = append(gs, gens[idx]) - fts = append(fts, eType2FieldType(eTypes[idx])) - } - - input := chunk.New(fts, 1024, 1024) - exprs := make(CNFExprs, 0, numCols) - for i := 0; i < numCols; i++ { - fillColumn(ts[i], input, i, vecExprBenchCase{geners: gs}) - exprs = append(exprs, &Column{Index: i, RetType: fts[i]}) - } - return exprs, input -} - -func generateRandomSel() []int { - randGen := newDefaultRandGen() - randGen.Seed(time.Now().UnixNano()) - var sel []int - count := 0 - // Use constant 256 to make it faster to generate randomly arranged sel slices - num := randGen.Intn(256) + 1 - existed := make([]bool, 1024) - for i := 0; i < 1024; i++ { - existed[i] = false - } - for count < num { - val := randGen.Intn(1024) - if !existed[val] { - existed[val] = true - count++ - } - } - for i := 0; i < 1024; i++ { - if existed[i] { - sel = append(sel, i) - } - } - return sel -} - -func BenchmarkVecEvalBool(b *testing.B) { - ctx := mock.NewContext() - selected := make([]bool, 0, 1024) - nulls := make([]bool, 0, 1024) - eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} - tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} - for numCols := 1; numCols <= 2; numCols++ { - typeCombination := make([]types.EvalType, numCols) - var combFunc func(nCols int) - combFunc = func(nCols int) { - if nCols == 0 { - name := "" - for _, t := range typeCombination { - for i := range eTypes { - if t == eTypes[i] { - name += tNames[t] + "/" - } - } - } - exprs, input := genVecEvalBool(numCols, typeCombination, eTypes) - b.Run("Vec-"+name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := VecEvalBool(ctx, exprs, input, selected, nulls) - if err != nil { - b.Fatal(err) - } - } - }) - b.Run("Row-"+name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - it := chunk.NewIterator4Chunk(input) - for row := it.Begin(); row != it.End(); row = it.Next() { - _, _, err := EvalBool(ctx, exprs, row) - if err != nil { - b.Fatal(err) - } - } - } - }) - return - } - for _, eType := range eTypes { - typeCombination[nCols-1] = eType - combFunc(nCols - 1) - } - } - - combFunc(numCols) - } -} - -func BenchmarkRowBasedFilterAndVectorizedFilter(b *testing.B) { - ctx := mock.NewContext() - selected := make([]bool, 0, 1024) - nulls := make([]bool, 0, 1024) - eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} - tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} - for numCols := 1; numCols <= 2; numCols++ { - typeCombination := make([]types.EvalType, numCols) - var combFunc func(nCols int) - combFunc = func(nCols int) { - if nCols == 0 { - name := "" - for _, t := range typeCombination { - for i := range eTypes { - if t == eTypes[i] { - name += tNames[t] + "/" - } - } - } - exprs, input := genVecEvalBool(numCols, typeCombination, eTypes) - it := chunk.NewIterator4Chunk(input) - b.Run("Vec-"+name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := vectorizedFilter(ctx, exprs, it, selected, nulls) - if err != nil { - b.Fatal(err) - } - } - }) - b.Run("Row-"+name, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := rowBasedFilter(ctx, exprs, it, selected, nulls) - if err != nil { - b.Fatal(err) - } - } - }) - return - } - for _, eType := range eTypes { - typeCombination[nCols-1] = eType - combFunc(nCols - 1) - } - } - combFunc(numCols) - } - - // Add special case to prove when some calculations are added, - // the vectorizedFilter for int types will be more faster than rowBasedFilter. - funcName := ast.Least - testCase := vecExprBenchCase{retEvalType: types.ETInt, childrenTypes: []types.EvalType{types.ETInt, types.ETInt}} - expr, _, input, _ := genVecExprBenchCase(ctx, funcName, testCase) - it := chunk.NewIterator4Chunk(input) - - b.Run("Vec-special case", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := vectorizedFilter(ctx, []Expression{expr}, it, selected, nulls) - if err != nil { - panic(err) - } - } - }) - b.Run("Row-special case", func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := rowBasedFilter(ctx, []Expression{expr}, it, selected, nulls) - if err != nil { - panic(err) - } - } - }) -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkCastIntAsIntRow, - BenchmarkCastIntAsIntVec, - BenchmarkVectorizedExecute, - BenchmarkScalarFunctionClone, - ) -} diff --git a/expression/collation_test.go b/expression/collation_test.go deleted file mode 100644 index 686a9002748ea..0000000000000 --- a/expression/collation_test.go +++ /dev/null @@ -1,765 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "testing" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func newExpression(coercibility Coercibility, repertoire Repertoire, chs, coll string) Expression { - constant := &Constant{RetType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetCharset(chs).SetCollate(coll).BuildP()} - constant.SetCoercibility(coercibility) - constant.SetRepertoire(repertoire) - return constant -} - -func TestInferCollation(t *testing.T) { - tests := []struct { - exprs []Expression - err bool - ec *ExprCollation - }{ - // same charset. - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - false, - &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - false, - &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - false, - &ExprCollation{CoercibilityNone, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - // binary charset with non-binary charset. - { - []Expression{ - newExpression(CoercibilityNumeric, UNICODE, charset.CharsetBin, charset.CollationBin), - newExpression(CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - false, - &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []Expression{ - newExpression(CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newExpression(CoercibilityNumeric, UNICODE, charset.CharsetBin, charset.CollationBin), - }, - false, - &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin), - }, - false, - &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin}, - }, - // different charset, one of them is utf8mb4 - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - false, - &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - true, - nil, - }, - // different charset, one of them is CoercibilityCoercible - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityCoercible, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin}, - }, - { - []Expression{ - newExpression(CoercibilityCoercible, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1}, - }, - // different charset, one of them is ASCII - { - []Expression{ - newExpression(CoercibilityImplicit, ASCII, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1}, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, ASCII, charset.CharsetLatin1, charset.CollationLatin1), - }, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin}, - }, - // 3 expressions. - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetBin, charset.CollationBin), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - true, - nil, - }, - { - []Expression{ - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), - newExpression(CoercibilityExplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), - newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - true, - nil, - }, - } - - for i, test := range tests { - ec := inferCollation(test.exprs...) - if test.err { - require.Nil(t, ec, i) - } else { - require.Equal(t, test.ec, ec, i) - } - } -} - -func newConstString(s string, coercibility Coercibility, chs, coll string) *Constant { - repe := ASCII - for i := 0; i < len(s); i++ { - if s[i] >= 0x80 { - repe = UNICODE - } - } - constant := &Constant{RetType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetCharset(chs).SetCollate(coll).BuildP(), Value: types.NewDatum(s)} - constant.SetCoercibility(coercibility) - constant.SetRepertoire(repe) - return constant -} - -func newColString(chs, coll string) *Column { - ft := types.FieldType{} - ft.SetType(mysql.TypeString) - ft.SetCharset(chs) - ft.SetCollate(coll) - column := &Column{RetType: &ft} - column.SetCoercibility(CoercibilityImplicit) - column.SetRepertoire(UNICODE) - if chs == charset.CharsetASCII { - column.SetRepertoire(ASCII) - } - return column -} - -func newColJSON() *Column { - ft := types.FieldType{} - ft.SetType(mysql.TypeJSON) - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - column := &Column{RetType: &ft} - return column -} - -func newConstInt(coercibility Coercibility) *Constant { - ft := types.FieldType{} - ft.SetType(mysql.TypeLong) - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - constant := &Constant{RetType: &ft, Value: types.NewDatum(1)} - constant.SetCoercibility(coercibility) - constant.SetRepertoire(ASCII) - return constant -} - -func newColInt(coercibility Coercibility) *Column { - ft := types.FieldType{} - ft.SetType(mysql.TypeLong) - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - column := &Column{RetType: &ft} - column.SetCoercibility(coercibility) - column.SetRepertoire(ASCII) - return column -} - -func TestDeriveCollation(t *testing.T) { - ctx := mock.NewContext() - tests := []struct { - fcs []string - args []Expression - argTps []types.EvalType - retTp types.EvalType - - err bool - ec *ExprCollation - }{ - { - []string{ast.Left, ast.Right, ast.Repeat, ast.Substr, ast.Substring, ast.Mid}, - []Expression{ - newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - }, - []types.EvalType{types.ETString, types.ETInt}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.Trim, ast.LTrim, ast.RTrim}, - []Expression{ - newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - []types.EvalType{types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.SubstringIndex}, - []Expression{ - newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), - newConstInt(CoercibilityExplicit), - }, - []types.EvalType{types.ETString, types.ETString, types.ETInt}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.Replace, ast.Translate}, - []Expression{ - newConstString("a", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), - newConstString("ㅂ", CoercibilityExplicit, charset.CharsetBin, charset.CollationBin), - }, - []types.EvalType{types.ETString, types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.InsertFunc}, - []Expression{ - newConstString("a", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - newConstInt(CoercibilityExplicit), - newConstString("ㅂ", CoercibilityExplicit, charset.CharsetBin, charset.CollationBin), - }, - []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin}, - }, - { - []string{ast.InsertFunc}, - []Expression{ - newConstString("a", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - newConstInt(CoercibilityExplicit), - newConstString("啊", CoercibilityImplicit, charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.InsertFunc}, - []Expression{ - newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - newConstInt(CoercibilityExplicit), - newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, - types.ETString, - true, - nil, - }, - { - []string{ast.Lpad, ast.Rpad}, - []Expression{ - newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETInt, types.ETString}, - types.ETString, - true, - nil, - }, - { - []string{ast.Lpad, ast.Rpad}, - []Expression{ - newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstInt(CoercibilityExplicit), - newConstString("啊", CoercibilityImplicit, charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETInt, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.FindInSet, ast.Regexp}, - []Expression{ - newColString(charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETString, types.ETString}, - types.ETInt, - true, - nil, - }, - { - []string{ast.Field}, - []Expression{ - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETString, types.ETString}, - types.ETInt, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.Field}, - []Expression{ - newColInt(CoercibilityImplicit), - newColInt(CoercibilityImplicit), - }, - []types.EvalType{types.ETInt, types.ETInt}, - types.ETInt, - false, - &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, - }, - { - []string{ast.Locate, ast.Instr, ast.Position}, - []Expression{ - newColInt(CoercibilityNumeric), - newColInt(CoercibilityNumeric), - }, - []types.EvalType{types.ETInt, types.ETInt}, - types.ETInt, - false, - &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, - }, - { - []string{ast.Format, ast.SHA2}, - []Expression{ - newColInt(CoercibilityNumeric), - newColInt(CoercibilityNumeric), - }, - []types.EvalType{types.ETInt, types.ETInt}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.Space, ast.ToBase64, ast.UUID, ast.Hex, ast.MD5, ast.SHA}, - []Expression{ - newColInt(CoercibilityNumeric), - }, - []types.EvalType{types.ETInt}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.GE, ast.LE, ast.GT, ast.LT, ast.EQ, ast.NE, ast.NullEQ, ast.Strcmp}, - []Expression{ - newColString(charset.CharsetASCII, charset.CollationASCII), - newColString(charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETString}, - types.ETInt, - false, - &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetGBK, charset.CollationGBKBin}, - }, - { - []string{ast.GE, ast.LE, ast.GT, ast.LT, ast.EQ, ast.NE, ast.NullEQ, ast.Strcmp}, - []Expression{ - newColString(charset.CharsetLatin1, charset.CollationLatin1), - newColString(charset.CharsetGBK, charset.CollationGBKBin), - }, - []types.EvalType{types.ETString, types.ETString}, - types.ETInt, - true, - nil, - }, - { - []string{ast.Bin, ast.FromBase64, ast.Oct, ast.Unhex, ast.WeightString}, - []Expression{ - newColString(charset.CharsetLatin1, charset.CollationLatin1), - }, - []types.EvalType{types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ast.ASCII, ast.BitLength, ast.CharLength, ast.CharacterLength, ast.Length, ast.OctetLength, ast.Ord}, - []Expression{ - newColString(charset.CharsetLatin1, charset.CollationLatin1), - }, - []types.EvalType{types.ETString}, - types.ETInt, - false, - &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, - }, - { - []string{ - ast.ExportSet, ast.Elt, ast.MakeSet, - }, - []Expression{ - newColInt(CoercibilityExplicit), - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETInt, types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.ExportSet, ast.Elt, ast.MakeSet, - }, - []Expression{ - newColInt(CoercibilityExplicit), - newColJSON(), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETInt, types.ETJson}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Concat, ast.ConcatWS, ast.Coalesce, ast.Greatest, ast.Least, - }, - []Expression{ - newColString(charset.CharsetGBK, charset.CollationGBKBin), - newColJSON(), - }, - []types.EvalType{types.ETString, types.ETJson}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Concat, ast.ConcatWS, ast.Coalesce, ast.Greatest, ast.Least, - }, - []Expression{ - newColJSON(), - newColString(charset.CharsetBin, charset.CharsetBin), - }, - []types.EvalType{types.ETJson, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetBin, charset.CharsetBin}, - }, - { - []string{ - ast.Concat, ast.ConcatWS, ast.Coalesce, ast.In, ast.Greatest, ast.Least, - }, - []Expression{ - newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETInt, types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Lower, ast.Lcase, ast.Reverse, ast.Upper, ast.Ucase, ast.Quote, - }, - []Expression{ - newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - []types.EvalType{types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Lower, ast.Lcase, ast.Reverse, ast.Upper, ast.Ucase, ast.Quote, - }, - []Expression{ - newColJSON(), - }, - []types.EvalType{types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.If, - }, - []Expression{ - newColInt(CoercibilityExplicit), - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETInt, types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Ifnull, - }, - []Expression{ - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Like, - }, - []Expression{ - newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstString("like", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - newConstString("\\", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), - }, - []types.EvalType{types.ETString, types.ETString, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.DateFormat, ast.TimeFormat, - }, - []Expression{ - newConstString("2020-02-02", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newConstString("%Y %M %D", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETDatetime, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.DateFormat, ast.TimeFormat, - }, - []Expression{ - newConstString("2020-02-02", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), - newConstString("%Y %M %D", CoercibilityCoercible, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), - }, - []types.EvalType{types.ETDatetime, types.ETString}, - types.ETString, - false, - &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Database, ast.User, ast.CurrentUser, ast.Version, ast.CurrentRole, ast.TiDBVersion, ast.CurrentResourceGroup, - }, - []Expression{}, - []types.EvalType{}, - types.ETString, - false, - &ExprCollation{CoercibilitySysconst, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - { - []string{ - ast.Cast, - }, - []Expression{ - newColInt(CoercibilityExplicit), - }, - []types.EvalType{types.ETInt}, - types.ETString, - false, - &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, - }, - } - - for i, test := range tests { - for _, fc := range test.fcs { - ec, err := deriveCollation(ctx, fc, test.args, test.retTp, test.argTps...) - if test.err { - require.Error(t, err, "Number: %d, function: %s", i, fc) - require.Nil(t, ec, i) - } else { - require.Equal(t, test.ec, ec, "Number: %d, function: %s", i, fc) - } - } - } -} - -func TestCompareString(t *testing.T) { - require.Equal(t, 0, types.CompareString("a", "A", "utf8_general_ci")) - require.Equal(t, 0, types.CompareString("À", "A", "utf8_general_ci")) - require.Equal(t, 0, types.CompareString("😜", "😃", "utf8_general_ci")) - require.Equal(t, 0, types.CompareString("a ", "a ", "utf8_general_ci")) - require.Equal(t, 0, types.CompareString("ß", "s", "utf8_general_ci")) - require.NotEqual(t, 0, types.CompareString("ß", "ss", "utf8_general_ci")) - - require.Equal(t, 0, types.CompareString("a", "A", "utf8_unicode_ci")) - require.Equal(t, 0, types.CompareString("À", "A", "utf8_unicode_ci")) - require.Equal(t, 0, types.CompareString("😜", "😃", "utf8_unicode_ci")) - require.Equal(t, 0, types.CompareString("a ", "a ", "utf8_unicode_ci")) - require.NotEqual(t, 0, types.CompareString("ß", "s", "utf8_unicode_ci")) - require.Equal(t, 0, types.CompareString("ß", "ss", "utf8_unicode_ci")) - - require.Equal(t, 0, types.CompareString("a", "A", "utf8mb4_0900_ai_ci")) - require.Equal(t, 0, types.CompareString("À", "A", "utf8mb4_0900_ai_ci")) - require.NotEqual(t, 0, types.CompareString("😜", "😃", "utf8mb4_0900_ai_ci")) - require.NotEqual(t, 0, types.CompareString("a ", "a ", "utf8mb4_0900_ai_ci")) - require.NotEqual(t, 0, types.CompareString("ß", "s", "utf8mb4_0900_ai_ci")) - require.Equal(t, 0, types.CompareString("ß", "ss", "utf8mb4_0900_ai_ci")) - require.NotEqual(t, 0, types.CompareString("\U000FFFFE", "\U000FFFFF", "utf8mb4_0900_ai_ci")) - require.Equal(t, 0, types.CompareString("æ", "ae", "utf8mb4_0900_ai_ci")) - - require.NotEqual(t, 0, types.CompareString("a", "A", "binary")) - require.NotEqual(t, 0, types.CompareString("À", "A", "binary")) - require.NotEqual(t, 0, types.CompareString("😜", "😃", "binary")) - require.NotEqual(t, 0, types.CompareString("a ", "a ", "binary")) - - ctx := mock.NewContext() - ft := types.NewFieldType(mysql.TypeVarString) - col1 := &Column{ - RetType: ft, - Index: 0, - } - col2 := &Column{ - RetType: ft, - Index: 1, - } - chk := chunk.NewChunkWithCapacity([]*types.FieldType{ft, ft}, 4) - chk.Column(0).AppendString("a") - chk.Column(1).AppendString("A") - chk.Column(0).AppendString("À") - chk.Column(1).AppendString("A") - chk.Column(0).AppendString("😜") - chk.Column(1).AppendString("😃") - chk.Column(0).AppendString("a ") - chk.Column(1).AppendString("a ") - for i := 0; i < 4; i++ { - v, isNull, err := CompareStringWithCollationInfo(ctx, col1, col2, chk.GetRow(0), chk.GetRow(0), "utf8_general_ci") - require.NoError(t, err) - require.False(t, isNull) - require.Equal(t, int64(0), v) - } -} diff --git a/expression/column.go b/expression/column.go deleted file mode 100644 index 25e3cb333c78f..0000000000000 --- a/expression/column.go +++ /dev/null @@ -1,790 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "cmp" - "fmt" - "slices" - "strings" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/size" -) - -// CorrelatedColumn stands for a column in a correlated sub query. -type CorrelatedColumn struct { - Column - - Data *types.Datum -} - -// Clone implements Expression interface. -func (col *CorrelatedColumn) Clone() Expression { - return &CorrelatedColumn{ - Column: col.Column, - Data: col.Data, - } -} - -// VecEvalInt evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETInt, input, result) -} - -// VecEvalReal evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETReal, input, result) -} - -// VecEvalString evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETString, input, result) -} - -// VecEvalDecimal evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETDecimal, input, result) -} - -// VecEvalTime evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETTimestamp, input, result) -} - -// VecEvalDuration evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETDuration, input, result) -} - -// VecEvalJSON evaluates this expression in a vectorized manner. -func (col *CorrelatedColumn) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return genVecFromConstExpr(ctx, col, types.ETJson, input, result) -} - -// Traverse implements the TraverseDown interface. -func (col *CorrelatedColumn) Traverse(action TraverseAction) Expression { - return action.Transform(col) -} - -// Eval implements Expression interface. -func (col *CorrelatedColumn) Eval(row chunk.Row) (types.Datum, error) { - return *col.Data, nil -} - -// EvalInt returns int representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { - if col.Data.IsNull() { - return 0, true, nil - } - if col.GetType().Hybrid() { - res, err := col.Data.ToInt64(ctx.GetSessionVars().StmtCtx) - return res, err != nil, err - } - return col.Data.GetInt64(), false, nil -} - -// EvalReal returns real representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { - if col.Data.IsNull() { - return 0, true, nil - } - return col.Data.GetFloat64(), false, nil -} - -// EvalString returns string representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { - if col.Data.IsNull() { - return "", true, nil - } - res, err := col.Data.ToString() - return res, err != nil, err -} - -// EvalDecimal returns decimal representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { - if col.Data.IsNull() { - return nil, true, nil - } - return col.Data.GetMysqlDecimal(), false, nil -} - -// EvalTime returns DATE/DATETIME/TIMESTAMP representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalTime(ctx sessionctx.Context, row chunk.Row) (types.Time, bool, error) { - if col.Data.IsNull() { - return types.ZeroTime, true, nil - } - return col.Data.GetMysqlTime(), false, nil -} - -// EvalDuration returns Duration representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalDuration(ctx sessionctx.Context, row chunk.Row) (types.Duration, bool, error) { - if col.Data.IsNull() { - return types.Duration{}, true, nil - } - return col.Data.GetMysqlDuration(), false, nil -} - -// EvalJSON returns JSON representation of CorrelatedColumn. -func (col *CorrelatedColumn) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { - if col.Data.IsNull() { - return types.BinaryJSON{}, true, nil - } - return col.Data.GetMysqlJSON(), false, nil -} - -// Equal implements Expression interface. -func (col *CorrelatedColumn) Equal(ctx sessionctx.Context, expr Expression) bool { - if cc, ok := expr.(*CorrelatedColumn); ok { - return col.Column.Equal(ctx, &cc.Column) - } - return false -} - -// IsCorrelated implements Expression interface. -func (col *CorrelatedColumn) IsCorrelated() bool { - return true -} - -// ConstItem implements Expression interface. -func (col *CorrelatedColumn) ConstItem(_ *stmtctx.StatementContext) bool { - return false -} - -// Decorrelate implements Expression interface. -func (col *CorrelatedColumn) Decorrelate(schema *Schema) Expression { - if !schema.Contains(&col.Column) { - return col - } - return &col.Column -} - -// ResolveIndices implements Expression interface. -func (col *CorrelatedColumn) ResolveIndices(_ *Schema) (Expression, error) { - return col, nil -} - -func (col *CorrelatedColumn) resolveIndices(_ *Schema) error { - return nil -} - -// ResolveIndicesByVirtualExpr implements Expression interface. -func (col *CorrelatedColumn) ResolveIndicesByVirtualExpr(_ *Schema) (Expression, bool) { - return col, true -} - -func (col *CorrelatedColumn) resolveIndicesByVirtualExpr(_ *Schema) bool { - return true -} - -// MemoryUsage return the memory usage of CorrelatedColumn -func (col *CorrelatedColumn) MemoryUsage() (sum int64) { - if col == nil { - return - } - - sum = col.Column.MemoryUsage() + size.SizeOfPointer - if col.Data != nil { - sum += col.Data.MemUsage() - } - return sum -} - -// RemapColumn remaps columns with provided mapping and returns new expression -func (col *CorrelatedColumn) RemapColumn(m map[int64]*Column) (Expression, error) { - mapped := m[(&col.Column).UniqueID] - if mapped == nil { - return nil, errors.Errorf("Can't remap column for %s", col) - } - return &CorrelatedColumn{ - Column: *mapped, - Data: col.Data, - }, nil -} - -// Column represents a column. -type Column struct { - RetType *types.FieldType - // ID is used to specify whether this column is ExtraHandleColumn or to access histogram. - // We'll try to remove it in the future. - ID int64 - // UniqueID is the unique id of this column. - UniqueID int64 - - // Index is used for execution, to tell the column's position in the given row. - Index int - - hashcode []byte - - // VirtualExpr is used to save expression for virtual column - VirtualExpr Expression - - OrigName string - IsHidden bool - - // IsPrefix indicates whether this column is a prefix column in index. - // - // for example: - // pk(col1, col2), index(col1(10)), key: col1(10)_col1_col2 => index's col1 will be true - // pk(col1(10), col2), index(col1), key: col1_col1(10)_col2 => pk's col1 will be true - IsPrefix bool - - // InOperand indicates whether this column is the inner operand of column equal condition converted - // from `[not] in (subq)`. - InOperand bool - - collationInfo - - CorrelatedColUniqueID int64 -} - -// Equal implements Expression interface. -func (col *Column) Equal(_ sessionctx.Context, expr Expression) bool { - if newCol, ok := expr.(*Column); ok { - return newCol.UniqueID == col.UniqueID - } - return false -} - -// EqualByExprAndID extends Equal by comparing virual expression -func (col *Column) EqualByExprAndID(_ sessionctx.Context, expr Expression) bool { - if newCol, ok := expr.(*Column); ok { - expr, isOk := col.VirtualExpr.(*ScalarFunction) - isVirExprMatched := isOk && expr.Equal(nil, newCol.VirtualExpr) && col.RetType.Equal(newCol.RetType) - return (newCol.UniqueID == col.UniqueID) || isVirExprMatched - } - return false -} - -// VecEvalInt evaluates this expression in a vectorized manner. -func (col *Column) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if col.RetType.Hybrid() { - it := chunk.NewIterator4Chunk(input) - result.ResizeInt64(0, false) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, null, err := col.EvalInt(ctx, row) - if err != nil { - return err - } - if null { - result.AppendNull() - } else { - result.AppendInt64(v) - } - } - return nil - } - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalReal evaluates this expression in a vectorized manner. -func (col *Column) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - n := input.NumRows() - src := input.Column(col.Index) - if col.GetType().GetType() == mysql.TypeFloat { - result.ResizeFloat64(n, false) - f32s := src.Float32s() - f64s := result.Float64s() - sel := input.Sel() - if sel != nil { - for i, j := range sel { - if src.IsNull(j) { - result.SetNull(i, true) - } else { - f64s[i] = float64(f32s[j]) - } - } - return nil - } - result.MergeNulls(src) - for i := range f32s { - if result.IsNull(i) { - continue - } - f64s[i] = float64(f32s[i]) - } - return nil - } - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalString evaluates this expression in a vectorized manner. -func (col *Column) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if col.RetType.Hybrid() { - it := chunk.NewIterator4Chunk(input) - result.ReserveString(input.NumRows()) - for row := it.Begin(); row != it.End(); row = it.Next() { - v, null, err := col.EvalString(ctx, row) - if err != nil { - return err - } - if null { - result.AppendNull() - } else { - result.AppendString(v) - } - } - return nil - } - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalDecimal evaluates this expression in a vectorized manner. -func (col *Column) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalTime evaluates this expression in a vectorized manner. -func (col *Column) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalDuration evaluates this expression in a vectorized manner. -func (col *Column) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -// VecEvalJSON evaluates this expression in a vectorized manner. -func (col *Column) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - input.Column(col.Index).CopyReconstruct(input.Sel(), result) - return nil -} - -const columnPrefix = "Column#" - -// String implements Stringer interface. -func (col *Column) String() string { - if col.IsHidden { - // A hidden column must be a virtual generated column, we should output its expression. - return col.VirtualExpr.String() - } - if col.OrigName != "" { - return col.OrigName - } - var builder strings.Builder - fmt.Fprintf(&builder, "%s%d", columnPrefix, col.UniqueID) - return builder.String() -} - -// MarshalJSON implements json.Marshaler interface. -func (col *Column) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("%q", col)), nil -} - -// GetType implements Expression interface. -func (col *Column) GetType() *types.FieldType { - return col.RetType -} - -// Traverse implements the TraverseDown interface. -func (col *Column) Traverse(action TraverseAction) Expression { - return action.Transform(col) -} - -// Eval implements Expression interface. -func (col *Column) Eval(row chunk.Row) (types.Datum, error) { - return row.GetDatum(col.Index, col.RetType), nil -} - -// EvalInt returns int representation of Column. -func (col *Column) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { - if col.GetType().Hybrid() { - val := row.GetDatum(col.Index, col.RetType) - if val.IsNull() { - return 0, true, nil - } - if val.Kind() == types.KindMysqlBit { - val, err := val.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) - return int64(val), err != nil, err - } - res, err := val.ToInt64(ctx.GetSessionVars().StmtCtx) - return res, err != nil, err - } - if row.IsNull(col.Index) { - return 0, true, nil - } - return row.GetInt64(col.Index), false, nil -} - -// EvalReal returns real representation of Column. -func (col *Column) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { - if row.IsNull(col.Index) { - return 0, true, nil - } - if col.GetType().GetType() == mysql.TypeFloat { - return float64(row.GetFloat32(col.Index)), false, nil - } - return row.GetFloat64(col.Index), false, nil -} - -// EvalString returns string representation of Column. -func (col *Column) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { - if row.IsNull(col.Index) { - return "", true, nil - } - - // Specially handle the ENUM/SET/BIT input value. - if col.GetType().Hybrid() { - val := row.GetDatum(col.Index, col.RetType) - res, err := val.ToString() - return res, err != nil, err - } - - val := row.GetString(col.Index) - return val, false, nil -} - -// EvalDecimal returns decimal representation of Column. -func (col *Column) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { - if row.IsNull(col.Index) { - return nil, true, nil - } - return row.GetMyDecimal(col.Index), false, nil -} - -// EvalTime returns DATE/DATETIME/TIMESTAMP representation of Column. -func (col *Column) EvalTime(ctx sessionctx.Context, row chunk.Row) (types.Time, bool, error) { - if row.IsNull(col.Index) { - return types.ZeroTime, true, nil - } - return row.GetTime(col.Index), false, nil -} - -// EvalDuration returns Duration representation of Column. -func (col *Column) EvalDuration(ctx sessionctx.Context, row chunk.Row) (types.Duration, bool, error) { - if row.IsNull(col.Index) { - return types.Duration{}, true, nil - } - duration := row.GetDuration(col.Index, col.RetType.GetDecimal()) - return duration, false, nil -} - -// EvalJSON returns JSON representation of Column. -func (col *Column) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { - if row.IsNull(col.Index) { - return types.BinaryJSON{}, true, nil - } - return row.GetJSON(col.Index), false, nil -} - -// Clone implements Expression interface. -func (col *Column) Clone() Expression { - newCol := *col - return &newCol -} - -// IsCorrelated implements Expression interface. -func (col *Column) IsCorrelated() bool { - return false -} - -// ConstItem implements Expression interface. -func (col *Column) ConstItem(_ *stmtctx.StatementContext) bool { - return false -} - -// Decorrelate implements Expression interface. -func (col *Column) Decorrelate(_ *Schema) Expression { - return col -} - -// HashCode implements Expression interface. -func (col *Column) HashCode(_ *stmtctx.StatementContext) []byte { - if len(col.hashcode) != 0 { - return col.hashcode - } - col.hashcode = make([]byte, 0, 9) - col.hashcode = append(col.hashcode, columnFlag) - col.hashcode = codec.EncodeInt(col.hashcode, col.UniqueID) - return col.hashcode -} - -// CleanHashCode will clean the hashcode you may be cached before. It's used especially in schema-cloned & reallocated-uniqueID's cases. -func (col *Column) CleanHashCode() { - col.hashcode = make([]byte, 0, 9) -} - -// ResolveIndices implements Expression interface. -func (col *Column) ResolveIndices(schema *Schema) (Expression, error) { - newCol := col.Clone() - err := newCol.resolveIndices(schema) - return newCol, err -} - -func (col *Column) resolveIndices(schema *Schema) error { - col.Index = schema.ColumnIndex(col) - if col.Index == -1 { - return errors.Errorf("Can't find column %s in schema %s", col, schema) - } - return nil -} - -// ResolveIndicesByVirtualExpr implements Expression interface. -func (col *Column) ResolveIndicesByVirtualExpr(schema *Schema) (Expression, bool) { - newCol := col.Clone() - isOk := newCol.resolveIndicesByVirtualExpr(schema) - return newCol, isOk -} - -func (col *Column) resolveIndicesByVirtualExpr(schema *Schema) bool { - for i, c := range schema.Columns { - if c.EqualByExprAndID(nil, col) { - col.Index = i - return true - } - } - return false -} - -// RemapColumn remaps columns with provided mapping and returns new expression -func (col *Column) RemapColumn(m map[int64]*Column) (Expression, error) { - mapped := m[col.UniqueID] - if mapped == nil { - return nil, errors.Errorf("Can't remap column for %s", col) - } - return mapped, nil -} - -// Vectorized returns if this expression supports vectorized evaluation. -func (col *Column) Vectorized() bool { - return true -} - -// ToInfo converts the expression.Column to model.ColumnInfo for casting values, -// beware it doesn't fill all the fields of the model.ColumnInfo. -func (col *Column) ToInfo() *model.ColumnInfo { - return &model.ColumnInfo{ - ID: col.ID, - FieldType: *col.RetType, - } -} - -// Column2Exprs will transfer column slice to expression slice. -func Column2Exprs(cols []*Column) []Expression { - result := make([]Expression, 0, len(cols)) - for _, col := range cols { - result = append(result, col) - } - return result -} - -// ColInfo2Col finds the corresponding column of the ColumnInfo in a column slice. -func ColInfo2Col(cols []*Column, col *model.ColumnInfo) *Column { - for _, c := range cols { - if c.ID == col.ID { - return c - } - } - return nil -} - -// IndexCol2Col finds the corresponding column of the IndexColumn in a column slice. -func IndexCol2Col(colInfos []*model.ColumnInfo, cols []*Column, col *model.IndexColumn) *Column { - for i, info := range colInfos { - if info.Name.L == col.Name.L { - if col.Length > 0 && info.FieldType.GetFlen() > col.Length { - c := *cols[i] - c.IsPrefix = true - return &c - } - return cols[i] - } - } - return nil -} - -// IndexInfo2PrefixCols gets the corresponding []*Column of the indexInfo's []*IndexColumn, -// together with a []int containing their lengths. -// If this index has three IndexColumn that the 1st and 3rd IndexColumn has corresponding *Column, -// the return value will be only the 1st corresponding *Column and its length. -// TODO: Use a struct to represent {*Column, int}. And merge IndexInfo2PrefixCols and IndexInfo2Cols. -func IndexInfo2PrefixCols(colInfos []*model.ColumnInfo, cols []*Column, index *model.IndexInfo) ([]*Column, []int) { - retCols := make([]*Column, 0, len(index.Columns)) - lengths := make([]int, 0, len(index.Columns)) - for _, c := range index.Columns { - col := IndexCol2Col(colInfos, cols, c) - if col == nil { - return retCols, lengths - } - retCols = append(retCols, col) - if c.Length != types.UnspecifiedLength && c.Length == col.RetType.GetFlen() { - lengths = append(lengths, types.UnspecifiedLength) - } else { - lengths = append(lengths, c.Length) - } - } - return retCols, lengths -} - -// IndexInfo2Cols gets the corresponding []*Column of the indexInfo's []*IndexColumn, -// together with a []int containing their lengths. -// If this index has three IndexColumn that the 1st and 3rd IndexColumn has corresponding *Column, -// the return value will be [col1, nil, col2]. -func IndexInfo2Cols(colInfos []*model.ColumnInfo, cols []*Column, index *model.IndexInfo) ([]*Column, []int) { - retCols := make([]*Column, 0, len(index.Columns)) - lens := make([]int, 0, len(index.Columns)) - for _, c := range index.Columns { - col := IndexCol2Col(colInfos, cols, c) - if col == nil { - retCols = append(retCols, col) - lens = append(lens, types.UnspecifiedLength) - continue - } - retCols = append(retCols, col) - if c.Length != types.UnspecifiedLength && c.Length == col.RetType.GetFlen() { - lens = append(lens, types.UnspecifiedLength) - } else { - lens = append(lens, c.Length) - } - } - return retCols, lens -} - -// FindPrefixOfIndex will find columns in index by checking the unique id. -// So it will return at once no matching column is found. -func FindPrefixOfIndex(cols []*Column, idxColIDs []int64) []*Column { - retCols := make([]*Column, 0, len(idxColIDs)) -idLoop: - for _, id := range idxColIDs { - for _, col := range cols { - if col.UniqueID == id { - retCols = append(retCols, col) - continue idLoop - } - } - // If no matching column is found, just return. - return retCols - } - return retCols -} - -// EvalVirtualColumn evals the virtual column -func (col *Column) EvalVirtualColumn(row chunk.Row) (types.Datum, error) { - return col.VirtualExpr.Eval(row) -} - -// SupportReverseEval checks whether the builtinFunc support reverse evaluation. -func (col *Column) SupportReverseEval() bool { - switch col.RetType.GetType() { - case mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, - mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: - return true - } - return false -} - -// ReverseEval evaluates the only one column value with given function result. -func (col *Column) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { - return types.ChangeReverseResultByUpperLowerBound(sc, col.RetType, res, rType) -} - -// Coercibility returns the coercibility value which is used to check collations. -func (col *Column) Coercibility() Coercibility { - if !col.HasCoercibility() { - col.SetCoercibility(deriveCoercibilityForColumn(col)) - } - return col.collationInfo.Coercibility() -} - -// Repertoire returns the repertoire value which is used to check collations. -func (col *Column) Repertoire() Repertoire { - if col.repertoire != 0 { - return col.repertoire - } - switch col.RetType.EvalType() { - case types.ETJson: - return UNICODE - case types.ETString: - if col.RetType.GetCharset() == charset.CharsetASCII { - return ASCII - } - return UNICODE - default: - return ASCII - } -} - -// SortColumns sort columns based on UniqueID. -func SortColumns(cols []*Column) []*Column { - sorted := make([]*Column, len(cols)) - copy(sorted, cols) - slices.SortFunc(sorted, func(i, j *Column) int { - return cmp.Compare(i.UniqueID, j.UniqueID) - }) - return sorted -} - -// InColumnArray check whether the col is in the cols array -func (col *Column) InColumnArray(cols []*Column) bool { - for _, c := range cols { - if col.Equal(nil, c) { - return true - } - } - return false -} - -// GcColumnExprIsTidbShard check whether the expression is tidb_shard() -func GcColumnExprIsTidbShard(virtualExpr Expression) bool { - if virtualExpr == nil { - return false - } - - f, ok := virtualExpr.(*ScalarFunction) - if !ok { - return false - } - - if f.FuncName.L != ast.TiDBShard { - return false - } - - return true -} - -const emptyColumnSize = int64(unsafe.Sizeof(Column{})) - -// MemoryUsage return the memory usage of Column -func (col *Column) MemoryUsage() (sum int64) { - if col == nil { - return - } - - sum = emptyColumnSize + int64(cap(col.hashcode)) + int64(len(col.OrigName)+len(col.charset)+len(col.collation)) - - if col.RetType != nil { - sum += col.RetType.MemoryUsage() - } - if col.VirtualExpr != nil { - sum += col.VirtualExpr.MemoryUsage() - } - return -} diff --git a/expression/column_test.go b/expression/column_test.go deleted file mode 100644 index 631081a1ebd3a..0000000000000 --- a/expression/column_test.go +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestColumn(t *testing.T) { - ctx := mock.NewContext() - col := &Column{RetType: types.NewFieldType(mysql.TypeLonglong), UniqueID: 1} - - require.True(t, col.Equal(nil, col)) - require.False(t, col.Equal(nil, &Column{})) - require.False(t, col.IsCorrelated()) - require.True(t, col.Equal(nil, col.Decorrelate(nil))) - - marshal, err := col.MarshalJSON() - require.NoError(t, err) - require.EqualValues(t, []byte{0x22, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x23, 0x31, 0x22}, marshal) - - intDatum := types.NewIntDatum(1) - corCol := &CorrelatedColumn{Column: *col, Data: &intDatum} - invalidCorCol := &CorrelatedColumn{Column: Column{}} - schema := NewSchema(&Column{UniqueID: 1}) - require.True(t, corCol.Equal(nil, corCol)) - require.False(t, corCol.Equal(nil, invalidCorCol)) - require.True(t, corCol.IsCorrelated()) - require.False(t, corCol.ConstItem(nil)) - require.True(t, corCol.Decorrelate(schema).Equal(nil, col)) - require.True(t, invalidCorCol.Decorrelate(schema).Equal(nil, invalidCorCol)) - - intCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeLonglong)}, - Data: &intDatum} - intVal, isNull, err := intCorCol.EvalInt(ctx, chunk.Row{}) - require.Equal(t, int64(1), intVal) - require.False(t, isNull) - require.NoError(t, err) - - realDatum := types.NewFloat64Datum(1.2) - realCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDouble)}, - Data: &realDatum} - realVal, isNull, err := realCorCol.EvalReal(ctx, chunk.Row{}) - require.Equal(t, float64(1.2), realVal) - require.False(t, isNull) - require.NoError(t, err) - - decimalDatum := types.NewDecimalDatum(types.NewDecFromStringForTest("1.2")) - decimalCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeNewDecimal)}, - Data: &decimalDatum} - decVal, isNull, err := decimalCorCol.EvalDecimal(ctx, chunk.Row{}) - require.Zero(t, decVal.Compare(types.NewDecFromStringForTest("1.2"))) - require.False(t, isNull) - require.NoError(t, err) - - stringDatum := types.NewStringDatum("abc") - stringCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeVarchar)}, - Data: &stringDatum} - strVal, isNull, err := stringCorCol.EvalString(ctx, chunk.Row{}) - require.Equal(t, "abc", strVal) - require.False(t, isNull) - require.NoError(t, err) - - durationCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDuration)}, - Data: &durationDatum} - durationVal, isNull, err := durationCorCol.EvalDuration(ctx, chunk.Row{}) - require.Zero(t, durationVal.Compare(duration)) - require.False(t, isNull) - require.NoError(t, err) - - timeDatum := types.NewTimeDatum(tm) - timeCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDatetime)}, - Data: &timeDatum} - timeVal, isNull, err := timeCorCol.EvalTime(ctx, chunk.Row{}) - require.Zero(t, timeVal.Compare(tm)) - require.False(t, isNull) - require.NoError(t, err) -} - -func TestColumnHashCode(t *testing.T) { - col1 := &Column{ - UniqueID: 12, - } - require.EqualValues(t, []byte{0x1, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc}, col1.HashCode(nil)) - - col2 := &Column{ - UniqueID: 2, - } - require.EqualValues(t, []byte{0x1, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, col2.HashCode(nil)) -} - -func TestColumn2Expr(t *testing.T) { - cols := make([]*Column, 0, 5) - for i := 0; i < 5; i++ { - cols = append(cols, &Column{UniqueID: int64(i)}) - } - - exprs := Column2Exprs(cols) - for i := range exprs { - require.True(t, exprs[i].Equal(nil, cols[i])) - } -} - -func TestColInfo2Col(t *testing.T) { - col0, col1 := &Column{ID: 0}, &Column{ID: 1} - cols := []*Column{col0, col1} - colInfo := &model.ColumnInfo{ID: 0} - res := ColInfo2Col(cols, colInfo) - require.True(t, res.Equal(nil, col1)) - - colInfo.ID = 3 - res = ColInfo2Col(cols, colInfo) - require.Nil(t, res) -} - -func TestIndexInfo2Cols(t *testing.T) { - col0 := &Column{UniqueID: 0, ID: 0, RetType: types.NewFieldType(mysql.TypeLonglong)} - col1 := &Column{UniqueID: 1, ID: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} - colInfo0 := &model.ColumnInfo{ID: 0, Name: model.NewCIStr("0")} - colInfo1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("1")} - indexCol0, indexCol1 := &model.IndexColumn{Name: model.NewCIStr("0")}, &model.IndexColumn{Name: model.NewCIStr("1")} - indexInfo := &model.IndexInfo{Columns: []*model.IndexColumn{indexCol0, indexCol1}} - - cols := []*Column{col0} - colInfos := []*model.ColumnInfo{colInfo0} - resCols, lengths := IndexInfo2PrefixCols(colInfos, cols, indexInfo) - require.Len(t, resCols, 1) - require.Len(t, lengths, 1) - require.True(t, resCols[0].Equal(nil, col0)) - - cols = []*Column{col1} - colInfos = []*model.ColumnInfo{colInfo1} - resCols, lengths = IndexInfo2PrefixCols(colInfos, cols, indexInfo) - require.Len(t, resCols, 0) - require.Len(t, lengths, 0) - - cols = []*Column{col0, col1} - colInfos = []*model.ColumnInfo{colInfo0, colInfo1} - resCols, lengths = IndexInfo2PrefixCols(colInfos, cols, indexInfo) - require.Len(t, resCols, 2) - require.Len(t, lengths, 2) - require.True(t, resCols[0].Equal(nil, col0)) - require.True(t, resCols[1].Equal(nil, col1)) -} - -func TestColHybird(t *testing.T) { - ctx := mock.NewContext() - - // bit - ft := types.NewFieldType(mysql.TypeBit) - col := &Column{RetType: ft, Index: 0} - input := chunk.New([]*types.FieldType{ft}, 1024, 1024) - for i := 0; i < 1024; i++ { - num, err := types.ParseBitStr(fmt.Sprintf("0b%b", i)) - require.NoError(t, err) - input.AppendBytes(0, num) - } - result := chunk.NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) - require.Nil(t, col.VecEvalInt(ctx, input, result)) - - it := chunk.NewIterator4Chunk(input) - for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { - v, _, err := col.EvalInt(ctx, row) - require.NoError(t, err) - require.Equal(t, result.GetInt64(i), v) - } - - // use a container which has the different field type with bit - result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) - require.Nil(t, col.VecEvalInt(ctx, input, result)) - for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { - v, _, err := col.EvalInt(ctx, row) - require.NoError(t, err) - require.Equal(t, result.GetInt64(i), v) - } - - // enum - ft = types.NewFieldType(mysql.TypeEnum) - col.RetType = ft - input = chunk.New([]*types.FieldType{ft}, 1024, 1024) - for i := 0; i < 1024; i++ { - input.AppendEnum(0, types.Enum{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) - } - result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) - require.Nil(t, col.VecEvalString(ctx, input, result)) - - it = chunk.NewIterator4Chunk(input) - for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { - v, _, err := col.EvalString(ctx, row) - require.NoError(t, err) - require.Equal(t, result.GetString(i), v) - } - - // set - ft = types.NewFieldType(mysql.TypeSet) - col.RetType = ft - input = chunk.New([]*types.FieldType{ft}, 1024, 1024) - for i := 0; i < 1024; i++ { - input.AppendSet(0, types.Set{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) - } - result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) - require.Nil(t, col.VecEvalString(ctx, input, result)) - - it = chunk.NewIterator4Chunk(input) - for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { - v, _, err := col.EvalString(ctx, row) - require.NoError(t, err) - require.Equal(t, result.GetString(i), v) - } -} - -func TestInColumnArray(t *testing.T) { - // normal case, col is in column array - col0, col1 := &Column{ID: 0, UniqueID: 0}, &Column{ID: 1, UniqueID: 1} - cols := []*Column{col0, col1} - require.True(t, col0.InColumnArray(cols)) - - // abnormal case, col is not in column array - require.False(t, col0.InColumnArray([]*Column{col1})) - - // abnormal case, input is nil - require.False(t, col0.InColumnArray(nil)) -} - -func TestGcColumnExprIsTidbShard(t *testing.T) { - ctx := mock.NewContext() - - // abnormal case - // nil, not tidb_shard - require.False(t, GcColumnExprIsTidbShard(nil)) - - // `a = 1`, not tidb_shard - ft := types.NewFieldType(mysql.TypeLonglong) - col := &Column{RetType: ft, Index: 0} - d1 := types.NewDatum(1) - con := &Constant{Value: d1, RetType: ft} - expr := NewFunctionInternal(ctx, ast.EQ, ft, col, con) - require.False(t, GcColumnExprIsTidbShard(expr)) - - // normal case - // tidb_shard(a) = 1 - shardExpr := NewFunctionInternal(ctx, ast.TiDBShard, ft, col) - require.True(t, GcColumnExprIsTidbShard(shardExpr)) -} diff --git a/expression/constant.go b/expression/constant.go deleted file mode 100644 index c53d0c2c2d74f..0000000000000 --- a/expression/constant.go +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "fmt" - "unsafe" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" -) - -// NewOne stands for a number 1. -func NewOne() *Constant { - retT := types.NewFieldType(mysql.TypeTiny) - retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion - retT.SetFlen(1) - retT.SetDecimal(0) - return &Constant{ - Value: types.NewDatum(1), - RetType: retT, - } -} - -// NewZero stands for a number 0. -func NewZero() *Constant { - retT := types.NewFieldType(mysql.TypeTiny) - retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion - retT.SetFlen(1) - retT.SetDecimal(0) - return &Constant{ - Value: types.NewDatum(0), - RetType: retT, - } -} - -// NewUInt64Const stands for constant of a given number. -func NewUInt64Const(num int) *Constant { - retT := types.NewFieldType(mysql.TypeLonglong) - retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion - retT.SetFlen(mysql.MaxIntWidth) - retT.SetDecimal(0) - return &Constant{ - Value: types.NewDatum(num), - RetType: retT, - } -} - -// NewUInt64ConstWithFieldType stands for constant of a given number with specified fieldType. -func NewUInt64ConstWithFieldType(num uint64, fieldType *types.FieldType) *Constant { - return &Constant{ - Value: types.NewDatum(num), - RetType: fieldType, - } -} - -// NewInt64Const stands for constant of a given number. -func NewInt64Const(num int64) *Constant { - retT := types.NewFieldType(mysql.TypeLonglong) - retT.SetFlen(mysql.MaxIntWidth) - retT.SetDecimal(0) - return &Constant{ - Value: types.NewDatum(num), - RetType: retT, - } -} - -// NewNull stands for null constant. -func NewNull() *Constant { - retT := types.NewFieldType(mysql.TypeTiny) - retT.SetFlen(1) - retT.SetDecimal(0) - return &Constant{ - Value: types.NewDatum(nil), - RetType: retT, - } -} - -// NewNullWithFieldType stands for null constant with specified fieldType. -func NewNullWithFieldType(fieldType *types.FieldType) *Constant { - return &Constant{ - Value: types.NewDatum(nil), - RetType: fieldType, - } -} - -// Constant stands for a constant value. -type Constant struct { - Value types.Datum - RetType *types.FieldType - // DeferredExpr holds deferred function in PlanCache cached plan. - // it's only used to represent non-deterministic functions(see expression.DeferredFunctions) - // in PlanCache cached plan, so let them can be evaluated until cached item be used. - DeferredExpr Expression - // ParamMarker holds param index inside sessionVars.PreparedParams. - // It's only used to reference a user variable provided in the `EXECUTE` statement or `COM_EXECUTE` binary protocol. - ParamMarker *ParamMarker - hashcode []byte - - collationInfo -} - -// ParamMarker indicates param provided by COM_STMT_EXECUTE. -type ParamMarker struct { - ctx sessionctx.Context - order int -} - -// GetUserVar returns the corresponding user variable presented in the `EXECUTE` statement or `COM_EXECUTE` command. -func (d *ParamMarker) GetUserVar() types.Datum { - sessionVars := d.ctx.GetSessionVars() - return sessionVars.PlanCacheParams.GetParamValue(d.order) -} - -// String implements fmt.Stringer interface. -func (c *Constant) String() string { - if c.ParamMarker != nil { - dt := c.ParamMarker.GetUserVar() - c.Value.SetValue(dt.GetValue(), c.RetType) - } else if c.DeferredExpr != nil { - return c.DeferredExpr.String() - } - return fmt.Sprintf("%v", c.Value.GetValue()) -} - -// MarshalJSON implements json.Marshaler interface. -func (c *Constant) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("%q", c)), nil -} - -// Clone implements Expression interface. -func (c *Constant) Clone() Expression { - con := *c - return &con -} - -// GetType implements Expression interface. -func (c *Constant) GetType() *types.FieldType { - if c.ParamMarker != nil { - // GetType() may be called in multi-threaded context, e.g, in building inner executors of IndexJoin, - // so it should avoid data race. We achieve this by returning different FieldType pointer for each call. - tp := types.NewFieldType(mysql.TypeUnspecified) - dt := c.ParamMarker.GetUserVar() - types.InferParamTypeFromDatum(&dt, tp) - return tp - } - return c.RetType -} - -// VecEvalInt evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETInt, input, result) - } - return c.DeferredExpr.VecEvalInt(ctx, input, result) -} - -// VecEvalReal evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETReal, input, result) - } - return c.DeferredExpr.VecEvalReal(ctx, input, result) -} - -// VecEvalString evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETString, input, result) - } - return c.DeferredExpr.VecEvalString(ctx, input, result) -} - -// VecEvalDecimal evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETDecimal, input, result) - } - return c.DeferredExpr.VecEvalDecimal(ctx, input, result) -} - -// VecEvalTime evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETTimestamp, input, result) - } - return c.DeferredExpr.VecEvalTime(ctx, input, result) -} - -// VecEvalDuration evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETDuration, input, result) - } - return c.DeferredExpr.VecEvalDuration(ctx, input, result) -} - -// VecEvalJSON evaluates this expression in a vectorized manner. -func (c *Constant) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - if c.DeferredExpr == nil { - return genVecFromConstExpr(ctx, c, types.ETJson, input, result) - } - return c.DeferredExpr.VecEvalJSON(ctx, input, result) -} - -func (c *Constant) getLazyDatum(row chunk.Row) (dt types.Datum, isLazy bool, err error) { - if c.ParamMarker != nil { - return c.ParamMarker.GetUserVar(), true, nil - } else if c.DeferredExpr != nil { - dt, err = c.DeferredExpr.Eval(row) - return dt, true, err - } - return types.Datum{}, false, nil -} - -// Traverse implements the TraverseDown interface. -func (c *Constant) Traverse(action TraverseAction) Expression { - return action.Transform(c) -} - -// Eval implements Expression interface. -func (c *Constant) Eval(row chunk.Row) (types.Datum, error) { - if dt, lazy, err := c.getLazyDatum(row); lazy { - if err != nil { - return c.Value, err - } - if dt.IsNull() { - c.Value.SetNull() - return c.Value, nil - } - if c.DeferredExpr != nil { - sf, sfOk := c.DeferredExpr.(*ScalarFunction) - if sfOk { - if dt.Kind() != types.KindMysqlDecimal { - val, err := dt.ConvertTo(sf.GetCtx().GetSessionVars().StmtCtx, c.RetType) - if err != nil { - return dt, err - } - return val, nil - } - if err := c.adjustDecimal(dt.GetMysqlDecimal()); err != nil { - return dt, err - } - } - } - return dt, nil - } - return c.Value, nil -} - -// EvalInt returns int representation of Constant. -func (c *Constant) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return 0, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return 0, true, nil - } else if dt.Kind() == types.KindBinaryLiteral { - val, err := dt.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) - return int64(val), err != nil, err - } else if c.GetType().Hybrid() || dt.Kind() == types.KindString { - res, err := dt.ToInt64(ctx.GetSessionVars().StmtCtx) - return res, false, err - } else if dt.Kind() == types.KindMysqlBit { - uintVal, err := dt.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) - return int64(uintVal), false, err - } - return dt.GetInt64(), false, nil -} - -// EvalReal returns real representation of Constant. -func (c *Constant) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return 0, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return 0, true, nil - } - if c.GetType().Hybrid() || dt.Kind() == types.KindBinaryLiteral || dt.Kind() == types.KindString { - res, err := dt.ToFloat64(ctx.GetSessionVars().StmtCtx) - return res, false, err - } - return dt.GetFloat64(), false, nil -} - -// EvalString returns string representation of Constant. -func (c *Constant) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return "", false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return "", true, nil - } - res, err := dt.ToString() - return res, false, err -} - -// EvalDecimal returns decimal representation of Constant. -func (c *Constant) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return nil, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return nil, true, nil - } - res, err := dt.ToDecimal(ctx.GetSessionVars().StmtCtx) - if err != nil { - return nil, false, err - } - if err := c.adjustDecimal(res); err != nil { - return nil, false, err - } - return res, false, nil -} - -func (c *Constant) adjustDecimal(d *types.MyDecimal) error { - // Decimal Value's precision and frac may be modified during plan building. - _, frac := d.PrecisionAndFrac() - if frac < c.GetType().GetDecimal() { - return d.Round(d, c.GetType().GetDecimal(), types.ModeHalfUp) - } - return nil -} - -// EvalTime returns DATE/DATETIME/TIMESTAMP representation of Constant. -func (c *Constant) EvalTime(ctx sessionctx.Context, row chunk.Row) (val types.Time, isNull bool, err error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return types.ZeroTime, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return types.ZeroTime, true, nil - } - return dt.GetMysqlTime(), false, nil -} - -// EvalDuration returns Duration representation of Constant. -func (c *Constant) EvalDuration(ctx sessionctx.Context, row chunk.Row) (val types.Duration, isNull bool, err error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return types.Duration{}, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return types.Duration{}, true, nil - } - return dt.GetMysqlDuration(), false, nil -} - -// EvalJSON returns JSON representation of Constant. -func (c *Constant) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { - dt, lazy, err := c.getLazyDatum(row) - if err != nil { - return types.BinaryJSON{}, false, err - } - if !lazy { - dt = c.Value - } - if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { - return types.BinaryJSON{}, true, nil - } - return dt.GetMysqlJSON(), false, nil -} - -// Equal implements Expression interface. -func (c *Constant) Equal(ctx sessionctx.Context, b Expression) bool { - y, ok := b.(*Constant) - if !ok { - return false - } - _, err1 := y.Eval(chunk.Row{}) - _, err2 := c.Eval(chunk.Row{}) - if err1 != nil || err2 != nil { - return false - } - con, err := c.Value.Compare(ctx.GetSessionVars().StmtCtx, &y.Value, collate.GetBinaryCollator()) - if err != nil || con != 0 { - return false - } - return true -} - -// IsCorrelated implements Expression interface. -func (c *Constant) IsCorrelated() bool { - return false -} - -// ConstItem implements Expression interface. -func (c *Constant) ConstItem(sc *stmtctx.StatementContext) bool { - return !sc.UseCache || (c.DeferredExpr == nil && c.ParamMarker == nil) -} - -// Decorrelate implements Expression interface. -func (c *Constant) Decorrelate(_ *Schema) Expression { - return c -} - -// HashCode implements Expression interface. -func (c *Constant) HashCode(sc *stmtctx.StatementContext) []byte { - if len(c.hashcode) > 0 { - return c.hashcode - } - - if c.DeferredExpr != nil { - c.hashcode = c.DeferredExpr.HashCode(sc) - return c.hashcode - } - - if c.ParamMarker != nil { - c.hashcode = append(c.hashcode, parameterFlag) - c.hashcode = codec.EncodeInt(c.hashcode, int64(c.ParamMarker.order)) - return c.hashcode - } - - _, err := c.Eval(chunk.Row{}) - if err != nil { - terror.Log(err) - } - c.hashcode = append(c.hashcode, constantFlag) - c.hashcode = codec.HashCode(c.hashcode, c.Value) - return c.hashcode -} - -// ResolveIndices implements Expression interface. -func (c *Constant) ResolveIndices(_ *Schema) (Expression, error) { - return c, nil -} - -func (c *Constant) resolveIndices(_ *Schema) error { - return nil -} - -// ResolveIndicesByVirtualExpr implements Expression interface. -func (c *Constant) ResolveIndicesByVirtualExpr(_ *Schema) (Expression, bool) { - return c, true -} - -func (c *Constant) resolveIndicesByVirtualExpr(_ *Schema) bool { - return true -} - -// RemapColumn remaps columns with provided mapping and returns new expression -func (c *Constant) RemapColumn(_ map[int64]*Column) (Expression, error) { - return c, nil -} - -// Vectorized returns if this expression supports vectorized evaluation. -func (c *Constant) Vectorized() bool { - if c.DeferredExpr != nil { - return c.DeferredExpr.Vectorized() - } - return true -} - -// SupportReverseEval checks whether the builtinFunc support reverse evaluation. -func (c *Constant) SupportReverseEval() bool { - if c.DeferredExpr != nil { - return c.DeferredExpr.SupportReverseEval() - } - return true -} - -// ReverseEval evaluates the only one column value with given function result. -func (c *Constant) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { - return c.Value, nil -} - -// Coercibility returns the coercibility value which is used to check collations. -func (c *Constant) Coercibility() Coercibility { - if !c.HasCoercibility() { - c.SetCoercibility(deriveCoercibilityForConstant(c)) - } - return c.collationInfo.Coercibility() -} - -const emptyConstantSize = int64(unsafe.Sizeof(Constant{})) - -// MemoryUsage return the memory usage of Constant -func (c *Constant) MemoryUsage() (sum int64) { - if c == nil { - return - } - - sum = emptyConstantSize + c.Value.MemUsage() + int64(cap(c.hashcode)) - if c.RetType != nil { - sum += c.RetType.MemoryUsage() - } - return -} diff --git a/expression/errors.go b/expression/errors.go deleted file mode 100644 index 3b5dacd8dc638..0000000000000 --- a/expression/errors.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package expression - -import ( - mysql "github.com/pingcap/tidb/errno" - pmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" -) - -// Error instances. -var ( - // All the exported errors are defined here: - ErrIncorrectParameterCount = dbterror.ClassExpression.NewStd(mysql.ErrWrongParamcountToNativeFct) - ErrDivisionByZero = dbterror.ClassExpression.NewStd(mysql.ErrDivisionByZero) - ErrRegexp = dbterror.ClassExpression.NewStd(mysql.ErrRegexp) - ErrOperandColumns = dbterror.ClassExpression.NewStd(mysql.ErrOperandColumns) - ErrCutValueGroupConcat = dbterror.ClassExpression.NewStd(mysql.ErrCutValueGroupConcat) - ErrFunctionsNoopImpl = dbterror.ClassExpression.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("function %s has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", nil)) - ErrInvalidArgumentForLogarithm = dbterror.ClassExpression.NewStd(mysql.ErrInvalidArgumentForLogarithm) - ErrIncorrectType = dbterror.ClassExpression.NewStd(mysql.ErrIncorrectType) - ErrInvalidTypeForJSON = dbterror.ClassExpression.NewStd(mysql.ErrInvalidTypeForJSON) - ErrInvalidTableSample = dbterror.ClassExpression.NewStd(mysql.ErrInvalidTableSample) - ErrInternal = dbterror.ClassOptimizer.NewStd(mysql.ErrInternal) - ErrNoDB = dbterror.ClassOptimizer.NewStd(mysql.ErrNoDB) - ErrNotSupportedYet = dbterror.ClassExpression.NewStd(mysql.ErrNotSupportedYet) - ErrInvalidJSONForFuncIndex = dbterror.ClassExpression.NewStd(mysql.ErrInvalidJSONValueForFuncIndex) - ErrDataOutOfRangeFuncIndex = dbterror.ClassExpression.NewStd(mysql.ErrDataOutOfRangeFunctionalIndex) - ErrFuncIndexDataIsTooLong = dbterror.ClassExpression.NewStd(mysql.ErrFunctionalIndexDataIsTooLong) - ErrFunctionNotExists = dbterror.ClassExpression.NewStd(mysql.ErrSpDoesNotExist) - - // All the un-exported errors are defined here: - errZlibZData = dbterror.ClassExpression.NewStd(mysql.ErrZlibZData) - errZlibZBuf = dbterror.ClassExpression.NewStd(mysql.ErrZlibZBuf) - errIncorrectArgs = dbterror.ClassExpression.NewStd(mysql.ErrWrongArguments) - errUnknownCharacterSet = dbterror.ClassExpression.NewStd(mysql.ErrUnknownCharacterSet) - errDefaultValue = dbterror.ClassExpression.NewStdErr(mysql.ErrInvalidDefault, pmysql.Message("invalid default value", nil)) - errDeprecatedSyntaxNoReplacement = dbterror.ClassExpression.NewStd(mysql.ErrWarnDeprecatedSyntaxNoReplacement) - errWarnAllowedPacketOverflowed = dbterror.ClassExpression.NewStd(mysql.ErrWarnAllowedPacketOverflowed) - errWarnOptionIgnored = dbterror.ClassExpression.NewStd(mysql.WarnOptionIgnored) - errTruncatedWrongValue = dbterror.ClassExpression.NewStd(mysql.ErrTruncatedWrongValue) - errUnknownLocale = dbterror.ClassExpression.NewStd(mysql.ErrUnknownLocale) - errNonUniq = dbterror.ClassExpression.NewStd(mysql.ErrNonUniq) - errWrongValueForType = dbterror.ClassExpression.NewStd(mysql.ErrWrongValueForType) - errUnknown = dbterror.ClassExpression.NewStd(mysql.ErrUnknown) - errSpecificAccessDenied = dbterror.ClassExpression.NewStd(mysql.ErrSpecificAccessDenied) - errUserLockDeadlock = dbterror.ClassExpression.NewStd(mysql.ErrUserLockDeadlock) - errUserLockWrongName = dbterror.ClassExpression.NewStd(mysql.ErrUserLockWrongName) - errJSONInBooleanContext = dbterror.ClassExpression.NewStd(mysql.ErrJSONInBooleanContext) - - // Sequence usage privilege check. - errSequenceAccessDenied = dbterror.ClassExpression.NewStd(mysql.ErrTableaccessDenied) - errUnsupportedJSONComparison = dbterror.ClassExpression.NewStdErr(mysql.ErrNotSupportedYet, - pmysql.Message("comparison of JSON in the LEAST and GREATEST operators", nil)) -) - -// handleInvalidTimeError reports error or warning depend on the context. -func handleInvalidTimeError(ctx sessionctx.Context, err error) error { - if err == nil || !(types.ErrWrongValue.Equal(err) || types.ErrWrongValueForType.Equal(err) || - types.ErrTruncatedWrongVal.Equal(err) || types.ErrInvalidWeekModeFormat.Equal(err) || - types.ErrDatetimeFunctionOverflow.Equal(err) || types.ErrIncorrectDatetimeValue.Equal(err)) { - return err - } - sc := ctx.GetSessionVars().StmtCtx - err = sc.HandleTruncate(err) - if ctx.GetSessionVars().StrictSQLMode && (sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt) { - return err - } - return nil -} - -// handleDivisionByZeroError reports error or warning depend on the context. -func handleDivisionByZeroError(ctx sessionctx.Context) error { - sc := ctx.GetSessionVars().StmtCtx - if sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt { - if !ctx.GetSessionVars().SQLMode.HasErrorForDivisionByZeroMode() { - return nil - } - if ctx.GetSessionVars().StrictSQLMode && !sc.DividedByZeroAsWarning { - return ErrDivisionByZero - } - } - sc.AppendWarning(ErrDivisionByZero) - return nil -} - -// handleAllowedPacketOverflowed reports error or warning depend on the context. -func handleAllowedPacketOverflowed(ctx sessionctx.Context, exprName string, maxAllowedPacketSize uint64) error { - err := errWarnAllowedPacketOverflowed.GenWithStackByArgs(exprName, maxAllowedPacketSize) - sc := ctx.GetSessionVars().StmtCtx - - // insert|update|delete ignore ... - if sc.TruncateAsWarning { - sc.AppendWarning(err) - return nil - } - - if ctx.GetSessionVars().StrictSQLMode && (sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt) { - return err - } - sc.AppendWarning(err) - return nil -} diff --git a/expression/explain.go b/expression/explain.go deleted file mode 100644 index 5a6fc6c4ac031..0000000000000 --- a/expression/explain.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "bytes" - "fmt" - "slices" - "strings" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" -) - -// ExplainInfo implements the Expression interface. -func (expr *ScalarFunction) ExplainInfo() string { - return expr.explainInfo(false) -} - -func (expr *ScalarFunction) explainInfo(normalized bool) string { - var buffer bytes.Buffer - fmt.Fprintf(&buffer, "%s(", expr.FuncName.L) - switch expr.FuncName.L { - case ast.Cast: - for _, arg := range expr.GetArgs() { - if normalized { - buffer.WriteString(arg.ExplainNormalizedInfo()) - } else { - buffer.WriteString(arg.ExplainInfo()) - } - buffer.WriteString(", ") - buffer.WriteString(expr.RetType.String()) - } - default: - for i, arg := range expr.GetArgs() { - if normalized { - buffer.WriteString(arg.ExplainNormalizedInfo()) - } else { - buffer.WriteString(arg.ExplainInfo()) - } - if i+1 < len(expr.GetArgs()) { - buffer.WriteString(", ") - } - } - } - buffer.WriteString(")") - return buffer.String() -} - -// ExplainNormalizedInfo implements the Expression interface. -func (expr *ScalarFunction) ExplainNormalizedInfo() string { - return expr.explainInfo(true) -} - -// ExplainInfo implements the Expression interface. -func (col *Column) ExplainInfo() string { - return col.String() -} - -// ExplainNormalizedInfo implements the Expression interface. -func (col *Column) ExplainNormalizedInfo() string { - if col.OrigName != "" { - return col.OrigName - } - return "?" -} - -// ExplainInfo implements the Expression interface. -func (expr *Constant) ExplainInfo() string { - dt, err := expr.Eval(chunk.Row{}) - if err != nil { - return "not recognized const vanue" - } - return expr.format(dt) -} - -// ExplainNormalizedInfo implements the Expression interface. -func (expr *Constant) ExplainNormalizedInfo() string { - return "?" -} - -func (expr *Constant) format(dt types.Datum) string { - switch dt.Kind() { - case types.KindNull: - return "NULL" - case types.KindString, types.KindBytes, types.KindMysqlEnum, types.KindMysqlSet, - types.KindMysqlJSON, types.KindBinaryLiteral, types.KindMysqlBit: - return fmt.Sprintf("\"%v\"", dt.GetValue()) - } - return fmt.Sprintf("%v", dt.GetValue()) -} - -// ExplainExpressionList generates explain information for a list of expressions. -func ExplainExpressionList(exprs []Expression, schema *Schema) string { - builder := &strings.Builder{} - for i, expr := range exprs { - switch expr.(type) { - case *Column, *CorrelatedColumn: - builder.WriteString(expr.String()) - if expr.String() != schema.Columns[i].String() { - // simple col projected again with another uniqueID without origin name. - builder.WriteString("->") - builder.WriteString(schema.Columns[i].String()) - } - case *Constant: - v := expr.String() - length := 64 - if len(v) < length { - builder.WriteString(v) - } else { - builder.WriteString(v[:length]) - fmt.Fprintf(builder, "(len:%d)", len(v)) - } - builder.WriteString("->") - builder.WriteString(schema.Columns[i].String()) - default: - builder.WriteString(expr.String()) - builder.WriteString("->") - builder.WriteString(schema.Columns[i].String()) - } - if i+1 < len(exprs) { - builder.WriteString(", ") - } - } - return builder.String() -} - -// SortedExplainExpressionList generates explain information for a list of expressions in order. -// In some scenarios, the expr's order may not be stable when executing multiple times. -// So we add a sort to make its explain result stable. -func SortedExplainExpressionList(exprs []Expression) []byte { - return sortedExplainExpressionList(exprs, false) -} - -func sortedExplainExpressionList(exprs []Expression, normalized bool) []byte { - buffer := bytes.NewBufferString("") - exprInfos := make([]string, 0, len(exprs)) - for _, expr := range exprs { - if normalized { - exprInfos = append(exprInfos, expr.ExplainNormalizedInfo()) - } else { - exprInfos = append(exprInfos, expr.ExplainInfo()) - } - } - slices.Sort(exprInfos) - for i, info := range exprInfos { - buffer.WriteString(info) - if i+1 < len(exprInfos) { - buffer.WriteString(", ") - } - } - return buffer.Bytes() -} - -// SortedExplainNormalizedExpressionList is same like SortedExplainExpressionList, but use for generating normalized information. -func SortedExplainNormalizedExpressionList(exprs []Expression) []byte { - return sortedExplainExpressionList(exprs, true) -} - -// SortedExplainNormalizedScalarFuncList is same like SortedExplainExpressionList, but use for generating normalized information. -func SortedExplainNormalizedScalarFuncList(exprs []*ScalarFunction) []byte { - expressions := make([]Expression, len(exprs)) - for i := range exprs { - expressions[i] = exprs[i] - } - return sortedExplainExpressionList(expressions, true) -} - -// ExplainColumnList generates explain information for a list of columns. -func ExplainColumnList(cols []*Column) []byte { - buffer := bytes.NewBufferString("") - for i, col := range cols { - buffer.WriteString(col.ExplainInfo()) - if i+1 < len(cols) { - buffer.WriteString(", ") - } - } - return buffer.Bytes() -} diff --git a/expression/expression_test.go b/expression/expression_test.go deleted file mode 100644 index 15e28f2e50466..0000000000000 --- a/expression/expression_test.go +++ /dev/null @@ -1,292 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/stretchr/testify/require" -) - -func TestNewValuesFunc(t *testing.T) { - ctx := createContext(t) - res := NewValuesFunc(ctx, 0, types.NewFieldType(mysql.TypeLonglong)) - require.Equal(t, "values", res.FuncName.O) - require.Equal(t, mysql.TypeLonglong, res.RetType.GetType()) - _, ok := res.Function.(*builtinValuesIntSig) - require.True(t, ok) -} - -func TestEvaluateExprWithNull(t *testing.T) { - ctx := createContext(t) - tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong, 0).add("col1", mysql.TypeLonglong, 0).build() - schema := tableInfoToSchemaForTest(tblInfo) - col0 := schema.Columns[0] - col1 := schema.Columns[1] - schema.Columns = schema.Columns[:1] - innerIfNull, err := newFunctionForTest(ctx, ast.Ifnull, col1, NewOne()) - require.NoError(t, err) - outerIfNull, err := newFunctionForTest(ctx, ast.Ifnull, col0, innerIfNull) - require.NoError(t, err) - - res := EvaluateExprWithNull(ctx, schema, outerIfNull) - require.Equal(t, "ifnull(Column#1, 1)", res.String()) - schema.Columns = append(schema.Columns, col1) - // ifnull(null, ifnull(null, 1)) - res = EvaluateExprWithNull(ctx, schema, outerIfNull) - require.True(t, res.Equal(ctx, NewOne())) -} - -func TestEvaluateExprWithNullAndParameters(t *testing.T) { - ctx := createContext(t) - tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong, 0).build() - schema := tableInfoToSchemaForTest(tblInfo) - col0 := schema.Columns[0] - - ctx.GetSessionVars().StmtCtx.UseCache = true - - // cases for parameters - ltWithoutParam, err := newFunctionForTest(ctx, ast.LT, col0, NewOne()) - require.NoError(t, err) - res := EvaluateExprWithNull(ctx, schema, ltWithoutParam) - require.True(t, res.Equal(ctx, NewNull())) // the expression is evaluated to null - param := NewOne() - param.ParamMarker = &ParamMarker{ctx: ctx, order: 0} - ctx.GetSessionVars().PlanCacheParams.Append(types.NewIntDatum(10)) - ltWithParam, err := newFunctionForTest(ctx, ast.LT, col0, param) - require.NoError(t, err) - res = EvaluateExprWithNull(ctx, schema, ltWithParam) - _, isConst := res.(*Constant) - require.True(t, isConst) // this expression is evaluated and skip-plan cache flag is set. - require.True(t, !ctx.GetSessionVars().StmtCtx.UseCache) -} - -func TestEvaluateExprWithNullNoChangeRetType(t *testing.T) { - ctx := createContext(t) - tblInfo := newTestTableBuilder("").add("col_str", mysql.TypeString, 0).build() - schema := tableInfoToSchemaForTest(tblInfo) - - castStrAsJSON := BuildCastFunction(ctx, schema.Columns[0], types.NewFieldType(mysql.TypeJSON)) - jsonConstant := &Constant{Value: types.NewDatum("123"), RetType: types.NewFieldType(mysql.TypeJSON)} - - // initially has ParseToJSONFlag - flagInCast := castStrAsJSON.(*ScalarFunction).RetType.GetFlag() - require.True(t, mysql.HasParseToJSONFlag(flagInCast)) - - // cast's ParseToJSONFlag removed by `DisableParseJSONFlag4Expr` - eq, err := newFunctionForTest(ctx, ast.EQ, jsonConstant, castStrAsJSON) - require.NoError(t, err) - flagInCast = eq.(*ScalarFunction).GetArgs()[1].(*ScalarFunction).RetType.GetFlag() - require.False(t, mysql.HasParseToJSONFlag(flagInCast)) - - // after EvaluateExprWithNull, this flag should be still false - EvaluateExprWithNull(ctx, schema, eq) - flagInCast = eq.(*ScalarFunction).GetArgs()[1].(*ScalarFunction).RetType.GetFlag() - require.False(t, mysql.HasParseToJSONFlag(flagInCast)) -} - -func TestConstant(t *testing.T) { - ctx := createContext(t) - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - require.False(t, NewZero().IsCorrelated()) - require.True(t, NewZero().ConstItem(sc)) - require.True(t, NewZero().Decorrelate(nil).Equal(ctx, NewZero())) - require.Equal(t, []byte{0x0, 0x8, 0x0}, NewZero().HashCode(sc)) - require.False(t, NewZero().Equal(ctx, NewOne())) - res, err := NewZero().MarshalJSON() - require.NoError(t, err) - require.Equal(t, []byte{0x22, 0x30, 0x22}, res) -} - -func TestIsBinaryLiteral(t *testing.T) { - col := &Column{RetType: types.NewFieldType(mysql.TypeEnum)} - require.False(t, IsBinaryLiteral(col)) - col.RetType.SetType(mysql.TypeSet) - require.False(t, IsBinaryLiteral(col)) - col.RetType.SetType(mysql.TypeBit) - require.False(t, IsBinaryLiteral(col)) - col.RetType.SetType(mysql.TypeDuration) - require.False(t, IsBinaryLiteral(col)) - - con := &Constant{RetType: types.NewFieldType(mysql.TypeVarString), Value: types.NewBinaryLiteralDatum([]byte{byte(0), byte(1)})} - require.True(t, IsBinaryLiteral(con)) - con.Value = types.NewIntDatum(1) - require.False(t, IsBinaryLiteral(col)) -} - -func TestConstItem(t *testing.T) { - ctx := createContext(t) - sf := newFunction(ast.Rand) - require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) - sf = newFunction(ast.UUID) - require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) - sf = newFunction(ast.GetParam, NewOne()) - require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) - sf = newFunction(ast.Abs, NewOne()) - require.True(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) -} - -func TestVectorizable(t *testing.T) { - exprs := make([]Expression, 0, 4) - sf := newFunction(ast.Rand) - column := &Column{ - UniqueID: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - exprs = append(exprs, sf) - exprs = append(exprs, NewOne()) - exprs = append(exprs, NewNull()) - exprs = append(exprs, column) - require.True(t, Vectorizable(exprs)) - - column0 := &Column{ - UniqueID: 1, - RetType: types.NewFieldType(mysql.TypeString), - } - column1 := &Column{ - UniqueID: 2, - RetType: types.NewFieldType(mysql.TypeString), - } - column2 := &Column{ - UniqueID: 3, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - exprs = exprs[:0] - sf = newFunction(ast.SetVar, column0, column1) - exprs = append(exprs, sf) - require.False(t, Vectorizable(exprs)) - - exprs = exprs[:0] - sf = newFunction(ast.GetVar, column0) - exprs = append(exprs, sf) - require.False(t, Vectorizable(exprs)) - - exprs = exprs[:0] - sf = newFunction(ast.NextVal, column0) - exprs = append(exprs, sf) - sf = newFunction(ast.LastVal, column0) - exprs = append(exprs, sf) - sf = newFunction(ast.SetVal, column1, column2) - exprs = append(exprs, sf) - require.False(t, Vectorizable(exprs)) -} - -type testTableBuilder struct { - tableName string - columnNames []string - tps []byte - flags []uint -} - -func newTestTableBuilder(tableName string) *testTableBuilder { - return &testTableBuilder{tableName: tableName} -} - -func (builder *testTableBuilder) add(name string, tp byte, flag uint) *testTableBuilder { - builder.columnNames = append(builder.columnNames, name) - builder.tps = append(builder.tps, tp) - builder.flags = append(builder.flags, flag) - return builder -} - -func (builder *testTableBuilder) build() *model.TableInfo { - ti := &model.TableInfo{ - ID: 1, - Name: model.NewCIStr(builder.tableName), - State: model.StatePublic, - } - for i, colName := range builder.columnNames { - tp := builder.tps[i] - fieldType := types.NewFieldType(tp) - flen, decimal := mysql.GetDefaultFieldLengthAndDecimal(tp) - fieldType.SetFlen(flen) - fieldType.SetDecimal(decimal) - charset, collate := types.DefaultCharsetForType(tp) - fieldType.SetCharset(charset) - fieldType.SetCollate(collate) - fieldType.SetFlag(builder.flags[i]) - ti.Columns = append(ti.Columns, &model.ColumnInfo{ - ID: int64(i + 1), - Name: model.NewCIStr(colName), - Offset: i, - FieldType: *fieldType, - State: model.StatePublic, - }) - } - return ti -} - -func tableInfoToSchemaForTest(tableInfo *model.TableInfo) *Schema { - columns := tableInfo.Columns - schema := NewSchema(make([]*Column, 0, len(columns))...) - for i, col := range columns { - schema.Append(&Column{ - UniqueID: int64(i), - ID: col.ID, - RetType: &col.FieldType, - }) - } - return schema -} - -func TestEvalExpr(t *testing.T) { - ctx := createContext(t) - eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} - tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} - for i := 0; i < len(tNames); i++ { - ft := eType2FieldType(eTypes[i]) - colExpr := &Column{Index: 0, RetType: ft} - input := chunk.New([]*types.FieldType{ft}, 1024, 1024) - fillColumnWithGener(eTypes[i], input, 0, nil) - colBuf := chunk.NewColumn(ft, 1024) - colBuf2 := chunk.NewColumn(ft, 1024) - var err error - require.True(t, colExpr.Vectorized()) - ctx.GetSessionVars().EnableVectorizedExpression = false - err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf) - require.NoError(t, err) - ctx.GetSessionVars().EnableVectorizedExpression = true - err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf2) - require.NoError(t, err) - for j := 0; j < 1024; j++ { - isNull := colBuf.IsNull(j) - isNull2 := colBuf2.IsNull(j) - require.Equal(t, isNull2, isNull) - if isNull { - continue - } - require.Equal(t, string(colBuf2.GetRaw(j)), string(colBuf.GetRaw(j))) - } - } -} - -func TestExpressionMemeoryUsage(t *testing.T) { - c1 := &Column{OrigName: "Origin"} - c2 := Column{OrigName: "OriginName"} - require.Greater(t, c2.MemoryUsage(), c1.MemoryUsage()) - c1 = nil - require.Equal(t, c1.MemoryUsage(), int64(0)) - - c3 := Constant{Value: types.NewIntDatum(1)} - c4 := Constant{Value: types.NewStringDatum("11")} - require.Greater(t, c4.MemoryUsage(), c3.MemoryUsage()) -} diff --git a/expression/extension.go b/expression/extension.go deleted file mode 100644 index 9ab506213d5f0..0000000000000 --- a/expression/extension.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "context" - "strings" - "sync" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sem" -) - -var extensionFuncs sync.Map - -func registerExtensionFunc(def *extension.FunctionDef) error { - if def == nil { - return errors.New("extension function def is nil") - } - - if err := def.Validate(); err != nil { - return err - } - - lowerName := strings.ToLower(def.Name) - if _, ok := funcs[lowerName]; ok { - return errors.Errorf("extension function name '%s' conflict with builtin", def.Name) - } - - class, err := newExtensionFuncClass(def) - if err != nil { - return err - } - - _, exist := extensionFuncs.LoadOrStore(lowerName, class) - if exist { - return errors.Errorf("duplicated extension function name '%s'", def.Name) - } - - return nil -} - -func removeExtensionFunc(name string) { - extensionFuncs.Delete(name) -} - -type extensionFuncClass struct { - baseFunctionClass - funcDef extension.FunctionDef - flen int -} - -func newExtensionFuncClass(def *extension.FunctionDef) (*extensionFuncClass, error) { - var flen int - switch def.EvalTp { - case types.ETString: - flen = mysql.MaxFieldVarCharLength - if def.EvalStringFunc == nil { - return nil, errors.New("eval function is nil") - } - case types.ETInt: - flen = mysql.MaxIntWidth - if def.EvalIntFunc == nil { - return nil, errors.New("eval function is nil") - } - default: - return nil, errors.Errorf("unsupported extension function ret type: '%v'", def.EvalTp) - } - - maxArgs := len(def.ArgTps) - minArgs := maxArgs - def.OptionalArgsLen - return &extensionFuncClass{ - baseFunctionClass: baseFunctionClass{def.Name, minArgs, maxArgs}, - flen: flen, - funcDef: *def, - }, nil -} - -func (c *extensionFuncClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { - if err := c.checkPrivileges(ctx); err != nil { - return nil, err - } - - if err := c.verifyArgs(args); err != nil { - return nil, err - } - bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, c.funcDef.EvalTp, c.funcDef.ArgTps[:len(args)]...) - if err != nil { - return nil, err - } - bf.tp.SetFlen(c.flen) - sig := &extensionFuncSig{context.TODO(), bf, c.funcDef} - return sig, nil -} - -func (c *extensionFuncClass) checkPrivileges(ctx sessionctx.Context) error { - fn := c.funcDef.RequireDynamicPrivileges - if fn == nil { - return nil - } - - semEnabled := sem.IsEnabled() - privs := fn(semEnabled) - if len(privs) == 0 { - return nil - } - - manager := privilege.GetPrivilegeManager(ctx) - activeRoles := ctx.GetSessionVars().ActiveRoles - - for _, priv := range privs { - if !manager.RequestDynamicVerification(activeRoles, priv, false) { - msg := priv - if !semEnabled { - msg = "SUPER or " + msg - } - return errSpecificAccessDenied.GenWithStackByArgs(msg) - } - } - - return nil -} - -var _ extension.FunctionContext = &extensionFuncSig{} - -type extensionFuncSig struct { - context.Context - baseBuiltinFunc - extension.FunctionDef -} - -func (b *extensionFuncSig) Clone() builtinFunc { - newSig := &extensionFuncSig{} - newSig.cloneFrom(&b.baseBuiltinFunc) - newSig.FunctionDef = b.FunctionDef - return newSig -} - -func (b *extensionFuncSig) evalString(row chunk.Row) (string, bool, error) { - if b.EvalTp == types.ETString { - return b.EvalStringFunc(b, row) - } - return b.baseBuiltinFunc.evalString(row) -} - -func (b *extensionFuncSig) evalInt(row chunk.Row) (int64, bool, error) { - if b.EvalTp == types.ETInt { - return b.EvalIntFunc(b, row) - } - return b.baseBuiltinFunc.evalInt(row) -} - -func (b *extensionFuncSig) EvalArgs(row chunk.Row) ([]types.Datum, error) { - if len(b.args) == 0 { - return nil, nil - } - - result := make([]types.Datum, 0, len(b.args)) - for _, arg := range b.args { - val, err := arg.Eval(row) - if err != nil { - return nil, err - } - result = append(result, val) - } - - return result, nil -} - -func (b *extensionFuncSig) ConnectionInfo() *variable.ConnectionInfo { - return b.ctx.GetSessionVars().ConnectionInfo -} - -func (b *extensionFuncSig) User() *auth.UserIdentity { - return b.ctx.GetSessionVars().User -} - -func (b *extensionFuncSig) ActiveRoles() []*auth.RoleIdentity { - return b.ctx.GetSessionVars().ActiveRoles -} - -func (b *extensionFuncSig) CurrentDB() string { - return b.ctx.GetSessionVars().CurrentDB -} - -func init() { - extension.RegisterExtensionFunc = registerExtensionFunc - extension.RemoveExtensionFunc = removeExtensionFunc -} diff --git a/expression/generator/helper/BUILD.bazel b/expression/generator/helper/BUILD.bazel deleted file mode 100644 index 92febc259de18..0000000000000 --- a/expression/generator/helper/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "helper", - srcs = ["helper.go"], - importpath = "github.com/pingcap/tidb/expression/generator/helper", - visibility = ["//visibility:public"], -) diff --git a/expression/generator/helper/helper.go b/expression/generator/helper/helper.go deleted file mode 100644 index fa76c9869f8a7..0000000000000 --- a/expression/generator/helper/helper.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package helper - -// TypeContext is the template context for each "github.com/pingcap/tidb/types".EvalType . -type TypeContext struct { - // Describe the name of "github.com/pingcap/tidb/types".ET{{ .ETName }} . - ETName string - // Describe the name of "github.com/pingcap/tidb/expression".VecExpr.VecEval{{ .TypeName }} . - TypeName string - // Describe the name of "github.com/pingcap/tidb/util/chunk".*Column.Append{{ .TypeNameInColumn }}, - // Resize{{ .TypeNameInColumn }}, Reserve{{ .TypeNameInColumn }}, Get{{ .TypeNameInColumn }} and - // {{ .TypeNameInColumn }}s. - // If undefined, it's same as TypeName. - TypeNameInColumn string - // Describe the type name in golang. - TypeNameGo string - // Same as "github.com/pingcap/tidb/util/chunk".getFixedLen() . - Fixed bool -} - -var ( - // TypeInt represents the template context of types.ETInt . - TypeInt = TypeContext{ETName: "Int", TypeName: "Int", TypeNameInColumn: "Int64", TypeNameGo: "int64", Fixed: true} - // TypeReal represents the template context of types.ETReal . - TypeReal = TypeContext{ETName: "Real", TypeName: "Real", TypeNameInColumn: "Float64", TypeNameGo: "float64", Fixed: true} - // TypeDecimal represents the template context of types.ETDecimal . - TypeDecimal = TypeContext{ETName: "Decimal", TypeName: "Decimal", TypeNameInColumn: "Decimal", TypeNameGo: "types.MyDecimal", Fixed: true} - // TypeString represents the template context of types.ETString . - TypeString = TypeContext{ETName: "String", TypeName: "String", TypeNameInColumn: "String", TypeNameGo: "string", Fixed: false} - // TypeDatetime represents the template context of types.ETDatetime . - TypeDatetime = TypeContext{ETName: "Datetime", TypeName: "Time", TypeNameInColumn: "Time", TypeNameGo: "types.Time", Fixed: true} - // TypeDuration represents the template context of types.ETDuration . - TypeDuration = TypeContext{ETName: "Duration", TypeName: "Duration", TypeNameInColumn: "GoDuration", TypeNameGo: "time.Duration", Fixed: true} - // TypeJSON represents the template context of types.ETJson . - TypeJSON = TypeContext{ETName: "Json", TypeName: "JSON", TypeNameInColumn: "JSON", TypeNameGo: "json.BinaryJSON", Fixed: false} -) diff --git a/expression/helper.go b/expression/helper.go deleted file mode 100644 index a659f92be0a7e..0000000000000 --- a/expression/helper.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "context" - "math" - "strings" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/logutil" - "github.com/tikv/client-go/v2/oracle" - "go.uber.org/zap" -) - -func boolToInt64(v bool) int64 { - if v { - return 1 - } - return 0 -} - -// IsValidCurrentTimestampExpr returns true if exprNode is a valid CurrentTimestamp expression. -// Here `valid` means it is consistent with the given fieldType's decimal. -func IsValidCurrentTimestampExpr(exprNode ast.ExprNode, fieldType *types.FieldType) bool { - fn, isFuncCall := exprNode.(*ast.FuncCallExpr) - if !isFuncCall || fn.FnName.L != ast.CurrentTimestamp { - return false - } - - containsArg := len(fn.Args) > 0 - // Fsp represents fractional seconds precision. - containsFsp := fieldType != nil && fieldType.GetDecimal() > 0 - var isConsistent bool - if containsArg { - v, ok := fn.Args[0].(*driver.ValueExpr) - isConsistent = ok && fieldType != nil && v.Datum.GetInt64() == int64(fieldType.GetDecimal()) - } - - return (containsArg && isConsistent) || (!containsArg && !containsFsp) -} - -// GetTimeCurrentTimestamp is used for generating a timestamp for some special cases: cast null value to timestamp type with not null flag. -func GetTimeCurrentTimestamp(ctx sessionctx.Context, tp byte, fsp int) (d types.Datum, err error) { - var t types.Time - t, err = getTimeCurrentTimeStamp(ctx, tp, fsp) - if err != nil { - return d, err - } - d.SetMysqlTime(t) - return d, nil -} - -func getTimeCurrentTimeStamp(ctx sessionctx.Context, tp byte, fsp int) (t types.Time, err error) { - value := types.NewTime(types.ZeroCoreTime, tp, fsp) - defaultTime, err := getStmtTimestamp(ctx) - if err != nil { - return value, err - } - value.SetCoreTime(types.FromGoTime(defaultTime.Truncate(time.Duration(math.Pow10(9-fsp)) * time.Nanosecond))) - if tp == mysql.TypeTimestamp || tp == mysql.TypeDatetime || tp == mysql.TypeDate { - err = value.ConvertTimeZone(time.Local, ctx.GetSessionVars().Location()) - if err != nil { - return value, err - } - } - return value, nil -} - -// GetTimeValue gets the time value with type tp. -func GetTimeValue(ctx sessionctx.Context, v interface{}, tp byte, fsp int, explicitTz *time.Location) (d types.Datum, err error) { - var value types.Time - - sc := ctx.GetSessionVars().StmtCtx - switch x := v.(type) { - case string: - lowerX := strings.ToLower(x) - if lowerX == ast.CurrentTimestamp || lowerX == ast.CurrentDate { - if value, err = getTimeCurrentTimeStamp(ctx, tp, fsp); err != nil { - return d, err - } - } else if lowerX == types.ZeroDatetimeStr { - value, err = types.ParseTimeFromNum(sc, 0, tp, fsp) - terror.Log(err) - } else { - value, err = types.ParseTime(sc, x, tp, fsp, explicitTz) - if err != nil { - return d, err - } - } - case *driver.ValueExpr: - switch x.Kind() { - case types.KindString: - value, err = types.ParseTime(sc, x.GetString(), tp, fsp, nil) - if err != nil { - return d, err - } - case types.KindInt64: - value, err = types.ParseTimeFromNum(sc, x.GetInt64(), tp, fsp) - if err != nil { - return d, err - } - case types.KindNull: - return d, nil - default: - return d, errDefaultValue - } - case *ast.FuncCallExpr: - if x.FnName.L == ast.CurrentTimestamp || x.FnName.L == ast.CurrentDate { - d.SetString(strings.ToUpper(x.FnName.L), mysql.DefaultCollationName) - return d, nil - } - return d, errDefaultValue - case *ast.UnaryOperationExpr: - // support some expression, like `-1` - v, err := EvalAstExpr(ctx, x) - if err != nil { - return d, err - } - ft := types.NewFieldType(mysql.TypeLonglong) - xval, err := v.ConvertTo(ctx.GetSessionVars().StmtCtx, ft) - if err != nil { - return d, err - } - - value, err = types.ParseTimeFromNum(sc, xval.GetInt64(), tp, fsp) - if err != nil { - return d, err - } - default: - return d, nil - } - d.SetMysqlTime(value) - return d, nil -} - -// if timestamp session variable set, use session variable as current time, otherwise use cached time -// during one sql statement, the "current_time" should be the same -func getStmtTimestamp(ctx sessionctx.Context) (time.Time, error) { - failpoint.Inject("injectNow", func(val failpoint.Value) { - v := time.Unix(int64(val.(int)), 0) - failpoint.Return(v, nil) - }) - - if ctx != nil { - staleTSO, err := ctx.GetSessionVars().StmtCtx.GetStaleTSO() - if staleTSO != 0 && err == nil { - return oracle.GetTimeFromTS(staleTSO), nil - } else if err != nil { - logutil.BgLogger().Error("get stale tso failed", zap.Error(err)) - } - } - - now := time.Now() - - if ctx == nil { - return now, nil - } - - sessionVars := ctx.GetSessionVars() - timestampStr, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), "timestamp") - if err != nil { - return now, err - } - - timestamp, err := types.StrToFloat(sessionVars.StmtCtx, timestampStr, false) - if err != nil { - return time.Time{}, err - } - seconds, fractionalSeconds := math.Modf(timestamp) - return time.Unix(int64(seconds), int64(fractionalSeconds*float64(time.Second))), nil -} diff --git a/expression/helper_test.go b/expression/helper_test.go deleted file mode 100644 index 4c9a5e3eda3b5..0000000000000 --- a/expression/helper_test.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "fmt" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestGetTimeValue(t *testing.T) { - ctx := mock.NewContext() - v, err := GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - - require.Equal(t, types.KindMysqlTime, v.Kind()) - timeValue := v.GetMysqlTime() - require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) - - sessionVars := ctx.GetSessionVars() - err = sessionVars.SetSystemVar("timestamp", "0") - require.NoError(t, err) - v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - - require.Equal(t, types.KindMysqlTime, v.Kind()) - timeValue = v.GetMysqlTime() - require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) - - err = sessionVars.SetSystemVar("timestamp", "0") - require.NoError(t, err) - v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - - require.Equal(t, types.KindMysqlTime, v.Kind()) - timeValue = v.GetMysqlTime() - require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) - - err = sessionVars.SetSystemVar("timestamp", "") - require.Error(t, err, "Incorrect argument type to variable 'timestamp'") - v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - - require.Equal(t, types.KindMysqlTime, v.Kind()) - timeValue = v.GetMysqlTime() - require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) - - // trigger the stmt context cache. - err = sessionVars.SetSystemVar("timestamp", "0") - require.NoError(t, err) - - v1, err := GetTimeCurrentTimestamp(ctx, mysql.TypeTimestamp, types.MinFsp) - require.NoError(t, err) - - v2, err := GetTimeCurrentTimestamp(ctx, mysql.TypeTimestamp, types.MinFsp) - require.NoError(t, err) - - require.Equal(t, v1, v2) - - err = sessionVars.SetSystemVar("timestamp", "1234") - require.NoError(t, err) - - tbls := []struct { - Expr interface{} - Ret interface{} - }{ - {"2012-12-12 00:00:00", "2012-12-12 00:00:00"}, - {ast.CurrentTimestamp, time.Unix(1234, 0).Format(types.TimeFormat)}, - {types.ZeroDatetimeStr, "0000-00-00 00:00:00"}, - {ast.NewValueExpr("2012-12-12 00:00:00", charset.CharsetUTF8MB4, charset.CollationUTF8MB4), "2012-12-12 00:00:00"}, - {ast.NewValueExpr(int64(0), "", ""), "0000-00-00 00:00:00"}, - {ast.NewValueExpr(nil, "", ""), nil}, - {&ast.FuncCallExpr{FnName: model.NewCIStr(ast.CurrentTimestamp)}, strings.ToUpper(ast.CurrentTimestamp)}, - // {&ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(int64(0))}, "0000-00-00 00:00:00"}, - } - - for i, tbl := range tbls { - comment := fmt.Sprintf("expr: %d", i) - v, err := GetTimeValue(ctx, tbl.Expr, mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - - switch v.Kind() { - case types.KindMysqlTime: - require.EqualValues(t, tbl.Ret, v.GetMysqlTime().String(), comment) - default: - require.EqualValues(t, tbl.Ret, v.GetValue(), comment) - } - } - - errTbl := []struct { - Expr interface{} - }{ - {"2012-13-12 00:00:00"}, - {ast.NewValueExpr("2012-13-12 00:00:00", charset.CharsetUTF8MB4, charset.CollationUTF8MB4)}, - {ast.NewValueExpr(int64(1), "", "")}, - {&ast.FuncCallExpr{FnName: model.NewCIStr("xxx")}}, - // {&ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(int64(1))}}, - } - - for _, tbl := range errTbl { - _, err := GetTimeValue(ctx, tbl.Expr, mysql.TypeTimestamp, types.MinFsp, nil) - require.Error(t, err) - } -} - -func TestIsCurrentTimestampExpr(t *testing.T) { - buildTimestampFuncCallExpr := func(i int64) *ast.FuncCallExpr { - var args []ast.ExprNode - if i != 0 { - args = []ast.ExprNode{&driver.ValueExpr{Datum: types.NewIntDatum(i)}} - } - return &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: args} - } - - v := IsValidCurrentTimestampExpr(ast.NewValueExpr("abc", charset.CharsetUTF8MB4, charset.CollationUTF8MB4), nil) - require.False(t, v) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), nil) - require.True(t, v) - ft := &types.FieldType{} - ft.SetDecimal(3) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(3), ft) - require.True(t, v) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(1), ft) - require.False(t, v) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), ft) - require.False(t, v) - - ft1 := &types.FieldType{} - ft1.SetDecimal(0) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), ft1) - require.False(t, v) - v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), nil) - require.False(t, v) -} - -func TestCurrentTimestampTimeZone(t *testing.T) { - ctx := mock.NewContext() - sessionVars := ctx.GetSessionVars() - - err := sessionVars.SetSystemVar("timestamp", "1234") - require.NoError(t, err) - err = sessionVars.SetSystemVar("time_zone", "+00:00") - require.NoError(t, err) - v, err := GetTimeValue(ctx, ast.CurrentTimestamp, mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - require.EqualValues(t, types.NewTime( - types.FromDate(1970, 1, 1, 0, 20, 34, 0), - mysql.TypeTimestamp, types.DefaultFsp), - v.GetMysqlTime()) - - // CurrentTimestamp from "timestamp" session variable is based on UTC, so change timezone - // would get different value. - err = sessionVars.SetSystemVar("time_zone", "+08:00") - require.NoError(t, err) - v, err = GetTimeValue(ctx, ast.CurrentTimestamp, mysql.TypeTimestamp, types.MinFsp, nil) - require.NoError(t, err) - require.EqualValues(t, types.NewTime( - types.FromDate(1970, 1, 1, 8, 20, 34, 0), - mysql.TypeTimestamp, types.DefaultFsp), - v.GetMysqlTime()) -} diff --git a/expression/integration_test/BUILD.bazel b/expression/integration_test/BUILD.bazel deleted file mode 100644 index c966c65cda3c8..0000000000000 --- a/expression/integration_test/BUILD.bazel +++ /dev/null @@ -1,44 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "integration_test_test", - timeout = "short", - srcs = [ - "integration_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 28, - deps = [ - "//config", - "//domain", - "//errno", - "//expression", - "//kv", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//session", - "//sessionctx/variable", - "//table", - "//tablecodec", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/collate", - "//util/sem", - "//util/timeutil", - "//util/versioninfo", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/expression/integration_test/integration_test.go b/expression/integration_test/integration_test.go deleted file mode 100644 index 682706d5e21a2..0000000000000 --- a/expression/integration_test/integration_test.go +++ /dev/null @@ -1,3273 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package integration_test - -import ( - "bytes" - "context" - "encoding/binary" - "encoding/hex" - "fmt" - "hash/crc32" - "math" - "math/rand" - "sort" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/versioninfo" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" -) - -func TestGetLock(t *testing.T) { - ctx := context.Background() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // Increase pessimistic txn max retry count to make test more stable. - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - newCfg.PessimisticTxn.MaxRetryCount = 2048 - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - }() - - // No timeout specified - err := tk.ExecToErr("SELECT get_lock('testlock')") - require.Error(t, err) - terr := errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(mysql.ErrWrongParamcountToNativeFct), terr.Code()) - - // 0 timeout = immediate - // Negative timeout = convert to max value - tk.MustQuery("SELECT get_lock('testlock1', 0)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT get_lock('testlock2', -10)").Check(testkit.Rows("1")) - // show warnings: - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect get_lock value: '-10'")) - tk.MustQuery("SELECT release_lock('testlock1'), release_lock('testlock2')").Check(testkit.Rows("1 1")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) - - // GetLock/ReleaseLock with NULL name or '' name - rs, _ := tk.Exec("SELECT get_lock('', 10)") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - rs, _ = tk.Exec("SELECT get_lock(NULL, 10)") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - rs, _ = tk.Exec("SELECT release_lock('')") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - rs, _ = tk.Exec("SELECT release_lock(NULL)") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - // NULL timeout is fine (= unlimited) - tk.MustQuery("SELECT get_lock('aaa', NULL)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('aaa')").Check(testkit.Rows("1")) - - // GetLock in CAPS, release lock in different case. - tk.MustQuery("SELECT get_lock('aBC', -10)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('AbC')").Check(testkit.Rows("1")) - - // Release unacquired LOCK and previously released lock - tk.MustQuery("SELECT release_lock('randombytes')").Check(testkit.Rows("0")) - tk.MustQuery("SELECT release_lock('abc')").Check(testkit.Rows("0")) - - // GetLock with integer name, 64, character name. - tk.MustQuery("SELECT get_lock(1234, 10)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT get_lock(REPEAT('a', 64), 10)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock(1234), release_lock(REPEAT('aa', 32))").Check(testkit.Rows("1 1")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) - - // 65 character name - rs, _ = tk.Exec("SELECT get_lock(REPEAT('a', 65), 10)") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - rs, _ = tk.Exec("SELECT release_lock(REPEAT('a', 65))") - _, err = session.GetRows4Test(ctx, tk.Session(), rs) - require.Error(t, err) - terr = errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) - - // len should be based on character length, not byte length - // accented a character = 66 bytes but only 33 chars - tk.MustQuery("SELECT get_lock(REPEAT(unhex('C3A4'), 33), 10)") - tk.MustQuery("SELECT release_lock(REPEAT(unhex('C3A4'), 33))") - - // Floating point timeout. - tk.MustQuery("SELECT get_lock('nnn', 1.2)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('nnn')").Check(testkit.Rows("1")) - - // Multiple locks acquired in one statement. - // Release all locks and one not held lock - tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) - tk.MustQuery("SELECT release_lock('a1'),release_lock('a2'),release_lock('a3'), release_lock('random'), release_lock('a4')").Check(testkit.Rows("1 1 1 0 1")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) - - // Multiple locks acquired, released all at once. - tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("4")) - tk.MustQuery("SELECT release_lock('a1')").Check(testkit.Rows("0")) // lock is free - - // Multiple locks acquired, reference count increased, released all at once. - tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) - tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a5', 1.2)").Check(testkit.Rows("1 1 1")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("7")) // 7 not 5, because the it includes ref count - tk.MustQuery("SELECT release_lock('a1')").Check(testkit.Rows("0")) // lock is free - tk.MustQuery("SELECT release_lock('a5')").Check(testkit.Rows("0")) // lock is free - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) - - // Test common cases: - // Get a lock, release it immediately. - // Try to release it again (its released) - tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) - - // Get a lock, acquire it again, release it twice. - tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) - - // Test someone else has the lock with short timeout. - tk2 := testkit.NewTestKit(t, store) - tk2.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) - tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("0")) // someone else has the lock - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) // never had the lock - // try again - tk.MustQuery("SELECT get_lock('mygloballock', 0)").Check(testkit.Rows("0")) // someone else has the lock - tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) // never had the lock - // release it - tk2.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) // works - - // Confirm all locks are released - tk2.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) - tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) -} - -func TestInfoBuiltin(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // for last_insert_id - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int auto_increment, a int, PRIMARY KEY (id))") - tk.MustExec("insert into t(a) values(1)") - result := tk.MustQuery("select last_insert_id();") - result.Check(testkit.Rows("1")) - tk.MustExec("insert into t values(2, 1)") - result = tk.MustQuery("select last_insert_id();") - result.Check(testkit.Rows("1")) - tk.MustExec("insert into t(a) values(1)") - result = tk.MustQuery("select last_insert_id();") - result.Check(testkit.Rows("3")) - - result = tk.MustQuery("select last_insert_id(5);") - result.Check(testkit.Rows("5")) - result = tk.MustQuery("select last_insert_id();") - result.Check(testkit.Rows("5")) - - // for found_rows - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int)") - tk.MustQuery("select * from t") // Test XSelectTableExec - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("1")) // Last query is found_rows(), it returns 1 row with value 0 - tk.MustExec("insert t values (1),(2),(2)") - tk.MustQuery("select * from t") - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("3")) - tk.MustQuery("select * from t where a = 0") - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("0")) - tk.MustQuery("select * from t where a = 1") - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("1")) - tk.MustQuery("select * from t where a like '2'") // Test SelectionExec - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("2")) - tk.MustQuery("show tables like 't'") - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("1")) - tk.MustQuery("select count(*) from t") // Test ProjectionExec - result = tk.MustQuery("select found_rows()") - result.Check(testkit.Rows("1")) - - // for database - result = tk.MustQuery("select database()") - result.Check(testkit.Rows("test")) - tk.MustExec("drop database test") - result = tk.MustQuery("select database()") - result.Check(testkit.Rows("")) - tk.MustExec("create database test") - tk.MustExec("use test") - - // for current_user - sessionVars := tk.Session().GetSessionVars() - originUser := sessionVars.User - sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "127.0.%%"} - result = tk.MustQuery("select current_user()") - result.Check(testkit.Rows("root@127.0.%%")) - sessionVars.User = originUser - - // for user - sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "127.0.%%"} - result = tk.MustQuery("select user()") - result.Check(testkit.Rows("root@localhost")) - sessionVars.User = originUser - - // for connection_id - originConnectionID := sessionVars.ConnectionID - sessionVars.ConnectionID = uint64(1) - result = tk.MustQuery("select connection_id()") - result.Check(testkit.Rows("1")) - sessionVars.ConnectionID = originConnectionID - - // for version - result = tk.MustQuery("select version()") - result.Check(testkit.Rows(mysql.ServerVersion)) - - // for tidb_version - result = tk.MustQuery("select tidb_version()") - tidbVersionResult := "" - for _, line := range result.Rows() { - tidbVersionResult += fmt.Sprint(line) - } - lines := strings.Split(tidbVersionResult, "\n") - assert.Equal(t, true, strings.Split(lines[0], " ")[2] == mysql.TiDBReleaseVersion, "errors in 'select tidb_version()'") - assert.Equal(t, true, strings.Split(lines[1], " ")[1] == versioninfo.TiDBEdition, "errors in 'select tidb_version()'") - - // for row_count - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, PRIMARY KEY (a))") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("0")) - tk.MustExec("insert into t(a, b) values(1, 11), (2, 22), (3, 33)") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("3")) - tk.MustExec("select * from t") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("-1")) - tk.MustExec("update t set b=22 where a=1") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("1")) - tk.MustExec("update t set b=22 where a=1") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("0")) - tk.MustExec("delete from t where a=2") - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("1")) - result = tk.MustQuery("select row_count();") - result.Check(testkit.Rows("-1")) - - // for benchmark - success := testkit.Rows("0") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int)") - result = tk.MustQuery(`select benchmark(3, benchmark(2, length("abc")))`) - result.Check(success) - err := tk.ExecToErr(`select benchmark(3, length("a", "b"))`) - require.Error(t, err) - // Quoted from https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_benchmark - // Although the expression can be a subquery, it must return a single column and at most a single row. - // For example, BENCHMARK(10, (SELECT * FROM t)) will fail if the table t has more than one column or - // more than one row. - oneColumnQuery := "select benchmark(10, (select a from t))" - twoColumnQuery := "select benchmark(10, (select * from t))" - // rows * columns: - // 0 * 1, success; - result = tk.MustQuery(oneColumnQuery) - result.Check(success) - // 0 * 2, error; - err = tk.ExecToErr(twoColumnQuery) - require.Error(t, err) - // 1 * 1, success; - tk.MustExec("insert t values (1, 2)") - result = tk.MustQuery(oneColumnQuery) - result.Check(success) - // 1 * 2, error; - err = tk.ExecToErr(twoColumnQuery) - require.Error(t, err) - // 2 * 1, error; - tk.MustExec("insert t values (3, 4)") - err = tk.ExecToErr(oneColumnQuery) - require.Error(t, err) - // 2 * 2, error. - err = tk.ExecToErr(twoColumnQuery) - require.Error(t, err) - - result = tk.MustQuery("select tidb_is_ddl_owner()") - var ret int64 - if tk.Session().IsDDLOwner() { - ret = 1 - } - result.Check(testkit.Rows(fmt.Sprintf("%v", ret))) -} - -func TestColumnInfoModified(t *testing.T) { - store := testkit.CreateMockStore(t) - - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists tab0") - testKit.MustExec("CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)") - testKit.MustExec("SELECT + - (- CASE + col0 WHEN + CAST( col0 AS SIGNED ) THEN col1 WHEN 79 THEN NULL WHEN + - col1 THEN col0 / + col0 END ) * - 16 FROM tab0") - ctx := testKit.Session() - is := domain.GetDomain(ctx).InfoSchema() - tbl, _ := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tab0")) - col := table.FindCol(tbl.Cols(), "col1") - require.Equal(t, mysql.TypeLong, col.GetType()) -} - -func TestFilterExtractFromDNF(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int)") - - tests := []struct { - exprStr string - result string - }{ - { - exprStr: "a = 1 or a = 1 or a = 1", - result: "[eq(test.t.a, 1)]", - }, - { - exprStr: "a = 1 or a = 1 or (a = 1 and b = 1)", - result: "[eq(test.t.a, 1)]", - }, - { - exprStr: "(a = 1 and a = 1) or a = 1 or b = 1", - result: "[or(or(and(eq(test.t.a, 1), eq(test.t.a, 1)), eq(test.t.a, 1)), eq(test.t.b, 1))]", - }, - { - exprStr: "(a = 1 and b = 2) or (a = 1 and b = 3) or (a = 1 and b = 4)", - result: "[eq(test.t.a, 1) or(eq(test.t.b, 2), or(eq(test.t.b, 3), eq(test.t.b, 4)))]", - }, - { - exprStr: "(a = 1 and b = 1 and c = 1) or (a = 1 and b = 1) or (a = 1 and b = 1 and c > 2 and c < 3)", - result: "[eq(test.t.a, 1) eq(test.t.b, 1)]", - }, - } - - ctx := context.Background() - for _, tt := range tests { - sql := "select * from t where " + tt.exprStr - sctx := tk.Session() - sc := sctx.GetSessionVars().StmtCtx - stmts, err := session.Parse(sctx, sql) - require.NoError(t, err, "error %v, for expr %s", err, tt.exprStr) - require.Len(t, stmts, 1) - ret := &plannercore.PreprocessorReturn{} - err = plannercore.Preprocess(context.Background(), sctx, stmts[0], plannercore.WithPreprocessorReturn(ret)) - require.NoError(t, err, "error %v, for resolve name, expr %s", err, tt.exprStr) - p, _, err := plannercore.BuildLogicalPlanForTest(ctx, sctx, stmts[0], ret.InfoSchema) - require.NoError(t, err, "error %v, for build plan, expr %s", err, tt.exprStr) - selection := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) - conds := make([]expression.Expression, len(selection.Conditions)) - for i, cond := range selection.Conditions { - conds[i] = expression.PushDownNot(sctx, cond) - } - afterFunc := expression.ExtractFiltersFromDNFs(sctx, conds) - sort.Slice(afterFunc, func(i, j int) bool { - return bytes.Compare(afterFunc[i].HashCode(sc), afterFunc[j].HashCode(sc)) < 0 - }) - require.Equal(t, fmt.Sprintf("%s", afterFunc), tt.result, "wrong result for expr: %s", tt.exprStr) - } -} - -func TestTiDBDecodeKeyFunc(t *testing.T) { - store := testkit.CreateMockStore(t) - - collate.SetNewCollationEnabledForTest(false) - defer collate.SetNewCollationEnabledForTest(true) - - tk := testkit.NewTestKit(t, store) - var result *testkit.Result - - // Row Keys - result = tk.MustQuery("select tidb_decode_key( '74800000000000002B5F72800000000000A5D3' )") - result.Check(testkit.Rows(`{"_tidb_rowid":42451,"table_id":"43"}`)) - result = tk.MustQuery("select tidb_decode_key( '74800000000000ffff5f7205bff199999999999a013131000000000000f9' )") - result.Check(testkit.Rows(`{"handle":"{1.1, 11}","table_id":65535}`)) - - // Index Keys - result = tk.MustQuery("select tidb_decode_key( '74800000000000019B5F698000000000000001015257303100000000FB013736383232313130FF3900000000000000F8010000000000000000F7' )") - result.Check(testkit.Rows(`{"index_id":1,"index_vals":"RW01, 768221109, ","table_id":411}`)) - result = tk.MustQuery("select tidb_decode_key( '7480000000000000695F698000000000000001038000000000004E20' )") - result.Check(testkit.Rows(`{"index_id":1,"index_vals":"20000","table_id":105}`)) - - // Table keys - result = tk.MustQuery("select tidb_decode_key( '7480000000000000FF4700000000000000F8' )") - result.Check(testkit.Rows(`{"table_id":71}`)) - - // Test invalid record/index key. - result = tk.MustQuery("select tidb_decode_key( '7480000000000000FF2E5F728000000011FFE1A3000000000000' )") - result.Check(testkit.Rows("7480000000000000FF2E5F728000000011FFE1A3000000000000")) - warns := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Len(t, warns, 1) - require.EqualError(t, warns[0].Err, "invalid key: 7480000000000000FF2E5F728000000011FFE1A3000000000000") - - // Test in real tables. - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a varchar(255), b int, c datetime, primary key (a, b, c));") - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - getTime := func(year, month, day int, timeType byte) types.Time { - ret := types.NewTime(types.FromDate(year, month, day, 0, 0, 0, 0), timeType, types.DefaultFsp) - return ret - } - buildCommonKeyFromData := func(tableID int64, data []types.Datum) string { - k, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, data...) - require.NoError(t, err) - h, err := kv.NewCommonHandle(k) - require.NoError(t, err) - k = tablecodec.EncodeRowKeyWithHandle(tableID, h) - return hex.EncodeToString(codec.EncodeBytes(nil, k)) - } - // split table t by ('bbbb', 10, '2020-01-01'); - data := []types.Datum{types.NewStringDatum("bbbb"), types.NewIntDatum(10), types.NewTimeDatum(getTime(2020, 1, 1, mysql.TypeDatetime))} - hexKey := buildCommonKeyFromData(tbl.Meta().ID, data) - sql := fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs := fmt.Sprintf(`{"handle":{"a":"bbbb","b":"10","c":"2020-01-01 00:00:00"},"table_id":%d}`, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) - - // split table t by ('bbbb', 10, null); - data = []types.Datum{types.NewStringDatum("bbbb"), types.NewIntDatum(10), types.NewDatum(nil)} - hexKey = buildCommonKeyFromData(tbl.Meta().ID, data) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - tk.MustQuery(sql).Check(testkit.Rows(hexKey)) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a varchar(255), b int, c datetime, index idx(a, b, c));") - dom = domain.GetDomain(tk.Session()) - is = dom.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - buildIndexKeyFromData := func(tableID, indexID int64, data []types.Datum) string { - k, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, data...) - require.NoError(t, err) - k = tablecodec.EncodeIndexSeekKey(tableID, indexID, k) - return hex.EncodeToString(codec.EncodeBytes(nil, k)) - } - // split table t index idx by ('aaaaa', 100, '2000-01-01'); - data = []types.Datum{types.NewStringDatum("aaaaa"), types.NewIntDatum(100), types.NewTimeDatum(getTime(2000, 1, 1, mysql.TypeDatetime))} - hexKey = buildIndexKeyFromData(tbl.Meta().ID, tbl.Indices()[0].Meta().ID, data) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - result = tk.MustQuery(sql) - rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"a":"aaaaa","b":"100","c":"2000-01-01 00:00:00"},"table_id":%d}`, tbl.Meta().ID) - result.Check(testkit.Rows(rs)) - // split table t index idx by (null, null, null); - data = []types.Datum{types.NewDatum(nil), types.NewDatum(nil), types.NewDatum(nil)} - hexKey = buildIndexKeyFromData(tbl.Meta().ID, tbl.Indices()[0].Meta().ID, data) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - result = tk.MustQuery(sql) - rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"a":null,"b":null,"c":null},"table_id":%d}`, tbl.Meta().ID) - result.Check(testkit.Rows(rs)) - - // https://github.com/pingcap/tidb/issues/27434. - hexKey = "7480000000000100375F69800000000000000103800000000001D4C1023B6458" - sql = fmt.Sprintf("select tidb_decode_key('%s')", hexKey) - tk.MustQuery(sql).Check(testkit.Rows(hexKey)) - - // https://github.com/pingcap/tidb/issues/33015. - hexKey = "74800000000000012B5F72800000000000A5D3" - sql = fmt.Sprintf("select tidb_decode_key('%s')", hexKey) - tk.MustQuery(sql).Check(testkit.Rows(`{"_tidb_rowid":42451,"table_id":"299"}`)) - - // Test the table with the nonclustered index. - const rowID = 10 - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int primary key nonclustered, b int, key bk (b));") - dom = domain.GetDomain(tk.Session()) - is = dom.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - buildTableRowKey := func(tableID, rowID int64) string { - return hex.EncodeToString( - codec.EncodeBytes( - nil, - tablecodec.EncodeRowKeyWithHandle(tableID, kv.IntHandle(rowID)), - )) - } - hexKey = buildTableRowKey(tbl.Meta().ID, rowID) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs = fmt.Sprintf(`{"_tidb_rowid":%d,"table_id":"%d"}`, rowID, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) - - // Test the table with the clustered index. - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int primary key clustered, b int, key bk (b));") - dom = domain.GetDomain(tk.Session()) - is = dom.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - hexKey = buildTableRowKey(tbl.Meta().ID, rowID) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs = fmt.Sprintf(`{"%s":%d,"table_id":"%d"}`, tbl.Meta().GetPkName().String(), rowID, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) - - // Test partition table. - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int primary key clustered, b int, key bk (b)) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (1), PARTITION p1 VALUES LESS THAN (2));") - dom = domain.GetDomain(tk.Session()) - is = dom.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - require.NotNil(t, tbl.Meta().Partition) - hexKey = buildTableRowKey(tbl.Meta().Partition.Definitions[0].ID, rowID) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs = fmt.Sprintf(`{"%s":%d,"partition_id":%d,"table_id":"%d"}`, tbl.Meta().GetPkName().String(), rowID, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) - - hexKey = tablecodec.EncodeTablePrefix(tbl.Meta().Partition.Definitions[0].ID).String() - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs = fmt.Sprintf(`{"partition_id":%d,"table_id":%d}`, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) - - data = []types.Datum{types.NewIntDatum(100)} - hexKey = buildIndexKeyFromData(tbl.Meta().Partition.Definitions[0].ID, tbl.Indices()[0].Meta().ID, data) - sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) - rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"b":"100"},"partition_id":%d,"table_id":%d}`, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) - tk.MustQuery(sql).Check(testkit.Rows(rs)) -} - -func TestIssue9710(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - getSAndMS := func(str string) (int, int) { - results := strings.Split(str, ":") - SAndMS := strings.Split(results[len(results)-1], ".") - var s, ms int - s, _ = strconv.Atoi(SAndMS[0]) - if len(SAndMS) > 1 { - ms, _ = strconv.Atoi(SAndMS[1]) - } - return s, ms - } - - for { - rs := tk.MustQuery("select now(), now(6), unix_timestamp(), unix_timestamp(now())") - s, ms := getSAndMS(rs.Rows()[0][1].(string)) - if ms < 500000 { - time.Sleep(time.Second / 10) - continue - } - - s1, _ := getSAndMS(rs.Rows()[0][0].(string)) - require.Equal(t, s, s1) // now() will truncate the result instead of rounding it - - require.Equal(t, rs.Rows()[0][2], rs.Rows()[0][3]) // unix_timestamp() will truncate the result - break - } -} - -func TestShardIndexOnTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int primary key clustered, a int, b int, unique key uk_expr((tidb_shard(a)),a))") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@session.tidb_enforce_mpp = 1") - rows := tk.MustQuery("explain select max(b) from t").Rows() - for _, row := range rows { - line := fmt.Sprintf("%v", row) - if strings.Contains(line, "TableFullScan") { - require.Contains(t, line, "tiflash") - } - } - tk.MustExec("set @@session.tidb_enforce_mpp = 0") - tk.MustExec("set @@session.tidb_allow_mpp = 0") - // when we isolated the read engine as 'tiflash' and banned TiDB opening allow-mpp, no suitable plan is generated. - _, err := tk.Exec("explain select max(b) from t") - require.NotNil(t, err) - require.Equal(t, err.Error(), "[planner:1815]Internal : Can't find a proper physical plan for this query") -} - -func TestExprPushdownBlacklist(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int , b date)") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("insert into mysql.expr_pushdown_blacklist " + - "values('<', 'tikv,tiflash,tidb', 'for test'),('cast', 'tiflash', 'for test'),('date_format', 'tikv', 'for test')") - tk.MustExec("admin reload expr_pushdown_blacklist") - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@session.tidb_opt_enable_late_materialization = OFF") - - // < not pushed, cast only pushed to TiKV, date_format only pushed to TiFlash, - // > pushed to both TiKV and TiFlash - rows := tk.MustQuery("explain format = 'brief' select * from test.t where b > date'1988-01-01' and b < date'1994-01-01' " + - "and cast(a as decimal(10,2)) > 10.10 and date_format(b,'%m') = '11'").Rows() - require.Equal(t, "gt(cast(test.t.a, decimal(10,2) BINARY), 10.10), lt(test.t.b, 1994-01-01)", fmt.Sprintf("%v", rows[0][4])) - require.Equal(t, "eq(date_format(test.t.b, \"%m\"), \"11\"), gt(test.t.b, 1988-01-01)", fmt.Sprintf("%v", rows[2][4])) - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tikv'") - rows = tk.MustQuery("explain format = 'brief' select * from test.t where b > date'1988-01-01' and b < date'1994-01-01' " + - "and cast(a as decimal(10,2)) > 10.10 and date_format(b,'%m') = '11'").Rows() - require.Equal(t, "eq(date_format(test.t.b, \"%m\"), \"11\"), lt(test.t.b, 1994-01-01)", fmt.Sprintf("%v", rows[0][4])) - require.Equal(t, "gt(cast(test.t.a, decimal(10,2) BINARY), 10.10), gt(test.t.b, 1988-01-01)", fmt.Sprintf("%v", rows[2][4])) - - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name = '<' and store_type = 'tikv,tiflash,tidb' and reason = 'for test'") - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name = 'date_format' and store_type = 'tikv' and reason = 'for test'") - tk.MustExec("admin reload expr_pushdown_blacklist") -} - -func TestNotExistFunc(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - // current db is empty - tk.MustGetErrMsg("SELECT xxx(1)", "[planner:1046]No database selected") - tk.MustGetErrMsg("SELECT yyy()", "[planner:1046]No database selected") - tk.MustGetErrMsg("SELECT T.upper(1)", "[expression:1305]FUNCTION t.upper does not exist") - - // current db is not empty - tk.MustExec("use test") - tk.MustGetErrMsg("SELECT xxx(1)", "[expression:1305]FUNCTION test.xxx does not exist") - tk.MustGetErrMsg("SELECT yyy()", "[expression:1305]FUNCTION test.yyy does not exist") - tk.MustGetErrMsg("SELECT t.upper(1)", "[expression:1305]FUNCTION t.upper does not exist") - tk.MustGetErrMsg("SELECT timestampliteral(rand())", "[expression:1305]FUNCTION test.timestampliteral does not exist") -} - -func TestDecodetoChunkReuse(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table chk (a int,b varchar(20))") - for i := 0; i < 200; i++ { - if i%5 == 0 { - tk.MustExec("insert chk values (NULL,NULL)") - continue - } - tk.MustExec(fmt.Sprintf("insert chk values (%d,'%s')", i, strconv.Itoa(i))) - } - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - tk.MustExec("set tidb_init_chunk_size = 2") - tk.MustExec("set tidb_max_chunk_size = 32") - defer func() { - tk.MustExec(fmt.Sprintf("set tidb_init_chunk_size = %d", variable.DefInitChunkSize)) - tk.MustExec(fmt.Sprintf("set tidb_max_chunk_size = %d", variable.DefMaxChunkSize)) - }() - rs, err := tk.Exec("select * from chk") - require.NoError(t, err) - req := rs.NewChunk(nil) - var count int - for { - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - numRows := req.NumRows() - if numRows == 0 { - break - } - for i := 0; i < numRows; i++ { - if count%5 == 0 { - require.True(t, req.GetRow(i).IsNull(0)) - require.True(t, req.GetRow(i).IsNull(1)) - } else { - require.False(t, req.GetRow(i).IsNull(0)) - require.False(t, req.GetRow(i).IsNull(1)) - require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) - require.Equal(t, strconv.Itoa(count), req.GetRow(i).GetString(1)) - } - count++ - } - } - require.Equal(t, count, 200) - rs.Close() -} - -func TestIssue16697(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (v varchar(1024))") - tk.MustExec("insert into t values (space(1024))") - for i := 0; i < 5; i++ { - tk.MustExec("insert into t select * from t") - } - rows := tk.MustQuery("explain analyze select * from t").Rows() - for _, row := range rows { - line := fmt.Sprintf("%v", row) - if strings.Contains(line, "Projection") { - require.Contains(t, line, "KB") - require.NotContains(t, line, "MB") - require.NotContains(t, line, "GB") - } - } -} - -func TestIssue19892(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test") - tk.MustExec("CREATE TABLE dd(a date, b datetime, c timestamp)") - - // check NO_ZERO_DATE - { - tk.MustExec("SET sql_mode=''") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('0000-00-00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") - tk.MustExec("UPDATE dd SET b = '0000-00-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('0000-00-00 20:00:00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-10-01 20:00:00')") - tk.MustExec("UPDATE dd SET c = '0000-00-00 20:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - - tk.MustExec("SET sql_mode='NO_ZERO_DATE'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('0000-0-00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '0000-0-00' for column 'b' at row 1")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('2000-10-01')") - tk.MustExec("UPDATE dd SET a = '0000-00-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '0000-00-00'")) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") - tk.MustExec("UPDATE dd SET c = '0000-00-00 10:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 10:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - - tk.MustExec("SET sql_mode='NO_ZERO_DATE,STRICT_TRANS_TABLES'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('0000-00-00 20:00:00')", "[table:1292]Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1") - tk.MustExec("INSERT IGNORE INTO dd(c) VALUES ('0000-00-00 20:00:00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") - tk.MustGetErrMsg("UPDATE dd SET b = '0000-00-00'", "[types:1292]Incorrect datetime value: '0000-00-00'") - tk.MustExec("UPDATE IGNORE dd SET b = '0000-00-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '0000-00-00'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") - tk.MustGetErrMsg("UPDATE dd SET c = '0000-00-00 00:00:00'", "[types:1292]Incorrect timestamp value: '0000-00-00 00:00:00'") - tk.MustExec("UPDATE IGNORE dd SET c = '0000-00-00 00:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 00:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - } - - // check NO_ZERO_IN_DATE - { - tk.MustExec("SET sql_mode=''") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('2000-01-00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00")) - tk.MustExec("INSERT INTO dd(a) values('2000-00-01')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00", "2000-00-01")) - tk.MustExec("INSERT INTO dd(a) values('0-01-02')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00", "2000-00-01", "2000-01-02")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('2000-01-02')") - tk.MustExec("UPDATE dd SET b = '2000-00-02'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-00-02 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-01-02 20:00:00')") - tk.MustExec("UPDATE dd SET c = '0000-01-02 20:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-02 20:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - - tk.MustExec("SET sql_mode='NO_ZERO_IN_DATE'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('2000-01-00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '2000-01-00' for column 'a' at row 1")) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('2000-01-02')") - tk.MustExec("UPDATE dd SET a = '2000-00-02'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '2000-00-02'")) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) - tk.MustExec("UPDATE dd SET b = '2000-01-0'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - // consistent with Mysql8 - tk.MustExec("UPDATE dd SET b = '0-01-02'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-01-02 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-01-02 20:00:00')") - tk.MustExec("UPDATE dd SET c = '2000-00-02 20:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '2000-00-02 20:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - - tk.MustExec("SET sql_mode='NO_ZERO_IN_DATE,STRICT_TRANS_TABLES'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustGetErrMsg("INSERT INTO dd(b) VALUES ('2000-01-00')", "[table:1292]Incorrect datetime value: '2000-01-00' for column 'b' at row 1") - tk.MustExec("INSERT IGNORE INTO dd(b) VALUES ('2000-00-01')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-00-01' for column 'b' at row 1")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) VALUES ('2000-01-02')") - tk.MustGetErrMsg("UPDATE dd SET b = '2000-01-00'", "[types:1292]Incorrect datetime value: '2000-01-00'") - tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-0'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - tk.MustExec("UPDATE dd SET b = '0000-1-2'") - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-01-02 00:00:00")) - tk.MustGetErrMsg("UPDATE dd SET c = '0000-01-05'", "[types:1292]Incorrect timestamp value: '0000-01-05'") - tk.MustExec("UPDATE IGNORE dd SET c = '0000-01-5'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-5'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('2000-01-00 20:00:00')", "[table:1292]Incorrect timestamp value: '2000-01-00 20:00:00' for column 'c' at row 1") - tk.MustExec("INSERT INTO dd(c) VALUES ('2000-01-02')") - tk.MustGetErrMsg("UPDATE dd SET c = '2000-01-00 20:00:00'", "[types:1292]Incorrect timestamp value: '2000-01-00 20:00:00'") - tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-00'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - } - - // check !NO_ZERO_DATE - tk.MustExec("SET sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('0000-00-00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") - tk.MustExec("UPDATE dd SET b = '0000-00-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('0000-00-00 00:00:00')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") - tk.MustExec("UPDATE dd SET c = '0000-00-00 00:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustGetErrMsg("INSERT INTO dd(b) VALUES ('2000-01-00')", "[table:1292]Incorrect datetime value: '2000-01-00' for column 'b' at row 1") - tk.MustExec("INSERT IGNORE INTO dd(b) VALUES ('2000-00-01')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-00-01' for column 'b' at row 1")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) VALUES ('2000-01-02')") - tk.MustGetErrMsg("UPDATE dd SET b = '2000-01-00'", "[types:1292]Incorrect datetime value: '2000-01-00'") - tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-0'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - tk.MustExec("UPDATE dd SET b = '0000-1-2'") - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-01-02 00:00:00")) - tk.MustGetErrMsg("UPDATE dd SET c = '0000-01-05'", "[types:1292]Incorrect timestamp value: '0000-01-05'") - tk.MustExec("UPDATE IGNORE dd SET c = '0000-01-5'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-5'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('2000-01-00 20:00:00')", "[table:1292]Incorrect timestamp value: '2000-01-00 20:00:00' for column 'c' at row 1") - tk.MustExec("INSERT INTO dd(c) VALUES ('2000-01-02')") - tk.MustGetErrMsg("UPDATE dd SET c = '2000-01-00 20:00:00'", "[types:1292]Incorrect timestamp value: '2000-01-00 20:00:00'") - tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-00'")) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - - // check !NO_ZERO_IN_DATE - tk.MustExec("SET sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") - { - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(a) values('2000-00-10')") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-00-10")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") - tk.MustExec("UPDATE dd SET b = '2000-00-10'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) - tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-00-10 00:00:00")) - - tk.MustExec("TRUNCATE TABLE dd") - tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") - tk.MustGetErrMsg("UPDATE dd SET c = '2000-00-10 00:00:00'", "[types:1292]Incorrect timestamp value: '2000-00-10 00:00:00'") - tk.MustExec("UPDATE IGNORE dd SET c = '2000-01-00 00:00:00'") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '2000-01-00 00:00:00'")) - tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) - } - tk.MustExec("drop table if exists table_20220419;") - tk.MustExec(`CREATE TABLE table_20220419 ( - id bigint(20) NOT NULL AUTO_INCREMENT, - lastLoginDate datetime NOT NULL, - PRIMARY KEY (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`) - tk.MustExec("set sql_mode='';") - tk.MustExec("insert into table_20220419 values(1,'0000-00-00 00:00:00');") - tk.MustExec("set sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';") - tk.MustGetErrMsg("insert into table_20220419(lastLoginDate) select lastLoginDate from table_20220419;", "[types:1292]Incorrect datetime value: '0000-00-00 00:00:00'") -} - -func TestIssue11333(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t(col1 decimal);") - tk.MustExec(" insert into t values(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000);") - tk.MustQuery(`select * from t;`).Check(testkit.Rows("0")) - tk.MustExec("create table t1(col1 decimal(65,30));") - tk.MustExec(" insert into t1 values(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000);") - tk.MustQuery(`select * from t1;`).Check(testkit.Rows("0.000000000000000000000000000000")) - tk.MustQuery(`select 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000000")) - tk.MustQuery(`select 0.0000000000000000000000000000000000000000000000000000000000000000000000012;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000001")) - tk.MustQuery(`select 0.000000000000000000000000000000000000000000000000000000000000000000000001;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000001")) -} - -func TestSecurityEnhancedMode(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - sem.Enable() - defer sem.Disable() - - // When SEM is enabled these features are restricted to all users - // regardless of what privileges they have available. - tk.MustGetErrMsg("SELECT 1 INTO OUTFILE '/tmp/aaaa'", "[planner:8132]Feature 'SELECT INTO' is not supported when security enhanced mode is enabled") -} - -func TestEnumIndex(t *testing.T) { - elems := []string{"\"a\"", "\"b\"", "\"c\""} - rand.Shuffle(len(elems), func(i, j int) { - elems[i], elems[j] = elems[j], elems[i] - }) - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t,tidx") - tk.MustExec("create table t(e enum(" + strings.Join(elems, ",") + "))") - tk.MustExec("create table tidx(e enum(" + strings.Join(elems, ",") + "), index idx(e))") - - nRows := 50 - values := make([]string, 0, nRows) - for i := 0; i < nRows; i++ { - values = append(values, fmt.Sprintf("(%v)", rand.Intn(len(elems))+1)) - } - tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) - tk.MustExec(fmt.Sprintf("insert into tidx values %v", strings.Join(values, ", "))) - - ops := []string{"=", "!=", ">", ">=", "<", "<="} - testElems := []string{"\"a\"", "\"b\"", "\"c\"", "\"d\"", "\"\"", "1", "2", "3", "4", "0", "-1"} - for i := 0; i < nRows; i++ { - cond := fmt.Sprintf("e" + ops[rand.Intn(len(ops))] + testElems[rand.Intn(len(testElems))]) - result := tk.MustQuery("select * from t where " + cond).Sort().Rows() - tk.MustQuery("select * from tidx where " + cond).Sort().Check(result) - } - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(e enum('d','c','b','a'), a int, index idx(e));") - tk.MustExec("insert into t values(1,1),(2,2),(3,3),(4,4);") - tk.MustQuery("select /*+ use_index(t, idx) */ * from t where e not in ('a','d') and a = 2;").Check( - testkit.Rows("c 2")) - - // issue 24419 - tk.MustExec("use test") - tk.MustExec("drop table if exists t02") - tk.MustExec("CREATE TABLE `t02` ( `COL1` enum('^YSQT0]V@9TFN>^WB6G?NG@S8>VYOM;BSC@64=ZISGS?O[JDFBI5M]QXJYQNSKU>NGAWLXS26LMTZ2YNN`XKIUGKY0IHDWV>E[BJJCABOKH1M^CB5E@DLS7Q88PWZTEAY]1ZQMN5NX[IFIYA983K:E4N77@FINM5HVGQCUCVNF5WLOOOEORAM=_JLMVFURMUASTVDBE','NL3V:J9LM4U5KUCVR;P','M5=T5FLQEZMPZAXH]4G:TSYYYVQ7O@4S6C3N8WPFKSP;SRD6VW@94BBH8XCT','P]I52Y46F?@RMOOF6;FWDTO`7FIT]R:]ELHD[CNLDSHC7FPBYOOJXLZSBV^5C^AAF6J5BCKE4V9==@H=4C]GMZXPNM','ECIQWH>?MK=ARGI0WVJNIBZFCFVJHFIUYJ:2?2WWZBNBWTPFNQPLLBFP9R_','E<_Y9OT@SOPYR72VIJVMBWIVPF@TTBZ@8ZPBZL=LXZF`WM4V2?K>AT','PZ@PR6XN28JL`B','ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9','QLDIOY[Y:JZR@OL__I^@FBO=O_?WOOR:2BE:QJC','BI^TGJ_NEEXYKV1POHTOJQPGCPVR=TYZMGWABUQR07J8U::W4','N`ZN4P@9T[JW;FR6=FA4WP@APNPG[XQVIK4]F]2>EC>JEIOXC``;;?OHP') DEFAULT NULL, `COL2` tinyint DEFAULT NULL, `COL3` time DEFAULT NULL, KEY `U_M_COL4` (`COL1`,`COL2`), KEY `U_M_COL5` (`COL3`,`COL2`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustExec("insert into t02(col1, col2) values ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 39), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 51), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 55), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', -30), ('ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9', -30);") - tk.MustQuery("select * from t02 where col1 not in (\"W1Rgd74pbJaGX47h1MPjpr0XSKJNCnwEleJ50Vbpl9EmbHJX6D6BXYKT2UAbl1uDw3ZGeYykhzG6Gld0wKdOiT4Gv5j9upHI0Q7vrXij4N9WNFJvB\", \"N`ZN4P@9T[JW;FR6=FA4WP@APNPG[XQVIK4]F]2>EC>JEIOXC``;;?OHP\") and col2 = -30;").Check( - testkit.Rows( - "OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A -30 ", - "ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9 -30 ")) - - // issue 24576 - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(col1 enum('a','b','c'), col2 enum('a','b','c'), col3 int, index idx(col1,col2));") - tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3);") - tk.MustQuery("select /*+ use_index(t,idx) */ col3 from t where col2 between 'b' and 'b' and col1 is not null;").Check( - testkit.Rows("2")) - tk.MustQuery("select /*+ use_index(t,idx) */ col3 from t where col2 = 'b' and col1 is not null;").Check( - testkit.Rows("2")) - - // issue25099 - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(e enum(\"a\",\"b\",\"c\"), index idx(e));") - tk.MustExec("insert ignore into t values(0),(1),(2),(3);") - tk.MustQuery("select * from t where e = '';").Check( - testkit.Rows("")) - tk.MustQuery("select * from t where e != 'a';").Sort().Check( - testkit.Rows("", "b", "c")) - tk.MustExec("alter table t drop index idx;") - tk.MustQuery("select * from t where e = '';").Check( - testkit.Rows("")) - tk.MustQuery("select * from t where e != 'a';").Sort().Check( - testkit.Rows("", "b", "c")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(e enum(\"\"), index idx(e));") - tk.MustExec("insert ignore into t values(0),(1);") - tk.MustQuery("select * from t where e = '';").Check( - testkit.Rows("", "")) - tk.MustExec("alter table t drop index idx;") - tk.MustQuery("select * from t where e = '';").Check( - testkit.Rows("", "")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(e enum(\"a\",\"b\",\"c\"), index idx(e));") - tk.MustExec("insert ignore into t values(0);") - tk.MustExec("select * from t t1 join t t2 on t1.e=t2.e;") - tk.MustQuery("select /*+ inl_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( - testkit.Rows(" ")) - tk.MustQuery("select /*+ hash_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( - testkit.Rows(" ")) - tk.MustQuery("select /*+ inl_hash_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( - testkit.Rows(" ")) -} - -func TestBuiltinFuncJSONMergePatch_InColumn(t *testing.T) { - ctx := context.Background() - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tests := []struct { - input [2]interface{} - expected interface{} - success bool - errCode int - }{ - // RFC 7396 document: https://datatracker.ietf.org/doc/html/rfc7396 - // RFC 7396 Example Test Cases - {[2]interface{}{`{"a":"b"}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, - {[2]interface{}{`{"a":"b"}`, `{"b":"c"}`}, `{"a": "b", "b": "c"}`, true, 0}, - {[2]interface{}{`{"a":"b"}`, `{"a":null}`}, `{}`, true, 0}, - {[2]interface{}{`{"a":"b", "b":"c"}`, `{"a":null}`}, `{"b": "c"}`, true, 0}, - {[2]interface{}{`{"a":["b"]}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, - {[2]interface{}{`{"a":"c"}`, `{"a":["b"]}`}, `{"a": ["b"]}`, true, 0}, - {[2]interface{}{`{"a":{"b":"c"}}`, `{"a":{"b":"d","c":null}}`}, `{"a": {"b": "d"}}`, true, 0}, - {[2]interface{}{`{"a":[{"b":"c"}]}`, `{"a": [1]}`}, `{"a": [1]}`, true, 0}, - {[2]interface{}{`["a","b"]`, `["c","d"]`}, `["c", "d"]`, true, 0}, - {[2]interface{}{`{"a":"b"}`, `["c"]`}, `["c"]`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `null`}, `null`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `"bar"`}, `"bar"`, true, 0}, - {[2]interface{}{`{"e":null}`, `{"a":1}`}, `{"e": null, "a": 1}`, true, 0}, - {[2]interface{}{`[1,2]`, `{"a":"b","c":null}`}, `{"a": "b"}`, true, 0}, - {[2]interface{}{`{}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a": {"bb": {}}}`, true, 0}, - // RFC 7396 Example Document - {[2]interface{}{`{"title":"Goodbye!","author":{"givenName":"John","familyName":"Doe"},"tags":["example","sample"],"content":"This will be unchanged"}`, `{"title":"Hello!","phoneNumber":"+01-123-456-7890","author":{"familyName":null},"tags":["example"]}`}, `{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}`, true, 0}, - - // From mysql Example Test Cases - {[2]interface{}{nil, `{"a":1}`}, nil, true, 0}, - {[2]interface{}{`{"a":1}`, nil}, nil, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `true`}, `true`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `false`}, `false`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `123`}, `123`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `123.1`}, `123.1`, true, 0}, - {[2]interface{}{`{"a":"foo"}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[2]interface{}{"null", `{"a":1}`}, `{"a":1}`, true, 0}, - {[2]interface{}{`{"a":1}`, "null"}, `null`, true, 0}, - - // Invalid json text - {[2]interface{}{`{"a":1}`, `[1]}`}, nil, false, mysql.ErrInvalidJSONText}, - } - - tk.MustExec(`use test;`) - tk.MustExec(`drop table if exists t;`) - tk.MustExec("CREATE TABLE t ( `id` INT NOT NULL AUTO_INCREMENT, `j` json NULL, `vc` VARCHAR ( 5000 ) NULL, PRIMARY KEY ( `id` ) );") - for id, tt := range tests { - tk.MustExec("insert into t values(?,?,?)", id+1, tt.input[0], tt.input[1]) - if tt.success { - result := tk.MustQuery("select json_merge_patch(j,vc) from t where id = ?", id+1) - if tt.expected == nil { - result.Check(testkit.Rows("")) - } else { - j, e := types.ParseBinaryJSONFromString(tt.expected.(string)) - require.NoError(t, e) - result.Check(testkit.Rows(j.String())) - } - } else { - rs, _ := tk.Exec("select json_merge_patch(j,vc) from t where id = ?;", id+1) - _, err := session.GetRows4Test(ctx, tk.Session(), rs) - terr := errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(tt.errCode), terr.Code()) - } - } -} - -func TestBuiltinFuncJSONMergePatch_InExpression(t *testing.T) { - ctx := context.Background() - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - tests := []struct { - input []interface{} - expected interface{} - success bool - errCode int - }{ - // RFC 7396 document: https://datatracker.ietf.org/doc/html/rfc7396 - // RFC 7396 Example Test Cases - {[]interface{}{`{"a":"b"}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, - {[]interface{}{`{"a":"b"}`, `{"b":"c"}`}, `{"a": "b","b": "c"}`, true, 0}, - {[]interface{}{`{"a":"b"}`, `{"a":null}`}, `{}`, true, 0}, - {[]interface{}{`{"a":"b", "b":"c"}`, `{"a":null}`}, `{"b": "c"}`, true, 0}, - {[]interface{}{`{"a":["b"]}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, - {[]interface{}{`{"a":"c"}`, `{"a":["b"]}`}, `{"a": ["b"]}`, true, 0}, - {[]interface{}{`{"a":{"b":"c"}}`, `{"a":{"b":"d","c":null}}`}, `{"a": {"b": "d"}}`, true, 0}, - {[]interface{}{`{"a":[{"b":"c"}]}`, `{"a": [1]}`}, `{"a": [1]}`, true, 0}, - {[]interface{}{`["a","b"]`, `["c","d"]`}, `["c", "d"]`, true, 0}, - {[]interface{}{`{"a":"b"}`, `["c"]`}, `["c"]`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `null`}, `null`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `"bar"`}, `"bar"`, true, 0}, - {[]interface{}{`{"e":null}`, `{"a":1}`}, `{"e": null,"a": 1}`, true, 0}, - {[]interface{}{`[1,2]`, `{"a":"b","c":null}`}, `{"a":"b"}`, true, 0}, - {[]interface{}{`{}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb": {}}}`, true, 0}, - // RFC 7396 Example Document - {[]interface{}{`{"title":"Goodbye!","author":{"givenName":"John","familyName":"Doe"},"tags":["example","sample"],"content":"This will be unchanged"}`, `{"title":"Hello!","phoneNumber":"+01-123-456-7890","author":{"familyName":null},"tags":["example"]}`}, `{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}`, true, 0}, - - // test cases - {[]interface{}{nil, `1`}, `1`, true, 0}, - {[]interface{}{`1`, nil}, nil, true, 0}, - {[]interface{}{nil, `null`}, `null`, true, 0}, - {[]interface{}{`null`, nil}, nil, true, 0}, - {[]interface{}{nil, `true`}, `true`, true, 0}, - {[]interface{}{`true`, nil}, nil, true, 0}, - {[]interface{}{nil, `false`}, `false`, true, 0}, - {[]interface{}{`false`, nil}, nil, true, 0}, - {[]interface{}{nil, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[]interface{}{`[1,2,3]`, nil}, nil, true, 0}, - {[]interface{}{nil, `{"a":"foo"}`}, nil, true, 0}, - {[]interface{}{`{"a":"foo"}`, nil}, nil, true, 0}, - - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"b":"123"}`, `{"c":1}`}, `{"b":"123","c":1}`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"c":1}`}, `{"c":1}`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `true`}, `true`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"d":1}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb":{}},"d":1}`, true, 0}, - {[]interface{}{`null`, `true`, `[1,2,3]`}, `[1,2,3]`, true, 0}, - - // From mysql Example Test Cases - {[]interface{}{nil, `null`, `[1,2,3]`, `{"a":1}`}, `{"a": 1}`, true, 0}, - {[]interface{}{`null`, nil, `[1,2,3]`, `{"a":1}`}, `{"a": 1}`, true, 0}, - {[]interface{}{`null`, `[1,2,3]`, nil, `{"a":1}`}, nil, true, 0}, - {[]interface{}{`null`, `[1,2,3]`, `{"a":1}`, nil}, nil, true, 0}, - - {[]interface{}{nil, `null`, `{"a":1}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[]interface{}{`null`, nil, `{"a":1}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[]interface{}{`null`, `{"a":1}`, nil, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[]interface{}{`null`, `{"a":1}`, `[1,2,3]`, nil}, nil, true, 0}, - - {[]interface{}{nil, `null`, `{"a":1}`, `true`}, `true`, true, 0}, - {[]interface{}{`null`, nil, `{"a":1}`, `true`}, `true`, true, 0}, - {[]interface{}{`null`, `{"a":1}`, nil, `true`}, `true`, true, 0}, - {[]interface{}{`null`, `{"a":1}`, `true`, nil}, nil, true, 0}, - - // non-object last item - {[]interface{}{"true", "false", "[]", "{}", "null"}, "null", true, 0}, - {[]interface{}{"false", "[]", "{}", "null", "true"}, "true", true, 0}, - {[]interface{}{"true", "[]", "{}", "null", "false"}, "false", true, 0}, - {[]interface{}{"true", "false", "{}", "null", "[]"}, "[]", true, 0}, - {[]interface{}{"true", "false", "{}", "null", "1"}, "1", true, 0}, - {[]interface{}{"true", "false", "{}", "null", "1.8"}, "1.8", true, 0}, - {[]interface{}{"true", "false", "{}", "null", `"112"`}, `"112"`, true, 0}, - - {[]interface{}{`{"a":"foo"}`, nil}, nil, true, 0}, - {[]interface{}{nil, `{"a":"foo"}`}, nil, true, 0}, - {[]interface{}{`{"a":"foo"}`, `false`}, `false`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `123`}, `123`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `123.1`}, `123.1`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, - {[]interface{}{`null`, `{"a":1}`}, `{"a":1}`, true, 0}, - {[]interface{}{`{"a":1}`, `null`}, `null`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"b":"123"}`, `{"c":1}`}, `{"b":"123","c":1}`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"c":1}`}, `{"c":1}`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `true`}, `true`, true, 0}, - {[]interface{}{`{"a":"foo"}`, `{"d":1}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb":{}},"d":1}`, true, 0}, - - // Invalid json text - {[]interface{}{`{"a":1}`, `[1]}`}, nil, false, mysql.ErrInvalidJSONText}, - {[]interface{}{`{{"a":1}`, `[1]`, `null`}, nil, false, mysql.ErrInvalidJSONText}, - {[]interface{}{`{"a":1}`, `jjj`, `null`}, nil, false, mysql.ErrInvalidJSONText}, - } - - for _, tt := range tests { - marks := make([]string, len(tt.input)) - for i := 0; i < len(marks); i++ { - marks[i] = "?" - } - sql := fmt.Sprintf("select json_merge_patch(%s);", strings.Join(marks, ",")) - if tt.success { - result := tk.MustQuery(sql, tt.input...) - if tt.expected == nil { - result.Check(testkit.Rows("")) - } else { - j, e := types.ParseBinaryJSONFromString(tt.expected.(string)) - require.NoError(t, e) - result.Check(testkit.Rows(j.String())) - } - } else { - rs, _ := tk.Exec(sql, tt.input...) - _, err := session.GetRows4Test(ctx, tk.Session(), rs) - terr := errors.Cause(err).(*terror.Error) - require.Equal(t, errors.ErrCode(tt.errCode), terr.Code()) - } - } -} - -// issue https://github.com/pingcap/tidb/issues/28544 -func TestPrimaryKeyRequiredSysvar(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`CREATE TABLE t ( - name varchar(60), - age int - )`) - tk.MustExec(`DROP TABLE t`) - - tk.MustExec("set @@sql_require_primary_key=true") - - // creating table without primary key should now fail - tk.MustGetErrCode(`CREATE TABLE t ( - name varchar(60), - age int - )`, errno.ErrTableWithoutPrimaryKey) - // but with primary key should work as usual - tk.MustExec(`CREATE TABLE t ( - id bigint(20) NOT NULL PRIMARY KEY AUTO_RANDOM, - name varchar(60), - age int - )`) - tk.MustGetErrMsg(`ALTER TABLE t - DROP COLUMN id`, "[ddl:8200]Unsupported drop integer primary key") - - // test with non-clustered primary key - tk.MustExec(`CREATE TABLE t2 ( - id int(11) NOT NULL, - c1 int(11) DEFAULT NULL, - PRIMARY KEY(id) NONCLUSTERED)`) - tk.MustGetErrMsg(`ALTER TABLE t2 - DROP COLUMN id`, "[ddl:8200]can't drop column id with composite index covered or Primary Key covered now") - tk.MustGetErrCode(`ALTER TABLE t2 DROP PRIMARY KEY`, errno.ErrTableWithoutPrimaryKey) - - // this sysvar is ignored in internal sessions - tk.Session().GetSessionVars().InRestrictedSQL = true - ctx := context.Background() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) - sql := `CREATE TABLE t3 ( - id int(11) NOT NULL, - c1 int(11) DEFAULT NULL)` - stmts, err := tk.Session().Parse(ctx, sql) - require.NoError(t, err) - res, err := tk.Session().ExecuteStmt(ctx, stmts[0]) - require.NoError(t, err) - if res != nil { - require.NoError(t, res.Close()) - } -} - -func TestTimestamp(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test;`) - tk.MustExec("SET time_zone = '+00:00';") - defer tk.MustExec("SET time_zone = DEFAULT;") - timestampStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) - timestampStr1 = timestampStr1[1:] - timestampStr1 = timestampStr1[:len(timestampStr1)-1] - timestamp1, err := strconv.ParseFloat(timestampStr1, 64) - require.NoError(t, err) - nowStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) - now1, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr1) - require.NoError(t, err) - tk.MustExec("set @@timestamp = 12345;") - tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) - tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) - tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) - tk.MustExec("set @@timestamp = default;") - time.Sleep(2 * time.Microsecond) - timestampStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) - timestampStr2 = timestampStr2[1:] - timestampStr2 = timestampStr2[:len(timestampStr2)-1] - timestamp2, err := strconv.ParseFloat(timestampStr2, 64) - require.NoError(t, err) - nowStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) - now2, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr2) - require.NoError(t, err) - require.Less(t, timestamp1, timestamp2) - require.Less(t, now1.UnixNano(), now2.UnixNano()) - tk.MustExec("set @@timestamp = 12345;") - tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) - tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) - tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) - tk.MustExec("set @@timestamp = 0;") - time.Sleep(2 * time.Microsecond) - timestampStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) - timestampStr3 = timestampStr3[1:] - timestampStr3 = timestampStr3[:len(timestampStr3)-1] - timestamp3, err := strconv.ParseFloat(timestampStr3, 64) - require.NoError(t, err) - nowStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) - now3, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr3) - require.NoError(t, err) - require.Less(t, timestamp2, timestamp3) - require.Less(t, now2.UnixNano(), now3.UnixNano()) -} - -func TestCastJSONTimeDuration(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(i INT, j JSON)") - - nowDate := time.Now().Format(time.DateOnly) - - // DATE/DATETIME/TIME will be automatically converted to json date/datetime/duration - tk.MustExec("insert into t values (0, DATE('1998-06-13'))") - tk.MustExec("insert into t values (1, CAST('1998-06-13 12:12:12' as DATETIME))") - tk.MustExec("insert into t values (2, DATE('1596-03-31'))") - tk.MustExec("insert into t values (3, CAST('1596-03-31 12:12:12' as DATETIME))") - tk.MustExec(`insert into t values (4, '"1596-03-31 12:12:12"')`) - tk.MustExec(`insert into t values (5, '"12:12:12"')`) - tk.MustExec("insert into t values (6, CAST('12:12:12' as TIME))") - tk.MustQuery("select i, cast(j as date), cast(j as datetime), cast(j as time), json_type(j) from t").Check(testkit.Rows( - "0 1998-06-13 1998-06-13 00:00:00 00:00:00 DATE", - "1 1998-06-13 1998-06-13 12:12:12 12:12:12 DATETIME", - "2 1596-03-31 1596-03-31 00:00:00 00:00:00 DATE", - "3 1596-03-31 1596-03-31 12:12:12 12:12:12 DATETIME", - "4 1596-03-31 1596-03-31 12:12:12 12:12:12 STRING", - "5 2012-12-12 2012-12-12 00:00:00 12:12:12 STRING", - fmt.Sprintf("6 %s %s 12:12:12 12:12:12 TIME", nowDate, nowDate), - )) -} - -func TestCompareBuiltin(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // compare as JSON - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (pk int NOT NULL PRIMARY KEY AUTO_INCREMENT, i INT, j JSON);") - tk.MustExec(`INSERT INTO t(i, j) VALUES (0, NULL)`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (1, '{"a": 2}')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (2, '[1,2]')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (3, '{"a":"b", "c":"d","ab":"abc", "bc": ["x", "y"]}')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (4, '["here", ["I", "am"], "!!!"]')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (5, '"scalar string"')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (6, 'true')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (7, 'false')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (8, 'null')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (9, '-1')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (10, CAST(CAST(1 AS UNSIGNED) AS JSON))`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (11, '32767')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (12, '32768')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (13, '-32768')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (14, '-32769')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (15, '2147483647')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (16, '2147483648')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (17, '-2147483648')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (18, '-2147483649')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (19, '18446744073709551615')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (20, '18446744073709551616')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (21, '3.14')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (22, '{}')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (23, '[]')`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (24, CAST(CAST('2015-01-15 23:24:25' AS DATETIME) AS JSON))`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (25, CAST(CAST('23:24:25' AS TIME) AS JSON))`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (26, CAST(CAST('2015-01-15' AS DATE) AS JSON))`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (27, CAST(TIMESTAMP('2015-01-15 23:24:25') AS JSON))`) - tk.MustExec(`INSERT INTO t(i, j) VALUES (28, CAST('[]' AS CHAR CHARACTER SET 'ascii'))`) - - result := tk.MustQuery(`SELECT i, - (j = '"scalar string"') AS c1, - (j = 'scalar string') AS c2, - (j = CAST('"scalar string"' AS JSON)) AS c3, - (j = CAST(CAST(j AS CHAR CHARACTER SET 'utf8mb4') AS JSON)) AS c4, - (j = CAST(NULL AS JSON)) AS c5, - (j = NULL) AS c6, - (j <=> NULL) AS c7, - (j <=> CAST(NULL AS JSON)) AS c8, - (j IN (-1, 2, 32768, 3.14)) AS c9, - (j IN (CAST('[1, 2]' AS JSON), CAST('{}' AS JSON), CAST(3.14 AS JSON))) AS c10, - (j = (SELECT j FROM t WHERE j = CAST('null' AS JSON))) AS c11, - (j = (SELECT j FROM t WHERE j IS NULL)) AS c12, - (j = (SELECT j FROM t WHERE 1<>1)) AS c13, - (j = DATE('2015-01-15')) AS c14, - (j = TIME('23:24:25')) AS c15, - (j = TIMESTAMP('2015-01-15 23:24:25')) AS c16, - (j = CURRENT_TIMESTAMP) AS c17, - (JSON_EXTRACT(j, '$.a') = 2) AS c18 - FROM t - ORDER BY i;`) - result.Check(testkit.Rows("0 1 1 ", - "1 0 0 0 1 0 0 0 0 0 0 0 0 0 1", - "2 0 0 0 1 0 0 0 1 0 0 0 0 0 ", - "3 0 0 0 1 0 0 0 0 0 0 0 0 0 0", - "4 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "5 0 1 1 1 0 0 0 0 0 0 0 0 0 ", - "6 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "7 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "8 0 0 0 1 0 0 0 0 1 0 0 0 0 ", - "9 0 0 0 1 0 0 1 0 0 0 0 0 0 ", - "10 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "11 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "12 0 0 0 1 0 0 1 0 0 0 0 0 0 ", - "13 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "14 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "15 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "16 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "17 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "18 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "19 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "20 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "21 0 0 0 1 0 0 1 1 0 0 0 0 0 ", - "22 0 0 0 1 0 0 0 1 0 0 0 0 0 ", - "23 0 0 0 1 0 0 0 0 0 0 0 0 0 ", - "24 0 0 0 0 0 0 0 0 0 0 0 1 0 ", - "25 0 0 0 0 0 0 0 0 0 0 1 0 0 ", - "26 0 0 0 0 0 0 0 0 0 1 0 0 0 ", - "27 0 0 0 0 0 0 0 0 0 0 0 1 0 ", - "28 0 0 0 1 0 0 0 0 0 0 0 0 0 ")) - - // for coalesce - result = tk.MustQuery("select coalesce(NULL), coalesce(NULL, NULL), coalesce(NULL, NULL, NULL);") - result.Check(testkit.Rows(" ")) - tk.MustQuery(`select coalesce(cast(1 as json), cast(2 as json));`).Check(testkit.Rows(`1`)) - tk.MustQuery(`select coalesce(NULL, cast(2 as json));`).Check(testkit.Rows(`2`)) - tk.MustQuery(`select coalesce(cast(1 as json), NULL);`).Check(testkit.Rows(`1`)) - tk.MustQuery(`select coalesce(NULL, NULL);`).Check(testkit.Rows(``)) - - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2(a int, b double, c datetime, d time, e char(20), f bit(10))") - tk.MustExec(`insert into t2 values(1, 1.1, "2017-08-01 12:01:01", "12:01:01", "abcdef", 0b10101)`) - - result = tk.MustQuery("select coalesce(NULL, a), coalesce(NULL, b, a), coalesce(c, NULL, a, b), coalesce(d, NULL), coalesce(d, c), coalesce(NULL, NULL, e, 1), coalesce(f), coalesce(1, a, b, c, d, e, f) from t2") - // coalesce(col_bit) is not same with MySQL, because it's a bug of MySQL(https://bugs.mysql.com/bug.php?id=103289&thanks=4) - result.Check(testkit.Rows(fmt.Sprintf("1 1.1 2017-08-01 12:01:01 12:01:01 %s 12:01:01 abcdef \x00\x15 1", time.Now().In(tk.Session().GetSessionVars().Location()).Format(time.DateOnly)))) - - // nullif - result = tk.MustQuery(`SELECT NULLIF(NULL, 1), NULLIF(1, NULL), NULLIF(1, 1), NULLIF(NULL, NULL);`) - result.Check(testkit.Rows(" 1 ")) - - result = tk.MustQuery(`SELECT NULLIF(1, 1.0), NULLIF(1, "1.0");`) - result.Check(testkit.Rows(" ")) - - result = tk.MustQuery(`SELECT NULLIF("abc", 1);`) - result.Check(testkit.Rows("abc")) - - result = tk.MustQuery(`SELECT NULLIF(1+2, 1);`) - result.Check(testkit.Rows("3")) - - result = tk.MustQuery(`SELECT NULLIF(1, 1+2);`) - result.Check(testkit.Rows("1")) - - result = tk.MustQuery(`SELECT NULLIF(2+3, 1+2);`) - result.Check(testkit.Rows("5")) - - result = tk.MustQuery(`SELECT HEX(NULLIF("abc", 1));`) - result.Check(testkit.Rows("616263")) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a date)") - result = tk.MustQuery("desc select a = a from t") - result.Check(testkit.Rows( - "Projection_3 10000.00 root eq(test.t.a, test.t.a)->Column#3", - "└─TableReader_5 10000.00 root data:TableFullScan_4", - " └─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", - )) - - // for interval - result = tk.MustQuery(`select interval(null, 1, 2), interval(1, 2, 3), interval(2, 1, 3)`) - result.Check(testkit.Rows("-1 0 1")) - result = tk.MustQuery(`select interval(3, 1, 2), interval(0, "b", "1", "2"), interval("a", "b", "1", "2")`) - result.Check(testkit.Rows("2 1 1")) - result = tk.MustQuery(`select interval(23, 1, 23, 23, 23, 30, 44, 200), interval(23, 1.7, 15.3, 23.1, 30, 44, 200), interval(9007199254740992, 9007199254740993)`) - result.Check(testkit.Rows("4 2 0")) - result = tk.MustQuery(`select interval(cast(9223372036854775808 as unsigned), cast(9223372036854775809 as unsigned)), interval(9223372036854775807, cast(9223372036854775808 as unsigned)), interval(-9223372036854775807, cast(9223372036854775808 as unsigned))`) - result.Check(testkit.Rows("0 0 0")) - result = tk.MustQuery(`select interval(cast(9223372036854775806 as unsigned), 9223372036854775807), interval(cast(9223372036854775806 as unsigned), -9223372036854775807), interval("9007199254740991", "9007199254740992")`) - result.Check(testkit.Rows("0 1 0")) - result = tk.MustQuery(`select interval(9007199254740992, "9007199254740993"), interval("9007199254740992", 9007199254740993), interval("9007199254740992", "9007199254740993")`) - result.Check(testkit.Rows("1 1 1")) - result = tk.MustQuery(`select INTERVAL(100, NULL, NULL, NULL, NULL, NULL, 100);`) - result.Check(testkit.Rows("6")) - result = tk.MustQuery(`SELECT INTERVAL(0,(1*5)/2) + INTERVAL(5,4,3);`) - result.Check(testkit.Rows("2")) - - // for greatest - result = tk.MustQuery(`select greatest(1, 2, 3), greatest("a", "b", "c"), greatest(1.1, 1.2, 1.3), greatest("123a", 1, 2)`) - result.Check(testkit.Rows("3 c 1.3 2")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - result = tk.MustQuery(`select greatest(cast("2017-01-01" as datetime), "123", "234", cast("2018-01-01" as date)), greatest(cast("2017-01-01" as date), "123", null)`) - result.Check(testkit.Rows("234 ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect time value: '123'", "Warning|1292|Incorrect time value: '234'", "Warning|1292|Incorrect time value: '123'")) - // for least - result = tk.MustQuery(`select least(1, 2, 3), least("a", "b", "c"), least(1.1, 1.2, 1.3), least("123a", 1, 2)`) - result.Check(testkit.Rows("1 a 1.1 1")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - result = tk.MustQuery(`select least(cast("2017-01-01" as datetime), "123", "234", cast("2018-01-01" as date)), least(cast("2017-01-01" as date), "123", null)`) - result.Check(testkit.Rows("123 ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect time value: '123'", "Warning|1292|Incorrect time value: '234'", "Warning|1292|Incorrect time value: '123'")) - tk.MustQuery(`select 1 < 17666000000000000000, 1 > 17666000000000000000, 1 = 17666000000000000000`).Check(testkit.Rows("1 0 0")) - - tk.MustExec("drop table if exists t") - - // insert value at utc timezone - tk.MustExec("set time_zone = '+00:00'") - tk.MustExec("create table t(a timestamp)") - tk.MustExec("insert into t value('1991-05-06 04:59:28')") - // check daylight saving time in Asia/Shanghai - tk.MustExec("set time_zone='Asia/Shanghai'") - tk.MustQuery("select * from t").Check(testkit.Rows("1991-05-06 13:59:28")) - // insert an nonexistent time - tk.MustExec("set time_zone = 'America/Los_Angeles'") - tk.MustExecToErr("insert into t value('2011-03-13 02:00:00')") - // reset timezone to a +8 offset - tk.MustExec("set time_zone = '+08:00'") - tk.MustQuery("select * from t").Check(testkit.Rows("1991-05-06 12:59:28")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a bigint unsigned)") - tk.MustExec("insert into t value(17666000000000000000)") - tk.MustQuery("select * from t where a = 17666000000000000000").Check(testkit.Rows("17666000000000000000")) - - // test for compare row - result = tk.MustQuery(`select row(1,2,3)=row(1,2,3)`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`select row(1,2,3)=row(1+3,2,3)`) - result.Check(testkit.Rows("0")) - result = tk.MustQuery(`select row(1,2,3)<>row(1,2,3)`) - result.Check(testkit.Rows("0")) - result = tk.MustQuery(`select row(1,2,3)<>row(1+3,2,3)`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`select row(1+3,2,3)<>row(1+3,2,3)`) - result.Check(testkit.Rows("0")) -} - -func TestTimeBuiltin(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - originSQLMode := tk.Session().GetSessionVars().StrictSQLMode - tk.Session().GetSessionVars().StrictSQLMode = true - defer func() { - tk.Session().GetSessionVars().StrictSQLMode = originSQLMode - }() - tk.MustExec("use test") - - // for makeDate - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b double, c datetime, d time, e char(20), f bit(10))") - tk.MustExec(`insert into t values(1, 1.1, "2017-01-01 12:01:01", "12:01:01", "abcdef", 0b10101)`) - result := tk.MustQuery("select makedate(a,a), makedate(b,b), makedate(c,c), makedate(d,d), makedate(e,e), makedate(f,f), makedate(null,null), makedate(a,b) from t") - result.Check(testkit.Rows("2001-01-01 2001-01-01 2021-01-21 2001-01-01")) - - // for date - result = tk.MustQuery(`select date("2019-09-12"), date("2019-09-12 12:12:09"), date("2019-09-12 12:12:09.121212");`) - result.Check(testkit.Rows("2019-09-12 2019-09-12 2019-09-12")) - result = tk.MustQuery(`select date("0000-00-00"), date("0000-00-00 12:12:09"), date("0000-00-00 00:00:00.121212"), date("0000-00-00 00:00:00.000000");`) - result.Check(testkit.Rows(" 0000-00-00 0000-00-00 ")) - result = tk.MustQuery(`select date("aa"), date(12.1), date("");`) - result.Check(testkit.Rows(" ")) - - // for year - result = tk.MustQuery(`select year("2013-01-09"), year("2013-00-09"), year("000-01-09"), year("1-01-09"), year("20131-01-09"), year(null);`) - result.Check(testkit.Rows("2013 2013 0 2001 ")) - result = tk.MustQuery(`select year("2013-00-00"), year("2013-00-00 00:00:00"), year("0000-00-00 12:12:12"), year("2017-00-00 12:12:12");`) - result.Check(testkit.Rows("2013 2013 0 2017")) - result = tk.MustQuery(`select year("aa"), year(2013), year(2012.09), year("1-01"), year("-09");`) - result.Check(testkit.Rows(" ")) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a bigint)`) - _, err := tk.Exec(`insert into t select year("aa")`) - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err %v", err) - tk.MustExec(`set sql_mode='STRICT_TRANS_TABLES'`) // without zero date - tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) - tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) // with zero date - tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) - tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) - _, err = tk.Exec(`insert into t select year("0000-00-00 00:00:00");`) - require.Error(t, err) - require.True(t, types.ErrWrongValue.Equal(err), "err %v", err) - - tk.MustExec(`insert into t select 1`) - tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) - _, err = tk.Exec(`update t set a = year("aa")`) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err %v", err) - _, err = tk.Exec(`delete from t where a = year("aa")`) - // Only `code` can be used to compare because the error `class` information - // will be lost after expression push-down - require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) - - // for month - result = tk.MustQuery(`select month("2013-01-09"), month("2013-00-09"), month("000-01-09"), month("1-01-09"), month("20131-01-09"), month(null);`) - result.Check(testkit.Rows("1 0 1 1 ")) - result = tk.MustQuery(`select month("2013-00-00"), month("2013-00-00 00:00:00"), month("0000-00-00 12:12:12"), month("2017-00-00 12:12:12");`) - result.Check(testkit.Rows("0 0 0 0")) - result = tk.MustQuery(`select month("aa"), month(2013), month(2012.09), month("1-01"), month("-09");`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select month("2013-012-09"), month("2013-0000000012-09"), month("2013-30-09"), month("000-41-09");`) - result.Check(testkit.Rows("12 12 ")) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a bigint)`) - _, err = tk.Exec(`insert into t select month("aa")`) - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err: %v", err) - tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) - tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) - tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) - tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) - _, err = tk.Exec(`insert into t select month("0000-00-00 00:00:00");`) - require.Error(t, err) - require.True(t, types.ErrWrongValue.Equal(err), "err: %v", err) - tk.MustExec(`insert into t select 1`) - tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) - tk.MustExec(`insert into t select 1`) - _, err = tk.Exec(`update t set a = month("aa")`) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) - _, err = tk.Exec(`delete from t where a = month("aa")`) - require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) - - // for week - result = tk.MustQuery(`select week("2012-12-22"), week("2012-12-22", -2), week("2012-12-22", 0), week("2012-12-22", 1), week("2012-12-22", 2), week("2012-12-22", 200);`) - result.Check(testkit.Rows("51 51 51 51 51 51")) - result = tk.MustQuery(`select week("2008-02-20"), week("2008-02-20", 0), week("2008-02-20", 1), week("2009-02-20", 2), week("2008-02-20", 3), week("2008-02-20", 4);`) - result.Check(testkit.Rows("7 7 8 7 8 8")) - result = tk.MustQuery(`select week("2008-02-20", 5), week("2008-02-20", 6), week("2009-02-20", 7), week("2008-02-20", 8), week("2008-02-20", 9);`) - result.Check(testkit.Rows("7 8 7 7 8")) - result = tk.MustQuery(`select week("aa", 1), week(null, 2), week(11, 2), week(12.99, 2);`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select week("aa"), week(null), week(11), week(12.99);`) - result.Check(testkit.Rows(" ")) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a datetime)`) - _, err = tk.Exec(`insert into t select week("aa", 1)`) - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) - tk.MustExec(`insert into t select now()`) - _, err = tk.Exec(`update t set a = week("aa", 1)`) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) - _, err = tk.Exec(`delete from t where a = week("aa", 1)`) - require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) - - // for weekofyear - result = tk.MustQuery(`select weekofyear("2012-12-22"), weekofyear("2008-02-20"), weekofyear("aa"), weekofyear(null), weekofyear(11), weekofyear(12.99);`) - result.Check(testkit.Rows("51 8 ")) - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a bigint)`) - _, err = tk.Exec(`insert into t select weekofyear("aa")`) - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) - - tk.MustExec(`insert into t select 1`) - _, err = tk.Exec(`update t set a = weekofyear("aa")`) - require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) - _, err = tk.Exec(`delete from t where a = weekofyear("aa")`) - require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) - - // for weekday - result = tk.MustQuery(`select weekday("2012-12-20"), weekday("2012-12-21"), weekday("2012-12-22"), weekday("2012-12-23"), weekday("2012-12-24"), weekday("2012-12-25"), weekday("2012-12-26"), weekday("2012-12-27");`) - result.Check(testkit.Rows("3 4 5 6 0 1 2 3")) - result = tk.MustQuery(`select weekday("2012-12-90"), weekday("0000-00-00"), weekday("aa"), weekday(null), weekday(11), weekday(12.99);`) - result.Check(testkit.Rows(" ")) - - // for quarter - result = tk.MustQuery(`select quarter("2012-00-20"), quarter("2012-01-21"), quarter("2012-03-22"), quarter("2012-05-23"), quarter("2012-08-24"), quarter("2012-09-25"), quarter("2012-11-26"), quarter("2012-12-27");`) - result.Check(testkit.Rows("0 1 1 2 3 3 4 4")) - result = tk.MustQuery(`select quarter("2012-14-20"), quarter("aa"), quarter(null), quarter(11), quarter(12.99);`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select quarter("0000-00-00"), quarter("0000-00-00 00:00:00");`) - result.Check(testkit.Rows("0 0")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - result = tk.MustQuery(`select quarter(0), quarter(0.0), quarter(0e1), quarter(0.00);`) - result.Check(testkit.Rows("0 0 0 0")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - - // for from_days - result = tk.MustQuery(`select from_days(0), from_days(-199), from_days(1111), from_days(120), from_days(1), from_days(1111111), from_days(9999999), from_days(22222);`) - result.Check(testkit.Rows("0000-00-00 0000-00-00 0003-01-16 0000-00-00 0000-00-00 3042-02-13 0000-00-00 0060-11-03")) - result = tk.MustQuery(`select from_days("2012-14-20"), from_days("111a"), from_days("aa"), from_days(null), from_days("123asf"), from_days(12.99);`) - result.Check(testkit.Rows("0005-07-05 0000-00-00 0000-00-00 0000-00-00 0000-00-00")) - - // Fix issue #3923 - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00');") - result.Check(testkit.Rows("00:00:00")) - result = tk.MustQuery("select timediff('12:00:00', cast('2004-12-30 12:00:00' as time));") - result.Check(testkit.Rows("00:00:00")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:00' as time), '2004-12-30 12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('2004-12-30 12:00:00', cast('2004-12-30 12:00:00' as time));") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '2004-12-30 12:00:00');") - result.Check(testkit.Rows("00:00:01")) - result = tk.MustQuery("select timediff('2004-12-30 12:00:00', cast('2004-12-30 12:00:01' as datetime));") - result.Check(testkit.Rows("-00:00:01")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as time), '-34 00:00:00');") - result.Check(testkit.Rows("828:00:01")) - result = tk.MustQuery("select timediff('-34 00:00:00', cast('2004-12-30 12:00:01' as time));") - result.Check(testkit.Rows("-828:00:01")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), cast('2004-12-30 11:00:01' as datetime));") - result.Check(testkit.Rows("01:00:00")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '2004-12-30 12:00:00.1');") - result.Check(testkit.Rows("00:00:00.9")) - result = tk.MustQuery("select timediff('2004-12-30 12:00:00.1', cast('2004-12-30 12:00:01' as datetime));") - result.Check(testkit.Rows("-00:00:00.9")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '-34 124:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('-34 124:00:00', cast('2004-12-30 12:00:01' as datetime));") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as time), '-34 124:00:00');") - result.Check(testkit.Rows("838:59:59")) - result = tk.MustQuery("select timediff('-34 124:00:00', cast('2004-12-30 12:00:01' as time));") - result.Check(testkit.Rows("-838:59:59")) - result = tk.MustQuery("select timediff(cast('2004-12-30' as datetime), '12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('12:00:00', cast('2004-12-30' as datetime));") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('12:00:00', '-34 12:00:00');") - result.Check(testkit.Rows("838:59:59")) - result = tk.MustQuery("select timediff('12:00:00', '34 12:00:00');") - result.Check(testkit.Rows("-816:00:00")) - result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '-34 12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('-34 12:00:00', '2014-1-2 12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('12:00:00', '2014-1-2 12:00:00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '2014-1-1 12:00:00');") - result.Check(testkit.Rows("24:00:00")) - tk.MustQuery("select timediff(cast('10:10:10' as time), cast('10:10:11' as time))").Check(testkit.Rows("-00:00:01")) - - result = tk.MustQuery("select timestampadd(MINUTE, 1, '2003-01-02'), timestampadd(WEEK, 1, '2003-01-02 23:59:59')" + - ", timestampadd(MICROSECOND, 1, 950501);") - result.Check(testkit.Rows("2003-01-02 00:01:00 2003-01-09 23:59:59 1995-05-01 00:00:00.000001")) - result = tk.MustQuery("select timestampadd(day, 2, 950501), timestampadd(MINUTE, 37.5,'2003-01-02'), timestampadd(MINUTE, 37.49,'2003-01-02')," + - " timestampadd(YeAr, 1, '2003-01-02');") - result.Check(testkit.Rows("1995-05-03 00:00:00 2003-01-02 00:38:00 2003-01-02 00:37:00 2004-01-02 00:00:00")) - result = tk.MustQuery("select to_seconds(950501), to_seconds('2009-11-29'), to_seconds('2009-11-29 13:43:32'), to_seconds('09-11-29 13:43:32');") - result.Check(testkit.Rows("62966505600 63426672000 63426721412 63426721412")) - result = tk.MustQuery("select to_days(950501), to_days('2007-10-07'), to_days('2007-10-07 00:00:59'), to_days('0000-01-01')") - result.Check(testkit.Rows("728779 733321 733321 1")) - - result = tk.MustQuery("select last_day('2003-02-05'), last_day('2004-02-05'), last_day('2004-01-01 01:01:01'), last_day(950501);") - result.Check(testkit.Rows("2003-02-28 2004-02-29 2004-01-31 1995-05-31")) - - tk.MustExec("SET SQL_MODE='';") - result = tk.MustQuery("select last_day('0000-00-00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select to_days('0000-00-00');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select to_seconds('0000-00-00');") - result.Check(testkit.Rows("")) - - result = tk.MustQuery("select timestamp('2003-12-31'), timestamp('2003-12-31 12:00:00','12:00:00');") - result.Check(testkit.Rows("2003-12-31 00:00:00 2004-01-01 00:00:00")) - result = tk.MustQuery("select timestamp(20170118123950.123), timestamp(20170118123950.999);") - result.Check(testkit.Rows("2017-01-18 12:39:50.123 2017-01-18 12:39:50.999")) - // Issue https://github.com/pingcap/tidb/issues/20003 - result = tk.MustQuery("select timestamp(0.0001, 0.00001);") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select timestamp('2003-12-31', '01:01:01.01'), timestamp('2003-12-31 12:34', '01:01:01.01')," + - " timestamp('2008-12-31','00:00:00.0'), timestamp('2008-12-31 00:00:00.000');") - - tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 00:00:01")) - tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 01:00:00")) - tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-02")) - tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-02-01")) - tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2002-01-01")) - tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 00:00:01")) - tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 01:00:00")) - tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-02 00:00:00")) - tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-02-01 00:00:00")) - tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2002-01-01 00:00:00")) - - result.Check(testkit.Rows("2003-12-31 01:01:01.01 2003-12-31 13:35:01.01 2008-12-31 00:00:00.0 2008-12-31 00:00:00.000")) - result = tk.MustQuery("select timestamp('2003-12-31', 1), timestamp('2003-12-31', -1);") - result.Check(testkit.Rows("2003-12-31 00:00:01 2003-12-30 23:59:59")) - result = tk.MustQuery("select timestamp('2003-12-31', '2000-12-12 01:01:01.01'), timestamp('2003-14-31','01:01:01.01');") - result.Check(testkit.Rows(" ")) - - result = tk.MustQuery("select TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01'), TIMESTAMPDIFF(yEaR,'2002-05-01', " + - "'2001-01-01'), TIMESTAMPDIFF(minute,binary('2003-02-01'),'2003-05-01 12:05:55'), TIMESTAMPDIFF(day," + - "'1995-05-02', 950501);") - result.Check(testkit.Rows("3 -1 128885 -1")) - - result = tk.MustQuery("select datediff('2007-12-31 23:59:59','2007-12-30'), datediff('2010-11-30 23:59:59', " + - "'2010-12-31'), datediff(950501,'2016-01-13'), datediff(950501.9,'2016-01-13'), datediff(binary(950501), '2016-01-13');") - result.Check(testkit.Rows("1 -31 -7562 -7562 -7562")) - result = tk.MustQuery("select datediff('0000-01-01','0001-01-01'), datediff('0001-00-01', '0001-00-01'), datediff('0001-01-00','0001-01-00'), datediff('2017-01-01','2017-01-01');") - result.Check(testkit.Rows("-365 0")) - - // for ADDTIME - result = tk.MustQuery("select addtime('01:01:11', '00:00:01.013'), addtime('01:01:11.00', '00:00:01'), addtime" + - "('2017-01-01 01:01:11.12', '00:00:01'), addtime('2017-01-01 01:01:11.12', '00:00:01.88');") - result.Check(testkit.Rows("01:01:12.013000 01:01:12 2017-01-01 01:01:12.120000 2017-01-01 01:01:13")) - result = tk.MustQuery("select addtime(cast('01:01:11' as time(4)), '00:00:01.013'), addtime(cast('01:01:11.00' " + - "as datetime(3)), '00:00:01')," + " addtime(cast('2017-01-01 01:01:11.12' as date), '00:00:01'), addtime(cast" + - "(cast('2017-01-01 01:01:11.12' as date) as datetime(2)), '00:00:01.88');") - result.Check(testkit.Rows("01:01:12.0130 2001-01-11 00:00:01.000 00:00:01 2017-01-01 00:00:01.88")) - result = tk.MustQuery("select addtime('2017-01-01 01:01:01', 5), addtime('2017-01-01 01:01:01', -5), addtime('2017-01-01 01:01:01', 0.0), addtime('2017-01-01 01:01:01', 1.34);") - result.Check(testkit.Rows("2017-01-01 01:01:06 2017-01-01 01:00:56 2017-01-01 01:01:01 2017-01-01 01:01:02.340000")) - result = tk.MustQuery("select addtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time)), addtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time(5)))") - result.Check(testkit.Rows("2001-01-11 00:00:01.000 2001-01-11 00:00:01.00000")) - result = tk.MustQuery("select addtime(cast('01:01:11.00' as date), cast('00:00:01' as time));") - result.Check(testkit.Rows("00:00:01")) - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a datetime, b timestamp, c time)") - tk.MustExec(`insert into t values("2017-01-01 12:30:31", "2017-01-01 12:30:31", "01:01:01")`) - result = tk.MustQuery("select addtime(a, b), addtime(cast(a as date), b), addtime(b,a), addtime(a,c), addtime(b," + - "c), addtime(c,a), addtime(c,b)" + - " from t;") - result.Check(testkit.Rows(" 2017-01-01 13:31:32 2017-01-01 13:31:32 ")) - result = tk.MustQuery("select addtime('01:01:11', cast('1' as time))") - result.Check(testkit.Rows("01:01:12")) - tk.MustQuery("select addtime(cast(null as char(20)), cast('1' as time))").Check(testkit.Rows("")) - require.NoError(t, tk.QueryToErr(`select addtime("01:01:11", cast('sdf' as time))`)) - tk.MustQuery(`select addtime("01:01:11", cast(null as char(20)))`).Check(testkit.Rows("")) - tk.MustQuery(`select addtime(cast(1 as time), cast(1 as time))`).Check(testkit.Rows("00:00:02")) - tk.MustQuery(`select addtime(cast(null as time), cast(1 as time))`).Check(testkit.Rows("")) - tk.MustQuery(`select addtime(cast(1 as time), cast(null as time))`).Check(testkit.Rows("")) - - // for SUBTIME - result = tk.MustQuery("select subtime('01:01:11', '00:00:01.013'), subtime('01:01:11.00', '00:00:01'), subtime" + - "('2017-01-01 01:01:11.12', '00:00:01'), subtime('2017-01-01 01:01:11.12', '00:00:01.88');") - result.Check(testkit.Rows("01:01:09.987000 01:01:10 2017-01-01 01:01:10.120000 2017-01-01 01:01:09.240000")) - result = tk.MustQuery("select subtime(cast('01:01:11' as time(4)), '00:00:01.013'), subtime(cast('01:01:11.00' " + - "as datetime(3)), '00:00:01')," + " subtime(cast('2017-01-01 01:01:11.12' as date), '00:00:01'), subtime(cast" + - "(cast('2017-01-01 01:01:11.12' as date) as datetime(2)), '00:00:01.88');") - result.Check(testkit.Rows("01:01:09.9870 2001-01-10 23:59:59.000 -00:00:01 2016-12-31 23:59:58.12")) - result = tk.MustQuery("select subtime('2017-01-01 01:01:01', 5), subtime('2017-01-01 01:01:01', -5), subtime('2017-01-01 01:01:01', 0.0), subtime('2017-01-01 01:01:01', 1.34);") - result.Check(testkit.Rows("2017-01-01 01:00:56 2017-01-01 01:01:06 2017-01-01 01:01:01 2017-01-01 01:00:59.660000")) - result = tk.MustQuery("select subtime('01:01:11', '0:0:1.013'), subtime('01:01:11.00', '0:0:1'), subtime('2017-01-01 01:01:11.12', '0:0:1'), subtime('2017-01-01 01:01:11.12', '0:0:1.120000');") - result.Check(testkit.Rows("01:01:09.987000 01:01:10 2017-01-01 01:01:10.120000 2017-01-01 01:01:10")) - result = tk.MustQuery("select subtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time)), subtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time(5)))") - result.Check(testkit.Rows("2001-01-10 23:59:59.000 2001-01-10 23:59:59.00000")) - result = tk.MustQuery("select subtime(cast('01:01:11.00' as date), cast('00:00:01' as time));") - result.Check(testkit.Rows("-00:00:01")) - result = tk.MustQuery("select subtime(a, b), subtime(cast(a as date), b), subtime(b,a), subtime(a,c), subtime(b," + - "c), subtime(c,a), subtime(c,b) from t;") - result.Check(testkit.Rows(" 2017-01-01 11:29:30 2017-01-01 11:29:30 ")) - tk.MustQuery("select subtime(cast('10:10:10' as time), cast('9:10:10' as time))").Check(testkit.Rows("01:00:00")) - tk.MustQuery("select subtime('10:10:10', cast('9:10:10' as time))").Check(testkit.Rows("01:00:00")) - - // SUBTIME issue #31868 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a DATETIME(6))") - tk.MustExec(`insert into t values ("1000-01-01 01:00:00.000000"), ("1000-01-01 01:00:00.000001")`) - tk.MustQuery(`SELECT SUBTIME(a, '00:00:00.000001') FROM t ORDER BY a;`).Check(testkit.Rows("1000-01-01 00:59:59.999999", "1000-01-01 01:00:00.000000")) - tk.MustQuery(`SELECT SUBTIME(a, '10:00:00.000001') FROM t ORDER BY a;`).Check(testkit.Rows("0999-12-31 14:59:59.999999", "0999-12-31 15:00:00.000000")) - - // ADDTIME & SUBTIME issue #5966 - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a datetime, b timestamp, c time, d date, e bit(1))") - tk.MustExec(`insert into t values("2017-01-01 12:30:31", "2017-01-01 12:30:31", "01:01:01", "2017-01-01", 0b1)`) - - result = tk.MustQuery("select addtime(a, e), addtime(b, e), addtime(c, e), addtime(d, e) from t") - result.Check(testkit.Rows(" ")) - result = tk.MustQuery("select addtime('2017-01-01 01:01:01', 0b1), addtime('2017-01-01', b'1'), addtime('01:01:01', 0b1011)") - result.Check(testkit.Rows(" ")) - result = tk.MustQuery("select addtime('2017-01-01', 1), addtime('2017-01-01 01:01:01', 1), addtime(cast('2017-01-01' as date), 1)") - result.Check(testkit.Rows("2017-01-01 00:00:01 2017-01-01 01:01:02 00:00:01")) - result = tk.MustQuery("select subtime(a, e), subtime(b, e), subtime(c, e), subtime(d, e) from t") - result.Check(testkit.Rows(" ")) - result = tk.MustQuery("select subtime('2017-01-01 01:01:01', 0b1), subtime('2017-01-01', b'1'), subtime('01:01:01', 0b1011)") - result.Check(testkit.Rows(" ")) - result = tk.MustQuery("select subtime('2017-01-01', 1), subtime('2017-01-01 01:01:01', 1), subtime(cast('2017-01-01' as date), 1)") - result.Check(testkit.Rows("2016-12-31 23:59:59 2017-01-01 01:01:00 -00:00:01")) - - result = tk.MustQuery("select addtime(-32073, 0), addtime(0, -32073);") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - result = tk.MustQuery("select addtime(-32073, c), addtime(c, -32073) from t;") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - result = tk.MustQuery("select addtime(a, -32073), addtime(b, -32073), addtime(d, -32073) from t;") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - - result = tk.MustQuery("select subtime(-32073, 0), subtime(0, -32073);") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - result = tk.MustQuery("select subtime(-32073, c), subtime(c, -32073) from t;") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - result = tk.MustQuery("select subtime(a, -32073), subtime(b, -32073), subtime(d, -32073) from t;") - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'", - "Warning|1292|Truncated incorrect time value: '-32073'")) - - // fixed issue #3986 - tk.MustExec("SET SQL_MODE='NO_ENGINE_SUBSTITUTION';") - tk.MustExec("SET TIME_ZONE='+03:00';") - tk.MustExec("DROP TABLE IF EXISTS t;") - tk.MustExec("CREATE TABLE t (ix TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);") - tk.MustExec("INSERT INTO t VALUES (0), (20030101010160), (20030101016001), (20030101240101), (20030132010101), (20031301010101), (20031200000000), (20030000000000);") - result = tk.MustQuery("SELECT CAST(ix AS SIGNED) FROM t;") - result.Check(testkit.Rows("0", "0", "0", "0", "0", "0", "0", "0")) - - // test time - result = tk.MustQuery("select time('2003-12-31 01:02:03')") - result.Check(testkit.Rows("01:02:03")) - result = tk.MustQuery("select time('2003-12-31 01:02:03.000123')") - result.Check(testkit.Rows("01:02:03.000123")) - result = tk.MustQuery("select time('01:02:03.000123')") - result.Check(testkit.Rows("01:02:03.000123")) - result = tk.MustQuery("select time('01:02:03')") - result.Check(testkit.Rows("01:02:03")) - result = tk.MustQuery("select time('-838:59:59.000000')") - result.Check(testkit.Rows("-838:59:59.000000")) - result = tk.MustQuery("select time('-838:59:59.000001')") - result.Check(testkit.Rows("-838:59:59.000000")) - result = tk.MustQuery("select time('-839:59:59.000000')") - result.Check(testkit.Rows("-838:59:59.000000")) - result = tk.MustQuery("select time('840:59:59.000000')") - result.Check(testkit.Rows("838:59:59.000000")) - // FIXME: #issue 4193 - // result = tk.MustQuery("select time('840:59:60.000000')") - // result.Check(testkit.Rows("")) - // result = tk.MustQuery("select time('800:59:59.9999999')") - // result.Check(testkit.Rows("801:00:00.000000")) - // result = tk.MustQuery("select time('12003-12-10 01:02:03.000123')") - // result.Check(testkit.Rows("") - // result = tk.MustQuery("select time('')") - // result.Check(testkit.Rows("") - // result = tk.MustQuery("select time('2003-12-10-10 01:02:03.000123')") - // result.Check(testkit.Rows("00:20:03") - - // Issue 20995 - result = tk.MustQuery("select time('0.1234567')") - result.Check(testkit.Rows("00:00:00.123457")) - - // for hour - result = tk.MustQuery(`SELECT hour("12:13:14.123456"), hour("12:13:14.000010"), hour("272:59:55"), hour(020005), hour(null), hour("27aaaa2:59:55");`) - result.Check(testkit.Rows("12 12 272 2 ")) - - // for hour, issue #4340 - result = tk.MustQuery(`SELECT HOUR(20171222020005);`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`SELECT HOUR(20171222020005.1);`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`SELECT HOUR(20171222020005.1e0);`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`SELECT HOUR("20171222020005");`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`SELECT HOUR("20171222020005.1");`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`select hour(20171222);`) - result.Check(testkit.Rows("")) - result = tk.MustQuery(`select hour(8381222);`) - result.Check(testkit.Rows("838")) - result = tk.MustQuery(`select hour(10000000000);`) - result.Check(testkit.Rows("")) - result = tk.MustQuery(`select hour(10100000000);`) - result.Check(testkit.Rows("")) - result = tk.MustQuery(`select hour(10001000000);`) - result.Check(testkit.Rows("")) - result = tk.MustQuery(`select hour(10101000000);`) - result.Check(testkit.Rows("0")) - - // for minute - result = tk.MustQuery(`SELECT minute("12:13:14.123456"), minute("12:13:14.000010"), minute("272:59:55"), minute(null), minute("27aaaa2:59:55");`) - result.Check(testkit.Rows("13 13 59 ")) - - // for second - result = tk.MustQuery(`SELECT second("12:13:14.123456"), second("12:13:14.000010"), second("272:59:55"), second(null), second("27aaaa2:59:55");`) - result.Check(testkit.Rows("14 14 55 ")) - - // for microsecond - result = tk.MustQuery(`SELECT microsecond("12:00:00.123456"), microsecond("12:00:00.000010"), microsecond(null), microsecond("27aaaa2:59:55");`) - result.Check(testkit.Rows("123456 10 ")) - - // for period_add - result = tk.MustQuery(`SELECT period_add(200807, 2), period_add(200807, -2);`) - result.Check(testkit.Rows("200809 200805")) - result = tk.MustQuery(`SELECT period_add(NULL, 2), period_add(-191, NULL), period_add(NULL, NULL), period_add(12.09, -2), period_add("200207aa", "1aa");`) - result.Check(testkit.Rows(" 200010 200208")) - for _, errPeriod := range []string{ - "period_add(0, 20)", "period_add(0, 0)", "period_add(-1, 1)", "period_add(200013, 1)", "period_add(-200012, 1)", "period_add('', '')", - } { - err := tk.QueryToErr(fmt.Sprintf("SELECT %v;", errPeriod)) - require.Error(t, err, "[expression:1210]Incorrect arguments to period_add") - } - - // for period_diff - result = tk.MustQuery(`SELECT period_diff(200807, 200705), period_diff(200807, 200908);`) - result.Check(testkit.Rows("14 -13")) - result = tk.MustQuery(`SELECT period_diff(NULL, 2), period_diff(-191, NULL), period_diff(NULL, NULL), period_diff(12.09, 2), period_diff("12aa", "11aa");`) - result.Check(testkit.Rows(" 10 1")) - for _, errPeriod := range []string{ - "period_diff(-00013,1)", "period_diff(00013,1)", "period_diff(0, 0)", "period_diff(200013, 1)", "period_diff(5612, 4513)", "period_diff('', '')", - } { - err := tk.QueryToErr(fmt.Sprintf("SELECT %v;", errPeriod)) - require.Error(t, err, "[expression:1210]Incorrect arguments to period_diff") - } - - // TODO: fix `CAST(xx as duration)` and release the test below: - // result = tk.MustQuery(`SELECT hour("aaa"), hour(123456), hour(1234567);`) - // result = tk.MustQuery(`SELECT minute("aaa"), minute(123456), minute(1234567);`) - // result = tk.MustQuery(`SELECT second("aaa"), second(123456), second(1234567);`) - // result = tk.MustQuery(`SELECT microsecond("aaa"), microsecond(123456), microsecond(1234567);`) - - // for time_format - result = tk.MustQuery("SELECT TIME_FORMAT('150:02:28', '%H:%i:%s %p');") - result.Check(testkit.Rows("150:02:28 AM")) - result = tk.MustQuery("SELECT TIME_FORMAT('bad string', '%H:%i:%s %p');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("SELECT TIME_FORMAT(null, '%H:%i:%s %p');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("SELECT TIME_FORMAT(123, '%H:%i:%s %p');") - result.Check(testkit.Rows("00:01:23 AM")) - result = tk.MustQuery("SELECT TIME_FORMAT('24:00:00', '%r');") - result.Check(testkit.Rows("12:00:00 AM")) - result = tk.MustQuery("SELECT TIME_FORMAT('25:00:00', '%r');") - result.Check(testkit.Rows("01:00:00 AM")) - result = tk.MustQuery("SELECT TIME_FORMAT('24:00:00', '%l %p');") - result.Check(testkit.Rows("12 AM")) - - // for date_format - result = tk.MustQuery(`SELECT DATE_FORMAT('2017-06-15', '%W %M %e %Y %r %y');`) - result.Check(testkit.Rows("Thursday June 15 2017 12:00:00 AM 17")) - result = tk.MustQuery(`SELECT DATE_FORMAT(151113102019.12, '%W %M %e %Y %r %y');`) - result.Check(testkit.Rows("Friday November 13 2015 10:20:19 AM 15")) - result = tk.MustQuery(`SELECT DATE_FORMAT('0000-00-00', '%W %M %e %Y %r %y');`) - result.Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) - result = tk.MustQuery(`SELECT DATE_FORMAT('0', '%W %M %e %Y %r %y'), DATE_FORMAT('0.0', '%W %M %e %Y %r %y'), DATE_FORMAT(0, 0);`) - result.Check(testkit.Rows(" 0")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Incorrect time value: '0'", - "Warning|1292|Incorrect datetime value: '0.0'")) - result = tk.MustQuery(`SELECT DATE_FORMAT(0, '%W %M %e %Y %r %y'), DATE_FORMAT(0.0, '%W %M %e %Y %r %y');`) - result.Check(testkit.Rows(" ")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - - // for yearweek - result = tk.MustQuery(`select yearweek("2014-12-27"), yearweek("2014-29-27"), yearweek("2014-00-27"), yearweek("2014-12-27 12:38:32"), yearweek("2014-12-27 12:38:32.1111111"), yearweek("2014-12-27 12:90:32"), yearweek("2014-12-27 89:38:32.1111111");`) - result.Check(testkit.Rows("201451 201451 201451 ")) - result = tk.MustQuery(`select yearweek(12121), yearweek(1.00009), yearweek("aaaaa"), yearweek(""), yearweek(NULL);`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select yearweek("0000-00-00"), yearweek("2019-01-29", "aa"), yearweek("2011-01-01", null);`) - result.Check(testkit.Rows(" 201904 201052")) - - // for dayOfWeek, dayOfMonth, dayOfYear - result = tk.MustQuery(`select dayOfWeek(null), dayOfWeek("2017-08-12"), dayOfWeek("0000-00-00"), dayOfWeek("2017-00-00"), dayOfWeek("0000-00-00 12:12:12"), dayOfWeek("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 7 ")) - result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 224 ")) - result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 12 0 0 0 0")) - - tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") - result = tk.MustQuery(`select dayOfWeek(null), dayOfWeek("2017-08-12"), dayOfWeek("0000-00-00"), dayOfWeek("2017-00-00"), dayOfWeek("0000-00-00 12:12:12"), dayOfWeek("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 7 ")) - result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 224 ")) - result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) - result.Check(testkit.Rows(" 12 0 0 0")) - - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a bigint)`) - tk.MustExec(`insert into t value(1)`) - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - - _, err = tk.Exec("insert into t value(dayOfWeek('0000-00-00'))") - require.True(t, types.ErrWrongValue.Equal(err), "%v", err) - _, err = tk.Exec(`update t set a = dayOfWeek("0000-00-00")`) - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`delete from t where a = dayOfWeek(123)`) - require.NoError(t, err) - - tk.MustExec("insert into t value(dayOfMonth('2017-00-00'))") - tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") - tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) - tk.MustExec("set sql_mode = 'NO_ZERO_DATE';") - tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") - tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") - tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) - tk.MustExec("set sql_mode = 'NO_ZERO_DATE,STRICT_TRANS_TABLES';") - _, err = tk.Exec("insert into t value(dayOfMonth('0000-00-00'))") - require.True(t, types.ErrWrongValue.Equal(err)) - tk.MustExec("insert into t value(0)") - _, err = tk.Exec(`update t set a = dayOfMonth("0000-00-00")`) - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`delete from t where a = dayOfMonth(123)`) - require.NoError(t, err) - - _, err = tk.Exec("insert into t value(dayOfYear('0000-00-00'))") - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`update t set a = dayOfYear("0000-00-00")`) - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`delete from t where a = dayOfYear(123)`) - require.NoError(t, err) - - tk.MustExec("set sql_mode = ''") - - // for unix_timestamp - tk.MustExec("SET time_zone = '+00:00';") - tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00.000001');").Check(testkit.Rows("0.000000")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00.999999');").Check(testkit.Rows("0.000000")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:01.000000');").Check(testkit.Rows("1.000000")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:07.999999');").Check(testkit.Rows("2147483647.999999")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:08.000000');").Check(testkit.Rows("2147483648.000000")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-18 23:59:59.999999');").Check(testkit.Rows("32536771199.999999")) - tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-19 00:00:00.000000');").Check(testkit.Rows("0.000000")) - - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113);") - result.Check(testkit.Rows("1447372800")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(20151113);") - result.Check(testkit.Rows("1447372800")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019);") - result.Check(testkit.Rows("1447410019")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019e0);") - result.Check(testkit.Rows("1447410019.000000")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(15111310201912e-2);") - result.Check(testkit.Rows("1447410019.120000")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019.12);") - result.Check(testkit.Rows("1447410019.12")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019.1234567);") - result.Check(testkit.Rows("1447410019.123457")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(20151113102019);") - result.Check(testkit.Rows("1447410019")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 10:20:19');") - result.Check(testkit.Rows("1447410019")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 10:20:19.012');") - result.Check(testkit.Rows("1447410019.012")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00');") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1969-12-31 23:59:59');") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-13-01 00:00:00');") - // FIXME: MySQL returns 0 here. - result.Check(testkit.Rows("")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:07.999999');") - result.Check(testkit.Rows("2147483647.999999")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-18 23:59:59.999999');") - result.Check(testkit.Rows("32536771199.999999")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-19 00:00:00');") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP(0);") - result.Check(testkit.Rows("0")) - // result = tk.MustQuery("SELECT UNIX_TIMESTAMP(-1);") - // result.Check(testkit.Rows("0")) - // result = tk.MustQuery("SELECT UNIX_TIMESTAMP(12345);") - // result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2017-01-01')") - result.Check(testkit.Rows("1483228800")) - // Test different time zone. - tk.MustExec("SET time_zone = '+08:00';") - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00');") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 08:00:00');") - result.Check(testkit.Rows("0")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 18:20:19.012'), UNIX_TIMESTAMP('2015-11-13 18:20:19.0123');") - result.Check(testkit.Rows("1447410019.012 1447410019.0123")) - result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 11:14:07.999999');") - result.Check(testkit.Rows("2147483647.999999")) - - result = tk.MustQuery("SELECT TIME_FORMAT('bad string', '%H:%i:%s %p');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("SELECT TIME_FORMAT(null, '%H:%i:%s %p');") - result.Check(testkit.Rows("")) - result = tk.MustQuery("SELECT TIME_FORMAT(123, '%H:%i:%s %p');") - result.Check(testkit.Rows("00:01:23 AM")) - - // for monthname - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a varchar(10))`) - tk.MustExec(`insert into t value("abc")`) - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - - tk.MustExec("insert into t value(monthname('0000-00-00'))") - tk.MustExec(`update t set a = monthname("0000-00-00")`) - tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") - tk.MustExec("insert into t value(monthname('0000-00-00'))") - tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") - tk.MustExec(`update t set a = monthname("0000-00-00")`) - tk.MustExec("set sql_mode = ''") - tk.MustExec("insert into t value(monthname('0000-00-00'))") - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE'") - _, err = tk.Exec(`update t set a = monthname("0000-00-00")`) - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`delete from t where a = monthname(123)`) - require.NoError(t, err) - result = tk.MustQuery(`select monthname("2017-12-01"), monthname("0000-00-00"), monthname("0000-01-00"), monthname("0000-01-00 00:00:00")`) - result.Check(testkit.Rows("December January January")) - tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") - - // for dayname - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a varchar(10))`) - tk.MustExec(`insert into t value("abc")`) - tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") - - _, err = tk.Exec("insert into t value(dayname('0000-00-00'))") - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`update t set a = dayname("0000-00-00")`) - require.True(t, types.ErrWrongValue.Equal(err)) - _, err = tk.Exec(`delete from t where a = dayname(123)`) - require.NoError(t, err) - result = tk.MustQuery(`select dayname("2017-12-01"), dayname("0000-00-00"), dayname("0000-01-00"), dayname("0000-01-00 00:00:00")`) - result.Check(testkit.Rows("Friday ")) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'", - "Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'", - "Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'")) - // for dayname implicit cast to boolean and real - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07')`) - result.Check(testkit.Rows()) - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is true`) - result.Check(testkit.Rows()) - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is false`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08')`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is true`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is false`) - result.Check(testkit.Rows()) - result = tk.MustQuery(`select cast(dayname("2016-03-07") as double), cast(dayname("2016-03-08") as double)`) - result.Check(testkit.Rows("0 1")) - - // for sec_to_time - result = tk.MustQuery("select sec_to_time(NULL)") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select sec_to_time(2378), sec_to_time(3864000), sec_to_time(-3864000)") - result.Check(testkit.Rows("00:39:38 838:59:59 -838:59:59")) - result = tk.MustQuery("select sec_to_time(86401.4), sec_to_time(-86401.4), sec_to_time(864014e-1), sec_to_time(-864014e-1), sec_to_time('86401.4'), sec_to_time('-86401.4')") - result.Check(testkit.Rows("24:00:01.4 -24:00:01.4 24:00:01.400000 -24:00:01.400000 24:00:01.400000 -24:00:01.400000")) - result = tk.MustQuery("select sec_to_time(86401.54321), sec_to_time(86401.543212345)") - result.Check(testkit.Rows("24:00:01.54321 24:00:01.543212")) - result = tk.MustQuery("select sec_to_time('123.4'), sec_to_time('123.4567891'), sec_to_time('123')") - result.Check(testkit.Rows("00:02:03.400000 00:02:03.456789 00:02:03.000000")) - - // for time_to_sec - result = tk.MustQuery("select time_to_sec(NULL)") - result.Check(testkit.Rows("")) - result = tk.MustQuery("select time_to_sec('22:23:00'), time_to_sec('00:39:38'), time_to_sec('23:00'), time_to_sec('00:00'), time_to_sec('00:00:00'), time_to_sec('23:59:59')") - result.Check(testkit.Rows("80580 2378 82800 0 0 86399")) - result = tk.MustQuery("select time_to_sec('1:0'), time_to_sec('1:00'), time_to_sec('1:0:0'), time_to_sec('-02:00'), time_to_sec('-02:00:05'), time_to_sec('020005')") - result.Check(testkit.Rows("3600 3600 3600 -7200 -7205 7205")) - result = tk.MustQuery("select time_to_sec('20171222020005'), time_to_sec(020005), time_to_sec(20171222020005), time_to_sec(171222020005)") - result.Check(testkit.Rows("7205 7205 7205 7205")) - - // for str_to_date - result = tk.MustQuery("select str_to_date('01-01-2017', '%d-%m-%Y'), str_to_date('59:20:12 01-01-2017', '%s:%i:%H %d-%m-%Y'), str_to_date('59:20:12', '%s:%i:%H')") - result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) - result = tk.MustQuery("select str_to_date('aaa01-01-2017', 'aaa%d-%m-%Y'), str_to_date('59:20:12 aaa01-01-2017', '%s:%i:%H aaa%d-%m-%Y'), str_to_date('59:20:12aaa', '%s:%i:%Haaa')") - result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) - - result = tk.MustQuery("select str_to_date('01-01-2017', '%d'), str_to_date('59', '%d-%Y')") - // TODO: MySQL returns " ". - result.Check(testkit.Rows("0000-00-01 ")) - result = tk.MustQuery("show warnings") - result.Sort().Check(testkit.RowsWithSep("|", - "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'", - "Warning|1292|Truncated incorrect datetime value: '01-01-2017'")) - - result = tk.MustQuery("select str_to_date('2018-6-1', '%Y-%m-%d'), str_to_date('2018-6-1', '%Y-%c-%d'), str_to_date('59:20:1', '%s:%i:%k'), str_to_date('59:20:1', '%s:%i:%l')") - result.Check(testkit.Rows("2018-06-01 2018-06-01 01:20:59 01:20:59")) - - result = tk.MustQuery("select str_to_date('2020-07-04 11:22:33 PM c', '%Y-%m-%d %r')") - result.Check(testkit.Rows("2020-07-04 23:22:33")) - result = tk.MustQuery("show warnings") - result.Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect datetime value: '2020-07-04 11:22:33 PM c'")) - - result = tk.MustQuery("select str_to_date('11:22:33 PM', ' %r')") - result.Check(testkit.Rows("23:22:33")) - result = tk.MustQuery("show warnings") - result.Check(testkit.Rows()) - - // for maketime - tk.MustExec(`drop table if exists t`) - tk.MustExec(`create table t(a double, b float, c decimal(10,4));`) - tk.MustExec(`insert into t value(1.23, 2.34, 3.1415)`) - result = tk.MustQuery("select maketime(1,1,a), maketime(2,2,b), maketime(3,3,c) from t;") - result.Check(testkit.Rows("01:01:01.230000 02:02:02.340000 03:03:03.1415")) - result = tk.MustQuery("select maketime(12, 13, 14), maketime('12', '15', 30.1), maketime(0, 1, 59.1), maketime(0, 1, '59.1'), maketime(0, 1, 59.5)") - result.Check(testkit.Rows("12:13:14 12:15:30.1 00:01:59.1 00:01:59.100000 00:01:59.5")) - result = tk.MustQuery("select maketime(12, 15, 60), maketime(12, 15, '60'), maketime(12, 60, 0), maketime(12, 15, null)") - result.Check(testkit.Rows(" ")) - result = tk.MustQuery("select maketime('', '', ''), maketime('h', 'm', 's');") - result.Check(testkit.Rows("00:00:00.000000 00:00:00.000000")) - - // for get_format - result = tk.MustQuery(`select GET_FORMAT(DATE,'USA'), GET_FORMAT(DATE,'JIS'), GET_FORMAT(DATE,'ISO'), GET_FORMAT(DATE,'EUR'), - GET_FORMAT(DATE,'INTERNAL'), GET_FORMAT(DATETIME,'USA') , GET_FORMAT(DATETIME,'JIS'), GET_FORMAT(DATETIME,'ISO'), - GET_FORMAT(DATETIME,'EUR') , GET_FORMAT(DATETIME,'INTERNAL'), GET_FORMAT(TIME,'USA') , GET_FORMAT(TIME,'JIS'), - GET_FORMAT(TIME,'ISO'), GET_FORMAT(TIME,'EUR'), GET_FORMAT(TIME,'INTERNAL')`) - result.Check(testkit.Rows("%m.%d.%Y %Y-%m-%d %Y-%m-%d %d.%m.%Y %Y%m%d %Y-%m-%d %H.%i.%s %Y-%m-%d %H:%i:%s %Y-%m-%d %H:%i:%s %Y-%m-%d %H.%i.%s %Y%m%d%H%i%s %h:%i:%s %p %H:%i:%s %H:%i:%s %H.%i.%s %H%i%s")) - - // for convert_tz - result = tk.MustQuery(`select convert_tz("2004-01-01 12:00:00", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01234567", "+00:00", "+10:32");`) - result.Check(testkit.Rows("2004-01-01 22:32:00 2004-01-01 22:32:00.01 2004-01-01 22:32:00.012346")) - result = tk.MustQuery(`select convert_tz(20040101, "+00:00", "+10:32"), convert_tz(20040101.01, "+00:00", "+10:32"), convert_tz(20040101.01234567, "+00:00", "+10:32");`) - result.Check(testkit.Rows("2004-01-01 10:32:00 2004-01-01 10:32:00.00 2004-01-01 10:32:00.000000")) - result = tk.MustQuery(`select convert_tz(NULL, "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", NULL, "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", NULL);`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select convert_tz("a", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "a", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "a");`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select convert_tz("", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "");`) - result.Check(testkit.Rows(" ")) - result = tk.MustQuery(`select convert_tz("0", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "0", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "0");`) - result.Check(testkit.Rows(" ")) - - // for from_unixtime - tk.MustExec(`set @@session.time_zone = "+08:00"`) - result = tk.MustQuery(`select from_unixtime(20170101), from_unixtime(20170101.9999999), from_unixtime(20170101.999), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x"), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x")`) - result.Check(testkit.Rows("1970-08-22 18:48:21 1970-08-22 18:48:22.000000 1970-08-22 18:48:21.999 1970 22nd August 06:48:21 1970 1970 22nd August 06:48:21 1970")) - tk.MustExec(`set @@session.time_zone = "+00:00"`) - result = tk.MustQuery(`select from_unixtime(20170101), from_unixtime(20170101.9999999), from_unixtime(20170101.999), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x"), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x")`) - result.Check(testkit.Rows("1970-08-22 10:48:21 1970-08-22 10:48:22.000000 1970-08-22 10:48:21.999 1970 22nd August 10:48:21 1970 1970 22nd August 10:48:21 1970")) - tk.MustExec(`set @@session.time_zone = @@global.time_zone`) - - // for extract - result = tk.MustQuery(`select extract(day from '800:12:12'), extract(hour from '800:12:12'), extract(month from 20170101), extract(day_second from '2017-01-01 12:12:12')`) - result.Check(testkit.Rows("12 800 1 1121212")) - result = tk.MustQuery("select extract(day_microsecond from '2017-01-01 12:12:12'), extract(day_microsecond from '01 12:12:12'), extract(day_microsecond from '12:12:12'), extract(day_microsecond from '01 00:00:00.89')") - result.Check(testkit.Rows("1121212000000 361212000000 121212000000 240000890000")) - result = tk.MustQuery("select extract(day_second from '2017-01-01 12:12:12'), extract(day_second from '01 12:12:12'), extract(day_second from '12:12:12'), extract(day_second from '01 00:00:00.89')") - result.Check(testkit.Rows("1121212 361212 121212 240000")) - result = tk.MustQuery("select extract(day_minute from '2017-01-01 12:12:12'), extract(day_minute from '01 12:12:12'), extract(day_minute from '12:12:12'), extract(day_minute from '01 00:00:00.89')") - result.Check(testkit.Rows("11212 3612 1212 2400")) - result = tk.MustQuery("select extract(day_hour from '2017-01-01 12:12:12'), extract(day_hour from '01 12:12:12'), extract(day_hour from '12:12:12'), extract(day_hour from '01 00:00:00.89')") - result.Check(testkit.Rows("112 36 12 24")) - result = tk.MustQuery("select extract(day_microsecond from cast('2017-01-01 12:12:12' as datetime)), extract(day_second from cast('2017-01-01 12:12:12' as datetime)), extract(day_minute from cast('2017-01-01 12:12:12' as datetime)), extract(day_hour from cast('2017-01-01 12:12:12' as datetime))") - result.Check(testkit.Rows("1121212000000 1121212 11212 112")) - result = tk.MustQuery("select extract(day_microsecond from cast(20010101020304.050607 as decimal(20,6))), extract(day_second from cast(20010101020304.050607 as decimal(20,6))), extract(day_minute from cast(20010101020304.050607 as decimal(20,6))), extract(day_hour from cast(20010101020304.050607 as decimal(20,6))), extract(day from cast(20010101020304.050607 as decimal(20,6)))") - result.Check(testkit.Rows("1020304050607 1020304 10203 102 1")) - result = tk.MustQuery("select extract(day_microsecond from cast(1020304.050607 as decimal(20,6))), extract(day_second from cast(1020304.050607 as decimal(20,6))), extract(day_minute from cast(1020304.050607 as decimal(20,6))), extract(day_hour from cast(1020304.050607 as decimal(20,6))), extract(day from cast(1020304.050607 as decimal(20,6)))") - result.Check(testkit.Rows("1020304050607 1020304 10203 102 4")) - - // for adddate, subdate - dateArithmeticalTests := []struct { - Date string - Interval string - Unit string - AddResult string - SubResult string - }{ - {"\"2011-11-11\"", "1", "DAY", "2011-11-12", "2011-11-10"}, - {"NULL", "1", "DAY", "", ""}, - {"\"2011-11-11\"", "NULL", "DAY", "", ""}, - {"\"2011-11-11 10:10:10\"", "1000", "MICROSECOND", "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000"}, - {"\"2011-11-11 10:10:10\"", "\"10\"", "SECOND", "2011-11-11 10:10:20", "2011-11-11 10:10:00"}, - {"\"2011-11-11 10:10:10\"", "\"10\"", "MINUTE", "2011-11-11 10:20:10", "2011-11-11 10:00:10"}, - {"\"2011-11-11 10:10:10\"", "\"10\"", "HOUR", "2011-11-11 20:10:10", "2011-11-11 00:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"11\"", "DAY", "2011-11-22 10:10:10", "2011-10-31 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"2\"", "WEEK", "2011-11-25 10:10:10", "2011-10-28 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"2\"", "MONTH", "2012-01-11 10:10:10", "2011-09-11 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"4\"", "QUARTER", "2012-11-11 10:10:10", "2010-11-11 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"2\"", "YEAR", "2013-11-11 10:10:10", "2009-11-11 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"10.00100000\"", "SECOND_MICROSECOND", "2011-11-11 10:10:20.100000", "2011-11-11 10:09:59.900000"}, - {"\"2011-11-11 10:10:10\"", "\"10.0010000000\"", "SECOND_MICROSECOND", "2011-11-11 10:10:30", "2011-11-11 10:09:50"}, - {"\"2011-11-11 10:10:10\"", "\"10.0010000010\"", "SECOND_MICROSECOND", "2011-11-11 10:10:30.000010", "2011-11-11 10:09:49.999990"}, - {"\"2011-11-11 10:10:10\"", "\"10:10.100\"", "MINUTE_MICROSECOND", "2011-11-11 10:20:20.100000", "2011-11-11 09:59:59.900000"}, - {"\"2011-11-11 10:10:10\"", "\"10:10\"", "MINUTE_SECOND", "2011-11-11 10:20:20", "2011-11-11 10:00:00"}, - {"\"2011-11-11 10:10:10\"", "\"10:10:10.100\"", "HOUR_MICROSECOND", "2011-11-11 20:20:20.100000", "2011-11-10 23:59:59.900000"}, - {"\"2011-11-11 10:10:10\"", "\"10:10:10\"", "HOUR_SECOND", "2011-11-11 20:20:20", "2011-11-11 00:00:00"}, - {"\"2011-11-11 10:10:10\"", "\"10:10\"", "HOUR_MINUTE", "2011-11-11 20:20:10", "2011-11-11 00:00:10"}, - {"\"2011-11-11 10:10:10\"", "\"11 10:10:10.100\"", "DAY_MICROSECOND", "2011-11-22 20:20:20.100000", "2011-10-30 23:59:59.900000"}, - {"\"2011-11-11 10:10:10\"", "\"11 10:10:10\"", "DAY_SECOND", "2011-11-22 20:20:20", "2011-10-31 00:00:00"}, - {"\"2011-11-11 10:10:10\"", "\"11 10:10\"", "DAY_MINUTE", "2011-11-22 20:20:10", "2011-10-31 00:00:10"}, - {"\"2011-11-11 10:10:10\"", "\"11 10\"", "DAY_HOUR", "2011-11-22 20:10:10", "2011-10-31 00:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"11-1\"", "YEAR_MONTH", "2022-12-11 10:10:10", "2000-10-11 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"11-11\"", "YEAR_MONTH", "2023-10-11 10:10:10", "1999-12-11 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"20\"", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "19.88", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"19.88\"", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"prefix19suffix\"", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"20-11\"", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"20,11\"", "daY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"1000\"", "dAy", "2014-08-07 10:10:10", "2009-02-14 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "\"true\"", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10"}, - {"\"2011-11-11 10:10:10\"", "true", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10"}, - {"\"2011-11-11\"", "1", "DAY", "2011-11-12", "2011-11-10"}, - {"\"2011-11-11\"", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"\"2011-11-11\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"\"2011-11-11\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - {"\"2011-11-11\"", "\"10:10\"", "HOUR_MINUTE", "2011-11-11 10:10:00", "2011-11-10 13:50:00"}, - {"\"2011-11-11\"", "\"10:10:10\"", "HOUR_SECOND", "2011-11-11 10:10:10", "2011-11-10 13:49:50"}, - {"\"2011-11-11\"", "\"10:10:10.101010\"", "HOUR_MICROSECOND", "2011-11-11 10:10:10.101010", "2011-11-10 13:49:49.898990"}, - {"\"2011-11-11\"", "\"10:10\"", "MINUTE_SECOND", "2011-11-11 00:10:10", "2011-11-10 23:49:50"}, - {"\"2011-11-11\"", "\"10:10.101010\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.101010", "2011-11-10 23:49:49.898990"}, - {"\"2011-11-11\"", "\"10.101010\"", "SECOND_MICROSECOND", "2011-11-11 00:00:10.101010", "2011-11-10 23:59:49.898990"}, - {"\"2011-11-11 00:00:00\"", "1", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, - {"\"2011-11-11 00:00:00\"", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"\"2011-11-11 00:00:00\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"\"2011-11-11 00:00:00\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - {"\"2011-11-11 00:00:00.500\"", "500000", "MICROSECOND", "2011-11-11 00:00:01", "2011-11-11 00:00:00"}, - - {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "2011-11-11 00:00:00", "2011-11-11 00:00:00"}, - {"\"20111111 10:10:10\"", "\"1\"", "DAY", "", ""}, - {"\"2011-11-11\"", "\"10\"", "SECOND_MICROSECOND", "2011-11-11 00:00:00.100000", "2011-11-10 23:59:59.900000"}, - {"\"2011-11-11\"", "\"10.0000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - {"\"2011-11-11\"", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, - - {"cast(\"2011-11-11\" as datetime)", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, - {"cast(\"2011-11-11\" as datetime)", "\"1000000\"", "MICROSECOND", "2011-11-11 00:00:01.000000", "2011-11-10 23:59:59.000000"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "1", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - - {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"1\"", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "SECOND", "2011-11-11 00:00:10.000000", "2011-11-10 23:59:50.000000"}, - - {"cast(\"2011-11-11\" as date)", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, - {"cast(\"2011-11-11\" as date)", "\"1000000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:01.000000", "2011-11-10 23:59:59.000000"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "1", "DAY", "2011-11-12", "2011-11-10"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, - - {"cast(\"2011-11-11 00:00:00\" as date)", "\"1\"", "DAY", "2011-11-12", "2011-11-10"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, - {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "SECOND", "2011-11-11 00:00:10.000000", "2011-11-10 23:59:50.000000"}, - - // interval decimal support - {"\"2011-01-01 00:00:00\"", "10.10", "YEAR_MONTH", "2021-11-01 00:00:00", "2000-03-01 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "DAY_HOUR", "2011-01-11 10:00:00", "2010-12-21 14:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_MINUTE", "2011-01-01 10:10:00", "2010-12-31 13:50:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "DAY_MINUTE", "2011-01-01 10:10:00", "2010-12-31 13:50:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "DAY_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, - {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, - {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, - {"\"2011-01-01 00:00:00\"", "10.10", "DAY_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, - {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, - {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, - {"\"2011-01-01 00:00:00\"", "10.10", "SECOND_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, - {"\"2011-01-01 00:00:00\"", "10.10", "YEAR", "2021-01-01 00:00:00", "2001-01-01 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "QUARTER", "2013-07-01 00:00:00", "2008-07-01 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "MONTH", "2011-11-01 00:00:00", "2010-03-01 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "WEEK", "2011-03-12 00:00:00", "2010-10-23 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "DAY", "2011-01-11 00:00:00", "2010-12-22 00:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "HOUR", "2011-01-01 10:00:00", "2010-12-31 14:00:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE", "2011-01-01 00:10:00", "2010-12-31 23:50:00"}, - {"\"2011-01-01 00:00:00\"", "10.10", "SECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, - {"\"2011-01-01 00:00:00\"", "10.10", "MICROSECOND", "2011-01-01 00:00:00.000010", "2010-12-31 23:59:59.999990"}, - {"\"2011-01-01 00:00:00\"", "10.90", "MICROSECOND", "2011-01-01 00:00:00.000011", "2010-12-31 23:59:59.999989"}, - {"cast(\"2011-01-01\" as date)", "1.1", "SECOND", "2011-01-01 00:00:01.1", "2010-12-31 23:59:58.9"}, - {"cast(\"2011-01-01\" as datetime)", "1.1", "SECOND", "2011-01-01 00:00:01.1", "2010-12-31 23:59:58.9"}, - {"cast(\"2011-01-01\" as datetime(3))", "1.1", "SECOND", "2011-01-01 00:00:01.100", "2010-12-31 23:59:58.900"}, - - {"\"2009-01-01\"", "6/4", "HOUR_MINUTE", "2009-01-04 12:20:00", "2008-12-28 11:40:00"}, - {"\"2009-01-01\"", "6/0", "HOUR_MINUTE", "", ""}, - {"\"1970-01-01 12:00:00\"", "CAST(6/4 AS DECIMAL(3,1))", "HOUR_MINUTE", "1970-01-01 13:05:00", "1970-01-01 10:55:00"}, - // for issue #8077 - {"\"2012-01-02\"", "\"prefix8\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, - {"\"2012-01-02\"", "\"prefix8prefix\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, - {"\"2012-01-02\"", "\"8:00\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, - {"\"2012-01-02\"", "\"8:00:00\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, - } - for _, tc := range dateArithmeticalTests { - addDate := fmt.Sprintf("select adddate(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) - subDate := fmt.Sprintf("select subdate(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) - result = tk.MustQuery(addDate) - result.Check(testkit.Rows(tc.AddResult)) - result = tk.MustQuery(subDate) - result.Check(testkit.Rows(tc.SubResult)) - } - - // Customized check for the cases of adddate(time, ...) - it returns datetime with current date padded. - // 1. Check if the result contains space, that is, it must contain YMD part. - // 2. Check if the result's suffix matches expected, that is, the HMS part is an exact match. - checkHmsMatch := func(actual []string, expected []interface{}) bool { - return strings.Contains(actual[0], " ") && strings.HasSuffix(actual[0], expected[0].(string)) - } - - // for date_add/sub(duration, ...) - dateAddSubDurationAnyTests := []struct { - Date string - Interval string - Unit string - AddResult string - SubResult string - checkHmsOnly bool // Duration + day returns datetime with current date padded, only check HMS part for them. - }{ - {"cast('01:02:03' as time)", "'1000'", "MICROSECOND", "01:02:03.001000", "01:02:02.999000", false}, - {"cast('01:02:03' as time)", "1000", "MICROSECOND", "01:02:03.001000", "01:02:02.999000", false}, - {"cast('01:02:03' as time)", "'1'", "SECOND", "01:02:04.000000", "01:02:02.000000", false}, - {"cast('01:02:03' as time)", "1", "SECOND", "01:02:04", "01:02:02", false}, - {"cast('01:02:03' as time)", "'1.1'", "SECOND", "01:02:04.100000", "01:02:01.900000", false}, - {"cast('01:02:03' as time)", "1.1", "SECOND", "01:02:04.1", "01:02:01.9", false}, - {"cast('01:02:03' as time(3))", "1.1", "SECOND", "01:02:04.100", "01:02:01.900", false}, - {"cast('01:02:03' as time)", "cast(1.1 as decimal(10, 3))", "SECOND", "01:02:04.100", "01:02:01.900", false}, - {"cast('01:02:03' as time)", "cast('1.5' as double)", "SECOND", "01:02:04.500000", "01:02:01.500000", false}, - {"cast('01:02:03' as time)", "1", "DAY_MICROSECOND", "01:02:03.100000", "01:02:02.900000", false}, - {"cast('01:02:03' as time)", "1.1", "DAY_MICROSECOND", "01:02:04.100000", "01:02:01.900000", false}, - {"cast('01:02:03' as time)", "100", "DAY_MICROSECOND", "01:02:03.100000", "01:02:02.900000", false}, - {"cast('01:02:03' as time)", "1000000", "DAY_MICROSECOND", "01:02:04.000000", "01:02:02.000000", false}, - {"cast('01:02:03' as time)", "1", "DAY_SECOND", "01:02:04", "01:02:02", true}, - {"cast('01:02:03' as time)", "1.1", "DAY_SECOND", "01:03:04", "01:01:02", true}, - {"cast('01:02:03' as time)", "1", "DAY_MINUTE", "01:03:03", "01:01:03", true}, - {"cast('01:02:03' as time)", "1.1", "DAY_MINUTE", "02:03:03", "00:01:03", true}, - {"cast('01:02:03' as time)", "1", "DAY_HOUR", "02:02:03", "00:02:03", true}, - {"cast('01:02:03' as time)", "1.1", "DAY_HOUR", "02:02:03", "00:02:03", true}, - {"cast('01:02:03' as time)", "1", "DAY", "01:02:03", "01:02:03", true}, - {"cast('01:02:03' as time)", "1", "WEEK", "01:02:03", "01:02:03", true}, - {"cast('01:02:03' as time)", "1", "MONTH", "01:02:03", "01:02:03", true}, - {"cast('01:02:03' as time)", "1", "QUARTER", "01:02:03", "01:02:03", true}, - {"cast('01:02:03' as time)", "1", "YEAR", "01:02:03", "01:02:03", true}, - {"cast('01:02:03' as time)", "1", "YEAR_MONTH", "01:02:03", "01:02:03", true}, - } - for _, tc := range dateAddSubDurationAnyTests { - addDate := fmt.Sprintf("select date_add(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) - subDate := fmt.Sprintf("select date_sub(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) - if tc.checkHmsOnly { - result = tk.MustQuery(addDate) - result.CheckWithFunc(testkit.Rows(tc.AddResult), checkHmsMatch) - result = tk.MustQuery(subDate) - result.CheckWithFunc(testkit.Rows(tc.SubResult), checkHmsMatch) - } else { - result = tk.MustQuery(addDate) - result.Check(testkit.Rows(tc.AddResult)) - result = tk.MustQuery(subDate) - result.Check(testkit.Rows(tc.SubResult)) - } - } - - tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast(1 as decimal))`).Check(testkit.Rows("2000-01-31 00:00:00")) - tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast(null as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select subdate(cast(null as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast("xxx" as decimal))`).Check(testkit.Rows("2000-02-01 00:00:00")) - tk.MustQuery(`select subdate(cast("xxx" as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("1999-12-31")) - tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast("xxx" as decimal))`).Check(testkit.Rows("2000-01-01")) - tk.MustQuery(`select subdate(cast("abc" as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select subdate(cast(null as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast(null as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(1 as decimal))`).Check(testkit.Rows("2000-02-02 00:00:00")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(null as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast(null as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast("xxx" as decimal))`).Check(testkit.Rows("2000-02-01 00:00:00")) - tk.MustQuery(`select adddate(cast("xxx" as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("2000-02-02 00:00:00")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(null as SIGNED))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast(null as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast("xxx" as SIGNED))`).Check(testkit.Rows("2000-02-01 00:00:00")) - tk.MustQuery(`select adddate(cast("xxx" as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("")) - tk.MustQuery(`select adddate(20100101, cast(1 as decimal))`).Check(testkit.Rows("2010-01-02")) - tk.MustQuery(`select adddate(cast('10:10:10' as time), 1)`).CheckWithFunc(testkit.Rows("10:10:10"), checkHmsMatch) - tk.MustQuery(`select adddate(cast('10:10:10' as time), cast(1 as decimal))`).CheckWithFunc(testkit.Rows("10:10:10"), checkHmsMatch) - - // for localtime, localtimestamp - result = tk.MustQuery(`select localtime() = now(), localtime = now(), localtimestamp() = now(), localtimestamp = now()`) - result.Check(testkit.Rows("1 1 1 1")) - - // for current_timestamp, current_timestamp() - result = tk.MustQuery(`select current_timestamp() = now(), current_timestamp = now()`) - result.Check(testkit.Rows("1 1")) - - // for tidb_parse_tso - tk.MustExec("SET time_zone = '+00:00';") - result = tk.MustQuery(`select tidb_parse_tso(404411537129996288)`) - result.Check(testkit.Rows("2018-11-20 09:53:04.877000")) - result = tk.MustQuery(`select tidb_parse_tso("404411537129996288")`) - result.Check(testkit.Rows("2018-11-20 09:53:04.877000")) - result = tk.MustQuery(`select tidb_parse_tso(1)`) - result.Check(testkit.Rows("1970-01-01 00:00:00.000000")) - result = tk.MustQuery(`select tidb_parse_tso(0)`) - result.Check(testkit.Rows("")) - result = tk.MustQuery(`select tidb_parse_tso(-1)`) - result.Check(testkit.Rows("")) - - // for tidb_parse_tso_logical - result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996288)`) - result.Check(testkit.Rows("0")) - result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996289)`) - result.Check(testkit.Rows("1")) - result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996290)`) - result.Check(testkit.Rows("2")) - result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(-1)`) - result.Check(testkit.Rows("")) - - // for tidb_bounded_staleness - tk.MustExec("SET time_zone = '+00:00';") - tt := time.Now().UTC() - ts := oracle.GoTimeToTS(tt) - tidbBoundedStalenessTests := []struct { - sql string - injectSafeTS uint64 - expect string - }{ - { - sql: `select tidb_bounded_staleness(DATE_SUB(NOW(), INTERVAL 600 SECOND), DATE_ADD(NOW(), INTERVAL 600 SECOND))`, - injectSafeTS: ts, - expect: tt.Format(types.TimeFSPFormat[:len(types.TimeFSPFormat)-3]), - }, - { - sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 13:00:00.000")`, - injectSafeTS: func() uint64 { - tt, err := time.Parse("2006-01-02 15:04:05.000", "2021-04-27 13:30:04.877") - require.NoError(t, err) - return oracle.GoTimeToTS(tt) - }(), - expect: "2021-04-27 13:00:00.000", - }, - { - sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 13:00:00.000")`, - injectSafeTS: func() uint64 { - tt, err := time.Parse("2006-01-02 15:04:05.000", "2021-04-27 11:30:04.877") - require.NoError(t, err) - return oracle.GoTimeToTS(tt) - }(), - expect: "2021-04-27 12:00:00.000", - }, - { - sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 11:00:00.000")`, - injectSafeTS: 0, - expect: "", - }, - // Time is too small. - { - sql: `select tidb_bounded_staleness("0020-04-27 12:00:00.000", "2021-04-27 11:00:00.000")`, - injectSafeTS: 0, - expect: "1970-01-01 00:00:00.000", - }, - // Wrong value. - { - sql: `select tidb_bounded_staleness(1, 2)`, - injectSafeTS: 0, - expect: "", - }, - { - sql: `select tidb_bounded_staleness("invalid_time_1", "invalid_time_2")`, - injectSafeTS: 0, - expect: "", - }, - } - for _, test := range tidbBoundedStalenessTests { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", - fmt.Sprintf("return(%v)", test.injectSafeTS))) - tk.MustQuery(test.sql).Check(testkit.Rows(test.expect)) - } - failpoint.Disable("github.com/pingcap/tidb/expression/injectSafeTS") - // test whether tidb_bounded_staleness is deterministic - result = tk.MustQuery(`select tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND)), tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND))`) - require.Len(t, result.Rows()[0], 2) - require.Equal(t, result.Rows()[0][0], result.Rows()[0][1]) - preResult := result.Rows()[0][0] - time.Sleep(time.Second) - result = tk.MustQuery(`select tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND)), tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND))`) - require.Len(t, result.Rows()[0], 2) - require.Equal(t, result.Rows()[0][0], result.Rows()[0][1]) - require.NotEqual(t, preResult, result.Rows()[0][0]) - - // fix issue 10308 - result = tk.MustQuery("select time(\"- -\");") - result.Check(testkit.Rows("00:00:00")) - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '- -'")) - result = tk.MustQuery("select time(\"---1\");") - result.Check(testkit.Rows("00:00:00")) - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '---1'")) - result = tk.MustQuery("select time(\"-- --1\");") - result.Check(testkit.Rows("00:00:00")) - tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '-- --1'")) - - // fix issue #15185 - result = tk.MustQuery(`select timestamp(11111.1111)`) - result.Check(testkit.Rows("2001-11-11 00:00:00.0000")) - result = tk.MustQuery(`select timestamp(cast(11111.1111 as decimal(60, 5)))`) - result.Check(testkit.Rows("2001-11-11 00:00:00.00000")) - result = tk.MustQuery(`select timestamp(1021121141105.4324)`) - result.Check(testkit.Rows("0102-11-21 14:11:05.4324")) - result = tk.MustQuery(`select timestamp(cast(1021121141105.4324 as decimal(60, 5)))`) - result.Check(testkit.Rows("0102-11-21 14:11:05.43240")) - result = tk.MustQuery(`select timestamp(21121141105.101)`) - result.Check(testkit.Rows("2002-11-21 14:11:05.101")) - result = tk.MustQuery(`select timestamp(cast(21121141105.101 as decimal(60, 5)))`) - result.Check(testkit.Rows("2002-11-21 14:11:05.10100")) - result = tk.MustQuery(`select timestamp(1121141105.799055)`) - result.Check(testkit.Rows("2000-11-21 14:11:05.799055")) - result = tk.MustQuery(`select timestamp(cast(1121141105.799055 as decimal(60, 5)))`) - result.Check(testkit.Rows("2000-11-21 14:11:05.79906")) - result = tk.MustQuery(`select timestamp(121141105.123)`) - result.Check(testkit.Rows("2000-01-21 14:11:05.123")) - result = tk.MustQuery(`select timestamp(cast(121141105.123 as decimal(60, 5)))`) - result.Check(testkit.Rows("2000-01-21 14:11:05.12300")) - result = tk.MustQuery(`select timestamp(1141105)`) - result.Check(testkit.Rows("0114-11-05 00:00:00")) - result = tk.MustQuery(`select timestamp(cast(1141105 as decimal(60, 5)))`) - result.Check(testkit.Rows("0114-11-05 00:00:00.00000")) - result = tk.MustQuery(`select timestamp(41105.11)`) - result.Check(testkit.Rows("2004-11-05 00:00:00.00")) - result = tk.MustQuery(`select timestamp(cast(41105.11 as decimal(60, 5)))`) - result.Check(testkit.Rows("2004-11-05 00:00:00.00000")) - result = tk.MustQuery(`select timestamp(1105.3)`) - result.Check(testkit.Rows("2000-11-05 00:00:00.0")) - result = tk.MustQuery(`select timestamp(cast(1105.3 as decimal(60, 5)))`) - result.Check(testkit.Rows("2000-11-05 00:00:00.00000")) - result = tk.MustQuery(`select timestamp(105)`) - result.Check(testkit.Rows("2000-01-05 00:00:00")) - result = tk.MustQuery(`select timestamp(cast(105 as decimal(60, 5)))`) - result.Check(testkit.Rows("2000-01-05 00:00:00.00000")) -} - -func TestSetVariables(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - _, err := tk.Exec("set sql_mode='adfasdfadsfdasd';") - require.Error(t, err) - _, err = tk.Exec("set @@sql_mode='adfasdfadsfdasd';") - require.Error(t, err) - _, err = tk.Exec("set @@global.sql_mode='adfasdfadsfdasd';") - require.Error(t, err) - _, err = tk.Exec("set @@session.sql_mode='adfasdfadsfdasd';") - require.Error(t, err) - - var r *testkit.Result - _, err = tk.Exec("set @@session.sql_mode=',NO_ZERO_DATE,ANSI,ANSI_QUOTES';") - require.NoError(t, err) - r = tk.MustQuery(`select @@session.sql_mode`) - r.Check(testkit.Rows("NO_ZERO_DATE,REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI")) - r = tk.MustQuery(`show variables like 'sql_mode'`) - r.Check(testkit.Rows("sql_mode NO_ZERO_DATE,REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI")) - - // for invalid SQL mode. - tk.MustExec("use test") - tk.MustExec("drop table if exists tab0") - tk.MustExec("CREATE TABLE tab0(col1 time)") - _, err = tk.Exec("set sql_mode='STRICT_TRANS_TABLES';") - require.NoError(t, err) - _, err = tk.Exec("INSERT INTO tab0 select cast('999:44:33' as time);") - require.Error(t, err) - require.Error(t, err, "[types:1292]Truncated incorrect time value: '999:44:33'") - _, err = tk.Exec("set sql_mode=' ,';") - require.Error(t, err) - _, err = tk.Exec("INSERT INTO tab0 select cast('999:44:33' as time);") - require.Error(t, err) - require.Error(t, err, "[types:1292]Truncated incorrect time value: '999:44:33'") - - // issue #5478 - _, err = tk.Exec("set session transaction read write;") - require.NoError(t, err) - _, err = tk.Exec("set global transaction read write;") - require.NoError(t, err) - r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) - r.Check(testkit.Rows("0 0 0 0")) - - _, err = tk.Exec("set session transaction read only;") - require.Error(t, err) - - _, err = tk.Exec("start transaction read only;") - require.Error(t, err) - - _, err = tk.Exec("set tidb_enable_noop_functions=1") - require.NoError(t, err) - - tk.MustExec("set session transaction read only;") - tk.MustExec("start transaction read only;") - - r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) - r.Check(testkit.Rows("1 0 1 0")) - _, err = tk.Exec("set global transaction read only;") - require.Error(t, err) - tk.MustExec("set global tidb_enable_noop_functions=1;") - tk.MustExec("set global transaction read only;") - r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) - r.Check(testkit.Rows("1 1 1 1")) - - _, err = tk.Exec("set session transaction read write;") - require.NoError(t, err) - _, err = tk.Exec("set global transaction read write;") - require.NoError(t, err) - r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) - r.Check(testkit.Rows("0 0 0 0")) - - // reset - tk.MustExec("set tidb_enable_noop_functions=0") - tk.MustExec("set global tidb_enable_noop_functions=1") - - _, err = tk.Exec("set @@global.max_user_connections='';") - require.Error(t, err) - require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_user_connections").Error()) - _, err = tk.Exec("set @@global.max_prepared_stmt_count='';") - require.Error(t, err) - require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_prepared_stmt_count").Error()) - - // Previously global values were cached. This is incorrect. - // See: https://github.com/pingcap/tidb/issues/24368 - tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 0")) - tk.MustExec("SET GLOBAL max_connections=1234") - tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 1234")) - // restore - tk.MustExec("SET GLOBAL max_connections=0") -} - -func TestPreparePlanCacheOnCachedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set tidb_enable_prepared_plan_cache=ON") - tk.Session() - - var err error - se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ - PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), - }) - require.NoError(t, err) - tk.SetSession(se) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - tk.MustExec("alter table t cache") - - var readFromTableCache bool - for i := 0; i < 50; i++ { - tk.MustQuery("select * from t where a = 1") - if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { - readFromTableCache = true - break - } - time.Sleep(50 * time.Millisecond) - } - require.True(t, readFromTableCache) - // already read cache after reading first time - tk.MustExec("prepare stmt from 'select * from t where a = ?';") - tk.MustExec("set @a = 1;") - tk.MustExec("execute stmt using @a;") - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) - tk.MustExec("execute stmt using @a;") - readFromTableCache = tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache - require.True(t, readFromTableCache) - tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) -} - -func TestIssue16205(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set tidb_enable_prepared_plan_cache=ON") - tk.MustExec("use test") - tk.MustExec("prepare stmt from 'select random_bytes(3)'") - rows1 := tk.MustQuery("execute stmt").Rows() - require.Len(t, rows1, 1) - rows2 := tk.MustQuery("execute stmt").Rows() - require.Len(t, rows2, 1) - require.NotEqual(t, rows1[0][0].(string), rows2[0][0].(string)) -} - -// issues 14448, 19383, 17734 -func TestNoopFunctions(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // variable changes in the test will not affect the plan cache - tk.MustExec("DROP TABLE IF EXISTS t1") - tk.MustExec("CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY)") - tk.MustExec("INSERT INTO t1 VALUES (1),(2),(3)") - - message := `.* has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions` - stmts := []string{ - "SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1", - "SELECT * FROM t1 LOCK IN SHARE MODE", - "SELECT * FROM t1 GROUP BY a DESC", - "SELECT * FROM t1 GROUP BY a ASC", - } - - for _, stmt := range stmts { - // test on - tk.MustExec("SET tidb_enable_noop_functions='ON'") - tk.MustExec(stmt) - // test warning - tk.MustExec("SET tidb_enable_noop_functions='WARN'") - tk.MustExec(stmt) - warn := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Regexp(t, message, warn[0].Err.Error()) - // test off - tk.MustExec("SET tidb_enable_noop_functions='OFF'") - _, err := tk.Exec(stmt) - require.Regexp(t, message, err.Error()) - } - - // These statements return a different error message - // to the above. Test for error, not specifically the message. - // After they execute, we need to reset the values because - // otherwise tidb_enable_noop_functions can't be changed. - - stmts = []string{ - "START TRANSACTION READ ONLY", - "SET TRANSACTION READ ONLY", - "SET tx_read_only = 1", - "SET transaction_read_only = 1", - } - - for _, stmt := range stmts { - // test off - tk.MustExec("SET tidb_enable_noop_functions='OFF'") - _, err := tk.Exec(stmt) - require.Error(t, err) - // test warning - tk.MustExec("SET tidb_enable_noop_functions='WARN'") - tk.MustExec(stmt) - warn := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Len(t, warn, 1) - // test on - tk.MustExec("SET tidb_enable_noop_functions='ON'") - tk.MustExec(stmt) - - // Reset (required for future loop iterations and future tests) - tk.MustExec("SET tx_read_only = 0") - tk.MustExec("SET transaction_read_only = 0") - } -} - -func TestCrossDCQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - tk.MustExec("create placement policy p1 leader_constraints='[+zone=sh]'") - tk.MustExec("create placement policy p2 leader_constraints='[+zone=bj]'") - tk.MustExec(`create table t1 (c int primary key, d int,e int,index idx_d(d),index idx_e(e)) -PARTITION BY RANGE (c) ( - PARTITION p0 VALUES LESS THAN (6) placement policy p1, - PARTITION p1 VALUES LESS THAN (11) placement policy p2 -);`) - defer func() { - tk.MustExec("drop table if exists t1") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - }() - - tk.MustExec(`insert into t1 (c,d,e) values (1,1,1);`) - tk.MustExec(`insert into t1 (c,d,e) values (2,3,5);`) - tk.MustExec(`insert into t1 (c,d,e) values (3,5,7);`) - - testcases := []struct { - name string - txnScope string - zone string - sql string - expectErr error - }{ - // FIXME: block by https://github.com/pingcap/tidb/issues/21872 - //{ - // name: "cross dc read to sh by holding bj, IndexReader", - // txnScope: "bj", - // sql: "select /*+ USE_INDEX(t1, idx_d) */ d from t1 where c < 5 and d < 1;", - // expectErr: fmt.Errorf(".*can not be read by.*"), - //}, - // FIXME: block by https://github.com/pingcap/tidb/issues/21847 - //{ - // name: "cross dc read to sh by holding bj, BatchPointGet", - // txnScope: "bj", - // sql: "select * from t1 where c in (1,2,3,4);", - // expectErr: fmt.Errorf(".*can not be read by.*"), - //}, - { - name: "cross dc read to sh by holding bj, PointGet", - txnScope: "local", - zone: "bj", - sql: "select * from t1 where c = 1", - expectErr: fmt.Errorf(".*can not be read by.*"), - }, - { - name: "cross dc read to sh by holding bj, IndexLookUp", - txnScope: "local", - zone: "bj", - sql: "select * from t1 use index (idx_d) where c < 5 and d < 5;", - expectErr: fmt.Errorf(".*can not be read by.*"), - }, - { - name: "cross dc read to sh by holding bj, IndexMerge", - txnScope: "local", - zone: "bj", - sql: "select /*+ USE_INDEX_MERGE(t1, idx_d, idx_e) */ * from t1 where c <5 and (d =5 or e=5);", - expectErr: fmt.Errorf(".*can not be read by.*"), - }, - { - name: "cross dc read to sh by holding bj, TableReader", - txnScope: "local", - zone: "bj", - sql: "select * from t1 where c < 6", - expectErr: fmt.Errorf(".*can not be read by.*"), - }, - { - name: "cross dc read to global by holding bj", - txnScope: "local", - zone: "bj", - sql: "select * from t1", - expectErr: fmt.Errorf(".*can not be read by.*"), - }, - { - name: "read sh dc by holding sh", - txnScope: "local", - zone: "sh", - sql: "select * from t1 where c < 6", - expectErr: nil, - }, - { - name: "read sh dc by holding global", - txnScope: "global", - zone: "", - sql: "select * from t1 where c < 6", - expectErr: nil, - }, - } - tk.MustExec("set global tidb_enable_local_txn = on;") - for _, testcase := range testcases { - t.Log(testcase.name) - require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", - fmt.Sprintf(`return("%v")`, testcase.zone))) - tk.MustExec(fmt.Sprintf("set @@txn_scope='%v'", testcase.txnScope)) - tk.Exec("begin") - res, err := tk.Exec(testcase.sql) - _, resErr := session.GetRows4Test(context.Background(), tk.Session(), res) - var checkErr error - if err != nil { - checkErr = err - } else { - checkErr = resErr - } - if testcase.expectErr != nil { - require.Error(t, checkErr) - require.Regexp(t, ".*can not be read by.*", checkErr.Error()) - } else { - require.NoError(t, checkErr) - } - if res != nil { - res.Close() - } - tk.Exec("commit") - } - require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) - tk.MustExec("set global tidb_enable_local_txn = off;") -} - -func TestTiDBRowChecksumBuiltin(t *testing.T) { - store := testkit.CreateMockStore(t) - - checksum := func(cols ...interface{}) uint32 { - buf := make([]byte, 0, 64) - for _, col := range cols { - switch x := col.(type) { - case int: - buf = binary.LittleEndian.AppendUint64(buf, uint64(x)) - case string: - buf = binary.LittleEndian.AppendUint32(buf, uint32(len(x))) - buf = append(buf, []byte(x)...) - } - } - return crc32.ChecksumIEEE(buf) - } - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set global tidb_enable_row_level_checksum = 1") - tk.MustExec("use test") - tk.MustExec("create table t (id int primary key, c int)") - - // row with 2 checksums - tk.MustExec("insert into t values (1, 10)") - tk.MustExec("alter table t change column c c varchar(10)") - checksum1 := fmt.Sprintf("%d,%d", checksum(1, 10), checksum(1, "10")) - // row with 1 checksum - tk.Session().GetSessionVars().EnableRowLevelChecksum = true - tk.MustExec("insert into t values (2, '20')") - checksum2 := fmt.Sprintf("%d", checksum(2, "20")) - // row without checksum - tk.Session().GetSessionVars().EnableRowLevelChecksum = false - tk.MustExec("insert into t values (3, '30')") - checksum3 := "" - - // fast point-get - tk.MustQuery("select tidb_row_checksum() from t where id = 1").Check(testkit.Rows(checksum1)) - tk.MustQuery("select tidb_row_checksum() from t where id = 2").Check(testkit.Rows(checksum2)) - tk.MustQuery("select tidb_row_checksum() from t where id = 3").Check(testkit.Rows(checksum3)) - // fast batch-point-get - tk.MustQuery("select tidb_row_checksum() from t where id in (1, 2, 3)").Check(testkit.Rows(checksum1, checksum2, checksum3)) - - // non-fast point-get - tk.MustGetDBError("select length(tidb_row_checksum()) from t where id = 1", expression.ErrNotSupportedYet) - tk.MustGetDBError("select c from t where id = 1 and tidb_row_checksum() is not null", expression.ErrNotSupportedYet) - // non-fast batch-point-get - tk.MustGetDBError("select length(tidb_row_checksum()) from t where id in (1, 2, 3)", expression.ErrNotSupportedYet) - tk.MustGetDBError("select c from t where id in (1, 2, 3) and tidb_row_checksum() is not null", expression.ErrNotSupportedYet) - - // other plans - tk.MustGetDBError("select tidb_row_checksum() from t", expression.ErrNotSupportedYet) - tk.MustGetDBError("select tidb_row_checksum() from t where id > 0", expression.ErrNotSupportedYet) -} diff --git a/expression/integration_test/main_test.go b/expression/integration_test/main_test.go deleted file mode 100644 index 822e7004139c5..0000000000000 --- a/expression/integration_test/main_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package integration_test - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/timeutil" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - testmain.ShortCircuitForBench(m) - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - // Some test depends on the values of timeutil.SystemLocation() - // If we don't SetSystemTZ() here, the value would change unpredictable. - // Affected by the order whether a testsuite runs before or after integration test. - // Note, SetSystemTZ() is a sync.Once operation. - timeutil.SetSystemTZ("system") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/expression/main_test.go b/expression/main_test.go deleted file mode 100644 index e161a2a3d3778..0000000000000 --- a/expression/main_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/timeutil" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - testmain.ShortCircuitForBench(m) - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - // Some test depends on the values of timeutil.SystemLocation() - // If we don't SetSystemTZ() here, the value would change unpredictable. - // Affected by the order whether a testsuite runs before or after integration test. - // Note, SetSystemTZ() is a sync.Once operation. - timeutil.SetSystemTZ("system") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} - -func createContext(t *testing.T) *mock.Context { - ctx := mock.NewContext() - ctx.GetSessionVars().StmtCtx.SetTimeZone(time.Local) - sc := ctx.GetSessionVars().StmtCtx - sc.TruncateAsWarning = true - require.NoError(t, ctx.GetSessionVars().SetSystemVar("max_allowed_packet", "67108864")) - ctx.GetSessionVars().PlanColumnID.Store(0) - return ctx -} diff --git a/expression/schema.go b/expression/schema.go deleted file mode 100644 index 697b3bfc8c425..0000000000000 --- a/expression/schema.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "strings" - "unsafe" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/size" -) - -// KeyInfo stores the columns of one unique key or primary key. -type KeyInfo []*Column - -// Clone copies the entire UniqueKey. -func (ki KeyInfo) Clone() KeyInfo { - result := make([]*Column, 0, len(ki)) - for _, col := range ki { - result = append(result, col.Clone().(*Column)) - } - return result -} - -// String implements fmt.Stringer interface. -func (ki KeyInfo) String() string { - ukColStrs := make([]string, 0, len(ki)) - for _, col := range ki { - ukColStrs = append(ukColStrs, col.String()) - } - return "[" + strings.Join(ukColStrs, ",") + "]" -} - -// Schema stands for the row schema and unique key information get from input. -type Schema struct { - Columns []*Column - Keys []KeyInfo - // UniqueKeys stores those unique indexes that allow null values, but Keys does not allow null values. - // since equivalence conditions can filter out null values, in this case a unique index with null values can be a Key. - UniqueKeys []KeyInfo -} - -// String implements fmt.Stringer interface. -func (s *Schema) String() string { - colStrs := make([]string, 0, len(s.Columns)) - for _, col := range s.Columns { - colStrs = append(colStrs, col.String()) - } - ukStrs := make([]string, 0, len(s.Keys)) - for _, key := range s.Keys { - ukStrs = append(ukStrs, key.String()) - } - return "Column: [" + strings.Join(colStrs, ",") + "] Unique key: [" + strings.Join(ukStrs, ",") + "]" -} - -// Clone copies the total schema. -func (s *Schema) Clone() *Schema { - cols := make([]*Column, 0, s.Len()) - keys := make([]KeyInfo, 0, len(s.Keys)) - for _, col := range s.Columns { - cols = append(cols, col.Clone().(*Column)) - } - for _, key := range s.Keys { - keys = append(keys, key.Clone()) - } - schema := NewSchema(cols...) - schema.SetUniqueKeys(keys) - return schema -} - -// ExprFromSchema checks if all columns of this expression are from the same schema. -func ExprFromSchema(expr Expression, schema *Schema) bool { - switch v := expr.(type) { - case *Column: - return schema.Contains(v) - case *ScalarFunction: - for _, arg := range v.GetArgs() { - if !ExprFromSchema(arg, schema) { - return false - } - } - return true - case *CorrelatedColumn, *Constant: - return true - } - return false -} - -// RetrieveColumn retrieves column in expression from the columns in schema. -func (s *Schema) RetrieveColumn(col *Column) *Column { - index := s.ColumnIndex(col) - if index != -1 { - return s.Columns[index] - } - return nil -} - -// IsUniqueKey checks if this column is a unique key. -func (s *Schema) IsUniqueKey(col *Column) bool { - for _, key := range s.Keys { - if len(key) == 1 && key[0].Equal(nil, col) { - return true - } - } - return false -} - -// IsUnique checks if this column is a unique key which may contain duplicate nulls . -func (s *Schema) IsUnique(col *Column) bool { - for _, key := range s.UniqueKeys { - if len(key) == 1 && key[0].Equal(nil, col) { - return true - } - } - return false -} - -// ColumnIndex finds the index for a column. -func (s *Schema) ColumnIndex(col *Column) int { - backupIdx := -1 - for i, c := range s.Columns { - if c.UniqueID == col.UniqueID { - backupIdx = i - if c.IsPrefix { - // instead of returning a prefix column - // prefer to find a full column - // only clustered index table can meet this: - // same column `c1` maybe appear in both primary key and secondary index - // so secondary index itself can have two `c1` column one for indexKey and one for handle - continue - } - return i - } - } - return backupIdx -} - -// Contains checks if the schema contains the column. -func (s *Schema) Contains(col *Column) bool { - return s.ColumnIndex(col) != -1 -} - -// Len returns the number of columns in schema. -func (s *Schema) Len() int { - return len(s.Columns) -} - -// Append append new column to the columns stored in schema. -func (s *Schema) Append(col ...*Column) { - s.Columns = append(s.Columns, col...) -} - -// SetUniqueKeys will set the value of Schema.Keys. -func (s *Schema) SetUniqueKeys(keys []KeyInfo) { - s.Keys = keys -} - -// ColumnsIndices will return a slice which contains the position of each column in schema. -// If there is one column that doesn't match, nil will be returned. -func (s *Schema) ColumnsIndices(cols []*Column) (ret []int) { - ret = make([]int, 0, len(cols)) - for _, col := range cols { - pos := s.ColumnIndex(col) - if pos == -1 { - return nil - } - ret = append(ret, pos) - } - return -} - -// ColumnsByIndices returns columns by multiple offsets. -// Callers should guarantee that all the offsets provided should be valid, which means offset should: -// 1. not smaller than 0, and -// 2. not exceed len(s.Columns) -func (s *Schema) ColumnsByIndices(offsets []int) []*Column { - cols := make([]*Column, 0, len(offsets)) - for _, offset := range offsets { - cols = append(cols, s.Columns[offset]) - } - return cols -} - -// ExtractColGroups checks if column groups are from current schema, and returns -// offsets of those satisfied column groups. -func (s *Schema) ExtractColGroups(colGroups [][]*Column) ([][]int, []int) { - if len(colGroups) == 0 { - return nil, nil - } - extracted := make([][]int, 0, len(colGroups)) - offsets := make([]int, 0, len(colGroups)) - for i, g := range colGroups { - if j := s.ColumnsIndices(g); j != nil { - extracted = append(extracted, j) - offsets = append(offsets, i) - } - } - return extracted, offsets -} - -const emptySchemaSize = int64(unsafe.Sizeof(Schema{})) - -// MemoryUsage return the memory usage of Schema -func (s *Schema) MemoryUsage() (sum int64) { - if s == nil { - return - } - - sum = emptySchemaSize + int64(cap(s.Columns))*size.SizeOfPointer + int64(cap(s.Keys)+cap(s.UniqueKeys))*size.SizeOfSlice - - for _, col := range s.Columns { - sum += col.MemoryUsage() - } - for _, cols := range s.Keys { - sum += int64(cap(cols)) * size.SizeOfPointer - for _, col := range cols { - sum += col.MemoryUsage() - } - } - for _, cols := range s.UniqueKeys { - sum += int64(cap(cols)) * size.SizeOfPointer - for _, col := range cols { - sum += col.MemoryUsage() - } - } - return -} - -// GetExtraHandleColumn gets the extra handle column. -func (s *Schema) GetExtraHandleColumn() *Column { - columnLen := len(s.Columns) - if columnLen > 0 && s.Columns[columnLen-1].ID == model.ExtraHandleID { - return s.Columns[columnLen-1] - } else if columnLen > 1 && s.Columns[columnLen-2].ID == model.ExtraHandleID { - return s.Columns[columnLen-2] - } - return nil -} - -// MergeSchema will merge two schema into one schema. We shouldn't need to consider unique keys. -// That will be processed in build_key_info.go. -func MergeSchema(lSchema, rSchema *Schema) *Schema { - if lSchema == nil && rSchema == nil { - return nil - } - if lSchema == nil { - return rSchema.Clone() - } - if rSchema == nil { - return lSchema.Clone() - } - tmpL := lSchema.Clone() - tmpR := rSchema.Clone() - ret := NewSchema(append(tmpL.Columns, tmpR.Columns...)...) - return ret -} - -// GetUsedList shows whether each column in schema is contained in usedCols. -func GetUsedList(usedCols []*Column, schema *Schema) []bool { - tmpSchema := NewSchema(usedCols...) - used := make([]bool, schema.Len()) - for i, col := range schema.Columns { - if !used[i] { - used[i] = tmpSchema.Contains(col) - - // When cols are a generated expression col, compare them in terms of virtual expr. - if expr, ok := col.VirtualExpr.(*ScalarFunction); ok && used[i] { - for j, colToCompare := range schema.Columns { - if !used[j] && j != i && (expr).Equal(nil, colToCompare.VirtualExpr) && col.RetType.Equal(colToCompare.RetType) { - used[j] = true - } - } - } - } - } - return used -} - -// NewSchema returns a schema made by its parameter. -func NewSchema(cols ...*Column) *Schema { - return &Schema{Columns: cols} -} diff --git a/expression/test/multivaluedindex/BUILD.bazel b/expression/test/multivaluedindex/BUILD.bazel deleted file mode 100644 index 64eeb7edf7142..0000000000000 --- a/expression/test/multivaluedindex/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "multivaluedindex_test", - timeout = "short", - srcs = [ - "main_test.go", - "multi_valued_index_test.go", - ], - flaky = True, - shard_count = 6, - deps = [ - "//config", - "//errno", - "//kv", - "//parser/model", - "//sessiontxn", - "//table", - "//tablecodec", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/timeutil", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/expression/test/multivaluedindex/main_test.go b/expression/test/multivaluedindex/main_test.go deleted file mode 100644 index a06597715800c..0000000000000 --- a/expression/test/multivaluedindex/main_test.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package multivaluedindex - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/timeutil" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - testmain.ShortCircuitForBench(m) - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Experimental.AllowsExpressionIndex = true - }) - tikv.EnableFailpoints() - - // Some test depends on the values of timeutil.SystemLocation() - // If we don't SetSystemTZ() here, the value would change unpredictable. - // Affected by the order whether a testsuite runs before or after integration test. - // Note, SetSystemTZ() is a sync.Once operation. - timeutil.SetSystemTZ("system") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/expression/test/multivaluedindex/multi_valued_index_test.go b/expression/test/multivaluedindex/multi_valued_index_test.go deleted file mode 100644 index c57a9bc99ee72..0000000000000 --- a/expression/test/multivaluedindex/multi_valued_index_test.go +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package multivaluedindex - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/stretchr/testify/require" -) - -func TestMultiValuedIndexDDL(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - - tk.MustExec("create table t(a json);") - tk.MustGetErrCode("select cast(a as signed array) from t", errno.ErrNotSupportedYet) - tk.MustGetErrCode("select json_extract(cast(a as signed array), '$[0]') from t", errno.ErrNotSupportedYet) - tk.MustGetErrCode("select * from t where cast(a as signed array)", errno.ErrNotSupportedYet) - tk.MustGetErrCode("select cast('[1,2,3]' as unsigned array);", errno.ErrNotSupportedYet) - - tk.MustExec("drop table t") - tk.MustGetErrCode("CREATE TABLE t(x INT, KEY k ((1 AND CAST(JSON_ARRAY(x) AS UNSIGNED ARRAY))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(cast(f1 as unsigned array) as unsigned array))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, primary key mvi((cast(cast(f1 as unsigned array) as unsigned array))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->>'$[*]' as unsigned array))));", errno.ErrInvalidTypeForJSON) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as year array))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as json array))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as char(10) charset gbk array))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("create table t(j json, gc json as ((concat(cast(j->'$[*]' as unsigned array),\"x\"))));", errno.ErrNotSupportedYet) - tk.MustGetErrCode("create table t(j json, gc json as (cast(j->'$[*]' as unsigned array)));", errno.ErrNotSupportedYet) - tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as char array))));`, errno.ErrNotSupportedYet) - tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as binary array))));`, errno.ErrNotSupportedYet) - tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as float array))));`, errno.ErrNotSupportedYet) - tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as decimal(4,2) array))));`, errno.ErrNotSupportedYet) - tk.MustGetErrCode("create view v as select cast('[1,2,3]' as unsigned array);", errno.ErrNotSupportedYet) - tk.MustExec("create table t(a json, index idx((cast(a as signed array))));") - tk.MustExec("drop table t;") - tk.MustExec("create table t(a json, index idx(((cast(a as signed array)))))") - tk.MustExec("drop table t;") - tk.MustExec(`create table t(j json, key i1((cast(j->"$" as double array))));`) - - tk.MustExec("drop table t") - tk.MustGetErrCode("create table t(a json, b int, index idx(b, (cast(a as signed array)), (cast(a as signed array))));", errno.ErrNotSupportedYet) - tk.MustExec("create table t(a json, b int);") - tk.MustGetErrCode("create index idx on t (b, (cast(a as signed array)), (cast(a as signed array)))", errno.ErrNotSupportedYet) - tk.MustGetErrCode("alter table t add index idx(b, (cast(a as signed array)), (cast(a as signed array)))", errno.ErrNotSupportedYet) - tk.MustExec("create index idx1 on t (b, (cast(a as signed array)))") - tk.MustExec("alter table t add index idx2(b, (cast(a as signed array)))") - - tk.MustExec("drop table t") - tk.MustExec("create table t(a json, b int, index idx3(b, (cast(a as signed array))));") - tk.MustExec("drop table t") - tk.MustExec("set names gbk") - tk.MustExec("create table t(a json, b int, index idx3(b, (cast(a as char(10) array))));") - - tk.MustExec("CREATE TABLE users (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, doc JSON);") - tk.MustExecToErr("CREATE TABLE t (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, doc JSON, FOREIGN KEY fk_user_id ((cast(doc->'$[*]' as signed array))) REFERENCES users(id));") -} - -func TestMultiValuedIndexDML(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE test;") - - mode := []string{`''`, `default`} - - for _, m := range mode { - tk.MustExec(fmt.Sprintf("set @@sql_mode=%s", m)) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as unsigned array))));`) - tk.MustExec(`insert into t values ('[1,2,3]');`) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrDataOutOfRangeFunctionalIndex) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as signed array))));`) - tk.MustExec(`insert into t values ('[1,2,3]');`) - tk.MustExec(`insert into t values ('[-1]');`) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as char(1) array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values ('["1"]');`) - tk.MustExec(`insert into t values ('["a"]');`) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as char(2) array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values ('["1"]');`) - tk.MustExec(`insert into t values ('["a"]');`) - tk.MustExec(`insert into t values ('["汉字"]');`) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as binary(1) array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values ('["1"]');`) - tk.MustExec(`insert into t values ('["a"]');`) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as binary(2) array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values ('["1"]');`) - tk.MustExec(`insert into t values ('["a"]');`) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as date array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values (json_array(cast("2022-02-02" as date)));`) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as time array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values (json_array(cast("11:00:00" as time)));`) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as datetime array))));`) - tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - - tk.MustExec(`drop table if exists t;`) - tk.MustExec(`create table t(a json, index idx((cast(a as double array))));`) - tk.MustExec(`insert into t values ('[1,2,3]');`) - tk.MustExec(`insert into t values ('[-1]');`) - tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustExec(`insert into t values ('[1.2]');`) - tk.MustExec(`insert into t values ('[1.0]');`) - tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) - tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) - } -} - -func TestWriteMultiValuedIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as signed array))))") - tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") - tk.MustExec("insert into t1 values (2, '[1,2,3]')") - tk.MustExec("insert into t1 values (3, '[]')") - tk.MustExec("insert into t1 values (4, '[2,3,4]')") - tk.MustExec("insert into t1 values (5, null)") - tk.MustExec("insert into t1 values (6, '1')") - - t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 11) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewDatum(nil), types.NewIntDatum(5)}, - {types.NewIntDatum(1), types.NewIntDatum(1)}, - {types.NewIntDatum(1), types.NewIntDatum(2)}, - {types.NewIntDatum(1), types.NewIntDatum(6)}, - {types.NewIntDatum(2), types.NewIntDatum(1)}, - {types.NewIntDatum(2), types.NewIntDatum(2)}, - {types.NewIntDatum(2), types.NewIntDatum(4)}, - {types.NewIntDatum(3), types.NewIntDatum(1)}, - {types.NewIntDatum(3), types.NewIntDatum(2)}, - {types.NewIntDatum(3), types.NewIntDatum(4)}, - {types.NewIntDatum(4), types.NewIntDatum(4)}, - }) - } - } - tk.MustExec("delete from t1") - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 0) - } - } - - tk.MustExec("drop table t1") - tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as char(5) array))))") - tk.MustExec("insert into t1 values (1, '[\"abc\", \"abc \"]')") - tk.MustExec("insert into t1 values (2, '[\"b\"]')") - tk.MustExec("insert into t1 values (3, '[\"b \"]')") - tk.MustQuery("select pk from t1 where 'b ' member of (a)").Check(testkit.Rows("3")) - - t1, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 4) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(1)}, - {types.NewBytesDatum([]byte("abc ")), types.NewIntDatum(1)}, - {types.NewBytesDatum([]byte("b")), types.NewIntDatum(2)}, - {types.NewBytesDatum([]byte("b ")), types.NewIntDatum(3)}, - }) - } - } - - tk.MustExec("update t1 set a = json_array_append(a, '$', 'bcd') where pk = 1") - tk.MustExec("update t1 set a = '[]' where pk = 2") - tk.MustExec("update t1 set a = '[\"abc\"]' where pk = 3") - - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 4) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(1)}, - {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(3)}, - {types.NewBytesDatum([]byte("abc ")), types.NewIntDatum(1)}, - {types.NewBytesDatum([]byte("bcd")), types.NewIntDatum(1)}, - }) - } - } - - tk.MustExec("delete from t1") - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 0) - } - } - - tk.MustExec("drop table t1") - tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as unsigned array))))") - tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") - tk.MustExec("insert into t1 values (2, '[1,2,3]')") - tk.MustExec("insert into t1 values (3, '[]')") - tk.MustExec("insert into t1 values (4, '[2,3,4]')") - tk.MustExec("insert into t1 values (5, null)") - tk.MustExec("insert into t1 values (6, '1')") - - t1, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 11) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewDatum(nil), types.NewIntDatum(5)}, - {types.NewUintDatum(1), types.NewIntDatum(1)}, - {types.NewUintDatum(1), types.NewIntDatum(2)}, - {types.NewUintDatum(1), types.NewIntDatum(6)}, - {types.NewUintDatum(2), types.NewIntDatum(1)}, - {types.NewUintDatum(2), types.NewIntDatum(2)}, - {types.NewUintDatum(2), types.NewIntDatum(4)}, - {types.NewUintDatum(3), types.NewIntDatum(1)}, - {types.NewUintDatum(3), types.NewIntDatum(2)}, - {types.NewUintDatum(3), types.NewIntDatum(4)}, - {types.NewUintDatum(4), types.NewIntDatum(4)}, - }) - } - } - tk.MustExec("delete from t1") - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 0) - } - } -} - -func TestWriteMultiValuedIndexPartitionTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table t1 -( - pk int primary key, - a json, - index idx ((cast(a as signed array))) -) partition by range columns (pk) (partition p0 values less than (10), partition p1 values less than (20));`) - tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") - tk.MustExec("insert into t1 values (11, '[1,2,3]')") - tk.MustExec("insert into t1 values (2, '[]')") - tk.MustExec("insert into t1 values (12, '[2,3,4]')") - tk.MustExec("insert into t1 values (3, null)") - tk.MustExec("insert into t1 values (13, null)") - - t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - - expect := map[string]struct { - count int - vals [][]types.Datum - }{ - "p0": {4, [][]types.Datum{ - {types.NewDatum(nil), types.NewIntDatum(3)}, - {types.NewIntDatum(1), types.NewIntDatum(1)}, - {types.NewIntDatum(2), types.NewIntDatum(1)}, - {types.NewIntDatum(3), types.NewIntDatum(1)}, - }}, - "p1": {7, [][]types.Datum{ - {types.NewDatum(nil), types.NewIntDatum(13)}, - {types.NewIntDatum(1), types.NewIntDatum(11)}, - {types.NewIntDatum(2), types.NewIntDatum(11)}, - {types.NewIntDatum(2), types.NewIntDatum(12)}, - {types.NewIntDatum(3), types.NewIntDatum(11)}, - {types.NewIntDatum(3), types.NewIntDatum(12)}, - {types.NewIntDatum(4), types.NewIntDatum(12)}, - }}, - } - - for _, def := range t1.Meta().GetPartitionInfo().Definitions { - partition := t1.(table.PartitionedTable).GetPartition(def.ID) - for _, index := range partition.Indices() { - if index.Meta().MVIndex { - checkCount(t, partition.IndexPrefix(), index, store, expect[def.Name.L].count) - checkKey(t, partition.IndexPrefix(), index, store, expect[def.Name.L].vals) - } - } - } - - tk.MustExec("delete from t1") - for _, def := range t1.Meta().GetPartitionInfo().Definitions { - partition := t1.(table.PartitionedTable).GetPartition(def.ID) - for _, index := range partition.Indices() { - if index.Meta().MVIndex { - checkCount(t, partition.IndexPrefix(), index, store, 0) - } - } - } -} - -func TestWriteMultiValuedIndexUnique(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1(pk int primary key, a json, unique index idx((cast(a as signed array))))") - tk.MustExec("insert into t1 values (1, '[1,2,2]')") - tk.MustGetErrCode("insert into t1 values (2, '[1]')", errno.ErrDupEntry) - tk.MustExec("insert into t1 values (3, '[3,3,4]')") - - t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 4) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewIntDatum(1)}, - {types.NewIntDatum(2)}, - {types.NewIntDatum(3)}, - {types.NewIntDatum(4)}, - }) - } - } -} - -func TestWriteMultiValuedIndexComposite(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1(pk int primary key, a json, c int, d int, index idx(c, (cast(a as signed array)), d))") - tk.MustExec("insert into t1 values (1, '[1,2,2]', 1, 1)") - tk.MustExec("insert into t1 values (2, '[2,2,2]', 2, 2)") - tk.MustExec("insert into t1 values (3, '[3,3,4]', 3, 3)") - tk.MustExec("insert into t1 values (4, null, 4, 4)") - tk.MustExec("insert into t1 values (5, '[]', 5, 5)") - - t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - for _, index := range t1.Indices() { - if index.Meta().MVIndex { - checkCount(t, t1.IndexPrefix(), index, store, 6) - checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ - {types.NewIntDatum(1), types.NewIntDatum(1), types.NewIntDatum(1), types.NewIntDatum(1)}, - {types.NewIntDatum(1), types.NewIntDatum(2), types.NewIntDatum(1), types.NewIntDatum(1)}, - {types.NewIntDatum(2), types.NewIntDatum(2), types.NewIntDatum(2), types.NewIntDatum(2)}, - {types.NewIntDatum(3), types.NewIntDatum(3), types.NewIntDatum(3), types.NewIntDatum(3)}, - {types.NewIntDatum(3), types.NewIntDatum(4), types.NewIntDatum(3), types.NewIntDatum(3)}, - {types.NewIntDatum(4), types.NewDatum(nil), types.NewIntDatum(4), types.NewIntDatum(4)}, - }) - } - } -} - -func checkCount(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, except int) { - c := 0 - checkIndex(t, prefix, index, store, func(it kv.Iterator) { - c++ - }) - require.Equal(t, except, c) -} - -func checkKey(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, except [][]types.Datum) { - idx := 0 - checkIndex(t, prefix, index, store, func(it kv.Iterator) { - indexKey := decodeIndexKey(t, it.Key()) - require.Equal(t, except[idx], indexKey) - idx++ - }) -} - -func checkIndex(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, fn func(kv.Iterator)) { - startKey := codec.EncodeInt(prefix, index.Meta().ID) - prefix.Next() - se := testkit.NewTestKit(t, store).Session() - err := sessiontxn.NewTxn(context.Background(), se) - require.NoError(t, err) - txn, err := se.Txn(true) - require.NoError(t, err) - it, err := txn.Iter(startKey, prefix.PrefixNext()) - require.NoError(t, err) - for it.Valid() && it.Key().HasPrefix(prefix) { - fn(it) - err = it.Next() - require.NoError(t, err) - } - it.Close() - se.Close() -} - -func decodeIndexKey(t *testing.T, key kv.Key) []types.Datum { - var idLen = 8 - var prefixLen = 1 + idLen /*tableID*/ + 2 - _, _, isRecord, err := tablecodec.DecodeKeyHead(key) - require.NoError(t, err) - require.False(t, isRecord) - indexKey := key[prefixLen+idLen:] - var datumValues []types.Datum - for len(indexKey) > 0 { - remain, d, err := codec.DecodeOne(indexKey) - require.NoError(t, err) - datumValues = append(datumValues, d) - indexKey = remain - } - return datumValues -} diff --git a/expression/util.go b/expression/util.go deleted file mode 100644 index 1d0a4abdfe403..0000000000000 --- a/expression/util.go +++ /dev/null @@ -1,1796 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "bytes" - "context" - "math" - "strconv" - "strings" - "unicode" - "unicode/utf8" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" - "golang.org/x/tools/container/intsets" -) - -// cowExprRef is a copy-on-write slice ref util using in `ColumnSubstitute` -// to reduce unnecessary allocation for Expression arguments array -type cowExprRef struct { - ref []Expression - new []Expression -} - -// Set will allocate new array if changed flag true -func (c *cowExprRef) Set(i int, changed bool, val Expression) { - if c.new != nil { - c.new[i] = val - return - } - if !changed { - return - } - c.new = make([]Expression, len(c.ref)) - copy(c.new, c.ref) - c.new[i] = val -} - -// Result return the final reference -func (c *cowExprRef) Result() []Expression { - if c.new != nil { - return c.new - } - return c.ref -} - -// Filter the input expressions, append the results to result. -func Filter(result []Expression, input []Expression, filter func(Expression) bool) []Expression { - for _, e := range input { - if filter(e) { - result = append(result, e) - } - } - return result -} - -// FilterOutInPlace do the filtering out in place. -// The remained are the ones who doesn't match the filter, storing in the original slice. -// The filteredOut are the ones match the filter, storing in a new slice. -func FilterOutInPlace(input []Expression, filter func(Expression) bool) (remained, filteredOut []Expression) { - for i := len(input) - 1; i >= 0; i-- { - if filter(input[i]) { - filteredOut = append(filteredOut, input[i]) - input = append(input[:i], input[i+1:]...) - } - } - return input, filteredOut -} - -// ExtractDependentColumns extracts all dependent columns from a virtual column. -func ExtractDependentColumns(expr Expression) []*Column { - // Pre-allocate a slice to reduce allocation, 8 doesn't have special meaning. - result := make([]*Column, 0, 8) - return extractDependentColumns(result, expr) -} - -func extractDependentColumns(result []*Column, expr Expression) []*Column { - switch v := expr.(type) { - case *Column: - result = append(result, v) - if v.VirtualExpr != nil { - result = extractDependentColumns(result, v.VirtualExpr) - } - case *ScalarFunction: - for _, arg := range v.GetArgs() { - result = extractDependentColumns(result, arg) - } - } - return result -} - -// ExtractColumns extracts all columns from an expression. -func ExtractColumns(expr Expression) []*Column { - // Pre-allocate a slice to reduce allocation, 8 doesn't have special meaning. - result := make([]*Column, 0, 8) - return extractColumns(result, expr, nil) -} - -// ExtractCorColumns extracts correlated column from given expression. -func ExtractCorColumns(expr Expression) (cols []*CorrelatedColumn) { - switch v := expr.(type) { - case *CorrelatedColumn: - return []*CorrelatedColumn{v} - case *ScalarFunction: - for _, arg := range v.GetArgs() { - cols = append(cols, ExtractCorColumns(arg)...) - } - } - return -} - -// ExtractColumnsFromExpressions is a more efficient version of ExtractColumns for batch operation. -// filter can be nil, or a function to filter the result column. -// It's often observed that the pattern of the caller like this: -// -// cols := ExtractColumns(...) -// -// for _, col := range cols { -// if xxx(col) {...} -// } -// -// Provide an additional filter argument, this can be done in one step. -// To avoid allocation for cols that not need. -func ExtractColumnsFromExpressions(result []*Column, exprs []Expression, filter func(*Column) bool) []*Column { - for _, expr := range exprs { - result = extractColumns(result, expr, filter) - } - return result -} - -func extractColumns(result []*Column, expr Expression, filter func(*Column) bool) []*Column { - switch v := expr.(type) { - case *Column: - if filter == nil || filter(v) { - result = append(result, v) - } - case *ScalarFunction: - for _, arg := range v.GetArgs() { - result = extractColumns(result, arg, filter) - } - } - return result -} - -// ExtractEquivalenceColumns detects the equivalence from CNF exprs. -func ExtractEquivalenceColumns(result [][]Expression, exprs []Expression) [][]Expression { - // exprs are CNF expressions, EQ condition only make sense in the top level of every expr. - for _, expr := range exprs { - result = extractEquivalenceColumns(result, expr) - } - return result -} - -// FindUpperBound looks for column < constant or column <= constant and returns both the column -// and constant. It return nil, 0 if the expression is not of this form. -// It is used by derived Top N pattern and it is put here since it looks like -// a general purpose routine. Similar routines can be added to find lower bound as well. -func FindUpperBound(expr Expression) (*Column, int64) { - scalarFunction, scalarFunctionOk := expr.(*ScalarFunction) - if scalarFunctionOk { - args := scalarFunction.GetArgs() - if len(args) == 2 { - col, colOk := args[0].(*Column) - constant, constantOk := args[1].(*Constant) - if colOk && constantOk && (scalarFunction.FuncName.L == ast.LT || scalarFunction.FuncName.L == ast.LE) { - value, valueOk := constant.Value.GetValue().(int64) - if valueOk { - if scalarFunction.FuncName.L == ast.LT { - return col, value - 1 - } - return col, value - } - } - } - } - return nil, 0 -} - -func extractEquivalenceColumns(result [][]Expression, expr Expression) [][]Expression { - switch v := expr.(type) { - case *ScalarFunction: - // a==b, a<=>b, the latter one is evaluated to true when a,b are both null. - if v.FuncName.L == ast.EQ || v.FuncName.L == ast.NullEQ { - args := v.GetArgs() - if len(args) == 2 { - col1, ok1 := args[0].(*Column) - col2, ok2 := args[1].(*Column) - if ok1 && ok2 { - result = append(result, []Expression{col1, col2}) - } - col, ok1 := args[0].(*Column) - scl, ok2 := args[1].(*ScalarFunction) - if ok1 && ok2 { - result = append(result, []Expression{col, scl}) - } - col, ok1 = args[1].(*Column) - scl, ok2 = args[0].(*ScalarFunction) - if ok1 && ok2 { - result = append(result, []Expression{col, scl}) - } - } - return result - } - if v.FuncName.L == ast.In { - args := v.GetArgs() - // only `col in (only 1 element)`, can we build an equivalence here. - if len(args[1:]) == 1 { - col1, ok1 := args[0].(*Column) - col2, ok2 := args[1].(*Column) - if ok1 && ok2 { - result = append(result, []Expression{col1, col2}) - } - col, ok1 := args[0].(*Column) - scl, ok2 := args[1].(*ScalarFunction) - if ok1 && ok2 { - result = append(result, []Expression{col, scl}) - } - col, ok1 = args[1].(*Column) - scl, ok2 = args[0].(*ScalarFunction) - if ok1 && ok2 { - result = append(result, []Expression{col, scl}) - } - } - return result - } - // For Non-EQ function, we don't have to traverse down. - // eg: (a=b or c=d) doesn't make any definitely equivalence assertion. - } - return result -} - -// extractColumnsAndCorColumns extracts columns and correlated columns from `expr` and append them to `result`. -func extractColumnsAndCorColumns(result []*Column, expr Expression) []*Column { - switch v := expr.(type) { - case *Column: - result = append(result, v) - case *CorrelatedColumn: - result = append(result, &v.Column) - case *ScalarFunction: - for _, arg := range v.GetArgs() { - result = extractColumnsAndCorColumns(result, arg) - } - } - return result -} - -// ExtractConstantEqColumnsOrScalar detects the constant equal relationship from CNF exprs. -func ExtractConstantEqColumnsOrScalar(ctx sessionctx.Context, result []Expression, exprs []Expression) []Expression { - // exprs are CNF expressions, EQ condition only make sense in the top level of every expr. - for _, expr := range exprs { - result = extractConstantEqColumnsOrScalar(ctx, result, expr) - } - return result -} - -func extractConstantEqColumnsOrScalar(ctx sessionctx.Context, result []Expression, expr Expression) []Expression { - switch v := expr.(type) { - case *ScalarFunction: - if v.FuncName.L == ast.EQ || v.FuncName.L == ast.NullEQ { - args := v.GetArgs() - if len(args) == 2 { - col, ok1 := args[0].(*Column) - _, ok2 := args[1].(*Constant) - if ok1 && ok2 { - result = append(result, col) - } - col, ok1 = args[1].(*Column) - _, ok2 = args[0].(*Constant) - if ok1 && ok2 { - result = append(result, col) - } - // take the correlated column as constant here. - col, ok1 = args[0].(*Column) - _, ok2 = args[1].(*CorrelatedColumn) - if ok1 && ok2 { - result = append(result, col) - } - col, ok1 = args[1].(*Column) - _, ok2 = args[0].(*CorrelatedColumn) - if ok1 && ok2 { - result = append(result, col) - } - scl, ok1 := args[0].(*ScalarFunction) - _, ok2 = args[1].(*Constant) - if ok1 && ok2 { - result = append(result, scl) - } - scl, ok1 = args[1].(*ScalarFunction) - _, ok2 = args[0].(*Constant) - if ok1 && ok2 { - result = append(result, scl) - } - // take the correlated column as constant here. - scl, ok1 = args[0].(*ScalarFunction) - _, ok2 = args[1].(*CorrelatedColumn) - if ok1 && ok2 { - result = append(result, scl) - } - scl, ok1 = args[1].(*ScalarFunction) - _, ok2 = args[0].(*CorrelatedColumn) - if ok1 && ok2 { - result = append(result, scl) - } - } - return result - } - if v.FuncName.L == ast.In { - args := v.GetArgs() - allArgsIsConst := true - // only `col in (all same const)`, can col be the constant column. - // eg: a in (1, "1") does, while a in (1, '2') doesn't. - guard := args[1] - for i, v := range args[1:] { - if _, ok := v.(*Constant); !ok { - allArgsIsConst = false - break - } - if i == 0 { - continue - } - if !guard.Equal(ctx, v) { - allArgsIsConst = false - break - } - } - if allArgsIsConst { - if col, ok := args[0].(*Column); ok { - result = append(result, col) - } else if scl, ok := args[0].(*ScalarFunction); ok { - result = append(result, scl) - } - } - return result - } - // For Non-EQ function, we don't have to traverse down. - } - return result -} - -// ExtractColumnsAndCorColumnsFromExpressions extracts columns and correlated columns from expressions and append them to `result`. -func ExtractColumnsAndCorColumnsFromExpressions(result []*Column, list []Expression) []*Column { - for _, expr := range list { - result = extractColumnsAndCorColumns(result, expr) - } - return result -} - -// ExtractColumnSet extracts the different values of `UniqueId` for columns in expressions. -func ExtractColumnSet(exprs ...Expression) *intsets.Sparse { - set := &intsets.Sparse{} - for _, expr := range exprs { - extractColumnSet(expr, set) - } - return set -} - -func extractColumnSet(expr Expression, set *intsets.Sparse) { - switch v := expr.(type) { - case *Column: - set.Insert(int(v.UniqueID)) - case *ScalarFunction: - for _, arg := range v.GetArgs() { - extractColumnSet(arg, set) - } - } -} - -// SetExprColumnInOperand is used to set columns in expr as InOperand. -func SetExprColumnInOperand(expr Expression) Expression { - switch v := expr.(type) { - case *Column: - col := v.Clone().(*Column) - col.InOperand = true - return col - case *ScalarFunction: - args := v.GetArgs() - for i, arg := range args { - args[i] = SetExprColumnInOperand(arg) - } - } - return expr -} - -// ColumnSubstitute substitutes the columns in filter to expressions in select fields. -// e.g. select * from (select b as a from t) k where a < 10 => select * from (select b as a from t where b < 10) k. -func ColumnSubstitute(expr Expression, schema *Schema, newExprs []Expression) Expression { - _, _, resExpr := ColumnSubstituteImpl(expr, schema, newExprs, false) - return resExpr -} - -// ColumnSubstituteAll substitutes the columns just like ColumnSubstitute, but we don't accept partial substitution. -// Only accept: -// -// 1: substitute them all once find col in schema. -// 2: nothing in expr can be substituted. -func ColumnSubstituteAll(expr Expression, schema *Schema, newExprs []Expression) (bool, Expression) { - _, hasFail, resExpr := ColumnSubstituteImpl(expr, schema, newExprs, true) - return hasFail, resExpr -} - -// ColumnSubstituteImpl tries to substitute column expr using newExprs, -// the newFunctionInternal is only called if its child is substituted -// @return bool means whether the expr has changed. -// @return bool means whether the expr should change (has the dependency in schema, while the corresponding expr has some compatibility), but finally fallback. -// @return Expression, the original expr or the changed expr, it depends on the first @return bool. -func ColumnSubstituteImpl(expr Expression, schema *Schema, newExprs []Expression, fail1Return bool) (bool, bool, Expression) { - switch v := expr.(type) { - case *Column: - id := schema.ColumnIndex(v) - if id == -1 { - return false, false, v - } - newExpr := newExprs[id] - if v.InOperand { - newExpr = SetExprColumnInOperand(newExpr) - } - return true, false, newExpr - case *ScalarFunction: - substituted := false - hasFail := false - if v.FuncName.L == ast.Cast || v.FuncName.L == ast.Grouping { - var newArg Expression - substituted, hasFail, newArg = ColumnSubstituteImpl(v.GetArgs()[0], schema, newExprs, fail1Return) - if fail1Return && hasFail { - return substituted, hasFail, v - } - if substituted { - flag := v.RetType.GetFlag() - var e Expression - if v.FuncName.L == ast.Cast { - e = BuildCastFunction(v.GetCtx(), newArg, v.RetType) - } else { - // for grouping function recreation, use clone (meta included) instead of newFunction - e = v.Clone() - e.(*ScalarFunction).Function.getArgs()[0] = newArg - } - e.SetCoercibility(v.Coercibility()) - e.GetType().SetFlag(flag) - return true, false, e - } - return false, false, v - } - // cowExprRef is a copy-on-write util, args array allocation happens only - // when expr in args is changed - refExprArr := cowExprRef{v.GetArgs(), nil} - oldCollEt, err := CheckAndDeriveCollationFromExprs(v.GetCtx(), v.FuncName.L, v.RetType.EvalType(), v.GetArgs()...) - if err != nil { - logutil.BgLogger().Error("Unexpected error happened during ColumnSubstitution", zap.Stack("stack")) - return false, false, v - } - var tmpArgForCollCheck []Expression - if collate.NewCollationEnabled() { - tmpArgForCollCheck = make([]Expression, len(v.GetArgs())) - } - for idx, arg := range v.GetArgs() { - changed, failed, newFuncExpr := ColumnSubstituteImpl(arg, schema, newExprs, fail1Return) - if fail1Return && failed { - return changed, failed, v - } - oldChanged := changed - if collate.NewCollationEnabled() && changed { - // Make sure the collation used by the ScalarFunction isn't changed and its result collation is not weaker than the collation used by the ScalarFunction. - changed = false - copy(tmpArgForCollCheck, refExprArr.Result()) - tmpArgForCollCheck[idx] = newFuncExpr - newCollEt, err := CheckAndDeriveCollationFromExprs(v.GetCtx(), v.FuncName.L, v.RetType.EvalType(), tmpArgForCollCheck...) - if err != nil { - logutil.BgLogger().Error("Unexpected error happened during ColumnSubstitution", zap.Stack("stack")) - return false, failed, v - } - if oldCollEt.Collation == newCollEt.Collation { - if newFuncExpr.GetType().GetCollate() == arg.GetType().GetCollate() && newFuncExpr.Coercibility() == arg.Coercibility() { - // It's safe to use the new expression, otherwise some cases in projection push-down will be wrong. - changed = true - } else { - changed = checkCollationStrictness(oldCollEt.Collation, newFuncExpr.GetType().GetCollate()) - } - } - } - hasFail = hasFail || failed || oldChanged != changed - if fail1Return && oldChanged != changed { - // Only when the oldChanged is true and changed is false, we will get here. - // And this means there some dependency in this arg can be substituted with - // given expressions, while it has some collation compatibility, finally we - // fall back to use the origin args. (commonly used in projection elimination - // in which fallback usage is unacceptable) - return changed, true, v - } - refExprArr.Set(idx, changed, newFuncExpr) - if changed { - substituted = true - } - } - if substituted { - return true, hasFail, NewFunctionInternal(v.GetCtx(), v.FuncName.L, v.RetType, refExprArr.Result()...) - } - } - return false, false, expr -} - -// checkCollationStrictness check collation strictness-ship between `coll` and `newFuncColl` -// return true iff `newFuncColl` is not weaker than `coll` -func checkCollationStrictness(coll, newFuncColl string) bool { - collGroupID, ok1 := CollationStrictnessGroup[coll] - newFuncCollGroupID, ok2 := CollationStrictnessGroup[newFuncColl] - - if ok1 && ok2 { - if collGroupID == newFuncCollGroupID { - return true - } - - for _, id := range CollationStrictness[collGroupID] { - if newFuncCollGroupID == id { - return true - } - } - } - - return false -} - -// getValidPrefix gets a prefix of string which can parsed to a number with base. the minimum base is 2 and the maximum is 36. -func getValidPrefix(s string, base int64) string { - var ( - validLen int - upper rune - ) - switch { - case base >= 2 && base <= 9: - upper = rune('0' + base) - case base <= 36: - upper = rune('A' + base - 10) - default: - return "" - } -Loop: - for i := 0; i < len(s); i++ { - c := rune(s[i]) - switch { - case unicode.IsDigit(c) || unicode.IsLower(c) || unicode.IsUpper(c): - c = unicode.ToUpper(c) - if c >= upper { - break Loop - } - validLen = i + 1 - case c == '+' || c == '-': - if i != 0 { - break Loop - } - default: - break Loop - } - } - if validLen > 1 && s[0] == '+' { - return s[1:validLen] - } - return s[:validLen] -} - -// SubstituteCorCol2Constant will substitute correlated column to constant value which it contains. -// If the args of one scalar function are all constant, we will substitute it to constant. -func SubstituteCorCol2Constant(expr Expression) (Expression, error) { - switch x := expr.(type) { - case *ScalarFunction: - allConstant := true - newArgs := make([]Expression, 0, len(x.GetArgs())) - for _, arg := range x.GetArgs() { - newArg, err := SubstituteCorCol2Constant(arg) - if err != nil { - return nil, err - } - _, ok := newArg.(*Constant) - newArgs = append(newArgs, newArg) - allConstant = allConstant && ok - } - if allConstant { - val, err := x.Eval(chunk.Row{}) - if err != nil { - return nil, err - } - return &Constant{Value: val, RetType: x.GetType()}, nil - } - var ( - err error - newSf Expression - ) - if x.FuncName.L == ast.Cast { - newSf = BuildCastFunction(x.GetCtx(), newArgs[0], x.RetType) - } else if x.FuncName.L == ast.Grouping { - newSf = x.Clone() - newSf.(*ScalarFunction).GetArgs()[0] = newArgs[0] - } else { - newSf, err = NewFunction(x.GetCtx(), x.FuncName.L, x.GetType(), newArgs...) - } - return newSf, err - case *CorrelatedColumn: - return &Constant{Value: *x.Data, RetType: x.GetType()}, nil - case *Constant: - if x.DeferredExpr != nil { - newExpr := FoldConstant(x) - return &Constant{Value: newExpr.(*Constant).Value, RetType: x.GetType()}, nil - } - } - return expr, nil -} - -func locateStringWithCollation(str, substr, coll string) int64 { - collator := collate.GetCollator(coll) - strKey := collator.KeyWithoutTrimRightSpace(str) - subStrKey := collator.KeyWithoutTrimRightSpace(substr) - - index := bytes.Index(strKey, subStrKey) - if index == -1 || index == 0 { - return int64(index + 1) - } - - // todo: we can use binary search to make it faster. - count := int64(0) - for { - r, size := utf8.DecodeRuneInString(str) - count++ - index -= len(collator.KeyWithoutTrimRightSpace(string(r))) - if index <= 0 { - return count + 1 - } - str = str[size:] - } -} - -// timeZone2Duration converts timezone whose format should satisfy the regular condition -// `(^(+|-)(0?[0-9]|1[0-2]):[0-5]?\d$)|(^+13:00$)` to int for use by time.FixedZone(). -func timeZone2int(tz string) int { - sign := 1 - if strings.HasPrefix(tz, "-") { - sign = -1 - } - - i := strings.Index(tz, ":") - h, err := strconv.Atoi(tz[1:i]) - terror.Log(err) - m, err := strconv.Atoi(tz[i+1:]) - terror.Log(err) - return sign * ((h * 3600) + (m * 60)) -} - -var logicalOps = map[string]struct{}{ - ast.LT: {}, - ast.GE: {}, - ast.GT: {}, - ast.LE: {}, - ast.EQ: {}, - ast.NE: {}, - ast.UnaryNot: {}, - ast.LogicAnd: {}, - ast.LogicOr: {}, - ast.LogicXor: {}, - ast.In: {}, - ast.IsNull: {}, - ast.IsTruthWithoutNull: {}, - ast.IsFalsity: {}, - ast.Like: {}, -} - -var oppositeOp = map[string]string{ - ast.LT: ast.GE, - ast.GE: ast.LT, - ast.GT: ast.LE, - ast.LE: ast.GT, - ast.EQ: ast.NE, - ast.NE: ast.EQ, - ast.LogicOr: ast.LogicAnd, - ast.LogicAnd: ast.LogicOr, -} - -// a op b is equal to b symmetricOp a -var symmetricOp = map[opcode.Op]opcode.Op{ - opcode.LT: opcode.GT, - opcode.GE: opcode.LE, - opcode.GT: opcode.LT, - opcode.LE: opcode.GE, - opcode.EQ: opcode.EQ, - opcode.NE: opcode.NE, - opcode.NullEQ: opcode.NullEQ, -} - -func pushNotAcrossArgs(ctx sessionctx.Context, exprs []Expression, not bool) ([]Expression, bool) { - newExprs := make([]Expression, 0, len(exprs)) - flag := false - for _, expr := range exprs { - newExpr, changed := pushNotAcrossExpr(ctx, expr, not) - flag = changed || flag - newExprs = append(newExprs, newExpr) - } - return newExprs, flag -} - -// todo: consider more no precision-loss downcast cases. -func noPrecisionLossCastCompatible(cast, argCol *types.FieldType) bool { - // now only consider varchar type and integer. - if !(types.IsTypeVarchar(cast.GetType()) && types.IsTypeVarchar(argCol.GetType())) && - !(mysql.IsIntegerType(cast.GetType()) && mysql.IsIntegerType(argCol.GetType())) { - // varchar type and integer on the storage layer is quite same, while the char type has its padding suffix. - return false - } - if types.IsTypeVarchar(cast.GetType()) { - // cast varchar function only bear the flen extension. - if cast.GetFlen() < argCol.GetFlen() { - return false - } - if !collate.CompatibleCollate(cast.GetCollate(), argCol.GetCollate()) { - return false - } - } else { - // For integers, we should ignore the potential display length represented by flen, using the default flen of the type. - castFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(cast.GetType()) - originFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(argCol.GetType()) - // cast integer function only bear the flen extension and signed symbol unchanged. - if castFlen < originFlen { - return false - } - if mysql.HasUnsignedFlag(cast.GetFlag()) != mysql.HasUnsignedFlag(argCol.GetFlag()) { - return false - } - } - return true -} - -func unwrapCast(sctx sessionctx.Context, parentF *ScalarFunction, castOffset int) (Expression, bool) { - _, collation := parentF.CharsetAndCollation() - cast, ok := parentF.GetArgs()[castOffset].(*ScalarFunction) - if !ok || cast.FuncName.L != ast.Cast { - return parentF, false - } - // eg: if (cast(A) EQ const) with incompatible collation, even if cast is eliminated, the condition still can not be used to build range. - if cast.RetType.EvalType() == types.ETString && !collate.CompatibleCollate(cast.RetType.GetCollate(), collation) { - return parentF, false - } - // 1-castOffset should be constant - if _, ok := parentF.GetArgs()[1-castOffset].(*Constant); !ok { - return parentF, false - } - - // the direct args of cast function should be column. - c, ok := cast.GetArgs()[0].(*Column) - if !ok { - return parentF, false - } - - // current only consider varchar and integer - if !noPrecisionLossCastCompatible(cast.RetType, c.RetType) { - return parentF, false - } - - // the column is covered by indexes, deconstructing it out. - if castOffset == 0 { - return NewFunctionInternal(sctx, parentF.FuncName.L, parentF.RetType, c, parentF.GetArgs()[1]), true - } - return NewFunctionInternal(sctx, parentF.FuncName.L, parentF.RetType, parentF.GetArgs()[0], c), true -} - -// eliminateCastFunction will detect the original arg before and the cast type after, once upon -// there is no precision loss between them, current cast wrapper can be eliminated. For string -// type, collation is also taken into consideration. (mainly used to build range or point) -func eliminateCastFunction(sctx sessionctx.Context, expr Expression) (_ Expression, changed bool) { - f, ok := expr.(*ScalarFunction) - if !ok { - return expr, false - } - _, collation := expr.CharsetAndCollation() - switch f.FuncName.L { - case ast.LogicOr: - dnfItems := FlattenDNFConditions(f) - rmCast := false - rmCastItems := make([]Expression, len(dnfItems)) - for i, dnfItem := range dnfItems { - newExpr, curDowncast := eliminateCastFunction(sctx, dnfItem) - rmCastItems[i] = newExpr - if curDowncast { - rmCast = true - } - } - if rmCast { - // compose the new DNF expression. - return ComposeDNFCondition(sctx, rmCastItems...), true - } - return expr, false - case ast.LogicAnd: - cnfItems := FlattenCNFConditions(f) - rmCast := false - rmCastItems := make([]Expression, len(cnfItems)) - for i, cnfItem := range cnfItems { - newExpr, curDowncast := eliminateCastFunction(sctx, cnfItem) - rmCastItems[i] = newExpr - if curDowncast { - rmCast = true - } - } - if rmCast { - // compose the new CNF expression. - return ComposeCNFCondition(sctx, rmCastItems...), true - } - return expr, false - case ast.EQ, ast.NullEQ, ast.LE, ast.GE, ast.LT, ast.GT: - // for case: eq(cast(test.t2.a, varchar(100), "aaaaa"), once t2.a is covered by index or pk, try deconstructing it out. - if newF, ok := unwrapCast(sctx, f, 0); ok { - return newF, true - } - // for case: eq("aaaaa", cast(test.t2.a, varchar(100)), once t2.a is covered by index or pk, try deconstructing it out. - if newF, ok := unwrapCast(sctx, f, 1); ok { - return newF, true - } - case ast.In: - // case for: cast(a as bigint) in (1,2,3), we could deconstruct column 'a out directly. - cast, ok := f.GetArgs()[0].(*ScalarFunction) - if !ok || cast.FuncName.L != ast.Cast { - return expr, false - } - // eg: if (cast(A) IN {const}) with incompatible collation, even if cast is eliminated, the condition still can not be used to build range. - if cast.RetType.EvalType() == types.ETString && !collate.CompatibleCollate(cast.RetType.GetCollate(), collation) { - return expr, false - } - for _, arg := range f.GetArgs()[1:] { - if _, ok := arg.(*Constant); !ok { - return expr, false - } - } - // the direct args of cast function should be column. - c, ok := cast.GetArgs()[0].(*Column) - if !ok { - return expr, false - } - // current only consider varchar and integer - if !noPrecisionLossCastCompatible(cast.RetType, c.RetType) { - return expr, false - } - newArgs := []Expression{c} - newArgs = append(newArgs, f.GetArgs()[1:]...) - return NewFunctionInternal(sctx, f.FuncName.L, f.RetType, newArgs...), true - } - return expr, false -} - -// pushNotAcrossExpr try to eliminate the NOT expr in expression tree. -// Input `not` indicates whether there's a `NOT` be pushed down. -// Output `changed` indicates whether the output expression differs from the -// input `expr` because of the pushed-down-not. -func pushNotAcrossExpr(ctx sessionctx.Context, expr Expression, not bool) (_ Expression, changed bool) { - if f, ok := expr.(*ScalarFunction); ok { - switch f.FuncName.L { - case ast.UnaryNot: - child, err := wrapWithIsTrue(ctx, true, f.GetArgs()[0], true) - if err != nil { - return expr, false - } - var childExpr Expression - childExpr, changed = pushNotAcrossExpr(f.GetCtx(), child, !not) - if !changed && !not { - return expr, false - } - return childExpr, true - case ast.LT, ast.GE, ast.GT, ast.LE, ast.EQ, ast.NE: - if not { - return NewFunctionInternal(f.GetCtx(), oppositeOp[f.FuncName.L], f.GetType(), f.GetArgs()...), true - } - newArgs, changed := pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), false) - if !changed { - return f, false - } - return NewFunctionInternal(f.GetCtx(), f.FuncName.L, f.GetType(), newArgs...), true - case ast.LogicAnd, ast.LogicOr: - var ( - newArgs []Expression - changed bool - ) - funcName := f.FuncName.L - if not { - newArgs, _ = pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), true) - funcName = oppositeOp[f.FuncName.L] - changed = true - } else { - newArgs, changed = pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), false) - } - if !changed { - return f, false - } - return NewFunctionInternal(f.GetCtx(), funcName, f.GetType(), newArgs...), true - } - } - if not { - expr = NewFunctionInternal(ctx, ast.UnaryNot, types.NewFieldType(mysql.TypeTiny), expr) - } - return expr, not -} - -// GetExprInsideIsTruth get the expression inside the `istrue_with_null` and `istrue`. -// This is useful when handling expressions from "not" or "!", because we might wrap `istrue_with_null` or `istrue` -// when handling them. See pushNotAcrossExpr() and wrapWithIsTrue() for details. -func GetExprInsideIsTruth(expr Expression) Expression { - if f, ok := expr.(*ScalarFunction); ok { - switch f.FuncName.L { - case ast.IsTruthWithNull, ast.IsTruthWithoutNull: - return GetExprInsideIsTruth(f.GetArgs()[0]) - default: - return expr - } - } - return expr -} - -// PushDownNot pushes the `not` function down to the expression's arguments. -func PushDownNot(ctx sessionctx.Context, expr Expression) Expression { - newExpr, _ := pushNotAcrossExpr(ctx, expr, false) - return newExpr -} - -// EliminateNoPrecisionLossCast remove the redundant cast function for range build convenience. -// 1: deeper cast embedded in other complicated function will not be considered. -// 2: cast args should be one for original base column and one for constant. -// 3: some collation compatibility and precision loss will be considered when remove this cast func. -func EliminateNoPrecisionLossCast(sctx sessionctx.Context, expr Expression) Expression { - newExpr, _ := eliminateCastFunction(sctx, expr) - return newExpr -} - -// ContainOuterNot checks if there is an outer `not`. -func ContainOuterNot(expr Expression) bool { - return containOuterNot(expr, false) -} - -// containOuterNot checks if there is an outer `not`. -// Input `not` means whether there is `not` outside `expr` -// -// eg. -// -// not(0+(t.a == 1 and t.b == 2)) returns true -// not(t.a) and not(t.b) returns false -func containOuterNot(expr Expression, not bool) bool { - if f, ok := expr.(*ScalarFunction); ok { - switch f.FuncName.L { - case ast.UnaryNot: - return containOuterNot(f.GetArgs()[0], true) - case ast.IsTruthWithNull, ast.IsNull: - return containOuterNot(f.GetArgs()[0], not) - default: - if not { - return true - } - hasNot := false - for _, expr := range f.GetArgs() { - hasNot = hasNot || containOuterNot(expr, not) - if hasNot { - return hasNot - } - } - return hasNot - } - } - return false -} - -// Contains tests if `exprs` contains `e`. -func Contains(exprs []Expression, e Expression) bool { - for _, expr := range exprs { - if e == expr { - return true - } - } - return false -} - -// ExtractFiltersFromDNFs checks whether the cond is DNF. If so, it will get the extracted part and the remained part. -// The original DNF will be replaced by the remained part or just be deleted if remained part is nil. -// And the extracted part will be appended to the end of the orignal slice. -func ExtractFiltersFromDNFs(ctx sessionctx.Context, conditions []Expression) []Expression { - var allExtracted []Expression - for i := len(conditions) - 1; i >= 0; i-- { - if sf, ok := conditions[i].(*ScalarFunction); ok && sf.FuncName.L == ast.LogicOr { - extracted, remained := extractFiltersFromDNF(ctx, sf) - allExtracted = append(allExtracted, extracted...) - if remained == nil { - conditions = append(conditions[:i], conditions[i+1:]...) - } else { - conditions[i] = remained - } - } - } - return append(conditions, allExtracted...) -} - -// extractFiltersFromDNF extracts the same condition that occurs in every DNF item and remove them from dnf leaves. -func extractFiltersFromDNF(ctx sessionctx.Context, dnfFunc *ScalarFunction) ([]Expression, Expression) { - dnfItems := FlattenDNFConditions(dnfFunc) - sc := ctx.GetSessionVars().StmtCtx - codeMap := make(map[string]int) - hashcode2Expr := make(map[string]Expression) - for i, dnfItem := range dnfItems { - innerMap := make(map[string]struct{}) - cnfItems := SplitCNFItems(dnfItem) - for _, cnfItem := range cnfItems { - code := cnfItem.HashCode(sc) - if i == 0 { - codeMap[string(code)] = 1 - hashcode2Expr[string(code)] = cnfItem - } else if _, ok := codeMap[string(code)]; ok { - // We need this check because there may be the case like `select * from t, t1 where (t.a=t1.a and t.a=t1.a) or (something). - // We should make sure that the two `t.a=t1.a` contributes only once. - // TODO: do this out of this function. - if _, ok = innerMap[string(code)]; !ok { - codeMap[string(code)]++ - innerMap[string(code)] = struct{}{} - } - } - } - } - // We should make sure that this item occurs in every DNF item. - for hashcode, cnt := range codeMap { - if cnt < len(dnfItems) { - delete(hashcode2Expr, hashcode) - } - } - if len(hashcode2Expr) == 0 { - return nil, dnfFunc - } - newDNFItems := make([]Expression, 0, len(dnfItems)) - onlyNeedExtracted := false - for _, dnfItem := range dnfItems { - cnfItems := SplitCNFItems(dnfItem) - newCNFItems := make([]Expression, 0, len(cnfItems)) - for _, cnfItem := range cnfItems { - code := cnfItem.HashCode(sc) - _, ok := hashcode2Expr[string(code)] - if !ok { - newCNFItems = append(newCNFItems, cnfItem) - } - } - // If the extracted part is just one leaf of the DNF expression. Then the value of the total DNF expression is - // always the same with the value of the extracted part. - if len(newCNFItems) == 0 { - onlyNeedExtracted = true - break - } - newDNFItems = append(newDNFItems, ComposeCNFCondition(ctx, newCNFItems...)) - } - extractedExpr := make([]Expression, 0, len(hashcode2Expr)) - for _, expr := range hashcode2Expr { - extractedExpr = append(extractedExpr, expr) - } - if onlyNeedExtracted { - return extractedExpr, nil - } - return extractedExpr, ComposeDNFCondition(ctx, newDNFItems...) -} - -// DeriveRelaxedFiltersFromDNF given a DNF expression, derive a relaxed DNF expression which only contains columns -// in specified schema; the derived expression is a superset of original expression, i.e, any tuple satisfying -// the original expression must satisfy the derived expression. Return nil when the derived expression is universal set. -// A running example is: for schema of t1, `(t1.a=1 and t2.a=1) or (t1.a=2 and t2.a=2)` would be derived as -// `t1.a=1 or t1.a=2`, while `t1.a=1 or t2.a=1` would get nil. -func DeriveRelaxedFiltersFromDNF(expr Expression, schema *Schema) Expression { - sf, ok := expr.(*ScalarFunction) - if !ok || sf.FuncName.L != ast.LogicOr { - return nil - } - ctx := sf.GetCtx() - dnfItems := FlattenDNFConditions(sf) - newDNFItems := make([]Expression, 0, len(dnfItems)) - for _, dnfItem := range dnfItems { - cnfItems := SplitCNFItems(dnfItem) - newCNFItems := make([]Expression, 0, len(cnfItems)) - for _, cnfItem := range cnfItems { - if itemSF, ok := cnfItem.(*ScalarFunction); ok && itemSF.FuncName.L == ast.LogicOr { - relaxedCNFItem := DeriveRelaxedFiltersFromDNF(cnfItem, schema) - if relaxedCNFItem != nil { - newCNFItems = append(newCNFItems, relaxedCNFItem) - } - // If relaxed expression for embedded DNF is universal set, just drop this CNF item - continue - } - // This cnfItem must be simple expression now - // If it cannot be fully covered by schema, just drop this CNF item - if ExprFromSchema(cnfItem, schema) { - newCNFItems = append(newCNFItems, cnfItem) - } - } - // If this DNF item involves no column of specified schema, the relaxed expression must be universal set - if len(newCNFItems) == 0 { - return nil - } - newDNFItems = append(newDNFItems, ComposeCNFCondition(ctx, newCNFItems...)) - } - return ComposeDNFCondition(ctx, newDNFItems...) -} - -// GetRowLen gets the length if the func is row, returns 1 if not row. -func GetRowLen(e Expression) int { - if f, ok := e.(*ScalarFunction); ok && f.FuncName.L == ast.RowFunc { - return len(f.GetArgs()) - } - return 1 -} - -// CheckArgsNotMultiColumnRow checks the args are not multi-column row. -func CheckArgsNotMultiColumnRow(args ...Expression) error { - for _, arg := range args { - if GetRowLen(arg) != 1 { - return ErrOperandColumns.GenWithStackByArgs(1) - } - } - return nil -} - -// GetFuncArg gets the argument of the function at idx. -func GetFuncArg(e Expression, idx int) Expression { - if f, ok := e.(*ScalarFunction); ok { - return f.GetArgs()[idx] - } - return nil -} - -// PopRowFirstArg pops the first element and returns the rest of row. -// e.g. After this function (1, 2, 3) becomes (2, 3). -func PopRowFirstArg(ctx sessionctx.Context, e Expression) (ret Expression, err error) { - if f, ok := e.(*ScalarFunction); ok && f.FuncName.L == ast.RowFunc { - args := f.GetArgs() - if len(args) == 2 { - return args[1], nil - } - ret, err = NewFunction(ctx, ast.RowFunc, f.GetType(), args[1:]...) - return ret, err - } - return -} - -// DatumToConstant generates a Constant expression from a Datum. -func DatumToConstant(d types.Datum, tp byte, flag uint) *Constant { - t := types.NewFieldType(tp) - t.AddFlag(flag) - return &Constant{Value: d, RetType: t} -} - -// ParamMarkerExpression generate a getparam function expression. -func ParamMarkerExpression(ctx sessionctx.Context, v *driver.ParamMarkerExpr, needParam bool) (*Constant, error) { - useCache := ctx.GetSessionVars().StmtCtx.UseCache - isPointExec := ctx.GetSessionVars().StmtCtx.PointExec - tp := types.NewFieldType(mysql.TypeUnspecified) - types.InferParamTypeFromDatum(&v.Datum, tp) - value := &Constant{Value: v.Datum, RetType: tp} - if useCache || isPointExec || needParam { - value.ParamMarker = &ParamMarker{ - order: v.Order, - ctx: ctx, - } - } - return value, nil -} - -// ParamMarkerInPrepareChecker checks whether the given ast tree has paramMarker and is in prepare statement. -type ParamMarkerInPrepareChecker struct { - InPrepareStmt bool -} - -// Enter implements Visitor Interface. -func (pc *ParamMarkerInPrepareChecker) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - switch v := in.(type) { - case *driver.ParamMarkerExpr: - pc.InPrepareStmt = !v.InExecute - return v, true - } - return in, false -} - -// Leave implements Visitor Interface. -func (pc *ParamMarkerInPrepareChecker) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, true -} - -// DisableParseJSONFlag4Expr disables ParseToJSONFlag for `expr` except Column. -// We should not *PARSE* a string as JSON under some scenarios. ParseToJSONFlag -// is 0 for JSON column yet(as well as JSON correlated column), so we can skip -// it. Moreover, Column.RetType refers to the infoschema, if we modify it, data -// race may happen if another goroutine read from the infoschema at the same -// time. -func DisableParseJSONFlag4Expr(expr Expression) { - if _, isColumn := expr.(*Column); isColumn { - return - } - if _, isCorCol := expr.(*CorrelatedColumn); isCorCol { - return - } - expr.GetType().SetFlag(expr.GetType().GetFlag() & ^mysql.ParseToJSONFlag) -} - -// ConstructPositionExpr constructs PositionExpr with the given ParamMarkerExpr. -func ConstructPositionExpr(p *driver.ParamMarkerExpr) *ast.PositionExpr { - return &ast.PositionExpr{P: p} -} - -// PosFromPositionExpr generates a position value from PositionExpr. -func PosFromPositionExpr(ctx sessionctx.Context, v *ast.PositionExpr) (int, bool, error) { - if v.P == nil { - return v.N, false, nil - } - value, err := ParamMarkerExpression(ctx, v.P.(*driver.ParamMarkerExpr), false) - if err != nil { - return 0, true, err - } - pos, isNull, err := GetIntFromConstant(ctx, value) - if err != nil || isNull { - return 0, true, err - } - return pos, false, nil -} - -// GetStringFromConstant gets a string value from the Constant expression. -func GetStringFromConstant(ctx sessionctx.Context, value Expression) (string, bool, error) { - con, ok := value.(*Constant) - if !ok { - err := errors.Errorf("Not a Constant expression %+v", value) - return "", true, err - } - str, isNull, err := con.EvalString(ctx, chunk.Row{}) - if err != nil || isNull { - return "", true, err - } - return str, false, nil -} - -// GetIntFromConstant gets an interger value from the Constant expression. -func GetIntFromConstant(ctx sessionctx.Context, value Expression) (int, bool, error) { - str, isNull, err := GetStringFromConstant(ctx, value) - if err != nil || isNull { - return 0, true, err - } - intNum, err := strconv.Atoi(str) - if err != nil { - return 0, true, nil - } - return intNum, false, nil -} - -// BuildNotNullExpr wraps up `not(isnull())` for given expression. -func BuildNotNullExpr(ctx sessionctx.Context, expr Expression) Expression { - isNull := NewFunctionInternal(ctx, ast.IsNull, types.NewFieldType(mysql.TypeTiny), expr) - notNull := NewFunctionInternal(ctx, ast.UnaryNot, types.NewFieldType(mysql.TypeTiny), isNull) - return notNull -} - -// IsRuntimeConstExpr checks if a expr can be treated as a constant in **executor**. -func IsRuntimeConstExpr(expr Expression) bool { - switch x := expr.(type) { - case *ScalarFunction: - if _, ok := unFoldableFunctions[x.FuncName.L]; ok { - return false - } - for _, arg := range x.GetArgs() { - if !IsRuntimeConstExpr(arg) { - return false - } - } - return true - case *Column: - return false - case *Constant, *CorrelatedColumn: - return true - } - return false -} - -// CheckNonDeterministic checks whether the current expression contains a non-deterministic func. -func CheckNonDeterministic(e Expression) bool { - switch x := e.(type) { - case *Constant, *Column, *CorrelatedColumn: - return false - case *ScalarFunction: - if _, ok := unFoldableFunctions[x.FuncName.L]; ok { - return true - } - for _, arg := range x.GetArgs() { - if CheckNonDeterministic(arg) { - return true - } - } - } - return false -} - -// CheckFuncInExpr checks whether there's a given function in the expression. -func CheckFuncInExpr(e Expression, funcName string) bool { - switch x := e.(type) { - case *Constant, *Column, *CorrelatedColumn: - return false - case *ScalarFunction: - if x.FuncName.L == funcName { - return true - } - for _, arg := range x.GetArgs() { - if CheckFuncInExpr(arg, funcName) { - return true - } - } - } - return false -} - -// IsMutableEffectsExpr checks if expr contains function which is mutable or has side effects. -func IsMutableEffectsExpr(expr Expression) bool { - switch x := expr.(type) { - case *ScalarFunction: - if _, ok := mutableEffectsFunctions[x.FuncName.L]; ok { - return true - } - for _, arg := range x.GetArgs() { - if IsMutableEffectsExpr(arg) { - return true - } - } - case *Column: - case *Constant: - if x.DeferredExpr != nil { - return IsMutableEffectsExpr(x.DeferredExpr) - } - } - return false -} - -// IsInmutableExpr checks whether this expression only consists of foldable functions and inmutable constants. -// This expression can be evaluated by using `expr.Eval(chunk.Row{})` directly if it's inmutable. -func IsInmutableExpr(expr Expression) bool { - switch x := expr.(type) { - case *ScalarFunction: - if _, ok := unFoldableFunctions[x.FuncName.L]; ok { - return false - } - if _, ok := mutableEffectsFunctions[x.FuncName.L]; ok { - return false - } - for _, arg := range x.GetArgs() { - if !IsInmutableExpr(arg) { - return false - } - } - return true - case *Constant: - if x.DeferredExpr != nil || x.ParamMarker != nil { - return false - } - return true - default: - return false - } -} - -// RemoveDupExprs removes identical exprs. Not that if expr contains functions which -// are mutable or have side effects, we cannot remove it even if it has duplicates; -// if the plan is going to be cached, we cannot remove expressions containing `?` neither. -func RemoveDupExprs(ctx sessionctx.Context, exprs []Expression) []Expression { - res := make([]Expression, 0, len(exprs)) - exists := make(map[string]struct{}, len(exprs)) - sc := ctx.GetSessionVars().StmtCtx - for _, expr := range exprs { - key := string(expr.HashCode(sc)) - if _, ok := exists[key]; !ok || IsMutableEffectsExpr(expr) { - res = append(res, expr) - exists[key] = struct{}{} - } - } - return res -} - -// GetUint64FromConstant gets a uint64 from constant expression. -func GetUint64FromConstant(expr Expression) (uint64, bool, bool) { - con, ok := expr.(*Constant) - if !ok { - logutil.BgLogger().Warn("not a constant expression", zap.String("expression", expr.ExplainInfo())) - return 0, false, false - } - dt := con.Value - if con.ParamMarker != nil { - dt = con.ParamMarker.GetUserVar() - } else if con.DeferredExpr != nil { - var err error - dt, err = con.DeferredExpr.Eval(chunk.Row{}) - if err != nil { - logutil.BgLogger().Warn("eval deferred expr failed", zap.Error(err)) - return 0, false, false - } - } - switch dt.Kind() { - case types.KindNull: - return 0, true, true - case types.KindInt64: - val := dt.GetInt64() - if val < 0 { - return 0, false, false - } - return uint64(val), false, true - case types.KindUint64: - return dt.GetUint64(), false, true - } - return 0, false, false -} - -// ContainVirtualColumn checks if the expressions contain a virtual column -func ContainVirtualColumn(exprs []Expression) bool { - for _, expr := range exprs { - switch v := expr.(type) { - case *Column: - if v.VirtualExpr != nil { - return true - } - case *ScalarFunction: - if ContainVirtualColumn(v.GetArgs()) { - return true - } - } - } - return false -} - -// ContainCorrelatedColumn checks if the expressions contain a correlated column -func ContainCorrelatedColumn(exprs []Expression) bool { - for _, expr := range exprs { - switch v := expr.(type) { - case *CorrelatedColumn: - return true - case *ScalarFunction: - if ContainCorrelatedColumn(v.GetArgs()) { - return true - } - } - } - return false -} - -// MaybeOverOptimized4PlanCache used to check whether an optimization can work -// for the statement when we enable the plan cache. -// In some situations, some optimizations maybe over-optimize and cache an -// overOptimized plan. The cached plan may not get the correct result when we -// reuse the plan for other statements. -// For example, `pk>=$a and pk<=$b` can be optimized to a PointGet when -// `$a==$b`, but it will cause wrong results when `$a!=$b`. -// So we need to do the check here. The check includes the following aspects: -// 1. Whether the plan cache switch is enable. -// 2. Whether the statement can be cached. -// 3. Whether the expressions contain a lazy constant. -// TODO: Do more careful check here. -func MaybeOverOptimized4PlanCache(ctx sessionctx.Context, exprs []Expression) bool { - // If we do not enable plan cache, all the optimization can work correctly. - if !ctx.GetSessionVars().StmtCtx.UseCache { - return false - } - return containMutableConst(ctx, exprs) -} - -// containMutableConst checks if the expressions contain a lazy constant. -func containMutableConst(ctx sessionctx.Context, exprs []Expression) bool { - for _, expr := range exprs { - switch v := expr.(type) { - case *Constant: - if v.ParamMarker != nil || v.DeferredExpr != nil { - return true - } - case *ScalarFunction: - if containMutableConst(ctx, v.GetArgs()) { - return true - } - } - } - return false -} - -// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr. -func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) { - for _, expr := range exprs { - switch v := expr.(type) { - case *Constant: - v.ParamMarker = nil - v.DeferredExpr = nil - case *ScalarFunction: - RemoveMutableConst(ctx, v.GetArgs()) - } - } -} - -const ( - _ = iota - kib = 1 << (10 * iota) - mib = 1 << (10 * iota) - gib = 1 << (10 * iota) - tib = 1 << (10 * iota) - pib = 1 << (10 * iota) - eib = 1 << (10 * iota) -) - -const ( - nano = 1 - micro = 1000 * nano - milli = 1000 * micro - sec = 1000 * milli - min = 60 * sec - hour = 60 * min - dayTime = 24 * hour -) - -// GetFormatBytes convert byte count to value with units. -func GetFormatBytes(bytes float64) string { - var divisor float64 - var unit string - - bytesAbs := math.Abs(bytes) - if bytesAbs >= eib { - divisor = eib - unit = "EiB" - } else if bytesAbs >= pib { - divisor = pib - unit = "PiB" - } else if bytesAbs >= tib { - divisor = tib - unit = "TiB" - } else if bytesAbs >= gib { - divisor = gib - unit = "GiB" - } else if bytesAbs >= mib { - divisor = mib - unit = "MiB" - } else if bytesAbs >= kib { - divisor = kib - unit = "KiB" - } else { - divisor = 1 - unit = "bytes" - } - - if divisor == 1 { - return strconv.FormatFloat(bytes, 'f', 0, 64) + " " + unit - } - value := bytes / divisor - if math.Abs(value) >= 100000.0 { - return strconv.FormatFloat(value, 'e', 2, 64) + " " + unit - } - return strconv.FormatFloat(value, 'f', 2, 64) + " " + unit -} - -// GetFormatNanoTime convert time in nanoseconds to value with units. -func GetFormatNanoTime(time float64) string { - var divisor float64 - var unit string - - timeAbs := math.Abs(time) - if timeAbs >= dayTime { - divisor = dayTime - unit = "d" - } else if timeAbs >= hour { - divisor = hour - unit = "h" - } else if timeAbs >= min { - divisor = min - unit = "min" - } else if timeAbs >= sec { - divisor = sec - unit = "s" - } else if timeAbs >= milli { - divisor = milli - unit = "ms" - } else if timeAbs >= micro { - divisor = micro - unit = "us" - } else { - divisor = 1 - unit = "ns" - } - - if divisor == 1 { - return strconv.FormatFloat(time, 'f', 0, 64) + " " + unit - } - value := time / divisor - if math.Abs(value) >= 100000.0 { - return strconv.FormatFloat(value, 'e', 2, 64) + " " + unit - } - return strconv.FormatFloat(value, 'f', 2, 64) + " " + unit -} - -// SQLDigestTextRetriever is used to find the normalized SQL statement text by SQL digests in statements_summary table. -// It's exported for test purposes. It's used by the `tidb_decode_sql_digests` builtin function, but also exposed to -// be used in other modules. -type SQLDigestTextRetriever struct { - // SQLDigestsMap is the place to put the digests that's requested for getting SQL text and also the place to put - // the query result. - SQLDigestsMap map[string]string - - // Replace querying for test purposes. - mockLocalData map[string]string - mockGlobalData map[string]string - // There are two ways for querying information: 1) query specified digests by WHERE IN query, or 2) query all - // information to avoid the too long WHERE IN clause. If there are more than `fetchAllLimit` digests needs to be - // queried, the second way will be chosen; otherwise, the first way will be chosen. - fetchAllLimit int -} - -// NewSQLDigestTextRetriever creates a new SQLDigestTextRetriever. -func NewSQLDigestTextRetriever() *SQLDigestTextRetriever { - return &SQLDigestTextRetriever{ - SQLDigestsMap: make(map[string]string), - fetchAllLimit: 512, - } -} - -func (r *SQLDigestTextRetriever) runMockQuery(data map[string]string, inValues []interface{}) (map[string]string, error) { - if len(inValues) == 0 { - return data, nil - } - res := make(map[string]string, len(inValues)) - for _, digest := range inValues { - if text, ok := data[digest.(string)]; ok { - res[digest.(string)] = text - } - } - return res, nil -} - -// runFetchDigestQuery runs query to the system tables to fetch the kv mapping of SQL digests and normalized SQL texts -// of the given SQL digests, if `inValues` is given, or all these mappings otherwise. If `queryGlobal` is false, it -// queries information_schema.statements_summary and information_schema.statements_summary_history; otherwise, it -// queries the cluster version of these two tables. -func (r *SQLDigestTextRetriever) runFetchDigestQuery(ctx context.Context, sctx sessionctx.Context, queryGlobal bool, inValues []interface{}) (map[string]string, error) { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) - // If mock data is set, query the mock data instead of the real statements_summary tables. - if !queryGlobal && r.mockLocalData != nil { - return r.runMockQuery(r.mockLocalData, inValues) - } else if queryGlobal && r.mockGlobalData != nil { - return r.runMockQuery(r.mockGlobalData, inValues) - } - - exec, ok := sctx.(sqlexec.RestrictedSQLExecutor) - if !ok { - return nil, errors.New("restricted sql can't be executed in this context") - } - - // Information in statements_summary will be periodically moved to statements_summary_history. Union them together - // to avoid missing information when statements_summary is just cleared. - stmt := "select digest, digest_text from information_schema.statements_summary union distinct " + - "select digest, digest_text from information_schema.statements_summary_history" - if queryGlobal { - stmt = "select digest, digest_text from information_schema.cluster_statements_summary union distinct " + - "select digest, digest_text from information_schema.cluster_statements_summary_history" - } - // Add the where clause if `inValues` is specified. - if len(inValues) > 0 { - stmt += " where digest in (" + strings.Repeat("%?,", len(inValues)-1) + "%?)" - } - - rows, _, err := exec.ExecRestrictedSQL(ctx, nil, stmt, inValues...) - if err != nil { - return nil, err - } - - res := make(map[string]string, len(rows)) - for _, row := range rows { - res[row.GetString(0)] = row.GetString(1) - } - return res, nil -} - -func (r *SQLDigestTextRetriever) updateDigestInfo(queryResult map[string]string) { - for digest, text := range r.SQLDigestsMap { - if len(text) > 0 { - // The text of this digest is already known - continue - } - sqlText, ok := queryResult[digest] - if ok { - r.SQLDigestsMap[digest] = sqlText - } - } -} - -// RetrieveLocal tries to retrieve the SQL text of the SQL digests from local information. -func (r *SQLDigestTextRetriever) RetrieveLocal(ctx context.Context, sctx sessionctx.Context) error { - if len(r.SQLDigestsMap) == 0 { - return nil - } - - var queryResult map[string]string - if len(r.SQLDigestsMap) <= r.fetchAllLimit { - inValues := make([]interface{}, 0, len(r.SQLDigestsMap)) - for key := range r.SQLDigestsMap { - inValues = append(inValues, key) - } - var err error - queryResult, err = r.runFetchDigestQuery(ctx, sctx, false, inValues) - if err != nil { - return errors.Trace(err) - } - - if len(queryResult) == len(r.SQLDigestsMap) { - r.SQLDigestsMap = queryResult - return nil - } - } else { - var err error - queryResult, err = r.runFetchDigestQuery(ctx, sctx, false, nil) - if err != nil { - return errors.Trace(err) - } - } - - r.updateDigestInfo(queryResult) - return nil -} - -// RetrieveGlobal tries to retrieve the SQL text of the SQL digests from the information of the whole cluster. -func (r *SQLDigestTextRetriever) RetrieveGlobal(ctx context.Context, sctx sessionctx.Context) error { - err := r.RetrieveLocal(ctx, sctx) - if err != nil { - return errors.Trace(err) - } - - // In some unit test environments it's unable to retrieve global info, and this function blocks it for tens of - // seconds, which wastes much time during unit test. In this case, enable this failpoint to bypass retrieving - // globally. - failpoint.Inject("sqlDigestRetrieverSkipRetrieveGlobal", func() { - failpoint.Return(nil) - }) - - var unknownDigests []interface{} - for k, v := range r.SQLDigestsMap { - if len(v) == 0 { - unknownDigests = append(unknownDigests, k) - } - } - - if len(unknownDigests) == 0 { - return nil - } - - var queryResult map[string]string - if len(r.SQLDigestsMap) <= r.fetchAllLimit { - queryResult, err = r.runFetchDigestQuery(ctx, sctx, true, unknownDigests) - if err != nil { - return errors.Trace(err) - } - } else { - queryResult, err = r.runFetchDigestQuery(ctx, sctx, true, nil) - if err != nil { - return errors.Trace(err) - } - } - - r.updateDigestInfo(queryResult) - return nil -} - -// ExprsToStringsForDisplay convert a slice of Expression to a slice of string using Expression.String(), and -// to make it better for display and debug, it also escapes the string to corresponding golang string literal, -// which means using \t, \n, \x??, \u????, ... to represent newline, control character, non-printable character, -// invalid utf-8 bytes and so on. -func ExprsToStringsForDisplay(exprs []Expression) []string { - strs := make([]string, len(exprs)) - for i, cond := range exprs { - quote := `"` - // We only need the escape functionality of strconv.Quote, the quoting is not needed, - // so we trim the \" prefix and suffix here. - strs[i] = strings.TrimSuffix( - strings.TrimPrefix( - strconv.Quote(cond.String()), - quote), - quote) - } - return strs -} diff --git a/expression/util_test.go b/expression/util_test.go deleted file mode 100644 index f71f31a02a0cf..0000000000000 --- a/expression/util_test.go +++ /dev/null @@ -1,602 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package expression - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestBaseBuiltin(t *testing.T) { - ctx := mock.NewContext() - bf, err := newBaseBuiltinFuncWithTp(ctx, "", nil, types.ETTimestamp) - require.NoError(t, err) - _, _, err = bf.evalInt(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalReal(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalString(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalDecimal(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalTime(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalDuration(chunk.Row{}) - require.Error(t, err) - _, _, err = bf.evalJSON(chunk.Row{}) - require.Error(t, err) -} - -func TestClone(t *testing.T) { - builtinFuncs := []builtinFunc{ - &builtinArithmeticPlusRealSig{}, &builtinArithmeticPlusDecimalSig{}, &builtinArithmeticPlusIntSig{}, &builtinArithmeticMinusRealSig{}, &builtinArithmeticMinusDecimalSig{}, - &builtinArithmeticMinusIntSig{}, &builtinArithmeticDivideRealSig{}, &builtinArithmeticDivideDecimalSig{}, &builtinArithmeticMultiplyRealSig{}, &builtinArithmeticMultiplyDecimalSig{}, - &builtinArithmeticMultiplyIntUnsignedSig{}, &builtinArithmeticMultiplyIntSig{}, &builtinArithmeticIntDivideIntSig{}, &builtinArithmeticIntDivideDecimalSig{}, - &builtinArithmeticModIntUnsignedUnsignedSig{}, &builtinArithmeticModIntUnsignedSignedSig{}, &builtinArithmeticModIntSignedUnsignedSig{}, &builtinArithmeticModIntSignedSignedSig{}, - &builtinArithmeticModRealSig{}, &builtinArithmeticModDecimalSig{}, &builtinCastIntAsIntSig{}, &builtinCastIntAsRealSig{}, &builtinCastIntAsStringSig{}, - &builtinCastIntAsDecimalSig{}, &builtinCastIntAsTimeSig{}, &builtinCastIntAsDurationSig{}, &builtinCastIntAsJSONSig{}, &builtinCastRealAsIntSig{}, - &builtinCastRealAsRealSig{}, &builtinCastRealAsStringSig{}, &builtinCastRealAsDecimalSig{}, &builtinCastRealAsTimeSig{}, &builtinCastRealAsDurationSig{}, - &builtinCastRealAsJSONSig{}, &builtinCastDecimalAsIntSig{}, &builtinCastDecimalAsRealSig{}, &builtinCastDecimalAsStringSig{}, &builtinCastDecimalAsDecimalSig{}, - &builtinCastDecimalAsTimeSig{}, &builtinCastDecimalAsDurationSig{}, &builtinCastDecimalAsJSONSig{}, &builtinCastStringAsIntSig{}, &builtinCastStringAsRealSig{}, - &builtinCastStringAsStringSig{}, &builtinCastStringAsDecimalSig{}, &builtinCastStringAsTimeSig{}, &builtinCastStringAsDurationSig{}, &builtinCastStringAsJSONSig{}, - &builtinCastTimeAsIntSig{}, &builtinCastTimeAsRealSig{}, &builtinCastTimeAsStringSig{}, &builtinCastTimeAsDecimalSig{}, &builtinCastTimeAsTimeSig{}, - &builtinCastTimeAsDurationSig{}, &builtinCastTimeAsJSONSig{}, &builtinCastDurationAsIntSig{}, &builtinCastDurationAsRealSig{}, &builtinCastDurationAsStringSig{}, - &builtinCastDurationAsDecimalSig{}, &builtinCastDurationAsTimeSig{}, &builtinCastDurationAsDurationSig{}, &builtinCastDurationAsJSONSig{}, &builtinCastJSONAsIntSig{}, - &builtinCastJSONAsRealSig{}, &builtinCastJSONAsStringSig{}, &builtinCastJSONAsDecimalSig{}, &builtinCastJSONAsTimeSig{}, &builtinCastJSONAsDurationSig{}, - &builtinCastJSONAsJSONSig{}, &builtinCoalesceIntSig{}, &builtinCoalesceRealSig{}, &builtinCoalesceDecimalSig{}, &builtinCoalesceStringSig{}, - &builtinCoalesceTimeSig{}, &builtinCoalesceDurationSig{}, &builtinGreatestIntSig{}, &builtinGreatestRealSig{}, &builtinGreatestDecimalSig{}, - &builtinGreatestStringSig{}, &builtinGreatestTimeSig{}, &builtinLeastIntSig{}, &builtinLeastRealSig{}, &builtinLeastDecimalSig{}, - &builtinLeastStringSig{}, &builtinLeastTimeSig{}, &builtinIntervalIntSig{}, &builtinIntervalRealSig{}, &builtinLTIntSig{}, - &builtinLTRealSig{}, &builtinLTDecimalSig{}, &builtinLTStringSig{}, &builtinLTDurationSig{}, &builtinLTTimeSig{}, - &builtinLEIntSig{}, &builtinLERealSig{}, &builtinLEDecimalSig{}, &builtinLEStringSig{}, &builtinLEDurationSig{}, - &builtinLETimeSig{}, &builtinGTIntSig{}, &builtinGTRealSig{}, &builtinGTDecimalSig{}, &builtinGTStringSig{}, - &builtinGTTimeSig{}, &builtinGTDurationSig{}, &builtinGEIntSig{}, &builtinGERealSig{}, &builtinGEDecimalSig{}, - &builtinGEStringSig{}, &builtinGETimeSig{}, &builtinGEDurationSig{}, &builtinNEIntSig{}, &builtinNERealSig{}, - &builtinNEDecimalSig{}, &builtinNEStringSig{}, &builtinNETimeSig{}, &builtinNEDurationSig{}, &builtinNullEQIntSig{}, - &builtinNullEQRealSig{}, &builtinNullEQDecimalSig{}, &builtinNullEQStringSig{}, &builtinNullEQTimeSig{}, &builtinNullEQDurationSig{}, - &builtinCaseWhenIntSig{}, &builtinCaseWhenRealSig{}, &builtinCaseWhenDecimalSig{}, &builtinCaseWhenStringSig{}, &builtinCaseWhenTimeSig{}, - &builtinCaseWhenDurationSig{}, &builtinIfNullIntSig{}, &builtinIfNullRealSig{}, &builtinIfNullDecimalSig{}, &builtinIfNullStringSig{}, - &builtinIfNullTimeSig{}, &builtinIfNullDurationSig{}, &builtinIfNullJSONSig{}, &builtinIfIntSig{}, &builtinIfRealSig{}, - &builtinIfDecimalSig{}, &builtinIfStringSig{}, &builtinIfTimeSig{}, &builtinIfDurationSig{}, &builtinIfJSONSig{}, - &builtinAesDecryptSig{}, &builtinAesDecryptIVSig{}, &builtinAesEncryptSig{}, &builtinAesEncryptIVSig{}, &builtinCompressSig{}, - &builtinMD5Sig{}, &builtinPasswordSig{}, &builtinRandomBytesSig{}, &builtinSHA1Sig{}, &builtinSHA2Sig{}, - &builtinUncompressSig{}, &builtinUncompressedLengthSig{}, &builtinDatabaseSig{}, &builtinFoundRowsSig{}, &builtinCurrentUserSig{}, - &builtinUserSig{}, &builtinConnectionIDSig{}, &builtinLastInsertIDSig{}, &builtinLastInsertIDWithIDSig{}, &builtinVersionSig{}, - &builtinTiDBVersionSig{}, &builtinRowCountSig{}, &builtinJSONTypeSig{}, &builtinJSONQuoteSig{}, &builtinJSONUnquoteSig{}, - &builtinJSONArraySig{}, &builtinJSONArrayAppendSig{}, &builtinJSONObjectSig{}, &builtinJSONExtractSig{}, &builtinJSONSetSig{}, - &builtinJSONInsertSig{}, &builtinJSONReplaceSig{}, &builtinJSONRemoveSig{}, &builtinJSONMergeSig{}, &builtinJSONContainsSig{}, - &builtinJSONStorageSizeSig{}, &builtinJSONDepthSig{}, &builtinJSONSearchSig{}, &builtinJSONKeysSig{}, &builtinJSONKeys2ArgsSig{}, &builtinJSONLengthSig{}, - &builtinLikeSig{}, &builtinIlikeSig{}, &builtinRegexpLikeFuncSig{}, &builtinRegexpSubstrFuncSig{}, &builtinRegexpInStrFuncSig{}, &builtinRegexpReplaceFuncSig{}, &builtinAbsRealSig{}, &builtinAbsIntSig{}, - &builtinAbsUIntSig{}, &builtinAbsDecSig{}, &builtinRoundRealSig{}, &builtinRoundIntSig{}, &builtinRoundDecSig{}, - &builtinRoundWithFracRealSig{}, &builtinRoundWithFracIntSig{}, &builtinRoundWithFracDecSig{}, &builtinCeilRealSig{}, &builtinCeilIntToDecSig{}, - &builtinCeilIntToIntSig{}, &builtinCeilDecToIntSig{}, &builtinCeilDecToDecSig{}, &builtinFloorRealSig{}, &builtinFloorIntToDecSig{}, - &builtinFloorIntToIntSig{}, &builtinFloorDecToIntSig{}, &builtinFloorDecToDecSig{}, &builtinLog1ArgSig{}, &builtinLog2ArgsSig{}, - &builtinLog2Sig{}, &builtinLog10Sig{}, &builtinRandSig{}, &builtinRandWithSeedFirstGenSig{}, &builtinPowSig{}, - &builtinConvSig{}, &builtinCRC32Sig{}, &builtinSignSig{}, &builtinSqrtSig{}, &builtinAcosSig{}, - &builtinAsinSig{}, &builtinAtan1ArgSig{}, &builtinAtan2ArgsSig{}, &builtinCosSig{}, &builtinCotSig{}, - &builtinDegreesSig{}, &builtinExpSig{}, &builtinPISig{}, &builtinRadiansSig{}, &builtinSinSig{}, - &builtinTanSig{}, &builtinTruncateIntSig{}, &builtinTruncateRealSig{}, &builtinTruncateDecimalSig{}, &builtinTruncateUintSig{}, - &builtinSleepSig{}, &builtinLockSig{}, &builtinReleaseLockSig{}, &builtinDecimalAnyValueSig{}, &builtinDurationAnyValueSig{}, - &builtinIntAnyValueSig{}, &builtinJSONAnyValueSig{}, &builtinRealAnyValueSig{}, &builtinStringAnyValueSig{}, &builtinTimeAnyValueSig{}, - &builtinInetAtonSig{}, &builtinInetNtoaSig{}, &builtinInet6AtonSig{}, &builtinInet6NtoaSig{}, &builtinIsIPv4Sig{}, - &builtinIsIPv4CompatSig{}, &builtinIsIPv4MappedSig{}, &builtinIsIPv6Sig{}, &builtinUUIDSig{}, &builtinNameConstIntSig{}, - &builtinNameConstRealSig{}, &builtinNameConstDecimalSig{}, &builtinNameConstTimeSig{}, &builtinNameConstDurationSig{}, &builtinNameConstStringSig{}, - &builtinNameConstJSONSig{}, &builtinLogicAndSig{}, &builtinLogicOrSig{}, &builtinLogicXorSig{}, &builtinRealIsTrueSig{}, - &builtinDecimalIsTrueSig{}, &builtinIntIsTrueSig{}, &builtinRealIsFalseSig{}, &builtinDecimalIsFalseSig{}, &builtinIntIsFalseSig{}, - &builtinUnaryMinusIntSig{}, &builtinDecimalIsNullSig{}, &builtinDurationIsNullSig{}, &builtinIntIsNullSig{}, &builtinRealIsNullSig{}, - &builtinStringIsNullSig{}, &builtinTimeIsNullSig{}, &builtinUnaryNotRealSig{}, &builtinUnaryNotDecimalSig{}, &builtinUnaryNotIntSig{}, &builtinSleepSig{}, &builtinInIntSig{}, - &builtinInStringSig{}, &builtinInDecimalSig{}, &builtinInRealSig{}, &builtinInTimeSig{}, &builtinInDurationSig{}, - &builtinInJSONSig{}, &builtinRowSig{}, &builtinSetStringVarSig{}, &builtinSetIntVarSig{}, &builtinSetRealVarSig{}, &builtinSetDecimalVarSig{}, - &builtinGetIntVarSig{}, &builtinGetRealVarSig{}, &builtinGetDecimalVarSig{}, &builtinGetStringVarSig{}, &builtinLockSig{}, - &builtinReleaseLockSig{}, &builtinValuesIntSig{}, &builtinValuesRealSig{}, &builtinValuesDecimalSig{}, &builtinValuesStringSig{}, - &builtinValuesTimeSig{}, &builtinValuesDurationSig{}, &builtinValuesJSONSig{}, &builtinBitCountSig{}, &builtinGetParamStringSig{}, - &builtinLengthSig{}, &builtinASCIISig{}, &builtinConcatSig{}, &builtinConcatWSSig{}, &builtinLeftSig{}, - &builtinLeftUTF8Sig{}, &builtinRightSig{}, &builtinRightUTF8Sig{}, &builtinRepeatSig{}, &builtinLowerSig{}, - &builtinReverseUTF8Sig{}, &builtinReverseSig{}, &builtinSpaceSig{}, &builtinUpperSig{}, &builtinStrcmpSig{}, - &builtinReplaceSig{}, &builtinConvertSig{}, &builtinSubstring2ArgsSig{}, &builtinSubstring3ArgsSig{}, &builtinSubstring2ArgsUTF8Sig{}, - &builtinSubstring3ArgsUTF8Sig{}, &builtinSubstringIndexSig{}, &builtinLocate2ArgsUTF8Sig{}, &builtinLocate3ArgsUTF8Sig{}, &builtinLocate2ArgsSig{}, - &builtinLocate3ArgsSig{}, &builtinHexStrArgSig{}, &builtinHexIntArgSig{}, &builtinUnHexSig{}, &builtinTrim1ArgSig{}, - &builtinTrim2ArgsSig{}, &builtinTrim3ArgsSig{}, &builtinLTrimSig{}, &builtinRTrimSig{}, &builtinLpadUTF8Sig{}, - &builtinLpadSig{}, &builtinRpadUTF8Sig{}, &builtinRpadSig{}, &builtinBitLengthSig{}, &builtinCharSig{}, - &builtinCharLengthUTF8Sig{}, &builtinFindInSetSig{}, &builtinMakeSetSig{}, &builtinOctIntSig{}, &builtinOctStringSig{}, - &builtinOrdSig{}, &builtinQuoteSig{}, &builtinBinSig{}, &builtinEltSig{}, &builtinExportSet3ArgSig{}, - &builtinExportSet4ArgSig{}, &builtinExportSet5ArgSig{}, &builtinFormatWithLocaleSig{}, &builtinFormatSig{}, &builtinFromBase64Sig{}, - &builtinToBase64Sig{}, &builtinInsertSig{}, &builtinInsertUTF8Sig{}, &builtinInstrUTF8Sig{}, &builtinInstrSig{}, - &builtinFieldRealSig{}, &builtinFieldIntSig{}, &builtinFieldStringSig{}, &builtinDateSig{}, &builtinDateLiteralSig{}, - &builtinDateDiffSig{}, &builtinNullTimeDiffSig{}, &builtinTimeStringTimeDiffSig{}, &builtinDurationStringTimeDiffSig{}, &builtinDurationDurationTimeDiffSig{}, - &builtinStringTimeTimeDiffSig{}, &builtinStringDurationTimeDiffSig{}, &builtinStringStringTimeDiffSig{}, &builtinTimeTimeTimeDiffSig{}, &builtinDateFormatSig{}, - &builtinHourSig{}, &builtinMinuteSig{}, &builtinSecondSig{}, &builtinMicroSecondSig{}, &builtinMonthSig{}, - &builtinMonthNameSig{}, &builtinNowWithArgSig{}, &builtinNowWithoutArgSig{}, &builtinDayNameSig{}, &builtinDayOfMonthSig{}, - &builtinDayOfWeekSig{}, &builtinDayOfYearSig{}, &builtinWeekWithModeSig{}, &builtinWeekWithoutModeSig{}, &builtinWeekDaySig{}, - &builtinWeekOfYearSig{}, &builtinYearSig{}, &builtinYearWeekWithModeSig{}, &builtinYearWeekWithoutModeSig{}, &builtinGetFormatSig{}, - &builtinSysDateWithFspSig{}, &builtinSysDateWithoutFspSig{}, &builtinCurrentDateSig{}, &builtinCurrentTime0ArgSig{}, &builtinCurrentTime1ArgSig{}, - &builtinTimeSig{}, &builtinTimeLiteralSig{}, &builtinUTCDateSig{}, &builtinUTCTimestampWithArgSig{}, &builtinUTCTimestampWithoutArgSig{}, - &builtinAddDatetimeAndDurationSig{}, &builtinAddDatetimeAndStringSig{}, &builtinAddTimeDateTimeNullSig{}, &builtinAddStringAndDurationSig{}, &builtinAddStringAndStringSig{}, - &builtinAddTimeStringNullSig{}, &builtinAddDurationAndDurationSig{}, &builtinAddDurationAndStringSig{}, &builtinAddTimeDurationNullSig{}, &builtinAddDateAndDurationSig{}, - &builtinAddDateAndStringSig{}, &builtinSubDatetimeAndDurationSig{}, &builtinSubDatetimeAndStringSig{}, &builtinSubTimeDateTimeNullSig{}, &builtinSubStringAndDurationSig{}, - &builtinSubStringAndStringSig{}, &builtinSubTimeStringNullSig{}, &builtinSubDurationAndDurationSig{}, &builtinSubDurationAndStringSig{}, &builtinSubTimeDurationNullSig{}, - &builtinSubDateAndDurationSig{}, &builtinSubDateAndStringSig{}, &builtinUnixTimestampCurrentSig{}, &builtinUnixTimestampIntSig{}, &builtinUnixTimestampDecSig{}, - &builtinConvertTzSig{}, &builtinMakeDateSig{}, &builtinMakeTimeSig{}, &builtinPeriodAddSig{}, &builtinPeriodDiffSig{}, - &builtinQuarterSig{}, &builtinSecToTimeSig{}, &builtinTimeToSecSig{}, &builtinTimestampAddSig{}, &builtinToDaysSig{}, - &builtinToSecondsSig{}, &builtinUTCTimeWithArgSig{}, &builtinUTCTimeWithoutArgSig{}, &builtinTimestamp1ArgSig{}, &builtinTimestamp2ArgsSig{}, - &builtinTimestampLiteralSig{}, &builtinLastDaySig{}, &builtinStrToDateDateSig{}, &builtinStrToDateDatetimeSig{}, &builtinStrToDateDurationSig{}, - &builtinFromUnixTime1ArgSig{}, &builtinFromUnixTime2ArgSig{}, &builtinExtractDatetimeFromStringSig{}, &builtinExtractDatetimeSig{}, &builtinExtractDurationSig{}, &builtinAddSubDateAsStringSig{}, - &builtinAddSubDateDatetimeAnySig{}, &builtinAddSubDateDurationAnySig{}, - } - for _, f := range builtinFuncs { - cf := f.Clone() - require.IsType(t, f, cf) - } -} - -func TestGetUint64FromConstant(t *testing.T) { - con := &Constant{ - Value: types.NewDatum(nil), - } - _, isNull, ok := GetUint64FromConstant(con) - require.True(t, ok) - require.True(t, isNull) - - con = &Constant{ - Value: types.NewIntDatum(-1), - } - _, _, ok = GetUint64FromConstant(con) - require.False(t, ok) - - con.Value = types.NewIntDatum(1) - num, isNull, ok := GetUint64FromConstant(con) - require.True(t, ok) - require.False(t, isNull) - require.Equal(t, uint64(1), num) - - con.Value = types.NewUintDatum(1) - num, _, _ = GetUint64FromConstant(con) - require.Equal(t, uint64(1), num) - - con.DeferredExpr = &Constant{Value: types.NewIntDatum(1)} - num, _, _ = GetUint64FromConstant(con) - require.Equal(t, uint64(1), num) - - ctx := mock.NewContext() - ctx.GetSessionVars().PlanCacheParams.Append(types.NewUintDatum(100)) - con.ParamMarker = &ParamMarker{order: 0, ctx: ctx} - num, _, _ = GetUint64FromConstant(con) - require.Equal(t, uint64(100), num) -} - -func TestSetExprColumnInOperand(t *testing.T) { - col := &Column{RetType: newIntFieldType()} - require.True(t, SetExprColumnInOperand(col).(*Column).InOperand) - - f, err := funcs[ast.Abs].getFunction(mock.NewContext(), []Expression{col}) - require.NoError(t, err) - fun := &ScalarFunction{Function: f} - SetExprColumnInOperand(fun) - require.True(t, f.getArgs()[0].(*Column).InOperand) -} - -func TestPopRowFirstArg(t *testing.T) { - c1, c2, c3 := &Column{RetType: newIntFieldType()}, &Column{RetType: newIntFieldType()}, &Column{RetType: newIntFieldType()} - f, err := funcs[ast.RowFunc].getFunction(mock.NewContext(), []Expression{c1, c2, c3}) - require.NoError(t, err) - fun := &ScalarFunction{Function: f, FuncName: model.NewCIStr(ast.RowFunc), RetType: newIntFieldType()} - fun2, err := PopRowFirstArg(mock.NewContext(), fun) - require.NoError(t, err) - require.Len(t, fun2.(*ScalarFunction).GetArgs(), 2) -} - -func TestGetStrIntFromConstant(t *testing.T) { - col := &Column{} - _, _, err := GetStringFromConstant(mock.NewContext(), col) - require.Error(t, err) - - con := &Constant{RetType: types.NewFieldType(mysql.TypeNull)} - _, isNull, err := GetStringFromConstant(mock.NewContext(), con) - require.NoError(t, err) - require.True(t, isNull) - - con = &Constant{RetType: newIntFieldType(), Value: types.NewIntDatum(1)} - ret, _, _ := GetStringFromConstant(mock.NewContext(), con) - require.Equal(t, "1", ret) - - con = &Constant{RetType: types.NewFieldType(mysql.TypeNull)} - _, isNull, _ = GetIntFromConstant(mock.NewContext(), con) - require.True(t, isNull) - - con = &Constant{RetType: newStringFieldType(), Value: types.NewStringDatum("abc")} - _, isNull, _ = GetIntFromConstant(mock.NewContext(), con) - require.True(t, isNull) - - con = &Constant{RetType: newStringFieldType(), Value: types.NewStringDatum("123")} - num, _, _ := GetIntFromConstant(mock.NewContext(), con) - require.Equal(t, 123, num) -} - -func TestSubstituteCorCol2Constant(t *testing.T) { - ctx := mock.NewContext() - corCol1 := &CorrelatedColumn{Data: &NewOne().Value} - corCol1.RetType = types.NewFieldType(mysql.TypeLonglong) - corCol2 := &CorrelatedColumn{Data: &NewOne().Value} - corCol2.RetType = types.NewFieldType(mysql.TypeLonglong) - cast := BuildCastFunction(ctx, corCol1, types.NewFieldType(mysql.TypeLonglong)) - plus := newFunction(ast.Plus, cast, corCol2) - plus2 := newFunction(ast.Plus, plus, NewOne()) - ans1 := &Constant{Value: types.NewIntDatum(3), RetType: types.NewFieldType(mysql.TypeLonglong)} - ret, err := SubstituteCorCol2Constant(plus2) - require.NoError(t, err) - require.True(t, ret.Equal(ctx, ans1)) - col1 := &Column{Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} - ret, err = SubstituteCorCol2Constant(col1) - require.NoError(t, err) - ans2 := col1 - require.True(t, ret.Equal(ctx, ans2)) - plus3 := newFunction(ast.Plus, plus2, col1) - ret, err = SubstituteCorCol2Constant(plus3) - require.NoError(t, err) - ans3 := newFunction(ast.Plus, ans1, col1) - require.True(t, ret.Equal(ctx, ans3)) -} - -func TestPushDownNot(t *testing.T) { - ctx := mock.NewContext() - col := &Column{Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} - // !((a=1||a=1)&&a=1) - eqFunc := newFunction(ast.EQ, col, NewOne()) - orFunc := newFunction(ast.LogicOr, eqFunc, eqFunc) - andFunc := newFunction(ast.LogicAnd, orFunc, eqFunc) - notFunc := newFunction(ast.UnaryNot, andFunc) - // (a!=1&&a!=1)||a=1 - neFunc := newFunction(ast.NE, col, NewOne()) - andFunc2 := newFunction(ast.LogicAnd, neFunc, neFunc) - orFunc2 := newFunction(ast.LogicOr, andFunc2, neFunc) - notFuncCopy := notFunc.Clone() - ret := PushDownNot(ctx, notFunc) - require.True(t, ret.Equal(ctx, orFunc2)) - require.True(t, notFunc.Equal(ctx, notFuncCopy)) - - // issue 15725 - // (not not a) should be optimized to (a is true) - notFunc = newFunction(ast.UnaryNot, col) - notFunc = newFunction(ast.UnaryNot, notFunc) - ret = PushDownNot(ctx, notFunc) - require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, col))) - - // (not not (a+1)) should be optimized to (a+1 is true) - plusFunc := newFunction(ast.Plus, col, NewOne()) - notFunc = newFunction(ast.UnaryNot, plusFunc) - notFunc = newFunction(ast.UnaryNot, notFunc) - ret = PushDownNot(ctx, notFunc) - require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, plusFunc))) - // (not not not a) should be optimized to (not (a is true)) - notFunc = newFunction(ast.UnaryNot, col) - notFunc = newFunction(ast.UnaryNot, notFunc) - notFunc = newFunction(ast.UnaryNot, notFunc) - ret = PushDownNot(ctx, notFunc) - require.True(t, ret.Equal(ctx, newFunction(ast.UnaryNot, newFunction(ast.IsTruthWithNull, col)))) - // (not not not not a) should be optimized to (a is true) - notFunc = newFunction(ast.UnaryNot, col) - notFunc = newFunction(ast.UnaryNot, notFunc) - notFunc = newFunction(ast.UnaryNot, notFunc) - notFunc = newFunction(ast.UnaryNot, notFunc) - ret = PushDownNot(ctx, notFunc) - require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, col))) -} - -func TestFilter(t *testing.T) { - conditions := []Expression{ - newFunction(ast.EQ, newColumn(0), newColumn(1)), - newFunction(ast.EQ, newColumn(1), newColumn(2)), - newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), - } - result := make([]Expression, 0, 5) - result = Filter(result, conditions, isLogicOrFunction) - require.Len(t, result, 1) -} - -func TestFilterOutInPlace(t *testing.T) { - conditions := []Expression{ - newFunction(ast.EQ, newColumn(0), newColumn(1)), - newFunction(ast.EQ, newColumn(1), newColumn(2)), - newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), - } - remained, filtered := FilterOutInPlace(conditions, isLogicOrFunction) - require.Equal(t, 2, len(remained)) - require.Equal(t, "eq", remained[0].(*ScalarFunction).FuncName.L) - require.Equal(t, "eq", remained[1].(*ScalarFunction).FuncName.L) - require.Equal(t, 1, len(filtered)) - require.Equal(t, "or", filtered[0].(*ScalarFunction).FuncName.L) -} - -func TestHashGroupKey(t *testing.T) { - ctx := mock.NewContext() - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} - tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} - for i := 0; i < len(tNames); i++ { - ft := eType2FieldType(eTypes[i]) - if eTypes[i] == types.ETDecimal { - ft.SetFlen(0) - } - colExpr := &Column{Index: 0, RetType: ft} - input := chunk.New([]*types.FieldType{ft}, 1024, 1024) - fillColumnWithGener(eTypes[i], input, 0, nil) - colBuf := chunk.NewColumn(ft, 1024) - bufs := make([][]byte, 1024) - for j := 0; j < 1024; j++ { - bufs[j] = bufs[j][:0] - } - var err error - err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf) - require.NoError(t, err) - bufs, err = codec.HashGroupKey(sc, 1024, colBuf, bufs, ft) - require.NoError(t, err) - - var buf []byte - for j := 0; j < input.NumRows(); j++ { - d, err := colExpr.Eval(input.GetRow(j)) - require.NoError(t, err) - buf, err = codec.EncodeValue(sc, buf[:0], d) - require.NoError(t, err) - require.Equal(t, string(bufs[j]), string(buf)) - } - } -} - -func isLogicOrFunction(e Expression) bool { - if f, ok := e.(*ScalarFunction); ok { - return f.FuncName.L == ast.LogicOr - } - return false -} - -func TestDisableParseJSONFlag4Expr(t *testing.T) { - var expr Expression - expr = &Column{RetType: newIntFieldType()} - ft := expr.GetType() - ft.AddFlag(mysql.ParseToJSONFlag) - DisableParseJSONFlag4Expr(expr) - require.True(t, mysql.HasParseToJSONFlag(ft.GetFlag())) - - expr = &CorrelatedColumn{Column: Column{RetType: newIntFieldType()}} - ft = expr.GetType() - ft.AddFlag(mysql.ParseToJSONFlag) - DisableParseJSONFlag4Expr(expr) - require.True(t, mysql.HasParseToJSONFlag(ft.GetFlag())) - expr = &ScalarFunction{RetType: newIntFieldType()} - ft = expr.GetType() - ft.AddFlag(mysql.ParseToJSONFlag) - DisableParseJSONFlag4Expr(expr) - require.False(t, mysql.HasParseToJSONFlag(ft.GetFlag())) -} - -func TestSQLDigestTextRetriever(t *testing.T) { - // Create a fake session as the argument to the retriever, though it's actually not used when mock data is set. - - r := NewSQLDigestTextRetriever() - clearResult := func() { - r.SQLDigestsMap = map[string]string{ - "digest1": "", - "digest2": "", - "digest3": "", - "digest4": "", - "digest5": "", - } - } - clearResult() - r.mockLocalData = map[string]string{ - "digest1": "text1", - "digest2": "text2", - "digest6": "text6", - } - r.mockGlobalData = map[string]string{ - "digest2": "text2", - "digest3": "text3", - "digest4": "text4", - "digest7": "text7", - } - - expectedLocalResult := map[string]string{ - "digest1": "text1", - "digest2": "text2", - "digest3": "", - "digest4": "", - "digest5": "", - } - expectedGlobalResult := map[string]string{ - "digest1": "text1", - "digest2": "text2", - "digest3": "text3", - "digest4": "text4", - "digest5": "", - } - - err := r.RetrieveLocal(context.Background(), nil) - require.NoError(t, err) - require.Equal(t, expectedLocalResult, r.SQLDigestsMap) - clearResult() - - err = r.RetrieveGlobal(context.Background(), nil) - require.NoError(t, err) - require.Equal(t, expectedGlobalResult, r.SQLDigestsMap) - clearResult() - - r.fetchAllLimit = 1 - err = r.RetrieveLocal(context.Background(), nil) - require.NoError(t, err) - require.Equal(t, expectedLocalResult, r.SQLDigestsMap) - clearResult() - - err = r.RetrieveGlobal(context.Background(), nil) - require.NoError(t, err) - require.Equal(t, expectedGlobalResult, r.SQLDigestsMap) -} - -func BenchmarkExtractColumns(b *testing.B) { - conditions := []Expression{ - newFunction(ast.EQ, newColumn(0), newColumn(1)), - newFunction(ast.EQ, newColumn(1), newColumn(2)), - newFunction(ast.EQ, newColumn(2), newColumn(3)), - newFunction(ast.EQ, newColumn(3), newLonglong(1)), - newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), - } - expr := ComposeCNFCondition(mock.NewContext(), conditions...) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ExtractColumns(expr) - } - b.ReportAllocs() -} - -func BenchmarkExprFromSchema(b *testing.B) { - conditions := []Expression{ - newFunction(ast.EQ, newColumn(0), newColumn(1)), - newFunction(ast.EQ, newColumn(1), newColumn(2)), - newFunction(ast.EQ, newColumn(2), newColumn(3)), - newFunction(ast.EQ, newColumn(3), newLonglong(1)), - newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), - } - expr := ComposeCNFCondition(mock.NewContext(), conditions...) - schema := &Schema{Columns: ExtractColumns(expr)} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - ExprFromSchema(expr, schema) - } - b.ReportAllocs() -} - -// MockExpr is mainly for test. -type MockExpr struct { - err error - t *types.FieldType - i interface{} -} - -func (m *MockExpr) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} -func (m *MockExpr) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { - return nil -} - -func (m *MockExpr) String() string { return "" } -func (m *MockExpr) MarshalJSON() ([]byte, error) { return nil, nil } -func (m *MockExpr) Eval(row chunk.Row) (types.Datum, error) { return types.NewDatum(m.i), m.err } -func (m *MockExpr) EvalInt(ctx sessionctx.Context, row chunk.Row) (val int64, isNull bool, err error) { - if x, ok := m.i.(int64); ok { - return x, false, m.err - } - return 0, m.i == nil, m.err -} -func (m *MockExpr) EvalReal(ctx sessionctx.Context, row chunk.Row) (val float64, isNull bool, err error) { - if x, ok := m.i.(float64); ok { - return x, false, m.err - } - return 0, m.i == nil, m.err -} -func (m *MockExpr) EvalString(ctx sessionctx.Context, row chunk.Row) (val string, isNull bool, err error) { - if x, ok := m.i.(string); ok { - return x, false, m.err - } - return "", m.i == nil, m.err -} -func (m *MockExpr) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (val *types.MyDecimal, isNull bool, err error) { - if x, ok := m.i.(*types.MyDecimal); ok { - return x, false, m.err - } - return nil, m.i == nil, m.err -} -func (m *MockExpr) EvalTime(ctx sessionctx.Context, row chunk.Row) (val types.Time, isNull bool, err error) { - if x, ok := m.i.(types.Time); ok { - return x, false, m.err - } - return types.ZeroTime, m.i == nil, m.err -} -func (m *MockExpr) EvalDuration(ctx sessionctx.Context, row chunk.Row) (val types.Duration, isNull bool, err error) { - if x, ok := m.i.(types.Duration); ok { - return x, false, m.err - } - return types.Duration{}, m.i == nil, m.err -} -func (m *MockExpr) EvalJSON(ctx sessionctx.Context, row chunk.Row) (val types.BinaryJSON, isNull bool, err error) { - if x, ok := m.i.(types.BinaryJSON); ok { - return x, false, m.err - } - return types.BinaryJSON{}, m.i == nil, m.err -} -func (m *MockExpr) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { - return types.Datum{}, m.err -} -func (m *MockExpr) GetType() *types.FieldType { return m.t } -func (m *MockExpr) Clone() Expression { return nil } -func (m *MockExpr) Equal(ctx sessionctx.Context, e Expression) bool { return false } -func (m *MockExpr) IsCorrelated() bool { return false } -func (m *MockExpr) ConstItem(_ *stmtctx.StatementContext) bool { return false } -func (m *MockExpr) Decorrelate(schema *Schema) Expression { return m } -func (m *MockExpr) ResolveIndices(schema *Schema) (Expression, error) { return m, nil } -func (m *MockExpr) resolveIndices(schema *Schema) error { return nil } -func (m *MockExpr) ResolveIndicesByVirtualExpr(schema *Schema) (Expression, bool) { return m, true } -func (m *MockExpr) resolveIndicesByVirtualExpr(schema *Schema) bool { return true } -func (m *MockExpr) RemapColumn(_ map[int64]*Column) (Expression, error) { return m, nil } -func (m *MockExpr) ExplainInfo() string { return "" } -func (m *MockExpr) ExplainNormalizedInfo() string { return "" } -func (m *MockExpr) HashCode(sc *stmtctx.StatementContext) []byte { return nil } -func (m *MockExpr) Vectorized() bool { return false } -func (m *MockExpr) SupportReverseEval() bool { return false } -func (m *MockExpr) HasCoercibility() bool { return false } -func (m *MockExpr) Coercibility() Coercibility { return 0 } -func (m *MockExpr) SetCoercibility(Coercibility) {} -func (m *MockExpr) Repertoire() Repertoire { return UNICODE } -func (m *MockExpr) SetRepertoire(Repertoire) {} - -func (m *MockExpr) CharsetAndCollation() (string, string) { - return "", "" -} -func (m *MockExpr) SetCharsetAndCollation(chs, coll string) {} - -func (m *MockExpr) MemoryUsage() (sum int64) { - return -} -func (m *MockExpr) Traverse(action TraverseAction) Expression { - return action.Transform(m) -} diff --git a/extension/BUILD.bazel b/extension/BUILD.bazel deleted file mode 100644 index 578b3eca306a1..0000000000000 --- a/extension/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "extension", - srcs = [ - "extensions.go", - "function.go", - "manifest.go", - "registry.go", - "session.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/extension", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/mysql", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//types", - "//util/chunk", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@io_etcd_go_etcd_client_v3//:client", - ], -) - -go_test( - name = "extension_test", - timeout = "short", - srcs = [ - "bootstrap_test.go", - "event_listener_test.go", - "function_test.go", - "main_test.go", - "registry_test.go", - ], - embed = [":extension"], - flaky = True, - shard_count = 14, - deps = [ - "//expression", - "//parser/ast", - "//parser/auth", - "//parser/mysql", - "//privilege/privileges", - "//server", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/sem", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/extension/_import/BUILD.bazel b/extension/_import/BUILD.bazel deleted file mode 100644 index 1dbb796e583ce..0000000000000 --- a/extension/_import/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "_import", - srcs = ["import.go"], - importpath = "github.com/pingcap/tidb/extension/_import", - visibility = ["//visibility:public"], -) diff --git a/extension/bootstrap_test.go b/extension/bootstrap_test.go deleted file mode 100644 index ae4a7ff03f091..0000000000000 --- a/extension/bootstrap_test.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package extension_test - -import ( - "testing" - - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestBootstrap(t *testing.T) { - defer func() { - extension.Reset() - }() - - extension.Reset() - require.NoError(t, extension.Register("test1", extension.WithBootstrapSQL("create table test.t1 (a int)"))) - require.NoError(t, extension.Register("test2", extension.WithBootstrap(func(ctx extension.BootstrapContext) error { - _, err := ctx.ExecuteSQL(ctx, "insert into test.t1 values(1)") - require.NoError(t, err) - - rows, err := ctx.ExecuteSQL(ctx, "select * from test.t1 where a=1") - require.NoError(t, err) - - require.Equal(t, 1, len(rows)) - require.Equal(t, int64(1), rows[0].GetInt64(0)) - return nil - }))) - require.NoError(t, extension.Setup()) - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("select * from test.t1").Check(testkit.Rows("1")) -} diff --git a/extension/enterprise b/extension/enterprise deleted file mode 160000 index 7e63af1f525eb..0000000000000 --- a/extension/enterprise +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7e63af1f525eb4b1a7e9c40bb9c426977329df48 diff --git a/extension/extensionimpl/BUILD.bazel b/extension/extensionimpl/BUILD.bazel deleted file mode 100644 index 8c062a16ae7c9..0000000000000 --- a/extension/extensionimpl/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "extensionimpl", - srcs = ["bootstrap.go"], - importpath = "github.com/pingcap/tidb/extension/extensionimpl", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//extension", - "//kv", - "//util/chunk", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@io_etcd_go_etcd_client_v3//:client", - ], -) diff --git a/extension/extensionimpl/bootstrap.go b/extension/extensionimpl/bootstrap.go deleted file mode 100644 index 8b6b154f988ab..0000000000000 --- a/extension/extensionimpl/bootstrap.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package extensionimpl - -import ( - "context" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" - clientv3 "go.etcd.io/etcd/client/v3" -) - -type bootstrapContext struct { - context.Context - - sqlExecutor sqlexec.SQLExecutor - etcdCli *clientv3.Client - sessionPool extension.SessionPool -} - -func (c *bootstrapContext) ExecuteSQL(ctx context.Context, sql string) (rows []chunk.Row, err error) { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) - rs, err := c.sqlExecutor.ExecuteInternal(ctx, sql) - if err != nil { - return nil, err - } - - if rs == nil { - return nil, nil - } - - defer func() { - closeErr := rs.Close() - if err == nil { - err = closeErr - } - }() - - return sqlexec.DrainRecordSet(ctx, rs, 8) -} - -func (c *bootstrapContext) EtcdClient() *clientv3.Client { - return c.etcdCli -} - -func (c *bootstrapContext) SessionPool() extension.SessionPool { - return c.sessionPool -} - -// Bootstrap bootstraps all extensions -func Bootstrap(ctx context.Context, do *domain.Domain) error { - extensions, err := extension.GetExtensions() - if err != nil { - return err - } - - if extensions == nil { - return nil - } - - pool := do.SysSessionPool() - sctx, err := pool.Get() - if err != nil { - return err - } - defer pool.Put(sctx) - - executor, ok := sctx.(sqlexec.SQLExecutor) - if !ok { - return errors.Errorf("type '%T' cannot be casted to 'sqlexec.SQLExecutor'", sctx) - } - - return extensions.Bootstrap(&bootstrapContext{ - Context: ctx, - sessionPool: pool, - sqlExecutor: executor, - etcdCli: do.GetEtcdClient(), - }) -} diff --git a/extension/main_test.go b/extension/main_test.go deleted file mode 100644 index a2600e2ad71ca..0000000000000 --- a/extension/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package extension - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/extension/registry_test.go b/extension/registry_test.go deleted file mode 100644 index d353388c07edc..0000000000000 --- a/extension/registry_test.go +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package extension_test - -import ( - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sem" - "github.com/stretchr/testify/require" -) - -func TestSetupExtensions(t *testing.T) { - defer func() { - extension.Reset() - }() - - extension.Reset() - require.NoError(t, extension.Setup()) - extensions, err := extension.GetExtensions() - require.NoError(t, err) - require.Equal(t, 0, len(extensions.Manifests())) - - extension.Reset() - require.NoError(t, extension.Register("test1")) - require.NoError(t, extension.Register("test2")) - require.NoError(t, extension.Setup()) - extensions, err = extension.GetExtensions() - require.NoError(t, err) - require.Equal(t, 2, len(extensions.Manifests())) - require.Equal(t, "test1", extensions.Manifests()[0].Name()) - require.Equal(t, "test2", extensions.Manifests()[1].Name()) -} - -func TestExtensionRegisterName(t *testing.T) { - defer extension.Reset() - - // test empty name - extension.Reset() - require.EqualError(t, extension.Register(""), "extension name should not be empty") - - // test dup name - extension.Reset() - require.NoError(t, extension.Register("test")) - require.EqualError(t, extension.Register("test"), "extension with name 'test' already registered") -} - -func TestRegisterExtensionWithClose(t *testing.T) { - defer extension.Reset() - - // normal register - extension.Reset() - cnt := 0 - require.NoError(t, extension.Register("test1", extension.WithClose(func() { - cnt++ - }))) - require.NoError(t, extension.Setup()) - require.Equal(t, 0, cnt) - - // reset will call close - extension.Reset() - require.Equal(t, 1, cnt) - - // reset again has no effect - extension.Reset() - require.Equal(t, 1, cnt) - - // Auto close when error - cnt = 0 - extension.Reset() - require.NoError(t, extension.Register("test1", extension.WithClose(func() { - cnt++ - }))) - require.NoError(t, extension.RegisterFactory("test2", func() ([]extension.Option, error) { - return nil, errors.New("error abc") - })) - require.EqualError(t, extension.Setup(), "error abc") - require.Equal(t, 1, cnt) -} - -func TestRegisterExtensionWithDyncPrivs(t *testing.T) { - defer extension.Reset() - - origDynPrivs := privileges.GetDynamicPrivileges() - origDynPrivs = append([]string{}, origDynPrivs...) - - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", "priv2"}))) - require.NoError(t, extension.Setup()) - privs := privileges.GetDynamicPrivileges() - require.Equal(t, origDynPrivs, privs[:len(origDynPrivs)]) - require.Equal(t, []string{"PRIV1", "PRIV2"}, privs[len(origDynPrivs):]) - - // test for empty dynamic privilege name - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", ""}))) - require.EqualError(t, extension.Setup(), "privilege name should not be empty") - require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) - - // test for duplicate name with builtin - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", "ROLE_ADMIN"}))) - require.EqualError(t, extension.Setup(), "privilege is already registered") - require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) - - // test for duplicate name with other extension - extension.Reset() - require.NoError(t, extension.Register("test1", extension.WithCustomDynPrivs([]string{"priv1"}))) - require.NoError(t, extension.Register("test2", extension.WithCustomDynPrivs([]string{"priv2", "priv1"}))) - require.EqualError(t, extension.Setup(), "privilege is already registered") - require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) -} - -func TestRegisterExtensionWithSysVars(t *testing.T) { - defer extension.Reset() - - sysVar1 := &variable.SysVar{ - Scope: variable.ScopeGlobal | variable.ScopeSession, - Name: "var1", - Value: variable.On, - Type: variable.TypeBool, - } - - sysVar2 := &variable.SysVar{ - Scope: variable.ScopeSession, - Name: "var2", - Value: "val2", - Type: variable.TypeStr, - } - - // normal register - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1, sysVar2}))) - require.NoError(t, extension.Setup()) - require.Same(t, sysVar1, variable.GetSysVar("var1")) - require.Same(t, sysVar2, variable.GetSysVar("var2")) - - // test for empty name - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{ - {Scope: variable.ScopeGlobal, Name: "", Value: "val3"}, - }))) - require.EqualError(t, extension.Setup(), "system var name should not be empty") - require.Nil(t, variable.GetSysVar("")) - - // test for duplicate name with builtin - extension.Reset() - require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{ - sysVar1, - {Scope: variable.ScopeGlobal, Name: variable.TiDBSnapshot, Value: "val3"}, - }))) - require.EqualError(t, extension.Setup(), "system var 'tidb_snapshot' has already registered") - require.Nil(t, variable.GetSysVar("var1")) - require.Equal(t, "", variable.GetSysVar(variable.TiDBSnapshot).Value) - require.Equal(t, variable.ScopeSession, variable.GetSysVar(variable.TiDBSnapshot).Scope) - - // test for duplicate name with other extension - extension.Reset() - require.NoError(t, extension.Register("test1", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1, sysVar2}))) - require.NoError(t, extension.Register("test2", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1}))) - require.EqualError(t, extension.Setup(), "system var 'var1' has already registered") - require.Nil(t, variable.GetSysVar("var1")) - require.Nil(t, variable.GetSysVar("var2")) -} - -func TestSetVariablePrivilege(t *testing.T) { - defer extension.Reset() - - sysVar1 := &variable.SysVar{ - Scope: variable.ScopeGlobal | variable.ScopeSession, - Name: "var1", - Value: "1", - MinValue: 0, - MaxValue: 100, - Type: variable.TypeInt, - RequireDynamicPrivileges: func(isGlobal bool, sem bool) []string { - privs := []string{"priv1"} - if isGlobal { - privs = append(privs, "priv2") - } - - if sem { - privs = append(privs, "restricted_priv3") - } - - return privs - }, - } - - extension.Reset() - require.NoError(t, extension.Register( - "test", - extension.WithCustomSysVariables([]*variable.SysVar{sysVar1}), - extension.WithCustomDynPrivs([]string{"priv1", "priv2", "restricted_priv3"}), - )) - require.NoError(t, extension.Setup()) - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create user u2@localhost") - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) - - sem.Disable() - tk1.MustExec("set @@var1=7") - tk1.MustQuery("select @@var1").Check(testkit.Rows("7")) - - require.EqualError(t, tk2.ExecToErr("set @@var1=10"), "[planner:1227]Access denied; you need (at least one of) the SUPER or priv1 privilege(s) for this operation") - tk2.MustQuery("select @@var1").Check(testkit.Rows("1")) - - tk.MustExec("GRANT priv1 on *.* TO u2@localhost") - tk2.MustExec("set @@var1=8") - tk2.MustQuery("select @@var1").Check(testkit.Rows("8")) - - tk1.MustExec("set @@global.var1=17") - tk1.MustQuery("select @@global.var1").Check(testkit.Rows("17")) - - tk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN on *.* TO u2@localhost") - require.EqualError(t, tk2.ExecToErr("set @@global.var1=18"), "[planner:1227]Access denied; you need (at least one of) the SUPER or priv2 privilege(s) for this operation") - tk2.MustQuery("select @@global.var1").Check(testkit.Rows("17")) - - tk.MustExec("GRANT priv2 on *.* TO u2@localhost") - tk2.MustExec("set @@global.var1=18") - tk2.MustQuery("select @@global.var1").Check(testkit.Rows("18")) - - sem.Enable() - defer sem.Disable() - - require.EqualError(t, tk1.ExecToErr("set @@global.var1=27"), "[planner:1227]Access denied; you need (at least one of) the restricted_priv3 privilege(s) for this operation") - tk1.MustQuery("select @@global.var1").Check(testkit.Rows("18")) - - require.EqualError(t, tk2.ExecToErr("set @@global.var1=27"), "[planner:1227]Access denied; you need (at least one of) the restricted_priv3 privilege(s) for this operation") - tk2.MustQuery("select @@global.var1").Check(testkit.Rows("18")) - - tk.MustExec("GRANT restricted_priv3 on *.* TO u2@localhost") - tk2.MustExec("set @@global.var1=28") - tk2.MustQuery("select @@global.var1").Check(testkit.Rows("28")) -} - -func TestCustomAccessCheck(t *testing.T) { - defer extension.Reset() - extension.Reset() - - require.NoError(t, extension.Register( - "test", - extension.WithCustomDynPrivs([]string{"priv1", "priv2", "restricted_priv3"}), - extension.WithCustomAccessCheck(func(db, tbl, column string, priv mysql.PrivilegeType, sem bool) []string { - if db != "test" || tbl != "t1" { - return nil - } - - var privs []string - if priv == mysql.SelectPriv { - privs = append(privs, "priv1") - } else if priv == mysql.UpdatePriv { - privs = append(privs, "priv2") - if sem { - privs = append(privs, "restricted_priv3") - } - } else { - return nil - } - - return privs - }), - )) - require.NoError(t, extension.Setup()) - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create user u2@localhost") - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk1.MustExec("use test") - - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("GRANT all on test.t1 TO u2@localhost") - tk2.MustExec("use test") - - tk1.MustExec("create table t1(id int primary key, v int)") - tk1.MustExec("insert into t1 values (1, 10), (2, 20)") - - tk1.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) - tk1.MustQuery("select * from t1").Check(testkit.Rows("1 10", "2 20")) - - require.EqualError(t, tk2.ExecToErr("select * from t1 where id=1"), "[planner:1142]SELECT command denied to user 'u2'@'localhost' for table 't1'") - require.EqualError(t, tk2.ExecToErr("select * from t1"), "[planner:1142]SELECT command denied to user 'u2'@'localhost' for table 't1'") - - tk.MustExec("GRANT priv1 on *.* TO u2@localhost") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) - tk2.MustQuery("select * from t1").Check(testkit.Rows("1 10", "2 20")) - - require.EqualError(t, tk2.ExecToErr("update t1 set v=11 where id=1"), "[planner:8121]privilege check for 'Update' fail") - require.EqualError(t, tk2.ExecToErr("update t1 set v=11 where id<2"), "[planner:8121]privilege check for 'Update' fail") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) - - tk.MustExec("GRANT priv2 on *.* TO u2@localhost") - tk2.MustExec("update t1 set v=11 where id=1") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 11")) - - tk2.MustExec("update t1 set v=12 where id<2") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) - - sem.Enable() - defer sem.Disable() - - require.EqualError(t, tk1.ExecToErr("update t1 set v=21 where id=1"), "[planner:8121]privilege check for 'Update' fail") - require.EqualError(t, tk1.ExecToErr("update t1 set v=21 where id<2"), "[planner:8121]privilege check for 'Update' fail") - tk1.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) - - require.EqualError(t, tk2.ExecToErr("update t1 set v=21 where id=1"), "[planner:8121]privilege check for 'Update' fail") - require.EqualError(t, tk2.ExecToErr("update t1 set v=21 where id<2"), "[planner:8121]privilege check for 'Update' fail") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) - - tk.MustExec("GRANT restricted_priv3 on *.* TO u2@localhost") - tk2.MustExec("update t1 set v=31 where id=1") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 31")) - - tk2.MustExec("update t1 set v=32 where id<2") - tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 32")) -} diff --git a/extension/session.go b/extension/session.go deleted file mode 100644 index db2b913f9a390..0000000000000 --- a/extension/session.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package extension - -import ( - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" -) - -// ConnEventInfo is the connection info for the event -type ConnEventInfo struct { - *variable.ConnectionInfo - SessionAlias string - ActiveRoles []*auth.RoleIdentity - Error error -} - -// ConnEventTp is the type of the connection event -type ConnEventTp uint8 - -const ( - // ConnConnected means connection connected, but not handshake yet - ConnConnected ConnEventTp = iota - // ConnHandshakeAccepted means connection is accepted after handshake - ConnHandshakeAccepted - // ConnHandshakeRejected means connections is rejected after handshake - ConnHandshakeRejected - // ConnReset means the connection is reset - ConnReset - // ConnDisconnected means the connection is disconnected - ConnDisconnected -) - -// StmtEventTp is the type of the statement event -type StmtEventTp uint8 - -const ( - // StmtError means the stmt is failed - StmtError StmtEventTp = iota - // StmtSuccess means the stmt is successfully executed - StmtSuccess -) - -// StmtEventInfo is the information of stmt event -type StmtEventInfo interface { - // User returns the user of the session - User() *auth.UserIdentity - // ActiveRoles returns the active roles of the user - ActiveRoles() []*auth.RoleIdentity - // CurrentDB returns the current database - CurrentDB() string - // ConnectionInfo returns the connection info of the current session - ConnectionInfo() *variable.ConnectionInfo - // SessionAlias returns the session alias value set by user - SessionAlias() string - // StmtNode returns the parsed ast of the statement - // When parse error, this method will return a nil value - StmtNode() ast.StmtNode - // ExecuteStmtNode will return the `ast.ExecuteStmt` node when the current statement is EXECUTE, - // otherwise a nil value will be returned - ExecuteStmtNode() *ast.ExecuteStmt - // ExecutePreparedStmt will return the prepared stmt node for the EXECUTE statement. - // If the current statement is not EXECUTE or prepared statement is not found, a nil value will be returned - ExecutePreparedStmt() ast.StmtNode - // PreparedParams will return the params for the EXECUTE statement - PreparedParams() []types.Datum - // OriginalText will return the text of the statement. - // Notice that for the EXECUTE statement, the prepared statement text will be used as the return value - OriginalText() string - // SQLDigest will return the normalized and redact text of the `OriginalText()` - SQLDigest() (normalized string, digest *parser.Digest) - // AffectedRows will return the affected rows of the current statement - AffectedRows() uint64 - // RelatedTables will return the related tables of the current statement - RelatedTables() []stmtctx.TableEntry - // GetError will return the error when the current statement is failed - GetError() error -} - -// SessionHandler is used to listen session events -type SessionHandler struct { - OnConnectionEvent func(ConnEventTp, *ConnEventInfo) - OnStmtEvent func(StmtEventTp, StmtEventInfo) -} - -func newSessionExtensions(es *Extensions) *SessionExtensions { - connExtensions := &SessionExtensions{} - for _, m := range es.Manifests() { - if m.sessionHandlerFactory != nil { - if handler := m.sessionHandlerFactory(); handler != nil { - if fn := handler.OnConnectionEvent; fn != nil { - connExtensions.connectionEventFuncs = append(connExtensions.connectionEventFuncs, fn) - } - if fn := handler.OnStmtEvent; fn != nil { - connExtensions.stmtEventFuncs = append(connExtensions.stmtEventFuncs, fn) - } - } - } - } - return connExtensions -} - -// SessionExtensions is the extensions -type SessionExtensions struct { - connectionEventFuncs []func(ConnEventTp, *ConnEventInfo) - stmtEventFuncs []func(StmtEventTp, StmtEventInfo) -} - -// OnConnectionEvent will be called when a connection event happens -func (es *SessionExtensions) OnConnectionEvent(tp ConnEventTp, event *ConnEventInfo) { - if es == nil { - return - } - - for _, fn := range es.connectionEventFuncs { - fn(tp, event) - } -} - -// HasStmtEventListeners returns a bool that indicates if any stmt event listener exists -func (es *SessionExtensions) HasStmtEventListeners() bool { - return es != nil && len(es.stmtEventFuncs) > 0 -} - -// OnStmtEvent will be called when a stmt event happens -func (es *SessionExtensions) OnStmtEvent(tp StmtEventTp, event StmtEventInfo) { - if es == nil { - return - } - - for _, fn := range es.stmtEventFuncs { - fn(tp, event) - } -} diff --git a/go.mod b/go.mod index 1539d371625a5..62526af91ed8e 100644 --- a/go.mod +++ b/go.mod @@ -83,7 +83,7 @@ require ( github.com/pingcap/kvproto v0.0.0-20230925123611-87bebcc0d071 github.com/pingcap/log v1.1.1-0.20230317032135-a0d097d16e22 github.com/pingcap/sysutil v1.0.1-0.20230407040306-fb007c5aff21 - github.com/pingcap/tidb/parser v0.0.0-20211011031125-9b13dc409c5e + github.com/pingcap/tidb/pkg/parser v0.0.0-20211011031125-9b13dc409c5e github.com/pingcap/tipb v0.0.0-20230919054518-dfd7d194838f github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 @@ -311,5 +311,5 @@ replace ( // fix potential security issue(CVE-2020-26160) introduced by indirect dependency. github.com/dgrijalva/jwt-go => github.com/form3tech-oss/jwt-go v3.2.6-0.20210809144907-32ab6a8243d7+incompatible github.com/go-ldap/ldap/v3 => github.com/YangKeao/ldap/v3 v3.4.5-0.20230421065457-369a3bab1117 - github.com/pingcap/tidb/parser => ./parser + github.com/pingcap/tidb/pkg/parser => ./pkg/parser ) diff --git a/infoschema/BUILD.bazel b/infoschema/BUILD.bazel deleted file mode 100644 index e30ad011587c6..0000000000000 --- a/infoschema/BUILD.bazel +++ /dev/null @@ -1,99 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "infoschema", - srcs = [ - "builder.go", - "cache.go", - "cluster.go", - "error.go", - "infoschema.go", - "metric_table_def.go", - "metrics_schema.go", - "tables.go", - ], - importpath = "github.com/pingcap/tidb/infoschema", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl/placement", - "//ddl/resourcegroup", - "//domain/infosync", - "//errno", - "//infoschema/metrics", - "//kv", - "//meta", - "//meta/autoid", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//privilege", - "//session/txninfo", - "//sessionctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//types", - "//util", - "//util/dbterror", - "//util/deadlockhistory", - "//util/domainutil", - "//util/execdetails", - "//util/logutil", - "//util/mathutil", - "//util/mock", - "//util/pdapi", - "//util/sem", - "//util/set", - "//util/sqlexec", - "//util/stmtsummary", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/diagnosticspb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//tikv", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "infoschema_test", - timeout = "short", - srcs = [ - "infoschema_test.go", - "main_test.go", - "metrics_schema_test.go", - ], - embed = [":infoschema"], - flaky = True, - shard_count = 8, - deps = [ - "//ddl/placement", - "//domain", - "//kv", - "//meta", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//session", - "//store/mockstore", - "//table", - "//testkit", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util", - "//util/set", - "@com_github_pingcap_errors//:errors", - "@com_github_prometheus_prometheus//promql", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/infoschema/builder.go b/infoschema/builder.go deleted file mode 100644 index 9d9289b4f1a6e..0000000000000 --- a/infoschema/builder.go +++ /dev/null @@ -1,1166 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "cmp" - "context" - "fmt" - "slices" - "strings" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/domainutil" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -type policyGetter struct { - is *infoSchema -} - -func (p *policyGetter) GetPolicy(policyID int64) (*model.PolicyInfo, error) { - if policy, ok := p.is.PolicyByID(policyID); ok { - return policy, nil - } - return nil, errors.Errorf("Cannot find placement policy with ID: %d", policyID) -} - -type bundleInfoBuilder struct { - deltaUpdate bool - // tables or partitions that need to update placement bundle - updateTables map[int64]interface{} - // all tables or partitions referring these policies should update placement bundle - updatePolicies map[int64]interface{} - // partitions that need to update placement bundle - updatePartitions map[int64]interface{} -} - -func (b *bundleInfoBuilder) ensureMap() { - if b.updateTables == nil { - b.updateTables = make(map[int64]interface{}) - } - if b.updatePartitions == nil { - b.updatePartitions = make(map[int64]interface{}) - } - if b.updatePolicies == nil { - b.updatePolicies = make(map[int64]interface{}) - } -} - -func (b *bundleInfoBuilder) SetDeltaUpdateBundles() { - b.deltaUpdate = true -} - -func (b *bundleInfoBuilder) deleteBundle(is *infoSchema, tblID int64) { - delete(is.ruleBundleMap, tblID) -} - -func (b *bundleInfoBuilder) markTableBundleShouldUpdate(tblID int64) { - b.ensureMap() - b.updateTables[tblID] = struct{}{} -} - -func (b *bundleInfoBuilder) markPartitionBundleShouldUpdate(partID int64) { - b.ensureMap() - b.updatePartitions[partID] = struct{}{} -} - -func (b *bundleInfoBuilder) markBundlesReferPolicyShouldUpdate(policyID int64) { - b.ensureMap() - b.updatePolicies[policyID] = struct{}{} -} - -func (b *bundleInfoBuilder) updateInfoSchemaBundles(is *infoSchema) { - if b.deltaUpdate { - b.completeUpdateTables(is) - for tblID := range b.updateTables { - b.updateTableBundles(is, tblID) - } - return - } - - // do full update bundles - is.ruleBundleMap = make(map[int64]*placement.Bundle) - for _, tbls := range is.schemaMap { - for _, tbl := range tbls.tables { - b.updateTableBundles(is, tbl.Meta().ID) - } - } -} - -func (b *bundleInfoBuilder) completeUpdateTables(is *infoSchema) { - if len(b.updatePolicies) == 0 && len(b.updatePartitions) == 0 { - return - } - - for _, tbls := range is.schemaMap { - for _, tbl := range tbls.tables { - tblInfo := tbl.Meta() - if tblInfo.PlacementPolicyRef != nil { - if _, ok := b.updatePolicies[tblInfo.PlacementPolicyRef.ID]; ok { - b.markTableBundleShouldUpdate(tblInfo.ID) - } - } - - if tblInfo.Partition != nil { - for _, par := range tblInfo.Partition.Definitions { - if _, ok := b.updatePartitions[par.ID]; ok { - b.markTableBundleShouldUpdate(tblInfo.ID) - } - } - } - } - } -} - -func (b *bundleInfoBuilder) updateTableBundles(is *infoSchema, tableID int64) { - tbl, ok := is.TableByID(tableID) - if !ok { - b.deleteBundle(is, tableID) - return - } - - getter := &policyGetter{is: is} - bundle, err := placement.NewTableBundle(getter, tbl.Meta()) - if err != nil { - logutil.BgLogger().Error("create table bundle failed", zap.Error(err)) - } else if bundle != nil { - is.ruleBundleMap[tableID] = bundle - } else { - b.deleteBundle(is, tableID) - } - - if tbl.Meta().Partition == nil { - return - } - - for _, par := range tbl.Meta().Partition.Definitions { - bundle, err = placement.NewPartitionBundle(getter, par) - if err != nil { - logutil.BgLogger().Error("create partition bundle failed", - zap.Error(err), - zap.Int64("partition id", par.ID), - ) - } else if bundle != nil { - is.ruleBundleMap[par.ID] = bundle - } else { - b.deleteBundle(is, par.ID) - } - } -} - -// Builder builds a new InfoSchema. -type Builder struct { - is *infoSchema - // dbInfos do not need to be copied everytime applying a diff, instead, - // they can be copied only once over the whole lifespan of Builder. - // This map will indicate which DB has been copied, so that they - // don't need to be copied again. - dirtyDB map[string]bool - // TODO: store is only used by autoid allocators - // detach allocators from storage, use passed transaction in the feature - store kv.Storage - - factory func() (pools.Resource, error) - bundleInfoBuilder -} - -// ApplyDiff applies SchemaDiff to the new InfoSchema. -// Return the detail updated table IDs that are produced from SchemaDiff and an error. -func (b *Builder) ApplyDiff(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - b.is.schemaMetaVersion = diff.Version - switch diff.Type { - case model.ActionCreateSchema: - return nil, b.applyCreateSchema(m, diff) - case model.ActionDropSchema: - return b.applyDropSchema(diff.SchemaID), nil - case model.ActionRecoverSchema: - return b.applyRecoverSchema(m, diff) - case model.ActionModifySchemaCharsetAndCollate: - return nil, b.applyModifySchemaCharsetAndCollate(m, diff) - case model.ActionModifySchemaDefaultPlacement: - return nil, b.applyModifySchemaDefaultPlacement(m, diff) - case model.ActionCreatePlacementPolicy: - return nil, b.applyCreatePolicy(m, diff) - case model.ActionDropPlacementPolicy: - return b.applyDropPolicy(diff.SchemaID), nil - case model.ActionAlterPlacementPolicy: - return b.applyAlterPolicy(m, diff) - case model.ActionCreateResourceGroup: - return nil, b.applyCreateOrAlterResourceGroup(m, diff) - case model.ActionAlterResourceGroup: - return nil, b.applyCreateOrAlterResourceGroup(m, diff) - case model.ActionDropResourceGroup: - return b.applyDropResourceGroup(m, diff), nil - case model.ActionTruncateTablePartition, model.ActionTruncateTable: - return b.applyTruncateTableOrPartition(m, diff) - case model.ActionDropTable, model.ActionDropTablePartition: - return b.applyDropTableOrPartition(m, diff) - case model.ActionRecoverTable: - return b.applyRecoverTable(m, diff) - case model.ActionCreateTables: - return b.applyCreateTables(m, diff) - case model.ActionReorganizePartition, model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning: - return b.applyReorganizePartition(m, diff) - case model.ActionExchangeTablePartition: - return b.applyExchangeTablePartition(m, diff) - case model.ActionFlashbackCluster: - return []int64{-1}, nil - default: - return b.applyDefaultAction(m, diff) - } -} - -func (b *Builder) applyCreateTables(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs := make([]int64, 0, len(diff.AffectedOpts)) - if diff.AffectedOpts != nil { - for _, opt := range diff.AffectedOpts { - affectedDiff := &model.SchemaDiff{ - Version: diff.Version, - Type: model.ActionCreateTable, - SchemaID: opt.SchemaID, - TableID: opt.TableID, - OldSchemaID: opt.OldSchemaID, - OldTableID: opt.OldTableID, - } - affectedIDs, err := b.ApplyDiff(m, affectedDiff) - if err != nil { - return nil, errors.Trace(err) - } - tblIDs = append(tblIDs, affectedIDs...) - } - } - return tblIDs, nil -} - -func (b *Builder) applyTruncateTableOrPartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - - if diff.Type == model.ActionTruncateTable { - b.deleteBundle(b.is, diff.OldTableID) - b.markTableBundleShouldUpdate(diff.TableID) - } - - for _, opt := range diff.AffectedOpts { - if diff.Type == model.ActionTruncateTablePartition { - // Reduce the impact on DML when executing partition DDL. eg. - // While session 1 performs the DML operation associated with partition 1, - // the TRUNCATE operation of session 2 on partition 2 does not cause the operation of session 1 to fail. - tblIDs = append(tblIDs, opt.OldTableID) - b.markPartitionBundleShouldUpdate(opt.TableID) - } - b.deleteBundle(b.is, opt.OldTableID) - } - return tblIDs, nil -} - -func (b *Builder) applyDropTableOrPartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - - b.markTableBundleShouldUpdate(diff.TableID) - for _, opt := range diff.AffectedOpts { - b.deleteBundle(b.is, opt.OldTableID) - } - return tblIDs, nil -} - -func (b *Builder) applyReorganizePartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - for _, opt := range diff.AffectedOpts { - if opt.OldTableID != 0 { - b.deleteBundle(b.is, opt.OldTableID) - } - if opt.TableID != 0 { - b.markTableBundleShouldUpdate(opt.TableID) - } - // TODO: Should we also check markPartitionBundleShouldUpdate?!? - } - return tblIDs, nil -} - -func (b *Builder) applyExchangeTablePartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - // It is not in StatePublic. - if diff.OldTableID == diff.TableID && diff.OldSchemaID == diff.SchemaID { - ntIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - if diff.AffectedOpts == nil || diff.AffectedOpts[0].OldSchemaID == 0 { - return ntIDs, err - } - // Reload parition tabe. - ptSchemaID := diff.AffectedOpts[0].OldSchemaID - ptID := diff.AffectedOpts[0].TableID - ptDiff := &model.SchemaDiff{ - Type: diff.Type, - Version: diff.Version, - TableID: ptID, - SchemaID: ptSchemaID, - OldTableID: ptID, - OldSchemaID: ptSchemaID, - } - ptIDs, err := b.applyTableUpdate(m, ptDiff) - if err != nil { - return nil, errors.Trace(err) - } - return append(ptIDs, ntIDs...), nil - } - ntSchemaID := diff.OldSchemaID - ntID := diff.OldTableID - ptSchemaID := diff.SchemaID - ptID := diff.TableID - partID := diff.TableID - if len(diff.AffectedOpts) > 0 { - ptID = diff.AffectedOpts[0].TableID - if diff.AffectedOpts[0].SchemaID != 0 { - ptSchemaID = diff.AffectedOpts[0].SchemaID - } - } - // The normal table needs to be updated first: - // Just update the tables separately - currDiff := &model.SchemaDiff{ - // This is only for the case since https://github.com/pingcap/tidb/pull/45877 - // Fixed now, by adding back the AffectedOpts - // to carry the partitioned Table ID. - Type: diff.Type, - Version: diff.Version, - TableID: ntID, - SchemaID: ntSchemaID, - } - if ptID != partID { - currDiff.TableID = partID - currDiff.OldTableID = ntID - currDiff.OldSchemaID = ntSchemaID - } - ntIDs, err := b.applyTableUpdate(m, currDiff) - if err != nil { - return nil, errors.Trace(err) - } - // partID is the new id for the non-partitioned table! - b.markTableBundleShouldUpdate(partID) - // Then the partitioned table, will re-read the whole table, including all partitions! - currDiff.TableID = ptID - currDiff.SchemaID = ptSchemaID - currDiff.OldTableID = ptID - currDiff.OldSchemaID = ptSchemaID - ptIDs, err := b.applyTableUpdate(m, currDiff) - if err != nil { - return nil, errors.Trace(err) - } - // ntID is the new id for the partition! - b.markPartitionBundleShouldUpdate(ntID) - err = updateAutoIDForExchangePartition(b.store, ptSchemaID, ptID, ntSchemaID, ntID) - if err != nil { - return nil, errors.Trace(err) - } - return append(ptIDs, ntIDs...), nil -} - -func (b *Builder) applyRecoverTable(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - - for _, opt := range diff.AffectedOpts { - b.markTableBundleShouldUpdate(opt.TableID) - } - return tblIDs, nil -} - -func updateAutoIDForExchangePartition(store kv.Storage, ptSchemaID, ptID, ntSchemaID, ntID int64) error { - err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - ptAutoIDs, err := t.GetAutoIDAccessors(ptSchemaID, ptID).Get() - if err != nil { - return err - } - - // non-partition table auto IDs. - ntAutoIDs, err := t.GetAutoIDAccessors(ntSchemaID, ntID).Get() - if err != nil { - return err - } - - // Set both tables to the maximum auto IDs between normal table and partitioned table. - newAutoIDs := meta.AutoIDGroup{ - RowID: mathutil.Max(ptAutoIDs.RowID, ntAutoIDs.RowID), - IncrementID: mathutil.Max(ptAutoIDs.IncrementID, ntAutoIDs.IncrementID), - RandomID: mathutil.Max(ptAutoIDs.RandomID, ntAutoIDs.RandomID), - } - err = t.GetAutoIDAccessors(ptSchemaID, ptID).Put(newAutoIDs) - if err != nil { - return err - } - err = t.GetAutoIDAccessors(ntSchemaID, ntID).Put(newAutoIDs) - if err != nil { - return err - } - return nil - }) - - return err -} - -func (b *Builder) applyDefaultAction(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - tblIDs, err := b.applyTableUpdate(m, diff) - if err != nil { - return nil, errors.Trace(err) - } - - for _, opt := range diff.AffectedOpts { - var err error - affectedDiff := &model.SchemaDiff{ - Version: diff.Version, - Type: diff.Type, - SchemaID: opt.SchemaID, - TableID: opt.TableID, - OldSchemaID: opt.OldSchemaID, - OldTableID: opt.OldTableID, - } - affectedIDs, err := b.ApplyDiff(m, affectedDiff) - if err != nil { - return nil, errors.Trace(err) - } - tblIDs = append(tblIDs, affectedIDs...) - } - - return tblIDs, nil -} - -func (b *Builder) applyTableUpdate(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - roDBInfo, ok := b.is.SchemaByID(diff.SchemaID) - if !ok { - return nil, ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", diff.SchemaID), - ) - } - dbInfo := b.getSchemaAndCopyIfNecessary(roDBInfo.Name.L) - var oldTableID, newTableID int64 - switch diff.Type { - case model.ActionCreateSequence, model.ActionRecoverTable: - newTableID = diff.TableID - case model.ActionCreateTable: - // WARN: when support create table with foreign key in https://github.com/pingcap/tidb/pull/37148, - // create table with foreign key requires a multi-step state change(none -> write-only -> public), - // when the table's state changes from write-only to public, infoSchema need to drop the old table - // which state is write-only, otherwise, infoSchema.sortedTablesBuckets will contain 2 table both - // have the same ID, but one state is write-only, another table's state is public, it's unexpected. - // - // WARN: this change will break the compatibility if execute create table with foreign key DDL when upgrading TiDB, - // since old-version TiDB doesn't know to delete the old table. - // Since the cluster-index feature also has similar problem, we chose to prevent DDL execution during the upgrade process to avoid this issue. - oldTableID = diff.OldTableID - newTableID = diff.TableID - case model.ActionDropTable, model.ActionDropView, model.ActionDropSequence: - oldTableID = diff.TableID - case model.ActionTruncateTable, model.ActionCreateView, - model.ActionExchangeTablePartition, model.ActionAlterTablePartitioning, - model.ActionRemovePartitioning: - oldTableID = diff.OldTableID - newTableID = diff.TableID - default: - oldTableID = diff.TableID - newTableID = diff.TableID - } - // handle placement rule cache - switch diff.Type { - case model.ActionCreateTable: - b.markTableBundleShouldUpdate(newTableID) - case model.ActionDropTable: - b.deleteBundle(b.is, oldTableID) - case model.ActionTruncateTable: - b.deleteBundle(b.is, oldTableID) - b.markTableBundleShouldUpdate(newTableID) - case model.ActionRecoverTable: - b.markTableBundleShouldUpdate(newTableID) - case model.ActionAlterTablePlacement: - b.markTableBundleShouldUpdate(newTableID) - } - b.copySortedTables(oldTableID, newTableID) - - tblIDs := make([]int64, 0, 2) - // We try to reuse the old allocator, so the cached auto ID can be reused. - var allocs autoid.Allocators - if tableIDIsValid(oldTableID) { - if oldTableID == newTableID && (diff.Type != model.ActionRenameTable && diff.Type != model.ActionRenameTables) && - // For repairing table in TiDB cluster, given 2 normal node and 1 repair node. - // For normal node's information schema, repaired table is existed. - // For repair node's information schema, repaired table is filtered (couldn't find it in `is`). - // So here skip to reserve the allocators when repairing table. - diff.Type != model.ActionRepairTable && - // Alter sequence will change the sequence info in the allocator, so the old allocator is not valid any more. - diff.Type != model.ActionAlterSequence { - oldAllocs, _ := b.is.AllocByID(oldTableID) - allocs = filterAllocators(diff, oldAllocs) - } - - tmpIDs := tblIDs - if (diff.Type == model.ActionRenameTable || diff.Type == model.ActionRenameTables) && diff.OldSchemaID != diff.SchemaID { - oldRoDBInfo, ok := b.is.SchemaByID(diff.OldSchemaID) - if !ok { - return nil, ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", diff.OldSchemaID), - ) - } - oldDBInfo := b.getSchemaAndCopyIfNecessary(oldRoDBInfo.Name.L) - tmpIDs = b.applyDropTable(oldDBInfo, oldTableID, tmpIDs) - } else { - tmpIDs = b.applyDropTable(dbInfo, oldTableID, tmpIDs) - } - - if oldTableID != newTableID { - // Update tblIDs only when oldTableID != newTableID because applyCreateTable() also updates tblIDs. - tblIDs = tmpIDs - } - } - if tableIDIsValid(newTableID) { - // All types except DropTableOrView. - var err error - tblIDs, err = b.applyCreateTable(m, dbInfo, newTableID, allocs, diff.Type, tblIDs) - if err != nil { - return nil, errors.Trace(err) - } - } - return tblIDs, nil -} - -func filterAllocators(diff *model.SchemaDiff, oldAllocs autoid.Allocators) autoid.Allocators { - var newAllocs autoid.Allocators - switch diff.Type { - case model.ActionRebaseAutoID, model.ActionModifyTableAutoIdCache: - // Only drop auto-increment allocator. - newAllocs = oldAllocs.Filter(func(a autoid.Allocator) bool { - tp := a.GetType() - return tp != autoid.RowIDAllocType && tp != autoid.AutoIncrementType - }) - case model.ActionRebaseAutoRandomBase: - // Only drop auto-random allocator. - newAllocs = oldAllocs.Filter(func(a autoid.Allocator) bool { - tp := a.GetType() - return tp != autoid.AutoRandomType - }) - default: - // Keep all allocators. - newAllocs = oldAllocs - } - return newAllocs -} - -func appendAffectedIDs(affected []int64, tblInfo *model.TableInfo) []int64 { - affected = append(affected, tblInfo.ID) - if pi := tblInfo.GetPartitionInfo(); pi != nil { - for _, def := range pi.Definitions { - affected = append(affected, def.ID) - } - } - return affected -} - -// copySortedTables copies sortedTables for old table and new table for later modification. -func (b *Builder) copySortedTables(oldTableID, newTableID int64) { - if tableIDIsValid(oldTableID) { - b.copySortedTablesBucket(tableBucketIdx(oldTableID)) - } - if tableIDIsValid(newTableID) && newTableID != oldTableID { - b.copySortedTablesBucket(tableBucketIdx(newTableID)) - } -} - -func (b *Builder) applyCreateOrAlterResourceGroup(m *meta.Meta, diff *model.SchemaDiff) error { - group, err := m.GetResourceGroup(diff.SchemaID) - if err != nil { - return errors.Trace(err) - } - if group == nil { - return ErrResourceGroupNotExists.GenWithStackByArgs(fmt.Sprintf("(Group ID %d)", diff.SchemaID)) - } - // TODO: need mark updated? - b.is.setResourceGroup(group) - return nil -} - -func (b *Builder) applyDropResourceGroup(m *meta.Meta, diff *model.SchemaDiff) []int64 { - group, ok := b.is.ResourceGroupByID(diff.SchemaID) - if !ok { - return nil - } - b.is.deleteResourceGroup(group.Name.L) - // TODO: return the related information. - return []int64{} -} - -func (b *Builder) applyCreatePolicy(m *meta.Meta, diff *model.SchemaDiff) error { - po, err := m.GetPolicy(diff.SchemaID) - if err != nil { - return errors.Trace(err) - } - if po == nil { - return ErrPlacementPolicyNotExists.GenWithStackByArgs( - fmt.Sprintf("(Policy ID %d)", diff.SchemaID), - ) - } - - if _, ok := b.is.PolicyByID(po.ID); ok { - // if old policy with the same id exists, it means replace, - // so the tables referring this policy's bundle should be updated - b.markBundlesReferPolicyShouldUpdate(po.ID) - } - - b.is.setPolicy(po) - return nil -} - -func (b *Builder) applyAlterPolicy(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - po, err := m.GetPolicy(diff.SchemaID) - if err != nil { - return nil, errors.Trace(err) - } - - if po == nil { - return nil, ErrPlacementPolicyNotExists.GenWithStackByArgs( - fmt.Sprintf("(Policy ID %d)", diff.SchemaID), - ) - } - - b.is.setPolicy(po) - b.markBundlesReferPolicyShouldUpdate(po.ID) - // TODO: return the policy related table ids - return []int64{}, nil -} - -func (b *Builder) applyCreateSchema(m *meta.Meta, diff *model.SchemaDiff) error { - di, err := m.GetDatabase(diff.SchemaID) - if err != nil { - return errors.Trace(err) - } - if di == nil { - // When we apply an old schema diff, the database may has been dropped already, so we need to fall back to - // full load. - return ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", diff.SchemaID), - ) - } - b.is.schemaMap[di.Name.L] = &schemaTables{dbInfo: di, tables: make(map[string]table.Table)} - return nil -} - -func (b *Builder) applyModifySchemaCharsetAndCollate(m *meta.Meta, diff *model.SchemaDiff) error { - di, err := m.GetDatabase(diff.SchemaID) - if err != nil { - return errors.Trace(err) - } - if di == nil { - // This should never happen. - return ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", diff.SchemaID), - ) - } - newDbInfo := b.getSchemaAndCopyIfNecessary(di.Name.L) - newDbInfo.Charset = di.Charset - newDbInfo.Collate = di.Collate - return nil -} - -func (b *Builder) applyModifySchemaDefaultPlacement(m *meta.Meta, diff *model.SchemaDiff) error { - di, err := m.GetDatabase(diff.SchemaID) - if err != nil { - return errors.Trace(err) - } - if di == nil { - // This should never happen. - return ErrDatabaseNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", diff.SchemaID), - ) - } - newDbInfo := b.getSchemaAndCopyIfNecessary(di.Name.L) - newDbInfo.PlacementPolicyRef = di.PlacementPolicyRef - return nil -} - -func (b *Builder) applyDropPolicy(PolicyID int64) []int64 { - po, ok := b.is.PolicyByID(PolicyID) - if !ok { - return nil - } - b.is.deletePolicy(po.Name.L) - // TODO: return the policy related table ids - return []int64{} -} - -func (b *Builder) applyDropSchema(schemaID int64) []int64 { - di, ok := b.is.SchemaByID(schemaID) - if !ok { - return nil - } - delete(b.is.schemaMap, di.Name.L) - - // Copy the sortedTables that contain the table we are going to drop. - tableIDs := make([]int64, 0, len(di.Tables)) - bucketIdxMap := make(map[int]struct{}, len(di.Tables)) - for _, tbl := range di.Tables { - bucketIdxMap[tableBucketIdx(tbl.ID)] = struct{}{} - // TODO: If the table ID doesn't exist. - tableIDs = appendAffectedIDs(tableIDs, tbl) - } - for bucketIdx := range bucketIdxMap { - b.copySortedTablesBucket(bucketIdx) - } - - di = di.Clone() - for _, id := range tableIDs { - b.deleteBundle(b.is, id) - b.applyDropTable(di, id, nil) - } - return tableIDs -} - -func (b *Builder) applyRecoverSchema(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { - if di, ok := b.is.SchemaByID(diff.SchemaID); ok { - return nil, ErrDatabaseExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", di.ID), - ) - } - di, err := m.GetDatabase(diff.SchemaID) - if err != nil { - return nil, errors.Trace(err) - } - b.is.schemaMap[di.Name.L] = &schemaTables{ - dbInfo: di, - tables: make(map[string]table.Table, len(diff.AffectedOpts)), - } - return b.applyCreateTables(m, diff) -} - -func (b *Builder) copySortedTablesBucket(bucketIdx int) { - oldSortedTables := b.is.sortedTablesBuckets[bucketIdx] - newSortedTables := make(sortedTables, len(oldSortedTables)) - copy(newSortedTables, oldSortedTables) - b.is.sortedTablesBuckets[bucketIdx] = newSortedTables -} - -func (b *Builder) applyCreateTable(m *meta.Meta, dbInfo *model.DBInfo, tableID int64, allocs autoid.Allocators, tp model.ActionType, affected []int64) ([]int64, error) { - tblInfo, err := m.GetTable(dbInfo.ID, tableID) - if err != nil { - return nil, errors.Trace(err) - } - if tblInfo == nil { - // When we apply an old schema diff, the table may has been dropped already, so we need to fall back to - // full load. - return nil, ErrTableNotExists.GenWithStackByArgs( - fmt.Sprintf("(Schema ID %d)", dbInfo.ID), - fmt.Sprintf("(Table ID %d)", tableID), - ) - } - - switch tp { - case model.ActionDropTablePartition: - case model.ActionTruncateTablePartition: - // ReorganizePartition handle the bundles in applyReorganizePartition - case model.ActionReorganizePartition, model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning: - default: - pi := tblInfo.GetPartitionInfo() - if pi != nil { - for _, partition := range pi.Definitions { - b.markPartitionBundleShouldUpdate(partition.ID) - } - } - } - - if tp != model.ActionTruncateTablePartition { - affected = appendAffectedIDs(affected, tblInfo) - } - - // Failpoint check whether tableInfo should be added to repairInfo. - // Typically used in repair table test to load mock `bad` tableInfo into repairInfo. - failpoint.Inject("repairFetchCreateTable", func(val failpoint.Value) { - if val.(bool) { - if domainutil.RepairInfo.InRepairMode() && tp != model.ActionRepairTable && domainutil.RepairInfo.CheckAndFetchRepairedTable(dbInfo, tblInfo) { - failpoint.Return(nil, nil) - } - } - }) - - ConvertCharsetCollateToLowerCaseIfNeed(tblInfo) - ConvertOldVersionUTF8ToUTF8MB4IfNeed(tblInfo) - - if len(allocs.Allocs) == 0 { - allocs = autoid.NewAllocatorsFromTblInfo(b.store, dbInfo.ID, tblInfo) - } else { - tblVer := autoid.AllocOptionTableInfoVersion(tblInfo.Version) - switch tp { - case model.ActionRebaseAutoID, model.ActionModifyTableAutoIdCache: - idCacheOpt := autoid.CustomAutoIncCacheOption(tblInfo.AutoIdCache) - // If the allocator type might be AutoIncrementType, create both AutoIncrementType - // and RowIDAllocType allocator for it. Because auto id and row id could share the same allocator. - // Allocate auto id may route to allocate row id, if row id allocator is nil, the program panic! - for _, tp := range [2]autoid.AllocatorType{autoid.AutoIncrementType, autoid.RowIDAllocType} { - newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), tp, tblVer, idCacheOpt) - allocs = allocs.Append(newAlloc) - } - case model.ActionRebaseAutoRandomBase: - newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), autoid.AutoRandomType, tblVer) - allocs = allocs.Append(newAlloc) - case model.ActionModifyColumn: - // Change column attribute from auto_increment to auto_random. - if tblInfo.ContainsAutoRandomBits() && allocs.Get(autoid.AutoRandomType) == nil { - // Remove auto_increment allocator. - allocs = allocs.Filter(func(a autoid.Allocator) bool { - return a.GetType() != autoid.AutoIncrementType && a.GetType() != autoid.RowIDAllocType - }) - newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), autoid.AutoRandomType, tblVer) - allocs = allocs.Append(newAlloc) - } - } - } - tbl, err := b.tableFromMeta(allocs, tblInfo) - if err != nil { - return nil, errors.Trace(err) - } - - b.is.addReferredForeignKeys(dbInfo.Name, tblInfo) - - tableNames := b.is.schemaMap[dbInfo.Name.L] - tableNames.tables[tblInfo.Name.L] = tbl - bucketIdx := tableBucketIdx(tableID) - b.is.sortedTablesBuckets[bucketIdx] = append(b.is.sortedTablesBuckets[bucketIdx], tbl) - slices.SortFunc(b.is.sortedTablesBuckets[bucketIdx], func(i, j table.Table) int { - return cmp.Compare(i.Meta().ID, j.Meta().ID) - }) - - if tblInfo.TempTableType != model.TempTableNone { - b.addTemporaryTable(tableID) - } - - newTbl, ok := b.is.TableByID(tableID) - if ok { - dbInfo.Tables = append(dbInfo.Tables, newTbl.Meta()) - } - return affected, nil -} - -// ConvertCharsetCollateToLowerCaseIfNeed convert the charset / collation of table and its columns to lower case, -// if the table's version is prior to TableInfoVersion3. -func ConvertCharsetCollateToLowerCaseIfNeed(tbInfo *model.TableInfo) { - if tbInfo.Version >= model.TableInfoVersion3 { - return - } - tbInfo.Charset = strings.ToLower(tbInfo.Charset) - tbInfo.Collate = strings.ToLower(tbInfo.Collate) - for _, col := range tbInfo.Columns { - col.SetCharset(strings.ToLower(col.GetCharset())) - col.SetCollate(strings.ToLower(col.GetCollate())) - } -} - -// ConvertOldVersionUTF8ToUTF8MB4IfNeed convert old version UTF8 to UTF8MB4 if config.TreatOldVersionUTF8AsUTF8MB4 is enable. -func ConvertOldVersionUTF8ToUTF8MB4IfNeed(tbInfo *model.TableInfo) { - if tbInfo.Version >= model.TableInfoVersion2 || !config.GetGlobalConfig().TreatOldVersionUTF8AsUTF8MB4 { - return - } - if tbInfo.Charset == charset.CharsetUTF8 { - tbInfo.Charset = charset.CharsetUTF8MB4 - tbInfo.Collate = charset.CollationUTF8MB4 - } - for _, col := range tbInfo.Columns { - if col.Version < model.ColumnInfoVersion2 && col.GetCharset() == charset.CharsetUTF8 { - col.SetCharset(charset.CharsetUTF8MB4) - col.SetCollate(charset.CollationUTF8MB4) - } - } -} - -func (b *Builder) applyDropTable(dbInfo *model.DBInfo, tableID int64, affected []int64) []int64 { - bucketIdx := tableBucketIdx(tableID) - sortedTbls := b.is.sortedTablesBuckets[bucketIdx] - idx := sortedTbls.searchTable(tableID) - if idx == -1 { - return affected - } - if tableNames, ok := b.is.schemaMap[dbInfo.Name.L]; ok { - tblInfo := sortedTbls[idx].Meta() - delete(tableNames.tables, tblInfo.Name.L) - affected = appendAffectedIDs(affected, tblInfo) - } - // Remove the table in sorted table slice. - b.is.sortedTablesBuckets[bucketIdx] = append(sortedTbls[0:idx], sortedTbls[idx+1:]...) - - // Remove the table in temporaryTables - if b.is.temporaryTableIDs != nil { - delete(b.is.temporaryTableIDs, tableID) - } - - // The old DBInfo still holds a reference to old table info, we need to remove it. - for i, tblInfo := range dbInfo.Tables { - if tblInfo.ID == tableID { - if i == len(dbInfo.Tables)-1 { - dbInfo.Tables = dbInfo.Tables[:i] - } else { - dbInfo.Tables = append(dbInfo.Tables[:i], dbInfo.Tables[i+1:]...) - } - b.is.deleteReferredForeignKeys(dbInfo.Name, tblInfo) - break - } - } - return affected -} - -// Build builds and returns the built infoschema. -func (b *Builder) Build() InfoSchema { - b.updateInfoSchemaBundles(b.is) - return b.is -} - -// InitWithOldInfoSchema initializes an empty new InfoSchema by copies all the data from old InfoSchema. -func (b *Builder) InitWithOldInfoSchema(oldSchema InfoSchema) *Builder { - oldIS := oldSchema.(*infoSchema) - b.is.schemaMetaVersion = oldIS.schemaMetaVersion - b.copySchemasMap(oldIS) - b.copyBundlesMap(oldIS) - b.copyPoliciesMap(oldIS) - b.copyResourceGroupMap(oldIS) - b.copyTemporaryTableIDsMap(oldIS) - b.copyReferredForeignKeyMap(oldIS) - - copy(b.is.sortedTablesBuckets, oldIS.sortedTablesBuckets) - return b -} - -func (b *Builder) copySchemasMap(oldIS *infoSchema) { - for k, v := range oldIS.schemaMap { - b.is.schemaMap[k] = v - } -} - -func (b *Builder) copyBundlesMap(oldIS *infoSchema) { - b.is.ruleBundleMap = make(map[int64]*placement.Bundle) - for id, v := range oldIS.ruleBundleMap { - b.is.ruleBundleMap[id] = v - } -} - -func (b *Builder) copyPoliciesMap(oldIS *infoSchema) { - is := b.is - for _, v := range oldIS.AllPlacementPolicies() { - is.policyMap[v.Name.L] = v - } -} - -func (b *Builder) copyResourceGroupMap(oldIS *infoSchema) { - is := b.is - for _, v := range oldIS.AllResourceGroups() { - is.resourceGroupMap[v.Name.L] = v - } -} - -func (b *Builder) copyTemporaryTableIDsMap(oldIS *infoSchema) { - is := b.is - if len(oldIS.temporaryTableIDs) == 0 { - is.temporaryTableIDs = nil - return - } - - is.temporaryTableIDs = make(map[int64]struct{}) - for tblID := range oldIS.temporaryTableIDs { - is.temporaryTableIDs[tblID] = struct{}{} - } -} - -func (b *Builder) copyReferredForeignKeyMap(oldIS *infoSchema) { - for k, v := range oldIS.referredForeignKeyMap { - b.is.referredForeignKeyMap[k] = v - } -} - -// getSchemaAndCopyIfNecessary creates a new schemaTables instance when a table in the database has changed. -// It also does modifications on the new one because old schemaTables must be read-only. -// And it will only copy the changed database once in the lifespan of the Builder. -// NOTE: please make sure the dbName is in lowercase. -func (b *Builder) getSchemaAndCopyIfNecessary(dbName string) *model.DBInfo { - if !b.dirtyDB[dbName] { - b.dirtyDB[dbName] = true - oldSchemaTables := b.is.schemaMap[dbName] - newSchemaTables := &schemaTables{ - dbInfo: oldSchemaTables.dbInfo.Copy(), - tables: make(map[string]table.Table, len(oldSchemaTables.tables)), - } - for k, v := range oldSchemaTables.tables { - newSchemaTables.tables[k] = v - } - b.is.schemaMap[dbName] = newSchemaTables - return newSchemaTables.dbInfo - } - return b.is.schemaMap[dbName].dbInfo -} - -// InitWithDBInfos initializes an empty new InfoSchema with a slice of DBInfo, all placement rules, and schema version. -func (b *Builder) InitWithDBInfos(dbInfos []*model.DBInfo, policies []*model.PolicyInfo, resourceGroups []*model.ResourceGroupInfo, schemaVersion int64) (*Builder, error) { - info := b.is - info.schemaMetaVersion = schemaVersion - // build the policies. - for _, policy := range policies { - info.setPolicy(policy) - } - - // build the groups. - for _, group := range resourceGroups { - info.setResourceGroup(group) - } - - // Maintain foreign key reference information. - for _, di := range dbInfos { - for _, t := range di.Tables { - b.is.addReferredForeignKeys(di.Name, t) - } - } - - for _, di := range dbInfos { - err := b.createSchemaTablesForDB(di, b.tableFromMeta) - if err != nil { - return nil, errors.Trace(err) - } - } - - // Initialize virtual tables. - for _, driver := range drivers { - err := b.createSchemaTablesForDB(driver.DBInfo, driver.TableFromMeta) - if err != nil { - return nil, errors.Trace(err) - } - } - - // Sort all tables by `ID` - for _, v := range info.sortedTablesBuckets { - slices.SortFunc(v, func(a, b table.Table) int { - return cmp.Compare(a.Meta().ID, b.Meta().ID) - }) - } - return b, nil -} - -func (b *Builder) tableFromMeta(alloc autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) { - ret, err := tables.TableFromMeta(alloc, tblInfo) - if err != nil { - return nil, errors.Trace(err) - } - if t, ok := ret.(table.CachedTable); ok { - var tmp pools.Resource - tmp, err = b.factory() - if err != nil { - return nil, errors.Trace(err) - } - - err = t.Init(tmp.(sqlexec.SQLExecutor)) - if err != nil { - return nil, errors.Trace(err) - } - } - return ret, nil -} - -type tableFromMetaFunc func(alloc autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) - -func (b *Builder) createSchemaTablesForDB(di *model.DBInfo, tableFromMeta tableFromMetaFunc) error { - schTbls := &schemaTables{ - dbInfo: di, - tables: make(map[string]table.Table, len(di.Tables)), - } - b.is.schemaMap[di.Name.L] = schTbls - - for _, t := range di.Tables { - allocs := autoid.NewAllocatorsFromTblInfo(b.store, di.ID, t) - var tbl table.Table - tbl, err := tableFromMeta(allocs, t) - if err != nil { - return errors.Wrap(err, fmt.Sprintf("Build table `%s`.`%s` schema failed", di.Name.O, t.Name.O)) - } - schTbls.tables[t.Name.L] = tbl - sortedTbls := b.is.sortedTablesBuckets[tableBucketIdx(t.ID)] - b.is.sortedTablesBuckets[tableBucketIdx(t.ID)] = append(sortedTbls, tbl) - if tblInfo := tbl.Meta(); tblInfo.TempTableType != model.TempTableNone { - b.addTemporaryTable(tblInfo.ID) - } - } - return nil -} - -func (b *Builder) addTemporaryTable(tblID int64) { - if b.is.temporaryTableIDs == nil { - b.is.temporaryTableIDs = make(map[int64]struct{}) - } - b.is.temporaryTableIDs[tblID] = struct{}{} -} - -type virtualTableDriver struct { - *model.DBInfo - TableFromMeta tableFromMetaFunc -} - -var drivers []*virtualTableDriver - -// RegisterVirtualTable register virtual tables to the builder. -func RegisterVirtualTable(dbInfo *model.DBInfo, tableFromMeta tableFromMetaFunc) { - drivers = append(drivers, &virtualTableDriver{dbInfo, tableFromMeta}) -} - -// NewBuilder creates a new Builder with a Handle. -func NewBuilder(store kv.Storage, factory func() (pools.Resource, error)) *Builder { - return &Builder{ - store: store, - is: &infoSchema{ - schemaMap: map[string]*schemaTables{}, - policyMap: map[string]*model.PolicyInfo{}, - resourceGroupMap: map[string]*model.ResourceGroupInfo{}, - ruleBundleMap: map[int64]*placement.Bundle{}, - sortedTablesBuckets: make([]sortedTables, bucketCount), - referredForeignKeyMap: make(map[SchemaAndTableName][]*model.ReferredFKInfo), - }, - dirtyDB: make(map[string]bool), - factory: factory, - } -} - -func tableBucketIdx(tableID int64) int { - return int(tableID % bucketCount) -} - -func tableIDIsValid(tableID int64) bool { - return tableID != 0 -} diff --git a/infoschema/cache.go b/infoschema/cache.go deleted file mode 100644 index 38e00ce092984..0000000000000 --- a/infoschema/cache.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "sort" - "sync" - - infoschema_metrics "github.com/pingcap/tidb/infoschema/metrics" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// InfoCache handles information schema, including getting and setting. -// The cache behavior, however, is transparent and under automatic management. -// It only promised to cache the infoschema, if it is newer than all the cached. -type InfoCache struct { - mu sync.RWMutex - // cache is sorted by both SchemaVersion and timestamp in descending order, assume they have same order - cache []schemaAndTimestamp -} - -type schemaAndTimestamp struct { - infoschema InfoSchema - timestamp int64 -} - -// NewCache creates a new InfoCache. -func NewCache(capacity int) *InfoCache { - return &InfoCache{ - cache: make([]schemaAndTimestamp, 0, capacity), - } -} - -// ReSize re-size the cache. -func (h *InfoCache) ReSize(capacity int) { - h.mu.Lock() - defer h.mu.Unlock() - if cap(h.cache) == capacity { - return - } - oldCache := h.cache - h.cache = make([]schemaAndTimestamp, 0, capacity) - for i, v := range oldCache { - if i >= capacity { - break - } - h.cache = append(h.cache, v) - } -} - -// Size returns the size of the cache, export for test. -func (h *InfoCache) Size() int { - h.mu.Lock() - defer h.mu.Unlock() - return len(h.cache) -} - -// Reset resets the cache. -func (h *InfoCache) Reset(capacity int) { - h.mu.Lock() - defer h.mu.Unlock() - h.cache = make([]schemaAndTimestamp, 0, capacity) -} - -// GetLatest gets the newest information schema. -func (h *InfoCache) GetLatest() InfoSchema { - h.mu.RLock() - defer h.mu.RUnlock() - infoschema_metrics.GetLatestCounter.Inc() - if len(h.cache) > 0 { - infoschema_metrics.HitLatestCounter.Inc() - return h.cache[0].infoschema - } - return nil -} - -// Len returns the size of the cache -func (h *InfoCache) Len() int { - return len(h.cache) -} - -func (h *InfoCache) getSchemaByTimestampNoLock(ts uint64) (InfoSchema, bool) { - logutil.BgLogger().Debug("SCHEMA CACHE get schema", zap.Uint64("timestamp", ts)) - // search one by one instead of binary search, because the timestamp of a schema could be 0 - // this is ok because the size of h.cache is small (currently set to 16) - // moreover, the most likely hit element in the array is the first one in steady mode - // thus it may have better performance than binary search - for i, is := range h.cache { - if is.timestamp == 0 || (i > 0 && h.cache[i-1].infoschema.SchemaMetaVersion() != is.infoschema.SchemaMetaVersion()+1) { - // the schema version doesn't have a timestamp or there is a gap in the schema cache - // ignore all the schema cache equals or less than this version in search by timestamp - break - } - if ts >= uint64(is.timestamp) { - // found the largest version before the given ts - return is.infoschema, true - } - } - - logutil.BgLogger().Debug("SCHEMA CACHE no schema found") - return nil, false -} - -// GetByVersion gets the information schema based on schemaVersion. Returns nil if it is not loaded. -func (h *InfoCache) GetByVersion(version int64) InfoSchema { - h.mu.RLock() - defer h.mu.RUnlock() - return h.getByVersionNoLock(version) -} - -func (h *InfoCache) getByVersionNoLock(version int64) InfoSchema { - infoschema_metrics.GetVersionCounter.Inc() - i := sort.Search(len(h.cache), func(i int) bool { - return h.cache[i].infoschema.SchemaMetaVersion() <= version - }) - - // `GetByVersion` is allowed to load the latest schema that is less than argument `version`. - // Consider cache has values [10, 9, _, _, 6, 5, 4, 3, 2, 1], version 8 and 7 is empty because of the diff is empty. - // If we want to get version 8, we can return version 6 because v7 and v8 do not change anything, they are totally the same, - // in this case the `i` will not be 0. - // If i == 0, it means the argument version is `10`, or greater than `10`, if `version` is 10 - // `h.cache[i].SchemaMetaVersion() == version` will be true, so we can return the latest schema, return nil if not. - // The following code is equivalent to: - // ``` - // if h.GetLatest().SchemaMetaVersion() < version { - // return nil - // } - // - // if i < len(h.cache) { - // hitVersionCounter.Inc() - // return h.cache[i] - // } - // ``` - - if i < len(h.cache) && (i != 0 || h.cache[i].infoschema.SchemaMetaVersion() == version) { - infoschema_metrics.HitVersionCounter.Inc() - return h.cache[i].infoschema - } - return nil -} - -// GetBySnapshotTS gets the information schema based on snapshotTS. -// It searches the schema cache and find the schema with max schema ts that equals or smaller than given snapshot ts -// Where the schema ts is the commitTs of the txn creates the schema diff -func (h *InfoCache) GetBySnapshotTS(snapshotTS uint64) InfoSchema { - h.mu.RLock() - defer h.mu.RUnlock() - - infoschema_metrics.GetTSCounter.Inc() - if schema, ok := h.getSchemaByTimestampNoLock(snapshotTS); ok { - infoschema_metrics.HitTSCounter.Inc() - return schema - } - return nil -} - -// Insert will **TRY** to insert the infoschema into the cache. -// It only promised to cache the newest infoschema. -// It returns 'true' if it is cached, 'false' otherwise. -// schemaTs is the commitTs of the txn creates the schema diff, which indicates since when the schema version is taking effect -func (h *InfoCache) Insert(is InfoSchema, schemaTS uint64) bool { - logutil.BgLogger().Debug("INSERT SCHEMA", zap.Uint64("schema ts", schemaTS), zap.Int64("schema version", is.SchemaMetaVersion())) - h.mu.Lock() - defer h.mu.Unlock() - - version := is.SchemaMetaVersion() - - // assume this is the timestamp order as well - i := sort.Search(len(h.cache), func(i int) bool { - return h.cache[i].infoschema.SchemaMetaVersion() <= version - }) - - // cached entry - if i < len(h.cache) && h.cache[i].infoschema.SchemaMetaVersion() == version { - // update timestamp if it is not 0 and cached one is 0 - if schemaTS > 0 && h.cache[i].timestamp == 0 { - h.cache[i].timestamp = int64(schemaTS) - } - return true - } - - if len(h.cache) < cap(h.cache) { - // has free space, grown the slice - h.cache = h.cache[:len(h.cache)+1] - copy(h.cache[i+1:], h.cache[i:]) - h.cache[i] = schemaAndTimestamp{ - infoschema: is, - timestamp: int64(schemaTS), - } - } else if i < len(h.cache) { - // drop older schema - copy(h.cache[i+1:], h.cache[i:]) - h.cache[i] = schemaAndTimestamp{ - infoschema: is, - timestamp: int64(schemaTS), - } - } else { - // older than all cached schemas, refuse to cache it - return false - } - - return true -} diff --git a/infoschema/cluster.go b/infoschema/cluster.go deleted file mode 100644 index 44b7e1cdc5a62..0000000000000 --- a/infoschema/cluster.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "net" - "strconv" - "strings" - - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sem" -) - -// Cluster table indicates that these tables need to get data from other tidb nodes, which may get from all other nodes, or may get from the ddl owner. -// Cluster table list, attention: -// 1. the table name should be upper case. -// 2. For tables that need to get data from all other TiDB nodes, clusterTableName should equal to "CLUSTER_" + memTableTableName. -const ( - // ClusterTableSlowLog is the string constant of cluster slow query memory table. - ClusterTableSlowLog = "CLUSTER_SLOW_QUERY" - ClusterTableProcesslist = "CLUSTER_PROCESSLIST" - // ClusterTableStatementsSummary is the string constant of cluster statement summary table. - ClusterTableStatementsSummary = "CLUSTER_STATEMENTS_SUMMARY" - // ClusterTableStatementsSummaryHistory is the string constant of cluster statement summary history table. - ClusterTableStatementsSummaryHistory = "CLUSTER_STATEMENTS_SUMMARY_HISTORY" - // ClusterTableStatementsSummaryEvicted is the string constant of cluster statement summary evict table. - ClusterTableStatementsSummaryEvicted = "CLUSTER_STATEMENTS_SUMMARY_EVICTED" - // ClusterTableTiDBTrx is the string constant of cluster transaction running table. - ClusterTableTiDBTrx = "CLUSTER_TIDB_TRX" - // ClusterTableDeadlocks is the string constant of cluster dead lock table. - ClusterTableDeadlocks = "CLUSTER_DEADLOCKS" - // ClusterTableDeadlocks is the string constant of cluster transaction summary table. - ClusterTableTrxSummary = "CLUSTER_TRX_SUMMARY" - // ClusterTableMemoryUsage is the memory usage status of tidb cluster. - ClusterTableMemoryUsage = "CLUSTER_MEMORY_USAGE" - // ClusterTableMemoryUsageOpsHistory is the memory control operators history of tidb cluster. - ClusterTableMemoryUsageOpsHistory = "CLUSTER_MEMORY_USAGE_OPS_HISTORY" -) - -// memTableToAllTiDBClusterTables means add memory table to cluster table that will send cop request to all TiDB nodes. -var memTableToAllTiDBClusterTables = map[string]string{ - TableSlowQuery: ClusterTableSlowLog, - TableProcesslist: ClusterTableProcesslist, - TableStatementsSummary: ClusterTableStatementsSummary, - TableStatementsSummaryHistory: ClusterTableStatementsSummaryHistory, - TableStatementsSummaryEvicted: ClusterTableStatementsSummaryEvicted, - TableTiDBTrx: ClusterTableTiDBTrx, - TableDeadlocks: ClusterTableDeadlocks, - TableTrxSummary: ClusterTableTrxSummary, - TableMemoryUsage: ClusterTableMemoryUsage, - TableMemoryUsageOpsHistory: ClusterTableMemoryUsageOpsHistory, -} - -// memTableToDDLOwnerClusterTables means add memory table to cluster table that will send cop request to DDL owner node. -var memTableToDDLOwnerClusterTables = map[string]string{ - TableTiFlashReplica: TableTiFlashReplica, -} - -// ClusterTableCopDestination means the destination that cluster tables will send cop requests to. -type ClusterTableCopDestination int - -const ( - // AllTiDB is uese by CLUSTER_* table, means that these tables will send cop request to all TiDB nodes. - AllTiDB ClusterTableCopDestination = iota - // DDLOwner is uese by tiflash_replica currently, means that this table will send cop request to DDL owner node. - DDLOwner -) - -// GetClusterTableCopDestination gets cluster table cop request destination. -func GetClusterTableCopDestination(tableName string) ClusterTableCopDestination { - if _, exist := memTableToDDLOwnerClusterTables[strings.ToUpper(tableName)]; exist { - return DDLOwner - } - return AllTiDB -} - -func init() { - var addrCol = columnInfo{name: util.ClusterTableInstanceColumnName, tp: mysql.TypeVarchar, size: 64} - for memTableName, clusterMemTableName := range memTableToAllTiDBClusterTables { - memTableCols := tableNameToColumns[memTableName] - if len(memTableCols) == 0 { - continue - } - cols := make([]columnInfo, 0, len(memTableCols)+1) - cols = append(cols, addrCol) - cols = append(cols, memTableCols...) - tableNameToColumns[clusterMemTableName] = cols - } -} - -// isClusterTableByName used to check whether the table is a cluster memory table. -func isClusterTableByName(dbName, tableName string) bool { - dbName = strings.ToUpper(dbName) - switch dbName { - case util.InformationSchemaName.O, util.PerformanceSchemaName.O: - tableName = strings.ToUpper(tableName) - for _, name := range memTableToAllTiDBClusterTables { - name = strings.ToUpper(name) - if name == tableName { - return true - } - } - for _, name := range memTableToDDLOwnerClusterTables { - name = strings.ToUpper(name) - if name == tableName { - return true - } - } - default: - } - return false -} - -// AppendHostInfoToRows appends host info to the rows. -func AppendHostInfoToRows(ctx sessionctx.Context, rows [][]types.Datum) ([][]types.Datum, error) { - addr, err := GetInstanceAddr(ctx) - if err != nil { - return nil, err - } - for i := range rows { - row := make([]types.Datum, 0, len(rows[i])+1) - row = append(row, types.NewStringDatum(addr)) - row = append(row, rows[i]...) - rows[i] = row - } - return rows, nil -} - -// GetInstanceAddr gets the instance address. -func GetInstanceAddr(ctx sessionctx.Context) (string, error) { - serverInfo, err := infosync.GetServerInfo() - if err != nil { - return "", err - } - addr := net.JoinHostPort(serverInfo.IP, strconv.FormatUint(uint64(serverInfo.StatusPort), 10)) - if sem.IsEnabled() { - checker := privilege.GetPrivilegeManager(ctx) - if checker == nil || !checker.RequestDynamicVerification(ctx.GetSessionVars().ActiveRoles, "RESTRICTED_TABLES_ADMIN", false) { - addr = serverInfo.ID - } - } - return addr, nil -} diff --git a/infoschema/error.go b/infoschema/error.go deleted file mode 100644 index e99b258b0dc83..0000000000000 --- a/infoschema/error.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -var ( - // ErrDatabaseExists returns for database already exists. - ErrDatabaseExists = dbterror.ClassSchema.NewStd(mysql.ErrDBCreateExists) - // ErrDatabaseDropExists returns for dropping a non-existent database. - ErrDatabaseDropExists = dbterror.ClassSchema.NewStd(mysql.ErrDBDropExists) - // ErrAccessDenied return when the user doesn't have the permission to access the table. - ErrAccessDenied = dbterror.ClassSchema.NewStd(mysql.ErrAccessDenied) - // ErrDatabaseNotExists returns for database not exists. - ErrDatabaseNotExists = dbterror.ClassSchema.NewStd(mysql.ErrBadDB) - // ErrPlacementPolicyExists returns for placement_policy policy already exists. - ErrPlacementPolicyExists = dbterror.ClassSchema.NewStd(mysql.ErrPlacementPolicyExists) - // ErrPlacementPolicyNotExists return for placement_policy policy not exists. - ErrPlacementPolicyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrPlacementPolicyNotExists) - // ErrResourceGroupExists return for resource group already exists. - ErrResourceGroupExists = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupExists) - // ErrResourceGroupNotExists return for resource group not exists. - ErrResourceGroupNotExists = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupNotExists) - // ErrResourceGroupInvalidBackgroundTaskName return for unknown resource group background task name. - ErrResourceGroupInvalidBackgroundTaskName = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupInvalidBackgroundTaskName) - // ErrReservedSyntax for internal syntax. - ErrReservedSyntax = dbterror.ClassSchema.NewStd(mysql.ErrReservedSyntax) - // ErrTableExists returns for table already exists. - ErrTableExists = dbterror.ClassSchema.NewStd(mysql.ErrTableExists) - // ErrTableDropExists returns for dropping a non-existent table. - ErrTableDropExists = dbterror.ClassSchema.NewStd(mysql.ErrBadTable) - // ErrSequenceDropExists returns for dropping a non-exist sequence. - ErrSequenceDropExists = dbterror.ClassSchema.NewStd(mysql.ErrUnknownSequence) - // ErrColumnNotExists returns for column not exists. - ErrColumnNotExists = dbterror.ClassSchema.NewStd(mysql.ErrBadField) - // ErrColumnExists returns for column already exists. - ErrColumnExists = dbterror.ClassSchema.NewStd(mysql.ErrDupFieldName) - // ErrKeyNameDuplicate returns for index duplicate when rename index. - ErrKeyNameDuplicate = dbterror.ClassSchema.NewStd(mysql.ErrDupKeyName) - // ErrNonuniqTable returns when none unique tables errors. - ErrNonuniqTable = dbterror.ClassSchema.NewStd(mysql.ErrNonuniqTable) - // ErrMultiplePriKey returns for multiple primary keys. - ErrMultiplePriKey = dbterror.ClassSchema.NewStd(mysql.ErrMultiplePriKey) - // ErrTooManyKeyParts returns for too many key parts. - ErrTooManyKeyParts = dbterror.ClassSchema.NewStd(mysql.ErrTooManyKeyParts) - // ErrForeignKeyNotExists returns for foreign key not exists. - ErrForeignKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrCantDropFieldOrKey) - // ErrTableNotLockedForWrite returns for write tables when only hold the table read lock. - ErrTableNotLockedForWrite = dbterror.ClassSchema.NewStd(mysql.ErrTableNotLockedForWrite) - // ErrTableNotLocked returns when session has explicitly lock tables, then visit unlocked table will return this error. - ErrTableNotLocked = dbterror.ClassSchema.NewStd(mysql.ErrTableNotLocked) - // ErrTableNotExists returns for table not exists. - ErrTableNotExists = dbterror.ClassSchema.NewStd(mysql.ErrNoSuchTable) - // ErrKeyNotExists returns for index not exists. - ErrKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrKeyDoesNotExist) - // ErrCannotAddForeign returns for foreign key exists. - ErrCannotAddForeign = dbterror.ClassSchema.NewStd(mysql.ErrCannotAddForeign) - // ErrForeignKeyOnPartitioned returns for foreign key on partition table. - ErrForeignKeyOnPartitioned = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyOnPartitioned) - // ErrForeignKeyNotMatch returns for foreign key not match. - ErrForeignKeyNotMatch = dbterror.ClassSchema.NewStd(mysql.ErrWrongFkDef) - // ErrIndexExists returns for index already exists. - ErrIndexExists = dbterror.ClassSchema.NewStd(mysql.ErrDupIndex) - // ErrUserDropExists returns for dropping a non-existent user. - ErrUserDropExists = dbterror.ClassSchema.NewStd(mysql.ErrBadUser) - // ErrUserAlreadyExists return for creating a existent user. - ErrUserAlreadyExists = dbterror.ClassSchema.NewStd(mysql.ErrUserAlreadyExists) - // ErrTableLocked returns when the table was locked by other session. - ErrTableLocked = dbterror.ClassSchema.NewStd(mysql.ErrTableLocked) - // ErrWrongObject returns when the table/view/sequence is not the expected object. - ErrWrongObject = dbterror.ClassSchema.NewStd(mysql.ErrWrongObject) - // ErrAdminCheckTable returns when the check table in temporary mode. - ErrAdminCheckTable = dbterror.ClassSchema.NewStd(mysql.ErrAdminCheckTable) - // ErrEmptyDatabase returns when the database is unexpectedly empty. - ErrEmptyDatabase = dbterror.ClassSchema.NewStd(mysql.ErrBadDB) - // ErrForbidSchemaChange returns when the schema change is illegal - ErrForbidSchemaChange = dbterror.ClassSchema.NewStd(mysql.ErrForbidSchemaChange) - // ErrTableWithoutPrimaryKey returns when there is no primary key on a table and sql_require_primary_key is set - ErrTableWithoutPrimaryKey = dbterror.ClassSchema.NewStd(mysql.ErrTableWithoutPrimaryKey) - // ErrForeignKeyCannotUseVirtualColumn returns when foreign key refer virtual generated column. - ErrForeignKeyCannotUseVirtualColumn = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyCannotUseVirtualColumn) - // ErrForeignKeyCannotOpenParent returns when foreign key refer table not exists. - ErrForeignKeyCannotOpenParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyCannotOpenParent) - // ErrForeignKeyNoColumnInParent returns when foreign key refer columns don't exist in parent table. - ErrForeignKeyNoColumnInParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyNoColumnInParent) - // ErrForeignKeyNoIndexInParent returns when foreign key refer columns don't have related index in parent table. - ErrForeignKeyNoIndexInParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyNoIndexInParent) - // ErrForeignKeyColumnNotNull returns when foreign key with SET NULL constrain and the related column has not null. - ErrForeignKeyColumnNotNull = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyColumnNotNull) - // ErrResourceGroupSupportDisabled returns for resource group feature is disabled - ErrResourceGroupSupportDisabled = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupSupportDisabled) - // ErrCheckConstraintDupName returns for duplicate constraint names. - ErrCheckConstraintDupName = dbterror.ClassSchema.NewStd(mysql.ErrCheckConstraintDupName) -) diff --git a/infoschema/infoschema.go b/infoschema/infoschema.go deleted file mode 100644 index 73ca30c2e31a6..0000000000000 --- a/infoschema/infoschema.go +++ /dev/null @@ -1,777 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "cmp" - "fmt" - "slices" - "sort" - "sync" - - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mock" -) - -// InfoSchema is the interface used to retrieve the schema information. -// It works as a in memory cache and doesn't handle any schema change. -// InfoSchema is read-only, and the returned value is a copy. -// TODO: add more methods to retrieve tables and columns. -type InfoSchema interface { - SchemaByName(schema model.CIStr) (*model.DBInfo, bool) - SchemaExists(schema model.CIStr) bool - TableByName(schema, table model.CIStr) (table.Table, error) - TableExists(schema, table model.CIStr) bool - SchemaByID(id int64) (*model.DBInfo, bool) - SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) - PolicyByName(name model.CIStr) (*model.PolicyInfo, bool) - ResourceGroupByName(name model.CIStr) (*model.ResourceGroupInfo, bool) - TableByID(id int64) (table.Table, bool) - AllocByID(id int64) (autoid.Allocators, bool) - AllSchemaNames() []string - AllSchemas() []*model.DBInfo - Clone() (result []*model.DBInfo) - SchemaTables(schema model.CIStr) []table.Table - SchemaMetaVersion() int64 - // TableIsView indicates whether the schema.table is a view. - TableIsView(schema, table model.CIStr) bool - // TableIsSequence indicates whether the schema.table is a sequence. - TableIsSequence(schema, table model.CIStr) bool - FindTableByPartitionID(partitionID int64) (table.Table, *model.DBInfo, *model.PartitionDefinition) - // PlacementBundleByPhysicalTableID is used to get a rule bundle. - PlacementBundleByPhysicalTableID(id int64) (*placement.Bundle, bool) - // AllPlacementBundles is used to get all placement bundles - AllPlacementBundles() []*placement.Bundle - // AllPlacementPolicies returns all placement policies - AllPlacementPolicies() []*model.PolicyInfo - // AllResourceGroups returns all resource groups - AllResourceGroups() []*model.ResourceGroupInfo - // HasTemporaryTable returns whether information schema has temporary table - HasTemporaryTable() bool - // GetTableReferredForeignKeys gets the table's ReferredFKInfo by lowercase schema and table name. - GetTableReferredForeignKeys(schema, table string) []*model.ReferredFKInfo -} - -type sortedTables []table.Table - -func (s sortedTables) searchTable(id int64) int { - idx := sort.Search(len(s), func(i int) bool { - return s[i].Meta().ID >= id - }) - if idx == len(s) || s[idx].Meta().ID != id { - return -1 - } - return idx -} - -type schemaTables struct { - dbInfo *model.DBInfo - tables map[string]table.Table -} - -const bucketCount = 512 - -type infoSchema struct { - // ruleBundleMap stores all placement rules - ruleBundleMap map[int64]*placement.Bundle - - // policyMap stores all placement policies. - policyMutex sync.RWMutex - policyMap map[string]*model.PolicyInfo - - // resourceGroupMap stores all resource groups. - resourceGroupMutex sync.RWMutex - resourceGroupMap map[string]*model.ResourceGroupInfo - - schemaMap map[string]*schemaTables - - // sortedTablesBuckets is a slice of sortedTables, a table's bucket index is (tableID % bucketCount). - sortedTablesBuckets []sortedTables - - // temporaryTables stores the temporary table ids - temporaryTableIDs map[int64]struct{} - - // schemaMetaVersion is the version of schema, and we should check version when change schema. - schemaMetaVersion int64 - - // referredForeignKeyMap records all table's ReferredFKInfo. - // referredSchemaAndTableName => child SchemaAndTableAndForeignKeyName => *model.ReferredFKInfo - referredForeignKeyMap map[SchemaAndTableName][]*model.ReferredFKInfo -} - -// SchemaAndTableName contains the lower-case schema name and table name. -type SchemaAndTableName struct { - schema string - table string -} - -// MockInfoSchema only serves for test. -func MockInfoSchema(tbList []*model.TableInfo) InfoSchema { - result := &infoSchema{} - result.schemaMap = make(map[string]*schemaTables) - result.policyMap = make(map[string]*model.PolicyInfo) - result.resourceGroupMap = make(map[string]*model.ResourceGroupInfo) - result.ruleBundleMap = make(map[int64]*placement.Bundle) - result.sortedTablesBuckets = make([]sortedTables, bucketCount) - dbInfo := &model.DBInfo{ID: 0, Name: model.NewCIStr("test"), Tables: tbList} - tableNames := &schemaTables{ - dbInfo: dbInfo, - tables: make(map[string]table.Table), - } - result.schemaMap["test"] = tableNames - for _, tb := range tbList { - tbl := table.MockTableFromMeta(tb) - tableNames.tables[tb.Name.L] = tbl - bucketIdx := tableBucketIdx(tb.ID) - result.sortedTablesBuckets[bucketIdx] = append(result.sortedTablesBuckets[bucketIdx], tbl) - } - for i := range result.sortedTablesBuckets { - slices.SortFunc(result.sortedTablesBuckets[i], func(i, j table.Table) int { - return cmp.Compare(i.Meta().ID, j.Meta().ID) - }) - } - return result -} - -// MockInfoSchemaWithSchemaVer only serves for test. -func MockInfoSchemaWithSchemaVer(tbList []*model.TableInfo, schemaVer int64) InfoSchema { - result := &infoSchema{} - result.schemaMap = make(map[string]*schemaTables) - result.policyMap = make(map[string]*model.PolicyInfo) - result.resourceGroupMap = make(map[string]*model.ResourceGroupInfo) - result.ruleBundleMap = make(map[int64]*placement.Bundle) - result.sortedTablesBuckets = make([]sortedTables, bucketCount) - dbInfo := &model.DBInfo{ID: 0, Name: model.NewCIStr("test"), Tables: tbList} - tableNames := &schemaTables{ - dbInfo: dbInfo, - tables: make(map[string]table.Table), - } - result.schemaMap["test"] = tableNames - for _, tb := range tbList { - tbl := table.MockTableFromMeta(tb) - tableNames.tables[tb.Name.L] = tbl - bucketIdx := tableBucketIdx(tb.ID) - result.sortedTablesBuckets[bucketIdx] = append(result.sortedTablesBuckets[bucketIdx], tbl) - } - for i := range result.sortedTablesBuckets { - slices.SortFunc(result.sortedTablesBuckets[i], func(i, j table.Table) int { - return cmp.Compare(i.Meta().ID, j.Meta().ID) - }) - } - result.schemaMetaVersion = schemaVer - return result -} - -var _ InfoSchema = (*infoSchema)(nil) - -func (is *infoSchema) SchemaByName(schema model.CIStr) (val *model.DBInfo, ok bool) { - tableNames, ok := is.schemaMap[schema.L] - if !ok { - return - } - return tableNames.dbInfo, true -} - -func (is *infoSchema) SchemaMetaVersion() int64 { - return is.schemaMetaVersion -} - -func (is *infoSchema) SchemaExists(schema model.CIStr) bool { - _, ok := is.schemaMap[schema.L] - return ok -} - -func (is *infoSchema) TableByName(schema, table model.CIStr) (t table.Table, err error) { - if tbNames, ok := is.schemaMap[schema.L]; ok { - if t, ok = tbNames.tables[table.L]; ok { - return - } - } - return nil, ErrTableNotExists.GenWithStackByArgs(schema, table) -} - -func (is *infoSchema) TableIsView(schema, table model.CIStr) bool { - if tbNames, ok := is.schemaMap[schema.L]; ok { - if t, ok := tbNames.tables[table.L]; ok { - return t.Meta().IsView() - } - } - return false -} - -func (is *infoSchema) TableIsSequence(schema, table model.CIStr) bool { - if tbNames, ok := is.schemaMap[schema.L]; ok { - if t, ok := tbNames.tables[table.L]; ok { - return t.Meta().IsSequence() - } - } - return false -} - -func (is *infoSchema) TableExists(schema, table model.CIStr) bool { - if tbNames, ok := is.schemaMap[schema.L]; ok { - if _, ok = tbNames.tables[table.L]; ok { - return true - } - } - return false -} - -func (is *infoSchema) PolicyByID(id int64) (val *model.PolicyInfo, ok bool) { - // TODO: use another hash map to avoid traveling on the policy map - for _, v := range is.policyMap { - if v.ID == id { - return v, true - } - } - return nil, false -} - -func (is *infoSchema) ResourceGroupByID(id int64) (val *model.ResourceGroupInfo, ok bool) { - is.resourceGroupMutex.RLock() - defer is.resourceGroupMutex.RUnlock() - for _, v := range is.resourceGroupMap { - if v.ID == id { - return v, true - } - } - return nil, false -} - -func (is *infoSchema) SchemaByID(id int64) (val *model.DBInfo, ok bool) { - for _, v := range is.schemaMap { - if v.dbInfo.ID == id { - return v.dbInfo, true - } - } - return nil, false -} - -func (is *infoSchema) SchemaByTable(tableInfo *model.TableInfo) (val *model.DBInfo, ok bool) { - if tableInfo == nil { - return nil, false - } - for _, v := range is.schemaMap { - if tbl, ok := v.tables[tableInfo.Name.L]; ok { - if tbl.Meta().ID == tableInfo.ID { - return v.dbInfo, true - } - } - } - return nil, false -} - -func (is *infoSchema) TableByID(id int64) (val table.Table, ok bool) { - slice := is.sortedTablesBuckets[tableBucketIdx(id)] - idx := slice.searchTable(id) - if idx == -1 { - return nil, false - } - return slice[idx], true -} - -func (is *infoSchema) AllocByID(id int64) (autoid.Allocators, bool) { - tbl, ok := is.TableByID(id) - if !ok { - return autoid.Allocators{}, false - } - return tbl.Allocators(nil), true -} - -func (is *infoSchema) AllSchemaNames() (names []string) { - for _, v := range is.schemaMap { - names = append(names, v.dbInfo.Name.O) - } - return -} - -func (is *infoSchema) AllSchemas() (schemas []*model.DBInfo) { - for _, v := range is.schemaMap { - schemas = append(schemas, v.dbInfo) - } - return -} - -func (is *infoSchema) SchemaTables(schema model.CIStr) (tables []table.Table) { - schemaTables, ok := is.schemaMap[schema.L] - if !ok { - return - } - for _, tbl := range schemaTables.tables { - tables = append(tables, tbl) - } - return -} - -// FindTableByPartitionID finds the partition-table info by the partitionID. -// FindTableByPartitionID will traverse all the tables to find the partitionID partition in which partition-table. -func (is *infoSchema) FindTableByPartitionID(partitionID int64) (table.Table, *model.DBInfo, *model.PartitionDefinition) { - for _, v := range is.schemaMap { - for _, tbl := range v.tables { - pi := tbl.Meta().GetPartitionInfo() - if pi == nil { - continue - } - for _, p := range pi.Definitions { - if p.ID == partitionID { - return tbl, v.dbInfo, &p - } - } - } - } - return nil, nil, nil -} - -// HasTemporaryTable returns whether information schema has temporary table -func (is *infoSchema) HasTemporaryTable() bool { - return len(is.temporaryTableIDs) != 0 -} - -func (is *infoSchema) Clone() (result []*model.DBInfo) { - for _, v := range is.schemaMap { - result = append(result, v.dbInfo.Clone()) - } - return -} - -// GetSequenceByName gets the sequence by name. -func GetSequenceByName(is InfoSchema, schema, sequence model.CIStr) (util.SequenceTable, error) { - tbl, err := is.TableByName(schema, sequence) - if err != nil { - return nil, err - } - if !tbl.Meta().IsSequence() { - return nil, ErrWrongObject.GenWithStackByArgs(schema, sequence, "SEQUENCE") - } - return tbl.(util.SequenceTable), nil -} - -func init() { - // Initialize the information shema database and register the driver to `drivers` - dbID := autoid.InformationSchemaDBID - infoSchemaTables := make([]*model.TableInfo, 0, len(tableNameToColumns)) - for name, cols := range tableNameToColumns { - tableInfo := buildTableMeta(name, cols) - infoSchemaTables = append(infoSchemaTables, tableInfo) - var ok bool - tableInfo.ID, ok = tableIDMap[tableInfo.Name.O] - if !ok { - panic(fmt.Sprintf("get information_schema table id failed, unknown system table `%v`", tableInfo.Name.O)) - } - for i, c := range tableInfo.Columns { - c.ID = int64(i) + 1 - } - tableInfo.MaxColumnID = int64(len(tableInfo.Columns)) - tableInfo.MaxIndexID = int64(len(tableInfo.Indices)) - } - infoSchemaDB := &model.DBInfo{ - ID: dbID, - Name: util.InformationSchemaName, - Charset: mysql.DefaultCharset, - Collate: mysql.DefaultCollationName, - Tables: infoSchemaTables, - } - RegisterVirtualTable(infoSchemaDB, createInfoSchemaTable) - util.GetSequenceByName = func(is interface{}, schema, sequence model.CIStr) (util.SequenceTable, error) { - return GetSequenceByName(is.(InfoSchema), schema, sequence) - } - mock.MockInfoschema = func(tbList []*model.TableInfo) sessionctx.InfoschemaMetaVersion { - return MockInfoSchema(tbList) - } -} - -// HasAutoIncrementColumn checks whether the table has auto_increment columns, if so, return true and the column name. -func HasAutoIncrementColumn(tbInfo *model.TableInfo) (bool, string) { - for _, col := range tbInfo.Columns { - if mysql.HasAutoIncrementFlag(col.GetFlag()) { - return true, col.Name.L - } - } - return false, "" -} - -// PolicyByName is used to find the policy. -func (is *infoSchema) PolicyByName(name model.CIStr) (*model.PolicyInfo, bool) { - is.policyMutex.RLock() - defer is.policyMutex.RUnlock() - t, r := is.policyMap[name.L] - return t, r -} - -// ResourceGroupByName is used to find the resource group. -func (is *infoSchema) ResourceGroupByName(name model.CIStr) (*model.ResourceGroupInfo, bool) { - is.resourceGroupMutex.RLock() - defer is.resourceGroupMutex.RUnlock() - t, r := is.resourceGroupMap[name.L] - return t, r -} - -// AllResourceGroups returns all resource groups. -func (is *infoSchema) AllResourceGroups() []*model.ResourceGroupInfo { - is.resourceGroupMutex.RLock() - defer is.resourceGroupMutex.RUnlock() - groups := make([]*model.ResourceGroupInfo, 0, len(is.resourceGroupMap)) - for _, group := range is.resourceGroupMap { - groups = append(groups, group) - } - return groups -} - -// AllPlacementPolicies returns all placement policies -func (is *infoSchema) AllPlacementPolicies() []*model.PolicyInfo { - is.policyMutex.RLock() - defer is.policyMutex.RUnlock() - policies := make([]*model.PolicyInfo, 0, len(is.policyMap)) - for _, policy := range is.policyMap { - policies = append(policies, policy) - } - return policies -} - -func (is *infoSchema) PlacementBundleByPhysicalTableID(id int64) (*placement.Bundle, bool) { - t, r := is.ruleBundleMap[id] - return t, r -} - -func (is *infoSchema) AllPlacementBundles() []*placement.Bundle { - bundles := make([]*placement.Bundle, 0, len(is.ruleBundleMap)) - for _, bundle := range is.ruleBundleMap { - bundles = append(bundles, bundle) - } - return bundles -} - -func (is *infoSchema) setResourceGroup(resourceGroup *model.ResourceGroupInfo) { - is.resourceGroupMutex.Lock() - defer is.resourceGroupMutex.Unlock() - is.resourceGroupMap[resourceGroup.Name.L] = resourceGroup -} - -func (is *infoSchema) deleteResourceGroup(name string) { - is.resourceGroupMutex.Lock() - defer is.resourceGroupMutex.Unlock() - delete(is.resourceGroupMap, name) -} - -func (is *infoSchema) setPolicy(policy *model.PolicyInfo) { - is.policyMutex.Lock() - defer is.policyMutex.Unlock() - is.policyMap[policy.Name.L] = policy -} - -func (is *infoSchema) deletePolicy(name string) { - is.policyMutex.Lock() - defer is.policyMutex.Unlock() - delete(is.policyMap, name) -} - -func (is *infoSchema) addReferredForeignKeys(schema model.CIStr, tbInfo *model.TableInfo) { - for _, fk := range tbInfo.ForeignKeys { - if fk.Version < model.FKVersion1 { - continue - } - refer := SchemaAndTableName{schema: fk.RefSchema.L, table: fk.RefTable.L} - referredFKList := is.referredForeignKeyMap[refer] - found := false - for _, referredFK := range referredFKList { - if referredFK.ChildSchema.L == schema.L && referredFK.ChildTable.L == tbInfo.Name.L && referredFK.ChildFKName.L == fk.Name.L { - referredFK.Cols = fk.RefCols - found = true - break - } - } - if found { - continue - } - - newReferredFKList := make([]*model.ReferredFKInfo, 0, len(referredFKList)+1) - newReferredFKList = append(newReferredFKList, referredFKList...) - newReferredFKList = append(newReferredFKList, &model.ReferredFKInfo{ - Cols: fk.RefCols, - ChildSchema: schema, - ChildTable: tbInfo.Name, - ChildFKName: fk.Name, - }) - sort.Slice(newReferredFKList, func(i, j int) bool { - if newReferredFKList[i].ChildSchema.L != newReferredFKList[j].ChildSchema.L { - return newReferredFKList[i].ChildSchema.L < newReferredFKList[j].ChildSchema.L - } - if newReferredFKList[i].ChildTable.L != newReferredFKList[j].ChildTable.L { - return newReferredFKList[i].ChildTable.L < newReferredFKList[j].ChildTable.L - } - return newReferredFKList[i].ChildFKName.L < newReferredFKList[j].ChildFKName.L - }) - is.referredForeignKeyMap[refer] = newReferredFKList - } -} - -func (is *infoSchema) deleteReferredForeignKeys(schema model.CIStr, tbInfo *model.TableInfo) { - for _, fk := range tbInfo.ForeignKeys { - if fk.Version < model.FKVersion1 { - continue - } - refer := SchemaAndTableName{schema: fk.RefSchema.L, table: fk.RefTable.L} - referredFKList := is.referredForeignKeyMap[refer] - if len(referredFKList) == 0 { - continue - } - newReferredFKList := make([]*model.ReferredFKInfo, 0, len(referredFKList)-1) - for _, referredFK := range referredFKList { - if referredFK.ChildSchema.L == schema.L && referredFK.ChildTable.L == tbInfo.Name.L && referredFK.ChildFKName.L == fk.Name.L { - continue - } - newReferredFKList = append(newReferredFKList, referredFK) - } - is.referredForeignKeyMap[refer] = newReferredFKList - } -} - -// GetTableReferredForeignKeys gets the table's ReferredFKInfo by lowercase schema and table name. -func (is *infoSchema) GetTableReferredForeignKeys(schema, table string) []*model.ReferredFKInfo { - name := SchemaAndTableName{schema: schema, table: table} - return is.referredForeignKeyMap[name] -} - -// SessionTables store local temporary tables -type SessionTables struct { - // Session tables can be accessed after the db is dropped, so there needs a way to retain the DBInfo. - // schemaTables.dbInfo will only be used when the db is dropped and it may be stale after the db is created again. - // But it's fine because we only need its name. - schemaMap map[string]*schemaTables - idx2table map[int64]table.Table -} - -// NewSessionTables creates a new NewSessionTables object -func NewSessionTables() *SessionTables { - return &SessionTables{ - schemaMap: make(map[string]*schemaTables), - idx2table: make(map[int64]table.Table), - } -} - -// TableByName get table by name -func (is *SessionTables) TableByName(schema, table model.CIStr) (table.Table, bool) { - if tbNames, ok := is.schemaMap[schema.L]; ok { - if t, ok := tbNames.tables[table.L]; ok { - return t, true - } - } - return nil, false -} - -// TableExists check if table with the name exists -func (is *SessionTables) TableExists(schema, table model.CIStr) (ok bool) { - _, ok = is.TableByName(schema, table) - return -} - -// TableByID get table by table id -func (is *SessionTables) TableByID(id int64) (tbl table.Table, ok bool) { - tbl, ok = is.idx2table[id] - return -} - -// AddTable add a table -func (is *SessionTables) AddTable(db *model.DBInfo, tbl table.Table) error { - schemaTables := is.ensureSchema(db) - - tblMeta := tbl.Meta() - if _, ok := schemaTables.tables[tblMeta.Name.L]; ok { - return ErrTableExists.GenWithStackByArgs(tblMeta.Name) - } - - if _, ok := is.idx2table[tblMeta.ID]; ok { - return ErrTableExists.GenWithStackByArgs(tblMeta.Name) - } - - schemaTables.tables[tblMeta.Name.L] = tbl - is.idx2table[tblMeta.ID] = tbl - - return nil -} - -// RemoveTable remove a table -func (is *SessionTables) RemoveTable(schema, table model.CIStr) (exist bool) { - tbls := is.schemaTables(schema) - if tbls == nil { - return false - } - - oldTable, exist := tbls.tables[table.L] - if !exist { - return false - } - - delete(tbls.tables, table.L) - delete(is.idx2table, oldTable.Meta().ID) - if len(tbls.tables) == 0 { - delete(is.schemaMap, schema.L) - } - return true -} - -// Count gets the count of the temporary tables. -func (is *SessionTables) Count() int { - return len(is.idx2table) -} - -// SchemaByTable get a table's schema name -func (is *SessionTables) SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) { - if tableInfo == nil { - return nil, false - } - - for _, v := range is.schemaMap { - if tbl, ok := v.tables[tableInfo.Name.L]; ok { - if tbl.Meta().ID == tableInfo.ID { - return v.dbInfo, true - } - } - } - - return nil, false -} - -func (is *SessionTables) ensureSchema(db *model.DBInfo) *schemaTables { - if tbls, ok := is.schemaMap[db.Name.L]; ok { - return tbls - } - - tbls := &schemaTables{dbInfo: db, tables: make(map[string]table.Table)} - is.schemaMap[db.Name.L] = tbls - return tbls -} - -func (is *SessionTables) schemaTables(schema model.CIStr) *schemaTables { - if is.schemaMap == nil { - return nil - } - - if tbls, ok := is.schemaMap[schema.L]; ok { - return tbls - } - - return nil -} - -// SessionExtendedInfoSchema implements InfoSchema -// Local temporary table has a loose relationship with database. -// So when a database is dropped, its temporary tables still exist and can be returned by TableByName/TableByID. -type SessionExtendedInfoSchema struct { - InfoSchema - LocalTemporaryTablesOnce sync.Once - LocalTemporaryTables *SessionTables - MdlTables *SessionTables -} - -// TableByName implements InfoSchema.TableByName -func (ts *SessionExtendedInfoSchema) TableByName(schema, table model.CIStr) (table.Table, error) { - if ts.LocalTemporaryTables != nil { - if tbl, ok := ts.LocalTemporaryTables.TableByName(schema, table); ok { - return tbl, nil - } - } - - if ts.MdlTables != nil { - if tbl, ok := ts.MdlTables.TableByName(schema, table); ok { - return tbl, nil - } - } - - return ts.InfoSchema.TableByName(schema, table) -} - -// TableByID implements InfoSchema.TableByID -func (ts *SessionExtendedInfoSchema) TableByID(id int64) (table.Table, bool) { - if ts.LocalTemporaryTables != nil { - if tbl, ok := ts.LocalTemporaryTables.TableByID(id); ok { - return tbl, true - } - } - - if ts.MdlTables != nil { - if tbl, ok := ts.MdlTables.TableByID(id); ok { - return tbl, true - } - } - - return ts.InfoSchema.TableByID(id) -} - -// SchemaByTable implements InfoSchema.SchemaByTable, it returns a stale DBInfo even if it's dropped. -func (ts *SessionExtendedInfoSchema) SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) { - if tableInfo == nil { - return nil, false - } - - if ts.LocalTemporaryTables != nil { - if db, ok := ts.LocalTemporaryTables.SchemaByTable(tableInfo); ok { - return db, true - } - } - - if ts.MdlTables != nil { - if tbl, ok := ts.MdlTables.SchemaByTable(tableInfo); ok { - return tbl, true - } - } - - return ts.InfoSchema.SchemaByTable(tableInfo) -} - -// UpdateTableInfo implements InfoSchema.SchemaByTable. -func (ts *SessionExtendedInfoSchema) UpdateTableInfo(db *model.DBInfo, tableInfo table.Table) error { - if ts.MdlTables == nil { - ts.MdlTables = NewSessionTables() - } - err := ts.MdlTables.AddTable(db, tableInfo) - if err != nil { - return err - } - return nil -} - -// HasTemporaryTable returns whether information schema has temporary table -func (ts *SessionExtendedInfoSchema) HasTemporaryTable() bool { - return ts.LocalTemporaryTables != nil && ts.LocalTemporaryTables.Count() > 0 || ts.InfoSchema.HasTemporaryTable() -} - -// DetachTemporaryTableInfoSchema returns a new SessionExtendedInfoSchema without temporary tables -func (ts *SessionExtendedInfoSchema) DetachTemporaryTableInfoSchema() *SessionExtendedInfoSchema { - return &SessionExtendedInfoSchema{ - InfoSchema: ts.InfoSchema, - MdlTables: ts.MdlTables, - } -} - -// FindTableByTblOrPartID looks for table.Table for the given id in the InfoSchema. -// The id can be either a table id or a partition id. -// If the id is a table id, the corresponding table.Table will be returned, and the second return value is nil. -// If the id is a partition id, the corresponding table.Table and PartitionDefinition will be returned. -// If the id is not found in the InfoSchema, nil will be returned for both return values. -func FindTableByTblOrPartID(is InfoSchema, id int64) (table.Table, *model.PartitionDefinition) { - tbl, ok := is.TableByID(id) - if ok { - return tbl, nil - } - tbl, _, partDef := is.FindTableByPartitionID(id) - return tbl, partDef -} diff --git a/infoschema/infoschema_test.go b/infoschema/infoschema_test.go deleted file mode 100644 index e62a9975baa70..0000000000000 --- a/infoschema/infoschema_test.go +++ /dev/null @@ -1,853 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package infoschema_test - -import ( - "context" - "encoding/json" - "strings" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestBasic(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - // Make sure it calls perfschema.Init(). - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - - dbName := model.NewCIStr("Test") - tbName := model.NewCIStr("T") - colName := model.NewCIStr("A") - idxName := model.NewCIStr("idx") - noexist := model.NewCIStr("noexist") - - colID, err := genGlobalID(store) - require.NoError(t, err) - colInfo := &model.ColumnInfo{ - ID: colID, - Name: colName, - Offset: 0, - FieldType: *types.NewFieldType(mysql.TypeLonglong), - State: model.StatePublic, - } - - idxInfo := &model.IndexInfo{ - Name: idxName, - Table: tbName, - Columns: []*model.IndexColumn{ - { - Name: colName, - Offset: 0, - Length: 10, - }, - }, - Unique: true, - Primary: true, - State: model.StatePublic, - } - - tbID, err := genGlobalID(store) - require.NoError(t, err) - tblInfo := &model.TableInfo{ - ID: tbID, - Name: tbName, - Columns: []*model.ColumnInfo{colInfo}, - Indices: []*model.IndexInfo{idxInfo}, - State: model.StatePublic, - } - - dbID, err := genGlobalID(store) - require.NoError(t, err) - dbInfo := &model.DBInfo{ - ID: dbID, - Name: dbName, - Tables: []*model.TableInfo{tblInfo}, - State: model.StatePublic, - } - - dbInfos := []*model.DBInfo{dbInfo} - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - err := meta.NewMeta(txn).CreateDatabase(dbInfo) - require.NoError(t, err) - return errors.Trace(err) - }) - require.NoError(t, err) - - builder, err := infoschema.NewBuilder(dom.Store(), nil).InitWithDBInfos(dbInfos, nil, nil, 1) - require.NoError(t, err) - - txn, err := store.Begin() - require.NoError(t, err) - checkApplyCreateNonExistsSchemaDoesNotPanic(t, txn, builder) - checkApplyCreateNonExistsTableDoesNotPanic(t, txn, builder, dbID) - err = txn.Rollback() - require.NoError(t, err) - - is := builder.Build() - - schemaNames := is.AllSchemaNames() - require.Len(t, schemaNames, 4) - require.True(t, testutil.CompareUnorderedStringSlice(schemaNames, []string{util.InformationSchemaName.O, util.MetricSchemaName.O, util.PerformanceSchemaName.O, "Test"})) - - schemas := is.AllSchemas() - require.Len(t, schemas, 4) - schemas = is.Clone() - require.Len(t, schemas, 4) - - require.True(t, is.SchemaExists(dbName)) - require.False(t, is.SchemaExists(noexist)) - - schema, ok := is.SchemaByID(dbID) - require.True(t, ok) - require.NotNil(t, schema) - - schema, ok = is.SchemaByID(tbID) - require.False(t, ok) - require.Nil(t, schema) - - schema, ok = is.SchemaByName(dbName) - require.True(t, ok) - require.NotNil(t, schema) - - schema, ok = is.SchemaByName(noexist) - require.False(t, ok) - require.Nil(t, schema) - - schema, ok = is.SchemaByTable(tblInfo) - require.True(t, ok) - require.NotNil(t, schema) - - noexistTblInfo := &model.TableInfo{ID: 12345, Name: tblInfo.Name} - schema, ok = is.SchemaByTable(noexistTblInfo) - require.False(t, ok) - require.Nil(t, schema) - - require.True(t, is.TableExists(dbName, tbName)) - require.False(t, is.TableExists(dbName, noexist)) - require.False(t, is.TableIsView(dbName, tbName)) - require.False(t, is.TableIsSequence(dbName, tbName)) - - tb, ok := is.TableByID(tbID) - require.True(t, ok) - require.NotNil(t, tb) - - tb, ok = is.TableByID(dbID) - require.False(t, ok) - require.Nil(t, tb) - - alloc, ok := is.AllocByID(tbID) - require.True(t, ok) - require.NotNil(t, alloc) - - tb, err = is.TableByName(dbName, tbName) - require.NoError(t, err) - require.NotNil(t, tb) - - _, err = is.TableByName(dbName, noexist) - require.Error(t, err) - - tbs := is.SchemaTables(dbName) - require.Len(t, tbs, 1) - - tbs = is.SchemaTables(noexist) - require.Len(t, tbs, 0) - - // Make sure partitions table exists - tb, err = is.TableByName(model.NewCIStr("information_schema"), model.NewCIStr("partitions")) - require.NoError(t, err) - require.NotNil(t, tb) - - err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - err := meta.NewMeta(txn).CreateTableOrView(dbID, tblInfo) - require.NoError(t, err) - return errors.Trace(err) - }) - require.NoError(t, err) - txn, err = store.Begin() - require.NoError(t, err) - _, err = builder.ApplyDiff(meta.NewMeta(txn), &model.SchemaDiff{Type: model.ActionRenameTable, SchemaID: dbID, TableID: tbID, OldSchemaID: dbID}) - require.NoError(t, err) - err = txn.Rollback() - require.NoError(t, err) - is = builder.Build() - schema, ok = is.SchemaByID(dbID) - require.True(t, ok) - require.Equal(t, 1, len(schema.Tables)) -} - -func TestMockInfoSchema(t *testing.T) { - tblID := int64(1234) - tblName := model.NewCIStr("tbl_m") - tableInfo := &model.TableInfo{ - ID: tblID, - Name: tblName, - State: model.StatePublic, - } - colInfo := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 0, - Name: model.NewCIStr("h"), - FieldType: *types.NewFieldType(mysql.TypeLong), - ID: 1, - } - tableInfo.Columns = []*model.ColumnInfo{colInfo} - is := infoschema.MockInfoSchema([]*model.TableInfo{tableInfo}) - tbl, ok := is.TableByID(tblID) - require.True(t, ok) - require.Equal(t, tblName, tbl.Meta().Name) - require.Equal(t, colInfo, tbl.Cols()[0].ColumnInfo) -} - -func checkApplyCreateNonExistsSchemaDoesNotPanic(t *testing.T, txn kv.Transaction, builder *infoschema.Builder) { - m := meta.NewMeta(txn) - _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateSchema, SchemaID: 999}) - require.True(t, infoschema.ErrDatabaseNotExists.Equal(err)) -} - -func checkApplyCreateNonExistsTableDoesNotPanic(t *testing.T, txn kv.Transaction, builder *infoschema.Builder, dbID int64) { - m := meta.NewMeta(txn) - _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: dbID, TableID: 999}) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) -} - -// TestInfoTables makes sure that all tables of information_schema could be found in infoschema handle. -func TestInfoTables(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos(nil, nil, nil, 0) - require.NoError(t, err) - is := builder.Build() - - infoTables := []string{ - "SCHEMATA", - "TABLES", - "COLUMNS", - "STATISTICS", - "CHARACTER_SETS", - "COLLATIONS", - "FILES", - "PROFILING", - "PARTITIONS", - "KEY_COLUMN_USAGE", - "REFERENTIAL_CONSTRAINTS", - "SESSION_VARIABLES", - "PLUGINS", - "TABLE_CONSTRAINTS", - "TRIGGERS", - "USER_PRIVILEGES", - "ENGINES", - "VIEWS", - "ROUTINES", - "SCHEMA_PRIVILEGES", - "COLUMN_PRIVILEGES", - "TABLE_PRIVILEGES", - "PARAMETERS", - "EVENTS", - "GLOBAL_STATUS", - "GLOBAL_VARIABLES", - "SESSION_STATUS", - "OPTIMIZER_TRACE", - "TABLESPACES", - "COLLATION_CHARACTER_SET_APPLICABILITY", - "PROCESSLIST", - "TIDB_TRX", - "DEADLOCKS", - "PLACEMENT_POLICIES", - "TRX_SUMMARY", - "RESOURCE_GROUPS", - } - for _, tbl := range infoTables { - tb, err1 := is.TableByName(util.InformationSchemaName, model.NewCIStr(tbl)) - require.Nil(t, err1) - require.NotNil(t, tb) - } -} - -func genGlobalID(store kv.Storage) (int64, error) { - var globalID int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - var err error - globalID, err = meta.NewMeta(txn).GenGlobalID() - return errors.Trace(err) - }) - return globalID, errors.Trace(err) -} - -func TestBuildSchemaWithGlobalTemporaryTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - is := dom.InfoSchema() - require.False(t, is.HasTemporaryTable()) - db, ok := is.SchemaByName(model.NewCIStr("test")) - require.True(t, ok) - - doChange := func(changes ...func(m *meta.Meta, builder *infoschema.Builder)) infoschema.InfoSchema { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - curIs := is - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - for _, change := range changes { - builder := infoschema.NewBuilder(store, nil).InitWithOldInfoSchema(curIs) - change(m, builder) - curIs = builder.Build() - } - return nil - }) - require.NoError(t, err) - return curIs - } - - createGlobalTemporaryTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { - return func(m *meta.Meta, builder *infoschema.Builder) { - err := m.CreateTableOrView(db.ID, &model.TableInfo{ - ID: tblID, - TempTableType: model.TempTableGlobal, - State: model.StatePublic, - }) - require.NoError(t, err) - _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: db.ID, TableID: tblID}) - require.NoError(t, err) - } - } - - createNormalTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { - return func(m *meta.Meta, builder *infoschema.Builder) { - err := m.CreateTableOrView(db.ID, &model.TableInfo{ - ID: tblID, - State: model.StatePublic, - }) - require.NoError(t, err) - _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: db.ID, TableID: tblID}) - require.NoError(t, err) - } - } - - dropTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { - return func(m *meta.Meta, builder *infoschema.Builder) { - err := m.DropTableOrView(db.ID, tblID) - require.NoError(t, err) - _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionDropTable, SchemaID: db.ID, TableID: tblID}) - require.NoError(t, err) - } - } - - truncateGlobalTemporaryTableChange := func(tblID, newTblID int64) func(m *meta.Meta, builder *infoschema.Builder) { - return func(m *meta.Meta, builder *infoschema.Builder) { - err := m.DropTableOrView(db.ID, tblID) - require.NoError(t, err) - - err = m.CreateTableOrView(db.ID, &model.TableInfo{ - ID: newTblID, - TempTableType: model.TempTableGlobal, - State: model.StatePublic, - }) - require.NoError(t, err) - _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionTruncateTable, SchemaID: db.ID, OldTableID: tblID, TableID: newTblID}) - require.NoError(t, err) - } - } - - alterTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { - return func(m *meta.Meta, builder *infoschema.Builder) { - _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionAddColumn, SchemaID: db.ID, TableID: tblID}) - require.NoError(t, err) - } - } - - // create table - tbID, err := genGlobalID(store) - require.NoError(t, err) - newIS := doChange( - createGlobalTemporaryTableChange(tbID), - ) - require.True(t, newIS.HasTemporaryTable()) - - // full load - newDB, ok := newIS.SchemaByName(model.NewCIStr("test")) - require.True(t, ok) - builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos([]*model.DBInfo{newDB}, newIS.AllPlacementPolicies(), newIS.AllResourceGroups(), newIS.SchemaMetaVersion()) - require.NoError(t, err) - require.True(t, builder.Build().HasTemporaryTable()) - - // create and then drop - tbID, err = genGlobalID(store) - require.NoError(t, err) - require.False(t, doChange( - createGlobalTemporaryTableChange(tbID), - dropTableChange(tbID), - ).HasTemporaryTable()) - - // create and then alter - tbID, err = genGlobalID(store) - require.NoError(t, err) - require.True(t, doChange( - createGlobalTemporaryTableChange(tbID), - alterTableChange(tbID), - ).HasTemporaryTable()) - - // create and truncate - tbID, err = genGlobalID(store) - require.NoError(t, err) - newTbID, err := genGlobalID(store) - require.NoError(t, err) - require.True(t, doChange( - createGlobalTemporaryTableChange(tbID), - truncateGlobalTemporaryTableChange(tbID, newTbID), - ).HasTemporaryTable()) - - // create two and drop one - tbID, err = genGlobalID(store) - require.NoError(t, err) - tbID2, err := genGlobalID(store) - require.NoError(t, err) - require.True(t, doChange( - createGlobalTemporaryTableChange(tbID), - createGlobalTemporaryTableChange(tbID2), - dropTableChange(tbID), - ).HasTemporaryTable()) - - // create temporary and then create normal - tbID, err = genGlobalID(store) - require.NoError(t, err) - tbID2, err = genGlobalID(store) - require.NoError(t, err) - require.True(t, doChange( - createGlobalTemporaryTableChange(tbID), - createNormalTableChange(tbID2), - ).HasTemporaryTable()) -} - -func TestBuildBundle(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - tk.MustExec("create placement policy p1 followers=1") - tk.MustExec("create placement policy p2 followers=2") - tk.MustExec(`create table t1(a int primary key) placement policy p1 partition by range(a) ( - partition p1 values less than (10) placement policy p2, - partition p2 values less than (20) - )`) - tk.MustExec("create table t2(a int)") - defer func() { - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - }() - - is := domain.GetDomain(tk.Session()).InfoSchema() - db, ok := is.SchemaByName(model.NewCIStr("test")) - require.True(t, ok) - - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - - var p1 model.PartitionDefinition - for _, par := range tbl1.Meta().Partition.Definitions { - if par.Name.L == "p1" { - p1 = par - break - } - } - require.NotNil(t, p1) - - var tb1Bundle, p1Bundle *placement.Bundle - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) - require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) (err error) { - m := meta.NewMeta(txn) - tb1Bundle, err = placement.NewTableBundle(m, tbl1.Meta()) - require.NoError(t, err) - require.NotNil(t, tb1Bundle) - - p1Bundle, err = placement.NewPartitionBundle(m, p1) - require.NoError(t, err) - require.NotNil(t, p1Bundle) - return - })) - - assertBundle := func(checkIS infoschema.InfoSchema, id int64, expected *placement.Bundle) { - actual, ok := checkIS.PlacementBundleByPhysicalTableID(id) - if expected == nil { - require.False(t, ok) - return - } - - expectedJSON, err := json.Marshal(expected) - require.NoError(t, err) - actualJSON, err := json.Marshal(actual) - require.NoError(t, err) - require.Equal(t, string(expectedJSON), string(actualJSON)) - } - - assertBundle(is, tbl1.Meta().ID, tb1Bundle) - assertBundle(is, tbl2.Meta().ID, nil) - assertBundle(is, p1.ID, p1Bundle) - - builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos([]*model.DBInfo{db}, is.AllPlacementPolicies(), is.AllResourceGroups(), is.SchemaMetaVersion()) - require.NoError(t, err) - is2 := builder.Build() - assertBundle(is2, tbl1.Meta().ID, tb1Bundle) - assertBundle(is2, tbl2.Meta().ID, nil) - assertBundle(is2, p1.ID, p1Bundle) -} - -func TestLocalTemporaryTables(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - createNewSchemaInfo := func(schemaName string) *model.DBInfo { - schemaID, err := genGlobalID(store) - require.NoError(t, err) - return &model.DBInfo{ - ID: schemaID, - Name: model.NewCIStr(schemaName), - State: model.StatePublic, - } - } - - createNewTable := func(schemaID int64, tbName string, tempType model.TempTableType) table.Table { - colID, err := genGlobalID(store) - require.NoError(t, err) - - colInfo := &model.ColumnInfo{ - ID: colID, - Name: model.NewCIStr("col1"), - Offset: 0, - FieldType: *types.NewFieldType(mysql.TypeLonglong), - State: model.StatePublic, - } - - tbID, err := genGlobalID(store) - require.NoError(t, err) - - tblInfo := &model.TableInfo{ - ID: tbID, - Name: model.NewCIStr(tbName), - Columns: []*model.ColumnInfo{colInfo}, - Indices: []*model.IndexInfo{}, - State: model.StatePublic, - } - - allocs := autoid.NewAllocatorsFromTblInfo(store, schemaID, tblInfo) - tbl, err := table.TableFromMeta(allocs, tblInfo) - require.NoError(t, err) - - return tbl - } - - assertTableByName := func(sc *infoschema.SessionTables, schemaName, tableName string, schema *model.DBInfo, tb table.Table) { - got, ok := sc.TableByName(model.NewCIStr(schemaName), model.NewCIStr(tableName)) - if tb == nil { - require.Nil(t, schema) - require.False(t, ok) - require.Nil(t, got) - } else { - require.NotNil(t, schema) - require.True(t, ok) - require.Equal(t, tb, got) - } - } - - assertTableExists := func(sc *infoschema.SessionTables, schemaName, tableName string, exists bool) { - got := sc.TableExists(model.NewCIStr(schemaName), model.NewCIStr(tableName)) - require.Equal(t, exists, got) - } - - assertTableByID := func(sc *infoschema.SessionTables, tbID int64, schema *model.DBInfo, tb table.Table) { - got, ok := sc.TableByID(tbID) - if tb == nil { - require.Nil(t, schema) - require.False(t, ok) - require.Nil(t, got) - } else { - require.NotNil(t, schema) - require.True(t, ok) - require.Equal(t, tb, got) - } - } - - assertSchemaByTable := func(sc *infoschema.SessionTables, db *model.DBInfo, tb *model.TableInfo) { - got, ok := sc.SchemaByTable(tb) - if db == nil { - require.Nil(t, got) - require.False(t, ok) - } else { - require.NotNil(t, got) - require.Equal(t, db.Name.L, got.Name.L) - require.True(t, ok) - } - } - - sc := infoschema.NewSessionTables() - db1 := createNewSchemaInfo("db1") - tb11 := createNewTable(db1.ID, "tb1", model.TempTableLocal) - tb12 := createNewTable(db1.ID, "Tb2", model.TempTableLocal) - tb13 := createNewTable(db1.ID, "tb3", model.TempTableLocal) - - // db1b has the same name with db1 - db1b := createNewSchemaInfo("db1") - tb15 := createNewTable(db1b.ID, "tb5", model.TempTableLocal) - tb16 := createNewTable(db1b.ID, "tb6", model.TempTableLocal) - tb17 := createNewTable(db1b.ID, "tb7", model.TempTableLocal) - - db2 := createNewSchemaInfo("db2") - tb21 := createNewTable(db2.ID, "tb1", model.TempTableLocal) - tb22 := createNewTable(db2.ID, "TB2", model.TempTableLocal) - tb24 := createNewTable(db2.ID, "tb4", model.TempTableLocal) - - prepareTables := []struct { - db *model.DBInfo - tb table.Table - }{ - {db1, tb11}, {db1, tb12}, {db1, tb13}, - {db1b, tb15}, {db1b, tb16}, {db1b, tb17}, - {db2, tb21}, {db2, tb22}, {db2, tb24}, - } - - for _, p := range prepareTables { - err = sc.AddTable(p.db, p.tb) - require.NoError(t, err) - } - - // test exist tables - for _, p := range prepareTables { - dbName := p.db.Name - tbName := p.tb.Meta().Name - - assertTableByName(sc, dbName.O, tbName.O, p.db, p.tb) - assertTableByName(sc, dbName.L, tbName.L, p.db, p.tb) - assertTableByName( - sc, - strings.ToUpper(dbName.L[:1])+dbName.L[1:], - strings.ToUpper(tbName.L[:1])+tbName.L[1:], - p.db, p.tb, - ) - - assertTableExists(sc, dbName.O, tbName.O, true) - assertTableExists(sc, dbName.L, tbName.L, true) - assertTableExists( - sc, - strings.ToUpper(dbName.L[:1])+dbName.L[1:], - strings.ToUpper(tbName.L[:1])+tbName.L[1:], - true, - ) - - assertTableByID(sc, p.tb.Meta().ID, p.db, p.tb) - assertSchemaByTable(sc, p.db, p.tb.Meta()) - } - - // test add dup table - err = sc.AddTable(db1, tb11) - require.True(t, infoschema.ErrTableExists.Equal(err)) - err = sc.AddTable(db1b, tb15) - require.True(t, infoschema.ErrTableExists.Equal(err)) - err = sc.AddTable(db1b, tb11) - require.True(t, infoschema.ErrTableExists.Equal(err)) - db1c := createNewSchemaInfo("db1") - err = sc.AddTable(db1c, createNewTable(db1c.ID, "tb1", model.TempTableLocal)) - require.True(t, infoschema.ErrTableExists.Equal(err)) - err = sc.AddTable(db1b, tb11) - require.True(t, infoschema.ErrTableExists.Equal(err)) - - // failed add has no effect - assertTableByName(sc, db1.Name.L, tb11.Meta().Name.L, db1, tb11) - - // delete some tables - require.True(t, sc.RemoveTable(model.NewCIStr("db1"), model.NewCIStr("tb1"))) - require.True(t, sc.RemoveTable(model.NewCIStr("Db2"), model.NewCIStr("tB2"))) - require.False(t, sc.RemoveTable(model.NewCIStr("db1"), model.NewCIStr("tbx"))) - require.False(t, sc.RemoveTable(model.NewCIStr("dbx"), model.NewCIStr("tbx"))) - - // test non exist tables by name - for _, c := range []struct{ dbName, tbName string }{ - {"db1", "tb1"}, {"db1", "tb4"}, {"db1", "tbx"}, - {"db2", "tb2"}, {"db2", "tb3"}, {"db2", "tbx"}, - {"dbx", "tb1"}, - } { - assertTableByName(sc, c.dbName, c.tbName, nil, nil) - assertTableExists(sc, c.dbName, c.tbName, false) - } - - // test non exist tables by id - nonExistID, err := genGlobalID(store) - require.NoError(t, err) - - for _, id := range []int64{nonExistID, tb11.Meta().ID, tb22.Meta().ID} { - assertTableByID(sc, id, nil, nil) - } - - // test non exist table schemaByTable - assertSchemaByTable(sc, nil, tb11.Meta()) - assertSchemaByTable(sc, nil, tb22.Meta()) - assertSchemaByTable(sc, nil, nil) - - // test SessionExtendedInfoSchema - dbTest := createNewSchemaInfo("test") - tmpTbTestA := createNewTable(dbTest.ID, "tba", model.TempTableLocal) - normalTbTestA := createNewTable(dbTest.ID, "tba", model.TempTableNone) - normalTbTestB := createNewTable(dbTest.ID, "tbb", model.TempTableNone) - normalTbTestC := createNewTable(db1.ID, "tbc", model.TempTableNone) - - is := &infoschema.SessionExtendedInfoSchema{ - InfoSchema: infoschema.MockInfoSchema([]*model.TableInfo{normalTbTestA.Meta(), normalTbTestB.Meta()}), - LocalTemporaryTables: sc, - } - - err = sc.AddTable(dbTest, tmpTbTestA) - require.NoError(t, err) - - // test TableByName - tbl, err := is.TableByName(dbTest.Name, normalTbTestA.Meta().Name) - require.NoError(t, err) - require.Equal(t, tmpTbTestA, tbl) - tbl, err = is.TableByName(dbTest.Name, normalTbTestB.Meta().Name) - require.NoError(t, err) - require.Equal(t, normalTbTestB.Meta(), tbl.Meta()) - tbl, err = is.TableByName(db1.Name, tb11.Meta().Name) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - require.Nil(t, tbl) - tbl, err = is.TableByName(db1.Name, tb12.Meta().Name) - require.NoError(t, err) - require.Equal(t, tb12, tbl) - - // test TableByID - tbl, ok := is.TableByID(normalTbTestA.Meta().ID) - require.True(t, ok) - require.Equal(t, normalTbTestA.Meta(), tbl.Meta()) - tbl, ok = is.TableByID(normalTbTestB.Meta().ID) - require.True(t, ok) - require.Equal(t, normalTbTestB.Meta(), tbl.Meta()) - tbl, ok = is.TableByID(tmpTbTestA.Meta().ID) - require.True(t, ok) - require.Equal(t, tmpTbTestA, tbl) - tbl, ok = is.TableByID(tb12.Meta().ID) - require.True(t, ok) - require.Equal(t, tb12, tbl) - - // test SchemaByTable - info, ok := is.SchemaByTable(normalTbTestA.Meta()) - require.True(t, ok) - require.Equal(t, dbTest.Name.L, info.Name.L) - info, ok = is.SchemaByTable(normalTbTestB.Meta()) - require.True(t, ok) - require.Equal(t, dbTest.Name.L, info.Name.L) - info, ok = is.SchemaByTable(tmpTbTestA.Meta()) - require.True(t, ok) - require.Equal(t, dbTest.Name.L, info.Name.L) - // SchemaByTable also returns DBInfo when the schema is not in the infoSchema but the table is an existing tmp table. - info, ok = is.SchemaByTable(tb12.Meta()) - require.True(t, ok) - require.Equal(t, db1.Name.L, info.Name.L) - // SchemaByTable returns nil when the schema is not in the infoSchema and the table is an non-existing normal table. - info, ok = is.SchemaByTable(normalTbTestC.Meta()) - require.False(t, ok) - require.Nil(t, info) - // SchemaByTable returns nil when the schema is not in the infoSchema and the table is an non-existing tmp table. - info, ok = is.SchemaByTable(tb22.Meta()) - require.False(t, ok) - require.Nil(t, info) -} - -// TestInfoSchemaCreateTableLike tests the table's column ID and index ID for memory database. -func TestInfoSchemaCreateTableLike(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table vi like information_schema.variables_info;") - tk.MustExec("alter table vi modify min_value varchar(32);") - tk.MustExec("create table u like metrics_schema.up;") - tk.MustExec("alter table u modify job int;") - tk.MustExec("create table so like performance_schema.setup_objects;") - tk.MustExec("alter table so modify object_name int;") - - tk.MustExec("create table t1 like information_schema.variables_info;") - tk.MustExec("alter table t1 add column c varchar(32);") - is := domain.GetDomain(tk.Session()).InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tblInfo := tbl.Meta() - require.Equal(t, tblInfo.Columns[8].Name.O, "c") - require.Equal(t, tblInfo.Columns[8].ID, int64(9)) - tk.MustExec("alter table t1 add index idx(c);") - is = domain.GetDomain(tk.Session()).InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tblInfo = tbl.Meta() - require.Equal(t, tblInfo.Indices[0].Name.O, "idx") - require.Equal(t, tblInfo.Indices[0].ID, int64(1)) - - // metrics_schema - tk.MustExec("create table t2 like metrics_schema.up;") - tk.MustExec("alter table t2 add column c varchar(32);") - is = domain.GetDomain(tk.Session()).InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tblInfo = tbl.Meta() - require.Equal(t, tblInfo.Columns[4].Name.O, "c") - require.Equal(t, tblInfo.Columns[4].ID, int64(5)) - tk.MustExec("alter table t2 add index idx(c);") - is = domain.GetDomain(tk.Session()).InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tblInfo = tbl.Meta() - require.Equal(t, tblInfo.Indices[0].Name.O, "idx") - require.Equal(t, tblInfo.Indices[0].ID, int64(1)) -} diff --git a/infoschema/internal/BUILD.bazel b/infoschema/internal/BUILD.bazel deleted file mode 100644 index 34b8e169a0ac3..0000000000000 --- a/infoschema/internal/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = ["testkit.go"], - importpath = "github.com/pingcap/tidb/infoschema/internal", - visibility = ["//infoschema:__subpackages__"], - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/infoschema/main_test.go b/infoschema/main_test.go deleted file mode 100644 index 1d1b99c743ec1..0000000000000 --- a/infoschema/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/infoschema/metrics/BUILD.bazel b/infoschema/metrics/BUILD.bazel deleted file mode 100644 index 7a6812a20032a..0000000000000 --- a/infoschema/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/infoschema/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/infoschema/metrics/metrics.go b/infoschema/metrics/metrics.go deleted file mode 100644 index 6e21b8de198e3..0000000000000 --- a/infoschema/metrics/metrics.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// infoschema metrics vars -var ( - GetLatestCounter prometheus.Counter - GetTSCounter prometheus.Counter - GetVersionCounter prometheus.Counter - - HitLatestCounter prometheus.Counter - HitTSCounter prometheus.Counter - HitVersionCounter prometheus.Counter - - LoadSchemaCounterSnapshot prometheus.Counter - - LoadSchemaDurationTotal prometheus.Observer - LoadSchemaDurationLoadDiff prometheus.Observer - LoadSchemaDurationLoadAll prometheus.Observer -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init infoschema metrics vars. -func InitMetricsVars() { - GetLatestCounter = metrics.InfoCacheCounters.WithLabelValues("get", "latest") - GetTSCounter = metrics.InfoCacheCounters.WithLabelValues("get", "ts") - GetVersionCounter = metrics.InfoCacheCounters.WithLabelValues("get", "version") - - HitLatestCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "latest") - HitTSCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "ts") - HitVersionCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "version") - - LoadSchemaCounterSnapshot = metrics.LoadSchemaCounter.WithLabelValues("snapshot") - - LoadSchemaDurationTotal = metrics.LoadSchemaDuration.WithLabelValues("total") - LoadSchemaDurationLoadDiff = metrics.LoadSchemaDuration.WithLabelValues("load-diff") - LoadSchemaDurationLoadAll = metrics.LoadSchemaDuration.WithLabelValues("load-all") -} diff --git a/infoschema/perfschema/BUILD.bazel b/infoschema/perfschema/BUILD.bazel deleted file mode 100644 index a71f9e10f3e9d..0000000000000 --- a/infoschema/perfschema/BUILD.bazel +++ /dev/null @@ -1,57 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "perfschema", - srcs = [ - "const.go", - "init.go", - "tables.go", - ], - importpath = "github.com/pingcap/tidb/infoschema/perfschema", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//expression", - "//infoschema", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//table", - "//table/tables", - "//types", - "//util", - "//util/profile", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - ], -) - -go_test( - name = "perfschema_test", - timeout = "short", - srcs = [ - "main_test.go", - "tables_test.go", - ], - data = glob(["testdata/**"]), - embed = [":perfschema"], - flaky = True, - shard_count = 5, - deps = [ - "//kv", - "//parser/terror", - "//session", - "//store/mockstore", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/infoschema/perfschema/main_test.go b/infoschema/perfschema/main_test.go deleted file mode 100644 index 4633b2f201852..0000000000000 --- a/infoschema/perfschema/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package perfschema - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/infoschema/perfschema/tables.go b/infoschema/perfschema/tables.go deleted file mode 100644 index 045307b84681c..0000000000000 --- a/infoschema/perfschema/tables.go +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package perfschema - -import ( - "cmp" - "context" - "fmt" - "net/http" - "slices" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/profile" -) - -const ( - tableNameGlobalStatus = "global_status" - tableNameSessionStatus = "session_status" - tableNameSetupActors = "setup_actors" - tableNameSetupObjects = "setup_objects" - tableNameSetupInstruments = "setup_instruments" - tableNameSetupConsumers = "setup_consumers" - tableNameEventsStatementsCurrent = "events_statements_current" - tableNameEventsStatementsHistory = "events_statements_history" - tableNameEventsStatementsHistoryLong = "events_statements_history_long" - tableNamePreparedStatementsInstances = "prepared_statements_instances" - tableNameEventsTransactionsCurrent = "events_transactions_current" - tableNameEventsTransactionsHistory = "events_transactions_history" - tableNameEventsTransactionsHistoryLong = "events_transactions_history_long" - tableNameEventsStagesCurrent = "events_stages_current" - tableNameEventsStagesHistory = "events_stages_history" - tableNameEventsStagesHistoryLong = "events_stages_history_long" - tableNameEventsStatementsSummaryByDigest = "events_statements_summary_by_digest" - tableNameTiDBProfileCPU = "tidb_profile_cpu" - tableNameTiDBProfileMemory = "tidb_profile_memory" - tableNameTiDBProfileMutex = "tidb_profile_mutex" - tableNameTiDBProfileAllocs = "tidb_profile_allocs" - tableNameTiDBProfileBlock = "tidb_profile_block" - tableNameTiDBProfileGoroutines = "tidb_profile_goroutines" - tableNameTiKVProfileCPU = "tikv_profile_cpu" - tableNamePDProfileCPU = "pd_profile_cpu" - tableNamePDProfileMemory = "pd_profile_memory" - tableNamePDProfileMutex = "pd_profile_mutex" - tableNamePDProfileAllocs = "pd_profile_allocs" - tableNamePDProfileBlock = "pd_profile_block" - tableNamePDProfileGoroutines = "pd_profile_goroutines" - tableNameSessionAccountConnectAttrs = "session_account_connect_attrs" - tableNameSessionConnectAttrs = "session_connect_attrs" - tableNameSessionVariables = "session_variables" -) - -var tableIDMap = map[string]int64{ - tableNameGlobalStatus: autoid.PerformanceSchemaDBID + 1, - tableNameSessionStatus: autoid.PerformanceSchemaDBID + 2, - tableNameSetupActors: autoid.PerformanceSchemaDBID + 3, - tableNameSetupObjects: autoid.PerformanceSchemaDBID + 4, - tableNameSetupInstruments: autoid.PerformanceSchemaDBID + 5, - tableNameSetupConsumers: autoid.PerformanceSchemaDBID + 6, - tableNameEventsStatementsCurrent: autoid.PerformanceSchemaDBID + 7, - tableNameEventsStatementsHistory: autoid.PerformanceSchemaDBID + 8, - tableNameEventsStatementsHistoryLong: autoid.PerformanceSchemaDBID + 9, - tableNamePreparedStatementsInstances: autoid.PerformanceSchemaDBID + 10, - tableNameEventsTransactionsCurrent: autoid.PerformanceSchemaDBID + 11, - tableNameEventsTransactionsHistory: autoid.PerformanceSchemaDBID + 12, - tableNameEventsTransactionsHistoryLong: autoid.PerformanceSchemaDBID + 13, - tableNameEventsStagesCurrent: autoid.PerformanceSchemaDBID + 14, - tableNameEventsStagesHistory: autoid.PerformanceSchemaDBID + 15, - tableNameEventsStagesHistoryLong: autoid.PerformanceSchemaDBID + 16, - tableNameEventsStatementsSummaryByDigest: autoid.PerformanceSchemaDBID + 17, - tableNameTiDBProfileCPU: autoid.PerformanceSchemaDBID + 18, - tableNameTiDBProfileMemory: autoid.PerformanceSchemaDBID + 19, - tableNameTiDBProfileMutex: autoid.PerformanceSchemaDBID + 20, - tableNameTiDBProfileAllocs: autoid.PerformanceSchemaDBID + 21, - tableNameTiDBProfileBlock: autoid.PerformanceSchemaDBID + 22, - tableNameTiDBProfileGoroutines: autoid.PerformanceSchemaDBID + 23, - tableNameTiKVProfileCPU: autoid.PerformanceSchemaDBID + 24, - tableNamePDProfileCPU: autoid.PerformanceSchemaDBID + 25, - tableNamePDProfileMemory: autoid.PerformanceSchemaDBID + 26, - tableNamePDProfileMutex: autoid.PerformanceSchemaDBID + 27, - tableNamePDProfileAllocs: autoid.PerformanceSchemaDBID + 28, - tableNamePDProfileBlock: autoid.PerformanceSchemaDBID + 29, - tableNamePDProfileGoroutines: autoid.PerformanceSchemaDBID + 30, - tableNameSessionVariables: autoid.PerformanceSchemaDBID + 31, - tableNameSessionConnectAttrs: autoid.PerformanceSchemaDBID + 32, - tableNameSessionAccountConnectAttrs: autoid.PerformanceSchemaDBID + 33, -} - -// perfSchemaTable stands for the fake table all its data is in the memory. -type perfSchemaTable struct { - infoschema.VirtualTable - meta *model.TableInfo - cols []*table.Column - tp table.Type - indices []table.Index -} - -var pluginTable = make(map[string]func(autoid.Allocators, *model.TableInfo) (table.Table, error)) - -// IsPredefinedTable judges whether this table is predefined. -func IsPredefinedTable(tableName string) bool { - _, ok := tableIDMap[strings.ToLower(tableName)] - return ok -} - -func tableFromMeta(allocs autoid.Allocators, meta *model.TableInfo) (table.Table, error) { - if f, ok := pluginTable[meta.Name.L]; ok { - ret, err := f(allocs, meta) - return ret, err - } - return createPerfSchemaTable(meta) -} - -// createPerfSchemaTable creates all perfSchemaTables -func createPerfSchemaTable(meta *model.TableInfo) (*perfSchemaTable, error) { - columns := make([]*table.Column, 0, len(meta.Columns)) - for _, colInfo := range meta.Columns { - col := table.ToColumn(colInfo) - columns = append(columns, col) - } - tp := table.VirtualTable - t := &perfSchemaTable{ - meta: meta, - cols: columns, - tp: tp, - } - if err := initTableIndices(t); err != nil { - return nil, err - } - return t, nil -} - -// Cols implements table.Table Type interface. -func (vt *perfSchemaTable) Cols() []*table.Column { - return vt.cols -} - -// VisibleCols implements table.Table VisibleCols interface. -func (vt *perfSchemaTable) VisibleCols() []*table.Column { - return vt.cols -} - -// HiddenCols implements table.Table HiddenCols interface. -func (vt *perfSchemaTable) HiddenCols() []*table.Column { - return nil -} - -// WritableCols implements table.Table Type interface. -func (vt *perfSchemaTable) WritableCols() []*table.Column { - return vt.cols -} - -// DeletableCols implements table.Table Type interface. -func (vt *perfSchemaTable) DeletableCols() []*table.Column { - return vt.cols -} - -// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. -func (vt *perfSchemaTable) FullHiddenColsAndVisibleCols() []*table.Column { - return vt.cols -} - -// GetPhysicalID implements table.Table GetID interface. -func (vt *perfSchemaTable) GetPhysicalID() int64 { - return vt.meta.ID -} - -// Meta implements table.Table Type interface. -func (vt *perfSchemaTable) Meta() *model.TableInfo { - return vt.meta -} - -// Type implements table.Table Type interface. -func (vt *perfSchemaTable) Type() table.Type { - return vt.tp -} - -// Indices implements table.Table Indices interface. -func (vt *perfSchemaTable) Indices() []table.Index { - return vt.indices -} - -// GetPartitionedTable implements table.Table GetPartitionedTable interface. -func (vt *perfSchemaTable) GetPartitionedTable() table.PartitionedTable { - return nil -} - -// initTableIndices initializes the indices of the perfSchemaTable. -func initTableIndices(t *perfSchemaTable) error { - tblInfo := t.meta - for _, idxInfo := range tblInfo.Indices { - if idxInfo.State == model.StateNone { - return table.ErrIndexStateCantNone.GenWithStackByArgs(idxInfo.Name) - } - idx := tables.NewIndex(t.meta.ID, tblInfo, idxInfo) - t.indices = append(t.indices, idx) - } - return nil -} - -func (vt *perfSchemaTable) getRows(ctx context.Context, sctx sessionctx.Context, cols []*table.Column) (fullRows [][]types.Datum, err error) { - switch vt.meta.Name.O { - case tableNameTiDBProfileCPU: - fullRows, err = (&profile.Collector{}).ProfileGraph("cpu") - case tableNameTiDBProfileMemory: - fullRows, err = (&profile.Collector{}).ProfileGraph("heap") - case tableNameTiDBProfileMutex: - fullRows, err = (&profile.Collector{}).ProfileGraph("mutex") - case tableNameTiDBProfileAllocs: - fullRows, err = (&profile.Collector{}).ProfileGraph("allocs") - case tableNameTiDBProfileBlock: - fullRows, err = (&profile.Collector{}).ProfileGraph("block") - case tableNameTiDBProfileGoroutines: - fullRows, err = (&profile.Collector{}).ProfileGraph("goroutine") - case tableNameTiKVProfileCPU: - interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) - fullRows, err = dataForRemoteProfile(sctx, "tikv", "/debug/pprof/profile?seconds="+interval, false) - case tableNamePDProfileCPU: - interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/profile?seconds="+interval, false) - case tableNamePDProfileMemory: - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/heap", false) - case tableNamePDProfileMutex: - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/mutex", false) - case tableNamePDProfileAllocs: - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/allocs", false) - case tableNamePDProfileBlock: - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/block", false) - case tableNamePDProfileGoroutines: - fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/goroutine?debug=2", true) - case tableNameSessionVariables: - fullRows, err = infoschema.GetDataFromSessionVariables(ctx, sctx) - case tableNameSessionConnectAttrs: - fullRows, err = infoschema.GetDataFromSessionConnectAttrs(sctx, false) - case tableNameSessionAccountConnectAttrs: - fullRows, err = infoschema.GetDataFromSessionConnectAttrs(sctx, true) - } - if err != nil { - return - } - if len(cols) == len(vt.cols) { - return - } - rows := make([][]types.Datum, len(fullRows)) - for i, fullRow := range fullRows { - row := make([]types.Datum, len(cols)) - for j, col := range cols { - row[j] = fullRow[col.Offset] - } - rows[i] = row - } - return rows, nil -} - -// IterRecords implements table.Table IterRecords interface. -func (vt *perfSchemaTable) IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error { - rows, err := vt.getRows(ctx, sctx, cols) - if err != nil { - return err - } - for i, row := range rows { - more, err := fn(kv.IntHandle(i), row, cols) - if err != nil { - return err - } - if !more { - break - } - } - return nil -} - -func dataForRemoteProfile(ctx sessionctx.Context, nodeType, uri string, isGoroutine bool) ([][]types.Datum, error) { - var ( - servers []infoschema.ServerInfo - err error - ) - switch nodeType { - case "tikv": - servers, err = infoschema.GetStoreServerInfo(ctx) - case "pd": - servers, err = infoschema.GetPDServerInfo(ctx) - default: - return nil, errors.Errorf("%s does not support profile remote component", nodeType) - } - failpoint.Inject("mockRemoteNodeStatusAddress", func(val failpoint.Value) { - // The cluster topology is injected by `failpoint` expression and - // there is no extra checks for it. (let the test fail if the expression invalid) - if s := val.(string); len(s) > 0 { - servers = servers[:0] - for _, server := range strings.Split(s, ";") { - parts := strings.Split(server, ",") - if parts[0] != nodeType { - continue - } - servers = append(servers, infoschema.ServerInfo{ - ServerType: parts[0], - Address: parts[1], - StatusAddr: parts[2], - }) - } - // erase error - err = nil - } - }) - if err != nil { - return nil, errors.Trace(err) - } - - type result struct { - addr string - rows [][]types.Datum - err error - } - - wg := sync.WaitGroup{} - ch := make(chan result, len(servers)) - for _, server := range servers { - statusAddr := server.StatusAddr - if len(statusAddr) == 0 { - ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("TiKV node %s does not contain status address", server.Address)) - continue - } - - wg.Add(1) - go func(address string) { - util.WithRecovery(func() { - defer wg.Done() - url := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), statusAddr, uri) - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - ch <- result{err: errors.Trace(err)} - return - } - // Forbidden PD follower proxy - req.Header.Add("PD-Allow-follower-handle", "true") - // TiKV output svg format in default - req.Header.Add("Content-Type", "application/protobuf") - resp, err := util.InternalHTTPClient().Do(req) - if err != nil { - ch <- result{err: errors.Trace(err)} - return - } - defer func() { - terror.Log(resp.Body.Close()) - }() - if resp.StatusCode != http.StatusOK { - ch <- result{err: errors.Errorf("request %s failed: %s", url, resp.Status)} - return - } - collector := profile.Collector{} - var rows [][]types.Datum - if isGoroutine { - rows, err = collector.ParseGoroutines(resp.Body) - } else { - rows, err = collector.ProfileReaderToDatums(resp.Body) - } - if err != nil { - ch <- result{err: errors.Trace(err)} - return - } - ch <- result{addr: address, rows: rows} - }, nil) - }(statusAddr) - } - - wg.Wait() - close(ch) - - // Keep the original order to make the result more stable - var results []result //nolint: prealloc - for result := range ch { - if result.err != nil { - ctx.GetSessionVars().StmtCtx.AppendWarning(result.err) - continue - } - results = append(results, result) - } - slices.SortFunc(results, func(i, j result) int { return cmp.Compare(i.addr, j.addr) }) - var finalRows [][]types.Datum - for _, result := range results { - addr := types.NewStringDatum(result.addr) - for _, row := range result.rows { - // Insert the node address in front of rows - finalRows = append(finalRows, append([]types.Datum{addr}, row...)) - } - } - return finalRows, nil -} diff --git a/infoschema/perfschema/tables_test.go b/infoschema/perfschema/tables_test.go deleted file mode 100644 index 1f2ed2536c8f7..0000000000000 --- a/infoschema/perfschema/tables_test.go +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package perfschema_test - -import ( - "fmt" - "io" - "net/http" - "net/http/httptest" - "os" - "runtime/pprof" - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema/perfschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" -) - -func TestPredefinedTables(t *testing.T) { - require.True(t, perfschema.IsPredefinedTable("EVENTS_statements_summary_by_digest")) - require.False(t, perfschema.IsPredefinedTable("statements")) -} - -func TestPerfSchemaTables(t *testing.T) { - store := newMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use performance_schema") - tk.MustQuery("select * from global_status where variable_name = 'Ssl_verify_mode'").Check(testkit.Rows()) - tk.MustQuery("select * from session_status where variable_name = 'Ssl_verify_mode'").Check(testkit.Rows()) - tk.MustQuery("select * from setup_actors").Check(testkit.Rows()) - tk.MustQuery("select * from events_stages_history_long").Check(testkit.Rows()) -} - -func TestSessionVariables(t *testing.T) { - store := newMockStore(t) - tk := testkit.NewTestKit(t, store) - - res := tk.MustQuery("select variable_value from performance_schema.session_variables order by variable_name limit 10;") - tk.MustQuery("select variable_value from information_schema.session_variables order by variable_name limit 10;").Check(res.Rows()) -} - -func TestTiKVProfileCPU(t *testing.T) { - store := newMockStore(t) - - router := http.NewServeMux() - mockServer := httptest.NewServer(router) - mockAddr := strings.TrimPrefix(mockServer.URL, "http://") - defer mockServer.Close() - - // mock tikv profile - copyHandler := func(filename string) http.HandlerFunc { - return func(w http.ResponseWriter, _ *http.Request) { - file, err := os.Open(filename) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - defer func() { terror.Log(file.Close()) }() - _, err = io.Copy(w, file) - terror.Log(err) - } - } - router.HandleFunc("/debug/pprof/profile", copyHandler("testdata/tikv.cpu.profile")) - - // failpoint setting - servers := []string{ - strings.Join([]string{"tikv", mockAddr, mockAddr}, ","), - strings.Join([]string{"pd", mockAddr, mockAddr}, ","), - } - fpExpr := strings.Join(servers, ";") - fpName := "github.com/pingcap/tidb/infoschema/perfschema/mockRemoteNodeStatusAddress" - require.NoError(t, failpoint.Enable(fpName, fmt.Sprintf(`return("%s")`, fpExpr))) - defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use performance_schema") - result := tk.MustQuery("select function, percent_abs, percent_rel from tikv_profile_cpu where depth < 3") - - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - result.Check(testkit.Rows( - "root 100% 100%", - "├─tikv::server::load_statistics::linux::ThreadLoadStatistics::record::h59facb8d680e7794 75.00% 75.00%", - "│ └─procinfo::pid::stat::stat_task::h69e1aa2c331aebb6 75.00% 100%", - "├─nom::nom::digit::h905aaaeff7d8ec8e 16.07% 16.07%", - "│ ├─ as core::iter::traits::iterator::Iterator>::next::h16936f9061bb75e4 6.25% 38.89%", - "│ ├─Unknown 3.57% 22.22%", - "│ ├─<&u8 as nom::traits::AsChar>::is_dec_digit::he9eacc3fad26ab81 2.68% 16.67%", - "│ ├─<&[u8] as nom::traits::InputIter>::iter_indices::h6192338433683bff 1.79% 11.11%", - "│ └─<&[T] as nom::traits::Slice>>::slice::h38d31f11f84aa302 1.79% 11.11%", - "├─::realloc::h5199c50710ab6f9d 1.79% 1.79%", - "│ └─rallocx 1.79% 100%", - "├─::dealloc::hea83459aa98dd2dc 1.79% 1.79%", - "│ └─sdallocx 1.79% 100%", - "├─::alloc::hc7962e02169a5c56 0.89% 0.89%", - "│ └─mallocx 0.89% 100%", - "├─engine::rocks::util::engine_metrics::flush_engine_iostall_properties::h64a7661c95aa1db7 0.89% 0.89%", - "│ └─rocksdb::rocksdb::DB::get_map_property_cf::h9722f9040411af44 0.89% 100%", - "├─core::ptr::real_drop_in_place::h8def0d99e7136f33 0.89% 0.89%", - "│ └─ as core::ops::drop::Drop>::drop::h9b59b303bffde02c 0.89% 100%", - "├─tikv_util::metrics::threads_linux::ThreadInfoStatistics::record::ha8cc290b3f46af88 0.89% 0.89%", - "│ └─procinfo::pid::stat::stat_task::h69e1aa2c331aebb6 0.89% 100%", - "├─crossbeam_utils::backoff::Backoff::snooze::h5c121ef4ce616a3c 0.89% 0.89%", - "│ └─core::iter::range::>::next::hdb23ceb766e7a91f 0.89% 100%", - "└─::next::he129c78b3deb639d 0.89% 0.89%", - " └─Unknown 0.89% 100%")) - - // We can use current processe profile to mock profile of PD because the PD has the - // same way of retrieving profile with TiDB. And the purpose of this test case is used - // to make sure all profile HTTP API have been accessed. - accessed := map[string]struct{}{} - handlerFactory := func(name string, debug ...int) func(w http.ResponseWriter, _ *http.Request) { - debugLevel := 0 - if len(debug) > 0 { - debugLevel = debug[0] - } - return func(w http.ResponseWriter, _ *http.Request) { - profile := pprof.Lookup(name) - if profile == nil { - http.Error(w, fmt.Sprintf("profile %s not found", name), http.StatusBadRequest) - return - } - if err := profile.WriteTo(w, debugLevel); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - accessed[name] = struct{}{} - } - } - - // mock PD profile - router.HandleFunc("/pd/api/v1/debug/pprof/profile", copyHandler("testdata/test.pprof")) - router.HandleFunc("/pd/api/v1/debug/pprof/heap", handlerFactory("heap")) - router.HandleFunc("/pd/api/v1/debug/pprof/mutex", handlerFactory("mutex")) - router.HandleFunc("/pd/api/v1/debug/pprof/allocs", handlerFactory("allocs")) - router.HandleFunc("/pd/api/v1/debug/pprof/block", handlerFactory("block")) - router.HandleFunc("/pd/api/v1/debug/pprof/goroutine", handlerFactory("goroutine", 2)) - - tk.MustQuery("select * from pd_profile_cpu where depth < 3") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - tk.MustQuery("select * from pd_profile_memory where depth < 3") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - tk.MustQuery("select * from pd_profile_mutex where depth < 3") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - tk.MustQuery("select * from pd_profile_allocs where depth < 3") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - tk.MustQuery("select * from pd_profile_block where depth < 3") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - tk.MustQuery("select * from pd_profile_goroutines") - warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) - - require.Lenf(t, accessed, 5, "expect all HTTP API had been accessed, but found: %v", accessed) -} - -// TestSessionConnectAttrs tests the `SESSION_CONNECT_ATTRS` table -func TestSessionConnectAttrs(t *testing.T) { - sm := &testkit.MockSessionManager{} - sm.ConAttrs = map[uint64]map[string]string{ - 123456: { - "_client_name": "libmysql", - }, - } - store := newMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.Session().SetSessionManager(sm) - tk.MustQuery("SELECT PROCESSLIST_ID,ATTR_NAME,ATTR_VALUE,ORDINAL_POSITION FROM performance_schema.SESSION_CONNECT_ATTRS").Check(testkit.Rows("123456 _client_name libmysql 0")) -} - -func newMockStore(t *testing.T) kv.Storage { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - session.DisableStats4Test() - - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - - t.Cleanup(func() { - dom.Close() - err := store.Close() - require.NoError(t, err) - view.Stop() - }) - - return store -} diff --git a/infoschema/tables.go b/infoschema/tables.go deleted file mode 100644 index 68a3f1ac6440d..0000000000000 --- a/infoschema/tables.go +++ /dev/null @@ -1,2488 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package infoschema - -import ( - "cmp" - "context" - "encoding/json" - "fmt" - "net" - "net/http" - "slices" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/diagnosticspb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/resourcegroup" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stmtsummary" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" -) - -const ( - // TableSchemata is the string constant of infoschema table. - TableSchemata = "SCHEMATA" - // TableTables is the string constant of infoschema table. - TableTables = "TABLES" - // TableColumns is the string constant of infoschema table - TableColumns = "COLUMNS" - tableColumnStatistics = "COLUMN_STATISTICS" - // TableStatistics is the string constant of infoschema table - TableStatistics = "STATISTICS" - // TableCharacterSets is the string constant of infoschema charactersets memory table - TableCharacterSets = "CHARACTER_SETS" - // TableCollations is the string constant of infoschema collations memory table. - TableCollations = "COLLATIONS" - tableFiles = "FILES" - // CatalogVal is the string constant of TABLE_CATALOG. - CatalogVal = "def" - // TableProfiling is the string constant of infoschema table. - TableProfiling = "PROFILING" - // TablePartitions is the string constant of infoschema table. - TablePartitions = "PARTITIONS" - // TableKeyColumn is the string constant of KEY_COLUMN_USAGE. - TableKeyColumn = "KEY_COLUMN_USAGE" - // TableReferConst is the string constant of REFERENTIAL_CONSTRAINTS. - TableReferConst = "REFERENTIAL_CONSTRAINTS" - // TableSessionVar is the string constant of SESSION_VARIABLES. - TableSessionVar = "SESSION_VARIABLES" - tablePlugins = "PLUGINS" - // TableConstraints is the string constant of TABLE_CONSTRAINTS. - TableConstraints = "TABLE_CONSTRAINTS" - tableTriggers = "TRIGGERS" - // TableUserPrivileges is the string constant of infoschema user privilege table. - TableUserPrivileges = "USER_PRIVILEGES" - tableSchemaPrivileges = "SCHEMA_PRIVILEGES" - tableTablePrivileges = "TABLE_PRIVILEGES" - tableColumnPrivileges = "COLUMN_PRIVILEGES" - // TableEngines is the string constant of infoschema table. - TableEngines = "ENGINES" - // TableViews is the string constant of infoschema table. - TableViews = "VIEWS" - tableRoutines = "ROUTINES" - tableParameters = "PARAMETERS" - tableEvents = "EVENTS" - tableGlobalStatus = "GLOBAL_STATUS" - tableGlobalVariables = "GLOBAL_VARIABLES" - tableSessionStatus = "SESSION_STATUS" - tableOptimizerTrace = "OPTIMIZER_TRACE" - tableTableSpaces = "TABLESPACES" - // TableCollationCharacterSetApplicability is the string constant of infoschema memory table. - TableCollationCharacterSetApplicability = "COLLATION_CHARACTER_SET_APPLICABILITY" - // TableProcesslist is the string constant of infoschema table. - TableProcesslist = "PROCESSLIST" - // TableTiDBIndexes is the string constant of infoschema table - TableTiDBIndexes = "TIDB_INDEXES" - // TableTiDBHotRegions is the string constant of infoschema table - TableTiDBHotRegions = "TIDB_HOT_REGIONS" - // TableTiDBHotRegionsHistory is the string constant of infoschema table - TableTiDBHotRegionsHistory = "TIDB_HOT_REGIONS_HISTORY" - // TableTiKVStoreStatus is the string constant of infoschema table - TableTiKVStoreStatus = "TIKV_STORE_STATUS" - // TableAnalyzeStatus is the string constant of Analyze Status - TableAnalyzeStatus = "ANALYZE_STATUS" - // TableTiKVRegionStatus is the string constant of infoschema table - TableTiKVRegionStatus = "TIKV_REGION_STATUS" - // TableTiKVRegionPeers is the string constant of infoschema table - TableTiKVRegionPeers = "TIKV_REGION_PEERS" - // TableTiDBServersInfo is the string constant of TiDB server information table. - TableTiDBServersInfo = "TIDB_SERVERS_INFO" - // TableSlowQuery is the string constant of slow query memory table. - TableSlowQuery = "SLOW_QUERY" - // TableClusterInfo is the string constant of cluster info memory table. - TableClusterInfo = "CLUSTER_INFO" - // TableClusterConfig is the string constant of cluster configuration memory table. - TableClusterConfig = "CLUSTER_CONFIG" - // TableClusterLog is the string constant of cluster log memory table. - TableClusterLog = "CLUSTER_LOG" - // TableClusterLoad is the string constant of cluster load memory table. - TableClusterLoad = "CLUSTER_LOAD" - // TableClusterHardware is the string constant of cluster hardware table. - TableClusterHardware = "CLUSTER_HARDWARE" - // TableClusterSystemInfo is the string constant of cluster system info table. - TableClusterSystemInfo = "CLUSTER_SYSTEMINFO" - // TableTiFlashReplica is the string constant of tiflash replica table. - TableTiFlashReplica = "TIFLASH_REPLICA" - // TableInspectionResult is the string constant of inspection result table. - TableInspectionResult = "INSPECTION_RESULT" - // TableMetricTables is a table that contains all metrics table definition. - TableMetricTables = "METRICS_TABLES" - // TableMetricSummary is a summary table that contains all metrics. - TableMetricSummary = "METRICS_SUMMARY" - // TableMetricSummaryByLabel is a metric table that contains all metrics that group by label info. - TableMetricSummaryByLabel = "METRICS_SUMMARY_BY_LABEL" - // TableInspectionSummary is the string constant of inspection summary table. - TableInspectionSummary = "INSPECTION_SUMMARY" - // TableInspectionRules is the string constant of currently implemented inspection and summary rules. - TableInspectionRules = "INSPECTION_RULES" - // TableDDLJobs is the string constant of DDL job table. - TableDDLJobs = "DDL_JOBS" - // TableSequences is the string constant of all sequences created by user. - TableSequences = "SEQUENCES" - // TableStatementsSummary is the string constant of statement summary table. - TableStatementsSummary = "STATEMENTS_SUMMARY" - // TableStatementsSummaryHistory is the string constant of statements summary history table. - TableStatementsSummaryHistory = "STATEMENTS_SUMMARY_HISTORY" - // TableStatementsSummaryEvicted is the string constant of statements summary evicted table. - TableStatementsSummaryEvicted = "STATEMENTS_SUMMARY_EVICTED" - // TableStorageStats is a table that contains all tables disk usage - TableStorageStats = "TABLE_STORAGE_STATS" - // TableTiFlashTables is the string constant of tiflash tables table. - TableTiFlashTables = "TIFLASH_TABLES" - // TableTiFlashSegments is the string constant of tiflash segments table. - TableTiFlashSegments = "TIFLASH_SEGMENTS" - // TableClientErrorsSummaryGlobal is the string constant of client errors table. - TableClientErrorsSummaryGlobal = "CLIENT_ERRORS_SUMMARY_GLOBAL" - // TableClientErrorsSummaryByUser is the string constant of client errors table. - TableClientErrorsSummaryByUser = "CLIENT_ERRORS_SUMMARY_BY_USER" - // TableClientErrorsSummaryByHost is the string constant of client errors table. - TableClientErrorsSummaryByHost = "CLIENT_ERRORS_SUMMARY_BY_HOST" - // TableTiDBTrx is current running transaction status table. - TableTiDBTrx = "TIDB_TRX" - // TableDeadlocks is the string constant of deadlock table. - TableDeadlocks = "DEADLOCKS" - // TableDataLockWaits is current lock waiting status table. - TableDataLockWaits = "DATA_LOCK_WAITS" - // TableAttributes is the string constant of attributes table. - TableAttributes = "ATTRIBUTES" - // TablePlacementPolicies is the string constant of placement policies table. - TablePlacementPolicies = "PLACEMENT_POLICIES" - // TableTrxSummary is the string constant of transaction summary table. - TableTrxSummary = "TRX_SUMMARY" - // TableVariablesInfo is the string constant of variables_info table. - TableVariablesInfo = "VARIABLES_INFO" - // TableUserAttributes is the string constant of user_attributes view. - TableUserAttributes = "USER_ATTRIBUTES" - // TableMemoryUsage is the memory usage status of tidb instance. - TableMemoryUsage = "MEMORY_USAGE" - // TableMemoryUsageOpsHistory is the memory control operators history. - TableMemoryUsageOpsHistory = "MEMORY_USAGE_OPS_HISTORY" - // TableResourceGroups is the metadata of resource groups. - TableResourceGroups = "RESOURCE_GROUPS" - // TableRunawayWatches is the query list of runaway watch. - TableRunawayWatches = "RUNAWAY_WATCHES" - // TableCheckConstraints is the list of CHECK constraints. - TableCheckConstraints = "CHECK_CONSTRAINTS" -) - -const ( - // DataLockWaitsColumnKey is the name of the KEY column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnKey = "KEY" - // DataLockWaitsColumnKeyInfo is the name of the KEY_INFO column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnKeyInfo = "KEY_INFO" - // DataLockWaitsColumnTrxID is the name of the TRX_ID column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnTrxID = "TRX_ID" - // DataLockWaitsColumnCurrentHoldingTrxID is the name of the CURRENT_HOLDING_TRX_ID column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnCurrentHoldingTrxID = "CURRENT_HOLDING_TRX_ID" - // DataLockWaitsColumnSQLDigest is the name of the SQL_DIGEST column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnSQLDigest = "SQL_DIGEST" - // DataLockWaitsColumnSQLDigestText is the name of the SQL_DIGEST_TEXT column of the DATA_LOCK_WAITS table. - DataLockWaitsColumnSQLDigestText = "SQL_DIGEST_TEXT" -) - -var tableIDMap = map[string]int64{ - TableSchemata: autoid.InformationSchemaDBID + 1, - TableTables: autoid.InformationSchemaDBID + 2, - TableColumns: autoid.InformationSchemaDBID + 3, - tableColumnStatistics: autoid.InformationSchemaDBID + 4, - TableStatistics: autoid.InformationSchemaDBID + 5, - TableCharacterSets: autoid.InformationSchemaDBID + 6, - TableCollations: autoid.InformationSchemaDBID + 7, - tableFiles: autoid.InformationSchemaDBID + 8, - CatalogVal: autoid.InformationSchemaDBID + 9, - TableProfiling: autoid.InformationSchemaDBID + 10, - TablePartitions: autoid.InformationSchemaDBID + 11, - TableKeyColumn: autoid.InformationSchemaDBID + 12, - TableReferConst: autoid.InformationSchemaDBID + 13, - TableSessionVar: autoid.InformationSchemaDBID + 14, - tablePlugins: autoid.InformationSchemaDBID + 15, - TableConstraints: autoid.InformationSchemaDBID + 16, - tableTriggers: autoid.InformationSchemaDBID + 17, - TableUserPrivileges: autoid.InformationSchemaDBID + 18, - tableSchemaPrivileges: autoid.InformationSchemaDBID + 19, - tableTablePrivileges: autoid.InformationSchemaDBID + 20, - tableColumnPrivileges: autoid.InformationSchemaDBID + 21, - TableEngines: autoid.InformationSchemaDBID + 22, - TableViews: autoid.InformationSchemaDBID + 23, - tableRoutines: autoid.InformationSchemaDBID + 24, - tableParameters: autoid.InformationSchemaDBID + 25, - tableEvents: autoid.InformationSchemaDBID + 26, - tableGlobalStatus: autoid.InformationSchemaDBID + 27, - tableGlobalVariables: autoid.InformationSchemaDBID + 28, - tableSessionStatus: autoid.InformationSchemaDBID + 29, - tableOptimizerTrace: autoid.InformationSchemaDBID + 30, - tableTableSpaces: autoid.InformationSchemaDBID + 31, - TableCollationCharacterSetApplicability: autoid.InformationSchemaDBID + 32, - TableProcesslist: autoid.InformationSchemaDBID + 33, - TableTiDBIndexes: autoid.InformationSchemaDBID + 34, - TableSlowQuery: autoid.InformationSchemaDBID + 35, - TableTiDBHotRegions: autoid.InformationSchemaDBID + 36, - TableTiKVStoreStatus: autoid.InformationSchemaDBID + 37, - TableAnalyzeStatus: autoid.InformationSchemaDBID + 38, - TableTiKVRegionStatus: autoid.InformationSchemaDBID + 39, - TableTiKVRegionPeers: autoid.InformationSchemaDBID + 40, - TableTiDBServersInfo: autoid.InformationSchemaDBID + 41, - TableClusterInfo: autoid.InformationSchemaDBID + 42, - TableClusterConfig: autoid.InformationSchemaDBID + 43, - TableClusterLoad: autoid.InformationSchemaDBID + 44, - TableTiFlashReplica: autoid.InformationSchemaDBID + 45, - ClusterTableSlowLog: autoid.InformationSchemaDBID + 46, - ClusterTableProcesslist: autoid.InformationSchemaDBID + 47, - TableClusterLog: autoid.InformationSchemaDBID + 48, - TableClusterHardware: autoid.InformationSchemaDBID + 49, - TableClusterSystemInfo: autoid.InformationSchemaDBID + 50, - TableInspectionResult: autoid.InformationSchemaDBID + 51, - TableMetricSummary: autoid.InformationSchemaDBID + 52, - TableMetricSummaryByLabel: autoid.InformationSchemaDBID + 53, - TableMetricTables: autoid.InformationSchemaDBID + 54, - TableInspectionSummary: autoid.InformationSchemaDBID + 55, - TableInspectionRules: autoid.InformationSchemaDBID + 56, - TableDDLJobs: autoid.InformationSchemaDBID + 57, - TableSequences: autoid.InformationSchemaDBID + 58, - TableStatementsSummary: autoid.InformationSchemaDBID + 59, - TableStatementsSummaryHistory: autoid.InformationSchemaDBID + 60, - ClusterTableStatementsSummary: autoid.InformationSchemaDBID + 61, - ClusterTableStatementsSummaryHistory: autoid.InformationSchemaDBID + 62, - TableStorageStats: autoid.InformationSchemaDBID + 63, - TableTiFlashTables: autoid.InformationSchemaDBID + 64, - TableTiFlashSegments: autoid.InformationSchemaDBID + 65, - // Removed, see https://github.com/pingcap/tidb/issues/28890 - //TablePlacementPolicy: autoid.InformationSchemaDBID + 66, - TableClientErrorsSummaryGlobal: autoid.InformationSchemaDBID + 67, - TableClientErrorsSummaryByUser: autoid.InformationSchemaDBID + 68, - TableClientErrorsSummaryByHost: autoid.InformationSchemaDBID + 69, - TableTiDBTrx: autoid.InformationSchemaDBID + 70, - ClusterTableTiDBTrx: autoid.InformationSchemaDBID + 71, - TableDeadlocks: autoid.InformationSchemaDBID + 72, - ClusterTableDeadlocks: autoid.InformationSchemaDBID + 73, - TableDataLockWaits: autoid.InformationSchemaDBID + 74, - TableStatementsSummaryEvicted: autoid.InformationSchemaDBID + 75, - ClusterTableStatementsSummaryEvicted: autoid.InformationSchemaDBID + 76, - TableAttributes: autoid.InformationSchemaDBID + 77, - TableTiDBHotRegionsHistory: autoid.InformationSchemaDBID + 78, - TablePlacementPolicies: autoid.InformationSchemaDBID + 79, - TableTrxSummary: autoid.InformationSchemaDBID + 80, - ClusterTableTrxSummary: autoid.InformationSchemaDBID + 81, - TableVariablesInfo: autoid.InformationSchemaDBID + 82, - TableUserAttributes: autoid.InformationSchemaDBID + 83, - TableMemoryUsage: autoid.InformationSchemaDBID + 84, - TableMemoryUsageOpsHistory: autoid.InformationSchemaDBID + 85, - ClusterTableMemoryUsage: autoid.InformationSchemaDBID + 86, - ClusterTableMemoryUsageOpsHistory: autoid.InformationSchemaDBID + 87, - TableResourceGroups: autoid.InformationSchemaDBID + 88, - TableRunawayWatches: autoid.InformationSchemaDBID + 89, - TableCheckConstraints: autoid.InformationSchemaDBID + 90, -} - -// columnInfo represents the basic column information of all kinds of INFORMATION_SCHEMA tables -type columnInfo struct { - // name of column - name string - // tp is column type - tp byte - // represent size of bytes of the column - size int - // represent decimal length of the column - decimal int - // flag represent NotNull, Unsigned, PriKey flags etc. - flag uint - // deflt is default value - deflt interface{} - // comment for the column - comment string - // enumElems represent all possible literal string values of an enum column - enumElems []string -} - -func buildColumnInfo(col columnInfo) *model.ColumnInfo { - mCharset := charset.CharsetBin - mCollation := charset.CharsetBin - if col.tp == mysql.TypeVarchar || col.tp == mysql.TypeBlob || col.tp == mysql.TypeLongBlob || col.tp == mysql.TypeEnum { - mCharset = charset.CharsetUTF8MB4 - mCollation = charset.CollationUTF8MB4 - } - fieldType := types.FieldType{} - fieldType.SetType(col.tp) - fieldType.SetCharset(mCharset) - fieldType.SetCollate(mCollation) - fieldType.SetFlen(col.size) - fieldType.SetDecimal(col.decimal) - fieldType.SetFlag(col.flag) - fieldType.SetElems(col.enumElems) - return &model.ColumnInfo{ - Name: model.NewCIStr(col.name), - FieldType: fieldType, - State: model.StatePublic, - DefaultValue: col.deflt, - Comment: col.comment, - } -} - -func buildTableMeta(tableName string, cs []columnInfo) *model.TableInfo { - cols := make([]*model.ColumnInfo, 0, len(cs)) - primaryIndices := make([]*model.IndexInfo, 0, 1) - tblInfo := &model.TableInfo{ - Name: model.NewCIStr(tableName), - State: model.StatePublic, - Charset: mysql.DefaultCharset, - Collate: mysql.DefaultCollationName, - } - for offset, c := range cs { - if tblInfo.Name.O == ClusterTableSlowLog && mysql.HasPriKeyFlag(c.flag) { - switch c.tp { - case mysql.TypeLong, mysql.TypeLonglong, - mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24: - tblInfo.PKIsHandle = true - default: - tblInfo.IsCommonHandle = true - tblInfo.CommonHandleVersion = 1 - index := &model.IndexInfo{ - Name: model.NewCIStr("primary"), - State: model.StatePublic, - Primary: true, - Unique: true, - Columns: []*model.IndexColumn{ - {Name: model.NewCIStr(c.name), Offset: offset, Length: types.UnspecifiedLength}}, - } - primaryIndices = append(primaryIndices, index) - tblInfo.Indices = primaryIndices - } - } - cols = append(cols, buildColumnInfo(c)) - } - for i, col := range cols { - col.Offset = i - } - tblInfo.Columns = cols - return tblInfo -} - -var schemataCols = []columnInfo{ - {name: "CATALOG_NAME", tp: mysql.TypeVarchar, size: 512}, - {name: "SCHEMA_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "DEFAULT_CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "DEFAULT_COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "SQL_PATH", tp: mysql.TypeVarchar, size: 512}, - {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, -} - -var tablesCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, - {name: "VERSION", tp: mysql.TypeLonglong, size: 21}, - {name: "ROW_FORMAT", tp: mysql.TypeVarchar, size: 10}, - {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, - {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, - {name: "AUTO_INCREMENT", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "UPDATE_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "CHECK_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "TABLE_COLLATION", tp: mysql.TypeVarchar, size: 32, deflt: mysql.DefaultCollationName}, - {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATE_OPTIONS", tp: mysql.TypeVarchar, size: 255}, - {name: "TABLE_COMMENT", tp: mysql.TypeVarchar, size: 2048}, - {name: "TIDB_TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "TIDB_ROW_ID_SHARDING_INFO", tp: mysql.TypeVarchar, size: 255}, - {name: "TIDB_PK_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, -} - -// See: http://dev.mysql.com/doc/refman/5.7/en/information-schema-columns-table.html -var columnsCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 64}, - {name: "COLUMN_DEFAULT", tp: mysql.TypeBlob, size: 196606}, - {name: "IS_NULLABLE", tp: mysql.TypeVarchar, size: 3}, - {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "NUMERIC_PRECISION", tp: mysql.TypeLonglong, size: 21}, - {name: "NUMERIC_SCALE", tp: mysql.TypeLonglong, size: 21}, - {name: "DATETIME_PRECISION", tp: mysql.TypeLonglong, size: 21}, - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "COLUMN_TYPE", tp: mysql.TypeBlob, size: 196606}, - {name: "COLUMN_KEY", tp: mysql.TypeVarchar, size: 3}, - {name: "EXTRA", tp: mysql.TypeVarchar, size: 30}, - {name: "PRIVILEGES", tp: mysql.TypeVarchar, size: 80}, - {name: "COLUMN_COMMENT", tp: mysql.TypeVarchar, size: 1024}, - {name: "GENERATION_EXPRESSION", tp: mysql.TypeBlob, size: 589779, flag: mysql.NotNullFlag}, -} - -var columnStatisticsCols = []columnInfo{ - {name: "SCHEMA_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "HISTOGRAM", tp: mysql.TypeJSON, size: 51}, -} - -var statisticsCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "NON_UNIQUE", tp: mysql.TypeVarchar, size: 1}, - {name: "INDEX_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "SEQ_IN_INDEX", tp: mysql.TypeLonglong, size: 2}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 21}, - {name: "COLLATION", tp: mysql.TypeVarchar, size: 1}, - {name: "CARDINALITY", tp: mysql.TypeLonglong, size: 21}, - {name: "SUB_PART", tp: mysql.TypeLonglong, size: 3}, - {name: "PACKED", tp: mysql.TypeVarchar, size: 10}, - {name: "NULLABLE", tp: mysql.TypeVarchar, size: 3}, - {name: "INDEX_TYPE", tp: mysql.TypeVarchar, size: 16}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 16}, - {name: "INDEX_COMMENT", tp: mysql.TypeVarchar, size: 1024}, - {name: "IS_VISIBLE", tp: mysql.TypeVarchar, size: 3}, - {name: "Expression", tp: mysql.TypeVarchar, size: 64}, -} - -var profilingCols = []columnInfo{ - {name: "QUERY_ID", tp: mysql.TypeLong, size: 20}, - {name: "SEQ", tp: mysql.TypeLong, size: 20}, - {name: "STATE", tp: mysql.TypeVarchar, size: 30}, - {name: "DURATION", tp: mysql.TypeNewDecimal, size: 9}, - {name: "CPU_USER", tp: mysql.TypeNewDecimal, size: 9}, - {name: "CPU_SYSTEM", tp: mysql.TypeNewDecimal, size: 9}, - {name: "CONTEXT_VOLUNTARY", tp: mysql.TypeLong, size: 20}, - {name: "CONTEXT_INVOLUNTARY", tp: mysql.TypeLong, size: 20}, - {name: "BLOCK_OPS_IN", tp: mysql.TypeLong, size: 20}, - {name: "BLOCK_OPS_OUT", tp: mysql.TypeLong, size: 20}, - {name: "MESSAGES_SENT", tp: mysql.TypeLong, size: 20}, - {name: "MESSAGES_RECEIVED", tp: mysql.TypeLong, size: 20}, - {name: "PAGE_FAULTS_MAJOR", tp: mysql.TypeLong, size: 20}, - {name: "PAGE_FAULTS_MINOR", tp: mysql.TypeLong, size: 20}, - {name: "SWAPS", tp: mysql.TypeLong, size: 20}, - {name: "SOURCE_FUNCTION", tp: mysql.TypeVarchar, size: 30}, - {name: "SOURCE_FILE", tp: mysql.TypeVarchar, size: 20}, - {name: "SOURCE_LINE", tp: mysql.TypeLong, size: 20}, -} - -var charsetCols = []columnInfo{ - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "DEFAULT_COLLATE_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "DESCRIPTION", tp: mysql.TypeVarchar, size: 60}, - {name: "MAXLEN", tp: mysql.TypeLonglong, size: 3}, -} - -var collationsCols = []columnInfo{ - {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, - {name: "ID", tp: mysql.TypeLonglong, size: 11}, - {name: "IS_DEFAULT", tp: mysql.TypeVarchar, size: 3}, - {name: "IS_COMPILED", tp: mysql.TypeVarchar, size: 3}, - {name: "SORTLEN", tp: mysql.TypeLonglong, size: 3}, -} - -var keyColumnUsageCols = []columnInfo{ - {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 10, flag: mysql.NotNullFlag}, - {name: "POSITION_IN_UNIQUE_CONSTRAINT", tp: mysql.TypeLonglong, size: 10}, - {name: "REFERENCED_TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "REFERENCED_TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "REFERENCED_COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, -} - -// See http://dev.mysql.com/doc/refman/5.7/en/information-schema-referential-constraints-table.html -var referConstCols = []columnInfo{ - {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "UNIQUE_CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "UNIQUE_CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "UNIQUE_CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "MATCH_OPTION", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "UPDATE_RULE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "DELETE_RULE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "REFERENCED_TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, -} - -// See http://dev.mysql.com/doc/refman/5.7/en/information-schema-variables-table.html -var sessionVarCols = []columnInfo{ - {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, -} - -// See https://dev.mysql.com/doc/refman/5.7/en/information-schema-plugins-table.html -var pluginsCols = []columnInfo{ - {name: "PLUGIN_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "PLUGIN_VERSION", tp: mysql.TypeVarchar, size: 20}, - {name: "PLUGIN_STATUS", tp: mysql.TypeVarchar, size: 10}, - {name: "PLUGIN_TYPE", tp: mysql.TypeVarchar, size: 80}, - {name: "PLUGIN_TYPE_VERSION", tp: mysql.TypeVarchar, size: 20}, - {name: "PLUGIN_LIBRARY", tp: mysql.TypeVarchar, size: 64}, - {name: "PLUGIN_LIBRARY_VERSION", tp: mysql.TypeVarchar, size: 20}, - {name: "PLUGIN_AUTHOR", tp: mysql.TypeVarchar, size: 64}, - {name: "PLUGIN_DESCRIPTION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "PLUGIN_LICENSE", tp: mysql.TypeVarchar, size: 80}, - {name: "LOAD_OPTION", tp: mysql.TypeVarchar, size: 64}, -} - -// See https://dev.mysql.com/doc/refman/5.7/en/information-schema-partitions-table.html -var partitionsCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "SUBPARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "PARTITION_ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 21}, - {name: "SUBPARTITION_ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 21}, - {name: "PARTITION_METHOD", tp: mysql.TypeVarchar, size: 18}, - {name: "SUBPARTITION_METHOD", tp: mysql.TypeVarchar, size: 12}, - {name: "PARTITION_EXPRESSION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "SUBPARTITION_EXPRESSION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "PARTITION_DESCRIPTION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, - {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATE_TIME", tp: mysql.TypeDatetime}, - {name: "UPDATE_TIME", tp: mysql.TypeDatetime}, - {name: "CHECK_TIME", tp: mysql.TypeDatetime}, - {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, - {name: "PARTITION_COMMENT", tp: mysql.TypeVarchar, size: 80}, - {name: "NODEGROUP", tp: mysql.TypeVarchar, size: 12}, - {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_PARTITION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, -} - -var tableConstraintsCols = []columnInfo{ - {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "CONSTRAINT_TYPE", tp: mysql.TypeVarchar, size: 64}, -} - -var tableTriggersCols = []columnInfo{ - {name: "TRIGGER_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "TRIGGER_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TRIGGER_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "EVENT_MANIPULATION", tp: mysql.TypeVarchar, size: 6}, - {name: "EVENT_OBJECT_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "EVENT_OBJECT_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "EVENT_OBJECT_TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "ACTION_ORDER", tp: mysql.TypeLonglong, size: 4}, - {name: "ACTION_CONDITION", tp: mysql.TypeBlob, size: -1}, - {name: "ACTION_STATEMENT", tp: mysql.TypeBlob, size: -1}, - {name: "ACTION_ORIENTATION", tp: mysql.TypeVarchar, size: 9}, - {name: "ACTION_TIMING", tp: mysql.TypeVarchar, size: 6}, - {name: "ACTION_REFERENCE_OLD_TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "ACTION_REFERENCE_NEW_TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "ACTION_REFERENCE_OLD_ROW", tp: mysql.TypeVarchar, size: 3}, - {name: "ACTION_REFERENCE_NEW_ROW", tp: mysql.TypeVarchar, size: 3}, - {name: "CREATED", tp: mysql.TypeDatetime, size: 2}, - {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192}, - {name: "DEFINER", tp: mysql.TypeVarchar, size: 77}, - {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32}, - {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32}, - {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32}, -} - -var tableUserPrivilegesCols = []columnInfo{ - {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, - {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3}, -} - -var tableSchemaPrivilegesCols = []columnInfo{ - {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, -} - -var tableTablePrivilegesCols = []columnInfo{ - {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, -} - -var tableColumnPrivilegesCols = []columnInfo{ - {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, -} - -var tableEnginesCols = []columnInfo{ - {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, - {name: "SUPPORT", tp: mysql.TypeVarchar, size: 8}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 80}, - {name: "TRANSACTIONS", tp: mysql.TypeVarchar, size: 3}, - {name: "XA", tp: mysql.TypeVarchar, size: 3}, - {name: "SAVEPOINTS", tp: mysql.TypeVarchar, size: 3}, -} - -var tableViewsCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "VIEW_DEFINITION", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag}, - {name: "CHECK_OPTION", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, - {name: "IS_UPDATABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, - {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, - {name: "SECURITY_TYPE", tp: mysql.TypeVarchar, size: 7, flag: mysql.NotNullFlag}, - {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, -} - -var tableRoutinesCols = []columnInfo{ - {name: "SPECIFIC_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ROUTINE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "ROUTINE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ROUTINE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ROUTINE_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, - {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeLong, size: 21}, - {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeLong, size: 21}, - {name: "NUMERIC_PRECISION", tp: mysql.TypeLonglong, size: 21}, - {name: "NUMERIC_SCALE", tp: mysql.TypeLong, size: 21}, - {name: "DATETIME_PRECISION", tp: mysql.TypeLonglong, size: 21}, - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "DTD_IDENTIFIER", tp: mysql.TypeLongBlob}, - {name: "ROUTINE_BODY", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, - {name: "ROUTINE_DEFINITION", tp: mysql.TypeLongBlob}, - {name: "EXTERNAL_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "EXTERNAL_LANGUAGE", tp: mysql.TypeVarchar, size: 64}, - {name: "PARAMETER_STYLE", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, - {name: "IS_DETERMINISTIC", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, - {name: "SQL_DATA_ACCESS", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "SQL_PATH", tp: mysql.TypeVarchar, size: 64}, - {name: "SECURITY_TYPE", tp: mysql.TypeVarchar, size: 7, flag: mysql.NotNullFlag}, - {name: "CREATED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, - {name: "LAST_ALTERED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, - {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192, flag: mysql.NotNullFlag}, - {name: "ROUTINE_COMMENT", tp: mysql.TypeLongBlob}, - {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, - {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, -} - -var tableParametersCols = []columnInfo{ - {name: "SPECIFIC_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "SPECIFIC_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "SPECIFIC_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ORDINAL_POSITION", tp: mysql.TypeVarchar, size: 21, flag: mysql.NotNullFlag}, - {name: "PARAMETER_MODE", tp: mysql.TypeVarchar, size: 5}, - {name: "PARAMETER_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeVarchar, size: 21}, - {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeVarchar, size: 21}, - {name: "NUMERIC_PRECISION", tp: mysql.TypeVarchar, size: 21}, - {name: "NUMERIC_SCALE", tp: mysql.TypeVarchar, size: 21}, - {name: "DATETIME_PRECISION", tp: mysql.TypeVarchar, size: 21}, - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "DTD_IDENTIFIER", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag}, - {name: "ROUTINE_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, -} - -var tableEventsCols = []columnInfo{ - {name: "EVENT_CATALOG", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "EVENT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "EVENT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, - {name: "TIME_ZONE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "EVENT_BODY", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, - {name: "EVENT_DEFINITION", tp: mysql.TypeLongBlob}, - {name: "EVENT_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, - {name: "EXECUTE_AT", tp: mysql.TypeDatetime}, - {name: "INTERVAL_VALUE", tp: mysql.TypeVarchar, size: 256}, - {name: "INTERVAL_FIELD", tp: mysql.TypeVarchar, size: 18}, - {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192, flag: mysql.NotNullFlag}, - {name: "STARTS", tp: mysql.TypeDatetime}, - {name: "ENDS", tp: mysql.TypeDatetime}, - {name: "STATUS", tp: mysql.TypeVarchar, size: 18, flag: mysql.NotNullFlag}, - {name: "ON_COMPLETION", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, - {name: "CREATED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, - {name: "LAST_ALTERED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, - {name: "LAST_EXECUTED", tp: mysql.TypeDatetime}, - {name: "EVENT_COMMENT", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ORIGINATOR", tp: mysql.TypeLong, size: 10, flag: mysql.NotNullFlag, deflt: 0}, - {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, -} - -var tableGlobalStatusCols = []columnInfo{ - {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, -} - -var tableGlobalVariablesCols = []columnInfo{ - {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, -} - -var tableSessionStatusCols = []columnInfo{ - {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, -} - -var tableOptimizerTraceCols = []columnInfo{ - {name: "QUERY", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag, deflt: ""}, - {name: "TRACE", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag, deflt: ""}, - {name: "MISSING_BYTES_BEYOND_MAX_MEM_SIZE", tp: mysql.TypeShort, size: 20, flag: mysql.NotNullFlag, deflt: 0}, - {name: "INSUFFICIENT_PRIVILEGES", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, -} - -var tableTableSpacesCols = []columnInfo{ - {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, - {name: "ENGINE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, - {name: "TABLESPACE_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "LOGFILE_GROUP_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "EXTENT_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "AUTOEXTEND_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "MAXIMUM_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "NODEGROUP_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "TABLESPACE_COMMENT", tp: mysql.TypeVarchar, size: 2048}, -} - -var tableCollationCharacterSetApplicabilityCols = []columnInfo{ - {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, -} - -var tableProcesslistCols = []columnInfo{ - {name: "ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, deflt: 0}, - {name: "USER", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, deflt: ""}, - {name: "HOST", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, - {name: "DB", tp: mysql.TypeVarchar, size: 64}, - {name: "COMMAND", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, deflt: ""}, - {name: "TIME", tp: mysql.TypeLong, size: 7, flag: mysql.NotNullFlag, deflt: 0}, - {name: "STATE", tp: mysql.TypeVarchar, size: 7}, - {name: "INFO", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "DIGEST", tp: mysql.TypeVarchar, size: 64, deflt: ""}, - {name: "MEM", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, - {name: "DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, - {name: "TxnStart", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, - {name: "RESOURCE_GROUP", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag, deflt: ""}, - {name: "SESSION_ALIAS", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, -} - -var tableTiDBIndexesCols = []columnInfo{ - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "NON_UNIQUE", tp: mysql.TypeLonglong, size: 21}, - {name: "KEY_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "SEQ_IN_INDEX", tp: mysql.TypeLonglong, size: 21}, - {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "SUB_PART", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_COMMENT", tp: mysql.TypeVarchar, size: 1024}, - {name: "Expression", tp: mysql.TypeVarchar, size: 64}, - {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "IS_VISIBLE", tp: mysql.TypeVarchar, size: 64}, - {name: "CLUSTERED", tp: mysql.TypeVarchar, size: 64}, -} - -var slowQueryCols = []columnInfo{ - {name: variable.SlowLogTimeStr, tp: mysql.TypeTimestamp, size: 26, decimal: 6, flag: mysql.PriKeyFlag | mysql.NotNullFlag | mysql.BinaryFlag}, - {name: variable.SlowLogTxnStartTSStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: variable.SlowLogUserStr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogHostStr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogConnIDStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: variable.SlowLogSessAliasStr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogExecRetryCount, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: variable.SlowLogExecRetryTime, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogQueryTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogParseTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCompileTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogRewriteTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogPreprocSubQueriesStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: variable.SlowLogPreProcSubQueryTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogOptimizeTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogWaitTSTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.PreWriteTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.WaitPrewriteBinlogTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.CommitTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.GetCommitTSTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.CommitBackoffTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.BackoffTypesStr, tp: mysql.TypeVarchar, size: 64}, - {name: execdetails.ResolveLockTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.LocalLatchWaitTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.WriteKeysStr, tp: mysql.TypeLonglong, size: 22}, - {name: execdetails.WriteSizeStr, tp: mysql.TypeLonglong, size: 22}, - {name: execdetails.PrewriteRegionStr, tp: mysql.TypeLonglong, size: 22}, - {name: execdetails.TxnRetryStr, tp: mysql.TypeLonglong, size: 22}, - {name: execdetails.CopTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.ProcessTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.WaitTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.BackoffTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.LockKeysTimeStr, tp: mysql.TypeDouble, size: 22}, - {name: execdetails.RequestCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.TotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.ProcessKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.RocksdbDeleteSkippedCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.RocksdbKeySkippedCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.RocksdbBlockCacheHitCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.RocksdbBlockReadCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: execdetails.RocksdbBlockReadByteStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, - {name: variable.SlowLogDBStr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogIndexNamesStr, tp: mysql.TypeVarchar, size: 100}, - {name: variable.SlowLogIsInternalStr, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogDigestStr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogStatsInfoStr, tp: mysql.TypeVarchar, size: 512}, - {name: variable.SlowLogCopProcAvg, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopProcP90, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopProcMax, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopProcAddr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogCopWaitAvg, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopWaitP90, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopWaitMax, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogCopWaitAddr, tp: mysql.TypeVarchar, size: 64}, - {name: variable.SlowLogMemMax, tp: mysql.TypeLonglong, size: 20}, - {name: variable.SlowLogDiskMax, tp: mysql.TypeLonglong, size: 20}, - {name: variable.SlowLogKVTotal, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogPDTotal, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogBackoffTotal, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogWriteSQLRespTotal, tp: mysql.TypeDouble, size: 22}, - {name: variable.SlowLogResultRows, tp: mysql.TypeLonglong, size: 22}, - {name: variable.SlowLogWarnings, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: variable.SlowLogBackoffDetail, tp: mysql.TypeVarchar, size: 4096}, - {name: variable.SlowLogPrepared, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogSucc, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogIsExplicitTxn, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogIsWriteCacheTable, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogPlanFromCache, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogPlanFromBinding, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogHasMoreResults, tp: mysql.TypeTiny, size: 1}, - {name: variable.SlowLogPlan, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: variable.SlowLogPlanDigest, tp: mysql.TypeVarchar, size: 128}, - {name: variable.SlowLogBinaryPlan, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: variable.SlowLogPrevStmt, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: variable.SlowLogQuerySQLStr, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, -} - -// TableTiDBHotRegionsCols is TiDB hot region mem table columns. -var TableTiDBHotRegionsCols = []columnInfo{ - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "MAX_HOT_DEGREE", tp: mysql.TypeLonglong, size: 21}, - {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21}, - {name: "FLOW_BYTES", tp: mysql.TypeLonglong, size: 21}, -} - -// TableTiDBHotRegionsHistoryCols is TiDB hot region history mem table columns. -var TableTiDBHotRegionsHistoryCols = []columnInfo{ - {name: "UPDATE_TIME", tp: mysql.TypeTimestamp, size: 26, decimal: 6}, - {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "PEER_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "IS_LEARNER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "IS_LEADER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "HOT_DEGREE", tp: mysql.TypeLonglong, size: 21}, - {name: "FLOW_BYTES", tp: mysql.TypeDouble, size: 22}, - {name: "KEY_RATE", tp: mysql.TypeDouble, size: 22}, - {name: "QUERY_RATE", tp: mysql.TypeDouble, size: 22}, -} - -// GetTableTiDBHotRegionsHistoryCols is to get TableTiDBHotRegionsHistoryCols. -// It is an optimization because Go does’t support const arrays. The solution is to use initialization functions. -// It is useful in the BCE optimization. -// https://go101.org/article/bounds-check-elimination.html -func GetTableTiDBHotRegionsHistoryCols() []columnInfo { - return TableTiDBHotRegionsHistoryCols -} - -// TableTiKVStoreStatusCols is TiDB kv store status columns. -var TableTiKVStoreStatusCols = []columnInfo{ - {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "ADDRESS", tp: mysql.TypeVarchar, size: 64}, - {name: "STORE_STATE", tp: mysql.TypeLonglong, size: 21}, - {name: "STORE_STATE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "LABEL", tp: mysql.TypeJSON, size: 51}, - {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, - {name: "CAPACITY", tp: mysql.TypeVarchar, size: 64}, - {name: "AVAILABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "LEADER_COUNT", tp: mysql.TypeLonglong, size: 21}, - {name: "LEADER_WEIGHT", tp: mysql.TypeDouble, size: 22}, - {name: "LEADER_SCORE", tp: mysql.TypeDouble, size: 22}, - {name: "LEADER_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21}, - {name: "REGION_WEIGHT", tp: mysql.TypeDouble, size: 22}, - {name: "REGION_SCORE", tp: mysql.TypeDouble, size: 22}, - {name: "REGION_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "START_TS", tp: mysql.TypeDatetime}, - {name: "LAST_HEARTBEAT_TS", tp: mysql.TypeDatetime}, - {name: "UPTIME", tp: mysql.TypeVarchar, size: 64}, -} - -var tableAnalyzeStatusCols = []columnInfo{ - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "JOB_INFO", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "PROCESSED_ROWS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, - {name: "START_TIME", tp: mysql.TypeDatetime}, - {name: "END_TIME", tp: mysql.TypeDatetime}, - {name: "STATE", tp: mysql.TypeVarchar, size: 64}, - {name: "FAIL_REASON", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 512}, - {name: "PROCESS_ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, - {name: "REMAINING_SECONDS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, - {name: "PROGRESS", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "ESTIMATED_TOTAL_ROWS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, -} - -// TableTiKVRegionStatusCols is TiKV region status mem table columns. -var TableTiKVRegionStatusCols = []columnInfo{ - {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "START_KEY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, - {name: "END_KEY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "IS_INDEX", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "IS_PARTITION", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "PARTITION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "EPOCH_CONF_VER", tp: mysql.TypeLonglong, size: 21}, - {name: "EPOCH_VERSION", tp: mysql.TypeLonglong, size: 21}, - {name: "WRITTEN_BYTES", tp: mysql.TypeLonglong, size: 21}, - {name: "READ_BYTES", tp: mysql.TypeLonglong, size: 21}, - {name: "APPROXIMATE_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "APPROXIMATE_KEYS", tp: mysql.TypeLonglong, size: 21}, - {name: "REPLICATIONSTATUS_STATE", tp: mysql.TypeVarchar, size: 64}, - {name: "REPLICATIONSTATUS_STATEID", tp: mysql.TypeLonglong, size: 21}, -} - -// TableTiKVRegionPeersCols is TiKV region peers mem table columns. -var TableTiKVRegionPeersCols = []columnInfo{ - {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "PEER_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "IS_LEARNER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "IS_LEADER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, - {name: "STATUS", tp: mysql.TypeVarchar, size: 10, deflt: 0}, - {name: "DOWN_SECONDS", tp: mysql.TypeLonglong, size: 21, deflt: 0}, -} - -// GetTableTiKVRegionPeersCols is to get TableTiKVRegionPeersCols. -// It is an optimization because Go does’t support const arrays. The solution is to use initialization functions. -// It is useful in the BCE optimization. -// https://go101.org/article/bounds-check-elimination.html -func GetTableTiKVRegionPeersCols() []columnInfo { - return TableTiKVRegionPeersCols -} - -var tableTiDBServersInfoCols = []columnInfo{ - {name: "DDL_ID", tp: mysql.TypeVarchar, size: 64}, - {name: "IP", tp: mysql.TypeVarchar, size: 64}, - {name: "PORT", tp: mysql.TypeLonglong, size: 21}, - {name: "STATUS_PORT", tp: mysql.TypeLonglong, size: 21}, - {name: "LEASE", tp: mysql.TypeVarchar, size: 64}, - {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, - {name: "GIT_HASH", tp: mysql.TypeVarchar, size: 64}, - {name: "BINLOG_STATUS", tp: mysql.TypeVarchar, size: 64}, - {name: "LABELS", tp: mysql.TypeVarchar, size: 128}, -} - -var tableClusterConfigCols = []columnInfo{ - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "KEY", tp: mysql.TypeVarchar, size: 256}, - {name: "VALUE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, -} - -var tableClusterLogCols = []columnInfo{ - {name: "TIME", tp: mysql.TypeVarchar, size: 32}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "LEVEL", tp: mysql.TypeVarchar, size: 8}, - {name: "MESSAGE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, -} - -var tableClusterLoadCols = []columnInfo{ - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "DEVICE_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "DEVICE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "NAME", tp: mysql.TypeVarchar, size: 256}, - {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, -} - -var tableClusterHardwareCols = []columnInfo{ - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "DEVICE_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "DEVICE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "NAME", tp: mysql.TypeVarchar, size: 256}, - {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, -} - -var tableClusterSystemInfoCols = []columnInfo{ - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "SYSTEM_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "SYSTEM_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "NAME", tp: mysql.TypeVarchar, size: 256}, - {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, -} - -var filesCols = []columnInfo{ - {name: "FILE_ID", tp: mysql.TypeLonglong, size: 4}, - {name: "FILE_NAME", tp: mysql.TypeVarchar, size: 4000}, - {name: "FILE_TYPE", tp: mysql.TypeVarchar, size: 20}, - {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "LOGFILE_GROUP_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "LOGFILE_GROUP_NUMBER", tp: mysql.TypeLonglong, size: 32}, - {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, - {name: "FULLTEXT_KEYS", tp: mysql.TypeVarchar, size: 64}, - {name: "DELETED_ROWS", tp: mysql.TypeLonglong, size: 4}, - {name: "UPDATE_COUNT", tp: mysql.TypeLonglong, size: 4}, - {name: "FREE_EXTENTS", tp: mysql.TypeLonglong, size: 4}, - {name: "TOTAL_EXTENTS", tp: mysql.TypeLonglong, size: 4}, - {name: "EXTENT_SIZE", tp: mysql.TypeLonglong, size: 4}, - {name: "INITIAL_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "MAXIMUM_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "AUTOEXTEND_SIZE", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATION_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "LAST_UPDATE_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "LAST_ACCESS_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "RECOVER_TIME", tp: mysql.TypeLonglong, size: 4}, - {name: "TRANSACTION_COUNTER", tp: mysql.TypeLonglong, size: 4}, - {name: "VERSION", tp: mysql.TypeLonglong, size: 21}, - {name: "ROW_FORMAT", tp: mysql.TypeVarchar, size: 10}, - {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, - {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, - {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "UPDATE_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "CHECK_TIME", tp: mysql.TypeDatetime, size: -1}, - {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, - {name: "STATUS", tp: mysql.TypeVarchar, size: 20}, - {name: "EXTRA", tp: mysql.TypeVarchar, size: 255}, -} - -var tableClusterInfoCols = []columnInfo{ - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "STATUS_ADDRESS", tp: mysql.TypeVarchar, size: 64}, - {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, - {name: "GIT_HASH", tp: mysql.TypeVarchar, size: 64}, - {name: "START_TIME", tp: mysql.TypeVarchar, size: 32}, - {name: "UPTIME", tp: mysql.TypeVarchar, size: 32}, - {name: "SERVER_ID", tp: mysql.TypeLonglong, size: 21, comment: "invalid if the configuration item `enable-global-kill` is set to FALSE"}, -} - -var tableTableTiFlashReplicaCols = []columnInfo{ - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "REPLICA_COUNT", tp: mysql.TypeLonglong, size: 64}, - {name: "LOCATION_LABELS", tp: mysql.TypeVarchar, size: 64}, - {name: "AVAILABLE", tp: mysql.TypeTiny, size: 1}, - {name: "PROGRESS", tp: mysql.TypeDouble, size: 22}, -} - -var tableInspectionResultCols = []columnInfo{ - {name: "RULE", tp: mysql.TypeVarchar, size: 64}, - {name: "ITEM", tp: mysql.TypeVarchar, size: 64}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "STATUS_ADDRESS", tp: mysql.TypeVarchar, size: 64}, - {name: "VALUE", tp: mysql.TypeVarchar, size: 64}, - {name: "REFERENCE", tp: mysql.TypeVarchar, size: 64}, - {name: "SEVERITY", tp: mysql.TypeVarchar, size: 64}, - {name: "DETAILS", tp: mysql.TypeVarchar, size: 256}, -} - -var tableInspectionSummaryCols = []columnInfo{ - {name: "RULE", tp: mysql.TypeVarchar, size: 64}, - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "LABEL", tp: mysql.TypeVarchar, size: 64}, - {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, - {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableInspectionRulesCols = []columnInfo{ - {name: "NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableMetricTablesCols = []columnInfo{ - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "PROMQL", tp: mysql.TypeVarchar, size: 64}, - {name: "LABELS", tp: mysql.TypeVarchar, size: 64}, - {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableMetricSummaryCols = []columnInfo{ - {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, - {name: "SUM_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableMetricSummaryByLabelCols = []columnInfo{ - {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, - {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "LABEL", tp: mysql.TypeVarchar, size: 64}, - {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, - {name: "SUM_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableDDLJobsCols = []columnInfo{ - {name: "JOB_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "JOB_TYPE", tp: mysql.TypeVarchar, size: 64}, - {name: "SCHEMA_STATE", tp: mysql.TypeVarchar, size: 64}, - {name: "SCHEMA_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "ROW_COUNT", tp: mysql.TypeLonglong, size: 21}, - {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "START_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "END_TIME", tp: mysql.TypeDatetime, size: 19}, - {name: "STATE", tp: mysql.TypeVarchar, size: 64}, - {name: "QUERY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, -} - -var tableSequencesCols = []columnInfo{ - {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "SEQUENCE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "SEQUENCE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CACHE", tp: mysql.TypeTiny, flag: mysql.NotNullFlag}, - {name: "CACHE_VALUE", tp: mysql.TypeLonglong, size: 21}, - {name: "CYCLE", tp: mysql.TypeTiny, flag: mysql.NotNullFlag}, - {name: "INCREMENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "MAX_VALUE", tp: mysql.TypeLonglong, size: 21}, - {name: "MIN_VALUE", tp: mysql.TypeLonglong, size: 21}, - {name: "START", tp: mysql.TypeLonglong, size: 21}, - {name: "COMMENT", tp: mysql.TypeVarchar, size: 64}, -} - -var tableStatementsSummaryCols = []columnInfo{ - {name: stmtsummary.SummaryBeginTimeStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "Begin time of this summary"}, - {name: stmtsummary.SummaryEndTimeStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "End time of this summary"}, - {name: stmtsummary.StmtTypeStr, tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, comment: "Statement type"}, - {name: stmtsummary.SchemaNameStr, tp: mysql.TypeVarchar, size: 64, comment: "Current schema"}, - {name: stmtsummary.DigestStr, tp: mysql.TypeVarchar, size: 64}, - {name: stmtsummary.DigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag, comment: "Normalized statement"}, - {name: stmtsummary.TableNamesStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Involved tables"}, - {name: stmtsummary.IndexNamesStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Used indices"}, - {name: stmtsummary.SampleUserStr, tp: mysql.TypeVarchar, size: 64, comment: "Sampled user who executed these statements"}, - {name: stmtsummary.ExecCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Count of executions"}, - {name: stmtsummary.SumErrorsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of errors"}, - {name: stmtsummary.SumWarningsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of warnings"}, - {name: stmtsummary.SumLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum latency of these statements"}, - {name: stmtsummary.MaxLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of these statements"}, - {name: stmtsummary.MinLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Min latency of these statements"}, - {name: stmtsummary.AvgLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of these statements"}, - {name: stmtsummary.AvgParseLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of parsing"}, - {name: stmtsummary.MaxParseLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of parsing"}, - {name: stmtsummary.AvgCompileLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of compiling"}, - {name: stmtsummary.MaxCompileLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of compiling"}, - {name: stmtsummary.SumCopTaskNumStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Total number of CopTasks"}, - {name: stmtsummary.MaxCopProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max processing time of CopTasks"}, - {name: stmtsummary.MaxCopProcessAddressStr, tp: mysql.TypeVarchar, size: 256, comment: "Address of the CopTask with max processing time"}, - {name: stmtsummary.MaxCopWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time of CopTasks"}, - {name: stmtsummary.MaxCopWaitAddressStr, tp: mysql.TypeVarchar, size: 256, comment: "Address of the CopTask with max waiting time"}, - {name: stmtsummary.AvgProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average processing time in TiKV"}, - {name: stmtsummary.MaxProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max processing time in TiKV"}, - {name: stmtsummary.AvgWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time in TiKV"}, - {name: stmtsummary.MaxWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time in TiKV"}, - {name: stmtsummary.AvgBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time before retry"}, - {name: stmtsummary.MaxBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time before retry"}, - {name: stmtsummary.AvgTotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of scanned keys"}, - {name: stmtsummary.MaxTotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of scanned keys"}, - {name: stmtsummary.AvgProcessedKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of processed keys"}, - {name: stmtsummary.MaxProcessedKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of processed keys"}, - {name: stmtsummary.AvgRocksdbDeleteSkippedCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb delete skipped count"}, - {name: stmtsummary.MaxRocksdbDeleteSkippedCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb delete skipped count"}, - {name: stmtsummary.AvgRocksdbKeySkippedCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb key skipped count"}, - {name: stmtsummary.MaxRocksdbKeySkippedCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb key skipped count"}, - {name: stmtsummary.AvgRocksdbBlockCacheHitCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block cache hit count"}, - {name: stmtsummary.MaxRocksdbBlockCacheHitCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block cache hit count"}, - {name: stmtsummary.AvgRocksdbBlockReadCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block read count"}, - {name: stmtsummary.MaxRocksdbBlockReadCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block read count"}, - {name: stmtsummary.AvgRocksdbBlockReadByteStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block read byte"}, - {name: stmtsummary.MaxRocksdbBlockReadByteStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block read byte"}, - {name: stmtsummary.AvgPrewriteTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of prewrite phase"}, - {name: stmtsummary.MaxPrewriteTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of prewrite phase"}, - {name: stmtsummary.AvgCommitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of commit phase"}, - {name: stmtsummary.MaxCommitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of commit phase"}, - {name: stmtsummary.AvgGetCommitTsTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of getting commit_ts"}, - {name: stmtsummary.MaxGetCommitTsTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of getting commit_ts"}, - {name: stmtsummary.AvgCommitBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time before retry during commit phase"}, - {name: stmtsummary.MaxCommitBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time before retry during commit phase"}, - {name: stmtsummary.AvgResolveLockTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time for resolving locks"}, - {name: stmtsummary.MaxResolveLockTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time for resolving locks"}, - {name: stmtsummary.AvgLocalLatchWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time of local transaction"}, - {name: stmtsummary.MaxLocalLatchWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time of local transaction"}, - {name: stmtsummary.AvgWriteKeysStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average count of written keys"}, - {name: stmtsummary.MaxWriteKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max count of written keys"}, - {name: stmtsummary.AvgWriteSizeStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average amount of written bytes"}, - {name: stmtsummary.MaxWriteSizeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max amount of written bytes"}, - {name: stmtsummary.AvgPrewriteRegionsStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of involved regions in prewrite phase"}, - {name: stmtsummary.MaxPrewriteRegionsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of involved regions in prewrite phase"}, - {name: stmtsummary.AvgTxnRetryStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of transaction retries"}, - {name: stmtsummary.MaxTxnRetryStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of transaction retries"}, - {name: stmtsummary.SumExecRetryStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum number of execution retries in pessimistic transactions"}, - {name: stmtsummary.SumExecRetryTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum time of execution retries in pessimistic transactions"}, - {name: stmtsummary.SumBackoffTimesStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of retries"}, - {name: stmtsummary.BackoffTypesStr, tp: mysql.TypeVarchar, size: 1024, comment: "Types of errors and the number of retries for each type"}, - {name: stmtsummary.AvgMemStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average memory(byte) used"}, - {name: stmtsummary.MaxMemStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max memory(byte) used"}, - {name: stmtsummary.AvgDiskStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average disk space(byte) used"}, - {name: stmtsummary.MaxDiskStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max disk space(byte) used"}, - {name: stmtsummary.AvgKvTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of TiKV used"}, - {name: stmtsummary.AvgPdTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of PD used"}, - {name: stmtsummary.AvgBackoffTotalTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of Backoff used"}, - {name: stmtsummary.AvgWriteSQLRespTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of write sql resp used"}, - {name: stmtsummary.MaxResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Max count of sql result rows"}, - {name: stmtsummary.MinResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Min count of sql result rows"}, - {name: stmtsummary.AvgResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Average count of sql result rows"}, - {name: stmtsummary.PreparedStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether prepared"}, - {name: stmtsummary.AvgAffectedRowsStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rows affected"}, - {name: stmtsummary.FirstSeenStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the first time"}, - {name: stmtsummary.LastSeenStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the last time"}, - {name: stmtsummary.PlanInCacheStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement hit plan cache"}, - {name: stmtsummary.PlanCacheHitsStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag, comment: "The number of times these statements hit plan cache"}, - {name: stmtsummary.PlanInBindingStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement is matched with the hints in the binding"}, - {name: stmtsummary.QuerySampleTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled original statement"}, - {name: stmtsummary.PrevSampleTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The previous statement before commit"}, - {name: stmtsummary.PlanDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "Digest of its execution plan"}, - {name: stmtsummary.PlanStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled execution plan"}, - {name: stmtsummary.BinaryPlan, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled binary plan"}, - {name: stmtsummary.Charset, tp: mysql.TypeVarchar, size: 64, comment: "Sampled charset"}, - {name: stmtsummary.Collation, tp: mysql.TypeVarchar, size: 64, comment: "Sampled collation"}, - {name: stmtsummary.PlanHint, tp: mysql.TypeVarchar, size: 64, comment: "Sampled plan hint"}, -} - -var tableStorageStatsCols = []columnInfo{ - {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, - {name: "PEER_COUNT", tp: mysql.TypeLonglong, size: 21}, - {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21, comment: "The region count of single replica of the table"}, - {name: "EMPTY_REGION_COUNT", tp: mysql.TypeLonglong, size: 21, comment: "The region count of single replica of the table"}, - {name: "TABLE_SIZE", tp: mysql.TypeLonglong, size: 64, comment: "The disk usage(MB) of single replica of the table, if the table size is empty or less than 1MB, it would show 1MB "}, - {name: "TABLE_KEYS", tp: mysql.TypeLonglong, size: 64, comment: "The count of keys of single replica of the table"}, -} - -var tableTableTiFlashTablesCols = []columnInfo{ - {name: "DATABASE", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_DATABASE", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "IS_TOMBSTONE", tp: mysql.TypeLonglong, size: 64}, - {name: "SEGMENT_COUNT", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_RATE_ROWS", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_RATE_SEGMENTS", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_PLACED_RATE", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_CACHE_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_CACHE_RATE", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_CACHE_WASTED_RATE", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_INDEX_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "AVG_SEGMENT_ROWS", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_SEGMENT_SIZE", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_COUNT", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_DELTA_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_DELTA_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "AVG_DELTA_ROWS", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_DELTA_SIZE", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_DELTA_DELETE_RANGES", tp: mysql.TypeDouble, size: 64}, - {name: "STABLE_COUNT", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_STABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_STABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "TOTAL_STABLE_SIZE_ON_DISK", tp: mysql.TypeLonglong, size: 64}, - {name: "AVG_STABLE_ROWS", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_STABLE_SIZE", tp: mysql.TypeDouble, size: 64}, - {name: "TOTAL_PACK_COUNT_IN_DELTA", tp: mysql.TypeLonglong, size: 64}, - {name: "MAX_PACK_COUNT_IN_DELTA", tp: mysql.TypeLonglong, size: 64}, - {name: "AVG_PACK_COUNT_IN_DELTA", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_PACK_ROWS_IN_DELTA", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_PACK_SIZE_IN_DELTA", tp: mysql.TypeDouble, size: 64}, - {name: "TOTAL_PACK_COUNT_IN_STABLE", tp: mysql.TypeLonglong, size: 64}, - {name: "AVG_PACK_COUNT_IN_STABLE", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_PACK_ROWS_IN_STABLE", tp: mysql.TypeDouble, size: 64}, - {name: "AVG_PACK_SIZE_IN_STABLE", tp: mysql.TypeDouble, size: 64}, - {name: "STORAGE_STABLE_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, - {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, - {name: "STORAGE_DELTA_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, - {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, - {name: "STORAGE_META_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_META_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, - {name: "STORAGE_META_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "STORAGE_META_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, - {name: "BACKGROUND_TASKS_LENGTH", tp: mysql.TypeLonglong, size: 64}, - {name: "TIFLASH_INSTANCE", tp: mysql.TypeVarchar, size: 64}, -} - -var tableTableTiFlashSegmentsCols = []columnInfo{ - {name: "DATABASE", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_DATABASE", tp: mysql.TypeVarchar, size: 64}, - {name: "TIDB_TABLE", tp: mysql.TypeVarchar, size: 64}, - {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "IS_TOMBSTONE", tp: mysql.TypeLonglong, size: 64}, - {name: "SEGMENT_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "RANGE", tp: mysql.TypeVarchar, size: 64}, - {name: "EPOCH", tp: mysql.TypeLonglong, size: 64}, - {name: "ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_RATE", tp: mysql.TypeDouble, size: 64}, - {name: "DELTA_MEMTABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_MEMTABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_MEMTABLE_COLUMN_FILES", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_MEMTABLE_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_PERSISTED_PAGE_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_PERSISTED_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_PERSISTED_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_PERSISTED_COLUMN_FILES", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_PERSISTED_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_CACHE_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "DELTA_INDEX_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_PAGE_ID", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES_ID_0", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES_ROWS", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES_SIZE", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES_SIZE_ON_DISK", tp: mysql.TypeLonglong, size: 64}, - {name: "STABLE_DMFILES_PACKS", tp: mysql.TypeLonglong, size: 64}, - {name: "TIFLASH_INSTANCE", tp: mysql.TypeVarchar, size: 64}, -} - -var tableClientErrorsSummaryGlobalCols = []columnInfo{ - {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, - {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, - {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, -} - -var tableClientErrorsSummaryByUserCols = []columnInfo{ - {name: "USER", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, - {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, - {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, -} - -var tableClientErrorsSummaryByHostCols = []columnInfo{ - {name: "HOST", tp: mysql.TypeVarchar, size: 255, flag: mysql.NotNullFlag}, - {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, - {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, - {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, -} - -var tableTiDBTrxCols = []columnInfo{ - {name: txninfo.IDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag}, - {name: txninfo.StartTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "Start time of the transaction"}, - {name: txninfo.CurrentSQLDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "Digest of the sql the transaction are currently running"}, - {name: txninfo.CurrentSQLDigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The normalized sql the transaction are currently running"}, - {name: txninfo.StateStr, tp: mysql.TypeEnum, enumElems: txninfo.TxnRunningStateStrs, comment: "Current running state of the transaction"}, - {name: txninfo.WaitingStartTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "Current lock waiting's start time"}, - {name: txninfo.MemBufferKeysStr, tp: mysql.TypeLonglong, size: 64, comment: "How many entries are in MemDB"}, - {name: txninfo.MemBufferBytesStr, tp: mysql.TypeLonglong, size: 64, comment: "MemDB used memory"}, - {name: txninfo.SessionIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag, comment: "Which session this transaction belongs to"}, - {name: txninfo.UserStr, tp: mysql.TypeVarchar, size: 16, comment: "The user who open this session"}, - {name: txninfo.DBStr, tp: mysql.TypeVarchar, size: 64, comment: "The schema this transaction works on"}, - {name: txninfo.AllSQLDigestsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the digests of SQL statements that the transaction has executed"}, - {name: txninfo.RelatedTableIDsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the table IDs that the transaction has accessed"}, - {name: txninfo.WaitingTimeStr, tp: mysql.TypeDouble, size: 22, comment: "Current lock waiting time"}, -} - -var tableDeadlocksCols = []columnInfo{ - {name: deadlockhistory.ColDeadlockIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag, comment: "The ID to distinguish different deadlock events"}, - {name: deadlockhistory.ColOccurTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "The physical time when the deadlock occurs"}, - {name: deadlockhistory.ColRetryableStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the deadlock is retryable. Retryable deadlocks are usually not reported to the client"}, - {name: deadlockhistory.ColTryLockTrxIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction ID (start ts) of the transaction that's trying to acquire the lock"}, - {name: deadlockhistory.ColCurrentSQLDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "The digest of the SQL that's being blocked"}, - {name: deadlockhistory.ColCurrentSQLDigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The normalized SQL that's being blocked"}, - {name: deadlockhistory.ColKeyStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The key on which a transaction is waiting for another"}, - {name: deadlockhistory.ColKeyInfoStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Information of the key"}, - {name: deadlockhistory.ColTrxHoldingLockStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction ID (start ts) of the transaction that's currently holding the lock"}, -} - -var tableDataLockWaitsCols = []columnInfo{ - {name: DataLockWaitsColumnKey, tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag, comment: "The key that's being waiting on"}, - {name: DataLockWaitsColumnKeyInfo, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Information of the key"}, - {name: DataLockWaitsColumnTrxID, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Current transaction that's waiting for the lock"}, - {name: DataLockWaitsColumnCurrentHoldingTrxID, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction that's holding the lock and blocks the current transaction"}, - {name: DataLockWaitsColumnSQLDigest, tp: mysql.TypeVarchar, size: 64, comment: "Digest of the SQL that's trying to acquire the lock"}, - {name: DataLockWaitsColumnSQLDigestText, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Digest of the SQL that's trying to acquire the lock"}, -} - -var tableStatementsSummaryEvictedCols = []columnInfo{ - {name: "BEGIN_TIME", tp: mysql.TypeTimestamp, size: 26}, - {name: "END_TIME", tp: mysql.TypeTimestamp, size: 26}, - {name: "EVICTED_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, -} - -var tableAttributesCols = []columnInfo{ - {name: "ID", tp: mysql.TypeVarchar, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, - {name: "TYPE", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag}, - {name: "ATTRIBUTES", tp: mysql.TypeVarchar, size: types.UnspecifiedLength}, - {name: "RANGES", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, -} - -var tableTrxSummaryCols = []columnInfo{ - {name: "DIGEST", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, comment: "Digest of a transaction"}, - {name: txninfo.AllSQLDigestsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the digests of SQL statements that the transaction has executed"}, -} - -var tablePlacementPoliciesCols = []columnInfo{ - {name: "POLICY_ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "CATALOG_NAME", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, - {name: "POLICY_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, // Catalog wide policy - {name: "PRIMARY_REGION", tp: mysql.TypeVarchar, size: 1024}, - {name: "REGIONS", tp: mysql.TypeVarchar, size: 1024}, - {name: "CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, - {name: "LEADER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, - {name: "FOLLOWER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, - {name: "LEARNER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, - {name: "SCHEDULE", tp: mysql.TypeVarchar, size: 20}, // EVEN or MAJORITY_IN_PRIMARY - {name: "FOLLOWERS", tp: mysql.TypeLonglong, size: 64}, - {name: "LEARNERS", tp: mysql.TypeLonglong, size: 64}, -} - -var tableVariablesInfoCols = []columnInfo{ - {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "VARIABLE_SCOPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "DEFAULT_VALUE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CURRENT_VALUE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "MIN_VALUE", tp: mysql.TypeLonglong, size: 64}, - {name: "MAX_VALUE", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, - {name: "POSSIBLE_VALUES", tp: mysql.TypeVarchar, size: 256}, - {name: "IS_NOOP", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, -} - -var tableUserAttributesCols = []columnInfo{ - {name: "USER", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "HOST", tp: mysql.TypeVarchar, size: 255, flag: mysql.NotNullFlag}, - {name: "ATTRIBUTE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, -} - -var tableMemoryUsageCols = []columnInfo{ - {name: "MEMORY_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "MEMORY_LIMIT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "MEMORY_CURRENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "MEMORY_MAX_USED", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "CURRENT_OPS", tp: mysql.TypeVarchar, size: 50}, - {name: "SESSION_KILL_LAST", tp: mysql.TypeDatetime}, - {name: "SESSION_KILL_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "GC_LAST", tp: mysql.TypeDatetime}, - {name: "GC_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "DISK_USAGE", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "QUERY_FORCE_DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, -} - -var tableMemoryUsageOpsHistoryCols = []columnInfo{ - {name: "TIME", tp: mysql.TypeDatetime, size: 64, flag: mysql.NotNullFlag}, - {name: "OPS", tp: mysql.TypeVarchar, size: 20, flag: mysql.NotNullFlag}, - {name: "MEMORY_LIMIT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "MEMORY_CURRENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, - {name: "PROCESSID", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, - {name: "MEM", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, - {name: "DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, - {name: "CLIENT", tp: mysql.TypeVarchar, size: 64}, - {name: "DB", tp: mysql.TypeVarchar, size: 64}, - {name: "USER", tp: mysql.TypeVarchar, size: 16}, - {name: "SQL_DIGEST", tp: mysql.TypeVarchar, size: 64}, - {name: "SQL_TEXT", tp: mysql.TypeVarchar, size: 256}, -} - -var tableResourceGroupsCols = []columnInfo{ - {name: "NAME", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag}, - {name: "RU_PER_SEC", tp: mysql.TypeVarchar, size: 21}, - {name: "PRIORITY", tp: mysql.TypeVarchar, size: 6}, - {name: "BURSTABLE", tp: mysql.TypeVarchar, size: 3}, - {name: "QUERY_LIMIT", tp: mysql.TypeVarchar, size: 256}, - {name: "BACKGROUND", tp: mysql.TypeVarchar, size: 256}, -} - -var tableRunawayWatchListCols = []columnInfo{ - {name: "ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, - {name: "RESOURCE_GROUP_NAME", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag}, - {name: "START_TIME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, - {name: "END_TIME", tp: mysql.TypeVarchar, size: 32}, - {name: "WATCH", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, - {name: "WATCH_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, - {name: "SOURCE", tp: mysql.TypeVarchar, size: 128, flag: mysql.NotNullFlag}, - {name: "ACTION", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, -} - -var tableCheckConstraintsCols = []columnInfo{ - {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, - {name: "CHECK_CLAUSE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, -} - -// GetShardingInfo returns a nil or description string for the sharding information of given TableInfo. -// The returned description string may be: -// - "NOT_SHARDED": for tables that SHARD_ROW_ID_BITS is not specified. -// - "NOT_SHARDED(PK_IS_HANDLE)": for tables of which primary key is row id. -// - "PK_AUTO_RANDOM_BITS={bit_number}, RANGE BITS={bit_number}": for tables of which primary key is sharded row id. -// - "SHARD_BITS={bit_number}": for tables that with SHARD_ROW_ID_BITS. -// -// The returned nil indicates that sharding information is not suitable for the table(for example, when the table is a View). -// This function is exported for unit test. -func GetShardingInfo(dbInfo *model.DBInfo, tableInfo *model.TableInfo) interface{} { - if dbInfo == nil || tableInfo == nil || tableInfo.IsView() || util.IsMemOrSysDB(dbInfo.Name.L) { - return nil - } - shardingInfo := "NOT_SHARDED" - if tableInfo.PKIsHandle { - if tableInfo.ContainsAutoRandomBits() { - shardingInfo = "PK_AUTO_RANDOM_BITS=" + strconv.Itoa(int(tableInfo.AutoRandomBits)) - rangeBits := tableInfo.AutoRandomRangeBits - if rangeBits != 0 && rangeBits != autoid.AutoRandomRangeBitsDefault { - shardingInfo = fmt.Sprintf("%s, RANGE BITS=%d", shardingInfo, rangeBits) - } - } else { - shardingInfo = "NOT_SHARDED(PK_IS_HANDLE)" - } - } else if tableInfo.ShardRowIDBits > 0 { - shardingInfo = "SHARD_BITS=" + strconv.Itoa(int(tableInfo.ShardRowIDBits)) - } - return shardingInfo -} - -const ( - // PrimaryKeyType is the string constant of PRIMARY KEY. - PrimaryKeyType = "PRIMARY KEY" - // PrimaryConstraint is the string constant of PRIMARY. - PrimaryConstraint = "PRIMARY" - // UniqueKeyType is the string constant of UNIQUE. - UniqueKeyType = "UNIQUE" - // ForeignKeyType is the string constant of Foreign Key. - ForeignKeyType = "FOREIGN KEY" -) - -const ( - // TiFlashWrite is the TiFlash write node in disaggregated mode. - TiFlashWrite = "tiflash_write" -) - -// ServerInfo represents the basic server information of single cluster component -type ServerInfo struct { - ServerType string - Address string - StatusAddr string - Version string - GitHash string - StartTimestamp int64 - ServerID uint64 - EngineRole string -} - -func (s *ServerInfo) isLoopBackOrUnspecifiedAddr(addr string) bool { - tcpAddr, err := net.ResolveTCPAddr("", addr) - if err != nil { - return false - } - ip := net.ParseIP(tcpAddr.IP.String()) - return ip != nil && (ip.IsUnspecified() || ip.IsLoopback()) -} - -// ResolveLoopBackAddr exports for testing. -func (s *ServerInfo) ResolveLoopBackAddr() { - if s.isLoopBackOrUnspecifiedAddr(s.Address) && !s.isLoopBackOrUnspecifiedAddr(s.StatusAddr) { - addr, err1 := net.ResolveTCPAddr("", s.Address) - statusAddr, err2 := net.ResolveTCPAddr("", s.StatusAddr) - if err1 == nil && err2 == nil { - addr.IP = statusAddr.IP - s.Address = addr.String() - } - } else if !s.isLoopBackOrUnspecifiedAddr(s.Address) && s.isLoopBackOrUnspecifiedAddr(s.StatusAddr) { - addr, err1 := net.ResolveTCPAddr("", s.Address) - statusAddr, err2 := net.ResolveTCPAddr("", s.StatusAddr) - if err1 == nil && err2 == nil { - statusAddr.IP = addr.IP - s.StatusAddr = statusAddr.String() - } - } -} - -// GetClusterServerInfo returns all components information of cluster -func GetClusterServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { - failpoint.Inject("mockClusterInfo", func(val failpoint.Value) { - // The cluster topology is injected by `failpoint` expression and - // there is no extra checks for it. (let the test fail if the expression invalid) - if s := val.(string); len(s) > 0 { - var servers []ServerInfo - for _, server := range strings.Split(s, ";") { - parts := strings.Split(server, ",") - serverID, err := strconv.ParseUint(parts[5], 10, 64) - if err != nil { - panic("convert parts[5] to uint64 failed") - } - servers = append(servers, ServerInfo{ - ServerType: parts[0], - Address: parts[1], - StatusAddr: parts[2], - Version: parts[3], - GitHash: parts[4], - ServerID: serverID, - }) - } - failpoint.Return(servers, nil) - } - }) - - type retriever func(ctx sessionctx.Context) ([]ServerInfo, error) - //nolint: prealloc - var servers []ServerInfo - for _, r := range []retriever{GetTiDBServerInfo, GetPDServerInfo, GetStoreServerInfo} { - nodes, err := r(ctx) - if err != nil { - return nil, err - } - for i := range nodes { - nodes[i].ResolveLoopBackAddr() - } - servers = append(servers, nodes...) - } - return servers, nil -} - -// GetTiDBServerInfo returns all TiDB nodes information of cluster -func GetTiDBServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { - // Get TiDB servers info. - tidbNodes, err := infosync.GetAllServerInfo(context.Background()) - if err != nil { - return nil, errors.Trace(err) - } - var isDefaultVersion bool - if len(config.GetGlobalConfig().ServerVersion) == 0 { - isDefaultVersion = true - } - var servers = make([]ServerInfo, 0, len(tidbNodes)) - for _, node := range tidbNodes { - servers = append(servers, ServerInfo{ - ServerType: "tidb", - Address: net.JoinHostPort(node.IP, strconv.Itoa(int(node.Port))), - StatusAddr: net.JoinHostPort(node.IP, strconv.Itoa(int(node.StatusPort))), - Version: FormatTiDBVersion(node.Version, isDefaultVersion), - GitHash: node.GitHash, - StartTimestamp: node.StartTimestamp, - ServerID: node.ServerIDGetter(), - }) - } - return servers, nil -} - -// FormatTiDBVersion make TiDBVersion consistent to TiKV and PD. -// The default TiDBVersion is 5.7.25-TiDB-${TiDBReleaseVersion}. -func FormatTiDBVersion(TiDBVersion string, isDefaultVersion bool) string { - var version, nodeVersion string - - // The user hasn't set the config 'ServerVersion'. - if isDefaultVersion { - nodeVersion = TiDBVersion[strings.Index(TiDBVersion, "TiDB-")+len("TiDB-"):] - if len(nodeVersion) > 0 && nodeVersion[0] == 'v' { - nodeVersion = nodeVersion[1:] - } - nodeVersions := strings.SplitN(nodeVersion, "-", 2) - if len(nodeVersions) == 1 { - version = nodeVersions[0] - } else if len(nodeVersions) >= 2 { - version = fmt.Sprintf("%s-%s", nodeVersions[0], nodeVersions[1]) - } - } else { // The user has already set the config 'ServerVersion',it would be a complex scene, so just use the 'ServerVersion' as version. - version = TiDBVersion - } - - return version -} - -// GetPDServerInfo returns all PD nodes information of cluster -func GetPDServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { - // Get PD servers info. - store := ctx.GetStore() - etcd, ok := store.(kv.EtcdBackend) - if !ok { - return nil, errors.Errorf("%T not an etcd backend", store) - } - members, err := etcd.EtcdAddrs() - if err != nil { - return nil, errors.Trace(err) - } - // TODO: maybe we should unify the PD API request interface. - var ( - memberNum = len(members) - servers = make([]ServerInfo, 0, memberNum) - errs = make([]error, 0, memberNum) - ) - if memberNum == 0 { - return servers, nil - } - // Try on each member until one succeeds or all fail. - for _, addr := range members { - // Get PD version, git_hash - url := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), addr, pdapi.Status) - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - ctx.GetSessionVars().StmtCtx.AppendWarning(err) - logutil.BgLogger().Warn("create pd server info request error", zap.String("url", url), zap.Error(err)) - errs = append(errs, err) - continue - } - req.Header.Add("PD-Allow-follower-handle", "true") - resp, err := util.InternalHTTPClient().Do(req) - if err != nil { - ctx.GetSessionVars().StmtCtx.AppendWarning(err) - logutil.BgLogger().Warn("request pd server info error", zap.String("url", url), zap.Error(err)) - errs = append(errs, err) - continue - } - var content = struct { - Version string `json:"version"` - GitHash string `json:"git_hash"` - StartTimestamp int64 `json:"start_timestamp"` - }{} - err = json.NewDecoder(resp.Body).Decode(&content) - terror.Log(resp.Body.Close()) - if err != nil { - ctx.GetSessionVars().StmtCtx.AppendWarning(err) - logutil.BgLogger().Warn("close pd server info request error", zap.String("url", url), zap.Error(err)) - errs = append(errs, err) - continue - } - if len(content.Version) > 0 && content.Version[0] == 'v' { - content.Version = content.Version[1:] - } - - servers = append(servers, ServerInfo{ - ServerType: "pd", - Address: addr, - StatusAddr: addr, - Version: content.Version, - GitHash: content.GitHash, - StartTimestamp: content.StartTimestamp, - }) - } - // Return the errors if all members' requests fail. - if len(errs) == memberNum { - errorMsg := "" - for idx, err := range errs { - errorMsg += err.Error() - if idx < memberNum-1 { - errorMsg += "; " - } - } - return nil, errors.Trace(fmt.Errorf("%s", errorMsg)) - } - return servers, nil -} - -func isTiFlashStore(store *metapb.Store) bool { - for _, label := range store.Labels { - if label.GetKey() == placement.EngineLabelKey && label.GetValue() == placement.EngineLabelTiFlash { - return true - } - } - return false -} - -func isTiFlashWriteNode(store *metapb.Store) bool { - for _, label := range store.Labels { - if label.GetKey() == placement.EngineRoleLabelKey && label.GetValue() == placement.EngineRoleLabelWrite { - return true - } - } - return false -} - -// GetStoreServerInfo returns all store nodes(TiKV or TiFlash) cluster information -func GetStoreServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { - failpoint.Inject("mockStoreServerInfo", func(val failpoint.Value) { - if s := val.(string); len(s) > 0 { - var servers []ServerInfo - for _, server := range strings.Split(s, ";") { - parts := strings.Split(server, ",") - servers = append(servers, ServerInfo{ - ServerType: parts[0], - Address: parts[1], - StatusAddr: parts[2], - Version: parts[3], - GitHash: parts[4], - StartTimestamp: 0, - }) - } - failpoint.Return(servers, nil) - } - }) - - store := ctx.GetStore() - // Get TiKV servers info. - tikvStore, ok := store.(tikv.Storage) - if !ok { - return nil, errors.Errorf("%T is not an TiKV or TiFlash store instance", store) - } - pdClient := tikvStore.GetRegionCache().PDClient() - if pdClient == nil { - return nil, errors.New("pd unavailable") - } - stores, err := pdClient.GetAllStores(context.Background()) - if err != nil { - return nil, errors.Trace(err) - } - servers := make([]ServerInfo, 0, len(stores)) - for _, store := range stores { - failpoint.Inject("mockStoreTombstone", func(val failpoint.Value) { - if val.(bool) { - store.State = metapb.StoreState_Tombstone - } - }) - - if store.GetState() == metapb.StoreState_Tombstone { - continue - } - var tp string - if isTiFlashStore(store) { - tp = kv.TiFlash.Name() - } else { - tp = tikv.GetStoreTypeByMeta(store).Name() - } - var engineRole string - if isTiFlashWriteNode(store) { - engineRole = placement.EngineRoleLabelWrite - } - servers = append(servers, ServerInfo{ - ServerType: tp, - Address: store.Address, - StatusAddr: store.StatusAddress, - Version: FormatStoreServerVersion(store.Version), - GitHash: store.GitHash, - StartTimestamp: store.StartTimestamp, - EngineRole: engineRole, - }) - } - return servers, nil -} - -// FormatStoreServerVersion format version of store servers(Tikv or TiFlash) -func FormatStoreServerVersion(version string) string { - if len(version) >= 1 && version[0] == 'v' { - version = version[1:] - } - return version -} - -// GetTiFlashStoreCount returns the count of tiflash server. -func GetTiFlashStoreCount(ctx sessionctx.Context) (cnt uint64, err error) { - failpoint.Inject("mockTiFlashStoreCount", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(uint64(10), nil) - } - }) - - stores, err := GetStoreServerInfo(ctx) - if err != nil { - return cnt, err - } - for _, store := range stores { - if store.ServerType == kv.TiFlash.Name() { - cnt++ - } - } - return cnt, nil -} - -// SysVarHiddenForSem checks if a given sysvar is hidden according to SEM and privileges. -func SysVarHiddenForSem(ctx sessionctx.Context, sysVarNameInLower string) bool { - if !sem.IsEnabled() || !sem.IsInvisibleSysVar(sysVarNameInLower) { - return false - } - checker := privilege.GetPrivilegeManager(ctx) - if checker == nil || checker.RequestDynamicVerification(ctx.GetSessionVars().ActiveRoles, "RESTRICTED_VARIABLES_ADMIN", false) { - return false - } - return true -} - -// GetDataFromSessionVariables return the [name, value] of all session variables -func GetDataFromSessionVariables(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { - sessionVars := sctx.GetSessionVars() - sysVars := variable.GetSysVars() - rows := make([][]types.Datum, 0, len(sysVars)) - for _, v := range sysVars { - if SysVarHiddenForSem(sctx, v.Name) { - continue - } - var value string - value, err := sessionVars.GetSessionOrGlobalSystemVar(ctx, v.Name) - if err != nil { - return nil, err - } - row := types.MakeDatums(v.Name, value) - rows = append(rows, row) - } - return rows, nil -} - -// GetDataFromSessionConnectAttrs produces the rows for the session_connect_attrs table. -func GetDataFromSessionConnectAttrs(sctx sessionctx.Context, sameAccount bool) ([][]types.Datum, error) { - sm := sctx.GetSessionManager() - if sm == nil { - return nil, nil - } - var user *auth.UserIdentity - if sameAccount { - user = sctx.GetSessionVars().User - } - allAttrs := sm.GetConAttrs(user) - rows := make([][]types.Datum, 0, len(allAttrs)*10) // 10 Attributes per connection - for pid, attrs := range allAttrs { // Note: PID is not ordered. - // Sorts the attributes by key and gives ORDINAL_POSITION based on this. This is needed as we didn't store the - // ORDINAL_POSITION and a map doesn't have a guaranteed sort order. This is needed to keep the ORDINAL_POSITION - // stable over multiple queries. - attrnames := make([]string, 0, len(attrs)) - for attrname := range attrs { - attrnames = append(attrnames, attrname) - } - sort.Strings(attrnames) - - for ord, attrkey := range attrnames { - row := types.MakeDatums( - pid, - attrkey, - attrs[attrkey], - ord, - ) - rows = append(rows, row) - } - } - return rows, nil -} - -var tableNameToColumns = map[string][]columnInfo{ - TableSchemata: schemataCols, - TableTables: tablesCols, - TableColumns: columnsCols, - tableColumnStatistics: columnStatisticsCols, - TableStatistics: statisticsCols, - TableCharacterSets: charsetCols, - TableCollations: collationsCols, - tableFiles: filesCols, - TableProfiling: profilingCols, - TablePartitions: partitionsCols, - TableKeyColumn: keyColumnUsageCols, - TableReferConst: referConstCols, - TableSessionVar: sessionVarCols, - tablePlugins: pluginsCols, - TableConstraints: tableConstraintsCols, - tableTriggers: tableTriggersCols, - TableUserPrivileges: tableUserPrivilegesCols, - tableSchemaPrivileges: tableSchemaPrivilegesCols, - tableTablePrivileges: tableTablePrivilegesCols, - tableColumnPrivileges: tableColumnPrivilegesCols, - TableEngines: tableEnginesCols, - TableViews: tableViewsCols, - tableRoutines: tableRoutinesCols, - tableParameters: tableParametersCols, - tableEvents: tableEventsCols, - tableGlobalStatus: tableGlobalStatusCols, - tableGlobalVariables: tableGlobalVariablesCols, - tableSessionStatus: tableSessionStatusCols, - tableOptimizerTrace: tableOptimizerTraceCols, - tableTableSpaces: tableTableSpacesCols, - TableCollationCharacterSetApplicability: tableCollationCharacterSetApplicabilityCols, - TableProcesslist: tableProcesslistCols, - TableTiDBIndexes: tableTiDBIndexesCols, - TableSlowQuery: slowQueryCols, - TableTiDBHotRegions: TableTiDBHotRegionsCols, - TableTiDBHotRegionsHistory: TableTiDBHotRegionsHistoryCols, - TableTiKVStoreStatus: TableTiKVStoreStatusCols, - TableAnalyzeStatus: tableAnalyzeStatusCols, - TableTiKVRegionStatus: TableTiKVRegionStatusCols, - TableTiKVRegionPeers: TableTiKVRegionPeersCols, - TableTiDBServersInfo: tableTiDBServersInfoCols, - TableClusterInfo: tableClusterInfoCols, - TableClusterConfig: tableClusterConfigCols, - TableClusterLog: tableClusterLogCols, - TableClusterLoad: tableClusterLoadCols, - TableTiFlashReplica: tableTableTiFlashReplicaCols, - TableClusterHardware: tableClusterHardwareCols, - TableClusterSystemInfo: tableClusterSystemInfoCols, - TableInspectionResult: tableInspectionResultCols, - TableMetricSummary: tableMetricSummaryCols, - TableMetricSummaryByLabel: tableMetricSummaryByLabelCols, - TableMetricTables: tableMetricTablesCols, - TableInspectionSummary: tableInspectionSummaryCols, - TableInspectionRules: tableInspectionRulesCols, - TableDDLJobs: tableDDLJobsCols, - TableSequences: tableSequencesCols, - TableStatementsSummary: tableStatementsSummaryCols, - TableStatementsSummaryHistory: tableStatementsSummaryCols, - TableStatementsSummaryEvicted: tableStatementsSummaryEvictedCols, - TableStorageStats: tableStorageStatsCols, - TableTiFlashTables: tableTableTiFlashTablesCols, - TableTiFlashSegments: tableTableTiFlashSegmentsCols, - TableClientErrorsSummaryGlobal: tableClientErrorsSummaryGlobalCols, - TableClientErrorsSummaryByUser: tableClientErrorsSummaryByUserCols, - TableClientErrorsSummaryByHost: tableClientErrorsSummaryByHostCols, - TableTiDBTrx: tableTiDBTrxCols, - TableDeadlocks: tableDeadlocksCols, - TableDataLockWaits: tableDataLockWaitsCols, - TableAttributes: tableAttributesCols, - TablePlacementPolicies: tablePlacementPoliciesCols, - TableTrxSummary: tableTrxSummaryCols, - TableVariablesInfo: tableVariablesInfoCols, - TableUserAttributes: tableUserAttributesCols, - TableMemoryUsage: tableMemoryUsageCols, - TableMemoryUsageOpsHistory: tableMemoryUsageOpsHistoryCols, - TableResourceGroups: tableResourceGroupsCols, - TableRunawayWatches: tableRunawayWatchListCols, - TableCheckConstraints: tableCheckConstraintsCols, -} - -func createInfoSchemaTable(_ autoid.Allocators, meta *model.TableInfo) (table.Table, error) { - columns := make([]*table.Column, len(meta.Columns)) - for i, col := range meta.Columns { - columns[i] = table.ToColumn(col) - } - tp := table.VirtualTable - if isClusterTableByName(util.InformationSchemaName.O, meta.Name.O) { - tp = table.ClusterTable - } - return &infoschemaTable{meta: meta, cols: columns, tp: tp}, nil -} - -type infoschemaTable struct { - meta *model.TableInfo - cols []*table.Column - tp table.Type -} - -// IterRecords implements table.Table IterRecords interface. -func (*infoschemaTable) IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error { - return nil -} - -// Cols implements table.Table Cols interface. -func (it *infoschemaTable) Cols() []*table.Column { - return it.cols -} - -// VisibleCols implements table.Table VisibleCols interface. -func (it *infoschemaTable) VisibleCols() []*table.Column { - return it.cols -} - -// HiddenCols implements table.Table HiddenCols interface. -func (it *infoschemaTable) HiddenCols() []*table.Column { - return nil -} - -// WritableCols implements table.Table WritableCols interface. -func (it *infoschemaTable) WritableCols() []*table.Column { - return it.cols -} - -// DeletableCols implements table.Table WritableCols interface. -func (it *infoschemaTable) DeletableCols() []*table.Column { - return it.cols -} - -// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. -func (it *infoschemaTable) FullHiddenColsAndVisibleCols() []*table.Column { - return it.cols -} - -// Indices implements table.Table Indices interface. -func (it *infoschemaTable) Indices() []table.Index { - return nil -} - -// RecordPrefix implements table.Table RecordPrefix interface. -func (it *infoschemaTable) RecordPrefix() kv.Key { - return nil -} - -// IndexPrefix implements table.Table IndexPrefix interface. -func (it *infoschemaTable) IndexPrefix() kv.Key { - return nil -} - -// AddRecord implements table.Table AddRecord interface. -func (it *infoschemaTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - return nil, table.ErrUnsupportedOp -} - -// RemoveRecord implements table.Table RemoveRecord interface. -func (it *infoschemaTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { - return table.ErrUnsupportedOp -} - -// UpdateRecord implements table.Table UpdateRecord interface. -func (it *infoschemaTable) UpdateRecord(gctx context.Context, ctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { - return table.ErrUnsupportedOp -} - -// Allocators implements table.Table Allocators interface. -func (it *infoschemaTable) Allocators(_ sessionctx.Context) autoid.Allocators { - return autoid.Allocators{} -} - -// Meta implements table.Table Meta interface. -func (it *infoschemaTable) Meta() *model.TableInfo { - return it.meta -} - -// GetPhysicalID implements table.Table GetPhysicalID interface. -func (it *infoschemaTable) GetPhysicalID() int64 { - return it.meta.ID -} - -// Type implements table.Table Type interface. -func (it *infoschemaTable) Type() table.Type { - return it.tp -} - -// GetPartitionedTable implements table.Table GetPartitionedTable interface. -func (it *infoschemaTable) GetPartitionedTable() table.PartitionedTable { - return nil -} - -// VirtualTable is a dummy table.Table implementation. -type VirtualTable struct{} - -// Cols implements table.Table Cols interface. -func (vt *VirtualTable) Cols() []*table.Column { - return nil -} - -// VisibleCols implements table.Table VisibleCols interface. -func (vt *VirtualTable) VisibleCols() []*table.Column { - return nil -} - -// HiddenCols implements table.Table HiddenCols interface. -func (vt *VirtualTable) HiddenCols() []*table.Column { - return nil -} - -// WritableCols implements table.Table WritableCols interface. -func (vt *VirtualTable) WritableCols() []*table.Column { - return nil -} - -// DeletableCols implements table.Table WritableCols interface. -func (vt *VirtualTable) DeletableCols() []*table.Column { - return nil -} - -// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. -func (vt *VirtualTable) FullHiddenColsAndVisibleCols() []*table.Column { - return nil -} - -// Indices implements table.Table Indices interface. -func (vt *VirtualTable) Indices() []table.Index { - return nil -} - -// RecordPrefix implements table.Table RecordPrefix interface. -func (vt *VirtualTable) RecordPrefix() kv.Key { - return nil -} - -// IndexPrefix implements table.Table IndexPrefix interface. -func (vt *VirtualTable) IndexPrefix() kv.Key { - return nil -} - -// AddRecord implements table.Table AddRecord interface. -func (vt *VirtualTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - return nil, table.ErrUnsupportedOp -} - -// RemoveRecord implements table.Table RemoveRecord interface. -func (vt *VirtualTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { - return table.ErrUnsupportedOp -} - -// UpdateRecord implements table.Table UpdateRecord interface. -func (vt *VirtualTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { - return table.ErrUnsupportedOp -} - -// Allocators implements table.Table Allocators interface. -func (vt *VirtualTable) Allocators(_ sessionctx.Context) autoid.Allocators { - return autoid.Allocators{} -} - -// Meta implements table.Table Meta interface. -func (vt *VirtualTable) Meta() *model.TableInfo { - return nil -} - -// GetPhysicalID implements table.Table GetPhysicalID interface. -func (vt *VirtualTable) GetPhysicalID() int64 { - return 0 -} - -// Type implements table.Table Type interface. -func (vt *VirtualTable) Type() table.Type { - return table.VirtualTable -} - -// GetTiFlashServerInfo returns all TiFlash server infos -func GetTiFlashServerInfo(sctx sessionctx.Context) ([]ServerInfo, error) { - if config.GetGlobalConfig().DisaggregatedTiFlash { - return nil, table.ErrUnsupportedOp - } - serversInfo, err := GetStoreServerInfo(sctx) - if err != nil { - return nil, err - } - serversInfo = FilterClusterServerInfo(serversInfo, set.NewStringSet(kv.TiFlash.Name()), set.NewStringSet()) - return serversInfo, nil -} - -// FetchClusterServerInfoWithoutPrivilegeCheck fetches cluster server information -func FetchClusterServerInfoWithoutPrivilegeCheck(ctx context.Context, sctx sessionctx.Context, serversInfo []ServerInfo, serverInfoType diagnosticspb.ServerInfoType, recordWarningInStmtCtx bool) ([][]types.Datum, error) { - type result struct { - idx int - rows [][]types.Datum - err error - } - wg := sync.WaitGroup{} - ch := make(chan result, len(serversInfo)) - infoTp := serverInfoType - finalRows := make([][]types.Datum, 0, len(serversInfo)*10) - for i, srv := range serversInfo { - address := srv.Address - remote := address - if srv.ServerType == "tidb" { - remote = srv.StatusAddr - } - wg.Add(1) - go func(index int, remote, address, serverTP string) { - util.WithRecovery(func() { - defer wg.Done() - items, err := getServerInfoByGRPC(ctx, remote, infoTp) - if err != nil { - ch <- result{idx: index, err: err} - return - } - partRows := serverInfoItemToRows(items, serverTP, address) - ch <- result{idx: index, rows: partRows} - }, nil) - }(i, remote, address, srv.ServerType) - } - wg.Wait() - close(ch) - // Keep the original order to make the result more stable - var results []result //nolint: prealloc - for result := range ch { - if result.err != nil { - if recordWarningInStmtCtx { - sctx.GetSessionVars().StmtCtx.AppendWarning(result.err) - } else { - log.Warn(result.err.Error()) - } - continue - } - results = append(results, result) - } - slices.SortFunc(results, func(i, j result) int { return cmp.Compare(i.idx, j.idx) }) - for _, result := range results { - finalRows = append(finalRows, result.rows...) - } - return finalRows, nil -} - -func serverInfoItemToRows(items []*diagnosticspb.ServerInfoItem, tp, addr string) [][]types.Datum { - rows := make([][]types.Datum, 0, len(items)) - for _, v := range items { - for _, item := range v.Pairs { - row := types.MakeDatums( - tp, - addr, - v.Tp, - v.Name, - item.Key, - item.Value, - ) - rows = append(rows, row) - } - } - return rows -} - -func getServerInfoByGRPC(ctx context.Context, address string, tp diagnosticspb.ServerInfoType) ([]*diagnosticspb.ServerInfoItem, error) { - opt := grpc.WithTransportCredentials(insecure.NewCredentials()) - security := config.GetGlobalConfig().Security - if len(security.ClusterSSLCA) != 0 { - clusterSecurity := security.ClusterSecurity() - tlsConfig, err := clusterSecurity.ToTLSConfig() - if err != nil { - return nil, errors.Trace(err) - } - opt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)) - } - conn, err := grpc.Dial(address, opt) - if err != nil { - return nil, err - } - defer func() { - err := conn.Close() - if err != nil { - log.Error("close grpc connection error", zap.Error(err)) - } - }() - - cli := diagnosticspb.NewDiagnosticsClient(conn) - ctx, cancel := context.WithTimeout(ctx, time.Second*10) - defer cancel() - r, err := cli.ServerInfo(ctx, &diagnosticspb.ServerInfoRequest{Tp: tp}) - if err != nil { - return nil, err - } - return r.Items, nil -} - -// FilterClusterServerInfo filters serversInfo by nodeTypes and addresses -func FilterClusterServerInfo(serversInfo []ServerInfo, nodeTypes, addresses set.StringSet) []ServerInfo { - if len(nodeTypes) == 0 && len(addresses) == 0 { - return serversInfo - } - - filterServers := make([]ServerInfo, 0, len(serversInfo)) - for _, srv := range serversInfo { - // Skip some node type which has been filtered in WHERE clause - // e.g: SELECT * FROM cluster_config WHERE type='tikv' - if len(nodeTypes) > 0 && !nodeTypes.Exist(srv.ServerType) { - continue - } - // Skip some node address which has been filtered in WHERE clause - // e.g: SELECT * FROM cluster_config WHERE address='192.16.8.12:2379' - if len(addresses) > 0 && !addresses.Exist(srv.Address) { - continue - } - filterServers = append(filterServers, srv) - } - return filterServers -} diff --git a/infoschema/test/cachetest/BUILD.bazel b/infoschema/test/cachetest/BUILD.bazel deleted file mode 100644 index 4cd2112cd6b95..0000000000000 --- a/infoschema/test/cachetest/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "cachetest_test", - timeout = "short", - srcs = [ - "cache_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 6, - deps = [ - "//infoschema", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/infoschema/test/cachetest/cache_test.go b/infoschema/test/cachetest/cache_test.go deleted file mode 100644 index f8c5cbe680753..0000000000000 --- a/infoschema/test/cachetest/cache_test.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cachetest - -import ( - "testing" - - "github.com/pingcap/tidb/infoschema" - "github.com/stretchr/testify/require" -) - -func TestNewCache(t *testing.T) { - ic := infoschema.NewCache(16) - require.NotNil(t, ic) -} - -func TestInsert(t *testing.T) { - ic := infoschema.NewCache(3) - require.NotNil(t, ic) - - is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) - ic.Insert(is2, 2) - require.Equal(t, is2, ic.GetByVersion(2)) - require.Equal(t, is2, ic.GetBySnapshotTS(2)) - require.Equal(t, is2, ic.GetBySnapshotTS(10)) - require.Nil(t, ic.GetBySnapshotTS(0)) - - // newer - is5 := infoschema.MockInfoSchemaWithSchemaVer(nil, 5) - ic.Insert(is5, 5) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is2, ic.GetByVersion(2)) - // there is a gap in schema cache, so don't use this version - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is5, ic.GetBySnapshotTS(10)) - - // older - is0 := infoschema.MockInfoSchemaWithSchemaVer(nil, 0) - ic.Insert(is0, 0) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is2, ic.GetByVersion(2)) - require.Equal(t, is0, ic.GetByVersion(0)) - - // replace 5, drop 0 - is6 := infoschema.MockInfoSchemaWithSchemaVer(nil, 6) - ic.Insert(is6, 6) - require.Equal(t, is6, ic.GetByVersion(6)) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is2, ic.GetByVersion(2)) - require.Nil(t, ic.GetByVersion(0)) - // there is a gap in schema cache, so don't use this version - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is5, ic.GetBySnapshotTS(5)) - require.Equal(t, is6, ic.GetBySnapshotTS(10)) - - // replace 2, drop 2 - is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) - ic.Insert(is3, 3) - require.Equal(t, is6, ic.GetByVersion(6)) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is3, ic.GetByVersion(3)) - require.Nil(t, ic.GetByVersion(2)) - require.Nil(t, ic.GetByVersion(0)) - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is6, ic.GetBySnapshotTS(10)) - - // insert 2, but failed silently - ic.Insert(is2, 2) - require.Equal(t, is6, ic.GetByVersion(6)) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is3, ic.GetByVersion(3)) - require.Nil(t, ic.GetByVersion(2)) - require.Nil(t, ic.GetByVersion(0)) - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is6, ic.GetBySnapshotTS(10)) - - // insert 5, but it is already in - ic.Insert(is5, 5) - require.Equal(t, is6, ic.GetByVersion(6)) - require.Equal(t, is5, ic.GetByVersion(5)) - require.Equal(t, is3, ic.GetByVersion(3)) - require.Nil(t, ic.GetByVersion(2)) - require.Nil(t, ic.GetByVersion(0)) - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is5, ic.GetBySnapshotTS(5)) - require.Equal(t, is6, ic.GetBySnapshotTS(10)) -} - -func TestGetByVersion(t *testing.T) { - ic := infoschema.NewCache(2) - require.NotNil(t, ic) - is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) - ic.Insert(is1, 1) - is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) - ic.Insert(is3, 3) - - require.Equal(t, is1, ic.GetByVersion(1)) - require.Equal(t, is3, ic.GetByVersion(3)) - require.Nilf(t, ic.GetByVersion(0), "index == 0, but not found") - require.Equal(t, int64(1), ic.GetByVersion(2).SchemaMetaVersion()) - require.Nilf(t, ic.GetByVersion(4), "index == length, but not found") -} - -func TestGetLatest(t *testing.T) { - ic := infoschema.NewCache(16) - require.NotNil(t, ic) - require.Nil(t, ic.GetLatest()) - - is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) - ic.Insert(is1, 1) - require.Equal(t, is1, ic.GetLatest()) - - // newer change the newest - is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) - ic.Insert(is2, 2) - require.Equal(t, is2, ic.GetLatest()) - - // older schema doesn't change the newest - is0 := infoschema.MockInfoSchemaWithSchemaVer(nil, 0) - ic.Insert(is0, 0) - require.Equal(t, is2, ic.GetLatest()) -} - -func TestGetByTimestamp(t *testing.T) { - ic := infoschema.NewCache(16) - require.NotNil(t, ic) - require.Nil(t, ic.GetLatest()) - require.Equal(t, 0, ic.Len()) - - is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) - ic.Insert(is1, 1) - require.Nil(t, ic.GetBySnapshotTS(0)) - require.Equal(t, is1, ic.GetBySnapshotTS(1)) - require.Equal(t, is1, ic.GetBySnapshotTS(2)) - require.Equal(t, 1, ic.Len()) - - is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) - ic.Insert(is3, 3) - require.Equal(t, is3, ic.GetLatest()) - require.Nil(t, ic.GetBySnapshotTS(0)) - // there is a gap, no schema returned for ts 2 - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is3, ic.GetBySnapshotTS(3)) - require.Equal(t, is3, ic.GetBySnapshotTS(4)) - require.Equal(t, 2, ic.Len()) - - is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) - // schema version 2 doesn't have timestamp set - // thus all schema before ver 2 cannot be searched by timestamp anymore - // because the ts of ver 2 is not accurate - ic.Insert(is2, 0) - require.Equal(t, is3, ic.GetLatest()) - require.Nil(t, ic.GetBySnapshotTS(0)) - require.Nil(t, ic.GetBySnapshotTS(1)) - require.Nil(t, ic.GetBySnapshotTS(2)) - require.Equal(t, is3, ic.GetBySnapshotTS(3)) - require.Equal(t, is3, ic.GetBySnapshotTS(4)) - require.Equal(t, 3, ic.Len()) - - // insert is2 again with correct timestamp, to correct previous wrong timestamp - ic.Insert(is2, 2) - require.Equal(t, is3, ic.GetLatest()) - require.Equal(t, is1, ic.GetBySnapshotTS(1)) - require.Equal(t, is2, ic.GetBySnapshotTS(2)) - require.Equal(t, is3, ic.GetBySnapshotTS(3)) - require.Equal(t, 3, ic.Len()) -} - -func TestReSize(t *testing.T) { - ic := infoschema.NewCache(2) - require.NotNil(t, ic) - is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) - ic.Insert(is1, 1) - is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) - ic.Insert(is2, 2) - - ic.ReSize(3) - require.Equal(t, 2, ic.Size()) - require.Equal(t, is1, ic.GetByVersion(1)) - require.Equal(t, is2, ic.GetByVersion(2)) - is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) - require.True(t, ic.Insert(is3, 3)) - require.Equal(t, is1, ic.GetByVersion(1)) - require.Equal(t, is2, ic.GetByVersion(2)) - require.Equal(t, is3, ic.GetByVersion(3)) - - ic.ReSize(1) - require.Equal(t, 1, ic.Size()) - require.Nil(t, ic.GetByVersion(1)) - require.Nil(t, ic.GetByVersion(2)) - require.Equal(t, is3, ic.GetByVersion(3)) - require.False(t, ic.Insert(is2, 2)) - require.Equal(t, 1, ic.Size()) - is4 := infoschema.MockInfoSchemaWithSchemaVer(nil, 4) - require.True(t, ic.Insert(is4, 4)) - require.Equal(t, 1, ic.Size()) - require.Nil(t, ic.GetByVersion(1)) - require.Nil(t, ic.GetByVersion(2)) - require.Nil(t, ic.GetByVersion(3)) - require.Equal(t, is4, ic.GetByVersion(4)) -} diff --git a/infoschema/test/cachetest/main_test.go b/infoschema/test/cachetest/main_test.go deleted file mode 100644 index 99447e88d1035..0000000000000 --- a/infoschema/test/cachetest/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cachetest - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/infoschema/test/clustertablestest/BUILD.bazel b/infoschema/test/clustertablestest/BUILD.bazel deleted file mode 100644 index 152a7684b1325..0000000000000 --- a/infoschema/test/clustertablestest/BUILD.bazel +++ /dev/null @@ -1,58 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "clustertablestest_test", - timeout = "short", - srcs = [ - "cluster_tables_test.go", - "main_test.go", - "tables_test.go", - ], - flaky = True, - shard_count = 45, - deps = [ - "//config", - "//domain", - "//errno", - "//executor", - "//infoschema", - "//infoschema/internal", - "//kv", - "//meta/autoid", - "//parser", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//server", - "//session", - "//session/txninfo", - "//sessionctx/variable", - "//store/helper", - "//store/mockstore", - "//store/mockstore/mockstorage", - "//store/mockstore/unistore", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//types", - "//util", - "//util/gctuner", - "//util/memory", - "//util/pdapi", - "//util/resourcegrouptag", - "//util/set", - "//util/stmtsummary", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_fn//:fn", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/infoschema/test/clustertablestest/main_test.go b/infoschema/test/clustertablestest/main_test.go deleted file mode 100644 index aa93694d63cd7..0000000000000 --- a/infoschema/test/clustertablestest/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package clustertablestest - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/infoschema/test/clustertablestest/tables_test.go b/infoschema/test/clustertablestest/tables_test.go deleted file mode 100644 index c7b6bb73e2431..0000000000000 --- a/infoschema/test/clustertablestest/tables_test.go +++ /dev/null @@ -1,1419 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package clustertablestest - -import ( - "fmt" - "math" - "os" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/infoschema/internal" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/gctuner" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/require" -) - -func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - return tk -} - -func newTestKitWithPlanCache(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := testkit.NewTestKit(t, store) - se, err := session.CreateSession4TestWithOpt(store, &session.Opt{PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false)}) - require.NoError(t, err) - tk.SetSession(se) - tk.RefreshConnectionID() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - return tk -} - -func TestInfoSchemaFieldValue(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists numschema, timeschema") - tk.MustExec("create table numschema(i int(2), f float(4,2), d decimal(4,3))") - tk.MustExec("create table timeschema(d date, dt datetime(3), ts timestamp(3), t time(4), y year(4))") - tk.MustExec("create table strschema(c char(3), c2 varchar(3), b blob(3), t text(3))") - tk.MustExec("create table floatschema(a float, b double(7, 3))") - - tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='numschema'"). - Check(testkit.Rows(" 2 0 ", " 4 2 ", " 4 3 ")) // FIXME: for mysql first one will be " 10 0 " - tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='timeschema'"). - Check(testkit.Rows(" ", " 3", " 3", " 4", " ")) - tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='strschema'"). - Check(testkit.Rows("3 12 ", "3 12 ", "255 255 ", "255 1020 ")) - tk.MustQuery("select NUMERIC_SCALE from information_schema.COLUMNS where table_name='floatschema'"). - Check(testkit.Rows("", "3")) - - // Test for auto increment ID. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c int auto_increment primary key, d int)") - tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( - testkit.Rows("1")) - tk.MustExec("insert into t(c, d) values(1, 1)") - tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( - testkit.Rows("2")) - - tk.MustQuery("show create table t").Check( - testkit.Rows("" + - "t CREATE TABLE `t` (\n" + - " `c` int(11) NOT NULL AUTO_INCREMENT,\n" + - " `d` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`c`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30002")) - - // Test auto_increment for table without auto_increment column - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (d int)") - tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( - testkit.Rows("")) - - tk.MustExec("create user xxx") - - // Test for length of enum and set - tk.MustExec("drop table if exists t") - tk.MustExec("create table t ( s set('a','bc','def','ghij') default NULL, e1 enum('a', 'ab', 'cdef'), s2 SET('1','2','3','4','1585','ONE','TWO','Y','N','THREE'))") - tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 's'").Check( - testkit.Rows("s 13")) - tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 'S'").Check( - testkit.Rows("s 13")) - tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 's2'").Check( - testkit.Rows("s2 30")) - tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 'e1'").Check( - testkit.Rows("e1 4")) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{ - Username: "xxx", - Hostname: "127.0.0.1", - }, nil, nil, nil)) - - tk1.MustQuery("select distinct(table_schema) from information_schema.tables").Check(testkit.Rows("INFORMATION_SCHEMA")) - - // Fix issue 9836 - sm := &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 1, - User: "root", - Host: "127.0.0.1", - Command: mysql.ComQuery, - StmtCtx: tk.Session().GetSessionVars().StmtCtx, - }) - tk.Session().SetSessionManager(sm) - tk.MustQuery("SELECT user,host,command FROM information_schema.processlist;").Check(testkit.Rows("root 127.0.0.1 Query")) - - // Test for all system tables `TABLE_TYPE` is `SYSTEM VIEW`. - rows1 := tk.MustQuery("select count(*) from information_schema.tables where table_schema in ('INFORMATION_SCHEMA','PERFORMANCE_SCHEMA','METRICS_SCHEMA');").Rows() - rows2 := tk.MustQuery("select count(*) from information_schema.tables where table_schema in ('INFORMATION_SCHEMA','PERFORMANCE_SCHEMA','METRICS_SCHEMA') and table_type = 'SYSTEM VIEW';").Rows() - require.Equal(t, rows2, rows1) - // Test for system table default value - tk.MustQuery("show create table information_schema.PROCESSLIST").Check( - testkit.Rows("" + - "PROCESSLIST CREATE TABLE `PROCESSLIST` (\n" + - " `ID` bigint(21) unsigned NOT NULL DEFAULT '0',\n" + - " `USER` varchar(16) NOT NULL DEFAULT '',\n" + - " `HOST` varchar(64) NOT NULL DEFAULT '',\n" + - " `DB` varchar(64) DEFAULT NULL,\n" + - " `COMMAND` varchar(16) NOT NULL DEFAULT '',\n" + - " `TIME` int(7) NOT NULL DEFAULT '0',\n" + - " `STATE` varchar(7) DEFAULT NULL,\n" + - " `INFO` longtext DEFAULT NULL,\n" + - " `DIGEST` varchar(64) DEFAULT '',\n" + - " `MEM` bigint(21) unsigned DEFAULT NULL,\n" + - " `DISK` bigint(21) unsigned DEFAULT NULL,\n" + - " `TxnStart` varchar(64) NOT NULL DEFAULT '',\n" + - " `RESOURCE_GROUP` varchar(32) NOT NULL DEFAULT '',\n" + - " `SESSION_ALIAS` varchar(64) NOT NULL DEFAULT ''\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustQuery("show create table information_schema.cluster_log").Check( - testkit.Rows("" + - "CLUSTER_LOG CREATE TABLE `CLUSTER_LOG` (\n" + - " `TIME` varchar(32) DEFAULT NULL,\n" + - " `TYPE` varchar(64) DEFAULT NULL,\n" + - " `INSTANCE` varchar(64) DEFAULT NULL,\n" + - " `LEVEL` varchar(8) DEFAULT NULL,\n" + - " `MESSAGE` longtext DEFAULT NULL\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) -} - -func TestSomeTables(t *testing.T) { - store := testkit.CreateMockStore(t) - - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk := testkit.NewTestKit(t, store) - tk.SetSession(se) - sm := &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 1, - User: "user-1", - Host: "localhost", - Port: "", - DB: "information_schema", - Command: byte(1), - Digest: "abc1", - State: 1, - Info: "do something", - StmtCtx: tk.Session().GetSessionVars().StmtCtx, - ResourceGroupName: "rg1", - SessionAlias: "alias1", - }) - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 2, - User: "user-2", - Host: "localhost", - Port: "", - DB: "test", - Command: byte(2), - Digest: "abc2", - State: 2, - Info: strings.Repeat("x", 101), - StmtCtx: tk.Session().GetSessionVars().StmtCtx, - ResourceGroupName: "rg2", - }) - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 3, - User: "user-3", - Host: "127.0.0.1", - Port: "12345", - DB: "test", - Command: byte(2), - Digest: "abc3", - State: 1, - Info: "check port", - StmtCtx: tk.Session().GetSessionVars().StmtCtx, - ResourceGroupName: "rg3", - SessionAlias: "中文alias", - }) - tk.Session().SetSessionManager(sm) - tk.MustQuery("select * from information_schema.PROCESSLIST order by ID;").Sort().Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 alias1", "in transaction", "do something"), - fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s abc2 0 0 rg2 ", "autocommit", strings.Repeat("x", 101)), - fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s abc3 0 0 rg3 中文alias", "in transaction", "check port"), - )) - tk.MustQuery("SHOW PROCESSLIST;").Sort().Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", "do something"), - fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 100)), - fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s", "in transaction", "check port"), - )) - tk.MustQuery("SHOW FULL PROCESSLIST;").Sort().Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", "do something"), - fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 101)), - fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s", "in transaction", "check port"), - )) - - sm = &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 1, - User: "user-1", - Host: "localhost", - DB: "information_schema", - Command: byte(1), - Digest: "abc1", - State: 1, - ResourceGroupName: "rg1", - }) - sm.PS = append(sm.PS, &util.ProcessInfo{ - ID: 2, - User: "user-2", - Host: "localhost", - Command: byte(2), - Digest: "abc2", - State: 2, - Info: strings.Repeat("x", 101), - CurTxnStartTS: 410090409861578752, - ResourceGroupName: "rg2", - SessionAlias: "alias3", - }) - tk.Session().SetSessionManager(sm) - tk.Session().GetSessionVars().TimeZone = time.UTC - tk.MustQuery("select * from information_schema.PROCESSLIST order by ID;").Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 ", "in transaction", ""), - fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s abc2 0 0 07-29 03:26:05.158(410090409861578752) rg2 alias3", "autocommit", strings.Repeat("x", 101)), - )) - tk.MustQuery("SHOW PROCESSLIST;").Sort().Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", ""), - fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 100)), - )) - tk.MustQuery("SHOW FULL PROCESSLIST;").Sort().Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", ""), - fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 101)), - )) - tk.MustQuery("select * from information_schema.PROCESSLIST where db is null;").Check( - testkit.Rows( - fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s abc2 0 0 07-29 03:26:05.158(410090409861578752) rg2 alias3", "autocommit", strings.Repeat("x", 101)), - )) - tk.MustQuery("select * from information_schema.PROCESSLIST where Info is null;").Check( - testkit.Rows( - fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 ", "in transaction", ""), - )) -} - -func TestTableRowIDShardingInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("DROP DATABASE IF EXISTS `sharding_info_test_db`") - tk.MustExec("CREATE DATABASE `sharding_info_test_db`") - - assertShardingInfo := func(tableName string, expectInfo interface{}) { - querySQL := fmt.Sprintf("select tidb_row_id_sharding_info from information_schema.tables where table_schema = 'sharding_info_test_db' and table_name = '%s'", tableName) - info := tk.MustQuery(querySQL).Rows()[0][0] - if expectInfo == nil { - require.Equal(t, "", info) - } else { - require.Equal(t, expectInfo, info) - } - } - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t1` (a int)") - assertShardingInfo("t1", "NOT_SHARDED") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t2` (a int key)") - assertShardingInfo("t2", "NOT_SHARDED(PK_IS_HANDLE)") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t3` (a int) SHARD_ROW_ID_BITS=4") - assertShardingInfo("t3", "SHARD_BITS=4") - - tk.MustExec("CREATE VIEW `sharding_info_test_db`.`tv` AS select 1") - assertShardingInfo("tv", nil) - - testFunc := func(dbName string, expectInfo interface{}) { - dbInfo := model.DBInfo{Name: model.NewCIStr(dbName)} - tableInfo := model.TableInfo{} - - info := infoschema.GetShardingInfo(&dbInfo, &tableInfo) - require.Equal(t, expectInfo, info) - } - - testFunc("information_schema", nil) - testFunc("mysql", nil) - testFunc("performance_schema", nil) - testFunc("uucc", "NOT_SHARDED") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t4` (a bigint key clustered auto_random)") - assertShardingInfo("t4", "PK_AUTO_RANDOM_BITS=5") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t5` (a bigint key clustered auto_random(1))") - assertShardingInfo("t5", "PK_AUTO_RANDOM_BITS=1") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t6` (a bigint key clustered auto_random(2, 32))") - assertShardingInfo("t6", "PK_AUTO_RANDOM_BITS=2, RANGE BITS=32") - - tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t7` (a bigint key clustered auto_random(5, 64))") - assertShardingInfo("t7", "PK_AUTO_RANDOM_BITS=5") - - tk.MustExec("DROP DATABASE `sharding_info_test_db`") -} - -func TestSlowQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - // Prepare slow log file. - slowLogFileName := "tidb_slow.log" - internal.PrepareSlowLogfile(t, slowLogFileName) - defer func() { require.NoError(t, os.Remove(slowLogFileName)) }() - expectedRes := [][]interface{}{ - {"2019-02-12 19:33:56.571953", - "406315658548871171", - "root", - "localhost", - "6", - "", - "57", - "0.12", - "4.895492", - "0.4", - "0.2", - "0.000000003", - "2", - "0.000000002", - "0.00000001", - "0.000000003", - "0.19", - "0.21", - "0.01", - "0", - "0.18", - "[txnLock]", - "0.03", - "0", - "15", - "480", - "1", - "8", - "0.3824278", - "0.161", - "0.101", - "0.092", - "1.71", - "1", - "100001", - "100000", - "100", - "10", - "10", - "10", - "100", - "test", - "", - "0", - "42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772", - "t1:1,t2:2", - "0.1", - "0.2", - "0.03", - "127.0.0.1:20160", - "0.05", - "0.6", - "0.8", - "0.0.0.0:20160", - "70724", - "65536", - "0", - "0", - "0", - "0", - "10", - "", - "", - "0", - "1", - "0", - "0", - "1", - "0", - "0", - "abcd", - "60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4", - "", - "update t set i = 2;", - "select * from t_slim;"}, - {"2021-09-08 14:39:54.506967", - "427578666238083075", - "root", - "172.16.0.0", - "40507", - "alias123", - "0", - "0", - "25.571605962", - "0.002923536", - "0.006800973", - "0.002100764", - "0", - "0", - "0", - "0.000015801", - "25.542014572", - "0", - "0.002294647", - "0.000605473", - "12.483", - "[tikvRPC regionMiss tikvRPC regionMiss regionMiss]", - "0", - "0", - "624", - "172064", - "60", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "rtdb", - "", - "0", - "124acb3a0bec903176baca5f9da00b4e7512a41c93b417923f26502edeb324cc", - "", - "0", - "0", - "0", - "", - "0", - "0", - "0", - "", - "856544", - "0", - "86.635049185", - "0.015486658", - "100.054", - "0", - "0", - "", - "", - "0", - "1", - "0", - "0", - "0", - "0", - "0", - "", - "", - "", - "", - "INSERT INTO ...;", - }, - } - - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", slowLogFileName)) - tk.MustExec("set time_zone = '+08:00';") - re := tk.MustQuery("select * from information_schema.slow_query") - re.Check(expectedRes) - - tk.MustExec("set time_zone = '+00:00';") - re = tk.MustQuery("select * from information_schema.slow_query") - expectedRes[0][0] = "2019-02-12 11:33:56.571953" - expectedRes[1][0] = "2021-09-08 06:39:54.506967" - re.Check(expectedRes) - - // Test for long query. - f, err := os.OpenFile(slowLogFileName, os.O_CREATE|os.O_WRONLY, 0644) - require.NoError(t, err) - defer func() { require.NoError(t, f.Close()) }() - _, err = f.Write([]byte(` -# Time: 2019-02-13T19:33:56.571953+08:00 -`)) - require.NoError(t, err) - sql := "select * from " - for len(sql) < 5000 { - sql += "abcdefghijklmnopqrstuvwxyz_1234567890_qwertyuiopasdfghjklzxcvbnm" - } - sql += ";" - _, err = f.Write([]byte(sql)) - require.NoError(t, err) - re = tk.MustQuery("select query from information_schema.slow_query order by time desc limit 1") - rows := re.Rows() - require.Equal(t, sql, rows[0][0]) -} - -func TestTableIfHasColumn(t *testing.T) { - columnName := variable.SlowLogHasMoreResults - store := testkit.CreateMockStore(t) - slowLogFileName := "tidb-table-has-column-slow.log" - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Log.SlowQueryFile = slowLogFileName - }) - f, err := os.OpenFile(slowLogFileName, os.O_CREATE|os.O_WRONLY, 0644) - require.NoError(t, err) - _, err = f.Write([]byte(`# Time: 2019-02-12T19:33:56.571953+08:00 -# Txn_start_ts: 406315658548871171 -# User@Host: root[root] @ localhost [127.0.0.1] -# Has_more_results: true -INSERT INTO ...; -`)) - require.NoError(t, f.Close()) - require.NoError(t, err) - defer func() { require.NoError(t, os.Remove(slowLogFileName)) }() - tk := testkit.NewTestKit(t, store) - - // check schema - tk.MustQuery(`select COUNT(*) from information_schema.columns -WHERE table_name = 'slow_query' and column_name = '` + columnName + `'`). - Check(testkit.Rows("1")) - - // check select - tk.MustQuery(`select ` + columnName + - ` from information_schema.slow_query`).Check(testkit.Rows("1")) -} - -func TestReloadDropDatabase(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database test_dbs") - tk.MustExec("use test_dbs") - tk.MustExec("create table t1 (a int)") - tk.MustExec("create table t2 (a int)") - tk.MustExec("create table t3 (a int)") - is := domain.GetDomain(tk.Session()).InfoSchema() - t2, err := is.TableByName(model.NewCIStr("test_dbs"), model.NewCIStr("t2")) - require.NoError(t, err) - tk.MustExec("drop database test_dbs") - is = domain.GetDomain(tk.Session()).InfoSchema() - _, err = is.TableByName(model.NewCIStr("test_dbs"), model.NewCIStr("t2")) - require.True(t, terror.ErrorEqual(infoschema.ErrTableNotExists, err)) - _, ok := is.TableByID(t2.Meta().ID) - require.False(t, ok) -} - -func TestSystemSchemaID(t *testing.T) { - _, dom := testkit.CreateMockStoreAndDomain(t) - - uniqueIDMap := make(map[int64]string) - checkSystemSchemaTableID(t, dom, "information_schema", autoid.InformationSchemaDBID, 1, 10000, uniqueIDMap) - checkSystemSchemaTableID(t, dom, "performance_schema", autoid.PerformanceSchemaDBID, 10000, 20000, uniqueIDMap) - checkSystemSchemaTableID(t, dom, "metrics_schema", autoid.MetricSchemaDBID, 20000, 30000, uniqueIDMap) -} - -func checkSystemSchemaTableID(t *testing.T, dom *domain.Domain, dbName string, dbID, start, end int64, uniqueIDMap map[int64]string) { - is := dom.InfoSchema() - require.NotNil(t, is) - db, ok := is.SchemaByName(model.NewCIStr(dbName)) - require.True(t, ok) - require.Equal(t, dbID, db.ID) - // Test for information_schema table id. - tables := is.SchemaTables(model.NewCIStr(dbName)) - require.Greater(t, len(tables), 0) - for _, tbl := range tables { - tid := tbl.Meta().ID - require.Greaterf(t, tid&autoid.SystemSchemaIDFlag, int64(0), "table name is %v", tbl.Meta().Name) - require.Greaterf(t, tid&^autoid.SystemSchemaIDFlag, start, "table name is %v", tbl.Meta().Name) - require.Lessf(t, tid&^autoid.SystemSchemaIDFlag, end, "table name is %v", tbl.Meta().Name) - - name, ok := uniqueIDMap[tid] - require.Falsef(t, ok, "schema id of %v is duplicate with %v, both is %v", name, tbl.Meta().Name, tid) - uniqueIDMap[tid] = tbl.Meta().Name.O - } -} - -func TestSelectHiddenColumn(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("DROP DATABASE IF EXISTS `test_hidden`;") - tk.MustExec("CREATE DATABASE `test_hidden`;") - tk.MustExec("USE test_hidden;") - tk.MustExec("CREATE TABLE hidden (a int , b int, c int);") - tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("3")) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test_hidden"), model.NewCIStr("hidden")) - require.NoError(t, err) - colInfo := tb.Meta().Columns - // Set column b to hidden - colInfo[1].Hidden = true - tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("2")) - tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden' and column_name = 'b'").Check(testkit.Rows("0")) - // Set column b to visible - colInfo[1].Hidden = false - tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden' and column_name = 'b'").Check(testkit.Rows("1")) - // Set a, b ,c to hidden - colInfo[0].Hidden = true - colInfo[1].Hidden = true - colInfo[2].Hidden = true - tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("0")) -} - -func TestFormatVersion(t *testing.T) { - // Test for defaultVersions. - versions := []struct { - version string - expected string - userset bool - }{ - // default versions - {"5.7.25-TiDB-None", "None", true}, - {"5.7.25-TiDB-8.0.18", "8.0.18", true}, - {"5.7.25-TiDB-8.0.18-beta.1", "8.0.18-beta.1", true}, - {"5.7.25-TiDB-v4.0.0-beta-446-g5268094af", "4.0.0-beta-446-g5268094af", true}, - {"5.7.25-TiDB-", "", true}, - {"5.7.25-TiDB-v4.0.0-TiDB-446", "4.0.0-TiDB-446", true}, - // userset - {"8.0.18", "8.0.18", false}, - {"5.7.25-TiDB", "5.7.25-TiDB", false}, - {"8.0.18-TiDB-4.0.0-beta.1", "8.0.18-TiDB-4.0.0-beta.1", false}, - } - for _, tt := range versions { - version := infoschema.FormatTiDBVersion(tt.version, tt.userset) - require.Equal(t, tt.expected, version) - } -} - -func TestFormatStoreServerVersion(t *testing.T) { - versions := []string{"v4.0.12", "4.0.12", "v5.0.1"} - resultVersion := []string{"4.0.12", "4.0.12", "5.0.1"} - - for i, versionString := range versions { - require.Equal(t, infoschema.FormatStoreServerVersion(versionString), resultVersion[i]) - } -} - -// TestStmtSummaryTable Test statements_summary. -func TestStmtSummaryTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustQuery("select column_comment from information_schema.columns " + - "where table_name='STATEMENTS_SUMMARY' and column_name='STMT_TYPE'", - ).Check(testkit.Rows("Statement type")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) - - // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - // Test INSERT - tk.MustExec("insert into t values(1, 'a')") - tk.MustExec("insert into t values(2, 'b')") - tk.MustExec("insert into t VALUES(3, 'c')") - tk.MustExec("/**/insert into t values(4, 'd')") - - sql := "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text " + - "from information_schema.statements_summary " + - "where digest_text like 'insert into `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) - - // Test point get. - tk.MustExec("drop table if exists p") - tk.MustExec("create table p(a int primary key, b int)") - for i := 1; i < 3; i++ { - tk.MustQuery("select b from p where a=1") - expectedResult := fmt.Sprintf("%d \tid \ttask\testRows\toperator info\n\tPoint_Get_1\troot\t1 \ttable:p, handle:1 %s", i, "test.p") - // Also make sure that the plan digest is not empty - sql = "select exec_count, plan, table_names from information_schema.statements_summary " + - "where digest_text like 'select `b` from `p`%' and plan_digest != ''" - tk.MustQuery(sql).Check(testkit.Rows(expectedResult)) - } - - // Point get another database. - tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'") - // Test for Encode plan cache. - p1 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() - require.Greater(t, len(p1), 0) - rows := tk.MustQuery("select tidb_decode_plan('" + p1 + "');").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, 1, len(rows[0])) - require.Regexp(t, "\n.*Point_Get.*table.tidb, index.PRIMARY.VARIABLE_NAME", rows[0][0]) - - sql = "select table_names from information_schema.statements_summary " + - "where digest_text like 'select `variable_value`%' and `schema_name`='test'" - tk.MustQuery(sql).Check(testkit.Rows("mysql.tidb")) - - // Test `create database`. - tk.MustExec("create database if not exists test") - // Test for Encode plan cache. - p2 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() - require.Equal(t, "", p2) - tk.MustQuery(`select table_names - from information_schema.statements_summary - where digest_text like 'create database%' and schema_name='test'`, - ).Check(testkit.Rows("")) - - // Test SELECT. - const failpointName = "github.com/pingcap/tidb/planner/core/mockPlanRowCount" - require.NoError(t, failpoint.Enable(failpointName, "return(100)")) - defer func() { require.NoError(t, failpoint.Disable(failpointName)) }() - tk.MustQuery("select * from t where a=2") - - // sum_cop_task_num is always 0 if tidb_enable_collect_execution_info disabled - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t100 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) - - // select ... order by - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text - from information_schema.statements_summary - order by exec_count desc limit 1`, - ).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) - - // Test different plans with same digest. - require.NoError(t, failpoint.Enable(failpointName, "return(1000)")) - tk.MustQuery("select * from t where a=3") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows( - "Select test test.t t:k 2 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t100 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) - - // Disable it again. - tk.MustExec("set global tidb_enable_stmt_summary = false") - defer tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) - - // Create a new session to test - tk = newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - // This statement shouldn't be summarized. - tk.MustQuery("select * from t where a=2") - - // The table should be cleared. - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, plan - from information_schema.statements_summary`, - ).Check(testkit.Rows()) - - tk.MustExec("SET GLOBAL tidb_enable_stmt_summary = on") - // It should work immediately. - tk.MustExec("begin") - tk.MustExec("insert into t values(1, 'a')") - tk.MustExec("commit") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text " + - "from information_schema.statements_summary " + - "where digest_text like 'insert into `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') ")) - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text - from information_schema.statements_summary - where digest_text='commit'`, - ).Check(testkit.Rows("Commit test 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a')")) - - tk.MustQuery("select * from t where a=2") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t1000 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t1000 \ttable:t, keep order:false, stats:pseudo")) - - // Disable it in global scope. - tk.MustExec("set global tidb_enable_stmt_summary = false") - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - - // Statement summary is disabled. - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, plan - from information_schema.statements_summary`, - ).Check(testkit.Rows()) - - tk.MustExec("set global tidb_enable_stmt_summary = on") - tk.MustExec("set global tidb_stmt_summary_history_size = 24") -} - -func TestStmtSummaryTablePrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - result := tk.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - result = tk.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Ordinary users can not see others' records - require.Equal(t, 0, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 0, len(result.Rows())) - tk1.MustExec("select * from t where b=1") - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Ordinary users can see his own records - require.Equal(t, 1, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - - tk.MustExec("grant process on *.* to 'test_user'@'localhost'") - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Users with 'PROCESS' privileges can query all records. - require.Equal(t, 2, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 2, len(result.Rows())) -} - -func TestCapturePrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t1") - - // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - tk.MustExec("select * from t where a=1") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - rows = tk1.MustQuery("show global bindings").Rows() - // Ordinary users can not see others' records - require.Len(t, rows, 0) - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk.MustExec("grant all on *.* to 'test_user'@'localhost'") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 2) -} - -// TestStmtSummaryInternalQuery Test statements_summary_history. -func TestStmtSummaryInternalQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - originalVal := config.CheckTableBeforeDrop - config.CheckTableBeforeDrop = true - defer func() { - config.CheckTableBeforeDrop = originalVal - }() - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - - // We use the sql binding evolve to check the internal query summary. - tk.MustExec("set @@tidb_use_plan_baselines = 1") - tk.MustExec("set @@tidb_evolve_plan_baselines = 1") - tk.MustExec("create global binding for select * from t where t.a = 1 using select * from t ignore index(k) where t.a = 1") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) - // Disable refreshing summary. - tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") - tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) - - // Test Internal - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - - tk.MustExec("select * from t where t.a = 1") - tk.MustQuery(`select exec_count, digest_text - from information_schema.statements_summary - where digest_text like "select original_sql , bind_sql , default_db , status%"`).Check(testkit.Rows()) - - // Enable internal query and evolve baseline. - tk.MustExec("set global tidb_stmt_summary_internal_query = 1") - defer tk.MustExec("set global tidb_stmt_summary_internal_query = false") - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - - tk.MustExec("admin flush bindings") - tk.MustExec("admin evolve bindings") - - // `exec_count` may be bigger than 1 because other cases are also running. - sql := "select digest_text " + - "from information_schema.statements_summary " + - "where digest_text like \"select `original_sql` , `bind_sql` , `default_db` , status%\"" - tk.MustQuery(sql).Check(testkit.Rows( - "select `original_sql` , `bind_sql` , `default_db` , status , `create_time` , `update_time` , charset , " + - "collation , source , `sql_digest` , `plan_digest` from `mysql` . `bind_info` where `update_time` > ? order by `update_time` , `create_time`")) - - // Test for issue #21642. - tk.MustQuery(`select tidb_version()`) - rows := tk.MustQuery("select plan from information_schema.statements_summary where digest_text like \"select `tidb_version`%\"").Rows() - require.Contains(t, rows[0][0].(string), "Projection") -} - -// TestSimpleStmtSummaryEvictedCount test stmtSummaryEvictedCount -func TestSimpleStmtSummaryEvictedCount(t *testing.T) { - store := testkit.CreateMockStore(t) - - now := time.Now().Unix() - interval := int64(1800) - beginTimeForCurInterval := now - now%interval - tk := newTestKitWithPlanCache(t, store) - tk.MustExec(fmt.Sprintf("set global tidb_stmt_summary_refresh_interval = %v", interval)) - - // no evict happens now, evicted count should be empty - tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("0")) - - // clean up side effects - defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 100") - defer tk.MustExec("set global tidb_stmt_summary_refresh_interval = 1800") - - tk.MustExec("set global tidb_enable_stmt_summary = 0") - // statements summary evicted is also disabled when set tidb_enable_stmt_summary to off - tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("0")) - tk.MustExec("set global tidb_enable_stmt_summary = 1") - // first sql - tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 1") - // second sql - tk.MustQuery("show databases;") - // query `evicted table` is also a SQL, passing it leads to the eviction of the previous SQLs. - tk.MustQuery("select * from `information_schema`.`STATEMENTS_SUMMARY_EVICTED`;"). - Check(testkit.Rows( - fmt.Sprintf("%s %s %v", - time.Unix(beginTimeForCurInterval, 0).Format(time.DateTime), - time.Unix(beginTimeForCurInterval+interval, 0).Format(time.DateTime), - int64(2)), - )) - - // test too much intervals - tk.MustExec("use test;") - tk.MustExec(fmt.Sprintf("set @@global.tidb_stmt_summary_refresh_interval=%v", interval)) - tk.MustExec("set @@global.tidb_enable_stmt_summary=0") - tk.MustExec("set @@global.tidb_enable_stmt_summary=1") - historySize := 24 - fpPath := "github.com/pingcap/tidb/util/stmtsummary/mockTimeForStatementsSummary" - for i := int64(0); i < 100; i++ { - err := failpoint.Enable(fpPath, fmt.Sprintf(`return("%v")`, time.Now().Unix()+interval*i)) - if err != nil { - panic(err.Error()) - } - tk.MustExec(fmt.Sprintf("create table if not exists th%v (p bigint key, q int);", i)) - } - err := failpoint.Disable(fpPath) - if err != nil { - panic(err.Error()) - } - tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;"). - Check(testkit.Rows(fmt.Sprintf("%v", historySize))) - - // test discrete intervals - tk.MustExec("set @@global.tidb_enable_stmt_summary=0") - tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=1;") - tk.MustExec("set @@global.tidb_enable_stmt_summary=1") - for i := int64(0); i < 3; i++ { - tk.MustExec(fmt.Sprintf("select count(*) from th%v", i)) - } - err = failpoint.Enable(fpPath, fmt.Sprintf(`return("%v")`, time.Now().Unix()+2*interval)) - if err != nil { - panic(err.Error()) - } - for i := int64(0); i < 3; i++ { - tk.MustExec(fmt.Sprintf("select count(*) from th%v", i)) - } - tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("2")) - tk.MustQuery("select BEGIN_TIME from information_schema.statements_summary_evicted;"). - Check(testkit. - Rows(time.Unix(beginTimeForCurInterval+2*interval, 0).Format(time.DateTime), - time.Unix(beginTimeForCurInterval, 0).Format(time.DateTime))) - require.NoError(t, failpoint.Disable(fpPath)) - // TODO: Add more tests. -} - -func TestStmtSummaryEvictedPointGet(t *testing.T) { - store := testkit.CreateMockStore(t) - - interval := int64(1800) - tk := newTestKitWithRoot(t, store) - tk.MustExec(fmt.Sprintf("set global tidb_stmt_summary_refresh_interval=%v;", interval)) - tk.MustExec("create database point_get;") - tk.MustExec("use point_get;") - for i := 0; i < 6; i++ { - tk.MustExec(fmt.Sprintf("create table if not exists th%v ("+ - "p bigint key,"+ - "q int);", i)) - } - - tk.MustExec("set @@global.tidb_enable_stmt_summary=0;") - tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=5;") - defer tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=100;") - // first SQL - tk.MustExec("set @@global.tidb_enable_stmt_summary=1;") - - for i := int64(0); i < 1000; i++ { - // six SQLs - tk.MustExec(fmt.Sprintf("select p from th%v where p=2333;", i%6)) - } - tk.MustQuery("select EVICTED_COUNT from information_schema.statements_summary_evicted;"). - Check(testkit.Rows("7")) - - tk.MustExec("set @@global.tidb_enable_stmt_summary=0;") - tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;"). - Check(testkit.Rows("0")) - tk.MustExec("set @@global.tidb_enable_stmt_summary=1;") -} - -func TestServerInfoResolveLoopBackAddr(t *testing.T) { - nodes := []infoschema.ServerInfo{ - {Address: "127.0.0.1:4000", StatusAddr: "192.168.130.22:10080"}, - {Address: "0.0.0.0:4000", StatusAddr: "192.168.130.22:10080"}, - {Address: "localhost:4000", StatusAddr: "192.168.130.22:10080"}, - {Address: "192.168.130.22:4000", StatusAddr: "0.0.0.0:10080"}, - {Address: "192.168.130.22:4000", StatusAddr: "127.0.0.1:10080"}, - {Address: "192.168.130.22:4000", StatusAddr: "localhost:10080"}, - } - for i := range nodes { - nodes[i].ResolveLoopBackAddr() - } - for _, n := range nodes { - require.Equal(t, "192.168.130.22:4000", n.Address) - require.Equal(t, "192.168.130.22:10080", n.StatusAddr) - } -} - -func TestInfoSchemaClientErrors(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - - tk.MustExec("FLUSH CLIENT_ERRORS_SUMMARY") - - errno.IncrementError(1365, "root", "localhost") - errno.IncrementError(1365, "infoschematest", "localhost") - errno.IncrementError(1365, "root", "localhost") - - tk.MustExec("CREATE USER 'infoschematest'@'localhost'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "infoschematest", Hostname: "localhost"}, nil, nil, nil)) - - err := tk.QueryToErr("SELECT * FROM information_schema.client_errors_summary_global") - require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation", err.Error()) - - err = tk.QueryToErr("SELECT * FROM information_schema.client_errors_summary_by_host") - require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation", err.Error()) - - tk.MustQuery("SELECT error_number, error_count, warning_count FROM information_schema.client_errors_summary_by_user ORDER BY error_number").Check(testkit.Rows("1365 1 0")) - - err = tk.ExecToErr("FLUSH CLIENT_ERRORS_SUMMARY") - require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the RELOAD privilege(s) for this operation", err.Error()) -} - -func TestTiDBTrx(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - tk.MustExec("drop table if exists test_tidb_trx") - tk.MustExec("create table test_tidb_trx(i int)") - // Execute the statement once so that the statement will be collected into statements_summary and able to be found - // by digest. - tk.MustExec("update test_tidb_trx set i = i + 1") - _, digest := parser.NormalizeDigest("update test_tidb_trx set i = i + 1") - sm := &testkit.MockSessionManager{TxnInfo: make([]*txninfo.TxnInfo, 2)} - memDBTracker := memory.NewTracker(memory.LabelForMemDB, -1) - memDBTracker.Consume(19) - tk.Session().GetSessionVars().MemDBFootprint = memDBTracker - sm.TxnInfo[0] = &txninfo.TxnInfo{ - StartTS: 424768545227014155, - CurrentSQLDigest: digest.String(), - State: txninfo.TxnIdle, - EntriesCount: 1, - ConnectionID: 2, - Username: "root", - CurrentDB: "test", - } - - blockTime2 := time.Date(2021, 05, 20, 13, 18, 30, 123456000, time.Local) - sm.TxnInfo[1] = &txninfo.TxnInfo{ - StartTS: 425070846483628033, - CurrentSQLDigest: "", - AllSQLDigests: []string{"sql1", "sql2", digest.String()}, - State: txninfo.TxnLockAcquiring, - ConnectionID: 10, - Username: "user1", - CurrentDB: "db1", - } - sm.TxnInfo[1].BlockStartTime.Valid = true - sm.TxnInfo[1].BlockStartTime.Time = blockTime2 - tk.Session().SetSessionManager(sm) - - tk.MustQuery(`select ID, - START_TIME, - CURRENT_SQL_DIGEST, - CURRENT_SQL_DIGEST_TEXT, - STATE, - WAITING_START_TIME, - MEM_BUFFER_KEYS, - MEM_BUFFER_BYTES, - SESSION_ID, - USER, - DB, - ALL_SQL_DIGESTS, - RELATED_TABLE_IDS - from information_schema.TIDB_TRX`).Check(testkit.Rows( - "424768545227014155 2021-05-07 12:56:48.001000 "+digest.String()+" update `test_tidb_trx` set `i` = `i` + ? Idle 1 19 2 root test [] ", - "425070846483628033 2021-05-20 21:16:35.778000 LockWaiting 2021-05-20 13:18:30.123456 0 19 10 user1 db1 [\"sql1\",\"sql2\",\""+digest.String()+"\"] ")) - - rows := tk.MustQuery(`select WAITING_TIME from information_schema.TIDB_TRX where WAITING_TIME is not null`) - require.Len(t, rows.Rows(), 1) - - // Test the all_sql_digests column can be directly passed to the tidb_decode_sql_digests function. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/sqlDigestRetrieverSkipRetrieveGlobal", "return")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/sqlDigestRetrieverSkipRetrieveGlobal")) - }() - tk.MustQuery("select tidb_decode_sql_digests(all_sql_digests) from information_schema.tidb_trx").Check(testkit.Rows( - "[]", - "[null,null,\"update `test_tidb_trx` set `i` = `i` + ?\"]")) -} - -func TestTiDBTrxSummary(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := newTestKitWithRoot(t, store) - tk.MustExec("drop table if exists test_tidb_trx") - tk.MustExec("create table test_tidb_trx(i int)") - _, beginDigest := parser.NormalizeDigest("begin") - _, digest := parser.NormalizeDigest("update test_tidb_trx set i = i + 1") - _, commitDigest := parser.NormalizeDigest("commit") - txninfo.Recorder.Clean() - txninfo.Recorder.SetMinDuration(500 * time.Millisecond) - defer txninfo.Recorder.SetMinDuration(2147483647) - txninfo.Recorder.ResizeSummaries(128) - defer txninfo.Recorder.ResizeSummaries(0) - tk.MustExec("begin") - tk.MustExec("update test_tidb_trx set i = i + 1") - time.Sleep(1 * time.Second) - tk.MustExec("update test_tidb_trx set i = i + 1") - tk.MustExec("commit") - // it is possible for TRX_SUMMARY to have other rows (due to parallel execution of tests) - for _, row := range tk.MustQuery("select * from information_schema.TRX_SUMMARY;").Rows() { - // so we just look for the row we are looking for - if row[0] == "1bb679108d0012a8" { - require.Equal(t, strings.TrimSpace(row[1].(string)), "[\""+beginDigest.String()+"\",\""+digest.String()+"\",\""+digest.String()+"\",\""+commitDigest.String()+"\"]") - return - } - } - t.Fatal("cannot find the expected row") -} - -func TestAttributes(t *testing.T) { - store := testkit.CreateMockStore(t) - - // test the failpoint for testing - fpName := "github.com/pingcap/tidb/executor/mockOutputOfAttributes" - tk := newTestKitWithRoot(t, store) - tk.MustQuery("select * from information_schema.attributes").Check(testkit.Rows()) - - require.NoError(t, failpoint.Enable(fpName, "return")) - defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - - tk.MustQuery(`select * from information_schema.attributes`).Check(testkit.Rows( - `schema/test/test_label key-range "merge_option=allow" [7480000000000000ff395f720000000000fa, 7480000000000000ff3a5f720000000000fa]`, - )) -} - -func TestMemoryUsageAndOpsHistory(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner")) - }() - gctuner.GlobalMemoryLimitTuner.Start() - defer func() { - time.Sleep(1 * time.Second) // Wait tuning finished. - }() - tk.MustExec("set global tidb_mem_oom_action = 'CANCEL'") - tk.MustExec("set global tidb_server_memory_limit=512<<20") - tk.MustExec("set global tidb_enable_tmp_storage_on_oom=off") - dom, err := session.GetDomain(store) - require.Nil(t, err) - go dom.ServerMemoryLimitHandle().SetSessionManager(tk.Session().GetSessionManager()).Run() - // OOM - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tk.MustExec("insert into t values(1)") - for i := 0; i < 9; i++ { - tk.MustExec("insert into t select * from t;") - } - - var tmp string - var ok bool - var beginTime = time.Now().Format(types.TimeFormat) - err = tk.QueryToErr("explain analyze select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a") - var endTime = time.Now().Format(types.TimeFormat) - require.NotNil(t, err) - // Check Memory Table - rows := tk.MustQuery("select * from INFORMATION_SCHEMA.MEMORY_USAGE").Rows() - require.Len(t, rows, 1) - row := rows[0] - require.Len(t, row, 11) - require.Equal(t, row[0], strconv.FormatUint(memory.GetMemTotalIgnoreErr(), 10)) // MEMORY_TOTAL - require.Equal(t, row[1], "536870912") // MEMORY_LIMIT - require.Greater(t, row[2], "0") // MEMORY_CURRENT - tmp, ok = row[3].(string) // MEMORY_MAX_USED - require.Equal(t, ok, true) - val, err := strconv.ParseUint(tmp, 10, 64) - require.Nil(t, err) - require.Greater(t, val, uint64(536870912)) - - tmp, ok = row[4].(string) // CURRENT_OPS - require.Equal(t, ok, true) - if tmp != "null" && tmp != "shrink" { - require.Fail(t, "CURRENT_OPS get wrong value") - } - require.GreaterOrEqual(t, row[5], beginTime) // SESSION_KILL_LAST - require.LessOrEqual(t, row[5], endTime) - require.Greater(t, row[6], "0") // SESSION_KILL_TOTAL - require.GreaterOrEqual(t, row[7], beginTime) // GC_LAST - require.LessOrEqual(t, row[7], endTime) - require.Greater(t, row[8], "0") // GC_TOTAL - require.Equal(t, row[9], "0") // DISK_USAGE - require.Equal(t, row[10], "0") // QUERY_FORCE_DISK - - rows = tk.MustQuery("select * from INFORMATION_SCHEMA.MEMORY_USAGE_OPS_HISTORY").Rows() - require.Greater(t, len(rows), 0) - row = rows[len(rows)-1] - require.Len(t, row, 12) - require.GreaterOrEqual(t, row[0], beginTime) // TIME - require.LessOrEqual(t, row[0], endTime) - require.Equal(t, row[1], "SessionKill") // OPS - require.Equal(t, row[2], "536870912") // MEMORY_LIMIT - tmp, ok = row[3].(string) // MEMORY_CURRENT - require.Equal(t, ok, true) - val, err = strconv.ParseUint(tmp, 10, 64) - require.Nil(t, err) - require.Greater(t, val, uint64(536870912)) - - require.Greater(t, row[4], "0") // PROCESSID - require.Greater(t, row[5], "0") // MEM - require.Equal(t, row[6], "0") // DISK - require.Equal(t, row[7], "") // CLIENT - require.Equal(t, row[8], "test") // DB - require.Equal(t, row[9], "") // USER - require.Equal(t, row[10], "e3237ec256015a3566757e0c2742507cd30ae04e4cac2fbc14d269eafe7b067b") // SQL_DIGEST - require.Equal(t, row[11], "explain analyze select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a") // SQL_TEXT -} - -func TestAddFieldsForBinding(t *testing.T) { - s := new(clusterTablesSuite) - s.store, s.dom = testkit.CreateMockStoreAndDomain(t) - s.rpcserver, s.listenAddr = s.setUpRPCService(t, "127.0.0.1:0", nil) - s.httpServer, s.mockAddr = s.setUpMockPDHTTPServer() - s.startTime = time.Now() - defer s.httpServer.Close() - defer s.rpcserver.Stop() - tk := s.newTestKitWithRoot(t) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, key(a))") - tk.MustExec("select /*+ ignore_index(t, a)*/ * from t where a = 1") - planDigest := "4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb" - rows := tk.MustQuery(fmt.Sprintf("select stmt_type, prepared, sample_user, schema_name, query_sample_text, charset, collation, plan_hint, digest_text "+ - "from information_schema.cluster_statements_summary where plan_digest = '%s'", planDigest)).Rows() - - require.Equal(t, rows[0][0], "Select") - require.Equal(t, rows[0][1], "0") - require.Equal(t, rows[0][2], "root") - require.Equal(t, rows[0][3], "test") - require.Equal(t, rows[0][4], "select /*+ ignore_index(t, a)*/ * from t where a = 1") - require.Equal(t, rows[0][5], "utf8mb4") - require.Equal(t, rows[0][6], "utf8mb4_bin") - require.Equal(t, rows[0][7], "use_index(@`sel_1` `test`.`t` ), ignore_index(`t` `a`)") - require.Equal(t, rows[0][8], "select * from `t` where `a` = ?") -} diff --git a/keyspace/BUILD.bazel b/keyspace/BUILD.bazel deleted file mode 100644 index df238f36945a3..0000000000000 --- a/keyspace/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "keyspace", - srcs = ["keyspace.go"], - importpath = "github.com/pingcap/tidb/keyspace", - visibility = ["//visibility:public"], - deps = [ - "//config", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "keyspace_test", - timeout = "short", - srcs = ["keyspace_test.go"], - embed = [":keyspace"], - flaky = True, - deps = [ - "//config", - "@com_github_stretchr_testify//require", - ], -) diff --git a/kv/BUILD.bazel b/kv/BUILD.bazel deleted file mode 100644 index 76bc2d668d1b4..0000000000000 --- a/kv/BUILD.bazel +++ /dev/null @@ -1,100 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "kv", - srcs = [ - "cachedb.go", - "checker.go", - "error.go", - "fault_injection.go", - "iter.go", - "key.go", - "keyflags.go", - "kv.go", - "mpp.go", - "option.go", - "txn.go", - "txn_scope_var.go", - "utils.go", - "variables.go", - "version.go", - ], - importpath = "github.com/pingcap/tidb/kv", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//domain/resourcegroup", - "//errno", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//types", - "//util/codec", - "//util/dbterror", - "//util/intest", - "//util/logutil", - "//util/memory", - "//util/set", - "//util/tiflash", - "//util/tiflashcompute", - "//util/trxevents", - "@com_github_coocood_freecache//:freecache", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "kv_test", - timeout = "short", - srcs = [ - "checker_test.go", - "error_test.go", - "fault_injection_test.go", - "interface_mock_test.go", - "key_test.go", - "main_test.go", - "mock_test.go", - "option_test.go", - "txn_test.go", - "utils_test.go", - "version_test.go", - ], - embed = [":kv"], - flaky = True, - shard_count = 23, - deps = [ - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util/codec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/kv/error.go b/kv/error.go deleted file mode 100644 index d8c344324fe80..0000000000000 --- a/kv/error.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package kv - -import ( - "strings" - - mysql "github.com/pingcap/tidb/errno" - pmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbterror" -) - -// TxnRetryableMark is used to uniform the commit error messages which could retry the transaction. -// *WARNING*: changing this string will affect the backward compatibility. -const TxnRetryableMark = "[try again later]" - -var ( - // ErrNotExist is used when try to get an entry with an unexist key from KV store. - ErrNotExist = dbterror.ClassKV.NewStd(mysql.ErrNotExist) - // ErrTxnRetryable is used when KV store occurs retryable error which SQL layer can safely retry the transaction. - // When using TiKV as the storage node, the error is returned ONLY when lock not found (txnLockNotFound) in Commit, - // subject to change it in the future. - ErrTxnRetryable = dbterror.ClassKV.NewStdErr( - mysql.ErrTxnRetryable, - pmysql.Message( - mysql.MySQLErrName[mysql.ErrTxnRetryable].Raw+TxnRetryableMark, - mysql.MySQLErrName[mysql.ErrTxnRetryable].RedactArgPos, - ), - ) - // ErrCannotSetNilValue is the error when sets an empty value. - ErrCannotSetNilValue = dbterror.ClassKV.NewStd(mysql.ErrCannotSetNilValue) - // ErrInvalidTxn is the error when commits or rollbacks in an invalid transaction. - ErrInvalidTxn = dbterror.ClassKV.NewStd(mysql.ErrInvalidTxn) - // ErrTxnTooLarge is the error when transaction is too large, lock time reached the maximum value. - ErrTxnTooLarge = dbterror.ClassKV.NewStd(mysql.ErrTxnTooLarge) - // ErrEntryTooLarge is the error when a key value entry is too large. - ErrEntryTooLarge = dbterror.ClassKV.NewStd(mysql.ErrEntryTooLarge) - // ErrKeyExists returns when key is already exist. - ErrKeyExists = dbterror.ClassKV.NewStd(mysql.ErrDupEntry) - // ErrNotImplemented returns when a function is not implemented yet. - ErrNotImplemented = dbterror.ClassKV.NewStd(mysql.ErrNotImplemented) - // ErrWriteConflict is the error when the commit meets an write conflict error. - ErrWriteConflict = dbterror.ClassKV.NewStdErr( - mysql.ErrWriteConflict, - pmysql.Message( - mysql.MySQLErrName[mysql.ErrWriteConflict].Raw+" "+TxnRetryableMark, - mysql.MySQLErrName[mysql.ErrWriteConflict].RedactArgPos, - ), - ) - // ErrWriteConflictInTiDB is the error when the commit meets an write conflict error when local latch is enabled. - ErrWriteConflictInTiDB = dbterror.ClassKV.NewStdErr( - mysql.ErrWriteConflictInTiDB, - pmysql.Message( - mysql.MySQLErrName[mysql.ErrWriteConflictInTiDB].Raw+" "+TxnRetryableMark, - mysql.MySQLErrName[mysql.ErrWriteConflictInTiDB].RedactArgPos, - ), - ) - // ErrLockExpire is the error when the lock is expired. - ErrLockExpire = dbterror.ClassTiKV.NewStd(mysql.ErrLockExpire) - // ErrAssertionFailed is the error when an assertion fails. - ErrAssertionFailed = dbterror.ClassTiKV.NewStd(mysql.ErrAssertionFailed) -) - -// IsTxnRetryableError checks if the error could safely retry the transaction. -func IsTxnRetryableError(err error) bool { - if err == nil { - return false - } - - if ErrTxnRetryable.Equal(err) || ErrWriteConflict.Equal(err) || ErrWriteConflictInTiDB.Equal(err) { - return true - } - - return false -} - -// IsErrNotFound checks if err is a kind of NotFound error. -func IsErrNotFound(err error) bool { - return ErrNotExist.Equal(err) -} - -// GetDuplicateErrorHandleString is used to concat the handle columns data with '-'. -// This is consistent with MySQL. -func GetDuplicateErrorHandleString(handle Handle) string { - dt, err := handle.Data() - if err != nil { - return err.Error() - } - var sb strings.Builder - for i, d := range dt { - if i != 0 { - sb.WriteString("-") - } - s, err := d.ToString() - if err != nil { - return err.Error() - } - sb.WriteString(s) - } - return sb.String() -} diff --git a/kv/error_test.go b/kv/error_test.go deleted file mode 100644 index e9842e63749df..0000000000000 --- a/kv/error_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package kv - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/assert" -) - -func TestError(t *testing.T) { - kvErrs := []*terror.Error{ - ErrNotExist, - ErrTxnRetryable, - ErrCannotSetNilValue, - ErrInvalidTxn, - ErrTxnTooLarge, - ErrEntryTooLarge, - ErrNotImplemented, - ErrWriteConflict, - ErrWriteConflictInTiDB, - } - - for _, err := range kvErrs { - code := terror.ToSQLError(err).Code - assert.NotEqual(t, mysql.ErrUnknown, code) - assert.Equal(t, uint16(err.Code()), code) - } -} diff --git a/kv/main_test.go b/kv/main_test.go deleted file mode 100644 index b7a2f3d8cf567..0000000000000 --- a/kv/main_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package kv - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/kv/mpp.go b/kv/mpp.go deleted file mode 100644 index 402309fd61619..0000000000000 --- a/kv/mpp.go +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package kv - -import ( - "context" - "strconv" - "strings" - "time" - - "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/tiflashcompute" - "github.com/pingcap/tipb/go-tipb" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" -) - -// MppVersion indicates the mpp-version used to build mpp plan -type MppVersion int64 - -const ( - // MppVersionV0 supports TiFlash version [~, ~] - MppVersionV0 MppVersion = iota - - // MppVersionV1 supports TiFlash version [v6.6.x, ~] - MppVersionV1 - - // MppVersionV2 supports TiFlash version [v7.3, ~], support ReportMPPTaskStatus service - MppVersionV2 - // MppVersionV3 - - mppVersionMax - - newestMppVersion MppVersion = mppVersionMax - 1 - - // MppVersionUnspecified means the illegal or unspecified version, it only used in TiDB. - MppVersionUnspecified MppVersion = -1 - - // MppVersionUnspecifiedName denotes name of UNSPECIFIED mpp version - MppVersionUnspecifiedName string = "UNSPECIFIED" -) - -// ToInt64 transforms MppVersion to int64 -func (v MppVersion) ToInt64() int64 { - return int64(v) -} - -// ToMppVersion transforms string to MppVersion -func ToMppVersion(name string) (MppVersion, bool) { - name = strings.ToUpper(name) - if name == MppVersionUnspecifiedName { - return MppVersionUnspecified, true - } - v, err := strconv.ParseInt(name, 10, 64) - if err != nil { - return MppVersionUnspecified, false - } - version := MppVersion(v) - if version >= MppVersionUnspecified && version <= newestMppVersion { - return version, true - } - return MppVersionUnspecified, false -} - -// GetNewestMppVersion returns the mpp-version can be used in mpp plan -func GetNewestMppVersion() MppVersion { - return newestMppVersion -} - -// MPPTaskMeta means the meta info such as location of a mpp task. -type MPPTaskMeta interface { - // GetAddress indicates which node this task should execute on. - GetAddress() string -} - -// MPPQueryID means the global unique id of a mpp query. -type MPPQueryID struct { - QueryTs uint64 // timestamp of query execution, used for TiFlash minTSO schedule - LocalQueryID uint64 // unique mpp query id in local tidb memory. - ServerID uint64 -} - -// MPPTask means the minimum execution unit of a mpp computation job. -type MPPTask struct { - Meta MPPTaskMeta // on which store this task will execute - ID int64 // mppTaskID - StartTs uint64 - GatherID uint64 - MppQueryID MPPQueryID - TableID int64 // physical table id - MppVersion MppVersion // mpp version - - PartitionTableIDs []int64 - TiFlashStaticPrune bool -} - -// ToPB generates the pb structure. -func (t *MPPTask) ToPB() *mpp.TaskMeta { - meta := &mpp.TaskMeta{ - StartTs: t.StartTs, - GatherId: t.GatherID, - QueryTs: t.MppQueryID.QueryTs, - LocalQueryId: t.MppQueryID.LocalQueryID, - ServerId: t.MppQueryID.ServerID, - TaskId: t.ID, - MppVersion: t.MppVersion.ToInt64(), - } - if t.ID != -1 { - meta.Address = t.Meta.GetAddress() - } - return meta -} - -// MppTaskStates denotes the state of mpp tasks -type MppTaskStates uint8 - -const ( - // MppTaskReady means the task is ready - MppTaskReady MppTaskStates = iota - // MppTaskRunning means the task is running - MppTaskRunning - // MppTaskCancelled means the task is cancelled - MppTaskCancelled - // MppTaskDone means the task is done - MppTaskDone -) - -// MPPDispatchRequest stands for a dispatching task. -type MPPDispatchRequest struct { - Data []byte // data encodes the dag coprocessor request. - Meta MPPTaskMeta // mpp store is the location of tiflash store. - IsRoot bool // root task returns data to tidb directly. - Timeout uint64 // If task is assigned but doesn't receive a connect request during timeout, the task should be destroyed. - // SchemaVer is for any schema-ful storage (like tiflash) to validate schema correctness if necessary. - SchemaVar int64 - StartTs uint64 - MppQueryID MPPQueryID - GatherID uint64 - ID int64 // identify a single task - MppVersion MppVersion - CoordinatorAddress string - ReportExecutionSummary bool - State MppTaskStates - ResourceGroupName string -} - -// CancelMPPTasksParam represents parameter for MPPClient's CancelMPPTasks -type CancelMPPTasksParam struct { - StoreAddr map[string]bool - Reqs []*MPPDispatchRequest -} - -// EstablishMPPConnsParam represents parameter for MPPClient's EstablishMPPConns -type EstablishMPPConnsParam struct { - Ctx context.Context - Req *MPPDispatchRequest - TaskMeta *mpp.TaskMeta -} - -// DispatchMPPTaskParam represents parameter for MPPClient's DispatchMPPTask -type DispatchMPPTaskParam struct { - Ctx context.Context - Req *MPPDispatchRequest - EnableCollectExecutionInfo bool - Bo *tikv.Backoffer -} - -// MPPClient accepts and processes mpp requests. -type MPPClient interface { - // ConstructMPPTasks schedules task for a plan fragment. - // TODO:: This interface will be refined after we support more executors. - ConstructMPPTasks(context.Context, *MPPBuildTasksRequest, time.Duration, tiflashcompute.DispatchPolicy, tiflash.ReplicaRead, func(error)) ([]MPPTaskMeta, error) - - // DispatchMPPTask dispatch mpp task, and returns valid response when retry = false and err is nil. - DispatchMPPTask(DispatchMPPTaskParam) (resp *mpp.DispatchTaskResponse, retry bool, err error) - - // EstablishMPPConns build a mpp connection to receive data, return valid response when err is nil. - EstablishMPPConns(EstablishMPPConnsParam) (*tikvrpc.MPPStreamResponse, error) - - // CancelMPPTasks cancels mpp tasks. - CancelMPPTasks(CancelMPPTasksParam) - - // CheckVisibility checks if it is safe to read using given ts. - CheckVisibility(startTime uint64) error - - // GetMPPStoreCount returns number of TiFlash stores if there is no error, else return (0, error). - GetMPPStoreCount() (int, error) -} - -// ReportStatusRequest wraps mpp ReportStatusRequest -type ReportStatusRequest struct { - Request *mpp.ReportTaskStatusRequest -} - -// MppCoordinator describes the basic api for executing mpp physical plan. -type MppCoordinator interface { - // Execute generates and executes mpp tasks for mpp physical plan. - Execute(ctx context.Context) (Response, []KeyRange, error) - // Next returns next data - Next(ctx context.Context) (ResultSubset, error) - // ReportStatus report task execution info to coordinator - // It shouldn't change any state outside coordinator itself, since the query which generated the coordinator may not exist - ReportStatus(info ReportStatusRequest) error - // Close and release the used resources. - Close() error - // IsClosed returns whether mpp coordinator is closed or not - IsClosed() bool -} - -// MPPBuildTasksRequest request the stores allocation for a mpp plan fragment. -// However, the request doesn't contain the particular plan, because only key ranges take effect on the location assignment. -type MPPBuildTasksRequest struct { - KeyRanges []KeyRange - StartTS uint64 - - PartitionIDAndRanges []PartitionIDAndRanges -} - -// ExchangeCompressionMode means the compress method used in exchange operator -type ExchangeCompressionMode int - -const ( - // ExchangeCompressionModeNONE indicates no compression - ExchangeCompressionModeNONE ExchangeCompressionMode = iota - // ExchangeCompressionModeFast indicates fast compression/decompression speed, compression ratio is lower than HC mode - ExchangeCompressionModeFast - // ExchangeCompressionModeHC indicates high compression (HC) ratio mode - ExchangeCompressionModeHC - // ExchangeCompressionModeUnspecified indicates unspecified compress method, let TiDB choose one - ExchangeCompressionModeUnspecified - - // RecommendedExchangeCompressionMode indicates recommended compression mode - RecommendedExchangeCompressionMode ExchangeCompressionMode = ExchangeCompressionModeFast - - exchangeCompressionModeUnspecifiedName string = "UNSPECIFIED" -) - -// Name returns the name of ExchangeCompressionMode -func (t ExchangeCompressionMode) Name() string { - if t == ExchangeCompressionModeUnspecified { - return exchangeCompressionModeUnspecifiedName - } - return t.ToTipbCompressionMode().String() -} - -// ToExchangeCompressionMode returns the ExchangeCompressionMode from name -func ToExchangeCompressionMode(name string) (ExchangeCompressionMode, bool) { - name = strings.ToUpper(name) - if name == exchangeCompressionModeUnspecifiedName { - return ExchangeCompressionModeUnspecified, true - } - value, ok := tipb.CompressionMode_value[name] - if ok { - return ExchangeCompressionMode(value), true - } - return ExchangeCompressionModeNONE, false -} - -// ToTipbCompressionMode returns tipb.CompressionMode from kv.ExchangeCompressionMode -func (t ExchangeCompressionMode) ToTipbCompressionMode() tipb.CompressionMode { - switch t { - case ExchangeCompressionModeNONE: - return tipb.CompressionMode_NONE - case ExchangeCompressionModeFast: - return tipb.CompressionMode_FAST - case ExchangeCompressionModeHC: - return tipb.CompressionMode_HIGH_COMPRESSION - } - return tipb.CompressionMode_NONE -} diff --git a/kv/txn.go b/kv/txn.go deleted file mode 100644 index 1806a33da4c92..0000000000000 --- a/kv/txn.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package kv - -import ( - "context" - "errors" - "fmt" - "math" - "math/rand" - "sync" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikvrpc" - "github.com/tikv/client-go/v2/tikvrpc/interceptor" - "go.uber.org/zap" -) - -const ( - // TimeToPrintLongTimeInternalTxn is the duration if the internal transaction lasts more than it, - // TiDB prints a log message. - TimeToPrintLongTimeInternalTxn = time.Minute * 5 -) - -var globalInnerTxnTsBox = innerTxnStartTsBox{ - innerTSLock: sync.Mutex{}, - innerTxnStartTsMap: make(map[uint64]struct{}, 256), -} - -type innerTxnStartTsBox struct { - innerTSLock sync.Mutex - innerTxnStartTsMap map[uint64]struct{} -} - -func (ib *innerTxnStartTsBox) storeInnerTxnTS(startTS uint64) { - ib.innerTSLock.Lock() - ib.innerTxnStartTsMap[startTS] = struct{}{} - ib.innerTSLock.Unlock() -} - -func (ib *innerTxnStartTsBox) deleteInnerTxnTS(startTS uint64) { - ib.innerTSLock.Lock() - delete(ib.innerTxnStartTsMap, startTS) - ib.innerTSLock.Unlock() -} - -// GetMinInnerTxnStartTS get the min StartTS between startTSLowerLimit and curMinStartTS in globalInnerTxnTsBox. -func GetMinInnerTxnStartTS(now time.Time, startTSLowerLimit uint64, - curMinStartTS uint64) uint64 { - return globalInnerTxnTsBox.getMinStartTS(now, startTSLowerLimit, curMinStartTS) -} - -func (ib *innerTxnStartTsBox) getMinStartTS(now time.Time, startTSLowerLimit uint64, - curMinStartTS uint64) uint64 { - minStartTS := curMinStartTS - ib.innerTSLock.Lock() - for innerTS := range ib.innerTxnStartTsMap { - PrintLongTimeInternalTxn(now, innerTS, true) - if innerTS > startTSLowerLimit && innerTS < minStartTS { - minStartTS = innerTS - } - } - ib.innerTSLock.Unlock() - return minStartTS -} - -// PrintLongTimeInternalTxn print the internal transaction information. -// runByFunction true means the transaction is run by `RunInNewTxn`, -// -// false means the transaction is run by internal session. -func PrintLongTimeInternalTxn(now time.Time, startTS uint64, runByFunction bool) { - if startTS > 0 { - innerTxnStartTime := oracle.GetTimeFromTS(startTS) - if now.Sub(innerTxnStartTime) > TimeToPrintLongTimeInternalTxn { - callerName := "internal session" - if runByFunction { - callerName = "RunInNewTxn" - } - infoHeader := fmt.Sprintf("An internal transaction running by %s lasts long time", callerName) - - logutil.BgLogger().Info(infoHeader, - zap.Duration("time", now.Sub(innerTxnStartTime)), zap.Uint64("startTS", startTS), - zap.Time("start time", innerTxnStartTime)) - } - } -} - -// RunInNewTxn will run the f in a new transaction environment, should be used by inner txn only. -func RunInNewTxn(ctx context.Context, store Storage, retryable bool, f func(ctx context.Context, txn Transaction) error) error { - var ( - err error - originalTxnTS uint64 - txn Transaction - ) - - defer func() { - globalInnerTxnTsBox.deleteInnerTxnTS(originalTxnTS) - }() - - for i := uint(0); i < maxRetryCnt; i++ { - txn, err = store.Begin() - if err != nil { - logutil.BgLogger().Error("RunInNewTxn", zap.Error(err)) - return err - } - setRequestSourceForInnerTxn(ctx, txn) - - // originalTxnTS is used to trace the original transaction when the function is retryable. - if i == 0 { - originalTxnTS = txn.StartTS() - globalInnerTxnTsBox.storeInnerTxnTS(originalTxnTS) - } - - err = f(ctx, txn) - if err != nil { - err1 := txn.Rollback() - terror.Log(err1) - if retryable && IsTxnRetryableError(err) { - logutil.BgLogger().Warn("RunInNewTxn", - zap.Uint64("retry txn", txn.StartTS()), - zap.Uint64("original txn", originalTxnTS), - zap.Error(err)) - continue - } - return err - } - - failpoint.Inject("mockCommitErrorInNewTxn", func(val failpoint.Value) { - if v := val.(string); len(v) > 0 { - switch v { - case "retry_once": - //nolint:noloopclosure - if i == 0 { - err = ErrTxnRetryable - } - case "no_retry": - failpoint.Return(errors.New("mock commit error")) - } - } - }) - - if err == nil { - err = txn.Commit(ctx) - if err == nil { - break - } - } - if retryable && IsTxnRetryableError(err) { - logutil.BgLogger().Warn("RunInNewTxn", - zap.Uint64("retry txn", txn.StartTS()), - zap.Uint64("original txn", originalTxnTS), - zap.Error(err)) - BackOff(i) - continue - } - return err - } - return err -} - -var ( - // maxRetryCnt represents maximum retry times in RunInNewTxn. - maxRetryCnt uint = 100 - // retryBackOffBase is the initial duration, in microsecond, a failed transaction stays dormancy before it retries - retryBackOffBase = 1 - // retryBackOffCap is the max amount of duration, in microsecond, a failed transaction stays dormancy before it retries - retryBackOffCap = 100 -) - -// BackOff Implements exponential backoff with full jitter. -// Returns real back off time in microsecond. -// See http://www.awsarchitectureblog.com/2015/03/backoff.html. -func BackOff(attempts uint) int { - upper := int(math.Min(float64(retryBackOffCap), float64(retryBackOffBase)*math.Pow(2.0, float64(attempts)))) - sleep := time.Duration(rand.Intn(upper)) * time.Millisecond // #nosec G404 - time.Sleep(sleep) - return int(sleep) -} - -func setRequestSourceForInnerTxn(ctx context.Context, txn Transaction) { - if source := ctx.Value(RequestSourceKey); source != nil { - requestSource := source.(RequestSource) - if requestSource.RequestSourceType != "" { - if !requestSource.RequestSourceInternal { - logutil.Logger(ctx).Warn("`RunInNewTxn` should be used by inner txn only") - } - txn.SetOption(RequestSourceInternal, requestSource.RequestSourceInternal) - txn.SetOption(RequestSourceType, requestSource.RequestSourceType) - if requestSource.ExplicitRequestSourceType != "" { - txn.SetOption(ExplicitRequestSourceType, requestSource.ExplicitRequestSourceType) - } - return - } - } - // panic in test mode in case there are requests without source in the future. - // log warnings in production mode. - if intest.InTest { - panic("unexpected no source type context, if you see this error, " + - "the `RequestSourceTypeKey` is missing in your context") - } else { - logutil.Logger(ctx).Warn("unexpected no source type context, if you see this warning, " + - "the `RequestSourceTypeKey` is missing in the context") - } -} - -// SetTxnResourceGroup update the resource group name of target txn. -func SetTxnResourceGroup(txn Transaction, name string) { - txn.SetOption(ResourceGroupName, name) - failpoint.Inject("TxnResourceGroupChecker", func(val failpoint.Value) { - expectedRgName := val.(string) - validateRNameInterceptor := func(next interceptor.RPCInterceptorFunc) interceptor.RPCInterceptorFunc { - return func(target string, req *tikvrpc.Request) (*tikvrpc.Response, error) { - var rgName *string - switch r := req.Req.(type) { - case *kvrpcpb.PrewriteRequest: - rgName = &r.Context.ResourceControlContext.ResourceGroupName - case *kvrpcpb.CommitRequest: - rgName = &r.Context.ResourceControlContext.ResourceGroupName - case *kvrpcpb.PessimisticLockRequest: - rgName = &r.Context.ResourceControlContext.ResourceGroupName - } - if rgName != nil && *rgName != expectedRgName { - panic(fmt.Sprintf("resource group name not match, expected: %s, actual: %s", expectedRgName, *rgName)) - } - return next(target, req) - } - } - txn.SetOption(RPCInterceptor, interceptor.NewRPCInterceptor("test-validate-rg-name", validateRNameInterceptor)) - }) -} diff --git a/lock/BUILD.bazel b/lock/BUILD.bazel deleted file mode 100644 index 14a4629494cde..0000000000000 --- a/lock/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "lock", - srcs = ["lock.go"], - importpath = "github.com/pingcap/tidb/lock", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//table", - "//util", - ], -) diff --git a/meta/BUILD.bazel b/meta/BUILD.bazel deleted file mode 100644 index d76b67a85357b..0000000000000 --- a/meta/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "meta", - srcs = [ - "meta.go", - "meta_autoid.go", - ], - importpath = "github.com/pingcap/tidb/meta", - visibility = ["//visibility:public"], - deps = [ - "//domain/resourcegroup", - "//errno", - "//kv", - "//metrics", - "//parser/model", - "//parser/mysql", - "//structure", - "//util/dbterror", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - ], -) - -go_test( - name = "meta_test", - timeout = "short", - srcs = [ - "main_test.go", - "meta_test.go", - ], - embed = [":meta"], - flaky = True, - shard_count = 12, - deps = [ - "//kv", - "//parser/model", - "//store/mockstore", - "//testkit/testsetup", - "//util", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/meta/autoid/BUILD.bazel b/meta/autoid/BUILD.bazel deleted file mode 100644 index 010c1df894e9f..0000000000000 --- a/meta/autoid/BUILD.bazel +++ /dev/null @@ -1,70 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "autoid", - srcs = [ - "autoid.go", - "autoid_service.go", - "errors.go", - "memid.go", - ], - importpath = "github.com/pingcap/tidb/meta/autoid", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//errno", - "//keyspace", - "//kv", - "//meta", - "//metrics", - "//parser/model", - "//parser/mysql", - "//types", - "//util/dbterror", - "//util/etcd", - "//util/execdetails", - "//util/logutil", - "//util/mathutil", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/autoid", - "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//:client", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "autoid_test", - timeout = "short", - srcs = [ - "autoid_test.go", - "bench_test.go", - "main_test.go", - "memid_test.go", - "seq_autoid_test.go", - ], - flaky = True, - shard_count = 10, - deps = [ - ":autoid", - "//kv", - "//meta", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//store/mockstore", - "//testkit/testsetup", - "//types", - "//util", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/meta/autoid/autoid.go b/meta/autoid/autoid.go deleted file mode 100644 index 2f1af76919815..0000000000000 --- a/meta/autoid/autoid.go +++ /dev/null @@ -1,1364 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package autoid - -import ( - "bytes" - "context" - "fmt" - "math" - "strconv" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/autoid" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/tracing" - "github.com/tikv/client-go/v2/txnkv/txnsnapshot" - tikvutil "github.com/tikv/client-go/v2/util" - clientv3 "go.etcd.io/etcd/client/v3" - "go.uber.org/zap" -) - -// Attention: -// For reading cluster TiDB memory tables, the system schema/table should be same. -// Once the system schema/table id been allocated, it can't be changed any more. -// Change the system schema/table id may have the compatibility problem. -const ( - // SystemSchemaIDFlag is the system schema/table id flag, uses the highest bit position as system schema ID flag, it's exports for test. - SystemSchemaIDFlag = 1 << 62 - // InformationSchemaDBID is the information_schema schema id, it's exports for test. - InformationSchemaDBID int64 = SystemSchemaIDFlag | 1 - // PerformanceSchemaDBID is the performance_schema schema id, it's exports for test. - PerformanceSchemaDBID int64 = SystemSchemaIDFlag | 10000 - // MetricSchemaDBID is the metrics_schema schema id, it's exported for test. - MetricSchemaDBID int64 = SystemSchemaIDFlag | 20000 -) - -const ( - minStep = 30000 - maxStep = 2000000 - defaultConsumeTime = 10 * time.Second - minIncrement = 1 - maxIncrement = 65535 -) - -// RowIDBitLength is the bit number of a row id in TiDB. -const RowIDBitLength = 64 - -const ( - // AutoRandomShardBitsDefault is the default number of shard bits. - AutoRandomShardBitsDefault = 5 - // AutoRandomRangeBitsDefault is the default number of range bits. - AutoRandomRangeBitsDefault = 64 - // AutoRandomShardBitsMax is the max number of shard bits. - AutoRandomShardBitsMax = 15 - // AutoRandomRangeBitsMax is the max number of range bits. - AutoRandomRangeBitsMax = 64 - // AutoRandomRangeBitsMin is the min number of range bits. - AutoRandomRangeBitsMin = 32 - // AutoRandomIncBitsMin is the min number of auto random incremental bits. - AutoRandomIncBitsMin = 27 -) - -// AutoRandomShardBitsNormalize normalizes the auto random shard bits. -func AutoRandomShardBitsNormalize(shard int, colName string) (ret uint64, err error) { - if shard == types.UnspecifiedLength { - return AutoRandomShardBitsDefault, nil - } - if shard <= 0 { - return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(AutoRandomNonPositive) - } - if shard > AutoRandomShardBitsMax { - errMsg := fmt.Sprintf(AutoRandomOverflowErrMsg, AutoRandomShardBitsMax, shard, colName) - return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(errMsg) - } - return uint64(shard), nil -} - -// AutoRandomRangeBitsNormalize normalizes the auto random range bits. -func AutoRandomRangeBitsNormalize(rangeBits int) (ret uint64, err error) { - if rangeBits == types.UnspecifiedLength { - return AutoRandomRangeBitsDefault, nil - } - if rangeBits < AutoRandomRangeBitsMin || rangeBits > AutoRandomRangeBitsMax { - errMsg := fmt.Sprintf(AutoRandomInvalidRangeBits, AutoRandomRangeBitsMin, AutoRandomRangeBitsMax, rangeBits) - return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(errMsg) - } - return uint64(rangeBits), nil -} - -// Test needs to change it, so it's a variable. -var step = int64(30000) - -// AllocatorType is the type of allocator for generating auto-id. Different type of allocators use different key-value pairs. -type AllocatorType uint8 - -const ( - // RowIDAllocType indicates the allocator is used to allocate row id. - RowIDAllocType AllocatorType = iota - // AutoIncrementType indicates the allocator is used to allocate auto increment value. - AutoIncrementType - // AutoRandomType indicates the allocator is used to allocate auto-shard id. - AutoRandomType - // SequenceType indicates the allocator is used to allocate sequence value. - SequenceType -) - -func (a AllocatorType) String() string { - switch a { - case RowIDAllocType: - return "_tidb_rowid" - case AutoIncrementType: - return "auto_increment" - case AutoRandomType: - return "auto_random" - case SequenceType: - return "sequence" - } - return "unknown" -} - -// CustomAutoIncCacheOption is one kind of AllocOption to customize the allocator step length. -type CustomAutoIncCacheOption int64 - -// ApplyOn implements the AllocOption interface. -func (step CustomAutoIncCacheOption) ApplyOn(alloc *allocator) { - if step == 0 { - return - } - alloc.step = int64(step) - alloc.customStep = true -} - -// AllocOptionTableInfoVersion is used to pass the TableInfo.Version to the allocator. -type AllocOptionTableInfoVersion uint16 - -// ApplyOn implements the AllocOption interface. -func (v AllocOptionTableInfoVersion) ApplyOn(alloc *allocator) { - alloc.tbVersion = uint16(v) -} - -// AllocOption is a interface to define allocator custom options coming in future. -type AllocOption interface { - ApplyOn(*allocator) -} - -// Allocator is an auto increment id generator. -// Just keep id unique actually. -type Allocator interface { - // Alloc allocs N consecutive autoID for table with tableID, returning (min, max] of the allocated autoID batch. - // It gets a batch of autoIDs at a time. So it does not need to access storage for each call. - // The consecutive feature is used to insert multiple rows in a statement. - // increment & offset is used to validate the start position (the allocator's base is not always the last allocated id). - // The returned range is (min, max]: - // case increment=1 & offset=1: you can derive the ids like min+1, min+2... max. - // case increment=x & offset=y: you firstly need to seek to firstID by `SeekToFirstAutoIDXXX`, then derive the IDs like firstID, firstID + increment * 2... in the caller. - Alloc(ctx context.Context, n uint64, increment, offset int64) (int64, int64, error) - - // AllocSeqCache allocs sequence batch value cached in table level(rather than in alloc), the returned range covering - // the size of sequence cache with it's increment. The returned round indicates the sequence cycle times if it is with - // cycle option. - AllocSeqCache() (min int64, max int64, round int64, err error) - - // Rebase rebases the autoID base for table with tableID and the new base value. - // If allocIDs is true, it will allocate some IDs and save to the cache. - // If allocIDs is false, it will not allocate IDs. - Rebase(ctx context.Context, newBase int64, allocIDs bool) error - - // ForceRebase set the next global auto ID to newBase. - ForceRebase(newBase int64) error - - // RebaseSeq rebases the sequence value in number axis with tableID and the new base value. - RebaseSeq(newBase int64) (int64, bool, error) - - // Base return the current base of Allocator. - Base() int64 - // End is only used for test. - End() int64 - // NextGlobalAutoID returns the next global autoID. - NextGlobalAutoID() (int64, error) - GetType() AllocatorType -} - -// Allocators represents a set of `Allocator`s. -type Allocators struct { - SepAutoInc bool - Allocs []Allocator -} - -// NewAllocators packs multiple `Allocator`s into Allocators. -func NewAllocators(sepAutoInc bool, allocators ...Allocator) Allocators { - return Allocators{ - SepAutoInc: sepAutoInc, - Allocs: allocators, - } -} - -// Append add an allocator to the allocators. -func (all Allocators) Append(a Allocator) Allocators { - return Allocators{ - SepAutoInc: all.SepAutoInc, - Allocs: append(all.Allocs, a), - } -} - -// Get returns the Allocator according to the AllocatorType. -func (all Allocators) Get(allocType AllocatorType) Allocator { - if !all.SepAutoInc { - if allocType == AutoIncrementType { - allocType = RowIDAllocType - } - } - - for _, a := range all.Allocs { - if a.GetType() == allocType { - return a - } - } - return nil -} - -// Filter filters all the allocators that match pred. -func (all Allocators) Filter(pred func(Allocator) bool) Allocators { - var ret []Allocator - for _, a := range all.Allocs { - if pred(a) { - ret = append(ret, a) - } - } - return Allocators{ - SepAutoInc: all.SepAutoInc, - Allocs: ret, - } -} - -type allocator struct { - mu sync.Mutex - base int64 - end int64 - store kv.Storage - // dbID is current database's ID. - dbID int64 - tbID int64 - tbVersion uint16 - isUnsigned bool - lastAllocTime time.Time - step int64 - customStep bool - allocType AllocatorType - sequence *model.SequenceInfo -} - -// GetStep is only used by tests -func GetStep() int64 { - return step -} - -// SetStep is only used by tests -func SetStep(s int64) { - step = s -} - -// Base implements autoid.Allocator Base interface. -func (alloc *allocator) Base() int64 { - alloc.mu.Lock() - defer alloc.mu.Unlock() - return alloc.base -} - -// End implements autoid.Allocator End interface. -func (alloc *allocator) End() int64 { - alloc.mu.Lock() - defer alloc.mu.Unlock() - return alloc.end -} - -// NextGlobalAutoID implements autoid.Allocator NextGlobalAutoID interface. -func (alloc *allocator) NextGlobalAutoID() (int64, error) { - var autoID int64 - startTime := time.Now() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - var err1 error - autoID, err1 = alloc.getIDAccessor(txn).Get() - if err1 != nil { - return errors.Trace(err1) - } - return nil - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.GlobalAutoID, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if alloc.isUnsigned { - return int64(uint64(autoID) + 1), err - } - return autoID + 1, err -} - -func (alloc *allocator) rebase4Unsigned(ctx context.Context, requiredBase uint64, allocIDs bool) error { - // Satisfied by alloc.base, nothing to do. - if requiredBase <= uint64(alloc.base) { - return nil - } - // Satisfied by alloc.end, need to update alloc.base. - if requiredBase <= uint64(alloc.end) { - alloc.base = int64(requiredBase) - return nil - } - - ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) - if allocatorStats != nil { - allocatorStats.rebaseCount++ - defer func() { - if commitDetail != nil { - allocatorStats.mergeCommitDetail(*commitDetail) - } - }() - } - var newBase, newEnd uint64 - startTime := time.Now() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - if allocatorStats != nil { - txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) - } - idAcc := alloc.getIDAccessor(txn) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - uCurrentEnd := uint64(currentEnd) - if allocIDs { - newBase = mathutil.Max(uCurrentEnd, requiredBase) - newEnd = mathutil.Min(math.MaxUint64-uint64(alloc.step), newBase) + uint64(alloc.step) - } else { - if uCurrentEnd >= requiredBase { - newBase = uCurrentEnd - newEnd = uCurrentEnd - // Required base satisfied, we don't need to update KV. - return nil - } - // If we don't want to allocate IDs, for example when creating a table with a given base value, - // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 - // will be allocated, so we need to increase the end to exactly the requiredBase. - newBase = requiredBase - newEnd = requiredBase - } - _, err1 = idAcc.Inc(int64(newEnd - uCurrentEnd)) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return err - } - alloc.base, alloc.end = int64(newBase), int64(newEnd) - return nil -} - -func (alloc *allocator) rebase4Signed(ctx context.Context, requiredBase int64, allocIDs bool) error { - // Satisfied by alloc.base, nothing to do. - if requiredBase <= alloc.base { - return nil - } - // Satisfied by alloc.end, need to update alloc.base. - if requiredBase <= alloc.end { - alloc.base = requiredBase - return nil - } - - ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) - if allocatorStats != nil { - allocatorStats.rebaseCount++ - defer func() { - if commitDetail != nil { - allocatorStats.mergeCommitDetail(*commitDetail) - } - }() - } - var newBase, newEnd int64 - startTime := time.Now() - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - if allocatorStats != nil { - txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) - } - idAcc := alloc.getIDAccessor(txn) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - if allocIDs { - newBase = mathutil.Max(currentEnd, requiredBase) - newEnd = mathutil.Min(math.MaxInt64-alloc.step, newBase) + alloc.step - } else { - if currentEnd >= requiredBase { - newBase = currentEnd - newEnd = currentEnd - // Required base satisfied, we don't need to update KV. - return nil - } - // If we don't want to allocate IDs, for example when creating a table with a given base value, - // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 - // will be allocated, so we need to increase the end to exactly the requiredBase. - newBase = requiredBase - newEnd = requiredBase - } - _, err1 = idAcc.Inc(newEnd - currentEnd) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return err - } - alloc.base, alloc.end = newBase, newEnd - return nil -} - -// rebase4Sequence won't alloc batch immediately, cause it won't cache value in allocator. -func (alloc *allocator) rebase4Sequence(requiredBase int64) (int64, bool, error) { - startTime := time.Now() - alreadySatisfied := false - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) - currentEnd, err := acc.SequenceValue().Get() - if err != nil { - return err - } - if alloc.sequence.Increment > 0 { - if currentEnd >= requiredBase { - // Required base satisfied, we don't need to update KV. - alreadySatisfied = true - return nil - } - } else { - if currentEnd <= requiredBase { - // Required base satisfied, we don't need to update KV. - alreadySatisfied = true - return nil - } - } - - // If we don't want to allocate IDs, for example when creating a table with a given base value, - // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 - // will be allocated, so we need to increase the end to exactly the requiredBase. - _, err = acc.SequenceValue().Inc(requiredBase - currentEnd) - return err - }) - // TODO: sequence metrics - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return 0, false, err - } - if alreadySatisfied { - return 0, true, nil - } - return requiredBase, false, err -} - -// Rebase implements autoid.Allocator Rebase interface. -// The requiredBase is the minimum base value after Rebase. -// The real base may be greater than the required base. -func (alloc *allocator) Rebase(ctx context.Context, requiredBase int64, allocIDs bool) error { - alloc.mu.Lock() - defer alloc.mu.Unlock() - if alloc.isUnsigned { - return alloc.rebase4Unsigned(ctx, uint64(requiredBase), allocIDs) - } - return alloc.rebase4Signed(ctx, requiredBase, allocIDs) -} - -// ForceRebase implements autoid.Allocator ForceRebase interface. -func (alloc *allocator) ForceRebase(requiredBase int64) error { - if requiredBase == -1 { - return ErrAutoincReadFailed.GenWithStack("Cannot force rebase the next global ID to '0'") - } - alloc.mu.Lock() - defer alloc.mu.Unlock() - startTime := time.Now() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - idAcc := alloc.getIDAccessor(txn) - currentEnd, err1 := idAcc.Get() - if err1 != nil { - return err1 - } - var step int64 - if !alloc.isUnsigned { - step = requiredBase - currentEnd - } else { - uRequiredBase, uCurrentEnd := uint64(requiredBase), uint64(currentEnd) - step = int64(uRequiredBase - uCurrentEnd) - } - _, err1 = idAcc.Inc(step) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return err - } - alloc.base, alloc.end = requiredBase, requiredBase - return nil -} - -// Rebase implements autoid.Allocator RebaseSeq interface. -// The return value is quite same as expression function, bool means whether it should be NULL, -// here it will be used in setval expression function (true meaning the set value has been satisfied, return NULL). -// case1:When requiredBase is satisfied with current value, it will return (0, true, nil), -// case2:When requiredBase is successfully set in, it will return (requiredBase, false, nil). -// If some error occurs in the process, return it immediately. -func (alloc *allocator) RebaseSeq(requiredBase int64) (int64, bool, error) { - alloc.mu.Lock() - defer alloc.mu.Unlock() - return alloc.rebase4Sequence(requiredBase) -} - -func (alloc *allocator) GetType() AllocatorType { - return alloc.allocType -} - -// NextStep return new auto id step according to previous step and consuming time. -func NextStep(curStep int64, consumeDur time.Duration) int64 { - failpoint.Inject("mockAutoIDCustomize", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(3) - } - }) - failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(step) - } - }) - - consumeRate := defaultConsumeTime.Seconds() / consumeDur.Seconds() - res := int64(float64(curStep) * consumeRate) - if res < minStep { - return minStep - } else if res > maxStep { - return maxStep - } - return res -} - -// MockForTest is exported for testing. -// The actual implementation is in github.com/pingcap/tidb/autoid_service because of the -// package circle depending issue. -var MockForTest func(kv.Storage) autoid.AutoIDAllocClient - -func newSinglePointAlloc(store kv.Storage, dbID, tblID int64, isUnsigned bool) *singlePointAlloc { - ebd, ok := store.(kv.EtcdBackend) - if !ok { - // newSinglePointAlloc fail because not etcd background - // This could happen in the server package unit test - return nil - } - - addrs, err := ebd.EtcdAddrs() - if err != nil { - panic(err) - } - - keyspaceID := uint32(store.GetCodec().GetKeyspaceID()) - spa := &singlePointAlloc{ - dbID: dbID, - tblID: tblID, - isUnsigned: isUnsigned, - keyspaceID: keyspaceID, - } - if len(addrs) > 0 { - etcdCli, err := clientv3.New(clientv3.Config{ - Endpoints: addrs, - AutoSyncInterval: 30 * time.Second, - TLS: ebd.TLSConfig(), - }) - etcd.SetEtcdCliByNamespace(etcdCli, keyspace.MakeKeyspaceEtcdNamespaceSlash(store.GetCodec())) - if err != nil { - logutil.BgLogger().Error("fail to connect etcd, fallback to default", zap.String("category", "autoid client"), zap.Error(err)) - return nil - } - spa.clientDiscover = clientDiscover{etcdCli: etcdCli} - } else { - spa.clientDiscover = clientDiscover{} - spa.mu.AutoIDAllocClient = MockForTest(store) - } - - // mockAutoIDChange failpoint is not implemented in this allocator, so fallback to use the default one. - failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) { - if val.(bool) { - spa = nil - } - }) - return spa -} - -// NewAllocator returns a new auto increment id generator on the store. -func NewAllocator(store kv.Storage, dbID, tbID int64, isUnsigned bool, - allocType AllocatorType, opts ...AllocOption) Allocator { - alloc := &allocator{ - store: store, - dbID: dbID, - tbID: tbID, - isUnsigned: isUnsigned, - step: step, - lastAllocTime: time.Now(), - allocType: allocType, - } - for _, fn := range opts { - fn.ApplyOn(alloc) - } - - // Use the MySQL compatible AUTO_INCREMENT mode. - if alloc.customStep && alloc.step == 1 && alloc.tbVersion >= model.TableInfoVersion5 { - if allocType == AutoIncrementType { - alloc1 := newSinglePointAlloc(store, dbID, tbID, isUnsigned) - if alloc1 != nil { - return alloc1 - } - } else if allocType == RowIDAllocType { - // Now that the autoid and rowid allocator are separated, the AUTO_ID_CACHE 1 setting should not make - // the rowid allocator do not use cache. - alloc.customStep = false - alloc.step = step - } - } - - return alloc -} - -// NewSequenceAllocator returns a new sequence value generator on the store. -func NewSequenceAllocator(store kv.Storage, dbID, tbID int64, info *model.SequenceInfo) Allocator { - return &allocator{ - store: store, - dbID: dbID, - tbID: tbID, - // Sequence allocator is always signed. - isUnsigned: false, - lastAllocTime: time.Now(), - allocType: SequenceType, - sequence: info, - } -} - -// TODO: Handle allocators when changing Table ID during ALTER TABLE t PARTITION BY ... - -// NewAllocatorsFromTblInfo creates an array of allocators of different types with the information of model.TableInfo. -func NewAllocatorsFromTblInfo(store kv.Storage, schemaID int64, tblInfo *model.TableInfo) Allocators { - var allocs []Allocator - dbID := tblInfo.GetDBID(schemaID) - idCacheOpt := CustomAutoIncCacheOption(tblInfo.AutoIdCache) - tblVer := AllocOptionTableInfoVersion(tblInfo.Version) - - hasRowID := !tblInfo.PKIsHandle && !tblInfo.IsCommonHandle - hasAutoIncID := tblInfo.GetAutoIncrementColInfo() != nil - if hasRowID || hasAutoIncID { - alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), RowIDAllocType, idCacheOpt, tblVer) - allocs = append(allocs, alloc) - } - if hasAutoIncID { - alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), AutoIncrementType, idCacheOpt, tblVer) - allocs = append(allocs, alloc) - } - hasAutoRandID := tblInfo.ContainsAutoRandomBits() - if hasAutoRandID { - alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), AutoRandomType, idCacheOpt, tblVer) - allocs = append(allocs, alloc) - } - if tblInfo.IsSequence() { - allocs = append(allocs, NewSequenceAllocator(store, dbID, tblInfo.ID, tblInfo.Sequence)) - } - return NewAllocators(tblInfo.SepAutoInc(), allocs...) -} - -// Alloc implements autoid.Allocator Alloc interface. -// For autoIncrement allocator, the increment and offset should always be positive in [1, 65535]. -// Attention: -// When increment and offset is not the default value(1), the return range (min, max] need to -// calculate the correct start position rather than simply the add 1 to min. Then you can derive -// the successive autoID by adding increment * cnt to firstID for (n-1) times. -// -// Example: -// (6, 13] is returned, increment = 4, offset = 1, n = 2. -// 6 is the last allocated value for other autoID or handle, maybe with different increment and step, -// but actually we don't care about it, all we need is to calculate the new autoID corresponding to the -// increment and offset at this time now. To simplify the rule is like (ID - offset) % increment = 0, -// so the first autoID should be 9, then add increment to it to get 13. -func (alloc *allocator) Alloc(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { - if alloc.tbID == 0 { - return 0, 0, errInvalidTableID.GenWithStackByArgs("Invalid tableID") - } - if n == 0 { - return 0, 0, nil - } - if alloc.allocType == AutoIncrementType || alloc.allocType == RowIDAllocType { - if !validIncrementAndOffset(increment, offset) { - return 0, 0, errInvalidIncrementAndOffset.GenWithStackByArgs(increment, offset) - } - } - alloc.mu.Lock() - defer alloc.mu.Unlock() - if alloc.isUnsigned { - return alloc.alloc4Unsigned(ctx, n, increment, offset) - } - return alloc.alloc4Signed(ctx, n, increment, offset) -} - -func (alloc *allocator) AllocSeqCache() (min int64, max int64, round int64, err error) { - alloc.mu.Lock() - defer alloc.mu.Unlock() - return alloc.alloc4Sequence() -} - -func validIncrementAndOffset(increment, offset int64) bool { - return (increment >= minIncrement && increment <= maxIncrement) && (offset >= minIncrement && offset <= maxIncrement) -} - -// CalcNeededBatchSize is used to calculate batch size for autoID allocation. -// It firstly seeks to the first valid position based on increment and offset, -// then plus the length remained, which could be (n-1) * increment. -func CalcNeededBatchSize(base, n, increment, offset int64, isUnsigned bool) int64 { - if increment == 1 { - return n - } - if isUnsigned { - // SeekToFirstAutoIDUnSigned seeks to the next unsigned valid position. - nr := SeekToFirstAutoIDUnSigned(uint64(base), uint64(increment), uint64(offset)) - // Calculate the total batch size needed. - nr += (uint64(n) - 1) * uint64(increment) - return int64(nr - uint64(base)) - } - nr := SeekToFirstAutoIDSigned(base, increment, offset) - // Calculate the total batch size needed. - nr += (n - 1) * increment - return nr - base -} - -// CalcSequenceBatchSize calculate the next sequence batch size. -func CalcSequenceBatchSize(base, size, increment, offset, min, max int64) (int64, error) { - // The sequence is positive growth. - if increment > 0 { - if increment == 1 { - // Sequence is already allocated to the end. - if base >= max { - return 0, ErrAutoincReadFailed - } - // The rest of sequence < cache size, return the rest. - if max-base < size { - return max - base, nil - } - // The rest of sequence is adequate. - return size, nil - } - nr, ok := SeekToFirstSequenceValue(base, increment, offset, min, max) - if !ok { - return 0, ErrAutoincReadFailed - } - // The rest of sequence < cache size, return the rest. - if max-nr < (size-1)*increment { - return max - base, nil - } - return (nr - base) + (size-1)*increment, nil - } - // The sequence is negative growth. - if increment == -1 { - if base <= min { - return 0, ErrAutoincReadFailed - } - if base-min < size { - return base - min, nil - } - return size, nil - } - nr, ok := SeekToFirstSequenceValue(base, increment, offset, min, max) - if !ok { - return 0, ErrAutoincReadFailed - } - // The rest of sequence < cache size, return the rest. - if nr-min < (size-1)*(-increment) { - return base - min, nil - } - return (base - nr) + (size-1)*(-increment), nil -} - -// SeekToFirstSequenceValue seeks to the next valid value (must be in range of [MIN, max]), -// the bool indicates whether the first value is got. -// The seeking formula is describe as below: -// -// nr := (base + increment - offset) / increment -// -// first := nr*increment + offset -// Because formula computation will overflow Int64, so we transfer it to uint64 for distance computation. -func SeekToFirstSequenceValue(base, increment, offset, min, max int64) (int64, bool) { - if increment > 0 { - // Sequence is already allocated to the end. - if base >= max { - return 0, false - } - uMax := EncodeIntToCmpUint(max) - uBase := EncodeIntToCmpUint(base) - uOffset := EncodeIntToCmpUint(offset) - uIncrement := uint64(increment) - if uMax-uBase < uIncrement { - // Enum the possible first value. - for i := uBase + 1; i <= uMax; i++ { - if (i-uOffset)%uIncrement == 0 { - return DecodeCmpUintToInt(i), true - } - } - return 0, false - } - nr := (uBase + uIncrement - uOffset) / uIncrement - nr = nr*uIncrement + uOffset - first := DecodeCmpUintToInt(nr) - return first, true - } - // Sequence is already allocated to the end. - if base <= min { - return 0, false - } - uMin := EncodeIntToCmpUint(min) - uBase := EncodeIntToCmpUint(base) - uOffset := EncodeIntToCmpUint(offset) - uIncrement := uint64(-increment) - if uBase-uMin < uIncrement { - // Enum the possible first value. - for i := uBase - 1; i >= uMin; i-- { - if (uOffset-i)%uIncrement == 0 { - return DecodeCmpUintToInt(i), true - } - } - return 0, false - } - nr := (uOffset - uBase + uIncrement) / uIncrement - nr = uOffset - nr*uIncrement - first := DecodeCmpUintToInt(nr) - return first, true -} - -// SeekToFirstAutoIDSigned seeks to the next valid signed position. -func SeekToFirstAutoIDSigned(base, increment, offset int64) int64 { - nr := (base + increment - offset) / increment - nr = nr*increment + offset - return nr -} - -// SeekToFirstAutoIDUnSigned seeks to the next valid unsigned position. -func SeekToFirstAutoIDUnSigned(base, increment, offset uint64) uint64 { - nr := (base + increment - offset) / increment - nr = nr*increment + offset - return nr -} - -func (alloc *allocator) alloc4Signed(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { - // Check offset rebase if necessary. - if offset-1 > alloc.base { - if err := alloc.rebase4Signed(ctx, offset-1, true); err != nil { - return 0, 0, err - } - } - // CalcNeededBatchSize calculates the total batch size needed. - n1 := CalcNeededBatchSize(alloc.base, int64(n), increment, offset, alloc.isUnsigned) - - // Condition alloc.base+N1 > alloc.end will overflow when alloc.base + N1 > MaxInt64. So need this. - if math.MaxInt64-alloc.base <= n1 { - return 0, 0, ErrAutoincReadFailed - } - // The local rest is not enough for allocN, skip it. - if alloc.base+n1 > alloc.end { - var newBase, newEnd int64 - startTime := time.Now() - nextStep := alloc.step - if !alloc.customStep && alloc.end > 0 { - // Although it may skip a segment here, we still think it is consumed. - consumeDur := startTime.Sub(alloc.lastAllocTime) - nextStep = NextStep(alloc.step, consumeDur) - } - - ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) - if allocatorStats != nil { - allocatorStats.allocCount++ - defer func() { - if commitDetail != nil { - allocatorStats.mergeCommitDetail(*commitDetail) - } - }() - } - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - defer tracing.StartRegion(ctx, "alloc.alloc4Signed").End() - if allocatorStats != nil { - txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) - } - - idAcc := alloc.getIDAccessor(txn) - var err1 error - newBase, err1 = idAcc.Get() - if err1 != nil { - return err1 - } - // CalcNeededBatchSize calculates the total batch size needed on global base. - n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep < n1 { - nextStep = n1 - } - tmpStep := mathutil.Min(math.MaxInt64-newBase, nextStep) - // The global rest is not enough for alloc. - if tmpStep < n1 { - return ErrAutoincReadFailed - } - newEnd, err1 = idAcc.Inc(tmpStep) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return 0, 0, err - } - // Store the step for non-customized-step allocator to calculate next dynamic step. - if !alloc.customStep { - alloc.step = nextStep - } - alloc.lastAllocTime = time.Now() - if newBase == math.MaxInt64 { - return 0, 0, ErrAutoincReadFailed - } - alloc.base, alloc.end = newBase, newEnd - } - logutil.Logger(context.TODO()).Debug("alloc N signed ID", - zap.Uint64("from ID", uint64(alloc.base)), - zap.Uint64("to ID", uint64(alloc.base+n1)), - zap.Int64("table ID", alloc.tbID), - zap.Int64("database ID", alloc.dbID)) - min = alloc.base - alloc.base += n1 - return min, alloc.base, nil -} - -func (alloc *allocator) alloc4Unsigned(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { - // Check offset rebase if necessary. - if uint64(offset-1) > uint64(alloc.base) { - if err := alloc.rebase4Unsigned(ctx, uint64(offset-1), true); err != nil { - return 0, 0, err - } - } - // CalcNeededBatchSize calculates the total batch size needed. - n1 := CalcNeededBatchSize(alloc.base, int64(n), increment, offset, alloc.isUnsigned) - - // Condition alloc.base+n1 > alloc.end will overflow when alloc.base + n1 > MaxInt64. So need this. - if math.MaxUint64-uint64(alloc.base) <= uint64(n1) { - return 0, 0, ErrAutoincReadFailed - } - // The local rest is not enough for alloc, skip it. - if uint64(alloc.base)+uint64(n1) > uint64(alloc.end) { - var newBase, newEnd int64 - startTime := time.Now() - nextStep := alloc.step - if !alloc.customStep { - // Although it may skip a segment here, we still treat it as consumed. - consumeDur := startTime.Sub(alloc.lastAllocTime) - nextStep = NextStep(alloc.step, consumeDur) - } - - ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) - if allocatorStats != nil { - allocatorStats.allocCount++ - defer func() { - if commitDetail != nil { - allocatorStats.mergeCommitDetail(*commitDetail) - } - }() - } - - if codeRun := ctx.Value("testIssue39528"); codeRun != nil { - *(codeRun.(*bool)) = true - return 0, 0, errors.New("mock error for test") - } - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - defer tracing.StartRegion(ctx, "alloc.alloc4Unsigned").End() - if allocatorStats != nil { - txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) - } - - idAcc := alloc.getIDAccessor(txn) - var err1 error - newBase, err1 = idAcc.Get() - if err1 != nil { - return err1 - } - // CalcNeededBatchSize calculates the total batch size needed on new base. - n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) - // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. - if nextStep < n1 { - nextStep = n1 - } - tmpStep := int64(mathutil.Min(math.MaxUint64-uint64(newBase), uint64(nextStep))) - // The global rest is not enough for alloc. - if tmpStep < n1 { - return ErrAutoincReadFailed - } - newEnd, err1 = idAcc.Inc(tmpStep) - return err1 - }) - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return 0, 0, err - } - // Store the step for non-customized-step allocator to calculate next dynamic step. - if !alloc.customStep { - alloc.step = nextStep - } - alloc.lastAllocTime = time.Now() - if uint64(newBase) == math.MaxUint64 { - return 0, 0, ErrAutoincReadFailed - } - alloc.base, alloc.end = newBase, newEnd - } - logutil.Logger(context.TODO()).Debug("alloc unsigned ID", - zap.Uint64(" from ID", uint64(alloc.base)), - zap.Uint64("to ID", uint64(alloc.base+n1)), - zap.Int64("table ID", alloc.tbID), - zap.Int64("database ID", alloc.dbID)) - min = alloc.base - // Use uint64 n directly. - alloc.base = int64(uint64(alloc.base) + uint64(n1)) - return min, alloc.base, nil -} - -func getAllocatorStatsFromCtx(ctx context.Context) (context.Context, *AllocatorRuntimeStats, **tikvutil.CommitDetails) { - var allocatorStats *AllocatorRuntimeStats - var commitDetail *tikvutil.CommitDetails - ctxValue := ctx.Value(AllocatorRuntimeStatsCtxKey) - if ctxValue != nil { - allocatorStats = ctxValue.(*AllocatorRuntimeStats) - ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) - } - return ctx, allocatorStats, &commitDetail -} - -// alloc4Sequence is used to alloc value for sequence, there are several aspects different from autoid logic. -// 1: sequence allocation don't need check rebase. -// 2: sequence allocation don't need auto step. -// 3: sequence allocation may have negative growth. -// 4: sequence allocation batch length can be dissatisfied. -// 5: sequence batch allocation will be consumed immediately. -func (alloc *allocator) alloc4Sequence() (min int64, max int64, round int64, err error) { - increment := alloc.sequence.Increment - offset := alloc.sequence.Start - minValue := alloc.sequence.MinValue - maxValue := alloc.sequence.MaxValue - cacheSize := alloc.sequence.CacheValue - if !alloc.sequence.Cache { - cacheSize = 1 - } - - var newBase, newEnd int64 - startTime := time.Now() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { - acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) - var ( - err1 error - seqStep int64 - ) - // Get the real offset if the sequence is in cycle. - // round is used to count cycle times in sequence with cycle option. - if alloc.sequence.Cycle { - // GetSequenceCycle is used to get the flag `round`, which indicates whether the sequence is already in cycle. - round, err1 = acc.SequenceCycle().Get() - if err1 != nil { - return err1 - } - if round > 0 { - if increment > 0 { - offset = alloc.sequence.MinValue - } else { - offset = alloc.sequence.MaxValue - } - } - } - - // Get the global new base. - newBase, err1 = acc.SequenceValue().Get() - if err1 != nil { - return err1 - } - - // CalcNeededBatchSize calculates the total batch size needed. - seqStep, err1 = CalcSequenceBatchSize(newBase, cacheSize, increment, offset, minValue, maxValue) - - if err1 != nil && err1 == ErrAutoincReadFailed { - if !alloc.sequence.Cycle { - return err1 - } - // Reset the sequence base and offset. - if alloc.sequence.Increment > 0 { - newBase = alloc.sequence.MinValue - 1 - offset = alloc.sequence.MinValue - } else { - newBase = alloc.sequence.MaxValue + 1 - offset = alloc.sequence.MaxValue - } - err1 = acc.SequenceValue().Put(newBase) - if err1 != nil { - return err1 - } - - // Reset sequence round state value. - round++ - // SetSequenceCycle is used to store the flag `round` which indicates whether the sequence is already in cycle. - // round > 0 means the sequence is already in cycle, so the offset should be minvalue / maxvalue rather than sequence.start. - // TiDB is a stateless node, it should know whether the sequence is already in cycle when restart. - err1 = acc.SequenceCycle().Put(round) - if err1 != nil { - return err1 - } - - // Recompute the sequence next batch size. - seqStep, err1 = CalcSequenceBatchSize(newBase, cacheSize, increment, offset, minValue, maxValue) - if err1 != nil { - return err1 - } - } - var delta int64 - if alloc.sequence.Increment > 0 { - delta = seqStep - } else { - delta = -seqStep - } - newEnd, err1 = acc.SequenceValue().Inc(delta) - return err1 - }) - - // TODO: sequence metrics - metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) - if err != nil { - return 0, 0, 0, err - } - logutil.Logger(context.TODO()).Debug("alloc sequence value", - zap.Uint64(" from value", uint64(newBase)), - zap.Uint64("to value", uint64(newEnd)), - zap.Int64("table ID", alloc.tbID), - zap.Int64("database ID", alloc.dbID)) - return newBase, newEnd, round, nil -} - -func (alloc *allocator) getIDAccessor(txn kv.Transaction) meta.AutoIDAccessor { - acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) - switch alloc.allocType { - case RowIDAllocType: - return acc.RowID() - case AutoIncrementType: - return acc.IncrementID(alloc.tbVersion) - case AutoRandomType: - return acc.RandomID() - case SequenceType: - return acc.SequenceValue() - } - return nil -} - -const signMask uint64 = 0x8000000000000000 - -// EncodeIntToCmpUint make int v to comparable uint type -func EncodeIntToCmpUint(v int64) uint64 { - return uint64(v) ^ signMask -} - -// DecodeCmpUintToInt decodes the u that encoded by EncodeIntToCmpUint -func DecodeCmpUintToInt(u uint64) int64 { - return int64(u ^ signMask) -} - -// TestModifyBaseAndEndInjection exported for testing modifying the base and end. -func TestModifyBaseAndEndInjection(alloc Allocator, base, end int64) { - alloc.(*allocator).mu.Lock() - alloc.(*allocator).base = base - alloc.(*allocator).end = end - alloc.(*allocator).mu.Unlock() -} - -// ShardIDFormat is used to calculate the bit length of different segments in auto id. -// Generally, an auto id is consist of 4 segments: sign bit, reserved bits, shard bits and incremental bits. -// Take "a BIGINT AUTO_INCREMENT PRIMARY KEY" as an example, assume that the `shard_row_id_bits` = 5, -// the layout is like -// -// | [sign_bit] (1 bit) | [reserved bits] (0 bits) | [shard_bits] (5 bits) | [incremental_bits] (64-1-5=58 bits) | -// -// Please always use NewShardIDFormat() to instantiate. -type ShardIDFormat struct { - FieldType *types.FieldType - ShardBits uint64 - // Derived fields. - IncrementalBits uint64 -} - -// NewShardIDFormat create an instance of ShardIDFormat. -// RangeBits means the bit length of the sign bit + shard bits + incremental bits. -// If RangeBits is 0, it will be calculated according to field type automatically. -func NewShardIDFormat(fieldType *types.FieldType, shardBits, rangeBits uint64) ShardIDFormat { - var incrementalBits uint64 - if rangeBits == 0 { - // Zero means that the range bits is not specified. We interpret it as the length of BIGINT. - incrementalBits = RowIDBitLength - shardBits - } else { - incrementalBits = rangeBits - shardBits - } - hasSignBit := !mysql.HasUnsignedFlag(fieldType.GetFlag()) - if hasSignBit { - incrementalBits-- - } - return ShardIDFormat{ - FieldType: fieldType, - ShardBits: shardBits, - IncrementalBits: incrementalBits, - } -} - -// IncrementalBitsCapacity returns the max capacity of incremental section of the current format. -func (s *ShardIDFormat) IncrementalBitsCapacity() uint64 { - return uint64(s.IncrementalMask()) -} - -// IncrementalMask returns 00..0[11..1], where [11..1] is the incremental part of the current format. -func (s *ShardIDFormat) IncrementalMask() int64 { - return (1 << s.IncrementalBits) - 1 -} - -// Compose generates an auto ID based on the given shard and an incremental ID. -func (s *ShardIDFormat) Compose(shard int64, id int64) int64 { - return ((shard & ((1 << s.ShardBits) - 1)) << s.IncrementalBits) | id -} - -type allocatorRuntimeStatsCtxKeyType struct{} - -// AllocatorRuntimeStatsCtxKey is the context key of allocator runtime stats. -var AllocatorRuntimeStatsCtxKey = allocatorRuntimeStatsCtxKeyType{} - -// AllocatorRuntimeStats is the execution stats of auto id allocator. -type AllocatorRuntimeStats struct { - *txnsnapshot.SnapshotRuntimeStats - *execdetails.RuntimeStatsWithCommit - allocCount int - rebaseCount int -} - -// NewAllocatorRuntimeStats return a new AllocatorRuntimeStats. -func NewAllocatorRuntimeStats() *AllocatorRuntimeStats { - return &AllocatorRuntimeStats{ - SnapshotRuntimeStats: &txnsnapshot.SnapshotRuntimeStats{}, - } -} - -func (e *AllocatorRuntimeStats) mergeCommitDetail(detail *tikvutil.CommitDetails) { - if detail == nil { - return - } - if e.RuntimeStatsWithCommit == nil { - e.RuntimeStatsWithCommit = &execdetails.RuntimeStatsWithCommit{} - } - e.RuntimeStatsWithCommit.MergeCommitDetails(detail) -} - -// String implements the RuntimeStats interface. -func (e *AllocatorRuntimeStats) String() string { - if e.allocCount == 0 && e.rebaseCount == 0 { - return "" - } - var buf bytes.Buffer - buf.WriteString("auto_id_allocator: {") - initialSize := buf.Len() - if e.allocCount > 0 { - buf.WriteString("alloc_cnt: ") - buf.WriteString(strconv.FormatInt(int64(e.allocCount), 10)) - } - if e.rebaseCount > 0 { - if buf.Len() > initialSize { - buf.WriteString(", ") - } - buf.WriteString("rebase_cnt: ") - buf.WriteString(strconv.FormatInt(int64(e.rebaseCount), 10)) - } - if e.SnapshotRuntimeStats != nil { - stats := e.SnapshotRuntimeStats.String() - if stats != "" { - if buf.Len() > initialSize { - buf.WriteString(", ") - } - buf.WriteString(e.SnapshotRuntimeStats.String()) - } - } - if e.RuntimeStatsWithCommit != nil { - stats := e.RuntimeStatsWithCommit.String() - if stats != "" { - if buf.Len() > initialSize { - buf.WriteString(", ") - } - buf.WriteString(stats) - } - } - buf.WriteString("}") - return buf.String() -} - -// Clone implements the RuntimeStats interface. -func (e *AllocatorRuntimeStats) Clone() *AllocatorRuntimeStats { - newRs := &AllocatorRuntimeStats{ - allocCount: e.allocCount, - rebaseCount: e.rebaseCount, - } - if e.SnapshotRuntimeStats != nil { - snapshotStats := e.SnapshotRuntimeStats.Clone() - newRs.SnapshotRuntimeStats = snapshotStats - } - if e.RuntimeStatsWithCommit != nil { - newRs.RuntimeStatsWithCommit = e.RuntimeStatsWithCommit.Clone().(*execdetails.RuntimeStatsWithCommit) - } - return newRs -} - -// Merge implements the RuntimeStats interface. -func (e *AllocatorRuntimeStats) Merge(other *AllocatorRuntimeStats) { - if other == nil { - return - } - if other.SnapshotRuntimeStats != nil { - if e.SnapshotRuntimeStats == nil { - e.SnapshotRuntimeStats = other.SnapshotRuntimeStats.Clone() - } else { - e.SnapshotRuntimeStats.Merge(other.SnapshotRuntimeStats) - } - } - if other.RuntimeStatsWithCommit != nil { - if e.RuntimeStatsWithCommit == nil { - e.RuntimeStatsWithCommit = other.RuntimeStatsWithCommit.Clone().(*execdetails.RuntimeStatsWithCommit) - } else { - e.RuntimeStatsWithCommit.Merge(other.RuntimeStatsWithCommit) - } - } -} diff --git a/meta/autoid/autoid_test.go b/meta/autoid/autoid_test.go deleted file mode 100644 index 912b1e173bc8c..0000000000000 --- a/meta/autoid/autoid_test.go +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package autoid_test - -import ( - "context" - "fmt" - "math" - "math/rand" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestSignedAutoid(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 3, Name: model.NewCIStr("t1")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 4, Name: model.NewCIStr("t2")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 5, Name: model.NewCIStr("t3")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - // Since the test here is applicable to any type of allocators, autoid.RowIDAllocType is chosen. - alloc := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - - globalAutoID, err := alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - _, id, err := alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(1), id) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), id) - globalAutoID, err = alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, autoid.GetStep()+1, globalAutoID) - - // rebase - err = alloc.Rebase(context.Background(), int64(1), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3), id) - err = alloc.Rebase(context.Background(), int64(3), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(4), id) - err = alloc.Rebase(context.Background(), int64(10), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(11), id) - err = alloc.Rebase(context.Background(), int64(3010), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3011), id) - - alloc = autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, autoid.GetStep()+1, id) - - alloc = autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(1), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), id) - - alloc = autoid.NewAllocator(store, 1, 3, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(3210), false) - require.NoError(t, err) - alloc = autoid.NewAllocator(store, 1, 3, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(3000), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3211), id) - err = alloc.Rebase(context.Background(), int64(6543), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(6544), id) - - // Test the MaxInt64 is the upper bound of `alloc` function but not `rebase`. - err = alloc.Rebase(context.Background(), int64(math.MaxInt64-1), true) - require.NoError(t, err) - _, _, err = alloc.Alloc(ctx, 1, 1, 1) - require.Error(t, err) - err = alloc.Rebase(context.Background(), int64(math.MaxInt64), true) - require.NoError(t, err) - - // alloc N for signed - alloc = autoid.NewAllocator(store, 1, 4, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - globalAutoID, err = alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - min, max, err := alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(1), max-min) - require.Equal(t, int64(1), min+1) - - min, max, err = alloc.Alloc(ctx, 2, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), max-min) - require.Equal(t, int64(2), min+1) - require.Equal(t, int64(3), max) - - min, max, err = alloc.Alloc(ctx, 100, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(100), max-min) - expected := int64(4) - for i := min + 1; i <= max; i++ { - require.Equal(t, expected, i) - expected++ - } - - err = alloc.Rebase(context.Background(), int64(1000), false) - require.NoError(t, err) - min, max, err = alloc.Alloc(ctx, 3, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3), max-min) - require.Equal(t, int64(1001), min+1) - require.Equal(t, int64(1002), min+2) - require.Equal(t, int64(1003), max) - - lastRemainOne := alloc.End() - err = alloc.Rebase(context.Background(), alloc.End()-2, false) - require.NoError(t, err) - min, max, err = alloc.Alloc(ctx, 5, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(5), max-min) - require.Greater(t, min+1, lastRemainOne) - - // Test for increment & offset for signed. - alloc = autoid.NewAllocator(store, 1, 5, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - - increment := int64(2) - offset := int64(100) - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - min, max, err = alloc.Alloc(ctx, 1, increment, offset) - require.NoError(t, err) - require.Equal(t, int64(99), min) - require.Equal(t, int64(100), max) - - min, max, err = alloc.Alloc(ctx, 2, increment, offset) - require.NoError(t, err) - require.Equal(t, int64(4), max-min) - require.Equal(t, autoid.CalcNeededBatchSize(100, 2, increment, offset, false), max-min) - require.Equal(t, int64(100), min) - require.Equal(t, int64(104), max) - - increment = int64(5) - min, max, err = alloc.Alloc(ctx, 3, increment, offset) - require.NoError(t, err) - require.Equal(t, int64(11), max-min) - require.Equal(t, autoid.CalcNeededBatchSize(104, 3, increment, offset, false), max-min) - require.Equal(t, int64(104), min) - require.Equal(t, int64(115), max) - firstID := autoid.SeekToFirstAutoIDSigned(104, increment, offset) - require.Equal(t, int64(105), firstID) - - increment = int64(15) - min, max, err = alloc.Alloc(ctx, 2, increment, offset) - require.NoError(t, err) - require.Equal(t, int64(30), max-min) - require.Equal(t, autoid.CalcNeededBatchSize(115, 2, increment, offset, false), max-min) - require.Equal(t, int64(115), min) - require.Equal(t, int64(145), max) - firstID = autoid.SeekToFirstAutoIDSigned(115, increment, offset) - require.Equal(t, int64(130), firstID) - - offset = int64(200) - min, max, err = alloc.Alloc(ctx, 2, increment, offset) - require.NoError(t, err) - require.Equal(t, int64(16), max-min) - // offset-1 > base will cause alloc rebase to offset-1. - require.Equal(t, autoid.CalcNeededBatchSize(offset-1, 2, increment, offset, false), max-min) - require.Equal(t, int64(199), min) - require.Equal(t, int64(215), max) - firstID = autoid.SeekToFirstAutoIDSigned(offset-1, increment, offset) - require.Equal(t, int64(200), firstID) -} - -func TestUnsignedAutoid(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) - }() - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 3, Name: model.NewCIStr("t1")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 4, Name: model.NewCIStr("t2")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 5, Name: model.NewCIStr("t3")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - alloc := autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - - globalAutoID, err := alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - _, id, err := alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(1), id) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), id) - globalAutoID, err = alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, autoid.GetStep()+1, globalAutoID) - - // rebase - err = alloc.Rebase(context.Background(), int64(1), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3), id) - err = alloc.Rebase(context.Background(), int64(3), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(4), id) - err = alloc.Rebase(context.Background(), int64(10), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(11), id) - err = alloc.Rebase(context.Background(), int64(3010), true) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3011), id) - - alloc = autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, autoid.GetStep()+1, id) - - alloc = autoid.NewAllocator(store, 1, 2, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(1), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), id) - - alloc = autoid.NewAllocator(store, 1, 3, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(3210), false) - require.NoError(t, err) - alloc = autoid.NewAllocator(store, 1, 3, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - err = alloc.Rebase(context.Background(), int64(3000), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(3211), id) - err = alloc.Rebase(context.Background(), int64(6543), false) - require.NoError(t, err) - _, id, err = alloc.Alloc(ctx, 1, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(6544), id) - - // Test the MaxUint64 is the upper bound of `alloc` func but not `rebase`. - var n uint64 = math.MaxUint64 - 1 - un := int64(n) - err = alloc.Rebase(context.Background(), un, true) - require.NoError(t, err) - _, _, err = alloc.Alloc(ctx, 1, 1, 1) - require.Error(t, err) - un = int64(n + 1) - err = alloc.Rebase(context.Background(), un, true) - require.NoError(t, err) - - // alloc N for unsigned - alloc = autoid.NewAllocator(store, 1, 4, true, autoid.RowIDAllocType) - require.NotNil(t, alloc) - globalAutoID, err = alloc.NextGlobalAutoID() - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - - min, max, err := alloc.Alloc(ctx, 2, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), max-min) - require.Equal(t, int64(1), min+1) - require.Equal(t, int64(2), max) - - err = alloc.Rebase(context.Background(), int64(500), true) - require.NoError(t, err) - min, max, err = alloc.Alloc(ctx, 2, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(2), max-min) - require.Equal(t, int64(501), min+1) - require.Equal(t, int64(502), max) - - lastRemainOne := alloc.End() - err = alloc.Rebase(context.Background(), alloc.End()-2, false) - require.NoError(t, err) - min, max, err = alloc.Alloc(ctx, 5, 1, 1) - require.NoError(t, err) - require.Equal(t, int64(5), max-min) - require.Greater(t, min+1, lastRemainOne) - - // Test increment & offset for unsigned. Using AutoRandomType to avoid valid range check for increment and offset. - alloc = autoid.NewAllocator(store, 1, 5, true, autoid.AutoRandomType) - require.NotNil(t, alloc) - require.NoError(t, err) - require.Equal(t, int64(1), globalAutoID) - - increment := int64(2) - n = math.MaxUint64 - 100 - offset := int64(n) - - min, max, err = alloc.Alloc(ctx, 2, increment, offset) - require.NoError(t, err) - require.Equal(t, uint64(math.MaxUint64-101), uint64(min)) - require.Equal(t, uint64(math.MaxUint64-98), uint64(max)) - - require.Equal(t, autoid.CalcNeededBatchSize(int64(uint64(offset)-1), 2, increment, offset, true), max-min) - firstID := autoid.SeekToFirstAutoIDUnSigned(uint64(min), uint64(increment), uint64(offset)) - require.Equal(t, uint64(math.MaxUint64-100), firstID) -} - -// TestConcurrentAlloc is used for the test that -// multiple allocators allocate ID with the same table ID concurrently. -func TestConcurrentAlloc(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - autoid.SetStep(100) - defer func() { - autoid.SetStep(5000) - }() - - dbID := int64(2) - tblID := int64(100) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - var mu sync.Mutex - var wg util.WaitGroupWrapper - m := map[int64]struct{}{} - count := 10 - errCh := make(chan error, count) - - allocIDs := func() { - ctx := context.Background() - alloc := autoid.NewAllocator(store, dbID, tblID, false, autoid.RowIDAllocType) - for j := 0; j < int(autoid.GetStep())+5; j++ { - _, id, err1 := alloc.Alloc(ctx, 1, 1, 1) - if err1 != nil { - errCh <- err1 - break - } - - mu.Lock() - if _, ok := m[id]; ok { - errCh <- fmt.Errorf("duplicate id:%v", id) - mu.Unlock() - break - } - m[id] = struct{}{} - mu.Unlock() - - // test Alloc N - N := rand.Uint64() % 100 - min, max, err1 := alloc.Alloc(ctx, N, 1, 1) - if err1 != nil { - errCh <- err1 - break - } - - errFlag := false - mu.Lock() - for i := min + 1; i <= max; i++ { - if _, ok := m[i]; ok { - errCh <- fmt.Errorf("duplicate id:%v", i) - errFlag = true - mu.Unlock() - break - } - m[i] = struct{}{} - } - if errFlag { - break - } - mu.Unlock() - } - } - for i := 0; i < count; i++ { - num := 1 - wg.Run(func() { - time.Sleep(time.Duration(num%10) * time.Microsecond) - allocIDs() - }) - } - wg.Wait() - - close(errCh) - err = <-errCh - require.NoError(t, err) -} - -// TestRollbackAlloc tests that when the allocation transaction commit failed, -// the local variable base and end doesn't change. -func TestRollbackAlloc(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - dbID := int64(1) - tblID := int64(2) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - injectConf := new(kv.InjectionConfig) - injectConf.SetCommitError(errors.New("injected")) - injectedStore := kv.NewInjectedStore(store, injectConf) - alloc := autoid.NewAllocator(injectedStore, 1, 2, false, autoid.RowIDAllocType) - _, _, err = alloc.Alloc(ctx, 1, 1, 1) - require.Error(t, err) - require.Equal(t, int64(0), alloc.Base()) - require.Equal(t, int64(0), alloc.End()) - - err = alloc.Rebase(context.Background(), 100, true) - require.Error(t, err) - require.Equal(t, int64(0), alloc.Base()) - require.Equal(t, int64(0), alloc.End()) -} - -// TestNextStep tests generate next auto id step. -func TestNextStep(t *testing.T) { - nextStep := autoid.NextStep(2000000, 1*time.Nanosecond) - require.Equal(t, int64(2000000), nextStep) - nextStep = autoid.NextStep(678910, 10*time.Second) - require.Equal(t, int64(678910), nextStep) - nextStep = autoid.NextStep(50000, 10*time.Minute) - require.Equal(t, int64(30000), nextStep) -} - -// Fix a computation logic bug in allocator computation. -func TestAllocComputationIssue(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDCustomize", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDCustomize")) - }() - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - // Since the test here is applicable to any type of allocators, autoid.RowIDAllocType is chosen. - unsignedAlloc1 := autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) - require.NotNil(t, unsignedAlloc1) - signedAlloc1 := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) - require.NotNil(t, signedAlloc1) - signedAlloc2 := autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) - require.NotNil(t, signedAlloc2) - - // the next valid two value must be 13 & 16, batch size = 6. - err = unsignedAlloc1.Rebase(context.Background(), 10, false) - require.NoError(t, err) - // the next valid two value must be 10 & 13, batch size = 6. - err = signedAlloc2.Rebase(context.Background(), 7, false) - require.NoError(t, err) - // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. - autoid.TestModifyBaseAndEndInjection(unsignedAlloc1, 9, 9) - // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. - autoid.TestModifyBaseAndEndInjection(signedAlloc1, 4, 6) - - // Here will recompute the new allocator batch size base on new base = 10, which will get 6. - min, max, err := unsignedAlloc1.Alloc(ctx, 2, 3, 1) - require.NoError(t, err) - require.Equal(t, int64(10), min) - require.Equal(t, int64(16), max) - min, max, err = signedAlloc2.Alloc(ctx, 2, 3, 1) - require.NoError(t, err) - require.Equal(t, int64(7), min) - require.Equal(t, int64(13), max) -} - -func TestIssue40584(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - err := store.Close() - require.NoError(t, err) - }() - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) - require.NoError(t, err) - err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) - require.NoError(t, err) - return nil - }) - require.NoError(t, err) - - alloc := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) - require.NotNil(t, alloc) - - finishAlloc := make(chan bool) - finishBase := make(chan bool) - var done int32 = 0 - - // call allocator.Alloc and allocator.Base in parallel for 3 seconds to detect data race - go func() { - for { - alloc.Alloc(ctx, 1, 1, 1) - if atomic.LoadInt32(&done) > 0 { - break - } - } - finishAlloc <- true - }() - - go func() { - for { - alloc.Base() - if atomic.LoadInt32(&done) > 0 { - break - } - } - finishBase <- true - }() - - runTime := time.NewTimer(time.Second * 3) - <-runTime.C - atomic.AddInt32(&done, 1) - <-finishAlloc - <-finishBase -} diff --git a/meta/autoid/bench_test.go b/meta/autoid/bench_test.go deleted file mode 100644 index d8b489060875d..0000000000000 --- a/meta/autoid/bench_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package autoid_test - -import ( - "context" - "fmt" - "math" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" -) - -func BenchmarkAllocator_Alloc(b *testing.B) { - b.StopTimer() - store, err := mockstore.NewMockStore() - if err != nil { - return - } - defer func() { - err := store.Close() - if err != nil { - b.Fatal(err) - } - }() - dbID := int64(1) - tblID := int64(2) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) - if err != nil { - return err - } - err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) - if err != nil { - return err - } - return nil - }) - if err != nil { - return - } - alloc := autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) - b.StartTimer() - for i := 0; i < b.N; i++ { - _, _, err := alloc.Alloc(ctx, 1, 1, 1) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkAllocator_SequenceAlloc(b *testing.B) { - b.StopTimer() - store, err := mockstore.NewMockStore() - if err != nil { - return - } - defer func() { - err := store.Close() - if err != nil { - b.Fatal(err) - } - }() - var seq *model.SequenceInfo - var sequenceBase int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) - if err != nil { - return err - } - seq = &model.SequenceInfo{ - Start: 1, - Cycle: true, - Cache: false, - MinValue: -10, - MaxValue: math.MaxInt64, - Increment: 2, - CacheValue: 2000000, - } - seqTable := &model.TableInfo{ - ID: 1, - Name: model.NewCIStr("seq"), - Sequence: seq, - } - sequenceBase = seq.Start - 1 - err = m.CreateSequenceAndSetSeqValue(1, seqTable, sequenceBase) - return err - }) - if err != nil { - return - } - alloc := autoid.NewSequenceAllocator(store, 1, 1, seq) - b.StartTimer() - for i := 0; i < b.N; i++ { - _, _, _, err := alloc.AllocSeqCache() - if err != nil { - fmt.Println("err") - } - } -} - -func BenchmarkAllocator_Seek(b *testing.B) { - base := int64(21421948021) - offset := int64(-351354365326) - increment := int64(3) - b.StartTimer() - for i := 0; i < b.N; i++ { - _, err := autoid.CalcSequenceBatchSize(base, 3, increment, offset, math.MinInt64, math.MaxInt64) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/meta/autoid/errors.go b/meta/autoid/errors.go deleted file mode 100644 index 6d76e2dbe3808..0000000000000 --- a/meta/autoid/errors.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package autoid - -import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -// Error instances. -var ( - errInvalidTableID = dbterror.ClassAutoid.NewStd(mysql.ErrInvalidTableID) - errInvalidIncrementAndOffset = dbterror.ClassAutoid.NewStd(mysql.ErrInvalidIncrementAndOffset) - errNotImplemented = dbterror.ClassAutoid.NewStd(mysql.ErrNotImplemented) - ErrAutoincReadFailed = dbterror.ClassAutoid.NewStd(mysql.ErrAutoincReadFailed) - ErrWrongAutoKey = dbterror.ClassAutoid.NewStd(mysql.ErrWrongAutoKey) - ErrInvalidAllocatorType = dbterror.ClassAutoid.NewStd(mysql.ErrUnknownAllocatorType) - ErrAutoRandReadFailed = dbterror.ClassAutoid.NewStd(mysql.ErrAutoRandReadFailed) -) - -const ( - // AutoRandomMustFirstColumnInPK is reported when auto_random is not the first column in primary key. - AutoRandomMustFirstColumnInPK = "column '%s' must be the first column in primary key" - // AutoRandomNoClusteredPKErrMsg indicates the primary key is not clustered. - AutoRandomNoClusteredPKErrMsg = "auto_random is only supported on the tables with clustered primary key" - // AutoRandomIncompatibleWithAutoIncErrMsg is reported when auto_random and auto_increment are specified on the same column. - AutoRandomIncompatibleWithAutoIncErrMsg = "auto_random is incompatible with auto_increment" - // AutoRandomIncompatibleWithDefaultValueErrMsg is reported when auto_random and default are specified on the same column. - AutoRandomIncompatibleWithDefaultValueErrMsg = "auto_random is incompatible with default" - // AutoRandomOverflowErrMsg is reported when auto_random is greater than max length of a MySQL data type. - AutoRandomOverflowErrMsg = "max allowed auto_random shard bits is %d, but got %d on column `%s`" - // AutoRandomModifyColTypeErrMsg is reported when a user is trying to modify the type of a column specified with auto_random. - AutoRandomModifyColTypeErrMsg = "modifying the auto_random column type is not supported" - // AutoRandomAlterErrMsg is reported when a user is trying to add/drop/modify the value of auto_random attribute. - AutoRandomAlterErrMsg = "adding/dropping/modifying auto_random is not supported" - // AutoRandomDecreaseBitErrMsg is reported when the auto_random shard bits is decreased. - AutoRandomDecreaseBitErrMsg = "decreasing auto_random shard bits is not supported" - // AutoRandomNonPositive is reported then a user specifies a non-positive value for auto_random. - AutoRandomNonPositive = "the value of auto_random should be positive" - // AutoRandomAvailableAllocTimesNote is reported when a table containing auto_random is created. - AutoRandomAvailableAllocTimesNote = "Available implicit allocation times: %d" - // AutoRandomExplicitInsertDisabledErrMsg is reported when auto_random column value is explicitly specified, but the session var 'allow_auto_random_explicit_insert' is false. - AutoRandomExplicitInsertDisabledErrMsg = "Explicit insertion on auto_random column is disabled. Try to set @@allow_auto_random_explicit_insert = true." - // AutoRandomOnNonBigIntColumn is reported when define auto random to non bigint column - AutoRandomOnNonBigIntColumn = "auto_random option must be defined on `bigint` column, but not on `%s` column" - // AutoRandomRebaseNotApplicable is reported when alter auto_random base on a non auto_random table. - AutoRandomRebaseNotApplicable = "alter auto_random_base of a non auto_random table" - // AutoRandomRebaseOverflow is reported when alter auto_random_base to a value that overflows the incremental bits. - AutoRandomRebaseOverflow = "alter auto_random_base to %d overflows the incremental bits, max allowed base is %d" - // AutoRandomAlterAddColumn is reported when adding an auto_random column. - AutoRandomAlterAddColumn = "unsupported add column '%s' constraint AUTO_RANDOM when altering '%s.%s'" - // AutoRandomAlterChangeFromAutoInc is reported when the column is changing from a non-auto_increment or a non-primary key. - AutoRandomAlterChangeFromAutoInc = "auto_random can only be converted from auto_increment clustered primary key" - // AutoRandomAllocatorNotFound is reported when auto_random ID allocator not found during changing from auto_inc to auto_random. - AutoRandomAllocatorNotFound = "auto_random ID allocator not found in table '%s.%s'" - // AutoRandomInvalidRangeBits is reported when the auto_random_range_bits is invalid. - AutoRandomInvalidRangeBits = "auto_random range bits must be between %d and %d, but got %d" - // AutoRandomIncrementalBitsTooSmall is reported when the auto_random available use space is too small. - AutoRandomIncrementalBitsTooSmall = "auto_random ID space is too small, please decrease the shard bits or increase the range bits" - // AutoRandomUnsupportedAlterRangeBits is reported when the auto_random range_bits is changed. - AutoRandomUnsupportedAlterRangeBits = "alter the range bits of auto_random column is not supported" -) diff --git a/meta/autoid/main_test.go b/meta/autoid/main_test.go deleted file mode 100644 index 40a7da98f94d7..0000000000000 --- a/meta/autoid/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package autoid_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/meta/main_test.go b/meta/main_test.go deleted file mode 100644 index 57e1b739ae0bc..0000000000000 --- a/meta/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package meta - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/metrics/BUILD.bazel b/metrics/BUILD.bazel deleted file mode 100644 index 43202ffe47865..0000000000000 --- a/metrics/BUILD.bazel +++ /dev/null @@ -1,66 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "metrics", - srcs = [ - "bindinfo.go", - "ddl.go", - "distsql.go", - "disttask.go", - "domain.go", - "executor.go", - "gc_worker.go", - "import.go", - "log_backup.go", - "meta.go", - "metrics.go", - "owner.go", - "resourcemanager.go", - "server.go", - "session.go", - "sli.go", - "stats.go", - "telemetry.go", - "topsql.go", - "ttl.go", - "wrapper.go", - ], - importpath = "github.com/pingcap/tidb/metrics", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/lightning/metric", - "//disttask/framework/proto", - "//parser/terror", - "//timer/metrics", - "//util/logutil", - "//util/mathutil", - "//util/promutil", - "@com_github_pingcap_errors//:errors", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_golang//prometheus/collectors", - "@com_github_prometheus_client_model//go", - "@com_github_tikv_client_go_v2//metrics", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "metrics_test", - timeout = "short", - srcs = [ - "main_test.go", - "metrics_internal_test.go", - "metrics_test.go", - ], - embed = [":metrics"], - flaky = True, - shard_count = 4, - deps = [ - "//parser/terror", - "//statistics/handle/cache", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/metrics/import.go b/metrics/import.go deleted file mode 100644 index 7a7c1d091d65e..0000000000000 --- a/metrics/import.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/br/pkg/lightning/metric" - "github.com/pingcap/tidb/util/promutil" - "github.com/prometheus/client_golang/prometheus" -) - -const importMetricSubsystem = "import" - -// GetRegisteredImportMetrics returns the registered import metrics. -func GetRegisteredImportMetrics(factory promutil.Factory, constLabels prometheus.Labels) *metric.Common { - metrics := metric.NewCommon(factory, TiDB, importMetricSubsystem, constLabels) - metrics.RegisterTo(prometheus.DefaultRegisterer) - return metrics -} - -// UnregisterImportMetrics unregisters the registered import metrics. -func UnregisterImportMetrics(metrics *metric.Common) { - metrics.UnregisterFrom(prometheus.DefaultRegisterer) -} diff --git a/metrics/main_test.go b/metrics/main_test.go deleted file mode 100644 index 78595382d8e0b..0000000000000 --- a/metrics/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/metrics/metrics.go b/metrics/metrics.go deleted file mode 100644 index 973a04003ea86..0000000000000 --- a/metrics/metrics.go +++ /dev/null @@ -1,326 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "sync" - - timermetrics "github.com/pingcap/tidb/timer/metrics" - "github.com/pingcap/tidb/util/logutil" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - tikvmetrics "github.com/tikv/client-go/v2/metrics" - "go.uber.org/zap" -) - -var ( - // PanicCounter measures the count of panics. - PanicCounter *prometheus.CounterVec - - // MemoryUsage measures the usage gauge of memory. - MemoryUsage *prometheus.GaugeVec -) - -// metrics labels. -const ( - LabelSession = "session" - LabelDomain = "domain" - LabelDDLOwner = "ddl-owner" - LabelDDL = "ddl" - LabelDDLWorker = "ddl-worker" - LabelDistReorg = "dist-reorg" - LabelDDLSyncer = "ddl-syncer" - LabelGCWorker = "gcworker" - LabelAnalyze = "analyze" - LabelWorkerPool = "worker-pool" - - LabelBatchRecvLoop = "batch-recv-loop" - LabelBatchSendLoop = "batch-send-loop" - - opSucc = "ok" - opFailed = "err" - - TiDB = "tidb" - LabelScope = "scope" - ScopeGlobal = "global" - ScopeSession = "session" - Server = "server" - TiKVClient = "tikvclient" -) - -// RetLabel returns "ok" when err == nil and "err" when err != nil. -// This could be useful when you need to observe the operation result. -func RetLabel(err error) string { - if err == nil { - return opSucc - } - return opFailed -} - -func init() { - InitMetrics() -} - -// InitMetrics is used to initialize metrics. -func InitMetrics() { - InitBindInfoMetrics() - InitDDLMetrics() - InitDistSQLMetrics() - InitDomainMetrics() - InitExecutorMetrics() - InitGCWorkerMetrics() - InitLogBackupMetrics() - InitMetaMetrics() - InitOwnerMetrics() - InitResourceManagerMetrics() - InitServerMetrics() - InitSessionMetrics() - InitSliMetrics() - InitStatsMetrics() - InitTelemetryMetrics() - InitTopSQLMetrics() - InitTTLMetrics() - InitDistTaskMetrics() - timermetrics.InitTimerMetrics() - - PanicCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "panic_total", - Help: "Counter of panic.", - }, []string{LblType}) - - MemoryUsage = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "memory_usage", - Help: "Memory Usage", - }, []string{LblModule, LblType}) -} - -// RegisterMetrics registers the metrics which are ONLY used in TiDB server. -func RegisterMetrics() { - // use new go collector - prometheus.DefaultRegisterer.Unregister(prometheus.NewGoCollector()) - prometheus.MustRegister(collectors.NewGoCollector(collectors.WithGoCollections(collectors.GoRuntimeMetricsCollection | collectors.GoRuntimeMemStatsCollection))) - - prometheus.MustRegister(AutoAnalyzeCounter) - prometheus.MustRegister(AutoAnalyzeHistogram) - prometheus.MustRegister(AutoIDHistogram) - prometheus.MustRegister(BatchAddIdxHistogram) - prometheus.MustRegister(BindUsageCounter) - prometheus.MustRegister(BindTotalGauge) - prometheus.MustRegister(BindMemoryUsage) - prometheus.MustRegister(CampaignOwnerCounter) - prometheus.MustRegister(ConnGauge) - prometheus.MustRegister(DisconnectionCounter) - prometheus.MustRegister(PreparedStmtGauge) - prometheus.MustRegister(CriticalErrorCounter) - prometheus.MustRegister(DDLCounter) - prometheus.MustRegister(BackfillTotalCounter) - prometheus.MustRegister(BackfillProgressGauge) - prometheus.MustRegister(DDLWorkerHistogram) - prometheus.MustRegister(DDLJobTableDuration) - prometheus.MustRegister(DDLRunningJobCount) - prometheus.MustRegister(DeploySyncerHistogram) - prometheus.MustRegister(DistSQLPartialCountHistogram) - prometheus.MustRegister(DistSQLCoprCacheCounter) - prometheus.MustRegister(DistSQLCoprClosestReadCounter) - prometheus.MustRegister(DistSQLCoprRespBodySize) - prometheus.MustRegister(DistSQLQueryHistogram) - prometheus.MustRegister(DistSQLScanKeysHistogram) - prometheus.MustRegister(DistSQLScanKeysPartialHistogram) - prometheus.MustRegister(ExecuteErrorCounter) - prometheus.MustRegister(ExecutorCounter) - prometheus.MustRegister(GetTokenDurationHistogram) - prometheus.MustRegister(NumOfMultiQueryHistogram) - prometheus.MustRegister(HandShakeErrorCounter) - prometheus.MustRegister(HandleJobHistogram) - prometheus.MustRegister(SyncLoadCounter) - prometheus.MustRegister(SyncLoadTimeoutCounter) - prometheus.MustRegister(SyncLoadHistogram) - prometheus.MustRegister(ReadStatsHistogram) - prometheus.MustRegister(JobsGauge) - prometheus.MustRegister(LoadPrivilegeCounter) - prometheus.MustRegister(InfoCacheCounters) - prometheus.MustRegister(LoadSchemaCounter) - prometheus.MustRegister(LoadSchemaDuration) - prometheus.MustRegister(MetaHistogram) - prometheus.MustRegister(NewSessionHistogram) - prometheus.MustRegister(OwnerHandleSyncerHistogram) - prometheus.MustRegister(PanicCounter) - prometheus.MustRegister(PlanCacheCounter) - prometheus.MustRegister(PlanCacheMissCounter) - prometheus.MustRegister(PlanCacheInstanceMemoryUsage) - prometheus.MustRegister(PlanCacheInstancePlanNumCounter) - prometheus.MustRegister(PseudoEstimation) - prometheus.MustRegister(PacketIOCounter) - prometheus.MustRegister(QueryDurationHistogram) - prometheus.MustRegister(QueryTotalCounter) - prometheus.MustRegister(AffectedRowsCounter) - prometheus.MustRegister(SchemaLeaseErrorCounter) - prometheus.MustRegister(ServerEventCounter) - prometheus.MustRegister(SessionExecuteCompileDuration) - prometheus.MustRegister(SessionExecuteParseDuration) - prometheus.MustRegister(SessionExecuteRunDuration) - prometheus.MustRegister(SessionRestrictedSQLCounter) - prometheus.MustRegister(SessionRetry) - prometheus.MustRegister(SessionRetryErrorCounter) - prometheus.MustRegister(StatementPerTransaction) - prometheus.MustRegister(StatsInaccuracyRate) - prometheus.MustRegister(StmtNodeCounter) - prometheus.MustRegister(DbStmtNodeCounter) - prometheus.MustRegister(ExecPhaseDuration) - prometheus.MustRegister(OngoingTxnDurationHistogram) - prometheus.MustRegister(MppCoordinatorStats) - prometheus.MustRegister(MppCoordinatorLatency) - prometheus.MustRegister(TimeJumpBackCounter) - prometheus.MustRegister(TransactionDuration) - prometheus.MustRegister(StatementDeadlockDetectDuration) - prometheus.MustRegister(StatementPessimisticRetryCount) - prometheus.MustRegister(StatementLockKeysCount) - prometheus.MustRegister(ValidateReadTSFromPDCount) - prometheus.MustRegister(UpdateSelfVersionHistogram) - prometheus.MustRegister(WatchOwnerCounter) - prometheus.MustRegister(GCActionRegionResultCounter) - prometheus.MustRegister(GCConfigGauge) - prometheus.MustRegister(GCHistogram) - prometheus.MustRegister(GCJobFailureCounter) - prometheus.MustRegister(GCRegionTooManyLocksCounter) - prometheus.MustRegister(GCWorkerCounter) - prometheus.MustRegister(TotalQueryProcHistogram) - prometheus.MustRegister(TotalCopProcHistogram) - prometheus.MustRegister(TotalCopWaitHistogram) - prometheus.MustRegister(CopMVCCRatioHistogram) - prometheus.MustRegister(HandleSchemaValidate) - prometheus.MustRegister(MaxProcs) - prometheus.MustRegister(GOGC) - prometheus.MustRegister(ConnIdleDurationHistogram) - prometheus.MustRegister(ServerInfo) - prometheus.MustRegister(TokenGauge) - prometheus.MustRegister(ConfigStatus) - prometheus.MustRegister(TiFlashQueryTotalCounter) - prometheus.MustRegister(TiFlashFailedMPPStoreState) - prometheus.MustRegister(SmallTxnWriteDuration) - prometheus.MustRegister(TxnWriteThroughput) - prometheus.MustRegister(LoadSysVarCacheCounter) - prometheus.MustRegister(TopSQLIgnoredCounter) - prometheus.MustRegister(TopSQLReportDurationHistogram) - prometheus.MustRegister(TopSQLReportDataHistogram) - prometheus.MustRegister(PDAPIExecutionHistogram) - prometheus.MustRegister(PDAPIRequestCounter) - prometheus.MustRegister(CPUProfileCounter) - prometheus.MustRegister(ReadFromTableCacheCounter) - prometheus.MustRegister(LoadTableCacheDurationHistogram) - prometheus.MustRegister(NonTransactionalDMLCount) - prometheus.MustRegister(PessimisticDMLDurationByAttempt) - prometheus.MustRegister(ResourceGroupQueryTotalCounter) - prometheus.MustRegister(MemoryUsage) - prometheus.MustRegister(StatsCacheCounter) - prometheus.MustRegister(StatsCacheGauge) - prometheus.MustRegister(StatsHealthyGauge) - prometheus.MustRegister(TxnStatusEnteringCounter) - prometheus.MustRegister(TxnDurationHistogram) - prometheus.MustRegister(LastCheckpoint) - prometheus.MustRegister(AdvancerOwner) - prometheus.MustRegister(AdvancerTickDuration) - prometheus.MustRegister(GetCheckpointBatchSize) - prometheus.MustRegister(RegionCheckpointRequest) - prometheus.MustRegister(RegionCheckpointFailure) - prometheus.MustRegister(AutoIDReqDuration) - prometheus.MustRegister(RegionCheckpointSubscriptionEvent) - prometheus.MustRegister(RCCheckTSWriteConfilictCounter) - prometheus.MustRegister(FairLockingUsageCount) - - prometheus.MustRegister(TTLQueryDuration) - prometheus.MustRegister(TTLProcessedExpiredRowsCounter) - prometheus.MustRegister(TTLJobStatus) - prometheus.MustRegister(TTLTaskStatus) - prometheus.MustRegister(TTLPhaseTime) - prometheus.MustRegister(TTLInsertRowsCount) - prometheus.MustRegister(TTLWatermarkDelay) - prometheus.MustRegister(TTLEventCounter) - - prometheus.MustRegister(timermetrics.TimerEventCounter) - - prometheus.MustRegister(EMACPUUsageGauge) - prometheus.MustRegister(PoolConcurrencyCounter) - - prometheus.MustRegister(HistoricalStatsCounter) - prometheus.MustRegister(PlanReplayerTaskCounter) - prometheus.MustRegister(PlanReplayerRegisterTaskGauge) - - prometheus.MustRegister(DistTaskGauge) - prometheus.MustRegister(DistTaskStarttimeGauge) - prometheus.MustRegister(DistTaskSubTaskCntGauge) - prometheus.MustRegister(DistTaskSubTaskStartTimeGauge) - - tikvmetrics.InitMetrics(TiDB, TiKVClient) - tikvmetrics.RegisterMetrics() - tikvmetrics.TiKVPanicCounter = PanicCounter // reset tidb metrics for tikv metrics -} - -var mode struct { - sync.Mutex - isSimplified bool -} - -// ToggleSimplifiedMode is used to register/unregister the metrics that unused by grafana. -func ToggleSimplifiedMode(simplified bool) { - var unusedMetricsByGrafana = []prometheus.Collector{ - StatementDeadlockDetectDuration, - ValidateReadTSFromPDCount, - LoadTableCacheDurationHistogram, - TxnWriteThroughput, - SmallTxnWriteDuration, - InfoCacheCounters, - ReadFromTableCacheCounter, - TiFlashQueryTotalCounter, - TiFlashFailedMPPStoreState, - CampaignOwnerCounter, - NonTransactionalDMLCount, - MemoryUsage, - TokenGauge, - tikvmetrics.TiKVRawkvSizeHistogram, - tikvmetrics.TiKVRawkvCmdHistogram, - tikvmetrics.TiKVReadThroughput, - tikvmetrics.TiKVSmallReadDuration, - tikvmetrics.TiKVBatchWaitOverLoad, - tikvmetrics.TiKVBatchClientRecycle, - tikvmetrics.TiKVRequestRetryTimesHistogram, - tikvmetrics.TiKVStatusDuration, - } - mode.Lock() - defer mode.Unlock() - if mode.isSimplified == simplified { - return - } - mode.isSimplified = simplified - if simplified { - for _, m := range unusedMetricsByGrafana { - prometheus.Unregister(m) - } - } else { - for _, m := range unusedMetricsByGrafana { - err := prometheus.Register(m) - if err != nil { - logutil.BgLogger().Error("cannot register metrics", zap.Error(err)) - break - } - } - } -} diff --git a/metrics/server.go b/metrics/server.go deleted file mode 100644 index e3e9f582ad057..0000000000000 --- a/metrics/server.go +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/prometheus/client_golang/prometheus" -) - -var ( - // ResettablePlanCacheCounterFortTest be used to support reset counter in test. - ResettablePlanCacheCounterFortTest = false -) - -// Metrics -var ( - PacketIOCounter *prometheus.CounterVec - QueryDurationHistogram *prometheus.HistogramVec - QueryTotalCounter *prometheus.CounterVec - AffectedRowsCounter *prometheus.CounterVec - ConnGauge prometheus.Gauge - DisconnectionCounter *prometheus.CounterVec - PreparedStmtGauge prometheus.Gauge - ExecuteErrorCounter *prometheus.CounterVec - CriticalErrorCounter prometheus.Counter - - EventStart = "start" - EventGracefulDown = "graceful_shutdown" - // Eventkill occurs when the server.Kill() function is called. - EventKill = "kill" - EventClose = "close" - - ServerEventCounter *prometheus.CounterVec - TimeJumpBackCounter prometheus.Counter - PlanCacheCounter *prometheus.CounterVec - PlanCacheMissCounter *prometheus.CounterVec - PlanCacheInstanceMemoryUsage *prometheus.GaugeVec - PlanCacheInstancePlanNumCounter *prometheus.GaugeVec - ReadFromTableCacheCounter prometheus.Counter - HandShakeErrorCounter prometheus.Counter - GetTokenDurationHistogram prometheus.Histogram - NumOfMultiQueryHistogram prometheus.Histogram - TotalQueryProcHistogram *prometheus.HistogramVec - TotalCopProcHistogram *prometheus.HistogramVec - TotalCopWaitHistogram *prometheus.HistogramVec - CopMVCCRatioHistogram *prometheus.HistogramVec - MaxProcs prometheus.Gauge - GOGC prometheus.Gauge - ConnIdleDurationHistogram *prometheus.HistogramVec - ServerInfo *prometheus.GaugeVec - TokenGauge prometheus.Gauge - ConfigStatus *prometheus.GaugeVec - TiFlashQueryTotalCounter *prometheus.CounterVec - TiFlashFailedMPPStoreState *prometheus.GaugeVec - PDAPIExecutionHistogram *prometheus.HistogramVec - PDAPIRequestCounter *prometheus.CounterVec - CPUProfileCounter prometheus.Counter - LoadTableCacheDurationHistogram prometheus.Histogram - RCCheckTSWriteConfilictCounter *prometheus.CounterVec -) - -// InitServerMetrics initializes server metrics. -func InitServerMetrics() { - PacketIOCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "packet_io_bytes", - Help: "Counters of packet IO bytes.", - }, []string{LblType}) - - QueryDurationHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "handle_query_duration_seconds", - Help: "Bucketed histogram of processing time (s) of handled queries.", - Buckets: prometheus.ExponentialBuckets(0.0005, 2, 29), // 0.5ms ~ 1.5days - }, []string{LblSQLType, LblDb, LblResourceGroup}) - - QueryTotalCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "query_total", - Help: "Counter of queries.", - }, []string{LblType, LblResult}) - - AffectedRowsCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "affected_rows", - Help: "Counters of server affected rows.", - }, []string{LblSQLType}) - - ConnGauge = NewGauge( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "connections", - Help: "Number of connections.", - }) - - DisconnectionCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "disconnection_total", - Help: "Counter of connections disconnected.", - }, []string{LblResult}) - - PreparedStmtGauge = NewGauge(prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "prepared_stmts", - Help: "number of prepared statements.", - }) - - ExecuteErrorCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "execute_error_total", - Help: "Counter of execute errors.", - }, []string{LblType, LblDb}) - - CriticalErrorCounter = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "critical_error_total", - Help: "Counter of critical errors.", - }) - - ServerEventCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "event_total", - Help: "Counter of tidb-server event.", - }, []string{LblType}) - - TimeJumpBackCounter = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "monitor", - Name: "time_jump_back_total", - Help: "Counter of system time jumps backward.", - }) - - PlanCacheCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "plan_cache_total", - Help: "Counter of query using plan cache.", - }, []string{LblType}) - - PlanCacheMissCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "plan_cache_miss_total", - Help: "Counter of plan cache miss.", - }, []string{LblType}) - - PlanCacheInstanceMemoryUsage = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "plan_cache_instance_memory_usage", - Help: "Total plan cache memory usage of all sessions in a instance", - }, []string{LblType}) - - PlanCacheInstancePlanNumCounter = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "plan_cache_instance_plan_num_total", - Help: "Counter of plan of all prepared plan cache in a instance", - }, []string{LblType}) - - ReadFromTableCacheCounter = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "read_from_tablecache_total", - Help: "Counter of query read from table cache.", - }, - ) - - HandShakeErrorCounter = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "handshake_error_total", - Help: "Counter of hand shake error.", - }, - ) - - GetTokenDurationHistogram = NewHistogram( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "get_token_duration_seconds", - Help: "Duration (us) for getting token, it should be small until concurrency limit is reached.", - Buckets: prometheus.ExponentialBuckets(1, 2, 30), // 1us ~ 528s - }) - - NumOfMultiQueryHistogram = NewHistogram( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "multi_query_num", - Help: "The number of queries contained in a multi-query statement.", - Buckets: prometheus.ExponentialBuckets(1, 2, 20), // 1 ~ 1048576 - }) - - TotalQueryProcHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "slow_query_process_duration_seconds", - Help: "Bucketed histogram of processing time (s) of of slow queries.", - Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days - }, []string{LblSQLType}) - - TotalCopProcHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "slow_query_cop_duration_seconds", - Help: "Bucketed histogram of all cop processing time (s) of of slow queries.", - Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days - }, []string{LblSQLType}) - - TotalCopWaitHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "slow_query_wait_duration_seconds", - Help: "Bucketed histogram of all cop waiting time (s) of of slow queries.", - Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days - }, []string{LblSQLType}) - - CopMVCCRatioHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "slow_query_cop_mvcc_ratio", - Help: "Bucketed histogram of all cop total keys / processed keys in slow queries.", - Buckets: prometheus.ExponentialBuckets(0.5, 2, 21), // 0.5 ~ 262144 - }, []string{LblSQLType}) - - MaxProcs = NewGauge( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "maxprocs", - Help: "The value of GOMAXPROCS.", - }) - - GOGC = NewGauge( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "gogc", - Help: "The value of GOGC", - }) - - ConnIdleDurationHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "conn_idle_duration_seconds", - Help: "Bucketed histogram of connection idle time (s).", - Buckets: prometheus.ExponentialBuckets(0.0005, 2, 29), // 0.5ms ~ 1.5days - }, []string{LblInTxn}) - - ServerInfo = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "info", - Help: "Indicate the tidb server info, and the value is the start timestamp (s).", - }, []string{LblVersion, LblHash}) - - TokenGauge = NewGauge( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "tokens", - Help: "The number of concurrent executing session", - }, - ) - - ConfigStatus = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "config", - Name: "status", - Help: "Status of the TiDB server configurations.", - }, []string{LblType}) - - TiFlashQueryTotalCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "tiflash_query_total", - Help: "Counter of TiFlash queries.", - }, []string{LblType, LblResult}) - - TiFlashFailedMPPStoreState = NewGaugeVec( - prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "tiflash_failed_store", - Help: "Statues of failed tiflash mpp store,-1 means detector heartbeat,0 means reachable,1 means abnormal.", - }, []string{LblAddress}) - - PDAPIExecutionHistogram = NewHistogramVec( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "pd_api_execution_duration_seconds", - Help: "Bucketed histogram of all pd api execution time (s)", - Buckets: prometheus.ExponentialBuckets(0.001, 2, 20), // 1ms ~ 524s - }, []string{LblType}) - - PDAPIRequestCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "pd_api_request_total", - Help: "Counter of the pd http api requests", - }, []string{LblType, LblResult}) - - CPUProfileCounter = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "cpu_profile_total", - Help: "Counter of cpu profiling", - }) - - LoadTableCacheDurationHistogram = NewHistogram( - prometheus.HistogramOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "load_table_cache_seconds", - Help: "Duration (us) for loading table cache.", - Buckets: prometheus.ExponentialBuckets(1, 2, 30), // 1us ~ 528s - }) - - RCCheckTSWriteConfilictCounter = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "server", - Name: "rc_check_ts_conflict_total", - Help: "Counter of WriteConflict caused by RCCheckTS.", - }, []string{LblType}) -} - -// ExecuteErrorToLabel converts an execute error to label. -func ExecuteErrorToLabel(err error) string { - err = errors.Cause(err) - switch x := err.(type) { - case *terror.Error: - return string(x.RFCCode()) - default: - return "unknown" - } -} diff --git a/metrics/telemetry.go b/metrics/telemetry.go deleted file mode 100644 index f16ef75c8e5b1..0000000000000 --- a/metrics/telemetry.go +++ /dev/null @@ -1,586 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/util/mathutil" - "github.com/prometheus/client_golang/prometheus" - dto "github.com/prometheus/client_model/go" -) - -// Metrics -var ( - TelemetrySQLCTECnt *prometheus.CounterVec - TelemetryMultiSchemaChangeCnt prometheus.Counter - TelemetryTablePartitionCnt prometheus.Counter - TelemetryTablePartitionListCnt prometheus.Counter - TelemetryTablePartitionRangeCnt prometheus.Counter - TelemetryTablePartitionHashCnt prometheus.Counter - TelemetryTablePartitionRangeColumnsCnt prometheus.Counter - TelemetryTablePartitionRangeColumnsGt1Cnt prometheus.Counter - TelemetryTablePartitionRangeColumnsGt2Cnt prometheus.Counter - TelemetryTablePartitionRangeColumnsGt3Cnt prometheus.Counter - TelemetryTablePartitionListColumnsCnt prometheus.Counter - TelemetryTablePartitionMaxPartitionsCnt prometheus.Counter - TelemetryAccountLockCnt *prometheus.CounterVec - TelemetryTablePartitionCreateIntervalPartitionsCnt prometheus.Counter - TelemetryTablePartitionAddIntervalPartitionsCnt prometheus.Counter - TelemetryTablePartitionDropIntervalPartitionsCnt prometheus.Counter - TelemetryExchangePartitionCnt prometheus.Counter - TelemetryAddIndexIngestCnt prometheus.Counter - TelemetryFlashbackClusterCnt prometheus.Counter - TelemetryIndexMergeUsage prometheus.Counter - TelemetryCompactPartitionCnt prometheus.Counter - TelemetryReorganizePartitionCnt prometheus.Counter - TelemetryDistReorgCnt prometheus.Counter - TelemetryStoreBatchedQueryCnt prometheus.Counter - TelemetryBatchedQueryTaskCnt prometheus.Counter - TelemetryStoreBatchedCnt prometheus.Counter - TelemetryStoreBatchedFallbackCnt prometheus.Counter -) - -// InitTelemetryMetrics initializes telemetry metrics. -func InitTelemetryMetrics() { - TelemetrySQLCTECnt = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "non_recursive_cte_usage", - Help: "Counter of usage of CTE", - }, []string{LblCTEType}) - - TelemetryMultiSchemaChangeCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "multi_schema_change_usage", - Help: "Counter of usage of multi-schema change", - }) - - TelemetryTablePartitionCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_usage", - Help: "Counter of CREATE TABLE which includes of table partitioning", - }) - - TelemetryTablePartitionListCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_list_usage", - Help: "Counter of CREATE TABLE which includes LIST partitioning", - }) - - TelemetryTablePartitionRangeCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_range_usage", - Help: "Counter of CREATE TABLE which includes RANGE partitioning", - }) - - TelemetryTablePartitionHashCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_hash_usage", - Help: "Counter of CREATE TABLE which includes HASH partitioning", - }) - - TelemetryTablePartitionRangeColumnsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_range_columns_usage", - Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning", - }) - - TelemetryTablePartitionRangeColumnsGt1Cnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_range_multi_columns_usage", - Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than one partitioning column", - }) - - TelemetryTablePartitionRangeColumnsGt2Cnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_range_multi_columns_usage", - Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than two partitioning columns", - }) - - TelemetryTablePartitionRangeColumnsGt3Cnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_range_multi_columns_usage", - Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than three partitioning columns", - }) - - TelemetryTablePartitionListColumnsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_list_columns_usage", - Help: "Counter of CREATE TABLE which includes LIST COLUMNS partitioning", - }) - - TelemetryTablePartitionMaxPartitionsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_max_partition_usage", - Help: "Counter of partitions created by CREATE TABLE statements", - }) - - TelemetryAccountLockCnt = NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "account_lock_usage", - Help: "Counter of locked/unlocked users", - }, []string{LblAccountLock}) - - TelemetryTablePartitionCreateIntervalPartitionsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_create_interval_partition_usage", - Help: "Counter of partitions created by CREATE TABLE INTERVAL statements", - }) - - TelemetryTablePartitionAddIntervalPartitionsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_add_interval_partition_usage", - Help: "Counter of partitions added by ALTER TABLE LAST PARTITION statements", - }) - - TelemetryTablePartitionDropIntervalPartitionsCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "table_partition_drop_interval_partition_usage", - Help: "Counter of partitions added by ALTER TABLE FIRST PARTITION statements", - }) - - TelemetryExchangePartitionCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "exchange_partition_usage", - Help: "Counter of usage of exchange partition statements", - }) - - TelemetryAddIndexIngestCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "add_index_ingest_usage", - Help: "Counter of usage of add index acceleration solution", - }) - - TelemetryFlashbackClusterCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "flashback_cluster_usage", - Help: "Counter of usage of flashback cluster", - }) - - TelemetryIndexMergeUsage = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "index_merge_usage", - Help: "Counter of usage of index merge", - }) - - TelemetryCompactPartitionCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "compact_partition_usage", - Help: "Counter of compact table partition", - }) - - TelemetryReorganizePartitionCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "reorganize_partition_usage", - Help: "Counter of alter table reorganize partition", - }) - - TelemetryDistReorgCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "distributed_reorg_count", - Help: "Counter of usage of distributed reorg DDL tasks count", - }) - - TelemetryStoreBatchedQueryCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "store_batched_query", - Help: "Counter of queries which use store batched coprocessor tasks", - }) - - TelemetryBatchedQueryTaskCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "batched_query_task", - Help: "Counter of coprocessor tasks in batched queries", - }) - - TelemetryStoreBatchedCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "store_batched", - Help: "Counter of store batched coprocessor tasks", - }) - - TelemetryStoreBatchedFallbackCnt = NewCounter( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "telemetry", - Name: "store_batched_fallback", - Help: "Counter of store batched fallback coprocessor tasks", - }) -} - -// readCounter reads the value of a prometheus.Counter. -// Returns -1 when failing to read the value. -func readCounter(m prometheus.Counter) int64 { - // Actually, it's not recommended to read the value of prometheus metric types directly: - // https://github.com/prometheus/client_golang/issues/486#issuecomment-433345239 - pb := &dto.Metric{} - // It's impossible to return an error though. - if err := m.Write(pb); err != nil { - return -1 - } - return int64(pb.GetCounter().GetValue()) -} - -// CTEUsageCounter records the usages of CTE. -type CTEUsageCounter struct { - NonRecursiveCTEUsed int64 `json:"nonRecursiveCTEUsed"` - RecursiveUsed int64 `json:"recursiveUsed"` - NonCTEUsed int64 `json:"nonCTEUsed"` -} - -// Sub returns the difference of two counters. -func (c CTEUsageCounter) Sub(rhs CTEUsageCounter) CTEUsageCounter { - return CTEUsageCounter{ - NonRecursiveCTEUsed: c.NonRecursiveCTEUsed - rhs.NonRecursiveCTEUsed, - RecursiveUsed: c.RecursiveUsed - rhs.RecursiveUsed, - NonCTEUsed: c.NonCTEUsed - rhs.NonCTEUsed, - } -} - -// GetCTECounter gets the TxnCommitCounter. -func GetCTECounter() CTEUsageCounter { - return CTEUsageCounter{ - NonRecursiveCTEUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "nonRecurCTE"})), - RecursiveUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "recurCTE"})), - NonCTEUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "notCTE"})), - } -} - -// AccountLockCounter records the number of lock users/roles -type AccountLockCounter struct { - LockUser int64 `json:"lockUser"` - UnlockUser int64 `json:"unlockUser"` - CreateOrAlterUser int64 `json:"createOrAlterUser"` -} - -// Sub returns the difference of two counters. -func (c AccountLockCounter) Sub(rhs AccountLockCounter) AccountLockCounter { - return AccountLockCounter{ - LockUser: c.LockUser - rhs.LockUser, - UnlockUser: c.UnlockUser - rhs.UnlockUser, - CreateOrAlterUser: c.CreateOrAlterUser - rhs.CreateOrAlterUser, - } -} - -// GetAccountLockCounter gets the AccountLockCounter -func GetAccountLockCounter() AccountLockCounter { - return AccountLockCounter{ - LockUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "lockUser"})), - UnlockUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "unlockUser"})), - CreateOrAlterUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "createOrAlterUser"})), - } -} - -// MultiSchemaChangeUsageCounter records the usages of multi-schema change. -type MultiSchemaChangeUsageCounter struct { - MultiSchemaChangeUsed int64 `json:"multi_schema_change_used"` -} - -// Sub returns the difference of two counters. -func (c MultiSchemaChangeUsageCounter) Sub(rhs MultiSchemaChangeUsageCounter) MultiSchemaChangeUsageCounter { - return MultiSchemaChangeUsageCounter{ - MultiSchemaChangeUsed: c.MultiSchemaChangeUsed - rhs.MultiSchemaChangeUsed, - } -} - -// GetMultiSchemaCounter gets the TxnCommitCounter. -func GetMultiSchemaCounter() MultiSchemaChangeUsageCounter { - return MultiSchemaChangeUsageCounter{ - MultiSchemaChangeUsed: readCounter(TelemetryMultiSchemaChangeCnt), - } -} - -// TablePartitionUsageCounter records the usages of table partition. -type TablePartitionUsageCounter struct { - TablePartitionCnt int64 `json:"table_partition_cnt"` - TablePartitionListCnt int64 `json:"table_partition_list_cnt"` - TablePartitionRangeCnt int64 `json:"table_partition_range_cnt"` - TablePartitionHashCnt int64 `json:"table_partition_hash_cnt"` - TablePartitionRangeColumnsCnt int64 `json:"table_partition_range_columns_cnt"` - TablePartitionRangeColumnsGt1Cnt int64 `json:"table_partition_range_columns_gt_1_cnt"` - TablePartitionRangeColumnsGt2Cnt int64 `json:"table_partition_range_columns_gt_2_cnt"` - TablePartitionRangeColumnsGt3Cnt int64 `json:"table_partition_range_columns_gt_3_cnt"` - TablePartitionListColumnsCnt int64 `json:"table_partition_list_columns_cnt"` - TablePartitionMaxPartitionsCnt int64 `json:"table_partition_max_partitions_cnt"` - TablePartitionCreateIntervalPartitionsCnt int64 `json:"table_partition_create_interval_partitions_cnt"` - TablePartitionAddIntervalPartitionsCnt int64 `json:"table_partition_add_interval_partitions_cnt"` - TablePartitionDropIntervalPartitionsCnt int64 `json:"table_partition_drop_interval_partitions_cnt"` - TablePartitionComactCnt int64 `json:"table_TablePartitionComactCnt"` - TablePartitionReorganizePartitionCnt int64 `json:"table_reorganize_partition_cnt"` -} - -// ExchangePartitionUsageCounter records the usages of exchange partition. -type ExchangePartitionUsageCounter struct { - ExchangePartitionCnt int64 `json:"exchange_partition_cnt"` -} - -// Sub returns the difference of two counters. -func (c ExchangePartitionUsageCounter) Sub(rhs ExchangePartitionUsageCounter) ExchangePartitionUsageCounter { - return ExchangePartitionUsageCounter{ - ExchangePartitionCnt: c.ExchangePartitionCnt - rhs.ExchangePartitionCnt, - } -} - -// GetExchangePartitionCounter gets the TxnCommitCounter. -func GetExchangePartitionCounter() ExchangePartitionUsageCounter { - return ExchangePartitionUsageCounter{ - ExchangePartitionCnt: readCounter(TelemetryExchangePartitionCnt), - } -} - -// Cal returns the difference of two counters. -func (c TablePartitionUsageCounter) Cal(rhs TablePartitionUsageCounter) TablePartitionUsageCounter { - return TablePartitionUsageCounter{ - TablePartitionCnt: c.TablePartitionCnt - rhs.TablePartitionCnt, - TablePartitionListCnt: c.TablePartitionListCnt - rhs.TablePartitionListCnt, - TablePartitionRangeCnt: c.TablePartitionRangeCnt - rhs.TablePartitionRangeCnt, - TablePartitionHashCnt: c.TablePartitionHashCnt - rhs.TablePartitionHashCnt, - TablePartitionRangeColumnsCnt: c.TablePartitionRangeColumnsCnt - rhs.TablePartitionRangeColumnsCnt, - TablePartitionRangeColumnsGt1Cnt: c.TablePartitionRangeColumnsGt1Cnt - rhs.TablePartitionRangeColumnsGt1Cnt, - TablePartitionRangeColumnsGt2Cnt: c.TablePartitionRangeColumnsGt2Cnt - rhs.TablePartitionRangeColumnsGt2Cnt, - TablePartitionRangeColumnsGt3Cnt: c.TablePartitionRangeColumnsGt3Cnt - rhs.TablePartitionRangeColumnsGt3Cnt, - TablePartitionListColumnsCnt: c.TablePartitionListColumnsCnt - rhs.TablePartitionListColumnsCnt, - TablePartitionMaxPartitionsCnt: mathutil.Max(c.TablePartitionMaxPartitionsCnt-rhs.TablePartitionMaxPartitionsCnt, rhs.TablePartitionMaxPartitionsCnt), - TablePartitionCreateIntervalPartitionsCnt: c.TablePartitionCreateIntervalPartitionsCnt - rhs.TablePartitionCreateIntervalPartitionsCnt, - TablePartitionAddIntervalPartitionsCnt: c.TablePartitionAddIntervalPartitionsCnt - rhs.TablePartitionAddIntervalPartitionsCnt, - TablePartitionDropIntervalPartitionsCnt: c.TablePartitionDropIntervalPartitionsCnt - rhs.TablePartitionDropIntervalPartitionsCnt, - TablePartitionComactCnt: c.TablePartitionComactCnt - rhs.TablePartitionComactCnt, - TablePartitionReorganizePartitionCnt: c.TablePartitionReorganizePartitionCnt - rhs.TablePartitionReorganizePartitionCnt, - } -} - -// ResetTablePartitionCounter gets the TxnCommitCounter. -func ResetTablePartitionCounter(pre TablePartitionUsageCounter) TablePartitionUsageCounter { - return TablePartitionUsageCounter{ - TablePartitionCnt: readCounter(TelemetryTablePartitionCnt), - TablePartitionListCnt: readCounter(TelemetryTablePartitionListCnt), - TablePartitionRangeCnt: readCounter(TelemetryTablePartitionRangeCnt), - TablePartitionHashCnt: readCounter(TelemetryTablePartitionHashCnt), - TablePartitionRangeColumnsCnt: readCounter(TelemetryTablePartitionRangeColumnsCnt), - TablePartitionRangeColumnsGt1Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt1Cnt), - TablePartitionRangeColumnsGt2Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt2Cnt), - TablePartitionRangeColumnsGt3Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt3Cnt), - TablePartitionListColumnsCnt: readCounter(TelemetryTablePartitionListColumnsCnt), - TablePartitionMaxPartitionsCnt: mathutil.Max(readCounter(TelemetryTablePartitionMaxPartitionsCnt)-pre.TablePartitionMaxPartitionsCnt, pre.TablePartitionMaxPartitionsCnt), - TablePartitionReorganizePartitionCnt: readCounter(TelemetryReorganizePartitionCnt), - } -} - -// GetTablePartitionCounter gets the TxnCommitCounter. -func GetTablePartitionCounter() TablePartitionUsageCounter { - return TablePartitionUsageCounter{ - TablePartitionCnt: readCounter(TelemetryTablePartitionCnt), - TablePartitionListCnt: readCounter(TelemetryTablePartitionListCnt), - TablePartitionRangeCnt: readCounter(TelemetryTablePartitionRangeCnt), - TablePartitionHashCnt: readCounter(TelemetryTablePartitionHashCnt), - TablePartitionRangeColumnsCnt: readCounter(TelemetryTablePartitionRangeColumnsCnt), - TablePartitionRangeColumnsGt1Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt1Cnt), - TablePartitionRangeColumnsGt2Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt2Cnt), - TablePartitionRangeColumnsGt3Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt3Cnt), - TablePartitionListColumnsCnt: readCounter(TelemetryTablePartitionListColumnsCnt), - TablePartitionMaxPartitionsCnt: readCounter(TelemetryTablePartitionMaxPartitionsCnt), - TablePartitionCreateIntervalPartitionsCnt: readCounter(TelemetryTablePartitionCreateIntervalPartitionsCnt), - TablePartitionAddIntervalPartitionsCnt: readCounter(TelemetryTablePartitionAddIntervalPartitionsCnt), - TablePartitionDropIntervalPartitionsCnt: readCounter(TelemetryTablePartitionDropIntervalPartitionsCnt), - TablePartitionComactCnt: readCounter(TelemetryCompactPartitionCnt), - TablePartitionReorganizePartitionCnt: readCounter(TelemetryReorganizePartitionCnt), - } -} - -// NonTransactionalStmtCounter records the usages of non-transactional statements. -type NonTransactionalStmtCounter struct { - DeleteCount int64 `json:"delete"` - UpdateCount int64 `json:"update"` - InsertCount int64 `json:"insert"` -} - -// Sub returns the difference of two counters. -func (n NonTransactionalStmtCounter) Sub(rhs NonTransactionalStmtCounter) NonTransactionalStmtCounter { - return NonTransactionalStmtCounter{ - DeleteCount: n.DeleteCount - rhs.DeleteCount, - UpdateCount: n.UpdateCount - rhs.UpdateCount, - InsertCount: n.InsertCount - rhs.InsertCount, - } -} - -// GetNonTransactionalStmtCounter gets the NonTransactionalStmtCounter. -func GetNonTransactionalStmtCounter() NonTransactionalStmtCounter { - return NonTransactionalStmtCounter{ - DeleteCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "delete"})), - UpdateCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "update"})), - InsertCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "insert"})), - } -} - -// GetSavepointStmtCounter gets the savepoint statement executed counter. -func GetSavepointStmtCounter() int64 { - return readCounter(StmtNodeCounter.With(prometheus.Labels{LblType: "Savepoint", LblDb: ""})) -} - -// GetLazyPessimisticUniqueCheckSetCounter returns the counter of setting tidb_constraint_check_in_place_pessimistic to false. -func GetLazyPessimisticUniqueCheckSetCounter() int64 { - return readCounter(LazyPessimisticUniqueCheckSetCount) -} - -// DDLUsageCounter records the usages of DDL related features. -type DDLUsageCounter struct { - AddIndexIngestUsed int64 `json:"add_index_ingest_used"` - MetadataLockUsed bool `json:"metadata_lock_used"` - FlashbackClusterUsed int64 `json:"flashback_cluster_used"` - DistReorgUsed int64 `json:"dist_reorg_used"` -} - -// Sub returns the difference of two counters. -func (a DDLUsageCounter) Sub(rhs DDLUsageCounter) DDLUsageCounter { - return DDLUsageCounter{ - AddIndexIngestUsed: a.AddIndexIngestUsed - rhs.AddIndexIngestUsed, - FlashbackClusterUsed: a.FlashbackClusterUsed - rhs.FlashbackClusterUsed, - DistReorgUsed: a.DistReorgUsed - rhs.DistReorgUsed, - } -} - -// GetDDLUsageCounter gets the add index acceleration solution counts. -func GetDDLUsageCounter() DDLUsageCounter { - return DDLUsageCounter{ - AddIndexIngestUsed: readCounter(TelemetryAddIndexIngestCnt), - FlashbackClusterUsed: readCounter(TelemetryFlashbackClusterCnt), - DistReorgUsed: readCounter(TelemetryDistReorgCnt), - } -} - -// IndexMergeUsageCounter records the usages of IndexMerge feature. -type IndexMergeUsageCounter struct { - IndexMergeUsed int64 `json:"index_merge_used"` -} - -// Sub returns the difference of two counters. -func (i IndexMergeUsageCounter) Sub(rhs IndexMergeUsageCounter) IndexMergeUsageCounter { - return IndexMergeUsageCounter{ - IndexMergeUsed: i.IndexMergeUsed - rhs.IndexMergeUsed, - } -} - -// GetIndexMergeCounter gets the IndexMerge usage counter. -func GetIndexMergeCounter() IndexMergeUsageCounter { - return IndexMergeUsageCounter{ - IndexMergeUsed: readCounter(TelemetryIndexMergeUsage), - } -} - -// StoreBatchCoprCounter records the usages of batch copr statements. -type StoreBatchCoprCounter struct { - // BatchSize is the global value of `tidb_store_batch_size` - BatchSize int `json:"batch_size"` - // BatchedQuery is the counter of queries that use this feature. - BatchedQuery int64 `json:"query"` - // BatchedQueryTask is the counter of total tasks in queries above. - BatchedQueryTask int64 `json:"tasks"` - // BatchedCount is the counter of successfully batched tasks. - BatchedCount int64 `json:"batched"` - // BatchedFallbackCount is the counter of fallback batched tasks by region miss. - BatchedFallbackCount int64 `json:"batched_fallback"` -} - -// Sub returns the difference of two counters. -func (n StoreBatchCoprCounter) Sub(rhs StoreBatchCoprCounter) StoreBatchCoprCounter { - return StoreBatchCoprCounter{ - BatchedQuery: n.BatchedQuery - rhs.BatchedQuery, - BatchedQueryTask: n.BatchedQueryTask - rhs.BatchedQueryTask, - BatchedCount: n.BatchedCount - rhs.BatchedCount, - BatchedFallbackCount: n.BatchedFallbackCount - rhs.BatchedFallbackCount, - } -} - -// GetStoreBatchCoprCounter gets the IndexMerge usage counter. -func GetStoreBatchCoprCounter() StoreBatchCoprCounter { - return StoreBatchCoprCounter{ - BatchedQuery: readCounter(TelemetryStoreBatchedQueryCnt), - BatchedQueryTask: readCounter(TelemetryBatchedQueryTaskCnt), - BatchedCount: readCounter(TelemetryStoreBatchedCnt), - BatchedFallbackCount: readCounter(TelemetryStoreBatchedFallbackCnt), - } -} - -// FairLockingUsageCounter records the usage of Fair Locking feature of pessimistic transaction. -type FairLockingUsageCounter struct { - TxnFairLockingUsed int64 `json:"txn_fair_locking_used"` - TxnFairLockingEffective int64 `json:"txn_fair_locking_effective"` -} - -// Sub returns the difference of two counters. -func (i FairLockingUsageCounter) Sub(rhs FairLockingUsageCounter) FairLockingUsageCounter { - return FairLockingUsageCounter{ - TxnFairLockingUsed: i.TxnFairLockingUsed - rhs.TxnFairLockingUsed, - TxnFairLockingEffective: i.TxnFairLockingEffective - rhs.TxnFairLockingEffective, - } -} - -// GetFairLockingUsageCounter returns the Fair Locking usage counter. -func GetFairLockingUsageCounter() FairLockingUsageCounter { - return FairLockingUsageCounter{ - TxnFairLockingUsed: readCounter(FairLockingUsageCount.WithLabelValues(LblFairLockingTxnUsed)), - TxnFairLockingEffective: readCounter(FairLockingUsageCount.WithLabelValues(LblFairLockingTxnEffective)), - } -} diff --git a/owner/BUILD.bazel b/owner/BUILD.bazel deleted file mode 100644 index 46d58e4562e13..0000000000000 --- a/owner/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "owner", - srcs = [ - "manager.go", - "mock.go", - ], - importpath = "github.com/pingcap/tidb/owner", - visibility = ["//visibility:public"], - deps = [ - "//ddl/util", - "//kv", - "//metrics", - "//parser/terror", - "//util", - "//util/logutil", - "//util/timeutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@io_etcd_go_etcd_api_v3//mvccpb", - "@io_etcd_go_etcd_api_v3//v3rpc/rpctypes", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "owner_test", - timeout = "short", - srcs = [ - "fail_test.go", - "main_test.go", - "manager_test.go", - ], - embed = [":owner"], - flaky = True, - shard_count = 5, - deps = [ - "//ddl", - "//infoschema", - "//kv", - "//parser/terror", - "//store/mockstore", - "//testkit", - "//testkit/testsetup", - "//util", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/owner/fail_test.go b/owner/fail_test.go deleted file mode 100644 index 5a6badfbbcb7b..0000000000000 --- a/owner/fail_test.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package owner - -import ( - "context" - "fmt" - "math" - "net" - "os" - "runtime" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - clientv3 "go.etcd.io/etcd/client/v3" - "google.golang.org/grpc" -) - -var ( - dialTimeout = 3 * time.Second - retryCnt = math.MaxInt32 -) - -func TestFailNewSession(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - - _ = os.Remove("new_session:0") - ln, err := net.Listen("unix", "new_session:0") - require.NoError(t, err) - - addr := ln.Addr() - endpoints := []string{fmt.Sprintf("%s://%s", addr.Network(), addr.String())} - require.NoError(t, err) - - srv := grpc.NewServer(grpc.ConnectionTimeout(time.Minute)) - - var stop util.WaitGroupWrapper - stop.Run(func() { - err = srv.Serve(ln) - assert.NoError(t, err) - }) - - defer func() { - srv.Stop() - stop.Wait() - }() - - func() { - cli, err := clientv3.New(clientv3.Config{ - Endpoints: endpoints, - DialTimeout: dialTimeout, - }) - require.NoError(t, err) - defer func() { - if cli != nil { - _ = cli.Close() - } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/closeClient")) - }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/closeClient", `return(true)`)) - - // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. - // The config is hard coded, not way to control it outside. - // Call stack: - // https://github.com/etcd-io/etcd/blob/ae9734e/clientv3/concurrency/session.go#L38 - // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/client.go#L253 - // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/retry_interceptor.go#L63 - _, err = util.NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) - isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) - require.Truef(t, isContextDone, "err %v", err) - }() - - func() { - cli, err := clientv3.New(clientv3.Config{ - Endpoints: endpoints, - DialTimeout: dialTimeout, - }) - require.NoError(t, err) - defer func() { - if cli != nil { - _ = cli.Close() - } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/closeGrpc")) - }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/closeGrpc", `return(true)`)) - - // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. - // The config is hard coded, not way to control it outside. - _, err = util.NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) - isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) - require.Truef(t, isContextDone, "err %v", err) - }() -} diff --git a/owner/main_test.go b/owner/main_test.go deleted file mode 100644 index fe648e2ff817b..0000000000000 --- a/owner/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -//Copyright 2021 PingCAP, Inc. -// -// 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. - -package owner - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/owner/manager.go b/owner/manager.go deleted file mode 100644 index f983b601f4451..0000000000000 --- a/owner/manager.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package owner - -import ( - "bytes" - "context" - "fmt" - "os" - "strconv" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - util2 "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "go.etcd.io/etcd/api/v3/mvccpb" - "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" - clientv3 "go.etcd.io/etcd/client/v3" - "go.etcd.io/etcd/client/v3/concurrency" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -// Manager is used to campaign the owner and manage the owner information. -type Manager interface { - // ID returns the ID of the manager. - ID() string - // IsOwner returns whether the ownerManager is the owner. - IsOwner() bool - // RetireOwner make the manager to be a not owner. It's exported for testing. - RetireOwner() - // GetOwnerID gets the owner ID. - GetOwnerID(ctx context.Context) (string, error) - // SetOwnerOpValue updates the owner op value. - SetOwnerOpValue(ctx context.Context, op OpType) error - // CampaignOwner campaigns the owner. - CampaignOwner(...int) error - // ResignOwner lets the owner start a new election. - ResignOwner(ctx context.Context) error - // Cancel cancels this etcd ownerManager. - Cancel() - // RequireOwner requires the ownerManager is owner. - RequireOwner(ctx context.Context) error - // CampaignCancel cancels one etcd campaign - CampaignCancel() - - // SetBeOwnerHook sets a hook. The hook is called before becoming an owner. - SetBeOwnerHook(hook func()) -} - -const ( - keyOpDefaultTimeout = 5 * time.Second -) - -// OpType is the owner key value operation type. -type OpType byte - -// List operation of types. -const ( - OpNone OpType = 0 - OpGetUpgradingState OpType = 1 -) - -// String implements fmt.Stringer interface. -func (ot OpType) String() string { - switch ot { - case OpGetUpgradingState: - return "get upgrading state" - default: - return "none" - } -} - -// DDLOwnerChecker is used to check whether tidb is owner. -type DDLOwnerChecker interface { - // IsOwner returns whether the ownerManager is the owner. - IsOwner() bool -} - -// ownerManager represents the structure which is used for electing owner. -type ownerManager struct { - id string // id is the ID of the manager. - key string - ctx context.Context - prompt string - logPrefix string - logCtx context.Context - etcdCli *clientv3.Client - cancel context.CancelFunc - elec unsafe.Pointer - sessionLease *atomicutil.Int64 - wg sync.WaitGroup - beOwnerHook func() - campaignCancel context.CancelFunc -} - -// NewOwnerManager creates a new Manager. -func NewOwnerManager(ctx context.Context, etcdCli *clientv3.Client, prompt, id, key string) Manager { - logPrefix := fmt.Sprintf("[%s] %s ownerManager %s", prompt, key, id) - ctx, cancelFunc := context.WithCancel(ctx) - return &ownerManager{ - etcdCli: etcdCli, - id: id, - key: key, - ctx: ctx, - prompt: prompt, - cancel: cancelFunc, - logPrefix: logPrefix, - logCtx: logutil.WithKeyValue(context.Background(), "owner info", logPrefix), - sessionLease: atomicutil.NewInt64(0), - } -} - -// ID implements Manager.ID interface. -func (m *ownerManager) ID() string { - return m.id -} - -// IsOwner implements Manager.IsOwner interface. -func (m *ownerManager) IsOwner() bool { - return atomic.LoadPointer(&m.elec) != unsafe.Pointer(nil) -} - -// Cancel implements Manager.Cancel interface. -func (m *ownerManager) Cancel() { - m.cancel() - m.wg.Wait() -} - -// RequireOwner implements Manager.RequireOwner interface. -func (*ownerManager) RequireOwner(_ context.Context) error { - return nil -} - -func (m *ownerManager) SetBeOwnerHook(hook func()) { - m.beOwnerHook = hook -} - -// ManagerSessionTTL is the etcd session's TTL in seconds. It's exported for testing. -var ManagerSessionTTL = 60 - -// setManagerSessionTTL sets the ManagerSessionTTL value, it's used for testing. -func setManagerSessionTTL() error { - ttlStr := os.Getenv("tidb_manager_ttl") - if ttlStr == "" { - return nil - } - ttl, err := strconv.Atoi(ttlStr) - if err != nil { - return errors.Trace(err) - } - ManagerSessionTTL = ttl - return nil -} - -// CampaignOwner implements Manager.CampaignOwner interface. -func (m *ownerManager) CampaignOwner(withTTL ...int) error { - ttl := ManagerSessionTTL - if len(withTTL) == 1 { - ttl = withTTL[0] - } - logPrefix := fmt.Sprintf("[%s] %s", m.prompt, m.key) - logutil.BgLogger().Info("start campaign owner", zap.String("ownerInfo", logPrefix)) - session, err := util2.NewSession(m.ctx, logPrefix, m.etcdCli, util2.NewSessionDefaultRetryCnt, ttl) - if err != nil { - return errors.Trace(err) - } - m.sessionLease.Store(int64(session.Lease())) - m.wg.Add(1) - go m.campaignLoop(session) - return nil -} - -// ResignOwner lets the owner start a new election. -func (m *ownerManager) ResignOwner(ctx context.Context) error { - elec := (*concurrency.Election)(atomic.LoadPointer(&m.elec)) - if elec == nil { - return errors.Errorf("This node is not a ddl owner, can't be resigned") - } - - childCtx, cancel := context.WithTimeout(ctx, keyOpDefaultTimeout) - err := elec.Resign(childCtx) - cancel() - if err != nil { - return errors.Trace(err) - } - - logutil.Logger(m.logCtx).Warn("resign ddl owner success") - return nil -} - -func (m *ownerManager) toBeOwner(elec *concurrency.Election) { - if m.beOwnerHook != nil { - m.beOwnerHook() - } - atomic.StorePointer(&m.elec, unsafe.Pointer(elec)) -} - -// RetireOwner make the manager to be a not owner. -func (m *ownerManager) RetireOwner() { - atomic.StorePointer(&m.elec, nil) -} - -// CampaignCancel implements Manager.CampaignCancel interface. -func (m *ownerManager) CampaignCancel() { - m.campaignCancel() - m.wg.Wait() -} - -func (m *ownerManager) campaignLoop(etcdSession *concurrency.Session) { - var campaignContext context.Context - campaignContext, m.campaignCancel = context.WithCancel(m.ctx) - defer func() { - m.campaignCancel() - if r := recover(); r != nil { - logutil.BgLogger().Error("recover panic", zap.String("prompt", m.prompt), zap.Any("error", r), zap.Stack("buffer")) - metrics.PanicCounter.WithLabelValues(metrics.LabelDDLOwner).Inc() - } - m.wg.Done() - }() - - logPrefix := m.logPrefix - logCtx := m.logCtx - var err error - for { - if err != nil { - metrics.CampaignOwnerCounter.WithLabelValues(m.prompt, err.Error()).Inc() - } - - select { - case <-etcdSession.Done(): - logutil.Logger(logCtx).Info("etcd session is done, creates a new one") - leaseID := etcdSession.Lease() - etcdSession, err = util2.NewSession(campaignContext, logPrefix, m.etcdCli, util2.NewSessionRetryUnlimited, ManagerSessionTTL) - if err != nil { - logutil.Logger(logCtx).Info("break campaign loop, NewSession failed", zap.Error(err)) - m.revokeSession(logPrefix, leaseID) - return - } - m.sessionLease.Store(int64(etcdSession.Lease())) - case <-campaignContext.Done(): - failpoint.Inject("MockDelOwnerKey", func(v failpoint.Value) { - if v.(string) == "delOwnerKeyAndNotOwner" { - logutil.Logger(logCtx).Info("mock break campaign and don't clear related info") - return - } - }) - logutil.Logger(logCtx).Info("break campaign loop, context is done") - m.revokeSession(logPrefix, etcdSession.Lease()) - return - default: - } - // If the etcd server turns clocks forward,the following case may occur. - // The etcd server deletes this session's lease ID, but etcd session doesn't find it. - // In this time if we do the campaign operation, the etcd server will return ErrLeaseNotFound. - if terror.ErrorEqual(err, rpctypes.ErrLeaseNotFound) { - if etcdSession != nil { - err = etcdSession.Close() - logutil.Logger(logCtx).Info("etcd session encounters the error of lease not found, closes it", zap.Error(err)) - } - continue - } - - elec := concurrency.NewElection(etcdSession, m.key) - err = elec.Campaign(campaignContext, m.id) - if err != nil { - logutil.Logger(logCtx).Info("failed to campaign", zap.Error(err)) - continue - } - - ownerKey, err := GetOwnerKey(campaignContext, logCtx, m.etcdCli, m.key, m.id) - if err != nil { - continue - } - - m.toBeOwner(elec) - m.watchOwner(campaignContext, etcdSession, ownerKey) - m.RetireOwner() - - metrics.CampaignOwnerCounter.WithLabelValues(m.prompt, metrics.NoLongerOwner).Inc() - logutil.Logger(logCtx).Warn("is not the owner") - } -} - -func (m *ownerManager) revokeSession(_ string, leaseID clientv3.LeaseID) { - // Revoke the session lease. - // If revoke takes longer than the ttl, lease is expired anyway. - cancelCtx, cancel := context.WithTimeout(context.Background(), - time.Duration(ManagerSessionTTL)*time.Second) - _, err := m.etcdCli.Revoke(cancelCtx, leaseID) - cancel() - logutil.Logger(m.logCtx).Info("revoke session", zap.Error(err)) -} - -// GetOwnerID implements Manager.GetOwnerID interface. -func (m *ownerManager) GetOwnerID(ctx context.Context) (string, error) { - _, ownerID, _, _, err := getOwnerInfo(ctx, m.logCtx, m.etcdCli, m.key) - return string(ownerID), errors.Trace(err) -} - -func getOwnerInfo(ctx, logCtx context.Context, etcdCli *clientv3.Client, ownerPath string) (string, []byte, OpType, int64, error) { - var op OpType - var resp *clientv3.GetResponse - var err error - for i := 0; i < 3; i++ { - if util.IsContextDone(ctx) { - return "", nil, op, 0, errors.Trace(ctx.Err()) - } - - childCtx, cancel := context.WithTimeout(ctx, util.KeyOpDefaultTimeout) - resp, err = etcdCli.Get(childCtx, ownerPath, clientv3.WithFirstCreate()...) - cancel() - if err == nil { - break - } - logutil.BgLogger().Info("etcd-cli get owner info failed", zap.String("category", "ddl"), zap.String("key", ownerPath), zap.Int("retryCnt", i), zap.Error(err)) - time.Sleep(util.KeyOpRetryInterval) - } - if err != nil { - logutil.Logger(logCtx).Warn("etcd-cli get owner info failed", zap.Error(err)) - return "", nil, op, 0, errors.Trace(err) - } - if len(resp.Kvs) == 0 { - return "", nil, op, 0, concurrency.ErrElectionNoLeader - } - - var ownerID []byte - ownerID, op = splitOwnerValues(resp.Kvs[0].Value) - logutil.Logger(logCtx).Info("get owner", zap.ByteString("owner key", resp.Kvs[0].Key), - zap.ByteString("ownerID", ownerID), zap.Stringer("op", op)) - return string(resp.Kvs[0].Key), ownerID, op, resp.Kvs[0].ModRevision, nil -} - -// GetOwnerKey gets the owner key information. -func GetOwnerKey(ctx, logCtx context.Context, etcdCli *clientv3.Client, etcdKey, id string) (string, error) { - ownerKey, ownerID, _, _, err := getOwnerInfo(ctx, logCtx, etcdCli, etcdKey) - if err != nil { - return "", errors.Trace(err) - } - if string(ownerID) != id { - logutil.Logger(logCtx).Warn("is not the owner") - return "", errors.New("ownerInfoNotMatch") - } - - return ownerKey, nil -} - -func splitOwnerValues(val []byte) ([]byte, OpType) { - vals := bytes.Split(val, []byte("_")) - var op OpType - if len(vals) == 2 { - op = OpType(vals[1][0]) - } - return vals[0], op -} - -func joinOwnerValues(vals ...[]byte) []byte { - return bytes.Join(vals, []byte("_")) -} - -// SetOwnerOpValue implements Manager.SetOwnerOpValue interface. -func (m *ownerManager) SetOwnerOpValue(ctx context.Context, op OpType) error { - // owner don't change. - ownerKey, ownerID, currOp, modRevision, err := getOwnerInfo(ctx, m.logCtx, m.etcdCli, m.key) - if err != nil { - return errors.Trace(err) - } - if currOp == op { - logutil.Logger(m.logCtx).Info("set owner op is the same as the original, so do nothing.", zap.Stringer("op", op)) - return nil - } - if string(ownerID) != m.id { - return errors.New("ownerInfoNotMatch") - } - newOwnerVal := joinOwnerValues(ownerID, []byte{byte(op)}) - - failpoint.Inject("MockDelOwnerKey", func(v failpoint.Value) { - if valStr, ok := v.(string); ok { - if err := mockDelOwnerKey(valStr, ownerKey, m); err != nil { - failpoint.Return(err) - } - } - }) - - leaseOp := clientv3.WithLease(clientv3.LeaseID(m.sessionLease.Load())) - resp, err := m.etcdCli.Txn(ctx). - If(clientv3.Compare(clientv3.ModRevision(ownerKey), "=", modRevision)). - Then(clientv3.OpPut(ownerKey, string(newOwnerVal), leaseOp)). - Commit() - logutil.BgLogger().Info("set owner op value", zap.String("owner key", ownerKey), zap.ByteString("ownerID", ownerID), - zap.Stringer("old Op", currOp), zap.Stringer("op", op), zap.Bool("isSuc", resp.Succeeded), zap.Error(err)) - if !resp.Succeeded { - err = errors.New("put owner key failed, cmp is false") - } - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.PutValue+"_"+metrics.RetLabel(err)).Inc() - return errors.Trace(err) -} - -// GetOwnerOpValue gets the owner op value. -func GetOwnerOpValue(ctx context.Context, etcdCli *clientv3.Client, ownerPath, logPrefix string) (OpType, error) { - // It's using for testing. - if etcdCli == nil { - return *mockOwnerOpValue.Load(), nil - } - - logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) - _, _, op, _, err := getOwnerInfo(ctx, logCtx, etcdCli, ownerPath) - return op, errors.Trace(err) -} - -func (m *ownerManager) watchOwner(ctx context.Context, etcdSession *concurrency.Session, key string) { - logPrefix := fmt.Sprintf("[%s] ownerManager %s watch owner key %v", m.prompt, m.id, key) - logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) - logutil.BgLogger().Debug(logPrefix) - watchCh := m.etcdCli.Watch(ctx, key) - for { - select { - case resp, ok := <-watchCh: - if !ok { - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.WatcherClosed).Inc() - logutil.Logger(logCtx).Info("watcher is closed, no owner") - return - } - if resp.Canceled { - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.Cancelled).Inc() - logutil.Logger(logCtx).Info("watch canceled, no owner") - return - } - - for _, ev := range resp.Events { - if ev.Type == mvccpb.DELETE { - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.Deleted).Inc() - logutil.Logger(logCtx).Info("watch failed, owner is deleted") - return - } - } - case <-etcdSession.Done(): - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.SessionDone).Inc() - return - case <-ctx.Done(): - metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.CtxDone).Inc() - return - } - } -} - -func init() { - err := setManagerSessionTTL() - if err != nil { - logutil.BgLogger().Warn("set manager session TTL failed", zap.Error(err)) - } -} diff --git a/owner/manager_test.go b/owner/manager_test.go deleted file mode 100644 index bcf6a35ff5506..0000000000000 --- a/owner/manager_test.go +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package owner_test - -import ( - "context" - "fmt" - "runtime" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - . "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - clientv3 "go.etcd.io/etcd/client/v3" - "go.etcd.io/etcd/client/v3/concurrency" - "go.etcd.io/etcd/tests/v3/integration" -) - -const testLease = 5 * time.Millisecond - -type testInfo struct { - store kv.Storage - cluster *integration.ClusterV3 - client *clientv3.Client - ddl DDL -} - -func newTestInfo(t *testing.T) *testInfo { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 4}) - - cli := cluster.Client(0) - ic := infoschema.NewCache(2) - ic.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) - d := NewDDL( - context.Background(), - WithEtcdClient(cli), - WithStore(store), - WithLease(testLease), - WithInfoCache(ic), - ) - - return &testInfo{ - store: store, - cluster: cluster, - client: cli, - ddl: d, - } -} - -func (ti *testInfo) Close(t *testing.T) { - err := ti.ddl.Stop() - require.NoError(t, err) - err = ti.store.Close() - require.NoError(t, err) - ti.cluster.Terminate(t) -} - -func TestSingle(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - integration.BeforeTestExternal(t) - - tInfo := newTestInfo(t) - client, d := tInfo.client, tInfo.ddl - defer tInfo.Close(t) - require.NoError(t, d.OwnerManager().CampaignOwner()) - isOwner := checkOwner(d, true) - require.True(t, isOwner) - - // test for newSession failed - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - manager := owner.NewOwnerManager(ctx, client, "ddl", "ddl_id", DDLOwnerKey) - cancel() - - err := manager.CampaignOwner() - comment := fmt.Sprintf("campaigned result don't match, err %v", err) - require.True(t, terror.ErrorEqual(err, context.Canceled) || terror.ErrorEqual(err, context.DeadlineExceeded), comment) - - isOwner = checkOwner(d, true) - require.True(t, isOwner) - - // The test is used to exit campaign loop. - d.OwnerManager().Cancel() - isOwner = checkOwner(d, false) - require.False(t, isOwner) - - time.Sleep(200 * time.Millisecond) - - // err is ok to be not nil since we canceled the manager. - ownerID, _ := manager.GetOwnerID(ctx) - require.Equal(t, "", ownerID) - op, _ := owner.GetOwnerOpValue(ctx, client, DDLOwnerKey, "log prefix") - require.Equal(t, op, owner.OpNone) -} - -func TestSetAndGetOwnerOpValue(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - integration.BeforeTestExternal(t) - - tInfo := newTestInfo(t) - defer tInfo.Close(t) - - require.NoError(t, tInfo.ddl.OwnerManager().CampaignOwner()) - isOwner := checkOwner(tInfo.ddl, true) - require.True(t, isOwner) - - // test set/get owner info - manager := tInfo.ddl.OwnerManager() - ownerID, err := manager.GetOwnerID(context.Background()) - require.NoError(t, err) - require.Equal(t, tInfo.ddl.GetID(), ownerID) - op, err := owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpNone) - err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) - require.NoError(t, err) - op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpGetUpgradingState) - // update the same as the original value - err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) - require.NoError(t, err) - op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpGetUpgradingState) - // test del owner key when SetOwnerOpValue - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/owner/MockDelOwnerKey", `return("delOwnerKeyAndNotOwner")`)) - err = manager.SetOwnerOpValue(context.Background(), owner.OpNone) - require.Error(t, err, "put owner key failed, cmp is false") - op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") - require.NotNil(t, err) - require.Equal(t, concurrency.ErrElectionNoLeader.Error(), err.Error()) - require.Equal(t, op, owner.OpNone) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/owner/MockDelOwnerKey")) - - // Let ddl run for the owner again. - require.NoError(t, tInfo.ddl.OwnerManager().CampaignOwner()) - isOwner = checkOwner(tInfo.ddl, true) - require.True(t, isOwner) - // Mock the manager become not owner because the owner is deleted(like TTL is timeout). - // And then the manager campaigns the owner again, and become the owner. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/owner/MockDelOwnerKey", `return("onlyDelOwnerKey")`)) - err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) - require.Error(t, err, "put owner key failed, cmp is false") - isOwner = checkOwner(tInfo.ddl, true) - require.True(t, isOwner) - op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpNone) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/owner/MockDelOwnerKey")) -} - -// TestGetOwnerOpValueBeforeSet tests get owner opValue before set this value when the etcdClient is nil. -func TestGetOwnerOpValueBeforeSet(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/owner/MockNotSetOwnerOp", `return(true)`)) - - _, dom := testkit.CreateMockStoreAndDomain(t) - ddl := dom.DDL() - require.NoError(t, ddl.OwnerManager().CampaignOwner()) - isOwner := checkOwner(ddl, true) - require.True(t, isOwner) - - // test set/get owner info - manager := ddl.OwnerManager() - ownerID, err := manager.GetOwnerID(context.Background()) - require.NoError(t, err) - require.Equal(t, ddl.GetID(), ownerID) - op, err := owner.GetOwnerOpValue(context.Background(), nil, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpNone) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/owner/MockNotSetOwnerOp")) - err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) - require.NoError(t, err) - op, err = owner.GetOwnerOpValue(context.Background(), nil, DDLOwnerKey, "log prefix") - require.NoError(t, err) - require.Equal(t, op, owner.OpGetUpgradingState) -} - -func TestCluster(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - integration.BeforeTestExternal(t) - - originalTTL := owner.ManagerSessionTTL - owner.ManagerSessionTTL = 3 - defer func() { - owner.ManagerSessionTTL = originalTTL - }() - - tInfo := newTestInfo(t) - store, cluster, d := tInfo.store, tInfo.cluster, tInfo.ddl - defer tInfo.Close(t) - require.NoError(t, d.OwnerManager().CampaignOwner()) - - isOwner := checkOwner(d, true) - require.True(t, isOwner) - - cli1 := cluster.Client(1) - ic2 := infoschema.NewCache(2) - ic2.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) - d1 := NewDDL( - context.Background(), - WithEtcdClient(cli1), - WithStore(store), - WithLease(testLease), - WithInfoCache(ic2), - ) - require.NoError(t, d1.OwnerManager().CampaignOwner()) - - isOwner = checkOwner(d1, false) - require.False(t, isOwner) - - // Delete the leader key, the d1 become the owner. - cliRW := cluster.Client(2) - err := deleteLeader(cliRW, DDLOwnerKey) - require.NoError(t, err) - - isOwner = checkOwner(d, false) - require.False(t, isOwner) - - d.OwnerManager().Cancel() - // d3 (not owner) stop - cli3 := cluster.Client(3) - ic3 := infoschema.NewCache(2) - ic3.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) - d3 := NewDDL( - context.Background(), - WithEtcdClient(cli3), - WithStore(store), - WithLease(testLease), - WithInfoCache(ic3), - ) - require.NoError(t, d3.OwnerManager().CampaignOwner()) - - isOwner = checkOwner(d3, false) - require.False(t, isOwner) - - d3.OwnerManager().Cancel() - // Cancel the owner context, there is no owner. - d1.OwnerManager().Cancel() - - logPrefix := fmt.Sprintf("[ddl] %s ownerManager %s", DDLOwnerKey, "useless id") - logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) - _, err = owner.GetOwnerKey(context.Background(), logCtx, cliRW, DDLOwnerKey, "useless id") - require.Truef(t, terror.ErrorEqual(err, concurrency.ErrElectionNoLeader), "get owner info result don't match, err %v", err) - op, err := owner.GetOwnerOpValue(context.Background(), cliRW, DDLOwnerKey, logPrefix) - require.Truef(t, terror.ErrorEqual(err, concurrency.ErrElectionNoLeader), "get owner info result don't match, err %v", err) - require.Equal(t, op, owner.OpNone) -} - -func checkOwner(d DDL, fbVal bool) (isOwner bool) { - manager := d.OwnerManager() - // The longest to wait for 30 seconds to - // make sure that campaigning owners is completed. - for i := 0; i < 6000; i++ { - time.Sleep(5 * time.Millisecond) - isOwner = manager.IsOwner() - if isOwner == fbVal { - break - } - } - return -} - -func deleteLeader(cli *clientv3.Client, prefixKey string) error { - session, err := concurrency.NewSession(cli) - if err != nil { - return errors.Trace(err) - } - defer func() { - _ = session.Close() - }() - election := concurrency.NewElection(session, prefixKey) - resp, err := election.Leader(context.Background()) - if err != nil { - return errors.Trace(err) - } - _, err = cli.Delete(context.Background(), string(resp.Kvs[0].Key)) - return errors.Trace(err) -} diff --git a/owner/mock.go b/owner/mock.go deleted file mode 100644 index b94f525bb97dd..0000000000000 --- a/owner/mock.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package owner - -import ( - "context" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" - "go.uber.org/zap" -) - -var _ Manager = &mockManager{} - -// mockManager represents the structure which is used for electing owner. -// It's used for local store and testing. -// So this worker will always be the owner. -type mockManager struct { - id string // id is the ID of manager. - storeID string - key string - ctx context.Context - wg sync.WaitGroup - cancel context.CancelFunc - beOwnerHook func() - campaignDone chan struct{} - resignDone chan struct{} -} - -var mockOwnerOpValue atomic.Pointer[OpType] - -// NewMockManager creates a new mock Manager. -func NewMockManager(ctx context.Context, id string, store kv.Storage, ownerKey string) Manager { - cancelCtx, cancelFunc := context.WithCancel(ctx) - storeID := "mock_store_id" - if store != nil { - storeID = store.UUID() - } - - // Make sure the mockOwnerOpValue is initialized before GetOwnerOpValue in bootstrap. - op := OpNone - mockOwnerOpValue.Store(&op) - return &mockManager{ - id: id, - storeID: storeID, - key: ownerKey, - ctx: cancelCtx, - cancel: cancelFunc, - campaignDone: make(chan struct{}), - resignDone: make(chan struct{}), - } -} - -// ID implements Manager.ID interface. -func (m *mockManager) ID() string { - return m.id -} - -// IsOwner implements Manager.IsOwner interface. -func (m *mockManager) IsOwner() bool { - logutil.BgLogger().Debug("owner manager checks owner", zap.String("category", "ddl"), - zap.String("ID", m.id), zap.String("ownerKey", m.key)) - return util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).IsOwner(m.id) -} - -func (m *mockManager) toBeOwner() { - ok := util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).SetOwner(m.id) - if ok { - logutil.BgLogger().Debug("owner manager gets owner", zap.String("category", "ddl"), - zap.String("ID", m.id), zap.String("ownerKey", m.key)) - if m.beOwnerHook != nil { - m.beOwnerHook() - } - } -} - -// RetireOwner implements Manager.RetireOwner interface. -func (m *mockManager) RetireOwner() { - util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).UnsetOwner(m.id) -} - -// Cancel implements Manager.Cancel interface. -func (m *mockManager) Cancel() { - m.cancel() - m.wg.Wait() - logutil.BgLogger().Info("owner manager is canceled", zap.String("category", "ddl"), - zap.String("ID", m.id), zap.String("ownerKey", m.key)) -} - -// GetOwnerID implements Manager.GetOwnerID interface. -func (m *mockManager) GetOwnerID(_ context.Context) (string, error) { - if m.IsOwner() { - return m.ID(), nil - } - return "", errors.New("no owner") -} - -func (*mockManager) SetOwnerOpValue(_ context.Context, op OpType) error { - failpoint.Inject("MockNotSetOwnerOp", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(nil) - } - }) - mockOwnerOpValue.Store(&op) - return nil -} - -// CampaignOwner implements Manager.CampaignOwner interface. -func (m *mockManager) CampaignOwner(_ ...int) error { - m.wg.Add(1) - go func() { - logutil.BgLogger().Debug("owner manager campaign owner", zap.String("category", "ddl"), - zap.String("ID", m.id), zap.String("ownerKey", m.key)) - defer m.wg.Done() - for { - select { - case <-m.campaignDone: - m.RetireOwner() - logutil.BgLogger().Debug("owner manager campaign done", zap.String("category", "ddl"), zap.String("ID", m.id)) - return - case <-m.ctx.Done(): - m.RetireOwner() - logutil.BgLogger().Debug("owner manager is cancelled", zap.String("category", "ddl"), zap.String("ID", m.id)) - return - case <-m.resignDone: - m.RetireOwner() - //nolint: errcheck - timeutil.Sleep(m.ctx, 1*time.Second) // Give a chance to the other owner managers to get owner. - default: - m.toBeOwner() - //nolint: errcheck - timeutil.Sleep(m.ctx, 1*time.Second) // Speed up domain.Close() - logutil.BgLogger().Debug("owner manager tick", zap.String("category", "ddl"), zap.String("ID", m.id), - zap.String("ownerKey", m.key), zap.String("currentOwner", util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).GetOwner())) - } - } - }() - return nil -} - -// ResignOwner lets the owner start a new election. -func (m *mockManager) ResignOwner(_ context.Context) error { - m.resignDone <- struct{}{} - return nil -} - -// RequireOwner implements Manager.RequireOwner interface. -func (*mockManager) RequireOwner(context.Context) error { - return nil -} - -func (m *mockManager) SetBeOwnerHook(hook func()) { - m.beOwnerHook = hook -} - -// CampaignCancel implements Manager.CampaignCancel interface -func (m *mockManager) CampaignCancel() { - m.campaignDone <- struct{}{} -} - -func mockDelOwnerKey(mockCal, ownerKey string, m *ownerManager) error { - checkIsOwner := func(m *ownerManager, checkTrue bool) error { - // 5s - for i := 0; i < 100; i++ { - if m.IsOwner() == checkTrue { - break - } - time.Sleep(50 * time.Millisecond) - } - if m.IsOwner() != checkTrue { - return errors.Errorf("expect manager state:%v", checkTrue) - } - return nil - } - - needCheckOwner := false - switch mockCal { - case "delOwnerKeyAndNotOwner": - m.CampaignCancel() - // Make sure the manager is not owner. And it will exit campaignLoop. - err := checkIsOwner(m, false) - if err != nil { - return err - } - case "onlyDelOwnerKey": - needCheckOwner = true - } - - err := util.DeleteKeyFromEtcd(ownerKey, m.etcdCli, 1, keyOpDefaultTimeout) - if err != nil { - return errors.Trace(err) - } - if needCheckOwner { - // Mock the manager become not owner because the owner is deleted(like TTL is timeout). - // And then the manager campaigns the owner again, and become the owner. - err = checkIsOwner(m, true) - if err != nil { - return err - } - } - return nil -} diff --git a/parser/BUILD.bazel b/parser/BUILD.bazel deleted file mode 100644 index 2bec326093320..0000000000000 --- a/parser/BUILD.bazel +++ /dev/null @@ -1,65 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -exports_files([ - "go.mod", - "go.sum", -]) - -go_library( - name = "parser", - srcs = [ - "digester.go", - "hintparser.go", - "hintparserimpl.go", - "lexer.go", - "misc.go", - "parser.go", - "yy_parser.go", - ], - importpath = "github.com/pingcap/tidb/parser", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/duration", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/tidb", - "//parser/types", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "parser_test", - timeout = "short", - srcs = [ - "bench_test.go", - "consistent_test.go", - "digester_test.go", - "hintparser_test.go", - "lexer_test.go", - "main_test.go", - "parser_test.go", - ], - data = glob(["**"]), - embed = [":parser"], - flaky = True, - shard_count = 50, - deps = [ - "//parser/ast", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/test_driver", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/parser/ast/BUILD.bazel b/parser/ast/BUILD.bazel deleted file mode 100644 index e3da13a6d4911..0000000000000 --- a/parser/ast/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ast", - srcs = [ - "advisor.go", - "ast.go", - "base.go", - "ddl.go", - "dml.go", - "expressions.go", - "flag.go", - "functions.go", - "misc.go", - "procedure.go", - "stats.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/parser/ast", - visibility = ["//visibility:public"], - deps = [ - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/tidb", - "//parser/types", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - ], -) - -go_test( - name = "ast_test", - timeout = "short", - srcs = [ - "base_test.go", - "ddl_test.go", - "dml_test.go", - "expressions_test.go", - "flag_test.go", - "format_test.go", - "functions_test.go", - "misc_test.go", - "procedure_test.go", - "util_test.go", - ], - embed = [":ast"], - flaky = True, - shard_count = 50, - deps = [ - "//parser", - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/mysql", - "//parser/test_driver", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/ast/ast.go b/parser/ast/ast.go deleted file mode 100644 index ab0ed2481e6ee..0000000000000 --- a/parser/ast/ast.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package ast is the abstract syntax tree parsed from a SQL statement by parser. -// It can be analysed and transformed by optimizer. -package ast - -import ( - "io" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/types" -) - -// Node is the basic element of the AST. -// Interfaces embed Node should have 'Node' name suffix. -type Node interface { - // Restore returns the sql text from ast tree - Restore(ctx *format.RestoreCtx) error - // Accept accepts Visitor to visit itself. - // The returned node should replace original node. - // ok returns false to stop visiting. - // - // Implementation of this method should first call visitor.Enter, - // assign the returned node to its method receiver, if skipChildren returns true, - // children should be skipped. Otherwise, call its children in particular order that - // later elements depends on former elements. Finally, return visitor.Leave. - Accept(v Visitor) (node Node, ok bool) - // Text returns the utf8 encoding text of the element. - Text() string - // OriginalText returns the original text of the element. - OriginalText() string - // SetText sets original text to the Node. - SetText(enc charset.Encoding, text string) - // SetOriginTextPosition set the start offset of this node in the origin text. - SetOriginTextPosition(offset int) - // OriginTextPosition get the start offset of this node in the origin text. - OriginTextPosition() int -} - -// Flags indicates whether an expression contains certain types of expression. -const ( - FlagConstant uint64 = 0 - FlagHasParamMarker uint64 = 1 << iota - FlagHasFunc - FlagHasReference - FlagHasAggregateFunc - FlagHasSubquery - FlagHasVariable - FlagHasDefault - FlagPreEvaluated - FlagHasWindowFunc -) - -// ExprNode is a node that can be evaluated. -// Name of implementations should have 'Expr' suffix. -type ExprNode interface { - // Node is embedded in ExprNode. - Node - // SetType sets evaluation type to the expression. - SetType(tp *types.FieldType) - // GetType gets the evaluation type of the expression. - GetType() *types.FieldType - // SetFlag sets flag to the expression. - // Flag indicates whether the expression contains - // parameter marker, reference, aggregate function... - SetFlag(flag uint64) - // GetFlag returns the flag of the expression. - GetFlag() uint64 - - // Format formats the AST into a writer. - Format(w io.Writer) -} - -// OptBinary is used for parser. -type OptBinary struct { - IsBinary bool - Charset string -} - -// FuncNode represents function call expression node. -type FuncNode interface { - ExprNode - functionExpression() -} - -// StmtNode represents statement node. -// Name of implementations should have 'Stmt' suffix. -type StmtNode interface { - Node - statement() -} - -// DDLNode represents DDL statement node. -type DDLNode interface { - StmtNode - ddlStatement() -} - -// DMLNode represents DML statement node. -type DMLNode interface { - StmtNode - dmlStatement() -} - -// ResultField represents a result field which can be a column from a table, -// or an expression in select field. It is a generated property during -// binding process. ResultField is the key element to evaluate a ColumnNameExpr. -// After resolving process, every ColumnNameExpr will be resolved to a ResultField. -// During execution, every row retrieved from table will set the row value to -// ResultFields of that table, so ColumnNameExpr resolved to that ResultField can be -// easily evaluated. -type ResultField struct { - Column *model.ColumnInfo - ColumnAsName model.CIStr - // EmptyOrgName indicates whether this field has an empty org_name. A field has an empty org name, if it's an - // expression. It's not sure whether it's safe to use empty string in `.Column.Name`, so a new field is added to - // indicate whether it's empty. - EmptyOrgName bool - - Table *model.TableInfo - TableAsName model.CIStr - DBName model.CIStr -} - -// ResultSetNode interface has a ResultFields property, represents a Node that returns result set. -// Implementations include SelectStmt, SubqueryExpr, TableSource, TableName, Join and SetOprStmt. -type ResultSetNode interface { - Node - - resultSet() -} - -// SensitiveStmtNode overloads StmtNode and provides a SecureText method. -type SensitiveStmtNode interface { - StmtNode - // SecureText is different from Text that it hide password information. - SecureText() string -} - -// Visitor visits a Node. -type Visitor interface { - // Enter is called before children nodes are visited. - // The returned node must be the same type as the input node n. - // skipChildren returns true means children nodes should be skipped, - // this is useful when work is done in Enter and there is no need to visit children. - Enter(n Node) (node Node, skipChildren bool) - // Leave is called after children nodes have been visited. - // The returned node's type can be different from the input node if it is a ExprNode, - // Non-expression node must be the same type as the input node n. - // ok returns false to stop visiting. - Leave(n Node) (node Node, ok bool) -} - -// GetStmtLabel generates a label for a statement. -func GetStmtLabel(stmtNode StmtNode) string { - switch x := stmtNode.(type) { - case *AlterTableStmt: - return "AlterTable" - case *AnalyzeTableStmt: - return "AnalyzeTable" - case *BeginStmt: - return "Begin" - case *ChangeStmt: - return "Change" - case *CommitStmt: - return "Commit" - case *CompactTableStmt: - return "CompactTable" - case *CreateDatabaseStmt: - return "CreateDatabase" - case *CreateIndexStmt: - return "CreateIndex" - case *CreateTableStmt: - return "CreateTable" - case *CreateViewStmt: - return "CreateView" - case *CreateUserStmt: - return "CreateUser" - case *DeleteStmt: - return "Delete" - case *DropDatabaseStmt: - return "DropDatabase" - case *DropIndexStmt: - return "DropIndex" - case *DropTableStmt: - if x.IsView { - return "DropView" - } - return "DropTable" - case *ExplainStmt: - if _, ok := x.Stmt.(*ShowStmt); ok { - return "DescTable" - } - if x.Analyze { - return "ExplainAnalyzeSQL" - } - return "ExplainSQL" - case *InsertStmt: - if x.IsReplace { - return "Replace" - } - return "Insert" - case *ImportIntoStmt: - return "ImportInto" - case *LoadDataStmt: - return "LoadData" - case *RollbackStmt: - return "Rollback" - case *SelectStmt: - return "Select" - case *SetStmt, *SetPwdStmt: - return "Set" - case *ShowStmt: - return "Show" - case *TruncateTableStmt: - return "TruncateTable" - case *UpdateStmt: - return "Update" - case *GrantStmt: - return "Grant" - case *RevokeStmt: - return "Revoke" - case *DeallocateStmt: - return "Deallocate" - case *ExecuteStmt: - return "Execute" - case *PrepareStmt: - return "Prepare" - case *UseStmt: - return "Use" - case *CreateBindingStmt: - return "CreateBinding" - case *IndexAdviseStmt: - return "IndexAdvise" - case *DropBindingStmt: - return "DropBinding" - case *TraceStmt: - return "Trace" - case *ShutdownStmt: - return "Shutdown" - case *SavepointStmt: - return "Savepoint" - } - return "other" -} diff --git a/parser/ast/base.go b/parser/ast/base.go deleted file mode 100644 index a7dcc9cefdc55..0000000000000 --- a/parser/ast/base.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "sync" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/types" -) - -// node is the struct implements Node interface except for Accept method. -// Node implementations should embed it in. -type node struct { - utf8Text string - enc charset.Encoding - once *sync.Once - - text string - offset int -} - -// SetOriginTextPosition implements Node interface. -func (n *node) SetOriginTextPosition(offset int) { - n.offset = offset -} - -// OriginTextPosition implements Node interface. -func (n *node) OriginTextPosition() int { - return n.offset -} - -// SetText implements Node interface. -func (n *node) SetText(enc charset.Encoding, text string) { - n.enc = enc - n.text = text - n.once = &sync.Once{} -} - -// Text implements Node interface. -func (n *node) Text() string { - if n.once == nil { - return n.text - } - n.once.Do(func() { - if n.enc == nil { - n.utf8Text = n.text - return - } - utf8Lit, _ := n.enc.Transform(nil, charset.HackSlice(n.text), charset.OpDecodeReplace) - n.utf8Text = charset.HackString(utf8Lit) - }) - return n.utf8Text -} - -// OriginalText implements Node interface. -func (n *node) OriginalText() string { - return n.text -} - -// stmtNode implements StmtNode interface. -// Statement implementations should embed it in. -type stmtNode struct { - node -} - -// statement implements StmtNode interface. -func (sn *stmtNode) statement() {} - -// ddlNode implements DDLNode interface. -// DDL implementations should embed it in. -type ddlNode struct { - stmtNode -} - -// ddlStatement implements DDLNode interface. -func (dn *ddlNode) ddlStatement() {} - -// dmlNode is the struct implements DMLNode interface. -// DML implementations should embed it in. -type dmlNode struct { - stmtNode -} - -// dmlStatement implements DMLNode interface. -func (dn *dmlNode) dmlStatement() {} - -// exprNode is the struct implements Expression interface. -// Expression implementations should embed it in. -type exprNode struct { - node - Type types.FieldType - flag uint64 -} - -// TexprNode is exported for parser driver. -type TexprNode = exprNode - -// SetType implements ExprNode interface. -func (en *exprNode) SetType(tp *types.FieldType) { - en.Type = *tp -} - -// GetType implements ExprNode interface. -func (en *exprNode) GetType() *types.FieldType { - return &en.Type -} - -// SetFlag implements ExprNode interface. -func (en *exprNode) SetFlag(flag uint64) { - en.flag = flag -} - -// GetFlag implements ExprNode interface. -func (en *exprNode) GetFlag() uint64 { - return en.flag -} - -type funcNode struct { - exprNode -} - -// functionExpression implements FunctionNode interface. -func (fn *funcNode) functionExpression() {} diff --git a/parser/ast/base_test.go b/parser/ast/base_test.go deleted file mode 100644 index 17a79321b1478..0000000000000 --- a/parser/ast/base_test.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package ast is the abstract syntax tree parsed from a SQL statement by parser. -// It can be analysed and transformed by optimizer. -package ast - -import ( - "testing" - - "github.com/pingcap/tidb/parser/charset" - "github.com/stretchr/testify/require" -) - -func TestNodeSetText(t *testing.T) { - n := &node{} - tests := []struct { - text string - enc charset.Encoding - expectUTF8Text string - expectText string - }{ - {"你好", nil, "你好", "你好"}, - {"\xd2\xbb", charset.EncodingGBKImpl, "一", "\xd2\xbb"}, - {"\xc1\xd0", charset.EncodingGBKImpl, "列", "\xc1\xd0"}, - } - for _, tt := range tests { - n.SetText(tt.enc, tt.text) - require.Equal(t, tt.expectUTF8Text, n.Text()) - require.Equal(t, tt.expectText, n.OriginalText()) - } -} diff --git a/parser/ast/ddl.go b/parser/ast/ddl.go deleted file mode 100644 index 19e2f2de3f8d6..0000000000000 --- a/parser/ast/ddl.go +++ /dev/null @@ -1,4754 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/parser/tidb" - "github.com/pingcap/tidb/parser/types" -) - -var ( - _ DDLNode = &AlterTableStmt{} - _ DDLNode = &AlterSequenceStmt{} - _ DDLNode = &AlterPlacementPolicyStmt{} - _ DDLNode = &AlterResourceGroupStmt{} - _ DDLNode = &CreateDatabaseStmt{} - _ DDLNode = &CreateIndexStmt{} - _ DDLNode = &CreateTableStmt{} - _ DDLNode = &CreateViewStmt{} - _ DDLNode = &CreateSequenceStmt{} - _ DDLNode = &CreatePlacementPolicyStmt{} - _ DDLNode = &CreateResourceGroupStmt{} - _ DDLNode = &DropDatabaseStmt{} - _ DDLNode = &FlashBackDatabaseStmt{} - _ DDLNode = &DropIndexStmt{} - _ DDLNode = &DropTableStmt{} - _ DDLNode = &DropSequenceStmt{} - _ DDLNode = &DropPlacementPolicyStmt{} - _ DDLNode = &DropResourceGroupStmt{} - _ DDLNode = &RenameTableStmt{} - _ DDLNode = &TruncateTableStmt{} - _ DDLNode = &RepairTableStmt{} - - _ Node = &AlterTableSpec{} - _ Node = &ColumnDef{} - _ Node = &ColumnOption{} - _ Node = &ColumnPosition{} - _ Node = &Constraint{} - _ Node = &IndexPartSpecification{} - _ Node = &ReferenceDef{} -) - -// CharsetOpt is used for parsing charset option from SQL. -type CharsetOpt struct { - Chs string - Col string -} - -// NullString represents a string that may be nil. -type NullString struct { - String string - Empty bool // Empty is true if String is empty backtick. -} - -// DatabaseOptionType is the type for database options. -type DatabaseOptionType int - -// Database option types. -const ( - DatabaseOptionNone DatabaseOptionType = iota - DatabaseOptionCharset - DatabaseOptionCollate - DatabaseOptionEncryption - DatabaseSetTiFlashReplica - DatabaseOptionPlacementPolicy = DatabaseOptionType(PlacementOptionPolicy) -) - -// DatabaseOption represents database option. -type DatabaseOption struct { - Tp DatabaseOptionType - Value string - UintValue uint64 - TiFlashReplica *TiFlashReplicaSpec -} - -// Restore implements Node interface. -func (n *DatabaseOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case DatabaseOptionCharset: - ctx.WriteKeyWord("CHARACTER SET") - ctx.WritePlain(" = ") - ctx.WritePlain(n.Value) - case DatabaseOptionCollate: - ctx.WriteKeyWord("COLLATE") - ctx.WritePlain(" = ") - ctx.WritePlain(n.Value) - case DatabaseOptionEncryption: - ctx.WriteKeyWord("ENCRYPTION") - ctx.WritePlain(" = ") - ctx.WriteString(n.Value) - case DatabaseOptionPlacementPolicy: - placementOpt := PlacementOption{ - Tp: PlacementOptionPolicy, - UintValue: n.UintValue, - StrValue: n.Value, - } - return placementOpt.Restore(ctx) - case DatabaseSetTiFlashReplica: - ctx.WriteKeyWord("SET TIFLASH REPLICA ") - ctx.WritePlainf("%d", n.TiFlashReplica.Count) - if len(n.TiFlashReplica.Labels) == 0 { - break - } - ctx.WriteKeyWord(" LOCATION LABELS ") - for i, v := range n.TiFlashReplica.Labels { - if i > 0 { - ctx.WritePlain(", ") - } - ctx.WriteString(v) - } - default: - return errors.Errorf("invalid DatabaseOptionType: %d", n.Tp) - } - return nil -} - -// CreateDatabaseStmt is a statement to create a database. -// See https://dev.mysql.com/doc/refman/5.7/en/create-database.html -type CreateDatabaseStmt struct { - ddlNode - - IfNotExists bool - Name model.CIStr - Options []*DatabaseOption -} - -// Restore implements Node interface. -func (n *CreateDatabaseStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE DATABASE ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.Name.O) - for i, option := range n.Options { - ctx.WritePlain(" ") - err := option.Restore(ctx) - if err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateDatabaseStmt DatabaseOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateDatabaseStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateDatabaseStmt) - return v.Leave(n) -} - -// AlterDatabaseStmt is a statement to change the structure of a database. -// See https://dev.mysql.com/doc/refman/5.7/en/alter-database.html -type AlterDatabaseStmt struct { - ddlNode - - Name model.CIStr - AlterDefaultDatabase bool - Options []*DatabaseOption -} - -// Restore implements Node interface. -func (n *AlterDatabaseStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.isAllPlacementOptions() { - return nil - } - // If all options placement options and RestoreTiDBSpecialComment flag is on, - // we should restore the whole node in special comment. For example, the restore result should be: - // /*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */ - // instead of - // ALTER DATABASE `db1` /*T![placement] PLACEMENT POLICY = `p1` */ - // because altering a database without any options is not a legal syntax in mysql - if n.isAllPlacementOptions() && ctx.Flags.HasTiDBSpecialCommentFlag() { - return restorePlacementStmtInSpecialComment(ctx, n) - } - - ctx.WriteKeyWord("ALTER DATABASE") - if !n.AlterDefaultDatabase { - ctx.WritePlain(" ") - ctx.WriteName(n.Name.O) - } - for i, option := range n.Options { - ctx.WritePlain(" ") - err := option.Restore(ctx) - if err != nil { - return errors.Annotatef(err, "An error occurred while splicing AlterDatabaseStmt DatabaseOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AlterDatabaseStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterDatabaseStmt) - return v.Leave(n) -} - -func (n *AlterDatabaseStmt) isAllPlacementOptions() bool { - for _, n := range n.Options { - switch n.Tp { - case DatabaseOptionPlacementPolicy: - default: - return false - } - } - return true -} - -// DropDatabaseStmt is a statement to drop a database and all tables in the database. -// See https://dev.mysql.com/doc/refman/5.7/en/drop-database.html -type DropDatabaseStmt struct { - ddlNode - - IfExists bool - Name model.CIStr -} - -// Restore implements Node interface. -func (n *DropDatabaseStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP DATABASE ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.Name.O) - return nil -} - -// Accept implements Node Accept interface. -func (n *DropDatabaseStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropDatabaseStmt) - return v.Leave(n) -} - -// FlashBackDatabaseStmt is a statement to restore a database and all tables in the database. -type FlashBackDatabaseStmt struct { - ddlNode - - DBName model.CIStr - NewName string -} - -// Restore implements Node interface. -func (n *FlashBackDatabaseStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("FLASHBACK DATABASE ") - ctx.WriteName(n.DBName.O) - if len(n.NewName) > 0 { - ctx.WriteKeyWord(" TO ") - ctx.WriteName(n.NewName) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *FlashBackDatabaseStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*FlashBackDatabaseStmt) - return v.Leave(n) -} - -// IndexPartSpecifications is used for parsing index column name or index expression from SQL. -type IndexPartSpecification struct { - node - - Column *ColumnName - Length int - // Order is parsed but should be ignored because MySQL v5.7 doesn't support it. - Desc bool - Expr ExprNode -} - -// Restore implements Node interface. -func (n *IndexPartSpecification) Restore(ctx *format.RestoreCtx) error { - if n.Expr != nil { - ctx.WritePlain("(") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications") - } - ctx.WritePlain(")") - if n.Desc { - ctx.WritePlain(" DESC") - } - return nil - } - if err := n.Column.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications") - } - if n.Length > 0 { - ctx.WritePlainf("(%d)", n.Length) - } - if n.Desc { - ctx.WritePlain(" DESC") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *IndexPartSpecification) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*IndexPartSpecification) - if n.Expr != nil { - node, ok := n.Expr.Accept(v) - if !ok { - return n, false - } - n.Expr = node.(ExprNode) - return v.Leave(n) - } - node, ok := n.Column.Accept(v) - if !ok { - return n, false - } - n.Column = node.(*ColumnName) - return v.Leave(n) -} - -// MatchType is the type for reference match type. -type MatchType int - -// match type -const ( - MatchNone MatchType = iota - MatchFull - MatchPartial - MatchSimple -) - -// ReferenceDef is used for parsing foreign key reference option from SQL. -// See http://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html -type ReferenceDef struct { - node - - Table *TableName - IndexPartSpecifications []*IndexPartSpecification - OnDelete *OnDeleteOpt - OnUpdate *OnUpdateOpt - Match MatchType -} - -// Restore implements Node interface. -func (n *ReferenceDef) Restore(ctx *format.RestoreCtx) error { - if n.Table != nil { - ctx.WriteKeyWord("REFERENCES ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ReferenceDef") - } - } - - if n.IndexPartSpecifications != nil { - ctx.WritePlain("(") - for i, indexColNames := range n.IndexPartSpecifications { - if i > 0 { - ctx.WritePlain(", ") - } - if err := indexColNames.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing IndexPartSpecifications: [%v]", i) - } - } - ctx.WritePlain(")") - } - - if n.Match != MatchNone { - ctx.WriteKeyWord(" MATCH ") - switch n.Match { - case MatchFull: - ctx.WriteKeyWord("FULL") - case MatchPartial: - ctx.WriteKeyWord("PARTIAL") - case MatchSimple: - ctx.WriteKeyWord("SIMPLE") - } - } - if n.OnDelete.ReferOpt != model.ReferOptionNoOption { - ctx.WritePlain(" ") - if err := n.OnDelete.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing OnDelete") - } - } - if n.OnUpdate.ReferOpt != model.ReferOptionNoOption { - ctx.WritePlain(" ") - if err := n.OnUpdate.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing OnUpdate") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ReferenceDef) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ReferenceDef) - if n.Table != nil { - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - } - for i, val := range n.IndexPartSpecifications { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.IndexPartSpecifications[i] = node.(*IndexPartSpecification) - } - onDelete, ok := n.OnDelete.Accept(v) - if !ok { - return n, false - } - n.OnDelete = onDelete.(*OnDeleteOpt) - onUpdate, ok := n.OnUpdate.Accept(v) - if !ok { - return n, false - } - n.OnUpdate = onUpdate.(*OnUpdateOpt) - return v.Leave(n) -} - -// OnDeleteOpt is used for optional on delete clause. -type OnDeleteOpt struct { - node - ReferOpt model.ReferOptionType -} - -// Restore implements Node interface. -func (n *OnDeleteOpt) Restore(ctx *format.RestoreCtx) error { - if n.ReferOpt != model.ReferOptionNoOption { - ctx.WriteKeyWord("ON DELETE ") - ctx.WriteKeyWord(n.ReferOpt.String()) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *OnDeleteOpt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*OnDeleteOpt) - return v.Leave(n) -} - -// OnUpdateOpt is used for optional on update clause. -type OnUpdateOpt struct { - node - ReferOpt model.ReferOptionType -} - -// Restore implements Node interface. -func (n *OnUpdateOpt) Restore(ctx *format.RestoreCtx) error { - if n.ReferOpt != model.ReferOptionNoOption { - ctx.WriteKeyWord("ON UPDATE ") - ctx.WriteKeyWord(n.ReferOpt.String()) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *OnUpdateOpt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*OnUpdateOpt) - return v.Leave(n) -} - -// ColumnOptionType is the type for ColumnOption. -type ColumnOptionType int - -// ColumnOption types. -const ( - ColumnOptionNoOption ColumnOptionType = iota - ColumnOptionPrimaryKey - ColumnOptionNotNull - ColumnOptionAutoIncrement - ColumnOptionDefaultValue - ColumnOptionUniqKey - ColumnOptionNull - ColumnOptionOnUpdate // For Timestamp and Datetime only. - ColumnOptionFulltext - ColumnOptionComment - ColumnOptionGenerated - ColumnOptionReference - ColumnOptionCollate - ColumnOptionCheck - ColumnOptionColumnFormat - ColumnOptionStorage - ColumnOptionAutoRandom -) - -var ( - invalidOptionForGeneratedColumn = map[ColumnOptionType]string{ - ColumnOptionAutoIncrement: "AUTO_INCREMENT", - ColumnOptionOnUpdate: "ON UPDATE", - ColumnOptionDefaultValue: "DEFAULT", - } -) - -// ColumnOption is used for parsing column constraint info from SQL. -type ColumnOption struct { - node - - Tp ColumnOptionType - // Expr is used for ColumnOptionDefaultValue/ColumnOptionOnUpdateColumnOptionGenerated. - // For ColumnOptionDefaultValue or ColumnOptionOnUpdate, it's the target value. - // For ColumnOptionGenerated, it's the target expression. - Expr ExprNode - // Stored is only for ColumnOptionGenerated, default is false. - Stored bool - // Refer is used for foreign key. - Refer *ReferenceDef - StrValue string - AutoRandOpt AutoRandomOption - // Enforced is only for Check, default is true. - Enforced bool - // Name is only used for Check Constraint name. - ConstraintName string - PrimaryKeyTp model.PrimaryKeyType -} - -// Restore implements Node interface. -func (n *ColumnOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case ColumnOptionNoOption: - return nil - case ColumnOptionPrimaryKey: - ctx.WriteKeyWord("PRIMARY KEY") - pkTp := n.PrimaryKeyTp.String() - if len(pkTp) != 0 { - ctx.WritePlain(" ") - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDClusteredIndex, func() error { - ctx.WriteKeyWord(pkTp) - return nil - }) - } - case ColumnOptionNotNull: - ctx.WriteKeyWord("NOT NULL") - case ColumnOptionAutoIncrement: - ctx.WriteKeyWord("AUTO_INCREMENT") - case ColumnOptionDefaultValue: - ctx.WriteKeyWord("DEFAULT ") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnOption DefaultValue Expr") - } - case ColumnOptionUniqKey: - ctx.WriteKeyWord("UNIQUE KEY") - case ColumnOptionNull: - ctx.WriteKeyWord("NULL") - case ColumnOptionOnUpdate: - ctx.WriteKeyWord("ON UPDATE ") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnOption ON UPDATE Expr") - } - case ColumnOptionFulltext: - return errors.New("TiDB Parser ignore the `ColumnOptionFulltext` type now") - case ColumnOptionComment: - ctx.WriteKeyWord("COMMENT ") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnOption COMMENT Expr") - } - case ColumnOptionGenerated: - ctx.WriteKeyWord("GENERATED ALWAYS AS") - ctx.WritePlain("(") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnOption GENERATED ALWAYS Expr") - } - ctx.WritePlain(")") - if n.Stored { - ctx.WriteKeyWord(" STORED") - } else { - ctx.WriteKeyWord(" VIRTUAL") - } - case ColumnOptionReference: - if err := n.Refer.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnOption ReferenceDef") - } - case ColumnOptionCollate: - if n.StrValue == "" { - return errors.New("Empty ColumnOption COLLATE") - } - ctx.WriteKeyWord("COLLATE ") - ctx.WritePlain(n.StrValue) - case ColumnOptionCheck: - if n.ConstraintName != "" { - ctx.WriteKeyWord("CONSTRAINT ") - ctx.WriteName(n.ConstraintName) - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("CHECK") - ctx.WritePlain("(") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Trace(err) - } - ctx.WritePlain(")") - if n.Enforced { - ctx.WriteKeyWord(" ENFORCED") - } else { - ctx.WriteKeyWord(" NOT ENFORCED") - } - case ColumnOptionColumnFormat: - ctx.WriteKeyWord("COLUMN_FORMAT ") - ctx.WriteKeyWord(n.StrValue) - case ColumnOptionStorage: - ctx.WriteKeyWord("STORAGE ") - ctx.WriteKeyWord(n.StrValue) - case ColumnOptionAutoRandom: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandom, func() error { - ctx.WriteKeyWord("AUTO_RANDOM") - opt := n.AutoRandOpt - if opt.ShardBits != types.UnspecifiedLength { - if opt.RangeBits != types.UnspecifiedLength { - ctx.WritePlainf("(%d, %d)", opt.ShardBits, opt.RangeBits) - } else { - ctx.WritePlainf("(%d)", opt.ShardBits) - } - } - return nil - }) - default: - return errors.New("An error occurred while splicing ColumnOption") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ColumnOption) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ColumnOption) - if n.Expr != nil { - node, ok := n.Expr.Accept(v) - if !ok { - return n, false - } - n.Expr = node.(ExprNode) - } - return v.Leave(n) -} - -// AutoRandomOption contains the length of shard bits and range bits. -type AutoRandomOption struct { - // ShardBits is the number of bits used to store the shard. - ShardBits int - // RangeBits is the number of int primary key bits that will be used by TiDB. - RangeBits int -} - -// IndexVisibility is the option for index visibility. -type IndexVisibility int - -// IndexVisibility options. -const ( - IndexVisibilityDefault IndexVisibility = iota - IndexVisibilityVisible - IndexVisibilityInvisible -) - -// IndexOption is the index options. -// -// KEY_BLOCK_SIZE [=] value -// | index_type -// | WITH PARSER parser_name -// | COMMENT 'string' -// -// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html -type IndexOption struct { - node - - KeyBlockSize uint64 - Tp model.IndexType - Comment string - ParserName model.CIStr - Visibility IndexVisibility - PrimaryKeyTp model.PrimaryKeyType -} - -// Restore implements Node interface. -func (n *IndexOption) Restore(ctx *format.RestoreCtx) error { - hasPrevOption := false - if n.PrimaryKeyTp != model.PrimaryKeyTypeDefault { - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDClusteredIndex, func() error { - ctx.WriteKeyWord(n.PrimaryKeyTp.String()) - return nil - }) - hasPrevOption = true - } - if n.KeyBlockSize > 0 { - if hasPrevOption { - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("KEY_BLOCK_SIZE") - ctx.WritePlainf("=%d", n.KeyBlockSize) - hasPrevOption = true - } - - if n.Tp != model.IndexTypeInvalid { - if hasPrevOption { - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("USING ") - ctx.WritePlain(n.Tp.String()) - hasPrevOption = true - } - - if len(n.ParserName.O) > 0 { - if hasPrevOption { - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("WITH PARSER ") - ctx.WriteName(n.ParserName.O) - hasPrevOption = true - } - - if n.Comment != "" { - if hasPrevOption { - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("COMMENT ") - ctx.WriteString(n.Comment) - hasPrevOption = true - } - - if n.Visibility != IndexVisibilityDefault { - if hasPrevOption { - ctx.WritePlain(" ") - } - switch n.Visibility { - case IndexVisibilityVisible: - ctx.WriteKeyWord("VISIBLE") - case IndexVisibilityInvisible: - ctx.WriteKeyWord("INVISIBLE") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *IndexOption) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*IndexOption) - return v.Leave(n) -} - -// ConstraintType is the type for Constraint. -type ConstraintType int - -// ConstraintTypes -const ( - ConstraintNoConstraint ConstraintType = iota - ConstraintPrimaryKey - ConstraintKey - ConstraintIndex - ConstraintUniq - ConstraintUniqKey - ConstraintUniqIndex - ConstraintForeignKey - ConstraintFulltext - ConstraintCheck -) - -// Constraint is constraint for table definition. -type Constraint struct { - node - - // only supported by MariaDB 10.0.2+ (ADD {INDEX|KEY}, ADD FOREIGN KEY), - // see https://mariadb.com/kb/en/library/alter-table/ - IfNotExists bool - - Tp ConstraintType - Name string - - Keys []*IndexPartSpecification // Used for PRIMARY KEY, UNIQUE, ...... - - Refer *ReferenceDef // Used for foreign key. - - Option *IndexOption // Index Options - - Expr ExprNode // Used for Check - - Enforced bool // Used for Check - - InColumn bool // Used for Check - - InColumnName string // Used for Check - IsEmptyIndex bool // Used for Check -} - -// Restore implements Node interface. -func (n *Constraint) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case ConstraintNoConstraint: - return nil - case ConstraintPrimaryKey: - ctx.WriteKeyWord("PRIMARY KEY") - case ConstraintKey: - ctx.WriteKeyWord("KEY") - if n.IfNotExists { - ctx.WriteKeyWord(" IF NOT EXISTS") - } - case ConstraintIndex: - ctx.WriteKeyWord("INDEX") - if n.IfNotExists { - ctx.WriteKeyWord(" IF NOT EXISTS") - } - case ConstraintUniq: - ctx.WriteKeyWord("UNIQUE") - case ConstraintUniqKey: - ctx.WriteKeyWord("UNIQUE KEY") - case ConstraintUniqIndex: - ctx.WriteKeyWord("UNIQUE INDEX") - case ConstraintFulltext: - ctx.WriteKeyWord("FULLTEXT") - case ConstraintCheck: - if n.Name != "" { - ctx.WriteKeyWord("CONSTRAINT ") - ctx.WriteName(n.Name) - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("CHECK") - ctx.WritePlain("(") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Trace(err) - } - ctx.WritePlain(") ") - if n.Enforced { - ctx.WriteKeyWord("ENFORCED") - } else { - ctx.WriteKeyWord("NOT ENFORCED") - } - return nil - } - - if n.Tp == ConstraintForeignKey { - ctx.WriteKeyWord("CONSTRAINT ") - if n.Name != "" { - ctx.WriteName(n.Name) - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("FOREIGN KEY ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - } else if n.Name != "" || n.IsEmptyIndex { - ctx.WritePlain(" ") - ctx.WriteName(n.Name) - } - - ctx.WritePlain("(") - for i, keys := range n.Keys { - if i > 0 { - ctx.WritePlain(", ") - } - if err := keys.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing Constraint Keys: [%v]", i) - } - } - ctx.WritePlain(")") - - if n.Refer != nil { - ctx.WritePlain(" ") - if err := n.Refer.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing Constraint Refer") - } - } - - if n.Option != nil { - ctx.WritePlain(" ") - if err := n.Option.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing Constraint Option") - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *Constraint) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*Constraint) - for i, val := range n.Keys { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Keys[i] = node.(*IndexPartSpecification) - } - if n.Refer != nil { - node, ok := n.Refer.Accept(v) - if !ok { - return n, false - } - n.Refer = node.(*ReferenceDef) - } - if n.Option != nil { - node, ok := n.Option.Accept(v) - if !ok { - return n, false - } - n.Option = node.(*IndexOption) - } - if n.Expr != nil { - node, ok := n.Expr.Accept(v) - if !ok { - return n, false - } - n.Expr = node.(ExprNode) - } - return v.Leave(n) -} - -// ColumnDef is used for parsing column definition from SQL. -type ColumnDef struct { - node - - Name *ColumnName - Tp *types.FieldType - Options []*ColumnOption -} - -// Restore implements Node interface. -func (n *ColumnDef) Restore(ctx *format.RestoreCtx) error { - if err := n.Name.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnDef Name") - } - if n.Tp != nil { - ctx.WritePlain(" ") - if err := n.Tp.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing ColumnDef Type") - } - } - for i, options := range n.Options { - ctx.WritePlain(" ") - if err := options.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing ColumnDef ColumnOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ColumnDef) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ColumnDef) - node, ok := n.Name.Accept(v) - if !ok { - return n, false - } - n.Name = node.(*ColumnName) - for i, val := range n.Options { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Options[i] = node.(*ColumnOption) - } - return v.Leave(n) -} - -// Validate checks if a column definition is legal. -// For example, generated column definitions that contain such -// column options as `ON UPDATE`, `AUTO_INCREMENT`, `DEFAULT` -// are illegal. -func (n *ColumnDef) Validate() error { - generatedCol := false - var illegalOpt4gc string - for _, opt := range n.Options { - if opt.Tp == ColumnOptionGenerated { - generatedCol = true - } - msg, found := invalidOptionForGeneratedColumn[opt.Tp] - if found { - illegalOpt4gc = msg - } - } - if generatedCol && illegalOpt4gc != "" { - return ErrWrongUsage.GenWithStackByArgs(illegalOpt4gc, "generated column") - } - return nil -} - -type TemporaryKeyword int - -const ( - TemporaryNone TemporaryKeyword = iota - TemporaryGlobal - TemporaryLocal -) - -// CreateTableStmt is a statement to create a table. -// See https://dev.mysql.com/doc/refman/5.7/en/create-table.html -type CreateTableStmt struct { - ddlNode - - IfNotExists bool - TemporaryKeyword - // Meanless when TemporaryKeyword is not TemporaryGlobal. - // ON COMMIT DELETE ROWS => true - // ON COMMIT PRESERVE ROW => false - OnCommitDelete bool - Table *TableName - ReferTable *TableName - Cols []*ColumnDef - Constraints []*Constraint - Options []*TableOption - Partition *PartitionOptions - OnDuplicate OnDuplicateKeyHandlingType - Select ResultSetNode -} - -// Restore implements Node interface. -func (n *CreateTableStmt) Restore(ctx *format.RestoreCtx) error { - switch n.TemporaryKeyword { - case TemporaryNone: - ctx.WriteKeyWord("CREATE TABLE ") - case TemporaryGlobal: - ctx.WriteKeyWord("CREATE GLOBAL TEMPORARY TABLE ") - case TemporaryLocal: - ctx.WriteKeyWord("CREATE TEMPORARY TABLE ") - } - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Table") - } - - if n.ReferTable != nil { - ctx.WriteKeyWord(" LIKE ") - if err := n.ReferTable.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing CreateTableStmt ReferTable") - } - } - lenCols := len(n.Cols) - lenConstraints := len(n.Constraints) - if lenCols+lenConstraints > 0 { - ctx.WritePlain(" (") - for i, col := range n.Cols { - if i > 0 { - ctx.WritePlain(",") - } - if err := col.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt ColumnDef: [%v]", i) - } - } - for i, constraint := range n.Constraints { - if i > 0 || lenCols >= 1 { - ctx.WritePlain(",") - } - if err := constraint.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt Constraints: [%v]", i) - } - } - ctx.WritePlain(")") - } - - options := tableOptionsWithRestoreTTLFlag(ctx.Flags, n.Options) - for i, option := range options { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt TableOption: [%v]", i) - } - } - - if n.Partition != nil { - ctx.WritePlain(" ") - if err := n.Partition.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Partition") - } - } - - if n.Select != nil { - switch n.OnDuplicate { - case OnDuplicateKeyHandlingError: - ctx.WriteKeyWord(" AS ") - case OnDuplicateKeyHandlingIgnore: - ctx.WriteKeyWord(" IGNORE AS ") - case OnDuplicateKeyHandlingReplace: - ctx.WriteKeyWord(" REPLACE AS ") - } - - if err := n.Select.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Select") - } - } - - if n.TemporaryKeyword == TemporaryGlobal { - if n.OnCommitDelete { - ctx.WriteKeyWord(" ON COMMIT DELETE ROWS") - } else { - ctx.WriteKeyWord(" ON COMMIT PRESERVE ROWS") - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateTableStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - if n.ReferTable != nil { - node, ok = n.ReferTable.Accept(v) - if !ok { - return n, false - } - n.ReferTable = node.(*TableName) - } - for i, val := range n.Cols { - node, ok = val.Accept(v) - if !ok { - return n, false - } - n.Cols[i] = node.(*ColumnDef) - } - for i, val := range n.Constraints { - node, ok = val.Accept(v) - if !ok { - return n, false - } - n.Constraints[i] = node.(*Constraint) - } - if n.Select != nil { - node, ok := n.Select.Accept(v) - if !ok { - return n, false - } - n.Select = node.(ResultSetNode) - } - if n.Partition != nil { - node, ok := n.Partition.Accept(v) - if !ok { - return n, false - } - n.Partition = node.(*PartitionOptions) - } - for i, option := range n.Options { - node, ok = option.Accept(v) - if !ok { - return n, false - } - n.Options[i] = node.(*TableOption) - } - - return v.Leave(n) -} - -// DropTableStmt is a statement to drop one or more tables. -// See https://dev.mysql.com/doc/refman/5.7/en/drop-table.html -type DropTableStmt struct { - ddlNode - - IfExists bool - Tables []*TableName - IsView bool - TemporaryKeyword // make sense ONLY if/when IsView == false -} - -// Restore implements Node interface. -func (n *DropTableStmt) Restore(ctx *format.RestoreCtx) error { - if n.IsView { - ctx.WriteKeyWord("DROP VIEW ") - } else { - switch n.TemporaryKeyword { - case TemporaryNone: - ctx.WriteKeyWord("DROP TABLE ") - case TemporaryGlobal: - ctx.WriteKeyWord("DROP GLOBAL TEMPORARY TABLE ") - case TemporaryLocal: - ctx.WriteKeyWord("DROP TEMPORARY TABLE ") - } - } - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index) - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *DropTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropTableStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} - -// DropPlacementPolicyStmt is a statement to drop a Policy. -type DropPlacementPolicyStmt struct { - ddlNode - - IfExists bool - PolicyName model.CIStr -} - -// Restore implements Restore interface. -func (n *DropPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restorePlacementStmtInSpecialComment(ctx, n) - } - - ctx.WriteKeyWord("DROP PLACEMENT POLICY ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.PolicyName.O) - return nil -} - -func (n *DropPlacementPolicyStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropPlacementPolicyStmt) - return v.Leave(n) -} - -type DropResourceGroupStmt struct { - ddlNode - - IfExists bool - ResourceGroupName model.CIStr -} - -// Restore implements Restore interface. -func (n *DropResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) - } - - ctx.WriteKeyWord("DROP RESOURCE GROUP ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.ResourceGroupName.O) - return nil -} - -func (n *DropResourceGroupStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropResourceGroupStmt) - return v.Leave(n) -} - -// DropSequenceStmt is a statement to drop a Sequence. -type DropSequenceStmt struct { - ddlNode - - IfExists bool - Sequences []*TableName -} - -// Restore implements Node interface. -func (n *DropSequenceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP SEQUENCE ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - for i, sequence := range n.Sequences { - if i != 0 { - ctx.WritePlain(", ") - } - if err := sequence.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DropSequenceStmt.Sequences[%d]", i) - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *DropSequenceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropSequenceStmt) - for i, val := range n.Sequences { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Sequences[i] = node.(*TableName) - } - return v.Leave(n) -} - -// RenameTableStmt is a statement to rename a table. -// See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html -type RenameTableStmt struct { - ddlNode - - TableToTables []*TableToTable -} - -// Restore implements Node interface. -func (n *RenameTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("RENAME TABLE ") - for index, table2table := range n.TableToTables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table2table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore RenameTableStmt.TableToTables") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RenameTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RenameTableStmt) - - for i, t := range n.TableToTables { - node, ok := t.Accept(v) - if !ok { - return n, false - } - n.TableToTables[i] = node.(*TableToTable) - } - - return v.Leave(n) -} - -// TableToTable represents renaming old table to new table used in RenameTableStmt. -type TableToTable struct { - node - OldTable *TableName - NewTable *TableName -} - -// Restore implements Node interface. -func (n *TableToTable) Restore(ctx *format.RestoreCtx) error { - if err := n.OldTable.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore TableToTable.OldTable") - } - ctx.WriteKeyWord(" TO ") - if err := n.NewTable.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore TableToTable.NewTable") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *TableToTable) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*TableToTable) - node, ok := n.OldTable.Accept(v) - if !ok { - return n, false - } - n.OldTable = node.(*TableName) - node, ok = n.NewTable.Accept(v) - if !ok { - return n, false - } - n.NewTable = node.(*TableName) - return v.Leave(n) -} - -// CreateViewStmt is a statement to create a View. -// See https://dev.mysql.com/doc/refman/5.7/en/create-view.html -type CreateViewStmt struct { - ddlNode - - OrReplace bool - ViewName *TableName - Cols []model.CIStr - Select StmtNode - SchemaCols []model.CIStr - Algorithm model.ViewAlgorithm - Definer *auth.UserIdentity - Security model.ViewSecurity - CheckOption model.ViewCheckOption -} - -// Restore implements Node interface. -func (n *CreateViewStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE ") - if n.OrReplace { - ctx.WriteKeyWord("OR REPLACE ") - } - ctx.WriteKeyWord("ALGORITHM") - ctx.WritePlain(" = ") - ctx.WriteKeyWord(n.Algorithm.String()) - ctx.WriteKeyWord(" DEFINER") - ctx.WritePlain(" = ") - - // todo Use n.Definer.Restore(ctx) to replace this part - if n.Definer.CurrentUser { - ctx.WriteKeyWord("current_user") - } else { - ctx.WriteName(n.Definer.Username) - if n.Definer.Hostname != "" { - ctx.WritePlain("@") - ctx.WriteName(n.Definer.Hostname) - } - } - - ctx.WriteKeyWord(" SQL SECURITY ") - ctx.WriteKeyWord(n.Security.String()) - ctx.WriteKeyWord(" VIEW ") - - if err := n.ViewName.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while create CreateViewStmt.ViewName") - } - - for i, col := range n.Cols { - if i == 0 { - ctx.WritePlain(" (") - } else { - ctx.WritePlain(",") - } - ctx.WriteName(col.O) - if i == len(n.Cols)-1 { - ctx.WritePlain(")") - } - } - - ctx.WriteKeyWord(" AS ") - - if err := n.Select.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while create CreateViewStmt.Select") - } - - if n.CheckOption != model.CheckOptionCascaded { - ctx.WriteKeyWord(" WITH ") - ctx.WriteKeyWord(n.CheckOption.String()) - ctx.WriteKeyWord(" CHECK OPTION") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateViewStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateViewStmt) - node, ok := n.ViewName.Accept(v) - if !ok { - return n, false - } - n.ViewName = node.(*TableName) - selnode, ok := n.Select.Accept(v) - if !ok { - return n, false - } - n.Select = selnode.(StmtNode) - return v.Leave(n) -} - -// CreatePlacementPolicyStmt is a statement to create a policy. -type CreatePlacementPolicyStmt struct { - ddlNode - - OrReplace bool - IfNotExists bool - PolicyName model.CIStr - PlacementOptions []*PlacementOption -} - -// Restore implements Node interface. -func (n *CreatePlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restorePlacementStmtInSpecialComment(ctx, n) - } - - ctx.WriteKeyWord("CREATE ") - if n.OrReplace { - ctx.WriteKeyWord("OR REPLACE ") - } - ctx.WriteKeyWord("PLACEMENT POLICY ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.PolicyName.O) - for i, option := range n.PlacementOptions { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreatePlacementPolicy TableOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CreatePlacementPolicyStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreatePlacementPolicyStmt) - return v.Leave(n) -} - -// CreateResourceGroupStmt is a statement to create a policy. -type CreateResourceGroupStmt struct { - ddlNode - - IfNotExists bool - ResourceGroupName model.CIStr - ResourceGroupOptionList []*ResourceGroupOption -} - -// Restore implements Node interface. -func (n *CreateResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) - } - - ctx.WriteKeyWord("CREATE ") - - ctx.WriteKeyWord("RESOURCE GROUP ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.ResourceGroupName.O) - for i, option := range n.ResourceGroupOptionList { - if i > 0 { - ctx.WritePlain(",") - } - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateResourceGroupStmt Option: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateResourceGroupStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateResourceGroupStmt) - return v.Leave(n) -} - -// CreateSequenceStmt is a statement to create a Sequence. -type CreateSequenceStmt struct { - ddlNode - - // TODO : support or replace if need : care for it will conflict on temporaryOpt. - IfNotExists bool - Name *TableName - SeqOptions []*SequenceOption - TblOptions []*TableOption -} - -// Restore implements Node interface. -func (n *CreateSequenceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE ") - ctx.WriteKeyWord("SEQUENCE ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - if err := n.Name.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while create CreateSequenceStmt.Name") - } - for i, option := range n.SeqOptions { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt SequenceOption: [%v]", i) - } - } - for i, option := range n.TblOptions { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt TableOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateSequenceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateSequenceStmt) - node, ok := n.Name.Accept(v) - if !ok { - return n, false - } - n.Name = node.(*TableName) - return v.Leave(n) -} - -// IndexLockAndAlgorithm stores the algorithm option and the lock option. -type IndexLockAndAlgorithm struct { - node - - LockTp LockType - AlgorithmTp AlgorithmType -} - -// Restore implements Node interface. -func (n *IndexLockAndAlgorithm) Restore(ctx *format.RestoreCtx) error { - hasPrevOption := false - if n.AlgorithmTp != AlgorithmTypeDefault { - ctx.WriteKeyWord("ALGORITHM") - ctx.WritePlain(" = ") - ctx.WriteKeyWord(n.AlgorithmTp.String()) - hasPrevOption = true - } - - if n.LockTp != LockTypeDefault { - if hasPrevOption { - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("LOCK") - ctx.WritePlain(" = ") - ctx.WriteKeyWord(n.LockTp.String()) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *IndexLockAndAlgorithm) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*IndexLockAndAlgorithm) - return v.Leave(n) -} - -// IndexKeyType is the type for index key. -type IndexKeyType int - -// Index key types. -const ( - IndexKeyTypeNone IndexKeyType = iota - IndexKeyTypeUnique - IndexKeyTypeSpatial - IndexKeyTypeFullText -) - -// CreateIndexStmt is a statement to create an index. -// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html -type CreateIndexStmt struct { - ddlNode - - // only supported by MariaDB 10.0.2+, - // see https://mariadb.com/kb/en/library/create-index/ - IfNotExists bool - - IndexName string - Table *TableName - IndexPartSpecifications []*IndexPartSpecification - IndexOption *IndexOption - KeyType IndexKeyType - LockAlg *IndexLockAndAlgorithm -} - -// Restore implements Node interface. -func (n *CreateIndexStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE ") - switch n.KeyType { - case IndexKeyTypeUnique: - ctx.WriteKeyWord("UNIQUE ") - case IndexKeyTypeSpatial: - ctx.WriteKeyWord("SPATIAL ") - case IndexKeyTypeFullText: - ctx.WriteKeyWord("FULLTEXT ") - } - ctx.WriteKeyWord("INDEX ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.IndexName) - ctx.WriteKeyWord(" ON ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.Table") - } - - ctx.WritePlain(" (") - for i, indexColName := range n.IndexPartSpecifications { - if i != 0 { - ctx.WritePlain(", ") - } - if err := indexColName.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateIndexStmt.IndexPartSpecifications: [%v]", i) - } - } - ctx.WritePlain(")") - - if n.IndexOption.Tp != model.IndexTypeInvalid || n.IndexOption.KeyBlockSize > 0 || n.IndexOption.Comment != "" || len(n.IndexOption.ParserName.O) > 0 || n.IndexOption.Visibility != IndexVisibilityDefault { - ctx.WritePlain(" ") - if err := n.IndexOption.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.IndexOption") - } - } - - if n.LockAlg != nil { - ctx.WritePlain(" ") - if err := n.LockAlg.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg") - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateIndexStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateIndexStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - for i, val := range n.IndexPartSpecifications { - node, ok = val.Accept(v) - if !ok { - return n, false - } - n.IndexPartSpecifications[i] = node.(*IndexPartSpecification) - } - if n.IndexOption != nil { - node, ok := n.IndexOption.Accept(v) - if !ok { - return n, false - } - n.IndexOption = node.(*IndexOption) - } - if n.LockAlg != nil { - node, ok := n.LockAlg.Accept(v) - if !ok { - return n, false - } - n.LockAlg = node.(*IndexLockAndAlgorithm) - } - return v.Leave(n) -} - -// DropIndexStmt is a statement to drop the index. -// See https://dev.mysql.com/doc/refman/5.7/en/drop-index.html -type DropIndexStmt struct { - ddlNode - - IfExists bool - IndexName string - Table *TableName - LockAlg *IndexLockAndAlgorithm - IsHypo bool // whether this operation is for a hypothetical index. -} - -// Restore implements Node interface. -func (n *DropIndexStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP INDEX ") - if n.IfExists { - _ = ctx.WriteWithSpecialComments("", func() error { - ctx.WriteKeyWord("IF EXISTS ") - return nil - }) - } - ctx.WriteName(n.IndexName) - ctx.WriteKeyWord(" ON ") - - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while add index") - } - - if n.LockAlg != nil { - ctx.WritePlain(" ") - if err := n.LockAlg.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg") - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *DropIndexStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropIndexStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - if n.LockAlg != nil { - node, ok := n.LockAlg.Accept(v) - if !ok { - return n, false - } - n.LockAlg = node.(*IndexLockAndAlgorithm) - } - return v.Leave(n) -} - -// LockTablesStmt is a statement to lock tables. -type LockTablesStmt struct { - ddlNode - - TableLocks []TableLock -} - -// TableLock contains the table name and lock type. -type TableLock struct { - Table *TableName - Type model.TableLockType -} - -// Accept implements Node Accept interface. -func (n *LockTablesStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*LockTablesStmt) - for i := range n.TableLocks { - node, ok := n.TableLocks[i].Table.Accept(v) - if !ok { - return n, false - } - n.TableLocks[i].Table = node.(*TableName) - } - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *LockTablesStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("LOCK TABLES ") - for i, tl := range n.TableLocks { - if i != 0 { - ctx.WritePlain(", ") - } - if err := tl.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while add index") - } - ctx.WriteKeyWord(" " + tl.Type.String()) - } - return nil -} - -// UnlockTablesStmt is a statement to unlock tables. -type UnlockTablesStmt struct { - ddlNode -} - -// Accept implements Node Accept interface. -func (n *UnlockTablesStmt) Accept(v Visitor) (Node, bool) { - _, _ = v.Enter(n) - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *UnlockTablesStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("UNLOCK TABLES") - return nil -} - -// CleanupTableLockStmt is a statement to cleanup table lock. -type CleanupTableLockStmt struct { - ddlNode - - Tables []*TableName -} - -// Accept implements Node Accept interface. -func (n *CleanupTableLockStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CleanupTableLockStmt) - for i := range n.Tables { - node, ok := n.Tables[i].Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *CleanupTableLockStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ADMIN CLEANUP TABLE LOCK ") - for i, v := range n.Tables { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CleanupTableLockStmt.Tables[%d]", i) - } - } - return nil -} - -// RepairTableStmt is a statement to repair tableInfo. -type RepairTableStmt struct { - ddlNode - Table *TableName - CreateStmt *CreateTableStmt -} - -// Accept implements Node Accept interface. -func (n *RepairTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RepairTableStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - node, ok = n.CreateStmt.Accept(v) - if !ok { - return n, false - } - n.CreateStmt = node.(*CreateTableStmt) - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *RepairTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ADMIN REPAIR TABLE ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.table : [%v]", n.Table) - } - ctx.WritePlain(" ") - if err := n.CreateStmt.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.createStmt : [%v]", n.CreateStmt) - } - return nil -} - -// PlacementOptionType is the type for PlacementOption -type PlacementOptionType int - -// PlacementOption types. -const ( - PlacementOptionPrimaryRegion PlacementOptionType = 0x3000 + iota - PlacementOptionRegions - PlacementOptionFollowerCount - PlacementOptionVoterCount - PlacementOptionLearnerCount - PlacementOptionSchedule - PlacementOptionConstraints - PlacementOptionLeaderConstraints - PlacementOptionLearnerConstraints - PlacementOptionFollowerConstraints - PlacementOptionVoterConstraints - PlacementOptionSurvivalPreferences - PlacementOptionPolicy -) - -// PlacementOption is used for parsing placement option. -type PlacementOption struct { - Tp PlacementOptionType - StrValue string - UintValue uint64 -} - -func (n *PlacementOption) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { - return nil - } - fn := func() error { - switch n.Tp { - case PlacementOptionPrimaryRegion: - ctx.WriteKeyWord("PRIMARY_REGION ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionRegions: - ctx.WriteKeyWord("REGIONS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionFollowerCount: - ctx.WriteKeyWord("FOLLOWERS ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case PlacementOptionVoterCount: - ctx.WriteKeyWord("VOTERS ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case PlacementOptionLearnerCount: - ctx.WriteKeyWord("LEARNERS ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case PlacementOptionSchedule: - ctx.WriteKeyWord("SCHEDULE ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionConstraints: - ctx.WriteKeyWord("CONSTRAINTS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionLeaderConstraints: - ctx.WriteKeyWord("LEADER_CONSTRAINTS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionFollowerConstraints: - ctx.WriteKeyWord("FOLLOWER_CONSTRAINTS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionVoterConstraints: - ctx.WriteKeyWord("VOTER_CONSTRAINTS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionLearnerConstraints: - ctx.WriteKeyWord("LEARNER_CONSTRAINTS ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case PlacementOptionPolicy: - ctx.WriteKeyWord("PLACEMENT POLICY ") - ctx.WritePlain("= ") - ctx.WriteName(n.StrValue) - case PlacementOptionSurvivalPreferences: - ctx.WriteKeyWord("SURVIVAL_PREFERENCES ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - default: - return errors.Errorf("invalid PlacementOption: %d", n.Tp) - } - return nil - } - // WriteSpecialComment - return ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, fn) -} - -// ResourceGroupOption is used for parsing resource group option. -type ResourceGroupOption struct { - Tp ResourceUnitType - StrValue string - UintValue uint64 - BoolValue bool - RunawayOptionList []*ResourceGroupRunawayOption - BackgroundOptions []*ResourceGroupBackgroundOption -} - -type ResourceUnitType int - -const ( - // RU mode - ResourceRURate ResourceUnitType = iota - ResourcePriority - // Raw mode - ResourceUnitCPU - ResourceUnitIOReadBandwidth - ResourceUnitIOWriteBandwidth - - // Options - ResourceBurstableOpiton - ResourceGroupRunaway - ResourceGroupBackground -) - -func (n *ResourceGroupOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case ResourceRURate: - ctx.WriteKeyWord("RU_PER_SEC ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case ResourcePriority: - ctx.WriteKeyWord("PRIORITY ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(model.PriorityValueToName(n.UintValue)) - case ResourceUnitCPU: - ctx.WriteKeyWord("CPU ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case ResourceUnitIOReadBandwidth: - ctx.WriteKeyWord("IO_READ_BANDWIDTH ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case ResourceUnitIOWriteBandwidth: - ctx.WriteKeyWord("IO_WRITE_BANDWIDTH ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case ResourceBurstableOpiton: - ctx.WriteKeyWord("BURSTABLE ") - ctx.WritePlain("= ") - ctx.WritePlain(strings.ToUpper(fmt.Sprintf("%v", n.BoolValue))) - case ResourceGroupRunaway: - ctx.WritePlain("QUERY_LIMIT ") - ctx.WritePlain("= ") - if len(n.RunawayOptionList) > 0 { - ctx.WritePlain("(") - for i, option := range n.RunawayOptionList { - if i > 0 { - ctx.WritePlain(" ") - } - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing ResourceGroupRunaway Option: [%v]", option) - } - } - ctx.WritePlain(")") - } else { - ctx.WritePlain("NULL") - } - case ResourceGroupBackground: - ctx.WritePlain("BACKGROUND ") - ctx.WritePlain("= ") - if len(n.BackgroundOptions) > 0 { - ctx.WritePlain("(") - for i, option := range n.BackgroundOptions { - if i > 0 { - ctx.WritePlain(", ") - } - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing ResourceGroup Background Option: [%v]", option) - } - } - ctx.WritePlain(")") - } else { - ctx.WritePlain("NULL") - } - default: - return errors.Errorf("invalid ResourceGroupOption: %d", n.Tp) - } - return nil -} - -type RunawayOptionType int - -const ( - RunawayRule RunawayOptionType = iota - RunawayAction - RunawayWatch -) - -// ResourceGroupRunawayOption is used for parsing resource group runaway rule option. -type ResourceGroupRunawayOption struct { - Tp RunawayOptionType - StrValue string - IntValue int32 -} - -func (n *ResourceGroupRunawayOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case RunawayRule: - ctx.WriteKeyWord("EXEC_ELAPSED ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case RunawayAction: - ctx.WriteKeyWord("ACTION ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(model.RunawayActionType(n.IntValue).String()) - case RunawayWatch: - ctx.WriteKeyWord("WATCH ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(model.RunawayWatchType(n.IntValue).String()) - ctx.WritePlain(" ") - ctx.WriteKeyWord("DURATION ") - ctx.WritePlain("= ") - if len(n.StrValue) > 0 { - ctx.WriteString(n.StrValue) - } else { - ctx.WriteKeyWord("UNLIMITED") - } - default: - return errors.Errorf("invalid ResourceGroupRunawayOption: %d", n.Tp) - } - return nil -} - -type BackgroundOptionType int - -const ( - BackgroundOptionNone BackgroundOptionType = iota - BackgroundOptionTaskNames -) - -// ResourceGroupBackgroundOption is used to config background job settings. -type ResourceGroupBackgroundOption struct { - Type BackgroundOptionType - StrValue string -} - -func (n *ResourceGroupBackgroundOption) Restore(ctx *format.RestoreCtx) error { - switch n.Type { - case BackgroundOptionTaskNames: - ctx.WriteKeyWord("TASK_TYPES ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - default: - return errors.Errorf("unknown ResourceGroupBackgroundOption: %d", n.Type) - } - - return nil -} - -type StatsOptionType int - -const ( - StatsOptionBuckets StatsOptionType = 0x5000 + iota - StatsOptionTopN - StatsOptionColsChoice - StatsOptionColList - StatsOptionSampleRate -) - -// TableOptionType is the type for TableOption -type TableOptionType int - -// TableOption types. -const ( - TableOptionNone TableOptionType = iota - TableOptionEngine - TableOptionCharset - TableOptionCollate - TableOptionAutoIdCache //nolint:revive - TableOptionAutoIncrement - TableOptionAutoRandomBase - TableOptionComment - TableOptionAvgRowLength - TableOptionCheckSum - TableOptionCompression - TableOptionConnection - TableOptionPassword - TableOptionKeyBlockSize - TableOptionMaxRows - TableOptionMinRows - TableOptionDelayKeyWrite - TableOptionRowFormat - TableOptionStatsPersistent - TableOptionStatsAutoRecalc - TableOptionShardRowID - TableOptionPreSplitRegion - TableOptionPackKeys - TableOptionTablespace - TableOptionNodegroup - TableOptionDataDirectory - TableOptionIndexDirectory - TableOptionStorageMedia - TableOptionStatsSamplePages - TableOptionSecondaryEngine - TableOptionSecondaryEngineNull - TableOptionInsertMethod - TableOptionTableCheckSum - TableOptionUnion - TableOptionEncryption - TableOptionTTL - TableOptionTTLEnable - TableOptionTTLJobInterval - TableOptionPlacementPolicy = TableOptionType(PlacementOptionPolicy) - TableOptionStatsBuckets = TableOptionType(StatsOptionBuckets) - TableOptionStatsTopN = TableOptionType(StatsOptionTopN) - TableOptionStatsColsChoice = TableOptionType(StatsOptionColsChoice) - TableOptionStatsColList = TableOptionType(StatsOptionColList) - TableOptionStatsSampleRate = TableOptionType(StatsOptionSampleRate) -) - -// RowFormat types -const ( - RowFormatDefault uint64 = iota + 1 - RowFormatDynamic - RowFormatFixed - RowFormatCompressed - RowFormatRedundant - RowFormatCompact - TokuDBRowFormatDefault - TokuDBRowFormatFast - TokuDBRowFormatSmall - TokuDBRowFormatZlib - TokuDBRowFormatQuickLZ - TokuDBRowFormatLzma - TokuDBRowFormatSnappy - TokuDBRowFormatUncompressed - TokuDBRowFormatZstd -) - -// OnDuplicateKeyHandlingType is the option that handle unique key values in 'CREATE TABLE ... SELECT' or `LOAD DATA`. -// See https://dev.mysql.com/doc/refman/5.7/en/create-table-select.html -// See https://dev.mysql.com/doc/refman/5.7/en/load-data.html -type OnDuplicateKeyHandlingType int - -// OnDuplicateKeyHandling types -const ( - OnDuplicateKeyHandlingError OnDuplicateKeyHandlingType = iota - OnDuplicateKeyHandlingIgnore - OnDuplicateKeyHandlingReplace -) - -const ( - TableOptionCharsetWithoutConvertTo uint64 = 0 - TableOptionCharsetWithConvertTo uint64 = 1 -) - -// TableOption is used for parsing table option from SQL. -type TableOption struct { - node - Tp TableOptionType - Default bool - StrValue string - UintValue uint64 - BoolValue bool - TimeUnitValue *TimeUnitExpr - Value ValueExpr - TableNames []*TableName - ColumnName *ColumnName -} - -func (n *TableOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case TableOptionEngine: - ctx.WriteKeyWord("ENGINE ") - ctx.WritePlain("= ") - if n.StrValue != "" { - ctx.WritePlain(n.StrValue) - } else { - ctx.WritePlain("''") - } - case TableOptionCharset: - if n.UintValue == TableOptionCharsetWithConvertTo { - ctx.WriteKeyWord("CONVERT TO ") - } else { - ctx.WriteKeyWord("DEFAULT ") - } - ctx.WriteKeyWord("CHARACTER SET ") - if n.UintValue == TableOptionCharsetWithoutConvertTo { - ctx.WriteKeyWord("= ") - } - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WriteKeyWord(n.StrValue) - } - case TableOptionCollate: - ctx.WriteKeyWord("DEFAULT COLLATE ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(n.StrValue) - case TableOptionAutoIncrement: - if n.BoolValue { - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDForceAutoInc, func() error { - ctx.WriteKeyWord("FORCE") - return nil - }) - ctx.WritePlain(" ") - } - ctx.WriteKeyWord("AUTO_INCREMENT ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionAutoIdCache: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoIDCache, func() error { - ctx.WriteKeyWord("AUTO_ID_CACHE ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - return nil - }) - case TableOptionAutoRandomBase: - if n.BoolValue { - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDForceAutoInc, func() error { - ctx.WriteKeyWord("FORCE") - return nil - }) - ctx.WritePlain(" ") - } - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandomBase, func() error { - ctx.WriteKeyWord("AUTO_RANDOM_BASE ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - return nil - }) - case TableOptionComment: - ctx.WriteKeyWord("COMMENT ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionAvgRowLength: - ctx.WriteKeyWord("AVG_ROW_LENGTH ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionCheckSum: - ctx.WriteKeyWord("CHECKSUM ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionCompression: - ctx.WriteKeyWord("COMPRESSION ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionConnection: - ctx.WriteKeyWord("CONNECTION ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionPassword: - ctx.WriteKeyWord("PASSWORD ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionKeyBlockSize: - ctx.WriteKeyWord("KEY_BLOCK_SIZE ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionMaxRows: - ctx.WriteKeyWord("MAX_ROWS ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionMinRows: - ctx.WriteKeyWord("MIN_ROWS ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionDelayKeyWrite: - ctx.WriteKeyWord("DELAY_KEY_WRITE ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionRowFormat: - ctx.WriteKeyWord("ROW_FORMAT ") - ctx.WritePlain("= ") - switch n.UintValue { - case RowFormatDefault: - ctx.WriteKeyWord("DEFAULT") - case RowFormatDynamic: - ctx.WriteKeyWord("DYNAMIC") - case RowFormatFixed: - ctx.WriteKeyWord("FIXED") - case RowFormatCompressed: - ctx.WriteKeyWord("COMPRESSED") - case RowFormatRedundant: - ctx.WriteKeyWord("REDUNDANT") - case RowFormatCompact: - ctx.WriteKeyWord("COMPACT") - case TokuDBRowFormatDefault: - ctx.WriteKeyWord("TOKUDB_DEFAULT") - case TokuDBRowFormatFast: - ctx.WriteKeyWord("TOKUDB_FAST") - case TokuDBRowFormatSmall: - ctx.WriteKeyWord("TOKUDB_SMALL") - case TokuDBRowFormatZlib: - ctx.WriteKeyWord("TOKUDB_ZLIB") - case TokuDBRowFormatQuickLZ: - ctx.WriteKeyWord("TOKUDB_QUICKLZ") - case TokuDBRowFormatLzma: - ctx.WriteKeyWord("TOKUDB_LZMA") - case TokuDBRowFormatSnappy: - ctx.WriteKeyWord("TOKUDB_SNAPPY") - case TokuDBRowFormatZstd: - ctx.WriteKeyWord("TOKUDB_ZSTD") - case TokuDBRowFormatUncompressed: - ctx.WriteKeyWord("TOKUDB_UNCOMPRESSED") - default: - return errors.Errorf("invalid TableOption: TableOptionRowFormat: %d", n.UintValue) - } - case TableOptionStatsPersistent: - // TODO: not support - ctx.WriteKeyWord("STATS_PERSISTENT ") - ctx.WritePlain("= ") - ctx.WriteKeyWord("DEFAULT") - ctx.WritePlain(" /* TableOptionStatsPersistent is not supported */ ") - case TableOptionStatsAutoRecalc: - ctx.WriteKeyWord("STATS_AUTO_RECALC ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WritePlainf("%d", n.UintValue) - } - case TableOptionShardRowID: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error { - ctx.WriteKeyWord("SHARD_ROW_ID_BITS ") - ctx.WritePlainf("= %d", n.UintValue) - return nil - }) - case TableOptionPreSplitRegion: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error { - ctx.WriteKeyWord("PRE_SPLIT_REGIONS ") - ctx.WritePlainf("= %d", n.UintValue) - return nil - }) - case TableOptionPackKeys: - // TODO: not support - ctx.WriteKeyWord("PACK_KEYS ") - ctx.WritePlain("= ") - ctx.WriteKeyWord("DEFAULT") - ctx.WritePlain(" /* TableOptionPackKeys is not supported */ ") - case TableOptionTablespace: - ctx.WriteKeyWord("TABLESPACE ") - ctx.WritePlain("= ") - ctx.WriteName(n.StrValue) - case TableOptionNodegroup: - ctx.WriteKeyWord("NODEGROUP ") - ctx.WritePlainf("= %d", n.UintValue) - case TableOptionDataDirectory: - ctx.WriteKeyWord("DATA DIRECTORY ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionIndexDirectory: - ctx.WriteKeyWord("INDEX DIRECTORY ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionStorageMedia: - ctx.WriteKeyWord("STORAGE ") - ctx.WriteKeyWord(n.StrValue) - case TableOptionStatsSamplePages: - ctx.WriteKeyWord("STATS_SAMPLE_PAGES ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WritePlainf("%d", n.UintValue) - } - case TableOptionSecondaryEngine: - ctx.WriteKeyWord("SECONDARY_ENGINE ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionSecondaryEngineNull: - ctx.WriteKeyWord("SECONDARY_ENGINE ") - ctx.WritePlain("= ") - ctx.WriteKeyWord("NULL") - case TableOptionInsertMethod: - ctx.WriteKeyWord("INSERT_METHOD ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(n.StrValue) - case TableOptionTableCheckSum: - ctx.WriteKeyWord("TABLE_CHECKSUM ") - ctx.WritePlain("= ") - ctx.WritePlainf("%d", n.UintValue) - case TableOptionUnion: - ctx.WriteKeyWord("UNION ") - ctx.WritePlain("= (") - for i, tableName := range n.TableNames { - if i != 0 { - ctx.WritePlain(",") - } - tableName.Restore(ctx) - } - ctx.WritePlain(")") - case TableOptionEncryption: - ctx.WriteKeyWord("ENCRYPTION ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - case TableOptionPlacementPolicy: - if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { - return nil - } - placementOpt := PlacementOption{ - Tp: PlacementOptionPolicy, - UintValue: n.UintValue, - StrValue: n.StrValue, - } - return placementOpt.Restore(ctx) - case TableOptionStatsBuckets: - ctx.WriteKeyWord("STATS_BUCKETS ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WritePlainf("%d", n.UintValue) - } - case TableOptionStatsTopN: - ctx.WriteKeyWord("STATS_TOPN ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WritePlainf("%d", n.UintValue) - } - case TableOptionStatsSampleRate: - ctx.WriteKeyWord("STATS_SAMPLE_RATE ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WritePlainf("%v", n.Value.GetValue()) - } - case TableOptionStatsColsChoice: - ctx.WriteKeyWord("STATS_COL_CHOICE ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WriteString(n.StrValue) - } - case TableOptionStatsColList: - ctx.WriteKeyWord("STATS_COL_LIST ") - ctx.WritePlain("= ") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WriteString(n.StrValue) - } - case TableOptionTTL: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { - ctx.WriteKeyWord("TTL ") - ctx.WritePlain("= ") - ctx.WriteName(n.ColumnName.Name.String()) - ctx.WritePlain(" + INTERVAL ") - err := n.Value.Restore(ctx) - ctx.WritePlain(" ") - if err != nil { - return err - } - return n.TimeUnitValue.Restore(ctx) - }) - case TableOptionTTLEnable: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { - ctx.WriteKeyWord("TTL_ENABLE ") - ctx.WritePlain("= ") - if n.BoolValue { - ctx.WriteString("ON") - } else { - ctx.WriteString("OFF") - } - return nil - }) - case TableOptionTTLJobInterval: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { - ctx.WriteKeyWord("TTL_JOB_INTERVAL ") - ctx.WritePlain("= ") - ctx.WriteString(n.StrValue) - return nil - }) - default: - return errors.Errorf("invalid TableOption: %d", n.Tp) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *TableOption) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*TableOption) - if n.Value != nil { - node, ok := n.Value.Accept(v) - if !ok { - return n, false - } - n.Value = node.(ValueExpr) - } - if n.TimeUnitValue != nil { - node, ok := n.TimeUnitValue.Accept(v) - if !ok { - return n, false - } - n.TimeUnitValue = node.(*TimeUnitExpr) - } - return v.Leave(n) -} - -// SequenceOptionType is the type for SequenceOption -type SequenceOptionType int - -// SequenceOption types. -const ( - SequenceOptionNone SequenceOptionType = iota - SequenceOptionIncrementBy - SequenceStartWith - SequenceNoMinValue - SequenceMinValue - SequenceNoMaxValue - SequenceMaxValue - SequenceNoCache - SequenceCache - SequenceNoCycle - SequenceCycle - // SequenceRestart is only used in alter sequence statement. - SequenceRestart - SequenceRestartWith -) - -// SequenceOption is used for parsing sequence option from SQL. -type SequenceOption struct { - Tp SequenceOptionType - IntValue int64 -} - -func (n *SequenceOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case SequenceOptionIncrementBy: - ctx.WriteKeyWord("INCREMENT BY ") - ctx.WritePlainf("%d", n.IntValue) - case SequenceStartWith: - ctx.WriteKeyWord("START WITH ") - ctx.WritePlainf("%d", n.IntValue) - case SequenceNoMinValue: - ctx.WriteKeyWord("NO MINVALUE") - case SequenceMinValue: - ctx.WriteKeyWord("MINVALUE ") - ctx.WritePlainf("%d", n.IntValue) - case SequenceNoMaxValue: - ctx.WriteKeyWord("NO MAXVALUE") - case SequenceMaxValue: - ctx.WriteKeyWord("MAXVALUE ") - ctx.WritePlainf("%d", n.IntValue) - case SequenceNoCache: - ctx.WriteKeyWord("NOCACHE") - case SequenceCache: - ctx.WriteKeyWord("CACHE ") - ctx.WritePlainf("%d", n.IntValue) - case SequenceNoCycle: - ctx.WriteKeyWord("NOCYCLE") - case SequenceCycle: - ctx.WriteKeyWord("CYCLE") - case SequenceRestart: - ctx.WriteKeyWord("RESTART") - case SequenceRestartWith: - ctx.WriteKeyWord("RESTART WITH ") - ctx.WritePlainf("%d", n.IntValue) - default: - return errors.Errorf("invalid SequenceOption: %d", n.Tp) - } - return nil -} - -// ColumnPositionType is the type for ColumnPosition. -type ColumnPositionType int - -// ColumnPosition Types -const ( - ColumnPositionNone ColumnPositionType = iota - ColumnPositionFirst - ColumnPositionAfter -) - -// ColumnPosition represent the position of the newly added column -type ColumnPosition struct { - node - // Tp is either ColumnPositionNone, ColumnPositionFirst or ColumnPositionAfter. - Tp ColumnPositionType - // RelativeColumn is the column the newly added column after if type is ColumnPositionAfter - RelativeColumn *ColumnName -} - -// Restore implements Node interface. -func (n *ColumnPosition) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case ColumnPositionNone: - // do nothing - case ColumnPositionFirst: - ctx.WriteKeyWord("FIRST") - case ColumnPositionAfter: - ctx.WriteKeyWord("AFTER ") - if err := n.RelativeColumn.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore ColumnPosition.RelativeColumn") - } - default: - return errors.Errorf("invalid ColumnPositionType: %d", n.Tp) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ColumnPosition) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ColumnPosition) - if n.RelativeColumn != nil { - node, ok := n.RelativeColumn.Accept(v) - if !ok { - return n, false - } - n.RelativeColumn = node.(*ColumnName) - } - return v.Leave(n) -} - -// AlterTableType is the type for AlterTableSpec. -type AlterTableType int - -// AlterTable types. -const ( - AlterTableOption AlterTableType = iota + 1 - AlterTableAddColumns - AlterTableAddConstraint - AlterTableDropColumn - AlterTableDropPrimaryKey - AlterTableDropIndex - AlterTableDropForeignKey - AlterTableModifyColumn - AlterTableChangeColumn - AlterTableRenameColumn - AlterTableRenameTable - AlterTableAlterColumn - AlterTableLock - AlterTableWriteable - AlterTableAlgorithm - AlterTableRenameIndex - AlterTableForce - AlterTableAddPartitions - // A tombstone for `AlterTableAlterPartition`. It will never be used anymore. - // Just left a tombstone here to keep the enum number unchanged. - __DEPRECATED_AlterTableAlterPartition //nolint:revive - AlterTablePartitionAttributes - AlterTablePartitionOptions - AlterTableCoalescePartitions - AlterTableDropPartition - AlterTableTruncatePartition - AlterTablePartition - AlterTableEnableKeys - AlterTableDisableKeys - AlterTableRemovePartitioning - AlterTableWithValidation - AlterTableWithoutValidation - AlterTableSecondaryLoad - AlterTableSecondaryUnload - AlterTableRebuildPartition - AlterTableReorganizePartition - AlterTableCheckPartitions - AlterTableExchangePartition - AlterTableOptimizePartition - AlterTableRepairPartition - AlterTableImportPartitionTablespace - AlterTableDiscardPartitionTablespace - AlterTableAlterCheck - AlterTableDropCheck - AlterTableImportTablespace - AlterTableDiscardTablespace - AlterTableIndexInvisible - // TODO: Add more actions - AlterTableOrderByColumns - // AlterTableSetTiFlashReplica uses to set the table TiFlash replica. - AlterTableSetTiFlashReplica - // A tombstone for `AlterTablePlacement`. It will never be used anymore. - // Just left a tombstone here to keep the enum number unchanged. - __DEPRECATED_AlterTablePlacement //nolint:revive - AlterTableAddStatistics - AlterTableDropStatistics - AlterTableAttributes - AlterTableCache - AlterTableNoCache - AlterTableStatsOptions - AlterTableDropFirstPartition - AlterTableAddLastPartition - AlterTableReorganizeLastPartition - AlterTableReorganizeFirstPartition - AlterTableRemoveTTL -) - -// LockType is the type for AlterTableSpec. -// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html#alter-table-concurrency -type LockType byte - -func (n LockType) String() string { - switch n { - case LockTypeNone: - return "NONE" - case LockTypeDefault: - return "DEFAULT" - case LockTypeShared: - return "SHARED" - case LockTypeExclusive: - return "EXCLUSIVE" - } - return "" -} - -// Lock Types. -const ( - LockTypeNone LockType = iota + 1 - LockTypeDefault - LockTypeShared - LockTypeExclusive -) - -// AlgorithmType is the algorithm of the DDL operations. -// See https://dev.mysql.com/doc/refman/8.0/en/alter-table.html#alter-table-performance. -type AlgorithmType byte - -// DDL algorithms. -// For now, TiDB only supported inplace and instance algorithms. If the user specify `copy`, -// will get an error. -const ( - AlgorithmTypeDefault AlgorithmType = iota - AlgorithmTypeCopy - AlgorithmTypeInplace - AlgorithmTypeInstant -) - -func (a AlgorithmType) String() string { - switch a { - case AlgorithmTypeDefault: - return "DEFAULT" - case AlgorithmTypeCopy: - return "COPY" - case AlgorithmTypeInplace: - return "INPLACE" - case AlgorithmTypeInstant: - return "INSTANT" - default: - return "DEFAULT" - } -} - -// AlterTableSpec represents alter table specification. -type AlterTableSpec struct { - node - - // only supported by MariaDB 10.0.2+ (DROP COLUMN, CHANGE COLUMN, MODIFY COLUMN, DROP INDEX, DROP FOREIGN KEY, DROP PARTITION) - // see https://mariadb.com/kb/en/library/alter-table/ - IfExists bool - - // only supported by MariaDB 10.0.2+ (ADD COLUMN, ADD PARTITION) - // see https://mariadb.com/kb/en/library/alter-table/ - IfNotExists bool - - NoWriteToBinlog bool - OnAllPartitions bool - - Tp AlterTableType - Name string - IndexName model.CIStr - Constraint *Constraint - Options []*TableOption - OrderByList []*AlterOrderItem - NewTable *TableName - NewColumns []*ColumnDef - NewConstraints []*Constraint - OldColumnName *ColumnName - NewColumnName *ColumnName - Position *ColumnPosition - LockType LockType - Algorithm AlgorithmType - Comment string - FromKey model.CIStr - ToKey model.CIStr - Partition *PartitionOptions - PartitionNames []model.CIStr - PartDefinitions []*PartitionDefinition - WithValidation bool - Num uint64 - Visibility IndexVisibility - TiFlashReplica *TiFlashReplicaSpec - Writeable bool - Statistics *StatisticsSpec - AttributesSpec *AttributesSpec - StatsOptionsSpec *StatsOptionsSpec -} - -type TiFlashReplicaSpec struct { - Count uint64 - Labels []string - Hypo bool // hypothetical replica is used by index advisor -} - -// AlterOrderItem represents an item in order by at alter table stmt. -type AlterOrderItem struct { - node - Column *ColumnName - Desc bool -} - -// Restore implements Node interface. -func (n *AlterOrderItem) Restore(ctx *format.RestoreCtx) error { - if err := n.Column.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterOrderItem.Column") - } - if n.Desc { - ctx.WriteKeyWord(" DESC") - } - return nil -} - -func (n *AlterTableSpec) IsAllPlacementRule() bool { - switch n.Tp { - case AlterTablePartitionAttributes, AlterTablePartitionOptions, AlterTableOption, AlterTableAttributes: - for _, o := range n.Options { - if o.Tp != TableOptionPlacementPolicy { - return false - } - } - return true - default: - return false - } -} - -// Restore implements Node interface. -func (n *AlterTableSpec) Restore(ctx *format.RestoreCtx) error { - if n.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { - return nil - } - switch n.Tp { - case AlterTableSetTiFlashReplica: - ctx.WriteKeyWord("SET TIFLASH REPLICA ") - ctx.WritePlainf("%d", n.TiFlashReplica.Count) - if len(n.TiFlashReplica.Labels) == 0 { - break - } - ctx.WriteKeyWord(" LOCATION LABELS ") - for i, v := range n.TiFlashReplica.Labels { - if i > 0 { - ctx.WritePlain(", ") - } - ctx.WriteString(v) - } - case AlterTableAddStatistics: - ctx.WriteKeyWord("ADD STATS_EXTENDED ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.Statistics.StatsName) - switch n.Statistics.StatsType { - case StatsTypeCardinality: - ctx.WriteKeyWord(" CARDINALITY(") - case StatsTypeDependency: - ctx.WriteKeyWord(" DEPENDENCY(") - case StatsTypeCorrelation: - ctx.WriteKeyWord(" CORRELATION(") - } - for i, col := range n.Statistics.Columns { - if i != 0 { - ctx.WritePlain(", ") - } - if err := col.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AddStatisticsSpec.Columns: [%v]", i) - } - } - ctx.WritePlain(")") - case AlterTableDropStatistics: - ctx.WriteKeyWord("DROP STATS_EXTENDED ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.Statistics.StatsName) - case AlterTableOption: - switch { - case len(n.Options) == 2 && n.Options[0].Tp == TableOptionCharset && n.Options[1].Tp == TableOptionCollate: - if n.Options[0].UintValue == TableOptionCharsetWithConvertTo { - ctx.WriteKeyWord("CONVERT TO ") - } - ctx.WriteKeyWord("CHARACTER SET ") - if n.Options[0].Default { - ctx.WriteKeyWord("DEFAULT") - } else { - ctx.WriteKeyWord(n.Options[0].StrValue) - } - ctx.WriteKeyWord(" COLLATE ") - ctx.WriteKeyWord(n.Options[1].StrValue) - case n.Options[0].Tp == TableOptionCharset && n.Options[0].Default: - if n.Options[0].UintValue == TableOptionCharsetWithConvertTo { - ctx.WriteKeyWord("CONVERT TO ") - } - ctx.WriteKeyWord("CHARACTER SET DEFAULT") - default: - for i, opt := range n.Options { - if i != 0 { - ctx.WritePlain(" ") - } - if err := opt.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d]", i) - } - } - } - case AlterTableAddColumns: - ctx.WriteKeyWord("ADD COLUMN ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - if n.Position != nil && len(n.NewColumns) == 1 { - if err := n.NewColumns[0].Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", 0) - } - if n.Position.Tp != ColumnPositionNone { - ctx.WritePlain(" ") - } - if err := n.Position.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") - } - } else { - lenCols := len(n.NewColumns) - ctx.WritePlain("(") - for i, col := range n.NewColumns { - if i != 0 { - ctx.WritePlain(", ") - } - if err := col.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", i) - } - } - for i, constraint := range n.NewConstraints { - if i != 0 || lenCols >= 1 { - ctx.WritePlain(", ") - } - if err := constraint.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewConstraints[%d]", i) - } - } - ctx.WritePlain(")") - } - case AlterTableAddConstraint: - ctx.WriteKeyWord("ADD ") - if err := n.Constraint.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Constraint") - } - case AlterTableDropColumn: - ctx.WriteKeyWord("DROP COLUMN ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - if err := n.OldColumnName.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") - } - // TODO: RestrictOrCascadeOpt not support - case AlterTableDropPrimaryKey: - ctx.WriteKeyWord("DROP PRIMARY KEY") - case AlterTableDropIndex: - ctx.WriteKeyWord("DROP INDEX ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.Name) - case AlterTableDropForeignKey: - ctx.WriteKeyWord("DROP FOREIGN KEY ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.Name) - case AlterTableModifyColumn: - ctx.WriteKeyWord("MODIFY COLUMN ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - if err := n.NewColumns[0].Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") - } - if n.Position.Tp != ColumnPositionNone { - ctx.WritePlain(" ") - } - if err := n.Position.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") - } - case AlterTableChangeColumn: - ctx.WriteKeyWord("CHANGE COLUMN ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - if err := n.OldColumnName.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") - } - ctx.WritePlain(" ") - if err := n.NewColumns[0].Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") - } - if n.Position.Tp != ColumnPositionNone { - ctx.WritePlain(" ") - } - if err := n.Position.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") - } - case AlterTableRenameColumn: - ctx.WriteKeyWord("RENAME COLUMN ") - if err := n.OldColumnName.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") - } - ctx.WriteKeyWord(" TO ") - if err := n.NewColumnName.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumnName") - } - case AlterTableRenameTable: - ctx.WriteKeyWord("RENAME AS ") - if err := n.NewTable.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewTable") - } - case AlterTableAlterColumn: - ctx.WriteKeyWord("ALTER COLUMN ") - if err := n.NewColumns[0].Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") - } - if len(n.NewColumns[0].Options) == 1 { - ctx.WriteKeyWord("SET DEFAULT ") - expr := n.NewColumns[0].Options[0].Expr - if valueExpr, ok := expr.(ValueExpr); ok { - if err := valueExpr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr") - } - } else { - ctx.WritePlain("(") - if err := expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr") - } - ctx.WritePlain(")") - } - } else { - ctx.WriteKeyWord(" DROP DEFAULT") - } - case AlterTableLock: - ctx.WriteKeyWord("LOCK ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(n.LockType.String()) - case AlterTableWriteable: - ctx.WriteKeyWord("READ ") - if n.Writeable { - ctx.WriteKeyWord("WRITE") - } else { - ctx.WriteKeyWord("ONLY") - } - case AlterTableOrderByColumns: - ctx.WriteKeyWord("ORDER BY ") - for i, alterOrderItem := range n.OrderByList { - if i != 0 { - ctx.WritePlain(",") - } - if err := alterOrderItem.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.OrderByList[%d]", i) - } - } - case AlterTableAlgorithm: - ctx.WriteKeyWord("ALGORITHM ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(n.Algorithm.String()) - case AlterTableRenameIndex: - ctx.WriteKeyWord("RENAME INDEX ") - ctx.WriteName(n.FromKey.O) - ctx.WriteKeyWord(" TO ") - ctx.WriteName(n.ToKey.O) - case AlterTableForce: - // TODO: not support - ctx.WriteKeyWord("FORCE") - ctx.WritePlain(" /* AlterTableForce is not supported */ ") - case AlterTableAddPartitions: - ctx.WriteKeyWord("ADD PARTITION") - if n.IfNotExists { - ctx.WriteKeyWord(" IF NOT EXISTS") - } - if n.NoWriteToBinlog { - ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") - } - if n.PartDefinitions != nil { - ctx.WritePlain(" (") - for i, def := range n.PartDefinitions { - if i != 0 { - ctx.WritePlain(", ") - } - if err := def.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i) - } - } - ctx.WritePlain(")") - } else if n.Num != 0 { - ctx.WriteKeyWord(" PARTITIONS ") - ctx.WritePlainf("%d", n.Num) - } - case AlterTableDropFirstPartition: - ctx.WriteKeyWord("FIRST PARTITION LESS THAN (") - if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableDropFirstPartition Exprs") - } - ctx.WriteKeyWord(")") - if n.NoWriteToBinlog { - ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") - } - case AlterTableAddLastPartition: - ctx.WriteKeyWord("LAST PARTITION LESS THAN (") - if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableAddLastPartition Exprs") - } - ctx.WriteKeyWord(")") - if n.NoWriteToBinlog { - ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") - } - case AlterTablePartitionOptions: - restoreWithoutSpecialComment := func() error { - origFlags := ctx.Flags - defer func() { - ctx.Flags = origFlags - }() - ctx.Flags &= ^format.RestoreTiDBSpecialComment - ctx.WriteKeyWord("PARTITION ") - ctx.WriteName(n.PartitionNames[0].O) - ctx.WritePlain(" ") - - for i, opt := range n.Options { - if i != 0 { - ctx.WritePlain(" ") - } - if err := opt.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d] for PARTITION `%s`", i, n.PartitionNames[0].O) - } - } - return nil - } - - var err error - if ctx.Flags.HasTiDBSpecialCommentFlag() { - // AlterTablePartitionOptions now only supports placement options, so add put all options to special comment - err = ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, restoreWithoutSpecialComment) - } else { - err = restoreWithoutSpecialComment() - } - - if err != nil { - return err - } - case AlterTablePartitionAttributes: - ctx.WriteKeyWord("PARTITION ") - ctx.WriteName(n.PartitionNames[0].O) - ctx.WritePlain(" ") - - spec := n.AttributesSpec - if err := spec.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec") - } - case AlterTableCoalescePartitions: - ctx.WriteKeyWord("COALESCE PARTITION ") - if n.NoWriteToBinlog { - ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") - } - ctx.WritePlainf("%d", n.Num) - case AlterTableDropPartition: - ctx.WriteKeyWord("DROP PARTITION ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableTruncatePartition: - ctx.WriteKeyWord("TRUNCATE PARTITION ") - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableCheckPartitions: - ctx.WriteKeyWord("CHECK PARTITION ") - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableOptimizePartition: - ctx.WriteKeyWord("OPTIMIZE PARTITION ") - if n.NoWriteToBinlog { - ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") - } - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableRepairPartition: - ctx.WriteKeyWord("REPAIR PARTITION ") - if n.NoWriteToBinlog { - ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") - } - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableImportPartitionTablespace: - ctx.WriteKeyWord("IMPORT PARTITION ") - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - } else { - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - } - ctx.WriteKeyWord(" TABLESPACE") - case AlterTableDiscardPartitionTablespace: - ctx.WriteKeyWord("DISCARD PARTITION ") - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - } else { - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - } - ctx.WriteKeyWord(" TABLESPACE") - case AlterTablePartition: - if err := n.Partition.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Partition") - } - case AlterTableEnableKeys: - ctx.WriteKeyWord("ENABLE KEYS") - case AlterTableDisableKeys: - ctx.WriteKeyWord("DISABLE KEYS") - case AlterTableRemovePartitioning: - ctx.WriteKeyWord("REMOVE PARTITIONING") - case AlterTableWithValidation: - ctx.WriteKeyWord("WITH VALIDATION") - case AlterTableWithoutValidation: - ctx.WriteKeyWord("WITHOUT VALIDATION") - case AlterTableRebuildPartition: - ctx.WriteKeyWord("REBUILD PARTITION ") - if n.NoWriteToBinlog { - ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") - } - if n.OnAllPartitions { - ctx.WriteKeyWord("ALL") - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(name.O) - } - case AlterTableReorganizeLastPartition: - ctx.WriteKeyWord("SPLIT MAXVALUE PARTITION LESS THAN (") - if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs") - } - ctx.WriteKeyWord(")") - case AlterTableReorganizeFirstPartition: - ctx.WriteKeyWord("MERGE FIRST PARTITION LESS THAN (") - if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs") - } - ctx.WriteKeyWord(")") - case AlterTableReorganizePartition: - ctx.WriteKeyWord("REORGANIZE PARTITION") - if n.NoWriteToBinlog { - ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") - } - if n.OnAllPartitions { - return nil - } - for i, name := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } else { - ctx.WritePlain(" ") - } - ctx.WriteName(name.O) - } - ctx.WriteKeyWord(" INTO ") - if n.PartDefinitions != nil { - ctx.WritePlain("(") - for i, def := range n.PartDefinitions { - if i != 0 { - ctx.WritePlain(", ") - } - if err := def.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i) - } - } - ctx.WritePlain(")") - } - case AlterTableExchangePartition: - ctx.WriteKeyWord("EXCHANGE PARTITION ") - ctx.WriteName(n.PartitionNames[0].O) - ctx.WriteKeyWord(" WITH TABLE ") - n.NewTable.Restore(ctx) - if !n.WithValidation { - ctx.WriteKeyWord(" WITHOUT VALIDATION") - } - case AlterTableSecondaryLoad: - ctx.WriteKeyWord("SECONDARY_LOAD") - case AlterTableSecondaryUnload: - ctx.WriteKeyWord("SECONDARY_UNLOAD") - case AlterTableAlterCheck: - ctx.WriteKeyWord("ALTER CHECK ") - ctx.WriteName(n.Constraint.Name) - if !n.Constraint.Enforced { - ctx.WriteKeyWord(" NOT") - } - ctx.WriteKeyWord(" ENFORCED") - case AlterTableDropCheck: - ctx.WriteKeyWord("DROP CHECK ") - ctx.WriteName(n.Constraint.Name) - case AlterTableImportTablespace: - ctx.WriteKeyWord("IMPORT TABLESPACE") - case AlterTableDiscardTablespace: - ctx.WriteKeyWord("DISCARD TABLESPACE") - case AlterTableIndexInvisible: - ctx.WriteKeyWord("ALTER INDEX ") - ctx.WriteName(n.IndexName.O) - switch n.Visibility { - case IndexVisibilityVisible: - ctx.WriteKeyWord(" VISIBLE") - case IndexVisibilityInvisible: - ctx.WriteKeyWord(" INVISIBLE") - } - case AlterTableAttributes: - spec := n.AttributesSpec - if err := spec.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec") - } - case AlterTableCache: - ctx.WriteKeyWord("CACHE") - case AlterTableNoCache: - ctx.WriteKeyWord("NOCACHE") - case AlterTableStatsOptions: - spec := n.StatsOptionsSpec - if err := spec.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.StatsOptionsSpec") - } - case AlterTableRemoveTTL: - _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { - ctx.WriteKeyWord("REMOVE TTL") - return nil - }) - default: - // TODO: not support - ctx.WritePlainf(" /* AlterTableType(%d) is not supported */ ", n.Tp) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AlterTableSpec) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterTableSpec) - if n.Constraint != nil { - node, ok := n.Constraint.Accept(v) - if !ok { - return n, false - } - n.Constraint = node.(*Constraint) - } - if n.NewTable != nil { - node, ok := n.NewTable.Accept(v) - if !ok { - return n, false - } - n.NewTable = node.(*TableName) - } - for i, col := range n.NewColumns { - node, ok := col.Accept(v) - if !ok { - return n, false - } - n.NewColumns[i] = node.(*ColumnDef) - } - for i, constraint := range n.NewConstraints { - node, ok := constraint.Accept(v) - if !ok { - return n, false - } - n.NewConstraints[i] = node.(*Constraint) - } - if n.OldColumnName != nil { - node, ok := n.OldColumnName.Accept(v) - if !ok { - return n, false - } - n.OldColumnName = node.(*ColumnName) - } - if n.Position != nil { - node, ok := n.Position.Accept(v) - if !ok { - return n, false - } - n.Position = node.(*ColumnPosition) - } - if n.Partition != nil { - node, ok := n.Partition.Accept(v) - if !ok { - return n, false - } - n.Partition = node.(*PartitionOptions) - } - for i, option := range n.Options { - node, ok := option.Accept(v) - if !ok { - return n, false - } - n.Options[i] = node.(*TableOption) - } - for _, def := range n.PartDefinitions { - if !def.acceptInPlace(v) { - return n, false - } - } - return v.Leave(n) -} - -// AlterTableStmt is a statement to change the structure of a table. -// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html -type AlterTableStmt struct { - ddlNode - - Table *TableName - Specs []*AlterTableSpec -} - -func (n *AlterTableStmt) HaveOnlyPlacementOptions() bool { - for _, n := range n.Specs { - if n.Tp != AlterTablePartitionOptions { - return false - } - if !n.IsAllPlacementRule() { - return false - } - } - return true -} - -// Restore implements Node interface. -func (n *AlterTableStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.HaveOnlyPlacementOptions() { - return nil - } - ctx.WriteKeyWord("ALTER TABLE ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterTableStmt.Table") - } - specs := make([]*AlterTableSpec, 0, len(n.Specs)) - for _, spec := range n.Specs { - if spec.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { - continue - } - if spec.Tp == AlterTableOption { - newOptions := tableOptionsWithRestoreTTLFlag(ctx.Flags, spec.Options) - if len(newOptions) == 0 { - continue - } - newSpec := *spec - newSpec.Options = newOptions - spec = &newSpec - } - specs = append(specs, spec) - } - for i, spec := range specs { - if i == 0 || spec.Tp == AlterTablePartition || spec.Tp == AlterTableRemovePartitioning || spec.Tp == AlterTableImportTablespace || spec.Tp == AlterTableDiscardTablespace { - ctx.WritePlain(" ") - } else { - ctx.WritePlain(", ") - } - if err := spec.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterTableStmt.Specs[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AlterTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterTableStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - for i, val := range n.Specs { - node, ok = val.Accept(v) - if !ok { - return n, false - } - n.Specs[i] = node.(*AlterTableSpec) - } - return v.Leave(n) -} - -// TruncateTableStmt is a statement to empty a table completely. -// See https://dev.mysql.com/doc/refman/5.7/en/truncate-table.html -type TruncateTableStmt struct { - ddlNode - - Table *TableName -} - -// Restore implements Node interface. -func (n *TruncateTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("TRUNCATE TABLE ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore TruncateTableStmt.Table") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *TruncateTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*TruncateTableStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - return v.Leave(n) -} - -var ( - ErrNoParts = terror.ClassDDL.NewStd(mysql.ErrNoParts) - ErrPartitionColumnList = terror.ClassDDL.NewStd(mysql.ErrPartitionColumnList) - ErrPartitionRequiresValues = terror.ClassDDL.NewStd(mysql.ErrPartitionRequiresValues) - ErrPartitionsMustBeDefined = terror.ClassDDL.NewStd(mysql.ErrPartitionsMustBeDefined) - ErrPartitionWrongNoPart = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoPart) - ErrPartitionWrongNoSubpart = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoSubpart) - ErrPartitionWrongValues = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongValues) - ErrRowSinglePartitionField = terror.ClassDDL.NewStd(mysql.ErrRowSinglePartitionField) - ErrSubpartition = terror.ClassDDL.NewStd(mysql.ErrSubpartition) - ErrSystemVersioningWrongPartitions = terror.ClassDDL.NewStd(mysql.ErrSystemVersioningWrongPartitions) - ErrTooManyValues = terror.ClassDDL.NewStd(mysql.ErrTooManyValues) - ErrWrongPartitionTypeExpectedSystemTime = terror.ClassDDL.NewStd(mysql.ErrWrongPartitionTypeExpectedSystemTime) - ErrUnknownCharacterSet = terror.ClassDDL.NewStd(mysql.ErrUnknownCharacterSet) - ErrCoalescePartitionNoPartition = terror.ClassDDL.NewStd(mysql.ErrCoalescePartitionNoPartition) - ErrWrongUsage = terror.ClassDDL.NewStd(mysql.ErrWrongUsage) -) - -type SubPartitionDefinition struct { - Name model.CIStr - Options []*TableOption -} - -func (spd *SubPartitionDefinition) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SUBPARTITION ") - ctx.WriteName(spd.Name.O) - for i, opt := range spd.Options { - ctx.WritePlain(" ") - if err := opt.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore SubPartitionDefinition.Options[%d]", i) - } - } - return nil -} - -type PartitionDefinitionClause interface { - restore(ctx *format.RestoreCtx) error - acceptInPlace(v Visitor) bool - // Validate checks if the clause is consistent with the given options. - // `pt` can be 0 and `columns` can be -1 to skip checking the clause against - // the partition type or number of columns in the expression list. - Validate(pt model.PartitionType, columns int) error -} - -type PartitionDefinitionClauseNone struct{} - -func (*PartitionDefinitionClauseNone) restore(_ *format.RestoreCtx) error { - return nil -} - -func (*PartitionDefinitionClauseNone) acceptInPlace(_ Visitor) bool { - return true -} - -func (*PartitionDefinitionClauseNone) Validate(pt model.PartitionType, _ int) error { - switch pt { - case 0: - case model.PartitionTypeRange: - return ErrPartitionRequiresValues.GenWithStackByArgs("RANGE", "LESS THAN") - case model.PartitionTypeList: - return ErrPartitionRequiresValues.GenWithStackByArgs("LIST", "IN") - case model.PartitionTypeSystemTime: - return ErrSystemVersioningWrongPartitions - } - return nil -} - -type PartitionDefinitionClauseLessThan struct { - Exprs []ExprNode -} - -func (n *PartitionDefinitionClauseLessThan) restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(" VALUES LESS THAN ") - ctx.WritePlain("(") - for i, expr := range n.Exprs { - if i != 0 { - ctx.WritePlain(", ") - } - if err := expr.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseLessThan.Exprs[%d]", i) - } - } - ctx.WritePlain(")") - return nil -} - -func (n *PartitionDefinitionClauseLessThan) acceptInPlace(v Visitor) bool { - for i, expr := range n.Exprs { - newExpr, ok := expr.Accept(v) - if !ok { - return false - } - n.Exprs[i] = newExpr.(ExprNode) - } - return true -} - -func (n *PartitionDefinitionClauseLessThan) Validate(pt model.PartitionType, columns int) error { - switch pt { - case model.PartitionTypeRange, 0: - default: - return ErrPartitionWrongValues.GenWithStackByArgs("RANGE", "LESS THAN") - } - - switch { - case columns == 0 && len(n.Exprs) != 1: - return ErrTooManyValues.GenWithStackByArgs("RANGE") - case columns > 0 && len(n.Exprs) != columns: - return ErrPartitionColumnList - } - return nil -} - -type PartitionDefinitionClauseIn struct { - Values [][]ExprNode -} - -func (n *PartitionDefinitionClauseIn) restore(ctx *format.RestoreCtx) error { - // we special-case an empty list of values to mean MariaDB's "DEFAULT" clause. - if len(n.Values) == 0 { - ctx.WriteKeyWord(" DEFAULT") - return nil - } - if len(n.Values) == 1 && len(n.Values[0]) == 1 { - if _, ok := n.Values[0][0].(*DefaultExpr); ok { - ctx.WriteKeyWord(" DEFAULT") - return nil - } - } - - ctx.WriteKeyWord(" VALUES IN ") - ctx.WritePlain("(") - for i, valList := range n.Values { - if i != 0 { - ctx.WritePlain(", ") - } - if len(valList) == 1 { - if err := valList[0].Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][0]", i) - } - } else { - ctx.WritePlain("(") - for j, val := range valList { - if j != 0 { - ctx.WritePlain(", ") - } - if err := val.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][%d]", i, j) - } - } - ctx.WritePlain(")") - } - } - ctx.WritePlain(")") - return nil -} - -func (n *PartitionDefinitionClauseIn) acceptInPlace(v Visitor) bool { - for _, valList := range n.Values { - for j, val := range valList { - newVal, ok := val.Accept(v) - if !ok { - return false - } - valList[j] = newVal.(ExprNode) - } - } - return true -} - -func (n *PartitionDefinitionClauseIn) Validate(pt model.PartitionType, columns int) error { - switch pt { - case model.PartitionTypeList, 0: - default: - return ErrPartitionWrongValues.GenWithStackByArgs("LIST", "IN") - } - - if len(n.Values) == 0 { - return nil - } - - nextIdx := 1 - expectedColCount := len(n.Values[0]) - // OK if one of the n.Values is DefaultExpr as only value - if expectedColCount == 1 { - if _, ok := n.Values[0][0].(*DefaultExpr); ok { - // Only DEFAULT in the partition definition, OK - if len(n.Values) > 1 { - expectedColCount = len(n.Values[1]) - nextIdx++ - } - } - } - for _, val := range n.Values[nextIdx:] { - if len(val) != expectedColCount { - if _, ok := val[0].(*DefaultExpr); ok && len(val) == 1 { - continue - } - return ErrPartitionColumnList - } - } - - switch { - case columns == 0 && expectedColCount != 1: - return ErrRowSinglePartitionField - case columns > 0 && expectedColCount != columns: - if len(n.Values) == 1 && expectedColCount == 1 { - if _, ok := n.Values[0][0].(*DefaultExpr); ok { - // Only one value, which is DEFAULT, which is OK - return nil - } - } - return ErrPartitionColumnList - } - return nil -} - -type PartitionDefinitionClauseHistory struct { - Current bool -} - -func (n *PartitionDefinitionClauseHistory) restore(ctx *format.RestoreCtx) error { - if n.Current { - ctx.WriteKeyWord(" CURRENT") - } else { - ctx.WriteKeyWord(" HISTORY") - } - return nil -} - -func (*PartitionDefinitionClauseHistory) acceptInPlace(_ Visitor) bool { - return true -} - -func (*PartitionDefinitionClauseHistory) Validate(pt model.PartitionType, _ int) error { - switch pt { - case 0, model.PartitionTypeSystemTime: - default: - return ErrWrongPartitionTypeExpectedSystemTime - } - - return nil -} - -// PartitionDefinition defines a single partition. -type PartitionDefinition struct { - Name model.CIStr - Clause PartitionDefinitionClause - Options []*TableOption - Sub []*SubPartitionDefinition -} - -// Comment returns the comment option given to this definition. -// The second return value indicates if the comment option exists. -func (n *PartitionDefinition) Comment() (string, bool) { - for _, opt := range n.Options { - if opt.Tp == TableOptionComment { - return opt.StrValue, true - } - } - return "", false -} - -func (n *PartitionDefinition) acceptInPlace(v Visitor) bool { - return n.Clause.acceptInPlace(v) -} - -// Restore implements Node interface. -func (n *PartitionDefinition) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("PARTITION ") - ctx.WriteName(n.Name.O) - - if err := n.Clause.restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PartitionDefinition.Clause") - } - - for i, opt := range n.Options { - ctx.WritePlain(" ") - if err := opt.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Options[%d]", i) - } - } - - if len(n.Sub) > 0 { - ctx.WritePlain(" (") - for i, spd := range n.Sub { - if i != 0 { - ctx.WritePlain(",") - } - if err := spd.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Sub[%d]", i) - } - } - ctx.WritePlain(")") - } - - return nil -} - -type PartitionIntervalExpr struct { - Expr ExprNode - // TimeUnitInvalid if not Time based INTERVAL! - TimeUnit TimeUnitType -} - -type PartitionInterval struct { - // To be able to get original text and replace the syntactic sugar with generated - // partition definitions - node - IntervalExpr PartitionIntervalExpr - FirstRangeEnd *ExprNode - LastRangeEnd *ExprNode - MaxValPart bool - NullPart bool -} - -// PartitionMethod describes how partitions or subpartitions are constructed. -type PartitionMethod struct { - // To be able to get original text and replace the syntactic sugar with generated - // partition definitions - node - // Tp is the type of the partition function - Tp model.PartitionType - // Linear is a modifier to the HASH and KEY type for choosing a different - // algorithm - Linear bool - // Expr is an expression used as argument of HASH, RANGE AND LIST types - Expr ExprNode - // ColumnNames is a list of column names used as argument of KEY, - // RANGE COLUMNS and LIST COLUMNS types - ColumnNames []*ColumnName - // Unit is a time unit used as argument of SYSTEM_TIME type - Unit TimeUnitType - // Limit is a row count used as argument of the SYSTEM_TIME type - Limit uint64 - - // Num is the number of (sub)partitions required by the method. - Num uint64 - - // KeyAlgorithm is the optional hash algorithm type for `PARTITION BY [LINEAR] KEY` syntax. - KeyAlgorithm *PartitionKeyAlgorithm - - Interval *PartitionInterval -} - -type PartitionKeyAlgorithm struct { - Type uint64 -} - -// Restore implements the Node interface -func (n *PartitionMethod) Restore(ctx *format.RestoreCtx) error { - if n.Linear { - ctx.WriteKeyWord("LINEAR ") - } - ctx.WriteKeyWord(n.Tp.String()) - - if n.KeyAlgorithm != nil { - ctx.WriteKeyWord(" ALGORITHM") - ctx.WritePlainf(" = %d", n.KeyAlgorithm.Type) - } - - switch { - case n.Tp == model.PartitionTypeSystemTime: - if n.Expr != nil && n.Unit != TimeUnitInvalid { - ctx.WriteKeyWord(" INTERVAL ") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr") - } - ctx.WritePlain(" ") - ctx.WriteKeyWord(n.Unit.String()) - } - if n.Limit > 0 { - ctx.WriteKeyWord(" LIMIT ") - ctx.WritePlainf("%d", n.Limit) - } - - case n.Expr != nil: - ctx.WritePlain(" (") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr") - } - ctx.WritePlain(")") - - default: - if n.Tp == model.PartitionTypeRange || n.Tp == model.PartitionTypeList { - ctx.WriteKeyWord(" COLUMNS") - } - ctx.WritePlain(" (") - for i, col := range n.ColumnNames { - if i > 0 { - ctx.WritePlain(",") - } - if err := col.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing PartitionMethod.ColumnName[%d]", i) - } - } - ctx.WritePlain(")") - } - - if n.Interval != nil { - ctx.WritePlain(" INTERVAL (") - n.Interval.IntervalExpr.Expr.Restore(ctx) - if n.Interval.IntervalExpr.TimeUnit != TimeUnitInvalid { - ctx.WritePlain(" ") - ctx.WriteKeyWord(n.Interval.IntervalExpr.TimeUnit.String()) - } - ctx.WritePlain(")") - if n.Interval.FirstRangeEnd != nil { - ctx.WritePlain(" FIRST PARTITION LESS THAN (") - (*n.Interval.FirstRangeEnd).Restore(ctx) - ctx.WritePlain(")") - } - if n.Interval.LastRangeEnd != nil { - ctx.WritePlain(" LAST PARTITION LESS THAN (") - (*n.Interval.LastRangeEnd).Restore(ctx) - ctx.WritePlain(")") - } - if n.Interval.NullPart { - ctx.WritePlain(" NULL PARTITION") - } - if n.Interval.MaxValPart { - ctx.WritePlain(" MAXVALUE PARTITION") - } - } - - return nil -} - -// acceptInPlace is like Node.Accept but does not allow replacing the node itself. -func (n *PartitionMethod) acceptInPlace(v Visitor) bool { - if n.Expr != nil { - expr, ok := n.Expr.Accept(v) - if !ok { - return false - } - n.Expr = expr.(ExprNode) - } - for i, colName := range n.ColumnNames { - newColName, ok := colName.Accept(v) - if !ok { - return false - } - n.ColumnNames[i] = newColName.(*ColumnName) - } - return true -} - -// PartitionOptions specifies the partition options. -type PartitionOptions struct { - PartitionMethod - Sub *PartitionMethod - Definitions []*PartitionDefinition -} - -// Validate checks if the partition is well-formed. -func (n *PartitionOptions) Validate() error { - // if both a partition list and the partition numbers are specified, their values must match - if n.Num != 0 && len(n.Definitions) != 0 && n.Num != uint64(len(n.Definitions)) { - return ErrPartitionWrongNoPart - } - // now check the subpartition count - if len(n.Definitions) > 0 { - // ensure the subpartition count for every partitions are the same - // then normalize n.Num and n.Sub.Num so equality comparison works. - n.Num = uint64(len(n.Definitions)) - - subDefCount := len(n.Definitions[0].Sub) - for _, pd := range n.Definitions[1:] { - if len(pd.Sub) != subDefCount { - return ErrPartitionWrongNoSubpart - } - } - if n.Sub != nil { - if n.Sub.Num != 0 && subDefCount != 0 && n.Sub.Num != uint64(subDefCount) { - return ErrPartitionWrongNoSubpart - } - if subDefCount != 0 { - n.Sub.Num = uint64(subDefCount) - } - } else if subDefCount != 0 { - return ErrSubpartition - } - } - - switch n.Tp { - case model.PartitionTypeHash, model.PartitionTypeKey: - if n.Num == 0 { - n.Num = 1 - } - case model.PartitionTypeRange, model.PartitionTypeList: - if n.Interval == nil && len(n.Definitions) == 0 { - return ErrPartitionsMustBeDefined.GenWithStackByArgs(n.Tp) - } - case model.PartitionTypeSystemTime: - if len(n.Definitions) < 2 { - return ErrSystemVersioningWrongPartitions - } - } - - for _, pd := range n.Definitions { - // ensure the partition definition types match the methods, - // e.g. RANGE partitions only allows VALUES LESS THAN - if err := pd.Clause.Validate(n.Tp, len(n.ColumnNames)); err != nil { - return err - } - } - - return nil -} - -func (n *PartitionOptions) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("PARTITION BY ") - if err := n.PartitionMethod.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PartitionOptions.PartitionMethod") - } - - if n.Num > 0 && len(n.Definitions) == 0 { - ctx.WriteKeyWord(" PARTITIONS ") - ctx.WritePlainf("%d", n.Num) - } - - if n.Sub != nil { - ctx.WriteKeyWord(" SUBPARTITION BY ") - if err := n.Sub.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PartitionOptions.Sub") - } - if n.Sub.Num > 0 { - ctx.WriteKeyWord(" SUBPARTITIONS ") - ctx.WritePlainf("%d", n.Sub.Num) - } - } - - if len(n.Definitions) > 0 { - ctx.WritePlain(" (") - for i, def := range n.Definitions { - if i > 0 { - ctx.WritePlain(",") - } - if err := def.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PartitionOptions.Definitions[%d]", i) - } - } - ctx.WritePlain(")") - } - - return nil -} - -func (n *PartitionOptions) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - n = newNode.(*PartitionOptions) - if !n.PartitionMethod.acceptInPlace(v) { - return n, false - } - if n.Sub != nil && !n.Sub.acceptInPlace(v) { - return n, false - } - for _, def := range n.Definitions { - if !def.acceptInPlace(v) { - return n, false - } - } - return v.Leave(n) -} - -// RecoverTableStmt is a statement to recover dropped table. -type RecoverTableStmt struct { - ddlNode - - JobID int64 - Table *TableName - JobNum int64 -} - -// Restore implements Node interface. -func (n *RecoverTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("RECOVER TABLE ") - if n.JobID != 0 { - ctx.WriteKeyWord("BY JOB ") - ctx.WritePlainf("%d", n.JobID) - } else { - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table") - } - if n.JobNum > 0 { - ctx.WritePlainf(" %d", n.JobNum) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RecoverTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - n = newNode.(*RecoverTableStmt) - if n.Table != nil { - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - } - return v.Leave(n) -} - -// FlashBackToTimestampStmt is a statement to restore the cluster to the specified timestamp -type FlashBackToTimestampStmt struct { - ddlNode - - FlashbackTS ExprNode - Tables []*TableName - DBName model.CIStr -} - -// Restore implements Node interface -func (n *FlashBackToTimestampStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("FLASHBACK ") - if len(n.Tables) != 0 { - ctx.WriteKeyWord("TABLE ") - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index) - } - } - } else if n.DBName.O != "" { - ctx.WriteKeyWord("DATABASE ") - ctx.WriteName(n.DBName.O) - } else { - ctx.WriteKeyWord("CLUSTER") - } - ctx.WriteKeyWord(" TO TIMESTAMP ") - if err := n.FlashbackTS.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing FlashBackToTimestampStmt.FlashbackTS") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *FlashBackToTimestampStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*FlashBackToTimestampStmt) - if len(n.Tables) != 0 { - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - } - node, ok := n.FlashbackTS.Accept(v) - if !ok { - return n, false - } - n.FlashbackTS = node.(ExprNode) - return v.Leave(n) -} - -// FlashBackTableStmt is a statement to restore a dropped/truncate table. -type FlashBackTableStmt struct { - ddlNode - - Table *TableName - NewName string -} - -// Restore implements Node interface. -func (n *FlashBackTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("FLASHBACK TABLE ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table") - } - if len(n.NewName) > 0 { - ctx.WriteKeyWord(" TO ") - ctx.WriteName(n.NewName) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *FlashBackTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - n = newNode.(*FlashBackTableStmt) - if n.Table != nil { - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - } - return v.Leave(n) -} - -type AttributesSpec struct { - node - - Attributes string - Default bool -} - -func (n *AttributesSpec) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ATTRIBUTES") - ctx.WritePlain("=") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - return nil - } - ctx.WriteString(n.Attributes) - return nil -} - -func (n *AttributesSpec) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AttributesSpec) - return v.Leave(n) -} - -type StatsOptionsSpec struct { - node - - StatsOptions string - Default bool -} - -func (n *StatsOptionsSpec) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("STATS_OPTIONS") - ctx.WritePlain("=") - if n.Default { - ctx.WriteKeyWord("DEFAULT") - return nil - } - ctx.WriteString(n.StatsOptions) - return nil -} - -func (n *StatsOptionsSpec) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*StatsOptionsSpec) - return v.Leave(n) -} - -// AlterPlacementPolicyStmt is a statement to alter placement policy option. -type AlterPlacementPolicyStmt struct { - ddlNode - - PolicyName model.CIStr - IfExists bool - PlacementOptions []*PlacementOption -} - -func (n *AlterPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { - return nil - } - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restorePlacementStmtInSpecialComment(ctx, n) - } - - ctx.WriteKeyWord("ALTER PLACEMENT POLICY ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.PolicyName.O) - for i, option := range n.PlacementOptions { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing AlterPlacementPolicyStmt TableOption: [%v]", i) - } - } - return nil -} - -func (n *AlterPlacementPolicyStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterPlacementPolicyStmt) - return v.Leave(n) -} - -func CheckAppend(ops []*ResourceGroupOption, newOp *ResourceGroupOption) bool { - for _, op := range ops { - if op.Tp == newOp.Tp { - return false - } - } - return true -} - -func CheckRunawayAppend(ops []*ResourceGroupRunawayOption, newOp *ResourceGroupRunawayOption) bool { - for _, op := range ops { - if op.Tp == newOp.Tp { - return false - } - } - return true -} - -func CheckBackgroundAppend(ops []*ResourceGroupBackgroundOption, newOp *ResourceGroupBackgroundOption) bool { - for _, op := range ops { - if op.Type == newOp.Type { - return false - } - } - return true -} - -// AlterResourceGroupStmt is a statement to alter placement policy option. -type AlterResourceGroupStmt struct { - ddlNode - - ResourceGroupName model.CIStr - IfExists bool - ResourceGroupOptionList []*ResourceGroupOption -} - -func (n *AlterResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { - if ctx.Flags.HasTiDBSpecialCommentFlag() { - return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) - } - - ctx.WriteKeyWord("ALTER RESOURCE GROUP ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - ctx.WriteName(n.ResourceGroupName.O) - for i, option := range n.ResourceGroupOptionList { - if i > 0 { - ctx.WritePlain(",") - } - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing AlterResourceGroupStmt Options: [%v]", i) - } - } - return nil -} - -func (n *AlterResourceGroupStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterResourceGroupStmt) - return v.Leave(n) -} - -// AlterSequenceStmt is a statement to alter sequence option. -type AlterSequenceStmt struct { - ddlNode - - // sequence name - Name *TableName - - IfExists bool - SeqOptions []*SequenceOption -} - -func (n *AlterSequenceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ALTER SEQUENCE ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - if err := n.Name.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterSequenceStmt.Table") - } - for i, option := range n.SeqOptions { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing AlterSequenceStmt SequenceOption: [%v]", i) - } - } - return nil -} - -func (n *AlterSequenceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterSequenceStmt) - node, ok := n.Name.Accept(v) - if !ok { - return n, false - } - n.Name = node.(*TableName) - return v.Leave(n) -} - -func restorePlacementStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode) error { - return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDPlacement) -} - -func restoreStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode, feature string) error { - origFlags := ctx.Flags - defer func() { - ctx.Flags = origFlags - }() - - ctx.Flags |= format.RestoreTiDBSpecialComment - return ctx.WriteWithSpecialComments(feature, func() error { - ctx.Flags &= ^format.RestoreTiDBSpecialComment - return n.Restore(ctx) - }) -} - -func tableOptionsWithRestoreTTLFlag(flags format.RestoreFlags, options []*TableOption) []*TableOption { - if !flags.HasRestoreWithTTLEnableOff() { - return options - } - - newOptions := make([]*TableOption, 0, len(options)) - for _, opt := range options { - if opt.Tp == TableOptionTTLEnable { - continue - } - - newOptions = append(newOptions, opt) - if opt.Tp == TableOptionTTL { - newOptions = append(newOptions, &TableOption{ - Tp: TableOptionTTLEnable, - BoolValue: false, - }) - } - } - return newOptions -} diff --git a/parser/ast/ddl_test.go b/parser/ast/ddl_test.go deleted file mode 100644 index dbbb212037db8..0000000000000 --- a/parser/ast/ddl_test.go +++ /dev/null @@ -1,920 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast_test - -import ( - "testing" - - . "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/stretchr/testify/require" -) - -func TestDDLVisitorCover(t *testing.T) { - ce := &checkExpr{} - constraint := &Constraint{Keys: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, Refer: &ReferenceDef{}, Option: &IndexOption{}} - - alterTableSpec := &AlterTableSpec{Constraint: constraint, Options: []*TableOption{{}}, NewTable: &TableName{}, NewColumns: []*ColumnDef{{Name: &ColumnName{}}}, OldColumnName: &ColumnName{}, Position: &ColumnPosition{RelativeColumn: &ColumnName{}}, AttributesSpec: &AttributesSpec{}} - - stmts := []struct { - node Node - expectedEnterCnt int - expectedLeaveCnt int - }{ - {&CreateDatabaseStmt{}, 0, 0}, - {&AlterDatabaseStmt{}, 0, 0}, - {&DropDatabaseStmt{}, 0, 0}, - {&DropIndexStmt{Table: &TableName{}}, 0, 0}, - {&DropTableStmt{Tables: []*TableName{{}, {}}}, 0, 0}, - {&RenameTableStmt{TableToTables: []*TableToTable{}}, 0, 0}, - {&TruncateTableStmt{Table: &TableName{}}, 0, 0}, - - // TODO: cover children - {&AlterTableStmt{Table: &TableName{}, Specs: []*AlterTableSpec{alterTableSpec}}, 0, 0}, - {&CreateIndexStmt{Table: &TableName{}}, 0, 0}, - {&CreateTableStmt{Table: &TableName{}, ReferTable: &TableName{}}, 0, 0}, - {&CreateViewStmt{ViewName: &TableName{}, Select: &SelectStmt{}}, 0, 0}, - {&AlterTableSpec{}, 0, 0}, - {&ColumnDef{Name: &ColumnName{}, Options: []*ColumnOption{{Expr: ce}}}, 1, 1}, - {&ColumnOption{Expr: ce}, 1, 1}, - {&ColumnPosition{RelativeColumn: &ColumnName{}}, 0, 0}, - {&Constraint{Keys: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, Refer: &ReferenceDef{}, Option: &IndexOption{}}, 0, 0}, - {&IndexPartSpecification{Column: &ColumnName{}}, 0, 0}, - {&ReferenceDef{Table: &TableName{}, IndexPartSpecifications: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, OnDelete: &OnDeleteOpt{}, OnUpdate: &OnUpdateOpt{}}, 0, 0}, - {&AlterTableSpec{NewConstraints: []*Constraint{constraint, constraint}}, 0, 0}, - {&AlterTableSpec{NewConstraints: []*Constraint{constraint}, NewColumns: []*ColumnDef{{Name: &ColumnName{}}}}, 0, 0}, - } - - for _, v := range stmts { - ce.reset() - v.node.Accept(checkVisitor{}) - require.Equal(t, v.expectedEnterCnt, ce.enterCnt) - require.Equal(t, v.expectedLeaveCnt, ce.leaveCnt) - v.node.Accept(visitor1{}) - } -} - -func TestDDLIndexColNameRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"(a + 1)", "(`a`+1)"}, - {"(1 * 1 + (1 + 1))", "(1*1+(1+1))"}, - {"((1 * 1 + (1 + 1)))", "((1*1+(1+1)))"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateIndexStmt).IndexPartSpecifications[0] - } - runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (%s) USING HASH", extractNodeFunc) -} - -func TestDDLIndexExprRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"world", "`world`"}, - {"world(2)", "`world`(2)"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateIndexStmt).IndexPartSpecifications[0] - } - runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (%s) USING HASH", extractNodeFunc) -} - -func TestDDLOnDeleteRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"on delete restrict", "ON DELETE RESTRICT"}, - {"on delete CASCADE", "ON DELETE CASCADE"}, - {"on delete SET NULL", "ON DELETE SET NULL"}, - {"on delete no action", "ON DELETE NO ACTION"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Constraints[1].Refer.OnDelete - } - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s)", extractNodeFunc) - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) on update CASCADE %s)", extractNodeFunc) - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s on update CASCADE)", extractNodeFunc) -} - -func TestDDLOnUpdateRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"ON UPDATE RESTRICT", "ON UPDATE RESTRICT"}, - {"on update CASCADE", "ON UPDATE CASCADE"}, - {"on update SET NULL", "ON UPDATE SET NULL"}, - {"on update no action", "ON UPDATE NO ACTION"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Constraints[1].Refer.OnUpdate - } - runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE %s )", extractNodeFunc) - runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s ON DELETE CASCADE)", extractNodeFunc) - runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s )", extractNodeFunc) -} - -func TestDDLIndexOption(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"key_block_size=16", "KEY_BLOCK_SIZE=16"}, - {"USING HASH", "USING HASH"}, - {"comment 'hello'", "COMMENT 'hello'"}, - {"key_block_size=16 USING HASH", "KEY_BLOCK_SIZE=16 USING HASH"}, - {"USING HASH KEY_BLOCK_SIZE=16", "KEY_BLOCK_SIZE=16 USING HASH"}, - {"USING HASH COMMENT 'foo'", "USING HASH COMMENT 'foo'"}, - {"COMMENT 'foo'", "COMMENT 'foo'"}, - {"key_block_size = 32 using hash comment 'hello'", "KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, - {"key_block_size=32 using btree comment 'hello'", "KEY_BLOCK_SIZE=32 USING BTREE COMMENT 'hello'"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateIndexStmt).IndexOption - } - runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (a) %s", extractNodeFunc) -} - -func TestTableToTableRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"t1 to t2", "`t1` TO `t2`"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*RenameTableStmt).TableToTables[0] - } - runNodeRestoreTest(t, testCases, "rename table %s", extractNodeFunc) -} - -func TestDDLReferenceDefRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"REFERENCES parent(id) ON DELETE CASCADE", "REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"REFERENCES parent(id,hello) ON DELETE CASCADE", "REFERENCES `parent`(`id`, `hello`) ON DELETE CASCADE"}, - {"REFERENCES parent(id,hello(12)) ON DELETE CASCADE", "REFERENCES `parent`(`id`, `hello`(12)) ON DELETE CASCADE"}, - {"REFERENCES parent(id(8),hello(12)) ON DELETE CASCADE", "REFERENCES `parent`(`id`(8), `hello`(12)) ON DELETE CASCADE"}, - {"REFERENCES parent(id)", "REFERENCES `parent`(`id`)"}, - {"REFERENCES parent((id+1))", "REFERENCES `parent`((`id`+1))"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Constraints[1].Refer - } - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) %s)", extractNodeFunc) -} - -func TestDDLConstraintRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"INDEX par_ind (parent_id)", "INDEX `par_ind`(`parent_id`)"}, - {"INDEX par_ind (parent_id(6))", "INDEX `par_ind`(`parent_id`(6))"}, - {"INDEX expr_ind ((id + parent_id))", "INDEX `expr_ind`((`id`+`parent_id`))"}, - {"INDEX expr_ind ((lower(id)))", "INDEX `expr_ind`((LOWER(`id`)))"}, - {"key par_ind (parent_id)", "INDEX `par_ind`(`parent_id`)"}, - {"key expr_ind ((lower(id)))", "INDEX `expr_ind`((LOWER(`id`)))"}, - {"unique par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, - {"unique key par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, - {"unique index par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, - {"unique expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, - {"unique expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, - {"unique key expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, - {"unique key expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, - {"unique index expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, - {"unique index expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, - {"fulltext key full_id (parent_id)", "FULLTEXT `full_id`(`parent_id`)"}, - {"fulltext INDEX full_id (parent_id)", "FULLTEXT `full_id`(`parent_id`)"}, - {"fulltext INDEX full_id ((parent_id+1))", "FULLTEXT `full_id`((`parent_id`+1))"}, - {"PRIMARY KEY (id)", "PRIMARY KEY(`id`)"}, - {"PRIMARY KEY (id) key_block_size = 32 using hash comment 'hello'", "PRIMARY KEY(`id`) KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, - {"PRIMARY KEY ((id+1))", "PRIMARY KEY((`id`+1))"}, - {"CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent((id+1)) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`((`id`+1)) ON DELETE CASCADE"}, - {"CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent((id+1)) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`((`id`+1)) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"CONSTRAINT fk_123 FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"CONSTRAINT fk_123 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"CONSTRAINT fk_123 FOREIGN KEY ((parent_id+1),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT `fk_123` FOREIGN KEY ((`parent_id`+1), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"CONSTRAINT fk_123 FOREIGN KEY ((parent_id+1)) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT `fk_123` FOREIGN KEY ((`parent_id`+1)) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"FOREIGN KEY ((parent_id+1),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY ((`parent_id`+1), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"FOREIGN KEY ((parent_id+1)) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY ((`parent_id`+1)) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Constraints[0] - } - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, %s)", extractNodeFunc) - - specialCommentCases := []NodeRestoreTestCase{ - {"PRIMARY KEY (id) CLUSTERED", "PRIMARY KEY(`id`) /*T![clustered_index] CLUSTERED */"}, - {"primary key (id) NONCLUSTERED", "PRIMARY KEY(`id`) /*T![clustered_index] NONCLUSTERED */"}, - {"PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */", "PRIMARY KEY(`id`) /*T![clustered_index] CLUSTERED */"}, - {"primary key (id) /*T![clustered_index] NONCLUSTERED */", "PRIMARY KEY(`id`) /*T![clustered_index] NONCLUSTERED */"}, - } - runNodeRestoreTestWithFlags(t, specialCommentCases, - "CREATE TABLE child (id INT, parent_id INT, %s)", - extractNodeFunc, format.DefaultRestoreFlags|format.RestoreTiDBSpecialComment) -} - -func TestDDLColumnOptionRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"primary key", "PRIMARY KEY"}, - {"not null", "NOT NULL"}, - {"null", "NULL"}, - {"auto_increment", "AUTO_INCREMENT"}, - {"DEFAULT 10", "DEFAULT 10"}, - {"DEFAULT '10'", "DEFAULT _UTF8MB4'10'"}, - {"DEFAULT 'hello'", "DEFAULT _UTF8MB4'hello'"}, - {"DEFAULT 1.1", "DEFAULT 1.1"}, - {"DEFAULT NULL", "DEFAULT NULL"}, - {"DEFAULT ''", "DEFAULT _UTF8MB4''"}, - {"DEFAULT TRUE", "DEFAULT TRUE"}, - {"DEFAULT FALSE", "DEFAULT FALSE"}, - {"UNIQUE KEY", "UNIQUE KEY"}, - {"on update CURRENT_TIMESTAMP", "ON UPDATE CURRENT_TIMESTAMP()"}, - {"comment 'hello'", "COMMENT 'hello'"}, - {"generated always as(id + 1)", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, - {"generated always as(id + 1) virtual", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, - {"generated always as(id + 1) stored", "GENERATED ALWAYS AS(`id`+1) STORED"}, - {"REFERENCES parent(id)", "REFERENCES `parent`(`id`)"}, - {"COLLATE utf8_bin", "COLLATE utf8_bin"}, - {"STORAGE DEFAULT", "STORAGE DEFAULT"}, - {"STORAGE DISK", "STORAGE DISK"}, - {"STORAGE MEMORY", "STORAGE MEMORY"}, - {"AUTO_RANDOM (3)", "AUTO_RANDOM(3)"}, - {"AUTO_RANDOM", "AUTO_RANDOM"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Cols[0].Options[0] - } - runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT %s)", extractNodeFunc) -} - -func TestGeneratedRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"generated always as(id + 1)", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, - {"generated always as(id + 1) virtual", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, - {"generated always as(id + 1) stored", "GENERATED ALWAYS AS(`id`+1) STORED"}, - {"generated always as(lower(id)) stored", "GENERATED ALWAYS AS(LOWER(`id`)) STORED"}, - {"generated always as(lower(child.id)) stored", "GENERATED ALWAYS AS(LOWER(`id`)) STORED"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Cols[0].Options[0] - } - runNodeRestoreTestWithFlagsStmtChange(t, testCases, "CREATE TABLE child (id INT %s)", extractNodeFunc, - format.DefaultRestoreFlags|format.RestoreWithoutSchemaName|format.RestoreWithoutTableName) -} - -func TestDDLColumnDefRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - // for type - {"id json", "`id` JSON"}, - {"id time(5)", "`id` TIME(5)"}, - {"id int(5) unsigned", "`id` INT(5) UNSIGNED"}, - {"id int(5) UNSIGNED ZEROFILL", "`id` INT(5) UNSIGNED ZEROFILL"}, - {"id float(12,3)", "`id` FLOAT(12,3)"}, - {"id float", "`id` FLOAT"}, - {"id double(22,3)", "`id` DOUBLE(22,3)"}, - {"id double", "`id` DOUBLE"}, - {"id tinyint(4)", "`id` TINYINT(4)"}, - {"id smallint(6)", "`id` SMALLINT(6)"}, - {"id mediumint(9)", "`id` MEDIUMINT(9)"}, - {"id integer(11)", "`id` INT(11)"}, - {"id bigint(20)", "`id` BIGINT(20)"}, - {"id DATE", "`id` DATE"}, - {"id DATETIME", "`id` DATETIME"}, - {"id DECIMAL(4,2)", "`id` DECIMAL(4,2)"}, - {"id char(1)", "`id` CHAR(1)"}, - {"id varchar(10) BINARY", "`id` VARCHAR(10) BINARY"}, - {"id binary(1)", "`id` BINARY(1)"}, - {"id timestamp(2)", "`id` TIMESTAMP(2)"}, - {"id timestamp", "`id` TIMESTAMP"}, - {"id datetime(2)", "`id` DATETIME(2)"}, - {"id date", "`id` DATE"}, - {"id year", "`id` YEAR"}, - {"id INT", "`id` INT"}, - {"id INT NULL", "`id` INT NULL"}, - {"id enum('a','b')", "`id` ENUM('a','b')"}, - {"id enum('''a''','''b''')", "`id` ENUM('''a''','''b''')"}, - {"id enum('a\\nb','a\\tb','a\\rb')", "`id` ENUM('a\nb','a\tb','a\rb')"}, - {"id enum('a','b') binary", "`id` ENUM('a','b') BINARY"}, - {"id enum(0x61, 0b01100010)", "`id` ENUM('a','b')"}, - {"id set('a','b')", "`id` SET('a','b')"}, - {"id set('''a''','''b''')", "`id` SET('''a''','''b''')"}, - {"id set('a\\nb','a'' \\r\\nb','a\\rb')", "`id` SET('a\nb','a'' \r\nb','a\rb')"}, - {`id set("a'\nb","a'b\tc")`, "`id` SET('a''\nb','a''b\tc')"}, - {"id set('a','b') binary", "`id` SET('a','b') BINARY"}, - {"id set(0x61, 0b01100010)", "`id` SET('a','b')"}, - {"id TEXT CHARACTER SET UTF8 COLLATE UTF8_UNICODE_CI", "`id` TEXT CHARACTER SET UTF8 COLLATE utf8_unicode_ci"}, - {"id text character set UTF8", "`id` TEXT CHARACTER SET UTF8"}, - {"id text charset UTF8", "`id` TEXT CHARACTER SET UTF8"}, - {"id varchar(50) collate UTF8MB4_CZECH_CI", "`id` VARCHAR(50) COLLATE utf8mb4_czech_ci"}, - {"id varchar(50) collate utf8_bin", "`id` VARCHAR(50) COLLATE utf8_bin"}, - {"id varchar(50) collate utf8_unicode_ci collate utf8mb4_bin", "`id` VARCHAR(50) COLLATE utf8_unicode_ci COLLATE utf8mb4_bin"}, - {"c1 char(10) character set LATIN1 collate latin1_german1_ci", "`c1` CHAR(10) CHARACTER SET LATIN1 COLLATE latin1_german1_ci"}, - - {"id int(11) PRIMARY KEY", "`id` INT(11) PRIMARY KEY"}, - {"id int(11) NOT NULL", "`id` INT(11) NOT NULL"}, - {"id INT(11) NULL", "`id` INT(11) NULL"}, - {"id INT(11) auto_increment", "`id` INT(11) AUTO_INCREMENT"}, - {"id INT(11) DEFAULT 10", "`id` INT(11) DEFAULT 10"}, - {"id INT(11) DEFAULT '10'", "`id` INT(11) DEFAULT _UTF8MB4'10'"}, - {"id INT(11) DEFAULT 1.1", "`id` INT(11) DEFAULT 1.1"}, - {"id INT(11) UNIQUE KEY", "`id` INT(11) UNIQUE KEY"}, - {"id INT(11) COLLATE ascii_bin", "`id` INT(11) COLLATE ascii_bin"}, - {"id INT(11) collate ascii_bin collate utf8_bin", "`id` INT(11) COLLATE ascii_bin COLLATE utf8_bin"}, - {"id INT(11) on update CURRENT_TIMESTAMP", "`id` INT(11) ON UPDATE CURRENT_TIMESTAMP()"}, - {"id INT(11) comment 'hello'", "`id` INT(11) COMMENT 'hello'"}, - {"id INT(11) generated always as(id + 1)", "`id` INT(11) GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, - {"id INT(11) REFERENCES parent(id)", "`id` INT(11) REFERENCES `parent`(`id`)"}, - - {"id bit", "`id` BIT(1)"}, - {"id bit(1)", "`id` BIT(1)"}, - {"id bit(64)", "`id` BIT(64)"}, - {"id tinyint", "`id` TINYINT"}, - {"id tinyint(255)", "`id` TINYINT(255)"}, - {"id bool", "`id` TINYINT(1)"}, - {"id boolean", "`id` TINYINT(1)"}, - {"id smallint", "`id` SMALLINT"}, - {"id smallint(255)", "`id` SMALLINT(255)"}, - {"id mediumint", "`id` MEDIUMINT"}, - {"id mediumint(255)", "`id` MEDIUMINT(255)"}, - {"id int", "`id` INT"}, - {"id int(255)", "`id` INT(255)"}, - {"id integer", "`id` INT"}, - {"id integer(255)", "`id` INT(255)"}, - {"id bigint", "`id` BIGINT"}, - {"id bigint(255)", "`id` BIGINT(255)"}, - {"id decimal", "`id` DECIMAL"}, - {"id decimal(10)", "`id` DECIMAL(10)"}, - {"id decimal(10,0)", "`id` DECIMAL(10,0)"}, - {"id decimal(65)", "`id` DECIMAL(65)"}, - {"id decimal(65,30)", "`id` DECIMAL(65,30)"}, - {"id dec(10,0)", "`id` DECIMAL(10,0)"}, - {"id numeric(10,0)", "`id` DECIMAL(10,0)"}, - {"id float(0)", "`id` FLOAT"}, - {"id float(24)", "`id` FLOAT"}, - {"id float(25)", "`id` DOUBLE"}, - {"id float(53)", "`id` DOUBLE"}, - {"id float(7,0)", "`id` FLOAT(7,0)"}, - {"id float(25,0)", "`id` FLOAT(25,0)"}, - {"id double(15,0)", "`id` DOUBLE(15,0)"}, - {"id double precision(15,0)", "`id` DOUBLE(15,0)"}, - {"id real(15,0)", "`id` DOUBLE(15,0)"}, - {"id year(4)", "`id` YEAR(4)"}, - {"id time", "`id` TIME"}, - {"id char", "`id` CHAR"}, - {"id char(0)", "`id` CHAR(0)"}, - {"id char(255)", "`id` CHAR(255)"}, - {"id national char(0)", "`id` CHAR(0)"}, - {"id binary", "`id` BINARY"}, - {"id varbinary(0)", "`id` VARBINARY(0)"}, - {"id varbinary(65535)", "`id` VARBINARY(65535)"}, - {"id tinyblob", "`id` TINYBLOB"}, - {"id tinytext", "`id` TINYTEXT"}, - {"id blob", "`id` BLOB"}, - {"id blob(0)", "`id` BLOB(0)"}, - {"id blob(65535)", "`id` BLOB(65535)"}, - {"id text(0)", "`id` TEXT(0)"}, - {"id text(65535)", "`id` TEXT(65535)"}, - {"id mediumblob", "`id` MEDIUMBLOB"}, - {"id mediumtext", "`id` MEDIUMTEXT"}, - {"id longblob", "`id` LONGBLOB"}, - {"id longtext", "`id` LONGTEXT"}, - {"id json", "`id` JSON"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*CreateTableStmt).Cols[0] - } - runNodeRestoreTest(t, testCases, "CREATE TABLE t (%s)", extractNodeFunc) -} - -func TestDDLTruncateTableStmtRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"truncate t1", "TRUNCATE TABLE `t1`"}, - {"truncate table t1", "TRUNCATE TABLE `t1`"}, - {"truncate a.t1", "TRUNCATE TABLE `a`.`t1`"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*TruncateTableStmt) - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestDDLDropTableStmtRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"drop table t1", "DROP TABLE `t1`"}, - {"drop table if exists t1", "DROP TABLE IF EXISTS `t1`"}, - {"drop temporary table t1", "DROP TEMPORARY TABLE `t1`"}, - {"drop temporary table if exists t1", "DROP TEMPORARY TABLE IF EXISTS `t1`"}, - {"DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`", "DROP TEMPORARY TABLE IF EXISTS `test`"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*DropTableStmt) - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestColumnPositionRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"", ""}, - {"first", "FIRST"}, - {"after b", "AFTER `b`"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*AlterTableStmt).Specs[0].Position - } - runNodeRestoreTest(t, testCases, "alter table t add column a varchar(255) %s", extractNodeFunc) -} - -func TestAlterTableSpecRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"ENGINE innodb", "ENGINE = innodb"}, - {"ENGINE = innodb", "ENGINE = innodb"}, - {"ENGINE = 'innodb'", "ENGINE = innodb"}, - {"ENGINE tokudb", "ENGINE = tokudb"}, - {"ENGINE = tokudb", "ENGINE = tokudb"}, - {"ENGINE = 'tokudb'", "ENGINE = tokudb"}, - {"DEFAULT CHARACTER SET utf8", "DEFAULT CHARACTER SET = UTF8"}, - {"DEFAULT CHARACTER SET = utf8", "DEFAULT CHARACTER SET = UTF8"}, - {"DEFAULT CHARSET utf8", "DEFAULT CHARACTER SET = UTF8"}, - {"DEFAULT CHARSET = utf8", "DEFAULT CHARACTER SET = UTF8"}, - {"DEFAULT COLLATE utf8_bin", "DEFAULT COLLATE = UTF8_BIN"}, - {"DEFAULT COLLATE = utf8_bin", "DEFAULT COLLATE = UTF8_BIN"}, - {"AUTO_INCREMENT 3", "AUTO_INCREMENT = 3"}, - {"AUTO_INCREMENT = 6", "AUTO_INCREMENT = 6"}, - {"COMMENT ''", "COMMENT = ''"}, - {"COMMENT 'system role'", "COMMENT = 'system role'"}, - {"COMMENT = 'system role'", "COMMENT = 'system role'"}, - {"AVG_ROW_LENGTH 12", "AVG_ROW_LENGTH = 12"}, - {"AVG_ROW_LENGTH = 6", "AVG_ROW_LENGTH = 6"}, - {"connection 'abc'", "CONNECTION = 'abc'"}, - {"CONNECTION = 'abc'", "CONNECTION = 'abc'"}, - {"checksum 1", "CHECKSUM = 1"}, - {"checksum = 0", "CHECKSUM = 0"}, - {"PASSWORD '123456'", "PASSWORD = '123456'"}, - {"PASSWORD = ''", "PASSWORD = ''"}, - {"compression 'NONE'", "COMPRESSION = 'NONE'"}, - {"compression = 'lz4'", "COMPRESSION = 'lz4'"}, - {"key_block_size 1024", "KEY_BLOCK_SIZE = 1024"}, - {"KEY_BLOCK_SIZE = 1024", "KEY_BLOCK_SIZE = 1024"}, - {"max_rows 1000", "MAX_ROWS = 1000"}, - {"max_rows = 1000", "MAX_ROWS = 1000"}, - {"min_rows 1000", "MIN_ROWS = 1000"}, - {"MIN_ROWS = 1000", "MIN_ROWS = 1000"}, - {"DELAY_KEY_WRITE 1", "DELAY_KEY_WRITE = 1"}, - {"DELAY_KEY_WRITE = 1000", "DELAY_KEY_WRITE = 1000"}, - {"ROW_FORMAT default", "ROW_FORMAT = DEFAULT"}, - {"ROW_FORMAT = default", "ROW_FORMAT = DEFAULT"}, - {"ROW_FORMAT = fixed", "ROW_FORMAT = FIXED"}, - {"ROW_FORMAT = compressed", "ROW_FORMAT = COMPRESSED"}, - {"ROW_FORMAT = compact", "ROW_FORMAT = COMPACT"}, - {"ROW_FORMAT = redundant", "ROW_FORMAT = REDUNDANT"}, - {"ROW_FORMAT = dynamic", "ROW_FORMAT = DYNAMIC"}, - {"ROW_FORMAT tokudb_default", "ROW_FORMAT = TOKUDB_DEFAULT"}, - {"ROW_FORMAT = tokudb_default", "ROW_FORMAT = TOKUDB_DEFAULT"}, - {"ROW_FORMAT = tokudb_fast", "ROW_FORMAT = TOKUDB_FAST"}, - {"ROW_FORMAT = tokudb_small", "ROW_FORMAT = TOKUDB_SMALL"}, - {"ROW_FORMAT = tokudb_zlib", "ROW_FORMAT = TOKUDB_ZLIB"}, - {"ROW_FORMAT = tokudb_zstd", "ROW_FORMAT = TOKUDB_ZSTD"}, - {"ROW_FORMAT = tokudb_quicklz", "ROW_FORMAT = TOKUDB_QUICKLZ"}, - {"ROW_FORMAT = tokudb_lzma", "ROW_FORMAT = TOKUDB_LZMA"}, - {"ROW_FORMAT = tokudb_snappy", "ROW_FORMAT = TOKUDB_SNAPPY"}, - {"ROW_FORMAT = tokudb_uncompressed", "ROW_FORMAT = TOKUDB_UNCOMPRESSED"}, - {"shard_row_id_bits 1", "SHARD_ROW_ID_BITS = 1"}, - {"shard_row_id_bits = 1", "SHARD_ROW_ID_BITS = 1"}, - {"CONVERT TO CHARACTER SET utf8", "CONVERT TO CHARACTER SET UTF8"}, - {"CONVERT TO CHARSET utf8", "CONVERT TO CHARACTER SET UTF8"}, - {"CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin", "CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, - {"CONVERT TO CHARSET utf8 COLLATE utf8_bin", "CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, - {"ADD COLUMN (a SMALLINT UNSIGNED)", "ADD COLUMN (`a` SMALLINT UNSIGNED)"}, - {"ADD COLUMN (a SMALLINT UNSIGNED, b varchar(255))", "ADD COLUMN (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, - {"ADD COLUMN a SMALLINT UNSIGNED", "ADD COLUMN `a` SMALLINT UNSIGNED"}, - {"ADD COLUMN a SMALLINT UNSIGNED FIRST", "ADD COLUMN `a` SMALLINT UNSIGNED FIRST"}, - {"ADD COLUMN a SMALLINT UNSIGNED AFTER b", "ADD COLUMN `a` SMALLINT UNSIGNED AFTER `b`"}, - {"ADD COLUMN name mediumtext CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL", "ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL"}, - {"ADD CONSTRAINT INDEX par_ind (parent_id)", "ADD INDEX `par_ind`(`parent_id`)"}, - {"ADD CONSTRAINT INDEX par_ind (parent_id(6))", "ADD INDEX `par_ind`(`parent_id`(6))"}, - {"ADD CONSTRAINT key par_ind (parent_id)", "ADD INDEX `par_ind`(`parent_id`)"}, - {"ADD CONSTRAINT unique par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, - {"ADD CONSTRAINT unique key par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, - {"ADD CONSTRAINT unique index par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, - {"ADD CONSTRAINT fulltext key full_id (parent_id)", "ADD FULLTEXT `full_id`(`parent_id`)"}, - {"ADD CONSTRAINT fulltext INDEX full_id (parent_id)", "ADD FULLTEXT `full_id`(`parent_id`)"}, - {"ADD CONSTRAINT PRIMARY KEY (id)", "ADD PRIMARY KEY(`id`)"}, - {"ADD CONSTRAINT PRIMARY KEY (id) key_block_size = 32 using hash comment 'hello'", "ADD PRIMARY KEY(`id`) KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, - {"ADD CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "ADD CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, - {"ADD CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "ADD CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"ADD CONSTRAINT fk_123 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "ADD CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, - {"DROP COLUMN a", "DROP COLUMN `a`"}, - {"DROP COLUMN a RESTRICT", "DROP COLUMN `a`"}, - {"DROP COLUMN a CASCADE", "DROP COLUMN `a`"}, - {"DROP PRIMARY KEY", "DROP PRIMARY KEY"}, - {"drop index a", "DROP INDEX `a`"}, - {"drop key a", "DROP INDEX `a`"}, - {"drop FOREIGN key a", "DROP FOREIGN KEY `a`"}, - {"MODIFY column a varchar(255)", "MODIFY COLUMN `a` VARCHAR(255)"}, - {"modify COLUMN a varchar(255) FIRST", "MODIFY COLUMN `a` VARCHAR(255) FIRST"}, - {"modify COLUMN a varchar(255) AFTER b", "MODIFY COLUMN `a` VARCHAR(255) AFTER `b`"}, - {"change column a b VARCHAR(255)", "CHANGE COLUMN `a` `b` VARCHAR(255)"}, - {"change COLUMN a b varchar(255) CHARACTER SET UTF8 BINARY", "CHANGE COLUMN `a` `b` VARCHAR(255) BINARY CHARACTER SET UTF8"}, - {"CHANGE column a b varchar(255) FIRST", "CHANGE COLUMN `a` `b` VARCHAR(255) FIRST"}, - {"change COLUMN a b varchar(255) AFTER c", "CHANGE COLUMN `a` `b` VARCHAR(255) AFTER `c`"}, - {"RENAME db1.t1", "RENAME AS `db1`.`t1`"}, - {"RENAME to db1.t1", "RENAME AS `db1`.`t1`"}, - {"RENAME as t1", "RENAME AS `t1`"}, - {"ALTER a SET DEFAULT 1", "ALTER COLUMN `a` SET DEFAULT 1"}, - {"ALTER a DROP DEFAULT", "ALTER COLUMN `a` DROP DEFAULT"}, - {"ALTER COLUMN a SET DEFAULT 1", "ALTER COLUMN `a` SET DEFAULT 1"}, - {"ALTER COLUMN a DROP DEFAULT", "ALTER COLUMN `a` DROP DEFAULT"}, - {"LOCK=NONE", "LOCK = NONE"}, - {"LOCK=DEFAULT", "LOCK = DEFAULT"}, - {"LOCK=SHARED", "LOCK = SHARED"}, - {"LOCK=EXCLUSIVE", "LOCK = EXCLUSIVE"}, - {"RENAME KEY a TO b", "RENAME INDEX `a` TO `b`"}, - {"RENAME INDEX a TO b", "RENAME INDEX `a` TO `b`"}, - {"ADD PARTITION", "ADD PARTITION"}, - {"ADD PARTITION ( PARTITION P1 VALUES LESS THAN (2010))", "ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010))"}, - {"ADD PARTITION ( PARTITION P2 VALUES LESS THAN MAXVALUE)", "ADD PARTITION (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, - {"ADD PARTITION (\nPARTITION P1 VALUES LESS THAN (2010),\nPARTITION P2 VALUES LESS THAN (2015),\nPARTITION P3 VALUES LESS THAN MAXVALUE)", "ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010), PARTITION `P2` VALUES LESS THAN (2015), PARTITION `P3` VALUES LESS THAN (MAXVALUE))"}, - {"ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT 'AP_START \\' AP_END')", "ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'AP_START '' AP_END')"}, - {"ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')", "ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')"}, - {"coalesce partition 3", "COALESCE PARTITION 3"}, - {"drop partition p1", "DROP PARTITION `p1`"}, - {"TRUNCATE PARTITION p0", "TRUNCATE PARTITION `p0`"}, - {"add stats_extended s1 cardinality(a,b)", "ADD STATS_EXTENDED `s1` CARDINALITY(`a`, `b`)"}, - {"add stats_extended if not exists s1 cardinality(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` CARDINALITY(`a`, `b`)"}, - {"add stats_extended s1 correlation(a,b)", "ADD STATS_EXTENDED `s1` CORRELATION(`a`, `b`)"}, - {"add stats_extended if not exists s1 correlation(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` CORRELATION(`a`, `b`)"}, - {"add stats_extended s1 dependency(a,b)", "ADD STATS_EXTENDED `s1` DEPENDENCY(`a`, `b`)"}, - {"add stats_extended if not exists s1 dependency(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` DEPENDENCY(`a`, `b`)"}, - {"drop stats_extended s1", "DROP STATS_EXTENDED `s1`"}, - {"drop stats_extended if exists s1", "DROP STATS_EXTENDED IF EXISTS `s1`"}, - {"placement policy p1", "PLACEMENT POLICY = `p1`"}, - {"placement policy p1 comment='aaa'", "PLACEMENT POLICY = `p1` COMMENT = 'aaa'"}, - {"partition p0 placement policy p1", "PARTITION `p0` PLACEMENT POLICY = `p1`"}, - } - extractNodeFunc := func(node Node) Node { - return node.(*AlterTableStmt).Specs[0] - } - runNodeRestoreTest(t, testCases, "ALTER TABLE t %s", extractNodeFunc) -} - -func TestAlterTableWithSpecialCommentRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"placement policy p1", "/*T![placement] PLACEMENT POLICY = `p1` */"}, - {"placement policy p1 comment='aaa'", "/*T![placement] PLACEMENT POLICY = `p1` */ COMMENT = 'aaa'"}, - {"partition p0 placement policy p1", "/*T![placement] PARTITION `p0` PLACEMENT POLICY = `p1` */"}, - } - - extractNodeFunc := func(node Node) Node { - return node.(*AlterTableStmt).Specs[0] - } - runNodeRestoreTestWithFlags(t, testCases, "ALTER TABLE t %s", extractNodeFunc, format.DefaultRestoreFlags|format.RestoreTiDBSpecialComment) -} - -func TestAlterTableOptionRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"ALTER TABLE t ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8", "ALTER TABLE `t` ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8"}, - {"ALTER TABLE t ROW_FORMAT = COMPRESSED, KEY_BLOCK_SIZE = 8", "ALTER TABLE `t` ROW_FORMAT = COMPRESSED, KEY_BLOCK_SIZE = 8"}, - } - extractNodeFunc := func(node Node) Node { - return node - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestAdminRepairTableRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"ADMIN REPAIR TABLE t CREATE TABLE t (a int)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` INT)"}, - {"ADMIN REPAIR TABLE t CREATE TABLE t (a char(1), b int)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` CHAR(1),`b` INT)"}, - {"ADMIN REPAIR TABLE t CREATE TABLE t (a TINYINT UNSIGNED)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` TINYINT UNSIGNED)"}, - } - extractNodeFunc := func(node Node) Node { - return node - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestSequenceRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"create sequence seq", "CREATE SEQUENCE `seq`"}, - {"create sequence if not exists seq", "CREATE SEQUENCE IF NOT EXISTS `seq`"}, - {"create sequence if not exists seq", "CREATE SEQUENCE IF NOT EXISTS `seq`"}, - {"create sequence if not exists seq increment 1", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, - {"create sequence if not exists seq increment = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, - {"create sequence if not exists seq minvalue 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, - {"create sequence if not exists seq minvalue = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, - {"create sequence if not exists seq nominvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, - {"create sequence if not exists seq no minvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, - {"create sequence if not exists seq maxvalue 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, - {"create sequence if not exists seq maxvalue = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, - {"create sequence if not exists seq nomaxvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, - {"create sequence if not exists seq no maxvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, - {"create sequence if not exists seq start 1", "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, - {"create sequence if not exists seq start with 1", "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, - {"create sequence if not exists seq cache 1", "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, - {"create sequence if not exists seq nocache", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, - {"create sequence if not exists seq no cache", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, - {"create sequence if not exists seq cycle", "CREATE SEQUENCE IF NOT EXISTS `seq` CYCLE"}, - {"create sequence if not exists seq nocycle", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, - {"create sequence if not exists seq no cycle", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, - {"create sequence seq increment 1 minvalue 0 maxvalue 1000", "CREATE SEQUENCE `seq` INCREMENT BY 1 MINVALUE 0 MAXVALUE 1000"}, - {"create sequence seq minvalue 0 maxvalue 1000 increment 1", "CREATE SEQUENCE `seq` MINVALUE 0 MAXVALUE 1000 INCREMENT BY 1"}, - {"create sequence seq cache = 1 minvalue 0 maxvalue -1000", "CREATE SEQUENCE `seq` CACHE 1 MINVALUE 0 MAXVALUE -1000"}, - {"create sequence seq increment -1 minvalue 0 maxvalue -1000", "CREATE SEQUENCE `seq` INCREMENT BY -1 MINVALUE 0 MAXVALUE -1000"}, - {"create sequence seq nocycle nocache maxvalue 1000 cache 1", "CREATE SEQUENCE `seq` NOCYCLE NOCACHE MAXVALUE 1000 CACHE 1"}, - {"create sequence seq increment -1 no minvalue no maxvalue cache = 1", "CREATE SEQUENCE `seq` INCREMENT BY -1 NO MINVALUE NO MAXVALUE CACHE 1"}, - {"create sequence if not exists seq increment 1 minvalue 0 nomaxvalue cache 100 nocycle", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1 MINVALUE 0 NO MAXVALUE CACHE 100 NOCYCLE"}, - - // test drop sequence - {"drop sequence seq", "DROP SEQUENCE `seq`"}, - {"drop sequence seq, seq2", "DROP SEQUENCE `seq`, `seq2`"}, - {"drop sequence if exists seq, seq2", "DROP SEQUENCE IF EXISTS `seq`, `seq2`"}, - {"drop sequence if exists seq", "DROP SEQUENCE IF EXISTS `seq`"}, - {"drop sequence sequence", "DROP SEQUENCE `sequence`"}, - } - extractNodeFunc := func(node Node) Node { - return node - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestDropIndexRestore(t *testing.T) { - sourceSQL := "drop index if exists idx on t" - cases := []struct { - flags format.RestoreFlags - expectSQL string - }{ - {format.DefaultRestoreFlags, "DROP INDEX IF EXISTS `idx` ON `t`"}, - {format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "DROP INDEX /*T! IF EXISTS */`idx` ON `t`"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestAlterDatabaseRestore(t *testing.T) { - sourceSQL1 := "alter database db1 charset='ascii'" - sourceSQL2 := "alter database db1 collate='ascii_bin'" - sourceSQL3 := "alter database db1 placement policy p1" - sourceSQL4 := "alter database db1 placement policy p1 charset='ascii'" - - cases := []struct { - sourceSQL string - flags format.RestoreFlags - expectSQL string - }{ - {sourceSQL1, format.DefaultRestoreFlags, "ALTER DATABASE `db1` CHARACTER SET = ascii"}, - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` CHARACTER SET = ascii"}, - {sourceSQL2, format.DefaultRestoreFlags, "ALTER DATABASE `db1` COLLATE = ascii_bin"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` COLLATE = ascii_bin"}, - {sourceSQL3, format.DefaultRestoreFlags, "ALTER DATABASE `db1` PLACEMENT POLICY = `p1`"}, - {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */"}, - {sourceSQL4, format.DefaultRestoreFlags, "ALTER DATABASE `db1` PLACEMENT POLICY = `p1` CHARACTER SET = ascii"}, - {sourceSQL4, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` /*T![placement] PLACEMENT POLICY = `p1` */ CHARACTER SET = ascii"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestCreatePlacementPolicyRestore(t *testing.T) { - sourceSQL1 := "create placement policy p1 primary_region=\"r1\" regions='r1,r2' followers=1" - sourceSQL2 := "create placement policy if not exists p1 primary_region=\"r1\" regions='r1,r2' followers=1" - sourceSQL3 := "create or replace placement policy p1 followers=1" - cases := []struct { - sourceSQL string - flags format.RestoreFlags - expectSQL string - }{ - {sourceSQL1, format.DefaultRestoreFlags, "CREATE PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, - {sourceSQL2, format.DefaultRestoreFlags, "CREATE PLACEMENT POLICY IF NOT EXISTS `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE PLACEMENT POLICY IF NOT EXISTS `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, - {sourceSQL3, format.DefaultRestoreFlags, "CREATE OR REPLACE PLACEMENT POLICY `p1` FOLLOWERS = 1"}, - {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE OR REPLACE PLACEMENT POLICY `p1` FOLLOWERS = 1 */"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestAlterPlacementPolicyRestore(t *testing.T) { - sourceSQL := "alter placement policy p1 primary_region=\"r1\" regions='r1,r2' followers=1" - cases := []struct { - flags format.RestoreFlags - expectSQL string - }{ - {format.DefaultRestoreFlags, "ALTER PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, - {format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] ALTER PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestDropPlacementPolicyRestore(t *testing.T) { - sourceSQL1 := "drop placement policy p1" - sourceSQL2 := "drop placement policy if exists p1" - cases := []struct { - sourceSQL string - flags format.RestoreFlags - expectSQL string - }{ - {sourceSQL1, format.DefaultRestoreFlags, "DROP PLACEMENT POLICY `p1`"}, - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] DROP PLACEMENT POLICY `p1` */"}, - {sourceSQL2, format.DefaultRestoreFlags, "DROP PLACEMENT POLICY IF EXISTS `p1`"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] DROP PLACEMENT POLICY IF EXISTS `p1` */"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestRemovePlacementRestore(t *testing.T) { - f := format.DefaultRestoreFlags | format.SkipPlacementRuleForRestore - cases := []struct { - sourceSQL string - expectSQL string - }{ - { - "CREATE TABLE t1 (id BIGINT NOT NULL PRIMARY KEY auto_increment, b varchar(255)) PLACEMENT POLICY=placement1;", - "CREATE TABLE `t1` (`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,`b` VARCHAR(255)) ", - }, - { - "CREATE TABLE `t1` (\n `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */", - "CREATE TABLE `t1` (`a` INT(11) DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = UTF8MB4 DEFAULT COLLATE = UTF8MB4_BIN ", - }, - { - "CREATE TABLE t4 (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL) PARTITION BY RANGE( YEAR(joined) ) (PARTITION p0 VALUES LESS THAN (1960) PLACEMENT POLICY=p1,PARTITION p1 VALUES LESS THAN (1970),PARTITION p2 VALUES LESS THAN (1980),PARTITION p3 VALUES LESS THAN (1990),PARTITION p4 VALUES LESS THAN MAXVALUE);", - "CREATE TABLE `t4` (`firstname` VARCHAR(25) NOT NULL,`lastname` VARCHAR(25) NOT NULL,`username` VARCHAR(16) NOT NULL,`email` VARCHAR(35),`joined` DATE NOT NULL) PARTITION BY RANGE (YEAR(`joined`)) (PARTITION `p0` VALUES LESS THAN (1960) ,PARTITION `p1` VALUES LESS THAN (1970),PARTITION `p2` VALUES LESS THAN (1980),PARTITION `p3` VALUES LESS THAN (1990),PARTITION `p4` VALUES LESS THAN (MAXVALUE))", - }, - { - "ALTER TABLE t3 PLACEMENT POLICY=DEFAULT;", - "ALTER TABLE `t3`", - }, - { - "ALTER TABLE t1 PLACEMENT POLICY=p10", - "ALTER TABLE `t1`", - }, - { - "ALTER TABLE t1 PLACEMENT POLICY=p10, add d text(50)", - "ALTER TABLE `t1` ADD COLUMN `d` TEXT(50)", - }, - { - "alter table tp PARTITION p1 placement policy p2", - "", - }, - { - "alter table t add d text(50) PARTITION p1 placement policy p2", - "ALTER TABLE `t` ADD COLUMN `d` TEXT(50)", - }, - { - "alter table tp set tiflash replica 1 PARTITION p1 placement policy p2", - "ALTER TABLE `tp` SET TIFLASH REPLICA 1", - }, - { - "ALTER DATABASE TestResetPlacementDB PLACEMENT POLICY SET DEFAULT", - "", - }, - - { - "ALTER DATABASE TestResetPlacementDB PLACEMENT POLICY p1 charset utf8mb4", - "ALTER DATABASE `TestResetPlacementDB` CHARACTER SET = utf8mb4", - }, - { - "/*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */", - "", - }, - { - "ALTER PLACEMENT POLICY p3 PRIMARY_REGION='us-east-1' REGIONS='us-east-1,us-east-2,us-west-1';", - "", - }, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlagsStmtChange(t, testCases, "%s", extractNodeFunc, f) - } -} - -func TestFlashBackDatabaseRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"flashback database M", "FLASHBACK DATABASE `M`"}, - {"flashback schema M", "FLASHBACK DATABASE `M`"}, - {"flashback database M to n", "FLASHBACK DATABASE `M` TO `n`"}, - {"flashback schema M to N", "FLASHBACK DATABASE `M` TO `N`"}, - } - extractNodeFunc := func(node Node) Node { - return node - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestTableOptionTTLRestore(t *testing.T) { - sourceSQL1 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR" - sourceSQL2 := "alter table t ttl_enable = 'OFF'" - sourceSQL3 := "alter table t remove ttl" - cases := []struct { - sourceSQL string - flags format.RestoreFlags - expectSQL string - }{ - {sourceSQL1, format.DefaultRestoreFlags, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR"}, - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */"}, - {sourceSQL2, format.DefaultRestoreFlags, "ALTER TABLE `t` TTL_ENABLE = 'OFF'"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER TABLE `t` /*T![ttl] TTL_ENABLE = 'OFF' */"}, - {sourceSQL3, format.DefaultRestoreFlags, "ALTER TABLE `t` REMOVE TTL"}, - {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER TABLE `t` /*T![ttl] REMOVE TTL */"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} - -func TestTableOptionTTLRestoreWithTTLEnableOffFlag(t *testing.T) { - sourceSQL1 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR" - sourceSQL2 := "alter table t ttl_enable = 'ON'" - sourceSQL3 := "alter table t remove ttl" - sourceSQL4 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR ttl_enable = 'ON'" - sourceSQL5 := "alter table t ttl_enable = 'ON' placement policy p1" - cases := []struct { - sourceSQL string - flags format.RestoreFlags - expectSQL string - }{ - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, - {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */ /*T![ttl] TTL_ENABLE = 'OFF' */"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "ALTER TABLE `t`"}, - {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t`"}, - {sourceSQL3, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` REMOVE TTL"}, - {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` /*T![ttl] REMOVE TTL */"}, - {sourceSQL4, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, - {sourceSQL4, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */ /*T![ttl] TTL_ENABLE = 'OFF' */"}, - {sourceSQL5, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` /*T![placement] PLACEMENT POLICY = `p1` */"}, - } - - extractNodeFunc := func(node Node) Node { - return node - } - - for _, ca := range cases { - testCases := []NodeRestoreTestCase{ - {ca.sourceSQL, ca.expectSQL}, - } - runNodeRestoreTestWithFlagsStmtChange(t, testCases, "%s", extractNodeFunc, ca.flags) - } -} diff --git a/parser/ast/format_test.go b/parser/ast/format_test.go deleted file mode 100644 index 59424d2876d34..0000000000000 --- a/parser/ast/format_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package ast_test - -import ( - "bytes" - "fmt" - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/stretchr/testify/require" -) - -func getDefaultCharsetAndCollate() (string, string) { - return "utf8", "utf8_bin" -} - -func TestAstFormat(t *testing.T) { - var testcases = []struct { - input string - output string - }{ - // Literals. - {`null`, `NULL`}, - {`true`, `TRUE`}, - {`350`, `350`}, - {`001e-12`, `1e-12`}, // Float. - {`345.678`, `345.678`}, - {`00.0001000`, `0.0001000`}, // Decimal. - {`null`, `NULL`}, - {`"Hello, world"`, `"Hello, world"`}, - {`'Hello, world'`, `"Hello, world"`}, - {`'Hello, "world"'`, `"Hello, \"world\""`}, - {`_utf8'你好'`, `"你好"`}, - {`x'bcde'`, "x'bcde'"}, - {`x''`, "x''"}, - {`x'0035'`, "x'0035'"}, // Shouldn't trim leading zero. - {`b'00111111'`, `b'111111'`}, - {`time'10:10:10.123'`, ast.TimeLiteral + `("10:10:10.123")`}, - {`timestamp'1999-01-01 10:0:0.123'`, ast.TimestampLiteral + `("1999-01-01 10:0:0.123")`}, - {`date '1700-01-01'`, ast.DateLiteral + `("1700-01-01")`}, - - // Expressions. - {`f between 30 and 50`, "`f` BETWEEN 30 AND 50"}, - {`f not between 30 and 50`, "`f` NOT BETWEEN 30 AND 50"}, - {`345 + " hello "`, `345 + " hello "`}, - {`"hello world" >= 'hello world'`, `"hello world" >= "hello world"`}, - {`case 3 when 1 then false else true end`, `CASE 3 WHEN 1 THEN FALSE ELSE TRUE END`}, - {`database.table.column`, "`database`.`table`.`column`"}, // ColumnNameExpr - {`3 is null`, `3 IS NULL`}, - {`3 is not null`, `3 IS NOT NULL`}, - {`3 is true`, `3 IS TRUE`}, - {`3 is not true`, `3 IS NOT TRUE`}, - {`3 is false`, `3 IS FALSE`}, - {` ( x is false )`, "(`x` IS FALSE)"}, - {`3 in ( a,b,"h",6 )`, "3 IN (`a`,`b`,\"h\",6)"}, - {`3 not in ( a,b,"h",6 )`, "3 NOT IN (`a`,`b`,\"h\",6)"}, - {`"abc" like '%b%'`, `"abc" LIKE "%b%"`}, - {`"abc" not like '%b%'`, `"abc" NOT LIKE "%b%"`}, - {`"abc" like '%b%' escape '_'`, `"abc" LIKE "%b%" ESCAPE '_'`}, - {`"abc" regexp '.*bc?'`, `"abc" REGEXP ".*bc?"`}, - {`"abc" not regexp '.*bc?'`, `"abc" NOT REGEXP ".*bc?"`}, - {`- 4`, `-4`}, - {`- ( - 4 ) `, `-(-4)`}, - {`a%b`, "`a` % `b`"}, - {`a%b+6`, "`a` % `b` + 6"}, - {`a%(b+6)`, "`a` % (`b` + 6)"}, - // Functions. - {` json_extract ( a,'$.b',"$.\"c d\"" ) `, "json_extract(`a`, \"$.b\", \"$.\\\"c d\\\"\")"}, - {` length ( a )`, "length(`a`)"}, - {`a -> '$.a'`, "json_extract(`a`, \"$.a\")"}, - {`a.b ->> '$.a'`, "json_unquote(json_extract(`a`.`b`, \"$.a\"))"}, - {`DATE_ADD('1970-01-01', interval 3 second)`, `date_add("1970-01-01", INTERVAL 3 SECOND)`}, - {`TIMESTAMPDIFF(month, '2001-01-01', '2001-02-02 12:03:05.123')`, `timestampdiff(MONTH, "2001-01-01", "2001-02-02 12:03:05.123")`}, - // Cast, Convert and Binary. - // There should not be spaces between 'cast' and '(' unless 'IGNORE_SPACE' mode is set. - // see: https://dev.mysql.com/doc/refman/5.7/en/function-resolution.html - {` cast( a as signed ) `, "CAST(`a` AS SIGNED)"}, - {` cast( a as unsigned integer) `, "CAST(`a` AS UNSIGNED)"}, - {` cast( a as char(3) binary) `, "CAST(`a` AS BINARY(3))"}, - {` cast( a as decimal ) `, "CAST(`a` AS DECIMAL(10))"}, - {` cast( a as decimal (3) ) `, "CAST(`a` AS DECIMAL(3))"}, - {` cast( a as decimal (3,3) ) `, "CAST(`a` AS DECIMAL(3, 3))"}, - {` ((case when (c0 = 0) then 0 when (c0 > 0) then (c1 / c0) end)) `, "((CASE WHEN (`c0` = 0) THEN 0 WHEN (`c0` > 0) THEN (`c1` / `c0`) END))"}, - {` convert (a, signed) `, "CONVERT(`a`, SIGNED)"}, - {` binary "hello"`, `BINARY "hello"`}, - } - for _, tt := range testcases { - expr := fmt.Sprintf("select %s", tt.input) - charset, collation := getDefaultCharsetAndCollate() - stmts, _, err := parser.New().Parse(expr, charset, collation) - node := stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr - require.NoError(t, err) - - writer := bytes.NewBufferString("") - node.Format(writer) - require.Equal(t, tt.output, writer.String()) - } -} diff --git a/parser/ast/misc.go b/parser/ast/misc.go deleted file mode 100644 index 3eed34fd39d01..0000000000000 --- a/parser/ast/misc.go +++ /dev/null @@ -1,4090 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "bytes" - "fmt" - "net/url" - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" -) - -var ( - _ StmtNode = &AdminStmt{} - _ StmtNode = &AlterUserStmt{} - _ StmtNode = &AlterRangeStmt{} - _ StmtNode = &BeginStmt{} - _ StmtNode = &BinlogStmt{} - _ StmtNode = &CommitStmt{} - _ StmtNode = &CreateUserStmt{} - _ StmtNode = &DeallocateStmt{} - _ StmtNode = &DoStmt{} - _ StmtNode = &ExecuteStmt{} - _ StmtNode = &ExplainStmt{} - _ StmtNode = &GrantStmt{} - _ StmtNode = &PrepareStmt{} - _ StmtNode = &RollbackStmt{} - _ StmtNode = &SetPwdStmt{} - _ StmtNode = &SetRoleStmt{} - _ StmtNode = &SetDefaultRoleStmt{} - _ StmtNode = &SetStmt{} - _ StmtNode = &SetSessionStatesStmt{} - _ StmtNode = &UseStmt{} - _ StmtNode = &FlushStmt{} - _ StmtNode = &KillStmt{} - _ StmtNode = &CreateBindingStmt{} - _ StmtNode = &DropBindingStmt{} - _ StmtNode = &SetBindingStmt{} - _ StmtNode = &ShutdownStmt{} - _ StmtNode = &RestartStmt{} - _ StmtNode = &RenameUserStmt{} - _ StmtNode = &HelpStmt{} - _ StmtNode = &PlanReplayerStmt{} - _ StmtNode = &CompactTableStmt{} - _ StmtNode = &SetResourceGroupStmt{} - - _ Node = &PrivElem{} - _ Node = &VariableAssignment{} -) - -// Isolation level constants. -const ( - ReadCommitted = "READ-COMMITTED" - ReadUncommitted = "READ-UNCOMMITTED" - Serializable = "SERIALIZABLE" - RepeatableRead = "REPEATABLE-READ" - - PumpType = "PUMP" - DrainerType = "DRAINER" -) - -// Transaction mode constants. -const ( - Optimistic = "OPTIMISTIC" - Pessimistic = "PESSIMISTIC" -) - -// TypeOpt is used for parsing data type option from SQL. -type TypeOpt struct { - IsUnsigned bool - IsZerofill bool -} - -// FloatOpt is used for parsing floating-point type option from SQL. -// See http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html -type FloatOpt struct { - Flen int - Decimal int -} - -// AuthOption is used for parsing create use statement. -type AuthOption struct { - // ByAuthString set as true, if AuthString is used for authorization. Otherwise, authorization is done by HashString. - ByAuthString bool - AuthString string - ByHashString bool - HashString string - AuthPlugin string -} - -// Restore implements Node interface. -func (n *AuthOption) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("IDENTIFIED") - if n.AuthPlugin != "" { - ctx.WriteKeyWord(" WITH ") - ctx.WriteString(n.AuthPlugin) - } - if n.ByAuthString { - ctx.WriteKeyWord(" BY ") - ctx.WriteString(n.AuthString) - } else if n.ByHashString { - ctx.WriteKeyWord(" AS ") - ctx.WriteString(n.HashString) - } - return nil -} - -// TraceStmt is a statement to trace what sql actually does at background. -type TraceStmt struct { - stmtNode - - Stmt StmtNode - Format string - - TracePlan bool - TracePlanTarget string -} - -// Restore implements Node interface. -func (n *TraceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("TRACE ") - if n.TracePlan { - ctx.WriteKeyWord("PLAN ") - if n.TracePlanTarget != "" { - ctx.WriteKeyWord("TARGET") - ctx.WritePlain(" = ") - ctx.WriteString(n.TracePlanTarget) - ctx.WritePlain(" ") - } - } else if n.Format != "row" { - ctx.WriteKeyWord("FORMAT") - ctx.WritePlain(" = ") - ctx.WriteString(n.Format) - ctx.WritePlain(" ") - } - if err := n.Stmt.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore TraceStmt.Stmt") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *TraceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*TraceStmt) - node, ok := n.Stmt.Accept(v) - if !ok { - return n, false - } - n.Stmt = node.(StmtNode) - return v.Leave(n) -} - -// ExplainForStmt is a statement to provite information about how is SQL statement executeing -// in connection #ConnectionID -// See https://dev.mysql.com/doc/refman/5.7/en/explain.html -type ExplainForStmt struct { - stmtNode - - Format string - ConnectionID uint64 -} - -// Restore implements Node interface. -func (n *ExplainForStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("EXPLAIN ") - ctx.WriteKeyWord("FORMAT ") - ctx.WritePlain("= ") - ctx.WriteString(n.Format) - ctx.WritePlain(" ") - ctx.WriteKeyWord("FOR ") - ctx.WriteKeyWord("CONNECTION ") - ctx.WritePlain(strconv.FormatUint(n.ConnectionID, 10)) - return nil -} - -// Accept implements Node Accept interface. -func (n *ExplainForStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ExplainForStmt) - return v.Leave(n) -} - -// ExplainStmt is a statement to provide information about how is SQL statement executed -// or get columns information in a table. -// See https://dev.mysql.com/doc/refman/5.7/en/explain.html -type ExplainStmt struct { - stmtNode - - Stmt StmtNode - Format string - Analyze bool -} - -// Restore implements Node interface. -func (n *ExplainStmt) Restore(ctx *format.RestoreCtx) error { - if showStmt, ok := n.Stmt.(*ShowStmt); ok { - ctx.WriteKeyWord("DESC ") - if err := showStmt.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Table") - } - if showStmt.Column != nil { - ctx.WritePlain(" ") - if err := showStmt.Column.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Column") - } - } - return nil - } - ctx.WriteKeyWord("EXPLAIN ") - if n.Analyze { - ctx.WriteKeyWord("ANALYZE ") - } - if !n.Analyze || strings.ToLower(n.Format) != "row" { - ctx.WriteKeyWord("FORMAT ") - ctx.WritePlain("= ") - ctx.WriteString(n.Format) - ctx.WritePlain(" ") - } - if err := n.Stmt.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore ExplainStmt.Stmt") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ExplainStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ExplainStmt) - node, ok := n.Stmt.Accept(v) - if !ok { - return n, false - } - n.Stmt = node.(StmtNode) - return v.Leave(n) -} - -// PlanReplayerStmt is a statement to dump or load information for recreating plans -type PlanReplayerStmt struct { - stmtNode - - Stmt StmtNode - Analyze bool - Load bool - HistoricalStatsInfo *AsOfClause - - // Capture indicates 'plan replayer capture ' - Capture bool - // Remove indicates `plan replayer capture remove - Remove bool - - SQLDigest string - PlanDigest string - - // File is used to store 2 cases: - // 1. plan replayer load 'file'; - // 2. plan replayer dump explain 'file' - File string - - // Fields below are currently useless. - - // Where is the where clause in select statement. - Where ExprNode - // OrderBy is the ordering expression list. - OrderBy *OrderByClause - // Limit is the limit clause. - Limit *Limit -} - -// Restore implements Node interface. -func (n *PlanReplayerStmt) Restore(ctx *format.RestoreCtx) error { - if n.Load { - ctx.WriteKeyWord("PLAN REPLAYER LOAD ") - ctx.WriteString(n.File) - return nil - } - if n.Capture { - ctx.WriteKeyWord("PLAN REPLAYER CAPTURE ") - ctx.WriteString(n.SQLDigest) - ctx.WriteKeyWord(" ") - ctx.WriteString(n.PlanDigest) - return nil - } - if n.Remove { - ctx.WriteKeyWord("PLAN REPLAYER CAPTURE REMOVE ") - ctx.WriteString(n.SQLDigest) - ctx.WriteKeyWord(" ") - ctx.WriteString(n.PlanDigest) - return nil - } - - ctx.WriteKeyWord("PLAN REPLAYER DUMP ") - - if n.HistoricalStatsInfo != nil { - ctx.WriteKeyWord("WITH STATS ") - if err := n.HistoricalStatsInfo.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.HistoricalStatsInfo") - } - ctx.WriteKeyWord(" ") - } - if n.Analyze { - ctx.WriteKeyWord("EXPLAIN ANALYZE ") - } else { - ctx.WriteKeyWord("EXPLAIN ") - } - if n.Stmt == nil { - if len(n.File) > 0 { - ctx.WriteString(n.File) - return nil - } - ctx.WriteKeyWord("SLOW QUERY") - if n.Where != nil { - ctx.WriteKeyWord(" WHERE ") - if err := n.Where.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Where") - } - } - if n.OrderBy != nil { - ctx.WriteKeyWord(" ") - if err := n.OrderBy.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.OrderBy") - } - } - if n.Limit != nil { - ctx.WriteKeyWord(" ") - if err := n.Limit.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Limit") - } - } - return nil - } - if err := n.Stmt.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Stmt") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *PlanReplayerStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - n = newNode.(*PlanReplayerStmt) - - if n.Load { - return v.Leave(n) - } - - if n.HistoricalStatsInfo != nil { - info, ok := n.HistoricalStatsInfo.Accept(v) - if !ok { - return n, false - } - n.HistoricalStatsInfo = info.(*AsOfClause) - } - - if n.Stmt == nil { - if n.Where != nil { - node, ok := n.Where.Accept(v) - if !ok { - return n, false - } - n.Where = node.(ExprNode) - } - - if n.OrderBy != nil { - node, ok := n.OrderBy.Accept(v) - if !ok { - return n, false - } - n.OrderBy = node.(*OrderByClause) - } - - if n.Limit != nil { - node, ok := n.Limit.Accept(v) - if !ok { - return n, false - } - n.Limit = node.(*Limit) - } - return v.Leave(n) - } - - node, ok := n.Stmt.Accept(v) - if !ok { - return n, false - } - n.Stmt = node.(StmtNode) - return v.Leave(n) -} - -type CompactReplicaKind string - -const ( - // CompactReplicaKindAll means compacting both TiKV and TiFlash replicas. - CompactReplicaKindAll = "ALL" - - // CompactReplicaKindTiFlash means compacting TiFlash replicas. - CompactReplicaKindTiFlash = "TIFLASH" - - // CompactReplicaKindTiKV means compacting TiKV replicas. - CompactReplicaKindTiKV = "TIKV" -) - -// CompactTableStmt is a statement to manually compact a table. -type CompactTableStmt struct { - stmtNode - - Table *TableName - PartitionNames []model.CIStr - ReplicaKind CompactReplicaKind -} - -// Restore implements Node interface. -func (n *CompactTableStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ALTER TABLE ") - n.Table.restoreName(ctx) - - ctx.WriteKeyWord(" COMPACT") - if len(n.PartitionNames) != 0 { - ctx.WriteKeyWord(" PARTITION ") - for i, partition := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(partition.O) - } - } - if n.ReplicaKind != CompactReplicaKindAll { - ctx.WriteKeyWord(" ") - // Note: There is only TiFlash replica available now. TiKV will be added later. - ctx.WriteKeyWord(string(n.ReplicaKind)) - ctx.WriteKeyWord(" REPLICA") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CompactTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CompactTableStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - return v.Leave(n) -} - -// PrepareStmt is a statement to prepares a SQL statement which contains placeholders, -// and it is executed with ExecuteStmt and released with DeallocateStmt. -// See https://dev.mysql.com/doc/refman/5.7/en/prepare.html -type PrepareStmt struct { - stmtNode - - Name string - SQLText string - SQLVar *VariableExpr -} - -// Restore implements Node interface. -func (n *PrepareStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("PREPARE ") - ctx.WriteName(n.Name) - ctx.WriteKeyWord(" FROM ") - if n.SQLText != "" { - ctx.WriteString(n.SQLText) - return nil - } - if n.SQLVar != nil { - if err := n.SQLVar.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore PrepareStmt.SQLVar") - } - return nil - } - return errors.New("An error occurred while restore PrepareStmt") -} - -// Accept implements Node Accept interface. -func (n *PrepareStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*PrepareStmt) - if n.SQLVar != nil { - node, ok := n.SQLVar.Accept(v) - if !ok { - return n, false - } - n.SQLVar = node.(*VariableExpr) - } - return v.Leave(n) -} - -// DeallocateStmt is a statement to release PreparedStmt. -// See https://dev.mysql.com/doc/refman/5.7/en/deallocate-prepare.html -type DeallocateStmt struct { - stmtNode - - Name string -} - -// Restore implements Node interface. -func (n *DeallocateStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DEALLOCATE PREPARE ") - ctx.WriteName(n.Name) - return nil -} - -// Accept implements Node Accept interface. -func (n *DeallocateStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DeallocateStmt) - return v.Leave(n) -} - -// Prepared represents a prepared statement. -type Prepared struct { - Stmt StmtNode - StmtType string - Params []ParamMarkerExpr - SchemaVersion int64 - CachedPlan interface{} - CachedNames interface{} -} - -// ExecuteStmt is a statement to execute PreparedStmt. -// See https://dev.mysql.com/doc/refman/5.7/en/execute.html -type ExecuteStmt struct { - stmtNode - - Name string - UsingVars []ExprNode - BinaryArgs interface{} - PrepStmt interface{} // the corresponding prepared statement - IdxInMulti int - - // FromGeneralStmt indicates whether this execute-stmt is converted from a general query. - // e.g. select * from t where a>2 --> execute 'select * from t where a>?' using 2 - FromGeneralStmt bool -} - -// Restore implements Node interface. -func (n *ExecuteStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("EXECUTE ") - ctx.WriteName(n.Name) - if len(n.UsingVars) > 0 { - ctx.WriteKeyWord(" USING ") - for i, val := range n.UsingVars { - if i != 0 { - ctx.WritePlain(",") - } - if err := val.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore ExecuteStmt.UsingVars index %d", i) - } - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *ExecuteStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ExecuteStmt) - for i, val := range n.UsingVars { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.UsingVars[i] = node.(ExprNode) - } - return v.Leave(n) -} - -// BeginStmt is a statement to start a new transaction. -// See https://dev.mysql.com/doc/refman/5.7/en/commit.html -type BeginStmt struct { - stmtNode - Mode string - CausalConsistencyOnly bool - ReadOnly bool - // AS OF is used to read the data at a specific point of time. - // Should only be used when ReadOnly is true. - AsOf *AsOfClause -} - -// Restore implements Node interface. -func (n *BeginStmt) Restore(ctx *format.RestoreCtx) error { - if n.Mode == "" { - if n.ReadOnly { - ctx.WriteKeyWord("START TRANSACTION READ ONLY") - if n.AsOf != nil { - ctx.WriteKeyWord(" ") - return n.AsOf.Restore(ctx) - } - } else if n.CausalConsistencyOnly { - ctx.WriteKeyWord("START TRANSACTION WITH CAUSAL CONSISTENCY ONLY") - } else { - ctx.WriteKeyWord("START TRANSACTION") - } - } else { - ctx.WriteKeyWord("BEGIN ") - ctx.WriteKeyWord(n.Mode) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *BeginStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - if n.AsOf != nil { - node, ok := n.AsOf.Accept(v) - if !ok { - return n, false - } - n.AsOf = node.(*AsOfClause) - } - - n = newNode.(*BeginStmt) - return v.Leave(n) -} - -// BinlogStmt is an internal-use statement. -// We just parse and ignore it. -// See http://dev.mysql.com/doc/refman/5.7/en/binlog.html -type BinlogStmt struct { - stmtNode - Str string -} - -// Restore implements Node interface. -func (n *BinlogStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("BINLOG ") - ctx.WriteString(n.Str) - return nil -} - -// Accept implements Node Accept interface. -func (n *BinlogStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*BinlogStmt) - return v.Leave(n) -} - -// CompletionType defines completion_type used in COMMIT and ROLLBACK statements -type CompletionType int8 - -const ( - // CompletionTypeDefault refers to NO_CHAIN - CompletionTypeDefault CompletionType = iota - CompletionTypeChain - CompletionTypeRelease -) - -func (n CompletionType) Restore(ctx *format.RestoreCtx) error { - switch n { - case CompletionTypeDefault: - case CompletionTypeChain: - ctx.WriteKeyWord(" AND CHAIN") - case CompletionTypeRelease: - ctx.WriteKeyWord(" RELEASE") - } - return nil -} - -// CommitStmt is a statement to commit the current transaction. -// See https://dev.mysql.com/doc/refman/5.7/en/commit.html -type CommitStmt struct { - stmtNode - // CompletionType overwrites system variable `completion_type` within transaction - CompletionType CompletionType -} - -// Restore implements Node interface. -func (n *CommitStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("COMMIT") - if err := n.CompletionType.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CommitStmt.CompletionType") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CommitStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CommitStmt) - return v.Leave(n) -} - -// RollbackStmt is a statement to roll back the current transaction. -// See https://dev.mysql.com/doc/refman/5.7/en/commit.html -type RollbackStmt struct { - stmtNode - // CompletionType overwrites system variable `completion_type` within transaction - CompletionType CompletionType - // SavepointName is the savepoint name. - SavepointName string -} - -// Restore implements Node interface. -func (n *RollbackStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ROLLBACK") - if n.SavepointName != "" { - ctx.WritePlain(" TO ") - ctx.WritePlain(n.SavepointName) - } - if err := n.CompletionType.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore RollbackStmt.CompletionType") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RollbackStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RollbackStmt) - return v.Leave(n) -} - -// UseStmt is a statement to use the DBName database as the current database. -// See https://dev.mysql.com/doc/refman/5.7/en/use.html -type UseStmt struct { - stmtNode - - DBName string -} - -// Restore implements Node interface. -func (n *UseStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("USE ") - ctx.WriteName(n.DBName) - return nil -} - -// Accept implements Node Accept interface. -func (n *UseStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*UseStmt) - return v.Leave(n) -} - -const ( - // SetNames is the const for set names stmt. - // If VariableAssignment.Name == Names, it should be set names stmt. - SetNames = "SetNAMES" - // SetCharset is the const for set charset stmt. - SetCharset = "SetCharset" -) - -// VariableAssignment is a variable assignment struct. -type VariableAssignment struct { - node - Name string - Value ExprNode - IsGlobal bool - IsSystem bool - - // ExtendValue is a way to store extended info. - // VariableAssignment should be able to store information for SetCharset/SetPWD Stmt. - // For SetCharsetStmt, Value is charset, ExtendValue is collation. - // TODO: Use SetStmt to implement set password statement. - ExtendValue ValueExpr -} - -// Restore implements Node interface. -func (n *VariableAssignment) Restore(ctx *format.RestoreCtx) error { - if n.IsSystem { - ctx.WritePlain("@@") - if n.IsGlobal { - ctx.WriteKeyWord("GLOBAL") - } else { - ctx.WriteKeyWord("SESSION") - } - ctx.WritePlain(".") - } else if n.Name != SetNames && n.Name != SetCharset { - ctx.WriteKeyWord("@") - } - if n.Name == SetNames { - ctx.WriteKeyWord("NAMES ") - } else if n.Name == SetCharset { - ctx.WriteKeyWord("CHARSET ") - } else { - ctx.WriteName(n.Name) - ctx.WritePlain("=") - } - if err := n.Value.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore VariableAssignment.Value") - } - if n.ExtendValue != nil { - ctx.WriteKeyWord(" COLLATE ") - if err := n.ExtendValue.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore VariableAssignment.ExtendValue") - } - } - return nil -} - -// Accept implements Node interface. -func (n *VariableAssignment) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*VariableAssignment) - node, ok := n.Value.Accept(v) - if !ok { - return n, false - } - n.Value = node.(ExprNode) - return v.Leave(n) -} - -// FlushStmtType is the type for FLUSH statement. -type FlushStmtType int - -// Flush statement types. -const ( - FlushNone FlushStmtType = iota - FlushTables - FlushPrivileges - FlushStatus - FlushTiDBPlugin - FlushHosts - FlushLogs - FlushClientErrorsSummary -) - -// LogType is the log type used in FLUSH statement. -type LogType int8 - -const ( - LogTypeDefault LogType = iota - LogTypeBinary - LogTypeEngine - LogTypeError - LogTypeGeneral - LogTypeSlow -) - -// FlushStmt is a statement to flush tables/privileges/optimizer costs and so on. -type FlushStmt struct { - stmtNode - - Tp FlushStmtType // Privileges/Tables/... - NoWriteToBinLog bool - LogType LogType - Tables []*TableName // For FlushTableStmt, if Tables is empty, it means flush all tables. - ReadLock bool - Plugins []string -} - -// Restore implements Node interface. -func (n *FlushStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("FLUSH ") - if n.NoWriteToBinLog { - ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") - } - switch n.Tp { - case FlushTables: - ctx.WriteKeyWord("TABLES") - for i, v := range n.Tables { - if i == 0 { - ctx.WritePlain(" ") - } else { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore FlushStmt.Tables[%d]", i) - } - } - if n.ReadLock { - ctx.WriteKeyWord(" WITH READ LOCK") - } - case FlushPrivileges: - ctx.WriteKeyWord("PRIVILEGES") - case FlushStatus: - ctx.WriteKeyWord("STATUS") - case FlushTiDBPlugin: - ctx.WriteKeyWord("TIDB PLUGINS") - for i, v := range n.Plugins { - if i == 0 { - ctx.WritePlain(" ") - } else { - ctx.WritePlain(", ") - } - ctx.WritePlain(v) - } - case FlushHosts: - ctx.WriteKeyWord("HOSTS") - case FlushLogs: - var logType string - switch n.LogType { - case LogTypeDefault: - logType = "LOGS" - case LogTypeBinary: - logType = "BINARY LOGS" - case LogTypeEngine: - logType = "ENGINE LOGS" - case LogTypeError: - logType = "ERROR LOGS" - case LogTypeGeneral: - logType = "GENERAL LOGS" - case LogTypeSlow: - logType = "SLOW LOGS" - } - ctx.WriteKeyWord(logType) - case FlushClientErrorsSummary: - ctx.WriteKeyWord("CLIENT_ERRORS_SUMMARY") - default: - return errors.New("Unsupported type of FlushStmt") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *FlushStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*FlushStmt) - return v.Leave(n) -} - -// KillStmt is a statement to kill a query or connection. -type KillStmt struct { - stmtNode - - // Query indicates whether terminate a single query on this connection or the whole connection. - // If Query is true, terminates the statement the connection is currently executing, but leaves the connection itself intact. - // If Query is false, terminates the connection associated with the given ConnectionID, after terminating any statement the connection is executing. - Query bool - ConnectionID uint64 - // TiDBExtension is used to indicate whether the user knows he is sending kill statement to the right tidb-server. - // When the SQL grammar is "KILL TIDB [CONNECTION | QUERY] connectionID", TiDBExtension will be set. - // It's a special grammar extension in TiDB. This extension exists because, when the connection is: - // client -> LVS proxy -> TiDB, and type Ctrl+C in client, the following action will be executed: - // new a connection; kill xxx; - // kill command may send to the wrong TiDB, because the exists of LVS proxy, and kill the wrong session. - // So, "KILL TIDB" grammar is introduced, and it REQUIRES DIRECT client -> TiDB TOPOLOGY. - // TODO: The standard KILL grammar will be supported once we have global connectionID. - TiDBExtension bool - - Expr ExprNode -} - -// Restore implements Node interface. -func (n *KillStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("KILL") - if n.TiDBExtension { - ctx.WriteKeyWord(" TIDB") - } - if n.Query { - ctx.WriteKeyWord(" QUERY") - } - if n.Expr != nil { - ctx.WriteKeyWord(" ") - if err := n.Expr.Restore(ctx); err != nil { - return errors.Trace(err) - } - } else { - ctx.WritePlainf(" %d", n.ConnectionID) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *KillStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*KillStmt) - return v.Leave(n) -} - -// SavepointStmt is the statement of SAVEPOINT. -type SavepointStmt struct { - stmtNode - // Name is the savepoint name. - Name string -} - -// Restore implements Node interface. -func (n *SavepointStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SAVEPOINT ") - ctx.WritePlain(n.Name) - return nil -} - -// Accept implements Node Accept interface. -func (n *SavepointStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - n = newNode.(*SavepointStmt) - return v.Leave(n) -} - -// ReleaseSavepointStmt is the statement of RELEASE SAVEPOINT. -type ReleaseSavepointStmt struct { - stmtNode - // Name is the savepoint name. - Name string -} - -// Restore implements Node interface. -func (n *ReleaseSavepointStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("RELEASE SAVEPOINT ") - ctx.WritePlain(n.Name) - return nil -} - -// Accept implements Node Accept interface. -func (n *ReleaseSavepointStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - n = newNode.(*ReleaseSavepointStmt) - return v.Leave(n) -} - -// SetStmt is the statement to set variables. -type SetStmt struct { - stmtNode - // Variables is the list of variable assignment. - Variables []*VariableAssignment -} - -// Restore implements Node interface. -func (n *SetStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET ") - for i, v := range n.Variables { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore SetStmt.Variables[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *SetStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetStmt) - for i, val := range n.Variables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Variables[i] = node.(*VariableAssignment) - } - return v.Leave(n) -} - -// SetConfigStmt is the statement to set cluster configs. -type SetConfigStmt struct { - stmtNode - - Type string // TiDB, TiKV, PD - Instance string // '127.0.0.1:3306' - Name string // the variable name - Value ExprNode -} - -func (n *SetConfigStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET CONFIG ") - if n.Type != "" { - ctx.WriteKeyWord(n.Type) - } else { - ctx.WriteString(n.Instance) - } - ctx.WritePlain(" ") - ctx.WriteKeyWord(n.Name) - ctx.WritePlain(" = ") - return n.Value.Restore(ctx) -} - -func (n *SetConfigStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetConfigStmt) - node, ok := n.Value.Accept(v) - if !ok { - return n, false - } - n.Value = node.(ExprNode) - return v.Leave(n) -} - -// SetSessionStatesStmt is a statement to restore session states. -type SetSessionStatesStmt struct { - stmtNode - - SessionStates string -} - -func (n *SetSessionStatesStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET SESSION_STATES ") - ctx.WriteString(n.SessionStates) - return nil -} - -func (n *SetSessionStatesStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetSessionStatesStmt) - return v.Leave(n) -} - -/* -// SetCharsetStmt is a statement to assign values to character and collation variables. -// See https://dev.mysql.com/doc/refman/5.7/en/set-statement.html -type SetCharsetStmt struct { - stmtNode - - Charset string - Collate string -} - -// Accept implements Node Accept interface. -func (n *SetCharsetStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetCharsetStmt) - return v.Leave(n) -} -*/ - -// SetPwdStmt is a statement to assign a password to user account. -// See https://dev.mysql.com/doc/refman/5.7/en/set-password.html -type SetPwdStmt struct { - stmtNode - - User *auth.UserIdentity - Password string -} - -// Restore implements Node interface. -func (n *SetPwdStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET PASSWORD") - if n.User != nil { - ctx.WriteKeyWord(" FOR ") - if err := n.User.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore SetPwdStmt.User") - } - } - ctx.WritePlain("=") - ctx.WriteString(n.Password) - return nil -} - -// SecureText implements SensitiveStatement interface. -func (n *SetPwdStmt) SecureText() string { - return fmt.Sprintf("set password for user %s", n.User) -} - -// Accept implements Node Accept interface. -func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetPwdStmt) - return v.Leave(n) -} - -type ChangeStmt struct { - stmtNode - - NodeType string - State string - NodeID string -} - -// Restore implements Node interface. -func (n *ChangeStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CHANGE ") - ctx.WriteKeyWord(n.NodeType) - ctx.WriteKeyWord(" TO NODE_STATE ") - ctx.WritePlain("=") - ctx.WriteString(n.State) - ctx.WriteKeyWord(" FOR NODE_ID ") - ctx.WriteString(n.NodeID) - return nil -} - -// SecureText implements SensitiveStatement interface. -func (n *ChangeStmt) SecureText() string { - return fmt.Sprintf("change %s to node_state='%s' for node_id '%s'", strings.ToLower(n.NodeType), n.State, n.NodeID) -} - -// Accept implements Node Accept interface. -func (n *ChangeStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ChangeStmt) - return v.Leave(n) -} - -// SetRoleStmtType is the type for FLUSH statement. -type SetRoleStmtType int - -// SetRole statement types. -const ( - SetRoleDefault SetRoleStmtType = iota - SetRoleNone - SetRoleAll - SetRoleAllExcept - SetRoleRegular -) - -type SetRoleStmt struct { - stmtNode - - SetRoleOpt SetRoleStmtType - RoleList []*auth.RoleIdentity -} - -func (n *SetRoleStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET ROLE") - switch n.SetRoleOpt { - case SetRoleDefault: - ctx.WriteKeyWord(" DEFAULT") - case SetRoleNone: - ctx.WriteKeyWord(" NONE") - case SetRoleAll: - ctx.WriteKeyWord(" ALL") - case SetRoleAllExcept: - ctx.WriteKeyWord(" ALL EXCEPT") - } - for i, role := range n.RoleList { - ctx.WritePlain(" ") - err := role.Restore(ctx) - if err != nil { - return errors.Annotate(err, "An error occurred while restore SetRoleStmt.RoleList") - } - if i != len(n.RoleList)-1 { - ctx.WritePlain(",") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *SetRoleStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetRoleStmt) - return v.Leave(n) -} - -type SetDefaultRoleStmt struct { - stmtNode - - SetRoleOpt SetRoleStmtType - RoleList []*auth.RoleIdentity - UserList []*auth.UserIdentity -} - -func (n *SetDefaultRoleStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET DEFAULT ROLE") - switch n.SetRoleOpt { - case SetRoleNone: - ctx.WriteKeyWord(" NONE") - case SetRoleAll: - ctx.WriteKeyWord(" ALL") - default: - } - for i, role := range n.RoleList { - ctx.WritePlain(" ") - err := role.Restore(ctx) - if err != nil { - return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.RoleList") - } - if i != len(n.RoleList)-1 { - ctx.WritePlain(",") - } - } - ctx.WritePlain(" TO") - for i, user := range n.UserList { - ctx.WritePlain(" ") - err := user.Restore(ctx) - if err != nil { - return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.UserList") - } - if i != len(n.UserList)-1 { - ctx.WritePlain(",") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *SetDefaultRoleStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetDefaultRoleStmt) - return v.Leave(n) -} - -// UserSpec is used for parsing create user statement. -type UserSpec struct { - User *auth.UserIdentity - AuthOpt *AuthOption - IsRole bool -} - -// Restore implements Node interface. -func (n *UserSpec) Restore(ctx *format.RestoreCtx) error { - if err := n.User.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore UserSpec.User") - } - if n.AuthOpt != nil { - ctx.WritePlain(" ") - if err := n.AuthOpt.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore UserSpec.AuthOpt") - } - } - return nil -} - -// SecurityString formats the UserSpec without password information. -func (n *UserSpec) SecurityString() string { - withPassword := false - if opt := n.AuthOpt; opt != nil { - if len(opt.AuthString) > 0 || len(opt.HashString) > 0 { - withPassword = true - } - } - if withPassword { - return fmt.Sprintf("{%s password = ***}", n.User) - } - return n.User.String() -} - -// EncodedPassword returns the encoded password (which is the real data mysql.user). -// The boolean value indicates input's password format is legal or not. -func (n *UserSpec) EncodedPassword() (string, bool) { - if n.AuthOpt == nil { - return "", true - } - - opt := n.AuthOpt - if opt.ByAuthString { - switch opt.AuthPlugin { - case mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password: - return auth.NewHashPassword(opt.AuthString, opt.AuthPlugin), true - case mysql.AuthSocket: - return "", true - default: - return auth.EncodePassword(opt.AuthString), true - } - } - - // store the LDAP dn directly in the password field - switch opt.AuthPlugin { - case mysql.AuthLDAPSimple, mysql.AuthLDAPSASL: - // TODO: validate the HashString to be a `dn` for LDAP - // It seems fine to not validate here, and LDAP server will give an error when the client'll try to login this user. - // The percona server implementation doesn't have a validation for this HashString. - // However, returning an error for obvious wrong format is more friendly. - return opt.HashString, true - } - - // In case we have 'IDENTIFIED WITH ' but no 'BY ' to set an empty password. - if opt.HashString == "" { - return opt.HashString, true - } - - // Not a legal password string. - switch opt.AuthPlugin { - case mysql.AuthCachingSha2Password: - if len(opt.HashString) != mysql.SHAPWDHashLen { - return "", false - } - case mysql.AuthTiDBSM3Password: - if len(opt.HashString) != mysql.SM3PWDHashLen { - return "", false - } - case "", mysql.AuthNativePassword: - if len(opt.HashString) != (mysql.PWDHashLen+1) || !strings.HasPrefix(opt.HashString, "*") { - return "", false - } - case mysql.AuthSocket: - default: - return "", false - } - return opt.HashString, true -} - -type AuthTokenOrTLSOption struct { - Type AuthTokenOrTLSOptionType - Value string -} - -func (t *AuthTokenOrTLSOption) Restore(ctx *format.RestoreCtx) error { - switch t.Type { - case TlsNone: - ctx.WriteKeyWord("NONE") - case Ssl: - ctx.WriteKeyWord("SSL") - case X509: - ctx.WriteKeyWord("X509") - case Cipher: - ctx.WriteKeyWord("CIPHER ") - ctx.WriteString(t.Value) - case Issuer: - ctx.WriteKeyWord("ISSUER ") - ctx.WriteString(t.Value) - case Subject: - ctx.WriteKeyWord("SUBJECT ") - ctx.WriteString(t.Value) - case SAN: - ctx.WriteKeyWord("SAN ") - ctx.WriteString(t.Value) - case TokenIssuer: - ctx.WriteKeyWord("TOKEN_ISSUER ") - ctx.WriteString(t.Value) - default: - return errors.Errorf("Unsupported AuthTokenOrTLSOption.Type %d", t.Type) - } - return nil -} - -type AuthTokenOrTLSOptionType int - -const ( - TlsNone AuthTokenOrTLSOptionType = iota - Ssl - X509 - Cipher - Issuer - Subject - SAN - TokenIssuer -) - -func (t AuthTokenOrTLSOptionType) String() string { - switch t { - case TlsNone: - return "NONE" - case Ssl: - return "SSL" - case X509: - return "X509" - case Cipher: - return "CIPHER" - case Issuer: - return "ISSUER" - case Subject: - return "SUBJECT" - case SAN: - return "SAN" - case TokenIssuer: - return "TOKEN_ISSUER" - default: - return "UNKNOWN" - } -} - -const ( - MaxQueriesPerHour = iota + 1 - MaxUpdatesPerHour - MaxConnectionsPerHour - MaxUserConnections -) - -type ResourceOption struct { - Type int - Count int64 -} - -func (r *ResourceOption) Restore(ctx *format.RestoreCtx) error { - switch r.Type { - case MaxQueriesPerHour: - ctx.WriteKeyWord("MAX_QUERIES_PER_HOUR ") - case MaxUpdatesPerHour: - ctx.WriteKeyWord("MAX_UPDATES_PER_HOUR ") - case MaxConnectionsPerHour: - ctx.WriteKeyWord("MAX_CONNECTIONS_PER_HOUR ") - case MaxUserConnections: - ctx.WriteKeyWord("MAX_USER_CONNECTIONS ") - default: - return errors.Errorf("Unsupported ResourceOption.Type %d", r.Type) - } - ctx.WritePlainf("%d", r.Count) - return nil -} - -const ( - PasswordExpire = iota + 1 - PasswordExpireDefault - PasswordExpireNever - PasswordExpireInterval - PasswordHistory - PasswordHistoryDefault - PasswordReuseInterval - PasswordReuseDefault - Lock - Unlock - FailedLoginAttempts - PasswordLockTime - PasswordLockTimeUnbounded - UserCommentType - UserAttributeType - - UserResourceGroupName -) - -type PasswordOrLockOption struct { - Type int - Count int64 -} - -func (p *PasswordOrLockOption) Restore(ctx *format.RestoreCtx) error { - switch p.Type { - case PasswordExpire: - ctx.WriteKeyWord("PASSWORD EXPIRE") - case PasswordExpireDefault: - ctx.WriteKeyWord("PASSWORD EXPIRE DEFAULT") - case PasswordExpireNever: - ctx.WriteKeyWord("PASSWORD EXPIRE NEVER") - case PasswordExpireInterval: - ctx.WriteKeyWord("PASSWORD EXPIRE INTERVAL") - ctx.WritePlainf(" %d", p.Count) - ctx.WriteKeyWord(" DAY") - case Lock: - ctx.WriteKeyWord("ACCOUNT LOCK") - case Unlock: - ctx.WriteKeyWord("ACCOUNT UNLOCK") - case FailedLoginAttempts: - ctx.WriteKeyWord("FAILED_LOGIN_ATTEMPTS") - ctx.WritePlainf(" %d", p.Count) - case PasswordLockTime: - ctx.WriteKeyWord("PASSWORD_LOCK_TIME") - ctx.WritePlainf(" %d", p.Count) - case PasswordLockTimeUnbounded: - ctx.WriteKeyWord("PASSWORD_LOCK_TIME UNBOUNDED") - case PasswordHistory: - ctx.WriteKeyWord("PASSWORD HISTORY") - ctx.WritePlainf(" %d", p.Count) - case PasswordHistoryDefault: - ctx.WriteKeyWord("PASSWORD HISTORY DEFAULT") - case PasswordReuseInterval: - ctx.WriteKeyWord("PASSWORD REUSE INTERVAL") - ctx.WritePlainf(" %d", p.Count) - ctx.WriteKeyWord(" DAY") - case PasswordReuseDefault: - ctx.WriteKeyWord("PASSWORD REUSE INTERVAL DEFAULT") - default: - return errors.Errorf("Unsupported PasswordOrLockOption.Type %d", p.Type) - } - return nil -} - -type CommentOrAttributeOption struct { - Type int - Value string -} - -func (c *CommentOrAttributeOption) Restore(ctx *format.RestoreCtx) error { - if c.Type == UserCommentType { - ctx.WriteKeyWord(" COMMENT ") - ctx.WriteString(c.Value) - } else if c.Type == UserAttributeType { - ctx.WriteKeyWord(" ATTRIBUTE ") - ctx.WriteString(c.Value) - } - return nil -} - -type ResourceGroupNameOption struct { - Value string -} - -func (c *ResourceGroupNameOption) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(" RESOURCE GROUP ") - ctx.WriteName(c.Value) - return nil -} - -// CreateUserStmt creates user account. -// See https://dev.mysql.com/doc/refman/8.0/en/create-user.html -type CreateUserStmt struct { - stmtNode - - IsCreateRole bool - IfNotExists bool - Specs []*UserSpec - AuthTokenOrTLSOptions []*AuthTokenOrTLSOption - ResourceOptions []*ResourceOption - PasswordOrLockOptions []*PasswordOrLockOption - CommentOrAttributeOption *CommentOrAttributeOption - ResourceGroupNameOption *ResourceGroupNameOption -} - -// Restore implements Node interface. -func (n *CreateUserStmt) Restore(ctx *format.RestoreCtx) error { - if n.IsCreateRole { - ctx.WriteKeyWord("CREATE ROLE ") - } else { - ctx.WriteKeyWord("CREATE USER ") - } - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - for i, v := range n.Specs { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.Specs[%d]", i) - } - } - - if len(n.AuthTokenOrTLSOptions) != 0 { - ctx.WriteKeyWord(" REQUIRE ") - } - - for i, option := range n.AuthTokenOrTLSOptions { - if i != 0 { - ctx.WriteKeyWord(" AND ") - } - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.AuthTokenOrTLSOptions[%d]", i) - } - } - - if len(n.ResourceOptions) != 0 { - ctx.WriteKeyWord(" WITH") - } - - for i, v := range n.ResourceOptions { - ctx.WritePlain(" ") - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.ResourceOptions[%d]", i) - } - } - - for i, v := range n.PasswordOrLockOptions { - ctx.WritePlain(" ") - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.PasswordOrLockOptions[%d]", i) - } - } - - if n.CommentOrAttributeOption != nil { - if err := n.CommentOrAttributeOption.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.CommentOrAttributeOption") - } - } - - if n.ResourceGroupNameOption != nil { - if err := n.ResourceGroupNameOption.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.ResourceGroupNameOption") - } - } - - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateUserStmt) - return v.Leave(n) -} - -// SecureText implements SensitiveStatement interface. -func (n *CreateUserStmt) SecureText() string { - var buf bytes.Buffer - buf.WriteString("create user") - for _, user := range n.Specs { - buf.WriteString(" ") - buf.WriteString(user.SecurityString()) - } - return buf.String() -} - -// AlterUserStmt modifies user account. -// See https://dev.mysql.com/doc/refman/8.0/en/alter-user.html -type AlterUserStmt struct { - stmtNode - - IfExists bool - CurrentAuth *AuthOption - Specs []*UserSpec - AuthTokenOrTLSOptions []*AuthTokenOrTLSOption - ResourceOptions []*ResourceOption - PasswordOrLockOptions []*PasswordOrLockOption - CommentOrAttributeOption *CommentOrAttributeOption - ResourceGroupNameOption *ResourceGroupNameOption -} - -// Restore implements Node interface. -func (n *AlterUserStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ALTER USER ") - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - if n.CurrentAuth != nil { - ctx.WriteKeyWord("USER") - ctx.WritePlain("() ") - if err := n.CurrentAuth.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterUserStmt.CurrentAuth") - } - } - for i, v := range n.Specs { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.Specs[%d]", i) - } - } - - if len(n.AuthTokenOrTLSOptions) != 0 { - ctx.WriteKeyWord(" REQUIRE ") - } - - for i, option := range n.AuthTokenOrTLSOptions { - if i != 0 { - ctx.WriteKeyWord(" AND ") - } - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.AuthTokenOrTLSOptions[%d]", i) - } - } - - if len(n.ResourceOptions) != 0 { - ctx.WriteKeyWord(" WITH") - } - - for i, v := range n.ResourceOptions { - ctx.WritePlain(" ") - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.ResourceOptions[%d]", i) - } - } - - for i, v := range n.PasswordOrLockOptions { - ctx.WritePlain(" ") - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.PasswordOrLockOptions[%d]", i) - } - } - - if n.CommentOrAttributeOption != nil { - if err := n.CommentOrAttributeOption.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.CommentOrAttributeOption") - } - } - - if n.ResourceGroupNameOption != nil { - if err := n.ResourceGroupNameOption.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.ResourceGroupNameOption") - } - } - - return nil -} - -// SecureText implements SensitiveStatement interface. -func (n *AlterUserStmt) SecureText() string { - var buf bytes.Buffer - buf.WriteString("alter user") - for _, user := range n.Specs { - buf.WriteString(" ") - buf.WriteString(user.SecurityString()) - } - return buf.String() -} - -// Accept implements Node Accept interface. -func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterUserStmt) - return v.Leave(n) -} - -// AlterInstanceStmt modifies instance. -// See https://dev.mysql.com/doc/refman/8.0/en/alter-instance.html -type AlterInstanceStmt struct { - stmtNode - - ReloadTLS bool - NoRollbackOnError bool -} - -// Restore implements Node interface. -func (n *AlterInstanceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ALTER INSTANCE") - if n.ReloadTLS { - ctx.WriteKeyWord(" RELOAD TLS") - } - if n.NoRollbackOnError { - ctx.WriteKeyWord(" NO ROLLBACK ON ERROR") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AlterInstanceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterInstanceStmt) - return v.Leave(n) -} - -// AlterRangeStmt modifies range configuration. -type AlterRangeStmt struct { - stmtNode - RangeName model.CIStr - PlacementOption *PlacementOption -} - -// Restore implements Node interface. -func (n *AlterRangeStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("ALTER RANGE ") - ctx.WriteName(n.RangeName.O) - ctx.WritePlain(" ") - if err := n.PlacementOption.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AlterRangeStmt.PlacementOption") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AlterRangeStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AlterRangeStmt) - return v.Leave(n) -} - -// DropUserStmt creates user account. -// See http://dev.mysql.com/doc/refman/5.7/en/drop-user.html -type DropUserStmt struct { - stmtNode - - IfExists bool - IsDropRole bool - UserList []*auth.UserIdentity -} - -// Restore implements Node interface. -func (n *DropUserStmt) Restore(ctx *format.RestoreCtx) error { - if n.IsDropRole { - ctx.WriteKeyWord("DROP ROLE ") - } else { - ctx.WriteKeyWord("DROP USER ") - } - if n.IfExists { - ctx.WriteKeyWord("IF EXISTS ") - } - for i, v := range n.UserList { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DropUserStmt.UserList[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *DropUserStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropUserStmt) - return v.Leave(n) -} - -// CreateBindingStmt creates sql binding hint. -type CreateBindingStmt struct { - stmtNode - - GlobalScope bool - OriginNode StmtNode - HintedNode StmtNode - PlanDigest string -} - -func (n *CreateBindingStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE ") - if n.GlobalScope { - ctx.WriteKeyWord("GLOBAL ") - } else { - ctx.WriteKeyWord("SESSION ") - } - if n.OriginNode == nil { - ctx.WriteKeyWord("BINDING FROM HISTORY USING PLAN DIGEST ") - ctx.WriteString(n.PlanDigest) - } else { - ctx.WriteKeyWord("BINDING FOR ") - if err := n.OriginNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - ctx.WriteKeyWord(" USING ") - if err := n.HintedNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - } - return nil -} - -func (n *CreateBindingStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateBindingStmt) - if n.OriginNode != nil { - origNode, ok := n.OriginNode.Accept(v) - if !ok { - return n, false - } - n.OriginNode = origNode.(StmtNode) - hintedNode, ok := n.HintedNode.Accept(v) - if !ok { - return n, false - } - n.HintedNode = hintedNode.(StmtNode) - } - return v.Leave(n) -} - -// DropBindingStmt deletes sql binding hint. -type DropBindingStmt struct { - stmtNode - - GlobalScope bool - OriginNode StmtNode - HintedNode StmtNode - SQLDigest string -} - -func (n *DropBindingStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP ") - if n.GlobalScope { - ctx.WriteKeyWord("GLOBAL ") - } else { - ctx.WriteKeyWord("SESSION ") - } - ctx.WriteKeyWord("BINDING FOR ") - if n.OriginNode == nil { - ctx.WriteKeyWord("SQL DIGEST ") - ctx.WriteString(n.SQLDigest) - } else { - if err := n.OriginNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - if n.HintedNode != nil { - ctx.WriteKeyWord(" USING ") - if err := n.HintedNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - } - } - return nil -} - -func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropBindingStmt) - if n.OriginNode != nil { - // OriginNode is nil means we build drop binding by sql digest - origNode, ok := n.OriginNode.Accept(v) - if !ok { - return n, false - } - n.OriginNode = origNode.(StmtNode) - if n.HintedNode != nil { - hintedNode, ok := n.HintedNode.Accept(v) - if !ok { - return n, false - } - n.HintedNode = hintedNode.(StmtNode) - } - } - return v.Leave(n) -} - -// BindingStatusType defines the status type for the binding -type BindingStatusType int8 - -// Binding status types. -const ( - BindingStatusTypeEnabled BindingStatusType = iota - BindingStatusTypeDisabled -) - -// SetBindingStmt sets sql binding status. -type SetBindingStmt struct { - stmtNode - - BindingStatusType BindingStatusType - OriginNode StmtNode - HintedNode StmtNode - SQLDigest string -} - -func (n *SetBindingStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET ") - ctx.WriteKeyWord("BINDING ") - switch n.BindingStatusType { - case BindingStatusTypeEnabled: - ctx.WriteKeyWord("ENABLED ") - case BindingStatusTypeDisabled: - ctx.WriteKeyWord("DISABLED ") - } - ctx.WriteKeyWord("FOR ") - if n.OriginNode == nil { - ctx.WriteKeyWord("SQL DIGEST ") - ctx.WriteString(n.SQLDigest) - } else { - if err := n.OriginNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - if n.HintedNode != nil { - ctx.WriteKeyWord(" USING ") - if err := n.HintedNode.Restore(ctx); err != nil { - return errors.Trace(err) - } - } - } - return nil -} - -func (n *SetBindingStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetBindingStmt) - if n.OriginNode != nil { - // OriginNode is nil means we set binding stmt by sql digest - origNode, ok := n.OriginNode.Accept(v) - if !ok { - return n, false - } - n.OriginNode = origNode.(StmtNode) - if n.HintedNode != nil { - hintedNode, ok := n.HintedNode.Accept(v) - if !ok { - return n, false - } - n.HintedNode = hintedNode.(StmtNode) - } - } - return v.Leave(n) -} - -// Extended statistics types. -const ( - StatsTypeCardinality uint8 = iota - StatsTypeDependency - StatsTypeCorrelation -) - -// StatisticsSpec is the specification for ADD /DROP STATISTICS. -type StatisticsSpec struct { - StatsName string - StatsType uint8 - Columns []*ColumnName -} - -// CreateStatisticsStmt is a statement to create extended statistics. -// Examples: -// -// CREATE STATISTICS stats1 (cardinality) ON t(a, b, c); -// CREATE STATISTICS stats2 (dependency) ON t(a, b); -// CREATE STATISTICS stats3 (correlation) ON t(a, b); -type CreateStatisticsStmt struct { - stmtNode - - IfNotExists bool - StatsName string - StatsType uint8 - Table *TableName - Columns []*ColumnName -} - -// Restore implements Node interface. -func (n *CreateStatisticsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CREATE STATISTICS ") - if n.IfNotExists { - ctx.WriteKeyWord("IF NOT EXISTS ") - } - ctx.WriteName(n.StatsName) - switch n.StatsType { - case StatsTypeCardinality: - ctx.WriteKeyWord(" (cardinality) ") - case StatsTypeDependency: - ctx.WriteKeyWord(" (dependency) ") - case StatsTypeCorrelation: - ctx.WriteKeyWord(" (correlation) ") - } - ctx.WriteKeyWord("ON ") - if err := n.Table.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CreateStatisticsStmt.Table") - } - - ctx.WritePlain("(") - for i, col := range n.Columns { - if i != 0 { - ctx.WritePlain(", ") - } - if err := col.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore CreateStatisticsStmt.Columns: [%v]", i) - } - } - ctx.WritePlain(")") - return nil -} - -// Accept implements Node Accept interface. -func (n *CreateStatisticsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CreateStatisticsStmt) - node, ok := n.Table.Accept(v) - if !ok { - return n, false - } - n.Table = node.(*TableName) - for i, col := range n.Columns { - node, ok = col.Accept(v) - if !ok { - return n, false - } - n.Columns[i] = node.(*ColumnName) - } - return v.Leave(n) -} - -// DropStatisticsStmt is a statement to drop extended statistics. -// Examples: -// -// DROP STATISTICS stats1; -type DropStatisticsStmt struct { - stmtNode - - StatsName string -} - -// Restore implements Node interface. -func (n *DropStatisticsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP STATISTICS ") - ctx.WriteName(n.StatsName) - return nil -} - -// Accept implements Node Accept interface. -func (n *DropStatisticsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropStatisticsStmt) - return v.Leave(n) -} - -// DoStmt is the struct for DO statement. -type DoStmt struct { - stmtNode - - Exprs []ExprNode -} - -// Restore implements Node interface. -func (n *DoStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DO ") - for i, v := range n.Exprs { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DoStmt.Exprs[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *DoStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DoStmt) - for i, val := range n.Exprs { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Exprs[i] = node.(ExprNode) - } - return v.Leave(n) -} - -// AdminStmtType is the type for admin statement. -type AdminStmtType int - -// Admin statement types. -const ( - AdminShowDDL = iota + 1 - AdminCheckTable - AdminShowDDLJobs - AdminCancelDDLJobs - AdminPauseDDLJobs - AdminResumeDDLJobs - AdminCheckIndex - AdminRecoverIndex - AdminCleanupIndex - AdminCheckIndexRange - AdminShowDDLJobQueries - AdminShowDDLJobQueriesWithRange - AdminChecksumTable - AdminShowSlow - AdminShowNextRowID - AdminReloadExprPushdownBlacklist - AdminReloadOptRuleBlacklist - AdminPluginDisable - AdminPluginEnable - AdminFlushBindings - AdminCaptureBindings - AdminEvolveBindings - AdminReloadBindings - AdminShowTelemetry - AdminResetTelemetryID - AdminReloadStatistics - AdminFlushPlanCache -) - -// HandleRange represents a range where handle value >= Begin and < End. -type HandleRange struct { - Begin int64 - End int64 -} - -type StatementScope int - -const ( - StatementScopeNone StatementScope = iota - StatementScopeSession - StatementScopeInstance - StatementScopeGlobal -) - -// ShowSlowType defines the type for SlowSlow statement. -type ShowSlowType int - -const ( - // ShowSlowTop is a ShowSlowType constant. - ShowSlowTop ShowSlowType = iota - // ShowSlowRecent is a ShowSlowType constant. - ShowSlowRecent -) - -// ShowSlowKind defines the kind for SlowSlow statement when the type is ShowSlowTop. -type ShowSlowKind int - -const ( - // ShowSlowKindDefault is a ShowSlowKind constant. - ShowSlowKindDefault ShowSlowKind = iota - // ShowSlowKindInternal is a ShowSlowKind constant. - ShowSlowKindInternal - // ShowSlowKindAll is a ShowSlowKind constant. - ShowSlowKindAll -) - -// ShowSlow is used for the following command: -// -// admin show slow top [ internal | all] N -// admin show slow recent N -type ShowSlow struct { - Tp ShowSlowType - Count uint64 - Kind ShowSlowKind -} - -// Restore implements Node interface. -func (n *ShowSlow) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case ShowSlowRecent: - ctx.WriteKeyWord("RECENT ") - case ShowSlowTop: - ctx.WriteKeyWord("TOP ") - switch n.Kind { - case ShowSlowKindDefault: - // do nothing - case ShowSlowKindInternal: - ctx.WriteKeyWord("INTERNAL ") - case ShowSlowKindAll: - ctx.WriteKeyWord("ALL ") - default: - return errors.New("Unsupported kind of ShowSlowTop") - } - default: - return errors.New("Unsupported type of ShowSlow") - } - ctx.WritePlainf("%d", n.Count) - return nil -} - -// LimitSimple is the struct for Admin statement limit option. -type LimitSimple struct { - Count uint64 - Offset uint64 -} - -// AdminStmt is the struct for Admin statement. -type AdminStmt struct { - stmtNode - - Tp AdminStmtType - Index string - Tables []*TableName - JobIDs []int64 - JobNumber int64 - - HandleRanges []HandleRange - ShowSlow *ShowSlow - Plugins []string - Where ExprNode - StatementScope StatementScope - LimitSimple LimitSimple -} - -// Restore implements Node interface. -func (n *AdminStmt) Restore(ctx *format.RestoreCtx) error { - restoreTables := func() error { - for i, v := range n.Tables { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AdminStmt.Tables[%d]", i) - } - } - return nil - } - restoreJobIDs := func() { - for i, v := range n.JobIDs { - if i != 0 { - ctx.WritePlain(", ") - } - ctx.WritePlainf("%d", v) - } - } - - ctx.WriteKeyWord("ADMIN ") - switch n.Tp { - case AdminShowDDL: - ctx.WriteKeyWord("SHOW DDL") - case AdminShowDDLJobs: - ctx.WriteKeyWord("SHOW DDL JOBS") - if n.JobNumber != 0 { - ctx.WritePlainf(" %d", n.JobNumber) - } - if n.Where != nil { - ctx.WriteKeyWord(" WHERE ") - if err := n.Where.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore ShowStmt.Where") - } - } - case AdminShowNextRowID: - ctx.WriteKeyWord("SHOW ") - if err := restoreTables(); err != nil { - return err - } - ctx.WriteKeyWord(" NEXT_ROW_ID") - case AdminCheckTable: - ctx.WriteKeyWord("CHECK TABLE ") - if err := restoreTables(); err != nil { - return err - } - case AdminCheckIndex: - ctx.WriteKeyWord("CHECK INDEX ") - if err := restoreTables(); err != nil { - return err - } - ctx.WritePlainf(" %s", n.Index) - case AdminRecoverIndex: - ctx.WriteKeyWord("RECOVER INDEX ") - if err := restoreTables(); err != nil { - return err - } - ctx.WritePlainf(" %s", n.Index) - case AdminCleanupIndex: - ctx.WriteKeyWord("CLEANUP INDEX ") - if err := restoreTables(); err != nil { - return err - } - ctx.WritePlainf(" %s", n.Index) - case AdminCheckIndexRange: - ctx.WriteKeyWord("CHECK INDEX ") - if err := restoreTables(); err != nil { - return err - } - ctx.WritePlainf(" %s", n.Index) - if n.HandleRanges != nil { - ctx.WritePlain(" ") - for i, v := range n.HandleRanges { - if i != 0 { - ctx.WritePlain(", ") - } - ctx.WritePlainf("(%d,%d)", v.Begin, v.End) - } - } - case AdminChecksumTable: - ctx.WriteKeyWord("CHECKSUM TABLE ") - if err := restoreTables(); err != nil { - return err - } - case AdminCancelDDLJobs: - ctx.WriteKeyWord("CANCEL DDL JOBS ") - restoreJobIDs() - case AdminPauseDDLJobs: - ctx.WriteKeyWord("PAUSE DDL JOBS ") - restoreJobIDs() - case AdminResumeDDLJobs: - ctx.WriteKeyWord("RESUME DDL JOBS ") - restoreJobIDs() - case AdminShowDDLJobQueries: - ctx.WriteKeyWord("SHOW DDL JOB QUERIES ") - restoreJobIDs() - case AdminShowDDLJobQueriesWithRange: - ctx.WriteKeyWord("SHOW DDL JOB QUERIES LIMIT ") - ctx.WritePlainf("%d, %d", n.LimitSimple.Offset, n.LimitSimple.Count) - case AdminShowSlow: - ctx.WriteKeyWord("SHOW SLOW ") - if err := n.ShowSlow.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore AdminStmt.ShowSlow") - } - case AdminReloadExprPushdownBlacklist: - ctx.WriteKeyWord("RELOAD EXPR_PUSHDOWN_BLACKLIST") - case AdminReloadOptRuleBlacklist: - ctx.WriteKeyWord("RELOAD OPT_RULE_BLACKLIST") - case AdminPluginEnable: - ctx.WriteKeyWord("PLUGINS ENABLE") - for i, v := range n.Plugins { - if i == 0 { - ctx.WritePlain(" ") - } else { - ctx.WritePlain(", ") - } - ctx.WritePlain(v) - } - case AdminPluginDisable: - ctx.WriteKeyWord("PLUGINS DISABLE") - for i, v := range n.Plugins { - if i == 0 { - ctx.WritePlain(" ") - } else { - ctx.WritePlain(", ") - } - ctx.WritePlain(v) - } - case AdminFlushBindings: - ctx.WriteKeyWord("FLUSH BINDINGS") - case AdminCaptureBindings: - ctx.WriteKeyWord("CAPTURE BINDINGS") - case AdminEvolveBindings: - ctx.WriteKeyWord("EVOLVE BINDINGS") - case AdminReloadBindings: - ctx.WriteKeyWord("RELOAD BINDINGS") - case AdminShowTelemetry: - ctx.WriteKeyWord("SHOW TELEMETRY") - case AdminResetTelemetryID: - ctx.WriteKeyWord("RESET TELEMETRY_ID") - case AdminReloadStatistics: - ctx.WriteKeyWord("RELOAD STATS_EXTENDED") - case AdminFlushPlanCache: - if n.StatementScope == StatementScopeSession { - ctx.WriteKeyWord("FLUSH SESSION PLAN_CACHE") - } else if n.StatementScope == StatementScopeInstance { - ctx.WriteKeyWord("FLUSH INSTANCE PLAN_CACHE") - } else if n.StatementScope == StatementScopeGlobal { - ctx.WriteKeyWord("FLUSH GLOBAL PLAN_CACHE") - } - default: - return errors.New("Unsupported AdminStmt type") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AdminStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - - n = newNode.(*AdminStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - - if n.Where != nil { - node, ok := n.Where.Accept(v) - if !ok { - return n, false - } - n.Where = node.(ExprNode) - } - - return v.Leave(n) -} - -// RoleOrPriv is a temporary structure to be further processed into auth.RoleIdentity or PrivElem -type RoleOrPriv struct { - Symbols string // hold undecided symbols - Node interface{} // hold auth.RoleIdentity or PrivElem that can be sure when parsing -} - -func (n *RoleOrPriv) ToRole() (*auth.RoleIdentity, error) { - if n.Node != nil { - if r, ok := n.Node.(*auth.RoleIdentity); ok { - return r, nil - } - return nil, errors.Errorf("can't convert to RoleIdentity, type %T", n.Node) - } - return &auth.RoleIdentity{Username: n.Symbols, Hostname: "%"}, nil -} - -func (n *RoleOrPriv) ToPriv() (*PrivElem, error) { - if n.Node != nil { - if p, ok := n.Node.(*PrivElem); ok { - return p, nil - } - return nil, errors.Errorf("can't convert to PrivElem, type %T", n.Node) - } - if len(n.Symbols) == 0 { - return nil, errors.New("symbols should not be length 0") - } - return &PrivElem{Priv: mysql.ExtendedPriv, Name: n.Symbols}, nil -} - -// PrivElem is the privilege type and optional column list. -type PrivElem struct { - node - - Priv mysql.PrivilegeType - Cols []*ColumnName - Name string -} - -// Restore implements Node interface. -func (n *PrivElem) Restore(ctx *format.RestoreCtx) error { - if n.Priv == mysql.AllPriv { - ctx.WriteKeyWord("ALL") - } else if n.Priv == mysql.ExtendedPriv { - ctx.WriteKeyWord(n.Name) - } else { - str, ok := mysql.Priv2Str[n.Priv] - if !ok { - return errors.New("Undefined privilege type") - } - ctx.WriteKeyWord(str) - } - if n.Cols != nil { - ctx.WritePlain(" (") - for i, v := range n.Cols { - if i != 0 { - ctx.WritePlain(",") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore PrivElem.Cols[%d]", i) - } - } - ctx.WritePlain(")") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *PrivElem) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*PrivElem) - for i, val := range n.Cols { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Cols[i] = node.(*ColumnName) - } - return v.Leave(n) -} - -// ObjectTypeType is the type for object type. -type ObjectTypeType int - -const ( - // ObjectTypeNone is for empty object type. - ObjectTypeNone ObjectTypeType = iota + 1 - // ObjectTypeTable means the following object is a table. - ObjectTypeTable - // ObjectTypeFunction means the following object is a stored function. - ObjectTypeFunction - // ObjectTypeProcedure means the following object is a stored procedure. - ObjectTypeProcedure -) - -// Restore implements Node interface. -func (n ObjectTypeType) Restore(ctx *format.RestoreCtx) error { - switch n { - case ObjectTypeNone: - // do nothing - case ObjectTypeTable: - ctx.WriteKeyWord("TABLE") - case ObjectTypeFunction: - ctx.WriteKeyWord("FUNCTION") - case ObjectTypeProcedure: - ctx.WriteKeyWord("PROCEDURE") - default: - return errors.New("Unsupported object type") - } - return nil -} - -// GrantLevelType is the type for grant level. -type GrantLevelType int - -const ( - // GrantLevelNone is the dummy const for default value. - GrantLevelNone GrantLevelType = iota + 1 - // GrantLevelGlobal means the privileges are administrative or apply to all databases on a given server. - GrantLevelGlobal - // GrantLevelDB means the privileges apply to all objects in a given database. - GrantLevelDB - // GrantLevelTable means the privileges apply to all columns in a given table. - GrantLevelTable -) - -// GrantLevel is used for store the privilege scope. -type GrantLevel struct { - Level GrantLevelType - DBName string - TableName string -} - -// Restore implements Node interface. -func (n *GrantLevel) Restore(ctx *format.RestoreCtx) error { - switch n.Level { - case GrantLevelDB: - if n.DBName == "" { - ctx.WritePlain("*") - } else { - ctx.WriteName(n.DBName) - ctx.WritePlain(".*") - } - case GrantLevelGlobal: - ctx.WritePlain("*.*") - case GrantLevelTable: - if n.DBName != "" { - ctx.WriteName(n.DBName) - ctx.WritePlain(".") - } - ctx.WriteName(n.TableName) - } - return nil -} - -// RevokeStmt is the struct for REVOKE statement. -type RevokeStmt struct { - stmtNode - - Privs []*PrivElem - ObjectType ObjectTypeType - Level *GrantLevel - Users []*UserSpec -} - -// Restore implements Node interface. -func (n *RevokeStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("REVOKE ") - for i, v := range n.Privs { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Privs[%d]", i) - } - } - ctx.WriteKeyWord(" ON ") - if n.ObjectType != ObjectTypeNone { - if err := n.ObjectType.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore RevokeStmt.ObjectType") - } - ctx.WritePlain(" ") - } - if err := n.Level.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore RevokeStmt.Level") - } - ctx.WriteKeyWord(" FROM ") - for i, v := range n.Users { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Users[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RevokeStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RevokeStmt) - for i, val := range n.Privs { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Privs[i] = node.(*PrivElem) - } - return v.Leave(n) -} - -// RevokeStmt is the struct for REVOKE statement. -type RevokeRoleStmt struct { - stmtNode - - Roles []*auth.RoleIdentity - Users []*auth.UserIdentity -} - -// Restore implements Node interface. -func (n *RevokeRoleStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("REVOKE ") - for i, role := range n.Roles { - if i != 0 { - ctx.WritePlain(", ") - } - if err := role.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Roles[%d]", i) - } - } - ctx.WriteKeyWord(" FROM ") - for i, v := range n.Users { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Users[%d]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RevokeRoleStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RevokeRoleStmt) - return v.Leave(n) -} - -// GrantStmt is the struct for GRANT statement. -type GrantStmt struct { - stmtNode - - Privs []*PrivElem - ObjectType ObjectTypeType - Level *GrantLevel - Users []*UserSpec - AuthTokenOrTLSOptions []*AuthTokenOrTLSOption - WithGrant bool -} - -// Restore implements Node interface. -func (n *GrantStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("GRANT ") - for i, v := range n.Privs { - if i != 0 && v.Priv != 0 { - ctx.WritePlain(", ") - } else if v.Priv == 0 { - ctx.WritePlain(" ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantStmt.Privs[%d]", i) - } - } - ctx.WriteKeyWord(" ON ") - if n.ObjectType != ObjectTypeNone { - if err := n.ObjectType.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore GrantStmt.ObjectType") - } - ctx.WritePlain(" ") - } - if err := n.Level.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore GrantStmt.Level") - } - ctx.WriteKeyWord(" TO ") - for i, v := range n.Users { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) - } - } - if n.AuthTokenOrTLSOptions != nil { - if len(n.AuthTokenOrTLSOptions) != 0 { - ctx.WriteKeyWord(" REQUIRE ") - } - for i, option := range n.AuthTokenOrTLSOptions { - if i != 0 { - ctx.WriteKeyWord(" AND ") - } - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantStmt.AuthTokenOrTLSOptions[%d]", i) - } - } - } - if n.WithGrant { - ctx.WriteKeyWord(" WITH GRANT OPTION") - } - return nil -} - -// SecureText implements SensitiveStatement interface. -func (n *GrantStmt) SecureText() string { - text := n.text - // Filter "identified by xxx" because it would expose password information. - idx := strings.Index(strings.ToLower(text), "identified") - if idx > 0 { - text = text[:idx] - } - return text -} - -// Accept implements Node Accept interface. -func (n *GrantStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*GrantStmt) - for i, val := range n.Privs { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Privs[i] = node.(*PrivElem) - } - return v.Leave(n) -} - -// GrantProxyStmt is the struct for GRANT PROXY statement. -type GrantProxyStmt struct { - stmtNode - - LocalUser *auth.UserIdentity - ExternalUsers []*auth.UserIdentity - WithGrant bool -} - -// Accept implements Node Accept interface. -func (n *GrantProxyStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*GrantProxyStmt) - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *GrantProxyStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("GRANT PROXY ON ") - if err := n.LocalUser.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantProxyStmt.LocalUser") - } - ctx.WriteKeyWord(" TO ") - for i, v := range n.ExternalUsers { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantProxyStmt.ExternalUsers[%d]", i) - } - } - if n.WithGrant { - ctx.WriteKeyWord(" WITH GRANT OPTION") - } - return nil -} - -// GrantRoleStmt is the struct for GRANT TO statement. -type GrantRoleStmt struct { - stmtNode - - Roles []*auth.RoleIdentity - Users []*auth.UserIdentity -} - -// Accept implements Node Accept interface. -func (n *GrantRoleStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*GrantRoleStmt) - return v.Leave(n) -} - -// Restore implements Node interface. -func (n *GrantRoleStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("GRANT ") - if len(n.Roles) > 0 { - for i, role := range n.Roles { - if i != 0 { - ctx.WritePlain(", ") - } - if err := role.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantRoleStmt.Roles[%d]", i) - } - } - } - ctx.WriteKeyWord(" TO ") - for i, v := range n.Users { - if i != 0 { - ctx.WritePlain(", ") - } - if err := v.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) - } - } - return nil -} - -// SecureText implements SensitiveStatement interface. -func (n *GrantRoleStmt) SecureText() string { - text := n.text - // Filter "identified by xxx" because it would expose password information. - idx := strings.Index(strings.ToLower(text), "identified") - if idx > 0 { - text = text[:idx] - } - return text -} - -// ShutdownStmt is a statement to stop the TiDB server. -// See https://dev.mysql.com/doc/refman/5.7/en/shutdown.html -type ShutdownStmt struct { - stmtNode -} - -// Restore implements Node interface. -func (n *ShutdownStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SHUTDOWN") - return nil -} - -// Accept implements Node Accept interface. -func (n *ShutdownStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*ShutdownStmt) - return v.Leave(n) -} - -// RestartStmt is a statement to restart the TiDB server. -// See https://dev.mysql.com/doc/refman/8.0/en/restart.html -type RestartStmt struct { - stmtNode -} - -// Restore implements Node interface. -func (n *RestartStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("RESTART") - return nil -} - -// Accept implements Node Accept interface. -func (n *RestartStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RestartStmt) - return v.Leave(n) -} - -// HelpStmt is a statement for server side help -// See https://dev.mysql.com/doc/refman/8.0/en/help.html -type HelpStmt struct { - stmtNode - - Topic string -} - -// Restore implements Node interface. -func (n *HelpStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("HELP ") - ctx.WriteString(n.Topic) - return nil -} - -// Accept implements Node Accept interface. -func (n *HelpStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*HelpStmt) - return v.Leave(n) -} - -// RenameUserStmt is a statement to rename a user. -// See http://dev.mysql.com/doc/refman/5.7/en/rename-user.html -type RenameUserStmt struct { - stmtNode - - UserToUsers []*UserToUser -} - -// Restore implements Node interface. -func (n *RenameUserStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("RENAME USER ") - for index, user2user := range n.UserToUsers { - if index != 0 { - ctx.WritePlain(", ") - } - if err := user2user.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore RenameUserStmt.UserToUsers") - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *RenameUserStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*RenameUserStmt) - - for i, t := range n.UserToUsers { - node, ok := t.Accept(v) - if !ok { - return n, false - } - n.UserToUsers[i] = node.(*UserToUser) - } - return v.Leave(n) -} - -// UserToUser represents renaming old user to new user used in RenameUserStmt. -type UserToUser struct { - node - OldUser *auth.UserIdentity - NewUser *auth.UserIdentity -} - -// Restore implements Node interface. -func (n *UserToUser) Restore(ctx *format.RestoreCtx) error { - if err := n.OldUser.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore UserToUser.OldUser") - } - ctx.WriteKeyWord(" TO ") - if err := n.NewUser.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore UserToUser.NewUser") - } - return nil -} - -// Accept implements Node Accept interface. -func (n *UserToUser) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*UserToUser) - return v.Leave(n) -} - -type BRIEKind uint8 -type BRIEOptionType uint16 - -const ( - BRIEKindBackup BRIEKind = iota - BRIEKindCancelJob - BRIEKindStreamStart - BRIEKindStreamMetaData - BRIEKindStreamStatus - BRIEKindStreamPause - BRIEKindStreamResume - BRIEKindStreamStop - BRIEKindStreamPurge - BRIEKindRestore - BRIEKindRestorePIT - BRIEKindShowJob - BRIEKindShowQuery - BRIEKindShowBackupMeta - // common BRIE options - BRIEOptionRateLimit BRIEOptionType = iota + 1 - BRIEOptionConcurrency - BRIEOptionChecksum - BRIEOptionSendCreds - BRIEOptionCheckpoint - BRIEOptionStartTS - BRIEOptionUntilTS - // backup options - BRIEOptionBackupTimeAgo - BRIEOptionBackupTS - BRIEOptionBackupTSO - BRIEOptionLastBackupTS - BRIEOptionLastBackupTSO - BRIEOptionGCTTL - // restore options - BRIEOptionOnline - BRIEOptionFullBackupStorage - BRIEOptionRestoredTS - // import options - BRIEOptionAnalyze - BRIEOptionBackend - BRIEOptionOnDuplicate - BRIEOptionSkipSchemaFiles - BRIEOptionStrictFormat - BRIEOptionTiKVImporter - BRIEOptionResume - // CSV options - BRIEOptionCSVBackslashEscape - BRIEOptionCSVDelimiter - BRIEOptionCSVHeader - BRIEOptionCSVNotNull - BRIEOptionCSVNull - BRIEOptionCSVSeparator - BRIEOptionCSVTrimLastSeparators - - BRIECSVHeaderIsColumns = ^uint64(0) -) - -type BRIEOptionLevel uint64 - -const ( - BRIEOptionLevelOff BRIEOptionLevel = iota // equals FALSE - BRIEOptionLevelRequired // equals TRUE - BRIEOptionLevelOptional -) - -func (kind BRIEKind) String() string { - switch kind { - case BRIEKindBackup: - return "BACKUP" - case BRIEKindRestore: - return "RESTORE" - case BRIEKindStreamStart: - return "BACKUP LOGS" - case BRIEKindStreamStop: - return "STOP BACKUP LOGS" - case BRIEKindStreamPause: - return "PAUSE BACKUP LOGS" - case BRIEKindStreamResume: - return "RESUME BACKUP LOGS" - case BRIEKindStreamStatus: - return "SHOW BACKUP LOGS STATUS" - case BRIEKindStreamMetaData: - return "SHOW BACKUP LOGS METADATA" - case BRIEKindStreamPurge: - return "PURGE BACKUP LOGS" - case BRIEKindRestorePIT: - return "RESTORE POINT" - case BRIEKindShowJob: - return "SHOW BR JOB" - case BRIEKindShowQuery: - return "SHOW BR JOB QUERY" - case BRIEKindCancelJob: - return "CANCEL BR JOB" - case BRIEKindShowBackupMeta: - return "SHOW BACKUP METADATA" - default: - return "" - } -} - -func (kind BRIEOptionType) String() string { - switch kind { - case BRIEOptionRateLimit: - return "RATE_LIMIT" - case BRIEOptionConcurrency: - return "CONCURRENCY" - case BRIEOptionChecksum: - return "CHECKSUM" - case BRIEOptionSendCreds: - return "SEND_CREDENTIALS_TO_TIKV" - case BRIEOptionBackupTimeAgo, BRIEOptionBackupTS, BRIEOptionBackupTSO: - return "SNAPSHOT" - case BRIEOptionLastBackupTS, BRIEOptionLastBackupTSO: - return "LAST_BACKUP" - case BRIEOptionOnline: - return "ONLINE" - case BRIEOptionCheckpoint: - return "CHECKPOINT" - case BRIEOptionAnalyze: - return "ANALYZE" - case BRIEOptionBackend: - return "BACKEND" - case BRIEOptionOnDuplicate: - return "ON_DUPLICATE" - case BRIEOptionSkipSchemaFiles: - return "SKIP_SCHEMA_FILES" - case BRIEOptionStrictFormat: - return "STRICT_FORMAT" - case BRIEOptionTiKVImporter: - return "TIKV_IMPORTER" - case BRIEOptionResume: - return "RESUME" - case BRIEOptionCSVBackslashEscape: - return "CSV_BACKSLASH_ESCAPE" - case BRIEOptionCSVDelimiter: - return "CSV_DELIMITER" - case BRIEOptionCSVHeader: - return "CSV_HEADER" - case BRIEOptionCSVNotNull: - return "CSV_NOT_NULL" - case BRIEOptionCSVNull: - return "CSV_NULL" - case BRIEOptionCSVSeparator: - return "CSV_SEPARATOR" - case BRIEOptionCSVTrimLastSeparators: - return "CSV_TRIM_LAST_SEPARATORS" - case BRIEOptionFullBackupStorage: - return "FULL_BACKUP_STORAGE" - case BRIEOptionRestoredTS: - return "RESTORED_TS" - case BRIEOptionStartTS: - return "START_TS" - case BRIEOptionUntilTS: - return "UNTIL_TS" - case BRIEOptionGCTTL: - return "GC_TTL" - default: - return "" - } -} - -func (level BRIEOptionLevel) String() string { - switch level { - case BRIEOptionLevelOff: - return "OFF" - case BRIEOptionLevelOptional: - return "OPTIONAL" - case BRIEOptionLevelRequired: - return "REQUIRED" - default: - return "" - } -} - -type BRIEOption struct { - Tp BRIEOptionType - StrValue string - UintValue uint64 -} - -func (opt *BRIEOption) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(opt.Tp.String()) - ctx.WritePlain(" = ") - switch opt.Tp { - case BRIEOptionBackupTS, BRIEOptionLastBackupTS, BRIEOptionBackend, BRIEOptionOnDuplicate, BRIEOptionTiKVImporter, BRIEOptionCSVDelimiter, BRIEOptionCSVNull, BRIEOptionCSVSeparator, BRIEOptionFullBackupStorage, BRIEOptionRestoredTS, BRIEOptionStartTS, BRIEOptionUntilTS, BRIEOptionGCTTL: - ctx.WriteString(opt.StrValue) - case BRIEOptionBackupTimeAgo: - ctx.WritePlainf("%d ", opt.UintValue/1000) - ctx.WriteKeyWord("MICROSECOND AGO") - case BRIEOptionRateLimit: - ctx.WritePlainf("%d ", opt.UintValue/1048576) - ctx.WriteKeyWord("MB") - ctx.WritePlain("/") - ctx.WriteKeyWord("SECOND") - case BRIEOptionCSVHeader: - if opt.UintValue == BRIECSVHeaderIsColumns { - ctx.WriteKeyWord("COLUMNS") - } else { - ctx.WritePlainf("%d", opt.UintValue) - } - case BRIEOptionChecksum, BRIEOptionAnalyze: - // BACKUP/RESTORE doesn't support OPTIONAL value for now, should warn at executor - ctx.WriteKeyWord(BRIEOptionLevel(opt.UintValue).String()) - default: - ctx.WritePlainf("%d", opt.UintValue) - } - return nil -} - -// BRIEStmt is a statement for backup, restore, import and export. -type BRIEStmt struct { - stmtNode - - Kind BRIEKind - Schemas []string - Tables []*TableName - Storage string - JobID int64 - Options []*BRIEOption -} - -func (n *BRIEStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*BRIEStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} - -func (n *BRIEStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(n.Kind.String()) - - switch n.Kind { - case BRIEKindRestore, BRIEKindBackup: - switch { - case len(n.Tables) != 0: - ctx.WriteKeyWord(" TABLE ") - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore BRIEStmt.Tables[%d]", index) - } - } - case len(n.Schemas) != 0: - ctx.WriteKeyWord(" DATABASE ") - for index, schema := range n.Schemas { - if index != 0 { - ctx.WritePlain(", ") - } - ctx.WriteName(schema) - } - default: - ctx.WriteKeyWord(" DATABASE") - ctx.WritePlain(" *") - } - - if n.Kind == BRIEKindBackup { - ctx.WriteKeyWord(" TO ") - ctx.WriteString(n.Storage) - } else { - ctx.WriteKeyWord(" FROM ") - ctx.WriteString(n.Storage) - } - case BRIEKindCancelJob, BRIEKindShowJob, BRIEKindShowQuery: - ctx.WritePlainf(" %d", n.JobID) - case BRIEKindStreamStart: - ctx.WriteKeyWord(" TO ") - ctx.WriteString(n.Storage) - case BRIEKindRestorePIT, BRIEKindStreamMetaData, BRIEKindShowBackupMeta, BRIEKindStreamPurge: - ctx.WriteKeyWord(" FROM ") - ctx.WriteString(n.Storage) - } - - for _, opt := range n.Options { - ctx.WritePlain(" ") - if err := opt.Restore(ctx); err != nil { - return err - } - } - - return nil -} - -// RedactURL redacts the secret tokens in the URL. only S3 url need redaction for now. -// if the url is not a valid url, return the original string. -func RedactURL(str string) string { - // FIXME: this solution is not scalable, and duplicates some logic from BR. - u, err := url.Parse(str) - if err != nil { - return str - } - scheme := u.Scheme - failpoint.Inject("forceRedactURL", func() { - scheme = "s3" - }) - if strings.ToLower(scheme) == "s3" { - values := u.Query() - for k := range values { - // see below on why we normalize key - // https://github.com/pingcap/tidb/blob/a7c0d95f16ea2582bb569278c3f829403e6c3a7e/br/pkg/storage/parse.go#L163 - normalizedKey := strings.ToLower(strings.ReplaceAll(k, "_", "-")) - if normalizedKey == "access-key" || normalizedKey == "secret-access-key" || normalizedKey == "session-token" { - values[k] = []string{"xxxxxx"} - } - } - u.RawQuery = values.Encode() - } - return u.String() -} - -// SecureText implements SensitiveStmtNode -func (n *BRIEStmt) SecureText() string { - redactedStmt := &BRIEStmt{ - Kind: n.Kind, - Schemas: n.Schemas, - Tables: n.Tables, - Storage: RedactURL(n.Storage), - Options: n.Options, - } - - var sb strings.Builder - _ = redactedStmt.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) - return sb.String() -} - -type LoadDataActionTp int - -const ( - LoadDataPause LoadDataActionTp = iota - LoadDataResume - LoadDataCancel - LoadDataDrop -) - -// LoadDataActionStmt represent PAUSE/RESUME/CANCEL/DROP LOAD DATA JOB statement. -type LoadDataActionStmt struct { - stmtNode - - Tp LoadDataActionTp - JobID int64 -} - -func (n *LoadDataActionStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - return v.Leave(newNode) -} - -func (n *LoadDataActionStmt) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case LoadDataPause: - ctx.WriteKeyWord("PAUSE LOAD DATA JOB ") - case LoadDataResume: - ctx.WriteKeyWord("RESUME LOAD DATA JOB ") - case LoadDataCancel: - ctx.WriteKeyWord("CANCEL LOAD DATA JOB ") - case LoadDataDrop: - ctx.WriteKeyWord("DROP LOAD DATA JOB ") - default: - return errors.Errorf("invalid load data action type: %d", n.Tp) - } - ctx.WritePlainf("%d", n.JobID) - return nil -} - -type ImportIntoActionTp string - -const ( - ImportIntoCancel ImportIntoActionTp = "cancel" -) - -// ImportIntoActionStmt represent CANCEL IMPORT INTO JOB statement. -// will support pause/resume/drop later. -type ImportIntoActionStmt struct { - stmtNode - - Tp ImportIntoActionTp - JobID int64 -} - -func (n *ImportIntoActionStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - return v.Leave(newNode) -} - -func (n *ImportIntoActionStmt) Restore(ctx *format.RestoreCtx) error { - if n.Tp != ImportIntoCancel { - return errors.Errorf("invalid IMPORT INTO action type: %s", n.Tp) - } - ctx.WriteKeyWord("CANCEL IMPORT JOB ") - ctx.WritePlainf("%d", n.JobID) - return nil -} - -// Ident is the table identifier composed of schema name and table name. -type Ident struct { - Schema model.CIStr - Name model.CIStr -} - -// String implements fmt.Stringer interface. -func (i Ident) String() string { - if i.Schema.O == "" { - return i.Name.O - } - return fmt.Sprintf("%s.%s", i.Schema, i.Name) -} - -// SelectStmtOpts wrap around select hints and switches -type SelectStmtOpts struct { - Distinct bool - SQLBigResult bool - SQLBufferResult bool - SQLCache bool - SQLSmallResult bool - CalcFoundRows bool - StraightJoin bool - Priority mysql.PriorityEnum - TableHints []*TableOptimizerHint - ExplicitAll bool -} - -// TableOptimizerHint is Table level optimizer hint -type TableOptimizerHint struct { - node - // HintName is the name or alias of the table(s) which the hint will affect. - // Table hints has no schema info - // It allows only table name or alias (if table has an alias) - HintName model.CIStr - // HintData is the payload of the hint. The actual type of this field - // is defined differently as according `HintName`. Define as following: - // - // Statement Execution Time Optimizer Hints - // See https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time - // - MAX_EXECUTION_TIME => uint64 - // - MEMORY_QUOTA => int64 - // - QUERY_TYPE => model.CIStr - // - // Time Range is used to hint the time range of inspection tables - // e.g: select /*+ time_range('','') */ * from information_schema.inspection_result. - // - TIME_RANGE => ast.HintTimeRange - // - READ_FROM_STORAGE => model.CIStr - // - USE_TOJA => bool - // - NTH_PLAN => int64 - HintData interface{} - // QBName is the default effective query block of this hint. - QBName model.CIStr - Tables []HintTable - Indexes []model.CIStr -} - -// HintTimeRange is the payload of `TIME_RANGE` hint -type HintTimeRange struct { - From string - To string -} - -// HintSetVar is the payload of `SET_VAR` hint -type HintSetVar struct { - VarName string - Value string -} - -// HintTable is table in the hint. It may have query block info. -type HintTable struct { - DBName model.CIStr - TableName model.CIStr - QBName model.CIStr - PartitionList []model.CIStr -} - -func (ht *HintTable) Restore(ctx *format.RestoreCtx) { - if ht.DBName.L != "" { - ctx.WriteName(ht.DBName.String()) - ctx.WriteKeyWord(".") - } - ctx.WriteName(ht.TableName.String()) - if ht.QBName.L != "" { - ctx.WriteKeyWord("@") - ctx.WriteName(ht.QBName.String()) - } - if len(ht.PartitionList) > 0 { - ctx.WriteKeyWord(" PARTITION") - ctx.WritePlain("(") - for i, p := range ht.PartitionList { - if i > 0 { - ctx.WritePlain(", ") - } - ctx.WriteName(p.String()) - } - ctx.WritePlain(")") - } -} - -// Restore implements Node interface. -func (n *TableOptimizerHint) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(n.HintName.String()) - ctx.WritePlain("(") - if n.QBName.L != "" { - if n.HintName.L != "qb_name" { - ctx.WriteKeyWord("@") - } - ctx.WriteName(n.QBName.String()) - } - if n.HintName.L == "qb_name" && len(n.Tables) == 0 { - ctx.WritePlain(")") - return nil - } - // Hints without args except query block. - switch n.HintName.L { - case "mpp_1phase_agg", "mpp_2phase_agg", "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "ignore_plan_cache", "limit_to_cop", "straight_join", "merge", "no_decorrelate": - ctx.WritePlain(")") - return nil - } - if n.QBName.L != "" { - ctx.WritePlain(" ") - } - // Hints with args except query block. - switch n.HintName.L { - case "max_execution_time": - ctx.WritePlainf("%d", n.HintData.(uint64)) - case "resource_group": - ctx.WriteName(n.HintData.(string)) - case "nth_plan": - ctx.WritePlainf("%d", n.HintData.(int64)) - case "tidb_hj", "tidb_smj", "tidb_inlj", "hash_join", "hash_join_build", "hash_join_probe", "merge_join", "inl_join", "broadcast_join", "shuffle_join", "inl_hash_join", "inl_merge_join", "leading": - for i, table := range n.Tables { - if i != 0 { - ctx.WritePlain(", ") - } - table.Restore(ctx) - } - case "use_index", "ignore_index", "use_index_merge", "force_index", "order_index", "no_order_index": - n.Tables[0].Restore(ctx) - ctx.WritePlain(" ") - for i, index := range n.Indexes { - if i != 0 { - ctx.WritePlain(", ") - } - ctx.WriteName(index.String()) - } - case "qb_name": - if len(n.Tables) > 0 { - ctx.WritePlain(", ") - for i, table := range n.Tables { - if i != 0 { - ctx.WritePlain(". ") - } - table.Restore(ctx) - } - } - case "use_toja", "use_cascades": - if n.HintData.(bool) { - ctx.WritePlain("TRUE") - } else { - ctx.WritePlain("FALSE") - } - case "query_type": - ctx.WriteKeyWord(n.HintData.(model.CIStr).String()) - case "memory_quota": - ctx.WritePlainf("%d MB", n.HintData.(int64)/1024/1024) - case "read_from_storage": - ctx.WriteKeyWord(n.HintData.(model.CIStr).String()) - for i, table := range n.Tables { - if i == 0 { - ctx.WritePlain("[") - } - table.Restore(ctx) - if i == len(n.Tables)-1 { - ctx.WritePlain("]") - } else { - ctx.WritePlain(", ") - } - } - case "time_range": - hintData := n.HintData.(HintTimeRange) - ctx.WriteString(hintData.From) - ctx.WritePlain(", ") - ctx.WriteString(hintData.To) - case "set_var": - hintData := n.HintData.(HintSetVar) - ctx.WritePlain(hintData.VarName) - ctx.WritePlain(" = ") - ctx.WritePlain(hintData.Value) - } - ctx.WritePlain(")") - return nil -} - -// Accept implements Node Accept interface. -func (n *TableOptimizerHint) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*TableOptimizerHint) - return v.Leave(n) -} - -// TextString represent a string, it can be a binary literal. -type TextString struct { - Value string - IsBinaryLiteral bool -} - -type BinaryLiteral interface { - ToString() string -} - -// NewDecimal creates a types.Decimal value, it's provided by parser driver. -var NewDecimal func(string) (interface{}, error) - -// NewHexLiteral creates a types.HexLiteral value, it's provided by parser driver. -var NewHexLiteral func(string) (interface{}, error) - -// NewBitLiteral creates a types.BitLiteral value, it's provided by parser driver. -var NewBitLiteral func(string) (interface{}, error) - -// SetResourceGroupStmt is a statement to set the resource group name for current session. -type SetResourceGroupStmt struct { - stmtNode - Name model.CIStr -} - -func (n *SetResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("SET RESOURCE GROUP ") - ctx.WriteName(n.Name.O) - return nil -} - -// Accept implements Node Accept interface. -func (n *SetResourceGroupStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*SetResourceGroupStmt) - return v.Leave(n) -} - -// CalibrateResourceType is the type for CalibrateResource statement. -type CalibrateResourceType int - -// calibrate resource [ workload < TPCC | OLTP_READ_WRITE | OLTP_READ_ONLY | OLTP_WRITE_ONLY | TPCH_10> ] -const ( - WorkloadNone CalibrateResourceType = iota - TPCC - OLTPREADWRITE - OLTPREADONLY - OLTPWRITEONLY - TPCH10 -) - -func (n CalibrateResourceType) Restore(ctx *format.RestoreCtx) error { - switch n { - case TPCC: - ctx.WriteKeyWord(" WORKLOAD TPCC") - case OLTPREADWRITE: - ctx.WriteKeyWord(" WORKLOAD OLTP_READ_WRITE") - case OLTPREADONLY: - ctx.WriteKeyWord(" WORKLOAD OLTP_READ_ONLY") - case OLTPWRITEONLY: - ctx.WriteKeyWord(" WORKLOAD OLTP_WRITE_ONLY") - case TPCH10: - ctx.WriteKeyWord(" WORKLOAD TPCH_10") - } - return nil -} - -// CalibrateResourceStmt is a statement to fetch the cluster RU capacity -type CalibrateResourceStmt struct { - stmtNode - DynamicCalibrateResourceOptionList []*DynamicCalibrateResourceOption - Tp CalibrateResourceType -} - -// Restore implements Node interface. -func (n *CalibrateResourceStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("CALIBRATE RESOURCE") - if err := n.Tp.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore CalibrateResourceStmt.CalibrateResourceType") - } - for i, option := range n.DynamicCalibrateResourceOptionList { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing DynamicCalibrateResourceOption: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *CalibrateResourceStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*CalibrateResourceStmt) - for _, val := range n.DynamicCalibrateResourceOptionList { - _, ok := val.Accept(v) - if !ok { - return n, false - } - } - return v.Leave(n) -} - -type DynamicCalibrateType int - -const ( - // specific time - CalibrateStartTime = iota - CalibrateEndTime - CalibrateDuration -) - -type DynamicCalibrateResourceOption struct { - stmtNode - Tp DynamicCalibrateType - StrValue string - Ts ExprNode - Unit TimeUnitType -} - -func (n *DynamicCalibrateResourceOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case CalibrateStartTime: - ctx.WriteKeyWord("START_TIME ") - if err := n.Ts.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing DynamicCalibrateResourceOption StartTime") - } - case CalibrateEndTime: - ctx.WriteKeyWord("END_TIME ") - if err := n.Ts.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while splicing DynamicCalibrateResourceOption EndTime") - } - case CalibrateDuration: - ctx.WriteKeyWord("DURATION ") - if len(n.StrValue) > 0 { - ctx.WriteString(n.StrValue) - } else { - ctx.WriteKeyWord("INTERVAL ") - if err := n.Ts.Restore(ctx); err != nil { - return errors.Annotate(err, "An error occurred while restore DynamicCalibrateResourceOption DURATION TS") - } - ctx.WritePlain(" ") - ctx.WriteKeyWord(n.Unit.String()) - } - default: - return errors.Errorf("invalid DynamicCalibrateResourceOption: %d", n.Tp) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *DynamicCalibrateResourceOption) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DynamicCalibrateResourceOption) - if n.Ts != nil { - node, ok := n.Ts.Accept(v) - if !ok { - return n, false - } - n.Ts = node.(ExprNode) - } - return v.Leave(n) -} - -// DropQueryWatchStmt is a statement to drop a runaway watch item. -type DropQueryWatchStmt struct { - stmtNode - IntValue int64 -} - -func (n *DropQueryWatchStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("QUERY WATCH REMOVE ") - ctx.WritePlainf("%d", n.IntValue) - return nil -} - -// Accept implements Node Accept interface. -func (n *DropQueryWatchStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - n = newNode.(*DropQueryWatchStmt) - return v.Leave(n) -} - -// AddQueryWatchStmt is a statement to add a runaway watch item. -type AddQueryWatchStmt struct { - stmtNode - QueryWatchOptionList []*QueryWatchOption -} - -func (n *AddQueryWatchStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("QUERY WATCH ADD") - for i, option := range n.QueryWatchOptionList { - ctx.WritePlain(" ") - if err := option.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing QueryWatchOptionList: [%v]", i) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AddQueryWatchStmt) Accept(v Visitor) (Node, bool) { - newNode, _ := v.Enter(n) - n = newNode.(*AddQueryWatchStmt) - for _, val := range n.QueryWatchOptionList { - _, ok := val.Accept(v) - if !ok { - return n, false - } - } - return v.Leave(n) -} - -type QueryWatchOptionType int - -const ( - QueryWatchResourceGroup QueryWatchOptionType = iota - QueryWatchAction - QueryWatchType -) - -// QueryWatchOption is used for parsing manual management of watching runaway queries option. -type QueryWatchOption struct { - stmtNode - Tp QueryWatchOptionType - StrValue model.CIStr - IntValue int32 - ExprValue ExprNode - BoolValue bool -} - -func (n *QueryWatchOption) Restore(ctx *format.RestoreCtx) error { - switch n.Tp { - case QueryWatchResourceGroup: - ctx.WriteKeyWord("RESOURCE GROUP ") - if n.ExprValue != nil { - if err := n.ExprValue.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing ExprValue: [%v]", n.ExprValue) - } - } else { - ctx.WriteName(n.StrValue.O) - } - case QueryWatchAction: - ctx.WriteKeyWord("ACTION ") - ctx.WritePlain("= ") - ctx.WriteKeyWord(model.RunawayActionType(n.IntValue).String()) - case QueryWatchType: - if n.BoolValue { - ctx.WriteKeyWord("SQL TEXT ") - ctx.WriteKeyWord(model.RunawayWatchType(n.IntValue).String()) - ctx.WriteKeyWord(" TO ") - } else { - switch n.IntValue { - case int32(model.WatchSimilar): - ctx.WriteKeyWord("SQL DIGEST ") - case int32(model.WatchPlan): - ctx.WriteKeyWord("PLAN DIGEST ") - } - } - if err := n.ExprValue.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while splicing ExprValue: [%v]", n.ExprValue) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *QueryWatchOption) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*QueryWatchOption) - if n.ExprValue != nil { - node, ok := n.ExprValue.Accept(v) - if !ok { - return n, false - } - n.ExprValue = node.(ExprNode) - } - return v.Leave(n) -} - -func CheckQueryWatchAppend(ops []*QueryWatchOption, newOp *QueryWatchOption) bool { - for _, op := range ops { - if op.Tp == newOp.Tp { - return false - } - } - return true -} diff --git a/parser/ast/misc_test.go b/parser/ast/misc_test.go deleted file mode 100644 index 9bba612433f73..0000000000000 --- a/parser/ast/misc_test.go +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast_test - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/stretchr/testify/require" -) - -type visitor struct{} - -func (v visitor) Enter(in ast.Node) (ast.Node, bool) { - return in, false -} - -func (v visitor) Leave(in ast.Node) (ast.Node, bool) { - return in, true -} - -type visitor1 struct { - visitor -} - -func (visitor1) Enter(in ast.Node) (ast.Node, bool) { - return in, true -} - -func TestMiscVisitorCover(t *testing.T) { - valueExpr := ast.NewValueExpr(42, mysql.DefaultCharset, mysql.DefaultCollationName) - stmts := []ast.Node{ - &ast.AdminStmt{}, - &ast.AlterUserStmt{}, - &ast.BeginStmt{}, - &ast.BinlogStmt{}, - &ast.CommitStmt{}, - &ast.CompactTableStmt{Table: &ast.TableName{}}, - &ast.CreateUserStmt{}, - &ast.DeallocateStmt{}, - &ast.DoStmt{}, - &ast.ExecuteStmt{UsingVars: []ast.ExprNode{valueExpr}}, - &ast.ExplainStmt{Stmt: &ast.ShowStmt{}}, - &ast.GrantStmt{}, - &ast.PrepareStmt{SQLVar: &ast.VariableExpr{Value: valueExpr}}, - &ast.RollbackStmt{}, - &ast.SetPwdStmt{}, - &ast.SetStmt{Variables: []*ast.VariableAssignment{ - { - Value: valueExpr, - }, - }}, - &ast.UseStmt{}, - &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{ - {}, - }, - }, - &ast.FlushStmt{}, - &ast.PrivElem{}, - &ast.VariableAssignment{Value: valueExpr}, - &ast.KillStmt{}, - &ast.DropStatsStmt{ - Tables: []*ast.TableName{ - {}, - }, - }, - &ast.ShutdownStmt{}, - } - - for _, v := range stmts { - v.Accept(visitor{}) - v.Accept(visitor1{}) - } -} - -func TestDDLVisitorCoverMisc(t *testing.T) { - sql := ` -create table t (c1 smallint unsigned, c2 int unsigned); -alter table t add column a smallint unsigned after b; -alter table t add column (a int, constraint check (a > 0)); -create index t_i on t (id); -create database test character set utf8; -drop database test; -drop index t_i on t; -drop table t; -truncate t; -create table t ( -jobAbbr char(4) not null, -constraint foreign key (jobabbr) references ffxi_jobtype (jobabbr) on delete cascade on update cascade -); -` - parse := parser.New() - stmts, _, err := parse.Parse(sql, "", "") - require.NoError(t, err) - for _, stmt := range stmts { - stmt.Accept(visitor{}) - stmt.Accept(visitor1{}) - } -} - -func TestDMLVistorCover(t *testing.T) { - sql := `delete from somelog where user = 'jcole' order by timestamp_column limit 1; -delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id; -select * from t where exists(select * from t k where t.c = k.c having sum(c) = 1); -insert into t_copy select * from t where t.x > 5; -(select /*+ TIDB_INLJ(t1) */ a from t1 where a=10 and b=1) union (select /*+ TIDB_SMJ(t2) */ a from t2 where a=11 and b=2) order by a limit 10; -update t1 set col1 = col1 + 1, col2 = col1; -show create table t; -load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'; -import into t from '/file.csv'` - - p := parser.New() - stmts, _, err := p.Parse(sql, "", "") - require.NoError(t, err) - for _, stmt := range stmts { - stmt.Accept(visitor{}) - stmt.Accept(visitor1{}) - } -} - -// test Change Pump or drainer status sql parser -func TestChangeStmt(t *testing.T) { - sql := `change pump to node_state='paused' for node_id '127.0.0.1:8249'; -change drainer to node_state='paused' for node_id '127.0.0.1:8249'; -shutdown;` - - p := parser.New() - stmts, _, err := p.Parse(sql, "", "") - require.NoError(t, err) - for _, stmt := range stmts { - stmt.Accept(visitor{}) - stmt.Accept(visitor1{}) - } -} - -func TestSensitiveStatement(t *testing.T) { - positive := []ast.StmtNode{ - &ast.SetPwdStmt{}, - &ast.CreateUserStmt{}, - &ast.AlterUserStmt{}, - &ast.GrantStmt{}, - } - for i, stmt := range positive { - _, ok := stmt.(ast.SensitiveStmtNode) - require.Truef(t, ok, "%d, %#v fail", i, stmt) - } - - negative := []ast.StmtNode{ - &ast.DropUserStmt{}, - &ast.RevokeStmt{}, - &ast.AlterTableStmt{}, - &ast.CreateDatabaseStmt{}, - &ast.CreateIndexStmt{}, - &ast.CreateTableStmt{}, - &ast.DropDatabaseStmt{}, - &ast.DropIndexStmt{}, - &ast.DropTableStmt{}, - &ast.RenameTableStmt{}, - &ast.TruncateTableStmt{}, - } - for _, stmt := range negative { - _, ok := stmt.(ast.SensitiveStmtNode) - require.False(t, ok) - } -} - -func TestUserSpec(t *testing.T) { - hashString := "*3D56A309CD04FA2EEF181462E59011F075C89548" - u := ast.UserSpec{ - User: &auth.UserIdentity{ - Username: "test", - }, - AuthOpt: &ast.AuthOption{ - ByAuthString: false, - AuthString: "xxx", - HashString: hashString, - }, - } - pwd, ok := u.EncodedPassword() - require.True(t, ok) - require.Equal(t, u.AuthOpt.HashString, pwd) - - u.AuthOpt.HashString = "not-good-password-format" - _, ok = u.EncodedPassword() - require.False(t, ok) - - u.AuthOpt.ByAuthString = true - pwd, ok = u.EncodedPassword() - require.True(t, ok) - require.Equal(t, hashString, pwd) - - u.AuthOpt.AuthString = "" - pwd, ok = u.EncodedPassword() - require.True(t, ok) - require.Equal(t, "", pwd) -} - -func TestTableOptimizerHintRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"USE_INDEX(t1 c1)", "USE_INDEX(`t1` `c1`)"}, - {"USE_INDEX(test.t1 c1)", "USE_INDEX(`test`.`t1` `c1`)"}, - {"USE_INDEX(@sel_1 t1 c1)", "USE_INDEX(@`sel_1` `t1` `c1`)"}, - {"USE_INDEX(t1@sel_1 c1)", "USE_INDEX(`t1`@`sel_1` `c1`)"}, - {"USE_INDEX(test.t1@sel_1 c1)", "USE_INDEX(`test`.`t1`@`sel_1` `c1`)"}, - {"USE_INDEX(test.t1@sel_1 partition(p0) c1)", "USE_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, - {"FORCE_INDEX(t1 c1)", "FORCE_INDEX(`t1` `c1`)"}, - {"FORCE_INDEX(test.t1 c1)", "FORCE_INDEX(`test`.`t1` `c1`)"}, - {"FORCE_INDEX(@sel_1 t1 c1)", "FORCE_INDEX(@`sel_1` `t1` `c1`)"}, - {"FORCE_INDEX(t1@sel_1 c1)", "FORCE_INDEX(`t1`@`sel_1` `c1`)"}, - {"FORCE_INDEX(test.t1@sel_1 c1)", "FORCE_INDEX(`test`.`t1`@`sel_1` `c1`)"}, - {"FORCE_INDEX(test.t1@sel_1 partition(p0) c1)", "FORCE_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, - {"IGNORE_INDEX(t1 c1)", "IGNORE_INDEX(`t1` `c1`)"}, - {"IGNORE_INDEX(@sel_1 t1 c1)", "IGNORE_INDEX(@`sel_1` `t1` `c1`)"}, - {"IGNORE_INDEX(t1@sel_1 c1)", "IGNORE_INDEX(`t1`@`sel_1` `c1`)"}, - {"IGNORE_INDEX(t1@sel_1 partition(p0, p1) c1)", "IGNORE_INDEX(`t1`@`sel_1` PARTITION(`p0`, `p1`) `c1`)"}, - {"ORDER_INDEX(t1 c1)", "ORDER_INDEX(`t1` `c1`)"}, - {"ORDER_INDEX(test.t1 c1)", "ORDER_INDEX(`test`.`t1` `c1`)"}, - {"ORDER_INDEX(@sel_1 t1 c1)", "ORDER_INDEX(@`sel_1` `t1` `c1`)"}, - {"ORDER_INDEX(t1@sel_1 c1)", "ORDER_INDEX(`t1`@`sel_1` `c1`)"}, - {"ORDER_INDEX(test.t1@sel_1 c1)", "ORDER_INDEX(`test`.`t1`@`sel_1` `c1`)"}, - {"ORDER_INDEX(test.t1@sel_1 partition(p0) c1)", "ORDER_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, - {"NO_ORDER_INDEX(t1 c1)", "NO_ORDER_INDEX(`t1` `c1`)"}, - {"NO_ORDER_INDEX(test.t1 c1)", "NO_ORDER_INDEX(`test`.`t1` `c1`)"}, - {"NO_ORDER_INDEX(@sel_1 t1 c1)", "NO_ORDER_INDEX(@`sel_1` `t1` `c1`)"}, - {"NO_ORDER_INDEX(t1@sel_1 c1)", "NO_ORDER_INDEX(`t1`@`sel_1` `c1`)"}, - {"NO_ORDER_INDEX(test.t1@sel_1 c1)", "NO_ORDER_INDEX(`test`.`t1`@`sel_1` `c1`)"}, - {"NO_ORDER_INDEX(test.t1@sel_1 partition(p0) c1)", "NO_ORDER_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, - {"TIDB_SMJ(`t1`)", "TIDB_SMJ(`t1`)"}, - {"TIDB_SMJ(t1)", "TIDB_SMJ(`t1`)"}, - {"TIDB_SMJ(t1,t2)", "TIDB_SMJ(`t1`, `t2`)"}, - {"TIDB_SMJ(@sel1 t1,t2)", "TIDB_SMJ(@`sel1` `t1`, `t2`)"}, - {"TIDB_SMJ(t1@sel1,t2@sel2)", "TIDB_SMJ(`t1`@`sel1`, `t2`@`sel2`)"}, - {"TIDB_INLJ(t1,t2)", "TIDB_INLJ(`t1`, `t2`)"}, - {"TIDB_INLJ(@sel1 t1,t2)", "TIDB_INLJ(@`sel1` `t1`, `t2`)"}, - {"TIDB_INLJ(t1@sel1,t2@sel2)", "TIDB_INLJ(`t1`@`sel1`, `t2`@`sel2`)"}, - {"TIDB_HJ(t1,t2)", "TIDB_HJ(`t1`, `t2`)"}, - {"TIDB_HJ(@sel1 t1,t2)", "TIDB_HJ(@`sel1` `t1`, `t2`)"}, - {"TIDB_HJ(t1@sel1,t2@sel2)", "TIDB_HJ(`t1`@`sel1`, `t2`@`sel2`)"}, - {"MERGE_JOIN(t1,t2)", "MERGE_JOIN(`t1`, `t2`)"}, - {"BROADCAST_JOIN(t1,t2)", "BROADCAST_JOIN(`t1`, `t2`)"}, - {"INL_HASH_JOIN(t1,t2)", "INL_HASH_JOIN(`t1`, `t2`)"}, - {"INL_MERGE_JOIN(t1,t2)", "INL_MERGE_JOIN(`t1`, `t2`)"}, - {"INL_JOIN(t1,t2)", "INL_JOIN(`t1`, `t2`)"}, - {"HASH_JOIN(t1,t2)", "HASH_JOIN(`t1`, `t2`)"}, - {"HASH_JOIN_BUILD(t1)", "HASH_JOIN_BUILD(`t1`)"}, - {"HASH_JOIN_PROBE(t1)", "HASH_JOIN_PROBE(`t1`)"}, - {"LEADING(t1)", "LEADING(`t1`)"}, - {"LEADING(t1, c1)", "LEADING(`t1`, `c1`)"}, - {"LEADING(t1, c1, t2)", "LEADING(`t1`, `c1`, `t2`)"}, - {"LEADING(@sel1 t1, c1)", "LEADING(@`sel1` `t1`, `c1`)"}, - {"LEADING(@sel1 t1)", "LEADING(@`sel1` `t1`)"}, - {"LEADING(@sel1 t1, c1, t2)", "LEADING(@`sel1` `t1`, `c1`, `t2`)"}, - {"LEADING(t1@sel1)", "LEADING(`t1`@`sel1`)"}, - {"LEADING(t1@sel1, c1)", "LEADING(`t1`@`sel1`, `c1`)"}, - {"LEADING(t1@sel1, c1, t2)", "LEADING(`t1`@`sel1`, `c1`, `t2`)"}, - {"MAX_EXECUTION_TIME(3000)", "MAX_EXECUTION_TIME(3000)"}, - {"MAX_EXECUTION_TIME(@sel1 3000)", "MAX_EXECUTION_TIME(@`sel1` 3000)"}, - {"USE_INDEX_MERGE(t1 c1)", "USE_INDEX_MERGE(`t1` `c1`)"}, - {"USE_INDEX_MERGE(@sel1 t1 c1)", "USE_INDEX_MERGE(@`sel1` `t1` `c1`)"}, - {"USE_INDEX_MERGE(t1@sel1 c1)", "USE_INDEX_MERGE(`t1`@`sel1` `c1`)"}, - {"USE_TOJA(TRUE)", "USE_TOJA(TRUE)"}, - {"USE_TOJA(FALSE)", "USE_TOJA(FALSE)"}, - {"USE_TOJA(@sel1 TRUE)", "USE_TOJA(@`sel1` TRUE)"}, - {"USE_CASCADES(TRUE)", "USE_CASCADES(TRUE)"}, - {"USE_CASCADES(FALSE)", "USE_CASCADES(FALSE)"}, - {"USE_CASCADES(@sel1 TRUE)", "USE_CASCADES(@`sel1` TRUE)"}, - {"QUERY_TYPE(OLAP)", "QUERY_TYPE(OLAP)"}, - {"QUERY_TYPE(OLTP)", "QUERY_TYPE(OLTP)"}, - {"QUERY_TYPE(@sel1 OLTP)", "QUERY_TYPE(@`sel1` OLTP)"}, - {"NTH_PLAN(10)", "NTH_PLAN(10)"}, - {"NTH_PLAN(@sel1 30)", "NTH_PLAN(@`sel1` 30)"}, - {"MEMORY_QUOTA(1 GB)", "MEMORY_QUOTA(1024 MB)"}, - {"MEMORY_QUOTA(@sel1 1 GB)", "MEMORY_QUOTA(@`sel1` 1024 MB)"}, - {"HASH_AGG()", "HASH_AGG()"}, - {"HASH_AGG(@sel1)", "HASH_AGG(@`sel1`)"}, - {"STREAM_AGG()", "STREAM_AGG()"}, - {"STREAM_AGG(@sel1)", "STREAM_AGG(@`sel1`)"}, - {"AGG_TO_COP()", "AGG_TO_COP()"}, - {"AGG_TO_COP(@sel_1)", "AGG_TO_COP(@`sel_1`)"}, - {"LIMIT_TO_COP()", "LIMIT_TO_COP()"}, - {"MERGE()", "MERGE()"}, - {"STRAIGHT_JOIN()", "STRAIGHT_JOIN()"}, - {"NO_INDEX_MERGE()", "NO_INDEX_MERGE()"}, - {"NO_INDEX_MERGE(@sel1)", "NO_INDEX_MERGE(@`sel1`)"}, - {"READ_CONSISTENT_REPLICA()", "READ_CONSISTENT_REPLICA()"}, - {"READ_CONSISTENT_REPLICA(@sel1)", "READ_CONSISTENT_REPLICA(@`sel1`)"}, - {"QB_NAME(sel1)", "QB_NAME(`sel1`)"}, - {"READ_FROM_STORAGE(@sel TIFLASH[t1, t2])", "READ_FROM_STORAGE(@`sel` TIFLASH[`t1`, `t2`])"}, - {"READ_FROM_STORAGE(@sel TIFLASH[t1 partition(p0)])", "READ_FROM_STORAGE(@`sel` TIFLASH[`t1` PARTITION(`p0`)])"}, - {"TIME_RANGE('2020-02-02 10:10:10','2020-02-02 11:10:10')", "TIME_RANGE('2020-02-02 10:10:10', '2020-02-02 11:10:10')"}, - {"RESOURCE_GROUP(rg1)", "RESOURCE_GROUP(`rg1`)"}, - {"RESOURCE_GROUP(`default`)", "RESOURCE_GROUP(`default`)"}, - } - extractNodeFunc := func(node ast.Node) ast.Node { - return node.(*ast.SelectStmt).TableHints[0] - } - runNodeRestoreTest(t, testCases, "select /*+ %s */ * from t1 join t2", extractNodeFunc) -} - -func TestChangeStmtRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'", "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'"}, - {"CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'", "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'"}, - } - extractNodeFunc := func(node ast.Node) ast.Node { - return node.(*ast.ChangeStmt) - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestBRIESecureText(t *testing.T) { - testCases := []struct { - input string - secured string - }{ - { - input: "restore database * from 'local:///tmp/br01' snapshot = 23333", - secured: `^\QRESTORE DATABASE * FROM 'local:///tmp/br01' SNAPSHOT = 23333\E$`, - }, - { - input: "backup database * to 's3://bucket/prefix?region=us-west-2'", - secured: `^\QBACKUP DATABASE * TO 's3://bucket/prefix?region=us-west-2'\E$`, - }, - { - // we need to use regexp to match to avoid the random ordering since a map was used. - // unfortunately Go's regexp doesn't support lookahead assertion, so the test case below - // has false positives. - input: "backup database * to 's3://bucket/prefix?access-key=abcdefghi&secret-access-key=123&force-path-style=true'", - secured: `^\QBACKUP DATABASE * TO 's3://bucket/prefix?\E((access-key=xxxxxx|force-path-style=true|secret-access-key=xxxxxx)(&|'$)){3}`, - }, - { - input: "backup database * to 'gcs://bucket/prefix?access-key=irrelevant&credentials-file=/home/user/secrets.txt'", - secured: `^\QBACKUP DATABASE * TO 'gcs://bucket/prefix?\E((access-key=irrelevant|credentials-file=/home/user/secrets\.txt)(&|'$)){2}`, - }, - } - - p := parser.New() - for _, tc := range testCases { - comment := fmt.Sprintf("input = %s", tc.input) - node, err := p.ParseOneStmt(tc.input, "", "") - require.NoError(t, err, comment) - n, ok := node.(ast.SensitiveStmtNode) - require.True(t, ok, comment) - require.Regexp(t, tc.secured, n.SecureText(), comment) - } -} - -func TestCompactTableStmtRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"alter table abc compact tiflash replica", "ALTER TABLE `abc` COMPACT TIFLASH REPLICA"}, - {"alter table abc compact", "ALTER TABLE `abc` COMPACT"}, - {"alter table test.abc compact", "ALTER TABLE `test`.`abc` COMPACT"}, - } - extractNodeFunc := func(node ast.Node) ast.Node { - return node.(*ast.CompactTableStmt) - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestPlanReplayerStmtRestore(t *testing.T) { - testCases := []NodeRestoreTestCase{ - {"plan replayer dump with stats as of timestamp '2023-06-28 12:34:00' explain select * from t where a > 10", - "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP _UTF8MB4'2023-06-28 12:34:00' EXPLAIN SELECT * FROM `t` WHERE `a`>10"}, - {"plan replayer dump explain analyze select * from t where a > 10", - "PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, - {"plan replayer dump with stats as of timestamp 12345 explain analyze select * from t where a > 10", - "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP 12345 EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, - {"plan replayer dump explain analyze 'test'", - "PLAN REPLAYER DUMP EXPLAIN ANALYZE 'test'"}, - {"plan replayer dump with stats as of timestamp '12345' explain analyze 'test2'", - "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP _UTF8MB4'12345' EXPLAIN ANALYZE 'test2'"}, - } - extractNodeFunc := func(node ast.Node) ast.Node { - return node.(*ast.PlanReplayerStmt) - } - runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) -} - -func TestRedactURL(t *testing.T) { - type args struct { - str string - } - tests := []struct { - args args - want string - }{ - {args{""}, ""}, - {args{":"}, ":"}, - {args{"~/file"}, "~/file"}, - {args{"gs://bucket/file"}, "gs://bucket/file"}, - // gs don't have access-key/secret-access-key, so it will NOT be redacted - {args{"gs://bucket/file?access-key=123"}, "gs://bucket/file?access-key=123"}, - {args{"gs://bucket/file?secret-access-key=123"}, "gs://bucket/file?secret-access-key=123"}, - {args{"s3://bucket/file"}, "s3://bucket/file"}, - {args{"s3://bucket/file?other-key=123"}, "s3://bucket/file?other-key=123"}, - {args{"s3://bucket/file?access-key=123"}, "s3://bucket/file?access-key=xxxxxx"}, - {args{"s3://bucket/file?secret-access-key=123"}, "s3://bucket/file?secret-access-key=xxxxxx"}, - // underline - {args{"s3://bucket/file?access_key=123"}, "s3://bucket/file?access_key=xxxxxx"}, - {args{"s3://bucket/file?secret_access_key=123"}, "s3://bucket/file?secret_access_key=xxxxxx"}, - } - for _, tt := range tests { - t.Run(tt.args.str, func(t *testing.T) { - got := ast.RedactURL(tt.args.str) - if got != tt.want { - t.Errorf("RedactURL() got = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/parser/ast/stats.go b/parser/ast/stats.go deleted file mode 100644 index f7e23a404cf0c..0000000000000 --- a/parser/ast/stats.go +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" -) - -var ( - _ StmtNode = &AnalyzeTableStmt{} - _ StmtNode = &DropStatsStmt{} - _ StmtNode = &LoadStatsStmt{} -) - -// AnalyzeTableStmt is used to create table statistics. -type AnalyzeTableStmt struct { - stmtNode - - TableNames []*TableName - PartitionNames []model.CIStr - IndexNames []model.CIStr - AnalyzeOpts []AnalyzeOpt - - // IndexFlag is true when we only analyze indices for a table. - IndexFlag bool - Incremental bool - // HistogramOperation is set in "ANALYZE TABLE ... UPDATE/DROP HISTOGRAM ..." statement. - HistogramOperation HistogramOperationType - // ColumnNames indicate the columns whose statistics need to be collected. - ColumnNames []model.CIStr - ColumnChoice model.ColumnChoice -} - -// AnalyzeOptType is the type for analyze options. -type AnalyzeOptionType int - -// Analyze option types. -const ( - AnalyzeOptNumBuckets = iota - AnalyzeOptNumTopN - AnalyzeOptCMSketchDepth - AnalyzeOptCMSketchWidth - AnalyzeOptNumSamples - AnalyzeOptSampleRate -) - -// AnalyzeOptionString stores the string form of analyze options. -var AnalyzeOptionString = map[AnalyzeOptionType]string{ - AnalyzeOptNumBuckets: "BUCKETS", - AnalyzeOptNumTopN: "TOPN", - AnalyzeOptCMSketchWidth: "CMSKETCH WIDTH", - AnalyzeOptCMSketchDepth: "CMSKETCH DEPTH", - AnalyzeOptNumSamples: "SAMPLES", - AnalyzeOptSampleRate: "SAMPLERATE", -} - -// HistogramOperationType is the type for histogram operation. -type HistogramOperationType int - -// Histogram operation types. -const ( - // HistogramOperationNop shows no operation in histogram. Default value. - HistogramOperationNop HistogramOperationType = iota - HistogramOperationUpdate - HistogramOperationDrop -) - -// String implements fmt.Stringer for HistogramOperationType. -func (hot HistogramOperationType) String() string { - switch hot { - case HistogramOperationUpdate: - return "UPDATE HISTOGRAM" - case HistogramOperationDrop: - return "DROP HISTOGRAM" - } - return "" -} - -// AnalyzeOpt stores the analyze option type and value. -type AnalyzeOpt struct { - Type AnalyzeOptionType - Value ValueExpr -} - -// Restore implements Node interface. -func (n *AnalyzeTableStmt) Restore(ctx *format.RestoreCtx) error { - if n.Incremental { - ctx.WriteKeyWord("ANALYZE INCREMENTAL TABLE ") - } else { - ctx.WriteKeyWord("ANALYZE TABLE ") - } - for i, table := range n.TableNames { - if i != 0 { - ctx.WritePlain(",") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore AnalyzeTableStmt.TableNames[%d]", i) - } - } - if len(n.PartitionNames) != 0 { - ctx.WriteKeyWord(" PARTITION ") - } - for i, partition := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(partition.O) - } - if n.HistogramOperation != HistogramOperationNop { - ctx.WritePlain(" ") - ctx.WriteKeyWord(n.HistogramOperation.String()) - ctx.WritePlain(" ") - if len(n.ColumnNames) > 0 { - ctx.WriteKeyWord("ON ") - for i, columnName := range n.ColumnNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(columnName.O) - } - } - } - switch n.ColumnChoice { - case model.AllColumns: - ctx.WriteKeyWord(" ALL COLUMNS") - case model.PredicateColumns: - ctx.WriteKeyWord(" PREDICATE COLUMNS") - case model.ColumnList: - ctx.WriteKeyWord(" COLUMNS ") - for i, columnName := range n.ColumnNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(columnName.O) - } - } - if n.IndexFlag { - ctx.WriteKeyWord(" INDEX") - } - for i, index := range n.IndexNames { - if i != 0 { - ctx.WritePlain(",") - } else { - ctx.WritePlain(" ") - } - ctx.WriteName(index.O) - } - if len(n.AnalyzeOpts) != 0 { - ctx.WriteKeyWord(" WITH") - for i, opt := range n.AnalyzeOpts { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WritePlainf(" %v ", opt.Value.GetValue()) - ctx.WritePlain(AnalyzeOptionString[opt.Type]) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *AnalyzeTableStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*AnalyzeTableStmt) - for i, val := range n.TableNames { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.TableNames[i] = node.(*TableName) - } - return v.Leave(n) -} - -// DropStatsStmt is used to drop table statistics. -// if the PartitionNames is not empty, or IsGlobalStats is true, it will contain exactly one table -type DropStatsStmt struct { - stmtNode - - Tables []*TableName - PartitionNames []model.CIStr - IsGlobalStats bool -} - -// Restore implements Node interface. -func (n *DropStatsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("DROP STATS ") - - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore DropStatsStmt.Tables[%d]", index) - } - } - - if n.IsGlobalStats { - ctx.WriteKeyWord(" GLOBAL") - return nil - } - - if len(n.PartitionNames) != 0 { - ctx.WriteKeyWord(" PARTITION ") - } - for i, partition := range n.PartitionNames { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteName(partition.O) - } - return nil -} - -// Accept implements Node Accept interface. -func (n *DropStatsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*DropStatsStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} - -// LoadStatsStmt is the statement node for loading statistic. -type LoadStatsStmt struct { - stmtNode - - Path string -} - -// Restore implements Node interface. -func (n *LoadStatsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("LOAD STATS ") - ctx.WriteString(n.Path) - return nil -} - -// Accept implements Node Accept interface. -func (n *LoadStatsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*LoadStatsStmt) - return v.Leave(n) -} - -// LockStatsStmt is the statement node for lock table statistic -type LockStatsStmt struct { - stmtNode - - Tables []*TableName -} - -// Restore implements Node interface. -func (n *LockStatsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("LOCK STATS ") - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore LockStatsStmt.Tables[%d]", index) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *LockStatsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*LockStatsStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} - -// UnlockStatsStmt is the statement node for unlock table statistic -type UnlockStatsStmt struct { - stmtNode - - Tables []*TableName -} - -// Restore implements Node interface. -func (n *UnlockStatsStmt) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord("UNLOCK STATS ") - for index, table := range n.Tables { - if index != 0 { - ctx.WritePlain(", ") - } - if err := table.Restore(ctx); err != nil { - return errors.Annotatef(err, "An error occurred while restore UnlockStatsStmt.Tables[%d]", index) - } - } - return nil -} - -// Accept implements Node Accept interface. -func (n *UnlockStatsStmt) Accept(v Visitor) (Node, bool) { - newNode, skipChildren := v.Enter(n) - if skipChildren { - return v.Leave(newNode) - } - n = newNode.(*UnlockStatsStmt) - for i, val := range n.Tables { - node, ok := val.Accept(v) - if !ok { - return n, false - } - n.Tables[i] = node.(*TableName) - } - return v.Leave(n) -} diff --git a/parser/ast/util_test.go b/parser/ast/util_test.go deleted file mode 100644 index f492c786b2a12..0000000000000 --- a/parser/ast/util_test.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package ast_test - -import ( - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/parser" - . "github.com/pingcap/tidb/parser/ast" - . "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/test_driver" - "github.com/stretchr/testify/require" -) - -func TestCacheable(t *testing.T) { - // test non-SelectStmt - var stmt Node = &DeleteStmt{} - require.False(t, IsReadOnly(stmt)) - - stmt = &InsertStmt{} - require.False(t, IsReadOnly(stmt)) - - stmt = &UpdateStmt{} - require.False(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{} - require.True(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{} - require.True(t, IsReadOnly(stmt)) - - stmt = &DoStmt{} - require.True(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{ - Stmt: &InsertStmt{}, - } - require.True(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{ - Analyze: true, - Stmt: &InsertStmt{}, - } - require.False(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{ - Stmt: &SelectStmt{}, - } - require.True(t, IsReadOnly(stmt)) - - stmt = &ExplainStmt{ - Analyze: true, - Stmt: &SelectStmt{}, - } - require.True(t, IsReadOnly(stmt)) - - stmt = &ShowStmt{} - require.True(t, IsReadOnly(stmt)) - - stmt = &ShowStmt{} - require.True(t, IsReadOnly(stmt)) -} - -func TestUnionReadOnly(t *testing.T) { - selectReadOnly := &SelectStmt{} - selectForUpdate := &SelectStmt{ - LockInfo: &SelectLockInfo{LockType: SelectLockForUpdate}, - } - selectForUpdateNoWait := &SelectStmt{ - LockInfo: &SelectLockInfo{LockType: SelectLockForUpdateNoWait}, - } - - setOprStmt := &SetOprStmt{ - SelectList: &SetOprSelectList{ - Selects: []Node{selectReadOnly, selectReadOnly}, - }, - } - require.True(t, IsReadOnly(setOprStmt)) - - setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectReadOnly, selectReadOnly} - require.True(t, IsReadOnly(setOprStmt)) - - setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdate} - require.False(t, IsReadOnly(setOprStmt)) - - setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdateNoWait} - require.False(t, IsReadOnly(setOprStmt)) - - setOprStmt.SelectList.Selects = []Node{selectForUpdate, selectForUpdateNoWait} - require.False(t, IsReadOnly(setOprStmt)) - - setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdate, selectForUpdateNoWait} - require.False(t, IsReadOnly(setOprStmt)) -} - -// CleanNodeText set the text of node and all child node empty. -// For test only. -func CleanNodeText(node Node) { - var cleaner nodeTextCleaner - node.Accept(&cleaner) -} - -// nodeTextCleaner clean the text of a node and it's child node. -// For test only. -type nodeTextCleaner struct { -} - -// Enter implements Visitor interface. -func (checker *nodeTextCleaner) Enter(in Node) (out Node, skipChildren bool) { - in.SetText(nil, "") - in.SetOriginTextPosition(0) - if v, ok := in.(ValueExpr); ok && v != nil { - tpFlag := v.GetType().GetFlag() - if tpFlag&mysql.UnderScoreCharsetFlag != 0 { - // ignore underscore charset flag to let `'abc' = _utf8'abc'` pass - tpFlag ^= mysql.UnderScoreCharsetFlag - v.GetType().SetFlag(tpFlag) - } - } - - switch node := in.(type) { - case *Constraint: - if node.Option != nil { - if node.Option.KeyBlockSize == 0x0 && node.Option.Tp == 0 && node.Option.Comment == "" { - node.Option = nil - } - } - case *FuncCallExpr: - node.FnName.O = strings.ToLower(node.FnName.O) - switch node.FnName.L { - case "convert": - node.Args[1].(*test_driver.ValueExpr).Datum.SetBytes(nil) - } - case *AggregateFuncExpr: - node.F = strings.ToLower(node.F) - case *FieldList: - for _, f := range node.Fields { - f.Offset = 0 - } - case *AlterTableSpec: - for _, opt := range node.Options { - opt.StrValue = strings.ToLower(opt.StrValue) - } - case *Join: - node.ExplicitParens = false - case *ColumnDef: - node.Tp.CleanElemIsBinaryLit() - } - return in, false -} - -// Leave implements Visitor interface. -func (checker *nodeTextCleaner) Leave(in Node) (out Node, ok bool) { - return in, true -} - -type NodeRestoreTestCase struct { - sourceSQL string - expectSQL string -} - -func runNodeRestoreTest(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node) { - runNodeRestoreTestWithFlags(t, nodeTestCases, template, extractNodeFunc, DefaultRestoreFlags) -} - -func runNodeRestoreTestWithFlags(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node, flags RestoreFlags) { - p := parser.New() - p.EnableWindowFunc(true) - for _, testCase := range nodeTestCases { - sourceSQL := fmt.Sprintf(template, testCase.sourceSQL) - expectSQL := fmt.Sprintf(template, testCase.expectSQL) - stmt, err := p.ParseOneStmt(sourceSQL, "", "") - comment := fmt.Sprintf("source %#v", testCase) - require.NoError(t, err, comment) - var sb strings.Builder - err = extractNodeFunc(stmt).Restore(NewRestoreCtx(flags, &sb)) - require.NoError(t, err, comment) - restoreSql := fmt.Sprintf(template, sb.String()) - comment = fmt.Sprintf("source %#v; restore %v", testCase, restoreSql) - require.Equal(t, expectSQL, restoreSql, comment) - stmt2, err := p.ParseOneStmt(restoreSql, "", "") - require.NoError(t, err, comment) - CleanNodeText(stmt) - CleanNodeText(stmt2) - require.Equal(t, stmt, stmt2, comment) - } -} - -// runNodeRestoreTestWithFlagsStmtChange likes runNodeRestoreTestWithFlags but not check if the ASTs are same. -// Sometimes the AST are different and it's expected. -func runNodeRestoreTestWithFlagsStmtChange(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node, flags RestoreFlags) { - p := parser.New() - p.EnableWindowFunc(true) - for _, testCase := range nodeTestCases { - sourceSQL := fmt.Sprintf(template, testCase.sourceSQL) - expectSQL := fmt.Sprintf(template, testCase.expectSQL) - stmt, err := p.ParseOneStmt(sourceSQL, "", "") - comment := fmt.Sprintf("source %#v", testCase) - require.NoError(t, err, comment) - var sb strings.Builder - err = extractNodeFunc(stmt).Restore(NewRestoreCtx(flags, &sb)) - require.NoError(t, err, comment) - restoreSql := fmt.Sprintf(template, sb.String()) - comment = fmt.Sprintf("source %#v; restore %v", testCase, restoreSql) - require.Equal(t, expectSQL, restoreSql, comment) - } -} diff --git a/parser/auth/BUILD.bazel b/parser/auth/BUILD.bazel deleted file mode 100644 index f31fe83ce1fca..0000000000000 --- a/parser/auth/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "auth", - srcs = [ - "auth.go", - "caching_sha2.go", - "mysql_native_password.go", - "tidb_sm3.go", - ], - importpath = "github.com/pingcap/tidb/parser/auth", - visibility = ["//visibility:public"], - deps = [ - "//parser/format", - "//parser/mysql", - "//parser/terror", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "auth_test", - timeout = "short", - srcs = [ - "caching_sha2_test.go", - "mysql_native_password_test.go", - "tidb_sm3_test.go", - ], - embed = [":auth"], - flaky = True, - shard_count = 16, - deps = [ - "//parser/mysql", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/charset/BUILD.bazel b/parser/charset/BUILD.bazel deleted file mode 100644 index dfc5896188a3b..0000000000000 --- a/parser/charset/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "charset", - srcs = [ - "charset.go", - "encoding.go", - "encoding_ascii.go", - "encoding_base.go", - "encoding_bin.go", - "encoding_gbk.go", - "encoding_latin1.go", - "encoding_table.go", - "encoding_utf8.go", - ], - importpath = "github.com/pingcap/tidb/parser/charset", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//parser/terror", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_golang_x_text//encoding", - "@org_golang_x_text//encoding/charmap", - "@org_golang_x_text//encoding/japanese", - "@org_golang_x_text//encoding/korean", - "@org_golang_x_text//encoding/simplifiedchinese", - "@org_golang_x_text//encoding/traditionalchinese", - "@org_golang_x_text//encoding/unicode", - "@org_golang_x_text//transform", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "charset_test", - timeout = "short", - srcs = [ - "charset_test.go", - "encoding_test.go", - ], - embed = [":charset"], - flaky = True, - shard_count = 8, - deps = [ - "@com_github_stretchr_testify//require", - "@org_golang_x_text//transform", - ], -) diff --git a/parser/charset/charset.go b/parser/charset/charset.go deleted file mode 100644 index c682ec68977d2..0000000000000 --- a/parser/charset/charset.go +++ /dev/null @@ -1,655 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package charset - -import ( - "cmp" - "slices" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "go.uber.org/zap" -) - -var ( - // ErrUnknownCollation is unknown collation. - ErrUnknownCollation = terror.ClassDDL.NewStd(mysql.ErrUnknownCollation) - // ErrCollationCharsetMismatch is collation charset mismatch. - ErrCollationCharsetMismatch = terror.ClassDDL.NewStd(mysql.ErrCollationCharsetMismatch) -) - -// Charset is a charset. -// Now we only support MySQL. -type Charset struct { - Name string - DefaultCollation string - Collations map[string]*Collation - Desc string - Maxlen int -} - -// Collation is a collation. -// Now we only support MySQL. -type Collation struct { - ID int - CharsetName string - Name string - IsDefault bool -} - -var collationsIDMap = make(map[int]*Collation) -var collationsNameMap = make(map[string]*Collation) -var supportedCollations = make([]*Collation, 0, len(supportedCollationNames)) - -// CharacterSetInfos contains all the supported charsets. -var CharacterSetInfos = map[string]*Charset{ - CharsetUTF8: {CharsetUTF8, CollationUTF8, make(map[string]*Collation), "UTF-8 Unicode", 3}, - CharsetUTF8MB4: {CharsetUTF8MB4, CollationUTF8MB4, make(map[string]*Collation), "UTF-8 Unicode", 4}, - CharsetASCII: {CharsetASCII, CollationASCII, make(map[string]*Collation), "US ASCII", 1}, - CharsetLatin1: {CharsetLatin1, CollationLatin1, make(map[string]*Collation), "Latin1", 1}, - CharsetBin: {CharsetBin, CollationBin, make(map[string]*Collation), "binary", 1}, - CharsetGBK: {CharsetGBK, CollationGBKBin, make(map[string]*Collation), "Chinese Internal Code Specification", 2}, -} - -// All the names supported collations should be in the following table. -var supportedCollationNames = map[string]struct{}{ - CollationUTF8: {}, - CollationUTF8MB4: {}, - CollationASCII: {}, - CollationLatin1: {}, - CollationBin: {}, - CollationGBKBin: {}, -} - -// TiFlashSupportedCharsets is a map which contains TiFlash supports charsets. -var TiFlashSupportedCharsets = map[string]struct{}{ - CharsetUTF8: {}, - CharsetUTF8MB4: {}, - CharsetASCII: {}, - CharsetLatin1: {}, - CharsetBin: {}, -} - -// GetSupportedCharsets gets descriptions for all charsets supported so far. -func GetSupportedCharsets() []*Charset { - charsets := make([]*Charset, 0, len(CharacterSetInfos)) - for _, ch := range CharacterSetInfos { - charsets = append(charsets, ch) - } - - // sort charset by name. - slices.SortFunc(charsets, func(i, j *Charset) int { - return cmp.Compare(i.Name, j.Name) - }) - return charsets -} - -// GetSupportedCollations gets information for all collations supported so far. -func GetSupportedCollations() []*Collation { - return supportedCollations -} - -// ValidCharsetAndCollation checks the charset and the collation validity -// and returns a boolean. -func ValidCharsetAndCollation(cs string, co string) bool { - // We will use utf8 as a default charset. - if cs == "" || cs == CharsetUTF8MB3 { - cs = CharsetUTF8 - } - chs, err := GetCharsetInfo(cs) - if err != nil { - return false - } - - if co == "" { - return true - } - co = utf8Alias(strings.ToLower(co)) - _, ok := chs.Collations[co] - return ok -} - -// GetDefaultCollationLegacy is compatible with the charset support in old version parser. -func GetDefaultCollationLegacy(charset string) (string, error) { - switch strings.ToLower(charset) { - case CharsetUTF8MB3: - return GetDefaultCollation(CharsetUTF8) - case CharsetUTF8, CharsetUTF8MB4, CharsetASCII, CharsetLatin1, CharsetBin: - return GetDefaultCollation(charset) - default: - return "", errors.Errorf("Unknown charset %s", charset) - } -} - -// GetDefaultCollation returns the default collation for charset. -func GetDefaultCollation(charset string) (string, error) { - cs, err := GetCharsetInfo(charset) - if err != nil { - return "", err - } - return cs.DefaultCollation, nil -} - -// GetDefaultCharsetAndCollate returns the default charset and collation. -func GetDefaultCharsetAndCollate() (defaultCharset string, defaultCollationName string) { - return mysql.DefaultCharset, mysql.DefaultCollationName -} - -// GetCharsetInfo returns charset and collation for cs as name. -func GetCharsetInfo(cs string) (*Charset, error) { - if strings.ToLower(cs) == CharsetUTF8MB3 { - cs = CharsetUTF8 - } - - if c, ok := CharacterSetInfos[strings.ToLower(cs)]; ok { - return c, nil - } - - if c, ok := charsets[strings.ToLower(cs)]; ok { - return c, errors.Errorf("Unsupported charset %s", cs) - } - - return nil, errors.Errorf("Unknown charset %s", cs) -} - -// GetCharsetInfoByID returns charset and collation for id as cs_number. -func GetCharsetInfoByID(coID int) (charsetStr string, collateStr string, err error) { - if coID == mysql.DefaultCollationID { - return mysql.DefaultCharset, mysql.DefaultCollationName, nil - } - if collation, ok := collationsIDMap[coID]; ok { - return collation.CharsetName, collation.Name, nil - } - - log.Warn( - "unable to get collation name from collation ID, return default charset and collation instead", - zap.Int("ID", coID), - zap.Stack("stack")) - return mysql.DefaultCharset, mysql.DefaultCollationName, errors.Errorf("Unknown collation id %d", coID) -} - -func utf8Alias(csname string) string { - switch csname { - case "utf8mb3_bin": - csname = "utf8_bin" - case "utf8mb3_unicode_ci": - csname = "utf8_unicode_ci" - case "utf8mb3_general_ci": - csname = "utf8_general_ci" - default: - } - return csname -} - -// GetCollationByName returns the collation by name. -func GetCollationByName(name string) (*Collation, error) { - csname := utf8Alias(strings.ToLower(name)) - collation, ok := collationsNameMap[csname] - if !ok { - return nil, ErrUnknownCollation.GenWithStackByArgs(name) - } - return collation, nil -} - -// GetCollationByID returns collations by given id. -func GetCollationByID(id int) (*Collation, error) { - collation, ok := collationsIDMap[id] - if !ok { - return nil, errors.Errorf("Unknown collation id %d", id) - } - - return collation, nil -} - -const ( - // CollationBin is the default collation for CharsetBin. - CollationBin = "binary" - // CollationUTF8 is the default collation for CharsetUTF8. - CollationUTF8 = "utf8_bin" - // CollationUTF8MB4 is the default collation for CharsetUTF8MB4. - CollationUTF8MB4 = "utf8mb4_bin" - // CollationASCII is the default collation for CharsetACSII. - CollationASCII = "ascii_bin" - // CollationLatin1 is the default collation for CharsetLatin1. - CollationLatin1 = "latin1_bin" - // CollationGBKBin is the default collation for CharsetGBK when new collation is disabled. - CollationGBKBin = "gbk_bin" - // CollationGBKChineseCI is the default collation for CharsetGBK when new collation is enabled. - CollationGBKChineseCI = "gbk_chinese_ci" -) - -const ( - // CharsetASCII is a subset of UTF8. - CharsetASCII = "ascii" - // CharsetBin is used for marking binary charset. - CharsetBin = "binary" - // CharsetLatin1 is a single byte charset. - CharsetLatin1 = "latin1" - // CharsetUTF8 is the default charset for string types. - CharsetUTF8 = "utf8" - // CharsetUTF8MB3 is 3 bytes utf8, a MySQL legacy encoding. "utf8" and "utf8mb3" are aliases. - CharsetUTF8MB3 = "utf8mb3" - // CharsetUTF8MB4 represents 4 bytes utf8, which works the same way as utf8 in Go. - CharsetUTF8MB4 = "utf8mb4" - //revive:disable:exported - CharsetARMSCII8 = "armscii8" - CharsetBig5 = "big5" - CharsetCP1250 = "cp1250" - CharsetCP1251 = "cp1251" - CharsetCP1256 = "cp1256" - CharsetCP1257 = "cp1257" - CharsetCP850 = "cp850" - CharsetCP852 = "cp852" - CharsetCP866 = "cp866" - CharsetCP932 = "cp932" - CharsetDEC8 = "dec8" - CharsetEUCJPMS = "eucjpms" - CharsetEUCKR = "euckr" - CharsetGB18030 = "gb18030" - CharsetGB2312 = "gb2312" - CharsetGBK = "gbk" - CharsetGEOSTD8 = "geostd8" - CharsetGreek = "greek" - CharsetHebrew = "hebrew" - CharsetHP8 = "hp8" - CharsetKEYBCS2 = "keybcs2" - CharsetKOI8R = "koi8r" - CharsetKOI8U = "koi8u" - CharsetLatin2 = "latin2" - CharsetLatin5 = "latin5" - CharsetLatin7 = "latin7" - CharsetMacCE = "macce" - CharsetMacRoman = "macroman" - CharsetSJIS = "sjis" - CharsetSWE7 = "swe7" - CharsetTIS620 = "tis620" - CharsetUCS2 = "ucs2" - CharsetUJIS = "ujis" - CharsetUTF16 = "utf16" - CharsetUTF16LE = "utf16le" - CharsetUTF32 = "utf32" - //revive:enable:exported -) - -var charsets = map[string]*Charset{ - CharsetARMSCII8: {Name: CharsetARMSCII8, Maxlen: 1, DefaultCollation: "armscii8_general_ci", Desc: "ARMSCII-8 Armenian", Collations: make(map[string]*Collation)}, - CharsetASCII: {Name: CharsetASCII, Maxlen: 1, DefaultCollation: "ascii_general_ci", Desc: "US ASCII", Collations: make(map[string]*Collation)}, - CharsetBig5: {Name: CharsetBig5, Maxlen: 2, DefaultCollation: "big5_chinese_ci", Desc: "Big5 Traditional Chinese", Collations: make(map[string]*Collation)}, - CharsetBin: {Name: CharsetBin, Maxlen: 1, DefaultCollation: "binary", Desc: "Binary pseudo charset", Collations: make(map[string]*Collation)}, - CharsetLatin1: {Name: CharsetLatin1, Maxlen: 1, DefaultCollation: "cp1250_general_ci", Desc: "Windows Central European", Collations: make(map[string]*Collation)}, - CharsetCP1250: {Name: CharsetCP1250, Maxlen: 1, DefaultCollation: "cp1251_general_ci", Desc: "Windows Cyrillic", Collations: make(map[string]*Collation)}, - CharsetCP1251: {Name: CharsetCP1251, Maxlen: 1, DefaultCollation: "cp1256_general_ci", Desc: "Windows Arabic", Collations: make(map[string]*Collation)}, - CharsetCP1256: {Name: CharsetCP1256, Maxlen: 1, DefaultCollation: "cp1257_general_ci", Desc: "Windows Baltic", Collations: make(map[string]*Collation)}, - CharsetCP1257: {Name: CharsetCP1257, Maxlen: 1, DefaultCollation: "cp850_general_ci", Desc: "DOS West European", Collations: make(map[string]*Collation)}, - CharsetCP850: {Name: CharsetCP850, Maxlen: 1, DefaultCollation: "cp852_general_ci", Desc: "DOS Central European", Collations: make(map[string]*Collation)}, - CharsetCP852: {Name: CharsetCP852, Maxlen: 1, DefaultCollation: "cp866_general_ci", Desc: "DOS Russian", Collations: make(map[string]*Collation)}, - CharsetCP866: {Name: CharsetCP866, Maxlen: 1, DefaultCollation: "cp932_japanese_ci", Desc: "SJIS for Windows Japanese", Collations: make(map[string]*Collation)}, - CharsetCP932: {Name: CharsetCP932, Maxlen: 2, DefaultCollation: "dec8_swedish_ci", Desc: "DEC West European", Collations: make(map[string]*Collation)}, - CharsetDEC8: {Name: CharsetDEC8, Maxlen: 1, DefaultCollation: "eucjpms_japanese_ci", Desc: "UJIS for Windows Japanese", Collations: make(map[string]*Collation)}, - CharsetEUCJPMS: {Name: CharsetEUCJPMS, Maxlen: 3, DefaultCollation: "euckr_korean_ci", Desc: "EUC-KR Korean", Collations: make(map[string]*Collation)}, - CharsetEUCKR: {Name: CharsetEUCKR, Maxlen: 2, DefaultCollation: "gb18030_chinese_ci", Desc: "China National Standard GB18030", Collations: make(map[string]*Collation)}, - CharsetGB18030: {Name: CharsetGB18030, Maxlen: 4, DefaultCollation: "gb2312_chinese_ci", Desc: "GB2312 Simplified Chinese", Collations: make(map[string]*Collation)}, - CharsetGB2312: {Name: CharsetGB2312, Maxlen: 2, DefaultCollation: "gbk_chinese_ci", Desc: "GBK Simplified Chinese", Collations: make(map[string]*Collation)}, - CharsetGBK: {Name: CharsetGBK, Maxlen: 2, DefaultCollation: "geostd8_general_ci", Desc: "GEOSTD8 Georgian", Collations: make(map[string]*Collation)}, - CharsetGEOSTD8: {Name: CharsetGEOSTD8, Maxlen: 1, DefaultCollation: "greek_general_ci", Desc: "ISO 8859-7 Greek", Collations: make(map[string]*Collation)}, - CharsetGreek: {Name: CharsetGreek, Maxlen: 1, DefaultCollation: "hebrew_general_ci", Desc: "ISO 8859-8 Hebrew", Collations: make(map[string]*Collation)}, - CharsetHebrew: {Name: CharsetHebrew, Maxlen: 1, DefaultCollation: "hp8_english_ci", Desc: "HP West European", Collations: make(map[string]*Collation)}, - CharsetHP8: {Name: CharsetHP8, Maxlen: 1, DefaultCollation: "keybcs2_general_ci", Desc: "DOS Kamenicky Czech-Slovak", Collations: make(map[string]*Collation)}, - CharsetKEYBCS2: {Name: CharsetKEYBCS2, Maxlen: 1, DefaultCollation: "koi8r_general_ci", Desc: "KOI8-R Relcom Russian", Collations: make(map[string]*Collation)}, - CharsetKOI8R: {Name: CharsetKOI8R, Maxlen: 1, DefaultCollation: "koi8u_general_ci", Desc: "KOI8-U Ukrainian", Collations: make(map[string]*Collation)}, - CharsetKOI8U: {Name: CharsetKOI8U, Maxlen: 1, DefaultCollation: "latin1_swedish_ci", Desc: "cp1252 West European", Collations: make(map[string]*Collation)}, - CharsetLatin2: {Name: CharsetLatin2, Maxlen: 1, DefaultCollation: "latin2_general_ci", Desc: "ISO 8859-2 Central European", Collations: make(map[string]*Collation)}, - CharsetLatin5: {Name: CharsetLatin5, Maxlen: 1, DefaultCollation: "latin5_turkish_ci", Desc: "ISO 8859-9 Turkish", Collations: make(map[string]*Collation)}, - CharsetLatin7: {Name: CharsetLatin7, Maxlen: 1, DefaultCollation: "latin7_general_ci", Desc: "ISO 8859-13 Baltic", Collations: make(map[string]*Collation)}, - CharsetMacCE: {Name: CharsetMacCE, Maxlen: 1, DefaultCollation: "macce_general_ci", Desc: "Mac Central European", Collations: make(map[string]*Collation)}, - CharsetMacRoman: {Name: CharsetMacRoman, Maxlen: 1, DefaultCollation: "macroman_general_ci", Desc: "Mac West European", Collations: make(map[string]*Collation)}, - CharsetSJIS: {Name: CharsetSJIS, Maxlen: 2, DefaultCollation: "sjis_japanese_ci", Desc: "Shift-JIS Japanese", Collations: make(map[string]*Collation)}, - CharsetSWE7: {Name: CharsetSWE7, Maxlen: 1, DefaultCollation: "swe7_swedish_ci", Desc: "7bit Swedish", Collations: make(map[string]*Collation)}, - CharsetTIS620: {Name: CharsetTIS620, Maxlen: 1, DefaultCollation: "tis620_thai_ci", Desc: "TIS620 Thai", Collations: make(map[string]*Collation)}, - CharsetUCS2: {Name: CharsetUCS2, Maxlen: 2, DefaultCollation: "ucs2_general_ci", Desc: "UCS-2 Unicode", Collations: make(map[string]*Collation)}, - CharsetUJIS: {Name: CharsetUJIS, Maxlen: 3, DefaultCollation: "ujis_japanese_ci", Desc: "EUC-JP Japanese", Collations: make(map[string]*Collation)}, - CharsetUTF16: {Name: CharsetUTF16, Maxlen: 4, DefaultCollation: "utf16_general_ci", Desc: "UTF-16 Unicode", Collations: make(map[string]*Collation)}, - CharsetUTF16LE: {Name: CharsetUTF16LE, Maxlen: 4, DefaultCollation: "utf16le_general_ci", Desc: "UTF-16LE Unicode", Collations: make(map[string]*Collation)}, - CharsetUTF32: {Name: CharsetUTF32, Maxlen: 4, DefaultCollation: "utf32_general_ci", Desc: "UTF-32 Unicode", Collations: make(map[string]*Collation)}, - CharsetUTF8: {Name: CharsetUTF8, Maxlen: 3, DefaultCollation: "utf8_general_ci", Desc: "UTF-8 Unicode", Collations: make(map[string]*Collation)}, - CharsetUTF8MB4: {Name: CharsetUTF8MB4, Maxlen: 4, DefaultCollation: "utf8mb4_0900_ai_ci", Desc: "UTF-8 Unicode", Collations: make(map[string]*Collation)}, -} - -var collations = []*Collation{ - {1, "big5", "big5_chinese_ci", true}, - {2, "latin2", "latin2_czech_cs", false}, - {3, "dec8", "dec8_swedish_ci", true}, - {4, "cp850", "cp850_general_ci", true}, - {5, "latin1", "latin1_german1_ci", false}, - {6, "hp8", "hp8_english_ci", true}, - {7, "koi8r", "koi8r_general_ci", true}, - {8, "latin1", "latin1_swedish_ci", false}, - {9, "latin2", "latin2_general_ci", true}, - {10, "swe7", "swe7_swedish_ci", true}, - {11, "ascii", "ascii_general_ci", false}, - {12, "ujis", "ujis_japanese_ci", true}, - {13, "sjis", "sjis_japanese_ci", true}, - {14, "cp1251", "cp1251_bulgarian_ci", false}, - {15, "latin1", "latin1_danish_ci", false}, - {16, "hebrew", "hebrew_general_ci", true}, - {18, "tis620", "tis620_thai_ci", true}, - {19, "euckr", "euckr_korean_ci", true}, - {20, "latin7", "latin7_estonian_cs", false}, - {21, "latin2", "latin2_hungarian_ci", false}, - {22, "koi8u", "koi8u_general_ci", true}, - {23, "cp1251", "cp1251_ukrainian_ci", false}, - {24, "gb2312", "gb2312_chinese_ci", true}, - {25, "greek", "greek_general_ci", true}, - {26, "cp1250", "cp1250_general_ci", true}, - {27, "latin2", "latin2_croatian_ci", false}, - {28, "gbk", "gbk_chinese_ci", false}, - {29, "cp1257", "cp1257_lithuanian_ci", false}, - {30, "latin5", "latin5_turkish_ci", true}, - {31, "latin1", "latin1_german2_ci", false}, - {32, "armscii8", "armscii8_general_ci", true}, - {33, "utf8", "utf8_general_ci", false}, - {34, "cp1250", "cp1250_czech_cs", false}, - {35, "ucs2", "ucs2_general_ci", true}, - {36, "cp866", "cp866_general_ci", true}, - {37, "keybcs2", "keybcs2_general_ci", true}, - {38, "macce", "macce_general_ci", true}, - {39, "macroman", "macroman_general_ci", true}, - {40, "cp852", "cp852_general_ci", true}, - {41, "latin7", "latin7_general_ci", true}, - {42, "latin7", "latin7_general_cs", false}, - {43, "macce", "macce_bin", false}, - {44, "cp1250", "cp1250_croatian_ci", false}, - {45, "utf8mb4", "utf8mb4_general_ci", false}, - {46, "utf8mb4", "utf8mb4_bin", true}, - {47, "latin1", "latin1_bin", true}, - {48, "latin1", "latin1_general_ci", false}, - {49, "latin1", "latin1_general_cs", false}, - {50, "cp1251", "cp1251_bin", false}, - {51, "cp1251", "cp1251_general_ci", true}, - {52, "cp1251", "cp1251_general_cs", false}, - {53, "macroman", "macroman_bin", false}, - {54, "utf16", "utf16_general_ci", true}, - {55, "utf16", "utf16_bin", false}, - {56, "utf16le", "utf16le_general_ci", true}, - {57, "cp1256", "cp1256_general_ci", true}, - {58, "cp1257", "cp1257_bin", false}, - {59, "cp1257", "cp1257_general_ci", true}, - {60, "utf32", "utf32_general_ci", true}, - {61, "utf32", "utf32_bin", false}, - {62, "utf16le", "utf16le_bin", false}, - {63, "binary", "binary", true}, - {64, "armscii8", "armscii8_bin", false}, - {65, "ascii", "ascii_bin", true}, - {66, "cp1250", "cp1250_bin", false}, - {67, "cp1256", "cp1256_bin", false}, - {68, "cp866", "cp866_bin", false}, - {69, "dec8", "dec8_bin", false}, - {70, "greek", "greek_bin", false}, - {71, "hebrew", "hebrew_bin", false}, - {72, "hp8", "hp8_bin", false}, - {73, "keybcs2", "keybcs2_bin", false}, - {74, "koi8r", "koi8r_bin", false}, - {75, "koi8u", "koi8u_bin", false}, - {76, "utf8", "utf8_tolower_ci", false}, - {77, "latin2", "latin2_bin", false}, - {78, "latin5", "latin5_bin", false}, - {79, "latin7", "latin7_bin", false}, - {80, "cp850", "cp850_bin", false}, - {81, "cp852", "cp852_bin", false}, - {82, "swe7", "swe7_bin", false}, - {83, "utf8", "utf8_bin", true}, - {84, "big5", "big5_bin", false}, - {85, "euckr", "euckr_bin", false}, - {86, "gb2312", "gb2312_bin", false}, - {87, "gbk", "gbk_bin", true}, - {88, "sjis", "sjis_bin", false}, - {89, "tis620", "tis620_bin", false}, - {90, "ucs2", "ucs2_bin", false}, - {91, "ujis", "ujis_bin", false}, - {92, "geostd8", "geostd8_general_ci", true}, - {93, "geostd8", "geostd8_bin", false}, - {94, "latin1", "latin1_spanish_ci", false}, - {95, "cp932", "cp932_japanese_ci", true}, - {96, "cp932", "cp932_bin", false}, - {97, "eucjpms", "eucjpms_japanese_ci", true}, - {98, "eucjpms", "eucjpms_bin", false}, - {99, "cp1250", "cp1250_polish_ci", false}, - {101, "utf16", "utf16_unicode_ci", false}, - {102, "utf16", "utf16_icelandic_ci", false}, - {103, "utf16", "utf16_latvian_ci", false}, - {104, "utf16", "utf16_romanian_ci", false}, - {105, "utf16", "utf16_slovenian_ci", false}, - {106, "utf16", "utf16_polish_ci", false}, - {107, "utf16", "utf16_estonian_ci", false}, - {108, "utf16", "utf16_spanish_ci", false}, - {109, "utf16", "utf16_swedish_ci", false}, - {110, "utf16", "utf16_turkish_ci", false}, - {111, "utf16", "utf16_czech_ci", false}, - {112, "utf16", "utf16_danish_ci", false}, - {113, "utf16", "utf16_lithuanian_ci", false}, - {114, "utf16", "utf16_slovak_ci", false}, - {115, "utf16", "utf16_spanish2_ci", false}, - {116, "utf16", "utf16_roman_ci", false}, - {117, "utf16", "utf16_persian_ci", false}, - {118, "utf16", "utf16_esperanto_ci", false}, - {119, "utf16", "utf16_hungarian_ci", false}, - {120, "utf16", "utf16_sinhala_ci", false}, - {121, "utf16", "utf16_german2_ci", false}, - {122, "utf16", "utf16_croatian_ci", false}, - {123, "utf16", "utf16_unicode_520_ci", false}, - {124, "utf16", "utf16_vietnamese_ci", false}, - {128, "ucs2", "ucs2_unicode_ci", false}, - {129, "ucs2", "ucs2_icelandic_ci", false}, - {130, "ucs2", "ucs2_latvian_ci", false}, - {131, "ucs2", "ucs2_romanian_ci", false}, - {132, "ucs2", "ucs2_slovenian_ci", false}, - {133, "ucs2", "ucs2_polish_ci", false}, - {134, "ucs2", "ucs2_estonian_ci", false}, - {135, "ucs2", "ucs2_spanish_ci", false}, - {136, "ucs2", "ucs2_swedish_ci", false}, - {137, "ucs2", "ucs2_turkish_ci", false}, - {138, "ucs2", "ucs2_czech_ci", false}, - {139, "ucs2", "ucs2_danish_ci", false}, - {140, "ucs2", "ucs2_lithuanian_ci", false}, - {141, "ucs2", "ucs2_slovak_ci", false}, - {142, "ucs2", "ucs2_spanish2_ci", false}, - {143, "ucs2", "ucs2_roman_ci", false}, - {144, "ucs2", "ucs2_persian_ci", false}, - {145, "ucs2", "ucs2_esperanto_ci", false}, - {146, "ucs2", "ucs2_hungarian_ci", false}, - {147, "ucs2", "ucs2_sinhala_ci", false}, - {148, "ucs2", "ucs2_german2_ci", false}, - {149, "ucs2", "ucs2_croatian_ci", false}, - {150, "ucs2", "ucs2_unicode_520_ci", false}, - {151, "ucs2", "ucs2_vietnamese_ci", false}, - {159, "ucs2", "ucs2_general_mysql500_ci", false}, - {160, "utf32", "utf32_unicode_ci", false}, - {161, "utf32", "utf32_icelandic_ci", false}, - {162, "utf32", "utf32_latvian_ci", false}, - {163, "utf32", "utf32_romanian_ci", false}, - {164, "utf32", "utf32_slovenian_ci", false}, - {165, "utf32", "utf32_polish_ci", false}, - {166, "utf32", "utf32_estonian_ci", false}, - {167, "utf32", "utf32_spanish_ci", false}, - {168, "utf32", "utf32_swedish_ci", false}, - {169, "utf32", "utf32_turkish_ci", false}, - {170, "utf32", "utf32_czech_ci", false}, - {171, "utf32", "utf32_danish_ci", false}, - {172, "utf32", "utf32_lithuanian_ci", false}, - {173, "utf32", "utf32_slovak_ci", false}, - {174, "utf32", "utf32_spanish2_ci", false}, - {175, "utf32", "utf32_roman_ci", false}, - {176, "utf32", "utf32_persian_ci", false}, - {177, "utf32", "utf32_esperanto_ci", false}, - {178, "utf32", "utf32_hungarian_ci", false}, - {179, "utf32", "utf32_sinhala_ci", false}, - {180, "utf32", "utf32_german2_ci", false}, - {181, "utf32", "utf32_croatian_ci", false}, - {182, "utf32", "utf32_unicode_520_ci", false}, - {183, "utf32", "utf32_vietnamese_ci", false}, - {192, "utf8", "utf8_unicode_ci", false}, - {193, "utf8", "utf8_icelandic_ci", false}, - {194, "utf8", "utf8_latvian_ci", false}, - {195, "utf8", "utf8_romanian_ci", false}, - {196, "utf8", "utf8_slovenian_ci", false}, - {197, "utf8", "utf8_polish_ci", false}, - {198, "utf8", "utf8_estonian_ci", false}, - {199, "utf8", "utf8_spanish_ci", false}, - {200, "utf8", "utf8_swedish_ci", false}, - {201, "utf8", "utf8_turkish_ci", false}, - {202, "utf8", "utf8_czech_ci", false}, - {203, "utf8", "utf8_danish_ci", false}, - {204, "utf8", "utf8_lithuanian_ci", false}, - {205, "utf8", "utf8_slovak_ci", false}, - {206, "utf8", "utf8_spanish2_ci", false}, - {207, "utf8", "utf8_roman_ci", false}, - {208, "utf8", "utf8_persian_ci", false}, - {209, "utf8", "utf8_esperanto_ci", false}, - {210, "utf8", "utf8_hungarian_ci", false}, - {211, "utf8", "utf8_sinhala_ci", false}, - {212, "utf8", "utf8_german2_ci", false}, - {213, "utf8", "utf8_croatian_ci", false}, - {214, "utf8", "utf8_unicode_520_ci", false}, - {215, "utf8", "utf8_vietnamese_ci", false}, - {223, "utf8", "utf8_general_mysql500_ci", false}, - {224, "utf8mb4", "utf8mb4_unicode_ci", false}, - {225, "utf8mb4", "utf8mb4_icelandic_ci", false}, - {226, "utf8mb4", "utf8mb4_latvian_ci", false}, - {227, "utf8mb4", "utf8mb4_romanian_ci", false}, - {228, "utf8mb4", "utf8mb4_slovenian_ci", false}, - {229, "utf8mb4", "utf8mb4_polish_ci", false}, - {230, "utf8mb4", "utf8mb4_estonian_ci", false}, - {231, "utf8mb4", "utf8mb4_spanish_ci", false}, - {232, "utf8mb4", "utf8mb4_swedish_ci", false}, - {233, "utf8mb4", "utf8mb4_turkish_ci", false}, - {234, "utf8mb4", "utf8mb4_czech_ci", false}, - {235, "utf8mb4", "utf8mb4_danish_ci", false}, - {236, "utf8mb4", "utf8mb4_lithuanian_ci", false}, - {237, "utf8mb4", "utf8mb4_slovak_ci", false}, - {238, "utf8mb4", "utf8mb4_spanish2_ci", false}, - {239, "utf8mb4", "utf8mb4_roman_ci", false}, - {240, "utf8mb4", "utf8mb4_persian_ci", false}, - {241, "utf8mb4", "utf8mb4_esperanto_ci", false}, - {242, "utf8mb4", "utf8mb4_hungarian_ci", false}, - {243, "utf8mb4", "utf8mb4_sinhala_ci", false}, - {244, "utf8mb4", "utf8mb4_german2_ci", false}, - {245, "utf8mb4", "utf8mb4_croatian_ci", false}, - {246, "utf8mb4", "utf8mb4_unicode_520_ci", false}, - {247, "utf8mb4", "utf8mb4_vietnamese_ci", false}, - {248, "gb18030", "gb18030_chinese_ci", false}, - {249, "gb18030", "gb18030_bin", true}, - {250, "gb18030", "gb18030_unicode_520_ci", false}, - {255, "utf8mb4", "utf8mb4_0900_ai_ci", false}, - {256, "utf8mb4", "utf8mb4_de_pb_0900_ai_ci", false}, - {257, "utf8mb4", "utf8mb4_is_0900_ai_ci", false}, - {258, "utf8mb4", "utf8mb4_lv_0900_ai_ci", false}, - {259, "utf8mb4", "utf8mb4_ro_0900_ai_ci", false}, - {260, "utf8mb4", "utf8mb4_sl_0900_ai_ci", false}, - {261, "utf8mb4", "utf8mb4_pl_0900_ai_ci", false}, - {262, "utf8mb4", "utf8mb4_et_0900_ai_ci", false}, - {263, "utf8mb4", "utf8mb4_es_0900_ai_ci", false}, - {264, "utf8mb4", "utf8mb4_sv_0900_ai_ci", false}, - {265, "utf8mb4", "utf8mb4_tr_0900_ai_ci", false}, - {266, "utf8mb4", "utf8mb4_cs_0900_ai_ci", false}, - {267, "utf8mb4", "utf8mb4_da_0900_ai_ci", false}, - {268, "utf8mb4", "utf8mb4_lt_0900_ai_ci", false}, - {269, "utf8mb4", "utf8mb4_sk_0900_ai_ci", false}, - {270, "utf8mb4", "utf8mb4_es_trad_0900_ai_ci", false}, - {271, "utf8mb4", "utf8mb4_la_0900_ai_ci", false}, - {273, "utf8mb4", "utf8mb4_eo_0900_ai_ci", false}, - {274, "utf8mb4", "utf8mb4_hu_0900_ai_ci", false}, - {275, "utf8mb4", "utf8mb4_hr_0900_ai_ci", false}, - {277, "utf8mb4", "utf8mb4_vi_0900_ai_ci", false}, - {278, "utf8mb4", "utf8mb4_0900_as_cs", false}, - {279, "utf8mb4", "utf8mb4_de_pb_0900_as_cs", false}, - {280, "utf8mb4", "utf8mb4_is_0900_as_cs", false}, - {281, "utf8mb4", "utf8mb4_lv_0900_as_cs", false}, - {282, "utf8mb4", "utf8mb4_ro_0900_as_cs", false}, - {283, "utf8mb4", "utf8mb4_sl_0900_as_cs", false}, - {284, "utf8mb4", "utf8mb4_pl_0900_as_cs", false}, - {285, "utf8mb4", "utf8mb4_et_0900_as_cs", false}, - {286, "utf8mb4", "utf8mb4_es_0900_as_cs", false}, - {287, "utf8mb4", "utf8mb4_sv_0900_as_cs", false}, - {288, "utf8mb4", "utf8mb4_tr_0900_as_cs", false}, - {289, "utf8mb4", "utf8mb4_cs_0900_as_cs", false}, - {290, "utf8mb4", "utf8mb4_da_0900_as_cs", false}, - {291, "utf8mb4", "utf8mb4_lt_0900_as_cs", false}, - {292, "utf8mb4", "utf8mb4_sk_0900_as_cs", false}, - {293, "utf8mb4", "utf8mb4_es_trad_0900_as_cs", false}, - {294, "utf8mb4", "utf8mb4_la_0900_as_cs", false}, - {296, "utf8mb4", "utf8mb4_eo_0900_as_cs", false}, - {297, "utf8mb4", "utf8mb4_hu_0900_as_cs", false}, - {298, "utf8mb4", "utf8mb4_hr_0900_as_cs", false}, - {300, "utf8mb4", "utf8mb4_vi_0900_as_cs", false}, - {303, "utf8mb4", "utf8mb4_ja_0900_as_cs", false}, - {304, "utf8mb4", "utf8mb4_ja_0900_as_cs_ks", false}, - {305, "utf8mb4", "utf8mb4_0900_as_ci", false}, - {306, "utf8mb4", "utf8mb4_ru_0900_ai_ci", false}, - {307, "utf8mb4", "utf8mb4_ru_0900_as_cs", false}, - {308, "utf8mb4", "utf8mb4_zh_0900_as_cs", false}, - {309, "utf8mb4", "utf8mb4_0900_bin", false}, - {2048, "utf8mb4", "utf8mb4_zh_pinyin_tidb_as_cs", false}, -} - -// AddCharset adds a new charset. -// Use only when adding a custom charset to the parser. -func AddCharset(c *Charset) { - CharacterSetInfos[c.Name] = c -} - -// RemoveCharset remove a charset. -// Use only when remove a custom charset to the parser. -func RemoveCharset(c string) { - delete(CharacterSetInfos, c) - for i := range supportedCollations { - if supportedCollations[i].Name == c { - supportedCollations = append(supportedCollations[:i], supportedCollations[i+1:]...) - } - } -} - -// AddCollation adds a new collation. -// Use only when adding a custom collation to the parser. -func AddCollation(c *Collation) { - collationsIDMap[c.ID] = c - collationsNameMap[c.Name] = c - - if _, ok := supportedCollationNames[c.Name]; ok { - AddSupportedCollation(c) - } - - if charset, ok := CharacterSetInfos[c.CharsetName]; ok { - charset.Collations[c.Name] = c - } - - if charset, ok := charsets[c.CharsetName]; ok { - charset.Collations[c.Name] = c - } -} - -// AddSupportedCollation adds a new collation into supportedCollations. -// Use only when adding a custom collation to the parser. -func AddSupportedCollation(c *Collation) { - supportedCollations = append(supportedCollations, c) -} - -// init method always puts to the end of file. -func init() { - for _, c := range collations { - AddCollation(c) - } -} diff --git a/parser/duration/BUILD.bazel b/parser/duration/BUILD.bazel deleted file mode 100644 index 8015c5ce5df7d..0000000000000 --- a/parser/duration/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "duration", - srcs = ["duration.go"], - importpath = "github.com/pingcap/tidb/parser/duration", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "duration_test", - timeout = "short", - srcs = ["duration_test.go"], - embed = [":duration"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/parser/format/BUILD.bazel b/parser/format/BUILD.bazel deleted file mode 100644 index b7c41426d3a93..0000000000000 --- a/parser/format/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "format", - srcs = ["format.go"], - importpath = "github.com/pingcap/tidb/parser/format", - visibility = ["//visibility:public"], -) - -go_test( - name = "format_test", - timeout = "short", - srcs = ["format_test.go"], - embed = [":format"], - flaky = True, - shard_count = 3, - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/goyacc/BUILD.bazel b/parser/goyacc/BUILD.bazel deleted file mode 100644 index 51d0991ed1b0c..0000000000000 --- a/parser/goyacc/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -go_library( - name = "goyacc_lib", - srcs = [ - "format_yacc.go", - "main.go", - ], - importpath = "github.com/pingcap/tidb/parser/goyacc", - visibility = ["//visibility:private"], - deps = [ - "//parser/format", - "@com_github_cznic_mathutil//:mathutil", - "@com_github_cznic_sortutil//:sortutil", - "@com_github_cznic_strutil//:strutil", - "@com_github_pingcap_errors//:errors", - "@org_modernc_parser//yacc", - "@org_modernc_y//:y", - ], -) - -go_binary( - name = "goyacc", - embed = [":goyacc_lib"], - visibility = ["//visibility:public"], -) diff --git a/parser/model/BUILD.bazel b/parser/model/BUILD.bazel deleted file mode 100644 index a96a1dae04f26..0000000000000 --- a/parser/model/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "model", - srcs = [ - "ddl.go", - "flags.go", - "model.go", - "reorg.go", - ], - importpath = "github.com/pingcap/tidb/parser/model", - visibility = ["//visibility:public"], - deps = [ - "//parser/auth", - "//parser/charset", - "//parser/duration", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "model_test", - timeout = "short", - srcs = [ - "ddl_test.go", - "model_test.go", - ], - embed = [":model"], - flaky = True, - shard_count = 20, - deps = [ - "//parser/charset", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/model/ddl.go b/parser/model/ddl.go deleted file mode 100644 index 456dcb8fad5ca..0000000000000 --- a/parser/model/ddl.go +++ /dev/null @@ -1,1000 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - "fmt" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" -) - -// ActionType is the type for DDL action. -type ActionType byte - -// List DDL actions. -const ( - ActionNone ActionType = 0 - ActionCreateSchema ActionType = 1 - ActionDropSchema ActionType = 2 - ActionCreateTable ActionType = 3 - ActionDropTable ActionType = 4 - ActionAddColumn ActionType = 5 - ActionDropColumn ActionType = 6 - ActionAddIndex ActionType = 7 - ActionDropIndex ActionType = 8 - ActionAddForeignKey ActionType = 9 - ActionDropForeignKey ActionType = 10 - ActionTruncateTable ActionType = 11 - ActionModifyColumn ActionType = 12 - ActionRebaseAutoID ActionType = 13 - ActionRenameTable ActionType = 14 - ActionSetDefaultValue ActionType = 15 - ActionShardRowID ActionType = 16 - ActionModifyTableComment ActionType = 17 - ActionRenameIndex ActionType = 18 - ActionAddTablePartition ActionType = 19 - ActionDropTablePartition ActionType = 20 - ActionCreateView ActionType = 21 - ActionModifyTableCharsetAndCollate ActionType = 22 - ActionTruncateTablePartition ActionType = 23 - ActionDropView ActionType = 24 - ActionRecoverTable ActionType = 25 - ActionModifySchemaCharsetAndCollate ActionType = 26 - ActionLockTable ActionType = 27 - ActionUnlockTable ActionType = 28 - ActionRepairTable ActionType = 29 - ActionSetTiFlashReplica ActionType = 30 - ActionUpdateTiFlashReplicaStatus ActionType = 31 - ActionAddPrimaryKey ActionType = 32 - ActionDropPrimaryKey ActionType = 33 - ActionCreateSequence ActionType = 34 - ActionAlterSequence ActionType = 35 - ActionDropSequence ActionType = 36 - ActionAddColumns ActionType = 37 // Deprecated, we use ActionMultiSchemaChange instead. - ActionDropColumns ActionType = 38 // Deprecated, we use ActionMultiSchemaChange instead. - ActionModifyTableAutoIdCache ActionType = 39 //nolint:revive - ActionRebaseAutoRandomBase ActionType = 40 - ActionAlterIndexVisibility ActionType = 41 - ActionExchangeTablePartition ActionType = 42 - ActionAddCheckConstraint ActionType = 43 - ActionDropCheckConstraint ActionType = 44 - ActionAlterCheckConstraint ActionType = 45 - - // `ActionAlterTableAlterPartition` is removed and will never be used. - // Just left a tombstone here for compatibility. - __DEPRECATED_ActionAlterTableAlterPartition ActionType = 46 //nolint:revive - - ActionRenameTables ActionType = 47 - ActionDropIndexes ActionType = 48 // Deprecated, we use ActionMultiSchemaChange instead. - ActionAlterTableAttributes ActionType = 49 - ActionAlterTablePartitionAttributes ActionType = 50 - ActionCreatePlacementPolicy ActionType = 51 - ActionAlterPlacementPolicy ActionType = 52 - ActionDropPlacementPolicy ActionType = 53 - ActionAlterTablePartitionPlacement ActionType = 54 - ActionModifySchemaDefaultPlacement ActionType = 55 - ActionAlterTablePlacement ActionType = 56 - ActionAlterCacheTable ActionType = 57 - ActionAlterTableStatsOptions ActionType = 58 - ActionAlterNoCacheTable ActionType = 59 - ActionCreateTables ActionType = 60 - ActionMultiSchemaChange ActionType = 61 - ActionFlashbackCluster ActionType = 62 - ActionRecoverSchema ActionType = 63 - ActionReorganizePartition ActionType = 64 - ActionAlterTTLInfo ActionType = 65 - ActionAlterTTLRemove ActionType = 67 - ActionCreateResourceGroup ActionType = 68 - ActionAlterResourceGroup ActionType = 69 - ActionDropResourceGroup ActionType = 70 - ActionAlterTablePartitioning ActionType = 71 - ActionRemovePartitioning ActionType = 72 -) - -var actionMap = map[ActionType]string{ - ActionCreateSchema: "create schema", - ActionDropSchema: "drop schema", - ActionCreateTable: "create table", - ActionCreateTables: "create tables", - ActionDropTable: "drop table", - ActionAddColumn: "add column", - ActionDropColumn: "drop column", - ActionAddIndex: "add index", - ActionDropIndex: "drop index", - ActionAddForeignKey: "add foreign key", - ActionDropForeignKey: "drop foreign key", - ActionTruncateTable: "truncate table", - ActionModifyColumn: "modify column", - ActionRebaseAutoID: "rebase auto_increment ID", - ActionRenameTable: "rename table", - ActionRenameTables: "rename tables", - ActionSetDefaultValue: "set default value", - ActionShardRowID: "shard row ID", - ActionModifyTableComment: "modify table comment", - ActionRenameIndex: "rename index", - ActionAddTablePartition: "add partition", - ActionDropTablePartition: "drop partition", - ActionCreateView: "create view", - ActionModifyTableCharsetAndCollate: "modify table charset and collate", - ActionTruncateTablePartition: "truncate partition", - ActionDropView: "drop view", - ActionRecoverTable: "recover table", - ActionModifySchemaCharsetAndCollate: "modify schema charset and collate", - ActionLockTable: "lock table", - ActionUnlockTable: "unlock table", - ActionRepairTable: "repair table", - ActionSetTiFlashReplica: "set tiflash replica", - ActionUpdateTiFlashReplicaStatus: "update tiflash replica status", - ActionAddPrimaryKey: "add primary key", - ActionDropPrimaryKey: "drop primary key", - ActionCreateSequence: "create sequence", - ActionAlterSequence: "alter sequence", - ActionDropSequence: "drop sequence", - ActionModifyTableAutoIdCache: "modify auto id cache", - ActionRebaseAutoRandomBase: "rebase auto_random ID", - ActionAlterIndexVisibility: "alter index visibility", - ActionExchangeTablePartition: "exchange partition", - ActionAddCheckConstraint: "add check constraint", - ActionDropCheckConstraint: "drop check constraint", - ActionAlterCheckConstraint: "alter check constraint", - ActionAlterTableAttributes: "alter table attributes", - ActionAlterTablePartitionPlacement: "alter table partition placement", - ActionAlterTablePartitionAttributes: "alter table partition attributes", - ActionCreatePlacementPolicy: "create placement policy", - ActionAlterPlacementPolicy: "alter placement policy", - ActionDropPlacementPolicy: "drop placement policy", - ActionModifySchemaDefaultPlacement: "modify schema default placement", - ActionAlterTablePlacement: "alter table placement", - ActionAlterCacheTable: "alter table cache", - ActionAlterNoCacheTable: "alter table nocache", - ActionAlterTableStatsOptions: "alter table statistics options", - ActionMultiSchemaChange: "alter table multi-schema change", - ActionFlashbackCluster: "flashback cluster", - ActionRecoverSchema: "flashback schema", - ActionReorganizePartition: "alter table reorganize partition", - ActionAlterTTLInfo: "alter table ttl", - ActionAlterTTLRemove: "alter table no_ttl", - ActionCreateResourceGroup: "create resource group", - ActionAlterResourceGroup: "alter resource group", - ActionDropResourceGroup: "drop resource group", - ActionAlterTablePartitioning: "alter table partition by", - ActionRemovePartitioning: "alter table remove partitioning", - - // `ActionAlterTableAlterPartition` is removed and will never be used. - // Just left a tombstone here for compatibility. - __DEPRECATED_ActionAlterTableAlterPartition: "alter partition", -} - -// String return current ddl action in string -func (action ActionType) String() string { - if v, ok := actionMap[action]; ok { - return v - } - return "none" -} - -// HistoryInfo is used for binlog. -type HistoryInfo struct { - SchemaVersion int64 - DBInfo *DBInfo - TableInfo *TableInfo - FinishedTS uint64 - - // MultipleTableInfos is like TableInfo but only for operations updating multiple tables. - MultipleTableInfos []*TableInfo -} - -// AddDBInfo adds schema version and schema information that are used for binlog. -// dbInfo is added in the following operations: create database, drop database. -func (h *HistoryInfo) AddDBInfo(schemaVer int64, dbInfo *DBInfo) { - h.SchemaVersion = schemaVer - h.DBInfo = dbInfo -} - -// AddTableInfo adds schema version and table information that are used for binlog. -// tblInfo is added except for the following operations: create database, drop database. -func (h *HistoryInfo) AddTableInfo(schemaVer int64, tblInfo *TableInfo) { - h.SchemaVersion = schemaVer - h.TableInfo = tblInfo -} - -// SetTableInfos is like AddTableInfo, but will add multiple table infos to the binlog. -func (h *HistoryInfo) SetTableInfos(schemaVer int64, tblInfos []*TableInfo) { - h.SchemaVersion = schemaVer - h.MultipleTableInfos = make([]*TableInfo, len(tblInfos)) - copy(h.MultipleTableInfos, tblInfos) -} - -// Clean cleans history information. -func (h *HistoryInfo) Clean() { - h.SchemaVersion = 0 - h.DBInfo = nil - h.TableInfo = nil - h.MultipleTableInfos = nil -} - -// TimeZoneLocation represents a single time zone. -type TimeZoneLocation struct { - Name string `json:"name"` - Offset int `json:"offset"` // seconds east of UTC - location *time.Location -} - -// GetLocation gets the timezone location. -func (tz *TimeZoneLocation) GetLocation() (*time.Location, error) { - if tz.location != nil { - return tz.location, nil - } - - var err error - if tz.Offset == 0 { - tz.location, err = time.LoadLocation(tz.Name) - } else { - tz.location = time.FixedZone(tz.Name, tz.Offset) - } - return tz.location, err -} - -// MultiSchemaInfo keeps some information for multi schema change. -type MultiSchemaInfo struct { - SubJobs []*SubJob `json:"sub_jobs"` - Revertible bool `json:"revertible"` - Seq int32 `json:"seq"` - - // SkipVersion is used to control whether generating a new schema version for a sub-job. - SkipVersion bool `json:"-"` - - AddColumns []CIStr `json:"-"` - DropColumns []CIStr `json:"-"` - ModifyColumns []CIStr `json:"-"` - AddIndexes []CIStr `json:"-"` - DropIndexes []CIStr `json:"-"` - AlterIndexes []CIStr `json:"-"` - - AddForeignKeys []AddForeignKeyInfo `json:"-"` - - RelativeColumns []CIStr `json:"-"` - PositionColumns []CIStr `json:"-"` -} - -// AddForeignKeyInfo contains foreign key information. -type AddForeignKeyInfo struct { - Name CIStr - Cols []CIStr -} - -// NewMultiSchemaInfo new a MultiSchemaInfo. -func NewMultiSchemaInfo() *MultiSchemaInfo { - return &MultiSchemaInfo{ - SubJobs: nil, - Revertible: true, - } -} - -// SubJob is a representation of one DDL schema change. A Job may contain zero(when multi-schema change is not applicable) or more SubJobs. -type SubJob struct { - Type ActionType `json:"type"` - Args []interface{} `json:"-"` - RawArgs json.RawMessage `json:"raw_args"` - SchemaState SchemaState `json:"schema_state"` - SnapshotVer uint64 `json:"snapshot_ver"` - RealStartTS uint64 `json:"real_start_ts"` - Revertible bool `json:"revertible"` - State JobState `json:"state"` - RowCount int64 `json:"row_count"` - Warning *terror.Error `json:"warning"` - CtxVars []interface{} `json:"-"` - SchemaVer int64 `json:"schema_version"` - ReorgTp ReorgType `json:"reorg_tp"` -} - -// IsNormal returns true if the sub-job is normally running. -func (sub *SubJob) IsNormal() bool { - switch sub.State { - case JobStateCancelling, JobStateCancelled, - JobStateRollingback, JobStateRollbackDone: - return false - default: - return true - } -} - -// IsFinished returns true if the job is done. -func (sub *SubJob) IsFinished() bool { - return sub.State == JobStateDone || - sub.State == JobStateRollbackDone || - sub.State == JobStateCancelled -} - -// ToProxyJob converts a sub-job to a proxy job. -func (sub *SubJob) ToProxyJob(parentJob *Job, seq int) Job { - return Job{ - ID: parentJob.ID, - Type: sub.Type, - SchemaID: parentJob.SchemaID, - TableID: parentJob.TableID, - SchemaName: parentJob.SchemaName, - State: sub.State, - Warning: sub.Warning, - Error: nil, - ErrorCount: 0, - RowCount: sub.RowCount, - Mu: sync.Mutex{}, - CtxVars: sub.CtxVars, - Args: sub.Args, - RawArgs: sub.RawArgs, - SchemaState: sub.SchemaState, - SnapshotVer: sub.SnapshotVer, - RealStartTS: sub.RealStartTS, - StartTS: parentJob.StartTS, - DependencyID: parentJob.DependencyID, - Query: parentJob.Query, - BinlogInfo: parentJob.BinlogInfo, - Version: parentJob.Version, - ReorgMeta: parentJob.ReorgMeta, - MultiSchemaInfo: &MultiSchemaInfo{Revertible: sub.Revertible, Seq: int32(seq)}, - Priority: parentJob.Priority, - SeqNum: parentJob.SeqNum, - Charset: parentJob.Charset, - Collate: parentJob.Collate, - AdminOperator: parentJob.AdminOperator, - TraceInfo: parentJob.TraceInfo, - } -} - -// FromProxyJob converts a proxy job to a sub-job. -func (sub *SubJob) FromProxyJob(proxyJob *Job, ver int64) { - sub.Revertible = proxyJob.MultiSchemaInfo.Revertible - sub.SchemaState = proxyJob.SchemaState - sub.SnapshotVer = proxyJob.SnapshotVer - sub.RealStartTS = proxyJob.RealStartTS - sub.Args = proxyJob.Args - sub.State = proxyJob.State - sub.Warning = proxyJob.Warning - sub.RowCount = proxyJob.RowCount - sub.SchemaVer = ver - sub.ReorgTp = proxyJob.ReorgMeta.ReorgTp -} - -// JobMeta is meta info of Job. -type JobMeta struct { - SchemaID int64 `json:"schema_id"` - TableID int64 `json:"table_id"` - // Type is the DDL job's type. - Type ActionType `json:"job_type"` - // Query is the DDL job's SQL string. - Query string `json:"query"` - // Priority is only used to set the operation priority of adding indices. - Priority int `json:"priority"` -} - -// Job is for a DDL operation. -type Job struct { - ID int64 `json:"id"` - Type ActionType `json:"type"` - SchemaID int64 `json:"schema_id"` - TableID int64 `json:"table_id"` - SchemaName string `json:"schema_name"` - TableName string `json:"table_name"` - State JobState `json:"state"` - Warning *terror.Error `json:"warning"` - Error *terror.Error `json:"err"` - // ErrorCount will be increased, every time we meet an error when running job. - ErrorCount int64 `json:"err_count"` - // RowCount means the number of rows that are processed. - RowCount int64 `json:"row_count"` - Mu sync.Mutex `json:"-"` - // CtxVars are variables attached to the job. It is for internal usage. - // E.g. passing arguments between functions by one single *Job pointer. - CtxVars []interface{} `json:"-"` - Args []interface{} `json:"-"` - // RawArgs : We must use json raw message to delay parsing special args. - RawArgs json.RawMessage `json:"raw_args"` - SchemaState SchemaState `json:"schema_state"` - // SnapshotVer means snapshot version for this job. - SnapshotVer uint64 `json:"snapshot_ver"` - // RealStartTS uses timestamp allocated by TSO. - // Now it's the TS when we actually start the job. - RealStartTS uint64 `json:"real_start_ts"` - // StartTS uses timestamp allocated by TSO. - // Now it's the TS when we put the job to TiKV queue. - StartTS uint64 `json:"start_ts"` - // DependencyID is the job's ID that the current job depends on. - DependencyID int64 `json:"dependency_id"` - // Query string of the ddl job. - Query string `json:"query"` - BinlogInfo *HistoryInfo `json:"binlog"` - - // Version indicates the DDL job version. For old jobs, it will be 0. - Version int64 `json:"version"` - - // ReorgMeta is meta info of ddl reorganization. - ReorgMeta *DDLReorgMeta `json:"reorg_meta"` - - // MultiSchemaInfo keeps some warning now for multi schema change. - MultiSchemaInfo *MultiSchemaInfo `json:"multi_schema_info"` - - // Priority is only used to set the operation priority of adding indices. - Priority int `json:"priority"` - - // SeqNum is the total order in all DDLs, it's used to identify the order of DDL. - SeqNum uint64 `json:"seq_num"` - - // Charset is the charset when the DDL Job is created. - Charset string `json:"charset"` - // Collate is the collation the DDL Job is created. - Collate string `json:"collate"` - - // AdminOperator indicates where the Admin command comes, by the TiDB - // itself (AdminCommandBySystem) or by user (AdminCommandByEndUser). - AdminOperator AdminCommandOperator `json:"admin_operator"` - - // TraceInfo indicates the information for SQL tracing - TraceInfo *TraceInfo `json:"trace_info"` -} - -// FinishTableJob is called when a job is finished. -// It updates the job's state information and adds tblInfo to the binlog. -func (job *Job) FinishTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfo *TableInfo) { - job.State = jobState - job.SchemaState = schemaState - job.BinlogInfo.AddTableInfo(ver, tblInfo) -} - -// FinishMultipleTableJob is called when a job is finished. -// It updates the job's state information and adds tblInfos to the binlog. -func (job *Job) FinishMultipleTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfos []*TableInfo) { - job.State = jobState - job.SchemaState = schemaState - job.BinlogInfo.SchemaVersion = ver - job.BinlogInfo.MultipleTableInfos = tblInfos - job.BinlogInfo.TableInfo = tblInfos[len(tblInfos)-1] -} - -// FinishDBJob is called when a job is finished. -// It updates the job's state information and adds dbInfo the binlog. -func (job *Job) FinishDBJob(jobState JobState, schemaState SchemaState, ver int64, dbInfo *DBInfo) { - job.State = jobState - job.SchemaState = schemaState - job.BinlogInfo.AddDBInfo(ver, dbInfo) -} - -// MarkNonRevertible mark the current job to be non-revertible. -// It means the job cannot be cancelled or rollbacked. -func (job *Job) MarkNonRevertible() { - if job.MultiSchemaInfo != nil { - job.MultiSchemaInfo.Revertible = false - } -} - -// Clone returns a copy of the job. -func (job *Job) Clone() *Job { - encode, err := job.Encode(true) - if err != nil { - return nil - } - var clone Job - err = clone.Decode(encode) - if err != nil { - return nil - } - if len(job.Args) > 0 { - clone.Args = make([]interface{}, len(job.Args)) - copy(clone.Args, job.Args) - } - if job.MultiSchemaInfo != nil { - for i, sub := range job.MultiSchemaInfo.SubJobs { - clone.MultiSchemaInfo.SubJobs[i].Args = make([]interface{}, len(sub.Args)) - copy(clone.MultiSchemaInfo.SubJobs[i].Args, sub.Args) - } - } - return &clone -} - -// TSConvert2Time converts timestamp to time. -func TSConvert2Time(ts uint64) time.Time { - t := int64(ts >> 18) // 18 is for the logical time. - return time.UnixMilli(t) -} - -// SetRowCount sets the number of rows. Make sure it can pass `make race`. -func (job *Job) SetRowCount(count int64) { - job.Mu.Lock() - defer job.Mu.Unlock() - - job.RowCount = count -} - -// GetRowCount gets the number of rows. Make sure it can pass `make race`. -func (job *Job) GetRowCount() int64 { - job.Mu.Lock() - defer job.Mu.Unlock() - - return job.RowCount -} - -// SetWarnings sets the warnings of rows handled. -func (job *Job) SetWarnings(warnings map[errors.ErrorID]*terror.Error, warningsCount map[errors.ErrorID]int64) { - job.Mu.Lock() - job.ReorgMeta.Warnings = warnings - job.ReorgMeta.WarningsCount = warningsCount - job.Mu.Unlock() -} - -// GetWarnings gets the warnings of the rows handled. -func (job *Job) GetWarnings() (map[errors.ErrorID]*terror.Error, map[errors.ErrorID]int64) { - job.Mu.Lock() - w, wc := job.ReorgMeta.Warnings, job.ReorgMeta.WarningsCount - job.Mu.Unlock() - return w, wc -} - -// Encode encodes job with json format. -// updateRawArgs is used to determine whether to update the raw args. -func (job *Job) Encode(updateRawArgs bool) ([]byte, error) { - var err error - if updateRawArgs { - job.RawArgs, err = json.Marshal(job.Args) - if err != nil { - return nil, errors.Trace(err) - } - if job.MultiSchemaInfo != nil { - for _, sub := range job.MultiSchemaInfo.SubJobs { - // Only update the args of executing sub-jobs. - if sub.Args == nil { - continue - } - sub.RawArgs, err = json.Marshal(sub.Args) - if err != nil { - return nil, errors.Trace(err) - } - } - } - } - - var b []byte - job.Mu.Lock() - defer job.Mu.Unlock() - b, err = json.Marshal(job) - - return b, errors.Trace(err) -} - -// Decode decodes job from the json buffer, we must use DecodeArgs later to -// decode special args for this job. -func (job *Job) Decode(b []byte) error { - err := json.Unmarshal(b, job) - return errors.Trace(err) -} - -// DecodeArgs decodes job args. -func (job *Job) DecodeArgs(args ...interface{}) error { - var rawArgs []json.RawMessage - if err := json.Unmarshal(job.RawArgs, &rawArgs); err != nil { - return errors.Trace(err) - } - - sz := len(rawArgs) - if sz > len(args) { - sz = len(args) - } - - for i := 0; i < sz; i++ { - if err := json.Unmarshal(rawArgs[i], args[i]); err != nil { - return errors.Trace(err) - } - } - job.Args = args[:sz] - return nil -} - -// String implements fmt.Stringer interface. -func (job *Job) String() string { - rowCount := job.GetRowCount() - ret := fmt.Sprintf("ID:%d, Type:%s, State:%s, SchemaState:%s, SchemaID:%d, TableID:%d, RowCount:%d, ArgLen:%d, start time: %v, Err:%v, ErrCount:%d, SnapshotVersion:%v", - job.ID, job.Type, job.State, job.SchemaState, job.SchemaID, job.TableID, rowCount, len(job.Args), TSConvert2Time(job.StartTS), job.Error, job.ErrorCount, job.SnapshotVer) - if job.ReorgMeta != nil { - warnings, _ := job.GetWarnings() - ret += fmt.Sprintf(", UniqueWarnings:%d", len(warnings)) - } - if job.Type != ActionMultiSchemaChange && job.MultiSchemaInfo != nil { - ret += fmt.Sprintf(", Multi-Schema Change:true, Revertible:%v", job.MultiSchemaInfo.Revertible) - } - return ret -} - -func (job *Job) hasDependentSchema(other *Job) (bool, error) { - if other.Type == ActionDropSchema || other.Type == ActionCreateSchema { - if other.SchemaID == job.SchemaID { - return true, nil - } - if job.Type == ActionRenameTable { - var oldSchemaID int64 - if err := job.DecodeArgs(&oldSchemaID); err != nil { - return false, errors.Trace(err) - } - if other.SchemaID == oldSchemaID { - return true, nil - } - } - if job.Type == ActionExchangeTablePartition { - var ( - defID int64 - ptSchemaID int64 - ptID int64 - partName string - withValidation bool - ) - if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { - return false, errors.Trace(err) - } - if other.SchemaID == ptSchemaID { - return true, nil - } - } - } - return false, nil -} - -func (job *Job) hasDependentTableForExchangePartition(other *Job) (bool, error) { - if job.Type == ActionExchangeTablePartition { - var ( - defID int64 - ptSchemaID int64 - ptID int64 - partName string - withValidation bool - ) - - if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { - return false, errors.Trace(err) - } - if ptID == other.TableID || defID == other.TableID { - return true, nil - } - - if other.Type == ActionExchangeTablePartition { - var ( - otherDefID int64 - otherPtSchemaID int64 - otherPtID int64 - otherPartName string - otherWithValidation bool - ) - if err := other.DecodeArgs(&otherDefID, &otherPtSchemaID, &otherPtID, &otherPartName, &otherWithValidation); err != nil { - return false, errors.Trace(err) - } - if job.TableID == other.TableID || job.TableID == otherPtID || job.TableID == otherDefID { - return true, nil - } - if ptID == other.TableID || ptID == otherPtID || ptID == otherDefID { - return true, nil - } - if defID == other.TableID || defID == otherPtID || defID == otherDefID { - return true, nil - } - } - } - return false, nil -} - -// IsDependentOn returns whether the job depends on "other". -// How to check the job depends on "other"? -// 1. The two jobs handle the same database when one of the two jobs is an ActionDropSchema or ActionCreateSchema type. -// 2. Or the two jobs handle the same table. -// 3. Or other job is flashback cluster. -func (job *Job) IsDependentOn(other *Job) (bool, error) { - if other.Type == ActionFlashbackCluster { - return true, nil - } - - isDependent, err := job.hasDependentSchema(other) - if err != nil || isDependent { - return isDependent, errors.Trace(err) - } - isDependent, err = other.hasDependentSchema(job) - if err != nil || isDependent { - return isDependent, errors.Trace(err) - } - - // TODO: If a job is ActionRenameTable, we need to check table name. - if other.TableID == job.TableID { - return true, nil - } - isDependent, err = job.hasDependentTableForExchangePartition(other) - if err != nil || isDependent { - return isDependent, errors.Trace(err) - } - isDependent, err = other.hasDependentTableForExchangePartition(job) - if err != nil || isDependent { - return isDependent, errors.Trace(err) - } - return false, nil -} - -// IsFinished returns whether job is finished or not. -// If the job state is Done or Cancelled, it is finished. -func (job *Job) IsFinished() bool { - return job.State == JobStateDone || job.State == JobStateRollbackDone || job.State == JobStateCancelled -} - -// IsCancelled returns whether the job is cancelled or not. -func (job *Job) IsCancelled() bool { - return job.State == JobStateCancelled -} - -// IsRollbackDone returns whether the job is rolled back or not. -func (job *Job) IsRollbackDone() bool { - return job.State == JobStateRollbackDone -} - -// IsRollingback returns whether the job is rolling back or not. -func (job *Job) IsRollingback() bool { - return job.State == JobStateRollingback -} - -// IsCancelling returns whether the job is cancelling or not. -func (job *Job) IsCancelling() bool { - return job.State == JobStateCancelling -} - -// IsPaused returns whether the job is paused. -func (job *Job) IsPaused() bool { - return job.State == JobStatePaused -} - -// IsPausedBySystem returns whether the job is paused by system. -func (job *Job) IsPausedBySystem() bool { - return job.IsPaused() && job.AdminOperator == AdminCommandBySystem -} - -// IsPausing indicates whether the job is pausing. -func (job *Job) IsPausing() bool { - return job.State == JobStatePausing -} - -// IsPausable checks whether we can pause the job. -func (job *Job) IsPausable() bool { - return job.NotStarted() || (job.IsRunning() && job.IsRollbackable()) -} - -// IsResumable checks whether the job can be rollback. -func (job *Job) IsResumable() bool { - return job.IsPaused() -} - -// IsSynced returns whether the DDL modification is synced among all TiDB servers. -func (job *Job) IsSynced() bool { - return job.State == JobStateSynced -} - -// IsDone returns whether job is done. -func (job *Job) IsDone() bool { - return job.State == JobStateDone -} - -// IsRunning returns whether job is still running or not. -func (job *Job) IsRunning() bool { - return job.State == JobStateRunning -} - -// IsQueueing returns whether job is queuing or not. -func (job *Job) IsQueueing() bool { - return job.State == JobStateQueueing -} - -// NotStarted returns true if the job is never run by a worker. -func (job *Job) NotStarted() bool { - return job.State == JobStateNone || job.State == JobStateQueueing -} - -// MayNeedReorg indicates that this job may need to reorganize the data. -func (job *Job) MayNeedReorg() bool { - switch job.Type { - case ActionAddIndex, ActionAddPrimaryKey, ActionReorganizePartition, - ActionRemovePartitioning, ActionAlterTablePartitioning: - return true - case ActionModifyColumn: - if len(job.CtxVars) > 0 { - needReorg, ok := job.CtxVars[0].(bool) - return ok && needReorg - } - return false - case ActionMultiSchemaChange: - for _, sub := range job.MultiSchemaInfo.SubJobs { - proxyJob := Job{Type: sub.Type, CtxVars: sub.CtxVars} - if proxyJob.MayNeedReorg() { - return true - } - } - return false - default: - return false - } -} - -// IsRollbackable checks whether the job can be rollback. -func (job *Job) IsRollbackable() bool { - switch job.Type { - case ActionDropIndex, ActionDropPrimaryKey: - // We can't cancel if index current state is in StateDeleteOnly or StateDeleteReorganization or StateWriteOnly, otherwise there will be an inconsistent issue between record and index. - // In WriteOnly state, we can rollback for normal index but can't rollback for expression index(need to drop hidden column). Since we can't - // know the type of index here, we consider all indices except primary index as non-rollbackable. - // TODO: distinguish normal index and expression index so that we can rollback `DropIndex` for normal index in WriteOnly state. - // TODO: make DropPrimaryKey rollbackable in WriteOnly, it need to deal with some tests. - if job.SchemaState == StateDeleteOnly || - job.SchemaState == StateDeleteReorganization || - job.SchemaState == StateWriteOnly { - return false - } - case ActionAddTablePartition: - return job.SchemaState == StateNone || job.SchemaState == StateReplicaOnly - case ActionDropColumn, ActionDropSchema, ActionDropTable, ActionDropSequence, - ActionDropForeignKey, ActionDropTablePartition, ActionTruncateTablePartition: - return job.SchemaState == StatePublic - case ActionRebaseAutoID, ActionShardRowID, - ActionTruncateTable, ActionAddForeignKey, ActionRenameTable, ActionRenameTables, - ActionModifyTableCharsetAndCollate, - ActionModifySchemaCharsetAndCollate, ActionRepairTable, - ActionModifyTableAutoIdCache, ActionModifySchemaDefaultPlacement, ActionDropCheckConstraint: - return job.SchemaState == StateNone - case ActionMultiSchemaChange: - return job.MultiSchemaInfo.Revertible - case ActionFlashbackCluster: - if job.SchemaState == StateWriteReorganization || - job.SchemaState == StateWriteOnly { - return false - } - } - return true -} - -// JobState is for job state. -type JobState int32 - -// List job states. -const ( - JobStateNone JobState = 0 - JobStateRunning JobState = 1 - // When DDL encountered an unrecoverable error at reorganization state, - // some keys has been added already, we need to remove them. - // JobStateRollingback is the state to do the rolling back job. - JobStateRollingback JobState = 2 - JobStateRollbackDone JobState = 3 - JobStateDone JobState = 4 - JobStateCancelled JobState = 5 - // JobStateSynced is used to mark the information about the completion of this job - // has been synchronized to all servers. - JobStateSynced JobState = 6 - // JobStateCancelling is used to mark the DDL job is cancelled by the client, but the DDL work hasn't handle it. - JobStateCancelling JobState = 7 - // JobStateQueueing means the job has not yet been started. - JobStateQueueing JobState = 8 - - JobStatePaused JobState = 9 - JobStatePausing JobState = 10 -) - -// String implements fmt.Stringer interface. -func (s JobState) String() string { - switch s { - case JobStateRunning: - return "running" - case JobStateRollingback: - return "rollingback" - case JobStateRollbackDone: - return "rollback done" - case JobStateDone: - return "done" - case JobStateCancelled: - return "cancelled" - case JobStateCancelling: - return "cancelling" - case JobStateSynced: - return "synced" - case JobStateQueueing: - return "queueing" - case JobStatePaused: - return "paused" - case JobStatePausing: - return "pausing" - default: - return "none" - } -} - -// StrToJobState converts string to JobState. -func StrToJobState(s string) JobState { - switch s { - case "running": - return JobStateRunning - case "rollingback": - return JobStateRollingback - case "rollback done": - return JobStateRollbackDone - case "done": - return JobStateDone - case "cancelled": - return JobStateCancelled - case "cancelling": - return JobStateCancelling - case "synced": - return JobStateSynced - case "queueing": - return JobStateQueueing - case "paused": - return JobStatePaused - case "pausing": - return JobStatePausing - default: - return JobStateNone - } -} - -// AdminCommandOperator indicates where the Cancel/Pause/Resume command on DDL -// jobs comes from. -type AdminCommandOperator int - -const ( - // AdminCommandByNotKnown indicates that unknow calling of the - // Cancel/Pause/Resume on DDL job. - AdminCommandByNotKnown AdminCommandOperator = iota - // AdminCommandByEndUser indicates that the Cancel/Pause/Resume command on - // DDL job is issued by the end user. - AdminCommandByEndUser - // AdminCommandBySystem indicates that the Cancel/Pause/Resume command on - // DDL job is issued by TiDB itself, such as Upgrade(bootstrap). - AdminCommandBySystem -) - -func (a *AdminCommandOperator) String() string { - switch *a { - case AdminCommandByEndUser: - return "EndUser" - case AdminCommandBySystem: - return "System" - default: - return "None" - } -} - -// SchemaDiff contains the schema modification at a particular schema version. -// It is used to reduce schema reload cost. -type SchemaDiff struct { - Version int64 `json:"version"` - Type ActionType `json:"type"` - SchemaID int64 `json:"schema_id"` - TableID int64 `json:"table_id"` - - // OldTableID is the table ID before truncate, only used by truncate table DDL. - OldTableID int64 `json:"old_table_id"` - // OldSchemaID is the schema ID before rename table, only used by rename table DDL. - OldSchemaID int64 `json:"old_schema_id"` - // RegenerateSchemaMap means whether to rebuild the schema map when applying to the schema diff. - RegenerateSchemaMap bool `json:"regenerate_schema_map"` - - AffectedOpts []*AffectedOption `json:"affected_options"` -} - -// AffectedOption is used when a ddl affects multi tables. -type AffectedOption struct { - SchemaID int64 `json:"schema_id"` - TableID int64 `json:"table_id"` - OldTableID int64 `json:"old_table_id"` - OldSchemaID int64 `json:"old_schema_id"` -} diff --git a/parser/model/ddl_test.go b/parser/model/ddl_test.go deleted file mode 100644 index b0d53708cc851..0000000000000 --- a/parser/model/ddl_test.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package model_test - -import ( - "testing" - "unsafe" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/require" -) - -func TestJobClone(t *testing.T) { - job := &model.Job{ - ID: 100, - Type: model.ActionCreateTable, - SchemaID: 101, - TableID: 102, - SchemaName: "test", - TableName: "t", - State: model.JobStateDone, - MultiSchemaInfo: nil, - } - clone := job.Clone() - require.Equal(t, job.ID, clone.ID) - require.Equal(t, job.Type, clone.Type) - require.Equal(t, job.SchemaID, clone.SchemaID) - require.Equal(t, job.TableID, clone.TableID) - require.Equal(t, job.SchemaName, clone.SchemaName) - require.Equal(t, job.TableName, clone.TableName) - require.Equal(t, job.State, clone.State) - require.Equal(t, job.MultiSchemaInfo, clone.MultiSchemaInfo) -} - -func TestJobSize(t *testing.T) { - msg := `Please make sure that the following methods work as expected: -- SubJob.FromProxyJob() -- SubJob.ToProxyJob() -` - job := model.Job{} - require.Equal(t, 336, int(unsafe.Sizeof(job)), msg) -} - -func TestBackfillMetaCodec(t *testing.T) { - jm := &model.JobMeta{ - SchemaID: 1, - TableID: 2, - Query: "alter table t add index idx(a)", - Priority: 1, - } - bm := &model.BackfillMeta{ - EndInclude: true, - Error: terror.ErrResultUndetermined, - JobMeta: jm, - } - bmBytes, err := bm.Encode() - require.NoError(t, err) - bmRet := &model.BackfillMeta{} - bmRet.Decode(bmBytes) - require.Equal(t, bm, bmRet) -} - -func TestMayNeedReorg(t *testing.T) { - //TODO(bb7133): add more test cases for different ActionType. - reorgJobTypes := []model.ActionType{ - model.ActionReorganizePartition, - model.ActionRemovePartitioning, - model.ActionAlterTablePartitioning, - model.ActionAddIndex, - model.ActionAddPrimaryKey, - } - generalJobTypes := []model.ActionType{ - model.ActionCreateTable, - model.ActionDropTable, - } - job := &model.Job{ - ID: 100, - Type: model.ActionCreateTable, - SchemaID: 101, - TableID: 102, - SchemaName: "test", - TableName: "t", - State: model.JobStateDone, - MultiSchemaInfo: nil, - } - for _, jobType := range reorgJobTypes { - job.Type = jobType - require.True(t, job.MayNeedReorg()) - } - for _, jobType := range generalJobTypes { - job.Type = jobType - require.False(t, job.MayNeedReorg()) - } -} diff --git a/parser/model/reorg.go b/parser/model/reorg.go deleted file mode 100644 index f2c3cb7a7367b..0000000000000 --- a/parser/model/reorg.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package model - -import ( - "encoding/json" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" -) - -// DDLReorgMeta is meta info of DDL reorganization. -type DDLReorgMeta struct { - SQLMode mysql.SQLMode `json:"sql_mode"` - Warnings map[errors.ErrorID]*terror.Error `json:"warnings"` - WarningsCount map[errors.ErrorID]int64 `json:"warnings_count"` - Location *TimeZoneLocation `json:"location"` - ReorgTp ReorgType `json:"reorg_tp"` - IsDistReorg bool `json:"is_dist_reorg"` - ResourceGroupName string `json:"resource_group_name"` -} - -// ReorgType indicates which process is used for the data reorganization. -type ReorgType int8 - -const ( - // ReorgTypeNone means the backfill task is not started yet. - ReorgTypeNone ReorgType = iota - // ReorgTypeTxn means the index records are backfill with transactions. - // All the index KVs are written through the transaction interface. - // This is the original backfill implementation. - ReorgTypeTxn - // ReorgTypeLitMerge means the index records are backfill with lightning. - // The index KVs are encoded to SST files and imported to the storage directly. - // The incremental index KVs written by DML are redirected to a temporary index. - // After the backfill is finished, the temporary index records are merged back to the original index. - ReorgTypeLitMerge - // ReorgTypeTxnMerge means backfill with transactions and merge incremental changes. - // The backfill index KVs are written through the transaction interface. - // The incremental index KVs written by DML are redirected to a temporary index. - // After the backfill is finished, the temporary index records are merged back to the original index. - ReorgTypeTxnMerge -) - -// NeedMergeProcess means the incremental changes need to be merged. -func (tp ReorgType) NeedMergeProcess() bool { - return tp == ReorgTypeLitMerge || tp == ReorgTypeTxnMerge -} - -// String implements fmt.Stringer interface. -func (tp ReorgType) String() string { - switch tp { - case ReorgTypeTxn: - return "txn" - case ReorgTypeLitMerge: - return "ingest" - case ReorgTypeTxnMerge: - return "txn-merge" - } - return "" -} - -// BackfillState is the state used by the backfill-merge process. -type BackfillState byte - -const ( - // BackfillStateInapplicable means the backfill-merge process is not used. - BackfillStateInapplicable BackfillState = iota - // BackfillStateRunning is the state that the backfill process is running. - // In this state, the index's write and delete operations are redirected to a temporary index. - BackfillStateRunning - // BackfillStateReadyToMerge is the state that the temporary index's records are ready to be merged back - // to the origin index. - // In this state, the index's write and delete operations are copied to a temporary index. - // This state is used to make sure that all the TiDB instances are aware of the copy - // during the merge(BackfillStateMerging). - BackfillStateReadyToMerge - // BackfillStateMerging is the state that the temp index is merging back to the origin index. - // In this state, the index's write and delete operations are copied to a temporary index. - BackfillStateMerging -) - -// String implements fmt.Stringer interface. -func (s BackfillState) String() string { - switch s { - case BackfillStateRunning: - return "backfill state running" - case BackfillStateReadyToMerge: - return "backfill state ready to merge" - case BackfillStateMerging: - return "backfill state merging" - case BackfillStateInapplicable: - return "backfill state inapplicable" - default: - return "backfill state unknown" - } -} - -// BackfillMeta is meta info of the backfill job. -type BackfillMeta struct { - IsUnique bool `json:"is_unique"` - EndInclude bool `json:"end_include"` - Error *terror.Error `json:"err"` - - SQLMode mysql.SQLMode `json:"sql_mode"` - Warnings map[errors.ErrorID]*terror.Error `json:"warnings"` - WarningsCount map[errors.ErrorID]int64 `json:"warnings_count"` - Location *TimeZoneLocation `json:"location"` - ReorgTp ReorgType `json:"reorg_tp"` - RowCount int64 `json:"row_count"` - StartKey []byte `json:"start_key"` - EndKey []byte `json:"end_key"` - CurrKey []byte `json:"curr_key"` - *JobMeta `json:"job_meta"` -} - -// Encode encodes BackfillMeta with json format. -func (bm *BackfillMeta) Encode() ([]byte, error) { - b, err := json.Marshal(bm) - return b, errors.Trace(err) -} - -// Decode decodes BackfillMeta from the json buffer. -func (bm *BackfillMeta) Decode(b []byte) error { - err := json.Unmarshal(b, bm) - return errors.Trace(err) -} diff --git a/parser/mysql/BUILD.bazel b/parser/mysql/BUILD.bazel deleted file mode 100644 index 46490b56224ee..0000000000000 --- a/parser/mysql/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mysql", - srcs = [ - "charset.go", - "const.go", - "errcode.go", - "errname.go", - "error.go", - "locale_format.go", - "privs.go", - "state.go", - "type.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/parser/mysql", - visibility = ["//visibility:public"], - deps = [ - "//parser/format", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "mysql_test", - timeout = "short", - srcs = [ - "const_test.go", - "error_test.go", - "privs_test.go", - "type_test.go", - ], - embed = [":mysql"], - flaky = True, - shard_count = 8, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/parser/opcode/BUILD.bazel b/parser/opcode/BUILD.bazel deleted file mode 100644 index 584bb81154c32..0000000000000 --- a/parser/opcode/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "opcode", - srcs = ["opcode.go"], - importpath = "github.com/pingcap/tidb/parser/opcode", - visibility = ["//visibility:public"], - deps = ["//parser/format"], -) - -go_test( - name = "opcode_test", - timeout = "short", - srcs = ["opcode_test.go"], - embed = [":opcode"], - flaky = True, -) diff --git a/parser/parser.go b/parser/parser.go deleted file mode 100644 index c37fac7eac113..0000000000000 --- a/parser/parser.go +++ /dev/null @@ -1,24733 +0,0 @@ -// Code generated by goyacc DO NOT EDIT. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -// Initial yacc source generated by ebnf2y[1] -// at 2013-10-04 23:10:47.861401015 +0200 CEST -// -// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ -// -// [1]: http://github.com/cznic/ebnf2y - -package parser - -import __yyfmt__ "fmt" - -import ( - "strings" - "time" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/duration" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/types" -) - -type yySymType struct { - yys int - offset int // offset - item interface{} - ident string - expr ast.ExprNode - statement ast.StmtNode -} - -type yyXError struct { - state, xsym int -} - -const ( - yyDefault = 58193 - yyEOFCode = 57344 - account = 57595 - action = 57596 - add = 57362 - addDate = 57961 - admin = 58077 - advise = 57597 - after = 57598 - against = 57599 - ago = 57600 - algorithm = 57601 - all = 57363 - alter = 57364 - always = 57602 - analyze = 57365 - and = 57366 - andand = 57357 - andnot = 58153 - any = 57603 - approxCountDistinct = 57962 - approxPercentile = 57963 - array = 57367 - as = 57368 - asc = 57369 - ascii = 57604 - asof = 57347 - assignmentEq = 58154 - attribute = 57605 - attributes = 57606 - autoIdCache = 57611 - autoIncrement = 57612 - autoRandom = 57613 - autoRandomBase = 57614 - avg = 57615 - avgRowLength = 57616 - backend = 57617 - background = 58075 - backup = 57618 - backups = 57619 - batch = 58078 - begin = 57620 - bernoulli = 57621 - between = 57370 - bigIntType = 57371 - binaryType = 57372 - binding = 57622 - bindingCache = 57623 - bindings = 57624 - binlog = 57625 - bitAnd = 57964 - bitLit = 58152 - bitOr = 57965 - bitType = 57626 - bitXor = 57966 - blobType = 57373 - block = 57627 - boolType = 57629 - booleanType = 57628 - both = 57374 - bound = 57967 - br = 57968 - briefType = 57969 - btree = 57630 - buckets = 58079 - builtinApproxCountDistinct = 58126 - builtinApproxPercentile = 58127 - builtinBitAnd = 58121 - builtinBitOr = 58122 - builtinBitXor = 58123 - builtinCast = 58124 - builtinCount = 58125 - builtinCurDate = 58128 - builtinCurTime = 58129 - builtinDateAdd = 58130 - builtinDateSub = 58131 - builtinExtract = 58132 - builtinGroupConcat = 58133 - builtinMax = 58134 - builtinMin = 58135 - builtinNow = 58136 - builtinPosition = 58137 - builtinStddevPop = 58141 - builtinStddevSamp = 58142 - builtinSubstring = 58138 - builtinSum = 58139 - builtinSysDate = 58140 - builtinTranslate = 58143 - builtinTrim = 58144 - builtinUser = 58145 - builtinVarPop = 58146 - builtinVarSamp = 58147 - builtins = 58080 - burstable = 57970 - by = 57375 - byteType = 57631 - cache = 57632 - calibrate = 57633 - call = 57376 - cancel = 58081 - capture = 57634 - cardinality = 58082 - cascade = 57377 - cascaded = 57635 - caseKwd = 57378 - cast = 57971 - causal = 57636 - chain = 57637 - change = 57379 - charType = 57381 - character = 57380 - charsetKwd = 57638 - check = 57382 - checkpoint = 57639 - checksum = 57640 - cipher = 57641 - cleanup = 57642 - client = 57643 - clientErrorsSummary = 57644 - close = 57670 - cluster = 57671 - clustered = 57672 - cmSketch = 58083 - coalesce = 57645 - collate = 57383 - collation = 57646 - column = 57384 - columnFormat = 57647 - columnStatsUsage = 58084 - columns = 57648 - comment = 57650 - commit = 57651 - committed = 57652 - compact = 57653 - compressed = 57654 - compression = 57655 - concurrency = 57656 - config = 57649 - connection = 57657 - consistency = 57658 - consistent = 57659 - constraint = 57385 - constraints = 57973 - context = 57660 - continueKwd = 57386 - convert = 57387 - cooldown = 58071 - copyKwd = 57972 - correlation = 58085 - cpu = 57661 - create = 57388 - createTableSelect = 58177 - cross = 57389 - csvBackslashEscape = 57662 - csvDelimiter = 57663 - csvHeader = 57664 - csvNotNull = 57665 - csvNull = 57666 - csvSeparator = 57667 - csvTrimLastSeparators = 57668 - cumeDist = 57390 - curDate = 57975 - curTime = 57974 - current = 57669 - currentDate = 57391 - currentRole = 57395 - currentTime = 57392 - currentTs = 57393 - currentUser = 57394 - cursor = 57396 - cycle = 57673 - data = 57674 - database = 57397 - databases = 57398 - dateAdd = 57976 - dateSub = 57977 - dateType = 57676 - datetimeType = 57675 - day = 57677 - dayHour = 57399 - dayMicrosecond = 57400 - dayMinute = 57401 - daySecond = 57402 - ddl = 58086 - deallocate = 57678 - decLit = 58149 - decimalType = 57403 - declare = 57679 - defaultKwd = 57404 - defined = 57978 - definer = 57680 - delayKeyWrite = 57681 - delayed = 57405 - deleteKwd = 57406 - denseRank = 57407 - dependency = 58087 - depth = 58088 - desc = 57408 - describe = 57409 - digest = 57682 - directory = 57683 - disable = 57684 - disabled = 57685 - discard = 57686 - disk = 57687 - distinct = 57410 - distinctRow = 57411 - div = 57412 - do = 57688 - dotType = 57979 - doubleAtIdentifier = 57354 - doubleType = 57413 - drainer = 58089 - drop = 57414 - dry = 58090 - dryRun = 58070 - dual = 57415 - dump = 57980 - duplicate = 57689 - dynamic = 57690 - elseIfKwd = 57416 - elseKwd = 57417 - empty = 58167 - enable = 57691 - enabled = 57692 - enclosed = 57418 - encryption = 57693 - end = 57694 - endTime = 57982 - enforced = 57695 - engine = 57696 - engines = 57697 - enum = 57698 - eq = 58155 - yyErrCode = 57345 - errorKwd = 57699 - escape = 57700 - escaped = 57419 - event = 57701 - events = 57702 - evolve = 57703 - exact = 57983 - except = 57423 - exchange = 57704 - exclusive = 57705 - execElapsed = 58069 - execute = 57706 - exists = 57420 - exit = 57421 - expansion = 57707 - expire = 57708 - explain = 57422 - exprPushdownBlacklist = 57984 - extended = 57709 - extract = 57985 - failedLoginAttempts = 57959 - falseKwd = 57424 - faultsSym = 57710 - fetch = 57425 - fields = 57711 - file = 57712 - first = 57713 - firstValue = 57426 - fixed = 57714 - flashback = 57986 - float4Type = 57428 - float8Type = 57429 - floatLit = 58148 - floatType = 57427 - flush = 57715 - follower = 57987 - followerConstraints = 57988 - followers = 57989 - following = 57717 - forKwd = 57430 - force = 57431 - foreign = 57432 - format = 57718 - found = 57716 - from = 57433 - full = 57719 - fullBackupStorage = 57990 - fulltext = 57434 - function = 57720 - gcTTL = 57992 - ge = 58156 - general = 57721 - generated = 57435 - getFormat = 57991 - global = 57722 - grant = 57436 - grants = 57723 - group = 57437 - groupConcat = 57993 - groups = 57438 - handler = 57724 - hash = 57725 - having = 57439 - help = 57726 - hexLit = 58151 - high = 58064 - highPriority = 57440 - higherThanComma = 58192 - higherThanParenthese = 58186 - hintComment = 57356 - histogram = 57727 - histogramsInFlight = 58110 - history = 57728 - hosts = 57729 - hour = 57730 - hourMicrosecond = 57441 - hourMinute = 57442 - hourSecond = 57443 - hypo = 57865 - identSQLErrors = 57732 - identified = 57731 - identifier = 57346 - ifKwd = 57444 - ignore = 57445 - ilike = 57476 - importKwd = 57733 - imports = 57734 - in = 57446 - increment = 57735 - incremental = 57736 - index = 57447 - indexes = 57737 - infile = 57448 - inner = 57449 - inout = 57450 - inplace = 57995 - insert = 57457 - insertMethod = 57738 - insertValues = 58175 - instance = 57739 - instant = 57996 - int1Type = 57459 - int2Type = 57460 - int3Type = 57461 - int4Type = 57462 - int8Type = 57463 - intLit = 58150 - intType = 57458 - integerType = 57451 - internal = 57997 - intersect = 57452 - interval = 57453 - into = 57454 - invalid = 57355 - invisible = 57740 - invoker = 57741 - io = 57742 - ioReadBandwidth = 58067 - ioWriteBandwidth = 58068 - ipc = 57743 - is = 57456 - isolation = 57744 - issuer = 57745 - iterate = 57464 - job = 58092 - jobs = 58091 - join = 57465 - jsonArrayagg = 57998 - jsonObjectAgg = 57999 - jsonType = 57746 - jss = 58158 - juss = 58159 - key = 57466 - keyBlockSize = 57747 - keys = 57467 - kill = 57468 - labels = 57748 - lag = 57469 - language = 57749 - last = 57750 - lastBackup = 57751 - lastValue = 57470 - lastval = 57752 - le = 58157 - lead = 57471 - leader = 58000 - leaderConstraints = 58001 - leading = 57472 - learner = 58002 - learnerConstraints = 58003 - learners = 58004 - leave = 57473 - left = 57474 - less = 57753 - level = 57754 - like = 57475 - limit = 57477 - linear = 57479 - lines = 57478 - list = 57755 - load = 57480 - local = 57756 - localTime = 57481 - localTs = 57482 - location = 57758 - lock = 57483 - locked = 57757 - logs = 57759 - long = 57579 - longblobType = 57484 - longtextType = 57485 - low = 58066 - lowPriority = 57486 - lowerThanCharsetKwd = 58178 - lowerThanComma = 58191 - lowerThanCreateTableSelect = 58176 - lowerThanEq = 58188 - lowerThanFunction = 58183 - lowerThanInsertValues = 58174 - lowerThanKey = 58179 - lowerThanLocal = 58180 - lowerThanNot = 58190 - lowerThanOn = 58187 - lowerThanParenthese = 58185 - lowerThanRemove = 58181 - lowerThanSelectOpt = 58168 - lowerThanSelectStmt = 58173 - lowerThanSetKeyword = 58172 - lowerThanStringLitToken = 58171 - lowerThanValueKeyword = 58169 - lowerThanWith = 58170 - lowerThenOrder = 58182 - lsh = 58160 - master = 57760 - match = 57487 - max = 58006 - maxConnectionsPerHour = 57763 - maxQueriesPerHour = 57764 - maxRows = 57765 - maxUpdatesPerHour = 57766 - maxUserConnections = 57767 - maxValue = 57488 - max_idxnum = 57761 - max_minutes = 57762 - mb = 57768 - medium = 58065 - mediumIntType = 57490 - mediumblobType = 57489 - mediumtextType = 57491 - member = 57769 - memberof = 57349 - memory = 57770 - merge = 57771 - metadata = 58007 - microsecond = 57772 - middleIntType = 57492 - min = 58005 - minRows = 57773 - minValue = 57775 - minute = 57774 - minuteMicrosecond = 57493 - minuteSecond = 57494 - mod = 57495 - mode = 57776 - modify = 57777 - month = 57778 - names = 57779 - national = 57780 - natural = 57594 - ncharType = 57781 - neg = 58189 - neq = 58161 - neqSynonym = 58162 - never = 57782 - next = 57783 - next_row_id = 57994 - nextval = 57784 - no = 57785 - noWriteToBinLog = 57497 - nocache = 57786 - nocycle = 57787 - nodeID = 58093 - nodeState = 58094 - nodegroup = 57788 - nomaxvalue = 57789 - nominvalue = 57790 - nonclustered = 57791 - none = 57792 - not = 57496 - not2 = 58166 - now = 58008 - nowait = 57793 - nthValue = 57498 - ntile = 57499 - null = 57500 - nulleq = 58163 - nulls = 57795 - numericType = 57501 - nvarcharType = 57794 - odbcDateType = 57359 - odbcTimeType = 57360 - odbcTimestampType = 57361 - of = 57502 - off = 57796 - offset = 57797 - oltpReadOnly = 57798 - oltpReadWrite = 57799 - oltpWriteOnly = 57800 - on = 57503 - onDuplicate = 57802 - online = 57803 - only = 57804 - open = 57805 - optRuleBlacklist = 58009 - optimistic = 58095 - optimize = 57504 - option = 57505 - optional = 57806 - optionally = 57506 - optionallyEnclosedBy = 57350 - or = 57507 - order = 57508 - out = 57509 - outer = 57510 - outfile = 57455 - over = 57511 - packKeys = 57807 - pageSym = 57808 - paramMarker = 58164 - parser = 57809 - partial = 57810 - partition = 57512 - partitioning = 57811 - partitions = 57812 - password = 57813 - passwordLockTime = 57960 - pause = 57814 - per_db = 57816 - per_table = 57817 - percent = 57815 - percentRank = 57513 - pessimistic = 58096 - pipes = 57358 - pipesAsOr = 57818 - placement = 58010 - plan = 58011 - planCache = 58012 - plugins = 57819 - point = 57820 - policy = 57821 - position = 58013 - preSplitRegions = 57822 - preceding = 57823 - precisionType = 57514 - predicate = 58014 - prepare = 57824 - preserve = 57825 - primary = 57515 - primaryRegion = 58015 - priority = 58063 - privileges = 57826 - procedure = 57516 - process = 57827 - processlist = 57828 - profile = 57829 - profiles = 57830 - proxy = 57831 - pump = 58097 - purge = 57832 - quarter = 57833 - queries = 57834 - query = 57835 - queryLimit = 58074 - quick = 57836 - rangeKwd = 57517 - rank = 57518 - rateLimit = 57837 - read = 57519 - realType = 57520 - rebuild = 57838 - recent = 58016 - recover = 57839 - recursive = 57521 - redundant = 57840 - references = 57522 - regexpKwd = 57523 - region = 58120 - regions = 58119 - release = 57524 - reload = 57841 - remove = 57842 - rename = 57525 - reorganize = 57843 - repair = 57844 - repeat = 57526 - repeatable = 57845 - replace = 57527 - replayer = 58017 - replica = 57846 - replicas = 57847 - replication = 57848 - require = 57528 - required = 57849 - reset = 58118 - resource = 57850 - respect = 57851 - restart = 57852 - restore = 57853 - restoredTS = 58018 - restores = 57854 - restrict = 57529 - resume = 57855 - reuse = 57856 - reverse = 57857 - revoke = 57530 - right = 57531 - rlike = 57532 - role = 57858 - rollback = 57859 - rollup = 57860 - routine = 57861 - row = 57533 - rowCount = 57862 - rowFormat = 57863 - rowNumber = 57535 - rows = 57534 - rsh = 58165 - rtree = 57864 - ruRate = 58062 - run = 58098 - running = 58019 - s3 = 58020 - sampleRate = 58100 - samples = 58099 - san = 57866 - savepoint = 57867 - schedule = 58021 - second = 57868 - secondMicrosecond = 57536 - secondaryEngine = 57869 - secondaryLoad = 57870 - secondaryUnload = 57871 - security = 57872 - selectKwd = 57537 - sendCredentialsToTiKV = 57873 - separator = 57874 - sequence = 57875 - serial = 57876 - serializable = 57877 - session = 57878 - sessionStates = 58101 - set = 57538 - setval = 57879 - shardRowIDBits = 57880 - share = 57881 - shared = 57882 - show = 57539 - shutdown = 57883 - signed = 57884 - similar = 58073 - simple = 57885 - singleAtIdentifier = 57353 - skip = 57886 - skipSchemaFiles = 57887 - slave = 57888 - slow = 57889 - smallIntType = 57540 - snapshot = 57890 - some = 57891 - source = 57892 - spatial = 57541 - split = 58116 - sql = 57542 - sqlBigResult = 57543 - sqlBufferResult = 57893 - sqlCache = 57894 - sqlCalcFoundRows = 57544 - sqlNoCache = 57895 - sqlSmallResult = 57545 - sqlTsiDay = 57896 - sqlTsiHour = 57897 - sqlTsiMinute = 57898 - sqlTsiMonth = 57899 - sqlTsiQuarter = 57900 - sqlTsiSecond = 57901 - sqlTsiWeek = 57902 - sqlTsiYear = 57903 - sqlexception = 57546 - sqlstate = 57547 - sqlwarning = 57548 - ssl = 57549 - staleness = 58022 - start = 57904 - startTS = 58024 - startTime = 58023 - starting = 57550 - statistics = 58102 - stats = 58103 - statsAutoRecalc = 57905 - statsBuckets = 58106 - statsColChoice = 57609 - statsColList = 57610 - statsExtended = 57551 - statsHealthy = 58107 - statsHistograms = 58105 - statsLocked = 58109 - statsMeta = 58104 - statsOptions = 57607 - statsPersistent = 57906 - statsSamplePages = 57907 - statsSampleRate = 57608 - statsTopN = 58108 - status = 57908 - std = 58025 - stddev = 58026 - stddevPop = 58027 - stddevSamp = 58028 - stop = 58029 - storage = 57909 - stored = 57556 - straightJoin = 57552 - strict = 58030 - strictFormat = 57910 - stringLit = 57352 - strong = 58031 - subDate = 58032 - subject = 57911 - subpartition = 57912 - subpartitions = 57913 - substring = 58034 - sum = 58033 - super = 57914 - survivalPreferences = 58035 - swaps = 57915 - switchesSym = 57916 - system = 57917 - systemTime = 57918 - tableChecksum = 57919 - tableKwd = 57554 - tableRefPriority = 58184 - tableSample = 57555 - tables = 57920 - tablespace = 57921 - target = 58036 - taskTypes = 58037 - telemetry = 58111 - telemetryID = 58112 - temporary = 57922 - temptable = 57923 - terminated = 57557 - textType = 57924 - than = 57925 - then = 57558 - tiFlash = 58114 - tidb = 58113 - tidbCurrentTSO = 57553 - tidbJson = 58038 - tikvImporter = 57926 - timeDuration = 57981 - timeType = 57928 - timestampAdd = 58039 - timestampDiff = 58040 - timestampType = 57927 - tinyIntType = 57560 - tinyblobType = 57559 - tinytextType = 57561 - tls = 58041 - to = 57562 - toTimestamp = 57348 - tokenIssuer = 57929 - tokudbDefault = 58042 - tokudbFast = 58043 - tokudbLzma = 58044 - tokudbQuickLZ = 58045 - tokudbSmall = 58047 - tokudbSnappy = 58046 - tokudbUncompressed = 58048 - tokudbZlib = 58049 - tokudbZstd = 58050 - top = 58051 - topn = 58115 - tp = 57930 - tpcc = 57931 - tpch10 = 57801 - trace = 57932 - traditional = 57933 - trailing = 57563 - transaction = 57934 - trigger = 57564 - triggers = 57935 - trim = 58052 - trueCardCost = 58058 - trueKwd = 57565 - truncate = 57936 - ttl = 57937 - ttlEnable = 57938 - ttlJobInterval = 57939 - unbounded = 57940 - uncommitted = 57941 - undefined = 57942 - underscoreCS = 57351 - unicodeSym = 57943 - union = 57567 - unique = 57566 - unknown = 57944 - unlimited = 58076 - unlock = 57568 - unsigned = 57569 - until = 57570 - untilTS = 58053 - update = 57571 - usage = 57572 - use = 57573 - user = 57945 - using = 57574 - utcDate = 57575 - utcTime = 57577 - utcTimestamp = 57576 - validation = 57946 - value = 57947 - values = 57578 - varPop = 58055 - varSamp = 58056 - varbinaryType = 57582 - varcharType = 57580 - varcharacter = 57581 - variables = 57948 - variance = 58054 - varying = 57583 - verboseType = 58057 - view = 57949 - virtual = 57584 - visible = 57950 - voter = 58059 - voterConstraints = 58060 - voters = 58061 - wait = 57958 - warnings = 57951 - watch = 58072 - week = 57952 - weightString = 57953 - when = 57585 - where = 57586 - while = 57587 - width = 58117 - window = 57589 - with = 57590 - without = 57954 - workload = 57955 - write = 57588 - x509 = 57956 - xor = 57591 - yearMonth = 57592 - yearType = 57957 - zerofill = 57593 - - yyMaxDepth = 200 - yyTabOfs = -2854 -) - -var ( - yyXLAT = map[int]int{ - 59: 0, // ';' (2503x) - 57344: 1, // $end (2490x) - 57842: 2, // remove (1990x) - 58116: 3, // split (1990x) - 57771: 4, // merge (1989x) - 57843: 5, // reorganize (1988x) - 57650: 6, // comment (1981x) - 57909: 7, // storage (1893x) - 57612: 8, // autoIncrement (1882x) - 44: 9, // ',' (1856x) - 57713: 10, // first (1781x) - 57598: 11, // after (1775x) - 57876: 12, // serial (1771x) - 57613: 13, // autoRandom (1770x) - 57647: 14, // columnFormat (1770x) - 57813: 15, // password (1742x) - 57638: 16, // charsetKwd (1734x) - 57640: 17, // checksum (1724x) - 58010: 18, // placement (1721x) - 57747: 19, // keyBlockSize (1705x) - 57921: 20, // tablespace (1701x) - 57693: 21, // encryption (1699x) - 57674: 22, // data (1697x) - 57696: 23, // engine (1696x) - 57738: 24, // insertMethod (1692x) - 57765: 25, // maxRows (1692x) - 57773: 26, // minRows (1692x) - 57788: 27, // nodegroup (1692x) - 57657: 28, // connection (1684x) - 57614: 29, // autoRandomBase (1681x) - 58106: 30, // statsBuckets (1679x) - 58108: 31, // statsTopN (1679x) - 57937: 32, // ttl (1679x) - 57611: 33, // autoIdCache (1678x) - 57616: 34, // avgRowLength (1678x) - 57655: 35, // compression (1678x) - 57681: 36, // delayKeyWrite (1678x) - 57807: 37, // packKeys (1678x) - 57822: 38, // preSplitRegions (1678x) - 57863: 39, // rowFormat (1678x) - 57869: 40, // secondaryEngine (1678x) - 57880: 41, // shardRowIDBits (1678x) - 57905: 42, // statsAutoRecalc (1678x) - 57609: 43, // statsColChoice (1678x) - 57610: 44, // statsColList (1678x) - 57906: 45, // statsPersistent (1678x) - 57907: 46, // statsSamplePages (1678x) - 57608: 47, // statsSampleRate (1678x) - 57919: 48, // tableChecksum (1678x) - 57938: 49, // ttlEnable (1678x) - 57939: 50, // ttlJobInterval (1678x) - 57850: 51, // resource (1656x) - 57605: 52, // attribute (1629x) - 57595: 53, // account (1627x) - 57959: 54, // failedLoginAttempts (1627x) - 57960: 55, // passwordLockTime (1627x) - 57346: 56, // identifier (1626x) - 41: 57, // ')' (1622x) - 57855: 58, // resume (1614x) - 57884: 59, // signed (1614x) - 57890: 60, // snapshot (1612x) - 57617: 61, // backend (1611x) - 57639: 62, // checkpoint (1611x) - 57656: 63, // concurrency (1611x) - 57662: 64, // csvBackslashEscape (1611x) - 57663: 65, // csvDelimiter (1611x) - 57664: 66, // csvHeader (1611x) - 57665: 67, // csvNotNull (1611x) - 57666: 68, // csvNull (1611x) - 57667: 69, // csvSeparator (1611x) - 57668: 70, // csvTrimLastSeparators (1611x) - 57990: 71, // fullBackupStorage (1611x) - 57992: 72, // gcTTL (1611x) - 57751: 73, // lastBackup (1611x) - 57802: 74, // onDuplicate (1611x) - 57803: 75, // online (1611x) - 57837: 76, // rateLimit (1611x) - 58018: 77, // restoredTS (1611x) - 57873: 78, // sendCredentialsToTiKV (1611x) - 57887: 79, // skipSchemaFiles (1611x) - 58024: 80, // startTS (1611x) - 57910: 81, // strictFormat (1611x) - 57926: 82, // tikvImporter (1611x) - 58053: 83, // untilTS (1611x) - 57620: 84, // begin (1605x) - 57651: 85, // commit (1605x) - 57785: 86, // no (1605x) - 57859: 87, // rollback (1605x) - 57904: 88, // start (1603x) - 57936: 89, // truncate (1602x) - 57632: 90, // cache (1600x) - 57786: 91, // nocache (1599x) - 57805: 92, // open (1599x) - 57596: 93, // action (1598x) - 57670: 94, // close (1598x) - 57673: 95, // cycle (1598x) - 57775: 96, // minValue (1598x) - 57694: 97, // end (1597x) - 57735: 98, // increment (1597x) - 57787: 99, // nocycle (1597x) - 57789: 100, // nomaxvalue (1597x) - 57790: 101, // nominvalue (1597x) - 57601: 102, // algorithm (1595x) - 57852: 103, // restart (1595x) - 57930: 104, // tp (1595x) - 57672: 105, // clustered (1594x) - 57740: 106, // invisible (1594x) - 57791: 107, // nonclustered (1594x) - 58119: 108, // regions (1594x) - 57950: 109, // visible (1594x) - 58075: 110, // background (1592x) - 57970: 111, // burstable (1592x) - 58063: 112, // priority (1592x) - 58074: 113, // queryLimit (1592x) - 58062: 114, // ruRate (1592x) - 57912: 115, // subpartition (1590x) - 57812: 116, // partitions (1589x) - 58011: 117, // plan (1589x) - 57957: 118, // yearType (1589x) - 57973: 119, // constraints (1587x) - 57988: 120, // followerConstraints (1587x) - 57989: 121, // followers (1587x) - 58001: 122, // leaderConstraints (1587x) - 58003: 123, // learnerConstraints (1587x) - 58004: 124, // learners (1587x) - 58015: 125, // primaryRegion (1587x) - 58021: 126, // schedule (1587x) - 57903: 127, // sqlTsiYear (1587x) - 58035: 128, // survivalPreferences (1587x) - 58060: 129, // voterConstraints (1587x) - 58061: 130, // voters (1587x) - 57648: 131, // columns (1585x) - 57949: 132, // view (1585x) - 57677: 133, // day (1584x) - 58072: 134, // watch (1583x) - 57978: 135, // defined (1582x) - 58069: 136, // execElapsed (1582x) - 57868: 137, // second (1582x) - 57730: 138, // hour (1581x) - 57772: 139, // microsecond (1581x) - 57774: 140, // minute (1581x) - 57778: 141, // month (1581x) - 57833: 142, // quarter (1581x) - 57896: 143, // sqlTsiDay (1581x) - 57897: 144, // sqlTsiHour (1581x) - 57898: 145, // sqlTsiMinute (1581x) - 57899: 146, // sqlTsiMonth (1581x) - 57900: 147, // sqlTsiQuarter (1581x) - 57901: 148, // sqlTsiSecond (1581x) - 57902: 149, // sqlTsiWeek (1581x) - 57952: 150, // week (1581x) - 57604: 151, // ascii (1580x) - 57631: 152, // byteType (1580x) - 57943: 153, // unicodeSym (1580x) - 57711: 154, // fields (1579x) - 57759: 155, // logs (1578x) - 57908: 156, // status (1578x) - 57920: 157, // tables (1578x) - 57981: 158, // timeDuration (1578x) - 57835: 159, // query (1576x) - 57874: 160, // separator (1576x) - 57641: 161, // cipher (1575x) - 57745: 162, // issuer (1575x) - 57763: 163, // maxConnectionsPerHour (1575x) - 57764: 164, // maxQueriesPerHour (1575x) - 57766: 165, // maxUpdatesPerHour (1575x) - 57767: 166, // maxUserConnections (1575x) - 57823: 167, // preceding (1575x) - 57866: 168, // san (1575x) - 57911: 169, // subject (1575x) - 57929: 170, // tokenIssuer (1575x) - 57982: 171, // endTime (1574x) - 57746: 172, // jsonType (1574x) - 57756: 173, // local (1574x) - 58023: 174, // startTime (1574x) - 57675: 175, // datetimeType (1573x) - 57676: 176, // dateType (1573x) - 57714: 177, // fixed (1573x) - 58092: 178, // job (1573x) - 57928: 179, // timeType (1573x) - 57624: 180, // bindings (1572x) - 57680: 181, // definer (1572x) - 57725: 182, // hash (1572x) - 57731: 183, // identified (1572x) - 57851: 184, // respect (1572x) - 57927: 185, // timestampType (1572x) - 57947: 186, // value (1572x) - 57618: 187, // backup (1571x) - 57628: 188, // booleanType (1571x) - 57669: 189, // current (1571x) - 57695: 190, // enforced (1571x) - 57717: 191, // following (1571x) - 57753: 192, // less (1571x) - 57793: 193, // nowait (1571x) - 57804: 194, // only (1571x) - 57867: 195, // savepoint (1571x) - 57886: 196, // skip (1571x) - 58037: 197, // taskTypes (1571x) - 57924: 198, // textType (1571x) - 57925: 199, // than (1571x) - 58114: 200, // tiFlash (1571x) - 57940: 201, // unbounded (1571x) - 57622: 202, // binding (1570x) - 57626: 203, // bitType (1570x) - 57629: 204, // boolType (1570x) - 57698: 205, // enum (1570x) - 57722: 206, // global (1570x) - 57865: 207, // hypo (1570x) - 57733: 208, // importKwd (1570x) - 57780: 209, // national (1570x) - 57781: 210, // ncharType (1570x) - 57994: 211, // next_row_id (1570x) - 57794: 212, // nvarcharType (1570x) - 57797: 213, // offset (1570x) - 57821: 214, // policy (1570x) - 58014: 215, // predicate (1570x) - 57922: 216, // temporary (1570x) - 57945: 217, // user (1570x) - 57682: 218, // digest (1569x) - 58091: 219, // jobs (1569x) - 57758: 220, // location (1569x) - 58012: 221, // planCache (1569x) - 57824: 222, // prepare (1569x) - 57846: 223, // replica (1569x) - 57858: 224, // role (1569x) - 58103: 225, // stats (1569x) - 57944: 226, // unknown (1569x) - 57958: 227, // wait (1569x) - 57630: 228, // btree (1568x) - 58071: 229, // cooldown (1568x) - 57679: 230, // declare (1568x) - 58070: 231, // dryRun (1568x) - 57718: 232, // format (1568x) - 57744: 233, // isolation (1568x) - 57750: 234, // last (1568x) - 57761: 235, // max_idxnum (1568x) - 57770: 236, // memory (1568x) - 57796: 237, // off (1568x) - 57806: 238, // optional (1568x) - 57816: 239, // per_db (1568x) - 57826: 240, // privileges (1568x) - 57849: 241, // required (1568x) - 57864: 242, // rtree (1568x) - 58100: 243, // sampleRate (1568x) - 57875: 244, // sequence (1568x) - 57878: 245, // session (1568x) - 57889: 246, // slow (1568x) - 57946: 247, // validation (1568x) - 57948: 248, // variables (1568x) - 57606: 249, // attributes (1567x) - 58081: 250, // cancel (1567x) - 57653: 251, // compact (1567x) - 58086: 252, // ddl (1567x) - 57684: 253, // disable (1567x) - 57688: 254, // do (1567x) - 57690: 255, // dynamic (1567x) - 57691: 256, // enable (1567x) - 57699: 257, // errorKwd (1567x) - 57983: 258, // exact (1567x) - 57715: 259, // flush (1567x) - 57719: 260, // full (1567x) - 57724: 261, // handler (1567x) - 57728: 262, // history (1567x) - 57768: 263, // mb (1567x) - 57776: 264, // mode (1567x) - 57783: 265, // next (1567x) - 57814: 266, // pause (1567x) - 57819: 267, // plugins (1567x) - 57828: 268, // processlist (1567x) - 57839: 269, // recover (1567x) - 57844: 270, // repair (1567x) - 57845: 271, // repeatable (1567x) - 58073: 272, // similar (1567x) - 58102: 273, // statistics (1567x) - 57913: 274, // subpartitions (1567x) - 58113: 275, // tidb (1567x) - 57954: 276, // without (1567x) - 58077: 277, // admin (1566x) - 58078: 278, // batch (1566x) - 57625: 279, // binlog (1566x) - 57627: 280, // block (1566x) - 57968: 281, // br (1566x) - 57969: 282, // briefType (1566x) - 58079: 283, // buckets (1566x) - 57633: 284, // calibrate (1566x) - 57634: 285, // capture (1566x) - 58082: 286, // cardinality (1566x) - 57637: 287, // chain (1566x) - 57644: 288, // clientErrorsSummary (1566x) - 58083: 289, // cmSketch (1566x) - 57645: 290, // coalesce (1566x) - 57654: 291, // compressed (1566x) - 57660: 292, // context (1566x) - 57972: 293, // copyKwd (1566x) - 58085: 294, // correlation (1566x) - 57661: 295, // cpu (1566x) - 57678: 296, // deallocate (1566x) - 58087: 297, // dependency (1566x) - 57683: 298, // directory (1566x) - 57686: 299, // discard (1566x) - 57687: 300, // disk (1566x) - 57979: 301, // dotType (1566x) - 58089: 302, // drainer (1566x) - 58090: 303, // dry (1566x) - 57689: 304, // duplicate (1566x) - 57704: 305, // exchange (1566x) - 57706: 306, // execute (1566x) - 57707: 307, // expansion (1566x) - 57986: 308, // flashback (1566x) - 57721: 309, // general (1566x) - 57726: 310, // help (1566x) - 58064: 311, // high (1566x) - 57727: 312, // histogram (1566x) - 57729: 313, // hosts (1566x) - 57732: 314, // identSQLErrors (1566x) - 57995: 315, // inplace (1566x) - 57739: 316, // instance (1566x) - 57996: 317, // instant (1566x) - 57743: 318, // ipc (1566x) - 57748: 319, // labels (1566x) - 57757: 320, // locked (1566x) - 58066: 321, // low (1566x) - 58065: 322, // medium (1566x) - 58007: 323, // metadata (1566x) - 57777: 324, // modify (1566x) - 58093: 325, // nodeID (1566x) - 58094: 326, // nodeState (1566x) - 57795: 327, // nulls (1566x) - 57808: 328, // pageSym (1566x) - 58097: 329, // pump (1566x) - 57832: 330, // purge (1566x) - 57838: 331, // rebuild (1566x) - 57840: 332, // redundant (1566x) - 57841: 333, // reload (1566x) - 57853: 334, // restore (1566x) - 57861: 335, // routine (1566x) - 58020: 336, // s3 (1566x) - 58099: 337, // samples (1566x) - 57870: 338, // secondaryLoad (1566x) - 57871: 339, // secondaryUnload (1566x) - 57881: 340, // share (1566x) - 57883: 341, // shutdown (1566x) - 57892: 342, // source (1566x) - 57607: 343, // statsOptions (1566x) - 58029: 344, // stop (1566x) - 57915: 345, // swaps (1566x) - 58038: 346, // tidbJson (1566x) - 58042: 347, // tokudbDefault (1566x) - 58043: 348, // tokudbFast (1566x) - 58044: 349, // tokudbLzma (1566x) - 58045: 350, // tokudbQuickLZ (1566x) - 58047: 351, // tokudbSmall (1566x) - 58046: 352, // tokudbSnappy (1566x) - 58048: 353, // tokudbUncompressed (1566x) - 58049: 354, // tokudbZlib (1566x) - 58050: 355, // tokudbZstd (1566x) - 58115: 356, // topn (1566x) - 57932: 357, // trace (1566x) - 57933: 358, // traditional (1566x) - 58058: 359, // trueCardCost (1566x) - 58076: 360, // unlimited (1566x) - 58057: 361, // verboseType (1566x) - 57951: 362, // warnings (1566x) - 57597: 363, // advise (1565x) - 57599: 364, // against (1565x) - 57600: 365, // ago (1565x) - 57602: 366, // always (1565x) - 57619: 367, // backups (1565x) - 57621: 368, // bernoulli (1565x) - 57623: 369, // bindingCache (1565x) - 58080: 370, // builtins (1565x) - 57635: 371, // cascaded (1565x) - 57636: 372, // causal (1565x) - 57642: 373, // cleanup (1565x) - 57643: 374, // client (1565x) - 57671: 375, // cluster (1565x) - 57646: 376, // collation (1565x) - 58084: 377, // columnStatsUsage (1565x) - 57652: 378, // committed (1565x) - 57649: 379, // config (1565x) - 57658: 380, // consistency (1565x) - 57659: 381, // consistent (1565x) - 58088: 382, // depth (1565x) - 57685: 383, // disabled (1565x) - 57980: 384, // dump (1565x) - 57692: 385, // enabled (1565x) - 57697: 386, // engines (1565x) - 57702: 387, // events (1565x) - 57703: 388, // evolve (1565x) - 57708: 389, // expire (1565x) - 57984: 390, // exprPushdownBlacklist (1565x) - 57709: 391, // extended (1565x) - 57710: 392, // faultsSym (1565x) - 57716: 393, // found (1565x) - 57720: 394, // function (1565x) - 57723: 395, // grants (1565x) - 58110: 396, // histogramsInFlight (1565x) - 57736: 397, // incremental (1565x) - 57737: 398, // indexes (1565x) - 57997: 399, // internal (1565x) - 57741: 400, // invoker (1565x) - 57742: 401, // io (1565x) - 57749: 402, // language (1565x) - 57754: 403, // level (1565x) - 57755: 404, // list (1565x) - 57760: 405, // master (1565x) - 57762: 406, // max_minutes (1565x) - 57782: 407, // never (1565x) - 57784: 408, // nextval (1565x) - 57792: 409, // none (1565x) - 57798: 410, // oltpReadOnly (1565x) - 57799: 411, // oltpReadWrite (1565x) - 57800: 412, // oltpWriteOnly (1565x) - 58095: 413, // optimistic (1565x) - 58009: 414, // optRuleBlacklist (1565x) - 57809: 415, // parser (1565x) - 57810: 416, // partial (1565x) - 57811: 417, // partitioning (1565x) - 57817: 418, // per_table (1565x) - 57815: 419, // percent (1565x) - 58096: 420, // pessimistic (1565x) - 57820: 421, // point (1565x) - 57825: 422, // preserve (1565x) - 57829: 423, // profile (1565x) - 57830: 424, // profiles (1565x) - 57834: 425, // queries (1565x) - 58016: 426, // recent (1565x) - 58120: 427, // region (1565x) - 58017: 428, // replayer (1565x) - 58118: 429, // reset (1565x) - 57854: 430, // restores (1565x) - 57856: 431, // reuse (1565x) - 57860: 432, // rollup (1565x) - 58098: 433, // run (1565x) - 57872: 434, // security (1565x) - 57877: 435, // serializable (1565x) - 58101: 436, // sessionStates (1565x) - 57885: 437, // simple (1565x) - 57888: 438, // slave (1565x) - 58107: 439, // statsHealthy (1565x) - 58105: 440, // statsHistograms (1565x) - 58109: 441, // statsLocked (1565x) - 58104: 442, // statsMeta (1565x) - 57916: 443, // switchesSym (1565x) - 57917: 444, // system (1565x) - 57918: 445, // systemTime (1565x) - 58036: 446, // target (1565x) - 58112: 447, // telemetryID (1565x) - 57923: 448, // temptable (1565x) - 58041: 449, // tls (1565x) - 58051: 450, // top (1565x) - 57931: 451, // tpcc (1565x) - 57801: 452, // tpch10 (1565x) - 57934: 453, // transaction (1565x) - 57935: 454, // triggers (1565x) - 57941: 455, // uncommitted (1565x) - 57942: 456, // undefined (1565x) - 58117: 457, // width (1565x) - 57955: 458, // workload (1565x) - 57956: 459, // x509 (1565x) - 57961: 460, // addDate (1564x) - 57603: 461, // any (1564x) - 57962: 462, // approxCountDistinct (1564x) - 57963: 463, // approxPercentile (1564x) - 57615: 464, // avg (1564x) - 57964: 465, // bitAnd (1564x) - 57965: 466, // bitOr (1564x) - 57966: 467, // bitXor (1564x) - 57967: 468, // bound (1564x) - 57971: 469, // cast (1564x) - 57975: 470, // curDate (1564x) - 57974: 471, // curTime (1564x) - 57976: 472, // dateAdd (1564x) - 57977: 473, // dateSub (1564x) - 57700: 474, // escape (1564x) - 57701: 475, // event (1564x) - 57705: 476, // exclusive (1564x) - 57985: 477, // extract (1564x) - 57712: 478, // file (1564x) - 57987: 479, // follower (1564x) - 57991: 480, // getFormat (1564x) - 57993: 481, // groupConcat (1564x) - 57734: 482, // imports (1564x) - 58067: 483, // ioReadBandwidth (1564x) - 58068: 484, // ioWriteBandwidth (1564x) - 57998: 485, // jsonArrayagg (1564x) - 57999: 486, // jsonObjectAgg (1564x) - 57752: 487, // lastval (1564x) - 58000: 488, // leader (1564x) - 58002: 489, // learner (1564x) - 58006: 490, // max (1564x) - 57769: 491, // member (1564x) - 58005: 492, // min (1564x) - 57779: 493, // names (1564x) - 58008: 494, // now (1564x) - 58013: 495, // position (1564x) - 57827: 496, // process (1564x) - 57831: 497, // proxy (1564x) - 57836: 498, // quick (1564x) - 57847: 499, // replicas (1564x) - 57848: 500, // replication (1564x) - 57857: 501, // reverse (1564x) - 57862: 502, // rowCount (1564x) - 58019: 503, // running (1564x) - 57879: 504, // setval (1564x) - 57882: 505, // shared (1564x) - 57891: 506, // some (1564x) - 57893: 507, // sqlBufferResult (1564x) - 57894: 508, // sqlCache (1564x) - 57895: 509, // sqlNoCache (1564x) - 58022: 510, // staleness (1564x) - 58025: 511, // std (1564x) - 58026: 512, // stddev (1564x) - 58027: 513, // stddevPop (1564x) - 58028: 514, // stddevSamp (1564x) - 58030: 515, // strict (1564x) - 58031: 516, // strong (1564x) - 58032: 517, // subDate (1564x) - 58034: 518, // substring (1564x) - 58033: 519, // sum (1564x) - 57914: 520, // super (1564x) - 58111: 521, // telemetry (1564x) - 58039: 522, // timestampAdd (1564x) - 58040: 523, // timestampDiff (1564x) - 58052: 524, // trim (1564x) - 58054: 525, // variance (1564x) - 58055: 526, // varPop (1564x) - 58056: 527, // varSamp (1564x) - 58059: 528, // voter (1564x) - 57953: 529, // weightString (1564x) - 57503: 530, // on (1474x) - 40: 531, // '(' (1469x) - 57590: 532, // with (1340x) - 57352: 533, // stringLit (1327x) - 58166: 534, // not2 (1278x) - 57404: 535, // defaultKwd (1230x) - 57496: 536, // not (1209x) - 57368: 537, // as (1176x) - 57383: 538, // collate (1144x) - 57567: 539, // union (1134x) - 57474: 540, // left (1131x) - 57531: 541, // right (1131x) - 57574: 542, // using (1120x) - 43: 543, // '+' (1107x) - 45: 544, // '-' (1105x) - 57495: 545, // mod (1085x) - 57512: 546, // partition (1062x) - 57578: 547, // values (1041x) - 57500: 548, // null (1039x) - 57445: 549, // ignore (1029x) - 57423: 550, // except (1023x) - 57452: 551, // intersect (1022x) - 57527: 552, // replace (1016x) - 57381: 553, // charType (1012x) - 57425: 554, // fetch (1005x) - 58155: 555, // eq (996x) - 57477: 556, // limit (996x) - 57538: 557, // set (996x) - 57430: 558, // forKwd (994x) - 57454: 559, // into (988x) - 58150: 560, // intLit (987x) - 57433: 561, // from (986x) - 57483: 562, // lock (981x) - 57586: 563, // where (973x) - 57508: 564, // order (968x) - 57431: 565, // force (963x) - 57366: 566, // and (960x) - 57507: 567, // or (936x) - 57357: 568, // andand (935x) - 57818: 569, // pipesAsOr (935x) - 57591: 570, // xor (935x) - 57437: 571, // group (906x) - 57439: 572, // having (901x) - 57552: 573, // straightJoin (893x) - 57589: 574, // window (887x) - 57573: 575, // use (885x) - 57465: 576, // join (881x) - 57408: 577, // desc (876x) - 57444: 578, // ifKwd (873x) - 57475: 579, // like (871x) - 57594: 580, // natural (871x) - 57389: 581, // cross (870x) - 57422: 582, // explain (870x) - 57449: 583, // inner (870x) - 42: 584, // '*' (868x) - 125: 585, // '}' (867x) - 57372: 586, // binaryType (864x) - 57457: 587, // insert (861x) - 57534: 588, // rows (855x) - 57585: 589, // when (849x) - 57417: 590, // elseKwd (845x) - 57517: 591, // rangeKwd (845x) - 57555: 592, // tableSample (845x) - 57438: 593, // groups (843x) - 57399: 594, // dayHour (842x) - 57400: 595, // dayMicrosecond (842x) - 57401: 596, // dayMinute (842x) - 57402: 597, // daySecond (842x) - 57441: 598, // hourMicrosecond (842x) - 57442: 599, // hourMinute (842x) - 57443: 600, // hourSecond (842x) - 57493: 601, // minuteMicrosecond (842x) - 57494: 602, // minuteSecond (842x) - 57536: 603, // secondMicrosecond (842x) - 57592: 604, // yearMonth (842x) - 57369: 605, // asc (840x) - 57446: 606, // in (834x) - 57558: 607, // then (834x) - 57554: 608, // tableKwd (827x) - 47: 609, // '/' (826x) - 37: 610, // '%' (825x) - 38: 611, // '&' (825x) - 94: 612, // '^' (825x) - 124: 613, // '|' (825x) - 57378: 614, // caseKwd (825x) - 57412: 615, // div (825x) - 58160: 616, // lsh (825x) - 57526: 617, // repeat (825x) - 58165: 618, // rsh (825x) - 60: 619, // '<' (824x) - 62: 620, // '>' (824x) - 58156: 621, // ge (824x) - 57456: 622, // is (824x) - 58157: 623, // le (824x) - 58161: 624, // neq (824x) - 58162: 625, // neqSynonym (824x) - 58163: 626, // nulleq (824x) - 57370: 627, // between (819x) - 57353: 628, // singleAtIdentifier (818x) - 57424: 629, // falseKwd (814x) - 57565: 630, // trueKwd (814x) - 57394: 631, // currentUser (813x) - 57476: 632, // ilike (811x) - 57523: 633, // regexpKwd (811x) - 57532: 634, // rlike (811x) - 57349: 635, // memberof (808x) - 58149: 636, // decLit (806x) - 58148: 637, // floatLit (806x) - 58151: 638, // hexLit (806x) - 57533: 639, // row (805x) - 58152: 640, // bitLit (804x) - 57453: 641, // interval (804x) - 58164: 642, // paramMarker (803x) - 123: 643, // '{' (801x) - 57397: 644, // database (797x) - 57420: 645, // exists (796x) - 57387: 646, // convert (793x) - 57351: 647, // underscoreCS (793x) - 58128: 648, // builtinCurDate (792x) - 58136: 649, // builtinNow (792x) - 57391: 650, // currentDate (792x) - 57393: 651, // currentTs (792x) - 57354: 652, // doubleAtIdentifier (792x) - 57481: 653, // localTime (792x) - 57482: 654, // localTs (792x) - 58125: 655, // builtinCount (790x) - 33: 656, // '!' (789x) - 126: 657, // '~' (789x) - 58126: 658, // builtinApproxCountDistinct (789x) - 58127: 659, // builtinApproxPercentile (789x) - 58121: 660, // builtinBitAnd (789x) - 58122: 661, // builtinBitOr (789x) - 58123: 662, // builtinBitXor (789x) - 58124: 663, // builtinCast (789x) - 58129: 664, // builtinCurTime (789x) - 58130: 665, // builtinDateAdd (789x) - 58131: 666, // builtinDateSub (789x) - 58132: 667, // builtinExtract (789x) - 58133: 668, // builtinGroupConcat (789x) - 58134: 669, // builtinMax (789x) - 58135: 670, // builtinMin (789x) - 58137: 671, // builtinPosition (789x) - 58141: 672, // builtinStddevPop (789x) - 58142: 673, // builtinStddevSamp (789x) - 58138: 674, // builtinSubstring (789x) - 58139: 675, // builtinSum (789x) - 58140: 676, // builtinSysDate (789x) - 58143: 677, // builtinTranslate (789x) - 58144: 678, // builtinTrim (789x) - 58145: 679, // builtinUser (789x) - 58146: 680, // builtinVarPop (789x) - 58147: 681, // builtinVarSamp (789x) - 57390: 682, // cumeDist (789x) - 57395: 683, // currentRole (789x) - 57392: 684, // currentTime (789x) - 57407: 685, // denseRank (789x) - 57426: 686, // firstValue (789x) - 57469: 687, // lag (789x) - 57470: 688, // lastValue (789x) - 57471: 689, // lead (789x) - 57498: 690, // nthValue (789x) - 57499: 691, // ntile (789x) - 57513: 692, // percentRank (789x) - 57518: 693, // rank (789x) - 57535: 694, // rowNumber (789x) - 57537: 695, // selectKwd (789x) - 57542: 696, // sql (789x) - 57553: 697, // tidbCurrentTSO (789x) - 57575: 698, // utcDate (789x) - 57577: 699, // utcTime (789x) - 57576: 700, // utcTimestamp (789x) - 57466: 701, // key (783x) - 57382: 702, // check (773x) - 57358: 703, // pipes (773x) - 57515: 704, // primary (773x) - 57566: 705, // unique (766x) - 57385: 706, // constraint (763x) - 57522: 707, // references (761x) - 57435: 708, // generated (757x) - 57380: 709, // character (753x) - 57447: 710, // index (737x) - 57487: 711, // match (723x) - 57562: 712, // to (632x) - 57365: 713, // analyze (626x) - 57571: 714, // update (621x) - 57363: 715, // all (610x) - 46: 716, // '.' (609x) - 58154: 717, // assignmentEq (575x) - 58158: 718, // jss (574x) - 58159: 719, // juss (574x) - 57488: 720, // maxValue (574x) - 57367: 721, // array (571x) - 57478: 722, // lines (567x) - 57375: 723, // by (559x) - 57364: 724, // alter (557x) - 57528: 725, // require (554x) - 64: 726, // '@' (549x) - 57414: 727, // drop (543x) - 57377: 728, // cascade (542x) - 57519: 729, // read (542x) - 57529: 730, // restrict (542x) - 57347: 731, // asof (541x) - 57581: 732, // varcharacter (541x) - 57580: 733, // varcharType (541x) - 57403: 734, // decimalType (540x) - 57413: 735, // doubleType (540x) - 57427: 736, // floatType (540x) - 57451: 737, // integerType (540x) - 57458: 738, // intType (540x) - 57520: 739, // realType (540x) - 57582: 740, // varbinaryType (539x) - 57371: 741, // bigIntType (538x) - 57373: 742, // blobType (538x) - 57388: 743, // create (538x) - 57428: 744, // float4Type (538x) - 57429: 745, // float8Type (538x) - 57432: 746, // foreign (538x) - 57434: 747, // fulltext (538x) - 57459: 748, // int1Type (538x) - 57460: 749, // int2Type (538x) - 57461: 750, // int3Type (538x) - 57462: 751, // int4Type (538x) - 57463: 752, // int8Type (538x) - 57579: 753, // long (538x) - 57484: 754, // longblobType (538x) - 57485: 755, // longtextType (538x) - 57489: 756, // mediumblobType (538x) - 57490: 757, // mediumIntType (538x) - 57491: 758, // mediumtextType (538x) - 57492: 759, // middleIntType (538x) - 57501: 760, // numericType (538x) - 57540: 761, // smallIntType (538x) - 57559: 762, // tinyblobType (538x) - 57560: 763, // tinyIntType (538x) - 57561: 764, // tinytextType (538x) - 57348: 765, // toTimestamp (537x) - 57379: 766, // change (535x) - 57525: 767, // rename (535x) - 57588: 768, // write (535x) - 57362: 769, // add (534x) - 58439: 770, // Identifier (534x) - 58520: 771, // NotKeywordToken (534x) - 58798: 772, // TiDBKeyword (534x) - 58808: 773, // UnReservedKeyword (534x) - 57504: 774, // optimize (533x) - 58763: 775, // SubSelect (259x) - 58818: 776, // UserVariable (200x) - 58491: 777, // Literal (198x) - 58734: 778, // SimpleIdent (198x) - 58753: 779, // StringLiteral (198x) - 58517: 780, // NextValueForSequence (195x) - 58416: 781, // FunctionCallGeneric (194x) - 58417: 782, // FunctionCallKeyword (194x) - 58418: 783, // FunctionCallNonKeyword (194x) - 58419: 784, // FunctionNameConflict (194x) - 58420: 785, // FunctionNameDateArith (194x) - 58421: 786, // FunctionNameDateArithMultiForms (194x) - 58422: 787, // FunctionNameDatetimePrecision (194x) - 58423: 788, // FunctionNameOptionalBraces (194x) - 58424: 789, // FunctionNameSequence (194x) - 58733: 790, // SimpleExpr (194x) - 58764: 791, // SumExpr (194x) - 58766: 792, // SystemVariable (194x) - 58829: 793, // Variable (194x) - 58853: 794, // WindowFuncCall (194x) - 58247: 795, // BitExpr (176x) - 58595: 796, // PredicateExpr (144x) - 58250: 797, // BoolPri (141x) - 58379: 798, // Expression (141x) - 58515: 799, // NUM (122x) - 58869: 800, // logAnd (107x) - 58870: 801, // logOr (107x) - 58370: 802, // EqOpt (98x) - 57406: 803, // deleteKwd (86x) - 58776: 804, // TableName (81x) - 58754: 805, // StringName (56x) - 58688: 806, // SelectStmt (52x) - 58689: 807, // SelectStmtBasic (52x) - 58691: 808, // SelectStmtFromDualTable (52x) - 58692: 809, // SelectStmtFromTable (52x) - 58709: 810, // SetOprClause (52x) - 58710: 811, // SetOprClauseList (51x) - 58713: 812, // SetOprStmtWithLimitOrderBy (51x) - 58714: 813, // SetOprStmtWoutLimitOrderBy (51x) - 57569: 814, // unsigned (50x) - 58859: 815, // WithClause (49x) - 58482: 816, // LengthNum (48x) - 58701: 817, // SelectStmtWithClause (48x) - 58712: 818, // SetOprStmt (48x) - 57593: 819, // zerofill (48x) - 57511: 820, // over (45x) - 58276: 821, // ColumnName (41x) - 58812: 822, // UpdateStmtNoWith (41x) - 58336: 823, // DeleteWithoutUsingStmt (40x) - 58467: 824, // InsertIntoStmt (38x) - 58470: 825, // Int64Num (38x) - 58652: 826, // ReplaceIntoStmt (38x) - 58811: 827, // UpdateStmt (38x) - 57409: 828, // describe (36x) - 57410: 829, // distinct (36x) - 57411: 830, // distinctRow (36x) - 57587: 831, // while (36x) - 58858: 832, // WindowingClause (35x) - 58335: 833, // DeleteWithUsingStmt (34x) - 57464: 834, // iterate (34x) - 57473: 835, // leave (34x) - 57405: 836, // delayed (33x) - 57440: 837, // highPriority (33x) - 57486: 838, // lowPriority (33x) - 58334: 839, // DeleteFromStmt (32x) - 57356: 840, // hintComment (27x) - 58390: 841, // FieldLen (25x) - 58565: 842, // OrderBy (25x) - 58695: 843, // SelectStmtLimit (25x) - 58559: 844, // OptWindowingClause (24x) - 58220: 845, // AnalyzeTableStmt (23x) - 58290: 846, // CommitStmt (23x) - 58679: 847, // RollbackStmt (23x) - 58717: 848, // SetStmt (23x) - 57543: 849, // sqlBigResult (23x) - 57544: 850, // sqlCalcFoundRows (23x) - 57545: 851, // sqlSmallResult (23x) - 57557: 852, // terminated (21x) - 58265: 853, // CharsetKw (20x) - 58440: 854, // IfExists (20x) - 58820: 855, // Username (20x) - 57418: 856, // enclosed (19x) - 58375: 857, // ExplainStmt (19x) - 58376: 858, // ExplainSym (19x) - 58577: 859, // PartitionNameList (19x) - 58806: 860, // TruncateTableStmt (19x) - 58813: 861, // UseStmt (19x) - 57419: 862, // escaped (18x) - 58380: 863, // ExpressionList (18x) - 57350: 864, // optionallyEnclosedBy (18x) - 58589: 865, // PlacementPolicyOption (18x) - 58606: 866, // ProcedureBlockContent (18x) - 58635: 867, // ProcedureUnlabelLoopStmt (18x) - 58608: 868, // ProcedureCaseStmt (17x) - 58609: 869, // ProcedureCloseCur (17x) - 58615: 870, // ProcedureFetchInto (17x) - 58621: 871, // ProcedureIfstmt (17x) - 58622: 872, // ProcedureIterate (17x) - 58623: 873, // ProcedureLabeledBlock (17x) - 58637: 874, // ProcedurelabeledLoopStmt (17x) - 58624: 875, // ProcedureLeave (17x) - 58625: 876, // ProcedureOpenCur (17x) - 58628: 877, // ProcedureProcStmt (17x) - 58631: 878, // ProcedureSearchedCase (17x) - 58632: 879, // ProcedureSimpleCase (17x) - 58633: 880, // ProcedureStatementStmt (17x) - 58636: 881, // ProcedureUnlabeledBlock (17x) - 58634: 882, // ProcedureUnlabelLoopBlock (17x) - 58441: 883, // IfNotExists (16x) - 58777: 884, // TableNameList (16x) - 58341: 885, // DistinctKwd (15x) - 58800: 886, // TimestampUnit (15x) - 58342: 887, // DistinctOpt (14x) - 58543: 888, // OptFieldLen (14x) - 58843: 889, // WhereClause (14x) - 58844: 890, // WhereClauseOptional (14x) - 58329: 891, // DefaultKwdOpt (13x) - 58371: 892, // EqOrAssignmentEq (13x) - 58378: 893, // ExprOrDefault (13x) - 57480: 894, // load (13x) - 58476: 895, // JoinTable (12x) - 58538: 896, // OptBinary (12x) - 57524: 897, // release (12x) - 58676: 898, // RolenameComposed (12x) - 58773: 899, // TableFactor (12x) - 58786: 900, // TableRef (12x) - 58799: 901, // TimeUnit (12x) - 58219: 902, // AnalyzeOptionListOpt (11x) - 58411: 903, // FromOrIn (11x) - 58215: 904, // AlterTableStmt (10x) - 58266: 905, // CharsetName (10x) - 58277: 906, // ColumnNameList (10x) - 58319: 907, // DBName (10x) - 57497: 908, // noWriteToBinLog (10x) - 58566: 909, // OrderByOptional (10x) - 58568: 910, // PartDefOption (10x) - 58732: 911, // SignedNum (10x) - 58253: 912, // BuggyDefaultFalseDistinctOpt (9x) - 58328: 913, // DefaultFalseDistinctOpt (9x) - 58477: 914, // JoinType (9x) - 58521: 915, // NotSym (9x) - 58528: 916, // NumLiteral (9x) - 58675: 917, // Rolename (9x) - 58670: 918, // RoleNameString (9x) - 58317: 919, // CrossOpt (8x) - 58377: 920, // ExplainableStmt (8x) - 58381: 921, // ExpressionListOpt (8x) - 58461: 922, // IndexPartSpecification (8x) - 58478: 923, // KeyOrIndex (8x) - 58518: 924, // NoWriteToBinLogAliasOpt (8x) - 58696: 925, // SelectStmtLimitOpt (8x) - 58832: 926, // VariableName (8x) - 58200: 927, // AllOrPartitionNameList (7x) - 58300: 928, // ConstraintKeywordOpt (7x) - 58324: 929, // DatabaseSym (7x) - 58396: 930, // FieldsOrColumns (7x) - 58408: 931, // ForceOpt (7x) - 58462: 932, // IndexPartSpecificationList (7x) - 57468: 933, // kill (7x) - 58599: 934, // Priority (7x) - 58629: 935, // ProcedureProcStmt1s (7x) - 58658: 936, // ResourceGroupName (7x) - 58680: 937, // RowFormat (7x) - 58683: 938, // RowValue (7x) - 58707: 939, // SetExpr (7x) - 58719: 940, // ShowDatabaseNameOpt (7x) - 58783: 941, // TableOption (7x) - 57583: 942, // varying (7x) - 58242: 943, // BeginTransactionStmt (6x) - 58244: 944, // BindableStmt (6x) - 58234: 945, // BRIEBooleanOptionName (6x) - 58235: 946, // BRIEIntegerOptionName (6x) - 58236: 947, // BRIEKeywordOptionName (6x) - 58237: 948, // BRIEOption (6x) - 58238: 949, // BRIEOptions (6x) - 58240: 950, // BRIEStringOptionName (6x) - 58264: 951, // Char (6x) - 57384: 952, // column (6x) - 58271: 953, // ColumnDef (6x) - 58321: 954, // DatabaseOption (6x) - 58372: 955, // EscapedTableRef (6x) - 58394: 956, // FieldTerminator (6x) - 57436: 957, // grant (6x) - 58443: 958, // IgnoreOptional (6x) - 58453: 959, // IndexInvisible (6x) - 58458: 960, // IndexNameList (6x) - 58464: 961, // IndexType (6x) - 58498: 962, // LoadDataStmt (6x) - 58578: 963, // PartitionNameListOpt (6x) - 57516: 964, // procedure (6x) - 58647: 965, // ReleaseSavepointStmt (6x) - 58677: 966, // RolenameList (6x) - 58684: 967, // SavepointStmt (6x) - 57539: 968, // show (6x) - 58781: 969, // TableOptimizerHints (6x) - 58821: 970, // UsernameList (6x) - 58860: 971, // WithClustered (6x) - 58198: 972, // AlgorithmClause (5x) - 58255: 973, // ByItem (5x) - 58270: 974, // CollationName (5x) - 58274: 975, // ColumnKeywordOpt (5x) - 58337: 976, // DirectPlacementOption (5x) - 58339: 977, // DirectResourceGroupOption (5x) - 58392: 978, // FieldOpt (5x) - 58393: 979, // FieldOpts (5x) - 58437: 980, // IdentList (5x) - 58456: 981, // IndexName (5x) - 58459: 982, // IndexOption (5x) - 58460: 983, // IndexOptionList (5x) - 57448: 984, // infile (5x) - 58487: 985, // LimitOption (5x) - 58502: 986, // LockClause (5x) - 58540: 987, // OptCharsetWithOptBinary (5x) - 58550: 988, // OptNullTreatment (5x) - 58593: 989, // PolicyName (5x) - 58600: 990, // PriorityOpt (5x) - 58687: 991, // SelectLockOpt (5x) - 58694: 992, // SelectStmtIntoOption (5x) - 58787: 993, // TableRefs (5x) - 58814: 994, // UserSpec (5x) - 58223: 995, // AsOfClause (4x) - 58226: 996, // Assignment (4x) - 58232: 997, // AuthString (4x) - 58251: 998, // Boolean (4x) - 58254: 999, // BuiltinFunction (4x) - 58256: 1000, // ByList (4x) - 58294: 1001, // ConfigItemName (4x) - 58298: 1002, // Constraint (4x) - 58404: 1003, // FloatOpt (4x) - 58465: 1004, // IndexTypeName (4x) - 58527: 1005, // NumList (4x) - 57505: 1006, // option (4x) - 57506: 1007, // optionally (4x) - 58556: 1008, // OptWild (4x) - 57510: 1009, // outer (4x) - 58594: 1010, // Precision (4x) - 58643: 1011, // ReferDef (4x) - 58666: 1012, // RestrictOrCascadeOpt (4x) - 58682: 1013, // RowStmt (4x) - 58702: 1014, // SequenceOption (4x) - 57551: 1015, // statsExtended (4x) - 58768: 1016, // TableAsName (4x) - 58769: 1017, // TableAsNameOpt (4x) - 58780: 1018, // TableNameOptWild (4x) - 58782: 1019, // TableOptimizerHintsOpt (4x) - 58784: 1020, // TableOptionList (4x) - 58795: 1021, // TextString (4x) - 58802: 1022, // TraceableStmt (4x) - 58803: 1023, // TransactionChar (4x) - 58815: 1024, // UserSpecList (4x) - 58828: 1025, // Varchar (4x) - 58854: 1026, // WindowName (4x) - 58227: 1027, // AssignmentList (3x) - 58229: 1028, // AttributesOpt (3x) - 58248: 1029, // BitValueType (3x) - 58249: 1030, // BlobType (3x) - 58252: 1031, // BooleanType (3x) - 58283: 1032, // ColumnOption (3x) - 58286: 1033, // ColumnPosition (3x) - 58291: 1034, // CommonTableExpr (3x) - 58313: 1035, // CreateTableStmt (3x) - 58318: 1036, // CurdateSym (3x) - 58322: 1037, // DatabaseOptionList (3x) - 58325: 1038, // DateAndTimeType (3x) - 58332: 1039, // DefaultTrueDistinctOpt (3x) - 58338: 1040, // DirectResourceGroupBackgroundOption (3x) - 58340: 1041, // DirectResourceGroupRunawayOption (3x) - 58362: 1042, // DynamicCalibrateResourceOption (3x) - 57416: 1043, // elseIfKwd (3x) - 58367: 1044, // EnforcedOrNot (3x) - 58383: 1045, // ExtendedPriv (3x) - 58399: 1046, // FixedPointType (3x) - 58405: 1047, // FloatingPointType (3x) - 58425: 1048, // GeneratedAlways (3x) - 58427: 1049, // GlobalScope (3x) - 58431: 1050, // GroupByClause (3x) - 58448: 1051, // IndexHint (3x) - 58452: 1052, // IndexHintType (3x) - 58457: 1053, // IndexNameAndTypeOpt (3x) - 58471: 1054, // IntegerType (3x) - 57467: 1055, // keys (3x) - 58489: 1056, // Lines (3x) - 58501: 1057, // LocationLabelList (3x) - 58514: 1058, // NChar (3x) - 58522: 1059, // NowSym (3x) - 58523: 1060, // NowSymFunc (3x) - 58524: 1061, // NowSymOptionFraction (3x) - 58529: 1062, // NumericType (3x) - 58516: 1063, // NVarchar (3x) - 58551: 1064, // OptOrder (3x) - 58555: 1065, // OptTemporary (3x) - 58569: 1066, // PartDefOptionList (3x) - 58571: 1067, // PartitionDefinition (3x) - 58582: 1068, // PasswordOrLockOption (3x) - 58592: 1069, // PluginNameList (3x) - 58598: 1070, // PrimaryOpt (3x) - 58601: 1071, // PrivElem (3x) - 58603: 1072, // PrivType (3x) - 58638: 1073, // QueryWatchOption (3x) - 58640: 1074, // QueryWatchTextOption (3x) - 58653: 1075, // RequireClause (3x) - 58654: 1076, // RequireClauseOpt (3x) - 58656: 1077, // RequireListElement (3x) - 58678: 1078, // RolenameWithoutIdent (3x) - 58671: 1079, // RoleOrPrivElem (3x) - 58693: 1080, // SelectStmtGroup (3x) - 58711: 1081, // SetOprOpt (3x) - 58731: 1082, // SignedLiteral (3x) - 58756: 1083, // StringType (3x) - 58767: 1084, // TableAliasRefList (3x) - 58770: 1085, // TableElement (3x) - 58797: 1086, // TextType (3x) - 58804: 1087, // TransactionChars (3x) - 57564: 1088, // trigger (3x) - 58807: 1089, // Type (3x) - 57568: 1090, // unlock (3x) - 57570: 1091, // until (3x) - 57572: 1092, // usage (3x) - 58825: 1093, // ValuesList (3x) - 58827: 1094, // ValuesStmtList (3x) - 58823: 1095, // ValueSym (3x) - 58830: 1096, // VariableAssignment (3x) - 58851: 1097, // WindowFrameStart (3x) - 58868: 1098, // Year (3x) - 58194: 1099, // AddQueryWatchStmt (2x) - 58196: 1100, // AdminStmt (2x) - 58199: 1101, // AllColumnsOrPredicateColumnsOpt (2x) - 58201: 1102, // AlterDatabaseStmt (2x) - 58202: 1103, // AlterInstanceStmt (2x) - 58203: 1104, // AlterOrderItem (2x) - 58205: 1105, // AlterPolicyStmt (2x) - 58206: 1106, // AlterRangeStmt (2x) - 58207: 1107, // AlterResourceGroupStmt (2x) - 58208: 1108, // AlterSequenceOption (2x) - 58210: 1109, // AlterSequenceStmt (2x) - 58211: 1110, // AlterTableSpec (2x) - 58216: 1111, // AlterUserStmt (2x) - 58217: 1112, // AnalyzeOption (2x) - 58246: 1113, // BinlogStmt (2x) - 58239: 1114, // BRIEStmt (2x) - 58241: 1115, // BRIETables (2x) - 58258: 1116, // CalibrateResourceStmt (2x) - 57376: 1117, // call (2x) - 58260: 1118, // CallStmt (2x) - 58261: 1119, // CancelImportStmt (2x) - 58262: 1120, // CastType (2x) - 58263: 1121, // ChangeStmt (2x) - 58269: 1122, // CheckConstraintKeyword (2x) - 58278: 1123, // ColumnNameListOpt (2x) - 58281: 1124, // ColumnNameOrUserVariable (2x) - 58280: 1125, // ColumnNameOrUserVarListOptWithBrackets (2x) - 58284: 1126, // ColumnOptionList (2x) - 58285: 1127, // ColumnOptionListOpt (2x) - 58289: 1128, // CommentOrAttributeOption (2x) - 58293: 1129, // CompletionTypeWithinTransaction (2x) - 58295: 1130, // ConnectionOption (2x) - 58297: 1131, // ConnectionOptions (2x) - 58301: 1132, // CreateBindingStmt (2x) - 58302: 1133, // CreateDatabaseStmt (2x) - 58303: 1134, // CreateIndexStmt (2x) - 58304: 1135, // CreatePolicyStmt (2x) - 58305: 1136, // CreateProcedureStmt (2x) - 58306: 1137, // CreateResourceGroupStmt (2x) - 58307: 1138, // CreateRoleStmt (2x) - 58309: 1139, // CreateSequenceStmt (2x) - 58310: 1140, // CreateStatisticsStmt (2x) - 58311: 1141, // CreateTableOptionListOpt (2x) - 58314: 1142, // CreateUserStmt (2x) - 58316: 1143, // CreateViewStmt (2x) - 57398: 1144, // databases (2x) - 58326: 1145, // DeallocateStmt (2x) - 58327: 1146, // DeallocateSym (2x) - 58330: 1147, // DefaultOrExpression (2x) - 58343: 1148, // DoStmt (2x) - 58344: 1149, // DropBindingStmt (2x) - 58345: 1150, // DropDatabaseStmt (2x) - 58346: 1151, // DropIndexStmt (2x) - 58347: 1152, // DropLoadDataStmt (2x) - 58348: 1153, // DropPolicyStmt (2x) - 58349: 1154, // DropProcedureStmt (2x) - 58350: 1155, // DropQueryWatchStmt (2x) - 58351: 1156, // DropResourceGroupStmt (2x) - 58352: 1157, // DropRoleStmt (2x) - 58353: 1158, // DropSequenceStmt (2x) - 58354: 1159, // DropStatisticsStmt (2x) - 58355: 1160, // DropStatsStmt (2x) - 58356: 1161, // DropTableStmt (2x) - 58357: 1162, // DropUserStmt (2x) - 58358: 1163, // DropViewStmt (2x) - 58360: 1164, // DuplicateOpt (2x) - 58363: 1165, // ElseCaseOpt (2x) - 58365: 1166, // EmptyStmt (2x) - 58366: 1167, // EncryptionOpt (2x) - 58368: 1168, // EnforcedOrNotOpt (2x) - 58373: 1169, // ExecuteStmt (2x) - 58374: 1170, // ExplainFormatType (2x) - 58385: 1171, // Field (2x) - 58388: 1172, // FieldItem (2x) - 58395: 1173, // Fields (2x) - 58400: 1174, // FlashbackDatabaseStmt (2x) - 58401: 1175, // FlashbackTableStmt (2x) - 58402: 1176, // FlashbackToNewName (2x) - 58403: 1177, // FlashbackToTimestampStmt (2x) - 58407: 1178, // FlushStmt (2x) - 58409: 1179, // FormatOpt (2x) - 58414: 1180, // FuncDatetimePrecList (2x) - 58415: 1181, // FuncDatetimePrecListOpt (2x) - 58428: 1182, // GrantProxyStmt (2x) - 58429: 1183, // GrantRoleStmt (2x) - 58430: 1184, // GrantStmt (2x) - 58432: 1185, // HandleRange (2x) - 58434: 1186, // HashString (2x) - 58435: 1187, // HavingClause (2x) - 58436: 1188, // HelpStmt (2x) - 58445: 1189, // ImportIntoStmt (2x) - 58447: 1190, // IndexAdviseStmt (2x) - 58449: 1191, // IndexHintList (2x) - 58450: 1192, // IndexHintListOpt (2x) - 58455: 1193, // IndexLockAndAlgorithmOpt (2x) - 57450: 1194, // inout (2x) - 58468: 1195, // InsertValues (2x) - 58473: 1196, // IntoOpt (2x) - 58479: 1197, // KeyOrIndexOpt (2x) - 58480: 1198, // KillOrKillTiDB (2x) - 58481: 1199, // KillStmt (2x) - 58483: 1200, // LikeOrIlikeEscapeOpt (2x) - 58486: 1201, // LimitClause (2x) - 57479: 1202, // linear (2x) - 58488: 1203, // LinearOpt (2x) - 58492: 1204, // LoadDataOption (2x) - 58494: 1205, // LoadDataOptionListOpt (2x) - 58495: 1206, // LoadDataSetItem (2x) - 58497: 1207, // LoadDataSetSpecOpt (2x) - 58499: 1208, // LoadStatsStmt (2x) - 58500: 1209, // LocalOpt (2x) - 58503: 1210, // LockStatsStmt (2x) - 58504: 1211, // LockTablesStmt (2x) - 58512: 1212, // MaxValueOrExpression (2x) - 58519: 1213, // NonTransactionalDMLStmt (2x) - 58525: 1214, // NowSymOptionFractionParentheses (2x) - 58530: 1215, // ObjectType (2x) - 57502: 1216, // of (2x) - 58531: 1217, // OfTablesOpt (2x) - 58532: 1218, // OnCommitOpt (2x) - 58533: 1219, // OnDelete (2x) - 58536: 1220, // OnUpdate (2x) - 58541: 1221, // OptCollate (2x) - 58545: 1222, // OptFull (2x) - 58547: 1223, // OptInteger (2x) - 58561: 1224, // OptionalBraces (2x) - 58560: 1225, // OptionLevel (2x) - 58549: 1226, // OptLeadLagInfo (2x) - 58548: 1227, // OptLLDefault (2x) - 57509: 1228, // out (2x) - 58567: 1229, // OuterOpt (2x) - 58572: 1230, // PartitionDefinitionList (2x) - 58573: 1231, // PartitionDefinitionListOpt (2x) - 58574: 1232, // PartitionIntervalOpt (2x) - 58580: 1233, // PartitionOpt (2x) - 58581: 1234, // PasswordOpt (2x) - 58583: 1235, // PasswordOrLockOptionList (2x) - 58584: 1236, // PasswordOrLockOptions (2x) - 58585: 1237, // PauseLoadDataStmt (2x) - 58588: 1238, // PlacementOptionList (2x) - 58591: 1239, // PlanReplayerStmt (2x) - 58597: 1240, // PreparedStmt (2x) - 58602: 1241, // PrivLevel (2x) - 58604: 1242, // ProcedurceCond (2x) - 58605: 1243, // ProcedurceLabelOpt (2x) - 58611: 1244, // ProcedureDecl (2x) - 58618: 1245, // ProcedureHcond (2x) - 58620: 1246, // ProcedureIf (2x) - 58641: 1247, // QuickOptional (2x) - 58642: 1248, // RecoverTableStmt (2x) - 58644: 1249, // ReferOpt (2x) - 58646: 1250, // RegexpSym (2x) - 58648: 1251, // RenameTableStmt (2x) - 58649: 1252, // RenameUserStmt (2x) - 58651: 1253, // RepeatableOpt (2x) - 58659: 1254, // ResourceGroupNameOption (2x) - 58660: 1255, // ResourceGroupOptionList (2x) - 58662: 1256, // ResourceGroupRunawayActionOption (2x) - 58664: 1257, // ResourceGroupRunawayWatchOption (2x) - 58665: 1258, // RestartStmt (2x) - 58667: 1259, // ResumeLoadDataStmt (2x) - 57530: 1260, // revoke (2x) - 58668: 1261, // RevokeRoleStmt (2x) - 58669: 1262, // RevokeStmt (2x) - 58672: 1263, // RoleOrPrivElemList (2x) - 58673: 1264, // RoleSpec (2x) - 58685: 1265, // SearchWhenThen (2x) - 58697: 1266, // SelectStmtOpt (2x) - 58700: 1267, // SelectStmtSQLCache (2x) - 58704: 1268, // SetBindingStmt (2x) - 58705: 1269, // SetDefaultRoleOpt (2x) - 58706: 1270, // SetDefaultRoleStmt (2x) - 58716: 1271, // SetRoleStmt (2x) - 58724: 1272, // ShowProfileType (2x) - 58727: 1273, // ShowStmt (2x) - 58728: 1274, // ShowTableAliasOpt (2x) - 58730: 1275, // ShutdownStmt (2x) - 58735: 1276, // SimpleWhenThen (2x) - 58740: 1277, // SplitOption (2x) - 58741: 1278, // SplitRegionStmt (2x) - 58737: 1279, // SpOptInout (2x) - 58738: 1280, // SpPdparam (2x) - 57546: 1281, // sqlexception (2x) - 57547: 1282, // sqlstate (2x) - 57548: 1283, // sqlwarning (2x) - 58745: 1284, // Statement (2x) - 58748: 1285, // StatsOptionsOpt (2x) - 58749: 1286, // StatsPersistentVal (2x) - 58750: 1287, // StatsType (2x) - 58757: 1288, // SubPartDefinition (2x) - 58760: 1289, // SubPartitionMethod (2x) - 58765: 1290, // Symbol (2x) - 58771: 1291, // TableElementList (2x) - 58774: 1292, // TableLock (2x) - 58778: 1293, // TableNameListOpt (2x) - 58785: 1294, // TableOrTables (2x) - 58794: 1295, // TablesTerminalSym (2x) - 58792: 1296, // TableToTable (2x) - 58796: 1297, // TextStringList (2x) - 58801: 1298, // TraceStmt (2x) - 58809: 1299, // UnlockStatsStmt (2x) - 58810: 1300, // UnlockTablesStmt (2x) - 58816: 1301, // UserToUser (2x) - 58831: 1302, // VariableAssignmentList (2x) - 58841: 1303, // WhenClause (2x) - 58846: 1304, // WindowDefinition (2x) - 58849: 1305, // WindowFrameBound (2x) - 58856: 1306, // WindowSpec (2x) - 58861: 1307, // WithGrantOptionOpt (2x) - 58862: 1308, // WithList (2x) - 58867: 1309, // Writeable (2x) - 58: 1310, // ':' (1x) - 58195: 1311, // AdminShowSlow (1x) - 58197: 1312, // AdminStmtLimitOpt (1x) - 58204: 1313, // AlterOrderList (1x) - 58209: 1314, // AlterSequenceOptionList (1x) - 58212: 1315, // AlterTableSpecList (1x) - 58213: 1316, // AlterTableSpecListOpt (1x) - 58214: 1317, // AlterTableSpecSingleOpt (1x) - 58218: 1318, // AnalyzeOptionList (1x) - 58221: 1319, // AnyOrAll (1x) - 58222: 1320, // ArrayKwdOpt (1x) - 58224: 1321, // AsOfClauseOpt (1x) - 58225: 1322, // AsOpt (1x) - 58230: 1323, // AuthOption (1x) - 58231: 1324, // AuthPlugin (1x) - 58233: 1325, // AutoRandomOpt (1x) - 58243: 1326, // BetweenOrNotOp (1x) - 58245: 1327, // BindingStatusType (1x) - 57374: 1328, // both (1x) - 58257: 1329, // CalibrateOption (1x) - 58259: 1330, // CalibrateResourceWorkloadOption (1x) - 58267: 1331, // CharsetNameOrDefault (1x) - 58268: 1332, // CharsetOpt (1x) - 58273: 1333, // ColumnFormat (1x) - 58275: 1334, // ColumnList (1x) - 58282: 1335, // ColumnNameOrUserVariableList (1x) - 58279: 1336, // ColumnNameOrUserVarListOpt (1x) - 58287: 1337, // ColumnSetValueList (1x) - 58292: 1338, // CompareOp (1x) - 58296: 1339, // ConnectionOptionList (1x) - 58299: 1340, // ConstraintElem (1x) - 57386: 1341, // continueKwd (1x) - 58308: 1342, // CreateSequenceOptionListOpt (1x) - 58312: 1343, // CreateTableSelectOpt (1x) - 58315: 1344, // CreateViewSelectOpt (1x) - 57396: 1345, // cursor (1x) - 58323: 1346, // DatabaseOptionListOpt (1x) - 58320: 1347, // DBNameList (1x) - 58331: 1348, // DefaultOrExpressionList (1x) - 58333: 1349, // DefaultValueExpr (1x) - 58359: 1350, // DryRunOptions (1x) - 57415: 1351, // dual (1x) - 58361: 1352, // DynamicCalibrateOptionList (1x) - 58364: 1353, // ElseOpt (1x) - 58369: 1354, // EnforcedOrNotOrNotNullOpt (1x) - 57421: 1355, // exit (1x) - 58382: 1356, // ExpressionOpt (1x) - 58384: 1357, // FetchFirstOpt (1x) - 58386: 1358, // FieldAsName (1x) - 58387: 1359, // FieldAsNameOpt (1x) - 58389: 1360, // FieldItemList (1x) - 58391: 1361, // FieldList (1x) - 58397: 1362, // FirstAndLastPartOpt (1x) - 58398: 1363, // FirstOrNext (1x) - 58406: 1364, // FlushOption (1x) - 58410: 1365, // FromDual (1x) - 58412: 1366, // FulltextSearchModifierOpt (1x) - 58413: 1367, // FuncDatetimePrec (1x) - 58426: 1368, // GetFormatSelector (1x) - 58433: 1369, // HandleRangeList (1x) - 58438: 1370, // IdentListWithParenOpt (1x) - 58442: 1371, // IgnoreLines (1x) - 58444: 1372, // IlikeOrNotOp (1x) - 58451: 1373, // IndexHintScope (1x) - 58454: 1374, // IndexKeyTypeOpt (1x) - 58463: 1375, // IndexPartSpecificationListOpt (1x) - 58466: 1376, // IndexTypeOpt (1x) - 58446: 1377, // InOrNotOp (1x) - 58469: 1378, // InstanceOption (1x) - 58472: 1379, // IntervalExpr (1x) - 58475: 1380, // IsolationLevel (1x) - 58474: 1381, // IsOrNotOp (1x) - 57472: 1382, // leading (1x) - 58484: 1383, // LikeOrNotOp (1x) - 58485: 1384, // LikeTableWithOrWithoutParen (1x) - 58490: 1385, // LinesTerminated (1x) - 58493: 1386, // LoadDataOptionList (1x) - 58496: 1387, // LoadDataSetList (1x) - 58505: 1388, // LockType (1x) - 58506: 1389, // LogTypeOpt (1x) - 58507: 1390, // Match (1x) - 58508: 1391, // MatchOpt (1x) - 58509: 1392, // MaxIndexNumOpt (1x) - 58510: 1393, // MaxMinutesOpt (1x) - 58511: 1394, // MaxValPartOpt (1x) - 58513: 1395, // MaxValueOrExpressionList (1x) - 58526: 1396, // NullPartOpt (1x) - 58534: 1397, // OnDeleteUpdateOpt (1x) - 58535: 1398, // OnDuplicateKeyUpdate (1x) - 58537: 1399, // OptBinMod (1x) - 58539: 1400, // OptCharset (1x) - 58542: 1401, // OptExistingWindowName (1x) - 58544: 1402, // OptFromFirstLast (1x) - 58546: 1403, // OptGConcatSeparator (1x) - 58562: 1404, // OptionalShardColumn (1x) - 58552: 1405, // OptPartitionClause (1x) - 58553: 1406, // OptSpPdparams (1x) - 58554: 1407, // OptTable (1x) - 58871: 1408, // optValue (1x) - 58557: 1409, // OptWindowFrameClause (1x) - 58558: 1410, // OptWindowOrderByClause (1x) - 58564: 1411, // Order (1x) - 58563: 1412, // OrReplace (1x) - 57455: 1413, // outfile (1x) - 58570: 1414, // PartDefValuesOpt (1x) - 58575: 1415, // PartitionKeyAlgorithmOpt (1x) - 58576: 1416, // PartitionMethod (1x) - 58579: 1417, // PartitionNumOpt (1x) - 58586: 1418, // PerDB (1x) - 58587: 1419, // PerTable (1x) - 58590: 1420, // PlanReplayerDumpOpt (1x) - 57514: 1421, // precisionType (1x) - 58596: 1422, // PrepareSQL (1x) - 58872: 1423, // procedurceElseIfs (1x) - 58607: 1424, // ProcedureCall (1x) - 58610: 1425, // ProcedureCursorSelectStmt (1x) - 58612: 1426, // ProcedureDeclIdents (1x) - 58613: 1427, // ProcedureDecls (1x) - 58614: 1428, // ProcedureDeclsOpt (1x) - 58616: 1429, // ProcedureFetchList (1x) - 58617: 1430, // ProcedureHandlerType (1x) - 58619: 1431, // ProcedureHcondList (1x) - 58626: 1432, // ProcedureOptDefault (1x) - 58627: 1433, // ProcedureOptFetchNo (1x) - 58630: 1434, // ProcedureProcStmts (1x) - 58639: 1435, // QueryWatchOptionList (1x) - 57521: 1436, // recursive (1x) - 58645: 1437, // RegexpOrNotOp (1x) - 58650: 1438, // ReorganizePartitionRuleOpt (1x) - 58655: 1439, // RequireList (1x) - 58657: 1440, // ResourceGroupBackgroundOptionList (1x) - 58661: 1441, // ResourceGroupPriorityOption (1x) - 58663: 1442, // ResourceGroupRunawayOptionList (1x) - 58674: 1443, // RoleSpecList (1x) - 58681: 1444, // RowOrRows (1x) - 58686: 1445, // SearchedWhenThenList (1x) - 58690: 1446, // SelectStmtFieldList (1x) - 58698: 1447, // SelectStmtOpts (1x) - 58699: 1448, // SelectStmtOptsList (1x) - 58703: 1449, // SequenceOptionList (1x) - 58708: 1450, // SetOpr (1x) - 58715: 1451, // SetRoleOpt (1x) - 58718: 1452, // ShardableStmt (1x) - 58720: 1453, // ShowIndexKwd (1x) - 58721: 1454, // ShowLikeOrWhereOpt (1x) - 58722: 1455, // ShowPlacementTarget (1x) - 58723: 1456, // ShowProfileArgsOpt (1x) - 58725: 1457, // ShowProfileTypes (1x) - 58726: 1458, // ShowProfileTypesOpt (1x) - 58729: 1459, // ShowTargetFilterable (1x) - 58736: 1460, // SimpleWhenThenList (1x) - 57541: 1461, // spatial (1x) - 58742: 1462, // SplitSyntaxOption (1x) - 58739: 1463, // SpPdparams (1x) - 57549: 1464, // ssl (1x) - 58743: 1465, // Start (1x) - 58744: 1466, // Starting (1x) - 57550: 1467, // starting (1x) - 58746: 1468, // StatementList (1x) - 58747: 1469, // StatementScope (1x) - 58751: 1470, // StorageMedia (1x) - 57556: 1471, // stored (1x) - 58752: 1472, // StringList (1x) - 58755: 1473, // StringNameOrBRIEOptionKeyword (1x) - 58758: 1474, // SubPartDefinitionList (1x) - 58759: 1475, // SubPartDefinitionListOpt (1x) - 58761: 1476, // SubPartitionNumOpt (1x) - 58762: 1477, // SubPartitionOpt (1x) - 58772: 1478, // TableElementListOpt (1x) - 58775: 1479, // TableLockList (1x) - 58788: 1480, // TableRefsClause (1x) - 58789: 1481, // TableSampleMethodOpt (1x) - 58790: 1482, // TableSampleOpt (1x) - 58791: 1483, // TableSampleUnitOpt (1x) - 58793: 1484, // TableToTableList (1x) - 57563: 1485, // trailing (1x) - 58805: 1486, // TrimDirection (1x) - 58817: 1487, // UserToUserList (1x) - 58819: 1488, // UserVariableList (1x) - 58822: 1489, // UsingRoles (1x) - 58824: 1490, // Values (1x) - 58826: 1491, // ValuesOpt (1x) - 58833: 1492, // ViewAlgorithm (1x) - 58834: 1493, // ViewCheckOption (1x) - 58835: 1494, // ViewDefiner (1x) - 58836: 1495, // ViewFieldList (1x) - 58837: 1496, // ViewName (1x) - 58838: 1497, // ViewSQLSecurity (1x) - 57584: 1498, // virtual (1x) - 58839: 1499, // VirtualOrStored (1x) - 58840: 1500, // WatchDurationOption (1x) - 58842: 1501, // WhenClauseList (1x) - 58845: 1502, // WindowClauseOptional (1x) - 58847: 1503, // WindowDefinitionList (1x) - 58848: 1504, // WindowFrameBetween (1x) - 58850: 1505, // WindowFrameExtent (1x) - 58852: 1506, // WindowFrameUnits (1x) - 58855: 1507, // WindowNameOrSpec (1x) - 58857: 1508, // WindowSpecDetails (1x) - 58863: 1509, // WithReadLockOpt (1x) - 58864: 1510, // WithRollupClause (1x) - 58865: 1511, // WithValidation (1x) - 58866: 1512, // WithValidationOpt (1x) - 58193: 1513, // $default (0x) - 58153: 1514, // andnot (0x) - 58228: 1515, // AssignmentListOpt (0x) - 58272: 1516, // ColumnDefList (0x) - 58288: 1517, // CommaOpt (0x) - 58177: 1518, // createTableSelect (0x) - 58167: 1519, // empty (0x) - 57345: 1520, // error (0x) - 58192: 1521, // higherThanComma (0x) - 58186: 1522, // higherThanParenthese (0x) - 58175: 1523, // insertValues (0x) - 57355: 1524, // invalid (0x) - 58178: 1525, // lowerThanCharsetKwd (0x) - 58191: 1526, // lowerThanComma (0x) - 58176: 1527, // lowerThanCreateTableSelect (0x) - 58188: 1528, // lowerThanEq (0x) - 58183: 1529, // lowerThanFunction (0x) - 58174: 1530, // lowerThanInsertValues (0x) - 58179: 1531, // lowerThanKey (0x) - 58180: 1532, // lowerThanLocal (0x) - 58190: 1533, // lowerThanNot (0x) - 58187: 1534, // lowerThanOn (0x) - 58185: 1535, // lowerThanParenthese (0x) - 58181: 1536, // lowerThanRemove (0x) - 58168: 1537, // lowerThanSelectOpt (0x) - 58173: 1538, // lowerThanSelectStmt (0x) - 58172: 1539, // lowerThanSetKeyword (0x) - 58171: 1540, // lowerThanStringLitToken (0x) - 58169: 1541, // lowerThanValueKeyword (0x) - 58170: 1542, // lowerThanWith (0x) - 58182: 1543, // lowerThenOrder (0x) - 58189: 1544, // neg (0x) - 57359: 1545, // odbcDateType (0x) - 57361: 1546, // odbcTimestampType (0x) - 57360: 1547, // odbcTimeType (0x) - 58779: 1548, // TableNameListOpt2 (0x) - 58184: 1549, // tableRefPriority (0x) - } - - yySymNames = []string{ - "';'", - "$end", - "remove", - "split", - "merge", - "reorganize", - "comment", - "storage", - "autoIncrement", - "','", - "first", - "after", - "serial", - "autoRandom", - "columnFormat", - "password", - "charsetKwd", - "checksum", - "placement", - "keyBlockSize", - "tablespace", - "encryption", - "data", - "engine", - "insertMethod", - "maxRows", - "minRows", - "nodegroup", - "connection", - "autoRandomBase", - "statsBuckets", - "statsTopN", - "ttl", - "autoIdCache", - "avgRowLength", - "compression", - "delayKeyWrite", - "packKeys", - "preSplitRegions", - "rowFormat", - "secondaryEngine", - "shardRowIDBits", - "statsAutoRecalc", - "statsColChoice", - "statsColList", - "statsPersistent", - "statsSamplePages", - "statsSampleRate", - "tableChecksum", - "ttlEnable", - "ttlJobInterval", - "resource", - "attribute", - "account", - "failedLoginAttempts", - "passwordLockTime", - "identifier", - "')'", - "resume", - "signed", - "snapshot", - "backend", - "checkpoint", - "concurrency", - "csvBackslashEscape", - "csvDelimiter", - "csvHeader", - "csvNotNull", - "csvNull", - "csvSeparator", - "csvTrimLastSeparators", - "fullBackupStorage", - "gcTTL", - "lastBackup", - "onDuplicate", - "online", - "rateLimit", - "restoredTS", - "sendCredentialsToTiKV", - "skipSchemaFiles", - "startTS", - "strictFormat", - "tikvImporter", - "untilTS", - "begin", - "commit", - "no", - "rollback", - "start", - "truncate", - "cache", - "nocache", - "open", - "action", - "close", - "cycle", - "minValue", - "end", - "increment", - "nocycle", - "nomaxvalue", - "nominvalue", - "algorithm", - "restart", - "tp", - "clustered", - "invisible", - "nonclustered", - "regions", - "visible", - "background", - "burstable", - "priority", - "queryLimit", - "ruRate", - "subpartition", - "partitions", - "plan", - "yearType", - "constraints", - "followerConstraints", - "followers", - "leaderConstraints", - "learnerConstraints", - "learners", - "primaryRegion", - "schedule", - "sqlTsiYear", - "survivalPreferences", - "voterConstraints", - "voters", - "columns", - "view", - "day", - "watch", - "defined", - "execElapsed", - "second", - "hour", - "microsecond", - "minute", - "month", - "quarter", - "sqlTsiDay", - "sqlTsiHour", - "sqlTsiMinute", - "sqlTsiMonth", - "sqlTsiQuarter", - "sqlTsiSecond", - "sqlTsiWeek", - "week", - "ascii", - "byteType", - "unicodeSym", - "fields", - "logs", - "status", - "tables", - "timeDuration", - "query", - "separator", - "cipher", - "issuer", - "maxConnectionsPerHour", - "maxQueriesPerHour", - "maxUpdatesPerHour", - "maxUserConnections", - "preceding", - "san", - "subject", - "tokenIssuer", - "endTime", - "jsonType", - "local", - "startTime", - "datetimeType", - "dateType", - "fixed", - "job", - "timeType", - "bindings", - "definer", - "hash", - "identified", - "respect", - "timestampType", - "value", - "backup", - "booleanType", - "current", - "enforced", - "following", - "less", - "nowait", - "only", - "savepoint", - "skip", - "taskTypes", - "textType", - "than", - "tiFlash", - "unbounded", - "binding", - "bitType", - "boolType", - "enum", - "global", - "hypo", - "importKwd", - "national", - "ncharType", - "next_row_id", - "nvarcharType", - "offset", - "policy", - "predicate", - "temporary", - "user", - "digest", - "jobs", - "location", - "planCache", - "prepare", - "replica", - "role", - "stats", - "unknown", - "wait", - "btree", - "cooldown", - "declare", - "dryRun", - "format", - "isolation", - "last", - "max_idxnum", - "memory", - "off", - "optional", - "per_db", - "privileges", - "required", - "rtree", - "sampleRate", - "sequence", - "session", - "slow", - "validation", - "variables", - "attributes", - "cancel", - "compact", - "ddl", - "disable", - "do", - "dynamic", - "enable", - "errorKwd", - "exact", - "flush", - "full", - "handler", - "history", - "mb", - "mode", - "next", - "pause", - "plugins", - "processlist", - "recover", - "repair", - "repeatable", - "similar", - "statistics", - "subpartitions", - "tidb", - "without", - "admin", - "batch", - "binlog", - "block", - "br", - "briefType", - "buckets", - "calibrate", - "capture", - "cardinality", - "chain", - "clientErrorsSummary", - "cmSketch", - "coalesce", - "compressed", - "context", - "copyKwd", - "correlation", - "cpu", - "deallocate", - "dependency", - "directory", - "discard", - "disk", - "dotType", - "drainer", - "dry", - "duplicate", - "exchange", - "execute", - "expansion", - "flashback", - "general", - "help", - "high", - "histogram", - "hosts", - "identSQLErrors", - "inplace", - "instance", - "instant", - "ipc", - "labels", - "locked", - "low", - "medium", - "metadata", - "modify", - "nodeID", - "nodeState", - "nulls", - "pageSym", - "pump", - "purge", - "rebuild", - "redundant", - "reload", - "restore", - "routine", - "s3", - "samples", - "secondaryLoad", - "secondaryUnload", - "share", - "shutdown", - "source", - "statsOptions", - "stop", - "swaps", - "tidbJson", - "tokudbDefault", - "tokudbFast", - "tokudbLzma", - "tokudbQuickLZ", - "tokudbSmall", - "tokudbSnappy", - "tokudbUncompressed", - "tokudbZlib", - "tokudbZstd", - "topn", - "trace", - "traditional", - "trueCardCost", - "unlimited", - "verboseType", - "warnings", - "advise", - "against", - "ago", - "always", - "backups", - "bernoulli", - "bindingCache", - "builtins", - "cascaded", - "causal", - "cleanup", - "client", - "cluster", - "collation", - "columnStatsUsage", - "committed", - "config", - "consistency", - "consistent", - "depth", - "disabled", - "dump", - "enabled", - "engines", - "events", - "evolve", - "expire", - "exprPushdownBlacklist", - "extended", - "faultsSym", - "found", - "function", - "grants", - "histogramsInFlight", - "incremental", - "indexes", - "internal", - "invoker", - "io", - "language", - "level", - "list", - "master", - "max_minutes", - "never", - "nextval", - "none", - "oltpReadOnly", - "oltpReadWrite", - "oltpWriteOnly", - "optimistic", - "optRuleBlacklist", - "parser", - "partial", - "partitioning", - "per_table", - "percent", - "pessimistic", - "point", - "preserve", - "profile", - "profiles", - "queries", - "recent", - "region", - "replayer", - "reset", - "restores", - "reuse", - "rollup", - "run", - "security", - "serializable", - "sessionStates", - "simple", - "slave", - "statsHealthy", - "statsHistograms", - "statsLocked", - "statsMeta", - "switchesSym", - "system", - "systemTime", - "target", - "telemetryID", - "temptable", - "tls", - "top", - "tpcc", - "tpch10", - "transaction", - "triggers", - "uncommitted", - "undefined", - "width", - "workload", - "x509", - "addDate", - "any", - "approxCountDistinct", - "approxPercentile", - "avg", - "bitAnd", - "bitOr", - "bitXor", - "bound", - "cast", - "curDate", - "curTime", - "dateAdd", - "dateSub", - "escape", - "event", - "exclusive", - "extract", - "file", - "follower", - "getFormat", - "groupConcat", - "imports", - "ioReadBandwidth", - "ioWriteBandwidth", - "jsonArrayagg", - "jsonObjectAgg", - "lastval", - "leader", - "learner", - "max", - "member", - "min", - "names", - "now", - "position", - "process", - "proxy", - "quick", - "replicas", - "replication", - "reverse", - "rowCount", - "running", - "setval", - "shared", - "some", - "sqlBufferResult", - "sqlCache", - "sqlNoCache", - "staleness", - "std", - "stddev", - "stddevPop", - "stddevSamp", - "strict", - "strong", - "subDate", - "substring", - "sum", - "super", - "telemetry", - "timestampAdd", - "timestampDiff", - "trim", - "variance", - "varPop", - "varSamp", - "voter", - "weightString", - "on", - "'('", - "with", - "stringLit", - "not2", - "defaultKwd", - "not", - "as", - "collate", - "union", - "left", - "right", - "using", - "'+'", - "'-'", - "mod", - "partition", - "values", - "null", - "ignore", - "except", - "intersect", - "replace", - "charType", - "fetch", - "eq", - "limit", - "set", - "forKwd", - "into", - "intLit", - "from", - "lock", - "where", - "order", - "force", - "and", - "or", - "andand", - "pipesAsOr", - "xor", - "group", - "having", - "straightJoin", - "window", - "use", - "join", - "desc", - "ifKwd", - "like", - "natural", - "cross", - "explain", - "inner", - "'*'", - "'}'", - "binaryType", - "insert", - "rows", - "when", - "elseKwd", - "rangeKwd", - "tableSample", - "groups", - "dayHour", - "dayMicrosecond", - "dayMinute", - "daySecond", - "hourMicrosecond", - "hourMinute", - "hourSecond", - "minuteMicrosecond", - "minuteSecond", - "secondMicrosecond", - "yearMonth", - "asc", - "in", - "then", - "tableKwd", - "'/'", - "'%'", - "'&'", - "'^'", - "'|'", - "caseKwd", - "div", - "lsh", - "repeat", - "rsh", - "'<'", - "'>'", - "ge", - "is", - "le", - "neq", - "neqSynonym", - "nulleq", - "between", - "singleAtIdentifier", - "falseKwd", - "trueKwd", - "currentUser", - "ilike", - "regexpKwd", - "rlike", - "memberof", - "decLit", - "floatLit", - "hexLit", - "row", - "bitLit", - "interval", - "paramMarker", - "'{'", - "database", - "exists", - "convert", - "underscoreCS", - "builtinCurDate", - "builtinNow", - "currentDate", - "currentTs", - "doubleAtIdentifier", - "localTime", - "localTs", - "builtinCount", - "'!'", - "'~'", - "builtinApproxCountDistinct", - "builtinApproxPercentile", - "builtinBitAnd", - "builtinBitOr", - "builtinBitXor", - "builtinCast", - "builtinCurTime", - "builtinDateAdd", - "builtinDateSub", - "builtinExtract", - "builtinGroupConcat", - "builtinMax", - "builtinMin", - "builtinPosition", - "builtinStddevPop", - "builtinStddevSamp", - "builtinSubstring", - "builtinSum", - "builtinSysDate", - "builtinTranslate", - "builtinTrim", - "builtinUser", - "builtinVarPop", - "builtinVarSamp", - "cumeDist", - "currentRole", - "currentTime", - "denseRank", - "firstValue", - "lag", - "lastValue", - "lead", - "nthValue", - "ntile", - "percentRank", - "rank", - "rowNumber", - "selectKwd", - "sql", - "tidbCurrentTSO", - "utcDate", - "utcTime", - "utcTimestamp", - "key", - "check", - "pipes", - "primary", - "unique", - "constraint", - "references", - "generated", - "character", - "index", - "match", - "to", - "analyze", - "update", - "all", - "'.'", - "assignmentEq", - "jss", - "juss", - "maxValue", - "array", - "lines", - "by", - "alter", - "require", - "'@'", - "drop", - "cascade", - "read", - "restrict", - "asof", - "varcharacter", - "varcharType", - "decimalType", - "doubleType", - "floatType", - "integerType", - "intType", - "realType", - "varbinaryType", - "bigIntType", - "blobType", - "create", - "float4Type", - "float8Type", - "foreign", - "fulltext", - "int1Type", - "int2Type", - "int3Type", - "int4Type", - "int8Type", - "long", - "longblobType", - "longtextType", - "mediumblobType", - "mediumIntType", - "mediumtextType", - "middleIntType", - "numericType", - "smallIntType", - "tinyblobType", - "tinyIntType", - "tinytextType", - "toTimestamp", - "change", - "rename", - "write", - "add", - "Identifier", - "NotKeywordToken", - "TiDBKeyword", - "UnReservedKeyword", - "optimize", - "SubSelect", - "UserVariable", - "Literal", - "SimpleIdent", - "StringLiteral", - "NextValueForSequence", - "FunctionCallGeneric", - "FunctionCallKeyword", - "FunctionCallNonKeyword", - "FunctionNameConflict", - "FunctionNameDateArith", - "FunctionNameDateArithMultiForms", - "FunctionNameDatetimePrecision", - "FunctionNameOptionalBraces", - "FunctionNameSequence", - "SimpleExpr", - "SumExpr", - "SystemVariable", - "Variable", - "WindowFuncCall", - "BitExpr", - "PredicateExpr", - "BoolPri", - "Expression", - "NUM", - "logAnd", - "logOr", - "EqOpt", - "deleteKwd", - "TableName", - "StringName", - "SelectStmt", - "SelectStmtBasic", - "SelectStmtFromDualTable", - "SelectStmtFromTable", - "SetOprClause", - "SetOprClauseList", - "SetOprStmtWithLimitOrderBy", - "SetOprStmtWoutLimitOrderBy", - "unsigned", - "WithClause", - "LengthNum", - "SelectStmtWithClause", - "SetOprStmt", - "zerofill", - "over", - "ColumnName", - "UpdateStmtNoWith", - "DeleteWithoutUsingStmt", - "InsertIntoStmt", - "Int64Num", - "ReplaceIntoStmt", - "UpdateStmt", - "describe", - "distinct", - "distinctRow", - "while", - "WindowingClause", - "DeleteWithUsingStmt", - "iterate", - "leave", - "delayed", - "highPriority", - "lowPriority", - "DeleteFromStmt", - "hintComment", - "FieldLen", - "OrderBy", - "SelectStmtLimit", - "OptWindowingClause", - "AnalyzeTableStmt", - "CommitStmt", - "RollbackStmt", - "SetStmt", - "sqlBigResult", - "sqlCalcFoundRows", - "sqlSmallResult", - "terminated", - "CharsetKw", - "IfExists", - "Username", - "enclosed", - "ExplainStmt", - "ExplainSym", - "PartitionNameList", - "TruncateTableStmt", - "UseStmt", - "escaped", - "ExpressionList", - "optionallyEnclosedBy", - "PlacementPolicyOption", - "ProcedureBlockContent", - "ProcedureUnlabelLoopStmt", - "ProcedureCaseStmt", - "ProcedureCloseCur", - "ProcedureFetchInto", - "ProcedureIfstmt", - "ProcedureIterate", - "ProcedureLabeledBlock", - "ProcedurelabeledLoopStmt", - "ProcedureLeave", - "ProcedureOpenCur", - "ProcedureProcStmt", - "ProcedureSearchedCase", - "ProcedureSimpleCase", - "ProcedureStatementStmt", - "ProcedureUnlabeledBlock", - "ProcedureUnlabelLoopBlock", - "IfNotExists", - "TableNameList", - "DistinctKwd", - "TimestampUnit", - "DistinctOpt", - "OptFieldLen", - "WhereClause", - "WhereClauseOptional", - "DefaultKwdOpt", - "EqOrAssignmentEq", - "ExprOrDefault", - "load", - "JoinTable", - "OptBinary", - "release", - "RolenameComposed", - "TableFactor", - "TableRef", - "TimeUnit", - "AnalyzeOptionListOpt", - "FromOrIn", - "AlterTableStmt", - "CharsetName", - "ColumnNameList", - "DBName", - "noWriteToBinLog", - "OrderByOptional", - "PartDefOption", - "SignedNum", - "BuggyDefaultFalseDistinctOpt", - "DefaultFalseDistinctOpt", - "JoinType", - "NotSym", - "NumLiteral", - "Rolename", - "RoleNameString", - "CrossOpt", - "ExplainableStmt", - "ExpressionListOpt", - "IndexPartSpecification", - "KeyOrIndex", - "NoWriteToBinLogAliasOpt", - "SelectStmtLimitOpt", - "VariableName", - "AllOrPartitionNameList", - "ConstraintKeywordOpt", - "DatabaseSym", - "FieldsOrColumns", - "ForceOpt", - "IndexPartSpecificationList", - "kill", - "Priority", - "ProcedureProcStmt1s", - "ResourceGroupName", - "RowFormat", - "RowValue", - "SetExpr", - "ShowDatabaseNameOpt", - "TableOption", - "varying", - "BeginTransactionStmt", - "BindableStmt", - "BRIEBooleanOptionName", - "BRIEIntegerOptionName", - "BRIEKeywordOptionName", - "BRIEOption", - "BRIEOptions", - "BRIEStringOptionName", - "Char", - "column", - "ColumnDef", - "DatabaseOption", - "EscapedTableRef", - "FieldTerminator", - "grant", - "IgnoreOptional", - "IndexInvisible", - "IndexNameList", - "IndexType", - "LoadDataStmt", - "PartitionNameListOpt", - "procedure", - "ReleaseSavepointStmt", - "RolenameList", - "SavepointStmt", - "show", - "TableOptimizerHints", - "UsernameList", - "WithClustered", - "AlgorithmClause", - "ByItem", - "CollationName", - "ColumnKeywordOpt", - "DirectPlacementOption", - "DirectResourceGroupOption", - "FieldOpt", - "FieldOpts", - "IdentList", - "IndexName", - "IndexOption", - "IndexOptionList", - "infile", - "LimitOption", - "LockClause", - "OptCharsetWithOptBinary", - "OptNullTreatment", - "PolicyName", - "PriorityOpt", - "SelectLockOpt", - "SelectStmtIntoOption", - "TableRefs", - "UserSpec", - "AsOfClause", - "Assignment", - "AuthString", - "Boolean", - "BuiltinFunction", - "ByList", - "ConfigItemName", - "Constraint", - "FloatOpt", - "IndexTypeName", - "NumList", - "option", - "optionally", - "OptWild", - "outer", - "Precision", - "ReferDef", - "RestrictOrCascadeOpt", - "RowStmt", - "SequenceOption", - "statsExtended", - "TableAsName", - "TableAsNameOpt", - "TableNameOptWild", - "TableOptimizerHintsOpt", - "TableOptionList", - "TextString", - "TraceableStmt", - "TransactionChar", - "UserSpecList", - "Varchar", - "WindowName", - "AssignmentList", - "AttributesOpt", - "BitValueType", - "BlobType", - "BooleanType", - "ColumnOption", - "ColumnPosition", - "CommonTableExpr", - "CreateTableStmt", - "CurdateSym", - "DatabaseOptionList", - "DateAndTimeType", - "DefaultTrueDistinctOpt", - "DirectResourceGroupBackgroundOption", - "DirectResourceGroupRunawayOption", - "DynamicCalibrateResourceOption", - "elseIfKwd", - "EnforcedOrNot", - "ExtendedPriv", - "FixedPointType", - "FloatingPointType", - "GeneratedAlways", - "GlobalScope", - "GroupByClause", - "IndexHint", - "IndexHintType", - "IndexNameAndTypeOpt", - "IntegerType", - "keys", - "Lines", - "LocationLabelList", - "NChar", - "NowSym", - "NowSymFunc", - "NowSymOptionFraction", - "NumericType", - "NVarchar", - "OptOrder", - "OptTemporary", - "PartDefOptionList", - "PartitionDefinition", - "PasswordOrLockOption", - "PluginNameList", - "PrimaryOpt", - "PrivElem", - "PrivType", - "QueryWatchOption", - "QueryWatchTextOption", - "RequireClause", - "RequireClauseOpt", - "RequireListElement", - "RolenameWithoutIdent", - "RoleOrPrivElem", - "SelectStmtGroup", - "SetOprOpt", - "SignedLiteral", - "StringType", - "TableAliasRefList", - "TableElement", - "TextType", - "TransactionChars", - "trigger", - "Type", - "unlock", - "until", - "usage", - "ValuesList", - "ValuesStmtList", - "ValueSym", - "VariableAssignment", - "WindowFrameStart", - "Year", - "AddQueryWatchStmt", - "AdminStmt", - "AllColumnsOrPredicateColumnsOpt", - "AlterDatabaseStmt", - "AlterInstanceStmt", - "AlterOrderItem", - "AlterPolicyStmt", - "AlterRangeStmt", - "AlterResourceGroupStmt", - "AlterSequenceOption", - "AlterSequenceStmt", - "AlterTableSpec", - "AlterUserStmt", - "AnalyzeOption", - "BinlogStmt", - "BRIEStmt", - "BRIETables", - "CalibrateResourceStmt", - "call", - "CallStmt", - "CancelImportStmt", - "CastType", - "ChangeStmt", - "CheckConstraintKeyword", - "ColumnNameListOpt", - "ColumnNameOrUserVariable", - "ColumnNameOrUserVarListOptWithBrackets", - "ColumnOptionList", - "ColumnOptionListOpt", - "CommentOrAttributeOption", - "CompletionTypeWithinTransaction", - "ConnectionOption", - "ConnectionOptions", - "CreateBindingStmt", - "CreateDatabaseStmt", - "CreateIndexStmt", - "CreatePolicyStmt", - "CreateProcedureStmt", - "CreateResourceGroupStmt", - "CreateRoleStmt", - "CreateSequenceStmt", - "CreateStatisticsStmt", - "CreateTableOptionListOpt", - "CreateUserStmt", - "CreateViewStmt", - "databases", - "DeallocateStmt", - "DeallocateSym", - "DefaultOrExpression", - "DoStmt", - "DropBindingStmt", - "DropDatabaseStmt", - "DropIndexStmt", - "DropLoadDataStmt", - "DropPolicyStmt", - "DropProcedureStmt", - "DropQueryWatchStmt", - "DropResourceGroupStmt", - "DropRoleStmt", - "DropSequenceStmt", - "DropStatisticsStmt", - "DropStatsStmt", - "DropTableStmt", - "DropUserStmt", - "DropViewStmt", - "DuplicateOpt", - "ElseCaseOpt", - "EmptyStmt", - "EncryptionOpt", - "EnforcedOrNotOpt", - "ExecuteStmt", - "ExplainFormatType", - "Field", - "FieldItem", - "Fields", - "FlashbackDatabaseStmt", - "FlashbackTableStmt", - "FlashbackToNewName", - "FlashbackToTimestampStmt", - "FlushStmt", - "FormatOpt", - "FuncDatetimePrecList", - "FuncDatetimePrecListOpt", - "GrantProxyStmt", - "GrantRoleStmt", - "GrantStmt", - "HandleRange", - "HashString", - "HavingClause", - "HelpStmt", - "ImportIntoStmt", - "IndexAdviseStmt", - "IndexHintList", - "IndexHintListOpt", - "IndexLockAndAlgorithmOpt", - "inout", - "InsertValues", - "IntoOpt", - "KeyOrIndexOpt", - "KillOrKillTiDB", - "KillStmt", - "LikeOrIlikeEscapeOpt", - "LimitClause", - "linear", - "LinearOpt", - "LoadDataOption", - "LoadDataOptionListOpt", - "LoadDataSetItem", - "LoadDataSetSpecOpt", - "LoadStatsStmt", - "LocalOpt", - "LockStatsStmt", - "LockTablesStmt", - "MaxValueOrExpression", - "NonTransactionalDMLStmt", - "NowSymOptionFractionParentheses", - "ObjectType", - "of", - "OfTablesOpt", - "OnCommitOpt", - "OnDelete", - "OnUpdate", - "OptCollate", - "OptFull", - "OptInteger", - "OptionalBraces", - "OptionLevel", - "OptLeadLagInfo", - "OptLLDefault", - "out", - "OuterOpt", - "PartitionDefinitionList", - "PartitionDefinitionListOpt", - "PartitionIntervalOpt", - "PartitionOpt", - "PasswordOpt", - "PasswordOrLockOptionList", - "PasswordOrLockOptions", - "PauseLoadDataStmt", - "PlacementOptionList", - "PlanReplayerStmt", - "PreparedStmt", - "PrivLevel", - "ProcedurceCond", - "ProcedurceLabelOpt", - "ProcedureDecl", - "ProcedureHcond", - "ProcedureIf", - "QuickOptional", - "RecoverTableStmt", - "ReferOpt", - "RegexpSym", - "RenameTableStmt", - "RenameUserStmt", - "RepeatableOpt", - "ResourceGroupNameOption", - "ResourceGroupOptionList", - "ResourceGroupRunawayActionOption", - "ResourceGroupRunawayWatchOption", - "RestartStmt", - "ResumeLoadDataStmt", - "revoke", - "RevokeRoleStmt", - "RevokeStmt", - "RoleOrPrivElemList", - "RoleSpec", - "SearchWhenThen", - "SelectStmtOpt", - "SelectStmtSQLCache", - "SetBindingStmt", - "SetDefaultRoleOpt", - "SetDefaultRoleStmt", - "SetRoleStmt", - "ShowProfileType", - "ShowStmt", - "ShowTableAliasOpt", - "ShutdownStmt", - "SimpleWhenThen", - "SplitOption", - "SplitRegionStmt", - "SpOptInout", - "SpPdparam", - "sqlexception", - "sqlstate", - "sqlwarning", - "Statement", - "StatsOptionsOpt", - "StatsPersistentVal", - "StatsType", - "SubPartDefinition", - "SubPartitionMethod", - "Symbol", - "TableElementList", - "TableLock", - "TableNameListOpt", - "TableOrTables", - "TablesTerminalSym", - "TableToTable", - "TextStringList", - "TraceStmt", - "UnlockStatsStmt", - "UnlockTablesStmt", - "UserToUser", - "VariableAssignmentList", - "WhenClause", - "WindowDefinition", - "WindowFrameBound", - "WindowSpec", - "WithGrantOptionOpt", - "WithList", - "Writeable", - "':'", - "AdminShowSlow", - "AdminStmtLimitOpt", - "AlterOrderList", - "AlterSequenceOptionList", - "AlterTableSpecList", - "AlterTableSpecListOpt", - "AlterTableSpecSingleOpt", - "AnalyzeOptionList", - "AnyOrAll", - "ArrayKwdOpt", - "AsOfClauseOpt", - "AsOpt", - "AuthOption", - "AuthPlugin", - "AutoRandomOpt", - "BetweenOrNotOp", - "BindingStatusType", - "both", - "CalibrateOption", - "CalibrateResourceWorkloadOption", - "CharsetNameOrDefault", - "CharsetOpt", - "ColumnFormat", - "ColumnList", - "ColumnNameOrUserVariableList", - "ColumnNameOrUserVarListOpt", - "ColumnSetValueList", - "CompareOp", - "ConnectionOptionList", - "ConstraintElem", - "continueKwd", - "CreateSequenceOptionListOpt", - "CreateTableSelectOpt", - "CreateViewSelectOpt", - "cursor", - "DatabaseOptionListOpt", - "DBNameList", - "DefaultOrExpressionList", - "DefaultValueExpr", - "DryRunOptions", - "dual", - "DynamicCalibrateOptionList", - "ElseOpt", - "EnforcedOrNotOrNotNullOpt", - "exit", - "ExpressionOpt", - "FetchFirstOpt", - "FieldAsName", - "FieldAsNameOpt", - "FieldItemList", - "FieldList", - "FirstAndLastPartOpt", - "FirstOrNext", - "FlushOption", - "FromDual", - "FulltextSearchModifierOpt", - "FuncDatetimePrec", - "GetFormatSelector", - "HandleRangeList", - "IdentListWithParenOpt", - "IgnoreLines", - "IlikeOrNotOp", - "IndexHintScope", - "IndexKeyTypeOpt", - "IndexPartSpecificationListOpt", - "IndexTypeOpt", - "InOrNotOp", - "InstanceOption", - "IntervalExpr", - "IsolationLevel", - "IsOrNotOp", - "leading", - "LikeOrNotOp", - "LikeTableWithOrWithoutParen", - "LinesTerminated", - "LoadDataOptionList", - "LoadDataSetList", - "LockType", - "LogTypeOpt", - "Match", - "MatchOpt", - "MaxIndexNumOpt", - "MaxMinutesOpt", - "MaxValPartOpt", - "MaxValueOrExpressionList", - "NullPartOpt", - "OnDeleteUpdateOpt", - "OnDuplicateKeyUpdate", - "OptBinMod", - "OptCharset", - "OptExistingWindowName", - "OptFromFirstLast", - "OptGConcatSeparator", - "OptionalShardColumn", - "OptPartitionClause", - "OptSpPdparams", - "OptTable", - "optValue", - "OptWindowFrameClause", - "OptWindowOrderByClause", - "Order", - "OrReplace", - "outfile", - "PartDefValuesOpt", - "PartitionKeyAlgorithmOpt", - "PartitionMethod", - "PartitionNumOpt", - "PerDB", - "PerTable", - "PlanReplayerDumpOpt", - "precisionType", - "PrepareSQL", - "procedurceElseIfs", - "ProcedureCall", - "ProcedureCursorSelectStmt", - "ProcedureDeclIdents", - "ProcedureDecls", - "ProcedureDeclsOpt", - "ProcedureFetchList", - "ProcedureHandlerType", - "ProcedureHcondList", - "ProcedureOptDefault", - "ProcedureOptFetchNo", - "ProcedureProcStmts", - "QueryWatchOptionList", - "recursive", - "RegexpOrNotOp", - "ReorganizePartitionRuleOpt", - "RequireList", - "ResourceGroupBackgroundOptionList", - "ResourceGroupPriorityOption", - "ResourceGroupRunawayOptionList", - "RoleSpecList", - "RowOrRows", - "SearchedWhenThenList", - "SelectStmtFieldList", - "SelectStmtOpts", - "SelectStmtOptsList", - "SequenceOptionList", - "SetOpr", - "SetRoleOpt", - "ShardableStmt", - "ShowIndexKwd", - "ShowLikeOrWhereOpt", - "ShowPlacementTarget", - "ShowProfileArgsOpt", - "ShowProfileTypes", - "ShowProfileTypesOpt", - "ShowTargetFilterable", - "SimpleWhenThenList", - "spatial", - "SplitSyntaxOption", - "SpPdparams", - "ssl", - "Start", - "Starting", - "starting", - "StatementList", - "StatementScope", - "StorageMedia", - "stored", - "StringList", - "StringNameOrBRIEOptionKeyword", - "SubPartDefinitionList", - "SubPartDefinitionListOpt", - "SubPartitionNumOpt", - "SubPartitionOpt", - "TableElementListOpt", - "TableLockList", - "TableRefsClause", - "TableSampleMethodOpt", - "TableSampleOpt", - "TableSampleUnitOpt", - "TableToTableList", - "trailing", - "TrimDirection", - "UserToUserList", - "UserVariableList", - "UsingRoles", - "Values", - "ValuesOpt", - "ViewAlgorithm", - "ViewCheckOption", - "ViewDefiner", - "ViewFieldList", - "ViewName", - "ViewSQLSecurity", - "virtual", - "VirtualOrStored", - "WatchDurationOption", - "WhenClauseList", - "WindowClauseOptional", - "WindowDefinitionList", - "WindowFrameBetween", - "WindowFrameExtent", - "WindowFrameUnits", - "WindowNameOrSpec", - "WindowSpecDetails", - "WithReadLockOpt", - "WithRollupClause", - "WithValidation", - "WithValidationOpt", - "$default", - "andnot", - "AssignmentListOpt", - "ColumnDefList", - "CommaOpt", - "createTableSelect", - "empty", - "error", - "higherThanComma", - "higherThanParenthese", - "insertValues", - "invalid", - "lowerThanCharsetKwd", - "lowerThanComma", - "lowerThanCreateTableSelect", - "lowerThanEq", - "lowerThanFunction", - "lowerThanInsertValues", - "lowerThanKey", - "lowerThanLocal", - "lowerThanNot", - "lowerThanOn", - "lowerThanParenthese", - "lowerThanRemove", - "lowerThanSelectOpt", - "lowerThanSelectStmt", - "lowerThanSetKeyword", - "lowerThanStringLitToken", - "lowerThanValueKeyword", - "lowerThanWith", - "lowerThenOrder", - "neg", - "odbcDateType", - "odbcTimestampType", - "odbcTimeType", - "TableNameListOpt2", - "tableRefPriority", - } - - yyReductions = []struct{ xsym, components int }{ - {0, 1}, - {1465, 1}, - {904, 6}, - {904, 8}, - {904, 10}, - {904, 5}, - {904, 7}, - {904, 7}, - {904, 9}, - {1255, 1}, - {1255, 2}, - {1255, 3}, - {1441, 1}, - {1441, 1}, - {1441, 1}, - {1442, 1}, - {1442, 2}, - {1442, 3}, - {1257, 1}, - {1257, 1}, - {1257, 1}, - {1256, 1}, - {1256, 1}, - {1256, 1}, - {1041, 3}, - {1041, 3}, - {1041, 4}, - {1500, 0}, - {1500, 3}, - {1500, 3}, - {977, 3}, - {977, 3}, - {977, 1}, - {977, 3}, - {977, 5}, - {977, 4}, - {977, 3}, - {977, 5}, - {977, 4}, - {977, 3}, - {1440, 1}, - {1440, 2}, - {1440, 3}, - {1040, 3}, - {1238, 1}, - {1238, 2}, - {1238, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {976, 3}, - {865, 4}, - {865, 4}, - {865, 4}, - {865, 4}, - {1028, 3}, - {1028, 3}, - {1285, 3}, - {1285, 3}, - {1317, 1}, - {1317, 2}, - {1317, 4}, - {1317, 8}, - {1317, 8}, - {1317, 3}, - {1317, 3}, - {1317, 2}, - {1057, 0}, - {1057, 3}, - {1110, 1}, - {1110, 5}, - {1110, 6}, - {1110, 5}, - {1110, 5}, - {1110, 5}, - {1110, 6}, - {1110, 2}, - {1110, 5}, - {1110, 6}, - {1110, 8}, - {1110, 8}, - {1110, 1}, - {1110, 1}, - {1110, 3}, - {1110, 4}, - {1110, 5}, - {1110, 3}, - {1110, 4}, - {1110, 8}, - {1110, 4}, - {1110, 7}, - {1110, 3}, - {1110, 4}, - {1110, 4}, - {1110, 4}, - {1110, 4}, - {1110, 2}, - {1110, 2}, - {1110, 4}, - {1110, 4}, - {1110, 5}, - {1110, 3}, - {1110, 2}, - {1110, 2}, - {1110, 5}, - {1110, 6}, - {1110, 6}, - {1110, 8}, - {1110, 5}, - {1110, 5}, - {1110, 3}, - {1110, 3}, - {1110, 3}, - {1110, 5}, - {1110, 1}, - {1110, 1}, - {1110, 1}, - {1110, 1}, - {1110, 2}, - {1110, 2}, - {1110, 1}, - {1110, 1}, - {1110, 4}, - {1110, 3}, - {1110, 4}, - {1110, 1}, - {1110, 1}, - {1438, 0}, - {1438, 5}, - {927, 1}, - {927, 1}, - {1512, 0}, - {1512, 1}, - {1511, 2}, - {1511, 2}, - {971, 1}, - {971, 1}, - {972, 3}, - {972, 3}, - {972, 3}, - {972, 3}, - {972, 3}, - {986, 3}, - {986, 3}, - {1309, 2}, - {1309, 2}, - {923, 1}, - {923, 1}, - {1197, 0}, - {1197, 1}, - {975, 0}, - {975, 1}, - {1033, 0}, - {1033, 1}, - {1033, 2}, - {1316, 0}, - {1316, 1}, - {1315, 1}, - {1315, 3}, - {859, 1}, - {859, 3}, - {928, 0}, - {928, 1}, - {928, 2}, - {1290, 1}, - {1251, 3}, - {1484, 1}, - {1484, 3}, - {1296, 3}, - {1252, 3}, - {1487, 1}, - {1487, 3}, - {1301, 3}, - {1248, 5}, - {1248, 3}, - {1248, 4}, - {1177, 4}, - {1177, 5}, - {1177, 5}, - {1175, 4}, - {1176, 0}, - {1176, 2}, - {1174, 4}, - {1278, 6}, - {1278, 8}, - {1277, 6}, - {1277, 2}, - {1462, 0}, - {1462, 2}, - {1462, 1}, - {1462, 3}, - {845, 5}, - {845, 6}, - {845, 7}, - {845, 7}, - {845, 8}, - {845, 9}, - {845, 8}, - {845, 7}, - {845, 6}, - {845, 8}, - {1101, 0}, - {1101, 2}, - {1101, 2}, - {902, 0}, - {902, 2}, - {1318, 1}, - {1318, 3}, - {1112, 2}, - {1112, 2}, - {1112, 3}, - {1112, 3}, - {1112, 2}, - {1112, 2}, - {996, 3}, - {1027, 1}, - {1027, 3}, - {1515, 0}, - {1515, 1}, - {943, 1}, - {943, 2}, - {943, 2}, - {943, 2}, - {943, 4}, - {943, 5}, - {943, 6}, - {943, 4}, - {943, 5}, - {1113, 2}, - {1516, 1}, - {1516, 3}, - {953, 3}, - {953, 3}, - {821, 1}, - {821, 3}, - {821, 5}, - {906, 1}, - {906, 3}, - {1123, 0}, - {1123, 1}, - {1370, 0}, - {1370, 3}, - {980, 1}, - {980, 3}, - {1336, 0}, - {1336, 1}, - {1335, 1}, - {1335, 3}, - {1124, 1}, - {1124, 1}, - {1125, 0}, - {1125, 3}, - {846, 1}, - {846, 2}, - {1070, 0}, - {1070, 1}, - {915, 1}, - {915, 1}, - {1044, 1}, - {1044, 2}, - {1168, 0}, - {1168, 1}, - {1354, 2}, - {1354, 1}, - {1032, 2}, - {1032, 1}, - {1032, 1}, - {1032, 2}, - {1032, 3}, - {1032, 1}, - {1032, 2}, - {1032, 2}, - {1032, 3}, - {1032, 3}, - {1032, 2}, - {1032, 6}, - {1032, 6}, - {1032, 1}, - {1032, 2}, - {1032, 2}, - {1032, 2}, - {1032, 2}, - {1325, 0}, - {1325, 3}, - {1325, 5}, - {1470, 1}, - {1470, 1}, - {1470, 1}, - {1333, 1}, - {1333, 1}, - {1333, 1}, - {1048, 0}, - {1048, 2}, - {1499, 0}, - {1499, 1}, - {1499, 1}, - {1126, 1}, - {1126, 2}, - {1127, 0}, - {1127, 1}, - {1340, 7}, - {1340, 7}, - {1340, 7}, - {1340, 7}, - {1340, 8}, - {1340, 5}, - {1390, 2}, - {1390, 2}, - {1390, 2}, - {1391, 0}, - {1391, 1}, - {1011, 5}, - {1219, 3}, - {1220, 3}, - {1397, 0}, - {1397, 1}, - {1397, 1}, - {1397, 2}, - {1397, 2}, - {1249, 1}, - {1249, 1}, - {1249, 2}, - {1249, 2}, - {1249, 2}, - {1349, 1}, - {1349, 1}, - {1349, 1}, - {1349, 1}, - {999, 3}, - {999, 3}, - {999, 4}, - {1214, 3}, - {1214, 1}, - {1061, 1}, - {1061, 3}, - {1061, 4}, - {1061, 3}, - {1061, 1}, - {780, 4}, - {780, 4}, - {1060, 1}, - {1060, 1}, - {1060, 1}, - {1060, 1}, - {1059, 1}, - {1059, 1}, - {1059, 1}, - {1036, 1}, - {1036, 1}, - {1082, 1}, - {1082, 2}, - {1082, 2}, - {916, 1}, - {916, 1}, - {916, 1}, - {1287, 1}, - {1287, 1}, - {1287, 1}, - {1327, 1}, - {1327, 1}, - {1140, 12}, - {1159, 3}, - {1134, 13}, - {1375, 0}, - {1375, 3}, - {932, 1}, - {932, 3}, - {922, 3}, - {922, 4}, - {1193, 0}, - {1193, 1}, - {1193, 1}, - {1193, 2}, - {1193, 2}, - {1374, 0}, - {1374, 1}, - {1374, 1}, - {1374, 1}, - {1102, 4}, - {1102, 3}, - {1133, 5}, - {907, 1}, - {989, 1}, - {936, 1}, - {936, 1}, - {954, 4}, - {954, 4}, - {954, 4}, - {954, 2}, - {954, 1}, - {954, 5}, - {1346, 0}, - {1346, 1}, - {1037, 1}, - {1037, 2}, - {1035, 12}, - {1035, 7}, - {1218, 0}, - {1218, 4}, - {1218, 4}, - {891, 0}, - {891, 1}, - {1233, 0}, - {1233, 6}, - {1289, 6}, - {1289, 5}, - {1415, 0}, - {1415, 3}, - {1416, 1}, - {1416, 5}, - {1416, 6}, - {1416, 4}, - {1416, 5}, - {1416, 4}, - {1416, 3}, - {1416, 1}, - {1232, 0}, - {1232, 7}, - {1379, 1}, - {1379, 2}, - {1396, 0}, - {1396, 2}, - {1394, 0}, - {1394, 2}, - {1362, 0}, - {1362, 14}, - {1203, 0}, - {1203, 1}, - {1477, 0}, - {1477, 4}, - {1476, 0}, - {1476, 2}, - {1417, 0}, - {1417, 2}, - {1231, 0}, - {1231, 3}, - {1230, 1}, - {1230, 3}, - {1067, 5}, - {1475, 0}, - {1475, 3}, - {1474, 1}, - {1474, 3}, - {1288, 3}, - {1066, 0}, - {1066, 2}, - {910, 3}, - {910, 3}, - {910, 4}, - {910, 3}, - {910, 4}, - {910, 4}, - {910, 3}, - {910, 3}, - {910, 3}, - {910, 3}, - {910, 1}, - {1414, 0}, - {1414, 4}, - {1414, 6}, - {1414, 1}, - {1414, 5}, - {1414, 1}, - {1414, 1}, - {1164, 0}, - {1164, 1}, - {1164, 1}, - {1322, 0}, - {1322, 1}, - {1343, 0}, - {1343, 1}, - {1343, 1}, - {1343, 1}, - {1343, 1}, - {1344, 1}, - {1344, 1}, - {1344, 1}, - {1344, 1}, - {1384, 2}, - {1384, 4}, - {1143, 11}, - {1412, 0}, - {1412, 2}, - {1492, 0}, - {1492, 3}, - {1492, 3}, - {1492, 3}, - {1494, 0}, - {1494, 3}, - {1497, 0}, - {1497, 3}, - {1497, 3}, - {1496, 1}, - {1495, 0}, - {1495, 3}, - {1334, 1}, - {1334, 3}, - {1493, 0}, - {1493, 4}, - {1493, 4}, - {1148, 2}, - {823, 13}, - {823, 9}, - {833, 10}, - {839, 1}, - {839, 1}, - {839, 2}, - {839, 2}, - {929, 1}, - {1150, 4}, - {1151, 7}, - {1151, 7}, - {1161, 6}, - {1065, 0}, - {1065, 1}, - {1065, 2}, - {1163, 4}, - {1163, 6}, - {1162, 3}, - {1162, 5}, - {1157, 3}, - {1157, 5}, - {1160, 3}, - {1160, 5}, - {1160, 4}, - {1012, 0}, - {1012, 1}, - {1012, 1}, - {1294, 1}, - {1294, 1}, - {802, 0}, - {802, 1}, - {1166, 0}, - {1298, 2}, - {1298, 5}, - {1298, 3}, - {1298, 6}, - {858, 1}, - {858, 1}, - {858, 1}, - {857, 2}, - {857, 3}, - {857, 2}, - {857, 4}, - {857, 7}, - {857, 5}, - {857, 7}, - {857, 5}, - {857, 3}, - {857, 6}, - {857, 6}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {1170, 1}, - {967, 2}, - {965, 3}, - {1114, 5}, - {1114, 5}, - {1114, 3}, - {1114, 4}, - {1114, 3}, - {1114, 6}, - {1114, 4}, - {1114, 6}, - {1114, 4}, - {1114, 5}, - {1114, 4}, - {1114, 5}, - {1114, 5}, - {1114, 5}, - {1115, 2}, - {1115, 2}, - {1115, 2}, - {1347, 1}, - {1347, 3}, - {949, 0}, - {949, 2}, - {946, 1}, - {946, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {945, 1}, - {950, 1}, - {950, 1}, - {950, 1}, - {950, 1}, - {947, 1}, - {947, 1}, - {947, 2}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 5}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 6}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {948, 3}, - {816, 1}, - {825, 1}, - {799, 1}, - {998, 1}, - {998, 1}, - {998, 1}, - {1225, 1}, - {1225, 1}, - {1225, 1}, - {1237, 5}, - {1259, 5}, - {1119, 4}, - {1152, 5}, - {798, 3}, - {798, 3}, - {798, 3}, - {798, 3}, - {798, 2}, - {798, 9}, - {798, 3}, - {798, 3}, - {798, 3}, - {798, 1}, - {1147, 1}, - {1147, 1}, - {1212, 1}, - {1212, 1}, - {1366, 0}, - {1366, 4}, - {1366, 7}, - {1366, 3}, - {1366, 3}, - {801, 1}, - {801, 1}, - {800, 1}, - {800, 1}, - {863, 1}, - {863, 3}, - {1395, 1}, - {1395, 3}, - {1348, 1}, - {1348, 3}, - {921, 0}, - {921, 1}, - {1181, 0}, - {1181, 1}, - {1180, 1}, - {797, 3}, - {797, 3}, - {797, 4}, - {797, 5}, - {797, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1338, 1}, - {1326, 1}, - {1326, 2}, - {1381, 1}, - {1381, 2}, - {1377, 1}, - {1377, 2}, - {1383, 1}, - {1383, 2}, - {1372, 1}, - {1372, 2}, - {1437, 1}, - {1437, 2}, - {1319, 1}, - {1319, 1}, - {1319, 1}, - {796, 5}, - {796, 3}, - {796, 5}, - {796, 4}, - {796, 4}, - {796, 3}, - {796, 5}, - {796, 1}, - {1250, 1}, - {1250, 1}, - {1200, 0}, - {1200, 2}, - {1171, 1}, - {1171, 3}, - {1171, 5}, - {1171, 2}, - {1359, 0}, - {1359, 1}, - {1358, 1}, - {1358, 2}, - {1358, 1}, - {1358, 2}, - {1361, 1}, - {1361, 3}, - {1510, 0}, - {1510, 2}, - {1050, 4}, - {1187, 0}, - {1187, 2}, - {1321, 0}, - {1321, 1}, - {995, 3}, - {854, 0}, - {854, 2}, - {883, 0}, - {883, 3}, - {958, 0}, - {958, 1}, - {981, 0}, - {981, 1}, - {983, 0}, - {983, 2}, - {982, 3}, - {982, 1}, - {982, 3}, - {982, 2}, - {982, 1}, - {982, 1}, - {1053, 1}, - {1053, 3}, - {1053, 3}, - {1376, 0}, - {1376, 1}, - {961, 2}, - {961, 2}, - {1004, 1}, - {1004, 1}, - {1004, 1}, - {1004, 1}, - {959, 1}, - {959, 1}, - {770, 1}, - {770, 1}, - {770, 1}, - {770, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {773, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {772, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {771, 1}, - {1118, 2}, - {1424, 1}, - {1424, 3}, - {1424, 4}, - {1424, 6}, - {824, 9}, - {1196, 0}, - {1196, 1}, - {1195, 5}, - {1195, 4}, - {1195, 4}, - {1195, 4}, - {1195, 4}, - {1195, 2}, - {1195, 1}, - {1195, 1}, - {1195, 1}, - {1195, 1}, - {1195, 2}, - {1095, 1}, - {1095, 1}, - {1093, 1}, - {1093, 3}, - {938, 3}, - {1491, 0}, - {1491, 1}, - {1490, 3}, - {1490, 1}, - {893, 1}, - {893, 1}, - {1337, 3}, - {1337, 5}, - {1398, 0}, - {1398, 5}, - {826, 6}, - {777, 1}, - {777, 1}, - {777, 1}, - {777, 1}, - {777, 1}, - {777, 1}, - {777, 1}, - {777, 2}, - {777, 1}, - {777, 1}, - {777, 2}, - {777, 2}, - {779, 1}, - {779, 2}, - {1313, 1}, - {1313, 3}, - {1104, 2}, - {842, 3}, - {1000, 1}, - {1000, 3}, - {973, 1}, - {973, 2}, - {1411, 1}, - {1411, 1}, - {1064, 0}, - {1064, 1}, - {1064, 1}, - {909, 0}, - {909, 1}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 5}, - {795, 5}, - {795, 5}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 3}, - {795, 1}, - {778, 1}, - {778, 3}, - {778, 5}, - {790, 1}, - {790, 1}, - {790, 1}, - {790, 1}, - {790, 3}, - {790, 1}, - {790, 1}, - {790, 1}, - {790, 1}, - {790, 1}, - {790, 2}, - {790, 2}, - {790, 2}, - {790, 2}, - {790, 3}, - {790, 2}, - {790, 1}, - {790, 3}, - {790, 5}, - {790, 6}, - {790, 2}, - {790, 4}, - {790, 2}, - {790, 7}, - {790, 5}, - {790, 6}, - {790, 6}, - {790, 4}, - {790, 4}, - {790, 3}, - {790, 3}, - {1320, 0}, - {1320, 1}, - {885, 1}, - {885, 1}, - {887, 1}, - {887, 1}, - {913, 0}, - {913, 1}, - {1039, 0}, - {1039, 1}, - {912, 1}, - {912, 2}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {784, 1}, - {1224, 0}, - {1224, 2}, - {788, 1}, - {788, 1}, - {788, 1}, - {788, 1}, - {788, 1}, - {787, 1}, - {787, 1}, - {787, 1}, - {787, 1}, - {787, 1}, - {787, 1}, - {782, 4}, - {782, 4}, - {782, 2}, - {782, 3}, - {782, 2}, - {782, 4}, - {782, 6}, - {782, 2}, - {782, 2}, - {782, 2}, - {782, 4}, - {782, 6}, - {782, 4}, - {783, 4}, - {783, 4}, - {783, 6}, - {783, 8}, - {783, 8}, - {783, 6}, - {783, 6}, - {783, 6}, - {783, 6}, - {783, 6}, - {783, 8}, - {783, 8}, - {783, 8}, - {783, 8}, - {783, 4}, - {783, 6}, - {783, 6}, - {783, 7}, - {783, 4}, - {783, 7}, - {783, 7}, - {783, 1}, - {783, 8}, - {1368, 1}, - {1368, 1}, - {1368, 1}, - {1368, 1}, - {785, 1}, - {785, 1}, - {786, 1}, - {786, 1}, - {1486, 1}, - {1486, 1}, - {1486, 1}, - {789, 4}, - {789, 6}, - {789, 1}, - {791, 6}, - {791, 4}, - {791, 4}, - {791, 5}, - {791, 6}, - {791, 5}, - {791, 6}, - {791, 5}, - {791, 6}, - {791, 5}, - {791, 6}, - {791, 5}, - {791, 5}, - {791, 8}, - {791, 6}, - {791, 6}, - {791, 6}, - {791, 6}, - {791, 6}, - {791, 6}, - {791, 6}, - {791, 5}, - {791, 6}, - {791, 7}, - {791, 8}, - {791, 8}, - {791, 9}, - {1403, 0}, - {1403, 2}, - {781, 4}, - {781, 6}, - {1367, 0}, - {1367, 2}, - {1367, 3}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {901, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {886, 1}, - {1356, 0}, - {1356, 1}, - {1501, 1}, - {1501, 2}, - {1303, 4}, - {1353, 0}, - {1353, 2}, - {1120, 2}, - {1120, 3}, - {1120, 1}, - {1120, 1}, - {1120, 2}, - {1120, 2}, - {1120, 2}, - {1120, 2}, - {1120, 2}, - {1120, 1}, - {1120, 1}, - {1120, 2}, - {1120, 1}, - {934, 1}, - {934, 1}, - {934, 1}, - {990, 0}, - {990, 1}, - {804, 1}, - {804, 3}, - {884, 1}, - {884, 3}, - {1018, 2}, - {1018, 4}, - {1084, 1}, - {1084, 3}, - {1008, 0}, - {1008, 2}, - {1247, 0}, - {1247, 1}, - {1240, 4}, - {1422, 1}, - {1422, 1}, - {1169, 2}, - {1169, 4}, - {1488, 1}, - {1488, 3}, - {1145, 3}, - {1146, 1}, - {1146, 1}, - {847, 1}, - {847, 2}, - {847, 3}, - {847, 4}, - {1129, 4}, - {1129, 4}, - {1129, 5}, - {1129, 2}, - {1129, 3}, - {1129, 1}, - {1129, 2}, - {1275, 1}, - {1258, 1}, - {1188, 2}, - {807, 4}, - {808, 3}, - {809, 7}, - {1482, 0}, - {1482, 7}, - {1482, 5}, - {1481, 0}, - {1481, 1}, - {1481, 1}, - {1481, 1}, - {1483, 0}, - {1483, 1}, - {1483, 1}, - {1253, 0}, - {1253, 4}, - {806, 7}, - {806, 6}, - {806, 5}, - {806, 6}, - {806, 6}, - {817, 2}, - {817, 2}, - {815, 2}, - {815, 3}, - {1308, 3}, - {1308, 1}, - {1034, 4}, - {1365, 2}, - {1502, 0}, - {1502, 2}, - {1503, 1}, - {1503, 3}, - {1304, 3}, - {1026, 1}, - {1306, 3}, - {1508, 4}, - {1401, 0}, - {1401, 1}, - {1405, 0}, - {1405, 3}, - {1410, 0}, - {1410, 3}, - {1409, 0}, - {1409, 2}, - {1506, 1}, - {1506, 1}, - {1506, 1}, - {1505, 1}, - {1505, 1}, - {1097, 2}, - {1097, 2}, - {1097, 2}, - {1097, 4}, - {1097, 2}, - {1504, 4}, - {1305, 1}, - {1305, 2}, - {1305, 2}, - {1305, 2}, - {1305, 4}, - {844, 0}, - {844, 1}, - {832, 2}, - {1507, 1}, - {1507, 1}, - {794, 4}, - {794, 4}, - {794, 4}, - {794, 4}, - {794, 4}, - {794, 5}, - {794, 7}, - {794, 7}, - {794, 6}, - {794, 6}, - {794, 9}, - {1226, 0}, - {1226, 3}, - {1226, 3}, - {1227, 0}, - {1227, 2}, - {988, 0}, - {988, 2}, - {988, 2}, - {1402, 0}, - {1402, 2}, - {1402, 2}, - {1480, 1}, - {993, 1}, - {993, 3}, - {955, 1}, - {955, 4}, - {900, 1}, - {900, 1}, - {899, 6}, - {899, 2}, - {899, 3}, - {963, 0}, - {963, 4}, - {1017, 0}, - {1017, 1}, - {1016, 1}, - {1016, 2}, - {1052, 2}, - {1052, 2}, - {1052, 2}, - {1373, 0}, - {1373, 2}, - {1373, 3}, - {1373, 3}, - {1051, 5}, - {960, 0}, - {960, 1}, - {960, 3}, - {960, 1}, - {960, 3}, - {1191, 1}, - {1191, 2}, - {1192, 0}, - {1192, 1}, - {895, 3}, - {895, 5}, - {895, 7}, - {895, 7}, - {895, 9}, - {895, 4}, - {895, 6}, - {895, 3}, - {895, 5}, - {914, 1}, - {914, 1}, - {1229, 0}, - {1229, 1}, - {919, 1}, - {919, 2}, - {919, 2}, - {1201, 0}, - {1201, 2}, - {985, 1}, - {985, 1}, - {1444, 1}, - {1444, 1}, - {1363, 1}, - {1363, 1}, - {1357, 0}, - {1357, 1}, - {843, 2}, - {843, 4}, - {843, 4}, - {843, 5}, - {925, 0}, - {925, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1266, 1}, - {1447, 0}, - {1447, 1}, - {1448, 2}, - {1448, 1}, - {969, 1}, - {1019, 0}, - {1019, 1}, - {1267, 1}, - {1267, 1}, - {1446, 1}, - {1080, 0}, - {1080, 1}, - {992, 0}, - {992, 5}, - {775, 3}, - {775, 3}, - {775, 3}, - {775, 3}, - {991, 0}, - {991, 3}, - {991, 3}, - {991, 4}, - {991, 5}, - {991, 4}, - {991, 5}, - {991, 5}, - {991, 4}, - {1217, 0}, - {1217, 2}, - {818, 1}, - {818, 1}, - {818, 2}, - {818, 2}, - {813, 3}, - {813, 3}, - {812, 4}, - {812, 4}, - {812, 5}, - {812, 2}, - {812, 2}, - {812, 3}, - {811, 1}, - {811, 3}, - {810, 1}, - {810, 1}, - {1450, 2}, - {1450, 2}, - {1450, 2}, - {1081, 1}, - {1121, 9}, - {1121, 9}, - {848, 2}, - {848, 4}, - {848, 6}, - {848, 4}, - {848, 4}, - {848, 3}, - {848, 6}, - {848, 6}, - {848, 3}, - {848, 4}, - {1271, 3}, - {1270, 6}, - {1269, 1}, - {1269, 1}, - {1269, 1}, - {1451, 3}, - {1451, 1}, - {1451, 1}, - {1087, 1}, - {1087, 3}, - {1023, 3}, - {1023, 2}, - {1023, 2}, - {1023, 3}, - {1380, 2}, - {1380, 2}, - {1380, 2}, - {1380, 1}, - {939, 1}, - {939, 1}, - {939, 1}, - {892, 1}, - {892, 1}, - {926, 1}, - {926, 3}, - {1001, 1}, - {1001, 3}, - {1001, 3}, - {1096, 3}, - {1096, 4}, - {1096, 4}, - {1096, 4}, - {1096, 3}, - {1096, 3}, - {1096, 2}, - {1096, 4}, - {1096, 4}, - {1096, 2}, - {1096, 2}, - {1331, 1}, - {1331, 1}, - {905, 1}, - {905, 1}, - {974, 1}, - {974, 1}, - {1302, 1}, - {1302, 3}, - {793, 1}, - {793, 1}, - {792, 1}, - {776, 1}, - {855, 1}, - {855, 3}, - {855, 2}, - {855, 2}, - {970, 1}, - {970, 3}, - {1234, 1}, - {1234, 4}, - {997, 1}, - {918, 1}, - {918, 1}, - {898, 3}, - {898, 2}, - {1078, 1}, - {1078, 1}, - {917, 1}, - {917, 1}, - {966, 1}, - {966, 3}, - {1312, 2}, - {1312, 4}, - {1312, 4}, - {1100, 3}, - {1100, 5}, - {1100, 6}, - {1100, 4}, - {1100, 4}, - {1100, 5}, - {1100, 5}, - {1100, 5}, - {1100, 6}, - {1100, 4}, - {1100, 5}, - {1100, 5}, - {1100, 5}, - {1100, 6}, - {1100, 6}, - {1100, 4}, - {1100, 3}, - {1100, 3}, - {1100, 4}, - {1100, 4}, - {1100, 5}, - {1100, 5}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 3}, - {1100, 4}, - {1311, 2}, - {1311, 2}, - {1311, 3}, - {1311, 3}, - {1369, 1}, - {1369, 3}, - {1185, 5}, - {1005, 1}, - {1005, 3}, - {1273, 3}, - {1273, 4}, - {1273, 4}, - {1273, 5}, - {1273, 4}, - {1273, 5}, - {1273, 5}, - {1273, 4}, - {1273, 6}, - {1273, 4}, - {1273, 8}, - {1273, 2}, - {1273, 5}, - {1273, 3}, - {1273, 3}, - {1273, 2}, - {1273, 5}, - {1273, 2}, - {1273, 2}, - {1273, 4}, - {1273, 4}, - {1273, 4}, - {1455, 2}, - {1455, 2}, - {1455, 4}, - {1458, 0}, - {1458, 1}, - {1457, 1}, - {1457, 3}, - {1272, 1}, - {1272, 1}, - {1272, 2}, - {1272, 2}, - {1272, 2}, - {1272, 1}, - {1272, 1}, - {1272, 1}, - {1272, 1}, - {1456, 0}, - {1456, 3}, - {1489, 0}, - {1489, 2}, - {1453, 1}, - {1453, 1}, - {1453, 1}, - {903, 1}, - {903, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 3}, - {1459, 3}, - {1459, 3}, - {1459, 3}, - {1459, 5}, - {1459, 4}, - {1459, 5}, - {1459, 5}, - {1459, 1}, - {1459, 5}, - {1459, 1}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 1}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 2}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 2}, - {1459, 1}, - {1459, 1}, - {1459, 1}, - {1459, 2}, - {1459, 2}, - {1454, 0}, - {1454, 2}, - {1454, 2}, - {1049, 0}, - {1049, 1}, - {1049, 1}, - {1469, 0}, - {1469, 1}, - {1469, 1}, - {1469, 1}, - {1222, 0}, - {1222, 1}, - {940, 0}, - {940, 2}, - {1274, 2}, - {1178, 3}, - {1069, 1}, - {1069, 3}, - {1364, 1}, - {1364, 1}, - {1364, 3}, - {1364, 1}, - {1364, 2}, - {1364, 3}, - {1364, 1}, - {1389, 0}, - {1389, 1}, - {1389, 1}, - {1389, 1}, - {1389, 1}, - {1389, 1}, - {924, 0}, - {924, 1}, - {924, 1}, - {1293, 0}, - {1293, 1}, - {1548, 0}, - {1548, 2}, - {1509, 0}, - {1509, 3}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1284, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {1022, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {920, 1}, - {1468, 1}, - {1468, 3}, - {1002, 2}, - {1122, 1}, - {1122, 1}, - {1085, 1}, - {1085, 1}, - {1291, 1}, - {1291, 3}, - {1478, 0}, - {1478, 3}, - {941, 1}, - {941, 4}, - {941, 4}, - {941, 4}, - {941, 3}, - {941, 4}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 1}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 3}, - {941, 2}, - {941, 2}, - {941, 3}, - {941, 3}, - {941, 5}, - {941, 3}, - {941, 7}, - {941, 3}, - {941, 3}, - {931, 0}, - {931, 1}, - {1286, 1}, - {1286, 1}, - {1141, 0}, - {1141, 1}, - {1020, 1}, - {1020, 2}, - {1020, 3}, - {1407, 0}, - {1407, 1}, - {860, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {937, 3}, - {1089, 1}, - {1089, 1}, - {1089, 1}, - {1062, 3}, - {1062, 2}, - {1062, 3}, - {1062, 3}, - {1062, 2}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1054, 1}, - {1031, 1}, - {1031, 1}, - {1223, 0}, - {1223, 1}, - {1223, 1}, - {1046, 1}, - {1046, 1}, - {1046, 1}, - {1047, 1}, - {1047, 1}, - {1047, 1}, - {1047, 2}, - {1047, 1}, - {1047, 1}, - {1029, 1}, - {1083, 3}, - {1083, 2}, - {1083, 3}, - {1083, 2}, - {1083, 3}, - {1083, 3}, - {1083, 2}, - {1083, 2}, - {1083, 1}, - {1083, 2}, - {1083, 5}, - {1083, 5}, - {1083, 1}, - {1083, 3}, - {1083, 2}, - {951, 1}, - {951, 1}, - {1058, 1}, - {1058, 2}, - {1058, 2}, - {1025, 2}, - {1025, 2}, - {1025, 1}, - {1025, 1}, - {1063, 2}, - {1063, 2}, - {1063, 1}, - {1063, 2}, - {1063, 2}, - {1063, 3}, - {1063, 3}, - {1063, 2}, - {1098, 1}, - {1098, 1}, - {1030, 1}, - {1030, 2}, - {1030, 1}, - {1030, 1}, - {1030, 2}, - {1086, 1}, - {1086, 2}, - {1086, 1}, - {1086, 1}, - {987, 1}, - {987, 1}, - {987, 1}, - {987, 1}, - {1038, 1}, - {1038, 2}, - {1038, 2}, - {1038, 2}, - {1038, 3}, - {841, 3}, - {888, 0}, - {888, 1}, - {978, 1}, - {978, 1}, - {978, 1}, - {979, 0}, - {979, 2}, - {1003, 0}, - {1003, 1}, - {1003, 1}, - {1010, 5}, - {1399, 0}, - {1399, 1}, - {896, 0}, - {896, 2}, - {896, 3}, - {1400, 0}, - {1400, 2}, - {853, 2}, - {853, 1}, - {853, 2}, - {1221, 0}, - {1221, 2}, - {1472, 1}, - {1472, 3}, - {1021, 1}, - {1021, 1}, - {1021, 1}, - {1297, 1}, - {1297, 3}, - {805, 1}, - {805, 1}, - {1473, 1}, - {1473, 1}, - {1473, 1}, - {827, 1}, - {827, 2}, - {822, 10}, - {822, 8}, - {861, 2}, - {889, 2}, - {890, 0}, - {890, 1}, - {1517, 0}, - {1517, 1}, - {1142, 9}, - {1138, 4}, - {1111, 9}, - {1111, 9}, - {1103, 3}, - {1106, 4}, - {1378, 2}, - {1378, 6}, - {994, 2}, - {1024, 1}, - {1024, 3}, - {1131, 0}, - {1131, 2}, - {1339, 1}, - {1339, 2}, - {1130, 2}, - {1130, 2}, - {1130, 2}, - {1130, 2}, - {1076, 0}, - {1076, 1}, - {1075, 2}, - {1075, 2}, - {1075, 2}, - {1075, 2}, - {1439, 1}, - {1439, 3}, - {1439, 2}, - {1077, 2}, - {1077, 2}, - {1077, 2}, - {1077, 2}, - {1077, 2}, - {1128, 0}, - {1128, 2}, - {1128, 2}, - {1254, 0}, - {1254, 3}, - {1236, 0}, - {1236, 1}, - {1235, 1}, - {1235, 2}, - {1068, 2}, - {1068, 2}, - {1068, 3}, - {1068, 3}, - {1068, 4}, - {1068, 5}, - {1068, 2}, - {1068, 5}, - {1068, 3}, - {1068, 3}, - {1068, 2}, - {1068, 2}, - {1068, 2}, - {1323, 0}, - {1323, 3}, - {1323, 3}, - {1323, 5}, - {1323, 5}, - {1323, 4}, - {1324, 1}, - {1186, 1}, - {1186, 1}, - {1264, 1}, - {1443, 1}, - {1443, 3}, - {944, 1}, - {944, 1}, - {944, 1}, - {944, 1}, - {944, 1}, - {944, 1}, - {944, 1}, - {944, 1}, - {1132, 7}, - {1132, 9}, - {1149, 5}, - {1149, 7}, - {1149, 7}, - {1268, 5}, - {1268, 7}, - {1268, 7}, - {1184, 9}, - {1182, 7}, - {1183, 4}, - {1307, 0}, - {1307, 3}, - {1307, 3}, - {1307, 3}, - {1307, 3}, - {1307, 3}, - {1045, 1}, - {1045, 2}, - {1079, 1}, - {1079, 1}, - {1079, 1}, - {1079, 3}, - {1079, 3}, - {1263, 1}, - {1263, 3}, - {1071, 1}, - {1071, 4}, - {1072, 1}, - {1072, 2}, - {1072, 1}, - {1072, 1}, - {1072, 2}, - {1072, 2}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 2}, - {1072, 1}, - {1072, 2}, - {1072, 1}, - {1072, 2}, - {1072, 2}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 1}, - {1072, 3}, - {1072, 2}, - {1072, 2}, - {1072, 2}, - {1072, 2}, - {1072, 2}, - {1072, 2}, - {1072, 2}, - {1072, 1}, - {1072, 1}, - {1215, 0}, - {1215, 1}, - {1215, 1}, - {1215, 1}, - {1241, 1}, - {1241, 3}, - {1241, 3}, - {1241, 3}, - {1241, 1}, - {1262, 7}, - {1261, 4}, - {962, 17}, - {1179, 0}, - {1179, 2}, - {1371, 0}, - {1371, 3}, - {1332, 0}, - {1332, 3}, - {1209, 0}, - {1209, 1}, - {1173, 0}, - {1173, 2}, - {930, 1}, - {930, 1}, - {1360, 2}, - {1360, 1}, - {1172, 3}, - {1172, 2}, - {1172, 3}, - {1172, 3}, - {1172, 4}, - {1172, 6}, - {956, 1}, - {956, 1}, - {956, 1}, - {1056, 0}, - {1056, 3}, - {1466, 0}, - {1466, 3}, - {1385, 0}, - {1385, 3}, - {1207, 0}, - {1207, 2}, - {1387, 3}, - {1387, 1}, - {1206, 3}, - {1205, 0}, - {1205, 2}, - {1386, 1}, - {1386, 3}, - {1204, 1}, - {1204, 3}, - {1189, 9}, - {1300, 2}, - {1211, 3}, - {1295, 1}, - {1295, 1}, - {1292, 2}, - {1388, 1}, - {1388, 2}, - {1388, 1}, - {1388, 2}, - {1479, 1}, - {1479, 3}, - {1213, 6}, - {1452, 1}, - {1452, 1}, - {1452, 1}, - {1452, 1}, - {1350, 0}, - {1350, 2}, - {1350, 3}, - {1404, 0}, - {1404, 2}, - {1199, 2}, - {1199, 3}, - {1199, 3}, - {1199, 2}, - {1198, 1}, - {1198, 2}, - {1208, 3}, - {1210, 3}, - {1210, 5}, - {1210, 7}, - {1299, 3}, - {1299, 5}, - {1299, 7}, - {1153, 5}, - {1137, 6}, - {1107, 6}, - {1156, 5}, - {1135, 7}, - {1105, 6}, - {1139, 6}, - {1342, 0}, - {1342, 1}, - {1449, 1}, - {1449, 2}, - {1014, 3}, - {1014, 3}, - {1014, 3}, - {1014, 3}, - {1014, 3}, - {1014, 1}, - {1014, 2}, - {1014, 3}, - {1014, 1}, - {1014, 2}, - {1014, 3}, - {1014, 1}, - {1014, 2}, - {1014, 1}, - {1014, 1}, - {1014, 2}, - {911, 1}, - {911, 2}, - {911, 2}, - {1158, 4}, - {1109, 5}, - {1314, 1}, - {1314, 2}, - {1108, 1}, - {1108, 1}, - {1108, 3}, - {1108, 3}, - {1190, 8}, - {1393, 0}, - {1393, 2}, - {1392, 0}, - {1392, 3}, - {1419, 0}, - {1419, 2}, - {1418, 0}, - {1418, 2}, - {1167, 1}, - {1094, 1}, - {1094, 3}, - {1013, 2}, - {1239, 6}, - {1239, 7}, - {1239, 10}, - {1239, 11}, - {1239, 6}, - {1239, 7}, - {1239, 4}, - {1239, 5}, - {1239, 6}, - {1420, 0}, - {1420, 3}, - {1406, 0}, - {1406, 1}, - {1463, 3}, - {1463, 1}, - {1280, 3}, - {1279, 0}, - {1279, 1}, - {1279, 1}, - {1279, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {880, 1}, - {1425, 1}, - {1425, 1}, - {1425, 1}, - {1425, 1}, - {881, 1}, - {1426, 1}, - {1426, 3}, - {1432, 0}, - {1432, 2}, - {1244, 4}, - {1244, 5}, - {1244, 6}, - {1430, 1}, - {1430, 1}, - {1431, 1}, - {1431, 3}, - {1245, 1}, - {1245, 1}, - {1245, 2}, - {1245, 1}, - {1242, 1}, - {1242, 3}, - {1408, 0}, - {1408, 1}, - {876, 2}, - {870, 5}, - {869, 2}, - {1433, 0}, - {1433, 2}, - {1433, 1}, - {1429, 1}, - {1429, 3}, - {1428, 0}, - {1428, 1}, - {1427, 2}, - {1427, 3}, - {1434, 0}, - {1434, 3}, - {935, 2}, - {935, 3}, - {866, 4}, - {871, 4}, - {1246, 4}, - {1423, 0}, - {1423, 2}, - {1423, 2}, - {868, 1}, - {868, 1}, - {1460, 1}, - {1460, 2}, - {1445, 1}, - {1445, 2}, - {1276, 4}, - {1265, 4}, - {1165, 0}, - {1165, 2}, - {879, 6}, - {878, 5}, - {882, 1}, - {867, 6}, - {867, 6}, - {873, 4}, - {1243, 0}, - {1243, 1}, - {874, 4}, - {872, 2}, - {875, 2}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {877, 1}, - {1136, 8}, - {1154, 4}, - {1116, 3}, - {1329, 0}, - {1329, 1}, - {1329, 1}, - {1352, 1}, - {1352, 2}, - {1352, 3}, - {1042, 3}, - {1042, 3}, - {1042, 3}, - {1042, 5}, - {1330, 2}, - {1330, 2}, - {1330, 2}, - {1330, 2}, - {1330, 2}, - {1099, 4}, - {1435, 1}, - {1435, 2}, - {1435, 3}, - {1073, 3}, - {1073, 3}, - {1073, 3}, - {1073, 1}, - {1074, 3}, - {1074, 3}, - {1074, 5}, - {1155, 4}, - } - - yyXErrors = map[yyXError]string{} - - yyParseTab = [4917][]uint16{ - // 0 - {2314, 2314, 3: 2861, 58: 2884, 84: 2863, 2866, 87: 2896, 2864, 3017, 103: 2898, 117: 3031, 159: 3033, 187: 2881, 195: 2879, 208: 3024, 222: 2892, 250: 2887, 254: 2869, 259: 2917, 266: 2883, 269: 2859, 277: 2916, 3027, 2865, 284: 3032, 296: 2895, 306: 2893, 308: 2860, 310: 2899, 330: 2885, 334: 2888, 341: 2897, 344: 2882, 357: 2874, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 2915, 562: 3026, 575: 3020, 577: 2877, 582: 2875, 587: 2890, 608: 2904, 695: 2900, 710: 3030, 713: 2862, 3019, 724: 2857, 727: 2868, 743: 2867, 766: 2914, 2858, 775: 2911, 803: 2870, 806: 2913, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 2995, 2994, 822: 3018, 2871, 2976, 826: 2988, 3004, 2876, 833: 2872, 839: 2934, 845: 2928, 2932, 2985, 2996, 857: 2936, 2878, 860: 3003, 3005, 894: 3023, 897: 2880, 904: 2921, 933: 3029, 943: 2929, 957: 3021, 962: 2979, 965: 2990, 967: 2993, 2886, 1035: 2941, 1090: 3025, 1099: 2949, 2919, 1102: 2920, 2923, 1105: 2926, 2924, 2927, 1109: 2925, 1111: 2922, 1113: 2930, 2931, 1116: 2937, 2889, 2974, 3014, 1121: 2938, 1132: 2945, 2939, 2940, 2946, 2947, 2948, 2944, 2950, 2951, 1142: 2943, 2942, 1145: 2933, 2894, 1148: 2952, 2966, 2953, 2954, 3015, 2957, 2956, 2962, 2961, 2963, 2958, 2964, 2965, 2955, 2960, 2959, 1166: 2918, 1169: 2935, 1174: 2970, 2968, 1177: 2969, 2967, 1182: 2972, 2973, 2971, 1188: 3010, 2975, 2977, 1198: 3028, 2978, 1208: 2980, 1210: 2981, 3007, 1213: 3011, 1237: 3012, 1239: 2983, 2984, 1248: 2989, 1251: 2986, 2987, 1258: 3009, 3013, 3022, 2992, 2991, 1268: 2997, 1270: 2999, 2998, 1273: 3001, 1275: 3008, 1278: 3000, 1284: 3016, 1298: 3002, 2982, 3006, 1465: 2855, 1468: 2856}, - {1: 2854}, - {7769, 2853}, - {18: 7722, 51: 7721, 217: 7718, 244: 7723, 316: 7719, 549: 4675, 591: 7720, 608: 2115, 644: 6649, 929: 7717, 958: 4674}, - {217: 7702, 608: 7701}, - // 5 - {608: 7695}, - {375: 7679, 608: 7680, 644: 6649, 929: 7681}, - {427: 7660, 546: 7661, 608: 2659, 1462: 7659}, - {397: 7615, 608: 7614}, - {2627, 2627, 413: 7613, 420: 7612}, - // 10 - {453: 7601}, - {533: 7600}, - {2594, 2594, 86: 6564, 566: 6562, 897: 6563, 1129: 7599}, - {18: 2365, 51: 7129, 102: 2365, 132: 2365, 181: 2365, 202: 790, 206: 7046, 216: 6150, 7126, 224: 7127, 244: 7130, 6806, 273: 7118, 567: 7125, 608: 2333, 644: 6649, 696: 2365, 705: 7120, 710: 2472, 747: 7122, 929: 7123, 964: 7131, 1049: 7128, 1065: 6149, 1374: 7119, 1412: 7124, 1461: 7121}, - {18: 7053, 51: 7054, 132: 7047, 157: 2333, 202: 790, 206: 7046, 7044, 216: 6150, 7048, 222: 1233, 224: 7049, 7050, 244: 7055, 6806, 273: 7041, 608: 2333, 644: 6649, 710: 7043, 894: 7051, 929: 7042, 964: 7056, 1049: 7052, 1065: 7045}, - // 15 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 7040}, - {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 561: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 7028}, - {2342, 2342}, - {2341, 2341}, - {531: 2907, 547: 2905, 608: 2904, 695: 2900, 714: 3019, 775: 3854, 803: 2870, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856, 822: 5705, 5703, 833: 5704}, - // 20 - {84: 2863, 2866, 87: 2896, 2864, 117: 7001, 195: 2879, 232: 7000, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 6999}, - {2: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 10: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 58: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 531: 2309, 2309, 547: 2309, 552: 2309, 558: 2309, 587: 2309, 608: 2309, 695: 2309, 713: 2309, 2309, 724: 2309, 803: 2309}, - {2: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 10: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 58: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 531: 2308, 2308, 547: 2308, 552: 2308, 558: 2308, 587: 2308, 608: 2308, 695: 2308, 713: 2308, 2308, 724: 2308, 803: 2308}, - {2: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 10: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 58: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 531: 2307, 2307, 547: 2307, 552: 2307, 558: 2307, 587: 2307, 608: 2307, 695: 2307, 713: 2307, 2307, 724: 2307, 803: 2307}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 6968, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2907, 2906, 547: 2905, 552: 2891, 558: 6967, 587: 2890, 608: 2904, 695: 2900, 713: 6969, 3019, 724: 4646, 770: 3927, 3051, 3052, 3050, 775: 4647, 803: 2870, 6965, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6966}, - // 25 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6964, 3051, 3052, 3050}, - {195: 6962}, - {155: 6955, 608: 6653, 644: 6649, 929: 6652, 1115: 6954}, - {187: 6952}, - {187: 6945, 894: 6946}, - // 30 - {187: 6939, 894: 6940}, - {187: 6934}, - {16: 4418, 18: 6767, 30: 6797, 6796, 92: 6776, 131: 783, 154: 783, 156: 790, 783, 180: 790, 187: 6755, 206: 6805, 208: 6768, 240: 6765, 245: 6806, 248: 790, 260: 6807, 267: 6791, 783, 281: 6756, 302: 6788, 314: 6781, 329: 6787, 362: 6780, 367: 6803, 369: 6785, 6766, 376: 6783, 6801, 379: 6774, 386: 6772, 6790, 391: 6778, 394: 6789, 6760, 6800, 398: 6770, 405: 6761, 423: 6764, 6763, 430: 6804, 436: 6792, 439: 6798, 6795, 6799, 6794, 454: 6784, 553: 4419, 608: 6759, 655: 6779, 709: 4417, 6769, 713: 6802, 743: 6758, 853: 6775, 964: 6786, 1015: 6793, 1049: 6782, 1055: 6771, 1144: 6773, 1222: 6762, 1453: 6777, 1459: 6757}, - {208: 6750, 281: 6749}, - {421: 6651, 608: 6653, 644: 6649, 929: 6652, 1115: 6650}, - // 35 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6638, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6640, 3051, 3052, 3050, 1424: 6639}, - {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 559: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 6625}, - {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 559: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6590}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6585, 3051, 3052, 3050}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6579, 3051, 3052, 3050}, - // 40 - {222: 6577}, - {222: 1234}, - {1232, 1232, 86: 6564, 566: 6562, 712: 6561, 897: 6563, 1129: 6560}, - {1221, 1221}, - {1220, 1220}, - // 45 - {533: 6559}, - {2: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 10: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 58: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 6529, 6535, 6536, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 531: 1057, 533: 1057, 1057, 1057, 1057, 540: 1057, 1057, 543: 1057, 1057, 1057, 547: 1057, 1057, 552: 1057, 1057, 560: 1057, 573: 6532, 578: 1057, 584: 1057, 586: 1057, 1057, 614: 1057, 617: 1057, 628: 1057, 1057, 1057, 1057, 636: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 697: 1057, 1057, 1057, 1057, 711: 1057, 715: 4174, 829: 4172, 4173, 836: 5961, 5960, 5959, 840: 5956, 849: 6528, 6531, 6527, 885: 6447, 887: 6525, 934: 6526, 969: 6524, 1266: 6534, 6530, 1447: 6523, 6533}, - {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 561: 6498, 424, 4661, 424, 571: 424, 889: 4662, 6499, 1365: 6497}, - {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1050: 6487, 1080: 6486}, - {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6481}, - // 50 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6476}, - {639: 3892, 1013: 3891, 1094: 3890}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6462, 1308: 6460, 1436: 6461}, - {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 6459, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848}, - {1028, 1028, 57: 1028, 530: 1028, 532: 1028, 542: 1028}, - // 55 - {1027, 1027, 57: 1027, 530: 1027, 532: 1027, 542: 1027}, - {539: 6444, 550: 6445, 6446, 1450: 6443}, - {674, 674, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {539: 1016, 550: 1016, 1016}, - {676, 676, 539: 1014, 550: 1014, 1014}, - // 60 - {302: 6428, 329: 6427}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 6265, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 6266, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 6270, 3069, 3070, 3102, 6262, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 6267, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6268, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 6261, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 6271, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6269, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 6273, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6279, 1302: 6272}, - {17: 6125, 58: 6128, 250: 6126, 259: 6132, 266: 6127, 6130, 269: 6123, 6131, 285: 6133, 333: 6129, 373: 6124, 388: 6134, 429: 6135, 702: 6122, 968: 6121}, - {23: 762, 155: 762, 762, 762, 173: 5251, 240: 762, 246: 762, 257: 762, 275: 762, 288: 762, 309: 762, 313: 762, 586: 762, 608: 762, 908: 5250, 924: 6094}, - {753, 753}, - // 65 - {752, 752}, - {751, 751}, - {750, 750}, - {749, 749}, - {748, 748}, - // 70 - {747, 747}, - {746, 746}, - {745, 745}, - {744, 744}, - {743, 743}, - // 75 - {742, 742}, - {741, 741}, - {740, 740}, - {739, 739}, - {738, 738}, - // 80 - {737, 737}, - {736, 736}, - {735, 735}, - {734, 734}, - {733, 733}, - // 85 - {732, 732}, - {731, 731}, - {730, 730}, - {729, 729}, - {728, 728}, - // 90 - {727, 727}, - {726, 726}, - {725, 725}, - {724, 724}, - {723, 723}, - // 95 - {722, 722}, - {721, 721}, - {720, 720}, - {719, 719}, - {718, 718}, - // 100 - {717, 717}, - {716, 716}, - {715, 715}, - {714, 714}, - {713, 713}, - // 105 - {712, 712}, - {711, 711}, - {710, 710}, - {709, 709}, - {708, 708}, - // 110 - {707, 707}, - {706, 706}, - {705, 705}, - {704, 704}, - {703, 703}, - // 115 - {702, 702}, - {701, 701}, - {700, 700}, - {699, 699}, - {698, 698}, - // 120 - {697, 697}, - {696, 696}, - {695, 695}, - {694, 694}, - {693, 693}, - // 125 - {692, 692}, - {691, 691}, - {690, 690}, - {689, 689}, - {688, 688}, - // 130 - {687, 687}, - {686, 686}, - {685, 685}, - {684, 684}, - {683, 683}, - // 135 - {682, 682}, - {681, 681}, - {680, 680}, - {679, 679}, - {678, 678}, - // 140 - {677, 677}, - {675, 675}, - {673, 673}, - {672, 672}, - {671, 671}, - // 145 - {670, 670}, - {669, 669}, - {668, 668}, - {667, 667}, - {666, 666}, - // 150 - {665, 665}, - {664, 664}, - {663, 663}, - {662, 662}, - {661, 661}, - // 155 - {660, 660}, - {659, 659}, - {658, 658}, - {657, 657}, - {656, 656}, - // 160 - {655, 655}, - {654, 654}, - {628, 628}, - {2: 571, 571, 571, 571, 571, 571, 571, 10: 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 58: 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 608: 6091, 1407: 6092}, - {430, 430, 542: 430}, - // 165 - {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 531: 1052, 549: 1052, 643: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 5958}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 5955}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 5907, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5815, 1088: 5821, 1092: 5830, 1263: 5908}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 3134, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5815, 1088: 5821, 1092: 5830, 1263: 5816}, - {22: 5771, 225: 5772}, - // 170 - {559: 5736}, - {157: 5707, 225: 5728, 608: 5708, 1295: 5727}, - {157: 5707, 225: 5709, 608: 5708, 1295: 5706}, - {530: 5689, 556: 210, 1404: 5688}, - {28: 5683, 56: 5210, 159: 5684, 531: 5681, 560: 3037, 799: 5682, 999: 5685}, - // 175 - {28: 204, 56: 204, 159: 204, 275: 5680, 531: 204, 560: 204}, - {363: 5663}, - {428: 4628}, - {51: 4602}, - {134: 3034}, - // 180 - {2: 3036, 769: 3035}, - {51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 3040, 3043, 1435: 3039}, - {560: 3037, 799: 3038}, - {2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 15: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 60: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 94: 2224, 2224, 2224, 98: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 118: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 133: 2224, 137: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 163: 2224, 2224, 2224, 2224, 213: 2224, 220: 2224, 235: 2224, 239: 2224, 263: 2224, 303: 2224, 530: 2224, 2224, 2224, 535: 2224, 537: 2224, 2224, 2224, 542: 2224, 546: 2224, 2224, 549: 2224, 2224, 2224, 2224, 2224, 2224, 556: 2224, 2224, 2224, 2224, 562: 2224, 2224, 565: 2224, 575: 2224, 577: 2224, 2224, 582: 2224, 587: 2224, 2224, 608: 2224, 614: 2224, 617: 2224, 639: 2224, 695: 2224, 709: 2224, 2224, 713: 2224, 2224, 720: 2224, 722: 2224, 803: 2224, 828: 2224, 831: 2224, 834: 2224, 2224}, - {1, 1}, - // 185 - {12, 12, 9: 4600, 51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 4599, 3043}, - {11, 11, 9: 11, 51: 11, 93: 11, 117: 11, 696: 11}, - {571: 4594}, - {229: 2316, 231: 2316, 555: 4588, 802: 4589, 933: 2316}, - {5, 5, 9: 5, 51: 5, 93: 5, 117: 5, 696: 5}, - // 190 - {198: 4580, 218: 4579}, - {218: 3046}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3592, 3597, 3679, 3596, 3593}, - {1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 4576, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 532: 1786, 1786, 1786, 536: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 549: 1786, 1786, 1786, 554: 1786, 1786, 1786, 1786, 1786, 1786, 561: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 579: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 588: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 609: 1786, 1786, 1786, 1786, 1786, 615: 1786, 1786, 618: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 632: 1786, 1786, 1786, 1786, 696: 1786, 703: 1786, 716: 1786, 718: 1786, 1786}, - {1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 4573, 1785, 1785, 1785, 536: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 549: 1785, 1785, 1785, 554: 1785, 1785, 1785, 1785, 1785, 1785, 561: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 579: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 588: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 609: 1785, 1785, 1785, 1785, 1785, 615: 1785, 1785, 618: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 632: 1785, 1785, 1785, 1785, 696: 1785, 703: 1785, 716: 1785, 718: 1785, 1785}, - // 195 - {2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 4570, 2090, 2090, 2090, 536: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 549: 2090, 2090, 2090, 554: 2090, 2090, 2090, 2090, 2090, 2090, 561: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 579: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 588: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 609: 2090, 2090, 2090, 2090, 2090, 615: 2090, 2090, 618: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 632: 2090, 2090, 2090, 2090, 696: 2090, 703: 2090, 716: 2090, 718: 2090, 2090}, - {2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 774: 2089}, - {2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 774: 2088}, - {2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 774: 2087}, - {2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 774: 2086}, - // 200 - {2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 774: 2085}, - {2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 1435, 2084, 2084, 2084, 536: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 549: 2084, 2084, 2084, 554: 2084, 2084, 2084, 2084, 2084, 2084, 561: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 579: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 588: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 609: 2084, 2084, 2084, 2084, 2084, 615: 2084, 2084, 618: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 632: 2084, 2084, 2084, 2084, 696: 2084, 703: 2084, 716: 2084, 718: 2084, 2084}, - {2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 774: 2083}, - {2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 774: 2082}, - {2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 774: 2081}, - // 205 - {2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 774: 2080}, - {2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 774: 2079}, - {2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 774: 2078}, - {2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 774: 2077}, - {2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 774: 2076}, - // 210 - {2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 774: 2075}, - {2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 774: 2074}, - {2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 774: 2073}, - {2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 4565, 2072, 2072, 2072, 536: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 549: 2072, 2072, 2072, 554: 2072, 2072, 2072, 2072, 2072, 2072, 561: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 579: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 588: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 609: 2072, 2072, 2072, 2072, 2072, 615: 2072, 2072, 618: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 632: 2072, 2072, 2072, 2072, 696: 2072, 703: 2072, 716: 2072, 718: 2072, 2072}, - {2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 774: 2071}, - // 215 - {2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 774: 2070}, - {2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 774: 2069}, - {2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 774: 2068}, - {2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 774: 2067}, - {2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 774: 2066}, - // 220 - {2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 774: 2065}, - {2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 774: 2064}, - {2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 774: 2063}, - {2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 774: 2062}, - {2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 774: 2061}, - // 225 - {2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 1434, 2060, 2060, 2060, 536: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 549: 2060, 2060, 2060, 554: 2060, 2060, 2060, 2060, 2060, 2060, 561: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 579: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 588: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 609: 2060, 2060, 2060, 2060, 2060, 615: 2060, 2060, 618: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 632: 2060, 2060, 2060, 2060, 696: 2060, 703: 2060, 716: 2060, 718: 2060, 2060}, - {2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 774: 2059}, - {2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 774: 2058}, - {2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 774: 2057}, - {2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 774: 2056}, - // 230 - {2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 774: 2055}, - {2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 774: 2054}, - {2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 774: 2053}, - {2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 774: 2052}, - {2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 774: 2051}, - // 235 - {2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 774: 2050}, - {2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 1431, 2049, 4564, 2049, 536: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 549: 2049, 2049, 2049, 554: 2049, 2049, 2049, 2049, 2049, 2049, 561: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 579: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 588: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 609: 2049, 2049, 2049, 2049, 2049, 615: 2049, 2049, 618: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 632: 2049, 2049, 2049, 2049, 696: 2049, 703: 2049, 716: 2049, 718: 2049, 2049}, - {2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 774: 2048}, - {2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 1429, 2047, 2047, 2047, 536: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 549: 2047, 2047, 2047, 554: 2047, 2047, 2047, 2047, 2047, 2047, 561: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 579: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 588: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 609: 2047, 2047, 2047, 2047, 2047, 615: 2047, 2047, 618: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 632: 2047, 2047, 2047, 2047, 696: 2047, 703: 2047, 716: 2047, 718: 2047, 2047}, - {2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 774: 2046}, - // 240 - {2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 774: 2045}, - {2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 774: 2044}, - {2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 774: 2043}, - {2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 774: 2042}, - {2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 774: 2041}, - // 245 - {2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 774: 2040}, - {2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 774: 2039}, - {2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 774: 2038}, - {2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 774: 2037}, - {2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 774: 2036}, - // 250 - {2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 774: 2035}, - {2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 774: 2034}, - {2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 774: 2033}, - {2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 774: 2032}, - {2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 774: 2031}, - // 255 - {2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 774: 2030}, - {2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 774: 2029}, - {2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 774: 2028}, - {2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 774: 2027}, - {2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 774: 2026}, - // 260 - {2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 774: 2025}, - {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 1425, 2024, 2024, 2024, 536: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 549: 2024, 2024, 2024, 554: 2024, 2024, 2024, 2024, 2024, 2024, 561: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 579: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 588: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 609: 2024, 2024, 2024, 2024, 2024, 615: 2024, 2024, 618: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 632: 2024, 2024, 2024, 2024, 696: 2024, 703: 2024, 716: 2024, 718: 2024, 2024}, - {2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 774: 2023}, - {2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 774: 2022}, - {2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 774: 2021}, - // 265 - {2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 774: 2020}, - {2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 774: 2019}, - {2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 1428, 2018, 2018, 2018, 536: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 549: 2018, 2018, 2018, 554: 2018, 2018, 2018, 2018, 2018, 2018, 561: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 579: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 588: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 609: 2018, 2018, 2018, 2018, 2018, 615: 2018, 2018, 618: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 632: 2018, 2018, 2018, 2018, 696: 2018, 703: 2018, 716: 2018, 718: 2018, 2018}, - {2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 774: 2017}, - {2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 774: 2016}, - // 270 - {2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 774: 2015}, - {2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 774: 2014}, - {2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 774: 2013}, - {2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 774: 2012}, - {2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 774: 2011}, - // 275 - {2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 774: 2010}, - {2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 774: 2009}, - {2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 4561, 2008, 2008, 2008, 536: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 549: 2008, 2008, 2008, 554: 2008, 2008, 2008, 2008, 2008, 2008, 561: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 579: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 588: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 609: 2008, 2008, 2008, 2008, 2008, 615: 2008, 2008, 618: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 632: 2008, 2008, 2008, 2008, 696: 2008, 703: 2008, 716: 2008, 718: 2008, 2008}, - {2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 774: 2007}, - {2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 774: 2006}, - // 280 - {2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 774: 2005}, - {2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 774: 2004}, - {2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 774: 2003}, - {2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 774: 2002}, - {2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 774: 2001}, - // 285 - {2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 774: 2000}, - {1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 774: 1999}, - {1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 774: 1998}, - {1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 774: 1997}, - {1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 774: 1996}, - // 290 - {1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 774: 1995}, - {1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 774: 1994}, - {1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 774: 1993}, - {1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 774: 1992}, - {1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 774: 1991}, - // 295 - {1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 774: 1990}, - {1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 774: 1989}, - {1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 774: 1988}, - {1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1419, 1987, 1987, 1987, 536: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 549: 1987, 1987, 1987, 554: 1987, 1987, 1987, 1987, 1987, 1987, 561: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 579: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 588: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 609: 1987, 1987, 1987, 1987, 1987, 615: 1987, 1987, 618: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 632: 1987, 1987, 1987, 1987, 696: 1987, 703: 1987, 716: 1987, 718: 1987, 1987}, - {1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 774: 1986}, - // 300 - {1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 774: 1985}, - {1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 774: 1984}, - {1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 774: 1983}, - {1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 774: 1982}, - {1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 774: 1981}, - // 305 - {1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1411, 1980, 4560, 1980, 536: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 549: 1980, 1980, 1980, 554: 1980, 1980, 1980, 1980, 1980, 1980, 561: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 579: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 588: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 609: 1980, 1980, 1980, 1980, 1980, 615: 1980, 1980, 618: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 632: 1980, 1980, 1980, 1980, 696: 1980, 703: 1980, 716: 1980, 718: 1980, 1980}, - {1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1410, 1979, 4559, 1979, 536: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 549: 1979, 1979, 1979, 554: 1979, 1979, 1979, 1979, 1979, 1979, 561: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 579: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 588: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 609: 1979, 1979, 1979, 1979, 1979, 615: 1979, 1979, 618: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 632: 1979, 1979, 1979, 1979, 696: 1979, 703: 1979, 716: 1979, 718: 1979, 1979}, - {1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 774: 1978}, - {1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 774: 1977}, - {1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1409, 1976, 1976, 1976, 536: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 549: 1976, 1976, 1976, 554: 1976, 1976, 1976, 1976, 1976, 1976, 561: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 579: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 588: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 609: 1976, 1976, 1976, 1976, 1976, 615: 1976, 1976, 618: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 632: 1976, 1976, 1976, 1976, 696: 1976, 703: 1976, 716: 1976, 718: 1976, 1976}, - // 310 - {1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 774: 1975}, - {1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 774: 1974}, - {1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 774: 1973}, - {1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 774: 1972}, - {1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1406, 1971, 1971, 1971, 536: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 549: 1971, 1971, 1971, 554: 1971, 1971, 1971, 1971, 1971, 1971, 561: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 579: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 588: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 609: 1971, 1971, 1971, 1971, 1971, 615: 1971, 1971, 618: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 632: 1971, 1971, 1971, 1971, 696: 1971, 703: 1971, 716: 1971, 718: 1971, 1971}, - // 315 - {1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 774: 1970}, - {1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1407, 1969, 1969, 1969, 536: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 549: 1969, 1969, 1969, 554: 1969, 1969, 1969, 1969, 1969, 1969, 561: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 579: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 588: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 609: 1969, 1969, 1969, 1969, 1969, 615: 1969, 1969, 618: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 632: 1969, 1969, 1969, 1969, 696: 1969, 703: 1969, 716: 1969, 718: 1969, 1969}, - {1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 4549, 1968, 1968, 1968, 536: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 549: 1968, 1968, 1968, 554: 1968, 1968, 1968, 1968, 1968, 1968, 561: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 579: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 588: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 609: 1968, 1968, 1968, 1968, 1968, 615: 1968, 1968, 618: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 632: 1968, 1968, 1968, 1968, 696: 1968, 703: 1968, 716: 1968, 718: 1968, 1968}, - {1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 774: 1967}, - {1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 774: 1966}, - // 320 - {1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1408, 1965, 1965, 1965, 536: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 549: 1965, 1965, 1965, 554: 1965, 1965, 1965, 1965, 1965, 1965, 561: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 579: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 588: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 609: 1965, 1965, 1965, 1965, 1965, 615: 1965, 1965, 618: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 632: 1965, 1965, 1965, 1965, 696: 1965, 703: 1965, 716: 1965, 718: 1965, 1965}, - {1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 774: 1964}, - {1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1432, 1963, 1963, 1963, 536: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 549: 1963, 1963, 1963, 554: 1963, 1963, 1963, 1963, 1963, 1963, 561: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 579: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 588: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 609: 1963, 1963, 1963, 1963, 1963, 615: 1963, 1963, 618: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 632: 1963, 1963, 1963, 1963, 696: 1963, 703: 1963, 716: 1963, 718: 1963, 1963}, - {1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 774: 1962}, - {1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 774: 1961}, - // 325 - {1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 774: 1960}, - {1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 774: 1959}, - {1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 774: 1958}, - {1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 774: 1957}, - {1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 774: 1956}, - // 330 - {1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 774: 1955}, - {1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 774: 1954}, - {1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 774: 1953}, - {1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 774: 1952}, - {1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 774: 1951}, - // 335 - {1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1418, 1950, 1950, 1950, 536: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 549: 1950, 1950, 1950, 554: 1950, 1950, 1950, 1950, 1950, 1950, 561: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 579: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 588: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 609: 1950, 1950, 1950, 1950, 1950, 615: 1950, 1950, 618: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 632: 1950, 1950, 1950, 1950, 696: 1950, 703: 1950, 716: 1950, 718: 1950, 1950}, - {1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 774: 1949}, - {1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 774: 1948}, - {1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 774: 1947}, - {1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 774: 1946}, - // 340 - {1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 774: 1945}, - {1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 774: 1944}, - {1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 774: 1943}, - {1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 774: 1942}, - {1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 774: 1941}, - // 345 - {1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 774: 1940}, - {1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 774: 1939}, - {1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 774: 1938}, - {1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 774: 1937}, - {1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 774: 1936}, - // 350 - {1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 774: 1935}, - {1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 774: 1934}, - {1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 774: 1933}, - {1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 774: 1932}, - {1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 774: 1931}, - // 355 - {1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 774: 1930}, - {1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 774: 1929}, - {1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 774: 1928}, - {1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1415, 1927, 1927, 1927, 536: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 549: 1927, 1927, 1927, 554: 1927, 1927, 1927, 1927, 1927, 1927, 561: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 579: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 588: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 609: 1927, 1927, 1927, 1927, 1927, 615: 1927, 1927, 618: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 632: 1927, 1927, 1927, 1927, 696: 1927, 703: 1927, 716: 1927, 718: 1927, 1927}, - {1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 774: 1926}, - // 360 - {1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 774: 1925}, - {1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 774: 1924}, - {1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 774: 1923}, - {1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 774: 1922}, - {1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 774: 1921}, - // 365 - {1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 774: 1920}, - {1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 774: 1919}, - {1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 774: 1918}, - {1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 774: 1917}, - {1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 774: 1916}, - // 370 - {1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 774: 1915}, - {1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 774: 1914}, - {1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 774: 1913}, - {1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 774: 1912}, - {1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 774: 1911}, - // 375 - {1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1413, 1910, 1910, 1910, 536: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 549: 1910, 1910, 1910, 554: 1910, 1910, 1910, 1910, 1910, 1910, 561: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 579: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 588: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 609: 1910, 1910, 1910, 1910, 1910, 615: 1910, 1910, 618: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 632: 1910, 1910, 1910, 1910, 696: 1910, 703: 1910, 716: 1910, 718: 1910, 1910}, - {1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1433, 1909, 1909, 1909, 536: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 549: 1909, 1909, 1909, 554: 1909, 1909, 1909, 1909, 1909, 1909, 561: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 579: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 588: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 609: 1909, 1909, 1909, 1909, 1909, 615: 1909, 1909, 618: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 632: 1909, 1909, 1909, 1909, 696: 1909, 703: 1909, 716: 1909, 718: 1909, 1909}, - {1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1421, 1908, 1908, 1908, 536: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 549: 1908, 1908, 1908, 554: 1908, 1908, 1908, 1908, 1908, 1908, 561: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 579: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 588: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 609: 1908, 1908, 1908, 1908, 1908, 615: 1908, 1908, 618: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 632: 1908, 1908, 1908, 1908, 696: 1908, 703: 1908, 716: 1908, 718: 1908, 1908}, - {1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 774: 1907}, - {1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 774: 1906}, - // 380 - {1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 774: 1905}, - {1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1423, 1904, 1904, 1904, 536: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 549: 1904, 1904, 1904, 554: 1904, 1904, 1904, 1904, 1904, 1904, 561: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 579: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 588: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 609: 1904, 1904, 1904, 1904, 1904, 615: 1904, 1904, 618: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 632: 1904, 1904, 1904, 1904, 696: 1904, 703: 1904, 716: 1904, 718: 1904, 1904}, - {1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1422, 1903, 1903, 1903, 536: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 549: 1903, 1903, 1903, 554: 1903, 1903, 1903, 1903, 1903, 1903, 561: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 579: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 588: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 609: 1903, 1903, 1903, 1903, 1903, 615: 1903, 1903, 618: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 632: 1903, 1903, 1903, 1903, 696: 1903, 703: 1903, 716: 1903, 718: 1903, 1903}, - {1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 774: 1902}, - {1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 774: 1901}, - // 385 - {1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 774: 1900}, - {1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 774: 1899}, - {1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 774: 1898}, - {1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1412, 1897, 1897, 1897, 536: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 549: 1897, 1897, 1897, 554: 1897, 1897, 1897, 1897, 1897, 1897, 561: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 579: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 588: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 609: 1897, 1897, 1897, 1897, 1897, 615: 1897, 1897, 618: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 632: 1897, 1897, 1897, 1897, 696: 1897, 703: 1897, 716: 1897, 718: 1897, 1897}, - {1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 774: 1896}, - // 390 - {1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 774: 1895}, - {1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 774: 1894}, - {1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 774: 1893}, - {1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 774: 1892}, - {1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 774: 1891}, - // 395 - {1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 774: 1890}, - {1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 774: 1889}, - {1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 774: 1888}, - {1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 774: 1887}, - {1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 774: 1886}, - // 400 - {1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 774: 1885}, - {1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 774: 1884}, - {1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 774: 1883}, - {1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 774: 1882}, - {1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 774: 1881}, - // 405 - {1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 774: 1880}, - {1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 774: 1879}, - {1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 774: 1878}, - {1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 774: 1877}, - {1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 774: 1876}, - // 410 - {1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 774: 1875}, - {1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 774: 1874}, - {1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 774: 1873}, - {1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 774: 1872}, - {1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 774: 1871}, - // 415 - {1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 774: 1870}, - {1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 774: 1869}, - {1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 774: 1868}, - {1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 774: 1867}, - {1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 774: 1866}, - // 420 - {1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 774: 1865}, - {1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 774: 1864}, - {1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 774: 1863}, - {1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 774: 1862}, - {1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 774: 1861}, - // 425 - {1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 774: 1860}, - {1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 774: 1859}, - {1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 774: 1858}, - {1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 774: 1857}, - {1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 774: 1856}, - // 430 - {1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 774: 1855}, - {1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 774: 1854}, - {1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 774: 1853}, - {1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 774: 1852}, - {1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 774: 1851}, - // 435 - {1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 774: 1850}, - {1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 774: 1849}, - {1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 774: 1848}, - {1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 774: 1847}, - {1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 774: 1846}, - // 440 - {1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 774: 1845}, - {1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 774: 1844}, - {1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 774: 1843}, - {1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 774: 1842}, - {1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 774: 1841}, - // 445 - {1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 774: 1840}, - {1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 774: 1839}, - {1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 774: 1838}, - {1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 774: 1837}, - {1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 774: 1836}, - // 450 - {1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 774: 1835}, - {1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 774: 1834}, - {1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 774: 1833}, - {1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 774: 1832}, - {1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 774: 1831}, - // 455 - {1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 774: 1830}, - {1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 774: 1829}, - {1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 774: 1828}, - {1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 774: 1827}, - {1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 774: 1826}, - // 460 - {1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 774: 1825}, - {1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 774: 1824}, - {1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 774: 1823}, - {1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 774: 1822}, - {1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 774: 1821}, - // 465 - {1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 774: 1820}, - {1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 774: 1819}, - {1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 774: 1818}, - {1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 774: 1817}, - {1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 774: 1816}, - // 470 - {1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 774: 1815}, - {1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 774: 1814}, - {1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 774: 1813}, - {1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 774: 1812}, - {1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 774: 1811}, - // 475 - {1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 774: 1810}, - {1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 774: 1809}, - {1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 774: 1808}, - {1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 774: 1807}, - {1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 774: 1806}, - // 480 - {1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 774: 1805}, - {1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 774: 1804}, - {1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 774: 1803}, - {1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 774: 1802}, - {1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 774: 1801}, - // 485 - {1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 774: 1800}, - {1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 774: 1799}, - {1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 774: 1798}, - {1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 774: 1797}, - {1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 774: 1796}, - // 490 - {1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 774: 1795}, - {1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 774: 1794}, - {1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 774: 1793}, - {1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 774: 1792}, - {1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 774: 1791}, - // 495 - {1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 774: 1790}, - {1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 774: 1789}, - {1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 774: 1788}, - {1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 774: 1787}, - {1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 4546, 1784, 1784, 1784, 536: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 549: 1784, 1784, 1784, 554: 1784, 1784, 1784, 1784, 1784, 1784, 561: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 579: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 588: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 609: 1784, 1784, 1784, 1784, 1784, 615: 1784, 1784, 618: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 632: 1784, 1784, 1784, 1784, 696: 1784, 703: 1784, 716: 1784, 718: 1784, 1784}, - // 500 - {1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 4535, 1783, 1783, 1783, 536: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 549: 1783, 1783, 1783, 554: 1783, 1783, 1783, 1783, 1783, 1783, 561: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 579: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 588: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 609: 1783, 1783, 1783, 1783, 1783, 615: 1783, 1783, 618: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 632: 1783, 1783, 1783, 1783, 696: 1783, 703: 1783, 716: 1783, 718: 1783, 1783}, - {1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 774: 1782}, - {1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 774: 1781}, - {1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 774: 1780}, - {1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 774: 1779}, - // 505 - {1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 774: 1778}, - {1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 774: 1777}, - {1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 774: 1776}, - {1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 774: 1775}, - {1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 774: 1774}, - // 510 - {1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 774: 1773}, - {1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 774: 1772}, - {1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 774: 1771}, - {1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 774: 1770}, - {1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 774: 1769}, - // 515 - {1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 774: 1768}, - {1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 774: 1767}, - {1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 774: 1766}, - {1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 774: 1765}, - {1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 774: 1764}, - // 520 - {1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 774: 1763}, - {1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 774: 1762}, - {1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 774: 1761}, - {1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 774: 1760}, - {1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 774: 1759}, - // 525 - {1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 774: 1758}, - {1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 774: 1757}, - {1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 774: 1756}, - {1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 774: 1755}, - {1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 774: 1754}, - // 530 - {1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 774: 1753}, - {1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 774: 1752}, - {1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 774: 1751}, - {1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 774: 1750}, - {1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 774: 1749}, - // 535 - {1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 774: 1748}, - {1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 774: 1747}, - {1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 774: 1746}, - {1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 774: 1745}, - {1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 774: 1744}, - // 540 - {1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 774: 1743}, - {1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 774: 1742}, - {1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 774: 1741}, - {1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 774: 1740}, - {1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 774: 1739}, - // 545 - {1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 774: 1738}, - {1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 774: 1737}, - {1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 774: 1736}, - {1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 774: 1735}, - {1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 774: 1734}, - // 550 - {1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 774: 1733}, - {1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 774: 1732}, - {1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 774: 1731}, - {1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 774: 1730}, - {1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 774: 1729}, - // 555 - {1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 774: 1728}, - {1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 774: 1727}, - {1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 774: 1726}, - {1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 774: 1725}, - {1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 774: 1724}, - // 560 - {1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 774: 1723}, - {1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 774: 1722}, - {1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 774: 1721}, - {1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 774: 1720}, - {1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 774: 1719}, - // 565 - {1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 774: 1718}, - {1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 774: 1717}, - {1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 774: 1716}, - {1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 774: 1715}, - {1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 774: 1714}, - // 570 - {1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 774: 1713}, - {1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 774: 1712}, - {1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 774: 1711}, - {1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 774: 1710}, - {1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 774: 1709}, - // 575 - {1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 774: 1708}, - {1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 774: 1707}, - {1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 774: 1706}, - {1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 774: 1705}, - {1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 774: 1704}, - // 580 - {1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 774: 1703}, - {1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 774: 1702}, - {1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 774: 1701}, - {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 774: 1700}, - {1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 774: 1699}, - // 585 - {1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 774: 1698}, - {1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 774: 1697}, - {1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 774: 1696}, - {1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 774: 1695}, - {1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 774: 1694}, - // 590 - {1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 774: 1693}, - {1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 774: 1692}, - {1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 774: 1691}, - {1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 774: 1690}, - {1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 774: 1689}, - // 595 - {1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 774: 1688}, - {1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 774: 1687}, - {1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 774: 1686}, - {1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 774: 1685}, - {1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 774: 1684}, - // 600 - {1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 774: 1683}, - {1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 774: 1682}, - {1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 774: 1681}, - {1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 774: 1680}, - {1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 774: 1679}, - // 605 - {1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 774: 1678}, - {1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1350, 1677, 1677, 1677, 536: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 549: 1677, 1677, 1677, 554: 1677, 1677, 1677, 1677, 1677, 1677, 561: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 579: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 588: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 609: 1677, 1677, 1677, 1677, 1677, 615: 1677, 1677, 618: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 632: 1677, 1677, 1677, 1677, 696: 1677, 703: 1677, 716: 1677, 718: 1677, 1677}, - {1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 774: 1676}, - {1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 774: 1675}, - {1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 774: 1674}, - // 610 - {1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 774: 1673}, - {1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 774: 1672}, - {1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 774: 1671}, - {1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 774: 1670}, - {1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 774: 1669}, - // 615 - {1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 774: 1668}, - {1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 774: 1667}, - {1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 774: 1666}, - {1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 774: 1665}, - {1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 774: 1664}, - // 620 - {1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 774: 1663}, - {1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 774: 1662}, - {1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 774: 1661}, - {1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 774: 1660}, - {1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 774: 1659}, - // 625 - {1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 4526, 1658, 1658, 1658, 536: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 549: 1658, 1658, 1658, 554: 1658, 1658, 1658, 1658, 1658, 1658, 561: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 579: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 588: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 609: 1658, 1658, 1658, 1658, 1658, 615: 1658, 1658, 618: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 632: 1658, 1658, 1658, 1658, 696: 1658, 703: 1658, 716: 1658, 718: 1658, 1658}, - {1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 774: 1657}, - {1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 774: 1656}, - {1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 774: 1655}, - {1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 774: 1654}, - // 630 - {1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 774: 1653}, - {1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 774: 1652}, - {1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 774: 1651}, - {1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 774: 1650}, - {1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 774: 1649}, - // 635 - {1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 774: 1648}, - {1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 774: 1647}, - {1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 774: 1646}, - {1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 774: 1645}, - {1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 774: 1644}, - // 640 - {1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 774: 1643}, - {1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 774: 1642}, - {1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 774: 1641}, - {1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1349, 1640, 1640, 1640, 536: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 549: 1640, 1640, 1640, 554: 1640, 1640, 1640, 1640, 1640, 1640, 561: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 579: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 588: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 609: 1640, 1640, 1640, 1640, 1640, 615: 1640, 1640, 618: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 632: 1640, 1640, 1640, 1640, 696: 1640, 703: 1640, 716: 1640, 718: 1640, 1640}, - {1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 774: 1639}, - // 645 - {1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 774: 1638}, - {1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 774: 1637}, - {1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 774: 1636}, - {1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 774: 1635}, - {1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 774: 1634}, - // 650 - {1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 774: 1633}, - {1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 774: 1632}, - {1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 774: 1631}, - {1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 774: 1630}, - {1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 774: 1629}, - // 655 - {1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 774: 1628}, - {1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 4519, 1627, 1627, 1627, 536: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 549: 1627, 1627, 1627, 554: 1627, 1627, 1627, 1627, 1627, 1627, 561: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 579: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 588: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 609: 1627, 1627, 1627, 1627, 1627, 615: 1627, 1627, 618: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 632: 1627, 1627, 1627, 1627, 696: 1627, 703: 1627, 716: 1627, 718: 1627, 1627}, - {1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 4512, 1626, 1626, 1626, 536: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 549: 1626, 1626, 1626, 554: 1626, 1626, 1626, 1626, 1626, 1626, 561: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 579: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 588: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 609: 1626, 1626, 1626, 1626, 1626, 615: 1626, 1626, 618: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 632: 1626, 1626, 1626, 1626, 696: 1626, 703: 1626, 716: 1626, 718: 1626, 1626}, - {1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 774: 1625}, - {1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 774: 1624}, - // 660 - {1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 774: 1623}, - {1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 774: 1622}, - {1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 774: 1621}, - {1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 774: 1620}, - {1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 774: 1619}, - // 665 - {1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 774: 1618}, - {1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 774: 1617}, - {1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 774: 1616}, - {1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 774: 1615}, - {1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 774: 1614}, - // 670 - {1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 774: 1613}, - {1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 774: 1612}, - {1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 774: 1611}, - {1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 774: 1610}, - {1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 774: 1609}, - // 675 - {1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 774: 1608}, - {1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 774: 1607}, - {1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 4492, 1606, 1606, 1606, 536: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 549: 1606, 1606, 1606, 554: 1606, 1606, 1606, 1606, 1606, 1606, 561: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 579: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 588: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 609: 1606, 1606, 1606, 1606, 1606, 615: 1606, 1606, 618: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 632: 1606, 1606, 1606, 1606, 696: 1606, 703: 1606, 716: 1606, 718: 1606, 1606}, - {1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 4484, 1605, 1605, 1605, 536: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 549: 1605, 1605, 1605, 554: 1605, 1605, 1605, 1605, 1605, 1605, 561: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 579: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 588: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 609: 1605, 1605, 1605, 1605, 1605, 615: 1605, 1605, 618: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 632: 1605, 1605, 1605, 1605, 696: 1605, 703: 1605, 716: 1605, 718: 1605, 1605}, - {1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 774: 1604}, - // 680 - {1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 774: 1603}, - {1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 774: 1602}, - {1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 774: 1601}, - {1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 774: 1600}, - {1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 774: 1599}, - // 685 - {1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 774: 1598}, - {1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 774: 1597}, - {1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 774: 1596}, - {1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 774: 1595}, - {1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 774: 1594}, - // 690 - {1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 774: 1593}, - {1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 774: 1592}, - {1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 774: 1591}, - {1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 774: 1590}, - {1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 774: 1589}, - // 695 - {1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 774: 1588}, - {1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 774: 1587}, - {1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 774: 1586}, - {1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 774: 1585}, - {1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 774: 1584}, - // 700 - {1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 774: 1583}, - {1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 774: 1582}, - {1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 774: 1581}, - {1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 774: 1580}, - {1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 774: 1579}, - // 705 - {1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 774: 1578}, - {1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 774: 1577}, - {1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 774: 1576}, - {1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 774: 1575}, - {1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 774: 1574}, - // 710 - {1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 774: 1573}, - {1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 774: 1572}, - {1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 774: 1571}, - {1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 774: 1570}, - {1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 774: 1569}, - // 715 - {1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 774: 1568}, - {1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 774: 1567}, - {1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 774: 1566}, - {1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 774: 1565}, - {1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 774: 1564}, - // 720 - {1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 774: 1563}, - {1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 774: 1562}, - {1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 532: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 548: 1526, 1526, 1526, 1526, 554: 1526, 1526, 1526, 1526, 1526, 1526, 561: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 579: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 588: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 609: 1526, 1526, 1526, 1526, 1526, 615: 1526, 1526, 618: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 632: 1526, 1526, 1526, 1526, 696: 1526, 701: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526}, - {1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 532: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 548: 1525, 1525, 1525, 1525, 554: 1525, 1525, 1525, 1525, 1525, 1525, 561: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 579: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 588: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 609: 1525, 1525, 1525, 1525, 1525, 615: 1525, 1525, 618: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 632: 1525, 1525, 1525, 1525, 696: 1525, 701: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525}, - {1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 532: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 548: 1524, 1524, 1524, 1524, 554: 1524, 1524, 1524, 1524, 1524, 1524, 561: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 579: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 588: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 609: 1524, 1524, 1524, 1524, 1524, 615: 1524, 1524, 618: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 632: 1524, 1524, 1524, 1524, 696: 1524, 701: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524}, - // 725 - {1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 532: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 548: 1523, 1523, 1523, 1523, 554: 1523, 1523, 1523, 1523, 1523, 1523, 561: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 579: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 588: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 609: 1523, 1523, 1523, 1523, 1523, 615: 1523, 1523, 618: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 632: 1523, 1523, 1523, 1523, 696: 1523, 701: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523}, - {1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 532: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 548: 1522, 1522, 1522, 1522, 554: 1522, 1522, 1522, 1522, 1522, 1522, 561: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 579: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 588: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 609: 1522, 1522, 1522, 1522, 1522, 615: 1522, 1522, 618: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 632: 1522, 1522, 1522, 1522, 696: 1522, 701: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522}, - {1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 532: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 548: 1521, 1521, 1521, 1521, 554: 1521, 1521, 1521, 1521, 1521, 1521, 561: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 579: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 588: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 609: 1521, 1521, 1521, 1521, 1521, 615: 1521, 1521, 618: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 632: 1521, 1521, 1521, 1521, 696: 1521, 701: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521}, - {1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 532: 1520, 4483, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 548: 1520, 1520, 1520, 1520, 554: 1520, 1520, 1520, 1520, 1520, 1520, 561: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 579: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 588: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 609: 1520, 1520, 1520, 1520, 1520, 615: 1520, 1520, 618: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 632: 1520, 1520, 1520, 1520, 696: 1520, 701: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520}, - {533: 4480, 638: 4481, 640: 4482}, - // 730 - {1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 532: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 548: 1518, 1518, 1518, 1518, 554: 1518, 1518, 1518, 1518, 1518, 1518, 561: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 579: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 588: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 609: 1518, 1518, 1518, 1518, 1518, 615: 1518, 1518, 618: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 632: 1518, 1518, 1518, 1518, 696: 1518, 701: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518}, - {1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 532: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 548: 1517, 1517, 1517, 1517, 554: 1517, 1517, 1517, 1517, 1517, 1517, 561: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 579: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 588: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 609: 1517, 1517, 1517, 1517, 1517, 615: 1517, 1517, 618: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 632: 1517, 1517, 1517, 1517, 696: 1517, 701: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517}, - {1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 532: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 548: 1514, 1514, 1514, 1514, 554: 1514, 1514, 1514, 1514, 1514, 1514, 561: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 579: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 588: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 609: 1514, 1514, 1514, 1514, 1514, 615: 1514, 1514, 618: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 632: 1514, 1514, 1514, 1514, 696: 1514, 701: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514}, - {1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 532: 1481, 1481, 1481, 536: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 549: 1481, 1481, 1481, 554: 1481, 1481, 1481, 1481, 1481, 1481, 561: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 579: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 588: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 609: 1481, 1481, 1481, 1481, 1481, 615: 1481, 1481, 618: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 632: 1481, 1481, 1481, 1481, 696: 1481, 703: 1481, 716: 4475, 718: 1481, 1481}, - {1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 532: 1478, 1478, 1478, 536: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 549: 1478, 1478, 1478, 554: 1478, 1478, 1478, 1478, 1478, 1478, 561: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 579: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 588: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 609: 1478, 1478, 1478, 1478, 1478, 615: 1478, 1478, 618: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 632: 1478, 1478, 1478, 1478, 696: 1478, 703: 1478, 718: 4471, 4472}, - // 735 - {1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 532: 1477, 1477, 1477, 536: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 549: 1477, 1477, 1477, 554: 1477, 1477, 1477, 1477, 1477, 1477, 561: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 579: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 588: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 609: 1477, 1477, 1477, 1477, 1477, 615: 1477, 1477, 618: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 632: 1477, 1477, 1477, 1477, 696: 1477, 703: 1477}, - {1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 532: 1476, 1476, 1476, 536: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 549: 1476, 1476, 1476, 554: 1476, 1476, 1476, 1476, 1476, 1476, 561: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 579: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 588: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 609: 1476, 1476, 1476, 1476, 1476, 615: 1476, 1476, 618: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 632: 1476, 1476, 1476, 1476, 696: 1476, 703: 1476}, - {1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 532: 1475, 1475, 1475, 536: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 549: 1475, 1475, 1475, 554: 1475, 1475, 1475, 1475, 1475, 1475, 561: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 579: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 588: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 609: 1475, 1475, 1475, 1475, 1475, 615: 1475, 1475, 618: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 632: 1475, 1475, 1475, 1475, 696: 1475, 703: 1475}, - {3, 3, 9: 3, 51: 3, 93: 3, 117: 3, 538: 3693, 696: 3, 703: 3694}, - {1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 532: 1473, 1473, 1473, 536: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 549: 1473, 1473, 1473, 554: 1473, 1473, 1473, 1473, 1473, 1473, 561: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 579: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 588: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 609: 1473, 1473, 1473, 1473, 1473, 615: 1473, 1473, 618: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 632: 1473, 1473, 1473, 1473, 696: 1473, 703: 1473}, - // 740 - {1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 532: 1472, 1472, 1472, 536: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 549: 1472, 1472, 1472, 554: 1472, 1472, 1472, 1472, 1472, 1472, 561: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 579: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 588: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 609: 1472, 1472, 1472, 1472, 1472, 615: 1472, 1472, 618: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 632: 1472, 1472, 1472, 1472, 696: 1472, 703: 1472}, - {1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 532: 1471, 1471, 1471, 536: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 549: 1471, 1471, 1471, 554: 1471, 1471, 1471, 1471, 1471, 1471, 561: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 579: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 588: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 609: 1471, 1471, 1471, 1471, 1471, 615: 1471, 1471, 618: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 632: 1471, 1471, 1471, 1471, 696: 1471, 703: 1471}, - {1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 532: 1470, 1470, 1470, 536: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 549: 1470, 1470, 1470, 554: 1470, 1470, 1470, 1470, 1470, 1470, 561: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 579: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 588: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 609: 1470, 1470, 1470, 1470, 1470, 615: 1470, 1470, 618: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 632: 1470, 1470, 1470, 1470, 696: 1470, 703: 1470}, - {1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 532: 1469, 1469, 1469, 536: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 549: 1469, 1469, 1469, 554: 1469, 1469, 1469, 1469, 1469, 1469, 561: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 579: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 588: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 609: 1469, 1469, 1469, 1469, 1469, 615: 1469, 1469, 618: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 632: 1469, 1469, 1469, 1469, 696: 1469, 703: 1469}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4470, 3597, 3679, 3596, 3593}, - // 745 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4469, 3597, 3679, 3596, 3593}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4468, 3597, 3679, 3596, 3593}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4467, 3597, 3679, 3596, 3593}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4466, 3597, 3679, 3596, 3593}, - {1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 532: 1462, 1462, 1462, 536: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 549: 1462, 1462, 1462, 554: 1462, 1462, 1462, 1462, 1462, 1462, 561: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 579: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 588: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 609: 1462, 1462, 1462, 1462, 1462, 615: 1462, 1462, 618: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 632: 1462, 1462, 1462, 1462, 696: 1462, 703: 1462}, - // 750 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 2906, 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3845, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 608: 2904, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 2900, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3844, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4460, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 863: 4461}, - {531: 4455}, - {531: 2907, 775: 4454}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4451, 3051, 3052, 3050}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4450, 3597, 3679, 3596, 3593}, - // 755 - {531: 4443}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 589: 1279, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4430, 1356: 4431}, - {531: 4372}, - {531: 3900}, - {531: 3889}, - // 760 - {531: 1430}, - {531: 1427}, - {531: 1426}, - {531: 1424}, - {531: 1420}, - // 765 - {531: 1417}, - {531: 1416}, - {531: 1414}, - {1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 536: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 549: 1403, 1403, 1403, 554: 1403, 1403, 1403, 1403, 1403, 1403, 561: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 579: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 588: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 609: 1403, 1403, 1403, 1403, 1403, 615: 1403, 1403, 618: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 632: 1403, 1403, 1403, 1403, 696: 1403, 703: 1403}, - {1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 536: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 549: 1402, 1402, 1402, 554: 1402, 1402, 1402, 1402, 1402, 1402, 561: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 579: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 588: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 609: 1402, 1402, 1402, 1402, 1402, 615: 1402, 1402, 618: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 632: 1402, 1402, 1402, 1402, 696: 1402, 703: 1402}, - // 770 - {1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 536: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 549: 1401, 1401, 1401, 554: 1401, 1401, 1401, 1401, 1401, 1401, 561: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 579: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 588: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 609: 1401, 1401, 1401, 1401, 1401, 615: 1401, 1401, 618: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 632: 1401, 1401, 1401, 1401, 696: 1401, 703: 1401}, - {1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 536: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 549: 1400, 1400, 1400, 554: 1400, 1400, 1400, 1400, 1400, 1400, 561: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 579: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 588: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 609: 1400, 1400, 1400, 1400, 1400, 615: 1400, 1400, 618: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 632: 1400, 1400, 1400, 1400, 696: 1400, 703: 1400}, - {1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 536: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 549: 1399, 1399, 1399, 554: 1399, 1399, 1399, 1399, 1399, 1399, 561: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 579: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 588: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 609: 1399, 1399, 1399, 1399, 1399, 615: 1399, 1399, 618: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 632: 1399, 1399, 1399, 1399, 696: 1399, 703: 1399}, - {1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 536: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 549: 1398, 1398, 1398, 554: 1398, 1398, 1398, 1398, 1398, 1398, 561: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 579: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 588: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 609: 1398, 1398, 1398, 1398, 1398, 615: 1398, 1398, 618: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 632: 1398, 1398, 1398, 1398, 696: 1398, 703: 1398}, - {1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 536: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 549: 1397, 1397, 1397, 554: 1397, 1397, 1397, 1397, 1397, 1397, 561: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 579: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 588: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 609: 1397, 1397, 1397, 1397, 1397, 615: 1397, 1397, 618: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 632: 1397, 1397, 1397, 1397, 696: 1397, 703: 1397}, - // 775 - {1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 536: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 549: 1396, 1396, 1396, 554: 1396, 1396, 1396, 1396, 1396, 1396, 561: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 579: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 588: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 609: 1396, 1396, 1396, 1396, 1396, 615: 1396, 1396, 618: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 632: 1396, 1396, 1396, 1396, 696: 1396, 703: 1396}, - {1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 536: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 549: 1395, 1395, 1395, 554: 1395, 1395, 1395, 1395, 1395, 1395, 561: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 579: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 588: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 609: 1395, 1395, 1395, 1395, 1395, 615: 1395, 1395, 618: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 632: 1395, 1395, 1395, 1395, 696: 1395, 703: 1395}, - {1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 536: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 549: 1394, 1394, 1394, 554: 1394, 1394, 1394, 1394, 1394, 1394, 561: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 579: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 588: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 609: 1394, 1394, 1394, 1394, 1394, 615: 1394, 1394, 618: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 632: 1394, 1394, 1394, 1394, 696: 1394, 703: 1394}, - {1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 536: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 549: 1393, 1393, 1393, 554: 1393, 1393, 1393, 1393, 1393, 1393, 561: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 579: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 588: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 609: 1393, 1393, 1393, 1393, 1393, 615: 1393, 1393, 618: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 632: 1393, 1393, 1393, 1393, 696: 1393, 703: 1393}, - {531: 4369}, - // 780 - {531: 4366}, - {1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 4363, 1405, 1405, 1405, 536: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 549: 1405, 1405, 1405, 554: 1405, 1405, 1405, 1405, 1405, 1405, 561: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 579: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 588: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 609: 1405, 1405, 1405, 1405, 1405, 615: 1405, 1405, 618: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 632: 1405, 1405, 1405, 1405, 696: 1405, 703: 1405, 1224: 4364}, - {531: 4361}, - {1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 4357, 1311, 1311, 1311, 536: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 549: 1311, 1311, 1311, 554: 1311, 1311, 1311, 1311, 1311, 1311, 561: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 579: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 588: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 609: 1311, 1311, 1311, 1311, 1311, 615: 1311, 1311, 618: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 632: 1311, 1311, 1311, 1311, 696: 1311, 703: 1311, 1367: 4356}, - {531: 4348}, - // 785 - {531: 4344}, - {531: 4339}, - {531: 4336}, - {531: 4331}, - {531: 4322}, - // 790 - {531: 4315}, - {531: 4310}, - {531: 4305}, - {531: 4291}, - {531: 4274}, - // 795 - {1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 532: 1358, 1358, 1358, 536: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 549: 1358, 1358, 1358, 554: 1358, 1358, 1358, 1358, 1358, 1358, 561: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 579: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 588: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 609: 1358, 1358, 1358, 1358, 1358, 615: 1358, 1358, 618: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 632: 1358, 1358, 1358, 1358, 696: 1358, 703: 1358}, - {531: 4267}, - {531: 1352}, - {531: 1351}, - {1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 532: 1343, 1343, 1343, 536: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 549: 1343, 1343, 1343, 554: 1343, 1343, 1343, 1343, 1343, 1343, 561: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 579: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 588: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 609: 1343, 1343, 1343, 1343, 1343, 615: 1343, 1343, 618: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 632: 1343, 1343, 1343, 1343, 696: 1343, 703: 1343}, - // 800 - {531: 4264}, - {531: 4261}, - {531: 4253}, - {531: 4245}, - {531: 4237}, - // 805 - {531: 4223}, - {531: 4214}, - {531: 4209}, - {531: 4204}, - {531: 4199}, - // 810 - {531: 4194}, - {531: 4189}, - {531: 4184}, - {531: 4171}, - {531: 4168}, - // 815 - {531: 4165}, - {531: 4162}, - {531: 4159}, - {531: 4156}, - {531: 4152}, - // 820 - {531: 4146}, - {531: 4133}, - {531: 4128}, - {531: 4123}, - {531: 3683}, - // 825 - {949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 532: 949, 949, 949, 536: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 549: 949, 949, 949, 554: 949, 949, 949, 949, 949, 949, 561: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 579: 949, 949, 949, 949, 949, 949, 949, 588: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 609: 949, 949, 949, 949, 949, 615: 949, 949, 618: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 632: 949, 949, 949, 949, 696: 949, 703: 949}, - {948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 532: 948, 948, 948, 536: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 549: 948, 948, 948, 554: 948, 948, 948, 948, 948, 948, 561: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 579: 948, 948, 948, 948, 948, 948, 948, 588: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 609: 948, 948, 948, 948, 948, 615: 948, 948, 618: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 632: 948, 948, 948, 948, 696: 948, 703: 948}, - {947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 532: 947, 947, 947, 536: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 549: 947, 947, 947, 554: 947, 947, 947, 947, 947, 947, 561: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 579: 947, 947, 947, 947, 947, 947, 947, 588: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 609: 947, 947, 947, 947, 947, 615: 947, 947, 618: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 632: 947, 947, 947, 947, 696: 947, 703: 947}, - {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 696: 946, 703: 946}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3685}, - // 830 - {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 703: 946, 717: 4121}, - {9: 4052, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4051}, - {531: 4023}, - {2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 532: 2204, 2204, 537: 2204, 539: 2204, 2204, 2204, 2204, 549: 2204, 2204, 2204, 554: 2204, 4006, 2204, 2204, 2204, 2204, 561: 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 580: 2204, 2204, 2204, 2204, 585: 2204, 588: 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 607: 2204, 619: 4003, 4001, 4000, 4008, 4002, 4004, 4005, 4007, 1338: 3999, 1381: 3998}, - // 835 - {2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 532: 2175, 2175, 537: 2175, 539: 2175, 2175, 2175, 2175, 549: 2175, 2175, 2175, 554: 2175, 2175, 2175, 2175, 2175, 2175, 561: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 580: 2175, 2175, 2175, 2175, 585: 2175, 588: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 607: 2175, 619: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175}, - {2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 532: 2144, 2144, 3812, 536: 3811, 2144, 539: 2144, 2144, 2144, 2144, 3782, 3783, 3788, 549: 2144, 2144, 2144, 554: 2144, 2144, 2144, 2144, 2144, 2144, 561: 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 579: 3816, 2144, 2144, 2144, 2144, 3784, 2144, 588: 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 3815, 2144, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 3813, 632: 3817, 3825, 3826, 3824, 915: 3814, 1250: 3818, 1326: 3820, 1372: 3822, 1377: 3819, 1383: 3821, 1437: 3823}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3738}, - {1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 532: 1482, 1482, 1482, 536: 1482, 1482, 3693, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 549: 1482, 1482, 1482, 554: 1482, 1482, 1482, 1482, 1482, 1482, 561: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 579: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 588: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 609: 1482, 1482, 1482, 1482, 1482, 615: 1482, 1482, 618: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 632: 1482, 1482, 1482, 1482, 703: 3694}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 3733}, - // 840 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3695, 3597, 3679, 3596, 3593}, - {1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 532: 1464, 1464, 1464, 536: 1464, 1464, 3693, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 549: 1464, 1464, 1464, 554: 1464, 1464, 1464, 1464, 1464, 1464, 561: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 579: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 588: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 609: 1464, 1464, 1464, 1464, 1464, 615: 1464, 1464, 618: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 632: 1464, 1464, 1464, 1464, 696: 1464, 703: 1464}, - {2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 774: 2090}, - {2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 774: 2084}, - {2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 774: 2072}, - // 845 - {2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 774: 2060}, - {2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 774: 2049}, - {2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 774: 2047}, - {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 774: 2024}, - {2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 774: 2018}, - // 850 - {2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 774: 2008}, - {1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 774: 1987}, - {1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 774: 1980}, - {1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 774: 1979}, - {1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 774: 1976}, - // 855 - {1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 774: 1971}, - {1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 774: 1969}, - {1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 774: 1968}, - {1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 774: 1965}, - {1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 774: 1963}, - // 860 - {1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 774: 1950}, - {1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 774: 1927}, - {1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 774: 1910}, - {1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 774: 1909}, - {1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 774: 1908}, - // 865 - {1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 774: 1904}, - {1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 774: 1903}, - {1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 774: 1897}, - {1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 774: 1786}, - {1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 774: 1785}, - // 870 - {1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 774: 1784}, - {1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 774: 1783}, - {1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 774: 1677}, - {1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 774: 1658}, - {1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 774: 1640}, - // 875 - {1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 774: 1627}, - {1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 774: 1626}, - {1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 774: 1606}, - {1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 774: 1605}, - {1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 532: 1474, 1474, 1474, 536: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 549: 1474, 1474, 1474, 554: 1474, 1474, 1474, 1474, 1474, 1474, 561: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 579: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 588: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 609: 1474, 1474, 1474, 1474, 1474, 615: 1474, 1474, 618: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 632: 1474, 1474, 1474, 1474, 696: 1474, 703: 1474}, - // 880 - {953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 561: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 579: 953, 953, 953, 953, 953, 953, 953, 588: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 615: 953, 953, 618: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 632: 953, 953, 953, 953, 695: 953, 953, 701: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953}, - {952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 561: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 579: 952, 952, 952, 952, 952, 952, 952, 588: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 615: 952, 952, 618: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 632: 952, 952, 952, 952, 695: 952, 952, 701: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952}, - {435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 561: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 579: 435, 435, 435, 435, 435, 435, 435, 435, 588: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 615: 435, 435, 618: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 632: 435, 435, 435, 435, 695: 435, 435, 701: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 712: 435, 435, 717: 435, 721: 435, 435, 435, 725: 435, 435}, - {434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 561: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 579: 434, 434, 434, 434, 434, 434, 434, 434, 588: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 615: 434, 434, 618: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 632: 434, 434, 434, 434, 695: 434, 434, 701: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 712: 434, 434, 717: 434, 721: 434, 434, 434, 725: 434, 434}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3746}, - // 885 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3810}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3809}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3808}, - {2: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 10: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 58: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 531: 2194, 533: 2194, 2194, 2194, 2194, 540: 2194, 2194, 543: 2194, 2194, 2194, 547: 2194, 2194, 552: 2194, 2194, 560: 2194, 578: 2194, 586: 2194, 2194, 614: 2194, 617: 2194, 628: 2194, 2194, 2194, 2194, 636: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 697: 2194, 2194, 2194, 2194, 711: 2194}, - {2: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 10: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 58: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 531: 2193, 533: 2193, 2193, 2193, 2193, 540: 2193, 2193, 543: 2193, 2193, 2193, 547: 2193, 2193, 552: 2193, 2193, 560: 2193, 578: 2193, 586: 2193, 2193, 614: 2193, 617: 2193, 628: 2193, 2193, 2193, 2193, 636: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 697: 2193, 2193, 2193, 2193, 711: 2193}, - // 890 - {2: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 10: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 58: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 531: 2192, 533: 2192, 2192, 2192, 2192, 540: 2192, 2192, 543: 2192, 2192, 2192, 547: 2192, 2192, 552: 2192, 2192, 560: 2192, 578: 2192, 586: 2192, 2192, 614: 2192, 617: 2192, 628: 2192, 2192, 2192, 2192, 636: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 697: 2192, 2192, 2192, 2192, 711: 2192}, - {2: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 10: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 58: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 531: 2191, 533: 2191, 2191, 2191, 2191, 540: 2191, 2191, 543: 2191, 2191, 2191, 547: 2191, 2191, 552: 2191, 2191, 560: 2191, 578: 2191, 586: 2191, 2191, 614: 2191, 617: 2191, 628: 2191, 2191, 2191, 2191, 636: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 697: 2191, 2191, 2191, 2191, 711: 2191}, - {543: 3776}, - {1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 549: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 561: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 579: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 588: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 615: 1308, 1308, 618: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 632: 1308, 1308, 1308, 1308, 695: 1308, 709: 1308, 1308}, - {1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 549: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 561: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 579: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 588: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 615: 1307, 1307, 618: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 632: 1307, 1307, 1307, 1307, 695: 1307, 709: 1307, 1307}, - // 895 - {1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 549: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 561: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 579: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 588: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 615: 1306, 1306, 618: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 632: 1306, 1306, 1306, 1306, 695: 1306, 709: 1306, 1306}, - {1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 549: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 561: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 579: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 588: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 615: 1305, 1305, 618: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 632: 1305, 1305, 1305, 1305, 695: 1305, 709: 1305, 1305}, - {1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 549: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 561: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 579: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 588: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 615: 1304, 1304, 618: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 632: 1304, 1304, 1304, 1304, 695: 1304, 709: 1304, 1304}, - {1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 549: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 561: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 579: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 588: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 615: 1303, 1303, 618: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 632: 1303, 1303, 1303, 1303, 695: 1303, 709: 1303, 1303}, - {1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 549: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 561: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 579: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 588: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 615: 1302, 1302, 618: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 632: 1302, 1302, 1302, 1302, 695: 1302, 709: 1302, 1302}, - // 900 - {1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 549: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 561: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 579: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 588: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 615: 1301, 1301, 618: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 632: 1301, 1301, 1301, 1301, 695: 1301, 709: 1301, 1301}, - {1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 549: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 561: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 579: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 588: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 615: 1300, 1300, 618: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 632: 1300, 1300, 1300, 1300, 695: 1300, 709: 1300, 1300}, - {1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 549: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 561: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 579: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 588: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 615: 1299, 1299, 618: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 632: 1299, 1299, 1299, 1299, 695: 1299, 709: 1299, 1299}, - {1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 549: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 561: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 579: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 588: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 615: 1298, 1298, 618: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 632: 1298, 1298, 1298, 1298, 695: 1298, 709: 1298, 1298}, - {1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 549: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 561: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 579: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 588: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 615: 1297, 1297, 618: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 632: 1297, 1297, 1297, 1297, 695: 1297, 709: 1297, 1297}, - // 905 - {1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 549: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 561: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 579: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 588: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 615: 1296, 1296, 618: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 632: 1296, 1296, 1296, 1296, 695: 1296, 709: 1296, 1296}, - {1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 549: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 561: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 579: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 588: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 615: 1295, 1295, 618: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 632: 1295, 1295, 1295, 1295, 695: 1295, 709: 1295, 1295}, - {1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 549: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 561: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 579: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 588: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 615: 1294, 1294, 618: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 632: 1294, 1294, 1294, 1294, 695: 1294, 709: 1294, 1294}, - {1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 549: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 561: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 579: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 588: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 615: 1293, 1293, 618: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 632: 1293, 1293, 1293, 1293, 695: 1293, 709: 1293, 1293}, - {1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 549: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 561: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 579: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 588: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 615: 1292, 1292, 618: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 632: 1292, 1292, 1292, 1292, 695: 1292, 709: 1292, 1292}, - // 910 - {1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 549: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 561: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 579: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 588: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 615: 1291, 1291, 618: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 632: 1291, 1291, 1291, 1291, 695: 1291, 709: 1291, 1291}, - {1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 549: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 561: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 579: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 588: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 615: 1290, 1290, 618: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 632: 1290, 1290, 1290, 1290, 695: 1290, 709: 1290, 1290}, - {1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 549: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 561: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 579: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 588: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 615: 1289, 1289, 618: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 632: 1289, 1289, 1289, 1289, 695: 1289, 709: 1289, 1289}, - {1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 549: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 561: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 579: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 588: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 615: 1288, 1288, 618: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 632: 1288, 1288, 1288, 1288, 695: 1288, 709: 1288, 1288}, - {1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 549: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 561: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 579: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 588: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 615: 1287, 1287, 618: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 632: 1287, 1287, 1287, 1287, 695: 1287, 709: 1287, 1287}, - // 915 - {1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 549: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 561: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 579: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 588: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 615: 1286, 1286, 618: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 632: 1286, 1286, 1286, 1286, 695: 1286, 709: 1286, 1286}, - {1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 549: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 561: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 579: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 588: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 615: 1285, 1285, 618: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 632: 1285, 1285, 1285, 1285, 695: 1285, 709: 1285, 1285}, - {1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 549: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 561: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 579: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 588: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 615: 1284, 1284, 618: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 632: 1284, 1284, 1284, 1284, 695: 1284, 709: 1284, 1284}, - {1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 549: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 561: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 579: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 588: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 615: 1283, 1283, 618: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 632: 1283, 1283, 1283, 1283, 695: 1283, 709: 1283, 1283}, - {1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 549: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 561: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 579: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 588: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 615: 1282, 1282, 618: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 632: 1282, 1282, 1282, 1282, 695: 1282, 709: 1282, 1282}, - // 920 - {1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 549: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 561: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 579: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 588: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 615: 1281, 1281, 618: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 632: 1281, 1281, 1281, 1281, 695: 1281, 709: 1281, 1281}, - {1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 549: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 561: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 579: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 588: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 615: 1280, 1280, 618: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 632: 1280, 1280, 1280, 1280, 695: 1280, 709: 1280, 1280}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3777}, - {1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 532: 1489, 1489, 1489, 536: 1489, 1489, 539: 1489, 1489, 1489, 1489, 1489, 1489, 3788, 549: 1489, 1489, 1489, 554: 1489, 1489, 1489, 1489, 1489, 1489, 561: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 579: 1489, 1489, 1489, 1489, 1489, 3784, 1489, 588: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 609: 3785, 3786, 1489, 3789, 1489, 615: 3787, 1489, 618: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 632: 1489, 1489, 1489, 1489}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3807}, - // 925 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3806}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3805}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3804}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3801, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3800}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3797, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3796}, - // 930 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3795}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3794}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3793}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3792}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3791}, - // 935 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3790}, - {1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 532: 1483, 1483, 1483, 536: 1483, 1483, 539: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 549: 1483, 1483, 1483, 554: 1483, 1483, 1483, 1483, 1483, 1483, 561: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 579: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 588: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 609: 1483, 1483, 1483, 1483, 1483, 615: 1483, 1483, 618: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 632: 1483, 1483, 1483, 1483}, - {1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 532: 1484, 1484, 1484, 536: 1484, 1484, 539: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 549: 1484, 1484, 1484, 554: 1484, 1484, 1484, 1484, 1484, 1484, 561: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 579: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 588: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 609: 1484, 1484, 1484, 3789, 1484, 615: 1484, 1484, 618: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 632: 1484, 1484, 1484, 1484}, - {1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 532: 1485, 1485, 1485, 536: 1485, 1485, 539: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 549: 1485, 1485, 1485, 554: 1485, 1485, 1485, 1485, 1485, 1485, 561: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 579: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 588: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 609: 1485, 1485, 1485, 3789, 1485, 615: 1485, 1485, 618: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 632: 1485, 1485, 1485, 1485}, - {1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 532: 1486, 1486, 1486, 536: 1486, 1486, 539: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 549: 1486, 1486, 1486, 554: 1486, 1486, 1486, 1486, 1486, 1486, 561: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 579: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 588: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 609: 1486, 1486, 1486, 3789, 1486, 615: 1486, 1486, 618: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 632: 1486, 1486, 1486, 1486}, - // 940 - {1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 532: 1487, 1487, 1487, 536: 1487, 1487, 539: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 549: 1487, 1487, 1487, 554: 1487, 1487, 1487, 1487, 1487, 1487, 561: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 579: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 588: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 609: 1487, 1487, 1487, 3789, 1487, 615: 1487, 1487, 618: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 632: 1487, 1487, 1487, 1487}, - {1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 532: 1488, 1488, 1488, 536: 1488, 1488, 539: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 549: 1488, 1488, 1488, 554: 1488, 1488, 1488, 1488, 1488, 1488, 561: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 579: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 588: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 609: 1488, 1488, 1488, 3789, 1488, 615: 1488, 1488, 618: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 632: 1488, 1488, 1488, 1488}, - {1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 532: 1492, 1492, 1492, 536: 1492, 1492, 539: 1492, 1492, 1492, 1492, 1492, 1492, 3788, 549: 1492, 1492, 1492, 554: 1492, 1492, 1492, 1492, 1492, 1492, 561: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 579: 1492, 1492, 1492, 1492, 1492, 3784, 1492, 588: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 609: 3785, 3786, 1492, 3789, 1492, 615: 3787, 1492, 618: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 632: 1492, 1492, 1492, 1492}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3798}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3799}, - // 945 - {1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 532: 1490, 1490, 1490, 536: 1490, 1490, 539: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 549: 1490, 1490, 1490, 554: 1490, 1490, 1490, 1490, 1490, 1490, 561: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 579: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 588: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 609: 1490, 1490, 1490, 1490, 1490, 615: 1490, 1490, 618: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 632: 1490, 1490, 1490, 1490}, - {1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 532: 1493, 1493, 1493, 536: 1493, 1493, 539: 1493, 1493, 1493, 1493, 1493, 1493, 3788, 549: 1493, 1493, 1493, 554: 1493, 1493, 1493, 1493, 1493, 1493, 561: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 579: 1493, 1493, 1493, 1493, 1493, 3784, 1493, 588: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 609: 3785, 3786, 1493, 3789, 1493, 615: 3787, 1493, 618: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 632: 1493, 1493, 1493, 1493}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3802}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3803}, - {1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 532: 1491, 1491, 1491, 536: 1491, 1491, 539: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 549: 1491, 1491, 1491, 554: 1491, 1491, 1491, 1491, 1491, 1491, 561: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 579: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 588: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 609: 1491, 1491, 1491, 1491, 1491, 615: 1491, 1491, 618: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 632: 1491, 1491, 1491, 1491}, - // 950 - {1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 532: 1494, 1494, 1494, 536: 1494, 1494, 539: 1494, 1494, 1494, 1494, 3782, 3783, 3788, 549: 1494, 1494, 1494, 554: 1494, 1494, 1494, 1494, 1494, 1494, 561: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 579: 1494, 1494, 1494, 1494, 1494, 3784, 1494, 588: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 609: 3785, 3786, 1494, 3789, 1494, 615: 3787, 1494, 618: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 632: 1494, 1494, 1494, 1494}, - {1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 532: 1495, 1495, 1495, 536: 1495, 1495, 539: 1495, 1495, 1495, 1495, 3782, 3783, 3788, 549: 1495, 1495, 1495, 554: 1495, 1495, 1495, 1495, 1495, 1495, 561: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 579: 1495, 1495, 1495, 1495, 1495, 3784, 1495, 588: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 609: 3785, 3786, 1495, 3789, 1495, 615: 3787, 1495, 618: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 632: 1495, 1495, 1495, 1495}, - {1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 532: 1496, 1496, 1496, 536: 1496, 1496, 539: 1496, 1496, 1496, 1496, 3782, 3783, 3788, 549: 1496, 1496, 1496, 554: 1496, 1496, 1496, 1496, 1496, 1496, 561: 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 579: 1496, 1496, 1496, 1496, 1496, 3784, 1496, 588: 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 609: 3785, 3786, 1496, 3789, 1496, 615: 3787, 3780, 618: 3781, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 632: 1496, 1496, 1496, 1496}, - {1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 532: 1497, 1497, 1497, 536: 1497, 1497, 539: 1497, 1497, 1497, 1497, 3782, 3783, 3788, 549: 1497, 1497, 1497, 554: 1497, 1497, 1497, 1497, 1497, 1497, 561: 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 579: 1497, 1497, 1497, 1497, 1497, 3784, 1497, 588: 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 609: 3785, 3786, 3779, 3789, 1497, 615: 3787, 3780, 618: 3781, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 632: 1497, 1497, 1497, 1497}, - {2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 532: 2210, 2210, 537: 2210, 539: 2210, 2210, 2210, 2210, 549: 2210, 2210, 2210, 554: 2210, 556: 2210, 2210, 2210, 2210, 561: 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 580: 2210, 2210, 2210, 2210, 585: 2210, 588: 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 607: 2210, 800: 3741, 3739}, - // 955 - {2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 532: 2211, 2211, 537: 2211, 539: 2211, 2211, 2211, 2211, 549: 2211, 2211, 2211, 554: 2211, 556: 2211, 2211, 2211, 2211, 561: 2211, 2211, 2211, 2211, 2211, 3745, 2211, 3744, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 580: 2211, 2211, 2211, 2211, 585: 2211, 588: 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 607: 2211, 800: 3741, 3739}, - {2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 532: 2212, 2212, 537: 2212, 539: 2212, 2212, 2212, 2212, 549: 2212, 2212, 2212, 554: 2212, 556: 2212, 2212, 2212, 2212, 561: 2212, 2212, 2212, 2212, 2212, 3745, 2212, 3744, 2212, 3740, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 580: 2212, 2212, 2212, 2212, 585: 2212, 588: 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 607: 2212, 800: 3741, 3739}, - {190: 2590, 226: 2590, 548: 2590, 579: 2590, 606: 2590, 627: 2590, 629: 2590, 2590, 632: 2590, 2590, 2590, 645: 2590}, - {190: 2589, 226: 2589, 548: 2589, 579: 2589, 606: 2589, 627: 2589, 629: 2589, 2589, 632: 2589, 2589, 2589, 645: 2589}, - {2: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 10: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 58: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 531: 2166, 533: 2166, 2166, 2166, 540: 2166, 2166, 543: 2166, 2166, 2166, 547: 2166, 2166, 552: 2166, 2166, 560: 2166, 578: 2166, 586: 2166, 2166, 614: 2166, 617: 2166, 628: 2166, 2166, 2166, 2166, 636: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 697: 2166, 2166, 2166, 2166}, - // 960 - {579: 3995, 606: 3994, 627: 3993, 632: 3996, 3825, 3826, 1250: 3997}, - {531: 2162}, - {2: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 10: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 58: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 531: 2160, 533: 2160, 2160, 2160, 540: 2160, 2160, 543: 2160, 2160, 2160, 547: 2160, 2160, 552: 2160, 2160, 560: 2160, 578: 2160, 586: 2160, 2160, 614: 2160, 617: 2160, 628: 2160, 2160, 2160, 2160, 636: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 697: 2160, 2160, 2160, 2160}, - {2: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 10: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 58: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 531: 2158, 533: 2158, 2158, 2158, 540: 2158, 2158, 543: 2158, 2158, 2158, 547: 2158, 2158, 552: 2158, 2158, 560: 2158, 578: 2158, 586: 2158, 2158, 614: 2158, 617: 2158, 628: 2158, 2158, 2158, 2158, 636: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 697: 2158, 2158, 2158, 2158}, - {2: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 10: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 58: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 531: 2156, 533: 2156, 2156, 2156, 540: 2156, 2156, 543: 2156, 2156, 2156, 547: 2156, 2156, 552: 2156, 2156, 560: 2156, 578: 2156, 586: 2156, 2156, 614: 2156, 617: 2156, 628: 2156, 2156, 2156, 2156, 636: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 697: 2156, 2156, 2156, 2156}, - // 965 - {531: 3840, 775: 3841}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3837}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3835, 3597, 3679, 3596, 3593}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3831, 3597, 3679, 3596, 3593}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3830, 3597, 3679, 3596, 3593}, - // 970 - {531: 3827}, - {2: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 10: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 58: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 531: 2143, 533: 2143, 2143, 2143, 540: 2143, 2143, 543: 2143, 2143, 2143, 547: 2143, 2143, 552: 2143, 2143, 560: 2143, 578: 2143, 586: 2143, 2143, 614: 2143, 617: 2143, 628: 2143, 2143, 2143, 2143, 636: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 697: 2143, 2143, 2143, 2143}, - {2: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 10: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 58: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 531: 2142, 533: 2142, 2142, 2142, 540: 2142, 2142, 543: 2142, 2142, 2142, 547: 2142, 2142, 552: 2142, 2142, 560: 2142, 578: 2142, 586: 2142, 2142, 614: 2142, 617: 2142, 628: 2142, 2142, 2142, 2142, 636: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 697: 2142, 2142, 2142, 2142}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3828, 3597, 3679, 3596, 3593}, - {57: 3829, 538: 3693, 703: 3694}, - // 975 - {2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 532: 2145, 2145, 537: 2145, 539: 2145, 2145, 2145, 2145, 549: 2145, 2145, 2145, 554: 2145, 2145, 2145, 2145, 2145, 2145, 561: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 580: 2145, 2145, 2145, 2145, 585: 2145, 588: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 607: 2145, 619: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145}, - {2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 532: 2146, 2146, 537: 2146, 3693, 2146, 2146, 2146, 2146, 549: 2146, 2146, 2146, 554: 2146, 2146, 2146, 2146, 2146, 2146, 561: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 580: 2146, 2146, 2146, 2146, 585: 2146, 588: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 607: 2146, 619: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 703: 3694}, - {2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 3833, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 532: 2141, 2141, 537: 2141, 3693, 2141, 2141, 2141, 2141, 549: 2141, 2141, 2141, 554: 2141, 2141, 2141, 2141, 2141, 2141, 561: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 580: 2141, 2141, 2141, 2141, 585: 2141, 588: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 607: 2141, 619: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 703: 3694, 1200: 3832}, - {2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 532: 2147, 2147, 537: 2147, 539: 2147, 2147, 2147, 2147, 549: 2147, 2147, 2147, 554: 2147, 2147, 2147, 2147, 2147, 2147, 561: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 580: 2147, 2147, 2147, 2147, 585: 2147, 588: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 607: 2147, 619: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147}, - {533: 3834}, - // 980 - {2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 532: 2140, 2140, 537: 2140, 539: 2140, 2140, 2140, 2140, 549: 2140, 2140, 2140, 554: 2140, 2140, 2140, 2140, 2140, 2140, 561: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 580: 2140, 2140, 2140, 2140, 585: 2140, 588: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 607: 2140, 619: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140}, - {2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 3833, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 532: 2141, 2141, 537: 2141, 3693, 2141, 2141, 2141, 2141, 549: 2141, 2141, 2141, 554: 2141, 2141, 2141, 2141, 2141, 2141, 561: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 580: 2141, 2141, 2141, 2141, 585: 2141, 588: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 607: 2141, 619: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 703: 3694, 1200: 3836}, - {2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 532: 2148, 2148, 537: 2148, 539: 2148, 2148, 2148, 2148, 549: 2148, 2148, 2148, 554: 2148, 2148, 2148, 2148, 2148, 2148, 561: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 580: 2148, 2148, 2148, 2148, 585: 2148, 588: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 607: 2148, 619: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148}, - {543: 3782, 3783, 3788, 566: 3838, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3839}, - // 985 - {2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 532: 2149, 2149, 537: 2149, 539: 2149, 2149, 2149, 2149, 549: 2149, 2149, 2149, 554: 2149, 2149, 2149, 2149, 2149, 2149, 561: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 580: 2149, 2149, 2149, 2149, 585: 2149, 588: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 607: 2149, 619: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 2906, 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3845, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 608: 2904, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 2900, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3844, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 863: 3843}, - {2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 532: 2150, 2150, 537: 2150, 539: 2150, 2150, 2150, 2150, 549: 2150, 2150, 2150, 554: 2150, 2150, 2150, 2150, 2150, 2150, 561: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 580: 2150, 2150, 2150, 2150, 585: 2150, 588: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 607: 2150, 619: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150}, - {2190, 2190, 9: 2190, 57: 2190, 160: 2190, 542: 2190, 564: 2190, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {9: 3990, 57: 3991}, - // 990 - {9: 1462, 57: 1462, 534: 1462, 536: 1462, 538: 1462, 1013, 543: 1462, 1462, 1462, 550: 1013, 1013, 554: 3859, 1462, 3858, 564: 3857, 566: 1462, 1462, 1462, 1462, 1462, 579: 1462, 584: 1462, 606: 1462, 609: 1462, 1462, 1462, 1462, 1462, 615: 1462, 1462, 618: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 632: 1462, 1462, 1462, 1462, 703: 1462, 842: 3860, 3861}, - {531: 3889, 639: 3892, 1013: 3891, 1094: 3890}, - {531: 2907, 547: 2905, 608: 2904, 695: 2900, 775: 3854, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856}, - {57: 3852, 539: 1014, 550: 1014, 1014}, - {57: 3851}, - // 995 - {57: 3850}, - {1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 536: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 547: 1041, 549: 1041, 1041, 1041, 554: 1041, 1041, 1041, 1041, 1041, 1041, 561: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 579: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 588: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 615: 1041, 1041, 618: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 632: 1041, 1041, 1041, 1041, 695: 1041, 1041, 703: 1041, 714: 1041, 803: 1041}, - {1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 536: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 547: 1042, 549: 1042, 1042, 1042, 554: 1042, 1042, 1042, 1042, 1042, 1042, 561: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 579: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 588: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 615: 1042, 1042, 618: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 632: 1042, 1042, 1042, 1042, 695: 1042, 1042, 703: 1042, 714: 1042, 803: 1042}, - {1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 536: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 547: 1043, 549: 1043, 1043, 1043, 554: 1043, 1043, 1043, 1043, 1043, 1043, 561: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 579: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 588: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 615: 1043, 1043, 618: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 632: 1043, 1043, 1043, 1043, 695: 1043, 1043, 703: 1043, 714: 1043, 803: 1043}, - {1198, 1198, 57: 1198, 530: 1198, 532: 1198, 539: 1014, 542: 1198, 550: 1014, 1014}, - // 1000 - {1197, 1197, 57: 1197, 530: 1197, 532: 1197, 539: 1013, 542: 1197, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {1026, 1026, 57: 1026, 530: 1026, 532: 1026, 542: 1026}, - {1025, 1025, 57: 1025, 530: 1025, 532: 1025, 542: 1025}, - {723: 3880}, - {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3875}, - // 1005 - {10: 3863, 265: 3864, 1363: 3865}, - {1019, 1019, 57: 1019, 530: 1019, 532: 1019, 542: 1019, 554: 3859, 556: 3858, 843: 3862}, - {1018, 1018, 57: 1018, 530: 1018, 532: 1018, 542: 1018}, - {1017, 1017, 57: 1017, 530: 1017, 532: 1017, 542: 1017}, - {560: 1076, 588: 1076, 639: 1076, 642: 1076}, - // 1010 - {560: 1075, 588: 1075, 639: 1075, 642: 1075}, - {560: 3037, 588: 1074, 639: 1074, 642: 3868, 799: 3866, 816: 3867, 985: 3869, 1357: 3870}, - {2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 15: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 57: 2226, 2226, 60: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 102: 2226, 104: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 118: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 133: 2226, 137: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 213: 2226, 220: 2226, 263: 2226, 530: 2226, 2226, 2226, 535: 2226, 537: 2226, 2226, 2226, 542: 2226, 546: 2226, 2226, 549: 2226, 2226, 2226, 2226, 2226, 557: 2226, 2226, 2226, 562: 2226, 565: 2226, 588: 2226, 608: 2226, 639: 2226, 695: 2226, 709: 2226, 2226, 713: 2226}, - {1080, 1080, 9: 1080, 57: 1080, 213: 1080, 530: 1080, 532: 1080, 539: 1080, 542: 1080, 550: 1080, 1080, 558: 1080, 1080, 562: 1080, 588: 1080, 639: 1080}, - {1079, 1079, 9: 1079, 57: 1079, 213: 1079, 530: 1079, 532: 1079, 539: 1079, 542: 1079, 550: 1079, 1079, 558: 1079, 1079, 562: 1079, 588: 1079, 639: 1079}, - // 1015 - {588: 1073, 639: 1073}, - {588: 3872, 639: 3871, 1444: 3873}, - {194: 1078}, - {194: 1077}, - {194: 3874}, - // 1020 - {1069, 1069, 57: 1069, 530: 1069, 532: 1069, 539: 1069, 542: 1069, 550: 1069, 1069, 558: 1069, 1069, 562: 1069}, - {1072, 1072, 9: 3876, 57: 1072, 213: 3877, 530: 1072, 532: 1072, 539: 1072, 542: 1072, 550: 1072, 1072, 558: 1072, 1072, 562: 1072}, - {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3879}, - {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3878}, - {1070, 1070, 57: 1070, 530: 1070, 532: 1070, 539: 1070, 542: 1070, 550: 1070, 1070, 558: 1070, 1070, 562: 1070}, - // 1025 - {1071, 1071, 57: 1071, 530: 1071, 532: 1071, 539: 1071, 542: 1071, 550: 1071, 1071, 558: 1071, 1071, 562: 1071}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 3882}, - {1506, 1506, 9: 1506, 57: 1506, 160: 1506, 530: 1506, 532: 1506, 539: 1506, 542: 1506, 550: 1506, 1506, 554: 1506, 556: 1506, 558: 1506, 1506, 562: 1506, 564: 1506, 566: 3745, 3743, 3744, 3742, 3740, 572: 1506, 574: 1506, 577: 3888, 588: 1506, 591: 1506, 593: 1506, 605: 3887, 800: 3741, 3739, 1411: 3886}, - {1509, 1509, 9: 3884, 57: 1509, 160: 1509, 530: 1509, 532: 1509, 539: 1509, 542: 1509, 550: 1509, 1509, 554: 1509, 556: 1509, 558: 1509, 1509, 562: 1509}, - {1508, 1508, 9: 1508, 57: 1508, 160: 1508, 530: 1508, 532: 1508, 539: 1508, 542: 1508, 550: 1508, 1508, 554: 1508, 556: 1508, 558: 1508, 1508, 562: 1508, 564: 1508, 572: 1508, 574: 1508, 588: 1508, 591: 1508, 593: 1508}, - // 1030 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3885}, - {1507, 1507, 9: 1507, 57: 1507, 160: 1507, 530: 1507, 532: 1507, 539: 1507, 542: 1507, 550: 1507, 1507, 554: 1507, 556: 1507, 558: 1507, 1507, 562: 1507, 564: 1507, 572: 1507, 574: 1507, 588: 1507, 591: 1507, 593: 1507}, - {1505, 1505, 9: 1505, 57: 1505, 160: 1505, 530: 1505, 532: 1505, 539: 1505, 542: 1505, 550: 1505, 1505, 554: 1505, 556: 1505, 558: 1505, 1505, 562: 1505, 564: 1505, 572: 1505, 574: 1505, 588: 1505, 591: 1505, 593: 1505}, - {1504, 1504, 9: 1504, 57: 1504, 160: 1504, 530: 1504, 532: 1504, 539: 1504, 542: 1504, 550: 1504, 1504, 554: 1504, 556: 1504, 558: 1504, 1504, 562: 1504, 564: 1504, 572: 1504, 574: 1504, 588: 1504, 591: 1504, 593: 1504}, - {1503, 1503, 9: 1503, 57: 1503, 160: 1503, 530: 1503, 532: 1503, 539: 1503, 542: 1503, 550: 1503, 1503, 554: 1503, 556: 1503, 558: 1503, 1503, 562: 1503, 564: 1503, 572: 1503, 574: 1503, 588: 1503, 591: 1503, 593: 1503}, - // 1035 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 3987}, - {1499, 1499, 9: 3913, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 3912}, - {147, 147, 9: 147, 57: 147, 530: 147, 532: 147, 539: 147, 542: 147, 550: 147, 147, 554: 147, 556: 147, 558: 147, 147, 562: 147, 564: 147}, - {531: 3893, 938: 3894}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 1537, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 3898, 1490: 3897, 3896}, - // 1040 - {145, 145, 9: 145, 57: 145, 530: 145, 532: 145, 539: 145, 542: 145, 550: 145, 145, 554: 145, 556: 145, 558: 145, 145, 562: 145, 564: 145}, - {1533, 1533, 9: 1533, 57: 1533, 530: 1533, 532: 1533, 542: 1533, 556: 1533, 561: 1533, 563: 1533, 1533, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {57: 3910}, - {9: 3908, 57: 1536}, - {9: 1534, 57: 1534}, - // 1045 - {1532, 1532, 9: 1532, 57: 1532, 530: 1532, 3900, 1532, 542: 1532, 556: 1532, 561: 1532, 563: 1532, 1532}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 3902}, - {57: 1481, 555: 1481, 716: 3904}, - {57: 3903}, - {1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 532: 1451, 1451, 1451, 536: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 549: 1451, 1451, 1451, 554: 1451, 1451, 1451, 1451, 1451, 1451, 561: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 579: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 588: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 609: 1451, 1451, 1451, 1451, 1451, 615: 1451, 1451, 618: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 632: 1451, 1451, 1451, 1451, 696: 1451, 703: 1451}, - // 1050 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3905, 3051, 3052, 3050}, - {57: 1480, 555: 1480, 716: 3906}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3907, 3051, 3052, 3050}, - {1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 532: 1479, 1479, 1479, 536: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 549: 1479, 1479, 1479, 554: 1479, 1479, 1479, 1479, 1479, 1479, 561: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 579: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 588: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 609: 1479, 1479, 1479, 1479, 1479, 615: 1479, 1479, 618: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 632: 1479, 1479, 1479, 1479, 696: 1479, 703: 1479, 718: 1479, 1479}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 3909}, - // 1055 - {9: 1535, 57: 1535}, - {1538, 1538, 9: 1538, 57: 1538, 108: 1538, 530: 1538, 532: 1538, 539: 1538, 542: 1538, 550: 1538, 1538, 554: 1538, 556: 1538, 558: 1538, 1538, 562: 1538, 564: 1538, 566: 1538}, - {1498, 1498, 57: 1498, 160: 1498, 530: 1498, 532: 1498, 539: 1498, 542: 1498, 550: 1498, 1498, 554: 1498, 556: 1498, 558: 1498, 1498, 562: 1498}, - {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 3915}, - {639: 3892, 1013: 3914}, - // 1060 - {146, 146, 9: 146, 57: 146, 530: 146, 532: 146, 539: 146, 542: 146, 550: 146, 146, 554: 146, 556: 146, 558: 146, 146, 562: 146, 564: 146}, - {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 3917}, - {1067, 1067, 57: 1067, 530: 1067, 532: 1067, 539: 1067, 542: 1067, 550: 1067, 1067, 558: 1067, 1067, 562: 1067}, - {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 3943}, - {340: 3924, 714: 3923}, - // 1065 - {606: 3920}, - {340: 3921}, - {264: 3922}, - {1031, 1031, 57: 1031, 530: 1031, 532: 1031, 539: 1031, 542: 1031, 550: 1031, 1031, 559: 1031}, - {1030, 1030, 57: 1030, 193: 1030, 196: 1030, 227: 1030, 530: 1030, 532: 1030, 539: 1030, 542: 1030, 550: 1030, 1030, 559: 1030, 1216: 3926, 3937}, - // 1070 - {1030, 1030, 57: 1030, 193: 1030, 196: 1030, 530: 1030, 532: 1030, 539: 1030, 542: 1030, 550: 1030, 1030, 559: 1030, 1216: 3926, 3925}, - {1037, 1037, 57: 1037, 193: 3934, 196: 3935, 530: 1037, 532: 1037, 539: 1037, 542: 1037, 550: 1037, 1037, 559: 1037}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 3929}, - {1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 579: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 588: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 615: 1254, 1254, 618: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 632: 1254, 1254, 1254, 1254, 646: 1254, 695: 1254, 1254, 701: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 3932, 720: 1254, 722: 1254, 1254, 1254, 727: 1254, 1254, 1254, 1254, 1254, 743: 1254, 765: 1254, 1254, 1254, 1254, 1254, 774: 1254}, - {1252, 1252, 9: 1252, 57: 1252, 193: 1252, 196: 1252, 227: 1252, 530: 1252, 532: 1252, 539: 1252, 542: 1252, 550: 1252, 1252, 559: 1252, 561: 1252, 712: 1252, 728: 1252, 730: 1252}, - // 1075 - {1029, 1029, 9: 3930, 57: 1029, 193: 1029, 196: 1029, 227: 1029, 530: 1029, 532: 1029, 539: 1029, 542: 1029, 550: 1029, 1029, 559: 1029}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3931}, - {1251, 1251, 9: 1251, 57: 1251, 193: 1251, 196: 1251, 215: 1251, 227: 1251, 530: 1251, 532: 1251, 539: 1251, 542: 1251, 550: 1251, 1251, 559: 1251, 561: 1251, 712: 1251, 715: 1251, 728: 1251, 730: 1251, 765: 1251}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3933, 3051, 3052, 3050}, - {1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 579: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 588: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 615: 1253, 1253, 618: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 632: 1253, 1253, 1253, 1253, 646: 1253, 695: 1253, 1253, 701: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 720: 1253, 722: 1253, 1253, 1253, 727: 1253, 1253, 1253, 1253, 1253, 743: 1253, 765: 1253, 1253, 1253, 1253, 1253, 774: 1253}, - // 1080 - {1034, 1034, 57: 1034, 530: 1034, 532: 1034, 539: 1034, 542: 1034, 550: 1034, 1034, 559: 1034}, - {320: 3936}, - {1032, 1032, 57: 1032, 530: 1032, 532: 1032, 539: 1032, 542: 1032, 550: 1032, 1032, 559: 1032}, - {1038, 1038, 57: 1038, 193: 3938, 196: 3940, 227: 3939, 530: 1038, 532: 1038, 539: 1038, 542: 1038, 550: 1038, 1038, 559: 1038}, - {1036, 1036, 57: 1036, 530: 1036, 532: 1036, 539: 1036, 542: 1036, 550: 1036, 1036, 559: 1036}, - // 1085 - {560: 3037, 799: 3942}, - {320: 3941}, - {1033, 1033, 57: 1033, 530: 1033, 532: 1033, 539: 1033, 542: 1033, 550: 1033, 1033, 559: 1033}, - {1035, 1035, 57: 1035, 530: 1035, 532: 1035, 539: 1035, 542: 1035, 550: 1035, 1035, 559: 1035}, - {1199, 1199, 57: 1199, 530: 1199, 532: 1199, 539: 1199, 542: 1199, 550: 1199, 1199}, - // 1090 - {1413: 3945}, - {533: 3946}, - {262, 262, 57: 262, 131: 3950, 154: 3949, 530: 262, 532: 262, 539: 262, 542: 262, 550: 262, 262, 722: 262, 930: 3948, 1173: 3947}, - {247, 247, 57: 247, 530: 247, 532: 247, 539: 247, 542: 247, 550: 247, 247, 722: 3978, 1056: 3977}, - {135: 3957, 852: 3953, 856: 3955, 862: 3956, 864: 3954, 1172: 3952, 1360: 3951}, - // 1095 - {260, 260, 17: 260, 58: 260, 60: 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 135: 260, 530: 260, 260, 561: 260, 606: 260, 713: 260, 852: 260, 856: 260, 862: 260, 864: 260}, - {259, 259, 17: 259, 58: 259, 60: 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 135: 259, 530: 259, 259, 561: 259, 606: 259, 713: 259, 852: 259, 856: 259, 862: 259, 864: 259}, - {261, 261, 57: 261, 135: 3957, 530: 261, 261, 261, 539: 261, 542: 261, 549: 261, 261, 261, 557: 261, 722: 261, 852: 3953, 856: 3955, 862: 3956, 864: 3954, 1172: 3976}, - {257, 257, 57: 257, 135: 257, 530: 257, 257, 257, 539: 257, 542: 257, 549: 257, 257, 257, 557: 257, 722: 257, 852: 257, 856: 257, 862: 257, 864: 257}, - {723: 3974}, - // 1100 - {533: 3968, 638: 3969, 640: 3970, 956: 3973}, - {723: 3971}, - {723: 3966}, - {548: 3958}, - {723: 3959}, - // 1105 - {533: 3960, 638: 3961, 640: 3962, 1021: 3963}, - {440, 440, 9: 440, 57: 440, 135: 440, 530: 440, 440, 440, 539: 440, 542: 440, 549: 440, 440, 440, 557: 440, 722: 440, 852: 440, 856: 440, 862: 440, 864: 440, 1007: 440}, - {439, 439, 9: 439, 57: 439, 135: 439, 530: 439, 439, 439, 539: 439, 542: 439, 549: 439, 439, 439, 557: 439, 722: 439, 852: 439, 856: 439, 862: 439, 864: 439, 1007: 439}, - {438, 438, 9: 438, 57: 438, 135: 438, 530: 438, 438, 438, 539: 438, 542: 438, 549: 438, 438, 438, 557: 438, 722: 438, 852: 438, 856: 438, 862: 438, 864: 438, 1007: 438}, - {252, 252, 57: 252, 135: 252, 530: 252, 252, 252, 539: 252, 542: 252, 549: 252, 252, 252, 557: 252, 722: 252, 852: 252, 856: 252, 862: 252, 864: 252, 1007: 3964}, - // 1110 - {856: 3965}, - {251, 251, 57: 251, 135: 251, 530: 251, 251, 251, 539: 251, 542: 251, 549: 251, 251, 251, 557: 251, 722: 251, 852: 251, 856: 251, 862: 251, 864: 251}, - {533: 3968, 638: 3969, 640: 3970, 956: 3967}, - {253, 253, 57: 253, 135: 253, 530: 253, 253, 253, 539: 253, 542: 253, 549: 253, 253, 253, 557: 253, 722: 253, 852: 253, 856: 253, 862: 253, 864: 253}, - {250, 250, 57: 250, 135: 250, 530: 250, 250, 250, 539: 250, 542: 250, 549: 250, 250, 250, 557: 250, 722: 250, 852: 250, 856: 250, 862: 250, 864: 250}, - // 1115 - {249, 249, 57: 249, 135: 249, 530: 249, 249, 249, 539: 249, 542: 249, 549: 249, 249, 249, 557: 249, 722: 249, 852: 249, 856: 249, 862: 249, 864: 249}, - {248, 248, 57: 248, 135: 248, 530: 248, 248, 248, 539: 248, 542: 248, 549: 248, 248, 248, 557: 248, 722: 248, 852: 248, 856: 248, 862: 248, 864: 248}, - {533: 3968, 638: 3969, 640: 3970, 956: 3972}, - {254, 254, 57: 254, 135: 254, 530: 254, 254, 254, 539: 254, 542: 254, 549: 254, 254, 254, 557: 254, 722: 254, 852: 254, 856: 254, 862: 254, 864: 254}, - {255, 255, 57: 255, 135: 255, 530: 255, 255, 255, 539: 255, 542: 255, 549: 255, 255, 255, 557: 255, 722: 255, 852: 255, 856: 255, 862: 255, 864: 255}, - // 1120 - {533: 3968, 638: 3969, 640: 3970, 956: 3975}, - {256, 256, 57: 256, 135: 256, 530: 256, 256, 256, 539: 256, 542: 256, 549: 256, 256, 256, 557: 256, 722: 256, 852: 256, 856: 256, 862: 256, 864: 256}, - {258, 258, 57: 258, 135: 258, 530: 258, 258, 258, 539: 258, 542: 258, 549: 258, 258, 258, 557: 258, 722: 258, 852: 258, 856: 258, 862: 258, 864: 258}, - {1044, 1044, 57: 1044, 530: 1044, 532: 1044, 539: 1044, 542: 1044, 550: 1044, 1044}, - {245, 245, 57: 245, 530: 245, 245, 245, 539: 245, 542: 245, 549: 245, 245, 245, 557: 245, 852: 245, 1466: 3979, 3980}, - // 1125 - {243, 243, 57: 243, 530: 243, 243, 243, 539: 243, 542: 243, 549: 243, 243, 243, 557: 243, 852: 3984, 1385: 3983}, - {723: 3981}, - {533: 3968, 638: 3969, 640: 3970, 956: 3982}, - {244, 244, 57: 244, 530: 244, 244, 244, 539: 244, 542: 244, 549: 244, 244, 244, 557: 244, 852: 244}, - {246, 246, 57: 246, 530: 246, 246, 246, 539: 246, 542: 246, 549: 246, 246, 246, 557: 246}, - // 1130 - {723: 3985}, - {533: 3968, 638: 3969, 640: 3970, 956: 3986}, - {242, 242, 57: 242, 530: 242, 242, 242, 539: 242, 542: 242, 549: 242, 242, 242, 557: 242}, - {57: 3988}, - {1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 532: 1450, 1450, 1450, 536: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 549: 1450, 1450, 1450, 554: 1450, 1450, 1450, 1450, 1450, 1450, 561: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 579: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 588: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 609: 1450, 1450, 1450, 1450, 1450, 615: 1450, 1450, 618: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 632: 1450, 1450, 1450, 1450, 696: 1450, 703: 1450}, - // 1135 - {1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 536: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 547: 1040, 549: 1040, 1040, 1040, 554: 1040, 1040, 1040, 1040, 1040, 1040, 561: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 579: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 588: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 615: 1040, 1040, 618: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 632: 1040, 1040, 1040, 1040, 695: 1040, 1040, 703: 1040, 714: 1040, 803: 1040}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3992}, - {2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 532: 2151, 2151, 537: 2151, 539: 2151, 2151, 2151, 2151, 549: 2151, 2151, 2151, 554: 2151, 2151, 2151, 2151, 2151, 2151, 561: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 580: 2151, 2151, 2151, 2151, 585: 2151, 588: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 607: 2151, 619: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151}, - {2189, 2189, 9: 2189, 57: 2189, 160: 2189, 542: 2189, 564: 2189, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 10: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 58: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 531: 2165, 533: 2165, 2165, 2165, 540: 2165, 2165, 543: 2165, 2165, 2165, 547: 2165, 2165, 552: 2165, 2165, 560: 2165, 578: 2165, 586: 2165, 2165, 614: 2165, 617: 2165, 628: 2165, 2165, 2165, 2165, 636: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 697: 2165, 2165, 2165, 2165}, - // 1140 - {531: 2161}, - {2: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 10: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 58: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 531: 2159, 533: 2159, 2159, 2159, 540: 2159, 2159, 543: 2159, 2159, 2159, 547: 2159, 2159, 552: 2159, 2159, 560: 2159, 578: 2159, 586: 2159, 2159, 614: 2159, 617: 2159, 628: 2159, 2159, 2159, 2159, 636: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 697: 2159, 2159, 2159, 2159}, - {2: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 10: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 58: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 531: 2157, 533: 2157, 2157, 2157, 540: 2157, 2157, 543: 2157, 2157, 2157, 547: 2157, 2157, 552: 2157, 2157, 560: 2157, 578: 2157, 586: 2157, 2157, 614: 2157, 617: 2157, 628: 2157, 2157, 2157, 2157, 636: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 697: 2157, 2157, 2157, 2157}, - {2: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 10: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 58: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 531: 2155, 533: 2155, 2155, 2155, 540: 2155, 2155, 543: 2155, 2155, 2155, 547: 2155, 2155, 552: 2155, 2155, 560: 2155, 578: 2155, 586: 2155, 2155, 614: 2155, 617: 2155, 628: 2155, 2155, 2155, 2155, 636: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 697: 2155, 2155, 2155, 2155}, - {226: 4021, 548: 4022, 629: 4020, 4019}, - // 1145 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 4013, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 4014, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 4012, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 715: 4015, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 4010, 1319: 4011}, - {2: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 10: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 58: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 531: 2174, 533: 2174, 2174, 2174, 540: 2174, 2174, 543: 2174, 2174, 2174, 547: 2174, 2174, 552: 2174, 2174, 560: 2174, 578: 2174, 586: 2174, 2174, 614: 2174, 617: 2174, 628: 2174, 2174, 2174, 2174, 636: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 697: 2174, 2174, 2174, 2174, 715: 2174}, - {2: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 10: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 58: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 531: 2173, 533: 2173, 2173, 2173, 540: 2173, 2173, 543: 2173, 2173, 2173, 547: 2173, 2173, 552: 2173, 2173, 560: 2173, 578: 2173, 586: 2173, 2173, 614: 2173, 617: 2173, 628: 2173, 2173, 2173, 2173, 636: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 697: 2173, 2173, 2173, 2173, 715: 2173}, - {2: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 10: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 58: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 531: 2172, 533: 2172, 2172, 2172, 540: 2172, 2172, 543: 2172, 2172, 2172, 547: 2172, 2172, 552: 2172, 2172, 560: 2172, 578: 2172, 586: 2172, 2172, 614: 2172, 617: 2172, 628: 2172, 2172, 2172, 2172, 636: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 697: 2172, 2172, 2172, 2172, 715: 2172}, - {2: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 10: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 58: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 531: 2171, 533: 2171, 2171, 2171, 540: 2171, 2171, 543: 2171, 2171, 2171, 547: 2171, 2171, 552: 2171, 2171, 560: 2171, 578: 2171, 586: 2171, 2171, 614: 2171, 617: 2171, 628: 2171, 2171, 2171, 2171, 636: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 697: 2171, 2171, 2171, 2171, 715: 2171}, - // 1150 - {2: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 10: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 58: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 531: 2170, 533: 2170, 2170, 2170, 540: 2170, 2170, 543: 2170, 2170, 2170, 547: 2170, 2170, 552: 2170, 2170, 560: 2170, 578: 2170, 586: 2170, 2170, 614: 2170, 617: 2170, 628: 2170, 2170, 2170, 2170, 636: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 697: 2170, 2170, 2170, 2170, 715: 2170}, - {2: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 10: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 58: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 531: 2169, 533: 2169, 2169, 2169, 540: 2169, 2169, 543: 2169, 2169, 2169, 547: 2169, 2169, 552: 2169, 2169, 560: 2169, 578: 2169, 586: 2169, 2169, 614: 2169, 617: 2169, 628: 2169, 2169, 2169, 2169, 636: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 697: 2169, 2169, 2169, 2169, 715: 2169}, - {2: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 10: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 58: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 531: 2168, 533: 2168, 2168, 2168, 540: 2168, 2168, 543: 2168, 2168, 2168, 547: 2168, 2168, 552: 2168, 2168, 560: 2168, 578: 2168, 586: 2168, 2168, 614: 2168, 617: 2168, 628: 2168, 2168, 2168, 2168, 636: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 697: 2168, 2168, 2168, 2168, 715: 2168}, - {2: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 10: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 58: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 531: 2167, 533: 2167, 2167, 2167, 540: 2167, 2167, 543: 2167, 2167, 2167, 547: 2167, 2167, 552: 2167, 2167, 560: 2167, 578: 2167, 586: 2167, 2167, 614: 2167, 617: 2167, 628: 2167, 2167, 2167, 2167, 636: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 697: 2167, 2167, 2167, 2167, 715: 2167}, - {226: 2164, 534: 3812, 536: 3811, 548: 2164, 629: 2164, 2164, 915: 4009}, - // 1155 - {226: 2163, 548: 2163, 629: 2163, 2163}, - {2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 532: 2178, 2178, 537: 2178, 539: 2178, 2178, 2178, 2178, 549: 2178, 2178, 2178, 554: 2178, 2178, 2178, 2178, 2178, 2178, 561: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 580: 2178, 2178, 2178, 2178, 585: 2178, 588: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 607: 2178, 619: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178}, - {531: 2907, 775: 4018}, - {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 703: 946, 717: 4016}, - {1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 2154, 1967, 1967, 1967, 536: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 549: 1967, 1967, 1967, 554: 1967, 1967, 1967, 1967, 1967, 1967, 561: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 579: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 588: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 609: 1967, 1967, 1967, 1967, 1967, 615: 1967, 1967, 618: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 632: 1967, 1967, 1967, 1967, 703: 1967, 716: 1967, 718: 1967, 1967}, - // 1160 - {1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 2153, 1966, 1966, 1966, 536: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 549: 1966, 1966, 1966, 554: 1966, 1966, 1966, 1966, 1966, 1966, 561: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 579: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 588: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 609: 1966, 1966, 1966, 1966, 1966, 615: 1966, 1966, 618: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 632: 1966, 1966, 1966, 1966, 703: 1966, 716: 1966, 718: 1966, 1966}, - {531: 2152}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 4017}, - {2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 532: 2176, 2176, 537: 2176, 539: 2176, 2176, 2176, 2176, 549: 2176, 2176, 2176, 554: 2176, 2176, 2176, 2176, 2176, 2176, 561: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 580: 2176, 2176, 2176, 2176, 585: 2176, 588: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 607: 2176, 619: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176}, - {2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 532: 2177, 2177, 537: 2177, 539: 2177, 2177, 2177, 2177, 549: 2177, 2177, 2177, 554: 2177, 2177, 2177, 2177, 2177, 2177, 561: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 580: 2177, 2177, 2177, 2177, 585: 2177, 588: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 607: 2177, 619: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177}, - // 1165 - {2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 532: 2207, 2207, 537: 2207, 539: 2207, 2207, 2207, 2207, 549: 2207, 2207, 2207, 554: 2207, 556: 2207, 2207, 2207, 2207, 561: 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 580: 2207, 2207, 2207, 2207, 585: 2207, 588: 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 607: 2207}, - {2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 532: 2206, 2206, 537: 2206, 539: 2206, 2206, 2206, 2206, 549: 2206, 2206, 2206, 554: 2206, 556: 2206, 2206, 2206, 2206, 561: 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 580: 2206, 2206, 2206, 2206, 585: 2206, 588: 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 607: 2206}, - {2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 532: 2205, 2205, 537: 2205, 539: 2205, 2205, 2205, 2205, 549: 2205, 2205, 2205, 554: 2205, 556: 2205, 2205, 2205, 2205, 561: 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 580: 2205, 2205, 2205, 2205, 585: 2205, 588: 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 607: 2205}, - {2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 532: 2179, 2179, 537: 2179, 539: 2179, 2179, 2179, 2179, 549: 2179, 2179, 2179, 554: 2179, 2179, 2179, 2179, 2179, 2179, 561: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 580: 2179, 2179, 2179, 2179, 585: 2179, 588: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 607: 2179, 619: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 4026}, - // 1170 - {2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 531: 2613, 546: 2613, 553: 2613, 555: 2613, 2613, 2613, 577: 2613, 586: 2613, 605: 2613, 709: 2613, 716: 4047, 2613, 727: 2613, 2613, 730: 2613, 732: 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 744: 2613, 2613, 748: 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613}, - {9: 2610, 57: 2610}, - {9: 4027, 57: 4028}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4046}, - {364: 4029}, - // 1175 - {531: 4030}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4031}, - {57: 2199, 532: 4034, 543: 3782, 3783, 3788, 584: 3784, 606: 4033, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 1366: 4032}, - {57: 4045}, - {188: 4038, 580: 4037}, - // 1180 - {159: 4035}, - {307: 4036}, - {57: 2195}, - {402: 4040}, - {264: 4039}, - // 1185 - {57: 2196}, - {264: 4041}, - {57: 2198, 532: 4042}, - {159: 4043}, - {307: 4044}, - // 1190 - {57: 2197}, - {2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 532: 2208, 2208, 537: 2208, 539: 2208, 2208, 2208, 2208, 549: 2208, 2208, 2208, 554: 2208, 556: 2208, 2208, 2208, 2208, 561: 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 580: 2208, 2208, 2208, 2208, 585: 2208, 588: 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 607: 2208}, - {9: 2609, 57: 2609}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4048, 3051, 3052, 3050}, - {2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 531: 2612, 546: 2612, 553: 2612, 555: 2612, 2612, 2612, 577: 2612, 586: 2612, 605: 2612, 709: 2612, 716: 4049, 2612, 727: 2612, 2612, 730: 2612, 732: 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 744: 2612, 2612, 748: 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612}, - // 1195 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4050, 3051, 3052, 3050}, - {2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 531: 2611, 546: 2611, 553: 2611, 555: 2611, 2611, 2611, 577: 2611, 586: 2611, 605: 2611, 709: 2611, 717: 2611, 727: 2611, 2611, 730: 2611, 732: 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 744: 2611, 2611, 748: 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611}, - {2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 532: 2209, 2209, 537: 2209, 539: 2209, 2209, 2209, 2209, 549: 2209, 2209, 2209, 554: 2209, 556: 2209, 2209, 2209, 2209, 561: 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 580: 2209, 2209, 2209, 2209, 585: 2209, 588: 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 607: 2209, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4053, 3597, 3679, 3596, 3593}, - {57: 4054, 538: 3693, 703: 3694}, - // 1200 - {184: 1134, 549: 1134, 561: 4056, 820: 1134, 1402: 4055}, - {184: 4060, 549: 4061, 820: 1137, 988: 4059}, - {10: 4057, 234: 4058}, - {184: 1133, 549: 1133, 820: 1133}, - {184: 1132, 549: 1132, 820: 1132}, - // 1205 - {820: 4064, 832: 4065}, - {327: 4063}, - {327: 4062}, - {820: 1135}, - {820: 1136}, - // 1210 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 4067, 770: 4066, 3051, 3052, 3050, 1026: 4069, 1306: 4070, 1507: 4068}, - {1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 532: 1143, 1143, 1143, 536: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 549: 1143, 1143, 1143, 554: 1143, 1143, 1143, 1143, 1143, 1143, 561: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 579: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 588: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 609: 1143, 1143, 1143, 1143, 1143, 615: 1143, 1143, 618: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 632: 1143, 1143, 1143, 1143, 696: 1143, 703: 1143}, - {1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 532: 1185, 1185, 1185, 536: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 549: 1185, 1185, 1185, 554: 1185, 1185, 1185, 1185, 1185, 1185, 561: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 579: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 588: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 609: 1185, 1185, 1185, 1185, 1185, 615: 1185, 1185, 618: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 632: 1185, 1185, 1185, 1185, 696: 1185, 703: 1185}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1182, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 546: 1182, 564: 1182, 588: 1182, 591: 1182, 593: 1182, 770: 4066, 3051, 3052, 3050, 1026: 4073, 1401: 4072, 1508: 4071}, - {1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 532: 1156, 1156, 1156, 536: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 549: 1156, 1156, 1156, 554: 1156, 1156, 1156, 1156, 1156, 1156, 561: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 579: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 588: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 609: 1156, 1156, 1156, 1156, 1156, 615: 1156, 1156, 618: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 632: 1156, 1156, 1156, 1156, 696: 1156, 703: 1156}, - // 1215 - {1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 532: 1155, 1155, 1155, 536: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 549: 1155, 1155, 1155, 554: 1155, 1155, 1155, 1155, 1155, 1155, 561: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 579: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 588: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 609: 1155, 1155, 1155, 1155, 1155, 615: 1155, 1155, 618: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 632: 1155, 1155, 1155, 1155, 696: 1155, 703: 1155}, - {1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 532: 1154, 1154, 1154, 536: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 549: 1154, 1154, 1154, 554: 1154, 1154, 1154, 1154, 1154, 1154, 561: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 579: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 588: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 609: 1154, 1154, 1154, 1154, 1154, 615: 1154, 1154, 618: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 632: 1154, 1154, 1154, 1154, 696: 1154, 703: 1154}, - {57: 4120}, - {57: 1180, 546: 4075, 564: 1180, 588: 1180, 591: 1180, 593: 1180, 1405: 4074}, - {57: 1181, 546: 1181, 564: 1181, 588: 1181, 591: 1181, 593: 1181}, - // 1220 - {57: 1178, 564: 4079, 588: 1178, 591: 1178, 593: 1178, 1410: 4078}, - {723: 4076}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 4077}, - {9: 3884, 57: 1179, 564: 1179, 588: 1179, 591: 1179, 593: 1179}, - {57: 1176, 588: 4084, 591: 4085, 593: 4086, 1409: 4082, 1506: 4083}, - // 1225 - {723: 4080}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 4081}, - {9: 3884, 57: 1177, 588: 1177, 591: 1177, 593: 1177}, - {57: 1183}, - {189: 4097, 201: 4093, 560: 4087, 627: 4098, 636: 4089, 4088, 641: 4096, 4095, 916: 4094, 1097: 4091, 1504: 4092, 4090}, - // 1230 - {189: 1174, 201: 1174, 560: 1174, 627: 1174, 636: 1174, 1174, 641: 1174, 1174}, - {189: 1173, 201: 1173, 560: 1173, 627: 1173, 636: 1173, 1173, 641: 1173, 1173}, - {189: 1172, 201: 1172, 560: 1172, 627: 1172, 636: 1172, 1172, 641: 1172, 1172}, - {2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 57: 2494, 167: 2494, 191: 2494, 530: 2494, 2494, 2494, 534: 2494, 2494, 2494, 2494, 2494, 2494, 546: 2494, 2494, 2494, 2494, 552: 2494, 2494, 565: 2494, 608: 2494, 695: 2494, 701: 2494, 2494, 704: 2494, 2494, 2494, 2494, 2494, 2494, 2494}, - {2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 57: 2493, 167: 2493, 191: 2493, 243: 2493, 530: 2493, 2493, 2493, 534: 2493, 2493, 2493, 2493, 2493, 2493, 546: 2493, 2493, 2493, 2493, 552: 2493, 2493, 565: 2493, 608: 2493, 695: 2493, 701: 2493, 2493, 704: 2493, 2493, 2493, 2493, 2493, 2493, 2493}, - // 1235 - {2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 57: 2492, 167: 2492, 191: 2492, 243: 2492, 530: 2492, 2492, 2492, 534: 2492, 2492, 2492, 2492, 2492, 2492, 546: 2492, 2492, 2492, 2492, 552: 2492, 2492, 565: 2492, 608: 2492, 695: 2492, 701: 2492, 2492, 704: 2492, 2492, 2492, 2492, 2492, 2492, 2492}, - {57: 1175}, - {57: 1171}, - {57: 1170}, - {167: 4115}, - // 1240 - {167: 4113}, - {167: 4111}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4118}, - {639: 4117}, - {189: 4097, 201: 4099, 560: 4087, 636: 4089, 4088, 641: 4102, 4101, 916: 4100, 1097: 4104, 1305: 4103}, - // 1245 - {167: 4115, 191: 4116}, - {167: 4113, 191: 4114}, - {167: 4111, 191: 4112}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4107}, - {566: 4105}, - // 1250 - {57: 1163, 566: 1163}, - {189: 4097, 201: 4099, 560: 4087, 636: 4089, 4088, 641: 4102, 4101, 916: 4100, 1097: 4104, 1305: 4106}, - {57: 1164}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4108}, - {167: 4109, 191: 4110}, - // 1255 - {57: 1166, 566: 1166}, - {57: 1159, 566: 1159}, - {57: 1167, 566: 1167}, - {57: 1160, 566: 1160}, - {57: 1168, 566: 1168}, - // 1260 - {57: 1161, 566: 1161}, - {57: 1169, 566: 1169}, - {57: 1162, 566: 1162}, - {57: 1165, 566: 1165}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4119}, - // 1265 - {167: 4109}, - {1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 532: 1184, 1184, 1184, 536: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 549: 1184, 1184, 1184, 554: 1184, 1184, 1184, 1184, 1184, 1184, 561: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 579: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 588: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 609: 1184, 1184, 1184, 1184, 1184, 615: 1184, 1184, 618: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 632: 1184, 1184, 1184, 1184, 696: 1184, 703: 1184}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4122}, - {2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 532: 2213, 2213, 537: 2213, 539: 2213, 2213, 2213, 2213, 549: 2213, 2213, 2213, 554: 2213, 556: 2213, 2213, 2213, 2213, 561: 2213, 2213, 2213, 2213, 2213, 3745, 3743, 3744, 3742, 3740, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 580: 2213, 2213, 2213, 2213, 585: 2213, 588: 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 607: 2213, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4124}, - // 1270 - {57: 4125, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {184: 4060, 549: 4061, 820: 1137, 988: 4126}, - {820: 4064, 832: 4127}, - {1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 532: 1144, 1144, 1144, 536: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 549: 1144, 1144, 1144, 554: 1144, 1144, 1144, 1144, 1144, 1144, 561: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 579: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 588: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 609: 1144, 1144, 1144, 1144, 1144, 615: 1144, 1144, 618: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 632: 1144, 1144, 1144, 1144, 696: 1144, 703: 1144}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4129}, - // 1275 - {57: 4130, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {184: 4060, 549: 4061, 820: 1137, 988: 4131}, - {820: 4064, 832: 4132}, - {1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 532: 1145, 1145, 1145, 536: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 549: 1145, 1145, 1145, 554: 1145, 1145, 1145, 1145, 1145, 1145, 561: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 579: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 588: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 609: 1145, 1145, 1145, 1145, 1145, 615: 1145, 1145, 618: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 632: 1145, 1145, 1145, 1145, 696: 1145, 703: 1145}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4134}, - // 1280 - {9: 4136, 57: 1142, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739, 1226: 4135}, - {57: 4143}, - {560: 4087, 636: 4089, 4088, 642: 4138, 916: 4137}, - {9: 4140, 57: 1139, 1227: 4142}, - {9: 4140, 57: 1139, 1227: 4139}, - // 1285 - {57: 1140}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4141}, - {57: 1138, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {57: 1141}, - {184: 4060, 549: 4061, 820: 1137, 988: 4144}, - // 1290 - {820: 4064, 832: 4145}, - {1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 532: 1146, 1146, 1146, 536: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 549: 1146, 1146, 1146, 554: 1146, 1146, 1146, 1146, 1146, 1146, 561: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 579: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 588: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 609: 1146, 1146, 1146, 1146, 1146, 615: 1146, 1146, 618: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 632: 1146, 1146, 1146, 1146, 696: 1146, 703: 1146}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4147}, - {9: 4136, 57: 1142, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739, 1226: 4148}, - {57: 4149}, - // 1295 - {184: 4060, 549: 4061, 820: 1137, 988: 4150}, - {820: 4064, 832: 4151}, - {1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 532: 1147, 1147, 1147, 536: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 549: 1147, 1147, 1147, 554: 1147, 1147, 1147, 1147, 1147, 1147, 561: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 579: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 588: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 609: 1147, 1147, 1147, 1147, 1147, 615: 1147, 1147, 618: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 632: 1147, 1147, 1147, 1147, 696: 1147, 703: 1147}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4153, 3597, 3679, 3596, 3593}, - {57: 4154, 538: 3693, 703: 3694}, - // 1300 - {820: 4064, 832: 4155}, - {1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 532: 1148, 1148, 1148, 536: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 549: 1148, 1148, 1148, 554: 1148, 1148, 1148, 1148, 1148, 1148, 561: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 579: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 588: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 609: 1148, 1148, 1148, 1148, 1148, 615: 1148, 1148, 618: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 632: 1148, 1148, 1148, 1148, 696: 1148, 703: 1148}, - {57: 4157}, - {820: 4064, 832: 4158}, - {1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 532: 1149, 1149, 1149, 536: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 549: 1149, 1149, 1149, 554: 1149, 1149, 1149, 1149, 1149, 1149, 561: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 579: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 588: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 609: 1149, 1149, 1149, 1149, 1149, 615: 1149, 1149, 618: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 632: 1149, 1149, 1149, 1149, 696: 1149, 703: 1149}, - // 1305 - {57: 4160}, - {820: 4064, 832: 4161}, - {1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 532: 1150, 1150, 1150, 536: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 549: 1150, 1150, 1150, 554: 1150, 1150, 1150, 1150, 1150, 1150, 561: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 579: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 588: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 609: 1150, 1150, 1150, 1150, 1150, 615: 1150, 1150, 618: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 632: 1150, 1150, 1150, 1150, 696: 1150, 703: 1150}, - {57: 4163}, - {820: 4064, 832: 4164}, - // 1310 - {1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 532: 1151, 1151, 1151, 536: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 549: 1151, 1151, 1151, 554: 1151, 1151, 1151, 1151, 1151, 1151, 561: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 579: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 588: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 609: 1151, 1151, 1151, 1151, 1151, 615: 1151, 1151, 618: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 632: 1151, 1151, 1151, 1151, 696: 1151, 703: 1151}, - {57: 4166}, - {820: 4064, 832: 4167}, - {1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 532: 1152, 1152, 1152, 536: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 549: 1152, 1152, 1152, 554: 1152, 1152, 1152, 1152, 1152, 1152, 561: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 579: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 588: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 609: 1152, 1152, 1152, 1152, 1152, 615: 1152, 1152, 618: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 632: 1152, 1152, 1152, 1152, 696: 1152, 703: 1152}, - {57: 4169}, - // 1315 - {820: 4064, 832: 4170}, - {1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 532: 1153, 1153, 1153, 536: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 549: 1153, 1153, 1153, 554: 1153, 1153, 1153, 1153, 1153, 1153, 561: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 579: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 588: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 609: 1153, 1153, 1153, 1153, 1153, 615: 1153, 1153, 618: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 632: 1153, 1153, 1153, 1153, 696: 1153, 703: 1153}, - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4178, 4177}, - {2: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 10: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 58: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 531: 1445, 533: 1445, 1445, 1445, 1445, 540: 1445, 1445, 543: 1445, 1445, 1445, 547: 1445, 1445, 552: 1445, 1445, 560: 1445, 573: 1445, 578: 1445, 584: 1445, 586: 1445, 1445, 608: 1445, 614: 1445, 617: 1445, 628: 1445, 1445, 1445, 1445, 636: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 697: 1445, 1445, 1445, 1445, 711: 1445, 715: 1445, 829: 1445, 1445, 836: 1445, 1445, 1445, 840: 1445, 849: 1445, 1445, 1445}, - {2: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 10: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 58: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 531: 1444, 533: 1444, 1444, 1444, 1444, 540: 1444, 1444, 543: 1444, 1444, 1444, 547: 1444, 1444, 552: 1444, 1444, 560: 1444, 573: 1444, 578: 1444, 584: 1444, 586: 1444, 1444, 608: 1444, 614: 1444, 617: 1444, 628: 1444, 1444, 1444, 1444, 636: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 697: 1444, 1444, 1444, 1444, 711: 1444, 715: 1444, 829: 1444, 1444, 836: 1444, 1444, 1444, 840: 1444, 849: 1444, 1444, 1444}, - // 1320 - {2: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 10: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 58: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 531: 1443, 533: 1443, 1443, 1443, 1443, 540: 1443, 1443, 543: 1443, 1443, 1443, 547: 1443, 1443, 552: 1443, 1443, 560: 1443, 573: 1443, 578: 1443, 584: 1443, 586: 1443, 1443, 608: 1443, 614: 1443, 617: 1443, 628: 1443, 1443, 1443, 1443, 636: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 697: 1443, 1443, 1443, 1443, 711: 1443, 715: 1443, 829: 1443, 1443, 836: 1443, 1443, 1443, 840: 1443, 849: 1443, 1443, 1443}, - {2: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 10: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 58: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 531: 1442, 533: 1442, 1442, 1442, 1442, 540: 1442, 1442, 543: 1442, 1442, 1442, 547: 1442, 1442, 552: 1442, 1442, 560: 1442, 578: 1442, 586: 1442, 1442, 614: 1442, 617: 1442, 628: 1442, 1442, 1442, 1442, 636: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 697: 1442, 1442, 1442, 1442, 711: 1442, 715: 4183}, - {2: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 10: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 58: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 531: 1440, 533: 1440, 1440, 1440, 1440, 540: 1440, 1440, 543: 1440, 1440, 1440, 547: 1440, 1440, 552: 1440, 1440, 560: 1440, 578: 1440, 586: 1440, 1440, 614: 1440, 617: 1440, 628: 1440, 1440, 1440, 1440, 636: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 697: 1440, 1440, 1440, 1440, 711: 1440}, - {2: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 10: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 58: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 531: 1437, 533: 1437, 1437, 1437, 1437, 540: 1437, 1437, 543: 1437, 1437, 1437, 547: 1437, 1437, 552: 1437, 1437, 560: 1437, 578: 1437, 586: 1437, 1437, 614: 1437, 617: 1437, 628: 1437, 1437, 1437, 1437, 636: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 697: 1437, 1437, 1437, 1437, 711: 1437}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4179}, - // 1325 - {57: 4180, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4181}, - {1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 532: 1322, 1322, 1322, 536: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 549: 1322, 1322, 1322, 554: 1322, 1322, 1322, 1322, 1322, 1322, 561: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 579: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 588: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 609: 1322, 1322, 1322, 1322, 1322, 615: 1322, 1322, 618: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 632: 1322, 1322, 1322, 1322, 696: 1322, 703: 1322}, - {1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 532: 1157, 1157, 1157, 536: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 549: 1157, 1157, 1157, 554: 1157, 1157, 1157, 1157, 1157, 1157, 561: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 579: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 588: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 609: 1157, 1157, 1157, 1157, 1157, 615: 1157, 1157, 618: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 632: 1157, 1157, 1157, 1157, 696: 1157, 703: 1157}, - {2: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 10: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 58: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 531: 1436, 533: 1436, 1436, 1436, 1436, 540: 1436, 1436, 543: 1436, 1436, 1436, 547: 1436, 1436, 552: 1436, 1436, 560: 1436, 578: 1436, 586: 1436, 1436, 614: 1436, 617: 1436, 628: 1436, 1436, 1436, 1436, 636: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 697: 1436, 1436, 1436, 1436, 711: 1436}, - // 1330 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4185, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4186}, - {57: 4187, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4188}, - {1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 532: 1323, 1323, 1323, 536: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 549: 1323, 1323, 1323, 554: 1323, 1323, 1323, 1323, 1323, 1323, 561: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 579: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 588: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 609: 1323, 1323, 1323, 1323, 1323, 615: 1323, 1323, 618: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 632: 1323, 1323, 1323, 1323, 696: 1323, 703: 1323}, - // 1335 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4190, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4191}, - {57: 4192, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4193}, - {1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 532: 1324, 1324, 1324, 536: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 549: 1324, 1324, 1324, 554: 1324, 1324, 1324, 1324, 1324, 1324, 561: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 579: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 588: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 609: 1324, 1324, 1324, 1324, 1324, 615: 1324, 1324, 618: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 632: 1324, 1324, 1324, 1324, 696: 1324, 703: 1324}, - // 1340 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4195, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4196}, - {57: 4197, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4198}, - {1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 532: 1325, 1325, 1325, 536: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 549: 1325, 1325, 1325, 554: 1325, 1325, 1325, 1325, 1325, 1325, 561: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 579: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 588: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 609: 1325, 1325, 1325, 1325, 1325, 615: 1325, 1325, 618: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 632: 1325, 1325, 1325, 1325, 696: 1325, 703: 1325}, - // 1345 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4200, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4201}, - {57: 4202, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4203}, - {1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 532: 1326, 1326, 1326, 536: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 549: 1326, 1326, 1326, 554: 1326, 1326, 1326, 1326, 1326, 1326, 561: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 579: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 588: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 609: 1326, 1326, 1326, 1326, 1326, 615: 1326, 1326, 618: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 632: 1326, 1326, 1326, 1326, 696: 1326, 703: 1326}, - // 1350 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4205, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4206}, - {57: 4207, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4208}, - {1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 532: 1327, 1327, 1327, 536: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 549: 1327, 1327, 1327, 554: 1327, 1327, 1327, 1327, 1327, 1327, 561: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 579: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 588: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 609: 1327, 1327, 1327, 1327, 1327, 615: 1327, 1327, 618: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 632: 1327, 1327, 1327, 1327, 696: 1327, 703: 1327}, - // 1355 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4210, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4211}, - {57: 4212, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4213}, - {1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 532: 1328, 1328, 1328, 536: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 549: 1328, 1328, 1328, 554: 1328, 1328, 1328, 1328, 1328, 1328, 561: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 579: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 588: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 609: 1328, 1328, 1328, 1328, 1328, 615: 1328, 1328, 618: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 632: 1328, 1328, 1328, 1328, 696: 1328, 703: 1328}, - // 1360 - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4215, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4216}, - {9: 3990, 57: 1499, 160: 1499, 564: 3857, 842: 3911, 909: 4217}, - {57: 1315, 160: 4219, 1403: 4218}, - {57: 4221}, - // 1365 - {533: 4220}, - {57: 1314}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4222}, - {1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 532: 1329, 1329, 1329, 536: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 549: 1329, 1329, 1329, 554: 1329, 1329, 1329, 1329, 1329, 1329, 561: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 579: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 588: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 609: 1329, 1329, 1329, 1329, 1329, 615: 1329, 1329, 618: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 632: 1329, 1329, 1329, 1329, 696: 1329, 703: 1329}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 4227, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4226, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4224, 829: 4172, 4173, 885: 4225}, - // 1370 - {57: 4235, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4233}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4230}, - {57: 4228}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4229}, - // 1375 - {1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 532: 1330, 1330, 1330, 536: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 549: 1330, 1330, 1330, 554: 1330, 1330, 1330, 1330, 1330, 1330, 561: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 579: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 588: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 609: 1330, 1330, 1330, 1330, 1330, 615: 1330, 1330, 618: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 632: 1330, 1330, 1330, 1330, 696: 1330, 703: 1330}, - {57: 4231, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4232}, - {1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 532: 1332, 1332, 1332, 536: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 549: 1332, 1332, 1332, 554: 1332, 1332, 1332, 1332, 1332, 1332, 561: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 579: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 588: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 609: 1332, 1332, 1332, 1332, 1332, 615: 1332, 1332, 618: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 632: 1332, 1332, 1332, 1332, 696: 1332, 703: 1332}, - {9: 3990, 57: 4234}, - // 1380 - {1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 532: 1333, 1333, 1333, 536: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 549: 1333, 1333, 1333, 554: 1333, 1333, 1333, 1333, 1333, 1333, 561: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 579: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 588: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 609: 1333, 1333, 1333, 1333, 1333, 615: 1333, 1333, 618: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 632: 1333, 1333, 1333, 1333, 696: 1333, 703: 1333}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4236}, - {1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 532: 1331, 1331, 1331, 536: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 549: 1331, 1331, 1331, 554: 1331, 1331, 1331, 1331, 1331, 1331, 561: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 579: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 588: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 609: 1331, 1331, 1331, 1331, 1331, 615: 1331, 1331, 618: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 632: 1331, 1331, 1331, 1331, 696: 1331, 703: 1331}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4239, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4238}, - {57: 4243, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1385 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4240}, - {57: 4241, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4242}, - {1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 532: 1334, 1334, 1334, 536: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 549: 1334, 1334, 1334, 554: 1334, 1334, 1334, 1334, 1334, 1334, 561: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 579: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 588: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 609: 1334, 1334, 1334, 1334, 1334, 615: 1334, 1334, 618: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 632: 1334, 1334, 1334, 1334, 696: 1334, 703: 1334}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4244}, - // 1390 - {1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 532: 1335, 1335, 1335, 536: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 549: 1335, 1335, 1335, 554: 1335, 1335, 1335, 1335, 1335, 1335, 561: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 579: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 588: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 609: 1335, 1335, 1335, 1335, 1335, 615: 1335, 1335, 618: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 632: 1335, 1335, 1335, 1335, 696: 1335, 703: 1335}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4247, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4246}, - {57: 4251, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4248}, - {57: 4249, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1395 - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4250}, - {1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 532: 1336, 1336, 1336, 536: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 549: 1336, 1336, 1336, 554: 1336, 1336, 1336, 1336, 1336, 1336, 561: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 579: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 588: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 609: 1336, 1336, 1336, 1336, 1336, 615: 1336, 1336, 618: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 632: 1336, 1336, 1336, 1336, 696: 1336, 703: 1336}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4252}, - {1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 532: 1337, 1337, 1337, 536: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 549: 1337, 1337, 1337, 554: 1337, 1337, 1337, 1337, 1337, 1337, 561: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 579: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 588: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 609: 1337, 1337, 1337, 1337, 1337, 615: 1337, 1337, 618: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 632: 1337, 1337, 1337, 1337, 696: 1337, 703: 1337}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4255, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4254}, - // 1400 - {57: 4259, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4256}, - {57: 4257, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4258}, - {1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 532: 1338, 1338, 1338, 536: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 549: 1338, 1338, 1338, 554: 1338, 1338, 1338, 1338, 1338, 1338, 561: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 579: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 588: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 609: 1338, 1338, 1338, 1338, 1338, 615: 1338, 1338, 618: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 632: 1338, 1338, 1338, 1338, 696: 1338, 703: 1338}, - // 1405 - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4260}, - {1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 532: 1339, 1339, 1339, 536: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 549: 1339, 1339, 1339, 554: 1339, 1339, 1339, 1339, 1339, 1339, 561: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 579: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 588: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 609: 1339, 1339, 1339, 1339, 1339, 615: 1339, 1339, 618: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 632: 1339, 1339, 1339, 1339, 696: 1339, 703: 1339}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4262}, - {9: 3990, 57: 4263}, - {1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 532: 1340, 1340, 1340, 536: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 549: 1340, 1340, 1340, 554: 1340, 1340, 1340, 1340, 1340, 1340, 561: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 579: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 588: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 609: 1340, 1340, 1340, 1340, 1340, 615: 1340, 1340, 618: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 632: 1340, 1340, 1340, 1340, 696: 1340, 703: 1340}, - // 1410 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4265}, - {9: 3990, 57: 4266}, - {1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 532: 1341, 1341, 1341, 536: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 549: 1341, 1341, 1341, 554: 1341, 1341, 1341, 1341, 1341, 1341, 561: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 579: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 588: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 609: 1341, 1341, 1341, 1341, 1341, 615: 1341, 1341, 618: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 632: 1341, 1341, 1341, 1341, 696: 1341, 703: 1341}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4268}, - {9: 4269, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1415 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4270}, - {9: 4271, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4272}, - {57: 4273, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 532: 1357, 1357, 1357, 536: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 549: 1357, 1357, 1357, 554: 1357, 1357, 1357, 1357, 1357, 1357, 561: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 579: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 588: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 609: 1357, 1357, 1357, 1357, 1357, 615: 1357, 1357, 618: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 632: 1357, 1357, 1357, 1357, 696: 1357, 703: 1357}, - // 1420 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4275, 1328: 4277, 1382: 4278, 1485: 4279, 4276}, - {57: 4287, 561: 4288, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 4281, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4280}, - {2: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 10: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 58: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 531: 1348, 533: 1348, 1348, 1348, 1348, 540: 1348, 1348, 543: 1348, 1348, 1348, 547: 1348, 1348, 552: 1348, 1348, 560: 1348, 1348, 578: 1348, 586: 1348, 1348, 614: 1348, 617: 1348, 628: 1348, 1348, 1348, 1348, 636: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 697: 1348, 1348, 1348, 1348, 711: 1348}, - {2: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 10: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 58: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 531: 1347, 533: 1347, 1347, 1347, 1347, 540: 1347, 1347, 543: 1347, 1347, 1347, 547: 1347, 1347, 552: 1347, 1347, 560: 1347, 1347, 578: 1347, 586: 1347, 1347, 614: 1347, 617: 1347, 628: 1347, 1347, 1347, 1347, 636: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 697: 1347, 1347, 1347, 1347, 711: 1347}, - // 1425 - {2: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 10: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 58: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 531: 1346, 533: 1346, 1346, 1346, 1346, 540: 1346, 1346, 543: 1346, 1346, 1346, 547: 1346, 1346, 552: 1346, 1346, 560: 1346, 1346, 578: 1346, 586: 1346, 1346, 614: 1346, 617: 1346, 628: 1346, 1346, 1346, 1346, 636: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 697: 1346, 1346, 1346, 1346, 711: 1346}, - {561: 4284, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4282}, - {57: 4283, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 532: 1363, 1363, 1363, 536: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 549: 1363, 1363, 1363, 554: 1363, 1363, 1363, 1363, 1363, 1363, 561: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 579: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 588: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 609: 1363, 1363, 1363, 1363, 1363, 615: 1363, 1363, 618: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 632: 1363, 1363, 1363, 1363, 696: 1363, 703: 1363}, - // 1430 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4285}, - {57: 4286, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 532: 1362, 1362, 1362, 536: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 549: 1362, 1362, 1362, 554: 1362, 1362, 1362, 1362, 1362, 1362, 561: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 579: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 588: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 609: 1362, 1362, 1362, 1362, 1362, 615: 1362, 1362, 618: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 632: 1362, 1362, 1362, 1362, 696: 1362, 703: 1362}, - {1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 532: 1365, 1365, 1365, 536: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 549: 1365, 1365, 1365, 554: 1365, 1365, 1365, 1365, 1365, 1365, 561: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 579: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 588: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 609: 1365, 1365, 1365, 1365, 1365, 615: 1365, 1365, 618: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 632: 1365, 1365, 1365, 1365, 696: 1365, 703: 1365}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4289}, - // 1435 - {57: 4290, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 532: 1364, 1364, 1364, 536: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 549: 1364, 1364, 1364, 554: 1364, 1364, 1364, 1364, 1364, 1364, 561: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 579: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 588: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 609: 1364, 1364, 1364, 1364, 1364, 615: 1364, 1364, 618: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 632: 1364, 1364, 1364, 1364, 696: 1364, 703: 1364}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4292}, - {9: 4293, 561: 4294, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4300}, - // 1440 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4295}, - {57: 4296, 558: 4297, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 532: 1370, 1370, 1370, 536: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 549: 1370, 1370, 1370, 554: 1370, 1370, 1370, 1370, 1370, 1370, 561: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 579: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 588: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 609: 1370, 1370, 1370, 1370, 1370, 615: 1370, 1370, 618: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 632: 1370, 1370, 1370, 1370, 696: 1370, 703: 1370}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4298}, - {57: 4299, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1445 - {1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 532: 1368, 1368, 1368, 536: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 549: 1368, 1368, 1368, 554: 1368, 1368, 1368, 1368, 1368, 1368, 561: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 579: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 588: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 609: 1368, 1368, 1368, 1368, 1368, 615: 1368, 1368, 618: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 632: 1368, 1368, 1368, 1368, 696: 1368, 703: 1368}, - {9: 4302, 57: 4301, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 532: 1371, 1371, 1371, 536: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 549: 1371, 1371, 1371, 554: 1371, 1371, 1371, 1371, 1371, 1371, 561: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 579: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 588: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 609: 1371, 1371, 1371, 1371, 1371, 615: 1371, 1371, 618: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 632: 1371, 1371, 1371, 1371, 696: 1371, 703: 1371}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4303}, - {57: 4304, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1450 - {1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 532: 1369, 1369, 1369, 536: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 549: 1369, 1369, 1369, 554: 1369, 1369, 1369, 1369, 1369, 1369, 561: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 579: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 588: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 609: 1369, 1369, 1369, 1369, 1369, 615: 1369, 1369, 618: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 632: 1369, 1369, 1369, 1369, 696: 1369, 703: 1369}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4306}, - {543: 3782, 3783, 3788, 584: 3784, 606: 4307, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4308}, - {57: 4309, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1455 - {1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 532: 1372, 1372, 1372, 536: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 549: 1372, 1372, 1372, 554: 1372, 1372, 1372, 1372, 1372, 1372, 561: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 579: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 588: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 609: 1372, 1372, 1372, 1372, 1372, 615: 1372, 1372, 618: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 632: 1372, 1372, 1372, 1372, 696: 1372, 703: 1372}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 886: 3747, 901: 4311}, - {561: 4312}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4313}, - {57: 4314, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1460 - {1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 532: 1374, 1374, 1374, 536: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 549: 1374, 1374, 1374, 554: 1374, 1374, 1374, 1374, 1374, 1374, 561: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 579: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 588: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 609: 1374, 1374, 1374, 1374, 1374, 615: 1374, 1374, 618: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 632: 1374, 1374, 1374, 1374, 696: 1374, 703: 1374}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4316}, - {9: 4317, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {641: 4318}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4319}, - // 1465 - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4320}, - {57: 4321}, - {1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 532: 1375, 1375, 1375, 536: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 549: 1375, 1375, 1375, 554: 1375, 1375, 1375, 1375, 1375, 1375, 561: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 579: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 588: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 609: 1375, 1375, 1375, 1375, 1375, 615: 1375, 1375, 618: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 632: 1375, 1375, 1375, 1375, 696: 1375, 703: 1375}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4323}, - {9: 4324, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1470 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 4326, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4325}, - {57: 4330, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4327}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4328}, - {57: 4329, 543: 3776}, - // 1475 - {1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 532: 1376, 1376, 1376, 536: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 549: 1376, 1376, 1376, 554: 1376, 1376, 1376, 1376, 1376, 1376, 561: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 579: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 588: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 609: 1376, 1376, 1376, 1376, 1376, 615: 1376, 1376, 618: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 632: 1376, 1376, 1376, 1376, 696: 1376, 703: 1376}, - {1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 532: 1377, 1377, 1377, 536: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 549: 1377, 1377, 1377, 554: 1377, 1377, 1377, 1377, 1377, 1377, 561: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 579: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 588: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 609: 1377, 1377, 1377, 1377, 1377, 615: 1377, 1377, 618: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 632: 1377, 1377, 1377, 1377, 696: 1377, 703: 1377}, - {57: 2182, 560: 4333, 1180: 4332, 4334}, - {57: 2181}, - {57: 2180}, - // 1480 - {57: 4335}, - {1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 532: 1378, 1378, 1378, 536: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 549: 1378, 1378, 1378, 554: 1378, 1378, 1378, 1378, 1378, 1378, 561: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 579: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 588: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 609: 1378, 1378, 1378, 1378, 1378, 615: 1378, 1378, 618: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 632: 1378, 1378, 1378, 1378, 696: 1378, 703: 1378}, - {57: 2182, 560: 4333, 1180: 4332, 4337}, - {57: 4338}, - {1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 532: 1379, 1379, 1379, 536: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 549: 1379, 1379, 1379, 554: 1379, 1379, 1379, 1379, 1379, 1379, 561: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 579: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 588: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 609: 1379, 1379, 1379, 1379, 1379, 615: 1379, 1379, 618: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 632: 1379, 1379, 1379, 1379, 696: 1379, 703: 1379}, - // 1485 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4340}, - {9: 4341, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4342}, - {57: 4343, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 532: 1381, 1381, 1381, 536: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 549: 1381, 1381, 1381, 554: 1381, 1381, 1381, 1381, 1381, 1381, 561: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 579: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 588: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 609: 1381, 1381, 1381, 1381, 1381, 615: 1381, 1381, 618: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 632: 1381, 1381, 1381, 1381, 696: 1381, 703: 1381}, - // 1490 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4346}, - {9: 3990, 57: 2183}, - {57: 4347}, - {1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 532: 1382, 1382, 1382, 536: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 549: 1382, 1382, 1382, 554: 1382, 1382, 1382, 1382, 1382, 1382, 561: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 579: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 588: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 609: 1382, 1382, 1382, 1382, 1382, 615: 1382, 1382, 618: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 632: 1382, 1382, 1382, 1382, 696: 1382, 703: 1382}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4349}, - // 1495 - {9: 3990, 57: 4350, 542: 4351}, - {1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 532: 1387, 1387, 1387, 536: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 549: 1387, 1387, 1387, 554: 1387, 1387, 1387, 1387, 1387, 1387, 561: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 579: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 588: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 609: 1387, 1387, 1387, 1387, 1387, 615: 1387, 1387, 618: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 632: 1387, 1387, 1387, 1387, 696: 1387, 703: 1387}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4352}, - {57: 4355}, - {955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 57: 955, 131: 955, 154: 955, 530: 955, 955, 955, 534: 955, 955, 955, 955, 955, 955, 546: 955, 955, 955, 955, 552: 955, 955, 557: 955, 565: 955, 586: 955, 608: 955, 695: 955, 701: 955, 955, 704: 955, 955, 955, 955, 955, 955, 955, 721: 955, 955}, - // 1500 - {954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 57: 954, 131: 954, 154: 954, 530: 954, 954, 954, 534: 954, 954, 954, 954, 954, 954, 546: 954, 954, 954, 954, 552: 954, 954, 557: 954, 565: 954, 586: 954, 608: 954, 695: 954, 701: 954, 954, 704: 954, 954, 954, 954, 954, 954, 954, 721: 954, 954}, - {1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 532: 1386, 1386, 1386, 536: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 549: 1386, 1386, 1386, 554: 1386, 1386, 1386, 1386, 1386, 1386, 561: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 579: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 588: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 609: 1386, 1386, 1386, 1386, 1386, 615: 1386, 1386, 618: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 632: 1386, 1386, 1386, 1386, 696: 1386, 703: 1386}, - {1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 532: 1388, 1388, 1388, 536: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 549: 1388, 1388, 1388, 554: 1388, 1388, 1388, 1388, 1388, 1388, 561: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 579: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 588: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 609: 1388, 1388, 1388, 1388, 1388, 615: 1388, 1388, 618: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 632: 1388, 1388, 1388, 1388, 696: 1388, 703: 1388}, - {57: 4358, 560: 4359}, - {1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 532: 1310, 1310, 1310, 536: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 549: 1310, 1310, 1310, 554: 1310, 1310, 1310, 1310, 1310, 1310, 561: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 579: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 588: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 609: 1310, 1310, 1310, 1310, 1310, 615: 1310, 1310, 618: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 632: 1310, 1310, 1310, 1310, 696: 1310, 703: 1310}, - // 1505 - {57: 4360}, - {1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 532: 1309, 1309, 1309, 536: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 549: 1309, 1309, 1309, 554: 1309, 1309, 1309, 1309, 1309, 1309, 561: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 579: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 588: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 609: 1309, 1309, 1309, 1309, 1309, 615: 1309, 1309, 618: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 632: 1309, 1309, 1309, 1309, 696: 1309, 703: 1309}, - {57: 4362}, - {1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 532: 1389, 1389, 1389, 536: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 549: 1389, 1389, 1389, 554: 1389, 1389, 1389, 1389, 1389, 1389, 561: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 579: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 588: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 609: 1389, 1389, 1389, 1389, 1389, 615: 1389, 1389, 618: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 632: 1389, 1389, 1389, 1389, 696: 1389, 703: 1389}, - {57: 4365}, - // 1510 - {1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 532: 1390, 1390, 1390, 536: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 549: 1390, 1390, 1390, 554: 1390, 1390, 1390, 1390, 1390, 1390, 561: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 579: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 588: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 609: 1390, 1390, 1390, 1390, 1390, 615: 1390, 1390, 618: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 632: 1390, 1390, 1390, 1390, 696: 1390, 703: 1390}, - {1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 532: 1404, 1404, 1404, 536: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 549: 1404, 1404, 1404, 554: 1404, 1404, 1404, 1404, 1404, 1404, 561: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 579: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 588: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 609: 1404, 1404, 1404, 1404, 1404, 615: 1404, 1404, 618: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 632: 1404, 1404, 1404, 1404, 696: 1404, 703: 1404, 712: 1404, 717: 1404, 725: 1404}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4367}, - {57: 4368}, - {1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 532: 1391, 1391, 1391, 536: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 549: 1391, 1391, 1391, 554: 1391, 1391, 1391, 1391, 1391, 1391, 561: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 579: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 588: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 609: 1391, 1391, 1391, 1391, 1391, 615: 1391, 1391, 618: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 632: 1391, 1391, 1391, 1391, 696: 1391, 703: 1391}, - // 1515 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4370}, - {57: 4371}, - {1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 532: 1392, 1392, 1392, 536: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 549: 1392, 1392, 1392, 554: 1392, 1392, 1392, 1392, 1392, 1392, 561: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 579: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 588: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 609: 1392, 1392, 1392, 1392, 1392, 615: 1392, 1392, 618: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 632: 1392, 1392, 1392, 1392, 696: 1392, 703: 1392}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4373}, - {9: 4374, 542: 4375, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1520 - {59: 4386, 118: 4382, 172: 4388, 175: 4383, 4381, 179: 4385, 553: 4393, 586: 4379, 709: 4392, 734: 4384, 4389, 4390, 739: 4391, 814: 4387, 951: 4380, 1120: 4378}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4376}, - {57: 4377}, - {1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 532: 1452, 1452, 1452, 536: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 549: 1452, 1452, 1452, 554: 1452, 1452, 1452, 1452, 1452, 1452, 561: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 579: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 588: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 609: 1452, 1452, 1452, 1452, 1452, 615: 1452, 1452, 618: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 632: 1452, 1452, 1452, 1452, 696: 1452, 703: 1452}, - {57: 4429}, - // 1525 - {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4428}, - {16: 465, 57: 465, 531: 4408, 553: 465, 586: 465, 709: 465, 721: 465, 841: 4409, 888: 4413}, - {57: 1270, 721: 1270}, - {57: 1269, 721: 1269}, - {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4412}, - // 1530 - {57: 458, 531: 4395, 721: 458, 841: 4396, 1003: 4411, 1010: 4397}, - {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4407}, - {57: 531, 721: 531, 737: 4404, 4405, 1223: 4406}, - {57: 531, 721: 531, 737: 4404, 4405, 1223: 4403}, - {57: 1263, 721: 1263}, - // 1535 - {57: 1262, 721: 1262}, - {57: 458, 531: 4395, 721: 458, 841: 4396, 1003: 4394, 1010: 4397}, - {57: 1260, 721: 1260}, - {16: 503, 57: 503, 531: 503, 553: 503, 586: 503, 709: 503, 721: 503}, - {16: 502, 57: 502, 531: 502, 553: 502, 586: 502, 709: 502, 721: 502}, - // 1540 - {57: 1261, 721: 1261}, - {560: 3037, 799: 3866, 816: 4398}, - {457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 57: 457, 59: 457, 530: 457, 534: 457, 457, 457, 457, 457, 546: 457, 548: 457, 701: 457, 457, 704: 457, 457, 457, 457, 457, 721: 457, 814: 457, 819: 457}, - {456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 57: 456, 59: 456, 530: 456, 534: 456, 456, 456, 456, 456, 546: 456, 548: 456, 701: 456, 456, 704: 456, 456, 456, 456, 456, 721: 456, 814: 456, 819: 456}, - {9: 4400, 57: 4399}, - // 1545 - {466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 16: 466, 57: 466, 59: 466, 151: 466, 466, 466, 530: 466, 534: 466, 466, 466, 466, 466, 546: 466, 548: 466, 553: 466, 577: 466, 586: 466, 605: 466, 701: 466, 466, 704: 466, 466, 466, 466, 466, 466, 721: 466, 814: 466, 819: 466}, - {560: 3037, 799: 3866, 816: 4401}, - {57: 4402}, - {455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 57: 455, 59: 455, 530: 455, 534: 455, 455, 455, 455, 455, 546: 455, 548: 455, 701: 455, 455, 704: 455, 455, 455, 455, 455, 721: 455, 814: 455, 819: 455}, - {57: 1264, 721: 1264}, - // 1550 - {57: 530, 721: 530}, - {57: 529, 721: 529}, - {57: 1265, 721: 1265}, - {57: 1266, 721: 1266}, - {560: 3037, 799: 3866, 816: 4410}, - // 1555 - {464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 16: 464, 57: 464, 59: 464, 151: 464, 464, 464, 530: 464, 534: 464, 464, 464, 464, 464, 546: 464, 548: 464, 553: 464, 577: 464, 586: 464, 605: 464, 701: 464, 464, 704: 464, 464, 464, 464, 464, 464, 721: 464, 814: 464, 819: 464}, - {57: 4399}, - {57: 1267, 721: 1267}, - {57: 1268, 721: 1268}, - {16: 4418, 57: 452, 553: 4419, 586: 4415, 709: 4417, 721: 452, 853: 4416, 896: 4414}, - // 1560 - {57: 1271, 721: 1271}, - {449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 16: 4418, 57: 449, 530: 449, 534: 449, 449, 449, 449, 449, 546: 449, 548: 449, 553: 4419, 701: 449, 449, 704: 449, 449, 449, 449, 449, 4417, 721: 449, 853: 4426, 1400: 4425}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4422}, - {557: 4421}, - {446, 446, 446, 446, 446, 446, 446, 446, 446, 10: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 58: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 533: 446, 535: 446, 555: 446, 563: 446, 579: 446, 586: 446}, - // 1565 - {557: 4420}, - {445, 445, 445, 445, 445, 445, 445, 445, 445, 10: 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 58: 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 533: 445, 535: 445, 555: 445, 563: 445, 579: 445, 586: 445}, - {447, 447, 447, 447, 447, 447, 447, 447, 447, 10: 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 58: 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 533: 447, 535: 447, 555: 447, 563: 447, 579: 447, 586: 447}, - {454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 57: 454, 530: 454, 534: 454, 454, 454, 454, 454, 546: 454, 548: 454, 586: 4423, 701: 454, 454, 704: 454, 454, 454, 454, 454, 721: 454, 1399: 4424}, - {453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 57: 453, 530: 453, 534: 453, 453, 453, 453, 453, 546: 453, 548: 453, 701: 453, 453, 704: 453, 453, 453, 453, 453, 721: 453}, - // 1570 - {450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 57: 450, 530: 450, 534: 450, 450, 450, 450, 450, 546: 450, 548: 450, 701: 450, 450, 704: 450, 450, 450, 450, 450, 721: 450}, - {451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 57: 451, 530: 451, 534: 451, 451, 451, 451, 451, 546: 451, 548: 451, 701: 451, 451, 704: 451, 451, 451, 451, 451, 721: 451}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4427}, - {448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 57: 448, 530: 448, 534: 448, 448, 448, 448, 448, 546: 448, 548: 448, 701: 448, 448, 704: 448, 448, 448, 448, 448, 721: 448}, - {57: 1272, 721: 1272}, - // 1575 - {1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 532: 1453, 1453, 1453, 536: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 549: 1453, 1453, 1453, 554: 1453, 1453, 1453, 1453, 1453, 1453, 561: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 579: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 588: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 609: 1453, 1453, 1453, 1453, 1453, 615: 1453, 1453, 618: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 632: 1453, 1453, 1453, 1453, 696: 1453, 703: 1453}, - {566: 3745, 3743, 3744, 3742, 3740, 589: 1278, 800: 3741, 3739}, - {589: 4434, 1303: 4433, 1501: 4432}, - {97: 1274, 589: 4434, 4440, 1303: 4439, 1353: 4438}, - {97: 1277, 589: 1277, 1277}, - // 1580 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4435}, - {566: 3745, 3743, 3744, 3742, 3740, 607: 4436, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4437}, - {97: 1275, 566: 3745, 3743, 3744, 3742, 3740, 589: 1275, 1275, 800: 3741, 3739}, - {97: 4442}, - // 1585 - {97: 1276, 589: 1276, 1276}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4441}, - {97: 1273, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 532: 1454, 1454, 1454, 536: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 549: 1454, 1454, 1454, 554: 1454, 1454, 1454, 1454, 1454, 1454, 561: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 579: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 588: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 609: 1454, 1454, 1454, 1454, 1454, 615: 1454, 1454, 618: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 632: 1454, 1454, 1454, 1454, 696: 1454, 703: 1454}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4444}, - // 1590 - {537: 4445, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {59: 4386, 118: 4382, 172: 4388, 175: 4383, 4381, 179: 4385, 553: 4393, 586: 4379, 709: 4392, 734: 4384, 4389, 4390, 739: 4391, 814: 4387, 951: 4380, 1120: 4446}, - {57: 1447, 721: 4448, 1320: 4447}, - {57: 4449}, - {57: 1446}, - // 1595 - {1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 532: 1455, 1455, 1455, 536: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 549: 1455, 1455, 1455, 554: 1455, 1455, 1455, 1455, 1455, 1455, 561: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 579: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 588: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 609: 1455, 1455, 1455, 1455, 1455, 615: 1455, 1455, 618: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 632: 1455, 1455, 1455, 1455, 696: 1455, 703: 1455}, - {1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 532: 1456, 1456, 1456, 536: 1456, 1456, 3693, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 549: 1456, 1456, 1456, 554: 1456, 1456, 1456, 1456, 1456, 1456, 561: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 579: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 588: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 609: 1456, 1456, 1456, 1456, 1456, 615: 1456, 1456, 618: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 632: 1456, 1456, 1456, 1456, 696: 1456, 703: 1456}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4452}, - {566: 3745, 3743, 3744, 3742, 3740, 585: 4453, 800: 3741, 3739}, - {1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 532: 1457, 1457, 1457, 536: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 549: 1457, 1457, 1457, 554: 1457, 1457, 1457, 1457, 1457, 1457, 561: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 579: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 588: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 609: 1457, 1457, 1457, 1457, 1457, 615: 1457, 1457, 618: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 632: 1457, 1457, 1457, 1457, 696: 1457, 703: 1457}, - // 1600 - {1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 532: 1458, 1458, 1458, 536: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 549: 1458, 1458, 1458, 554: 1458, 1458, 1458, 1458, 1458, 1458, 561: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 579: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 588: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 609: 1458, 1458, 1458, 1458, 1458, 615: 1458, 1458, 618: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 632: 1458, 1458, 1458, 1458, 696: 1458, 703: 1458}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4456}, - {9: 4457}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4458}, - {9: 2189, 57: 4459, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1605 - {1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 532: 1459, 1459, 1459, 536: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 549: 1459, 1459, 1459, 554: 1459, 1459, 1459, 1459, 1459, 1459, 561: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 579: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 588: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 609: 1459, 1459, 1459, 1459, 1459, 615: 1459, 1459, 618: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 632: 1459, 1459, 1459, 1459, 696: 1459, 703: 1459}, - {9: 2190, 57: 4465, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {9: 4462}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4463}, - {9: 2189, 57: 4464, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1610 - {1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 532: 1460, 1460, 1460, 536: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 549: 1460, 1460, 1460, 554: 1460, 1460, 1460, 1460, 1460, 1460, 561: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 579: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 588: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 609: 1460, 1460, 1460, 1460, 1460, 615: 1460, 1460, 618: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 632: 1460, 1460, 1460, 1460, 696: 1460, 703: 1460}, - {1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 532: 1461, 1461, 1461, 536: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 549: 1461, 1461, 1461, 554: 1461, 1461, 1461, 1461, 1461, 1461, 561: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 579: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 588: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 609: 1461, 1461, 1461, 1461, 1461, 615: 1461, 1461, 618: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 632: 1461, 1461, 1461, 1461, 696: 1461, 703: 1461}, - {1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 532: 1463, 1463, 1463, 536: 1463, 1463, 3693, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 549: 1463, 1463, 1463, 554: 1463, 1463, 1463, 1463, 1463, 1463, 561: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 579: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 588: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 609: 1463, 1463, 1463, 1463, 1463, 615: 1463, 1463, 618: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 632: 1463, 1463, 1463, 1463, 696: 1463, 703: 1463}, - {1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 532: 1465, 1465, 1465, 536: 1465, 1465, 3693, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 549: 1465, 1465, 1465, 554: 1465, 1465, 1465, 1465, 1465, 1465, 561: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 579: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 588: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 609: 1465, 1465, 1465, 1465, 1465, 615: 1465, 1465, 618: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 632: 1465, 1465, 1465, 1465, 696: 1465, 703: 1465}, - {1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 532: 1466, 1466, 1466, 536: 1466, 1466, 3693, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 549: 1466, 1466, 1466, 554: 1466, 1466, 1466, 1466, 1466, 1466, 561: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 579: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 588: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 609: 1466, 1466, 1466, 1466, 1466, 615: 1466, 1466, 618: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 632: 1466, 1466, 1466, 1466, 696: 1466, 703: 1466}, - // 1615 - {1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 532: 1467, 1467, 1467, 536: 1467, 1467, 3693, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 549: 1467, 1467, 1467, 554: 1467, 1467, 1467, 1467, 1467, 1467, 561: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 579: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 588: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 609: 1467, 1467, 1467, 1467, 1467, 615: 1467, 1467, 618: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 632: 1467, 1467, 1467, 1467, 696: 1467, 703: 1467}, - {1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 532: 1468, 1468, 1468, 536: 1468, 1468, 3693, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 549: 1468, 1468, 1468, 554: 1468, 1468, 1468, 1468, 1468, 1468, 561: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 579: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 588: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 609: 1468, 1468, 1468, 1468, 1468, 615: 1468, 1468, 618: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 632: 1468, 1468, 1468, 1468, 696: 1468, 703: 1468}, - {533: 4474}, - {533: 4473}, - {1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 532: 1448, 1448, 1448, 536: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 549: 1448, 1448, 1448, 554: 1448, 1448, 1448, 1448, 1448, 1448, 561: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 579: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 588: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 609: 1448, 1448, 1448, 1448, 1448, 615: 1448, 1448, 618: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 632: 1448, 1448, 1448, 1448, 696: 1448, 703: 1448}, - // 1620 - {1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 532: 1449, 1449, 1449, 536: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 549: 1449, 1449, 1449, 554: 1449, 1449, 1449, 1449, 1449, 1449, 561: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 579: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 588: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 609: 1449, 1449, 1449, 1449, 1449, 615: 1449, 1449, 618: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 632: 1449, 1449, 1449, 1449, 696: 1449, 703: 1449}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4476, 3051, 3052, 3050}, - {1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 4477, 1480, 1480, 1480, 536: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 549: 1480, 1480, 1480, 554: 1480, 1480, 1480, 1480, 1480, 1480, 561: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 579: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 588: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 609: 1480, 1480, 1480, 1480, 1480, 615: 1480, 1480, 618: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 632: 1480, 1480, 1480, 1480, 696: 1480, 703: 1480, 716: 3906, 718: 1480, 1480}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4478}, - {57: 4479}, - // 1625 - {1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 532: 1312, 1312, 1312, 536: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 549: 1312, 1312, 1312, 554: 1312, 1312, 1312, 1312, 1312, 1312, 561: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 579: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 588: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 609: 1312, 1312, 1312, 1312, 1312, 615: 1312, 1312, 618: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 632: 1312, 1312, 1312, 1312, 696: 1312, 703: 1312}, - {1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 532: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 548: 1519, 1519, 1519, 1519, 554: 1519, 1519, 1519, 1519, 1519, 1519, 561: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 579: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 588: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 609: 1519, 1519, 1519, 1519, 1519, 615: 1519, 1519, 618: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 632: 1519, 1519, 1519, 1519, 696: 1519, 701: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519}, - {1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 532: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 548: 1516, 1516, 1516, 1516, 554: 1516, 1516, 1516, 1516, 1516, 1516, 561: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 579: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 588: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 609: 1516, 1516, 1516, 1516, 1516, 615: 1516, 1516, 618: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 632: 1516, 1516, 1516, 1516, 696: 1516, 701: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516}, - {1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 532: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 548: 1515, 1515, 1515, 1515, 554: 1515, 1515, 1515, 1515, 1515, 1515, 561: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 579: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 588: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 609: 1515, 1515, 1515, 1515, 1515, 615: 1515, 1515, 618: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 632: 1515, 1515, 1515, 1515, 696: 1515, 701: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515}, - {1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 532: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 548: 1513, 1513, 1513, 1513, 554: 1513, 1513, 1513, 1513, 1513, 1513, 561: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 579: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 588: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 609: 1513, 1513, 1513, 1513, 1513, 615: 1513, 1513, 618: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 632: 1513, 1513, 1513, 1513, 696: 1513, 701: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513}, - // 1630 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4486, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4485}, - {57: 4490, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4487}, - {57: 4488, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4489}, - // 1635 - {1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 532: 1320, 1320, 1320, 536: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 549: 1320, 1320, 1320, 554: 1320, 1320, 1320, 1320, 1320, 1320, 561: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 579: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 588: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 609: 1320, 1320, 1320, 1320, 1320, 615: 1320, 1320, 618: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 632: 1320, 1320, 1320, 1320, 696: 1320, 703: 1320}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4491}, - {1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 532: 1321, 1321, 1321, 536: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 549: 1321, 1321, 1321, 554: 1321, 1321, 1321, 1321, 1321, 1321, 561: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 579: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 588: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 609: 1321, 1321, 1321, 1321, 1321, 615: 1321, 1321, 618: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 632: 1321, 1321, 1321, 1321, 696: 1321, 703: 1321}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4494, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4493}, - {9: 4504, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1640 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4495}, - {9: 4496, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4498, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4497}, - {57: 4502, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4499}, - // 1645 - {57: 4500, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4501}, - {1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 532: 1316, 1316, 1316, 536: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 549: 1316, 1316, 1316, 554: 1316, 1316, 1316, 1316, 1316, 1316, 561: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 579: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 588: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 609: 1316, 1316, 1316, 1316, 1316, 615: 1316, 1316, 618: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 632: 1316, 1316, 1316, 1316, 696: 1316, 703: 1316}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4503}, - {1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 532: 1318, 1318, 1318, 536: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 549: 1318, 1318, 1318, 554: 1318, 1318, 1318, 1318, 1318, 1318, 561: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 579: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 588: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 609: 1318, 1318, 1318, 1318, 1318, 615: 1318, 1318, 618: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 632: 1318, 1318, 1318, 1318, 696: 1318, 703: 1318}, - // 1650 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4506, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4505}, - {57: 4510, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4507}, - {57: 4508, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4509}, - // 1655 - {1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 532: 1317, 1317, 1317, 536: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 549: 1317, 1317, 1317, 554: 1317, 1317, 1317, 1317, 1317, 1317, 561: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 579: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 588: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 609: 1317, 1317, 1317, 1317, 1317, 615: 1317, 1317, 618: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 632: 1317, 1317, 1317, 1317, 696: 1317, 703: 1317}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4511}, - {1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 532: 1319, 1319, 1319, 536: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 549: 1319, 1319, 1319, 554: 1319, 1319, 1319, 1319, 1319, 1319, 561: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 579: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 588: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 609: 1319, 1319, 1319, 1319, 1319, 615: 1319, 1319, 618: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 632: 1319, 1319, 1319, 1319, 696: 1319, 703: 1319}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 886: 4513}, - {9: 4514}, - // 1660 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4515}, - {9: 4516, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4517}, - {57: 4518, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 532: 1366, 1366, 1366, 536: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 549: 1366, 1366, 1366, 554: 1366, 1366, 1366, 1366, 1366, 1366, 561: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 579: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 588: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 609: 1366, 1366, 1366, 1366, 1366, 615: 1366, 1366, 618: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 632: 1366, 1366, 1366, 1366, 696: 1366, 703: 1366}, - // 1665 - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 886: 4520}, - {9: 4521}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4522}, - {9: 4523, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4524}, - // 1670 - {57: 4525, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 532: 1367, 1367, 1367, 536: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 549: 1367, 1367, 1367, 554: 1367, 1367, 1367, 1367, 1367, 1367, 561: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 579: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 588: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 609: 1367, 1367, 1367, 1367, 1367, 615: 1367, 1367, 618: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 632: 1367, 1367, 1367, 1367, 696: 1367, 703: 1367}, - {175: 4529, 4528, 179: 4530, 185: 4531, 1368: 4527}, - {9: 4532}, - {9: 1356}, - // 1675 - {9: 1355}, - {9: 1354}, - {9: 1353}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4533}, - {57: 4534, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 1680 - {1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 532: 1373, 1373, 1373, 536: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 549: 1373, 1373, 1373, 554: 1373, 1373, 1373, 1373, 1373, 1373, 561: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 579: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 588: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 609: 1373, 1373, 1373, 1373, 1373, 615: 1373, 1373, 618: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 632: 1373, 1373, 1373, 1373, 696: 1373, 703: 1373}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4536}, - {9: 4537}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 4539}, - {2225, 2225, 6: 2225, 2225, 2225, 2225, 15: 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 57: 2225, 86: 2225, 88: 2225, 90: 2225, 2225, 95: 2225, 2225, 98: 2225, 2225, 2225, 2225, 103: 2225, 133: 2225, 163: 2225, 2225, 2225, 2225, 535: 2225, 538: 2225, 2225, 553: 2225, 2225, 556: 2225, 563: 2225, 565: 2225, 709: 2225, 2225, 720: 2225}, - // 1685 - {57: 4545}, - {168, 168, 6: 168, 168, 168, 15: 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 57: 168, 86: 168, 88: 168, 90: 168, 168, 95: 168, 168, 98: 168, 168, 168, 168, 103: 168, 535: 168, 538: 168, 168, 553: 168, 565: 168, 709: 168, 168, 720: 168}, - {560: 3037, 799: 4538, 825: 4544}, - {560: 3037, 799: 4543}, - {166, 166, 6: 166, 166, 166, 15: 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 57: 166, 86: 166, 88: 166, 90: 166, 166, 95: 166, 166, 98: 166, 166, 166, 166, 103: 166, 535: 166, 538: 166, 166, 553: 166, 565: 166, 709: 166, 166, 720: 166}, - // 1690 - {167, 167, 6: 167, 167, 167, 15: 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 57: 167, 86: 167, 88: 167, 90: 167, 167, 95: 167, 167, 98: 167, 167, 167, 167, 103: 167, 535: 167, 538: 167, 167, 553: 167, 565: 167, 709: 167, 167, 720: 167}, - {1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 532: 1344, 1344, 1344, 536: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 549: 1344, 1344, 1344, 554: 1344, 1344, 1344, 1344, 1344, 1344, 561: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 579: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 588: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 609: 1344, 1344, 1344, 1344, 1344, 615: 1344, 1344, 618: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 632: 1344, 1344, 1344, 1344, 696: 1344, 703: 1344}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4547}, - {57: 4548}, - {1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 532: 1345, 1345, 1345, 536: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 549: 1345, 1345, 1345, 554: 1345, 1345, 1345, 1345, 1345, 1345, 561: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 579: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 588: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 609: 1345, 1345, 1345, 1345, 1345, 615: 1345, 1345, 618: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 632: 1345, 1345, 1345, 1345, 696: 1345, 703: 1345}, - // 1695 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4550}, - {57: 4551, 537: 4552, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 532: 1361, 1361, 1361, 536: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 549: 1361, 1361, 1361, 554: 1361, 1361, 1361, 1361, 1361, 1361, 561: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 579: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 588: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 609: 1361, 1361, 1361, 1361, 1361, 615: 1361, 1361, 618: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 632: 1361, 1361, 1361, 1361, 696: 1361, 703: 1361}, - {553: 4393, 586: 4554, 709: 4392, 951: 4553}, - {531: 4408, 841: 4557}, - // 1700 - {531: 4408, 841: 4555}, - {57: 4556}, - {1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 532: 1359, 1359, 1359, 536: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 549: 1359, 1359, 1359, 554: 1359, 1359, 1359, 1359, 1359, 1359, 561: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 579: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 588: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 609: 1359, 1359, 1359, 1359, 1359, 615: 1359, 1359, 618: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 632: 1359, 1359, 1359, 1359, 696: 1359, 703: 1359}, - {57: 4558}, - {1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 532: 1360, 1360, 1360, 536: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 549: 1360, 1360, 1360, 554: 1360, 1360, 1360, 1360, 1360, 1360, 561: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 579: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 588: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 609: 1360, 1360, 1360, 1360, 1360, 615: 1360, 1360, 618: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 632: 1360, 1360, 1360, 1360, 696: 1360, 703: 1360}, - // 1705 - {1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 532: 1383, 1383, 1383, 536: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 549: 1383, 1383, 1383, 554: 1383, 1383, 1383, 1383, 1383, 1383, 561: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 579: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 588: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 609: 1383, 1383, 1383, 1383, 1383, 615: 1383, 1383, 618: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 632: 1383, 1383, 1383, 1383, 696: 1383, 703: 1383}, - {1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 532: 1384, 1384, 1384, 536: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 549: 1384, 1384, 1384, 554: 1384, 1384, 1384, 1384, 1384, 1384, 561: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 579: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 588: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 609: 1384, 1384, 1384, 1384, 1384, 615: 1384, 1384, 618: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 632: 1384, 1384, 1384, 1384, 696: 1384, 703: 1384}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4562}, - {57: 4563}, - {1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 532: 1380, 1380, 1380, 536: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 549: 1380, 1380, 1380, 554: 1380, 1380, 1380, 1380, 1380, 1380, 561: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 579: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 588: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 609: 1380, 1380, 1380, 1380, 1380, 615: 1380, 1380, 618: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 632: 1380, 1380, 1380, 1380, 696: 1380, 703: 1380}, - // 1710 - {1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 532: 1385, 1385, 1385, 536: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 549: 1385, 1385, 1385, 554: 1385, 1385, 1385, 1385, 1385, 1385, 561: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 579: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 588: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 609: 1385, 1385, 1385, 1385, 1385, 615: 1385, 1385, 618: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 632: 1385, 1385, 1385, 1385, 696: 1385, 703: 1385}, - {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4566, 4177}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4567}, - {57: 4568, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4569}, - // 1715 - {1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 532: 1342, 1342, 1342, 536: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 549: 1342, 1342, 1342, 554: 1342, 1342, 1342, 1342, 1342, 1342, 561: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 579: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 588: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 609: 1342, 1342, 1342, 1342, 1342, 615: 1342, 1342, 618: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 632: 1342, 1342, 1342, 1342, 696: 1342, 703: 1342}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4571}, - {57: 4572}, - {1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 532: 1313, 1313, 1313, 536: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 549: 1313, 1313, 1313, 554: 1313, 1313, 1313, 1313, 1313, 1313, 561: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 579: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 588: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 609: 1313, 1313, 1313, 1313, 1313, 615: 1313, 1313, 618: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 632: 1313, 1313, 1313, 1313, 696: 1313, 703: 1313}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4574}, - // 1720 - {57: 4575}, - {2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 532: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 548: 2507, 2507, 2507, 2507, 554: 2507, 2507, 2507, 2507, 2507, 2507, 561: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 579: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 588: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 609: 2507, 2507, 2507, 2507, 2507, 615: 2507, 2507, 618: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 632: 2507, 2507, 2507, 2507, 696: 2507, 701: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507}, - {558: 4577}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4578}, - {2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 532: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 548: 2508, 2508, 2508, 2508, 554: 2508, 2508, 2508, 2508, 2508, 2508, 561: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 579: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 588: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 609: 2508, 2508, 2508, 2508, 2508, 615: 2508, 2508, 618: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 632: 2508, 2508, 2508, 2508, 696: 2508, 701: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508}, - // 1725 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4587, 3597, 3679, 3596, 3593}, - {117: 4583, 258: 4581, 272: 4582, 1257: 4584}, - {9: 2836, 57: 2836, 93: 2836, 134: 2836, 136: 2836, 158: 2836, 712: 2836}, - {9: 2835, 57: 2835, 93: 2835, 134: 2835, 136: 2835, 158: 2835, 712: 2835}, - {9: 2834, 57: 2834, 93: 2834, 134: 2834, 136: 2834, 158: 2834, 712: 2834}, - // 1730 - {712: 4585}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4586, 3597, 3679, 3596, 3593}, - {2, 2, 9: 2, 51: 2, 93: 2, 117: 2, 538: 3693, 696: 2, 703: 3694}, - {4, 4, 9: 4, 51: 4, 93: 4, 117: 4, 538: 3693, 696: 4, 703: 3694}, - {2: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 10: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 58: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 531: 2315, 533: 2315, 2315, 2315, 2315, 540: 2315, 2315, 543: 2315, 2315, 2315, 547: 2315, 2315, 2315, 552: 2315, 2315, 560: 2315, 578: 2315, 586: 2315, 2315, 614: 2315, 617: 2315, 628: 2315, 2315, 2315, 2315, 636: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 697: 2315, 2315, 2315, 2315, 711: 2315, 933: 2315}, - // 1735 - {229: 4591, 231: 4590, 933: 4592, 1256: 4593}, - {2833, 2833, 9: 2833, 51: 2833, 57: 2833, 93: 2833, 117: 2833, 134: 2833, 136: 2833, 696: 2833}, - {2832, 2832, 9: 2832, 51: 2832, 57: 2832, 93: 2832, 117: 2832, 134: 2832, 136: 2832, 696: 2832}, - {2831, 2831, 9: 2831, 51: 2831, 57: 2831, 93: 2831, 117: 2831, 134: 2831, 136: 2831, 696: 2831}, - {6, 6, 9: 6, 51: 6, 93: 6, 117: 6, 696: 6}, - // 1740 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 628: 3682, 770: 4595, 3051, 3052, 3050, 776: 4598, 936: 4597}, - {2463, 2463, 9: 2463, 51: 2463, 93: 2463, 110: 2463, 2463, 2463, 2463, 2463, 117: 2463, 696: 2463}, - {2462, 2462, 9: 2462, 51: 2462, 93: 2462, 110: 2462, 2462, 2462, 2462, 2462, 117: 2462, 696: 2462}, - {8, 8, 9: 8, 51: 8, 93: 8, 117: 8, 696: 8}, - {7, 7, 9: 7, 51: 7, 93: 7, 117: 7, 696: 7}, - // 1745 - {10, 10, 9: 10, 51: 10, 93: 10, 117: 10, 696: 10}, - {51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 4601, 3043}, - {9, 9, 9: 9, 51: 9, 93: 9, 117: 9, 696: 9}, - {27, 27, 158: 4609, 171: 4608, 174: 4607, 458: 4610, 1042: 4606, 1329: 4603, 4605, 1352: 4604}, - {28, 28}, - // 1750 - {26, 26, 9: 4626, 158: 4609, 171: 4608, 174: 4607, 1042: 4625}, - {25, 25}, - {24, 24, 9: 24, 158: 24, 171: 24, 174: 24}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 531: 2316, 533: 2316, 2316, 2316, 2316, 540: 2316, 2316, 543: 2316, 2316, 2316, 547: 2316, 2316, 552: 2316, 2316, 555: 4588, 560: 2316, 578: 2316, 586: 2316, 2316, 614: 2316, 617: 2316, 628: 2316, 2316, 2316, 2316, 636: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 697: 2316, 2316, 2316, 2316, 711: 2316, 802: 4623}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 531: 2316, 533: 2316, 2316, 2316, 2316, 540: 2316, 2316, 543: 2316, 2316, 2316, 547: 2316, 2316, 552: 2316, 2316, 555: 4588, 560: 2316, 578: 2316, 586: 2316, 2316, 614: 2316, 617: 2316, 628: 2316, 2316, 2316, 2316, 636: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 697: 2316, 2316, 2316, 2316, 711: 2316, 802: 4621}, - // 1755 - {533: 2316, 555: 4588, 641: 2316, 802: 4616}, - {410: 4613, 4612, 4614, 451: 4611, 4615}, - {17, 17}, - {16, 16}, - {15, 15}, - // 1760 - {14, 14}, - {13, 13}, - {533: 4617, 641: 4618}, - {19, 19, 9: 19, 158: 19, 171: 19, 174: 19}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4619}, - // 1765 - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4620}, - {18, 18, 9: 18, 158: 18, 171: 18, 174: 18}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4622}, - {20, 20, 9: 20, 158: 20, 171: 20, 174: 20, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4624}, - // 1770 - {21, 21, 9: 21, 158: 21, 171: 21, 174: 21, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {23, 23, 9: 23, 158: 23, 171: 23, 174: 23}, - {158: 4609, 171: 4608, 174: 4607, 1042: 4627}, - {22, 22, 9: 22, 158: 22, 171: 22, 174: 22}, - {285: 4631, 384: 4629, 894: 4630}, - // 1775 - {532: 4639, 582: 135, 1420: 4638}, - {533: 4637}, - {2: 4633, 533: 4632}, - {533: 4636}, - {533: 4634}, - // 1780 - {533: 4635}, - {136, 136}, - {137, 137}, - {138, 138}, - {582: 4645}, - // 1785 - {225: 4640}, - {731: 4641, 995: 4642}, - {185: 4643}, - {582: 134}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4644}, - // 1790 - {2120, 2120, 9: 2120, 57: 2120, 530: 2120, 532: 2120, 539: 2120, 2120, 2120, 2120, 549: 2120, 2120, 2120, 554: 2120, 556: 2120, 2120, 2120, 2120, 562: 2120, 2120, 2120, 2120, 3745, 3743, 3744, 3742, 3740, 2120, 2120, 2120, 2120, 2120, 2120, 580: 2120, 2120, 2120, 2120, 585: 2120, 592: 2120, 800: 3741, 3739}, - {246: 4658, 531: 2907, 2906, 4659, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 713: 4657, 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 4656}, - {549: 4675, 608: 2115, 958: 4674}, - {630, 630, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {632, 632, 539: 1014, 550: 1014, 1014}, - // 1795 - {637, 637}, - {636, 636}, - {635, 635}, - {634, 634}, - {633, 633}, - // 1800 - {631, 631}, - {629, 629}, - {144, 144}, - {246: 4668, 531: 2907, 2906, 4669, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 4667}, - {159: 4660}, - // 1805 - {140, 140}, - {424, 424, 554: 424, 556: 424, 563: 4661, 424, 889: 4662, 4663}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4666}, - {423, 423, 57: 423, 530: 423, 532: 423, 539: 423, 542: 423, 550: 423, 423, 554: 423, 556: 423, 558: 423, 423, 562: 423, 564: 423, 571: 423, 423, 574: 423}, - {1499, 1499, 554: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 4664}, - // 1810 - {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 4665}, - {142, 142}, - {425, 425, 57: 425, 530: 425, 532: 425, 539: 425, 542: 425, 550: 425, 425, 554: 425, 556: 425, 558: 425, 425, 562: 425, 564: 425, 566: 3745, 3743, 3744, 3742, 3740, 425, 425, 574: 425, 800: 3741, 3739}, - {143, 143}, - {159: 4670}, - // 1815 - {139, 139}, - {424, 424, 554: 424, 556: 424, 563: 4661, 424, 889: 4662, 4671}, - {1499, 1499, 554: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 4672}, - {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 4673}, - {141, 141}, - // 1820 - {608: 4676}, - {2: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 10: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 58: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 531: 2114, 559: 2114, 561: 2114, 608: 2114, 643: 2114}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4677}, - {2691, 2691, 2691, 2691, 2691, 2691, 4725, 4727, 580, 10: 4694, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 89: 4696, 4717, 4718, 102: 4719, 208: 4699, 234: 4688, 249: 4682, 251: 4680, 253: 4703, 256: 4704, 270: 4698, 276: 4714, 290: 4692, 299: 4700, 305: 4695, 324: 4705, 331: 4701, 338: 4715, 4716, 343: 4683, 532: 4713, 535: 4724, 538: 2446, 4761, 546: 2691, 553: 2446, 557: 4685, 562: 4720, 564: 4702, 4712, 646: 4686, 702: 4691, 709: 2446, 4730, 713: 4679, 724: 4707, 727: 4693, 729: 4721, 766: 4706, 4708, 769: 4687, 774: 4697, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 972: 4711, 986: 4709, 1020: 4684, 1028: 4689, 1110: 4723, 1285: 4690, 1309: 4710, 1315: 4722, 4678}, - {2444, 2444, 5551, 5553, 5554, 5552, 546: 5555, 1233: 5550, 1317: 5549}, - // 1825 - {546: 5523}, - {2849, 2849, 200: 5517, 546: 5518}, - {214: 5509}, - {533: 2316, 535: 2316, 555: 4588, 802: 5506}, - {533: 2316, 535: 2316, 555: 4588, 802: 5503}, - // 1830 - {2777, 2777, 2777, 2777, 2777, 2777, 4725, 4727, 580, 2777, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 546: 2777, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5500}, - {200: 5484, 207: 5485}, - {712: 5476}, - {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 531: 2696, 546: 5329, 578: 2696, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 928: 5331, 952: 4920, 975: 5327, 1002: 5328, 1015: 5330}, - {546: 5320}, - // 1835 - {2765, 2765, 2765, 2765, 2765, 2765, 9: 2765, 546: 2765}, - {2764, 2764, 2764, 2764, 2764, 2764, 9: 2764, 546: 2764}, - {546: 5318}, - {546: 5315}, - {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 546: 5294, 578: 2696, 701: 4906, 4921, 704: 5293, 706: 4922, 710: 4907, 746: 5297, 923: 5296, 952: 4920, 975: 5292, 1015: 5295, 1122: 5298}, - // 1840 - {546: 5285}, - {546: 5274}, - {546: 5272}, - {546: 5269}, - {546: 5266}, - // 1845 - {20: 5263, 546: 5262}, - {20: 5259, 546: 5258}, - {546: 5248}, - {723: 5241}, - {1055: 5240}, - // 1850 - {1055: 5239}, - {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 578: 2696, 952: 4920, 975: 5235}, - {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 578: 2696, 952: 4920, 975: 4946}, - {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 702: 4921, 706: 4922, 710: 4919, 952: 4920, 975: 4917, 1122: 4918}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 537: 4904, 555: 4588, 701: 4906, 710: 4907, 712: 4902, 802: 4903, 923: 4905, 952: 4901}, - // 1855 - {2732, 2732, 2732, 2732, 2732, 2732, 9: 2732, 546: 2732}, - {2731, 2731, 2731, 2731, 2731, 2731, 9: 2731, 546: 2731}, - {2730, 2730, 2730, 2730, 2730, 2730, 9: 2730, 546: 2730}, - {2729, 2729, 2729, 2729, 2729, 2729, 8: 579, 2729, 29: 579, 546: 2729}, - {247: 4900}, - // 1860 - {247: 4899}, - {2726, 2726, 2726, 2726, 2726, 2726, 9: 2726, 546: 2726}, - {2725, 2725, 2725, 2725, 2725, 2725, 9: 2725, 546: 2725}, - {2721, 2721, 2721, 2721, 2721, 2721, 9: 2721, 546: 2721}, - {2720, 2720, 2720, 2720, 2720, 2720, 9: 2720, 546: 2720}, - // 1865 - {56: 2316, 293: 2316, 315: 2316, 317: 2316, 535: 2316, 555: 4588, 802: 4893}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 535: 2316, 555: 4588, 802: 4890}, - {194: 4889, 768: 4888}, - {2690, 2690, 2690, 2690, 2690, 2690, 9: 4886, 546: 2690}, - {2689, 2689, 2689, 2689, 2689, 2689, 9: 2689, 546: 2689}, - // 1870 - {16: 2445, 18: 2445, 21: 2445, 538: 2445, 553: 2445, 709: 2445}, - {533: 2316, 555: 4588, 802: 4884}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4882}, - {23: 4877, 236: 4878, 300: 4879}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4875}, - // 1875 - {298: 4872}, - {298: 4869}, - {555: 4588, 560: 2316, 802: 4867}, - {555: 4588, 560: 2316, 802: 4865}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 555: 4588, 802: 4863}, - // 1880 - {555: 4588, 560: 2316, 802: 4861}, - {2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 15: 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 57: 2390, 530: 2390, 2390, 2390, 535: 2390, 537: 2390, 2390, 2390, 546: 2390, 2390, 549: 2390, 552: 2390, 2390, 565: 2390, 608: 2390, 695: 2390, 709: 2390, 2390}, - {617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 15: 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 530: 617, 617, 617, 535: 617, 537: 617, 617, 617, 546: 617, 617, 549: 617, 552: 617, 617, 565: 617, 608: 617, 695: 617, 709: 617, 617}, - {16: 4418, 538: 4856, 553: 4419, 709: 4417, 853: 4855}, - {8: 4849, 29: 4850}, - // 1885 - {555: 4588, 560: 2316, 802: 4847}, - {555: 4588, 560: 2316, 802: 4845}, - {533: 2316, 555: 4588, 802: 4843}, - {555: 4588, 560: 2316, 802: 4841}, - {555: 4588, 560: 2316, 802: 4839}, - // 1890 - {533: 2316, 555: 4588, 802: 4837}, - {533: 2316, 555: 4588, 802: 4835}, - {555: 4588, 560: 2316, 802: 4833}, - {555: 4588, 560: 2316, 802: 4831}, - {603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 15: 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 530: 603, 603, 603, 535: 603, 537: 603, 603, 603, 546: 603, 603, 549: 603, 552: 603, 603, 565: 603, 608: 603, 695: 603, 709: 603, 603}, - // 1895 - {535: 2316, 555: 4588, 560: 2316, 802: 4829}, - {535: 2316, 555: 4588, 560: 2316, 802: 4826}, - {535: 2316, 555: 4588, 560: 2316, 802: 4823}, - {555: 4588, 560: 2316, 802: 4821}, - {555: 4588, 560: 2316, 802: 4819}, - // 1900 - {555: 4588, 560: 2316, 636: 2316, 2316, 802: 4817}, - {533: 2316, 555: 4588, 802: 4815}, - {533: 2316, 555: 4588, 802: 4813}, - {555: 4588, 560: 2316, 802: 4811}, - {555: 4588, 560: 2316, 802: 4809}, - // 1905 - {535: 2316, 555: 4588, 560: 2316, 802: 4805}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 548: 2316, 555: 4588, 802: 4802}, - {531: 2316, 555: 4588, 802: 4797}, - {533: 2316, 555: 4588, 802: 4794}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 555: 4588, 802: 4788}, - // 1910 - {533: 2316, 555: 4588, 802: 4786}, - {533: 2316, 555: 4588, 802: 4784}, - {574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 15: 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 530: 574, 574, 574, 535: 574, 537: 574, 574, 574, 546: 574, 574, 549: 574, 552: 574, 574, 565: 574, 608: 574, 695: 574, 709: 574, 574}, - {177: 2316, 251: 2316, 255: 2316, 291: 2316, 332: 2316, 347: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 535: 2316, 555: 4588, 802: 4768}, - {177: 4771, 251: 4774, 255: 4770, 291: 4772, 332: 4773, 347: 4775, 4776, 4781, 4780, 4777, 4782, 4783, 4778, 4779, 535: 4769}, - // 1915 - {568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 15: 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 530: 568, 568, 568, 535: 568, 537: 568, 568, 568, 546: 568, 568, 549: 568, 552: 568, 568, 565: 568, 608: 568, 695: 568, 709: 568, 568}, - {567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 15: 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 530: 567, 567, 567, 535: 567, 537: 567, 567, 567, 546: 567, 567, 549: 567, 552: 567, 567, 565: 567, 608: 567, 695: 567, 709: 567, 567}, - {566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 15: 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 530: 566, 566, 566, 535: 566, 537: 566, 566, 566, 546: 566, 566, 549: 566, 552: 566, 566, 565: 566, 608: 566, 695: 566, 709: 566, 566}, - {565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 15: 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 530: 565, 565, 565, 535: 565, 537: 565, 565, 565, 546: 565, 565, 549: 565, 552: 565, 565, 565: 565, 608: 565, 695: 565, 709: 565, 565}, - {564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 15: 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 530: 564, 564, 564, 535: 564, 537: 564, 564, 564, 546: 564, 564, 549: 564, 552: 564, 564, 565: 564, 608: 564, 695: 564, 709: 564, 564}, - // 1920 - {563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 15: 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 530: 563, 563, 563, 535: 563, 537: 563, 563, 563, 546: 563, 563, 549: 563, 552: 563, 563, 565: 563, 608: 563, 695: 563, 709: 563, 563}, - {562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 15: 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 530: 562, 562, 562, 535: 562, 537: 562, 562, 562, 546: 562, 562, 549: 562, 552: 562, 562, 565: 562, 608: 562, 695: 562, 709: 562, 562}, - {561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 15: 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 530: 561, 561, 561, 535: 561, 537: 561, 561, 561, 546: 561, 561, 549: 561, 552: 561, 561, 565: 561, 608: 561, 695: 561, 709: 561, 561}, - {560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 15: 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 530: 560, 560, 560, 535: 560, 537: 560, 560, 560, 546: 560, 560, 549: 560, 552: 560, 560, 565: 560, 608: 560, 695: 560, 709: 560, 560}, - {559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 15: 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 530: 559, 559, 559, 535: 559, 537: 559, 559, 559, 546: 559, 559, 549: 559, 552: 559, 559, 565: 559, 608: 559, 695: 559, 709: 559, 559}, - // 1925 - {558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 15: 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 530: 558, 558, 558, 535: 558, 537: 558, 558, 558, 546: 558, 558, 549: 558, 552: 558, 558, 565: 558, 608: 558, 695: 558, 709: 558, 558}, - {557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 15: 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 530: 557, 557, 557, 535: 557, 537: 557, 557, 557, 546: 557, 557, 549: 557, 552: 557, 557, 565: 557, 608: 557, 695: 557, 709: 557, 557}, - {556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 15: 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 530: 556, 556, 556, 535: 556, 537: 556, 556, 556, 546: 556, 556, 549: 556, 552: 556, 556, 565: 556, 608: 556, 695: 556, 709: 556, 556}, - {555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 15: 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 530: 555, 555, 555, 535: 555, 537: 555, 555, 555, 546: 555, 555, 549: 555, 552: 555, 555, 565: 555, 608: 555, 695: 555, 709: 555, 555}, - {554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 15: 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 530: 554, 554, 554, 535: 554, 537: 554, 554, 554, 546: 554, 554, 549: 554, 552: 554, 554, 565: 554, 608: 554, 695: 554, 709: 554, 554}, - // 1930 - {533: 4785}, - {581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 15: 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 530: 581, 581, 581, 535: 581, 537: 581, 581, 581, 546: 581, 581, 549: 581, 552: 581, 581, 565: 581, 608: 581, 695: 581, 709: 581, 581}, - {533: 4787}, - {582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 15: 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 530: 582, 582, 582, 535: 582, 537: 582, 582, 582, 546: 582, 582, 549: 582, 552: 582, 582, 565: 582, 608: 582, 695: 582, 709: 582, 582}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4789, 3051, 3052, 3050}, - // 1935 - {543: 4790}, - {641: 4791}, - {533: 3586, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4792, 779: 3582}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 886: 3747, 901: 4793}, - {583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 15: 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 530: 583, 583, 583, 535: 583, 537: 583, 583, 583, 546: 583, 583, 549: 583, 552: 583, 583, 565: 583, 608: 583, 695: 583, 709: 583, 583}, - // 1940 - {533: 4796, 1167: 4795}, - {584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 15: 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 530: 584, 584, 584, 535: 584, 537: 584, 584, 584, 546: 584, 584, 549: 584, 552: 584, 584, 565: 584, 608: 584, 695: 584, 709: 584, 584}, - {148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 15: 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 530: 148, 148, 148, 535: 148, 537: 148, 148, 148, 546: 148, 148, 549: 148, 552: 148, 148, 557: 148, 565: 148, 608: 148, 695: 148, 709: 148, 148}, - {531: 4798}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 759, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 4799, 1293: 4800}, - // 1945 - {758, 758, 9: 3930, 57: 758, 532: 758}, - {57: 4801}, - {585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 15: 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 530: 585, 585, 585, 535: 585, 537: 585, 585, 585, 546: 585, 585, 549: 585, 552: 585, 585, 565: 585, 608: 585, 695: 585, 709: 585, 585}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 548: 4803, 770: 3737, 3051, 3052, 3050, 805: 4804}, - {587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 15: 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 530: 587, 587, 587, 535: 587, 537: 587, 587, 587, 546: 587, 587, 549: 587, 552: 587, 587, 565: 587, 608: 587, 695: 587, 709: 587, 587}, - // 1950 - {586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 15: 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 530: 586, 586, 586, 535: 586, 537: 586, 586, 586, 546: 586, 586, 549: 586, 552: 586, 586, 565: 586, 608: 586, 695: 586, 709: 586, 586}, - {535: 4807, 560: 3037, 799: 3866, 816: 4808, 1286: 4806}, - {590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 15: 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 530: 590, 590, 590, 535: 590, 537: 590, 590, 590, 546: 590, 590, 549: 590, 552: 590, 590, 565: 590, 608: 590, 695: 590, 709: 590, 590}, - {578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 15: 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 530: 578, 578, 578, 535: 578, 537: 578, 578, 578, 546: 578, 578, 549: 578, 552: 578, 578, 565: 578, 608: 578, 695: 578, 709: 578, 578}, - {577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 15: 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 530: 577, 577, 577, 535: 577, 537: 577, 577, 577, 546: 577, 577, 549: 577, 552: 577, 577, 565: 577, 608: 577, 695: 577, 709: 577, 577}, - // 1955 - {560: 3037, 799: 3866, 816: 4810}, - {591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 15: 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 530: 591, 591, 591, 535: 591, 537: 591, 591, 591, 546: 591, 591, 549: 591, 552: 591, 591, 565: 591, 608: 591, 695: 591, 709: 591, 591}, - {560: 3037, 799: 3866, 816: 4812}, - {592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 15: 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 530: 592, 592, 592, 535: 592, 537: 592, 592, 592, 546: 592, 592, 549: 592, 552: 592, 592, 565: 592, 608: 592, 695: 592, 709: 592, 592}, - {533: 4814}, - // 1960 - {593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 15: 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 530: 593, 593, 593, 535: 593, 537: 593, 593, 593, 546: 593, 593, 549: 593, 552: 593, 593, 565: 593, 608: 593, 695: 593, 709: 593, 593}, - {533: 4816}, - {594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 15: 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 530: 594, 594, 594, 535: 594, 537: 594, 594, 594, 546: 594, 594, 549: 594, 552: 594, 594, 565: 594, 608: 594, 695: 594, 709: 594, 594}, - {560: 4087, 636: 4089, 4088, 916: 4818}, - {595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 15: 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 530: 595, 595, 595, 535: 595, 537: 595, 595, 595, 546: 595, 595, 549: 595, 552: 595, 595, 565: 595, 608: 595, 695: 595, 709: 595, 595}, - // 1965 - {560: 3037, 799: 3866, 816: 4820}, - {596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 15: 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 530: 596, 596, 596, 535: 596, 537: 596, 596, 596, 546: 596, 596, 549: 596, 552: 596, 596, 565: 596, 608: 596, 695: 596, 709: 596, 596}, - {560: 3037, 799: 3866, 816: 4822}, - {597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 15: 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 530: 597, 597, 597, 535: 597, 537: 597, 597, 597, 546: 597, 597, 549: 597, 552: 597, 597, 565: 597, 608: 597, 695: 597, 709: 597, 597}, - {535: 4825, 560: 3037, 799: 3866, 816: 4824}, - // 1970 - {599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 15: 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 530: 599, 599, 599, 535: 599, 537: 599, 599, 599, 546: 599, 599, 549: 599, 552: 599, 599, 565: 599, 608: 599, 695: 599, 709: 599, 599}, - {598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 15: 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 530: 598, 598, 598, 535: 598, 537: 598, 598, 598, 546: 598, 598, 549: 598, 552: 598, 598, 565: 598, 608: 598, 695: 598, 709: 598, 598}, - {535: 4828, 560: 3037, 799: 3866, 816: 4827}, - {601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 15: 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 530: 601, 601, 601, 535: 601, 537: 601, 601, 601, 546: 601, 601, 549: 601, 552: 601, 601, 565: 601, 608: 601, 695: 601, 709: 601, 601}, - {600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 15: 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 530: 600, 600, 600, 535: 600, 537: 600, 600, 600, 546: 600, 600, 549: 600, 552: 600, 600, 565: 600, 608: 600, 695: 600, 709: 600, 600}, - // 1975 - {535: 4807, 560: 3037, 799: 3866, 816: 4808, 1286: 4830}, - {602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 15: 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 530: 602, 602, 602, 535: 602, 537: 602, 602, 602, 546: 602, 602, 549: 602, 552: 602, 602, 565: 602, 608: 602, 695: 602, 709: 602, 602}, - {560: 3037, 799: 3866, 816: 4832}, - {604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 15: 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 530: 604, 604, 604, 535: 604, 537: 604, 604, 604, 546: 604, 604, 549: 604, 552: 604, 604, 565: 604, 608: 604, 695: 604, 709: 604, 604}, - {560: 3037, 799: 3866, 816: 4834}, - // 1980 - {605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 15: 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 530: 605, 605, 605, 535: 605, 537: 605, 605, 605, 546: 605, 605, 549: 605, 552: 605, 605, 565: 605, 608: 605, 695: 605, 709: 605, 605}, - {533: 4836}, - {606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 15: 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 530: 606, 606, 606, 535: 606, 537: 606, 606, 606, 546: 606, 606, 549: 606, 552: 606, 606, 565: 606, 608: 606, 695: 606, 709: 606, 606}, - {533: 4838}, - {607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 15: 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 530: 607, 607, 607, 535: 607, 537: 607, 607, 607, 546: 607, 607, 549: 607, 552: 607, 607, 565: 607, 608: 607, 695: 607, 709: 607, 607}, - // 1985 - {560: 3037, 799: 3866, 816: 4840}, - {608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 15: 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 530: 608, 608, 608, 535: 608, 537: 608, 608, 608, 546: 608, 608, 549: 608, 552: 608, 608, 565: 608, 608: 608, 695: 608, 709: 608, 608}, - {560: 3037, 799: 3866, 816: 4842}, - {609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 15: 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 530: 609, 609, 609, 535: 609, 537: 609, 609, 609, 546: 609, 609, 549: 609, 552: 609, 609, 565: 609, 608: 609, 695: 609, 709: 609, 609}, - {533: 4844}, - // 1990 - {610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 15: 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 530: 610, 610, 610, 535: 610, 537: 610, 610, 610, 546: 610, 610, 549: 610, 552: 610, 610, 565: 610, 608: 610, 695: 610, 709: 610, 610}, - {560: 3037, 799: 3866, 816: 4846}, - {611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 15: 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 530: 611, 611, 611, 535: 611, 537: 611, 611, 611, 546: 611, 611, 549: 611, 552: 611, 611, 565: 611, 608: 611, 695: 611, 709: 611, 611}, - {560: 3037, 799: 3866, 816: 4848}, - {613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 15: 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 530: 613, 613, 613, 535: 613, 537: 613, 613, 613, 546: 613, 613, 549: 613, 552: 613, 613, 565: 613, 608: 613, 695: 613, 709: 613, 613}, - // 1995 - {555: 4588, 560: 2316, 802: 4853}, - {555: 4588, 560: 2316, 802: 4851}, - {560: 3037, 799: 3866, 816: 4852}, - {612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 15: 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 530: 612, 612, 612, 535: 612, 537: 612, 612, 612, 546: 612, 612, 549: 612, 552: 612, 612, 565: 612, 608: 612, 695: 612, 709: 612, 612}, - {560: 3037, 799: 3866, 816: 4854}, - // 2000 - {614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 15: 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 530: 614, 614, 614, 535: 614, 537: 614, 614, 614, 546: 614, 614, 549: 614, 552: 614, 614, 565: 614, 608: 614, 695: 614, 709: 614, 614}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 4859}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 4857}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 4858}, - {615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 15: 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 530: 615, 615, 615, 535: 615, 537: 615, 615, 615, 546: 615, 615, 549: 615, 552: 615, 615, 565: 615, 608: 615, 695: 615, 709: 615, 615}, - // 2005 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4860}, - {616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 15: 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 530: 616, 616, 616, 535: 616, 537: 616, 616, 616, 546: 616, 616, 549: 616, 552: 616, 616, 565: 616, 608: 616, 695: 616, 709: 616, 616}, - {560: 3037, 799: 3866, 816: 4862}, - {2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 15: 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 57: 2391, 530: 2391, 2391, 2391, 535: 2391, 537: 2391, 2391, 2391, 546: 2391, 2391, 549: 2391, 552: 2391, 2391, 565: 2391, 608: 2391, 695: 2391, 709: 2391, 2391}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4864, 3051, 3052, 3050}, - // 2010 - {2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 15: 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 57: 2392, 530: 2392, 2392, 2392, 535: 2392, 537: 2392, 2392, 2392, 546: 2392, 2392, 549: 2392, 552: 2392, 2392, 565: 2392, 608: 2392, 695: 2392, 709: 2392, 2392}, - {560: 3037, 799: 3866, 816: 4866}, - {2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 15: 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 57: 2393, 530: 2393, 2393, 2393, 535: 2393, 537: 2393, 2393, 2393, 546: 2393, 2393, 549: 2393, 552: 2393, 2393, 565: 2393, 608: 2393, 695: 2393, 709: 2393, 2393}, - {560: 3037, 799: 3866, 816: 4868}, - {2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 15: 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 57: 2394, 530: 2394, 2394, 2394, 535: 2394, 537: 2394, 2394, 2394, 546: 2394, 2394, 549: 2394, 552: 2394, 2394, 565: 2394, 608: 2394, 695: 2394, 709: 2394, 2394}, - // 2015 - {533: 2316, 555: 4588, 802: 4870}, - {533: 4871}, - {2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 15: 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 57: 2395, 530: 2395, 2395, 2395, 535: 2395, 537: 2395, 2395, 2395, 546: 2395, 2395, 549: 2395, 552: 2395, 2395, 565: 2395, 608: 2395, 695: 2395, 709: 2395, 2395}, - {533: 2316, 555: 4588, 802: 4873}, - {533: 4874}, - // 2020 - {2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 15: 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 57: 2396, 530: 2396, 2396, 2396, 535: 2396, 537: 2396, 2396, 2396, 546: 2396, 2396, 549: 2396, 552: 2396, 2396, 565: 2396, 608: 2396, 695: 2396, 709: 2396, 2396}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4876}, - {2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 15: 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 57: 2397, 530: 2397, 2397, 2397, 535: 2397, 537: 2397, 2397, 2397, 546: 2397, 2397, 549: 2397, 552: 2397, 2397, 565: 2397, 608: 2397, 695: 2397, 709: 2397, 2397}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4880}, - {589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 15: 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 530: 589, 589, 589, 535: 589, 537: 589, 589, 589, 546: 589, 589, 549: 589, 552: 589, 589, 565: 589, 608: 589, 695: 589, 709: 589, 589}, - // 2025 - {588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 15: 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 530: 588, 588, 588, 535: 588, 537: 588, 588, 588, 546: 588, 588, 549: 588, 552: 588, 588, 565: 588, 608: 588, 695: 588, 709: 588, 588}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4881}, - {2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 15: 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 57: 2398, 530: 2398, 2398, 2398, 535: 2398, 537: 2398, 2398, 2398, 546: 2398, 2398, 549: 2398, 552: 2398, 2398, 565: 2398, 608: 2398, 695: 2398, 709: 2398, 2398}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4883}, - {2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 15: 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 57: 2399, 530: 2399, 2399, 2399, 535: 2399, 537: 2399, 2399, 2399, 546: 2399, 2399, 549: 2399, 552: 2399, 2399, 565: 2399, 608: 2399, 695: 2399, 709: 2399, 2399}, - // 2030 - {533: 4885}, - {2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 15: 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 57: 2400, 530: 2400, 2400, 2400, 535: 2400, 537: 2400, 2400, 2400, 546: 2400, 2400, 549: 2400, 552: 2400, 2400, 565: 2400, 608: 2400, 695: 2400, 709: 2400, 2400}, - {6: 4725, 4727, 580, 10: 4694, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 89: 4696, 4717, 4718, 102: 4719, 208: 4699, 234: 4688, 249: 4682, 253: 4703, 256: 4704, 270: 4698, 276: 4714, 290: 4692, 299: 4700, 305: 4695, 324: 4705, 331: 4701, 338: 4715, 4716, 343: 4683, 532: 4713, 535: 4724, 538: 2446, 4761, 553: 2446, 557: 4685, 562: 4720, 564: 4702, 4712, 646: 4686, 702: 4691, 709: 2446, 4730, 724: 4707, 727: 4693, 729: 4721, 766: 4706, 4708, 769: 4687, 774: 4697, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 972: 4711, 986: 4709, 1020: 4684, 1028: 4689, 1110: 4887, 1285: 4690, 1309: 4710}, - {2688, 2688, 2688, 2688, 2688, 2688, 9: 2688, 546: 2688}, - {2702, 2702, 2702, 2702, 2702, 2702, 9: 2702, 546: 2702}, - // 2035 - {2701, 2701, 2701, 2701, 2701, 2701, 9: 2701, 546: 2701}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4891, 770: 4892, 3051, 3052, 3050}, - {2704, 2704, 2704, 2704, 2704, 2704, 9: 2704, 102: 2704, 546: 2704}, - {2703, 2703, 2703, 2703, 2703, 2703, 9: 2703, 102: 2703, 546: 2703}, - {56: 4898, 293: 4895, 315: 4896, 317: 4897, 535: 4894}, - // 2040 - {2709, 2709, 2709, 2709, 2709, 2709, 9: 2709, 546: 2709, 562: 2709}, - {2708, 2708, 2708, 2708, 2708, 2708, 9: 2708, 546: 2708, 562: 2708}, - {2707, 2707, 2707, 2707, 2707, 2707, 9: 2707, 546: 2707, 562: 2707}, - {2706, 2706, 2706, 2706, 2706, 2706, 9: 2706, 546: 2706, 562: 2706}, - {2705, 2705, 2705, 2705, 2705, 2705, 9: 2705, 546: 2705, 562: 2705}, - // 2045 - {2727, 2727, 2727, 2727, 2727, 2727, 9: 2727, 546: 2727}, - {2728, 2728, 2728, 2728, 2728, 2728, 9: 2728, 546: 2728}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4914, 3051, 3052, 3050}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4913}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4912}, - // 2050 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4911}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4908, 3051, 3052, 3050}, - {2: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 10: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 58: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 531: 2700, 542: 2700, 558: 2700, 578: 2700}, - {2: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 10: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 58: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 531: 2699, 542: 2699, 558: 2699, 578: 2699}, - {712: 4909}, - // 2055 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4910, 3051, 3052, 3050}, - {2733, 2733, 2733, 2733, 2733, 2733, 9: 2733, 546: 2733}, - {2734, 2734, 2734, 2734, 2734, 2734, 9: 2734, 546: 2734}, - {2735, 2735, 2735, 2735, 2735, 2735, 9: 2735, 546: 2735}, - {2736, 2736, 2736, 2736, 2736, 2736, 9: 2736, 546: 2736}, - // 2060 - {712: 4915}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4916, 3051, 3052, 3050}, - {2737, 2737, 2737, 2737, 2737, 2737, 9: 2737, 546: 2737}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4932}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4927, 3051, 3052, 3050}, - // 2065 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4923, 3051, 3052, 3050}, - {2: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 10: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 58: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 531: 2695, 578: 2695}, - {2: 625, 625, 625, 625, 625, 625, 625, 10: 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 58: 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625}, - {2: 624, 624, 624, 624, 624, 624, 624, 10: 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 58: 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624}, - {106: 4926, 109: 4925, 959: 4924}, - // 2070 - {2722, 2722, 2722, 2722, 2722, 2722, 9: 2722, 546: 2722}, - {2092, 2092, 2092, 2092, 2092, 2092, 2092, 9: 2092, 19: 2092, 57: 2092, 102: 2092, 104: 2092, 2092, 2092, 2092, 109: 2092, 532: 2092, 542: 2092, 546: 2092, 562: 2092}, - {2091, 2091, 2091, 2091, 2091, 2091, 2091, 9: 2091, 19: 2091, 57: 2091, 102: 2091, 104: 2091, 2091, 2091, 2091, 109: 2091, 532: 2091, 542: 2091, 546: 2091, 562: 2091}, - {190: 4929, 534: 3812, 536: 3811, 915: 4930, 1044: 4928}, - {2724, 2724, 2724, 2724, 2724, 2724, 9: 2724, 546: 2724}, - // 2075 - {2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 57: 2588, 530: 2588, 534: 2588, 2588, 2588, 2588, 2588, 546: 2588, 548: 2588, 701: 2588, 2588, 704: 2588, 2588, 2588, 2588, 2588}, - {190: 4931}, - {2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 57: 2587, 530: 2587, 534: 2587, 2587, 2587, 2587, 2587, 546: 2587, 548: 2587, 701: 2587, 2587, 704: 2587, 2587, 2587, 2587, 2587}, - {557: 4933, 727: 4934}, - {535: 4936}, - // 2080 - {535: 4935}, - {2738, 2738, 2738, 2738, 2738, 2738, 9: 2738, 546: 2738}, - {531: 4938, 533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4939, 779: 3582, 1082: 4937}, - {2740, 2740, 2740, 2740, 2740, 2740, 9: 2740, 546: 2740}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4944}, - // 2085 - {2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 57: 2497, 530: 2497, 534: 2497, 2497, 2497, 2497, 2497, 546: 2497, 548: 2497, 701: 2497, 2497, 704: 2497, 2497, 2497, 2497, 2497}, - {560: 4087, 636: 4089, 4088, 916: 4943}, - {560: 4087, 636: 4089, 4088, 916: 4942}, - {2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 57: 2495, 530: 2495, 534: 2495, 2495, 2495, 2495, 2495, 546: 2495, 548: 2495, 701: 2495, 2495, 704: 2495, 2495, 2495, 2495, 2495}, - {2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 57: 2496, 530: 2496, 534: 2496, 2496, 2496, 2496, 2496, 546: 2496, 548: 2496, 701: 2496, 2496, 704: 2496, 2496, 2496, 2496, 2496}, - // 2090 - {57: 4945, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2739, 2739, 2739, 2739, 2739, 2739, 9: 2739, 546: 2739}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 4947}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4950}, - {645: 4949}, - // 2095 - {2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 58: 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 533: 2118, 535: 2118, 546: 2118, 631: 2118}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 4951}, - {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5231}, - {12: 4954, 118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 4953, 1098: 5019}, - {2548, 2548, 2548, 2548, 2548, 2548, 5096, 5102, 5090, 2548, 2548, 2548, 5094, 5103, 5101, 57: 2548, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2548, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5105, 1048: 5098, 1070: 5091, 1126: 5106, 5230}, - // 2100 - {2548, 2548, 2548, 2548, 2548, 2548, 5096, 5102, 5090, 2548, 2548, 2548, 5094, 5103, 5101, 57: 2548, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2548, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5105, 1048: 5098, 1070: 5091, 1126: 5106, 5086}, - {553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 57: 553, 530: 553, 534: 553, 553, 553, 553, 553, 546: 553, 548: 553, 701: 553, 553, 704: 553, 553, 553, 553, 553}, - {552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 57: 552, 530: 552, 534: 552, 552, 552, 552, 552, 546: 552, 548: 552, 701: 552, 552, 704: 552, 552, 552, 552, 552}, - {551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 57: 551, 530: 551, 534: 551, 551, 551, 551, 551, 546: 551, 548: 551, 701: 551, 551, 704: 551, 551, 551, 551, 551}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 59: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 814: 465, 819: 465, 841: 4409, 888: 5083}, - // 2105 - {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5082}, - {458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 57: 458, 59: 458, 530: 458, 4395, 534: 458, 458, 458, 458, 458, 546: 458, 548: 458, 701: 458, 458, 704: 458, 458, 458, 458, 458, 814: 458, 819: 458, 841: 4396, 1003: 5080, 1010: 4397}, - {458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 57: 458, 59: 458, 530: 458, 4395, 534: 458, 458, 458, 458, 458, 546: 458, 548: 458, 701: 458, 458, 704: 458, 458, 458, 458, 458, 814: 458, 819: 458, 841: 4396, 1003: 5078, 1010: 4397}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5077}, - {545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 57: 545, 59: 545, 530: 545, 545, 534: 545, 545, 545, 545, 545, 546: 545, 548: 545, 701: 545, 545, 704: 545, 545, 545, 545, 545, 814: 545, 819: 545}, - // 2110 - {544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 57: 544, 59: 544, 530: 544, 544, 534: 544, 544, 544, 544, 544, 546: 544, 548: 544, 701: 544, 544, 704: 544, 544, 544, 544, 544, 814: 544, 819: 544}, - {543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 57: 543, 59: 543, 530: 543, 543, 534: 543, 543, 543, 543, 543, 546: 543, 548: 543, 701: 543, 543, 704: 543, 543, 543, 543, 543, 814: 543, 819: 543}, - {542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 57: 542, 59: 542, 530: 542, 542, 534: 542, 542, 542, 542, 542, 546: 542, 548: 542, 701: 542, 542, 704: 542, 542, 542, 542, 542, 814: 542, 819: 542}, - {541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 57: 541, 59: 541, 530: 541, 541, 534: 541, 541, 541, 541, 541, 546: 541, 548: 541, 701: 541, 541, 704: 541, 541, 541, 541, 541, 814: 541, 819: 541}, - {540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 57: 540, 59: 540, 530: 540, 540, 534: 540, 540, 540, 540, 540, 546: 540, 548: 540, 701: 540, 540, 704: 540, 540, 540, 540, 540, 814: 540, 819: 540}, - // 2115 - {539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 57: 539, 59: 539, 530: 539, 539, 534: 539, 539, 539, 539, 539, 546: 539, 548: 539, 701: 539, 539, 704: 539, 539, 539, 539, 539, 814: 539, 819: 539}, - {538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 57: 538, 59: 538, 530: 538, 538, 534: 538, 538, 538, 538, 538, 546: 538, 548: 538, 701: 538, 538, 704: 538, 538, 538, 538, 538, 814: 538, 819: 538}, - {537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 57: 537, 59: 537, 530: 537, 537, 534: 537, 537, 537, 537, 537, 546: 537, 548: 537, 701: 537, 537, 704: 537, 537, 537, 537, 537, 814: 537, 819: 537}, - {536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 57: 536, 59: 536, 530: 536, 536, 534: 536, 536, 536, 536, 536, 546: 536, 548: 536, 701: 536, 536, 704: 536, 536, 536, 536, 536, 814: 536, 819: 536}, - {535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 57: 535, 59: 535, 530: 535, 535, 534: 535, 535, 535, 535, 535, 546: 535, 548: 535, 701: 535, 535, 704: 535, 535, 535, 535, 535, 814: 535, 819: 535}, - // 2120 - {534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 57: 534, 59: 534, 530: 534, 534, 534: 534, 534, 534, 534, 534, 546: 534, 548: 534, 701: 534, 534, 704: 534, 534, 534, 534, 534, 814: 534, 819: 534}, - {533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 57: 533, 59: 533, 530: 533, 534: 533, 533, 533, 533, 533, 546: 533, 548: 533, 701: 533, 533, 704: 533, 533, 533, 533, 533, 814: 533, 819: 533}, - {532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 57: 532, 59: 532, 530: 532, 534: 532, 532, 532, 532, 532, 546: 532, 548: 532, 701: 532, 532, 704: 532, 532, 532, 532, 532, 814: 532, 819: 532}, - {528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 57: 528, 59: 528, 530: 528, 528, 534: 528, 528, 528, 528, 528, 546: 528, 548: 528, 701: 528, 528, 704: 528, 528, 528, 528, 528, 814: 528, 819: 528}, - {527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 57: 527, 59: 527, 530: 527, 527, 534: 527, 527, 527, 527, 527, 546: 527, 548: 527, 701: 527, 527, 704: 527, 527, 527, 527, 527, 814: 527, 819: 527}, - // 2125 - {526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 57: 526, 59: 526, 530: 526, 526, 534: 526, 526, 526, 526, 526, 546: 526, 548: 526, 701: 526, 526, 704: 526, 526, 526, 526, 526, 814: 526, 819: 526}, - {525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 57: 525, 59: 525, 530: 525, 525, 534: 525, 525, 525, 525, 525, 546: 525, 548: 525, 701: 525, 525, 704: 525, 525, 525, 525, 525, 814: 525, 819: 525}, - {524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 57: 524, 59: 524, 530: 524, 524, 534: 524, 524, 524, 524, 524, 546: 524, 548: 524, 701: 524, 524, 704: 524, 524, 524, 524, 524, 814: 524, 819: 524}, - {523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 57: 523, 59: 523, 530: 523, 523, 534: 523, 523, 523, 523, 523, 546: 523, 548: 523, 701: 523, 523, 704: 523, 523, 523, 523, 523, 814: 523, 819: 523, 1421: 5076}, - {521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 57: 521, 59: 521, 530: 521, 521, 534: 521, 521, 521, 521, 521, 546: 521, 548: 521, 701: 521, 521, 704: 521, 521, 521, 521, 521, 814: 521, 819: 521}, - // 2130 - {520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 57: 520, 59: 520, 530: 520, 520, 534: 520, 520, 520, 520, 520, 546: 520, 548: 520, 701: 520, 520, 704: 520, 520, 520, 520, 520, 814: 520, 819: 520}, - {519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 57: 519, 530: 519, 519, 534: 519, 519, 519, 519, 519, 546: 519, 548: 519, 701: 519, 519, 704: 519, 519, 519, 519, 519}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 4408, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 841: 5073, 853: 4416, 896: 5074}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 4408, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 841: 5070, 853: 4416, 896: 5071}, - {531: 4408, 841: 5068}, - // 2135 - {531: 4408, 841: 5066}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5065}, - {531: 4408, 841: 5064}, - {510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 57: 510, 530: 510, 534: 510, 510, 510, 510, 510, 546: 510, 548: 510, 701: 510, 510, 704: 510, 510, 510, 510, 510}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5063}, - // 2140 - {531: 5059}, - {531: 5052}, - {506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 57: 506, 530: 506, 534: 506, 506, 506, 506, 506, 546: 506, 548: 506, 701: 506, 506, 704: 506, 506, 506, 506, 506}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 5045, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 5044, 732: 5003, 5002, 740: 5046, 853: 4416, 896: 5047, 987: 5043, 1025: 5042}, - {503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 16: 503, 57: 503, 530: 503, 503, 534: 503, 503, 503, 503, 503, 546: 503, 548: 503, 553: 503, 586: 503, 701: 503, 503, 704: 503, 503, 503, 503, 503, 503, 942: 5041}, - // 2145 - {502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 16: 502, 57: 502, 530: 502, 502, 534: 502, 502, 502, 502, 502, 546: 502, 548: 502, 553: 502, 586: 502, 701: 502, 502, 704: 502, 502, 502, 502, 502, 502, 942: 5040}, - {501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 16: 501, 57: 501, 530: 501, 501, 534: 501, 501, 501, 501, 501, 546: 501, 548: 501, 553: 501, 586: 501, 701: 501, 501, 704: 501, 501, 501, 501, 501, 501, 732: 5038, 5037, 942: 5039}, - {553: 5032, 709: 5031, 732: 5034, 5033}, - {496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 16: 496, 57: 496, 151: 496, 496, 496, 530: 496, 496, 534: 496, 496, 496, 496, 496, 546: 496, 548: 496, 553: 496, 586: 496, 701: 496, 496, 704: 496, 496, 496, 496, 496, 496}, - {495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 16: 495, 57: 495, 151: 495, 495, 495, 530: 495, 495, 534: 495, 495, 495, 495, 495, 546: 495, 548: 495, 553: 495, 586: 495, 701: 495, 495, 704: 495, 495, 495, 495, 495, 495}, - // 2150 - {531: 492}, - {486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 57: 486, 59: 486, 530: 486, 486, 534: 486, 486, 486, 486, 486, 546: 486, 548: 486, 701: 486, 486, 704: 486, 486, 486, 486, 486, 814: 486, 819: 486}, - {485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 57: 485, 59: 485, 530: 485, 485, 534: 485, 485, 485, 485, 485, 546: 485, 548: 485, 701: 485, 485, 704: 485, 485, 485, 485, 485, 814: 485, 819: 485}, - {484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 57: 484, 530: 484, 534: 484, 484, 484, 484, 484, 546: 484, 548: 484, 701: 484, 484, 704: 484, 484, 484, 484, 484}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5030}, - // 2155 - {482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 57: 482, 530: 482, 534: 482, 482, 482, 482, 482, 546: 482, 548: 482, 701: 482, 482, 704: 482, 482, 482, 482, 482}, - {481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 57: 481, 530: 481, 534: 481, 481, 481, 481, 481, 546: 481, 548: 481, 701: 481, 481, 704: 481, 481, 481, 481, 481}, - {479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 16: 479, 57: 479, 151: 479, 479, 479, 530: 479, 534: 479, 479, 479, 479, 479, 546: 479, 548: 479, 553: 479, 586: 479, 701: 479, 479, 704: 479, 479, 479, 479, 479, 479}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 16: 465, 57: 465, 151: 465, 465, 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 553: 465, 586: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 465, 841: 4409, 888: 5029}, - {477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 16: 477, 57: 477, 151: 477, 477, 477, 530: 477, 534: 477, 477, 477, 477, 477, 546: 477, 548: 477, 553: 477, 586: 477, 701: 477, 477, 704: 477, 477, 477, 477, 477, 477}, - // 2160 - {476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 16: 476, 57: 476, 151: 476, 476, 476, 530: 476, 534: 476, 476, 476, 476, 476, 546: 476, 548: 476, 553: 476, 586: 476, 701: 476, 476, 704: 476, 476, 476, 476, 476, 476}, - {471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 57: 471, 530: 471, 534: 471, 471, 471, 471, 471, 546: 471, 548: 471, 701: 471, 471, 704: 471, 471, 471, 471, 471}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5028}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5027}, - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5026}, - // 2165 - {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 59: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 814: 465, 819: 465, 841: 4409, 888: 5020}, - {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5021}, - {467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 57: 467, 59: 5023, 530: 467, 534: 467, 467, 467, 467, 467, 546: 467, 548: 467, 701: 467, 467, 704: 467, 467, 467, 467, 467, 814: 5022, 819: 5024, 978: 5025}, - {463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 57: 463, 59: 463, 530: 463, 534: 463, 463, 463, 463, 463, 546: 463, 548: 463, 701: 463, 463, 704: 463, 463, 463, 463, 463, 814: 463, 819: 463}, - {462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 57: 462, 59: 462, 530: 462, 534: 462, 462, 462, 462, 462, 546: 462, 548: 462, 701: 462, 462, 704: 462, 462, 462, 462, 462, 814: 462, 819: 462}, - // 2170 - {461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 57: 461, 59: 461, 530: 461, 534: 461, 461, 461, 461, 461, 546: 461, 548: 461, 701: 461, 461, 704: 461, 461, 461, 461, 461, 814: 461, 819: 461}, - {459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 57: 459, 59: 459, 530: 459, 534: 459, 459, 459, 459, 459, 546: 459, 548: 459, 701: 459, 459, 704: 459, 459, 459, 459, 459, 814: 459, 819: 459}, - {468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 57: 468, 530: 468, 534: 468, 468, 468, 468, 468, 546: 468, 548: 468, 701: 468, 468, 704: 468, 468, 468, 468, 468}, - {469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 57: 469, 530: 469, 534: 469, 469, 469, 469, 469, 546: 469, 548: 469, 701: 469, 469, 704: 469, 469, 469, 469, 469}, - {470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 57: 470, 530: 470, 534: 470, 470, 470, 470, 470, 546: 470, 548: 470, 701: 470, 470, 704: 470, 470, 470, 470, 470}, - // 2175 - {478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 16: 478, 57: 478, 151: 478, 478, 478, 530: 478, 534: 478, 478, 478, 478, 478, 546: 478, 548: 478, 553: 478, 586: 478, 701: 478, 478, 704: 478, 478, 478, 478, 478, 478}, - {483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 57: 483, 530: 483, 534: 483, 483, 483, 483, 483, 546: 483, 548: 483, 701: 483, 483, 704: 483, 483, 483, 483, 483}, - {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 16: 500, 57: 500, 530: 500, 500, 534: 500, 500, 500, 500, 500, 546: 500, 548: 500, 553: 500, 586: 500, 701: 500, 500, 704: 500, 500, 500, 500, 500, 500, 942: 5036}, - {499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 16: 499, 57: 499, 530: 499, 499, 534: 499, 499, 499, 499, 499, 546: 499, 548: 499, 553: 499, 586: 499, 701: 499, 499, 704: 499, 499, 499, 499, 499, 499, 942: 5035}, - {531: 494}, - // 2180 - {531: 493}, - {531: 488}, - {531: 489}, - {531: 491}, - {531: 490}, - // 2185 - {531: 487}, - {497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 16: 497, 57: 497, 151: 497, 497, 497, 530: 497, 497, 534: 497, 497, 497, 497, 497, 546: 497, 548: 497, 553: 497, 586: 497, 701: 497, 497, 704: 497, 497, 497, 497, 497, 497}, - {498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 16: 498, 57: 498, 151: 498, 498, 498, 530: 498, 498, 534: 498, 498, 498, 498, 498, 546: 498, 548: 498, 553: 498, 586: 498, 701: 498, 498, 704: 498, 498, 498, 498, 498, 498}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5051}, - {504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 57: 504, 530: 504, 534: 504, 504, 504, 504, 504, 546: 504, 548: 504, 701: 504, 504, 704: 504, 504, 504, 504, 504}, - // 2190 - {557: 4421, 942: 5041}, - {557: 4420, 942: 5040}, - {480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 57: 480, 530: 480, 534: 480, 480, 480, 480, 480, 546: 480, 548: 480, 701: 480, 480, 704: 480, 480, 480, 480, 480}, - {475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 57: 475, 530: 475, 534: 475, 475, 475, 475, 475, 546: 475, 548: 475, 701: 475, 475, 704: 475, 475, 475, 475, 475}, - {474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 57: 474, 530: 474, 534: 474, 474, 474, 474, 474, 546: 474, 548: 474, 701: 474, 474, 704: 474, 474, 474, 474, 474}, - // 2195 - {473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 57: 473, 530: 473, 534: 473, 473, 473, 473, 473, 546: 473, 548: 473, 701: 473, 473, 704: 473, 473, 473, 473, 473}, - {472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 57: 472, 530: 472, 534: 472, 472, 472, 472, 472, 546: 472, 548: 472, 701: 472, 472, 704: 472, 472, 472, 472, 472}, - {505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 57: 505, 530: 505, 534: 505, 505, 505, 505, 505, 546: 505, 548: 505, 701: 505, 505, 704: 505, 505, 505, 505, 505}, - {533: 3960, 638: 3961, 640: 3962, 1021: 5054, 1297: 5053}, - {9: 5056, 57: 5055}, - // 2200 - {9: 437, 57: 437}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5058}, - {533: 3960, 638: 3961, 640: 3962, 1021: 5057}, - {9: 436, 57: 436}, - {507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 57: 507, 530: 507, 534: 507, 507, 507, 507, 507, 546: 507, 548: 507, 701: 507, 507, 704: 507, 507, 507, 507, 507}, - // 2205 - {533: 3960, 638: 3961, 640: 3962, 1021: 5054, 1297: 5060}, - {9: 5056, 57: 5061}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5062}, - {508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 57: 508, 530: 508, 534: 508, 508, 508, 508, 508, 546: 508, 548: 508, 701: 508, 508, 704: 508, 508, 508, 508, 508}, - {509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 57: 509, 530: 509, 534: 509, 509, 509, 509, 509, 546: 509, 548: 509, 701: 509, 509, 704: 509, 509, 509, 509, 509}, - // 2210 - {511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 57: 511, 530: 511, 534: 511, 511, 511, 511, 511, 546: 511, 548: 511, 701: 511, 511, 704: 511, 511, 511, 511, 511}, - {512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 57: 512, 530: 512, 534: 512, 512, 512, 512, 512, 546: 512, 548: 512, 701: 512, 512, 704: 512, 512, 512, 512, 512}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5067}, - {513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 57: 513, 530: 513, 534: 513, 513, 513, 513, 513, 546: 513, 548: 513, 701: 513, 513, 704: 513, 513, 513, 513, 513}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5069}, - // 2215 - {514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 57: 514, 530: 514, 534: 514, 514, 514, 514, 514, 546: 514, 548: 514, 701: 514, 514, 704: 514, 514, 514, 514, 514}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5072}, - {515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 57: 515, 530: 515, 534: 515, 515, 515, 515, 515, 546: 515, 548: 515, 701: 515, 515, 704: 515, 515, 515, 515, 515}, - {516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 57: 516, 530: 516, 534: 516, 516, 516, 516, 516, 546: 516, 548: 516, 701: 516, 516, 704: 516, 516, 516, 516, 516}, - {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5075}, - // 2220 - {517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 57: 517, 530: 517, 534: 517, 517, 517, 517, 517, 546: 517, 548: 517, 701: 517, 517, 704: 517, 517, 517, 517, 517}, - {518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 57: 518, 530: 518, 534: 518, 518, 518, 518, 518, 546: 518, 548: 518, 701: 518, 518, 704: 518, 518, 518, 518, 518}, - {522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 57: 522, 59: 522, 530: 522, 522, 534: 522, 522, 522, 522, 522, 546: 522, 548: 522, 701: 522, 522, 704: 522, 522, 522, 522, 522, 814: 522, 819: 522}, - {546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 57: 546, 530: 546, 534: 546, 546, 546, 546, 546, 546: 546, 548: 546, 701: 546, 546, 704: 546, 546, 546, 546, 546}, - {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5079}, - // 2225 - {547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 57: 547, 59: 5023, 530: 547, 534: 547, 547, 547, 547, 547, 546: 547, 548: 547, 701: 547, 547, 704: 547, 547, 547, 547, 547, 814: 5022, 819: 5024, 978: 5025}, - {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5081}, - {548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 57: 548, 59: 5023, 530: 548, 534: 548, 548, 548, 548, 548, 546: 548, 548: 548, 701: 548, 548, 704: 548, 548, 548, 548, 548, 814: 5022, 819: 5024, 978: 5025}, - {549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 57: 549, 59: 5023, 530: 549, 534: 549, 549, 549, 549, 549, 546: 549, 548: 549, 701: 549, 549, 704: 549, 549, 549, 549, 549, 814: 5022, 819: 5024, 978: 5025}, - {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5084}, - // 2230 - {550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 57: 550, 59: 5023, 530: 550, 534: 550, 550, 550, 550, 550, 546: 550, 548: 550, 701: 550, 550, 704: 550, 550, 550, 550, 550, 814: 5022, 819: 5024, 978: 5025}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2684, 2684, 704: 2684, 2684, 710: 2684, 746: 2684, 2684, 770: 5229, 3051, 3052, 3050, 1290: 5228}, - {2614, 2614, 2614, 2614, 2614, 2614, 9: 2614, 2614, 2614, 57: 2614, 546: 2614}, - {701: 2591}, - {548: 5227}, - // 2235 - {2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 57: 2581, 530: 2581, 534: 2581, 2581, 2581, 2581, 2581, 546: 2581, 548: 2581, 701: 2581, 2581, 704: 2581, 2581, 2581, 2581, 2581}, - {2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 57: 2580, 530: 2580, 534: 2580, 2580, 2580, 2580, 2580, 546: 2580, 548: 2580, 701: 2580, 2580, 704: 2580, 2580, 2580, 2580, 2580}, - {701: 5223}, - {2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 57: 2577, 530: 2577, 534: 2577, 2577, 2577, 2577, 2577, 546: 2577, 548: 2577, 701: 5222, 2577, 704: 2577, 2577, 2577, 2577, 2577}, - {56: 5210, 265: 5212, 408: 5213, 531: 5209, 533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 5195, 5194, 5190, 5191, 653: 5192, 5193, 777: 4939, 779: 3582, 5207, 999: 5208, 1036: 5189, 1059: 5187, 5188, 5211, 1082: 5206, 1214: 5205, 1349: 5204}, - // 2240 - {535: 5202}, - {714: 5185}, - {533: 5184}, - {702: 5175}, - {537: 5168}, - // 2245 - {2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 57: 2569, 530: 2569, 534: 2569, 2569, 2569, 2569, 2569, 546: 2569, 548: 2569, 701: 2569, 2569, 704: 2569, 2569, 2569, 2569, 2569}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 5167}, - {177: 5165, 255: 5166, 535: 5164, 1333: 5163}, - {236: 5162, 300: 5161, 535: 5160, 1470: 5159}, - {2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 57: 2564, 530: 2564, 5153, 534: 2564, 2564, 2564, 2564, 2564, 546: 2564, 548: 2564, 701: 2564, 2564, 704: 2564, 2564, 2564, 2564, 2564, 1325: 5152}, - // 2250 - {366: 5151}, - {2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 57: 2550, 530: 2550, 534: 2550, 2550, 2550, 2550, 2550, 546: 2550, 548: 2550, 701: 2550, 2550, 704: 2550, 2550, 2550, 2550, 2550}, - {2547, 2547, 2547, 2547, 2547, 2547, 5096, 5102, 5090, 2547, 2547, 2547, 5094, 5103, 5101, 57: 2547, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2547, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5150, 1048: 5098, 1070: 5091}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5108}, - {2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 57: 2483, 530: 2483, 5110, 534: 2483, 2483, 2483, 2483, 2483, 546: 2483, 548: 2483, 701: 2483, 2483, 704: 2483, 2483, 2483, 2483, 2483, 711: 2483, 1375: 5109}, - // 2255 - {2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 57: 2537, 530: 2537, 534: 2537, 2537, 2537, 2537, 2537, 546: 2537, 548: 2537, 701: 2537, 2537, 704: 2537, 2537, 2537, 2537, 2537, 711: 5125, 1390: 5126, 5127}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5111}, - {9: 5123, 57: 5122}, - {9: 2481, 57: 2481}, - {9: 465, 57: 465, 531: 4408, 577: 465, 605: 465, 841: 4409, 888: 5120}, - // 2260 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5115}, - {57: 5116, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {9: 1502, 57: 1502, 577: 5119, 605: 5118, 1064: 5117}, - {9: 2478, 57: 2478}, - {1501, 1501, 1501, 1501, 1501, 1501, 9: 1501, 57: 1501, 546: 1501}, - // 2265 - {1500, 1500, 1500, 1500, 1500, 1500, 9: 1500, 57: 1500, 546: 1500}, - {9: 1502, 57: 1502, 577: 5119, 605: 5118, 1064: 5121}, - {9: 2479, 57: 2479}, - {2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 57: 2482, 530: 2482, 534: 2482, 2482, 2482, 2482, 2482, 546: 2482, 548: 2482, 701: 2482, 2482, 704: 2482, 2482, 2482, 2482, 2482, 711: 2482}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5124}, - // 2270 - {9: 2480, 57: 2480}, - {260: 5147, 416: 5148, 437: 5149}, - {2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 57: 2536, 530: 2536, 534: 2536, 2536, 2536, 2536, 2536, 546: 2536, 548: 2536, 701: 2536, 2536, 704: 2536, 2536, 2536, 2536, 2536}, - {2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 57: 2532, 530: 5129, 534: 2532, 2532, 2532, 2532, 2532, 546: 2532, 548: 2532, 701: 2532, 2532, 704: 2532, 2532, 2532, 2532, 2532, 1219: 5130, 5131, 1397: 5128}, - {2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 57: 2535, 530: 2535, 534: 2535, 2535, 2535, 2535, 2535, 546: 2535, 548: 2535, 701: 2535, 2535, 704: 2535, 2535, 2535, 2535, 2535}, - // 2275 - {714: 5145, 803: 5134}, - {2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 57: 2531, 530: 5143, 534: 2531, 2531, 2531, 2531, 2531, 546: 2531, 548: 2531, 701: 2531, 2531, 704: 2531, 2531, 2531, 2531, 2531, 1220: 5144}, - {2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 57: 2530, 530: 5132, 534: 2530, 2530, 2530, 2530, 2530, 546: 2530, 548: 2530, 701: 2530, 2530, 704: 2530, 2530, 2530, 2530, 2530, 1219: 5133}, - {803: 5134}, - {2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 57: 2528, 530: 2528, 534: 2528, 2528, 2528, 2528, 2528, 546: 2528, 548: 2528, 701: 2528, 2528, 704: 2528, 2528, 2528, 2528, 2528}, - // 2280 - {86: 5139, 557: 5138, 728: 5137, 730: 5136, 1249: 5135}, - {2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 57: 2534, 530: 2534, 534: 2534, 2534, 2534, 2534, 2534, 546: 2534, 548: 2534, 701: 2534, 2534, 704: 2534, 2534, 2534, 2534, 2534}, - {2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 57: 2527, 530: 2527, 534: 2527, 2527, 2527, 2527, 2527, 546: 2527, 548: 2527, 701: 2527, 2527, 704: 2527, 2527, 2527, 2527, 2527}, - {2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 57: 2526, 530: 2526, 534: 2526, 2526, 2526, 2526, 2526, 546: 2526, 548: 2526, 701: 2526, 2526, 704: 2526, 2526, 2526, 2526, 2526}, - {535: 5142, 548: 5141}, - // 2285 - {93: 5140}, - {2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 57: 2524, 530: 2524, 534: 2524, 2524, 2524, 2524, 2524, 546: 2524, 548: 2524, 701: 2524, 2524, 704: 2524, 2524, 2524, 2524, 2524}, - {2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 57: 2525, 530: 2525, 534: 2525, 2525, 2525, 2525, 2525, 546: 2525, 548: 2525, 701: 2525, 2525, 704: 2525, 2525, 2525, 2525, 2525}, - {2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 57: 2523, 530: 2523, 534: 2523, 2523, 2523, 2523, 2523, 546: 2523, 548: 2523, 701: 2523, 2523, 704: 2523, 2523, 2523, 2523, 2523}, - {714: 5145}, - // 2290 - {2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 57: 2529, 530: 2529, 534: 2529, 2529, 2529, 2529, 2529, 546: 2529, 548: 2529, 701: 2529, 2529, 704: 2529, 2529, 2529, 2529, 2529}, - {86: 5139, 557: 5138, 728: 5137, 730: 5136, 1249: 5146}, - {2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 57: 2533, 530: 2533, 534: 2533, 2533, 2533, 2533, 2533, 546: 2533, 548: 2533, 701: 2533, 2533, 704: 2533, 2533, 2533, 2533, 2533}, - {2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 57: 2540, 530: 2540, 534: 2540, 2540, 2540, 2540, 2540, 546: 2540, 548: 2540, 701: 2540, 2540, 704: 2540, 2540, 2540, 2540, 2540}, - {2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 57: 2539, 530: 2539, 534: 2539, 2539, 2539, 2539, 2539, 546: 2539, 548: 2539, 701: 2539, 2539, 704: 2539, 2539, 2539, 2539, 2539}, - // 2295 - {2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 57: 2538, 530: 2538, 534: 2538, 2538, 2538, 2538, 2538, 546: 2538, 548: 2538, 701: 2538, 2538, 704: 2538, 2538, 2538, 2538, 2538}, - {2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 57: 2549, 530: 2549, 534: 2549, 2549, 2549, 2549, 2549, 546: 2549, 548: 2549, 701: 2549, 2549, 704: 2549, 2549, 2549, 2549, 2549}, - {537: 2554}, - {2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 57: 2565, 530: 2565, 534: 2565, 2565, 2565, 2565, 2565, 546: 2565, 548: 2565, 701: 2565, 2565, 704: 2565, 2565, 2565, 2565, 2565}, - {560: 3037, 799: 3866, 816: 5154}, - // 2300 - {9: 5156, 57: 5155}, - {2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 57: 2563, 530: 2563, 534: 2563, 2563, 2563, 2563, 2563, 546: 2563, 548: 2563, 701: 2563, 2563, 704: 2563, 2563, 2563, 2563, 2563}, - {560: 3037, 799: 3866, 816: 5157}, - {57: 5158}, - {2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 57: 2562, 530: 2562, 534: 2562, 2562, 2562, 2562, 2562, 546: 2562, 548: 2562, 701: 2562, 2562, 704: 2562, 2562, 2562, 2562, 2562}, - // 2305 - {2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 57: 2566, 530: 2566, 534: 2566, 2566, 2566, 2566, 2566, 546: 2566, 548: 2566, 701: 2566, 2566, 704: 2566, 2566, 2566, 2566, 2566}, - {2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 57: 2561, 530: 2561, 534: 2561, 2561, 2561, 2561, 2561, 546: 2561, 548: 2561, 701: 2561, 2561, 704: 2561, 2561, 2561, 2561, 2561}, - {2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 57: 2560, 530: 2560, 534: 2560, 2560, 2560, 2560, 2560, 546: 2560, 548: 2560, 701: 2560, 2560, 704: 2560, 2560, 2560, 2560, 2560}, - {2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 57: 2559, 530: 2559, 534: 2559, 2559, 2559, 2559, 2559, 546: 2559, 548: 2559, 701: 2559, 2559, 704: 2559, 2559, 2559, 2559, 2559}, - {2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 57: 2567, 530: 2567, 534: 2567, 2567, 2567, 2567, 2567, 546: 2567, 548: 2567, 701: 2567, 2567, 704: 2567, 2567, 2567, 2567, 2567}, - // 2310 - {2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 57: 2558, 530: 2558, 534: 2558, 2558, 2558, 2558, 2558, 546: 2558, 548: 2558, 701: 2558, 2558, 704: 2558, 2558, 2558, 2558, 2558}, - {2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 57: 2557, 530: 2557, 534: 2557, 2557, 2557, 2557, 2557, 546: 2557, 548: 2557, 701: 2557, 2557, 704: 2557, 2557, 2557, 2557, 2557}, - {2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 57: 2556, 530: 2556, 534: 2556, 2556, 2556, 2556, 2556, 546: 2556, 548: 2556, 701: 2556, 2556, 704: 2556, 2556, 2556, 2556, 2556}, - {2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 57: 2568, 530: 2568, 534: 2568, 2568, 2568, 2568, 2568, 546: 2568, 548: 2568, 701: 2568, 2568, 704: 2568, 2568, 2568, 2568, 2568}, - {531: 5169}, - // 2315 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5170}, - {57: 5171, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 57: 2553, 530: 2553, 534: 2553, 2553, 2553, 2553, 2553, 546: 2553, 548: 2553, 701: 2553, 2553, 704: 2553, 2553, 2553, 2553, 2553, 1471: 5174, 1498: 5173, 5172}, - {2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 57: 2570, 530: 2570, 534: 2570, 2570, 2570, 2570, 2570, 546: 2570, 548: 2570, 701: 2570, 2570, 704: 2570, 2570, 2570, 2570, 2570}, - {2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 57: 2552, 530: 2552, 534: 2552, 2552, 2552, 2552, 2552, 546: 2552, 548: 2552, 701: 2552, 2552, 704: 2552, 2552, 2552, 2552, 2552}, - // 2320 - {2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 57: 2551, 530: 2551, 534: 2551, 2551, 2551, 2551, 2551, 546: 2551, 548: 2551, 701: 2551, 2551, 704: 2551, 2551, 2551, 2551, 2551}, - {531: 5176}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5177}, - {57: 5178, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 57: 2586, 190: 4929, 530: 2586, 534: 3812, 2586, 3811, 2586, 2586, 546: 2586, 548: 2586, 701: 2586, 2586, 704: 2586, 2586, 2586, 2586, 2586, 915: 5179, 1044: 5180, 1168: 5181, 1354: 5182}, - // 2325 - {190: 4931, 548: 5183}, - {2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 57: 2585, 530: 2585, 534: 2585, 2585, 2585, 2585, 2585, 546: 2585, 548: 2585, 701: 2585, 2585, 704: 2585, 2585, 2585, 2585, 2585}, - {2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 57: 2583, 530: 2583, 534: 2583, 2583, 2583, 2583, 2583, 546: 2583, 548: 2583, 701: 2583, 2583, 704: 2583, 2583, 2583, 2583, 2583}, - {2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 57: 2571, 530: 2571, 534: 2571, 2571, 2571, 2571, 2571, 546: 2571, 548: 2571, 701: 2571, 2571, 704: 2571, 2571, 2571, 2571, 2571}, - {2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 57: 2584, 530: 2584, 534: 2584, 2584, 2584, 2584, 2584, 546: 2584, 548: 2584, 701: 2584, 2584, 704: 2584, 2584, 2584, 2584, 2584}, - // 2330 - {2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 57: 2572, 530: 2572, 534: 2572, 2572, 2572, 2572, 2572, 546: 2572, 548: 2572, 701: 2572, 2572, 704: 2572, 2572, 2572, 2572, 2572}, - {648: 5195, 5194, 5190, 5191, 653: 5192, 5193, 1036: 5189, 1059: 5187, 5188, 5186}, - {2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 57: 2573, 530: 2573, 534: 2573, 2573, 2573, 2573, 2573, 546: 2573, 548: 2573, 701: 2573, 2573, 704: 2573, 2573, 2573, 2573, 2573}, - {2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 57: 2513, 530: 2513, 534: 2513, 2513, 2513, 2513, 2513, 546: 2513, 548: 2513, 701: 2513, 2513, 704: 2513, 2513, 2513, 2513, 2513}, - {531: 5198}, - // 2335 - {531: 5196}, - {2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 57: 2509, 530: 2509, 2498, 534: 2509, 2509, 2509, 2509, 2509, 546: 2509, 548: 2509, 701: 2509, 2509, 704: 2509, 2509, 2509, 2509, 2509}, - {2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 57: 2502, 530: 2502, 2506, 534: 2502, 2502, 2502, 2502, 2502, 546: 2502, 548: 2502, 701: 2502, 2502, 704: 2502, 2502, 2502, 2502, 2502}, - {2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 57: 2501, 530: 2501, 2505, 534: 2501, 2501, 2501, 2501, 2501, 546: 2501, 548: 2501, 701: 2501, 2501, 704: 2501, 2501, 2501, 2501, 2501}, - {2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 57: 2500, 530: 2500, 2504, 534: 2500, 2500, 2500, 2500, 2500, 546: 2500, 548: 2500, 701: 2500, 2500, 704: 2500, 2500, 2500, 2500, 2500}, - // 2340 - {531: 2503}, - {531: 2499}, - {57: 5197}, - {2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 57: 2510, 530: 2510, 534: 2510, 2510, 2510, 2510, 2510, 546: 2510, 548: 2510, 701: 2510, 2510, 704: 2510, 2510, 2510, 2510, 2510}, - {57: 5199, 560: 3037, 799: 5200}, - // 2345 - {2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 57: 2512, 530: 2512, 534: 2512, 2512, 2512, 2512, 2512, 546: 2512, 548: 2512, 701: 2512, 2512, 704: 2512, 2512, 2512, 2512, 2512}, - {57: 5201}, - {2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 57: 2511, 530: 2511, 534: 2511, 2511, 2511, 2511, 2511, 546: 2511, 548: 2511, 701: 2511, 2511, 704: 2511, 2511, 2511, 2511, 2511}, - {186: 5203}, - {2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 57: 2574, 530: 2574, 534: 2574, 2574, 2574, 2574, 2574, 546: 2574, 548: 2574, 701: 2574, 2574, 704: 2574, 2574, 2574, 2574, 2574}, - // 2350 - {2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 57: 2575, 530: 2575, 534: 2575, 2575, 2575, 2575, 2575, 546: 2575, 548: 2575, 701: 2575, 2575, 704: 2575, 2575, 2575, 2575, 2575}, - {2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 57: 2522, 530: 2522, 534: 2522, 2522, 2522, 2522, 2522, 546: 2522, 548: 2522, 701: 2522, 2522, 704: 2522, 2522, 2522, 2522, 2522}, - {2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 57: 2521, 530: 2521, 534: 2521, 2521, 2521, 2521, 2521, 546: 2521, 548: 2521, 701: 2521, 2521, 704: 2521, 2521, 2521, 2521, 2521}, - {2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 57: 2520, 530: 2520, 534: 2520, 2520, 2520, 2520, 2520, 546: 2520, 548: 2520, 701: 2520, 2520, 704: 2520, 2520, 2520, 2520, 2520}, - {2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 57: 2519, 530: 2519, 534: 2519, 2519, 2519, 2519, 2519, 546: 2519, 548: 2519, 701: 2519, 2519, 704: 2519, 2519, 2519, 2519, 2519}, - // 2355 - {56: 5210, 531: 5209, 648: 5195, 5194, 5190, 5191, 653: 5192, 5193, 999: 5218, 1036: 5189, 1059: 5187, 5188, 5211, 1214: 5219}, - {531: 5214}, - {2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 57: 2514, 530: 2514, 534: 2514, 2514, 2514, 2514, 2514, 546: 2514, 548: 2514, 701: 2514, 2514, 704: 2514, 2514, 2514, 2514, 2514}, - {186: 4576}, - {531: 4573}, - // 2360 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 5215, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 5216}, - {2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 57: 2517, 530: 2517, 534: 2517, 2517, 2517, 2517, 2517, 546: 2517, 548: 2517, 701: 2517, 2517, 704: 2517, 2517, 2517, 2517, 2517}, - {9: 3990, 57: 5217}, - {2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 57: 2516, 530: 2516, 534: 2516, 2516, 2516, 2516, 2516, 546: 2516, 548: 2516, 701: 2516, 2516, 704: 2516, 2516, 2516, 2516, 2516}, - {57: 5221}, - // 2365 - {57: 5220}, - {2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 57: 2515, 530: 2515, 534: 2515, 2515, 2515, 2515, 2515, 546: 2515, 548: 2515, 701: 2515, 2515, 704: 2515, 2515, 2515, 2515, 2515}, - {2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 57: 2518, 530: 2518, 534: 2518, 2518, 2518, 2518, 2518, 546: 2518, 548: 2518, 701: 2518, 2518, 704: 2518, 2518, 2518, 2518, 2518}, - {2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 57: 2576, 530: 2576, 534: 2576, 2576, 2576, 2576, 2576, 546: 2576, 548: 2576, 701: 2576, 2576, 704: 2576, 2576, 2576, 2576, 2576}, - {2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 57: 2579, 105: 5224, 107: 5225, 530: 2579, 534: 2579, 2579, 2579, 2579, 2579, 546: 2579, 548: 2579, 701: 2579, 2579, 704: 2579, 2579, 2579, 2579, 2579, 971: 5226}, - // 2370 - {2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 19: 2711, 57: 2711, 102: 2711, 104: 2711, 2711, 2711, 2711, 109: 2711, 530: 2711, 532: 2711, 534: 2711, 2711, 2711, 2711, 2711, 542: 2711, 546: 2711, 548: 2711, 562: 2711, 701: 2711, 2711, 704: 2711, 2711, 2711, 2711, 2711}, - {2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 19: 2710, 57: 2710, 102: 2710, 104: 2710, 2710, 2710, 2710, 109: 2710, 530: 2710, 532: 2710, 534: 2710, 2710, 2710, 2710, 2710, 542: 2710, 546: 2710, 548: 2710, 562: 2710, 701: 2710, 2710, 704: 2710, 2710, 2710, 2710, 2710}, - {2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 57: 2578, 530: 2578, 534: 2578, 2578, 2578, 2578, 2578, 546: 2578, 548: 2578, 701: 2578, 2578, 704: 2578, 2578, 2578, 2578, 2578}, - {2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 57: 2582, 530: 2582, 534: 2582, 2582, 2582, 2582, 2582, 546: 2582, 548: 2582, 701: 2582, 2582, 704: 2582, 2582, 2582, 2582, 2582}, - {701: 2683, 2683, 704: 2683, 2683, 710: 2683, 746: 2683, 2683}, - // 2375 - {2682, 2682, 2682, 2682, 2682, 2682, 9: 2682, 546: 2682, 701: 2682, 2682, 704: 2682, 2682, 710: 2682, 746: 2682, 2682}, - {2615, 2615, 2615, 2615, 2615, 2615, 9: 2615, 2615, 2615, 57: 2615, 546: 2615}, - {2741, 2741, 2741, 2741, 2741, 2741, 9: 2741, 546: 2741}, - {2693, 2693, 2693, 2693, 2693, 2693, 9: 2693, 546: 2693}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5234}, - // 2380 - {2692, 2692, 2692, 2692, 2692, 2692, 9: 2692, 546: 2692}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5236}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 5237}, - {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5238}, - {2742, 2742, 2742, 2742, 2742, 2742, 9: 2742, 546: 2742}, - // 2385 - {2743, 2743, 2743, 2743, 2743, 2743, 9: 2743, 546: 2743}, - {2744, 2744, 2744, 2744, 2744, 2744, 9: 2744, 546: 2744}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5244, 1104: 5243, 1313: 5242}, - {2745, 2745, 2745, 2745, 2745, 2745, 9: 5246, 546: 2745}, - {1512, 1512, 1512, 1512, 1512, 1512, 9: 1512, 546: 1512}, - // 2390 - {1502, 1502, 1502, 1502, 1502, 1502, 9: 1502, 546: 1502, 577: 5119, 605: 5118, 1064: 5245}, - {1510, 1510, 1510, 1510, 1510, 1510, 9: 1510, 546: 1510}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5244, 1104: 5247}, - {1511, 1511, 1511, 1511, 1511, 1511, 9: 1511, 546: 1511}, - {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5249}, - // 2395 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5252}, - {761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 58: 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 531: 761, 546: 761, 560: 761, 586: 761, 608: 761, 715: 761}, - {760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 58: 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 531: 760, 546: 760, 560: 760, 586: 760, 608: 760, 715: 760}, - {2748, 2748, 2748, 2748, 2748, 2748, 9: 2748, 546: 2748}, - {2717, 2717, 2717, 2717, 2717, 2717, 9: 2717, 20: 2717, 546: 2717}, - // 2400 - {2716, 2716, 2716, 2716, 2716, 2716, 9: 5256, 20: 2716, 546: 2716}, - {2687, 2687, 2687, 2687, 2687, 2687, 9: 2687, 20: 2687, 57: 2687, 131: 2687, 200: 2687, 215: 2687, 532: 2687, 546: 2687, 559: 2687, 710: 2687, 715: 2687}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5257, 3051, 3052, 3050}, - {2686, 2686, 2686, 2686, 2686, 2686, 9: 2686, 20: 2686, 57: 2686, 131: 2686, 200: 2686, 215: 2686, 532: 2686, 546: 2686, 559: 2686, 710: 2686, 715: 2686}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5260}, - // 2405 - {2749, 2749, 2749, 2749, 2749, 2749, 9: 2749, 546: 2749}, - {20: 5261}, - {2751, 2751, 2751, 2751, 2751, 2751, 9: 2751, 546: 2751}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5264}, - {2750, 2750, 2750, 2750, 2750, 2750, 9: 2750, 546: 2750}, - // 2410 - {20: 5265}, - {2752, 2752, 2752, 2752, 2752, 2752, 9: 2752, 546: 2752}, - {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5267}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5268}, - {2753, 2753, 2753, 2753, 2753, 2753, 9: 2753, 546: 2753}, - // 2415 - {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5270}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5271}, - {2754, 2754, 2754, 2754, 2754, 2754, 9: 2754, 546: 2754}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5273}, - {2755, 2755, 2755, 2755, 2755, 2755, 9: 2755, 546: 2755}, - // 2420 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5275, 3051, 3052, 3050}, - {532: 5276}, - {608: 5277}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5278}, - {2715, 2715, 2715, 2715, 2715, 2715, 9: 2715, 276: 5282, 532: 5281, 546: 2715, 1511: 5280, 5279}, - // 2425 - {2756, 2756, 2756, 2756, 2756, 2756, 9: 2756, 546: 2756}, - {2714, 2714, 2714, 2714, 2714, 2714, 9: 2714, 546: 2714}, - {247: 5284}, - {247: 5283}, - {2712, 2712, 2712, 2712, 2712, 2712, 9: 2712, 546: 2712}, - // 2430 - {2713, 2713, 2713, 2713, 2713, 2713, 9: 2713, 546: 2713}, - {192: 5286}, - {199: 5287}, - {531: 5288}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5289}, - // 2435 - {57: 5290, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2119, 2119, 2119, 2119, 2119, 2119, 9: 2119, 546: 2119, 578: 4948, 854: 5291}, - {2758, 2758, 2758, 2758, 2758, 2758, 9: 2758, 546: 2758}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5310}, - {701: 5309}, - // 2440 - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5307}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5305}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5303}, - {701: 5300}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5299, 3051, 3052, 3050}, - // 2445 - {2723, 2723, 2723, 2723, 2723, 2723, 9: 2723, 546: 2723}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5301}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5229, 3051, 3052, 3050, 1290: 5302}, - {2746, 2746, 2746, 2746, 2746, 2746, 9: 2746, 546: 2746}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5304, 3051, 3052, 3050}, - // 2450 - {2747, 2747, 2747, 2747, 2747, 2747, 9: 2747, 546: 2747}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5306, 3051, 3052, 3050}, - {2757, 2757, 2757, 2757, 2757, 2757, 9: 2757, 546: 2757}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5308}, - {2759, 2759, 2759, 2759, 2759, 2759, 9: 5256, 546: 2759}, - // 2455 - {2760, 2760, 2760, 2760, 2760, 2760, 9: 2760, 546: 2760}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5311}, - {2321, 2321, 2321, 2321, 2321, 2321, 9: 2321, 546: 2321, 728: 5314, 730: 5313, 1012: 5312}, - {2761, 2761, 2761, 2761, 2761, 2761, 9: 2761, 546: 2761}, - {2320, 2320, 2320, 2320, 2320, 2320, 9: 2320, 546: 2320}, - // 2460 - {2319, 2319, 2319, 2319, 2319, 2319, 9: 2319, 546: 2319}, - {173: 5251, 560: 762, 908: 5250, 924: 5316}, - {560: 3037, 799: 5317}, - {2762, 2762, 2762, 2762, 2762, 2762, 9: 2762, 546: 2762}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5319}, - // 2465 - {2763, 2763, 2763, 2763, 2763, 2763, 9: 2763, 546: 2763}, - {192: 5321}, - {199: 5322}, - {531: 5323}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5324}, - // 2470 - {57: 5325, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {762, 762, 762, 762, 762, 762, 9: 762, 173: 5251, 546: 762, 908: 5250, 924: 5326}, - {2767, 2767, 2767, 2767, 2767, 2767, 9: 2767, 546: 2767}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 578: 5345, 883: 5465}, - {2770, 2770, 2770, 2770, 2770, 2770, 9: 2770, 546: 2770}, - // 2475 - {2117, 2117, 2117, 2117, 2117, 2117, 9: 2117, 116: 2117, 173: 2117, 531: 2117, 546: 2117, 578: 5345, 883: 5414, 908: 2117}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 5405}, - {701: 4906, 5337, 704: 5332, 5335, 710: 4907, 746: 5336, 5333, 923: 5334, 1340: 5338}, - {701: 5399}, - {2: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 10: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 58: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 531: 2698, 701: 4906, 710: 4907, 923: 5354, 1197: 5393}, - // 2480 - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 542: 2117, 578: 5345, 883: 5387}, - {2: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 10: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 58: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 531: 2698, 542: 2698, 701: 4906, 710: 4907, 923: 5354, 1197: 5355}, - {701: 5343}, - {531: 5339}, - {626, 626, 626, 626, 626, 626, 9: 626, 57: 626, 546: 626}, - // 2485 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5340}, - {57: 5341, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2586, 2586, 2586, 2586, 2586, 2586, 9: 2586, 57: 2586, 190: 4929, 534: 3812, 536: 3811, 546: 2586, 915: 4930, 1044: 5180, 1168: 5342}, - {2541, 2541, 2541, 2541, 2541, 2541, 9: 2541, 57: 2541, 546: 2541}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 578: 5345, 883: 5344}, - // 2490 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 770: 5349, 3051, 3052, 3050, 981: 5348}, - {534: 3812, 536: 3811, 915: 5346}, - {645: 5347}, - {2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 58: 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 531: 2116, 533: 2116, 535: 2116, 542: 2116, 546: 2116, 631: 2116, 908: 2116}, - {531: 5350}, - // 2495 - {531: 2112}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5351}, - {9: 5123, 57: 5352}, - {707: 5107, 1011: 5353}, - {2542, 2542, 2542, 2542, 2542, 2542, 9: 2542, 57: 2542, 546: 2542}, - // 2500 - {2: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 10: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 58: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 531: 2697, 542: 2697}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5356}, - {531: 5367}, - {104: 5365, 531: 2112, 542: 2112}, - {531: 2103, 542: 5359}, - // 2505 - {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5360}, - {531: 2102}, - {2096, 2096, 2096, 2096, 2096, 2096, 2096, 9: 2096, 19: 2096, 57: 2096, 102: 2096, 104: 2096, 2096, 2096, 2096, 109: 2096, 530: 2096, 2096, 2096, 542: 2096, 546: 2096, 562: 2096}, - {2095, 2095, 2095, 2095, 2095, 2095, 2095, 9: 2095, 19: 2095, 57: 2095, 102: 2095, 104: 2095, 2095, 2095, 2095, 109: 2095, 530: 2095, 2095, 2095, 542: 2095, 546: 2095, 562: 2095}, - {2094, 2094, 2094, 2094, 2094, 2094, 2094, 9: 2094, 19: 2094, 57: 2094, 102: 2094, 104: 2094, 2094, 2094, 2094, 109: 2094, 530: 2094, 2094, 2094, 542: 2094, 546: 2094, 562: 2094}, - // 2510 - {2093, 2093, 2093, 2093, 2093, 2093, 2093, 9: 2093, 19: 2093, 57: 2093, 102: 2093, 104: 2093, 2093, 2093, 2093, 109: 2093, 530: 2093, 2093, 2093, 542: 2093, 546: 2093, 562: 2093}, - {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5366}, - {531: 2101}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5368}, - {9: 5123, 57: 5369}, - // 2515 - {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5370}, - {2543, 2543, 2543, 2543, 2543, 2543, 5375, 9: 2543, 19: 5372, 57: 2543, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2543, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, - {2110, 2110, 2110, 2110, 2110, 2110, 2110, 9: 2110, 19: 2110, 57: 2110, 102: 2110, 104: 2110, 2110, 2110, 2110, 109: 2110, 532: 2110, 542: 2110, 546: 2110, 562: 2110}, - {555: 4588, 560: 2316, 802: 5385}, - {2108, 2108, 2108, 2108, 2108, 2108, 2108, 9: 2108, 19: 2108, 57: 2108, 102: 2108, 104: 2108, 2108, 2108, 2108, 109: 2108, 532: 2108, 542: 2108, 546: 2108, 562: 2108}, - // 2520 - {415: 5383}, - {533: 5382}, - {2105, 2105, 2105, 2105, 2105, 2105, 2105, 9: 2105, 19: 2105, 57: 2105, 102: 2105, 104: 2105, 2105, 2105, 2105, 109: 2105, 532: 2105, 542: 2105, 546: 2105, 562: 2105}, - {2104, 2104, 2104, 2104, 2104, 2104, 2104, 9: 2104, 19: 2104, 57: 2104, 102: 2104, 104: 2104, 2104, 2104, 2104, 109: 2104, 532: 2104, 542: 2104, 546: 2104, 562: 2104}, - {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5381}, - // 2525 - {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5380}, - {2097, 2097, 2097, 2097, 2097, 2097, 2097, 9: 2097, 19: 2097, 57: 2097, 102: 2097, 104: 2097, 2097, 2097, 2097, 109: 2097, 530: 2097, 532: 2097, 542: 2097, 546: 2097, 562: 2097}, - {2098, 2098, 2098, 2098, 2098, 2098, 2098, 9: 2098, 19: 2098, 57: 2098, 102: 2098, 104: 2098, 2098, 2098, 2098, 109: 2098, 530: 2098, 532: 2098, 542: 2098, 546: 2098, 562: 2098}, - {2106, 2106, 2106, 2106, 2106, 2106, 2106, 9: 2106, 19: 2106, 57: 2106, 102: 2106, 104: 2106, 2106, 2106, 2106, 109: 2106, 532: 2106, 542: 2106, 546: 2106, 562: 2106}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5384, 3051, 3052, 3050}, - // 2530 - {2107, 2107, 2107, 2107, 2107, 2107, 2107, 9: 2107, 19: 2107, 57: 2107, 102: 2107, 104: 2107, 2107, 2107, 2107, 109: 2107, 532: 2107, 542: 2107, 546: 2107, 562: 2107}, - {560: 3037, 799: 3866, 816: 5386}, - {2109, 2109, 2109, 2109, 2109, 2109, 2109, 9: 2109, 19: 2109, 57: 2109, 102: 2109, 104: 2109, 2109, 2109, 2109, 109: 2109, 532: 2109, 542: 2109, 546: 2109, 562: 2109}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5388}, - {531: 5389}, - // 2535 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5390}, - {9: 5123, 57: 5391}, - {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5392}, - {2544, 2544, 2544, 2544, 2544, 2544, 5375, 9: 2544, 19: 5372, 57: 2544, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2544, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 770: 5349, 3051, 3052, 3050, 981: 5394}, - // 2540 - {531: 5395}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5396}, - {9: 5123, 57: 5397}, - {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5398}, - {2545, 2545, 2545, 2545, 2545, 2545, 5375, 9: 2545, 19: 5372, 57: 2545, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2545, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, - // 2545 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5400}, - {531: 5401}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5402}, - {9: 5123, 57: 5403}, - {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5404}, - // 2550 - {2546, 2546, 2546, 2546, 2546, 2546, 5375, 9: 2546, 19: 5372, 57: 2546, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2546, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5406, 3051, 3052, 3050}, - {286: 5408, 294: 5410, 297: 5409, 1287: 5407}, - {531: 5411}, - {57: 2491, 531: 2491}, - // 2555 - {57: 2490, 531: 2490}, - {57: 2489, 531: 2489}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5412}, - {9: 4027, 57: 5413}, - {2766, 2766, 2766, 2766, 2766, 2766, 9: 2766, 546: 2766}, - // 2560 - {762, 762, 762, 762, 762, 762, 9: 762, 116: 762, 173: 5251, 531: 762, 546: 762, 908: 5250, 924: 5415}, - {2412, 2412, 2412, 2412, 2412, 2412, 9: 2412, 116: 5417, 531: 5418, 546: 2412, 1231: 5416}, - {2769, 2769, 2769, 2769, 2769, 2769, 9: 2769, 546: 2769}, - {560: 3037, 799: 5464}, - {546: 5421, 1067: 5420, 1230: 5419}, - // 2565 - {9: 5462, 57: 5461}, - {9: 2410, 57: 2410}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5422, 3051, 3052, 3050}, - {6: 2389, 2389, 9: 2389, 18: 2389, 20: 2389, 22: 2389, 2389, 2389, 2389, 2389, 2389, 57: 2389, 189: 5427, 262: 5426, 531: 2389, 535: 5425, 547: 5424, 710: 2389, 1414: 5423}, - {6: 2402, 2402, 9: 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 57: 2402, 531: 2402, 710: 2402, 1066: 5448}, - // 2570 - {192: 5428, 606: 5429}, - {6: 2386, 2386, 9: 2386, 18: 2386, 20: 2386, 22: 2386, 2386, 2386, 2386, 2386, 2386, 57: 2386, 531: 2386, 710: 2386}, - {6: 2384, 2384, 9: 2384, 18: 2384, 20: 2384, 22: 2384, 2384, 2384, 2384, 2384, 2384, 57: 2384, 531: 2384, 710: 2384}, - {6: 2383, 2383, 9: 2383, 18: 2383, 20: 2383, 22: 2383, 2383, 2383, 2383, 2383, 2383, 57: 2383, 531: 2383, 710: 2383}, - {199: 5438}, - // 2575 - {531: 5430}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 5432, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5433, 1147: 5434, 1348: 5431}, - {9: 5436, 57: 5435}, - {9: 2203, 57: 2203, 531: 3900}, - {9: 2202, 57: 2202, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - // 2580 - {9: 2186, 57: 2186}, - {6: 2385, 2385, 9: 2385, 18: 2385, 20: 2385, 22: 2385, 2385, 2385, 2385, 2385, 2385, 57: 2385, 531: 2385, 710: 2385}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 5432, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5433, 1147: 5437}, - {9: 2185, 57: 2185}, - {531: 5440, 720: 5439}, - // 2585 - {6: 2388, 2388, 9: 2388, 18: 2388, 20: 2388, 22: 2388, 2388, 2388, 2388, 2388, 2388, 57: 2388, 531: 2388, 710: 2388}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 720: 5442, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5443, 1212: 5444, 1395: 5441}, - {9: 5446, 57: 5445}, - {9: 2201, 57: 2201}, - {9: 2200, 57: 2200, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - // 2590 - {9: 2188, 57: 2188}, - {6: 2387, 2387, 9: 2387, 18: 2387, 20: 2387, 22: 2387, 2387, 2387, 2387, 2387, 2387, 57: 2387, 531: 2387, 710: 2387}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 720: 5442, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5443, 1212: 5447}, - {9: 2187, 57: 2187}, - {6: 4725, 5452, 9: 2407, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 57: 2407, 531: 5450, 710: 4730, 865: 4735, 910: 5451, 1475: 5449}, - // 2595 - {9: 2408, 57: 2408}, - {115: 5455, 1288: 5454, 1474: 5453}, - {2401, 2401, 6: 2401, 2401, 9: 2401, 18: 2401, 20: 2401, 22: 2401, 2401, 2401, 2401, 2401, 2401, 57: 2401, 531: 2401, 710: 2401}, - {23: 4877}, - {9: 5459, 57: 5458}, - // 2600 - {9: 2405, 57: 2405}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5456, 3051, 3052, 3050}, - {6: 2402, 2402, 9: 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 57: 2402, 710: 2402, 1066: 5457}, - {6: 4725, 5452, 9: 2403, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 57: 2403, 710: 4730, 865: 4735, 910: 5451}, - {9: 2406, 57: 2406}, - // 2605 - {115: 5455, 1288: 5460}, - {9: 2404, 57: 2404}, - {2411, 2411, 2411, 2411, 2411, 2411, 9: 2411, 530: 2411, 2411, 2411, 537: 2411, 546: 2411, 2411, 549: 2411, 552: 2411, 608: 2411, 695: 2411}, - {546: 5421, 1067: 5463}, - {9: 2409, 57: 2409}, - // 2610 - {2768, 2768, 2768, 2768, 2768, 2768, 9: 2768, 546: 2768}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5467, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 5466}, - {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5475}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5471, 1291: 5468}, - {9: 5473, 57: 5472}, - // 2615 - {9: 623, 57: 623}, - {9: 622, 57: 622}, - {9: 621, 57: 621}, - {2771, 2771, 2771, 2771, 2771, 2771, 9: 2771, 546: 2771}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5474}, - // 2620 - {9: 620, 57: 620}, - {2772, 2772, 2772, 2772, 2772, 2772, 9: 2772, 546: 2772}, - {16: 4418, 553: 4419, 709: 4417, 853: 5477}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 5479, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 5478}, - {444, 444, 444, 444, 444, 444, 9: 444, 538: 5481, 546: 444, 1221: 5483}, - // 2625 - {444, 444, 444, 444, 444, 444, 9: 444, 538: 5481, 546: 444, 1221: 5480}, - {2773, 2773, 2773, 2773, 2773, 2773, 9: 2773, 546: 2773}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 5482}, - {443, 443, 443, 443, 443, 443, 9: 443, 546: 443}, - {2774, 2774, 2774, 2774, 2774, 2774, 9: 2774, 546: 2774}, - // 2630 - {223: 5496}, - {200: 5486}, - {223: 5487}, - {560: 3037, 799: 3866, 816: 5488}, - {2779, 2779, 2779, 2779, 2779, 2779, 9: 2779, 220: 5489, 546: 2779, 1057: 5490}, - // 2635 - {319: 5491}, - {2775, 2775, 2775, 2775, 2775, 2775, 9: 2775, 546: 2775}, - {533: 5493, 1472: 5492}, - {2778, 2778, 2778, 2778, 2778, 2778, 9: 5494, 16: 2778, 18: 2778, 21: 2778, 535: 2778, 538: 2778, 546: 2778, 553: 2778, 557: 2778, 709: 2778}, - {442, 442, 442, 442, 442, 442, 9: 442, 16: 442, 18: 442, 21: 442, 535: 442, 538: 442, 546: 442, 553: 442, 557: 442, 709: 442}, - // 2640 - {533: 5495}, - {441, 441, 441, 441, 441, 441, 9: 441, 16: 441, 18: 441, 21: 441, 535: 441, 538: 441, 546: 441, 553: 441, 557: 441, 709: 441}, - {560: 3037, 799: 3866, 816: 5497}, - {2779, 2779, 2779, 2779, 2779, 2779, 9: 2779, 220: 5489, 546: 2779, 1057: 5498}, - {2776, 2776, 2776, 2776, 2776, 2776, 9: 2776, 546: 2776}, - // 2645 - {8: 579, 29: 579}, - {573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 15: 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 530: 573, 573, 573, 535: 573, 537: 573, 573, 573, 546: 573, 573, 549: 573, 552: 573, 573, 565: 573, 608: 573, 695: 573, 709: 573, 573}, - {6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5502}, - {572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 15: 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 530: 572, 572, 572, 535: 572, 537: 572, 572, 572, 546: 572, 572, 549: 572, 552: 572, 572, 565: 572, 608: 572, 695: 572, 709: 572, 572}, - {533: 5505, 535: 5504}, - // 2650 - {2789, 2789, 2789, 2789, 2789, 2789, 9: 2789, 546: 2789}, - {2788, 2788, 2788, 2788, 2788, 2788, 9: 2788, 546: 2788}, - {533: 5508, 535: 5507}, - {2791, 2791, 2791, 2791, 2791, 2791, 9: 2791, 546: 2791}, - {2790, 2790, 2790, 2790, 2790, 2790, 9: 2790, 546: 2790}, - // 2655 - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 535: 2316, 555: 4588, 557: 5511, 802: 5510}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5513, 535: 5515, 770: 5516, 3051, 3052, 3050, 989: 5514}, - {535: 5512}, - {2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 15: 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 57: 2792, 530: 2792, 2792, 2792, 535: 2792, 537: 2792, 2792, 2792, 546: 2792, 2792, 549: 2792, 552: 2792, 2792, 557: 2792, 565: 2792, 608: 2792, 695: 2792, 709: 2792, 2792}, - {2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 15: 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 57: 2795, 530: 2795, 2795, 2795, 535: 2795, 537: 2795, 2795, 2795, 546: 2795, 2795, 549: 2795, 552: 2795, 2795, 557: 2795, 565: 2795, 608: 2795, 695: 2795, 709: 2795, 2795}, - // 2660 - {2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 15: 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 57: 2794, 530: 2794, 2794, 2794, 535: 2794, 537: 2794, 2794, 2794, 546: 2794, 2794, 549: 2794, 552: 2794, 2794, 557: 2794, 565: 2794, 608: 2794, 695: 2794, 709: 2794, 2794}, - {2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 15: 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 57: 2793, 530: 2793, 2793, 2793, 535: 2793, 537: 2793, 2793, 2793, 546: 2793, 2793, 549: 2793, 552: 2793, 2793, 557: 2793, 565: 2793, 608: 2793, 695: 2793, 709: 2793, 2793}, - {2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 15: 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 57: 2464, 108: 2464, 119: 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 128: 2464, 2464, 2464, 530: 2464, 2464, 2464, 535: 2464, 537: 2464, 2464, 2464, 546: 2464, 2464, 549: 2464, 552: 2464, 2464, 557: 2464, 565: 2464, 608: 2464, 695: 2464, 709: 2464, 2464}, - {223: 5522}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5519}, - // 2665 - {2847, 2847, 9: 5256, 200: 5520}, - {223: 5521}, - {2846, 2846}, - {2848, 2848}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5524}, - // 2670 - {2642, 2642, 9: 5256, 532: 5527, 710: 5526, 902: 5525}, - {2851, 2851}, - {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 5542}, - {560: 5532, 636: 4089, 4088, 799: 5530, 916: 5531, 1112: 5529, 1318: 5528}, - {2641, 2641, 9: 5540}, - // 2675 - {2640, 2640, 9: 2640}, - {283: 5534, 289: 5536, 337: 5537, 356: 5535}, - {243: 5533}, - {243: 2494, 283: 2224, 289: 2224, 337: 2224, 356: 2224}, - {2633, 2633, 9: 2633}, - // 2680 - {2638, 2638, 9: 2638}, - {2637, 2637, 9: 2637}, - {382: 5538, 457: 5539}, - {2634, 2634, 9: 2634}, - {2636, 2636, 9: 2636}, - // 2685 - {2635, 2635, 9: 2635}, - {560: 5532, 636: 4089, 4088, 799: 5530, 916: 5531, 1112: 5541}, - {2639, 2639, 9: 2639}, - {2642, 2642, 9: 5546, 532: 5527, 902: 5545}, - {1106, 1106, 9: 1106, 57: 1106, 532: 1106}, - // 2690 - {1104, 1104, 9: 1104, 57: 1104, 532: 1104}, - {2850, 2850}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 704: 5548, 770: 5547, 3051, 3052, 3050}, - {1105, 1105, 9: 1105, 57: 1105, 532: 1105}, - {1103, 1103, 9: 1103, 57: 1103, 532: 1103}, - // 2695 - {2852, 2852}, - {2787, 2787}, - {32: 5662, 417: 5661}, - {546: 5653}, - {720: 5646}, - // 2700 - {10: 5639}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 723: 5557, 770: 5556, 3051, 3052, 3050}, - {2402, 2402, 6: 2402, 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 249: 4682, 710: 2402, 1028: 5637, 1066: 5638}, - {182: 2420, 404: 5562, 445: 5563, 591: 5561, 701: 2420, 1202: 5564, 5559, 1289: 5560, 1416: 5558}, - {2414, 2414, 115: 2414, 5627, 530: 2414, 2414, 2414, 537: 2414, 547: 2414, 549: 2414, 552: 2414, 608: 2414, 695: 2414, 1417: 5626}, - // 2705 - {182: 5614, 701: 5613}, - {2438, 2438, 115: 2438, 2438, 530: 2438, 2438, 2438, 537: 2438, 547: 2438, 549: 2438, 552: 2438, 608: 2438, 695: 2438}, - {131: 3950, 154: 3949, 531: 5577, 930: 5578}, - {131: 3950, 154: 3949, 531: 5570, 930: 5571}, - {2431, 2431, 115: 2431, 2431, 530: 2431, 2431, 2431, 537: 2431, 547: 2431, 549: 2431, 552: 2431, 556: 5566, 608: 2431, 641: 5565, 695: 2431}, - // 2710 - {182: 2419, 701: 2419}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5568}, - {560: 3037, 799: 3866, 816: 5567}, - {2432, 2432, 115: 2432, 2432, 530: 2432, 2432, 2432, 537: 2432, 547: 2432, 549: 2432, 552: 2432, 608: 2432, 695: 2432}, - {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 5569}, - // 2715 - {2433, 2433, 115: 2433, 2433, 530: 2433, 2433, 2433, 537: 2433, 547: 2433, 549: 2433, 552: 2433, 608: 2433, 695: 2433}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5575}, - {531: 5572}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5573}, - {9: 4027, 57: 5574}, - // 2720 - {2434, 2434, 115: 2434, 2434, 530: 2434, 2434, 2434, 537: 2434, 547: 2434, 549: 2434, 552: 2434, 608: 2434, 695: 2434}, - {57: 5576, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2435, 2435, 115: 2435, 2435, 530: 2435, 2435, 2435, 537: 2435, 547: 2435, 549: 2435, 552: 2435, 608: 2435, 695: 2435}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5610}, - {531: 5579}, - // 2725 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5580}, - {9: 4027, 57: 5581}, - {2430, 2430, 115: 2430, 2430, 530: 2430, 2430, 2430, 537: 2430, 547: 2430, 549: 2430, 552: 2430, 608: 2430, 641: 5583, 695: 2430, 1232: 5582}, - {2436, 2436, 115: 2436, 2436, 530: 2436, 2436, 2436, 537: 2436, 547: 2436, 549: 2436, 552: 2436, 608: 2436, 695: 2436}, - {531: 5584}, - // 2730 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5586, 1379: 5585}, - {57: 5588}, - {57: 2428, 118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 543: 3782, 3783, 3788, 584: 3784, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 886: 3747, 901: 5587}, - {57: 2427}, - {2422, 2422, 10: 5590, 115: 2422, 2422, 530: 2422, 2422, 2422, 537: 2422, 547: 2422, 2422, 2422, 552: 2422, 608: 2422, 695: 2422, 720: 2422, 1362: 5589}, - // 2735 - {2426, 2426, 115: 2426, 2426, 530: 2426, 2426, 2426, 537: 2426, 547: 2426, 5605, 2426, 552: 2426, 608: 2426, 695: 2426, 720: 2426, 1396: 5604}, - {546: 5591}, - {192: 5592}, - {199: 5593}, - {531: 5594}, - // 2740 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5595}, - {57: 5596, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {234: 5597}, - {546: 5598}, - {192: 5599}, - // 2745 - {199: 5600}, - {531: 5601}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5602}, - {57: 5603, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2421, 2421, 115: 2421, 2421, 530: 2421, 2421, 2421, 537: 2421, 547: 2421, 2421, 2421, 552: 2421, 608: 2421, 695: 2421, 720: 2421}, - // 2750 - {2424, 2424, 115: 2424, 2424, 530: 2424, 2424, 2424, 537: 2424, 547: 2424, 549: 2424, 552: 2424, 608: 2424, 695: 2424, 720: 5608, 1394: 5607}, - {546: 5606}, - {2425, 2425, 115: 2425, 2425, 530: 2425, 2425, 2425, 537: 2425, 547: 2425, 549: 2425, 552: 2425, 608: 2425, 695: 2425, 720: 2425}, - {2429, 2429, 115: 2429, 2429, 530: 2429, 2429, 2429, 537: 2429, 547: 2429, 549: 2429, 552: 2429, 608: 2429, 695: 2429}, - {546: 5609}, - // 2755 - {2423, 2423, 115: 2423, 2423, 530: 2423, 2423, 2423, 537: 2423, 547: 2423, 549: 2423, 552: 2423, 608: 2423, 695: 2423}, - {57: 5611, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2430, 2430, 115: 2430, 2430, 530: 2430, 2430, 2430, 537: 2430, 547: 2430, 549: 2430, 552: 2430, 608: 2430, 641: 5583, 695: 2430, 1232: 5612}, - {2437, 2437, 115: 2437, 2437, 530: 2437, 2437, 2437, 537: 2437, 547: 2437, 549: 2437, 552: 2437, 608: 2437, 695: 2437}, - {102: 5619, 531: 2440, 1415: 5618}, - // 2760 - {531: 5615}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5616}, - {57: 5617, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2441, 2441, 115: 2441, 2441, 274: 2441, 530: 2441, 2441, 2441, 537: 2441, 547: 2441, 549: 2441, 552: 2441, 608: 2441, 695: 2441}, - {531: 5622}, - // 2765 - {555: 5620}, - {560: 3037, 799: 5621}, - {531: 2439}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2608, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5623, 1123: 5624}, - {9: 4027, 57: 2607}, - // 2770 - {57: 5625}, - {2442, 2442, 115: 2442, 2442, 274: 2442, 530: 2442, 2442, 2442, 537: 2442, 547: 2442, 549: 2442, 552: 2442, 608: 2442, 695: 2442}, - {2418, 2418, 115: 5630, 530: 2418, 2418, 2418, 537: 2418, 547: 2418, 549: 2418, 552: 2418, 608: 2418, 695: 2418, 1477: 5629}, - {560: 3037, 799: 3866, 816: 5628}, - {2413, 2413, 115: 2413, 530: 2413, 2413, 2413, 537: 2413, 547: 2413, 549: 2413, 552: 2413, 608: 2413, 695: 2413}, - // 2775 - {2412, 2412, 530: 2412, 5418, 2412, 537: 2412, 547: 2412, 549: 2412, 552: 2412, 608: 2412, 695: 2412, 1231: 5636}, - {723: 5631}, - {182: 2420, 701: 2420, 1202: 5564, 5559, 1289: 5632}, - {2416, 2416, 274: 5634, 530: 2416, 2416, 2416, 537: 2416, 547: 2416, 549: 2416, 552: 2416, 608: 2416, 695: 2416, 1476: 5633}, - {2417, 2417, 530: 2417, 2417, 2417, 537: 2417, 547: 2417, 549: 2417, 552: 2417, 608: 2417, 695: 2417}, - // 2780 - {560: 3037, 799: 3866, 816: 5635}, - {2415, 2415, 530: 2415, 2415, 2415, 537: 2415, 547: 2415, 549: 2415, 552: 2415, 608: 2415, 695: 2415}, - {2443, 2443, 530: 2443, 2443, 2443, 537: 2443, 547: 2443, 549: 2443, 552: 2443, 608: 2443, 695: 2443}, - {2782, 2782}, - {2781, 2781, 6: 4725, 5452, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 710: 4730, 865: 4735, 910: 5451}, - // 2785 - {546: 5640}, - {192: 5641}, - {199: 5642}, - {531: 5643}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5644}, - // 2790 - {57: 5645, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2783, 2783}, - {546: 5647}, - {192: 5648}, - {199: 5649}, - // 2795 - {531: 5650}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5651}, - {57: 5652, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, - {2784, 2784}, - {762, 762, 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 908: 5250, 924: 5654}, - // 2800 - {2719, 2719, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5656, 1438: 5655}, - {2785, 2785}, - {9: 5256, 559: 5657}, - {531: 5658}, - {546: 5421, 1067: 5420, 1230: 5659}, - // 2805 - {9: 5462, 57: 5660}, - {2718, 2718}, - {2786, 2786}, - {2780, 2780}, - {173: 5664, 984: 264, 1209: 5665}, - // 2810 - {984: 263}, - {984: 5666}, - {533: 5667}, - {156, 156, 235: 156, 406: 5669, 722: 156, 1393: 5668}, - {154, 154, 235: 5672, 722: 154, 1392: 5671}, - // 2815 - {560: 3037, 799: 5670}, - {155, 155, 235: 155, 722: 155}, - {247, 247, 722: 3978, 1056: 5679}, - {152, 152, 239: 152, 418: 5674, 722: 152, 1419: 5673}, - {150, 150, 239: 5677, 722: 150, 1418: 5676}, - // 2820 - {560: 3037, 799: 5675}, - {151, 151, 239: 151, 722: 151}, - {153, 153, 722: 153}, - {560: 3037, 799: 5678}, - {149, 149, 722: 149}, - // 2825 - {157, 157}, - {28: 203, 56: 203, 159: 203, 531: 203, 560: 203}, - {56: 5210, 531: 5681, 999: 5218}, - {208, 208}, - {560: 3037, 799: 5687}, - // 2830 - {560: 3037, 799: 5686}, - {205, 205}, - {206, 206}, - {207, 207}, - {556: 5691}, - // 2835 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5690}, - {556: 209}, - {560: 3037, 799: 5692}, - {303: 5694, 532: 213, 552: 213, 587: 213, 714: 213, 803: 213, 1350: 5693}, - {532: 2906, 552: 2891, 587: 2890, 714: 3019, 803: 2870, 815: 5697, 822: 3018, 2871, 5701, 826: 5702, 5700, 833: 2872, 839: 5699, 1452: 5698}, - // 2840 - {433: 5695}, - {159: 5696, 532: 212, 552: 212, 587: 212, 714: 212, 803: 212}, - {532: 211, 552: 211, 587: 211, 714: 211, 803: 211}, - {714: 3019, 803: 2870, 822: 5705, 5703, 833: 5704}, - {218, 218}, - // 2845 - {217, 217}, - {216, 216}, - {215, 215}, - {214, 214}, - {2340, 2340}, - // 2850 - {2339, 2339}, - {429, 429, 542: 429}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5718, 1292: 5719, 1479: 5717}, - {227, 227, 227, 227, 227, 227, 227, 227, 227, 10: 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 58: 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227}, - {226, 226, 226, 226, 226, 226, 226, 226, 226, 10: 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 58: 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226}, - // 2855 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5710, 884: 5711}, - {1252, 1252, 9: 1252, 546: 5712}, - {201, 201, 9: 3930}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5714, 770: 5255, 3051, 3052, 3050, 859: 5713}, - {200, 200, 9: 5256}, - // 2860 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5715}, - {9: 5256, 57: 5716}, - {199, 199}, - {228, 228, 9: 5725}, - {729: 5721, 768: 5722, 1388: 5720}, - // 2865 - {220, 220, 9: 220}, - {225, 225, 9: 225}, - {224, 224, 9: 224, 173: 5724}, - {222, 222, 9: 222, 173: 5723}, - {221, 221, 9: 221}, - // 2870 - {223, 223, 9: 223}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5718, 1292: 5726}, - {219, 219, 9: 219}, - {229, 229}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5729, 884: 5730}, - // 2875 - {1252, 1252, 9: 1252, 546: 5731}, - {198, 198, 9: 3930}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5733, 770: 5255, 3051, 3052, 3050, 859: 5732}, - {197, 197, 9: 5256}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5734}, - // 2880 - {9: 5256, 57: 5735}, - {196, 196}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5737}, - {531: 5738, 557: 2596, 561: 2596, 1125: 5739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2602, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 628: 3682, 770: 4024, 3051, 3052, 3050, 776: 5766, 821: 5765, 1124: 5764, 1335: 5763, 5767}, - // 2885 - {557: 5740, 561: 241, 1207: 5741}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 5758, 1206: 5757, 1387: 5756}, - {561: 5742}, - {533: 5743}, - {270, 270, 232: 5744, 532: 270, 1179: 5745}, - // 2890 - {533: 5755}, - {236, 236, 532: 5746, 1205: 5747}, - {56: 5750, 1204: 5749, 1386: 5748}, - {230, 230}, - {235, 235, 9: 5753}, - // 2895 - {234, 234, 9: 234}, - {232, 232, 9: 232, 555: 5751}, - {533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4939, 779: 3582, 1082: 5752}, - {231, 231, 9: 231}, - {56: 5750, 1204: 5754}, - // 2900 - {233, 233, 9: 233}, - {269, 269, 532: 269, 549: 269, 552: 269, 559: 269}, - {240, 240, 9: 5761, 532: 240, 561: 240}, - {238, 238, 9: 238, 532: 238, 561: 238}, - {555: 5759}, - // 2905 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 5760}, - {237, 237, 9: 237, 532: 237, 561: 237}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 5758, 1206: 5762}, - {239, 239, 9: 239, 532: 239, 561: 239}, - {9: 5769, 57: 2601}, - // 2910 - {9: 2600, 57: 2600}, - {9: 2598, 57: 2598}, - {9: 2597, 57: 2597}, - {57: 5768}, - {2595, 2595, 532: 2595, 557: 2595, 561: 2595}, - // 2915 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 628: 3682, 770: 4024, 3051, 3052, 3050, 776: 5766, 821: 5765, 1124: 5770}, - {9: 2599, 57: 2599}, - {173: 5664, 984: 264, 1209: 5774}, - {533: 5773}, - {202, 202}, - // 2920 - {984: 5775}, - {533: 5776}, - {232: 5744, 549: 270, 552: 270, 559: 270, 1179: 5777}, - {549: 5778, 552: 5779, 559: 2382, 1164: 5780}, - {2381, 2381, 530: 2381, 2381, 2381, 537: 2381, 547: 2381, 559: 2381, 608: 2381, 695: 2381}, - // 2925 - {2380, 2380, 530: 2380, 2380, 2380, 537: 2380, 547: 2380, 559: 2380, 608: 2380, 695: 2380}, - {559: 5781}, - {608: 5782}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5783}, - {266, 266, 131: 266, 154: 266, 531: 266, 266, 549: 266, 557: 266, 709: 5785, 722: 266, 1332: 5784}, - // 2930 - {262, 262, 131: 3950, 154: 3949, 531: 262, 262, 549: 262, 557: 262, 722: 262, 930: 3948, 1173: 5788}, - {557: 5786}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 5787}, - {265, 265, 131: 265, 154: 265, 531: 265, 265, 549: 265, 557: 265, 722: 265}, - {247, 247, 531: 247, 247, 549: 247, 557: 247, 722: 3978, 1056: 5789}, - // 2935 - {268, 268, 531: 268, 268, 549: 5791, 557: 268, 1371: 5790}, - {2596, 2596, 531: 5738, 2596, 557: 2596, 1125: 5794}, - {560: 3037, 799: 5792}, - {722: 5793}, - {267, 267, 531: 267, 267, 557: 267}, - // 2940 - {241, 241, 532: 241, 557: 5740, 1207: 5795}, - {236, 236, 532: 5746, 1205: 5796}, - {271, 271}, - {9: 328, 56: 328, 530: 328, 561: 328, 628: 2090, 712: 328, 726: 2090}, - {9: 293, 530: 293, 293, 561: 293, 628: 2058, 712: 293, 726: 2058}, - // 2945 - {9: 307, 530: 307, 307, 561: 307, 628: 2032, 712: 307, 726: 2032}, - {9: 294, 530: 294, 294, 561: 294, 628: 2029, 712: 294, 726: 2029}, - {9: 283, 530: 283, 283, 561: 283, 628: 1992, 712: 283, 726: 1992}, - {9: 303, 530: 303, 303, 561: 303, 628: 1914, 712: 303, 726: 1914}, - {9: 308, 530: 308, 308, 561: 308, 628: 1907, 712: 308, 726: 1907}, - // 2950 - {374: 5906, 438: 5905, 628: 1888, 726: 1888}, - {9: 295, 530: 295, 295, 561: 295, 628: 1885, 712: 295, 726: 1885}, - {9: 284, 530: 284, 284, 561: 284, 628: 1882, 712: 284, 726: 1882}, - {628: 5903, 726: 5902}, - {9: 932, 530: 932, 561: 932, 628: 435, 712: 932, 726: 435}, - // 2955 - {9: 931, 530: 931, 561: 931, 712: 931}, - {9: 324, 56: 5901, 530: 324, 561: 324, 712: 324}, - {9: 326, 530: 326, 561: 326, 712: 326}, - {9: 325, 530: 325, 561: 325, 712: 325}, - {561: 5899}, - // 2960 - {9: 304, 530: 304, 304, 559: 5897, 561: 304, 712: 304}, - {9: 321, 530: 321, 561: 321, 712: 321}, - {9: 5849, 530: 5850, 561: 5851}, - {9: 319, 530: 319, 5846, 561: 319, 712: 319}, - {9: 317, 240: 5845, 530: 317, 317, 561: 317, 712: 317}, - // 2965 - {9: 315, 335: 5844, 530: 315, 315, 561: 315, 712: 315}, - {9: 314, 20: 5838, 132: 5840, 216: 5839, 5837, 224: 5841, 335: 5842, 530: 314, 314, 561: 314, 712: 314}, - {9: 311, 530: 311, 311, 561: 311, 712: 311}, - {9: 310, 530: 310, 310, 561: 310, 712: 310}, - {9: 309, 224: 5836, 530: 309, 309, 561: 309, 712: 309}, - // 2970 - {9: 306, 530: 306, 306, 561: 306, 712: 306}, - {9: 305, 530: 305, 305, 561: 305, 712: 305}, - {132: 5835, 1144: 5834}, - {9: 301, 530: 301, 301, 561: 301, 712: 301}, - {1006: 5833}, - // 2975 - {9: 299, 530: 299, 299, 561: 299, 712: 299}, - {9: 296, 530: 296, 296, 561: 296, 712: 296}, - {157: 5832}, - {9: 291, 530: 291, 291, 561: 291, 712: 291}, - {9: 300, 530: 300, 300, 561: 300, 712: 300}, - // 2980 - {9: 302, 530: 302, 302, 561: 302, 712: 302}, - {9: 289, 530: 289, 289, 561: 289, 712: 289}, - {9: 287, 530: 287, 287, 561: 287, 712: 287}, - {9: 313, 530: 313, 313, 561: 313, 712: 313}, - {9: 312, 530: 312, 312, 561: 312, 712: 312}, - // 2985 - {157: 5843}, - {9: 290, 530: 290, 290, 561: 290, 712: 290}, - {9: 288, 530: 288, 288, 561: 288, 712: 288}, - {9: 286, 530: 286, 286, 561: 286, 712: 286}, - {9: 292, 530: 292, 292, 561: 292, 712: 292}, - // 2990 - {9: 285, 530: 285, 285, 561: 285, 712: 285}, - {9: 316, 530: 316, 316, 561: 316, 712: 316}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5847}, - {9: 4027, 57: 5848}, - {9: 318, 530: 318, 561: 318, 712: 318}, - // 2995 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 3134, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5896, 1088: 5821, 1092: 5830}, - {2: 282, 282, 282, 282, 282, 282, 282, 10: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 58: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 5863, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 584: 282, 608: 5862, 964: 5864, 1215: 5865}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5855}, - {945, 945, 6: 945, 9: 945, 15: 945, 51: 945, 945, 945, 945, 945, 132: 945, 183: 945, 532: 945, 542: 945, 555: 945, 628: 5860, 696: 945, 712: 945, 717: 945, 725: 945, 5859}, - {1405, 1405, 6: 1405, 9: 1405, 15: 1405, 51: 1405, 1405, 1405, 1405, 1405, 132: 1405, 183: 1405, 531: 4363, 1405, 542: 1405, 555: 1405, 696: 1405, 712: 1405, 717: 1405, 725: 1405, 1224: 5858}, - // 3000 - {941, 941, 9: 941, 532: 941}, - {272, 272, 9: 5856}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5857}, - {940, 940, 9: 940, 532: 940}, - {942, 942, 6: 942, 9: 942, 15: 942, 51: 942, 942, 942, 942, 942, 132: 942, 183: 942, 532: 942, 542: 942, 555: 942, 696: 942, 712: 942, 717: 942, 725: 942}, - // 3005 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5861}, - {943, 943, 6: 943, 9: 943, 15: 943, 51: 943, 943, 943, 943, 943, 132: 943, 183: 943, 532: 943, 542: 943, 555: 943, 696: 943, 712: 943, 717: 943, 725: 943}, - {944, 944, 6: 944, 9: 944, 15: 944, 51: 944, 944, 944, 944, 944, 132: 944, 183: 944, 532: 944, 542: 944, 555: 944, 696: 944, 712: 944, 717: 944, 725: 944}, - {2: 281, 281, 281, 281, 281, 281, 281, 10: 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 58: 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 584: 281}, - {2: 280, 280, 280, 280, 280, 280, 280, 10: 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 58: 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 584: 280}, - // 3010 - {2: 279, 279, 279, 279, 279, 279, 279, 10: 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 58: 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 584: 279}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5866, 770: 5867, 3051, 3052, 3050, 1241: 5868}, - {561: 278, 712: 278, 716: 5894}, - {561: 274, 712: 274, 716: 5891}, - {561: 5869}, - // 3015 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 5872}, - {365, 365, 6: 365, 9: 365, 15: 365, 51: 365, 365, 365, 365, 365, 183: 5876, 532: 365, 725: 365, 1323: 5875}, - {411, 411, 6: 411, 9: 411, 15: 411, 51: 411, 411, 411, 411, 411, 532: 411, 725: 411}, - {273, 273, 9: 5873}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5874}, - // 3020 - {410, 410, 6: 410, 9: 410, 15: 410, 51: 410, 410, 410, 410, 410, 532: 410, 725: 410}, - {412, 412, 6: 412, 9: 412, 15: 412, 51: 412, 412, 412, 412, 412, 532: 412, 725: 412}, - {532: 5878, 723: 5877}, - {15: 5889, 533: 5886, 997: 5888}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5880, 1324: 5879}, - // 3025 - {363, 363, 6: 363, 9: 363, 15: 363, 51: 363, 363, 363, 363, 363, 532: 363, 537: 5882, 723: 5881, 725: 363}, - {359, 359, 6: 359, 9: 359, 15: 359, 51: 359, 359, 359, 359, 359, 532: 359, 537: 359, 723: 359, 725: 359}, - {533: 5886, 997: 5887}, - {533: 5884, 638: 5885, 1186: 5883}, - {361, 361, 6: 361, 9: 361, 15: 361, 51: 361, 361, 361, 361, 361, 532: 361, 725: 361}, - // 3030 - {358, 358, 6: 358, 9: 358, 15: 358, 51: 358, 358, 358, 358, 358, 532: 358, 725: 358}, - {357, 357, 6: 357, 9: 357, 15: 357, 51: 357, 357, 357, 357, 357, 532: 357, 725: 357}, - {937, 937, 6: 937, 9: 937, 15: 937, 51: 937, 937, 937, 937, 937, 57: 937, 532: 937, 725: 937}, - {362, 362, 6: 362, 9: 362, 15: 362, 51: 362, 362, 362, 362, 362, 532: 362, 725: 362}, - {364, 364, 6: 364, 9: 364, 15: 364, 51: 364, 364, 364, 364, 364, 532: 364, 725: 364}, - // 3035 - {533: 5884, 638: 5885, 1186: 5890}, - {360, 360, 6: 360, 9: 360, 15: 360, 51: 360, 360, 360, 360, 360, 532: 360, 725: 360}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5892, 770: 5893, 3051, 3052, 3050}, - {561: 276, 712: 276}, - {561: 275, 712: 275}, - // 3040 - {584: 5895}, - {561: 277, 712: 277}, - {9: 320, 530: 320, 561: 320, 712: 320}, - {336: 5898}, - {9: 322, 530: 322, 561: 322, 712: 322}, - // 3045 - {336: 5900}, - {9: 323, 530: 323, 561: 323, 712: 323}, - {9: 327, 56: 327, 530: 327, 561: 327, 712: 327}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5904}, - {933, 933, 9: 933, 530: 933, 561: 933, 712: 933}, - // 3050 - {934, 934, 9: 934, 530: 934, 561: 934, 712: 934}, - {9: 298, 530: 298, 298, 561: 298, 712: 298}, - {9: 297, 530: 297, 297, 561: 297, 712: 297}, - {530: 5949, 628: 2005, 726: 2005}, - {9: 5849, 530: 5909, 712: 5910}, - // 3055 - {2: 282, 282, 282, 282, 282, 282, 282, 10: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 58: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 5863, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 584: 282, 608: 5862, 964: 5864, 1215: 5912}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5911}, - {335, 335, 9: 5856}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5866, 770: 5867, 3051, 3052, 3050, 1241: 5913}, - {712: 5914}, - // 3060 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 5915}, - {401, 401, 9: 5873, 532: 401, 725: 5917, 1075: 5916, 5918}, - {400, 400, 6: 400, 15: 400, 51: 400, 400, 400, 400, 400, 532: 400}, - {161: 5938, 5936, 168: 5939, 5937, 5940, 409: 5931, 459: 5933, 1077: 5935, 1439: 5934, 1464: 5932}, - {334, 334, 532: 5920, 1307: 5919}, - // 3065 - {337, 337}, - {163: 5924, 5922, 5923, 5925, 957: 5921}, - {1006: 5930}, - {560: 3037, 799: 5929}, - {560: 3037, 799: 5928}, - // 3070 - {560: 3037, 799: 5927}, - {560: 3037, 799: 5926}, - {329, 329}, - {330, 330}, - {331, 331}, - // 3075 - {332, 332}, - {333, 333}, - {399, 399, 6: 399, 15: 399, 51: 399, 399, 399, 399, 399, 532: 399}, - {398, 398, 6: 398, 15: 398, 51: 398, 398, 398, 398, 398, 532: 398}, - {397, 397, 6: 397, 15: 397, 51: 397, 397, 397, 397, 397, 532: 397}, - // 3080 - {396, 396, 6: 396, 15: 396, 51: 396, 396, 396, 396, 396, 161: 5938, 5936, 168: 5939, 5937, 5940, 532: 396, 566: 5946, 1077: 5947}, - {395, 395, 6: 395, 15: 395, 51: 395, 395, 395, 395, 395, 161: 395, 395, 168: 395, 395, 395, 532: 395, 566: 395}, - {533: 5945}, - {533: 5944}, - {533: 5943}, - // 3085 - {533: 5942}, - {533: 5941}, - {388, 388, 6: 388, 15: 388, 51: 388, 388, 388, 388, 388, 161: 388, 388, 168: 388, 388, 388, 532: 388, 566: 388}, - {389, 389, 6: 389, 15: 389, 51: 389, 389, 389, 389, 389, 161: 389, 389, 168: 389, 389, 389, 532: 389, 566: 389}, - {390, 390, 6: 390, 15: 390, 51: 390, 390, 390, 390, 390, 161: 390, 390, 168: 390, 390, 390, 532: 390, 566: 390}, - // 3090 - {391, 391, 6: 391, 15: 391, 51: 391, 391, 391, 391, 391, 161: 391, 391, 168: 391, 391, 391, 532: 391, 566: 391}, - {392, 392, 6: 392, 15: 392, 51: 392, 392, 392, 392, 392, 161: 392, 392, 168: 392, 392, 392, 532: 392, 566: 392}, - {161: 5938, 5936, 168: 5939, 5937, 5940, 1077: 5948}, - {393, 393, 6: 393, 15: 393, 51: 393, 393, 393, 393, 393, 161: 393, 393, 168: 393, 393, 393, 532: 393, 566: 393}, - {394, 394, 6: 394, 15: 394, 51: 394, 394, 394, 394, 394, 161: 394, 394, 168: 394, 394, 394, 532: 394, 566: 394}, - // 3095 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5950}, - {712: 5951}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5952}, - {334, 334, 9: 5856, 532: 5920, 1307: 5953}, - {336, 336}, - // 3100 - {2465, 2465, 9: 2465, 16: 2465, 18: 2465, 21: 2465, 535: 2465, 538: 2465, 553: 2465, 557: 2465, 561: 2465, 563: 2465, 579: 2465, 709: 2465, 712: 2465, 765: 2465}, - {426, 426}, - {2: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 10: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 58: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 531: 1053, 533: 1053, 1053, 1053, 1053, 540: 1053, 1053, 543: 1053, 1053, 1053, 547: 1053, 1053, 1053, 552: 1053, 1053, 559: 1053, 1053, 1053, 573: 1053, 578: 1053, 584: 1053, 586: 1053, 1053, 614: 1053, 617: 1053, 628: 1053, 1053, 1053, 1053, 636: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 697: 1053, 1053, 1053, 1053, 711: 1053, 715: 1053, 829: 1053, 1053, 836: 1053, 1053, 1053, 840: 1053, 849: 1053, 1053, 1053}, - {2: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 10: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 58: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 531: 1051, 549: 1051, 559: 1051, 561: 1051, 643: 1051, 836: 1051, 1051, 1051}, - {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 531: 1256, 549: 1256, 643: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 5963}, - // 3105 - {2: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 10: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 58: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 531: 1259, 533: 1259, 1259, 1259, 1259, 540: 1259, 1259, 543: 1259, 1259, 1259, 547: 1259, 1259, 1259, 552: 1259, 1259, 559: 1259, 1259, 1259, 573: 1259, 578: 1259, 584: 1259, 586: 1259, 1259, 614: 1259, 617: 1259, 628: 1259, 1259, 1259, 1259, 636: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 697: 1259, 1259, 1259, 1259, 711: 1259, 715: 1259, 829: 1259, 1259, 836: 1259, 1259, 1259, 840: 1259, 849: 1259, 1259, 1259}, - {2: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 10: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 58: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 531: 1258, 533: 1258, 1258, 1258, 1258, 540: 1258, 1258, 543: 1258, 1258, 1258, 547: 1258, 1258, 1258, 552: 1258, 1258, 559: 1258, 1258, 1258, 573: 1258, 578: 1258, 584: 1258, 586: 1258, 1258, 614: 1258, 617: 1258, 628: 1258, 1258, 1258, 1258, 636: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 697: 1258, 1258, 1258, 1258, 711: 1258, 715: 1258, 829: 1258, 1258, 836: 1258, 1258, 1258, 840: 1258, 849: 1258, 1258, 1258}, - {2: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 10: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 58: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 531: 1257, 533: 1257, 1257, 1257, 1257, 540: 1257, 1257, 543: 1257, 1257, 1257, 547: 1257, 1257, 1257, 552: 1257, 1257, 559: 1257, 1257, 1257, 573: 1257, 578: 1257, 584: 1257, 586: 1257, 1257, 614: 1257, 617: 1257, 628: 1257, 1257, 1257, 1257, 636: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 697: 1257, 1257, 1257, 1257, 711: 1257, 715: 1257, 829: 1257, 1257, 836: 1257, 1257, 1257, 840: 1257, 849: 1257, 1257, 1257}, - {2: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 10: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 58: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 531: 1255, 549: 1255, 559: 1255, 561: 1255, 643: 1255}, - {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 531: 2115, 549: 4675, 643: 2115, 958: 5964}, - // 3110 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5967, 955: 5965, 993: 5966}, - {1130, 1130, 9: 1130, 57: 1130, 530: 1130, 532: 1130, 539: 1130, 542: 1130, 550: 1130, 1130, 554: 1130, 556: 1130, 1130, 1130, 1130, 562: 1130, 1130, 1130, 571: 1130, 1130, 574: 1130}, - {9: 6019, 557: 6088}, - {9: 1128, 540: 5986, 5987, 557: 6073, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6070, 3051, 3052, 3050}, - // 3115 - {1126, 1126, 9: 1126, 57: 1126, 530: 1126, 532: 1126, 539: 1126, 1126, 1126, 1126, 550: 1126, 1126, 554: 1126, 556: 1126, 1126, 1126, 1126, 562: 1126, 1126, 1126, 571: 1126, 1126, 1126, 1126, 576: 1126, 580: 1126, 1126, 583: 1126, 585: 1126}, - {1125, 1125, 9: 1125, 57: 1125, 530: 1125, 532: 1125, 539: 1125, 1125, 1125, 1125, 550: 1125, 1125, 554: 1125, 556: 1125, 1125, 1125, 1125, 562: 1125, 1125, 1125, 571: 1125, 1125, 1125, 1125, 576: 1125, 580: 1125, 1125, 583: 1125, 585: 1125}, - {1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 532: 1121, 537: 1121, 539: 1121, 1121, 1121, 1121, 546: 6023, 549: 1121, 1121, 1121, 554: 1121, 556: 1121, 1121, 1121, 1121, 562: 1121, 1121, 1121, 1121, 571: 1121, 1121, 1121, 1121, 1121, 1121, 580: 1121, 1121, 583: 1121, 585: 1121, 592: 1121, 731: 1121, 963: 6022}, - {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1119, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 1119, 532: 1119, 537: 5980, 539: 1119, 1119, 1119, 1119, 550: 1119, 1119, 554: 1119, 556: 1119, 1119, 1119, 1119, 562: 1119, 1119, 1119, 571: 1119, 1119, 1119, 1119, 576: 1119, 580: 1119, 1119, 583: 1119, 585: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 5977}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 2906, 547: 2905, 608: 2904, 643: 5968, 695: 2900, 770: 3927, 3051, 3052, 3050, 775: 5976, 804: 5971, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 5974}, - // 3120 - {9: 6019, 57: 6020}, - {1128, 1128, 9: 1128, 57: 1128, 530: 1128, 532: 1128, 539: 1128, 5986, 5987, 1128, 550: 1128, 1128, 554: 1128, 556: 1128, 1128, 1128, 1128, 562: 1128, 1128, 1128, 571: 1128, 1128, 5985, 1128, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 3989, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 537: 5980, 539: 1013, 1119, 1119, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 573: 1119, 576: 1119, 580: 1119, 1119, 583: 1119, 770: 5979, 3051, 3052, 3050, 842: 3860, 3861, 1016: 5978, 5977}, - {1123, 1123, 9: 1123, 57: 1123, 530: 1123, 532: 1123, 539: 1123, 1123, 1123, 1123, 550: 1123, 1123, 554: 1123, 556: 1123, 1123, 1123, 1123, 562: 1123, 1123, 1123, 571: 1123, 1123, 1123, 1123, 576: 1123, 580: 1123, 1123, 583: 1123, 585: 1123}, - {1118, 1118, 9: 1118, 57: 1118, 530: 1118, 532: 1118, 539: 1118, 1118, 1118, 1118, 549: 1118, 1118, 1118, 554: 1118, 556: 1118, 1118, 1118, 1118, 562: 1118, 1118, 1118, 1118, 571: 1118, 1118, 1118, 1118, 1118, 1118, 580: 1118, 1118, 583: 1118, 585: 1118, 592: 1118, 731: 1118}, - // 3125 - {1117, 1117, 9: 1117, 57: 1117, 530: 1117, 532: 1117, 539: 1117, 1117, 1117, 1117, 549: 1117, 1117, 1117, 554: 1117, 556: 1117, 1117, 1117, 1117, 562: 1117, 1117, 1117, 1117, 571: 1117, 1117, 1117, 1117, 1117, 1117, 580: 1117, 1117, 583: 1117, 585: 1117, 592: 1117, 731: 1117}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5981, 3051, 3052, 3050}, - {1116, 1116, 9: 1116, 57: 1116, 530: 1116, 532: 1116, 539: 1116, 1116, 1116, 1116, 549: 1116, 1116, 1116, 554: 1116, 556: 1116, 1116, 1116, 1116, 562: 1116, 1116, 1116, 1116, 571: 1116, 1116, 1116, 1116, 1116, 1116, 580: 1116, 1116, 583: 1116, 585: 1116, 592: 1116, 731: 1116}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6012}, - {576: 1087, 1009: 5999, 1229: 6003}, - // 3130 - {540: 5986, 5987, 576: 5996, 914: 5997}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5993}, - {576: 1089, 1009: 1089}, - {576: 1088, 1009: 1088}, - {2: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 10: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 58: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 531: 1085}, - // 3135 - {576: 5992}, - {576: 5991}, - {2: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 10: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 58: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 531: 1083}, - {2: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 10: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 58: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 531: 1084}, - {1091, 1091, 9: 1091, 57: 1091, 530: 5994, 532: 1091, 539: 1091, 1091, 1091, 1091, 550: 1091, 1091, 554: 1091, 556: 1091, 1091, 1091, 1091, 562: 1091, 1091, 1091, 571: 1091, 1091, 1091, 1091, 576: 1091, 580: 1091, 1091, 583: 1091, 585: 1091, 914: 5983, 919: 5982}, - // 3140 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5995}, - {1090, 1090, 9: 1090, 57: 1090, 530: 1090, 532: 1090, 539: 1090, 1090, 1090, 1090, 550: 1090, 1090, 554: 1090, 556: 1090, 1090, 1090, 1090, 562: 1090, 1090, 1090, 566: 3745, 3743, 3744, 3742, 3740, 1090, 1090, 1090, 1090, 576: 1090, 580: 1090, 1090, 583: 1090, 585: 1090, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6002}, - {576: 1087, 1009: 5999, 1229: 5998}, - {576: 6000}, - // 3145 - {576: 1086}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6001}, - {1092, 1092, 9: 1092, 57: 1092, 530: 1092, 532: 1092, 539: 1092, 1092, 1092, 1092, 550: 1092, 1092, 554: 1092, 556: 1092, 1092, 1092, 1092, 562: 1092, 1092, 1092, 571: 1092, 1092, 1092, 1092, 576: 1092, 580: 1092, 1092, 583: 1092, 585: 1092, 914: 5983, 919: 5982}, - {1093, 1093, 9: 1093, 57: 1093, 530: 1093, 532: 1093, 539: 1093, 1093, 1093, 1093, 550: 1093, 1093, 554: 1093, 556: 1093, 1093, 1093, 1093, 562: 1093, 1093, 1093, 571: 1093, 1093, 1093, 1093, 576: 1093, 580: 1093, 1093, 583: 1093, 585: 1093, 914: 5983, 919: 5982}, - {576: 6004}, - // 3150 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6005}, - {530: 6006, 540: 5986, 5987, 6007, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6011}, - {531: 6008}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 6009}, - // 3155 - {9: 4027, 57: 6010}, - {1094, 1094, 9: 1094, 57: 1094, 530: 1094, 532: 1094, 539: 1094, 1094, 1094, 1094, 550: 1094, 1094, 554: 1094, 556: 1094, 1094, 1094, 1094, 562: 1094, 1094, 1094, 571: 1094, 1094, 1094, 1094, 576: 1094, 580: 1094, 1094, 583: 1094, 585: 1094}, - {1095, 1095, 9: 1095, 57: 1095, 530: 1095, 532: 1095, 539: 1095, 1095, 1095, 1095, 550: 1095, 1095, 554: 1095, 556: 1095, 1095, 1095, 1095, 562: 1095, 1095, 1095, 566: 3745, 3743, 3744, 3742, 3740, 1095, 1095, 1095, 1095, 576: 1095, 580: 1095, 1095, 583: 1095, 585: 1095, 800: 3741, 3739}, - {1098, 1098, 9: 1098, 57: 1098, 530: 6013, 532: 1098, 539: 1098, 5986, 5987, 6014, 550: 1098, 1098, 554: 1098, 556: 1098, 1098, 1098, 1098, 562: 1098, 1098, 1098, 571: 1098, 1098, 5985, 1098, 576: 5988, 580: 5984, 5989, 583: 5990, 585: 1098, 914: 5983, 919: 5982}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6018}, - // 3160 - {531: 6015}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 6016}, - {9: 4027, 57: 6017}, - {1096, 1096, 9: 1096, 57: 1096, 530: 1096, 532: 1096, 539: 1096, 1096, 1096, 1096, 550: 1096, 1096, 554: 1096, 556: 1096, 1096, 1096, 1096, 562: 1096, 1096, 1096, 571: 1096, 1096, 1096, 1096, 576: 1096, 580: 1096, 1096, 583: 1096, 585: 1096}, - {1097, 1097, 9: 1097, 57: 1097, 530: 1097, 532: 1097, 539: 1097, 1097, 1097, 1097, 550: 1097, 1097, 554: 1097, 556: 1097, 1097, 1097, 1097, 562: 1097, 1097, 1097, 566: 3745, 3743, 3744, 3742, 3740, 1097, 1097, 1097, 1097, 576: 1097, 580: 1097, 1097, 583: 1097, 585: 1097, 800: 3741, 3739}, - // 3165 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 6021}, - {1122, 1122, 9: 1122, 57: 1122, 530: 1122, 532: 1122, 539: 1122, 1122, 1122, 1122, 550: 1122, 1122, 554: 1122, 556: 1122, 1122, 1122, 1122, 562: 1122, 1122, 1122, 571: 1122, 1122, 1122, 1122, 576: 1122, 580: 1122, 1122, 583: 1122, 585: 1122}, - {1129, 1129, 9: 1129, 57: 1129, 530: 1129, 532: 1129, 539: 1129, 542: 1129, 550: 1129, 1129, 554: 1129, 556: 1129, 1129, 1129, 1129, 562: 1129, 1129, 1129, 571: 1129, 1129, 574: 1129}, - {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1119, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 1119, 532: 1119, 537: 5980, 539: 1119, 1119, 1119, 1119, 549: 1119, 1119, 1119, 554: 1119, 556: 1119, 1119, 1119, 1119, 562: 1119, 1119, 1119, 1119, 571: 1119, 1119, 1119, 1119, 1119, 1119, 580: 1119, 1119, 583: 1119, 585: 1119, 592: 1119, 731: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 6027}, - {531: 6024}, - // 3170 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 6025}, - {9: 5256, 57: 6026}, - {1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 537: 1120, 539: 1120, 1120, 1120, 1120, 547: 1120, 549: 1120, 1120, 1120, 554: 1120, 556: 1120, 1120, 1120, 1120, 562: 1120, 1120, 1120, 1120, 571: 1120, 1120, 1120, 1120, 1120, 1120, 580: 1120, 1120, 583: 1120, 585: 1120, 592: 1120, 608: 1120, 627: 1120, 695: 1120, 710: 1120, 723: 1120, 731: 1120}, - {2122, 2122, 9: 2122, 57: 2122, 530: 2122, 532: 2122, 539: 2122, 2122, 2122, 2122, 549: 2122, 2122, 2122, 554: 2122, 556: 2122, 2122, 2122, 2122, 562: 2122, 2122, 2122, 2122, 571: 2122, 2122, 2122, 2122, 2122, 2122, 580: 2122, 2122, 583: 2122, 585: 2122, 592: 2122, 731: 4641, 995: 6028, 1321: 6029}, - {2121, 2121, 9: 2121, 57: 2121, 530: 2121, 532: 2121, 539: 2121, 2121, 2121, 2121, 549: 2121, 2121, 2121, 554: 2121, 556: 2121, 2121, 2121, 2121, 562: 2121, 2121, 2121, 2121, 571: 2121, 2121, 2121, 2121, 2121, 2121, 580: 2121, 2121, 583: 2121, 585: 2121, 592: 2121}, - // 3175 - {1100, 1100, 9: 1100, 57: 1100, 530: 1100, 532: 1100, 539: 1100, 1100, 1100, 1100, 549: 6032, 1100, 1100, 554: 1100, 556: 1100, 1100, 1100, 1100, 562: 1100, 1100, 1100, 6033, 571: 1100, 1100, 1100, 1100, 6031, 1100, 580: 1100, 1100, 583: 1100, 585: 1100, 592: 1100, 1051: 6035, 6034, 1191: 6036, 6030}, - {1215, 1215, 9: 1215, 57: 1215, 530: 1215, 532: 1215, 539: 1215, 1215, 1215, 1215, 550: 1215, 1215, 554: 1215, 556: 1215, 1215, 1215, 1215, 562: 1215, 1215, 1215, 571: 1215, 1215, 1215, 1215, 576: 1215, 580: 1215, 1215, 583: 1215, 585: 1215, 592: 6051, 1482: 6052}, - {701: 4906, 710: 4907, 923: 6050}, - {701: 4906, 710: 4907, 923: 6049}, - {701: 4906, 710: 4907, 923: 6048}, - // 3180 - {531: 1112, 558: 6038, 1373: 6039}, - {1102, 1102, 9: 1102, 57: 1102, 530: 1102, 532: 1102, 539: 1102, 1102, 1102, 1102, 549: 1102, 1102, 1102, 554: 1102, 556: 1102, 1102, 1102, 1102, 562: 1102, 1102, 1102, 1102, 571: 1102, 1102, 1102, 1102, 1102, 1102, 580: 1102, 1102, 583: 1102, 585: 1102, 592: 1102}, - {1099, 1099, 9: 1099, 57: 1099, 530: 1099, 532: 1099, 539: 1099, 1099, 1099, 1099, 549: 6032, 1099, 1099, 554: 1099, 556: 1099, 1099, 1099, 1099, 562: 1099, 1099, 1099, 6033, 571: 1099, 1099, 1099, 1099, 6031, 1099, 580: 1099, 1099, 583: 1099, 585: 1099, 592: 1099, 1051: 6037, 6034}, - {1101, 1101, 9: 1101, 57: 1101, 530: 1101, 532: 1101, 539: 1101, 1101, 1101, 1101, 549: 1101, 1101, 1101, 554: 1101, 556: 1101, 1101, 1101, 1101, 562: 1101, 1101, 1101, 1101, 571: 1101, 1101, 1101, 1101, 1101, 1101, 580: 1101, 1101, 583: 1101, 585: 1101, 592: 1101}, - {564: 6044, 571: 6045, 576: 6043}, - // 3185 - {531: 6040}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1107, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 6041}, - {9: 5546, 57: 6042}, - {1108, 1108, 9: 1108, 57: 1108, 530: 1108, 532: 1108, 539: 1108, 1108, 1108, 1108, 549: 1108, 1108, 1108, 554: 1108, 556: 1108, 1108, 1108, 1108, 562: 1108, 1108, 1108, 1108, 571: 1108, 1108, 1108, 1108, 1108, 1108, 580: 1108, 1108, 583: 1108, 585: 1108, 592: 1108}, - {531: 1111}, - // 3190 - {723: 6047}, - {723: 6046}, - {531: 1109}, - {531: 1110}, - {531: 1113, 558: 1113}, - // 3195 - {531: 1114, 558: 1114}, - {531: 1115, 558: 1115}, - {108: 6056, 368: 6055, 444: 6054, 531: 1212, 1481: 6053}, - {1124, 1124, 9: 1124, 57: 1124, 530: 1124, 532: 1124, 539: 1124, 1124, 1124, 1124, 550: 1124, 1124, 554: 1124, 556: 1124, 1124, 1124, 1124, 562: 1124, 1124, 1124, 571: 1124, 1124, 1124, 1124, 576: 1124, 580: 1124, 1124, 583: 1124, 585: 1124}, - {531: 6057}, - // 3200 - {531: 1211}, - {531: 1210}, - {531: 1209}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 6059, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6058}, - {57: 1208, 419: 6067, 566: 3745, 3743, 3744, 3742, 3740, 588: 6066, 800: 3741, 3739, 1483: 6065}, - // 3205 - {1205, 1205, 9: 1205, 57: 1205, 271: 6061, 530: 1205, 532: 1205, 539: 1205, 1205, 1205, 1205, 550: 1205, 1205, 554: 1205, 556: 1205, 1205, 1205, 1205, 562: 1205, 1205, 1205, 571: 1205, 1205, 1205, 1205, 576: 1205, 580: 1205, 1205, 583: 1205, 585: 1205, 1253: 6060}, - {1213, 1213, 9: 1213, 57: 1213, 530: 1213, 532: 1213, 539: 1213, 1213, 1213, 1213, 550: 1213, 1213, 554: 1213, 556: 1213, 1213, 1213, 1213, 562: 1213, 1213, 1213, 571: 1213, 1213, 1213, 1213, 576: 1213, 580: 1213, 1213, 583: 1213, 585: 1213}, - {531: 6062}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6063}, - {57: 6064, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 3210 - {1204, 1204, 9: 1204, 57: 1204, 530: 1204, 532: 1204, 539: 1204, 1204, 1204, 1204, 550: 1204, 1204, 554: 1204, 556: 1204, 1204, 1204, 1204, 562: 1204, 1204, 1204, 571: 1204, 1204, 1204, 1204, 576: 1204, 580: 1204, 1204, 583: 1204, 585: 1204}, - {57: 6068}, - {57: 1207}, - {57: 1206}, - {1205, 1205, 9: 1205, 57: 1205, 271: 6061, 530: 1205, 532: 1205, 539: 1205, 1205, 1205, 1205, 550: 1205, 1205, 554: 1205, 556: 1205, 1205, 1205, 1205, 562: 1205, 1205, 1205, 571: 1205, 1205, 1205, 1205, 576: 1205, 580: 1205, 1205, 583: 1205, 585: 1205, 1253: 6069}, - // 3215 - {1214, 1214, 9: 1214, 57: 1214, 530: 1214, 532: 1214, 539: 1214, 1214, 1214, 1214, 550: 1214, 1214, 554: 1214, 556: 1214, 1214, 1214, 1214, 562: 1214, 1214, 1214, 571: 1214, 1214, 1214, 1214, 576: 1214, 580: 1214, 1214, 583: 1214, 585: 1214}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6071}, - {540: 5986, 5987, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 585: 6072, 914: 5983, 919: 5982}, - {1127, 1127, 9: 1127, 57: 1127, 530: 1127, 532: 1127, 539: 1127, 542: 1127, 550: 1127, 1127, 554: 1127, 556: 1127, 1127, 1127, 1127, 562: 1127, 1127, 1127, 571: 1127, 1127, 574: 1127}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6076}, - // 3220 - {555: 6085, 717: 6086, 892: 6084}, - {2631, 2631, 9: 2631, 542: 2631, 556: 2631, 563: 2631, 2631}, - {424, 424, 9: 6077, 542: 424, 556: 424, 563: 4661, 424, 889: 4662, 6078}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6083}, - {1499, 1499, 542: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 6079}, - // 3225 - {1082, 1082, 542: 1082, 556: 6080, 1201: 6081}, - {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 6082}, - {428, 428, 542: 428}, - {1081, 1081, 542: 1081}, - {2630, 2630, 9: 2630, 542: 2630, 556: 2630, 563: 2630, 2630}, - // 3230 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6087}, - {2: 975, 975, 975, 975, 975, 975, 975, 10: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 58: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 533: 975, 975, 975, 975, 540: 975, 975, 543: 975, 975, 975, 547: 975, 975, 552: 975, 975, 560: 975, 578: 975, 586: 975, 975, 614: 975, 617: 975, 628: 975, 975, 975, 975, 636: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 697: 975, 975, 975, 975, 711: 975}, - {2: 974, 974, 974, 974, 974, 974, 974, 10: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 58: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 533: 974, 974, 974, 974, 540: 974, 974, 543: 974, 974, 974, 547: 974, 974, 552: 974, 974, 560: 974, 578: 974, 586: 974, 974, 614: 974, 617: 974, 628: 974, 974, 974, 974, 636: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 697: 974, 974, 974, 974, 711: 974}, - {2632, 2632, 9: 2632, 542: 2632, 556: 2632, 563: 2632, 2632}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6089}, - // 3235 - {424, 424, 9: 6077, 542: 424, 563: 4661, 889: 4662, 6090}, - {427, 427, 542: 427}, - {2: 570, 570, 570, 570, 570, 570, 570, 10: 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 58: 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6093}, - {569, 569}, - // 3240 - {23: 6106, 155: 768, 6099, 6096, 240: 6098, 246: 6109, 257: 6107, 275: 6100, 288: 6104, 309: 6108, 313: 6101, 586: 6105, 608: 6095, 1294: 6103, 1364: 6097, 1389: 6102}, - {2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 10: 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 58: 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 532: 2318, 578: 2318}, - {2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 10: 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 58: 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 532: 2317, 578: 2317}, - {778, 778}, - {775, 775}, - // 3245 - {774, 774}, - {267: 6116}, - {772, 772}, - {155: 6115}, - {759, 759, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 759, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 4799, 1293: 6110}, - // 3250 - {769, 769}, - {155: 767}, - {155: 766}, - {155: 765}, - {155: 764}, - // 3255 - {155: 763}, - {755, 755, 532: 6112, 1509: 6111}, - {770, 770}, - {729: 6113}, - {562: 6114}, - // 3260 - {754, 754}, - {771, 771}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6118}, - {777, 777, 9: 777}, - {773, 773, 9: 6119}, - // 3265 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6120, 3051, 3052, 3050}, - {776, 776, 9: 776}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 6232, 3305, 3203, 3057, 3420, 3084, 6233, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 6234, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6235}, - {608: 6218, 710: 6219}, - {710: 6215}, - // 3270 - {608: 6210, 710: 6209}, - {608: 6207}, - {252: 6204}, - {252: 6201}, - {252: 6195}, - // 3275 - {180: 6192, 273: 6194, 390: 6190, 414: 6191, 1015: 6193}, - {253: 6187, 256: 6186}, - {608: 6145}, - {180: 6139, 206: 6141, 221: 787, 245: 6143, 316: 6142, 1469: 6140}, - {180: 6138}, - // 3280 - {180: 6137}, - {447: 6136}, - {894, 894}, - {899, 899}, - {900, 900}, - // 3285 - {901, 901}, - {221: 6144}, - {221: 786}, - {221: 785}, - {221: 784}, - // 3290 - {893, 893}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6146}, - {743: 6147, 1035: 6148}, - {206: 6151, 216: 6150, 608: 2333, 1065: 6149}, - {902, 902}, - // 3295 - {608: 6153}, - {157: 2332, 608: 2332}, - {216: 6152}, - {157: 2331, 608: 2331}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 6154}, - // 3300 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6155}, - {619, 619, 6: 619, 619, 619, 15: 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 530: 619, 6159, 619, 535: 619, 537: 619, 619, 619, 546: 619, 619, 549: 619, 552: 619, 619, 565: 619, 579: 6158, 608: 619, 695: 619, 709: 619, 619, 1384: 6157, 1478: 6156}, - {576, 576, 6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 530: 576, 576, 576, 535: 4724, 537: 576, 2446, 4761, 546: 576, 576, 549: 576, 552: 576, 2446, 565: 5499, 608: 576, 695: 576, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 1020: 6174, 1141: 6173}, - {2449, 2449, 530: 6167, 1218: 6166}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6165}, - // 3305 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 579: 6160, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5471, 1291: 6161}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6163}, - {9: 5473, 57: 6162}, - {618, 618, 6: 618, 618, 618, 15: 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 530: 618, 618, 618, 535: 618, 537: 618, 618, 618, 546: 618, 618, 549: 618, 552: 618, 618, 565: 618, 608: 618, 695: 618, 709: 618, 618}, - {57: 6164}, - // 3310 - {2367, 2367, 530: 2367}, - {2368, 2368, 530: 2368}, - {2450, 2450}, - {85: 6168}, - {422: 6170, 803: 6169}, - // 3315 - {588: 6172}, - {588: 6171}, - {2447, 2447}, - {2448, 2448}, - {2444, 2444, 530: 2444, 2444, 2444, 537: 2444, 546: 6176, 2444, 549: 2444, 552: 2444, 608: 2444, 695: 2444, 1233: 6175}, - // 3320 - {575, 575, 6: 4725, 4727, 580, 5501, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 530: 575, 575, 575, 535: 4724, 537: 575, 2446, 4761, 546: 575, 575, 549: 575, 552: 575, 2446, 565: 5499, 608: 575, 695: 575, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5500}, - {2382, 2382, 530: 2382, 2382, 2382, 537: 2382, 547: 2382, 549: 5778, 552: 5779, 608: 2382, 695: 2382, 1164: 6177}, - {723: 5557}, - {2379, 2379, 530: 2379, 2379, 2379, 537: 6179, 547: 2379, 608: 2379, 695: 2379, 1322: 6178}, - {2377, 2377, 530: 2377, 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 6184, 806: 6182, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6183, 6181, 1343: 6180}, - // 3325 - {2378, 2378, 530: 2378, 2378, 2378, 547: 2378, 608: 2378, 695: 2378}, - {2449, 2449, 530: 6167, 1218: 6185}, - {2376, 2376, 530: 2376}, - {2375, 2375, 530: 2375, 539: 1014, 550: 1014, 1014}, - {2374, 2374, 530: 2374}, - // 3330 - {2373, 2373, 530: 2373, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {2451, 2451}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6189}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6188}, - {904, 904, 9: 6119}, - // 3335 - {905, 905, 9: 6119}, - {907, 907}, - {906, 906}, - {898, 898}, - {897, 897}, - // 3340 - {896, 896}, - {219: 6196}, - {560: 3037, 799: 4538, 825: 6198, 1005: 6197}, - {911, 911, 9: 6199}, - {885, 885, 9: 885}, - // 3345 - {560: 3037, 799: 4538, 825: 6200}, - {884, 884, 9: 884}, - {219: 6202}, - {560: 3037, 799: 4538, 825: 6198, 1005: 6203}, - {912, 912, 9: 6199}, - // 3350 - {219: 6205}, - {560: 3037, 799: 4538, 825: 6198, 1005: 6206}, - {913, 913, 9: 6199}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6208}, - {914, 914, 9: 3930}, - // 3355 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6213}, - {562: 6211}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6212}, - {903, 903, 9: 3930}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6214, 3051, 3052, 3050}, - // 3360 - {916, 916}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6216}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6217, 3051, 3052, 3050}, - {917, 917}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6231}, - // 3365 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6220}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6221, 3051, 3052, 3050}, - {918, 918, 531: 6224, 1185: 6223, 1369: 6222}, - {915, 915, 9: 6229}, - {888, 888, 9: 888}, - // 3370 - {560: 3037, 799: 4538, 825: 6225}, - {9: 6226}, - {560: 3037, 799: 4538, 825: 6227}, - {57: 6228}, - {886, 886, 9: 886}, - // 3375 - {531: 6224, 1185: 6230}, - {887, 887, 9: 887}, - {919, 919, 9: 3930}, - {211: 1893, 426: 6252, 450: 6253, 716: 1893, 1311: 6251}, - {923, 923, 178: 6238, 211: 1712, 219: 6237, 716: 1712}, - // 3380 - {895, 895, 211: 1691, 716: 1691}, - {211: 6236}, - {920, 920}, - {424, 424, 560: 3037, 563: 4661, 799: 4538, 825: 6249, 889: 4662, 6248}, - {425: 6239}, - // 3385 - {556: 6240, 560: 3037, 799: 4538, 825: 6198, 1005: 6241, 1312: 6242}, - {560: 3037, 799: 3866, 816: 6243}, - {910, 910, 9: 6199}, - {909, 909}, - {926, 926, 9: 6244, 213: 6245}, - // 3390 - {560: 3037, 799: 3866, 816: 6247}, - {560: 3037, 799: 3866, 816: 6246}, - {924, 924}, - {925, 925}, - {922, 922}, - // 3395 - {424, 424, 563: 4661, 889: 4662, 6250}, - {921, 921}, - {908, 908}, - {560: 3037, 799: 6259}, - {399: 6255, 560: 3037, 715: 6256, 799: 6254}, - // 3400 - {891, 891}, - {560: 3037, 799: 6258}, - {560: 3037, 799: 6257}, - {889, 889}, - {890, 890}, - // 3405 - {892, 892}, - {2: 446, 446, 446, 446, 446, 446, 446, 10: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 58: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 533: 446, 535: 446, 555: 2060, 586: 446, 716: 2060, 2060}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6415, 555: 2058, 716: 2058, 2058, 770: 6414, 3051, 3052, 3050}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6412, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2021, 716: 2021, 2021, 770: 6274, 3051, 3052, 3050, 926: 6315}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2015, 716: 2015, 2015, 770: 6274, 3051, 3052, 3050, 926: 6409}, - // 3410 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6405, 555: 2013, 586: 4354, 716: 2013, 2013, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 6404}, - {555: 6085, 558: 6394, 716: 2008, 2008, 892: 6393}, - {555: 2000, 571: 6391, 716: 2000, 2000}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 6296, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 535: 6388, 555: 1998, 715: 6386, 1998, 1998, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6299, 1269: 6387, 1451: 6385}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6383, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 1995, 716: 1995, 1995, 770: 6274, 3051, 3052, 3050, 926: 6312}, - // 3415 - {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6366}, - {383: 6320, 385: 6319, 555: 1921, 716: 1921, 1921, 1327: 6321}, - {533: 6318, 555: 1701, 716: 1701, 1701}, - {1006, 1006, 9: 6308}, - {224: 6294}, - // 3420 - {555: 973, 716: 6292, 973}, - {555: 6085, 717: 6086, 892: 6290}, - {555: 6085, 717: 6086, 892: 6285}, - {555: 6085, 717: 6086, 892: 6283}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6282, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 6281, 1331: 6280}, - // 3425 - {951, 951, 9: 951}, - {958, 958, 9: 958}, - {957, 957, 9: 957}, - {956, 956, 9: 956}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6284}, - // 3430 - {963, 963, 9: 963, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6289}, - {977, 977, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 977, 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4450, 3597, 3679, 3596, 3593}, - {978, 978, 9: 978}, - {976, 976, 9: 976}, - // 3435 - {964, 964, 9: 964}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6291}, - {968, 968, 9: 968}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6293, 3051, 3052, 3050}, - {555: 972, 717: 972}, - // 3440 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 6296, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 715: 6298, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6299, 1269: 6297}, - {935, 935, 9: 935, 628: 2090, 712: 935, 726: 2090}, - {994, 994, 628: 1916, 712: 994, 726: 1916}, - {712: 6306}, - {712: 993}, - // 3445 - {992, 992, 9: 6304, 712: 992}, - {936, 936, 9: 936, 628: 435, 712: 936, 726: 435}, - {930, 930, 9: 930, 712: 930}, - {929, 929, 9: 929, 712: 929}, - {928, 928, 9: 928, 712: 928}, - // 3450 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6305, 6301}, - {927, 927, 9: 927, 712: 927}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 6307}, - {995, 995, 9: 5856}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 6309, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6310, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6311}, - // 3455 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2021, 716: 2021, 2021, 770: 6274, 3051, 3052, 3050, 926: 6315}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 1995, 716: 1995, 1995, 770: 6274, 3051, 3052, 3050, 926: 6312}, - {950, 950, 9: 950}, - {555: 6085, 717: 6086, 892: 6313}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6314}, - // 3460 - {966, 966, 9: 966}, - {555: 6085, 717: 6086, 892: 6316}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6317}, - {967, 967, 9: 967}, - {998, 998}, - // 3465 - {558: 2488}, - {558: 2487}, - {558: 6322}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 6334, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 6333}, - {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 561: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 6339}, - // 3470 - {531: 2907, 547: 2905, 608: 2904, 695: 2900, 714: 3019, 775: 3854, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856, 822: 5705}, - {350, 350, 539: 1013, 542: 350, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {352, 352, 539: 1014, 542: 352, 550: 1014, 1014}, - {353, 353, 542: 353}, - {351, 351, 542: 351}, - // 3475 - {349, 349, 542: 349}, - {348, 348, 542: 348}, - {347, 347, 542: 347}, - {346, 346, 542: 346}, - {340, 340, 542: 6337}, - // 3480 - {218: 6335}, - {533: 6336}, - {338, 338}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 6338}, - {339, 339}, - // 3485 - {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 561: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6340}, - {2: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 10: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 58: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 6342, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 549: 1244, 561: 1244, 1247: 6341}, - {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 561: 2115, 958: 6343}, - {2: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 10: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 58: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 549: 1243, 561: 1243}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 561: 6344, 770: 6346, 3051, 3052, 3050, 1018: 6347, 1084: 6345}, - // 3490 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6359}, - {9: 6355, 561: 6354}, - {9: 1246, 542: 1246, 561: 1246, 716: 6349, 1008: 6348}, - {9: 1248, 542: 1248, 561: 1248}, - {9: 1250, 542: 1250, 561: 1250}, - // 3495 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6351, 770: 6350, 3051, 3052, 3050}, - {9: 1246, 542: 1246, 561: 1246, 716: 6353, 1008: 6352}, - {9: 1245, 542: 1245, 561: 1245}, - {9: 1249, 542: 1249, 561: 1249}, - {584: 6351}, - // 3500 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 6357}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6346, 3051, 3052, 3050, 1018: 6356}, - {9: 1247, 542: 1247, 561: 1247}, - {424, 424, 9: 6019, 542: 424, 563: 4661, 889: 4662, 6358}, - {2344, 2344, 542: 2344}, - // 3505 - {1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 10: 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 58: 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 537: 1121, 542: 1121, 546: 6023, 549: 1121, 556: 1121, 563: 1121, 1121, 1121, 575: 1121, 963: 6360}, - {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 537: 5980, 542: 1119, 549: 1119, 556: 1119, 563: 1119, 1119, 1119, 575: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 6361}, - {1100, 1100, 542: 1100, 549: 6032, 556: 1100, 563: 1100, 1100, 6033, 575: 6031, 1051: 6035, 6034, 1191: 6036, 6362}, - {424, 424, 542: 424, 556: 424, 563: 4661, 424, 889: 4662, 6363}, - {1499, 1499, 542: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 6364}, - // 3510 - {1082, 1082, 542: 1082, 556: 6080, 1201: 6365}, - {2345, 2345, 542: 2345}, - {1001, 1001, 9: 6381}, - {988, 988, 9: 988}, - {403: 6373}, - // 3515 - {194: 6371, 768: 6370}, - {985, 985, 9: 985}, - {984, 984, 9: 984, 731: 4641, 995: 6372}, - {983, 983, 9: 983}, - {271: 6375, 435: 6377, 729: 6376, 1380: 6374}, - // 3520 - {986, 986, 9: 986}, - {729: 6380}, - {378: 6378, 455: 6379}, - {979, 979, 9: 979}, - {981, 981, 9: 981}, - // 3525 - {980, 980, 9: 980}, - {982, 982, 9: 982}, - {233: 6368, 729: 6369, 1023: 6382}, - {987, 987, 9: 987}, - {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6384}, - // 3530 - {1002, 1002, 9: 6381}, - {996, 996}, - {993, 993, 550: 6389}, - {990, 990}, - {989, 989}, - // 3535 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6390}, - {991, 991, 9: 6304}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 6392}, - {997, 997}, - {15: 6399, 533: 6398, 1234: 6403}, - // 3540 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6395}, - {555: 6085, 717: 6086, 892: 6396}, - {15: 6399, 533: 6398, 1234: 6397}, - {1004, 1004}, - {939, 939}, - // 3545 - {531: 6400}, - {533: 5886, 997: 6401}, - {57: 6402}, - {938, 938}, - {1005, 1005}, - // 3550 - {962, 962, 9: 962, 538: 6406}, - {959, 959, 9: 959}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6407, 770: 3737, 3051, 3052, 3050, 805: 6408}, - {961, 961, 9: 961}, - {960, 960, 9: 960}, - // 3555 - {555: 6085, 717: 6086, 892: 6410}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6411}, - {965, 965, 9: 965}, - {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6413}, - {1003, 1003, 9: 6381}, - // 3560 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6424}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6416}, - {555: 6085, 717: 6086, 892: 6422}, - {544: 6419, 555: 971, 716: 6418, 971}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6421}, - // 3565 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6420}, - {555: 969, 717: 969}, - {555: 970, 717: 970}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6423}, - {999, 999}, - // 3570 - {555: 6085, 717: 6086, 892: 6425}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6426}, - {1000, 1000}, - {712: 6436}, - {712: 6429}, - // 3575 - {326: 6430}, - {555: 6431}, - {533: 6432}, - {558: 6433}, - {325: 6434}, - // 3580 - {533: 6435}, - {1007, 1007}, - {326: 6437}, - {555: 6438}, - {533: 6439}, - // 3585 - {558: 6440}, - {325: 6441}, - {533: 6442}, - {1008, 1008}, - {531: 2907, 547: 2905, 608: 2904, 695: 2900, 775: 6454, 806: 6453, 2901, 2902, 2903, 6455}, - // 3590 - {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6452}, - {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6451}, - {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6449}, - {2: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 10: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 58: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 531: 1442, 533: 1442, 1442, 1442, 1442, 540: 1442, 1442, 543: 1442, 1442, 1442, 547: 1442, 1442, 552: 1442, 1442, 560: 1442, 573: 1442, 578: 1442, 584: 1442, 586: 1442, 1442, 608: 1442, 614: 1442, 617: 1442, 628: 1442, 1442, 1442, 1442, 636: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 697: 1442, 1442, 1442, 1442, 711: 1442, 715: 1442, 829: 1442, 1442, 836: 1442, 1442, 1442, 840: 1442, 849: 1442, 1442, 1442}, - {531: 1438, 547: 1438, 608: 1438, 695: 1438}, - // 3595 - {531: 1010, 547: 1010, 608: 1010, 695: 1010}, - {531: 1009, 547: 1009, 608: 1009, 695: 1009}, - {531: 1011, 547: 1011, 608: 1011, 695: 1011}, - {531: 1012, 547: 1012, 608: 1012, 695: 1012}, - {1024, 1024, 57: 1024, 530: 1024, 532: 1024, 539: 1014, 542: 1024, 550: 1014, 1014}, - // 3600 - {1023, 1023, 57: 1023, 530: 1023, 532: 1023, 539: 1013, 542: 1023, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 6456, 6457}, - {539: 1015, 550: 1015, 1015}, - {1022, 1022, 57: 1022, 530: 1022, 532: 1022, 542: 1022, 554: 3859, 556: 3858, 843: 6458}, - {1021, 1021, 57: 1021, 530: 1021, 532: 1021, 542: 1021}, - {1020, 1020, 57: 1020, 530: 1020, 532: 1020, 542: 1020}, - // 3605 - {57: 3989, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {9: 6474, 531: 1196, 547: 1196, 608: 1196, 695: 1196, 714: 1196, 803: 1196}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6462, 1308: 6473}, - {9: 1193, 531: 1193, 547: 1193, 608: 1193, 695: 1193, 714: 1193, 803: 1193}, - {531: 6464, 537: 2606, 1370: 6465}, - // 3610 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 6468}, - {537: 6466}, - {531: 2907, 775: 6467}, - {9: 1192, 531: 1192, 547: 1192, 608: 1192, 695: 1192, 714: 1192, 803: 1192}, - {9: 6471, 57: 6470}, - // 3615 - {2604, 2604, 9: 2604, 57: 2604, 532: 2604}, - {537: 2605}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6472, 3051, 3052, 3050}, - {2603, 2603, 9: 2603, 57: 2603, 532: 2603}, - {9: 6474, 531: 1195, 547: 1195, 608: 1195, 695: 1195, 714: 1195, 803: 1195}, - // 3620 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6475}, - {9: 1194, 531: 1194, 547: 1194, 608: 1194, 695: 1194, 714: 1194, 803: 1194}, - {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6477}, - {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6478}, - {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6479}, - // 3625 - {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6480}, - {1200, 1200, 57: 1200, 530: 1200, 532: 1200, 539: 1200, 542: 1200, 550: 1200, 1200}, - {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6482}, - {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6483}, - {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6484}, - // 3630 - {1201, 1201, 57: 1201, 530: 1201, 532: 1201, 539: 1201, 542: 1201, 550: 1201, 1201}, - {723: 6492}, - {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6488}, - {1046, 1046, 57: 1046, 530: 1046, 532: 1046, 539: 1046, 542: 1046, 550: 1046, 1046, 554: 1046, 556: 1046, 558: 1046, 1046, 562: 1046, 564: 1046, 572: 1046, 574: 1046}, - {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6489}, - // 3635 - {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6490}, - {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6491}, - {1202, 1202, 57: 1202, 530: 1202, 532: 1202, 539: 1202, 542: 1202, 550: 1202, 1202}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 6493}, - {2127, 2127, 9: 3884, 57: 2127, 530: 2127, 532: 6494, 539: 2127, 542: 2127, 550: 2127, 2127, 554: 2127, 556: 2127, 558: 2127, 2127, 562: 2127, 564: 2127, 572: 2127, 574: 2127, 1510: 6495}, - // 3640 - {432: 6496}, - {2125, 2125, 57: 2125, 530: 2125, 532: 2125, 539: 2125, 542: 2125, 550: 2125, 2125, 554: 2125, 556: 2125, 558: 2125, 2125, 562: 2125, 564: 2125, 572: 2125, 574: 2125}, - {2126, 2126, 57: 2126, 530: 2126, 532: 2126, 539: 2126, 542: 2126, 550: 2126, 2126, 554: 2126, 556: 2126, 558: 2126, 2126, 562: 2126, 564: 2126, 572: 2126, 574: 2126}, - {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 562: 424, 4661, 424, 571: 424, 889: 4662, 6522}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 6507, 1351: 6506, 1480: 6505}, - // 3645 - {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1050: 6487, 1080: 6500}, - {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6501}, - {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6502}, - {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6503}, - {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6504}, - // 3650 - {1203, 1203, 57: 1203, 530: 1203, 532: 1203, 539: 1203, 542: 1203, 550: 1203, 1203}, - {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 562: 424, 4661, 424, 571: 424, 424, 574: 424, 889: 4662, 6508}, - {1191, 1191, 57: 1191, 530: 1191, 532: 1191, 539: 1191, 542: 1191, 550: 1191, 1191, 554: 1191, 556: 1191, 558: 1191, 1191, 562: 1191, 1191, 1191, 571: 1191}, - {1131, 1131, 9: 6019, 57: 1131, 530: 1131, 532: 1131, 539: 1131, 542: 1131, 550: 1131, 1131, 554: 1131, 556: 1131, 558: 1131, 1131, 562: 1131, 1131, 1131, 571: 1131, 1131, 574: 1131}, - {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1047, 574: 1047, 1050: 6487, 1080: 6509}, - // 3655 - {2124, 2124, 57: 2124, 530: 2124, 532: 2124, 539: 2124, 542: 2124, 550: 2124, 2124, 554: 2124, 556: 2124, 558: 2124, 2124, 562: 2124, 564: 2124, 572: 6510, 574: 2124, 1187: 6511}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6521}, - {1190, 1190, 57: 1190, 530: 1190, 532: 1190, 539: 1190, 542: 1190, 550: 1190, 1190, 554: 1190, 556: 1190, 558: 1190, 1190, 562: 1190, 564: 1190, 574: 6513, 1502: 6512}, - {1216, 1216, 57: 1216, 530: 1216, 532: 1216, 539: 1216, 542: 1216, 550: 1216, 1216, 554: 1216, 556: 1216, 558: 1216, 1216, 562: 1216, 564: 1216}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4066, 3051, 3052, 3050, 1026: 6516, 1304: 6515, 1503: 6514}, - // 3660 - {1189, 1189, 9: 6519, 57: 1189, 530: 1189, 532: 1189, 539: 1189, 542: 1189, 550: 1189, 1189, 554: 1189, 556: 1189, 558: 1189, 1189, 562: 1189, 564: 1189}, - {1188, 1188, 9: 1188, 57: 1188, 530: 1188, 532: 1188, 539: 1188, 542: 1188, 550: 1188, 1188, 554: 1188, 556: 1188, 558: 1188, 1188, 562: 1188, 564: 1188}, - {537: 6517}, - {531: 4067, 1306: 6518}, - {1186, 1186, 9: 1186, 57: 1186, 530: 1186, 532: 1186, 539: 1186, 542: 1186, 550: 1186, 1186, 554: 1186, 556: 1186, 558: 1186, 1186, 562: 1186, 564: 1186}, - // 3665 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4066, 3051, 3052, 3050, 1026: 6516, 1304: 6520}, - {1187, 1187, 9: 1187, 57: 1187, 530: 1187, 532: 1187, 539: 1187, 542: 1187, 550: 1187, 1187, 554: 1187, 556: 1187, 558: 1187, 1187, 562: 1187, 564: 1187}, - {2123, 2123, 57: 2123, 530: 2123, 532: 2123, 539: 2123, 542: 2123, 550: 2123, 2123, 554: 2123, 556: 2123, 558: 2123, 2123, 561: 2123, 2123, 2123, 2123, 566: 3745, 3743, 3744, 3742, 3740, 2123, 574: 2123, 800: 3741, 3739}, - {1217, 1217, 57: 1217, 530: 1217, 532: 1217, 539: 1217, 542: 1217, 550: 1217, 1217, 554: 1217, 556: 1217, 558: 1217, 1217, 562: 1217, 564: 1217, 571: 1217}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 6539, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 6540, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6538, 1171: 6541, 1361: 6542, 1446: 6543}, - // 3670 - {2: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 10: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 58: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 531: 1066, 533: 1066, 1066, 1066, 1066, 540: 1066, 1066, 543: 1066, 1066, 1066, 547: 1066, 1066, 552: 1066, 1066, 560: 1066, 573: 1066, 578: 1066, 584: 1066, 586: 1066, 1066, 614: 1066, 617: 1066, 628: 1066, 1066, 1066, 1066, 636: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 697: 1066, 1066, 1066, 1066, 711: 1066, 715: 1066, 829: 1066, 1066, 836: 1066, 1066, 1066, 840: 1066, 849: 1066, 1066, 1066}, - {2: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 10: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 58: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 531: 1065, 533: 1065, 1065, 1065, 1065, 540: 1065, 1065, 543: 1065, 1065, 1065, 547: 1065, 1065, 552: 1065, 1065, 560: 1065, 573: 1065, 578: 1065, 584: 1065, 586: 1065, 1065, 614: 1065, 617: 1065, 628: 1065, 1065, 1065, 1065, 636: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 697: 1065, 1065, 1065, 1065, 711: 1065, 715: 1065, 829: 1065, 1065, 836: 1065, 1065, 1065, 840: 1065, 849: 1065, 1065, 1065}, - {2: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 10: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 58: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 531: 1064, 533: 1064, 1064, 1064, 1064, 540: 1064, 1064, 543: 1064, 1064, 1064, 547: 1064, 1064, 552: 1064, 1064, 560: 1064, 573: 1064, 578: 1064, 584: 1064, 586: 1064, 1064, 614: 1064, 617: 1064, 628: 1064, 1064, 1064, 1064, 636: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 697: 1064, 1064, 1064, 1064, 711: 1064, 715: 1064, 829: 1064, 1064, 836: 1064, 1064, 1064, 840: 1064, 849: 1064, 1064, 1064}, - {2: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 10: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 58: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 531: 1063, 533: 1063, 1063, 1063, 1063, 540: 1063, 1063, 543: 1063, 1063, 1063, 547: 1063, 1063, 552: 1063, 1063, 560: 1063, 573: 1063, 578: 1063, 584: 1063, 586: 1063, 1063, 614: 1063, 617: 1063, 628: 1063, 1063, 1063, 1063, 636: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 697: 1063, 1063, 1063, 1063, 711: 1063, 715: 1063, 829: 1063, 1063, 836: 1063, 1063, 1063, 840: 1063, 849: 1063, 1063, 1063}, - {2: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 10: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 58: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 531: 1062, 533: 1062, 1062, 1062, 1062, 540: 1062, 1062, 543: 1062, 1062, 1062, 547: 1062, 1062, 552: 1062, 1062, 560: 1062, 573: 1062, 578: 1062, 584: 1062, 586: 1062, 1062, 614: 1062, 617: 1062, 628: 1062, 1062, 1062, 1062, 636: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 697: 1062, 1062, 1062, 1062, 711: 1062, 715: 1062, 829: 1062, 1062, 836: 1062, 1062, 1062, 840: 1062, 849: 1062, 1062, 1062}, - // 3675 - {2: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 10: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 58: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 531: 1061, 533: 1061, 1061, 1061, 1061, 540: 1061, 1061, 543: 1061, 1061, 1061, 547: 1061, 1061, 552: 1061, 1061, 560: 1061, 573: 1061, 578: 1061, 584: 1061, 586: 1061, 1061, 614: 1061, 617: 1061, 628: 1061, 1061, 1061, 1061, 636: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 697: 1061, 1061, 1061, 1061, 711: 1061, 715: 1061, 829: 1061, 1061, 836: 1061, 1061, 1061, 840: 1061, 849: 1061, 1061, 1061}, - {2: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 10: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 58: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 531: 1060, 533: 1060, 1060, 1060, 1060, 540: 1060, 1060, 543: 1060, 1060, 1060, 547: 1060, 1060, 552: 1060, 1060, 560: 1060, 573: 1060, 578: 1060, 584: 1060, 586: 1060, 1060, 614: 1060, 617: 1060, 628: 1060, 1060, 1060, 1060, 636: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 697: 1060, 1060, 1060, 1060, 711: 1060, 715: 1060, 829: 1060, 1060, 836: 1060, 1060, 1060, 840: 1060, 849: 1060, 1060, 1060}, - {2: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 10: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 58: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 531: 1059, 533: 1059, 1059, 1059, 1059, 540: 1059, 1059, 543: 1059, 1059, 1059, 547: 1059, 1059, 552: 1059, 1059, 560: 1059, 573: 1059, 578: 1059, 584: 1059, 586: 1059, 1059, 614: 1059, 617: 1059, 628: 1059, 1059, 1059, 1059, 636: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 697: 1059, 1059, 1059, 1059, 711: 1059, 715: 1059, 829: 1059, 1059, 836: 1059, 1059, 1059, 840: 1059, 849: 1059, 1059, 1059}, - {2: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 10: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 58: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 531: 1058, 533: 1058, 1058, 1058, 1058, 540: 1058, 1058, 543: 1058, 1058, 1058, 547: 1058, 1058, 552: 1058, 1058, 560: 1058, 573: 1058, 578: 1058, 584: 1058, 586: 1058, 1058, 614: 1058, 617: 1058, 628: 1058, 1058, 1058, 1058, 636: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 697: 1058, 1058, 1058, 1058, 711: 1058, 715: 1058, 829: 1058, 1058, 836: 1058, 1058, 1058, 840: 1058, 849: 1058, 1058, 1058}, - {2: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 10: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 58: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 6529, 6535, 6536, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 531: 1056, 533: 1056, 1056, 1056, 1056, 540: 1056, 1056, 543: 1056, 1056, 1056, 547: 1056, 1056, 552: 1056, 1056, 560: 1056, 573: 6532, 578: 1056, 584: 1056, 586: 1056, 1056, 614: 1056, 617: 1056, 628: 1056, 1056, 1056, 1056, 636: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 697: 1056, 1056, 1056, 1056, 711: 1056, 715: 4174, 829: 4172, 4173, 836: 5961, 5960, 5959, 840: 5956, 849: 6528, 6531, 6527, 885: 6447, 887: 6525, 934: 6526, 969: 6524, 1266: 6537, 6530}, - // 3680 - {2: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 10: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 58: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 531: 1054, 533: 1054, 1054, 1054, 1054, 540: 1054, 1054, 543: 1054, 1054, 1054, 547: 1054, 1054, 552: 1054, 1054, 560: 1054, 573: 1054, 578: 1054, 584: 1054, 586: 1054, 1054, 614: 1054, 617: 1054, 628: 1054, 1054, 1054, 1054, 636: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 697: 1054, 1054, 1054, 1054, 711: 1054, 715: 1054, 829: 1054, 1054, 836: 1054, 1054, 1054, 840: 1054, 849: 1054, 1054, 1054}, - {2: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 10: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 58: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 531: 1050, 533: 1050, 1050, 1050, 1050, 540: 1050, 1050, 543: 1050, 1050, 1050, 547: 1050, 1050, 552: 1050, 1050, 560: 1050, 573: 1050, 578: 1050, 584: 1050, 586: 1050, 1050, 614: 1050, 617: 1050, 628: 1050, 1050, 1050, 1050, 636: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 697: 1050, 1050, 1050, 1050, 711: 1050, 715: 1050, 829: 1050, 1050, 836: 1050, 1050, 1050, 840: 1050, 849: 1050, 1050, 1050}, - {2: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 10: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 58: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 531: 1049, 533: 1049, 1049, 1049, 1049, 540: 1049, 1049, 543: 1049, 1049, 1049, 547: 1049, 1049, 552: 1049, 1049, 560: 1049, 573: 1049, 578: 1049, 584: 1049, 586: 1049, 1049, 614: 1049, 617: 1049, 628: 1049, 1049, 1049, 1049, 636: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 697: 1049, 1049, 1049, 1049, 711: 1049, 715: 1049, 829: 1049, 1049, 836: 1049, 1049, 1049, 840: 1049, 849: 1049, 1049, 1049}, - {2: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 10: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 58: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 531: 1055, 533: 1055, 1055, 1055, 1055, 540: 1055, 1055, 543: 1055, 1055, 1055, 547: 1055, 1055, 552: 1055, 1055, 560: 1055, 573: 1055, 578: 1055, 584: 1055, 586: 1055, 1055, 614: 1055, 617: 1055, 628: 1055, 1055, 1055, 1055, 636: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 697: 1055, 1055, 1055, 1055, 711: 1055, 715: 1055, 829: 1055, 1055, 836: 1055, 1055, 1055, 840: 1055, 849: 1055, 1055, 1055}, - {2135, 2135, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 2135, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2135, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 2135, 532: 2135, 6556, 537: 6555, 539: 2135, 542: 2135, 550: 2135, 2135, 554: 2135, 556: 2135, 558: 2135, 2135, 561: 2135, 2135, 2135, 2135, 566: 3745, 3743, 3744, 3742, 3740, 2135, 2135, 770: 6554, 3051, 3052, 3050, 800: 3741, 3739, 1358: 6553, 6552}, - // 3685 - {2139, 2139, 9: 2139, 57: 2139, 530: 2139, 532: 2139, 539: 2139, 542: 2139, 550: 2139, 2139, 554: 2139, 556: 2139, 558: 2139, 2139, 561: 2139, 2139, 2139, 2139, 571: 2139, 2139}, - {1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 532: 1481, 1481, 1481, 536: 1481, 1481, 1481, 1481, 542: 1481, 1481, 1481, 1481, 550: 1481, 1481, 554: 1481, 1481, 1481, 558: 1481, 1481, 561: 1481, 1481, 1481, 1481, 566: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 579: 1481, 584: 1481, 606: 1481, 609: 1481, 1481, 1481, 1481, 1481, 615: 1481, 1481, 618: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 632: 1481, 1481, 1481, 1481, 703: 1481, 716: 6547, 718: 1481, 1481}, - {2129, 2129, 9: 2129, 57: 2129, 530: 2129, 532: 2129, 539: 2129, 542: 2129, 550: 2129, 2129, 554: 2129, 556: 2129, 558: 2129, 2129, 561: 2129, 2129, 2129, 2129, 571: 2129, 2129}, - {1048, 1048, 9: 6545, 57: 1048, 530: 1048, 532: 1048, 539: 1048, 542: 1048, 550: 1048, 1048, 554: 1048, 556: 1048, 558: 1048, 1048, 561: 1048, 1048, 1048, 1048, 571: 1048, 1048}, - {2124, 2124, 57: 2124, 530: 2124, 532: 2124, 539: 2124, 542: 2124, 550: 2124, 2124, 554: 2124, 556: 2124, 558: 2124, 2124, 561: 2124, 2124, 2124, 2124, 571: 2124, 6510, 1187: 6544}, - // 3690 - {1218, 1218, 57: 1218, 530: 1218, 532: 1218, 539: 1218, 542: 1218, 550: 1218, 1218, 554: 1218, 556: 1218, 558: 1218, 1218, 561: 1218, 1218, 1218, 1218, 571: 1218}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 6539, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 6540, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6538, 1171: 6546}, - {2128, 2128, 9: 2128, 57: 2128, 530: 2128, 532: 2128, 539: 2128, 542: 2128, 550: 2128, 2128, 554: 2128, 556: 2128, 558: 2128, 2128, 561: 2128, 2128, 2128, 2128, 571: 2128, 2128}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6548, 770: 6549, 3051, 3052, 3050}, - {2138, 2138, 9: 2138, 57: 2138, 530: 2138, 532: 2138, 539: 2138, 542: 2138, 550: 2138, 2138, 554: 2138, 556: 2138, 558: 2138, 2138, 561: 2138, 2138, 2138, 2138, 571: 2138, 2138}, - // 3695 - {1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 4477, 1480, 1480, 1480, 536: 1480, 1480, 1480, 1480, 542: 1480, 1480, 1480, 1480, 550: 1480, 1480, 554: 1480, 1480, 1480, 558: 1480, 1480, 561: 1480, 1480, 1480, 1480, 566: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 579: 1480, 584: 1480, 606: 1480, 609: 1480, 1480, 1480, 1480, 1480, 615: 1480, 1480, 618: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 632: 1480, 1480, 1480, 1480, 703: 1480, 716: 6550, 718: 1480, 1480}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6551, 770: 3907, 3051, 3052, 3050}, - {2137, 2137, 9: 2137, 57: 2137, 530: 2137, 532: 2137, 539: 2137, 542: 2137, 550: 2137, 2137, 554: 2137, 556: 2137, 558: 2137, 2137, 561: 2137, 2137, 2137, 2137, 571: 2137, 2137}, - {2136, 2136, 9: 2136, 57: 2136, 530: 2136, 532: 2136, 539: 2136, 542: 2136, 550: 2136, 2136, 554: 2136, 556: 2136, 558: 2136, 2136, 561: 2136, 2136, 2136, 2136, 571: 2136, 2136}, - {2134, 2134, 9: 2134, 57: 2134, 530: 2134, 532: 2134, 539: 2134, 542: 2134, 550: 2134, 2134, 554: 2134, 556: 2134, 558: 2134, 2134, 561: 2134, 2134, 2134, 2134, 571: 2134, 2134}, - // 3700 - {2133, 2133, 9: 2133, 57: 2133, 530: 2133, 532: 2133, 539: 2133, 542: 2133, 550: 2133, 2133, 554: 2133, 556: 2133, 558: 2133, 2133, 561: 2133, 2133, 2133, 2133, 571: 2133, 2133}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6558, 770: 6557, 3051, 3052, 3050}, - {2131, 2131, 9: 2131, 57: 2131, 530: 2131, 532: 2131, 539: 2131, 542: 2131, 550: 2131, 2131, 554: 2131, 556: 2131, 558: 2131, 2131, 561: 2131, 2131, 2131, 2131, 571: 2131, 2131}, - {2132, 2132, 9: 2132, 57: 2132, 530: 2132, 532: 2132, 539: 2132, 542: 2132, 550: 2132, 2132, 554: 2132, 556: 2132, 558: 2132, 2132, 561: 2132, 2132, 2132, 2132, 571: 2132, 2132}, - {2130, 2130, 9: 2130, 57: 2130, 530: 2130, 532: 2130, 539: 2130, 542: 2130, 550: 2130, 2130, 554: 2130, 556: 2130, 558: 2130, 2130, 561: 2130, 2130, 2130, 2130, 571: 2130, 2130}, - // 3705 - {1219, 1219}, - {1231, 1231}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 6574, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6575, 3051, 3052, 3050}, - {86: 6567, 287: 6566}, - {1223, 1223}, - // 3710 - {897: 6565}, - {1222, 1222}, - {1225, 1225, 86: 6572}, - {287: 6568}, - {1224, 1224, 86: 6570, 897: 6569}, - // 3715 - {1227, 1227}, - {897: 6571}, - {1226, 1226}, - {897: 6573}, - {1228, 1228}, - // 3720 - {1898, 1898, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6576, 3051, 3052, 3050}, - {1230, 1230}, - {1229, 1229}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6578, 3051, 3052, 3050}, - {1235, 1235}, - // 3725 - {1239, 1239, 542: 6580}, - {628: 3682, 776: 6582, 1488: 6581}, - {1238, 1238, 9: 6583}, - {1237, 1237, 9: 1237}, - {628: 3682, 776: 6584}, - // 3730 - {1236, 1236, 9: 1236}, - {561: 6586}, - {533: 6588, 628: 3682, 776: 6589, 1422: 6587}, - {1242, 1242}, - {1241, 1241}, - // 3735 - {1240, 1240}, - {2: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 10: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 58: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 559: 6591, 1196: 6592}, - {2: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 10: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 58: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6593}, - {186: 1121, 531: 1121, 1121, 546: 6023, 1121, 557: 1121, 608: 1121, 695: 1121, 963: 6594}, - // 3740 - {186: 6602, 531: 6595, 2906, 547: 6603, 557: 6601, 608: 2904, 695: 2900, 775: 6600, 806: 6598, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6599, 6597, 1095: 6596, 1195: 6604}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2608, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 770: 4024, 3051, 3052, 3050, 775: 6459, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 821: 4025, 906: 5623, 1123: 6617}, - {531: 3893, 938: 6614, 1093: 6613}, - {1547, 1547, 530: 1547, 542: 1547}, - {1546, 1546, 530: 1546, 539: 1014, 542: 1546, 550: 1014, 1014}, - // 3745 - {1545, 1545, 530: 1545, 542: 1545}, - {1544, 1544, 530: 1544, 539: 1013, 542: 1544, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6606, 1337: 6605}, - {531: 1542}, - {531: 1541, 639: 3892, 1013: 3891, 1094: 3890}, - // 3750 - {1527, 1527, 542: 1527}, - {1543, 1543, 9: 6609, 530: 1543, 542: 1543}, - {555: 6085, 717: 6086, 892: 6607}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6608}, - {1531, 1531, 9: 1531, 530: 1531, 542: 1531}, - // 3755 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6610}, - {555: 6085, 717: 6086, 892: 6611}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6612}, - {1530, 1530, 9: 1530, 530: 1530, 542: 1530}, - {1548, 1548, 9: 6615, 530: 1548, 542: 1548}, - // 3760 - {1540, 1540, 9: 1540, 530: 1540, 542: 1540}, - {531: 3893, 938: 6616}, - {1539, 1539, 9: 1539, 530: 1539, 542: 1539}, - {57: 6618}, - {186: 6602, 531: 2907, 2906, 547: 6603, 608: 2904, 695: 2900, 775: 6623, 806: 6621, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6622, 6620, 1095: 6619}, - // 3765 - {531: 3893, 938: 6614, 1093: 6624}, - {1552, 1552, 530: 1552, 542: 1552}, - {1551, 1551, 530: 1551, 539: 1014, 542: 1551, 550: 1014, 1014}, - {1550, 1550, 530: 1550, 542: 1550}, - {1549, 1549, 530: 1549, 539: 1013, 542: 1549, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - // 3770 - {1553, 1553, 9: 6615, 530: 1553, 542: 1553}, - {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 559: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6626}, - {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 559: 2115, 958: 6627}, - {2: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 10: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 58: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 559: 6591, 1196: 6628}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6629}, - // 3775 - {186: 1121, 531: 1121, 1121, 546: 6023, 1121, 557: 1121, 608: 1121, 695: 1121, 963: 6630}, - {186: 6602, 531: 6595, 2906, 547: 6603, 557: 6601, 608: 2904, 695: 2900, 775: 6600, 806: 6598, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6599, 6597, 1095: 6596, 1195: 6631}, - {1529, 1529, 530: 6633, 542: 1529, 1398: 6632}, - {1556, 1556, 542: 1556}, - {304: 6634}, - // 3780 - {701: 6635}, - {714: 6636}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6637}, - {1528, 1528, 9: 6077, 542: 1528}, - {1560, 1560, 531: 6646, 716: 2090}, - // 3785 - {1561, 1561}, - {716: 6641}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6642, 3051, 3052, 3050}, - {1559, 1559, 531: 6643}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 6644}, - // 3790 - {57: 6645}, - {1557, 1557}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 6647}, - {57: 6648}, - {1558, 1558}, - // 3795 - {2: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 10: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 58: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 535: 2338, 538: 2338, 553: 2338, 557: 2338, 578: 2338, 584: 2338, 709: 2338}, - {561: 6746}, - {561: 6660}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6655, 770: 5954, 3051, 3052, 3050, 907: 6657, 1347: 6656}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6654}, - // 3800 - {9: 3930, 561: 2269, 712: 2269}, - {561: 2271, 712: 2271}, - {9: 6658, 561: 2270, 712: 2270}, - {9: 2268, 561: 2268, 712: 2268}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6659}, - // 3805 - {9: 2267, 561: 2267, 712: 2267}, - {533: 6661}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6662}, - {2272, 2272, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - {2265, 2265, 17: 2265, 58: 2265, 60: 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 530: 2265, 713: 2265}, - // 3810 - {555: 2264, 560: 2264}, - {555: 2263, 560: 2263}, - {555: 2262, 560: 2262, 629: 2262, 2262}, - {555: 2261, 560: 2261, 629: 2261, 2261}, - {555: 2260, 560: 2260, 629: 2260, 2260}, - // 3815 - {555: 2259, 560: 2259, 629: 2259, 2259}, - {555: 2258, 560: 2258, 629: 2258, 2258}, - {555: 2257, 560: 2257, 629: 2257, 2257}, - {555: 2256, 560: 2256, 629: 2256, 2256}, - {555: 2255, 560: 2255, 629: 2255, 2255}, - // 3820 - {533: 2254, 555: 2254}, - {533: 2253, 555: 2253}, - {533: 2252, 555: 2252}, - {533: 2251, 555: 2251}, - {2: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 10: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 58: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 533: 2250, 549: 2250, 552: 2250, 555: 2250}, - // 3825 - {2: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 10: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 58: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 533: 2249, 549: 2249, 552: 2249, 555: 2249}, - {304: 6745}, - {555: 4588, 560: 2316, 802: 6743}, - {555: 4588, 560: 2316, 629: 2316, 2316, 802: 6741}, - {533: 2316, 555: 4588, 802: 6739}, - // 3830 - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 549: 2316, 552: 2316, 555: 4588, 802: 6734}, - {533: 2316, 555: 4588, 560: 2316, 802: 6729}, - {533: 2316, 555: 4588, 560: 2316, 802: 6726}, - {555: 4588, 560: 2316, 802: 6721}, - {131: 2316, 154: 2316, 555: 4588, 560: 2316, 802: 6718}, - // 3835 - {237: 2316, 2316, 241: 2316, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 6715}, - {237: 2316, 2316, 241: 2316, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 6706}, - {533: 2316, 555: 4588, 802: 6704}, - {533: 2316, 555: 4588, 802: 6702}, - {533: 2316, 555: 4588, 802: 6700}, - // 3840 - {533: 2316, 555: 4588, 802: 6698}, - {533: 2316, 555: 4588, 802: 6696}, - {533: 6697}, - {2227, 2227, 17: 2227, 58: 2227, 60: 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 530: 2227, 713: 2227}, - {533: 6699}, - // 3845 - {2228, 2228, 17: 2228, 58: 2228, 60: 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 530: 2228, 713: 2228}, - {533: 6701}, - {2229, 2229, 17: 2229, 58: 2229, 60: 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 530: 2229, 713: 2229}, - {533: 6703}, - {2230, 2230, 17: 2230, 58: 2230, 60: 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 530: 2230, 713: 2230}, - // 3850 - {533: 6705}, - {2231, 2231, 17: 2231, 58: 2231, 60: 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 530: 2231, 713: 2231}, - {237: 6712, 6713, 241: 6714, 560: 3037, 629: 6710, 6711, 799: 6709, 998: 6707, 1225: 6708}, - {2233, 2233, 17: 2233, 58: 2233, 60: 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 530: 2233, 713: 2233}, - {2232, 2232, 17: 2232, 58: 2232, 60: 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 530: 2232, 713: 2232}, - // 3855 - {2223, 2223, 9: 2223, 17: 2223, 58: 2223, 60: 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 110: 2223, 2223, 2223, 2223, 2223, 530: 2223, 713: 2223}, - {2222, 2222, 9: 2222, 17: 2222, 58: 2222, 60: 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 110: 2222, 2222, 2222, 2222, 2222, 530: 2222, 713: 2222}, - {2221, 2221, 9: 2221, 17: 2221, 58: 2221, 60: 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 110: 2221, 2221, 2221, 2221, 2221, 530: 2221, 713: 2221}, - {2220, 2220, 17: 2220, 58: 2220, 60: 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 530: 2220, 713: 2220}, - {2219, 2219, 17: 2219, 58: 2219, 60: 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 530: 2219, 713: 2219}, - // 3860 - {2218, 2218, 17: 2218, 58: 2218, 60: 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 530: 2218, 713: 2218}, - {237: 6712, 6713, 241: 6714, 560: 3037, 629: 6710, 6711, 799: 6709, 998: 6716, 1225: 6717}, - {2235, 2235, 17: 2235, 58: 2235, 60: 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 530: 2235, 713: 2235}, - {2234, 2234, 17: 2234, 58: 2234, 60: 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 530: 2234, 713: 2234}, - {131: 3950, 154: 3949, 560: 3037, 799: 3866, 816: 6720, 930: 6719}, - // 3865 - {2237, 2237, 17: 2237, 58: 2237, 60: 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 530: 2237, 713: 2237}, - {2236, 2236, 17: 2236, 58: 2236, 60: 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 530: 2236, 713: 2236}, - {560: 3037, 799: 3866, 816: 6722}, - {263: 6723}, - {609: 6724}, - // 3870 - {137: 6725}, - {2238, 2238, 17: 2238, 58: 2238, 60: 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 530: 2238, 713: 2238}, - {533: 6727, 560: 3037, 799: 3866, 816: 6728}, - {2240, 2240, 17: 2240, 58: 2240, 60: 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 530: 2240, 713: 2240}, - {2239, 2239, 17: 2239, 58: 2239, 60: 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 530: 2239, 713: 2239}, - // 3875 - {533: 6731, 560: 3037, 799: 3866, 816: 6730}, - {2241, 2241, 17: 2241, 58: 2241, 60: 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 530: 2241, 713: 2241, 886: 6732}, - {2242, 2242, 17: 2242, 58: 2242, 60: 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 530: 2242, 713: 2242}, - {365: 6733}, - {2243, 2243, 17: 2243, 58: 2243, 60: 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 530: 2243, 713: 2243}, - // 3880 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 549: 6737, 552: 6738, 770: 3737, 3051, 3052, 3050, 805: 6736, 1473: 6735}, - {2244, 2244, 17: 2244, 58: 2244, 60: 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 530: 2244, 713: 2244}, - {433, 433, 17: 433, 58: 433, 60: 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 530: 433, 713: 433}, - {432, 432, 17: 432, 58: 432, 60: 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 530: 432, 713: 432}, - {431, 431, 17: 431, 58: 431, 60: 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 530: 431, 713: 431}, - // 3885 - {533: 6740}, - {2245, 2245, 17: 2245, 58: 2245, 60: 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 530: 2245, 713: 2245}, - {560: 3037, 629: 6710, 6711, 799: 6709, 998: 6742}, - {2246, 2246, 17: 2246, 58: 2246, 60: 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 530: 2246, 713: 2246}, - {560: 3037, 799: 3866, 816: 6744}, - // 3890 - {2247, 2247, 17: 2247, 58: 2247, 60: 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 530: 2247, 713: 2247}, - {2: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 10: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 58: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 533: 2248, 549: 2248, 552: 2248, 555: 2248}, - {533: 6747}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6748}, - {2273, 2273, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - // 3895 - {178: 6753}, - {178: 6751}, - {560: 3037, 799: 4538, 825: 6752}, - {2215, 2215}, - {560: 3037, 799: 4538, 825: 6754}, - // 3900 - {2275, 2275}, - {155: 6926, 323: 6927}, - {178: 6922}, - {793, 793, 563: 6919, 579: 6918, 1454: 6917}, - {18: 6902, 51: 6903, 132: 6899, 217: 6904, 244: 6901, 608: 6898, 644: 6900, 964: 6905}, - // 3905 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 6887, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6888}, - {872, 872, 558: 6882}, - {156: 6881}, - {131: 3950, 154: 3949, 157: 6876, 268: 6875, 930: 6877}, - {868, 868}, - // 3910 - {858, 858, 236: 6857, 280: 6858, 292: 6859, 295: 6856, 318: 6861, 328: 6860, 342: 6863, 345: 6862, 554: 858, 556: 858, 558: 858, 715: 6864, 1272: 6855, 1457: 6854, 6853}, - {866, 866}, - {865, 865}, - {796, 796, 319: 6845, 558: 6844, 563: 796, 579: 796}, - {178: 6841, 219: 6842}, - // 3915 - {561: 841, 606: 841}, - {561: 840, 606: 840}, - {561: 839, 606: 839}, - {836, 836, 563: 836, 579: 836}, - {835, 835, 563: 835, 579: 835}, - // 3920 - {834, 834, 563: 834, 579: 834}, - {833, 833, 563: 833, 579: 833}, - {157: 6839}, - {561: 6809, 606: 6810, 903: 6834}, - {131: 783, 154: 783, 260: 6807, 1222: 6828}, - // 3925 - {531: 6823}, - {824, 824, 563: 824, 579: 824}, - {822, 822, 563: 822, 579: 822}, - {156: 6821, 180: 6822, 248: 6820}, - {818, 818, 563: 818, 579: 818}, - // 3930 - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6819}, - {156: 6818}, - {156: 6817}, - {156: 6816}, - {156: 6815}, - // 3935 - {156: 6814}, - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6811}, - {810, 810, 563: 810, 579: 810}, - {809, 809, 563: 809, 579: 809}, - {808, 808, 563: 808, 579: 808}, - // 3940 - {807, 807, 563: 807, 579: 807}, - {806, 806, 563: 806, 579: 806}, - {805, 805, 563: 805, 579: 805}, - {804, 804, 563: 804, 579: 804}, - {803, 803, 563: 803, 579: 803}, - // 3945 - {802, 802, 563: 802, 579: 802}, - {801, 801, 563: 801, 579: 801}, - {800, 800, 563: 800, 579: 800}, - {156: 6808}, - {798, 798, 563: 798, 579: 798}, - // 3950 - {797, 797, 563: 797, 579: 797}, - {156: 789, 180: 789, 248: 789}, - {156: 788, 180: 788, 202: 788, 248: 788}, - {131: 782, 154: 782, 157: 782, 268: 782}, - {799, 799, 563: 799, 579: 799}, - // 3955 - {2: 838, 838, 838, 838, 838, 838, 838, 10: 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 58: 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838}, - {2: 837, 837, 837, 837, 837, 837, 837, 10: 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 58: 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837}, - {811, 811, 563: 811, 579: 811}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6813}, - {780, 780, 563: 780, 579: 780}, - // 3960 - {812, 812, 563: 812, 579: 812}, - {813, 813, 563: 813, 579: 813}, - {814, 814, 563: 814, 579: 814}, - {815, 815, 563: 815, 579: 815}, - {816, 816, 563: 816, 579: 816}, - // 3965 - {817, 817, 563: 817, 579: 817}, - {821, 821, 563: 821, 579: 821}, - {820, 820, 563: 820, 579: 820}, - {819, 819, 563: 819, 579: 819}, - {584: 6824}, - // 3970 - {57: 6825}, - {314: 6827, 362: 6826}, - {825, 825, 563: 825, 579: 825}, - {823, 823, 563: 823, 579: 823}, - {131: 3950, 154: 3949, 930: 6829}, - // 3975 - {561: 6809, 606: 6810, 903: 6831, 1274: 6830}, - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6833}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6832}, - {779, 779, 561: 779, 563: 779, 579: 779, 606: 779}, - {826, 826, 563: 826, 579: 826}, - // 3980 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6835, 3051, 3052, 3050, 804: 6836}, - {1254, 1254, 561: 6809, 563: 1254, 579: 1254, 606: 6810, 716: 3932, 903: 6837}, - {829, 829, 563: 829, 579: 829}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6838, 3051, 3052, 3050}, - {828, 828, 563: 828, 579: 828}, - // 3985 - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6840}, - {831, 831, 563: 831, 579: 831}, - {560: 3037, 799: 4538, 825: 6843}, - {794, 794, 563: 794, 579: 794}, - {863, 863}, - // 3990 - {608: 6848, 644: 6649, 929: 6847, 1455: 6846}, - {795, 795, 563: 795, 579: 795}, - {864, 864}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6852}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6849}, - // 3995 - {860, 860, 546: 6850}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6851, 3051, 3052, 3050}, - {859, 859}, - {861, 861}, - {845, 845, 554: 845, 556: 845, 558: 6871, 1456: 6870}, - // 4000 - {857, 857, 9: 6868, 554: 857, 556: 857, 558: 857}, - {856, 856, 9: 856, 554: 856, 556: 856, 558: 856}, - {854, 854, 9: 854, 554: 854, 556: 854, 558: 854}, - {853, 853, 9: 853, 554: 853, 556: 853, 558: 853}, - {401: 6867}, - // 4005 - {443: 6866}, - {392: 6865}, - {849, 849, 9: 849, 554: 849, 556: 849, 558: 849}, - {848, 848, 9: 848, 554: 848, 556: 848, 558: 848}, - {847, 847, 9: 847, 554: 847, 556: 847, 558: 847}, - // 4010 - {846, 846, 9: 846, 554: 846, 556: 846, 558: 846}, - {850, 850, 9: 850, 554: 850, 556: 850, 558: 850}, - {851, 851, 9: 851, 554: 851, 556: 851, 558: 851}, - {852, 852, 9: 852, 554: 852, 556: 852, 558: 852}, - {236: 6857, 280: 6858, 292: 6859, 295: 6856, 318: 6861, 328: 6860, 342: 6863, 345: 6862, 715: 6864, 1272: 6869}, - // 4015 - {855, 855, 9: 855, 554: 855, 556: 855, 558: 855}, - {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 6874}, - {159: 6872}, - {560: 3037, 799: 4538, 825: 6873}, - {844, 844, 554: 844, 556: 844}, - // 4020 - {867, 867}, - {869, 869}, - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6880}, - {561: 6809, 606: 6810, 903: 6831, 1274: 6878}, - {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6879}, - // 4025 - {827, 827, 563: 827, 579: 827}, - {832, 832, 563: 832, 579: 832}, - {870, 870}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6883}, - {843, 843, 542: 6885, 1489: 6884}, - // 4030 - {871, 871}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6886}, - {842, 842, 9: 6304}, - {781, 781, 108: 1989, 211: 1989, 546: 1989, 561: 6809, 563: 781, 579: 781, 606: 6810, 710: 1989, 716: 1989, 903: 6812, 940: 6897}, - {108: 1121, 211: 6890, 546: 6023, 710: 1121, 963: 6889}, - // 4035 - {108: 6891, 710: 6892}, - {874, 874}, - {424, 424, 563: 4661, 889: 4662, 6896}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6893, 3051, 3052, 3050}, - {108: 6894}, - // 4040 - {424, 424, 563: 4661, 889: 4662, 6895}, - {873, 873}, - {875, 875}, - {830, 830, 563: 830, 579: 830}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6916}, - // 4045 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6915}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 6913}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6912}, - {214: 6910}, - {571: 6908}, - // 4050 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6907}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6906}, - {862, 862}, - {876, 876}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 6909}, - // 4055 - {877, 877}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 6911}, - {878, 878}, - {879, 879}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6914}, - // 4060 - {880, 880}, - {881, 881}, - {882, 882}, - {883, 883}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 6921, 3597, 3679, 3596, 3593}, - // 4065 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6920}, - {791, 791, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {792, 792, 538: 3693, 703: 3694}, - {159: 6924, 560: 3037, 799: 4538, 825: 6923}, - {2277, 2277}, - // 4070 - {560: 3037, 799: 4538, 825: 6925}, - {2276, 2276}, - {156: 6930, 323: 6931}, - {561: 6928}, - {533: 6929}, - // 4075 - {2274, 2274}, - {2279, 2279}, - {561: 6932}, - {533: 6933}, - {2278, 2278}, - // 4080 - {155: 6935}, - {561: 6936}, - {533: 6937}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6938}, - {2280, 2280, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - // 4085 - {155: 6944}, - {22: 6941}, - {178: 6942}, - {560: 3037, 799: 4538, 825: 6943}, - {2216, 2216}, - // 4090 - {2281, 2281}, - {155: 6950}, - {22: 6947}, - {178: 6948}, - {560: 3037, 799: 4538, 825: 6949}, - // 4095 - {2217, 2217}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6951}, - {2282, 2282, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - {155: 6953}, - {2283, 2283}, - // 4100 - {712: 6959}, - {712: 6956}, - {533: 6957}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6958}, - {2284, 2284, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - // 4105 - {533: 6960}, - {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6961}, - {2285, 2285, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6963, 3051, 3052, 3050}, - {2286, 2286}, - // 4110 - {2287, 2287}, - {2306, 2306, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6998}, - {2304, 2304}, - {28: 6996}, - {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 10: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 58: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 555: 6985, 716: 2024}, - // 4115 - {232: 6971, 531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6970}, - {2298, 2298}, - {555: 6972}, - {172: 6976, 282: 6979, 301: 6978, 346: 6982, 358: 6975, 6981, 361: 6980, 533: 6974, 639: 6977, 1170: 6973}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6984}, - // 4120 - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6983}, - {531: 2295, 2295, 547: 2295, 552: 2295, 558: 2295, 587: 2295, 608: 2295, 695: 2295, 714: 2295, 724: 2295, 803: 2295}, - {531: 2294, 2294, 547: 2294, 552: 2294, 558: 2294, 587: 2294, 608: 2294, 695: 2294, 714: 2294, 724: 2294, 803: 2294}, - {531: 2293, 2293, 547: 2293, 552: 2293, 558: 2293, 587: 2293, 608: 2293, 695: 2293, 714: 2293, 724: 2293, 803: 2293}, - {531: 2292, 2292, 547: 2292, 552: 2292, 558: 2292, 587: 2292, 608: 2292, 695: 2292, 714: 2292, 724: 2292, 803: 2292}, - // 4125 - {531: 2291, 2291, 547: 2291, 552: 2291, 558: 2291, 587: 2291, 608: 2291, 695: 2291, 714: 2291, 724: 2291, 803: 2291}, - {531: 2290, 2290, 547: 2290, 552: 2290, 558: 2290, 587: 2290, 608: 2290, 695: 2290, 714: 2290, 724: 2290, 803: 2290}, - {531: 2289, 2289, 547: 2289, 552: 2289, 558: 2289, 587: 2289, 608: 2289, 695: 2289, 714: 2289, 724: 2289, 803: 2289}, - {531: 2288, 2288, 547: 2288, 552: 2288, 558: 2288, 587: 2288, 608: 2288, 695: 2288, 714: 2288, 724: 2288, 803: 2288}, - {2296, 2296}, - // 4130 - {2297, 2297}, - {172: 6976, 282: 6979, 301: 6978, 346: 6982, 358: 6975, 6981, 361: 6980, 533: 6986, 639: 6977, 1170: 6987}, - {531: 2907, 2906, 547: 2905, 552: 2891, 558: 6992, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6993}, - {531: 2907, 2906, 547: 2905, 552: 2891, 558: 6988, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6989}, - {28: 6990}, - // 4135 - {2299, 2299}, - {560: 3037, 799: 6991}, - {2300, 2300}, - {28: 6994}, - {2301, 2301}, - // 4140 - {560: 3037, 799: 6995}, - {2302, 2302}, - {560: 3037, 799: 6997}, - {2303, 2303}, - {2305, 2305}, - // 4145 - {2313, 2313}, - {555: 7025}, - {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 446: 7021, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7020}, - {646, 646, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {648, 648, 539: 1014, 550: 1014, 1014}, - // 4150 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 6265, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 6266, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 6262, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6268, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 6261, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 6271, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6269, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6279, 1302: 6272}, - {653, 653}, - {652, 652}, - {651, 651}, - {650, 650}, - // 4155 - {649, 649}, - {647, 647}, - {645, 645}, - {644, 644}, - {643, 643}, - // 4160 - {642, 642}, - {641, 641}, - {640, 640}, - {639, 639}, - {638, 638}, - // 4165 - {22: 5771}, - {2311, 2311}, - {555: 7022}, - {533: 7023}, - {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7024}, - // 4170 - {2310, 2310}, - {533: 7026}, - {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7027}, - {2312, 2312}, - {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 561: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 7029}, - // 4175 - {2: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 10: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 58: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 6342, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 549: 1244, 561: 1244, 1247: 7030}, - {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 561: 2115, 958: 7031}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 561: 7032, 770: 6346, 3051, 3052, 3050, 1018: 6347, 1084: 6345}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7034, 3051, 3052, 3050, 804: 6359, 1018: 6347, 1084: 7033}, - {9: 6355, 542: 7037}, - // 4180 - {1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1246, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 58: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 537: 1254, 542: 1246, 546: 1254, 549: 1254, 556: 1254, 563: 1254, 1254, 1254, 575: 1254, 716: 7035, 1008: 6348}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6351, 770: 7036, 3051, 3052, 3050}, - {1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1246, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 58: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 537: 1253, 542: 1246, 546: 1253, 549: 1253, 556: 1253, 563: 1253, 1253, 1253, 575: 1253, 716: 6353, 1008: 6352}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 7038}, - {424, 424, 9: 6019, 563: 4661, 889: 4662, 7039}, - // 4185 - {2343, 2343}, - {2346, 2346, 9: 3990}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7117, 3051, 3052, 3050}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7115}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7106}, - // 4190 - {710: 7101}, - {157: 6096, 608: 6095, 1294: 7097}, - {202: 789, 216: 6152}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 578: 7092, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7091}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 578: 7088, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 7087}, - // 4195 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 578: 7084, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 7083}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7079, 884: 7078}, - {22: 7075}, - {202: 7067}, - {214: 7064}, - // 4200 - {571: 7061}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7059}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7057}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7058}, - {29, 29}, - // 4205 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7060}, - {165, 165, 9: 3930}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 535: 2119, 578: 4948, 854: 7062}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7063}, - {192, 192}, - // 4210 - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7065}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7066}, - {195, 195}, - {558: 7068}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 7070, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7069}, - // 4215 - {343, 343, 542: 7073}, - {218: 7071}, - {533: 7072}, - {341, 341}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7074}, - // 4220 - {342, 342}, - {178: 7076}, - {560: 3037, 799: 4538, 825: 7077}, - {2214, 2214}, - {2324, 2324, 9: 3930}, - // 4225 - {1252, 1252, 9: 1252, 206: 7081, 546: 7080}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7082}, - {2322, 2322}, - {2323, 2323, 9: 5256}, - {2326, 2326, 9: 6304}, - // 4230 - {645: 7085}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 7086}, - {2325, 2325, 9: 6304}, - {2328, 2328, 9: 5856}, - {645: 7089}, - // 4235 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 7090}, - {2327, 2327, 9: 5856}, - {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7096}, - {645: 7093}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7094}, - // 4240 - {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7095}, - {2329, 2329}, - {2330, 2330}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7098}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7099}, - // 4245 - {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7100}, - {2334, 2334}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7102}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7103, 3051, 3052, 3050}, - {530: 7104}, - // 4250 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7105}, - {2335, 2335}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7107, 3051, 3052, 3050}, - {530: 7108}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7109}, - // 4255 - {2477, 2477, 102: 4719, 562: 4720, 972: 7111, 986: 7110, 1193: 7112}, - {2476, 2476, 102: 4719, 972: 7114}, - {2475, 2475, 562: 4720, 986: 7113}, - {2336, 2336}, - {2473, 2473}, - // 4260 - {2474, 2474}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7116}, - {2337, 2337}, - {2485, 2485}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7589}, - // 4265 - {710: 7577}, - {710: 2471}, - {710: 2470}, - {710: 2469}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7554}, - // 4270 - {18: 7472, 102: 7471, 132: 2363, 181: 2363, 696: 2363, 1492: 7470}, - {552: 7469}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 533: 2117, 578: 5345, 631: 2117, 883: 7417}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 533: 2117, 578: 5345, 883: 7411}, - {202: 7400}, - // 4275 - {571: 7341}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7305}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7132}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7133}, - {531: 7134}, - // 4280 - {2: 128, 128, 128, 128, 128, 128, 128, 10: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 133, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 606: 7138, 1194: 7140, 1228: 7139, 1279: 7137, 7136, 1406: 7141, 1463: 7135}, - {9: 7303, 57: 132}, - {9: 130, 57: 130}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7301, 3051, 3052, 3050}, - {2: 127, 127, 127, 127, 127, 127, 127, 10: 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 58: 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}, - // 4285 - {2: 126, 126, 126, 126, 126, 126, 126, 10: 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 58: 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126}, - {2: 125, 125, 125, 125, 125, 125, 125, 10: 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 58: 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125}, - {57: 7142}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7185, 7165, 7164, 7173, 7174, 7177}, - {122, 122, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - // 4290 - {124, 124, 539: 1014, 550: 1014, 1014}, - {123, 123}, - {121, 121}, - {120, 120}, - {119, 119}, - // 4295 - {118, 118}, - {117, 117}, - {116, 116}, - {115, 115}, - {114, 114}, - // 4300 - {113, 113}, - {112, 112}, - {111, 111}, - {110, 110}, - {105, 105}, - // 4305 - {56: 7300}, - {56: 82, 265: 7291, 561: 7292, 1433: 7290}, - {56: 7289}, - {56: 77, 84: 77, 77, 87: 77, 89: 77, 92: 77, 94: 77, 97: 77, 230: 7242, 531: 77, 77, 547: 77, 552: 77, 554: 77, 557: 77, 575: 77, 577: 77, 77, 582: 77, 587: 77, 608: 77, 614: 77, 617: 77, 695: 77, 713: 77, 77, 803: 77, 828: 77, 831: 77, 834: 77, 77, 1244: 7244, 1427: 7243, 7245}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7231, 1246: 7232}, - // 4310 - {63, 63}, - {62, 62}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 589: 7211, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7208, 1265: 7209, 1445: 7210}, - {51, 51}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7203}, - // 4315 - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7195}, - {1310: 7188}, - {56: 7187}, - {56: 7186}, - {42, 42}, - // 4320 - {41, 41}, - {40, 40}, - {39, 39}, - {38, 38}, - {37, 37}, - // 4325 - {36, 36}, - {35, 35}, - {34, 34}, - {33, 33}, - {32, 32}, - // 4330 - {31, 31}, - {30, 30}, - {43, 43}, - {44, 44}, - {84: 7162, 617: 7169, 831: 7168, 866: 7189, 7190}, - // 4335 - {47, 47, 56: 7191, 1243: 7193}, - {47, 47, 56: 7191, 1243: 7192}, - {46, 46}, - {45, 45}, - {48, 48}, - // 4340 - {7202}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177, 1091: 7197}, - {7201}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7198}, - {97: 7199, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 4345 - {617: 7200}, - {49, 49, 56: 49}, - {56: 70, 84: 70, 70, 87: 70, 89: 70, 92: 70, 94: 70, 97: 70, 531: 70, 70, 547: 70, 552: 70, 554: 70, 557: 70, 575: 70, 577: 70, 70, 582: 70, 587: 70, 589: 70, 70, 608: 70, 614: 70, 617: 70, 695: 70, 713: 70, 70, 803: 70, 828: 70, 831: 70, 834: 70, 70, 1043: 70, 1091: 70}, - {56: 71, 84: 71, 71, 87: 71, 89: 71, 92: 71, 94: 71, 97: 71, 531: 71, 71, 547: 71, 552: 71, 554: 71, 557: 71, 575: 71, 577: 71, 71, 582: 71, 587: 71, 589: 71, 71, 608: 71, 614: 71, 617: 71, 695: 71, 713: 71, 71, 803: 71, 828: 71, 831: 71, 834: 71, 71, 1043: 71, 1091: 71}, - {254: 7204, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - // 4350 - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7205}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 7206, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, - {831: 7207}, - {50, 50, 56: 50}, - {566: 3745, 3743, 3744, 3742, 3740, 589: 7223, 800: 3741, 3739, 1276: 7221, 1460: 7222}, - // 4355 - {97: 59, 589: 59, 59}, - {97: 55, 589: 7211, 7216, 1165: 7217, 1265: 7215}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7212}, - {566: 3745, 3743, 3744, 3742, 3740, 607: 7213, 800: 3741, 3739}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7214}, - // 4360 - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 56, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 589: 56, 56, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, - {97: 58, 589: 58, 58}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7220}, - {97: 7218}, - {614: 7219}, - // 4365 - {52, 52}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 54, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, - {97: 61, 589: 61, 61}, - {97: 55, 589: 7223, 7216, 1165: 7228, 1276: 7227}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7224}, - // 4370 - {566: 3745, 3743, 3744, 3742, 3740, 607: 7225, 800: 3741, 3739}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7226}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 57, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 589: 57, 57, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, - {97: 60, 589: 60, 60}, - {97: 7229}, - // 4375 - {614: 7230}, - {53, 53}, - {566: 3745, 3743, 3744, 3742, 3740, 607: 7235, 800: 3741, 3739}, - {97: 7233}, - {578: 7234}, - // 4380 - {68, 68}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7236}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 66, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 590: 7239, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177, 1043: 7238, 1423: 7237}, - {97: 67}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7231, 1246: 7241}, - // 4385 - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7240}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 64, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, - {97: 65}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 7253, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7254, 3051, 3052, 3050, 1341: 7257, 1355: 7258, 1426: 7255, 1430: 7256}, - {56: 76, 84: 76, 76, 87: 76, 89: 76, 92: 76, 94: 76, 97: 76, 230: 7242, 531: 76, 76, 547: 76, 552: 76, 554: 76, 557: 76, 575: 76, 577: 76, 76, 582: 76, 587: 76, 608: 76, 614: 76, 617: 76, 695: 76, 713: 76, 76, 803: 76, 828: 76, 831: 76, 834: 76, 76, 1244: 7251}, - // 4390 - {7250}, - {56: 73, 84: 73, 73, 87: 73, 89: 73, 92: 73, 94: 73, 97: 73, 531: 73, 73, 547: 73, 552: 73, 554: 73, 557: 73, 575: 73, 577: 73, 73, 582: 73, 587: 73, 608: 73, 614: 73, 617: 73, 695: 73, 713: 73, 73, 803: 73, 828: 73, 831: 73, 834: 73, 73, 1434: 7246}, - {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 7248, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7247, 7165, 7164, 7173, 7174, 7177}, - {7249}, - {69, 69, 56: 69}, - // 4395 - {56: 72, 84: 72, 72, 87: 72, 89: 72, 92: 72, 94: 72, 97: 72, 531: 72, 72, 547: 72, 552: 72, 554: 72, 557: 72, 575: 72, 577: 72, 72, 582: 72, 587: 72, 608: 72, 614: 72, 617: 72, 695: 72, 713: 72, 72, 803: 72, 828: 72, 831: 72, 834: 72, 72}, - {56: 75, 84: 75, 75, 87: 75, 89: 75, 92: 75, 94: 75, 97: 75, 230: 75, 531: 75, 75, 547: 75, 552: 75, 554: 75, 557: 75, 575: 75, 577: 75, 75, 582: 75, 587: 75, 608: 75, 614: 75, 617: 75, 695: 75, 713: 75, 75, 803: 75, 828: 75, 831: 75, 834: 75, 75}, - {7252}, - {56: 74, 84: 74, 74, 87: 74, 89: 74, 92: 74, 94: 74, 97: 74, 230: 74, 531: 74, 74, 547: 74, 552: 74, 554: 74, 557: 74, 575: 74, 577: 74, 74, 582: 74, 587: 74, 608: 74, 614: 74, 617: 74, 695: 74, 713: 74, 74, 803: 74, 828: 74, 831: 74, 834: 74, 74}, - {9: 2090, 118: 2090, 127: 2090, 172: 2090, 175: 2090, 2090, 2090, 179: 2090, 185: 2090, 188: 2090, 198: 2090, 203: 2090, 2090, 2090, 209: 2090, 2090, 212: 2090, 553: 2090, 557: 2090, 586: 2090, 709: 2090, 732: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 744: 2090, 2090, 748: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 1345: 7282}, - // 4400 - {9: 104, 118: 104, 127: 104, 172: 104, 175: 104, 104, 104, 179: 104, 185: 104, 188: 104, 198: 104, 203: 104, 104, 104, 209: 104, 104, 212: 104, 553: 104, 557: 104, 586: 104, 709: 104, 732: 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 744: 104, 104, 748: 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104}, - {9: 7276, 118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 7277, 1098: 5019}, - {261: 7259}, - {261: 97}, - {261: 96}, - // 4405 - {558: 7260}, - {536: 7265, 560: 3037, 799: 7267, 1242: 7263, 1245: 7262, 1281: 7266, 7268, 7264, 1431: 7261}, - {9: 7274, 56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7273, 7165, 7164, 7173, 7174, 7177}, - {9: 95, 56: 95, 84: 95, 95, 87: 95, 89: 95, 92: 95, 94: 95, 531: 95, 95, 547: 95, 552: 95, 554: 95, 557: 95, 575: 95, 577: 95, 95, 582: 95, 587: 95, 608: 95, 614: 95, 617: 95, 695: 95, 713: 95, 95, 803: 95, 828: 95, 831: 95, 834: 95, 95}, - {9: 93, 56: 93, 84: 93, 93, 87: 93, 89: 93, 92: 93, 94: 93, 531: 93, 93, 547: 93, 552: 93, 554: 93, 557: 93, 575: 93, 577: 93, 93, 582: 93, 587: 93, 608: 93, 614: 93, 617: 93, 695: 93, 713: 93, 93, 803: 93, 828: 93, 831: 93, 834: 93, 93}, - // 4410 - {9: 92, 56: 92, 84: 92, 92, 87: 92, 89: 92, 92: 92, 94: 92, 531: 92, 92, 547: 92, 552: 92, 554: 92, 557: 92, 575: 92, 577: 92, 92, 582: 92, 587: 92, 608: 92, 614: 92, 617: 92, 695: 92, 713: 92, 92, 803: 92, 828: 92, 831: 92, 834: 92, 92}, - {393: 7272}, - {9: 90, 56: 90, 84: 90, 90, 87: 90, 89: 90, 92: 90, 94: 90, 531: 90, 90, 547: 90, 552: 90, 554: 90, 557: 90, 575: 90, 577: 90, 90, 582: 90, 587: 90, 608: 90, 614: 90, 617: 90, 695: 90, 713: 90, 90, 803: 90, 828: 90, 831: 90, 834: 90, 90}, - {9: 89, 56: 89, 84: 89, 89, 87: 89, 89: 89, 92: 89, 94: 89, 531: 89, 89, 547: 89, 552: 89, 554: 89, 557: 89, 575: 89, 577: 89, 89, 582: 89, 587: 89, 608: 89, 614: 89, 617: 89, 695: 89, 713: 89, 89, 803: 89, 828: 89, 831: 89, 834: 89, 89}, - {186: 7270, 533: 87, 1408: 7269}, - // 4415 - {533: 7271}, - {533: 86}, - {9: 88, 56: 88, 84: 88, 88, 87: 88, 89: 88, 92: 88, 94: 88, 531: 88, 88, 547: 88, 552: 88, 554: 88, 557: 88, 575: 88, 577: 88, 88, 582: 88, 587: 88, 608: 88, 614: 88, 617: 88, 695: 88, 713: 88, 88, 803: 88, 828: 88, 831: 88, 834: 88, 88}, - {9: 91, 56: 91, 84: 91, 91, 87: 91, 89: 91, 92: 91, 94: 91, 531: 91, 91, 547: 91, 552: 91, 554: 91, 557: 91, 575: 91, 577: 91, 91, 582: 91, 587: 91, 608: 91, 614: 91, 617: 91, 695: 91, 713: 91, 91, 803: 91, 828: 91, 831: 91, 834: 91, 91}, - {98}, - // 4420 - {536: 7265, 560: 3037, 799: 7267, 1242: 7263, 1245: 7275, 1281: 7266, 7268, 7264}, - {9: 94, 56: 94, 84: 94, 94, 87: 94, 89: 94, 92: 94, 94: 94, 531: 94, 94, 547: 94, 552: 94, 554: 94, 557: 94, 575: 94, 577: 94, 94, 582: 94, 587: 94, 608: 94, 614: 94, 617: 94, 695: 94, 713: 94, 94, 803: 94, 828: 94, 831: 94, 834: 94, 94}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7281, 3051, 3052, 3050}, - {102, 535: 7278, 1432: 7279}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7280}, - // 4425 - {100}, - {101, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, - {9: 103, 118: 103, 127: 103, 172: 103, 175: 103, 103, 103, 179: 103, 185: 103, 188: 103, 198: 103, 203: 103, 103, 103, 209: 103, 103, 212: 103, 553: 103, 557: 103, 586: 103, 709: 103, 732: 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 744: 103, 103, 748: 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103}, - {558: 7283}, - {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 7284, 806: 7285, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 7286, 7287, 1425: 7288}, - // 4430 - {107, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {109, 539: 1014, 550: 1014, 1014}, - {108}, - {106}, - {99}, - // 4435 - {83, 83}, - {56: 7294}, - {561: 7293}, - {56: 80}, - {56: 81}, - // 4440 - {559: 7295}, - {56: 7297, 1429: 7296}, - {84, 84, 9: 7298}, - {79, 79, 9: 79}, - {56: 7299}, - // 4445 - {78, 78, 9: 78}, - {85, 85}, - {118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 7302, 1098: 5019}, - {9: 129, 57: 129}, - {2: 128, 128, 128, 128, 128, 128, 128, 10: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 58: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 606: 7138, 1194: 7140, 1228: 7139, 1279: 7137, 7304}, - // 4450 - {9: 131, 57: 131}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7306}, - {188, 188, 6: 188, 188, 188, 15: 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 535: 188, 538: 188, 188, 553: 188, 565: 188, 709: 188, 188, 720: 7315, 1014: 7309, 1342: 7307, 1449: 7308}, - {576, 576, 6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 1020: 6174, 1141: 7340}, - {187, 187, 6: 187, 187, 187, 15: 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 535: 187, 538: 187, 187, 553: 187, 565: 187, 709: 187, 187, 720: 7315, 1014: 7339}, - // 4455 - {186, 186, 6: 186, 186, 186, 15: 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 86: 186, 88: 186, 90: 186, 186, 95: 186, 186, 98: 186, 186, 186, 186, 535: 186, 538: 186, 186, 553: 186, 565: 186, 709: 186, 186, 720: 186}, - {543: 2316, 2316, 555: 4588, 560: 2316, 723: 7336, 802: 7335}, - {532: 7332, 543: 2316, 2316, 555: 4588, 560: 2316, 802: 7331}, - {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7329}, - {179, 179, 6: 179, 179, 179, 15: 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 86: 179, 88: 179, 90: 179, 179, 95: 179, 179, 98: 179, 179, 179, 179, 103: 179, 535: 179, 538: 179, 179, 553: 179, 565: 179, 709: 179, 179, 720: 179}, - // 4460 - {90: 7327, 95: 7328, 7325, 720: 7326}, - {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7323}, - {176, 176, 6: 176, 176, 176, 15: 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 86: 176, 88: 176, 90: 176, 176, 95: 176, 176, 98: 176, 176, 176, 176, 103: 176, 535: 176, 538: 176, 176, 553: 176, 565: 176, 709: 176, 176, 720: 176}, - {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7321}, - {173, 173, 6: 173, 173, 173, 15: 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 86: 173, 88: 173, 90: 173, 173, 95: 173, 173, 98: 173, 173, 173, 173, 103: 173, 535: 173, 538: 173, 173, 553: 173, 565: 173, 709: 173, 173, 720: 173}, - // 4465 - {171, 171, 6: 171, 171, 171, 15: 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 86: 171, 88: 171, 90: 171, 171, 95: 171, 171, 98: 171, 171, 171, 171, 103: 171, 535: 171, 538: 171, 171, 553: 171, 565: 171, 709: 171, 171, 720: 171}, - {170, 170, 6: 170, 170, 170, 15: 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 86: 170, 88: 170, 90: 170, 170, 95: 170, 170, 98: 170, 170, 170, 170, 103: 170, 535: 170, 538: 170, 170, 553: 170, 565: 170, 709: 170, 170, 720: 170}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7322}, - {174, 174, 6: 174, 174, 174, 15: 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 86: 174, 88: 174, 90: 174, 174, 95: 174, 174, 98: 174, 174, 174, 174, 103: 174, 535: 174, 538: 174, 174, 553: 174, 565: 174, 709: 174, 174, 720: 174}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7324}, - // 4470 - {177, 177, 6: 177, 177, 177, 15: 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 86: 177, 88: 177, 90: 177, 177, 95: 177, 177, 98: 177, 177, 177, 177, 103: 177, 535: 177, 538: 177, 177, 553: 177, 565: 177, 709: 177, 177, 720: 177}, - {178, 178, 6: 178, 178, 178, 15: 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 86: 178, 88: 178, 90: 178, 178, 95: 178, 178, 98: 178, 178, 178, 178, 103: 178, 535: 178, 538: 178, 178, 553: 178, 565: 178, 709: 178, 178, 720: 178}, - {175, 175, 6: 175, 175, 175, 15: 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 86: 175, 88: 175, 90: 175, 175, 95: 175, 175, 98: 175, 175, 175, 175, 103: 175, 535: 175, 538: 175, 175, 553: 175, 565: 175, 709: 175, 175, 720: 175}, - {172, 172, 6: 172, 172, 172, 15: 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 86: 172, 88: 172, 90: 172, 172, 95: 172, 172, 98: 172, 172, 172, 172, 103: 172, 535: 172, 538: 172, 172, 553: 172, 565: 172, 709: 172, 172, 720: 172}, - {169, 169, 6: 169, 169, 169, 15: 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 86: 169, 88: 169, 90: 169, 169, 95: 169, 169, 98: 169, 169, 169, 169, 103: 169, 535: 169, 538: 169, 169, 553: 169, 565: 169, 709: 169, 169, 720: 169}, - // 4475 - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7330}, - {180, 180, 6: 180, 180, 180, 15: 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 86: 180, 88: 180, 90: 180, 180, 95: 180, 180, 98: 180, 180, 180, 180, 103: 180, 535: 180, 538: 180, 180, 553: 180, 565: 180, 709: 180, 180, 720: 180}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7334}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7333}, - {181, 181, 6: 181, 181, 181, 15: 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 86: 181, 88: 181, 90: 181, 181, 95: 181, 181, 98: 181, 181, 181, 181, 103: 181, 535: 181, 538: 181, 181, 553: 181, 565: 181, 709: 181, 181, 720: 181}, - // 4480 - {182, 182, 6: 182, 182, 182, 15: 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 86: 182, 88: 182, 90: 182, 182, 95: 182, 182, 98: 182, 182, 182, 182, 103: 182, 535: 182, 538: 182, 182, 553: 182, 565: 182, 709: 182, 182, 720: 182}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7338}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7337}, - {183, 183, 6: 183, 183, 183, 15: 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 86: 183, 88: 183, 90: 183, 183, 95: 183, 183, 98: 183, 183, 183, 183, 103: 183, 535: 183, 538: 183, 183, 553: 183, 565: 183, 709: 183, 183, 720: 183}, - {184, 184, 6: 184, 184, 184, 15: 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 86: 184, 88: 184, 90: 184, 184, 95: 184, 184, 98: 184, 184, 184, 184, 103: 184, 535: 184, 538: 184, 184, 553: 184, 565: 184, 709: 184, 184, 720: 184}, - // 4485 - {185, 185, 6: 185, 185, 185, 15: 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 86: 185, 88: 185, 90: 185, 185, 95: 185, 185, 98: 185, 185, 185, 185, 535: 185, 538: 185, 185, 553: 185, 565: 185, 709: 185, 185, 720: 185}, - {189, 189}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 535: 2117, 578: 5345, 883: 7342}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7343}, - {110: 7350, 7348, 7347, 7349, 7346, 977: 7344, 1255: 7345}, - // 4490 - {2845, 2845, 9: 2845, 110: 2845, 2845, 2845, 2845, 2845}, - {194, 194, 9: 7398, 110: 7350, 7348, 7347, 7349, 7346, 977: 7397}, - {555: 4588, 560: 2316, 802: 7395}, - {311: 2316, 321: 2316, 2316, 555: 4588, 802: 7390}, - {2822, 2822, 9: 2822, 110: 2822, 2822, 2822, 2822, 2822, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 7388}, - // 4495 - {531: 2316, 548: 2316, 555: 4588, 802: 7364}, - {531: 2316, 548: 2316, 555: 4588, 802: 7351}, - {531: 7352, 548: 7353}, - {57: 7355, 197: 7357, 1040: 7356, 1440: 7354}, - {2815, 2815, 9: 2815, 110: 2815, 2815, 2815, 2815, 2815}, - // 4500 - {9: 7362, 57: 7360, 197: 7357, 1040: 7361}, - {2816, 2816, 9: 2816, 110: 2816, 2816, 2816, 2816, 2816}, - {9: 2814, 57: 2814, 197: 2814}, - {533: 2316, 555: 4588, 802: 7358}, - {533: 7359}, - // 4505 - {9: 2811, 57: 2811, 197: 2811}, - {2817, 2817, 9: 2817, 110: 2817, 2817, 2817, 2817, 2817}, - {9: 2813, 57: 2813, 197: 2813}, - {197: 7357, 1040: 7363}, - {9: 2812, 57: 2812, 197: 2812}, - // 4510 - {531: 7365, 548: 7366}, - {57: 7372, 93: 7370, 134: 7371, 136: 7369, 1041: 7367, 1442: 7368}, - {2818, 2818, 9: 2818, 110: 2818, 2818, 2818, 2818, 2818}, - {9: 2839, 57: 2839, 93: 2839, 134: 2839, 136: 2839}, - {9: 7385, 57: 7386, 93: 7370, 134: 7371, 136: 7369, 1041: 7384}, - // 4515 - {533: 2316, 555: 4588, 802: 7382}, - {229: 2316, 231: 2316, 555: 4588, 802: 7380, 933: 2316}, - {117: 2316, 258: 2316, 272: 2316, 555: 4588, 802: 7373}, - {2819, 2819, 9: 2819, 110: 2819, 2819, 2819, 2819, 2819}, - {117: 4583, 258: 4581, 272: 4582, 1257: 7374}, - // 4520 - {9: 2827, 57: 2827, 93: 2827, 134: 2827, 136: 2827, 158: 7376, 1500: 7375}, - {9: 2828, 57: 2828, 93: 2828, 134: 2828, 136: 2828}, - {360: 2316, 533: 2316, 555: 4588, 802: 7377}, - {360: 7379, 533: 7378}, - {9: 2826, 57: 2826, 93: 2826, 134: 2826, 136: 2826}, - // 4525 - {9: 2825, 57: 2825, 93: 2825, 134: 2825, 136: 2825}, - {229: 4591, 231: 4590, 933: 4592, 1256: 7381}, - {9: 2829, 57: 2829, 93: 2829, 134: 2829, 136: 2829}, - {533: 7383}, - {9: 2830, 57: 2830, 93: 2830, 134: 2830, 136: 2830}, - // 4530 - {9: 2838, 57: 2838, 93: 2838, 134: 2838, 136: 2838}, - {93: 7370, 134: 7371, 136: 7369, 1041: 7387}, - {2820, 2820, 9: 2820, 110: 2820, 2820, 2820, 2820, 2820}, - {9: 2837, 57: 2837, 93: 2837, 134: 2837, 136: 2837}, - {560: 3037, 629: 6710, 6711, 799: 6709, 998: 7389}, - // 4535 - {2821, 2821, 9: 2821, 110: 2821, 2821, 2821, 2821, 2821}, - {311: 7393, 321: 7391, 7392, 1441: 7394}, - {2842, 2842, 9: 2842, 110: 2842, 2842, 2842, 2842, 2842}, - {2841, 2841, 9: 2841, 110: 2841, 2841, 2841, 2841, 2841}, - {2840, 2840, 9: 2840, 110: 2840, 2840, 2840, 2840, 2840}, - // 4540 - {2823, 2823, 9: 2823, 110: 2823, 2823, 2823, 2823, 2823}, - {560: 3037, 799: 3866, 816: 7396}, - {2824, 2824, 9: 2824, 110: 2824, 2824, 2824, 2824, 2824}, - {2844, 2844, 9: 2844, 110: 2844, 2844, 2844, 2844, 2844}, - {110: 7350, 7348, 7347, 7349, 7346, 977: 7399}, - // 4545 - {2843, 2843, 9: 2843, 110: 2843, 2843, 2843, 2843, 2843}, - {558: 7401, 561: 7402}, - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7408}, - {262: 7403}, - {542: 7404}, - // 4550 - {117: 7405}, - {218: 7406}, - {533: 7407}, - {344, 344}, - {542: 7409}, - // 4555 - {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7410}, - {345, 345}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 7413, 6301, 1264: 7414, 1443: 7412}, - {419, 419, 9: 7415}, - {356, 356, 9: 356}, - // 4560 - {355, 355, 9: 355}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 7413, 6301, 1264: 7416}, - {354, 354, 9: 354}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 7418}, - {401, 401, 6: 401, 9: 5873, 15: 401, 51: 401, 401, 401, 401, 401, 532: 401, 725: 5917, 1075: 5916, 7419}, - // 4565 - {409, 409, 6: 409, 15: 409, 51: 409, 409, 409, 409, 409, 532: 7421, 1131: 7420}, - {382, 382, 6: 382, 15: 7437, 51: 382, 382, 7436, 7438, 7439, 1068: 7435, 1235: 7434, 7433}, - {163: 7426, 7424, 7425, 7427, 1130: 7423, 1339: 7422}, - {408, 408, 6: 408, 15: 408, 51: 408, 408, 408, 408, 408, 163: 7426, 7424, 7425, 7427, 1130: 7432}, - {407, 407, 6: 407, 15: 407, 51: 407, 407, 407, 407, 407, 163: 407, 407, 407, 407}, - // 4570 - {560: 3037, 799: 4538, 825: 7431}, - {560: 3037, 799: 4538, 825: 7430}, - {560: 3037, 799: 4538, 825: 7429}, - {560: 3037, 799: 4538, 825: 7428}, - {402, 402, 6: 402, 15: 402, 51: 402, 402, 402, 402, 402, 163: 402, 402, 402, 402}, - // 4575 - {403, 403, 6: 403, 15: 403, 51: 403, 403, 403, 403, 403, 163: 403, 403, 403, 403}, - {404, 404, 6: 404, 15: 404, 51: 404, 404, 404, 404, 404, 163: 404, 404, 404, 404}, - {405, 405, 6: 405, 15: 405, 51: 405, 405, 405, 405, 405, 163: 405, 405, 405, 405}, - {406, 406, 6: 406, 15: 406, 51: 406, 406, 406, 406, 406, 163: 406, 406, 406, 406}, - {387, 387, 6: 7461, 51: 387, 7462, 1128: 7460}, - // 4580 - {381, 381, 6: 381, 15: 7437, 51: 381, 381, 7436, 7438, 7439, 1068: 7459}, - {380, 380, 6: 380, 15: 380, 51: 380, 380, 380, 380, 380}, - {562: 7458, 1090: 7457}, - {262: 7443, 389: 7445, 431: 7444}, - {560: 3037, 799: 4538, 825: 7442}, - // 4585 - {201: 7441, 560: 3037, 799: 4538, 825: 7440}, - {367, 367, 6: 367, 15: 367, 51: 367, 367, 367, 367, 367}, - {366, 366, 6: 366, 15: 366, 51: 366, 366, 366, 366, 366}, - {368, 368, 6: 368, 15: 368, 51: 368, 368, 368, 368, 368}, - {535: 7455, 560: 3037, 799: 7456}, - // 4590 - {641: 7451}, - {372, 372, 6: 372, 15: 372, 51: 372, 372, 372, 372, 372, 407: 7447, 535: 7448, 641: 7446}, - {560: 3037, 799: 4538, 825: 7449}, - {370, 370, 6: 370, 15: 370, 51: 370, 370, 370, 370, 370}, - {369, 369, 6: 369, 15: 369, 51: 369, 369, 369, 369, 369}, - // 4595 - {133: 7450}, - {371, 371, 6: 371, 15: 371, 51: 371, 371, 371, 371, 371}, - {535: 7452, 560: 3037, 799: 7453}, - {374, 374, 6: 374, 15: 374, 51: 374, 374, 374, 374, 374}, - {133: 7454}, - // 4600 - {373, 373, 6: 373, 15: 373, 51: 373, 373, 373, 373, 373}, - {376, 376, 6: 376, 15: 376, 51: 376, 376, 376, 376, 376}, - {375, 375, 6: 375, 15: 375, 51: 375, 375, 375, 375, 375}, - {378, 378, 6: 378, 15: 378, 51: 378, 378, 378, 378, 378}, - {377, 377, 6: 377, 15: 377, 51: 377, 377, 377, 377, 377}, - // 4605 - {379, 379, 6: 379, 15: 379, 51: 379, 379, 379, 379, 379}, - {384, 384, 51: 7466, 1254: 7465}, - {533: 7464}, - {533: 7463}, - {385, 385, 51: 385}, - // 4610 - {386, 386, 51: 386}, - {420, 420}, - {571: 7467}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7468}, - {383, 383}, - // 4615 - {18: 2364, 102: 2364, 132: 2364, 181: 2364, 696: 2364}, - {132: 2359, 181: 7522, 696: 2359, 1494: 7521}, - {555: 7517}, - {214: 7473}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7474}, - // 4620 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7475}, - {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7476, 1238: 7477}, - {2810, 2810, 9: 2810, 108: 2810, 119: 2810, 2810, 2810, 2810, 2810, 2810, 2810, 2810, 128: 2810, 2810, 2810}, - {191, 191, 9: 7515, 108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7514}, - {533: 2316, 555: 4588, 802: 7512}, - // 4625 - {533: 2316, 555: 4588, 802: 7510}, - {555: 4588, 560: 2316, 802: 7508}, - {555: 4588, 560: 2316, 802: 7506}, - {555: 4588, 560: 2316, 802: 7504}, - {533: 2316, 555: 4588, 802: 7502}, - // 4630 - {533: 2316, 555: 4588, 802: 7500}, - {533: 2316, 555: 4588, 802: 7498}, - {533: 2316, 555: 4588, 802: 7496}, - {533: 2316, 555: 4588, 802: 7494}, - {533: 2316, 555: 4588, 802: 7492}, - // 4635 - {533: 2316, 555: 4588, 802: 7490}, - {533: 7491}, - {2796, 2796, 9: 2796, 108: 2796, 119: 2796, 2796, 2796, 2796, 2796, 2796, 2796, 2796, 128: 2796, 2796, 2796}, - {533: 7493}, - {2797, 2797, 9: 2797, 108: 2797, 119: 2797, 2797, 2797, 2797, 2797, 2797, 2797, 2797, 128: 2797, 2797, 2797}, - // 4640 - {533: 7495}, - {2798, 2798, 9: 2798, 108: 2798, 119: 2798, 2798, 2798, 2798, 2798, 2798, 2798, 2798, 128: 2798, 2798, 2798}, - {533: 7497}, - {2799, 2799, 9: 2799, 108: 2799, 119: 2799, 2799, 2799, 2799, 2799, 2799, 2799, 2799, 128: 2799, 2799, 2799}, - {533: 7499}, - // 4645 - {2800, 2800, 9: 2800, 108: 2800, 119: 2800, 2800, 2800, 2800, 2800, 2800, 2800, 2800, 128: 2800, 2800, 2800}, - {533: 7501}, - {2801, 2801, 9: 2801, 108: 2801, 119: 2801, 2801, 2801, 2801, 2801, 2801, 2801, 2801, 128: 2801, 2801, 2801}, - {533: 7503}, - {2802, 2802, 9: 2802, 108: 2802, 119: 2802, 2802, 2802, 2802, 2802, 2802, 2802, 2802, 128: 2802, 2802, 2802}, - // 4650 - {560: 3037, 799: 3866, 816: 7505}, - {2803, 2803, 9: 2803, 108: 2803, 119: 2803, 2803, 2803, 2803, 2803, 2803, 2803, 2803, 128: 2803, 2803, 2803}, - {560: 3037, 799: 3866, 816: 7507}, - {2804, 2804, 9: 2804, 108: 2804, 119: 2804, 2804, 2804, 2804, 2804, 2804, 2804, 2804, 128: 2804, 2804, 2804}, - {560: 3037, 799: 3866, 816: 7509}, - // 4655 - {2805, 2805, 9: 2805, 108: 2805, 119: 2805, 2805, 2805, 2805, 2805, 2805, 2805, 2805, 128: 2805, 2805, 2805}, - {533: 7511}, - {2806, 2806, 9: 2806, 108: 2806, 119: 2806, 2806, 2806, 2806, 2806, 2806, 2806, 2806, 128: 2806, 2806, 2806}, - {533: 7513}, - {2807, 2807, 9: 2807, 108: 2807, 119: 2807, 2807, 2807, 2807, 2807, 2807, 2807, 2807, 128: 2807, 2807, 2807}, - // 4660 - {2809, 2809, 9: 2809, 108: 2809, 119: 2809, 2809, 2809, 2809, 2809, 2809, 2809, 2809, 128: 2809, 2809, 2809}, - {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7516}, - {2808, 2808, 9: 2808, 108: 2808, 119: 2808, 2808, 2808, 2808, 2808, 2808, 2808, 2808, 128: 2808, 2808, 2808}, - {4: 7519, 448: 7520, 456: 7518}, - {132: 2362, 181: 2362, 696: 2362}, - // 4665 - {132: 2361, 181: 2361, 696: 2361}, - {132: 2360, 181: 2360, 696: 2360}, - {132: 2357, 696: 7526, 1497: 7525}, - {555: 7523}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7524}, - // 4670 - {132: 2358, 696: 2358}, - {132: 7530}, - {434: 7527}, - {181: 7528, 400: 7529}, - {132: 2356}, - // 4675 - {132: 2355}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7532, 1496: 7531}, - {531: 7534, 537: 2353, 1495: 7533}, - {531: 2354, 537: 2354}, - {537: 7540}, - // 4680 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7536, 3051, 3052, 3050, 1334: 7535}, - {9: 7538, 57: 7537}, - {9: 2351, 57: 2351}, - {537: 2352}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7539, 3051, 3052, 3050}, - // 4685 - {9: 2350, 57: 2350}, - {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 7544, 806: 7542, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 7543, 7541, 1344: 7545}, - {2372, 2372, 532: 2372}, - {2371, 2371, 532: 2371, 539: 1014, 550: 1014, 1014}, - {2370, 2370, 532: 2370}, - // 4690 - {2369, 2369, 532: 2369, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, - {2349, 2349, 532: 7547, 1493: 7546}, - {2366, 2366}, - {173: 7549, 371: 7548}, - {702: 7552}, - // 4695 - {702: 7550}, - {1006: 7551}, - {2347, 2347}, - {1006: 7553}, - {2348, 2348}, - // 4700 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7555}, - {2455, 2455, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7561, 1037: 7560, 1346: 7556}, - {2466, 2466}, - {16: 4418, 18: 4681, 21: 7569, 538: 7568, 553: 4419, 709: 4417, 853: 7567, 865: 7570}, - {2457, 2457, 16: 2457, 18: 2457, 21: 2457, 535: 2457, 538: 2457, 553: 2457, 557: 2457, 709: 2457}, - // 4705 - {200: 7563}, - {2454, 2454, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, - {2453, 2453, 16: 2453, 18: 2453, 21: 2453, 535: 2453, 538: 2453, 553: 2453, 557: 2453, 709: 2453}, - {2452, 2452, 16: 2452, 18: 2452, 21: 2452, 535: 2452, 538: 2452, 553: 2452, 557: 2452, 709: 2452}, - {223: 7564}, - // 4710 - {560: 3037, 799: 3866, 816: 7565}, - {2779, 2779, 16: 2779, 18: 2779, 21: 2779, 220: 5489, 535: 2779, 538: 2779, 553: 2779, 557: 2779, 709: 2779, 1057: 7566}, - {2456, 2456, 16: 2456, 18: 2456, 21: 2456, 535: 2456, 538: 2456, 553: 2456, 557: 2456, 709: 2456}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 7575}, - {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 7573}, - // 4715 - {533: 2316, 555: 4588, 802: 7571}, - {2458, 2458, 16: 2458, 18: 2458, 21: 2458, 535: 2458, 538: 2458, 553: 2458, 557: 2458, 709: 2458}, - {533: 4796, 1167: 7572}, - {2459, 2459, 16: 2459, 18: 2459, 21: 2459, 535: 2459, 538: 2459, 553: 2459, 557: 2459, 709: 2459}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 7574}, - // 4720 - {2460, 2460, 16: 2460, 18: 2460, 21: 2460, 535: 2460, 538: 2460, 553: 2460, 557: 2460, 709: 2460}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 7576}, - {2461, 2461, 16: 2461, 18: 2461, 21: 2461, 535: 2461, 538: 2461, 553: 2461, 557: 2461, 709: 2461}, - {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7578}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7579, 3051, 3052, 3050}, - // 4725 - {104: 5379, 530: 2100, 542: 5378, 961: 7581, 1376: 7580}, - {530: 7582}, - {530: 2099}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7583}, - {531: 7584}, - // 4730 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 7585}, - {9: 5123, 57: 7586}, - {2111, 2111, 6: 2111, 19: 2111, 102: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 562: 2111, 983: 7587}, - {2477, 2477, 6: 5375, 19: 5372, 102: 4719, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 562: 4720, 959: 5376, 961: 5373, 971: 5377, 7111, 982: 5371, 986: 7110, 1193: 7588}, - {2484, 2484}, - // 4735 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7590, 3051, 3052, 3050}, - {531: 7591}, - {286: 5408, 294: 5410, 297: 5409, 1287: 7592}, - {57: 7593}, - {530: 7594}, - // 4740 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7595}, - {531: 7596}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 7597}, - {9: 4027, 57: 7598}, - {2486, 2486}, - // 4745 - {2593, 2593}, - {2618, 2618}, - {2624, 2624, 532: 7603, 729: 7602}, - {194: 7610, 768: 7609}, - {372: 7605, 381: 7604}, - // 4750 - {60: 7608}, - {380: 7606}, - {194: 7607}, - {2621, 2621}, - {2622, 2622}, - // 4755 - {2623, 2623}, - {2620, 2620, 731: 4641, 995: 7611}, - {2619, 2619}, - {2626, 2626}, - {2625, 2625}, - // 4760 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7627, 884: 7626}, - {608: 7616}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7617}, - {546: 7619, 710: 7618}, - {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7624}, - // 4765 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7620}, - {9: 5256, 710: 7621}, - {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7622}, - {2642, 2642, 9: 5546, 532: 5527, 902: 7623}, - {2650, 2650}, - // 4770 - {2642, 2642, 9: 5546, 532: 5527, 902: 7625}, - {2653, 2653}, - {2645, 2645, 9: 3930, 215: 7647, 532: 2645, 715: 7646, 1101: 7657}, - {1252, 1252, 9: 1252, 131: 7632, 215: 1252, 532: 1252, 546: 7629, 710: 7628, 714: 7630, 1252, 727: 7631}, - {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7655}, - // 4775 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7642}, - {312: 7638}, - {312: 7635}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7633}, - {2642, 2642, 9: 6471, 532: 5527, 902: 7634}, - // 4780 - {2647, 2647}, - {530: 7636}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7637}, - {2648, 2648, 9: 6471}, - {530: 7639}, - // 4785 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7640}, - {2642, 2642, 9: 6471, 532: 5527, 902: 7641}, - {2649, 2649}, - {2645, 2645, 9: 5256, 131: 7645, 215: 7647, 532: 2645, 710: 7644, 715: 7646, 1101: 7643}, - {2642, 2642, 532: 5527, 902: 7654}, - // 4790 - {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7652}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7650}, - {131: 7649}, - {131: 7648}, - {2643, 2643, 532: 2643}, - // 4795 - {2644, 2644, 532: 2644}, - {2642, 2642, 9: 6471, 532: 5527, 902: 7651}, - {2646, 2646}, - {2642, 2642, 9: 5546, 532: 5527, 902: 7653}, - {2651, 2651}, - // 4800 - {2652, 2652}, - {2642, 2642, 9: 5546, 532: 5527, 902: 7656}, - {2654, 2654}, - {2642, 2642, 532: 5527, 902: 7658}, - {2655, 2655}, - // 4805 - {608: 7664}, - {558: 7662}, - {608: 2657}, - {546: 7663, 608: 2658}, - {608: 2656}, - // 4810 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7665}, - {546: 6023, 627: 1121, 710: 1121, 723: 1121, 963: 7666}, - {627: 7669, 710: 7668, 723: 7670, 1277: 7667}, - {2663, 2663}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7677, 3051, 3052, 3050}, - // 4815 - {531: 3893, 938: 7672}, - {531: 3893, 938: 6614, 1093: 7671}, - {2660, 2660, 9: 6615}, - {566: 7673}, - {531: 3893, 938: 7674}, - // 4820 - {108: 7675}, - {560: 3037, 799: 4538, 825: 7676}, - {2661, 2661}, - {627: 7669, 723: 7670, 1277: 7678}, - {2662, 2662}, - // 4825 - {765: 7693}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7689, 884: 7688}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7682}, - {2666, 2666, 712: 7684, 765: 7683, 1176: 7685}, - {533: 7687}, - // 4830 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7686, 3051, 3052, 3050}, - {2664, 2664}, - {2665, 2665}, - {2668, 2668}, - {9: 3930, 765: 7691}, - // 4835 - {2666, 2666, 9: 1252, 712: 7684, 765: 1252, 1176: 7690}, - {2667, 2667}, - {533: 7692}, - {2669, 2669}, - {533: 7694}, - // 4840 - {2670, 2670}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 723: 7696, 770: 3927, 3051, 3052, 3050, 804: 7697}, - {178: 7699}, - {2672, 2672, 560: 3037, 799: 4538, 825: 7698}, - {2671, 2671}, - // 4845 - {560: 3037, 799: 4538, 825: 7700}, - {2673, 2673}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7712, 1296: 7711, 1484: 7710}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7705, 1301: 7704, 1487: 7703}, - {2677, 2677, 9: 7708}, - // 4850 - {2676, 2676, 9: 2676}, - {712: 7706}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7707}, - {2674, 2674, 9: 2674}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7705, 1301: 7709}, - // 4855 - {2675, 2675, 9: 2675}, - {2681, 2681, 9: 7715}, - {2680, 2680, 9: 2680}, - {712: 7713}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7714}, - // 4860 - {2678, 2678, 9: 2678}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7712, 1296: 7716}, - {2679, 2679, 9: 2679}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 2446, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 770: 5954, 3051, 3052, 3050, 865: 7558, 891: 7557, 907: 7766, 954: 7561, 1037: 7767}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 533: 2119, 578: 4948, 631: 2119, 854: 7752}, - // 4865 - {333: 7746, 1378: 7745}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7743, 3051, 3052, 3050}, - {571: 7739}, - {214: 7735}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7724}, - // 4870 - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7725}, - {86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 103: 7729, 720: 7315, 1014: 7728, 1108: 7727, 1314: 7726}, - {164, 164, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 103: 7729, 720: 7315, 1014: 7728, 1108: 7734}, - {163, 163, 86: 163, 88: 163, 90: 163, 163, 95: 163, 163, 98: 163, 163, 163, 163, 103: 163, 720: 163}, - {161, 161, 86: 161, 88: 161, 90: 161, 161, 95: 161, 161, 98: 161, 161, 161, 161, 103: 161, 720: 161}, - // 4875 - {160, 160, 86: 160, 88: 160, 90: 160, 160, 95: 160, 160, 98: 160, 160, 160, 160, 103: 160, 532: 7731, 543: 2316, 2316, 555: 4588, 560: 2316, 720: 160, 802: 7730}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7733}, - {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7732}, - {158, 158, 86: 158, 88: 158, 90: 158, 158, 95: 158, 158, 98: 158, 158, 158, 158, 103: 158, 720: 158}, - {159, 159, 86: 159, 88: 159, 90: 159, 159, 95: 159, 159, 98: 159, 159, 159, 159, 103: 159, 720: 159}, - // 4880 - {162, 162, 86: 162, 88: 162, 90: 162, 162, 95: 162, 162, 98: 162, 162, 162, 162, 103: 162, 720: 162}, - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7736}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7737}, - {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7476, 1238: 7738}, - {190, 190, 9: 7515, 108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7514}, - // 4885 - {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 535: 2119, 578: 4948, 854: 7740}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7741}, - {110: 7350, 7348, 7347, 7349, 7346, 977: 7344, 1255: 7742}, - {193, 193, 9: 7398, 110: 7350, 7348, 7347, 7349, 7346, 977: 7397}, - {18: 4681, 865: 7744}, - // 4890 - {415, 415}, - {416, 416}, - {449: 7747}, - {414, 414, 86: 7748}, - {87: 7749}, - // 4895 - {530: 7750}, - {257: 7751}, - {413, 413}, - {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 7753, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 7754}, - {1965, 1965, 6: 1965, 9: 1965, 15: 1965, 51: 1965, 1965, 1965, 1965, 1965, 183: 1965, 531: 7760, 1965, 628: 1965, 725: 1965, 1965}, - // 4900 - {401, 401, 6: 401, 9: 5873, 15: 401, 51: 401, 401, 401, 401, 401, 532: 401, 725: 5917, 1075: 5916, 7755}, - {409, 409, 6: 409, 15: 409, 51: 409, 409, 409, 409, 409, 532: 7421, 1131: 7756}, - {382, 382, 6: 382, 15: 7437, 51: 382, 382, 7436, 7438, 7439, 1068: 7435, 1235: 7434, 7757}, - {387, 387, 6: 7461, 51: 387, 7462, 1128: 7758}, - {384, 384, 51: 7466, 1254: 7759}, - // 4905 - {418, 418}, - {57: 7761}, - {183: 7762}, - {723: 7763}, - {533: 5886, 997: 7764}, - // 4910 - {417, 417}, - {16: 1647, 18: 1647, 21: 1647, 214: 5509, 535: 1647, 538: 1647, 553: 1647, 557: 1647, 709: 1647}, - {16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7561, 1037: 7768}, - {2467, 2467, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, - {2468, 2468, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, - // 4915 - {2314, 2314, 3: 2861, 58: 2884, 84: 2863, 2866, 87: 2896, 2864, 3017, 103: 2898, 117: 3031, 159: 3033, 187: 2881, 195: 2879, 208: 3024, 222: 2892, 250: 2887, 254: 2869, 259: 2917, 266: 2883, 269: 2859, 277: 2916, 3027, 2865, 284: 3032, 296: 2895, 306: 2893, 308: 2860, 310: 2899, 330: 2885, 334: 2888, 341: 2897, 344: 2882, 357: 2874, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 2915, 562: 3026, 575: 3020, 577: 2877, 582: 2875, 587: 2890, 608: 2904, 695: 2900, 710: 3030, 713: 2862, 3019, 724: 2857, 727: 2868, 743: 2867, 766: 2914, 2858, 775: 2911, 803: 2870, 806: 2913, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 2995, 2994, 822: 3018, 2871, 2976, 826: 2988, 3004, 2876, 833: 2872, 839: 2934, 845: 2928, 2932, 2985, 2996, 857: 2936, 2878, 860: 3003, 3005, 894: 3023, 897: 2880, 904: 2921, 933: 3029, 943: 2929, 957: 3021, 962: 2979, 965: 2990, 967: 2993, 2886, 1035: 2941, 1090: 3025, 1099: 2949, 2919, 1102: 2920, 2923, 1105: 2926, 2924, 2927, 1109: 2925, 1111: 2922, 1113: 2930, 2931, 1116: 2937, 2889, 2974, 3014, 1121: 2938, 1132: 2945, 2939, 2940, 2946, 2947, 2948, 2944, 2950, 2951, 1142: 2943, 2942, 1145: 2933, 2894, 1148: 2952, 2966, 2953, 2954, 3015, 2957, 2956, 2962, 2961, 2963, 2958, 2964, 2965, 2955, 2960, 2959, 1166: 2918, 1169: 2935, 1174: 2970, 2968, 1177: 2969, 2967, 1182: 2972, 2973, 2971, 1188: 3010, 2975, 2977, 1198: 3028, 2978, 1208: 2980, 1210: 2981, 3007, 1213: 3011, 1237: 3012, 1239: 2983, 2984, 1248: 2989, 1251: 2986, 2987, 1258: 3009, 3013, 3022, 2992, 2991, 1268: 2997, 1270: 2999, 2998, 1273: 3001, 1275: 3008, 1278: 3000, 1284: 7770, 1298: 3002, 2982, 3006}, - {627, 627}, - } -) - -var yyDebug = 0 - -type yyLexer interface { - Lex(lval *yySymType) int - Errorf(format string, a ...interface{}) error - AppendError(err error) - AppendWarn(err error) - Errors() (warns []error, errs []error) -} - -type yyLexerEx interface { - yyLexer - Reduced(rule, state int, lval *yySymType) bool -} - -func yySymName(c int) (s string) { - x, ok := yyXLAT[c] - if ok { - return yySymNames[x] - } - - return __yyfmt__.Sprintf("%d", c) -} - -func yylex1(yylex yyLexer, lval *yySymType) (n int) { - n = yylex.Lex(lval) - if n <= 0 { - n = yyEOFCode - } - if yyDebug >= 3 { - __yyfmt__.Printf("\nlex %s(%#x %d), lval: %+v\n", yySymName(n), n, n, lval) - } - return n -} - -func yyParse(yylex yyLexer, parser *Parser) int { - const yyError = 1520 - - yyEx, _ := yylex.(yyLexerEx) - var yyn int - parser.yylval = yySymType{} - yyS := parser.cache - - Nerrs := 0 /* number of errors */ - Errflag := 0 /* error recovery flag */ - yyerrok := func() { - if yyDebug >= 2 { - __yyfmt__.Printf("yyerrok()\n") - } - Errflag = 0 - } - _ = yyerrok - yystate := 0 - yychar := -1 - var yyxchar int - var yyshift int - yyp := -1 - goto yystack - -ret0: - return 0 - -ret1: - return 1 - -yystack: - /* put a state and value onto the stack */ - yyp++ - if yyp+1 >= len(yyS) { - nyys := make([]yySymType, len(yyS)*2) - copy(nyys, yyS) - yyS = nyys - parser.cache = yyS - } - parser.yyVAL = &yyS[yyp+1] - yyS[yyp].yys = yystate - -yynewstate: - if yychar < 0 { - yychar = yylex1(yylex, &parser.yylval) - var ok bool - if yyxchar, ok = yyXLAT[yychar]; !ok { - yyxchar = len(yySymNames) // > tab width - } - } - if yyDebug >= 4 { - var a []int - for _, v := range yyS[:yyp+1] { - a = append(a, v.yys) - } - __yyfmt__.Printf("state stack %v\n", a) - } - row := yyParseTab[yystate] - yyn = 0 - if yyxchar < len(row) { - if yyn = int(row[yyxchar]); yyn != 0 { - yyn += yyTabOfs - } - } - switch { - case yyn > 0: // shift - yychar = -1 - *parser.yyVAL = parser.yylval - yystate = yyn - yyshift = yyn - if yyDebug >= 2 { - __yyfmt__.Printf("shift, and goto state %d\n", yystate) - } - if Errflag > 0 { - Errflag-- - } - goto yystack - case yyn < 0: // reduce - case yystate == 1: // accept - if yyDebug >= 2 { - __yyfmt__.Println("accept") - } - goto ret0 - } - - if yyn == 0 { - /* error ... attempt to resume parsing */ - switch Errflag { - case 0: /* brand new error */ - if yyDebug >= 1 { - __yyfmt__.Printf("no action for %s in state %d\n", yySymName(yychar), yystate) - } - msg, ok := yyXErrors[yyXError{yystate, yyxchar}] - if !ok { - msg, ok = yyXErrors[yyXError{yystate, -1}] - } - if !ok && yyshift != 0 { - msg, ok = yyXErrors[yyXError{yyshift, yyxchar}] - } - if !ok { - msg, ok = yyXErrors[yyXError{yyshift, -1}] - } - if !ok || msg == "" { - msg = "syntax error" - } - // ignore goyacc error message - yylex.AppendError(yylex.Errorf("")) - Nerrs++ - fallthrough - - case 1, 2: /* incompletely recovered error ... try again */ - Errflag = 3 - - /* find a state where "error" is a legal shift action */ - for yyp >= 0 { - row := yyParseTab[yyS[yyp].yys] - if yyError < len(row) { - yyn = int(row[yyError]) + yyTabOfs - if yyn > 0 { // hit - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery found error shift in state %d\n", yyS[yyp].yys) - } - yystate = yyn /* simulate a shift of "error" */ - goto yystack - } - } - - /* the current p has no shift on "error", pop stack */ - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) - } - yyp-- - } - /* there is no state on the stack with an error shift ... abort */ - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery failed\n") - } - goto ret1 - - case 3: /* no shift yet; clobber input char */ - if yyDebug >= 2 { - __yyfmt__.Printf("error recovery discards %s\n", yySymName(yychar)) - } - if yychar == yyEOFCode { - goto ret1 - } - - yychar = -1 - goto yynewstate /* try again in the same state */ - } - } - - r := -yyn - x0 := yyReductions[r] - x, n := x0.xsym, x0.components - yypt := yyp - _ = yypt // guard against "declared and not used" - - yyp -= n - if yyp+1 >= len(yyS) { - nyys := make([]yySymType, len(yyS)*2) - copy(nyys, yyS) - yyS = nyys - parser.cache = yyS - } - parser.yyVAL = &yyS[yyp+1] - - /* consult goto table to find next state */ - exState := yystate - yystate = int(yyParseTab[yyS[yyp].yys][x]) + yyTabOfs - /* reduction by production r */ - if yyDebug >= 2 { - __yyfmt__.Printf("reduce using rule %v (%s), and goto state %d\n", r, yySymNames[x], yystate) - } - - switch r { - case 2: - { - specs := yyS[yypt-1].item.([]*ast.AlterTableSpec) - if yyS[yypt-0].item != nil { - specs = append(specs, yyS[yypt-0].item.(*ast.AlterTableSpec)) - } - parser.yyVAL.statement = &ast.AlterTableStmt{ - Table: yyS[yypt-2].item.(*ast.TableName), - Specs: specs, - } - } - case 3: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, PartitionNames: yyS[yypt-1].item.([]model.CIStr), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 4: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-6].item.(*ast.TableName)}, - PartitionNames: yyS[yypt-3].item.([]model.CIStr), - IndexNames: yyS[yypt-1].item.([]model.CIStr), - IndexFlag: true, - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), - } - } - case 5: - { - parser.yyVAL.statement = &ast.CompactTableStmt{ - Table: yyS[yypt-1].item.(*ast.TableName), - ReplicaKind: ast.CompactReplicaKindAll, - } - } - case 6: - { - parser.yyVAL.statement = &ast.CompactTableStmt{ - Table: yyS[yypt-3].item.(*ast.TableName), - ReplicaKind: ast.CompactReplicaKindTiFlash, - } - } - case 7: - { - parser.yyVAL.statement = &ast.CompactTableStmt{ - Table: yyS[yypt-3].item.(*ast.TableName), - PartitionNames: yyS[yypt-0].item.([]model.CIStr), - ReplicaKind: ast.CompactReplicaKindAll, - } - } - case 8: - { - parser.yyVAL.statement = &ast.CompactTableStmt{ - Table: yyS[yypt-5].item.(*ast.TableName), - PartitionNames: yyS[yypt-2].item.([]model.CIStr), - ReplicaKind: ast.CompactReplicaKindTiFlash, - } - } - case 9: - { - parser.yyVAL.item = []*ast.ResourceGroupOption{yyS[yypt-0].item.(*ast.ResourceGroupOption)} - } - case 10: - { - if !ast.CheckAppend(yyS[yypt-1].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) - } - case 11: - { - if !ast.CheckAppend(yyS[yypt-2].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) - } - case 12: - { - parser.yyVAL.item = uint64(1) - } - case 13: - { - parser.yyVAL.item = uint64(8) - } - case 14: - { - parser.yyVAL.item = uint64(16) - } - case 15: - { - parser.yyVAL.item = []*ast.ResourceGroupRunawayOption{yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)} - } - case 16: - { - if !ast.CheckRunawayAppend(yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) { - yylex.AppendError(yylex.Errorf("Dupliated runaway options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) - } - case 17: - { - if !ast.CheckRunawayAppend(yyS[yypt-2].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) { - yylex.AppendError(yylex.Errorf("Dupliated runaway options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) - } - case 18: - { - parser.yyVAL.item = int32(model.WatchExact) - } - case 19: - { - parser.yyVAL.item = int32(model.WatchSimilar) - } - case 20: - { - parser.yyVAL.item = int32(model.WatchPlan) - } - case 21: - { - parser.yyVAL.item = int32(model.RunawayActionDryRun) - } - case 22: - { - parser.yyVAL.item = int32(model.RunawayActionCooldown) - } - case 23: - { - parser.yyVAL.item = int32(model.RunawayActionKill) - } - case 24: - { - _, err := time.ParseDuration(yyS[yypt-0].ident) - if err != nil { - yylex.AppendError(yylex.Errorf("The EXEC_ELAPSED option is not a valid duration: %s", err.Error())) - return 1 - } - parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayRule, StrValue: yyS[yypt-0].ident} - } - case 25: - { - parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayAction, IntValue: yyS[yypt-0].item.(int32)} - } - case 26: - { - dur := strings.ToLower(yyS[yypt-0].item.(string)) - if dur == "unlimited" { - dur = "" - } - if len(dur) > 0 { - _, err := time.ParseDuration(dur) - if err != nil { - yylex.AppendError(yylex.Errorf("The WATCH DURATION option is not a valid duration: %s", err.Error())) - return 1 - } - } - parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayWatch, StrValue: dur, IntValue: yyS[yypt-1].item.(int32)} - } - case 27: - { - parser.yyVAL.item = "" - } - case 28: - { - parser.yyVAL.item = yyS[yypt-0].ident - } - case 29: - { - parser.yyVAL.item = "" - } - case 30: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceRURate, UintValue: yyS[yypt-0].item.(uint64)} - } - case 31: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourcePriority, UintValue: yyS[yypt-0].item.(uint64)} - } - case 32: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceBurstableOpiton, BoolValue: true} - } - case 33: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceBurstableOpiton, BoolValue: yyS[yypt-0].item.(bool)} - } - case 34: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption)} - } - case 35: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: nil} - } - case 36: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: nil} - } - case 37: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption)} - } - case 38: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: nil} - } - case 39: - { - parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: nil} - } - case 40: - { - parser.yyVAL.item = []*ast.ResourceGroupBackgroundOption{yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)} - } - case 41: - { - if !ast.CheckBackgroundAppend(yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) { - yylex.AppendError(yylex.Errorf("Dupliated background options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) - } - case 42: - { - if !ast.CheckBackgroundAppend(yyS[yypt-2].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) { - yylex.AppendError(yylex.Errorf("Dupliated background options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) - } - case 43: - { - parser.yyVAL.item = &ast.ResourceGroupBackgroundOption{Type: ast.BackgroundOptionTaskNames, StrValue: yyS[yypt-0].ident} - } - case 44: - { - parser.yyVAL.item = []*ast.PlacementOption{yyS[yypt-0].item.(*ast.PlacementOption)} - } - case 45: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.PlacementOption), yyS[yypt-0].item.(*ast.PlacementOption)) - } - case 46: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.PlacementOption), yyS[yypt-0].item.(*ast.PlacementOption)) - } - case 47: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPrimaryRegion, StrValue: yyS[yypt-0].ident} - } - case 48: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionRegions, StrValue: yyS[yypt-0].ident} - } - case 49: - { - cnt := yyS[yypt-0].item.(uint64) - if cnt == 0 { - yylex.AppendError(yylex.Errorf("FOLLOWERS must be positive")) - return 1 - } - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionFollowerCount, UintValue: cnt} - } - case 50: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionVoterCount, UintValue: yyS[yypt-0].item.(uint64)} - } - case 51: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLearnerCount, UintValue: yyS[yypt-0].item.(uint64)} - } - case 52: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionSchedule, StrValue: yyS[yypt-0].ident} - } - case 53: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionConstraints, StrValue: yyS[yypt-0].ident} - } - case 54: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLeaderConstraints, StrValue: yyS[yypt-0].ident} - } - case 55: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionFollowerConstraints, StrValue: yyS[yypt-0].ident} - } - case 56: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionVoterConstraints, StrValue: yyS[yypt-0].ident} - } - case 57: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLearnerConstraints, StrValue: yyS[yypt-0].ident} - } - case 58: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionSurvivalPreferences, StrValue: yyS[yypt-0].ident} - } - case 59: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} - } - case 60: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} - } - case 61: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} - } - case 62: - { - parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} - } - case 63: - { - parser.yyVAL.item = &ast.AttributesSpec{Default: true} - } - case 64: - { - parser.yyVAL.item = &ast.AttributesSpec{Default: false, Attributes: yyS[yypt-0].ident} - } - case 65: - { - parser.yyVAL.item = &ast.StatsOptionsSpec{Default: true} - } - case 66: - { - parser.yyVAL.item = &ast.StatsOptionsSpec{Default: false, StatsOptions: yyS[yypt-0].ident} - } - case 67: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTablePartition, - Partition: yyS[yypt-0].item.(*ast.PartitionOptions), - } - } else { - parser.yyVAL.item = nil - } - } - case 68: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRemovePartitioning, - } - } - case 69: - { - ret := yyS[yypt-0].item.(*ast.AlterTableSpec) - ret.NoWriteToBinlog = yyS[yypt-1].item.(bool) - parser.yyVAL.item = ret - } - case 70: - { - partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-1].expr} - startOffset := parser.yyVAL.offset - endOffset := parser.yylval.offset - partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - partitionMethod.SetOriginTextPosition(startOffset) - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableReorganizeLastPartition, - Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, - } - } - case 71: - { - partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-1].expr} - startOffset := parser.yyVAL.offset - endOffset := parser.yylval.offset - partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - // Needed for replacing syntactic sugar with generated partitioning definition string - partitionMethod.SetOriginTextPosition(startOffset) - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableReorganizeFirstPartition, - Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, - } - } - case 72: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTablePartitionAttributes, - PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-1].ident)}, - AttributesSpec: yyS[yypt-0].item.(*ast.AttributesSpec), - } - } - case 73: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTablePartitionOptions, - PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-1].ident)}, - Options: yyS[yypt-0].item.([]*ast.TableOption), - } - } - case 74: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRemoveTTL, - } - } - case 75: - { - parser.yyVAL.item = []string{} - } - case 76: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 77: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableOption, - Options: yyS[yypt-0].item.([]*ast.TableOption), - } - } - case 78: - { - tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ - Count: yyS[yypt-1].item.(uint64), - Labels: yyS[yypt-0].item.([]string), - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableSetTiFlashReplica, - TiFlashReplica: tiflashReplicaSpec, - } - } - case 79: - { - tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ - Count: yyS[yypt-1].item.(uint64), - Labels: yyS[yypt-0].item.([]string), - Hypo: true, - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableSetTiFlashReplica, - TiFlashReplica: tiflashReplicaSpec, - } - } - case 80: - { - op := &ast.AlterTableSpec{ - Tp: ast.AlterTableOption, - Options: []*ast.TableOption{{Tp: ast.TableOptionCharset, StrValue: yyS[yypt-1].ident, - UintValue: ast.TableOptionCharsetWithConvertTo}}, - } - if yyS[yypt-0].ident != "" { - op.Options = append(op.Options, &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident}) - } - parser.yyVAL.item = op - } - case 81: - { - op := &ast.AlterTableSpec{ - Tp: ast.AlterTableOption, - Options: []*ast.TableOption{{Tp: ast.TableOptionCharset, Default: true, - UintValue: ast.TableOptionCharsetWithConvertTo}}, - } - if yyS[yypt-0].ident != "" { - op.Options = append(op.Options, &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident}) - } - parser.yyVAL.item = op - } - case 82: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfNotExists: yyS[yypt-2].item.(bool), - Tp: ast.AlterTableAddColumns, - NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, - Position: yyS[yypt-0].item.(*ast.ColumnPosition), - } - } - case 83: - { - tes := yyS[yypt-1].item.([]interface{}) - var columnDefs []*ast.ColumnDef - var constraints []*ast.Constraint - for _, te := range tes { - switch te := te.(type) { - case *ast.ColumnDef: - columnDefs = append(columnDefs, te) - case *ast.Constraint: - constraints = append(constraints, te) - } - } - parser.yyVAL.item = &ast.AlterTableSpec{ - IfNotExists: yyS[yypt-3].item.(bool), - Tp: ast.AlterTableAddColumns, - NewColumns: columnDefs, - NewConstraints: constraints, - } - } - case 84: - { - constraint := yyS[yypt-0].item.(*ast.Constraint) - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAddConstraint, - Constraint: constraint, - } - } - case 85: - { - var defs []*ast.PartitionDefinition - if yyS[yypt-0].item != nil { - defs = yyS[yypt-0].item.([]*ast.PartitionDefinition) - } - noWriteToBinlog := yyS[yypt-1].item.(bool) - if noWriteToBinlog { - yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) - parser.lastErrorAsWarn() - } - parser.yyVAL.item = &ast.AlterTableSpec{ - IfNotExists: yyS[yypt-2].item.(bool), - NoWriteToBinlog: noWriteToBinlog, - Tp: ast.AlterTableAddPartitions, - PartDefinitions: defs, - } - } - case 86: - { - noWriteToBinlog := yyS[yypt-2].item.(bool) - if noWriteToBinlog { - yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) - parser.lastErrorAsWarn() - } - parser.yyVAL.item = &ast.AlterTableSpec{ - IfNotExists: yyS[yypt-3].item.(bool), - NoWriteToBinlog: noWriteToBinlog, - Tp: ast.AlterTableAddPartitions, - Num: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 87: - { - noWriteToBinlog := yyS[yypt-0].item.(bool) - if noWriteToBinlog { - yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) - parser.lastErrorAsWarn() - } - partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-2].expr} - startOffset := parser.yyVAL.offset - endOffset := parser.yylval.offset - partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - // Needed for replacing syntactic sugar with generated partitioning definition string - partitionMethod.SetOriginTextPosition(startOffset) - parser.yyVAL.item = &ast.AlterTableSpec{ - NoWriteToBinlog: noWriteToBinlog, - Tp: ast.AlterTableAddLastPartition, - Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, - } - } - case 88: - { - statsSpec := &ast.StatisticsSpec{ - StatsName: yyS[yypt-4].ident, - StatsType: yyS[yypt-3].item.(uint8), - Columns: yyS[yypt-1].item.([]*ast.ColumnName), - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAddStatistics, - IfNotExists: yyS[yypt-5].item.(bool), - Statistics: statsSpec, - } - } - case 89: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAttributes, - AttributesSpec: yyS[yypt-0].item.(*ast.AttributesSpec), - } - } - case 90: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableStatsOptions, - StatsOptionsSpec: yyS[yypt-0].item.(*ast.StatsOptionsSpec), - } - } - case 91: - { - yylex.AppendError(yylex.Errorf("The CHECK PARTITIONING clause is parsed but not implement yet.")) - parser.lastErrorAsWarn() - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableCheckPartitions, - } - if yyS[yypt-0].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - } - parser.yyVAL.item = ret - } - case 92: - { - noWriteToBinlog := yyS[yypt-1].item.(bool) - if noWriteToBinlog { - yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) - parser.lastErrorAsWarn() - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableCoalescePartitions, - NoWriteToBinlog: noWriteToBinlog, - Num: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 93: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-2].item.(bool), - Tp: ast.AlterTableDropColumn, - OldColumnName: yyS[yypt-1].item.(*ast.ColumnName), - } - } - case 94: - { - parser.yyVAL.item = &ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey} - } - case 95: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-1].item.(bool), - Tp: ast.AlterTableDropPartition, - PartitionNames: yyS[yypt-0].item.([]model.CIStr), - } - } - case 96: - { - partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-2].expr} - startOffset := parser.yyVAL.offset - endOffset := parser.yylval.offset - partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - // Needed for replacing syntactic sugar with generated partitioning definition string - partitionMethod.SetOriginTextPosition(startOffset) - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-0].item.(bool), - Tp: ast.AlterTableDropFirstPartition, - Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, - } - } - case 97: - { - statsSpec := &ast.StatisticsSpec{ - StatsName: yyS[yypt-0].ident, - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableDropStatistics, - IfExists: yyS[yypt-1].item.(bool), - Statistics: statsSpec, - } - } - case 98: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableExchangePartition, - PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-4].ident)}, - NewTable: yyS[yypt-1].item.(*ast.TableName), - WithValidation: yyS[yypt-0].item.(bool), - } - } - case 99: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableTruncatePartition, - } - if yyS[yypt-0].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - } - parser.yyVAL.item = ret - } - case 100: - { - ret := &ast.AlterTableSpec{ - NoWriteToBinlog: yyS[yypt-1].item.(bool), - Tp: ast.AlterTableOptimizePartition, - } - if yyS[yypt-0].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - } - parser.yyVAL.item = ret - } - case 101: - { - ret := &ast.AlterTableSpec{ - NoWriteToBinlog: yyS[yypt-1].item.(bool), - Tp: ast.AlterTableRepairPartition, - } - if yyS[yypt-0].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - } - parser.yyVAL.item = ret - } - case 102: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableImportPartitionTablespace, - } - if yyS[yypt-1].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-1].item.([]model.CIStr) - } - parser.yyVAL.item = ret - yylex.AppendError(yylex.Errorf("The IMPORT PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 103: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableDiscardPartitionTablespace, - } - if yyS[yypt-1].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-1].item.([]model.CIStr) - } - parser.yyVAL.item = ret - yylex.AppendError(yylex.Errorf("The DISCARD PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 104: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableImportTablespace, - } - parser.yyVAL.item = ret - yylex.AppendError(yylex.Errorf("The IMPORT TABLESPACE clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 105: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableDiscardTablespace, - } - parser.yyVAL.item = ret - yylex.AppendError(yylex.Errorf("The DISCARD TABLESPACE clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 106: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableRebuildPartition, - NoWriteToBinlog: yyS[yypt-1].item.(bool), - } - if yyS[yypt-0].item == nil { - ret.OnAllPartitions = true - } else { - ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - } - parser.yyVAL.item = ret - } - case 107: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-1].item.(bool), - Tp: ast.AlterTableDropIndex, - Name: yyS[yypt-0].ident, - } - } - case 108: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-1].item.(bool), - Tp: ast.AlterTableDropForeignKey, - Name: yyS[yypt-0].ident, - } - } - case 109: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableOrderByColumns, - OrderByList: yyS[yypt-0].item.([]*ast.AlterOrderItem), - } - } - case 110: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableDisableKeys, - } - } - case 111: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableEnableKeys, - } - } - case 112: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-2].item.(bool), - Tp: ast.AlterTableModifyColumn, - NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, - Position: yyS[yypt-0].item.(*ast.ColumnPosition), - } - } - case 113: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - IfExists: yyS[yypt-3].item.(bool), - Tp: ast.AlterTableChangeColumn, - OldColumnName: yyS[yypt-2].item.(*ast.ColumnName), - NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, - Position: yyS[yypt-0].item.(*ast.ColumnPosition), - } - } - case 114: - { - option := &ast.ColumnOption{Expr: yyS[yypt-0].expr} - colDef := &ast.ColumnDef{ - Name: yyS[yypt-3].item.(*ast.ColumnName), - Options: []*ast.ColumnOption{option}, - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAlterColumn, - NewColumns: []*ast.ColumnDef{colDef}, - } - } - case 115: - { - option := &ast.ColumnOption{Expr: yyS[yypt-1].expr} - colDef := &ast.ColumnDef{ - Name: yyS[yypt-5].item.(*ast.ColumnName), - Options: []*ast.ColumnOption{option}, - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAlterColumn, - NewColumns: []*ast.ColumnDef{colDef}, - } - } - case 116: - { - colDef := &ast.ColumnDef{ - Name: yyS[yypt-2].item.(*ast.ColumnName), - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAlterColumn, - NewColumns: []*ast.ColumnDef{colDef}, - } - } - case 117: - { - oldColName := &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-2].ident)} - newColName := &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-0].ident)} - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRenameColumn, - OldColumnName: oldColName, - NewColumnName: newColName, - } - } - case 118: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRenameTable, - NewTable: yyS[yypt-0].item.(*ast.TableName), - } - } - case 119: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRenameTable, - NewTable: yyS[yypt-0].item.(*ast.TableName), - } - } - case 120: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRenameTable, - NewTable: yyS[yypt-0].item.(*ast.TableName), - } - } - case 121: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableRenameIndex, - FromKey: model.NewCIStr(yyS[yypt-2].ident), - ToKey: model.NewCIStr(yyS[yypt-0].ident), - } - } - case 122: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableLock, - LockType: yyS[yypt-0].item.(ast.LockType), - } - } - case 123: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableWriteable, - Writeable: yyS[yypt-0].item.(bool), - } - } - case 124: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAlgorithm, - Algorithm: yyS[yypt-0].item.(ast.AlgorithmType), - } - } - case 125: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableForce, - } - } - case 126: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableWithValidation, - } - } - case 127: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableWithoutValidation, - } - } - case 128: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableSecondaryLoad, - } - yylex.AppendError(yylex.Errorf("The SECONDARY_LOAD clause is parsed but not implement yet.")) - parser.lastErrorAsWarn() - } - case 129: - { - // Parse it and ignore it. Just for compatibility. - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableSecondaryUnload, - } - yylex.AppendError(yylex.Errorf("The SECONDARY_UNLOAD VALIDATION clause is parsed but not implement yet.")) - parser.lastErrorAsWarn() - } - case 130: - { - c := &ast.Constraint{ - Name: yyS[yypt-1].ident, - Enforced: yyS[yypt-0].item.(bool), - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableAlterCheck, - Constraint: c, - } - } - case 131: - { - // Parse it and ignore it. Just for compatibility. - c := &ast.Constraint{ - Name: yyS[yypt-0].ident, - } - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableDropCheck, - Constraint: c, - } - } - case 132: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableIndexInvisible, - IndexName: model.NewCIStr(yyS[yypt-1].ident), - Visibility: yyS[yypt-0].item.(ast.IndexVisibility), - } - } - case 133: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableCache, - } - } - case 134: - { - parser.yyVAL.item = &ast.AlterTableSpec{ - Tp: ast.AlterTableNoCache, - } - } - case 135: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableReorganizePartition, - OnAllPartitions: true, - } - parser.yyVAL.item = ret - } - case 136: - { - ret := &ast.AlterTableSpec{ - Tp: ast.AlterTableReorganizePartition, - PartitionNames: yyS[yypt-4].item.([]model.CIStr), - PartDefinitions: yyS[yypt-1].item.([]*ast.PartitionDefinition), - } - parser.yyVAL.item = ret - } - case 137: - { - parser.yyVAL.item = nil - } - case 139: - { - parser.yyVAL.item = true - } - case 141: - { - parser.yyVAL.item = true - } - case 142: - { - parser.yyVAL.item = false - } - case 143: - { - parser.yyVAL.item = model.PrimaryKeyTypeClustered - } - case 144: - { - parser.yyVAL.item = model.PrimaryKeyTypeNonClustered - } - case 145: - { - parser.yyVAL.item = ast.AlgorithmTypeDefault - } - case 146: - { - parser.yyVAL.item = ast.AlgorithmTypeCopy - } - case 147: - { - parser.yyVAL.item = ast.AlgorithmTypeInplace - } - case 148: - { - parser.yyVAL.item = ast.AlgorithmTypeInstant - } - case 149: - { - yylex.AppendError(ErrUnknownAlterAlgorithm.GenWithStackByArgs(yyS[yypt-2].ident)) - return 1 - } - case 150: - { - parser.yyVAL.item = ast.LockTypeDefault - } - case 151: - { - id := strings.ToUpper(yyS[yypt-0].ident) - - if id == "NONE" { - parser.yyVAL.item = ast.LockTypeNone - } else if id == "SHARED" { - parser.yyVAL.item = ast.LockTypeShared - } else if id == "EXCLUSIVE" { - parser.yyVAL.item = ast.LockTypeExclusive - } else { - yylex.AppendError(ErrUnknownAlterLock.GenWithStackByArgs(yyS[yypt-0].ident)) - return 1 - } - } - case 152: - { - parser.yyVAL.item = true - } - case 153: - { - parser.yyVAL.item = false - } - case 160: - { - parser.yyVAL.item = &ast.ColumnPosition{Tp: ast.ColumnPositionNone} - } - case 161: - { - parser.yyVAL.item = &ast.ColumnPosition{Tp: ast.ColumnPositionFirst} - } - case 162: - { - parser.yyVAL.item = &ast.ColumnPosition{ - Tp: ast.ColumnPositionAfter, - RelativeColumn: yyS[yypt-0].item.(*ast.ColumnName), - } - } - case 163: - { - parser.yyVAL.item = make([]*ast.AlterTableSpec, 0, 1) - } - case 165: - { - parser.yyVAL.item = []*ast.AlterTableSpec{yyS[yypt-0].item.(*ast.AlterTableSpec)} - } - case 166: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.AlterTableSpec), yyS[yypt-0].item.(*ast.AlterTableSpec)) - } - case 167: - { - parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} - } - case 168: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) - } - case 169: - { - parser.yyVAL.item = nil - } - case 170: - { - parser.yyVAL.item = nil - } - case 171: - { - parser.yyVAL.item = yyS[yypt-0].ident - } - case 173: - { - parser.yyVAL.statement = &ast.RenameTableStmt{ - TableToTables: yyS[yypt-0].item.([]*ast.TableToTable), - } - } - case 174: - { - parser.yyVAL.item = []*ast.TableToTable{yyS[yypt-0].item.(*ast.TableToTable)} - } - case 175: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableToTable), yyS[yypt-0].item.(*ast.TableToTable)) - } - case 176: - { - parser.yyVAL.item = &ast.TableToTable{ - OldTable: yyS[yypt-2].item.(*ast.TableName), - NewTable: yyS[yypt-0].item.(*ast.TableName), - } - } - case 177: - { - parser.yyVAL.statement = &ast.RenameUserStmt{ - UserToUsers: yyS[yypt-0].item.([]*ast.UserToUser), - } - } - case 178: - { - parser.yyVAL.item = []*ast.UserToUser{yyS[yypt-0].item.(*ast.UserToUser)} - } - case 179: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserToUser), yyS[yypt-0].item.(*ast.UserToUser)) - } - case 180: - { - parser.yyVAL.item = &ast.UserToUser{ - OldUser: yyS[yypt-2].item.(*auth.UserIdentity), - NewUser: yyS[yypt-0].item.(*auth.UserIdentity), - } - } - case 181: - { - parser.yyVAL.statement = &ast.RecoverTableStmt{ - JobID: yyS[yypt-0].item.(int64), - } - } - case 182: - { - parser.yyVAL.statement = &ast.RecoverTableStmt{ - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 183: - { - parser.yyVAL.statement = &ast.RecoverTableStmt{ - Table: yyS[yypt-1].item.(*ast.TableName), - JobNum: yyS[yypt-0].item.(int64), - } - } - case 184: - { - parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ - FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), - } - } - case 185: - { - parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ - Tables: yyS[yypt-2].item.([]*ast.TableName), - FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), - } - } - case 186: - { - parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ - DBName: model.NewCIStr(yyS[yypt-2].ident), - FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), - } - } - case 187: - { - parser.yyVAL.statement = &ast.FlashBackTableStmt{ - Table: yyS[yypt-1].item.(*ast.TableName), - NewName: yyS[yypt-0].ident, - } - } - case 188: - { - parser.yyVAL.ident = "" - } - case 189: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 190: - { - parser.yyVAL.statement = &ast.FlashBackDatabaseStmt{ - DBName: model.NewCIStr(yyS[yypt-1].ident), - NewName: yyS[yypt-0].ident, - } - } - case 191: - { - parser.yyVAL.statement = &ast.SplitRegionStmt{ - SplitSyntaxOpt: yyS[yypt-4].item.(*ast.SplitSyntaxOption), - Table: yyS[yypt-2].item.(*ast.TableName), - PartitionNames: yyS[yypt-1].item.([]model.CIStr), - SplitOpt: yyS[yypt-0].item.(*ast.SplitOption), - } - } - case 192: - { - parser.yyVAL.statement = &ast.SplitRegionStmt{ - SplitSyntaxOpt: yyS[yypt-6].item.(*ast.SplitSyntaxOption), - Table: yyS[yypt-4].item.(*ast.TableName), - PartitionNames: yyS[yypt-3].item.([]model.CIStr), - IndexName: model.NewCIStr(yyS[yypt-1].ident), - SplitOpt: yyS[yypt-0].item.(*ast.SplitOption), - } - } - case 193: - { - parser.yyVAL.item = &ast.SplitOption{ - Lower: yyS[yypt-4].item.([]ast.ExprNode), - Upper: yyS[yypt-2].item.([]ast.ExprNode), - Num: yyS[yypt-0].item.(int64), - } - } - case 194: - { - parser.yyVAL.item = &ast.SplitOption{ - ValueLists: yyS[yypt-0].item.([][]ast.ExprNode), - } - } - case 195: - { - parser.yyVAL.item = &ast.SplitSyntaxOption{} - } - case 196: - { - parser.yyVAL.item = &ast.SplitSyntaxOption{ - HasRegionFor: true, - } - } - case 197: - { - parser.yyVAL.item = &ast.SplitSyntaxOption{ - HasPartition: true, - } - } - case 198: - { - parser.yyVAL.item = &ast.SplitSyntaxOption{ - HasRegionFor: true, - HasPartition: true, - } - } - case 199: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: yyS[yypt-2].item.([]*ast.TableName), ColumnChoice: yyS[yypt-1].item.(model.ColumnChoice), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 200: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, IndexNames: yyS[yypt-1].item.([]model.CIStr), IndexFlag: true, AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 201: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, IndexNames: yyS[yypt-1].item.([]model.CIStr), IndexFlag: true, Incremental: true, AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 202: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, PartitionNames: yyS[yypt-2].item.([]model.CIStr), ColumnChoice: yyS[yypt-1].item.(model.ColumnChoice), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 203: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, - PartitionNames: yyS[yypt-3].item.([]model.CIStr), - IndexNames: yyS[yypt-1].item.([]model.CIStr), - IndexFlag: true, - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), - } - } - case 204: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, - PartitionNames: yyS[yypt-3].item.([]model.CIStr), - IndexNames: yyS[yypt-1].item.([]model.CIStr), - IndexFlag: true, - Incremental: true, - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), - } - } - case 205: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, - ColumnNames: yyS[yypt-1].item.([]model.CIStr), - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), - HistogramOperation: ast.HistogramOperationUpdate, - } - } - case 206: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, - ColumnNames: yyS[yypt-0].item.([]model.CIStr), - HistogramOperation: ast.HistogramOperationDrop, - } - } - case 207: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, - ColumnNames: yyS[yypt-1].item.([]model.CIStr), - ColumnChoice: model.ColumnList, - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 208: - { - parser.yyVAL.statement = &ast.AnalyzeTableStmt{ - TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, - PartitionNames: yyS[yypt-3].item.([]model.CIStr), - ColumnNames: yyS[yypt-1].item.([]model.CIStr), - ColumnChoice: model.ColumnList, - AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} - } - case 209: - { - parser.yyVAL.item = model.DefaultChoice - } - case 210: - { - parser.yyVAL.item = model.AllColumns - } - case 211: - { - parser.yyVAL.item = model.PredicateColumns - } - case 212: - { - parser.yyVAL.item = []ast.AnalyzeOpt{} - } - case 213: - { - parser.yyVAL.item = yyS[yypt-0].item.([]ast.AnalyzeOpt) - } - case 214: - { - parser.yyVAL.item = []ast.AnalyzeOpt{yyS[yypt-0].item.(ast.AnalyzeOpt)} - } - case 215: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.AnalyzeOpt), yyS[yypt-0].item.(ast.AnalyzeOpt)) - } - case 216: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumBuckets, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} - } - case 217: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumTopN, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} - } - case 218: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchDepth, Value: ast.NewValueExpr(yyS[yypt-2].item, "", "")} - } - case 219: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchWidth, Value: ast.NewValueExpr(yyS[yypt-2].item, "", "")} - } - case 220: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumSamples, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} - } - case 221: - { - parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptSampleRate, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} - } - case 222: - { - parser.yyVAL.item = &ast.Assignment{Column: yyS[yypt-2].item.(*ast.ColumnName), Expr: yyS[yypt-0].expr} - } - case 223: - { - parser.yyVAL.item = []*ast.Assignment{yyS[yypt-0].item.(*ast.Assignment)} - } - case 224: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.Assignment), yyS[yypt-0].item.(*ast.Assignment)) - } - case 225: - { - parser.yyVAL.item = []*ast.Assignment{} - } - case 227: - { - parser.yyVAL.statement = &ast.BeginStmt{} - } - case 228: - { - parser.yyVAL.statement = &ast.BeginStmt{ - Mode: ast.Pessimistic, - } - } - case 229: - { - parser.yyVAL.statement = &ast.BeginStmt{ - Mode: ast.Optimistic, - } - } - case 230: - { - parser.yyVAL.statement = &ast.BeginStmt{} - } - case 231: - { - parser.yyVAL.statement = &ast.BeginStmt{} - } - case 232: - { - parser.yyVAL.statement = &ast.BeginStmt{} - } - case 233: - { - parser.yyVAL.statement = &ast.BeginStmt{ - CausalConsistencyOnly: true, - } - } - case 234: - { - parser.yyVAL.statement = &ast.BeginStmt{ - ReadOnly: true, - } - } - case 235: - { - parser.yyVAL.statement = &ast.BeginStmt{ - ReadOnly: true, - AsOf: yyS[yypt-0].item.(*ast.AsOfClause), - } - } - case 236: - { - parser.yyVAL.statement = &ast.BinlogStmt{Str: yyS[yypt-0].ident} - } - case 237: - { - parser.yyVAL.item = []*ast.ColumnDef{yyS[yypt-0].item.(*ast.ColumnDef)} - } - case 238: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnDef), yyS[yypt-0].item.(*ast.ColumnDef)) - } - case 239: - { - colDef := &ast.ColumnDef{Name: yyS[yypt-2].item.(*ast.ColumnName), Tp: yyS[yypt-1].item.(*types.FieldType), Options: yyS[yypt-0].item.([]*ast.ColumnOption)} - if err := colDef.Validate(); err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.item = colDef - } - case 240: - { - // TODO: check flen 0 - tp := types.NewFieldType(mysql.TypeLonglong) - options := []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} - options = append(options, yyS[yypt-0].item.([]*ast.ColumnOption)...) - tp.AddFlag(mysql.UnsignedFlag) - colDef := &ast.ColumnDef{Name: yyS[yypt-2].item.(*ast.ColumnName), Tp: tp, Options: options} - if err := colDef.Validate(); err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.item = colDef - } - case 241: - { - parser.yyVAL.item = &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 242: - { - parser.yyVAL.item = &ast.ColumnName{Table: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 243: - { - parser.yyVAL.item = &ast.ColumnName{Schema: model.NewCIStr(yyS[yypt-4].ident), Table: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 244: - { - parser.yyVAL.item = []*ast.ColumnName{yyS[yypt-0].item.(*ast.ColumnName)} - } - case 245: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnName), yyS[yypt-0].item.(*ast.ColumnName)) - } - case 246: - { - parser.yyVAL.item = []*ast.ColumnName{} - } - case 248: - { - parser.yyVAL.item = []model.CIStr{} - } - case 249: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 250: - { - parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} - } - case 251: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) - } - case 252: - { - parser.yyVAL.item = []*ast.ColumnNameOrUserVar{} - } - case 254: - { - parser.yyVAL.item = []*ast.ColumnNameOrUserVar{yyS[yypt-0].item.(*ast.ColumnNameOrUserVar)} - } - case 255: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnNameOrUserVar), yyS[yypt-0].item.(*ast.ColumnNameOrUserVar)) - } - case 256: - { - parser.yyVAL.item = &ast.ColumnNameOrUserVar{ColumnName: yyS[yypt-0].item.(*ast.ColumnName)} - } - case 257: - { - parser.yyVAL.item = &ast.ColumnNameOrUserVar{UserVar: yyS[yypt-0].expr.(*ast.VariableExpr)} - } - case 258: - { - parser.yyVAL.item = []*ast.ColumnNameOrUserVar{} - } - case 259: - { - parser.yyVAL.item = yyS[yypt-1].item.([]*ast.ColumnNameOrUserVar) - } - case 260: - { - parser.yyVAL.statement = &ast.CommitStmt{} - } - case 261: - { - parser.yyVAL.statement = &ast.CommitStmt{CompletionType: yyS[yypt-0].item.(ast.CompletionType)} - } - case 265: - { - parser.yyVAL.ident = "NOT" - } - case 266: - { - parser.yyVAL.item = true - } - case 267: - { - parser.yyVAL.item = false - } - case 268: - { - parser.yyVAL.item = true - } - case 270: - { - parser.yyVAL.item = 0 - } - case 271: - { - if yyS[yypt-0].item.(bool) { - parser.yyVAL.item = 1 - } else { - parser.yyVAL.item = 2 - } - } - case 272: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionNotNull} - } - case 273: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionNull} - } - case 274: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement} - } - case 275: - { - // KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY - // can also be specified as just KEY when given in a column definition. - // See http://dev.mysql.com/doc/refman/5.7/en/create-table.html - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey} - } - case 276: - { - // KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY - // can also be specified as just KEY when given in a column definition. - // See http://dev.mysql.com/doc/refman/5.7/en/create-table.html - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey, PrimaryKeyTp: yyS[yypt-0].item.(model.PrimaryKeyType)} - } - case 277: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} - } - case 278: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} - } - case 279: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionDefaultValue, Expr: yyS[yypt-0].expr} - } - case 280: - { - parser.yyVAL.item = []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} - } - case 281: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionOnUpdate, Expr: yyS[yypt-0].expr} - } - case 282: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionComment, Expr: ast.NewValueExpr(yyS[yypt-0].ident, "", "")} - } - case 283: - { - // See https://dev.mysql.com/doc/refman/5.7/en/create-table.html - // The CHECK clause is parsed but ignored by all storage engines. - // See the branch named `EnforcedOrNotOrNotNullOpt`. - - optionCheck := &ast.ColumnOption{ - Tp: ast.ColumnOptionCheck, - Expr: yyS[yypt-2].expr, - Enforced: true, - } - // Keep the column type check constraint name. - if yyS[yypt-5].item != nil { - optionCheck.ConstraintName = yyS[yypt-5].item.(string) - } - switch yyS[yypt-0].item.(int) { - case 0: - parser.yyVAL.item = []*ast.ColumnOption{optionCheck, {Tp: ast.ColumnOptionNotNull}} - case 1: - optionCheck.Enforced = true - parser.yyVAL.item = optionCheck - case 2: - optionCheck.Enforced = false - parser.yyVAL.item = optionCheck - default: - } - } - case 284: - { - startOffset := parser.startOffset(&yyS[yypt-2]) - endOffset := parser.endOffset(&yyS[yypt-1]) - expr := yyS[yypt-2].expr - expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - - parser.yyVAL.item = &ast.ColumnOption{ - Tp: ast.ColumnOptionGenerated, - Expr: expr, - Stored: yyS[yypt-0].item.(bool), - } - } - case 285: - { - parser.yyVAL.item = &ast.ColumnOption{ - Tp: ast.ColumnOptionReference, - Refer: yyS[yypt-0].item.(*ast.ReferenceDef), - } - } - case 286: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionCollate, StrValue: yyS[yypt-0].ident} - } - case 287: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionColumnFormat, StrValue: yyS[yypt-0].ident} - } - case 288: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionStorage, StrValue: yyS[yypt-0].ident} - yylex.AppendError(yylex.Errorf("The STORAGE clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 289: - { - parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionAutoRandom, AutoRandOpt: yyS[yypt-0].item.(ast.AutoRandomOption)} - } - case 290: - { - parser.yyVAL.item = ast.AutoRandomOption{ShardBits: types.UnspecifiedLength, RangeBits: types.UnspecifiedLength} - } - case 291: - { - parser.yyVAL.item = ast.AutoRandomOption{ShardBits: int(yyS[yypt-1].item.(uint64)), RangeBits: types.UnspecifiedLength} - } - case 292: - { - parser.yyVAL.item = ast.AutoRandomOption{ShardBits: int(yyS[yypt-3].item.(uint64)), RangeBits: int(yyS[yypt-1].item.(uint64))} - } - case 296: - { - parser.yyVAL.ident = "DEFAULT" - } - case 297: - { - parser.yyVAL.ident = "FIXED" - } - case 298: - { - parser.yyVAL.ident = "DYNAMIC" - } - case 301: - { - parser.yyVAL.item = false - } - case 302: - { - parser.yyVAL.item = false - } - case 303: - { - parser.yyVAL.item = true - } - case 304: - { - if columnOption, ok := yyS[yypt-0].item.(*ast.ColumnOption); ok { - parser.yyVAL.item = []*ast.ColumnOption{columnOption} - } else { - parser.yyVAL.item = yyS[yypt-0].item - } - } - case 305: - { - if columnOption, ok := yyS[yypt-0].item.(*ast.ColumnOption); ok { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ColumnOption), columnOption) - } else { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ColumnOption), yyS[yypt-0].item.([]*ast.ColumnOption)...) - } - } - case 306: - { - parser.yyVAL.item = []*ast.ColumnOption{} - } - case 308: - { - c := &ast.Constraint{ - Tp: ast.ConstraintPrimaryKey, - Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, - IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, - } - if yyS[yypt-0].item != nil { - c.Option = yyS[yypt-0].item.(*ast.IndexOption) - } - if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { - if c.Option == nil { - c.Option = &ast.IndexOption{} - } - c.Option.Tp = indexType.(model.IndexType) - } - parser.yyVAL.item = c - } - case 309: - { - c := &ast.Constraint{ - Tp: ast.ConstraintFulltext, - Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - Name: yyS[yypt-4].item.(*ast.NullString).String, - IsEmptyIndex: yyS[yypt-4].item.(*ast.NullString).Empty, - } - if yyS[yypt-0].item != nil { - c.Option = yyS[yypt-0].item.(*ast.IndexOption) - } - parser.yyVAL.item = c - } - case 310: - { - c := &ast.Constraint{ - IfNotExists: yyS[yypt-5].item.(bool), - Tp: ast.ConstraintIndex, - Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, - IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, - } - if yyS[yypt-0].item != nil { - c.Option = yyS[yypt-0].item.(*ast.IndexOption) - } - if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { - if c.Option == nil { - c.Option = &ast.IndexOption{} - } - c.Option.Tp = indexType.(model.IndexType) - } - parser.yyVAL.item = c - } - case 311: - { - c := &ast.Constraint{ - Tp: ast.ConstraintUniq, - Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, - IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, - } - if yyS[yypt-0].item != nil { - c.Option = yyS[yypt-0].item.(*ast.IndexOption) - } - - if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { - if c.Option == nil { - c.Option = &ast.IndexOption{} - } - c.Option.Tp = indexType.(model.IndexType) - } - parser.yyVAL.item = c - } - case 312: - { - parser.yyVAL.item = &ast.Constraint{ - IfNotExists: yyS[yypt-5].item.(bool), - Tp: ast.ConstraintForeignKey, - Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - Name: yyS[yypt-4].item.(*ast.NullString).String, - Refer: yyS[yypt-0].item.(*ast.ReferenceDef), - IsEmptyIndex: yyS[yypt-4].item.(*ast.NullString).Empty, - } - } - case 313: - { - parser.yyVAL.item = &ast.Constraint{ - Tp: ast.ConstraintCheck, - Expr: yyS[yypt-2].expr.(ast.ExprNode), - Enforced: yyS[yypt-0].item.(bool), - } - } - case 314: - { - parser.yyVAL.item = ast.MatchFull - } - case 315: - { - parser.yyVAL.item = ast.MatchPartial - } - case 316: - { - parser.yyVAL.item = ast.MatchSimple - } - case 317: - { - parser.yyVAL.item = ast.MatchNone - } - case 318: - { - parser.yyVAL.item = yyS[yypt-0].item - yylex.AppendError(yylex.Errorf("The MATCH clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 319: - { - onDeleteUpdate := yyS[yypt-0].item.([2]interface{}) - parser.yyVAL.item = &ast.ReferenceDef{ - Table: yyS[yypt-3].item.(*ast.TableName), - IndexPartSpecifications: yyS[yypt-2].item.([]*ast.IndexPartSpecification), - OnDelete: onDeleteUpdate[0].(*ast.OnDeleteOpt), - OnUpdate: onDeleteUpdate[1].(*ast.OnUpdateOpt), - Match: yyS[yypt-1].item.(ast.MatchType), - } - } - case 320: - { - parser.yyVAL.item = &ast.OnDeleteOpt{ReferOpt: yyS[yypt-0].item.(model.ReferOptionType)} - } - case 321: - { - parser.yyVAL.item = &ast.OnUpdateOpt{ReferOpt: yyS[yypt-0].item.(model.ReferOptionType)} - } - case 322: - { - parser.yyVAL.item = [2]interface{}{&ast.OnDeleteOpt{}, &ast.OnUpdateOpt{}} - } - case 323: - { - parser.yyVAL.item = [2]interface{}{yyS[yypt-0].item, &ast.OnUpdateOpt{}} - } - case 324: - { - parser.yyVAL.item = [2]interface{}{&ast.OnDeleteOpt{}, yyS[yypt-0].item} - } - case 325: - { - parser.yyVAL.item = [2]interface{}{yyS[yypt-1].item, yyS[yypt-0].item} - } - case 326: - { - parser.yyVAL.item = [2]interface{}{yyS[yypt-0].item, yyS[yypt-1].item} - } - case 327: - { - parser.yyVAL.item = model.ReferOptionRestrict - } - case 328: - { - parser.yyVAL.item = model.ReferOptionCascade - } - case 329: - { - parser.yyVAL.item = model.ReferOptionSetNull - } - case 330: - { - parser.yyVAL.item = model.ReferOptionNoAction - } - case 331: - { - parser.yyVAL.item = model.ReferOptionSetDefault - yylex.AppendError(yylex.Errorf("The SET DEFAULT clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 336: - { - parser.yyVAL.expr = yyS[yypt-1].expr.(*ast.FuncCallExpr) - } - case 337: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-2].ident), - } - } - case 338: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 339: - { - parser.yyVAL.expr = yyS[yypt-1].expr.(*ast.FuncCallExpr) - } - case 341: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} - } - case 342: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} - } - case 343: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: []ast.ExprNode{ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}} - } - case 344: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_DATE")} - } - case 345: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_DATE")} - } - case 346: - { - objNameExpr := &ast.TableNameExpr{ - Name: yyS[yypt-0].item.(*ast.TableName), - } - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.NextVal), - Args: []ast.ExprNode{objNameExpr}, - } - } - case 347: - { - objNameExpr := &ast.TableNameExpr{ - Name: yyS[yypt-1].item.(*ast.TableName), - } - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.NextVal), - Args: []ast.ExprNode{objNameExpr}, - } - } - case 357: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) - } - case 358: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Plus, V: ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation)} - } - case 359: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation)} - } - case 363: - { - parser.yyVAL.item = ast.StatsTypeCardinality - } - case 364: - { - parser.yyVAL.item = ast.StatsTypeDependency - } - case 365: - { - parser.yyVAL.item = ast.StatsTypeCorrelation - } - case 366: - { - parser.yyVAL.item = ast.BindingStatusTypeEnabled - } - case 367: - { - parser.yyVAL.item = ast.BindingStatusTypeDisabled - } - case 368: - { - parser.yyVAL.statement = &ast.CreateStatisticsStmt{ - IfNotExists: yyS[yypt-9].item.(bool), - StatsName: yyS[yypt-8].ident, - StatsType: yyS[yypt-6].item.(uint8), - Table: yyS[yypt-3].item.(*ast.TableName), - Columns: yyS[yypt-1].item.([]*ast.ColumnName), - } - } - case 369: - { - parser.yyVAL.statement = &ast.DropStatisticsStmt{StatsName: yyS[yypt-0].ident} - } - case 370: - { - var indexOption *ast.IndexOption - if yyS[yypt-1].item != nil { - indexOption = yyS[yypt-1].item.(*ast.IndexOption) - if indexOption.Tp == model.IndexTypeInvalid { - if yyS[yypt-7].item != nil { - indexOption.Tp = yyS[yypt-7].item.(model.IndexType) - } - } - } else { - indexOption = &ast.IndexOption{} - if yyS[yypt-7].item != nil { - indexOption.Tp = yyS[yypt-7].item.(model.IndexType) - } - } - var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm - if yyS[yypt-0].item != nil { - indexLockAndAlgorithm = yyS[yypt-0].item.(*ast.IndexLockAndAlgorithm) - if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { - indexLockAndAlgorithm = nil - } - } - parser.yyVAL.statement = &ast.CreateIndexStmt{ - IfNotExists: yyS[yypt-9].item.(bool), - IndexName: yyS[yypt-8].ident, - Table: yyS[yypt-5].item.(*ast.TableName), - IndexPartSpecifications: yyS[yypt-3].item.([]*ast.IndexPartSpecification), - IndexOption: indexOption, - KeyType: yyS[yypt-11].item.(ast.IndexKeyType), - LockAlg: indexLockAndAlgorithm, - } - } - case 371: - { - parser.yyVAL.item = ([]*ast.IndexPartSpecification)(nil) - } - case 372: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 373: - { - parser.yyVAL.item = []*ast.IndexPartSpecification{yyS[yypt-0].item.(*ast.IndexPartSpecification)} - } - case 374: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.IndexPartSpecification), yyS[yypt-0].item.(*ast.IndexPartSpecification)) - } - case 375: - { - parser.yyVAL.item = &ast.IndexPartSpecification{Column: yyS[yypt-2].item.(*ast.ColumnName), Length: yyS[yypt-1].item.(int), Desc: yyS[yypt-0].item.(bool)} - } - case 376: - { - parser.yyVAL.item = &ast.IndexPartSpecification{Expr: yyS[yypt-2].expr, Desc: yyS[yypt-0].item.(bool)} - } - case 377: - { - parser.yyVAL.item = nil - } - case 378: - { - parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ - LockTp: yyS[yypt-0].item.(ast.LockType), - AlgorithmTp: ast.AlgorithmTypeDefault, - } - } - case 379: - { - parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ - LockTp: ast.LockTypeDefault, - AlgorithmTp: yyS[yypt-0].item.(ast.AlgorithmType), - } - } - case 380: - { - parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ - LockTp: yyS[yypt-1].item.(ast.LockType), - AlgorithmTp: yyS[yypt-0].item.(ast.AlgorithmType), - } - } - case 381: - { - parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ - LockTp: yyS[yypt-0].item.(ast.LockType), - AlgorithmTp: yyS[yypt-1].item.(ast.AlgorithmType), - } - } - case 382: - { - parser.yyVAL.item = ast.IndexKeyTypeNone - } - case 383: - { - parser.yyVAL.item = ast.IndexKeyTypeUnique - } - case 384: - { - parser.yyVAL.item = ast.IndexKeyTypeSpatial - } - case 385: - { - parser.yyVAL.item = ast.IndexKeyTypeFullText - } - case 386: - { - parser.yyVAL.statement = &ast.AlterDatabaseStmt{ - Name: model.NewCIStr(yyS[yypt-1].ident), - AlterDefaultDatabase: false, - Options: yyS[yypt-0].item.([]*ast.DatabaseOption), - } - } - case 387: - { - parser.yyVAL.statement = &ast.AlterDatabaseStmt{ - Name: model.NewCIStr(""), - AlterDefaultDatabase: true, - Options: yyS[yypt-0].item.([]*ast.DatabaseOption), - } - } - case 388: - { - parser.yyVAL.statement = &ast.CreateDatabaseStmt{ - IfNotExists: yyS[yypt-2].item.(bool), - Name: model.NewCIStr(yyS[yypt-1].ident), - Options: yyS[yypt-0].item.([]*ast.DatabaseOption), - } - } - case 393: - { - parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionCharset, Value: yyS[yypt-0].ident} - } - case 394: - { - parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionCollate, Value: yyS[yypt-0].ident} - } - case 395: - { - parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionEncryption, Value: yyS[yypt-0].ident} - } - case 396: - { - placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) - parser.yyVAL.item = &ast.DatabaseOption{ - // offset trick, enums are identical but of different type - Tp: ast.DatabaseOptionType(placementOptions.Tp), - Value: placementOptions.StrValue, - UintValue: placementOptions.UintValue, - } - } - case 397: - { - placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) - parser.yyVAL.item = &ast.DatabaseOption{ - // offset trick, enums are identical but of different type - Tp: ast.DatabaseOptionType(placementOptions.Tp), - Value: placementOptions.StrValue, - UintValue: placementOptions.UintValue, - } - } - case 398: - { - tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ - Count: yyS[yypt-1].item.(uint64), - Labels: yyS[yypt-0].item.([]string), - } - parser.yyVAL.item = &ast.DatabaseOption{ - Tp: ast.DatabaseSetTiFlashReplica, - TiFlashReplica: tiflashReplicaSpec, - } - } - case 399: - { - parser.yyVAL.item = []*ast.DatabaseOption{} - } - case 401: - { - parser.yyVAL.item = []*ast.DatabaseOption{yyS[yypt-0].item.(*ast.DatabaseOption)} - } - case 402: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.DatabaseOption), yyS[yypt-0].item.(*ast.DatabaseOption)) - } - case 403: - { - stmt := yyS[yypt-6].item.(*ast.CreateTableStmt) - stmt.Table = yyS[yypt-7].item.(*ast.TableName) - stmt.IfNotExists = yyS[yypt-8].item.(bool) - stmt.TemporaryKeyword = yyS[yypt-10].item.(ast.TemporaryKeyword) - stmt.Options = yyS[yypt-5].item.([]*ast.TableOption) - if yyS[yypt-4].item != nil { - stmt.Partition = yyS[yypt-4].item.(*ast.PartitionOptions) - } - stmt.OnDuplicate = yyS[yypt-3].item.(ast.OnDuplicateKeyHandlingType) - stmt.Select = yyS[yypt-1].item.(*ast.CreateTableStmt).Select - if (yyS[yypt-0].item != nil && stmt.TemporaryKeyword != ast.TemporaryGlobal) || (stmt.TemporaryKeyword == ast.TemporaryGlobal && yyS[yypt-0].item == nil) { - yylex.AppendError(yylex.Errorf("GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together")) - } else { - if stmt.TemporaryKeyword == ast.TemporaryGlobal { - stmt.OnCommitDelete = yyS[yypt-0].item.(bool) - } - } - parser.yyVAL.statement = stmt - } - case 404: - { - tmp := &ast.CreateTableStmt{ - Table: yyS[yypt-2].item.(*ast.TableName), - ReferTable: yyS[yypt-1].item.(*ast.TableName), - IfNotExists: yyS[yypt-3].item.(bool), - TemporaryKeyword: yyS[yypt-5].item.(ast.TemporaryKeyword), - } - if (yyS[yypt-0].item != nil && tmp.TemporaryKeyword != ast.TemporaryGlobal) || (tmp.TemporaryKeyword == ast.TemporaryGlobal && yyS[yypt-0].item == nil) { - yylex.AppendError(yylex.Errorf("GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together")) - } else { - if tmp.TemporaryKeyword == ast.TemporaryGlobal { - tmp.OnCommitDelete = yyS[yypt-0].item.(bool) - } - } - parser.yyVAL.statement = tmp - } - case 405: - { - parser.yyVAL.item = nil - } - case 406: - { - parser.yyVAL.item = true - } - case 407: - { - parser.yyVAL.item = false - } - case 410: - { - parser.yyVAL.item = nil - } - case 411: - { - method := yyS[yypt-3].item.(*ast.PartitionMethod) - method.Num = yyS[yypt-2].item.(uint64) - sub, _ := yyS[yypt-1].item.(*ast.PartitionMethod) - defs, _ := yyS[yypt-0].item.([]*ast.PartitionDefinition) - opt := &ast.PartitionOptions{ - PartitionMethod: *method, - Sub: sub, - Definitions: defs, - } - if err := opt.Validate(); err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.item = opt - } - case 412: - { - keyAlgorithm, _ := yyS[yypt-3].item.(*ast.PartitionKeyAlgorithm) - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeKey, - Linear: len(yyS[yypt-5].ident) != 0, - ColumnNames: yyS[yypt-1].item.([]*ast.ColumnName), - KeyAlgorithm: keyAlgorithm, - } - } - case 413: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeHash, - Linear: len(yyS[yypt-4].ident) != 0, - Expr: yyS[yypt-1].expr.(ast.ExprNode), - } - } - case 414: - { - parser.yyVAL.item = nil - } - case 415: - { - tp := getUint64FromNUM(yyS[yypt-0].item) - if tp != 1 && tp != 2 { - yylex.AppendError(ErrSyntax) - return 1 - } - parser.yyVAL.item = &ast.PartitionKeyAlgorithm{ - Type: tp, - } - } - case 417: - { - partitionInterval, _ := yyS[yypt-0].item.(*ast.PartitionInterval) - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeRange, - Expr: yyS[yypt-2].expr.(ast.ExprNode), - Interval: partitionInterval, - } - } - case 418: - { - partitionInterval, _ := yyS[yypt-0].item.(*ast.PartitionInterval) - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeRange, - ColumnNames: yyS[yypt-2].item.([]*ast.ColumnName), - Interval: partitionInterval, - } - } - case 419: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeList, - Expr: yyS[yypt-1].expr.(ast.ExprNode), - } - } - case 420: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeList, - ColumnNames: yyS[yypt-1].item.([]*ast.ColumnName), - } - } - case 421: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeSystemTime, - Expr: yyS[yypt-1].expr.(ast.ExprNode), - Unit: yyS[yypt-0].item.(ast.TimeUnitType), - } - } - case 422: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeSystemTime, - Limit: yyS[yypt-0].item.(uint64), - } - } - case 423: - { - parser.yyVAL.item = &ast.PartitionMethod{ - Tp: model.PartitionTypeSystemTime, - } - } - case 424: - { - parser.yyVAL.item = nil - } - case 425: - { - partitionInterval := &ast.PartitionInterval{ - IntervalExpr: yyS[yypt-4].item.(ast.PartitionIntervalExpr), - FirstRangeEnd: yyS[yypt-2].item.(ast.PartitionInterval).FirstRangeEnd, - LastRangeEnd: yyS[yypt-2].item.(ast.PartitionInterval).LastRangeEnd, - NullPart: yyS[yypt-1].item.(bool), - MaxValPart: yyS[yypt-0].item.(bool), - } - startOffset := parser.yyVAL.offset - endOffset := parser.yylval.offset - partitionInterval.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - // Needed for replacing syntactic sugar with generated partitioning definition string - partitionInterval.SetOriginTextPosition(startOffset) - parser.yyVAL.item = partitionInterval - } - case 426: - { - parser.yyVAL.item = ast.PartitionIntervalExpr{Expr: yyS[yypt-0].expr, TimeUnit: ast.TimeUnitInvalid} - } - case 427: - { - parser.yyVAL.item = ast.PartitionIntervalExpr{Expr: yyS[yypt-1].expr, TimeUnit: yyS[yypt-0].item.(ast.TimeUnitType)} - } - case 428: - { - parser.yyVAL.item = false - } - case 429: - { - parser.yyVAL.item = true - } - case 430: - { - parser.yyVAL.item = false - } - case 431: - { - parser.yyVAL.item = true - } - case 432: - { - parser.yyVAL.item = ast.PartitionInterval{} // First/LastRangeEnd defaults to nil - } - case 433: - { - first := yyS[yypt-8].expr.(ast.ExprNode) - last := yyS[yypt-1].expr.(ast.ExprNode) - parser.yyVAL.item = ast.PartitionInterval{ - FirstRangeEnd: &first, - LastRangeEnd: &last, - } - } - case 434: - { - parser.yyVAL.ident = "" - } - case 436: - { - parser.yyVAL.item = nil - } - case 437: - { - method := yyS[yypt-1].item.(*ast.PartitionMethod) - method.Num = yyS[yypt-0].item.(uint64) - parser.yyVAL.item = method - } - case 438: - { - parser.yyVAL.item = uint64(0) - } - case 439: - { - res := yyS[yypt-0].item.(uint64) - if res == 0 { - yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("subpartitions")) - return 1 - } - parser.yyVAL.item = res - } - case 440: - { - parser.yyVAL.item = uint64(0) - } - case 441: - { - res := yyS[yypt-0].item.(uint64) - if res == 0 { - yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("partitions")) - return 1 - } - parser.yyVAL.item = res - } - case 442: - { - parser.yyVAL.item = nil - } - case 443: - { - parser.yyVAL.item = yyS[yypt-1].item.([]*ast.PartitionDefinition) - } - case 444: - { - parser.yyVAL.item = []*ast.PartitionDefinition{yyS[yypt-0].item.(*ast.PartitionDefinition)} - } - case 445: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.PartitionDefinition), yyS[yypt-0].item.(*ast.PartitionDefinition)) - } - case 446: - { - parser.yyVAL.item = &ast.PartitionDefinition{ - Name: model.NewCIStr(yyS[yypt-3].ident), - Clause: yyS[yypt-2].item.(ast.PartitionDefinitionClause), - Options: yyS[yypt-1].item.([]*ast.TableOption), - Sub: yyS[yypt-0].item.([]*ast.SubPartitionDefinition), - } - } - case 447: - { - parser.yyVAL.item = make([]*ast.SubPartitionDefinition, 0) - } - case 448: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 449: - { - parser.yyVAL.item = []*ast.SubPartitionDefinition{yyS[yypt-0].item.(*ast.SubPartitionDefinition)} - } - case 450: - { - list := yyS[yypt-2].item.([]*ast.SubPartitionDefinition) - parser.yyVAL.item = append(list, yyS[yypt-0].item.(*ast.SubPartitionDefinition)) - } - case 451: - { - parser.yyVAL.item = &ast.SubPartitionDefinition{ - Name: model.NewCIStr(yyS[yypt-1].ident), - Options: yyS[yypt-0].item.([]*ast.TableOption), - } - } - case 452: - { - parser.yyVAL.item = make([]*ast.TableOption, 0) - } - case 453: - { - list := yyS[yypt-1].item.([]*ast.TableOption) - parser.yyVAL.item = append(list, yyS[yypt-0].item.(*ast.TableOption)) - } - case 454: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionComment, StrValue: yyS[yypt-0].ident} - } - case 455: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: yyS[yypt-0].ident} - } - case 456: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: yyS[yypt-0].ident} - } - case 457: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionInsertMethod, StrValue: yyS[yypt-0].ident} - } - case 458: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionDataDirectory, StrValue: yyS[yypt-0].ident} - } - case 459: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionIndexDirectory, StrValue: yyS[yypt-0].ident} - } - case 460: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionMaxRows, UintValue: yyS[yypt-0].item.(uint64)} - } - case 461: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionMinRows, UintValue: yyS[yypt-0].item.(uint64)} - } - case 462: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionTablespace, StrValue: yyS[yypt-0].ident} - } - case 463: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionNodegroup, UintValue: yyS[yypt-0].item.(uint64)} - } - case 464: - { - placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) - parser.yyVAL.item = &ast.TableOption{ - // offset trick, enums are identical but of different type - Tp: ast.TableOptionType(placementOptions.Tp), - StrValue: placementOptions.StrValue, - UintValue: placementOptions.UintValue, - } - } - case 465: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseNone{} - } - case 466: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseLessThan{ - Exprs: []ast.ExprNode{&ast.MaxValueExpr{}}, - } - } - case 467: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseLessThan{ - Exprs: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 468: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseIn{ - Values: [][]ast.ExprNode{{&ast.DefaultExpr{}}}, - } - } - case 469: - { - exprs := yyS[yypt-1].item.([]ast.ExprNode) - values := make([][]ast.ExprNode, 0, len(exprs)) - for _, expr := range exprs { - if row, ok := expr.(*ast.RowExpr); ok { - values = append(values, row.Values) - } else { - values = append(values, []ast.ExprNode{expr}) - } - } - parser.yyVAL.item = &ast.PartitionDefinitionClauseIn{Values: values} - } - case 470: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseHistory{Current: false} - } - case 471: - { - parser.yyVAL.item = &ast.PartitionDefinitionClauseHistory{Current: true} - } - case 472: - { - parser.yyVAL.item = ast.OnDuplicateKeyHandlingError - } - case 473: - { - parser.yyVAL.item = ast.OnDuplicateKeyHandlingIgnore - } - case 474: - { - parser.yyVAL.item = ast.OnDuplicateKeyHandlingReplace - } - case 477: - { - parser.yyVAL.item = &ast.CreateTableStmt{} - } - case 478: - { - parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 479: - { - parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 480: - { - parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 481: - { - var sel ast.ResultSetNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.item = &ast.CreateTableStmt{Select: sel} - } - case 485: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 486: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 487: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 488: - { - startOffset := parser.startOffset(&yyS[yypt-1]) - selStmt := yyS[yypt-1].statement.(ast.StmtNode) - selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - x := &ast.CreateViewStmt{ - OrReplace: yyS[yypt-9].item.(bool), - ViewName: yyS[yypt-4].item.(*ast.TableName), - Select: selStmt, - Algorithm: yyS[yypt-8].item.(model.ViewAlgorithm), - Definer: yyS[yypt-7].item.(*auth.UserIdentity), - Security: yyS[yypt-6].item.(model.ViewSecurity), - } - if yyS[yypt-3].item != nil { - x.Cols = yyS[yypt-3].item.([]model.CIStr) - } - if yyS[yypt-0].item != nil { - x.CheckOption = yyS[yypt-0].item.(model.ViewCheckOption) - endOffset := parser.startOffset(&yyS[yypt]) - selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) - } else { - x.CheckOption = model.CheckOptionCascaded - } - parser.yyVAL.statement = x - } - case 489: - { - parser.yyVAL.item = false - } - case 490: - { - parser.yyVAL.item = true - } - case 491: - { - parser.yyVAL.item = model.AlgorithmUndefined - } - case 492: - { - parser.yyVAL.item = model.AlgorithmUndefined - } - case 493: - { - parser.yyVAL.item = model.AlgorithmMerge - } - case 494: - { - parser.yyVAL.item = model.AlgorithmTemptable - } - case 495: - { - parser.yyVAL.item = &auth.UserIdentity{CurrentUser: true} - } - case 496: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 497: - { - parser.yyVAL.item = model.SecurityDefiner - } - case 498: - { - parser.yyVAL.item = model.SecurityDefiner - } - case 499: - { - parser.yyVAL.item = model.SecurityInvoker - } - case 501: - { - parser.yyVAL.item = nil - } - case 502: - { - parser.yyVAL.item = yyS[yypt-1].item.([]model.CIStr) - } - case 503: - { - parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} - } - case 504: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) - } - case 505: - { - parser.yyVAL.item = nil - } - case 506: - { - parser.yyVAL.item = model.CheckOptionCascaded - } - case 507: - { - parser.yyVAL.item = model.CheckOptionLocal - } - case 508: - { - parser.yyVAL.statement = &ast.DoStmt{ - Exprs: yyS[yypt-0].item.([]ast.ExprNode), - } - } - case 509: - { - // Single Table - tn := yyS[yypt-6].item.(*ast.TableName) - tn.IndexHints = yyS[yypt-3].item.([]*ast.IndexHint) - tn.PartitionNames = yyS[yypt-5].item.([]model.CIStr) - join := &ast.Join{Left: &ast.TableSource{Source: tn, AsName: yyS[yypt-4].item.(model.CIStr)}, Right: nil} - x := &ast.DeleteStmt{ - TableRefs: &ast.TableRefsClause{TableRefs: join}, - Priority: yyS[yypt-10].item.(mysql.PriorityEnum), - Quick: yyS[yypt-9].item.(bool), - IgnoreErr: yyS[yypt-8].item.(bool), - } - if yyS[yypt-11].item != nil { - x.TableHints = yyS[yypt-11].item.([]*ast.TableOptimizerHint) - } - if yyS[yypt-2].item != nil { - x.Where = yyS[yypt-2].item.(ast.ExprNode) - } - if yyS[yypt-1].item != nil { - x.Order = yyS[yypt-1].item.(*ast.OrderByClause) - } - if yyS[yypt-0].item != nil { - x.Limit = yyS[yypt-0].item.(*ast.Limit) - } - - parser.yyVAL.statement = x - } - case 510: - { - // Multiple Table - x := &ast.DeleteStmt{ - Priority: yyS[yypt-6].item.(mysql.PriorityEnum), - Quick: yyS[yypt-5].item.(bool), - IgnoreErr: yyS[yypt-4].item.(bool), - IsMultiTable: true, - BeforeFrom: true, - Tables: &ast.DeleteTableList{Tables: yyS[yypt-3].item.([]*ast.TableName)}, - TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-1].item.(*ast.Join)}, - } - if yyS[yypt-7].item != nil { - x.TableHints = yyS[yypt-7].item.([]*ast.TableOptimizerHint) - } - if yyS[yypt-0].item != nil { - x.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = x - } - case 511: - { - // Multiple Table - x := &ast.DeleteStmt{ - Priority: yyS[yypt-7].item.(mysql.PriorityEnum), - Quick: yyS[yypt-6].item.(bool), - IgnoreErr: yyS[yypt-5].item.(bool), - IsMultiTable: true, - Tables: &ast.DeleteTableList{Tables: yyS[yypt-3].item.([]*ast.TableName)}, - TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-1].item.(*ast.Join)}, - } - if yyS[yypt-8].item != nil { - x.TableHints = yyS[yypt-8].item.([]*ast.TableOptimizerHint) - } - if yyS[yypt-0].item != nil { - x.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = x - } - case 514: - { - d := yyS[yypt-0].statement.(*ast.DeleteStmt) - d.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = d - } - case 515: - { - d := yyS[yypt-0].statement.(*ast.DeleteStmt) - d.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = d - } - case 517: - { - parser.yyVAL.statement = &ast.DropDatabaseStmt{IfExists: yyS[yypt-1].item.(bool), Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 518: - { - var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm - if yyS[yypt-0].item != nil { - indexLockAndAlgorithm = yyS[yypt-0].item.(*ast.IndexLockAndAlgorithm) - if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { - indexLockAndAlgorithm = nil - } - } - parser.yyVAL.statement = &ast.DropIndexStmt{IfExists: yyS[yypt-4].item.(bool), IndexName: yyS[yypt-3].ident, Table: yyS[yypt-1].item.(*ast.TableName), LockAlg: indexLockAndAlgorithm} - } - case 519: - { - parser.yyVAL.statement = &ast.DropIndexStmt{IfExists: yyS[yypt-3].item.(bool), IndexName: yyS[yypt-2].ident, Table: yyS[yypt-0].item.(*ast.TableName), IsHypo: true} - } - case 520: - { - parser.yyVAL.statement = &ast.DropTableStmt{IfExists: yyS[yypt-2].item.(bool), Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: false, TemporaryKeyword: yyS[yypt-4].item.(ast.TemporaryKeyword)} - } - case 521: - { - parser.yyVAL.item = ast.TemporaryNone - } - case 522: - { - parser.yyVAL.item = ast.TemporaryLocal - } - case 523: - { - parser.yyVAL.item = ast.TemporaryGlobal - } - case 524: - { - parser.yyVAL.statement = &ast.DropTableStmt{Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: true} - } - case 525: - { - parser.yyVAL.statement = &ast.DropTableStmt{IfExists: true, Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: true} - } - case 526: - { - parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: false, IfExists: false, UserList: yyS[yypt-0].item.([]*auth.UserIdentity)} - } - case 527: - { - parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: false, IfExists: true, UserList: yyS[yypt-0].item.([]*auth.UserIdentity)} - } - case 528: - { - tmp := make([]*auth.UserIdentity, 0, 10) - roleList := yyS[yypt-0].item.([]*auth.RoleIdentity) - for _, r := range roleList { - tmp = append(tmp, &auth.UserIdentity{Username: r.Username, Hostname: r.Hostname}) - } - parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: true, IfExists: false, UserList: tmp} - } - case 529: - { - tmp := make([]*auth.UserIdentity, 0, 10) - roleList := yyS[yypt-0].item.([]*auth.RoleIdentity) - for _, r := range roleList { - tmp = append(tmp, &auth.UserIdentity{Username: r.Username, Hostname: r.Hostname}) - } - parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: true, IfExists: true, UserList: tmp} - } - case 530: - { - parser.yyVAL.statement = &ast.DropStatsStmt{Tables: yyS[yypt-0].item.([]*ast.TableName)} - } - case 531: - { - yylex.AppendError(ErrWarnDeprecatedSyntaxNoReplacement.FastGenByArgs("'DROP STATS ... PARTITION ...'", "")) - parser.lastErrorAsWarn() - parser.yyVAL.statement = &ast.DropStatsStmt{ - Tables: []*ast.TableName{yyS[yypt-2].item.(*ast.TableName)}, - PartitionNames: yyS[yypt-0].item.([]model.CIStr), - } - } - case 532: - { - yylex.AppendError(ErrWarnDeprecatedSyntax.FastGenByArgs("DROP STATS ... GLOBAL", "DROP STATS ...")) - parser.lastErrorAsWarn() - parser.yyVAL.statement = &ast.DropStatsStmt{ - Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, - IsGlobalStats: true, - } - } - case 540: - { - parser.yyVAL.statement = nil - } - case 541: - { - parser.yyVAL.statement = &ast.TraceStmt{ - Stmt: yyS[yypt-0].statement, - Format: "row", - TracePlan: false, - } - startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) - } - case 542: - { - parser.yyVAL.statement = &ast.TraceStmt{ - Stmt: yyS[yypt-0].statement, - Format: yyS[yypt-1].ident, - TracePlan: false, - } - startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) - } - case 543: - { - parser.yyVAL.statement = &ast.TraceStmt{ - Stmt: yyS[yypt-0].statement, - TracePlan: true, - } - startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) - } - case 544: - { - parser.yyVAL.statement = &ast.TraceStmt{ - Stmt: yyS[yypt-0].statement, - TracePlan: true, - TracePlanTarget: yyS[yypt-1].ident, - } - startOffset := parser.startOffset(&yyS[yypt]) - yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) - } - case 548: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: &ast.ShowStmt{ - Tp: ast.ShowColumns, - Table: yyS[yypt-0].item.(*ast.TableName), - }, - } - } - case 549: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: &ast.ShowStmt{ - Tp: ast.ShowColumns, - Table: yyS[yypt-1].item.(*ast.TableName), - Column: yyS[yypt-0].item.(*ast.ColumnName), - }, - } - } - case 550: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: "row", - } - } - case 551: - { - parser.yyVAL.statement = &ast.ExplainForStmt{ - Format: "row", - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 552: - { - parser.yyVAL.statement = &ast.ExplainForStmt{ - Format: yyS[yypt-3].ident, - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 553: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: yyS[yypt-1].ident, - } - } - case 554: - { - parser.yyVAL.statement = &ast.ExplainForStmt{ - Format: yyS[yypt-3].ident, - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 555: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: yyS[yypt-1].ident, - } - } - case 556: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: "row", - Analyze: true, - } - } - case 557: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: yyS[yypt-1].ident, - Analyze: true, - } - } - case 558: - { - parser.yyVAL.statement = &ast.ExplainStmt{ - Stmt: yyS[yypt-0].statement, - Format: yyS[yypt-1].ident, - Analyze: true, - } - } - case 567: - { - parser.yyVAL.statement = &ast.SavepointStmt{Name: yyS[yypt-0].ident} - } - case 568: - { - parser.yyVAL.statement = &ast.ReleaseSavepointStmt{Name: yyS[yypt-0].ident} - } - case 569: - { - stmt := yyS[yypt-3].item.(*ast.BRIEStmt) - stmt.Kind = ast.BRIEKindBackup - stmt.Storage = yyS[yypt-1].ident - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 570: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamStart - stmt.Storage = yyS[yypt-1].ident - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 571: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamStop - parser.yyVAL.statement = stmt - } - case 572: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamPause - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 573: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamResume - parser.yyVAL.statement = stmt - } - case 574: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamPurge - stmt.Storage = yyS[yypt-1].ident - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 575: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamStatus - parser.yyVAL.statement = stmt - } - case 576: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindStreamMetaData - stmt.Storage = yyS[yypt-0].ident - parser.yyVAL.statement = stmt - } - case 577: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindShowJob - stmt.JobID = yyS[yypt-0].item.(int64) - parser.yyVAL.statement = stmt - } - case 578: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindShowQuery - stmt.JobID = yyS[yypt-0].item.(int64) - parser.yyVAL.statement = stmt - } - case 579: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindCancelJob - stmt.JobID = yyS[yypt-0].item.(int64) - parser.yyVAL.statement = stmt - } - case 580: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindShowBackupMeta - stmt.Storage = yyS[yypt-0].ident - parser.yyVAL.statement = stmt - } - case 581: - { - stmt := yyS[yypt-3].item.(*ast.BRIEStmt) - stmt.Kind = ast.BRIEKindRestore - stmt.Storage = yyS[yypt-1].ident - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 582: - { - stmt := &ast.BRIEStmt{} - stmt.Kind = ast.BRIEKindRestorePIT - stmt.Storage = yyS[yypt-1].ident - stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) - parser.yyVAL.statement = stmt - } - case 583: - { - parser.yyVAL.item = &ast.BRIEStmt{} - } - case 584: - { - parser.yyVAL.item = &ast.BRIEStmt{Schemas: yyS[yypt-0].item.([]string)} - } - case 585: - { - parser.yyVAL.item = &ast.BRIEStmt{Tables: yyS[yypt-0].item.([]*ast.TableName)} - } - case 586: - { - parser.yyVAL.item = []string{yyS[yypt-0].ident} - } - case 587: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) - } - case 588: - { - parser.yyVAL.item = []*ast.BRIEOption{} - } - case 589: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.BRIEOption), yyS[yypt-0].item.(*ast.BRIEOption)) - } - case 590: - { - parser.yyVAL.item = ast.BRIEOptionConcurrency - } - case 591: - { - parser.yyVAL.item = ast.BRIEOptionResume - } - case 592: - { - parser.yyVAL.item = ast.BRIEOptionSendCreds - } - case 593: - { - parser.yyVAL.item = ast.BRIEOptionOnline - } - case 594: - { - parser.yyVAL.item = ast.BRIEOptionCheckpoint - } - case 595: - { - parser.yyVAL.item = ast.BRIEOptionSkipSchemaFiles - } - case 596: - { - parser.yyVAL.item = ast.BRIEOptionStrictFormat - } - case 597: - { - parser.yyVAL.item = ast.BRIEOptionCSVNotNull - } - case 598: - { - parser.yyVAL.item = ast.BRIEOptionCSVBackslashEscape - } - case 599: - { - parser.yyVAL.item = ast.BRIEOptionCSVTrimLastSeparators - } - case 600: - { - parser.yyVAL.item = ast.BRIEOptionTiKVImporter - } - case 601: - { - parser.yyVAL.item = ast.BRIEOptionCSVSeparator - } - case 602: - { - parser.yyVAL.item = ast.BRIEOptionCSVDelimiter - } - case 603: - { - parser.yyVAL.item = ast.BRIEOptionCSVNull - } - case 604: - { - parser.yyVAL.item = ast.BRIEOptionBackend - } - case 605: - { - parser.yyVAL.item = ast.BRIEOptionOnDuplicate - } - case 606: - { - parser.yyVAL.item = ast.BRIEOptionOnDuplicate - } - case 607: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: yyS[yypt-2].item.(ast.BRIEOptionType), - UintValue: yyS[yypt-0].item.(uint64), - } - } - case 608: - { - value := uint64(0) - if yyS[yypt-0].item.(bool) { - value = 1 - } - parser.yyVAL.item = &ast.BRIEOption{ - Tp: yyS[yypt-2].item.(ast.BRIEOptionType), - UintValue: value, - } - } - case 609: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: yyS[yypt-2].item.(ast.BRIEOptionType), - StrValue: yyS[yypt-0].ident, - } - } - case 610: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: yyS[yypt-2].item.(ast.BRIEOptionType), - StrValue: strings.ToLower(yyS[yypt-0].ident), - } - } - case 611: - { - unit, err := yyS[yypt-1].item.(ast.TimeUnitType).Duration() - if err != nil { - yylex.AppendError(err) - return 1 - } - // TODO: check overflow? - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionBackupTimeAgo, - UintValue: yyS[yypt-2].item.(uint64) * uint64(unit), - } - } - case 612: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionBackupTS, - StrValue: yyS[yypt-0].ident, - } - } - case 613: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionBackupTSO, - UintValue: yyS[yypt-0].item.(uint64), - } - } - case 614: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionLastBackupTS, - StrValue: yyS[yypt-0].ident, - } - } - case 615: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionLastBackupTSO, - UintValue: yyS[yypt-0].item.(uint64), - } - } - case 616: - { - // TODO: check overflow? - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionRateLimit, - UintValue: yyS[yypt-3].item.(uint64) * 1048576, - } - } - case 617: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionCSVHeader, - UintValue: ast.BRIECSVHeaderIsColumns, - } - } - case 618: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionCSVHeader, - UintValue: yyS[yypt-0].item.(uint64), - } - } - case 619: - { - value := uint64(0) - if yyS[yypt-0].item.(bool) { - value = 1 - } - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionChecksum, - UintValue: value, - } - } - case 620: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionChecksum, - UintValue: uint64(yyS[yypt-0].item.(ast.BRIEOptionLevel)), - } - } - case 621: - { - value := uint64(0) - if yyS[yypt-0].item.(bool) { - value = 1 - } - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionAnalyze, - UintValue: value, - } - } - case 622: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionAnalyze, - UintValue: uint64(yyS[yypt-0].item.(ast.BRIEOptionLevel)), - } - } - case 623: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionFullBackupStorage, - StrValue: yyS[yypt-0].ident, - } - } - case 624: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionRestoredTS, - StrValue: yyS[yypt-0].ident, - } - } - case 625: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionStartTS, - StrValue: yyS[yypt-0].ident, - } - } - case 626: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionUntilTS, - StrValue: yyS[yypt-0].ident, - } - } - case 627: - { - parser.yyVAL.item = &ast.BRIEOption{ - Tp: ast.BRIEOptionGCTTL, - StrValue: yyS[yypt-0].ident, - } - } - case 628: - { - parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) - } - case 629: - { - v, rangeErrMsg := getInt64FromNUM(yyS[yypt-0].item) - if len(rangeErrMsg) != 0 { - yylex.AppendError(yylex.Errorf(rangeErrMsg)) - return 1 - } - parser.yyVAL.item = v - } - case 631: - { - parser.yyVAL.item = yyS[yypt-0].item.(int64) != 0 - } - case 632: - { - parser.yyVAL.item = false - } - case 633: - { - parser.yyVAL.item = true - } - case 634: - { - parser.yyVAL.item = ast.BRIEOptionLevelOff - } - case 635: - { - parser.yyVAL.item = ast.BRIEOptionLevelOptional - } - case 636: - { - parser.yyVAL.item = ast.BRIEOptionLevelRequired - } - case 637: - { - parser.yyVAL.statement = &ast.LoadDataActionStmt{ - Tp: ast.LoadDataPause, - JobID: yyS[yypt-0].item.(int64), - } - } - case 638: - { - parser.yyVAL.statement = &ast.LoadDataActionStmt{ - Tp: ast.LoadDataResume, - JobID: yyS[yypt-0].item.(int64), - } - } - case 639: - { - parser.yyVAL.statement = &ast.ImportIntoActionStmt{ - Tp: ast.ImportIntoCancel, - JobID: yyS[yypt-0].item.(int64), - } - } - case 640: - { - parser.yyVAL.statement = &ast.LoadDataActionStmt{ - Tp: ast.LoadDataDrop, - JobID: yyS[yypt-0].item.(int64), - } - } - case 641: - { - v := yyS[yypt-2].ident - v = strings.TrimPrefix(v, "@") - parser.yyVAL.expr = &ast.VariableExpr{ - Name: v, - IsGlobal: false, - IsSystem: false, - Value: yyS[yypt-0].expr, - } - } - case 642: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicOr, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 643: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicXor, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 644: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicAnd, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 645: - { - expr, ok := yyS[yypt-0].expr.(*ast.ExistsSubqueryExpr) - if ok { - expr.Not = !expr.Not - parser.yyVAL.expr = yyS[yypt-0].expr - } else { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not, V: yyS[yypt-0].expr} - } - } - case 646: - { - parser.yyVAL.expr = &ast.MatchAgainst{ - ColumnNames: yyS[yypt-6].item.([]*ast.ColumnName), - Against: yyS[yypt-2].expr, - Modifier: ast.FulltextSearchModifier(yyS[yypt-1].item.(int)), - } - } - case 647: - { - parser.yyVAL.expr = &ast.IsTruthExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), True: int64(1)} - } - case 648: - { - parser.yyVAL.expr = &ast.IsTruthExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), True: int64(0)} - } - case 649: - { - /* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */ - parser.yyVAL.expr = &ast.IsNullExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool)} - } - case 651: - { - parser.yyVAL.expr = &ast.DefaultExpr{} - } - case 653: - { - parser.yyVAL.expr = &ast.MaxValueExpr{} - } - case 655: - { - parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode - } - case 656: - { - parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode - } - case 657: - { - parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode | ast.FulltextSearchModifierWithQueryExpansion - } - case 658: - { - parser.yyVAL.item = ast.FulltextSearchModifierBooleanMode - } - case 659: - { - parser.yyVAL.item = ast.FulltextSearchModifierWithQueryExpansion - } - case 664: - { - parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} - } - case 665: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) - } - case 666: - { - parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} - } - case 667: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) - } - case 668: - { - parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} - } - case 669: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) - } - case 670: - { - parser.yyVAL.item = []ast.ExprNode{} - } - case 672: - { - parser.yyVAL.item = []ast.ExprNode{} - } - case 674: - { - expr := ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - parser.yyVAL.item = []ast.ExprNode{expr} - } - case 675: - { - parser.yyVAL.expr = &ast.IsNullExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool)} - } - case 676: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: yyS[yypt-1].item.(opcode.Op), L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 677: - { - sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) - sq.MultiRows = true - parser.yyVAL.expr = &ast.CompareSubqueryExpr{Op: yyS[yypt-2].item.(opcode.Op), L: yyS[yypt-3].expr, R: sq, All: yyS[yypt-1].item.(bool)} - } - case 678: - { - v := yyS[yypt-2].ident - v = strings.TrimPrefix(v, "@") - variable := &ast.VariableExpr{ - Name: v, - IsGlobal: false, - IsSystem: false, - Value: yyS[yypt-0].expr, - } - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: yyS[yypt-3].item.(opcode.Op), L: yyS[yypt-4].expr, R: variable} - } - case 680: - { - parser.yyVAL.item = opcode.GE - } - case 681: - { - parser.yyVAL.item = opcode.GT - } - case 682: - { - parser.yyVAL.item = opcode.LE - } - case 683: - { - parser.yyVAL.item = opcode.LT - } - case 684: - { - parser.yyVAL.item = opcode.NE - } - case 685: - { - parser.yyVAL.item = opcode.NE - } - case 686: - { - parser.yyVAL.item = opcode.EQ - } - case 687: - { - parser.yyVAL.item = opcode.NullEQ - } - case 688: - { - parser.yyVAL.item = true - } - case 689: - { - parser.yyVAL.item = false - } - case 690: - { - parser.yyVAL.item = true - } - case 691: - { - parser.yyVAL.item = false - } - case 692: - { - parser.yyVAL.item = true - } - case 693: - { - parser.yyVAL.item = false - } - case 694: - { - parser.yyVAL.item = true - } - case 695: - { - parser.yyVAL.item = false - } - case 696: - { - parser.yyVAL.item = true - } - case 697: - { - parser.yyVAL.item = false - } - case 698: - { - parser.yyVAL.item = true - } - case 699: - { - parser.yyVAL.item = false - } - case 700: - { - parser.yyVAL.item = false - } - case 701: - { - parser.yyVAL.item = false - } - case 702: - { - parser.yyVAL.item = true - } - case 703: - { - parser.yyVAL.expr = &ast.PatternInExpr{Expr: yyS[yypt-4].expr, Not: !yyS[yypt-3].item.(bool), List: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 704: - { - sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) - sq.MultiRows = true - parser.yyVAL.expr = &ast.PatternInExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), Sel: sq} - } - case 705: - { - parser.yyVAL.expr = &ast.BetweenExpr{ - Expr: yyS[yypt-4].expr, - Left: yyS[yypt-2].expr, - Right: yyS[yypt-0].expr, - Not: !yyS[yypt-3].item.(bool), - } - } - case 706: - { - escape := yyS[yypt-0].ident - if len(escape) > 1 { - yylex.AppendError(ErrWrongArguments.GenWithStackByArgs("ESCAPE")) - return 1 - } else if len(escape) == 0 { - escape = "\\" - } - parser.yyVAL.expr = &ast.PatternLikeOrIlikeExpr{ - Expr: yyS[yypt-3].expr, - Pattern: yyS[yypt-1].expr, - Not: !yyS[yypt-2].item.(bool), - Escape: escape[0], - IsLike: true, - } - } - case 707: - { - escape := yyS[yypt-0].ident - if len(escape) > 1 { - yylex.AppendError(ErrWrongArguments.GenWithStackByArgs("ESCAPE")) - return 1 - } else if len(escape) == 0 { - escape = "\\" - } - parser.yyVAL.expr = &ast.PatternLikeOrIlikeExpr{ - Expr: yyS[yypt-3].expr, - Pattern: yyS[yypt-1].expr, - Not: !yyS[yypt-2].item.(bool), - Escape: escape[0], - IsLike: false, - } - } - case 708: - { - parser.yyVAL.expr = &ast.PatternRegexpExpr{Expr: yyS[yypt-2].expr, Pattern: yyS[yypt-0].expr, Not: !yyS[yypt-1].item.(bool)} - } - case 709: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONMemberOf), Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-1].expr}} - } - case 713: - { - parser.yyVAL.ident = "\\" - } - case 714: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 715: - { - parser.yyVAL.item = &ast.SelectField{WildCard: &ast.WildCardField{}} - } - case 716: - { - wildCard := &ast.WildCardField{Table: model.NewCIStr(yyS[yypt-2].ident)} - parser.yyVAL.item = &ast.SelectField{WildCard: wildCard} - } - case 717: - { - wildCard := &ast.WildCardField{Schema: model.NewCIStr(yyS[yypt-4].ident), Table: model.NewCIStr(yyS[yypt-2].ident)} - parser.yyVAL.item = &ast.SelectField{WildCard: wildCard} - } - case 718: - { - expr := yyS[yypt-1].expr - asName := yyS[yypt-0].ident - parser.yyVAL.item = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)} - } - case 719: - { - parser.yyVAL.ident = "" - } - case 722: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 724: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 725: - { - field := yyS[yypt-0].item.(*ast.SelectField) - field.Offset = parser.startOffset(&yyS[yypt]) - if field.Expr != nil { - endOffset := parser.yylval.offset - field.SetText(parser.lexer.client, strings.TrimSpace(parser.src[field.Offset:endOffset])) - } - parser.yyVAL.item = []*ast.SelectField{field} - } - case 726: - { - fl := yyS[yypt-2].item.([]*ast.SelectField) - field := yyS[yypt-0].item.(*ast.SelectField) - field.Offset = parser.startOffset(&yyS[yypt]) - if field.Expr != nil { - endOffset := parser.yylval.offset - field.SetText(parser.lexer.client, strings.TrimSpace(parser.src[field.Offset:endOffset])) - } - parser.yyVAL.item = append(fl, field) - } - case 727: - { - parser.yyVAL.item = false - } - case 728: - { - parser.yyVAL.item = true - } - case 729: - { - parser.yyVAL.item = &ast.GroupByClause{Items: yyS[yypt-1].item.([]*ast.ByItem), Rollup: yyS[yypt-0].item.(bool)} - } - case 730: - { - parser.yyVAL.item = nil - } - case 731: - { - parser.yyVAL.item = &ast.HavingClause{Expr: yyS[yypt-0].expr} - } - case 732: - { - parser.yyVAL.item = nil - } - case 734: - { - parser.yyVAL.item = &ast.AsOfClause{ - TsExpr: yyS[yypt-0].expr.(ast.ExprNode), - } - } - case 735: - { - parser.yyVAL.item = false - } - case 736: - { - parser.yyVAL.item = true - } - case 737: - { - parser.yyVAL.item = false - } - case 738: - { - parser.yyVAL.item = true - } - case 739: - { - parser.yyVAL.item = false - } - case 740: - { - parser.yyVAL.item = true - } - case 741: - { - parser.yyVAL.item = &ast.NullString{ - String: "", - Empty: false, - } - } - case 742: - { - parser.yyVAL.item = &ast.NullString{ - String: yyS[yypt-0].ident, - Empty: len(yyS[yypt-0].ident) == 0, - } - } - case 743: - { - parser.yyVAL.item = nil - } - case 744: - { - // Merge the options - if yyS[yypt-1].item == nil { - parser.yyVAL.item = yyS[yypt-0].item - } else { - opt1 := yyS[yypt-1].item.(*ast.IndexOption) - opt2 := yyS[yypt-0].item.(*ast.IndexOption) - if len(opt2.Comment) > 0 { - opt1.Comment = opt2.Comment - } else if opt2.Tp != 0 { - opt1.Tp = opt2.Tp - } else if opt2.KeyBlockSize > 0 { - opt1.KeyBlockSize = opt2.KeyBlockSize - } else if len(opt2.ParserName.O) > 0 { - opt1.ParserName = opt2.ParserName - } else if opt2.Visibility != ast.IndexVisibilityDefault { - opt1.Visibility = opt2.Visibility - } else if opt2.PrimaryKeyTp != model.PrimaryKeyTypeDefault { - opt1.PrimaryKeyTp = opt2.PrimaryKeyTp - } - parser.yyVAL.item = opt1 - } - } - case 745: - { - parser.yyVAL.item = &ast.IndexOption{ - KeyBlockSize: yyS[yypt-0].item.(uint64), - } - } - case 746: - { - parser.yyVAL.item = &ast.IndexOption{ - Tp: yyS[yypt-0].item.(model.IndexType), - } - } - case 747: - { - parser.yyVAL.item = &ast.IndexOption{ - ParserName: model.NewCIStr(yyS[yypt-0].ident), - } - yylex.AppendError(yylex.Errorf("The WITH PARASER clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 748: - { - parser.yyVAL.item = &ast.IndexOption{ - Comment: yyS[yypt-0].ident, - } - } - case 749: - { - parser.yyVAL.item = &ast.IndexOption{ - Visibility: yyS[yypt-0].item.(ast.IndexVisibility), - } - } - case 750: - { - parser.yyVAL.item = &ast.IndexOption{ - PrimaryKeyTp: yyS[yypt-0].item.(model.PrimaryKeyType), - } - } - case 751: - { - parser.yyVAL.item = []interface{}{yyS[yypt-0].item, nil} - } - case 752: - { - parser.yyVAL.item = []interface{}{yyS[yypt-2].item, yyS[yypt-0].item} - } - case 753: - { - parser.yyVAL.item = []interface{}{&ast.NullString{String: yyS[yypt-2].ident, Empty: len(yyS[yypt-2].ident) == 0}, yyS[yypt-0].item} - } - case 754: - { - parser.yyVAL.item = nil - } - case 756: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 757: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 758: - { - parser.yyVAL.item = model.IndexTypeBtree - } - case 759: - { - parser.yyVAL.item = model.IndexTypeHash - } - case 760: - { - parser.yyVAL.item = model.IndexTypeRtree - } - case 761: - { - parser.yyVAL.item = model.IndexTypeHypo - } - case 762: - { - parser.yyVAL.item = ast.IndexVisibilityVisible - } - case 763: - { - parser.yyVAL.item = ast.IndexVisibilityInvisible - } - case 1293: - { - parser.yyVAL.statement = &ast.CallStmt{ - Procedure: yyS[yypt-0].expr.(*ast.FuncCallExpr), - } - } - case 1294: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - Tp: ast.FuncCallExprTypeGeneric, - FnName: model.NewCIStr(yyS[yypt-0].ident), - Args: []ast.ExprNode{}, - } - } - case 1295: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - Tp: ast.FuncCallExprTypeGeneric, - Schema: model.NewCIStr(yyS[yypt-2].ident), - FnName: model.NewCIStr(yyS[yypt-0].ident), - Args: []ast.ExprNode{}, - } - } - case 1296: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - Tp: ast.FuncCallExprTypeGeneric, - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 1297: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - Tp: ast.FuncCallExprTypeGeneric, - Schema: model.NewCIStr(yyS[yypt-5].ident), - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 1298: - { - x := yyS[yypt-1].item.(*ast.InsertStmt) - x.Priority = yyS[yypt-6].item.(mysql.PriorityEnum) - x.IgnoreErr = yyS[yypt-5].item.(bool) - // Wraps many layers here so that it can be processed the same way as select statement. - ts := &ast.TableSource{Source: yyS[yypt-3].item.(*ast.TableName)} - x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} - if yyS[yypt-0].item != nil { - x.OnDuplicate = yyS[yypt-0].item.([]*ast.Assignment) - } - if yyS[yypt-7].item != nil { - x.TableHints = yyS[yypt-7].item.([]*ast.TableOptimizerHint) - } - x.PartitionNames = yyS[yypt-2].item.([]model.CIStr) - parser.yyVAL.statement = x - } - case 1301: - { - parser.yyVAL.item = &ast.InsertStmt{ - Columns: yyS[yypt-3].item.([]*ast.ColumnName), - Lists: yyS[yypt-0].item.([][]ast.ExprNode), - } - } - case 1302: - { - parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1303: - { - parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1304: - { - parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1305: - { - var sel ast.ResultSetNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: sel} - } - case 1306: - { - parser.yyVAL.item = &ast.InsertStmt{Lists: yyS[yypt-0].item.([][]ast.ExprNode)} - } - case 1307: - { - parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1308: - { - parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1309: - { - parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} - } - case 1310: - { - var sel ast.ResultSetNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.item = &ast.InsertStmt{Select: sel} - } - case 1311: - { - parser.yyVAL.item = yyS[yypt-0].item.(*ast.InsertStmt) - } - case 1314: - { - parser.yyVAL.item = [][]ast.ExprNode{yyS[yypt-0].item.([]ast.ExprNode)} - } - case 1315: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([][]ast.ExprNode), yyS[yypt-0].item.([]ast.ExprNode)) - } - case 1316: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 1317: - { - parser.yyVAL.item = []ast.ExprNode{} - } - case 1319: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) - } - case 1320: - { - parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} - } - case 1322: - { - parser.yyVAL.expr = &ast.DefaultExpr{} - } - case 1323: - { - parser.yyVAL.item = &ast.InsertStmt{ - Columns: []*ast.ColumnName{yyS[yypt-2].item.(*ast.ColumnName)}, - Lists: [][]ast.ExprNode{{yyS[yypt-0].expr.(ast.ExprNode)}}, - Setlist: true, - } - } - case 1324: - { - ins := yyS[yypt-4].item.(*ast.InsertStmt) - ins.Columns = append(ins.Columns, yyS[yypt-2].item.(*ast.ColumnName)) - ins.Lists[0] = append(ins.Lists[0], yyS[yypt-0].expr.(ast.ExprNode)) - parser.yyVAL.item = ins - } - case 1325: - { - parser.yyVAL.item = nil - } - case 1326: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 1327: - { - x := yyS[yypt-0].item.(*ast.InsertStmt) - x.IsReplace = true - x.Priority = yyS[yypt-4].item.(mysql.PriorityEnum) - ts := &ast.TableSource{Source: yyS[yypt-2].item.(*ast.TableName)} - x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} - x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) - parser.yyVAL.statement = x - } - case 1328: - { - parser.yyVAL.expr = ast.NewValueExpr(false, parser.charset, parser.collation) - } - case 1329: - { - parser.yyVAL.expr = ast.NewValueExpr(nil, parser.charset, parser.collation) - } - case 1330: - { - parser.yyVAL.expr = ast.NewValueExpr(true, parser.charset, parser.collation) - } - case 1331: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1332: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1333: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1335: - { - // See https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html - co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) - if err != nil { - yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) - return 1 - } - expr := ast.NewValueExpr(yyS[yypt-0].ident, yyS[yypt-1].ident, co) - tp := expr.GetType() - tp.SetCharset(yyS[yypt-1].ident) - tp.SetCollate(co) - tp.AddFlag(mysql.UnderScoreCharsetFlag) - if tp.GetCollate() == charset.CollationBin { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.expr = expr - } - case 1336: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1337: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1338: - { - co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) - if err != nil { - yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) - return 1 - } - expr := ast.NewValueExpr(yyS[yypt-0].item, yyS[yypt-1].ident, co) - tp := expr.GetType() - tp.SetCharset(yyS[yypt-1].ident) - tp.SetCollate(co) - tp.AddFlag(mysql.UnderScoreCharsetFlag) - if tp.GetCollate() == charset.CollationBin { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.expr = expr - } - case 1339: - { - co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) - if err != nil { - yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) - return 1 - } - expr := ast.NewValueExpr(yyS[yypt-0].item, yyS[yypt-1].ident, co) - tp := expr.GetType() - tp.SetCharset(yyS[yypt-1].ident) - tp.SetCollate(co) - tp.AddFlag(mysql.UnderScoreCharsetFlag) - if tp.GetCollate() == charset.CollationBin { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.expr = expr - } - case 1340: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) - parser.yyVAL.expr = expr - } - case 1341: - { - valExpr := yyS[yypt-1].expr.(ast.ValueExpr) - strLit := valExpr.GetString() - expr := ast.NewValueExpr(strLit+yyS[yypt-0].ident, parser.charset, parser.collation) - // Fix #4239, use first string literal as projection name. - if valExpr.GetProjectionOffset() >= 0 { - expr.SetProjectionOffset(valExpr.GetProjectionOffset()) - } else { - expr.SetProjectionOffset(len(strLit)) - } - parser.yyVAL.expr = expr - } - case 1342: - { - parser.yyVAL.item = []*ast.AlterOrderItem{yyS[yypt-0].item.(*ast.AlterOrderItem)} - } - case 1343: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.AlterOrderItem), yyS[yypt-0].item.(*ast.AlterOrderItem)) - } - case 1344: - { - parser.yyVAL.item = &ast.AlterOrderItem{Column: yyS[yypt-1].item.(*ast.ColumnName), Desc: yyS[yypt-0].item.(bool)} - } - case 1345: - { - parser.yyVAL.item = &ast.OrderByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} - } - case 1346: - { - parser.yyVAL.item = []*ast.ByItem{yyS[yypt-0].item.(*ast.ByItem)} - } - case 1347: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ByItem), yyS[yypt-0].item.(*ast.ByItem)) - } - case 1348: - { - expr := yyS[yypt-0].expr - valueExpr, ok := expr.(ast.ValueExpr) - if ok { - position, isPosition := valueExpr.GetValue().(int64) - if isPosition { - expr = &ast.PositionExpr{N: int(position)} - } - } - parser.yyVAL.item = &ast.ByItem{Expr: expr, NullOrder: true} - } - case 1349: - { - expr := yyS[yypt-1].expr - valueExpr, ok := expr.(ast.ValueExpr) - if ok { - position, isPosition := valueExpr.GetValue().(int64) - if isPosition { - expr = &ast.PositionExpr{N: int(position)} - } - } - parser.yyVAL.item = &ast.ByItem{Expr: expr, Desc: yyS[yypt-0].item.(bool)} - } - case 1350: - { - parser.yyVAL.item = false - } - case 1351: - { - parser.yyVAL.item = true - } - case 1352: - { - parser.yyVAL.item = false // ASC by default - } - case 1353: - { - parser.yyVAL.item = false - } - case 1354: - { - parser.yyVAL.item = true - } - case 1355: - { - parser.yyVAL.item = nil - } - case 1357: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Or, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1358: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.And, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1359: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LeftShift, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1360: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.RightShift, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1361: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Plus, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1362: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Minus, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1363: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr("DATE_ADD"), - Args: []ast.ExprNode{ - yyS[yypt-4].expr, - yyS[yypt-1].expr, - &ast.TimeUnitExpr{Unit: yyS[yypt-0].item.(ast.TimeUnitType)}, - }, - } - } - case 1364: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr("DATE_SUB"), - Args: []ast.ExprNode{ - yyS[yypt-4].expr, - yyS[yypt-1].expr, - &ast.TimeUnitExpr{Unit: yyS[yypt-0].item.(ast.TimeUnitType)}, - }, - } - } - case 1365: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr("DATE_ADD"), - Args: []ast.ExprNode{ - yyS[yypt-0].expr, - yyS[yypt-3].expr, - &ast.TimeUnitExpr{Unit: yyS[yypt-2].item.(ast.TimeUnitType)}, - }, - } - } - case 1366: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mul, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1367: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Div, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1368: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1369: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.IntDiv, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1370: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1371: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Xor, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} - } - case 1373: - { - parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ - Name: model.NewCIStr(yyS[yypt-0].ident), - }} - } - case 1374: - { - parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ - Table: model.NewCIStr(yyS[yypt-2].ident), - Name: model.NewCIStr(yyS[yypt-0].ident), - }} - } - case 1375: - { - parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ - Schema: model.NewCIStr(yyS[yypt-4].ident), - Table: model.NewCIStr(yyS[yypt-2].ident), - Name: model.NewCIStr(yyS[yypt-0].ident), - }} - } - case 1380: - { - parser.yyVAL.expr = &ast.SetCollationExpr{Expr: yyS[yypt-2].expr, Collate: yyS[yypt-0].ident} - } - case 1383: - { - parser.yyVAL.expr = ast.NewParamMarkerExpr(yyS[yypt].offset) - } - case 1386: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not2, V: yyS[yypt-0].expr} - } - case 1387: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.BitNeg, V: yyS[yypt-0].expr} - } - case 1388: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Minus, V: yyS[yypt-0].expr} - } - case 1389: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Plus, V: yyS[yypt-0].expr} - } - case 1390: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.Concat), Args: []ast.ExprNode{yyS[yypt-2].expr, yyS[yypt-0].expr}} - } - case 1391: - { - parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not2, V: yyS[yypt-0].expr} - } - case 1393: - { - startOffset := parser.startOffset(&yyS[yypt-1]) - endOffset := parser.endOffset(&yyS[yypt]) - expr := yyS[yypt-1].expr - expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) - parser.yyVAL.expr = &ast.ParenthesesExpr{Expr: expr} - } - case 1394: - { - values := append(yyS[yypt-3].item.([]ast.ExprNode), yyS[yypt-1].expr) - parser.yyVAL.expr = &ast.RowExpr{Values: values} - } - case 1395: - { - values := append(yyS[yypt-3].item.([]ast.ExprNode), yyS[yypt-1].expr) - parser.yyVAL.expr = &ast.RowExpr{Values: values} - } - case 1396: - { - sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) - sq.Exists = true - parser.yyVAL.expr = &ast.ExistsSubqueryExpr{Sel: sq} - } - case 1397: - { - /* - * ODBC escape syntax. - * See https://dev.mysql.com/doc/refman/5.7/en/expressions.html - */ - tp := yyS[yypt-1].expr.GetType() - switch yyS[yypt-2].ident { - case "d": - tp.SetCharset("") - tp.SetCollate("") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.DateLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} - case "t": - tp.SetCharset("") - tp.SetCollate("") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimeLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} - case "ts": - tp.SetCharset("") - tp.SetCollate("") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimestampLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} - default: - parser.yyVAL.expr = yyS[yypt-1].expr - } - } - case 1398: - { - // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#operator_binary - tp := types.NewFieldType(mysql.TypeString) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CharsetBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.expr = &ast.FuncCastExpr{ - Expr: yyS[yypt-0].expr, - Tp: tp, - FunctionType: ast.CastBinaryOperator, - } - } - case 1399: - { - /* See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */ - tp := yyS[yypt-2].item.(*types.FieldType) - defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) - if tp.GetFlen() == types.UnspecifiedLength { - tp.SetFlen(defaultFlen) - } - if tp.GetDecimal() == types.UnspecifiedLength { - tp.SetDecimal(defaultDecimal) - } - isArray := yyS[yypt-1].item.(bool) - tp.SetArray(isArray) - explicitCharset := parser.explicitCharset - if isArray && !explicitCharset && tp.GetCharset() != charset.CharsetBin { - tp.SetCharset(charset.CharsetUTF8MB4) - tp.SetCollate(charset.CollationUTF8MB4) - } - parser.explicitCharset = false - parser.yyVAL.expr = &ast.FuncCastExpr{ - Expr: yyS[yypt-4].expr, - Tp: tp, - FunctionType: ast.CastFunction, - ExplicitCharSet: explicitCharset, - } - } - case 1400: - { - x := &ast.CaseExpr{WhenClauses: yyS[yypt-2].item.([]*ast.WhenClause)} - if yyS[yypt-3].expr != nil { - x.Value = yyS[yypt-3].expr - } - if yyS[yypt-1].item != nil { - x.ElseClause = yyS[yypt-1].item.(ast.ExprNode) - } - parser.yyVAL.expr = x - } - case 1401: - { - // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert - tp := yyS[yypt-1].item.(*types.FieldType) - defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) - if tp.GetFlen() == types.UnspecifiedLength { - tp.SetFlen(defaultFlen) - } - if tp.GetDecimal() == types.UnspecifiedLength { - tp.SetDecimal(defaultDecimal) - } - explicitCharset := parser.explicitCharset - parser.explicitCharset = false - parser.yyVAL.expr = &ast.FuncCastExpr{ - Expr: yyS[yypt-3].expr, - Tp: tp, - FunctionType: ast.CastConvertFunction, - ExplicitCharSet: explicitCharset, - } - } - case 1402: - { - // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert - charset1 := ast.NewValueExpr(yyS[yypt-1].ident, "", "") - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{yyS[yypt-3].expr, charset1}, - } - } - case 1403: - { - parser.yyVAL.expr = &ast.DefaultExpr{Name: yyS[yypt-1].expr.(*ast.ColumnNameExpr).Name} - } - case 1404: - { - parser.yyVAL.expr = &ast.ValuesExpr{Column: yyS[yypt-1].expr.(*ast.ColumnNameExpr)} - } - case 1405: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{yyS[yypt-2].expr, expr}} - } - case 1406: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) - extract := &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{yyS[yypt-2].expr, expr}} - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONUnquote), Args: []ast.ExprNode{extract}} - } - case 1407: - { - parser.yyVAL.item = false - } - case 1408: - { - parser.yyVAL.item = true - } - case 1411: - { - parser.yyVAL.item = false - } - case 1412: - { - parser.yyVAL.item = true - } - case 1413: - { - parser.yyVAL.item = false - } - case 1415: - { - parser.yyVAL.item = true - } - case 1418: - { - parser.yyVAL.item = true - } - case 1462: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1463: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1464: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-1].ident)} - } - case 1465: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-2].ident)} - } - case 1466: - { - args := []ast.ExprNode{} - if yyS[yypt-0].item != nil { - args = append(args, yyS[yypt-0].item.(ast.ExprNode)) - } - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-1].ident), Args: args} - } - case 1467: - { - nilVal := ast.NewValueExpr(nil, parser.charset, parser.collation) - args := yyS[yypt-1].item.([]ast.ExprNode) - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.CharFunc), - Args: append(args, nilVal), - } - } - case 1468: - { - charset1 := ast.NewValueExpr(yyS[yypt-1].ident, "", "") - args := yyS[yypt-3].item.([]ast.ExprNode) - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.CharFunc), - Args: append(args, charset1), - } - } - case 1469: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.DateLiteral), Args: []ast.ExprNode{expr}} - } - case 1470: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimeLiteral), Args: []ast.ExprNode{expr}} - } - case 1471: - { - expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimestampLiteral), Args: []ast.ExprNode{expr}} - } - case 1472: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.InsertFunc), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1473: - { - parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-3].expr, R: yyS[yypt-1].expr} - } - case 1474: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.PasswordFunc), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1475: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1476: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1477: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{ - yyS[yypt-3].expr, - yyS[yypt-1].expr, - &ast.TimeUnitExpr{Unit: ast.TimeUnitDay}, - }, - } - } - case 1478: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{ - yyS[yypt-5].expr, - yyS[yypt-2].expr, - &ast.TimeUnitExpr{Unit: yyS[yypt-1].item.(ast.TimeUnitType)}, - }, - } - } - case 1479: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{ - yyS[yypt-5].expr, - yyS[yypt-2].expr, - &ast.TimeUnitExpr{Unit: yyS[yypt-1].item.(ast.TimeUnitType)}, - }, - } - } - case 1480: - { - timeUnit := &ast.TimeUnitExpr{Unit: yyS[yypt-3].item.(ast.TimeUnitType)} - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{timeUnit, yyS[yypt-1].expr}, - } - } - case 1481: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{ - &ast.GetFormatSelectorExpr{Selector: yyS[yypt-3].item.(ast.GetFormatSelectorType)}, - yyS[yypt-1].expr, - }, - } - } - case 1482: - { - parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-5].ident), Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}} - } - case 1483: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1484: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1485: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1486: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1487: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: yyS[yypt-5].item.(ast.TimeUnitType)}, yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1488: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: yyS[yypt-5].item.(ast.TimeUnitType)}, yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1489: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: []ast.ExprNode{yyS[yypt-1].expr}, - } - } - case 1490: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{yyS[yypt-1].expr, yyS[yypt-3].expr}, - } - } - case 1491: - { - spaceVal := ast.NewValueExpr(" ", parser.charset, parser.collation) - direction := &ast.TrimDirectionExpr{Direction: yyS[yypt-3].item.(ast.TrimDirectionType)} - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-5].ident), - Args: []ast.ExprNode{yyS[yypt-1].expr, spaceVal, direction}, - } - } - case 1492: - { - direction := &ast.TrimDirectionExpr{Direction: yyS[yypt-4].item.(ast.TrimDirectionType)} - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-6].ident), - Args: []ast.ExprNode{yyS[yypt-1].expr, yyS[yypt-3].expr, direction}, - } - } - case 1493: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: []ast.ExprNode{yyS[yypt-1].expr}, - } - } - case 1494: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-6].ident), - Args: []ast.ExprNode{yyS[yypt-4].expr, ast.NewValueExpr("CHAR", parser.charset, parser.collation), ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}, - } - } - case 1495: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-6].ident), - Args: []ast.ExprNode{yyS[yypt-4].expr, ast.NewValueExpr("BINARY", parser.charset, parser.collation), ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}, - } - } - case 1497: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-7].ident), - Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, - } - } - case 1498: - { - parser.yyVAL.item = ast.GetFormatSelectorDate - } - case 1499: - { - parser.yyVAL.item = ast.GetFormatSelectorDatetime - } - case 1500: - { - parser.yyVAL.item = ast.GetFormatSelectorTime - } - case 1501: - { - parser.yyVAL.item = ast.GetFormatSelectorDatetime - } - case 1506: - { - parser.yyVAL.item = ast.TrimBoth - } - case 1507: - { - parser.yyVAL.item = ast.TrimLeading - } - case 1508: - { - parser.yyVAL.item = ast.TrimTrailing - } - case 1509: - { - objNameExpr := &ast.TableNameExpr{ - Name: yyS[yypt-1].item.(*ast.TableName), - } - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.LastVal), - Args: []ast.ExprNode{objNameExpr}, - } - } - case 1510: - { - objNameExpr := &ast.TableNameExpr{ - Name: yyS[yypt-3].item.(*ast.TableName), - } - valueExpr := ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation) - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(ast.SetVal), - Args: []ast.ExprNode{objNameExpr, valueExpr}, - } - } - case 1512: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1513: - { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-3].ident, Args: yyS[yypt-1].item.([]ast.ExprNode), Distinct: false} - } - case 1514: - { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-3].ident, Args: yyS[yypt-1].item.([]ast.ExprNode)} - } - case 1515: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1516: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1517: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1518: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1519: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1520: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1521: - { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: yyS[yypt-1].item.([]ast.ExprNode), Distinct: true} - } - case 1522: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1523: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1524: - { - args := []ast.ExprNode{ast.NewValueExpr(1, parser.charset, parser.collation)} - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: args, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: args} - } - } - case 1525: - { - args := yyS[yypt-4].item.([]ast.ExprNode) - args = append(args, yyS[yypt-2].item.(ast.ExprNode)) - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: args, Distinct: yyS[yypt-5].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - agg := &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: args, Distinct: yyS[yypt-5].item.(bool)} - if yyS[yypt-3].item != nil { - agg.Order = yyS[yypt-3].item.(*ast.OrderByClause) - } - parser.yyVAL.expr = agg - } - } - case 1526: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1527: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1528: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1529: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: ast.AggFuncStddevPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: ast.AggFuncStddevPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1530: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1531: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: ast.AggFuncVarPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: ast.AggFuncVarPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - } - case 1532: - { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} - } - case 1533: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1534: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} - } - } - case 1535: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-6].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}} - } - } - case 1536: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}} - } - } - case 1537: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}} - } - } - case 1538: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} - } else { - parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}} - } - } - case 1539: - { - parser.yyVAL.item = ast.NewValueExpr(",", "", "") - } - case 1540: - { - parser.yyVAL.item = ast.NewValueExpr(yyS[yypt-0].ident, "", "") - } - case 1541: - { - parser.yyVAL.expr = &ast.FuncCallExpr{ - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 1542: - { - var tp ast.FuncCallExprType - if isInTokenMap(yyS[yypt-3].ident) { - tp = ast.FuncCallExprTypeKeyword - } else { - tp = ast.FuncCallExprTypeGeneric - } - parser.yyVAL.expr = &ast.FuncCallExpr{ - Tp: tp, - Schema: model.NewCIStr(yyS[yypt-5].ident), - FnName: model.NewCIStr(yyS[yypt-3].ident), - Args: yyS[yypt-1].item.([]ast.ExprNode), - } - } - case 1543: - { - parser.yyVAL.item = nil - } - case 1544: - { - parser.yyVAL.item = nil - } - case 1545: - { - expr := ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation) - parser.yyVAL.item = expr - } - case 1547: - { - parser.yyVAL.item = ast.TimeUnitSecondMicrosecond - } - case 1548: - { - parser.yyVAL.item = ast.TimeUnitMinuteMicrosecond - } - case 1549: - { - parser.yyVAL.item = ast.TimeUnitMinuteSecond - } - case 1550: - { - parser.yyVAL.item = ast.TimeUnitHourMicrosecond - } - case 1551: - { - parser.yyVAL.item = ast.TimeUnitHourSecond - } - case 1552: - { - parser.yyVAL.item = ast.TimeUnitHourMinute - } - case 1553: - { - parser.yyVAL.item = ast.TimeUnitDayMicrosecond - } - case 1554: - { - parser.yyVAL.item = ast.TimeUnitDaySecond - } - case 1555: - { - parser.yyVAL.item = ast.TimeUnitDayMinute - } - case 1556: - { - parser.yyVAL.item = ast.TimeUnitDayHour - } - case 1557: - { - parser.yyVAL.item = ast.TimeUnitYearMonth - } - case 1558: - { - parser.yyVAL.item = ast.TimeUnitMicrosecond - } - case 1559: - { - parser.yyVAL.item = ast.TimeUnitSecond - } - case 1560: - { - parser.yyVAL.item = ast.TimeUnitMinute - } - case 1561: - { - parser.yyVAL.item = ast.TimeUnitHour - } - case 1562: - { - parser.yyVAL.item = ast.TimeUnitDay - } - case 1563: - { - parser.yyVAL.item = ast.TimeUnitWeek - } - case 1564: - { - parser.yyVAL.item = ast.TimeUnitMonth - } - case 1565: - { - parser.yyVAL.item = ast.TimeUnitQuarter - } - case 1566: - { - parser.yyVAL.item = ast.TimeUnitYear - } - case 1567: - { - parser.yyVAL.item = ast.TimeUnitSecond - } - case 1568: - { - parser.yyVAL.item = ast.TimeUnitMinute - } - case 1569: - { - parser.yyVAL.item = ast.TimeUnitHour - } - case 1570: - { - parser.yyVAL.item = ast.TimeUnitDay - } - case 1571: - { - parser.yyVAL.item = ast.TimeUnitWeek - } - case 1572: - { - parser.yyVAL.item = ast.TimeUnitMonth - } - case 1573: - { - parser.yyVAL.item = ast.TimeUnitQuarter - } - case 1574: - { - parser.yyVAL.item = ast.TimeUnitYear - } - case 1575: - { - parser.yyVAL.expr = nil - } - case 1577: - { - parser.yyVAL.item = []*ast.WhenClause{yyS[yypt-0].item.(*ast.WhenClause)} - } - case 1578: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.WhenClause), yyS[yypt-0].item.(*ast.WhenClause)) - } - case 1579: - { - parser.yyVAL.item = &ast.WhenClause{ - Expr: yyS[yypt-2].expr, - Result: yyS[yypt-0].expr, - } - } - case 1580: - { - parser.yyVAL.item = nil - } - case 1581: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 1582: - { - tp := types.NewFieldType(mysql.TypeVarString) - tp.SetFlen(yyS[yypt-0].item.(int)) // TODO: Flen should be the flen of expression - if tp.GetFlen() != types.UnspecifiedLength { - tp.SetType(mysql.TypeString) - } - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1583: - { - tp := types.NewFieldType(mysql.TypeVarString) - tp.SetFlen(yyS[yypt-1].item.(int)) // TODO: Flen should be the flen of expression - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - } else if tp.GetCharset() != "" { - co, err := charset.GetDefaultCollation(tp.GetCharset()) - if err != nil { - yylex.AppendError(yylex.Errorf("Get collation error for charset: %s", tp.GetCharset())) - return 1 - } - tp.SetCollate(co) - parser.explicitCharset = true - } else { - tp.SetCharset(parser.charset) - tp.SetCollate(parser.collation) - } - parser.yyVAL.item = tp - } - case 1584: - { - tp := types.NewFieldType(mysql.TypeDate) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1585: - { - tp := types.NewFieldType(mysql.TypeYear) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1586: - { - tp := types.NewFieldType(mysql.TypeDatetime) - flen, _ := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDatetime) - tp.SetFlen(flen) - tp.SetDecimal(yyS[yypt-0].item.(int)) - if tp.GetDecimal() > 0 { - tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) - } - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1587: - { - fopt := yyS[yypt-0].item.(*ast.FloatOpt) - tp := types.NewFieldType(mysql.TypeNewDecimal) - tp.SetFlen(fopt.Flen) - tp.SetDecimal(fopt.Decimal) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1588: - { - tp := types.NewFieldType(mysql.TypeDuration) - flen, _ := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDuration) - tp.SetFlen(flen) - tp.SetDecimal(yyS[yypt-0].item.(int)) - if tp.GetDecimal() > 0 { - tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) - } - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1589: - { - tp := types.NewFieldType(mysql.TypeLonglong) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 1590: - { - tp := types.NewFieldType(mysql.TypeLonglong) - tp.AddFlag(mysql.UnsignedFlag | mysql.BinaryFlag) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - parser.yyVAL.item = tp - } - case 1591: - { - tp := types.NewFieldType(mysql.TypeJSON) - tp.AddFlag(mysql.BinaryFlag | mysql.ParseToJSONFlag) - tp.SetCharset(mysql.DefaultCharset) - tp.SetCollate(mysql.DefaultCollationName) - parser.yyVAL.item = tp - } - case 1592: - { - tp := types.NewFieldType(mysql.TypeDouble) - flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDouble) - tp.SetFlen(flen) - tp.SetDecimal(decimal) - tp.AddFlag(mysql.BinaryFlag) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - parser.yyVAL.item = tp - } - case 1593: - { - tp := types.NewFieldType(mysql.TypeFloat) - fopt := yyS[yypt-0].item.(*ast.FloatOpt) - if fopt.Flen >= 54 { - yylex.AppendError(ErrTooBigPrecision.GenWithStackByArgs(fopt.Flen, "CAST", 53)) - } else if fopt.Flen >= 25 { - tp = types.NewFieldType(mysql.TypeDouble) - } - flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) - tp.SetFlen(flen) - tp.SetDecimal(decimal) - tp.AddFlag(mysql.BinaryFlag) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - parser.yyVAL.item = tp - } - case 1594: - { - var tp *types.FieldType - if parser.lexer.GetSQLMode().HasRealAsFloatMode() { - tp = types.NewFieldType(mysql.TypeFloat) - } else { - tp = types.NewFieldType(mysql.TypeDouble) - } - flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) - tp.SetFlen(flen) - tp.SetDecimal(decimal) - tp.AddFlag(mysql.BinaryFlag) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - parser.yyVAL.item = tp - } - case 1595: - { - parser.yyVAL.item = mysql.LowPriority - } - case 1596: - { - parser.yyVAL.item = mysql.HighPriority - } - case 1597: - { - parser.yyVAL.item = mysql.DelayedPriority - } - case 1598: - { - parser.yyVAL.item = mysql.NoPriority - } - case 1600: - { - parser.yyVAL.item = &ast.TableName{Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 1601: - { - parser.yyVAL.item = &ast.TableName{Schema: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 1602: - { - tbl := []*ast.TableName{yyS[yypt-0].item.(*ast.TableName)} - parser.yyVAL.item = tbl - } - case 1603: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableName), yyS[yypt-0].item.(*ast.TableName)) - } - case 1604: - { - parser.yyVAL.item = &ast.TableName{Name: model.NewCIStr(yyS[yypt-1].ident)} - } - case 1605: - { - parser.yyVAL.item = &ast.TableName{Schema: model.NewCIStr(yyS[yypt-3].ident), Name: model.NewCIStr(yyS[yypt-1].ident)} - } - case 1606: - { - tbl := []*ast.TableName{yyS[yypt-0].item.(*ast.TableName)} - parser.yyVAL.item = tbl - } - case 1607: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableName), yyS[yypt-0].item.(*ast.TableName)) - } - case 1610: - { - parser.yyVAL.item = false - } - case 1611: - { - parser.yyVAL.item = true - } - case 1612: - { - var sqlText string - var sqlVar *ast.VariableExpr - switch x := yyS[yypt-0].item.(type) { - case string: - sqlText = x - case *ast.VariableExpr: - sqlVar = x - } - parser.yyVAL.statement = &ast.PrepareStmt{ - Name: yyS[yypt-2].ident, - SQLText: sqlText, - SQLVar: sqlVar, - } - } - case 1613: - { - parser.yyVAL.item = yyS[yypt-0].ident - } - case 1614: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 1615: - { - parser.yyVAL.statement = &ast.ExecuteStmt{Name: yyS[yypt-0].ident} - } - case 1616: - { - parser.yyVAL.statement = &ast.ExecuteStmt{ - Name: yyS[yypt-2].ident, - UsingVars: yyS[yypt-0].item.([]ast.ExprNode), - } - } - case 1617: - { - parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} - } - case 1618: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) - } - case 1619: - { - parser.yyVAL.statement = &ast.DeallocateStmt{Name: yyS[yypt-0].ident} - } - case 1622: - { - parser.yyVAL.statement = &ast.RollbackStmt{} - } - case 1623: - { - parser.yyVAL.statement = &ast.RollbackStmt{CompletionType: yyS[yypt-0].item.(ast.CompletionType)} - } - case 1624: - { - parser.yyVAL.statement = &ast.RollbackStmt{SavepointName: yyS[yypt-0].ident} - } - case 1625: - { - parser.yyVAL.statement = &ast.RollbackStmt{SavepointName: yyS[yypt-0].ident} - } - case 1626: - { - parser.yyVAL.item = ast.CompletionTypeChain - } - case 1627: - { - parser.yyVAL.item = ast.CompletionTypeRelease - } - case 1628: - { - parser.yyVAL.item = ast.CompletionTypeDefault - } - case 1629: - { - parser.yyVAL.item = ast.CompletionTypeChain - } - case 1630: - { - parser.yyVAL.item = ast.CompletionTypeDefault - } - case 1631: - { - parser.yyVAL.item = ast.CompletionTypeRelease - } - case 1632: - { - parser.yyVAL.item = ast.CompletionTypeDefault - } - case 1633: - { - parser.yyVAL.statement = &ast.ShutdownStmt{} - } - case 1634: - { - parser.yyVAL.statement = &ast.RestartStmt{} - } - case 1635: - { - parser.yyVAL.statement = &ast.HelpStmt{Topic: yyS[yypt-0].ident} - } - case 1636: - { - st := &ast.SelectStmt{ - SelectStmtOpts: yyS[yypt-2].item.(*ast.SelectStmtOpts), - Distinct: yyS[yypt-2].item.(*ast.SelectStmtOpts).Distinct, - Fields: yyS[yypt-1].item.(*ast.FieldList), - Kind: ast.SelectStmtKindSelect, - } - if st.SelectStmtOpts.TableHints != nil { - st.TableHints = st.SelectStmtOpts.TableHints - } - if yyS[yypt-0].item != nil { - st.Having = yyS[yypt-0].item.(*ast.HavingClause) - } - parser.yyVAL.item = st - } - case 1637: - { - st := yyS[yypt-2].item.(*ast.SelectStmt) - lastField := st.Fields.Fields[len(st.Fields.Fields)-1] - if lastField.Expr != nil && lastField.AsName.O == "" { - lastEnd := yyS[yypt-1].offset - 1 - lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) - } - if yyS[yypt-0].item != nil { - st.Where = yyS[yypt-0].item.(ast.ExprNode) - } - } - case 1638: - { - st := yyS[yypt-6].item.(*ast.SelectStmt) - st.From = yyS[yypt-4].item.(*ast.TableRefsClause) - lastField := st.Fields.Fields[len(st.Fields.Fields)-1] - if lastField.Expr != nil && lastField.AsName.O == "" { - lastEnd := parser.endOffset(&yyS[yypt-5]) - lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) - } - if yyS[yypt-3].item != nil { - st.Where = yyS[yypt-3].item.(ast.ExprNode) - } - if yyS[yypt-2].item != nil { - st.GroupBy = yyS[yypt-2].item.(*ast.GroupByClause) - } - if yyS[yypt-1].item != nil { - st.Having = yyS[yypt-1].item.(*ast.HavingClause) - } - if yyS[yypt-0].item != nil { - st.WindowSpecs = (yyS[yypt-0].item.([]ast.WindowSpec)) - } - parser.yyVAL.item = st - } - case 1639: - { - parser.yyVAL.item = nil - } - case 1640: - { - var repSeed ast.ExprNode - if yyS[yypt-0].expr != nil { - repSeed = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) - } - parser.yyVAL.item = &ast.TableSample{ - SampleMethod: yyS[yypt-5].item.(ast.SampleMethodType), - Expr: ast.NewValueExpr(yyS[yypt-3].expr, parser.charset, parser.collation), - SampleClauseUnit: yyS[yypt-2].item.(ast.SampleClauseUnitType), - RepeatableSeed: repSeed, - } - } - case 1641: - { - var repSeed ast.ExprNode - if yyS[yypt-0].expr != nil { - repSeed = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) - } - parser.yyVAL.item = &ast.TableSample{ - SampleMethod: yyS[yypt-3].item.(ast.SampleMethodType), - RepeatableSeed: repSeed, - } - } - case 1642: - { - parser.yyVAL.item = ast.SampleMethodTypeNone - } - case 1643: - { - parser.yyVAL.item = ast.SampleMethodTypeSystem - } - case 1644: - { - parser.yyVAL.item = ast.SampleMethodTypeBernoulli - } - case 1645: - { - parser.yyVAL.item = ast.SampleMethodTypeTiDBRegion - } - case 1646: - { - parser.yyVAL.item = ast.SampleClauseUnitTypeDefault - } - case 1647: - { - parser.yyVAL.item = ast.SampleClauseUnitTypeRow - } - case 1648: - { - parser.yyVAL.item = ast.SampleClauseUnitTypePercent - } - case 1649: - { - parser.yyVAL.expr = nil - } - case 1650: - { - parser.yyVAL.expr = yyS[yypt-1].expr - } - case 1651: - { - st := yyS[yypt-6].item.(*ast.SelectStmt) - if yyS[yypt-1].item != nil { - st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) - } - if yyS[yypt-5].item != nil { - st.Where = yyS[yypt-5].item.(ast.ExprNode) - } - if yyS[yypt-4].item != nil { - st.GroupBy = yyS[yypt-4].item.(*ast.GroupByClause) - } - if yyS[yypt-3].item != nil { - st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) - } - if yyS[yypt-2].item != nil { - st.Limit = yyS[yypt-2].item.(*ast.Limit) - } - if yyS[yypt-0].item != nil { - st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) - } - parser.yyVAL.statement = st - } - case 1652: - { - st := yyS[yypt-5].item.(*ast.SelectStmt) - if yyS[yypt-4].item != nil { - st.GroupBy = yyS[yypt-4].item.(*ast.GroupByClause) - } - if yyS[yypt-3].item != nil { - st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) - } - if yyS[yypt-2].item != nil { - st.Limit = yyS[yypt-2].item.(*ast.Limit) - } - if yyS[yypt-1].item != nil { - st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) - } - if yyS[yypt-0].item != nil { - st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) - } - parser.yyVAL.statement = st - } - case 1653: - { - st := yyS[yypt-4].item.(*ast.SelectStmt) - if yyS[yypt-1].item != nil { - st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) - } - if yyS[yypt-3].item != nil { - st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) - } - if yyS[yypt-2].item != nil { - st.Limit = yyS[yypt-2].item.(*ast.Limit) - } - if yyS[yypt-0].item != nil { - st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) - } - parser.yyVAL.statement = st - } - case 1654: - { - st := &ast.SelectStmt{ - Kind: ast.SelectStmtKindTable, - Fields: &ast.FieldList{Fields: []*ast.SelectField{{WildCard: &ast.WildCardField{}}}}, - } - ts := &ast.TableSource{Source: yyS[yypt-4].item.(*ast.TableName)} - st.From = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} - if yyS[yypt-3].item != nil { - st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) - } - if yyS[yypt-2].item != nil { - st.Limit = yyS[yypt-2].item.(*ast.Limit) - } - if yyS[yypt-1].item != nil { - st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) - } - if yyS[yypt-0].item != nil { - st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) - } - parser.yyVAL.statement = st - } - case 1655: - { - st := &ast.SelectStmt{ - Kind: ast.SelectStmtKindValues, - Fields: &ast.FieldList{Fields: []*ast.SelectField{{WildCard: &ast.WildCardField{}}}}, - Lists: yyS[yypt-4].item.([]*ast.RowExpr), - } - if yyS[yypt-3].item != nil { - st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) - } - if yyS[yypt-2].item != nil { - st.Limit = yyS[yypt-2].item.(*ast.Limit) - } - if yyS[yypt-1].item != nil { - st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) - } - if yyS[yypt-0].item != nil { - st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) - } - parser.yyVAL.statement = st - } - case 1656: - { - sel := yyS[yypt-0].statement.(*ast.SelectStmt) - sel.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = sel - } - case 1657: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - x.WithBeforeBraces = true - x.With = yyS[yypt-1].item.(*ast.WithClause) - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - x.With = yyS[yypt-1].item.(*ast.WithClause) - sel = x - } - parser.yyVAL.statement = sel - } - case 1658: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 1659: - { - ws := yyS[yypt-0].item.(*ast.WithClause) - ws.IsRecursive = true - for _, cte := range ws.CTEs { - cte.IsRecursive = true - } - parser.yyVAL.item = ws - } - case 1660: - { - ws := yyS[yypt-2].item.(*ast.WithClause) - ws.CTEs = append(ws.CTEs, yyS[yypt-0].item.(*ast.CommonTableExpression)) - parser.yyVAL.item = ws - } - case 1661: - { - ws := &ast.WithClause{} - ws.CTEs = make([]*ast.CommonTableExpression, 0, 4) - ws.CTEs = append(ws.CTEs, yyS[yypt-0].item.(*ast.CommonTableExpression)) - parser.yyVAL.item = ws - } - case 1662: - { - cte := &ast.CommonTableExpression{} - cte.Name = model.NewCIStr(yyS[yypt-3].ident) - cte.ColNameList = yyS[yypt-2].item.([]model.CIStr) - cte.Query = yyS[yypt-0].expr.(*ast.SubqueryExpr) - parser.yyVAL.item = cte - } - case 1664: - { - parser.yyVAL.item = nil - } - case 1665: - { - parser.yyVAL.item = yyS[yypt-0].item.([]ast.WindowSpec) - } - case 1666: - { - parser.yyVAL.item = []ast.WindowSpec{yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1667: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.WindowSpec), yyS[yypt-0].item.(ast.WindowSpec)) - } - case 1668: - { - var spec = yyS[yypt-0].item.(ast.WindowSpec) - spec.Name = yyS[yypt-2].item.(model.CIStr) - parser.yyVAL.item = spec - } - case 1669: - { - parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) - } - case 1670: - { - parser.yyVAL.item = yyS[yypt-1].item.(ast.WindowSpec) - } - case 1671: - { - spec := ast.WindowSpec{Ref: yyS[yypt-3].item.(model.CIStr)} - if yyS[yypt-2].item != nil { - spec.PartitionBy = yyS[yypt-2].item.(*ast.PartitionByClause) - } - if yyS[yypt-1].item != nil { - spec.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) - } - if yyS[yypt-0].item != nil { - spec.Frame = yyS[yypt-0].item.(*ast.FrameClause) - } - parser.yyVAL.item = spec - } - case 1672: - { - parser.yyVAL.item = model.CIStr{} - } - case 1674: - { - parser.yyVAL.item = nil - } - case 1675: - { - parser.yyVAL.item = &ast.PartitionByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} - } - case 1676: - { - parser.yyVAL.item = nil - } - case 1677: - { - parser.yyVAL.item = &ast.OrderByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} - } - case 1678: - { - parser.yyVAL.item = nil - } - case 1679: - { - parser.yyVAL.item = &ast.FrameClause{ - Type: yyS[yypt-1].item.(ast.FrameType), - Extent: yyS[yypt-0].item.(ast.FrameExtent), - } - } - case 1680: - { - parser.yyVAL.item = ast.FrameType(ast.Rows) - } - case 1681: - { - parser.yyVAL.item = ast.FrameType(ast.Ranges) - } - case 1682: - { - parser.yyVAL.item = ast.FrameType(ast.Groups) - } - case 1683: - { - parser.yyVAL.item = ast.FrameExtent{ - Start: yyS[yypt-0].item.(ast.FrameBound), - End: ast.FrameBound{Type: ast.CurrentRow}, - } - } - case 1685: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, UnBounded: true} - } - case 1686: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} - } - case 1687: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset)} - } - case 1688: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: yyS[yypt-2].expr, Unit: yyS[yypt-1].item.(ast.TimeUnitType)} - } - case 1689: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.CurrentRow} - } - case 1690: - { - parser.yyVAL.item = ast.FrameExtent{Start: yyS[yypt-2].item.(ast.FrameBound), End: yyS[yypt-0].item.(ast.FrameBound)} - } - case 1692: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Following, UnBounded: true} - } - case 1693: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} - } - case 1694: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset)} - } - case 1695: - { - parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: yyS[yypt-2].expr, Unit: yyS[yypt-1].item.(ast.TimeUnitType)} - } - case 1696: - { - parser.yyVAL.item = nil - } - case 1697: - { - spec := yyS[yypt-0].item.(ast.WindowSpec) - parser.yyVAL.item = &spec - } - case 1698: - { - parser.yyVAL.item = yyS[yypt-0].item.(ast.WindowSpec) - } - case 1699: - { - parser.yyVAL.item = ast.WindowSpec{Name: yyS[yypt-0].item.(model.CIStr), OnlyAlias: true} - } - case 1701: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1702: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1703: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1704: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1705: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1706: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1707: - { - args := []ast.ExprNode{yyS[yypt-4].expr} - if yyS[yypt-3].item != nil { - args = append(args, yyS[yypt-3].item.([]ast.ExprNode)...) - } - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: args, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1708: - { - args := []ast.ExprNode{yyS[yypt-4].expr} - if yyS[yypt-3].item != nil { - args = append(args, yyS[yypt-3].item.([]ast.ExprNode)...) - } - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: args, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1709: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-3].expr}, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1710: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-3].expr}, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1711: - { - parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-6].expr, yyS[yypt-4].expr}, FromLast: yyS[yypt-2].item.(bool), IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} - } - case 1712: - { - parser.yyVAL.item = nil - } - case 1713: - { - args := []ast.ExprNode{ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} - if yyS[yypt-0].item != nil { - args = append(args, yyS[yypt-0].item.(ast.ExprNode)) - } - parser.yyVAL.item = args - } - case 1714: - { - args := []ast.ExprNode{ast.NewParamMarkerExpr(yyS[yypt-1].offset)} - if yyS[yypt-0].item != nil { - args = append(args, yyS[yypt-0].item.(ast.ExprNode)) - } - parser.yyVAL.item = args - } - case 1715: - { - parser.yyVAL.item = nil - } - case 1716: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 1717: - { - parser.yyVAL.item = false - } - case 1718: - { - parser.yyVAL.item = false - } - case 1719: - { - parser.yyVAL.item = true - } - case 1720: - { - parser.yyVAL.item = false - } - case 1721: - { - parser.yyVAL.item = false - } - case 1722: - { - parser.yyVAL.item = true - } - case 1723: - { - parser.yyVAL.item = &ast.TableRefsClause{TableRefs: yyS[yypt-0].item.(*ast.Join)} - } - case 1724: - { - if j, ok := yyS[yypt-0].item.(*ast.Join); ok { - // if $1 is Join, use it directly - parser.yyVAL.item = j - } else { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-0].item.(ast.ResultSetNode), Right: nil} - } - } - case 1725: - { - /* from a, b is default cross join */ - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-2].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), Tp: ast.CrossJoin} - } - case 1727: - { - /* - * ODBC escape syntax for outer join is { OJ join_table } - * Use an Identifier for OJ - */ - parser.yyVAL.item = yyS[yypt-1].item - } - case 1730: - { - tn := yyS[yypt-5].item.(*ast.TableName) - tn.PartitionNames = yyS[yypt-4].item.([]model.CIStr) - tn.IndexHints = yyS[yypt-1].item.([]*ast.IndexHint) - if yyS[yypt-0].item != nil { - tn.TableSample = yyS[yypt-0].item.(*ast.TableSample) - } - if yyS[yypt-2].item != nil { - tn.AsOf = yyS[yypt-2].item.(*ast.AsOfClause) - } - parser.yyVAL.item = &ast.TableSource{Source: tn, AsName: yyS[yypt-3].item.(model.CIStr)} - } - case 1731: - { - resultNode := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query - parser.yyVAL.item = &ast.TableSource{Source: resultNode, AsName: yyS[yypt-0].item.(model.CIStr)} - } - case 1732: - { - j := yyS[yypt-1].item.(*ast.Join) - j.ExplicitParens = true - parser.yyVAL.item = yyS[yypt-1].item - } - case 1733: - { - parser.yyVAL.item = []model.CIStr{} - } - case 1734: - { - parser.yyVAL.item = yyS[yypt-1].item - } - case 1735: - { - parser.yyVAL.item = model.CIStr{} - } - case 1737: - { - parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) - } - case 1738: - { - parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) - } - case 1739: - { - parser.yyVAL.item = ast.HintUse - } - case 1740: - { - parser.yyVAL.item = ast.HintIgnore - } - case 1741: - { - parser.yyVAL.item = ast.HintForce - } - case 1742: - { - parser.yyVAL.item = ast.HintForScan - } - case 1743: - { - parser.yyVAL.item = ast.HintForJoin - } - case 1744: - { - parser.yyVAL.item = ast.HintForOrderBy - } - case 1745: - { - parser.yyVAL.item = ast.HintForGroupBy - } - case 1746: - { - parser.yyVAL.item = &ast.IndexHint{ - IndexNames: yyS[yypt-1].item.([]model.CIStr), - HintType: yyS[yypt-4].item.(ast.IndexHintType), - HintScope: yyS[yypt-3].item.(ast.IndexHintScope), - } - } - case 1747: - { - var nameList []model.CIStr - parser.yyVAL.item = nameList - } - case 1748: - { - parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} - } - case 1749: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) - } - case 1750: - { - parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} - } - case 1751: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) - } - case 1752: - { - parser.yyVAL.item = []*ast.IndexHint{yyS[yypt-0].item.(*ast.IndexHint)} - } - case 1753: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.IndexHint), yyS[yypt-0].item.(*ast.IndexHint)) - } - case 1754: - { - parser.yyVAL.item = []*ast.IndexHint{} - } - case 1756: - { - parser.yyVAL.item = ast.NewCrossJoin(yyS[yypt-2].item.(ast.ResultSetNode), yyS[yypt-0].item.(ast.ResultSetNode)) - } - case 1757: - { - on := &ast.OnCondition{Expr: yyS[yypt-0].expr} - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-4].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), Tp: ast.CrossJoin, On: on} - } - case 1758: - { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-6].item.(ast.ResultSetNode), Right: yyS[yypt-4].item.(ast.ResultSetNode), Tp: ast.CrossJoin, Using: yyS[yypt-1].item.([]*ast.ColumnName)} - } - case 1759: - { - on := &ast.OnCondition{Expr: yyS[yypt-0].expr} - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-6].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), Tp: yyS[yypt-5].item.(ast.JoinType), On: on} - } - case 1760: - { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-8].item.(ast.ResultSetNode), Right: yyS[yypt-4].item.(ast.ResultSetNode), Tp: yyS[yypt-7].item.(ast.JoinType), Using: yyS[yypt-1].item.([]*ast.ColumnName)} - } - case 1761: - { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-3].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), NaturalJoin: true} - } - case 1762: - { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-5].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), Tp: yyS[yypt-3].item.(ast.JoinType), NaturalJoin: true} - } - case 1763: - { - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-2].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), StraightJoin: true} - } - case 1764: - { - on := &ast.OnCondition{Expr: yyS[yypt-0].expr} - parser.yyVAL.item = &ast.Join{Left: yyS[yypt-4].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), StraightJoin: true, On: on} - } - case 1765: - { - parser.yyVAL.item = ast.LeftJoin - } - case 1766: - { - parser.yyVAL.item = ast.RightJoin - } - case 1772: - { - parser.yyVAL.item = nil - } - case 1773: - { - parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-0].item.(ast.ValueExpr)} - } - case 1774: - { - parser.yyVAL.item = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) - } - case 1775: - { - parser.yyVAL.item = ast.NewParamMarkerExpr(yyS[yypt].offset) - } - case 1780: - { - parser.yyVAL.item = ast.NewValueExpr(uint64(1), parser.charset, parser.collation) - } - case 1782: - { - parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-0].item.(ast.ExprNode)} - } - case 1783: - { - parser.yyVAL.item = &ast.Limit{Offset: yyS[yypt-2].item.(ast.ExprNode), Count: yyS[yypt-0].item.(ast.ExprNode)} - } - case 1784: - { - parser.yyVAL.item = &ast.Limit{Offset: yyS[yypt-0].item.(ast.ExprNode), Count: yyS[yypt-2].item.(ast.ExprNode)} - } - case 1785: - { - parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-2].item.(ast.ExprNode)} - } - case 1786: - { - parser.yyVAL.item = nil - } - case 1788: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.TableHints = yyS[yypt-0].item.([]*ast.TableOptimizerHint) - parser.yyVAL.item = opt - } - case 1789: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - if yyS[yypt-0].item.(bool) { - opt.Distinct = true - } else { - opt.Distinct = false - opt.ExplicitAll = true - } - parser.yyVAL.item = opt - } - case 1790: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.Priority = yyS[yypt-0].item.(mysql.PriorityEnum) - parser.yyVAL.item = opt - } - case 1791: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.SQLSmallResult = true - parser.yyVAL.item = opt - } - case 1792: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.SQLBigResult = true - parser.yyVAL.item = opt - } - case 1793: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.SQLBufferResult = true - parser.yyVAL.item = opt - } - case 1794: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = yyS[yypt-0].item.(bool) - parser.yyVAL.item = opt - } - case 1795: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.CalcFoundRows = true - parser.yyVAL.item = opt - } - case 1796: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - opt.StraightJoin = true - parser.yyVAL.item = opt - } - case 1797: - { - opt := &ast.SelectStmtOpts{} - opt.SQLCache = true - parser.yyVAL.item = opt - } - case 1799: - { - opts := yyS[yypt-1].item.(*ast.SelectStmtOpts) - opt := yyS[yypt-0].item.(*ast.SelectStmtOpts) - - // Merge options. - // Always use the first hint. - if opt.TableHints != nil && opts.TableHints == nil { - opts.TableHints = opt.TableHints - } - if opt.Distinct { - opts.Distinct = true - } - if opt.Priority != mysql.NoPriority { - opts.Priority = opt.Priority - } - if opt.SQLSmallResult { - opts.SQLSmallResult = true - } - if opt.SQLBigResult { - opts.SQLBigResult = true - } - if opt.SQLBufferResult { - opts.SQLBufferResult = true - } - if !opt.SQLCache { - opts.SQLCache = false - } - if opt.CalcFoundRows { - opts.CalcFoundRows = true - } - if opt.StraightJoin { - opts.StraightJoin = true - } - if opt.ExplicitAll { - opts.ExplicitAll = true - } - - if opts.Distinct && opts.ExplicitAll { - yylex.AppendError(ErrWrongUsage.GenWithStackByArgs("ALL", "DISTINCT")) - return 1 - } - - parser.yyVAL.item = opts - } - case 1801: - { - hints, warns := parser.parseHint(yyS[yypt-0].ident) - for _, w := range warns { - yylex.AppendError(w) - parser.lastErrorAsWarn() - } - parser.yyVAL.item = hints - } - case 1802: - { - parser.yyVAL.item = nil - } - case 1804: - { - parser.yyVAL.item = true - } - case 1805: - { - parser.yyVAL.item = false - } - case 1806: - { - parser.yyVAL.item = &ast.FieldList{Fields: yyS[yypt-0].item.([]*ast.SelectField)} - } - case 1807: - { - parser.yyVAL.item = nil - } - case 1809: - { - parser.yyVAL.item = nil - } - case 1810: - { - x := &ast.SelectIntoOption{ - Tp: ast.SelectIntoOutfile, - FileName: yyS[yypt-2].ident, - } - if yyS[yypt-1].item != nil { - x.FieldsInfo = yyS[yypt-1].item.(*ast.FieldsClause) - } - if yyS[yypt-0].item != nil { - x.LinesInfo = yyS[yypt-0].item.(*ast.LinesClause) - } - - parser.yyVAL.item = x - } - case 1811: - { - rs := yyS[yypt-1].statement.(*ast.SelectStmt) - endOffset := parser.endOffset(&yyS[yypt]) - parser.setLastSelectFieldText(rs, endOffset) - src := parser.src - // See the implementation of yyParse function - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - } - case 1812: - { - rs := yyS[yypt-1].statement.(*ast.SetOprStmt) - src := parser.src - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - } - case 1813: - { - switch rs := yyS[yypt-1].statement.(type) { - case *ast.SelectStmt: - endOffset := parser.endOffset(&yyS[yypt]) - parser.setLastSelectFieldText(rs, endOffset) - src := parser.src - // See the implementation of yyParse function - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - case *ast.SetOprStmt: - src := parser.src - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - } - } - case 1814: - { - subQuery := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query - isRecursive := true - // remove redundant brackets like '((select 1))' - for isRecursive { - if _, isRecursive = subQuery.(*ast.SubqueryExpr); isRecursive { - subQuery = subQuery.(*ast.SubqueryExpr).Query - } - } - switch rs := subQuery.(type) { - case *ast.SelectStmt: - endOffset := parser.endOffset(&yyS[yypt]) - parser.setLastSelectFieldText(rs, endOffset) - src := parser.src - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - case *ast.SetOprStmt: - src := parser.src - rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) - parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} - } - } - case 1815: - { - parser.yyVAL.item = nil - } - case 1816: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForUpdate, - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 1817: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForShare, - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 1818: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForUpdateNoWait, - Tables: yyS[yypt-1].item.([]*ast.TableName), - } - } - case 1819: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForUpdateWaitN, - WaitSec: getUint64FromNUM(yyS[yypt-0].item), - Tables: yyS[yypt-2].item.([]*ast.TableName), - } - } - case 1820: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForShareNoWait, - Tables: yyS[yypt-1].item.([]*ast.TableName), - } - } - case 1821: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForUpdateSkipLocked, - Tables: yyS[yypt-2].item.([]*ast.TableName), - } - } - case 1822: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForShareSkipLocked, - Tables: yyS[yypt-2].item.([]*ast.TableName), - } - } - case 1823: - { - parser.yyVAL.item = &ast.SelectLockInfo{ - LockType: ast.SelectLockForShare, - Tables: []*ast.TableName{}, - } - } - case 1824: - { - parser.yyVAL.item = []*ast.TableName{} - } - case 1825: - { - parser.yyVAL.item = yyS[yypt-0].item.([]*ast.TableName) - } - case 1828: - { - setOpr := yyS[yypt-0].statement.(*ast.SetOprStmt) - setOpr.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = setOpr - } - case 1829: - { - setOpr := yyS[yypt-0].statement.(*ast.SetOprStmt) - setOpr.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = setOpr - } - case 1830: - { - setOprList1 := yyS[yypt-2].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-1]) - parser.setLastSelectFieldText(sel, endOffset) - } - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: yyS[yypt-2].item.([]ast.Node)}} - st := yyS[yypt-0].statement.(*ast.SelectStmt) - setOpr.Limit = st.Limit - setOpr.OrderBy = st.OrderBy - st.Limit = nil - st.OrderBy = nil - st.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) - setOpr.SelectList.Selects = append(setOpr.SelectList.Selects, st) - parser.yyVAL.statement = setOpr - } - case 1831: - { - setOprList1 := yyS[yypt-2].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-1]) - parser.setLastSelectFieldText(sel, endOffset) - } - var setOprList2 []ast.Node - var with2 *ast.WithClause - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList2 = []ast.Node{x} - with2 = x.With - case *ast.SetOprStmt: - setOprList2 = x.SelectList.Selects - with2 = x.With - } - nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} - nextSetOprList.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) - setOprList := append(setOprList1, nextSetOprList) - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} - parser.yyVAL.statement = setOpr - } - case 1832: - { - setOprList1 := yyS[yypt-3].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-2]) - parser.setLastSelectFieldText(sel, endOffset) - } - var setOprList2 []ast.Node - var with2 *ast.WithClause - switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList2 = []ast.Node{x} - with2 = x.With - case *ast.SetOprStmt: - setOprList2 = x.SelectList.Selects - with2 = x.With - } - nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} - nextSetOprList.AfterSetOperator = yyS[yypt-2].item.(*ast.SetOprType) - setOprList := append(setOprList1, nextSetOprList) - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} - setOpr.OrderBy = yyS[yypt-0].item.(*ast.OrderByClause) - parser.yyVAL.statement = setOpr - } - case 1833: - { - setOprList1 := yyS[yypt-3].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-2]) - parser.setLastSelectFieldText(sel, endOffset) - } - var setOprList2 []ast.Node - var with2 *ast.WithClause - switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList2 = []ast.Node{x} - with2 = x.With - case *ast.SetOprStmt: - setOprList2 = x.SelectList.Selects - with2 = x.With - } - nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} - nextSetOprList.AfterSetOperator = yyS[yypt-2].item.(*ast.SetOprType) - setOprList := append(setOprList1, nextSetOprList) - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} - setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) - parser.yyVAL.statement = setOpr - } - case 1834: - { - setOprList1 := yyS[yypt-4].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-3]) - parser.setLastSelectFieldText(sel, endOffset) - } - var setOprList2 []ast.Node - var with2 *ast.WithClause - switch x := yyS[yypt-2].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList2 = []ast.Node{x} - with2 = x.With - case *ast.SetOprStmt: - setOprList2 = x.SelectList.Selects - with2 = x.With - } - nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} - nextSetOprList.AfterSetOperator = yyS[yypt-3].item.(*ast.SetOprType) - setOprList := append(setOprList1, nextSetOprList) - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} - setOpr.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) - setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) - parser.yyVAL.statement = setOpr - } - case 1835: - { - var setOprList []ast.Node - var with *ast.WithClause - switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList = []ast.Node{x} - with = x.With - case *ast.SetOprStmt: - setOprList = x.SelectList.Selects - with = x.With - } - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} - setOpr.OrderBy = yyS[yypt-0].item.(*ast.OrderByClause) - parser.yyVAL.statement = setOpr - } - case 1836: - { - var setOprList []ast.Node - var with *ast.WithClause - switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList = []ast.Node{x} - with = x.With - case *ast.SetOprStmt: - setOprList = x.SelectList.Selects - with = x.With - } - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} - setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) - parser.yyVAL.statement = setOpr - } - case 1837: - { - var setOprList []ast.Node - var with *ast.WithClause - switch x := yyS[yypt-2].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList = []ast.Node{x} - with = x.With - case *ast.SetOprStmt: - setOprList = x.SelectList.Selects - with = x.With - } - setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} - setOpr.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) - setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) - parser.yyVAL.statement = setOpr - } - case 1839: - { - setOprList1 := yyS[yypt-2].item.([]ast.Node) - setOprList2 := yyS[yypt-0].item.([]ast.Node) - if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { - endOffset := parser.endOffset(&yyS[yypt-1]) - parser.setLastSelectFieldText(sel, endOffset) - } - switch x := setOprList2[0].(type) { - case *ast.SelectStmt: - x.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) - case *ast.SetOprSelectList: - x.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) - } - parser.yyVAL.item = append(setOprList1, setOprList2...) - } - case 1840: - { - parser.yyVAL.item = []ast.Node{yyS[yypt-0].statement.(*ast.SelectStmt)} - } - case 1841: - { - var setOprList []ast.Node - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - setOprList = []ast.Node{&ast.SetOprSelectList{Selects: []ast.Node{x}}} - case *ast.SetOprStmt: - setOprList = []ast.Node{&ast.SetOprSelectList{Selects: x.SelectList.Selects, With: x.With}} - } - parser.yyVAL.item = setOprList - } - case 1842: - { - var tp ast.SetOprType - tp = ast.Union - if yyS[yypt-0].item == false { - tp = ast.UnionAll - } - parser.yyVAL.item = &tp - } - case 1843: - { - var tp ast.SetOprType - tp = ast.Except - if yyS[yypt-0].item == false { - tp = ast.ExceptAll - } - parser.yyVAL.item = &tp - } - case 1844: - { - var tp ast.SetOprType - tp = ast.Intersect - if yyS[yypt-0].item == false { - tp = ast.IntersectAll - } - parser.yyVAL.item = &tp - } - case 1846: - { - parser.yyVAL.statement = &ast.ChangeStmt{ - NodeType: ast.PumpType, - State: yyS[yypt-3].ident, - NodeID: yyS[yypt-0].ident, - } - } - case 1847: - { - parser.yyVAL.statement = &ast.ChangeStmt{ - NodeType: ast.DrainerType, - State: yyS[yypt-3].ident, - NodeID: yyS[yypt-0].ident, - } - } - case 1848: - { - parser.yyVAL.statement = &ast.SetStmt{Variables: yyS[yypt-0].item.([]*ast.VariableAssignment)} - } - case 1849: - { - parser.yyVAL.statement = &ast.SetPwdStmt{Password: yyS[yypt-0].ident} - } - case 1850: - { - parser.yyVAL.statement = &ast.SetPwdStmt{User: yyS[yypt-2].item.(*auth.UserIdentity), Password: yyS[yypt-0].ident} - } - case 1851: - { - vars := yyS[yypt-0].item.([]*ast.VariableAssignment) - for _, v := range vars { - v.IsGlobal = true - } - parser.yyVAL.statement = &ast.SetStmt{Variables: vars} - } - case 1852: - { - parser.yyVAL.statement = &ast.SetStmt{Variables: yyS[yypt-0].item.([]*ast.VariableAssignment)} - } - case 1853: - { - assigns := yyS[yypt-0].item.([]*ast.VariableAssignment) - for i := 0; i < len(assigns); i++ { - if assigns[i].Name == "tx_isolation" { - // A special session variable that make setting tx_isolation take effect one time. - assigns[i].Name = "tx_isolation_one_shot" - } - } - parser.yyVAL.statement = &ast.SetStmt{Variables: assigns} - } - case 1854: - { - parser.yyVAL.statement = &ast.SetConfigStmt{Type: strings.ToLower(yyS[yypt-3].ident), Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr} - } - case 1855: - { - parser.yyVAL.statement = &ast.SetConfigStmt{Instance: yyS[yypt-3].ident, Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr} - } - case 1856: - { - parser.yyVAL.statement = &ast.SetSessionStatesStmt{SessionStates: yyS[yypt-0].ident} - } - case 1857: - { - parser.yyVAL.statement = &ast.SetResourceGroupStmt{Name: model.NewCIStr(yyS[yypt-0].ident)} - } - case 1858: - { - parser.yyVAL.statement = yyS[yypt-0].item.(*ast.SetRoleStmt) - } - case 1859: - { - tmp := yyS[yypt-2].item.(*ast.SetRoleStmt) - parser.yyVAL.statement = &ast.SetDefaultRoleStmt{ - SetRoleOpt: tmp.SetRoleOpt, - RoleList: tmp.RoleList, - UserList: yyS[yypt-0].item.([]*auth.UserIdentity), - } - } - case 1860: - { - parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleNone, RoleList: nil} - } - case 1861: - { - parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleAll, RoleList: nil} - } - case 1862: - { - parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleRegular, RoleList: yyS[yypt-0].item.([]*auth.RoleIdentity)} - } - case 1863: - { - parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleAllExcept, RoleList: yyS[yypt-0].item.([]*auth.RoleIdentity)} - } - case 1865: - { - parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleDefault, RoleList: nil} - } - case 1866: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.item = yyS[yypt-0].item - } else { - parser.yyVAL.item = []*ast.VariableAssignment{} - } - } - case 1867: - { - if yyS[yypt-0].item != nil { - varAssigns := yyS[yypt-0].item.([]*ast.VariableAssignment) - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.VariableAssignment), varAssigns...) - } else { - parser.yyVAL.item = yyS[yypt-2].item - } - } - case 1868: - { - varAssigns := []*ast.VariableAssignment{} - expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) - varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_isolation", Value: expr, IsSystem: true}) - parser.yyVAL.item = varAssigns - } - case 1869: - { - varAssigns := []*ast.VariableAssignment{} - expr := ast.NewValueExpr("0", parser.charset, parser.collation) - varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_only", Value: expr, IsSystem: true}) - parser.yyVAL.item = varAssigns - } - case 1870: - { - varAssigns := []*ast.VariableAssignment{} - expr := ast.NewValueExpr("1", parser.charset, parser.collation) - varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_only", Value: expr, IsSystem: true}) - parser.yyVAL.item = varAssigns - } - case 1871: - { - varAssigns := []*ast.VariableAssignment{} - asof := yyS[yypt-0].item.(*ast.AsOfClause) - if asof != nil { - varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_ts", Value: asof.TsExpr, IsSystem: true}) - } - parser.yyVAL.item = varAssigns - } - case 1872: - { - parser.yyVAL.ident = ast.RepeatableRead - } - case 1873: - { - parser.yyVAL.ident = ast.ReadCommitted - } - case 1874: - { - parser.yyVAL.ident = ast.ReadUncommitted - } - case 1875: - { - parser.yyVAL.ident = ast.Serializable - } - case 1876: - { - parser.yyVAL.expr = ast.NewValueExpr("ON", parser.charset, parser.collation) - } - case 1877: - { - parser.yyVAL.expr = ast.NewValueExpr("BINARY", parser.charset, parser.collation) - } - case 1882: - { - parser.yyVAL.ident = yyS[yypt-2].ident + "." + yyS[yypt-0].ident - } - case 1884: - { - parser.yyVAL.ident = yyS[yypt-2].ident + "." + yyS[yypt-0].ident - } - case 1885: - { - parser.yyVAL.ident = yyS[yypt-2].ident + "-" + yyS[yypt-0].ident - } - case 1886: - { - parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} - } - case 1887: - { - parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsGlobal: true, IsSystem: true} - } - case 1888: - { - parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} - } - case 1889: - { - parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} - } - case 1890: - { - v := strings.ToLower(yyS[yypt-2].ident) - var isGlobal bool - if strings.HasPrefix(v, "@@global.") { - isGlobal = true - v = strings.TrimPrefix(v, "@@global.") - } else if strings.HasPrefix(v, "@@session.") { - v = strings.TrimPrefix(v, "@@session.") - } else if strings.HasPrefix(v, "@@local.") { - v = strings.TrimPrefix(v, "@@local.") - } else if strings.HasPrefix(v, "@@") { - v = strings.TrimPrefix(v, "@@") - } - parser.yyVAL.item = &ast.VariableAssignment{Name: v, Value: yyS[yypt-0].expr, IsGlobal: isGlobal, IsSystem: true} - } - case 1891: - { - v := yyS[yypt-2].ident - v = strings.TrimPrefix(v, "@") - parser.yyVAL.item = &ast.VariableAssignment{Name: v, Value: yyS[yypt-0].expr} - } - case 1892: - { - parser.yyVAL.item = &ast.VariableAssignment{ - Name: ast.SetNames, - Value: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), - } - } - case 1893: - { - parser.yyVAL.item = &ast.VariableAssignment{ - Name: ast.SetNames, - Value: ast.NewValueExpr(yyS[yypt-2].ident, "", ""), - } - } - case 1894: - { - parser.yyVAL.item = &ast.VariableAssignment{ - Name: ast.SetNames, - Value: ast.NewValueExpr(yyS[yypt-2].ident, "", ""), - ExtendValue: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), - } - } - case 1895: - { - v := &ast.DefaultExpr{} - parser.yyVAL.item = &ast.VariableAssignment{Name: ast.SetNames, Value: v} - } - case 1896: - { - parser.yyVAL.item = &ast.VariableAssignment{Name: ast.SetCharset, Value: yyS[yypt-0].expr} - } - case 1897: - { - parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].ident, "", "") - } - case 1898: - { - parser.yyVAL.expr = &ast.DefaultExpr{} - } - case 1899: - { - // Validate input charset name to keep the same behavior as parser of MySQL. - cs, err := charset.GetCharsetInfo(yyS[yypt-0].ident) - if err != nil { - yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs(yyS[yypt-0].ident)) - return 1 - } - // Use charset name returned from charset.GetCharsetInfo(), - // to keep lower case of input for generated column restore. - parser.yyVAL.ident = cs.Name - } - case 1900: - { - parser.yyVAL.ident = charset.CharsetBin - } - case 1901: - { - info, err := charset.GetCollationByName(yyS[yypt-0].ident) - if err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.ident = info.Name - } - case 1902: - { - parser.yyVAL.ident = charset.CollationBin - } - case 1903: - { - parser.yyVAL.item = []*ast.VariableAssignment{yyS[yypt-0].item.(*ast.VariableAssignment)} - } - case 1904: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.VariableAssignment), yyS[yypt-0].item.(*ast.VariableAssignment)) - } - case 1907: - { - v := strings.ToLower(yyS[yypt-0].ident) - var isGlobal bool - explicitScope := true - if strings.HasPrefix(v, "@@global.") { - isGlobal = true - v = strings.TrimPrefix(v, "@@global.") - } else if strings.HasPrefix(v, "@@session.") { - v = strings.TrimPrefix(v, "@@session.") - } else if strings.HasPrefix(v, "@@local.") { - v = strings.TrimPrefix(v, "@@local.") - } else if strings.HasPrefix(v, "@@") { - v, explicitScope = strings.TrimPrefix(v, "@@"), false - } - parser.yyVAL.expr = &ast.VariableExpr{Name: v, IsGlobal: isGlobal, IsSystem: true, ExplicitScope: explicitScope} - } - case 1908: - { - v := yyS[yypt-0].ident - v = strings.TrimPrefix(v, "@") - parser.yyVAL.expr = &ast.VariableExpr{Name: v, IsGlobal: false, IsSystem: false} - } - case 1909: - { - parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} - } - case 1910: - { - parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-2].ident, Hostname: strings.ToLower(yyS[yypt-0].ident)} - } - case 1911: - { - parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-1].ident, Hostname: strings.ToLower(strings.TrimPrefix(yyS[yypt-0].ident, "@"))} - } - case 1912: - { - parser.yyVAL.item = &auth.UserIdentity{CurrentUser: true} - } - case 1913: - { - parser.yyVAL.item = []*auth.UserIdentity{yyS[yypt-0].item.(*auth.UserIdentity)} - } - case 1914: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*auth.UserIdentity), yyS[yypt-0].item.(*auth.UserIdentity)) - } - case 1916: - { - parser.yyVAL.ident = yyS[yypt-1].ident - } - case 1920: - { - parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-2].ident, Hostname: strings.ToLower(yyS[yypt-0].ident)} - } - case 1921: - { - parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-1].ident, Hostname: strings.ToLower(strings.TrimPrefix(yyS[yypt-0].ident, "@"))} - } - case 1922: - { - parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} - } - case 1923: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 1924: - { - parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} - } - case 1925: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 1926: - { - parser.yyVAL.item = []*auth.RoleIdentity{yyS[yypt-0].item.(*auth.RoleIdentity)} - } - case 1927: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*auth.RoleIdentity), yyS[yypt-0].item.(*auth.RoleIdentity)) - } - case 1928: - { - parser.yyVAL.item = &ast.LimitSimple{Offset: 0, Count: yyS[yypt-0].item.(uint64)} - } - case 1929: - { - parser.yyVAL.item = &ast.LimitSimple{Offset: yyS[yypt-2].item.(uint64), Count: yyS[yypt-0].item.(uint64)} - } - case 1930: - { - parser.yyVAL.item = &ast.LimitSimple{Offset: yyS[yypt-0].item.(uint64), Count: yyS[yypt-2].item.(uint64)} - } - case 1931: - { - parser.yyVAL.statement = &ast.AdminStmt{Tp: ast.AdminShowDDL} - } - case 1932: - { - stmt := &ast.AdminStmt{Tp: ast.AdminShowDDLJobs} - if yyS[yypt-0].item != nil { - stmt.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = stmt - } - case 1933: - { - stmt := &ast.AdminStmt{ - Tp: ast.AdminShowDDLJobs, - JobNumber: yyS[yypt-1].item.(int64), - } - if yyS[yypt-0].item != nil { - stmt.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = stmt - } - case 1934: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminShowNextRowID, - Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, - } - } - case 1935: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCheckTable, - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 1936: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCheckIndex, - Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, - Index: string(yyS[yypt-0].ident), - } - } - case 1937: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminRecoverIndex, - Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, - Index: string(yyS[yypt-0].ident), - } - } - case 1938: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCleanupIndex, - Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, - Index: string(yyS[yypt-0].ident), - } - } - case 1939: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCheckIndexRange, - Tables: []*ast.TableName{yyS[yypt-2].item.(*ast.TableName)}, - Index: string(yyS[yypt-1].ident), - HandleRanges: yyS[yypt-0].item.([]ast.HandleRange), - } - } - case 1940: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminChecksumTable, - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 1941: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCancelDDLJobs, - JobIDs: yyS[yypt-0].item.([]int64), - } - } - case 1942: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminPauseDDLJobs, - JobIDs: yyS[yypt-0].item.([]int64), - } - } - case 1943: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminResumeDDLJobs, - JobIDs: yyS[yypt-0].item.([]int64), - } - } - case 1944: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminShowDDLJobQueries, - JobIDs: yyS[yypt-0].item.([]int64), - } - } - case 1945: - { - ret := &ast.AdminStmt{ - Tp: ast.AdminShowDDLJobQueriesWithRange, - } - ret.LimitSimple.Count = yyS[yypt-0].item.(*ast.LimitSimple).Count - ret.LimitSimple.Offset = yyS[yypt-0].item.(*ast.LimitSimple).Offset - parser.yyVAL.statement = ret - } - case 1946: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminShowSlow, - ShowSlow: yyS[yypt-0].item.(*ast.ShowSlow), - } - } - case 1947: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminReloadExprPushdownBlacklist, - } - } - case 1948: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminReloadOptRuleBlacklist, - } - } - case 1949: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminPluginEnable, - Plugins: yyS[yypt-0].item.([]string), - } - } - case 1950: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminPluginDisable, - Plugins: yyS[yypt-0].item.([]string), - } - } - case 1951: - { - parser.yyVAL.statement = &ast.CleanupTableLockStmt{ - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 1952: - { - parser.yyVAL.statement = &ast.RepairTableStmt{ - Table: yyS[yypt-1].item.(*ast.TableName), - CreateStmt: yyS[yypt-0].statement.(*ast.CreateTableStmt), - } - } - case 1953: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminFlushBindings, - } - } - case 1954: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminCaptureBindings, - } - } - case 1955: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminEvolveBindings, - } - } - case 1956: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminReloadBindings, - } - } - case 1957: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminReloadStatistics, - } - } - case 1958: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminReloadStatistics, - } - } - case 1959: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminShowTelemetry, - } - } - case 1960: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminResetTelemetryID, - } - } - case 1961: - { - parser.yyVAL.statement = &ast.AdminStmt{ - Tp: ast.AdminFlushPlanCache, - StatementScope: yyS[yypt-1].item.(ast.StatementScope), - } - } - case 1962: - { - parser.yyVAL.item = &ast.ShowSlow{ - Tp: ast.ShowSlowRecent, - Count: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 1963: - { - parser.yyVAL.item = &ast.ShowSlow{ - Tp: ast.ShowSlowTop, - Kind: ast.ShowSlowKindDefault, - Count: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 1964: - { - parser.yyVAL.item = &ast.ShowSlow{ - Tp: ast.ShowSlowTop, - Kind: ast.ShowSlowKindInternal, - Count: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 1965: - { - parser.yyVAL.item = &ast.ShowSlow{ - Tp: ast.ShowSlowTop, - Kind: ast.ShowSlowKindAll, - Count: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 1966: - { - parser.yyVAL.item = []ast.HandleRange{yyS[yypt-0].item.(ast.HandleRange)} - } - case 1967: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.HandleRange), yyS[yypt-0].item.(ast.HandleRange)) - } - case 1968: - { - parser.yyVAL.item = ast.HandleRange{Begin: yyS[yypt-3].item.(int64), End: yyS[yypt-1].item.(int64)} - } - case 1969: - { - parser.yyVAL.item = []int64{yyS[yypt-0].item.(int64)} - } - case 1970: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]int64), yyS[yypt-0].item.(int64)) - } - case 1971: - { - stmt := yyS[yypt-1].item.(*ast.ShowStmt) - if yyS[yypt-0].item != nil { - if x, ok := yyS[yypt-0].item.(*ast.PatternLikeOrIlikeExpr); ok && x.Expr == nil { - stmt.Pattern = x - } else { - stmt.Where = yyS[yypt-0].item.(ast.ExprNode) - } - } - parser.yyVAL.statement = stmt - } - case 1972: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateTable, - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 1973: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateView, - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 1974: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateDatabase, - IfNotExists: yyS[yypt-1].item.(bool), - DBName: yyS[yypt-0].ident, - } - } - case 1975: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateSequence, - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 1976: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreatePlacementPolicy, - DBName: yyS[yypt-0].ident, - } - } - case 1977: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateResourceGroup, - ResourceGroupName: yyS[yypt-0].ident, - } - } - case 1978: - { - // See https://dev.mysql.com/doc/refman/5.7/en/show-create-user.html - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateUser, - User: yyS[yypt-0].item.(*auth.UserIdentity), - } - } - case 1979: - { - stmt := &ast.ShowStmt{ - Tp: ast.ShowRegions, - Table: yyS[yypt-3].item.(*ast.TableName), - } - stmt.Table.PartitionNames = yyS[yypt-2].item.([]model.CIStr) - if yyS[yypt-0].item != nil { - stmt.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = stmt - } - case 1980: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowTableNextRowId, - Table: yyS[yypt-1].item.(*ast.TableName), - } - } - case 1981: - { - stmt := &ast.ShowStmt{ - Tp: ast.ShowRegions, - Table: yyS[yypt-5].item.(*ast.TableName), - IndexName: model.NewCIStr(yyS[yypt-2].ident), - } - stmt.Table.PartitionNames = yyS[yypt-4].item.([]model.CIStr) - if yyS[yypt-0].item != nil { - stmt.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = stmt - } - case 1982: - { - // See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html - parser.yyVAL.statement = &ast.ShowStmt{Tp: ast.ShowGrants} - } - case 1983: - { - // See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html - if yyS[yypt-0].item != nil { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowGrants, - User: yyS[yypt-1].item.(*auth.UserIdentity), - Roles: yyS[yypt-0].item.([]*auth.RoleIdentity), - } - } else { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowGrants, - User: yyS[yypt-1].item.(*auth.UserIdentity), - Roles: nil, - } - } - } - case 1984: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowMasterStatus, - } - } - case 1985: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowProcessList, - Full: yyS[yypt-1].item.(bool), - } - } - case 1986: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowProfiles, - } - } - case 1987: - { - v := &ast.ShowStmt{ - Tp: ast.ShowProfile, - } - if yyS[yypt-2].item != nil { - v.ShowProfileTypes = yyS[yypt-2].item.([]int) - } - if yyS[yypt-1].item != nil { - v.ShowProfileArgs = yyS[yypt-1].item.(*int64) - } - if yyS[yypt-0].item != nil { - v.ShowProfileLimit = yyS[yypt-0].item.(*ast.Limit) - } - parser.yyVAL.statement = v - } - case 1988: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowPrivileges, - } - } - case 1989: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowBuiltins, - } - } - case 1990: - { - parser.yyVAL.statement = yyS[yypt-0].item.(*ast.ShowStmt) - } - case 1991: - { - v := yyS[yypt-0].item.(int64) - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowImportJobs, - ImportJobID: &v, - } - } - case 1992: - { - parser.yyVAL.statement = &ast.ShowStmt{ - Tp: ast.ShowCreateProcedure, - Procedure: yyS[yypt-0].item.(*ast.TableName), - } - } - case 1993: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowPlacementForDatabase, - DBName: yyS[yypt-0].ident, - } - } - case 1994: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowPlacementForTable, - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 1995: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowPlacementForPartition, - Table: yyS[yypt-2].item.(*ast.TableName), - Partition: model.NewCIStr(yyS[yypt-0].ident), - } - } - case 1996: - { - parser.yyVAL.item = nil - } - case 1998: - { - parser.yyVAL.item = []int{yyS[yypt-0].item.(int)} - } - case 1999: - { - l := yyS[yypt-2].item.([]int) - l = append(l, yyS[yypt-0].item.(int)) - parser.yyVAL.item = l - } - case 2000: - { - parser.yyVAL.item = ast.ProfileTypeCPU - } - case 2001: - { - parser.yyVAL.item = ast.ProfileTypeMemory - } - case 2002: - { - parser.yyVAL.item = ast.ProfileTypeBlockIo - } - case 2003: - { - parser.yyVAL.item = ast.ProfileTypeContextSwitch - } - case 2004: - { - parser.yyVAL.item = ast.ProfileTypePageFaults - } - case 2005: - { - parser.yyVAL.item = ast.ProfileTypeIpc - } - case 2006: - { - parser.yyVAL.item = ast.ProfileTypeSwaps - } - case 2007: - { - parser.yyVAL.item = ast.ProfileTypeSource - } - case 2008: - { - parser.yyVAL.item = ast.ProfileTypeAll - } - case 2009: - { - parser.yyVAL.item = nil - } - case 2010: - { - v := yyS[yypt-0].item.(int64) - parser.yyVAL.item = &v - } - case 2011: - { - parser.yyVAL.item = nil - } - case 2012: - { - parser.yyVAL.item = yyS[yypt-0].item.([]*auth.RoleIdentity) - } - case 2018: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowEngines} - } - case 2019: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowDatabases} - } - case 2020: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowConfig} - } - case 2021: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowCharset} - } - case 2022: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowTables, - DBName: yyS[yypt-0].ident, - Full: yyS[yypt-2].item.(bool), - } - } - case 2023: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowOpenTables, - DBName: yyS[yypt-0].ident, - } - } - case 2024: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowTableStatus, - DBName: yyS[yypt-0].ident, - } - } - case 2025: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowIndex, - Table: yyS[yypt-0].item.(*ast.TableName), - } - } - case 2026: - { - show := &ast.ShowStmt{ - Tp: ast.ShowIndex, - Table: &ast.TableName{Name: model.NewCIStr(yyS[yypt-2].ident), Schema: model.NewCIStr(yyS[yypt-0].ident)}, - } - parser.yyVAL.item = show - } - case 2027: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowColumns, - Table: yyS[yypt-1].item.(*ast.TableName), - DBName: yyS[yypt-0].ident, - Full: yyS[yypt-3].item.(bool), - } - } - case 2028: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowColumns, - Table: yyS[yypt-1].item.(*ast.TableName), - DBName: yyS[yypt-0].ident, - Full: yyS[yypt-3].item.(bool), - Extended: true, - } - } - case 2029: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowWarnings, CountWarningsOrErrors: true} - } - case 2030: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowWarnings} - } - case 2031: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowErrors, CountWarningsOrErrors: true} - } - case 2032: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowErrors} - } - case 2033: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowVariables, - GlobalScope: yyS[yypt-1].item.(bool), - } - } - case 2034: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowStatus, - GlobalScope: yyS[yypt-1].item.(bool), - } - } - case 2035: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowBindings, - GlobalScope: yyS[yypt-1].item.(bool), - } - } - case 2036: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowCollation, - } - } - case 2037: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowTriggers, - DBName: yyS[yypt-0].ident, - } - } - case 2038: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowBindingCacheStatus, - } - } - case 2039: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowProcedureStatus, - } - } - case 2040: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowPumpStatus, - } - } - case 2041: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowDrainerStatus, - } - } - case 2042: - { - // This statement is similar to SHOW PROCEDURE STATUS but for stored functions. - // See http://dev.mysql.com/doc/refman/5.7/en/show-function-status.html - // We do not support neither stored functions nor stored procedures. - // So we reuse show procedure status process logic. - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowFunctionStatus, - } - } - case 2043: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowEvents, - DBName: yyS[yypt-0].ident, - } - } - case 2044: - { - parser.yyVAL.item = &ast.ShowStmt{ - Tp: ast.ShowPlugins, - } - } - case 2045: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowSessionStates} - } - case 2046: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsExtended} - } - case 2047: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsMeta, Table: &ast.TableName{Name: model.NewCIStr("STATS_META"), Schema: model.NewCIStr(mysql.SystemDB)}} - } - case 2048: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsHistograms, Table: &ast.TableName{Name: model.NewCIStr("STATS_HISTOGRAMS"), Schema: model.NewCIStr(mysql.SystemDB)}} - } - case 2049: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsTopN} - } - case 2050: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsBuckets, Table: &ast.TableName{Name: model.NewCIStr("STATS_BUCKETS"), Schema: model.NewCIStr(mysql.SystemDB)}} - } - case 2051: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsHealthy} - } - case 2052: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsLocked, Table: &ast.TableName{Name: model.NewCIStr("STATS_TABLE_LOCKED"), Schema: model.NewCIStr(mysql.SystemDB)}} - } - case 2053: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowHistogramsInFlight} - } - case 2054: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowColumnStatsUsage} - } - case 2055: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowAnalyzeStatus} - } - case 2056: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowBackups} - } - case 2057: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowRestores} - } - case 2058: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowPlacement} - } - case 2059: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowPlacementLabels} - } - case 2060: - { - parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowImportJobs} - } - case 2061: - { - parser.yyVAL.item = nil - } - case 2062: - { - parser.yyVAL.item = &ast.PatternLikeOrIlikeExpr{ - Pattern: yyS[yypt-0].expr, - Escape: '\\', - IsLike: true, - } - } - case 2063: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 2064: - { - parser.yyVAL.item = false - } - case 2065: - { - parser.yyVAL.item = true - } - case 2066: - { - parser.yyVAL.item = false - } - case 2067: - { - parser.yyVAL.item = ast.StatementScopeSession - } - case 2068: - { - parser.yyVAL.item = ast.StatementScopeGlobal - } - case 2069: - { - parser.yyVAL.item = ast.StatementScopeInstance - } - case 2070: - { - parser.yyVAL.item = ast.StatementScopeSession - } - case 2071: - { - parser.yyVAL.item = false - } - case 2072: - { - parser.yyVAL.item = true - } - case 2073: - { - parser.yyVAL.ident = "" - } - case 2074: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 2075: - { - parser.yyVAL.item = yyS[yypt-0].item.(*ast.TableName) - } - case 2076: - { - tmp := yyS[yypt-0].item.(*ast.FlushStmt) - tmp.NoWriteToBinLog = yyS[yypt-1].item.(bool) - parser.yyVAL.statement = tmp - } - case 2077: - { - parser.yyVAL.item = []string{yyS[yypt-0].ident} - } - case 2078: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) - } - case 2079: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushPrivileges, - } - } - case 2080: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushStatus, - } - } - case 2081: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushTiDBPlugin, - Plugins: yyS[yypt-0].item.([]string), - } - } - case 2082: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushHosts, - } - } - case 2083: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushLogs, - LogType: yyS[yypt-1].item.(ast.LogType), - } - } - case 2084: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushTables, - Tables: yyS[yypt-1].item.([]*ast.TableName), - ReadLock: yyS[yypt-0].item.(bool), - } - } - case 2085: - { - parser.yyVAL.item = &ast.FlushStmt{ - Tp: ast.FlushClientErrorsSummary, - } - } - case 2086: - { - parser.yyVAL.item = ast.LogTypeDefault - } - case 2087: - { - parser.yyVAL.item = ast.LogTypeBinary - } - case 2088: - { - parser.yyVAL.item = ast.LogTypeEngine - } - case 2089: - { - parser.yyVAL.item = ast.LogTypeError - } - case 2090: - { - parser.yyVAL.item = ast.LogTypeGeneral - } - case 2091: - { - parser.yyVAL.item = ast.LogTypeSlow - } - case 2092: - { - parser.yyVAL.item = false - } - case 2093: - { - parser.yyVAL.item = true - } - case 2094: - { - parser.yyVAL.item = true - } - case 2095: - { - parser.yyVAL.item = []*ast.TableName{} - } - case 2097: - { - parser.yyVAL.item = []*ast.TableName{} - } - case 2098: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2099: - { - parser.yyVAL.item = false - } - case 2100: - { - parser.yyVAL.item = true - } - case 2180: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2208: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2224: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2226: - { - if yyS[yypt-0].statement != nil { - s := yyS[yypt-0].statement - if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(parser.lexer.client, lexer.stmtText()) - } - parser.result = append(parser.result, s) - } - } - case 2227: - { - if yyS[yypt-0].statement != nil { - s := yyS[yypt-0].statement - if lexer, ok := yylex.(stmtTexter); ok { - s.SetText(parser.lexer.client, lexer.stmtText()) - } - parser.result = append(parser.result, s) - } - } - case 2228: - { - cst := yyS[yypt-0].item.(*ast.Constraint) - if yyS[yypt-1].item != nil { - cst.Name = yyS[yypt-1].item.(string) - cst.IsEmptyIndex = len(cst.Name) == 0 - } - parser.yyVAL.item = cst - } - case 2233: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.item = []interface{}{yyS[yypt-0].item.(interface{})} - } else { - parser.yyVAL.item = []interface{}{} - } - } - case 2234: - { - if yyS[yypt-0].item != nil { - parser.yyVAL.item = append(yyS[yypt-2].item.([]interface{}), yyS[yypt-0].item) - } else { - parser.yyVAL.item = yyS[yypt-2].item - } - } - case 2235: - { - var columnDefs []*ast.ColumnDef - var constraints []*ast.Constraint - parser.yyVAL.item = &ast.CreateTableStmt{ - Cols: columnDefs, - Constraints: constraints, - } - } - case 2236: - { - tes := yyS[yypt-1].item.([]interface{}) - var columnDefs []*ast.ColumnDef - var constraints []*ast.Constraint - for _, te := range tes { - switch te := te.(type) { - case *ast.ColumnDef: - columnDefs = append(columnDefs, te) - case *ast.Constraint: - constraints = append(constraints, te) - } - } - parser.yyVAL.item = &ast.CreateTableStmt{ - Cols: columnDefs, - Constraints: constraints, - } - } - case 2238: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCharset, StrValue: yyS[yypt-0].ident, - UintValue: ast.TableOptionCharsetWithoutConvertTo} - } - case 2239: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident, - UintValue: ast.TableOptionCharsetWithoutConvertTo} - } - case 2240: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoIncrement, UintValue: yyS[yypt-0].item.(uint64), BoolValue: yyS[yypt-3].item.(bool)} - } - case 2241: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoIdCache, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2242: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoRandomBase, UintValue: yyS[yypt-0].item.(uint64), BoolValue: yyS[yypt-3].item.(bool)} - } - case 2243: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAvgRowLength, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2244: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionConnection, StrValue: yyS[yypt-0].ident} - } - case 2245: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCheckSum, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2246: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionTableCheckSum, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2247: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionPassword, StrValue: yyS[yypt-0].ident} - } - case 2248: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCompression, StrValue: yyS[yypt-0].ident} - } - case 2249: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionKeyBlockSize, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2250: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionDelayKeyWrite, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2251: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionRowFormat, UintValue: yyS[yypt-0].item.(uint64)} - } - case 2252: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsPersistent} - } - case 2253: - { - n := yyS[yypt-0].item.(uint64) - if n != 0 && n != 1 { - yylex.AppendError(yylex.Errorf("The value of STATS_AUTO_RECALC must be one of [0|1|DEFAULT].")) - return 1 - } - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsAutoRecalc, UintValue: n} - yylex.AppendError(yylex.Errorf("The STATS_AUTO_RECALC is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 2254: - { - parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsAutoRecalc, Default: true} - yylex.AppendError(yylex.Errorf("The STATS_AUTO_RECALC is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - } - case 2255: - { - // Parse it but will ignore it. - // In MySQL, STATS_SAMPLE_PAGES=N(Where 0 mysql.MaxFloatPrecisionLength { - tp.SetType(mysql.TypeDouble) - } - tp.SetFlen(types.UnspecifiedLength) - } - tp.SetDecimal(fopt.Decimal) - for _, o := range yyS[yypt-0].item.([]*ast.TypeOpt) { - if o.IsUnsigned { - tp.AddFlag(mysql.UnsignedFlag) - } - if o.IsZerofill { - tp.AddFlag(mysql.ZerofillFlag) - } - } - parser.yyVAL.item = tp - } - case 2308: - { - tp := types.NewFieldType(yyS[yypt-1].item.(byte)) - tp.SetFlen(yyS[yypt-0].item.(int)) - if tp.GetFlen() == types.UnspecifiedLength { - tp.SetFlen(1) - } - parser.yyVAL.item = tp - } - case 2309: - { - parser.yyVAL.item = mysql.TypeTiny - } - case 2310: - { - parser.yyVAL.item = mysql.TypeShort - } - case 2311: - { - parser.yyVAL.item = mysql.TypeInt24 - } - case 2312: - { - parser.yyVAL.item = mysql.TypeInt24 - } - case 2313: - { - parser.yyVAL.item = mysql.TypeLong - } - case 2314: - { - parser.yyVAL.item = mysql.TypeTiny - } - case 2315: - { - parser.yyVAL.item = mysql.TypeShort - } - case 2316: - { - parser.yyVAL.item = mysql.TypeInt24 - } - case 2317: - { - parser.yyVAL.item = mysql.TypeLong - } - case 2318: - { - parser.yyVAL.item = mysql.TypeLonglong - } - case 2319: - { - parser.yyVAL.item = mysql.TypeLong - } - case 2320: - { - parser.yyVAL.item = mysql.TypeLonglong - } - case 2321: - { - parser.yyVAL.item = mysql.TypeTiny - } - case 2322: - { - parser.yyVAL.item = mysql.TypeTiny - } - case 2326: - { - parser.yyVAL.item = mysql.TypeNewDecimal - } - case 2327: - { - parser.yyVAL.item = mysql.TypeNewDecimal - } - case 2328: - { - parser.yyVAL.item = mysql.TypeNewDecimal - } - case 2329: - { - parser.yyVAL.item = mysql.TypeFloat - } - case 2330: - { - if parser.lexer.GetSQLMode().HasRealAsFloatMode() { - parser.yyVAL.item = mysql.TypeFloat - } else { - parser.yyVAL.item = mysql.TypeDouble - } - } - case 2331: - { - parser.yyVAL.item = mysql.TypeDouble - } - case 2332: - { - parser.yyVAL.item = mysql.TypeDouble - } - case 2333: - { - parser.yyVAL.item = mysql.TypeFloat - } - case 2334: - { - parser.yyVAL.item = mysql.TypeDouble - } - case 2335: - { - parser.yyVAL.item = mysql.TypeBit - } - case 2336: - { - tp := types.NewFieldType(mysql.TypeString) - tp.SetFlen(yyS[yypt-1].item.(int)) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2337: - { - tp := types.NewFieldType(mysql.TypeString) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2338: - { - tp := types.NewFieldType(mysql.TypeString) - tp.SetFlen(yyS[yypt-1].item.(int)) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2339: - { - tp := types.NewFieldType(mysql.TypeString) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2340: - { - tp := types.NewFieldType(mysql.TypeVarchar) - tp.SetFlen(yyS[yypt-1].item.(int)) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2341: - { - tp := types.NewFieldType(mysql.TypeVarchar) - tp.SetFlen(yyS[yypt-1].item.(int)) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2342: - { - tp := types.NewFieldType(mysql.TypeString) - tp.SetFlen(yyS[yypt-0].item.(int)) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CharsetBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 2343: - { - tp := types.NewFieldType(mysql.TypeVarchar) - tp.SetFlen(yyS[yypt-0].item.(int)) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CharsetBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 2344: - { - tp := yyS[yypt-0].item.(*types.FieldType) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CharsetBin) - tp.AddFlag(mysql.BinaryFlag) - parser.yyVAL.item = tp - } - case 2345: - { - tp := yyS[yypt-1].item.(*types.FieldType) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { - tp.AddFlag(mysql.BinaryFlag) - tp.SetCollate(charset.CollationBin) - } - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2346: - { - tp := types.NewFieldType(mysql.TypeEnum) - elems := yyS[yypt-2].item.([]*ast.TextString) - opt := yyS[yypt-0].item.(*ast.OptBinary) - tp.SetElems(make([]string, len(elems))) - fieldLen := -1 // enum_flen = max(ele_flen) - for i, e := range elems { - trimmed := strings.TrimRight(e.Value, " ") - tp.SetElemWithIsBinaryLit(i, trimmed, e.IsBinaryLiteral) - if len(trimmed) > fieldLen { - fieldLen = len(trimmed) - } - } - tp.SetFlen(fieldLen) - tp.SetCharset(opt.Charset) - if opt.IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2347: - { - tp := types.NewFieldType(mysql.TypeSet) - elems := yyS[yypt-2].item.([]*ast.TextString) - opt := yyS[yypt-0].item.(*ast.OptBinary) - tp.SetElems(make([]string, len(elems))) - fieldLen := len(elems) - 1 // set_flen = sum(ele_flen) + number_of_ele - 1 - for i, e := range elems { - trimmed := strings.TrimRight(e.Value, " ") - tp.SetElemWithIsBinaryLit(i, trimmed, e.IsBinaryLiteral) - fieldLen += len(trimmed) - } - tp.SetFlen(fieldLen) - tp.SetCharset(opt.Charset) - if opt.IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2348: - { - tp := types.NewFieldType(mysql.TypeJSON) - tp.SetDecimal(0) - tp.SetCharset(charset.CharsetBin) - tp.SetCollate(charset.CollationBin) - parser.yyVAL.item = tp - } - case 2349: - { - tp := types.NewFieldType(mysql.TypeMediumBlob) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { - tp.AddFlag(mysql.BinaryFlag) - tp.SetCollate(charset.CollationBin) - } - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2350: - { - tp := types.NewFieldType(mysql.TypeMediumBlob) - tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) - if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { - tp.AddFlag(mysql.BinaryFlag) - tp.SetCollate(charset.CollationBin) - } - if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { - tp.AddFlag(mysql.BinaryFlag) - } - parser.yyVAL.item = tp - } - case 2370: - { - tp := types.NewFieldType(mysql.TypeTinyBlob) - parser.yyVAL.item = tp - } - case 2371: - { - tp := types.NewFieldType(mysql.TypeBlob) - tp.SetFlen(yyS[yypt-0].item.(int)) - parser.yyVAL.item = tp - } - case 2372: - { - tp := types.NewFieldType(mysql.TypeMediumBlob) - parser.yyVAL.item = tp - } - case 2373: - { - tp := types.NewFieldType(mysql.TypeLongBlob) - parser.yyVAL.item = tp - } - case 2374: - { - tp := types.NewFieldType(mysql.TypeMediumBlob) - parser.yyVAL.item = tp - } - case 2375: - { - tp := types.NewFieldType(mysql.TypeTinyBlob) - parser.yyVAL.item = tp - } - case 2376: - { - tp := types.NewFieldType(mysql.TypeBlob) - tp.SetFlen(yyS[yypt-0].item.(int)) - parser.yyVAL.item = tp - } - case 2377: - { - tp := types.NewFieldType(mysql.TypeMediumBlob) - parser.yyVAL.item = tp - } - case 2378: - { - tp := types.NewFieldType(mysql.TypeLongBlob) - parser.yyVAL.item = tp - } - case 2380: - { - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: false, - Charset: charset.CharsetLatin1, - } - } - case 2381: - { - cs, err := charset.GetCharsetInfo("ucs2") - if err != nil { - yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs("ucs2")) - return 1 - } - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: false, - Charset: cs.Name, - } - } - case 2382: - { - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: false, - Charset: charset.CharsetBin, - } - } - case 2383: - { - tp := types.NewFieldType(mysql.TypeDate) - parser.yyVAL.item = tp - } - case 2384: - { - tp := types.NewFieldType(mysql.TypeDatetime) - tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) - tp.SetDecimal(yyS[yypt-0].item.(int)) - if tp.GetDecimal() > 0 { - tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) - } - parser.yyVAL.item = tp - } - case 2385: - { - tp := types.NewFieldType(mysql.TypeTimestamp) - tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) - tp.SetDecimal(yyS[yypt-0].item.(int)) - if tp.GetDecimal() > 0 { - tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) - } - parser.yyVAL.item = tp - } - case 2386: - { - tp := types.NewFieldType(mysql.TypeDuration) - tp.SetFlen(mysql.MaxDurationWidthNoFsp) - tp.SetDecimal(yyS[yypt-0].item.(int)) - if tp.GetDecimal() > 0 { - tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) - } - parser.yyVAL.item = tp - } - case 2387: - { - tp := types.NewFieldType(mysql.TypeYear) - tp.SetFlen(yyS[yypt-1].item.(int)) - if tp.GetFlen() != types.UnspecifiedLength && tp.GetFlen() != 4 { - yylex.AppendError(ErrInvalidYearColumnLength.GenWithStackByArgs()) - return -1 - } - parser.yyVAL.item = tp - } - case 2388: - { - parser.yyVAL.item = int(yyS[yypt-1].item.(uint64)) - } - case 2389: - { - parser.yyVAL.item = types.UnspecifiedLength - } - case 2391: - { - parser.yyVAL.item = &ast.TypeOpt{IsUnsigned: true} - } - case 2392: - { - parser.yyVAL.item = &ast.TypeOpt{IsUnsigned: false} - } - case 2393: - { - parser.yyVAL.item = &ast.TypeOpt{IsZerofill: true, IsUnsigned: true} - } - case 2394: - { - parser.yyVAL.item = []*ast.TypeOpt{} - } - case 2395: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.TypeOpt), yyS[yypt-0].item.(*ast.TypeOpt)) - } - case 2396: - { - parser.yyVAL.item = &ast.FloatOpt{Flen: types.UnspecifiedLength, Decimal: types.UnspecifiedLength} - } - case 2397: - { - parser.yyVAL.item = &ast.FloatOpt{Flen: yyS[yypt-0].item.(int), Decimal: types.UnspecifiedLength} - } - case 2399: - { - parser.yyVAL.item = &ast.FloatOpt{Flen: int(yyS[yypt-3].item.(uint64)), Decimal: int(yyS[yypt-1].item.(uint64))} - } - case 2400: - { - parser.yyVAL.item = false - } - case 2401: - { - parser.yyVAL.item = true - } - case 2402: - { - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: false, - Charset: "", - } - } - case 2403: - { - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: true, - Charset: yyS[yypt-0].ident, - } - } - case 2404: - { - parser.yyVAL.item = &ast.OptBinary{ - IsBinary: yyS[yypt-0].item.(bool), - Charset: yyS[yypt-1].ident, - } - } - case 2405: - { - parser.yyVAL.ident = "" - } - case 2406: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 2410: - { - parser.yyVAL.ident = "" - } - case 2411: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 2412: - { - parser.yyVAL.item = []string{yyS[yypt-0].ident} - } - case 2413: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) - } - case 2414: - { - parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].ident} - } - case 2415: - { - parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].item.(ast.BinaryLiteral).ToString(), IsBinaryLiteral: true} - } - case 2416: - { - parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].item.(ast.BinaryLiteral).ToString(), IsBinaryLiteral: true} - } - case 2417: - { - parser.yyVAL.item = []*ast.TextString{yyS[yypt-0].item.(*ast.TextString)} - } - case 2418: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TextString), yyS[yypt-0].item.(*ast.TextString)) - } - case 2425: - { - u := yyS[yypt-0].statement.(*ast.UpdateStmt) - u.With = yyS[yypt-1].item.(*ast.WithClause) - parser.yyVAL.statement = u - } - case 2426: - { - var refs *ast.Join - if x, ok := yyS[yypt-5].item.(*ast.Join); ok { - refs = x - } else { - refs = &ast.Join{Left: yyS[yypt-5].item.(ast.ResultSetNode)} - } - st := &ast.UpdateStmt{ - Priority: yyS[yypt-7].item.(mysql.PriorityEnum), - TableRefs: &ast.TableRefsClause{TableRefs: refs}, - List: yyS[yypt-3].item.([]*ast.Assignment), - IgnoreErr: yyS[yypt-6].item.(bool), - } - if yyS[yypt-8].item != nil { - st.TableHints = yyS[yypt-8].item.([]*ast.TableOptimizerHint) - } - if yyS[yypt-2].item != nil { - st.Where = yyS[yypt-2].item.(ast.ExprNode) - } - if yyS[yypt-1].item != nil { - st.Order = yyS[yypt-1].item.(*ast.OrderByClause) - } - if yyS[yypt-0].item != nil { - st.Limit = yyS[yypt-0].item.(*ast.Limit) - } - parser.yyVAL.statement = st - } - case 2427: - { - st := &ast.UpdateStmt{ - Priority: yyS[yypt-5].item.(mysql.PriorityEnum), - TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-3].item.(*ast.Join)}, - List: yyS[yypt-1].item.([]*ast.Assignment), - IgnoreErr: yyS[yypt-4].item.(bool), - } - if yyS[yypt-6].item != nil { - st.TableHints = yyS[yypt-6].item.([]*ast.TableOptimizerHint) - } - if yyS[yypt-0].item != nil { - st.Where = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.statement = st - } - case 2428: - { - parser.yyVAL.statement = &ast.UseStmt{DBName: yyS[yypt-0].ident} - } - case 2429: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 2430: - { - parser.yyVAL.item = nil - } - case 2434: - { - // See https://dev.mysql.com/doc/refman/8.0/en/create-user.html - ret := &ast.CreateUserStmt{ - IsCreateRole: false, - IfNotExists: yyS[yypt-6].item.(bool), - Specs: yyS[yypt-5].item.([]*ast.UserSpec), - AuthTokenOrTLSOptions: yyS[yypt-4].item.([]*ast.AuthTokenOrTLSOption), - ResourceOptions: yyS[yypt-3].item.([]*ast.ResourceOption), - PasswordOrLockOptions: yyS[yypt-2].item.([]*ast.PasswordOrLockOption), - } - if yyS[yypt-1].item != nil { - ret.CommentOrAttributeOption = yyS[yypt-1].item.(*ast.CommentOrAttributeOption) - } - if yyS[yypt-0].item != nil { - ret.ResourceGroupNameOption = yyS[yypt-0].item.(*ast.ResourceGroupNameOption) - } - parser.yyVAL.statement = ret - } - case 2435: - { - // See https://dev.mysql.com/doc/refman/8.0/en/create-role.html - parser.yyVAL.statement = &ast.CreateUserStmt{ - IsCreateRole: true, - IfNotExists: yyS[yypt-1].item.(bool), - Specs: yyS[yypt-0].item.([]*ast.UserSpec), - } - } - case 2436: - { - ret := &ast.AlterUserStmt{ - IfExists: yyS[yypt-6].item.(bool), - Specs: yyS[yypt-5].item.([]*ast.UserSpec), - AuthTokenOrTLSOptions: yyS[yypt-4].item.([]*ast.AuthTokenOrTLSOption), - ResourceOptions: yyS[yypt-3].item.([]*ast.ResourceOption), - PasswordOrLockOptions: yyS[yypt-2].item.([]*ast.PasswordOrLockOption), - } - if yyS[yypt-1].item != nil { - ret.CommentOrAttributeOption = yyS[yypt-1].item.(*ast.CommentOrAttributeOption) - } - if yyS[yypt-0].item != nil { - ret.ResourceGroupNameOption = yyS[yypt-0].item.(*ast.ResourceGroupNameOption) - } - parser.yyVAL.statement = ret - } - case 2437: - { - auth := &ast.AuthOption{ - AuthString: yyS[yypt-0].ident, - ByAuthString: true, - } - parser.yyVAL.statement = &ast.AlterUserStmt{ - IfExists: yyS[yypt-6].item.(bool), - CurrentAuth: auth, - } - } - case 2438: - { - parser.yyVAL.statement = yyS[yypt-0].item.(*ast.AlterInstanceStmt) - } - case 2439: - { - option := yyS[yypt-0].item.(*ast.PlacementOption) - parser.yyVAL.statement = &ast.AlterRangeStmt{RangeName: model.NewCIStr(yyS[yypt-1].ident), PlacementOption: option} - } - case 2440: - { - parser.yyVAL.item = &ast.AlterInstanceStmt{ - ReloadTLS: true, - } - } - case 2441: - { - parser.yyVAL.item = &ast.AlterInstanceStmt{ - ReloadTLS: true, - NoRollbackOnError: true, - } - } - case 2442: - { - userSpec := &ast.UserSpec{ - User: yyS[yypt-1].item.(*auth.UserIdentity), - } - if yyS[yypt-0].item != nil { - userSpec.AuthOpt = yyS[yypt-0].item.(*ast.AuthOption) - } - parser.yyVAL.item = userSpec - } - case 2443: - { - parser.yyVAL.item = []*ast.UserSpec{yyS[yypt-0].item.(*ast.UserSpec)} - } - case 2444: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserSpec), yyS[yypt-0].item.(*ast.UserSpec)) - } - case 2445: - { - l := []*ast.ResourceOption{} - parser.yyVAL.item = l - } - case 2446: - { - parser.yyVAL.item = yyS[yypt-0].item - yylex.AppendError(yylex.Errorf("TiDB does not support WITH ConnectionOptions now, they would be parsed but ignored.")) - parser.lastErrorAsWarn() - } - case 2447: - { - parser.yyVAL.item = []*ast.ResourceOption{yyS[yypt-0].item.(*ast.ResourceOption)} - } - case 2448: - { - l := yyS[yypt-1].item.([]*ast.ResourceOption) - l = append(l, yyS[yypt-0].item.(*ast.ResourceOption)) - parser.yyVAL.item = l - } - case 2449: - { - parser.yyVAL.item = &ast.ResourceOption{ - Type: ast.MaxQueriesPerHour, - Count: yyS[yypt-0].item.(int64), - } - } - case 2450: - { - parser.yyVAL.item = &ast.ResourceOption{ - Type: ast.MaxUpdatesPerHour, - Count: yyS[yypt-0].item.(int64), - } - } - case 2451: - { - parser.yyVAL.item = &ast.ResourceOption{ - Type: ast.MaxConnectionsPerHour, - Count: yyS[yypt-0].item.(int64), - } - } - case 2452: - { - parser.yyVAL.item = &ast.ResourceOption{ - Type: ast.MaxUserConnections, - Count: yyS[yypt-0].item.(int64), - } - } - case 2453: - { - parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{} - } - case 2455: - { - t := &ast.AuthTokenOrTLSOption{ - Type: ast.TlsNone, - } - parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} - } - case 2456: - { - t := &ast.AuthTokenOrTLSOption{ - Type: ast.Ssl, - } - parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} - } - case 2457: - { - t := &ast.AuthTokenOrTLSOption{ - Type: ast.X509, - } - parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} - } - case 2458: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2459: - { - parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)} - } - case 2460: - { - l := yyS[yypt-2].item.([]*ast.AuthTokenOrTLSOption) - l = append(l, yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)) - parser.yyVAL.item = l - } - case 2461: - { - l := yyS[yypt-1].item.([]*ast.AuthTokenOrTLSOption) - l = append(l, yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)) - parser.yyVAL.item = l - } - case 2462: - { - parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ - Type: ast.Issuer, - Value: yyS[yypt-0].ident, - } - } - case 2463: - { - parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ - Type: ast.Subject, - Value: yyS[yypt-0].ident, - } - } - case 2464: - { - parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ - Type: ast.Cipher, - Value: yyS[yypt-0].ident, - } - } - case 2465: - { - parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ - Type: ast.SAN, - Value: yyS[yypt-0].ident, - } - } - case 2466: - { - parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ - Type: ast.TokenIssuer, - Value: yyS[yypt-0].ident, - } - } - case 2467: - { - parser.yyVAL.item = nil - } - case 2468: - { - parser.yyVAL.item = &ast.CommentOrAttributeOption{Type: ast.UserCommentType, Value: yyS[yypt-0].ident} - } - case 2469: - { - parser.yyVAL.item = &ast.CommentOrAttributeOption{Type: ast.UserAttributeType, Value: yyS[yypt-0].ident} - } - case 2470: - { - parser.yyVAL.item = nil - } - case 2471: - { - parser.yyVAL.item = &ast.ResourceGroupNameOption{Value: yyS[yypt-0].ident} - } - case 2472: - { - parser.yyVAL.item = []*ast.PasswordOrLockOption{} - } - case 2473: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2474: - { - parser.yyVAL.item = []*ast.PasswordOrLockOption{yyS[yypt-0].item.(*ast.PasswordOrLockOption)} - } - case 2475: - { - l := yyS[yypt-1].item.([]*ast.PasswordOrLockOption) - l = append(l, yyS[yypt-0].item.(*ast.PasswordOrLockOption)) - parser.yyVAL.item = l - } - case 2476: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.Unlock, - } - } - case 2477: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.Lock, - } - } - case 2478: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordHistoryDefault, - } - } - case 2479: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordHistory, - Count: yyS[yypt-0].item.(int64), - } - } - case 2480: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordReuseDefault, - } - } - case 2481: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordReuseInterval, - Count: yyS[yypt-1].item.(int64), - } - } - case 2482: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordExpire, - } - } - case 2483: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordExpireInterval, - Count: yyS[yypt-1].item.(int64), - } - } - case 2484: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordExpireNever, - } - } - case 2485: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordExpireDefault, - } - } - case 2486: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.FailedLoginAttempts, - Count: yyS[yypt-0].item.(int64), - } - } - case 2487: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordLockTime, - Count: yyS[yypt-0].item.(int64), - } - } - case 2488: - { - parser.yyVAL.item = &ast.PasswordOrLockOption{ - Type: ast.PasswordLockTimeUnbounded, - } - } - case 2489: - { - parser.yyVAL.item = nil - } - case 2490: - { - parser.yyVAL.item = &ast.AuthOption{ - AuthString: yyS[yypt-0].ident, - ByAuthString: true, - } - } - case 2491: - { - parser.yyVAL.item = &ast.AuthOption{ - AuthPlugin: yyS[yypt-0].ident, - } - } - case 2492: - { - parser.yyVAL.item = &ast.AuthOption{ - AuthPlugin: yyS[yypt-2].ident, - AuthString: yyS[yypt-0].ident, - ByAuthString: true, - } - } - case 2493: - { - parser.yyVAL.item = &ast.AuthOption{ - AuthPlugin: yyS[yypt-2].ident, - HashString: yyS[yypt-0].ident, - ByHashString: true, - } - } - case 2494: - { - parser.yyVAL.item = &ast.AuthOption{ - AuthPlugin: mysql.AuthNativePassword, - HashString: yyS[yypt-0].ident, - ByHashString: true, - } - } - case 2497: - { - parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() - } - case 2498: - { - role := yyS[yypt-0].item.(*auth.RoleIdentity) - roleSpec := &ast.UserSpec{ - User: &auth.UserIdentity{ - Username: role.Username, - Hostname: role.Hostname, - }, - IsRole: true, - } - parser.yyVAL.item = roleSpec - } - case 2499: - { - parser.yyVAL.item = []*ast.UserSpec{yyS[yypt-0].item.(*ast.UserSpec)} - } - case 2500: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserSpec), yyS[yypt-0].item.(*ast.UserSpec)) - } - case 2504: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2509: - { - startOffset := parser.startOffset(&yyS[yypt-2]) - endOffset := parser.startOffset(&yyS[yypt-1]) - originStmt := yyS[yypt-2].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) - - startOffset = parser.startOffset(&yyS[yypt]) - hintedStmt := yyS[yypt-0].statement - hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - x := &ast.CreateBindingStmt{ - OriginNode: originStmt, - HintedNode: hintedStmt, - GlobalScope: yyS[yypt-5].item.(bool), - } - - parser.yyVAL.statement = x - } - case 2510: - { - x := &ast.CreateBindingStmt{ - GlobalScope: yyS[yypt-7].item.(bool), - PlanDigest: yyS[yypt-0].ident, - } - - parser.yyVAL.statement = x - } - case 2511: - { - startOffset := parser.startOffset(&yyS[yypt]) - originStmt := yyS[yypt-0].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - x := &ast.DropBindingStmt{ - OriginNode: originStmt, - GlobalScope: yyS[yypt-3].item.(bool), - } - - parser.yyVAL.statement = x - } - case 2512: - { - startOffset := parser.startOffset(&yyS[yypt-2]) - endOffset := parser.startOffset(&yyS[yypt-1]) - originStmt := yyS[yypt-2].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) - - startOffset = parser.startOffset(&yyS[yypt]) - hintedStmt := yyS[yypt-0].statement - hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - x := &ast.DropBindingStmt{ - OriginNode: originStmt, - HintedNode: hintedStmt, - GlobalScope: yyS[yypt-5].item.(bool), - } - - parser.yyVAL.statement = x - } - case 2513: - { - x := &ast.DropBindingStmt{ - GlobalScope: yyS[yypt-5].item.(bool), - SQLDigest: yyS[yypt-0].ident, - } - - parser.yyVAL.statement = x - } - case 2514: - { - startOffset := parser.startOffset(&yyS[yypt]) - originStmt := yyS[yypt-0].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - x := &ast.SetBindingStmt{ - BindingStatusType: yyS[yypt-2].item.(ast.BindingStatusType), - OriginNode: originStmt, - } - - parser.yyVAL.statement = x - } - case 2515: - { - startOffset := parser.startOffset(&yyS[yypt-2]) - endOffset := parser.startOffset(&yyS[yypt-1]) - originStmt := yyS[yypt-2].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) - - startOffset = parser.startOffset(&yyS[yypt]) - hintedStmt := yyS[yypt-0].statement - hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - x := &ast.SetBindingStmt{ - BindingStatusType: yyS[yypt-4].item.(ast.BindingStatusType), - OriginNode: originStmt, - HintedNode: hintedStmt, - } - - parser.yyVAL.statement = x - } - case 2516: - { - x := &ast.SetBindingStmt{ - BindingStatusType: yyS[yypt-4].item.(ast.BindingStatusType), - SQLDigest: yyS[yypt-0].ident, - } - - parser.yyVAL.statement = x - } - case 2517: - { - p, err := convertToPriv(yyS[yypt-7].item.([]*ast.RoleOrPriv)) - if err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.statement = &ast.GrantStmt{ - Privs: p, - ObjectType: yyS[yypt-5].item.(ast.ObjectTypeType), - Level: yyS[yypt-4].item.(*ast.GrantLevel), - Users: yyS[yypt-2].item.([]*ast.UserSpec), - AuthTokenOrTLSOptions: yyS[yypt-1].item.([]*ast.AuthTokenOrTLSOption), - WithGrant: yyS[yypt-0].item.(bool), - } - } - case 2518: - { - parser.yyVAL.statement = &ast.GrantProxyStmt{ - LocalUser: yyS[yypt-3].item.(*auth.UserIdentity), - ExternalUsers: yyS[yypt-1].item.([]*auth.UserIdentity), - WithGrant: yyS[yypt-0].item.(bool), - } - } - case 2519: - { - r, err := convertToRole(yyS[yypt-2].item.([]*ast.RoleOrPriv)) - if err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.statement = &ast.GrantRoleStmt{ - Roles: r, - Users: yyS[yypt-0].item.([]*auth.UserIdentity), - } - } - case 2520: - { - parser.yyVAL.item = false - } - case 2521: - { - parser.yyVAL.item = true - } - case 2522: - { - parser.yyVAL.item = false - } - case 2523: - { - parser.yyVAL.item = false - } - case 2524: - { - parser.yyVAL.item = false - } - case 2525: - { - parser.yyVAL.item = false - } - case 2526: - { - parser.yyVAL.item = []string{yyS[yypt-0].ident} - } - case 2527: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]string), yyS[yypt-0].ident) - } - case 2528: - { - parser.yyVAL.item = &ast.RoleOrPriv{ - Node: yyS[yypt-0].item, - } - } - case 2529: - { - parser.yyVAL.item = &ast.RoleOrPriv{ - Node: yyS[yypt-0].item, - } - } - case 2530: - { - parser.yyVAL.item = &ast.RoleOrPriv{ - Symbols: strings.Join(yyS[yypt-0].item.([]string), " "), - } - } - case 2531: - { - parser.yyVAL.item = &ast.RoleOrPriv{ - Symbols: "LOAD FROM S3", - } - } - case 2532: - { - parser.yyVAL.item = &ast.RoleOrPriv{ - Symbols: "SELECT INTO S3", - } - } - case 2533: - { - parser.yyVAL.item = []*ast.RoleOrPriv{yyS[yypt-0].item.(*ast.RoleOrPriv)} - } - case 2534: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.RoleOrPriv), yyS[yypt-0].item.(*ast.RoleOrPriv)) - } - case 2535: - { - parser.yyVAL.item = &ast.PrivElem{ - Priv: yyS[yypt-0].item.(mysql.PrivilegeType), - } - } - case 2536: - { - parser.yyVAL.item = &ast.PrivElem{ - Priv: yyS[yypt-3].item.(mysql.PrivilegeType), - Cols: yyS[yypt-1].item.([]*ast.ColumnName), - } - } - case 2537: - { - parser.yyVAL.item = mysql.AllPriv - } - case 2538: - { - parser.yyVAL.item = mysql.AllPriv - } - case 2539: - { - parser.yyVAL.item = mysql.AlterPriv - } - case 2540: - { - parser.yyVAL.item = mysql.CreatePriv - } - case 2541: - { - parser.yyVAL.item = mysql.CreateUserPriv - } - case 2542: - { - parser.yyVAL.item = mysql.CreateTablespacePriv - } - case 2543: - { - parser.yyVAL.item = mysql.TriggerPriv - } - case 2544: - { - parser.yyVAL.item = mysql.DeletePriv - } - case 2545: - { - parser.yyVAL.item = mysql.DropPriv - } - case 2546: - { - parser.yyVAL.item = mysql.ProcessPriv - } - case 2547: - { - parser.yyVAL.item = mysql.ExecutePriv - } - case 2548: - { - parser.yyVAL.item = mysql.IndexPriv - } - case 2549: - { - parser.yyVAL.item = mysql.InsertPriv - } - case 2550: - { - parser.yyVAL.item = mysql.SelectPriv - } - case 2551: - { - parser.yyVAL.item = mysql.SuperPriv - } - case 2552: - { - parser.yyVAL.item = mysql.ShowDBPriv - } - case 2553: - { - parser.yyVAL.item = mysql.UpdatePriv - } - case 2554: - { - parser.yyVAL.item = mysql.GrantPriv - } - case 2555: - { - parser.yyVAL.item = mysql.ReferencesPriv - } - case 2556: - { - parser.yyVAL.item = mysql.ReplicationSlavePriv - } - case 2557: - { - parser.yyVAL.item = mysql.ReplicationClientPriv - } - case 2558: - { - parser.yyVAL.item = mysql.UsagePriv - } - case 2559: - { - parser.yyVAL.item = mysql.ReloadPriv - } - case 2560: - { - parser.yyVAL.item = mysql.FilePriv - } - case 2561: - { - parser.yyVAL.item = mysql.ConfigPriv - } - case 2562: - { - parser.yyVAL.item = mysql.CreateTMPTablePriv - } - case 2563: - { - parser.yyVAL.item = mysql.LockTablesPriv - } - case 2564: - { - parser.yyVAL.item = mysql.CreateViewPriv - } - case 2565: - { - parser.yyVAL.item = mysql.ShowViewPriv - } - case 2566: - { - parser.yyVAL.item = mysql.CreateRolePriv - } - case 2567: - { - parser.yyVAL.item = mysql.DropRolePriv - } - case 2568: - { - parser.yyVAL.item = mysql.CreateRoutinePriv - } - case 2569: - { - parser.yyVAL.item = mysql.AlterRoutinePriv - } - case 2570: - { - parser.yyVAL.item = mysql.EventPriv - } - case 2571: - { - parser.yyVAL.item = mysql.ShutdownPriv - } - case 2572: - { - parser.yyVAL.item = ast.ObjectTypeNone - } - case 2573: - { - parser.yyVAL.item = ast.ObjectTypeTable - } - case 2574: - { - parser.yyVAL.item = ast.ObjectTypeFunction - } - case 2575: - { - parser.yyVAL.item = ast.ObjectTypeProcedure - } - case 2576: - { - parser.yyVAL.item = &ast.GrantLevel{ - Level: ast.GrantLevelDB, - } - } - case 2577: - { - parser.yyVAL.item = &ast.GrantLevel{ - Level: ast.GrantLevelGlobal, - } - } - case 2578: - { - parser.yyVAL.item = &ast.GrantLevel{ - Level: ast.GrantLevelDB, - DBName: yyS[yypt-2].ident, - } - } - case 2579: - { - parser.yyVAL.item = &ast.GrantLevel{ - Level: ast.GrantLevelTable, - DBName: yyS[yypt-2].ident, - TableName: yyS[yypt-0].ident, - } - } - case 2580: - { - parser.yyVAL.item = &ast.GrantLevel{ - Level: ast.GrantLevelTable, - TableName: yyS[yypt-0].ident, - } - } - case 2581: - { - p, err := convertToPriv(yyS[yypt-5].item.([]*ast.RoleOrPriv)) - if err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.statement = &ast.RevokeStmt{ - Privs: p, - ObjectType: yyS[yypt-3].item.(ast.ObjectTypeType), - Level: yyS[yypt-2].item.(*ast.GrantLevel), - Users: yyS[yypt-0].item.([]*ast.UserSpec), - } - } - case 2582: - { - // MySQL has special syntax for REVOKE ALL [PRIVILEGES], GRANT OPTION - // which uses the RevokeRoleStmt syntax but is of type RevokeStmt. - // It is documented at https://dev.mysql.com/doc/refman/5.7/en/revoke.html - // as the "second syntax" for REVOKE. It is only valid if *both* - // ALL PRIVILEGES + GRANT OPTION are specified in that order. - if isRevokeAllGrant(yyS[yypt-2].item.([]*ast.RoleOrPriv)) { - var users []*ast.UserSpec - for _, u := range yyS[yypt-0].item.([]*auth.UserIdentity) { - users = append(users, &ast.UserSpec{ - User: u, - }) - } - parser.yyVAL.statement = &ast.RevokeStmt{ - Privs: []*ast.PrivElem{{Priv: mysql.AllPriv}, {Priv: mysql.GrantPriv}}, - ObjectType: ast.ObjectTypeNone, - Level: &ast.GrantLevel{Level: ast.GrantLevelGlobal}, - Users: users, - } - } else { - r, err := convertToRole(yyS[yypt-2].item.([]*ast.RoleOrPriv)) - if err != nil { - yylex.AppendError(err) - return 1 - } - parser.yyVAL.statement = &ast.RevokeRoleStmt{ - Roles: r, - Users: yyS[yypt-0].item.([]*auth.UserIdentity), - } - } - } - case 2583: - { - x := &ast.LoadDataStmt{ - FileLocRef: ast.FileLocServerOrRemote, - Path: yyS[yypt-12].ident, - Format: yyS[yypt-11].item.(*string), - OnDuplicate: yyS[yypt-10].item.(ast.OnDuplicateKeyHandlingType), - Table: yyS[yypt-7].item.(*ast.TableName), - Charset: yyS[yypt-6].item.(*string), - FieldsInfo: yyS[yypt-5].item.(*ast.FieldsClause), - LinesInfo: yyS[yypt-4].item.(*ast.LinesClause), - IgnoreLines: yyS[yypt-3].item.(*uint64), - ColumnsAndUserVars: yyS[yypt-2].item.([]*ast.ColumnNameOrUserVar), - ColumnAssignments: yyS[yypt-1].item.([]*ast.Assignment), - Options: yyS[yypt-0].item.([]*ast.LoadDataOpt), - } - if yyS[yypt-14].item != nil { - x.FileLocRef = ast.FileLocClient - // See https://dev.mysql.com/doc/refman/5.7/en/load-data.html#load-data-duplicate-key-handling - // If you do not specify IGNORE or REPLACE modifier , then we set default behavior to IGNORE when LOCAL modifier is specified - if x.OnDuplicate == ast.OnDuplicateKeyHandlingError { - x.OnDuplicate = ast.OnDuplicateKeyHandlingIgnore - } - } - columns := []*ast.ColumnName{} - for _, v := range x.ColumnsAndUserVars { - if v.ColumnName != nil { - columns = append(columns, v.ColumnName) - } - } - x.Columns = columns - - parser.yyVAL.statement = x - } - case 2584: - { - parser.yyVAL.item = (*string)(nil) - } - case 2585: - { - str := yyS[yypt-0].ident - parser.yyVAL.item = &str - } - case 2586: - { - parser.yyVAL.item = (*uint64)(nil) - } - case 2587: - { - v := getUint64FromNUM(yyS[yypt-1].item) - parser.yyVAL.item = &v - } - case 2588: - { - parser.yyVAL.item = (*string)(nil) - } - case 2589: - { - v := yyS[yypt-0].ident - parser.yyVAL.item = &v - } - case 2590: - { - parser.yyVAL.item = nil - } - case 2591: - { - parser.yyVAL.item = yyS[yypt-0].ident - } - case 2592: - { - parser.yyVAL.item = (*ast.FieldsClause)(nil) - } - case 2593: - { - fieldsClause := &ast.FieldsClause{} - fieldItems := yyS[yypt-0].item.([]*ast.FieldItem) - for _, item := range fieldItems { - switch item.Type { - case ast.Terminated: - fieldsClause.Terminated = &item.Value - case ast.Enclosed: - fieldsClause.Enclosed = &item.Value - fieldsClause.OptEnclosed = item.OptEnclosed - case ast.Escaped: - fieldsClause.Escaped = &item.Value - case ast.DefinedNullBy: - fieldsClause.DefinedNullBy = &item.Value - fieldsClause.NullValueOptEnclosed = item.OptEnclosed - } - } - parser.yyVAL.item = fieldsClause - } - case 2596: - { - fieldItems := yyS[yypt-1].item.([]*ast.FieldItem) - parser.yyVAL.item = append(fieldItems, yyS[yypt-0].item.(*ast.FieldItem)) - } - case 2597: - { - fieldItems := make([]*ast.FieldItem, 1, 1) - fieldItems[0] = yyS[yypt-0].item.(*ast.FieldItem) - parser.yyVAL.item = fieldItems - } - case 2598: - { - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.Terminated, - Value: yyS[yypt-0].ident, - } - } - case 2599: - { - str := yyS[yypt-0].ident - if str != "\\" && len(str) > 1 { - yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) - return 1 - } - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.Enclosed, - Value: str, - OptEnclosed: true, - } - } - case 2600: - { - str := yyS[yypt-0].ident - if str != "\\" && len(str) > 1 { - yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) - return 1 - } - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.Enclosed, - Value: str, - } - } - case 2601: - { - str := yyS[yypt-0].ident - if str != "\\" && len(str) > 1 { - yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) - return 1 - } - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.Escaped, - Value: str, - } - } - case 2602: - { - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.DefinedNullBy, - Value: yyS[yypt-0].item.(*ast.TextString).Value, - } - } - case 2603: - { - parser.yyVAL.item = &ast.FieldItem{ - Type: ast.DefinedNullBy, - Value: yyS[yypt-2].item.(*ast.TextString).Value, - OptEnclosed: true, - } - } - case 2605: - { - parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() - } - case 2606: - { - parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() - } - case 2607: - { - parser.yyVAL.item = (*ast.LinesClause)(nil) - } - case 2608: - { - parser.yyVAL.item = &ast.LinesClause{Starting: yyS[yypt-1].item.(*string), Terminated: yyS[yypt-0].item.(*string)} - } - case 2609: - { - parser.yyVAL.item = (*string)(nil) - } - case 2610: - { - s := yyS[yypt-0].ident - parser.yyVAL.item = &s - } - case 2611: - { - parser.yyVAL.item = (*string)(nil) - } - case 2612: - { - s := yyS[yypt-0].ident - parser.yyVAL.item = &s - } - case 2613: - { - parser.yyVAL.item = ([]*ast.Assignment)(nil) - } - case 2614: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2615: - { - l := yyS[yypt-2].item.([]*ast.Assignment) - parser.yyVAL.item = append(l, yyS[yypt-0].item.(*ast.Assignment)) - } - case 2616: - { - parser.yyVAL.item = []*ast.Assignment{yyS[yypt-0].item.(*ast.Assignment)} - } - case 2617: - { - parser.yyVAL.item = &ast.Assignment{ - Column: yyS[yypt-2].expr.(*ast.ColumnNameExpr).Name, - Expr: yyS[yypt-0].expr, - } - } - case 2618: - { - parser.yyVAL.item = []*ast.LoadDataOpt{} - } - case 2619: - { - parser.yyVAL.item = yyS[yypt-0].item.([]*ast.LoadDataOpt) - } - case 2620: - { - parser.yyVAL.item = []*ast.LoadDataOpt{yyS[yypt-0].item.(*ast.LoadDataOpt)} - } - case 2621: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.LoadDataOpt), yyS[yypt-0].item.(*ast.LoadDataOpt)) - } - case 2622: - { - parser.yyVAL.item = &ast.LoadDataOpt{Name: strings.ToLower(yyS[yypt-0].ident)} - } - case 2623: - { - parser.yyVAL.item = &ast.LoadDataOpt{Name: strings.ToLower(yyS[yypt-2].ident), Value: yyS[yypt-0].expr.(ast.ExprNode)} - } - case 2624: - { - parser.yyVAL.statement = &ast.ImportIntoStmt{ - Table: yyS[yypt-6].item.(*ast.TableName), - ColumnsAndUserVars: yyS[yypt-5].item.([]*ast.ColumnNameOrUserVar), - ColumnAssignments: yyS[yypt-4].item.([]*ast.Assignment), - Path: yyS[yypt-2].ident, - Format: yyS[yypt-1].item.(*string), - Options: yyS[yypt-0].item.([]*ast.LoadDataOpt), - } - } - case 2625: - { - parser.yyVAL.statement = &ast.UnlockTablesStmt{} - } - case 2626: - { - parser.yyVAL.statement = &ast.LockTablesStmt{ - TableLocks: yyS[yypt-0].item.([]ast.TableLock), - } - } - case 2629: - { - parser.yyVAL.item = ast.TableLock{ - Table: yyS[yypt-1].item.(*ast.TableName), - Type: yyS[yypt-0].item.(model.TableLockType), - } - } - case 2630: - { - parser.yyVAL.item = model.TableLockRead - } - case 2631: - { - parser.yyVAL.item = model.TableLockReadLocal - } - case 2632: - { - parser.yyVAL.item = model.TableLockWrite - } - case 2633: - { - parser.yyVAL.item = model.TableLockWriteLocal - } - case 2634: - { - parser.yyVAL.item = []ast.TableLock{yyS[yypt-0].item.(ast.TableLock)} - } - case 2635: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.TableLock), yyS[yypt-0].item.(ast.TableLock)) - } - case 2636: - { - parser.yyVAL.statement = &ast.NonTransactionalDMLStmt{ - DryRun: yyS[yypt-1].item.(int), - ShardColumn: yyS[yypt-4].item.(*ast.ColumnName), - Limit: getUint64FromNUM(yyS[yypt-2].item), - DMLStmt: yyS[yypt-0].statement.(ast.ShardableDMLStmt), - } - } - case 2641: - { - parser.yyVAL.item = ast.NoDryRun - } - case 2642: - { - parser.yyVAL.item = ast.DryRunSplitDml - } - case 2643: - { - parser.yyVAL.item = ast.DryRunQuery - } - case 2644: - { - parser.yyVAL.item = (*ast.ColumnName)(nil) - } - case 2645: - { - parser.yyVAL.item = yyS[yypt-0].item.(*ast.ColumnName) - } - case 2646: - { - parser.yyVAL.statement = &ast.KillStmt{ - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - TiDBExtension: yyS[yypt-1].item.(bool), - } - } - case 2647: - { - parser.yyVAL.statement = &ast.KillStmt{ - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - TiDBExtension: yyS[yypt-2].item.(bool), - } - } - case 2648: - { - parser.yyVAL.statement = &ast.KillStmt{ - ConnectionID: getUint64FromNUM(yyS[yypt-0].item), - Query: true, - TiDBExtension: yyS[yypt-2].item.(bool), - } - } - case 2649: - { - parser.yyVAL.statement = &ast.KillStmt{ - TiDBExtension: yyS[yypt-1].item.(bool), - Expr: yyS[yypt-0].expr, - } - } - case 2650: - { - parser.yyVAL.item = false - } - case 2651: - { - parser.yyVAL.item = true - } - case 2652: - { - parser.yyVAL.statement = &ast.LoadStatsStmt{ - Path: yyS[yypt-0].ident, - } - } - case 2653: - { - parser.yyVAL.statement = &ast.LockStatsStmt{ - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 2654: - { - x := yyS[yypt-2].item.(*ast.TableName) - x.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - parser.yyVAL.statement = &ast.LockStatsStmt{ - Tables: []*ast.TableName{x}, - } - } - case 2655: - { - x := yyS[yypt-4].item.(*ast.TableName) - x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) - parser.yyVAL.statement = &ast.LockStatsStmt{ - Tables: []*ast.TableName{x}, - } - } - case 2656: - { - parser.yyVAL.statement = &ast.UnlockStatsStmt{ - Tables: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 2657: - { - x := yyS[yypt-2].item.(*ast.TableName) - x.PartitionNames = yyS[yypt-0].item.([]model.CIStr) - parser.yyVAL.statement = &ast.UnlockStatsStmt{ - Tables: []*ast.TableName{x}, - } - } - case 2658: - { - x := yyS[yypt-4].item.(*ast.TableName) - x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) - parser.yyVAL.statement = &ast.UnlockStatsStmt{ - Tables: []*ast.TableName{x}, - } - } - case 2659: - { - parser.yyVAL.statement = &ast.DropPlacementPolicyStmt{ - IfExists: yyS[yypt-1].item.(bool), - PolicyName: model.NewCIStr(yyS[yypt-0].ident), - } - } - case 2660: - { - parser.yyVAL.statement = &ast.CreateResourceGroupStmt{ - IfNotExists: yyS[yypt-2].item.(bool), - ResourceGroupName: model.NewCIStr(yyS[yypt-1].ident), - ResourceGroupOptionList: yyS[yypt-0].item.([]*ast.ResourceGroupOption), - } - } - case 2661: - { - parser.yyVAL.statement = &ast.AlterResourceGroupStmt{ - IfExists: yyS[yypt-2].item.(bool), - ResourceGroupName: model.NewCIStr(yyS[yypt-1].ident), - ResourceGroupOptionList: yyS[yypt-0].item.([]*ast.ResourceGroupOption), - } - } - case 2662: - { - parser.yyVAL.statement = &ast.DropResourceGroupStmt{ - IfExists: yyS[yypt-1].item.(bool), - ResourceGroupName: model.NewCIStr(yyS[yypt-0].ident), - } - } - case 2663: - { - parser.yyVAL.statement = &ast.CreatePlacementPolicyStmt{ - OrReplace: yyS[yypt-5].item.(bool), - IfNotExists: yyS[yypt-2].item.(bool), - PolicyName: model.NewCIStr(yyS[yypt-1].ident), - PlacementOptions: yyS[yypt-0].item.([]*ast.PlacementOption), - } - } - case 2664: - { - parser.yyVAL.statement = &ast.AlterPlacementPolicyStmt{ - IfExists: yyS[yypt-2].item.(bool), - PolicyName: model.NewCIStr(yyS[yypt-1].ident), - PlacementOptions: yyS[yypt-0].item.([]*ast.PlacementOption), - } - } - case 2665: - { - parser.yyVAL.statement = &ast.CreateSequenceStmt{ - IfNotExists: yyS[yypt-3].item.(bool), - Name: yyS[yypt-2].item.(*ast.TableName), - SeqOptions: yyS[yypt-1].item.([]*ast.SequenceOption), - TblOptions: yyS[yypt-0].item.([]*ast.TableOption), - } - } - case 2666: - { - parser.yyVAL.item = []*ast.SequenceOption{} - } - case 2668: - { - parser.yyVAL.item = []*ast.SequenceOption{yyS[yypt-0].item.(*ast.SequenceOption)} - } - case 2669: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.SequenceOption), yyS[yypt-0].item.(*ast.SequenceOption)) - } - case 2670: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceOptionIncrementBy, IntValue: yyS[yypt-0].item.(int64)} - } - case 2671: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceOptionIncrementBy, IntValue: yyS[yypt-0].item.(int64)} - } - case 2672: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceStartWith, IntValue: yyS[yypt-0].item.(int64)} - } - case 2673: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceStartWith, IntValue: yyS[yypt-0].item.(int64)} - } - case 2674: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceMinValue, IntValue: yyS[yypt-0].item.(int64)} - } - case 2675: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMinValue} - } - case 2676: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMinValue} - } - case 2677: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceMaxValue, IntValue: yyS[yypt-0].item.(int64)} - } - case 2678: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMaxValue} - } - case 2679: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMaxValue} - } - case 2680: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceCache, IntValue: yyS[yypt-0].item.(int64)} - } - case 2681: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCache} - } - case 2682: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCache} - } - case 2683: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceCycle} - } - case 2684: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCycle} - } - case 2685: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCycle} - } - case 2687: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2688: - { - unsigned_num := getUint64FromNUM(yyS[yypt-0].item) - if unsigned_num > 9223372036854775808 { - yylex.AppendError(yylex.Errorf("the Signed Value should be at the range of [-9223372036854775808, 9223372036854775807].")) - return 1 - } else if unsigned_num == 9223372036854775808 { - signed_one := int64(1) - parser.yyVAL.item = signed_one << 63 - } else { - parser.yyVAL.item = -int64(unsigned_num) - } - } - case 2689: - { - parser.yyVAL.statement = &ast.DropSequenceStmt{ - IfExists: yyS[yypt-1].item.(bool), - Sequences: yyS[yypt-0].item.([]*ast.TableName), - } - } - case 2690: - { - parser.yyVAL.statement = &ast.AlterSequenceStmt{ - IfExists: yyS[yypt-2].item.(bool), - Name: yyS[yypt-1].item.(*ast.TableName), - SeqOptions: yyS[yypt-0].item.([]*ast.SequenceOption), - } - } - case 2691: - { - parser.yyVAL.item = []*ast.SequenceOption{yyS[yypt-0].item.(*ast.SequenceOption)} - } - case 2692: - { - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.SequenceOption), yyS[yypt-0].item.(*ast.SequenceOption)) - } - case 2694: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestart} - } - case 2695: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestartWith, IntValue: yyS[yypt-0].item.(int64)} - } - case 2696: - { - parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestartWith, IntValue: yyS[yypt-0].item.(int64)} - } - case 2697: - { - x := &ast.IndexAdviseStmt{ - Path: yyS[yypt-3].ident, - MaxMinutes: yyS[yypt-2].item.(uint64), - } - if yyS[yypt-5].item != nil { - x.IsLocal = true - } - if yyS[yypt-1].item != nil { - x.MaxIndexNum = yyS[yypt-1].item.(*ast.MaxIndexNumClause) - } - if yyS[yypt-0].item != nil { - x.LinesInfo = yyS[yypt-0].item.(*ast.LinesClause) - } - parser.yyVAL.statement = x - } - case 2698: - { - parser.yyVAL.item = uint64(ast.UnspecifiedSize) - } - case 2699: - { - parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) - } - case 2700: - { - parser.yyVAL.item = nil - } - case 2701: - { - parser.yyVAL.item = &ast.MaxIndexNumClause{ - PerTable: yyS[yypt-1].item.(uint64), - PerDB: yyS[yypt-0].item.(uint64), - } - } - case 2702: - { - parser.yyVAL.item = uint64(ast.UnspecifiedSize) - } - case 2703: - { - parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) - } - case 2704: - { - parser.yyVAL.item = uint64(ast.UnspecifiedSize) - } - case 2705: - { - parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) - } - case 2706: - { - // Parse it but will ignore it - switch yyS[yypt-0].ident { - case "Y", "y": - yylex.AppendError(yylex.Errorf("The ENCRYPTION clause is parsed but ignored by all storage engines.")) - parser.lastErrorAsWarn() - case "N", "n": - break - default: - yylex.AppendError(ErrWrongValue.GenWithStackByArgs("argument (should be Y or N)", yyS[yypt-0].ident)) - return 1 - } - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 2707: - { - parser.yyVAL.item = append([]*ast.RowExpr{}, yyS[yypt-0].item.(*ast.RowExpr)) - } - case 2708: - { - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.RowExpr), yyS[yypt-0].item.(*ast.RowExpr)) - } - case 2709: - { - parser.yyVAL.item = &ast.RowExpr{Values: yyS[yypt-0].item.([]ast.ExprNode)} - } - case 2710: - { - x := &ast.PlanReplayerStmt{ - Stmt: yyS[yypt-0].statement, - Analyze: false, - Load: false, - File: "", - Where: nil, - OrderBy: nil, - Limit: nil, - } - if yyS[yypt-2].item != nil { - x.HistoricalStatsInfo = yyS[yypt-2].item.(*ast.AsOfClause) - } - startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - parser.yyVAL.statement = x - } - case 2711: - { - x := &ast.PlanReplayerStmt{ - Stmt: yyS[yypt-0].statement, - Analyze: true, - Load: false, - File: "", - Where: nil, - OrderBy: nil, - Limit: nil, - } - if yyS[yypt-3].item != nil { - x.HistoricalStatsInfo = yyS[yypt-3].item.(*ast.AsOfClause) - } - startOffset := parser.startOffset(&yyS[yypt]) - x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) - - parser.yyVAL.statement = x - } - case 2712: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: false, - Load: false, - File: "", - } - if yyS[yypt-6].item != nil { - x.HistoricalStatsInfo = yyS[yypt-6].item.(*ast.AsOfClause) - } - if yyS[yypt-2].item != nil { - x.Where = yyS[yypt-2].item.(ast.ExprNode) - } - if yyS[yypt-1].item != nil { - x.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) - } - if yyS[yypt-0].item != nil { - x.Limit = yyS[yypt-0].item.(*ast.Limit) - } - - parser.yyVAL.statement = x - } - case 2713: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: true, - Load: false, - File: "", - } - if yyS[yypt-7].item != nil { - x.HistoricalStatsInfo = yyS[yypt-7].item.(*ast.AsOfClause) - } - if yyS[yypt-2].item != nil { - x.Where = yyS[yypt-2].item.(ast.ExprNode) - } - if yyS[yypt-1].item != nil { - x.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) - } - if yyS[yypt-0].item != nil { - x.Limit = yyS[yypt-0].item.(*ast.Limit) - } - - parser.yyVAL.statement = x - } - case 2714: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: false, - Load: false, - File: yyS[yypt-0].ident, - } - if yyS[yypt-2].item != nil { - x.HistoricalStatsInfo = yyS[yypt-2].item.(*ast.AsOfClause) - } - parser.yyVAL.statement = x - } - case 2715: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: true, - Load: false, - File: yyS[yypt-0].ident, - } - if yyS[yypt-3].item != nil { - x.HistoricalStatsInfo = yyS[yypt-3].item.(*ast.AsOfClause) - } - parser.yyVAL.statement = x - } - case 2716: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: false, - Load: true, - File: yyS[yypt-0].ident, - Where: nil, - OrderBy: nil, - Limit: nil, - } - - parser.yyVAL.statement = x - } - case 2717: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: false, - Capture: true, - SQLDigest: yyS[yypt-1].ident, - PlanDigest: yyS[yypt-0].ident, - Where: nil, - OrderBy: nil, - Limit: nil, - } - - parser.yyVAL.statement = x - } - case 2718: - { - x := &ast.PlanReplayerStmt{ - Stmt: nil, - Analyze: false, - Remove: true, - SQLDigest: yyS[yypt-1].ident, - PlanDigest: yyS[yypt-0].ident, - Where: nil, - OrderBy: nil, - Limit: nil, - } - - parser.yyVAL.statement = x - } - case 2719: - { - parser.yyVAL.item = nil - } - case 2720: - { - parser.yyVAL.item = yyS[yypt-0].item.(*ast.AsOfClause) - } - case 2721: - { - parser.yyVAL.item = []*ast.StoreParameter{} - } - case 2722: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2723: - { - l := yyS[yypt-2].item.([]*ast.StoreParameter) - l = append(l, yyS[yypt-0].item.(*ast.StoreParameter)) - parser.yyVAL.item = l - } - case 2724: - { - parser.yyVAL.item = []*ast.StoreParameter{yyS[yypt-0].item.(*ast.StoreParameter)} - } - case 2725: - { - x := &ast.StoreParameter{ - Paramstatus: yyS[yypt-2].item.(int), - ParamType: yyS[yypt-0].item.(*types.FieldType), - ParamName: yyS[yypt-1].ident, - } - parser.yyVAL.item = x - } - case 2726: - { - parser.yyVAL.item = ast.MODE_IN - } - case 2727: - { - parser.yyVAL.item = ast.MODE_IN - } - case 2728: - { - parser.yyVAL.item = ast.MODE_OUT - } - case 2729: - { - parser.yyVAL.item = ast.MODE_INOUT - } - case 2732: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2747: - { - var sel ast.StmtNode - switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { - case *ast.SelectStmt: - x.IsInBraces = true - sel = x - case *ast.SetOprStmt: - x.IsInBraces = true - sel = x - } - parser.yyVAL.statement = sel - } - case 2749: - { - parser.yyVAL.statement = yyS[yypt-0].statement - } - case 2750: - { - parser.yyVAL.item = []string{strings.ToLower(yyS[yypt-0].ident)} - } - case 2751: - { - l := yyS[yypt-2].item.([]string) - l = append(l, strings.ToLower(yyS[yypt-0].ident)) - parser.yyVAL.item = l - } - case 2752: - { - parser.yyVAL.item = nil - } - case 2753: - { - parser.yyVAL.item = yyS[yypt-0].expr - } - case 2754: - { - x := &ast.ProcedureDecl{ - DeclNames: yyS[yypt-2].item.([]string), - DeclType: yyS[yypt-1].item.(*types.FieldType), - } - if yyS[yypt-0].item != nil { - x.DeclDefault = yyS[yypt-0].item.(ast.ExprNode) - } - parser.yyVAL.item = x - } - case 2755: - { - name := strings.ToLower(yyS[yypt-3].ident) - parser.yyVAL.item = &ast.ProcedureCursor{ - CurName: name, - Selectstring: yyS[yypt-0].statement.(ast.StmtNode), - } - } - case 2756: - { - parser.yyVAL.item = &ast.ProcedureErrorControl{ - ControlHandle: yyS[yypt-4].item.(int), - ErrorCon: yyS[yypt-1].item.([]ast.ErrNode), - Operate: yyS[yypt-0].statement.(ast.StmtNode), - } - } - case 2757: - { - parser.yyVAL.item = ast.PROCEDUR_CONTINUE - } - case 2758: - { - parser.yyVAL.item = ast.PROCEDUR_EXIT - } - case 2759: - { - parser.yyVAL.item = []ast.ErrNode{yyS[yypt-0].statement.(ast.ErrNode)} - } - case 2760: - { - l := yyS[yypt-2].item.([]ast.ErrNode) - l = append(l, yyS[yypt-0].statement.(ast.ErrNode)) - parser.yyVAL.item = l - } - case 2761: - { - parser.yyVAL.statement = yyS[yypt-0].statement.(ast.ErrNode) - } - case 2762: - { - parser.yyVAL.statement = &ast.ProcedureErrorCon{ - ErrorCon: ast.PROCEDUR_SQLWARNING, - } - } - case 2763: - { - parser.yyVAL.statement = &ast.ProcedureErrorCon{ - ErrorCon: ast.PROCEDUR_NOT_FOUND, - } - } - case 2764: - { - parser.yyVAL.statement = &ast.ProcedureErrorCon{ - ErrorCon: ast.PROCEDUR_SQLEXCEPTION, - } - } - case 2765: - { - parser.yyVAL.statement = &ast.ProcedureErrorVal{ - ErrorNum: getUint64FromNUM(yyS[yypt-0].item), - } - } - case 2766: - { - parser.yyVAL.statement = &ast.ProcedureErrorState{ - CodeStatus: yyS[yypt-0].ident, - } - } - case 2769: - { - name := strings.ToLower(yyS[yypt-0].ident) - parser.yyVAL.statement = &ast.ProcedureOpenCur{ - CurName: name, - } - } - case 2770: - { - name := strings.ToLower(yyS[yypt-2].ident) - parser.yyVAL.statement = &ast.ProcedureFetchInto{ - CurName: name, - Variables: yyS[yypt-0].item.([]string), - } - } - case 2771: - { - name := strings.ToLower(yyS[yypt-0].ident) - parser.yyVAL.statement = &ast.ProcedureCloseCur{ - CurName: name, - } - } - case 2775: - { - parser.yyVAL.item = []string{strings.ToLower(yyS[yypt-0].ident)} - } - case 2776: - { - l := yyS[yypt-2].item.([]string) - l = append(l, strings.ToLower(yyS[yypt-0].ident)) - parser.yyVAL.item = l - } - case 2777: - { - parser.yyVAL.item = []ast.DeclNode{} - } - case 2778: - { - parser.yyVAL.item = yyS[yypt-0].item - } - case 2779: - { - parser.yyVAL.item = []ast.DeclNode{yyS[yypt-1].item.(ast.DeclNode)} - } - case 2780: - { - l := yyS[yypt-2].item.([]ast.DeclNode) - l = append(l, yyS[yypt-1].item.(ast.DeclNode)) - parser.yyVAL.item = l - } - case 2781: - { - parser.yyVAL.item = []ast.StmtNode{} - } - case 2782: - { - l := yyS[yypt-2].item.([]ast.StmtNode) - l = append(l, yyS[yypt-1].statement.(ast.StmtNode)) - parser.yyVAL.item = l - } - case 2783: - { - parser.yyVAL.item = []ast.StmtNode{yyS[yypt-1].statement.(ast.StmtNode)} - } - case 2784: - { - l := yyS[yypt-2].item.([]ast.StmtNode) - l = append(l, yyS[yypt-1].statement.(ast.StmtNode)) - parser.yyVAL.item = l - } - case 2785: - { - x := &ast.ProcedureBlock{ - ProcedureVars: yyS[yypt-2].item.([]ast.DeclNode), - ProcedureProcStmts: yyS[yypt-1].item.([]ast.StmtNode), - } - parser.yyVAL.statement = x - } - case 2786: - { - parser.yyVAL.statement = &ast.ProcedureIfInfo{ - IfBody: yyS[yypt-2].statement.(*ast.ProcedureIfBlock), - } - } - case 2787: - { - ifBlock := &ast.ProcedureIfBlock{ - IfExpr: yyS[yypt-3].expr.(ast.ExprNode), - ProcedureIfStmts: yyS[yypt-1].item.([]ast.StmtNode), - } - if yyS[yypt-0].statement != nil { - ifBlock.ProcedureElseStmt = yyS[yypt-0].statement.(ast.StmtNode) - } - parser.yyVAL.statement = ifBlock - } - case 2788: - { - parser.yyVAL.statement = nil - } - case 2789: - { - parser.yyVAL.statement = &ast.ProcedureElseIfBlock{ - ProcedureIfStmt: yyS[yypt-0].statement.(*ast.ProcedureIfBlock), - } - } - case 2790: - { - parser.yyVAL.statement = &ast.ProcedureElseBlock{ - ProcedureIfStmts: yyS[yypt-0].item.([]ast.StmtNode), - } - } - case 2791: - { - parser.yyVAL.statement = yyS[yypt-0].statement - } - case 2792: - { - parser.yyVAL.statement = yyS[yypt-0].statement - } - case 2793: - { - parser.yyVAL.item = []*ast.SimpleWhenThenStmt{yyS[yypt-0].statement.(*ast.SimpleWhenThenStmt)} - } - case 2794: - { - l := yyS[yypt-1].item.([]*ast.SimpleWhenThenStmt) - l = append(l, yyS[yypt-0].statement.(*ast.SimpleWhenThenStmt)) - parser.yyVAL.item = l - } - case 2795: - { - parser.yyVAL.item = []*ast.SearchWhenThenStmt{yyS[yypt-0].statement.(*ast.SearchWhenThenStmt)} - } - case 2796: - { - l := yyS[yypt-1].item.([]*ast.SearchWhenThenStmt) - l = append(l, yyS[yypt-0].statement.(*ast.SearchWhenThenStmt)) - parser.yyVAL.item = l - } - case 2797: - { - parser.yyVAL.statement = &ast.SimpleWhenThenStmt{ - Expr: yyS[yypt-2].expr.(ast.ExprNode), - ProcedureStmts: yyS[yypt-0].item.([]ast.StmtNode), - } - } - case 2798: - { - parser.yyVAL.statement = &ast.SearchWhenThenStmt{ - Expr: yyS[yypt-2].expr.(ast.ExprNode), - ProcedureStmts: yyS[yypt-0].item.([]ast.StmtNode), - } - } - case 2799: - { - parser.yyVAL.item = nil - } - case 2800: - { - parser.yyVAL.item = yyS[yypt-0].item.([]ast.StmtNode) - } - case 2801: - { - caseStmt := &ast.SimpleCaseStmt{ - Condition: yyS[yypt-4].expr.(ast.ExprNode), - WhenCases: yyS[yypt-3].item.([]*ast.SimpleWhenThenStmt), - } - if yyS[yypt-2].item != nil { - caseStmt.ElseCases = yyS[yypt-2].item.([]ast.StmtNode) - } - parser.yyVAL.statement = caseStmt - } - case 2802: - { - caseStmt := &ast.SearchCaseStmt{ - WhenCases: yyS[yypt-3].item.([]*ast.SearchWhenThenStmt), - } - if yyS[yypt-2].item != nil { - caseStmt.ElseCases = yyS[yypt-2].item.([]ast.StmtNode) - } - parser.yyVAL.statement = caseStmt - } - case 2803: - { - parser.yyVAL.statement = yyS[yypt-0].statement - } - case 2804: - { - parser.yyVAL.statement = &ast.ProcedureWhileStmt{ - Condition: yyS[yypt-4].expr.(ast.ExprNode), - Body: yyS[yypt-2].item.([]ast.StmtNode), - } - } - case 2805: - { - parser.yyVAL.statement = &ast.ProcedureRepeatStmt{ - Body: yyS[yypt-4].item.([]ast.StmtNode), - Condition: yyS[yypt-2].expr.(ast.ExprNode), - } - } - case 2806: - { - labelBlock := &ast.ProcedureLabelBlock{ - LabelName: yyS[yypt-3].ident, - Block: yyS[yypt-1].statement.(*ast.ProcedureBlock), - } - if yyS[yypt-0].ident != "" && (yyS[yypt-3].ident != yyS[yypt-0].ident) { - labelBlock.LabelError = true - labelBlock.LabelEnd = yyS[yypt-0].ident - } - parser.yyVAL.statement = labelBlock - } - case 2807: - { - parser.yyVAL.ident = "" - } - case 2808: - { - parser.yyVAL.ident = yyS[yypt-0].ident - } - case 2809: - { - labelLoop := &ast.ProcedureLabelLoop{ - LabelName: yyS[yypt-3].ident, - Block: yyS[yypt-1].statement.(ast.StmtNode), - } - if yyS[yypt-0].ident != "" && (yyS[yypt-3].ident != yyS[yypt-0].ident) { - labelLoop.LabelError = true - labelLoop.LabelEnd = yyS[yypt-0].ident - } - parser.yyVAL.statement = labelLoop - } - case 2810: - { - parser.yyVAL.statement = &ast.ProcedureJump{ - Name: yyS[yypt-0].ident, - IsLeave: false, - } - } - case 2811: - { - parser.yyVAL.statement = &ast.ProcedureJump{ - Name: yyS[yypt-0].ident, - IsLeave: true, - } - } - case 2824: - { - x := &ast.ProcedureInfo{ - IfNotExists: yyS[yypt-5].item.(bool), - ProcedureName: yyS[yypt-4].item.(*ast.TableName), - ProcedureParam: yyS[yypt-2].item.([]*ast.StoreParameter), - ProcedureBody: yyS[yypt-0].statement, - } - startOffset := parser.startOffset(&yyS[yypt]) - originStmt := yyS[yypt-0].statement - originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:parser.yylval.offset])) - startOffset = parser.startOffset(&yyS[yypt-3]) - if parser.src[startOffset] == '(' { - startOffset++ - } - endOffset := parser.startOffset(&yyS[yypt-1]) - x.ProcedureParamStr = strings.TrimSpace(parser.src[startOffset:endOffset]) - parser.yyVAL.statement = x - } - case 2825: - { - parser.yyVAL.statement = &ast.DropProcedureStmt{ - IfExists: yyS[yypt-1].item.(bool), - ProcedureName: yyS[yypt-0].item.(*ast.TableName), - } - } - case 2826: - { - parser.yyVAL.statement = yyS[yypt-0].item.(*ast.CalibrateResourceStmt) - } - case 2827: - { - parser.yyVAL.item = &ast.CalibrateResourceStmt{} - } - case 2828: - { - parser.yyVAL.item = &ast.CalibrateResourceStmt{ - DynamicCalibrateResourceOptionList: yyS[yypt-0].item.([]*ast.DynamicCalibrateResourceOption), - } - } - case 2829: - { - parser.yyVAL.item = &ast.CalibrateResourceStmt{ - Tp: yyS[yypt-0].item.(ast.CalibrateResourceType), - } - } - case 2830: - { - parser.yyVAL.item = []*ast.DynamicCalibrateResourceOption{yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)} - } - case 2831: - { - if yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)[0].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp || - (len(yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)) > 1 && yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)[1].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption), yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)) - } - case 2832: - { - if yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)[0].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp || - (len(yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)) > 1 && yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)[1].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption), yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)) - } - case 2833: - { - parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateStartTime, Ts: yyS[yypt-0].expr.(ast.ExprNode)} - } - case 2834: - { - parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateEndTime, Ts: yyS[yypt-0].expr.(ast.ExprNode)} - } - case 2835: - { - _, err := duration.ParseDuration(yyS[yypt-0].ident) - if err != nil { - yylex.AppendError(yylex.Errorf("The DURATION option is not a valid duration: %s", err.Error())) - return 1 - } - parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateDuration, StrValue: yyS[yypt-0].ident} - } - case 2836: - { - parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateDuration, Ts: yyS[yypt-1].expr.(ast.ExprNode), Unit: yyS[yypt-0].item.(ast.TimeUnitType)} - } - case 2837: - { - parser.yyVAL.item = ast.TPCC - } - case 2838: - { - parser.yyVAL.item = ast.OLTPREADWRITE - } - case 2839: - { - parser.yyVAL.item = ast.OLTPREADONLY - } - case 2840: - { - parser.yyVAL.item = ast.OLTPWRITEONLY - } - case 2841: - { - parser.yyVAL.item = ast.TPCH10 - } - case 2842: - { - parser.yyVAL.statement = &ast.AddQueryWatchStmt{ - QueryWatchOptionList: yyS[yypt-0].item.([]*ast.QueryWatchOption), - } - } - case 2843: - { - parser.yyVAL.item = []*ast.QueryWatchOption{yyS[yypt-0].item.(*ast.QueryWatchOption)} - } - case 2844: - { - if !ast.CheckQueryWatchAppend(yyS[yypt-1].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) - } - case 2845: - { - if !ast.CheckQueryWatchAppend(yyS[yypt-2].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) { - yylex.AppendError(yylex.Errorf("Dupliated options specified")) - return 1 - } - parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) - } - case 2846: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchResourceGroup, StrValue: model.NewCIStr(yyS[yypt-0].ident)} - } - case 2847: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchResourceGroup, ExprValue: yyS[yypt-0].expr} - } - case 2848: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchAction, IntValue: yyS[yypt-0].item.(int32)} - } - case 2849: - { - parser.yyVAL.item = yyS[yypt-0].item.(*ast.QueryWatchOption) - } - case 2850: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: int32(model.WatchSimilar), ExprValue: yyS[yypt-0].expr} - } - case 2851: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: int32(model.WatchPlan), ExprValue: yyS[yypt-0].expr} - } - case 2852: - { - parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: yyS[yypt-2].item.(int32), ExprValue: yyS[yypt-0].expr, BoolValue: true} - } - case 2853: - { - parser.yyVAL.statement = &ast.DropQueryWatchStmt{ - IntValue: yyS[yypt-0].item.(int64), - } - } - - } - - if !parser.lexer.skipPositionRecording { - yySetOffset(parser.yyVAL, parser.yyVAL.offset) - } - - if yyEx != nil && yyEx.Reduced(r, exState, parser.yyVAL) { - return -1 - } - goto yystack /* stack new state and value */ -} diff --git a/parser/parser_test.go b/parser/parser_test.go deleted file mode 100644 index a9606251ffd68..0000000000000 --- a/parser/parser_test.go +++ /dev/null @@ -1,7472 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package parser_test - -import ( - "bytes" - "fmt" - "runtime" - "strings" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - . "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/parser/test_driver" - "github.com/stretchr/testify/require" -) - -func TestSimple(t *testing.T) { - p := parser.New() - - reservedKws := []string{ - "add", "all", "alter", "analyze", "and", "as", "asc", "between", "bigint", - "binary", "blob", "both", "by", "call", "cascade", "case", "change", "character", "check", "collate", - "column", "constraint", "convert", "create", "cross", "current_date", "current_time", - "current_timestamp", "current_user", "database", "databases", "day_hour", "day_microsecond", - "day_minute", "day_second", "decimal", "default", "delete", "desc", "describe", - "distinct", "distinctRow", "div", "double", "drop", "dual", "else", "enclosed", "escaped", - "exists", "explain", "false", "float", "fetch", "for", "force", "foreign", "from", - "fulltext", "grant", "group", "having", "hour_microsecond", "hour_minute", - "hour_second", "if", "ignore", "in", "index", "infile", "inner", "insert", "int", "into", "integer", - "interval", "is", "join", "key", "keys", "kill", "leading", "left", "like", "ilike", "limit", "lines", "load", - "localtime", "localtimestamp", "lock", "longblob", "longtext", "mediumblob", "maxvalue", "mediumint", "mediumtext", - "minute_microsecond", "minute_second", "mod", "not", "no_write_to_binlog", "null", "numeric", - "on", "option", "optionally", "or", "order", "outer", "partition", "precision", "primary", "procedure", "range", "read", "real", "recursive", - "references", "regexp", "rename", "repeat", "replace", "revoke", "restrict", "right", "rlike", - "schema", "schemas", "second_microsecond", "select", "set", "show", "smallint", - "starting", "table", "terminated", "then", "tinyblob", "tinyint", "tinytext", "to", - "trailing", "true", "union", "unique", "unlock", "unsigned", - "update", "use", "using", "utc_date", "values", "varbinary", "varchar", - "when", "where", "write", "xor", "year_month", "zerofill", - "generated", "virtual", "stored", "usage", - "delayed", "high_priority", "low_priority", - "cumeDist", "denseRank", "firstValue", "lag", "lastValue", "lead", "nthValue", "ntile", - "over", "percentRank", "rank", "row", "rows", "rowNumber", "window", "linear", - "match", "until", "placement", "tablesample", "failedLoginAttempts", "passwordLockTime", - // TODO: support the following keywords - // "with", - } - for _, kw := range reservedKws { - src := fmt.Sprintf("SELECT * FROM db.%s;", kw) - _, err := p.ParseOneStmt(src, "", "") - - require.NoErrorf(t, err, "source %s", src) - - src = fmt.Sprintf("SELECT * FROM %s.desc", kw) - _, err = p.ParseOneStmt(src, "", "") - require.NoErrorf(t, err, "source %s", src) - - src = fmt.Sprintf("SELECT t.%s FROM t", kw) - _, err = p.ParseOneStmt(src, "", "") - require.NoErrorf(t, err, "source %s", src) - } - - // Testcase for unreserved keywords - unreservedKws := []string{ - "auto_increment", "after", "begin", "bit", "bool", "boolean", "charset", "columns", "commit", - "date", "datediff", "datetime", "deallocate", "do", "from_days", "end", "engine", "engines", "execute", "extended", "first", "file", "full", - "local", "names", "offset", "password", "prepare", "quick", "rollback", "savepoint", "session", "signed", - "start", "global", "tables", "tablespace", "target", "text", "time", "timestamp", "tidb", "transaction", "truncate", "unknown", - "value", "warnings", "year", "now", "substr", "subpartition", "subpartitions", "substring", "mode", "any", "some", "user", "identified", - "collation", "comment", "avg_row_length", "checksum", "compression", "connection", "key_block_size", - "max_rows", "min_rows", "national", "quarter", "escape", "grants", "status", "fields", "triggers", "language", - "delay_key_write", "isolation", "partitions", "repeatable", "committed", "uncommitted", "only", "serializable", "level", - "curtime", "variables", "dayname", "version", "btree", "hash", "row_format", "dynamic", "fixed", "compressed", - "compact", "redundant", "1 sql_no_cache", "1 sql_cache", "action", "round", - "enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", "sleep", "no", "greatest", "least", - "binlog", "hex", "unhex", "function", "indexes", "from_unixtime", "processlist", "events", "less", "than", "timediff", - "ln", "log", "log2", "log10", "timestampdiff", "pi", "proxy", "quote", "none", "super", "shared", "exclusive", - "always", "stats", "stats_meta", "stats_histogram", "stats_buckets", "stats_healthy", "tidb_version", "replication", "slave", "client", - "max_connections_per_hour", "max_queries_per_hour", "max_updates_per_hour", "max_user_connections", "event", "reload", "routine", "temporary", - "following", "preceding", "unbounded", "respect", "nulls", "current", "last", "against", "expansion", - "chain", "error", "general", "nvarchar", "pack_keys", "p", "shard_row_id_bits", "pre_split_regions", - "constraints", "role", "replicas", "policy", "s3", "strict", "running", "stop", "preserve", "placement", "attributes", "attribute", "resource", - "burstable", "calibrate", "rollup", - } - for _, kw := range unreservedKws { - src := fmt.Sprintf("SELECT %s FROM tbl;", kw) - _, err := p.ParseOneStmt(src, "", "") - require.NoErrorf(t, err, "source %s", src) - } - - // Testcase for prepared statement - src := "SELECT id+?, id+? from t;" - _, err := p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // Testcase for -- Comment and unary -- operator - src = "CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED); -- foo\nSelect --1 from foo;" - stmts, _, err := p.Parse(src, "", "") - require.NoError(t, err) - require.Len(t, stmts, 2) - - // Testcase for /*! xx */ - // See http://dev.mysql.com/doc/refman/5.7/en/comments.html - // Fix: https://github.com/pingcap/tidb/issues/971 - src = "/*!40101 SET character_set_client = utf8 */;" - stmts, _, err = p.Parse(src, "", "") - require.NoError(t, err) - require.Len(t, stmts, 1) - stmt := stmts[0] - _, ok := stmt.(*ast.SetStmt) - require.True(t, ok) - - // for issue #2017 - src = "insert into blobtable (a) values ('/*! truncated */');" - stmt, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - is, ok := stmt.(*ast.InsertStmt) - require.True(t, ok) - require.Len(t, is.Lists, 1) - require.Len(t, is.Lists[0], 1) - require.Equal(t, "/*! truncated */", is.Lists[0][0].(ast.ValueExpr).GetDatumString()) - - // Testcase for CONVERT(expr,type) - src = "SELECT CONVERT('111', SIGNED);" - st, err := p.ParseOneStmt(src, "", "") - require.NoError(t, err) - ss, ok := st.(*ast.SelectStmt) - require.True(t, ok) - require.Len(t, ss.Fields.Fields, 1) - cv, ok := ss.Fields.Fields[0].Expr.(*ast.FuncCastExpr) - require.True(t, ok) - require.Equal(t, ast.CastConvertFunction, cv.FunctionType) - - // for query start with comment - srcs := []string{ - "/* some comments */ SELECT CONVERT('111', SIGNED) ;", - "/* some comments */ /*comment*/ SELECT CONVERT('111', SIGNED) ;", - "SELECT /*comment*/ CONVERT('111', SIGNED) ;", - "SELECT CONVERT('111', /*comment*/ SIGNED) ;", - "SELECT CONVERT('111', SIGNED) /*comment*/;", - } - for _, src := range srcs { - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - _, ok = st.(*ast.SelectStmt) - require.True(t, ok) - } - - // for issue #961 - src = "create table t (c int key);" - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - cs, ok := st.(*ast.CreateTableStmt) - require.True(t, ok) - require.Len(t, cs.Cols, 1) - require.Len(t, cs.Cols[0].Options, 1) - require.Equal(t, ast.ColumnOptionPrimaryKey, cs.Cols[0].Options[0].Tp) - - // for issue #4497 - src = "create table t1(a NVARCHAR(100));" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // for issue 2803 - src = "use quote;" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // issue #4354 - src = "select b'';" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - src = "select B'';" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // src = "select 0b'';" - // _, err = p.ParseOneStmt(src, "", "") - // require.Error(t, err) - - // for #4909, support numericType `signed` filedOpt. - src = "CREATE TABLE t(_sms smallint signed, _smu smallint unsigned);" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // for #7371, support NATIONAL CHARACTER - // reference link: https://dev.mysql.com/doc/refman/5.7/en/charset-national.html - src = "CREATE TABLE t(c1 NATIONAL CHARACTER(10));" - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - src = `CREATE TABLE t(a tinyint signed, - b smallint signed, - c mediumint signed, - d int signed, - e int1 signed, - f int2 signed, - g int3 signed, - h int4 signed, - i int8 signed, - j integer signed, - k bigint signed, - l bool signed, - m boolean signed - );` - - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - ct, ok := st.(*ast.CreateTableStmt) - require.True(t, ok) - for _, col := range ct.Cols { - require.Equal(t, uint(0), col.Tp.GetFlag()&mysql.UnsignedFlag) - } - - // for issue #4006 - src = `insert into tb(v) (select v from tb);` - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // for issue #34642 - src = `SELECT a as c having c = a;` - _, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - - // for issue #9823 - src = "SELECT 9223372036854775807;" - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - sel, ok := st.(*ast.SelectStmt) - require.True(t, ok) - expr := sel.Fields.Fields[0] - vExpr := expr.Expr.(*test_driver.ValueExpr) - require.Equal(t, test_driver.KindInt64, vExpr.Kind()) - src = "SELECT 9223372036854775808;" - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - sel, ok = st.(*ast.SelectStmt) - require.True(t, ok) - expr = sel.Fields.Fields[0] - vExpr = expr.Expr.(*test_driver.ValueExpr) - require.Equal(t, test_driver.KindUint64, vExpr.Kind()) - - src = `select 99e+r10 from t1;` - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - sel, ok = st.(*ast.SelectStmt) - require.True(t, ok) - bExpr, ok := sel.Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) - require.True(t, ok) - require.Equal(t, opcode.Plus, bExpr.Op) - require.Equal(t, "99e", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) - require.Equal(t, "r10", bExpr.R.(*ast.ColumnNameExpr).Name.Name.O) - - src = `select t./*123*/*,@c3:=0 from t order by t.c1;` - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - sel, ok = st.(*ast.SelectStmt) - require.True(t, ok) - require.Equal(t, "t", sel.Fields.Fields[0].WildCard.Table.O) - varExpr, ok := sel.Fields.Fields[1].Expr.(*ast.VariableExpr) - require.True(t, ok) - require.Equal(t, "c3", varExpr.Name) - - src = `select t.1e from test.t;` - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - sel, ok = st.(*ast.SelectStmt) - require.True(t, ok) - colExpr, ok := sel.Fields.Fields[0].Expr.(*ast.ColumnNameExpr) - require.True(t, ok) - require.Equal(t, "t", colExpr.Name.Table.O) - require.Equal(t, "1e", colExpr.Name.Name.O) - tName := sel.From.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) - require.Equal(t, "test", tName.Schema.O) - require.Equal(t, "t", tName.Name.O) - - src = "select t. `a` > 10 from t;" - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - bExpr, ok = st.(*ast.SelectStmt).Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) - require.True(t, ok) - require.Equal(t, opcode.GT, bExpr.Op) - require.Equal(t, "a", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) - require.Equal(t, "t", bExpr.L.(*ast.ColumnNameExpr).Name.Table.O) - require.Equal(t, int64(10), bExpr.R.(ast.ValueExpr).GetValue().(int64)) - - p.SetSQLMode(mysql.ModeANSIQuotes) - src = `select t."dot"=10 from t;` - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - bExpr, ok = st.(*ast.SelectStmt).Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) - require.True(t, ok) - require.Equal(t, opcode.EQ, bExpr.Op) - require.Equal(t, "dot", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) - require.Equal(t, "t", bExpr.L.(*ast.ColumnNameExpr).Name.Table.O) - require.Equal(t, int64(10), bExpr.R.(ast.ValueExpr).GetValue().(int64)) -} - -func TestSpecialComments(t *testing.T) { - p := parser.New() - - // 1. Make sure /*! ... */ respects the same SQL mode. - _, err := p.ParseOneStmt(`SELECT /*! '\' */;`, "", "") - require.Error(t, err) - - p.SetSQLMode(mysql.ModeNoBackslashEscapes) - st, err := p.ParseOneStmt(`SELECT /*! '\' */;`, "", "") - require.NoError(t, err) - require.IsType(t, &ast.SelectStmt{}, st) - - // 2. Make sure multiple statements inside /*! ... */ will not crash - // (this is issue #330) - stmts, _, err := p.Parse("/*! SET x = 1; SELECT 2 */", "", "") - require.NoError(t, err) - require.Len(t, stmts, 2) - require.IsType(t, &ast.SetStmt{}, stmts[0]) - require.Equal(t, "/*! SET x = 1;", stmts[0].Text()) - require.IsType(t, &ast.SelectStmt{}, stmts[1]) - require.Equal(t, " SELECT 2 */", stmts[1].Text()) - // ^ not sure if correct approach; having multiple statements in MySQL is a syntax error. - - // 3. Make sure invalid text won't cause infinite loop - // (this is issue #336) - st, err = p.ParseOneStmt("SELECT /*+ 😅 */ SLEEP(1);", "", "") - require.NoError(t, err) - sel, ok := st.(*ast.SelectStmt) - require.True(t, ok) - require.Len(t, sel.TableHints, 0) -} - -type testCase struct { - src string - ok bool - restore string -} - -type testErrMsgCase struct { - src string - err error -} - -func RunTest(t *testing.T, table []testCase, enableWindowFunc bool) { - p := parser.New() - p.EnableWindowFunc(enableWindowFunc) - for _, tbl := range table { - _, _, err := p.Parse(tbl.src, "", "") - if !tbl.ok { - require.Errorf(t, err, "source %v", tbl.src, errors.Trace(err)) - continue - } - require.NoErrorf(t, err, "source %v", tbl.src, errors.Trace(err)) - // restore correctness test - if tbl.ok { - RunRestoreTest(t, tbl.src, tbl.restore, enableWindowFunc) - } - } -} - -func RunRestoreTest(t *testing.T, sourceSQLs, expectSQLs string, enableWindowFunc bool) { - var sb strings.Builder - p := parser.New() - p.EnableWindowFunc(enableWindowFunc) - comment := fmt.Sprintf("source %v", sourceSQLs) - stmts, _, err := p.Parse(sourceSQLs, "", "") - require.NoErrorf(t, err, "source %v", sourceSQLs) - restoreSQLs := "" - for _, stmt := range stmts { - sb.Reset() - err = stmt.Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) - require.NoError(t, err, comment) - restoreSQL := sb.String() - comment = fmt.Sprintf("source %v; restore %v", sourceSQLs, restoreSQL) - restoreStmt, err := p.ParseOneStmt(restoreSQL, "", "") - require.NoError(t, err, comment) - CleanNodeText(stmt) - CleanNodeText(restoreStmt) - require.Equal(t, stmt, restoreStmt, comment) - if restoreSQLs != "" { - restoreSQLs += "; " - } - restoreSQLs += restoreSQL - } - require.Equalf(t, expectSQLs, restoreSQLs, "restore %v; expect %v", restoreSQLs, expectSQLs) -} - -func RunTestInRealAsFloatMode(t *testing.T, table []testCase, enableWindowFunc bool) { - p := parser.New() - p.EnableWindowFunc(enableWindowFunc) - p.SetSQLMode(mysql.ModeRealAsFloat) - for _, tbl := range table { - _, _, err := p.Parse(tbl.src, "", "") - comment := fmt.Sprintf("source %v", tbl.src) - if !tbl.ok { - require.Error(t, err, comment) - continue - } - require.NoError(t, err, comment) - // restore correctness test - if tbl.ok { - RunRestoreTestInRealAsFloatMode(t, tbl.src, tbl.restore, enableWindowFunc) - } - } -} - -func RunRestoreTestInRealAsFloatMode(t *testing.T, sourceSQLs, expectSQLs string, enableWindowFunc bool) { - var sb strings.Builder - p := parser.New() - p.EnableWindowFunc(enableWindowFunc) - p.SetSQLMode(mysql.ModeRealAsFloat) - comment := fmt.Sprintf("source %v", sourceSQLs) - stmts, _, err := p.Parse(sourceSQLs, "", "") - require.NoError(t, err, comment) - restoreSQLs := "" - for _, stmt := range stmts { - sb.Reset() - err = stmt.Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) - require.NoError(t, err, comment) - restoreSQL := sb.String() - comment = fmt.Sprintf("source %v; restore %v", sourceSQLs, restoreSQL) - restoreStmt, err := p.ParseOneStmt(restoreSQL, "", "") - require.NoError(t, err, comment) - CleanNodeText(stmt) - CleanNodeText(restoreStmt) - require.Equal(t, stmt, restoreStmt, comment) - if restoreSQLs != "" { - restoreSQLs += "; " - } - restoreSQLs += restoreSQL - } - require.Equal(t, expectSQLs, restoreSQLs, "restore %v; expect %v", restoreSQLs, expectSQLs) -} - -func RunErrMsgTest(t *testing.T, table []testErrMsgCase) { - p := parser.New() - for _, tbl := range table { - _, _, err := p.Parse(tbl.src, "", "") - comment := fmt.Sprintf("source %v", tbl.src) - if tbl.err != nil { - require.True(t, terror.ErrorEqual(err, tbl.err), comment) - } else { - require.NoError(t, err, comment) - } - } -} - -func TestDMLStmt(t *testing.T) { - table := []testCase{ - {"", true, ""}, - {";", true, ""}, - {"INSERT INTO foo VALUES (1234)", true, "INSERT INTO `foo` VALUES (1234)"}, - {"INSERT INTO foo VALUES (1234, 5678)", true, "INSERT INTO `foo` VALUES (1234,5678)"}, - {"INSERT INTO t1 (SELECT * FROM t2)", true, "INSERT INTO `t1` (SELECT * FROM `t2`)"}, - {"INSERT INTO t partition (p0) values(1234)", true, "INSERT INTO `t` PARTITION(`p0`) VALUES (1234)"}, - {"REPLACE INTO t partition (p0) values(1234)", true, "REPLACE INTO `t` PARTITION(`p0`) VALUES (1234)"}, - {"INSERT INTO t partition (p0, p1, p2) values(1234)", true, "INSERT INTO `t` PARTITION(`p0`, `p1`, `p2`) VALUES (1234)"}, - {"REPLACE INTO t partition (p0, p1, p2) values(1234)", true, "REPLACE INTO `t` PARTITION(`p0`, `p1`, `p2`) VALUES (1234)"}, - // 15 - {"INSERT INTO foo VALUES (1 || 2)", true, "INSERT INTO `foo` VALUES (1 OR 2)"}, - {"INSERT INTO foo VALUES (1 | 2)", true, "INSERT INTO `foo` VALUES (1|2)"}, - {"INSERT INTO foo VALUES (false || true)", true, "INSERT INTO `foo` VALUES (FALSE OR TRUE)"}, - {"INSERT INTO foo VALUES (bar(5678))", true, "INSERT INTO `foo` VALUES (BAR(5678))"}, - // 20 - {"INSERT INTO foo VALUES ()", true, "INSERT INTO `foo` VALUES ()"}, - {"SELECT * FROM t", true, "SELECT * FROM `t`"}, - {"SELECT * FROM t AS u", true, "SELECT * FROM `t` AS `u`"}, - // 25 - {"SELECT * FROM t, v", true, "SELECT * FROM (`t`) JOIN `v`"}, - {"SELECT * FROM t AS u, v", true, "SELECT * FROM (`t` AS `u`) JOIN `v`"}, - {"SELECT * FROM t, v AS w", true, "SELECT * FROM (`t`) JOIN `v` AS `w`"}, - {"SELECT * FROM t AS u, v AS w", true, "SELECT * FROM (`t` AS `u`) JOIN `v` AS `w`"}, - {"SELECT * FROM foo, bar, foo", true, "SELECT * FROM ((`foo`) JOIN `bar`) JOIN `foo`"}, - // 30 - {"SELECT DISTINCTS * FROM t", false, ""}, - {"SELECT DISTINCT * FROM t", true, "SELECT DISTINCT * FROM `t`"}, - {"SELECT DISTINCTROW * FROM t", true, "SELECT DISTINCT * FROM `t`"}, - {"SELECT ALL * FROM t", true, "SELECT ALL * FROM `t`"}, - {"SELECT DISTINCT ALL * FROM t", false, ""}, - {"SELECT DISTINCTROW ALL * FROM t", false, ""}, - {"INSERT INTO foo (a) VALUES (42)", true, "INSERT INTO `foo` (`a`) VALUES (42)"}, - {"INSERT INTO foo (a,) VALUES (42,)", false, ""}, - // 35 - {"INSERT INTO foo (a,b) VALUES (42,314)", true, "INSERT INTO `foo` (`a`,`b`) VALUES (42,314)"}, - {"INSERT INTO foo (a,b,) VALUES (42,314)", false, ""}, - {"INSERT INTO foo (a,b,) VALUES (42,314,)", false, ""}, - {"INSERT INTO foo () VALUES ()", true, "INSERT INTO `foo` () VALUES ()"}, - {"INSERT INTO foo VALUE ()", true, "INSERT INTO `foo` VALUES ()"}, - - // for issue 2402 - {"INSERT INTO tt VALUES (01000001783);", true, "INSERT INTO `tt` VALUES (1000001783)"}, - {"INSERT INTO tt VALUES (default);", true, "INSERT INTO `tt` VALUES (DEFAULT)"}, - - {"REPLACE INTO foo VALUES (1 || 2)", true, "REPLACE INTO `foo` VALUES (1 OR 2)"}, - {"REPLACE INTO foo VALUES (1 | 2)", true, "REPLACE INTO `foo` VALUES (1|2)"}, - {"REPLACE INTO foo VALUES (false || true)", true, "REPLACE INTO `foo` VALUES (FALSE OR TRUE)"}, - {"REPLACE INTO foo VALUES (bar(5678))", true, "REPLACE INTO `foo` VALUES (BAR(5678))"}, - {"REPLACE INTO foo VALUES ()", true, "REPLACE INTO `foo` VALUES ()"}, - {"REPLACE INTO foo (a,b) VALUES (42,314)", true, "REPLACE INTO `foo` (`a`,`b`) VALUES (42,314)"}, - {"REPLACE INTO foo (a,b,) VALUES (42,314)", false, ""}, - {"REPLACE INTO foo (a,b,) VALUES (42,314,)", false, ""}, - {"REPLACE INTO foo () VALUES ()", true, "REPLACE INTO `foo` () VALUES ()"}, - {"REPLACE INTO foo VALUE ()", true, "REPLACE INTO `foo` VALUES ()"}, - // 40 - {`SELECT stuff.id - FROM stuff - WHERE stuff.value >= ALL (SELECT stuff.value - FROM stuff)`, true, "SELECT `stuff`.`id` FROM `stuff` WHERE `stuff`.`value`>=ALL (SELECT `stuff`.`value` FROM `stuff`)"}, - {"BEGIN", true, "START TRANSACTION"}, - {"START TRANSACTION", true, "START TRANSACTION"}, - // 45 - {"COMMIT", true, "COMMIT"}, - {"COMMIT AND NO CHAIN", true, "COMMIT"}, - {"COMMIT NO RELEASE", true, "COMMIT"}, - {"COMMIT AND NO CHAIN NO RELEASE", true, "COMMIT"}, - {"COMMIT AND NO CHAIN RELEASE", true, "COMMIT RELEASE"}, - {"COMMIT AND CHAIN NO RELEASE", true, "COMMIT AND CHAIN"}, - {"COMMIT AND CHAIN RELEASE", false, ""}, - {"ROLLBACK", true, "ROLLBACK"}, - {"ROLLBACK AND NO CHAIN", true, "ROLLBACK"}, - {"ROLLBACK NO RELEASE", true, "ROLLBACK"}, - {"ROLLBACK AND NO CHAIN NO RELEASE", true, "ROLLBACK"}, - {"ROLLBACK AND NO CHAIN RELEASE", true, "ROLLBACK RELEASE"}, - {"ROLLBACK AND CHAIN NO RELEASE", true, "ROLLBACK AND CHAIN"}, - {"ROLLBACK AND CHAIN RELEASE", false, ""}, - {`BEGIN; - INSERT INTO foo VALUES (42, 3.14); - INSERT INTO foo VALUES (-1, 2.78); - COMMIT;`, true, "START TRANSACTION; INSERT INTO `foo` VALUES (42,3.14); INSERT INTO `foo` VALUES (-1,2.78); COMMIT"}, - {`BEGIN; - INSERT INTO tmp SELECT * from bar; - SELECT * from tmp; - ROLLBACK;`, true, "START TRANSACTION; INSERT INTO `tmp` SELECT * FROM `bar`; SELECT * FROM `tmp`; ROLLBACK"}, - {"SAVEPOINT x", true, "SAVEPOINT x"}, - {"RELEASE SAVEPOINT x", true, "RELEASE SAVEPOINT x"}, - {"ROLLBACK TO x", true, "ROLLBACK TO x"}, - {"ROLLBACK TO X", true, "ROLLBACK TO X"}, - {"ROLLBACK TO SAVEPOINT x", true, "ROLLBACK TO x"}, - - // table statement - {"TABLE t", true, "TABLE `t`"}, - {"(TABLE t)", true, "(TABLE `t`)"}, - {"TABLE t1, t2", false, ""}, - {"TABLE t ORDER BY b", true, "TABLE `t` ORDER BY `b`"}, - {"TABLE t LIMIT 3", true, "TABLE `t` LIMIT 3"}, - {"TABLE t ORDER BY b LIMIT 3", true, "TABLE `t` ORDER BY `b` LIMIT 3"}, - {"TABLE t ORDER BY b LIMIT 3 OFFSET 2", true, "TABLE `t` ORDER BY `b` LIMIT 2,3"}, - {"TABLE t ORDER BY b LIMIT 2,3", true, "TABLE `t` ORDER BY `b` LIMIT 2,3"}, - {"INSERT INTO ta TABLE tb", true, "INSERT INTO `ta` TABLE `tb`"}, - {"INSERT INTO t.a TABLE t.b", true, "INSERT INTO `t`.`a` TABLE `t`.`b`"}, - {"REPLACE INTO ta TABLE tb", true, "REPLACE INTO `ta` TABLE `tb`"}, - {"REPLACE INTO t.a TABLE t.b", true, "REPLACE INTO `t`.`a` TABLE `t`.`b`"}, - {"TABLE t1 INTO OUTFILE 'a.txt'", true, "TABLE `t1` INTO OUTFILE 'a.txt'"}, - {"TABLE t ORDER BY a INTO OUTFILE '/tmp/abc'", true, "TABLE `t` ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, - {"CREATE TABLE t.a TABLE t.b", true, "CREATE TABLE `t`.`a` AS TABLE `t`.`b`"}, - {"CREATE TABLE ta TABLE tb", true, "CREATE TABLE `ta` AS TABLE `tb`"}, - {"CREATE TABLE ta (x INT) TABLE tb", true, "CREATE TABLE `ta` (`x` INT) AS TABLE `tb`"}, - {"CREATE VIEW v AS TABLE t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS TABLE `t`"}, - {"CREATE VIEW v AS (TABLE t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (TABLE `t`)"}, - {"SELECT * FROM t1 WHERE a IN (TABLE t2)", true, "SELECT * FROM `t1` WHERE `a` IN (TABLE `t2`)"}, - - // values statement - {"VALUES ROW(1)", true, "VALUES ROW(1)"}, - {"VALUES ROW()", true, "VALUES ROW()"}, - {"VALUES ROW(1, default)", true, "VALUES ROW(1,DEFAULT)"}, - {"VALUES ROW(1), ROW(2,3)", true, "VALUES ROW(1), ROW(2,3)"}, - {"VALUES (1,2)", false, ""}, - {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8)", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8)"}, - {"VALUES ROW(1,s,3.1), ROW(5,y,9.9)", true, "VALUES ROW(1,`s`,3.1), ROW(5,`y`,9.9)"}, - {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) LIMIT 3", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) LIMIT 3"}, - {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY a", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY `a`"}, - {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY a LIMIT 2", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY `a` LIMIT 2"}, - {"VALUES ROW(1,-2,3), ROW(5,7,9) INTO OUTFILE 'a.txt'", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTO OUTFILE 'a.txt'"}, - {"VALUES ROW(1,-2,3), ROW(5,7,9) ORDER BY a INTO OUTFILE '/tmp/abc'", true, "VALUES ROW(1,-2,3), ROW(5,7,9) ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, - {"CREATE TABLE ta VALUES ROW(1)", true, "CREATE TABLE `ta` AS VALUES ROW(1)"}, - {"CREATE TABLE ta AS VALUES ROW(1)", true, "CREATE TABLE `ta` AS VALUES ROW(1)"}, - {"CREATE VIEW a AS VALUES ROW(1)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `a` AS VALUES ROW(1)"}, - - // qualified select - {"SELECT a.b.c FROM t", true, "SELECT `a`.`b`.`c` FROM `t`"}, - {"SELECT a.b.*.c FROM t", false, ""}, - {"SELECT a.b.* FROM t", true, "SELECT `a`.`b`.* FROM `t`"}, - {"SELECT a FROM t", true, "SELECT `a` FROM `t`"}, - {"SELECT a.b.c.d FROM t", false, ""}, - - // do statement - {"DO 1", true, "DO 1"}, - {"DO 1, sleep(1)", true, "DO 1, SLEEP(1)"}, - {"DO 1 from t", false, ""}, - - // load data - {"load data local infile '/tmp/t.csv' into table t1 fields terminated by ',' optionally enclosed by '\"' ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES"}, - {"load data infile '/tmp/t.csv' into table t", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t`"}, - {"load data infile '/tmp/t.csv' into table t character set utf8", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` CHARACTER SET utf8"}, - {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, - {"load data infile '/tmp/t.csv' into table t columns terminated by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, - {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, - {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, - {"load data infile '/tmp/t.csv' into table t lines starting by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` LINES STARTING BY 'ab'"}, - {"load data infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy'"}, - {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy'"}, - {"load data infile '/tmp/t.csv' into table t terminated by 'xy' fields terminated by 'ab'", false, ""}, - {"load data local infile '/tmp/t.csv' into table t", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t`"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, - {"load data local infile '/tmp/t.csv' into table t columns terminated by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, - {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, - {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab'"}, - {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy'"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy'"}, - {"load data local infile '/tmp/t.csv' into table t terminated by 'xy' fields terminated by 'ab'", false, ""}, - {"load data infile '/tmp/t.csv' into table t (a,b)", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t columns terminated by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' enclosed by 'b' escaped by '*' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' lines terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy' (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t (a,b) fields terminated by 'ab'", false, ""}, - {"load data local infile '/tmp/t.csv' into table t ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` IGNORE 1 LINES"}, - {"load data local infile '/tmp/t.csv' into table t ignore -1 lines", false, ""}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' (a,b) ignore 1 lines", false, ""}, - {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy' ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy' IGNORE 1 LINES"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*' ignore 1 lines (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' IGNORE 1 LINES (`a`,`b`)"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by ''", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY ''"}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk'"}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B' enclosed by X'0D';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk' ENCLOSED BY '\r'"}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B' enclosed by X'0D0D';", false, ""}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk'"}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011' enclosed by B'1101';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk' ENCLOSED BY '\r'"}, - {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011' enclosed by B'110100001101';", false, ""}, - {"load data local infile '~/1.csv' into table `t_ascii` lines starting by B'110101101101011' terminated by B'110101101101011';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` LINES STARTING BY 'kk' TERMINATED BY 'kk'"}, - {"load data local infile '~/1.csv' into table `t_ascii` lines starting by X'6B6B' terminated by X'6B6B';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` LINES STARTING BY 'kk' TERMINATED BY 'kk'"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' escaped by '' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY ''"}, - {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' escaped by '' enclosed by 'b' SET b = CAST(CONV(MID(@var1, 3, LENGTH(@var1)-3), 2, 10) AS UNSIGNED)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '' SET `b`=CAST(CONV(MID(@`var1`, 3, LENGTH(@`var1`)-3), 2, 10) AS UNSIGNED)"}, - - {"load data infile '/tmp/t.csv' into table t fields enclosed by ''", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ENCLOSED BY ''"}, - {"load data infile '/tmp/t.csv' into table t fields enclosed by 'a'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ENCLOSED BY 'a'"}, - {"load data infile '/tmp/t.csv' into table t fields enclosed by 'aa'", false, ""}, - {"load data infile '/tmp/t.csv' into table t fields escaped by ''", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ESCAPED BY ''"}, - {"load data infile '/tmp/t.csv' into table t fields escaped by 'a'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ESCAPED BY 'a'"}, - {"load data infile '/tmp/t.csv' into table t fields escaped by 'aa'", false, ""}, - - {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @dummy, column2, @dummy, column3)", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`dummy`,`column2`,@`dummy`,`column3`)"}, - {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, - {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @var1, @var2) SET column2 = @var1/100, column3 = DEFAULT, column4=CURRENT_TIMESTAMP, column5=@var2+1", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`var1`,@`var2`) SET `column2`=@`var1`/100, `column3`=DEFAULT, `column4`=CURRENT_TIMESTAMP(), `column5`=@`var2`+1"}, - - {"LOAD DATA INFILE '/tmp/t.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, - {"LOAD DATA LOCAL INFILE '/tmp/t.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, - {"LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, - {"LOAD DATA LOCAL INFILE '/tmp/t.csv' REPLACE INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' REPLACE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, - - {"load data infile 's3://bucket-name/t.csv' into table t", true, "LOAD DATA INFILE 's3://bucket-name/t.csv' INTO TABLE `t`"}, - {"load data infile '/tmp/t.csv' into table t fields defined null by 'nil'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY 'nil'"}, - {"load data infile '/tmp/t.csv' into table t fields defined null by X'00'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY '\x00'"}, - {"load data infile '/tmp/t.csv' into table t fields defined null by 'NULL' optionally enclosed ignore 1 lines", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY 'NULL' OPTIONALLY ENCLOSED IGNORE 1 LINES"}, - {"load data infile '/tmp/t.csv' format 'delimited data' into table t (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE '/tmp/t.csv' FORMAT 'delimited data' INTO TABLE `t` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, - {"load data local infile '/tmp/t.sql' format 'sql file' replace into table t (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.sql' FORMAT 'sql file' REPLACE INTO TABLE `t` (`a`,`b`)"}, - {"load data infile '/tmp/t.parquet' format 'parquet' into table t (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE '/tmp/t.parquet' FORMAT 'parquet' INTO TABLE `t` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, - - {"load data infile '/tmp/t.csv' into table t with detached", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH detached"}, - // we must add "`" to table name, since the offset of restored sql might be different with the source - {"load data infile '/tmp/t.csv' into table `t` with threads=10", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH threads=10"}, - {"load data infile '/tmp/t.csv' into table `t` with threads=10, detached", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH threads=10, detached"}, - - // IMPORT INTO - {"import into t from '/file.csv'", true, "IMPORT INTO `t` FROM '/file.csv'"}, - {"import into t (a,b) from '/file.csv'", true, "IMPORT INTO `t` (`a`,`b`) FROM '/file.csv'"}, - {"import into t (a,@1) from '/file.csv'", true, "IMPORT INTO `t` (`a`,@`1`) FROM '/file.csv'"}, - {"import into t (a,@1) set b=@1+100 from '/file.csv'", true, "IMPORT INTO `t` (`a`,@`1`) SET `b`=@`1`+100 FROM '/file.csv'"}, - {"import into t from '/file.csv' format 'sql file'", true, "IMPORT INTO `t` FROM '/file.csv' FORMAT 'sql file'"}, - {"import into t from '/file.csv' with detached", true, "IMPORT INTO `t` FROM '/file.csv' WITH detached"}, - {"import into `t` from '/file.csv' with thread=1", true, "IMPORT INTO `t` FROM '/file.csv' WITH thread=1"}, - {"import into `t` from '/file.csv' with detached, thread=1", true, "IMPORT INTO `t` FROM '/file.csv' WITH detached, thread=1"}, - - // select for update/share - {"select * from t for update", true, "SELECT * FROM `t` FOR UPDATE"}, - {"select * from t for share", true, "SELECT * FROM `t` FOR SHARE"}, - {"select * from t for update nowait", true, "SELECT * FROM `t` FOR UPDATE NOWAIT"}, - {"select * from t for update wait 5", true, "SELECT * FROM `t` FOR UPDATE WAIT 5"}, - {"select * from t limit 1 for update wait 11", true, "SELECT * FROM `t` LIMIT 1 FOR UPDATE WAIT 11"}, - {"select * from t for share nowait", true, "SELECT * FROM `t` FOR SHARE NOWAIT"}, - {"select * from t for update skip locked", true, "SELECT * FROM `t` FOR UPDATE SKIP LOCKED"}, - {"select * from t for share skip locked", true, "SELECT * FROM `t` FOR SHARE SKIP LOCKED"}, - {"select * from t lock in share mode", true, "SELECT * FROM `t` FOR SHARE"}, - {"select * from t lock in share mode nowait", false, ""}, - {"select * from t lock in share mode skip locked", false, ""}, - - {"select * from t for update of t", true, "SELECT * FROM `t` FOR UPDATE OF `t`"}, - {"select * from t for share of t", true, "SELECT * FROM `t` FOR SHARE OF `t`"}, - {"select * from t for update of t nowait", true, "SELECT * FROM `t` FOR UPDATE OF `t` NOWAIT"}, - {"select * from t for update of t wait 5", true, "SELECT * FROM `t` FOR UPDATE OF `t` WAIT 5"}, - {"select * from t limit 1 for update of t wait 11", true, "SELECT * FROM `t` LIMIT 1 FOR UPDATE OF `t` WAIT 11"}, - {"select * from t for share of t nowait", true, "SELECT * FROM `t` FOR SHARE OF `t` NOWAIT"}, - {"select * from t for update of t skip locked", true, "SELECT * FROM `t` FOR UPDATE OF `t` SKIP LOCKED"}, - {"select * from t for share of t skip locked", true, "SELECT * FROM `t` FOR SHARE OF `t` SKIP LOCKED"}, - - // select into outfile - {"select a, b from t into outfile '/tmp/result.txt'", true, "SELECT `a`,`b` FROM `t` INTO OUTFILE '/tmp/result.txt'"}, - {"select a from t order by a into outfile '/tmp/abc'", true, "SELECT `a` FROM `t` ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, - {"select 1 into outfile '/tmp/1.csv'", true, "SELECT 1 INTO OUTFILE '/tmp/1.csv'"}, - {"select 1 for update into outfile '/tmp/1.csv'", true, "SELECT 1 FOR UPDATE INTO OUTFILE '/tmp/1.csv'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ','", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ','"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' lines terminated BY '\n'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' LINES TERMINATED BY '\n'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"' lines terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\r'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"' lines terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"' lines starting by 'xy' terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES STARTING BY 'xy' TERMINATED BY '\r'"}, - {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"' lines starting by 'xy' terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES STARTING BY 'xy' TERMINATED BY '\r'"}, - - // from join - {"SELECT * from t1, t2, t3", true, "SELECT * FROM ((`t1`) JOIN `t2`) JOIN `t3`"}, - {"select * from t1 join t2 left join t3 on t2.id = t3.id", true, "SELECT * FROM (`t1` JOIN `t2`) LEFT JOIN `t3` ON `t2`.`id`=`t3`.`id`"}, - {"select * from t1 right join t2 on t1.id = t2.id left join t3 on t3.id = t2.id", true, "SELECT * FROM (`t1` RIGHT JOIN `t2` ON `t1`.`id`=`t2`.`id`) LEFT JOIN `t3` ON `t3`.`id`=`t2`.`id`"}, - {"select * from t1 right join t2 on t1.id = t2.id left join t3", false, ""}, - {"select * from t1 join t2 left join t3 using (id)", true, "SELECT * FROM (`t1` JOIN `t2`) LEFT JOIN `t3` USING (`id`)"}, - {"select * from t1 right join t2 using (id) left join t3 using (id)", true, "SELECT * FROM (`t1` RIGHT JOIN `t2` USING (`id`)) LEFT JOIN `t3` USING (`id`)"}, - {"select * from t1 right join t2 using (id) left join t3", false, ""}, - {"select * from t1 natural join t2", true, "SELECT * FROM `t1` NATURAL JOIN `t2`"}, - {"select * from t1 natural right join t2", true, "SELECT * FROM `t1` NATURAL RIGHT JOIN `t2`"}, - {"select * from t1 natural left outer join t2", true, "SELECT * FROM `t1` NATURAL LEFT JOIN `t2`"}, - {"select * from t1 natural inner join t2", false, ""}, - {"select * from t1 natural cross join t2", false, ""}, - {"select * from t3 join t1 join t2 on t1.a=t2.a on t3.b=t2.b", true, "SELECT * FROM `t3` JOIN (`t1` JOIN `t2` ON `t1`.`a`=`t2`.`a`) ON `t3`.`b`=`t2`.`b`"}, - - // for straight_join - {"select * from t1 straight_join t2 on t1.id = t2.id", true, "SELECT * FROM `t1` STRAIGHT_JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, - {"select straight_join * from t1 join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, - {"select straight_join * from t1 left join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` LEFT JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, - {"select straight_join * from t1 right join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` RIGHT JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, - {"select straight_join * from t1 straight_join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` STRAIGHT_JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, - - // delete statement - // single table syntax - {"DELETE from t1", true, "DELETE FROM `t1`"}, - {"DELETE from t1.*", false, ""}, - {"DELETE LOW_priORITY from t1", true, "DELETE LOW_PRIORITY FROM `t1`"}, - {"DELETE quick from t1", true, "DELETE QUICK FROM `t1`"}, - {"DELETE ignore from t1", true, "DELETE IGNORE FROM `t1`"}, - {"DELETE low_priority quick ignore from t1", true, "DELETE LOW_PRIORITY QUICK IGNORE FROM `t1`"}, - {"DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a", true, "DELETE FROM `t1` WHERE `t1`.`a`>0 ORDER BY `t1`.`a`"}, - {"delete from t1 where a=26", true, "DELETE FROM `t1` WHERE `a`=26"}, - {"DELETE from t1 where a=1 limit 1", true, "DELETE FROM `t1` WHERE `a`=1 LIMIT 1"}, - {"DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a LIMIT 1", true, "DELETE FROM `t1` WHERE `t1`.`a`>0 ORDER BY `t1`.`a` LIMIT 1"}, - {"DELETE FROM x.y z WHERE z.a > 0", true, "DELETE FROM `x`.`y` AS `z` WHERE `z`.`a`>0"}, - {"DELETE FROM t1 AS w WHERE a > 0", true, "DELETE FROM `t1` AS `w` WHERE `a`>0"}, - {"DELETE from t1 partition (p0,p1)", true, "DELETE FROM `t1` PARTITION(`p0`, `p1`)"}, - - // multi table syntax: before from - {"delete low_priority t1, t2 from t1, t2", true, "DELETE LOW_PRIORITY `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete quick t1, t2 from t1, t2", true, "DELETE QUICK `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete ignore t1, t2 from t1, t2", true, "DELETE IGNORE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete ignore t1, t2 from t1 partition (p0,p1), t2", true, "DELETE IGNORE `t1`,`t2` FROM (`t1` PARTITION(`p0`, `p1`)) JOIN `t2`"}, - {"delete low_priority quick ignore t1, t2 from t1, t2 where t1.a > 5", true, "DELETE LOW_PRIORITY QUICK IGNORE `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`a`>5"}, - {"delete t1, t2 from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete t1, t2 from t1, t2 where t1.a = 1 and t2.b <> 1", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`a`=1 AND `t2`.`b`!=1"}, - {"delete t1 from t1, t2", true, "DELETE `t1` FROM (`t1`) JOIN `t2`"}, - {"delete t2 from t1, t2", true, "DELETE `t2` FROM (`t1`) JOIN `t2`"}, - {"delete t1 from t1", true, "DELETE `t1` FROM `t1`"}, - {"delete t1,t2,t3 from t1, t2, t3", true, "DELETE `t1`,`t2`,`t3` FROM ((`t1`) JOIN `t2`) JOIN `t3`"}, - {"delete t1,t2,t3 from t1, t2, t3 where t3.c < 5 and t1.a = 3", true, "DELETE `t1`,`t2`,`t3` FROM ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t3`.`c`<5 AND `t1`.`a`=3"}, - {"delete t1 from t1, t1 as t2 where t1.b = t2.b and t1.a > t2.a", true, "DELETE `t1` FROM (`t1`) JOIN `t1` AS `t2` WHERE `t1`.`b`=`t2`.`b` AND `t1`.`a`>`t2`.`a`"}, - {"delete t1.*,t2 from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete t.t1.*,t2 from t1, t2", true, "DELETE `t`.`t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete t1.*, t2.* from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, - {"delete t11.*, t12.* from t11, t12 where t11.a = t12.a and t11.b <> 1", true, "DELETE `t11`,`t12` FROM (`t11`) JOIN `t12` WHERE `t11`.`a`=`t12`.`a` AND `t11`.`b`!=1"}, - - // multi table syntax: with using - {"DELETE quick FROM t1,t2 USING t1,t2", true, "DELETE QUICK FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, - {"DELETE low_priority ignore FROM t1,t2 USING t1,t2", true, "DELETE LOW_PRIORITY IGNORE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, - {"DELETE low_priority quick ignore FROM t1,t2 USING t1,t2", true, "DELETE LOW_PRIORITY QUICK IGNORE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, - {"DELETE FROM t1 USING t1 WHERE post='1'", true, "DELETE FROM `t1` USING `t1` WHERE `post`=_UTF8MB4'1'"}, - {"DELETE FROM t1,t2 USING t1,t2", true, "DELETE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, - {"DELETE FROM t1,t2,t3 USING t1,t2,t3 where t3.a = 1", true, "DELETE FROM `t1`,`t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t3`.`a`=1"}, - {"DELETE FROM t2,t3 USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, - {"DELETE FROM t2.*,t3.* USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, - {"DELETE FROM t1,t2.*,t3.* USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t1`,`t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, - - // for delete statement - {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true, "DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, - {"DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true, "DELETE FROM `t1`,`t2` USING (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, - // for optimizer hint in delete statement - {"DELETE /*+ TiDB_INLJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id;", true, "DELETE /*+ TIDB_INLJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, - {"DELETE /*+ TiDB_HJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id", true, "DELETE /*+ TIDB_HJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, - {"DELETE /*+ TiDB_SMJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id", true, "DELETE /*+ TIDB_SMJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, - // for "USE INDEX" in delete statement - {"DELETE FROM t1 USE INDEX(idx_a) WHERE t1.id=1;", true, "DELETE FROM `t1` USE INDEX (`idx_a`) WHERE `t1`.`id`=1"}, - {"DELETE t1, t2 FROM t1 USE INDEX(idx_a) JOIN t2 WHERE t1.id=t2.id;", true, "DELETE `t1`,`t2` FROM `t1` USE INDEX (`idx_a`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, - {"DELETE t1, t2 FROM t1 USE INDEX(idx_a) JOIN t2 USE INDEX(idx_a) WHERE t1.id=t2.id;", true, "DELETE `t1`,`t2` FROM `t1` USE INDEX (`idx_a`) JOIN `t2` USE INDEX (`idx_a`) WHERE `t1`.`id`=`t2`.`id`"}, - - // for fail case - {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id limit 10;", false, ""}, - {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id order by t1.id;", false, ""}, - - // for admin - {"admin show ddl;", true, "ADMIN SHOW DDL"}, - {"admin show ddl jobs;", true, "ADMIN SHOW DDL JOBS"}, - {"admin show ddl jobs where id > 0;", true, "ADMIN SHOW DDL JOBS WHERE `id`>0"}, - {"admin show ddl jobs 20 where id=0;", true, "ADMIN SHOW DDL JOBS 20 WHERE `id`=0"}, - {"admin show ddl jobs -1;", false, ""}, - {"admin show ddl job queries 1", true, "ADMIN SHOW DDL JOB QUERIES 1"}, - {"admin show ddl job queries 1, 2, 3, 4", true, "ADMIN SHOW DDL JOB QUERIES 1, 2, 3, 4"}, - {"admin show ddl job queries limit 5", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 0, 5"}, - {"admin show ddl job queries limit 5, 10", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 5, 10"}, - {"admin show ddl job queries limit 3 offset 2", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 2, 3"}, - {"admin show ddl job queries limit 22 offset 0", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 0, 22"}, - {"admin show t1 next_row_id", true, "ADMIN SHOW `t1` NEXT_ROW_ID"}, - {"admin check table t1, t2;", true, "ADMIN CHECK TABLE `t1`, `t2`"}, - {"admin check index tableName idxName;", true, "ADMIN CHECK INDEX `tableName` idxName"}, - {"admin check index tableName idxName (1, 2), (4, 5);", true, "ADMIN CHECK INDEX `tableName` idxName (1,2), (4,5)"}, - {"admin checksum table t1, t2;", true, "ADMIN CHECKSUM TABLE `t1`, `t2`"}, - {"admin cancel ddl jobs 1", true, "ADMIN CANCEL DDL JOBS 1"}, - {"admin cancel ddl jobs 1, 2", true, "ADMIN CANCEL DDL JOBS 1, 2"}, - {"admin pause ddl jobs 1, 3", true, "ADMIN PAUSE DDL JOBS 1, 3"}, - {"admin pause ddl jobs 5", true, "ADMIN PAUSE DDL JOBS 5"}, - {"admin pause ddl jobs", false, "ADMIN PAUSE DDL JOBS"}, - {"admin pause ddl jobs str_not_num", false, "ADMIN PAUSE DDL JOBS str_not_num"}, - {"admin resume ddl jobs 1, 2", true, "ADMIN RESUME DDL JOBS 1, 2"}, - {"admin resume ddl jobs 3", true, "ADMIN RESUME DDL JOBS 3"}, - {"admin resume ddl jobs", false, "ADMIN RESUME DDL JOBS"}, - {"admin resume ddl jobs str_not_num", false, "ADMIN RESUME DDL JOBS str_not_num"}, - {"admin recover index t1 idx_a", true, "ADMIN RECOVER INDEX `t1` idx_a"}, - {"admin cleanup index t1 idx_a", true, "ADMIN CLEANUP INDEX `t1` idx_a"}, - {"admin show slow top 3", true, "ADMIN SHOW SLOW TOP 3"}, - {"admin show slow top internal 7", true, "ADMIN SHOW SLOW TOP INTERNAL 7"}, - {"admin show slow top all 9", true, "ADMIN SHOW SLOW TOP ALL 9"}, - {"admin show slow recent 11", true, "ADMIN SHOW SLOW RECENT 11"}, - {"admin reload expr_pushdown_blacklist", true, "ADMIN RELOAD EXPR_PUSHDOWN_BLACKLIST"}, - {"admin plugins disable audit, whitelist", true, "ADMIN PLUGINS DISABLE audit, whitelist"}, - {"admin plugins enable audit, whitelist", true, "ADMIN PLUGINS ENABLE audit, whitelist"}, - {"admin flush bindings", true, "ADMIN FLUSH BINDINGS"}, - {"admin capture bindings", true, "ADMIN CAPTURE BINDINGS"}, - {"admin evolve bindings", true, "ADMIN EVOLVE BINDINGS"}, - {"admin reload bindings", true, "ADMIN RELOAD BINDINGS"}, - {"admin show telemetry", true, "ADMIN SHOW TELEMETRY"}, - {"admin reset telemetry_id", true, "ADMIN RESET TELEMETRY_ID"}, - // This case would be removed once TiDB PR to remove ADMIN RELOAD STATISTICS is merged. - {"admin reload statistics", true, "ADMIN RELOAD STATS_EXTENDED"}, - {"admin reload stats_extended", true, "ADMIN RELOAD STATS_EXTENDED"}, - // Test for 'admin flush plan_cache' - {"admin flush instance plan_cache", true, "ADMIN FLUSH INSTANCE PLAN_CACHE"}, - {"admin flush session plan_cache", true, "ADMIN FLUSH SESSION PLAN_CACHE"}, - // We do not support the global level. We will check it in the later. - {"admin flush global plan_cache", true, "ADMIN FLUSH GLOBAL PLAN_CACHE"}, - - // for on duplicate key update - {"INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);", true, "INSERT INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, - {"INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c:=VALUES(a)+VALUES(b);", true, "INSERT INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, - {"INSERT IGNORE INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);", true, "INSERT IGNORE INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, - {"INSERT IGNORE INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c:=VALUES(a)+VALUES(b);", true, "INSERT IGNORE INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, - - // for insert ... set - {"INSERT INTO t SET a=1,b=2", true, "INSERT INTO `t` SET `a`=1,`b`=2"}, - {"INSERT INTO t (a) SET a=1", false, ""}, - - // for update statement - {"UPDATE LOW_PRIORITY IGNORE t SET id = id + 1 ORDER BY id DESC;", true, "UPDATE LOW_PRIORITY IGNORE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, - {"UPDATE t SET id = id + 1 ORDER BY id DESC;", true, "UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, - {"UPDATE t SET id = id + 1 ORDER BY id DESC limit 3 ;", true, "UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC LIMIT 3"}, - {"UPDATE t SET id = id + 1, name = 'jojo';", true, "UPDATE `t` SET `id`=`id`+1, `name`=_UTF8MB4'jojo'"}, - {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id;", true, "UPDATE (`items`) JOIN `month` SET `items`.`price`=`month`.`price` WHERE `items`.`id`=`month`.`id`"}, - {"UPDATE user T0 LEFT OUTER JOIN user_profile T1 ON T1.id = T0.profile_id SET T0.profile_id = 1 WHERE T0.profile_id IN (1);", true, "UPDATE `user` AS `T0` LEFT JOIN `user_profile` AS `T1` ON `T1`.`id`=`T0`.`profile_id` SET `T0`.`profile_id`=1 WHERE `T0`.`profile_id` IN (1)"}, - {"UPDATE t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, - // for optimizer hint in update statement - {"UPDATE /*+ TiDB_INLJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_INLJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, - {"UPDATE /*+ TiDB_SMJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_SMJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, - {"UPDATE /*+ TiDB_HJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_HJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, - // fail case for update statement - {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id LIMIT 10;", false, ""}, - {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id order by month.id;", false, ""}, - // for "USE INDEX" in delete statement - {"UPDATE t1 USE INDEX(idx_a) SET t1.price=3.25 WHERE t1.id=1;", true, "UPDATE `t1` USE INDEX (`idx_a`) SET `t1`.`price`=3.25 WHERE `t1`.`id`=1"}, - {"UPDATE t1 USE INDEX(idx_a) JOIN t2 SET t1.price=t2.price WHERE t1.id=t2.id;", true, "UPDATE `t1` USE INDEX (`idx_a`) JOIN `t2` SET `t1`.`price`=`t2`.`price` WHERE `t1`.`id`=`t2`.`id`"}, - {"UPDATE t1 USE INDEX(idx_a) JOIN t2 USE INDEX(idx_a) SET t1.price=t2.price WHERE t1.id=t2.id;", true, "UPDATE `t1` USE INDEX (`idx_a`) JOIN `t2` USE INDEX (`idx_a`) SET `t1`.`price`=`t2`.`price` WHERE `t1`.`id`=`t2`.`id`"}, - - // for select with where clause - {"SELECT * FROM t WHERE 1 = 1", true, "SELECT * FROM `t` WHERE 1=1"}, - - // for select with FETCH FIRST syntax - {"SELECT * FROM t FETCH FIRST 5 ROW ONLY", true, "SELECT * FROM `t` LIMIT 5"}, - {"SELECT * FROM t FETCH NEXT 5 ROW ONLY", true, "SELECT * FROM `t` LIMIT 5"}, - {"SELECT * FROM t FETCH FIRST 5 ROWS ONLY", true, "SELECT * FROM `t` LIMIT 5"}, - {"SELECT * FROM t FETCH NEXT 5 ROWS ONLY", true, "SELECT * FROM `t` LIMIT 5"}, - {"SELECT * FROM t FETCH FIRST ROW ONLY", true, "SELECT * FROM `t` LIMIT 1"}, - {"SELECT * FROM t FETCH NEXT ROW ONLY", true, "SELECT * FROM `t` LIMIT 1"}, - - // for dual - {"select 1 from dual", true, "SELECT 1"}, - {"select 1 from dual limit 1", true, "SELECT 1 LIMIT 1"}, - {"select 1 where exists (select 2)", true, "SELECT 1 FROM DUAL WHERE EXISTS (SELECT 2)"}, - {"select 1 from dual where not exists (select 2)", true, "SELECT 1 FROM DUAL WHERE NOT EXISTS (SELECT 2)"}, - {"select 1 as a from dual order by a", true, "SELECT 1 AS `a` ORDER BY `a`"}, - {"select 1 as a from dual where 1 < any (select 2) order by a", true, "SELECT 1 AS `a` FROM DUAL WHERE 1 now() - interval 10 hour", true, "SHOW BACKUPS WHERE `start_time`>DATE_SUB(NOW(), INTERVAL 10 HOUR)"}, - {"show backup", false, ""}, - {"show restore", false, ""}, - - // for load stats - {"load stats '/tmp/stats.json'", true, "LOAD STATS '/tmp/stats.json'"}, - // for lock stats - {"lock stats test.t", true, "LOCK STATS `test`.`t`"}, - {"lock stats t, t2", true, "LOCK STATS `t`, `t2`"}, - {"lock stats t partition (p0, p1)", true, "LOCK STATS `t` PARTITION(`p0`, `p1`)"}, - // for unlock stats - {"unlock stats test.t", true, "UNLOCK STATS `test`.`t`"}, - {"unlock stats t, t2", true, "UNLOCK STATS `t`, `t2`"}, - {"unlock stats t partition (p0, p1)", true, "UNLOCK STATS `t` PARTITION(`p0`, `p1`)"}, - // set - // user defined - {"SET @ = 1", true, "SET @``=1"}, - {"SET @' ' = 1", true, "SET @` `=1"}, - {"SET @! = 1", false, ""}, - {"SET @1 = 1", true, "SET @`1`=1"}, - {"SET @a = 1", true, "SET @`a`=1"}, - {"SET @b := 1", true, "SET @`b`=1"}, - {"SET @.c = 1", true, "SET @`.c`=1"}, - {"SET @_d = 1", true, "SET @`_d`=1"}, - {"SET @_e._$. = 1", true, "SET @`_e._$.`=1"}, - {"SET @~f = 1", false, ""}, - {"SET @`g,` = 1", true, "SET @`g,`=1"}, - {"SET", false, ""}, - {"SET @a = 1, @b := 2", true, "SET @`a`=1, @`b`=2"}, - // session system variables - {"SET SESSION autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@session.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@SESSION.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@GLOBAL.GTID_PURGED = '123'", true, "SET @@GLOBAL.`gtid_purged`=_UTF8MB4'123'"}, - {"SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN", true, "SET @`MYSQLDUMP_TEMP_LOG_BIN`=@@SESSION.`sql_log_bin`"}, - {"SET LOCAL autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@local.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, - // global system variables - {"SET GLOBAL autocommit = 1", true, "SET @@GLOBAL.`autocommit`=1"}, - {"SET @@global.autocommit = 1", true, "SET @@GLOBAL.`autocommit`=1"}, - // set through mysql extension assignment syntax - {"SET autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@session.autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @MYSQLDUMP_TEMP_LOG_BIN := @@SESSION.SQL_LOG_BIN", true, "SET @`MYSQLDUMP_TEMP_LOG_BIN`=@@SESSION.`sql_log_bin`"}, - {"SET LOCAL autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, - {"SET @@global.autocommit := default", true, "SET @@GLOBAL.`autocommit`=DEFAULT"}, - // set default value - {"SET @@global.autocommit = default", true, "SET @@GLOBAL.`autocommit`=DEFAULT"}, - {"SET @@session.autocommit = default", true, "SET @@SESSION.`autocommit`=DEFAULT"}, - // set binary value - {"SET @@character_set_results = binary", true, "SET @@SESSION.`character_set_results`=_UTF8MB4'BINARY'"}, - // SET CHARACTER SET - {"SET CHARACTER SET utf8mb4;", true, "SET CHARSET 'utf8mb4'"}, - {"SET CHARACTER SET 'utf8mb4';", true, "SET CHARSET 'utf8mb4'"}, - // set password - {"SET PASSWORD = 'password';", true, "SET PASSWORD='password'"}, - {"SET PASSWORD FOR 'root'@'localhost' = 'password';", true, "SET PASSWORD FOR `root`@`localhost`='password'"}, - // SET TRANSACTION Syntax - {"SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'REPEATABLE-READ'"}, - {"SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@GLOBAL.`tx_isolation`=_UTF8MB4'REPEATABLE-READ'"}, - {"SET SESSION TRANSACTION READ WRITE", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'0'"}, - {"SET SESSION TRANSACTION READ ONLY", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'1'"}, - {"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'READ-COMMITTED'"}, - {"SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'READ-UNCOMMITTED'"}, - {"SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'SERIALIZABLE'"}, - {"SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'REPEATABLE-READ'"}, - {"SET TRANSACTION READ WRITE", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'0'"}, - {"SET TRANSACTION READ ONLY", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'1'"}, - {"SET TRANSACTION ISOLATION LEVEL READ COMMITTED", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'READ-COMMITTED'"}, - {"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'READ-UNCOMMITTED'"}, - {"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'SERIALIZABLE'"}, - // for set names - {"set names utf8", true, "SET NAMES 'utf8'"}, - {"set names utf8 collate utf8_unicode_ci", true, "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'"}, - {"set names binary", true, "SET NAMES 'binary'"}, - - // for set character set | name default - {"set names default", true, "SET NAMES DEFAULT"}, - {"set character set default", true, "SET CHARSET DEFAULT"}, - {"set charset default", true, "SET CHARSET DEFAULT"}, - {"set char set default", true, "SET CHARSET DEFAULT"}, - - {"set role `role1`", true, "SET ROLE `role1`@`%`"}, - {"SET ROLE DEFAULT", true, "SET ROLE DEFAULT"}, - {"SET ROLE ALL", true, "SET ROLE ALL"}, - {"SET ROLE ALL EXCEPT `role1`, `role2`", true, "SET ROLE ALL EXCEPT `role1`@`%`, `role2`@`%`"}, - {"SET DEFAULT ROLE administrator, developer TO `joe`@`10.0.0.1`", true, "SET DEFAULT ROLE `administrator`@`%`, `developer`@`%` TO `joe`@`10.0.0.1`"}, - // for set names and set vars - {"set names utf8, @@session.sql_mode=1;", true, "SET NAMES 'utf8', @@SESSION.`sql_mode`=1"}, - {"set @@session.sql_mode=1, names utf8, charset utf8;", true, "SET @@SESSION.`sql_mode`=1, NAMES 'utf8', CHARSET 'utf8'"}, - - // for set/show config - {"set config TIKV LOG.LEVEL='info'", true, "SET CONFIG TIKV LOG.LEVEL = _UTF8MB4'info'"}, - {"set config PD LOG.LEVEL='info'", true, "SET CONFIG PD LOG.LEVEL = _UTF8MB4'info'"}, - {"set config TIDB LOG.LEVEL='info'", true, "SET CONFIG TIDB LOG.LEVEL = _UTF8MB4'info'"}, - {"set config '127.0.0.1:3306' LOG.LEVEL='info'", true, "SET CONFIG '127.0.0.1:3306' LOG.LEVEL = _UTF8MB4'info'"}, - {"set config '127.0.0.1:3306' AUTO-COMPACTION-MODE=TRUE", true, "SET CONFIG '127.0.0.1:3306' AUTO-COMPACTION-MODE = TRUE"}, - {"set config '127.0.0.1:3306' LABEL-PROPERTY.REJECT-LEADER.KEY='zone'", true, "SET CONFIG '127.0.0.1:3306' LABEL-PROPERTY.REJECT-LEADER.KEY = _UTF8MB4'zone'"}, - {"show config", true, "SHOW CONFIG"}, - {"show config where type='tidb'", true, "SHOW CONFIG WHERE `type`=_UTF8MB4'tidb'"}, - {"show config where instance='127.0.0.1:3306'", true, "SHOW CONFIG WHERE `instance`=_UTF8MB4'127.0.0.1:3306'"}, - {"create table CONFIG (a int)", true, "CREATE TABLE `CONFIG` (`a` INT)"}, // check that `CONFIG` is unreserved keyword - - // for FLUSH statement - {"flush no_write_to_binlog tables tbl1 with read lock", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1` WITH READ LOCK"}, - {"flush table", true, "FLUSH TABLES"}, - {"flush tables", true, "FLUSH TABLES"}, - {"flush tables tbl1", true, "FLUSH TABLES `tbl1`"}, - {"flush no_write_to_binlog tables tbl1", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1`"}, - {"flush local tables tbl1", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1`"}, - {"flush table with read lock", true, "FLUSH TABLES WITH READ LOCK"}, - {"flush tables tbl1, tbl2, tbl3", true, "FLUSH TABLES `tbl1`, `tbl2`, `tbl3`"}, - {"flush tables tbl1, tbl2, tbl3 with read lock", true, "FLUSH TABLES `tbl1`, `tbl2`, `tbl3` WITH READ LOCK"}, - {"flush privileges", true, "FLUSH PRIVILEGES"}, - {"flush status", true, "FLUSH STATUS"}, - {"flush tidb plugins plugin1", true, "FLUSH TIDB PLUGINS plugin1"}, - {"flush tidb plugins plugin1, plugin2", true, "FLUSH TIDB PLUGINS plugin1, plugin2"}, - {"flush hosts", true, "FLUSH HOSTS"}, - {"flush logs", true, "FLUSH LOGS"}, - {"flush binary logs", true, "FLUSH BINARY LOGS"}, - {"flush engine logs", true, "FLUSH ENGINE LOGS"}, - {"flush error logs", true, "FLUSH ERROR LOGS"}, - {"flush general logs", true, "FLUSH GENERAL LOGS"}, - {"flush slow logs", true, "FLUSH SLOW LOGS"}, - {"flush client_errors_summary", true, "FLUSH CLIENT_ERRORS_SUMMARY"}, - - // for change statement - {"change pump to node_state ='paused' for node_id '127.0.0.1:8250'", true, "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:8250'"}, - {"change drainer to node_state ='paused' for node_id '127.0.0.1:8249'", true, "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:8249'"}, - - // for call statement - {"call ", false, ""}, - {"call test", true, "CALL `test`()"}, - {"call test()", true, "CALL `test`()"}, - {"call test(1, 'test', true)", true, "CALL `test`(1, _UTF8MB4'test', TRUE)"}, - {"call x.y;", true, "CALL `x`.`y`()"}, - {"call x.y();", true, "CALL `x`.`y`()"}, - {"call x.y('p', 'q', 'r');", true, "CALL `x`.`y`(_UTF8MB4'p', _UTF8MB4'q', _UTF8MB4'r')"}, - {"call `x`.`y`;", true, "CALL `x`.`y`()"}, - {"call `x`.`y`();", true, "CALL `x`.`y`()"}, - {"call `x`.`y`('p', 'q', 'r');", true, "CALL `x`.`y`(_UTF8MB4'p', _UTF8MB4'q', _UTF8MB4'r')"}, - } - RunTest(t, table, false) -} - -func TestSetVariable(t *testing.T) { - table := []struct { - Input string - Name string - IsGlobal bool - IsSystem bool - }{ - - // Set system variable xx.xx, although xx.xx isn't a system variable, the parser should accept it. - {"set xx.xx = 666", "xx.xx", false, true}, - // Set session system variable xx.xx - {"set session xx.xx = 666", "xx.xx", false, true}, - {"set local xx.xx = 666", "xx.xx", false, true}, - {"set global xx.xx = 666", "xx.xx", true, true}, - - {"set @@xx.xx = 666", "xx.xx", false, true}, - {"set @@session.xx.xx = 666", "xx.xx", false, true}, - {"set @@local.xx.xx = 666", "xx.xx", false, true}, - {"set @@global.xx.xx = 666", "xx.xx", true, true}, - - // Set user defined variable xx.xx - {"set @xx.xx = 666", "xx.xx", false, false}, - } - - p := parser.New() - for _, tbl := range table { - stmt, err := p.ParseOneStmt(tbl.Input, "", "") - require.NoError(t, err) - - setStmt, ok := stmt.(*ast.SetStmt) - require.True(t, ok) - require.Len(t, setStmt.Variables, 1) - - v := setStmt.Variables[0] - require.Equal(t, tbl.Name, v.Name) - require.Equal(t, tbl.IsGlobal, v.IsGlobal) - require.Equal(t, tbl.IsSystem, v.IsSystem) - } - - _, err := p.ParseOneStmt("set xx.xx.xx = 666", "", "") - require.Error(t, err) -} - -func TestFlushTable(t *testing.T) { - p := parser.New() - stmt, _, err := p.Parse("flush local tables tbl1,tbl2 with read lock", "", "") - require.NoError(t, err) - flushTable := stmt[0].(*ast.FlushStmt) - require.Equal(t, ast.FlushTables, flushTable.Tp) - require.Equal(t, "tbl1", flushTable.Tables[0].Name.L) - require.Equal(t, "tbl2", flushTable.Tables[1].Name.L) - require.True(t, flushTable.NoWriteToBinLog) - require.True(t, flushTable.ReadLock) -} - -func TestFlushPrivileges(t *testing.T) { - p := parser.New() - stmt, _, err := p.Parse("flush privileges", "", "") - require.NoError(t, err) - flushPrivilege := stmt[0].(*ast.FlushStmt) - require.Equal(t, ast.FlushPrivileges, flushPrivilege.Tp) -} - -func TestExpression(t *testing.T) { - table := []testCase{ - // sign expression - {"SELECT ++1", true, "SELECT ++1"}, - {"SELECT -*1", false, "SELECT -*1"}, - {"SELECT -+1", true, "SELECT -+1"}, - {"SELECT -1", true, "SELECT -1"}, - {"SELECT --1", true, "SELECT --1"}, - - // for string literal - {`select '''a''', """a"""`, true, "SELECT _UTF8MB4'''a''',_UTF8MB4'\"a\"'"}, - {`select ''a''`, false, ""}, - {`select ""a""`, false, ""}, - {`select '''a''';`, true, "SELECT _UTF8MB4'''a'''"}, - {`select '\'a\'';`, true, "SELECT _UTF8MB4'''a'''"}, - {`select "\"a\"";`, true, "SELECT _UTF8MB4'\"a\"'"}, - {`select """a""";`, true, "SELECT _UTF8MB4'\"a\"'"}, - {`select _utf8"string";`, true, "SELECT _UTF8'string'"}, - {`select _binary"string";`, true, "SELECT _BINARY'string'"}, - {"select N'string'", true, "SELECT _UTF8'string'"}, - {"select n'string'", true, "SELECT _UTF8'string'"}, - {"select _utf8 0xD0B1;", true, "SELECT _UTF8 x'd0b1'"}, - {"select _utf8 X'D0B1';", true, "SELECT _UTF8 x'd0b1'"}, - {"select _utf8 0b1101000010110001;", true, "SELECT _UTF8 b'1101000010110001'"}, - {"select _utf8 B'1101000010110001';", true, "SELECT _UTF8 b'1101000010110001'"}, - // for comparison - {"select 1 <=> 0, 1 <=> null, 1 = null", true, "SELECT 1<=>0,1<=>NULL,1=NULL"}, - // for date literal - {"select date'1989-09-10'", true, "SELECT DATE '1989-09-10'"}, - {"select date 19890910", false, ""}, - // for time literal - {"select time '00:00:00.111'", true, "SELECT TIME '00:00:00.111'"}, - {"select time 19890910", false, ""}, - // for timestamp literal - {"select timestamp '1989-09-10 11:11:11'", true, "SELECT TIMESTAMP '1989-09-10 11:11:11'"}, - {"select timestamp 19890910", false, ""}, - - // The ODBC syntax for time/date/timestamp literal. - // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html - {"select {ts '1989-09-10 11:11:11'}", true, "SELECT TIMESTAMP '1989-09-10 11:11:11'"}, - {"select {d '1989-09-10'}", true, "SELECT DATE '1989-09-10'"}, - {"select {t '00:00:00.111'}", true, "SELECT TIME '00:00:00.111'"}, - {"select * from t where a > {ts '1989-09-10 11:11:11'}", true, "SELECT * FROM `t` WHERE `a`>TIMESTAMP '1989-09-10 11:11:11'"}, - {"select * from t where a > {ts {abc '1989-09-10 11:11:11'}}", true, "SELECT * FROM `t` WHERE `a`>TIMESTAMP '1989-09-10 11:11:11'"}, - // If the identifier is not in (t, d, ts), we just ignore it and consider the following expression as the value. - // See: https://dev.mysql.com/doc/refman/5.7/en/expressions.html - {"select {ts123 '1989-09-10 11:11:11'}", true, "SELECT _UTF8MB4'1989-09-10 11:11:11'"}, - {"select {ts123 123}", true, "SELECT 123"}, - {"select {ts123 1 xor 1}", true, "SELECT 1 XOR 1"}, - {"select * from t where a > {ts123 '1989-09-10 11:11:11'}", true, "SELECT * FROM `t` WHERE `a`>_UTF8MB4'1989-09-10 11:11:11'"}, - {"select .t.a from t", false, ""}, - } - - RunTest(t, table, false) -} - -func TestBuiltin(t *testing.T) { - table := []testCase{ - // for builtin functions - {"SELECT POW(1, 2)", true, "SELECT POW(1, 2)"}, - {"SELECT POW(1, 2, 1)", true, "SELECT POW(1, 2, 1)"}, // illegal number of arguments shall pass too - {"SELECT POW(1, 0.5)", true, "SELECT POW(1, 0.5)"}, - {"SELECT POW(1, -1)", true, "SELECT POW(1, -1)"}, - {"SELECT POW(-1, 1)", true, "SELECT POW(-1, 1)"}, - {"SELECT RAND();", true, "SELECT RAND()"}, - {"SELECT RAND(1);", true, "SELECT RAND(1)"}, - {"SELECT MOD(10, 2);", true, "SELECT 10%2"}, - {"SELECT ROUND(-1.23);", true, "SELECT ROUND(-1.23)"}, - {"SELECT ROUND(1.23, 1);", true, "SELECT ROUND(1.23, 1)"}, - {"SELECT ROUND(1.23, 1, 1);", true, "SELECT ROUND(1.23, 1, 1)"}, - {"SELECT CEIL(-1.23);", true, "SELECT CEIL(-1.23)"}, - {"SELECT CEILING(1.23);", true, "SELECT CEILING(1.23)"}, - {"SELECT FLOOR(-1.23);", true, "SELECT FLOOR(-1.23)"}, - {"SELECT LN(1);", true, "SELECT LN(1)"}, - {"SELECT LN(1, 2);", true, "SELECT LN(1, 2)"}, - {"SELECT LOG(-2);", true, "SELECT LOG(-2)"}, - {"SELECT LOG(2, 65536);", true, "SELECT LOG(2, 65536)"}, - {"SELECT LOG(2, 65536, 1);", true, "SELECT LOG(2, 65536, 1)"}, - {"SELECT LOG2(2);", true, "SELECT LOG2(2)"}, - {"SELECT LOG2(2, 2);", true, "SELECT LOG2(2, 2)"}, - {"SELECT LOG10(10);", true, "SELECT LOG10(10)"}, - {"SELECT LOG10(10, 1);", true, "SELECT LOG10(10, 1)"}, - {"SELECT ABS(10, 1);", true, "SELECT ABS(10, 1)"}, - {"SELECT ABS(10);", true, "SELECT ABS(10)"}, - {"SELECT ABS();", true, "SELECT ABS()"}, - {"SELECT CONV(10+'10'+'10'+X'0a',10,10);", true, "SELECT CONV(10+_UTF8MB4'10'+_UTF8MB4'10'+x'0a', 10, 10)"}, - {"SELECT CONV();", true, "SELECT CONV()"}, - {"SELECT CRC32('MySQL');", true, "SELECT CRC32(_UTF8MB4'MySQL')"}, - {"SELECT CRC32();", true, "SELECT CRC32()"}, - {"SELECT SIGN();", true, "SELECT SIGN()"}, - {"SELECT SIGN(0);", true, "SELECT SIGN(0)"}, - {"SELECT SQRT(0);", true, "SELECT SQRT(0)"}, - {"SELECT SQRT();", true, "SELECT SQRT()"}, - {"SELECT ACOS();", true, "SELECT ACOS()"}, - {"SELECT ACOS(1);", true, "SELECT ACOS(1)"}, - {"SELECT ACOS(1, 2);", true, "SELECT ACOS(1, 2)"}, - {"SELECT ASIN();", true, "SELECT ASIN()"}, - {"SELECT ASIN(1);", true, "SELECT ASIN(1)"}, - {"SELECT ASIN(1, 2);", true, "SELECT ASIN(1, 2)"}, - {"SELECT ATAN(0), ATAN(1), ATAN(1, 2);", true, "SELECT ATAN(0),ATAN(1),ATAN(1, 2)"}, - {"SELECT ATAN2(), ATAN2(1,2);", true, "SELECT ATAN2(),ATAN2(1, 2)"}, - {"SELECT COS(0);", true, "SELECT COS(0)"}, - {"SELECT COS(1);", true, "SELECT COS(1)"}, - {"SELECT COS(1, 2);", true, "SELECT COS(1, 2)"}, - {"SELECT COT();", true, "SELECT COT()"}, - {"SELECT COT(1);", true, "SELECT COT(1)"}, - {"SELECT COT(1, 2);", true, "SELECT COT(1, 2)"}, - {"SELECT DEGREES();", true, "SELECT DEGREES()"}, - {"SELECT DEGREES(0);", true, "SELECT DEGREES(0)"}, - {"SELECT EXP();", true, "SELECT EXP()"}, - {"SELECT EXP(1);", true, "SELECT EXP(1)"}, - {"SELECT PI();", true, "SELECT PI()"}, - {"SELECT PI(1);", true, "SELECT PI(1)"}, - {"SELECT RADIANS();", true, "SELECT RADIANS()"}, - {"SELECT RADIANS(1);", true, "SELECT RADIANS(1)"}, - {"SELECT SIN();", true, "SELECT SIN()"}, - {"SELECT SIN(1);", true, "SELECT SIN(1)"}, - {"SELECT TAN(1);", true, "SELECT TAN(1)"}, - {"SELECT TAN();", true, "SELECT TAN()"}, - {"SELECT TRUNCATE(1.223,1);", true, "SELECT TRUNCATE(1.223, 1)"}, - {"SELECT TRUNCATE();", true, "SELECT TRUNCATE()"}, - - {"SELECT SUBSTR('Quadratically',5);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5)"}, - {"SELECT SUBSTR('Quadratically',5, 3);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5, 3)"}, - {"SELECT SUBSTR('Quadratically' FROM 5);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5)"}, - {"SELECT SUBSTR('Quadratically' FROM 5 FOR 3);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5, 3)"}, - - {"SELECT SUBSTRING('Quadratically',5);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5)"}, - {"SELECT SUBSTRING('Quadratically',5, 3);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5, 3)"}, - {"SELECT SUBSTRING('Quadratically' FROM 5);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5)"}, - {"SELECT SUBSTRING('Quadratically' FROM 5 FOR 3);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5, 3)"}, - - {"SELECT CONVERT('111', SIGNED);", true, "SELECT CONVERT(_UTF8MB4'111', SIGNED)"}, - - {"SELECT LEAST(), LEAST(1, 2, 3);", true, "SELECT LEAST(),LEAST(1, 2, 3)"}, - - {"SELECT INTERVAL(1, 0, 1, 2)", true, "SELECT INTERVAL(1, 0, 1, 2)"}, - {"SELECT (INTERVAL(1, 0, 1, 2)+5)*7+INTERVAL(1, 0, 1, 2)/2", true, "SELECT (INTERVAL(1, 0, 1, 2)+5)*7+INTERVAL(1, 0, 1, 2)/2"}, - {"SELECT INTERVAL(0, (1*5)/2)+INTERVAL(5, 4, 3)", true, "SELECT INTERVAL(0, (1*5)/2)+INTERVAL(5, 4, 3)"}, - {"SELECT DATE_ADD('2008-01-02', INTERVAL INTERVAL(1, 0, 1) DAY);", true, "SELECT DATE_ADD(_UTF8MB4'2008-01-02', INTERVAL INTERVAL(1, 0, 1) DAY)"}, - - // information functions - {"SELECT DATABASE();", true, "SELECT DATABASE()"}, - {"SELECT SCHEMA();", true, "SELECT SCHEMA()"}, - {"SELECT USER();", true, "SELECT USER()"}, - {"SELECT USER(1);", true, "SELECT USER(1)"}, - {"SELECT CURRENT_USER();", true, "SELECT CURRENT_USER()"}, - {"SELECT CURRENT_ROLE();", true, "SELECT CURRENT_ROLE()"}, - {"SELECT CURRENT_USER;", true, "SELECT CURRENT_USER()"}, - {"SELECT CONNECTION_ID();", true, "SELECT CONNECTION_ID()"}, - {"SELECT VERSION();", true, "SELECT VERSION()"}, - {"SELECT CURRENT_RESOURCE_GROUP();", true, "SELECT CURRENT_RESOURCE_GROUP()"}, - {"SELECT BENCHMARK(1000000, AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));", true, "SELECT BENCHMARK(1000000, AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3')))"}, - {"SELECT BENCHMARK(AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));", true, "SELECT BENCHMARK(AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3')))"}, - {"SELECT CHARSET('abc');", true, "SELECT CHARSET(_UTF8MB4'abc')"}, - {"SELECT COERCIBILITY('abc');", true, "SELECT COERCIBILITY(_UTF8MB4'abc')"}, - {"SELECT COERCIBILITY('abc', 'a');", true, "SELECT COERCIBILITY(_UTF8MB4'abc', _UTF8MB4'a')"}, - {"SELECT COLLATION('abc');", true, "SELECT COLLATION(_UTF8MB4'abc')"}, - {"SELECT ROW_COUNT();", true, "SELECT ROW_COUNT()"}, - {"SELECT SESSION_USER();", true, "SELECT SESSION_USER()"}, - {"SELECT SYSTEM_USER();", true, "SELECT SYSTEM_USER()"}, - {"SELECT FORMAT_BYTES(512);", true, "SELECT FORMAT_BYTES(512)"}, - {"SELECT FORMAT_NANO_TIME(3501);", true, "SELECT FORMAT_NANO_TIME(3501)"}, - - {"SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2);", true, "SELECT SUBSTRING_INDEX(_UTF8MB4'www.mysql.com', _UTF8MB4'.', 2)"}, - {"SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2);", true, "SELECT SUBSTRING_INDEX(_UTF8MB4'www.mysql.com', _UTF8MB4'.', -2)"}, - - {`SELECT ASCII(), ASCII(""), ASCII("A"), ASCII(1);`, true, "SELECT ASCII(),ASCII(_UTF8MB4''),ASCII(_UTF8MB4'A'),ASCII(1)"}, - - {`SELECT LOWER("A"), UPPER("a")`, true, "SELECT LOWER(_UTF8MB4'A'),UPPER(_UTF8MB4'a')"}, - {`SELECT LCASE("A"), UCASE("a")`, true, "SELECT LCASE(_UTF8MB4'A'),UCASE(_UTF8MB4'a')"}, - - {`SELECT REPLACE('www.mysql.com', 'w', 'Ww')`, true, "SELECT REPLACE(_UTF8MB4'www.mysql.com', _UTF8MB4'w', _UTF8MB4'Ww')"}, - - {`SELECT LOCATE('bar', 'foobarbar');`, true, "SELECT LOCATE(_UTF8MB4'bar', _UTF8MB4'foobarbar')"}, - {`SELECT LOCATE('bar', 'foobarbar', 5);`, true, "SELECT LOCATE(_UTF8MB4'bar', _UTF8MB4'foobarbar', 5)"}, - - {`SELECT tidb_version();`, true, "SELECT TIDB_VERSION()"}, - {`SELECT tidb_is_ddl_owner();`, true, "SELECT TIDB_IS_DDL_OWNER()"}, - {`SELECT tidb_decode_plan();`, true, "SELECT TIDB_DECODE_PLAN()"}, - {`SELECT tidb_decode_key('abc');`, true, "SELECT TIDB_DECODE_KEY(_UTF8MB4'abc')"}, - {`SELECT tidb_decode_base64_key('abc');`, true, "SELECT TIDB_DECODE_BASE64_KEY(_UTF8MB4'abc')"}, - {`SELECT tidb_decode_sql_digests('[]');`, true, "SELECT TIDB_DECODE_SQL_DIGESTS(_UTF8MB4'[]')"}, - {`SELECT get_mvcc_info('hex', '0xabc');`, true, "SELECT GET_MVCC_INFO(_UTF8MB4'hex', _UTF8MB4'0xabc')"}, - - // for time fsp - {"CREATE TABLE t( c1 TIME(2), c2 DATETIME(2), c3 TIMESTAMP(2) );", true, "CREATE TABLE `t` (`c1` TIME(2),`c2` DATETIME(2),`c3` TIMESTAMP(2))"}, - - // for row - {"select row(1)", false, ""}, - {"select row(1, 1,)", false, ""}, - {"select (1, 1,)", false, ""}, - {"select row(1, 1) > row(1, 1), row(1, 1, 1) > row(1, 1, 1)", true, "SELECT ROW(1,1)>ROW(1,1),ROW(1,1,1)>ROW(1,1,1)"}, - {"Select (1, 1) > (1, 1)", true, "SELECT ROW(1,1)>ROW(1,1)"}, - {"create table t (`row` int)", true, "CREATE TABLE `t` (`row` INT)"}, - {"create table t (row int)", false, ""}, - - // for cast with charset - {"SELECT *, CAST(data AS CHAR CHARACTER SET utf8) FROM t;", true, "SELECT *,CAST(`data` AS CHAR CHARSET UTF8) FROM `t`"}, - {"SELECT CAST(data AS CHARACTER);", true, "SELECT CAST(`data` AS CHAR)"}, - {"SELECT CAST(data AS CHARACTER(10) CHARACTER SET utf8);", true, "SELECT CAST(`data` AS CHAR(10) CHARSET UTF8)"}, - {"SELECT CAST(data AS BINARY)", true, "SELECT CAST(`data` AS BINARY)"}, - - // for cast as JSON - {"SELECT *, CAST(data AS JSON) FROM t;", true, "SELECT *,CAST(`data` AS JSON) FROM `t`"}, - - // for cast as signed int, fix issue #3691. - {"select cast(1 as signed int);", true, "SELECT CAST(1 AS SIGNED)"}, - - // for cast as double - {"select cast(1 as double);", true, "SELECT CAST(1 AS DOUBLE)"}, - - // for cast as float - {"select cast(1 as float);", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(0));", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(24));", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(25));", true, "SELECT CAST(1 AS DOUBLE)"}, - {"select cast(1 as float(53));", true, "SELECT CAST(1 AS DOUBLE)"}, - {"select cast(1 as float(54));", false, ""}, - - {"select cast(1 as real);", true, "SELECT CAST(1 AS DOUBLE)"}, - {"select cast('2000' as year);", true, "SELECT CAST(_UTF8MB4'2000' AS YEAR)"}, - {"select cast(time '2000' as year);", true, "SELECT CAST(TIME '2000' AS YEAR)"}, - - {"select cast(b as signed array);", true, "SELECT CAST(`b` AS SIGNED ARRAY)"}, - {"select cast(b as char(10) array);", true, "SELECT CAST(`b` AS CHAR(10) ARRAY)"}, - - // for last_insert_id - {"SELECT last_insert_id();", true, "SELECT LAST_INSERT_ID()"}, - {"SELECT last_insert_id(1);", true, "SELECT LAST_INSERT_ID(1)"}, - - // for binary operator - {"SELECT binary 'a';", true, "SELECT BINARY _UTF8MB4'a'"}, - - // for bit_count - {`SELECT BIT_COUNT(1);`, true, "SELECT BIT_COUNT(1)"}, - - // select time - {"select current_timestamp", true, "SELECT CURRENT_TIMESTAMP()"}, - {"select current_timestamp()", true, "SELECT CURRENT_TIMESTAMP()"}, - {"select current_timestamp(6)", true, "SELECT CURRENT_TIMESTAMP(6)"}, - {"select current_timestamp(null)", false, ""}, - {"select current_timestamp(-1)", false, ""}, - {"select current_timestamp(1.0)", false, ""}, - {"select current_timestamp('2')", false, ""}, - {"select now()", true, "SELECT NOW()"}, - {"select now(6)", true, "SELECT NOW(6)"}, - {"select sysdate(), sysdate(6)", true, "SELECT SYSDATE(),SYSDATE(6)"}, - {"SELECT time('01:02:03');", true, "SELECT TIME(_UTF8MB4'01:02:03')"}, - {"SELECT time('01:02:03.1')", true, "SELECT TIME(_UTF8MB4'01:02:03.1')"}, - {"SELECT time('20.1')", true, "SELECT TIME(_UTF8MB4'20.1')"}, - {"SELECT TIMEDIFF('2000:01:01 00:00:00', '2000:01:01 00:00:00.000001');", true, "SELECT TIMEDIFF(_UTF8MB4'2000:01:01 00:00:00', _UTF8MB4'2000:01:01 00:00:00.000001')"}, - {"SELECT TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01');", true, "SELECT TIMESTAMPDIFF(MONTH, _UTF8MB4'2003-02-01', _UTF8MB4'2003-05-01')"}, - {"SELECT TIMESTAMPDIFF(YEAR,'2002-05-01','2001-01-01');", true, "SELECT TIMESTAMPDIFF(YEAR, _UTF8MB4'2002-05-01', _UTF8MB4'2001-01-01')"}, - {"SELECT TIMESTAMPDIFF(MINUTE,'2003-02-01','2003-05-01 12:05:55');", true, "SELECT TIMESTAMPDIFF(MINUTE, _UTF8MB4'2003-02-01', _UTF8MB4'2003-05-01 12:05:55')"}, - - // select current_time - {"select current_time", true, "SELECT CURRENT_TIME()"}, - {"select current_time()", true, "SELECT CURRENT_TIME()"}, - {"select current_time(6)", true, "SELECT CURRENT_TIME(6)"}, - {"select current_time(-1)", false, ""}, - {"select current_time(1.0)", false, ""}, - {"select current_time('1')", false, ""}, - {"select current_time(null)", false, ""}, - {"select curtime()", true, "SELECT CURTIME()"}, - {"select curtime(6)", true, "SELECT CURTIME(6)"}, - {"select curtime(-1)", false, ""}, - {"select curtime(1.0)", false, ""}, - {"select curtime('1')", false, ""}, - {"select curtime(null)", false, ""}, - - // select utc_timestamp - {"select utc_timestamp", true, "SELECT UTC_TIMESTAMP()"}, - {"select utc_timestamp()", true, "SELECT UTC_TIMESTAMP()"}, - {"select utc_timestamp(6)", true, "SELECT UTC_TIMESTAMP(6)"}, - {"select utc_timestamp(-1)", false, ""}, - {"select utc_timestamp(1.0)", false, ""}, - {"select utc_timestamp('1')", false, ""}, - {"select utc_timestamp(null)", false, ""}, - - // select utc_time - {"select utc_time", true, "SELECT UTC_TIME()"}, - {"select utc_time()", true, "SELECT UTC_TIME()"}, - {"select utc_time(6)", true, "SELECT UTC_TIME(6)"}, - {"select utc_time(-1)", false, ""}, - {"select utc_time(1.0)", false, ""}, - {"select utc_time('1')", false, ""}, - {"select utc_time(null)", false, ""}, - - // for microsecond, second, minute, hour - {"SELECT MICROSECOND('2009-12-31 23:59:59.000010');", true, "SELECT MICROSECOND(_UTF8MB4'2009-12-31 23:59:59.000010')"}, - {"SELECT SECOND('10:05:03');", true, "SELECT SECOND(_UTF8MB4'10:05:03')"}, - {"SELECT MINUTE('2008-02-03 10:05:03');", true, "SELECT MINUTE(_UTF8MB4'2008-02-03 10:05:03')"}, - {"SELECT HOUR(), HOUR('10:05:03');", true, "SELECT HOUR(),HOUR(_UTF8MB4'10:05:03')"}, - - // for date, day, weekday - {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE()", true, "SELECT CURRENT_DATE(),CURRENT_DATE(),CURDATE()"}, - {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE(1)", false, ""}, - {"SELECT DATEDIFF('2003-12-31', '2003-12-30');", true, "SELECT DATEDIFF(_UTF8MB4'2003-12-31', _UTF8MB4'2003-12-30')"}, - {"SELECT DATE('2003-12-31 01:02:03');", true, "SELECT DATE(_UTF8MB4'2003-12-31 01:02:03')"}, - {"SELECT DATE();", true, "SELECT DATE()"}, - {"SELECT DATE('2003-12-31 01:02:03', '');", true, "SELECT DATE(_UTF8MB4'2003-12-31 01:02:03', _UTF8MB4'')"}, - {`SELECT DATE_FORMAT('2003-12-31 01:02:03', '%W %M %Y');`, true, "SELECT DATE_FORMAT(_UTF8MB4'2003-12-31 01:02:03', _UTF8MB4'%W %M %Y')"}, - {"SELECT DAY('2007-02-03');", true, "SELECT DAY(_UTF8MB4'2007-02-03')"}, - {"SELECT DAYOFMONTH('2007-02-03');", true, "SELECT DAYOFMONTH(_UTF8MB4'2007-02-03')"}, - {"SELECT DAYOFWEEK('2007-02-03');", true, "SELECT DAYOFWEEK(_UTF8MB4'2007-02-03')"}, - {"SELECT DAYOFYEAR('2007-02-03');", true, "SELECT DAYOFYEAR(_UTF8MB4'2007-02-03')"}, - {"SELECT DAYNAME('2007-02-03');", true, "SELECT DAYNAME(_UTF8MB4'2007-02-03')"}, - {"SELECT FROM_DAYS(1423);", true, "SELECT FROM_DAYS(1423)"}, - {"SELECT WEEKDAY('2007-02-03');", true, "SELECT WEEKDAY(_UTF8MB4'2007-02-03')"}, - - // for utc_date - {"SELECT UTC_DATE, UTC_DATE();", true, "SELECT UTC_DATE(),UTC_DATE()"}, - {"SELECT UTC_DATE(), UTC_DATE()+0", true, "SELECT UTC_DATE(),UTC_DATE()+0"}, - - // for week, month, year - {"SELECT WEEK();", true, "SELECT WEEK()"}, - {"SELECT WEEK('2007-02-03');", true, "SELECT WEEK(_UTF8MB4'2007-02-03')"}, - {"SELECT WEEK('2007-02-03', 0);", true, "SELECT WEEK(_UTF8MB4'2007-02-03', 0)"}, - {"SELECT WEEKOFYEAR('2007-02-03');", true, "SELECT WEEKOFYEAR(_UTF8MB4'2007-02-03')"}, - {"SELECT MONTH('2007-02-03');", true, "SELECT MONTH(_UTF8MB4'2007-02-03')"}, - {"SELECT MONTHNAME('2007-02-03');", true, "SELECT MONTHNAME(_UTF8MB4'2007-02-03')"}, - {"SELECT YEAR('2007-02-03');", true, "SELECT YEAR(_UTF8MB4'2007-02-03')"}, - {"SELECT YEARWEEK('2007-02-03');", true, "SELECT YEARWEEK(_UTF8MB4'2007-02-03')"}, - {"SELECT YEARWEEK('2007-02-03', 0);", true, "SELECT YEARWEEK(_UTF8MB4'2007-02-03', 0)"}, - - // for ADDTIME, SUBTIME - {"SELECT ADDTIME('01:00:00.999999', '02:00:00.999998');", true, "SELECT ADDTIME(_UTF8MB4'01:00:00.999999', _UTF8MB4'02:00:00.999998')"}, - {"SELECT ADDTIME('02:00:00.999998');", true, "SELECT ADDTIME(_UTF8MB4'02:00:00.999998')"}, - {"SELECT ADDTIME();", true, "SELECT ADDTIME()"}, - {"SELECT SUBTIME('01:00:00.999999', '02:00:00.999998');", true, "SELECT SUBTIME(_UTF8MB4'01:00:00.999999', _UTF8MB4'02:00:00.999998')"}, - - // for CONVERT_TZ - {"SELECT CONVERT_TZ();", true, "SELECT CONVERT_TZ()"}, - {"SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');", true, "SELECT CONVERT_TZ(_UTF8MB4'2004-01-01 12:00:00', _UTF8MB4'+00:00', _UTF8MB4'+10:00')"}, - {"SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00', '+10:00');", true, "SELECT CONVERT_TZ(_UTF8MB4'2004-01-01 12:00:00', _UTF8MB4'+00:00', _UTF8MB4'+10:00', _UTF8MB4'+10:00')"}, - - // for GET_FORMAT - {"SELECT GET_FORMAT(DATE, 'USA');", true, "SELECT GET_FORMAT(DATE, _UTF8MB4'USA')"}, - {"SELECT GET_FORMAT(DATETIME, 'USA');", true, "SELECT GET_FORMAT(DATETIME, _UTF8MB4'USA')"}, - {"SELECT GET_FORMAT(TIME, 'USA');", true, "SELECT GET_FORMAT(TIME, _UTF8MB4'USA')"}, - {"SELECT GET_FORMAT(TIMESTAMP, 'USA');", true, "SELECT GET_FORMAT(DATETIME, _UTF8MB4'USA')"}, - - // for LOCALTIME, LOCALTIMESTAMP - {"SELECT LOCALTIME(), LOCALTIME(1)", true, "SELECT LOCALTIME(),LOCALTIME(1)"}, - {"SELECT LOCALTIMESTAMP(), LOCALTIMESTAMP(2)", true, "SELECT LOCALTIMESTAMP(),LOCALTIMESTAMP(2)"}, - - // for MAKEDATE, MAKETIME - {"SELECT MAKEDATE(2011,31);", true, "SELECT MAKEDATE(2011, 31)"}, - {"SELECT MAKETIME(12,15,30);", true, "SELECT MAKETIME(12, 15, 30)"}, - {"SELECT MAKEDATE();", true, "SELECT MAKEDATE()"}, - {"SELECT MAKETIME();", true, "SELECT MAKETIME()"}, - - // for PERIOD_ADD, PERIOD_DIFF - {"SELECT PERIOD_ADD(200801,2)", true, "SELECT PERIOD_ADD(200801, 2)"}, - {"SELECT PERIOD_DIFF(200802,200703)", true, "SELECT PERIOD_DIFF(200802, 200703)"}, - - // for QUARTER - {"SELECT QUARTER('2008-04-01');", true, "SELECT QUARTER(_UTF8MB4'2008-04-01')"}, - - // for SEC_TO_TIME - {"SELECT SEC_TO_TIME(2378)", true, "SELECT SEC_TO_TIME(2378)"}, - - // for TIME_FORMAT - {`SELECT TIME_FORMAT('100:00:00', '%H %k %h %I %l')`, true, "SELECT TIME_FORMAT(_UTF8MB4'100:00:00', _UTF8MB4'%H %k %h %I %l')"}, - - // for TIME_TO_SEC - {"SELECT TIME_TO_SEC('22:23:00')", true, "SELECT TIME_TO_SEC(_UTF8MB4'22:23:00')"}, - - // for TIMESTAMPADD - {"SELECT TIMESTAMPADD(WEEK,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(WEEK, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_SECOND,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(SECOND, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_MINUTE,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MINUTE, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_HOUR,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(HOUR, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_DAY,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(DAY, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_WEEK,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(WEEK, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_MONTH,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MONTH, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_QUARTER,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(QUARTER, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_YEAR,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(YEAR, 1, _UTF8MB4'2003-01-02')"}, - {"SELECT TIMESTAMPADD(SQL_TSI_MICROSECOND,1,'2003-01-02');", false, ""}, - {"SELECT TIMESTAMPADD(MICROSECOND,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MICROSECOND, 1, _UTF8MB4'2003-01-02')"}, - - // for TO_DAYS, TO_SECONDS - {"SELECT TO_DAYS('2007-10-07')", true, "SELECT TO_DAYS(_UTF8MB4'2007-10-07')"}, - {"SELECT TO_SECONDS('2009-11-29')", true, "SELECT TO_SECONDS(_UTF8MB4'2009-11-29')"}, - - // for LAST_DAY - {"SELECT LAST_DAY('2003-02-05');", true, "SELECT LAST_DAY(_UTF8MB4'2003-02-05')"}, - - // for UTC_TIME - {"SELECT UTC_TIME(), UTC_TIME(1)", true, "SELECT UTC_TIME(),UTC_TIME(1)"}, - - // for time extract - {`select extract(microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(hour from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(day from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(week from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(WEEK FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(month from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MONTH FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(quarter from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(QUARTER FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(year from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(YEAR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(second_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(SECOND_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(minute_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(minute_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(hour_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(hour_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(hour_minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(day_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(day_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(day_minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(day_hour from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_HOUR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - {`select extract(year_month from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(YEAR_MONTH FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, - - // for from_unixtime - {`select from_unixtime(1447430881)`, true, "SELECT FROM_UNIXTIME(1447430881)"}, - {`select from_unixtime(1447430881.123456)`, true, "SELECT FROM_UNIXTIME(1447430881.123456)"}, - {`select from_unixtime(1447430881.1234567)`, true, "SELECT FROM_UNIXTIME(1447430881.1234567)"}, - {`select from_unixtime(1447430881.9999999)`, true, "SELECT FROM_UNIXTIME(1447430881.9999999)"}, - {`select from_unixtime(1447430881, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, - {`select from_unixtime(1447430881.123456, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881.123456, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, - {`select from_unixtime(1447430881.1234567, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881.1234567, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, - - // for issue 224 - {`SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;`, true, "SELECT CAST(_UTF8MB4'test collated returns' AS CHAR CHARSET UTF8) COLLATE utf8_bin"}, - - // for string functions - // trim - {`SELECT TRIM(' bar ');`, true, "SELECT TRIM(_UTF8MB4' bar ')"}, - {`SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');`, true, "SELECT TRIM(LEADING _UTF8MB4'x' FROM _UTF8MB4'xxxbarxxx')"}, - {`SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');`, true, "SELECT TRIM(BOTH _UTF8MB4'x' FROM _UTF8MB4'xxxbarxxx')"}, - {`SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');`, true, "SELECT TRIM(TRAILING _UTF8MB4'xyz' FROM _UTF8MB4'barxxyz')"}, - {`SELECT LTRIM(' foo ');`, true, "SELECT LTRIM(_UTF8MB4' foo ')"}, - {`SELECT RTRIM(' bar ');`, true, "SELECT RTRIM(_UTF8MB4' bar ')"}, - - {`SELECT RPAD('hi', 6, 'c');`, true, "SELECT RPAD(_UTF8MB4'hi', 6, _UTF8MB4'c')"}, - {`SELECT BIT_LENGTH('hi');`, true, "SELECT BIT_LENGTH(_UTF8MB4'hi')"}, - {`SELECT CHAR(65);`, true, "SELECT CHAR_FUNC(65, NULL)"}, - {`SELECT CHAR_LENGTH('abc');`, true, "SELECT CHAR_LENGTH(_UTF8MB4'abc')"}, - {`SELECT CHARACTER_LENGTH('abc');`, true, "SELECT CHARACTER_LENGTH(_UTF8MB4'abc')"}, - {`SELECT FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo');`, true, "SELECT FIELD(_UTF8MB4'ej', _UTF8MB4'Hej', _UTF8MB4'ej', _UTF8MB4'Heja', _UTF8MB4'hej', _UTF8MB4'foo')"}, - {`SELECT FIND_IN_SET('foo', 'foo,bar')`, true, "SELECT FIND_IN_SET(_UTF8MB4'foo', _UTF8MB4'foo,bar')"}, - {`SELECT FIND_IN_SET('foo')`, true, "SELECT FIND_IN_SET(_UTF8MB4'foo')"}, // illegal number of argument still pass - {`SELECT MAKE_SET(1,'a'), MAKE_SET(1,'a','b','c')`, true, "SELECT MAKE_SET(1, _UTF8MB4'a'),MAKE_SET(1, _UTF8MB4'a', _UTF8MB4'b', _UTF8MB4'c')"}, - {`SELECT MID('Sakila', -5, 3)`, true, "SELECT MID(_UTF8MB4'Sakila', -5, 3)"}, - {`SELECT OCT(12)`, true, "SELECT OCT(12)"}, - {`SELECT OCTET_LENGTH('text')`, true, "SELECT OCTET_LENGTH(_UTF8MB4'text')"}, - {`SELECT ORD('2')`, true, "SELECT ORD(_UTF8MB4'2')"}, - {`SELECT POSITION('bar' IN 'foobarbar')`, true, "SELECT POSITION(_UTF8MB4'bar' IN _UTF8MB4'foobarbar')"}, - {`SELECT QUOTE('Don\'t!')`, true, "SELECT QUOTE(_UTF8MB4'Don''t!')"}, - {`SELECT BIN(12)`, true, "SELECT BIN(12)"}, - {`SELECT ELT(1, 'ej', 'Heja', 'hej', 'foo')`, true, "SELECT ELT(1, _UTF8MB4'ej', _UTF8MB4'Heja', _UTF8MB4'hej', _UTF8MB4'foo')"}, - {`SELECT EXPORT_SET(5,'Y','N'), EXPORT_SET(5,'Y','N',','), EXPORT_SET(5,'Y','N',',',4)`, true, "SELECT EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N'),EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N', _UTF8MB4','),EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N', _UTF8MB4',', 4)"}, - {`SELECT FORMAT(), FORMAT(12332.2,2,'de_DE'), FORMAT(12332.123456, 4)`, true, "SELECT FORMAT(),FORMAT(12332.2, 2, _UTF8MB4'de_DE'),FORMAT(12332.123456, 4)"}, - {`SELECT FROM_BASE64('abc')`, true, "SELECT FROM_BASE64(_UTF8MB4'abc')"}, - {`SELECT TO_BASE64('abc')`, true, "SELECT TO_BASE64(_UTF8MB4'abc')"}, - {`SELECT INSERT(), INSERT('Quadratic', 3, 4, 'What'), INSTR('foobarbar', 'bar')`, true, "SELECT INSERT_FUNC(),INSERT_FUNC(_UTF8MB4'Quadratic', 3, 4, _UTF8MB4'What'),INSTR(_UTF8MB4'foobarbar', _UTF8MB4'bar')"}, - {`SELECT LOAD_FILE('/tmp/picture')`, true, "SELECT LOAD_FILE(_UTF8MB4'/tmp/picture')"}, - {`SELECT LPAD('hi',4,'??')`, true, "SELECT LPAD(_UTF8MB4'hi', 4, _UTF8MB4'??')"}, - {`SELECT LEFT("foobar", 3)`, true, "SELECT LEFT(_UTF8MB4'foobar', 3)"}, - {`SELECT RIGHT("foobar", 3)`, true, "SELECT RIGHT(_UTF8MB4'foobar', 3)"}, - - // repeat - {`SELECT REPEAT("a", 10);`, true, "SELECT REPEAT(_UTF8MB4'a', 10)"}, - - // for miscellaneous functions - {`SELECT SLEEP(10);`, true, "SELECT SLEEP(10)"}, - {`SELECT ANY_VALUE(@arg);`, true, "SELECT ANY_VALUE(@`arg`)"}, - {`SELECT INET_ATON('10.0.5.9');`, true, "SELECT INET_ATON(_UTF8MB4'10.0.5.9')"}, - {`SELECT INET_NTOA(167773449);`, true, "SELECT INET_NTOA(167773449)"}, - {`SELECT INET6_ATON('fdfe::5a55:caff:fefa:9089');`, true, "SELECT INET6_ATON(_UTF8MB4'fdfe::5a55:caff:fefa:9089')"}, - {`SELECT INET6_NTOA(INET_NTOA(167773449));`, true, "SELECT INET6_NTOA(INET_NTOA(167773449))"}, - {`SELECT IS_FREE_LOCK(@str);`, true, "SELECT IS_FREE_LOCK(@`str`)"}, - {`SELECT IS_IPV4('10.0.5.9');`, true, "SELECT IS_IPV4(_UTF8MB4'10.0.5.9')"}, - {`SELECT IS_IPV4_COMPAT(INET6_ATON('::10.0.5.9'));`, true, "SELECT IS_IPV4_COMPAT(INET6_ATON(_UTF8MB4'::10.0.5.9'))"}, - {`SELECT IS_IPV4_MAPPED(INET6_ATON('::10.0.5.9'));`, true, "SELECT IS_IPV4_MAPPED(INET6_ATON(_UTF8MB4'::10.0.5.9'))"}, - {`SELECT IS_IPV6('10.0.5.9');`, true, "SELECT IS_IPV6(_UTF8MB4'10.0.5.9')"}, - {`SELECT IS_USED_LOCK(@str);`, true, "SELECT IS_USED_LOCK(@`str`)"}, - {`SELECT MASTER_POS_WAIT(@log_name, @log_pos), MASTER_POS_WAIT(@log_name, @log_pos, @timeout), MASTER_POS_WAIT(@log_name, @log_pos, @timeout, @channel_name);`, true, "SELECT MASTER_POS_WAIT(@`log_name`, @`log_pos`),MASTER_POS_WAIT(@`log_name`, @`log_pos`, @`timeout`),MASTER_POS_WAIT(@`log_name`, @`log_pos`, @`timeout`, @`channel_name`)"}, - {`SELECT NAME_CONST('myname', 14);`, true, "SELECT NAME_CONST(_UTF8MB4'myname', 14)"}, - {`SELECT RELEASE_ALL_LOCKS();`, true, "SELECT RELEASE_ALL_LOCKS()"}, - {`SELECT UUID();`, true, "SELECT UUID()"}, - {`SELECT UUID_SHORT()`, true, "SELECT UUID_SHORT()"}, - {`SELECT UUID_TO_BIN('6ccd780c-baba-1026-9564-5b8c656024db')`, true, "SELECT UUID_TO_BIN(_UTF8MB4'6ccd780c-baba-1026-9564-5b8c656024db')"}, - {`SELECT UUID_TO_BIN('6ccd780c-baba-1026-9564-5b8c656024db', 1)`, true, "SELECT UUID_TO_BIN(_UTF8MB4'6ccd780c-baba-1026-9564-5b8c656024db', 1)"}, - {`SELECT BIN_TO_UUID(0x6ccd780cbaba102695645b8c656024db)`, true, "SELECT BIN_TO_UUID(x'6ccd780cbaba102695645b8c656024db')"}, - {`SELECT BIN_TO_UUID(0x6ccd780cbaba102695645b8c656024db, 1)`, true, "SELECT BIN_TO_UUID(x'6ccd780cbaba102695645b8c656024db', 1)"}, - // test illegal arguments - {`SELECT SLEEP();`, true, "SELECT SLEEP()"}, - {`SELECT ANY_VALUE();`, true, "SELECT ANY_VALUE()"}, - {`SELECT INET_ATON();`, true, "SELECT INET_ATON()"}, - {`SELECT INET_NTOA();`, true, "SELECT INET_NTOA()"}, - {`SELECT INET6_ATON();`, true, "SELECT INET6_ATON()"}, - {`SELECT INET6_NTOA(INET_NTOA());`, true, "SELECT INET6_NTOA(INET_NTOA())"}, - {`SELECT IS_FREE_LOCK();`, true, "SELECT IS_FREE_LOCK()"}, - {`SELECT IS_IPV4();`, true, "SELECT IS_IPV4()"}, - {`SELECT IS_IPV4_COMPAT(INET6_ATON());`, true, "SELECT IS_IPV4_COMPAT(INET6_ATON())"}, - {`SELECT IS_IPV4_MAPPED(INET6_ATON());`, true, "SELECT IS_IPV4_MAPPED(INET6_ATON())"}, - {`SELECT IS_IPV6()`, true, "SELECT IS_IPV6()"}, - {`SELECT IS_USED_LOCK();`, true, "SELECT IS_USED_LOCK()"}, - {`SELECT MASTER_POS_WAIT();`, true, "SELECT MASTER_POS_WAIT()"}, - {`SELECT NAME_CONST();`, true, "SELECT NAME_CONST()"}, - {`SELECT RELEASE_ALL_LOCKS(1);`, true, "SELECT RELEASE_ALL_LOCKS(1)"}, - {`SELECT UUID(1);`, true, "SELECT UUID(1)"}, - {`SELECT UUID_SHORT(1)`, true, "SELECT UUID_SHORT(1)"}, - // interval - {`select "2011-11-11 10:10:10.123456" + interval 10 second`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select "2011-11-11 10:10:10.123456" - interval 10 second`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select interval 10 second + "2011-11-11 10:10:10.123456"`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - // for date_add - {`select date_add("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, - {`select date_add("2011-11-11 10:10:10.123456", 10)`, false, ""}, - {`select date_add("2011-11-11 10:10:10.123456", 0.10)`, false, ""}, - {`select date_add("2011-11-11 10:10:10.123456", "11,11")`, false, ""}, - - {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_microsecond)`, false, ""}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_day)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_week)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_quarter)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, - {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_year)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, - - // for strcmp - {`select strcmp('abc', 'def')`, true, "SELECT STRCMP(_UTF8MB4'abc', _UTF8MB4'def')"}, - - // for adddate - {`select adddate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, - {`select adddate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, - {`select adddate("2011-11-11 10:10:10.123456", 10)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select adddate("2011-11-11 10:10:10.123456", 0.10)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 0.10 DAY)"}, - {`select adddate("2011-11-11 10:10:10.123456", "11,11")`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11,11' DAY)"}, - - // for date_sub - {`select date_sub("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, - {`select date_sub("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, - {`select date_sub("2011-11-11 10:10:10.123456", 10)`, false, ""}, - {`select date_sub("2011-11-11 10:10:10.123456", 0.10)`, false, ""}, - {`select date_sub("2011-11-11 10:10:10.123456", "11,11")`, false, ""}, - - // for subdate - {`select subdate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, - {`select subdate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, - {`select subdate("2011-11-11 10:10:10.123456", 10)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, - {`select subdate("2011-11-11 10:10:10.123456", 0.10)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 0.10 DAY)"}, - {`select subdate("2011-11-11 10:10:10.123456", "11,11")`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11,11' DAY)"}, - - // for unix_timestamp - {`select unix_timestamp()`, true, "SELECT UNIX_TIMESTAMP()"}, - {`select unix_timestamp('2015-11-13 10:20:19.012')`, true, "SELECT UNIX_TIMESTAMP(_UTF8MB4'2015-11-13 10:20:19.012')"}, - - // for misc functions - {`SELECT GET_LOCK('lock1',10);`, true, "SELECT GET_LOCK(_UTF8MB4'lock1', 10)"}, - {`SELECT RELEASE_LOCK('lock1');`, true, "SELECT RELEASE_LOCK(_UTF8MB4'lock1')"}, - - // for aggregate functions - {`select avg(), avg(c1,c2) from t;`, false, "SELECT AVG(),AVG(`c1`, `c2`) FROM `t`"}, - {`select avg(distinct c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, - {`select avg(distinctrow c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, - {`select avg(distinct all c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, - {`select avg(distinctrow all c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, - {`select avg(c2) from t;`, true, "SELECT AVG(`c2`) FROM `t`"}, - {`select bit_and(c1) from t;`, true, "SELECT BIT_AND(`c1`) FROM `t`"}, - {`select bit_and(all c1) from t;`, true, "SELECT BIT_AND(`c1`) FROM `t`"}, - {`select bit_and(distinct c1) from t;`, false, ""}, - {`select bit_and(distinctrow c1) from t;`, false, ""}, - {`select bit_and(distinctrow all c1) from t;`, false, ""}, - {`select bit_and(distinct all c1) from t;`, false, ""}, - {`select bit_and(), bit_and(distinct c1) from t;`, false, ""}, - {`select bit_and(), bit_and(distinctrow c1) from t;`, false, ""}, - {`select bit_and(), bit_and(all c1) from t;`, false, ""}, - {`select bit_or(c1) from t;`, true, "SELECT BIT_OR(`c1`) FROM `t`"}, - {`select bit_or(all c1) from t;`, true, "SELECT BIT_OR(`c1`) FROM `t`"}, - {`select bit_or(distinct c1) from t;`, false, ""}, - {`select bit_or(distinctrow c1) from t;`, false, ""}, - {`select bit_or(distinctrow all c1) from t;`, false, ""}, - {`select bit_or(distinct all c1) from t;`, false, ""}, - {`select bit_or(), bit_or(distinct c1) from t;`, false, ""}, - {`select bit_or(), bit_or(distinctrow c1) from t;`, false, ""}, - {`select bit_or(), bit_or(all c1) from t;`, false, ""}, - {`select bit_xor(c1) from t;`, true, "SELECT BIT_XOR(`c1`) FROM `t`"}, - {`select bit_xor(all c1) from t;`, true, "SELECT BIT_XOR(`c1`) FROM `t`"}, - {`select bit_xor(distinct c1) from t;`, false, ""}, - {`select bit_xor(distinctrow c1) from t;`, false, ""}, - {`select bit_xor(distinctrow all c1) from t;`, false, ""}, - {`select bit_xor(), bit_xor(distinct c1) from t;`, false, ""}, - {`select bit_xor(), bit_xor(distinctrow c1) from t;`, false, ""}, - {`select bit_xor(), bit_xor(all c1) from t;`, false, ""}, - {`select max(c1,c2) from t;`, false, ""}, - {`select max(distinct c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, - {`select max(distinctrow c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, - {`select max(distinct all c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, - {`select max(distinctrow all c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, - {`select max(c2) from t;`, true, "SELECT MAX(`c2`) FROM `t`"}, - {`select min(c1,c2) from t;`, false, ""}, - {`select min(distinct c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, - {`select min(distinctrow c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, - {`select min(distinct all c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, - {`select min(distinctrow all c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, - {`select min(c2) from t;`, true, "SELECT MIN(`c2`) FROM `t`"}, - {`select sum(c1,c2) from t;`, false, ""}, - {`select sum(distinct c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, - {`select sum(distinctrow c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, - {`select sum(distinct all c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, - {`select sum(distinctrow all c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, - {`select sum(c2) from t;`, true, "SELECT SUM(`c2`) FROM `t`"}, - {`select count(c1) from t;`, true, "SELECT COUNT(`c1`) FROM `t`"}, - {`select count(distinct *) from t;`, false, ""}, - {`select count(distinctrow *) from t;`, false, ""}, - {`select count(*) from t;`, true, "SELECT COUNT(1) FROM `t`"}, - {`select count(distinct c1, c2) from t;`, true, "SELECT COUNT(DISTINCT `c1`, `c2`) FROM `t`"}, - {`select count(distinctrow c1, c2) from t;`, true, "SELECT COUNT(DISTINCT `c1`, `c2`) FROM `t`"}, - {`select count(c1, c2) from t;`, false, ""}, - {`select count(all c1) from t;`, true, "SELECT COUNT(`c1`) FROM `t`"}, - {`select count(distinct all c1) from t;`, false, ""}, - {`select count(distinctrow all c1) from t;`, false, ""}, - {`select approx_count_distinct(c1) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`) FROM `t`"}, - {`select approx_count_distinct(c1, c2) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`, `c2`) FROM `t`"}, - {`select approx_count_distinct(c1, 123) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`, 123) FROM `t`"}, - {`select approx_percentile(c1) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`) FROM `t`"}, - {`select approx_percentile(c1, c2) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`, `c2`) FROM `t`"}, - {`select approx_percentile(c1, 123) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`, 123) FROM `t`"}, - {`select group_concat(c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(`c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, - {`select group_concat(c2,c1 SEPARATOR ';') from t group by c1;`, true, "SELECT GROUP_CONCAT(`c2`, `c1` SEPARATOR ';') FROM `t` GROUP BY `c1`"}, - {`select group_concat(distinct c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(DISTINCT `c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, - {`select group_concat(distinctrow c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(DISTINCT `c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, - {`SELECT student_name, GROUP_CONCAT(DISTINCT test_score ORDER BY test_score DESC SEPARATOR ' ') FROM student GROUP BY student_name;`, true, "SELECT `student_name`,GROUP_CONCAT(DISTINCT `test_score` ORDER BY `test_score` DESC SEPARATOR ' ') FROM `student` GROUP BY `student_name`"}, - {`select std(c1), std(all c1), std(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, - {`select std(c1, c2) from t`, false, ""}, - {`select stddev(c1), stddev(all c1), stddev(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, - {`select stddev(c1, c2) from t`, false, ""}, - {`select stddev_pop(c1), stddev_pop(all c1), stddev_pop(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, - {`select stddev_pop(c1, c2) from t`, false, ""}, - {`select stddev_samp(c1), stddev_samp(all c1), stddev_samp(distinct c1) from t`, true, "SELECT STDDEV_SAMP(`c1`),STDDEV_SAMP(`c1`),STDDEV_SAMP(DISTINCT `c1`) FROM `t`"}, - {`select stddev_samp(c1, c2) from t`, false, ""}, - {`select variance(c1), variance(all c1), variance(distinct c1) from t`, true, "SELECT VAR_POP(`c1`),VAR_POP(`c1`),VAR_POP(DISTINCT `c1`) FROM `t`"}, - {`select variance(c1, c2) from t`, false, ""}, - {`select var_pop(c1), var_pop(all c1), var_pop(distinct c1) from t`, true, "SELECT VAR_POP(`c1`),VAR_POP(`c1`),VAR_POP(DISTINCT `c1`) FROM `t`"}, - {`select var_pop(c1, c2) from t`, false, ""}, - {`select var_samp(c1), var_samp(all c1), var_samp(distinct c1) from t`, true, "SELECT VAR_SAMP(`c1`),VAR_SAMP(`c1`),VAR_SAMP(DISTINCT `c1`) FROM `t`"}, - {`select var_samp(c1, c2) from t`, false, ""}, - {`select json_arrayagg(c2) from t group by c1`, true, "SELECT JSON_ARRAYAGG(`c2`) FROM `t` GROUP BY `c1`"}, - {`select json_arrayagg(c1, c2) from t group by c1`, false, ""}, - {`select json_arrayagg(distinct c2) from t group by c1`, false, "SELECT JSON_ARRAYAGG(DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_arrayagg(all c2) from t group by c1`, true, "SELECT JSON_ARRAYAGG(`c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(c1, c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(c1, c2, c3) from t group by c1`, false, ""}, - {`select json_objectagg(distinct c1, c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(DISTINCT `c1`, `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(c1, distinct c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(`c1`, DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(distinct c1, distinct c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(DISTINCT `c1`, DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(all c1, c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(c1, all c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, - {`select json_objectagg(all c1, all c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, - - // for encryption and compression functions - {`select AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3'))`, true, "SELECT AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3'))"}, - {`select AES_DECRYPT(@crypt_str,@key_str)`, true, "SELECT AES_DECRYPT(@`crypt_str`, @`key_str`)"}, - {`select AES_DECRYPT(@crypt_str,@key_str,@init_vector);`, true, "SELECT AES_DECRYPT(@`crypt_str`, @`key_str`, @`init_vector`)"}, - {`SELECT COMPRESS('');`, true, "SELECT COMPRESS(_UTF8MB4'')"}, - {`SELECT DECODE(@crypt_str, @pass_str);`, true, "SELECT DECODE(@`crypt_str`, @`pass_str`)"}, - {`SELECT DES_DECRYPT(@crypt_str), DES_DECRYPT(@crypt_str, @key_str);`, true, "SELECT DES_DECRYPT(@`crypt_str`),DES_DECRYPT(@`crypt_str`, @`key_str`)"}, - {`SELECT DES_ENCRYPT(@str), DES_ENCRYPT(@key_num);`, true, "SELECT DES_ENCRYPT(@`str`),DES_ENCRYPT(@`key_num`)"}, - {`SELECT ENCODE('cleartext', CONCAT('my_random_salt','my_secret_password'));`, true, "SELECT ENCODE(_UTF8MB4'cleartext', CONCAT(_UTF8MB4'my_random_salt', _UTF8MB4'my_secret_password'))"}, - {`SELECT ENCRYPT('hello'), ENCRYPT('hello', @salt);`, true, "SELECT ENCRYPT(_UTF8MB4'hello'),ENCRYPT(_UTF8MB4'hello', @`salt`)"}, - {`SELECT MD5('testing');`, true, "SELECT MD5(_UTF8MB4'testing')"}, - {`SELECT OLD_PASSWORD(@str);`, true, "SELECT OLD_PASSWORD(@`str`)"}, - {`SELECT PASSWORD(@str);`, true, "SELECT PASSWORD_FUNC(@`str`)"}, - {`SELECT RANDOM_BYTES(@len);`, true, "SELECT RANDOM_BYTES(@`len`)"}, - {`SELECT SHA1('abc');`, true, "SELECT SHA1(_UTF8MB4'abc')"}, - {`SELECT SHA('abc');`, true, "SELECT SHA(_UTF8MB4'abc')"}, - {`SELECT SHA2('abc', 224);`, true, "SELECT SHA2(_UTF8MB4'abc', 224)"}, - {`SELECT SM3('abc');`, true, "SELECT SM3(_UTF8MB4'abc')"}, - {`SELECT UNCOMPRESS('any string');`, true, "SELECT UNCOMPRESS(_UTF8MB4'any string')"}, - {`SELECT UNCOMPRESSED_LENGTH(@compressed_string);`, true, "SELECT UNCOMPRESSED_LENGTH(@`compressed_string`)"}, - {`SELECT VALIDATE_PASSWORD_STRENGTH(@str);`, true, "SELECT VALIDATE_PASSWORD_STRENGTH(@`str`)"}, - - // For JSON functions. - {`SELECT JSON_EXTRACT();`, true, "SELECT JSON_EXTRACT()"}, - {`SELECT JSON_UNQUOTE();`, true, "SELECT JSON_UNQUOTE()"}, - {`SELECT JSON_TYPE('[123]');`, true, "SELECT JSON_TYPE(_UTF8MB4'[123]')"}, - {`SELECT JSON_TYPE();`, true, "SELECT JSON_TYPE()"}, - - // For two json grammar sugar. - {`SELECT a->'$.a' FROM t`, true, "SELECT JSON_EXTRACT(`a`, _UTF8MB4'$.a') FROM `t`"}, - {`SELECT a->>'$.a' FROM t`, true, "SELECT JSON_UNQUOTE(JSON_EXTRACT(`a`, _UTF8MB4'$.a')) FROM `t`"}, - {`SELECT '{}'->'$.a' FROM t`, false, ""}, - {`SELECT '{}'->>'$.a' FROM t`, false, ""}, - {`SELECT a->3 FROM t`, false, ""}, - {`SELECT a->>3 FROM t`, false, ""}, - - {`SELECT 1 member of (a)`, true, "SELECT 1 MEMBER OF (`a`)"}, - {`SELECT 1 member of a`, false, ""}, - {`SELECT 1 member a`, false, ""}, - {`SELECT 1 not member of a`, false, ""}, - {`SELECT 1 member of (1+1)`, false, ""}, - {`SELECT concat('a') member of (cast(1 as char(1)))`, true, "SELECT CONCAT(_UTF8MB4'a') MEMBER OF (CAST(1 AS CHAR(1)))"}, - - // Test that quoted identifier can be a function name. - {"SELECT `uuid`()", true, "SELECT UUID()"}, - - // Test sequence function. - {"select nextval(seq)", true, "SELECT NEXTVAL(`seq`)"}, - {"select lastval(seq)", true, "SELECT LASTVAL(`seq`)"}, - {"select setval(seq, 100)", true, "SELECT SETVAL(`seq`, 100)"}, - {"select next value for seq", true, "SELECT NEXTVAL(`seq`)"}, - {"select next value for sequence", true, "SELECT NEXTVAL(`sequence`)"}, - {"select NeXt vAluE for seQuEncE2", true, "SELECT NEXTVAL(`seQuEncE2`)"}, - - // Test regexp functions - {"select regexp_like('aBc', 'abc', 'im');", true, "SELECT REGEXP_LIKE(_UTF8MB4'aBc', _UTF8MB4'abc', _UTF8MB4'im')"}, - {"select regexp_substr('aBc', 'abc', 1, 1, 'im');", true, "SELECT REGEXP_SUBSTR(_UTF8MB4'aBc', _UTF8MB4'abc', 1, 1, _UTF8MB4'im')"}, - {"select regexp_instr('aBc', 'abc', 1, 1, 0, 'im');", true, "SELECT REGEXP_INSTR(_UTF8MB4'aBc', _UTF8MB4'abc', 1, 1, 0, _UTF8MB4'im')"}, - {"select regexp_replace('aBc', 'abc', 'def', 1, 1, 'i');", true, "SELECT REGEXP_REPLACE(_UTF8MB4'aBc', _UTF8MB4'abc', _UTF8MB4'def', 1, 1, _UTF8MB4'i')"}, - - // Test ilike functions - {"select 'aBc' ilike 'abc';", true, "SELECT _UTF8MB4'aBc' ILIKE _UTF8MB4'abc'"}, - } - RunTest(t, table, false) - - // Test in REAL_AS_FLOAT SQL mode. - table2 := []testCase{ - // for cast as float - {"select cast(1 as float);", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(0));", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(24));", true, "SELECT CAST(1 AS FLOAT)"}, - {"select cast(1 as float(25));", true, "SELECT CAST(1 AS DOUBLE)"}, - {"select cast(1 as float(53));", true, "SELECT CAST(1 AS DOUBLE)"}, - {"select cast(1 as float(54));", false, ""}, - - // for cast as real - {"select cast(1 as real);", true, "SELECT CAST(1 AS FLOAT)"}, - } - RunTestInRealAsFloatMode(t, table2, false) -} - -func TestIdentifier(t *testing.T) { - table := []testCase{ - // for quote identifier - {"select `a`, `a.b`, `a b` from t", true, "SELECT `a`,`a.b`,`a b` FROM `t`"}, - // for unquoted identifier - {"create table MergeContextTest$Simple (value integer not null, primary key (value))", true, "CREATE TABLE `MergeContextTest$Simple` (`value` INT NOT NULL,PRIMARY KEY(`value`))"}, - // for as - {"select 1 as a, 1 as `a`, 1 as \"a\", 1 as 'a'", true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`,1 AS `a`"}, - {`select 1 as a, 1 as "a", 1 as 'a'`, true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`"}, - {`select 1 a, 1 "a", 1 'a'`, true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`"}, - {`select * from t as "a"`, false, ""}, - {`select * from t a`, true, "SELECT * FROM `t` AS `a`"}, - // reserved keyword can't be used as identifier directly, but A.B pattern is an exception - {`select * from ROW`, false, ""}, - {`select COUNT from DESC`, false, ""}, - {`select COUNT from SELECT.DESC`, true, "SELECT `COUNT` FROM `SELECT`.`DESC`"}, - {"use `select`", true, "USE `select`"}, - {"use `sel``ect`", true, "USE `sel``ect`"}, //nolint: misspell - {"use select", false, "USE `select`"}, - {`select * from t as a`, true, "SELECT * FROM `t` AS `a`"}, - {"select 1 full, 1 row, 1 abs", false, ""}, - {"select 1 full, 1 `row`, 1 abs", true, "SELECT 1 AS `full`,1 AS `row`,1 AS `abs`"}, - {"select * from t full, t1 row, t2 abs", false, ""}, - {"select * from t full, t1 `row`, t2 abs", true, "SELECT * FROM ((`t` AS `full`) JOIN `t1` AS `row`) JOIN `t2` AS `abs`"}, - // for issue 1878, identifiers may begin with digit. - {"create database 123test", true, "CREATE DATABASE `123test`"}, - {"create database 123", false, "CREATE DATABASE `123`"}, - {"create database `123`", true, "CREATE DATABASE `123`"}, - {"create database `12``3`", true, "CREATE DATABASE `12``3`"}, - {"create table `123` (123a1 int)", true, "CREATE TABLE `123` (`123a1` INT)"}, - {"create table 123 (123a1 int)", false, ""}, - {fmt.Sprintf("select * from t%cble", 0), false, ""}, - // for issue 3954, should NOT be recognized as identifiers. - {`select .78+123`, true, "SELECT 0.78+123"}, - {`select .78+.21`, true, "SELECT 0.78+0.21"}, - {`select .78-123`, true, "SELECT 0.78-123"}, - {`select .78-.21`, true, "SELECT 0.78-0.21"}, - {`select .78--123`, true, "SELECT 0.78--123"}, - {`select .78*123`, true, "SELECT 0.78*123"}, - {`select .78*.21`, true, "SELECT 0.78*0.21"}, - {`select .78/123`, true, "SELECT 0.78/123"}, - {`select .78/.21`, true, "SELECT 0.78/0.21"}, - {`select .78,123`, true, "SELECT 0.78,123"}, - {`select .78,.21`, true, "SELECT 0.78,0.21"}, - {`select .78 , 123`, true, "SELECT 0.78,123"}, - {`select .78.123`, false, ""}, - {`select .78#123`, true, "SELECT 0.78"}, - {`insert float_test values(.67, 'string');`, true, "INSERT INTO `float_test` VALUES (0.67,_UTF8MB4'string')"}, - {`select .78'123'`, true, "SELECT 0.78 AS `123`"}, - {"select .78`123`", true, "SELECT 0.78 AS `123`"}, - {`select .78"123"`, true, "SELECT 0.78 AS `123`"}, - {"select 111 as \xd6\xf7", true, "SELECT 111 AS `??`"}, - } - RunTest(t, table, false) -} - -func TestBuiltinFuncAsIdentifier(t *testing.T) { - whitespaceFuncs := []struct { - funcName string - args string - }{ - {"BIT_AND", "`c1`"}, - {"BIT_OR", "`c1`"}, - {"BIT_XOR", "`c1`"}, - {"CAST", "1 AS FLOAT"}, - {"COUNT", "1"}, - {"CURDATE", ""}, - {"CURTIME", ""}, - {"DATE_ADD", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, - {"DATE_SUB", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, - {"EXTRACT", "SECOND FROM _UTF8MB4'2011-11-11 10:10:10'"}, - {"GROUP_CONCAT", "`c2`, `c1` SEPARATOR ','"}, - {"MAX", "`c1`"}, - {"MID", "_UTF8MB4'Sakila', -5, 3"}, - {"MIN", "`c1`"}, - {"NOW", ""}, - {"POSITION", "_UTF8MB4'bar' IN _UTF8MB4'foobarbar'"}, - {"STDDEV_POP", "`c1`"}, - {"STDDEV_SAMP", "`c1`"}, - {"SUBSTR", "_UTF8MB4'Quadratically', 5"}, - {"SUBSTRING", "_UTF8MB4'Quadratically', 5"}, - {"SUM", "`c1`"}, - {"SYSDATE", ""}, - {"TRIM", "_UTF8MB4' foo '"}, - {"VAR_POP", "`c1`"}, - {"VAR_SAMP", "`c1`"}, - } - - testcases := make([]testCase, 0, 3*len(whitespaceFuncs)) - runTests := func(ignoreSpace bool) { - p := parser.New() - if ignoreSpace { - p.SetSQLMode(mysql.ModeIgnoreSpace) - } - for _, c := range testcases { - _, _, err := p.Parse(c.src, "", "") - if !c.ok { - require.Errorf(t, err, "source %v", c.src) - continue - } - require.NoErrorf(t, err, "source %v", c.src) - if c.ok && !ignoreSpace { - RunRestoreTest(t, c.src, c.restore, false) - } - } - } - - for _, function := range whitespaceFuncs { - // `x` is recognized as a function name for `x()`. - testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) - - // In MySQL, `select x ()` is recognized as a stored function. - // In TiDB, most of these functions are recognized as identifiers while some are builtin functions (such as COUNT, CURDATE) - // because the later ones are not added to the token map. We'd better not to modify it since it breaks compatibility. - // For example, `select CURDATE ()` reports an error in MySQL but it works well for TiDB. - - // `x` is recognized as an identifier for `x ()`. - testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) - - // `x` is recognized as a function name for `x()`. - testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), false, ""}) - } - runTests(false) - - testcases = make([]testCase, 0, 4*len(whitespaceFuncs)) - for _, function := range whitespaceFuncs { - testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) - testcases = append(testcases, testCase{fmt.Sprintf("select %s (%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) - testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), false, ""}) - testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), false, ""}) - } - runTests(true) - - normalFuncs := []struct { - funcName string - args string - }{ - {"ADDDATE", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, - {"SESSION_USER", ""}, - {"SUBDATE", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, - {"SYSTEM_USER", ""}, - } - - testcases = make([]testCase, 0, 4*len(normalFuncs)) - for _, function := range normalFuncs { - // `x` is recognized as a function name for `select x()`. - testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) - - // `x` is recognized as a function name for `select x ()`. - testcases = append(testcases, testCase{fmt.Sprintf("select %s (%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) - - // `x` is recognized as an identifier for `create table x ()`. - testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) - - // `x` is recognized as an identifier for `create table x()`. - testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) - } - runTests(false) - runTests(true) -} - -func TestDDL(t *testing.T) { - table := []testCase{ - {"CREATE", false, ""}, - {"CREATE TABLE", false, ""}, - {"CREATE TABLE foo (", false, ""}, - {"CREATE TABLE foo ()", false, ""}, - {"CREATE TABLE foo ();", false, ""}, - {"CREATE TABLE foo.* (a varchar(50), b int);", false, ""}, - {"CREATE TABLE foo (a varchar(50), b int);", true, "CREATE TABLE `foo` (`a` VARCHAR(50),`b` INT)"}, - {"CREATE TABLE foo (a TINYINT UNSIGNED);", true, "CREATE TABLE `foo` (`a` TINYINT UNSIGNED)"}, - {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED)", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, - {"CREATE TABLE foo (a bigint unsigned, b bool);", true, "CREATE TABLE `foo` (`a` BIGINT UNSIGNED,`b` TINYINT(1))"}, - {"CREATE TABLE foo (a TINYINT, b SMALLINT) CREATE TABLE bar (x INT, y int64)", false, ""}, - {"CREATE TABLE foo (a int, b float); CREATE TABLE bar (x double, y float)", true, "CREATE TABLE `foo` (`a` INT,`b` FLOAT); CREATE TABLE `bar` (`x` DOUBLE,`y` FLOAT)"}, - {"CREATE TABLE foo (a bytes)", false, ""}, - {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED)", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, - {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) -- foo", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, - {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) // foo", false, ""}, - {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) /* foo */", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, - {"CREATE TABLE foo /* foo */ (a SMALLINT UNSIGNED, b INT UNSIGNED) /* foo */", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, - {"CREATE TABLE foo (name CHAR(50) BINARY);", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY)"}, - {"CREATE TABLE foo (name CHAR(50) COLLATE utf8_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE utf8_bin)"}, - {"CREATE TABLE foo (id varchar(50) collate utf8_bin);", true, "CREATE TABLE `foo` (`id` VARCHAR(50) COLLATE utf8_bin)"}, - {"CREATE TABLE foo (name CHAR(50) CHARACTER SET UTF8)", true, "CREATE TABLE `foo` (`name` CHAR(50) CHARACTER SET UTF8)"}, - {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 BINARY)", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY CHARACTER SET UTF8)"}, - {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 BINARY CHARACTER set utf8)", false, ""}, - {"CREATE TABLE foo (name CHAR(50) BINARY CHARACTER SET utf8 COLLATE utf8_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY CHARACTER SET UTF8 COLLATE utf8_bin)"}, - {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 COLLATE utf8_bin COLLATE ascii_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) CHARACTER SET UTF8 COLLATE utf8_bin COLLATE ascii_bin)"}, - {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin COLLATE latin1_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin COLLATE latin1_bin)"}, - {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin)"}, - {"CREATE TABLE foo (a.b, b);", false, ""}, - {"CREATE TABLE foo (a, b.c);", false, ""}, - {"CREATE TABLE (name CHAR(50) BINARY)", false, ""}, - {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin, INDEX (name ASC))", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin,INDEX(`name`))"}, - {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin, INDEX (name DESC))", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin,INDEX(`name` DESC))"}, - // test enable or disable cached table - {"ALTER TABLE tmp CACHE", true, "ALTER TABLE `tmp` CACHE"}, - {"ALTER TABLE tmp NOCACHE", true, "ALTER TABLE `tmp` NOCACHE"}, - // for create temporary table - {"CREATE TEMPORARY TABLE t (a varchar(50), b int);", true, "CREATE TEMPORARY TABLE `t` (`a` VARCHAR(50),`b` INT)"}, - {"CREATE TEMPORARY TABLE t LIKE t1", true, "CREATE TEMPORARY TABLE `t` LIKE `t1`"}, - {"DROP TEMPORARY TABLE t", true, "DROP TEMPORARY TABLE `t`"}, - {"create global temporary table t (a int, b varchar(255)) on commit delete rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) ON COMMIT DELETE ROWS"}, - {"create temporary table t (a int, b varchar(255))", true, "CREATE TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255))"}, - {"create global temporary table t (a int, b varchar(255))", false, ""}, // missing on commit delete rows - {"create temporary table t (a int, b varchar(255)) on commit delete rows", false, ""}, - {"create table t (a int, b varchar(255)) on commit delete rows", false, ""}, - {"create global temporary table t (a int, b varchar(255)) partition by hash(a) partitions 10 on commit delete rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) PARTITION BY HASH (`a`) PARTITIONS 10 ON COMMIT DELETE ROWS"}, - {"create global temporary table t (a int, b varchar(255)) on commit preserve rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) ON COMMIT PRESERVE ROWS"}, - {"drop global temporary table t", true, "DROP GLOBAL TEMPORARY TABLE `t`"}, - {"drop temporary table t", true, "DROP TEMPORARY TABLE `t`"}, - // test use key word as column name - {"CREATE TABLE foo (pump varchar(50), b int);", true, "CREATE TABLE `foo` (`pump` VARCHAR(50),`b` INT)"}, - {"CREATE TABLE foo (drainer varchar(50), b int);", true, "CREATE TABLE `foo` (`drainer` VARCHAR(50),`b` INT)"}, - {"CREATE TABLE foo (node_id varchar(50), b int);", true, "CREATE TABLE `foo` (`node_id` VARCHAR(50),`b` INT)"}, - {"CREATE TABLE foo (node_state varchar(50), b int);", true, "CREATE TABLE `foo` (`node_state` VARCHAR(50),`b` INT)"}, - // for table option - {"create table t (c int) avg_row_length = 3", true, "CREATE TABLE `t` (`c` INT) AVG_ROW_LENGTH = 3"}, - {"create table t (c int) avg_row_length 3", true, "CREATE TABLE `t` (`c` INT) AVG_ROW_LENGTH = 3"}, - {"create table t (c int) checksum = 0", true, "CREATE TABLE `t` (`c` INT) CHECKSUM = 0"}, - {"create table t (c int) checksum 1", true, "CREATE TABLE `t` (`c` INT) CHECKSUM = 1"}, - {"create table t (c int) table_checksum = 0", true, "CREATE TABLE `t` (`c` INT) TABLE_CHECKSUM = 0"}, - {"create table t (c int) table_checksum 1", true, "CREATE TABLE `t` (`c` INT) TABLE_CHECKSUM = 1"}, - {"create table t (c int) compression = 'NONE'", true, "CREATE TABLE `t` (`c` INT) COMPRESSION = 'NONE'"}, - {"create table t (c int) compression 'lz4'", true, "CREATE TABLE `t` (`c` INT) COMPRESSION = 'lz4'"}, - {"create table t (c int) connection = 'abc'", true, "CREATE TABLE `t` (`c` INT) CONNECTION = 'abc'"}, - {"create table t (c int) connection 'abc'", true, "CREATE TABLE `t` (`c` INT) CONNECTION = 'abc'"}, - {"create table t (c int) key_block_size = 1024", true, "CREATE TABLE `t` (`c` INT) KEY_BLOCK_SIZE = 1024"}, - {"create table t (c int) key_block_size 1024", true, "CREATE TABLE `t` (`c` INT) KEY_BLOCK_SIZE = 1024"}, - {"create table t (c int) max_rows = 1000", true, "CREATE TABLE `t` (`c` INT) MAX_ROWS = 1000"}, - {"create table t (c int) max_rows 1000", true, "CREATE TABLE `t` (`c` INT) MAX_ROWS = 1000"}, - {"create table t (c int) min_rows = 1000", true, "CREATE TABLE `t` (`c` INT) MIN_ROWS = 1000"}, - {"create table t (c int) min_rows 1000", true, "CREATE TABLE `t` (`c` INT) MIN_ROWS = 1000"}, - {"create table t (c int) password = 'abc'", true, "CREATE TABLE `t` (`c` INT) PASSWORD = 'abc'"}, - {"create table t (c int) password 'abc'", true, "CREATE TABLE `t` (`c` INT) PASSWORD = 'abc'"}, - {"create table t (c int) DELAY_KEY_WRITE=1", true, "CREATE TABLE `t` (`c` INT) DELAY_KEY_WRITE = 1"}, - {"create table t (c int) DELAY_KEY_WRITE 1", true, "CREATE TABLE `t` (`c` INT) DELAY_KEY_WRITE = 1"}, - {"create table t (c int) ROW_FORMAT = default", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DEFAULT"}, - {"create table t (c int) ROW_FORMAT default", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DEFAULT"}, - {"create table t (c int) ROW_FORMAT = fixed", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = FIXED"}, - {"create table t (c int) ROW_FORMAT = compressed", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = COMPRESSED"}, - {"create table t (c int) ROW_FORMAT = compact", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = COMPACT"}, - {"create table t (c int) ROW_FORMAT = redundant", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = REDUNDANT"}, - {"create table t (c int) ROW_FORMAT = dynamic", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DYNAMIC"}, - {"create table t (c int) STATS_PERSISTENT = default", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, - {"create table t (c int) STATS_PERSISTENT = 0", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, - {"create table t (c int) STATS_PERSISTENT = 1", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, - {"create table t (c int) STATS_SAMPLE_PAGES 0", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 0"}, - {"create table t (c int) STATS_SAMPLE_PAGES 10", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 10"}, - {"create table t (c int) STATS_SAMPLE_PAGES = 10", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 10"}, - {"create table t (c int) STATS_SAMPLE_PAGES = default", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = DEFAULT"}, - {"create table t (c int) PACK_KEYS = 1", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, - {"create table t (c int) PACK_KEYS = 0", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, - {"create table t (c int) PACK_KEYS = DEFAULT", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, - {"create table t (c int) STORAGE DISK", true, "CREATE TABLE `t` (`c` INT) STORAGE DISK"}, - {"create table t (c int) STORAGE MEMORY", true, "CREATE TABLE `t` (`c` INT) STORAGE MEMORY"}, - {"create table t (c int) SECONDARY_ENGINE null", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = NULL"}, - {"create table t (c int) SECONDARY_ENGINE = innodb", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = 'innodb'"}, - {"create table t (c int) SECONDARY_ENGINE 'null'", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = 'null'"}, - {`create table testTableCompression (c VARCHAR(15000)) compression="ZLIB";`, true, "CREATE TABLE `testTableCompression` (`c` VARCHAR(15000)) COMPRESSION = 'ZLIB'"}, - {`create table t1 (c1 int) compression="zlib";`, true, "CREATE TABLE `t1` (`c1` INT) COMPRESSION = 'zlib'"}, - {`create table t1 (c1 int) collate=binary;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT COLLATE = BINARY"}, - {`create table t1 (c1 int) collate=utf8mb4_0900_as_cs;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT COLLATE = UTF8MB4_0900_AS_CS"}, - {`create table t1 (c1 int) default charset=binary collate=binary;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT CHARACTER SET = BINARY DEFAULT COLLATE = BINARY"}, - - // for table option `UNION` - {"ALTER TABLE t_n UNION ( ), KEY_BLOCK_SIZE = 1", true, "ALTER TABLE `t_n` UNION = (), KEY_BLOCK_SIZE = 1"}, - {"ALTER TABLE d_n.t_n UNION ( t_n ) REMOVE PARTITIONING", true, "ALTER TABLE `d_n`.`t_n` UNION = (`t_n`) REMOVE PARTITIONING"}, - {"ALTER TABLE d_n.t_n LOCK DEFAULT , UNION = ( t_n , d_n.t_n ) REMOVE PARTITIONING", true, "ALTER TABLE `d_n`.`t_n` LOCK = DEFAULT, UNION = (`t_n`,`d_n`.`t_n`) REMOVE PARTITIONING"}, - {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , MAX_ROWS 10, UNION ( d_n.t_n ) , ROW_FORMAT REDUNDANT, STATS_PERSISTENT = DEFAULT", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, MAX_ROWS = 10, UNION = (`d_n`.`t_n`), ROW_FORMAT = REDUNDANT, STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, - - // partition option - {"create table t (b int) partition by range columns (b) (partition p0 values less than (not 3), partition p2 values less than (20));", false, ""}, - {"create table t (b int) partition by range columns (b) (partition p0 values less than (1 or 3), partition p2 values less than (20));", false, ""}, - {"create table t (b int) partition by range columns (b) (partition p0 values less than (3 is null), partition p2 values less than (20));", false, ""}, - {"create table t (b int) partition by range (b is null) (partition p0 values less than (10));", false, ""}, - {"create table t (b int) partition by list (not b) (partition p0 values in (10, 20));", false, ""}, - {"create table t (b int) partition by hash ( not b );", false, ""}, - {"create table t (b int) partition by range columns (b) (partition p0 values less than (3 in (3, 4, 5)), partition p2 values less than (20));", false, ""}, - {"CREATE TABLE t (id int) ENGINE = INNDB PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN (20));", true, "CREATE TABLE `t` (`id` INT) ENGINE = INNDB PARTITION BY RANGE (`id`) (PARTITION `p0` VALUES LESS THAN (10),PARTITION `p1` VALUES LESS THAN (20))"}, - {"create table t (c int) PARTITION BY HASH (c) PARTITIONS 32;", true, "CREATE TABLE `t` (`c` INT) PARTITION BY HASH (`c`) PARTITIONS 32"}, - {"create table t (c int) PARTITION BY HASH (Year(VDate)) (PARTITION p1980 VALUES LESS THAN (1980) ENGINE = MyISAM, PARTITION p1990 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION pothers VALUES LESS THAN MAXVALUE ENGINE = MyISAM)", false, ""}, - {"create table t (c int) PARTITION BY RANGE (Year(VDate)) (PARTITION p1980 VALUES LESS THAN (1980) ENGINE = MyISAM, PARTITION p1990 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION pothers VALUES LESS THAN MAXVALUE ENGINE = MyISAM)", true, "CREATE TABLE `t` (`c` INT) PARTITION BY RANGE (YEAR(`VDate`)) (PARTITION `p1980` VALUES LESS THAN (1980) ENGINE = MyISAM,PARTITION `p1990` VALUES LESS THAN (1990) ENGINE = MyISAM,PARTITION `pothers` VALUES LESS THAN (MAXVALUE) ENGINE = MyISAM)"}, - {"create table t (c int, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '') PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) (PARTITION p201610 VALUES LESS THAN(1477929600), PARTITION p201611 VALUES LESS THAN(1480521600),PARTITION p201612 VALUES LESS THAN(1483200000),PARTITION p201701 VALUES LESS THAN(1485878400),PARTITION p201702 VALUES LESS THAN(1488297600),PARTITION p201703 VALUES LESS THAN(1490976000))", true, "CREATE TABLE `t` (`c` INT,`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '') PARTITION BY RANGE (UNIX_TIMESTAMP(`create_time`)) (PARTITION `p201610` VALUES LESS THAN (1477929600),PARTITION `p201611` VALUES LESS THAN (1480521600),PARTITION `p201612` VALUES LESS THAN (1483200000),PARTITION `p201701` VALUES LESS THAN (1485878400),PARTITION `p201702` VALUES LESS THAN (1488297600),PARTITION `p201703` VALUES LESS THAN (1490976000))"}, - {"CREATE TABLE `md_product_shop` (`shopCode` varchar(4) DEFAULT NULL COMMENT '地点') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 /*!50100 PARTITION BY KEY (shopCode) PARTITIONS 19 */;", true, "CREATE TABLE `md_product_shop` (`shopCode` VARCHAR(4) DEFAULT NULL COMMENT '地点') ENGINE = InnoDB DEFAULT CHARACTER SET = UTF8MB4 PARTITION BY KEY (`shopCode`) PARTITIONS 19"}, - {"CREATE TABLE `payinfo1` (`id` bigint(20) NOT NULL AUTO_INCREMENT, `oderTime` datetime NOT NULL) ENGINE=InnoDB AUTO_INCREMENT=641533032 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 /*!50500 PARTITION BY RANGE COLUMNS(oderTime) (PARTITION P2011 VALUES LESS THAN ('2012-01-01 00:00:00') ENGINE = InnoDB, PARTITION P1201 VALUES LESS THAN ('2012-02-01 00:00:00') ENGINE = InnoDB, PARTITION PMAX VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB)*/;", true, "CREATE TABLE `payinfo1` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`oderTime` DATETIME NOT NULL) ENGINE = InnoDB AUTO_INCREMENT = 641533032 DEFAULT CHARACTER SET = UTF8 ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8 PARTITION BY RANGE COLUMNS (`oderTime`) (PARTITION `P2011` VALUES LESS THAN (_UTF8MB4'2012-01-01 00:00:00') ENGINE = InnoDB,PARTITION `P1201` VALUES LESS THAN (_UTF8MB4'2012-02-01 00:00:00') ENGINE = InnoDB,PARTITION `PMAX` VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB)"}, - {`CREATE TABLE app_channel_daily_report (id bigint(20) NOT NULL AUTO_INCREMENT, app_version varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'default', gmt_create datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (id)) ENGINE=InnoDB AUTO_INCREMENT=33703438 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci -/*!50100 PARTITION BY RANGE (month(gmt_create)-1) -(PARTITION part0 VALUES LESS THAN (1) COMMENT = '1月份' ENGINE = InnoDB, - PARTITION part1 VALUES LESS THAN (2) COMMENT = '2月份' ENGINE = InnoDB, - PARTITION part2 VALUES LESS THAN (3) COMMENT = '3月份' ENGINE = InnoDB, - PARTITION part3 VALUES LESS THAN (4) COMMENT = '4月份' ENGINE = InnoDB, - PARTITION part4 VALUES LESS THAN (5) COMMENT = '5月份' ENGINE = InnoDB, - PARTITION part5 VALUES LESS THAN (6) COMMENT = '6月份' ENGINE = InnoDB, - PARTITION part6 VALUES LESS THAN (7) COMMENT = '7月份' ENGINE = InnoDB, - PARTITION part7 VALUES LESS THAN (8) COMMENT = '8月份' ENGINE = InnoDB, - PARTITION part8 VALUES LESS THAN (9) COMMENT = '9月份' ENGINE = InnoDB, - PARTITION part9 VALUES LESS THAN (10) COMMENT = '10月份' ENGINE = InnoDB, - PARTITION part10 VALUES LESS THAN (11) COMMENT = '11月份' ENGINE = InnoDB, - PARTITION part11 VALUES LESS THAN (12) COMMENT = '12月份' ENGINE = InnoDB) */ ;`, true, "CREATE TABLE `app_channel_daily_report` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`app_version` VARCHAR(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT _UTF8MB4'default',`gmt_create` DATETIME NOT NULL COMMENT '创建时间',PRIMARY KEY(`id`)) ENGINE = InnoDB AUTO_INCREMENT = 33703438 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_UNICODE_CI PARTITION BY RANGE (MONTH(`gmt_create`)-1) (PARTITION `part0` VALUES LESS THAN (1) COMMENT = '1月份' ENGINE = InnoDB,PARTITION `part1` VALUES LESS THAN (2) COMMENT = '2月份' ENGINE = InnoDB,PARTITION `part2` VALUES LESS THAN (3) COMMENT = '3月份' ENGINE = InnoDB,PARTITION `part3` VALUES LESS THAN (4) COMMENT = '4月份' ENGINE = InnoDB,PARTITION `part4` VALUES LESS THAN (5) COMMENT = '5月份' ENGINE = InnoDB,PARTITION `part5` VALUES LESS THAN (6) COMMENT = '6月份' ENGINE = InnoDB,PARTITION `part6` VALUES LESS THAN (7) COMMENT = '7月份' ENGINE = InnoDB,PARTITION `part7` VALUES LESS THAN (8) COMMENT = '8月份' ENGINE = InnoDB,PARTITION `part8` VALUES LESS THAN (9) COMMENT = '9月份' ENGINE = InnoDB,PARTITION `part9` VALUES LESS THAN (10) COMMENT = '10月份' ENGINE = InnoDB,PARTITION `part10` VALUES LESS THAN (11) COMMENT = '11月份' ENGINE = InnoDB,PARTITION `part11` VALUES LESS THAN (12) COMMENT = '12月份' ENGINE = InnoDB)"}, - - // for placement option - // 1. create table - {`create table t (c int) primary_region="us";`, false, ""}, - {`create table t (c int) regions="us,3";`, false, ""}, - {`create table t (c int) followers="us,3";`, false, ""}, - {`create table t (c int) followers=3;`, false, ""}, - {`create table t (c int) followers=0;`, false, ""}, - {`create table t (c int) voters=3;`, false, ""}, - {`create table t (c int) learners="us,3";`, false, ""}, - {`create table t (c int) learners=3;`, false, ""}, - {`create table t (c int) schedule="even";`, false, ""}, - {`create table t (c int) constraints="ww";`, false, ""}, - {`create table t (c int) leader_constraints="ww";`, false, ""}, - {`create table t (c int) follower_constraints="ww";`, false, ""}, - {`create table t (c int) voter_constraints="ww";`, false, ""}, - {`create table t (c int) learner_constraints="ww";`, false, ""}, - {`create table t (c int) survival_preference="ww";`, false, ""}, - {`create table t (c int) /*T![placement] primary_region="us" */;`, false, ""}, - {`create table t (c int) /*T![placement] regions="us,3" */;`, false, ""}, - {`create table t (c int) /*T![placement] followers="us,3 */";`, false, ""}, - {`create table t (c int) /*T![placement] followers=3 */;`, false, ""}, - {`create table t (c int) /*T![placement] followers=0 */;`, false, ""}, - {`create table t (c int) /*T![placement] voters="us,3" */;`, false, ""}, - {`create table t (c int) /*T![placement] primary_region="us" regions="us,3" */;`, false, ""}, - {`create table t (c int) placement policy="ww";`, true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `ww`"}, - {"create table t (c int) /*T![placement] placement policy=`x` */;", true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `x`"}, - {`create table t (c int) /*T![placement] placement policy="y" */;`, true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `y`"}, - // 2. alter table - {`alter table t primary_region="us";`, false, ""}, - {`alter table t regions="us,3";`, false, ""}, - {`alter table t followers=3;`, false, ""}, - {`alter table t followers=0;`, false, ""}, - {`alter table t voters=3;`, false, ""}, - {`alter table t learners=3;`, false, ""}, - {`alter table t schedule="even";`, false, ""}, - {`alter table t constraints="ww";`, false, ""}, - {`alter table t leader_constraints="ww";`, false, ""}, - {`alter table t follower_constraints="ww";`, false, ""}, - {`alter table t voter_constraints="ww";`, false, ""}, - {`alter table t learner_constraints="ww";`, false, ""}, - {`alter table t /*T![placement] primary_region="us" */;`, false, ""}, - {`alter table t placement policy="ww";`, true, "ALTER TABLE `t` PLACEMENT POLICY = `ww`"}, - {`alter table t /*T![placement] placement policy="ww" */;`, true, "ALTER TABLE `t` PLACEMENT POLICY = `ww`"}, - {`alter table t compact;`, true, "ALTER TABLE `t` COMPACT"}, - {`alter table t compact tiflash replica;`, true, "ALTER TABLE `t` COMPACT TIFLASH REPLICA"}, - {`alter table t compact partition p1,p2;`, true, "ALTER TABLE `t` COMPACT PARTITION `p1`,`p2`"}, - {`alter table t compact partition p1,p2 tiflash replica;`, true, "ALTER TABLE `t` COMPACT PARTITION `p1`,`p2` TIFLASH REPLICA"}, - // 3. create db - {`create database t primary_region="us";`, false, ""}, - {`create database t regions="us,3";`, false, ""}, - {`create database t followers=3;`, false, ""}, - {`create database t followers=0;`, false, ""}, - {`create database t voters=3;`, false, ""}, - {`create database t learners=3;`, false, ""}, - {`create database t schedule="even";`, false, ""}, - {`create database t constraints="ww";`, false, ""}, - {`create database t leader_constraints="ww";`, false, ""}, - {`create database t follower_constraints="ww";`, false, ""}, - {`create database t voter_constraints="ww";`, false, ""}, - {`create database t learner_constraints="ww";`, false, ""}, - {`create database t /*T![placement] primary_region="us" */;`, false, ""}, - {`create database t placement policy="ww";`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, - {`create database t default placement policy="ww";`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, - {`create database t /*T![placement] placement policy="ww" */;`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, - // 4. alter db - {`alter database t primary_region="us";`, false, ""}, - {`alter database t regions="us,3";`, false, ""}, - {`alter database t followers=3;`, false, ""}, - {`alter database t followers=0;`, false, ""}, - {`alter database t voters=3;`, false, ""}, - {`alter database t learners=3;`, false, ""}, - {`alter database t schedule="even";`, false, ""}, - {`alter database t constraints="ww";`, false, ""}, - {`alter database t leader_constraints="ww";`, false, ""}, - {`alter database t follower_constraints="ww";`, false, ""}, - {`alter database t voter_constraints="ww";`, false, ""}, - {`alter database t learner_constraints="ww";`, false, ""}, - {`alter database t /*T![placement] primary_region="us" */;`, false, ""}, - {`alter database t placement policy="ww";`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `ww`"}, - {`alter database t default placement policy="ww";`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `ww`"}, - {`alter database t PLACEMENT POLICY='DEFAULT';`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - {`alter database t PLACEMENT POLICY=DEFAULT;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - {`alter database t PLACEMENT POLICY = DEFAULT;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - {`alter database t PLACEMENT POLICY SET DEFAULT`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - {"alter database t PLACEMENT POLICY=`DEFAULT`;", true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - {`alter database t /*T![placement] PLACEMENT POLICY='DEFAULT' */;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, - // 5. create partition - {`create table m (c int) partition by range (c) (partition p1 values less than (200) primary_region="us");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) regions="us,3");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) followers=3);`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) voters=3);`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) learners=3);`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) schedule="even");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) constraints="ww");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) leader_constraints="ww");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) follower_constraints="ww");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) voter_constraints="ww");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) learner_constraints="ww");`, false, ""}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) placement policy="ww");`, true, "CREATE TABLE `m` (`c` INT) PARTITION BY RANGE (`c`) (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, - {`create table m (c int) partition by range (c) (partition p1 values less than (200) /*T![placement] placement policy="ww" */);`, true, "CREATE TABLE `m` (`c` INT) PARTITION BY RANGE (`c`) (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, - // 6. alter partition - {`alter table m partition t primary_region="us";`, false, ""}, - {`alter table m partition t regions="us,3";`, false, ""}, - {`alter table m partition t followers=3;`, false, ""}, - {`alter table m partition t primary_region="us" followers=3;`, false, ""}, - {`alter table m partition t voters=3;`, false, ""}, - {`alter table m partition t learners=3;`, false, ""}, - {`alter table m partition t schedule="even";`, false, ""}, - {`alter table m partition t constraints="ww";`, false, ""}, - {`alter table m partition t leader_constraints="ww";`, false, ""}, - {`alter table m partition t follower_constraints="ww";`, false, ""}, - {`alter table m partition t voter_constraints="ww";`, false, ""}, - {`alter table m partition t learner_constraints="ww";`, false, ""}, - {`alter table m partition t placement policy="ww";`, true, "ALTER TABLE `m` PARTITION `t` PLACEMENT POLICY = `ww`"}, - {`alter table m partition t /*T![placement] placement policy="ww" */;`, true, "ALTER TABLE `m` PARTITION `t` PLACEMENT POLICY = `ww`"}, - // 7. add partition - {`alter table m add partition (partition p1 values less than (200) primary_region="us");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) regions="us,3");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) followers=3);`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) voters=3);`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) learners=3);`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) schedule="even");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) constraints="ww");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) leader_constraints="ww");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) follower_constraints="ww");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) voter_constraints="ww");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) learner_constraints="ww");`, false, ""}, - {`alter table m add partition (partition p1 values less than (200) placement policy="ww");`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, - {`alter table m add partition (partition p1 values less than (200) /*T![placement] placement policy="ww" */);`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, - {`alter table m add column a int, add partition (partition p1 values less than (200))`, true, "ALTER TABLE `m` ADD COLUMN `a` INT, ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200))"}, - // TODO: Do not allow this order! - {`alter table m add partition (partition p1 values less than (200)), add column a int`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200)), ADD COLUMN `a` INT"}, - // for check clause - {"create table t (c1 bool, c2 bool, check (c1 in (0, 1)) not enforced, check (c2 in (0, 1)))", true, "CREATE TABLE `t` (`c1` TINYINT(1),`c2` TINYINT(1),CHECK(`c1` IN (0,1)) NOT ENFORCED,CHECK(`c2` IN (0,1)) ENFORCED)"}, - {"CREATE TABLE Customer (SD integer CHECK (SD > 0), First_Name varchar(30));", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) ENFORCED,`First_Name` VARCHAR(30))"}, - {"CREATE TABLE Customer (SD integer CHECK (SD > 0) not enforced, SS varchar(30) check(ss='test') enforced);", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) NOT ENFORCED,`SS` VARCHAR(30) CHECK(`ss`=_UTF8MB4'test') ENFORCED)"}, - {"CREATE TABLE Customer (SD integer CHECK (SD > 0) not null, First_Name varchar(30) comment 'string' not null);", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) ENFORCED NOT NULL,`First_Name` VARCHAR(30) COMMENT 'string' NOT NULL)"}, - {"CREATE TABLE Customer (SD integer comment 'string' CHECK (SD > 0) not null);", true, "CREATE TABLE `Customer` (`SD` INT COMMENT 'string' CHECK(`SD`>0) ENFORCED NOT NULL)"}, - {"CREATE TABLE Customer (SD integer comment 'string' not enforced, First_Name varchar(30));", false, ""}, - {"CREATE TABLE Customer (SD integer not enforced, First_Name varchar(30));", false, ""}, - - {"create database xxx", true, "CREATE DATABASE `xxx`"}, - {"create database if exists xxx", false, ""}, - {"create database if not exists xxx", true, "CREATE DATABASE IF NOT EXISTS `xxx`"}, - - // for create database with encryption - {"create database xxx encryption = 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, - {"create database xxx encryption 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, - {"create database xxx default encryption = 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, - {"create database xxx default encryption 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, - {"create database xxx encryption = 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, - {"create database xxx encryption 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, - {"create database xxx default encryption = 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, - {"create database xxx default encryption 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, - {"create database xxx encryption = N", false, ""}, - - {"create schema xxx", true, "CREATE DATABASE `xxx`"}, - {"create schema if exists xxx", false, ""}, - {"create schema if not exists xxx", true, "CREATE DATABASE IF NOT EXISTS `xxx`"}, - // for drop database/schema/table/view/stats - {"drop database xxx", true, "DROP DATABASE `xxx`"}, - {"drop database if exists xxx", true, "DROP DATABASE IF EXISTS `xxx`"}, - {"drop database if not exists xxx", false, ""}, - {"drop schema xxx", true, "DROP DATABASE `xxx`"}, - {"drop schema if exists xxx", true, "DROP DATABASE IF EXISTS `xxx`"}, - {"drop schema if not exists xxx", false, ""}, - {"drop table", false, ""}, - {"drop table if exists t'xyz", false, ""}, - {"drop table if exists t'", false, ""}, - {"drop table if exists t`", false, ""}, - {`drop table if exists t'`, false, ""}, - {`drop table if exists t"`, false, ""}, - {"drop table xxx", true, "DROP TABLE `xxx`"}, - {"drop table xxx, yyy", true, "DROP TABLE `xxx`, `yyy`"}, - {"drop tables xxx", true, "DROP TABLE `xxx`"}, - {"drop tables xxx, yyy", true, "DROP TABLE `xxx`, `yyy`"}, - {"drop table if exists xxx", true, "DROP TABLE IF EXISTS `xxx`"}, - {"drop table if exists xxx, yyy", true, "DROP TABLE IF EXISTS `xxx`, `yyy`"}, - {"drop table if not exists xxx", false, ""}, - {"drop table xxx restrict", true, "DROP TABLE `xxx`"}, - {"drop table xxx, yyy cascade", true, "DROP TABLE `xxx`, `yyy`"}, - {"drop table if exists xxx restrict", true, "DROP TABLE IF EXISTS `xxx`"}, - {"drop view", false, "DROP VIEW"}, - {"drop view xxx", true, "DROP VIEW `xxx`"}, - {"drop view xxx, yyy", true, "DROP VIEW `xxx`, `yyy`"}, - {"drop view if exists xxx", true, "DROP VIEW IF EXISTS `xxx`"}, - {"drop view if exists xxx, yyy", true, "DROP VIEW IF EXISTS `xxx`, `yyy`"}, - {"drop stats t", true, "DROP STATS `t`"}, - {"drop stats t1, t2, t3", true, "DROP STATS `t1`, `t2`, `t3`"}, - {"drop stats t global", true, "DROP STATS `t` GLOBAL"}, - {"drop stats t partition p0", true, "DROP STATS `t` PARTITION `p0`"}, - {"drop stats t partition p0, p1, p2", true, "DROP STATS `t` PARTITION `p0`,`p1`,`p2`"}, - // for issue 974 - {`CREATE TABLE address ( - id bigint(20) NOT NULL AUTO_INCREMENT, - create_at datetime NOT NULL, - deleted tinyint(1) NOT NULL, - update_at datetime NOT NULL, - version bigint(20) DEFAULT NULL, - address varchar(128) NOT NULL, - address_detail varchar(128) NOT NULL, - cellphone varchar(16) NOT NULL, - latitude double NOT NULL, - longitude double NOT NULL, - name varchar(16) NOT NULL, - sex tinyint(1) NOT NULL, - user_id bigint(20) NOT NULL, - PRIMARY KEY (id), - CONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id), - INDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment '' - ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`),INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, - // for issue 975 - {`CREATE TABLE test_data ( - id bigint(20) NOT NULL AUTO_INCREMENT, - create_at datetime NOT NULL, - deleted tinyint(1) NOT NULL, - update_at datetime NOT NULL, - version bigint(20) DEFAULT NULL, - address varchar(255) NOT NULL, - amount decimal(19,2) DEFAULT NULL, - charge_id varchar(32) DEFAULT NULL, - paid_amount decimal(19,2) DEFAULT NULL, - transaction_no varchar(64) DEFAULT NULL, - wx_mp_app_id varchar(32) DEFAULT NULL, - contacts varchar(50) DEFAULT NULL, - deliver_fee decimal(19,2) DEFAULT NULL, - deliver_info varchar(255) DEFAULT NULL, - deliver_time varchar(255) DEFAULT NULL, - description varchar(255) DEFAULT NULL, - invoice varchar(255) DEFAULT NULL, - order_from int(11) DEFAULT NULL, - order_state int(11) NOT NULL, - packing_fee decimal(19,2) DEFAULT NULL, - payment_time datetime DEFAULT NULL, - payment_type int(11) DEFAULT NULL, - phone varchar(50) NOT NULL, - store_employee_id bigint(20) DEFAULT NULL, - store_id bigint(20) NOT NULL, - user_id bigint(20) NOT NULL, - payment_mode int(11) NOT NULL, - current_latitude double NOT NULL, - current_longitude double NOT NULL, - address_latitude double NOT NULL, - address_longitude double NOT NULL, - PRIMARY KEY (id), - CONSTRAINT food_order_ibfk_1 FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id), - CONSTRAINT food_order_ibfk_2 FOREIGN KEY (store_id) REFERENCES waimaiqa.store (id), - CONSTRAINT food_order_ibfk_3 FOREIGN KEY (store_employee_id) REFERENCES waimaiqa.store_employee (id), - UNIQUE FK_UNIQUE_charge_id USING BTREE (charge_id) comment '', - INDEX FK_eqst2x1xisn3o0wbrlahnnqq8 USING BTREE (store_employee_id) comment '', - INDEX FK_8jcmec4kb03f4dod0uqwm54o9 USING BTREE (store_id) comment '', - INDEX FK_a3t0m9apja9jmrn60uab30pqd USING BTREE (user_id) comment '' - ) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARACTER SET utf8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `test_data` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(255) NOT NULL,`amount` DECIMAL(19,2) DEFAULT NULL,`charge_id` VARCHAR(32) DEFAULT NULL,`paid_amount` DECIMAL(19,2) DEFAULT NULL,`transaction_no` VARCHAR(64) DEFAULT NULL,`wx_mp_app_id` VARCHAR(32) DEFAULT NULL,`contacts` VARCHAR(50) DEFAULT NULL,`deliver_fee` DECIMAL(19,2) DEFAULT NULL,`deliver_info` VARCHAR(255) DEFAULT NULL,`deliver_time` VARCHAR(255) DEFAULT NULL,`description` VARCHAR(255) DEFAULT NULL,`invoice` VARCHAR(255) DEFAULT NULL,`order_from` INT(11) DEFAULT NULL,`order_state` INT(11) NOT NULL,`packing_fee` DECIMAL(19,2) DEFAULT NULL,`payment_time` DATETIME DEFAULT NULL,`payment_type` INT(11) DEFAULT NULL,`phone` VARCHAR(50) NOT NULL,`store_employee_id` BIGINT(20) DEFAULT NULL,`store_id` BIGINT(20) NOT NULL,`user_id` BIGINT(20) NOT NULL,`payment_mode` INT(11) NOT NULL,`current_latitude` DOUBLE NOT NULL,`current_longitude` DOUBLE NOT NULL,`address_latitude` DOUBLE NOT NULL,`address_longitude` DOUBLE NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `food_order_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`),CONSTRAINT `food_order_ibfk_2` FOREIGN KEY (`store_id`) REFERENCES `waimaiqa`.`store`(`id`),CONSTRAINT `food_order_ibfk_3` FOREIGN KEY (`store_employee_id`) REFERENCES `waimaiqa`.`store_employee`(`id`),UNIQUE `FK_UNIQUE_charge_id`(`charge_id`) USING BTREE,INDEX `FK_eqst2x1xisn3o0wbrlahnnqq8`(`store_employee_id`) USING BTREE,INDEX `FK_8jcmec4kb03f4dod0uqwm54o9`(`store_id`) USING BTREE,INDEX `FK_a3t0m9apja9jmrn60uab30pqd`(`user_id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 95 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, - {`create table t (c int KEY);`, true, "CREATE TABLE `t` (`c` INT PRIMARY KEY)"}, - {`CREATE TABLE address ( - id bigint(20) NOT NULL AUTO_INCREMENT, - create_at datetime NOT NULL, - deleted tinyint(1) NOT NULL, - update_at datetime NOT NULL, - version bigint(20) DEFAULT NULL, - address varchar(128) NOT NULL, - address_detail varchar(128) NOT NULL, - cellphone varchar(16) NOT NULL, - latitude double NOT NULL, - longitude double NOT NULL, - name varchar(16) NOT NULL, - sex tinyint(1) NOT NULL, - user_id bigint(20) NOT NULL, - PRIMARY KEY (id), - CONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id) ON DELETE CASCADE ON UPDATE NO ACTION, - INDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment '' - ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET utf8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION,INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, - {"CREATE TABLE address (\r\nid bigint(20) NOT NULL AUTO_INCREMENT,\r\ncreate_at datetime NOT NULL,\r\ndeleted tinyint(1) NOT NULL,\r\nupdate_at datetime NOT NULL,\r\nversion bigint(20) DEFAULT NULL,\r\naddress varchar(128) NOT NULL,\r\naddress_detail varchar(128) NOT NULL,\r\ncellphone varchar(16) NOT NULL,\r\nlatitude double NOT NULL,\r\nlongitude double NOT NULL,\r\nname varchar(16) NOT NULL,\r\nsex tinyint(1) NOT NULL,\r\nuser_id bigint(20) NOT NULL,\r\nPRIMARY KEY (id),\r\nCONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id) ON DELETE CASCADE ON UPDATE NO ACTION,\r\nINDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment ''\r\n) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;", true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION,INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, - // for issue 1802 - {`CREATE TABLE t1 ( - accout_id int(11) DEFAULT '0', - summoner_id int(11) DEFAULT '0', - union_name varbinary(52) NOT NULL, - union_id int(11) DEFAULT '0', - PRIMARY KEY (union_name)) ENGINE=MyISAM DEFAULT CHARSET=binary;`, true, "CREATE TABLE `t1` (`accout_id` INT(11) DEFAULT _UTF8MB4'0',`summoner_id` INT(11) DEFAULT _UTF8MB4'0',`union_name` VARBINARY(52) NOT NULL,`union_id` INT(11) DEFAULT _UTF8MB4'0',PRIMARY KEY(`union_name`)) ENGINE = MyISAM DEFAULT CHARACTER SET = BINARY"}, - // for issue pingcap/parser#310 - {`CREATE TABLE t (a DECIMAL(20,0), b DECIMAL(30), c FLOAT(25,0))`, true, "CREATE TABLE `t` (`a` DECIMAL(20,0),`b` DECIMAL(30),`c` FLOAT(25,0))"}, - // Create table with multiple index options. - {`create table t (c int, index ci (c) USING BTREE COMMENT "123");`, true, "CREATE TABLE `t` (`c` INT,INDEX `ci`(`c`) USING BTREE COMMENT '123')"}, - // for default value - {"CREATE TABLE sbtest (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, k integer UNSIGNED DEFAULT '0' NOT NULL, c char(120) DEFAULT '' NOT NULL, pad char(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) )", true, "CREATE TABLE `sbtest` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,`k` INT UNSIGNED DEFAULT _UTF8MB4'0' NOT NULL,`c` CHAR(120) DEFAULT _UTF8MB4'' NOT NULL,`pad` CHAR(60) DEFAULT _UTF8MB4'' NOT NULL,PRIMARY KEY(`id`))"}, - {"create table test (create_date TIMESTAMP NOT NULL COMMENT '创建日期 create date' DEFAULT now());", true, "CREATE TABLE `test` (`create_date` TIMESTAMP NOT NULL COMMENT '创建日期 create date' DEFAULT CURRENT_TIMESTAMP())"}, - {"create table ts (t int, v timestamp(3) default CURRENT_TIMESTAMP(3));", true, "CREATE TABLE `ts` (`t` INT,`v` TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3))"}, // TODO: The number yacc in parentheses has not been implemented yet. - // Create table with primary key name. - {"create table if not exists `t` (`id` int not null auto_increment comment '消息ID', primary key `pk_id` (`id`) );", true, "CREATE TABLE IF NOT EXISTS `t` (`id` INT NOT NULL AUTO_INCREMENT COMMENT '消息ID',PRIMARY KEY `pk_id`(`id`))"}, - // Create table with like. - {"create table a like b", true, "CREATE TABLE `a` LIKE `b`"}, - {"create table a (id int REFERENCES a (id) ON delete NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE NO ACTION)"}, - {"create table a (id int REFERENCES a (id) ON update set default )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON UPDATE SET DEFAULT)"}, - {"create table a (id int REFERENCES a (id) ON delete set null on update CASCADE)", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE SET NULL ON UPDATE CASCADE)"}, - {"create table a (id int REFERENCES a (id) ON update set default on delete RESTRICT)", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE RESTRICT ON UPDATE SET DEFAULT)"}, - {"create table a (id int REFERENCES a (id) MATCH FULL ON delete NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH FULL ON DELETE NO ACTION)"}, - {"create table a (id int REFERENCES a (id) MATCH PARTIAL ON update NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH PARTIAL ON UPDATE NO ACTION)"}, - {"create table a (id int REFERENCES a (id) MATCH SIMPLE ON update NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH SIMPLE ON UPDATE NO ACTION)"}, - {"create table a (id int REFERENCES a (id) ON update set default )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON UPDATE SET DEFAULT)"}, - {"create table a (id int REFERENCES a (id) ON update set default on update CURRENT_TIMESTAMP)", false, ""}, - {"create table a (id int REFERENCES a (id) ON delete set default on update CURRENT_TIMESTAMP)", false, ""}, - {"create table a (like b)", true, "CREATE TABLE `a` LIKE `b`"}, - {"create table if not exists a like b", true, "CREATE TABLE IF NOT EXISTS `a` LIKE `b`"}, - {"create table if not exists a (like b)", true, "CREATE TABLE IF NOT EXISTS `a` LIKE `b`"}, - {"create table if not exists a like (b)", false, ""}, - {"create table a (t int) like b", false, ""}, - {"create table a (t int) like (b)", false, ""}, - // Create table with select statement - {"create table a select * from b", true, "CREATE TABLE `a` AS SELECT * FROM `b`"}, - {"create table a as select * from b", true, "CREATE TABLE `a` AS SELECT * FROM `b`"}, - {"create table a (m int, n datetime) as select * from b", true, "CREATE TABLE `a` (`m` INT,`n` DATETIME) AS SELECT * FROM `b`"}, - {"create table a (unique(n)) as select n from b", true, "CREATE TABLE `a` (UNIQUE(`n`)) AS SELECT `n` FROM `b`"}, - {"create table a ignore as select n from b", true, "CREATE TABLE `a` IGNORE AS SELECT `n` FROM `b`"}, - {"create table a replace as select n from b", true, "CREATE TABLE `a` REPLACE AS SELECT `n` FROM `b`"}, - {"create table a (m int) replace as (select n as m from b union select n+1 as m from c group by 1 limit 2)", true, "CREATE TABLE `a` (`m` INT) REPLACE AS (SELECT `n` AS `m` FROM `b` UNION SELECT `n`+1 AS `m` FROM `c` GROUP BY 1 LIMIT 2)"}, - - // Create table with no option is valid for parser - {"create table a", true, "CREATE TABLE `a`"}, - - {"create table t (a timestamp default now)", false, ""}, - {"create table t (a timestamp default now())", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP())"}, - {"create table t (a timestamp default (((now()))))", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP())"}, - {"create table t (a timestamp default now() on update now)", false, ""}, - {"create table t (a timestamp default now() on update now())", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP())"}, - {"create table t (a timestamp default now() on update (now()))", false, ""}, - {"CREATE TABLE t (c TEXT) default CHARACTER SET utf8, default COLLATE utf8_general_ci;", true, "CREATE TABLE `t` (`c` TEXT) DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI"}, - {"CREATE TABLE t (c TEXT) shard_row_id_bits = 1;", true, "CREATE TABLE `t` (`c` TEXT) SHARD_ROW_ID_BITS = 1"}, - {"CREATE TABLE t (c TEXT) shard_row_id_bits = 1, PRE_SPLIT_REGIONS = 1;", true, "CREATE TABLE `t` (`c` TEXT) SHARD_ROW_ID_BITS = 1 PRE_SPLIT_REGIONS = 1"}, - // Create table with ON UPDATE CURRENT_TIMESTAMP(6), specify fraction part. - {"CREATE TABLE IF NOT EXISTS `general_log` (`event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),`user_host` mediumtext NOT NULL,`thread_id` bigint(20) unsigned NOT NULL,`server_id` int(10) unsigned NOT NULL,`command_type` varchar(64) NOT NULL,`argument` mediumblob NOT NULL) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log'", true, "CREATE TABLE IF NOT EXISTS `general_log` (`event_time` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),`user_host` MEDIUMTEXT NOT NULL,`thread_id` BIGINT(20) UNSIGNED NOT NULL,`server_id` INT(10) UNSIGNED NOT NULL,`command_type` VARCHAR(64) NOT NULL,`argument` MEDIUMBLOB NOT NULL) ENGINE = CSV DEFAULT CHARACTER SET = UTF8 COMMENT = 'General log'"}, // TODO: The number yacc in parentheses has not been implemented yet. - // For reference_definition in column_definition. - {"CREATE TABLE followers ( f1 int NOT NULL REFERENCES user_profiles (uid) );", true, "CREATE TABLE `followers` (`f1` INT NOT NULL REFERENCES `user_profiles`(`uid`))"}, - - // For column default expression - {"create table t (a int default rand())", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, - {"create table t (a int default rand(1))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, - {"create table t (a int default (rand()))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, - {"create table t (a int default (rand(1)))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, - {"create table t (a int default (((rand()))))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, - {"create table t (a int default (((rand(1)))))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, - {"create table t (d date default current_date())", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, - {"create table t (d date default current_date)", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, - {"create table t (d date default (current_date()))", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, - {"create table t (d date default (curdate()))", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, - {"create table t (d date default curdate())", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, - - // For table option `ENCRYPTION` - {"create table t (a int) encryption = 'n';", true, "CREATE TABLE `t` (`a` INT) ENCRYPTION = 'n'"}, - {"create table t (a int) encryption 'n';", true, "CREATE TABLE `t` (`a` INT) ENCRYPTION = 'n'"}, - {"alter table t encryption = 'y';", true, "ALTER TABLE `t` ENCRYPTION = 'y'"}, - {"alter table t encryption 'y';", true, "ALTER TABLE `t` ENCRYPTION = 'y'"}, - - // for alter database/schema/table - {"ALTER DATABASE t CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, - {"ALTER DATABASE CHARACTER SET = 'utf8'", true, "ALTER DATABASE CHARACTER SET = utf8"}, - {"ALTER DATABASE t DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, - {"ALTER SCHEMA t DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, - {"ALTER SCHEMA DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE CHARACTER SET = utf8"}, - {"ALTER SCHEMA t DEFAULT CHARSET = 'UTF8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, - - {"ALTER DATABASE t COLLATE = binary", true, "ALTER DATABASE `t` COLLATE = binary"}, - {"ALTER DATABASE t CHARSET=binary COLLATE = binary", true, "ALTER DATABASE `t` CHARACTER SET = binary COLLATE = binary"}, - - {"ALTER DATABASE t COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, - {"ALTER DATABASE COLLATE = 'utf8_bin'", true, "ALTER DATABASE COLLATE = utf8_bin"}, - {"ALTER DATABASE t DEFAULT COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, - {"ALTER SCHEMA t DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, - {"ALTER SCHEMA DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE COLLATE = utf8_bin"}, - {"ALTER SCHEMA `` DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE `` COLLATE = utf8_bin"}, - - {"ALTER DATABASE t CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` CHARACTER SET = utf8mb4 COLLATE = utf8_bin"}, - { - "ALTER DATABASE t DEFAULT CHARSET = 'utf8mb4' DEFAULT COLLATE = 'utf8mb4_general_ci' CHARACTER SET = 'utf8' COLLATE = 'utf8mb4_bin'", - true, - "ALTER DATABASE `t` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci CHARACTER SET = utf8 COLLATE = utf8mb4_bin", - }, - {"ALTER DATABASE DEFAULT CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", true, "ALTER DATABASE CHARACTER SET = utf8mb4 COLLATE = utf8_bin"}, - { - "ALTER DATABASE DEFAULT CHARSET = 'utf8mb4' DEFAULT COLLATE = 'utf8mb4_general_ci' CHARACTER SET = 'utf8' COLLATE = 'utf8mb4_bin'", - true, - "ALTER DATABASE CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci CHARACTER SET = utf8 COLLATE = utf8mb4_bin", - }, - - {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED)", true, "ALTER TABLE `t` ADD COLUMN (`a` SMALLINT UNSIGNED)"}, - {"ALTER TABLE t.* ADD COLUMN (a SMALLINT UNSIGNED)", false, ""}, - {"ALTER TABLE t ADD COLUMN IF NOT EXISTS (a SMALLINT UNSIGNED)", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS (`a` SMALLINT UNSIGNED)"}, - {"ALTER TABLE ADD COLUMN (a SMALLINT UNSIGNED)", false, ""}, - {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED, b varchar(255))", true, "ALTER TABLE `t` ADD COLUMN (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, - {"ALTER TABLE t ADD COLUMN IF NOT EXISTS (a SMALLINT UNSIGNED, b varchar(255))", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, - {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED FIRST)", false, ""}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED FIRST"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED AFTER b", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED AFTER `b`"}, - {"ALTER TABLE t ADD COLUMN IF NOT EXISTS a SMALLINT UNSIGNED AFTER b", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS `a` SMALLINT UNSIGNED AFTER `b`"}, - {"ALTER TABLE employees ADD PARTITION", true, "ALTER TABLE `employees` ADD PARTITION"}, - {"ALTER TABLE employees ADD PARTITION ( PARTITION P1 VALUES LESS THAN (2010))", true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010))"}, - {"ALTER TABLE employees ADD PARTITION ( PARTITION P2 VALUES LESS THAN MAXVALUE)", true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, - {"ALTER TABLE employees ADD PARTITION IF NOT EXISTS ( PARTITION P2 VALUES LESS THAN MAXVALUE)", true, "ALTER TABLE `employees` ADD PARTITION IF NOT EXISTS (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, - {"ALTER TABLE employees ADD PARTITION IF NOT EXISTS PARTITIONS 5", true, "ALTER TABLE `employees` ADD PARTITION IF NOT EXISTS PARTITIONS 5"}, - {`ALTER TABLE employees ADD PARTITION ( - PARTITION P1 VALUES LESS THAN (2010), - PARTITION P2 VALUES LESS THAN (2015), - PARTITION P3 VALUES LESS THAN MAXVALUE)`, true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010), PARTITION `P2` VALUES LESS THAN (2015), PARTITION `P3` VALUES LESS THAN (MAXVALUE))"}, - {"alter table t add partition (partition x values in ((3, 4), (5, 6)))", true, "ALTER TABLE `t` ADD PARTITION (PARTITION `x` VALUES IN ((3, 4), (5, 6)))"}, - {"ALTER TABLE employees ADD PARTITION NO_WRITE_TO_BINLOG", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG"}, - {"ALTER TABLE employees ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10"}, - // LOCAL is alias to NO_WRITE_TO_BINLOG - {"ALTER TABLE employees ADD PARTITION LOCAL", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG"}, - {"ALTER TABLE employees ADD PARTITION LOCAL PARTITIONS 10", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10"}, - - // For rebuild table partition statement. - {"ALTER TABLE t_n REBUILD PARTITION ALL", true, "ALTER TABLE `t_n` REBUILD PARTITION ALL"}, - {"ALTER TABLE d_n.t_n REBUILD PARTITION LOCAL ALL", true, "ALTER TABLE `d_n`.`t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG ALL"}, - {"ALTER TABLE t_n REBUILD PARTITION LOCAL ident", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `ident`"}, - {"ALTER TABLE t_n REBUILD PARTITION NO_WRITE_TO_BINLOG ident , ident", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `ident`,`ident`"}, - // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), - // and the remains should re recognized as identifier, used as partition name here. - {"ALTER TABLE t_n REBUILD PARTITION LOCAL", false, ""}, - {"ALTER TABLE t_n REBUILD PARTITION LOCAL local", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `local`"}, - {"ALTER TABLE t_n REBUILD PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, - - // For drop table partition statement. - {"alter table t drop partition p1;", true, "ALTER TABLE `t` DROP PARTITION `p1`"}, - {"alter table t drop partition p2;", true, "ALTER TABLE `t` DROP PARTITION `p2`"}, - {"alter table t drop partition if exists p2;", true, "ALTER TABLE `t` DROP PARTITION IF EXISTS `p2`"}, - {"alter table t drop partition p1, p2;", true, "ALTER TABLE `t` DROP PARTITION `p1`,`p2`"}, - {"alter table t drop partition if exists p1, p2;", true, "ALTER TABLE `t` DROP PARTITION IF EXISTS `p1`,`p2`"}, - // For check table partition statement - {"alter table t check partition all;", true, "ALTER TABLE `t` CHECK PARTITION ALL"}, - {"alter table t check partition p;", true, "ALTER TABLE `t` CHECK PARTITION `p`"}, - {"alter table t check partition p1, p2;", true, "ALTER TABLE `t` CHECK PARTITION `p1`,`p2`"}, - {"alter table employees add partition partitions 1;", true, "ALTER TABLE `employees` ADD PARTITION PARTITIONS 1"}, - {"alter table employees add partition partitions 2;", true, "ALTER TABLE `employees` ADD PARTITION PARTITIONS 2"}, - {"alter table clients coalesce partition 3;", true, "ALTER TABLE `clients` COALESCE PARTITION 3"}, - {"alter table clients coalesce partition 4;", true, "ALTER TABLE `clients` COALESCE PARTITION 4"}, - {"alter table clients coalesce partition no_write_to_binlog 4;", true, "ALTER TABLE `clients` COALESCE PARTITION NO_WRITE_TO_BINLOG 4"}, - {"alter table clients coalesce partition local 4;", true, "ALTER TABLE `clients` COALESCE PARTITION NO_WRITE_TO_BINLOG 4"}, - {"ALTER TABLE t DISABLE KEYS", true, "ALTER TABLE `t` DISABLE KEYS"}, - {"ALTER TABLE t ENABLE KEYS", true, "ALTER TABLE `t` ENABLE KEYS"}, - {"ALTER TABLE t MODIFY COLUMN a varchar(255)", true, "ALTER TABLE `t` MODIFY COLUMN `a` VARCHAR(255)"}, - {"ALTER TABLE t MODIFY COLUMN IF EXISTS a varchar(255)", true, "ALTER TABLE `t` MODIFY COLUMN IF EXISTS `a` VARCHAR(255)"}, - {"ALTER TABLE t CHANGE COLUMN a b varchar(255)", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255)"}, - {"ALTER TABLE t CHANGE COLUMN IF EXISTS a b varchar(255)", true, "ALTER TABLE `t` CHANGE COLUMN IF EXISTS `a` `b` VARCHAR(255)"}, - {"ALTER TABLE t CHANGE COLUMN a b varchar(255) CHARACTER SET UTF8 BINARY", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255) BINARY CHARACTER SET UTF8"}, - {"ALTER TABLE t CHANGE COLUMN a b varchar(255) FIRST", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255) FIRST"}, - - // For alter table rename statement. - {"ALTER TABLE db.t RENAME to db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, - {"ALTER TABLE db.t RENAME db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, - {"ALTER TABLE db.t RENAME = db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, - {"ALTER TABLE db.t RENAME as db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, - {"ALTER TABLE t RENAME to t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, - {"ALTER TABLE t RENAME t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, - {"ALTER TABLE t RENAME = t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, - {"ALTER TABLE t RENAME as t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, - - // For #499, alter table order by - {"ALTER TABLE t_n ORDER BY ident", true, "ALTER TABLE `t_n` ORDER BY `ident`"}, - {"ALTER TABLE t_n ORDER BY ident ASC", true, "ALTER TABLE `t_n` ORDER BY `ident`"}, - {"ALTER TABLE t_n ORDER BY ident DESC", true, "ALTER TABLE `t_n` ORDER BY `ident` DESC"}, - {"ALTER TABLE t_n ORDER BY ident1, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, - {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, - {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, - {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2` DESC"}, - {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2`"}, - {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2`"}, - {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2` DESC"}, - {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, - {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, - {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3` DESC"}, - {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 ASC, ident3 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, - {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 DESC, ident3 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2` DESC,`ident3` DESC"}, - - // For alter table rename column statement. - {"ALTER TABLE t RENAME COLUMN a TO b", true, "ALTER TABLE `t` RENAME COLUMN `a` TO `b`"}, - {"ALTER TABLE t RENAME COLUMN t.a TO t.b", false, ""}, - {"ALTER TABLE t RENAME COLUMN a TO t.b", false, ""}, - {"ALTER TABLE t RENAME COLUMN t.a TO b", false, ""}, - - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT 1", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, - {"ALTER TABLE t ALTER a SET DEFAULT 1", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT CURRENT_TIMESTAMP", false, ""}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT NOW()", false, ""}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT 1+1", false, ""}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (CURRENT_TIMESTAMP())", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (CURRENT_TIMESTAMP())"}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (NOW())", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (NOW())"}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (1+1)", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (1+1)"}, - {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (1)", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, - {"ALTER TABLE t ALTER COLUMN a DROP DEFAULT", true, "ALTER TABLE `t` ALTER COLUMN `a` DROP DEFAULT"}, - {"ALTER TABLE t ALTER a DROP DEFAULT", true, "ALTER TABLE `t` ALTER COLUMN `a` DROP DEFAULT"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=none", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=default", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=shared", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=exclusive", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock none", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock default", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock shared", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock exclusive", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=NONE", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=DEFAULT", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=SHARED", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=EXCLUSIVE", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, - {"ALTER TABLE t ADD FULLTEXT KEY `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, - {"ALTER TABLE t ADD FULLTEXT `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, - {"ALTER TABLE t ADD FULLTEXT INDEX `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, - {"ALTER TABLE t ADD INDEX (a) USING BTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING BTREE COMMENT 'a'"}, - {"ALTER TABLE t ADD INDEX IF NOT EXISTS (a) USING BTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX IF NOT EXISTS(`a`) USING BTREE COMMENT 'a'"}, - {"ALTER TABLE t ADD INDEX (a) USING RTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING RTREE COMMENT 'a'"}, - {"ALTER TABLE t ADD KEY (a) USING HASH COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING HASH COMMENT 'a'"}, - {"ALTER TABLE t ADD KEY IF NOT EXISTS (a) USING HASH COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX IF NOT EXISTS(`a`) USING HASH COMMENT 'a'"}, - {"ALTER TABLE t ADD PRIMARY KEY ident USING RTREE ( a DESC , b )", true, "ALTER TABLE `t` ADD PRIMARY KEY `ident`(`a` DESC, `b`) USING RTREE"}, - {"ALTER TABLE t ADD KEY USING RTREE ( a ) ", true, "ALTER TABLE `t` ADD INDEX(`a`) USING RTREE"}, - {"ALTER TABLE t ADD KEY USING RTREE ( ident ASC , ident ( 123 ) )", true, "ALTER TABLE `t` ADD INDEX(`ident`, `ident`(123)) USING RTREE"}, - {"ALTER TABLE t ADD PRIMARY KEY (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`) COMMENT 'a'"}, - {"ALTER TABLE t ADD UNIQUE (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, - {"ALTER TABLE t ADD UNIQUE KEY (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, - {"ALTER TABLE t ADD UNIQUE INDEX (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, - {"ALTER TABLE t ADD CONSTRAINT fk_t2_id FOREIGN KEY (t2_id) REFERENCES t(id)", true, "ALTER TABLE `t` ADD CONSTRAINT `fk_t2_id` FOREIGN KEY (`t2_id`) REFERENCES `t`(`id`)"}, - {"ALTER TABLE t ADD CONSTRAINT fk_t2_id FOREIGN KEY IF NOT EXISTS (t2_id) REFERENCES t(id)", true, "ALTER TABLE `t` ADD CONSTRAINT `fk_t2_id` FOREIGN KEY IF NOT EXISTS (`t2_id`) REFERENCES `t`(`id`)"}, - {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1) NOT ENFORCED, ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) NOT ENFORCED, ADD UNIQUE(`a`)"}, - {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1) ENFORCED, ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) ENFORCED, ADD UNIQUE(`a`)"}, - {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1), ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) ENFORCED, ADD UNIQUE(`a`)"}, - {"ALTER TABLE t ENGINE ''", true, "ALTER TABLE `t` ENGINE = ''"}, - {"ALTER TABLE t ENGINE = ''", true, "ALTER TABLE `t` ENGINE = ''"}, - {"ALTER TABLE t ENGINE = 'innodb'", true, "ALTER TABLE `t` ENGINE = innodb"}, - {"ALTER TABLE t ENGINE = innodb", true, "ALTER TABLE `t` ENGINE = innodb"}, - {"ALTER TABLE `db`.`t` ENGINE = ``", true, "ALTER TABLE `db`.`t` ENGINE = ''"}, - {"ALTER TABLE t INSERT_METHOD = FIRST", true, "ALTER TABLE `t` INSERT_METHOD = FIRST"}, - {"ALTER TABLE t INSERT_METHOD LAST", true, "ALTER TABLE `t` INSERT_METHOD = LAST"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, ADD COLUMN `a` SMALLINT"}, - {"ALTER TABLE t ADD COLUMN a SMALLINT, ENGINE = '', default COLLATE = UTF8_GENERAL_CI", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT, ENGINE = '', DEFAULT COLLATE = UTF8_GENERAL_CI"}, - {"ALTER TABLE t ENGINE = '', COMMENT='', default COLLATE = UTF8_GENERAL_CI", true, "ALTER TABLE `t` ENGINE = '', COMMENT = '', DEFAULT COLLATE = UTF8_GENERAL_CI"}, - {"ALTER TABLE t ENGINE = '', ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` ENGINE = '', ADD COLUMN `a` SMALLINT"}, - {"ALTER TABLE t default COLLATE = UTF8_GENERAL_CI, ENGINE = '', ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` DEFAULT COLLATE = UTF8_GENERAL_CI, ENGINE = '', ADD COLUMN `a` SMALLINT"}, - {"ALTER TABLE t shard_row_id_bits = 1", true, "ALTER TABLE `t` SHARD_ROW_ID_BITS = 1"}, - {"ALTER TABLE t AUTO_INCREMENT 3", true, "ALTER TABLE `t` AUTO_INCREMENT = 3"}, - {"ALTER TABLE t AUTO_INCREMENT = 3", true, "ALTER TABLE `t` AUTO_INCREMENT = 3"}, - {"ALTER TABLE t FORCE AUTO_INCREMENT 3", true, "ALTER TABLE `t` FORCE AUTO_INCREMENT = 3"}, - {"ALTER TABLE t FORCE AUTO_INCREMENT = 3", true, "ALTER TABLE `t` FORCE AUTO_INCREMENT = 3"}, - {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = DEFAULT;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = DEFAULT"}, - {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = INPLACE;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = INPLACE"}, - {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = COPY;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = COPY"}, - {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL, ALGORITHM = INSTANT;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = INSTANT"}, - {"ALTER TABLE t CONVERT TO CHARACTER SET UTF8;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8"}, - {"ALTER TABLE t CONVERT TO CHARSET UTF8;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8"}, - {"ALTER TABLE t CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, - {"ALTER TABLE t CONVERT TO CHARSET UTF8 COLLATE UTF8_BIN;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, - - // alter table convert to character set default, issue #498 - {"alter table d_n.t_n convert to character set default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, - {"alter table d_n.t_n convert to charset default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, - {"alter table d_n.t_n convert to char set default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, - {"alter table d_n.t_n convert to character set default collate utf8mb4_0900_ai_ci", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT COLLATE UTF8MB4_0900_AI_CI"}, - - {"ALTER TABLE t FORCE", true, "ALTER TABLE `t` FORCE /* AlterTableForce is not supported */ "}, - {"ALTER TABLE t DROP INDEX;", false, "ALTER TABLE `t` DROP INDEX"}, - {"ALTER TABLE t DROP INDEX a", true, "ALTER TABLE `t` DROP INDEX `a`"}, - {"ALTER TABLE t DROP INDEX IF EXISTS a", true, "ALTER TABLE `t` DROP INDEX IF EXISTS `a`"}, - - // For alter table alter index statement - {"ALTER TABLE t ALTER INDEX a INVISIBLE", true, "ALTER TABLE `t` ALTER INDEX `a` INVISIBLE"}, - {"ALTER TABLE t ALTER INDEX a VISIBLE", true, "ALTER TABLE `t` ALTER INDEX `a` VISIBLE"}, - - {"ALTER TABLE t DROP FOREIGN KEY a", true, "ALTER TABLE `t` DROP FOREIGN KEY `a`"}, - {"ALTER TABLE t DROP FOREIGN KEY IF EXISTS a", true, "ALTER TABLE `t` DROP FOREIGN KEY IF EXISTS `a`"}, - {"ALTER TABLE t DROP COLUMN a CASCADE", true, "ALTER TABLE `t` DROP COLUMN `a`"}, - {"ALTER TABLE t DROP COLUMN IF EXISTS a CASCADE", true, "ALTER TABLE `t` DROP COLUMN IF EXISTS `a`"}, - {`ALTER TABLE testTableCompression COMPRESSION="LZ4";`, true, "ALTER TABLE `testTableCompression` COMPRESSION = 'LZ4'"}, - {`ALTER TABLE t1 COMPRESSION="zlib";`, true, "ALTER TABLE `t1` COMPRESSION = 'zlib'"}, - {"ALTER TABLE t1", true, "ALTER TABLE `t1`"}, - {"ALTER TABLE t1 ,", false, ""}, - - // For #6405 - {"ALTER TABLE t RENAME KEY a TO b;", true, "ALTER TABLE `t` RENAME INDEX `a` TO `b`"}, - {"ALTER TABLE t RENAME INDEX a TO b;", true, "ALTER TABLE `t` RENAME INDEX `a` TO `b`"}, - - // For #497, support `ALTER TABLE ALTER CHECK` and `ALTER TABLE DROP CHECK` syntax - {"ALTER TABLE d_n.t_n DROP CHECK ident;", true, "ALTER TABLE `d_n`.`t_n` DROP CHECK `ident`"}, - {"ALTER TABLE t_n LOCK = DEFAULT , DROP CHECK ident;", true, "ALTER TABLE `t_n` LOCK = DEFAULT, DROP CHECK `ident`"}, - {"ALTER TABLE t_n ALTER CHECK ident ENFORCED;", true, "ALTER TABLE `t_n` ALTER CHECK `ident` ENFORCED"}, - {"ALTER TABLE t_n ALTER CHECK ident NOT ENFORCED;", true, "ALTER TABLE `t_n` ALTER CHECK `ident` NOT ENFORCED"}, - {"ALTER TABLE t_n DROP CONSTRAINT ident", true, "ALTER TABLE `t_n` DROP CHECK `ident`"}, - {"ALTER TABLE t_n DROP CHECK ident", true, "ALTER TABLE `t_n` DROP CHECK `ident`"}, - {"ALTER TABLE t_n ALTER CONSTRAINT ident", false, ""}, - {"ALTER TABLE t_n ALTER CONSTRAINT ident enforced", true, "ALTER TABLE `t_n` ALTER CHECK `ident` ENFORCED"}, - {"ALTER TABLE t_n ALTER CHECK ident not enforced", true, "ALTER TABLE `t_n` ALTER CHECK `ident` NOT ENFORCED"}, - - {"alter table t analyze partition a", true, "ANALYZE TABLE `t` PARTITION `a`"}, - {"alter table t analyze partition a with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` WITH 4 BUCKETS"}, - {"alter table t analyze partition a index b", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b`"}, - {"alter table t analyze partition a index b with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b` WITH 4 BUCKETS"}, - - {"alter table t partition by hash(a)", true, "ALTER TABLE `t` PARTITION BY HASH (`a`) PARTITIONS 1"}, - {"alter table t add column a int partition by hash(a)", true, "ALTER TABLE `t` ADD COLUMN `a` INT PARTITION BY HASH (`a`) PARTITIONS 1"}, - {"alter table t partition by range(a)", false, ""}, - {"alter table t partition by range(a) (partition x values less than (75))", true, "ALTER TABLE `t` PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (75))"}, - {"alter table t add column a int, partition by range(a) (partition x values less than (75))", false, ""}, - {"alter table t comment 'cmt' partition by hash(a)", true, "ALTER TABLE `t` COMMENT = 'cmt' PARTITION BY HASH (`a`) PARTITIONS 1"}, - {"alter table t enable keys, comment = 'cmt' partition by hash(a)", true, "ALTER TABLE `t` ENABLE KEYS, COMMENT = 'cmt' PARTITION BY HASH (`a`) PARTITIONS 1"}, - {"alter table t enable keys, comment = 'cmt', partition by hash(a)", false, ""}, - {"alter table t partition by hash(a) enable keys", false, ""}, - {"alter table t partition by hash(a), enable keys", false, ""}, - - // Test keyword `FIELDS` - {"alter table t partition by range FIELDS(a) (partition x values less than maxvalue)", true, "ALTER TABLE `t` PARTITION BY RANGE COLUMNS (`a`) (PARTITION `x` VALUES LESS THAN (MAXVALUE))"}, - {"alter table t partition by list FIELDS(a) (PARTITION p0 VALUES IN (5, 10, 15))", true, "ALTER TABLE `t` PARTITION BY LIST COLUMNS (`a`) (PARTITION `p0` VALUES IN (5, 10, 15))"}, - {"alter table t partition by range FIELDS(a,b,c) (partition p1 values less than (1,1,1));", true, "ALTER TABLE `t` PARTITION BY RANGE COLUMNS (`a`,`b`,`c`) (PARTITION `p1` VALUES LESS THAN (1, 1, 1))"}, - {"alter table t partition by list FIELDS(a,b,c) (PARTITION p0 VALUES IN ((5, 10, 15)))", true, "ALTER TABLE `t` PARTITION BY LIST COLUMNS (`a`,`b`,`c`) (PARTITION `p0` VALUES IN ((5, 10, 15)))"}, - - {"alter table t with validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITH VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, - {"alter table t without validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITHOUT VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, - {"alter table t without validation, with validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITHOUT VALIDATION, WITH VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, - {"alter table t with validation, modify column b int as (a + 2) ", true, "ALTER TABLE `t` WITH VALIDATION, MODIFY COLUMN `b` INT GENERATED ALWAYS AS(`a`+2) VIRTUAL"}, - {"alter table t with validation, change column b c int as (a + 2)", true, "ALTER TABLE `t` WITH VALIDATION, CHANGE COLUMN `b` `c` INT GENERATED ALWAYS AS(`a`+2) VIRTUAL"}, - - {"ALTER TABLE d_n.t_n ADD PARTITION NO_WRITE_TO_BINLOG", true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION NO_WRITE_TO_BINLOG"}, - {"ALTER TABLE d_n.t_n ADD PARTITION LOCAL", true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION NO_WRITE_TO_BINLOG"}, - - {"alter table t with validation, exchange partition p with table nt without validation;", true, "ALTER TABLE `t` WITH VALIDATION, EXCHANGE PARTITION `p` WITH TABLE `nt` WITHOUT VALIDATION"}, - {"alter table t exchange partition p with table nt with validation;", true, "ALTER TABLE `t` EXCHANGE PARTITION `p` WITH TABLE `nt`"}, - - // For reorganize partition statement - {"alter table t reorganize partition;", true, "ALTER TABLE `t` REORGANIZE PARTITION"}, - {"alter table t reorganize partition local;", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG"}, - {"alter table t reorganize partition no_write_to_binlog;", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG"}, - {"ALTER TABLE members REORGANIZE PARTITION n0 INTO (PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION `n0` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, - {"ALTER TABLE members REORGANIZE PARTITION LOCAL n0 INTO (PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION NO_WRITE_TO_BINLOG `n0` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, - {"ALTER TABLE members REORGANIZE PARTITION p1,p2,p3 INTO ( PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION `p1`,`p2`,`p3` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, - {"alter table t reorganize partition remove partition;", false, ""}, - {"alter table t reorganize partition no_write_to_binlog remove into (partition p0 VALUES LESS THAN (1991));", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG `remove` INTO (PARTITION `p0` VALUES LESS THAN (1991))"}, - - // alter attributes - {"ALTER TABLE t ATTRIBUTES='str'", true, "ALTER TABLE `t` ATTRIBUTES='str'"}, - {"ALTER TABLE t ATTRIBUTES='str1,str2'", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t ATTRIBUTES=\"str1,str2\"", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t ATTRIBUTES 'str1,str2'", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t ATTRIBUTES \"str1,str2\"", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t ATTRIBUTES=DEFAULT", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t ATTRIBUTES=default", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t ATTRIBUTES=DeFaUlT", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t ATTRIBUTES", false, ""}, - {"ALTER TABLE t PARTITION p ATTRIBUTES='str'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str'"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES='str1,str2'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES=\"str1,str2\"", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES 'str1,str2'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES \"str1,str2\"", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES=DEFAULT", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES=default", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES=DeFaUlT", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, - {"ALTER TABLE t PARTITION p ATTRIBUTES", false, ""}, - // For https://github.com/pingcap/tidb/issues/26778 - {"CREATE TABLE t1 (attributes int);", true, "CREATE TABLE `t1` (`attributes` INT)"}, - - // For create index statement - {"CREATE INDEX idx ON t (a)", true, "CREATE INDEX `idx` ON `t` (`a`)"}, - {"CREATE INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, - {"CREATE UNIQUE INDEX idx ON t (a)", true, "CREATE UNIQUE INDEX `idx` ON `t` (`a`)"}, - {"CREATE UNIQUE INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE UNIQUE INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, - {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE BTREE", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, - {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE HASH", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING HASH"}, - {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE RTREE", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING RTREE"}, - {"CREATE UNIQUE INDEX ident TYPE BTREE ON d_n.t_n ( ident , ident ASC )", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, - {"CREATE UNIQUE INDEX ident USING BTREE ON d_n.t_n ( ident , ident ASC )", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, - {"CREATE SPATIAL INDEX idx ON t (a)", true, "CREATE SPATIAL INDEX `idx` ON `t` (`a`)"}, - {"CREATE SPATIAL INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE SPATIAL INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, - {"CREATE FULLTEXT INDEX idx ON t (a)", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`)"}, - {"CREATE FULLTEXT INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE FULLTEXT INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, - {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident`"}, - {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident comment 'string'", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, - {"CREATE FULLTEXT INDEX idx ON t (a) comment 'string' with parser ident", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, - {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident comment 'string' lock default", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, - {"CREATE INDEX idx ON t (a) USING HASH", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH"}, - {"CREATE INDEX idx ON t (a) COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) COMMENT 'foo'"}, - {"CREATE INDEX idx ON t (a) USING HASH COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH COMMENT 'foo'"}, - {"CREATE INDEX idx ON t (a) LOCK=NONE", true, "CREATE INDEX `idx` ON `t` (`a`) LOCK = NONE"}, - {"CREATE INDEX idx USING BTREE ON t (a) USING HASH COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH COMMENT 'foo'"}, - {"CREATE INDEX idx USING BTREE ON t (a)", true, "CREATE INDEX `idx` ON `t` (`a`) USING BTREE"}, - {"CREATE INDEX idx ON t ( a ) VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) VISIBLE"}, - {"CREATE INDEX idx ON t ( a ) INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) INVISIBLE"}, - {"CREATE INDEX idx ON t ( a ) INVISIBLE VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) VISIBLE"}, - {"CREATE INDEX idx ON t ( a ) VISIBLE INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) INVISIBLE"}, - {"CREATE INDEX idx ON t ( a ) USING HASH VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH VISIBLE"}, - {"CREATE INDEX idx ON t ( a ) USING HASH INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH INVISIBLE"}, - - // For create index with algorithm - {"CREATE INDEX idx ON t ( a ) ALGORITHM = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM = INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM = COPY", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = COPY"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM COPY", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = COPY"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM = DEFAULT LOCK = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, - {"CREATE INDEX idx ON t ( a ) LOCK = DEFAULT ALGORITHM = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM = INPLACE LOCK = EXCLUSIVE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, - {"CREATE INDEX idx ON t ( a ) LOCK = EXCLUSIVE ALGORITHM = INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM = ident", false, ""}, - {"CREATE INDEX idx ON t ( a ) ALGORITHM ident", false, ""}, - - // For dorp index statement - {"drop index a on t", true, "DROP INDEX `a` ON `t`"}, - {"drop index a on db.t", true, "DROP INDEX `a` ON `db`.`t`"}, - {"drop index a on db.`tb-ttb`", true, "DROP INDEX `a` ON `db`.`tb-ttb`"}, - {"drop index if exists a on t", true, "DROP INDEX IF EXISTS `a` ON `t`"}, - {"drop index if exists a on db.t", true, "DROP INDEX IF EXISTS `a` ON `db`.`t`"}, - {"drop index if exists a on db.`tb-ttb`", true, "DROP INDEX IF EXISTS `a` ON `db`.`tb-ttb`"}, - {"drop index idx on t algorithm = default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t algorithm default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t algorithm = inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE"}, - {"drop index idx on t algorithm inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE"}, - {"drop index idx on t lock = default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t lock default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t lock = shared", true, "DROP INDEX `idx` ON `t` LOCK = SHARED"}, - {"drop index idx on t lock shared", true, "DROP INDEX `idx` ON `t` LOCK = SHARED"}, - {"drop index idx on t algorithm = default lock = default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t lock = default algorithm = default", true, "DROP INDEX `idx` ON `t`"}, - {"drop index idx on t algorithm = inplace lock = exclusive", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, - {"drop index idx on t lock = exclusive algorithm = inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, - {"drop index idx on t algorithm = algorithm_type", false, ""}, - {"drop index idx on t algorithm algorithm_type", false, ""}, - {"drop index idx on t lock = lock_type", false, ""}, - {"drop index idx on t lock lock_type", false, ""}, - - // for rename table statement - {"RENAME TABLE t TO t1", true, "RENAME TABLE `t` TO `t1`"}, - {"RENAME TABLE t t1", false, "RENAME TABLE `t` TO `t1`"}, - {"RENAME TABLE d.t TO d1.t1", true, "RENAME TABLE `d`.`t` TO `d1`.`t1`"}, - {"RENAME TABLE t1 TO t2, t3 TO t4", true, "RENAME TABLE `t1` TO `t2`, `t3` TO `t4`"}, - - // for truncate statement - {"TRUNCATE TABLE t1", true, "TRUNCATE TABLE `t1`"}, - {"TRUNCATE t1", true, "TRUNCATE TABLE `t1`"}, - - // for empty alert table index - {"ALTER TABLE t ADD INDEX () ", false, ""}, - {"ALTER TABLE t ADD UNIQUE ()", false, ""}, - {"ALTER TABLE t ADD UNIQUE INDEX ()", false, ""}, - {"ALTER TABLE t ADD UNIQUE KEY ()", false, ""}, - - // for keyword `SECONDARY_LOAD`, `SECONDARY_UNLOAD` - {"ALTER TABLE d_n.t_n SECONDARY_LOAD", true, "ALTER TABLE `d_n`.`t_n` SECONDARY_LOAD"}, - {"ALTER TABLE d_n.t_n SECONDARY_UNLOAD", true, "ALTER TABLE `d_n`.`t_n` SECONDARY_UNLOAD"}, - {"ALTER TABLE t_n LOCK = DEFAULT , SECONDARY_LOAD", true, "ALTER TABLE `t_n` LOCK = DEFAULT, SECONDARY_LOAD"}, - {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , SECONDARY_LOAD", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, SECONDARY_LOAD"}, - {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , SECONDARY_UNLOAD", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, SECONDARY_UNLOAD"}, - - // for issue 4538 - {"create table a (process double)", true, "CREATE TABLE `a` (`process` DOUBLE)"}, - - // for issue 4740 - {"create table t (a int1, b int2, c int3, d int4, e int8)", true, "CREATE TABLE `t` (`a` TINYINT,`b` SMALLINT,`c` MEDIUMINT,`d` INT,`e` BIGINT)"}, - - // for issue 5918 - {"create table t (lv long varchar null)", true, "CREATE TABLE `t` (`lv` MEDIUMTEXT NULL)"}, - - // special table name - {"CREATE TABLE cdp_test.`test2-1` (id int(11) DEFAULT NULL,key(id));", true, "CREATE TABLE `cdp_test`.`test2-1` (`id` INT(11) DEFAULT NULL,INDEX(`id`))"}, - {"CREATE TABLE miantiao (`扁豆焖面` INT(11));", true, "CREATE TABLE `miantiao` (`扁豆焖面` INT(11))"}, - - // for create table select - {"CREATE TABLE bar (m INT) SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) AS SELECT `n` FROM `foo`"}, - {"CREATE TABLE bar (m INT) IGNORE SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) IGNORE AS SELECT `n` FROM `foo`"}, - {"CREATE TABLE bar (m INT) REPLACE SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) REPLACE AS SELECT `n` FROM `foo`"}, - - // for generated column definition - {"create table t (a timestamp, b timestamp as (a) not null on update current_timestamp);", false, ""}, - {"create table t (a bigint, b bigint as (a) primary key auto_increment);", false, ""}, - {"create table t (a bigint, b bigint as (a) not null default 10);", false, ""}, - {"create table t (a bigint, b bigint as (a+1) not null);", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL)"}, - {"create table t (a bigint, b bigint as (a+1) not null);", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL)"}, - {"create table t (a bigint, b bigint as (a+1) not null comment 'ttt');", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL COMMENT 'ttt')"}, - {"create table t(a int, index idx((cast(a as binary(1)))));", true, "CREATE TABLE `t` (`a` INT,INDEX `idx`((CAST(`a` AS BINARY(1)))))"}, - {"alter table t add column (f timestamp as (a+1) default '2019-01-01 11:11:11');", false, ""}, - {"alter table t modify column f int as (a+1) default 55;", false, ""}, - - // for column format - {"create table t (a int column_format fixed)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT FIXED)"}, - {"create table t (a int column_format default)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT DEFAULT)"}, - {"create table t (a int column_format dynamic)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT DYNAMIC)"}, - {"alter table t modify column a bigint column_format default", true, "ALTER TABLE `t` MODIFY COLUMN `a` BIGINT COLUMN_FORMAT DEFAULT"}, - - // for recover table - {"recover table by job 11", true, "RECOVER TABLE BY JOB 11"}, - {"recover table by job 11,12,13", false, ""}, - {"recover table by job", false, ""}, - {"recover table t1", true, "RECOVER TABLE `t1`"}, - {"recover table t1,t2", false, ""}, - {"recover table ", false, ""}, - {"recover table t1 100", true, "RECOVER TABLE `t1` 100"}, - {"recover table t1 abc", false, ""}, - - // for flashback table. - {"flashback table t", true, "FLASHBACK TABLE `t`"}, - {"flashback table t TO t1", true, "FLASHBACK TABLE `t` TO `t1`"}, - {"flashback table t TO timestamp", true, "FLASHBACK TABLE `t` TO `timestamp`"}, - - // for flashback database. - {"flashback database db1", true, "FLASHBACK DATABASE `db1`"}, - {"flashback schema db1", true, "FLASHBACK DATABASE `db1`"}, - {"flashback database db1 to db2", true, "FLASHBACK DATABASE `db1` TO `db2`"}, - {"flashback schema db1 to db2", true, "FLASHBACK DATABASE `db1` TO `db2`"}, - - // for flashback to timestamp - {"flashback cluster to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK CLUSTER TO TIMESTAMP '2021-05-26 16:45:26'"}, - {"flashback table t to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK TABLE `t` TO TIMESTAMP '2021-05-26 16:45:26'"}, - {"flashback table t,t1 to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK TABLE `t`, `t1` TO TIMESTAMP '2021-05-26 16:45:26'"}, - {"flashback database test to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK DATABASE `test` TO TIMESTAMP '2021-05-26 16:45:26'"}, - {"flashback schema test to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK DATABASE `test` TO TIMESTAMP '2021-05-26 16:45:26'"}, - {"flashback cluster to timestamp TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", false, ""}, - {"flashback cluster to timestamp DATE_SUB(NOW(), INTERVAL 3 SECOND)", false, ""}, - {"flashback table to timestamp '2021-05-26 16:45:26'", false, ""}, - {"flashback database to timestamp '2021-05-26 16:45:26'", false, ""}, - - // for remove partitioning - {"alter table t remove partitioning", true, "ALTER TABLE `t` REMOVE PARTITIONING"}, - {"alter table db.ident remove partitioning", true, "ALTER TABLE `db`.`ident` REMOVE PARTITIONING"}, - {"alter table t lock = default remove partitioning", true, "ALTER TABLE `t` LOCK = DEFAULT REMOVE PARTITIONING"}, - {"alter table t add column a int remove partitioning", true, "ALTER TABLE `t` ADD COLUMN `a` INT REMOVE PARTITIONING"}, - {"alter table t add column a int, add index (c) remove partitioning", true, "ALTER TABLE `t` ADD COLUMN `a` INT, ADD INDEX(`c`) REMOVE PARTITIONING"}, - {"alter table t add column a int, remove partitioning", false, ""}, - {"alter table t add column a int, add index (c), remove partitioning", false, ""}, - {"alter table t remove partitioning add column a int", false, ""}, - {"alter table t remove partitioning, add column a int", false, ""}, - - // for references without IndexColNameList - {"alter table t add column a double (4,2) zerofill references b match full on update set null first", true, "ALTER TABLE `t` ADD COLUMN `a` DOUBLE(4,2) UNSIGNED ZEROFILL REFERENCES `b` MATCH FULL ON UPDATE SET NULL FIRST"}, - {"alter table d_n.t_n add constraint foreign key ident (ident(1)) references d_n.t_n match full on delete set null", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`(1)) REFERENCES `d_n`.`t_n` MATCH FULL ON DELETE SET NULL"}, - {"alter table t_n add constraint ident foreign key (ident,ident(1)) references t_n match full on update set null on delete restrict", true, "ALTER TABLE `t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`, `ident`(1)) REFERENCES `t_n` MATCH FULL ON DELETE RESTRICT ON UPDATE SET NULL"}, - {"alter table d_n.t_n add foreign key ident (ident, ident(1) asc) references t_n match partial on delete cascade remove partitioning", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`, `ident`(1)) REFERENCES `t_n` MATCH PARTIAL ON DELETE CASCADE REMOVE PARTITIONING"}, - {"alter table d_n.t_n add constraint foreign key (ident asc) references d_n.t_n match simple on update cascade on delete cascade", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT FOREIGN KEY (`ident`) REFERENCES `d_n`.`t_n` MATCH SIMPLE ON DELETE CASCADE ON UPDATE CASCADE"}, - - // for character vary syntax - {"create table t (a character varying(1));", true, "CREATE TABLE `t` (`a` VARCHAR(1))"}, - {"create table t (a character varying(255));", true, "CREATE TABLE `t` (`a` VARCHAR(255))"}, - {"create table t (a char varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a varcharacter(1));", true, "CREATE TABLE `t` (`a` VARCHAR(1))"}, - {"create table t (a varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a varcharacter(1), b varcharacter(255));", true, "CREATE TABLE `t` (`a` VARCHAR(1),`b` VARCHAR(255))"}, - {"create table t (a char);", true, "CREATE TABLE `t` (`a` CHAR)"}, - {"create table t (a character);", true, "CREATE TABLE `t` (`a` CHAR)"}, - {"create table t (a character varying(50), b int);", true, "CREATE TABLE `t` (`a` VARCHAR(50),`b` INT)"}, - {"create table t (a character, b int);", true, "CREATE TABLE `t` (`a` CHAR,`b` INT)"}, - {"create table t (a national character varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a national char varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a national char);", true, "CREATE TABLE `t` (`a` CHAR)"}, - {"create table t (a national character);", true, "CREATE TABLE `t` (`a` CHAR)"}, - {"create table t (a nchar);", true, "CREATE TABLE `t` (`a` CHAR)"}, - {"create table t (a nchar varchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a nchar varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a national varchar);", false, ""}, - {"create table t (a national varchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a national varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a nchar varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table t (a nvarchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, - {"create table nchar (a int);", true, "CREATE TABLE `nchar` (`a` INT)"}, - {"create table nchar (a int, b nchar);", true, "CREATE TABLE `nchar` (`a` INT,`b` CHAR)"}, - {"create table nchar (a int, b nchar(50));", true, "CREATE TABLE `nchar` (`a` INT,`b` CHAR(50))"}, - {"alter table t_n storage disk , modify ident national varcharacter(12) column_format fixed first;", true, "ALTER TABLE `t_n` STORAGE DISK, MODIFY COLUMN `ident` VARCHAR(12) COLUMN_FORMAT FIXED FIRST"}, - - // Test keyword `SERIAL` - {"create table t (a serial);", true, "CREATE TABLE `t` (`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, - {"create table t (a serial null);", true, "CREATE TABLE `t` (`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY NULL)"}, - {"create table t (b int, a serial);", true, "CREATE TABLE `t` (`b` INT,`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, - {"create table t (a int serial default value);", true, "CREATE TABLE `t` (`a` INT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, - {"create table t (a int serial default value null);", true, "CREATE TABLE `t` (`a` INT NOT NULL AUTO_INCREMENT UNIQUE KEY NULL)"}, - {"create table t (a bigint serial default value);", true, "CREATE TABLE `t` (`a` BIGINT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, - {"create table t (a smallint serial default value);", true, "CREATE TABLE `t` (`a` SMALLINT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, - - // for LONG syntax - {"create table t (a long);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, - {"create table t (a long varchar);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, - {"create table t (a long varcharacter);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, - {"create table t (a long char varying);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, - {"create table t (a long character varying);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, - {"create table t (a mediumtext, b long varchar, c long, d long varcharacter, e long char varying, f long character varying, g long);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT,`b` MEDIUMTEXT,`c` MEDIUMTEXT,`d` MEDIUMTEXT,`e` MEDIUMTEXT,`f` MEDIUMTEXT,`g` MEDIUMTEXT)"}, - {"create table t (a long varbinary);", true, "CREATE TABLE `t` (`a` MEDIUMBLOB)"}, - {"create table t (a long char varying, b long varbinary);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT,`b` MEDIUMBLOB)"}, - {"create table t (a long char set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, - {"create table t (a long char varying char set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, - {"create table t (a long character set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, - {"create table t (a long character varying character set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, - {"alter table d_n.t_n modify column ident long after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, - {"alter table d_n.t_n modify column ident long char varying after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, - {"alter table d_n.t_n modify column ident long character varying after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, - {"alter table d_n.t_n modify column ident long varchar after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, - {"alter table d_n.t_n modify column ident long varcharacter after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, - {"alter table t_n change column ident ident long char varying binary charset utf8 first , tablespace ident", true, "ALTER TABLE `t_n` CHANGE COLUMN `ident` `ident` MEDIUMTEXT BINARY CHARACTER SET UTF8 FIRST, TABLESPACE = `ident`"}, - {"alter table t_n change column ident ident long character varying binary charset utf8 first , tablespace ident", true, "ALTER TABLE `t_n` CHANGE COLUMN `ident` `ident` MEDIUMTEXT BINARY CHARACTER SET UTF8 FIRST, TABLESPACE = `ident`"}, - - // for STATS_AUTO_RECALC syntax - {"create table t (a int) stats_auto_recalc 2;", false, ""}, - {"create table t (a int) stats_auto_recalc = 10;", false, ""}, - {"create table t (a int) stats_auto_recalc 0;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 0"}, - {"create table t (a int) stats_auto_recalc default;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = DEFAULT"}, - {"create table t (a int) stats_auto_recalc = 0;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 0"}, - {"create table t (a int) stats_auto_recalc = 1;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1"}, - {"create table t (a int) stats_auto_recalc=default;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = DEFAULT"}, - {"create table t (a int) stats_persistent = 1, stats_auto_recalc = 1;", true, "CREATE TABLE `t` (`a` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ STATS_AUTO_RECALC = 1"}, - {"create table t (a int) stats_auto_recalc = 1, stats_sample_pages = 25;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1 STATS_SAMPLE_PAGES = 25"}, - {"alter table t modify a bigint, ENGINE=InnoDB, stats_auto_recalc = 0", true, "ALTER TABLE `t` MODIFY COLUMN `a` BIGINT, ENGINE = InnoDB, STATS_AUTO_RECALC = 0"}, - {"create table stats_auto_recalc (a int);", true, "CREATE TABLE `stats_auto_recalc` (`a` INT)"}, - {"create table stats_auto_recalc (a int) stats_auto_recalc=1;", true, "CREATE TABLE `stats_auto_recalc` (`a` INT) STATS_AUTO_RECALC = 1"}, - - // for TYPE/USING syntax - {"create table t (a int, primary key type type btree (a));", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY `type`(`a`) USING BTREE)"}, - {"create table t (a int, primary key type btree (a));", false, ""}, - {"create table t (a int, primary key using btree (a));", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, - {"create table t (a int, primary key (a) type btree);", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, - {"create table t (a int, primary key (a) using btree);", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, - {"create table t (a int, unique index type type btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE `type`(`a`) USING BTREE)"}, - {"create table t (a int, unique index type using btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE `type`(`a`) USING BTREE)"}, - {"create table t (a int, unique index type btree (a));", false, ""}, - {"create table t (a int, unique index using btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, - {"create table t (a int, unique index (a) using btree);", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, - {"create table t (a int, unique key (a) using btree);", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, - {"create table t (a int, index type type btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX `type`(`a`) USING BTREE)"}, - {"create table t (a int, index type btree (a));", false, ""}, - {"create table t (a int, index type using btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX `type`(`a`) USING BTREE)"}, - {"create table t (a int, index using btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX(`a`) USING BTREE)"}, - - // for issue 500 - {`ALTER TABLE d_n.t_n WITHOUT VALIDATION , ADD PARTITION ( PARTITION ident VALUES LESS THAN ( MAXVALUE ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITHOUT VALIDATION, ADD PARTITION (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string MAX_ROWS = 12)"}, - {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION ident VALUES LESS THAN MAXVALUE STORAGE ENGINE = text_string, PARTITION ident VALUES LESS THAN ( MAXVALUE ) (SUBPARTITION text_string MIN_ROWS 11))`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string, PARTITION `ident` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `text_string` MIN_ROWS = 11))"}, - // for test VALUE IN - {`ALTER TABLE d_n.t_n WITHOUT VALIDATION , ADD PARTITION ( PARTITION ident VALUES IN ( DEFAULT ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITHOUT VALIDATION, ADD PARTITION (PARTITION `ident` DEFAULT ENGINE = text_string MAX_ROWS = 12)"}, - {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG ( PARTITION ident VALUES IN ( DEFAULT ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` DEFAULT ENGINE = text_string MAX_ROWS = 12)"}, - {`ALTER TABLE d_n.t_n ADD PARTITION ( PARTITION ident VALUES IN ( DEFAULT ), partition ptext values in ('default') )`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` DEFAULT, PARTITION `ptext` VALUES IN (_UTF8MB4'default'))"}, - {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION ident VALUES LESS THAN MAXVALUE STORAGE ENGINE = text_string, PARTITION ident VALUES IN ( DEFAULT ) (SUBPARTITION text_string MIN_ROWS 11))`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string, PARTITION `ident` DEFAULT (SUBPARTITION `text_string` MIN_ROWS = 11))"}, - {`ALTER TABLE d_n.t_n ADD PARTITION (PARTITION ident VALUES IN ( DEFAULT ))`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` DEFAULT)"}, - {`ALTER TABLE d_n.t_n ADD PARTITION (PARTITION ident VALUES IN (1, default ))`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` VALUES IN (1, DEFAULT))"}, - // for issue 501 - {"ALTER TABLE t IMPORT TABLESPACE;", true, "ALTER TABLE `t` IMPORT TABLESPACE"}, - {"ALTER TABLE t DISCARD TABLESPACE;", true, "ALTER TABLE `t` DISCARD TABLESPACE"}, - {"ALTER TABLE db.t IMPORT TABLESPACE;", true, "ALTER TABLE `db`.`t` IMPORT TABLESPACE"}, - {"ALTER TABLE db.t DISCARD TABLESPACE;", true, "ALTER TABLE `db`.`t` DISCARD TABLESPACE"}, - - // for CONSTRAINT syntax, see issue 413 - {"ALTER TABLE t ADD ( CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, - {"ALTER TABLE t ADD ( CONSTRAINT CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, - {"ALTER TABLE t ADD COLUMN ( CONSTRAINT ident CHECK ( 1>2 ) NOT ENFORCED )", true, "ALTER TABLE `t` ADD COLUMN (CONSTRAINT `ident` CHECK(1>2) NOT ENFORCED)"}, - {"alter table t add column (b int, constraint c unique key (b))", true, "ALTER TABLE `t` ADD COLUMN (`b` INT, UNIQUE `c`(`b`))"}, - {"ALTER TABLE t ADD COLUMN ( CONSTRAINT CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, - {"ALTER TABLE t ADD COLUMN ( CONSTRAINT CHECK ( true ) ENFORCED , CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED, CHECK(TRUE) ENFORCED)"}, - {"ALTER TABLE t ADD COLUMN (a1 int, CONSTRAINT b1 CHECK (a1>0))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, CONSTRAINT `b1` CHECK(`a1`>0) ENFORCED)"}, - {"ALTER TABLE t ADD COLUMN (a1 int, a2 int, CONSTRAINT b1 CHECK (a1>0), CONSTRAINT b2 CHECK (a2<10))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, CONSTRAINT `b1` CHECK(`a1`>0) ENFORCED, CONSTRAINT `b2` CHECK(`a2`<10) ENFORCED)"}, - {"ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY (`a1`))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY(`a1`))"}, - {"ALTER TABLE t ADD (a1 int, CONSTRAINT PRIMARY KEY (a1))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY(`a1`))"}, - {"ALTER TABLE t ADD (a1 int, a2 int, PRIMARY KEY (a1), UNIQUE (a2))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, PRIMARY KEY(`a1`), UNIQUE(`a2`))"}, - {"ALTER TABLE t ADD (a1 int, a2 int, PRIMARY KEY (a1), CONSTRAINT b2 UNIQUE (a2))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, PRIMARY KEY(`a1`), UNIQUE `b2`(`a2`))"}, - {"ALTER TABLE ident ADD ( CONSTRAINT FOREIGN KEY ident ( EXECUTE ( 123 ) ) REFERENCES t ( a ) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL )", true, "ALTER TABLE `ident` ADD COLUMN (CONSTRAINT `ident` FOREIGN KEY (`EXECUTE`(123)) REFERENCES `t`(`a`) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL)"}, - // for CONSTRAINT cont'd, the following tests are for another aspect of the incompatibility - {"ALTER TABLE t ADD COLUMN a DATE CHECK ( a > 0 ) FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` DATE CHECK(`a`>0) ENFORCED FIRST"}, - {"ALTER TABLE t ADD a1 int CONSTRAINT ident CHECK ( a1 > 1 ) REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE;", true, "ALTER TABLE `t` ADD COLUMN `a1` INT CONSTRAINT `ident` CHECK(`a1`>1) ENFORCED REFERENCES `b` ON DELETE CASCADE ON UPDATE CASCADE"}, - {"ALTER TABLE t ADD COLUMN a DATE CONSTRAINT CHECK ( a > 0 ) FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` DATE CHECK(`a`>0) ENFORCED FIRST"}, - {"ALTER TABLE t ADD a TINYBLOB CONSTRAINT ident CHECK ( 1>2 ) REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE", true, "ALTER TABLE `t` ADD COLUMN `a` TINYBLOB CONSTRAINT `ident` CHECK(1>2) ENFORCED REFERENCES `b` ON DELETE CASCADE ON UPDATE CASCADE"}, - {"ALTER TABLE t ADD a2 int CONSTRAINT ident CHECK (a2 > 1) ENFORCED", true, "ALTER TABLE `t` ADD COLUMN `a2` INT CONSTRAINT `ident` CHECK(`a2`>1) ENFORCED"}, - {"ALTER TABLE t ADD a2 int CONSTRAINT ident CHECK (a2 > 1) NOT ENFORCED", true, "ALTER TABLE `t` ADD COLUMN `a2` INT CONSTRAINT `ident` CHECK(`a2`>1) NOT ENFORCED"}, - {"ALTER TABLE t ADD a2 int CONSTRAINT ident primary key REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE;", false, ""}, - {"ALTER TABLE t ADD a2 int CONSTRAINT ident primary key (a2))", false, ""}, - {"ALTER TABLE t ADD a2 int CONSTRAINT ident unique key (a2))", false, ""}, - - {"ALTER TABLE t SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER TABLE `t` SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, - {"ALTER TABLE t SET TIFLASH REPLICA 0", true, "ALTER TABLE `t` SET TIFLASH REPLICA 0"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 0", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 0"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 1 LOCATION LABELS 'a','b' SET TIFLASH REPLICA 2", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 LOCATION LABELS 'a', 'b' SET TIFLASH REPLICA 2"}, - {"ALTER DATABASE t SET TIFLASH REPLICA 1 LOCATION LABELS 'a','b' SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 LOCATION LABELS 'a', 'b' SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, - - // for issue 537 - {"CREATE TABLE IF NOT EXISTS table_ident (a SQL_TSI_YEAR(4), b SQL_TSI_YEAR);", true, "CREATE TABLE IF NOT EXISTS `table_ident` (`a` YEAR(4),`b` YEAR)"}, - {`CREATE TABLE IF NOT EXISTS table_ident (ident1 BOOL COMMENT "text_string" unique, ident2 SQL_TSI_YEAR(4) ZEROFILL);`, true, "CREATE TABLE IF NOT EXISTS `table_ident` (`ident1` TINYINT(1) COMMENT 'text_string' UNIQUE KEY,`ident2` YEAR(4))"}, - {"create table t (y sql_tsi_year(4), y1 sql_tsi_year)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, - {"create table t (y sql_tsi_year(4) unsigned zerofill zerofill, y1 sql_tsi_year signed unsigned zerofill)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, - - // for issue 549 - {"insert into t set a = default", true, "INSERT INTO `t` SET `a`=DEFAULT"}, - {"insert into t set a := default", true, "INSERT INTO `t` SET `a`=DEFAULT"}, - {"replace t set a = default", true, "REPLACE INTO `t` SET `a`=DEFAULT"}, - {"update t set a = default", true, "UPDATE `t` SET `a`=DEFAULT"}, - {"insert into t set a = default on duplicate key update a = default", true, "INSERT INTO `t` SET `a`=DEFAULT ON DUPLICATE KEY UPDATE `a`=DEFAULT"}, - - // for issue 529 - {"create table t (a text byte ascii)", false, ""}, - {"create table t (a text byte charset latin1)", false, ""}, - {"create table t (a longtext ascii)", true, "CREATE TABLE `t` (`a` LONGTEXT CHARACTER SET LATIN1)"}, - {"create table t (a mediumtext ascii)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET LATIN1)"}, - {"create table t (a tinytext ascii)", true, "CREATE TABLE `t` (`a` TINYTEXT CHARACTER SET LATIN1)"}, - {"create table t (a text byte)", true, "CREATE TABLE `t` (`a` BLOB)"}, - {"create table t (a long byte, b text ascii)", true, "CREATE TABLE `t` (`a` MEDIUMBLOB,`b` TEXT CHARACTER SET LATIN1)"}, - {"create table t (a text ascii, b mediumtext ascii, c int)", true, "CREATE TABLE `t` (`a` TEXT CHARACTER SET LATIN1,`b` MEDIUMTEXT CHARACTER SET LATIN1,`c` INT)"}, - {"create table t (a int, b text ascii, c mediumtext ascii)", true, "CREATE TABLE `t` (`a` INT,`b` TEXT CHARACTER SET LATIN1,`c` MEDIUMTEXT CHARACTER SET LATIN1)"}, - {"create table t (a long ascii, b long ascii)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET LATIN1,`b` MEDIUMTEXT CHARACTER SET LATIN1)"}, - {"create table t (a long character set utf8mb4, b long charset utf8mb4, c long char set utf8mb4)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8MB4,`b` MEDIUMTEXT CHARACTER SET UTF8MB4,`c` MEDIUMTEXT CHARACTER SET UTF8MB4)"}, - - {"create table t (a int STORAGE MEMORY, b varchar(255) STORAGE MEMORY)", true, "CREATE TABLE `t` (`a` INT STORAGE MEMORY,`b` VARCHAR(255) STORAGE MEMORY)"}, - {"create table t (a int storage DISK, b varchar(255) STORAGE DEFAULT)", true, "CREATE TABLE `t` (`a` INT STORAGE DISK,`b` VARCHAR(255) STORAGE DEFAULT)"}, - {"create table t (a int STORAGE DEFAULT, b varchar(255) STORAGE DISK)", true, "CREATE TABLE `t` (`a` INT STORAGE DEFAULT,`b` VARCHAR(255) STORAGE DISK)"}, - - // for issue 555 - {"create table t (a fixed(6, 3), b fixed key)", true, "CREATE TABLE `t` (`a` DECIMAL(6,3),`b` DECIMAL PRIMARY KEY)"}, - {"create table t (a numeric, b fixed(6))", true, "CREATE TABLE `t` (`a` DECIMAL,`b` DECIMAL(6))"}, - {"create table t (a fixed(65, 30) zerofill, b numeric, c fixed(65) unsigned zerofill)", true, "CREATE TABLE `t` (`a` DECIMAL(65,30) UNSIGNED ZEROFILL,`b` DECIMAL,`c` DECIMAL(65) UNSIGNED ZEROFILL)"}, - - // create table with expression index - {"create table a(a int, key(lower(a)));", false, ""}, - {"create table a(a int, key(a+1));", false, ""}, - {"create table a(a int, key(a, a+1));", false, ""}, - {"create table a(a int, b int, key((a+1), (b+1)));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1), (`b`+1)))"}, - {"create table a(a int, b int, key(a, (b+1)));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX(`a`, (`b`+1)))"}, - {"create table a(a int, b int, key((a+1), b));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1), `b`))"}, - {"create table a(a int, b int, key((a + 1) desc));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1) DESC))"}, - - // for create sequence - {"create sequence sequence", true, "CREATE SEQUENCE `sequence`"}, - {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, - {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, - {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, - {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, - {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, - {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, - {"create sequence if not exists seq increment", false, ""}, - {"create sequence if not exists seq increment 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, - {"create sequence if not exists seq increment = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, - {"create sequence if not exists seq increment by 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, - {"create sequence if not exists seq minvalue", false, ""}, - {"create sequence if not exists seq minvalue 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, - {"create sequence if not exists seq minvalue = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, - {"create sequence if not exists seq no", false, ""}, - {"create sequence if not exists seq nominvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, - {"create sequence if not exists seq no minvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, - {"create sequence if not exists seq maxvalue", false, ""}, - {"create sequence if not exists seq maxvalue 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, - {"create sequence if not exists seq maxvalue = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, - {"create sequence if not exists seq no", false, ""}, - {"create sequence if not exists seq nomaxvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, - {"create sequence if not exists seq no maxvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, - {"create sequence if not exists seq start", false, ""}, - {"create sequence if not exists seq start with", false, ""}, - {"create sequence if not exists seq start =", false, ""}, - {"create sequence if not exists seq start with", false, ""}, - {"create sequence if not exists seq start 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, - {"create sequence if not exists seq start = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, - {"create sequence if not exists seq start with 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, - {"create sequence if not exists seq cache", false, ""}, - {"create sequence if not exists seq cache 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, - {"create sequence if not exists seq cache = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, - {"create sequence if not exists seq nocache", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, - {"create sequence if not exists seq no cache", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, - {"create sequence if not exists seq cycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CYCLE"}, - {"create sequence if not exists seq nocycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, - {"create sequence if not exists seq no cycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, - {"create sequence seq increment 1 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 1 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, - {"create sequence seq increment 1 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 1 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, - // TODO : support or replace if need : care for it will conflict on temporary. - {"create sequence seq increment 10 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 10 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, - {"create sequence if not exists seq cache 1 increment 1 start with -1 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1 INCREMENT BY 1 START WITH -1 MINVALUE 0 MAXVALUE 1000"}, - {"create sequence sEq start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `sEq` START WITH 0 MINVALUE 0 MAXVALUE 1000"}, - {"create sequence if not exists seq increment 1 start with 0 minvalue -2 maxvalue 1000", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1 START WITH 0 MINVALUE -2 MAXVALUE 1000"}, - {"create sequence seq increment -1 start with -1 minvalue -1 maxvalue -1000 cache = 10 nocycle", true, "CREATE SEQUENCE `seq` INCREMENT BY -1 START WITH -1 MINVALUE -1 MAXVALUE -1000 CACHE 10 NOCYCLE"}, - - // test sequence is not a reserved keyword - {"create table sequence (a int)", true, "CREATE TABLE `sequence` (`a` INT)"}, - {"create table t (sequence int)", true, "CREATE TABLE `t` (`sequence` INT)"}, - - // test drop sequence - {"drop sequence", false, ""}, - {"drop sequence seq", true, "DROP SEQUENCE `seq`"}, - {"drop sequence if exists seq", true, "DROP SEQUENCE IF EXISTS `seq`"}, - {"drop sequence seq", true, "DROP SEQUENCE `seq`"}, - {"drop sequence if exists seq", true, "DROP SEQUENCE IF EXISTS `seq`"}, - {"drop sequence if exists seq, seq2, seq3", true, "DROP SEQUENCE IF EXISTS `seq`, `seq2`, `seq3`"}, - {"drop sequence seq seq2", false, ""}, - {"drop sequence seq, seq2", true, "DROP SEQUENCE `seq`, `seq2`"}, - - // for auto_random - {"create table t (a bigint auto_random(3) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(3) PRIMARY KEY,`b` VARCHAR(255))"}, - {"create table t (a bigint auto_random primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM PRIMARY KEY,`b` VARCHAR(255))"}, - {"create table t (a bigint primary key auto_random(4), b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(4),`b` VARCHAR(255))"}, - {"create table t (a bigint primary key auto_random(3) primary key unique, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(3) PRIMARY KEY UNIQUE KEY,`b` VARCHAR(255))"}, - {"create table t (a bigint auto_random(5, 53) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(5, 53) PRIMARY KEY,`b` VARCHAR(255))"}, - {"create table t (a bigint auto_random(15, 32) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(15, 32) PRIMARY KEY,`b` VARCHAR(255))"}, - - // for auto_id_cache - {"create table t (a int) auto_id_cache=1", true, "CREATE TABLE `t` (`a` INT) AUTO_ID_CACHE = 1"}, - {"create table t (a int auto_increment key) auto_id_cache 10", true, "CREATE TABLE `t` (`a` INT AUTO_INCREMENT PRIMARY KEY) AUTO_ID_CACHE = 10"}, - {"create table t (a bigint, b varchar(255)) auto_id_cache 50", true, "CREATE TABLE `t` (`a` BIGINT,`b` VARCHAR(255)) AUTO_ID_CACHE = 50"}, - - // for auto_random_id - {"create table t (a bigint auto_random(3) primary key) auto_random_base = 10", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(3) PRIMARY KEY) AUTO_RANDOM_BASE = 10"}, - {"create table t (a bigint primary key auto_random(4), b varchar(100)) auto_random_base 200", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(4),`b` VARCHAR(100)) AUTO_RANDOM_BASE = 200"}, - {"alter table t auto_random_base = 50", true, "ALTER TABLE `t` AUTO_RANDOM_BASE = 50"}, - {"alter table t auto_increment 30, auto_random_base 40", true, "ALTER TABLE `t` AUTO_INCREMENT = 30, AUTO_RANDOM_BASE = 40"}, - {"alter table t force auto_random_base = 50", true, "ALTER TABLE `t` FORCE AUTO_RANDOM_BASE = 50"}, - {"alter table t auto_increment 30, force auto_random_base 40", true, "ALTER TABLE `t` AUTO_INCREMENT = 30, FORCE AUTO_RANDOM_BASE = 40"}, - - // for alter sequence - {"alter sequence seq", false, ""}, - {"alter sequence seq comment=\"haha\"", false, ""}, - {"alter sequence seq start = 1", true, "ALTER SEQUENCE `seq` START WITH 1"}, - {"alter sequence seq start with 1 increment by 1", true, "ALTER SEQUENCE `seq` START WITH 1 INCREMENT BY 1"}, - {"alter sequence seq start with 1 increment by 2 minvalue 0 maxvalue 100", true, "ALTER SEQUENCE `seq` START WITH 1 INCREMENT BY 2 MINVALUE 0 MAXVALUE 100"}, - {"alter sequence seq increment -1 start with -1 minvalue -1 maxvalue -1000 cache = 10 nocycle", true, "ALTER SEQUENCE `seq` INCREMENT BY -1 START WITH -1 MINVALUE -1 MAXVALUE -1000 CACHE 10 NOCYCLE"}, - {"alter sequence if exists seq2 increment = 2", true, "ALTER SEQUENCE IF EXISTS `seq2` INCREMENT BY 2"}, - {"alter sequence seq restart", true, "ALTER SEQUENCE `seq` RESTART"}, - {"alter sequence seq start with 3 restart with 5", true, "ALTER SEQUENCE `seq` START WITH 3 RESTART WITH 5"}, - {"alter sequence seq restart = 5", true, "ALTER SEQUENCE `seq` RESTART WITH 5"}, - {"create sequence seq restart = 5", false, ""}, - - // for issue 18149 - {"create table t (a int, index ``(a))", true, "CREATE TABLE `t` (`a` INT,INDEX ``(`a`))"}, - - // for clustered index - {"create table t (a int, b varchar(255), primary key(b, a) clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) CLUSTERED)"}, - {"create table t (a int, b varchar(255), primary key(b, a) nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED)"}, - {"create table t (a int primary key nonclustered, b varchar(255))", true, "CREATE TABLE `t` (`a` INT PRIMARY KEY NONCLUSTERED,`b` VARCHAR(255))"}, - {"create table t (a int, b varchar(255) primary key clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) PRIMARY KEY CLUSTERED)"}, - {"create table t (a int, b varchar(255) default 'a' primary key clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) DEFAULT _UTF8MB4'a' PRIMARY KEY CLUSTERED)"}, - {"create table t (a int, b varchar(255) primary key nonclustered, primary key(b, a) nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) PRIMARY KEY NONCLUSTERED,PRIMARY KEY(`b`, `a`) NONCLUSTERED)"}, - {"create table t (a int, b varchar(255), primary key(b, a) using RTREE nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED USING RTREE)"}, - {"create table t (a int, b varchar(255), primary key(b, a) using RTREE clustered nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED USING RTREE)"}, - {"create table t (a int, b varchar(255), primary key(b, a) using RTREE nonclustered clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) CLUSTERED USING RTREE)"}, - {"create table t (a int, b varchar(255) clustered primary key)", false, ""}, - {"create table t (a int, b varchar(255) primary key nonclustered clustered)", false, ""}, - {"alter table t add primary key (`a`, `b`) clustered", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`, `b`) CLUSTERED"}, - {"alter table t add primary key (`a`, `b`) nonclustered", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`, `b`) NONCLUSTERED"}, - - // for drop placement policy - {"drop placement policy x", true, "DROP PLACEMENT POLICY `x`"}, - {"drop placement policy x, y", false, ""}, - {"drop placement policy if exists x", true, "DROP PLACEMENT POLICY IF EXISTS `x`"}, - {"drop placement policy if exists x, y", false, ""}, - // for show create placement policy - {"show create placement policy x", true, "SHOW CREATE PLACEMENT POLICY `x`"}, - {"show create placement policy if exists x", false, ""}, - {"show create placement policy x, y", false, ""}, - {"show create placement policy `placement`", true, "SHOW CREATE PLACEMENT POLICY `placement`"}, - // for create placement policy - {"create placement policy x primary_region='us'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'us'"}, - {"create placement policy x region='us, 3'", false, ""}, - {"create placement policy x followers=3", true, "CREATE PLACEMENT POLICY `x` FOLLOWERS = 3"}, - {"create placement policy x followers=0", false, ""}, - {"create placement policy x voters=3", true, "CREATE PLACEMENT POLICY `x` VOTERS = 3"}, - {"create placement policy x learners=3", true, "CREATE PLACEMENT POLICY `x` LEARNERS = 3"}, - {"create placement policy x schedule='even'", true, "CREATE PLACEMENT POLICY `x` SCHEDULE = 'even'"}, - {"create placement policy x constraints='ww'", true, "CREATE PLACEMENT POLICY `x` CONSTRAINTS = 'ww'"}, - {"create placement policy x leader_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` LEADER_CONSTRAINTS = 'ww'"}, - {"create placement policy x follower_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` FOLLOWER_CONSTRAINTS = 'ww'"}, - {"create placement policy x voter_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` VOTER_CONSTRAINTS = 'ww'"}, - {"create placement policy x learner_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` LEARNER_CONSTRAINTS = 'ww'"}, - {"create placement policy x primary_region='cn' regions='us' schedule='even'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' REGIONS = 'us' SCHEDULE = 'even'"}, - {"create placement policy x primary_region='cn', leader_constraints='ww', leader_constraints='yy'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' LEADER_CONSTRAINTS = 'ww' LEADER_CONSTRAINTS = 'yy'"}, - {"create placement policy if not exists x regions = 'us', follower_constraints='yy'", true, "CREATE PLACEMENT POLICY IF NOT EXISTS `x` REGIONS = 'us' FOLLOWER_CONSTRAINTS = 'yy'"}, - {"create or replace placement policy x regions='us'", true, "CREATE OR REPLACE PLACEMENT POLICY `x` REGIONS = 'us'"}, - {"create placement policy x placement policy y", false, ""}, - - {"alter placement policy x primary_region='us'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'us'"}, - {"alter placement policy x region='us, 3'", false, ""}, - {"alter placement policy x followers=3", true, "ALTER PLACEMENT POLICY `x` FOLLOWERS = 3"}, - {"alter placement policy x voters=3", true, "ALTER PLACEMENT POLICY `x` VOTERS = 3"}, - {"alter placement policy x learners=3", true, "ALTER PLACEMENT POLICY `x` LEARNERS = 3"}, - {"alter placement policy x schedule='even'", true, "ALTER PLACEMENT POLICY `x` SCHEDULE = 'even'"}, - {"alter placement policy x constraints='ww'", true, "ALTER PLACEMENT POLICY `x` CONSTRAINTS = 'ww'"}, - {"alter placement policy x leader_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` LEADER_CONSTRAINTS = 'ww'"}, - {"alter placement policy x follower_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` FOLLOWER_CONSTRAINTS = 'ww'"}, - {"alter placement policy x voter_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` VOTER_CONSTRAINTS = 'ww'"}, - {"alter placement policy x learner_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` LEARNER_CONSTRAINTS = 'ww'"}, - {"alter placement policy x primary_region='cn' regions='us' schedule='even'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' REGIONS = 'us' SCHEDULE = 'even'"}, - {"alter placement policy x primary_region='cn', leader_constraints='ww', leader_constraints='yy'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' LEADER_CONSTRAINTS = 'ww' LEADER_CONSTRAINTS = 'yy'"}, - {"alter placement policy if exists x regions = 'us', follower_constraints='yy'", true, "ALTER PLACEMENT POLICY IF EXISTS `x` REGIONS = 'us' FOLLOWER_CONSTRAINTS = 'yy'"}, - {"alter placement policy x placement policy y", false, ""}, - - // for create resource group - {"create resource group x cpu ='8c'", false, ""}, - {"create resource group x region ='us, 3'", false, ""}, - {"create resource group x cpu='8c', io_read_bandwidth='2GB/s', io_write_bandwidth='200MB/s'", false, ""}, - {"create resource group x ru_per_sec=2000", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 2000"}, - {"create resource group x ru_per_sec=200000", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 200000"}, - {"create resource group x followers=0", false, ""}, - {"create resource group x ru_per_sec=1000, burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BURSTABLE = TRUE"}, - {"create resource group x burstable, ru_per_sec=2000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 2000"}, - {"create resource group x ru_per_sec=3000 burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 3000, BURSTABLE = TRUE"}, - {"create resource group x burstable ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, - {"create resource group x burstable=false ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = FALSE, RU_PER_SEC = 4000"}, - {"create resource group x burstable = true ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, - {"create resource group x ru_per_sec=20, priority=LOW, burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 20, PRIORITY = LOW, BURSTABLE = TRUE"}, - {"create resource group default ru_per_sec=20, priority=LOW, burstable", true, "CREATE RESOURCE GROUP `default` RU_PER_SEC = 20, PRIORITY = LOW, BURSTABLE = TRUE"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION DRYRUN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = DRYRUN)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10m' ACTION COOLDOWN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10m' ACTION = COOLDOWN)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(ACTION KILL EXEC_ELAPSED='10m')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (ACTION = KILL EXEC_ELAPSED = '10m')"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' WATCH=SIMILAR DURATION '10m' ACTION COOLDOWN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' WATCH = SIMILAR DURATION = '10m' ACTION = COOLDOWN)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED \"10s\" ACTION COOLDOWN WATCH EXACT DURATION='10m')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m')"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '9s' ACTION COOLDOWN WATCH EXACT)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '9s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '8s' ACTION COOLDOWN WATCH EXACT DURATION = UNLIMITED)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '8s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '7s' ACTION COOLDOWN WATCH EXACT DURATION UNLIMITED)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '7s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '7s' ACTION COOLDOWN WATCH EXACT DURATION 'UNLIMITED')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '7s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, - {"create resource group x ru_per_sec=1000 background = (task_types='')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BACKGROUND = (TASK_TYPES = '')"}, - {"create resource group x ru_per_sec=1000 background (task_types='br,lightning')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BACKGROUND = (TASK_TYPES = 'br,lightning')"}, - {`create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED "10s" ACTION COOLDOWN WATCH EXACT DURATION='10m') background (task_types 'br,lightning')`, true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m'), BACKGROUND = (TASK_TYPES = 'br,lightning')"}, - {`create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED "10s" ACTION COOLDOWN WATCH PLAN DURATION='10m') background (task_types 'br,lightning')`, true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = PLAN DURATION = '10m'), BACKGROUND = (TASK_TYPES = 'br,lightning')"}, - // This case is expected in parser test but not in actual ddl job. - {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s')"}, - {"create resource group x ru_per_sec=1000 QUERY=(EXEC_ELAPSED '10s')", false, ""}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT=EXEC_ELAPSED '10s'", false, ""}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s'", false, ""}, - {"create resource group x ru_per_sec=1000 LIMIT=(EXEC_ELAPSED '10s')", false, ""}, - {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN ACTION KILL)", false, ""}, - - {"alter resource group x cpu ='8c'", false, ""}, - {"alter resource group x region ='us, 3'", false, ""}, - {"alter resource group default priority = high", true, "ALTER RESOURCE GROUP `default` PRIORITY = HIGH"}, - {"alter resource group x cpu='8c', io_read_bandwidth='2GB/s', io_write_bandwidth='200MB/s'", false, ""}, - {"alter resource group x ru_per_sec=1000", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000"}, - {"alter resource group x ru_per_sec=2000, BURSTABLE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 2000, BURSTABLE = TRUE"}, - {"alter resource group x BURSTABLE, ru_per_sec=3000", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 3000"}, - {"alter resource group x BURSTABLE ru_per_sec=4000", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, - // This case is expected in parser test but not in actual ddl job. - // Todo: support patch setting(not cover all). - {"alter resource group x BURSTABLE", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE"}, - {"alter resource group x ru_per_sec=200000 BURSTABLE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 200000, BURSTABLE = TRUE"}, - {"alter resource group x followers=0", false, ""}, - {"alter resource group x ru_per_sec=20 priority=MID BURSTABLE", false, ""}, - {"alter resource group x ru_per_sec=20 priority=HIGH BURSTABLE=FALSE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 20, PRIORITY = HIGH, BURSTABLE = FALSE"}, - - {"alter resource group x QUERY_LIMIT=NULL", true, "ALTER RESOURCE GROUP `x` QUERY_LIMIT = NULL"}, - {"alter resource group x QUERY_LIMIT=()", true, "ALTER RESOURCE GROUP `x` QUERY_LIMIT = NULL"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION DRYRUN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = DRYRUN)"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=()", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = NULL"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10m' ACTION COOLDOWN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10m' ACTION = COOLDOWN)"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=( ACTION KILL EXEC_ELAPSED '10m')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (ACTION = KILL EXEC_ELAPSED = '10m')"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' WATCH SIMILAR DURATION '10m' ACTION COOLDOWN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' WATCH = SIMILAR DURATION = '10m' ACTION = COOLDOWN)"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION COOLDOWN WATCH EXACT DURATION '10m')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m')"}, - // This case is expected in parser test but not in actual ddl job. - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s')"}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT EXEC_ELAPSED '10s'", false, ""}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN ACTION KILL)", false, ""}, - {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN WATCH SIMILAR DURATION '10m' ACTION COOLDOWN)", false, ""}, - {"alter resource group x background=()", true, "ALTER RESOURCE GROUP `x` BACKGROUND = NULL"}, - {"alter resource group x background NULL", true, "ALTER RESOURCE GROUP `x` BACKGROUND = NULL"}, - {"alter resource group default priority=low background = ( task_types \"ttl\" )", true, "ALTER RESOURCE GROUP `default` PRIORITY = LOW, BACKGROUND = (TASK_TYPES = 'ttl')"}, - {"alter resource group default burstable background ( task_types = 'a,b,c' )", true, "ALTER RESOURCE GROUP `default` BURSTABLE = TRUE, BACKGROUND = (TASK_TYPES = 'a,b,c')"}, - - {"drop resource group x;", true, "DROP RESOURCE GROUP `x`"}, - {"drop resource group DEFAULT;", true, "DROP RESOURCE GROUP `DEFAULT`"}, - {"drop resource group if exists x;", true, "DROP RESOURCE GROUP IF EXISTS `x`"}, - {"drop resource group x,y", false, ""}, - {"drop resource group if exists x,y", false, ""}, - - {"set resource group x;", true, "SET RESOURCE GROUP `x`"}, - {"set resource group ``;", true, "SET RESOURCE GROUP ``"}, - {"set resource group `default`;", true, "SET RESOURCE GROUP `default`"}, - {"set resource group default;", true, "SET RESOURCE GROUP `default`"}, - {"set resource group x y", false, ""}, - - {"CREATE ROLE `RESOURCE`", true, "CREATE ROLE `RESOURCE`@`%`"}, - {"CREATE ROLE RESOURCE", false, ""}, - - // for table stats options - // 1. create table with options - {"CREATE TABLE t (a int) STATS_BUCKETS=1", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1"}, - {"CREATE TABLE t (a int) STATS_BUCKETS='abc'", false, ""}, - {"CREATE TABLE t (a int) STATS_BUCKETS=", false, ""}, - {"CREATE TABLE t (a int) STATS_TOPN=1", true, "CREATE TABLE `t` (`a` INT) STATS_TOPN = 1"}, - {"CREATE TABLE t (a int) STATS_TOPN='abc'", false, ""}, - {"CREATE TABLE t (a int) STATS_AUTO_RECALC=1", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1"}, - {"CREATE TABLE t (a int) STATS_AUTO_RECALC='abc'", false, ""}, - {"CREATE TABLE t(a int) STATS_SAMPLE_RATE=0.1", true, "CREATE TABLE `t` (`a` INT) STATS_SAMPLE_RATE = 0.1"}, - {"CREATE TABLE t (a int) STATS_SAMPLE_RATE='abc'", false, ""}, - {"CREATE TABLE t (a int) STATS_COL_CHOICE='all'", true, "CREATE TABLE `t` (`a` INT) STATS_COL_CHOICE = 'all'"}, - {"CREATE TABLE t (a int) STATS_COL_CHOICE='list'", true, "CREATE TABLE `t` (`a` INT) STATS_COL_CHOICE = 'list'"}, - {"CREATE TABLE t (a int) STATS_COL_CHOICE=1", false, ""}, - {"CREATE TABLE t (a int, b int) STATS_COL_LIST='a,b'", true, "CREATE TABLE `t` (`a` INT,`b` INT) STATS_COL_LIST = 'a,b'"}, - {"CREATE TABLE t (a int, b int) STATS_COL_LIST=1", false, ""}, - {"CREATE TABLE t (a int) STATS_BUCKETS=1,STATS_TOPN=1", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1 STATS_TOPN = 1"}, - // 2. create partition table with options - {"CREATE TABLE t (a int) STATS_BUCKETS=1,STATS_TOPN=1 PARTITION BY RANGE (a) (PARTITION p1 VALUES LESS THAN (200))", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1 STATS_TOPN = 1 PARTITION BY RANGE (`a`) (PARTITION `p1` VALUES LESS THAN (200))"}, - // 3. alter table with options - {"ALTER TABLE t STATS_OPTIONS='str'", true, "ALTER TABLE `t` STATS_OPTIONS='str'"}, - {"ALTER TABLE t STATS_OPTIONS='str1,str2'", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, - {"ALTER TABLE t STATS_OPTIONS=\"str1,str2\"", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, - {"ALTER TABLE t STATS_OPTIONS 'str1,str2'", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, - {"ALTER TABLE t STATS_OPTIONS \"str1,str2\"", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, - {"ALTER TABLE t STATS_OPTIONS=DEFAULT", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, - {"ALTER TABLE t STATS_OPTIONS=default", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, - {"ALTER TABLE t STATS_OPTIONS=DeFaUlT", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, - {"ALTER TABLE t STATS_OPTIONS", false, ""}, - - // Restore INSERT_METHOD table option - {"CREATE TABLE t (a int) INSERT_METHOD=FIRST", true, "CREATE TABLE `t` (`a` INT) INSERT_METHOD = FIRST"}, - } - RunTest(t, table, false) -} - -func TestHintError(t *testing.T) { - p := parser.New() - stmt, warns, err := p.Parse("select /*+ tidb_unknown(T1,t2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - require.Len(t, warns, 1) - require.Regexp(t, `Optimizer hint syntax error at line 1 column 23 near "tidb_unknown\(T1,t2\) \*/" $`, warns[0].Error()) - require.Len(t, stmt[0].(*ast.SelectStmt).TableHints, 0) - stmt, warns, err = p.Parse("select /*+ TIDB_INLJ(t1, T2) tidb_unknow(T1,t2, 1) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.Len(t, stmt[0].(*ast.SelectStmt).TableHints, 0) - require.NoError(t, err) - require.Len(t, warns, 1) - require.Regexp(t, `Optimizer hint syntax error at line 1 column 40 near "tidb_unknow\(T1,t2, 1\) \*/" $`, warns[0].Error()) - _, _, err = p.Parse("select c1, c2 from /*+ tidb_unknow(T1,t2) */ t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) // Hints are ignored after the "FROM" keyword! - _, _, err = p.Parse("select1 /*+ TIDB_INLJ(t1, T2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.EqualError(t, err, "line 1 column 7 near \"select1 /*+ TIDB_INLJ(t1, T2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1\" ") - _, _, err = p.Parse("select /*+ TIDB_INLJ(t1, T2) */ c1, c2 fromt t1, t2 where t1.c1 = t2.c1", "", "") - require.EqualError(t, err, "line 1 column 47 near \"t1, t2 where t1.c1 = t2.c1\" ") - _, _, err = p.Parse("SELECT 1 FROM DUAL WHERE 1 IN (SELECT /*+ DEBUG_HINT3 */ 1)", "", "") - require.NoError(t, err) - stmt, _, err = p.Parse("insert into t select /*+ memory_quota(1 MB) */ * from t;", "", "") - require.NoError(t, err) - require.Len(t, stmt[0].(*ast.InsertStmt).TableHints, 0) - require.Len(t, stmt[0].(*ast.InsertStmt).Select.(*ast.SelectStmt).TableHints, 1) - stmt, _, err = p.Parse("insert /*+ memory_quota(1 MB) */ into t select * from t;", "", "") - require.NoError(t, err) - require.Len(t, stmt[0].(*ast.InsertStmt).TableHints, 1) - - _, warns, err = p.Parse("SELECT id FROM tbl WHERE id = 0 FOR UPDATE /*+ xyz */", "", "") - require.NoError(t, err) - require.Len(t, warns, 1) - require.Regexp(t, `near '/\*\+' at line 1$`, warns[0].Error()) - - _, warns, err = p.Parse("create global binding for select /*+ max_execution_time(1) */ 1 using select /*+ max_execution_time(1) */ 1;\n", "", "") - require.NoError(t, err) - require.Len(t, warns, 0) -} - -func TestErrorMsg(t *testing.T) { - p := parser.New() - _, _, err := p.Parse("select1 1", "", "") - require.EqualError(t, err, "line 1 column 7 near \"select1 1\" ") - _, _, err = p.Parse("select 1 from1 dual", "", "") - require.EqualError(t, err, "line 1 column 19 near \"dual\" ") - _, _, err = p.Parse("select * from t1 join t2 from t1.a = t2.a;", "", "") - require.EqualError(t, err, "line 1 column 29 near \"from t1.a = t2.a;\" ") - _, _, err = p.Parse("select * from t1 join t2 one t1.a = t2.a;", "", "") - require.EqualError(t, err, "line 1 column 31 near \"t1.a = t2.a;\" ") - _, _, err = p.Parse("select * from t1 join t2 on t1.a >>> t2.a;", "", "") - require.EqualError(t, err, "line 1 column 36 near \"> t2.a;\" ") - - _, _, err = p.Parse("create table t(f_year year(5))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;", "", "") - require.EqualError(t, err, "[parser:1818]Supports only YEAR or YEAR(4) column") - - _, _, err = p.Parse("select ifnull(a,0) & ifnull(a,0) like '55' ESCAPE '\\\\a' from t;", "", "") - require.EqualError(t, err, "[parser:1210]Incorrect arguments to ESCAPE") - - _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Enclosed by '\\\\b';", "", "") - require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") - - _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Escaped by '\\\\b';", "", "") - require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") - - _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Enclosed by '\\\\b' Escaped by '\\\\b' ;", "", "") - require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") - - _, _, err = p.Parse("ALTER DATABASE `` CHARACTER SET = ''", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: ''") - - _, _, err = p.Parse("ALTER DATABASE t CHARACTER SET = ''", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: ''") - - _, _, err = p.Parse("ALTER SCHEMA t CHARACTER SET = 'SOME_INVALID_CHARSET'", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: 'SOME_INVALID_CHARSET'") - - _, _, err = p.Parse("ALTER DATABASE t COLLATE = ''", "", "") - require.EqualError(t, err, "[ddl:1273]Unknown collation: ''") - - _, _, err = p.Parse("ALTER SCHEMA t COLLATE = 'SOME_INVALID_COLLATION'", "", "") - require.EqualError(t, err, "[ddl:1273]Unknown collation: 'SOME_INVALID_COLLATION'") - - _, _, err = p.Parse("ALTER DATABASE CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", "", "") - require.EqualError(t, err, "line 1 column 24 near \"= 'utf8mb4' COLLATE = 'utf8_bin'\" ") - - _, _, err = p.Parse("ALTER DATABASE t ENCRYPTION = ''", "", "") - require.EqualError(t, err, "[parser:1525]Incorrect argument (should be Y or N) value: ''") - - _, _, err = p.Parse("ALTER DATABASE", "", "") - require.EqualError(t, err, "line 1 column 14 near \"\" ") - - _, _, err = p.Parse("ALTER SCHEMA `ANY_DB_NAME`", "", "") - require.EqualError(t, err, "line 1 column 26 near \"\" ") - - _, _, err = p.Parse("alter table t partition by range FIELDS(a)", "", "") - require.EqualError(t, err, "[ddl:1492]For RANGE partitions each partition must be defined") - - _, _, err = p.Parse("alter table t partition by list FIELDS(a)", "", "") - require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") - - _, _, err = p.Parse("alter table t partition by list FIELDS(a)", "", "") - require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") - - _, _, err = p.Parse("alter table t partition by list FIELDS(a,b,c)", "", "") - require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") - - _, _, err = p.Parse("alter table t lock = first", "", "") - require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'first'") - - _, _, err = p.Parse("alter table t lock = start", "", "") - require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'start'") - - _, _, err = p.Parse("alter table t lock = commit", "", "") - require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'commit'") - - _, _, err = p.Parse("alter table t lock = binlog", "", "") - require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'binlog'") - - _, _, err = p.Parse("alter table t lock = randomStr123", "", "") - require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'randomStr123'") - - _, _, err = p.Parse("create table t (a longtext unicode)", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") - - _, _, err = p.Parse("create table t (a long byte, b text unicode)", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") - - _, _, err = p.Parse("create table t (a long ascii, b long unicode)", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") - - _, _, err = p.Parse("create table t (a text unicode, b mediumtext ascii, c int)", "", "") - require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") - - _, _, err = p.Parse("select 1 collate some_unknown_collation", "", "") - require.EqualError(t, err, "[ddl:1273]Unknown collation: 'some_unknown_collation'") -} - -func TestOptimizerHints(t *testing.T) { - p := parser.New() - // Test USE_INDEX - stmt, _, err := p.Parse("select /*+ USE_INDEX(T1,T2), use_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt := stmt[0].(*ast.SelectStmt) - - hints := selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "use_index", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "t2", hints[0].Indexes[0].L) - - require.Equal(t, "use_index", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "t4", hints[1].Indexes[0].L) - - // Test FORCE_INDEX - stmt, _, err = p.Parse("select /*+ FORCE_INDEX(T1,T2), force_index(t3,t4) RESOURCE_GROUP(rg1)*/ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 3) - require.Equal(t, "force_index", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "t2", hints[0].Indexes[0].L) - - require.Equal(t, "force_index", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "t4", hints[1].Indexes[0].L) - - require.Equal(t, "resource_group", hints[2].HintName.L) - require.Equal(t, hints[2].HintData, "rg1") - - // Test IGNORE_INDEX - stmt, _, err = p.Parse("select /*+ IGNORE_INDEX(T1,T2), ignore_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "ignore_index", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "t2", hints[0].Indexes[0].L) - - require.Equal(t, "ignore_index", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "t4", hints[1].Indexes[0].L) - - // Test ORDER_INDEX - stmt, _, err = p.Parse("select /*+ ORDER_INDEX(T1,T2), order_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "order_index", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "t2", hints[0].Indexes[0].L) - - require.Equal(t, "order_index", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "t4", hints[1].Indexes[0].L) - - // Test NO_ORDER_INDEX - stmt, _, err = p.Parse("select /*+ NO_ORDER_INDEX(T1,T2), no_order_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_order_index", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "t2", hints[0].Indexes[0].L) - - require.Equal(t, "no_order_index", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "t4", hints[1].Indexes[0].L) - - // Test TIDB_SMJ - stmt, _, err = p.Parse("select /*+ TIDB_SMJ(T1,t2), tidb_smj(T3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "tidb_smj", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "tidb_smj", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test MERGE_JOIN - stmt, _, err = p.Parse("select /*+ MERGE_JOIN(t1, T2), merge_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "merge_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "merge_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // TEST BROADCAST_JOIN - stmt, _, err = p.Parse("select /*+ BROADCAST_JOIN(t1, T2), broadcast_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "broadcast_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "broadcast_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test TIDB_INLJ - stmt, _, err = p.Parse("select /*+ TIDB_INLJ(t1, T2), tidb_inlj(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "tidb_inlj", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "tidb_inlj", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test INL_JOIN - stmt, _, err = p.Parse("select /*+ INL_JOIN(t1, T2), inl_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "inl_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "inl_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test INL_HASH_JOIN - stmt, _, err = p.Parse("select /*+ INL_HASH_JOIN(t1, T2), inl_hash_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "inl_hash_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "inl_hash_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test INL_MERGE_JOIN - stmt, _, err = p.Parse("select /*+ INL_MERGE_JOIN(t1, T2), inl_merge_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "inl_merge_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "inl_merge_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test TIDB_HJ - stmt, _, err = p.Parse("select /*+ TIDB_HJ(t1, T2), tidb_hj(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "tidb_hj", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "tidb_hj", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test HASH_JOIN - stmt, _, err = p.Parse("select /*+ HASH_JOIN(t1, T2), hash_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "hash_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "hash_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - // Test HASH_JOIN_BUILD and HASH_JOIN_PROBE - stmt, _, err = p.Parse("select /*+ hash_join_build(t1), hash_join_probe(t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "hash_join_build", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - - require.Equal(t, "hash_join_probe", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t4", hints[1].Tables[0].TableName.L) - - // Test HASH_JOIN with SWAP_JOIN_INPUTS/NO_SWAP_JOIN_INPUTS - // t1 for build, t4 for probe - stmt, _, err = p.Parse("select /*+ HASH_JOIN(t1, T2), hash_join(t3, t4), SWAP_JOIN_INPUTS(t1), NO_SWAP_JOIN_INPUTS(t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 4) - require.Equal(t, "hash_join", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - - require.Equal(t, "hash_join", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - require.Equal(t, "t4", hints[1].Tables[1].TableName.L) - - require.Equal(t, "swap_join_inputs", hints[2].HintName.L) - require.Len(t, hints[2].Tables, 1) - require.Equal(t, "t1", hints[2].Tables[0].TableName.L) - - require.Equal(t, "no_swap_join_inputs", hints[3].HintName.L) - require.Len(t, hints[3].Tables, 1) - require.Equal(t, "t4", hints[3].Tables[0].TableName.L) - - // Test MAX_EXECUTION_TIME - queries := []string{ - "SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 where t1.c1 = t2.c1", - "SELECT /*+ MAX_EXECUTION_TIME(1000) */ 1", - "SELECT /*+ MAX_EXECUTION_TIME(1000) */ SLEEP(20)", - "SELECT /*+ MAX_EXECUTION_TIME(1000) */ 1 FROM DUAL", - } - for i, query := range queries { - stmt, _, err = p.Parse(query, "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - hints = selectStmt.TableHints - require.Len(t, hints, 1) - require.Equal(t, "max_execution_time", hints[0].HintName.L, "case", i) - require.Equal(t, uint64(1000), hints[0].HintData.(uint64)) - } - - // Test NTH_PLAN - queries = []string{ - "SELECT /*+ NTH_PLAN(10) */ * FROM t1 INNER JOIN t2 where t1.c1 = t2.c1", - "SELECT /*+ NTH_PLAN(10) */ 1", - "SELECT /*+ NTH_PLAN(10) */ SLEEP(20)", - "SELECT /*+ NTH_PLAN(10) */ 1 FROM DUAL", - } - for i, query := range queries { - stmt, _, err = p.Parse(query, "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - hints = selectStmt.TableHints - require.Len(t, hints, 1) - require.Equal(t, "nth_plan", hints[0].HintName.L, "case", i) - require.Equal(t, int64(10), hints[0].HintData.(int64)) - } - - // Test USE_INDEX_MERGE - stmt, _, err = p.Parse("select /*+ USE_INDEX_MERGE(t1, c1), use_index_merge(t2, c1), use_index_merge(t3, c1, primary, c2) */ c1, c2 from t1, t2, t3 where t1.c1 = t2.c1 and t3.c2 = t1.c2", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 3) - require.Equal(t, "use_index_merge", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Len(t, hints[0].Indexes, 1) - require.Equal(t, "c1", hints[0].Indexes[0].L) - - require.Equal(t, "use_index_merge", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t2", hints[1].Tables[0].TableName.L) - require.Len(t, hints[1].Indexes, 1) - require.Equal(t, "c1", hints[1].Indexes[0].L) - - require.Equal(t, "use_index_merge", hints[2].HintName.L) - require.Len(t, hints[2].Tables, 1) - require.Equal(t, "t3", hints[2].Tables[0].TableName.L) - require.Len(t, hints[2].Indexes, 3) - require.Equal(t, "c1", hints[2].Indexes[0].L) - require.Equal(t, "primary", hints[2].Indexes[1].L) - require.Equal(t, "c2", hints[2].Indexes[2].L) - - // Test READ_FROM_STORAGE - stmt, _, err = p.Parse("select /*+ READ_FROM_STORAGE(tiflash[t1, t2], tikv[t3]) */ c1, c2 from t1, t2, t1 t3 where t1.c1 = t2.c1 and t2.c1 = t3.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "read_from_storage", hints[0].HintName.L) - require.Equal(t, "tiflash", hints[0].HintData.(model.CIStr).L) - require.Len(t, hints[0].Tables, 2) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - require.Equal(t, "t2", hints[0].Tables[1].TableName.L) - require.Equal(t, "read_from_storage", hints[1].HintName.L) - require.Equal(t, "tikv", hints[1].HintData.(model.CIStr).L) - require.Len(t, hints[1].Tables, 1) - require.Equal(t, "t3", hints[1].Tables[0].TableName.L) - - // Test USE_TOJA - stmt, _, err = p.Parse("select /*+ USE_TOJA(true), use_toja(false) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "use_toja", hints[0].HintName.L) - require.True(t, hints[0].HintData.(bool)) - - require.Equal(t, "use_toja", hints[1].HintName.L) - require.False(t, hints[1].HintData.(bool)) - - // Test IGNORE_PLAN_CACHE - stmt, _, err = p.Parse("select /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) - require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) - - stmt, _, err = p.Parse("delete /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ from t where a = 1", "", "") - require.NoError(t, err) - deleteStmt := stmt[0].(*ast.DeleteStmt) - hints = deleteStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) - require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) - - stmt, _, err = p.Parse("update /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ t set a = 1 where a = 10", "", "") - require.NoError(t, err) - updateStmt := stmt[0].(*ast.UpdateStmt) - hints = updateStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) - require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) - - // Test USE_CASCADES - stmt, _, err = p.Parse("select /*+ USE_CASCADES(true), use_cascades(false) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "use_cascades", hints[0].HintName.L) - require.True(t, hints[0].HintData.(bool)) - - require.Equal(t, "use_cascades", hints[1].HintName.L) - require.False(t, hints[1].HintData.(bool)) - - // Test USE_PLAN_CACHE - stmt, _, err = p.Parse("select /*+ USE_PLAN_CACHE(), use_plan_cache() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "use_plan_cache", hints[0].HintName.L) - require.Equal(t, "use_plan_cache", hints[1].HintName.L) - - // Test QUERY_TYPE - stmt, _, err = p.Parse("select /*+ QUERY_TYPE(OLAP), query_type(OLTP) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "query_type", hints[0].HintName.L) - require.Equal(t, "olap", hints[0].HintData.(model.CIStr).L) - require.Equal(t, "query_type", hints[1].HintName.L) - require.Equal(t, "oltp", hints[1].HintData.(model.CIStr).L) - - // Test MEMORY_QUOTA - stmt, _, err = p.Parse("select /*+ MEMORY_QUOTA(1 MB), memory_quota(1 GB) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "memory_quota", hints[0].HintName.L) - require.Equal(t, int64(1024*1024), hints[0].HintData.(int64)) - require.Equal(t, "memory_quota", hints[1].HintName.L) - require.Equal(t, int64(1024*1024*1024), hints[1].HintData.(int64)) - - _, _, err = p.Parse("select /*+ MEMORY_QUOTA(18446744073709551612 MB), memory_quota(8689934592 GB) */ 1", "", "") - require.NoError(t, err) - - // Test HASH_AGG - stmt, _, err = p.Parse("select /*+ HASH_AGG(), hash_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "hash_agg", hints[0].HintName.L) - require.Equal(t, "hash_agg", hints[1].HintName.L) - - // Test MPPAgg - stmt, _, err = p.Parse("select /*+ MPP_1PHASE_AGG(), mpp_1phase_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "mpp_1phase_agg", hints[0].HintName.L) - require.Equal(t, "mpp_1phase_agg", hints[1].HintName.L) - - stmt, _, err = p.Parse("select /*+ MPP_2PHASE_AGG(), mpp_2phase_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "mpp_2phase_agg", hints[0].HintName.L) - require.Equal(t, "mpp_2phase_agg", hints[1].HintName.L) - - // Test ShuffleJoin - stmt, _, err = p.Parse("select /*+ SHUFFLE_JOIN(t1, t2), shuffle_join(t1, t2) */ * from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "shuffle_join", hints[0].HintName.L) - require.Equal(t, "shuffle_join", hints[1].HintName.L) - - // Test STREAM_AGG - stmt, _, err = p.Parse("select /*+ STREAM_AGG(), stream_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "stream_agg", hints[0].HintName.L) - require.Equal(t, "stream_agg", hints[1].HintName.L) - - // Test AGG_TO_COP - stmt, _, err = p.Parse("select /*+ AGG_TO_COP(), agg_to_cop() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "agg_to_cop", hints[0].HintName.L) - require.Equal(t, "agg_to_cop", hints[1].HintName.L) - - // Test NO_INDEX_MERGE - stmt, _, err = p.Parse("select /*+ NO_INDEX_MERGE(), no_index_merge() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_index_merge", hints[0].HintName.L) - require.Equal(t, "no_index_merge", hints[1].HintName.L) - - // Test READ_CONSISTENT_REPLICA - stmt, _, err = p.Parse("select /*+ READ_CONSISTENT_REPLICA(), read_consistent_replica() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "read_consistent_replica", hints[0].HintName.L) - require.Equal(t, "read_consistent_replica", hints[1].HintName.L) - - // Test LIMIT_TO_COP - stmt, _, err = p.Parse("select /*+ LIMIT_TO_COP(), limit_to_cop() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "limit_to_cop", hints[0].HintName.L) - require.Equal(t, "limit_to_cop", hints[1].HintName.L) - - // Test CTE MERGE - stmt, _, err = p.Parse("with cte(x) as (select * from t1) select /*+ MERGE(), merge() */ * from cte;", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "merge", hints[0].HintName.L) - require.Equal(t, "merge", hints[1].HintName.L) - - // Test STRAIGHT_JOIN - stmt, _, err = p.Parse("select /*+ STRAIGHT_JOIN(), straight_join() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "straight_join", hints[0].HintName.L) - require.Equal(t, "straight_join", hints[1].HintName.L) - - // Test LEADING - stmt, _, err = p.Parse("select /*+ LEADING(T1), LEADING(t2, t3), LEADING(T4, t5, t6) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 3) - require.Equal(t, "leading", hints[0].HintName.L) - require.Len(t, hints[0].Tables, 1) - require.Equal(t, "t1", hints[0].Tables[0].TableName.L) - - require.Equal(t, "leading", hints[1].HintName.L) - require.Len(t, hints[1].Tables, 2) - require.Equal(t, "t2", hints[1].Tables[0].TableName.L) - require.Equal(t, "t3", hints[1].Tables[1].TableName.L) - - require.Equal(t, "leading", hints[2].HintName.L) - require.Len(t, hints[2].Tables, 3) - require.Equal(t, "t4", hints[2].Tables[0].TableName.L) - require.Equal(t, "t5", hints[2].Tables[1].TableName.L) - require.Equal(t, "t6", hints[2].Tables[2].TableName.L) - - // Test NO_HASH_JOIN - stmt, _, err = p.Parse("select /*+ NO_HASH_JOIN(t1, t2), NO_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_hash_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - require.Equal(t, hints[0].Tables[1].TableName.L, "t2") - - require.Equal(t, "no_hash_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test NO_MERGE_JOIN - stmt, _, err = p.Parse("select /*+ NO_MERGE_JOIN(t1), NO_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_merge_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "no_merge_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test INDEX_JOIN - stmt, _, err = p.Parse("select /*+ INDEX_JOIN(t1), INDEX_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "index_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "index_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test NO_INDEX_JOIN - stmt, _, err = p.Parse("select /*+ NO_INDEX_JOIN(t1), NO_INDEX_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_index_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "no_index_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test INDEX_HASH_JOIN - stmt, _, err = p.Parse("select /*+ INDEX_HASH_JOIN(t1), INDEX_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "index_hash_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "index_hash_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test NO_INDEX_HASH_JOIN - stmt, _, err = p.Parse("select /*+ NO_INDEX_HASH_JOIN(t1), NO_INDEX_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_index_hash_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "no_index_hash_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test INDEX_MERGE_JOIN - stmt, _, err = p.Parse("select /*+ INDEX_MERGE_JOIN(t1), INDEX_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "index_merge_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "index_merge_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") - - // Test NO_INDEX_MERGE_JOIN - stmt, _, err = p.Parse("select /*+ NO_INDEX_MERGE_JOIN(t1), NO_INDEX_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") - require.NoError(t, err) - selectStmt = stmt[0].(*ast.SelectStmt) - - hints = selectStmt.TableHints - require.Len(t, hints, 2) - require.Equal(t, "no_index_merge_join", hints[0].HintName.L) - require.Equal(t, hints[0].Tables[0].TableName.L, "t1") - - require.Equal(t, "no_index_merge_join", hints[1].HintName.L) - require.Equal(t, hints[1].Tables[0].TableName.L, "t3") -} - -func TestType(t *testing.T) { - table := []testCase{ - // for time fsp - {"CREATE TABLE t( c1 TIME(2), c2 DATETIME(2), c3 TIMESTAMP(2) );", true, "CREATE TABLE `t` (`c1` TIME(2),`c2` DATETIME(2),`c3` TIMESTAMP(2))"}, - - // for hexadecimal - {"select x'0a', X'11', 0x11", true, "SELECT x'0a',x'11',x'11'"}, - {"select x'13181C76734725455A'", true, "SELECT x'13181c76734725455a'"}, - {"select x'0xaa'", false, ""}, - {"select 0X11", false, ""}, - {"select 0x4920616D2061206C6F6E672068657820737472696E67", true, "SELECT x'4920616d2061206c6f6e672068657820737472696e67'"}, - - // for bit - {"select 0b01, 0b0, b'11', B'11'", true, "SELECT b'1',b'0',b'11',b'11'"}, - // 0B01 and 0b21 are identifiers, the following two statement could parse. - // {"select 0B01", false, ""}, - // {"select 0b21", false, ""}, - - // for enum and set type - {"create table t (c1 enum('a', 'b'), c2 set('a', 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, - {"create table t (c1 enum('a ', 'b\t'), c2 set('a ', 'b\t'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b\t'),`c2` SET('a','b\t'))"}, - {"create table t (c1 enum('a', 'b') binary, c2 set('a', 'b') binary)", true, "CREATE TABLE `t` (`c1` ENUM('a','b') BINARY,`c2` SET('a','b') BINARY)"}, - {"create table t (c1 enum(0x61, 'b'), c2 set(0x61, 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, - {"create table t (c1 enum(0b01100001, 'b'), c2 set(0b01100001, 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, - {"create table t (c1 enum)", false, ""}, - {"create table t (c1 set)", false, ""}, - - // for blob and text field length - {"create table t (c1 blob(1024), c2 text(1024))", true, "CREATE TABLE `t` (`c1` BLOB(1024),`c2` TEXT(1024))"}, - - // for year - {"create table t (y year(4), y1 year)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, - {"create table t (y year(4) unsigned zerofill zerofill, y1 year signed unsigned zerofill)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, - - // for national - {"create table t (c1 national char(2), c2 national varchar(2))", true, "CREATE TABLE `t` (`c1` CHAR(2),`c2` VARCHAR(2))"}, - - // for json type - {`create table t (a JSON);`, true, "CREATE TABLE `t` (`a` JSON)"}, - } - RunTest(t, table, false) -} - -func TestPrivilege(t *testing.T) { - table := []testCase{ - // for create user - {`CREATE USER 'ttt' REQUIRE X509;`, true, "CREATE USER `ttt`@`%` REQUIRE X509"}, - {`CREATE USER 'ttt' REQUIRE SSL;`, true, "CREATE USER `ttt`@`%` REQUIRE SSL"}, - {`CREATE USER 'ttt' REQUIRE NONE;`, true, "CREATE USER `ttt`@`%` REQUIRE NONE"}, - {`CREATE USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';`, true, "CREATE USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, - {`CREATE USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' CIPHER 'EDH-RSA-DES-CBC3-SHA' SUBJECT '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com';`, true, "CREATE USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA' AND SUBJECT '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com'"}, - {`CREATE USER 'ttt' REQUIRE SAN 'DNS:mysql-user, URI:spiffe://example.org/myservice'`, true, "CREATE USER `ttt`@`%` REQUIRE SAN 'DNS:mysql-user, URI:spiffe://example.org/myservice'"}, - {`CREATE USER 'ttt' WITH MAX_QUERIES_PER_HOUR 2;`, true, "CREATE USER `ttt`@`%` WITH MAX_QUERIES_PER_HOUR 2"}, - {`CREATE USER 'ttt'@'localhost' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;`, true, "CREATE USER `ttt`@`localhost` REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, - {`CREATE USER 'u1'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ;`, true, "CREATE USER `u1`@`%` IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, - {`CREATE USER 'test'`, true, "CREATE USER `test`@`%`"}, - {`CREATE USER test`, true, "CREATE USER `test`@`%`"}, - {"CREATE USER `test`", true, "CREATE USER `test`@`%`"}, - {"CREATE USER test-user", false, ""}, - {"CREATE USER test.user", false, ""}, - {"CREATE USER 'test-user'", true, "CREATE USER `test-user`@`%`"}, - {"CREATE USER `test-user`", true, "CREATE USER `test-user`@`%`"}, - {"CREATE USER test.user", false, ""}, - {"CREATE USER 'test.user'", true, "CREATE USER `test.user`@`%`"}, - {"CREATE USER `test.user`", true, "CREATE USER `test.user`@`%`"}, - {"CREATE USER uesr1@LOCALhost", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER `uesr1`@localhost", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER uesr1@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER `uesr1`@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER 'uesr1'@localhost", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER uesr1@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER 'uesr1'@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER 'uesr1'@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, - {"CREATE USER `uesr1`@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, - {"create user 'test@localhost' password expire;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE"}, - {"create user 'test@localhost' password expire never;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE NEVER"}, - {"create user 'test@localhost' password expire default;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE DEFAULT"}, - {"create user 'test@localhost' password expire interval 3 day;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE INTERVAL 3 DAY"}, - {"create user 'test@localhost' identified by 'password' failed_login_attempts 3 password_lock_time 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 3"}, - {"create user 'test@localhost' identified by 'password' failed_login_attempts 3 password_lock_time unbounded;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME UNBOUNDED"}, - {"create user 'test@localhost' identified by 'password' failed_login_attempts 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3"}, - {"create user 'test@localhost' identified by 'password' password_lock_time 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' PASSWORD_LOCK_TIME 3"}, - {"create user 'test@localhost' identified by 'password' password_lock_time unbounded;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' PASSWORD_LOCK_TIME UNBOUNDED"}, - {"CREATE USER 'sha_test'@'localhost' IDENTIFIED WITH 'caching_sha2_password' BY 'sha_test'", true, "CREATE USER `sha_test`@`localhost` IDENTIFIED WITH 'caching_sha2_password' BY 'sha_test'"}, - {"CREATE USER 'sha_test3'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS 0x24412430303524255B03496C662C1055127B3B654A2F04207D01485276703644704B76303247474564416A516662346C5868646D32764C6B514F43585A473779565947514F34", true, "CREATE USER `sha_test3`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'"}, - {"CREATE USER 'sha_test4'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'", true, "CREATE USER `sha_test4`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'"}, - {"CREATE USER `user@pingcap.com`@'localhost' IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc' ATTRIBUTE '{\"email\": \"user@pingcap.com\"}'", true, "CREATE USER `user@pingcap.com`@`localhost` IDENTIFIED WITH 'tidb_auth_token' REQUIRE TOKEN_ISSUER 'issuer-abc' ATTRIBUTE '{\"email\": \"user@pingcap.com\"}'"}, - {"CREATE USER 'nopwd_native'@'localhost' IDENTIFIED WITH 'mysql_native_password'", true, "CREATE USER `nopwd_native`@`localhost` IDENTIFIED WITH 'mysql_native_password'"}, - {"CREATE USER 'nopwd_sha'@'localhost' IDENTIFIED WITH 'caching_sha2_password'", true, "CREATE USER `nopwd_sha`@`localhost` IDENTIFIED WITH 'caching_sha2_password'"}, - {"CREATE ROLE `test-role`, `role1`@'localhost'", true, "CREATE ROLE `test-role`@`%`, `role1`@`localhost`"}, - {"CREATE ROLE `test-role`", true, "CREATE ROLE `test-role`@`%`"}, - {"CREATE ROLE role1", true, "CREATE ROLE `role1`@`%`"}, - {"CREATE ROLE `role1`@'localhost'", true, "CREATE ROLE `role1`@`localhost`"}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password'"}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password by 'new-password'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password' BY 'new-password'"}, - {"create user 'bug19354014user'@'%' identified WITH mysql_native_password as 'hashstring'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, - {`CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER IF NOT EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, - {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, - {`CREATE USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, - {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, - {`CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY 'hashstring' RESOURCE GROUP rg1`, true, "CREATE USER `root`@`127.0.0.1` IDENTIFIED BY 'hashstring' RESOURCE GROUP `rg1`"}, - {`ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, - {`ALTER USER 'root'@'localhost' RESOURCE GROUP rg2`, true, "ALTER USER `root`@`localhost` RESOURCE GROUP `rg2`"}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, - {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, - {`ALTER USER USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER USER() IDENTIFIED BY 'new-password'"}, - {`ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'"}, - {"alter user 'test@localhost' password expire;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE"}, - {"alter user 'test@localhost' password expire never;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE NEVER"}, - {"alter user 'test@localhost' password expire default;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE DEFAULT"}, - {"alter user 'test@localhost' password expire interval 3 day;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE INTERVAL 3 DAY"}, - {"ALTER USER 'ttt' REQUIRE X509;", true, "ALTER USER `ttt`@`%` REQUIRE X509"}, - {"ALTER USER 'ttt' REQUIRE SSL;", true, "ALTER USER `ttt`@`%` REQUIRE SSL"}, - {"ALTER USER 'ttt' REQUIRE NONE;", true, "ALTER USER `ttt`@`%` REQUIRE NONE"}, - {"ALTER USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';", true, "ALTER USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, - {"ALTER USER 'ttt' WITH MAX_QUERIES_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_QUERIES_PER_HOUR 2"}, - {"ALTER USER 'ttt' WITH MAX_UPDATES_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_UPDATES_PER_HOUR 2"}, - {"ALTER USER 'ttt' WITH MAX_CONNECTIONS_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_CONNECTIONS_PER_HOUR 2"}, - {"ALTER USER 'ttt' WITH MAX_USER_CONNECTIONS 2;", true, "ALTER USER `ttt`@`%` WITH MAX_USER_CONNECTIONS 2"}, - {"ALTER USER 'ttt'@'localhost' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;", true, "ALTER USER `ttt`@`localhost` REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, - {`DROP USER 'root'@'localhost', 'root1'@'localhost'`, true, "DROP USER `root`@`localhost`, `root1`@`localhost`"}, - {`DROP USER IF EXISTS 'root'@'localhost'`, true, "DROP USER IF EXISTS `root`@`localhost`"}, - {`RENAME USER 'root'@'localhost' TO 'root'@'%'`, true, "RENAME USER `root`@`localhost` TO `root`@`%`"}, - {`RENAME USER 'fred' TO 'barry'`, true, "RENAME USER `fred`@`%` TO `barry`@`%`"}, - {`RENAME USER u1 to u2, u3 to u4`, true, "RENAME USER `u1`@`%` TO `u2`@`%`, `u3`@`%` TO `u4`@`%`"}, - {`DROP ROLE 'role'@'localhost', 'role1'@'localhost'`, true, "DROP ROLE `role`@`localhost`, `role1`@`localhost`"}, - {`DROP ROLE 'administrator', 'developer';`, true, "DROP ROLE `administrator`@`%`, `developer`@`%`"}, - {`DROP ROLE IF EXISTS 'role'@'localhost'`, true, "DROP ROLE IF EXISTS `role`@`localhost`"}, - - // for grant statement - {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE X509;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE X509"}, - {"GRANT ALL ON db1.* TO 'jeffrey'@'LOCALhost' REQUIRE SSL;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE SSL"}, - {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE NONE;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE NONE"}, - {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, - {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost';", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost`"}, - {"GRANT ALL ON TABLE db1.* TO 'jeffrey'@'localhost';", true, "GRANT ALL ON TABLE `db1`.* TO `jeffrey`@`localhost`"}, - {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' WITH GRANT OPTION;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` WITH GRANT OPTION"}, - {"GRANT SELECT ON db2.invoice TO 'jeffrey'@'localhost';", true, "GRANT SELECT ON `db2`.`invoice` TO `jeffrey`@`localhost`"}, - {"GRANT ALL ON *.* TO 'someuser'@'somehost';", true, "GRANT ALL ON *.* TO `someuser`@`somehost`"}, - {"GRANT ALL ON *.* TO 'SOMEuser'@'SOMEhost';", true, "GRANT ALL ON *.* TO `SOMEuser`@`somehost`"}, - {"GRANT SELECT, INSERT ON *.* TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON *.* TO `someuser`@`somehost`"}, - {"GRANT ALL ON mydb.* TO 'someuser'@'somehost';", true, "GRANT ALL ON `mydb`.* TO `someuser`@`somehost`"}, - {"GRANT SELECT, INSERT ON mydb.* TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON `mydb`.* TO `someuser`@`somehost`"}, - {"GRANT ALL ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT ALL ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, - {"GRANT SELECT, INSERT ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, - {"GRANT SELECT (col1), INSERT (col1,col2) ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT SELECT (`col1`), INSERT (`col1`,`col2`) ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, - {"grant all privileges on zabbix.* to 'zabbix'@'localhost' identified by 'password';", true, "GRANT ALL ON `zabbix`.* TO `zabbix`@`localhost` IDENTIFIED BY 'password'"}, - {"GRANT SELECT ON test.* to 'test'", true, "GRANT SELECT ON `test`.* TO `test`@`%`"}, // For issue 2654. - {"grant PROCESS,usage, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'xxxxxxxxxx'@'%' identified by password 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'", true, "GRANT PROCESS, USAGE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `xxxxxxxxxx`@`%` IDENTIFIED WITH 'mysql_native_password' AS 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'"}, - {"/* rds internal mark */ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, RELOAD, PROCESS, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER on *.* to 'root2'@'%' identified by password '*sdsadsdsadssadsadsadsadsada' with grant option", true, "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, RELOAD, PROCESS, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO `root2`@`%` IDENTIFIED WITH 'mysql_native_password' AS '*sdsadsdsadssadsadsadsadsada' WITH GRANT OPTION"}, - {"GRANT 'role1', 'role2' TO 'user1'@'LOCalhost', 'user2'@'LOcalhost';", true, "GRANT `role1`@`%`, `role2`@`%` TO `user1`@`localhost`, `user2`@`localhost`"}, - {"GRANT 'u1' TO 'u1';", true, "GRANT `u1`@`%` TO `u1`@`%`"}, - {"GRANT 'app_read'@'%','app_write'@'%' TO 'rw_user1'@'localhost'", true, "GRANT `app_read`@`%`, `app_write`@`%` TO `rw_user1`@`localhost`"}, - {"GRANT 'app_developer' TO 'dev1'@'localhost';", true, "GRANT `app_developer`@`%` TO `dev1`@`localhost`"}, - {"GRANT SHUTDOWN ON *.* TO 'dev1'@'localhost';", true, "GRANT SHUTDOWN ON *.* TO `dev1`@`localhost`"}, - {"GRANT CONFIG ON *.* TO 'dev1'@'localhost';", true, "GRANT CONFIG ON *.* TO `dev1`@`localhost`"}, - {"GRANT CREATE ON *.* TO 'dev1'@'localhost';", true, "GRANT CREATE ON *.* TO `dev1`@`localhost`"}, - {"GRANT CREATE TABLESPACE ON *.* TO 'dev1'@'localhost';", true, "GRANT CREATE TABLESPACE ON *.* TO `dev1`@`localhost`"}, - {"GRANT EXECUTE ON FUNCTION db1.anomaly_score TO 'user1'@'domain-or-ip-address1'", true, "GRANT EXECUTE ON FUNCTION `db1`.`anomaly_score` TO `user1`@`domain-or-ip-address1`"}, - {"GRANT EXECUTE ON PROCEDURE mydb.myproc TO 'someuser'@'somehost'", true, "GRANT EXECUTE ON PROCEDURE `mydb`.`myproc` TO `someuser`@`somehost`"}, - {"GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN ON *.* TO 'root'@'localhost'", true, "GRANT APPLICATION_PASSWORD_ADMIN, AUDIT_ADMIN ON *.* TO `root`@`localhost`"}, - {"GRANT LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'localhost'", true, "GRANT LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO `root`@`localhost`"}, - {"GRANT PROXY ON 'localuser'@'localhost' TO 'externaluser'@'somehost'", true, "GRANT PROXY ON `localuser`@`localhost` TO `externaluser`@`somehost`"}, - {"GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION", true, "GRANT PROXY ON ``@`` TO `root`@`localhost` WITH GRANT OPTION"}, - {"GRANT PROXY ON 'proxied_user' TO 'proxy_user1', 'proxy_user2'", true, "GRANT PROXY ON `proxied_user`@`%` TO `proxy_user1`@`%`, `proxy_user2`@`%`"}, - {"grant grant option on *.* to u1", true, "GRANT GRANT OPTION ON *.* TO `u1`@`%`"}, // not typical syntax, but supported - - // for revoke statement - {"REVOKE ALL ON db1.* FROM 'jeffrey'@'LOCalhost';", true, "REVOKE ALL ON `db1`.* FROM `jeffrey`@`localhost`"}, - {"REVOKE SELECT ON db2.invoice FROM 'jeffrey'@'localhost';", true, "REVOKE SELECT ON `db2`.`invoice` FROM `jeffrey`@`localhost`"}, - {"REVOKE ALL ON *.* FROM 'someuser'@'somehost';", true, "REVOKE ALL ON *.* FROM `someuser`@`somehost`"}, - {"REVOKE SELECT, INSERT ON *.* FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON *.* FROM `someuser`@`somehost`"}, - {"REVOKE ALL ON mydb.* FROM 'someuser'@'somehost';", true, "REVOKE ALL ON `mydb`.* FROM `someuser`@`somehost`"}, - {"REVOKE SELECT, INSERT ON mydb.* FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON `mydb`.* FROM `someuser`@`somehost`"}, - {"REVOKE ALL ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE ALL ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, - {"REVOKE SELECT, INSERT ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, - {"REVOKE SELECT (col1), INSERT (col1,col2) ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE SELECT (`col1`), INSERT (`col1`,`col2`) ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, - {"REVOKE all privileges on zabbix.* FROM 'zabbix'@'localhost' identified by 'password';", true, "REVOKE ALL ON `zabbix`.* FROM `zabbix`@`localhost` IDENTIFIED BY 'password'"}, - {"REVOKE 'role1', 'role2' FROM 'user1'@'localhost', 'user2'@'localhost';", true, "REVOKE `role1`@`%`, `role2`@`%` FROM `user1`@`localhost`, `user2`@`localhost`"}, - {"REVOKE SHUTDOWN ON *.* FROM 'dev1'@'localhost';", true, "REVOKE SHUTDOWN ON *.* FROM `dev1`@`localhost`"}, - {"REVOKE CONFIG ON *.* FROM 'dev1'@'localhost';", true, "REVOKE CONFIG ON *.* FROM `dev1`@`localhost`"}, - {"REVOKE EXECUTE ON FUNCTION db.func FROM 'user'@'localhost'", true, "REVOKE EXECUTE ON FUNCTION `db`.`func` FROM `user`@`localhost`"}, - {"REVOKE EXECUTE ON PROCEDURE db.func FROM 'user'@'localhost'", true, "REVOKE EXECUTE ON PROCEDURE `db`.`func` FROM `user`@`localhost`"}, - {"REVOKE APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN ON *.* FROM 'root'@'localhost'", true, "REVOKE APPLICATION_PASSWORD_ADMIN, AUDIT_ADMIN ON *.* FROM `root`@`localhost`"}, - {"revoke all privileges, grant option from u1", true, "REVOKE ALL, GRANT OPTION ON *.* FROM `u1`@`%`"}, // special case syntax - {"revoke all privileges, grant option from u1, u2, u3", true, "REVOKE ALL, GRANT OPTION ON *.* FROM `u1`@`%`, `u2`@`%`, `u3`@`%`"}, // special case syntax - } - RunTest(t, table, false) -} - -func TestComment(t *testing.T) { - table := []testCase{ - {"create table t (c int comment 'comment')", true, "CREATE TABLE `t` (`c` INT COMMENT 'comment')"}, - {"create table t (c int) comment = 'comment'", true, "CREATE TABLE `t` (`c` INT) COMMENT = 'comment'"}, - {"create table t (c int) comment 'comment'", true, "CREATE TABLE `t` (`c` INT) COMMENT = 'comment'"}, - {"create table t (c int) comment comment", false, ""}, - {"create table t (comment text)", true, "CREATE TABLE `t` (`comment` TEXT)"}, - {"START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */", true, "START TRANSACTION"}, - // for comment in query - {"/*comment*/ /*comment*/ select c /* this is a comment */ from t;", true, "SELECT `c` FROM `t`"}, - // for unclosed comment - {"delete from t where a = 7 or 1=1/*' and b = 'p'", false, ""}, - - {"create table t (ssl int)", false, ""}, - {"create table t (require int)", false, ""}, - {"create table t (account int)", true, "CREATE TABLE `t` (`account` INT)"}, - {"create table t (expire int)", true, "CREATE TABLE `t` (`expire` INT)"}, - {"create table t (cipher int)", true, "CREATE TABLE `t` (`cipher` INT)"}, - {"create table t (issuer int)", true, "CREATE TABLE `t` (`issuer` INT)"}, - {"create table t (never int)", true, "CREATE TABLE `t` (`never` INT)"}, - {"create table t (subject int)", true, "CREATE TABLE `t` (`subject` INT)"}, - {"create table t (x509 int)", true, "CREATE TABLE `t` (`x509` INT)"}, - - // COMMENT/ATTRIBUTE in CREATE/ALTER USER - {"create user commentUser COMMENT '123456' '{\"name\": \"Tom\", \"age\", 19}", false, ""}, - {"alter user commentUser COMMENT '123456' '{\"name\": \"Tom\", \"age\", 19}", false, ""}, - {"create user commentUser COMMENT '123456'", true, "CREATE USER `commentUser`@`%` COMMENT '123456'"}, - {"alter user commentUser COMMENT '123456'", true, "ALTER USER `commentUser`@`%` COMMENT '123456'"}, - {"create user commentUser ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'", true, "CREATE USER `commentUser`@`%` ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'"}, - {"alter user commentUser ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'", true, "ALTER USER `commentUser`@`%` ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'"}, - } - RunTest(t, table, false) -} - -func TestParserErrMsg(t *testing.T) { - commentMsgCases := []testErrMsgCase{ - {"delete from t where a = 7 or 1=1/*' and b = 'p'", errors.New("near '/*' and b = 'p'' at line 1")}, - {"delete from t where a = 7 or\n 1=1/*' and b = 'p'", errors.New("near '/*' and b = 'p'' at line 2")}, - {"select 1/*", errors.New("near '/*' at line 1")}, - {"select 1/* comment */", nil}, - } - funcCallMsgCases := []testErrMsgCase{ - {"select a.b()", nil}, - {"SELECT foo.bar('baz');", nil}, - } - RunErrMsgTest(t, commentMsgCases) - RunErrMsgTest(t, funcCallMsgCases) -} - -type subqueryChecker struct { - text string - t *testing.T -} - -// Enter implements ast.Visitor interface. -func (sc *subqueryChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { - if expr, ok := inNode.(*ast.SubqueryExpr); ok { - require.Equal(sc.t, sc.text, expr.Query.Text()) - return inNode, true - } - return inNode, false -} - -// Leave implements ast.Visitor interface. -func (sc *subqueryChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { - return inNode, true -} - -func TestSubquery(t *testing.T) { - table := []testCase{ - // for compare subquery - {"SELECT 1 > (select 1)", true, "SELECT 1>(SELECT 1)"}, - {"SELECT 1 > ANY (select 1)", true, "SELECT 1>ANY (SELECT 1)"}, - {"SELECT 1 > ALL (select 1)", true, "SELECT 1>ALL (SELECT 1)"}, - {"SELECT 1 > SOME (select 1)", true, "SELECT 1>ANY (SELECT 1)"}, - - // for exists subquery - {"SELECT EXISTS select 1", false, ""}, - {"SELECT EXISTS (select 1)", true, "SELECT EXISTS (SELECT 1)"}, - {"SELECT + EXISTS (select 1)", true, "SELECT +EXISTS (SELECT 1)"}, - {"SELECT - EXISTS (select 1)", true, "SELECT -EXISTS (SELECT 1)"}, - {"SELECT NOT EXISTS (select 1)", true, "SELECT NOT EXISTS (SELECT 1)"}, - {"SELECT + NOT EXISTS (select 1)", false, ""}, - {"SELECT - NOT EXISTS (select 1)", false, ""}, - {"SELECT * FROM t where t.a in (select a from t limit 1, 10)", true, "SELECT * FROM `t` WHERE `t`.`a` IN (SELECT `a` FROM `t` LIMIT 1,10)"}, - {"SELECT * FROM t where t.a in ((select a from t limit 1, 10))", true, "SELECT * FROM `t` WHERE `t`.`a` IN ((SELECT `a` FROM `t` LIMIT 1,10))"}, - {"SELECT * FROM t where t.a in ((select a from t limit 1, 10), 1)", true, "SELECT * FROM `t` WHERE `t`.`a` IN ((SELECT `a` FROM `t` LIMIT 1,10),1)"}, - {"select * from ((select a from t) t1 join t t2) join t3", true, "SELECT * FROM ((SELECT `a` FROM `t`) AS `t1` JOIN `t` AS `t2`) JOIN `t3`"}, - {"SELECT t1.a AS a FROM ((SELECT a FROM t) AS t1)", true, "SELECT `t1`.`a` AS `a` FROM (SELECT `a` FROM `t`) AS `t1`"}, - {"select count(*) from (select a, b from x1 union all select a, b from x3 union all (select x1.a, x3.b from (select * from x3 union all select * from x2) x3 left join x1 on x3.a = x1.b))", true, "SELECT COUNT(1) FROM (SELECT `a`,`b` FROM `x1` UNION ALL SELECT `a`,`b` FROM `x3` UNION ALL (SELECT `x1`.`a`,`x3`.`b` FROM (SELECT * FROM `x3` UNION ALL SELECT * FROM `x2`) AS `x3` LEFT JOIN `x1` ON `x3`.`a`=`x1`.`b`))"}, - {"(SELECT 1 a,3 b) UNION (SELECT 2,1) ORDER BY (SELECT 2)", true, "(SELECT 1 AS `a`,3 AS `b`) UNION (SELECT 2,1) ORDER BY (SELECT 2)"}, - {"((select * from t1)) union (select * from t1)", true, "(SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)"}, - {"(((select * from t1))) union (select * from t1)", true, "(SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)"}, - {"select * from (((select * from t1)) union (select * from t1) union (select * from t1)) a", true, "SELECT * FROM ((SELECT * FROM `t1`) UNION (SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)) AS `a`"}, - {"SELECT COUNT(*) FROM plan_executions WHERE (EXISTS((SELECT * FROM triggers WHERE plan_executions.trigger_id=triggers.id AND triggers.type='CRON')))", true, "SELECT COUNT(1) FROM `plan_executions` WHERE (EXISTS (SELECT * FROM `triggers` WHERE `plan_executions`.`trigger_id`=`triggers`.`id` AND `triggers`.`type`=_UTF8MB4'CRON'))"}, - {"select exists((select 1));", true, "SELECT EXISTS (SELECT 1)"}, - {"select * from ((SELECT 1 a,3 b) UNION (SELECT 2,1) ORDER BY (SELECT 2)) t order by a,b", true, "SELECT * FROM ((SELECT 1 AS `a`,3 AS `b`) UNION (SELECT 2,1) ORDER BY (SELECT 2)) AS `t` ORDER BY `a`,`b`"}, - {"select (select * from t1 where a != t.a union all (select * from t2 where a != t.a) order by a limit 1) from t1 t", true, "SELECT (SELECT * FROM `t1` WHERE `a`!=`t`.`a` UNION ALL (SELECT * FROM `t2` WHERE `a`!=`t`.`a`) ORDER BY `a` LIMIT 1) FROM `t1` AS `t`"}, - {"(WITH v0 AS (SELECT TRUE) (SELECT 'abc' EXCEPT (SELECT TRUE)))", true, "WITH `v0` AS (SELECT TRUE) (SELECT _UTF8MB4'abc' EXCEPT (SELECT TRUE))"}, - } - RunTest(t, table, false) - - tests := []struct { - input string - text string - }{ - {"SELECT 1 > (select 1)", "select 1"}, - {"SELECT 1 > (select 1 union select 2)", "select 1 union select 2"}, - } - p := parser.New() - for _, tbl := range tests { - stmt, err := p.ParseOneStmt(tbl.input, "", "") - require.NoError(t, err) - stmt.Accept(&subqueryChecker{ - text: tbl.text, - t: t, - }) - } -} - -func TestSetOperator(t *testing.T) { - table := []testCase{ - // union and union all - {"select c1 from t1 union select c2 from t2", true, "SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, - {"select c1 from t1 union (select c2 from t2)", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`)"}, - {"select c1 from t1 union (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, - {"select c1 from t1 union select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2` ORDER BY `c2`"}, - {"select c1 from t1 union (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1"}, - {"select c1 from t1 union (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"select c1 from t1 union (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) union distinct select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2`"}, - {"(select c1 from t1) union distinctrow select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2`"}, - {"(select c1 from t1) union all select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION ALL SELECT `c2` FROM `t2`"}, - {"(select c1 from t1) union distinct all select c2 from t2", false, ""}, - {"(select c1 from t1) union distinctrow all select c2 from t2", false, ""}, - {"(select c1 from t1) union (select c2 from t2) order by c1 union select c3 from t3", false, ""}, - {"(select c1 from t1) union (select c2 from t2) limit 1 union select c3 from t3", false, ""}, - {"(select c1 from t1) union select c2 from t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"select (select 1 union select 1) as a", true, "SELECT (SELECT 1 UNION SELECT 1) AS `a`"}, - {"select * from (select 1 union select 2) as a", true, "SELECT * FROM (SELECT 1 UNION SELECT 2) AS `a`"}, - {"insert into t select c1 from t1 union select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, - {"insert into t (c) select c1 from t1 union select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, - {"select 2 as a from dual union select 1 as b from dual order by a", true, "SELECT 2 AS `a` UNION SELECT 1 AS `b` ORDER BY `a`"}, - {"table t1 union table t2", true, "TABLE `t1` UNION TABLE `t2`"}, - {"table t1 union (table t2)", true, "TABLE `t1` UNION (TABLE `t2`)"}, - {"table t1 union select * from t2", true, "TABLE `t1` UNION SELECT * FROM `t2`"}, - {"select * from t1 union table t2", true, "SELECT * FROM `t1` UNION TABLE `t2`"}, - {"table t1 union (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"select c1 from t1 union (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` UNION (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) union table t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION TABLE `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"(table t1) union select c2 from t2 union (table t3) order by c1 limit 1", true, "(TABLE `t1`) UNION SELECT `c2` FROM `t2` UNION (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, - {"values row(1,-2,3), row(5,7,9) union values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"values row(1,-2,3), row(5,7,9) union (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION (VALUES ROW(1,-2,3), ROW(5,7,9))"}, - {"values row(1,-2,3), row(5,7,9) union select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION SELECT * FROM `t`"}, - {"values row(1,-2,3), row(5,7,9) union table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION TABLE `t`"}, - {"select * from t union values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"table t union values row(1,-2,3), row(5,7,9)", true, "TABLE `t` UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, - // except - {"select c1 from t1 except select c2 from t2", true, "SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, - {"select c1 from t1 except (select c2 from t2)", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`)"}, - {"select c1 from t1 except (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, - {"select c1 from t1 except select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2` ORDER BY `c2`"}, - {"select c1 from t1 except (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) LIMIT 1"}, - {"select c1 from t1 except (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"select c1 from t1 except (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) except (select c2 from t2) order by c1 except select c3 from t3", false, ""}, - {"(select c1 from t1) except (select c2 from t2) limit 1 except select c3 from t3", false, ""}, - {"(select c1 from t1) except select c2 from t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"select (select 1 except select 1) as a", true, "SELECT (SELECT 1 EXCEPT SELECT 1) AS `a`"}, - {"select * from (select 1 except select 2) as a", true, "SELECT * FROM (SELECT 1 EXCEPT SELECT 2) AS `a`"}, - {"insert into t select c1 from t1 except select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, - {"insert into t (c) select c1 from t1 except select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, - {"select 2 as a from dual except select 1 as b from dual order by a", true, "SELECT 2 AS `a` EXCEPT SELECT 1 AS `b` ORDER BY `a`"}, - {"table t1 except table t2", true, "TABLE `t1` EXCEPT TABLE `t2`"}, - {"table t1 except (table t2)", true, "TABLE `t1` EXCEPT (TABLE `t2`)"}, - {"table t1 except select * from t2", true, "TABLE `t1` EXCEPT SELECT * FROM `t2`"}, - {"select * from t1 except table t2", true, "SELECT * FROM `t1` EXCEPT TABLE `t2`"}, - {"table t1 except (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"select c1 from t1 except (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) except table t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT TABLE `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"(table t1) except select c2 from t2 except (table t3) order by c1 limit 1", true, "(TABLE `t1`) EXCEPT SELECT `c2` FROM `t2` EXCEPT (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, - {"values row(1,-2,3), row(5,7,9) except values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"values row(1,-2,3), row(5,7,9) except (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT (VALUES ROW(1,-2,3), ROW(5,7,9))"}, - {"values row(1,-2,3), row(5,7,9) except select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT SELECT * FROM `t`"}, - {"values row(1,-2,3), row(5,7,9) except table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT TABLE `t`"}, - {"select * from t except values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"table t except values row(1,-2,3), row(5,7,9)", true, "TABLE `t` EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - // intersect - {"select c1 from t1 intersect select c2 from t2", true, "SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, - {"select c1 from t1 intersect (select c2 from t2)", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`)"}, - {"select c1 from t1 intersect (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, - {"select c1 from t1 intersect select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2` ORDER BY `c2`"}, - {"select c1 from t1 intersect (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) LIMIT 1"}, - {"select c1 from t1 intersect (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"select c1 from t1 intersect (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) intersect (select c2 from t2) order by c1 intersect select c3 from t3", false, ""}, - {"(select c1 from t1) intersect (select c2 from t2) limit 1 intersect select c3 from t3", false, ""}, - {"(select c1 from t1) intersect select c2 from t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT SELECT `c2` FROM `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"select (select 1 intersect select 1) as a", true, "SELECT (SELECT 1 INTERSECT SELECT 1) AS `a`"}, - {"select * from (select 1 intersect select 2) as a", true, "SELECT * FROM (SELECT 1 INTERSECT SELECT 2) AS `a`"}, - {"insert into t select c1 from t1 intersect select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, - {"insert into t (c) select c1 from t1 intersect select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, - {"select 2 as a from dual intersect select 1 as b from dual order by a", true, "SELECT 2 AS `a` INTERSECT SELECT 1 AS `b` ORDER BY `a`"}, - {"table t1 intersect table t2", true, "TABLE `t1` INTERSECT TABLE `t2`"}, - {"table t1 intersect (table t2)", true, "TABLE `t1` INTERSECT (TABLE `t2`)"}, - {"table t1 intersect select * from t2", true, "TABLE `t1` INTERSECT SELECT * FROM `t2`"}, - {"select * from t1 intersect table t2", true, "SELECT * FROM `t1` INTERSECT TABLE `t2`"}, - {"table t1 intersect (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, - {"select c1 from t1 intersect (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) intersect table t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT TABLE `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"(table t1) intersect select c2 from t2 intersect (table t3) order by c1 limit 1", true, "(TABLE `t1`) INTERSECT SELECT `c2` FROM `t2` INTERSECT (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, - {"values row(1,-2,3), row(5,7,9) intersect values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"values row(1,-2,3), row(5,7,9) intersect (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT (VALUES ROW(1,-2,3), ROW(5,7,9))"}, - {"values row(1,-2,3), row(5,7,9) intersect select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT SELECT * FROM `t`"}, - {"values row(1,-2,3), row(5,7,9) intersect table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT TABLE `t`"}, - {"select * from t intersect values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - {"table t intersect values row(1,-2,3), row(5,7,9)", true, "TABLE `t` INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, - // mixture of union, except and intersect - {"(select c1 from t1) intersect select c2 from t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) union all select c2 from t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION ALL SELECT `c2` FROM `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) except select c2 from t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"select 1 union distinct select 1 except select 1 intersect select 1", true, "SELECT 1 UNION SELECT 1 EXCEPT SELECT 1 INTERSECT SELECT 1"}, - // mixture of union, except and intersect with parentheses - {"(select c1 from t1) intersect all (select c2 from t2 union (select c3 from t3)) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT ALL (SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`)) ORDER BY `c1` LIMIT 1"}, - {"(select c1 from t1) union all (select c2 from t2 except select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION ALL (SELECT `c2` FROM `t2` EXCEPT SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"((select c1 from t1) except select c2 from t2) intersect all (select c3 from t3) order by c1 limit 1", true, "((SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2`) INTERSECT ALL (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, - {"select 1 union distinct (select 1 except all select 1 intersect select 1)", true, "SELECT 1 UNION (SELECT 1 EXCEPT ALL SELECT 1 INTERSECT SELECT 1)"}, - } - RunTest(t, table, false) -} - -func checkOrderBy(t *testing.T, s ast.Node, hasOrderBy []bool, i int) int { - switch x := s.(type) { - case *ast.SelectStmt: - require.Equal(t, hasOrderBy[i], x.OrderBy != nil) - return i + 1 - case *ast.SetOprSelectList: - for _, sel := range x.Selects { - i = checkOrderBy(t, sel, hasOrderBy, i) - } - return i - } - return i -} - -func TestUnionOrderBy(t *testing.T) { - p := parser.New() - p.EnableWindowFunc(false) - - tests := []struct { - src string - hasOrderBy []bool - }{ - {"select 2 as a from dual union select 1 as b from dual order by a", []bool{false, false, true}}, - {"select 2 as a from dual union (select 1 as b from dual order by a)", []bool{false, true, false}}, - {"(select 2 as a from dual order by a) union select 1 as b from dual order by a", []bool{true, false, true}}, - {"select 1 a, 2 b from dual order by a", []bool{true}}, - {"select 1 a, 2 b from dual", []bool{false}}, - } - - for _, tbl := range tests { - stmt, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err) - us, ok := stmt[0].(*ast.SetOprStmt) - if ok { - var i int - for _, s := range us.SelectList.Selects { - i = checkOrderBy(t, s, tbl.hasOrderBy, i) - } - require.Equal(t, tbl.hasOrderBy[i], us.OrderBy != nil) - } - ss, ok := stmt[0].(*ast.SelectStmt) - if ok { - require.Equal(t, tbl.hasOrderBy[0], ss.OrderBy != nil) - } - } -} - -func TestLikeEscape(t *testing.T) { - table := []testCase{ - // for like escape - {`select "abc_" like "abc\\_" escape ''`, true, "SELECT _UTF8MB4'abc_' LIKE _UTF8MB4'abc\\_'"}, - {`select "abc_" like "abc\\_" escape '\\'`, true, "SELECT _UTF8MB4'abc_' LIKE _UTF8MB4'abc\\_'"}, - {`select "abc_" like "abc\\_" escape '||'`, false, ""}, - {`select "abc" like "escape" escape '+'`, true, "SELECT _UTF8MB4'abc' LIKE _UTF8MB4'escape' ESCAPE '+'"}, - {"select '''_' like '''_' escape ''''", true, "SELECT _UTF8MB4'''_' LIKE _UTF8MB4'''_' ESCAPE ''''"}, - } - - RunTest(t, table, false) -} - -func TestLockUnlockTables(t *testing.T) { - table := []testCase{ - {`UNLOCK TABLES;`, true, "UNLOCK TABLES"}, - {`LOCK TABLES t1 READ;`, true, "LOCK TABLES `t1` READ"}, - {`LOCK TABLES t1 READ LOCAL;`, true, "LOCK TABLES `t1` READ LOCAL"}, - {`show table status like 't'`, true, "SHOW TABLE STATUS LIKE _UTF8MB4't'"}, - {`LOCK TABLES t2 WRITE`, true, "LOCK TABLES `t2` WRITE"}, - {`LOCK TABLES t2 WRITE LOCAL;`, true, "LOCK TABLES `t2` WRITE LOCAL"}, - {`LOCK TABLES t1 WRITE, t2 READ;`, true, "LOCK TABLES `t1` WRITE, `t2` READ"}, - {`LOCK TABLES t1 WRITE LOCAL, t2 READ LOCAL;`, true, "LOCK TABLES `t1` WRITE LOCAL, `t2` READ LOCAL"}, - - // for unlock table and lock table - {`UNLOCK TABLE;`, true, "UNLOCK TABLES"}, - {`LOCK TABLE t1 READ;`, true, "LOCK TABLES `t1` READ"}, - {`LOCK TABLE t1 READ LOCAL;`, true, "LOCK TABLES `t1` READ LOCAL"}, - {`show table status like 't'`, true, "SHOW TABLE STATUS LIKE _UTF8MB4't'"}, - {`LOCK TABLE t2 WRITE`, true, "LOCK TABLES `t2` WRITE"}, - {`LOCK TABLE t2 WRITE LOCAL;`, true, "LOCK TABLES `t2` WRITE LOCAL"}, - {`LOCK TABLE t1 WRITE, t2 READ;`, true, "LOCK TABLES `t1` WRITE, `t2` READ"}, - - // for cleanup table lock. - {"ADMIN CLEANUP TABLE LOCK", false, ""}, - {"ADMIN CLEANUP TABLE LOCK t", true, "ADMIN CLEANUP TABLE LOCK `t`"}, - {"ADMIN CLEANUP TABLE LOCK t1,t2", true, "ADMIN CLEANUP TABLE LOCK `t1`, `t2`"}, - - // For alter table read only/write. - {"ALTER TABLE t READ ONLY", true, "ALTER TABLE `t` READ ONLY"}, - {"ALTER TABLE t READ WRITE", true, "ALTER TABLE `t` READ WRITE"}, - } - - RunTest(t, table, false) -} - -func TestWithRollup(t *testing.T) { - table := []testCase{ - {`select * from t group by a, b rollup`, false, ""}, - {`select * from t group by a, b with rollup`, true, "SELECT * FROM `t` GROUP BY `a`,`b` WITH ROLLUP"}, - // should be ERROR 1241 (21000): Operand should contain 1 column(s) in runtime. - {`select * from t group by (a, b) with rollup`, true, "SELECT * FROM `t` GROUP BY ROW(`a`,`b`) WITH ROLLUP"}, - {`select * from t group by (a+b) with rollup`, true, "SELECT * FROM `t` GROUP BY (`a`+`b`) WITH ROLLUP"}, - } - RunTest(t, table, false) -} - -func TestIndexHint(t *testing.T) { - table := []testCase{ - {`select * from t use index (primary)`, true, "SELECT * FROM `t` USE INDEX (`primary`)"}, - {"select * from t use index (`primary`)", true, "SELECT * FROM `t` USE INDEX (`primary`)"}, - {`select * from t use index ();`, true, "SELECT * FROM `t` USE INDEX ()"}, - {`select * from t use index (idx);`, true, "SELECT * FROM `t` USE INDEX (`idx`)"}, - {`select * from t use index (idx1, idx2);`, true, "SELECT * FROM `t` USE INDEX (`idx1`, `idx2`)"}, - {`select * from t ignore key (idx1)`, true, "SELECT * FROM `t` IGNORE INDEX (`idx1`)"}, - {`select * from t force index for join (idx1)`, true, "SELECT * FROM `t` FORCE INDEX FOR JOIN (`idx1`)"}, - {`select * from t use index for order by (idx1)`, true, "SELECT * FROM `t` USE INDEX FOR ORDER BY (`idx1`)"}, - {`select * from t force index for group by (idx1)`, true, "SELECT * FROM `t` FORCE INDEX FOR GROUP BY (`idx1`)"}, - {`select * from t use index for group by (idx1) use index for order by (idx2), t2`, true, "SELECT * FROM (`t` USE INDEX FOR GROUP BY (`idx1`) USE INDEX FOR ORDER BY (`idx2`)) JOIN `t2`"}, - } - - RunTest(t, table, false) -} - -func TestPriority(t *testing.T) { - table := []testCase{ - {`select high_priority * from t`, true, "SELECT HIGH_PRIORITY * FROM `t`"}, - {`select low_priority * from t`, true, "SELECT LOW_PRIORITY * FROM `t`"}, - {`select delayed * from t`, true, "SELECT DELAYED * FROM `t`"}, - {`insert high_priority into t values (1)`, true, "INSERT HIGH_PRIORITY INTO `t` VALUES (1)"}, - {`insert LOW_PRIORITY into t values (1)`, true, "INSERT LOW_PRIORITY INTO `t` VALUES (1)"}, - {`insert delayed into t values (1)`, true, "INSERT DELAYED INTO `t` VALUES (1)"}, - {`update low_priority t set a = 2`, true, "UPDATE LOW_PRIORITY `t` SET `a`=2"}, - {`update high_priority t set a = 2`, true, "UPDATE HIGH_PRIORITY `t` SET `a`=2"}, - {`update delayed t set a = 2`, true, "UPDATE DELAYED `t` SET `a`=2"}, - {`delete low_priority from t where a = 2`, true, "DELETE LOW_PRIORITY FROM `t` WHERE `a`=2"}, - {`delete high_priority from t where a = 2`, true, "DELETE HIGH_PRIORITY FROM `t` WHERE `a`=2"}, - {`delete delayed from t where a = 2`, true, "DELETE DELAYED FROM `t` WHERE `a`=2"}, - {`replace high_priority into t values (1)`, true, "REPLACE HIGH_PRIORITY INTO `t` VALUES (1)"}, - {`replace LOW_PRIORITY into t values (1)`, true, "REPLACE LOW_PRIORITY INTO `t` VALUES (1)"}, - {`replace delayed into t values (1)`, true, "REPLACE DELAYED INTO `t` VALUES (1)"}, - } - RunTest(t, table, false) - - p := parser.New() - stmt, _, err := p.Parse("select HIGH_PRIORITY * from t", "", "") - require.NoError(t, err) - sel := stmt[0].(*ast.SelectStmt) - require.Equal(t, mysql.HighPriority, sel.SelectStmtOpts.Priority) -} - -func TestSQLResult(t *testing.T) { - table := []testCase{ - {`select SQL_BIG_RESULT c1 from t group by c1`, true, "SELECT SQL_BIG_RESULT `c1` FROM `t` GROUP BY `c1`"}, - {`select SQL_SMALL_RESULT c1 from t group by c1`, true, "SELECT SQL_SMALL_RESULT `c1` FROM `t` GROUP BY `c1`"}, - {`select SQL_BUFFER_RESULT * from t`, true, "SELECT SQL_BUFFER_RESULT * FROM `t`"}, - {`select sql_small_result sql_big_result sql_buffer_result 1`, true, "SELECT SQL_SMALL_RESULT SQL_BIG_RESULT SQL_BUFFER_RESULT 1"}, - {`select STRAIGHT_JOIN SQL_SMALL_RESULT * from t`, true, "SELECT SQL_SMALL_RESULT STRAIGHT_JOIN * FROM `t`"}, - {`select SQL_CALC_FOUND_ROWS DISTINCT * from t`, true, "SELECT SQL_CALC_FOUND_ROWS DISTINCT * FROM `t`"}, - } - - RunTest(t, table, false) -} - -func TestSQLNoCache(t *testing.T) { - table := []testCase{ - {`select SQL_NO_CACHE * from t`, false, ""}, - {`select SQL_CACHE * from t`, true, "SELECT * FROM `t`"}, - {`select * from t`, true, "SELECT * FROM `t`"}, - } - - p := parser.New() - for _, tbl := range table { - stmt, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err) - - sel := stmt[0].(*ast.SelectStmt) - require.Equal(t, tbl.ok, sel.SelectStmtOpts.SQLCache) - } -} - -func TestEscape(t *testing.T) { - table := []testCase{ - {`select """;`, false, ""}, - {`select """";`, true, "SELECT _UTF8MB4'\"'"}, - {`select "汉字";`, true, "SELECT _UTF8MB4'汉字'"}, - {`select 'abc"def';`, true, "SELECT _UTF8MB4'abc\"def'"}, - {`select 'a\r\n';`, true, "SELECT _UTF8MB4'a\r\n'"}, - {`select "\a\r\n"`, true, "SELECT _UTF8MB4'a\r\n'"}, - {`select "\xFF"`, true, "SELECT _UTF8MB4'xFF'"}, - } - RunTest(t, table, false) -} - -func TestExplain(t *testing.T) { - table := []testCase{ - {"explain select c1 from t1", true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1`"}, - {"explain delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id;", true, "EXPLAIN FORMAT = 'row' DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, - {"explain insert into t values (1), (2), (3)", true, "EXPLAIN FORMAT = 'row' INSERT INTO `t` VALUES (1),(2),(3)"}, - {"explain replace into foo values (1 || 2)", true, "EXPLAIN FORMAT = 'row' REPLACE INTO `foo` VALUES (1 OR 2)"}, - {"explain update t set id = id + 1 order by id desc;", true, "EXPLAIN FORMAT = 'row' UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, - {"explain select c1 from t1 union (select c2 from t2) limit 1, 1", true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {`explain format = "row" select c1 from t1 union (select c2 from t2) limit 1, 1`, true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"explain format = 'brief' select * from t", true, "EXPLAIN FORMAT = 'brief' SELECT * FROM `t`"}, - {"DESC SCHE.TABL", true, "DESC `SCHE`.`TABL`"}, - {"DESC SCHE.TABL COLUM", true, "DESC `SCHE`.`TABL` `COLUM`"}, - {"DESCRIBE SCHE.TABL COLUM", true, "DESC `SCHE`.`TABL` `COLUM`"}, - {"EXPLAIN ANALYZE SELECT 1", true, "EXPLAIN ANALYZE SELECT 1"}, - {"EXPLAIN ANALYZE format=VERBOSE SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'VERBOSE' SELECT 1"}, - {"EXPLAIN ANALYZE format=TRUE_CARD_COST SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'TRUE_CARD_COST' SELECT 1"}, - {"EXPLAIN ANALYZE format='VERBOSE' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'VERBOSE' SELECT 1"}, - {"EXPLAIN ANALYZE format='TRUE_CARD_COST' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'TRUE_CARD_COST' SELECT 1"}, - {"EXPLAIN FORMAT = 'dot' SELECT 1", true, "EXPLAIN FORMAT = 'dot' SELECT 1"}, - {"EXPLAIN FORMAT = DOT SELECT 1", true, "EXPLAIN FORMAT = 'DOT' SELECT 1"}, - {"EXPLAIN FORMAT = 'row' SELECT 1", true, "EXPLAIN FORMAT = 'row' SELECT 1"}, - {"EXPLAIN FORMAT = 'ROW' SELECT 1", true, "EXPLAIN FORMAT = 'ROW' SELECT 1"}, - {"EXPLAIN FORMAT = 'BRIEF' SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, - {"EXPLAIN FORMAT = BRIEF SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, - {"EXPLAIN FORMAT = 'verbose' SELECT 1", true, "EXPLAIN FORMAT = 'verbose' SELECT 1"}, - {"EXPLAIN FORMAT = 'VERBOSE' SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, - {"EXPLAIN FORMAT = VERBOSE SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, - {"EXPLAIN SELECT 1", true, "EXPLAIN FORMAT = 'row' SELECT 1"}, - {"EXPLAIN FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 1"}, - {"EXPLAIN FOR connection 42", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 42"}, - {"EXPLAIN FORMAT = 'dot' FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'dot' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = DOT FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'DOT' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = 'row' FOR connection 1", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = ROW FOR connection 1", true, "EXPLAIN FORMAT = 'ROW' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = TRADITIONAL FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'TRADITIONAL' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = TRADITIONAL SELECT 1", true, "EXPLAIN FORMAT = 'TRADITIONAL' SELECT 1"}, - {"EXPLAIN FORMAT = BRIEF SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, - {"EXPLAIN FORMAT = 'brief' SELECT 1", true, "EXPLAIN FORMAT = 'brief' SELECT 1"}, - {"EXPLAIN FORMAT = DOT SELECT 1", true, "EXPLAIN FORMAT = 'DOT' SELECT 1"}, - {"EXPLAIN FORMAT = 'dot' SELECT 1", true, "EXPLAIN FORMAT = 'dot' SELECT 1"}, - {"EXPLAIN FORMAT = VERBOSE SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, - {"EXPLAIN FORMAT = 'verbose' SELECT 1", true, "EXPLAIN FORMAT = 'verbose' SELECT 1"}, - {"EXPLAIN FORMAT = JSON FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'JSON' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = JSON SELECT 1", true, "EXPLAIN FORMAT = 'JSON' SELECT 1"}, - {"EXPLAIN FORMAT = 'hint' SELECT 1", true, "EXPLAIN FORMAT = 'hint' SELECT 1"}, - {"EXPLAIN ANALYZE FORMAT = 'verbose' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'verbose' SELECT 1"}, - {"EXPLAIN ANALYZE FORMAT = 'binary' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'binary' SELECT 1"}, - {"EXPLAIN ALTER TABLE t1 ADD INDEX (a)", true, "EXPLAIN FORMAT = 'row' ALTER TABLE `t1` ADD INDEX(`a`)"}, - {"EXPLAIN ALTER TABLE t1 ADD a varchar(255)", true, "EXPLAIN FORMAT = 'row' ALTER TABLE `t1` ADD COLUMN `a` VARCHAR(255)"}, - {"EXPLAIN FORMAT = TIDB_JSON FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'TIDB_JSON' FOR CONNECTION 1"}, - {"EXPLAIN FORMAT = tidb_json SELECT 1", true, "EXPLAIN FORMAT = 'tidb_json' SELECT 1"}, - {"EXPLAIN ANALYZE FORMAT = tidb_json SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'tidb_json' SELECT 1"}, - } - RunTest(t, table, false) -} - -func TestPrepare(t *testing.T) { - table := []testCase{ - {"PREPARE pname FROM 'SELECT ?'", true, "PREPARE `pname` FROM 'SELECT ?'"}, - {"PREPARE pname FROM @test", true, "PREPARE `pname` FROM @`test`"}, - {"PREPARE `` FROM @test", true, "PREPARE `` FROM @`test`"}, - } - RunTest(t, table, false) -} - -func TestDeallocate(t *testing.T) { - table := []testCase{ - {"DEALLOCATE PREPARE test", true, "DEALLOCATE PREPARE `test`"}, - {"DEALLOCATE PREPARE ``", true, "DEALLOCATE PREPARE ``"}, - } - RunTest(t, table, false) -} - -func TestExecute(t *testing.T) { - table := []testCase{ - {"EXECUTE test", true, "EXECUTE `test`"}, - {"EXECUTE test USING @var1,@var2", true, "EXECUTE `test` USING @`var1`,@`var2`"}, - {"EXECUTE `` USING @var1,@var2", true, "EXECUTE `` USING @`var1`,@`var2`"}, - } - RunTest(t, table, false) -} - -func TestTrace(t *testing.T) { - table := []testCase{ - {"trace begin", true, "TRACE START TRANSACTION"}, - {"trace commit", true, "TRACE COMMIT"}, - {"trace rollback", true, "TRACE ROLLBACK"}, - {"trace set a = 1", true, "TRACE SET @@SESSION.`a`=1"}, - {"trace select c1 from t1", true, "TRACE SELECT `c1` FROM `t1`"}, - {"trace delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id;", true, "TRACE DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, - {"trace insert into t values (1), (2), (3)", true, "TRACE INSERT INTO `t` VALUES (1),(2),(3)"}, - {"trace replace into foo values (1 || 2)", true, "TRACE REPLACE INTO `foo` VALUES (1 OR 2)"}, - {"trace update t set id = id + 1 order by id desc;", true, "TRACE UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, - {"trace select c1 from t1 union (select c2 from t2) limit 1, 1", true, "TRACE SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"trace format = 'row' select c1 from t1 union (select c2 from t2) limit 1, 1", true, "TRACE SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, - {"trace format = 'json' update t set id = id + 1 order by id desc;", true, "TRACE FORMAT = 'json' UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, - {"trace plan select c1 from t1", true, "TRACE PLAN SELECT `c1` FROM `t1`"}, - {"trace plan target = 'estimation' select c1 from t1", true, "TRACE PLAN TARGET = 'estimation' SELECT `c1` FROM `t1`"}, - {"trace plan target = 'arandomstring' select c1 from t1", true, "TRACE PLAN TARGET = 'arandomstring' SELECT `c1` FROM `t1`"}, - } - RunTest(t, table, false) -} - -func TestBinding(t *testing.T) { - table := []testCase{ - {"create global binding for select * from t using select * from t use index(a)", true, "CREATE GLOBAL BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"create session binding for select * from t using select * from t use index(a)", true, "CREATE SESSION BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"drop global binding for select * from t", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t`"}, - {"drop session binding for select * from t", true, "DROP SESSION BINDING FOR SELECT * FROM `t`"}, - {"drop global binding for select * from t using select * from t use index(a)", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"drop session binding for select * from t using select * from t use index(a)", true, "DROP SESSION BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"show global bindings", true, "SHOW GLOBAL BINDINGS"}, - {"show session bindings", true, "SHOW SESSION BINDINGS"}, - {"set binding enabled for select * from t", true, "SET BINDING ENABLED FOR SELECT * FROM `t`"}, - {"set binding enabled for select * from t using select * from t use index(a)", true, "SET BINDING ENABLED FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"set binding disabled for select * from t", true, "SET BINDING DISABLED FOR SELECT * FROM `t`"}, - {"set binding disabled for select * from t using select * from t use index(a)", true, "SET BINDING DISABLED FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, - {"create global binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "CREATE GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, - {"create session binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "CREATE SESSION BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, - {"drop global binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, - {"drop session binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "DROP SESSION BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, - {"drop global binding for select * from t union all select * from t", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create session binding for select 1 union select 2 intersect select 3 using select 1 union select 2 intersect select 3", true, "CREATE SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3 USING SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, - {"drop session binding for select 1 union select 2 intersect select 3 using select 1 union select 2 intersect select 3", true, "DROP SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3 USING SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, - {"drop session binding for select 1 union select 2 intersect select 3", true, "DROP SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, - // Update cases. - {"CREATE GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "CREATE GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, - {"CREATE SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "CREATE SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, - {"drop global binding for update t set a = 1 where b = 1", true, "DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1"}, - {"drop session binding for update t set a = 1 where b = 1", true, "DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1"}, - {"DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, - {"DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, - // Multi-table Update. - {"CREATE GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "CREATE GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - {"CREATE SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "CREATE SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - {"DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - {"DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - {"DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - {"DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, - // Delete cases. - {"CREATE GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "CREATE GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, - {"CREATE SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "CREATE SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, - {"drop global binding for delete from t where a = 1", true, "DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1"}, - {"drop session binding for delete from t where a = 1", true, "DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1"}, - {"DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, - {"DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, - // Multi-table Delete. - {"CREATE GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "CREATE GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - {"CREATE SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "CREATE SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - {"drop global binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.a = 1", true, "DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - {"drop session binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.a = 1", true, "DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - {"DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - {"DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, - // Insert cases. - {"CREATE GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"CREATE SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"drop global binding for insert into t1 select * from t2 where t1.a=1", true, "DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, - {"drop session binding for insert into t1 select * from t2 where t1.a=1", true, "DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, - {"DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - // Replace cases. - {"CREATE GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"CREATE SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"drop global binding for replace into t1 select * from t2 where t1.a=1", true, "DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, - {"drop session binding for replace into t1 select * from t2 where t1.a=1", true, "DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, - {"DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, - {"DROP SESSION BINDING FOR SQL DIGEST 'a'", true, "DROP SESSION BINDING FOR SQL DIGEST 'a'"}, - {"drop global binding for sql digest 's'", true, "DROP GLOBAL BINDING FOR SQL DIGEST 's'"}, - {"create session binding from history using plan digest 'sss'", true, "CREATE SESSION BINDING FROM HISTORY USING PLAN DIGEST 'sss'"}, - {"CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST 'sss'", true, "CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST 'sss'"}, - {"set binding enabled for sql digest '1'", true, "SET BINDING ENABLED FOR SQL DIGEST '1'"}, - {"set binding disabled for sql digest '1'", true, "SET BINDING DISABLED FOR SQL DIGEST '1'"}, - } - RunTest(t, table, false) - - p := parser.New() - sms, _, err := p.Parse("create global binding for select * from t using select * from t use index(a)", "", "") - require.NoError(t, err) - v, ok := sms[0].(*ast.CreateBindingStmt) - require.True(t, ok) - require.Equal(t, "select * from t", v.OriginNode.Text()) - require.Equal(t, "select * from t use index(a)", v.HintedNode.Text()) - require.True(t, v.GlobalScope) -} - -func TestView(t *testing.T) { - table := []testCase{ - {"create view v as select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = undefined view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = temptable view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = current_user view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, - - // create view with `(` select statement `)` - {"create view v as (select * from t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = undefined view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = temptable view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security definer view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t) with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`) WITH LOCAL CHECK OPTION"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t) with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = current_user view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, - - // create view with union statement - {"create view v as select * from t union select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = undefined view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = temptable view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = current_user view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, - - // create view with union all statement - {"create view v as select * from t union all select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = undefined view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = temptable view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - {"create or replace algorithm = merge definer = current_user view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - - // create view with `(` union statement `)` - {"create view v as (select * from t union all select * from t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = undefined view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = temptable view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security definer view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t) with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`) WITH LOCAL CHECK OPTION"}, - {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t) with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, - {"create or replace algorithm = merge definer = current_user view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, - } - RunTest(t, table, false) - - // Test case for the text of the select statement in create view statement. - p := parser.New() - sms, _, err := p.Parse("create view v as select * from t", "", "") - require.NoError(t, err) - v, ok := sms[0].(*ast.CreateViewStmt) - require.True(t, ok) - require.Equal(t, model.AlgorithmUndefined, v.Algorithm) - require.Equal(t, "select * from t", v.Select.Text()) - require.Equal(t, model.SecurityDefiner, v.Security) - require.Equal(t, model.CheckOptionCascaded, v.CheckOption) - - src := `CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = root@localhost - SQL SECURITY DEFINER - VIEW V(a,b,c) AS select c,d,e from t - WITH CASCADED CHECK OPTION;` - - var st ast.StmtNode - st, err = p.ParseOneStmt(src, "", "") - require.NoError(t, err) - v, ok = st.(*ast.CreateViewStmt) - require.True(t, ok) - require.True(t, v.OrReplace) - require.Equal(t, model.AlgorithmUndefined, v.Algorithm) - require.Equal(t, "root", v.Definer.Username) - require.Equal(t, "localhost", v.Definer.Hostname) - require.Equal(t, model.NewCIStr("a"), v.Cols[0]) - require.Equal(t, model.NewCIStr("b"), v.Cols[1]) - require.Equal(t, model.NewCIStr("c"), v.Cols[2]) - require.Equal(t, "select c,d,e from t", v.Select.Text()) - require.Equal(t, model.SecurityDefiner, v.Security) - require.Equal(t, model.CheckOptionCascaded, v.CheckOption) -} - -func TestTimestampDiffUnit(t *testing.T) { - // Test case for timestampdiff unit. - // TimeUnit should be unified to upper case. - p := parser.New() - stmt, _, err := p.Parse("SELECT TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01'), TIMESTAMPDIFF(month,'2003-02-01','2003-05-01');", "", "") - require.NoError(t, err) - ss := stmt[0].(*ast.SelectStmt) - fields := ss.Fields.Fields - require.Len(t, fields, 2) - expr := fields[0].Expr - f, ok := expr.(*ast.FuncCallExpr) - require.True(t, ok) - require.Equal(t, ast.TimeUnitMonth, f.Args[0].(*ast.TimeUnitExpr).Unit) - - expr = fields[1].Expr - f, ok = expr.(*ast.FuncCallExpr) - require.True(t, ok) - require.Equal(t, ast.TimeUnitMonth, f.Args[0].(*ast.TimeUnitExpr).Unit) - - // Test Illegal TimeUnit for TimestampDiff - table := []testCase{ - {"SELECT TIMESTAMPDIFF(SECOND_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(MINUTE_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(MINUTE_SECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(HOUR_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(HOUR_SECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(HOUR_MINUTE,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(DAY_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(DAY_SECOND,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(DAY_MINUTE,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(DAY_HOUR,'2003-02-01','2003-05-01')", false, ""}, - {"SELECT TIMESTAMPDIFF(YEAR_MONTH,'2003-02-01','2003-05-01')", false, ""}, - } - RunTest(t, table, false) -} - -func TestFuncCallExprOffset(t *testing.T) { - // Test case for offset field on func call expr. - p := parser.New() - stmt, _, err := p.Parse("SELECT s.a(), b();", "", "") - require.NoError(t, err) - ss := stmt[0].(*ast.SelectStmt) - fields := ss.Fields.Fields - require.Len(t, fields, 2) - - { - // s.a() - expr := fields[0].Expr - f, ok := expr.(*ast.FuncCallExpr) - require.True(t, ok) - require.Equal(t, 7, f.OriginTextPosition()) - } - - { - // b() - expr := fields[1].Expr - f, ok := expr.(*ast.FuncCallExpr) - require.True(t, ok) - require.Equal(t, 14, f.OriginTextPosition()) - } -} - -func TestSessionManage(t *testing.T) { - table := []testCase{ - // Kill statement. - // See https://dev.mysql.com/doc/refman/5.7/en/kill.html - {"kill 23123", true, "KILL 23123"}, - {"kill CONNECTION_ID()", true, "KILL CONNECTION_ID()"}, - {"kill connection 23123", true, "KILL 23123"}, - {"kill query 23123", true, "KILL QUERY 23123"}, - {"kill tidb 23123", true, "KILL TIDB 23123"}, - {"kill tidb connection 23123", true, "KILL TIDB 23123"}, - {"kill tidb query 23123", true, "KILL TIDB QUERY 23123"}, - {"show processlist", true, "SHOW PROCESSLIST"}, - {"show full processlist", true, "SHOW FULL PROCESSLIST"}, - {"shutdown", true, "SHUTDOWN"}, - {"restart", true, "RESTART"}, - } - RunTest(t, table, false) -} - -func TestParseShowOpenTables(t *testing.T) { - table := []testCase{ - {"SHOW OPEN TABLES", true, "SHOW OPEN TABLES"}, - {"SHOW OPEN TABLES IN test", true, "SHOW OPEN TABLES IN `test`"}, - {"SHOW OPEN TABLES FROM test", true, "SHOW OPEN TABLES IN `test`"}, - } - RunTest(t, table, false) -} - -func TestSQLModeANSIQuotes(t *testing.T) { - p := parser.New() - p.SetSQLMode(mysql.ModeANSIQuotes) - tests := []string{ - `CREATE TABLE "table" ("id" int)`, - `select * from t "tt"`, - } - for _, test := range tests { - _, _, err := p.Parse(test, "", "") - require.NoError(t, err) - } -} - -func TestDDLStatements(t *testing.T) { - p := parser.New() - // Tests that whatever the charset it is define, we always assign utf8 charset and utf8_bin collate. - createTableStr := `CREATE TABLE t ( - a varchar(64) binary, - b char(10) charset utf8 collate utf8_general_ci, - c text charset latin1) ENGINE=innoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin` - stmts, _, err := p.Parse(createTableStr, "", "") - require.NoError(t, err) - stmt := stmts[0].(*ast.CreateTableStmt) - require.True(t, mysql.HasBinaryFlag(stmt.Cols[0].Tp.GetFlag())) - for _, colDef := range stmt.Cols[1:] { - require.False(t, mysql.HasBinaryFlag(colDef.Tp.GetFlag())) - } - for _, tblOpt := range stmt.Options { - switch tblOpt.Tp { - case ast.TableOptionCharset: - require.Equal(t, "utf8", tblOpt.StrValue) - case ast.TableOptionCollate: - require.Equal(t, "utf8_bin", tblOpt.StrValue) - } - } - createTableStr = `CREATE TABLE t ( - a varbinary(64), - b binary(10), - c blob)` - stmts, _, err = p.Parse(createTableStr, "", "") - require.NoError(t, err) - stmt = stmts[0].(*ast.CreateTableStmt) - for _, colDef := range stmt.Cols { - require.Equal(t, charset.CharsetBin, colDef.Tp.GetCharset()) - require.Equal(t, charset.CollationBin, colDef.Tp.GetCollate()) - require.True(t, mysql.HasBinaryFlag(colDef.Tp.GetFlag())) - } - // Test set collate for all column types - createTableStr = `CREATE TABLE t ( - c_int int collate utf8_bin, - c_real real collate utf8_bin, - c_float float collate utf8_bin, - c_bool bool collate utf8_bin, - c_char char collate utf8_bin, - c_binary binary collate utf8_bin, - c_varchar varchar(2) collate utf8_bin, - c_year year collate utf8_bin, - c_date date collate utf8_bin, - c_time time collate utf8_bin, - c_datetime datetime collate utf8_bin, - c_timestamp timestamp collate utf8_bin, - c_tinyblob tinyblob collate utf8_bin, - c_blob blob collate utf8_bin, - c_mediumblob mediumblob collate utf8_bin, - c_longblob longblob collate utf8_bin, - c_bit bit collate utf8_bin, - c_long_varchar long varchar collate utf8_bin, - c_tinytext tinytext collate utf8_bin, - c_text text collate utf8_bin, - c_mediumtext mediumtext collate utf8_bin, - c_longtext longtext collate utf8_bin, - c_decimal decimal collate utf8_bin, - c_numeric numeric collate utf8_bin, - c_enum enum('1') collate utf8_bin, - c_set set('1') collate utf8_bin, - c_json json collate utf8_bin)` - _, _, err = p.Parse(createTableStr, "", "") - require.NoError(t, err) - - createTableStr = `CREATE TABLE t (c_double double(10))` - _, _, err = p.Parse(createTableStr, "", "") - require.EqualError(t, err, "[parser:1149]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use") - p.SetStrictDoubleTypeCheck(false) - _, _, err = p.Parse(createTableStr, "", "") - require.NoError(t, err) - p.SetStrictDoubleTypeCheck(true) - - createTableStr = `CREATE TABLE t (c_double double(10, 2))` - _, _, err = p.Parse(createTableStr, "", "") - require.NoError(t, err) - - createTableStr = `create global temporary table t010(local_01 int, local_03 varchar(20))` - _, _, err = p.Parse(createTableStr, "", "") - require.EqualError(t, err, "line 1 column 70 near \"\"GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together ") - - createTableStr = `create global temporary table t010(local_01 int, local_03 varchar(20)) on commit preserve rows` - _, _, err = p.Parse(createTableStr, "", "") - require.NoError(t, err) -} - -func TestAnalyze(t *testing.T) { - table := []testCase{ - {"analyze table t1", true, "ANALYZE TABLE `t1`"}, - {"analyze table t1.*", false, ""}, - {"analyze table t,t1", true, "ANALYZE TABLE `t`,`t1`"}, - {"analyze table t1 index", true, "ANALYZE TABLE `t1` INDEX"}, - {"analyze table t1 index a", true, "ANALYZE TABLE `t1` INDEX `a`"}, - {"analyze table t1 index a,b", true, "ANALYZE TABLE `t1` INDEX `a`,`b`"}, - {"analyze table t with 4 buckets", true, "ANALYZE TABLE `t` WITH 4 BUCKETS"}, - {"analyze table t with 4 topn", true, "ANALYZE TABLE `t` WITH 4 TOPN"}, - {"analyze table t with 4 cmsketch width", true, "ANALYZE TABLE `t` WITH 4 CMSKETCH WIDTH"}, - {"analyze table t with 4 cmsketch depth", true, "ANALYZE TABLE `t` WITH 4 CMSKETCH DEPTH"}, - {"analyze table t with 4 samples", true, "ANALYZE TABLE `t` WITH 4 SAMPLES"}, - {"analyze table t with 4 buckets, 4 topn, 4 cmsketch width, 4 cmsketch depth, 4 samples", true, "ANALYZE TABLE `t` WITH 4 BUCKETS, 4 TOPN, 4 CMSKETCH WIDTH, 4 CMSKETCH DEPTH, 4 SAMPLES"}, - {"analyze table t index a with 4 buckets", true, "ANALYZE TABLE `t` INDEX `a` WITH 4 BUCKETS"}, - {"analyze table t partition a", true, "ANALYZE TABLE `t` PARTITION `a`"}, - {"analyze table t partition a with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` WITH 4 BUCKETS"}, - {"analyze table t partition a index b", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b`"}, - {"analyze table t partition a index b with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b` WITH 4 BUCKETS"}, - {"analyze incremental table t index", true, "ANALYZE INCREMENTAL TABLE `t` INDEX"}, - {"analyze incremental table t index idx", true, "ANALYZE INCREMENTAL TABLE `t` INDEX `idx`"}, - {"analyze table t update histogram on b with 1024 buckets", true, "ANALYZE TABLE `t` UPDATE HISTOGRAM ON `b` WITH 1024 BUCKETS"}, - {"analyze table t drop histogram on b", true, "ANALYZE TABLE `t` DROP HISTOGRAM ON `b`"}, - {"analyze table t update histogram on c1, c2;", true, "ANALYZE TABLE `t` UPDATE HISTOGRAM ON `c1`,`c2`"}, - {"analyze table t drop histogram on c1, c2;", true, "ANALYZE TABLE `t` DROP HISTOGRAM ON `c1`,`c2`"}, - {"analyze table t update histogram on t.c1, t.c2", false, ""}, - {"analyze table t drop histogram on t.c1, t.c2", false, ""}, - {"analyze table t1,t2 all columns", true, "ANALYZE TABLE `t1`,`t2` ALL COLUMNS"}, - {"analyze table t partition a all columns", true, "ANALYZE TABLE `t` PARTITION `a` ALL COLUMNS"}, - {"analyze table t1,t2 all columns with 4 topn", true, "ANALYZE TABLE `t1`,`t2` ALL COLUMNS WITH 4 TOPN"}, - {"analyze table t partition a all columns with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` ALL COLUMNS WITH 1024 BUCKETS"}, - {"analyze table t1,t2 predicate columns", true, "ANALYZE TABLE `t1`,`t2` PREDICATE COLUMNS"}, - {"analyze table t partition a predicate columns", true, "ANALYZE TABLE `t` PARTITION `a` PREDICATE COLUMNS"}, - {"analyze table t1,t2 predicate columns with 4 topn", true, "ANALYZE TABLE `t1`,`t2` PREDICATE COLUMNS WITH 4 TOPN"}, - {"analyze table t partition a predicate columns with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` PREDICATE COLUMNS WITH 1024 BUCKETS"}, - {"analyze table t columns c1,c2", true, "ANALYZE TABLE `t` COLUMNS `c1`,`c2`"}, - {"analyze table t partition a columns c1,c2", true, "ANALYZE TABLE `t` PARTITION `a` COLUMNS `c1`,`c2`"}, - {"analyze table t columns t.c1,t.c2", false, ""}, - {"analyze table t partition a columns t.c1,t.c2", false, ""}, - {"analyze table t columns c1,c2 with 4 topn", true, "ANALYZE TABLE `t` COLUMNS `c1`,`c2` WITH 4 TOPN"}, - {"analyze table t partition a columns c1,c2 with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` COLUMNS `c1`,`c2` WITH 1024 BUCKETS"}, - {"analyze table t index a columns c", false, ""}, - {"analyze table t index a all columns", false, ""}, - {"analyze table t index a predicate columns", false, ""}, - {"analyze table t with 10 samplerate", true, "ANALYZE TABLE `t` WITH 10 SAMPLERATE"}, - {"analyze table t with 0.1 samplerate", true, "ANALYZE TABLE `t` WITH 0.1 SAMPLERATE"}, - } - RunTest(t, table, false) -} - -func TestTableSample(t *testing.T) { - table := []testCase{ - // positive test cases - {"select * from tbl tablesample system (50);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (50)"}, - {"select * from tbl tablesample system (50 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (50 PERCENT)"}, - {"select * from tbl tablesample system (49.9 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (49.9 PERCENT)"}, - {"select * from tbl tablesample system (120 rows);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (120 ROWS)"}, - {"select * from tbl tablesample bernoulli (50);", true, "SELECT * FROM `tbl` TABLESAMPLE BERNOULLI (50)"}, - {"select * from tbl tablesample (50);", true, "SELECT * FROM `tbl` TABLESAMPLE (50)"}, - {"select * from tbl tablesample (50) repeatable (123456789);", true, "SELECT * FROM `tbl` TABLESAMPLE (50) REPEATABLE(123456789)"}, - {"select * from tbl as a tablesample (50);", true, "SELECT * FROM `tbl` AS `a` TABLESAMPLE (50)"}, - {"select * from tbl `tablesample` tablesample (50);", true, "SELECT * FROM `tbl` AS `tablesample` TABLESAMPLE (50)"}, - {"select * from tbl tablesample (50) where id > 20;", true, "SELECT * FROM `tbl` TABLESAMPLE (50) WHERE `id`>20"}, - {"select * from tbl partition (p0) tablesample (50);", true, "SELECT * FROM `tbl` PARTITION(`p0`) TABLESAMPLE (50)"}, - {"select * from tbl tablesample (0 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE (0 PERCENT)"}, - {"select * from tbl tablesample (100 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE (100 PERCENT)"}, - {"select * from tbl tablesample (0 rows);", true, "SELECT * FROM `tbl` TABLESAMPLE (0 ROWS)"}, - {"select * from tbl tablesample ('34');", true, "SELECT * FROM `tbl` TABLESAMPLE (_UTF8MB4'34')"}, - {"select * from tbl1 tablesample (10), tbl2 tablesample (20);", true, "SELECT * FROM (`tbl1` TABLESAMPLE (10)) JOIN `tbl2` TABLESAMPLE (20)"}, - {"select * from tbl1 a tablesample (10) join tbl2 b tablesample (20) on a.id <> b.id;", true, "SELECT * FROM `tbl1` AS `a` TABLESAMPLE (10) JOIN `tbl2` AS `b` TABLESAMPLE (20) ON `a`.`id`!=`b`.`id`"}, - {"select * from demo tablesample bernoulli(50) limit 1 into outfile '/tmp/sample.csv';", true, "SELECT * FROM `demo` TABLESAMPLE BERNOULLI (50) LIMIT 1 INTO OUTFILE '/tmp/sample.csv'"}, - {"select * from demo tablesample bernoulli(50) order by a, b into outfile '/tmp/sample.csv';", true, "SELECT * FROM `demo` TABLESAMPLE BERNOULLI (50) ORDER BY `a`,`b` INTO OUTFILE '/tmp/sample.csv'"}, - - // negative test cases - {"select * from tbl tablesample system(50) a;", false, ""}, - {"select * from tbl tablesample (50) partition (p0);", false, ""}, - {"select * from tbl where id > 20 tablesample system(50);", false, ""}, - {"select * from (select * from tbl) a tablesample system(50);", false, ""}, - {"select * from tbl tablesample system(50) tablesample system(50);", false, ""}, - {"select * from tbl tablesample system(50, 50);", false, ""}, - {"select * from tbl tablesample dhfksdlfljcoew(50);", false, ""}, - {"select * from tbl tablesample system;", false, ""}, - {"select * from tbl tablesample system (33) repeatable;", false, ""}, - {"select 1 from dual tablesample system (50);", false, ""}, - } - RunTest(t, table, false) - p := parser.New() - cases := []string{ - "select * from tbl tablesample (33.3 + 44.4);", - "select * from tbl tablesample (33.3 + 44.4 percent);", - "select * from tbl tablesample (33 + 44 rows);", - "select * from tbl tablesample (33 + 44 rows) repeatable (55 + 66);", - "select * from tbl tablesample (200);", - "select * from tbl tablesample (-10);", - "select * from tbl tablesample (null);", - "select * from tbl tablesample (33.3 rows);", - "select * from tbl tablesample (-4 rows);", - "select * from tbl tablesample (50) repeatable ('ssss');", - "delete from tbl using tbl2 tablesample(10 rows) repeatable (111) where tbl.id = tbl2.id", - "update tbl tablesample regions() set id = '1'", - } - for _, sql := range cases { - _, err := p.ParseOneStmt(sql, "", "") - require.NoErrorf(t, err, "source %v", sql) - } -} - -func TestGeneratedColumn(t *testing.T) { - tests := []struct { - input string - ok bool - expr string - }{ - {"create table t (c int, d int generated always as (c + 1) virtual)", true, "c + 1"}, - {"create table t (c int, d int as ( c + 1 ) virtual)", true, "c + 1"}, - {"create table t (c int, d int as (1 + 1) stored)", true, "1 + 1"}, - } - p := parser.New() - for _, tbl := range tests { - stmtNodes, _, err := p.Parse(tbl.input, "", "") - if tbl.ok { - require.NoError(t, err) - stmtNode := stmtNodes[0] - for _, col := range stmtNode.(*ast.CreateTableStmt).Cols { - for _, opt := range col.Options { - if opt.Tp == ast.ColumnOptionGenerated { - require.Equal(t, tbl.expr, opt.Expr.Text()) - } - } - } - } else { - require.Error(t, err) - } - } - - _, _, err := p.Parse("create table t1 (a int, b int as (a + 1) default 10);", "", "") - require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of DEFAULT and generated column") - _, _, err = p.Parse("create table t1 (a int, b int as (a + 1) on update now());", "", "") - require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of ON UPDATE and generated column") - _, _, err = p.Parse("create table t1 (a int, b int as (a + 1) auto_increment);", "", "") - require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of AUTO_INCREMENT and generated column") -} - -func TestSetTransaction(t *testing.T) { - // Set transaction is equivalent to setting the global or session value of tx_isolation. - // For example: - // SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED - // SET SESSION tx_isolation='READ-COMMITTED' - tests := []struct { - input string - isGlobal bool - value string - }{ - { - "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", - false, "READ-COMMITTED", - }, - { - "SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ", - true, "REPEATABLE-READ", - }, - } - p := parser.New() - for _, tbl := range tests { - stmt1, err := p.ParseOneStmt(tbl.input, "", "") - require.NoError(t, err) - setStmt := stmt1.(*ast.SetStmt) - vars := setStmt.Variables[0] - require.Equal(t, "tx_isolation", vars.Name) - require.Equal(t, tbl.isGlobal, vars.IsGlobal) - require.Equal(t, true, vars.IsSystem) - require.Equal(t, tbl.value, vars.Value.(ast.ValueExpr).GetValue()) - } -} - -func TestSideEffect(t *testing.T) { - // This test cover a bug that parse an error SQL doesn't leave the parser in a - // clean state, cause the following SQL parse fail. - p := parser.New() - _, err := p.ParseOneStmt("create table t /*!50100 'abc', 'abc' */;", "", "") - require.Error(t, err) - - _, err = p.ParseOneStmt("show tables;", "", "") - require.NoError(t, err) -} - -func TestTablePartition(t *testing.T) { - table := []testCase{ - {"ALTER TABLE t1 TRUNCATE PARTITION p0", true, "ALTER TABLE `t1` TRUNCATE PARTITION `p0`"}, - {"ALTER TABLE t1 TRUNCATE PARTITION p0, p1", true, "ALTER TABLE `t1` TRUNCATE PARTITION `p0`,`p1`"}, - {"ALTER TABLE t1 TRUNCATE PARTITION ALL", true, "ALTER TABLE `t1` TRUNCATE PARTITION ALL"}, - {"ALTER TABLE t1 TRUNCATE PARTITION ALL, p0", false, ""}, - {"ALTER TABLE t1 TRUNCATE PARTITION p0, ALL", false, ""}, - - {"ALTER TABLE t1 OPTIMIZE PARTITION p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION `p0`"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`"}, - // LOCAL is alias to NO_WRITE_TO_BINLOG - {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION `p0`,`p1`"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION ALL"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL"}, - {"ALTER TABLE t1 OPTIMIZE PARTITION ALL, p0", false, ""}, - {"ALTER TABLE t1 OPTIMIZE PARTITION p0, ALL", false, ""}, - // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), - // and the remains should re recognized as identifier, used as partition name here. - {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL", false, ""}, - {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL local", true, "ALTER TABLE `t_n` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `local`"}, - {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, - - {"ALTER TABLE t1 REPAIR PARTITION p0", true, "ALTER TABLE `t1` REPAIR PARTITION `p0`"}, - {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG p0", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`"}, - // LOCAL is alias to NO_WRITE_TO_BINLOG - {"ALTER TABLE t1 REPAIR PARTITION LOCAL p0", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`"}, - {"ALTER TABLE t1 REPAIR PARTITION p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION `p0`,`p1`"}, - {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, - {"ALTER TABLE t1 REPAIR PARTITION LOCAL p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, - {"ALTER TABLE t1 REPAIR PARTITION ALL", true, "ALTER TABLE `t1` REPAIR PARTITION ALL"}, - {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG ALL", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG ALL"}, - {"ALTER TABLE t1 REPAIR PARTITION LOCAL ALL", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG ALL"}, - {"ALTER TABLE t1 REPAIR PARTITION ALL, p0", false, ""}, - {"ALTER TABLE t1 REPAIR PARTITION p0, ALL", false, ""}, - // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), - // and the remains should re recognized as identifier, used as partition name here. - {"ALTER TABLE t_n REPAIR PARTITION LOCAL", false, ""}, - {"ALTER TABLE t_n REPAIR PARTITION LOCAL local", true, "ALTER TABLE `t_n` REPAIR PARTITION NO_WRITE_TO_BINLOG `local`"}, - {"ALTER TABLE t_n REPAIR PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` REPAIR PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, - - {"ALTER TABLE t1 IMPORT PARTITION p0 TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION `p0` TABLESPACE"}, - {"ALTER TABLE t1 IMPORT PARTITION p0, p1 TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION `p0`,`p1` TABLESPACE"}, - {"ALTER TABLE t1 IMPORT PARTITION ALL TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION ALL TABLESPACE"}, - {"ALTER TABLE t1 IMPORT PARTITION ALL, p0 TABLESPACE", false, ""}, - {"ALTER TABLE t1 IMPORT PARTITION p0, ALL TABLESPACE", false, ""}, - - {"ALTER TABLE t1 DISCARD PARTITION p0 TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION `p0` TABLESPACE"}, - {"ALTER TABLE t1 DISCARD PARTITION p0, p1 TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION `p0`,`p1` TABLESPACE"}, - {"ALTER TABLE t1 DISCARD PARTITION ALL TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION ALL TABLESPACE"}, - {"ALTER TABLE t1 DISCARD PARTITION ALL, p0 TABLESPACE", false, ""}, - {"ALTER TABLE t1 DISCARD PARTITION p0, ALL TABLESPACE", false, ""}, - - {"ALTER TABLE t1 ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT 'APSTART \\' APEND')", true, "ALTER TABLE `t1` ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'APSTART '' APEND')"}, - {"ALTER TABLE t1 ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')", true, "ALTER TABLE `t1` ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')"}, - {`CREATE TABLE t1 (a int not null,b int not null,c int not null,primary key(a,b)) - partition by range (a) - partitions 3 - (partition x1 values less than (5), - partition x2 values less than (10), - partition x3 values less than maxvalue);`, true, "CREATE TABLE `t1` (`a` INT NOT NULL,`b` INT NOT NULL,`c` INT NOT NULL,PRIMARY KEY(`a`, `b`)) PARTITION BY RANGE (`a`) (PARTITION `x1` VALUES LESS THAN (5),PARTITION `x2` VALUES LESS THAN (10),PARTITION `x3` VALUES LESS THAN (MAXVALUE))"}, - {"CREATE TABLE t1 (a int not null) partition by range (a) (partition x1 values less than (5) tablespace ts1)", true, "CREATE TABLE `t1` (`a` INT NOT NULL) PARTITION BY RANGE (`a`) (PARTITION `x1` VALUES LESS THAN (5) TABLESPACE = `ts1`)"}, - {`create table t (a int) partition by range (a) - (PARTITION p0 VALUES LESS THAN (63340531200) ENGINE = MyISAM, - PARTITION p1 VALUES LESS THAN (63342604800) ENGINE MyISAM)`, true, "CREATE TABLE `t` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) ENGINE = MyISAM,PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, - {`create table t (a int) partition by range (a) - (PARTITION p0 VALUES LESS THAN (63340531200) ENGINE = MyISAM COMMENT 'xxx', - PARTITION p1 VALUES LESS THAN (63342604800) ENGINE = MyISAM)`, true, "CREATE TABLE `t` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) ENGINE = MyISAM COMMENT = 'xxx',PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, - {`create table t1 (a int) partition by range (a) - (PARTITION p0 VALUES LESS THAN (63340531200) COMMENT 'xxx' ENGINE = MyISAM , - PARTITION p1 VALUES LESS THAN (63342604800) ENGINE = MyISAM)`, true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) COMMENT = 'xxx' ENGINE = MyISAM,PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, - {`create table t (id int) - partition by range (id) - subpartition by key (id) subpartitions 2 - (partition p0 values less than (42))`, true, "CREATE TABLE `t` (`id` INT) PARTITION BY RANGE (`id`) SUBPARTITION BY KEY (`id`) SUBPARTITIONS 2 (PARTITION `p0` VALUES LESS THAN (42))"}, - {`create table t (id int) - partition by range (id) - subpartition by hash (id) - (partition p0 values less than (42))`, true, "CREATE TABLE `t` (`id` INT) PARTITION BY RANGE (`id`) SUBPARTITION BY HASH (`id`) (PARTITION `p0` VALUES LESS THAN (42))"}, - {`create table t1 (a varchar(5), b int signed, c varchar(10), d datetime) - partition by range columns(b,c) - subpartition by hash(to_seconds(d)) - ( partition p0 values less than (2, 'b'), - partition p1 values less than (4, 'd'), - partition p2 values less than (10, 'za'));`, true, - "CREATE TABLE `t1` (`a` VARCHAR(5),`b` INT,`c` VARCHAR(10),`d` DATETIME) PARTITION BY RANGE COLUMNS (`b`,`c`) SUBPARTITION BY HASH (TO_SECONDS(`d`)) (PARTITION `p0` VALUES LESS THAN (2, _UTF8MB4'b'),PARTITION `p1` VALUES LESS THAN (4, _UTF8MB4'd'),PARTITION `p2` VALUES LESS THAN (10, _UTF8MB4'za'))"}, - {`CREATE TABLE t1 (a INT, b TIMESTAMP DEFAULT '0000-00-00 00:00:00') -ENGINE=INNODB PARTITION BY LINEAR HASH (a) PARTITIONS 1;`, true, "CREATE TABLE `t1` (`a` INT,`b` TIMESTAMP DEFAULT _UTF8MB4'0000-00-00 00:00:00') ENGINE = INNODB PARTITION BY LINEAR HASH (`a`) PARTITIONS 1"}, - - // empty clause is valid only for HASH/KEY partitions - {"create table t1 (a int) partition by hash (a) (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x`,PARTITION `y`)"}, - {"create table t1 (a int) partition by key (a) (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY KEY (`a`) (PARTITION `x`,PARTITION `y`)"}, - {"create table t1 (a int) partition by range (a) (partition x, partition y)", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x, partition y)", false, ""}, - {"create table t1 (a int) partition by system_time (partition x, partition y)", false, ""}, - // VALUES LESS THAN clause is valid only for RANGE partitions - {"create table t1 (a int) partition by hash (a) (partition x values less than (10))", false, ""}, - {"create table t1 (a int) partition by key (a) (partition x values less than (10))", false, ""}, - {"create table t1 (a int) partition by range (a) (partition x values less than (maxvalue))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (MAXVALUE))"}, - {"create table t1 (a int) partition by range (a) (partition x values less than (default))", false, ""}, - {"create table t (a varchar(100), b int) partition by list columns (a) (partition p1 values in ('a','b','DEFAULT'), partition pDef values in (default))", true, "CREATE TABLE `t` (`a` VARCHAR(100),`b` INT) PARTITION BY LIST COLUMNS (`a`) (PARTITION `p1` VALUES IN (_UTF8MB4'a', _UTF8MB4'b', _UTF8MB4'DEFAULT'),PARTITION `pDef` DEFAULT)"}, - {"create table t1 (a int) partition by range (a) (partition x values less than (10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (10))"}, - {"create table t1 (a int) partition by list (a) (partition x values less than (10))", false, ""}, - {"create table t1 (a int) partition by system_time (partition x values less than (10))", false, ""}, - // VALUES IN clause is valid only for LIST partitions - {"create table t1 (a int) partition by hash (a) (partition x values in (10))", false, ""}, - {"create table t1 (a int) partition by key (a) (partition x values in (10))", false, ""}, - {"create table t1 (a int) partition by range (a) (partition x values in (10))", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x values in (10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` VALUES IN (10))"}, - {"create table t1 (a int) partition by list (a) (partition x values in (default))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` DEFAULT)"}, - {"create table t1 (a int) partition by list (a) (partition x values in (maxvalue))", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x values in (default, 10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` VALUES IN (DEFAULT, 10))"}, - {"create table t1 (a int) partition by system_time (partition x values in (10))", false, ""}, - // HISTORY/CURRENT clauses are valid only for SYSTEM_TIME partitions - {"create table t1 (a int) partition by hash (a) (partition x history, partition y current)", false, ""}, - {"create table t1 (a int) partition by key (a) (partition x history, partition y current)", false, ""}, - {"create table t1 (a int) partition by range (a) (partition x history, partition y current)", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x history, partition y current)", false, ""}, - {"create table t1 (a int) partition by system_time (partition x history, partition y current)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME (PARTITION `x` HISTORY,PARTITION `y` CURRENT)"}, - - // LIST, RANGE and SYSTEM_TIME partitions all required definitions - {"create table t1 (a int) partition by hash (a)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) PARTITIONS 1"}, - {"create table t1 (a int) partition by key (a)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY KEY (`a`) PARTITIONS 1"}, - {"create table t1 (a int) partition by range (a)", false, ""}, - {"create table t1 (a int) partition by list (a)", false, ""}, - {"create table t1 (a int) partition by system_time", false, ""}, - // SYSTEM_TIME required 2 or more partitions - {"create table t1 (a int) partition by system_time (partition x history)", false, ""}, - {"create table t1 (a int) partition by system_time (partition x current)", false, ""}, - - // number of columns and number of values in VALUES clauses must match - {"create table t1 (a int, b int) partition by range (a) (partition x values less than (10, 20))", false, ""}, - {"create table t (id int) partition by range columns (id) (partition p0 values less than (1, 2))", false, ""}, - {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than (10, 20))", true, "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE COLUMNS (`a`,`b`) (PARTITION `x` VALUES LESS THAN (10, 20))"}, - {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than (10))", false, ""}, - {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than maxvalue)", false, ""}, - {"create table t1 (a int, b int) partition by list (a) (partition x values in ((10, 20)))", false, ""}, - {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20)))", true, "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY LIST COLUMNS (`a`,`b`) (PARTITION `x` VALUES IN ((10, 20)))"}, - {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in (10, 20))", false, ""}, - {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in (10, (20, 30)))", false, ""}, - {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20), 30))", false, ""}, - {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20), (30, 40, 50)))", false, ""}, - - // there must be at least one column/partition/value inside (...) - {"create table t1 (a int) partition by hash (a) ()", false, ""}, - {"create table t1 (a int primary key) partition by key ()", true, "CREATE TABLE `t1` (`a` INT PRIMARY KEY) PARTITION BY KEY () PARTITIONS 1"}, - {"create table t1 (a int) partition by range columns () (partition x values less than maxvalue)", false, ""}, - {"create table t1 (a int) partition by list columns () (partition x default)", false, ""}, - {"create table t1 (a int) partition by range (a) (partition x values less than ())", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x values in ())", false, ""}, - {"create table t1 (a int) partition by list (a) (partition x default)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` DEFAULT)"}, - - // only hash and key subpartitions are allowed - {"create table t1 (a int, b int) partition by range (a) subpartition by range (b) (partition x values less than maxvalue)", false, ""}, - - // number of partitions/subpartitions must be matching - {"create table t1 (a int) partition by hash (a) partitions 2 (partition x)", false, ""}, - {"create table t1 (a int) partition by hash (a) partitions 2 (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x`,PARTITION `y`)"}, - {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 2 (partition x values less than maxvalue (subpartition y))", false, ""}, - { - "create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 2 (partition x values less than maxvalue (subpartition y, subpartition z))", true, - "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 2 (PARTITION `x` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `y`,SUBPARTITION `z`))", - }, - { - "create table t1 (a int, b int) partition by range (a) subpartition by hash (b) (partition x values less than (10) (subpartition y,subpartition z),partition a values less than (20) (subpartition b,subpartition c))", true, - "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 2 (PARTITION `x` VALUES LESS THAN (10) (SUBPARTITION `y`,SUBPARTITION `z`),PARTITION `a` VALUES LESS THAN (20) (SUBPARTITION `b`,SUBPARTITION `c`))", - }, - {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) (partition x values less than (10) (subpartition y),partition a values less than (20) (subpartition b,subpartition c))", false, ""}, - {"create table t1 (a int, b int) partition by range (a) (partition x values less than (10) (subpartition y))", false, ""}, - {"create table t1 (a int) partition by hash (a) partitions 0", false, ""}, - {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 0 (partition x values less than (10))", false, ""}, - - // other partition tests - {"create table t1 (a int) partition by system_time interval 7 day limit 50000 (partition x history, partition y current)", false, ""}, - { - "create table t1 (a int) partition by system_time interval 7 day (partition x history, partition y current)", true, - "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME INTERVAL 7 DAY (PARTITION `x` HISTORY,PARTITION `y` CURRENT)", - }, - { - "create table t1 (a int) partition by system_time limit 50000 (partition x history, partition y current)", true, - "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME LIMIT 50000 (PARTITION `x` HISTORY,PARTITION `y` CURRENT)", - }, - { - "create table t1 (a int) partition by hash(a) (partition x engine InnoDB comment 'xxxx' data directory '/var/data' index directory '/var/index' max_rows 70000 min_rows 50 tablespace `innodb_file_per_table` nodegroup 255)", true, - "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x` ENGINE = InnoDB COMMENT = 'xxxx' DATA DIRECTORY = '/var/data' INDEX DIRECTORY = '/var/index' MAX_ROWS = 70000 MIN_ROWS = 50 TABLESPACE = `innodb_file_per_table` NODEGROUP = 255)", - }, - { - "create table t1 (a int, b int) partition by range(a) subpartition by hash(b) (partition x values less than maxvalue (subpartition y engine InnoDB comment 'xxxx' data directory '/var/data' index directory '/var/index' max_rows 70000 min_rows 50 tablespace `innodb_file_per_table` nodegroup 255))", true, - "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 1 (PARTITION `x` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `y` ENGINE = InnoDB COMMENT = 'xxxx' DATA DIRECTORY = '/var/data' INDEX DIRECTORY = '/var/index' MAX_ROWS = 70000 MIN_ROWS = 50 TABLESPACE = `innodb_file_per_table` NODEGROUP = 255))", - }, - } - RunTest(t, table, false) - - // Check comment content. - p := parser.New() - stmt, err := p.ParseOneStmt("create table t (id int) partition by range (id) (partition p0 values less than (10) comment 'check')", "", "") - require.NoError(t, err) - createTable := stmt.(*ast.CreateTableStmt) - comment, ok := createTable.Partition.Definitions[0].Comment() - require.True(t, ok) - require.Equal(t, "check", comment) -} - -func TestTablePartitionNameList(t *testing.T) { - table := []testCase{ - {`select * from t partition (p0,p1)`, true, ""}, - } - - p := parser.New() - for _, tbl := range table { - stmt, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err) - - sel := stmt[0].(*ast.SelectStmt) - source, ok := sel.From.TableRefs.Left.(*ast.TableSource) - require.True(t, ok) - tableName, ok := source.Source.(*ast.TableName) - require.True(t, ok) - require.Len(t, tableName.PartitionNames, 2) - require.Equal(t, model.CIStr{O: "p0", L: "p0"}, tableName.PartitionNames[0]) - require.Equal(t, model.CIStr{O: "p1", L: "p1"}, tableName.PartitionNames[1]) - } -} - -func TestNotExistsSubquery(t *testing.T) { - table := []testCase{ - {`select * from t1 where not exists (select * from t2 where t1.a = t2.a)`, true, ""}, - } - - p := parser.New() - for _, tbl := range table { - stmt, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err) - - sel := stmt[0].(*ast.SelectStmt) - exists, ok := sel.Where.(*ast.ExistsSubqueryExpr) - require.True(t, ok) - require.Equal(t, tbl.ok, exists.Not) - } -} - -func TestWindowFunctionIdentifier(t *testing.T) { - //nolint: prealloc - var table []testCase - for key := range parser.WindowFuncTokenMapForTest { - table = append(table, testCase{fmt.Sprintf("select 1 %s", key), false, fmt.Sprintf("SELECT 1 AS `%s`", key)}) - } - RunTest(t, table, true) - - for i := range table { - table[i].ok = true - } - RunTest(t, table, false) -} - -func TestWindowFunctions(t *testing.T) { - table := []testCase{ - // For window function descriptions. - // See https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html - {`SELECT CUME_DIST() OVER w FROM t;`, true, "SELECT CUME_DIST() OVER `w` FROM `t`"}, - {`SELECT DENSE_RANK() OVER (w) FROM t;`, true, "SELECT DENSE_RANK() OVER (`w`) FROM `t`"}, - {`SELECT FIRST_VALUE(val) OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) OVER `w` FROM `t`"}, - {`SELECT FIRST_VALUE(val) RESPECT NULLS OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) OVER `w` FROM `t`"}, - {`SELECT FIRST_VALUE(val) IGNORE NULLS OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) IGNORE NULLS OVER `w` FROM `t`"}, - {`SELECT LAG(val) OVER (w) FROM t;`, true, "SELECT LAG(`val`) OVER (`w`) FROM `t`"}, - {`SELECT LAG(val, 1) OVER (w) FROM t;`, true, "SELECT LAG(`val`, 1) OVER (`w`) FROM `t`"}, - {`SELECT LAG(val, 1, def) OVER (w) FROM t;`, true, "SELECT LAG(`val`, 1, `def`) OVER (`w`) FROM `t`"}, - {`SELECT LAST_VALUE(val) OVER (w) FROM t;`, true, "SELECT LAST_VALUE(`val`) OVER (`w`) FROM `t`"}, - {`SELECT LEAD(val) OVER w FROM t;`, true, "SELECT LEAD(`val`) OVER `w` FROM `t`"}, - {`SELECT LEAD(val, 1) OVER w FROM t;`, true, "SELECT LEAD(`val`, 1) OVER `w` FROM `t`"}, - {`SELECT LEAD(val, 1, def) OVER w FROM t;`, true, "SELECT LEAD(`val`, 1, `def`) OVER `w` FROM `t`"}, - {`SELECT NTH_VALUE(val, 233) OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) OVER `w` FROM `t`"}, - {`SELECT NTH_VALUE(val, 233) FROM FIRST OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) OVER `w` FROM `t`"}, - {`SELECT NTH_VALUE(val, 233) FROM LAST OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) FROM LAST OVER `w` FROM `t`"}, - {`SELECT NTH_VALUE(val, 233) FROM LAST IGNORE NULLS OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) FROM LAST IGNORE NULLS OVER `w` FROM `t`"}, - {`SELECT NTH_VALUE(val) OVER w FROM t;`, false, ""}, - {`SELECT NTILE(233) OVER (w) FROM t;`, true, "SELECT NTILE(233) OVER (`w`) FROM `t`"}, - {`SELECT PERCENT_RANK() OVER (w) FROM t;`, true, "SELECT PERCENT_RANK() OVER (`w`) FROM `t`"}, - {`SELECT RANK() OVER (w) FROM t;`, true, "SELECT RANK() OVER (`w`) FROM `t`"}, - {`SELECT ROW_NUMBER() OVER (w) FROM t;`, true, "SELECT ROW_NUMBER() OVER (`w`) FROM `t`"}, - {`SELECT n, LAG(n, 1, 0) OVER (w), LEAD(n, 1, 0) OVER w, n + LAG(n, 1, 0) OVER (w) FROM fib;`, true, "SELECT `n`,LAG(`n`, 1, 0) OVER (`w`),LEAD(`n`, 1, 0) OVER `w`,`n`+LAG(`n`, 1, 0) OVER (`w`) FROM `fib`"}, - - // For window function concepts and syntax. - // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html - {`SELECT SUM(profit) OVER(PARTITION BY country) AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER (PARTITION BY `country`) AS `country_profit` FROM `sales`"}, - {`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT AVG(profit) OVER() AS country_profit FROM sales;`, true, "SELECT AVG(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT BIT_XOR(profit) OVER() AS country_profit FROM sales;`, true, "SELECT BIT_XOR(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT COUNT(profit) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT COUNT(ALL profit) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT COUNT(*) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(1) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT MAX(profit) OVER() AS country_profit FROM sales;`, true, "SELECT MAX(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT MIN(profit) OVER() AS country_profit FROM sales;`, true, "SELECT MIN(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER () AS `country_profit` FROM `sales`"}, - {`SELECT ROW_NUMBER() OVER(PARTITION BY country) AS row_num1 FROM sales;`, true, "SELECT ROW_NUMBER() OVER (PARTITION BY `country`) AS `row_num1` FROM `sales`"}, - {`SELECT ROW_NUMBER() OVER(PARTITION BY country, d ORDER BY year, product) AS row_num2 FROM sales;`, true, "SELECT ROW_NUMBER() OVER (PARTITION BY `country`, `d` ORDER BY `year`,`product`) AS `row_num2` FROM `sales`"}, - - // For window function frame specification. - // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html - {`SELECT SUM(val) OVER (PARTITION BY subject ORDER BY time ROWS UNBOUNDED PRECEDING) FROM t;`, true, "SELECT SUM(`val`) OVER (PARTITION BY `subject` ORDER BY `time` ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM `t`"}, - {`SELECT AVG(val) OVER (PARTITION BY subject ORDER BY time ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (PARTITION BY `subject` ORDER BY `time` ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM `t`"}, - {`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM `t`"}, - {`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) FROM `t`"}, - {`SELECT AVG(val) OVER (RANGE BETWEEN INTERVAL 5 DAY PRECEDING AND INTERVAL '2:30' MINUTE_SECOND FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN INTERVAL 5 DAY PRECEDING AND INTERVAL _UTF8MB4'2:30' MINUTE_SECOND FOLLOWING) FROM `t`"}, - {`SELECT AVG(val) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM `t`"}, - {`SELECT AVG(val) OVER (RANGE CURRENT ROW) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM `t`"}, - - // For named windows. - // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-named-windows.html - {`SELECT RANK() OVER (w) FROM t WINDOW w AS (ORDER BY val);`, true, "SELECT RANK() OVER (`w`) FROM `t` WINDOW `w` AS (ORDER BY `val`)"}, - {`SELECT RANK() OVER w FROM t WINDOW w AS ();`, true, "SELECT RANK() OVER `w` FROM `t` WINDOW `w` AS ()"}, - {`SELECT FIRST_VALUE(year) OVER (w ORDER BY year) AS first FROM sales WINDOW w AS (PARTITION BY country);`, true, "SELECT FIRST_VALUE(`year`) OVER (`w` ORDER BY `year`) AS `first` FROM `sales` WINDOW `w` AS (PARTITION BY `country`)"}, - {`SELECT RANK() OVER (w1) FROM t WINDOW w1 AS (w2), w2 AS (), w3 AS (w1);`, true, "SELECT RANK() OVER (`w1`) FROM `t` WINDOW `w1` AS (`w2`),`w2` AS (),`w3` AS (`w1`)"}, - {`SELECT RANK() OVER w1 FROM t WINDOW w1 AS (w2), w2 AS (w3), w3 AS (w1);`, true, "SELECT RANK() OVER `w1` FROM `t` WINDOW `w1` AS (`w2`),`w2` AS (`w3`),`w3` AS (`w1`)"}, - - // For TSO functions - {`select tidb_parse_tso(1)`, true, "SELECT TIDB_PARSE_TSO(1)"}, - {`select tidb_parse_tso_logical(1)`, true, "SELECT TIDB_PARSE_TSO_LOGICAL(1)"}, - {`select tidb_bounded_staleness('2015-09-21 00:07:01', NOW())`, true, "SELECT TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, - {`select tidb_bounded_staleness(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())`, true, "SELECT TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, - {`select tidb_bounded_staleness('2015-09-21 00:07:01', '2021-04-27 11:26:13')`, true, "SELECT TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, - - {`select from_unixtime(404411537129996288)`, true, "SELECT FROM_UNIXTIME(404411537129996288)"}, - {`select from_unixtime(404411537129996288.22)`, true, "SELECT FROM_UNIXTIME(404411537129996288.22)"}, - } - RunTest(t, table, true) -} - -type windowFrameBoundChecker struct { - fb *ast.FrameBound - exprRc int - unit ast.TimeUnitType - t *testing.T -} - -// Enter implements ast.Visitor interface. -func (wfc *windowFrameBoundChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { - if _, ok := inNode.(*ast.FrameBound); ok { - wfc.fb = inNode.(*ast.FrameBound) - if wfc.fb.Unit != ast.TimeUnitInvalid { - _, ok := wfc.fb.Expr.(ast.ValueExpr) - require.False(wfc.t, ok) - } - } - return inNode, false -} - -// Leave implements ast.Visitor interface. -func (wfc *windowFrameBoundChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { - if _, ok := inNode.(*ast.FrameBound); ok { - wfc.fb = nil - } - if wfc.fb != nil { - if inNode == wfc.fb.Expr { - wfc.exprRc++ - } - wfc.unit = wfc.fb.Unit - } - return inNode, true -} - -// For issue #51 -// See https://github.com/pingcap/parser/pull/51 for details -func TestVisitFrameBound(t *testing.T) { - p := parser.New() - p.EnableWindowFunc(true) - table := []struct { - s string - exprRc int - unit ast.TimeUnitType - }{ - {`SELECT AVG(val) OVER (RANGE INTERVAL 1+3 MINUTE_SECOND PRECEDING) FROM t;`, 1, ast.TimeUnitMinuteSecond}, - {`SELECT AVG(val) OVER (RANGE 5 PRECEDING) FROM t;`, 1, ast.TimeUnitInvalid}, - {`SELECT AVG(val) OVER () FROM t;`, 0, ast.TimeUnitInvalid}, - } - for _, tbl := range table { - stmt, err := p.ParseOneStmt(tbl.s, "", "") - require.NoError(t, err) - checker := windowFrameBoundChecker{t: t} - stmt.Accept(&checker) - require.Equal(t, tbl.exprRc, checker.exprRc) - require.Equal(t, tbl.unit, checker.unit) - } -} - -func TestFieldText(t *testing.T) { - p := parser.New() - stmts, _, err := p.Parse("select a from t", "", "") - require.NoError(t, err) - tmp := stmts[0].(*ast.SelectStmt) - require.Equal(t, "a", tmp.Fields.Fields[0].Text()) - - sqls := []string{ - "trace select a from t", - "trace format = 'row' select a from t", - "trace format = 'json' select a from t", - } - for _, sql := range sqls { - stmts, _, err = p.Parse(sql, "", "") - require.NoError(t, err) - traceStmt := stmts[0].(*ast.TraceStmt) - require.Equal(t, sql, traceStmt.Text()) - require.Equal(t, "select a from t", traceStmt.Stmt.Text()) - } -} - -// See https://github.com/pingcap/parser/issue/94 -func TestQuotedSystemVariables(t *testing.T) { - p := parser.New() - - st, err := p.ParseOneStmt( - "select @@Sql_Mode, @@`SQL_MODE`, @@session.`sql_mode`, @@global.`s ql``mode`, @@session.'sql\\nmode', @@local.\"sql\\\"mode\";", - "", - "", - ) - require.NoError(t, err) - ss := st.(*ast.SelectStmt) - expected := []*ast.VariableExpr{ - { - Name: "sql_mode", - IsGlobal: false, - IsSystem: true, - ExplicitScope: false, - }, - { - Name: "sql_mode", - IsGlobal: false, - IsSystem: true, - ExplicitScope: false, - }, - { - Name: "sql_mode", - IsGlobal: false, - IsSystem: true, - ExplicitScope: true, - }, - { - Name: "s ql`mode", - IsGlobal: true, - IsSystem: true, - ExplicitScope: true, - }, - { - Name: "sql\nmode", - IsGlobal: false, - IsSystem: true, - ExplicitScope: true, - }, - { - Name: `sql"mode`, - IsGlobal: false, - IsSystem: true, - ExplicitScope: true, - }, - } - - require.Len(t, ss.Fields.Fields, len(expected)) - for i, field := range ss.Fields.Fields { - ve := field.Expr.(*ast.VariableExpr) - comment := fmt.Sprintf("field %d, ve = %v", i, ve) - require.Equal(t, expected[i].Name, ve.Name, comment) - require.Equal(t, expected[i].IsGlobal, ve.IsGlobal, comment) - require.Equal(t, expected[i].IsSystem, ve.IsSystem, comment) - require.Equal(t, expected[i].ExplicitScope, ve.ExplicitScope, comment) - } -} - -// See https://github.com/pingcap/parser/issue/95 -func TestQuotedVariableColumnName(t *testing.T) { - p := parser.New() - - st, err := p.ParseOneStmt( - "select @abc, @`abc`, @'aBc', @\"AbC\", @6, @`6`, @'6', @\"6\", @@sql_mode, @@`sql_mode`, @;", - "", - "", - ) - require.NoError(t, err) - ss := st.(*ast.SelectStmt) - expected := []string{ - "@abc", - "@`abc`", - "@'aBc'", - `@"AbC"`, - "@6", - "@`6`", - "@'6'", - `@"6"`, - "@@sql_mode", - "@@`sql_mode`", - "@", - } - - require.Len(t, ss.Fields.Fields, len(expected)) - for i, field := range ss.Fields.Fields { - require.Equal(t, expected[i], field.Text()) - } -} - -func TestCharset(t *testing.T) { - p := parser.New() - - st, err := p.ParseOneStmt("ALTER SCHEMA GLOBAL DEFAULT CHAR SET utf8mb4", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.AlterDatabaseStmt)) - st, err = p.ParseOneStmt("ALTER DATABASE CHAR SET = utf8mb4", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.AlterDatabaseStmt)) - st, err = p.ParseOneStmt("ALTER DATABASE DEFAULT CHAR SET = utf8mb4", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.AlterDatabaseStmt)) -} - -func TestUnderscoreCharset(t *testing.T) { - p := parser.New() - tests := []struct { - cs string - parseFail bool - unSupport bool - }{ - {"utf8", false, false}, - {"gbk", false, true}, - {"ujis", false, true}, - {"gbk1", true, true}, - {"ujisx", true, true}, - } - for _, tt := range tests { - sql := fmt.Sprintf("select hex(_%s '3F')", tt.cs) - _, err := p.ParseOneStmt(sql, "", "") - if tt.parseFail { - require.EqualError(t, err, fmt.Sprintf("line 1 column %d near \"'3F')\" ", len(tt.cs)+17)) - } else if tt.unSupport { - require.EqualError(t, err, ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", tt.cs).Error()) - } else { - require.NoError(t, err) - } - } -} - -func TestFulltextSearch(t *testing.T) { - p := parser.New() - - st, err := p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search')", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.SelectStmt)) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH() AGAINST('search')", "", "") - require.Error(t, err) - require.Nil(t, st) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST()", "", "") - require.Error(t, err) - require.Nil(t, st) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search' IN)", "", "") - require.Error(t, err) - require.Nil(t, st) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search' IN BOOLEAN MODE WITH QUERY EXPANSION)", "", "") - require.Error(t, err) - require.Nil(t, st) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' IN NATURAL LANGUAGE MODE)", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.SelectStmt)) - writer := bytes.NewBufferString("") - st.(*ast.SelectStmt).Where.Format(writer) - require.Equal(t, "MATCH(title,content) AGAINST(\"search\")", writer.String()) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' IN BOOLEAN MODE)", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.SelectStmt)) - writer.Reset() - st.(*ast.SelectStmt).Where.Format(writer) - require.Equal(t, "MATCH(title,content) AGAINST(\"search\" IN BOOLEAN MODE)", writer.String()) - - st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' WITH QUERY EXPANSION)", "", "") - require.NoError(t, err) - require.NotNil(t, st.(*ast.SelectStmt)) - writer.Reset() - st.(*ast.SelectStmt).Where.Format(writer) - require.Equal(t, "MATCH(title,content) AGAINST(\"search\" WITH QUERY EXPANSION)", writer.String()) -} - -func TestStartTransaction(t *testing.T) { - cases := []testCase{ - {"START TRANSACTION READ WRITE", true, "START TRANSACTION"}, - {"START TRANSACTION WITH CONSISTENT SNAPSHOT", true, "START TRANSACTION"}, - {"START TRANSACTION WITH CAUSAL CONSISTENCY ONLY", true, "START TRANSACTION WITH CAUSAL CONSISTENCY ONLY"}, - {"START TRANSACTION READ ONLY", true, "START TRANSACTION READ ONLY"}, - {"START TRANSACTION READ ONLY AS OF", false, ""}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP", false, ""}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP '2015-09-21 00:07:01'", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP _UTF8MB4'2015-09-21 00:07:01'"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', '2021-04-27 11:26:13')", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, - } - - RunTest(t, cases, false) -} - -func TestSignedInt64OutOfRange(t *testing.T) { - p := parser.New() - cases := []string{ - "recover table by job 18446744073709551612", - "recover table t 18446744073709551612", - "admin check index t idx (0, 18446744073709551612)", - "create user abc@def with max_queries_per_hour 18446744073709551612", - } - - for _, s := range cases { - _, err := p.ParseOneStmt(s, "", "") - require.Error(t, err) - require.Contains(t, err.Error(), "out of range") - } -} - -// CleanNodeText set the text of node and all child node empty. -// For test only. -func CleanNodeText(node ast.Node) { - var cleaner nodeTextCleaner - node.Accept(&cleaner) -} - -// nodeTextCleaner clean the text of a node and it's child node. -// For test only. -type nodeTextCleaner struct { -} - -func cleanPartition(n ast.Node) { - if p, ok := n.(*ast.PartitionOptions); ok && p != nil { - var tmpCleaner nodeTextCleaner - if p.Interval != nil { - p.Interval.SetText(nil, "") - p.Interval.SetOriginTextPosition(0) - p.Interval.IntervalExpr.Expr.Accept(&tmpCleaner) - if p.Interval.FirstRangeEnd != nil { - (*p.Interval.FirstRangeEnd).Accept(&tmpCleaner) - } - if p.Interval.LastRangeEnd != nil { - (*p.Interval.LastRangeEnd).Accept(&tmpCleaner) - } - } - } -} - -// Enter implements Visitor interface. -func (checker *nodeTextCleaner) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - in.SetText(nil, "") - in.SetOriginTextPosition(0) - if v, ok := in.(ast.ValueExpr); ok && v != nil { - tpFlag := v.GetType().GetFlag() - if tpFlag&mysql.UnderScoreCharsetFlag != 0 { - // ignore underscore charset flag to let `'abc' = _utf8'abc'` pass - tpFlag ^= mysql.UnderScoreCharsetFlag - v.GetType().SetFlag(tpFlag) - } - } - - switch node := in.(type) { - case *ast.CreateTableStmt: - for _, opt := range node.Options { - switch opt.Tp { - case ast.TableOptionCharset: - opt.StrValue = strings.ToUpper(opt.StrValue) - case ast.TableOptionCollate: - opt.StrValue = strings.ToUpper(opt.StrValue) - } - } - for _, col := range node.Cols { - col.Tp.SetCharset(strings.ToUpper(col.Tp.GetCharset())) - col.Tp.SetCollate(strings.ToUpper(col.Tp.GetCollate())) - - for i, option := range col.Options { - if option.Tp == 0 && option.Expr == nil && !option.Stored && option.Refer == nil { - col.Options = append(col.Options[:i], col.Options[i+1:]...) - } - } - } - case *ast.DeleteStmt: - for _, tableHint := range node.TableHints { - tableHint.HintName.O = "" - } - case *ast.UpdateStmt: - for _, tableHint := range node.TableHints { - tableHint.HintName.O = "" - } - case *ast.Constraint: - if node.Option != nil { - if node.Option.KeyBlockSize == 0x0 && node.Option.Tp == 0 && node.Option.Comment == "" { - node.Option = nil - } - } - case *ast.FuncCallExpr: - node.FnName.O = strings.ToLower(node.FnName.O) - node.SetOriginTextPosition(0) - case *ast.AggregateFuncExpr: - node.F = strings.ToLower(node.F) - case *ast.SelectField: - node.Offset = 0 - case *test_driver.ValueExpr: - if node.Kind() == test_driver.KindMysqlDecimal { - _ = node.GetMysqlDecimal().FromString(node.GetMysqlDecimal().ToString()) - } - case *ast.GrantStmt: - var privs []*ast.PrivElem - for _, v := range node.Privs { - if v.Priv != 0 { - privs = append(privs, v) - } - } - node.Privs = privs - case *ast.AlterTableStmt: - var specs []*ast.AlterTableSpec - for _, v := range node.Specs { - if v.Tp != 0 && !(v.Tp == ast.AlterTableOption && len(v.Options) == 0) { - specs = append(specs, v) - } - } - node.Specs = specs - case *ast.Join: - node.ExplicitParens = false - case *ast.ColumnDef: - node.Tp.CleanElemIsBinaryLit() - case *ast.PartitionOptions: - cleanPartition(node) - } - return in, false -} - -// Leave implements Visitor interface. -func (checker *nodeTextCleaner) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, true -} - -// For index advisor -func TestIndexAdviseStmt(t *testing.T) { - table := []testCase{ - {"INDEX ADVISE INFILE '/tmp/t.sql'", true, "INDEX ADVISE INFILE '/tmp/t.sql'"}, - {"INDEX ADVISE LOCAL INFILE '/tmp/t.sql'", true, "INDEX ADVISE LOCAL INFILE '/tmp/t.sql'"}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1", false, ""}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4 PER_TABLE 8", false, ""}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 4", false, ""}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_DB 4", false, ""}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4", false, ""}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 LINES STARTING BY 'ab' TERMINATED BY '\n'", false, ""}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 LINES STARTING BY 'ab' TERMINATED BY 'cd'", false, ""}, - - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4 LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4 LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", false, ""}, - {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", false, ""}, - } - - RunTest(t, table, false) -} - -// For BRIE -func TestBRIE(t *testing.T) { - table := []testCase{ - {"BACKUP DATABASE a TO 'local:///tmp/archive01/'", true, "BACKUP DATABASE `a` TO 'local:///tmp/archive01/'"}, - {"BACKUP SCHEMA a TO 'local:///tmp/archive01/'", true, "BACKUP DATABASE `a` TO 'local:///tmp/archive01/'"}, - {"BACKUP DATABASE a,b,c TO 'noop://'", true, "BACKUP DATABASE `a`, `b`, `c` TO 'noop://'"}, - {"BACKUP DATABASE a.b TO 'noop://'", false, ""}, - {"BACKUP DATABASE * TO 'noop://'", true, "BACKUP DATABASE * TO 'noop://'"}, - {"BACKUP DATABASE *, a TO 'noop://'", false, ""}, - {"BACKUP DATABASE a, * TO 'noop://'", false, ""}, - {"BACKUP DATABASE TO 'noop://'", false, ""}, - {"BACKUP TABLE a TO 'noop://'", true, "BACKUP TABLE `a` TO 'noop://'"}, - {"BACKUP TABLE a.b TO 'noop://'", true, "BACKUP TABLE `a`.`b` TO 'noop://'"}, - {"BACKUP TABLE a.b,c.d,e TO 'noop://'", true, "BACKUP TABLE `a`.`b`, `c`.`d`, `e` TO 'noop://'"}, - {"BACKUP TABLE a.* TO 'noop://'", false, ""}, - {"BACKUP TABLE * TO 'noop://'", false, ""}, - {"BACKUP TABLE TO 'noop://'", false, ""}, - {"RESTORE DATABASE * FROM 's3://bucket/path/'", true, "RESTORE DATABASE * FROM 's3://bucket/path/'"}, - - {"BACKUP DATABASE * TO 'noop://' LAST_BACKUP = '2020-02-02 14:14:14'", true, "BACKUP DATABASE * TO 'noop://' LAST_BACKUP = '2020-02-02 14:14:14'"}, - {"BACKUP DATABASE * TO 'noop://' LAST_BACKUP = 1234567890", true, "BACKUP DATABASE * TO 'noop://' LAST_BACKUP = 1234567890"}, - - {"backup database * to 'noop://' rate_limit 500 MB/second snapshot 5 minute ago", true, "BACKUP DATABASE * TO 'noop://' RATE_LIMIT = 500 MB/SECOND SNAPSHOT = 300000000 MICROSECOND AGO"}, - {"backup database * to 'noop://' snapshot = '2020-03-18 18:13:54'", true, "BACKUP DATABASE * TO 'noop://' SNAPSHOT = '2020-03-18 18:13:54'"}, - {"backup database * to 'noop://' snapshot = 1234567890", true, "BACKUP DATABASE * TO 'noop://' SNAPSHOT = 1234567890"}, - {"restore table g from 'noop://' concurrency 40 checksum 0 online 1", true, "RESTORE TABLE `g` FROM 'noop://' CONCURRENCY = 40 CHECKSUM = OFF ONLINE = 1"}, - { - "backup table x to 's3://bucket/path/?endpoint=https://test-cluster-s3.local&access-key=aaaaaaaaa&secret-access-key=bbbbbbbb&force-path-style=1'", - true, - "BACKUP TABLE `x` TO 's3://bucket/path/?endpoint=https://test-cluster-s3.local&access-key=aaaaaaaaa&secret-access-key=bbbbbbbb&force-path-style=1'", - }, - { - "backup database * to 's3://bucket/path/?provider=alibaba®ion=us-west-9&storage-class=glacier&sse=AES256&acl=authenticated-read&use-accelerate-endpoint=1' send_credentials_to_tikv = 1", - true, - "BACKUP DATABASE * TO 's3://bucket/path/?provider=alibaba®ion=us-west-9&storage-class=glacier&sse=AES256&acl=authenticated-read&use-accelerate-endpoint=1' SEND_CREDENTIALS_TO_TIKV = 1", - }, - { - "restore database * from 'gcs://bucket/path/?endpoint=https://test-cluster.gcs.local&storage-class=coldline&predefined-acl=OWNER&credentials-file=/data/private/creds.json'", - true, - "RESTORE DATABASE * FROM 'gcs://bucket/path/?endpoint=https://test-cluster.gcs.local&storage-class=coldline&predefined-acl=OWNER&credentials-file=/data/private/creds.json'", - }, - {"restore table g from 'noop://' checksum off", true, "RESTORE TABLE `g` FROM 'noop://' CHECKSUM = OFF"}, - {"restore table g from 'noop://' checksum optional", true, "RESTORE TABLE `g` FROM 'noop://' CHECKSUM = OPTIONAL"}, - {"backup logs to 'noop://'", true, "BACKUP LOGS TO 'noop://'"}, - {"backup logs to 'noop://' start_ts='20220304'", true, "BACKUP LOGS TO 'noop://' START_TS = '20220304'"}, - {"pause backup logs", true, "PAUSE BACKUP LOGS"}, - {"pause backup logs gc_ttl='20220304'", true, "PAUSE BACKUP LOGS GC_TTL = '20220304'"}, - {"resume backup logs", true, "RESUME BACKUP LOGS"}, - {"show backup logs status", true, "SHOW BACKUP LOGS STATUS"}, - {"show backup logs metadata from 'noop://'", true, "SHOW BACKUP LOGS METADATA FROM 'noop://'"}, - {"show br job 1234", true, "SHOW BR JOB 1234"}, - {"show br job query 1234", true, "SHOW BR JOB QUERY 1234"}, - {"cancel br job 1234", true, "CANCEL BR JOB 1234"}, - {"purge backup logs from 'noop://'", true, "PURGE BACKUP LOGS FROM 'noop://'"}, - {"purge backup logs from 'noop://' until_ts='2012122304'", true, "PURGE BACKUP LOGS FROM 'noop://' UNTIL_TS = '2012122304'"}, - {"restore point from 'noop://log_backup'", true, "RESTORE POINT FROM 'noop://log_backup'"}, - {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log'"}, - {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log' restored_ts='20230123'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log' RESTORED_TS = '20230123'"}, - {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log' start_ts='20230101' restored_ts='20230123'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log' START_TS = '20230101' RESTORED_TS = '20230123'"}, - } - - RunTest(t, table, false) -} - -func TestStatisticsOps(t *testing.T) { - table := []testCase{ - {"create statistics stats1 (cardinality) on t(a,b,c)", true, "CREATE STATISTICS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, - {"create statistics stats2 (dependency) on t(a,b)", true, "CREATE STATISTICS `stats2` (DEPENDENCY) ON `t`(`a`, `b`)"}, - {"create statistics stats3 (correlation) on t(a,b)", true, "CREATE STATISTICS `stats3` (CORRELATION) ON `t`(`a`, `b`)"}, - {"create statistics stats3 on t(a,b)", false, ""}, - {"create statistics if not exists stats1 (cardinality) on t(a,b,c)", true, "CREATE STATISTICS IF NOT EXISTS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, - {"create statistics if not exists stats2 (dependency) on t(a,b)", true, "CREATE STATISTICS IF NOT EXISTS `stats2` (DEPENDENCY) ON `t`(`a`, `b`)"}, - {"create statistics if not exists stats3 (correlation) on t(a,b)", true, "CREATE STATISTICS IF NOT EXISTS `stats3` (CORRELATION) ON `t`(`a`, `b`)"}, - {"create statistics if not exists stats3 on t(a,b)", false, ""}, - {"create statistics stats1(cardinality) on t(a,b,c)", true, "CREATE STATISTICS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, - {"drop statistics stats1", true, "DROP STATISTICS `stats1`"}, - } - RunTest(t, table, false) - - p := parser.New() - sms, _, err := p.Parse("create statistics if not exists stats1 (cardinality) on t(a,b,c)", "", "") - require.NoError(t, err) - v, ok := sms[0].(*ast.CreateStatisticsStmt) - require.True(t, ok) - require.True(t, v.IfNotExists) - require.Equal(t, "stats1", v.StatsName) - require.Equal(t, ast.StatsTypeCardinality, v.StatsType) - require.Equal(t, model.CIStr{O: "t", L: "t"}, v.Table.Name) - require.Len(t, v.Columns, 3) - require.Equal(t, model.CIStr{O: "a", L: "a"}, v.Columns[0].Name) - require.Equal(t, model.CIStr{O: "b", L: "b"}, v.Columns[1].Name) - require.Equal(t, model.CIStr{O: "c", L: "c"}, v.Columns[2].Name) -} - -func TestHighNotPrecedenceMode(t *testing.T) { - p := parser.New() - var sb strings.Builder - - sms, _, err := p.Parse("SELECT NOT 1 BETWEEN -5 AND 5", "", "") - require.NoError(t, err) - v, ok := sms[0].(*ast.SelectStmt) - require.True(t, ok) - v1, ok := v.Fields.Fields[0].Expr.(*ast.UnaryOperationExpr) - require.True(t, ok) - require.Equal(t, opcode.Not, v1.Op) - err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) - require.NoError(t, err) - restoreSQL := sb.String() - require.Equal(t, "SELECT NOT 1 BETWEEN -5 AND 5", restoreSQL) - sb.Reset() - - sms, _, err = p.Parse("SELECT !1 BETWEEN -5 AND 5", "", "") - require.NoError(t, err) - v, ok = sms[0].(*ast.SelectStmt) - require.True(t, ok) - _, ok = v.Fields.Fields[0].Expr.(*ast.BetweenExpr) - require.True(t, ok) - err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) - require.NoError(t, err) - restoreSQL = sb.String() - require.Equal(t, "SELECT !1 BETWEEN -5 AND 5", restoreSQL) - sb.Reset() - - p = parser.New() - p.SetSQLMode(mysql.ModeHighNotPrecedence) - sms, _, err = p.Parse("SELECT NOT 1 BETWEEN -5 AND 5", "", "") - require.NoError(t, err) - v, ok = sms[0].(*ast.SelectStmt) - require.True(t, ok) - _, ok = v.Fields.Fields[0].Expr.(*ast.BetweenExpr) - require.True(t, ok) - err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) - require.NoError(t, err) - restoreSQL = sb.String() - require.Equal(t, "SELECT !1 BETWEEN -5 AND 5", restoreSQL) -} - -// For CTE -func TestCTE(t *testing.T) { - table := []testCase{ - {"WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` (col1, col2) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` AS (SELECT 1,2), cte2 as (select 3) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2), `cte2` AS (SELECT 3) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH RECURSIVE cte (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5)SELECT * FROM cte;", true, "WITH RECURSIVE `cte` (`n`) AS (SELECT 1 UNION ALL SELECT `n`+1 FROM `cte` WHERE `n`<5) SELECT * FROM `cte`"}, - {"with cte(a) as (select 1) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) UPDATE (`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a`=`cte`.`a`"}, - {"with cte(a) as (select 1) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) DELETE `t` FROM (`t`) JOIN `cte` WHERE `t`.`a`=`cte`.`a`"}, - {"WITH cte1 AS (SELECT 1) SELECT * FROM (WITH cte2 AS (SELECT 2) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT 1) SELECT * FROM (WITH `cte2` AS (SELECT 2) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, - {"WITH cte AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, - {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `t`) TABLE `cte`"}, - {"with cte as (select 1) select 1 union with cte as (select 1) select * from cte;", false, ""}, - {"with cte as (select 1) (select 1);", true, "WITH `cte` AS (SELECT 1) (SELECT 1)"}, - {"with cte as (select 1) (select 1 union select 1)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT 1)"}, - {"select * from (with cte as (select 1) select 1 union select 2) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT 1) SELECT 1 UNION SELECT 2) AS `qn`"}, - {"select * from t where 1 > (with cte as (select 2) select * from cte)", true, "SELECT * FROM `t` WHERE 1>(WITH `cte` AS (SELECT 2) SELECT * FROM `cte`)"}, - {"( with cte(n) as ( select 1 ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte` UNION SELECT `n`+2 FROM `cte`) UNION SELECT 1"}, - {"( with cte(n) as ( select 1 ) select n+1 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte`) UNION SELECT 1"}, - {"( with cte(n) as ( select 1 ) (select n+1 from cte)) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) (SELECT `n`+1 FROM `cte`)) UNION SELECT 1"}, - } - - RunTest(t, table, false) -} - -// For CTE Merge -func TestCTEMerge(t *testing.T) { - table := []testCase{ - {"WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` (col1, col2) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` AS (SELECT 1,2), cte2 as (select 3) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2), `cte2` AS (SELECT 3) SELECT `col1`,`col2` FROM `cte`"}, - {"with cte(a) as (select 1) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) UPDATE (`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a`=`cte`.`a`"}, - {"with cte(a) as (select 1) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) DELETE `t` FROM (`t`) JOIN `cte` WHERE `t`.`a`=`cte`.`a`"}, - {"WITH cte1 AS (SELECT 1) SELECT * FROM (WITH cte2 AS (SELECT 2) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT 1) SELECT * FROM (WITH `cte2` AS (SELECT 2) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, - {"WITH cte AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, - {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `t`) TABLE `cte`"}, - {"with cte as (select 1) select 1 union with cte as (select 1) select * from cte;", false, ""}, - {"with cte as (select 1) (select 1);", true, "WITH `cte` AS (SELECT 1) (SELECT 1)"}, - {"with cte as (select 1) (select 1 union select 1)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT 1)"}, - {"select * from (with cte as (select 1) select 1 union select 2) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT 1) SELECT 1 UNION SELECT 2) AS `qn`"}, - {"select * from t where 1 > (with cte as (select 2) select * from cte)", true, "SELECT * FROM `t` WHERE 1>(WITH `cte` AS (SELECT 2) SELECT * FROM `cte`)"}, - {"( with cte(n) as ( select 1 ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte` UNION SELECT `n`+2 FROM `cte`) UNION SELECT 1"}, - {"( with cte(n) as ( select 1 ) select n+1 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte`) UNION SELECT 1"}, - {"( with cte(n) as ( select 1 ) (select n+1 from cte)) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) (SELECT `n`+1 FROM `cte`)) UNION SELECT 1"}, - } - - RunTest(t, table, false) -} - -func TestAsOfClause(t *testing.T) { - table := []testCase{ - {"SELECT * FROM `t` AS /* comment */ a;", true, "SELECT * FROM `t` AS `a`"}, - {"SELECT * FROM `t` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW());", true, "SELECT * FROM `t` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, - {"select * from `t` as of timestamp '2021-04-15 00:00:00'", true, "SELECT * FROM `t` AS OF TIMESTAMP _UTF8MB4'2021-04-15 00:00:00'"}, - {"SELECT * FROM (`a` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())) JOIN `b` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW());", true, "SELECT * FROM (`a` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())) JOIN `b` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, - {"INSERT INTO `employees` (SELECT * FROM `employees` AS OF TIMESTAMP (DATE_SUB(NOW(), INTERVAL _UTF8MB4'60' MINUTE)) NOT IN (SELECT * FROM `employees`))", true, "INSERT INTO `employees` (SELECT * FROM `employees` AS OF TIMESTAMP (DATE_SUB(NOW(), INTERVAL _UTF8MB4'60' MINUTE)) NOT IN (SELECT * FROM `employees`))"}, - {"SET TRANSACTION READ ONLY as of timestamp '2021-04-21 00:42:12'", true, "SET @@SESSION.`tx_read_ts`=_UTF8MB4'2021-04-21 00:42:12'"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP '2015-09-21 00:07:01'", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP _UTF8MB4'2015-09-21 00:07:01'"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, - {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', '2021-04-27 11:26:13')", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, - } - RunTest(t, table, false) -} - -// For `PARTITION BY [LINEAR] KEY ALGORITHM` syntax -func TestPartitionKeyAlgorithm(t *testing.T) { - table := []testCase{ - {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 1 (c1,c2) PARTITIONS 4", true, "CREATE TABLE `t` (`c1` INT,`c2` INT) PARTITION BY LINEAR KEY ALGORITHM = 1 (`c1`,`c2`) PARTITIONS 4"}, - {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = -1 (c1,c2) PARTITIONS 4", false, ""}, - {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 0 (c1,c2) PARTITIONS 4", false, ""}, - {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 3 (c1,c2) PARTITIONS 4", false, ""}, - } - - RunTest(t, table, false) -} - -// server side help syntax -func TestHelp(t *testing.T) { - table := []testCase{ - {"HELP 'select'", true, "HELP 'select'"}, - } - - RunTest(t, table, false) -} - -func TestWithoutCharsetFlags(t *testing.T) { - type testCaseWithFlag struct { - src string - ok bool - restore string - flag RestoreFlags - } - - flag := RestoreStringSingleQuotes | RestoreSpacesAroundBinaryOperation | RestoreBracketAroundBinaryOperation | RestoreNameBackQuotes - cases := []testCaseWithFlag{ - {"select 'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, - {"select _utf8'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, - {"select _utf8mb4'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, - {"select _utf8 X'D0B1'", true, "SELECT x'd0b1'", flag | RestoreStringWithoutCharset}, - - {"select _utf8mb4'a'", true, "SELECT 'a'", flag | RestoreStringWithoutDefaultCharset}, - {"select _utf8'a'", true, "SELECT _utf8'a'", flag | RestoreStringWithoutDefaultCharset}, - {"select _utf8'a'", true, "SELECT _utf8'a'", flag | RestoreStringWithoutDefaultCharset}, - {"select _utf8 X'D0B1'", true, "SELECT _utf8 x'd0b1'", flag | RestoreStringWithoutDefaultCharset}, - } - - p := parser.New() - p.EnableWindowFunc(false) - for _, tbl := range cases { - stmts, _, err := p.Parse(tbl.src, "", "") - if !tbl.ok { - require.Error(t, err) - continue - } - require.NoError(t, err) - // restore correctness test - var sb strings.Builder - restoreSQLs := "" - for _, stmt := range stmts { - sb.Reset() - ctx := NewRestoreCtx(tbl.flag, &sb) - ctx.DefaultDB = "test" - err = stmt.Restore(ctx) - require.NoError(t, err) - restoreSQL := sb.String() - if restoreSQLs != "" { - restoreSQLs += "; " - } - restoreSQLs += restoreSQL - } - require.Equal(t, tbl.restore, restoreSQLs) - } -} - -func TestRestoreBinOpWithBrackets(t *testing.T) { - cases := []testCase{ - {"select mod(a+b, 4)+1", true, "SELECT (((`a` + `b`) % 4) + 1)"}, - {"select mod( year(a) - abs(weekday(a) + dayofweek(a)), 4) + 1", true, "SELECT (((year(`a`) - abs((weekday(`a`) + dayofweek(`a`)))) % 4) + 1)"}, - } - - p := parser.New() - p.EnableWindowFunc(false) - for _, tbl := range cases { - _, _, err := p.Parse(tbl.src, "", "") - comment := fmt.Sprintf("source %v", tbl.src) - if !tbl.ok { - require.Error(t, err, comment) - continue - } - require.NoError(t, err, comment) - // restore correctness test - if tbl.ok { - var sb strings.Builder - comment := fmt.Sprintf("source %v", tbl.src) - stmts, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err, comment) - restoreSQLs := "" - for _, stmt := range stmts { - sb.Reset() - ctx := NewRestoreCtx(RestoreStringSingleQuotes|RestoreSpacesAroundBinaryOperation|RestoreBracketAroundBinaryOperation|RestoreStringWithoutCharset|RestoreNameBackQuotes, &sb) - ctx.DefaultDB = "test" - err = stmt.Restore(ctx) - require.NoError(t, err, comment) - restoreSQL := sb.String() - comment = fmt.Sprintf("source %v; restore %v", tbl.src, restoreSQL) - if restoreSQLs != "" { - restoreSQLs += "; " - } - restoreSQLs += restoreSQL - } - comment = fmt.Sprintf("restore %v; expect %v", restoreSQLs, tbl.restore) - require.Equal(t, tbl.restore, restoreSQLs, comment) - } - } -} - -// For CTE bindings. -func TestCTEBindings(t *testing.T) { - table := []testCase{ - {"WITH `cte` AS (SELECT * from t) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` (col1, col2) AS (SELECT * from t UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT * FROM `test`.`t` UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH `cte` AS (SELECT * from t), cte2 as (select * from cte) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT * FROM `test`.`t`), `cte2` AS (SELECT * FROM `cte`) SELECT `col1`,`col2` FROM `cte`"}, - {"WITH RECURSIVE cte (n) AS ( SELECT * from t UNION ALL SELECT n + 1 FROM cte WHERE n < 5)SELECT * FROM cte;", true, "WITH RECURSIVE `cte` (`n`) AS (SELECT * FROM `test`.`t` UNION ALL SELECT `n` + 1 FROM `cte` WHERE `n` < 5) SELECT * FROM `cte`"}, - {"with cte(a) as (select * from t) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT * FROM `test`.`t`) UPDATE (`test`.`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a` = `cte`.`a`"}, - {"with cte(a) as (select * from t) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT * FROM `test`.`t`) DELETE `test`.`t` FROM (`test`.`t`) JOIN `cte` WHERE `t`.`a` = `cte`.`a`"}, - {"WITH cte1 AS (SELECT * from t) SELECT * FROM (WITH cte2 AS (SELECT * from cte1) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT * FROM `test`.`t`) SELECT * FROM (WITH `cte2` AS (SELECT * FROM `cte1`) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, - {"WITH cte AS (SELECT * from t) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, - {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `test`.`t`) TABLE `cte`"}, - {"with cte as (select * from t) select 1 union with cte as (select * from t) select * from cte;", false, ""}, - {"with cte as (select * from t) (select * from t);", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) (SELECT * FROM `test`.`t`)"}, - {"with cte as (select 1) (select 1 union select * from t)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT * FROM `test`.`t`)"}, - {"select * from (with cte as (select * from t) select 1 union select * from t) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT 1 UNION SELECT * FROM `test`.`t`) AS `qn`"}, - {"select * from t where 1 > (with cte as (select * from t) select * from cte)", true, "SELECT * FROM `test`.`t` WHERE 1 > (WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT * FROM `cte`)"}, - {"( with cte(n) as ( select * from t ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT * FROM `test`.`t`) SELECT `n` + 1 FROM `cte` UNION SELECT `n` + 2 FROM `cte`) UNION SELECT 1"}, - {"( with cte(n) as ( select * from t ) select n+1 from cte) union select * from t", true, "(WITH `cte` (`n`) AS (SELECT * FROM `test`.`t`) SELECT `n` + 1 FROM `cte`) UNION SELECT * FROM `test`.`t`"}, - {"with cte as (select * from t union select * from cte) select * from cte", true, "WITH `cte` AS (SELECT * FROM `test`.`t` UNION SELECT * FROM `test`.`cte`) SELECT * FROM `cte`"}, - } - - p := parser.New() - p.EnableWindowFunc(false) - for _, tbl := range table { - _, _, err := p.Parse(tbl.src, "", "") - comment := fmt.Sprintf("source %v", tbl.src) - if !tbl.ok { - require.Error(t, err, comment) - continue - } - require.NoError(t, err, comment) - // restore correctness test - if tbl.ok { - var sb strings.Builder - comment := fmt.Sprintf("source %v", tbl.src) - stmts, _, err := p.Parse(tbl.src, "", "") - require.NoError(t, err, comment) - restoreSQLs := "" - for _, stmt := range stmts { - sb.Reset() - ctx := NewRestoreCtx(RestoreStringSingleQuotes|RestoreSpacesAroundBinaryOperation|RestoreStringWithoutCharset|RestoreNameBackQuotes, &sb) - ctx.DefaultDB = "test" - err = stmt.Restore(ctx) - require.NoError(t, err, comment) - restoreSQL := sb.String() - comment = fmt.Sprintf("source %v; restore %v", tbl.src, restoreSQL) - if restoreSQLs != "" { - restoreSQLs += "; " - } - restoreSQLs += restoreSQL - } - comment = fmt.Sprintf("restore %v; expect %v", restoreSQLs, tbl.restore) - require.Equal(t, tbl.restore, restoreSQLs, comment) - } - } -} - -func TestPlanReplayer(t *testing.T) { - table := []testCase{ - {"PLAN REPLAYER DUMP EXPLAIN SELECT a FROM t", true, "PLAN REPLAYER DUMP EXPLAIN SELECT `a` FROM `t`"}, - {"PLAN REPLAYER DUMP EXPLAIN SELECT * FROM t WHERE a > 10", true, "PLAN REPLAYER DUMP EXPLAIN SELECT * FROM `t` WHERE `a`>10"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM t WHERE a > 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, - {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE a > 10 and t < 1 ORDER BY t LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE `a`>10 AND `t`<1 ORDER BY `t` LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE a > 10 and t < 1 ORDER BY t LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE `a`>10 AND `t`<1 ORDER BY `t` LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE a > 10 and t < 1 LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE `a`>10 AND `t`<1 LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE a > 10 and t < 1 LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE `a`>10 AND `t`<1 LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY LIMIT 10"}, - {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY"}, - {"PLAN REPLAYER LOAD '/tmp/sdfaalskdjf.zip'", true, "PLAN REPLAYER LOAD '/tmp/sdfaalskdjf.zip'"}, - {"PLAN REPLAYER DUMP EXPLAIN 'sql.txt'", true, "PLAN REPLAYER DUMP EXPLAIN 'sql.txt'"}, - {"PLAN REPLAYER DUMP EXPLAIN ANALYZE 'sql.txt'", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE 'sql.txt'"}, - {"PLAN REPLAYER CAPTURE '123' '123'", true, "PLAN REPLAYER CAPTURE '123' '123'"}, - {"PLAN REPLAYER CAPTURE REMOVE '123' '123'", true, "PLAN REPLAYER CAPTURE REMOVE '123' '123'"}, - } - RunTest(t, table, false) - - p := parser.New() - sms, _, err := p.Parse("PLAN REPLAYER DUMP EXPLAIN SELECT a FROM t", "", "") - require.NoError(t, err) - v, ok := sms[0].(*ast.PlanReplayerStmt) - require.True(t, ok) - require.Equal(t, "SELECT a FROM t", v.Stmt.Text()) - require.False(t, v.Analyze) - - sms, _, err = p.Parse("PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT a FROM t", "", "") - require.NoError(t, err) - v, ok = sms[0].(*ast.PlanReplayerStmt) - require.True(t, ok) - require.Equal(t, "SELECT a FROM t", v.Stmt.Text()) - require.True(t, v.Analyze) -} - -func TestGBKEncoding(t *testing.T) { - p := parser.New() - gbkEncoding, _ := charset.Lookup("gbk") - encoder := gbkEncoding.NewEncoder() - sql, err := encoder.String("create table 测试表 (测试列 varchar(255) default 'GBK测试用例');") - require.NoError(t, err) - - stmt, _, err := p.ParseSQL(sql) - require.NoError(t, err) - checker := &gbkEncodingChecker{} - _, _ = stmt[0].Accept(checker) - require.NotEqual(t, "测试表", checker.tblName) - require.NotEqual(t, "测试列", checker.colName) - - gbkOpt := parser.CharsetClient("gbk") - stmt, _, err = p.ParseSQL(sql, gbkOpt) - require.NoError(t, err) - _, _ = stmt[0].Accept(checker) - require.Equal(t, "测试表", checker.tblName) - require.Equal(t, "测试列", checker.colName) - require.Equal(t, "GBK测试用例", checker.expr) - - _, _, err = p.ParseSQL("select _gbk '\xc6\x5c' from dual;") - require.Error(t, err) - - for _, test := range []struct { - sql string - err bool - }{ - {"select '\xc6\x5c' from `\xab\x60`;", false}, - {`prepare p1 from "insert into t values ('中文');";`, false}, - {"select '啊';", false}, - {"create table t1(s set('a一','b二','c三'));", false}, - {"insert into t3 values('一a');", false}, - {"select '\xa5\x5c'", false}, - {"select '''\xa5\x5c'", false}, - {"select ```\xa5\x5c`", false}, - {"select '\x65\x5c'", true}, - } { - _, _, err = p.ParseSQL(test.sql, gbkOpt) - if test.err { - require.Error(t, err, test.sql) - } else { - require.NoError(t, err, test.sql) - } - } -} - -type gbkEncodingChecker struct { - tblName string - colName string - expr string -} - -func (g *gbkEncodingChecker) Enter(n ast.Node) (node ast.Node, skipChildren bool) { - if tn, ok := n.(*ast.TableName); ok { - g.tblName = tn.Name.O - return n, false - } - if cn, ok := n.(*ast.ColumnName); ok { - g.colName = cn.Name.O - return n, false - } - if c, ok := n.(*ast.ColumnOption); ok { - if ve, ok := c.Expr.(ast.ValueExpr); ok { - g.expr = ve.GetString() - return n, false - } - } - return n, false -} - -func (g *gbkEncodingChecker) Leave(n ast.Node) (node ast.Node, ok bool) { - return n, true -} - -func TestInsertStatementMemoryAllocation(t *testing.T) { - sql := "insert t values (1)" + strings.Repeat(",(1)", 1000) - var oldStats, newStats runtime.MemStats - runtime.ReadMemStats(&oldStats) - _, err := parser.New().ParseOneStmt(sql, "", "") - require.NoError(t, err) - runtime.ReadMemStats(&newStats) - require.Less(t, int(newStats.TotalAlloc-oldStats.TotalAlloc), 1024*500) -} - -func TestCharsetIntroducer(t *testing.T) { - p := parser.New() - defer charset.RemoveCharset("gbk") - // `_gbk` is treated as a character set. - _, _, err := p.Parse("select _gbk 'a';", "", "") - require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") - _, _, err = p.Parse("select _gbk 0x1234;", "", "") - require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") - _, _, err = p.Parse("select _gbk 0b101001;", "", "") - require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") -} - -func TestNonTransactionalDML(t *testing.T) { - cases := []testCase{ - // deletes - {"batch on c limit 10 delete from t where c = 10", true, - "BATCH ON `c` LIMIT 10 DELETE FROM `t` WHERE `c`=10"}, - {"batch on c limit 10 dry run delete from t where c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN DELETE FROM `t` WHERE `c`=10"}, - {"batch on c limit 10 dry run query delete from t where c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN QUERY DELETE FROM `t` WHERE `c`=10"}, - {"batch limit 10 delete from t where c = 10", true, - "BATCH LIMIT 10 DELETE FROM `t` WHERE `c`=10"}, - {"batch limit 10 dry run delete from t where c = 10", true, - "BATCH LIMIT 10 DRY RUN DELETE FROM `t` WHERE `c`=10"}, - {"batch limit 10 dry run query delete from t where c = 10", true, - "BATCH LIMIT 10 DRY RUN QUERY DELETE FROM `t` WHERE `c`=10"}, - // updates - {"batch on c limit 10 update t set c = 10", true, - "BATCH ON `c` LIMIT 10 UPDATE `t` SET `c`=10"}, - {"batch on c limit 10 dry run update t set c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN UPDATE `t` SET `c`=10"}, - {"batch on c limit 10 dry run query update t set c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN QUERY UPDATE `t` SET `c`=10"}, - {"batch limit 10 update t set c = 10", true, - "BATCH LIMIT 10 UPDATE `t` SET `c`=10"}, - {"batch limit 10 dry run update t set c = 10", true, - "BATCH LIMIT 10 DRY RUN UPDATE `t` SET `c`=10"}, - {"batch limit 10 dry run query update t set c = 10", true, - "BATCH LIMIT 10 DRY RUN QUERY UPDATE `t` SET `c`=10"}, - // inserts - {"batch on c limit 10 insert into t1 select * from t2 where c = 10", true, - "BATCH ON `c` LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - {"batch on c limit 10 dry run insert into t1 select * from t2 where c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - {"batch on c limit 10 dry run query insert into t1 select * from t2 where c = 10", true, - "BATCH ON `c` LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - {"batch limit 10 insert into t1 select * from t2 where c = 10", true, - "BATCH LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - {"batch limit 10 dry run insert into t1 select * from t2 where c = 10", true, - "BATCH LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - {"batch limit 10 dry run query insert into t1 select * from t2 where c = 10", true, - "BATCH LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, - // inserts on duplicate key update - {"batch on c limit 10 insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH ON `c` LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - {"batch on c limit 10 dry run insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH ON `c` LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - {"batch on c limit 10 dry run query insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH ON `c` LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - {"batch limit 10 insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - {"batch limit 10 dry run insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - {"batch limit 10 dry run query insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, - "BATCH LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, - } - - RunTest(t, cases, false) -} - -func TestIntervalPartition(t *testing.T) { - table := []testCase{ - {"CREATE TABLE t (c1 integer,c2 integer) PARTITION BY RANGE (c1) INTERVAL (1000)", true, "CREATE TABLE `t` (`c1` INT,`c2` INT) PARTITION BY RANGE (`c1`) INTERVAL (1000)"}, - {"CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE (c2) INTERVAL (1 Month)", true, "CREATE TABLE `t` (`c1` INT,`c2` DATE) PARTITION BY RANGE (`c2`) INTERVAL (1 MONTH)"}, - {"CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE (c1) (partition p1 values less than (22))", true, "CREATE TABLE `t` (`c1` INT,`c2` DATE) PARTITION BY RANGE (`c1`) (PARTITION `p1` VALUES LESS THAN (22))"}, - {`CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE COLUMNS (c2) INTERVAL (1 year) first partition less than ("2022-02-01")`, false, ""}, - {`CREATE TABLE t (c1 int, c2 datetime) PARTITION BY RANGE COLUMNS (c2) INTERVAL (1 day) first partition less than ("2022-01-02") last partition less than ("2022-06-01") NULL PARTITION MAXVALUE PARTITION`, true, "CREATE TABLE `t` (`c1` INT,`c2` DATETIME) PARTITION BY RANGE COLUMNS (`c2`) INTERVAL (1 DAY) FIRST PARTITION LESS THAN (_UTF8MB4'2022-01-02') LAST PARTITION LESS THAN (_UTF8MB4'2022-06-01') NULL PARTITION MAXVALUE PARTITION"}, - {`ALTER TABLE t LAST PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` LAST PARTITION LESS THAN (1000)"}, - {`ALTER TABLE t REORGANIZE MAX PARTITION INTO NEW LAST PARTITION LESS THAN (1000)`, false, ""}, - {`ALTER TABLE t REORGANIZE MAX PARTITION INTO LAST PARTITION LESS THAN (1000)`, false, ""}, - {`ALTER TABLE t REORGANIZE MAXVALUE PARTITION INTO NEW LAST PARTITION LESS THAN (1000)`, false, ""}, - {`ALTER TABLE t REORGANIZE MAXVALUE PARTITION INTO LAST PARTITION LESS THAN (1000)`, false, ""}, - {`ALTER TABLE t split MAXVALUE PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` SPLIT MAXVALUE PARTITION LESS THAN (1000)"}, - {`ALTER TABLE t merge first PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` MERGE FIRST PARTITION LESS THAN (1000)"}, - {`ALTER TABLE t first PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` FIRST PARTITION LESS THAN (1000)"}, - } - - RunTest(t, table, false) -} - -func TestTTLTableOption(t *testing.T) { - table := []testCase{ - // create table with various temporal interval - {"create table t (created_at datetime) TTL = created_at + INTERVAL 3.1415 YEAR", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 3.1415 YEAR"}, - {"create table t (created_at datetime) TTL = created_at + INTERVAL '1 1:1:1' DAY_SECOND", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL _UTF8MB4'1 1:1:1' DAY_SECOND"}, - {"create table t (created_at datetime) TTL = created_at + INTERVAL 1 YEAR", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR"}, - {"create table t (created_at datetime) TTL = created_at + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, - {"create table t (created_at datetime) TTL created_at + INTERVAL 1 YEAR TTL_ENABLE 'OFF'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, - {"create table t (created_at datetime) TTL created_at + INTERVAL 1 YEAR TTL_ENABLE 'OFF' TTL_JOB_INTERVAL='8h'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF' TTL_JOB_INTERVAL = '8h'"}, - {"create table t (created_at datetime) /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON'*/", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON'"}, - - // alter table with various temporal interval - {"alter table t TTL = created_at + INTERVAL 1 MONTH", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH"}, - {"alter table t TTL_ENABLE = 'ON'", true, "ALTER TABLE `t` TTL_ENABLE = 'ON'"}, - {"alter table t TTL_ENABLE = 'OFF'", true, "ALTER TABLE `t` TTL_ENABLE = 'OFF'"}, - {"alter table t TTL = created_at + INTERVAL 1 MONTH TTL_ENABLE 'OFF'", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH TTL_ENABLE = 'OFF'"}, - {"alter table t TTL = created_at + INTERVAL 1 MONTH TTL_ENABLE 'OFF' TTL_JOB_INTERVAL '1h'", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH TTL_ENABLE = 'OFF' TTL_JOB_INTERVAL = '1h'"}, - {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON'"}, - {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON' TTL_JOB_INTERVAL='8h'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON' TTL_JOB_INTERVAL = '8h'"}, - {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON' TTL_JOB_INTERVAL='8.645124531235h'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON' TTL_JOB_INTERVAL = '8.645124531235h'"}, - - // alter table to remove ttl settings - {"alter table t remove ttl", true, "ALTER TABLE `t` REMOVE TTL"}, - - // validate invalid TTL_ENABLE settings - {"create table t (created_at datetime) TTL_ENABLE = 'test_case'", false, ""}, - {"create table t (created_at datetime) /*T![ttl] TTL_ENABLE = 'test_case' */", false, ""}, - {"alter table t /*T![ttl] TTL_ENABLE = 'test_case' */", false, ""}, - - // validate invalid TTL_JOB_INTERVAL settings - {"create table t (created_at datetime) TTL_JOB_INTERVAL = '@monthly'", false, ""}, - {"create table t (created_at datetime) TTL_JOB_INTERVAL = '10hourxx'", false, ""}, - {"create table t (created_at datetime) TTL_JOB_INTERVAL = '10.10.255h'", false, ""}, - } - - RunTest(t, table, false) -} - -func TestIssue45898(t *testing.T) { - p := parser.New() - p.ParseSQL("a.") - stmts, _, err := p.ParseSQL("select count(1) from t") - require.NoError(t, err) - var sb strings.Builder - restoreCtx := NewRestoreCtx(DefaultRestoreFlags, &sb) - sb.Reset() - stmts[0].Restore(restoreCtx) - require.Equal(t, "SELECT COUNT(1) FROM `t`", sb.String()) -} - -func TestMultiStmt(t *testing.T) { - p := parser.New() - stmts, _, err := p.Parse("SELECT 'foo'; SELECT 'foo;bar','baz'; select 'foo' , 'bar' , 'baz' ;select 1", "", "") - require.NoError(t, err) - require.Equal(t, len(stmts), 4) - stmt1 := stmts[0].(*ast.SelectStmt) - stmt2 := stmts[1].(*ast.SelectStmt) - stmt3 := stmts[2].(*ast.SelectStmt) - stmt4 := stmts[3].(*ast.SelectStmt) - require.Equal(t, "'foo'", stmt1.Fields.Fields[0].Text()) - require.Equal(t, "'foo;bar'", stmt2.Fields.Fields[0].Text()) - require.Equal(t, "'baz'", stmt2.Fields.Fields[1].Text()) - require.Equal(t, "'foo'", stmt3.Fields.Fields[0].Text()) - require.Equal(t, "'bar'", stmt3.Fields.Fields[1].Text()) - require.Equal(t, "'baz'", stmt3.Fields.Fields[2].Text()) - require.Equal(t, "1", stmt4.Fields.Fields[0].Text()) -} - -// https://dev.mysql.com/doc/refman/8.1/en/other-vendor-data-types.html -func TestCompatTypes(t *testing.T) { - table := []testCase{ - {`CREATE TABLE t(id INT PRIMARY KEY, c1 BOOL)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT(1))"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 BOOLEAN)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT(1))"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 CHARACTER VARYING(0))`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` VARCHAR(0))"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 FIXED)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DECIMAL)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 FLOAT4)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` FLOAT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 FLOAT8)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DOUBLE)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT1)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT2)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` SMALLINT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT3)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMINT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT4)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` INT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT8)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` BIGINT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG VARBINARY)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMBLOB)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG VARCHAR)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMTEXT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMTEXT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 MIDDLEINT)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMINT)"}, - {`CREATE TABLE t(id INT PRIMARY KEY, c1 NUMERIC)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DECIMAL)"}, - } - - RunTest(t, table, false) -} diff --git a/parser/terror/BUILD.bazel b/parser/terror/BUILD.bazel deleted file mode 100644 index eec345033fa08..0000000000000 --- a/parser/terror/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "terror", - srcs = ["terror.go"], - importpath = "github.com/pingcap/tidb/parser/terror", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "terror_test", - timeout = "short", - srcs = ["terror_test.go"], - embed = [":terror"], - flaky = True, - shard_count = 6, - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/terror/terror.go b/parser/terror/terror.go deleted file mode 100644 index 042b7580033ff..0000000000000 --- a/parser/terror/terror.go +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package terror - -import ( - "fmt" - "runtime/debug" - "strconv" - "strings" - "sync" - "sync/atomic" - - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "go.uber.org/zap" -) - -// ErrCode represents a specific error type in a error class. -// Same error code can be used in different error classes. -type ErrCode int - -const ( - // Executor error codes. - - // CodeUnknown is for errors of unknown reason. - CodeUnknown ErrCode = -1 - // CodeExecResultIsEmpty indicates execution result is empty. - CodeExecResultIsEmpty ErrCode = 3 - - // Expression error codes. - - // CodeMissConnectionID indicates connection id is missing. - CodeMissConnectionID ErrCode = 1 - - // Special error codes. - - // CodeResultUndetermined indicates the sql execution result is undetermined. - CodeResultUndetermined ErrCode = 2 -) - -// ErrClass represents a class of errors. -type ErrClass int - -// Error implements error interface. -type Error = errors.Error - -// Error classes. -var ( - ClassAutoid = RegisterErrorClass(1, "autoid") - ClassDDL = RegisterErrorClass(2, "ddl") - ClassDomain = RegisterErrorClass(3, "domain") - ClassEvaluator = RegisterErrorClass(4, "evaluator") - ClassExecutor = RegisterErrorClass(5, "executor") - ClassExpression = RegisterErrorClass(6, "expression") - ClassAdmin = RegisterErrorClass(7, "admin") - ClassKV = RegisterErrorClass(8, "kv") - ClassMeta = RegisterErrorClass(9, "meta") - ClassOptimizer = RegisterErrorClass(10, "planner") - ClassParser = RegisterErrorClass(11, "parser") - ClassPerfSchema = RegisterErrorClass(12, "perfschema") - ClassPrivilege = RegisterErrorClass(13, "privilege") - ClassSchema = RegisterErrorClass(14, "schema") - ClassServer = RegisterErrorClass(15, "server") - ClassStructure = RegisterErrorClass(16, "structure") - ClassVariable = RegisterErrorClass(17, "variable") - ClassXEval = RegisterErrorClass(18, "xeval") - ClassTable = RegisterErrorClass(19, "table") - ClassTypes = RegisterErrorClass(20, "types") - ClassGlobal = RegisterErrorClass(21, "global") - ClassMockTikv = RegisterErrorClass(22, "mocktikv") - ClassJSON = RegisterErrorClass(23, "json") - ClassTiKV = RegisterErrorClass(24, "tikv") - ClassSession = RegisterErrorClass(25, "session") - ClassPlugin = RegisterErrorClass(26, "plugin") - ClassUtil = RegisterErrorClass(27, "util") - // Add more as needed. -) - -var errClass2Desc = make(map[ErrClass]string) -var rfcCode2errClass = newCode2ErrClassMap() - -type code2ErrClassMap struct { - data sync.Map -} - -func newCode2ErrClassMap() *code2ErrClassMap { - return &code2ErrClassMap{ - data: sync.Map{}, - } -} - -func (m *code2ErrClassMap) Get(key string) (ErrClass, bool) { - ret, have := m.data.Load(key) - if !have { - return ErrClass(-1), false - } - return ret.(ErrClass), true -} - -func (m *code2ErrClassMap) Put(key string, err ErrClass) { - m.data.Store(key, err) -} - -var registerFinish uint32 - -// RegisterFinish makes the register of new error panic. -// The use pattern should be register all the errors during initialization, and then call RegisterFinish. -func RegisterFinish() { - atomic.StoreUint32(®isterFinish, 1) -} - -func frozen() bool { - return atomic.LoadUint32(®isterFinish) != 0 -} - -// RegisterErrorClass registers new error class for terror. -func RegisterErrorClass(classCode int, desc string) ErrClass { - errClass := ErrClass(classCode) - if _, exists := errClass2Desc[errClass]; exists { - panic(fmt.Sprintf("duplicate register ClassCode %d - %s", classCode, desc)) - } - errClass2Desc[errClass] = desc - return errClass -} - -// String implements fmt.Stringer interface. -func (ec ErrClass) String() string { - if s, exists := errClass2Desc[ec]; exists { - return s - } - return strconv.Itoa(int(ec)) -} - -// EqualClass returns true if err is *Error with the same class. -func (ec ErrClass) EqualClass(err error) bool { - e := errors.Cause(err) - if e == nil { - return false - } - if te, ok := e.(*Error); ok { - rfcCode := te.RFCCode() - if index := strings.Index(string(rfcCode), ":"); index > 0 { - if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has { - return class == ec - } - } - } - return false -} - -// NotEqualClass returns true if err is not *Error with the same class. -func (ec ErrClass) NotEqualClass(err error) bool { - return !ec.EqualClass(err) -} - -func (ec ErrClass) initError(code ErrCode) string { - if frozen() { - debug.PrintStack() - panic("register error after initialized is prohibited") - } - clsMap, ok := ErrClassToMySQLCodes[ec] - if !ok { - clsMap = make(map[ErrCode]struct{}) - ErrClassToMySQLCodes[ec] = clsMap - } - clsMap[code] = struct{}{} - class := errClass2Desc[ec] - rfcCode := fmt.Sprintf("%s:%d", class, code) - rfcCode2errClass.Put(class, ec) - return rfcCode -} - -// New defines an *Error with an error code and an error message. -// Usually used to create base *Error. -// Attention: -// this method is not goroutine-safe and -// usually be used in global variable initializer -// -// Deprecated: use NewStd or NewStdErr instead. -func (ec ErrClass) New(code ErrCode, message string) *Error { - rfcCode := ec.initError(code) - err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode)) - return err -} - -// NewStdErr defines an *Error with an error code, an error -// message and workaround to create standard error. -func (ec ErrClass) NewStdErr(code ErrCode, message *mysql.ErrMessage) *Error { - rfcCode := ec.initError(code) - err := errors.Normalize( - message.Raw, errors.RedactArgs(message.RedactArgPos), - errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode), - ) - return err -} - -// NewStd calls New using the standard message for the error code -// Attention: -// this method is not goroutine-safe and -// usually be used in global variable initializer -func (ec ErrClass) NewStd(code ErrCode) *Error { - return ec.NewStdErr(code, mysql.MySQLErrName[uint16(code)]) -} - -// Synthesize synthesizes an *Error in the air -// it didn't register error into ErrClassToMySQLCodes -// so it's goroutine-safe -// and often be used to create Error came from other systems like TiKV. -func (ec ErrClass) Synthesize(code ErrCode, message string) *Error { - return errors.Normalize( - message, errors.MySQLErrorCode(int(code)), - errors.RFCCodeText(fmt.Sprintf("%s:%d", errClass2Desc[ec], code)), - ) -} - -// ToSQLError convert Error to mysql.SQLError. -func ToSQLError(e *Error) *mysql.SQLError { - code := getMySQLErrorCode(e) - return mysql.NewErrf(code, "%s", nil, e.GetMsg()) -} - -var defaultMySQLErrorCode uint16 - -func getMySQLErrorCode(e *Error) uint16 { - rfcCode := e.RFCCode() - var class ErrClass - if index := strings.Index(string(rfcCode), ":"); index > 0 { - ec, has := rfcCode2errClass.Get(string(rfcCode)[:index]) - if !has { - log.Warn("Unknown error class", zap.String("class", string(rfcCode)[:index])) - return defaultMySQLErrorCode - } - class = ec - } - codeMap, ok := ErrClassToMySQLCodes[class] - if !ok { - log.Warn("Unknown error class", zap.Int("class", int(class))) - return defaultMySQLErrorCode - } - _, ok = codeMap[ErrCode(e.Code())] - if !ok { - log.Debug("Unknown error code", zap.Int("class", int(class)), zap.Int("code", int(e.Code()))) - return defaultMySQLErrorCode - } - return uint16(e.Code()) -} - -var ( - // ErrClassToMySQLCodes is the map of ErrClass to code-set. - ErrClassToMySQLCodes = make(map[ErrClass]map[ErrCode]struct{}) - // ErrCritical is the critical error class. - ErrCritical = ClassGlobal.NewStdErr(CodeExecResultIsEmpty, mysql.Message("critical error %v", nil)) - // ErrResultUndetermined is the error when execution result is unknown. - ErrResultUndetermined = ClassGlobal.NewStdErr( - CodeResultUndetermined, - mysql.Message("execution result undetermined", nil), - ) -) - -func init() { - defaultMySQLErrorCode = mysql.ErrUnknown -} - -// ErrorEqual returns a boolean indicating whether err1 is equal to err2. -func ErrorEqual(err1, err2 error) bool { - e1 := errors.Cause(err1) - e2 := errors.Cause(err2) - - if e1 == e2 { - return true - } - - if e1 == nil || e2 == nil { - return e1 == e2 - } - - te1, ok1 := e1.(*Error) - te2, ok2 := e2.(*Error) - if ok1 && ok2 { - return te1.RFCCode() == te2.RFCCode() - } - - return e1.Error() == e2.Error() -} - -// ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2. -func ErrorNotEqual(err1, err2 error) bool { - return !ErrorEqual(err1, err2) -} - -// MustNil cleans up and fatals if err is not nil. -func MustNil(err error, closeFuns ...func()) { - if err != nil { - for _, f := range closeFuns { - f() - } - log.Fatal("unexpected error", zap.Error(err), zap.Stack("stack")) - } -} - -// Call executes a function and checks the returned err. -func Call(fn func() error) { - err := fn() - if err != nil { - log.Error("function call errored", zap.Error(err), zap.Stack("stack")) - } -} - -// Log logs the error if it is not nil. -func Log(err error) { - if err != nil { - log.Error("encountered error", zap.Error(err), zap.Stack("stack")) - } -} - -// GetErrClass returns the error class of the error. -func GetErrClass(e *Error) ErrClass { - rfcCode := e.RFCCode() - if index := strings.Index(string(rfcCode), ":"); index > 0 { - if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has { - return class - } - } - return ErrClass(-1) -} diff --git a/parser/test_driver/BUILD.bazel b/parser/test_driver/BUILD.bazel deleted file mode 100644 index 270069665738a..0000000000000 --- a/parser/test_driver/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "test_driver", - srcs = [ - "test_driver.go", - "test_driver_datum.go", - "test_driver_helper.go", - "test_driver_mydecimal.go", - ], - importpath = "github.com/pingcap/tidb/parser/test_driver", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/charset", - "//parser/format", - "//parser/mysql", - "//parser/types", - "@com_github_pingcap_errors//:errors", - ], -) diff --git a/parser/tidb/BUILD.bazel b/parser/tidb/BUILD.bazel deleted file mode 100644 index a5624aef73caa..0000000000000 --- a/parser/tidb/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tidb", - srcs = ["features.go"], - importpath = "github.com/pingcap/tidb/parser/tidb", - visibility = ["//visibility:public"], -) diff --git a/parser/types/BUILD.bazel b/parser/types/BUILD.bazel deleted file mode 100644 index 79d488076faee..0000000000000 --- a/parser/types/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "types", - srcs = [ - "etc.go", - "eval_type.go", - "field_type.go", - ], - importpath = "github.com/pingcap/tidb/parser/types", - visibility = ["//visibility:public"], - deps = [ - "//parser/charset", - "//parser/format", - "//parser/mysql", - "//parser/terror", - "@com_github_cznic_mathutil//:mathutil", - ], -) - -go_test( - name = "types_test", - timeout = "short", - srcs = [ - "etc_test.go", - "field_type_test.go", - ], - embed = [":types"], - flaky = True, - shard_count = 6, - deps = [ - "//parser", - "//parser/ast", - "//parser/charset", - "//parser/mysql", - "//parser/test_driver", - "@com_github_stretchr_testify//require", - ], -) diff --git a/parser/types/etc.go b/parser/types/etc.go deleted file mode 100644 index b482e7ecc8b48..0000000000000 --- a/parser/types/etc.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2014 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -import ( - "strings" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" -) - -// IsTypeBlob returns a boolean indicating whether the tp is a blob type. -func IsTypeBlob(tp byte) bool { - switch tp { - case mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob: - return true - default: - return false - } -} - -// IsTypeChar returns a boolean indicating -// whether the tp is the char type like a string type or a varchar type. -func IsTypeChar(tp byte) bool { - return tp == mysql.TypeString || tp == mysql.TypeVarchar -} - -var type2Str = map[byte]string{ - mysql.TypeBit: "bit", - mysql.TypeBlob: "text", - mysql.TypeDate: "date", - mysql.TypeDatetime: "datetime", - mysql.TypeUnspecified: "unspecified", - mysql.TypeNewDecimal: "decimal", - mysql.TypeDouble: "double", - mysql.TypeEnum: "enum", - mysql.TypeFloat: "float", - mysql.TypeGeometry: "geometry", - mysql.TypeInt24: "mediumint", - mysql.TypeJSON: "json", - mysql.TypeLong: "int", - mysql.TypeLonglong: "bigint", - mysql.TypeLongBlob: "longtext", - mysql.TypeMediumBlob: "mediumtext", - mysql.TypeNull: "null", - mysql.TypeSet: "set", - mysql.TypeShort: "smallint", - mysql.TypeString: "char", - mysql.TypeDuration: "time", - mysql.TypeTimestamp: "timestamp", - mysql.TypeTiny: "tinyint", - mysql.TypeTinyBlob: "tinytext", - mysql.TypeVarchar: "varchar", - mysql.TypeVarString: "var_string", - mysql.TypeYear: "year", -} - -var str2Type = map[string]byte{ - "bit": mysql.TypeBit, - "text": mysql.TypeBlob, - "date": mysql.TypeDate, - "datetime": mysql.TypeDatetime, - "unspecified": mysql.TypeUnspecified, - "decimal": mysql.TypeNewDecimal, - "double": mysql.TypeDouble, - "enum": mysql.TypeEnum, - "float": mysql.TypeFloat, - "geometry": mysql.TypeGeometry, - "mediumint": mysql.TypeInt24, - "json": mysql.TypeJSON, - "int": mysql.TypeLong, - "bigint": mysql.TypeLonglong, - "longtext": mysql.TypeLongBlob, - "mediumtext": mysql.TypeMediumBlob, - "null": mysql.TypeNull, - "set": mysql.TypeSet, - "smallint": mysql.TypeShort, - "char": mysql.TypeString, - "time": mysql.TypeDuration, - "timestamp": mysql.TypeTimestamp, - "tinyint": mysql.TypeTiny, - "tinytext": mysql.TypeTinyBlob, - "varchar": mysql.TypeVarchar, - "var_string": mysql.TypeVarString, - "year": mysql.TypeYear, -} - -// TypeStr converts tp to a string. -func TypeStr(tp byte) (r string) { - return type2Str[tp] -} - -// TypeToStr converts a field to a string. -// It is used for converting Text to Blob, -// or converting Char to Binary. -// Args: -// -// tp: type enum -// cs: charset -func TypeToStr(tp byte, cs string) (r string) { - ts := type2Str[tp] - if cs != "binary" { - return ts - } - if IsTypeBlob(tp) { - ts = strings.Replace(ts, "text", "blob", 1) - } else if IsTypeChar(tp) { - ts = strings.Replace(ts, "char", "binary", 1) - } else if tp == mysql.TypeNull { - ts = "binary" - } - return ts -} - -// StrToType convert a string to type enum. -// Args: -// -// ts: type string -func StrToType(ts string) (tp byte) { - ts = strings.Replace(ts, "blob", "text", 1) - ts = strings.Replace(ts, "binary", "char", 1) - if tp, ok := str2Type[ts]; ok { - return tp - } - - return mysql.TypeUnspecified -} - -var ( - dig2bytes = [10]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4} -) - -// constant values. -const ( - digitsPerWord = 9 // A word holds 9 digits. - wordSize = 4 // A word is 4 bytes int32. -) - -var ( - // ErrInvalidDefault is returned when meet a invalid default value. - ErrInvalidDefault = terror.ClassTypes.NewStd(mysql.ErrInvalidDefault) - // ErrDataOutOfRange is returned when meet a value out of range. - ErrDataOutOfRange = terror.ClassTypes.NewStd(mysql.ErrDataOutOfRange) - // ErrTruncatedWrongValue is returned when meet a value bigger than - // 99999999999999999999999999999999999999999999999999999999999999999 during parsing. - ErrTruncatedWrongValue = terror.ClassTypes.NewStd(mysql.ErrTruncatedWrongValue) - // ErrIllegalValueForType is returned when strconv.ParseFloat meet strconv.ErrRange during parsing. - ErrIllegalValueForType = terror.ClassTypes.NewStd(mysql.ErrIllegalValueForType) -) diff --git a/parser/types/etc_test.go b/parser/types/etc_test.go deleted file mode 100644 index 5ed4269eb887e..0000000000000 --- a/parser/types/etc_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/stretchr/testify/require" -) - -func TestStrToType(t *testing.T) { - for tp, str := range type2Str { - a := StrToType(str) - require.Equal(t, tp, a) - } - - tp := StrToType("blob") - require.Equal(t, tp, mysql.TypeBlob) - - tp = StrToType("binary") - require.Equal(t, tp, mysql.TypeString) -} diff --git a/parser/types/field_type.go b/parser/types/field_type.go deleted file mode 100644 index df14009255ed3..0000000000000 --- a/parser/types/field_type.go +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -import ( - "encoding/json" - "fmt" - "io" - "strings" - "unsafe" - - "github.com/cznic/mathutil" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" -) - -// UnspecifiedLength is unspecified length. -const ( - UnspecifiedLength = -1 -) - -// TiDBStrictIntegerDisplayWidth represent whether return warnings when integerType with (length) was parsed. -// The default is `false`, it will be parsed as warning, and the result in show-create-table will ignore the -// display length when it set to `true`. This is for compatibility with MySQL 8.0 in which integer max display -// length is deprecated, referring this issue #6688 for more details. -var ( - TiDBStrictIntegerDisplayWidth bool -) - -// FieldType records field type information. -type FieldType struct { - // tp is type of the field - tp byte - // flag represent NotNull, Unsigned, PriKey flags etc. - flag uint - // flen represent size of bytes of the field - flen int - // decimal represent decimal length of the field - decimal int - // charset represent character set - charset string - // collate represent collate rules of the charset - collate string - // elems is the element list for enum and set type. - elems []string - elemsIsBinaryLit []bool - array bool - // Please keep in mind that jsonFieldType should be updated if you add a new field here. -} - -// NewFieldType returns a FieldType, -// with a type and other information about field type. -func NewFieldType(tp byte) *FieldType { - return &FieldType{ - tp: tp, - flen: UnspecifiedLength, - decimal: UnspecifiedLength, - } -} - -// IsDecimalValid checks whether the decimal is valid. -func (ft *FieldType) IsDecimalValid() bool { - if ft.GetType() == mysql.TypeNewDecimal && (ft.decimal < 0 || ft.decimal > mysql.MaxDecimalScale || ft.flen <= 0 || ft.flen > mysql.MaxDecimalWidth || ft.flen < ft.decimal) { - return false - } - return true -} - -// IsVarLengthType Determine whether the column type is a variable-length type -func (ft *FieldType) IsVarLengthType() bool { - switch ft.GetType() { - case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeJSON, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - return true - default: - return false - } -} - -// GetType returns the type of the FieldType. -func (ft *FieldType) GetType() byte { - if ft.array { - return mysql.TypeJSON - } - return ft.tp -} - -// GetFlag returns the flag of the FieldType. -func (ft *FieldType) GetFlag() uint { - return ft.flag -} - -// GetFlen returns the length of the field. -func (ft *FieldType) GetFlen() int { - return ft.flen -} - -// GetDecimal returns the decimal of the FieldType. -func (ft *FieldType) GetDecimal() int { - return ft.decimal -} - -// GetCharset returns the field's charset -func (ft *FieldType) GetCharset() string { - return ft.charset -} - -// GetCollate returns the collation of the field. -func (ft *FieldType) GetCollate() string { - return ft.collate -} - -// GetElems returns the elements of the FieldType. -func (ft *FieldType) GetElems() []string { - return ft.elems -} - -// SetType sets the type of the FieldType. -func (ft *FieldType) SetType(tp byte) { - ft.tp = tp - ft.array = false -} - -// SetFlag sets the flag of the FieldType. -func (ft *FieldType) SetFlag(flag uint) { - ft.flag = flag -} - -// AddFlag adds a flag to the FieldType. -func (ft *FieldType) AddFlag(flag uint) { - ft.flag |= flag -} - -// AndFlag and the flag of the FieldType. -func (ft *FieldType) AndFlag(flag uint) { - ft.flag &= flag -} - -// ToggleFlag toggle the flag of the FieldType. -func (ft *FieldType) ToggleFlag(flag uint) { - ft.flag ^= flag -} - -// DelFlag delete the flag of the FieldType. -func (ft *FieldType) DelFlag(flag uint) { - ft.flag &= ^flag -} - -// SetFlen sets the length of the field. -func (ft *FieldType) SetFlen(flen int) { - ft.flen = flen -} - -// SetFlenUnderLimit sets the length of the field to the value of the argument -func (ft *FieldType) SetFlenUnderLimit(flen int) { - if ft.GetType() == mysql.TypeNewDecimal { - ft.flen = mathutil.Min(flen, mysql.MaxDecimalWidth) - } else { - ft.flen = flen - } -} - -// SetDecimal sets the decimal of the FieldType. -func (ft *FieldType) SetDecimal(decimal int) { - ft.decimal = decimal -} - -// SetDecimalUnderLimit sets the decimal of the field to the value of the argument -func (ft *FieldType) SetDecimalUnderLimit(decimal int) { - if ft.GetType() == mysql.TypeNewDecimal { - ft.decimal = mathutil.Min(decimal, mysql.MaxDecimalScale) - } else { - ft.decimal = decimal - } -} - -// UpdateFlenAndDecimalUnderLimit updates the length and decimal to the value of the argument -func (ft *FieldType) UpdateFlenAndDecimalUnderLimit(old *FieldType, deltaDecimal int, deltaFlen int) { - if ft.GetType() != mysql.TypeNewDecimal { - return - } - if old.decimal < 0 { - deltaFlen += mysql.MaxDecimalScale - ft.decimal = mysql.MaxDecimalScale - } else { - ft.SetDecimal(old.decimal + deltaDecimal) - } - if old.flen < 0 { - ft.flen = mysql.MaxDecimalWidth - } else { - ft.SetFlenUnderLimit(old.flen + deltaFlen) - } -} - -// SetCharset sets the charset of the FieldType. -func (ft *FieldType) SetCharset(charset string) { - ft.charset = charset -} - -// SetCollate sets the collation of the FieldType. -func (ft *FieldType) SetCollate(collate string) { - ft.collate = collate -} - -// SetElems sets the elements of the FieldType. -func (ft *FieldType) SetElems(elems []string) { - ft.elems = elems -} - -// SetElem sets the element of the FieldType. -func (ft *FieldType) SetElem(idx int, element string) { - ft.elems[idx] = element -} - -// SetArray sets the array field of the FieldType. -func (ft *FieldType) SetArray(array bool) { - ft.array = array -} - -// IsArray return true if the filed type is array. -func (ft *FieldType) IsArray() bool { - return ft.array -} - -// ArrayType return the type of the array. -func (ft *FieldType) ArrayType() *FieldType { - if !ft.array { - return ft - } - clone := ft.Clone() - clone.SetArray(false) - return clone -} - -// SetElemWithIsBinaryLit sets the element of the FieldType. -func (ft *FieldType) SetElemWithIsBinaryLit(idx int, element string, isBinaryLit bool) { - ft.elems[idx] = element - if isBinaryLit { - // Create the binary literal flags lazily. - if ft.elemsIsBinaryLit == nil { - ft.elemsIsBinaryLit = make([]bool, len(ft.elems)) - } - ft.elemsIsBinaryLit[idx] = true - } -} - -// GetElem returns the element of the FieldType. -func (ft *FieldType) GetElem(idx int) string { - return ft.elems[idx] -} - -// GetElemIsBinaryLit returns the binary literal flag of the element at index idx. -func (ft *FieldType) GetElemIsBinaryLit(idx int) bool { - if len(ft.elemsIsBinaryLit) == 0 { - return false - } - return ft.elemsIsBinaryLit[idx] -} - -// CleanElemIsBinaryLit cleans the binary literal flag of the element at index idx. -func (ft *FieldType) CleanElemIsBinaryLit() { - if ft != nil && ft.elemsIsBinaryLit != nil { - ft.elemsIsBinaryLit = nil - } -} - -// Clone returns a copy of itself. -func (ft *FieldType) Clone() *FieldType { - ret := *ft - return &ret -} - -// Equal checks whether two FieldType objects are equal. -func (ft *FieldType) Equal(other *FieldType) bool { - // We do not need to compare whole `ft.flag == other.flag` when wrapping cast upon an Expression. - // but need compare unsigned_flag of ft.flag. - // When tp is float or double with decimal unspecified, do not check whether flen is equal, - // because flen for them is useless. - // The decimal field can be ignored if the type is int or string. - tpEqual := (ft.GetType() == other.GetType()) || (ft.GetType() == mysql.TypeVarchar && other.GetType() == mysql.TypeVarString) || (ft.GetType() == mysql.TypeVarString && other.GetType() == mysql.TypeVarchar) - flenEqual := ft.flen == other.flen || (ft.EvalType() == ETReal && ft.decimal == UnspecifiedLength) - ignoreDecimal := ft.EvalType() == ETInt || ft.EvalType() == ETString - partialEqual := tpEqual && - (ignoreDecimal || ft.decimal == other.decimal) && - ft.charset == other.charset && - ft.collate == other.collate && - flenEqual && - mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag) - if !partialEqual || len(ft.elems) != len(other.elems) { - return false - } - for i := range ft.elems { - if ft.elems[i] != other.elems[i] { - return false - } - } - return true -} - -// PartialEqual checks whether two FieldType objects are equal. -// If unsafe is true and the objects is string type, PartialEqual will ignore flen. -// See https://github.com/pingcap/tidb/issues/35490#issuecomment-1211658886 for more detail. -func (ft *FieldType) PartialEqual(other *FieldType, unsafe bool) bool { - if !unsafe || ft.EvalType() != ETString || other.EvalType() != ETString { - return ft.Equal(other) - } - - partialEqual := ft.charset == other.charset && ft.collate == other.collate && mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag) - if !partialEqual || len(ft.elems) != len(other.elems) { - return false - } - for i := range ft.elems { - if ft.elems[i] != other.elems[i] { - return false - } - } - return true -} - -// EvalType gets the type in evaluation. -func (ft *FieldType) EvalType() EvalType { - switch ft.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, - mysql.TypeBit, mysql.TypeYear: - return ETInt - case mysql.TypeFloat, mysql.TypeDouble: - return ETReal - case mysql.TypeNewDecimal: - return ETDecimal - case mysql.TypeDate, mysql.TypeDatetime: - return ETDatetime - case mysql.TypeTimestamp: - return ETTimestamp - case mysql.TypeDuration: - return ETDuration - case mysql.TypeJSON: - return ETJson - case mysql.TypeEnum, mysql.TypeSet: - if ft.flag&mysql.EnumSetAsIntFlag > 0 { - return ETInt - } - } - return ETString -} - -// Hybrid checks whether a type is a hybrid type, which can represent different types of value in specific context. -func (ft *FieldType) Hybrid() bool { - return ft.GetType() == mysql.TypeEnum || ft.GetType() == mysql.TypeBit || ft.GetType() == mysql.TypeSet -} - -// Init initializes the FieldType data. -func (ft *FieldType) Init(tp byte) { - ft.tp = tp - ft.flen = UnspecifiedLength - ft.decimal = UnspecifiedLength -} - -// CompactStr only considers tp/CharsetBin/flen/Deimal. -// This is used for showing column type in infoschema. -func (ft *FieldType) CompactStr() string { - ts := TypeToStr(ft.GetType(), ft.charset) - suffix := "" - - defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimal(ft.GetType()) - isDecimalNotDefault := ft.decimal != defaultDecimal && ft.decimal != 0 && ft.decimal != UnspecifiedLength - - // displayFlen and displayDecimal are flen and decimal values with `-1` substituted with default value. - displayFlen, displayDecimal := ft.flen, ft.decimal - if displayFlen == UnspecifiedLength { - displayFlen = defaultFlen - } - if displayDecimal == UnspecifiedLength { - displayDecimal = defaultDecimal - } - - switch ft.GetType() { - case mysql.TypeEnum, mysql.TypeSet: - // Format is ENUM ('e1', 'e2') or SET ('e1', 'e2') - es := make([]string, 0, len(ft.elems)) - for _, e := range ft.elems { - e = format.OutputFormat(e) - es = append(es, e) - } - suffix = fmt.Sprintf("('%s')", strings.Join(es, "','")) - case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: - if isDecimalNotDefault { - suffix = fmt.Sprintf("(%d)", displayDecimal) - } - case mysql.TypeDouble, mysql.TypeFloat: - // 1. flen Not Default, decimal Not Default -> Valid - // 2. flen Not Default, decimal Default (-1) -> Invalid - // 3. flen Default, decimal Not Default -> Valid - // 4. flen Default, decimal Default -> Valid (hide) - if isDecimalNotDefault { - suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) - } - case mysql.TypeNewDecimal: - suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) - case mysql.TypeBit, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString: - suffix = fmt.Sprintf("(%d)", displayFlen) - case mysql.TypeTiny: - // With display length deprecation active tinyint(1) still has - // a display length to indicate this might have been a BOOL. - // Connectors expect this. - // - // See also: - // https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-19.html - if !TiDBStrictIntegerDisplayWidth || (mysql.HasZerofillFlag(ft.flag) || displayFlen == 1) { - suffix = fmt.Sprintf("(%d)", displayFlen) - } - case mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - // Referring this issue #6688, the integer max display length is deprecated in MySQL 8.0. - // Since the length doesn't take any effect in TiDB storage or showing result, we remove it here. - if !TiDBStrictIntegerDisplayWidth || mysql.HasZerofillFlag(ft.flag) { - suffix = fmt.Sprintf("(%d)", displayFlen) - } - case mysql.TypeYear: - suffix = fmt.Sprintf("(%d)", ft.flen) - case mysql.TypeNull: - suffix = "(0)" - } - return ts + suffix -} - -// InfoSchemaStr joins the CompactStr with unsigned flag and -// returns a string. -func (ft *FieldType) InfoSchemaStr() string { - suffix := "" - if mysql.HasUnsignedFlag(ft.flag) && - ft.GetType() != mysql.TypeBit && - ft.GetType() != mysql.TypeYear { - suffix = " unsigned" - } - return ft.CompactStr() + suffix -} - -// String joins the information of FieldType and returns a string. -// Note: when flen or decimal is unspecified, this function will use the default value instead of -1. -func (ft *FieldType) String() string { - strs := []string{ft.CompactStr()} - if mysql.HasUnsignedFlag(ft.flag) { - strs = append(strs, "UNSIGNED") - } - if mysql.HasZerofillFlag(ft.flag) { - strs = append(strs, "ZEROFILL") - } - if mysql.HasBinaryFlag(ft.flag) && ft.GetType() != mysql.TypeString { - strs = append(strs, "BINARY") - } - - if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) { - if ft.charset != "" && ft.charset != charset.CharsetBin { - strs = append(strs, fmt.Sprintf("CHARACTER SET %s", ft.charset)) - } - if ft.collate != "" && ft.collate != charset.CharsetBin { - strs = append(strs, fmt.Sprintf("COLLATE %s", ft.collate)) - } - } - - return strings.Join(strs, " ") -} - -// Restore implements Node interface. -func (ft *FieldType) Restore(ctx *format.RestoreCtx) error { - ctx.WriteKeyWord(TypeToStr(ft.GetType(), ft.charset)) - - precision := UnspecifiedLength - scale := UnspecifiedLength - - switch ft.GetType() { - case mysql.TypeEnum, mysql.TypeSet: - ctx.WritePlain("(") - for i, e := range ft.elems { - if i != 0 { - ctx.WritePlain(",") - } - ctx.WriteString(e) - } - ctx.WritePlain(")") - case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: - precision = ft.decimal - case mysql.TypeUnspecified, mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: - precision = ft.flen - scale = ft.decimal - default: - precision = ft.flen - } - - if precision != UnspecifiedLength { - ctx.WritePlainf("(%d", precision) - if scale != UnspecifiedLength { - ctx.WritePlainf(",%d", scale) - } - ctx.WritePlain(")") - } - - if mysql.HasUnsignedFlag(ft.flag) { - ctx.WriteKeyWord(" UNSIGNED") - } - if mysql.HasZerofillFlag(ft.flag) { - ctx.WriteKeyWord(" ZEROFILL") - } - if mysql.HasBinaryFlag(ft.flag) && ft.charset != charset.CharsetBin { - ctx.WriteKeyWord(" BINARY") - } - - if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) { - if ft.charset != "" && ft.charset != charset.CharsetBin { - ctx.WriteKeyWord(" CHARACTER SET " + ft.charset) - } - if ft.collate != "" && ft.collate != charset.CharsetBin { - ctx.WriteKeyWord(" COLLATE ") - ctx.WritePlain(ft.collate) - } - } - - return nil -} - -// RestoreAsCastType is used for write AST back to string. -func (ft *FieldType) RestoreAsCastType(ctx *format.RestoreCtx, explicitCharset bool) { - switch ft.tp { - case mysql.TypeVarString, mysql.TypeString: - skipWriteBinary := false - if ft.charset == charset.CharsetBin && ft.collate == charset.CollationBin { - ctx.WriteKeyWord("BINARY") - skipWriteBinary = true - } else { - ctx.WriteKeyWord("CHAR") - } - if ft.flen != UnspecifiedLength { - ctx.WritePlainf("(%d)", ft.flen) - } - if !explicitCharset { - break - } - if !skipWriteBinary && ft.flag&mysql.BinaryFlag != 0 { - ctx.WriteKeyWord(" BINARY") - } - if ft.charset != charset.CharsetBin && ft.charset != mysql.DefaultCharset { - ctx.WriteKeyWord(" CHARSET ") - ctx.WriteKeyWord(ft.charset) - } - case mysql.TypeDate: - ctx.WriteKeyWord("DATE") - case mysql.TypeDatetime: - ctx.WriteKeyWord("DATETIME") - if ft.decimal > 0 { - ctx.WritePlainf("(%d)", ft.decimal) - } - case mysql.TypeNewDecimal: - ctx.WriteKeyWord("DECIMAL") - if ft.flen > 0 && ft.decimal > 0 { - ctx.WritePlainf("(%d, %d)", ft.flen, ft.decimal) - } else if ft.flen > 0 { - ctx.WritePlainf("(%d)", ft.flen) - } - case mysql.TypeDuration: - ctx.WriteKeyWord("TIME") - if ft.decimal > 0 { - ctx.WritePlainf("(%d)", ft.decimal) - } - case mysql.TypeLonglong: - if ft.flag&mysql.UnsignedFlag != 0 { - ctx.WriteKeyWord("UNSIGNED") - } else { - ctx.WriteKeyWord("SIGNED") - } - case mysql.TypeJSON: - ctx.WriteKeyWord("JSON") - case mysql.TypeDouble: - ctx.WriteKeyWord("DOUBLE") - case mysql.TypeFloat: - ctx.WriteKeyWord("FLOAT") - case mysql.TypeYear: - ctx.WriteKeyWord("YEAR") - } - if ft.array { - ctx.WritePlain(" ") - ctx.WriteKeyWord("ARRAY") - } -} - -// FormatAsCastType is used for write AST back to string. -func (ft *FieldType) FormatAsCastType(w io.Writer, explicitCharset bool) { - var sb strings.Builder - restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) - ft.RestoreAsCastType(restoreCtx, explicitCharset) - fmt.Fprint(w, sb.String()) -} - -// VarStorageLen indicates this column is a variable length column. -const VarStorageLen = -1 - -// StorageLength is the length of stored value for the type. -func (ft *FieldType) StorageLength() int { - switch ft.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, - mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeYear, mysql.TypeDuration, - mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeEnum, mysql.TypeSet, - mysql.TypeBit: - // This may not be the accurate length, because we may encode them as varint. - return 8 - case mysql.TypeNewDecimal: - precision, frac := ft.flen-ft.decimal, ft.decimal - return precision/digitsPerWord*wordSize + dig2bytes[precision%digitsPerWord] + frac/digitsPerWord*wordSize + dig2bytes[frac%digitsPerWord] - default: - return VarStorageLen - } -} - -// HasCharset indicates if a COLUMN has an associated charset. Returning false here prevents some information -// statements(like `SHOW CREATE TABLE`) from attaching a CHARACTER SET clause to the column. -func HasCharset(ft *FieldType) bool { - switch ft.GetType() { - case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, - mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - return !mysql.HasBinaryFlag(ft.flag) - case mysql.TypeEnum, mysql.TypeSet: - return true - } - return false -} - -// for json -type jsonFieldType struct { - Tp byte - Flag uint - Flen int - Decimal int - Charset string - Collate string - Elems []string - ElemsIsBinaryLit []bool - Array bool -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -func (ft *FieldType) UnmarshalJSON(data []byte) error { - var r jsonFieldType - err := json.Unmarshal(data, &r) - if err == nil { - ft.tp = r.Tp - ft.flag = r.Flag - ft.flen = r.Flen - ft.decimal = r.Decimal - ft.charset = r.Charset - ft.collate = r.Collate - ft.elems = r.Elems - ft.elemsIsBinaryLit = r.ElemsIsBinaryLit - ft.array = r.Array - } - return err -} - -// MarshalJSON marshals the FieldType to JSON. -func (ft *FieldType) MarshalJSON() ([]byte, error) { - var r jsonFieldType - r.Tp = ft.tp - r.Flag = ft.flag - r.Flen = ft.flen - r.Decimal = ft.decimal - r.Charset = ft.charset - r.Collate = ft.collate - r.Elems = ft.elems - r.ElemsIsBinaryLit = ft.elemsIsBinaryLit - r.Array = ft.array - return json.Marshal(r) -} - -const emptyFieldTypeSize = int64(unsafe.Sizeof(FieldType{})) - -// MemoryUsage return the memory usage of FieldType -func (ft *FieldType) MemoryUsage() (sum int64) { - if ft == nil { - return - } - sum = emptyFieldTypeSize + int64(len(ft.charset)+len(ft.collate)) + int64(cap(ft.elems))*int64(unsafe.Sizeof(*new(string))) + - int64(cap(ft.elemsIsBinaryLit))*int64(unsafe.Sizeof(*new(bool))) - - for _, s := range ft.elems { - sum += int64(len(s)) - } - return -} diff --git a/parser/types/field_type_test.go b/parser/types/field_type_test.go deleted file mode 100644 index 28c301f65af56..0000000000000 --- a/parser/types/field_type_test.go +++ /dev/null @@ -1,331 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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, -// See the License for the specific language governing permissions and -// limitations under the License. - -package types_test - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - // import parser_driver - _ "github.com/pingcap/tidb/parser/test_driver" - . "github.com/pingcap/tidb/parser/types" - "github.com/stretchr/testify/require" -) - -func TestFieldType(t *testing.T) { - ft := NewFieldType(mysql.TypeDuration) - require.Equal(t, UnspecifiedLength, ft.GetFlen()) - require.Equal(t, UnspecifiedLength, ft.GetDecimal()) - ft.SetDecimal(5) - require.Equal(t, "time(5)", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeLong) - ft.SetFlen(5) - ft.SetFlag(mysql.UnsignedFlag | mysql.ZerofillFlag) - require.Equal(t, "int(5) UNSIGNED ZEROFILL", ft.String()) - require.Equal(t, "int(5) unsigned", ft.InfoSchemaStr()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(12) // Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "float(12,3)", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(12) // Default - ft.SetDecimal(-1) // Default - require.Equal(t, "float", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(5) // Not Default - ft.SetDecimal(-1) // Default - require.Equal(t, "float", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(7) // Not Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "float(7,3)", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(22) // Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "double(22,3)", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(22) // Default - ft.SetDecimal(-1) // Default - require.Equal(t, "double", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(5) // Not Default - ft.SetDecimal(-1) // Default - require.Equal(t, "double", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(7) // Not Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "double(7,3)", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeBlob) - ft.SetFlen(10) - ft.SetCharset("UTF8") - ft.SetCollate("UTF8_UNICODE_GI") - require.Equal(t, "text CHARACTER SET UTF8 COLLATE UTF8_UNICODE_GI", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeVarchar) - ft.SetFlen(10) - ft.AddFlag(mysql.BinaryFlag) - require.Equal(t, "varchar(10) BINARY", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeString) - ft.SetCharset(charset.CharsetBin) - ft.AddFlag(mysql.BinaryFlag) - require.Equal(t, "binary(1)", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a", "b"}) - require.Equal(t, "enum('a','b')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"'a'", "'b'"}) - require.Equal(t, "enum('''a''','''b''')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a\nb", "a\tb", "a\rb"}) - require.Equal(t, "enum('a\\nb','a\tb','a\\rb')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) - require.Equal(t, "enum('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a", "b"}) - require.Equal(t, "set('a','b')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"'a'", "'b'"}) - require.Equal(t, "set('''a''','''b''')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) - require.Equal(t, "set('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a'\nb", "a'b\tc"}) - require.Equal(t, "set('a''\\nb','a''b c')", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeTimestamp) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "timestamp(2)", ft.String()) - require.False(t, HasCharset(ft)) - ft = NewFieldType(mysql.TypeTimestamp) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "timestamp", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeDatetime) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "datetime(2)", ft.String()) - require.False(t, HasCharset(ft)) - ft = NewFieldType(mysql.TypeDatetime) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "datetime", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeDate) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "date", ft.String()) - require.False(t, HasCharset(ft)) - ft = NewFieldType(mysql.TypeDate) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "date", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeYear) - ft.SetFlen(4) - ft.SetDecimal(0) - require.Equal(t, "year(4)", ft.String()) - require.False(t, HasCharset(ft)) - ft = NewFieldType(mysql.TypeYear) - ft.SetFlen(2) - ft.SetDecimal(2) - require.Equal(t, "year(2)", ft.String()) - require.False(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeVarchar) - ft.SetFlen(0) - ft.SetDecimal(0) - require.Equal(t, "varchar(0)", ft.String()) - require.True(t, HasCharset(ft)) - - ft = NewFieldType(mysql.TypeString) - ft.SetFlen(0) - ft.SetDecimal(0) - require.Equal(t, "char(0)", ft.String()) - require.True(t, HasCharset(ft)) -} - -func TestHasCharsetFromStmt(t *testing.T) { - template := "CREATE TABLE t(a %s)" - - types := []struct { - strType string - hasCharset bool - }{ - {"int", false}, - {"real", false}, - {"float", false}, - {"bit", false}, - {"bool", false}, - {"char(1)", true}, - {"national char(1)", true}, - {"binary", false}, - {"varchar(1)", true}, - {"national varchar(1)", true}, - {"varbinary(1)", false}, - {"year", false}, - {"date", false}, - {"time", false}, - {"datetime", false}, - {"timestamp", false}, - {"blob", false}, - {"tinyblob", false}, - {"mediumblob", false}, - {"longblob", false}, - {"bit", false}, - {"text", true}, - {"tinytext", true}, - {"mediumtext", true}, - {"longtext", true}, - {"json", false}, - {"enum('1')", true}, - {"set('1')", true}, - } - - p := parser.New() - for _, typ := range types { - sql := fmt.Sprintf(template, typ.strType) - stmt, err := p.ParseOneStmt(sql, "", "") - require.NoError(t, err) - - col := stmt.(*ast.CreateTableStmt).Cols[0] - require.Equal(t, typ.hasCharset, HasCharset(col.Tp)) - } -} - -func TestEnumSetFlen(t *testing.T) { - p := parser.New() - cases := []struct { - sql string - ex int - }{ - {"enum('a')", 1}, - {"enum('a', 'b')", 1}, - {"enum('a', 'bb')", 2}, - {"enum('a', 'b', 'c')", 1}, - {"enum('a', 'bb', 'c')", 2}, - {"enum('a', 'bb', 'c')", 2}, - {"enum('')", 0}, - {"enum('a', '')", 1}, - {"set('a')", 1}, - {"set('a', 'b')", 3}, - {"set('a', 'bb')", 4}, - {"set('a', 'b', 'c')", 5}, - {"set('a', 'bb', 'c')", 6}, - {"set('')", 0}, - {"set('a', '')", 2}, - } - - for _, ca := range cases { - stmt, err := p.ParseOneStmt(fmt.Sprintf("create table t (e %v)", ca.sql), "", "") - require.NoError(t, err) - col := stmt.(*ast.CreateTableStmt).Cols[0] - require.Equal(t, ca.ex, col.Tp.GetFlen()) - } -} - -func TestFieldTypeEqual(t *testing.T) { - // tp not equal - ft1 := NewFieldType(mysql.TypeDouble) - ft2 := NewFieldType(mysql.TypeFloat) - require.Equal(t, false, ft1.Equal(ft2)) - - // decimal not equal - ft2 = NewFieldType(mysql.TypeDouble) - ft2.SetDecimal(5) - require.Equal(t, false, ft1.Equal(ft2)) - - // flen not equal and decimal not -1 - ft1.SetDecimal(5) - ft1.SetFlen(22) - require.Equal(t, false, ft1.Equal(ft2)) - - // flen equal - ft2.SetFlen(22) - require.Equal(t, true, ft1.Equal(ft2)) - - // decimal is -1 - ft1.SetDecimal(-1) - ft2.SetDecimal(-1) - ft1.SetFlen(23) - require.Equal(t, true, ft1.Equal(ft2)) -} - -func TestCompactStr(t *testing.T) { - cases := []struct { - t byte // Field Type - flen int // Field Length - flags uint // Field Flags, e.g. ZEROFILL - e1 string // Expected string with TiDBStrictIntegerDisplayWidth disabled - e2 string // Expected string with TiDBStrictIntegerDisplayWidth enabled - }{ - // TINYINT(1) is considered a bool by connectors, this should always display - // the display length. - {mysql.TypeTiny, 1, 0, `tinyint(1)`, `tinyint(1)`}, - {mysql.TypeTiny, 2, 0, `tinyint(2)`, `tinyint`}, - - // If the ZEROFILL flag is set the display length should not be hidden. - {mysql.TypeLong, 10, 0, `int(10)`, `int`}, - {mysql.TypeLong, 10, mysql.ZerofillFlag, `int(10)`, `int(10)`}, - } - for _, cc := range cases { - ft := NewFieldType(cc.t) - ft.SetFlen(cc.flen) - ft.SetFlag(cc.flags) - - TiDBStrictIntegerDisplayWidth = false - require.Equal(t, cc.e1, ft.CompactStr()) - - TiDBStrictIntegerDisplayWidth = true - require.Equal(t, cc.e2, ft.CompactStr()) - } -} diff --git a/pkg/autoid_service/BUILD.bazel b/pkg/autoid_service/BUILD.bazel new file mode 100644 index 0000000000000..012e1bd92bca2 --- /dev/null +++ b/pkg/autoid_service/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "autoid_service", + srcs = ["autoid.go"], + importpath = "github.com/pingcap/tidb/pkg/autoid_service", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/owner", + "//pkg/parser/model", + "//pkg/util/etcd", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/autoid", + "@io_etcd_go_etcd_client_v3//:client", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//keepalive", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "autoid_service_test", + timeout = "short", + srcs = ["autoid_test.go"], + embed = [":autoid_service"], + flaky = True, + deps = [ + "//pkg/parser/model", + "//pkg/testkit", + "@com_github_pingcap_kvproto//pkg/autoid", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + ], +) diff --git a/autoid_service/OWNERS b/pkg/autoid_service/OWNERS similarity index 100% rename from autoid_service/OWNERS rename to pkg/autoid_service/OWNERS diff --git a/pkg/autoid_service/autoid.go b/pkg/autoid_service/autoid.go new file mode 100644 index 0000000000000..e2ee96ee915d2 --- /dev/null +++ b/pkg/autoid_service/autoid.go @@ -0,0 +1,570 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package autoid + +import ( + "context" + "crypto/tls" + "math" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/autoid" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + autoid1 "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" +) + +var ( + errAutoincReadFailed = errors.New("auto increment action failed") +) + +const ( + autoIDLeaderPath = "tidb/autoid/leader" +) + +type autoIDKey struct { + dbID int64 + tblID int64 +} + +type autoIDValue struct { + sync.Mutex + base int64 + end int64 + isUnsigned bool + token chan struct{} +} + +func (alloc *autoIDValue) alloc4Unsigned(ctx context.Context, store kv.Storage, dbID, tblID int64, isUnsigned bool, + n uint64, increment, offset int64) (min int64, max int64, err error) { + // Check offset rebase if necessary. + if uint64(offset-1) > uint64(alloc.base) { + if err := alloc.rebase4Unsigned(ctx, store, dbID, tblID, uint64(offset-1)); err != nil { + return 0, 0, err + } + } + // calcNeededBatchSize calculates the total batch size needed. + n1 := calcNeededBatchSize(alloc.base, int64(n), increment, offset, isUnsigned) + + // The local rest is not enough for alloc, skip it. + if uint64(alloc.base)+uint64(n1) > uint64(alloc.end) || alloc.base == 0 { + var newBase, newEnd int64 + nextStep := int64(batch) + // Although it may skip a segment here, we still treat it as consumed. + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) + var err1 error + newBase, err1 = idAcc.Get() + if err1 != nil { + return err1 + } + // calcNeededBatchSize calculates the total batch size needed on new base. + n1 = calcNeededBatchSize(newBase, int64(n), increment, offset, isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } + tmpStep := int64(mathutil.Min(math.MaxUint64-uint64(newBase), uint64(nextStep))) + // The global rest is not enough for alloc. + if tmpStep < n1 { + return errAutoincReadFailed + } + newEnd, err1 = idAcc.Inc(tmpStep) + return err1 + }) + if err != nil { + return 0, 0, err + } + if uint64(newBase) == math.MaxUint64 { + return 0, 0, errAutoincReadFailed + } + alloc.base, alloc.end = newBase, newEnd + } + min = alloc.base + // Use uint64 n directly. + alloc.base = int64(uint64(alloc.base) + uint64(n1)) + return min, alloc.base, nil +} + +func (alloc *autoIDValue) alloc4Signed(ctx context.Context, + store kv.Storage, + dbID, tblID int64, + isUnsigned bool, + n uint64, increment, offset int64) (min int64, max int64, err error) { + // Check offset rebase if necessary. + if offset-1 > alloc.base { + if err := alloc.rebase4Signed(ctx, store, dbID, tblID, offset-1); err != nil { + return 0, 0, err + } + } + // calcNeededBatchSize calculates the total batch size needed. + n1 := calcNeededBatchSize(alloc.base, int64(n), increment, offset, isUnsigned) + + // Condition alloc.base+N1 > alloc.end will overflow when alloc.base + N1 > MaxInt64. So need this. + if math.MaxInt64-alloc.base <= n1 { + return 0, 0, errAutoincReadFailed + } + + // The local rest is not enough for allocN, skip it. + // If alloc.base is 0, the alloc may not be initialized, force fetch from remote. + if alloc.base+n1 > alloc.end || alloc.base == 0 { + var newBase, newEnd int64 + nextStep := int64(batch) + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) + var err1 error + newBase, err1 = idAcc.Get() + if err1 != nil { + return err1 + } + // calcNeededBatchSize calculates the total batch size needed on global base. + n1 = calcNeededBatchSize(newBase, int64(n), increment, offset, isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } + tmpStep := mathutil.Min(math.MaxInt64-newBase, nextStep) + // The global rest is not enough for alloc. + if tmpStep < n1 { + return errAutoincReadFailed + } + newEnd, err1 = idAcc.Inc(tmpStep) + return err1 + }) + if err != nil { + return 0, 0, err + } + if newBase == math.MaxInt64 { + return 0, 0, errAutoincReadFailed + } + alloc.base, alloc.end = newBase, newEnd + } + min = alloc.base + alloc.base += n1 + return min, alloc.base, nil +} + +func (alloc *autoIDValue) rebase4Unsigned(ctx context.Context, + store kv.Storage, + dbID, tblID int64, + requiredBase uint64) error { + // Satisfied by alloc.base, nothing to do. + if requiredBase <= uint64(alloc.base) { + return nil + } + // Satisfied by alloc.end, need to update alloc.base. + if requiredBase <= uint64(alloc.end) { + alloc.base = int64(requiredBase) + return nil + } + + var newBase, newEnd uint64 + var oldValue int64 + startTime := time.Now() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + oldValue = currentEnd + uCurrentEnd := uint64(currentEnd) + newBase = mathutil.Max(uCurrentEnd, requiredBase) + newEnd = mathutil.Min(math.MaxUint64-uint64(batch), newBase) + uint64(batch) + _, err1 = idAcc.Inc(int64(newEnd - uCurrentEnd)) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + + logutil.BgLogger().Info("rebase4Unsigned from", + zap.String("category", "autoid service"), + zap.Int64("dbID", dbID), + zap.Int64("tblID", tblID), + zap.Int64("from", oldValue), + zap.Uint64("to", newEnd)) + alloc.base, alloc.end = int64(newBase), int64(newEnd) + return nil +} + +func (alloc *autoIDValue) rebase4Signed(ctx context.Context, store kv.Storage, dbID, tblID int64, requiredBase int64) error { + // Satisfied by alloc.base, nothing to do. + if requiredBase <= alloc.base { + return nil + } + // Satisfied by alloc.end, need to update alloc.base. + if requiredBase <= alloc.end { + alloc.base = requiredBase + return nil + } + + var oldValue, newBase, newEnd int64 + startTime := time.Now() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + oldValue = currentEnd + newBase = mathutil.Max(currentEnd, requiredBase) + newEnd = mathutil.Min(math.MaxInt64-batch, newBase) + batch + _, err1 = idAcc.Inc(newEnd - currentEnd) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + + logutil.BgLogger().Info("rebase4Signed from", + zap.Int64("dbID", dbID), + zap.Int64("tblID", tblID), + zap.Int64("from", oldValue), + zap.Int64("to", newEnd), + zap.String("category", "autoid service")) + alloc.base, alloc.end = newBase, newEnd + return nil +} + +// Service implement the grpc AutoIDAlloc service, defined in kvproto/pkg/autoid. +type Service struct { + autoIDLock sync.Mutex + autoIDMap map[autoIDKey]*autoIDValue + + leaderShip owner.Manager + store kv.Storage +} + +// New return a Service instance. +func New(selfAddr string, etcdAddr []string, store kv.Storage, tlsConfig *tls.Config) *Service { + cfg := config.GetGlobalConfig() + etcdLogCfg := zap.NewProductionConfig() + + cli, err := clientv3.New(clientv3.Config{ + LogConfig: &etcdLogCfg, + Endpoints: etcdAddr, + AutoSyncInterval: 30 * time.Second, + DialTimeout: 5 * time.Second, + DialOptions: []grpc.DialOption{ + grpc.WithBackoffMaxDelay(time.Second * 3), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: time.Duration(cfg.TiKVClient.GrpcKeepAliveTime) * time.Second, + Timeout: time.Duration(cfg.TiKVClient.GrpcKeepAliveTimeout) * time.Second, + }), + }, + TLS: tlsConfig, + }) + if store.GetCodec().GetKeyspace() != nil { + etcd.SetEtcdCliByNamespace(cli, keyspace.MakeKeyspaceEtcdNamespaceSlash(store.GetCodec())) + } + if err != nil { + panic(err) + } + return newWithCli(selfAddr, cli, store) +} + +func newWithCli(selfAddr string, cli *clientv3.Client, store kv.Storage) *Service { + l := owner.NewOwnerManager(context.Background(), cli, "autoid", selfAddr, autoIDLeaderPath) + l.SetBeOwnerHook(func() { + logutil.BgLogger().Info("leader change of autoid service, this node become owner", + zap.String("addr", selfAddr), + zap.String("category", "autoid service")) + }) + // 10 means that autoid service's etcd lease is 10s. + err := l.CampaignOwner(10) + if err != nil { + panic(err) + } + + return &Service{ + autoIDMap: make(map[autoIDKey]*autoIDValue), + leaderShip: l, + store: store, + } +} + +type mockClient struct { + Service +} + +func (m *mockClient) AllocAutoID(ctx context.Context, in *autoid.AutoIDRequest, _ ...grpc.CallOption) (*autoid.AutoIDResponse, error) { + return m.Service.AllocAutoID(ctx, in) +} + +func (m *mockClient) Rebase(ctx context.Context, in *autoid.RebaseRequest, _ ...grpc.CallOption) (*autoid.RebaseResponse, error) { + return m.Service.Rebase(ctx, in) +} + +var global = make(map[string]*mockClient) + +// MockForTest is used for testing, the UT test and unistore use this. +func MockForTest(store kv.Storage) autoid.AutoIDAllocClient { + uuid := store.UUID() + ret, ok := global[uuid] + if !ok { + ret = &mockClient{ + Service{ + autoIDMap: make(map[autoIDKey]*autoIDValue), + leaderShip: nil, + store: store, + }, + } + global[uuid] = ret + } + return ret +} + +// Close closes the Service and clean up resource. +func (s *Service) Close() { + if s.leaderShip != nil && s.leaderShip.IsOwner() { + for k, v := range s.autoIDMap { + if v.base > 0 { + err := v.forceRebase(context.Background(), s.store, k.dbID, k.tblID, v.base, v.isUnsigned) + if err != nil { + logutil.BgLogger().Warn("save cached ID fail when service exit", zap.String("category", "autoid service"), + zap.Int64("db id", k.dbID), + zap.Int64("table id", k.tblID), + zap.Int64("value", v.base), + zap.Error(err)) + } + } + } + s.leaderShip.Cancel() + } +} + +// seekToFirstAutoIDSigned seeks to the next valid signed position. +func seekToFirstAutoIDSigned(base, increment, offset int64) int64 { + nr := (base + increment - offset) / increment + nr = nr*increment + offset + return nr +} + +// seekToFirstAutoIDUnSigned seeks to the next valid unsigned position. +func seekToFirstAutoIDUnSigned(base, increment, offset uint64) uint64 { + nr := (base + increment - offset) / increment + nr = nr*increment + offset + return nr +} + +func calcNeededBatchSize(base, n, increment, offset int64, isUnsigned bool) int64 { + if increment == 1 { + return n + } + if isUnsigned { + // SeekToFirstAutoIDUnSigned seeks to the next unsigned valid position. + nr := seekToFirstAutoIDUnSigned(uint64(base), uint64(increment), uint64(offset)) + // calculate the total batch size needed. + nr += (uint64(n) - 1) * uint64(increment) + return int64(nr - uint64(base)) + } + nr := seekToFirstAutoIDSigned(base, increment, offset) + // calculate the total batch size needed. + nr += (n - 1) * increment + return nr - base +} + +const batch = 4000 + +// AllocAutoID implements gRPC AutoIDAlloc interface. +func (s *Service) AllocAutoID(ctx context.Context, req *autoid.AutoIDRequest) (*autoid.AutoIDResponse, error) { + serviceKeyspaceID := uint32(s.store.GetCodec().GetKeyspaceID()) + if req.KeyspaceID != serviceKeyspaceID { + logutil.BgLogger().Info("Current service is not request keyspace leader.", zap.Uint32("req-keyspace-id", req.KeyspaceID), zap.Uint32("service-keyspace-id", serviceKeyspaceID)) + return nil, errors.Trace(errors.New("not leader")) + } + var res *autoid.AutoIDResponse + for { + var err error + res, err = s.allocAutoID(ctx, req) + if err != nil { + return nil, errors.Trace(err) + } + if res != nil { + break + } + } + return res, nil +} + +func (s *Service) getAlloc(dbID, tblID int64, isUnsigned bool) *autoIDValue { + key := autoIDKey{dbID: dbID, tblID: tblID} + s.autoIDLock.Lock() + defer s.autoIDLock.Unlock() + + val, ok := s.autoIDMap[key] + if !ok { + val = &autoIDValue{ + isUnsigned: isUnsigned, + token: make(chan struct{}, 1), + } + s.autoIDMap[key] = val + } + + return val +} + +func (s *Service) allocAutoID(ctx context.Context, req *autoid.AutoIDRequest) (*autoid.AutoIDResponse, error) { + if s.leaderShip != nil && !s.leaderShip.IsOwner() { + logutil.BgLogger().Info("Alloc AutoID fail, not leader", zap.String("category", "autoid service")) + return nil, errors.New("not leader") + } + + failpoint.Inject("mockErr", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil, errors.New("mock reload failed")) + } + }) + + val := s.getAlloc(req.DbID, req.TblID, req.IsUnsigned) + + if req.N == 0 { + if val.base != 0 { + return &autoid.AutoIDResponse{ + Min: val.base, + Max: val.base, + }, nil + } + // This item is not initialized, get the data from remote. + var currentEnd int64 + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, s.store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(req.DbID, req.TblID).IncrementID(model.TableInfoVersion5) + var err1 error + currentEnd, err1 = idAcc.Get() + if err1 != nil { + return err1 + } + val.end = currentEnd + return nil + }) + if err != nil { + return &autoid.AutoIDResponse{Errmsg: []byte(err.Error())}, nil + } + return &autoid.AutoIDResponse{ + Min: currentEnd, + Max: currentEnd, + }, nil + } + + val.Lock() + defer val.Unlock() + + var min, max int64 + var err error + if req.IsUnsigned { + min, max, err = val.alloc4Unsigned(ctx, s.store, req.DbID, req.TblID, req.IsUnsigned, req.N, req.Increment, req.Offset) + } else { + min, max, err = val.alloc4Signed(ctx, s.store, req.DbID, req.TblID, req.IsUnsigned, req.N, req.Increment, req.Offset) + } + + if err != nil { + return &autoid.AutoIDResponse{Errmsg: []byte(err.Error())}, nil + } + return &autoid.AutoIDResponse{ + Min: min, + Max: max, + }, nil +} + +func (alloc *autoIDValue) forceRebase(ctx context.Context, store kv.Storage, dbID, tblID, requiredBase int64, isUnsigned bool) error { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + var oldValue int64 + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := meta.NewMeta(txn).GetAutoIDAccessors(dbID, tblID).IncrementID(model.TableInfoVersion5) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + oldValue = currentEnd + var step int64 + if !isUnsigned { + step = requiredBase - currentEnd + } else { + uRequiredBase, uCurrentEnd := uint64(requiredBase), uint64(currentEnd) + step = int64(uRequiredBase - uCurrentEnd) + } + _, err1 = idAcc.Inc(step) + return err1 + }) + if err != nil { + return err + } + logutil.BgLogger().Info("forceRebase from", + zap.Int64("dbID", dbID), + zap.Int64("tblID", tblID), + zap.Int64("from", oldValue), + zap.Int64("to", requiredBase), + zap.Bool("isUnsigned", isUnsigned), + zap.String("category", "autoid service")) + alloc.base, alloc.end = requiredBase, requiredBase + return nil +} + +// Rebase implements gRPC AutoIDAlloc interface. +// req.N = 0 is handled specially, it is used to return the current auto ID value. +func (s *Service) Rebase(ctx context.Context, req *autoid.RebaseRequest) (*autoid.RebaseResponse, error) { + if s.leaderShip != nil && !s.leaderShip.IsOwner() { + logutil.BgLogger().Info("Rebase() fail, not leader", zap.String("category", "autoid service")) + return nil, errors.New("not leader") + } + + val := s.getAlloc(req.DbID, req.TblID, req.IsUnsigned) + if req.Force { + err := val.forceRebase(ctx, s.store, req.DbID, req.TblID, req.Base, req.IsUnsigned) + if err != nil { + return &autoid.RebaseResponse{Errmsg: []byte(err.Error())}, nil + } + } + + var err error + if req.IsUnsigned { + err = val.rebase4Unsigned(ctx, s.store, req.DbID, req.TblID, uint64(req.Base)) + } else { + err = val.rebase4Signed(ctx, s.store, req.DbID, req.TblID, req.Base) + } + if err != nil { + return &autoid.RebaseResponse{Errmsg: []byte(err.Error())}, nil + } + return &autoid.RebaseResponse{}, nil +} + +func init() { + autoid1.MockForTest = MockForTest +} diff --git a/pkg/autoid_service/autoid_test.go b/pkg/autoid_service/autoid_test.go new file mode 100644 index 0000000000000..8b52ad653df3f --- /dev/null +++ b/pkg/autoid_service/autoid_test.go @@ -0,0 +1,196 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package autoid + +import ( + "context" + "math" + "net" + "testing" + "time" + + "github.com/pingcap/kvproto/pkg/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "go.etcd.io/etcd/tests/v3/integration" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type autoIDResp struct { + *autoid.AutoIDResponse + error + *testing.T +} + +func (resp autoIDResp) check(min, max int64) { + require.NoError(resp.T, resp.error) + require.Equal(resp.T, resp.AutoIDResponse, &autoid.AutoIDResponse{Min: min, Max: max}) +} + +func (resp autoIDResp) checkErrmsg() { + require.NoError(resp.T, resp.error) + require.True(resp.T, len(resp.GetErrmsg()) > 0) +} + +type rebaseResp struct { + *autoid.RebaseResponse + error + *testing.T +} + +func (resp rebaseResp) check(msg string) { + require.NoError(resp.T, resp.error) + require.Equal(resp.T, string(resp.RebaseResponse.GetErrmsg()), msg) +} + +func TestAPI(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + cli := MockForTest(store) + tk.MustExec("use test") + tk.MustExec("create table t (id int key auto_increment);") + is := dom.InfoSchema() + dbInfo, ok := is.SchemaByName(model.NewCIStr("test")) + require.True(t, ok) + + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tbInfo := tbl.Meta() + + ctx := context.Background() + checkCurrValue := func(t *testing.T, cli autoid.AutoIDAllocClient, min, max int64) { + req := &autoid.AutoIDRequest{DbID: dbInfo.ID, TblID: tbInfo.ID, N: 0, KeyspaceID: uint32(tikv.NullspaceID)} + resp, err := cli.AllocAutoID(ctx, req) + require.NoError(t, err) + require.Equal(t, resp, &autoid.AutoIDResponse{Min: min, Max: max}) + } + autoIDRequest := func(t *testing.T, cli autoid.AutoIDAllocClient, unsigned bool, n uint64, more ...int64) autoIDResp { + increment := int64(1) + offset := int64(1) + if len(more) >= 1 { + increment = more[0] + } + if len(more) >= 2 { + offset = more[1] + } + req := &autoid.AutoIDRequest{DbID: dbInfo.ID, TblID: tbInfo.ID, IsUnsigned: unsigned, N: n, Increment: increment, Offset: offset, KeyspaceID: uint32(tikv.NullspaceID)} + resp, err := cli.AllocAutoID(ctx, req) + return autoIDResp{resp, err, t} + } + rebaseRequest := func(t *testing.T, cli autoid.AutoIDAllocClient, unsigned bool, n int64, force ...struct{}) rebaseResp { + req := &autoid.RebaseRequest{ + DbID: dbInfo.ID, + TblID: tbInfo.ID, + Base: n, + IsUnsigned: unsigned, + Force: len(force) > 0, + } + resp, err := cli.Rebase(ctx, req) + return rebaseResp{resp, err, t} + } + var force = struct{}{} + + // basic auto id operation + autoIDRequest(t, cli, false, 1).check(0, 1) + autoIDRequest(t, cli, false, 10).check(1, 11) + checkCurrValue(t, cli, 11, 11) + autoIDRequest(t, cli, false, 128).check(11, 139) + autoIDRequest(t, cli, false, 1, 10, 5).check(139, 145) + + // basic rebase operation + rebaseRequest(t, cli, false, 666).check("") + autoIDRequest(t, cli, false, 1).check(666, 667) + + rebaseRequest(t, cli, false, 6666).check("") + autoIDRequest(t, cli, false, 1).check(6666, 6667) + + // rebase will not decrease the value without 'force' + rebaseRequest(t, cli, false, 44).check("") + checkCurrValue(t, cli, 6667, 6667) + rebaseRequest(t, cli, false, 44, force).check("") + checkCurrValue(t, cli, 44, 44) + + // max increase 1 + rebaseRequest(t, cli, false, math.MaxInt64, force).check("") + checkCurrValue(t, cli, math.MaxInt64, math.MaxInt64) + autoIDRequest(t, cli, false, 1).checkErrmsg() + + rebaseRequest(t, cli, true, 0, force).check("") + checkCurrValue(t, cli, 0, 0) + autoIDRequest(t, cli, true, 1).check(0, 1) + autoIDRequest(t, cli, true, 10).check(1, 11) + autoIDRequest(t, cli, true, 128).check(11, 139) + autoIDRequest(t, cli, true, 1, 10, 5).check(139, 145) + + // max increase 1 + rebaseRequest(t, cli, true, math.MaxInt64).check("") + checkCurrValue(t, cli, math.MaxInt64, math.MaxInt64) + autoIDRequest(t, cli, true, 1).check(math.MaxInt64, math.MinInt64) + autoIDRequest(t, cli, true, 1).check(math.MinInt64, math.MinInt64+1) + + rebaseRequest(t, cli, true, -1).check("") + checkCurrValue(t, cli, -1, -1) + autoIDRequest(t, cli, true, 1).check(-1, 0) +} + +func TestGRPC(t *testing.T) { + integration.BeforeTestExternal(t) + store := testkit.CreateMockStore(t) + cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + defer cluster.Terminate(t) + etcdCli := cluster.RandClient() + + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer listener.Close() + addr := listener.Addr().String() + + service := newWithCli(addr, etcdCli, store) + defer service.Close() + + var i int + for !service.leaderShip.IsOwner() { + time.Sleep(100 * time.Millisecond) + i++ + if i >= 20 { + break + } + } + require.Less(t, i, 20) + + grpcServer := grpc.NewServer() + autoid.RegisterAutoIDAllocServer(grpcServer, service) + go func() { + grpcServer.Serve(listener) + }() + defer grpcServer.Stop() + + grpcConn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + cli := autoid.NewAutoIDAllocClient(grpcConn) + _, err = cli.AllocAutoID(context.Background(), &autoid.AutoIDRequest{ + DbID: 0, + TblID: 0, + N: 1, + Increment: 1, + Offset: 1, + IsUnsigned: false, + KeyspaceID: uint32(tikv.NullspaceID), + }) + require.NoError(t, err) +} diff --git a/pkg/bindinfo/BUILD.bazel b/pkg/bindinfo/BUILD.bazel new file mode 100644 index 0000000000000..333c75d7934bb --- /dev/null +++ b/pkg/bindinfo/BUILD.bazel @@ -0,0 +1,83 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "bindinfo", + srcs = [ + "bind_cache.go", + "bind_record.go", + "handle.go", + "session_handle.go", + "stat.go", + ], + importpath = "github.com/pingcap/tidb/pkg/bindinfo", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util/chunk", + "//pkg/util/hack", + "//pkg/util/hint", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/parser", + "//pkg/util/sqlexec", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/table-filter", + "//pkg/util/timeutil", + "@org_golang_x_exp//maps", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "bindinfo_test", + timeout = "moderate", + srcs = [ + "bind_cache_test.go", + "capture_test.go", + "handle_test.go", + "main_test.go", + "optimize_test.go", + "session_handle_test.go", + "temptable_test.go", + ], + embed = [":bindinfo"], + flaky = True, + race = "on", + shard_count = 42, + deps = [ + "//pkg/bindinfo/internal", + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/server", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/hack", + "//pkg/util/parser", + "//pkg/util/stmtsummary", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/bindinfo/bind_cache.go b/pkg/bindinfo/bind_cache.go similarity index 97% rename from bindinfo/bind_cache.go rename to pkg/bindinfo/bind_cache.go index b88a355237c49..163961d007855 100644 --- a/bindinfo/bind_cache.go +++ b/pkg/bindinfo/bind_cache.go @@ -18,11 +18,11 @@ import ( "errors" "sync" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" ) // bindCache uses the LRU cache to store the bindRecord. diff --git a/bindinfo/bind_cache_test.go b/pkg/bindinfo/bind_cache_test.go similarity index 96% rename from bindinfo/bind_cache_test.go rename to pkg/bindinfo/bind_cache_test.go index 7234038f40acf..ebf6e352590a6 100644 --- a/bindinfo/bind_cache_test.go +++ b/pkg/bindinfo/bind_cache_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" ) diff --git a/bindinfo/bind_record.go b/pkg/bindinfo/bind_record.go similarity index 97% rename from bindinfo/bind_record.go rename to pkg/bindinfo/bind_record.go index 50e8e0ba20784..279e430a5c91d 100644 --- a/bindinfo/bind_record.go +++ b/pkg/bindinfo/bind_record.go @@ -18,12 +18,12 @@ import ( "time" "unsafe" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/hint" ) const ( diff --git a/bindinfo/capture_test.go b/pkg/bindinfo/capture_test.go similarity index 99% rename from bindinfo/capture_test.go rename to pkg/bindinfo/capture_test.go index 333e9b8c63cff..3b9589a1a5240 100644 --- a/bindinfo/capture_test.go +++ b/pkg/bindinfo/capture_test.go @@ -20,16 +20,16 @@ import ( "testing" "time" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/bindinfo/internal" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/bindinfo/internal" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/pingcap/tidb/pkg/util/stmtsummary" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/pkg/bindinfo/handle.go b/pkg/bindinfo/handle.go new file mode 100644 index 0000000000000..5cfa1bf4f78ab --- /dev/null +++ b/pkg/bindinfo/handle.go @@ -0,0 +1,1288 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package bindinfo + +import ( + "context" + "fmt" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/logutil" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/pingcap/tidb/pkg/util/sqlexec" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + tablefilter "github.com/pingcap/tidb/pkg/util/table-filter" + "github.com/pingcap/tidb/pkg/util/timeutil" + "go.uber.org/zap" + "golang.org/x/exp/maps" +) + +// BindHandle is used to handle all global sql bind operations. +type BindHandle struct { + sctx struct { + sync.Mutex + sessionctx.Context + } + + // bindInfo caches the sql bind info from storage. + // + // The Mutex protects that there is only one goroutine changes the content + // of atomic.Value. + // + // NOTE: Concurrent Value Write: + // + // bindInfo.Lock() + // newCache := bindInfo.Value.Load() + // do the write operation on the newCache + // bindInfo.Value.Store(newCache) + // bindInfo.Unlock() + // + // NOTE: Concurrent Value Read: + // + // cache := bindInfo.Load(). + // read the content + // + bindInfo struct { + sync.Mutex + atomic.Value + parser *parser.Parser + lastUpdateTime types.Time + } + + // invalidBindRecordMap indicates the invalid bind records found during querying. + // A record will be deleted from this map, after 2 bind-lease, after it is dropped from the kv. + invalidBindRecordMap tmpBindRecordMap + + // pendingVerifyBindRecordMap indicates the pending verify bind records that found during query. + pendingVerifyBindRecordMap tmpBindRecordMap +} + +// Lease influences the duration of loading bind info and handling invalid bind. +var Lease = 3 * time.Second + +const ( + // OwnerKey is the bindinfo owner path that is saved to etcd. + OwnerKey = "/tidb/bindinfo/owner" + // Prompt is the prompt for bindinfo owner manager. + Prompt = "bindinfo" + // BuiltinPseudoSQL4BindLock is used to simulate LOCK TABLE for mysql.bind_info. + BuiltinPseudoSQL4BindLock = "builtin_pseudo_sql_for_bind_lock" +) + +type bindRecordUpdate struct { + bindRecord *BindRecord + updateTime time.Time +} + +// NewBindHandle creates a new BindHandle. +func NewBindHandle(ctx sessionctx.Context) *BindHandle { + handle := &BindHandle{} + handle.Reset(ctx) + return handle +} + +// Reset is to reset the BindHandle and clean old info. +func (h *BindHandle) Reset(ctx sessionctx.Context) { + h.bindInfo.Lock() + defer h.bindInfo.Unlock() + h.sctx.Context = ctx + h.bindInfo.Value.Store(newBindCache()) + h.bindInfo.parser = parser.New() + h.invalidBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate)) + h.invalidBindRecordMap.flushFunc = func(record *BindRecord) error { + _, err := h.DropBindRecord(record.OriginalSQL, record.Db, &record.Bindings[0]) + return err + } + h.pendingVerifyBindRecordMap.Value.Store(make(map[string]*bindRecordUpdate)) + h.pendingVerifyBindRecordMap.flushFunc = func(record *BindRecord) error { + // BindSQL has already been validated when coming here, so we use nil sctx parameter. + return h.AddBindRecord(nil, record) + } + variable.RegisterStatistics(h) +} + +// Update updates the global sql bind cache. +func (h *BindHandle) Update(fullLoad bool) (err error) { + h.bindInfo.Lock() + lastUpdateTime := h.bindInfo.lastUpdateTime + var timeCondition string + if !fullLoad { + timeCondition = fmt.Sprintf("WHERE update_time>'%s'", lastUpdateTime.String()) + } + + exec := h.sctx.Context.(sqlexec.RestrictedSQLExecutor) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + // No need to acquire the session context lock for ExecRestrictedSQL, it + // uses another background session. + selectStmt := fmt.Sprintf(`SELECT original_sql, bind_sql, default_db, status, create_time, + update_time, charset, collation, source, sql_digest, plan_digest FROM mysql.bind_info + %s ORDER BY update_time, create_time`, timeCondition) + rows, _, err := exec.ExecRestrictedSQL(ctx, nil, selectStmt) + + if err != nil { + h.bindInfo.Unlock() + return err + } + + newCache, memExceededErr := h.bindInfo.Value.Load().(*bindCache).Copy() + defer func() { + h.bindInfo.lastUpdateTime = lastUpdateTime + h.bindInfo.Value.Store(newCache) + h.bindInfo.Unlock() + }() + + for _, row := range rows { + // If the memory usage of the binding_cache exceeds its capacity, we will break and do not handle. + if memExceededErr != nil { + break + } + // Skip the builtin record which is designed for binding synchronization. + if row.GetString(0) == BuiltinPseudoSQL4BindLock { + continue + } + hash, meta, err := h.newBindRecord(row) + + // Update lastUpdateTime to the newest one. + // Even if this one is an invalid bind. + if meta.Bindings[0].UpdateTime.Compare(lastUpdateTime) > 0 { + lastUpdateTime = meta.Bindings[0].UpdateTime + } + + if err != nil { + logutil.BgLogger().Debug("failed to generate bind record from data row", zap.String("category", "sql-bind"), zap.Error(err)) + continue + } + + oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) + newRecord := merge(oldRecord, meta).removeDeletedBindings() + if len(newRecord.Bindings) > 0 { + err = newCache.SetBindRecord(hash, newRecord) + if err != nil { + memExceededErr = err + } + } else { + newCache.RemoveBindRecord(hash, newRecord) + } + updateMetrics(metrics.ScopeGlobal, oldRecord, newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db), true) + } + if memExceededErr != nil { + // When the memory capacity of bing_cache is not enough, + // there will be some memory-related errors in multiple places. + // Only needs to be handled once. + logutil.BgLogger().Warn("BindHandle.Update", zap.String("category", "sql-bind"), zap.Error(memExceededErr)) + } + return nil +} + +// CreateBindRecord creates a BindRecord to the storage and the cache. +// It replaces all the exists bindings for the same normalized SQL. +func (h *BindHandle) CreateBindRecord(sctx sessionctx.Context, record *BindRecord) (err error) { + err = record.prepareHints(sctx) + if err != nil { + return err + } + + record.Db = strings.ToLower(record.Db) + h.bindInfo.Lock() + h.sctx.Lock() + defer func() { + h.sctx.Unlock() + h.bindInfo.Unlock() + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + if err != nil { + return + } + + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + + sqlDigest := parser.DigestNormalized(record.OriginalSQL) + h.setBindRecord(sqlDigest.String(), record) + }() + + // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. + if err = h.lockBindInfoTable(); err != nil { + return err + } + + now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) + + updateTs := now.String() + _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %?`, + deleted, updateTs, record.OriginalSQL, updateTs) + if err != nil { + return err + } + + for i := range record.Bindings { + record.Bindings[i].CreateTime = now + record.Bindings[i].UpdateTime = now + + // Insert the BindRecord to the storage. + _, err = exec.ExecuteInternal(ctx, `INSERT INTO mysql.bind_info VALUES (%?,%?, %?, %?, %?, %?, %?, %?, %?, %?, %?)`, + record.OriginalSQL, + record.Bindings[i].BindSQL, + record.Db, + record.Bindings[i].Status, + record.Bindings[i].CreateTime.String(), + record.Bindings[i].UpdateTime.String(), + record.Bindings[i].Charset, + record.Bindings[i].Collation, + record.Bindings[i].Source, + record.Bindings[i].SQLDigest, + record.Bindings[i].PlanDigest, + ) + if err != nil { + return err + } + } + return nil +} + +// AddBindRecord adds a BindRecord to the storage and BindRecord to the cache. +func (h *BindHandle) AddBindRecord(sctx sessionctx.Context, record *BindRecord) (err error) { + err = record.prepareHints(sctx) + if err != nil { + return err + } + + record.Db = strings.ToLower(record.Db) + oldRecord := h.GetBindRecord(parser.DigestNormalized(record.OriginalSQL).String(), record.OriginalSQL, record.Db) + var duplicateBinding *Binding + if oldRecord != nil { + binding := oldRecord.FindBinding(record.Bindings[0].ID) + if binding != nil { + // There is already a binding with status `Enabled`, `Disabled`, `PendingVerify` or `Rejected`, we could directly cancel the job. + if record.Bindings[0].Status == PendingVerify { + return nil + } + // Otherwise, we need to remove it before insert. + duplicateBinding = binding + } + } + + h.bindInfo.Lock() + h.sctx.Lock() + defer func() { + h.sctx.Unlock() + h.bindInfo.Unlock() + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + if err != nil { + return + } + + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + + h.appendBindRecord(parser.DigestNormalized(record.OriginalSQL).String(), record) + }() + + // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. + if err = h.lockBindInfoTable(); err != nil { + return err + } + if duplicateBinding != nil { + _, err = exec.ExecuteInternal(ctx, `DELETE FROM mysql.bind_info WHERE original_sql = %? AND bind_sql = %?`, record.OriginalSQL, duplicateBinding.BindSQL) + if err != nil { + return err + } + } + + now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) + for i := range record.Bindings { + if duplicateBinding != nil { + record.Bindings[i].CreateTime = duplicateBinding.CreateTime + } else { + record.Bindings[i].CreateTime = now + } + record.Bindings[i].UpdateTime = now + + if record.Bindings[i].SQLDigest == "" { + parser4binding := parser.New() + var originNode ast.StmtNode + originNode, err = parser4binding.ParseOneStmt(record.OriginalSQL, record.Bindings[i].Charset, record.Bindings[i].Collation) + if err != nil { + return err + } + _, sqlDigestWithDB := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(originNode, record.Db, record.OriginalSQL)) + record.Bindings[i].SQLDigest = sqlDigestWithDB.String() + } + // Insert the BindRecord to the storage. + _, err = exec.ExecuteInternal(ctx, `INSERT INTO mysql.bind_info VALUES (%?, %?, %?, %?, %?, %?, %?, %?, %?, %?, %?)`, + record.OriginalSQL, + record.Bindings[i].BindSQL, + record.Db, + record.Bindings[i].Status, + record.Bindings[i].CreateTime.String(), + record.Bindings[i].UpdateTime.String(), + record.Bindings[i].Charset, + record.Bindings[i].Collation, + record.Bindings[i].Source, + record.Bindings[i].SQLDigest, + record.Bindings[i].PlanDigest, + ) + if err != nil { + return err + } + } + return nil +} + +// DropBindRecord drops a BindRecord to the storage and BindRecord int the cache. +func (h *BindHandle) DropBindRecord(originalSQL, db string, binding *Binding) (deletedRows uint64, err error) { + db = strings.ToLower(db) + h.bindInfo.Lock() + h.sctx.Lock() + defer func() { + h.sctx.Unlock() + h.bindInfo.Unlock() + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + if err != nil { + return 0, err + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil || deletedRows == 0 { + return + } + + record := &BindRecord{OriginalSQL: originalSQL, Db: db} + if binding != nil { + record.Bindings = append(record.Bindings, *binding) + } + h.removeBindRecord(parser.DigestNormalized(originalSQL).String(), record) + }() + + // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. + if err = h.lockBindInfoTable(); err != nil { + return 0, err + } + + updateTs := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3).String() + + if binding == nil { + _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND status != %?`, + deleted, updateTs, originalSQL, updateTs, deleted) + } else { + _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND bind_sql = %? and status != %?`, + deleted, updateTs, originalSQL, updateTs, binding.BindSQL, deleted) + } + if err != nil { + return 0, err + } + + return h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows(), nil +} + +// DropBindRecordByDigest drop BindRecord to the storage and BindRecord int the cache. +func (h *BindHandle) DropBindRecordByDigest(sqlDigest string) (deletedRows uint64, err error) { + oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest) + if err != nil { + return 0, err + } + return h.DropBindRecord(oldRecord.OriginalSQL, strings.ToLower(oldRecord.Db), nil) +} + +// SetBindRecordStatus set a BindRecord's status to the storage and bind cache. +func (h *BindHandle) SetBindRecordStatus(originalSQL string, binding *Binding, newStatus string) (ok bool, err error) { + h.bindInfo.Lock() + h.sctx.Lock() + defer func() { + h.sctx.Unlock() + h.bindInfo.Unlock() + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + if err != nil { + return + } + var ( + updateTs types.Time + oldStatus0, oldStatus1 string + affectRows int + ) + if newStatus == Disabled { + // For compatibility reasons, when we need to 'set binding disabled for ', + // we need to consider both the 'enabled' and 'using' status. + oldStatus0 = Using + oldStatus1 = Enabled + } else if newStatus == Enabled { + // In order to unify the code, two identical old statuses are set. + oldStatus0 = Disabled + oldStatus1 = Disabled + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + if affectRows == 0 { + return + } + + // The set binding status operation is success. + ok = true + record := &BindRecord{OriginalSQL: originalSQL} + sqlDigest := parser.DigestNormalized(record.OriginalSQL) + oldRecord := h.GetBindRecord(sqlDigest.String(), originalSQL, "") + setBindingStatusInCacheSucc := false + if oldRecord != nil && len(oldRecord.Bindings) > 0 { + record.Bindings = make([]Binding, len(oldRecord.Bindings)) + copy(record.Bindings, oldRecord.Bindings) + for ind, oldBinding := range record.Bindings { + if oldBinding.Status == oldStatus0 || oldBinding.Status == oldStatus1 { + if binding == nil || (binding != nil && oldBinding.isSame(binding)) { + setBindingStatusInCacheSucc = true + record.Bindings[ind].Status = newStatus + record.Bindings[ind].UpdateTime = updateTs + } + } + } + } + if setBindingStatusInCacheSucc { + h.setBindRecord(sqlDigest.String(), record) + } + }() + + // Lock mysql.bind_info to synchronize with SetBindingStatus on other tidb instances. + if err = h.lockBindInfoTable(); err != nil { + return + } + + updateTs = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) + updateTsStr := updateTs.String() + + if binding == nil { + _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND status IN (%?, %?)`, + newStatus, updateTsStr, originalSQL, updateTsStr, oldStatus0, oldStatus1) + } else { + _, err = exec.ExecuteInternal(ctx, `UPDATE mysql.bind_info SET status = %?, update_time = %? WHERE original_sql = %? AND update_time < %? AND bind_sql = %? AND status IN (%?, %?)`, + newStatus, updateTsStr, originalSQL, updateTsStr, binding.BindSQL, oldStatus0, oldStatus1) + } + affectRows = int(h.sctx.Context.GetSessionVars().StmtCtx.AffectedRows()) + return +} + +// SetBindRecordStatusByDigest set a BindRecord's status to the storage and bind cache. +func (h *BindHandle) SetBindRecordStatusByDigest(newStatus, sqlDigest string) (ok bool, err error) { + oldRecord, err := h.GetBindRecordBySQLDigest(sqlDigest) + if err != nil { + return false, err + } + return h.SetBindRecordStatus(oldRecord.OriginalSQL, nil, newStatus) +} + +// GCBindRecord physically removes the deleted bind records in mysql.bind_info. +func (h *BindHandle) GCBindRecord() (err error) { + h.bindInfo.Lock() + h.sctx.Lock() + defer func() { + h.sctx.Unlock() + h.bindInfo.Unlock() + }() + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + _, err = exec.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + if err != nil { + return err + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + }() + + // Lock mysql.bind_info to synchronize with CreateBindRecord / AddBindRecord / DropBindRecord on other tidb instances. + if err = h.lockBindInfoTable(); err != nil { + return err + } + + // To make sure that all the deleted bind records have been acknowledged to all tidb, + // we only garbage collect those records with update_time before 10 leases. + updateTime := time.Now().Add(-(10 * Lease)) + updateTimeStr := types.NewTime(types.FromGoTime(updateTime), mysql.TypeTimestamp, 3).String() + _, err = exec.ExecuteInternal(ctx, `DELETE FROM mysql.bind_info WHERE status = 'deleted' and update_time < %?`, updateTimeStr) + return err +} + +// lockBindInfoTable simulates `LOCK TABLE mysql.bind_info WRITE` by acquiring a pessimistic lock on a +// special builtin row of mysql.bind_info. Note that this function must be called with h.sctx.Lock() held. +// We can replace this implementation to normal `LOCK TABLE mysql.bind_info WRITE` if that feature is +// generally available later. +// This lock would enforce the CREATE / DROP GLOBAL BINDING statements to be executed sequentially, +// even if they come from different tidb instances. +func (h *BindHandle) lockBindInfoTable() error { + // h.sctx already locked. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + exec, _ := h.sctx.Context.(sqlexec.SQLExecutor) + _, err := exec.ExecuteInternal(ctx, h.LockBindInfoSQL()) + return err +} + +// LockBindInfoSQL simulates LOCK TABLE by updating a same row in each pessimistic transaction. +func (*BindHandle) LockBindInfoSQL() string { + sql, err := sqlexec.EscapeSQL("UPDATE mysql.bind_info SET source= %? WHERE original_sql= %?", Builtin, BuiltinPseudoSQL4BindLock) + if err != nil { + return "" + } + return sql +} + +// tmpBindRecordMap is used to temporarily save bind record changes. +// Those changes will be flushed into store periodically. +type tmpBindRecordMap struct { + sync.Mutex + atomic.Value + flushFunc func(record *BindRecord) error +} + +// flushToStore calls flushFunc for items in tmpBindRecordMap and removes them with a delay. +func (tmpMap *tmpBindRecordMap) flushToStore() { + tmpMap.Lock() + defer tmpMap.Unlock() + newMap := copyBindRecordUpdateMap(tmpMap.Load().(map[string]*bindRecordUpdate)) + for key, bindRecord := range newMap { + if bindRecord.updateTime.IsZero() { + err := tmpMap.flushFunc(bindRecord.bindRecord) + if err != nil { + logutil.BgLogger().Debug("flush bind record failed", zap.String("category", "sql-bind"), zap.Error(err)) + } + bindRecord.updateTime = time.Now() + continue + } + + if time.Since(bindRecord.updateTime) > 6*time.Second { + delete(newMap, key) + updateMetrics(metrics.ScopeGlobal, bindRecord.bindRecord, nil, false) + } + } + tmpMap.Store(newMap) +} + +// Add puts a BindRecord into tmpBindRecordMap. +func (tmpMap *tmpBindRecordMap) Add(bindRecord *BindRecord) { + key := bindRecord.OriginalSQL + ":" + bindRecord.Db + ":" + bindRecord.Bindings[0].ID + if _, ok := tmpMap.Load().(map[string]*bindRecordUpdate)[key]; ok { + return + } + tmpMap.Lock() + defer tmpMap.Unlock() + if _, ok := tmpMap.Load().(map[string]*bindRecordUpdate)[key]; ok { + return + } + newMap := copyBindRecordUpdateMap(tmpMap.Load().(map[string]*bindRecordUpdate)) + newMap[key] = &bindRecordUpdate{ + bindRecord: bindRecord, + } + tmpMap.Store(newMap) + updateMetrics(metrics.ScopeGlobal, nil, bindRecord, false) +} + +// DropInvalidBindRecord executes the drop BindRecord tasks. +func (h *BindHandle) DropInvalidBindRecord() { + h.invalidBindRecordMap.flushToStore() +} + +// AddDropInvalidBindTask adds BindRecord which needs to be deleted into invalidBindRecordMap. +func (h *BindHandle) AddDropInvalidBindTask(invalidBindRecord *BindRecord) { + h.invalidBindRecordMap.Add(invalidBindRecord) +} + +// Size returns the size of bind info cache. +func (h *BindHandle) Size() int { + size := len(h.bindInfo.Load().(*bindCache).GetAllBindRecords()) + return size +} + +// GetBindRecord returns the BindRecord of the (normdOrigSQL,db) if BindRecord exist. +func (h *BindHandle) GetBindRecord(hash, normdOrigSQL, db string) *BindRecord { + return h.bindInfo.Load().(*bindCache).GetBindRecord(hash, normdOrigSQL, db) +} + +// GetBindRecordBySQLDigest returns the BindRecord of the sql digest. +func (h *BindHandle) GetBindRecordBySQLDigest(sqlDigest string) (*BindRecord, error) { + return h.bindInfo.Load().(*bindCache).GetBindRecordBySQLDigest(sqlDigest) +} + +// GetAllBindRecord returns all bind records in cache. +func (h *BindHandle) GetAllBindRecord() (bindRecords []*BindRecord) { + return h.bindInfo.Load().(*bindCache).GetAllBindRecords() +} + +// SetBindCacheCapacity reset the capacity for the bindCache. +// It will not affect already cached BindRecords. +func (h *BindHandle) SetBindCacheCapacity(capacity int64) { + h.bindInfo.Load().(*bindCache).SetMemCapacity(capacity) +} + +// GetMemUsage returns the memory usage for the bind cache. +func (h *BindHandle) GetMemUsage() (memUsage int64) { + return h.bindInfo.Load().(*bindCache).GetMemUsage() +} + +// GetMemCapacity returns the memory capacity for the bind cache. +func (h *BindHandle) GetMemCapacity() (memCapacity int64) { + return h.bindInfo.Load().(*bindCache).GetMemCapacity() +} + +// newBindRecord builds BindRecord from a tuple in storage. +func (h *BindHandle) newBindRecord(row chunk.Row) (string, *BindRecord, error) { + status := row.GetString(3) + // For compatibility, the 'Using' status binding will be converted to the 'Enabled' status binding. + if status == Using { + status = Enabled + } + hint := Binding{ + BindSQL: row.GetString(1), + Status: status, + CreateTime: row.GetTime(4), + UpdateTime: row.GetTime(5), + Charset: row.GetString(6), + Collation: row.GetString(7), + Source: row.GetString(8), + SQLDigest: row.GetString(9), + PlanDigest: row.GetString(10), + } + bindRecord := &BindRecord{ + OriginalSQL: row.GetString(0), + Db: strings.ToLower(row.GetString(2)), + Bindings: []Binding{hint}, + } + hash := parser.DigestNormalized(bindRecord.OriginalSQL) + h.sctx.Lock() + defer h.sctx.Unlock() + h.sctx.GetSessionVars().CurrentDB = bindRecord.Db + err := bindRecord.prepareHints(h.sctx.Context) + return hash.String(), bindRecord, err +} + +// setBindRecord sets the BindRecord to the cache, if there already exists a BindRecord, +// it will be overridden. +func (h *BindHandle) setBindRecord(hash string, meta *BindRecord) { + newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() + if err0 != nil { + logutil.BgLogger().Warn("BindHandle.setBindRecord", zap.String("category", "sql-bind"), zap.Error(err0)) + } + oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) + err1 := newCache.SetBindRecord(hash, meta) + if err1 != nil && err0 == nil { + logutil.BgLogger().Warn("BindHandle.setBindRecord", zap.String("category", "sql-bind"), zap.Error(err1)) + } + h.bindInfo.Value.Store(newCache) + updateMetrics(metrics.ScopeGlobal, oldRecord, meta, false) +} + +// appendBindRecord adds the BindRecord to the cache, all the stale BindRecords are +// removed from the cache after this operation. +func (h *BindHandle) appendBindRecord(hash string, meta *BindRecord) { + newCache, err0 := h.bindInfo.Value.Load().(*bindCache).Copy() + if err0 != nil { + logutil.BgLogger().Warn("BindHandle.appendBindRecord", zap.String("category", "sql-bind"), zap.Error(err0)) + } + oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) + newRecord := merge(oldRecord, meta) + err1 := newCache.SetBindRecord(hash, newRecord) + if err1 != nil && err0 == nil { + // Only need to handle the error once. + logutil.BgLogger().Warn("BindHandle.appendBindRecord", zap.String("category", "sql-bind"), zap.Error(err1)) + } + h.bindInfo.Value.Store(newCache) + updateMetrics(metrics.ScopeGlobal, oldRecord, newRecord, false) +} + +// removeBindRecord removes the BindRecord from the cache. +func (h *BindHandle) removeBindRecord(hash string, meta *BindRecord) { + newCache, err := h.bindInfo.Value.Load().(*bindCache).Copy() + if err != nil { + logutil.BgLogger().Warn("", zap.String("category", "sql-bind"), zap.Error(err)) + } + oldRecord := newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db) + newCache.RemoveBindRecord(hash, meta) + h.bindInfo.Value.Store(newCache) + updateMetrics(metrics.ScopeGlobal, oldRecord, newCache.GetBindRecord(hash, meta.OriginalSQL, meta.Db), false) +} + +func copyBindRecordUpdateMap(oldMap map[string]*bindRecordUpdate) map[string]*bindRecordUpdate { + newMap := make(map[string]*bindRecordUpdate, len(oldMap)) + maps.Copy(newMap, oldMap) + return newMap +} + +type captureFilter struct { + frequency int64 + tables []tablefilter.Filter // `schema.table` + users map[string]struct{} + + fail bool + currentDB string +} + +func (cf *captureFilter) Enter(in ast.Node) (out ast.Node, skipChildren bool) { + if x, ok := in.(*ast.TableName); ok { + tblEntry := stmtctx.TableEntry{ + DB: x.Schema.L, + Table: x.Name.L, + } + if x.Schema.L == "" { + tblEntry.DB = cf.currentDB + } + for _, tableFilter := range cf.tables { + if tableFilter.MatchTable(tblEntry.DB, tblEntry.Table) { + cf.fail = true // some filter is matched + } + } + } + return in, cf.fail +} + +func (*captureFilter) Leave(in ast.Node) (out ast.Node, ok bool) { + return in, true +} + +func (cf *captureFilter) isEmpty() bool { + return len(cf.tables) == 0 && len(cf.users) == 0 +} + +// ParseCaptureTableFilter checks whether this filter is valid and parses it. +func ParseCaptureTableFilter(tableFilter string) (f tablefilter.Filter, valid bool) { + // forbid wildcards '!' and '@' for safety, + // please see https://github.com/pingcap/tidb-tools/tree/master/pkg/table-filter for more details. + tableFilter = strings.TrimLeft(tableFilter, " \t") + if tableFilter == "" { + return nil, false + } + if tableFilter[0] == '!' || tableFilter[0] == '@' { + return nil, false + } + var err error + f, err = tablefilter.Parse([]string{tableFilter}) + if err != nil { + return nil, false + } + return f, true +} + +func (h *BindHandle) extractCaptureFilterFromStorage() (filter *captureFilter) { + filter = &captureFilter{ + frequency: 1, + users: make(map[string]struct{}), + } + exec := h.sctx.Context.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + // No need to acquire the session context lock for ExecRestrictedSQL, it + // uses another background session. + rows, _, err := exec.ExecRestrictedSQL(ctx, nil, `SELECT filter_type, filter_value FROM mysql.capture_plan_baselines_blacklist order by filter_type`) + if err != nil { + logutil.BgLogger().Warn("failed to load mysql.capture_plan_baselines_blacklist", zap.String("category", "sql-bind"), zap.Error(err)) + return + } + for _, row := range rows { + filterTp := strings.ToLower(row.GetString(0)) + valStr := strings.ToLower(row.GetString(1)) + switch filterTp { + case "table": + tfilter, valid := ParseCaptureTableFilter(valStr) + if !valid { + logutil.BgLogger().Warn("capture table filter is invalid, ignore it", zap.String("category", "sql-bind"), zap.String("filter_value", valStr)) + continue + } + filter.tables = append(filter.tables, tfilter) + case "user": + filter.users[valStr] = struct{}{} + case "frequency": + f, err := strconv.ParseInt(valStr, 10, 64) + if err != nil { + logutil.BgLogger().Warn("failed to parse frequency type value, ignore it", zap.String("category", "sql-bind"), zap.String("filter_value", valStr), zap.Error(err)) + continue + } + if f < 1 { + logutil.BgLogger().Warn("frequency threshold is less than 1, ignore it", zap.String("category", "sql-bind"), zap.Int64("frequency", f)) + continue + } + if f > filter.frequency { + filter.frequency = f + } + default: + logutil.BgLogger().Warn("unknown capture filter type, ignore it", zap.String("category", "sql-bind"), zap.String("filter_type", filterTp)) + } + } + return +} + +// CaptureBaselines is used to automatically capture plan baselines. +func (h *BindHandle) CaptureBaselines() { + parser4Capture := parser.New() + captureFilter := h.extractCaptureFilterFromStorage() + emptyCaptureFilter := captureFilter.isEmpty() + bindableStmts := stmtsummaryv2.GetMoreThanCntBindableStmt(captureFilter.frequency) + for _, bindableStmt := range bindableStmts { + stmt, err := parser4Capture.ParseOneStmt(bindableStmt.Query, bindableStmt.Charset, bindableStmt.Collation) + if err != nil { + logutil.BgLogger().Debug("parse SQL failed in baseline capture", zap.String("category", "sql-bind"), zap.String("SQL", bindableStmt.Query), zap.Error(err)) + continue + } + if insertStmt, ok := stmt.(*ast.InsertStmt); ok && insertStmt.Select == nil { + continue + } + if !emptyCaptureFilter { + captureFilter.fail = false + captureFilter.currentDB = bindableStmt.Schema + stmt.Accept(captureFilter) + if captureFilter.fail { + continue + } + + if len(captureFilter.users) > 0 { + filteredByUser := true + for user := range bindableStmt.Users { + if _, ok := captureFilter.users[user]; !ok { + filteredByUser = false // some user not in the black-list has processed this stmt + break + } + } + if filteredByUser { + continue + } + } + } + dbName := utilparser.GetDefaultDB(stmt, bindableStmt.Schema) + normalizedSQL, digest := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmt, dbName, bindableStmt.Query)) + if r := h.GetBindRecord(digest.String(), normalizedSQL, dbName); r != nil && r.HasAvailableBinding() { + continue + } + bindSQL := GenerateBindSQL(context.TODO(), stmt, bindableStmt.PlanHint, true, dbName) + if bindSQL == "" { + continue + } + h.sctx.Lock() + charset, collation := h.sctx.GetSessionVars().GetCharsetInfo() + h.sctx.Unlock() + binding := Binding{ + BindSQL: bindSQL, + Status: Enabled, + Charset: charset, + Collation: collation, + Source: Capture, + SQLDigest: digest.String(), + } + // We don't need to pass the `sctx` because the BindSQL has been validated already. + err = h.CreateBindRecord(nil, &BindRecord{OriginalSQL: normalizedSQL, Db: dbName, Bindings: []Binding{binding}}) + if err != nil { + logutil.BgLogger().Debug("create bind record failed in baseline capture", zap.String("category", "sql-bind"), zap.String("SQL", bindableStmt.Query), zap.Error(err)) + } + } +} + +func getHintsForSQL(sctx sessionctx.Context, sql string) (string, error) { + origVals := sctx.GetSessionVars().UsePlanBaselines + sctx.GetSessionVars().UsePlanBaselines = false + + // Usually passing a sprintf to ExecuteInternal is not recommended, but in this case + // it is safe because ExecuteInternal does not permit MultiStatement execution. Thus, + // the statement won't be able to "break out" from EXPLAIN. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, fmt.Sprintf("EXPLAIN FORMAT='hint' %s", sql)) + sctx.GetSessionVars().UsePlanBaselines = origVals + if rs != nil { + defer func() { + // Audit log is collected in Close(), set InRestrictedSQL to avoid 'create sql binding' been recorded as 'explain'. + origin := sctx.GetSessionVars().InRestrictedSQL + sctx.GetSessionVars().InRestrictedSQL = true + terror.Call(rs.Close) + sctx.GetSessionVars().InRestrictedSQL = origin + }() + } + if err != nil { + return "", err + } + chk := rs.NewChunk(nil) + err = rs.Next(context.TODO(), chk) + if err != nil { + return "", err + } + return chk.GetRow(0).GetString(0), nil +} + +// GenerateBindSQL generates binding sqls from stmt node and plan hints. +func GenerateBindSQL(ctx context.Context, stmtNode ast.StmtNode, planHint string, skipCheckIfHasParam bool, defaultDB string) string { + // If would be nil for very simple cases such as point get, we do not need to evolve for them. + if planHint == "" { + return "" + } + if !skipCheckIfHasParam { + paramChecker := ¶mMarkerChecker{} + stmtNode.Accept(paramChecker) + // We need to evolve on current sql, but we cannot restore values for paramMarkers yet, + // so just ignore them now. + if paramChecker.hasParamMarker { + return "" + } + } + // We need to evolve plan based on the current sql, not the original sql which may have different parameters. + // So here we would remove the hint and inject the current best plan hint. + hint.BindHint(stmtNode, &hint.HintsSet{}) + bindSQL := utilparser.RestoreWithDefaultDB(stmtNode, defaultDB, "") + if bindSQL == "" { + return "" + } + switch n := stmtNode.(type) { + case *ast.DeleteStmt: + deleteIdx := strings.Index(bindSQL, "DELETE") + // Remove possible `explain` prefix. + bindSQL = bindSQL[deleteIdx:] + return strings.Replace(bindSQL, "DELETE", fmt.Sprintf("DELETE /*+ %s*/", planHint), 1) + case *ast.UpdateStmt: + updateIdx := strings.Index(bindSQL, "UPDATE") + // Remove possible `explain` prefix. + bindSQL = bindSQL[updateIdx:] + return strings.Replace(bindSQL, "UPDATE", fmt.Sprintf("UPDATE /*+ %s*/", planHint), 1) + case *ast.SelectStmt: + var selectIdx int + if n.With != nil { + var withSb strings.Builder + withIdx := strings.Index(bindSQL, "WITH") + restoreCtx := format.NewRestoreCtx(format.RestoreStringSingleQuotes|format.RestoreSpacesAroundBinaryOperation|format.RestoreStringWithoutCharset|format.RestoreNameBackQuotes, &withSb) + restoreCtx.DefaultDB = defaultDB + if err := n.With.Restore(restoreCtx); err != nil { + logutil.BgLogger().Debug("restore SQL failed", zap.String("category", "sql-bind"), zap.Error(err)) + return "" + } + withEnd := withIdx + len(withSb.String()) + tmp := strings.Replace(bindSQL[withEnd:], "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) + return strings.Join([]string{bindSQL[withIdx:withEnd], tmp}, "") + } + selectIdx = strings.Index(bindSQL, "SELECT") + // Remove possible `explain` prefix. + bindSQL = bindSQL[selectIdx:] + return strings.Replace(bindSQL, "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) + case *ast.InsertStmt: + insertIdx := int(0) + if n.IsReplace { + insertIdx = strings.Index(bindSQL, "REPLACE") + } else { + insertIdx = strings.Index(bindSQL, "INSERT") + } + // Remove possible `explain` prefix. + bindSQL = bindSQL[insertIdx:] + return strings.Replace(bindSQL, "SELECT", fmt.Sprintf("SELECT /*+ %s*/", planHint), 1) + } + logutil.Logger(ctx).Debug("unexpected statement type when generating bind SQL", zap.String("category", "sql-bind"), zap.Any("statement", stmtNode)) + return "" +} + +type paramMarkerChecker struct { + hasParamMarker bool +} + +func (e *paramMarkerChecker) Enter(in ast.Node) (ast.Node, bool) { + if _, ok := in.(*driver.ParamMarkerExpr); ok { + e.hasParamMarker = true + return in, true + } + return in, false +} + +func (*paramMarkerChecker) Leave(in ast.Node) (ast.Node, bool) { + return in, true +} + +// AddEvolvePlanTask adds the evolve plan task into memory cache. It would be flushed to store periodically. +func (h *BindHandle) AddEvolvePlanTask(originalSQL, db string, binding Binding) { + br := &BindRecord{ + OriginalSQL: originalSQL, + Db: db, + Bindings: []Binding{binding}, + } + h.pendingVerifyBindRecordMap.Add(br) +} + +// SaveEvolveTasksToStore saves the evolve task into store. +func (h *BindHandle) SaveEvolveTasksToStore() { + h.pendingVerifyBindRecordMap.flushToStore() +} + +func getEvolveParameters(sctx sessionctx.Context) (time.Duration, time.Time, time.Time, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL( + ctx, + nil, + "SELECT variable_name, variable_value FROM mysql.global_variables WHERE variable_name IN (%?, %?, %?)", + variable.TiDBEvolvePlanTaskMaxTime, + variable.TiDBEvolvePlanTaskStartTime, + variable.TiDBEvolvePlanTaskEndTime, + ) + if err != nil { + return 0, time.Time{}, time.Time{}, err + } + maxTime, startTimeStr, endTimeStr := int64(variable.DefTiDBEvolvePlanTaskMaxTime), variable.DefTiDBEvolvePlanTaskStartTime, variable.DefAutoAnalyzeEndTime + for _, row := range rows { + switch row.GetString(0) { + case variable.TiDBEvolvePlanTaskMaxTime: + maxTime, err = strconv.ParseInt(row.GetString(1), 10, 64) + if err != nil { + return 0, time.Time{}, time.Time{}, err + } + case variable.TiDBEvolvePlanTaskStartTime: + startTimeStr = row.GetString(1) + case variable.TiDBEvolvePlanTaskEndTime: + endTimeStr = row.GetString(1) + } + } + startTime, err := time.ParseInLocation(variable.FullDayTimeFormat, startTimeStr, time.UTC) + if err != nil { + return 0, time.Time{}, time.Time{}, err + } + endTime, err := time.ParseInLocation(variable.FullDayTimeFormat, endTimeStr, time.UTC) + if err != nil { + return 0, time.Time{}, time.Time{}, err + } + return time.Duration(maxTime) * time.Second, startTime, endTime, nil +} + +const ( + // acceptFactor is the factor to decide should we accept the pending verified plan. + // A pending verified plan will be accepted if it performs at least `acceptFactor` times better than the accepted plans. + acceptFactor = 1.5 + // verifyTimeoutFactor is how long to wait to verify the pending plan. + // For debugging purposes it is useful to wait a few times longer than the current execution time so that + // an informative error can be written to the log. + verifyTimeoutFactor = 2.0 + // nextVerifyDuration is the duration that we will retry the rejected plans. + nextVerifyDuration = 7 * 24 * time.Hour +) + +func (h *BindHandle) getOnePendingVerifyJob() (originalSQL, db string, binding Binding) { + cache := h.bindInfo.Value.Load().(*bindCache) + for _, bindRecord := range cache.GetAllBindRecords() { + for _, bind := range bindRecord.Bindings { + if bind.Status == PendingVerify { + return bindRecord.OriginalSQL, bindRecord.Db, bind + } + if bind.Status != Rejected { + continue + } + dur, err := bind.SinceUpdateTime() + // Should not happen. + if err != nil { + continue + } + // Rejected and retry it now. + if dur > nextVerifyDuration { + return bindRecord.OriginalSQL, bindRecord.Db, bind + } + } + } + return "", "", Binding{} +} + +func (*BindHandle) getRunningDuration(sctx sessionctx.Context, db, sql string, maxTime time.Duration) (time.Duration, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBindInfo) + if db != "" { + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "use %n", db) + if err != nil { + return 0, err + } + } + ctx, cancelFunc := context.WithCancel(ctx) + timer := time.NewTimer(maxTime) + defer timer.Stop() + resultChan := make(chan error) + startTime := time.Now() + go runSQL(ctx, sctx, sql, resultChan) + select { + case err := <-resultChan: + cancelFunc() + if err != nil { + return 0, err + } + return time.Since(startTime), nil + case <-timer.C: + cancelFunc() + logutil.BgLogger().Debug("plan verification timed out", zap.String("category", "sql-bind"), zap.Duration("timeElapsed", time.Since(startTime)), zap.String("query", sql)) + } + <-resultChan + return -1, nil +} + +func runSQL(ctx context.Context, sctx sessionctx.Context, sql string, resultChan chan<- error) { + defer func() { + if r := recover(); r != nil { + buf := make([]byte, 4096) + stackSize := runtime.Stack(buf, false) + buf = buf[:stackSize] + resultChan <- fmt.Errorf("run sql panicked: %v", string(buf)) + } + }() + rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) + if err != nil { + if rs != nil { + terror.Call(rs.Close) + } + resultChan <- err + return + } + chk := rs.NewChunk(nil) + for { + err = rs.Next(ctx, chk) + if err != nil || chk.NumRows() == 0 { + break + } + } + terror.Call(rs.Close) + resultChan <- err +} + +// HandleEvolvePlanTask tries to evolve one plan task. +// It only processes one task at a time because we want each task to use the latest parameters. +func (h *BindHandle) HandleEvolvePlanTask(sctx sessionctx.Context, adminEvolve bool) error { + originalSQL, db, binding := h.getOnePendingVerifyJob() + if originalSQL == "" { + return nil + } + maxTime, startTime, endTime, err := getEvolveParameters(sctx) + if err != nil { + return err + } + if maxTime == 0 || (!timeutil.WithinDayTimePeriod(startTime, endTime, time.Now()) && !adminEvolve) { + return nil + } + sctx.GetSessionVars().UsePlanBaselines = true + currentPlanTime, err := h.getRunningDuration(sctx, db, binding.BindSQL, maxTime) + // If we just return the error to the caller, this job will be retried again and again and cause endless logs, + // since it is still in the bind record. Now we just drop it and if it is actually retryable, + // we will hope for that we can capture this evolve task again. + if err != nil { + _, err = h.DropBindRecord(originalSQL, db, &binding) + return err + } + // If the accepted plan timeouts, it is hard to decide the timeout for verify plan. + // Currently we simply mark the verify plan as `using` if it could run successfully within maxTime. + if currentPlanTime > 0 { + maxTime = time.Duration(float64(currentPlanTime) * verifyTimeoutFactor) + } + sctx.GetSessionVars().UsePlanBaselines = false + verifyPlanTime, err := h.getRunningDuration(sctx, db, binding.BindSQL, maxTime) + if err != nil { + _, err = h.DropBindRecord(originalSQL, db, &binding) + return err + } + if verifyPlanTime == -1 || (float64(verifyPlanTime)*acceptFactor > float64(currentPlanTime)) { + binding.Status = Rejected + digestText, _ := parser.NormalizeDigest(binding.BindSQL) // for log desensitization + logutil.BgLogger().Debug("new plan rejected", zap.String("category", "sql-bind"), + zap.Duration("currentPlanTime", currentPlanTime), + zap.Duration("verifyPlanTime", verifyPlanTime), + zap.String("digestText", digestText), + ) + } else { + binding.Status = Enabled + } + // We don't need to pass the `sctx` because the BindSQL has been validated already. + return h.AddBindRecord(nil, &BindRecord{OriginalSQL: originalSQL, Db: db, Bindings: []Binding{binding}}) +} + +// Clear resets the bind handle. It is only used for test. +func (h *BindHandle) Clear() { + h.bindInfo.Lock() + h.bindInfo.Store(newBindCache()) + h.bindInfo.lastUpdateTime = types.ZeroTimestamp + h.bindInfo.Unlock() + h.invalidBindRecordMap.Store(make(map[string]*bindRecordUpdate)) + h.pendingVerifyBindRecordMap.Store(make(map[string]*bindRecordUpdate)) +} + +// FlushBindings flushes the BindRecord in temp maps to storage and loads them into cache. +func (h *BindHandle) FlushBindings() error { + h.DropInvalidBindRecord() + h.SaveEvolveTasksToStore() + return h.Update(false) +} + +// ReloadBindings clears existing binding cache and do a full load from mysql.bind_info. +// It is used to maintain consistency between cache and mysql.bind_info if the table is deleted or truncated. +func (h *BindHandle) ReloadBindings() error { + h.bindInfo.Lock() + h.bindInfo.Store(newBindCache()) + h.bindInfo.lastUpdateTime = types.ZeroTimestamp + h.bindInfo.Unlock() + return h.Update(true) +} diff --git a/pkg/bindinfo/handle_test.go b/pkg/bindinfo/handle_test.go new file mode 100644 index 0000000000000..984fdb0189be7 --- /dev/null +++ b/pkg/bindinfo/handle_test.go @@ -0,0 +1,609 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package bindinfo_test + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/bindinfo/internal" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/testkit" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +func TestBindingCache(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create global binding for select * from t using select * from t use index(idx);") + tk.MustExec("create database tmp") + tk.MustExec("use tmp") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create global binding for select * from t using select * from t use index(idx);") + + require.Nil(t, dom.BindHandle().Update(false)) + require.Nil(t, dom.BindHandle().Update(false)) + res := tk.MustQuery("show global bindings") + require.Equal(t, 2, len(res.Rows())) + + tk.MustExec("drop global binding for select * from t;") + require.Nil(t, dom.BindHandle().Update(false)) + require.Equal(t, 1, len(dom.BindHandle().GetAllBindRecord())) +} + +func TestBindingLastUpdateTime(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t0;") + tk.MustExec("create table t0(a int, key(a));") + tk.MustExec("create global binding for select * from t0 using select * from t0 use index(a);") + tk.MustExec("admin reload bindings;") + + bindHandle := bindinfo.NewBindHandle(tk.Session()) + err := bindHandle.Update(true) + require.NoError(t, err) + sql, hash := parser.NormalizeDigest("select * from test . t0") + bindData := bindHandle.GetBindRecord(hash.String(), sql, "test") + require.Equal(t, 1, len(bindData.Bindings)) + bind := bindData.Bindings[0] + updateTime := bind.UpdateTime.String() + + rows1 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() + updateTime1 := rows1[0][1] + require.Equal(t, updateTime, updateTime1) + + rows2 := tk.MustQuery("show session status like 'last_plan_binding_update_time';").Rows() + updateTime2 := rows2[0][1] + require.Equal(t, updateTime, updateTime2) + tk.MustQuery(`show global status like 'last_plan_binding_update_time';`).Check(testkit.Rows()) +} + +func TestBindingLastUpdateTimeWithInvalidBind(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + rows0 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() + updateTime0 := rows0[0][1] + require.Equal(t, updateTime0, "0000-00-00 00:00:00") + + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t`', 'select * from `test` . `t` use index(`idx`)', 'test', 'enabled', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("admin reload bindings;") + + rows1 := tk.MustQuery("show status like 'last_plan_binding_update_time';").Rows() + updateTime1 := rows1[0][1] + require.Equal(t, updateTime1, "2000-01-01 09:00:00.000") + + rows2 := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows2, 0) +} + +func TestBindParse(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(i int)") + tk.MustExec("create index index_t on t(i)") + + originSQL := "select * from `test` . `t`" + bindSQL := "select * from `test` . `t` use index(index_t)" + defaultDb := "test" + status := bindinfo.Enabled + charset := "utf8mb4" + collation := "utf8mb4_bin" + source := bindinfo.Manual + mockDigest := "0f644e22c38ecc71d4592c52df127df7f86b6ca7f7c0ee899113b794578f9396" + sql := fmt.Sprintf(`INSERT INTO mysql.bind_info(original_sql,bind_sql,default_db,status,create_time,update_time,charset,collation,source, sql_digest, plan_digest) VALUES ('%s', '%s', '%s', '%s', NOW(), NOW(),'%s', '%s', '%s', '%s', '%s')`, + originSQL, bindSQL, defaultDb, status, charset, collation, source, mockDigest, mockDigest) + tk.MustExec(sql) + bindHandle := bindinfo.NewBindHandle(tk.Session()) + err := bindHandle.Update(true) + require.NoError(t, err) + require.Equal(t, 1, bindHandle.Size()) + + sql, hash := parser.NormalizeDigest("select * from test . t") + bindData := bindHandle.GetBindRecord(hash.String(), sql, "test") + require.NotNil(t, bindData) + require.Equal(t, "select * from `test` . `t`", bindData.OriginalSQL) + bind := bindData.Bindings[0] + require.Equal(t, "select * from `test` . `t` use index(index_t)", bind.BindSQL) + require.Equal(t, "test", bindData.Db) + require.Equal(t, bindinfo.Enabled, bind.Status) + require.Equal(t, "utf8mb4", bind.Charset) + require.Equal(t, "utf8mb4_bin", bind.Collation) + require.NotNil(t, bind.CreateTime) + require.NotNil(t, bind.UpdateTime) + dur, err := bind.SinceUpdateTime() + require.NoError(t, err) + require.GreaterOrEqual(t, int64(dur), int64(0)) + + // Test fields with quotes or slashes. + sql = `CREATE GLOBAL BINDING FOR select * from t where i BETWEEN "a" and "b" USING select * from t use index(index_t) where i BETWEEN "a\nb\rc\td\0e" and 'x'` + tk.MustExec(sql) + tk.MustExec(`DROP global binding for select * from t use index(idx) where i BETWEEN "a\nb\rc\td\0e" and "x"`) + + // Test SetOprStmt. + tk.MustExec(`create binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()`) + tk.MustExec(`drop binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()`) + tk.MustExec(`create binding for select * from t INTERSECT select * from t using select * from t use index(index_t) INTERSECT select * from t use index()`) + tk.MustExec(`drop binding for select * from t INTERSECT select * from t using select * from t use index(index_t) INTERSECT select * from t use index()`) + tk.MustExec(`create binding for select * from t EXCEPT select * from t using select * from t use index(index_t) EXCEPT select * from t use index()`) + tk.MustExec(`drop binding for select * from t EXCEPT select * from t using select * from t use index(index_t) EXCEPT select * from t use index()`) + tk.MustExec(`create binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())`) + tk.MustExec(`drop binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())`) + + // Test Update / Delete. + tk.MustExec("create table t1(a int, b int, c int, key(b), key(c))") + tk.MustExec("create table t2(a int, b int, c int, key(b), key(c))") + tk.MustExec("create binding for delete from t1 where b = 1 and c > 1 using delete /*+ use_index(t1, c) */ from t1 where b = 1 and c > 1") + tk.MustExec("drop binding for delete from t1 where b = 1 and c > 1 using delete /*+ use_index(t1, c) */ from t1 where b = 1 and c > 1") + tk.MustExec("create binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1 using delete /*+ hash_join(t1, t2), use_index(t1, c) */ t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1") + tk.MustExec("drop binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1 using delete /*+ hash_join(t1, t2), use_index(t1, c) */ t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.c = 1") + tk.MustExec("create binding for update t1 set a = 1 where b = 1 and c > 1 using update /*+ use_index(t1, c) */ t1 set a = 1 where b = 1 and c > 1") + tk.MustExec("drop binding for update t1 set a = 1 where b = 1 and c > 1 using update /*+ use_index(t1, c) */ t1 set a = 1 where b = 1 and c > 1") + tk.MustExec("create binding for update t1, t2 set t1.a = 1 where t1.b = t2.b using update /*+ inl_join(t1) */ t1, t2 set t1.a = 1 where t1.b = t2.b") + tk.MustExec("drop binding for update t1, t2 set t1.a = 1 where t1.b = t2.b using update /*+ inl_join(t1) */ t1, t2 set t1.a = 1 where t1.b = t2.b") + // Test Insert / Replace. + tk.MustExec("create binding for insert into t1 select * from t2 where t2.b = 1 and t2.c > 1 using insert into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") + tk.MustExec("drop binding for insert into t1 select * from t2 where t2.b = 1 and t2.c > 1 using insert into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") + tk.MustExec("create binding for replace into t1 select * from t2 where t2.b = 1 and t2.c > 1 using replace into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") + tk.MustExec("drop binding for replace into t1 select * from t2 where t2.b = 1 and t2.c > 1 using replace into t1 select /*+ use_index(t2,c) */ * from t2 where t2.b = 1 and t2.c > 1") + err = tk.ExecToErr("create binding for insert into t1 values(1,1,1) using insert into t1 values(1,1,1)") + require.Equal(t, "create binding only supports INSERT / REPLACE INTO SELECT", err.Error()) + err = tk.ExecToErr("create binding for replace into t1 values(1,1,1) using replace into t1 values(1,1,1)") + require.Equal(t, "create binding only supports INSERT / REPLACE INTO SELECT", err.Error()) + + // Test errors. + tk.MustExec(`drop table if exists t1`) + tk.MustExec("create table t1(i int, s varchar(20))") + _, err = tk.Exec("create global binding for select * from t using select * from t1 use index for join(index_t)") + require.NotNil(t, err, "err %v", err) +} + +func TestEvolveInvalidBindings(t *testing.T) { + originalVal := config.CheckTableBeforeDrop + config.CheckTableBeforeDrop = true + defer func() { + config.CheckTableBeforeDrop = originalVal + }() + + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx_a(a))") + tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t) */ * from t where a > 10") + // Manufacture a rejected binding by hacking mysql.bind_info. + tk.MustExec("insert into mysql.bind_info values('select * from test . t where a > ?', 'SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10', 'test', 'rejected', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + tk.MustQuery("select bind_sql, status from mysql.bind_info where source != 'builtin'").Sort().Check(testkit.Rows( + "SELECT /*+ USE_INDEX(`t` )*/ * FROM `test`.`t` WHERE `a` > 10 enabled", + "SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10 rejected", + )) + // Reload cache from mysql.bind_info. + dom.BindHandle().Clear() + require.Nil(t, dom.BindHandle().Update(true)) + + tk.MustExec("alter table t drop index idx_a") + tk.MustExec("admin evolve bindings") + require.Nil(t, dom.BindHandle().Update(false)) + rows := tk.MustQuery("show global bindings").Sort().Rows() + require.Equal(t, 2, len(rows)) + // Make sure this "enabled" binding is not overrided. + require.Equal(t, "SELECT /*+ USE_INDEX(`t` )*/ * FROM `test`.`t` WHERE `a` > 10", rows[0][1]) + status := rows[0][3].(string) + require.True(t, status == bindinfo.Enabled) + require.Equal(t, "SELECT /*+ USE_INDEX(t,idx_a) */ * FROM test.t WHERE a > 10", rows[1][1]) + status = rows[1][3].(string) + require.True(t, status == bindinfo.Enabled || status == bindinfo.Rejected) + _, sqlDigestWithDB := parser.NormalizeDigest("select * from test.t where a > 10") // test sqlDigest if exists after add columns to mysql.bind_info + require.Equal(t, rows[0][9], sqlDigestWithDB.String()) +} + +func TestSetBindingStatus(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index idx_a(a))") + tk.MustQuery("show global bindings").Check(testkit.Rows()) + tk.MustExec("create global binding for select * from t where a > 10 using select /*+ USE_INDEX(t, idx_a) */ * from t where a > 10") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + tk.MustExec("select * from t where a > 10") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + + tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 There are no bindings can be set the status. Please check the SQL text")) + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + tk.MustExec("select * from t where a > 10") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + + tk.MustExec("set binding disabled for select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Disabled, rows[0][3]) + tk.MustExec("select * from t where a > 10") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + + tk.MustExec("set binding enabled for select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + + tk.MustExec("set binding disabled for select * from t where a > 10") + tk.MustExec("create global binding for select * from t where a > 10 using select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + tk.MustExec("select * from t where a > 10") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + + tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Disabled, rows[0][3]) + tk.MustExec("select * from t where a > 10") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + + tk.MustExec("set binding enabled for select * from t where a > 10 using select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + + tk.MustExec("set binding disabled for select * from t where a > 10 using select * from t where a > 10") + tk.MustExec("drop global binding for select * from t where a > 10 using select * from t where a > 10") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 0) +} + +func TestSetBindingStatusWithoutBindingInCache(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index idx_a(a))") + internal.UtilCleanBindingEnv(tk, dom) + tk.MustQuery("show global bindings").Check(testkit.Rows()) + + // Simulate creating bindings on other machines + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT /*+ USE_INDEX(`t` `idx_a`)*/ * FROM `test`.`t` WHERE `a` > 10', 'test', 'enabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + dom.BindHandle().Clear() + tk.MustExec("set binding disabled for select * from t where a > 10") + tk.MustExec("admin reload bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Disabled, rows[0][3]) + + // clear the mysql.bind_info + internal.UtilCleanBindingEnv(tk, dom) + + // Simulate creating bindings on other machines + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'deleted', '2000-01-01 09:00:00', '2000-01-01 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + tk.MustExec("insert into mysql.bind_info values('select * from `test` . `t` where `a` > ?', 'SELECT * FROM `test`.`t` WHERE `a` > 10', 'test', 'disabled', '2000-01-02 09:00:00', '2000-01-02 09:00:00', '', '','" + + bindinfo.Manual + "', '', '')") + dom.BindHandle().Clear() + tk.MustExec("set binding enabled for select * from t where a > 10") + tk.MustExec("admin reload bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + require.Equal(t, bindinfo.Enabled, rows[0][3]) + + internal.UtilCleanBindingEnv(tk, dom) +} + +var testSQLs = []struct { + createSQL string + overlaySQL string + querySQL string + originSQL string + bindSQL string + dropSQL string + memoryUsage float64 +}{ + { + createSQL: "binding for select * from t where i>100 using select * from t use index(index_t) where i>100", + overlaySQL: "binding for select * from t where i>99 using select * from t use index(index_t) where i>99", + querySQL: "select * from t where i > 30.0", + originSQL: "select * from `test` . `t` where `i` > ?", + bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) WHERE `i` > 99", + dropSQL: "binding for select * from t where i>100", + memoryUsage: float64(167), + }, + { + createSQL: "binding for select * from t union all select * from t using select * from t use index(index_t) union all select * from t use index()", + overlaySQL: "", + querySQL: "select * from t union all select * from t", + originSQL: "select * from `test` . `t` union all select * from `test` . `t`", + bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) UNION ALL SELECT * FROM `test`.`t` USE INDEX ()", + dropSQL: "binding for select * from t union all select * from t", + memoryUsage: float64(237), + }, + { + createSQL: "binding for (select * from t) union all (select * from t) using (select * from t use index(index_t)) union all (select * from t use index())", + overlaySQL: "", + querySQL: "(select * from t) union all (select * from t)", + originSQL: "( select * from `test` . `t` ) union all ( select * from `test` . `t` )", + bindSQL: "(SELECT * FROM `test`.`t` USE INDEX (`index_t`)) UNION ALL (SELECT * FROM `test`.`t` USE INDEX ())", + dropSQL: "binding for (select * from t) union all (select * from t)", + memoryUsage: float64(249), + }, + { + createSQL: "binding for select * from t intersect select * from t using select * from t use index(index_t) intersect select * from t use index()", + overlaySQL: "", + querySQL: "select * from t intersect select * from t", + originSQL: "select * from `test` . `t` intersect select * from `test` . `t`", + bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) INTERSECT SELECT * FROM `test`.`t` USE INDEX ()", + dropSQL: "binding for select * from t intersect select * from t", + memoryUsage: float64(237), + }, + { + createSQL: "binding for select * from t except select * from t using select * from t use index(index_t) except select * from t use index()", + overlaySQL: "", + querySQL: "select * from t except select * from t", + originSQL: "select * from `test` . `t` except select * from `test` . `t`", + bindSQL: "SELECT * FROM `test`.`t` USE INDEX (`index_t`) EXCEPT SELECT * FROM `test`.`t` USE INDEX ()", + dropSQL: "binding for select * from t except select * from t", + memoryUsage: float64(231), + }, + { + createSQL: "binding for select * from t using select /*+ use_index(t,index_t)*/ * from t", + overlaySQL: "", + querySQL: "select * from t ", + originSQL: "select * from `test` . `t`", + bindSQL: "SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t`", + dropSQL: "binding for select * from t", + memoryUsage: float64(166), + }, + { + createSQL: "binding for delete from t where i = 1 using delete /*+ use_index(t,index_t) */ from t where i = 1", + overlaySQL: "", + querySQL: "delete from t where i = 2", + originSQL: "delete from `test` . `t` where `i` = ?", + bindSQL: "DELETE /*+ use_index(`t` `index_t`)*/ FROM `test`.`t` WHERE `i` = 1", + dropSQL: "binding for delete from t where i = 1", + memoryUsage: float64(190), + }, + { + createSQL: "binding for delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 1 using delete /*+ use_index(t,index_t), hash_join(t,t1) */ t, t1 from t inner join t1 on t.s = t1.s where t.i = 1", + overlaySQL: "", + querySQL: "delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 2", + originSQL: "delete `test` . `t` , `test` . `t1` from `test` . `t` join `test` . `t1` on `t` . `s` = `t1` . `s` where `t` . `i` = ?", + bindSQL: "DELETE /*+ use_index(`t` `index_t`) hash_join(`t`, `t1`)*/ `test`.`t`,`test`.`t1` FROM `test`.`t` JOIN `test`.`t1` ON `t`.`s` = `t1`.`s` WHERE `t`.`i` = 1", + dropSQL: "binding for delete t, t1 from t inner join t1 on t.s = t1.s where t.i = 1", + memoryUsage: float64(402), + }, + { + createSQL: "binding for update t set s = 'a' where i = 1 using update /*+ use_index(t,index_t) */ t set s = 'a' where i = 1", + overlaySQL: "", + querySQL: "update t set s='b' where i=2", + originSQL: "update `test` . `t` set `s` = ? where `i` = ?", + bindSQL: "UPDATE /*+ use_index(`t` `index_t`)*/ `test`.`t` SET `s`='a' WHERE `i` = 1", + dropSQL: "binding for update t set s = 'a' where i = 1", + memoryUsage: float64(204), + }, + { + createSQL: "binding for update t, t1 set t.s = 'a' where t.i = t1.i using update /*+ inl_join(t1) */ t, t1 set t.s = 'a' where t.i = t1.i", + overlaySQL: "", + querySQL: "update t , t1 set t.s='b' where t.i=t1.i", + originSQL: "update ( `test` . `t` ) join `test` . `t1` set `t` . `s` = ? where `t` . `i` = `t1` . `i`", + bindSQL: "UPDATE /*+ inl_join(`t1`)*/ (`test`.`t`) JOIN `test`.`t1` SET `t`.`s`='a' WHERE `t`.`i` = `t1`.`i`", + dropSQL: "binding for update t, t1 set t.s = 'a' where t.i = t1.i", + memoryUsage: float64(262), + }, + { + createSQL: "binding for insert into t1 select * from t where t.i = 1 using insert into t1 select /*+ use_index(t,index_t) */ * from t where t.i = 1", + overlaySQL: "", + querySQL: "insert into t1 select * from t where t.i = 2", + originSQL: "insert into `test` . `t1` select * from `test` . `t` where `t` . `i` = ?", + bindSQL: "INSERT INTO `test`.`t1` SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t` WHERE `t`.`i` = 1", + dropSQL: "binding for insert into t1 select * from t where t.i = 1", + memoryUsage: float64(254), + }, + { + createSQL: "binding for replace into t1 select * from t where t.i = 1 using replace into t1 select /*+ use_index(t,index_t) */ * from t where t.i = 1", + overlaySQL: "", + querySQL: "replace into t1 select * from t where t.i = 2", + originSQL: "replace into `test` . `t1` select * from `test` . `t` where `t` . `i` = ?", + bindSQL: "REPLACE INTO `test`.`t1` SELECT /*+ use_index(`t` `index_t`)*/ * FROM `test`.`t` WHERE `t`.`i` = 1", + dropSQL: "binding for replace into t1 select * from t where t.i = 1", + memoryUsage: float64(256), + }, +} + +func TestGlobalBinding(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + for _, testSQL := range testSQLs { + internal.UtilCleanBindingEnv(tk, dom) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(i int, s varchar(20))") + tk.MustExec("create table t1(i int, s varchar(20))") + tk.MustExec("create index index_t on t(i,s)") + + metrics.BindTotalGauge.Reset() + metrics.BindMemoryUsage.Reset() + + _, err := tk.Exec("create global " + testSQL.createSQL) + require.NoError(t, err, "err %v", err) + + if testSQL.overlaySQL != "" { + _, err = tk.Exec("create global " + testSQL.overlaySQL) + require.NoError(t, err) + } + + pb := &dto.Metric{} + err = metrics.BindTotalGauge.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) + require.NoError(t, err) + require.Equal(t, float64(1), pb.GetGauge().GetValue()) + err = metrics.BindMemoryUsage.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) + require.NoError(t, err) + require.Equal(t, testSQL.memoryUsage, pb.GetGauge().GetValue()) + + sql, hash := internal.UtilNormalizeWithDefaultDB(t, testSQL.querySQL) + + bindData := dom.BindHandle().GetBindRecord(hash, sql, "test") + require.NotNil(t, bindData) + require.Equal(t, testSQL.originSQL, bindData.OriginalSQL) + bind := bindData.Bindings[0] + require.Equal(t, testSQL.bindSQL, bind.BindSQL) + require.Equal(t, "test", bindData.Db) + require.Equal(t, bindinfo.Enabled, bind.Status) + require.NotNil(t, bind.Charset) + require.NotNil(t, bind.Collation) + require.NotNil(t, bind.CreateTime) + require.NotNil(t, bind.UpdateTime) + + rs, err := tk.Exec("show global bindings") + require.NoError(t, err) + chk := rs.NewChunk(nil) + err = rs.Next(context.TODO(), chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, testSQL.originSQL, row.GetString(0)) + require.Equal(t, testSQL.bindSQL, row.GetString(1)) + require.Equal(t, "test", row.GetString(2)) + require.Equal(t, bindinfo.Enabled, row.GetString(3)) + require.NotNil(t, row.GetTime(4)) + require.NotNil(t, row.GetTime(5)) + require.NotNil(t, row.GetString(6)) + require.NotNil(t, row.GetString(7)) + + bindHandle := bindinfo.NewBindHandle(tk.Session()) + err = bindHandle.Update(true) + require.NoError(t, err) + require.Equal(t, 1, bindHandle.Size()) + + bindData = bindHandle.GetBindRecord(hash, sql, "test") + require.NotNil(t, bindData) + require.Equal(t, testSQL.originSQL, bindData.OriginalSQL) + bind = bindData.Bindings[0] + require.Equal(t, testSQL.bindSQL, bind.BindSQL) + require.Equal(t, "test", bindData.Db) + require.Equal(t, bindinfo.Enabled, bind.Status) + require.NotNil(t, bind.Charset) + require.NotNil(t, bind.Collation) + require.NotNil(t, bind.CreateTime) + require.NotNil(t, bind.UpdateTime) + + _, err = tk.Exec("drop global " + testSQL.dropSQL) + require.Equal(t, uint64(1), tk.Session().AffectedRows()) + require.NoError(t, err) + bindData = dom.BindHandle().GetBindRecord(hash, sql, "test") + require.Nil(t, bindData) + + err = metrics.BindTotalGauge.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) + require.NoError(t, err) + require.Equal(t, float64(0), pb.GetGauge().GetValue()) + err = metrics.BindMemoryUsage.WithLabelValues(metrics.ScopeGlobal, bindinfo.Enabled).Write(pb) + require.NoError(t, err) + // From newly created global bind handle. + require.Equal(t, testSQL.memoryUsage, pb.GetGauge().GetValue()) + + bindHandle = bindinfo.NewBindHandle(tk.Session()) + err = bindHandle.Update(true) + require.NoError(t, err) + require.Equal(t, 0, bindHandle.Size()) + + bindData = bindHandle.GetBindRecord(hash, sql, "test") + require.Nil(t, bindData) + + rs, err = tk.Exec("show global bindings") + require.NoError(t, err) + chk = rs.NewChunk(nil) + err = rs.Next(context.TODO(), chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + + _, err = tk.Exec("delete from mysql.bind_info where source != 'builtin'") + require.NoError(t, err) + } +} + +func TestOutdatedInfoSchema(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create global binding for select * from t using select * from t use index(idx)") + require.Nil(t, dom.BindHandle().Update(false)) + internal.UtilCleanBindingEnv(tk, dom) + tk.MustExec("create global binding for select * from t using select * from t use index(idx)") +} + +func TestReloadBindings(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create global binding for select * from t using select * from t use index(idx)") + rows := tk.MustQuery("show global bindings").Rows() + require.Equal(t, 1, len(rows)) + rows = tk.MustQuery("select * from mysql.bind_info where source != 'builtin'").Rows() + require.Equal(t, 1, len(rows)) + tk.MustExec("delete from mysql.bind_info where source != 'builtin'") + require.Nil(t, dom.BindHandle().Update(false)) + rows = tk.MustQuery("show global bindings").Rows() + require.Equal(t, 1, len(rows)) + require.Nil(t, dom.BindHandle().Update(true)) + rows = tk.MustQuery("show global bindings").Rows() + require.Equal(t, 1, len(rows)) + tk.MustExec("admin reload bindings") + rows = tk.MustQuery("show global bindings").Rows() + require.Equal(t, 0, len(rows)) +} diff --git a/pkg/bindinfo/internal/BUILD.bazel b/pkg/bindinfo/internal/BUILD.bazel new file mode 100644 index 0000000000000..17dd8231256dd --- /dev/null +++ b/pkg/bindinfo/internal/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/bindinfo/internal", + visibility = ["//pkg/bindinfo:__subpackages__"], + deps = [ + "//pkg/domain", + "//pkg/parser", + "//pkg/testkit", + "//pkg/util/parser", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/bindinfo/internal/testutil.go b/pkg/bindinfo/internal/testutil.go new file mode 100644 index 0000000000000..616acef986314 --- /dev/null +++ b/pkg/bindinfo/internal/testutil.go @@ -0,0 +1,40 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/testkit" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/stretchr/testify/require" +) + +// UtilCleanBindingEnv cleans the binding environment. +func UtilCleanBindingEnv(tk *testkit.TestKit, dom *domain.Domain) { + tk.MustExec("delete from mysql.bind_info where source != 'builtin'") + dom.BindHandle().Clear() +} + +// UtilNormalizeWithDefaultDB normalizes the SQL and returns the normalized SQL and its digest. +func UtilNormalizeWithDefaultDB(t *testing.T, sql string) (normalized, digest string) { + testParser := parser.New() + stmt, err := testParser.ParseOneStmt(sql, "", "") + require.NoError(t, err) + normalized, digestResult := parser.NormalizeDigestForBinding(utilparser.RestoreWithDefaultDB(stmt, "test", "")) + return normalized, digestResult.String() +} diff --git a/pkg/bindinfo/main_test.go b/pkg/bindinfo/main_test.go new file mode 100644 index 0000000000000..e812dc23429d8 --- /dev/null +++ b/pkg/bindinfo/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package bindinfo_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/bindinfo/optimize_test.go b/pkg/bindinfo/optimize_test.go new file mode 100644 index 0000000000000..fcb10300fdc3f --- /dev/null +++ b/pkg/bindinfo/optimize_test.go @@ -0,0 +1,38 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package bindinfo_test + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestOptimizeOnlyOnce(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idxa(a))") + tk.MustExec("create global binding for select * from t using select * from t use index(idxa)") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/checkOptimizeCountOne", "return(\"select * from t\")")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/checkOptimizeCountOne")) + }() + tk.MustQuery("select * from t").Check(testkit.Rows()) +} diff --git a/bindinfo/session_handle.go b/pkg/bindinfo/session_handle.go similarity index 94% rename from bindinfo/session_handle.go rename to pkg/bindinfo/session_handle.go index 007b654974f42..ba34cf0b50c13 100644 --- a/bindinfo/session_handle.go +++ b/pkg/bindinfo/session_handle.go @@ -20,14 +20,14 @@ import ( "strings" "time" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/bindinfo/session_handle_test.go b/pkg/bindinfo/session_handle_test.go similarity index 98% rename from bindinfo/session_handle_test.go rename to pkg/bindinfo/session_handle_test.go index f30e8089c8f53..82633bb6c0eb5 100644 --- a/bindinfo/session_handle_test.go +++ b/pkg/bindinfo/session_handle_test.go @@ -20,14 +20,14 @@ import ( "testing" "time" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/bindinfo/internal" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/bindinfo/internal" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/stmtsummary" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" ) diff --git a/pkg/bindinfo/stat.go b/pkg/bindinfo/stat.go new file mode 100644 index 0000000000000..50bbbfca455ec --- /dev/null +++ b/pkg/bindinfo/stat.go @@ -0,0 +1,40 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package bindinfo + +import ( + "github.com/pingcap/tidb/pkg/sessionctx/variable" +) + +var ( + lastPlanBindingUpdateTime = "last_plan_binding_update_time" +) + +// GetScope gets the status variables scope. +func (*BindHandle) GetScope(_ string) variable.ScopeFlag { + return variable.ScopeSession +} + +// Stats returns the server statistics. +func (h *BindHandle) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { + h.bindInfo.Lock() + defer func() { + h.bindInfo.Unlock() + }() + m := make(map[string]interface{}) + m[lastPlanBindingUpdateTime] = h.bindInfo.lastUpdateTime.String() + + return m, nil +} diff --git a/bindinfo/temptable_test.go b/pkg/bindinfo/temptable_test.go similarity index 98% rename from bindinfo/temptable_test.go rename to pkg/bindinfo/temptable_test.go index a3e0ff10dcd58..50d540e909171 100644 --- a/bindinfo/temptable_test.go +++ b/pkg/bindinfo/temptable_test.go @@ -18,8 +18,8 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/testkit" ) // TestSelectBindingOnGlobalTempTableProhibited covers https://github.com/pingcap/tidb/issues/26377 diff --git a/pkg/bindinfo/tests/BUILD.bazel b/pkg/bindinfo/tests/BUILD.bazel new file mode 100644 index 0000000000000..fa3e6b4c833d6 --- /dev/null +++ b/pkg/bindinfo/tests/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tests_test", + timeout = "short", + srcs = [ + "bind_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 28, + deps = [ + "//pkg/bindinfo", + "//pkg/bindinfo/internal", + "//pkg/config", + "//pkg/domain", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/parser", + "//pkg/util/stmtsummary", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/bindinfo/tests/bind_test.go b/pkg/bindinfo/tests/bind_test.go similarity index 99% rename from bindinfo/tests/bind_test.go rename to pkg/bindinfo/tests/bind_test.go index 2a6ca5de40554..b13164011457a 100644 --- a/bindinfo/tests/bind_test.go +++ b/pkg/bindinfo/tests/bind_test.go @@ -20,18 +20,18 @@ import ( "strconv" "testing" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/bindinfo/internal" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/bindinfo/internal" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/pingcap/tidb/pkg/util/stmtsummary" "github.com/stretchr/testify/require" ) diff --git a/pkg/bindinfo/tests/main_test.go b/pkg/bindinfo/tests/main_test.go new file mode 100644 index 0000000000000..ff908964a031a --- /dev/null +++ b/pkg/bindinfo/tests/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/config/BUILD.bazel b/pkg/config/BUILD.bazel new file mode 100644 index 0000000000000..d56e963b0ff62 --- /dev/null +++ b/pkg/config/BUILD.bazel @@ -0,0 +1,51 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "config", + srcs = [ + "config.go", + "config_util.go", + "const.go", + ], + importpath = "github.com/pingcap/tidb/pkg/config", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/streamhelper/config", + "//pkg/parser/terror", + "//pkg/util/logutil", + "//pkg/util/tiflashcompute", + "//pkg/util/tikvutil", + "//pkg/util/versioninfo", + "@com_github_burntsushi_toml//:toml", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//config", + "@com_github_uber_jaeger_client_go//config", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "config_test", + timeout = "short", + srcs = [ + "config_test.go", + "config_util_test.go", + "main_test.go", + ], + data = glob(["**"]), + embed = [":config"], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/logutil", + "@com_github_burntsushi_toml//:toml", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_uber_jaeger_client_go//config", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/config/OWNERS b/pkg/config/OWNERS similarity index 100% rename from config/OWNERS rename to pkg/config/OWNERS diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000000000..6b29b8c5ee8b7 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,1554 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package config + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "flag" + "fmt" + "math" + "os" + "os/user" + "path/filepath" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/BurntSushi/toml" + "github.com/pingcap/errors" + zaplog "github.com/pingcap/log" + logbackupconf "github.com/pingcap/tidb/br/pkg/streamhelper/config" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/util/tikvutil" + "github.com/pingcap/tidb/pkg/util/versioninfo" + tikvcfg "github.com/tikv/client-go/v2/config" + tracing "github.com/uber/jaeger-client-go/config" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +// Config number limitations +const ( + MaxLogFileSize = 4096 // MB + // MaxTxnEntrySize is the max value of TxnEntrySizeLimit. + MaxTxnEntrySizeLimit = 120 * 1024 * 1024 // 120MB + // DefTxnEntrySizeLimit is the default value of TxnEntrySizeLimit. + DefTxnEntrySizeLimit = 6 * 1024 * 1024 + // DefTxnTotalSizeLimit is the default value of TxnTxnTotalSizeLimit. + DefTxnTotalSizeLimit = 100 * 1024 * 1024 + SuperLargeTxnSize uint64 = 100 * 1024 * 1024 * 1024 * 1024 // 100T, we expect a txn can never be this large + // DefMaxIndexLength is the maximum index length(in bytes). This value is consistent with MySQL. + DefMaxIndexLength = 3072 + // DefMaxOfMaxIndexLength is the maximum index length(in bytes) for TiDB v3.0.7 and previous version. + DefMaxOfMaxIndexLength = 3072 * 4 + // DefIndexLimit is the limitation of index on a single table. This value is consistent with MySQL. + DefIndexLimit = 64 + // DefMaxOfIndexLimit is the maximum limitation of index on a single table for TiDB. + DefMaxOfIndexLimit = 64 * 8 + // DefPort is the default port of TiDB + DefPort = 4000 + // DefStatusPort is the default status port of TiDB + DefStatusPort = 10080 + // DefHost is the default host of TiDB + DefHost = "0.0.0.0" + // DefStatusHost is the default status host of TiDB + DefStatusHost = "0.0.0.0" + // DefTableColumnCountLimit is limit of the number of columns in a table + DefTableColumnCountLimit = 1017 + // DefMaxOfTableColumnCountLimit is maximum limitation of the number of columns in a table + DefMaxOfTableColumnCountLimit = 4096 + // DefStatsLoadConcurrencyLimit is limit of the concurrency of stats-load + DefStatsLoadConcurrencyLimit = 1 + // DefMaxOfStatsLoadConcurrencyLimit is maximum limitation of the concurrency of stats-load + DefMaxOfStatsLoadConcurrencyLimit = 128 + // DefStatsLoadQueueSizeLimit is limit of the size of stats-load request queue + DefStatsLoadQueueSizeLimit = 1 + // DefMaxOfStatsLoadQueueSizeLimit is maximum limitation of the size of stats-load request queue + DefMaxOfStatsLoadQueueSizeLimit = 100000 + // DefDDLSlowOprThreshold sets log DDL operations whose execution time exceeds the threshold value. + DefDDLSlowOprThreshold = 300 + // DefExpensiveQueryTimeThreshold indicates the time threshold of expensive query. + DefExpensiveQueryTimeThreshold = 60 + // DefExpensiveTxnTimeThreshold indicates the time threshold of expensive txn. + DefExpensiveTxnTimeThreshold = 600 + // DefMemoryUsageAlarmRatio is the threshold triggering an alarm which the memory usage of tidb-server instance exceeds. + DefMemoryUsageAlarmRatio = 0.8 + // DefTempDir is the default temporary directory path for TiDB. + DefTempDir = "/tmp/tidb" + // DefAuthTokenRefreshInterval is the default time interval to refresh tidb auth token. + DefAuthTokenRefreshInterval = time.Hour + // EnvVarKeyspaceName is the system env name for keyspace name. + EnvVarKeyspaceName = "KEYSPACE_NAME" +) + +// Valid config maps +var ( + ValidStorage = map[string]bool{ + "mocktikv": true, + "tikv": true, + "unistore": true, + } + // CheckTableBeforeDrop enable to execute `admin check table` before `drop table`. + CheckTableBeforeDrop = false + // checkBeforeDropLDFlag is a go build flag. + checkBeforeDropLDFlag = "None" + // tempStorageDirName is the default temporary storage dir name by base64 encoding a string `port/statusPort` + tempStorageDirName = encodeDefTempStorageDir(os.TempDir(), DefHost, DefStatusHost, DefPort, DefStatusPort) +) + +// InstanceConfigSection indicates a config session that has options moved to [instance] session. +type InstanceConfigSection struct { + // SectionName indicates the origin section name. + SectionName string + // NameMappings maps the origin name to the name in [instance]. + NameMappings map[string]string +} + +var ( + // sectionMovedToInstance records all config section and options that should be moved to [instance]. + sectionMovedToInstance = []InstanceConfigSection{ + { + "", + map[string]string{ + "check-mb4-value-in-utf8": "tidb_check_mb4_value_in_utf8", + "enable-collect-execution-info": "tidb_enable_collect_execution_info", + "max-server-connections": "max_connections", + "run-ddl": "tidb_enable_ddl", + }, + }, + { + "log", + map[string]string{ + "enable-slow-log": "tidb_enable_slow_log", + "slow-threshold": "tidb_slow_log_threshold", + "record-plan-in-slow-log": "tidb_record_plan_in_slow_log", + }, + }, + { + "performance", + map[string]string{ + "force-priority": "tidb_force_priority", + "memory-usage-alarm-ratio": "tidb_memory_usage_alarm_ratio", + }, + }, + { + "plugin", + map[string]string{ + "load": "plugin_load", + "dir": "plugin_dir", + }, + }, + } + + // ConflictOptions indicates the conflict config options existing in both [instance] and other sections in config file. + ConflictOptions []InstanceConfigSection + + // DeprecatedOptions indicates the config options existing in some other sections in config file. + // They should be moved to [instance] section. + DeprecatedOptions []InstanceConfigSection + + // TikvConfigLock protects against concurrent tikv config refresh + TikvConfigLock sync.Mutex +) + +// Config contains configuration options. +type Config struct { + Host string `toml:"host" json:"host"` + AdvertiseAddress string `toml:"advertise-address" json:"advertise-address"` + Port uint `toml:"port" json:"port"` + Cors string `toml:"cors" json:"cors"` + Store string `toml:"store" json:"store"` + Path string `toml:"path" json:"path"` + Socket string `toml:"socket" json:"socket"` + Lease string `toml:"lease" json:"lease"` + SplitTable bool `toml:"split-table" json:"split-table"` + TokenLimit uint `toml:"token-limit" json:"token-limit"` + TempDir string `toml:"temp-dir" json:"temp-dir"` + TempStoragePath string `toml:"tmp-storage-path" json:"tmp-storage-path"` + // TempStorageQuota describe the temporary storage Quota during query exector when TiDBEnableTmpStorageOnOOM is enabled + // If the quota exceed the capacity of the TempStoragePath, the tidb-server would exit with fatal error + TempStorageQuota int64 `toml:"tmp-storage-quota" json:"tmp-storage-quota"` // Bytes + TxnLocalLatches tikvcfg.TxnLocalLatches `toml:"-" json:"-"` + ServerVersion string `toml:"server-version" json:"server-version"` + VersionComment string `toml:"version-comment" json:"version-comment"` + TiDBEdition string `toml:"tidb-edition" json:"tidb-edition"` + TiDBReleaseVersion string `toml:"tidb-release-version" json:"tidb-release-version"` + KeyspaceName string `toml:"keyspace-name" json:"keyspace-name"` + Log Log `toml:"log" json:"log"` + Instance Instance `toml:"instance" json:"instance"` + Security Security `toml:"security" json:"security"` + Status Status `toml:"status" json:"status"` + Performance Performance `toml:"performance" json:"performance"` + PreparedPlanCache PreparedPlanCache `toml:"prepared-plan-cache" json:"prepared-plan-cache"` + OpenTracing OpenTracing `toml:"opentracing" json:"opentracing"` + ProxyProtocol ProxyProtocol `toml:"proxy-protocol" json:"proxy-protocol"` + PDClient tikvcfg.PDClient `toml:"pd-client" json:"pd-client"` + TiKVClient tikvcfg.TiKVClient `toml:"tikv-client" json:"tikv-client"` + Binlog Binlog `toml:"binlog" json:"binlog"` + CompatibleKillQuery bool `toml:"compatible-kill-query" json:"compatible-kill-query"` + PessimisticTxn PessimisticTxn `toml:"pessimistic-txn" json:"pessimistic-txn"` + MaxIndexLength int `toml:"max-index-length" json:"max-index-length"` + IndexLimit int `toml:"index-limit" json:"index-limit"` + TableColumnCountLimit uint32 `toml:"table-column-count-limit" json:"table-column-count-limit"` + GracefulWaitBeforeShutdown int `toml:"graceful-wait-before-shutdown" json:"graceful-wait-before-shutdown"` + // AlterPrimaryKey is used to control alter primary key feature. + AlterPrimaryKey bool `toml:"alter-primary-key" json:"alter-primary-key"` + // TreatOldVersionUTF8AsUTF8MB4 is use to treat old version table/column UTF8 charset as UTF8MB4. This is for compatibility. + // Currently not support dynamic modify, because this need to reload all old version schema. + TreatOldVersionUTF8AsUTF8MB4 bool `toml:"treat-old-version-utf8-as-utf8mb4" json:"treat-old-version-utf8-as-utf8mb4"` + // EnableTableLock indicate whether enable table lock. + // TODO: remove this after table lock features stable. + EnableTableLock bool `toml:"enable-table-lock" json:"enable-table-lock"` + DelayCleanTableLock uint64 `toml:"delay-clean-table-lock" json:"delay-clean-table-lock"` + SplitRegionMaxNum uint64 `toml:"split-region-max-num" json:"split-region-max-num"` + TopSQL TopSQL `toml:"top-sql" json:"top-sql"` + // RepairMode indicates that the TiDB is in the repair mode for table meta. + RepairMode bool `toml:"repair-mode" json:"repair-mode"` + RepairTableList []string `toml:"repair-table-list" json:"repair-table-list"` + // IsolationRead indicates that the TiDB reads data from which isolation level(engine and label). + IsolationRead IsolationRead `toml:"isolation-read" json:"isolation-read"` + // NewCollationsEnabledOnFirstBootstrap indicates if the new collations are enabled, it effects only when a TiDB cluster bootstrapped on the first time. + NewCollationsEnabledOnFirstBootstrap bool `toml:"new_collations_enabled_on_first_bootstrap" json:"new_collations_enabled_on_first_bootstrap"` + // Experimental contains parameters for experimental features. + Experimental Experimental `toml:"experimental" json:"experimental"` + // SkipRegisterToDashboard tells TiDB don't register itself to the dashboard. + SkipRegisterToDashboard bool `toml:"skip-register-to-dashboard" json:"skip-register-to-dashboard"` + // EnableTelemetry enables the usage data report to PingCAP. + EnableTelemetry bool `toml:"enable-telemetry" json:"enable-telemetry"` + // Labels indicates the labels set for the tidb server. The labels describe some specific properties for the tidb + // server like `zone`/`rack`/`host`. Currently, labels won't affect the tidb server except for some special + // label keys. Now we have following special keys: + // 1. 'group' is a special label key which should be automatically set by tidb-operator. We don't suggest + // users to set 'group' in labels. + // 2. 'zone' is a special key that indicates the DC location of this tidb-server. If it is set, the value for this + // key will be the default value of the session variable `txn_scope` for this tidb-server. + Labels map[string]string `toml:"labels" json:"labels"` + // EnableGlobalIndex enables creating global index. + EnableGlobalIndex bool `toml:"enable-global-index" json:"enable-global-index"` + // DeprecateIntegerDisplayWidth indicates whether deprecating the max display length for integer. + DeprecateIntegerDisplayWidth bool `toml:"deprecate-integer-display-length" json:"deprecate-integer-display-length"` + // EnableEnumLengthLimit indicates whether the enum/set element length is limited. + // According to MySQL 8.0 Refman: + // The maximum supported length of an individual SET element is M <= 255 and (M x w) <= 1020, + // where M is the element literal length and w is the number of bytes required for the maximum-length character in the character set. + // See https://dev.mysql.com/doc/refman/8.0/en/string-type-syntax.html for more details. + EnableEnumLengthLimit bool `toml:"enable-enum-length-limit" json:"enable-enum-length-limit"` + // StoresRefreshInterval indicates the interval of refreshing stores info, the unit is second. + StoresRefreshInterval uint64 `toml:"stores-refresh-interval" json:"stores-refresh-interval"` + // EnableTCP4Only enables net.Listen("tcp4",...) + // Note that: it can make lvs with toa work and thus tidb can get real client ip. + EnableTCP4Only bool `toml:"enable-tcp4-only" json:"enable-tcp4-only"` + // The client will forward the requests through the follower + // if one of the following conditions happens: + // 1. there is a network partition problem between TiDB and PD leader. + // 2. there is a network partition problem between TiDB and TiKV leader. + EnableForwarding bool `toml:"enable-forwarding" json:"enable-forwarding"` + // MaxBallastObjectSize set the max size of the ballast object, the unit is byte. + // The default value is the smallest of the following two values: 2GB or + // one quarter of the total physical memory in the current system. + MaxBallastObjectSize int `toml:"max-ballast-object-size" json:"max-ballast-object-size"` + // BallastObjectSize set the initial size of the ballast object, the unit is byte. + BallastObjectSize int `toml:"ballast-object-size" json:"ballast-object-size"` + TrxSummary TrxSummary `toml:"transaction-summary" json:"transaction-summary"` + // EnableGlobalKill indicates whether to enable global kill. + EnableGlobalKill bool `toml:"enable-global-kill" json:"enable-global-kill"` + // Enable32BitsConnectionID indicates whether to enable 32bits connection ID for global kill. + Enable32BitsConnectionID bool `toml:"enable-32bits-connection-id" json:"enable-32bits-connection-id"` + // InitializeSQLFile is a file that will be executed after first bootstrap only. + // It can be used to set GLOBAL system variable values + InitializeSQLFile string `toml:"initialize-sql-file" json:"initialize-sql-file"` + + // The following items are deprecated. We need to keep them here temporarily + // to support the upgrade process. They can be removed in future. + + // EnableBatchDML, MemQuotaQuery, OOMAction unused since bootstrap v90 + EnableBatchDML bool `toml:"enable-batch-dml" json:"enable-batch-dml"` + MemQuotaQuery int64 `toml:"mem-quota-query" json:"mem-quota-query"` + OOMAction string `toml:"oom-action" json:"oom-action"` + + // OOMUseTmpStorage unused since bootstrap v93 + OOMUseTmpStorage bool `toml:"oom-use-tmp-storage" json:"oom-use-tmp-storage"` + + // These items are deprecated because they are turned into instance system variables. + CheckMb4ValueInUTF8 AtomicBool `toml:"check-mb4-value-in-utf8" json:"check-mb4-value-in-utf8"` + EnableCollectExecutionInfo bool `toml:"enable-collect-execution-info" json:"enable-collect-execution-info"` + Plugin Plugin `toml:"plugin" json:"plugin"` + MaxServerConnections uint32 `toml:"max-server-connections" json:"max-server-connections"` + RunDDL bool `toml:"run-ddl" json:"run-ddl"` + + // These configs are related to disaggregated-tiflash mode. + DisaggregatedTiFlash bool `toml:"disaggregated-tiflash" json:"disaggregated-tiflash"` + TiFlashComputeAutoScalerType string `toml:"autoscaler-type" json:"autoscaler-type"` + TiFlashComputeAutoScalerAddr string `toml:"autoscaler-addr" json:"autoscaler-addr"` + IsTiFlashComputeFixedPool bool `toml:"is-tiflashcompute-fixed-pool" json:"is-tiflashcompute-fixed-pool"` + AutoScalerClusterID string `toml:"autoscaler-cluster-id" json:"autoscaler-cluster-id"` + UseAutoScaler bool `toml:"use-autoscaler" json:"use-autoscaler"` + + // TiDBMaxReuseChunk indicates max cached chunk num + TiDBMaxReuseChunk uint32 `toml:"tidb-max-reuse-chunk" json:"tidb-max-reuse-chunk"` + // TiDBMaxReuseColumn indicates max cached column num + TiDBMaxReuseColumn uint32 `toml:"tidb-max-reuse-column" json:"tidb-max-reuse-column"` + // TiDBEnableExitCheck indicates whether exit-checking in domain for background process + TiDBEnableExitCheck bool `toml:"tidb-enable-exit-check" json:"tidb-enable-exit-check"` + + // InMemSlowQueryTopNNum indicates the number of TopN slow queries stored in memory. + InMemSlowQueryTopNNum int `toml:"in-mem-slow-query-topn-num" json:"in-mem-slow-query-topn-num"` + // InMemSlowQueryRecentNum indicates the number of recent slow queries stored in memory. + InMemSlowQueryRecentNum int `toml:"in-mem-slow-query-recent-num" json:"in-mem-slow-query-recent-num"` +} + +// UpdateTempStoragePath is to update the `TempStoragePath` if port/statusPort was changed +// and the `tmp-storage-path` was not specified in the conf.toml or was specified the same as the default value. +func (c *Config) UpdateTempStoragePath() { + if c.TempStoragePath == tempStorageDirName { + c.TempStoragePath = encodeDefTempStorageDir(os.TempDir(), c.Host, c.Status.StatusHost, c.Port, c.Status.StatusPort) + } else { + c.TempStoragePath = encodeDefTempStorageDir(c.TempStoragePath, c.Host, c.Status.StatusHost, c.Port, c.Status.StatusPort) + } +} + +// GetTiKVConfig returns configuration options from tikvcfg +func (c *Config) GetTiKVConfig() *tikvcfg.Config { + return &tikvcfg.Config{ + CommitterConcurrency: int(tikvutil.CommitterConcurrency.Load()), + MaxTxnTTL: c.Performance.MaxTxnTTL, + TiKVClient: c.TiKVClient, + Security: c.Security.ClusterSecurity(), + PDClient: c.PDClient, + PessimisticTxn: tikvcfg.PessimisticTxn{MaxRetryCount: c.PessimisticTxn.MaxRetryCount}, + TxnLocalLatches: c.TxnLocalLatches, + StoresRefreshInterval: c.StoresRefreshInterval, + OpenTracingEnable: c.OpenTracing.Enable, + Path: c.Path, + EnableForwarding: c.EnableForwarding, + TxnScope: c.Labels["zone"], + } +} + +func encodeDefTempStorageDir(tempDir string, host, statusHost string, port, statusPort uint) string { + dirName := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v/%v:%v", host, port, statusHost, statusPort))) + osUID := "" + currentUser, err := user.Current() + if err == nil { + osUID = currentUser.Uid + } + return filepath.Join(tempDir, osUID+"_tidb", dirName, "tmp-storage") +} + +// nullableBool defaults unset bool options to unset instead of false, which enables us to know if the user has set 2 +// conflict options at the same time. +type nullableBool struct { + IsValid bool + IsTrue bool +} + +var ( + nbUnset = nullableBool{false, false} + nbFalse = nullableBool{true, false} + nbTrue = nullableBool{true, true} +) + +func (b *nullableBool) toBool() bool { + return b.IsValid && b.IsTrue +} + +func (b nullableBool) MarshalJSON() ([]byte, error) { + switch b { + case nbTrue: + return json.Marshal(true) + case nbFalse: + return json.Marshal(false) + default: + return json.Marshal(nil) + } +} + +func (b *nullableBool) UnmarshalText(text []byte) error { + str := string(text) + switch str { + case "", "null": + *b = nbUnset + return nil + case "true": + *b = nbTrue + case "false": + *b = nbFalse + default: + *b = nbUnset + return errors.New("Invalid value for bool type: " + str) + } + return nil +} + +func (b nullableBool) MarshalText() ([]byte, error) { + if !b.IsValid { + return []byte(""), nil + } + if b.IsTrue { + return []byte("true"), nil + } + return []byte("false"), nil +} + +func (b *nullableBool) UnmarshalJSON(data []byte) error { + var err error + var v interface{} + if err = json.Unmarshal(data, &v); err != nil { + return err + } + switch raw := v.(type) { + case bool: + *b = nullableBool{true, raw} + default: + *b = nbUnset + } + return err +} + +// AtomicBool is a helper type for atomic operations on a boolean value. +type AtomicBool struct { + atomicutil.Bool +} + +// NewAtomicBool creates an AtomicBool. +func NewAtomicBool(v bool) *AtomicBool { + return &AtomicBool{*atomicutil.NewBool(v)} +} + +// MarshalText implements the encoding.TextMarshaler interface. +func (b AtomicBool) MarshalText() ([]byte, error) { + if b.Load() { + return []byte("true"), nil + } + return []byte("false"), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +func (b *AtomicBool) UnmarshalText(text []byte) error { + str := string(text) + switch str { + case "", "null": + *b = AtomicBool{*atomicutil.NewBool(false)} + case "true": + *b = AtomicBool{*atomicutil.NewBool(true)} + case "false": + *b = AtomicBool{*atomicutil.NewBool(false)} + default: + *b = AtomicBool{*atomicutil.NewBool(false)} + return errors.New("Invalid value for bool type: " + str) + } + return nil +} + +// LogBackup is the config for log backup service. +// For now, it includes the embed advancer. +type LogBackup struct { + Advancer logbackupconf.Config `toml:"advancer" json:"advancer"` + Enabled bool `toml:"enabled" json:"enabled"` +} + +// Log is the log section of config. +type Log struct { + // Log level. + Level string `toml:"level" json:"level"` + // Log format, one of json or text. + Format string `toml:"format" json:"format"` + // Disable automatic timestamps in output. Deprecated: use EnableTimestamp instead. + DisableTimestamp nullableBool `toml:"disable-timestamp" json:"disable-timestamp"` + // EnableTimestamp enables automatic timestamps in log output. + EnableTimestamp nullableBool `toml:"enable-timestamp" json:"enable-timestamp"` + // DisableErrorStack stops annotating logs with the full stack error + // message. Deprecated: use EnableErrorStack instead. + DisableErrorStack nullableBool `toml:"disable-error-stack" json:"disable-error-stack"` + // EnableErrorStack enables annotating logs with the full stack error + // message. + EnableErrorStack nullableBool `toml:"enable-error-stack" json:"enable-error-stack"` + // File log config. + File logutil.FileLogConfig `toml:"file" json:"file"` + + SlowQueryFile string `toml:"slow-query-file" json:"slow-query-file"` + // ExpensiveThreshold is deprecated. + ExpensiveThreshold uint `toml:"expensive-threshold" json:"expensive-threshold"` + + // The following items are deprecated. We need to keep them here temporarily + // to support the upgrade process. They can be removed in future. + + // QueryLogMaxLen unused since bootstrap v90 + QueryLogMaxLen uint64 `toml:"query-log-max-len" json:"query-log-max-len"` + // EnableSlowLog, SlowThreshold, RecordPlanInSlowLog are deprecated. + EnableSlowLog AtomicBool `toml:"enable-slow-log" json:"enable-slow-log"` + SlowThreshold uint64 `toml:"slow-threshold" json:"slow-threshold"` + RecordPlanInSlowLog uint32 `toml:"record-plan-in-slow-log" json:"record-plan-in-slow-log"` + + // Make tidb panic if write log operation hang in `Timeout` seconds + Timeout int `toml:"timeout" json:"timeout"` +} + +// Instance is the section of instance scope system variables. +type Instance struct { + // These variables only exist in [instance] section. + + // TiDBGeneralLog is used to log every query in the server in info level. + TiDBGeneralLog bool `toml:"tidb_general_log" json:"tidb_general_log"` + // EnablePProfSQLCPU is used to add label sql label to pprof result. + EnablePProfSQLCPU bool `toml:"tidb_pprof_sql_cpu" json:"tidb_pprof_sql_cpu"` + // DDLSlowOprThreshold sets log DDL operations whose execution time exceeds the threshold value. + DDLSlowOprThreshold uint32 `toml:"ddl_slow_threshold" json:"ddl_slow_threshold"` + // ExpensiveQueryTimeThreshold indicates the time threshold of expensive query. + ExpensiveQueryTimeThreshold uint64 `toml:"tidb_expensive_query_time_threshold" json:"tidb_expensive_query_time_threshold"` + // ExpensiveTxnTimeThreshold indicates the time threshold of expensive transaction. + ExpensiveTxnTimeThreshold uint64 `toml:"tidb_expensive_txn_time_threshold" json:"tidb_expensive_txn_time_threshold"` + // StmtSummaryEnablePersistent indicates whether to enable file persistence for stmtsummary. + StmtSummaryEnablePersistent bool `toml:"tidb_stmt_summary_enable_persistent" json:"tidb_stmt_summary_enable_persistent"` + // StmtSummaryFilename indicates the file name written by stmtsummary + // when StmtSummaryEnablePersistent is true. + StmtSummaryFilename string `toml:"tidb_stmt_summary_filename" json:"tidb_stmt_summary_filename"` + // StmtSummaryFileMaxDays indicates how many days the files written by + // stmtsummary will be kept when StmtSummaryEnablePersistent is true. + StmtSummaryFileMaxDays int `toml:"tidb_stmt_summary_file_max_days" json:"tidb_stmt_summary_file_max_days"` + // StmtSummaryFileMaxSize indicates the maximum size (in mb) of a single file + // written by stmtsummary when StmtSummaryEnablePersistent is true. + StmtSummaryFileMaxSize int `toml:"tidb_stmt_summary_file_max_size" json:"tidb_stmt_summary_file_max_size"` + // StmtSummaryFileMaxBackups indicates the maximum number of files written + // by stmtsummary when StmtSummaryEnablePersistent is true. + StmtSummaryFileMaxBackups int `toml:"tidb_stmt_summary_file_max_backups" json:"tidb_stmt_summary_file_max_backups"` + + // These variables exist in both 'instance' section and another place. + // The configuration in 'instance' section takes precedence. + + EnableSlowLog AtomicBool `toml:"tidb_enable_slow_log" json:"tidb_enable_slow_log"` + SlowThreshold uint64 `toml:"tidb_slow_log_threshold" json:"tidb_slow_log_threshold"` + RecordPlanInSlowLog uint32 `toml:"tidb_record_plan_in_slow_log" json:"tidb_record_plan_in_slow_log"` + CheckMb4ValueInUTF8 AtomicBool `toml:"tidb_check_mb4_value_in_utf8" json:"tidb_check_mb4_value_in_utf8"` + ForcePriority string `toml:"tidb_force_priority" json:"tidb_force_priority"` + MemoryUsageAlarmRatio float64 `toml:"tidb_memory_usage_alarm_ratio" json:"tidb_memory_usage_alarm_ratio"` + // EnableCollectExecutionInfo enables the TiDB to collect execution info. + EnableCollectExecutionInfo AtomicBool `toml:"tidb_enable_collect_execution_info" json:"tidb_enable_collect_execution_info"` + PluginDir string `toml:"plugin_dir" json:"plugin_dir"` + PluginLoad string `toml:"plugin_load" json:"plugin_load"` + // MaxConnections is the maximum permitted number of simultaneous client connections. + MaxConnections uint32 `toml:"max_connections" json:"max_connections"` + TiDBEnableDDL AtomicBool `toml:"tidb_enable_ddl" json:"tidb_enable_ddl"` + TiDBRCReadCheckTS bool `toml:"tidb_rc_read_check_ts" json:"tidb_rc_read_check_ts"` + // TiDBServiceScope indicates the role for tidb for distributed task framework. + TiDBServiceScope string `toml:"tidb_service_scope" json:"tidb_service_scope"` +} + +func (l *Log) getDisableTimestamp() bool { + if l.EnableTimestamp == nbUnset && l.DisableTimestamp == nbUnset { + return false + } + if l.EnableTimestamp == nbUnset { + return l.DisableTimestamp.toBool() + } + return !l.EnableTimestamp.toBool() +} + +func (l *Log) getDisableErrorStack() bool { + if l.EnableErrorStack == nbUnset && l.DisableErrorStack == nbUnset { + return true + } + if l.EnableErrorStack == nbUnset { + return l.DisableErrorStack.toBool() + } + return !l.EnableErrorStack.toBool() +} + +// The following constants represents the valid action configurations for Security.SpilledFileEncryptionMethod. +// "plaintext" means encryption is disabled. +// NOTE: Although the values is case insensitive, we should use lower-case +// strings because the configuration value will be transformed to lower-case +// string and compared with these constants in the further usage. +const ( + SpilledFileEncryptionMethodPlaintext = "plaintext" + SpilledFileEncryptionMethodAES128CTR = "aes128-ctr" +) + +// Security is the security section of the config. +type Security struct { + SkipGrantTable bool `toml:"skip-grant-table" json:"skip-grant-table"` + SSLCA string `toml:"ssl-ca" json:"ssl-ca"` + SSLCert string `toml:"ssl-cert" json:"ssl-cert"` + SSLKey string `toml:"ssl-key" json:"ssl-key"` + ClusterSSLCA string `toml:"cluster-ssl-ca" json:"cluster-ssl-ca"` + ClusterSSLCert string `toml:"cluster-ssl-cert" json:"cluster-ssl-cert"` + ClusterSSLKey string `toml:"cluster-ssl-key" json:"cluster-ssl-key"` + ClusterVerifyCN []string `toml:"cluster-verify-cn" json:"cluster-verify-cn"` + // Used for auth plugin `tidb_session_token`. + SessionTokenSigningCert string `toml:"session-token-signing-cert" json:"session-token-signing-cert"` + SessionTokenSigningKey string `toml:"session-token-signing-key" json:"session-token-signing-key"` + // If set to "plaintext", the spilled files will not be encrypted. + SpilledFileEncryptionMethod string `toml:"spilled-file-encryption-method" json:"spilled-file-encryption-method"` + // EnableSEM prevents SUPER users from having full access. + EnableSEM bool `toml:"enable-sem" json:"enable-sem"` + // Allow automatic TLS certificate generation + AutoTLS bool `toml:"auto-tls" json:"auto-tls"` + MinTLSVersion string `toml:"tls-version" json:"tls-version"` + RSAKeySize int `toml:"rsa-key-size" json:"rsa-key-size"` + SecureBootstrap bool `toml:"secure-bootstrap" json:"secure-bootstrap"` + // The path of the JWKS for tidb_auth_token authentication + AuthTokenJWKS string `toml:"auth-token-jwks" json:"auth-token-jwks"` + // The refresh time interval of JWKS + AuthTokenRefreshInterval string `toml:"auth-token-refresh-interval" json:"auth-token-refresh-interval"` + // Disconnect directly when the password is expired + DisconnectOnExpiredPassword bool `toml:"disconnect-on-expired-password" json:"disconnect-on-expired-password"` +} + +// The ErrConfigValidationFailed error is used so that external callers can do a type assertion +// to defer handling of this specific error when someone does not want strict type checking. +// This is needed only because logging hasn't been set up at the time we parse the config file. +// This should all be ripped out once strict config checking is made the default behavior. +type ErrConfigValidationFailed struct { + confFile string + UndecodedItems []string +} + +func (e *ErrConfigValidationFailed) Error() string { + return fmt.Sprintf("config file %s contained invalid configuration options: %s; check "+ + "TiDB manual to make sure this option has not been deprecated and removed from your TiDB "+ + "version if the option does not appear to be a typo", e.confFile, strings.Join( + e.UndecodedItems, ", ")) +} + +// ErrConfigInstanceSection error is used to warning the user +// which config options should be moved to 'instance'. +type ErrConfigInstanceSection struct { + confFile string + configSections *[]InstanceConfigSection + deprecatedSections *[]InstanceConfigSection +} + +func (e *ErrConfigInstanceSection) Error() string { + var builder strings.Builder + if len(*e.configSections) > 0 { + builder.WriteString("Conflict configuration options exists on both [instance] section and some other sections. ") + } + if len(*e.deprecatedSections) > 0 { + builder.WriteString("Some configuration options should be moved to [instance] section. ") + } + builder.WriteString("Please use the latter config options in [instance] instead: ") + for _, configSection := range *e.configSections { + for oldName, newName := range configSection.NameMappings { + builder.WriteString(fmt.Sprintf(" (%s, %s)", oldName, newName)) + } + } + for _, configSection := range *e.deprecatedSections { + for oldName, newName := range configSection.NameMappings { + builder.WriteString(fmt.Sprintf(" (%s, %s)", oldName, newName)) + } + } + builder.WriteString(".") + + return builder.String() +} + +// ClusterSecurity returns Security info for cluster +func (s *Security) ClusterSecurity() tikvcfg.Security { + return tikvcfg.NewSecurity(s.ClusterSSLCA, s.ClusterSSLCert, s.ClusterSSLKey, s.ClusterVerifyCN) +} + +// Status is the status section of the config. +type Status struct { + StatusHost string `toml:"status-host" json:"status-host"` + MetricsAddr string `toml:"metrics-addr" json:"metrics-addr"` + StatusPort uint `toml:"status-port" json:"status-port"` + MetricsInterval uint `toml:"metrics-interval" json:"metrics-interval"` + ReportStatus bool `toml:"report-status" json:"report-status"` + RecordQPSbyDB bool `toml:"record-db-qps" json:"record-db-qps"` + RecordDBLabel bool `toml:"record-db-label" json:"record-db-label"` + // After a duration of this time in seconds if the server doesn't see any activity it pings + // the client to see if the transport is still alive. + GRPCKeepAliveTime uint `toml:"grpc-keepalive-time" json:"grpc-keepalive-time"` + // After having pinged for keepalive check, the server waits for a duration of timeout in seconds + // and if no activity is seen even after that the connection is closed. + GRPCKeepAliveTimeout uint `toml:"grpc-keepalive-timeout" json:"grpc-keepalive-timeout"` + // The number of max concurrent streams/requests on a client connection. + GRPCConcurrentStreams uint `toml:"grpc-concurrent-streams" json:"grpc-concurrent-streams"` + // Sets window size for stream. The default value is 2MB. + GRPCInitialWindowSize int `toml:"grpc-initial-window-size" json:"grpc-initial-window-size"` + // Set maximum message length in bytes that gRPC can send. `-1` means unlimited. The default value is 10MB. + GRPCMaxSendMsgSize int `toml:"grpc-max-send-msg-size" json:"grpc-max-send-msg-size"` +} + +// Performance is the performance section of the config. +type Performance struct { + MaxProcs uint `toml:"max-procs" json:"max-procs"` + // Deprecated: use ServerMemoryQuota instead + MaxMemory uint64 `toml:"max-memory" json:"max-memory"` + ServerMemoryQuota uint64 `toml:"server-memory-quota" json:"server-memory-quota"` + StatsLease string `toml:"stats-lease" json:"stats-lease"` + StmtCountLimit uint `toml:"stmt-count-limit" json:"stmt-count-limit"` + PseudoEstimateRatio float64 `toml:"pseudo-estimate-ratio" json:"pseudo-estimate-ratio"` + BindInfoLease string `toml:"bind-info-lease" json:"bind-info-lease"` + TxnEntrySizeLimit uint64 `toml:"txn-entry-size-limit" json:"txn-entry-size-limit"` + TxnTotalSizeLimit uint64 `toml:"txn-total-size-limit" json:"txn-total-size-limit"` + TCPKeepAlive bool `toml:"tcp-keep-alive" json:"tcp-keep-alive"` + TCPNoDelay bool `toml:"tcp-no-delay" json:"tcp-no-delay"` + CrossJoin bool `toml:"cross-join" json:"cross-join"` + DistinctAggPushDown bool `toml:"distinct-agg-push-down" json:"distinct-agg-push-down"` + // Whether enable projection push down for coprocessors (both tikv & tiflash), default false. + ProjectionPushDown bool `toml:"projection-push-down" json:"projection-push-down"` + MaxTxnTTL uint64 `toml:"max-txn-ttl" json:"max-txn-ttl"` + // Deprecated + MemProfileInterval string `toml:"-" json:"-"` + + IndexUsageSyncLease string `toml:"index-usage-sync-lease" json:"index-usage-sync-lease"` + PlanReplayerGCLease string `toml:"plan-replayer-gc-lease" json:"plan-replayer-gc-lease"` + GOGC int `toml:"gogc" json:"gogc"` + EnforceMPP bool `toml:"enforce-mpp" json:"enforce-mpp"` + StatsLoadConcurrency uint `toml:"stats-load-concurrency" json:"stats-load-concurrency"` + StatsLoadQueueSize uint `toml:"stats-load-queue-size" json:"stats-load-queue-size"` + AnalyzePartitionConcurrencyQuota uint `toml:"analyze-partition-concurrency-quota" json:"analyze-partition-concurrency-quota"` + PlanReplayerDumpWorkerConcurrency uint `toml:"plan-replayer-dump-worker-concurrency" json:"plan-replayer-dump-worker-concurrency"` + EnableStatsCacheMemQuota bool `toml:"enable-stats-cache-mem-quota" json:"enable-stats-cache-mem-quota"` + // The following items are deprecated. We need to keep them here temporarily + // to support the upgrade process. They can be removed in future. + + // CommitterConcurrency, RunAutoAnalyze unused since bootstrap v90 + CommitterConcurrency int `toml:"committer-concurrency" json:"committer-concurrency"` + RunAutoAnalyze bool `toml:"run-auto-analyze" json:"run-auto-analyze"` + + // ForcePriority, MemoryUsageAlarmRatio are deprecated. + ForcePriority string `toml:"force-priority" json:"force-priority"` + MemoryUsageAlarmRatio float64 `toml:"memory-usage-alarm-ratio" json:"memory-usage-alarm-ratio"` + + EnableLoadFMSketch bool `toml:"enable-load-fmsketch" json:"enable-load-fmsketch"` + + LiteInitStats bool `toml:"lite-init-stats" json:"lite-init-stats"` + + // If ForceInitStats is true, when tidb starts up, it doesn't provide service until init stats is finished. + // If ForceInitStats is false, tidb can provide service before init stats is finished. Note that during the period + // of init stats the optimizer may make bad decisions due to pseudo stats. + ForceInitStats bool `toml:"force-init-stats" json:"force-init-stats"` +} + +// PlanCache is the PlanCache section of the config. +type PlanCache struct { + Enabled bool `toml:"enabled" json:"enabled"` + Capacity uint `toml:"capacity" json:"capacity"` + Shards uint `toml:"shards" json:"shards"` +} + +// PreparedPlanCache is the PreparedPlanCache section of the config. +type PreparedPlanCache struct { + Enabled bool `toml:"enabled" json:"enabled"` + Capacity uint `toml:"capacity" json:"capacity"` + MemoryGuardRatio float64 `toml:"memory-guard-ratio" json:"memory-guard-ratio"` +} + +// OpenTracing is the opentracing section of the config. +type OpenTracing struct { + Enable bool `toml:"enable" json:"enable"` + RPCMetrics bool `toml:"rpc-metrics" json:"rpc-metrics"` + Sampler OpenTracingSampler `toml:"sampler" json:"sampler"` + Reporter OpenTracingReporter `toml:"reporter" json:"reporter"` +} + +// OpenTracingSampler is the config for opentracing sampler. +// See https://godoc.org/github.com/uber/jaeger-client-go/config#SamplerConfig +type OpenTracingSampler struct { + Type string `toml:"type" json:"type"` + Param float64 `toml:"param" json:"param"` + SamplingServerURL string `toml:"sampling-server-url" json:"sampling-server-url"` + MaxOperations int `toml:"max-operations" json:"max-operations"` + SamplingRefreshInterval time.Duration `toml:"sampling-refresh-interval" json:"sampling-refresh-interval"` +} + +// OpenTracingReporter is the config for opentracing reporter. +// See https://godoc.org/github.com/uber/jaeger-client-go/config#ReporterConfig +type OpenTracingReporter struct { + QueueSize int `toml:"queue-size" json:"queue-size"` + BufferFlushInterval time.Duration `toml:"buffer-flush-interval" json:"buffer-flush-interval"` + LogSpans bool `toml:"log-spans" json:"log-spans"` + LocalAgentHostPort string `toml:"local-agent-host-port" json:"local-agent-host-port"` +} + +// ProxyProtocol is the PROXY protocol section of the config. +type ProxyProtocol struct { + // PROXY protocol acceptable client networks. + // Empty string means disable PROXY protocol, + // * means all networks. + Networks string `toml:"networks" json:"networks"` + // PROXY protocol header read timeout, Unit is second. + HeaderTimeout uint `toml:"header-timeout" json:"header-timeout"` + // PROXY protocol header process fallback-able. + // If set to true and not send PROXY protocol header, connection will return connection's client IP. + Fallbackable bool `toml:"fallbackable" json:"fallbackable"` +} + +// Binlog is the config for binlog. +type Binlog struct { + Enable bool `toml:"enable" json:"enable"` + // If IgnoreError is true, when writing binlog meets error, TiDB would + // ignore the error. + IgnoreError bool `toml:"ignore-error" json:"ignore-error"` + WriteTimeout string `toml:"write-timeout" json:"write-timeout"` + // Use socket file to write binlog, for compatible with kafka version tidb-binlog. + BinlogSocket string `toml:"binlog-socket" json:"binlog-socket"` + // The strategy for sending binlog to pump, value can be "range" or "hash" now. + Strategy string `toml:"strategy" json:"strategy"` +} + +// PessimisticTxn is the config for pessimistic transaction. +type PessimisticTxn struct { + // The max count of retry for a single statement in a pessimistic transaction. + MaxRetryCount uint `toml:"max-retry-count" json:"max-retry-count"` + // The max count of deadlock events that will be recorded in the information_schema.deadlocks table. + DeadlockHistoryCapacity uint `toml:"deadlock-history-capacity" json:"deadlock-history-capacity"` + // Whether retryable deadlocks (in-statement deadlocks) are collected to the information_schema.deadlocks table. + DeadlockHistoryCollectRetryable bool `toml:"deadlock-history-collect-retryable" json:"deadlock-history-collect-retryable"` + // PessimisticAutoCommit represents if true it means the auto-commit transactions will be in pessimistic mode. + PessimisticAutoCommit AtomicBool `toml:"pessimistic-auto-commit" json:"pessimistic-auto-commit"` + // ConstraintCheckInPlacePessimistic is the default value for the session variable `tidb_constraint_check_in_place_pessimistic` + ConstraintCheckInPlacePessimistic bool `toml:"constraint-check-in-place-pessimistic" json:"constraint-check-in-place-pessimistic"` +} + +// TrxSummary is the config for transaction summary collecting. +type TrxSummary struct { + // how many transaction summary in `transaction_summary` each TiDB node should keep. + TransactionSummaryCapacity uint `toml:"transaction-summary-capacity" json:"transaction-summary-capacity"` + // how long a transaction should be executed to make it be recorded in `transaction_id_digest`. + TransactionIDDigestMinDuration uint `toml:"transaction-id-digest-min-duration" json:"transaction-id-digest-min-duration"` +} + +// Valid Validatse TrxSummary configs +func (config *TrxSummary) Valid() error { + if config.TransactionSummaryCapacity > 5000 { + return errors.New("transaction-summary.transaction-summary-capacity should not be larger than 5000") + } + return nil +} + +// DefaultPessimisticTxn returns the default configuration for PessimisticTxn +func DefaultPessimisticTxn() PessimisticTxn { + return PessimisticTxn{ + MaxRetryCount: 256, + DeadlockHistoryCapacity: 10, + DeadlockHistoryCollectRetryable: false, + PessimisticAutoCommit: *NewAtomicBool(false), + ConstraintCheckInPlacePessimistic: true, + } +} + +// DefaultTrxSummary returns the default configuration for TrxSummary collector +func DefaultTrxSummary() TrxSummary { + // TrxSummary is not enabled by default before GA + return TrxSummary{ + TransactionSummaryCapacity: 500, + TransactionIDDigestMinDuration: 2147483647, + } +} + +// Plugin is the config for plugin +type Plugin struct { + Dir string `toml:"dir" json:"dir"` + Load string `toml:"load" json:"load"` +} + +// TopSQL is the config for TopSQL. +type TopSQL struct { + // The TopSQL's data receiver address. + ReceiverAddress string `toml:"receiver-address" json:"receiver-address"` +} + +// IsolationRead is the config for isolation read. +type IsolationRead struct { + // Engines filters tidb-server access paths by engine type. + Engines []string `toml:"engines" json:"engines"` +} + +// Experimental controls the features that are still experimental: their semantics, interfaces are subject to change. +// Using these features in the production environment is not recommended. +type Experimental struct { + // Whether enable creating expression index. + AllowsExpressionIndex bool `toml:"allow-expression-index" json:"allow-expression-index"` + // Whether enable charset feature. + EnableNewCharset bool `toml:"enable-new-charset" json:"-"` +} + +var defTiKVCfg = tikvcfg.DefaultConfig() +var defaultConf = Config{ + Host: DefHost, + AdvertiseAddress: "", + Port: DefPort, + Socket: "/tmp/tidb-{Port}.sock", + Cors: "", + Store: "unistore", + Path: "/tmp/tidb", + RunDDL: true, + SplitTable: true, + Lease: "45s", + TokenLimit: 1000, + OOMUseTmpStorage: true, + TempDir: DefTempDir, + TempStorageQuota: -1, + TempStoragePath: tempStorageDirName, + MemQuotaQuery: 1 << 30, + OOMAction: "cancel", + EnableBatchDML: false, + CheckMb4ValueInUTF8: *NewAtomicBool(true), + MaxIndexLength: 3072, + IndexLimit: 64, + TableColumnCountLimit: 1017, + AlterPrimaryKey: false, + TreatOldVersionUTF8AsUTF8MB4: true, + EnableTableLock: false, + DelayCleanTableLock: 0, + SplitRegionMaxNum: 1000, + RepairMode: false, + RepairTableList: []string{}, + MaxServerConnections: 0, + TxnLocalLatches: defTiKVCfg.TxnLocalLatches, + GracefulWaitBeforeShutdown: 0, + ServerVersion: "", + TiDBEdition: "", + VersionComment: "", + TiDBReleaseVersion: "", + Log: Log{ + Level: "info", + Format: "text", + File: logutil.NewFileLogConfig(logutil.DefaultLogMaxSize), + SlowQueryFile: "tidb-slow.log", + SlowThreshold: logutil.DefaultSlowThreshold, + ExpensiveThreshold: 10000, // ExpensiveThreshold is deprecated. + DisableErrorStack: nbUnset, + EnableErrorStack: nbUnset, // If both options are nbUnset, getDisableErrorStack() returns true + EnableTimestamp: nbUnset, + DisableTimestamp: nbUnset, // If both options are nbUnset, getDisableTimestamp() returns false + QueryLogMaxLen: logutil.DefaultQueryLogMaxLen, + RecordPlanInSlowLog: logutil.DefaultRecordPlanInSlowLog, + EnableSlowLog: *NewAtomicBool(logutil.DefaultTiDBEnableSlowLog), + }, + Instance: Instance{ + TiDBGeneralLog: false, + EnablePProfSQLCPU: false, + DDLSlowOprThreshold: DefDDLSlowOprThreshold, + ExpensiveQueryTimeThreshold: DefExpensiveQueryTimeThreshold, + ExpensiveTxnTimeThreshold: DefExpensiveTxnTimeThreshold, + StmtSummaryEnablePersistent: false, + StmtSummaryFilename: "tidb-statements.log", + StmtSummaryFileMaxDays: 3, + StmtSummaryFileMaxSize: 64, + StmtSummaryFileMaxBackups: 0, + EnableSlowLog: *NewAtomicBool(logutil.DefaultTiDBEnableSlowLog), + SlowThreshold: logutil.DefaultSlowThreshold, + RecordPlanInSlowLog: logutil.DefaultRecordPlanInSlowLog, + CheckMb4ValueInUTF8: *NewAtomicBool(true), + ForcePriority: "NO_PRIORITY", + MemoryUsageAlarmRatio: DefMemoryUsageAlarmRatio, + EnableCollectExecutionInfo: *NewAtomicBool(true), + PluginDir: "/data/deploy/plugin", + PluginLoad: "", + MaxConnections: 0, + TiDBEnableDDL: *NewAtomicBool(true), + TiDBRCReadCheckTS: false, + TiDBServiceScope: "", + }, + Status: Status{ + ReportStatus: true, + StatusHost: DefStatusHost, + StatusPort: DefStatusPort, + MetricsInterval: 15, + RecordQPSbyDB: false, + RecordDBLabel: false, + GRPCKeepAliveTime: 10, + GRPCKeepAliveTimeout: 3, + GRPCConcurrentStreams: 1024, + GRPCInitialWindowSize: 2 * 1024 * 1024, + GRPCMaxSendMsgSize: math.MaxInt32, + }, + Performance: Performance{ + MaxMemory: 0, + ServerMemoryQuota: 0, + MemoryUsageAlarmRatio: DefMemoryUsageAlarmRatio, + TCPKeepAlive: true, + TCPNoDelay: true, + CrossJoin: true, + StatsLease: "3s", + StmtCountLimit: 5000, + PseudoEstimateRatio: 0.8, + ForcePriority: "NO_PRIORITY", + BindInfoLease: "3s", + TxnEntrySizeLimit: DefTxnEntrySizeLimit, + TxnTotalSizeLimit: DefTxnTotalSizeLimit, + DistinctAggPushDown: false, + ProjectionPushDown: false, + CommitterConcurrency: defTiKVCfg.CommitterConcurrency, + MaxTxnTTL: defTiKVCfg.MaxTxnTTL, // 1hour + // TODO: set indexUsageSyncLease to 60s. + IndexUsageSyncLease: "0s", + GOGC: 100, + EnforceMPP: false, + PlanReplayerGCLease: "10m", + StatsLoadConcurrency: 5, + StatsLoadQueueSize: 1000, + AnalyzePartitionConcurrencyQuota: 16, + PlanReplayerDumpWorkerConcurrency: 1, + EnableStatsCacheMemQuota: true, + RunAutoAnalyze: true, + EnableLoadFMSketch: false, + LiteInitStats: true, + ForceInitStats: true, + }, + ProxyProtocol: ProxyProtocol{ + Networks: "", + HeaderTimeout: 5, + Fallbackable: false, + }, + PreparedPlanCache: PreparedPlanCache{ + Enabled: true, + Capacity: 100, + MemoryGuardRatio: 0.1, + }, + OpenTracing: OpenTracing{ + Enable: false, + Sampler: OpenTracingSampler{ + Type: "const", + Param: 1.0, + }, + Reporter: OpenTracingReporter{}, + }, + PDClient: defTiKVCfg.PDClient, + TiKVClient: defTiKVCfg.TiKVClient, + Binlog: Binlog{ + WriteTimeout: "15s", + Strategy: "range", + }, + Plugin: Plugin{ + Dir: "/data/deploy/plugin", + Load: "", + }, + PessimisticTxn: DefaultPessimisticTxn(), + IsolationRead: IsolationRead{ + Engines: []string{"tikv", "tiflash", "tidb"}, + }, + Experimental: Experimental{}, + EnableCollectExecutionInfo: true, + EnableTelemetry: false, + Labels: make(map[string]string), + EnableGlobalIndex: false, + Security: Security{ + SpilledFileEncryptionMethod: SpilledFileEncryptionMethodPlaintext, + EnableSEM: false, + AutoTLS: false, + RSAKeySize: 4096, + AuthTokenJWKS: "", + AuthTokenRefreshInterval: DefAuthTokenRefreshInterval.String(), + DisconnectOnExpiredPassword: true, + }, + DeprecateIntegerDisplayWidth: false, + EnableEnumLengthLimit: true, + StoresRefreshInterval: defTiKVCfg.StoresRefreshInterval, + EnableForwarding: defTiKVCfg.EnableForwarding, + NewCollationsEnabledOnFirstBootstrap: true, + EnableGlobalKill: true, + Enable32BitsConnectionID: true, + TrxSummary: DefaultTrxSummary(), + DisaggregatedTiFlash: false, + TiFlashComputeAutoScalerType: tiflashcompute.DefASStr, + TiFlashComputeAutoScalerAddr: tiflashcompute.DefAWSAutoScalerAddr, + IsTiFlashComputeFixedPool: false, + AutoScalerClusterID: "", + UseAutoScaler: false, + TiDBMaxReuseChunk: 64, + TiDBMaxReuseColumn: 256, + TiDBEnableExitCheck: false, + InMemSlowQueryTopNNum: 30, + InMemSlowQueryRecentNum: 500, +} + +var ( + globalConf atomic.Value +) + +// NewConfig creates a new config instance with default value. +func NewConfig() *Config { + conf := defaultConf + return &conf +} + +// GetGlobalConfig returns the global configuration for this server. +// It should store configuration from command line and configuration file. +// Other parts of the system can read the global configuration use this function. +func GetGlobalConfig() *Config { + return globalConf.Load().(*Config) +} + +// StoreGlobalConfig stores a new config to the globalConf. It mostly uses in the test to avoid some data races. +func StoreGlobalConfig(config *Config) { + globalConf.Store(config) + TikvConfigLock.Lock() + defer TikvConfigLock.Unlock() + cfg := *config.GetTiKVConfig() + tikvcfg.StoreGlobalConfig(&cfg) +} + +// removedConfig contains items that are no longer supported. +// they might still be in the config struct to support import, +// but are not actively used. +var removedConfig = map[string]struct{}{ + "pessimistic-txn.ttl": {}, + "pessimistic-txn.enable": {}, + "log.file.log-rotate": {}, + "log.log-slow-query": {}, + "txn-local-latches": {}, + "txn-local-latches.enabled": {}, + "txn-local-latches.capacity": {}, + "performance.max-memory": {}, + "max-txn-time-use": {}, + "experimental.allow-auto-random": {}, + "enable-redact-log": {}, // use variable tidb_redact_log instead + "enable-streaming": {}, + "performance.mem-profile-interval": {}, + "security.require-secure-transport": {}, + "lower-case-table-names": {}, + "stmt-summary": {}, + "stmt-summary.enable": {}, + "stmt-summary.enable-internal-query": {}, + "stmt-summary.max-stmt-count": {}, + "stmt-summary.max-sql-length": {}, + "stmt-summary.refresh-interval": {}, + "stmt-summary.history-size": {}, + "enable-batch-dml": {}, // use tidb_enable_batch_dml + "mem-quota-query": {}, + "log.query-log-max-len": {}, + "performance.committer-concurrency": {}, + "experimental.enable-global-kill": {}, + "performance.run-auto-analyze": {}, // use tidb_enable_auto_analyze + // use tidb_enable_prepared_plan_cache, tidb_prepared_plan_cache_size and tidb_prepared_plan_cache_memory_guard_ratio + "prepared-plan-cache.enabled": {}, + "prepared-plan-cache.capacity": {}, + "prepared-plan-cache.memory-guard-ratio": {}, + "oom-action": {}, + "check-mb4-value-in-utf8": {}, // use tidb_check_mb4_value_in_utf8 + "enable-collect-execution-info": {}, // use tidb_enable_collect_execution_info + "log.enable-slow-log": {}, // use tidb_enable_slow_log + "log.slow-threshold": {}, // use tidb_slow_log_threshold + "log.record-plan-in-slow-log": {}, // use tidb_record_plan_in_slow_log + "log.expensive-threshold": {}, + "performance.force-priority": {}, // use tidb_force_priority + "performance.memory-usage-alarm-ratio": {}, // use tidb_memory_usage_alarm_ratio + "plugin.load": {}, // use plugin_load + "plugin.dir": {}, // use plugin_dir + "performance.feedback-probability": {}, // This feature is deprecated + "performance.query-feedback-limit": {}, + "oom-use-tmp-storage": {}, // use tidb_enable_tmp_storage_on_oom + "max-server-connections": {}, // use sysvar max_connections + "run-ddl": {}, // use sysvar tidb_enable_ddl + "instance.tidb_memory_usage_alarm_ratio": {}, // use sysvar tidb_memory_usage_alarm_ratio +} + +// isAllRemovedConfigItems returns true if all the items that couldn't validate +// belong to the list of removedConfig items. +func isAllRemovedConfigItems(items []string) bool { + for _, item := range items { + if _, ok := removedConfig[item]; !ok { + return false + } + } + return true +} + +// InitializeConfig initialize the global config handler. +// The function enforceCmdArgs is used to merge the config file with command arguments: +// For example, if you start TiDB by the command "./tidb-server --port=3000", the port number should be +// overwritten to 3000 and ignore the port number in the config file. +func InitializeConfig(confPath string, configCheck, configStrict bool, enforceCmdArgs func(*Config, *flag.FlagSet), fset *flag.FlagSet) { + cfg := GetGlobalConfig() + var err error + if confPath != "" { + if err = cfg.Load(confPath); err != nil { + // Unused config item error turns to warnings. + if tmp, ok := err.(*ErrConfigValidationFailed); ok { + // This block is to accommodate an interim situation where strict config checking + // is not the default behavior of TiDB. The warning message must be deferred until + // logging has been set up. After strict config checking is the default behavior, + // This should all be removed. + if (!configCheck && !configStrict) || isAllRemovedConfigItems(tmp.UndecodedItems) { + fmt.Fprintln(os.Stderr, err.Error()) + err = nil + } + } else if tmp, ok := err.(*ErrConfigInstanceSection); ok { + logutil.BgLogger().Warn(tmp.Error()) + err = nil + } + } + // In configCheck we always print out which options in the config file + // have been removed. This helps users upgrade better. + if configCheck { + err = cfg.RemovedVariableCheck(confPath) + if err != nil { + logutil.BgLogger().Warn(err.Error()) + err = nil // treat as warning + } + } + + terror.MustNil(err) + } else { + // configCheck should have the config file specified. + if configCheck { + fmt.Fprintln(os.Stderr, "config check failed", errors.New("no config file specified for config-check")) + os.Exit(1) + } + } + enforceCmdArgs(cfg, fset) + + if err := cfg.Valid(); err != nil { + if !filepath.IsAbs(confPath) { + if tmp, err := filepath.Abs(confPath); err == nil { + confPath = tmp + } + } + fmt.Fprintln(os.Stderr, "load config file:", confPath) + fmt.Fprintln(os.Stderr, "invalid config", err) + os.Exit(1) + } + if configCheck { + fmt.Println("config check successful") + os.Exit(0) + } + StoreGlobalConfig(cfg) +} + +// RemovedVariableCheck checks if the config file contains any items +// which have been removed. These will not take effect any more. +func (c *Config) RemovedVariableCheck(confFile string) error { + metaData, err := toml.DecodeFile(confFile, c) + if err != nil { + return err + } + var removed []string + for item := range removedConfig { + // We need to split the string to account for the top level + // and the section hierarchy of config. + tmp := strings.Split(item, ".") + if len(tmp) == 2 && metaData.IsDefined(tmp[0], tmp[1]) { + removed = append(removed, item) + } else if len(tmp) == 1 && metaData.IsDefined(tmp[0]) { + removed = append(removed, item) + } + } + if len(removed) > 0 { + sort.Strings(removed) // deterministic for tests + return fmt.Errorf("The following configuration options are no longer supported in this version of TiDB. Check the release notes for more information: %s", strings.Join(removed, ", ")) + } + return nil +} + +// Load loads config options from a toml file. +func (c *Config) Load(confFile string) error { + metaData, err := toml.DecodeFile(confFile, c) + if c.TokenLimit == 0 { + c.TokenLimit = 1000 + } + // If any items in confFile file are not mapped into the Config struct, issue + // an error and stop the server from starting. + undecoded := metaData.Undecoded() + if len(undecoded) > 0 && err == nil { + var undecodedItems []string + for _, item := range undecoded { + undecodedItems = append(undecodedItems, item.String()) + } + err = &ErrConfigValidationFailed{confFile, undecodedItems} + } + + for _, section := range sectionMovedToInstance { + newConflictSection := InstanceConfigSection{SectionName: section.SectionName, NameMappings: map[string]string{}} + newDeprecatedSection := InstanceConfigSection{SectionName: section.SectionName, NameMappings: map[string]string{}} + for oldName, newName := range section.NameMappings { + if section.SectionName == "" && metaData.IsDefined(oldName) || + section.SectionName != "" && metaData.IsDefined(section.SectionName, oldName) { + if metaData.IsDefined("instance", newName) { + newConflictSection.NameMappings[oldName] = newName + } else { + newDeprecatedSection.NameMappings[oldName] = newName + } + } + } + if len(newConflictSection.NameMappings) > 0 { + ConflictOptions = append(ConflictOptions, newConflictSection) + } + if len(newDeprecatedSection.NameMappings) > 0 { + DeprecatedOptions = append(DeprecatedOptions, newDeprecatedSection) + } + } + if len(ConflictOptions) > 0 || len(DeprecatedOptions) > 0 { + // Give a warning that the 'instance' section should be used. + err = &ErrConfigInstanceSection{confFile, &ConflictOptions, &DeprecatedOptions} + } + + return err +} + +// Valid checks if this config is valid. +func (c *Config) Valid() error { + if c.Log.EnableErrorStack == c.Log.DisableErrorStack && c.Log.EnableErrorStack != nbUnset { + logutil.BgLogger().Warn(fmt.Sprintf("\"enable-error-stack\" (%v) conflicts \"disable-error-stack\" (%v). \"disable-error-stack\" is deprecated, please use \"enable-error-stack\" instead. disable-error-stack is ignored.", c.Log.EnableErrorStack, c.Log.DisableErrorStack)) + // if two options conflict, we will use the value of EnableErrorStack + c.Log.DisableErrorStack = nbUnset + } + if c.Log.EnableTimestamp == c.Log.DisableTimestamp && c.Log.EnableTimestamp != nbUnset { + logutil.BgLogger().Warn(fmt.Sprintf("\"enable-timestamp\" (%v) conflicts \"disable-timestamp\" (%v). \"disable-timestamp\" is deprecated, please use \"enable-timestamp\" instead", c.Log.EnableTimestamp, c.Log.DisableTimestamp)) + // if two options conflict, we will use the value of EnableTimestamp + c.Log.DisableTimestamp = nbUnset + } + if c.Security.SkipGrantTable && !hasRootPrivilege() { + return fmt.Errorf("TiDB run with skip-grant-table need root privilege") + } + if !ValidStorage[c.Store] { + nameList := make([]string, 0, len(ValidStorage)) + for k, v := range ValidStorage { + if v { + nameList = append(nameList, k) + } + } + return fmt.Errorf("invalid store=%s, valid storages=%v", c.Store, nameList) + } + if c.Store == "mocktikv" && !c.Instance.TiDBEnableDDL.Load() { + return fmt.Errorf("can't disable DDL on mocktikv") + } + if c.MaxIndexLength < DefMaxIndexLength || c.MaxIndexLength > DefMaxOfMaxIndexLength { + return fmt.Errorf("max-index-length should be [%d, %d]", DefMaxIndexLength, DefMaxOfMaxIndexLength) + } + if c.IndexLimit < DefIndexLimit || c.IndexLimit > DefMaxOfIndexLimit { + return fmt.Errorf("index-limit should be [%d, %d]", DefIndexLimit, DefMaxOfIndexLimit) + } + if c.Log.File.MaxSize > MaxLogFileSize { + return fmt.Errorf("invalid max log file size=%v which is larger than max=%v", c.Log.File.MaxSize, MaxLogFileSize) + } + if c.TableColumnCountLimit < DefTableColumnCountLimit || c.TableColumnCountLimit > DefMaxOfTableColumnCountLimit { + return fmt.Errorf("table-column-limit should be [%d, %d]", DefIndexLimit, DefMaxOfTableColumnCountLimit) + } + + // txn-local-latches + if err := c.TxnLocalLatches.Valid(); err != nil { + return err + } + + // For tikvclient. + if err := c.TiKVClient.Valid(); err != nil { + return err + } + if err := c.TrxSummary.Valid(); err != nil { + return err + } + + if c.Performance.TxnTotalSizeLimit > 1<<40 { + return fmt.Errorf("txn-total-size-limit should be less than %d", 1<<40) + } + + if c.Instance.MemoryUsageAlarmRatio > 1 || c.Instance.MemoryUsageAlarmRatio < 0 { + return fmt.Errorf("tidb_memory_usage_alarm_ratio in [Instance] must be greater than or equal to 0 and less than or equal to 1") + } + + if len(c.IsolationRead.Engines) < 1 { + return fmt.Errorf("the number of [isolation-read]engines for isolation read should be at least 1") + } + for _, engine := range c.IsolationRead.Engines { + if engine != "tidb" && engine != "tikv" && engine != "tiflash" { + return fmt.Errorf("type of [isolation-read]engines can't be %v should be one of tidb or tikv or tiflash", engine) + } + } + + // test security + c.Security.SpilledFileEncryptionMethod = strings.ToLower(c.Security.SpilledFileEncryptionMethod) + switch c.Security.SpilledFileEncryptionMethod { + case SpilledFileEncryptionMethodPlaintext, SpilledFileEncryptionMethodAES128CTR: + default: + return fmt.Errorf("unsupported [security]spilled-file-encryption-method %v, TiDB only supports [%v, %v]", + c.Security.SpilledFileEncryptionMethod, SpilledFileEncryptionMethodPlaintext, SpilledFileEncryptionMethodAES128CTR) + } + + // check stats load config + if c.Performance.StatsLoadConcurrency < DefStatsLoadConcurrencyLimit || c.Performance.StatsLoadConcurrency > DefMaxOfStatsLoadConcurrencyLimit { + return fmt.Errorf("stats-load-concurrency should be [%d, %d]", DefStatsLoadConcurrencyLimit, DefMaxOfStatsLoadConcurrencyLimit) + } + if c.Performance.StatsLoadQueueSize < DefStatsLoadQueueSizeLimit || c.Performance.StatsLoadQueueSize > DefMaxOfStatsLoadQueueSizeLimit { + return fmt.Errorf("stats-load-queue-size should be [%d, %d]", DefStatsLoadQueueSizeLimit, DefMaxOfStatsLoadQueueSizeLimit) + } + + // Check tiflash_compute topo fetch is valid. + if c.DisaggregatedTiFlash && c.UseAutoScaler { + if !tiflashcompute.IsValidAutoScalerConfig(c.TiFlashComputeAutoScalerType) { + return fmt.Errorf("invalid AutoScaler type, expect %s, %s or %s, got %s", + tiflashcompute.MockASStr, tiflashcompute.AWSASStr, tiflashcompute.GCPASStr, c.TiFlashComputeAutoScalerType) + } + if c.TiFlashComputeAutoScalerAddr == "" { + return fmt.Errorf("autoscaler-addr cannot be empty when disaggregated-tiflash mode is true") + } + } + + // test log level + l := zap.NewAtomicLevel() + return l.UnmarshalText([]byte(c.Log.Level)) +} + +// UpdateGlobal updates the global config, and provide a restore function that can be used to restore to the original. +func UpdateGlobal(f func(conf *Config)) { + g := GetGlobalConfig() + newConf := *g + f(&newConf) + StoreGlobalConfig(&newConf) +} + +// RestoreFunc gets a function that restore the config to the current value. +func RestoreFunc() (restore func()) { + g := GetGlobalConfig() + return func() { + StoreGlobalConfig(g) + } +} + +func hasRootPrivilege() bool { + return os.Geteuid() == 0 +} + +// TableLockEnabled uses to check whether enabled the table lock feature. +func TableLockEnabled() bool { + return GetGlobalConfig().EnableTableLock +} + +// TableLockDelayClean uses to get the time of delay clean table lock. +var TableLockDelayClean = func() uint64 { + return GetGlobalConfig().DelayCleanTableLock +} + +// ToLogConfig converts *Log to *logutil.LogConfig. +func (l *Log) ToLogConfig() *logutil.LogConfig { + return logutil.NewLogConfig(l.Level, l.Format, l.SlowQueryFile, l.File, l.getDisableTimestamp(), + func(config *zaplog.Config) { config.DisableErrorVerbose = l.getDisableErrorStack() }, + func(config *zaplog.Config) { config.Timeout = l.Timeout }, + ) +} + +// ToTracingConfig converts *OpenTracing to *tracing.Configuration. +func (t *OpenTracing) ToTracingConfig() *tracing.Configuration { + ret := &tracing.Configuration{ + Disabled: !t.Enable, + RPCMetrics: t.RPCMetrics, + Reporter: &tracing.ReporterConfig{}, + Sampler: &tracing.SamplerConfig{}, + } + ret.Reporter.QueueSize = t.Reporter.QueueSize + ret.Reporter.BufferFlushInterval = t.Reporter.BufferFlushInterval + ret.Reporter.LogSpans = t.Reporter.LogSpans + ret.Reporter.LocalAgentHostPort = t.Reporter.LocalAgentHostPort + + ret.Sampler.Type = t.Sampler.Type + ret.Sampler.Param = t.Sampler.Param + ret.Sampler.SamplingServerURL = t.Sampler.SamplingServerURL + ret.Sampler.MaxOperations = t.Sampler.MaxOperations + ret.Sampler.SamplingRefreshInterval = t.Sampler.SamplingRefreshInterval + return ret +} + +func init() { + initByLDFlags(versioninfo.TiDBEdition, checkBeforeDropLDFlag) +} + +func initByLDFlags(edition, checkBeforeDropLDFlag string) { + if edition != versioninfo.CommunityEdition { + defaultConf.EnableTelemetry = false + } + conf := defaultConf + StoreGlobalConfig(&conf) + if checkBeforeDropLDFlag == "1" { + CheckTableBeforeDrop = true + } +} + +// hideConfig is used to filter a single line of config for hiding. +var hideConfig = []string{ + "performance.index-usage-sync-lease", +} + +// GetJSONConfig returns the config as JSON with hidden items removed +// It replaces the earlier HideConfig() which used strings.Split() in +// an way that didn't work for similarly named items (like enable). +func GetJSONConfig() (string, error) { + j, err := json.Marshal(GetGlobalConfig()) + if err != nil { + return "", err + } + + jsonValue := make(map[string]interface{}) + err = json.Unmarshal(j, &jsonValue) + if err != nil { + return "", err + } + + removedPaths := make([]string, 0, len(removedConfig)+len(hideConfig)) + for removedItem := range removedConfig { + removedPaths = append(removedPaths, removedItem) + } + removedPaths = append(removedPaths, hideConfig...) + + for _, path := range removedPaths { + s := strings.Split(path, ".") + curValue := jsonValue + for i, key := range s { + if i == len(s)-1 { + delete(curValue, key) + } + if curValue[key] == nil { + break + } + mapValue, ok := curValue[key].(map[string]interface{}) + if !ok { + break + } + curValue = mapValue + } + } + + buf, err := json.Marshal(jsonValue) + if err != nil { + return "", err + } + + var resBuf bytes.Buffer + if err = json.Indent(&resBuf, buf, "", "\t"); err != nil { + return "", err + } + return resBuf.String(), nil +} + +// ContainHiddenConfig checks whether it contains the configuration that needs to be hidden. +func ContainHiddenConfig(s string) bool { + s = strings.ToLower(s) + for _, hc := range hideConfig { + if strings.Contains(s, hc) { + return true + } + } + for dc := range removedConfig { + if strings.Contains(s, dc) { + return true + } + } + return false +} + +// GetGlobalKeyspaceName is used to get global keyspace name +// from config file or command line. +func GetGlobalKeyspaceName() string { + return GetGlobalConfig().KeyspaceName +} diff --git a/config/config.toml.example b/pkg/config/config.toml.example similarity index 100% rename from config/config.toml.example rename to pkg/config/config.toml.example diff --git a/config/config_test.go b/pkg/config/config_test.go similarity index 99% rename from config/config_test.go rename to pkg/config/config_test.go index 51ff37ab6a832..e87fcdcbb81ae 100644 --- a/config/config_test.go +++ b/pkg/config/config_test.go @@ -30,7 +30,7 @@ import ( "github.com/BurntSushi/toml" "github.com/pingcap/errors" zaplog "github.com/pingcap/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" tracing "github.com/uber/jaeger-client-go/config" ) diff --git a/config/config_util.go b/pkg/config/config_util.go similarity index 100% rename from config/config_util.go rename to pkg/config/config_util.go diff --git a/config/config_util_test.go b/pkg/config/config_util_test.go similarity index 100% rename from config/config_util_test.go rename to pkg/config/config_util_test.go diff --git a/config/const.go b/pkg/config/const.go similarity index 100% rename from config/const.go rename to pkg/config/const.go diff --git a/pkg/config/main_test.go b/pkg/config/main_test.go new file mode 100644 index 0000000000000..063e004864021 --- /dev/null +++ b/pkg/config/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package config + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("syscall.Syscall"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/BUILD.bazel b/pkg/ddl/BUILD.bazel new file mode 100644 index 0000000000000..d5fff640768c2 --- /dev/null +++ b/pkg/ddl/BUILD.bazel @@ -0,0 +1,314 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package_group( + name = "ddl_friend", + packages = [ + "-//pkg/planner/...", + "//...", + ], +) + +go_library( + name = "ddl", + srcs = [ + "backfilling.go", + "backfilling_clean_s3.go", + "backfilling_dispatcher.go", + "backfilling_dist_scheduler.go", + "backfilling_import_cloud.go", + "backfilling_import_local.go", + "backfilling_merge_sort.go", + "backfilling_operators.go", + "backfilling_read_index.go", + "backfilling_scheduler.go", + "callback.go", + "cluster.go", + "column.go", + "constant.go", + "constraint.go", + "ddl.go", + "ddl_algorithm.go", + "ddl_api.go", + "ddl_tiflash_api.go", + "ddl_worker.go", + "ddl_workerpool.go", + "delete_range.go", + "delete_range_util.go", + "dist_owner.go", + "foreign_key.go", + "generated_column.go", + "index.go", + "index_cop.go", + "index_merge_tmp.go", + "job_table.go", + "mock.go", + "multi_schema_change.go", + "options.go", + "partition.go", + "placement_policy.go", + "reorg.go", + "resource_group.go", + "rollingback.go", + "sanity_check.go", + "schema.go", + "sequence.go", + "split_region.go", + "stat.go", + "table.go", + "table_lock.go", + "ttl.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl", + visibility = [ + ":ddl_friend", + ], + deps = [ + "//br/pkg/lightning/backend", + "//br/pkg/lightning/backend/external", + "//br/pkg/lightning/common", + "//br/pkg/lightning/config", + "//br/pkg/storage", + "//pkg/config", + "//pkg/ddl/copr", + "//pkg/ddl/ingest", + "//pkg/ddl/internal/session", + "//pkg/ddl/label", + "//pkg/ddl/placement", + "//pkg/ddl/resourcegroup", + "//pkg/ddl/syncer", + "//pkg/ddl/util", + "//pkg/distsql", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/handle", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler", + "//pkg/disttask/framework/scheduler/execute", + "//pkg/disttask/framework/storage", + "//pkg/disttask/operator", + "//pkg/domain/infosync", + "//pkg/domain/resourcegroup", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/owner", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/privilege", + "//pkg/resourcemanager/pool/workerpool", + "//pkg/resourcemanager/util", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/statistics", + "//pkg/statistics/handle", + "//pkg/store/copr", + "//pkg/store/driver/backoff", + "//pkg/store/helper", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/tidb-binlog/pump_client", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/backoff", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/disttask", + "//pkg/util/domainutil", + "//pkg/util/filter", + "//pkg/util/gcutil", + "//pkg/util/hack", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/ranger", + "//pkg/util/resourcegrouptag", + "//pkg/util/rowDecoder", + "//pkg/util/rowcodec", + "//pkg/util/set", + "//pkg/util/size", + "//pkg/util/slice", + "//pkg/util/sqlexec", + "//pkg/util/stringutil", + "//pkg/util/syncutil", + "//pkg/util/timeutil", + "//pkg/util/topsql", + "//pkg/util/topsql/state", + "@com_github_google_uuid//:uuid", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//txnkv/rangetask", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//:client", + "@org_golang_x_sync//errgroup", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "ddl_test", + timeout = "moderate", + srcs = [ + "attributes_sql_test.go", + "backfilling_dispatcher_test.go", + "backfilling_test.go", + "cancel_test.go", + "cluster_test.go", + "column_change_test.go", + "column_modify_test.go", + "column_test.go", + "column_type_change_test.go", + "constraint_test.go", + "db_cache_test.go", + "db_change_failpoints_test.go", + "db_change_test.go", + "db_integration_test.go", + "db_rename_test.go", + "db_table_test.go", + "db_test.go", + "ddl_algorithm_test.go", + "ddl_api_test.go", + "ddl_error_test.go", + "ddl_test.go", + "ddl_worker_test.go", + "ddl_workerpool_test.go", + "export_test.go", + "fail_test.go", + "foreign_key_test.go", + "index_change_test.go", + "index_cop_test.go", + "index_modify_test.go", + "integration_test.go", + "job_table_test.go", + "main_test.go", + "modify_column_test.go", + "multi_schema_change_test.go", + "mv_index_test.go", + "options_test.go", + "partition_test.go", + "placement_policy_ddl_test.go", + "placement_policy_test.go", + "placement_sql_test.go", + "primary_key_handle_test.go", + "reorg_partition_test.go", + "repair_table_test.go", + "restart_test.go", + "rollingback_test.go", + "schema_test.go", + "sequence_test.go", + "stat_test.go", + "table_modify_test.go", + "table_split_test.go", + "table_test.go", + "tiflash_replica_test.go", + "ttl_test.go", + ], + embed = [":ddl"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/ddl/copr", + "//pkg/ddl/ingest", + "//pkg/ddl/internal/session", + "//pkg/ddl/placement", + "//pkg/ddl/schematracker", + "//pkg/ddl/syncer", + "//pkg/ddl/testutil", + "//pkg/ddl/util", + "//pkg/ddl/util/callback", + "//pkg/disttask/framework/proto", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/planner/core", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/gcworker", + "//pkg/store/helper", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/domainutil", + "//pkg/util/gcutil", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/mock", + "//pkg/util/sem", + "//pkg/util/sqlexec", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//:client", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/ddl/attributes_sql_test.go b/pkg/ddl/attributes_sql_test.go similarity index 96% rename from ddl/attributes_sql_test.go rename to pkg/ddl/attributes_sql_test.go index a5a3e1dc56b43..c7c240d80e2bd 100644 --- a/ddl/attributes_sql_test.go +++ b/pkg/ddl/attributes_sql_test.go @@ -22,12 +22,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/gcutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/gcutil" "github.com/stretchr/testify/require" tikvutil "github.com/tikv/client-go/v2/util" ) @@ -319,9 +319,9 @@ PARTITION BY RANGE (c) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11) );`) - failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`) + failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`) defer func() { - failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed") + failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed") }() timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) @@ -372,9 +372,9 @@ PARTITION BY RANGE (c) ( PARTITION p0 VALUES LESS THAN (6), PARTITION p1 VALUES LESS THAN (11) );`) - failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`) + failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`) defer func() { - failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed") + failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed") }() timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) diff --git a/ddl/backfilling.go b/pkg/ddl/backfilling.go similarity index 97% rename from ddl/backfilling.go rename to pkg/ddl/backfilling.go index f5dda05b74301..8c5ca71b0eed6 100644 --- a/ddl/backfilling.go +++ b/pkg/ddl/backfilling.go @@ -25,25 +25,25 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - sess "github.com/pingcap/tidb/ddl/internal/session" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tidb/util/topsql" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tidb/pkg/util/topsql" "github.com/prometheus/client_golang/prometheus" "github.com/tikv/client-go/v2/tikv" kvutil "github.com/tikv/client-go/v2/util" diff --git a/ddl/backfilling_clean_s3.go b/pkg/ddl/backfilling_clean_s3.go similarity index 92% rename from ddl/backfilling_clean_s3.go rename to pkg/ddl/backfilling_clean_s3.go index 24523e94653c5..5238b14ec38e9 100644 --- a/ddl/backfilling_clean_s3.go +++ b/pkg/ddl/backfilling_clean_s3.go @@ -21,10 +21,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/backfilling_dispatcher.go b/pkg/ddl/backfilling_dispatcher.go similarity index 95% rename from ddl/backfilling_dispatcher.go rename to pkg/ddl/backfilling_dispatcher.go index ccbac822d4ef4..10151812475bb 100644 --- a/ddl/backfilling_dispatcher.go +++ b/pkg/ddl/backfilling_dispatcher.go @@ -26,19 +26,19 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - framework_store "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - disttaskutil "github.com/pingcap/tidb/util/disttask" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + framework_store "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + disttaskutil "github.com/pingcap/tidb/pkg/util/disttask" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" ) diff --git a/ddl/backfilling_dispatcher_test.go b/pkg/ddl/backfilling_dispatcher_test.go similarity index 95% rename from ddl/backfilling_dispatcher_test.go rename to pkg/ddl/backfilling_dispatcher_test.go index 09e665e7d0e67..9acb21eaeffdb 100644 --- a/ddl/backfilling_dispatcher_test.go +++ b/pkg/ddl/backfilling_dispatcher_test.go @@ -21,13 +21,13 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/ddl/backfilling_dist_scheduler.go b/pkg/ddl/backfilling_dist_scheduler.go similarity index 94% rename from ddl/backfilling_dist_scheduler.go rename to pkg/ddl/backfilling_dist_scheduler.go index 3ea678555064c..a59a410516b4e 100644 --- a/ddl/backfilling_dist_scheduler.go +++ b/pkg/ddl/backfilling_dist_scheduler.go @@ -20,13 +20,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/backfilling_import_cloud.go b/pkg/ddl/backfilling_import_cloud.go similarity index 93% rename from ddl/backfilling_import_cloud.go rename to pkg/ddl/backfilling_import_cloud.go index e658803bde5c6..546ffdd972def 100644 --- a/ddl/backfilling_import_cloud.go +++ b/pkg/ddl/backfilling_import_cloud.go @@ -21,11 +21,11 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/backend" "github.com/pingcap/tidb/br/pkg/lightning/config" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/backfilling_import_local.go b/pkg/ddl/backfilling_import_local.go similarity index 90% rename from ddl/backfilling_import_local.go rename to pkg/ddl/backfilling_import_local.go index 2b8e2b5805915..032f249261f94 100644 --- a/ddl/backfilling_import_local.go +++ b/pkg/ddl/backfilling_import_local.go @@ -18,11 +18,11 @@ import ( "context" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" ) type localImportExecutor struct { diff --git a/ddl/backfilling_merge_sort.go b/pkg/ddl/backfilling_merge_sort.go similarity index 92% rename from ddl/backfilling_merge_sort.go rename to pkg/ddl/backfilling_merge_sort.go index 2862f0141c309..06822c7d4d818 100644 --- a/ddl/backfilling_merge_sort.go +++ b/pkg/ddl/backfilling_merge_sort.go @@ -24,12 +24,12 @@ import ( "github.com/google/uuid" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" "go.uber.org/zap" ) diff --git a/ddl/backfilling_operators.go b/pkg/ddl/backfilling_operators.go similarity index 96% rename from ddl/backfilling_operators.go rename to pkg/ddl/backfilling_operators.go index 008d6cd60fb0c..6cfe8ee932f96 100644 --- a/ddl/backfilling_operators.go +++ b/pkg/ddl/backfilling_operators.go @@ -30,23 +30,23 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/resourcemanager/pool/workerpool" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/ddl/backfilling_read_index.go b/pkg/ddl/backfilling_read_index.go similarity index 94% rename from ddl/backfilling_read_index.go rename to pkg/ddl/backfilling_read_index.go index 289cf7b942db3..6bc7ee52979ac 100644 --- a/ddl/backfilling_read_index.go +++ b/pkg/ddl/backfilling_read_index.go @@ -24,16 +24,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/backfilling_scheduler.go b/pkg/ddl/backfilling_scheduler.go similarity index 95% rename from ddl/backfilling_scheduler.go rename to pkg/ddl/backfilling_scheduler.go index 7f54f6650110a..02c48a532ee12 100644 --- a/ddl/backfilling_scheduler.go +++ b/pkg/ddl/backfilling_scheduler.go @@ -21,24 +21,24 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/resourcemanager/pool/workerpool" - poolutil "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - decoder "github.com/pingcap/tidb/util/rowDecoder" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool" + poolutil "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" "go.uber.org/zap" ) diff --git a/ddl/backfilling_test.go b/pkg/ddl/backfilling_test.go similarity index 92% rename from ddl/backfilling_test.go rename to pkg/ddl/backfilling_test.go index 17030d9a2f6f0..afa1f60c2120d 100644 --- a/ddl/backfilling_test.go +++ b/pkg/ddl/backfilling_test.go @@ -19,11 +19,11 @@ import ( "context" "testing" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/callback.go b/pkg/ddl/callback.go new file mode 100644 index 0000000000000..114003317df4c --- /dev/null +++ b/pkg/ddl/callback.go @@ -0,0 +1,234 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// Interceptor is used for DDL. +type Interceptor interface { + // OnGetInfoSchema is an intercept which is called in the function ddl.GetInfoSchema(). It is used in the tests. + OnGetInfoSchema(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema +} + +// BaseInterceptor implements Interceptor. +type BaseInterceptor struct{} + +// OnGetInfoSchema implements Interceptor.OnGetInfoSchema interface. +func (*BaseInterceptor) OnGetInfoSchema(_ sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { + return is +} + +// Callback is used for DDL. +type Callback interface { + ReorgCallback + // OnChanged is called after a ddl statement is finished. + OnChanged(err error) error + // OnSchemaStateChanged is called after a schema state is changed. + OnSchemaStateChanged(schemaVer int64) + // OnJobRunBefore is called before running job. + OnJobRunBefore(job *model.Job) + // OnJobRunAfter is called after running job. + OnJobRunAfter(job *model.Job) + // OnJobUpdated is called after the running job is updated. + OnJobUpdated(job *model.Job) + // OnWatched is called after watching owner is completed. + OnWatched(ctx context.Context) + // OnGetJobBefore is called before getting job. + OnGetJobBefore(jobType string) + // OnGetJobAfter is called after getting job. + OnGetJobAfter(jobType string, job *model.Job) +} + +// BaseCallback implements Callback.OnChanged interface. +type BaseCallback struct { +} + +// OnChanged implements Callback interface. +func (*BaseCallback) OnChanged(err error) error { + return err +} + +// OnSchemaStateChanged implements Callback interface. +func (*BaseCallback) OnSchemaStateChanged(_ int64) { + // Nothing to do. +} + +// OnJobRunBefore implements Callback.OnJobRunBefore interface. +func (*BaseCallback) OnJobRunBefore(_ *model.Job) { + // Nothing to do. +} + +// OnJobRunAfter implements Callback.OnJobRunAfter interface. +func (*BaseCallback) OnJobRunAfter(_ *model.Job) { + // Nothing to do. +} + +// OnJobUpdated implements Callback.OnJobUpdated interface. +func (*BaseCallback) OnJobUpdated(_ *model.Job) { + // Nothing to do. +} + +// OnWatched implements Callback.OnWatched interface. +func (*BaseCallback) OnWatched(_ context.Context) { + // Nothing to do. +} + +// OnGetJobBefore implements Callback.OnGetJobBefore interface. +func (*BaseCallback) OnGetJobBefore(_ string) { + // Nothing to do. +} + +// OnGetJobAfter implements Callback.OnGetJobAfter interface. +func (*BaseCallback) OnGetJobAfter(_ string, _ *model.Job) { + // Nothing to do. +} + +// OnUpdateReorgInfo implements ReorgCallback interface. +func (*BaseCallback) OnUpdateReorgInfo(_ *model.Job, _ int64) { +} + +// DomainReloader is used to avoid import loop. +type DomainReloader interface { + Reload() error +} + +// ReorgCallback is the callback for DDL reorganization. +type ReorgCallback interface { + // OnUpdateReorgInfo is called after updating reorg info for partitions. + OnUpdateReorgInfo(job *model.Job, pid int64) +} + +// ****************************** Start of Customized DDL Callback Instance **************************************** + +// DefaultCallback is the default callback that TiDB will use. +type DefaultCallback struct { + *BaseCallback + do DomainReloader +} + +// OnChanged overrides ddl Callback interface. +func (c *DefaultCallback) OnChanged(err error) error { + if err != nil { + return err + } + logutil.BgLogger().Info("performing DDL change, must reload") + + err = c.do.Reload() + if err != nil { + logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) + } + + return nil +} + +// OnSchemaStateChanged overrides the ddl Callback interface. +func (c *DefaultCallback) OnSchemaStateChanged(_ int64) { + err := c.do.Reload() + if err != nil { + logutil.BgLogger().Error("domain callback failed on schema state changed", zap.Error(err)) + } +} + +func newDefaultCallBack(do DomainReloader) Callback { + return &DefaultCallback{BaseCallback: &BaseCallback{}, do: do} +} + +// ****************************** End of Default DDL Callback Instance ********************************************* + +// ****************************** Start of CTC DDL Callback Instance *********************************************** + +// ctcCallback is the customized callback that TiDB will use. +// ctc is named from column type change, here after we call them ctc for short. +type ctcCallback struct { + *BaseCallback + do DomainReloader +} + +// OnChanged overrides ddl Callback interface. +func (c *ctcCallback) OnChanged(err error) error { + if err != nil { + return err + } + logutil.BgLogger().Info("performing DDL change, must reload") + + err = c.do.Reload() + if err != nil { + logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) + } + return nil +} + +// OnSchemaStateChanged overrides the ddl Callback interface. +func (c *ctcCallback) OnSchemaStateChanged(_ int64) { + err := c.do.Reload() + if err != nil { + logutil.BgLogger().Error("domain callback failed on schema state changed", zap.Error(err)) + } +} + +// OnJobRunBefore is used to run the user customized logic of `onJobRunBefore` first. +func (*ctcCallback) OnJobRunBefore(job *model.Job) { + log.Info("on job run before", zap.String("job", job.String())) + // Only block the ctc type ddl here. + if job.Type != model.ActionModifyColumn { + return + } + switch job.SchemaState { + case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: + logutil.BgLogger().Warn(fmt.Sprintf("[DDL_HOOK] Hang for 0.5 seconds on %s state triggered", job.SchemaState.String())) + time.Sleep(500 * time.Millisecond) + } +} + +func newCTCCallBack(do DomainReloader) Callback { + return &ctcCallback{do: do} +} + +// ****************************** End of CTC DDL Callback Instance *************************************************** + +var ( + customizedCallBackRegisterMap = map[string]func(do DomainReloader) Callback{} +) + +func init() { + // init the callback register map. + customizedCallBackRegisterMap["default_hook"] = newDefaultCallBack + customizedCallBackRegisterMap["ctc_hook"] = newCTCCallBack +} + +// GetCustomizedHook get the hook registered in the hookMap. +func GetCustomizedHook(s string) (func(do DomainReloader) Callback, error) { + s = strings.ToLower(s) + s = strings.TrimSpace(s) + fact, ok := customizedCallBackRegisterMap[s] + if !ok { + logutil.BgLogger().Error("bad ddl hook " + s) + return nil, errors.Errorf("ddl hook `%s` is not found in hook registered map", s) + } + return fact, nil +} diff --git a/ddl/cancel_test.go b/pkg/ddl/cancel_test.go similarity index 98% rename from ddl/cancel_test.go rename to pkg/ddl/cancel_test.go index 0bb662600bd16..e283933fc37e5 100644 --- a/ddl/cancel_test.go +++ b/pkg/ddl/cancel_test.go @@ -21,13 +21,13 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" "github.com/stretchr/testify/require" atomicutil "go.uber.org/atomic" ) @@ -237,9 +237,9 @@ func TestCancel(t *testing.T) { tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 1") tk = testkit.NewTestKit(t, store) tk.MustExec("use test") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockBackfillSlow", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockBackfillSlow", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockBackfillSlow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockBackfillSlow")) }() hook := &callback.TestDDLCallback{Do: dom} diff --git a/pkg/ddl/cluster.go b/pkg/ddl/cluster.go new file mode 100644 index 0000000000000..e05d0c4006be0 --- /dev/null +++ b/pkg/ddl/cluster.go @@ -0,0 +1,829 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "bytes" + "cmp" + "context" + "encoding/hex" + "fmt" + "slices" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/errorpb" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/filter" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/logutil" + tikvstore "github.com/tikv/client-go/v2/kv" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + "github.com/tikv/client-go/v2/txnkv/rangetask" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +var pdScheduleKey = []string{ + "merge-schedule-limit", +} + +const ( + flashbackMaxBackoff = 1800000 // 1800s + flashbackTimeout = 3 * time.Minute // 3min +) + +const ( + pdScheduleArgsOffset = 1 + iota + gcEnabledOffset + autoAnalyzeOffset + readOnlyOffset + totalLockedRegionsOffset + startTSOffset + commitTSOffset + ttlJobEnableOffSet + keyRangesOffset +) + +func closePDSchedule() error { + closeMap := make(map[string]interface{}) + for _, key := range pdScheduleKey { + closeMap[key] = 0 + } + return infosync.SetPDScheduleConfig(context.Background(), closeMap) +} + +func savePDSchedule(job *model.Job) error { + retValue, err := infosync.GetPDScheduleConfig(context.Background()) + if err != nil { + return err + } + saveValue := make(map[string]interface{}) + for _, key := range pdScheduleKey { + saveValue[key] = retValue[key] + } + job.Args[pdScheduleArgsOffset] = &saveValue + return nil +} + +func recoverPDSchedule(pdScheduleParam map[string]interface{}) error { + if pdScheduleParam == nil { + return nil + } + return infosync.SetPDScheduleConfig(context.Background(), pdScheduleParam) +} + +func getStoreGlobalMinSafeTS(s kv.Storage) time.Time { + minSafeTS := s.GetMinSafeTS(kv.GlobalTxnScope) + // Inject mocked SafeTS for test. + failpoint.Inject("injectSafeTS", func(val failpoint.Value) { + injectTS := val.(int) + minSafeTS = uint64(injectTS) + }) + return oracle.GetTimeFromTS(minSafeTS) +} + +// ValidateFlashbackTS validates that flashBackTS in range [gcSafePoint, currentTS). +func ValidateFlashbackTS(ctx context.Context, sctx sessionctx.Context, flashBackTS uint64) error { + currentTS, err := sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) + // If we fail to calculate currentTS from local time, fallback to get a timestamp from PD. + if err != nil { + metrics.ValidateReadTSFromPDCount.Inc() + currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) + if err != nil { + return errors.Errorf("fail to validate flashback timestamp: %v", err) + } + currentTS = currentVer.Ver + } + oracleFlashbackTS := oracle.GetTimeFromTS(flashBackTS) + if oracleFlashbackTS.After(oracle.GetTimeFromTS(currentTS)) { + return errors.Errorf("cannot set flashback timestamp to future time") + } + + flashbackGetMinSafeTimeTimeout := time.Minute + failpoint.Inject("changeFlashbackGetMinSafeTimeTimeout", func(val failpoint.Value) { + t := val.(int) + flashbackGetMinSafeTimeTimeout = time.Duration(t) + }) + + start := time.Now() + minSafeTime := getStoreGlobalMinSafeTS(sctx.GetStore()) + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for oracleFlashbackTS.After(minSafeTime) { + if time.Since(start) >= flashbackGetMinSafeTimeTimeout { + return errors.Errorf("cannot set flashback timestamp after min-resolved-ts(%s)", minSafeTime) + } + select { + case <-ticker.C: + minSafeTime = getStoreGlobalMinSafeTS(sctx.GetStore()) + case <-ctx.Done(): + return ctx.Err() + } + } + + gcSafePoint, err := gcutil.GetGCSafePoint(sctx) + if err != nil { + return err + } + + return gcutil.ValidateSnapshotWithGCSafePoint(flashBackTS, gcSafePoint) +} + +func getTiDBTTLJobEnable(sess sessionctx.Context) (string, error) { + val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBTTLJobEnable) + if err != nil { + return "", errors.Trace(err) + } + return val, nil +} + +func setTiDBTTLJobEnable(ctx context.Context, sess sessionctx.Context, value string) error { + return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBTTLJobEnable, value) +} + +func setTiDBEnableAutoAnalyze(ctx context.Context, sess sessionctx.Context, value string) error { + return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBEnableAutoAnalyze, value) +} + +func getTiDBEnableAutoAnalyze(sess sessionctx.Context) (string, error) { + val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableAutoAnalyze) + if err != nil { + return "", errors.Trace(err) + } + return val, nil +} + +func setTiDBSuperReadOnly(ctx context.Context, sess sessionctx.Context, value string) error { + return sess.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(ctx, variable.TiDBSuperReadOnly, value) +} + +func getTiDBSuperReadOnly(sess sessionctx.Context) (string, error) { + val, err := sess.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBSuperReadOnly) + if err != nil { + return "", errors.Trace(err) + } + return val, nil +} + +func isFlashbackSupportedDDLAction(action model.ActionType) bool { + switch action { + case model.ActionSetTiFlashReplica, model.ActionUpdateTiFlashReplicaStatus, model.ActionAlterPlacementPolicy, + model.ActionAlterTablePlacement, model.ActionAlterTablePartitionPlacement, model.ActionCreatePlacementPolicy, + model.ActionDropPlacementPolicy, model.ActionModifySchemaDefaultPlacement, + model.ActionAlterTableAttributes, model.ActionAlterTablePartitionAttributes: + return false + default: + return true + } +} + +func checkSystemSchemaID(t *meta.Meta, schemaID int64, flashbackTSString string) error { + if schemaID <= 0 { + return nil + } + dbInfo, err := t.GetDatabase(schemaID) + if err != nil || dbInfo == nil { + return errors.Trace(err) + } + if filter.IsSystemSchema(dbInfo.Name.L) { + return errors.Errorf("Detected modified system table during [%s, now), can't do flashback", flashbackTSString) + } + return nil +} + +func checkAndSetFlashbackClusterInfo(se sessionctx.Context, d *ddlCtx, t *meta.Meta, job *model.Job, flashbackTS uint64) (err error) { + if err = ValidateFlashbackTS(d.ctx, se, flashbackTS); err != nil { + return err + } + + if err = gcutil.DisableGC(se); err != nil { + return err + } + if err = closePDSchedule(); err != nil { + return err + } + if err = setTiDBEnableAutoAnalyze(d.ctx, se, variable.Off); err != nil { + return err + } + if err = setTiDBSuperReadOnly(d.ctx, se, variable.On); err != nil { + return err + } + if err = setTiDBTTLJobEnable(d.ctx, se, variable.Off); err != nil { + return err + } + + nowSchemaVersion, err := t.GetSchemaVersion() + if err != nil { + return errors.Trace(err) + } + + flashbackSnapshotMeta := meta.NewSnapshotMeta(d.store.GetSnapshot(kv.NewVersion(flashbackTS))) + flashbackSchemaVersion, err := flashbackSnapshotMeta.GetSchemaVersion() + if err != nil { + return errors.Trace(err) + } + + flashbackTSString := oracle.GetTimeFromTS(flashbackTS).String() + + // Check if there is an upgrade during [flashbackTS, now) + sql := fmt.Sprintf("select VARIABLE_VALUE from mysql.tidb as of timestamp '%s' where VARIABLE_NAME='tidb_server_version'", flashbackTSString) + rows, err := sess.NewSession(se).Execute(d.ctx, sql, "check_tidb_server_version") + if err != nil || len(rows) == 0 { + return errors.Errorf("Get history `tidb_server_version` failed, can't do flashback") + } + sql = fmt.Sprintf("select 1 from mysql.tidb where VARIABLE_NAME='tidb_server_version' and VARIABLE_VALUE=%s", rows[0].GetString(0)) + rows, err = sess.NewSession(se).Execute(d.ctx, sql, "check_tidb_server_version") + if err != nil { + return errors.Trace(err) + } + if len(rows) == 0 { + return errors.Errorf("Detected TiDB upgrade during [%s, now), can't do flashback", flashbackTSString) + } + + // Check is there a DDL task at flashbackTS. + sql = fmt.Sprintf("select count(*) from mysql.%s as of timestamp '%s'", JobTable, flashbackTSString) + rows, err = sess.NewSession(se).Execute(d.ctx, sql, "check_history_job") + if err != nil || len(rows) == 0 { + return errors.Errorf("Get history ddl jobs failed, can't do flashback") + } + if rows[0].GetInt64(0) != 0 { + return errors.Errorf("Detected another DDL job at %s, can't do flashback", flashbackTSString) + } + + // If flashbackSchemaVersion not same as nowSchemaVersion, we should check all schema diffs during [flashbackTs, now). + for i := flashbackSchemaVersion + 1; i <= nowSchemaVersion; i++ { + diff, err := t.GetSchemaDiff(i) + if err != nil { + return errors.Trace(err) + } + if diff == nil { + continue + } + if !isFlashbackSupportedDDLAction(diff.Type) { + return errors.Errorf("Detected unsupported DDL job type(%s) during [%s, now), can't do flashback", diff.Type.String(), flashbackTSString) + } + err = checkSystemSchemaID(flashbackSnapshotMeta, diff.SchemaID, flashbackTSString) + if err != nil { + return errors.Trace(err) + } + } + + jobs, err := GetAllDDLJobs(se) + if err != nil { + return errors.Trace(err) + } + // Other ddl jobs in queue, return error. + if len(jobs) != 1 { + var otherJob *model.Job + for _, j := range jobs { + if j.ID != job.ID { + otherJob = j + break + } + } + return errors.Errorf("have other ddl jobs(jobID: %d) in queue, can't do flashback", otherJob.ID) + } + return nil +} + +func addToSlice(schema string, tableName string, tableID int64, flashbackIDs []int64) []int64 { + if filter.IsSystemSchema(schema) && !strings.HasPrefix(tableName, "stats_") && tableName != "gc_delete_range" { + flashbackIDs = append(flashbackIDs, tableID) + } + return flashbackIDs +} + +// GetTableDataKeyRanges get keyRanges by `flashbackIDs`. +// This func will return all flashback table data key ranges. +func GetTableDataKeyRanges(nonFlashbackTableIDs []int64) []kv.KeyRange { + var keyRanges []kv.KeyRange + + nonFlashbackTableIDs = append(nonFlashbackTableIDs, -1) + + slices.SortFunc(nonFlashbackTableIDs, func(a, b int64) int { + return cmp.Compare(a, b) + }) + + for i := 1; i < len(nonFlashbackTableIDs); i++ { + keyRanges = append(keyRanges, kv.KeyRange{ + StartKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[i-1] + 1), + EndKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[i]), + }) + } + + // Add all other key ranges. + keyRanges = append(keyRanges, kv.KeyRange{ + StartKey: tablecodec.EncodeTablePrefix(nonFlashbackTableIDs[len(nonFlashbackTableIDs)-1] + 1), + EndKey: tablecodec.EncodeTablePrefix(meta.MaxGlobalID), + }) + + return keyRanges +} + +// GetFlashbackKeyRanges get keyRanges for flashback cluster. +// It contains all non system table key ranges and meta data key ranges. +// The time complexity is O(nlogn). +func GetFlashbackKeyRanges(sess sessionctx.Context, flashbackTS uint64) ([]kv.KeyRange, error) { + schemas := sess.GetDomainInfoSchema().(infoschema.InfoSchema).AllSchemas() + + // The semantic of keyRanges(output). + keyRanges := make([]kv.KeyRange, 0) + + // get snapshot schema IDs. + flashbackSnapshotMeta := meta.NewSnapshotMeta(sess.GetStore().GetSnapshot(kv.NewVersion(flashbackTS))) + snapshotSchemas, err := flashbackSnapshotMeta.ListDatabases() + if err != nil { + return nil, errors.Trace(err) + } + + schemaIDs := make(map[int64]struct{}) + for _, schema := range schemas { + if !filter.IsSystemSchema(schema.Name.L) { + schemaIDs[schema.ID] = struct{}{} + } + } + for _, schema := range snapshotSchemas { + if !filter.IsSystemSchema(schema.Name.L) { + schemaIDs[schema.ID] = struct{}{} + } + } + + // The meta data key ranges. + for schemaID := range schemaIDs { + metaStartKey := tablecodec.EncodeMetaKeyPrefix(meta.DBkey(schemaID)) + metaEndKey := tablecodec.EncodeMetaKeyPrefix(meta.DBkey(schemaID + 1)) + keyRanges = append(keyRanges, kv.KeyRange{ + StartKey: metaStartKey, + EndKey: metaEndKey, + }) + } + + startKey := tablecodec.EncodeMetaKeyPrefix([]byte("DBs")) + keyRanges = append(keyRanges, kv.KeyRange{ + StartKey: startKey, + EndKey: startKey.PrefixNext(), + }) + + var nonFlashbackTableIDs []int64 + for _, db := range schemas { + for _, table := range db.Tables { + if !table.IsBaseTable() || table.ID > meta.MaxGlobalID { + continue + } + nonFlashbackTableIDs = addToSlice(db.Name.L, table.Name.L, table.ID, nonFlashbackTableIDs) + if table.Partition != nil { + for _, partition := range table.Partition.Definitions { + nonFlashbackTableIDs = addToSlice(db.Name.L, table.Name.L, partition.ID, nonFlashbackTableIDs) + } + } + } + } + + return append(keyRanges, GetTableDataKeyRanges(nonFlashbackTableIDs)...), nil +} + +// SendPrepareFlashbackToVersionRPC prepares regions for flashback, the purpose is to put region into flashback state which region stop write +// Function also be called by BR for volume snapshot backup and restore +func SendPrepareFlashbackToVersionRPC( + ctx context.Context, + s tikv.Storage, + flashbackTS, startTS uint64, + r tikvstore.KeyRange, +) (rangetask.TaskStat, error) { + startKey, rangeEndKey := r.StartKey, r.EndKey + var taskStat rangetask.TaskStat + bo := tikv.NewBackoffer(ctx, flashbackMaxBackoff) + for { + select { + case <-ctx.Done(): + return taskStat, errors.WithStack(ctx.Err()) + default: + } + + if len(rangeEndKey) > 0 && bytes.Compare(startKey, rangeEndKey) >= 0 { + break + } + + loc, err := s.GetRegionCache().LocateKey(bo, startKey) + if err != nil { + return taskStat, err + } + + endKey := loc.EndKey + isLast := len(endKey) == 0 || (len(rangeEndKey) > 0 && bytes.Compare(endKey, rangeEndKey) >= 0) + // If it is the last region. + if isLast { + endKey = rangeEndKey + } + + logutil.BgLogger().Info("send prepare flashback request", zap.String("category", "ddl"), zap.Uint64("region_id", loc.Region.GetID()), + zap.String("start_key", hex.EncodeToString(startKey)), zap.String("end_key", hex.EncodeToString(endKey))) + + req := tikvrpc.NewRequest(tikvrpc.CmdPrepareFlashbackToVersion, &kvrpcpb.PrepareFlashbackToVersionRequest{ + StartKey: startKey, + EndKey: endKey, + StartTs: startTS, + Version: flashbackTS, + }) + + resp, err := s.SendReq(bo, req, loc.Region, flashbackTimeout) + if err != nil { + return taskStat, err + } + regionErr, err := resp.GetRegionError() + if err != nil { + return taskStat, err + } + failpoint.Inject("mockPrepareMeetsEpochNotMatch", func(val failpoint.Value) { + if val.(bool) && bo.ErrorsNum() == 0 { + regionErr = &errorpb.Error{ + Message: "stale epoch", + EpochNotMatch: &errorpb.EpochNotMatch{}, + } + } + }) + if regionErr != nil { + err = bo.Backoff(tikv.BoRegionMiss(), errors.New(regionErr.String())) + if err != nil { + return taskStat, err + } + continue + } + if resp.Resp == nil { + logutil.BgLogger().Warn("prepare flashback miss resp body", zap.Uint64("region_id", loc.Region.GetID())) + err = bo.Backoff(tikv.BoTiKVRPC(), errors.New("prepare flashback rpc miss resp body")) + if err != nil { + return taskStat, err + } + continue + } + prepareFlashbackToVersionResp := resp.Resp.(*kvrpcpb.PrepareFlashbackToVersionResponse) + if err := prepareFlashbackToVersionResp.GetError(); err != "" { + boErr := bo.Backoff(tikv.BoTiKVRPC(), errors.New(err)) + if boErr != nil { + return taskStat, boErr + } + continue + } + taskStat.CompletedRegions++ + if isLast { + break + } + bo = tikv.NewBackoffer(ctx, flashbackMaxBackoff) + startKey = endKey + } + return taskStat, nil +} + +// SendFlashbackToVersionRPC flashback the MVCC key to the version +// Function also be called by BR for volume snapshot backup and restore +func SendFlashbackToVersionRPC( + ctx context.Context, + s tikv.Storage, + version uint64, + startTS, commitTS uint64, + r tikvstore.KeyRange, +) (rangetask.TaskStat, error) { + startKey, rangeEndKey := r.StartKey, r.EndKey + var taskStat rangetask.TaskStat + bo := tikv.NewBackoffer(ctx, flashbackMaxBackoff) + for { + select { + case <-ctx.Done(): + return taskStat, errors.WithStack(ctx.Err()) + default: + } + + if len(rangeEndKey) > 0 && bytes.Compare(startKey, rangeEndKey) >= 0 { + break + } + + loc, err := s.GetRegionCache().LocateKey(bo, startKey) + if err != nil { + return taskStat, err + } + + endKey := loc.EndKey + isLast := len(endKey) == 0 || (len(rangeEndKey) > 0 && bytes.Compare(endKey, rangeEndKey) >= 0) + // If it is the last region. + if isLast { + endKey = rangeEndKey + } + + logutil.BgLogger().Info("send flashback request", zap.String("category", "ddl"), zap.Uint64("region_id", loc.Region.GetID()), + zap.String("start_key", hex.EncodeToString(startKey)), zap.String("end_key", hex.EncodeToString(endKey))) + + req := tikvrpc.NewRequest(tikvrpc.CmdFlashbackToVersion, &kvrpcpb.FlashbackToVersionRequest{ + Version: version, + StartKey: startKey, + EndKey: endKey, + StartTs: startTS, + CommitTs: commitTS, + }) + + resp, err := s.SendReq(bo, req, loc.Region, flashbackTimeout) + if err != nil { + logutil.BgLogger().Warn("send request meets error", zap.Uint64("region_id", loc.Region.GetID()), zap.Error(err)) + if err.Error() != fmt.Sprintf("region %d is not prepared for the flashback", loc.Region.GetID()) { + return taskStat, err + } + } else { + regionErr, err := resp.GetRegionError() + if err != nil { + return taskStat, err + } + if regionErr != nil { + err = bo.Backoff(tikv.BoRegionMiss(), errors.New(regionErr.String())) + if err != nil { + return taskStat, err + } + continue + } + if resp.Resp == nil { + logutil.BgLogger().Warn("flashback miss resp body", zap.Uint64("region_id", loc.Region.GetID())) + err = bo.Backoff(tikv.BoTiKVRPC(), errors.New("flashback rpc miss resp body")) + if err != nil { + return taskStat, err + } + continue + } + flashbackToVersionResp := resp.Resp.(*kvrpcpb.FlashbackToVersionResponse) + if respErr := flashbackToVersionResp.GetError(); respErr != "" { + boErr := bo.Backoff(tikv.BoTiKVRPC(), errors.New(respErr)) + if boErr != nil { + return taskStat, boErr + } + continue + } + } + taskStat.CompletedRegions++ + if isLast { + break + } + bo = tikv.NewBackoffer(ctx, flashbackMaxBackoff) + startKey = endKey + } + return taskStat, nil +} + +func flashbackToVersion( + ctx context.Context, + d *ddlCtx, + handler rangetask.TaskHandler, + startKey []byte, endKey []byte, +) (err error) { + return rangetask.NewRangeTaskRunner( + "flashback-to-version-runner", + d.store.(tikv.Storage), + int(variable.GetDDLFlashbackConcurrency()), + handler, + ).RunOnRange(ctx, startKey, endKey) +} + +func splitRegionsByKeyRanges(d *ddlCtx, keyRanges []kv.KeyRange) { + if s, ok := d.store.(kv.SplittableStore); ok { + for _, keys := range keyRanges { + for { + // tableID is useless when scatter == false + _, err := s.SplitRegions(d.ctx, [][]byte{keys.StartKey, keys.EndKey}, false, nil) + if err == nil { + break + } + } + } + } +} + +// A Flashback has 4 different stages. +// 1. before lock flashbackClusterJobID, check clusterJobID and lock it. +// 2. before flashback start, check timestamp, disable GC and close PD schedule, get flashback key ranges. +// 3. phase 1, lock flashback key ranges. +// 4. phase 2, send flashback RPC, do flashback jobs. +func (w *worker) onFlashbackCluster(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + inFlashbackTest := false + failpoint.Inject("mockFlashbackTest", func(val failpoint.Value) { + if val.(bool) { + inFlashbackTest = true + } + }) + // TODO: Support flashback in unistore. + if d.store.Name() != "TiKV" && !inFlashbackTest { + job.State = model.JobStateCancelled + return ver, errors.Errorf("Not support flashback cluster in non-TiKV env") + } + + var flashbackTS, lockedRegions, startTS, commitTS uint64 + var pdScheduleValue map[string]interface{} + var autoAnalyzeValue, readOnlyValue, ttlJobEnableValue string + var gcEnabledValue bool + var keyRanges []kv.KeyRange + if err := job.DecodeArgs(&flashbackTS, &pdScheduleValue, &gcEnabledValue, &autoAnalyzeValue, &readOnlyValue, &lockedRegions, &startTS, &commitTS, &ttlJobEnableValue, &keyRanges); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + var totalRegions, completedRegions atomic.Uint64 + totalRegions.Store(lockedRegions) + + sess, err := w.sessPool.Get() + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + defer w.sessPool.Put(sess) + + switch job.SchemaState { + // Stage 1, check and set FlashbackClusterJobID, and update job args. + case model.StateNone: + if err = savePDSchedule(job); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + gcEnableValue, err := gcutil.CheckGCEnable(sess) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.Args[gcEnabledOffset] = &gcEnableValue + autoAnalyzeValue, err = getTiDBEnableAutoAnalyze(sess) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.Args[autoAnalyzeOffset] = &autoAnalyzeValue + readOnlyValue, err = getTiDBSuperReadOnly(sess) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.Args[readOnlyOffset] = &readOnlyValue + ttlJobEnableValue, err = getTiDBTTLJobEnable(sess) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.Args[ttlJobEnableOffSet] = &ttlJobEnableValue + job.SchemaState = model.StateDeleteOnly + return ver, nil + // Stage 2, check flashbackTS, close GC and PD schedule, get flashback key ranges. + case model.StateDeleteOnly: + if err = checkAndSetFlashbackClusterInfo(sess, d, t, job, flashbackTS); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // We should get startTS here to avoid lost startTS when TiDB crashed during send prepare flashback RPC. + startTS, err = d.store.GetOracle().GetTimestamp(d.ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.Args[startTSOffset] = startTS + keyRanges, err = GetFlashbackKeyRanges(sess, flashbackTS) + if err != nil { + return ver, errors.Trace(err) + } + job.Args[keyRangesOffset] = keyRanges + job.SchemaState = model.StateWriteOnly + return updateSchemaVersion(d, t, job) + // Stage 3, lock related key ranges. + case model.StateWriteOnly: + // TODO: Support flashback in unistore. + if inFlashbackTest { + job.SchemaState = model.StateWriteReorganization + return updateSchemaVersion(d, t, job) + } + // Split region by keyRanges, make sure no unrelated key ranges be locked. + splitRegionsByKeyRanges(d, keyRanges) + totalRegions.Store(0) + for _, r := range keyRanges { + if err = flashbackToVersion(d.ctx, d, + func(ctx context.Context, r tikvstore.KeyRange) (rangetask.TaskStat, error) { + stats, err := SendPrepareFlashbackToVersionRPC(ctx, d.store.(tikv.Storage), flashbackTS, startTS, r) + totalRegions.Add(uint64(stats.CompletedRegions)) + return stats, err + }, r.StartKey, r.EndKey); err != nil { + logutil.BgLogger().Warn("Get error when do flashback", zap.String("category", "ddl"), zap.Error(err)) + return ver, err + } + } + job.Args[totalLockedRegionsOffset] = totalRegions.Load() + + // We should get commitTS here to avoid lost commitTS when TiDB crashed during send flashback RPC. + commitTS, err = d.store.GetOracle().GetTimestamp(d.ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) + if err != nil { + return ver, errors.Trace(err) + } + job.Args[commitTSOffset] = commitTS + job.SchemaState = model.StateWriteReorganization + return ver, nil + // Stage 4, get key ranges and send flashback RPC. + case model.StateWriteReorganization: + // TODO: Support flashback in unistore. + if inFlashbackTest { + asyncNotifyEvent(d, &util.Event{Tp: model.ActionFlashbackCluster}) + job.State = model.JobStateDone + job.SchemaState = model.StatePublic + return ver, nil + } + + for _, r := range keyRanges { + if err = flashbackToVersion(d.ctx, d, + func(ctx context.Context, r tikvstore.KeyRange) (rangetask.TaskStat, error) { + // Use same startTS as prepare phase to simulate 1PC txn. + stats, err := SendFlashbackToVersionRPC(ctx, d.store.(tikv.Storage), flashbackTS, startTS, commitTS, r) + completedRegions.Add(uint64(stats.CompletedRegions)) + logutil.BgLogger().Info("flashback cluster stats", zap.String("category", "ddl"), + zap.Uint64("complete regions", completedRegions.Load()), + zap.Uint64("total regions", totalRegions.Load()), + zap.Error(err)) + return stats, err + }, r.StartKey, r.EndKey); err != nil { + logutil.BgLogger().Warn("Get error when do flashback", zap.String("category", "ddl"), zap.Error(err)) + return ver, errors.Trace(err) + } + } + + asyncNotifyEvent(d, &util.Event{Tp: model.ActionFlashbackCluster}) + job.State = model.JobStateDone + job.SchemaState = model.StatePublic + return updateSchemaVersion(d, t, job) + } + return ver, nil +} + +func finishFlashbackCluster(w *worker, job *model.Job) error { + // Didn't do anything during flashback, return directly + if job.SchemaState == model.StateNone { + return nil + } + + var flashbackTS, lockedRegions, startTS, commitTS uint64 + var pdScheduleValue map[string]interface{} + var autoAnalyzeValue, readOnlyValue, ttlJobEnableValue string + var gcEnabled bool + + if err := job.DecodeArgs(&flashbackTS, &pdScheduleValue, &gcEnabled, &autoAnalyzeValue, &readOnlyValue, &lockedRegions, &startTS, &commitTS, &ttlJobEnableValue); err != nil { + return errors.Trace(err) + } + sess, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(sess) + + err = kv.RunInNewTxn(w.ctx, w.store, true, func(ctx context.Context, txn kv.Transaction) error { + if err = recoverPDSchedule(pdScheduleValue); err != nil { + return err + } + if gcEnabled { + if err = gcutil.EnableGC(sess); err != nil { + return err + } + } + if err = setTiDBSuperReadOnly(w.ctx, sess, readOnlyValue); err != nil { + return err + } + + if job.IsCancelled() { + // only restore `tidb_ttl_job_enable` when flashback failed + if err = setTiDBTTLJobEnable(w.ctx, sess, ttlJobEnableValue); err != nil { + return err + } + } + + return setTiDBEnableAutoAnalyze(w.ctx, sess, autoAnalyzeValue) + }) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/ddl/cluster_test.go b/pkg/ddl/cluster_test.go new file mode 100644 index 0000000000000..d0fd1a7a1554c --- /dev/null +++ b/pkg/ddl/cluster_test.go @@ -0,0 +1,278 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" +) + +func TestGetTableDataKeyRanges(t *testing.T) { + // case 1, empty flashbackIDs + keyRanges := ddl.GetTableDataKeyRanges([]int64{}) + require.Len(t, keyRanges, 1) + require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) + require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) + + // case 2, insert a execluded table ID + keyRanges = ddl.GetTableDataKeyRanges([]int64{3}) + require.Len(t, keyRanges, 2) + require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) + require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(3)) + require.Equal(t, keyRanges[1].StartKey, tablecodec.EncodeTablePrefix(4)) + require.Equal(t, keyRanges[1].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) + + // case 3, insert some execluded table ID + keyRanges = ddl.GetTableDataKeyRanges([]int64{3, 5, 9}) + require.Len(t, keyRanges, 4) + require.Equal(t, keyRanges[0].StartKey, tablecodec.EncodeTablePrefix(0)) + require.Equal(t, keyRanges[0].EndKey, tablecodec.EncodeTablePrefix(3)) + require.Equal(t, keyRanges[1].StartKey, tablecodec.EncodeTablePrefix(4)) + require.Equal(t, keyRanges[1].EndKey, tablecodec.EncodeTablePrefix(5)) + require.Equal(t, keyRanges[2].StartKey, tablecodec.EncodeTablePrefix(6)) + require.Equal(t, keyRanges[2].EndKey, tablecodec.EncodeTablePrefix(9)) + require.Equal(t, keyRanges[3].StartKey, tablecodec.EncodeTablePrefix(10)) + require.Equal(t, keyRanges[3].EndKey, tablecodec.EncodeTablePrefix(meta.MaxGlobalID)) +} + +func TestFlashbackCloseAndResetPDSchedule(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + originHook := dom.DDL().GetHook() + tk := testkit.NewTestKit(t, store) + + injectSafeTS := oracle.GoTimeToTS(time.Now().Add(10 * time.Second)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", + fmt.Sprintf("return(%v)", injectSafeTS))) + + oldValue := map[string]interface{}{ + "merge-schedule-limit": 1, + } + require.NoError(t, infosync.SetPDScheduleConfig(context.Background(), oldValue)) + + timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) + defer resetGC() + tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + assert.Equal(t, model.ActionFlashbackCluster, job.Type) + if job.SchemaState == model.StateWriteReorganization { + closeValue, err := infosync.GetPDScheduleConfig(context.Background()) + assert.NoError(t, err) + assert.Equal(t, closeValue["merge-schedule-limit"], 0) + // cancel flashback job + job.State = model.JobStateCancelled + job.Error = dbterror.ErrCancelledDDLJob + } + } + dom.DDL().SetHook(hook) + + time.Sleep(10 * time.Millisecond) + ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) + require.NoError(t, err) + + tk.MustGetErrCode(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts)), errno.ErrCancelledDDLJob) + dom.DDL().SetHook(originHook) + + finishValue, err := infosync.GetPDScheduleConfig(context.Background()) + require.NoError(t, err) + require.EqualValues(t, finishValue["merge-schedule-limit"], 1) + + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) +} + +func TestAddDDLDuringFlashback(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + originHook := dom.DDL().GetHook() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + + time.Sleep(10 * time.Millisecond) + ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) + require.NoError(t, err) + + injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", + fmt.Sprintf("return(%v)", injectSafeTS))) + + timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) + defer resetGC() + tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + assert.Equal(t, model.ActionFlashbackCluster, job.Type) + if job.SchemaState == model.StateWriteOnly { + tk1 := testkit.NewTestKit(t, store) + _, err := tk1.Exec("alter table test.t add column b int") + assert.ErrorContains(t, err, "Can't add ddl job, have flashback cluster job") + } + } + dom.DDL().SetHook(hook) + tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) + + dom.DDL().SetHook(originHook) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) +} + +func TestGlobalVariablesOnFlashback(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + originHook := dom.DDL().GetHook() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + + time.Sleep(10 * time.Millisecond) + ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) + require.NoError(t, err) + + injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", + fmt.Sprintf("return(%v)", injectSafeTS))) + + timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) + defer resetGC() + tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + assert.Equal(t, model.ActionFlashbackCluster, job.Type) + if job.SchemaState == model.StateWriteReorganization { + rs, err := tk.Exec("show variables like 'tidb_gc_enable'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + rs, err = tk.Exec("show variables like 'tidb_enable_auto_analyze'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + rs, err = tk.Exec("show variables like 'tidb_super_read_only'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) + rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + } + } + dom.DDL().SetHook(hook) + // first try with `tidb_gc_enable` = on and `tidb_super_read_only` = off and `tidb_ttl_job_enable` = on + tk.MustExec("set global tidb_gc_enable = on") + tk.MustExec("set global tidb_super_read_only = off") + tk.MustExec("set global tidb_ttl_job_enable = on") + + tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) + + rs, err := tk.Exec("show variables like 'tidb_super_read_only'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + rs, err = tk.Exec("show variables like 'tidb_gc_enable'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) + rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + + // second try with `tidb_gc_enable` = off and `tidb_super_read_only` = on and `tidb_ttl_job_enable` = off + tk.MustExec("set global tidb_gc_enable = off") + tk.MustExec("set global tidb_super_read_only = on") + tk.MustExec("set global tidb_ttl_job_enable = off") + + ts, err = tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) + require.NoError(t, err) + tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) + rs, err = tk.Exec("show variables like 'tidb_super_read_only'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) + rs, err = tk.Exec("show variables like 'tidb_gc_enable'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + + dom.DDL().SetHook(originHook) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) +} + +func TestCancelFlashbackCluster(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + originHook := dom.DDL().GetHook() + tk := testkit.NewTestKit(t, store) + + time.Sleep(10 * time.Millisecond) + ts, err := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) + require.NoError(t, err) + + injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", + fmt.Sprintf("return(%v)", injectSafeTS))) + + timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) + defer resetGC() + tk.MustExec(fmt.Sprintf(safePointSQL, timeBeforeDrop)) + + // Try canceled on StateDeleteOnly, cancel success + hook := newCancelJobHook(t, store, dom, func(job *model.Job) bool { + return job.SchemaState == model.StateDeleteOnly + }) + dom.DDL().SetHook(hook) + tk.MustExec("set global tidb_ttl_job_enable = on") + tk.MustGetErrCode(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts)), errno.ErrCancelledDDLJob) + hook.MustCancelDone(t) + + rs, err := tk.Exec("show variables like 'tidb_ttl_job_enable'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.On) + + // Try canceled on StateWriteReorganization, cancel failed + hook = newCancelJobHook(t, store, dom, func(job *model.Job) bool { + return job.SchemaState == model.StateWriteReorganization + }) + dom.DDL().SetHook(hook) + tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) + hook.MustCancelFailed(t) + + rs, err = tk.Exec("show variables like 'tidb_ttl_job_enable'") + assert.NoError(t, err) + assert.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.Off) + + dom.DDL().SetHook(originHook) + + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) +} diff --git a/pkg/ddl/column.go b/pkg/ddl/column.go new file mode 100644 index 0000000000000..826a774915fc8 --- /dev/null +++ b/pkg/ddl/column.go @@ -0,0 +1,2029 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/bits" + "sort" + "strings" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/sqlexec" + kvutil "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +// InitAndAddColumnToTable initializes the ColumnInfo in-place and adds it to the table. +func InitAndAddColumnToTable(tblInfo *model.TableInfo, colInfo *model.ColumnInfo) *model.ColumnInfo { + cols := tblInfo.Columns + colInfo.ID = AllocateColumnID(tblInfo) + colInfo.State = model.StateNone + // To support add column asynchronous, we should mark its offset as the last column. + // So that we can use origin column offset to get value from row. + colInfo.Offset = len(cols) + // Append the column info to the end of the tblInfo.Columns. + // It will reorder to the right offset in "Columns" when it state change to public. + tblInfo.Columns = append(cols, colInfo) + return colInfo +} + +func checkAddColumn(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ColumnInfo, *model.ColumnInfo, + *ast.ColumnPosition, bool /* ifNotExists */, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, nil, false, errors.Trace(err) + } + col := &model.ColumnInfo{} + pos := &ast.ColumnPosition{} + offset := 0 + ifNotExists := false + err = job.DecodeArgs(col, pos, &offset, &ifNotExists) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, nil, false, errors.Trace(err) + } + + columnInfo := model.FindColumnInfo(tblInfo.Columns, col.Name.L) + if columnInfo != nil { + if columnInfo.State == model.StatePublic { + // We already have a column with the same column name. + job.State = model.JobStateCancelled + return nil, nil, nil, nil, ifNotExists, infoschema.ErrColumnExists.GenWithStackByArgs(col.Name) + } + } + + err = CheckAfterPositionExists(tblInfo, pos) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, nil, false, infoschema.ErrColumnExists.GenWithStackByArgs(col.Name) + } + + return tblInfo, columnInfo, col, pos, false, nil +} + +func onAddColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + // Handle the rolling back job. + if job.IsRollingback() { + ver, err = onDropColumn(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + return ver, nil + } + + failpoint.Inject("errorBeforeDecodeArgs", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + failpoint.Return(ver, errors.New("occur an error before decode args")) + } + }) + + tblInfo, columnInfo, colFromArgs, pos, ifNotExists, err := checkAddColumn(t, job) + if err != nil { + if ifNotExists && infoschema.ErrColumnExists.Equal(err) { + job.Warning = toTError(err) + job.State = model.JobStateDone + return ver, nil + } + return ver, errors.Trace(err) + } + if columnInfo == nil { + columnInfo = InitAndAddColumnToTable(tblInfo, colFromArgs) + logutil.BgLogger().Info("run add column job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Reflect("columnInfo", *columnInfo)) + if err = checkAddColumnTooManyColumns(len(tblInfo.Columns)); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + + originalState := columnInfo.State + switch columnInfo.State { + case model.StateNone: + // none -> delete only + columnInfo.State = model.StateDeleteOnly + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != columnInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateDeleteOnly + case model.StateDeleteOnly: + // delete only -> write only + columnInfo.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + // Update the job state when all affairs done. + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + // write only -> reorganization + columnInfo.State = model.StateWriteReorganization + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + // Update the job state when all affairs done. + job.SchemaState = model.StateWriteReorganization + job.MarkNonRevertible() + case model.StateWriteReorganization: + // reorganization -> public + // Adjust table column offset. + offset, err := LocateOffsetToMove(columnInfo.Offset, pos, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + tblInfo.MoveColumnInfo(columnInfo.Offset, offset) + columnInfo.State = model.StatePublic + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != columnInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + asyncNotifyEvent(d, &ddlutil.Event{Tp: model.ActionAddColumn, TableInfo: tblInfo, ColumnInfos: []*model.ColumnInfo{columnInfo}}) + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("column", columnInfo.State) + } + + return ver, errors.Trace(err) +} + +// CheckAfterPositionExists makes sure the column specified in AFTER clause is exists. +// For example, ALTER TABLE t ADD COLUMN c3 INT AFTER c1. +func CheckAfterPositionExists(tblInfo *model.TableInfo, pos *ast.ColumnPosition) error { + if pos != nil && pos.Tp == ast.ColumnPositionAfter { + c := model.FindColumnInfo(tblInfo.Columns, pos.RelativeColumn.Name.L) + if c == nil { + return infoschema.ErrColumnNotExists.GenWithStackByArgs(pos.RelativeColumn, tblInfo.Name) + } + } + return nil +} + +func setIndicesState(indexInfos []*model.IndexInfo, state model.SchemaState) { + for _, indexInfo := range indexInfos { + indexInfo.State = state + } +} + +func checkDropColumnForStatePublic(colInfo *model.ColumnInfo) (err error) { + // When the dropping column has not-null flag and it hasn't the default value, we can backfill the column value like "add column". + // NOTE: If the state of StateWriteOnly can be rollbacked, we'd better reconsider the original default value. + // And we need consider the column without not-null flag. + if colInfo.GetOriginDefaultValue() == nil && mysql.HasNotNullFlag(colInfo.GetFlag()) { + // If the column is timestamp default current_timestamp, and DDL owner is new version TiDB that set column.Version to 1, + // then old TiDB update record in the column write only stage will uses the wrong default value of the dropping column. + // Because new version of the column default value is UTC time, but old version TiDB will think the default value is the time in system timezone. + // But currently will be ok, because we can't cancel the drop column job when the job is running, + // so the column will be dropped succeed and client will never see the wrong default value of the dropped column. + // More info about this problem, see PR#9115. + originDefVal, err := generateOriginDefaultValue(colInfo, nil) + if err != nil { + return err + } + return colInfo.SetOriginDefaultValue(originDefVal) + } + return nil +} + +func onDropColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, colInfo, idxInfos, ifExists, err := checkDropColumn(d, t, job) + if err != nil { + if ifExists && dbterror.ErrCantDropFieldOrKey.Equal(err) { + // Convert the "not exists" error to a warning. + job.Warning = toTError(err) + job.State = model.JobStateDone + return ver, nil + } + return ver, errors.Trace(err) + } + if job.MultiSchemaInfo != nil && !job.IsRollingback() && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + job.SchemaState = colInfo.State + // Store the mark and enter the next DDL handling loop. + return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) + } + + originalState := colInfo.State + switch colInfo.State { + case model.StatePublic: + // public -> write only + colInfo.State = model.StateWriteOnly + setIndicesState(idxInfos, model.StateWriteOnly) + tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) + err = checkDropColumnForStatePublic(colInfo) + if err != nil { + return ver, errors.Trace(err) + } + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != colInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateWriteOnly: + // write only -> delete only + colInfo.State = model.StateDeleteOnly + tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) + if len(idxInfos) > 0 { + newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) + for _, idx := range tblInfo.Indices { + if !indexInfoContains(idx.ID, idxInfos) { + newIndices = append(newIndices, idx) + } + } + tblInfo.Indices = newIndices + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + job.Args = append(job.Args, indexInfosToIDList(idxInfos)) + case model.StateDeleteOnly: + // delete only -> reorganization + colInfo.State = model.StateDeleteReorganization + tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateDeleteReorganization: + // reorganization -> absent + // All reorganization jobs are done, drop this column. + tblInfo.MoveColumnInfo(colInfo.Offset, len(tblInfo.Columns)-1) + tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-1] + colInfo.State = model.StateNone + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != colInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + if job.IsRollingback() { + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + } else { + // We should set related index IDs for job + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + job.Args = append(job.Args, getPartitionIDs(tblInfo)) + } + default: + return ver, errors.Trace(dbterror.ErrInvalidDDLJob.GenWithStackByArgs("table", tblInfo.State)) + } + job.SchemaState = colInfo.State + return ver, errors.Trace(err) +} + +func checkDropColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ColumnInfo, []*model.IndexInfo, bool /* ifExists */, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, false, errors.Trace(err) + } + + var colName model.CIStr + var ifExists bool + // indexIds is used to make sure we don't truncate args when decoding the rawArgs. + var indexIds []int64 + err = job.DecodeArgs(&colName, &ifExists, &indexIds) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, false, errors.Trace(err) + } + + colInfo := model.FindColumnInfo(tblInfo.Columns, colName.L) + if colInfo == nil || colInfo.Hidden { + job.State = model.JobStateCancelled + return nil, nil, nil, ifExists, dbterror.ErrCantDropFieldOrKey.GenWithStack("column %s doesn't exist", colName) + } + if err = isDroppableColumn(tblInfo, colName); err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, false, errors.Trace(err) + } + if err = checkDropColumnWithForeignKeyConstraintInOwner(d, t, job, tblInfo, colName.L); err != nil { + return nil, nil, nil, false, errors.Trace(err) + } + if err = checkDropColumnWithTTLConfig(tblInfo, colName.L); err != nil { + return nil, nil, nil, false, errors.Trace(err) + } + idxInfos := listIndicesWithColumn(colName.L, tblInfo.Indices) + return tblInfo, colInfo, idxInfos, false, nil +} + +func onSetDefaultValue(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + newCol := &model.ColumnInfo{} + err := job.DecodeArgs(newCol) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + return updateColumnDefaultValue(d, t, job, newCol, &newCol.Name) +} + +func needChangeColumnData(oldCol, newCol *model.ColumnInfo) bool { + toUnsigned := mysql.HasUnsignedFlag(newCol.GetFlag()) + originUnsigned := mysql.HasUnsignedFlag(oldCol.GetFlag()) + needTruncationOrToggleSign := func() bool { + return (newCol.GetFlen() > 0 && (newCol.GetFlen() < oldCol.GetFlen() || newCol.GetDecimal() < oldCol.GetDecimal())) || + (toUnsigned != originUnsigned) + } + // Ignore the potential max display length represented by integer's flen, use default flen instead. + defaultOldColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(oldCol.GetType()) + defaultNewColFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(newCol.GetType()) + needTruncationOrToggleSignForInteger := func() bool { + return (defaultNewColFlen > 0 && defaultNewColFlen < defaultOldColFlen) || (toUnsigned != originUnsigned) + } + + // Deal with the same type. + if oldCol.GetType() == newCol.GetType() { + switch oldCol.GetType() { + case mysql.TypeNewDecimal: + // Since type decimal will encode the precision, frac, negative(signed) and wordBuf into storage together, there is no short + // cut to eliminate data reorg change for column type change between decimal. + return oldCol.GetFlen() != newCol.GetFlen() || oldCol.GetDecimal() != newCol.GetDecimal() || toUnsigned != originUnsigned + case mysql.TypeEnum, mysql.TypeSet: + return IsElemsChangedToModifyColumn(oldCol.GetElems(), newCol.GetElems()) + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + return toUnsigned != originUnsigned + case mysql.TypeString: + // Due to the behavior of padding \x00 at binary type, always change column data when binary length changed + if types.IsBinaryStr(&oldCol.FieldType) { + return newCol.GetFlen() != oldCol.GetFlen() + } + } + + return needTruncationOrToggleSign() + } + + if ConvertBetweenCharAndVarchar(oldCol.GetType(), newCol.GetType()) { + return true + } + + // Deal with the different type. + switch oldCol.GetType() { + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + switch newCol.GetType() { + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + return needTruncationOrToggleSign() + } + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + switch newCol.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + return needTruncationOrToggleSignForInteger() + } + // conversion between float and double needs reorganization, see issue #31372 + } + + return true +} + +// ConvertBetweenCharAndVarchar check whether column converted between char and varchar +// TODO: it is used for plugins. so change plugin's using and remove it. +func ConvertBetweenCharAndVarchar(oldCol, newCol byte) bool { + return types.ConvertBetweenCharAndVarchar(oldCol, newCol) +} + +// IsElemsChangedToModifyColumn check elems changed +func IsElemsChangedToModifyColumn(oldElems, newElems []string) bool { + if len(newElems) < len(oldElems) { + return true + } + for index, oldElem := range oldElems { + newElem := newElems[index] + if oldElem != newElem { + return true + } + } + return false +} + +type modifyingColInfo struct { + newCol *model.ColumnInfo + oldColName *model.CIStr + modifyColumnTp byte + updatedAutoRandomBits uint64 + changingCol *model.ColumnInfo + changingIdxs []*model.IndexInfo + pos *ast.ColumnPosition + removedIdxs []int64 +} + +func getModifyColumnInfo(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ColumnInfo, *modifyingColInfo, error) { + modifyInfo := &modifyingColInfo{pos: &ast.ColumnPosition{}} + err := job.DecodeArgs(&modifyInfo.newCol, &modifyInfo.oldColName, modifyInfo.pos, &modifyInfo.modifyColumnTp, + &modifyInfo.updatedAutoRandomBits, &modifyInfo.changingCol, &modifyInfo.changingIdxs, &modifyInfo.removedIdxs) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, modifyInfo, errors.Trace(err) + } + + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return nil, nil, nil, modifyInfo, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return nil, nil, nil, modifyInfo, errors.Trace(err) + } + + oldCol := model.FindColumnInfo(tblInfo.Columns, modifyInfo.oldColName.L) + if oldCol == nil || oldCol.State != model.StatePublic { + job.State = model.JobStateCancelled + return nil, nil, nil, modifyInfo, errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(*(modifyInfo.oldColName), tblInfo.Name)) + } + + return dbInfo, tblInfo, oldCol, modifyInfo, errors.Trace(err) +} + +// GetOriginDefaultValueForModifyColumn gets the original default value for modifying column. +// Since column type change is implemented as adding a new column then substituting the old one. +// Case exists when update-where statement fetch a NULL for not-null column without any default data, +// it will errors. +// So we set original default value here to prevent this error. If the oldCol has the original default value, we use it. +// Otherwise we set the zero value as original default value. +// Besides, in insert & update records, we have already implement using the casted value of relative column to insert +// rather than the original default value. +func GetOriginDefaultValueForModifyColumn(sessCtx sessionctx.Context, changingCol, oldCol *model.ColumnInfo) (interface{}, error) { + var err error + originDefVal := oldCol.GetOriginDefaultValue() + if originDefVal != nil { + odv, err := table.CastValue(sessCtx, types.NewDatum(originDefVal), changingCol, false, false) + if err != nil { + logutil.BgLogger().Info("cast origin default value failed", zap.String("category", "ddl"), zap.Error(err)) + } + if !odv.IsNull() { + if originDefVal, err = odv.ToString(); err != nil { + originDefVal = nil + logutil.BgLogger().Info("convert default value to string failed", zap.String("category", "ddl"), zap.Error(err)) + } + } + } + if originDefVal == nil { + originDefVal, err = generateOriginDefaultValue(changingCol, nil) + if err != nil { + return nil, errors.Trace(err) + } + } + return originDefVal, nil +} + +func (w *worker) onModifyColumn(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + dbInfo, tblInfo, oldCol, modifyInfo, err := getModifyColumnInfo(t, job) + if err != nil { + return ver, err + } + + if job.IsRollingback() { + // For those column-type-change jobs which don't reorg the data. + if !needChangeColumnData(oldCol, modifyInfo.newCol) { + return rollbackModifyColumnJob(d, t, tblInfo, job, modifyInfo.newCol, oldCol, modifyInfo.modifyColumnTp) + } + // For those column-type-change jobs which reorg the data. + return rollbackModifyColumnJobWithData(d, t, tblInfo, job, oldCol, modifyInfo) + } + + // If we want to rename the column name, we need to check whether it already exists. + if modifyInfo.newCol.Name.L != modifyInfo.oldColName.L { + c := model.FindColumnInfo(tblInfo.Columns, modifyInfo.newCol.Name.L) + if c != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(infoschema.ErrColumnExists.GenWithStackByArgs(modifyInfo.newCol.Name)) + } + } + + failpoint.Inject("uninitializedOffsetAndState", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + if modifyInfo.newCol.State != model.StatePublic { + failpoint.Return(ver, errors.New("the column state is wrong")) + } + } + }) + + err = checkAndApplyAutoRandomBits(d, t, dbInfo, tblInfo, oldCol, modifyInfo.newCol, modifyInfo.updatedAutoRandomBits) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if !needChangeColumnData(oldCol, modifyInfo.newCol) { + return w.doModifyColumn(d, t, job, dbInfo, tblInfo, modifyInfo.newCol, oldCol, modifyInfo.pos) + } + + if err = isGeneratedRelatedColumn(tblInfo, modifyInfo.newCol, oldCol); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + if tblInfo.Partition != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs("table is partition table")) + } + + changingCol := modifyInfo.changingCol + if changingCol == nil { + newColName := model.NewCIStr(genChangingColumnUniqueName(tblInfo, oldCol)) + if mysql.HasPriKeyFlag(oldCol.GetFlag()) { + job.State = model.JobStateCancelled + msg := "this column has primary key flag" + return ver, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) + } + + changingCol = modifyInfo.newCol.Clone() + changingCol.Name = newColName + changingCol.ChangeStateInfo = &model.ChangeStateInfo{DependencyColumnOffset: oldCol.Offset} + + originDefVal, err := GetOriginDefaultValueForModifyColumn(newContext(d.store), changingCol, oldCol) + if err != nil { + return ver, errors.Trace(err) + } + if err = changingCol.SetOriginDefaultValue(originDefVal); err != nil { + return ver, errors.Trace(err) + } + + InitAndAddColumnToTable(tblInfo, changingCol) + indexesToChange := FindRelatedIndexesToChange(tblInfo, oldCol.Name) + for _, info := range indexesToChange { + newIdxID := AllocateIndexID(tblInfo) + if !info.isTemp { + // We create a temp index for each normal index. + tmpIdx := info.IndexInfo.Clone() + tmpIdxName := genChangingIndexUniqueName(tblInfo, info.IndexInfo) + setIdxIDName(tmpIdx, newIdxID, model.NewCIStr(tmpIdxName)) + SetIdxColNameOffset(tmpIdx.Columns[info.Offset], changingCol) + tblInfo.Indices = append(tblInfo.Indices, tmpIdx) + } else { + // The index is a temp index created by previous modify column job(s). + // We can overwrite it to reduce reorg cost, because it will be dropped eventually. + tmpIdx := info.IndexInfo + oldTempIdxID := tmpIdx.ID + setIdxIDName(tmpIdx, newIdxID, tmpIdx.Name /* unchanged */) + SetIdxColNameOffset(tmpIdx.Columns[info.Offset], changingCol) + modifyInfo.removedIdxs = append(modifyInfo.removedIdxs, oldTempIdxID) + } + } + } else { + changingCol = model.FindColumnInfoByID(tblInfo.Columns, modifyInfo.changingCol.ID) + if changingCol == nil { + logutil.BgLogger().Error("the changing column has been removed", zap.String("category", "ddl"), zap.Error(err)) + job.State = model.JobStateCancelled + return ver, errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) + } + } + + return w.doModifyColumnTypeWithData(d, t, job, dbInfo, tblInfo, changingCol, oldCol, modifyInfo.newCol.Name, modifyInfo.pos, modifyInfo.removedIdxs) +} + +func setIdxIDName(idxInfo *model.IndexInfo, newID int64, newName model.CIStr) { + idxInfo.ID = newID + idxInfo.Name = newName +} + +// SetIdxColNameOffset sets index column name and offset from changing ColumnInfo. +func SetIdxColNameOffset(idxCol *model.IndexColumn, changingCol *model.ColumnInfo) { + idxCol.Name = changingCol.Name + idxCol.Offset = changingCol.Offset + canPrefix := types.IsTypePrefixable(changingCol.GetType()) + if !canPrefix || (changingCol.GetFlen() < idxCol.Length) { + idxCol.Length = types.UnspecifiedLength + } +} + +// rollbackModifyColumnJobWithData is used to rollback modify-column job which need to reorg the data. +func rollbackModifyColumnJobWithData(d *ddlCtx, t *meta.Meta, tblInfo *model.TableInfo, job *model.Job, oldCol *model.ColumnInfo, modifyInfo *modifyingColInfo) (ver int64, err error) { + // If the not-null change is included, we should clean the flag info in oldCol. + if modifyInfo.modifyColumnTp == mysql.TypeNull { + // Reset NotNullFlag flag. + tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.NotNullFlag) + // Reset PreventNullInsertFlag flag. + tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.PreventNullInsertFlag) + } + var changingIdxIDs []int64 + if modifyInfo.changingCol != nil { + changingIdxIDs = buildRelatedIndexIDs(tblInfo, modifyInfo.changingCol.ID) + // The job is in the middle state. The appended changingCol and changingIndex should + // be removed from the tableInfo as well. + removeChangingColAndIdxs(tblInfo, modifyInfo.changingCol.ID) + } + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + // Reconstruct the job args to add the temporary index ids into delete range table. + job.Args = []interface{}{changingIdxIDs, getPartitionIDs(tblInfo)} + return ver, nil +} + +func removeChangingColAndIdxs(tblInfo *model.TableInfo, changingColID int64) { + restIdx := tblInfo.Indices[:0] + for _, idx := range tblInfo.Indices { + if !idx.HasColumnInIndexColumns(tblInfo, changingColID) { + restIdx = append(restIdx, idx) + } + } + tblInfo.Indices = restIdx + + restCols := tblInfo.Columns[:0] + for _, c := range tblInfo.Columns { + if c.ID != changingColID { + restCols = append(restCols, c) + } + } + tblInfo.Columns = restCols +} + +func (w *worker) doModifyColumnTypeWithData( + d *ddlCtx, t *meta.Meta, job *model.Job, + dbInfo *model.DBInfo, tblInfo *model.TableInfo, changingCol, oldCol *model.ColumnInfo, + colName model.CIStr, pos *ast.ColumnPosition, rmIdxIDs []int64) (ver int64, _ error) { + var err error + originalState := changingCol.State + targetCol := changingCol.Clone() + targetCol.Name = colName + changingIdxs := buildRelatedIndexInfos(tblInfo, changingCol.ID) + switch changingCol.State { + case model.StateNone: + // Column from null to not null. + if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(changingCol.GetFlag()) { + // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. + err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, targetCol, oldCol.GetType() != changingCol.GetType()) + if err != nil { + if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { + job.State = model.JobStateRollingback + } + return ver, err + } + } + // none -> delete only + updateChangingObjState(changingCol, changingIdxs, model.StateDeleteOnly) + failpoint.Inject("mockInsertValueAfterCheckNull", func(val failpoint.Value) { + if valStr, ok := val.(string); ok { + var sctx sessionctx.Context + sctx, err := w.sessPool.Get() + if err != nil { + failpoint.Return(ver, err) + } + defer w.sessPool.Put(sctx) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + //nolint:forcetypeassert + _, _, err = sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, valStr) + if err != nil { + job.State = model.JobStateCancelled + failpoint.Return(ver, err) + } + } + }) + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != changingCol.State) + if err != nil { + return ver, errors.Trace(err) + } + // Make sure job args change after `updateVersionAndTableInfoWithCheck`, otherwise, the job args will + // be updated in `updateDDLJob` even if it meets an error in `updateVersionAndTableInfoWithCheck`. + job.SchemaState = model.StateDeleteOnly + metrics.GetBackfillProgressByLabel(metrics.LblModifyColumn, job.SchemaName, tblInfo.Name.String()).Set(0) + job.Args = append(job.Args, changingCol, changingIdxs, rmIdxIDs) + case model.StateDeleteOnly: + // Column from null to not null. + if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(changingCol.GetFlag()) { + // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. + err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, targetCol, oldCol.GetType() != changingCol.GetType()) + if err != nil { + if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { + job.State = model.JobStateRollingback + } + return ver, err + } + } + // delete only -> write only + updateChangingObjState(changingCol, changingIdxs, model.StateWriteOnly) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + // write only -> reorganization + updateChangingObjState(changingCol, changingIdxs, model.StateWriteReorganization) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) + if err != nil { + return ver, errors.Trace(err) + } + // Initialize SnapshotVer to 0 for later reorganization check. + job.SnapshotVer = 0 + job.SchemaState = model.StateWriteReorganization + case model.StateWriteReorganization: + tbl, err := getTable(d.store, dbInfo.ID, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + + var done bool + if job.MultiSchemaInfo != nil { + done, ver, err = doReorgWorkForModifyColumnMultiSchema(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) + } else { + done, ver, err = doReorgWorkForModifyColumn(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) + } + if !done { + return ver, err + } + + rmIdxIDs = append(buildRelatedIndexIDs(tblInfo, oldCol.ID), rmIdxIDs...) + + err = adjustTableInfoAfterModifyColumnWithData(tblInfo, pos, oldCol, changingCol, colName, changingIdxs) + if err != nil { + job.State = model.JobStateRollingback + return ver, errors.Trace(err) + } + + updateChangingObjState(changingCol, changingIdxs, model.StatePublic) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != changingCol.State) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + // Refactor the job args to add the old index ids into delete range table. + job.Args = []interface{}{rmIdxIDs, getPartitionIDs(tblInfo)} + asyncNotifyEvent(d, &ddlutil.Event{Tp: model.ActionModifyColumn, TableInfo: tblInfo, ColumnInfos: []*model.ColumnInfo{changingCol}}) + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("column", changingCol.State) + } + + return ver, errors.Trace(err) +} + +func doReorgWorkForModifyColumnMultiSchema(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, + oldCol, changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) (done bool, ver int64, err error) { + if job.MultiSchemaInfo.Revertible { + done, ver, err = doReorgWorkForModifyColumn(w, d, t, job, tbl, oldCol, changingCol, changingIdxs) + if done { + // We need another round to wait for all the others sub-jobs to finish. + job.MarkNonRevertible() + } + // We need another round to run the reorg process. + return false, ver, err + } + // Non-revertible means all the sub jobs finished. + return true, ver, err +} + +func doReorgWorkForModifyColumn(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, + oldCol, changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) (done bool, ver int64, err error) { + job.ReorgMeta.ReorgTp = model.ReorgTypeTxn + sctx, err1 := w.sessPool.Get() + if err1 != nil { + err = errors.Trace(err1) + return + } + defer w.sessPool.Put(sctx) + rh := newReorgHandler(sess.NewSession(sctx)) + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return false, ver, errors.Trace(err) + } + reorgInfo, err := getReorgInfo(d.jobContext(job.ID, job.ReorgMeta), + d, rh, job, dbInfo, tbl, BuildElements(changingCol, changingIdxs), false) + if err != nil || reorgInfo == nil || reorgInfo.first { + // If we run reorg firstly, we should update the job snapshot version + // and then run the reorg next time. + return false, ver, errors.Trace(err) + } + + // Inject a failpoint so that we can pause here and do verification on other components. + // With a failpoint-enabled version of TiDB, you can trigger this failpoint by the following command: + // enable: curl -X PUT -d "pause" "http://127.0.0.1:10080/fail/github.com/pingcap/tidb/pkg/ddl/mockDelayInModifyColumnTypeWithData". + // disable: curl -X DELETE "http://127.0.0.1:10080/fail/github.com/pingcap/tidb/pkg/ddl/mockDelayInModifyColumnTypeWithData" + failpoint.Inject("mockDelayInModifyColumnTypeWithData", func() {}) + err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (addIndexErr error) { + defer util.Recover(metrics.LabelDDL, "onModifyColumn", + func() { + addIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("modify table `%v` column `%v` panic", tbl.Meta().Name, oldCol.Name) + }, false) + // Use old column name to generate less confusing error messages. + changingColCpy := changingCol.Clone() + changingColCpy.Name = oldCol.Name + return w.updateCurrentElement(tbl, reorgInfo) + }) + if err != nil { + if dbterror.ErrPausedDDLJob.Equal(err) { + return false, ver, nil + } + + if dbterror.ErrWaitReorgTimeout.Equal(err) { + // If timeout, we should return, check for the owner and re-wait job done. + return false, ver, nil + } + if kv.IsTxnRetryableError(err) || dbterror.ErrNotOwner.Equal(err) { + return false, ver, errors.Trace(err) + } + if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { + logutil.BgLogger().Warn("run modify column job failed, RemoveDDLReorgHandle failed, can't convert job to rollback", zap.String("category", "ddl"), + zap.String("job", job.String()), zap.Error(err1)) + } + logutil.BgLogger().Warn("run modify column job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) + job.State = model.JobStateRollingback + return false, ver, errors.Trace(err) + } + return true, ver, nil +} + +func adjustTableInfoAfterModifyColumnWithData(tblInfo *model.TableInfo, pos *ast.ColumnPosition, + oldCol, changingCol *model.ColumnInfo, newName model.CIStr, changingIdxs []*model.IndexInfo) (err error) { + if pos != nil && pos.RelativeColumn != nil && oldCol.Name.L == pos.RelativeColumn.Name.L { + // For cases like `modify column b after b`, it should report this error. + return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) + } + internalColName := changingCol.Name + changingCol = replaceOldColumn(tblInfo, oldCol, changingCol, newName) + if len(changingIdxs) > 0 { + updateNewIdxColsNameOffset(changingIdxs, internalColName, changingCol) + indexesToRemove := filterIndexesToRemove(changingIdxs, newName, tblInfo) + replaceOldIndexes(tblInfo, indexesToRemove) + } + if tblInfo.TTLInfo != nil { + updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, changingCol.Name) + } + // Move the new column to a correct offset. + destOffset, err := LocateOffsetToMove(changingCol.Offset, pos, tblInfo) + if err != nil { + return errors.Trace(err) + } + tblInfo.MoveColumnInfo(changingCol.Offset, destOffset) + return nil +} + +func replaceOldColumn(tblInfo *model.TableInfo, oldCol, changingCol *model.ColumnInfo, + newName model.CIStr) *model.ColumnInfo { + tblInfo.MoveColumnInfo(changingCol.Offset, len(tblInfo.Columns)-1) + changingCol = updateChangingCol(changingCol, newName, oldCol.Offset) + tblInfo.Columns[oldCol.Offset] = changingCol + tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-1] + return changingCol +} + +func replaceOldIndexes(tblInfo *model.TableInfo, changingIdxs []*model.IndexInfo) { + // Remove the changing indexes. + for i, idx := range tblInfo.Indices { + for _, cIdx := range changingIdxs { + if cIdx.ID == idx.ID { + tblInfo.Indices[i] = nil + break + } + } + } + tmp := tblInfo.Indices[:0] + for _, idx := range tblInfo.Indices { + if idx != nil { + tmp = append(tmp, idx) + } + } + tblInfo.Indices = tmp + // Replace the old indexes with changing indexes. + for _, cIdx := range changingIdxs { + // The index name should be changed from '_Idx$_name' to 'name'. + idxName := getChangingIndexOriginName(cIdx) + for i, idx := range tblInfo.Indices { + if strings.EqualFold(idxName, idx.Name.O) { + cIdx.Name = model.NewCIStr(idxName) + tblInfo.Indices[i] = cIdx + break + } + } + } +} + +// updateNewIdxColsNameOffset updates the name&offset of the index column. +func updateNewIdxColsNameOffset(changingIdxs []*model.IndexInfo, + oldName model.CIStr, changingCol *model.ColumnInfo) { + for _, idx := range changingIdxs { + for _, col := range idx.Columns { + if col.Name.L == oldName.L { + SetIdxColNameOffset(col, changingCol) + } + } + } +} + +func updateFKInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model.CIStr) { + if oldCol.L == newCol.L { + return + } + for _, fk := range tblInfo.ForeignKeys { + for i := range fk.Cols { + if fk.Cols[i].L == oldCol.L { + fk.Cols[i] = newCol + } + } + } +} + +func updateTTLInfoWhenModifyColumn(tblInfo *model.TableInfo, oldCol, newCol model.CIStr) { + if oldCol.L == newCol.L { + return + } + if tblInfo.TTLInfo != nil { + if tblInfo.TTLInfo.ColumnName.L == oldCol.L { + tblInfo.TTLInfo.ColumnName = newCol + } + } +} + +// filterIndexesToRemove filters out the indexes that can be removed. +func filterIndexesToRemove(changingIdxs []*model.IndexInfo, colName model.CIStr, tblInfo *model.TableInfo) []*model.IndexInfo { + indexesToRemove := make([]*model.IndexInfo, 0, len(changingIdxs)) + for _, idx := range changingIdxs { + var hasOtherChangingCol bool + for _, col := range idx.Columns { + if col.Name.L == colName.L { + continue // ignore the current modifying column. + } + if !hasOtherChangingCol { + hasOtherChangingCol = tblInfo.Columns[col.Offset].ChangeStateInfo != nil + } + } + // For the indexes that still contains other changing column, skip removing it now. + // We leave the removal work to the last modify column job. + if !hasOtherChangingCol { + indexesToRemove = append(indexesToRemove, idx) + } + } + return indexesToRemove +} + +func updateChangingCol(col *model.ColumnInfo, newName model.CIStr, newOffset int) *model.ColumnInfo { + col.Name = newName + col.ChangeStateInfo = nil + col.Offset = newOffset + // After changing the column, the column's type is change, so it needs to set OriginDefaultValue back + // so that there is no error in getting the default value from OriginDefaultValue. + // Besides, nil data that was not backfilled in the "add column" is backfilled after the column is changed. + // So it can set OriginDefaultValue to nil. + col.OriginDefaultValue = nil + return col +} + +func buildRelatedIndexInfos(tblInfo *model.TableInfo, colID int64) []*model.IndexInfo { + var indexInfos []*model.IndexInfo + for _, idx := range tblInfo.Indices { + if idx.HasColumnInIndexColumns(tblInfo, colID) { + indexInfos = append(indexInfos, idx) + } + } + return indexInfos +} + +func buildRelatedIndexIDs(tblInfo *model.TableInfo, colID int64) []int64 { + var oldIdxIDs []int64 + for _, idx := range tblInfo.Indices { + if idx.HasColumnInIndexColumns(tblInfo, colID) { + oldIdxIDs = append(oldIdxIDs, idx.ID) + } + } + return oldIdxIDs +} + +// LocateOffsetToMove returns the offset of the column to move. +func LocateOffsetToMove(currentOffset int, pos *ast.ColumnPosition, tblInfo *model.TableInfo) (destOffset int, err error) { + if pos == nil { + return currentOffset, nil + } + // Get column offset. + switch pos.Tp { + case ast.ColumnPositionFirst: + return 0, nil + case ast.ColumnPositionAfter: + c := model.FindColumnInfo(tblInfo.Columns, pos.RelativeColumn.Name.L) + if c == nil || c.State != model.StatePublic { + return 0, infoschema.ErrColumnNotExists.GenWithStackByArgs(pos.RelativeColumn, tblInfo.Name) + } + if currentOffset <= c.Offset { + return c.Offset, nil + } + return c.Offset + 1, nil + case ast.ColumnPositionNone: + return currentOffset, nil + default: + return 0, errors.Errorf("unknown column position type") + } +} + +// BuildElements is exported for testing. +func BuildElements(changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo) []*meta.Element { + elements := make([]*meta.Element, 0, len(changingIdxs)+1) + elements = append(elements, &meta.Element{ID: changingCol.ID, TypeKey: meta.ColumnElementKey}) + for _, idx := range changingIdxs { + elements = append(elements, &meta.Element{ID: idx.ID, TypeKey: meta.IndexElementKey}) + } + return elements +} + +func (w *worker) updatePhysicalTableRow(t table.Table, reorgInfo *reorgInfo) error { + logutil.BgLogger().Info("start to update table row", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) + if tbl, ok := t.(table.PartitionedTable); ok { + done := false + for !done { + p := tbl.GetPartition(reorgInfo.PhysicalTableID) + if p == nil { + return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, t.Meta().ID) + } + workType := typeReorgPartitionWorker + switch reorgInfo.Job.Type { + case model.ActionReorganizePartition, + model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning: + // Expected + default: + // workType = typeUpdateColumnWorker + // TODO: Support Modify Column on partitioned table + // https://github.com/pingcap/tidb/issues/38297 + return dbterror.ErrCancelledDDLJob.GenWithStack("Modify Column on partitioned table / typeUpdateColumnWorker not yet supported.") + } + err := w.writePhysicalTableRecord(w.sessPool, p, workType, reorgInfo) + if err != nil { + return err + } + done, err = updateReorgInfo(w.sessPool, tbl, reorgInfo) + if err != nil { + return errors.Trace(err) + } + } + return nil + } + if tbl, ok := t.(table.PhysicalTable); ok { + return w.writePhysicalTableRecord(w.sessPool, tbl, typeUpdateColumnWorker, reorgInfo) + } + return dbterror.ErrCancelledDDLJob.GenWithStack("internal error for phys tbl id: %d tbl id: %d", reorgInfo.PhysicalTableID, t.Meta().ID) +} + +// TestReorgGoroutineRunning is only used in test to indicate the reorg goroutine has been started. +var TestReorgGoroutineRunning = make(chan interface{}) + +// updateCurrentElement update the current element for reorgInfo. +func (w *worker) updateCurrentElement(t table.Table, reorgInfo *reorgInfo) error { + failpoint.Inject("mockInfiniteReorgLogic", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + a := new(interface{}) + TestReorgGoroutineRunning <- a + for { + time.Sleep(30 * time.Millisecond) + if w.isReorgCancelled(reorgInfo.Job.ID) { + // Job is cancelled. So it can't be done. + failpoint.Return(dbterror.ErrCancelledDDLJob) + } + } + } + }) + // TODO: Support partition tables. + if bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { + //nolint:forcetypeassert + err := w.updatePhysicalTableRow(t.(table.PhysicalTable), reorgInfo) + if err != nil { + return errors.Trace(err) + } + } + + if _, ok := t.(table.PartitionedTable); ok { + // TODO: remove when modify column of partitioned table is supported + // https://github.com/pingcap/tidb/issues/38297 + return dbterror.ErrCancelledDDLJob.GenWithStack("Modify Column on partitioned table / typeUpdateColumnWorker not yet supported.") + } + // Get the original start handle and end handle. + currentVer, err := getValidCurrentVersion(reorgInfo.d.store) + if err != nil { + return errors.Trace(err) + } + //nolint:forcetypeassert + originalStartHandle, originalEndHandle, err := getTableRange(reorgInfo.NewJobContext(), reorgInfo.d, t.(table.PhysicalTable), currentVer.Ver, reorgInfo.Job.Priority) + if err != nil { + return errors.Trace(err) + } + + startElementOffset := 0 + startElementOffsetToResetHandle := -1 + // This backfill job starts with backfilling index data, whose index ID is currElement.ID. + if bytes.Equal(reorgInfo.currElement.TypeKey, meta.IndexElementKey) { + for i, element := range reorgInfo.elements[1:] { + if reorgInfo.currElement.ID == element.ID { + startElementOffset = i + startElementOffsetToResetHandle = i + break + } + } + } + + for i := startElementOffset; i < len(reorgInfo.elements[1:]); i++ { + // This backfill job has been exited during processing. At that time, the element is reorgInfo.elements[i+1] and handle range is [reorgInfo.StartHandle, reorgInfo.EndHandle]. + // Then the handle range of the rest elements' is [originalStartHandle, originalEndHandle]. + if i == startElementOffsetToResetHandle+1 { + reorgInfo.StartKey, reorgInfo.EndKey = originalStartHandle, originalEndHandle + } + + // Update the element in the reorgInfo for updating the reorg meta below. + reorgInfo.currElement = reorgInfo.elements[i+1] + // Write the reorg info to store so the whole reorganize process can recover from panic. + err := reorgInfo.UpdateReorgMeta(reorgInfo.StartKey, w.sessPool) + logutil.BgLogger().Info("update column and indexes", zap.String("category", "ddl"), + zap.Int64("job ID", reorgInfo.Job.ID), + zap.Stringer("element", reorgInfo.currElement), + zap.String("start key", hex.EncodeToString(reorgInfo.StartKey)), + zap.String("end key", hex.EncodeToString(reorgInfo.EndKey))) + if err != nil { + return errors.Trace(err) + } + err = w.addTableIndex(t, reorgInfo) + if err != nil { + return errors.Trace(err) + } + } + return nil +} + +type updateColumnWorker struct { + *backfillCtx + oldColInfo *model.ColumnInfo + newColInfo *model.ColumnInfo + + // The following attributes are used to reduce memory allocation. + rowRecords []*rowRecord + rowDecoder *decoder.RowDecoder + + rowMap map[int64]types.Datum + + checksumBuffer rowcodec.RowData + checksumNeeded bool +} + +func newUpdateColumnWorker(sessCtx sessionctx.Context, id int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) *updateColumnWorker { + if !bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { + logutil.BgLogger().Error("Element type for updateColumnWorker incorrect", zap.String("jobQuery", reorgInfo.Query), + zap.String("reorgInfo", reorgInfo.String())) + return nil + } + var oldCol, newCol *model.ColumnInfo + for _, col := range t.WritableCols() { + if col.ID == reorgInfo.currElement.ID { + newCol = col.ColumnInfo + oldCol = table.FindCol(t.Cols(), getChangingColumnOriginName(newCol)).ColumnInfo + break + } + } + rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) + checksumNeeded := false + failpoint.Inject("forceRowLevelChecksumOnUpdateColumnBackfill", func() { + orig := variable.EnableRowLevelChecksum.Load() + defer variable.EnableRowLevelChecksum.Store(orig) + variable.EnableRowLevelChecksum.Store(true) + }) + // We use global `EnableRowLevelChecksum` to detect whether checksum is enabled in ddl backfill worker because + // `SessionVars.IsRowLevelChecksumEnabled` will filter out internal sessions. + if variable.EnableRowLevelChecksum.Load() { + if numNonPubCols := len(t.DeletableCols()) - len(t.Cols()); numNonPubCols > 1 { + cols := make([]*model.ColumnInfo, len(t.DeletableCols())) + for i, col := range t.DeletableCols() { + cols[i] = col.ToInfo() + } + logutil.BgLogger().Warn("skip checksum in update-column backfill since the number of non-public columns is greater than 1", + zap.String("jobQuery", reorgInfo.Query), zap.String("reorgInfo", reorgInfo.String()), zap.Any("cols", cols)) + } else { + checksumNeeded = true + } + } + return &updateColumnWorker{ + backfillCtx: newBackfillCtx(reorgInfo.d, id, sessCtx, reorgInfo.SchemaName, t, jc, "update_col_rate", false), + oldColInfo: oldCol, + newColInfo: newCol, + rowDecoder: rowDecoder, + rowMap: make(map[int64]types.Datum, len(decodeColMap)), + checksumNeeded: checksumNeeded, + } +} + +func (w *updateColumnWorker) AddMetricInfo(cnt float64) { + w.metricCounter.Add(cnt) +} + +func (*updateColumnWorker) String() string { + return typeUpdateColumnWorker.String() +} + +func (w *updateColumnWorker) GetCtx() *backfillCtx { + return w.backfillCtx +} + +type rowRecord struct { + key []byte // It's used to lock a record. Record it to reduce the encoding time. + vals []byte // It's the record. + warning *terror.Error // It's used to record the cast warning of a record. +} + +// getNextHandleKey gets next handle of entry that we are going to process. +func getNextHandleKey(taskRange reorgBackfillTask, + taskDone bool, lastAccessedHandle kv.Key) (nextHandle kv.Key) { + if !taskDone { + // The task is not done. So we need to pick the last processed entry's handle and add one. + return lastAccessedHandle.Next() + } + + return taskRange.endKey.Next() +} + +func (w *updateColumnWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*rowRecord, kv.Key, bool, error) { + w.rowRecords = w.rowRecords[:0] + startTime := time.Now() + + // taskDone means that the added handle is out of taskRange.endHandle. + taskDone := false + var lastAccessedHandle kv.Key + oprStartTime := startTime + err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, taskRange.physicalTable.RecordPrefix(), + txn.StartTS(), taskRange.startKey, taskRange.endKey, func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { + oprEndTime := time.Now() + logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in updateColumnWorker fetchRowColVals", 0) + oprStartTime = oprEndTime + + taskDone = recordKey.Cmp(taskRange.endKey) >= 0 + + if taskDone || len(w.rowRecords) >= w.batchCnt { + return false, nil + } + + if err1 := w.getRowRecord(handle, recordKey, rawRow); err1 != nil { + return false, errors.Trace(err1) + } + lastAccessedHandle = recordKey + if recordKey.Cmp(taskRange.endKey) == 0 { + taskDone = true + return false, nil + } + return true, nil + }) + + if len(w.rowRecords) == 0 { + taskDone = true + } + + logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) + return w.rowRecords, getNextHandleKey(taskRange, taskDone, lastAccessedHandle), taskDone, errors.Trace(err) +} + +func (w *updateColumnWorker) getRowRecord(handle kv.Handle, recordKey []byte, rawRow []byte) error { + sysTZ := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() + _, err := w.rowDecoder.DecodeTheExistedColumnMap(w.sessCtx, handle, rawRow, sysTZ, w.rowMap) + if err != nil { + return errors.Trace(dbterror.ErrCantDecodeRecord.GenWithStackByArgs("column", err)) + } + + if _, ok := w.rowMap[w.newColInfo.ID]; ok { + // The column is already added by update or insert statement, skip it. + w.cleanRowMap() + return nil + } + + var recordWarning *terror.Error + // Since every updateColumnWorker handle their own work individually, we can cache warning in statement context when casting datum. + oldWarn := w.sessCtx.GetSessionVars().StmtCtx.GetWarnings() + if oldWarn == nil { + oldWarn = []stmtctx.SQLWarn{} + } else { + oldWarn = oldWarn[:0] + } + w.sessCtx.GetSessionVars().StmtCtx.SetWarnings(oldWarn) + val := w.rowMap[w.oldColInfo.ID] + col := w.newColInfo + if val.Kind() == types.KindNull && col.FieldType.GetType() == mysql.TypeTimestamp && mysql.HasNotNullFlag(col.GetFlag()) { + if v, err := expression.GetTimeCurrentTimestamp(w.sessCtx, col.GetType(), col.GetDecimal()); err == nil { + // convert null value to timestamp should be substituted with current timestamp if NOT_NULL flag is set. + w.rowMap[w.oldColInfo.ID] = v + } + } + newColVal, err := table.CastValue(w.sessCtx, w.rowMap[w.oldColInfo.ID], w.newColInfo, false, false) + if err != nil { + return w.reformatErrors(err) + } + warn := w.sessCtx.GetSessionVars().StmtCtx.GetWarnings() + if len(warn) != 0 { + //nolint:forcetypeassert + recordWarning = errors.Cause(w.reformatErrors(warn[0].Err)).(*terror.Error) + } + + failpoint.Inject("MockReorgTimeoutInOneRegion", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + if handle.IntValue() == 3000 && atomic.CompareAndSwapInt32(&TestCheckReorgTimeout, 0, 1) { + failpoint.Return(errors.Trace(dbterror.ErrWaitReorgTimeout)) + } + } + }) + + w.rowMap[w.newColInfo.ID] = newColVal + _, err = w.rowDecoder.EvalRemainedExprColumnMap(w.sessCtx, w.rowMap) + if err != nil { + return errors.Trace(err) + } + newColumnIDs := make([]int64, 0, len(w.rowMap)) + newRow := make([]types.Datum, 0, len(w.rowMap)) + for colID, val := range w.rowMap { + newColumnIDs = append(newColumnIDs, colID) + newRow = append(newRow, val) + } + checksums := w.calcChecksums() + sctx, rd := w.sessCtx.GetSessionVars().StmtCtx, &w.sessCtx.GetSessionVars().RowEncoder + newRowVal, err := tablecodec.EncodeRow(sctx, newRow, newColumnIDs, nil, nil, rd, checksums...) + if err != nil { + return errors.Trace(err) + } + + w.rowRecords = append(w.rowRecords, &rowRecord{key: recordKey, vals: newRowVal, warning: recordWarning}) + w.cleanRowMap() + return nil +} + +func (w *updateColumnWorker) calcChecksums() []uint32 { + if !w.checksumNeeded { + return nil + } + // when w.checksumNeeded is true, it indicates that there is only one write-reorg column (the new column) and other + // columns are public, thus we have to calculate two checksums that one of which only contains the old column and + // the other only contains the new column. + var checksums [2]uint32 + for i, id := range []int64{w.newColInfo.ID, w.oldColInfo.ID} { + if len(w.checksumBuffer.Cols) > 0 { + w.checksumBuffer.Cols = w.checksumBuffer.Cols[:0] + } + for _, col := range w.table.DeletableCols() { + if col.ID == id || (col.IsVirtualGenerated()) { + continue + } + d := w.rowMap[col.ID] + w.checksumBuffer.Cols = append(w.checksumBuffer.Cols, rowcodec.ColData{ColumnInfo: col.ToInfo(), Datum: &d}) + } + if !sort.IsSorted(w.checksumBuffer) { + sort.Sort(w.checksumBuffer) + } + checksum, err := w.checksumBuffer.Checksum() + if err != nil { + logutil.BgLogger().Warn("skip checksum in update-column backfill due to encode error", zap.Error(err)) + return nil + } + checksums[i] = checksum + } + return checksums[:] +} + +// reformatErrors casted error because `convertTo` function couldn't package column name and datum value for some errors. +func (w *updateColumnWorker) reformatErrors(err error) error { + // Since row count is not precious in concurrent reorganization, here we substitute row count with datum value. + if types.ErrTruncated.Equal(err) || types.ErrDataTooLong.Equal(err) { + dStr := datumToStringNoErr(w.rowMap[w.oldColInfo.ID]) + err = types.ErrTruncated.GenWithStack("Data truncated for column '%s', value is '%s'", w.oldColInfo.Name, dStr) + } + + if types.ErrWarnDataOutOfRange.Equal(err) { + dStr := datumToStringNoErr(w.rowMap[w.oldColInfo.ID]) + err = types.ErrWarnDataOutOfRange.GenWithStack("Out of range value for column '%s', the value is '%s'", w.oldColInfo.Name, dStr) + } + return err +} + +func datumToStringNoErr(d types.Datum) string { + if v, err := d.ToString(); err == nil { + return v + } + return fmt.Sprintf("%v", d.GetValue()) +} + +func (w *updateColumnWorker) cleanRowMap() { + for id := range w.rowMap { + delete(w.rowMap, id) + } +} + +// BackfillData will backfill the table record in a transaction. A lock corresponds to a rowKey if the value of rowKey is changed. +func (w *updateColumnWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { + oprStartTime := time.Now() + ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) + errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + taskCtx.addedCount = 0 + taskCtx.scanCount = 0 + + // Because TiCDC do not want this kind of change, + // so we set the lossy DDL reorg txn source to 1 to + // avoid TiCDC to replicate this kind of change. + var txnSource uint64 + if val := txn.GetOption(kv.TxnSource); val != nil { + txnSource, _ = val.(uint64) + } + err := kv.SetLossyDDLReorgSource(&txnSource, kv.LossyDDLColumnReorgSource) + if err != nil { + return errors.Trace(err) + } + txn.SetOption(kv.TxnSource, txnSource) + + txn.SetOption(kv.Priority, handleRange.priority) + if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { + txn.SetOption(kv.ResourceGroupTagger, tagger) + } + txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) + + rowRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) + if err != nil { + return errors.Trace(err) + } + taskCtx.nextKey = nextKey + taskCtx.done = taskDone + + // Optimize for few warnings! + warningsMap := make(map[errors.ErrorID]*terror.Error, 2) + warningsCountMap := make(map[errors.ErrorID]int64, 2) + for _, rowRecord := range rowRecords { + taskCtx.scanCount++ + + err = txn.Set(rowRecord.key, rowRecord.vals) + if err != nil { + return errors.Trace(err) + } + taskCtx.addedCount++ + if rowRecord.warning != nil { + if _, ok := warningsCountMap[rowRecord.warning.ID()]; ok { + warningsCountMap[rowRecord.warning.ID()]++ + } else { + warningsCountMap[rowRecord.warning.ID()] = 1 + warningsMap[rowRecord.warning.ID()] = rowRecord.warning + } + } + } + + // Collect the warnings. + taskCtx.warnings, taskCtx.warningsCount = warningsMap, warningsCountMap + + return nil + }) + logSlowOperations(time.Since(oprStartTime), "BackfillData", 3000) + + return +} + +func updateChangingObjState(changingCol *model.ColumnInfo, changingIdxs []*model.IndexInfo, schemaState model.SchemaState) { + changingCol.State = schemaState + for _, idx := range changingIdxs { + idx.State = schemaState + } +} + +// doModifyColumn updates the column information and reorders all columns. It does not support modifying column data. +func (w *worker) doModifyColumn( + d *ddlCtx, t *meta.Meta, job *model.Job, dbInfo *model.DBInfo, tblInfo *model.TableInfo, + newCol, oldCol *model.ColumnInfo, pos *ast.ColumnPosition) (ver int64, _ error) { + if oldCol.ID != newCol.ID { + job.State = model.JobStateRollingback + return ver, dbterror.ErrColumnInChange.GenWithStackByArgs(oldCol.Name, newCol.ID) + } + // Column from null to not null. + if !mysql.HasNotNullFlag(oldCol.GetFlag()) && mysql.HasNotNullFlag(newCol.GetFlag()) { + noPreventNullFlag := !mysql.HasPreventNullInsertFlag(oldCol.GetFlag()) + + // lease = 0 means it's in an integration test. In this case we don't delay so the test won't run too slowly. + // We need to check after the flag is set + if d.lease > 0 && !noPreventNullFlag { + delayForAsyncCommit() + } + + // Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. + err := modifyColsFromNull2NotNull(w, dbInfo, tblInfo, []*model.ColumnInfo{oldCol}, newCol, oldCol.GetType() != newCol.GetType()) + if err != nil { + if dbterror.ErrWarnDataTruncated.Equal(err) || dbterror.ErrInvalidUseOfNull.Equal(err) { + job.State = model.JobStateRollingback + } + return ver, err + } + // The column should get into prevent null status first. + if noPreventNullFlag { + return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) + } + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + // Store the mark and enter the next DDL handling loop. + return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) + } + + if err := adjustTableInfoAfterModifyColumn(tblInfo, newCol, oldCol, pos); err != nil { + job.State = model.JobStateRollingback + return ver, errors.Trace(err) + } + + childTableInfos, err := adjustForeignKeyChildTableInfoAfterModifyColumn(d, t, job, tblInfo, newCol, oldCol) + if err != nil { + return ver, errors.Trace(err) + } + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true, childTableInfos...) + if err != nil { + // Modified the type definition of 'null' to 'not null' before this, so rollBack the job when an error occurs. + job.State = model.JobStateRollingback + return ver, errors.Trace(err) + } + + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + // For those column-type-change type which doesn't need reorg data, we should also mock the job args for delete range. + job.Args = []interface{}{[]int64{}, []int64{}} + return ver, nil +} + +func adjustTableInfoAfterModifyColumn( + tblInfo *model.TableInfo, newCol, oldCol *model.ColumnInfo, pos *ast.ColumnPosition) error { + // We need the latest column's offset and state. This information can be obtained from the store. + newCol.Offset = oldCol.Offset + newCol.State = oldCol.State + if pos != nil && pos.RelativeColumn != nil && oldCol.Name.L == pos.RelativeColumn.Name.L { + // For cases like `modify column b after b`, it should report this error. + return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) + } + destOffset, err := LocateOffsetToMove(oldCol.Offset, pos, tblInfo) + if err != nil { + return errors.Trace(infoschema.ErrColumnNotExists.GenWithStackByArgs(oldCol.Name, tblInfo.Name)) + } + tblInfo.Columns[oldCol.Offset] = newCol + tblInfo.MoveColumnInfo(oldCol.Offset, destOffset) + updateNewIdxColsNameOffset(tblInfo.Indices, oldCol.Name, newCol) + updateFKInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name) + updateTTLInfoWhenModifyColumn(tblInfo, oldCol.Name, newCol.Name) + return nil +} + +func adjustForeignKeyChildTableInfoAfterModifyColumn(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, newCol, oldCol *model.ColumnInfo) ([]schemaIDAndTableInfo, error) { + if !variable.EnableForeignKey.Load() || newCol.Name.L == oldCol.Name.L { + return nil, nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return nil, err + } + referredFKs := is.GetTableReferredForeignKeys(job.SchemaName, tblInfo.Name.L) + if len(referredFKs) == 0 { + return nil, nil + } + fkh := newForeignKeyHelper() + fkh.addLoadedTable(job.SchemaName, tblInfo.Name.L, job.SchemaID, tblInfo) + for _, referredFK := range referredFKs { + info, err := fkh.getTableFromStorage(is, t, referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + if infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err) { + continue + } + return nil, err + } + fkInfo := model.FindFKInfoByName(info.tblInfo.ForeignKeys, referredFK.ChildFKName.L) + if fkInfo == nil { + continue + } + for i := range fkInfo.RefCols { + if fkInfo.RefCols[i].L == oldCol.Name.L { + fkInfo.RefCols[i] = newCol.Name + } + } + } + infoList := make([]schemaIDAndTableInfo, 0, len(fkh.loaded)) + for _, info := range fkh.loaded { + if info.tblInfo.ID == tblInfo.ID { + continue + } + infoList = append(infoList, info) + } + return infoList, nil +} + +func checkAndApplyAutoRandomBits(d *ddlCtx, m *meta.Meta, dbInfo *model.DBInfo, tblInfo *model.TableInfo, + oldCol *model.ColumnInfo, newCol *model.ColumnInfo, newAutoRandBits uint64) error { + if newAutoRandBits == 0 { + return nil + } + idAcc := m.GetAutoIDAccessors(dbInfo.ID, tblInfo.ID) + err := checkNewAutoRandomBits(idAcc, oldCol, newCol, newAutoRandBits, tblInfo.AutoRandomRangeBits, tblInfo.SepAutoInc()) + if err != nil { + return err + } + return applyNewAutoRandomBits(d, m, dbInfo, tblInfo, oldCol, newAutoRandBits) +} + +// checkNewAutoRandomBits checks whether the new auto_random bits number can cause overflow. +func checkNewAutoRandomBits(idAccessors meta.AutoIDAccessors, oldCol *model.ColumnInfo, + newCol *model.ColumnInfo, newShardBits, newRangeBits uint64, sepAutoInc bool) error { + shardFmt := autoid.NewShardIDFormat(&newCol.FieldType, newShardBits, newRangeBits) + + idAcc := idAccessors.RandomID() + convertedFromAutoInc := mysql.HasAutoIncrementFlag(oldCol.GetFlag()) + if convertedFromAutoInc { + if sepAutoInc { + idAcc = idAccessors.IncrementID(model.TableInfoVersion5) + } else { + idAcc = idAccessors.RowID() + } + } + // Generate a new auto ID first to prevent concurrent update in DML. + _, err := idAcc.Inc(1) + if err != nil { + return err + } + currentIncBitsVal, err := idAcc.Get() + if err != nil { + return err + } + // Find the max number of available shard bits by + // counting leading zeros in current inc part of auto_random ID. + usedBits := uint64(64 - bits.LeadingZeros64(uint64(currentIncBitsVal))) + if usedBits > shardFmt.IncrementalBits { + overflowCnt := usedBits - shardFmt.IncrementalBits + errMsg := fmt.Sprintf(autoid.AutoRandomOverflowErrMsg, newShardBits-overflowCnt, newShardBits, oldCol.Name.O) + return dbterror.ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) + } + return nil +} + +// applyNewAutoRandomBits set auto_random bits to TableInfo and +// migrate auto_increment ID to auto_random ID if possible. +func applyNewAutoRandomBits(d *ddlCtx, m *meta.Meta, dbInfo *model.DBInfo, + tblInfo *model.TableInfo, oldCol *model.ColumnInfo, newAutoRandBits uint64) error { + tblInfo.AutoRandomBits = newAutoRandBits + needMigrateFromAutoIncToAutoRand := mysql.HasAutoIncrementFlag(oldCol.GetFlag()) + if !needMigrateFromAutoIncToAutoRand { + return nil + } + autoRandAlloc := autoid.NewAllocatorsFromTblInfo(d.store, dbInfo.ID, tblInfo).Get(autoid.AutoRandomType) + if autoRandAlloc == nil { + errMsg := fmt.Sprintf(autoid.AutoRandomAllocatorNotFound, dbInfo.Name.O, tblInfo.Name.O) + return dbterror.ErrInvalidAutoRandom.GenWithStackByArgs(errMsg) + } + idAcc := m.GetAutoIDAccessors(dbInfo.ID, tblInfo.ID).RowID() + nextAutoIncID, err := idAcc.Get() + if err != nil { + return errors.Trace(err) + } + err = autoRandAlloc.Rebase(context.Background(), nextAutoIncID, false) + if err != nil { + return errors.Trace(err) + } + if err := idAcc.Del(); err != nil { + return errors.Trace(err) + } + return nil +} + +// checkForNullValue ensure there are no null values of the column of this table. +// `isDataTruncated` indicates whether the new field and the old field type are the same, in order to be compatible with mysql. +func checkForNullValue(ctx context.Context, sctx sessionctx.Context, isDataTruncated bool, schema, table model.CIStr, newCol *model.ColumnInfo, oldCols ...*model.ColumnInfo) error { + needCheckNullValue := false + for _, oldCol := range oldCols { + if oldCol.GetType() != mysql.TypeTimestamp && newCol.GetType() == mysql.TypeTimestamp { + // special case for convert null value of non-timestamp type to timestamp type, null value will be substituted with current timestamp. + continue + } + needCheckNullValue = true + } + if !needCheckNullValue { + return nil + } + var buf strings.Builder + buf.WriteString("select 1 from %n.%n where ") + paramsList := make([]interface{}, 0, 2+len(oldCols)) + paramsList = append(paramsList, schema.L, table.L) + for i, col := range oldCols { + if i == 0 { + buf.WriteString("%n is null") + paramsList = append(paramsList, col.Name.L) + } else { + buf.WriteString(" or %n is null") + paramsList = append(paramsList, col.Name.L) + } + } + buf.WriteString(" limit 1") + //nolint:forcetypeassert + rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, buf.String(), paramsList...) + if err != nil { + return errors.Trace(err) + } + rowCount := len(rows) + if rowCount != 0 { + if isDataTruncated { + return dbterror.ErrWarnDataTruncated.GenWithStackByArgs(newCol.Name.L, rowCount) + } + return dbterror.ErrInvalidUseOfNull + } + return nil +} + +func updateColumnDefaultValue(d *ddlCtx, t *meta.Meta, job *model.Job, newCol *model.ColumnInfo, oldColName *model.CIStr) (ver int64, _ error) { + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + // Store the mark and enter the next DDL handling loop. + return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) + } + + oldCol := model.FindColumnInfo(tblInfo.Columns, oldColName.L) + if oldCol == nil || oldCol.State != model.StatePublic { + job.State = model.JobStateCancelled + return ver, infoschema.ErrColumnNotExists.GenWithStackByArgs(newCol.Name, tblInfo.Name) + } + + if hasDefaultValue, _, err := checkColumnDefaultValue(newContext(d.store), table.ToColumn(oldCol.Clone()), newCol.DefaultValue); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } else if !hasDefaultValue { + job.State = model.JobStateCancelled + return ver, dbterror.ErrInvalidDefaultValue.GenWithStackByArgs(newCol.Name) + } + + // The newCol's offset may be the value of the old schema version, so we can't use newCol directly. + oldCol.DefaultValue = newCol.DefaultValue + oldCol.DefaultValueBit = newCol.DefaultValueBit + oldCol.DefaultIsExpr = newCol.DefaultIsExpr + if mysql.HasNoDefaultValueFlag(newCol.GetFlag()) { + oldCol.AddFlag(mysql.NoDefaultValueFlag) + } else { + oldCol.DelFlag(mysql.NoDefaultValueFlag) + sctx := newContext(d.store) + err = checkDefaultValue(sctx, table.ToColumn(oldCol), true) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func isColumnWithIndex(colName string, indices []*model.IndexInfo) bool { + for _, indexInfo := range indices { + for _, col := range indexInfo.Columns { + if col.Name.L == colName { + return true + } + } + } + return false +} + +func isColumnCanDropWithIndex(colName string, indices []*model.IndexInfo) error { + for _, indexInfo := range indices { + if indexInfo.Primary || len(indexInfo.Columns) > 1 { + for _, col := range indexInfo.Columns { + if col.Name.L == colName { + return dbterror.ErrCantDropColWithIndex.GenWithStack("can't drop column %s with composite index covered or Primary Key covered now", colName) + } + } + } + } + return nil +} + +func listIndicesWithColumn(colName string, indices []*model.IndexInfo) []*model.IndexInfo { + ret := make([]*model.IndexInfo, 0) + for _, indexInfo := range indices { + if len(indexInfo.Columns) == 1 && colName == indexInfo.Columns[0].Name.L { + ret = append(ret, indexInfo) + } + } + return ret +} + +// GetColumnForeignKeyInfo returns the wanted foreign key info +func GetColumnForeignKeyInfo(colName string, fkInfos []*model.FKInfo) *model.FKInfo { + for _, fkInfo := range fkInfos { + for _, col := range fkInfo.Cols { + if col.L == colName { + return fkInfo + } + } + } + return nil +} + +// AllocateColumnID allocates next column ID from TableInfo. +func AllocateColumnID(tblInfo *model.TableInfo) int64 { + tblInfo.MaxColumnID++ + return tblInfo.MaxColumnID +} + +func checkAddColumnTooManyColumns(colNum int) error { + if uint32(colNum) > atomic.LoadUint32(&config.GetGlobalConfig().TableColumnCountLimit) { + return dbterror.ErrTooManyFields + } + return nil +} + +// rollbackModifyColumnJob rollbacks the job when an error occurs. +func rollbackModifyColumnJob(d *ddlCtx, t *meta.Meta, tblInfo *model.TableInfo, job *model.Job, newCol, oldCol *model.ColumnInfo, modifyColumnTp byte) (ver int64, _ error) { + var err error + if oldCol.ID == newCol.ID && modifyColumnTp == mysql.TypeNull { + // field NotNullFlag flag reset. + tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.NotNullFlag) + // field PreventNullInsertFlag flag reset. + tblInfo.Columns[oldCol.Offset].SetFlag(oldCol.GetFlag() &^ mysql.PreventNullInsertFlag) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + } + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + // For those column-type-change type which doesn't need reorg data, we should also mock the job args for delete range. + job.Args = []interface{}{[]int64{}, []int64{}} + return ver, nil +} + +// modifyColsFromNull2NotNull modifies the type definitions of 'null' to 'not null'. +// Introduce the `mysql.PreventNullInsertFlag` flag to prevent users from inserting or updating null values. +func modifyColsFromNull2NotNull(w *worker, dbInfo *model.DBInfo, tblInfo *model.TableInfo, cols []*model.ColumnInfo, newCol *model.ColumnInfo, isDataTruncated bool) error { + // Get sessionctx from context resource pool. + var sctx sessionctx.Context + sctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(sctx) + + skipCheck := false + failpoint.Inject("skipMockContextDoExec", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + skipCheck = true + } + }) + if !skipCheck { + // If there is a null value inserted, it cannot be modified and needs to be rollback. + err = checkForNullValue(w.ctx, sctx, isDataTruncated, dbInfo.Name, tblInfo.Name, newCol, cols...) + if err != nil { + return errors.Trace(err) + } + } + + // Prevent this field from inserting null values. + for _, col := range cols { + col.AddFlag(mysql.PreventNullInsertFlag) + } + return nil +} + +func generateOriginDefaultValue(col *model.ColumnInfo, ctx sessionctx.Context) (interface{}, error) { + var err error + odValue := col.GetDefaultValue() + if odValue == nil && mysql.HasNotNullFlag(col.GetFlag()) { + switch col.GetType() { + // Just use enum field's first element for OriginDefaultValue. + case mysql.TypeEnum: + defEnum, verr := types.ParseEnumValue(col.GetElems(), 1) + if verr != nil { + return nil, errors.Trace(verr) + } + defVal := types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()) + return defVal.ToString() + default: + zeroVal := table.GetZeroValue(col) + odValue, err = zeroVal.ToString() + if err != nil { + return nil, errors.Trace(err) + } + } + } + + if odValue == strings.ToUpper(ast.CurrentTimestamp) { + var t time.Time + if ctx == nil { + t = time.Now() + } else { + t, _ = expression.GetStmtTimestamp(ctx) + } + if col.GetType() == mysql.TypeTimestamp { + odValue = types.NewTime(types.FromGoTime(t.UTC()), col.GetType(), col.GetDecimal()).String() + } else if col.GetType() == mysql.TypeDatetime { + odValue = types.NewTime(types.FromGoTime(t), col.GetType(), col.GetDecimal()).String() + } + } + return odValue, nil +} + +func indexInfoContains(idxID int64, idxInfos []*model.IndexInfo) bool { + for _, idxInfo := range idxInfos { + if idxID == idxInfo.ID { + return true + } + } + return false +} + +func indexInfosToIDList(idxInfos []*model.IndexInfo) []int64 { + ids := make([]int64, 0, len(idxInfos)) + for _, idxInfo := range idxInfos { + ids = append(ids, idxInfo.ID) + } + return ids +} + +func genChangingColumnUniqueName(tblInfo *model.TableInfo, oldCol *model.ColumnInfo) string { + suffix := 0 + newColumnNamePrefix := fmt.Sprintf("%s%s", changingColumnPrefix, oldCol.Name.O) + newColumnLowerName := fmt.Sprintf("%s_%d", strings.ToLower(newColumnNamePrefix), suffix) + // Check whether the new column name is used. + columnNameMap := make(map[string]bool, len(tblInfo.Columns)) + for _, col := range tblInfo.Columns { + columnNameMap[col.Name.L] = true + } + for columnNameMap[newColumnLowerName] { + suffix++ + newColumnLowerName = fmt.Sprintf("%s_%d", strings.ToLower(newColumnNamePrefix), suffix) + } + return fmt.Sprintf("%s_%d", newColumnNamePrefix, suffix) +} + +func genChangingIndexUniqueName(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) string { + suffix := 0 + newIndexNamePrefix := fmt.Sprintf("%s%s", changingIndexPrefix, idxInfo.Name.O) + newIndexLowerName := fmt.Sprintf("%s_%d", strings.ToLower(newIndexNamePrefix), suffix) + // Check whether the new index name is used. + indexNameMap := make(map[string]bool, len(tblInfo.Indices)) + for _, idx := range tblInfo.Indices { + indexNameMap[idx.Name.L] = true + } + for indexNameMap[newIndexLowerName] { + suffix++ + newIndexLowerName = fmt.Sprintf("%s_%d", strings.ToLower(newIndexNamePrefix), suffix) + } + return fmt.Sprintf("%s_%d", newIndexNamePrefix, suffix) +} + +func getChangingIndexOriginName(changingIdx *model.IndexInfo) string { + idxName := strings.TrimPrefix(changingIdx.Name.O, changingIndexPrefix) + // Since the unique idxName may contain the suffix number (indexName_num), better trim the suffix. + var pos int + if pos = strings.LastIndex(idxName, "_"); pos == -1 { + return idxName + } + return idxName[:pos] +} + +func getChangingColumnOriginName(changingColumn *model.ColumnInfo) string { + columnName := strings.TrimPrefix(changingColumn.Name.O, changingColumnPrefix) + var pos int + if pos = strings.LastIndex(columnName, "_"); pos == -1 { + return columnName + } + return columnName[:pos] +} diff --git a/ddl/column_change_test.go b/pkg/ddl/column_change_test.go similarity index 96% rename from ddl/column_change_test.go rename to pkg/ddl/column_change_test.go index ef350a2b03c92..36462cb063c9a 100644 --- a/ddl/column_change_test.go +++ b/pkg/ddl/column_change_test.go @@ -22,20 +22,20 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/ddl/column_modify_test.go b/pkg/ddl/column_modify_test.go similarity index 97% rename from ddl/column_modify_test.go rename to pkg/ddl/column_modify_test.go index 89007c448099e..d09b7f9715203 100644 --- a/ddl/column_modify_test.go +++ b/pkg/ddl/column_modify_test.go @@ -23,22 +23,22 @@ import ( "time" "github.com/pingcap/errors" - testddlutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + testddlutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/column_test.go b/pkg/ddl/column_test.go new file mode 100644 index 0000000000000..0f83e803a2995 --- /dev/null +++ b/pkg/ddl/column_test.go @@ -0,0 +1,948 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "reflect" + "strconv" + "sync" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func testCreateColumn(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, + colName string, pos string, defaultValue interface{}, dom *domain.Domain) int64 { + sql := fmt.Sprintf("alter table t1 add column %s int ", colName) + if defaultValue != nil { + sql += fmt.Sprintf("default %v ", defaultValue) + } + sql += pos + tk.MustExec(sql) + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + v := getSchemaVer(t, ctx) + require.NoError(t, dom.Reload()) + tblInfo, exist := dom.InfoSchema().TableByID(tblID) + require.True(t, exist) + checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) + return id +} + +func testCreateColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, + colNames []string, positions []string, defaultValue interface{}, dom *domain.Domain) int64 { + sql := "alter table t1 add column " + for i, colName := range colNames { + if i != 0 { + sql += ", add column " + } + sql += fmt.Sprintf("%s int %s", colName, positions[i]) + if defaultValue != nil { + sql += fmt.Sprintf(" default %v", defaultValue) + } + } + tk.MustExec(sql) + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + v := getSchemaVer(t, ctx) + require.NoError(t, dom.Reload()) + tblInfo, exist := dom.InfoSchema().TableByID(tblID) + require.True(t, exist) + checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) + return id +} + +func testDropColumnInternal(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName string, isError bool, dom *domain.Domain) int64 { + sql := fmt.Sprintf("alter table t1 drop column %s ", colName) + _, err := tk.Exec(sql) + if isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + v := getSchemaVer(t, ctx) + require.NoError(t, dom.Reload()) + tblInfo, exist := dom.InfoSchema().TableByID(tblID) + require.True(t, exist) + checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) + return id +} + +func testDropTable(tk *testkit.TestKit, t *testing.T, dbName, tblName string, dom *domain.Domain) int64 { + sql := fmt.Sprintf("drop table %s ", tblName) + tk.MustExec("use " + dbName) + tk.MustExec(sql) + + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + require.NoError(t, dom.Reload()) + _, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) + require.Error(t, err) + return id +} + +func testCreateIndex(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, unique bool, indexName string, colName string, dom *domain.Domain) int64 { + un := "" + if unique { + un = "unique" + } + sql := fmt.Sprintf("alter table t1 add %s index %s(%s)", un, indexName, colName) + tk.MustExec(sql) + + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + v := getSchemaVer(t, ctx) + require.NoError(t, dom.Reload()) + tblInfo, exist := dom.InfoSchema().TableByID(tblID) + require.True(t, exist) + checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) + return id +} + +func testDropColumns(tk *testkit.TestKit, t *testing.T, ctx sessionctx.Context, tblID int64, colName []string, isError bool, dom *domain.Domain) int64 { + sql := "alter table t1 drop column " + for i, name := range colName { + if i != 0 { + sql += ", drop column " + } + sql += name + } + _, err := tk.Exec(sql) + if isError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + + idi, _ := strconv.Atoi(tk.MustQuery("admin show ddl jobs 1;").Rows()[0][0].(string)) + id := int64(idi) + v := getSchemaVer(t, ctx) + require.NoError(t, dom.Reload()) + tblInfo, exist := dom.InfoSchema().TableByID(tblID) + require.True(t, exist) + checkHistoryJobArgs(t, ctx, id, &historyJobArgs{ver: v, tbl: tblInfo.Meta()}) + return id +} + +func TestColumnBasic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") + + num := 10 + for i := 0; i < num; i++ { + tk.MustExec(fmt.Sprintf("insert into t1 values(%d, %d, %d)", i, 10*i, 100*i)) + } + + ctx := testNewContext(store) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + + tbl := testGetTable(t, dom, tableID) + + i := 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Len(t, data, 3) + require.Equal(t, data[0].GetInt64(), int64(i)) + require.Equal(t, data[1].GetInt64(), int64(10*i)) + require.Equal(t, data[2].GetInt64(), int64(100*i)) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equal(t, i, num) + + require.Nil(t, table.FindCol(tbl.Cols(), "c4")) + + jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "after c3", 100, dom) + testCheckJobDone(t, store, jobID, true) + + tbl = testGetTable(t, dom, tableID) + require.NotNil(t, table.FindCol(tbl.Cols(), "c4")) + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), + func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Len(t, data, 4) + require.Equal(t, data[0].GetInt64(), int64(i)) + require.Equal(t, data[1].GetInt64(), int64(10*i)) + require.Equal(t, data[2].GetInt64(), int64(100*i)) + require.Equal(t, data[3].GetInt64(), int64(100)) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equal(t, i, num) + + h, err := tbl.AddRecord(ctx, types.MakeDatums(11, 12, 13, 14)) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + values, err := tables.RowWithCols(tbl, ctx, h, tbl.Cols()) + require.NoError(t, err) + + require.Len(t, values, 4) + require.Equal(t, values[3].GetInt64(), int64(14)) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom) + testCheckJobDone(t, store, jobID, false) + + tbl = testGetTable(t, dom, tableID) + values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) + require.NoError(t, err) + + require.Len(t, values, 3) + require.Equal(t, values[2].GetInt64(), int64(13)) + + jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", "", 111, dom) + testCheckJobDone(t, store, jobID, true) + + tbl = testGetTable(t, dom, tableID) + values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) + require.NoError(t, err) + + require.Len(t, values, 4) + require.Equal(t, values[3].GetInt64(), int64(111)) + + jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", "", 101, dom) + testCheckJobDone(t, store, jobID, true) + + tbl = testGetTable(t, dom, tableID) + values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) + require.NoError(t, err) + + require.Len(t, values, 5) + require.Equal(t, values[4].GetInt64(), int64(101)) + + jobID = testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", "first", 202, dom) + testCheckJobDone(t, store, jobID, true) + + tbl = testGetTable(t, dom, tableID) + cols := tbl.Cols() + require.Len(t, cols, 6) + require.Equal(t, cols[0].Offset, 0) + require.Equal(t, cols[0].Name.L, "c6") + require.Equal(t, cols[1].Offset, 1) + require.Equal(t, cols[1].Name.L, "c1") + require.Equal(t, cols[2].Offset, 2) + require.Equal(t, cols[2].Name.L, "c2") + require.Equal(t, cols[3].Offset, 3) + require.Equal(t, cols[3].Name.L, "c3") + require.Equal(t, cols[4].Offset, 4) + require.Equal(t, cols[4].Name.L, "c4") + require.Equal(t, cols[5].Offset, 5) + require.Equal(t, cols[5].Name.L, "c5") + + values, err = tables.RowWithCols(tbl, ctx, h, cols) + require.NoError(t, err) + + require.Len(t, values, 6) + require.Equal(t, values[0].GetInt64(), int64(202)) + require.Equal(t, values[5].GetInt64(), int64(101)) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c2", false, dom) + testCheckJobDone(t, store, jobID, false) + + tbl = testGetTable(t, dom, tableID) + + values, err = tables.RowWithCols(tbl, ctx, h, tbl.Cols()) + require.NoError(t, err) + require.Len(t, values, 5) + require.Equal(t, values[0].GetInt64(), int64(202)) + require.Equal(t, values[4].GetInt64(), int64(101)) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c1", false, dom) + testCheckJobDone(t, store, jobID, false) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c3", false, dom) + testCheckJobDone(t, store, jobID, false) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c4", false, dom) + testCheckJobDone(t, store, jobID, false) + + jobID = testCreateIndex(tk, t, testkit.NewTestKit(t, store).Session(), tableID, false, "c5_idx", "c5", dom) + testCheckJobDone(t, store, jobID, true) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c5", false, dom) + testCheckJobDone(t, store, jobID, false) + + jobID = testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c6", true, dom) + testCheckJobDone(t, store, jobID, false) + + testDropTable(tk, t, "test", "t1", dom) +} + +func checkColumnKVExist(ctx sessionctx.Context, t table.Table, handle kv.Handle, col *table.Column, columnValue interface{}, isExist bool) error { + err := sessiontxn.NewTxn(context.Background(), ctx) + if err != nil { + return errors.Trace(err) + } + defer func() { + if txn, err1 := ctx.Txn(true); err1 == nil { + err = txn.Commit(context.Background()) + if err != nil { + panic(err) + } + } + }() + key := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) + txn, err := ctx.Txn(true) + if err != nil { + return errors.Trace(err) + } + data, err := txn.Get(context.TODO(), key) + if !isExist { + if terror.ErrorEqual(err, kv.ErrNotExist) { + return nil + } + } + if err != nil { + return errors.Trace(err) + } + colMap := make(map[int64]*types.FieldType) + colMap[col.ID] = &col.FieldType + rowMap, err := tablecodec.DecodeRowToDatumMap(data, colMap, ctx.GetSessionVars().Location()) + if err != nil { + return errors.Trace(err) + } + val, ok := rowMap[col.ID] + if isExist { + if !ok || val.GetValue() != columnValue { + return errors.Errorf("%v is not equal to %v", val.GetValue(), columnValue) + } + } else { + if ok { + return errors.Errorf("column value should not exists") + } + } + return nil +} + +func checkNoneColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, columnValue interface{}, dom *domain.Domain) { + tbl := testGetTable(t, dom, tableID) + err := checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) + require.NoError(t, err) + err = testGetColumn(tbl, col.Name.L, false) + require.NoError(t, err) +} + +func checkDeleteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { + tbl := testGetTable(t, dom, tableID) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + i := 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) + require.NoError(t, err) + // Test add a new row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + newRow := types.MakeDatums(int64(11), int64(22), int64(33)) + newHandle, err := tbl.AddRecord(ctx, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + rows := [][]types.Datum{row, newRow} + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 2, i, "expect 2, got %v", i) + + err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) + require.NoError(t, err) + // Test remove a row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + err = tbl.RemoveRecord(ctx, newHandle, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + i++ + return true, nil + }) + require.NoError(t, err) + + require.Equalf(t, 1, i, "expect 1, got %v", i) + err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false) + require.NoError(t, err) + err = testGetColumn(tbl, col.Name.L, false) + require.NoError(t, err) +} + +func checkWriteOnlyColumn(t *testing.T, ctx sessionctx.Context, tableID int64, handle kv.Handle, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { + tbl := testGetTable(t, dom, tableID) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i := 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + + err = checkColumnKVExist(ctx, tbl, handle, col, columnValue, false) + require.NoError(t, err) + + // Test add a new row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + newRow := types.MakeDatums(int64(11), int64(22), int64(33)) + newHandle, err := tbl.AddRecord(ctx, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + rows := [][]types.Datum{row, newRow} + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 2, i, "expect 2, got %v", i) + + err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true) + require.NoError(t, err) + // Test remove a row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + err = tbl.RemoveRecord(ctx, newHandle, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + + err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, false) + require.NoError(t, err) + err = testGetColumn(tbl, col.Name.L, false) + require.NoError(t, err) +} + +func checkReorganizationColumn(t *testing.T, ctx sessionctx.Context, tableID int64, col *table.Column, row []types.Datum, columnValue interface{}, dom *domain.Domain) { + tbl := testGetTable(t, dom, tableID) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i := 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, row), "%v not equal to %v", data, row) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + + // Test add a new row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + newRow := types.MakeDatums(int64(11), int64(22), int64(33)) + newHandle, err := tbl.AddRecord(ctx, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + rows := [][]types.Datum{row, newRow} + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 2, i, "expect 2, got %v", i) + + err = checkColumnKVExist(ctx, tbl, newHandle, col, columnValue, true) + require.NoError(t, err) + + // Test remove a row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + err = tbl.RemoveRecord(ctx, newHandle, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + err = testGetColumn(tbl, col.Name.L, false) + require.NoError(t, err) +} + +func checkPublicColumn(t *testing.T, ctx sessionctx.Context, tableID int64, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, columnCnt int) { + tbl := testGetTable(t, dom, tableID) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i := 0 + var updatedRow []types.Datum + updatedRow = append(updatedRow, oldRow...) + for j := 0; j < columnCnt; j++ { + updatedRow = append(updatedRow, types.NewDatum(columnValue)) + } + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, updatedRow), "%v not equal to %v", data, updatedRow) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + + // Test add a new row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + newRow := types.MakeDatums(int64(11), int64(22), int64(33), int64(44)) + for j := 1; j < columnCnt; j++ { + newRow = append(newRow, types.NewDatum(int64(44))) + } + handle, err := tbl.AddRecord(ctx, newRow) + require.NoError(t, err) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + rows := [][]types.Datum{updatedRow, newRow} + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 2, i, "expect 2, got %v", i) + + // Test remove a row. + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + err = tbl.RemoveRecord(ctx, handle, newRow) + require.NoError(t, err) + + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + i = 0 + err = tables.IterRecords(tbl, ctx, tbl.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + require.Truef(t, reflect.DeepEqual(data, rows[i]), "%v not equal to %v", data, rows[i]) + i++ + return true, nil + }) + require.NoError(t, err) + require.Equalf(t, 1, i, "expect 1, got %v", i) + + err = testGetColumn(tbl, newCol.Name.L, true) + require.NoError(t, err) +} + +func checkAddColumn(t *testing.T, state model.SchemaState, tableID int64, handle kv.Handle, newCol *table.Column, oldRow []types.Datum, columnValue interface{}, dom *domain.Domain, store kv.Storage, columnCnt int) { + ctx := testNewContext(store) + switch state { + case model.StateNone: + checkNoneColumn(t, ctx, tableID, handle, newCol, columnValue, dom) + case model.StateDeleteOnly: + checkDeleteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom) + case model.StateWriteOnly: + checkWriteOnlyColumn(t, ctx, tableID, handle, newCol, oldRow, columnValue, dom) + case model.StateWriteReorganization, model.StateDeleteReorganization: + checkReorganizationColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom) + case model.StatePublic: + checkPublicColumn(t, ctx, tableID, newCol, oldRow, columnValue, dom, columnCnt) + } +} + +func testGetColumn(t table.Table, name string, isExist bool) error { + col := table.FindCol(t.Cols(), name) + if isExist { + if col == nil { + return errors.Errorf("column should not be nil") + } + } else { + if col != nil { + return errors.Errorf("column should be nil") + } + } + return nil +} + +func TestAddColumn(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) + + d := dom.DDL() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + tbl := testGetTable(t, dom, tableID) + + ctx := testNewContext(store) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + oldRow := types.MakeDatums(int64(1), int64(2), int64(3)) + handle, err := tbl.AddRecord(ctx, oldRow) + require.NoError(t, err) + + txn, err := ctx.Txn(true) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + newColName := "c4" + defaultColValue := int64(4) + + checkOK := false + + tc := &callback.TestDDLCallback{Do: dom} + onJobUpdatedExportedFunc := func(job *model.Job) { + if checkOK { + return + } + + tbl := testGetTable(t, dom, tableID) + newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName) + if newCol == nil { + return + } + + checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 1) + + if newCol.State == model.StatePublic { + checkOK = true + } + } + tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + d.SetHook(tc) + + jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColName, "", defaultColValue, dom) + testCheckJobDone(t, store, jobID, true) + + require.True(t, checkOK) + + jobID = testDropTable(tk, t, "test", "t1", dom) + testCheckJobDone(t, store, jobID, false) +} + +func TestAddColumns(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) + + d := dom.DDL() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int);") + + newColNames := []string{"c4", "c5", "c6"} + positions := make([]string, 3) + for i := range positions { + positions[i] = "" + } + defaultColValue := int64(4) + + var mu sync.Mutex + var hookErr error + checkOK := false + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + tbl := testGetTable(t, dom, tableID) + + ctx := testNewContext(store) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + oldRow := types.MakeDatums(int64(1), int64(2), int64(3)) + handle, err := tbl.AddRecord(ctx, oldRow) + require.NoError(t, err) + + txn, err := ctx.Txn(true) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + tc := &callback.TestDDLCallback{Do: dom} + onJobUpdatedExportedFunc := func(job *model.Job) { + mu.Lock() + defer mu.Unlock() + if checkOK { + return + } + + tbl := testGetTable(t, dom, tableID) + for _, newColName := range newColNames { + newCol := table.FindCol(tbl.(*tables.TableCommon).Columns, newColName) + if newCol == nil { + return + } + + checkAddColumn(t, newCol.State, tableID, handle, newCol, oldRow, defaultColValue, dom, store, 3) + + if newCol.State == model.StatePublic { + checkOK = true + } + } + } + tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + d.SetHook(tc) + + jobID := testCreateColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, newColNames, positions, defaultColValue, dom) + + testCheckJobDone(t, store, jobID, true) + mu.Lock() + hErr := hookErr + ok := checkOK + mu.Unlock() + require.NoError(t, hErr) + require.True(t, ok) + + jobID = testDropTable(tk, t, "test", "t1", dom) + testCheckJobDone(t, store, jobID, false) +} + +func TestDropColumnInColumnTest(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);") + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + tbl := testGetTable(t, dom, tableID) + + ctx := testNewContext(store) + colName := "c4" + defaultColValue := int64(4) + row := types.MakeDatums(int64(1), int64(2), int64(3)) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + _, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue))) + require.NoError(t, err) + + txn, err := ctx.Txn(true) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + checkOK := false + var hookErr error + var mu sync.Mutex + + d := dom.DDL() + tc := &callback.TestDDLCallback{Do: dom} + onJobUpdatedExportedFunc := func(job *model.Job) { + mu.Lock() + defer mu.Unlock() + if checkOK { + return + } + tbl := testGetTable(t, dom, tableID) + col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName) + if col == nil { + checkOK = true + return + } + } + tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + d.SetHook(tc) + + jobID := testDropColumnInternal(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colName, false, dom) + testCheckJobDone(t, store, jobID, false) + mu.Lock() + hErr := hookErr + ok := checkOK + mu.Unlock() + require.NoError(t, hErr) + require.True(t, ok) + + jobID = testDropTable(tk, t, "test", "t1", dom) + testCheckJobDone(t, store, jobID, false) +} + +func TestDropColumns(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int, c4 int);") + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + tbl := testGetTable(t, dom, tableID) + + ctx := testNewContext(store) + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + colNames := []string{"c3", "c4"} + defaultColValue := int64(4) + row := types.MakeDatums(int64(1), int64(2), int64(3)) + _, err = tbl.AddRecord(ctx, append(row, types.NewDatum(defaultColValue))) + require.NoError(t, err) + + txn, err := ctx.Txn(true) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + checkOK := false + var hookErr error + var mu sync.Mutex + + d := dom.DDL() + tc := &callback.TestDDLCallback{Do: dom} + onJobUpdatedExportedFunc := func(job *model.Job) { + mu.Lock() + defer mu.Unlock() + if checkOK { + return + } + tbl := testGetTable(t, dom, tableID) + for _, colName := range colNames { + col := table.FindCol(tbl.(*tables.TableCommon).Columns, colName) + if col == nil { + checkOK = true + return + } + } + } + tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + d.SetHook(tc) + + jobID := testDropColumns(tk, t, testkit.NewTestKit(t, store).Session(), tableID, colNames, false, dom) + testCheckJobDone(t, store, jobID, false) + mu.Lock() + hErr := hookErr + ok := checkOK + mu.Unlock() + require.NoError(t, hErr) + require.True(t, ok) + + jobID = testDropTable(tk, t, "test", "t1", dom) + testCheckJobDone(t, store, jobID, false) +} + +func testGetTable(t *testing.T, dom *domain.Domain, tableID int64) table.Table { + require.NoError(t, dom.Reload()) + tbl, exist := dom.InfoSchema().TableByID(tableID) + require.True(t, exist) + return tbl +} + +func TestWriteDataWriteOnlyMode(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk2.MustExec("use test") + tk.MustExec("CREATE TABLE t (`col1` bigint(20) DEFAULT 1,`col2` float,UNIQUE KEY `key1` (`col1`))") + + originalCallback := dom.DDL().GetHook() + defer dom.DDL().SetHook(originalCallback) + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateWriteOnly { + return + } + tk2.MustExec("insert ignore into t values (1, 2)") + tk2.MustExec("insert ignore into t values (2, 2)") + } + dom.DDL().SetHook(hook) + tk.MustExec("alter table t change column `col1` `col1` varchar(20)") + + hook = &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateWriteOnly { + return + } + tk2.MustExec("insert ignore into t values (1)") + tk2.MustExec("insert ignore into t values (2)") + } + dom.DDL().SetHook(hook) + tk.MustExec("alter table t drop column `col1`") + dom.DDL().SetHook(originalCallback) +} diff --git a/ddl/column_type_change_test.go b/pkg/ddl/column_type_change_test.go similarity index 95% rename from ddl/column_type_change_test.go rename to pkg/ddl/column_type_change_test.go index f4ec34d00543b..912e3790c216c 100644 --- a/ddl/column_type_change_test.go +++ b/pkg/ddl/column_type_change_test.go @@ -22,20 +22,20 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) @@ -281,8 +281,8 @@ func TestRowLevelChecksumWithMultiSchemaChange(t *testing.T) { tk.MustExec("create table t (id int primary key, v varchar(10))") tk.MustExec("insert into t values (1, \"123\")") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/forceRowLevelChecksumOnUpdateColumnBackfill", "return")) - defer failpoint.Disable("github.com/pingcap/tidb/ddl/forceRowLevelChecksumOnUpdateColumnBackfill") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/forceRowLevelChecksumOnUpdateColumnBackfill", "return")) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/forceRowLevelChecksumOnUpdateColumnBackfill") tk.MustExec("alter table t add column vv int, modify column v varchar(5)") tbl := external.GetTableByName(t, tk, "test", "t") @@ -538,9 +538,9 @@ func TestDDLExitWhenCancelMeetPanic(t *testing.T) { tk.MustExec("alter table t add index(b)") tk.MustExec("set @@global.tidb_ddl_error_count_limit=3") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockExceedErrorLimit", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockExceedErrorLimit")) }() hook := &callback.TestDDLCallback{Do: dom} @@ -575,9 +575,9 @@ func TestCancelCTCInReorgStateWillCauseGoroutineLeak(t *testing.T) { tk.MustExec("set global tidb_enable_row_level_checksum = 1") tk.MustExec("use test") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockInfiniteReorgLogic", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockInfiniteReorgLogic", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockInfiniteReorgLogic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockInfiniteReorgLogic")) }() // set ddl hook diff --git a/pkg/ddl/constant.go b/pkg/ddl/constant.go new file mode 100644 index 0000000000000..8b3757bf6f7b9 --- /dev/null +++ b/pkg/ddl/constant.go @@ -0,0 +1,85 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "github.com/pingcap/tidb/pkg/meta" +) + +const ( + // JobTable stores the information of DDL jobs. + JobTable = "tidb_ddl_job" + // ReorgTable stores the information of DDL reorganization. + ReorgTable = "tidb_ddl_reorg" + // HistoryTable stores the history DDL jobs. + HistoryTable = "tidb_ddl_history" + + // JobTableID is the table ID of `tidb_ddl_job`. + JobTableID = meta.MaxInt48 - 1 + // ReorgTableID is the table ID of `tidb_ddl_reorg`. + ReorgTableID = meta.MaxInt48 - 2 + // HistoryTableID is the table ID of `tidb_ddl_history`. + HistoryTableID = meta.MaxInt48 - 3 + // MDLTableID is the table ID of `tidb_mdl_info`. + MDLTableID = meta.MaxInt48 - 4 + // BackgroundSubtaskTableID is the table ID of `tidb_background_subtask`. + BackgroundSubtaskTableID = meta.MaxInt48 - 5 + // BackgroundSubtaskHistoryTableID is the table ID of `tidb_background_subtask_history`. + BackgroundSubtaskHistoryTableID = meta.MaxInt48 - 6 + + // JobTableSQL is the CREATE TABLE SQL of `tidb_ddl_job`. + JobTableSQL = "create table " + JobTable + "(job_id bigint not null, reorg int, schema_ids text(65535), table_ids text(65535), job_meta longblob, type int, processing int, primary key(job_id))" + // ReorgTableSQL is the CREATE TABLE SQL of `tidb_ddl_reorg`. + ReorgTableSQL = "create table " + ReorgTable + "(job_id bigint not null, ele_id bigint, ele_type blob, start_key blob, end_key blob, physical_id bigint, reorg_meta longblob, unique key(job_id, ele_id, ele_type(20)))" + // HistoryTableSQL is the CREATE TABLE SQL of `tidb_ddl_history`. + HistoryTableSQL = "create table " + HistoryTable + "(job_id bigint not null, job_meta longblob, db_name char(64), table_name char(64), schema_ids text(65535), table_ids text(65535), create_time datetime, primary key(job_id))" + // BackgroundSubtaskTableSQL is the CREATE TABLE SQL of `tidb_background_subtask`. + BackgroundSubtaskTableSQL = `create table tidb_background_subtask ( + id bigint not null auto_increment primary key, + step int, + namespace varchar(256), + task_key varchar(256), + ddl_physical_tid bigint(20), + type int, + exec_id varchar(256), + exec_expired timestamp, + state varchar(64) not null, + checkpoint longblob not null, + start_time bigint, + state_update_time bigint, + meta longblob, + error BLOB, + summary json, + key idx_task_key(task_key))` + // BackgroundSubtaskHistoryTableSQL is the CREATE TABLE SQL of `tidb_background_subtask_history`. + BackgroundSubtaskHistoryTableSQL = `create table tidb_background_subtask_history ( + id bigint not null auto_increment primary key, + step int, + namespace varchar(256), + task_key varchar(256), + ddl_physical_tid bigint(20), + type int, + exec_id varchar(256), + exec_expired timestamp, + state varchar(64) not null, + checkpoint longblob not null, + start_time bigint, + state_update_time bigint, + meta longblob, + error BLOB, + summary json, + key idx_task_key(task_key), + key idx_state_update_time(state_update_time))` +) diff --git a/pkg/ddl/constraint.go b/pkg/ddl/constraint.go new file mode 100644 index 0000000000000..02b9843887caf --- /dev/null +++ b/pkg/ddl/constraint.go @@ -0,0 +1,417 @@ +// Copyright 2023-2023 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +func (w *worker) onAddCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + // Handle the rolling back job. + if job.IsRollingback() { + ver, err = onDropCheckConstraint(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + return ver, nil + } + + failpoint.Inject("errorBeforeDecodeArgs", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(ver, errors.New("occur an error before decode args")) + } + }) + + dbInfo, tblInfo, constraintInfoInMeta, constraintInfoInJob, err := checkAddCheckConstraint(t, job) + if err != nil { + return ver, errors.Trace(err) + } + if constraintInfoInMeta == nil { + // It's first time to run add constraint job, so there is no constraint info in meta. + // Use the raw constraint info from job directly and modify table info here. + constraintInfoInJob.ID = allocateConstraintID(tblInfo) + // Reset constraint name according to real-time constraints name at this point. + constrNames := map[string]bool{} + for _, constr := range tblInfo.Constraints { + constrNames[constr.Name.L] = true + } + setNameForConstraintInfo(tblInfo.Name.L, constrNames, []*model.ConstraintInfo{constraintInfoInJob}) + // Double check the constraint dependency. + existedColsMap := make(map[string]struct{}) + cols := tblInfo.Columns + for _, v := range cols { + if v.State == model.StatePublic { + existedColsMap[v.Name.L] = struct{}{} + } + } + dependedCols := constraintInfoInJob.ConstraintCols + for _, k := range dependedCols { + if _, ok := existedColsMap[k.L]; !ok { + // The table constraint depended on a non-existed column. + return ver, dbterror.ErrTableCheckConstraintReferUnknown.GenWithStackByArgs(constraintInfoInJob.Name, k) + } + } + + tblInfo.Constraints = append(tblInfo.Constraints, constraintInfoInJob) + constraintInfoInMeta = constraintInfoInJob + } + + originalState := constraintInfoInMeta.State + switch constraintInfoInMeta.State { + case model.StateNone: + job.SchemaState = model.StateWriteOnly + constraintInfoInMeta.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) + case model.StateWriteOnly: + job.SchemaState = model.StateWriteReorganization + constraintInfoInMeta.State = model.StateWriteReorganization + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) + case model.StateWriteReorganization: + err = w.verifyRemainRecordsForCheckConstraint(dbInfo, tblInfo, constraintInfoInMeta, job) + if err != nil { + return ver, errors.Trace(err) + } + constraintInfoInMeta.State = model.StatePublic + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != constraintInfoInMeta.State) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("constraint", constraintInfoInMeta.State) + } + + return ver, errors.Trace(err) +} + +func checkAddCheckConstraint(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ConstraintInfo, *model.ConstraintInfo, error) { + schemaID := job.SchemaID + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return nil, nil, nil, nil, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, nil, errors.Trace(err) + } + constraintInfo1 := &model.ConstraintInfo{} + err = job.DecodeArgs(constraintInfo1) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, nil, errors.Trace(err) + } + // do the double-check with constraint existence. + constraintInfo2 := tblInfo.FindConstraintInfoByName(constraintInfo1.Name.L) + if constraintInfo2 != nil { + if constraintInfo2.State == model.StatePublic { + // We already have a constraint with the same constraint name. + job.State = model.JobStateCancelled + return nil, nil, nil, nil, infoschema.ErrColumnExists.GenWithStackByArgs(constraintInfo1.Name) + } + // if not, that means constraint was in intermediate state. + } + return dbInfo, tblInfo, constraintInfo2, constraintInfo1, nil +} + +// onDropCheckConstraint can be called from two case: +// 1: rollback in add constraint.(in rollback function the job.args will be changed) +// 2: user drop constraint ddl. +func onDropCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, constraintInfo, err := checkDropCheckConstraint(t, job) + if err != nil { + return ver, errors.Trace(err) + } + + originalState := constraintInfo.State + switch constraintInfo.State { + case model.StatePublic: + job.SchemaState = model.StateWriteOnly + constraintInfo.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) + case model.StateWriteOnly: + // write only state constraint will still take effect to check the newly inserted data. + // So the dependent column shouldn't be dropped even in this intermediate state. + constraintInfo.State = model.StateNone + // remove the constraint from tableInfo. + for i, constr := range tblInfo.Constraints { + if constr.Name.L == constraintInfo.Name.L { + tblInfo.Constraints = append(tblInfo.Constraints[0:i], tblInfo.Constraints[i+1:]...) + } + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != constraintInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + if job.IsRollingback() { + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + } else { + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + } + default: + err = dbterror.ErrInvalidDDLJob.GenWithStackByArgs("constraint", tblInfo.State) + } + return ver, errors.Trace(err) +} + +func checkDropCheckConstraint(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.ConstraintInfo, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, errors.Trace(err) + } + + var constrName model.CIStr + err = job.DecodeArgs(&constrName) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, errors.Trace(err) + } + + // double check with constraint existence. + constraintInfo := tblInfo.FindConstraintInfoByName(constrName.L) + if constraintInfo == nil { + job.State = model.JobStateCancelled + return nil, nil, dbterror.ErrConstraintNotFound.GenWithStackByArgs(constrName) + } + return tblInfo, constraintInfo, nil +} + +func (w *worker) onAlterCheckConstraint(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + dbInfo, tblInfo, constraintInfo, enforced, err := checkAlterCheckConstraint(t, job) + if err != nil { + return ver, errors.Trace(err) + } + + // enforced will fetch table data and check the constraint. + if enforced { + originalState := constraintInfo.State + switch constraintInfo.State { + case model.StatePublic: + job.SchemaState = model.StateWriteReorganization + constraintInfo.State = model.StateWriteReorganization + constraintInfo.Enforced = enforced + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) + case model.StateWriteReorganization: + job.SchemaState = model.StateWriteOnly + constraintInfo.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) + case model.StateWriteOnly: + err = w.verifyRemainRecordsForCheckConstraint(dbInfo, tblInfo, constraintInfo, job) + if err != nil { + if !table.ErrCheckConstraintViolated.Equal(err) { + return ver, errors.Trace(err) + } + constraintInfo.Enforced = !enforced + } + constraintInfo.State = model.StatePublic + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != constraintInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + } + } else { + constraintInfo.Enforced = enforced + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) + if err != nil { + // update version and tableInfo error will cause retry. + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + } + return ver, err +} + +func checkAlterCheckConstraint(t *meta.Meta, job *model.Job) (*model.DBInfo, *model.TableInfo, *model.ConstraintInfo, bool, error) { + schemaID := job.SchemaID + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return nil, nil, nil, false, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, false, errors.Trace(err) + } + + var ( + enforced bool + constrName model.CIStr + ) + err = job.DecodeArgs(&constrName, &enforced) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, false, errors.Trace(err) + } + // do the double check with constraint existence. + constraintInfo := tblInfo.FindConstraintInfoByName(constrName.L) + if constraintInfo == nil { + job.State = model.JobStateCancelled + return nil, nil, nil, false, dbterror.ErrConstraintNotFound.GenWithStackByArgs(constrName) + } + return dbInfo, tblInfo, constraintInfo, enforced, nil +} + +func allocateConstraintID(tblInfo *model.TableInfo) int64 { + tblInfo.MaxConstraintID++ + return tblInfo.MaxConstraintID +} + +func buildConstraintInfo(tblInfo *model.TableInfo, dependedCols []model.CIStr, constr *ast.Constraint, state model.SchemaState) (*model.ConstraintInfo, error) { + constraintName := model.NewCIStr(constr.Name) + if err := checkTooLongConstraint(constraintName); err != nil { + return nil, errors.Trace(err) + } + + // Restore check constraint expression to string. + var sb strings.Builder + restoreFlags := format.RestoreStringSingleQuotes | format.RestoreKeyWordLowercase | format.RestoreNameBackQuotes | + format.RestoreSpacesAroundBinaryOperation + restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) + + sb.Reset() + err := constr.Expr.Restore(restoreCtx) + if err != nil { + return nil, errors.Trace(err) + } + + // Create constraint info. + constraintInfo := &model.ConstraintInfo{ + Name: constraintName, + Table: tblInfo.Name, + ConstraintCols: dependedCols, + ExprString: sb.String(), + Enforced: constr.Enforced, + InColumn: constr.InColumn, + State: state, + } + + return constraintInfo, nil +} + +func checkTooLongConstraint(constr model.CIStr) error { + if len(constr.L) > mysql.MaxConstraintIdentifierLen { + return dbterror.ErrTooLongIdent.GenWithStackByArgs(constr) + } + return nil +} + +// findDependentColsInExpr returns a set of string, which indicates +// the names of the columns that are dependent by exprNode. +func findDependentColsInExpr(expr ast.ExprNode) map[string]struct{} { + colNames := FindColumnNamesInExpr(expr) + colsMap := make(map[string]struct{}, len(colNames)) + for _, depCol := range colNames { + colsMap[depCol.Name.L] = struct{}{} + } + return colsMap +} + +func (w *worker) verifyRemainRecordsForCheckConstraint(dbInfo *model.DBInfo, tableInfo *model.TableInfo, constr *model.ConstraintInfo, job *model.Job) error { + // Inject a fail-point to skip the remaining records check. + failpoint.Inject("mockVerifyRemainDataSuccess", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil) + } + }) + // Get sessionctx from ddl context resource pool in ddl worker. + var sctx sessionctx.Context + sctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(sctx) + + // If there is any row can't pass the check expression, the add constraint action will error. + // It's no need to construct expression node out and pull the chunk rows through it. Here we + // can let the check expression restored string as the filter in where clause directly. + // Prepare internal SQL to fetch data from physical table under this filter. + sql := fmt.Sprintf("select 1 from `%s`.`%s` where not %s limit 1", dbInfo.Name.L, tableInfo.Name.L, constr.ExprString) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, sql) + if err != nil { + return errors.Trace(err) + } + rowCount := len(rows) + if rowCount != 0 { + // If check constraint fail, the job state should be changed to canceled, otherwise it will tracked in. + job.State = model.JobStateCancelled + return dbterror.ErrCheckConstraintIsViolated.GenWithStackByArgs(constr.Name.L) + } + return nil +} + +func setNameForConstraintInfo(tableLowerName string, namesMap map[string]bool, infos []*model.ConstraintInfo) { + cnt := 1 + constraintPrefix := tableLowerName + "_chk_" + for _, constrInfo := range infos { + if constrInfo.Name.O == "" { + constrName := fmt.Sprintf("%s%d", constraintPrefix, cnt) + for { + // loop until find constrName that haven't been used. + if !namesMap[constrName] { + namesMap[constrName] = true + break + } + cnt++ + constrName = fmt.Sprintf("%s%d", constraintPrefix, cnt) + } + constrInfo.Name = model.NewCIStr(constrName) + } + } +} + +// IsColumnDroppableWithCheckConstraint check whether the column in check-constraint whose dependent col is more than 1 +func IsColumnDroppableWithCheckConstraint(col model.CIStr, tblInfo *model.TableInfo) error { + for _, cons := range tblInfo.Constraints { + if len(cons.ConstraintCols) > 1 { + for _, colName := range cons.ConstraintCols { + if colName.L == col.L { + return dbterror.ErrCantDropColWithCheckConstraint.GenWithStackByArgs(cons.Name, col) + } + } + } + } + return nil +} + +// IsColumnRenameableWithCheckConstraint check whether the column is referenced in check-constraint +func IsColumnRenameableWithCheckConstraint(col model.CIStr, tblInfo *model.TableInfo) error { + for _, cons := range tblInfo.Constraints { + for _, colName := range cons.ConstraintCols { + if colName.L == col.L { + return dbterror.ErrCantDropColWithCheckConstraint.GenWithStackByArgs(cons.Name, col) + } + } + } + return nil +} diff --git a/ddl/constraint_test.go b/pkg/ddl/constraint_test.go similarity index 95% rename from ddl/constraint_test.go rename to pkg/ddl/constraint_test.go index 4b13d968f6421..c1837bcd54d6b 100644 --- a/ddl/constraint_test.go +++ b/pkg/ddl/constraint_test.go @@ -19,12 +19,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" "github.com/stretchr/testify/require" ) @@ -107,7 +107,7 @@ func TestAlterAddConstraintStateChange(t *testing.T) { } //StatNone StateWriteReorganization - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockVerifyRemainDataSuccess", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockVerifyRemainDataSuccess", "return(true)")) callback.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) d.SetHook(callback) tk.MustExec("alter table t add constraint c0 check ( a > 10)") @@ -115,7 +115,7 @@ func TestAlterAddConstraintStateChange(t *testing.T) { tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n `a` int(11) DEFAULT NULL,\nCONSTRAINT `c0` CHECK ((`a` > 10))\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) tk.MustExec("alter table t drop constraint c0") tk.MustExec("delete from t where a = 1") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockVerifyRemainDataSuccess")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockVerifyRemainDataSuccess")) } func TestAlterAddConstraintStateChange1(t *testing.T) { diff --git a/pkg/ddl/copr/BUILD.bazel b/pkg/ddl/copr/BUILD.bazel new file mode 100644 index 0000000000000..3cb9d8a12584e --- /dev/null +++ b/pkg/ddl/copr/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "copr", + srcs = ["copr_ctx.go"], + importpath = "github.com/pingcap/tidb/pkg/ddl/copr", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/table/tables", + "//pkg/types", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "copr_test", + timeout = "short", + srcs = ["copr_ctx_test.go"], + embed = [":copr"], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/expression", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + ], +) diff --git a/ddl/copr/copr_ctx.go b/pkg/ddl/copr/copr_ctx.go similarity index 97% rename from ddl/copr/copr_ctx.go rename to pkg/ddl/copr/copr_ctx.go index db43f238ffce8..0eb0ebe79befc 100644 --- a/ddl/copr/copr_ctx.go +++ b/pkg/ddl/copr/copr_ctx.go @@ -16,11 +16,11 @@ package copr import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" ) // CopContext contains the information that is needed when building a coprocessor request. diff --git a/ddl/copr/copr_ctx_test.go b/pkg/ddl/copr/copr_ctx_test.go similarity index 96% rename from ddl/copr/copr_ctx_test.go rename to pkg/ddl/copr/copr_ctx_test.go index 427776bd83610..4d43f14f6de00 100644 --- a/ddl/copr/copr_ctx_test.go +++ b/pkg/ddl/copr/copr_ctx_test.go @@ -18,11 +18,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/ddl/db_cache_test.go b/pkg/ddl/db_cache_test.go similarity index 94% rename from ddl/db_cache_test.go rename to pkg/ddl/db_cache_test.go index a16523df4174a..4c648d5f1ed3f 100644 --- a/ddl/db_cache_test.go +++ b/pkg/ddl/db_cache_test.go @@ -18,14 +18,14 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/sem" "github.com/stretchr/testify/require" ) diff --git a/ddl/db_change_failpoints_test.go b/pkg/ddl/db_change_failpoints_test.go similarity index 86% rename from ddl/db_change_failpoints_test.go rename to pkg/ddl/db_change_failpoints_test.go index 7c1bfa4a9ec3a..434accce08496 100644 --- a/ddl/db_change_failpoints_test.go +++ b/pkg/ddl/db_change_failpoints_test.go @@ -22,23 +22,23 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/gcutil" + "github.com/pingcap/tidb/pkg/ddl" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/gcutil" "github.com/stretchr/testify/require" ) // TestModifyColumnTypeArgs test job raw args won't be updated when error occurs in `updateVersionAndTableInfo`. func TestModifyColumnTypeArgs(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr", `return(2)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr", `return(2)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr")) }() store := testkit.CreateMockStore(t) @@ -81,9 +81,9 @@ func TestModifyColumnTypeArgs(t *testing.T) { } func TestParallelUpdateTableReplica(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() store, dom := testkit.CreateMockStoreAndDomain(t) @@ -119,9 +119,9 @@ func TestParallelUpdateTableReplica(t *testing.T) { // TestParallelFlashbackTable tests parallel flashback table. func TestParallelFlashbackTable(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) if originGC { ddlutil.EmulatorGCEnable() } else { diff --git a/ddl/db_change_test.go b/pkg/ddl/db_change_test.go similarity index 99% rename from ddl/db_change_test.go rename to pkg/ddl/db_change_test.go index f4cd50d93e486..d2a02d1960fe2 100644 --- a/ddl/db_change_test.go +++ b/pkg/ddl/db_change_test.go @@ -23,23 +23,23 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/ddl/db_integration_test.go b/pkg/ddl/db_integration_test.go similarity index 99% rename from ddl/db_integration_test.go rename to pkg/ddl/db_integration_test.go index 77f85a1f25dd9..5fcedeb4e2384 100644 --- a/ddl/db_integration_test.go +++ b/pkg/ddl/db_integration_test.go @@ -26,32 +26,32 @@ import ( "time" "github.com/pingcap/errors" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mock" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/ddl/db_rename_test.go b/pkg/ddl/db_rename_test.go similarity index 97% rename from ddl/db_rename_test.go rename to pkg/ddl/db_rename_test.go index 8cf8fbe0c9815..2d8afc38a21c4 100644 --- a/ddl/db_rename_test.go +++ b/pkg/ddl/db_rename_test.go @@ -18,12 +18,12 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/ddl/db_table_test.go b/pkg/ddl/db_table_test.go similarity index 97% rename from ddl/db_table_test.go rename to pkg/ddl/db_table_test.go index fd918ed370771..800fc54704219 100644 --- a/ddl/db_table_test.go +++ b/pkg/ddl/db_table_test.go @@ -24,27 +24,27 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - testddlutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + testddlutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/db_test.go b/pkg/ddl/db_test.go new file mode 100644 index 0000000000000..313f0f6489f48 --- /dev/null +++ b/pkg/ddl/db_test.go @@ -0,0 +1,1100 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "math" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + parsertypes "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" +) + +const ( + // waitForCleanDataRound indicates how many times should we check data is cleaned or not. + waitForCleanDataRound = 150 + // waitForCleanDataInterval is a min duration between 2 check for data clean. + waitForCleanDataInterval = time.Millisecond * 100 +) + +const defaultBatchSize = 1024 + +const dbTestLease = 600 * time.Millisecond + +func TestGetTimeZone(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + testCases := []struct { + tzSQL string + tzStr string + tzName string + offset int + err string + }{ + {"set time_zone = '+00:00'", "", "", 0, ""}, + {"set time_zone = '-00:00'", "", "", 0, ""}, + {"set time_zone = 'UTC'", "UTC", "UTC", 0, ""}, + {"set time_zone = '+05:00'", "", "", 18000, ""}, + {"set time_zone = '-08:00'", "", "", -28800, ""}, + {"set time_zone = '+08:00'", "", "", 28800, ""}, + {"set time_zone = 'Asia/Shanghai'", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, + {"set time_zone = 'SYSTEM'", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, + {"set time_zone = DEFAULT", "Asia/Shanghai", "Asia/Shanghai", 0, ""}, + {"set time_zone = 'GMT'", "GMT", "GMT", 0, ""}, + {"set time_zone = 'GMT+1'", "GMT", "GMT", 0, "[variable:1298]Unknown or incorrect time zone: 'GMT+1'"}, + {"set time_zone = 'Etc/GMT+12'", "Etc/GMT+12", "Etc/GMT+12", 0, ""}, + {"set time_zone = 'Etc/GMT-12'", "Etc/GMT-12", "Etc/GMT-12", 0, ""}, + {"set time_zone = 'EST'", "EST", "EST", 0, ""}, + {"set time_zone = 'Australia/Lord_Howe'", "Australia/Lord_Howe", "Australia/Lord_Howe", 0, ""}, + } + for _, tc := range testCases { + if tc.err != "" { + tk.MustGetErrMsg(tc.tzSQL, tc.err) + } else { + tk.MustExec(tc.tzSQL) + } + require.Equal(t, tc.tzStr, tk.Session().GetSessionVars().TimeZone.String(), fmt.Sprintf("sql: %s", tc.tzSQL)) + tz, offset := ddlutil.GetTimeZone(tk.Session()) + require.Equal(t, tz, tc.tzName, fmt.Sprintf("sql: %s, offset: %d", tc.tzSQL, offset)) + require.Equal(t, offset, tc.offset, fmt.Sprintf("sql: %s", tc.tzSQL)) + } +} + +func TestIssue22819(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("set global tidb_enable_metadata_lock=0") + tk1.MustExec("use test;") + tk1.MustExec("create table t1 (v int) partition by hash (v) partitions 2") + tk1.MustExec("insert into t1 values (1)") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test;") + tk1.MustExec("begin") + tk1.MustExec("update t1 set v = 2 where v = 1") + + tk2.MustExec("alter table t1 truncate partition p0") + + err := tk1.ExecToErr("commit") + require.Error(t, err) + require.Regexp(t, ".*8028.*Information schema is changed during the execution of the statement.*", err.Error()) +} + +func TestIssue22307(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values(1, 1);") + + hook := &callback.TestDDLCallback{Do: dom} + var checkErr1, checkErr2 error + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateWriteOnly { + return + } + _, checkErr1 = tk.Exec("update t set a = 3 where b = 1;") + _, checkErr2 = tk.Exec("update t set a = 3 order by b;") + } + dom.DDL().SetHook(hook) + done := make(chan error, 1) + // test transaction on add column. + go backgroundExec(store, "test", "alter table t drop column b;", done) + err := <-done + require.NoError(t, err) + require.EqualError(t, checkErr1, "[planner:1054]Unknown column 'b' in 'where clause'") + require.EqualError(t, checkErr2, "[planner:1054]Unknown column 'b' in 'order clause'") +} + +func TestIssue23473(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_23473;") + tk.MustExec("create table t_23473 (k int primary key, v int)") + tk.MustExec("alter table t_23473 change column k k bigint") + + tbl := external.GetTableByName(t, tk, "test", "t_23473") + require.True(t, mysql.HasNoDefaultValueFlag(tbl.Cols()[0].GetFlag())) +} + +func TestAutoConvertBlobTypeByLength(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + sql := fmt.Sprintf("create table t0(c0 Blob(%d), c1 Blob(%d), c2 Blob(%d), c3 Blob(%d))", + 255-1, 65535-1, 16777215-1, 4294967295-1) + tk.MustExec(sql) + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t0' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + + tbl, exist := dom.InfoSchema().TableByID(tableID) + require.True(t, exist) + + require.Equal(t, tbl.Cols()[0].GetType(), mysql.TypeTinyBlob) + require.Equal(t, tbl.Cols()[0].GetFlen(), 255) + require.Equal(t, tbl.Cols()[1].GetType(), mysql.TypeBlob) + require.Equal(t, tbl.Cols()[1].GetFlen(), 65535) + require.Equal(t, tbl.Cols()[2].GetType(), mysql.TypeMediumBlob) + require.Equal(t, tbl.Cols()[2].GetFlen(), 16777215) + require.Equal(t, tbl.Cols()[3].GetType(), mysql.TypeLongBlob) + require.Equal(t, tbl.Cols()[3].GetFlen(), 4294967295) +} + +func TestAddExpressionIndexRollback(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int, c3 int, unique key(c1))") + tk.MustExec("insert into t1 values (20, 20, 20), (40, 40, 40), (80, 80, 80), (160, 160, 160);") + + var checkErr error + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + + d := dom.DDL() + hook := &callback.TestDDLCallback{Do: dom} + var currJob *model.Job + ctx := mock.NewContext() + ctx.Store = store + times := 0 + onJobUpdatedExportedFunc := func(job *model.Job) { + if checkErr != nil { + return + } + switch job.SchemaState { + case model.StateDeleteOnly: + _, checkErr = tk1.Exec("insert into t1 values (6, 3, 3) on duplicate key update c1 = 10") + if checkErr == nil { + _, checkErr = tk1.Exec("update t1 set c1 = 7 where c2=6;") + } + if checkErr == nil { + _, checkErr = tk1.Exec("delete from t1 where c1 = 40;") + } + case model.StateWriteOnly: + _, checkErr = tk1.Exec("insert into t1 values (2, 2, 2)") + if checkErr == nil { + _, checkErr = tk1.Exec("update t1 set c1 = 3 where c2 = 80") + } + case model.StateWriteReorganization: + if checkErr == nil && job.SchemaState == model.StateWriteReorganization && times == 0 { + _, checkErr = tk1.Exec("insert into t1 values (4, 4, 4)") + if checkErr != nil { + return + } + _, checkErr = tk1.Exec("update t1 set c1 = 5 where c2 = 80") + if checkErr != nil { + return + } + currJob = job + times++ + } + } + } + hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + d.SetHook(hook) + + tk.MustGetErrMsg("alter table t1 add index expr_idx ((pow(c1, c2)));", "[types:1690]DOUBLE value is out of range in 'pow(160, 160)'") + require.NoError(t, checkErr) + tk.MustQuery("select * from t1 order by c1;").Check(testkit.Rows("2 2 2", "4 4 4", "5 80 80", "10 3 3", "20 20 20", "160 160 160")) + + // Check whether the reorg information is cleaned up. + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + element, start, end, physicalID, err := ddl.NewReorgHandlerForTest(testkit.NewTestKit(t, store).Session()).GetDDLReorgHandle(currJob) + require.True(t, meta.ErrDDLReorgElementNotExist.Equal(err)) + require.Nil(t, element) + require.Nil(t, start) + require.Nil(t, end) + require.Equal(t, int64(0), physicalID) +} + +func TestDropTableOnTiKVDiskFull(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table test_disk_full_drop_table(a int);") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcTiKVAllowedOnAlmostFull")) + }() + tk.MustExec("drop table test_disk_full_drop_table;") +} + +func TestRebaseAutoID(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("drop database if exists tidb;") + tk.MustExec("create database tidb;") + tk.MustExec("use tidb;") + tk.MustExec("create table tidb.test (a int auto_increment primary key, b int);") + tk.MustExec("insert tidb.test values (null, 1);") + tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1")) + tk.MustExec("alter table tidb.test auto_increment = 6000;") + tk.MustExec("insert tidb.test values (null, 1);") + tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1")) + tk.MustExec("alter table tidb.test auto_increment = 5;") + tk.MustExec("insert tidb.test values (null, 1);") + tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1", "11000 1")) + + // Current range for table test is [11000, 15999]. + // Though it does not have a tuple "a = 15999", its global next auto increment id should be 16000. + // Anyway it is not compatible with MySQL. + tk.MustExec("alter table tidb.test auto_increment = 12000;") + tk.MustExec("insert tidb.test values (null, 1);") + tk.MustQuery("select * from tidb.test").Check(testkit.Rows("1 1", "6000 1", "11000 1", "16000 1")) + + tk.MustExec("create table tidb.test2 (a int);") + tk.MustGetErrCode("alter table tidb.test2 add column b int auto_increment key, auto_increment=10;", errno.ErrUnsupportedDDLOperation) +} + +func TestProcessColumnFlags(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // check `processColumnFlags()` + tk.MustExec("create table t(a year(4) comment 'xxx', b year, c bit)") + defer tk.MustExec("drop table t;") + + check := func(n string, f func(uint) bool) { + tbl := external.GetTableByName(t, tk, "test", "t") + for _, col := range tbl.Cols() { + if strings.EqualFold(col.Name.L, n) { + require.True(t, f(col.GetFlag())) + break + } + } + } + + yearcheck := func(f uint) bool { + return mysql.HasUnsignedFlag(f) && mysql.HasZerofillFlag(f) && !mysql.HasBinaryFlag(f) + } + + tk.MustExec("alter table t modify a year(4)") + check("a", yearcheck) + + tk.MustExec("alter table t modify a year(4) unsigned") + check("a", yearcheck) + + tk.MustExec("alter table t modify a year(4) zerofill") + + tk.MustExec("alter table t modify b year") + check("b", yearcheck) + + tk.MustExec("alter table t modify c bit") + check("c", func(f uint) bool { + return mysql.HasUnsignedFlag(f) && !mysql.HasBinaryFlag(f) + }) +} + +func TestForbidCacheTableForSystemTable(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + sysTables := make([]string, 0, 24) + memOrSysDB := []string{"MySQL", "INFORMATION_SCHEMA", "PERFORMANCE_SCHEMA", "METRICS_SCHEMA"} + for _, db := range memOrSysDB { + tk.MustExec("use " + db) + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) + rows := tk.MustQuery("show tables").Rows() + for i := 0; i < len(rows); i++ { + sysTables = append(sysTables, rows[i][0].(string)) + } + for _, one := range sysTables { + err := tk.ExecToErr(fmt.Sprintf("alter table `%s` cache", one)) + if db == "MySQL" { + if one == "tidb_mdl_view" { + require.EqualError(t, err, "[ddl:1347]'MySQL.tidb_mdl_view' is not BASE TABLE") + } else { + require.EqualError(t, err, "[ddl:8200]ALTER table cache for tables in system database is currently unsupported") + } + } else { + require.EqualError(t, err, fmt.Sprintf("[planner:1142]ALTER command denied to user 'root'@'%%' for table '%s'", strings.ToLower(one))) + } + } + sysTables = sysTables[:0] + } +} + +func TestAlterShardRowIDBits(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + // Test alter shard_row_id_bits + tk.MustExec("create table t1 (a int) shard_row_id_bits = 5") + tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) + tk.MustExec("insert into t1 set a=1;") + + // Test increase shard_row_id_bits failed by overflow global auto ID. + tk.MustGetErrMsg("alter table t1 SHARD_ROW_ID_BITS = 10;", "[autoid:1467]shard_row_id_bits 10 will cause next global auto ID 72057594037932936 overflow") + + // Test reduce shard_row_id_bits will be ok. + tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 3;") + checkShardRowID := func(maxShardRowIDBits, shardRowIDBits uint64) { + tbl := external.GetTableByName(t, tk, "test", "t1") + require.True(t, tbl.Meta().MaxShardRowIDBits == maxShardRowIDBits) + require.True(t, tbl.Meta().ShardRowIDBits == shardRowIDBits) + } + checkShardRowID(5, 3) + + // Test reduce shard_row_id_bits but calculate overflow should use the max record shard_row_id_bits. + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int) shard_row_id_bits = 10") + tk.MustExec("alter table t1 SHARD_ROW_ID_BITS = 5;") + checkShardRowID(10, 5) + tk.MustExec(fmt.Sprintf("alter table t1 auto_increment = %d;", 1<<56)) + tk.MustGetErrMsg("insert into t1 set a=1;", "[autoid:1467]Failed to read auto-increment value from storage engine") +} + +func TestDDLJobErrorCount(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists ddl_error_table, new_ddl_error_table") + tk.MustExec("create table ddl_error_table(a int)") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockErrEntrySizeTooLarge", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockErrEntrySizeTooLarge")) + }() + + var jobID int64 + hook := &callback.TestDDLCallback{} + onJobUpdatedExportedFunc := func(job *model.Job) { + jobID = job.ID + } + hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + originHook := dom.DDL().GetHook() + dom.DDL().SetHook(hook) + defer dom.DDL().SetHook(originHook) + + tk.MustGetErrCode("rename table ddl_error_table to new_ddl_error_table", errno.ErrEntryTooLarge) + + historyJob, err := ddl.GetHistoryJobByID(tk.Session(), jobID) + require.NoError(t, err) + require.NotNil(t, historyJob) + require.Equal(t, int64(1), historyJob.ErrorCount) + require.True(t, kv.ErrEntryTooLarge.Equal(historyJob.Error)) + tk.MustQuery("select * from ddl_error_table;").Check(testkit.Rows()) +} + +// TestAddIndexFailOnCaseWhenCanExit is used to close #19325. +func TestAddIndexFailOnCaseWhenCanExit(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockCaseWhenParseFailure", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockCaseWhenParseFailure")) + }() + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + originalVal := variable.GetDDLErrorCountLimit() + tk.MustExec("set @@global.tidb_ddl_error_count_limit = 1") + defer tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_error_count_limit = %d", originalVal)) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1, 1)") + tk.MustGetErrMsg("alter table t add index idx(b)", "[ddl:-1]job.ErrCount:0, mock unknown type: ast.whenClause.") + tk.MustExec("drop table if exists t") +} + +func TestCreateTableWithIntegerLengthWarning(t *testing.T) { + // Inject the strict-integer-display-width variable in parser directly. + parsertypes.TiDBStrictIntegerDisplayWidth = true + defer func() { parsertypes.TiDBStrictIntegerDisplayWidth = false }() + store := testkit.CreateMockStoreWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + + tk.MustExec("create table t(a tinyint(1))") + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a smallint(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a mediumint(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a bigint(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a integer(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int1(1))") // Note that int1(1) is tinyint(1) which is boolean-ish + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int2(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int3(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int4(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int8(2))") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1681 Integer display width is deprecated and will be removed in a future release.")) + + tk.MustExec("drop table if exists t") +} +func TestShowCountWarningsOrErrors(t *testing.T) { + // Inject the strict-integer-display-width variable in parser directly. + parsertypes.TiDBStrictIntegerDisplayWidth = true + defer func() { parsertypes.TiDBStrictIntegerDisplayWidth = false }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // test sql run work + tk.MustExec("show count(*) warnings") + tk.MustExec("show count(*) errors") + + // test count warnings + tk.MustExec("drop table if exists t1,t2,t3") + // Warning: Integer display width is deprecated and will be removed in a future release. + tk.MustExec("create table t(a int8(2));" + + "create table t1(a int4(2));" + + "create table t2(a int4(2));") + tk.MustQuery("show count(*) warnings").Check(tk.MustQuery("select @@session.warning_count").Rows()) + + // test count errors + tk.MustExec("drop table if exists show_errors") + tk.MustExec("create table show_errors (a int)") + // Error: Table exist + _, _ = tk.Exec("create table show_errors (a int)") + tk.MustQuery("show count(*) errors").Check(tk.MustQuery("select @@session.error_count").Rows()) +} + +// Close issue #24172. +// See https://github.com/pingcap/tidb/issues/24172 +func TestCancelJobWriteConflict(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + + tk1.MustExec("create table t(id int)") + + var cancelErr error + var rs []sqlexec.RecordSet + hook := &callback.TestDDLCallback{Do: dom} + d := dom.DDL() + originalHook := d.GetHook() + d.SetHook(hook) + defer d.SetHook(originalHook) + + // Test when cancelling cannot be retried and adding index succeeds. + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/mockCommitErrorInNewTxn", `return("no_retry")`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/kv/mockCommitErrorInNewTxn")) + }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFailedCommandOnConcurencyDDL", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFailedCommandOnConcurencyDDL")) + }() + + stmt := fmt.Sprintf("admin cancel ddl jobs %d", job.ID) + rs, cancelErr = tk2.Session().Execute(context.Background(), stmt) + } + } + tk1.MustExec("alter table t add index (id)") + require.EqualError(t, cancelErr, "mock failed admin command on ddl jobs") + + // Test when cancelling is retried only once and adding index is cancelled in the end. + var jobID int64 + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { + jobID = job.ID + stmt := fmt.Sprintf("admin cancel ddl jobs %d", job.ID) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/mockCommitErrorInNewTxn", `return("retry_once")`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/kv/mockCommitErrorInNewTxn")) + }() + rs, cancelErr = tk2.Session().Execute(context.Background(), stmt) + } + } + tk1.MustGetErrCode("alter table t add index (id)", errno.ErrCancelledDDLJob) + require.NoError(t, cancelErr) + result := tk2.ResultSetToResultWithCtx(context.Background(), rs[0], "cancel ddl job fails") + result.Check(testkit.Rows(fmt.Sprintf("%d successful", jobID))) +} + +func TestTxnSavepointWithDDL(t *testing.T) { + store, _ := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("set global tidb_enable_metadata_lock=0") + tk2.MustExec("use test;") + + prepareFn := func() { + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (c1 int primary key, c2 int)") + tk.MustExec("create table t2 (c1 int primary key, c2 int)") + } + prepareFn() + + tk.MustExec("begin pessimistic") + tk.MustExec("savepoint s1") + tk.MustExec("insert t1 values (1, 11)") + tk.MustExec("rollback to s1") + tk2.MustExec("alter table t1 add index idx2(c2)") + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustExec("admin check table t1") + + tk.MustExec("begin pessimistic") + tk.MustExec("savepoint s1") + tk.MustExec("insert t1 values (1, 11)") + tk.MustExec("savepoint s2") + tk.MustExec("insert t2 values (1, 11)") + tk.MustExec("rollback to s2") + tk2.MustExec("alter table t2 add index idx2(c2)") + tk.MustExec("commit") + tk.MustQuery("select * from t2").Check(testkit.Rows()) + tk.MustExec("admin check table t1, t2") + + prepareFn() + tk.MustExec("truncate table t1") + tk.MustExec("begin pessimistic") + tk.MustExec("savepoint s1") + tk.MustExec("insert t1 values (1, 11)") + tk.MustExec("savepoint s2") + tk.MustExec("insert t2 values (1, 11)") + tk.MustExec("rollback to s2") + tk2.MustExec("alter table t1 add index idx2(c2)") + tk2.MustExec("alter table t2 add index idx2(c2)") + err := tk.ExecToErr("commit") + require.Error(t, err) + require.Regexp(t, ".*8028.*Information schema is changed during the execution of the statement.*", err.Error()) + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustExec("admin check table t1, t2") +} + +func TestSnapshotVersion(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + + dd := dom.DDL() + ddl.DisableTiFlashPoll(dd) + require.Equal(t, dbTestLease, dd.GetLease()) + + snapTS := oracle.GoTimeToTS(time.Now()) + tk.MustExec("create database test2") + tk.MustExec("use test2") + tk.MustExec("create table t(a int)") + + is := dom.InfoSchema() + require.NotNil(t, is) + + // For updating the self schema version. + goCtx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) + err := dd.SchemaSyncer().OwnerCheckAllVersions(goCtx, 0, is.SchemaMetaVersion()) + cancel() + require.NoError(t, err) + + snapIs, err := dom.GetSnapshotInfoSchema(snapTS) + require.NotNil(t, snapIs) + require.NoError(t, err) + + // Make sure that the self schema version doesn't be changed. + goCtx, cancel = context.WithTimeout(context.Background(), 100*time.Millisecond) + err = dd.SchemaSyncer().OwnerCheckAllVersions(goCtx, 0, is.SchemaMetaVersion()) + cancel() + require.NoError(t, err) + + // for GetSnapshotInfoSchema + currSnapTS := oracle.GoTimeToTS(time.Now()) + currSnapIs, err := dom.GetSnapshotInfoSchema(currSnapTS) + require.NoError(t, err) + require.NotNil(t, currSnapTS) + require.Equal(t, is.SchemaMetaVersion(), currSnapIs.SchemaMetaVersion()) + + // for GetSnapshotMeta + dbInfo, ok := currSnapIs.SchemaByName(model.NewCIStr("test2")) + require.True(t, ok) + + tbl, err := currSnapIs.TableByName(model.NewCIStr("test2"), model.NewCIStr("t")) + require.NoError(t, err) + + m, err := dom.GetSnapshotMeta(snapTS) + require.NoError(t, err) + + tblInfo1, err := m.GetTable(dbInfo.ID, tbl.Meta().ID) + require.True(t, meta.ErrDBNotExists.Equal(err)) + require.Nil(t, tblInfo1) + + m, err = dom.GetSnapshotMeta(currSnapTS) + require.NoError(t, err) + + tblInfo2, err := m.GetTable(dbInfo.ID, tbl.Meta().ID) + require.NoError(t, err) + require.Equal(t, tblInfo2, tbl.Meta()) +} + +func TestSchemaValidator(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + tk := testkit.NewTestKit(t, store) + + dd := dom.DDL() + ddl.DisableTiFlashPoll(dd) + require.Equal(t, dbTestLease, dd.GetLease()) + + tk.MustExec("create table test.t(a int)") + + err := dom.Reload() + require.NoError(t, err) + schemaVer := dom.InfoSchema().SchemaMetaVersion() + ver, err := store.CurrentVersion(kv.GlobalTxnScope) + require.NoError(t, err) + + ts := ver.Ver + _, res := dom.SchemaValidator.Check(ts, schemaVer, nil, true) + require.Equal(t, domain.ResultSucc, res) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed", `return(true)`)) + + err = dom.Reload() + require.Error(t, err) + _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) + require.Equal(t, domain.ResultSucc, res) + time.Sleep(dbTestLease) + + ver, err = store.CurrentVersion(kv.GlobalTxnScope) + require.NoError(t, err) + ts = ver.Ver + _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) + require.Equal(t, domain.ResultUnknown, res) + + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed")) + err = dom.Reload() + require.NoError(t, err) + + _, res = dom.SchemaValidator.Check(ts, schemaVer, nil, true) + require.Equal(t, domain.ResultSucc, res) + + // For schema check, it tests for getting the result of "ResultUnknown". + is := dom.InfoSchema() + schemaChecker := domain.NewSchemaChecker(dom, is.SchemaMetaVersion(), nil, true) + // Make sure it will retry one time and doesn't take a long time. + domain.SchemaOutOfDateRetryTimes.Store(1) + domain.SchemaOutOfDateRetryInterval.Store(time.Millisecond * 1) + dom.SchemaValidator.Stop() + _, err = schemaChecker.Check(uint64(123456)) + require.EqualError(t, err, domain.ErrInfoSchemaExpired.Error()) +} + +func TestLogAndShowSlowLog(t *testing.T) { + _, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "aaa", Duration: time.Second, Internal: true}) + dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "bbb", Duration: 3 * time.Second, SessAlias: "alias1"}) + dom.LogSlowQuery(&domain.SlowQueryInfo{SQL: "ccc", Duration: 2 * time.Second}) + // Collecting slow queries is asynchronous, wait a while to ensure it's done. + time.Sleep(5 * time.Millisecond) + + result := dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2}) + require.Len(t, result, 2) + require.Equal(t, "bbb", result[0].SQL) + require.Equal(t, "alias1", result[0].SessAlias) + require.Equal(t, 3*time.Second, result[0].Duration) + require.Equal(t, "ccc", result[1].SQL) + require.Equal(t, 2*time.Second, result[1].Duration) + require.Empty(t, result[1].SessAlias) + + result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 2, Kind: ast.ShowSlowKindInternal}) + require.Len(t, result, 1) + require.Equal(t, "aaa", result[0].SQL) + require.Equal(t, time.Second, result[0].Duration) + require.True(t, result[0].Internal) + + result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowTop, Count: 4, Kind: ast.ShowSlowKindAll}) + require.Len(t, result, 3) + require.Equal(t, "bbb", result[0].SQL) + require.Equal(t, 3*time.Second, result[0].Duration) + require.Equal(t, "alias1", result[0].SessAlias) + require.Equal(t, "ccc", result[1].SQL) + require.Equal(t, 2*time.Second, result[1].Duration) + require.Empty(t, result[1].SessAlias) + require.Equal(t, "aaa", result[2].SQL) + require.Equal(t, time.Second, result[2].Duration) + require.True(t, result[2].Internal) + require.Empty(t, result[2].SessAlias) + + result = dom.ShowSlowQuery(&ast.ShowSlow{Tp: ast.ShowSlowRecent, Count: 2}) + require.Len(t, result, 2) + require.Equal(t, "ccc", result[0].SQL) + require.Equal(t, 2*time.Second, result[0].Duration) + require.Empty(t, result[0].SessAlias) + require.Equal(t, "bbb", result[1].SQL) + require.Equal(t, 3*time.Second, result[1].Duration) + require.Equal(t, "alias1", result[1].SessAlias) +} + +func TestReportingMinStartTimestamp(t *testing.T) { + _, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, dbTestLease) + + infoSyncer := dom.InfoSyncer() + sm := &testkit.MockSessionManager{ + PS: make([]*util.ProcessInfo, 0), + } + infoSyncer.SetSessionManager(sm) + beforeTS := oracle.GoTimeToTS(time.Now()) + infoSyncer.ReportMinStartTS(dom.Store()) + afterTS := oracle.GoTimeToTS(time.Now()) + require.False(t, infoSyncer.GetMinStartTS() > beforeTS && infoSyncer.GetMinStartTS() < afterTS) + + now := time.Now() + validTS := oracle.GoTimeToLowerLimitStartTS(now.Add(time.Minute), tikv.MaxTxnTimeUse) + lowerLimit := oracle.GoTimeToLowerLimitStartTS(now, tikv.MaxTxnTimeUse) + sm.PS = []*util.ProcessInfo{ + {CurTxnStartTS: 0}, + {CurTxnStartTS: math.MaxUint64}, + {CurTxnStartTS: lowerLimit}, + {CurTxnStartTS: validTS}, + } + infoSyncer.SetSessionManager(sm) + infoSyncer.ReportMinStartTS(dom.Store()) + require.Equal(t, validTS, infoSyncer.GetMinStartTS()) +} + +// for issue #34931 +func TestBuildMaxLengthIndexWithNonRestrictedSqlMode(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + maxIndexLength := config.GetGlobalConfig().MaxIndexLength + + tt := []struct { + ColType string + SpecifiedColLen bool + SpecifiedIndexLen bool + }{ + { + "text", + false, + true, + }, + { + "blob", + false, + true, + }, + { + "varchar", + true, + false, + }, + { + "varbinary", + true, + false, + }, + } + + sqlTemplate := "create table %s (id int, name %s, age int, %s index(name%s%s)) charset=%s;" + // test character strings for varchar and text + for _, tc := range tt { + for _, cs := range charset.CharacterSetInfos { + tableName := fmt.Sprintf("t_%s", cs.Name) + tk.MustExec(fmt.Sprintf("drop table if exists %s", tableName)) + tk.MustExec("set @@sql_mode=default") + + // test in strict sql mode + maxLen := cs.Maxlen + if tc.ColType == "varbinary" || tc.ColType == "blob" { + maxLen = 1 + } + expectKeyLength := maxIndexLength / maxLen + length := 2 * expectKeyLength + + indexLen := "" + // specify index length for text type + if tc.SpecifiedIndexLen { + indexLen = fmt.Sprintf("(%d)", length) + } + + col := tc.ColType + // specify column length for varchar type + if tc.SpecifiedColLen { + col += fmt.Sprintf("(%d)", length) + } + sql := fmt.Sprintf(sqlTemplate, + tableName, col, "", indexLen, "", cs.Name) + tk.MustGetErrCode(sql, errno.ErrTooLongKey) + + tk.MustExec("set @@sql_mode=''") + + err := tk.ExecToErr(sql) + require.NoErrorf(t, err, "exec sql '%s' failed", sql) + + require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + + warnErr := tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err + tErr := errors.Cause(warnErr).(*terror.Error) + sqlErr := terror.ToSQLError(tErr) + require.Equal(t, errno.ErrTooLongKey, int(sqlErr.Code)) + + if cs.Name == charset.CharsetBin { + if tc.ColType == "varchar" || tc.ColType == "varbinary" { + col = fmt.Sprintf("varbinary(%d)", length) + } else { + col = "blob" + } + } + rows := fmt.Sprintf("%s CREATE TABLE `%s` (\n `id` int(11) DEFAULT NULL,\n `name` %s DEFAULT NULL,\n `age` int(11) DEFAULT NULL,\n KEY `name` (`name`(%d))\n) ENGINE=InnoDB DEFAULT CHARSET=%s", + tableName, tableName, col, expectKeyLength, cs.Name) + // add collation for binary charset + if cs.Name != charset.CharsetBin { + rows += fmt.Sprintf(" COLLATE=%s", cs.DefaultCollation) + } + + tk.MustQuery(fmt.Sprintf("show create table %s", tableName)).Check(testkit.Rows(rows)) + + ukTable := fmt.Sprintf("t_%s_uk", cs.Name) + mkTable := fmt.Sprintf("t_%s_mk", cs.Name) + tk.MustExec(fmt.Sprintf("drop table if exists %s", ukTable)) + tk.MustExec(fmt.Sprintf("drop table if exists %s", mkTable)) + + // For a unique index, an error occurs regardless of SQL mode because reducing + //the index length might enable insertion of non-unique entries that do not meet + //the specified uniqueness requirement. + sql = fmt.Sprintf(sqlTemplate, ukTable, col, "unique", indexLen, "", cs.Name) + tk.MustGetErrCode(sql, errno.ErrTooLongKey) + + // The multiple column index in which the length sum exceeds the maximum size + // will return an error instead produce a warning in strict sql mode. + indexLen = fmt.Sprintf("(%d)", expectKeyLength) + sql = fmt.Sprintf(sqlTemplate, mkTable, col, "", indexLen, ", age", cs.Name) + tk.MustGetErrCode(sql, errno.ErrTooLongKey) + } + } +} + +func TestTiDBDownBeforeUpdateGlobalVersion(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDownBeforeUpdateGlobalVersion", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkDownBeforeUpdateGlobalVersion", `return(true)`)) + tk.MustExec("alter table t add column b int") + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDownBeforeUpdateGlobalVersion")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkDownBeforeUpdateGlobalVersion")) +} + +func TestDDLBlockedCreateView(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + + hook := &callback.TestDDLCallback{Do: dom} + first := true + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateWriteOnly { + return + } + if !first { + return + } + first = false + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("create view v as select * from t") + } + dom.DDL().SetHook(hook) + tk.MustExec("alter table t modify column a char(10)") +} + +func TestHashPartitionAddColumn(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int) partition by hash(a) partitions 4") + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateWriteOnly { + return + } + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("delete from t") + } + dom.DDL().SetHook(hook) + tk.MustExec("alter table t add column c int") +} + +func TestSetInvalidDefaultValueAfterModifyColumn(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + + var wg sync.WaitGroup + var checkErr error + one := false + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateDeleteOnly { + return + } + if one { + return + } + one = true + wg.Add(1) + go func() { + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + _, checkErr = tk2.Exec("alter table t alter column a set default 1") + wg.Done() + }() + } + dom.DDL().SetHook(hook) + tk.MustExec("alter table t modify column a text(100)") + wg.Wait() + require.EqualError(t, checkErr, "[ddl:1101]BLOB/TEXT/JSON column 'a' can't have a default value") +} + +func TestMDLTruncateTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk3 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int);") + tk.MustExec("begin") + tk.MustExec("select * from t for update") + + var wg sync.WaitGroup + + hook := &callback.TestDDLCallback{Do: dom} + wg.Add(2) + var timetk2 time.Time + var timetk3 time.Time + + one := false + f := func(job *model.Job) { + if one { + return + } + one = true + go func() { + tk3.MustExec("truncate table test.t") + timetk3 = time.Now() + wg.Done() + }() + } + + hook.OnJobUpdatedExported.Store(&f) + dom.DDL().SetHook(hook) + + go func() { + tk2.MustExec("truncate table test.t") + timetk2 = time.Now() + wg.Done() + }() + + time.Sleep(2 * time.Second) + timeMain := time.Now() + tk.MustExec("commit") + wg.Wait() + require.True(t, timetk2.After(timeMain)) + require.True(t, timetk3.After(timeMain)) +} diff --git a/pkg/ddl/ddl.go b/pkg/ddl/ddl.go new file mode 100644 index 0000000000000..0a4246f7b751f --- /dev/null +++ b/pkg/ddl/ddl.go @@ -0,0 +1,1863 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package ddl + +import ( + "cmp" + "context" + "fmt" + "runtime" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/google/uuid" + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/table" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/syncutil" + "github.com/tikv/client-go/v2/tikvrpc" + clientv3 "go.etcd.io/etcd/client/v3" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +const ( + // currentVersion is for all new DDL jobs. + currentVersion = 1 + // DDLOwnerKey is the ddl owner path that is saved to etcd, and it's exported for testing. + DDLOwnerKey = "/tidb/ddl/fg/owner" + // addingDDLJobPrefix is the path prefix used to record the newly added DDL job, and it's saved to etcd. + addingDDLJobPrefix = "/tidb/ddl/add_ddl_job_" + ddlPrompt = "ddl" + + shardRowIDBitsMax = 15 + + batchAddingJobs = 10 + + reorgWorkerCnt = 10 + generalWorkerCnt = 1 + + // checkFlagIndexInJobArgs is the recoverCheckFlag index used in RecoverTable/RecoverSchema job arg list. + checkFlagIndexInJobArgs = 1 +) + +const ( + // The recoverCheckFlag is used to judge the gc work status when RecoverTable/RecoverSchema. + recoverCheckFlagNone int64 = iota + recoverCheckFlagEnableGC + recoverCheckFlagDisableGC +) + +// OnExist specifies what to do when a new object has a name collision. +type OnExist uint8 + +// AllocTableIDIf specifies whether to retain the old table ID. +// If this returns "false", then we would assume the table ID has been +// allocated before calling `CreateTableWithInfo` family. +type AllocTableIDIf func(*model.TableInfo) bool + +// CreateTableWithInfoConfig is the configuration of `CreateTableWithInfo`. +type CreateTableWithInfoConfig struct { + OnExist OnExist + ShouldAllocTableID AllocTableIDIf +} + +// CreateTableWithInfoConfigurier is the "diff" which can be applied to the +// CreateTableWithInfoConfig, currently implementations are "OnExist" and "AllocTableIDIf". +type CreateTableWithInfoConfigurier interface { + // Apply the change over the config. + Apply(*CreateTableWithInfoConfig) +} + +// GetCreateTableWithInfoConfig applies the series of configurier from default config +// and returns the final config. +func GetCreateTableWithInfoConfig(cs []CreateTableWithInfoConfigurier) CreateTableWithInfoConfig { + config := CreateTableWithInfoConfig{} + for _, c := range cs { + c.Apply(&config) + } + if config.ShouldAllocTableID == nil { + config.ShouldAllocTableID = func(*model.TableInfo) bool { return true } + } + return config +} + +// Apply implements Configurier. +func (o OnExist) Apply(c *CreateTableWithInfoConfig) { + c.OnExist = o +} + +// Apply implements Configurier. +func (a AllocTableIDIf) Apply(c *CreateTableWithInfoConfig) { + c.ShouldAllocTableID = a +} + +const ( + // OnExistError throws an error on name collision. + OnExistError OnExist = iota + // OnExistIgnore skips creating the new object. + OnExistIgnore + // OnExistReplace replaces the old object by the new object. This is only + // supported by VIEWs at the moment. For other object types, this is + // equivalent to OnExistError. + OnExistReplace + + jobRecordCapacity = 16 + jobOnceCapacity = 1000 +) + +var ( + // EnableSplitTableRegion is a flag to decide whether to split a new region for + // a newly created table. It takes effect only if the Storage supports split + // region. + EnableSplitTableRegion = uint32(0) +) + +// DDL is responsible for updating schema in data store and maintaining in-memory InfoSchema cache. +type DDL interface { + CreateSchema(ctx sessionctx.Context, stmt *ast.CreateDatabaseStmt) error + AlterSchema(sctx sessionctx.Context, stmt *ast.AlterDatabaseStmt) error + DropSchema(ctx sessionctx.Context, stmt *ast.DropDatabaseStmt) error + CreateTable(ctx sessionctx.Context, stmt *ast.CreateTableStmt) error + CreateView(ctx sessionctx.Context, stmt *ast.CreateViewStmt) error + DropTable(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) + RecoverTable(ctx sessionctx.Context, recoverInfo *RecoverInfo) (err error) + RecoverSchema(ctx sessionctx.Context, recoverSchemaInfo *RecoverSchemaInfo) error + DropView(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) + CreateIndex(ctx sessionctx.Context, stmt *ast.CreateIndexStmt) error + DropIndex(ctx sessionctx.Context, stmt *ast.DropIndexStmt) error + AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast.AlterTableStmt) error + TruncateTable(ctx sessionctx.Context, tableIdent ast.Ident) error + RenameTable(ctx sessionctx.Context, stmt *ast.RenameTableStmt) error + LockTables(ctx sessionctx.Context, stmt *ast.LockTablesStmt) error + UnlockTables(ctx sessionctx.Context, lockedTables []model.TableLockTpInfo) error + CleanupTableLock(ctx sessionctx.Context, tables []*ast.TableName) error + UpdateTableReplicaInfo(ctx sessionctx.Context, physicalID int64, available bool) error + RepairTable(ctx sessionctx.Context, createStmt *ast.CreateTableStmt) error + CreateSequence(ctx sessionctx.Context, stmt *ast.CreateSequenceStmt) error + DropSequence(ctx sessionctx.Context, stmt *ast.DropSequenceStmt) (err error) + AlterSequence(ctx sessionctx.Context, stmt *ast.AlterSequenceStmt) error + CreatePlacementPolicy(ctx sessionctx.Context, stmt *ast.CreatePlacementPolicyStmt) error + DropPlacementPolicy(ctx sessionctx.Context, stmt *ast.DropPlacementPolicyStmt) error + AlterPlacementPolicy(ctx sessionctx.Context, stmt *ast.AlterPlacementPolicyStmt) error + AddResourceGroup(ctx sessionctx.Context, stmt *ast.CreateResourceGroupStmt) error + AlterResourceGroup(ctx sessionctx.Context, stmt *ast.AlterResourceGroupStmt) error + DropResourceGroup(ctx sessionctx.Context, stmt *ast.DropResourceGroupStmt) error + FlashbackCluster(ctx sessionctx.Context, flashbackTS uint64) error + + // CreateSchemaWithInfo creates a database (schema) given its database info. + // + // WARNING: the DDL owns the `info` after calling this function, and will modify its fields + // in-place. If you want to keep using `info`, please call Clone() first. + CreateSchemaWithInfo( + ctx sessionctx.Context, + info *model.DBInfo, + onExist OnExist) error + + // CreateTableWithInfo creates a table, view or sequence given its table info. + // + // WARNING: the DDL owns the `info` after calling this function, and will modify its fields + // in-place. If you want to keep using `info`, please call Clone() first. + CreateTableWithInfo( + ctx sessionctx.Context, + schema model.CIStr, + info *model.TableInfo, + cs ...CreateTableWithInfoConfigurier) error + + // BatchCreateTableWithInfo is like CreateTableWithInfo, but can handle multiple tables. + BatchCreateTableWithInfo(ctx sessionctx.Context, + schema model.CIStr, + info []*model.TableInfo, + cs ...CreateTableWithInfoConfigurier) error + + // CreatePlacementPolicyWithInfo creates a placement policy + // + // WARNING: the DDL owns the `policy` after calling this function, and will modify its fields + // in-place. If you want to keep using `policy`, please call Clone() first. + CreatePlacementPolicyWithInfo(ctx sessionctx.Context, policy *model.PolicyInfo, onExist OnExist) error + + // Start campaigns the owner and starts workers. + // ctxPool is used for the worker's delRangeManager and creates sessions. + Start(ctxPool *pools.ResourcePool) error + // GetLease returns current schema lease time. + GetLease() time.Duration + // Stats returns the DDL statistics. + Stats(vars *variable.SessionVars) (map[string]interface{}, error) + // GetScope gets the status variables scope. + GetScope(status string) variable.ScopeFlag + // Stop stops DDL worker. + Stop() error + // RegisterStatsHandle registers statistics handle and its corresponding event channel for ddl. + RegisterStatsHandle(*handle.Handle) + // SchemaSyncer gets the schema syncer. + SchemaSyncer() syncer.SchemaSyncer + // StateSyncer gets the cluster state syncer. + StateSyncer() syncer.StateSyncer + // OwnerManager gets the owner manager. + OwnerManager() owner.Manager + // GetID gets the ddl ID. + GetID() string + // GetTableMaxHandle gets the max row ID of a normal table or a partition. + GetTableMaxHandle(ctx *JobContext, startTS uint64, tbl table.PhysicalTable) (kv.Handle, bool, error) + // SetBinlogClient sets the binlog client for DDL worker. It's exported for testing. + SetBinlogClient(*pumpcli.PumpsClient) + // GetHook gets the hook. It's exported for testing. + GetHook() Callback + // SetHook sets the hook. + SetHook(h Callback) + // GetInfoSchemaWithInterceptor gets the infoschema binding to d. It's exported for testing. + GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema + // DoDDLJob does the DDL job, it's exported for test. + DoDDLJob(ctx sessionctx.Context, job *model.Job) error +} + +type limitJobTask struct { + job *model.Job + err chan error + cacheErr error +} + +// ddl is used to handle the statements that define the structure or schema of the database. +type ddl struct { + m sync.RWMutex + wg tidbutil.WaitGroupWrapper // It's only used to deal with data race in restart_test. + limitJobCh chan *limitJobTask + + *ddlCtx + sessPool *sess.Pool + delRangeMgr delRangeManager + enableTiFlashPoll *atomicutil.Bool + // used in the concurrency ddl. + reorgWorkerPool *workerPool + generalDDLWorkerPool *workerPool + // get notification if any DDL coming. + ddlJobCh chan struct{} +} + +// waitSchemaSyncedController is to control whether to waitSchemaSynced or not. +type waitSchemaSyncedController struct { + mu sync.RWMutex + job map[int64]struct{} + + // Use to check if the DDL job is the first run on this owner. + onceMap map[int64]struct{} +} + +func newWaitSchemaSyncedController() *waitSchemaSyncedController { + return &waitSchemaSyncedController{ + job: make(map[int64]struct{}, jobRecordCapacity), + onceMap: make(map[int64]struct{}, jobOnceCapacity), + } +} + +func (w *waitSchemaSyncedController) registerSync(job *model.Job) { + w.mu.Lock() + defer w.mu.Unlock() + w.job[job.ID] = struct{}{} +} + +func (w *waitSchemaSyncedController) isSynced(job *model.Job) bool { + w.mu.RLock() + defer w.mu.RUnlock() + _, ok := w.job[job.ID] + return !ok +} + +func (w *waitSchemaSyncedController) synced(job *model.Job) { + w.mu.Lock() + defer w.mu.Unlock() + delete(w.job, job.ID) +} + +// maybeAlreadyRunOnce returns true means that the job may be the first run on this owner. +// Returns false means that the job must not be the first run on this owner. +func (w *waitSchemaSyncedController) maybeAlreadyRunOnce(id int64) bool { + w.mu.Lock() + defer w.mu.Unlock() + _, ok := w.onceMap[id] + return ok +} + +func (w *waitSchemaSyncedController) setAlreadyRunOnce(id int64) { + w.mu.Lock() + defer w.mu.Unlock() + if len(w.onceMap) > jobOnceCapacity { + // If the map is too large, we reset it. These jobs may need to check schema synced again, but it's ok. + w.onceMap = make(map[int64]struct{}, jobRecordCapacity) + } + w.onceMap[id] = struct{}{} +} + +// ddlCtx is the context when we use worker to handle DDL jobs. +type ddlCtx struct { + ctx context.Context + cancel context.CancelFunc + uuid string + store kv.Storage + ownerManager owner.Manager + schemaSyncer syncer.SchemaSyncer + stateSyncer syncer.StateSyncer + ddlJobDoneCh chan struct{} + ddlEventCh chan<- *util.Event + lease time.Duration // lease is schema lease. + binlogCli *pumpcli.PumpsClient // binlogCli is used for Binlog. + infoCache *infoschema.InfoCache + statsHandle *handle.Handle + tableLockCkr util.DeadTableLockChecker + etcdCli *clientv3.Client + // backfillJobCh gets notification if any backfill jobs coming. + backfillJobCh chan struct{} + + *waitSchemaSyncedController + *schemaVersionManager + // recording the running jobs. + runningJobs struct { + sync.RWMutex + ids map[int64]struct{} + } + // It holds the running DDL jobs ID. + runningJobIDs []string + // reorgCtx is used for reorganization. + reorgCtx reorgContexts + // backfillCtx is used for backfill workers. + backfillCtx struct { + syncutil.RWMutex + jobCtxMap map[int64]*JobContext + } + + jobCtx struct { + sync.RWMutex + // jobCtxMap maps job ID to job's ctx. + jobCtxMap map[int64]*JobContext + } + + // hook may be modified. + mu struct { + sync.RWMutex + hook Callback + interceptor Interceptor + } + + ddlSeqNumMu struct { + sync.Mutex + seqNum uint64 + } +} + +// schemaVersionManager is used to manage the schema version. To prevent the conflicts on this key between different DDL job, +// we use another transaction to update the schema version, so that we need to lock the schema version and unlock it until the job is committed. +type schemaVersionManager struct { + schemaVersionMu sync.Mutex + // lockOwner stores the job ID that is holding the lock. + lockOwner atomicutil.Int64 +} + +func newSchemaVersionManager() *schemaVersionManager { + return &schemaVersionManager{} +} + +func (sv *schemaVersionManager) setSchemaVersion(job *model.Job, store kv.Storage) (schemaVersion int64, err error) { + sv.lockSchemaVersion(job.ID) + err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + var err error + m := meta.NewMeta(txn) + schemaVersion, err = m.GenSchemaVersion() + return err + }) + return schemaVersion, err +} + +// lockSchemaVersion gets the lock to prevent the schema version from being updated. +func (sv *schemaVersionManager) lockSchemaVersion(jobID int64) { + ownerID := sv.lockOwner.Load() + // There may exist one job update schema version many times in multiple-schema-change, so we do not lock here again + // if they are the same job. + if ownerID != jobID { + sv.schemaVersionMu.Lock() + sv.lockOwner.Store(jobID) + } +} + +// unlockSchemaVersion releases the lock. +func (sv *schemaVersionManager) unlockSchemaVersion(jobID int64) { + ownerID := sv.lockOwner.Load() + if ownerID == jobID { + sv.lockOwner.Store(0) + sv.schemaVersionMu.Unlock() + } +} + +func (dc *ddlCtx) isOwner() bool { + isOwner := dc.ownerManager.IsOwner() + logutil.BgLogger().Debug("check whether is the DDL owner", zap.String("category", "ddl"), zap.Bool("isOwner", isOwner), zap.String("selfID", dc.uuid)) + if isOwner { + metrics.DDLCounter.WithLabelValues(metrics.DDLOwner + "_" + mysql.TiDBReleaseVersion).Inc() + } + return isOwner +} + +func (dc *ddlCtx) setDDLLabelForTopSQL(jobID int64, jobQuery string) { + dc.jobCtx.Lock() + defer dc.jobCtx.Unlock() + ctx, exists := dc.jobCtx.jobCtxMap[jobID] + if !exists { + ctx = NewJobContext() + dc.jobCtx.jobCtxMap[jobID] = ctx + } + ctx.setDDLLabelForTopSQL(jobQuery) +} + +func (dc *ddlCtx) setDDLSourceForDiagnosis(jobID int64, jobType model.ActionType) { + dc.jobCtx.Lock() + defer dc.jobCtx.Unlock() + ctx, exists := dc.jobCtx.jobCtxMap[jobID] + if !exists { + ctx = NewJobContext() + dc.jobCtx.jobCtxMap[jobID] = ctx + } + ctx.setDDLLabelForDiagnosis(jobType) +} + +func (dc *ddlCtx) getResourceGroupTaggerForTopSQL(jobID int64) tikvrpc.ResourceGroupTagger { + dc.jobCtx.Lock() + defer dc.jobCtx.Unlock() + ctx, exists := dc.jobCtx.jobCtxMap[jobID] + if !exists { + return nil + } + return ctx.getResourceGroupTaggerForTopSQL() +} + +func (dc *ddlCtx) removeJobCtx(job *model.Job) { + dc.jobCtx.Lock() + defer dc.jobCtx.Unlock() + delete(dc.jobCtx.jobCtxMap, job.ID) +} + +func (dc *ddlCtx) jobContext(jobID int64, reorgMeta *model.DDLReorgMeta) *JobContext { + dc.jobCtx.RLock() + defer dc.jobCtx.RUnlock() + var ctx *JobContext + if jobContext, exists := dc.jobCtx.jobCtxMap[jobID]; exists { + ctx = jobContext + } else { + ctx = NewJobContext() + } + if reorgMeta != nil && len(ctx.resourceGroupName) == 0 { + ctx.resourceGroupName = reorgMeta.ResourceGroupName + } + return ctx +} + +func (dc *ddlCtx) removeBackfillCtxJobCtx(jobID int64) { + dc.backfillCtx.Lock() + delete(dc.backfillCtx.jobCtxMap, jobID) + dc.backfillCtx.Unlock() +} + +func (dc *ddlCtx) backfillCtxJobIDs() []int64 { + dc.backfillCtx.Lock() + defer dc.backfillCtx.Unlock() + + runningJobIDs := make([]int64, 0, len(dc.backfillCtx.jobCtxMap)) + for id := range dc.backfillCtx.jobCtxMap { + runningJobIDs = append(runningJobIDs, id) + } + return runningJobIDs +} + +type reorgContexts struct { + sync.RWMutex + // reorgCtxMap maps job ID to reorg context. + reorgCtxMap map[int64]*reorgCtx +} + +func (dc *ddlCtx) getReorgCtx(jobID int64) *reorgCtx { + dc.reorgCtx.RLock() + defer dc.reorgCtx.RUnlock() + return dc.reorgCtx.reorgCtxMap[jobID] +} + +func (dc *ddlCtx) newReorgCtx(jobID int64, rowCount int64) *reorgCtx { + dc.reorgCtx.Lock() + defer dc.reorgCtx.Unlock() + existedRC, ok := dc.reorgCtx.reorgCtxMap[jobID] + if ok { + existedRC.references.Add(1) + return existedRC + } + rc := &reorgCtx{} + rc.doneCh = make(chan error, 1) + // initial reorgCtx + rc.setRowCount(rowCount) + rc.mu.warnings = make(map[errors.ErrorID]*terror.Error) + rc.mu.warningsCount = make(map[errors.ErrorID]int64) + rc.references.Add(1) + dc.reorgCtx.reorgCtxMap[jobID] = rc + return rc +} + +func (dc *ddlCtx) removeReorgCtx(jobID int64) { + dc.reorgCtx.Lock() + defer dc.reorgCtx.Unlock() + ctx, ok := dc.reorgCtx.reorgCtxMap[jobID] + if ok { + ctx.references.Sub(1) + if ctx.references.Load() == 0 { + delete(dc.reorgCtx.reorgCtxMap, jobID) + } + } +} + +func (dc *ddlCtx) notifyReorgWorkerJobStateChange(job *model.Job) { + rc := dc.getReorgCtx(job.ID) + if rc == nil { + logutil.BgLogger().Warn("cannot find reorgCtx", zap.Int64("Job ID", job.ID)) + return + } + logutil.BgLogger().Info("notify reorg worker the job's state", + zap.Int64("Job ID", job.ID), zap.String("Job State", job.State.String()), + zap.String("Schema State", job.SchemaState.String()), zap.String("category", "ddl")) + rc.notifyJobState(job.State) +} + +// EnableTiFlashPoll enables TiFlash poll loop aka PollTiFlashReplicaStatus. +func EnableTiFlashPoll(d interface{}) { + if dd, ok := d.(*ddl); ok { + dd.enableTiFlashPoll.Store(true) + } +} + +// DisableTiFlashPoll disables TiFlash poll loop aka PollTiFlashReplicaStatus. +func DisableTiFlashPoll(d interface{}) { + if dd, ok := d.(*ddl); ok { + dd.enableTiFlashPoll.Store(false) + } +} + +// IsTiFlashPollEnabled reveals enableTiFlashPoll +func (d *ddl) IsTiFlashPollEnabled() bool { + return d.enableTiFlashPoll.Load() +} + +// RegisterStatsHandle registers statistics handle and its corresponding even channel for ddl. +func (d *ddl) RegisterStatsHandle(h *handle.Handle) { + d.ddlCtx.statsHandle = h + d.ddlEventCh = h.DDLEventCh() +} + +// asyncNotifyEvent will notify the ddl event to outside world, say statistic handle. When the channel is full, we may +// give up notify and log it. +func asyncNotifyEvent(d *ddlCtx, e *util.Event) { + if d.ddlEventCh != nil { + if d.lease == 0 { + // If lease is 0, it's always used in test. + select { + case d.ddlEventCh <- e: + default: + } + return + } + for i := 0; i < 10; i++ { + select { + case d.ddlEventCh <- e: + return + default: + time.Sleep(time.Microsecond * 10) + } + } + logutil.BgLogger().Warn("fail to notify DDL event", zap.String("category", "ddl"), zap.String("event", e.String())) + } +} + +// NewDDL creates a new DDL. +func NewDDL(ctx context.Context, options ...Option) DDL { + return newDDL(ctx, options...) +} + +func newDDL(ctx context.Context, options ...Option) *ddl { + opt := &Options{ + Hook: &BaseCallback{}, + } + for _, o := range options { + o(opt) + } + + id := uuid.New().String() + var manager owner.Manager + var schemaSyncer syncer.SchemaSyncer + var stateSyncer syncer.StateSyncer + var deadLockCkr util.DeadTableLockChecker + if etcdCli := opt.EtcdCli; etcdCli == nil { + // The etcdCli is nil if the store is localstore which is only used for testing. + // So we use mockOwnerManager and MockSchemaSyncer. + manager = owner.NewMockManager(ctx, id, opt.Store, DDLOwnerKey) + schemaSyncer = NewMockSchemaSyncer() + stateSyncer = NewMockStateSyncer() + } else { + manager = owner.NewOwnerManager(ctx, etcdCli, ddlPrompt, id, DDLOwnerKey) + schemaSyncer = syncer.NewSchemaSyncer(etcdCli, id) + stateSyncer = syncer.NewStateSyncer(etcdCli, util.ServerGlobalState) + deadLockCkr = util.NewDeadTableLockChecker(etcdCli) + } + + // TODO: make store and infoCache explicit arguments + // these two should be ensured to exist + if opt.Store == nil { + panic("store should not be nil") + } + if opt.InfoCache == nil { + panic("infoCache should not be nil") + } + + ddlCtx := &ddlCtx{ + uuid: id, + store: opt.Store, + lease: opt.Lease, + ddlJobDoneCh: make(chan struct{}, 1), + ownerManager: manager, + schemaSyncer: schemaSyncer, + stateSyncer: stateSyncer, + binlogCli: binloginfo.GetPumpsClient(), + infoCache: opt.InfoCache, + tableLockCkr: deadLockCkr, + etcdCli: opt.EtcdCli, + schemaVersionManager: newSchemaVersionManager(), + waitSchemaSyncedController: newWaitSchemaSyncedController(), + runningJobIDs: make([]string, 0, jobRecordCapacity), + } + ddlCtx.reorgCtx.reorgCtxMap = make(map[int64]*reorgCtx) + ddlCtx.jobCtx.jobCtxMap = make(map[int64]*JobContext) + ddlCtx.mu.hook = opt.Hook + ddlCtx.mu.interceptor = &BaseInterceptor{} + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) + ddlCtx.ctx, ddlCtx.cancel = context.WithCancel(ctx) + ddlCtx.runningJobs.ids = make(map[int64]struct{}) + + d := &ddl{ + ddlCtx: ddlCtx, + limitJobCh: make(chan *limitJobTask, batchAddingJobs), + enableTiFlashPoll: atomicutil.NewBool(true), + ddlJobCh: make(chan struct{}, 100), + } + + scheduler.RegisterTaskType(BackfillTaskType, + func(ctx context.Context, id string, task *proto.Task, taskTable scheduler.TaskTable) scheduler.Scheduler { + return newBackfillDistScheduler(ctx, id, task, taskTable, d) + }, scheduler.WithSummary, + ) + + backFillDsp, err := NewBackfillingDispatcherExt(d) + if err != nil { + logutil.BgLogger().Warn("NewBackfillingDispatcherExt failed", zap.String("category", "ddl"), zap.Error(err)) + } else { + dispatcher.RegisterDispatcherFactory(BackfillTaskType, + func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { + return newLitBackfillDispatcher(ctx, taskMgr, serverID, task, backFillDsp) + }) + dispatcher.RegisterDispatcherCleanUpFactory(BackfillTaskType, newBackfillCleanUpS3) + } + + // Register functions for enable/disable ddl when changing system variable `tidb_enable_ddl`. + variable.EnableDDL = d.EnableDDL + variable.DisableDDL = d.DisableDDL + variable.SwitchMDL = d.SwitchMDL + + return d +} + +// Stop implements DDL.Stop interface. +func (d *ddl) Stop() error { + d.m.Lock() + defer d.m.Unlock() + + d.close() + logutil.BgLogger().Info("stop DDL", zap.String("category", "ddl"), zap.String("ID", d.uuid)) + return nil +} + +func (d *ddl) newDeleteRangeManager(mock bool) delRangeManager { + var delRangeMgr delRangeManager + if !mock { + delRangeMgr = newDelRangeManager(d.store, d.sessPool) + logutil.BgLogger().Info("start delRangeManager OK", zap.String("category", "ddl"), zap.Bool("is a emulator", !d.store.SupportDeleteRange())) + } else { + delRangeMgr = newMockDelRangeManager() + } + + delRangeMgr.start() + return delRangeMgr +} + +func (d *ddl) prepareWorkers4ConcurrencyDDL() { + workerFactory := func(tp workerType) func() (pools.Resource, error) { + return func() (pools.Resource, error) { + wk := newWorker(d.ctx, tp, d.sessPool, d.delRangeMgr, d.ddlCtx) + sessForJob, err := d.sessPool.Get() + if err != nil { + return nil, err + } + sessForJob.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) + wk.sess = sess.NewSession(sessForJob) + metrics.DDLCounter.WithLabelValues(fmt.Sprintf("%s_%s", metrics.CreateDDL, wk.String())).Inc() + return wk, nil + } + } + // reorg worker count at least 1 at most 10. + reorgCnt := min(max(runtime.GOMAXPROCS(0)/4, 1), reorgWorkerCnt) + d.reorgWorkerPool = newDDLWorkerPool(pools.NewResourcePool(workerFactory(addIdxWorker), reorgCnt, reorgCnt, 0), reorg) + d.generalDDLWorkerPool = newDDLWorkerPool(pools.NewResourcePool(workerFactory(generalWorker), generalWorkerCnt, generalWorkerCnt, 0), general) + failpoint.Inject("NoDDLDispatchLoop", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return() + } + }) + d.wg.Run(d.startDispatchLoop) +} + +// Start implements DDL.Start interface. +func (d *ddl) Start(ctxPool *pools.ResourcePool) error { + logutil.BgLogger().Info("start DDL", zap.String("category", "ddl"), zap.String("ID", d.uuid), zap.Bool("runWorker", config.GetGlobalConfig().Instance.TiDBEnableDDL.Load())) + + d.wg.Run(d.limitDDLJobs) + d.sessPool = sess.NewSessionPool(ctxPool, d.store) + d.ownerManager.SetBeOwnerHook(func() { + var err error + d.ddlSeqNumMu.Lock() + defer d.ddlSeqNumMu.Unlock() + d.ddlSeqNumMu.seqNum, err = d.GetNextDDLSeqNum() + if err != nil { + logutil.BgLogger().Error("error when getting the ddl history count", zap.Error(err)) + } + }) + + d.delRangeMgr = d.newDeleteRangeManager(ctxPool == nil) + + if err := d.stateSyncer.Init(d.ctx); err != nil { + logutil.BgLogger().Warn("start DDL init state syncer failed", zap.String("category", "ddl"), zap.Error(err)) + return errors.Trace(err) + } + + d.prepareWorkers4ConcurrencyDDL() + + if config.TableLockEnabled() { + d.wg.Add(1) + go d.startCleanDeadTableLock() + } + + // If tidb_enable_ddl is true, we need campaign owner and do DDL jobs. Besides, we also can do backfill jobs. + // Otherwise, we needn't do that. + if config.GetGlobalConfig().Instance.TiDBEnableDDL.Load() { + if err := d.EnableDDL(); err != nil { + return err + } + } + + variable.RegisterStatistics(d) + + metrics.DDLCounter.WithLabelValues(metrics.CreateDDLInstance).Inc() + + // Start some background routine to manage TiFlash replica. + d.wg.Run(d.PollTiFlashRoutine) + + ctx, err := d.sessPool.Get() + if err != nil { + return err + } + defer d.sessPool.Put(ctx) + + ingest.InitGlobalLightningEnv(d.ctx, ctx) + + return nil +} + +// EnableDDL enable this node to execute ddl. +// Since ownerManager.CampaignOwner will start a new goroutine to run ownerManager.campaignLoop, +// we should make sure that before invoking EnableDDL(), ddl is DISABLE. +func (d *ddl) EnableDDL() error { + err := d.ownerManager.CampaignOwner() + return errors.Trace(err) +} + +// DisableDDL disable this node to execute ddl. +// We should make sure that before invoking DisableDDL(), ddl is ENABLE. +func (d *ddl) DisableDDL() error { + if d.ownerManager.IsOwner() { + // If there is only one node, we should NOT disable ddl. + serverInfo, err := infosync.GetAllServerInfo(d.ctx) + if err != nil { + logutil.BgLogger().Error("error when GetAllServerInfo", zap.String("category", "ddl"), zap.Error(err)) + return err + } + if len(serverInfo) <= 1 { + return dbterror.ErrDDLSetting.GenWithStackByArgs("disabling", "can not disable ddl owner when it is the only one tidb instance") + } + // FIXME: if possible, when this node is the only node with DDL, ths setting of DisableDDL should fail. + } + + // disable campaign by interrupting campaignLoop + d.ownerManager.CampaignCancel() + return nil +} + +// GetNextDDLSeqNum return the next DDL seq num. +func (d *ddl) GetNextDDLSeqNum() (uint64, error) { + var count uint64 + ctx := kv.WithInternalSourceType(d.ctx, kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + var err error + count, err = t.GetHistoryDDLCount() + return err + }) + return count, err +} + +func (d *ddl) close() { + if isChanClosed(d.ctx.Done()) { + return + } + + startTime := time.Now() + d.cancel() + d.wg.Wait() + d.ownerManager.Cancel() + d.schemaSyncer.Close() + if d.reorgWorkerPool != nil { + d.reorgWorkerPool.close() + } + if d.generalDDLWorkerPool != nil { + d.generalDDLWorkerPool.close() + } + + // d.delRangeMgr using sessions from d.sessPool. + // Put it before d.sessPool.close to reduce the time spent by d.sessPool.close. + if d.delRangeMgr != nil { + d.delRangeMgr.clear() + } + if d.sessPool != nil { + d.sessPool.Close() + } + variable.UnregisterStatistics(d) + + logutil.BgLogger().Info("DDL closed", zap.String("category", "ddl"), zap.String("ID", d.uuid), zap.Duration("take time", time.Since(startTime))) +} + +// GetLease implements DDL.GetLease interface. +func (d *ddl) GetLease() time.Duration { + lease := d.lease + return lease +} + +// GetInfoSchemaWithInterceptor gets the infoschema binding to d. It's exported for testing. +// Please don't use this function, it is used by TestParallelDDLBeforeRunDDLJob to intercept the calling of d.infoHandle.Get(), use d.infoHandle.Get() instead. +// Otherwise, the TestParallelDDLBeforeRunDDLJob will hang up forever. +func (d *ddl) GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema { + is := d.infoCache.GetLatest() + + d.mu.RLock() + defer d.mu.RUnlock() + return d.mu.interceptor.OnGetInfoSchema(ctx, is) +} + +func (d *ddl) genGlobalIDs(count int) ([]int64, error) { + var ret []int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { + failpoint.Inject("mockGenGlobalIDFail", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(errors.New("gofail genGlobalIDs error")) + } + }) + + m := meta.NewMeta(txn) + var err error + ret, err = m.GenGlobalIDs(count) + return err + }) + + return ret, err +} + +func (d *ddl) genPlacementPolicyID() (int64, error) { + var ret int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, d.store, true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + var err error + ret, err = m.GenPlacementPolicyID() + return err + }) + + return ret, err +} + +// SchemaSyncer implements DDL.SchemaSyncer interface. +func (d *ddl) SchemaSyncer() syncer.SchemaSyncer { + return d.schemaSyncer +} + +// StateSyncer implements DDL.StateSyncer interface. +func (d *ddl) StateSyncer() syncer.StateSyncer { + return d.stateSyncer +} + +// OwnerManager implements DDL.OwnerManager interface. +func (d *ddl) OwnerManager() owner.Manager { + return d.ownerManager +} + +// GetID implements DDL.GetID interface. +func (d *ddl) GetID() string { + return d.uuid +} + +var ( + fastDDLIntervalPolicy = []time.Duration{ + 500 * time.Millisecond, + } + normalDDLIntervalPolicy = []time.Duration{ + 500 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + } + slowDDLIntervalPolicy = []time.Duration{ + 500 * time.Millisecond, + 500 * time.Millisecond, + 1 * time.Second, + 1 * time.Second, + 3 * time.Second, + } +) + +func getIntervalFromPolicy(policy []time.Duration, i int) (time.Duration, bool) { + plen := len(policy) + if i < plen { + return policy[i], true + } + return policy[plen-1], false +} + +func getJobCheckInterval(job *model.Job, i int) (time.Duration, bool) { + switch job.Type { + case model.ActionAddIndex, model.ActionAddPrimaryKey, model.ActionModifyColumn, + model.ActionReorganizePartition, + model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning: + return getIntervalFromPolicy(slowDDLIntervalPolicy, i) + case model.ActionCreateTable, model.ActionCreateSchema: + return getIntervalFromPolicy(fastDDLIntervalPolicy, i) + default: + return getIntervalFromPolicy(normalDDLIntervalPolicy, i) + } +} + +func (dc *ddlCtx) asyncNotifyWorker(ch chan struct{}, etcdPath string, jobID int64, jobType string) { + // If the workers don't run, we needn't notify workers. + // TODO: It does not affect informing the backfill worker. + if !config.GetGlobalConfig().Instance.TiDBEnableDDL.Load() { + return + } + if dc.isOwner() { + asyncNotify(ch) + } else { + dc.asyncNotifyByEtcd(etcdPath, jobID, jobType) + } +} + +func updateTickerInterval(ticker *time.Ticker, lease time.Duration, job *model.Job, i int) *time.Ticker { + interval, changed := getJobCheckInterval(job, i) + if !changed { + return ticker + } + // For now we should stop old ticker and create a new ticker + ticker.Stop() + return time.NewTicker(chooseLeaseTime(lease, interval)) +} + +func recordLastDDLInfo(ctx sessionctx.Context, job *model.Job) { + if job == nil { + return + } + ctx.GetSessionVars().LastDDLInfo.Query = job.Query + ctx.GetSessionVars().LastDDLInfo.SeqNum = job.SeqNum +} + +func setDDLJobQuery(ctx sessionctx.Context, job *model.Job) { + switch job.Type { + case model.ActionUpdateTiFlashReplicaStatus, model.ActionUnlockTable: + job.Query = "" + default: + job.Query, _ = ctx.Value(sessionctx.QueryString).(string) + } +} + +// DoDDLJob will return +// - nil: found in history DDL job and no job error +// - context.Cancel: job has been sent to worker, but not found in history DDL job before cancel +// - other: found in history DDL job and return that job error +func (d *ddl) DoDDLJob(ctx sessionctx.Context, job *model.Job) error { + job.TraceInfo = &model.TraceInfo{ + ConnectionID: ctx.GetSessionVars().ConnectionID, + SessionAlias: ctx.GetSessionVars().SessionAlias, + } + if mci := ctx.GetSessionVars().StmtCtx.MultiSchemaInfo; mci != nil { + // In multiple schema change, we don't run the job. + // Instead, we merge all the jobs into one pending job. + return appendToSubJobs(mci, job) + } + // Get a global job ID and put the DDL job in the queue. + setDDLJobQuery(ctx, job) + task := &limitJobTask{job, make(chan error), nil} + d.limitJobCh <- task + + failpoint.Inject("mockParallelSameDDLJobTwice", func(val failpoint.Value) { + if val.(bool) { + <-task.err + // The same job will be put to the DDL queue twice. + job = job.Clone() + task1 := &limitJobTask{job, make(chan error), nil} + d.limitJobCh <- task1 + // The second job result is used for test. + task = task1 + } + }) + + // worker should restart to continue handling tasks in limitJobCh, and send back through task.err + err := <-task.err + if err != nil { + // The transaction of enqueuing job is failed. + return errors.Trace(err) + } + + sessVars := ctx.GetSessionVars() + sessVars.StmtCtx.IsDDLJobInQueue = true + + // Notice worker that we push a new job and wait the job done. + d.asyncNotifyWorker(d.ddlJobCh, addingDDLJobConcurrent, job.ID, job.Type.String()) + logutil.BgLogger().Info("start DDL job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.String("query", job.Query)) + + var historyJob *model.Job + jobID := job.ID + + // Attach the context of the jobId to the calling session so that + // KILL can cancel this DDL job. + ctx.GetSessionVars().StmtCtx.DDLJobID = jobID + + // For a job from start to end, the state of it will be none -> delete only -> write only -> reorganization -> public + // For every state changes, we will wait as lease 2 * lease time, so here the ticker check is 10 * lease. + // But we use etcd to speed up, normally it takes less than 0.5s now, so we use 0.5s or 1s or 3s as the max value. + initInterval, _ := getJobCheckInterval(job, 0) + ticker := time.NewTicker(chooseLeaseTime(10*d.lease, initInterval)) + startTime := time.Now() + metrics.JobsGauge.WithLabelValues(job.Type.String()).Inc() + defer func() { + ticker.Stop() + metrics.JobsGauge.WithLabelValues(job.Type.String()).Dec() + metrics.HandleJobHistogram.WithLabelValues(job.Type.String(), metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + recordLastDDLInfo(ctx, historyJob) + }() + i := 0 + for { + failpoint.Inject("storeCloseInLoop", func(_ failpoint.Value) { + _ = d.Stop() + }) + + select { + case <-d.ddlJobDoneCh: + case <-ticker.C: + i++ + ticker = updateTickerInterval(ticker, 10*d.lease, job, i) + case <-d.ctx.Done(): + logutil.BgLogger().Info("DoDDLJob will quit because context done", zap.String("category", "ddl")) + return context.Canceled + } + + // If the connection being killed, we need to CANCEL the DDL job. + if atomic.LoadUint32(&sessVars.Killed) == 1 { + if atomic.LoadInt32(&sessVars.ConnectionStatus) == variable.ConnStatusShutdown { + logutil.BgLogger().Info("DoDDLJob will quit because context done", zap.String("category", "ddl")) + return context.Canceled + } + if sessVars.StmtCtx.DDLJobID != 0 { + se, err := d.sessPool.Get() + if err != nil { + logutil.BgLogger().Error("get session failed, check again", zap.String("category", "ddl"), zap.Error(err)) + continue + } + sessVars.StmtCtx.DDLJobID = 0 // Avoid repeat. + errs, err := CancelJobsBySystem(se, []int64{jobID}) + d.sessPool.Put(se) + if len(errs) > 0 { + logutil.BgLogger().Warn("error canceling DDL job", zap.Error(errs[0])) + } + if err != nil { + logutil.BgLogger().Warn("Kill command could not cancel DDL job", zap.Error(err)) + continue + } + } + } + + se, err := d.sessPool.Get() + if err != nil { + logutil.BgLogger().Error("get session failed, check again", zap.String("category", "ddl"), zap.Error(err)) + continue + } + historyJob, err = GetHistoryJobByID(se, jobID) + d.sessPool.Put(se) + if err != nil { + logutil.BgLogger().Error("get history DDL job failed, check again", zap.String("category", "ddl"), zap.Error(err)) + continue + } + if historyJob == nil { + logutil.BgLogger().Debug("DDL job is not in history, maybe not run", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) + continue + } + + d.checkHistoryJobInTest(ctx, historyJob) + + // If a job is a history job, the state must be JobStateSynced or JobStateRollbackDone or JobStateCancelled. + if historyJob.IsSynced() { + // Judge whether there are some warnings when executing DDL under the certain SQL mode. + if historyJob.ReorgMeta != nil && len(historyJob.ReorgMeta.Warnings) != 0 { + if len(historyJob.ReorgMeta.Warnings) != len(historyJob.ReorgMeta.WarningsCount) { + logutil.BgLogger().Info("DDL warnings doesn't match the warnings count", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) + } else { + for key, warning := range historyJob.ReorgMeta.Warnings { + keyCount := historyJob.ReorgMeta.WarningsCount[key] + if keyCount == 1 { + ctx.GetSessionVars().StmtCtx.AppendWarning(warning) + } else { + newMsg := fmt.Sprintf("%d warnings with this error code, first warning: "+warning.GetMsg(), keyCount) + newWarning := dbterror.ClassTypes.Synthesize(terror.ErrCode(warning.Code()), newMsg) + ctx.GetSessionVars().StmtCtx.AppendWarning(newWarning) + } + } + } + } + appendMultiChangeWarningsToOwnerCtx(ctx, historyJob) + + logutil.BgLogger().Info("DDL job is finished", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) + return nil + } + + if historyJob.Error != nil { + logutil.BgLogger().Info("DDL job is failed", zap.String("category", "ddl"), zap.Int64("jobID", jobID)) + return errors.Trace(historyJob.Error) + } + panic("When the state is JobStateRollbackDone or JobStateCancelled, historyJob.Error should never be nil") + } +} + +func (d *ddl) callHookOnChanged(job *model.Job, err error) error { + if job.State == model.JobStateNone { + // We don't call the hook if the job haven't run yet. + return err + } + d.mu.RLock() + defer d.mu.RUnlock() + + err = d.mu.hook.OnChanged(err) + return errors.Trace(err) +} + +// SetBinlogClient implements DDL.SetBinlogClient interface. +func (d *ddl) SetBinlogClient(binlogCli *pumpcli.PumpsClient) { + d.binlogCli = binlogCli +} + +// GetHook implements DDL.GetHook interface. +func (d *ddl) GetHook() Callback { + d.mu.Lock() + defer d.mu.Unlock() + + return d.mu.hook +} + +// SetHook set the customized hook. +func (d *ddl) SetHook(h Callback) { + d.mu.Lock() + defer d.mu.Unlock() + + d.mu.hook = h +} + +func (d *ddl) startCleanDeadTableLock() { + defer func() { + d.wg.Done() + }() + + defer tidbutil.Recover(metrics.LabelDDL, "startCleanDeadTableLock", nil, false) + + ticker := time.NewTicker(time.Second * 10) + defer ticker.Stop() + for { + select { + case <-ticker.C: + if !d.ownerManager.IsOwner() { + continue + } + deadLockTables, err := d.tableLockCkr.GetDeadLockedTables(d.ctx, d.infoCache.GetLatest().AllSchemas()) + if err != nil { + logutil.BgLogger().Info("get dead table lock failed.", zap.String("category", "ddl"), zap.Error(err)) + continue + } + for se, tables := range deadLockTables { + err := d.CleanDeadTableLock(tables, se) + if err != nil { + logutil.BgLogger().Info("clean dead table lock failed.", zap.String("category", "ddl"), zap.Error(err)) + } + } + case <-d.ctx.Done(): + return + } + } +} + +// SwitchMDL enables MDL or disable MDL. +func (d *ddl) SwitchMDL(enable bool) error { + isEnableBefore := variable.EnableMDL.Load() + if isEnableBefore == enable { + return nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*30) + defer cancel() + + // Check if there is any DDL running. + // This check can not cover every corner cases, so users need to guarantee that there is no DDL running by themselves. + sessCtx, err := d.sessPool.Get() + if err != nil { + return err + } + defer d.sessPool.Put(sessCtx) + se := sess.NewSession(sessCtx) + rows, err := se.Execute(ctx, "select 1 from mysql.tidb_ddl_job", "check job") + if err != nil { + return err + } + if len(rows) != 0 { + return errors.New("please wait for all jobs done") + } + + variable.EnableMDL.Store(enable) + err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), d.store, true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + oldEnable, _, err := m.GetMetadataLock() + if err != nil { + return err + } + if oldEnable != enable { + err = m.SetMetadataLock(enable) + } + return err + }) + if err != nil { + logutil.BgLogger().Warn("switch metadata lock feature", zap.String("category", "ddl"), zap.Bool("enable", enable), zap.Error(err)) + return err + } + logutil.BgLogger().Info("switch metadata lock feature", zap.String("category", "ddl"), zap.Bool("enable", enable)) + return nil +} + +// RecoverInfo contains information needed by DDL.RecoverTable. +type RecoverInfo struct { + SchemaID int64 + TableInfo *model.TableInfo + DropJobID int64 + SnapshotTS uint64 + AutoIDs meta.AutoIDGroup + OldSchemaName string + OldTableName string +} + +// RecoverSchemaInfo contains information needed by DDL.RecoverSchema. +type RecoverSchemaInfo struct { + *model.DBInfo + RecoverTabsInfo []*RecoverInfo + DropJobID int64 + SnapshotTS uint64 + OldSchemaName model.CIStr +} + +// delayForAsyncCommit sleeps `SafeWindow + AllowedClockDrift` before a DDL job finishes. +// It should be called before any DDL that could break data consistency. +// This provides a safe window for async commit and 1PC to commit with an old schema. +func delayForAsyncCommit() { + if variable.EnableMDL.Load() { + // If metadata lock is enabled. The transaction of DDL must begin after prewrite of the async commit transaction, + // then the commit ts of DDL must be greater than the async commit transaction. In this case, the corresponding schema of the async commit transaction + // is correct. But if metadata lock is disabled, we can't ensure that the corresponding schema of the async commit transaction isn't change. + return + } + cfg := config.GetGlobalConfig().TiKVClient.AsyncCommit + duration := cfg.SafeWindow + cfg.AllowedClockDrift + logutil.BgLogger().Info("sleep before DDL finishes to make async commit and 1PC safe", + zap.Duration("duration", duration)) + time.Sleep(duration) +} + +var ( + // RunInGoTest is used to identify whether ddl in running in the test. + RunInGoTest bool +) + +// GetDropOrTruncateTableInfoFromJobsByStore implements GetDropOrTruncateTableInfoFromJobs +func GetDropOrTruncateTableInfoFromJobsByStore(jobs []*model.Job, gcSafePoint uint64, getTable func(uint64, int64, int64) (*model.TableInfo, error), fn func(*model.Job, *model.TableInfo) (bool, error)) (bool, error) { + for _, job := range jobs { + // Check GC safe point for getting snapshot infoSchema. + err := gcutil.ValidateSnapshotWithGCSafePoint(job.StartTS, gcSafePoint) + if err != nil { + return false, err + } + if job.Type != model.ActionDropTable && job.Type != model.ActionTruncateTable { + continue + } + + tbl, err := getTable(job.StartTS, job.SchemaID, job.TableID) + if err != nil { + if meta.ErrDBNotExists.Equal(err) { + // The dropped/truncated DDL maybe execute failed that caused by the parallel DDL execution, + // then can't find the table from the snapshot info-schema. Should just ignore error here, + // see more in TestParallelDropSchemaAndDropTable. + continue + } + return false, err + } + if tbl == nil { + // The dropped/truncated DDL maybe execute failed that caused by the parallel DDL execution, + // then can't find the table from the snapshot info-schema. Should just ignore error here, + // see more in TestParallelDropSchemaAndDropTable. + continue + } + finish, err := fn(job, tbl) + if err != nil || finish { + return finish, err + } + } + return false, nil +} + +// Info is for DDL information. +type Info struct { + SchemaVer int64 + ReorgHandle kv.Key // It's only used for DDL information. + Jobs []*model.Job // It's the currently running jobs. +} + +// GetDDLInfoWithNewTxn returns DDL information using a new txn. +func GetDDLInfoWithNewTxn(s sessionctx.Context) (*Info, error) { + se := sess.NewSession(s) + err := se.Begin() + if err != nil { + return nil, err + } + info, err := GetDDLInfo(s) + se.Rollback() + return info, err +} + +// GetDDLInfo returns DDL information. +func GetDDLInfo(s sessionctx.Context) (*Info, error) { + var err error + info := &Info{} + se := sess.NewSession(s) + txn, err := se.Txn() + if err != nil { + return nil, errors.Trace(err) + } + t := meta.NewMeta(txn) + info.Jobs = make([]*model.Job, 0, 2) + var generalJob, reorgJob *model.Job + generalJob, reorgJob, err = get2JobsFromTable(se) + if err != nil { + return nil, errors.Trace(err) + } + + if generalJob != nil { + info.Jobs = append(info.Jobs, generalJob) + } + + if reorgJob != nil { + info.Jobs = append(info.Jobs, reorgJob) + } + + info.SchemaVer, err = t.GetSchemaVersionWithNonEmptyDiff() + if err != nil { + return nil, errors.Trace(err) + } + if reorgJob == nil { + return info, nil + } + + _, info.ReorgHandle, _, _, err = newReorgHandler(se).GetDDLReorgHandle(reorgJob) + if err != nil { + if meta.ErrDDLReorgElementNotExist.Equal(err) { + return info, nil + } + return nil, errors.Trace(err) + } + + return info, nil +} + +func get2JobsFromTable(sess *sess.Session) (*model.Job, *model.Job, error) { + var generalJob, reorgJob *model.Job + jobs, err := getJobsBySQL(sess, JobTable, "not reorg order by job_id limit 1") + if err != nil { + return nil, nil, errors.Trace(err) + } + + if len(jobs) != 0 { + generalJob = jobs[0] + } + jobs, err = getJobsBySQL(sess, JobTable, "reorg order by job_id limit 1") + if err != nil { + return nil, nil, errors.Trace(err) + } + if len(jobs) != 0 { + reorgJob = jobs[0] + } + return generalJob, reorgJob, nil +} + +// cancelRunningJob cancel a DDL job that is in the concurrent state. +func cancelRunningJob(_ *sess.Session, job *model.Job, + byWho model.AdminCommandOperator) (err error) { + // These states can't be cancelled. + if job.IsDone() || job.IsSynced() { + return dbterror.ErrCancelFinishedDDLJob.GenWithStackByArgs(job.ID) + } + + // If the state is rolling back, it means the work is cleaning the data after cancelling the job. + if job.IsCancelled() || job.IsRollingback() || job.IsRollbackDone() { + return nil + } + + if !job.IsRollbackable() { + return dbterror.ErrCannotCancelDDLJob.GenWithStackByArgs(job.ID) + } + job.State = model.JobStateCancelling + job.AdminOperator = byWho + return nil +} + +// pauseRunningJob check and pause the running Job +func pauseRunningJob(_ *sess.Session, job *model.Job, + byWho model.AdminCommandOperator) (err error) { + if job.IsPausing() || job.IsPaused() { + return dbterror.ErrPausedDDLJob.GenWithStackByArgs(job.ID) + } + if !job.IsPausable() { + errMsg := fmt.Sprintf("state [%s] or schema state [%s]", job.State.String(), job.SchemaState.String()) + err = dbterror.ErrCannotPauseDDLJob.GenWithStackByArgs(job.ID, errMsg) + if err != nil { + return err + } + } + + job.State = model.JobStatePausing + job.AdminOperator = byWho + return nil +} + +// resumePausedJob check and resume the Paused Job +func resumePausedJob(_ *sess.Session, job *model.Job, + byWho model.AdminCommandOperator) (err error) { + if !job.IsResumable() { + errMsg := fmt.Sprintf("job has not been paused, job state:%s, schema state:%s", + job.State, job.SchemaState) + return dbterror.ErrCannotResumeDDLJob.GenWithStackByArgs(job.ID, errMsg) + } + // The Paused job should only be resumed by who paused it + if job.AdminOperator != byWho { + errMsg := fmt.Sprintf("job has been paused by [%s], should not resumed by [%s]", + job.AdminOperator.String(), byWho.String()) + return dbterror.ErrCannotResumeDDLJob.GenWithStackByArgs(job.ID, errMsg) + } + + job.State = model.JobStateQueueing + + return nil +} + +// processJobs command on the Job according to the process +func processJobs(process func(*sess.Session, *model.Job, model.AdminCommandOperator) (err error), + sessCtx sessionctx.Context, + ids []int64, + byWho model.AdminCommandOperator) (jobErrs []error, err error) { + failpoint.Inject("mockFailedCommandOnConcurencyDDL", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil, errors.New("mock failed admin command on ddl jobs")) + } + }) + + if len(ids) == 0 { + return nil, nil + } + + ns := sess.NewSession(sessCtx) + // We should process (and try) all the jobs in one Transaction. + for tryN := uint(0); tryN < 3; tryN++ { + jobErrs = make([]error, len(ids)) + // Need to figure out which one could not be paused + jobMap := make(map[int64]int, len(ids)) + idsStr := make([]string, 0, len(ids)) + for idx, id := range ids { + jobMap[id] = idx + idsStr = append(idsStr, strconv.FormatInt(id, 10)) + } + + err = ns.Begin() + if err != nil { + return nil, err + } + jobs, err := getJobsBySQL(ns, JobTable, fmt.Sprintf("job_id in (%s) order by job_id", strings.Join(idsStr, ", "))) + if err != nil { + ns.Rollback() + return nil, err + } + + for _, job := range jobs { + i, ok := jobMap[job.ID] + if !ok { + logutil.BgLogger().Debug("Job ID from meta is not consistent with requested job id,", + zap.Int64("fetched job ID", job.ID)) + jobErrs[i] = dbterror.ErrInvalidDDLJob.GenWithStackByArgs(job.ID) + continue + } + delete(jobMap, job.ID) + + err = process(ns, job, byWho) + if err != nil { + jobErrs[i] = err + continue + } + + err = updateDDLJob2Table(ns, job, false) + if err != nil { + jobErrs[i] = err + continue + } + } + + failpoint.Inject("mockCommitFailedOnDDLCommand", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(jobErrs, errors.New("mock commit failed on admin command on ddl jobs")) + } + }) + + // There may be some conflict during the update, try it again + if err = ns.Commit(); err != nil { + continue + } + + for id, idx := range jobMap { + jobErrs[idx] = dbterror.ErrDDLJobNotFound.GenWithStackByArgs(id) + } + + return jobErrs, nil + } + + return jobErrs, err +} + +// CancelJobs cancels the DDL jobs according to user command. +func CancelJobs(se sessionctx.Context, ids []int64) (errs []error, err error) { + return processJobs(cancelRunningJob, se, ids, model.AdminCommandByEndUser) +} + +// PauseJobs pause all the DDL jobs according to user command. +func PauseJobs(se sessionctx.Context, ids []int64) ([]error, error) { + return processJobs(pauseRunningJob, se, ids, model.AdminCommandByEndUser) +} + +// ResumeJobs resume all the DDL jobs according to user command. +func ResumeJobs(se sessionctx.Context, ids []int64) ([]error, error) { + return processJobs(resumePausedJob, se, ids, model.AdminCommandByEndUser) +} + +// CancelJobsBySystem cancels Jobs because of internal reasons. +func CancelJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { + return processJobs(cancelRunningJob, se, ids, model.AdminCommandBySystem) +} + +// PauseJobsBySystem pauses Jobs because of internal reasons. +func PauseJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { + return processJobs(pauseRunningJob, se, ids, model.AdminCommandBySystem) +} + +// ResumeJobsBySystem resumes Jobs that are paused by TiDB itself. +func ResumeJobsBySystem(se sessionctx.Context, ids []int64) (errs []error, err error) { + return processJobs(resumePausedJob, se, ids, model.AdminCommandBySystem) +} + +// pprocessAllJobs processes all the jobs in the job table, 100 jobs at a time in case of high memory usage. +func processAllJobs(process func(*sess.Session, *model.Job, model.AdminCommandOperator) (err error), + se sessionctx.Context, byWho model.AdminCommandOperator) (map[int64]error, error) { + var err error + var jobErrs = make(map[int64]error) + + ns := sess.NewSession(se) + err = ns.Begin() + if err != nil { + return nil, err + } + + var jobID int64 + var jobIDMax int64 + var limit = 100 + for { + var jobs []*model.Job + jobs, err = getJobsBySQL(ns, JobTable, + fmt.Sprintf("job_id >= %s order by job_id asc limit %s", + strconv.FormatInt(jobID, 10), + strconv.FormatInt(int64(limit), 10))) + if err != nil { + ns.Rollback() + return nil, err + } + + for _, job := range jobs { + err = process(ns, job, byWho) + if err != nil { + jobErrs[job.ID] = err + continue + } + + err = updateDDLJob2Table(ns, job, false) + if err != nil { + jobErrs[job.ID] = err + continue + } + } + + // Just in case the job ID is not sequential + if len(jobs) > 0 && jobs[len(jobs)-1].ID > jobIDMax { + jobIDMax = jobs[len(jobs)-1].ID + } + + // If rows returned is smaller than $limit, then there is no more records + if len(jobs) < limit { + break + } + + jobID = jobIDMax + 1 + } + + err = ns.Commit() + if err != nil { + return nil, err + } + return jobErrs, nil +} + +// PauseAllJobsBySystem pauses all running Jobs because of internal reasons. +func PauseAllJobsBySystem(se sessionctx.Context) (map[int64]error, error) { + return processAllJobs(pauseRunningJob, se, model.AdminCommandBySystem) +} + +// ResumeAllJobsBySystem resumes all paused Jobs because of internal reasons. +func ResumeAllJobsBySystem(se sessionctx.Context) (map[int64]error, error) { + return processAllJobs(resumePausedJob, se, model.AdminCommandBySystem) +} + +// GetAllDDLJobs get all DDL jobs and sorts jobs by job.ID. +func GetAllDDLJobs(se sessionctx.Context) ([]*model.Job, error) { + return getJobsBySQL(sess.NewSession(se), JobTable, "1 order by job_id") +} + +// DefNumHistoryJobs is default value of the default number of history job +const DefNumHistoryJobs = 10 + +const batchNumHistoryJobs = 128 + +// GetLastNHistoryDDLJobs returns the DDL history jobs and an error. +// The maximum count of history jobs is num. +func GetLastNHistoryDDLJobs(t *meta.Meta, maxNumJobs int) ([]*model.Job, error) { + iterator, err := GetLastHistoryDDLJobsIterator(t) + if err != nil { + return nil, errors.Trace(err) + } + return iterator.GetLastJobs(maxNumJobs, nil) +} + +// IterHistoryDDLJobs iterates history DDL jobs until the `finishFn` return true or error. +func IterHistoryDDLJobs(txn kv.Transaction, finishFn func([]*model.Job) (bool, error)) error { + txnMeta := meta.NewMeta(txn) + iter, err := GetLastHistoryDDLJobsIterator(txnMeta) + if err != nil { + return err + } + cacheJobs := make([]*model.Job, 0, DefNumHistoryJobs) + for { + cacheJobs, err = iter.GetLastJobs(DefNumHistoryJobs, cacheJobs) + if err != nil || len(cacheJobs) == 0 { + return err + } + finish, err := finishFn(cacheJobs) + if err != nil || finish { + return err + } + } +} + +// IterAllDDLJobs will iterates running DDL jobs first, return directly if `finishFn` return true or error, +// then iterates history DDL jobs until the `finishFn` return true or error. +func IterAllDDLJobs(ctx sessionctx.Context, txn kv.Transaction, finishFn func([]*model.Job) (bool, error)) error { + jobs, err := GetAllDDLJobs(ctx) + if err != nil { + return err + } + + finish, err := finishFn(jobs) + if err != nil || finish { + return err + } + return IterHistoryDDLJobs(txn, finishFn) +} + +// GetLastHistoryDDLJobsIterator gets latest N history DDL jobs iterator. +func GetLastHistoryDDLJobsIterator(m *meta.Meta) (meta.LastJobIterator, error) { + return m.GetLastHistoryDDLJobsIterator() +} + +// GetAllHistoryDDLJobs get all the done DDL jobs. +func GetAllHistoryDDLJobs(m *meta.Meta) ([]*model.Job, error) { + iterator, err := GetLastHistoryDDLJobsIterator(m) + if err != nil { + return nil, errors.Trace(err) + } + allJobs := make([]*model.Job, 0, batchNumHistoryJobs) + for { + jobs, err := iterator.GetLastJobs(batchNumHistoryJobs, nil) + if err != nil { + return nil, errors.Trace(err) + } + allJobs = append(allJobs, jobs...) + if len(jobs) < batchNumHistoryJobs { + break + } + } + // sort job. + slices.SortFunc(allJobs, func(i, j *model.Job) int { + return cmp.Compare(i.ID, j.ID) + }) + return allJobs, nil +} + +// ScanHistoryDDLJobs get some of the done DDL jobs. +// When the DDL history is quite large, GetAllHistoryDDLJobs() API can't work well, because it makes the server OOM. +// The result is in descending order by job ID. +func ScanHistoryDDLJobs(m *meta.Meta, startJobID int64, limit int) ([]*model.Job, error) { + var iter meta.LastJobIterator + var err error + if startJobID == 0 { + iter, err = m.GetLastHistoryDDLJobsIterator() + } else { + if limit == 0 { + return nil, errors.New("when 'start_job_id' is specified, it must work with a 'limit'") + } + iter, err = m.GetHistoryDDLJobsIterator(startJobID) + } + if err != nil { + return nil, errors.Trace(err) + } + return iter.GetLastJobs(limit, nil) +} + +// GetHistoryJobByID return history DDL job by ID. +func GetHistoryJobByID(sess sessionctx.Context, id int64) (*model.Job, error) { + err := sessiontxn.NewTxn(context.Background(), sess) + if err != nil { + return nil, err + } + defer func() { + // we can ignore the commit error because this txn is readonly. + _ = sess.CommitTxn(context.Background()) + }() + txn, err := sess.Txn(true) + if err != nil { + return nil, err + } + t := meta.NewMeta(txn) + job, err := t.GetHistoryDDLJob(id) + return job, errors.Trace(err) +} + +// AddHistoryDDLJob record the history job. +func AddHistoryDDLJob(sess *sess.Session, t *meta.Meta, job *model.Job, updateRawArgs bool) error { + err := addHistoryDDLJob2Table(sess, job, updateRawArgs) + if err != nil { + logutil.BgLogger().Info("failed to add DDL job to history table", zap.String("category", "ddl"), zap.Error(err)) + } + // we always add history DDL job to job list at this moment. + return t.AddHistoryDDLJob(job, updateRawArgs) +} + +// addHistoryDDLJob2Table adds DDL job to history table. +func addHistoryDDLJob2Table(sess *sess.Session, job *model.Job, updateRawArgs bool) error { + b, err := job.Encode(updateRawArgs) + if err != nil { + return err + } + _, err = sess.Execute(context.Background(), + fmt.Sprintf("insert ignore into mysql.tidb_ddl_history(job_id, job_meta, db_name, table_name, schema_ids, table_ids, create_time) values (%d, %s, %s, %s, %s, %s, %v)", + job.ID, util.WrapKey2String(b), strconv.Quote(job.SchemaName), strconv.Quote(job.TableName), + strconv.Quote(strconv.FormatInt(job.SchemaID, 10)), + strconv.Quote(strconv.FormatInt(job.TableID, 10)), + strconv.Quote(model.TSConvert2Time(job.StartTS).String())), + "insert_history") + return errors.Trace(err) +} diff --git a/ddl/ddl_algorithm.go b/pkg/ddl/ddl_algorithm.go similarity index 97% rename from ddl/ddl_algorithm.go rename to pkg/ddl/ddl_algorithm.go index acc061c3c27b9..137cc8d5fb771 100644 --- a/ddl/ddl_algorithm.go +++ b/pkg/ddl/ddl_algorithm.go @@ -17,8 +17,8 @@ package ddl import ( "fmt" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/dbterror" ) // AlterAlgorithm is used to store supported alter algorithm. diff --git a/ddl/ddl_algorithm_test.go b/pkg/ddl/ddl_algorithm_test.go similarity index 97% rename from ddl/ddl_algorithm_test.go rename to pkg/ddl/ddl_algorithm_test.go index 339582651de0e..42ca596a1b2f5 100644 --- a/ddl/ddl_algorithm_test.go +++ b/pkg/ddl/ddl_algorithm_test.go @@ -17,9 +17,9 @@ package ddl_test import ( "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) diff --git a/ddl/ddl_api.go b/pkg/ddl/ddl_api.go similarity index 99% rename from ddl/ddl_api.go rename to pkg/ddl/ddl_api.go index c8020085a5a16..c87bc650a8048 100644 --- a/ddl/ddl_api.go +++ b/pkg/ddl/ddl_api.go @@ -32,45 +32,45 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/resourcegroup" - ddlutil "github.com/pingcap/tidb/ddl/util" - rg "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - field_types "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/domainutil" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/resourcegroup" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + rg "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + field_types "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/domainutil" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/tikv/client-go/v2/oracle" kvutil "github.com/tikv/client-go/v2/util" "go.uber.org/zap" diff --git a/ddl/ddl_api_test.go b/pkg/ddl/ddl_api_test.go similarity index 95% rename from ddl/ddl_api_test.go rename to pkg/ddl/ddl_api_test.go index f2cd768e0247d..e6dec271e9fac 100644 --- a/ddl/ddl_api_test.go +++ b/pkg/ddl/ddl_api_test.go @@ -20,11 +20,11 @@ import ( "slices" "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/ddl_error_test.go b/pkg/ddl/ddl_error_test.go new file mode 100644 index 0000000000000..f09d60d9e3b01 --- /dev/null +++ b/pkg/ddl/ddl_error_test.go @@ -0,0 +1,182 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. +// + +package ddl_test + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +// This test file contains tests that test the expected or unexpected DDL error. +// For expected error, we use SQL to check it. +// For unexpected error, we mock a SQL job to check it. + +func TestTableError(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, testLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table testDrop(a int)") + // Schema ID is wrong, so dropping table is failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + err := tk.ExecToErr("drop table testDrop") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) + + // Table ID is wrong, so dropping table is failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobTableId", `return(-1)`)) + err = tk.ExecToErr("drop table testDrop") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobTableId")) + + // Args is wrong, so creating table is failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg", `return(true)`)) + err = tk.ExecToErr("create table test.t1(a int)") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg")) + + // Table exists, so creating table is failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + err = tk.ExecToErr("create table test.t1(a int)") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) + // Table exists, so creating table is failed. + tk.MustExec("create table test.t2(a int)") + tk.MustGetErrCode("create table test.t2(a int)", errno.ErrTableExists) +} + +func TestViewError(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, testLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + + // Args is wrong, so creating view is failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg", `return(true)`)) + err := tk.ExecToErr("create view v as select * from t") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg")) +} + +func TestForeignKeyError(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, testLease) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int, index(a))") + tk.MustExec("create table t1 (a int, FOREIGN KEY fk(a) REFERENCES t(a))") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + err := tk.ExecToErr("alter table t1 add foreign key idx(a) REFERENCES t(a)") + require.Error(t, err) + err = tk.ExecToErr("alter table t1 drop index fk") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) +} + +func TestIndexError(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, testLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + tk.MustExec("alter table t add index a(a)") + + // Schema ID is wrong. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + err := tk.ExecToErr("alter table t add index idx(a)") + require.Error(t, err) + err = tk.ExecToErr("alter table t1 drop a") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) + + // for adding index + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg", `return(true)`)) + err = tk.ExecToErr("alter table t add index idx(a)") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop index a") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg")) +} + +func TestColumnError(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, testLease) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int, aa int, ab int)") + tk.MustExec("alter table t add index a(a)") + + // Invalid schema ID. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + err := tk.ExecToErr("alter table t add column ta int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t add column ta int, add column tb int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa, drop column ab") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) + + // Invalid table ID. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobTableId", `return(-1)`)) + err = tk.ExecToErr("alter table t add column ta int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t add column ta int, add column tb int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa, drop column ab") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobTableId")) + + // Invalid argument. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg", `return(true)`)) + err = tk.ExecToErr("alter table t add column ta int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa") + require.Error(t, err) + err = tk.ExecToErr("alter table t add column ta int, add column tb int") + require.Error(t, err) + err = tk.ExecToErr("alter table t drop column aa, drop column ab") + require.Error(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockModifyJobArg")) + + tk.MustGetErrCode("alter table t add column c int after c5", errno.ErrBadField) + tk.MustGetErrCode("alter table t drop column c5", errno.ErrCantDropFieldOrKey) + tk.MustGetErrCode("alter table t add column c int after c5, add column d int", errno.ErrBadField) + tk.MustGetErrCode("alter table t drop column ab, drop column c5", errno.ErrCantDropFieldOrKey) +} + +func TestCreateDatabaseError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId", `return(-1)`)) + tk.MustExec("create database db1;") + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockModifyJobSchemaId")) +} diff --git a/pkg/ddl/ddl_test.go b/pkg/ddl/ddl_test.go new file mode 100644 index 0000000000000..7b8df8620df02 --- /dev/null +++ b/pkg/ddl/ddl_test.go @@ -0,0 +1,287 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +// DDLForTest exports for testing. +type DDLForTest interface { + // SetInterceptor sets the interceptor. + SetInterceptor(h Interceptor) + NewReorgCtx(jobID int64, rowCount int64) *reorgCtx + GetReorgCtx(jobID int64) *reorgCtx + RemoveReorgCtx(id int64) +} + +// SetInterceptor implements DDL.SetInterceptor interface. +func (d *ddl) SetInterceptor(i Interceptor) { + d.mu.Lock() + defer d.mu.Unlock() + + d.mu.interceptor = i +} + +// IsReorgCanceled exports for testing. +func (rc *reorgCtx) IsReorgCanceled() bool { + return rc.isReorgCanceled() +} + +// NewReorgCtx exports for testing. +func (d *ddl) NewReorgCtx(jobID int64, rowCount int64) *reorgCtx { + return d.newReorgCtx(jobID, rowCount) +} + +// GetReorgCtx exports for testing. +func (d *ddl) GetReorgCtx(jobID int64) *reorgCtx { + return d.getReorgCtx(jobID) +} + +// RemoveReorgCtx exports for testing. +func (d *ddl) RemoveReorgCtx(id int64) { + d.removeReorgCtx(id) +} + +// JobNeedGCForTest is only used for test. +var JobNeedGCForTest = jobNeedGC + +func createMockStore(t *testing.T) kv.Storage { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + return store +} + +func TestGetIntervalFromPolicy(t *testing.T) { + policy := []time.Duration{ + 1 * time.Second, + 2 * time.Second, + } + var ( + val time.Duration + changed bool + ) + + val, changed = getIntervalFromPolicy(policy, 0) + require.Equal(t, val, 1*time.Second) + require.True(t, changed) + + val, changed = getIntervalFromPolicy(policy, 1) + require.Equal(t, val, 2*time.Second) + require.True(t, changed) + + val, changed = getIntervalFromPolicy(policy, 2) + require.Equal(t, val, 2*time.Second) + require.False(t, changed) + + val, changed = getIntervalFromPolicy(policy, 3) + require.Equal(t, val, 2*time.Second) + require.False(t, changed) +} + +func colDefStrToFieldType(t *testing.T, str string, ctx sessionctx.Context) *types.FieldType { + sqlA := "alter table t modify column a " + str + stmt, err := parser.New().ParseOneStmt(sqlA, "", "") + require.NoError(t, err) + colDef := stmt.(*ast.AlterTableStmt).Specs[0].NewColumns[0] + chs, coll := charset.GetDefaultCharsetAndCollate() + col, _, err := buildColumnAndConstraint(ctx, 0, colDef, nil, chs, coll) + require.NoError(t, err) + return &col.FieldType +} + +func TestModifyColumn(t *testing.T) { + ctx := mock.NewContext() + tests := []struct { + origin string + to string + err error + }{ + {"int", "bigint", nil}, + {"int", "int unsigned", nil}, + {"varchar(10)", "text", nil}, + {"varbinary(10)", "blob", nil}, + {"text", "blob", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8mb4 to binary")}, + {"varchar(10)", "varchar(8)", nil}, + {"varchar(10)", "varchar(11)", nil}, + {"varchar(10) character set utf8 collate utf8_bin", "varchar(10) character set utf8", nil}, + {"decimal(2,1)", "decimal(3,2)", nil}, + {"decimal(2,1)", "decimal(2,2)", nil}, + {"decimal(2,1)", "decimal(2,1)", nil}, + {"decimal(2,1)", "int", nil}, + {"decimal", "int", nil}, + {"decimal(2,1)", "bigint", nil}, + {"int", "varchar(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from binary to gbk")}, + {"varchar(10) character set gbk", "int", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to binary")}, + {"varchar(10) character set gbk", "varchar(10) character set utf8", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to utf8")}, + {"varchar(10) character set gbk", "char(10) character set utf8", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from gbk to utf8")}, + {"varchar(10) character set utf8", "char(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8 to gbk")}, + {"varchar(10) character set utf8", "varchar(10) character set gbk", dbterror.ErrUnsupportedModifyCharset.GenWithStackByArgs("charset from utf8 to gbk")}, + {"varchar(10) character set gbk", "varchar(255) character set gbk", nil}, + } + for _, tt := range tests { + ftA := colDefStrToFieldType(t, tt.origin, ctx) + ftB := colDefStrToFieldType(t, tt.to, ctx) + err := checkModifyTypes(ftA, ftB, false) + if err == nil { + require.NoErrorf(t, tt.err, "origin:%v, to:%v", tt.origin, tt.to) + } else { + require.EqualError(t, err, tt.err.Error()) + } + } +} + +func TestFieldCase(t *testing.T) { + var fields = []string{"field", "Field"} + colObjects := make([]*model.ColumnInfo, len(fields)) + for i, name := range fields { + colObjects[i] = &model.ColumnInfo{ + Name: model.NewCIStr(name), + } + } + err := checkDuplicateColumn(colObjects) + require.EqualError(t, err, infoschema.ErrColumnExists.GenWithStackByArgs("Field").Error()) +} + +func TestIgnorableSpec(t *testing.T) { + specs := []ast.AlterTableType{ + ast.AlterTableOption, + ast.AlterTableAddColumns, + ast.AlterTableAddConstraint, + ast.AlterTableDropColumn, + ast.AlterTableDropPrimaryKey, + ast.AlterTableDropIndex, + ast.AlterTableDropForeignKey, + ast.AlterTableModifyColumn, + ast.AlterTableChangeColumn, + ast.AlterTableRenameTable, + ast.AlterTableAlterColumn, + } + for _, spec := range specs { + require.False(t, isIgnorableSpec(spec)) + } + + ignorableSpecs := []ast.AlterTableType{ + ast.AlterTableLock, + ast.AlterTableAlgorithm, + } + for _, spec := range ignorableSpecs { + require.True(t, isIgnorableSpec(spec)) + } +} + +func TestBuildJobDependence(t *testing.T) { + store := createMockStore(t) + defer func() { + require.NoError(t, store.Close()) + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + // Add some non-add-index jobs. + job1 := &model.Job{ID: 1, TableID: 1, Type: model.ActionAddColumn} + job2 := &model.Job{ID: 2, TableID: 1, Type: model.ActionCreateTable} + job3 := &model.Job{ID: 3, TableID: 2, Type: model.ActionDropColumn} + job6 := &model.Job{ID: 6, TableID: 1, Type: model.ActionDropTable} + job7 := &model.Job{ID: 7, TableID: 2, Type: model.ActionModifyColumn} + job9 := &model.Job{ID: 9, SchemaID: 111, Type: model.ActionDropSchema} + job11 := &model.Job{ID: 11, TableID: 2, Type: model.ActionRenameTable, Args: []interface{}{int64(111), "old db name"}} + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + require.NoError(t, m.EnQueueDDLJob(job1)) + require.NoError(t, m.EnQueueDDLJob(job2)) + require.NoError(t, m.EnQueueDDLJob(job3)) + require.NoError(t, m.EnQueueDDLJob(job6)) + require.NoError(t, m.EnQueueDDLJob(job7)) + require.NoError(t, m.EnQueueDDLJob(job9)) + require.NoError(t, m.EnQueueDDLJob(job11)) + return nil + }) + require.NoError(t, err) + job4 := &model.Job{ID: 4, TableID: 1, Type: model.ActionAddIndex} + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err := buildJobDependence(m, job4) + require.NoError(t, err) + require.Equal(t, job4.DependencyID, int64(2)) + return nil + }) + require.NoError(t, err) + job5 := &model.Job{ID: 5, TableID: 2, Type: model.ActionAddIndex} + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err := buildJobDependence(m, job5) + require.NoError(t, err) + require.Equal(t, job5.DependencyID, int64(3)) + return nil + }) + require.NoError(t, err) + job8 := &model.Job{ID: 8, TableID: 3, Type: model.ActionAddIndex} + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err := buildJobDependence(m, job8) + require.NoError(t, err) + require.Equal(t, job8.DependencyID, int64(0)) + return nil + }) + require.NoError(t, err) + job10 := &model.Job{ID: 10, SchemaID: 111, TableID: 3, Type: model.ActionAddIndex} + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err := buildJobDependence(m, job10) + require.NoError(t, err) + require.Equal(t, job10.DependencyID, int64(9)) + return nil + }) + require.NoError(t, err) + job12 := &model.Job{ID: 12, SchemaID: 112, TableID: 2, Type: model.ActionAddIndex} + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err := buildJobDependence(m, job12) + require.NoError(t, err) + require.Equal(t, job12.DependencyID, int64(11)) + return nil + }) + require.NoError(t, err) +} + +func TestError(t *testing.T) { + kvErrs := []*terror.Error{ + dbterror.ErrDDLJobNotFound, + dbterror.ErrCancelFinishedDDLJob, + dbterror.ErrCannotCancelDDLJob, + } + for _, err := range kvErrs { + code := terror.ToSQLError(err).Code + require.NotEqual(t, mysql.ErrUnknown, code) + require.Equal(t, uint16(err.Code()), code) + } +} diff --git a/ddl/ddl_tiflash_api.go b/pkg/ddl/ddl_tiflash_api.go similarity index 97% rename from ddl/ddl_tiflash_api.go rename to pkg/ddl/ddl_tiflash_api.go index c878f07c3648e..93b1ddb38ecda 100644 --- a/ddl/ddl_tiflash_api.go +++ b/pkg/ddl/ddl_tiflash_api.go @@ -30,16 +30,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" atomicutil "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/ddl/ddl_worker.go b/pkg/ddl/ddl_worker.go similarity index 98% rename from ddl/ddl_worker.go rename to pkg/ddl/ddl_worker.go index 2bc922d4991bb..603adbf500fe4 100644 --- a/ddl/ddl_worker.go +++ b/pkg/ddl/ddl_worker.go @@ -26,25 +26,25 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/tikv/client-go/v2/tikvrpc" kvutil "github.com/tikv/client-go/v2/util" clientv3 "go.etcd.io/etcd/client/v3" diff --git a/ddl/ddl_worker_test.go b/pkg/ddl/ddl_worker_test.go similarity index 96% rename from ddl/ddl_worker_test.go rename to pkg/ddl/ddl_worker_test.go index 27046004fcd20..9d6abc20dece7 100644 --- a/ddl/ddl_worker_test.go +++ b/pkg/ddl/ddl_worker_test.go @@ -22,12 +22,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) @@ -61,14 +61,14 @@ func TestAddBatchJobError(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) ctx := testNewContext(store) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockAddBatchDDLJobsErr", `return(true)`)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockAddBatchDDLJobsErr", `return(true)`)) // Test the job runner should not hang forever. job := &model.Job{SchemaID: 1, TableID: 1} ctx.SetValue(sessionctx.QueryString, "skip") err := dom.DDL().DoDDLJob(ctx, job) require.Error(t, err) require.Equal(t, err.Error(), "mockAddBatchDDLJobsErr") - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockAddBatchDDLJobsErr")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockAddBatchDDLJobsErr")) } func TestParallelDDL(t *testing.T) { diff --git a/ddl/ddl_workerpool.go b/pkg/ddl/ddl_workerpool.go similarity index 98% rename from ddl/ddl_workerpool.go rename to pkg/ddl/ddl_workerpool.go index cc32e7cc312d9..c02383c9ae0ca 100644 --- a/ddl/ddl_workerpool.go +++ b/pkg/ddl/ddl_workerpool.go @@ -17,7 +17,7 @@ package ddl import ( "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/ddl/ddl_workerpool_test.go b/pkg/ddl/ddl_workerpool_test.go similarity index 100% rename from ddl/ddl_workerpool_test.go rename to pkg/ddl/ddl_workerpool_test.go diff --git a/ddl/delete_range.go b/pkg/ddl/delete_range.go similarity index 97% rename from ddl/delete_range.go rename to pkg/ddl/delete_range.go index 04bcfc4e0f909..16de4f259b1d2 100644 --- a/ddl/delete_range.go +++ b/pkg/ddl/delete_range.go @@ -25,16 +25,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "go.uber.org/zap" ) diff --git a/ddl/delete_range_util.go b/pkg/ddl/delete_range_util.go similarity index 100% rename from ddl/delete_range_util.go rename to pkg/ddl/delete_range_util.go diff --git a/ddl/dist_owner.go b/pkg/ddl/dist_owner.go similarity index 100% rename from ddl/dist_owner.go rename to pkg/ddl/dist_owner.go diff --git a/ddl/export_test.go b/pkg/ddl/export_test.go similarity index 85% rename from ddl/export_test.go rename to pkg/ddl/export_test.go index 919a4b2c32049..1435c9876f9af 100644 --- a/ddl/export_test.go +++ b/pkg/ddl/export_test.go @@ -18,14 +18,14 @@ import ( "context" "time" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type resultChanForTest struct { diff --git a/pkg/ddl/fail_test.go b/pkg/ddl/fail_test.go new file mode 100644 index 0000000000000..64aa9b435709d --- /dev/null +++ b/pkg/ddl/fail_test.go @@ -0,0 +1,65 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. +package ddl_test + +import ( + "strconv" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestFailBeforeDecodeArgs(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t1 (c1 int, c2 int);") + tk.MustExec("insert t1 values (1, 2);") + + var tableID int64 + rs := tk.MustQuery("select TIDB_TABLE_ID from information_schema.tables where table_name='t1' and table_schema='test';") + tableIDi, _ := strconv.Atoi(rs.Rows()[0][0].(string)) + tableID = int64(tableIDi) + + d := dom.DDL() + tc := &callback.TestDDLCallback{Do: dom} + + first := true + stateCnt := 0 + tc.OnJobRunBeforeExported = func(job *model.Job) { + // It can be other schema states except failed schema state. + // This schema state can only appear once. + if job.SchemaState == model.StateWriteOnly { + stateCnt++ + } else if job.SchemaState == model.StateWriteReorganization { + if first { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/errorBeforeDecodeArgs", `return(true)`)) + first = false + } else { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/errorBeforeDecodeArgs")) + } + } + } + d.SetHook(tc) + defaultValue := int64(3) + jobID := testCreateColumn(tk, t, testkit.NewTestKit(t, store).Session(), tableID, "c3", "", defaultValue, dom) + // Make sure the schema state only appears once. + require.Equal(t, 1, stateCnt) + testCheckJobDone(t, store, jobID, true) +} diff --git a/pkg/ddl/foreign_key.go b/pkg/ddl/foreign_key.go new file mode 100644 index 0000000000000..ce3f17d1353cc --- /dev/null +++ b/pkg/ddl/foreign_key.go @@ -0,0 +1,729 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +func (w *worker) onCreateForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return ver, errors.Trace(err) + } + + var fkInfo model.FKInfo + var fkCheck bool + err = job.DecodeArgs(&fkInfo, &fkCheck) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + if job.IsRollingback() { + return dropForeignKey(d, t, job, tblInfo, fkInfo.Name) + } + switch job.SchemaState { + case model.StateNone: + err = checkAddForeignKeyValidInOwner(d, t, job.SchemaName, tblInfo, &fkInfo, fkCheck) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + fkInfo.State = model.StateWriteOnly + fkInfo.ID = allocateFKIndexID(tblInfo) + tblInfo.ForeignKeys = append(tblInfo.ForeignKeys, &fkInfo) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateWriteOnly + return ver, nil + case model.StateWriteOnly: + err = checkForeignKeyConstrain(w, job.SchemaName, tblInfo.Name.L, &fkInfo, fkCheck) + if err != nil { + job.State = model.JobStateRollingback + return ver, err + } + tblInfo.ForeignKeys[len(tblInfo.ForeignKeys)-1].State = model.StateWriteReorganization + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateWriteReorganization + case model.StateWriteReorganization: + tblInfo.ForeignKeys[len(tblInfo.ForeignKeys)-1].State = model.StatePublic + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.SchemaState = model.StatePublic + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + default: + return ver, dbterror.ErrInvalidDDLState.GenWithStack("foreign key", fkInfo.State) + } + return ver, nil +} + +func onDropForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return ver, errors.Trace(err) + } + + var fkName model.CIStr + err = job.DecodeArgs(&fkName) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + return dropForeignKey(d, t, job, tblInfo, fkName) +} + +func dropForeignKey(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, fkName model.CIStr) (ver int64, err error) { + var fkInfo *model.FKInfo + for _, fk := range tblInfo.ForeignKeys { + if fk.Name.L == fkName.L { + fkInfo = fk + break + } + } + if fkInfo == nil { + job.State = model.JobStateCancelled + return ver, infoschema.ErrForeignKeyNotExists.GenWithStackByArgs(fkName) + } + nfks := tblInfo.ForeignKeys[:0] + for _, fk := range tblInfo.ForeignKeys { + if fk.Name.L != fkName.L { + nfks = append(nfks, fk) + } + } + tblInfo.ForeignKeys = nfks + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + if job.IsRollingback() { + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + } else { + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + } + job.SchemaState = model.StateNone + return ver, err +} + +func allocateFKIndexID(tblInfo *model.TableInfo) int64 { + tblInfo.MaxForeignKeyID++ + return tblInfo.MaxForeignKeyID +} + +func checkTableForeignKeysValid(sctx sessionctx.Context, is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo) error { + if !variable.EnableForeignKey.Load() { + return nil + } + fkCheck := sctx.GetSessionVars().ForeignKeyChecks + for _, fk := range tbInfo.ForeignKeys { + if fk.Version < model.FKVersion1 { + continue + } + err := checkTableForeignKeyValid(is, schema, tbInfo, fk, fkCheck) + if err != nil { + return err + } + } + + referredFKInfos := is.GetTableReferredForeignKeys(schema, tbInfo.Name.L) + for _, referredFK := range referredFKInfos { + childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + return err + } + fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) + if fk == nil { + continue + } + err = checkTableForeignKey(tbInfo, childTable.Meta(), fk) + if err != nil { + return err + } + } + return nil +} + +func checkTableForeignKeyValid(is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { + var referTblInfo *model.TableInfo + if fk.RefSchema.L == schema && fk.RefTable.L == tbInfo.Name.L { + same := true + for i, col := range fk.Cols { + if col.L != fk.RefCols[i].L { + same = false + break + } + } + if same { + // self-reference with same columns is not support. + return infoschema.ErrCannotAddForeign + } + referTblInfo = tbInfo + } else { + referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) + if err != nil { + if (infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err)) && !fkCheck { + return nil + } + return infoschema.ErrForeignKeyCannotOpenParent.GenWithStackByArgs(fk.RefTable.O) + } + referTblInfo = referTable.Meta() + } + return checkTableForeignKey(referTblInfo, tbInfo, fk) +} + +func getAndCheckLatestInfoSchema(d *ddlCtx, _ *meta.Meta) (infoschema.InfoSchema, error) { + // TODO(crazycs520): fix me, need to make sure the `d.infoCache` is the latest infoschema. + return d.infoCache.GetLatest(), nil +} + +func checkTableForeignKeyValidInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, fkCheck bool) (retryable bool, _ error) { + if !variable.EnableForeignKey.Load() { + return false, nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return true, err + } + for _, fk := range tbInfo.ForeignKeys { + if fk.Version < model.FKVersion1 { + continue + } + var referTableInfo *model.TableInfo + if fk.RefSchema.L == job.SchemaName && fk.RefTable.L == tbInfo.Name.L { + referTableInfo = tbInfo + } else { + referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) + if err != nil { + if !fkCheck && (infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err)) { + continue + } + return false, err + } + referTableInfo = referTable.Meta() + } + + err := checkTableForeignKey(referTableInfo, tbInfo, fk) + if err != nil { + return false, err + } + } + referredFKInfos := is.GetTableReferredForeignKeys(job.SchemaName, tbInfo.Name.L) + for _, referredFK := range referredFKInfos { + childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + return false, err + } + fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) + if fk == nil { + continue + } + err = checkTableForeignKey(tbInfo, childTable.Meta(), fk) + if err != nil { + return false, err + } + } + return false, nil +} + +func checkTableForeignKey(referTblInfo, tblInfo *model.TableInfo, fkInfo *model.FKInfo) error { + if referTblInfo.TempTableType != model.TempTableNone || tblInfo.TempTableType != model.TempTableNone { + return infoschema.ErrCannotAddForeign + } + if referTblInfo.TTLInfo != nil { + return dbterror.ErrUnsupportedTTLReferencedByFK + } + if referTblInfo.GetPartitionInfo() != nil || tblInfo.GetPartitionInfo() != nil { + return infoschema.ErrForeignKeyOnPartitioned + } + + // check refer columns in parent table. + for i := range fkInfo.RefCols { + refCol := model.FindColumnInfo(referTblInfo.Columns, fkInfo.RefCols[i].L) + if refCol == nil { + return infoschema.ErrForeignKeyNoColumnInParent.GenWithStackByArgs(fkInfo.RefCols[i], fkInfo.Name, fkInfo.RefTable) + } + if refCol.IsGenerated() && !refCol.GeneratedStored { + return infoschema.ErrForeignKeyCannotUseVirtualColumn.GenWithStackByArgs(fkInfo.Name, fkInfo.RefCols[i]) + } + col := model.FindColumnInfo(tblInfo.Columns, fkInfo.Cols[i].L) + if col == nil { + return dbterror.ErrKeyColumnDoesNotExits.GenWithStackByArgs(fkInfo.Cols[i]) + } + if col.GetType() != refCol.GetType() || + mysql.HasUnsignedFlag(col.GetFlag()) != mysql.HasUnsignedFlag(refCol.GetFlag()) || + col.GetCharset() != refCol.GetCharset() || + col.GetCollate() != refCol.GetCollate() { + return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(col.Name, refCol.Name, fkInfo.Name) + } + if len(fkInfo.RefCols) == 1 && mysql.HasPriKeyFlag(refCol.GetFlag()) && referTblInfo.PKIsHandle { + return nil + } + } + // check refer columns should have index. + if model.FindIndexByColumns(referTblInfo, referTblInfo.Indices, fkInfo.RefCols...) == nil { + return infoschema.ErrForeignKeyNoIndexInParent.GenWithStackByArgs(fkInfo.Name, fkInfo.RefTable) + } + return nil +} + +func checkModifyColumnWithForeignKeyConstraint(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, originalCol, newCol *model.ColumnInfo) error { + if newCol.GetType() == originalCol.GetType() && newCol.GetFlen() == originalCol.GetFlen() && newCol.GetDecimal() == originalCol.GetDecimal() { + return nil + } + // WARN: is maybe nil. + if is == nil { + return nil + } + for _, fkInfo := range tbInfo.ForeignKeys { + for i, col := range fkInfo.Cols { + if col.L == originalCol.Name.L { + if !is.TableExists(fkInfo.RefSchema, fkInfo.RefTable) { + continue + } + referTable, err := is.TableByName(fkInfo.RefSchema, fkInfo.RefTable) + if err != nil { + return err + } + referCol := model.FindColumnInfo(referTable.Meta().Columns, fkInfo.RefCols[i].L) + if referCol == nil { + continue + } + if newCol.GetType() != referCol.GetType() { + return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(originalCol.Name, fkInfo.RefCols[i], fkInfo.Name) + } + if newCol.GetFlen() < referCol.GetFlen() || newCol.GetFlen() < originalCol.GetFlen() || + (newCol.GetType() == mysql.TypeNewDecimal && (newCol.GetFlen() != originalCol.GetFlen() || newCol.GetDecimal() != originalCol.GetDecimal())) { + return dbterror.ErrForeignKeyColumnCannotChange.GenWithStackByArgs(originalCol.Name, fkInfo.Name) + } + } + } + } + referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) + for _, referredFK := range referredFKs { + for i, col := range referredFK.Cols { + if col.L == originalCol.Name.L { + if !is.TableExists(referredFK.ChildSchema, referredFK.ChildTable) { + continue + } + childTblInfo, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + return err + } + fk := model.FindFKInfoByName(childTblInfo.Meta().ForeignKeys, referredFK.ChildFKName.L) + childCol := model.FindColumnInfo(childTblInfo.Meta().Columns, fk.Cols[i].L) + if childCol == nil { + continue + } + if newCol.GetType() != childCol.GetType() { + return dbterror.ErrFKIncompatibleColumns.GenWithStackByArgs(childCol.Name, originalCol.Name, referredFK.ChildFKName) + } + if newCol.GetFlen() < childCol.GetFlen() || newCol.GetFlen() < originalCol.GetFlen() || + (newCol.GetType() == mysql.TypeNewDecimal && (newCol.GetFlen() != childCol.GetFlen() || newCol.GetDecimal() != childCol.GetDecimal())) { + return dbterror.ErrForeignKeyColumnCannotChangeChild.GenWithStackByArgs(originalCol.Name, referredFK.ChildFKName, referredFK.ChildSchema.L+"."+referredFK.ChildTable.L) + } + } + } + } + + return nil +} + +func checkTableHasForeignKeyReferred(is infoschema.InfoSchema, schema, tbl string, ignoreTables []ast.Ident, fkCheck bool) *model.ReferredFKInfo { + if !fkCheck { + return nil + } + referredFKs := is.GetTableReferredForeignKeys(schema, tbl) + for _, referredFK := range referredFKs { + found := false + for _, tb := range ignoreTables { + if referredFK.ChildSchema.L == tb.Schema.L && referredFK.ChildTable.L == tb.Name.L { + found = true + break + } + } + if found { + continue + } + if is.TableExists(referredFK.ChildSchema, referredFK.ChildTable) { + return referredFK + } + } + return nil +} + +func checkDropTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job) error { + if !variable.EnableForeignKey.Load() { + return nil + } + var objectIdents []ast.Ident + var fkCheck bool + err := job.DecodeArgs(&objectIdents, &fkCheck) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + referredFK, err := checkTableHasForeignKeyReferredInOwner(d, t, job.SchemaName, job.TableName, objectIdents, fkCheck) + if err != nil { + return err + } + if referredFK != nil { + job.State = model.JobStateCancelled + msg := fmt.Sprintf("`%s`.`%s` CONSTRAINT `%s`", referredFK.ChildSchema, referredFK.ChildTable, referredFK.ChildFKName) + return errors.Trace(dbterror.ErrTruncateIllegalForeignKey.GenWithStackByArgs(msg)) + } + return nil +} + +func checkTruncateTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, fkCheck bool) error { + referredFK, err := checkTableHasForeignKeyReferredInOwner(d, t, job.SchemaName, job.TableName, []ast.Ident{{Name: tblInfo.Name, Schema: model.NewCIStr(job.SchemaName)}}, fkCheck) + if err != nil { + return err + } + if referredFK != nil { + job.State = model.JobStateCancelled + msg := fmt.Sprintf("`%s`.`%s` CONSTRAINT `%s`", referredFK.ChildSchema, referredFK.ChildTable, referredFK.ChildFKName) + return errors.Trace(dbterror.ErrTruncateIllegalForeignKey.GenWithStackByArgs(msg)) + } + return nil +} + +func checkTableHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, schema, tbl string, ignoreTables []ast.Ident, fkCheck bool) (_ *model.ReferredFKInfo, _ error) { + if !variable.EnableForeignKey.Load() { + return nil, nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return nil, err + } + referredFK := checkTableHasForeignKeyReferred(is, schema, tbl, ignoreTables, fkCheck) + return referredFK, nil +} + +func checkIndexNeededInForeignKey(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, idxInfo *model.IndexInfo) error { + referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) + if len(tbInfo.ForeignKeys) == 0 && len(referredFKs) == 0 { + return nil + } + remainIdxs := make([]*model.IndexInfo, 0, len(tbInfo.Indices)) + for _, idx := range tbInfo.Indices { + if idx.ID == idxInfo.ID { + continue + } + remainIdxs = append(remainIdxs, idx) + } + checkFn := func(cols []model.CIStr) error { + if !model.IsIndexPrefixCovered(tbInfo, idxInfo, cols...) { + return nil + } + if tbInfo.PKIsHandle && len(cols) == 1 { + refColInfo := model.FindColumnInfo(tbInfo.Columns, cols[0].L) + if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { + return nil + } + } + for _, index := range remainIdxs { + if model.IsIndexPrefixCovered(tbInfo, index, cols...) { + return nil + } + } + return dbterror.ErrDropIndexNeededInForeignKey.GenWithStackByArgs(idxInfo.Name) + } + for _, fk := range tbInfo.ForeignKeys { + if fk.Version < model.FKVersion1 { + continue + } + err := checkFn(fk.Cols) + if err != nil { + return err + } + } + for _, referredFK := range referredFKs { + err := checkFn(referredFK.Cols) + if err != nil { + return err + } + } + return nil +} + +func checkIndexNeededInForeignKeyInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, dbName string, tbInfo *model.TableInfo, idxInfo *model.IndexInfo) error { + if !variable.EnableForeignKey.Load() { + return nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return err + } + err = checkIndexNeededInForeignKey(is, dbName, tbInfo, idxInfo) + if err != nil { + job.State = model.JobStateCancelled + return err + } + return nil +} + +func checkDropColumnWithForeignKeyConstraint(is infoschema.InfoSchema, dbName string, tbInfo *model.TableInfo, colName string) error { + for _, fkInfo := range tbInfo.ForeignKeys { + for _, col := range fkInfo.Cols { + if col.L == colName { + return dbterror.ErrFkColumnCannotDrop.GenWithStackByArgs(colName, fkInfo.Name) + } + } + } + referredFKs := is.GetTableReferredForeignKeys(dbName, tbInfo.Name.L) + for _, referredFK := range referredFKs { + for _, col := range referredFK.Cols { + if col.L == colName { + return dbterror.ErrFkColumnCannotDropChild.GenWithStackByArgs(colName, referredFK.ChildFKName, referredFK.ChildTable) + } + } + } + return nil +} + +func checkDropColumnWithForeignKeyConstraintInOwner(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, colName string) error { + if !variable.EnableForeignKey.Load() { + return nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return errors.Trace(err) + } + err = checkDropColumnWithForeignKeyConstraint(is, job.SchemaName, tbInfo, colName) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + return nil +} + +type foreignKeyHelper struct { + loaded map[schemaAndTable]schemaIDAndTableInfo +} + +type schemaAndTable struct { + schema string + table string +} + +func newForeignKeyHelper() foreignKeyHelper { + return foreignKeyHelper{loaded: make(map[schemaAndTable]schemaIDAndTableInfo)} +} + +func (h *foreignKeyHelper) addLoadedTable(schemaName, tableName string, schemaID int64, tblInfo *model.TableInfo) { + k := schemaAndTable{schema: schemaName, table: tableName} + h.loaded[k] = schemaIDAndTableInfo{schemaID: schemaID, tblInfo: tblInfo} +} + +func (h *foreignKeyHelper) getLoadedTables() []schemaIDAndTableInfo { + tableList := make([]schemaIDAndTableInfo, 0, len(h.loaded)) + for _, info := range h.loaded { + tableList = append(tableList, info) + } + return tableList +} + +func (h *foreignKeyHelper) getTableFromStorage(is infoschema.InfoSchema, t *meta.Meta, schema, table model.CIStr) (result schemaIDAndTableInfo, _ error) { + k := schemaAndTable{schema: schema.L, table: table.L} + if info, ok := h.loaded[k]; ok { + return info, nil + } + db, ok := is.SchemaByName(schema) + if !ok { + return result, errors.Trace(infoschema.ErrDatabaseNotExists.GenWithStackByArgs(schema)) + } + tb, err := is.TableByName(schema, table) + if err != nil { + return result, errors.Trace(err) + } + tbInfo, err := getTableInfo(t, tb.Meta().ID, db.ID) + if err != nil { + return result, errors.Trace(err) + } + result.schemaID, result.tblInfo = db.ID, tbInfo + h.loaded[k] = result + return result, nil +} + +func checkDatabaseHasForeignKeyReferred(is infoschema.InfoSchema, schema model.CIStr, fkCheck bool) error { + if !fkCheck { + return nil + } + tables := is.SchemaTables(schema) + tableNames := make([]ast.Ident, len(tables)) + for i := range tables { + tableNames[i] = ast.Ident{Schema: schema, Name: tables[i].Meta().Name} + } + for _, tbl := range tables { + if referredFK := checkTableHasForeignKeyReferred(is, schema.L, tbl.Meta().Name.L, tableNames, fkCheck); referredFK != nil { + return errors.Trace(dbterror.ErrForeignKeyCannotDropParent.GenWithStackByArgs(tbl.Meta().Name, referredFK.ChildFKName, referredFK.ChildTable)) + } + } + return nil +} + +func checkDatabaseHasForeignKeyReferredInOwner(d *ddlCtx, t *meta.Meta, job *model.Job) error { + if !variable.EnableForeignKey.Load() { + return nil + } + var fkCheck bool + err := job.DecodeArgs(&fkCheck) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + if !fkCheck { + return nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return errors.Trace(err) + } + err = checkDatabaseHasForeignKeyReferred(is, model.NewCIStr(job.SchemaName), fkCheck) + if err != nil { + job.State = model.JobStateCancelled + } + return errors.Trace(err) +} + +func checkFKDupName(tbInfo *model.TableInfo, fkName model.CIStr) error { + for _, fkInfo := range tbInfo.ForeignKeys { + if fkName.L == fkInfo.Name.L { + return dbterror.ErrFkDupName.GenWithStackByArgs(fkName.O) + } + } + return nil +} + +func checkAddForeignKeyValid(is infoschema.InfoSchema, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { + if !variable.EnableForeignKey.Load() { + return nil + } + err := checkTableForeignKeyValid(is, schema, tbInfo, fk, fkCheck) + if err != nil { + return err + } + return nil +} + +func checkAddForeignKeyValidInOwner(d *ddlCtx, t *meta.Meta, schema string, tbInfo *model.TableInfo, fk *model.FKInfo, fkCheck bool) error { + err := checkFKDupName(tbInfo, fk.Name) + if err != nil { + return err + } + if !variable.EnableForeignKey.Load() { + return nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return errors.Trace(err) + } + err = checkAddForeignKeyValid(is, schema, tbInfo, fk, fkCheck) + if err != nil { + return errors.Trace(err) + } + // check foreign key columns should have index. + if len(fk.Cols) == 1 && tbInfo.PKIsHandle { + pkCol := tbInfo.GetPkColInfo() + if pkCol != nil && pkCol.Name.L == fk.Cols[0].L { + return nil + } + } + if model.FindIndexByColumns(tbInfo, tbInfo.Indices, fk.Cols...) == nil { + return errors.Errorf("Failed to add the foreign key constraint. Missing index for '%s' foreign key columns in the table '%s'", fk.Name, tbInfo.Name) + } + return nil +} + +func checkForeignKeyConstrain(w *worker, schema, table string, fkInfo *model.FKInfo, fkCheck bool) error { + if !fkCheck { + return nil + } + sctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + originValue := sctx.GetSessionVars().OptimizerEnableNAAJ + sctx.GetSessionVars().OptimizerEnableNAAJ = true + defer func() { + sctx.GetSessionVars().OptimizerEnableNAAJ = originValue + w.sessPool.Put(sctx) + }() + + var buf strings.Builder + buf.WriteString("select 1 from %n.%n where ") + paramsList := make([]interface{}, 0, 4+len(fkInfo.Cols)*2) + paramsList = append(paramsList, schema, table) + for i, col := range fkInfo.Cols { + if i == 0 { + buf.WriteString("%n is not null") + paramsList = append(paramsList, col.L) + } else { + buf.WriteString(" and %n is not null") + paramsList = append(paramsList, col.L) + } + } + buf.WriteString(" and (") + for i, col := range fkInfo.Cols { + if i == 0 { + buf.WriteString("%n") + } else { + buf.WriteString(",%n") + } + paramsList = append(paramsList, col.L) + } + buf.WriteString(") not in (select ") + for i, col := range fkInfo.RefCols { + if i == 0 { + buf.WriteString("%n") + } else { + buf.WriteString(",%n") + } + paramsList = append(paramsList, col.L) + } + buf.WriteString(" from %n.%n ) limit 1") + paramsList = append(paramsList, fkInfo.RefSchema.L, fkInfo.RefTable.L) + rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(w.ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, buf.String(), paramsList...) + if err != nil { + return errors.Trace(err) + } + rowCount := len(rows) + if rowCount != 0 { + return dbterror.ErrNoReferencedRow2.GenWithStackByArgs(fkInfo.String(schema, table)) + } + return nil +} diff --git a/pkg/ddl/foreign_key_test.go b/pkg/ddl/foreign_key_test.go new file mode 100644 index 0000000000000..2fa7cbd248856 --- /dev/null +++ b/pkg/ddl/foreign_key_test.go @@ -0,0 +1,432 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func testCreateForeignKey(t *testing.T, d ddl.DDL, ctx sessionctx.Context, dbInfo *model.DBInfo, tblInfo *model.TableInfo, fkName string, keys []string, refTable string, refKeys []string, onDelete model.ReferOptionType, onUpdate model.ReferOptionType) *model.Job { + FKName := model.NewCIStr(fkName) + Keys := make([]model.CIStr, len(keys)) + for i, key := range keys { + Keys[i] = model.NewCIStr(key) + } + + RefTable := model.NewCIStr(refTable) + RefKeys := make([]model.CIStr, len(refKeys)) + for i, key := range refKeys { + RefKeys[i] = model.NewCIStr(key) + } + + fkInfo := &model.FKInfo{ + Name: FKName, + RefTable: RefTable, + RefCols: RefKeys, + Cols: Keys, + OnDelete: int(onDelete), + OnUpdate: int(onUpdate), + State: model.StateNone, + } + + job := &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionAddForeignKey, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{fkInfo}, + } + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.NoError(t, err) + return job +} + +func testDropForeignKey(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, foreignKeyName string) *model.Job { + job := &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionDropForeignKey, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{model.NewCIStr(foreignKeyName)}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + return job +} + +func getForeignKey(t table.Table, name string) *model.FKInfo { + for _, fk := range t.Meta().ForeignKeys { + // only public foreign key can be read. + if fk.State != model.StatePublic { + continue + } + if fk.Name.L == strings.ToLower(name) { + return fk + } + } + return nil +} + +func TestForeignKey(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := dom.DDL() + dbInfo, err := testSchemaInfo(store, "test_foreign") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), dom.DDL(), dbInfo) + tblInfo, err := testTableInfo(store, "t", 3) + require.NoError(t, err) + tblInfo.Indices = append(tblInfo.Indices, &model.IndexInfo{ + ID: 1, + Name: model.NewCIStr("idx_fk"), + Table: model.NewCIStr("t"), + Columns: []*model.IndexColumn{{ + Name: model.NewCIStr("c1"), + Offset: 0, + Length: types.UnspecifiedLength, + }}, + State: model.StatePublic, + }) + testCreateTable(t, testkit.NewTestKit(t, store).Session(), d, dbInfo, tblInfo) + + // fix data race + var mu sync.Mutex + checkOK := false + var hookErr error + tc := &callback.TestDDLCallback{} + onJobUpdatedExportedFunc := func(job *model.Job) { + if job.State != model.JobStateDone { + return + } + mu.Lock() + defer mu.Unlock() + var t table.Table + t, err = testGetTableWithError(store, dbInfo.ID, tblInfo.ID) + if err != nil { + hookErr = errors.Trace(err) + return + } + fk := getForeignKey(t, "c1_fk") + if fk == nil { + hookErr = errors.New("foreign key not exists") + return + } + checkOK = true + } + tc.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + ctx := testkit.NewTestKit(t, store).Session() + job := testCreateForeignKey(t, d, ctx, dbInfo, tblInfo, "c1_fk", []string{"c1"}, "t2", []string{"c1"}, model.ReferOptionCascade, model.ReferOptionSetNull) + testCheckJobDone(t, store, job.ID, true) + require.NoError(t, err) + mu.Lock() + hErr := hookErr + ok := checkOK + mu.Unlock() + require.NoError(t, hErr) + require.True(t, ok) + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + + mu.Lock() + checkOK = false + mu.Unlock() + // fix data race pr/#9491 + tc2 := &callback.TestDDLCallback{} + onJobUpdatedExportedFunc2 := func(job *model.Job) { + if job.State != model.JobStateDone { + return + } + mu.Lock() + defer mu.Unlock() + var t table.Table + t, err = testGetTableWithError(store, dbInfo.ID, tblInfo.ID) + if err != nil { + hookErr = errors.Trace(err) + return + } + fk := getForeignKey(t, "c1_fk") + if fk != nil { + hookErr = errors.New("foreign key has not been dropped") + return + } + checkOK = true + } + tc2.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc2) + d.SetHook(tc2) + + job = testDropForeignKey(t, ctx, d, dbInfo, tblInfo, "c1_fk") + testCheckJobDone(t, store, job.ID, false) + mu.Lock() + hErr = hookErr + ok = checkOK + mu.Unlock() + require.NoError(t, hErr) + require.True(t, ok) + d.SetHook(originalHook) + + tk := testkit.NewTestKit(t, store) + jobID := testDropTable(tk, t, dbInfo.Name.L, tblInfo.Name.L, dom) + testCheckJobDone(t, store, jobID, false) + + require.NoError(t, err) +} + +func TestTruncateOrDropTableWithForeignKeyReferred2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := dom.DDL() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@global.tidb_enable_foreign_key=1") + tk2.MustExec("set @@foreign_key_checks=1;") + tk2.MustExec("use test") + + tk.MustExec("create table t1 (id int key, a int);") + + var wg sync.WaitGroup + var truncateErr, dropErr error + testTruncate := true + tc := &callback.TestDDLCallback{} + tc.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateNone { + return + } + if job.Type != model.ActionCreateTable { + return + } + wg.Add(1) + if testTruncate { + go func() { + defer wg.Done() + truncateErr = tk2.ExecToErr("truncate table t1") + }() + } else { + go func() { + defer wg.Done() + dropErr = tk2.ExecToErr("drop table t1") + }() + } + // make sure tk2's ddl job already put into ddl job queue. + time.Sleep(time.Millisecond * 100) + } + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + tk.MustExec("create table t2 (a int, b int, foreign key fk(b) references t1(id));") + wg.Wait() + require.Error(t, truncateErr) + require.Equal(t, "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk`)", truncateErr.Error()) + + tk.MustExec("drop table t2") + testTruncate = false + tk.MustExec("create table t2 (a int, b int, foreign key fk(b) references t1(id));") + wg.Wait() + require.Error(t, dropErr) + require.Equal(t, "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk`)", dropErr.Error()) +} + +func TestDropIndexNeededInForeignKey2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := dom.DDL() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@global.tidb_enable_foreign_key=1") + tk2.MustExec("set @@foreign_key_checks=1;") + tk2.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int)") + tk.MustExec("create table t2 (a int, b int, index idx1 (b),index idx2 (b), foreign key (b) references t1(id));") + + var wg sync.WaitGroup + var dropErr error + tc := &callback.TestDDLCallback{} + tc.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StatePublic || job.Type != model.ActionDropIndex { + return + } + wg.Add(1) + go func() { + defer wg.Done() + dropErr = tk2.ExecToErr("alter table t2 drop index idx2") + }() + // make sure tk2's ddl job already put into ddl job queue. + time.Sleep(time.Millisecond * 100) + } + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + tk.MustExec("alter table t2 drop index idx1") + wg.Wait() + require.Error(t, dropErr) + require.Equal(t, "[ddl:1553]Cannot drop index 'idx2': needed in a foreign key constraint", dropErr.Error()) +} + +func TestDropDatabaseWithForeignKeyReferred2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := dom.DDL() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@global.tidb_enable_foreign_key=1") + tk2.MustExec("set @@foreign_key_checks=1;") + tk2.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") + tk.MustExec("create database test2") + var wg sync.WaitGroup + var dropErr error + tc := &callback.TestDDLCallback{} + tc.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StateNone { + return + } + if job.Type != model.ActionCreateTable { + return + } + wg.Add(1) + go func() { + defer wg.Done() + dropErr = tk2.ExecToErr("drop database test") + }() + // make sure tk2's ddl job already put into ddl job queue. + time.Sleep(time.Millisecond * 100) + } + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + tk.MustExec("create table test2.t3 (id int key, b int, foreign key fk_b(b) references test.t2(id));") + wg.Wait() + require.Error(t, dropErr) + require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", dropErr.Error()) + tk.MustExec("drop table test2.t3") + tk.MustExec("drop database test") +} + +func TestAddForeignKey2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := dom.DDL() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int, index(b));") + var wg sync.WaitGroup + var addErr error + tc := &callback.TestDDLCallback{} + tc.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState != model.StatePublic || job.Type != model.ActionDropIndex { + return + } + wg.Add(1) + go func() { + defer wg.Done() + addErr = tk2.ExecToErr("alter table t2 add foreign key (b) references t1(id);") + }() + // make sure tk2's ddl job already put into ddl job queue. + time.Sleep(time.Millisecond * 100) + } + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + tk.MustExec("alter table t2 drop index b") + wg.Wait() + require.Error(t, addErr) + require.Equal(t, "[ddl:-1]Failed to add the foreign key constraint. Missing index for 'fk_1' foreign key columns in the table 't2'", addErr.Error()) +} + +func TestAddForeignKey3(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := dom.DDL() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int, b int, index(id), index(b));") + tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3)") + tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 3)") + + var insertErrs []error + var deleteErrs []error + tc := &callback.TestDDLCallback{} + tc.OnJobRunBeforeExported = func(job *model.Job) { + if job.Type != model.ActionAddForeignKey { + return + } + if job.SchemaState == model.StateWriteOnly || job.SchemaState == model.StateWriteReorganization { + err := tk2.ExecToErr("insert into t2 values (10, 10)") + insertErrs = append(insertErrs, err) + err = tk2.ExecToErr("delete from t1 where id = 1") + deleteErrs = append(deleteErrs, err) + } + } + originalHook := d.GetHook() + defer d.SetHook(originalHook) + d.SetHook(tc) + + tk.MustExec("alter table t2 add foreign key (id) references t1(id) on delete cascade") + require.Equal(t, 2, len(insertErrs)) + for _, err := range insertErrs { + require.Error(t, err) + require.Equal(t, "[planner:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`) ON DELETE CASCADE)", err.Error()) + } + for _, err := range deleteErrs { + require.Error(t, err) + require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_1` FOREIGN KEY (`id`) REFERENCES `t1` (`id`) ON DELETE CASCADE)", err.Error()) + } + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1 1", "2 2", "3 3")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 1", "2 2", "3 3")) +} diff --git a/ddl/generated_column.go b/pkg/ddl/generated_column.go similarity index 97% rename from ddl/generated_column.go rename to pkg/ddl/generated_column.go index da14b09a438df..098e3609af9ce 100644 --- a/ddl/generated_column.go +++ b/pkg/ddl/generated_column.go @@ -18,15 +18,15 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/dbterror" ) // columnGenerationInDDL is a struct for validating generated columns in DDL. diff --git a/pkg/ddl/index.go b/pkg/ddl/index.go new file mode 100644 index 0000000000000..01fae3dfcee05 --- /dev/null +++ b/pkg/ddl/index.go @@ -0,0 +1,2515 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "bytes" + "cmp" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/backoff" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/prometheus/client_golang/prometheus" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + kvutil "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" +) + +const ( + // MaxCommentLength is exported for testing. + MaxCommentLength = 1024 +) + +var ( + telemetryAddIndexIngestUsage = metrics.TelemetryAddIndexIngestCnt +) + +func buildIndexColumns(ctx sessionctx.Context, columns []*model.ColumnInfo, indexPartSpecifications []*ast.IndexPartSpecification) ([]*model.IndexColumn, bool, error) { + // Build offsets. + idxParts := make([]*model.IndexColumn, 0, len(indexPartSpecifications)) + var col *model.ColumnInfo + var mvIndex bool + maxIndexLength := config.GetGlobalConfig().MaxIndexLength + // The sum of length of all index columns. + sumLength := 0 + for _, ip := range indexPartSpecifications { + col = model.FindColumnInfo(columns, ip.Column.Name.L) + if col == nil { + return nil, false, dbterror.ErrKeyColumnDoesNotExits.GenWithStack("column does not exist: %s", ip.Column.Name) + } + + if err := checkIndexColumn(ctx, col, ip.Length); err != nil { + return nil, false, err + } + if col.FieldType.IsArray() { + if mvIndex { + return nil, false, dbterror.ErrNotSupportedYet.GenWithStackByArgs("more than one multi-valued key part per index") + } + mvIndex = true + } + indexColLen := ip.Length + indexColumnLength, err := getIndexColumnLength(col, ip.Length) + if err != nil { + return nil, false, err + } + sumLength += indexColumnLength + + // The sum of all lengths must be shorter than the max length for prefix. + if sumLength > maxIndexLength { + // The multiple column index and the unique index in which the length sum exceeds the maximum size + // will return an error instead produce a warning. + if ctx == nil || ctx.GetSessionVars().StrictSQLMode || mysql.HasUniKeyFlag(col.GetFlag()) || len(indexPartSpecifications) > 1 { + return nil, false, dbterror.ErrTooLongKey.GenWithStackByArgs(sumLength, maxIndexLength) + } + // truncate index length and produce warning message in non-restrict sql mode. + colLenPerUint, err := getIndexColumnLength(col, 1) + if err != nil { + return nil, false, err + } + indexColLen = maxIndexLength / colLenPerUint + // produce warning message + ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrTooLongKey.FastGenByArgs(sumLength, maxIndexLength)) + } + + idxParts = append(idxParts, &model.IndexColumn{ + Name: col.Name, + Offset: col.Offset, + Length: indexColLen, + }) + } + + return idxParts, mvIndex, nil +} + +// CheckPKOnGeneratedColumn checks the specification of PK is valid. +func CheckPKOnGeneratedColumn(tblInfo *model.TableInfo, indexPartSpecifications []*ast.IndexPartSpecification) (*model.ColumnInfo, error) { + var lastCol *model.ColumnInfo + for _, colName := range indexPartSpecifications { + lastCol = tblInfo.FindPublicColumnByName(colName.Column.Name.L) + if lastCol == nil { + return nil, dbterror.ErrKeyColumnDoesNotExits.GenWithStackByArgs(colName.Column.Name) + } + // Virtual columns cannot be used in primary key. + if lastCol.IsGenerated() && !lastCol.GeneratedStored { + if lastCol.Hidden { + return nil, dbterror.ErrFunctionalIndexPrimaryKey + } + return nil, dbterror.ErrUnsupportedOnGeneratedColumn.GenWithStackByArgs("Defining a virtual generated column as primary key") + } + } + + return lastCol, nil +} + +func checkIndexPrefixLength(columns []*model.ColumnInfo, idxColumns []*model.IndexColumn) error { + idxLen, err := indexColumnsLen(columns, idxColumns) + if err != nil { + return err + } + if idxLen > config.GetGlobalConfig().MaxIndexLength { + return dbterror.ErrTooLongKey.GenWithStackByArgs(idxLen, config.GetGlobalConfig().MaxIndexLength) + } + return nil +} + +func checkIndexColumn(ctx sessionctx.Context, col *model.ColumnInfo, indexColumnLen int) error { + if col.GetFlen() == 0 && (types.IsTypeChar(col.FieldType.GetType()) || types.IsTypeVarchar(col.FieldType.GetType())) { + if col.Hidden { + return errors.Trace(dbterror.ErrWrongKeyColumnFunctionalIndex.GenWithStackByArgs(col.GeneratedExprString)) + } + return errors.Trace(dbterror.ErrWrongKeyColumn.GenWithStackByArgs(col.Name)) + } + + // JSON column cannot index. + if col.FieldType.GetType() == mysql.TypeJSON && !col.FieldType.IsArray() { + if col.Hidden { + return dbterror.ErrFunctionalIndexOnJSONOrGeometryFunction + } + return errors.Trace(dbterror.ErrJSONUsedAsKey.GenWithStackByArgs(col.Name.O)) + } + + // Length must be specified and non-zero for BLOB and TEXT column indexes. + if types.IsTypeBlob(col.FieldType.GetType()) { + if indexColumnLen == types.UnspecifiedLength { + if col.Hidden { + return dbterror.ErrFunctionalIndexOnBlob + } + return errors.Trace(dbterror.ErrBlobKeyWithoutLength.GenWithStackByArgs(col.Name.O)) + } + if indexColumnLen == types.ErrorLength { + return errors.Trace(dbterror.ErrKeyPart0.GenWithStackByArgs(col.Name.O)) + } + } + + // Length can only be specified for specifiable types. + if indexColumnLen != types.UnspecifiedLength && !types.IsTypePrefixable(col.FieldType.GetType()) { + return errors.Trace(dbterror.ErrIncorrectPrefixKey) + } + + // Key length must be shorter or equal to the column length. + if indexColumnLen != types.UnspecifiedLength && + types.IsTypeChar(col.FieldType.GetType()) { + if col.GetFlen() < indexColumnLen { + return errors.Trace(dbterror.ErrIncorrectPrefixKey) + } + // Length must be non-zero for char. + if indexColumnLen == types.ErrorLength { + return errors.Trace(dbterror.ErrKeyPart0.GenWithStackByArgs(col.Name.O)) + } + } + + if types.IsString(col.FieldType.GetType()) { + desc, err := charset.GetCharsetInfo(col.GetCharset()) + if err != nil { + return err + } + indexColumnLen *= desc.Maxlen + } + // Specified length must be shorter than the max length for prefix. + maxIndexLength := config.GetGlobalConfig().MaxIndexLength + if indexColumnLen > maxIndexLength && (ctx == nil || ctx.GetSessionVars().StrictSQLMode) { + // return error in strict sql mode + return dbterror.ErrTooLongKey.GenWithStackByArgs(indexColumnLen, maxIndexLength) + } + return nil +} + +// getIndexColumnLength calculate the bytes number required in an index column. +func getIndexColumnLength(col *model.ColumnInfo, colLen int) (int, error) { + length := types.UnspecifiedLength + if colLen != types.UnspecifiedLength { + length = colLen + } else if col.GetFlen() != types.UnspecifiedLength { + length = col.GetFlen() + } + + switch col.GetType() { + case mysql.TypeBit: + return (length + 7) >> 3, nil + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob: + // Different charsets occupy different numbers of bytes on each character. + desc, err := charset.GetCharsetInfo(col.GetCharset()) + if err != nil { + return 0, dbterror.ErrUnsupportedCharset.GenWithStackByArgs(col.GetCharset(), col.GetCollate()) + } + return desc.Maxlen * length, nil + case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeShort: + return mysql.DefaultLengthOfMysqlTypes[col.GetType()], nil + case mysql.TypeFloat: + if length <= mysql.MaxFloatPrecisionLength { + return mysql.DefaultLengthOfMysqlTypes[mysql.TypeFloat], nil + } + return mysql.DefaultLengthOfMysqlTypes[mysql.TypeDouble], nil + case mysql.TypeNewDecimal: + return calcBytesLengthForDecimal(length), nil + case mysql.TypeYear, mysql.TypeDate, mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeTimestamp: + return mysql.DefaultLengthOfMysqlTypes[col.GetType()], nil + default: + return length, nil + } +} + +// decimal using a binary format that packs nine decimal (base 10) digits into four bytes. +func calcBytesLengthForDecimal(m int) int { + return (m / 9 * 4) + ((m%9)+1)/2 +} + +// BuildIndexInfo builds a new IndexInfo according to the index information. +func BuildIndexInfo( + ctx sessionctx.Context, + allTableColumns []*model.ColumnInfo, + indexName model.CIStr, + isPrimary bool, + isUnique bool, + isGlobal bool, + indexPartSpecifications []*ast.IndexPartSpecification, + indexOption *ast.IndexOption, + state model.SchemaState, +) (*model.IndexInfo, error) { + if err := checkTooLongIndex(indexName); err != nil { + return nil, errors.Trace(err) + } + + idxColumns, mvIndex, err := buildIndexColumns(ctx, allTableColumns, indexPartSpecifications) + if err != nil { + return nil, errors.Trace(err) + } + + // Create index info. + idxInfo := &model.IndexInfo{ + Name: indexName, + Columns: idxColumns, + State: state, + Primary: isPrimary, + Unique: isUnique, + Global: isGlobal, + MVIndex: mvIndex, + } + + if indexOption != nil { + idxInfo.Comment = indexOption.Comment + if indexOption.Visibility == ast.IndexVisibilityInvisible { + idxInfo.Invisible = true + } + if indexOption.Tp == model.IndexTypeInvalid { + // Use btree as default index type. + idxInfo.Tp = model.IndexTypeBtree + } else { + idxInfo.Tp = indexOption.Tp + } + } else { + // Use btree as default index type. + idxInfo.Tp = model.IndexTypeBtree + } + + return idxInfo, nil +} + +// AddIndexColumnFlag aligns the column flags of columns in TableInfo to IndexInfo. +func AddIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { + if indexInfo.Primary { + for _, col := range indexInfo.Columns { + tblInfo.Columns[col.Offset].AddFlag(mysql.PriKeyFlag) + } + return + } + + col := indexInfo.Columns[0] + if indexInfo.Unique && len(indexInfo.Columns) == 1 { + tblInfo.Columns[col.Offset].AddFlag(mysql.UniqueKeyFlag) + } else { + tblInfo.Columns[col.Offset].AddFlag(mysql.MultipleKeyFlag) + } +} + +// DropIndexColumnFlag drops the column flag of columns in TableInfo according to the IndexInfo. +func DropIndexColumnFlag(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) { + if indexInfo.Primary { + for _, col := range indexInfo.Columns { + tblInfo.Columns[col.Offset].DelFlag(mysql.PriKeyFlag) + } + } else if indexInfo.Unique && len(indexInfo.Columns) == 1 { + tblInfo.Columns[indexInfo.Columns[0].Offset].DelFlag(mysql.UniqueKeyFlag) + } else { + tblInfo.Columns[indexInfo.Columns[0].Offset].DelFlag(mysql.MultipleKeyFlag) + } + + col := indexInfo.Columns[0] + // other index may still cover this col + for _, index := range tblInfo.Indices { + if index.Name.L == indexInfo.Name.L { + continue + } + + if index.Columns[0].Name.L != col.Name.L { + continue + } + + AddIndexColumnFlag(tblInfo, index) + } +} + +// ValidateRenameIndex checks if index name is ok to be renamed. +func ValidateRenameIndex(from, to model.CIStr, tbl *model.TableInfo) (ignore bool, err error) { + if fromIdx := tbl.FindIndexByName(from.L); fromIdx == nil { + return false, errors.Trace(infoschema.ErrKeyNotExists.GenWithStackByArgs(from.O, tbl.Name)) + } + // Take case-sensitivity into account, if `FromKey` and `ToKey` are the same, nothing need to be changed + if from.O == to.O { + return true, nil + } + // If spec.FromKey.L == spec.ToKey.L, we operate on the same index(case-insensitive) and change its name (case-sensitive) + // e.g: from `inDex` to `IndEX`. Otherwise, we try to rename an index to another different index which already exists, + // that's illegal by rule. + if toIdx := tbl.FindIndexByName(to.L); toIdx != nil && from.L != to.L { + return false, errors.Trace(infoschema.ErrKeyNameDuplicate.GenWithStackByArgs(toIdx.Name.O)) + } + return false, nil +} + +func onRenameIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, from, to, err := checkRenameIndex(t, job) + if err != nil || tblInfo == nil { + return ver, errors.Trace(err) + } + if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { + return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Rename Index")) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + // Store the mark and enter the next DDL handling loop. + return updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, false) + } + + renameIndexes(tblInfo, from, to) + if ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func validateAlterIndexVisibility(ctx sessionctx.Context, indexName model.CIStr, invisible bool, tbl *model.TableInfo) (bool, error) { + var idx *model.IndexInfo + if idx = tbl.FindIndexByName(indexName.L); idx == nil || idx.State != model.StatePublic { + return false, errors.Trace(infoschema.ErrKeyNotExists.GenWithStackByArgs(indexName.O, tbl.Name)) + } + if ctx == nil || ctx.GetSessionVars() == nil || ctx.GetSessionVars().StmtCtx.MultiSchemaInfo == nil { + // Early return. + if idx.Invisible == invisible { + return true, nil + } + } + return false, nil +} + +func onAlterIndexVisibility(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, from, invisible, err := checkAlterIndexVisibility(t, job) + if err != nil || tblInfo == nil { + return ver, errors.Trace(err) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + return updateVersionAndTableInfo(d, t, job, tblInfo, false) + } + + setIndexVisibility(tblInfo, from, invisible) + if ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func setIndexVisibility(tblInfo *model.TableInfo, name model.CIStr, invisible bool) { + for _, idx := range tblInfo.Indices { + if idx.Name.L == name.L || (isTempIdxInfo(idx, tblInfo) && getChangingIndexOriginName(idx) == name.O) { + idx.Invisible = invisible + } + } +} + +func getNullColInfos(tblInfo *model.TableInfo, indexInfo *model.IndexInfo) ([]*model.ColumnInfo, error) { + nullCols := make([]*model.ColumnInfo, 0, len(indexInfo.Columns)) + for _, colName := range indexInfo.Columns { + col := model.FindColumnInfo(tblInfo.Columns, colName.Name.L) + if !mysql.HasNotNullFlag(col.GetFlag()) || mysql.HasPreventNullInsertFlag(col.GetFlag()) { + nullCols = append(nullCols, col) + } + } + return nullCols, nil +} + +func checkPrimaryKeyNotNull(d *ddlCtx, w *worker, t *meta.Meta, job *model.Job, + tblInfo *model.TableInfo, indexInfo *model.IndexInfo) (warnings []string, err error) { + if !indexInfo.Primary { + return nil, nil + } + + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return nil, err + } + nullCols, err := getNullColInfos(tblInfo, indexInfo) + if err != nil { + return nil, err + } + if len(nullCols) == 0 { + return nil, nil + } + + err = modifyColsFromNull2NotNull(w, dbInfo, tblInfo, nullCols, &model.ColumnInfo{Name: model.NewCIStr("")}, false) + if err == nil { + return nil, nil + } + _, err = convertAddIdxJob2RollbackJob(d, t, job, tblInfo, []*model.IndexInfo{indexInfo}, err) + // TODO: Support non-strict mode. + // warnings = append(warnings, ErrWarnDataTruncated.GenWithStackByArgs(oldCol.Name.L, 0).Error()) + return nil, err +} + +// moveAndUpdateHiddenColumnsToPublic updates the hidden columns to public, and +// moves the hidden columns to proper offsets, so that Table.Columns' states meet the assumption of +// [public, public, ..., public, non-public, non-public, ..., non-public]. +func moveAndUpdateHiddenColumnsToPublic(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { + hiddenColOffset := make(map[int]struct{}, 0) + for _, col := range idxInfo.Columns { + if tblInfo.Columns[col.Offset].Hidden { + hiddenColOffset[col.Offset] = struct{}{} + } + } + if len(hiddenColOffset) == 0 { + return + } + // Find the first non-public column. + firstNonPublicPos := len(tblInfo.Columns) - 1 + for i, c := range tblInfo.Columns { + if c.State != model.StatePublic { + firstNonPublicPos = i + break + } + } + for _, col := range idxInfo.Columns { + tblInfo.Columns[col.Offset].State = model.StatePublic + if _, needMove := hiddenColOffset[col.Offset]; needMove { + tblInfo.MoveColumnInfo(col.Offset, firstNonPublicPos) + } + } +} + +func (w *worker) onCreateIndex(d *ddlCtx, t *meta.Meta, job *model.Job, isPK bool) (ver int64, err error) { + // Handle the rolling back job. + if job.IsRollingback() { + ver, err = onDropIndex(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + return ver, nil + } + + // Handle normal job. + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return ver, errors.Trace(err) + } + if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { + return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Create Index")) + } + + unique := make([]bool, 1) + global := make([]bool, 1) + indexNames := make([]model.CIStr, 1) + indexPartSpecifications := make([][]*ast.IndexPartSpecification, 1) + indexOption := make([]*ast.IndexOption, 1) + var sqlMode mysql.SQLMode + var warnings []string + hiddenCols := make([][]*model.ColumnInfo, 1) + + if isPK { + // Notice: sqlMode and warnings is used to support non-strict mode. + err = job.DecodeArgs(&unique[0], &indexNames[0], &indexPartSpecifications[0], &indexOption[0], &sqlMode, &warnings, &global[0]) + } else { + err = job.DecodeArgs(&unique[0], &indexNames[0], &indexPartSpecifications[0], &indexOption[0], &hiddenCols[0], &global[0]) + if err != nil { + err = job.DecodeArgs(&unique, &indexNames, &indexPartSpecifications, &indexOption, &hiddenCols, &global) + } + } + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + allIndexInfos := make([]*model.IndexInfo, 0, len(indexNames)) + for i, indexName := range indexNames { + indexInfo := tblInfo.FindIndexByName(indexName.L) + if indexInfo != nil && indexInfo.State == model.StatePublic { + job.State = model.JobStateCancelled + err = dbterror.ErrDupKeyName.GenWithStack("index already exist %s", indexName) + if isPK { + err = infoschema.ErrMultiplePriKey + } + return ver, err + } + if indexInfo == nil { + for _, hiddenCol := range hiddenCols[i] { + columnInfo := model.FindColumnInfo(tblInfo.Columns, hiddenCol.Name.L) + if columnInfo != nil && columnInfo.State == model.StatePublic { + // We already have a column with the same column name. + job.State = model.JobStateCancelled + // TODO: refine the error message + return ver, infoschema.ErrColumnExists.GenWithStackByArgs(hiddenCol.Name) + } + } + } + if indexInfo == nil { + if len(hiddenCols) > 0 { + for _, hiddenCol := range hiddenCols[i] { + InitAndAddColumnToTable(tblInfo, hiddenCol) + } + } + if err = checkAddColumnTooManyColumns(len(tblInfo.Columns)); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + indexInfo, err = BuildIndexInfo( + nil, + tblInfo.Columns, + indexName, + isPK, + unique[i], + global[i], + indexPartSpecifications[i], + indexOption[i], + model.StateNone, + ) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + if isPK { + if _, err = CheckPKOnGeneratedColumn(tblInfo, indexPartSpecifications[i]); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + } + indexInfo.ID = AllocateIndexID(tblInfo) + tblInfo.Indices = append(tblInfo.Indices, indexInfo) + if err = checkTooManyIndexes(tblInfo.Indices); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // Here we need do this check before set state to `DeleteOnly`, + // because if hidden columns has been set to `DeleteOnly`, + // the `DeleteOnly` columns are missing when we do this check. + if err := checkInvisibleIndexOnPK(tblInfo); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + logutil.BgLogger().Info("run add index job", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Reflect("indexInfo", indexInfo)) + } + allIndexInfos = append(allIndexInfos, indexInfo) + } + + originalState := allIndexInfos[0].State + +SwitchIndexState: + switch allIndexInfos[0].State { + case model.StateNone: + // none -> delete only + var reorgTp model.ReorgType + reorgTp, err = pickBackfillType(w.ctx, job, allIndexInfos[0].Unique, d) + if err != nil { + if !errorIsRetryable(err, job) { + job.State = model.JobStateCancelled + } + return ver, err + } + if reorgTp.NeedMergeProcess() { + // Increase telemetryAddIndexIngestUsage + telemetryAddIndexIngestUsage.Inc() + for _, indexInfo := range allIndexInfos { + indexInfo.BackfillState = model.BackfillStateRunning + } + } + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateDeleteOnly + moveAndUpdateHiddenColumnsToPublic(tblInfo, indexInfo) + } + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != model.StateDeleteOnly) + if err != nil { + return ver, err + } + job.SchemaState = model.StateDeleteOnly + case model.StateDeleteOnly: + // delete only -> write only + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateWriteOnly + _, err = checkPrimaryKeyNotNull(d, w, t, job, tblInfo, indexInfo) + if err != nil { + break SwitchIndexState + } + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteOnly) + if err != nil { + return ver, err + } + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + // write only -> reorganization + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateWriteReorganization + _, err = checkPrimaryKeyNotNull(d, w, t, job, tblInfo, indexInfo) + if err != nil { + break SwitchIndexState + } + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteReorganization) + if err != nil { + return ver, err + } + // Initialize SnapshotVer to 0 for later reorganization check. + job.SnapshotVer = 0 + job.SchemaState = model.StateWriteReorganization + case model.StateWriteReorganization: + // reorganization -> public + tbl, err := getTable(d.store, schemaID, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + + var done bool + if job.MultiSchemaInfo != nil { + done, ver, err = doReorgWorkForCreateIndexMultiSchema(w, d, t, job, tbl, allIndexInfos) + } else { + done, ver, err = doReorgWorkForCreateIndex(w, d, t, job, tbl, allIndexInfos) + } + if !done { + return ver, err + } + + // Set column index flag. + for _, indexInfo := range allIndexInfos { + AddIndexColumnFlag(tblInfo, indexInfo) + if isPK { + if err = UpdateColsNull2NotNull(tblInfo, indexInfo); err != nil { + return ver, errors.Trace(err) + } + } + indexInfo.State = model.StatePublic + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StatePublic) + if err != nil { + return ver, errors.Trace(err) + } + + allIndexIDs := make([]int64, 0, len(allIndexInfos)) + ifExists := make([]bool, 0, len(allIndexInfos)) + for _, indexInfo := range allIndexInfos { + allIndexIDs = append(allIndexIDs, indexInfo.ID) + ifExists = append(ifExists, false) + } + job.Args = []interface{}{allIndexIDs, ifExists, getPartitionIDs(tbl.Meta())} + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + if !job.ReorgMeta.IsDistReorg && job.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { + ingest.LitBackCtxMgr.Unregister(job.ID) + } + logutil.BgLogger().Info("run add index job done", zap.String("category", "ddl"), + zap.String("charset", job.Charset), + zap.String("collation", job.Collate)) + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("index", tblInfo.State) + } + + return ver, errors.Trace(err) +} + +// pickBackfillType determines which backfill process will be used. +func pickBackfillType(ctx context.Context, job *model.Job, unique bool, d *ddlCtx) (model.ReorgType, error) { + if job.ReorgMeta.ReorgTp != model.ReorgTypeNone { + // The backfill task has been started. + // Don't change the backfill type. + return job.ReorgMeta.ReorgTp, nil + } + if !IsEnableFastReorg() { + job.ReorgMeta.ReorgTp = model.ReorgTypeTxn + return model.ReorgTypeTxn, nil + } + if ingest.LitInitialized { + available, err := ingest.LitBackCtxMgr.CheckAvailable() + if err != nil { + return model.ReorgTypeNone, err + } + if available { + ctx := logutil.WithCategory(ctx, "ddl-ingest") + err = cleanupSortPath(ctx, job.ID) + if err != nil { + return model.ReorgTypeNone, err + } + if variable.EnableDistTask.Load() { + _, err = ingest.LitBackCtxMgr.Register(ctx, unique, job.ID, d.etcdCli, job.ReorgMeta.ResourceGroupName) + } else { + _, err = ingest.LitBackCtxMgr.Register(ctx, unique, job.ID, nil, job.ReorgMeta.ResourceGroupName) + } + if err != nil { + return model.ReorgTypeNone, err + } + job.ReorgMeta.ReorgTp = model.ReorgTypeLitMerge + if variable.EnableDistTask.Load() { + job.ReorgMeta.IsDistReorg = true + } + return model.ReorgTypeLitMerge, nil + } + } + // The lightning environment is unavailable, but we can still use the txn-merge backfill. + logutil.BgLogger().Info("fallback to txn-merge backfill process", zap.String("category", "ddl"), + zap.Bool("lightning env initialized", ingest.LitInitialized)) + job.ReorgMeta.ReorgTp = model.ReorgTypeTxnMerge + return model.ReorgTypeTxnMerge, nil +} + +// cleanupSortPath is used to clean up the temp data of the previous jobs. +// Because we don't remove all the files after the support of checkpoint, +// there maybe some stale files in the sort path if TiDB is killed during the backfill process. +func cleanupSortPath(ctx context.Context, currentJobID int64) error { + sortPath := ingest.ConfigSortPath() + err := os.MkdirAll(sortPath, 0700) + if err != nil { + return errors.Trace(err) + } + entries, err := os.ReadDir(sortPath) + if err != nil { + logutil.Logger(ctx).Warn(ingest.LitErrReadSortPath, zap.Error(err)) + return errors.Trace(err) + } + for _, entry := range entries { + if !entry.IsDir() { + continue + } + jobID, err := ingest.DecodeBackendTag(entry.Name()) + if err != nil { + logutil.Logger(ctx).Warn(ingest.LitErrCleanSortPath, zap.Error(err)) + continue + } + if _, ok := ingest.LitBackCtxMgr.Load(jobID); ok { + // The job is still running, skip it. + logutil.Logger(ctx).Warn("the job is still running, skip removing it", + zap.Int64("running job ID", jobID)) + continue + } + // Remove all the temp data of the previous done jobs. + if jobID < currentJobID { + logutil.Logger(ctx).Info("remove stale temp index data", + zap.Int64("jobID", jobID), zap.Int64("currentJobID", currentJobID)) + err := os.RemoveAll(filepath.Join(sortPath, entry.Name())) + if err != nil { + logutil.Logger(ctx).Warn(ingest.LitErrCleanSortPath, zap.Error(err)) + return nil + } + } + } + return nil +} + +// IngestJobsNotExisted checks the ddl about `add index` with ingest method not existed. +func IngestJobsNotExisted(ctx sessionctx.Context) bool { + se := sess.NewSession(ctx) + template := "select job_meta from mysql.tidb_ddl_job where reorg and (type = %d or type = %d) and processing;" + sql := fmt.Sprintf(template, model.ActionAddIndex, model.ActionAddPrimaryKey) + rows, err := se.Execute(context.Background(), sql, "check-pitr") + if err != nil { + logutil.BgLogger().Warn("cannot check ingest job", zap.Error(err)) + return false + } + for _, row := range rows { + jobBinary := row.GetBytes(0) + runJob := model.Job{} + err := runJob.Decode(jobBinary) + if err != nil { + logutil.BgLogger().Warn("cannot check ingest job", zap.Error(err)) + return false + } + // Check whether this add index job is using lightning to do the backfill work. + if runJob.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { + return false + } + } + return true +} + +func doReorgWorkForCreateIndexMultiSchema(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, + tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { + if job.MultiSchemaInfo.Revertible { + done, ver, err = doReorgWorkForCreateIndex(w, d, t, job, tbl, allIndexInfos) + if done { + job.MarkNonRevertible() + if err == nil { + ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) + } + } + // We need another round to wait for all the others sub-jobs to finish. + return false, ver, err + } + return true, ver, err +} + +func doReorgWorkForCreateIndex(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, + tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { + var reorgTp model.ReorgType + reorgTp, err = pickBackfillType(w.ctx, job, allIndexInfos[0].Unique, d) + if err != nil { + return false, ver, err + } + if !reorgTp.NeedMergeProcess() { + return runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) + } + switch allIndexInfos[0].BackfillState { + case model.BackfillStateRunning: + logutil.BgLogger().Info("index backfill state running", zap.String("category", "ddl"), + zap.Int64("job ID", job.ID), zap.String("table", tbl.Meta().Name.O), + zap.Bool("ingest mode", reorgTp == model.ReorgTypeLitMerge), + zap.String("index", allIndexInfos[0].Name.O)) + switch reorgTp { + case model.ReorgTypeLitMerge: + if job.ReorgMeta.IsDistReorg { + done, ver, err = runIngestReorgJobDist(w, d, t, job, tbl, allIndexInfos) + } else { + done, ver, err = runIngestReorgJob(w, d, t, job, tbl, allIndexInfos) + } + case model.ReorgTypeTxnMerge: + done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) + } + if err != nil || !done { + return false, ver, errors.Trace(err) + } + for _, indexInfo := range allIndexInfos { + indexInfo.BackfillState = model.BackfillStateReadyToMerge + } + ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) + return false, ver, errors.Trace(err) + case model.BackfillStateReadyToMerge: + logutil.BgLogger().Info("index backfill state ready to merge", zap.String("category", "ddl"), zap.Int64("job ID", job.ID), + zap.String("table", tbl.Meta().Name.O), zap.String("index", allIndexInfos[0].Name.O)) + for _, indexInfo := range allIndexInfos { + indexInfo.BackfillState = model.BackfillStateMerging + } + if reorgTp == model.ReorgTypeLitMerge { + ingest.LitBackCtxMgr.Unregister(job.ID) + } + job.SnapshotVer = 0 // Reset the snapshot version for merge index reorg. + ver, err = updateVersionAndTableInfo(d, t, job, tbl.Meta(), true) + return false, ver, errors.Trace(err) + case model.BackfillStateMerging: + done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, true) + if !done { + return false, ver, err + } + for _, indexInfo := range allIndexInfos { + indexInfo.BackfillState = model.BackfillStateInapplicable // Prevent double-write on this index. + } + return true, ver, err + default: + return false, 0, dbterror.ErrInvalidDDLState.GenWithStackByArgs("backfill", allIndexInfos[0].BackfillState) + } +} + +func runIngestReorgJobDist(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, + tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { + done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) + if err != nil { + return false, ver, errors.Trace(err) + } + + if !done { + return false, ver, nil + } + + return true, ver, nil +} + +func runIngestReorgJob(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, + tbl table.Table, allIndexInfos []*model.IndexInfo) (done bool, ver int64, err error) { + bc, ok := ingest.LitBackCtxMgr.Load(job.ID) + if ok && bc.Done() { + return true, 0, nil + } + ctx := logutil.WithCategory(w.ctx, "ddl-ingest") + bc, err = ingest.LitBackCtxMgr.Register(ctx, allIndexInfos[0].Unique, job.ID, nil, job.ReorgMeta.ResourceGroupName) + if err != nil { + ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) + return false, ver, errors.Trace(err) + } + done, ver, err = runReorgJobAndHandleErr(w, d, t, job, tbl, allIndexInfos, false) + if err != nil { + if !errorIsRetryable(err, job) { + logutil.BgLogger().Warn("run reorg job failed, convert job to rollback", zap.String("category", "ddl"), + zap.String("job", job.String()), zap.Error(err)) + ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) + } + return false, ver, errors.Trace(err) + } + if !done { + return false, ver, nil + } + for _, indexInfo := range allIndexInfos { + err = bc.FinishImport(indexInfo.ID, indexInfo.Unique, tbl) + if err != nil { + if common.ErrFoundDuplicateKeys.Equal(err) { + err = convertToKeyExistsErr(err, indexInfo, tbl.Meta()) + } + if kv.ErrKeyExists.Equal(err) { + logutil.BgLogger().Warn("import index duplicate key, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) + ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) + } else { + logutil.BgLogger().Warn("lightning import error", zap.String("category", "ddl"), zap.Error(err)) + if !errorIsRetryable(err, job) { + ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) + } + } + return false, ver, errors.Trace(err) + } + } + bc.SetDone() + return true, ver, nil +} + +func errorIsRetryable(err error, job *model.Job) bool { + if job.ErrorCount+1 >= variable.GetDDLErrorCountLimit() { + return false + } + originErr := errors.Cause(err) + if tErr, ok := originErr.(*terror.Error); ok { + sqlErr := terror.ToSQLError(tErr) + _, ok := dbterror.ReorgRetryableErrCodes[sqlErr.Code] + return ok + } + // For the unknown errors, we should retry. + return true +} + +func convertToKeyExistsErr(originErr error, idxInfo *model.IndexInfo, tblInfo *model.TableInfo) error { + tErr, ok := errors.Cause(originErr).(*terror.Error) + if !ok { + return originErr + } + if len(tErr.Args()) != 2 { + return originErr + } + key, keyIsByte := tErr.Args()[0].([]byte) + value, valIsByte := tErr.Args()[1].([]byte) + if !keyIsByte || !valIsByte { + return originErr + } + return genKeyExistsErr(key, value, idxInfo, tblInfo) +} + +func runReorgJobAndHandleErr(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, + tbl table.Table, allIndexInfos []*model.IndexInfo, mergingTmpIdx bool) (done bool, ver int64, err error) { + elements := make([]*meta.Element, 0, len(allIndexInfos)) + for _, indexInfo := range allIndexInfos { + elements = append(elements, &meta.Element{ID: indexInfo.ID, TypeKey: meta.IndexElementKey}) + } + + failpoint.Inject("mockDMLExecutionStateMerging", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) && allIndexInfos[0].BackfillState == model.BackfillStateMerging && + MockDMLExecutionStateMerging != nil { + MockDMLExecutionStateMerging() + } + }) + + sctx, err1 := w.sessPool.Get() + if err1 != nil { + err = err1 + return + } + defer w.sessPool.Put(sctx) + rh := newReorgHandler(sess.NewSession(sctx)) + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return false, ver, errors.Trace(err) + } + reorgInfo, err := getReorgInfo(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, tbl, elements, mergingTmpIdx) + if err != nil || reorgInfo == nil || reorgInfo.first { + // If we run reorg firstly, we should update the job snapshot version + // and then run the reorg next time. + return false, ver, errors.Trace(err) + } + err = overwriteReorgInfoFromGlobalCheckpoint(w, rh.s, job, reorgInfo) + if err != nil { + return false, ver, errors.Trace(err) + } + err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (addIndexErr error) { + defer util.Recover(metrics.LabelDDL, "onCreateIndex", + func() { + addIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("add table `%v` index `%v` panic", tbl.Meta().Name, allIndexInfos[0].Name) + }, false) + return w.addTableIndex(tbl, reorgInfo) + }) + if err != nil { + if dbterror.ErrPausedDDLJob.Equal(err) { + return false, ver, nil + } + if dbterror.ErrWaitReorgTimeout.Equal(err) { + // if timeout, we should return, check for the owner and re-wait job done. + return false, ver, nil + } + if common.ErrFoundDuplicateKeys.Equal(err) { + // TODO(tangenta): get duplicate column and match index. + err = convertToKeyExistsErr(err, allIndexInfos[0], tbl.Meta()) + } + if !errorIsRetryable(err, job) || + // TODO: Remove this check make it can be retry. Related test is TestModifyColumnReorgInfo. + job.ReorgMeta.IsDistReorg { + logutil.BgLogger().Warn("run add index job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) + ver, err = convertAddIdxJob2RollbackJob(d, t, job, tbl.Meta(), allIndexInfos, err) + if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { + logutil.BgLogger().Warn("run add index job failed, convert job to rollback, RemoveDDLReorgHandle failed", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err1)) + } + } + return false, ver, errors.Trace(err) + } + failpoint.Inject("mockDMLExecutionStateBeforeImport", func(_ failpoint.Value) { + if MockDMLExecutionStateBeforeImport != nil { + MockDMLExecutionStateBeforeImport() + } + }) + return true, ver, nil +} + +func onDropIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, allIndexInfos, ifExists, err := checkDropIndex(d, t, job) + if err != nil { + if ifExists && dbterror.ErrCantDropFieldOrKey.Equal(err) { + job.Warning = toTError(err) + job.State = model.JobStateDone + return ver, nil + } + return ver, errors.Trace(err) + } + if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { + return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("Drop Index")) + } + + if job.MultiSchemaInfo != nil && !job.IsRollingback() && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + job.SchemaState = allIndexInfos[0].State + return updateVersionAndTableInfo(d, t, job, tblInfo, false) + } + + originalState := allIndexInfos[0].State + switch allIndexInfos[0].State { + case model.StatePublic: + // public -> write only + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateWriteOnly + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateWriteOnly) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateWriteOnly: + // write only -> delete only + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateDeleteOnly + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateDeleteOnly) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateDeleteOnly: + // delete only -> reorganization + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateDeleteReorganization + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != model.StateDeleteReorganization) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateDeleteReorganization: + // reorganization -> absent + idxIds := make([]int64, 0, len(allIndexInfos)) + for _, indexInfo := range allIndexInfos { + indexInfo.State = model.StateNone + // Set column index flag. + DropIndexColumnFlag(tblInfo, indexInfo) + RemoveDependentHiddenColumns(tblInfo, indexInfo) + removeIndexInfo(tblInfo, indexInfo) + idxIds = append(idxIds, indexInfo.ID) + } + + failpoint.Inject("mockExceedErrorLimit", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + panic("panic test in cancelling add index") + } + }) + + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, originalState != model.StateNone) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + if job.IsRollingback() { + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + job.Args[0] = idxIds + } else { + // the partition ids were append by convertAddIdxJob2RollbackJob, it is weird, but for the compatibility, + // we should keep appending the partitions in the convertAddIdxJob2RollbackJob. + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + // Global index key has t{tableID}_ prefix. + // Assign partitionIDs empty to guarantee correct prefix in insertJobIntoDeleteRangeTable. + if allIndexInfos[0].Global { + job.Args = append(job.Args, idxIds[0], []int64{}) + } else { + job.Args = append(job.Args, idxIds[0], getPartitionIDs(tblInfo)) + } + } + default: + return ver, errors.Trace(dbterror.ErrInvalidDDLState.GenWithStackByArgs("index", allIndexInfos[0].State)) + } + job.SchemaState = allIndexInfos[0].State + return ver, errors.Trace(err) +} + +// RemoveDependentHiddenColumns removes hidden columns by the indexInfo. +func RemoveDependentHiddenColumns(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { + hiddenColOffs := make([]int, 0) + for _, indexColumn := range idxInfo.Columns { + col := tblInfo.Columns[indexColumn.Offset] + if col.Hidden { + hiddenColOffs = append(hiddenColOffs, col.Offset) + } + } + // Sort the offset in descending order. + slices.SortFunc(hiddenColOffs, func(a, b int) int { return cmp.Compare(b, a) }) + // Move all the dependent hidden columns to the end. + endOffset := len(tblInfo.Columns) - 1 + for _, offset := range hiddenColOffs { + tblInfo.MoveColumnInfo(offset, endOffset) + } + tblInfo.Columns = tblInfo.Columns[:len(tblInfo.Columns)-len(hiddenColOffs)] +} + +func removeIndexInfo(tblInfo *model.TableInfo, idxInfo *model.IndexInfo) { + indices := tblInfo.Indices + offset := -1 + for i, idx := range indices { + if idxInfo.ID == idx.ID { + offset = i + break + } + } + if offset == -1 { + // The target index has been removed. + return + } + // Remove the target index. + tblInfo.Indices = append(tblInfo.Indices[:offset], tblInfo.Indices[offset+1:]...) +} + +func checkDropIndex(d *ddlCtx, t *meta.Meta, job *model.Job) (*model.TableInfo, []*model.IndexInfo, bool /* ifExists */, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, false, errors.Trace(err) + } + + indexNames := make([]model.CIStr, 1) + ifExists := make([]bool, 1) + if err = job.DecodeArgs(&indexNames[0], &ifExists[0]); err != nil { + if err = job.DecodeArgs(&indexNames, &ifExists); err != nil { + job.State = model.JobStateCancelled + return nil, nil, false, errors.Trace(err) + } + } + + indexInfos := make([]*model.IndexInfo, 0, len(indexNames)) + for i, idxName := range indexNames { + indexInfo := tblInfo.FindIndexByName(idxName.L) + if indexInfo == nil { + job.State = model.JobStateCancelled + return nil, nil, ifExists[i], dbterror.ErrCantDropFieldOrKey.GenWithStack("index %s doesn't exist", idxName) + } + + // Check that drop primary index will not cause invisible implicit primary index. + if err := checkInvisibleIndexesOnPK(tblInfo, []*model.IndexInfo{indexInfo}, job); err != nil { + job.State = model.JobStateCancelled + return nil, nil, false, errors.Trace(err) + } + + // Double check for drop index needed in foreign key. + if err := checkIndexNeededInForeignKeyInOwner(d, t, job, job.SchemaName, tblInfo, indexInfo); err != nil { + return nil, nil, false, errors.Trace(err) + } + indexInfos = append(indexInfos, indexInfo) + } + return tblInfo, indexInfos, false, nil +} + +func checkInvisibleIndexesOnPK(tblInfo *model.TableInfo, indexInfos []*model.IndexInfo, job *model.Job) error { + newIndices := make([]*model.IndexInfo, 0, len(tblInfo.Indices)) + for _, oidx := range tblInfo.Indices { + needAppend := true + for _, idx := range indexInfos { + if idx.Name.L == oidx.Name.L { + needAppend = false + break + } + } + if needAppend { + newIndices = append(newIndices, oidx) + } + } + newTbl := tblInfo.Clone() + newTbl.Indices = newIndices + if err := checkInvisibleIndexOnPK(newTbl); err != nil { + job.State = model.JobStateCancelled + return err + } + + return nil +} + +func checkRenameIndex(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIStr, model.CIStr, error) { + var from, to model.CIStr + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, from, to, errors.Trace(err) + } + + if err := job.DecodeArgs(&from, &to); err != nil { + job.State = model.JobStateCancelled + return nil, from, to, errors.Trace(err) + } + + // Double check. See function `RenameIndex` in ddl_api.go + duplicate, err := ValidateRenameIndex(from, to, tblInfo) + if duplicate { + return nil, from, to, nil + } + if err != nil { + job.State = model.JobStateCancelled + return nil, from, to, errors.Trace(err) + } + return tblInfo, from, to, errors.Trace(err) +} + +func checkAlterIndexVisibility(t *meta.Meta, job *model.Job) (*model.TableInfo, model.CIStr, bool, error) { + var ( + indexName model.CIStr + invisible bool + ) + + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, indexName, invisible, errors.Trace(err) + } + + if err := job.DecodeArgs(&indexName, &invisible); err != nil { + job.State = model.JobStateCancelled + return nil, indexName, invisible, errors.Trace(err) + } + + skip, err := validateAlterIndexVisibility(nil, indexName, invisible, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return nil, indexName, invisible, errors.Trace(err) + } + if skip { + job.State = model.JobStateDone + return nil, indexName, invisible, nil + } + return tblInfo, indexName, invisible, nil +} + +// indexRecord is the record information of an index. +type indexRecord struct { + handle kv.Handle + key []byte // It's used to lock a record. Record it to reduce the encoding time. + vals []types.Datum // It's the index values. + rsData []types.Datum // It's the restored data for handle. + skip bool // skip indicates that the index key is already exists, we should not add it. +} + +type baseIndexWorker struct { + *backfillCtx + indexes []table.Index + + tp backfillerType + // The following attributes are used to reduce memory allocation. + defaultVals []types.Datum + idxRecords []*indexRecord + rowMap map[int64]types.Datum + rowDecoder *decoder.RowDecoder +} + +type addIndexTxnWorker struct { + baseIndexWorker + + // The following attributes are used to reduce memory allocation. + idxKeyBufs [][]byte + batchCheckKeys []kv.Key + batchCheckValues [][]byte + distinctCheckFlags []bool + recordIdx []int +} + +func newAddIndexTxnWorker( + decodeColMap map[int64]decoder.Column, + t table.PhysicalTable, + bfCtx *backfillCtx, + jobID int64, + elements []*meta.Element, + eleTypeKey []byte, +) (*addIndexTxnWorker, error) { + if !bytes.Equal(eleTypeKey, meta.IndexElementKey) { + logutil.BgLogger().Error("Element type for addIndexTxnWorker incorrect", + zap.Int64("job ID", jobID), zap.ByteString("element type", eleTypeKey), zap.Int64("element ID", elements[0].ID)) + return nil, errors.Errorf("element type is not index, typeKey: %v", eleTypeKey) + } + + allIndexes := make([]table.Index, 0, len(elements)) + for _, elem := range elements { + if !bytes.Equal(elem.TypeKey, meta.IndexElementKey) { + continue + } + indexInfo := model.FindIndexInfoByID(t.Meta().Indices, elem.ID) + index := tables.NewIndex(t.GetPhysicalID(), t.Meta(), indexInfo) + allIndexes = append(allIndexes, index) + } + rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) + + return &addIndexTxnWorker{ + baseIndexWorker: baseIndexWorker{ + backfillCtx: bfCtx, + indexes: allIndexes, + rowDecoder: rowDecoder, + defaultVals: make([]types.Datum, len(t.WritableCols())), + rowMap: make(map[int64]types.Datum, len(decodeColMap)), + }, + }, nil +} + +func (w *baseIndexWorker) AddMetricInfo(cnt float64) { + w.metricCounter.Add(cnt) +} + +func (w *baseIndexWorker) String() string { + return w.tp.String() +} + +func (w *baseIndexWorker) GetCtx() *backfillCtx { + return w.backfillCtx +} + +// mockNotOwnerErrOnce uses to make sure `notOwnerErr` only mock error once. +var mockNotOwnerErrOnce uint32 + +// getIndexRecord gets index columns values use w.rowDecoder, and generate indexRecord. +func (w *baseIndexWorker) getIndexRecord(idxInfo *model.IndexInfo, handle kv.Handle, recordKey []byte) (*indexRecord, error) { + cols := w.table.WritableCols() + failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { + if valStr, ok := val.(string); ok { + switch valStr { + case "cantDecodeRecordErr": + failpoint.Return(nil, errors.Trace(dbterror.ErrCantDecodeRecord.GenWithStackByArgs("index", + errors.New("mock can't decode record error")))) + case "modifyColumnNotOwnerErr": + if idxInfo.Name.O == "_Idx$_idx_0" && handle.IntValue() == 7168 && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 0, 1) { + failpoint.Return(nil, errors.Trace(dbterror.ErrNotOwner)) + } + case "addIdxNotOwnerErr": + // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. + // First step, we need to exit "addPhysicalTableIndex". + if idxInfo.Name.O == "idx2" && handle.IntValue() == 6144 && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 1, 2) { + failpoint.Return(nil, errors.Trace(dbterror.ErrNotOwner)) + } + } + } + }) + idxVal := make([]types.Datum, len(idxInfo.Columns)) + var err error + for j, v := range idxInfo.Columns { + col := cols[v.Offset] + idxColumnVal, ok := w.rowMap[col.ID] + if ok { + idxVal[j] = idxColumnVal + continue + } + idxColumnVal, err = tables.GetColDefaultValue(w.sessCtx, col, w.defaultVals) + if err != nil { + return nil, errors.Trace(err) + } + + idxVal[j] = idxColumnVal + } + + rsData := tables.TryGetHandleRestoredDataWrapper(w.table.Meta(), nil, w.rowMap, idxInfo) + idxRecord := &indexRecord{handle: handle, key: recordKey, vals: idxVal, rsData: rsData} + return idxRecord, nil +} + +func (w *baseIndexWorker) cleanRowMap() { + for id := range w.rowMap { + delete(w.rowMap, id) + } +} + +// getNextKey gets next key of entry that we are going to process. +func (w *baseIndexWorker) getNextKey(taskRange reorgBackfillTask, taskDone bool) (nextKey kv.Key) { + if !taskDone { + // The task is not done. So we need to pick the last processed entry's handle and add one. + lastHandle := w.idxRecords[len(w.idxRecords)-1].handle + recordKey := tablecodec.EncodeRecordKey(taskRange.physicalTable.RecordPrefix(), lastHandle) + return recordKey.Next() + } + return taskRange.endKey +} + +func (w *baseIndexWorker) updateRowDecoder(handle kv.Handle, rawRecord []byte) error { + sysZone := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() + _, err := w.rowDecoder.DecodeAndEvalRowWithMap(w.sessCtx, handle, rawRecord, sysZone, w.rowMap) + return errors.Trace(err) +} + +// fetchRowColVals fetch w.batchCnt count records that need to reorganize indices, and build the corresponding indexRecord slice. +// fetchRowColVals returns: +// 1. The corresponding indexRecord slice. +// 2. Next handle of entry that we need to process. +// 3. Boolean indicates whether the task is done. +// 4. error occurs in fetchRowColVals. nil if no error occurs. +func (w *baseIndexWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*indexRecord, kv.Key, bool, error) { + // TODO: use tableScan to prune columns. + w.idxRecords = w.idxRecords[:0] + startTime := time.Now() + + // taskDone means that the reorged handle is out of taskRange.endHandle. + taskDone := false + oprStartTime := startTime + err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, taskRange.physicalTable.RecordPrefix(), txn.StartTS(), + taskRange.startKey, taskRange.endKey, func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { + oprEndTime := time.Now() + logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in baseIndexWorker fetchRowColVals", 0) + oprStartTime = oprEndTime + + taskDone = recordKey.Cmp(taskRange.endKey) >= 0 + + if taskDone || len(w.idxRecords) >= w.batchCnt { + return false, nil + } + + // Decode one row, generate records of this row. + err := w.updateRowDecoder(handle, rawRow) + if err != nil { + return false, err + } + for _, index := range w.indexes { + idxRecord, err1 := w.getIndexRecord(index.Meta(), handle, recordKey) + if err1 != nil { + return false, errors.Trace(err1) + } + w.idxRecords = append(w.idxRecords, idxRecord) + } + // If there are generated column, rowDecoder will use column value that not in idxInfo.Columns to calculate + // the generated value, so we need to clear up the reusing map. + w.cleanRowMap() + + if recordKey.Cmp(taskRange.endKey) == 0 { + taskDone = true + return false, nil + } + return true, nil + }) + + if len(w.idxRecords) == 0 { + taskDone = true + } + + logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Stringer("worker", w), zap.Uint64("txnStartTS", txn.StartTS()), + zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) + return w.idxRecords, w.getNextKey(taskRange, taskDone), taskDone, errors.Trace(err) +} + +func (w *addIndexTxnWorker) initBatchCheckBufs(batchCount int) { + if len(w.idxKeyBufs) < batchCount { + w.idxKeyBufs = make([][]byte, batchCount) + } + + w.batchCheckKeys = w.batchCheckKeys[:0] + w.batchCheckValues = w.batchCheckValues[:0] + w.distinctCheckFlags = w.distinctCheckFlags[:0] + w.recordIdx = w.recordIdx[:0] +} + +func (w *addIndexTxnWorker) checkHandleExists(idxInfo *model.IndexInfo, key kv.Key, value []byte, handle kv.Handle) error { + tblInfo := w.table.Meta() + idxColLen := len(idxInfo.Columns) + h, err := tablecodec.DecodeIndexHandle(key, value, idxColLen) + if err != nil { + return errors.Trace(err) + } + hasBeenBackFilled := h.Equal(handle) + if hasBeenBackFilled { + return nil + } + return genKeyExistsErr(key, value, idxInfo, tblInfo) +} + +func genKeyExistsErr(key, value []byte, idxInfo *model.IndexInfo, tblInfo *model.TableInfo) error { + idxColLen := len(idxInfo.Columns) + indexName := fmt.Sprintf("%s.%s", tblInfo.Name.String(), idxInfo.Name.String()) + colInfos := tables.BuildRowcodecColInfoForIndexColumns(idxInfo, tblInfo) + values, err := tablecodec.DecodeIndexKV(key, value, idxColLen, tablecodec.HandleNotNeeded, colInfos) + if err != nil { + logutil.BgLogger().Warn("decode index key value failed", zap.String("index", indexName), + zap.String("key", hex.EncodeToString(key)), zap.String("value", hex.EncodeToString(value)), zap.Error(err)) + return kv.ErrKeyExists.FastGenByArgs(key, indexName) + } + valueStr := make([]string, 0, idxColLen) + for i, val := range values[:idxColLen] { + d, err := tablecodec.DecodeColumnValue(val, colInfos[i].Ft, time.Local) + if err != nil { + logutil.BgLogger().Warn("decode column value failed", zap.String("index", indexName), + zap.String("key", hex.EncodeToString(key)), zap.String("value", hex.EncodeToString(value)), zap.Error(err)) + return kv.ErrKeyExists.FastGenByArgs(key, indexName) + } + str, err := d.ToString() + if err != nil { + str = string(val) + } + if types.IsBinaryStr(colInfos[i].Ft) || types.IsTypeBit(colInfos[i].Ft) { + str = util.FmtNonASCIIPrintableCharToHex(str) + } + valueStr = append(valueStr, str) + } + return kv.ErrKeyExists.FastGenByArgs(strings.Join(valueStr, "-"), indexName) +} + +// batchCheckUniqueKey checks the unique keys in the batch. +// Note that `idxRecords` may belong to multiple indexes. +func (w *addIndexTxnWorker) batchCheckUniqueKey(txn kv.Transaction, idxRecords []*indexRecord) error { + w.initBatchCheckBufs(len(idxRecords)) + stmtCtx := w.sessCtx.GetSessionVars().StmtCtx + uniqueBatchKeys := make([]kv.Key, 0, len(idxRecords)) + cnt := 0 + for i, record := range idxRecords { + idx := w.indexes[i%len(w.indexes)] + if !idx.Meta().Unique { + // non-unique key need not to check, use `nil` as a placeholder to keep + // `idxRecords[i]` belonging to `indexes[i%len(indexes)]`. + w.batchCheckKeys = append(w.batchCheckKeys, nil) + w.batchCheckValues = append(w.batchCheckValues, nil) + w.distinctCheckFlags = append(w.distinctCheckFlags, false) + w.recordIdx = append(w.recordIdx, 0) + continue + } + // skip by default. + idxRecords[i].skip = true + iter := idx.GenIndexKVIter(stmtCtx, record.vals, record.handle, idxRecords[i].rsData) + for iter.Valid() { + var buf []byte + if cnt < len(w.idxKeyBufs) { + buf = w.idxKeyBufs[cnt] + } + key, val, distinct, err := iter.Next(buf) + if err != nil { + return errors.Trace(err) + } + if cnt < len(w.idxKeyBufs) { + w.idxKeyBufs[cnt] = key + } else { + w.idxKeyBufs = append(w.idxKeyBufs, key) + } + cnt++ + w.batchCheckKeys = append(w.batchCheckKeys, key) + w.batchCheckValues = append(w.batchCheckValues, val) + w.distinctCheckFlags = append(w.distinctCheckFlags, distinct) + w.recordIdx = append(w.recordIdx, i) + uniqueBatchKeys = append(uniqueBatchKeys, key) + } + } + + if len(uniqueBatchKeys) == 0 { + return nil + } + + batchVals, err := txn.BatchGet(context.Background(), uniqueBatchKeys) + if err != nil { + return errors.Trace(err) + } + + // 1. unique-key/primary-key is duplicate and the handle is equal, skip it. + // 2. unique-key/primary-key is duplicate and the handle is not equal, return duplicate error. + // 3. non-unique-key is duplicate, skip it. + for i, key := range w.batchCheckKeys { + if len(key) == 0 { + continue + } + idx := w.indexes[i%len(w.indexes)] + val, found := batchVals[string(key)] + if found { + if w.distinctCheckFlags[i] { + if err := w.checkHandleExists(idx.Meta(), key, val, idxRecords[w.recordIdx[i]].handle); err != nil { + return errors.Trace(err) + } + } + } else if w.distinctCheckFlags[i] { + // The keys in w.batchCheckKeys also maybe duplicate, + // so we need to backfill the not found key into `batchVals` map. + batchVals[string(key)] = w.batchCheckValues[i] + } + idxRecords[w.recordIdx[i]].skip = found && idxRecords[w.recordIdx[i]].skip + } + // Constrains is already checked. + stmtCtx.BatchCheck = true + return nil +} + +type addIndexIngestWorker struct { + ctx context.Context + d *ddlCtx + metricCounter prometheus.Counter + sessCtx sessionctx.Context + + tbl table.PhysicalTable + indexes []table.Index + writers []ingest.Writer + copReqSenderPool *copReqSenderPool + checkpointMgr *ingest.CheckpointManager + + resultCh chan *backfillResult + jobID int64 + distribute bool +} + +func newAddIndexIngestWorker( + ctx context.Context, + t table.PhysicalTable, + d *ddlCtx, + engines []ingest.Engine, + resultCh chan *backfillResult, + jobID int64, + schemaName string, + indexIDs []int64, + writerID int, + copReqSenderPool *copReqSenderPool, + sessCtx sessionctx.Context, + checkpointMgr *ingest.CheckpointManager, + distribute bool, +) (*addIndexIngestWorker, error) { + indexes := make([]table.Index, 0, len(indexIDs)) + writers := make([]ingest.Writer, 0, len(indexIDs)) + for i, indexID := range indexIDs { + indexInfo := model.FindIndexInfoByID(t.Meta().Indices, indexID) + index := tables.NewIndex(t.GetPhysicalID(), t.Meta(), indexInfo) + lw, err := engines[i].CreateWriter(writerID) + if err != nil { + return nil, err + } + indexes = append(indexes, index) + writers = append(writers, lw) + } + + return &addIndexIngestWorker{ + ctx: ctx, + d: d, + sessCtx: sessCtx, + metricCounter: metrics.BackfillTotalCounter.WithLabelValues( + metrics.GenerateReorgLabel("add_idx_rate", schemaName, t.Meta().Name.O)), + tbl: t, + indexes: indexes, + writers: writers, + copReqSenderPool: copReqSenderPool, + resultCh: resultCh, + jobID: jobID, + checkpointMgr: checkpointMgr, + distribute: distribute, + }, nil +} + +// WriteLocal will write index records to lightning engine. +func (w *addIndexIngestWorker) WriteLocal(rs *IndexRecordChunk) (count int, nextKey kv.Key, err error) { + oprStartTime := time.Now() + copCtx := w.copReqSenderPool.copCtx + vars := w.sessCtx.GetSessionVars() + cnt, lastHandle, err := writeChunkToLocal( + w.ctx, w.writers, w.indexes, copCtx, vars, rs.Chunk) + if err != nil || cnt == 0 { + return 0, nil, err + } + w.metricCounter.Add(float64(cnt)) + logSlowOperations(time.Since(oprStartTime), "writeChunkToLocal", 3000) + nextKey = tablecodec.EncodeRecordKey(w.tbl.RecordPrefix(), lastHandle) + return cnt, nextKey, nil +} + +func writeChunkToLocal( + ctx context.Context, + writers []ingest.Writer, + indexes []table.Index, + copCtx copr.CopContext, + vars *variable.SessionVars, + copChunk *chunk.Chunk, +) (int, kv.Handle, error) { + sCtx, writeBufs := vars.StmtCtx, vars.GetWriteStmtBufs() + iter := chunk.NewIterator4Chunk(copChunk) + c := copCtx.GetBase() + + maxIdxColCnt := maxIndexColumnCount(indexes) + idxDataBuf := make([]types.Datum, maxIdxColCnt) + handleDataBuf := make([]types.Datum, len(c.HandleOutputOffsets)) + count := 0 + var lastHandle kv.Handle + + unlockFns := make([]func(), 0, len(writers)) + for _, w := range writers { + unlock := w.LockForWrite() + unlockFns = append(unlockFns, unlock) + } + defer func() { + for _, unlock := range unlockFns { + unlock() + } + }() + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + handleDataBuf = handleDataBuf[:0] + handleDataBuf := extractDatumByOffsets(row, c.HandleOutputOffsets, c.ExprColumnInfos, handleDataBuf) + handle, err := buildHandle(handleDataBuf, c.TableInfo, c.PrimaryKeyInfo, sCtx) + if err != nil { + return 0, nil, errors.Trace(err) + } + for i, index := range indexes { + idxID := index.Meta().ID + idxDataBuf = idxDataBuf[:0] + idxDataBuf = extractDatumByOffsets( + row, copCtx.IndexColumnOutputOffsets(idxID), c.ExprColumnInfos, idxDataBuf) + rsData := getRestoreData(c.TableInfo, copCtx.IndexInfo(idxID), c.PrimaryKeyInfo, handleDataBuf) + err = writeOneKVToLocal(ctx, writers[i], index, sCtx, writeBufs, idxDataBuf, rsData, handle) + if err != nil { + return 0, nil, errors.Trace(err) + } + } + count++ + lastHandle = handle + } + return count, lastHandle, nil +} + +func maxIndexColumnCount(indexes []table.Index) int { + maxCnt := 0 + for _, idx := range indexes { + colCnt := len(idx.Meta().Columns) + if colCnt > maxCnt { + maxCnt = colCnt + } + } + return maxCnt +} + +func writeOneKVToLocal( + ctx context.Context, + writer ingest.Writer, + index table.Index, + sCtx *stmtctx.StatementContext, + writeBufs *variable.WriteStmtBufs, + idxDt, rsData []types.Datum, + handle kv.Handle, +) error { + iter := index.GenIndexKVIter(sCtx, idxDt, handle, rsData) + for iter.Valid() { + key, idxVal, _, err := iter.Next(writeBufs.IndexKeyBuf) + if err != nil { + return errors.Trace(err) + } + failpoint.Inject("mockLocalWriterPanic", func() { + panic("mock panic") + }) + if !index.Meta().Unique { + handle = nil + } + err = writer.WriteRow(ctx, key, idxVal, handle) + if err != nil { + return errors.Trace(err) + } + failpoint.Inject("mockLocalWriterError", func() { + failpoint.Return(errors.New("mock engine error")) + }) + writeBufs.IndexKeyBuf = key + } + return nil +} + +// BackfillData will backfill table index in a transaction. A lock corresponds to a rowKey if the value of rowKey is changed, +// Note that index columns values may change, and an index is not allowed to be added, so the txn will rollback and retry. +// BackfillData will add w.batchCnt indices once, default value of w.batchCnt is 128. +func (w *addIndexTxnWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { + failpoint.Inject("errorMockPanic", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + panic("panic test") + } + }) + + oprStartTime := time.Now() + jobID := handleRange.getJobID() + ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) + errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) (err error) { + taskCtx.finishTS = txn.StartTS() + taskCtx.addedCount = 0 + taskCtx.scanCount = 0 + txn.SetOption(kv.Priority, handleRange.priority) + if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(jobID); tagger != nil { + txn.SetOption(kv.ResourceGroupTagger, tagger) + } + txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) + + idxRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) + if err != nil { + return errors.Trace(err) + } + taskCtx.nextKey = nextKey + taskCtx.done = taskDone + + err = w.batchCheckUniqueKey(txn, idxRecords) + if err != nil { + return errors.Trace(err) + } + + for i, idxRecord := range idxRecords { + taskCtx.scanCount++ + // The index is already exists, we skip it, no needs to backfill it. + // The following update, delete, insert on these rows, TiDB can handle it correctly. + if idxRecord.skip { + continue + } + + // We need to add this lock to make sure pessimistic transaction can realize this operation. + // For the normal pessimistic transaction, it's ok. But if async commit is used, it may lead to inconsistent data and index. + err := txn.LockKeys(context.Background(), new(kv.LockCtx), idxRecord.key) + if err != nil { + return errors.Trace(err) + } + + handle, err := w.indexes[i%len(w.indexes)].Create( + w.sessCtx, txn, idxRecord.vals, idxRecord.handle, idxRecord.rsData, table.WithIgnoreAssertion, table.FromBackfill) + if err != nil { + if kv.ErrKeyExists.Equal(err) && idxRecord.handle.Equal(handle) { + // Index already exists, skip it. + continue + } + return errors.Trace(err) + } + taskCtx.addedCount++ + } + + return nil + }) + logSlowOperations(time.Since(oprStartTime), "AddIndexBackfillData", 3000) + failpoint.Inject("mockDMLExecution", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) && MockDMLExecution != nil { + MockDMLExecution() + } + }) + return +} + +// MockDMLExecution is only used for test. +var MockDMLExecution func() + +// MockDMLExecutionMerging is only used for test. +var MockDMLExecutionMerging func() + +// MockDMLExecutionStateMerging is only used for test. +var MockDMLExecutionStateMerging func() + +// MockDMLExecutionStateBeforeImport is only used for test. +var MockDMLExecutionStateBeforeImport func() + +func (w *worker) addPhysicalTableIndex(t table.PhysicalTable, reorgInfo *reorgInfo) error { + if reorgInfo.mergingTmpIdx { + logutil.BgLogger().Info("start to merge temp index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) + return w.writePhysicalTableRecord(w.sessPool, t, typeAddIndexMergeTmpWorker, reorgInfo) + } + logutil.BgLogger().Info("start to add table index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) + return w.writePhysicalTableRecord(w.sessPool, t, typeAddIndexWorker, reorgInfo) +} + +// addTableIndex handles the add index reorganization state for a table. +func (w *worker) addTableIndex(t table.Table, reorgInfo *reorgInfo) error { + // TODO: Support typeAddIndexMergeTmpWorker. + if reorgInfo.ReorgMeta.IsDistReorg && !reorgInfo.mergingTmpIdx { + if reorgInfo.ReorgMeta.ReorgTp == model.ReorgTypeLitMerge { + err := w.executeDistGlobalTask(reorgInfo) + if err != nil { + return err + } + return checkDuplicateForUniqueIndex(w.ctx, t, reorgInfo) + } + } + + var err error + if tbl, ok := t.(table.PartitionedTable); ok { + var finish bool + for !finish { + p := tbl.GetPartition(reorgInfo.PhysicalTableID) + if p == nil { + return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, t.Meta().ID) + } + err = w.addPhysicalTableIndex(p, reorgInfo) + if err != nil { + break + } + w.ddlCtx.mu.RLock() + w.ddlCtx.mu.hook.OnUpdateReorgInfo(reorgInfo.Job, reorgInfo.PhysicalTableID) + w.ddlCtx.mu.RUnlock() + + finish, err = updateReorgInfo(w.sessPool, tbl, reorgInfo) + if err != nil { + return errors.Trace(err) + } + // Every time we finish a partition, we update the progress of the job. + if rc := w.getReorgCtx(reorgInfo.Job.ID); rc != nil { + reorgInfo.Job.SetRowCount(rc.getRowCount()) + } + } + } else { + //nolint:forcetypeassert + phyTbl := t.(table.PhysicalTable) + err = w.addPhysicalTableIndex(phyTbl, reorgInfo) + } + return errors.Trace(err) +} + +func checkDuplicateForUniqueIndex(ctx context.Context, t table.Table, reorgInfo *reorgInfo) error { + var bc ingest.BackendCtx + var err error + defer func() { + if bc != nil { + ingest.LitBackCtxMgr.Unregister(reorgInfo.ID) + } + }() + for _, elem := range reorgInfo.elements { + indexInfo := model.FindIndexInfoByID(t.Meta().Indices, elem.ID) + if indexInfo == nil { + return errors.New("unexpected error, can't find index info") + } + if indexInfo.Unique { + ctx := logutil.WithCategory(ctx, "ddl-ingest") + if bc == nil { + bc, err = ingest.LitBackCtxMgr.Register(ctx, indexInfo.Unique, reorgInfo.ID, nil, reorgInfo.ReorgMeta.ResourceGroupName) + if err != nil { + return err + } + } + err = bc.CollectRemoteDuplicateRows(indexInfo.ID, t) + if err != nil { + return err + } + } + } + return nil +} + +// MockDMLExecutionOnTaskFinished is used to mock DML execution when tasks finished. +var MockDMLExecutionOnTaskFinished func() + +// MockDMLExecutionOnDDLPaused is used to mock DML execution when ddl job paused. +var MockDMLExecutionOnDDLPaused func() + +func (w *worker) executeDistGlobalTask(reorgInfo *reorgInfo) error { + if reorgInfo.mergingTmpIdx { + return errors.New("do not support merge index") + } + + taskType := BackfillTaskType + taskKey := fmt.Sprintf("ddl/%s/%d", taskType, reorgInfo.Job.ID) + g, ctx := errgroup.WithContext(context.Background()) + done := make(chan struct{}) + + // generate taskKey for multi schema change. + if mInfo := reorgInfo.Job.MultiSchemaInfo; mInfo != nil { + taskKey = fmt.Sprintf("%s/%d", taskKey, mInfo.Seq) + } + + // for resuming add index task. + taskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + task, err := taskManager.GetGlobalTaskByKey(taskKey) + if err != nil { + return err + } + if task != nil { + // It's possible that the task state is succeed but the ddl job is paused. + // When task in succeed state, we can skip the dist task execution/scheduing process. + if task.State == proto.TaskStateSucceed { + return nil + } + g.Go(func() error { + defer close(done) + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logutil.BgLogger(), + func(ctx context.Context) (bool, error) { + return true, handle.ResumeTask(taskKey) + }, + ) + if err != nil { + return err + } + err = handle.WaitGlobalTask(ctx, task) + if w.isReorgPaused(reorgInfo.Job.ID) { + logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.Error(err)) + return dbterror.ErrPausedDDLJob.GenWithStackByArgs(reorgInfo.Job.ID) + } + return err + }) + } else { + elemIDs := make([]int64, 0, len(reorgInfo.elements)) + for _, elem := range reorgInfo.elements { + elemIDs = append(elemIDs, elem.ID) + } + + taskMeta := &BackfillGlobalMeta{ + Job: *reorgInfo.Job.Clone(), + EleIDs: elemIDs, + EleTypeKey: reorgInfo.currElement.TypeKey, + CloudStorageURI: variable.CloudStorageURI.Load(), + } + + metaData, err := json.Marshal(taskMeta) + if err != nil { + return err + } + + g.Go(func() error { + defer close(done) + err := handle.SubmitAndRunGlobalTask(ctx, taskKey, taskType, distPhysicalTableConcurrency, metaData) + failpoint.Inject("pauseAfterDistTaskSuccess", func() { + MockDMLExecutionOnTaskFinished() + }) + if w.isReorgPaused(reorgInfo.Job.ID) { + logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.Error(err)) + return dbterror.ErrPausedDDLJob.GenWithStackByArgs(reorgInfo.Job.ID) + } + return err + }) + } + + g.Go(func() error { + checkFinishTk := time.NewTicker(CheckBackfillJobFinishInterval) + defer checkFinishTk.Stop() + updateRowCntTk := time.NewTicker(UpdateBackfillJobRowCountInterval) + defer updateRowCntTk.Stop() + for { + select { + case <-done: + w.updateJobRowCount(taskKey, reorgInfo.Job.ID) + return nil + case <-checkFinishTk.C: + if err = w.isReorgRunnable(reorgInfo.Job.ID, true); err != nil { + if dbterror.ErrPausedDDLJob.Equal(err) { + if err = handle.PauseTask(taskKey); err != nil { + logutil.BgLogger().Error("pause global task error", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) + continue + } + } + if !dbterror.ErrCancelledDDLJob.Equal(err) { + return errors.Trace(err) + } + if err = handle.CancelGlobalTask(taskKey); err != nil { + logutil.BgLogger().Error("cancel global task error", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) + // continue to cancel global task + continue + } + } + case <-updateRowCntTk.C: + w.updateJobRowCount(taskKey, reorgInfo.Job.ID) + } + } + }) + err = g.Wait() + return err +} + +func (w *worker) updateJobRowCount(taskKey string, jobID int64) { + taskMgr, err := storage.GetTaskManager() + if err != nil { + logutil.BgLogger().Warn("cannot get task manager", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) + return + } + gTask, err := taskMgr.GetGlobalTaskByKey(taskKey) + if err != nil || gTask == nil { + logutil.BgLogger().Warn("cannot get global task", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) + return + } + rowCount, err := taskMgr.GetSubtaskRowCount(gTask.ID, proto.StepOne) + if err != nil { + logutil.BgLogger().Warn("cannot get subtask row count", zap.String("category", "ddl"), zap.String("task_key", taskKey), zap.Error(err)) + return + } + w.getReorgCtx(jobID).setRowCount(rowCount) +} + +func getNextPartitionInfo(reorg *reorgInfo, t table.PartitionedTable, currPhysicalTableID int64) (int64, kv.Key, kv.Key, error) { + pi := t.Meta().GetPartitionInfo() + if pi == nil { + return 0, nil, nil, nil + } + + // During data copying, copy data from partitions to be dropped + nextPartitionDefs := pi.DroppingDefinitions + if bytes.Equal(reorg.currElement.TypeKey, meta.IndexElementKey) { + // During index re-creation, process data from partitions to be added + nextPartitionDefs = pi.AddingDefinitions + } + if len(nextPartitionDefs) == 0 { + nextPartitionDefs = pi.Definitions + } + pid, err := findNextPartitionID(currPhysicalTableID, nextPartitionDefs) + if err != nil { + // Fatal error, should not run here. + logutil.BgLogger().Error("find next partition ID failed", zap.String("category", "ddl"), zap.Reflect("table", t), zap.Error(err)) + return 0, nil, nil, errors.Trace(err) + } + if pid == 0 { + // Next partition does not exist, all the job done. + return 0, nil, nil, nil + } + + failpoint.Inject("mockUpdateCachedSafePoint", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + ts := oracle.GoTimeToTS(time.Now()) + //nolint:forcetypeassert + s := reorg.d.store.(tikv.Storage) + s.UpdateSPCache(ts, time.Now()) + time.Sleep(time.Second * 3) + } + }) + + var startKey, endKey kv.Key + if reorg.mergingTmpIdx { + indexID := reorg.currElement.ID + startKey, endKey = tablecodec.GetTableIndexKeyRange(pid, tablecodec.TempIndexPrefix|indexID) + } else { + currentVer, err := getValidCurrentVersion(reorg.d.store) + if err != nil { + return 0, nil, nil, errors.Trace(err) + } + startKey, endKey, err = getTableRange(reorg.NewJobContext(), reorg.d, t.GetPartition(pid), currentVer.Ver, reorg.Job.Priority) + if err != nil { + return 0, nil, nil, errors.Trace(err) + } + } + return pid, startKey, endKey, nil +} + +// updateReorgInfo will find the next partition according to current reorgInfo. +// If no more partitions, or table t is not a partitioned table, returns true to +// indicate that the reorganize work is finished. +func updateReorgInfo(sessPool *sess.Pool, t table.PartitionedTable, reorg *reorgInfo) (bool, error) { + pid, startKey, endKey, err := getNextPartitionInfo(reorg, t, reorg.PhysicalTableID) + if err != nil { + return false, errors.Trace(err) + } + if pid == 0 { + // Next partition does not exist, all the job done. + return true, nil + } + reorg.PhysicalTableID, reorg.StartKey, reorg.EndKey = pid, startKey, endKey + + // Write the reorg info to store so the whole reorganize process can recover from panic. + err = reorg.UpdateReorgMeta(reorg.StartKey, sessPool) + logutil.BgLogger().Info("job update reorgInfo", zap.String("category", "ddl"), + zap.Int64("jobID", reorg.Job.ID), + zap.Stringer("element", reorg.currElement), + zap.Int64("partitionTableID", pid), + zap.String("startKey", hex.EncodeToString(reorg.StartKey)), + zap.String("endKey", hex.EncodeToString(reorg.EndKey)), zap.Error(err)) + return false, errors.Trace(err) +} + +// findNextPartitionID finds the next partition ID in the PartitionDefinition array. +// Returns 0 if current partition is already the last one. +func findNextPartitionID(currentPartition int64, defs []model.PartitionDefinition) (int64, error) { + for i, def := range defs { + if currentPartition == def.ID { + if i == len(defs)-1 { + return 0, nil + } + return defs[i+1].ID, nil + } + } + return 0, errors.Errorf("partition id not found %d", currentPartition) +} + +// AllocateIndexID allocates an index ID from TableInfo. +func AllocateIndexID(tblInfo *model.TableInfo) int64 { + tblInfo.MaxIndexID++ + return tblInfo.MaxIndexID +} + +func getIndexInfoByNameAndColumn(oldTableInfo *model.TableInfo, newOne *model.IndexInfo) *model.IndexInfo { + for _, oldOne := range oldTableInfo.Indices { + if newOne.Name.L == oldOne.Name.L && indexColumnSliceEqual(newOne.Columns, oldOne.Columns) { + return oldOne + } + } + return nil +} + +func indexColumnSliceEqual(a, b []*model.IndexColumn) bool { + if len(a) != len(b) { + return false + } + if len(a) == 0 { + logutil.BgLogger().Warn("admin repair table : index's columns length equal to 0", zap.String("category", "ddl")) + return true + } + // Accelerate the compare by eliminate index bound check. + b = b[:len(a)] + for i, v := range a { + if v.Name.L != b[i].Name.L { + return false + } + } + return true +} + +type cleanUpIndexWorker struct { + baseIndexWorker +} + +func newCleanUpIndexWorker(sessCtx sessionctx.Context, id int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) *cleanUpIndexWorker { + indexes := make([]table.Index, 0, len(t.Indices())) + rowDecoder := decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap) + for _, index := range t.Indices() { + if index.Meta().Global { + indexes = append(indexes, index) + } + } + return &cleanUpIndexWorker{ + baseIndexWorker: baseIndexWorker{ + backfillCtx: newBackfillCtx(reorgInfo.d, id, sessCtx, reorgInfo.SchemaName, t, jc, "cleanup_idx_rate", false), + indexes: indexes, + rowDecoder: rowDecoder, + defaultVals: make([]types.Datum, len(t.WritableCols())), + rowMap: make(map[int64]types.Datum, len(decodeColMap)), + }, + } +} + +func (w *cleanUpIndexWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { + failpoint.Inject("errorMockPanic", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + panic("panic test") + } + }) + + oprStartTime := time.Now() + ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) + errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + taskCtx.addedCount = 0 + taskCtx.scanCount = 0 + txn.SetOption(kv.Priority, handleRange.priority) + if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { + txn.SetOption(kv.ResourceGroupTagger, tagger) + } + txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) + + idxRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) + if err != nil { + return errors.Trace(err) + } + taskCtx.nextKey = nextKey + taskCtx.done = taskDone + + txn.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) + + n := len(w.indexes) + for i, idxRecord := range idxRecords { + taskCtx.scanCount++ + // we fetch records row by row, so records will belong to + // index[0], index[1] ... index[n-1], index[0], index[1] ... + // respectively. So indexes[i%n] is the index of idxRecords[i]. + err := w.indexes[i%n].Delete(w.sessCtx.GetSessionVars().StmtCtx, txn, idxRecord.vals, idxRecord.handle) + if err != nil { + return errors.Trace(err) + } + taskCtx.addedCount++ + } + return nil + }) + logSlowOperations(time.Since(oprStartTime), "cleanUpIndexBackfillDataInTxn", 3000) + + return +} + +// cleanupPhysicalTableIndex handles the drop partition reorganization state for a non-partitioned table or a partition. +func (w *worker) cleanupPhysicalTableIndex(t table.PhysicalTable, reorgInfo *reorgInfo) error { + logutil.BgLogger().Info("start to clean up index", zap.String("category", "ddl"), zap.String("job", reorgInfo.Job.String()), zap.String("reorgInfo", reorgInfo.String())) + return w.writePhysicalTableRecord(w.sessPool, t, typeCleanUpIndexWorker, reorgInfo) +} + +// cleanupGlobalIndex handles the drop partition reorganization state to clean up index entries of partitions. +func (w *worker) cleanupGlobalIndexes(tbl table.PartitionedTable, partitionIDs []int64, reorgInfo *reorgInfo) error { + var err error + var finish bool + for !finish { + p := tbl.GetPartition(reorgInfo.PhysicalTableID) + if p == nil { + return dbterror.ErrCancelledDDLJob.GenWithStack("Can not find partition id %d for table %d", reorgInfo.PhysicalTableID, tbl.Meta().ID) + } + err = w.cleanupPhysicalTableIndex(p, reorgInfo) + if err != nil { + break + } + finish, err = w.updateReorgInfoForPartitions(tbl, reorgInfo, partitionIDs) + if err != nil { + return errors.Trace(err) + } + } + + return errors.Trace(err) +} + +// updateReorgInfoForPartitions will find the next partition in partitionIDs according to current reorgInfo. +// If no more partitions, or table t is not a partitioned table, returns true to +// indicate that the reorganize work is finished. +func (w *worker) updateReorgInfoForPartitions(t table.PartitionedTable, reorg *reorgInfo, partitionIDs []int64) (bool, error) { + pi := t.Meta().GetPartitionInfo() + if pi == nil { + return true, nil + } + + var pid int64 + for i, pi := range partitionIDs { + if pi == reorg.PhysicalTableID { + if i == len(partitionIDs)-1 { + return true, nil + } + pid = partitionIDs[i+1] + break + } + } + + currentVer, err := getValidCurrentVersion(reorg.d.store) + if err != nil { + return false, errors.Trace(err) + } + start, end, err := getTableRange(reorg.NewJobContext(), reorg.d, t.GetPartition(pid), currentVer.Ver, reorg.Job.Priority) + if err != nil { + return false, errors.Trace(err) + } + reorg.StartKey, reorg.EndKey, reorg.PhysicalTableID = start, end, pid + + // Write the reorg info to store so the whole reorganize process can recover from panic. + err = reorg.UpdateReorgMeta(reorg.StartKey, w.sessPool) + logutil.BgLogger().Info("job update reorg info", zap.String("category", "ddl"), zap.Int64("jobID", reorg.Job.ID), + zap.Stringer("element", reorg.currElement), + zap.Int64("partition table ID", pid), zap.String("start key", hex.EncodeToString(start)), + zap.String("end key", hex.EncodeToString(end)), zap.Error(err)) + return false, errors.Trace(err) +} + +// changingIndex is used to store the index that need to be changed during modifying column. +type changingIndex struct { + IndexInfo *model.IndexInfo + // Column offset in idxInfo.Columns. + Offset int + // When the modifying column is contained in the index, a temp index is created. + // isTemp indicates whether the indexInfo is a temp index created by a previous modify column job. + isTemp bool +} + +// FindRelatedIndexesToChange finds the indexes that covering the given column. +// The normal one will be overridden by the temp one. +func FindRelatedIndexesToChange(tblInfo *model.TableInfo, colName model.CIStr) []changingIndex { + // In multi-schema change jobs that contains several "modify column" sub-jobs, there may be temp indexes for another temp index. + // To prevent reorganizing too many indexes, we should create the temp indexes that are really necessary. + var normalIdxInfos, tempIdxInfos []changingIndex + for _, idxInfo := range tblInfo.Indices { + if pos := findIdxCol(idxInfo, colName); pos != -1 { + isTemp := isTempIdxInfo(idxInfo, tblInfo) + r := changingIndex{IndexInfo: idxInfo, Offset: pos, isTemp: isTemp} + if isTemp { + tempIdxInfos = append(tempIdxInfos, r) + } else { + normalIdxInfos = append(normalIdxInfos, r) + } + } + } + // Overwrite if the index has the corresponding temp index. For example, + // we try to find the indexes that contain the column `b` and there are two indexes, `i(a, b)` and `$i($a, b)`. + // Note that the symbol `$` means temporary. The index `$i($a, b)` is temporarily created by the previous "modify a" statement. + // In this case, we would create a temporary index like $$i($a, $b), so the latter should be chosen. + result := normalIdxInfos + for _, tmpIdx := range tempIdxInfos { + origName := getChangingIndexOriginName(tmpIdx.IndexInfo) + for i, normIdx := range normalIdxInfos { + if normIdx.IndexInfo.Name.O == origName { + result[i] = tmpIdx + } + } + } + return result +} + +func isTempIdxInfo(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) bool { + for _, idxCol := range idxInfo.Columns { + if tblInfo.Columns[idxCol.Offset].ChangeStateInfo != nil { + return true + } + } + return false +} + +func findIdxCol(idxInfo *model.IndexInfo, colName model.CIStr) int { + for offset, idxCol := range idxInfo.Columns { + if idxCol.Name.L == colName.L { + return offset + } + } + return -1 +} + +func renameIndexes(tblInfo *model.TableInfo, from, to model.CIStr) { + for _, idx := range tblInfo.Indices { + if idx.Name.L == from.L { + idx.Name = to + } else if isTempIdxInfo(idx, tblInfo) && getChangingIndexOriginName(idx) == from.O { + idx.Name.L = strings.Replace(idx.Name.L, from.L, to.L, 1) + idx.Name.O = strings.Replace(idx.Name.O, from.O, to.O, 1) + } + } +} diff --git a/ddl/index_change_test.go b/pkg/ddl/index_change_test.go similarity index 96% rename from ddl/index_change_test.go rename to pkg/ddl/index_change_test.go index b63b98b74fed0..4deef10c50c8e 100644 --- a/ddl/index_change_test.go +++ b/pkg/ddl/index_change_test.go @@ -21,16 +21,16 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/ddl/index_cop.go b/pkg/ddl/index_cop.go similarity index 91% rename from ddl/index_cop.go rename to pkg/ddl/index_cop.go index b8c71a8d532a0..9c231d54a3449 100644 --- a/ddl/index_cop.go +++ b/pkg/ddl/index_cop.go @@ -21,29 +21,29 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/pingcap/tipb/go-tipb" kvutil "github.com/tikv/client-go/v2/util" "go.uber.org/zap" diff --git a/ddl/index_cop_test.go b/pkg/ddl/index_cop_test.go similarity index 91% rename from ddl/index_cop_test.go rename to pkg/ddl/index_cop_test.go index 623446dff6d03..e81f2703798ef 100644 --- a/ddl/index_cop_test.go +++ b/pkg/ddl/index_cop_test.go @@ -19,14 +19,14 @@ import ( "strconv" "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/ddl/index_merge_tmp.go b/pkg/ddl/index_merge_tmp.go similarity index 96% rename from ddl/index_merge_tmp.go rename to pkg/ddl/index_merge_tmp.go index 283355292f106..8d7ffb16ec94a 100644 --- a/ddl/index_merge_tmp.go +++ b/pkg/ddl/index_merge_tmp.go @@ -21,14 +21,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/logutil" kvutil "github.com/tikv/client-go/v2/util" "go.uber.org/zap" ) diff --git a/ddl/index_modify_test.go b/pkg/ddl/index_modify_test.go similarity index 98% rename from ddl/index_modify_test.go rename to pkg/ddl/index_modify_test.go index 7b243e297b211..690d38ec2ff16 100644 --- a/ddl/index_modify_test.go +++ b/pkg/ddl/index_modify_test.go @@ -26,24 +26,24 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - testddlutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + testddlutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/ingest/BUILD.bazel b/pkg/ddl/ingest/BUILD.bazel new file mode 100644 index 0000000000000..2e51d87b36776 --- /dev/null +++ b/pkg/ddl/ingest/BUILD.bazel @@ -0,0 +1,87 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ingest", + srcs = [ + "backend.go", + "backend_mgr.go", + "checkpoint.go", + "config.go", + "disk_root.go", + "engine.go", + "engine_mgr.go", + "env.go", + "mem_root.go", + "message.go", + "mock.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/ingest", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/backend", + "//br/pkg/lightning/backend/encode", + "//br/pkg/lightning/backend/kv", + "//br/pkg/lightning/backend/local", + "//br/pkg/lightning/checkpoints", + "//br/pkg/lightning/common", + "//br/pkg/lightning/config", + "//br/pkg/lightning/errormanager", + "//br/pkg/lightning/log", + "//pkg/config", + "//pkg/ddl/internal/session", + "//pkg/ddl/util", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/util", + "//pkg/util/dbterror", + "//pkg/util/generic", + "//pkg/util/logutil", + "//pkg/util/size", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "ingest_test", + timeout = "short", + srcs = [ + "checkpoint_test.go", + "env_test.go", + "integration_test.go", + "main_test.go", + "mem_root_test.go", + ], + embed = [":ingest"], + flaky = True, + race = "on", + shard_count = 15, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/ingest/testutil", + "//pkg/ddl/internal/session", + "//pkg/ddl/testutil", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/errno", + "//pkg/parser/model", + "//pkg/testkit", + "//tests/realtikvtest", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/ddl/ingest/backend.go b/pkg/ddl/ingest/backend.go similarity index 97% rename from ddl/ingest/backend.go rename to pkg/ddl/ingest/backend.go index d466fcb48255a..3d4bf54a68b33 100644 --- a/ddl/ingest/backend.go +++ b/pkg/ddl/ingest/backend.go @@ -25,12 +25,12 @@ import ( lightning "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/errormanager" "github.com/pingcap/tidb/br/pkg/lightning/log" - tikv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/generic" - "github.com/pingcap/tidb/util/logutil" + tikv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/generic" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" atomicutil "go.uber.org/atomic" diff --git a/ddl/ingest/backend_mgr.go b/pkg/ddl/ingest/backend_mgr.go similarity index 97% rename from ddl/ingest/backend_mgr.go rename to pkg/ddl/ingest/backend_mgr.go index f63e7e91dcee9..28fae55be8f91 100644 --- a/ddl/ingest/backend_mgr.go +++ b/pkg/ddl/ingest/backend_mgr.go @@ -23,10 +23,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/local" "github.com/pingcap/tidb/br/pkg/lightning/config" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/generic" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/generic" + "github.com/pingcap/tidb/pkg/util/logutil" kvutil "github.com/tikv/client-go/v2/util" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" diff --git a/ddl/ingest/checkpoint.go b/pkg/ddl/ingest/checkpoint.go similarity index 98% rename from ddl/ingest/checkpoint.go rename to pkg/ddl/ingest/checkpoint.go index 71ade5794738e..ff5b9d5b58106 100644 --- a/ddl/ingest/checkpoint.go +++ b/pkg/ddl/ingest/checkpoint.go @@ -25,12 +25,12 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/ingest/checkpoint_test.go b/pkg/ddl/ingest/checkpoint_test.go similarity index 97% rename from ddl/ingest/checkpoint_test.go rename to pkg/ddl/ingest/checkpoint_test.go index 1e3eff1df5940..70c09d691cbd0 100644 --- a/ddl/ingest/checkpoint_test.go +++ b/pkg/ddl/ingest/checkpoint_test.go @@ -20,9 +20,9 @@ import ( "testing" "github.com/ngaut/pools" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/ingest/config.go b/pkg/ddl/ingest/config.go new file mode 100644 index 0000000000000..8c02c15c122a9 --- /dev/null +++ b/pkg/ddl/ingest/config.go @@ -0,0 +1,164 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ingest + +import ( + "context" + "math" + "path/filepath" + "sync/atomic" + + "github.com/pingcap/tidb/br/pkg/lightning/backend" + "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" + lightning "github.com/pingcap/tidb/br/pkg/lightning/config" + tidb "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" + "go.uber.org/zap" +) + +// ImporterRangeConcurrencyForTest is only used for test. +var ImporterRangeConcurrencyForTest *atomic.Int32 + +// Config is the configuration for the lightning local backend used in DDL. +type Config struct { + Lightning *lightning.Config + KeyspaceName string + IsRaftKV2 bool +} + +func genConfig(ctx context.Context, memRoot MemRoot, jobID int64, unique bool, isRaftKV2 bool) (*Config, error) { + tidbCfg := tidb.GetGlobalConfig() + cfg := lightning.NewConfig() + cfg.TikvImporter.Backend = lightning.BackendLocal + // Each backend will build a single dir in lightning dir. + cfg.TikvImporter.SortedKVDir = filepath.Join(LitSortPath, EncodeBackendTag(jobID)) + if ImporterRangeConcurrencyForTest != nil { + cfg.TikvImporter.RangeConcurrency = int(ImporterRangeConcurrencyForTest.Load()) + } + err := cfg.AdjustForDDL() + if err != nil { + logutil.Logger(ctx).Warn(LitWarnConfigError, zap.Error(err)) + return nil, err + } + adjustImportMemory(ctx, memRoot, cfg) + cfg.Checkpoint.Enable = true + if unique { + cfg.TikvImporter.DuplicateResolution = lightning.DupeResAlgErr + // TODO(lance6716): will introduce fail-fast for DDL usage later + cfg.Conflict.Threshold = math.MaxInt64 + } else { + cfg.TikvImporter.DuplicateResolution = lightning.DupeResAlgNone + } + cfg.TiDB.PdAddr = tidbCfg.Path + cfg.TiDB.Host = "127.0.0.1" + cfg.TiDB.StatusPort = int(tidbCfg.Status.StatusPort) + // Set TLS related information + cfg.Security.CAPath = tidbCfg.Security.ClusterSSLCA + cfg.Security.CertPath = tidbCfg.Security.ClusterSSLCert + cfg.Security.KeyPath = tidbCfg.Security.ClusterSSLKey + // in DDL scenario, we don't switch import mode + cfg.Cron.SwitchMode = lightning.Duration{Duration: 0} + + c := &Config{ + Lightning: cfg, + KeyspaceName: tidb.GetGlobalKeyspaceName(), + IsRaftKV2: isRaftKV2, + } + + return c, err +} + +var ( + compactMemory = 1 * size.GB + compactConcurrency = 4 +) + +func generateLocalEngineConfig(id int64, dbName, tbName string) *backend.EngineConfig { + return &backend.EngineConfig{ + Local: backend.LocalEngineConfig{ + Compact: true, + CompactThreshold: int64(compactMemory), + CompactConcurrency: compactConcurrency, + }, + TableInfo: &checkpoints.TidbTableInfo{ + ID: id, + DB: dbName, + Name: tbName, + }, + KeepSortDir: true, + } +} + +// adjustImportMemory adjusts the lightning memory parameters according to the memory root's max limitation. +func adjustImportMemory(ctx context.Context, memRoot MemRoot, cfg *lightning.Config) { + var scale int64 + // Try aggressive resource usage successful. + if tryAggressiveMemory(ctx, memRoot, cfg) { + return + } + + defaultMemSize := int64(cfg.TikvImporter.LocalWriterMemCacheSize) * int64(cfg.TikvImporter.RangeConcurrency) + defaultMemSize += 4 * int64(cfg.TikvImporter.EngineMemCacheSize) + logutil.Logger(ctx).Info(LitInfoInitMemSetting, + zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), + zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), + zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) + + maxLimit := memRoot.MaxMemoryQuota() + scale = defaultMemSize / maxLimit + + if scale == 1 || scale == 0 { + return + } + + cfg.TikvImporter.LocalWriterMemCacheSize /= lightning.ByteSize(scale) + cfg.TikvImporter.EngineMemCacheSize /= lightning.ByteSize(scale) + // TODO: adjust range concurrency number to control total concurrency in the future. + logutil.Logger(ctx).Info(LitInfoChgMemSetting, + zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), + zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), + zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) +} + +// tryAggressiveMemory lightning memory parameters according memory root's max limitation. +func tryAggressiveMemory(ctx context.Context, memRoot MemRoot, cfg *lightning.Config) bool { + var defaultMemSize int64 + defaultMemSize = int64(int(cfg.TikvImporter.LocalWriterMemCacheSize) * cfg.TikvImporter.RangeConcurrency) + defaultMemSize += int64(cfg.TikvImporter.EngineMemCacheSize) + + if (defaultMemSize + memRoot.CurrentUsage()) > memRoot.MaxMemoryQuota() { + return false + } + logutil.Logger(ctx).Info(LitInfoChgMemSetting, + zap.Int64("local writer memory cache size", int64(cfg.TikvImporter.LocalWriterMemCacheSize)), + zap.Int64("engine memory cache size", int64(cfg.TikvImporter.EngineMemCacheSize)), + zap.Int("range concurrency", cfg.TikvImporter.RangeConcurrency)) + return true +} + +// defaultImportantVariables is used in obtainImportantVariables to retrieve the system +// variables from downstream which may affect KV encode result. The values record the default +// values if missing. +var defaultImportantVariables = map[string]string{ + "max_allowed_packet": "67108864", // 64MB + "div_precision_increment": "4", + "time_zone": "SYSTEM", + "lc_time_names": "en_US", + "default_week_format": "0", + "block_encryption_mode": "aes-128-ecb", + "group_concat_max_len": "1024", + "tidb_row_format_version": "1", +} diff --git a/ddl/ingest/disk_root.go b/pkg/ddl/ingest/disk_root.go similarity index 96% rename from ddl/ingest/disk_root.go rename to pkg/ddl/ingest/disk_root.go index 9af58325cb3ee..9fbef5be7ae43 100644 --- a/ddl/ingest/disk_root.go +++ b/pkg/ddl/ingest/disk_root.go @@ -23,9 +23,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" lcom "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/ingest/engine.go b/pkg/ddl/ingest/engine.go similarity index 98% rename from ddl/ingest/engine.go rename to pkg/ddl/ingest/engine.go index 13ef94b41fbbe..c1b0ec91fb9b5 100644 --- a/ddl/ingest/engine.go +++ b/pkg/ddl/ingest/engine.go @@ -25,9 +25,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/common" "github.com/pingcap/tidb/br/pkg/lightning/config" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/generic" - "github.com/pingcap/tidb/util/logutil" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/generic" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/ingest/engine_mgr.go b/pkg/ddl/ingest/engine_mgr.go similarity index 97% rename from ddl/ingest/engine_mgr.go rename to pkg/ddl/ingest/engine_mgr.go index b488c58a81ce9..aefabbcea7278 100644 --- a/ddl/ingest/engine_mgr.go +++ b/pkg/ddl/ingest/engine_mgr.go @@ -19,8 +19,8 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/backend" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ddl/ingest/env.go b/pkg/ddl/ingest/env.go similarity index 94% rename from ddl/ingest/env.go rename to pkg/ddl/ingest/env.go index ffbe7706212e4..ab440ca6fea71 100644 --- a/ddl/ingest/env.go +++ b/pkg/ddl/ingest/env.go @@ -21,11 +21,11 @@ import ( "strconv" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" "go.uber.org/zap" ) diff --git a/ddl/ingest/env_test.go b/pkg/ddl/ingest/env_test.go similarity index 92% rename from ddl/ingest/env_test.go rename to pkg/ddl/ingest/env_test.go index 90d37ea5c3101..26dcb206e0c9e 100644 --- a/ddl/ingest/env_test.go +++ b/pkg/ddl/ingest/env_test.go @@ -17,8 +17,8 @@ package ingest_test import ( "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/ingest" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/ingest" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/ingest/integration_test.go b/pkg/ddl/ingest/integration_test.go new file mode 100644 index 0000000000000..d7b91579befa4 --- /dev/null +++ b/pkg/ddl/ingest/integration_test.go @@ -0,0 +1,311 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package ingest_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/ingest" + ingesttestutil "github.com/pingcap/tidb/pkg/ddl/ingest/testutil" + "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/tests/realtikvtest" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestAddIndexIngestGeneratedColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + assertLastNDDLUseIngest := func(n int) { + tk.MustExec("admin check table t;") + rows := tk.MustQuery(fmt.Sprintf("admin show ddl jobs %d;", n)).Rows() + require.Len(t, rows, n) + for i := 0; i < n; i++ { + //nolint: forcetypeassert + jobTp := rows[i][3].(string) + require.True(t, strings.Contains(jobTp, "ingest"), jobTp) + } + } + tk.MustExec("create table t (a int, b int, c int as (b+10), d int as (b+c), primary key (a) clustered);") + tk.MustExec("insert into t (a, b) values (1, 1), (2, 2), (3, 3);") + tk.MustExec("alter table t add index idx(c);") + tk.MustExec("alter table t add index idx1(c, a);") + tk.MustExec("alter table t add index idx2(a);") + tk.MustExec("alter table t add index idx3(d);") + tk.MustExec("alter table t add index idx4(d, c);") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 11 12", "2 2 12 14", "3 3 13 16")) + assertLastNDDLUseIngest(5) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int, b char(10), c char(10) as (concat(b, 'x')), d int, e char(20) as (c));") + tk.MustExec("insert into t (a, b, d) values (1, '1', 1), (2, '2', 2), (3, '3', 3);") + tk.MustExec("alter table t add index idx(c);") + tk.MustExec("alter table t add index idx1(a, c);") + tk.MustExec("alter table t add index idx2(c(7));") + tk.MustExec("alter table t add index idx3(e(5));") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 1x 1 1x", "2 2 2x 2 2x", "3 3 3x 3 3x")) + assertLastNDDLUseIngest(4) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int, b char(10), c tinyint, d int as (a + c), e bigint as (d - a), primary key(b, a) clustered);") + tk.MustExec("insert into t (a, b, c) values (1, '1', 1), (2, '2', 2), (3, '3', 3);") + tk.MustExec("alter table t add index idx(d);") + tk.MustExec("alter table t add index idx1(b(2), d);") + tk.MustExec("alter table t add index idx2(d, c);") + tk.MustExec("alter table t add index idx3(e);") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 1 1 2 1", "2 2 2 4 2", "3 3 3 6 3")) + assertLastNDDLUseIngest(4) +} + +func TestIngestError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 1;") + tk.MustExec("create table t (a int primary key, b int);") + for i := 0; i < 4; i++ { + tk.MustExec(fmt.Sprintf("insert into t values (%d, %d);", i*10000, i*10000)) + } + tk.MustQuery("split table t between (0) and (50000) regions 5;").Check(testkit.Rows("4 1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockCopSenderError", "1*return")) + tk.MustExec("alter table t add index idx(a);") + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockCopSenderError")) + tk.MustExec("admin check table t;") + rows := tk.MustQuery("admin show ddl jobs 1;").Rows() + //nolint: forcetypeassert + jobTp := rows[0][3].(string) + require.True(t, strings.Contains(jobTp, "ingest"), jobTp) + + tk.MustExec("drop table t;") + tk.MustExec("create table t (a int primary key, b int);") + for i := 0; i < 4; i++ { + tk.MustExec(fmt.Sprintf("insert into t values (%d, %d);", i*10000, i*10000)) + } + tk.MustQuery("split table t between (0) and (50000) regions 5;").Check(testkit.Rows("4 1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockLocalWriterError", "1*return")) + tk.MustExec("alter table t add index idx(a);") + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockLocalWriterError")) + tk.MustExec("admin check table t;") + rows = tk.MustQuery("admin show ddl jobs 1;").Rows() + //nolint: forcetypeassert + jobTp = rows[0][3].(string) + require.True(t, strings.Contains(jobTp, "ingest"), jobTp) +} + +func TestAddIndexIngestPanic(t *testing.T) { + store := realtikvtest.CreateMockStoreAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + // Mock panic on coprocessor request sender. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockCopSenderPanic", "return(true)")) + tk.MustExec("create table t (a int, b int, c int, d int, primary key (a) clustered);") + tk.MustExec("insert into t (a, b, c, d) values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") + tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrReorgPanic) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockCopSenderPanic")) + + // Mock panic on local engine writer. + tk.MustExec("drop table t;") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockLocalWriterPanic", "return")) + tk.MustExec("create table t (a int, b int, c int, d int, primary key (a) clustered);") + tk.MustExec("insert into t (a, b, c, d) values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") + tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrReorgPanic) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockLocalWriterPanic")) +} + +func TestAddIndexIngestCancel(t *testing.T) { + store, dom := realtikvtest.CreateMockStoreAndDomainAndSetup(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("create table t (a int, b int);") + tk.MustExec("insert into t (a, b) values (1, 1), (2, 2), (3, 3);") + defHook := dom.DDL().GetHook() + customHook := newTestCallBack(t, dom) + cancelled := false + customHook.OnJobRunBeforeExported = func(job *model.Job) { + if cancelled { + return + } + if job.Type == model.ActionAddIndex && job.SchemaState == model.StateWriteReorganization { + idx := testutil.FindIdxInfo(dom, "test", "t", "idx") + if idx == nil { + return + } + if idx.BackfillState == model.BackfillStateRunning { + tk2 := testkit.NewTestKit(t, store) + rs, err := tk2.Exec(fmt.Sprintf("admin cancel ddl jobs %d", job.ID)) + assert.NoError(t, err) + assert.NoError(t, rs.Close()) + cancelled = true + } + } + } + dom.DDL().SetHook(customHook) + tk.MustGetErrCode("alter table t add index idx(b);", errno.ErrCancelledDDLJob) + require.True(t, cancelled) + dom.DDL().SetHook(defHook) + ok, err := ingest.LitBackCtxMgr.CheckAvailable() + require.NoError(t, err) + require.True(t, ok) +} + +type testCallback struct { + ddl.Callback + OnJobRunBeforeExported func(job *model.Job) +} + +func newTestCallBack(t *testing.T, dom *domain.Domain) *testCallback { + defHookFactory, err := ddl.GetCustomizedHook("default_hook") + require.NoError(t, err) + return &testCallback{ + Callback: defHookFactory(dom), + } +} + +func (c *testCallback) OnJobRunBefore(job *model.Job) { + if c.OnJobRunBeforeExported != nil { + c.OnJobRunBeforeExported(job) + } +} + +func TestIngestPartitionRowCount(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec(`create table t (a int, b int, c int as (b+10), d int as (b+c), + primary key (a) clustered) partition by range (a) ( + partition p0 values less than (1), + partition p1 values less than (2), + partition p2 values less than MAXVALUE);`) + tk.MustExec("insert into t (a, b) values (0, 0), (1, 1), (2, 2);") + tk.MustExec("alter table t add index idx(d);") + rows := tk.MustQuery("admin show ddl jobs 1;").Rows() + require.Len(t, rows, 1) + //nolint: forcetypeassert + rowCount := rows[0][7].(string) + require.Equal(t, "3", rowCount) + tk.MustExec("admin check table t;") +} + +func TestAddIndexIngestClientError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("CREATE TABLE t1 (f1 json);") + tk.MustExec(`insert into t1(f1) values (cast("null" as json));`) + tk.MustGetErrCode("create index i1 on t1((cast(f1 as unsigned array)));", errno.ErrInvalidJSONValueForFuncIndex) +} + +func TestAddIndexCancelOnNoneState(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tkCancel := testkit.NewTestKit(t, store) + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("use test") + tk.MustExec(`create table t (c1 int, c2 int, c3 int)`) + tk.MustExec("insert into t values(1, 1, 1);") + + hook := &callback.TestDDLCallback{Do: dom} + first := true + hook.OnJobRunBeforeExported = func(job *model.Job) { + if job.SchemaState == model.StateNone && first { + _, err := tkCancel.Exec(fmt.Sprintf("admin cancel ddl jobs %d", job.ID)) + assert.NoError(t, err) + first = false + } + } + dom.DDL().SetHook(hook.Clone()) + tk.MustGetErrCode("alter table t add index idx1(c1)", errno.ErrCancelledDDLJob) + available, err := ingest.LitBackCtxMgr.CheckAvailable() + require.NoError(t, err) + require.True(t, available) +} + +func TestAddIndexIngestTimezone(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("SET time_zone = '-06:00';") + tk.MustExec("create table t (`src` varchar(48),`t` timestamp,`timezone` varchar(100));") + tk.MustExec("insert into t values('2000-07-29 23:15:30','2000-07-29 23:15:30','-6:00');") + // Test Daylight time. + tk.MustExec("insert into t values('1991-07-21 00:00:00','1991-07-21 00:00:00','-6:00');") + tk.MustExec("alter table t add index idx(t);") + tk.MustExec("admin check table t;") + + tk.MustExec("alter table t drop index idx;") + tk.MustExec("SET time_zone = 'Asia/Shanghai';") + tk.MustExec("insert into t values('2000-07-29 23:15:30','2000-07-29 23:15:30', '+8:00');") + tk.MustExec("insert into t values('1991-07-21 00:00:00','1991-07-21 00:00:00','+8:00');") + tk.MustExec("alter table t add index idx(t);") + tk.MustExec("admin check table t;") +} + +func TestAddIndexIngestMultiSchemaChange(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + defer ingesttestutil.InjectMockBackendMgr(t, store)() + + tk.MustExec("create table t (a int, b int);") + tk.MustExec("insert into t values(1, 1), (2, 2);") + tk.MustExec("alter table t add index idx(a), add index idx_2(b);") + tk.MustExec("admin check table t;") + tk.MustExec("alter table t drop index idx, drop index idx_2;") + tk.MustExec(`alter table t + add unique index idx(a), + add unique index idx_2(b, a), + add unique index idx_3(b);`) + tk.MustExec("admin check table t;") + + tk.MustExec("drop table t;") + tk.MustExec(`create table t (a int, b int, c int as (b+10), d int as (b+c), + primary key (a) clustered) partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20), + partition p2 values less than MAXVALUE);`) + for i := 0; i < 30; i++ { + insertSQL := fmt.Sprintf("insert into t (a, b) values (%d, %d);", i, i) + tk.MustExec(insertSQL) + } + tk.MustExec("alter table t add index idx_a(a), add index idx_ab(a, b), add index idx_d(d);") + tk.MustExec("admin check table t;") +} diff --git a/ddl/ingest/main_test.go b/pkg/ddl/ingest/main_test.go similarity index 100% rename from ddl/ingest/main_test.go rename to pkg/ddl/ingest/main_test.go diff --git a/ddl/ingest/mem_root.go b/pkg/ddl/ingest/mem_root.go similarity index 100% rename from ddl/ingest/mem_root.go rename to pkg/ddl/ingest/mem_root.go diff --git a/ddl/ingest/mem_root_test.go b/pkg/ddl/ingest/mem_root_test.go similarity index 98% rename from ddl/ingest/mem_root_test.go rename to pkg/ddl/ingest/mem_root_test.go index 60ff34d1d2572..70fc072b63b94 100644 --- a/ddl/ingest/mem_root_test.go +++ b/pkg/ddl/ingest/mem_root_test.go @@ -17,7 +17,7 @@ package ingest_test import ( "testing" - "github.com/pingcap/tidb/ddl/ingest" + "github.com/pingcap/tidb/pkg/ddl/ingest" "github.com/stretchr/testify/require" ) diff --git a/ddl/ingest/message.go b/pkg/ddl/ingest/message.go similarity index 97% rename from ddl/ingest/message.go rename to pkg/ddl/ingest/message.go index c1f019081b0cb..0f74de19dd0b4 100644 --- a/ddl/ingest/message.go +++ b/pkg/ddl/ingest/message.go @@ -17,8 +17,8 @@ package ingest import ( "context" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/ddl/ingest/mock.go b/pkg/ddl/ingest/mock.go new file mode 100644 index 0000000000000..5a13fad34e976 --- /dev/null +++ b/pkg/ddl/ingest/mock.go @@ -0,0 +1,234 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package ingest + +import ( + "context" + "encoding/hex" + "sync" + + "github.com/pingcap/tidb/br/pkg/lightning/backend/local" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" + clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" +) + +// MockBackendCtxMgr is a mock backend context manager. +type MockBackendCtxMgr struct { + sessCtxProvider func() sessionctx.Context + runningJobs map[int64]*MockBackendCtx +} + +// NewMockBackendCtxMgr creates a new mock backend context manager. +func NewMockBackendCtxMgr(sessCtxProvider func() sessionctx.Context) *MockBackendCtxMgr { + return &MockBackendCtxMgr{ + sessCtxProvider: sessCtxProvider, + runningJobs: make(map[int64]*MockBackendCtx), + } +} + +// CheckAvailable implements BackendCtxMgr.Available interface. +func (m *MockBackendCtxMgr) CheckAvailable() (bool, error) { + return len(m.runningJobs) == 0, nil +} + +// Register implements BackendCtxMgr.Register interface. +func (m *MockBackendCtxMgr) Register(_ context.Context, _ bool, jobID int64, _ *clientv3.Client, _ string) (BackendCtx, error) { + logutil.BgLogger().Info("mock backend mgr register", zap.Int64("jobID", jobID)) + if mockCtx, ok := m.runningJobs[jobID]; ok { + return mockCtx, nil + } + sessCtx := m.sessCtxProvider() + mockCtx := &MockBackendCtx{ + mu: sync.Mutex{}, + sessCtx: sessCtx, + } + m.runningJobs[jobID] = mockCtx + return mockCtx, nil +} + +// Unregister implements BackendCtxMgr.Unregister interface. +func (m *MockBackendCtxMgr) Unregister(jobID int64) { + if mCtx, ok := m.runningJobs[jobID]; ok { + mCtx.sessCtx.StmtCommit(context.Background()) + err := mCtx.sessCtx.CommitTxn(context.Background()) + logutil.BgLogger().Info("mock backend mgr unregister", zap.Int64("jobID", jobID), zap.Error(err)) + delete(m.runningJobs, jobID) + if mCtx.checkpointMgr != nil { + mCtx.checkpointMgr.Close() + } + } +} + +// Load implements BackendCtxMgr.Load interface. +func (m *MockBackendCtxMgr) Load(jobID int64) (BackendCtx, bool) { + logutil.BgLogger().Info("mock backend mgr load", zap.Int64("jobID", jobID)) + if mockCtx, ok := m.runningJobs[jobID]; ok { + return mockCtx, true + } + return nil, false +} + +// ResetSessCtx is only used for mocking test. +func (m *MockBackendCtxMgr) ResetSessCtx() { + for _, mockCtx := range m.runningJobs { + mockCtx.sessCtx = m.sessCtxProvider() + } +} + +// MockBackendCtx is a mock backend context. +type MockBackendCtx struct { + sessCtx sessionctx.Context + mu sync.Mutex + checkpointMgr *CheckpointManager +} + +// Register implements BackendCtx.Register interface. +func (m *MockBackendCtx) Register(jobID, indexID int64, _, _ string) (Engine, error) { + logutil.BgLogger().Info("mock backend ctx register", zap.Int64("jobID", jobID), zap.Int64("indexID", indexID)) + return &MockEngineInfo{sessCtx: m.sessCtx, mu: &m.mu}, nil +} + +// Unregister implements BackendCtx.Unregister interface. +func (*MockBackendCtx) Unregister(jobID, indexID int64) { + logutil.BgLogger().Info("mock backend ctx unregister", zap.Int64("jobID", jobID), zap.Int64("indexID", indexID)) +} + +// CollectRemoteDuplicateRows implements BackendCtx.CollectRemoteDuplicateRows interface. +func (*MockBackendCtx) CollectRemoteDuplicateRows(indexID int64, _ table.Table) error { + logutil.BgLogger().Info("mock backend ctx collect remote duplicate rows", zap.Int64("indexID", indexID)) + return nil +} + +// FinishImport implements BackendCtx.FinishImport interface. +func (*MockBackendCtx) FinishImport(indexID int64, _ bool, _ table.Table) error { + logutil.BgLogger().Info("mock backend ctx finish import", zap.Int64("indexID", indexID)) + return nil +} + +// ResetWorkers implements BackendCtx.ResetWorkers interface. +func (*MockBackendCtx) ResetWorkers(_ int64) { +} + +// Flush implements BackendCtx.Flush interface. +func (*MockBackendCtx) Flush(_ int64, _ FlushMode) (flushed bool, imported bool, err error) { + return false, false, nil +} + +// Done implements BackendCtx.Done interface. +func (*MockBackendCtx) Done() bool { + return false +} + +// SetDone implements BackendCtx.SetDone interface. +func (*MockBackendCtx) SetDone() { +} + +// AttachCheckpointManager attaches a checkpoint manager to the backend context. +func (m *MockBackendCtx) AttachCheckpointManager(mgr *CheckpointManager) { + m.checkpointMgr = mgr +} + +// GetCheckpointManager returns the checkpoint manager attached to the backend context. +func (m *MockBackendCtx) GetCheckpointManager() *CheckpointManager { + return m.checkpointMgr +} + +// GetLocalBackend returns the local backend. +func (*MockBackendCtx) GetLocalBackend() *local.Backend { + return nil +} + +// MockWriteHook the hook for write in mock engine. +type MockWriteHook func(key, val []byte) + +// MockEngineInfo is a mock engine info. +type MockEngineInfo struct { + sessCtx sessionctx.Context + mu *sync.Mutex + + onWrite MockWriteHook +} + +// NewMockEngineInfo creates a new mock engine info. +func NewMockEngineInfo(sessCtx sessionctx.Context) *MockEngineInfo { + return &MockEngineInfo{ + sessCtx: sessCtx, + mu: &sync.Mutex{}, + } +} + +// Flush implements Engine.Flush interface. +func (*MockEngineInfo) Flush() error { + return nil +} + +// ImportAndClean implements Engine.ImportAndClean interface. +func (*MockEngineInfo) ImportAndClean() error { + return nil +} + +// Clean implements Engine.Clean interface. +func (*MockEngineInfo) Clean() { +} + +// SetHook set the write hook. +func (m *MockEngineInfo) SetHook(onWrite func(key, val []byte)) { + m.onWrite = onWrite +} + +// CreateWriter implements Engine.CreateWriter interface. +func (m *MockEngineInfo) CreateWriter(id int) (Writer, error) { + logutil.BgLogger().Info("mock engine info create writer", zap.Int("id", id)) + return &MockWriter{sessCtx: m.sessCtx, mu: m.mu, onWrite: m.onWrite}, nil +} + +// MockWriter is a mock writer. +type MockWriter struct { + sessCtx sessionctx.Context + mu *sync.Mutex + onWrite MockWriteHook +} + +// WriteRow implements Writer.WriteRow interface. +func (m *MockWriter) WriteRow(_ context.Context, key, idxVal []byte, _ kv.Handle) error { + logutil.BgLogger().Info("mock writer write row", + zap.String("key", hex.EncodeToString(key)), + zap.String("idxVal", hex.EncodeToString(idxVal))) + m.mu.Lock() + defer m.mu.Unlock() + if m.onWrite != nil { + m.onWrite(key, idxVal) + return nil + } + txn, err := m.sessCtx.Txn(true) + if err != nil { + return err + } + return txn.Set(key, idxVal) +} + +// LockForWrite implements Writer.LockForWrite interface. +func (*MockWriter) LockForWrite() func() { + return func() {} +} + +// Close implements Writer.Close interface. +func (*MockWriter) Close(_ context.Context) error { + return nil +} diff --git a/pkg/ddl/ingest/tests/BUILD.bazel b/pkg/ddl/ingest/tests/BUILD.bazel new file mode 100644 index 0000000000000..8ee0e08783e45 --- /dev/null +++ b/pkg/ddl/ingest/tests/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tests_test", + timeout = "short", + srcs = ["partition_table_test.go"], + flaky = True, + race = "off", + deps = [ + "//pkg/config", + "//pkg/ddl/ingest", + "//pkg/ddl/ingest/testutil", + "//pkg/ddl/util/callback", + "//pkg/parser/model", + "//pkg/testkit", + ], +) diff --git a/pkg/ddl/ingest/tests/partition_table_test.go b/pkg/ddl/ingest/tests/partition_table_test.go new file mode 100644 index 0000000000000..10d7ffbc43ebe --- /dev/null +++ b/pkg/ddl/ingest/tests/partition_table_test.go @@ -0,0 +1,74 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/ingest" + ingesttestutil "github.com/pingcap/tidb/pkg/ddl/ingest/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" +) + +func TestAddIndexIngestRecoverPartition(t *testing.T) { + port := config.GetGlobalConfig().Port + tc := testkit.NewDistExecutionContext(t, 3) + defer tc.Close() + defer ingesttestutil.InjectMockBackendMgr(t, tc.Store)() + tk := testkit.NewTestKit(t, tc.Store) + tk.MustExec("use test;") + tk.MustExec("create table t (a int primary key, b int) partition by hash(a) partitions 8;") + tk.MustExec("insert into t values (2, 3), (3, 3), (5, 5);") + + partCnt := 0 + changeOwner0To1 := func(job *model.Job, _ int64) { + partCnt++ + if partCnt == 3 { + tc.SetOwner(1) + // TODO(tangenta): mock multiple backends in a better way. + //nolint: forcetypeassert + // TODO(tangenta): When owner changes, wait last ddl owner's DDL dispatching loop exits. + ingest.LitBackCtxMgr.(*ingest.MockBackendCtxMgr).ResetSessCtx() + bc, _ := ingest.LitBackCtxMgr.Load(job.ID) + bc.GetCheckpointManager().Close() + bc.AttachCheckpointManager(nil) + config.GetGlobalConfig().Port = port + 1 + } + } + changeOwner1To2 := func(job *model.Job, _ int64) { + partCnt++ + if partCnt == 6 { + tc.SetOwner(2) + //nolint: forcetypeassert + ingest.LitBackCtxMgr.(*ingest.MockBackendCtxMgr).ResetSessCtx() + bc, _ := ingest.LitBackCtxMgr.Load(job.ID) + bc.GetCheckpointManager().Close() + bc.AttachCheckpointManager(nil) + config.GetGlobalConfig().Port = port + 2 + } + } + tc.SetOwner(0) + hook0 := &callback.TestDDLCallback{} + hook0.OnUpdateReorgInfoExported = changeOwner0To1 + hook1 := &callback.TestDDLCallback{} + hook1.OnUpdateReorgInfoExported = changeOwner1To2 + tc.GetDomain(0).DDL().SetHook(hook0) + tc.GetDomain(1).DDL().SetHook(hook1) + tk.MustExec("alter table t add index idx(b);") + tk.MustExec("admin check table t;") +} diff --git a/pkg/ddl/ingest/testutil/BUILD.bazel b/pkg/ddl/ingest/testutil/BUILD.bazel new file mode 100644 index 0000000000000..55c9c927aaa34 --- /dev/null +++ b/pkg/ddl/ingest/testutil/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/ddl/ingest/testutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/ingest", + "//pkg/kv", + "//pkg/sessionctx", + "//pkg/testkit", + ], +) diff --git a/pkg/ddl/ingest/testutil/testutil.go b/pkg/ddl/ingest/testutil/testutil.go new file mode 100644 index 0000000000000..a95b40821003b --- /dev/null +++ b/pkg/ddl/ingest/testutil/testutil.go @@ -0,0 +1,43 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package testutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" +) + +// InjectMockBackendMgr mock LitBackCtxMgr. +func InjectMockBackendMgr(t *testing.T, store kv.Storage) (restore func()) { + tk := testkit.NewTestKit(t, store) + oldLitBackendMgr := ingest.LitBackCtxMgr + oldInitialized := ingest.LitInitialized + + ingest.LitBackCtxMgr = ingest.NewMockBackendCtxMgr(func() sessionctx.Context { + tk.MustExec("rollback;") + tk.MustExec("begin;") + return tk.Session() + }) + ingest.LitInitialized = true + + return func() { + ingest.LitBackCtxMgr = oldLitBackendMgr + ingest.LitInitialized = oldInitialized + } +} diff --git a/pkg/ddl/integration_test.go b/pkg/ddl/integration_test.go new file mode 100644 index 0000000000000..e323154dc1bb9 --- /dev/null +++ b/pkg/ddl/integration_test.go @@ -0,0 +1,60 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestDDLStatementsBackFill(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + needReorg := false + callback := &callback.TestDDLCallback{ + Do: dom, + } + onJobUpdatedExportedFunc := func(job *model.Job) { + if job.SchemaState == model.StateWriteReorganization { + needReorg = true + } + } + callback.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + dom.DDL().SetHook(callback) + tk.MustExec("create table t (a int, b char(65));") + tk.MustExec("insert into t values (1, '123');") + testCases := []struct { + ddlSQL string + expectedNeedReorg bool + }{ + {"alter table t modify column a bigint;", false}, + {"alter table t modify column b char(255);", false}, + {"alter table t modify column a varchar(100);", true}, + {"create table t1 (a int, b int);", false}, + {"alter table t1 add index idx_a(a);", true}, + {"alter table t1 add primary key(b) nonclustered;", true}, + {"alter table t1 drop primary key;", false}, + } + for _, tc := range testCases { + needReorg = false + tk.MustExec(tc.ddlSQL) + require.Equal(t, tc.expectedNeedReorg, needReorg, tc) + } +} diff --git a/pkg/ddl/internal/session/BUILD.bazel b/pkg/ddl/internal/session/BUILD.bazel new file mode 100644 index 0000000000000..b929e0f3cfdb7 --- /dev/null +++ b/pkg/ddl/internal/session/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "session", + srcs = [ + "session.go", + "session_pool.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/internal/session", + visibility = ["//pkg/ddl:__subpackages__"], + deps = [ + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessiontxn", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/sqlexec", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "session_test", + timeout = "short", + srcs = ["session_pool_test.go"], + flaky = True, + deps = [ + ":session", + "//pkg/testkit", + "@com_github_ngaut_pools//:pools", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/ddl/internal/session/session.go b/pkg/ddl/internal/session/session.go new file mode 100644 index 0000000000000..cd07216bd664d --- /dev/null +++ b/pkg/ddl/internal/session/session.go @@ -0,0 +1,137 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +// Session wraps sessionctx.Context for transaction usage. +type Session struct { + sessionctx.Context +} + +// NewSession creates a new Session. +func NewSession(s sessionctx.Context) *Session { + return &Session{s} +} + +// Begin starts a transaction. +func (s *Session) Begin() error { + err := sessiontxn.NewTxn(context.Background(), s.Context) + if err != nil { + return err + } + s.GetSessionVars().SetInTxn(true) + return nil +} + +// Commit commits the transaction. +func (s *Session) Commit() error { + s.StmtCommit(context.Background()) + return s.CommitTxn(context.Background()) +} + +// Txn activate and returns the current transaction. +func (s *Session) Txn() (kv.Transaction, error) { + return s.Context.Txn(true) +} + +// Rollback aborts the transaction. +func (s *Session) Rollback() { + s.StmtRollback(context.Background(), false) + s.RollbackTxn(context.Background()) +} + +// Reset resets the session. +func (s *Session) Reset() { + s.StmtRollback(context.Background(), false) +} + +// Execute executes a query. +func (s *Session) Execute(ctx context.Context, query string, label string) ([]chunk.Row, error) { + startTime := time.Now() + var err error + defer func() { + metrics.DDLJobTableDuration.WithLabelValues(label + "-" + metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + }() + + if ctx.Value(kv.RequestSourceKey) == nil { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) + } + rs, err := s.Context.(sqlexec.SQLExecutor).ExecuteInternal(ctx, query) + if err != nil { + return nil, errors.Trace(err) + } + + if rs == nil { + return nil, nil + } + var rows []chunk.Row + defer terror.Call(rs.Close) + if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { + return nil, errors.Trace(err) + } + return rows, nil +} + +// Session returns the sessionctx.Context. +func (s *Session) Session() sessionctx.Context { + return s.Context +} + +// RunInTxn runs a function in a transaction. +func (s *Session) RunInTxn(f func(*Session) error) (err error) { + err = s.Begin() + if err != nil { + return err + } + failpoint.Inject("NotifyBeginTxnCh", func(val failpoint.Value) { + //nolint:forcetypeassert + v := val.(int) + if v == 1 { + MockDDLOnce = 1 + TestNotifyBeginTxnCh <- struct{}{} + } else if v == 2 && MockDDLOnce == 1 { + <-TestNotifyBeginTxnCh + MockDDLOnce = 0 + } + }) + + err = f(s) + if err != nil { + s.Rollback() + return + } + return errors.Trace(s.Commit()) +} + +var ( + // MockDDLOnce is only used for test. + MockDDLOnce = int64(0) + // TestNotifyBeginTxnCh is used for if the txn is beginning in RunInTxn. + TestNotifyBeginTxnCh = make(chan struct{}) +) diff --git a/ddl/internal/session/session_pool.go b/pkg/ddl/internal/session/session_pool.go similarity index 91% rename from ddl/internal/session/session_pool.go rename to pkg/ddl/internal/session/session_pool.go index e7afd726609d7..ac10d872e73ed 100644 --- a/ddl/internal/session/session_pool.go +++ b/pkg/ddl/internal/session/session_pool.go @@ -20,12 +20,12 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" "go.uber.org/zap" ) diff --git a/ddl/internal/session/session_pool_test.go b/pkg/ddl/internal/session/session_pool_test.go similarity index 95% rename from ddl/internal/session/session_pool_test.go rename to pkg/ddl/internal/session/session_pool_test.go index 96de32dad51bc..37729d5be4381 100644 --- a/ddl/internal/session/session_pool_test.go +++ b/pkg/ddl/internal/session/session_pool_test.go @@ -19,8 +19,8 @@ import ( "testing" "github.com/ngaut/pools" - "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/ddl/job_table.go b/pkg/ddl/job_table.go similarity index 97% rename from ddl/job_table.go rename to pkg/ddl/job_table.go index b3ea152d694ed..2e494a5a182f7 100644 --- a/ddl/job_table.go +++ b/pkg/ddl/job_table.go @@ -28,21 +28,21 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/ddl/ingest" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - tidb_util "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + tidb_util "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/ddl/job_table_test.go b/pkg/ddl/job_table_test.go similarity index 92% rename from ddl/job_table_test.go rename to pkg/ddl/job_table_test.go index d1f8a619335c4..42190eb5baefd 100644 --- a/ddl/job_table_test.go +++ b/pkg/ddl/job_table_test.go @@ -24,12 +24,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) @@ -95,10 +95,10 @@ func TestDDLScheduling(t *testing.T) { record = append(record, job.ID) } - err := failpoint.Enable("github.com/pingcap/tidb/ddl/mockRunJobTime", `return(true)`) + err := failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockRunJobTime", `return(true)`) require.NoError(t, err) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/ddl/mockRunJobTime") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockRunJobTime") require.NoError(t, err) }() @@ -199,9 +199,9 @@ func TestUpgradingRelatedJobState(t *testing.T) { {"alter table e2 add index idx3(id)", model.JobStateRollbackDone, errors.New("[ddl:8214]Cancelled DDL job")}, } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpgradingState", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpgradingState", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpgradingState")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpgradingState")) }() num := 0 diff --git a/pkg/ddl/label/BUILD.bazel b/pkg/ddl/label/BUILD.bazel new file mode 100644 index 0000000000000..3e8f40d4c88ca --- /dev/null +++ b/pkg/ddl/label/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "label", + srcs = [ + "attributes.go", + "errors.go", + "rule.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/label", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/tablecodec", + "//pkg/util/codec", + "@in_gopkg_yaml_v2//:yaml_v2", + ], +) + +go_test( + name = "label_test", + timeout = "short", + srcs = [ + "attributes_test.go", + "main_test.go", + "rule_test.go", + ], + embed = [":label"], + flaky = True, + shard_count = 8, + deps = [ + "//pkg/parser/ast", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/ddl/label/attributes.go b/pkg/ddl/label/attributes.go similarity index 100% rename from ddl/label/attributes.go rename to pkg/ddl/label/attributes.go diff --git a/ddl/label/attributes_test.go b/pkg/ddl/label/attributes_test.go similarity index 100% rename from ddl/label/attributes_test.go rename to pkg/ddl/label/attributes_test.go diff --git a/ddl/label/errors.go b/pkg/ddl/label/errors.go similarity index 100% rename from ddl/label/errors.go rename to pkg/ddl/label/errors.go diff --git a/pkg/ddl/label/main_test.go b/pkg/ddl/label/main_test.go new file mode 100644 index 0000000000000..ef7a39bb4a235 --- /dev/null +++ b/pkg/ddl/label/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package label + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/label/rule.go b/pkg/ddl/label/rule.go new file mode 100644 index 0000000000000..e3d08999ee8ed --- /dev/null +++ b/pkg/ddl/label/rule.go @@ -0,0 +1,172 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package label + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "slices" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "gopkg.in/yaml.v2" +) + +const ( + // IDPrefix is the prefix for label rule ID. + IDPrefix = "schema" + ruleType = "key-range" +) + +const ( + // RuleIndexDefault is the default index for a rule. + RuleIndexDefault int = iota + // RuleIndexDatabase is the index for a rule of database. + RuleIndexDatabase + // RuleIndexTable is the index for a rule of table. + RuleIndexTable + // RuleIndexPartition is the index for a rule of partition. + RuleIndexPartition +) + +var ( + // TableIDFormat is the format of the label rule ID for a table. + // The format follows "schema/database_name/table_name". + TableIDFormat = "%s/%s/%s" + // PartitionIDFormat is the format of the label rule ID for a partition. + // The format follows "schema/database_name/table_name/partition_name". + PartitionIDFormat = "%s/%s/%s/%s" +) + +// Rule is used to establish the relationship between labels and a key range. +type Rule struct { + ID string `json:"id"` + Index int `json:"index"` + Labels Labels `json:"labels"` + RuleType string `json:"rule_type"` + Data []interface{} `json:"data"` +} + +// NewRule creates a rule. +func NewRule() *Rule { + return &Rule{} +} + +// ApplyAttributesSpec will transfer attributes defined in AttributesSpec to the labels. +func (r *Rule) ApplyAttributesSpec(spec *ast.AttributesSpec) error { + if spec.Default { + r.Labels = []Label{} + return nil + } + // construct a string list + attrBytes := []byte("[" + spec.Attributes + "]") + attributes := []string{} + err := yaml.UnmarshalStrict(attrBytes, &attributes) + if err != nil { + return err + } + r.Labels, err = NewLabels(attributes) + return err +} + +// String implements fmt.Stringer. +func (r *Rule) String() string { + t, err := json.Marshal(r) + if err != nil { + return "" + } + return string(t) +} + +// Clone clones a rule. +func (r *Rule) Clone() *Rule { + newRule := NewRule() + *newRule = *r + return newRule +} + +// Reset will reset the label rule for a table/partition with a given ID and names. +func (r *Rule) Reset(dbName, tableName, partName string, ids ...int64) *Rule { + isPartition := partName != "" + if isPartition { + r.ID = fmt.Sprintf(PartitionIDFormat, IDPrefix, dbName, tableName, partName) + } else { + r.ID = fmt.Sprintf(TableIDFormat, IDPrefix, dbName, tableName) + } + if len(r.Labels) == 0 { + return r + } + var hasDBKey, hasTableKey, hasPartitionKey bool + for i := range r.Labels { + switch r.Labels[i].Key { + case dbKey: + r.Labels[i].Value = dbName + hasDBKey = true + case tableKey: + r.Labels[i].Value = tableName + hasTableKey = true + case partitionKey: + if isPartition { + r.Labels[i].Value = partName + hasPartitionKey = true + } + default: + } + } + + if !hasDBKey { + r.Labels = append(r.Labels, Label{Key: dbKey, Value: dbName}) + } + + if !hasTableKey { + r.Labels = append(r.Labels, Label{Key: tableKey, Value: tableName}) + } + + if isPartition && !hasPartitionKey { + r.Labels = append(r.Labels, Label{Key: partitionKey, Value: partName}) + } + r.RuleType = ruleType + r.Data = []interface{}{} + slices.Sort(ids) + for i := 0; i < len(ids); i++ { + data := map[string]string{ + "start_key": hex.EncodeToString(codec.EncodeBytes(nil, tablecodec.GenTablePrefix(ids[i]))), + "end_key": hex.EncodeToString(codec.EncodeBytes(nil, tablecodec.GenTablePrefix(ids[i]+1))), + } + r.Data = append(r.Data, data) + } + // We may support more types later. + r.Index = RuleIndexTable + if isPartition { + r.Index = RuleIndexPartition + } + return r +} + +// RulePatch is the patch to update the label rules. +type RulePatch struct { + SetRules []*Rule `json:"sets"` + DeleteRules []string `json:"deletes"` +} + +// NewRulePatch returns a patch of rules which need to be set or deleted. +func NewRulePatch(setRules []*Rule, deleteRules []string) *RulePatch { + return &RulePatch{ + SetRules: setRules, + DeleteRules: deleteRules, + } +} diff --git a/ddl/label/rule_test.go b/pkg/ddl/label/rule_test.go similarity index 98% rename from ddl/label/rule_test.go rename to pkg/ddl/label/rule_test.go index 1fdc197471fe0..f392e3dc58c9e 100644 --- a/ddl/label/rule_test.go +++ b/pkg/ddl/label/rule_test.go @@ -17,7 +17,7 @@ package label import ( "testing" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/main_test.go b/pkg/ddl/main_test.go new file mode 100644 index 0000000000000..a763a5dff4ff1 --- /dev/null +++ b/pkg/ddl/main_test.go @@ -0,0 +1,74 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + + domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) + domain.SchemaOutOfDateRetryTimes.Store(50) + + autoid.SetStep(5000) + ddl.ReorgWaitTimeout = 30 * time.Millisecond + ddl.CheckBackfillJobFinishInterval = 50 * time.Millisecond + ddl.RunInGoTest = true + ddl.SetBatchInsertDeleteRangeSize(2) + + config.UpdateGlobal(func(conf *config.Config) { + // Test for table lock. + conf.EnableTableLock = true + conf.Instance.SlowThreshold = 10000 + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + + _, err := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "ddl: infosync.GlobalInfoSyncerInit: %v\n", err) + os.Exit(1) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/mock.go b/pkg/ddl/mock.go new file mode 100644 index 0000000000000..95071ad080133 --- /dev/null +++ b/pkg/ddl/mock.go @@ -0,0 +1,250 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + clientv3 "go.etcd.io/etcd/client/v3" + atomicutil "go.uber.org/atomic" +) + +// SetBatchInsertDeleteRangeSize sets the batch insert/delete range size in the test +func SetBatchInsertDeleteRangeSize(i int) { + batchInsertDeleteRangeSize = i +} + +var _ syncer.SchemaSyncer = &MockSchemaSyncer{} + +const mockCheckVersInterval = 2 * time.Millisecond + +// MockSchemaSyncer is a mock schema syncer, it is exported for testing. +type MockSchemaSyncer struct { + selfSchemaVersion int64 + mdlSchemaVersions sync.Map + globalVerCh chan clientv3.WatchResponse + mockSession chan struct{} +} + +// NewMockSchemaSyncer creates a new mock SchemaSyncer. +func NewMockSchemaSyncer() syncer.SchemaSyncer { + return &MockSchemaSyncer{} +} + +// Init implements SchemaSyncer.Init interface. +func (s *MockSchemaSyncer) Init(_ context.Context) error { + s.mdlSchemaVersions = sync.Map{} + s.globalVerCh = make(chan clientv3.WatchResponse, 1) + s.mockSession = make(chan struct{}, 1) + return nil +} + +// GlobalVersionCh implements SchemaSyncer.GlobalVersionCh interface. +func (s *MockSchemaSyncer) GlobalVersionCh() clientv3.WatchChan { + return s.globalVerCh +} + +// WatchGlobalSchemaVer implements SchemaSyncer.WatchGlobalSchemaVer interface. +func (*MockSchemaSyncer) WatchGlobalSchemaVer(context.Context) {} + +// UpdateSelfVersion implements SchemaSyncer.UpdateSelfVersion interface. +func (s *MockSchemaSyncer) UpdateSelfVersion(_ context.Context, jobID int64, version int64) error { + failpoint.Inject("mockUpdateMDLToETCDError", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(errors.New("mock update mdl to etcd error")) + } + }) + if variable.EnableMDL.Load() { + s.mdlSchemaVersions.Store(jobID, version) + } else { + atomic.StoreInt64(&s.selfSchemaVersion, version) + } + return nil +} + +// Done implements SchemaSyncer.Done interface. +func (s *MockSchemaSyncer) Done() <-chan struct{} { + return s.mockSession +} + +// CloseSession mockSession, it is exported for testing. +func (s *MockSchemaSyncer) CloseSession() { + close(s.mockSession) +} + +// Restart implements SchemaSyncer.Restart interface. +func (s *MockSchemaSyncer) Restart(_ context.Context) error { + s.mockSession = make(chan struct{}, 1) + return nil +} + +// OwnerUpdateGlobalVersion implements SchemaSyncer.OwnerUpdateGlobalVersion interface. +func (s *MockSchemaSyncer) OwnerUpdateGlobalVersion(_ context.Context, _ int64) error { + select { + case s.globalVerCh <- clientv3.WatchResponse{}: + default: + } + return nil +} + +// OwnerCheckAllVersions implements SchemaSyncer.OwnerCheckAllVersions interface. +func (s *MockSchemaSyncer) OwnerCheckAllVersions(ctx context.Context, jobID int64, latestVer int64) error { + ticker := time.NewTicker(mockCheckVersInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + failpoint.Inject("checkOwnerCheckAllVersionsWaitTime", func(v failpoint.Value) { + if v.(bool) { + panic("shouldn't happen") + } + }) + return errors.Trace(ctx.Err()) + case <-ticker.C: + if variable.EnableMDL.Load() { + ver, ok := s.mdlSchemaVersions.Load(jobID) + if ok && ver.(int64) >= latestVer { + return nil + } + } else { + ver := atomic.LoadInt64(&s.selfSchemaVersion) + if ver >= latestVer { + return nil + } + } + } + } +} + +// Close implements SchemaSyncer.Close interface. +func (*MockSchemaSyncer) Close() {} + +// NewMockStateSyncer creates a new mock StateSyncer. +func NewMockStateSyncer() syncer.StateSyncer { + return &MockStateSyncer{} +} + +// clusterState mocks cluster state. +// We move it from MockStateSyncer to here. Because we want to make it unaffected by ddl close. +var clusterState *atomicutil.Pointer[syncer.StateInfo] + +// MockStateSyncer is a mock state syncer, it is exported for testing. +type MockStateSyncer struct { + globalVerCh chan clientv3.WatchResponse + mockSession chan struct{} +} + +// Init implements StateSyncer.Init interface. +func (s *MockStateSyncer) Init(context.Context) error { + s.globalVerCh = make(chan clientv3.WatchResponse, 1) + s.mockSession = make(chan struct{}, 1) + state := syncer.NewStateInfo(syncer.StateNormalRunning) + if clusterState == nil { + clusterState = atomicutil.NewPointer(state) + } + return nil +} + +// UpdateGlobalState implements StateSyncer.UpdateGlobalState interface. +func (s *MockStateSyncer) UpdateGlobalState(_ context.Context, stateInfo *syncer.StateInfo) error { + failpoint.Inject("mockUpgradingState", func(val failpoint.Value) { + if val.(bool) { + clusterState.Store(stateInfo) + failpoint.Return(nil) + } + }) + s.globalVerCh <- clientv3.WatchResponse{} + clusterState.Store(stateInfo) + return nil +} + +// GetGlobalState implements StateSyncer.GetGlobalState interface. +func (*MockStateSyncer) GetGlobalState(context.Context) (*syncer.StateInfo, error) { + return clusterState.Load(), nil +} + +// IsUpgradingState implements StateSyncer.IsUpgradingState interface. +func (*MockStateSyncer) IsUpgradingState() bool { + return clusterState.Load().State == syncer.StateUpgrading +} + +// WatchChan implements StateSyncer.WatchChan interface. +func (s *MockStateSyncer) WatchChan() clientv3.WatchChan { + return s.globalVerCh +} + +// Rewatch implements StateSyncer.Rewatch interface. +func (*MockStateSyncer) Rewatch(context.Context) {} + +type mockDelRange struct { +} + +// newMockDelRangeManager creates a mock delRangeManager only used for test. +func newMockDelRangeManager() delRangeManager { + return &mockDelRange{} +} + +// addDelRangeJob implements delRangeManager interface. +func (*mockDelRange) addDelRangeJob(_ context.Context, _ *model.Job) error { + return nil +} + +// removeFromGCDeleteRange implements delRangeManager interface. +func (*mockDelRange) removeFromGCDeleteRange(_ context.Context, _ int64) error { + return nil +} + +// start implements delRangeManager interface. +func (*mockDelRange) start() {} + +// clear implements delRangeManager interface. +func (*mockDelRange) clear() {} + +// MockTableInfo mocks a table info by create table stmt ast and a specified table id. +func MockTableInfo(ctx sessionctx.Context, stmt *ast.CreateTableStmt, tableID int64) (*model.TableInfo, error) { + chs, coll := charset.GetDefaultCharsetAndCollate() + cols, newConstraints, err := buildColumnsAndConstraints(ctx, stmt.Cols, stmt.Constraints, chs, coll) + if err != nil { + return nil, errors.Trace(err) + } + tbl, err := BuildTableInfo(ctx, stmt.Table.Name, cols, newConstraints, "", "") + if err != nil { + return nil, errors.Trace(err) + } + tbl.ID = tableID + + if err = setTableAutoRandomBits(ctx, tbl, stmt.Cols); err != nil { + return nil, errors.Trace(err) + } + + // The specified charset will be handled in handleTableOptions + if err = handleTableOptions(stmt.Options, tbl); err != nil { + return nil, errors.Trace(err) + } + + return tbl, nil +} diff --git a/ddl/modify_column_test.go b/pkg/ddl/modify_column_test.go similarity index 93% rename from ddl/modify_column_test.go rename to pkg/ddl/modify_column_test.go index 47643e75e3dda..87e2e5f4cb12f 100644 --- a/ddl/modify_column_test.go +++ b/pkg/ddl/modify_column_test.go @@ -23,17 +23,17 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) @@ -114,7 +114,7 @@ func TestModifyColumnReorgInfo(t *testing.T) { elements = []*meta.Element{{ID: indexInfo.ID, TypeKey: meta.IndexElementKey}} } } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr", `return("cantDecodeRecordErr")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr", `return("cantDecodeRecordErr")`)) dom.DDL().SetHook(hook) err := tk.ExecToErr(sql) require.EqualError(t, err, "[ddl:8202]Cannot decode index value, because mock can't decode record error") @@ -144,12 +144,12 @@ func TestModifyColumnReorgInfo(t *testing.T) { {ID: 3, TypeKey: meta.IndexElementKey}, {ID: 4, TypeKey: meta.IndexElementKey}} checkReorgHandle(elements, expectedElements) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr")) tk.MustExec("admin check table t1") // Check whether the reorg information is cleaned up when executing "modify column" successfully. // Test encountering a "notOwnerErr" error which caused the processing backfill job to exit halfway. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr", `return("modifyColumnNotOwnerErr")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr", `return("modifyColumnNotOwnerErr")`)) tk.MustExec(sql) expectedElements = []*meta.Element{ {ID: 5, TypeKey: meta.ColumnElementKey}, @@ -157,11 +157,11 @@ func TestModifyColumnReorgInfo(t *testing.T) { {ID: 6, TypeKey: meta.IndexElementKey}} checkReorgHandle(elements, expectedElements) tk.MustExec("admin check table t1") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr")) // Test encountering a "notOwnerErr" error which caused the processing backfill job to exit halfway. // During the period, the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr", `return("addIdxNotOwnerErr")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr", `return("addIdxNotOwnerErr")`)) // TODO: Remove this check after "err" isn't nil in runReorgJobAndHandleErr. if variable.EnableDistTask.Load() { err = tk.ExecToErr("alter table t1 add index idx2(c1)") @@ -173,7 +173,7 @@ func TestModifyColumnReorgInfo(t *testing.T) { checkReorgHandle(elements, expectedElements) } tk.MustExec("admin check table t1") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockGetIndexRecordErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockGetIndexRecordErr")) } func TestModifyColumnNullToNotNullWithChangingVal2(t *testing.T) { @@ -181,9 +181,9 @@ func TestModifyColumnNullToNotNullWithChangingVal2(t *testing.T) { tk := testkit.NewTestKit(t, store) tk.MustExec("use test") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockInsertValueAfterCheckNull", `return("insert into test.tt values (NULL, NULL)")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockInsertValueAfterCheckNull", `return("insert into test.tt values (NULL, NULL)")`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockInsertValueAfterCheckNull")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockInsertValueAfterCheckNull")) }() tk.MustExec("drop table if exists tt;") @@ -561,9 +561,9 @@ func TestModifyColumnTypeWhenInterception(t *testing.T) { // Make the regions scale like: [1, 1024), [1024, 2048), [2048, 3072), [3072, 4096] tk.MustQuery("split table t between(0) and (4096) regions 4") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/MockReorgTimeoutInOneRegion", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/MockReorgTimeoutInOneRegion", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/MockReorgTimeoutInOneRegion")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/MockReorgTimeoutInOneRegion")) }() tk.MustExec("alter table t modify column b decimal(3,1)") tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 4096 warnings with this error code, first warning: Truncated incorrect DECIMAL value: '11.22'")) diff --git a/ddl/multi_schema_change.go b/pkg/ddl/multi_schema_change.go similarity index 98% rename from ddl/multi_schema_change.go rename to pkg/ddl/multi_schema_change.go index c99c93402ee53..1380aeb25241f 100644 --- a/ddl/multi_schema_change.go +++ b/pkg/ddl/multi_schema_change.go @@ -16,13 +16,13 @@ package ddl import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/dbterror" ) func (d *ddl) MultiSchemaChange(ctx sessionctx.Context, ti ast.Ident) error { diff --git a/ddl/multi_schema_change_test.go b/pkg/ddl/multi_schema_change_test.go similarity index 98% rename from ddl/multi_schema_change_test.go rename to pkg/ddl/multi_schema_change_test.go index 38e1ddac016c8..0e6f7bf3d9eab 100644 --- a/ddl/multi_schema_change_test.go +++ b/pkg/ddl/multi_schema_change_test.go @@ -19,15 +19,15 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -877,10 +877,10 @@ func newCancelJobHook(t *testing.T, store kv.Storage, dom *domain.Domain, } func putTheSameDDLJobTwice(t *testing.T, fn func()) { - err := failpoint.Enable("github.com/pingcap/tidb/ddl/mockParallelSameDDLJobTwice", `return(true)`) + err := failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockParallelSameDDLJobTwice", `return(true)`) require.NoError(t, err) fn() - err = failpoint.Disable("github.com/pingcap/tidb/ddl/mockParallelSameDDLJobTwice") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockParallelSameDDLJobTwice") require.NoError(t, err) } diff --git a/ddl/mv_index_test.go b/pkg/ddl/mv_index_test.go similarity index 94% rename from ddl/mv_index_test.go rename to pkg/ddl/mv_index_test.go index d603a55533ae5..1891d5bdbc62a 100644 --- a/ddl/mv_index_test.go +++ b/pkg/ddl/mv_index_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" ) func TestMultiValuedIndexOnlineDDL(t *testing.T) { diff --git a/pkg/ddl/options.go b/pkg/ddl/options.go new file mode 100644 index 0000000000000..df458747bb10c --- /dev/null +++ b/pkg/ddl/options.go @@ -0,0 +1,70 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "time" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + clientv3 "go.etcd.io/etcd/client/v3" +) + +// Option represents an option to initialize the DDL module +type Option func(*Options) + +// Options represents all the options of the DDL module needs +type Options struct { + EtcdCli *clientv3.Client + Store kv.Storage + InfoCache *infoschema.InfoCache + Hook Callback + Lease time.Duration +} + +// WithEtcdClient specifies the `clientv3.Client` of DDL used to request the etcd service +func WithEtcdClient(client *clientv3.Client) Option { + return func(options *Options) { + options.EtcdCli = client + } +} + +// WithStore specifies the `kv.Storage` of DDL used to request the KV service +func WithStore(store kv.Storage) Option { + return func(options *Options) { + options.Store = store + } +} + +// WithInfoCache specifies the `infoschema.InfoCache` +func WithInfoCache(ic *infoschema.InfoCache) Option { + return func(options *Options) { + options.InfoCache = ic + } +} + +// WithHook specifies the `Callback` of DDL used to notify the outer module when events are triggered +func WithHook(callback Callback) Option { + return func(options *Options) { + options.Hook = callback + } +} + +// WithLease specifies the schema lease duration +func WithLease(lease time.Duration) Option { + return func(options *Options) { + options.Lease = lease + } +} diff --git a/ddl/options_test.go b/pkg/ddl/options_test.go similarity index 92% rename from ddl/options_test.go rename to pkg/ddl/options_test.go index ccd8471c04250..98447e65f776f 100644 --- a/ddl/options_test.go +++ b/pkg/ddl/options_test.go @@ -18,9 +18,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/pkg/ddl/partition.go b/pkg/ddl/partition.go new file mode 100644 index 0000000000000..ac141e49054ba --- /dev/null +++ b/pkg/ddl/partition.go @@ -0,0 +1,4253 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/config" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mock" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/slice" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/tikv/client-go/v2/tikv" + kvutil "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +const ( + partitionMaxValue = "MAXVALUE" +) + +func checkAddPartition(t *meta.Meta, job *model.Job) (*model.TableInfo, *model.PartitionInfo, []model.PartitionDefinition, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, errors.Trace(err) + } + partInfo := &model.PartitionInfo{} + err = job.DecodeArgs(&partInfo) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, errors.Trace(err) + } + if len(tblInfo.Partition.AddingDefinitions) > 0 { + return tblInfo, partInfo, tblInfo.Partition.AddingDefinitions, nil + } + return tblInfo, partInfo, []model.PartitionDefinition{}, nil +} + +// TODO: Move this into reorganize partition! +func (w *worker) onAddTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + // Handle the rolling back job + if job.IsRollingback() { + ver, err := w.onDropTablePartition(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + return ver, nil + } + + // notice: addingDefinitions is empty when job is in state model.StateNone + tblInfo, partInfo, addingDefinitions, err := checkAddPartition(t, job) + if err != nil { + return ver, err + } + + // In order to skip maintaining the state check in partitionDefinition, TiDB use addingDefinition instead of state field. + // So here using `job.SchemaState` to judge what the stage of this job is. + switch job.SchemaState { + case model.StateNone: + // job.SchemaState == model.StateNone means the job is in the initial state of add partition. + // Here should use partInfo from job directly and do some check action. + err = checkAddPartitionTooManyPartitions(uint64(len(tblInfo.Partition.Definitions) + len(partInfo.Definitions))) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkAddPartitionValue(tblInfo, partInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkAddPartitionNameUnique(tblInfo, partInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + // move the adding definition into tableInfo. + updateAddingPartitionInfo(partInfo, tblInfo) + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + // modify placement settings + for _, def := range tblInfo.Partition.AddingDefinitions { + if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, def.PlacementPolicyRef); err != nil { + return ver, errors.Trace(err) + } + } + + if tblInfo.TiFlashReplica != nil { + // Must set placement rule, and make sure it succeeds. + if err := infosync.ConfigureTiFlashPDForPartitions(true, &tblInfo.Partition.AddingDefinitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); err != nil { + logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) + return ver, errors.Trace(err) + } + } + + bundles, err := alterTablePartitionBundles(t, tblInfo, tblInfo.Partition.AddingDefinitions) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + ids := getIDs([]*model.TableInfo{tblInfo}) + for _, p := range tblInfo.Partition.AddingDefinitions { + ids = append(ids, p.ID) + } + if _, err := alterTableLabelRule(job.SchemaName, tblInfo, ids); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + // none -> replica only + job.SchemaState = model.StateReplicaOnly + case model.StateReplicaOnly: + // replica only -> public + failpoint.Inject("sleepBeforeReplicaOnly", func(val failpoint.Value) { + sleepSecond := val.(int) + time.Sleep(time.Duration(sleepSecond) * time.Second) + }) + // Here need do some tiflash replica complement check. + // TODO: If a table is with no TiFlashReplica or it is not available, the replica-only state can be eliminated. + if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { + // For available state, the new added partition should wait it's replica to + // be finished. Otherwise the query to this partition will be blocked. + needRetry, err := checkPartitionReplica(tblInfo.TiFlashReplica.Count, addingDefinitions, d) + if err != nil { + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + if needRetry { + // The new added partition hasn't been replicated. + // Do nothing to the job this time, wait next worker round. + time.Sleep(tiflashCheckTiDBHTTPAPIHalfInterval) + // Set the error here which will lead this job exit when it's retry times beyond the limitation. + return ver, errors.Errorf("[ddl] add partition wait for tiflash replica to complete") + } + } + + // When TiFlash Replica is ready, we must move them into `AvailablePartitionIDs`. + if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { + for _, d := range partInfo.Definitions { + tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, d.ID) + err = infosync.UpdateTiFlashProgressCache(d.ID, 1) + if err != nil { + // just print log, progress will be updated in `refreshTiFlashTicker` + logutil.BgLogger().Error("update tiflash sync progress cache failed", + zap.Error(err), + zap.Int64("tableID", tblInfo.ID), + zap.Int64("partitionID", d.ID), + ) + } + } + } + // For normal and replica finished table, move the `addingDefinitions` into `Definitions`. + updatePartitionInfo(tblInfo) + + preSplitAndScatter(w.sess.Context, d.store, tblInfo, addingDefinitions) + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionAddTablePartition, TableInfo: tblInfo, PartInfo: partInfo}) + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) + } + + return ver, errors.Trace(err) +} + +// alterTableLabelRule updates Label Rules if they exists +// returns true if changed. +func alterTableLabelRule(schemaName string, meta *model.TableInfo, ids []int64) (bool, error) { + tableRuleID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, schemaName, meta.Name.L) + oldRule, err := infosync.GetLabelRules(context.TODO(), []string{tableRuleID}) + if err != nil { + return false, errors.Trace(err) + } + if len(oldRule) == 0 { + return false, nil + } + + r, ok := oldRule[tableRuleID] + if ok { + rule := r.Reset(schemaName, meta.Name.L, "", ids...) + err = infosync.PutLabelRule(context.TODO(), rule) + if err != nil { + return false, errors.Wrapf(err, "failed to notify PD label rule") + } + return true, nil + } + return false, nil +} + +func alterTablePartitionBundles(t *meta.Meta, tblInfo *model.TableInfo, addingDefinitions []model.PartitionDefinition) ([]*placement.Bundle, error) { + var bundles []*placement.Bundle + + // tblInfo do not include added partitions, so we should add them first + tblInfo = tblInfo.Clone() + p := *tblInfo.Partition + p.Definitions = append([]model.PartitionDefinition{}, p.Definitions...) + p.Definitions = append(tblInfo.Partition.Definitions, addingDefinitions...) + tblInfo.Partition = &p + + // bundle for table should be recomputed because it includes some default configs for partitions + tblBundle, err := placement.NewTableBundle(t, tblInfo) + if err != nil { + return nil, errors.Trace(err) + } + + if tblBundle != nil { + bundles = append(bundles, tblBundle) + } + + partitionBundles, err := placement.NewPartitionListBundles(t, addingDefinitions) + if err != nil { + return nil, errors.Trace(err) + } + + bundles = append(bundles, partitionBundles...) + return bundles, nil +} + +// When drop/truncate a partition, we should still keep the dropped partition's placement settings to avoid unnecessary region schedules. +// When a partition is not configured with a placement policy directly, its rule is in the table's placement group which will be deleted after +// partition truncated/dropped. So it is necessary to create a standalone placement group with partition id after it. +func droppedPartitionBundles(t *meta.Meta, tblInfo *model.TableInfo, dropPartitions []model.PartitionDefinition) ([]*placement.Bundle, error) { + partitions := make([]model.PartitionDefinition, 0, len(dropPartitions)) + for _, def := range dropPartitions { + def = def.Clone() + if def.PlacementPolicyRef == nil { + def.PlacementPolicyRef = tblInfo.PlacementPolicyRef + } + + if def.PlacementPolicyRef != nil { + partitions = append(partitions, def) + } + } + + return placement.NewPartitionListBundles(t, partitions) +} + +// updatePartitionInfo merge `addingDefinitions` into `Definitions` in the tableInfo. +func updatePartitionInfo(tblInfo *model.TableInfo) { + parInfo := &model.PartitionInfo{} + oldDefs, newDefs := tblInfo.Partition.Definitions, tblInfo.Partition.AddingDefinitions + parInfo.Definitions = make([]model.PartitionDefinition, 0, len(newDefs)+len(oldDefs)) + parInfo.Definitions = append(parInfo.Definitions, oldDefs...) + parInfo.Definitions = append(parInfo.Definitions, newDefs...) + tblInfo.Partition.Definitions = parInfo.Definitions + tblInfo.Partition.AddingDefinitions = nil +} + +// updateAddingPartitionInfo write adding partitions into `addingDefinitions` field in the tableInfo. +func updateAddingPartitionInfo(partitionInfo *model.PartitionInfo, tblInfo *model.TableInfo) { + newDefs := partitionInfo.Definitions + tblInfo.Partition.AddingDefinitions = make([]model.PartitionDefinition, 0, len(newDefs)) + tblInfo.Partition.AddingDefinitions = append(tblInfo.Partition.AddingDefinitions, newDefs...) +} + +// rollbackAddingPartitionInfo remove the `addingDefinitions` in the tableInfo. +func rollbackAddingPartitionInfo(tblInfo *model.TableInfo) ([]int64, []string, []*placement.Bundle) { + physicalTableIDs := make([]int64, 0, len(tblInfo.Partition.AddingDefinitions)) + partNames := make([]string, 0, len(tblInfo.Partition.AddingDefinitions)) + rollbackBundles := make([]*placement.Bundle, 0, len(tblInfo.Partition.AddingDefinitions)) + for _, one := range tblInfo.Partition.AddingDefinitions { + physicalTableIDs = append(physicalTableIDs, one.ID) + partNames = append(partNames, one.Name.L) + if one.PlacementPolicyRef != nil { + rollbackBundles = append(rollbackBundles, placement.NewBundle(one.ID)) + } + } + tblInfo.Partition.AddingDefinitions = nil + return physicalTableIDs, partNames, rollbackBundles +} + +// Check if current table already contains DEFAULT list partition +func checkAddListPartitions(tblInfo *model.TableInfo) error { + for i := range tblInfo.Partition.Definitions { + for j := range tblInfo.Partition.Definitions[i].InValues { + for _, val := range tblInfo.Partition.Definitions[i].InValues[j] { + if val == "DEFAULT" { // should already be normalized + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("ADD List partition, already contains DEFAULT partition. Please use REORGANIZE PARTITION instead") + } + } + } + } + return nil +} + +// checkAddPartitionValue check add Partition Values, +// For Range: values less than value must be strictly increasing for each partition. +// For List: if a Default partition exists, +// +// no ADD partition can be allowed +// (needs reorganize partition instead). +func checkAddPartitionValue(meta *model.TableInfo, part *model.PartitionInfo) error { + switch meta.Partition.Type { + case model.PartitionTypeRange: + if len(meta.Partition.Columns) == 0 { + newDefs, oldDefs := part.Definitions, meta.Partition.Definitions + rangeValue := oldDefs[len(oldDefs)-1].LessThan[0] + if strings.EqualFold(rangeValue, "MAXVALUE") { + return errors.Trace(dbterror.ErrPartitionMaxvalue) + } + + currentRangeValue, err := strconv.Atoi(rangeValue) + if err != nil { + return errors.Trace(err) + } + + for i := 0; i < len(newDefs); i++ { + ifMaxvalue := strings.EqualFold(newDefs[i].LessThan[0], "MAXVALUE") + if ifMaxvalue && i == len(newDefs)-1 { + return nil + } else if ifMaxvalue && i != len(newDefs)-1 { + return errors.Trace(dbterror.ErrPartitionMaxvalue) + } + + nextRangeValue, err := strconv.Atoi(newDefs[i].LessThan[0]) + if err != nil { + return errors.Trace(err) + } + if nextRangeValue <= currentRangeValue { + return errors.Trace(dbterror.ErrRangeNotIncreasing) + } + currentRangeValue = nextRangeValue + } + } + case model.PartitionTypeList: + err := checkAddListPartitions(meta) + if err != nil { + return err + } + } + return nil +} + +func checkPartitionReplica(replicaCount uint64, addingDefinitions []model.PartitionDefinition, d *ddlCtx) (needWait bool, err error) { + failpoint.Inject("mockWaitTiFlashReplica", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(true, nil) + } + }) + failpoint.Inject("mockWaitTiFlashReplicaOK", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(false, nil) + } + }) + + ctx := context.Background() + pdCli := d.store.(tikv.Storage).GetRegionCache().PDClient() + stores, err := pdCli.GetAllStores(ctx) + if err != nil { + return needWait, errors.Trace(err) + } + // Check whether stores have `count` tiflash engines. + tiFlashStoreCount := uint64(0) + for _, store := range stores { + if storeHasEngineTiFlashLabel(store) { + tiFlashStoreCount++ + } + } + if replicaCount > tiFlashStoreCount { + return false, errors.Errorf("[ddl] the tiflash replica count: %d should be less than the total tiflash server count: %d", replicaCount, tiFlashStoreCount) + } + for _, pd := range addingDefinitions { + startKey, endKey := tablecodec.GetTableHandleKeyRange(pd.ID) + regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) + if err != nil { + return needWait, errors.Trace(err) + } + // For every region in the partition, if it has some corresponding peers and + // no pending peers, that means the replication has completed. + for _, region := range regions { + regionState, err := pdCli.GetRegionByID(ctx, region.Meta.Id) + if err != nil { + return needWait, errors.Trace(err) + } + tiflashPeerAtLeastOne := checkTiFlashPeerStoreAtLeastOne(stores, regionState.Meta.Peers) + failpoint.Inject("ForceTiflashNotAvailable", func(v failpoint.Value) { + tiflashPeerAtLeastOne = v.(bool) + }) + // It's unnecessary to wait all tiflash peer to be replicated. + // Here only make sure that tiflash peer count > 0 (at least one). + if tiflashPeerAtLeastOne { + continue + } + needWait = true + logutil.BgLogger().Info("partition replicas check failed in replica-only DDL state", zap.String("category", "ddl"), zap.Int64("pID", pd.ID), zap.Uint64("wait region ID", region.Meta.Id), zap.Bool("tiflash peer at least one", tiflashPeerAtLeastOne), zap.Time("check time", time.Now())) + return needWait, nil + } + } + logutil.BgLogger().Info("partition replicas check ok in replica-only DDL state", zap.String("category", "ddl")) + return needWait, nil +} + +func checkTiFlashPeerStoreAtLeastOne(stores []*metapb.Store, peers []*metapb.Peer) bool { + for _, peer := range peers { + for _, store := range stores { + if peer.StoreId == store.Id && storeHasEngineTiFlashLabel(store) { + return true + } + } + } + return false +} + +func storeHasEngineTiFlashLabel(store *metapb.Store) bool { + for _, label := range store.Labels { + if label.Key == placement.EngineLabelKey && label.Value == placement.EngineLabelTiFlash { + return true + } + } + return false +} + +func checkListPartitions(defs []*ast.PartitionDefinition) error { + for _, def := range defs { + _, ok := def.Clause.(*ast.PartitionDefinitionClauseIn) + if !ok { + switch def.Clause.(type) { + case *ast.PartitionDefinitionClauseLessThan: + return ast.ErrPartitionWrongValues.GenWithStackByArgs("RANGE", "LESS THAN") + case *ast.PartitionDefinitionClauseNone: + return ast.ErrPartitionRequiresValues.GenWithStackByArgs("LIST", "IN") + default: + return dbterror.ErrUnsupportedCreatePartition.GenWithStack("Only VALUES IN () is supported for LIST partitioning") + } + } + } + return nil +} + +// buildTablePartitionInfo builds partition info and checks for some errors. +func buildTablePartitionInfo(ctx sessionctx.Context, s *ast.PartitionOptions, tbInfo *model.TableInfo) error { + if s == nil { + return nil + } + + if strings.EqualFold(ctx.GetSessionVars().EnableTablePartition, "OFF") { + ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrTablePartitionDisabled) + return nil + } + + var enable bool + switch s.Tp { + case model.PartitionTypeRange: + enable = true + case model.PartitionTypeList: + // Partition by list is enabled only when tidb_enable_list_partition is 'ON'. + enable = ctx.GetSessionVars().EnableListTablePartition + if enable { + err := checkListPartitions(s.Definitions) + if err != nil { + return err + } + } + case model.PartitionTypeHash, model.PartitionTypeKey: + // Partition by hash and key is enabled by default. + if s.Sub != nil { + // Subpartitioning only allowed with Range or List + return ast.ErrSubpartition + } + // Note that linear hash is simply ignored, and creates non-linear hash/key. + if s.Linear { + ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("LINEAR %s is not supported, using non-linear %s instead", s.Tp.String(), s.Tp.String()))) + } + if s.Tp == model.PartitionTypeHash || len(s.ColumnNames) != 0 { + enable = true + } + } + + if !enable { + ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("Unsupported partition type %v, treat as normal table", s.Tp))) + return nil + } + if s.Sub != nil { + ctx.GetSessionVars().StmtCtx.AppendWarning(dbterror.ErrUnsupportedCreatePartition.GenWithStack(fmt.Sprintf("Unsupported subpartitioning, only using %v partitioning", s.Tp))) + } + + pi := &model.PartitionInfo{ + Type: s.Tp, + Enable: enable, + Num: s.Num, + } + tbInfo.Partition = pi + if s.Expr != nil { + if err := checkPartitionFuncValid(ctx, tbInfo, s.Expr); err != nil { + return errors.Trace(err) + } + buf := new(bytes.Buffer) + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags|format.RestoreBracketAroundBinaryOperation, buf) + if err := s.Expr.Restore(restoreCtx); err != nil { + return err + } + pi.Expr = buf.String() + } else if s.ColumnNames != nil { + pi.Columns = make([]model.CIStr, 0, len(s.ColumnNames)) + for _, cn := range s.ColumnNames { + pi.Columns = append(pi.Columns, cn.Name) + } + if err := checkColumnsPartitionType(tbInfo); err != nil { + return err + } + } + + err := generatePartitionDefinitionsFromInterval(ctx, s, tbInfo) + if err != nil { + return errors.Trace(err) + } + + defs, err := buildPartitionDefinitionsInfo(ctx, s.Definitions, tbInfo, s.Num) + if err != nil { + return errors.Trace(err) + } + + tbInfo.Partition.Definitions = defs + + if s.Interval != nil { + // Syntactic sugar for INTERVAL partitioning + // Generate the resulting CREATE TABLE as the query string + query, ok := ctx.Value(sessionctx.QueryString).(string) + if ok { + sqlMode := ctx.GetSessionVars().SQLMode + var buf bytes.Buffer + AppendPartitionDefs(tbInfo.Partition, &buf, sqlMode) + + syntacticSugar := s.Interval.OriginalText() + syntacticStart := s.Interval.OriginTextPosition() + newQuery := query[:syntacticStart] + "(" + buf.String() + ")" + query[syntacticStart+len(syntacticSugar):] + ctx.SetValue(sessionctx.QueryString, newQuery) + } + } + + partCols, err := getPartitionColSlices(ctx, tbInfo, s) + if err != nil { + return errors.Trace(err) + } + + for _, index := range tbInfo.Indices { + if index.Unique && !checkUniqueKeyIncludePartKey(partCols, index.Columns) { + index.Global = config.GetGlobalConfig().EnableGlobalIndex + } + } + return nil +} + +func getPartitionColSlices(sctx sessionctx.Context, tblInfo *model.TableInfo, s *ast.PartitionOptions) (partCols stringSlice, err error) { + if s.Expr != nil { + extractCols := newPartitionExprChecker(sctx, tblInfo) + s.Expr.Accept(extractCols) + partColumns, err := extractCols.columns, extractCols.err + if err != nil { + return nil, err + } + partCols = columnInfoSlice(partColumns) + } else if len(s.ColumnNames) > 0 { + partCols = columnNameSlice(s.ColumnNames) + } else { + return nil, errors.Errorf("Table partition metadata not correct, neither partition expression or list of partition columns") + } + return partCols, nil +} + +// getPartitionIntervalFromTable checks if a partitioned table matches a generated INTERVAL partitioned scheme +// will return nil if error occurs, i.e. not an INTERVAL partitioned table +func getPartitionIntervalFromTable(ctx sessionctx.Context, tbInfo *model.TableInfo) *ast.PartitionInterval { + if tbInfo.Partition == nil || + tbInfo.Partition.Type != model.PartitionTypeRange { + return nil + } + if len(tbInfo.Partition.Columns) > 1 { + // Multi-column RANGE COLUMNS is not supported with INTERVAL + return nil + } + if len(tbInfo.Partition.Definitions) < 2 { + // Must have at least two partitions to calculate an INTERVAL + return nil + } + + var ( + interval ast.PartitionInterval + startIdx = 0 + endIdx = len(tbInfo.Partition.Definitions) - 1 + isIntType = true + minVal = "0" + ) + if len(tbInfo.Partition.Columns) > 0 { + partCol := findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) + if partCol.FieldType.EvalType() == types.ETInt { + min := getLowerBoundInt(partCol) + minVal = strconv.FormatInt(min, 10) + } else if partCol.FieldType.EvalType() == types.ETDatetime { + isIntType = false + minVal = "0000-01-01" + } else { + // Only INT and Datetime columns are supported for INTERVAL partitioning + return nil + } + } else { + if !isPartExprUnsigned(tbInfo) { + minVal = "-9223372036854775808" + } + } + + // Check if possible null partition + firstPartLessThan := driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[0].LessThan[0]) + if strings.EqualFold(firstPartLessThan, minVal) { + interval.NullPart = true + startIdx++ + firstPartLessThan = driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[startIdx].LessThan[0]) + } + // flag if MAXVALUE partition + lastPartLessThan := driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[endIdx].LessThan[0]) + if strings.EqualFold(lastPartLessThan, partitionMaxValue) { + interval.MaxValPart = true + endIdx-- + lastPartLessThan = driver.UnwrapFromSingleQuotes(tbInfo.Partition.Definitions[endIdx].LessThan[0]) + } + // Guess the interval + if startIdx >= endIdx { + // Must have at least two partitions to calculate an INTERVAL + return nil + } + var firstExpr, lastExpr ast.ExprNode + if isIntType { + exprStr := fmt.Sprintf("((%s) - (%s)) DIV %d", lastPartLessThan, firstPartLessThan, endIdx-startIdx) + exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprStr, nil, nil) + if err != nil { + return nil + } + val, isNull, err := exprs[0].EvalInt(ctx, chunk.Row{}) + if isNull || err != nil || val < 1 { + // If NULL, error or interval < 1 then cannot be an INTERVAL partitioned table + return nil + } + interval.IntervalExpr.Expr = ast.NewValueExpr(val, "", "") + interval.IntervalExpr.TimeUnit = ast.TimeUnitInvalid + firstExpr, err = astIntValueExprFromStr(firstPartLessThan, minVal == "0") + if err != nil { + return nil + } + interval.FirstRangeEnd = &firstExpr + lastExpr, err = astIntValueExprFromStr(lastPartLessThan, minVal == "0") + if err != nil { + return nil + } + interval.LastRangeEnd = &lastExpr + } else { // types.ETDatetime + exprStr := fmt.Sprintf("TIMESTAMPDIFF(SECOND, '%s', '%s')", firstPartLessThan, lastPartLessThan) + exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprStr, nil, nil) + if err != nil { + return nil + } + val, isNull, err := exprs[0].EvalInt(ctx, chunk.Row{}) + if isNull || err != nil || val < 1 { + // If NULL, error or interval < 1 then cannot be an INTERVAL partitioned table + return nil + } + + // This will not find all matches > 28 days, since INTERVAL 1 MONTH can generate + // 2022-01-31, 2022-02-28, 2022-03-31 etc. so we just assume that if there is a + // diff >= 28 days, we will try with Month and not retry with something else... + i := val / int64(endIdx-startIdx) + if i < (28 * 24 * 60 * 60) { + // Since it is not stored or displayed, non need to try Minute..Week! + interval.IntervalExpr.Expr = ast.NewValueExpr(i, "", "") + interval.IntervalExpr.TimeUnit = ast.TimeUnitSecond + } else { + // Since it is not stored or displayed, non need to try to match Quarter or Year! + if (endIdx - startIdx) <= 3 { + // in case February is in the range + i = i / (28 * 24 * 60 * 60) + } else { + // This should be good for intervals up to 5 years + i = i / (30 * 24 * 60 * 60) + } + interval.IntervalExpr.Expr = ast.NewValueExpr(i, "", "") + interval.IntervalExpr.TimeUnit = ast.TimeUnitMonth + } + + firstExpr = ast.NewValueExpr(firstPartLessThan, "", "") + lastExpr = ast.NewValueExpr(lastPartLessThan, "", "") + interval.FirstRangeEnd = &firstExpr + interval.LastRangeEnd = &lastExpr + } + + partitionMethod := ast.PartitionMethod{ + Tp: model.PartitionTypeRange, + Interval: &interval, + } + partOption := &ast.PartitionOptions{PartitionMethod: partitionMethod} + // Generate the definitions from interval, first and last + err := generatePartitionDefinitionsFromInterval(ctx, partOption, tbInfo) + if err != nil { + return nil + } + + return &interval +} + +// comparePartitionAstAndModel compares a generated *ast.PartitionOptions and a *model.PartitionInfo +func comparePartitionAstAndModel(ctx sessionctx.Context, pAst *ast.PartitionOptions, pModel *model.PartitionInfo) error { + a := pAst.Definitions + m := pModel.Definitions + if len(pAst.Definitions) != len(pModel.Definitions) { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: number of partitions generated != partition defined (%d != %d)", len(a), len(m)) + } + for i := range pAst.Definitions { + // Allow options to differ! (like Placement Rules) + // Allow names to differ! + + // Check MAXVALUE + maxVD := false + if strings.EqualFold(m[i].LessThan[0], partitionMaxValue) { + maxVD = true + } + generatedExpr := a[i].Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs[0] + _, maxVG := generatedExpr.(*ast.MaxValueExpr) + if maxVG || maxVD { + if maxVG && maxVD { + continue + } + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("INTERVAL partitioning: MAXVALUE clause defined for partition %s differs between generated and defined", m[i].Name.O)) + } + + lessThan := m[i].LessThan[0] + if len(lessThan) > 1 && lessThan[:1] == "'" && lessThan[len(lessThan)-1:] == "'" { + lessThan = driver.UnwrapFromSingleQuotes(lessThan) + } + cmpExpr := &ast.BinaryOperationExpr{ + Op: opcode.EQ, + L: ast.NewValueExpr(lessThan, "", ""), + R: generatedExpr, + } + cmp, err := expression.EvalAstExpr(ctx, cmpExpr) + if err != nil { + return err + } + if cmp.GetInt64() != 1 { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("INTERVAL partitioning: LESS THAN for partition %s differs between generated and defined", m[i].Name.O)) + } + } + return nil +} + +// comparePartitionDefinitions check if generated definitions are the same as the given ones +// Allow names to differ +// returns error in case of error or non-accepted difference +func comparePartitionDefinitions(ctx sessionctx.Context, a, b []*ast.PartitionDefinition) error { + if len(a) != len(b) { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("number of partitions generated != partition defined (%d != %d)", len(a), len(b)) + } + for i := range a { + if len(b[i].Sub) > 0 { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does have unsupported subpartitions", b[i].Name.O)) + } + // TODO: We could extend the syntax to allow for table options too, like: + // CREATE TABLE t ... INTERVAL ... LAST PARTITION LESS THAN ('2015-01-01') PLACEMENT POLICY = 'cheapStorage' + // ALTER TABLE t LAST PARTITION LESS THAN ('2022-01-01') PLACEMENT POLICY 'defaultStorage' + // ALTER TABLE t LAST PARTITION LESS THAN ('2023-01-01') PLACEMENT POLICY 'fastStorage' + if len(b[i].Options) > 0 { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does have unsupported options", b[i].Name.O)) + } + lessThan, ok := b[i].Clause.(*ast.PartitionDefinitionClauseLessThan) + if !ok { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s does not have the right type for LESS THAN", b[i].Name.O)) + } + definedExpr := lessThan.Exprs[0] + generatedExpr := a[i].Clause.(*ast.PartitionDefinitionClauseLessThan).Exprs[0] + _, maxVD := definedExpr.(*ast.MaxValueExpr) + _, maxVG := generatedExpr.(*ast.MaxValueExpr) + if maxVG || maxVD { + if maxVG && maxVD { + continue + } + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s differs between generated and defined for MAXVALUE", b[i].Name.O)) + } + cmpExpr := &ast.BinaryOperationExpr{ + Op: opcode.EQ, + L: definedExpr, + R: generatedExpr, + } + cmp, err := expression.EvalAstExpr(ctx, cmpExpr) + if err != nil { + return err + } + if cmp.GetInt64() != 1 { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(fmt.Sprintf("partition %s differs between generated and defined for expression", b[i].Name.O)) + } + } + return nil +} + +func getLowerBoundInt(partCols ...*model.ColumnInfo) int64 { + ret := int64(0) + for _, col := range partCols { + if mysql.HasUnsignedFlag(col.FieldType.GetFlag()) { + return 0 + } + ret = mathutil.Min(ret, types.IntergerSignedLowerBound(col.GetType())) + } + return ret +} + +// generatePartitionDefinitionsFromInterval generates partition Definitions according to INTERVAL options on partOptions +func generatePartitionDefinitionsFromInterval(ctx sessionctx.Context, partOptions *ast.PartitionOptions, tbInfo *model.TableInfo) error { + if partOptions.Interval == nil { + return nil + } + if tbInfo.Partition.Type != model.PartitionTypeRange { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only allowed on RANGE partitioning") + } + if len(partOptions.ColumnNames) > 1 || len(tbInfo.Partition.Columns) > 1 { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, does not allow RANGE COLUMNS with more than one column") + } + var partCol *model.ColumnInfo + if len(tbInfo.Partition.Columns) > 0 { + partCol = findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) + if partCol == nil { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, could not find any RANGE COLUMNS") + } + // Only support Datetime, date and INT column types for RANGE INTERVAL! + switch partCol.FieldType.EvalType() { + case types.ETInt, types.ETDatetime: + default: + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only supports Date, Datetime and INT types") + } + } + // Allow given partition definitions, but check it later! + definedPartDefs := partOptions.Definitions + partOptions.Definitions = make([]*ast.PartitionDefinition, 0, 1) + if partOptions.Interval.FirstRangeEnd == nil || partOptions.Interval.LastRangeEnd == nil { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, currently requires FIRST and LAST partitions to be defined") + } + switch partOptions.Interval.IntervalExpr.TimeUnit { + case ast.TimeUnitInvalid, ast.TimeUnitYear, ast.TimeUnitQuarter, ast.TimeUnitMonth, ast.TimeUnitWeek, ast.TimeUnitDay, ast.TimeUnitHour, ast.TimeUnitDayMinute, ast.TimeUnitSecond: + default: + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning, only supports YEAR, QUARTER, MONTH, WEEK, DAY, HOUR, MINUTE and SECOND as time unit") + } + first := ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{*partOptions.Interval.FirstRangeEnd}, + } + last := ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{*partOptions.Interval.LastRangeEnd}, + } + if len(tbInfo.Partition.Columns) > 0 { + colTypes := collectColumnsType(tbInfo) + if len(colTypes) != len(tbInfo.Partition.Columns) { + return dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") + } + if _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, first.Exprs); err != nil { + return err + } + if _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, last.Exprs); err != nil { + return err + } + } else { + if err := checkPartitionValuesIsInt(ctx, "FIRST PARTITION", first.Exprs, tbInfo); err != nil { + return err + } + if err := checkPartitionValuesIsInt(ctx, "LAST PARTITION", last.Exprs, tbInfo); err != nil { + return err + } + } + if partOptions.Interval.NullPart { + var partExpr ast.ExprNode + if len(tbInfo.Partition.Columns) == 1 && partOptions.Interval.IntervalExpr.TimeUnit != ast.TimeUnitInvalid { + // Notice compatibility with MySQL, keyword here is 'supported range' but MySQL seems to work from 0000-01-01 too + // https://dev.mysql.com/doc/refman/8.0/en/datetime.html says range 1000-01-01 - 9999-12-31 + // https://docs.pingcap.com/tidb/dev/data-type-date-and-time says The supported range is '0000-01-01' to '9999-12-31' + // set LESS THAN to ZeroTime + partExpr = ast.NewValueExpr("0000-01-01", "", "") + } else { + var min int64 + if partCol != nil { + min = getLowerBoundInt(partCol) + } else { + if !isPartExprUnsigned(tbInfo) { + min = math.MinInt64 + } + } + partExpr = ast.NewValueExpr(min, "", "") + } + partOptions.Definitions = append(partOptions.Definitions, &ast.PartitionDefinition{ + Name: model.NewCIStr("P_NULL"), + Clause: &ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{partExpr}, + }, + }) + } + + err := GeneratePartDefsFromInterval(ctx, ast.AlterTablePartition, tbInfo, partOptions) + if err != nil { + return err + } + + if partOptions.Interval.MaxValPart { + partOptions.Definitions = append(partOptions.Definitions, &ast.PartitionDefinition{ + Name: model.NewCIStr("P_MAXVALUE"), + Clause: &ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{&ast.MaxValueExpr{}}, + }, + }) + } + + if len(definedPartDefs) > 0 { + err := comparePartitionDefinitions(ctx, partOptions.Definitions, definedPartDefs) + if err != nil { + return err + } + // Seems valid, so keep the defined so that the user defined names are kept etc. + partOptions.Definitions = definedPartDefs + } else if len(tbInfo.Partition.Definitions) > 0 { + err := comparePartitionAstAndModel(ctx, partOptions, tbInfo.Partition) + if err != nil { + return err + } + } + + return nil +} + +func astIntValueExprFromStr(s string, unsigned bool) (ast.ExprNode, error) { + if unsigned { + u, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return nil, err + } + return ast.NewValueExpr(u, "", ""), nil + } + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return nil, err + } + return ast.NewValueExpr(i, "", ""), nil +} + +// GeneratePartDefsFromInterval generates range partitions from INTERVAL partitioning. +// Handles +// - CREATE TABLE: all partitions are generated +// - ALTER TABLE FIRST PARTITION (expr): Drops all partitions before the partition matching the expr (i.e. sets that partition as the new first partition) +// i.e. will return the partitions from old FIRST partition to (and including) new FIRST partition +// - ALTER TABLE LAST PARTITION (expr): Creates new partitions from (excluding) old LAST partition to (including) new LAST partition +// +// partition definitions will be set on partitionOptions +func GeneratePartDefsFromInterval(ctx sessionctx.Context, tp ast.AlterTableType, tbInfo *model.TableInfo, partitionOptions *ast.PartitionOptions) error { + if partitionOptions == nil { + return nil + } + var sb strings.Builder + err := partitionOptions.Interval.IntervalExpr.Expr.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) + if err != nil { + return err + } + intervalString := driver.UnwrapFromSingleQuotes(sb.String()) + if len(intervalString) < 1 || intervalString[:1] < "1" || intervalString[:1] > "9" { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL, should be a positive number") + } + var currVal types.Datum + var startExpr, lastExpr, currExpr ast.ExprNode + var timeUnit ast.TimeUnitType + var partCol *model.ColumnInfo + if len(tbInfo.Partition.Columns) == 1 { + partCol = findColumnByName(tbInfo.Partition.Columns[0].L, tbInfo) + if partCol == nil { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL COLUMNS partitioning: could not find partitioning column") + } + } + timeUnit = partitionOptions.Interval.IntervalExpr.TimeUnit + switch tp { + case ast.AlterTablePartition: + // CREATE TABLE + startExpr = *partitionOptions.Interval.FirstRangeEnd + lastExpr = *partitionOptions.Interval.LastRangeEnd + case ast.AlterTableDropFirstPartition: + startExpr = *partitionOptions.Interval.FirstRangeEnd + lastExpr = partitionOptions.Expr + case ast.AlterTableAddLastPartition: + startExpr = *partitionOptions.Interval.LastRangeEnd + lastExpr = partitionOptions.Expr + default: + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: Internal error during generating altered INTERVAL partitions, no known alter type") + } + lastVal, err := expression.EvalAstExpr(ctx, lastExpr) + if err != nil { + return err + } + var partDefs []*ast.PartitionDefinition + if len(partitionOptions.Definitions) != 0 { + partDefs = partitionOptions.Definitions + } else { + partDefs = make([]*ast.PartitionDefinition, 0, 1) + } + for i := 0; i < mysql.PartitionCountLimit; i++ { + if i == 0 { + currExpr = startExpr + // TODO: adjust the startExpr and have an offset for interval to handle + // Month/Quarters with start partition on day 28/29/30 + if tp == ast.AlterTableAddLastPartition { + // ALTER TABLE LAST PARTITION ... + // Current LAST PARTITION/start already exists, skip to next partition + continue + } + } else { + currExpr = &ast.BinaryOperationExpr{ + Op: opcode.Mul, + L: ast.NewValueExpr(i, "", ""), + R: partitionOptions.Interval.IntervalExpr.Expr, + } + if timeUnit == ast.TimeUnitInvalid { + currExpr = &ast.BinaryOperationExpr{ + Op: opcode.Plus, + L: startExpr, + R: currExpr, + } + } else { + currExpr = &ast.FuncCallExpr{ + FnName: model.NewCIStr("DATE_ADD"), + Args: []ast.ExprNode{ + startExpr, + currExpr, + &ast.TimeUnitExpr{Unit: timeUnit}, + }, + } + } + } + currVal, err = expression.EvalAstExpr(ctx, currExpr) + if err != nil { + return err + } + cmp, err := currVal.Compare(ctx.GetSessionVars().StmtCtx, &lastVal, collate.GetBinaryCollator()) + if err != nil { + return err + } + if cmp > 0 { + lastStr, err := lastVal.ToString() + if err != nil { + return err + } + sb.Reset() + err = startExpr.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) + if err != nil { + return err + } + startStr := sb.String() + errStr := fmt.Sprintf("INTERVAL: expr (%s) not matching FIRST + n INTERVALs (%s + n * %s", + lastStr, startStr, intervalString) + if timeUnit != ast.TimeUnitInvalid { + errStr = errStr + " " + timeUnit.String() + } + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs(errStr + ")") + } + valStr, err := currVal.ToString() + if err != nil { + return err + } + if len(valStr) == 0 || valStr[0:1] == "'" { + return dbterror.ErrGeneralUnsupportedDDL.GenWithStackByArgs("INTERVAL partitioning: Error when generating partition values") + } + partName := "P_LT_" + valStr + if timeUnit != ast.TimeUnitInvalid { + currExpr = ast.NewValueExpr(valStr, "", "") + } else { + if valStr[:1] == "-" { + currExpr = ast.NewValueExpr(currVal.GetInt64(), "", "") + } else { + currExpr = ast.NewValueExpr(currVal.GetUint64(), "", "") + } + } + partDefs = append(partDefs, &ast.PartitionDefinition{ + Name: model.NewCIStr(partName), + Clause: &ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{currExpr}, + }, + }) + if cmp == 0 { + // Last partition! + break + } + } + if len(tbInfo.Partition.Definitions)+len(partDefs) > mysql.PartitionCountLimit { + return errors.Trace(dbterror.ErrTooManyPartitions) + } + partitionOptions.Definitions = partDefs + return nil +} + +// buildPartitionDefinitionsInfo build partition definitions info without assign partition id. tbInfo will be constant +func buildPartitionDefinitionsInfo(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo, numParts uint64) (partitions []model.PartitionDefinition, err error) { + switch tbInfo.Partition.Type { + case model.PartitionTypeNone: + if len(defs) != 1 { + return nil, dbterror.ErrUnsupportedPartitionType + } + partitions = []model.PartitionDefinition{{Name: defs[0].Name}} + if comment, set := defs[0].Comment(); set { + partitions[0].Comment = comment + } + case model.PartitionTypeRange: + partitions, err = buildRangePartitionDefinitions(ctx, defs, tbInfo) + case model.PartitionTypeHash, model.PartitionTypeKey: + partitions, err = buildHashPartitionDefinitions(ctx, defs, tbInfo, numParts) + case model.PartitionTypeList: + partitions, err = buildListPartitionDefinitions(ctx, defs, tbInfo) + default: + err = dbterror.ErrUnsupportedPartitionType + } + + if err != nil { + return nil, err + } + + return partitions, nil +} + +func setPartitionPlacementFromOptions(partition *model.PartitionDefinition, options []*ast.TableOption) error { + // the partition inheritance of placement rules don't have to copy the placement elements to themselves. + // For example: + // t placement policy x (p1 placement policy y, p2) + // p2 will share the same rule as table t does, but it won't copy the meta to itself. we will + // append p2 range to the coverage of table t's rules. This mechanism is good for cascading change + // when policy x is altered. + for _, opt := range options { + if opt.Tp == ast.TableOptionPlacementPolicy { + partition.PlacementPolicyRef = &model.PolicyRefInfo{ + Name: model.NewCIStr(opt.StrValue), + } + } + } + + return nil +} + +func isNonDefaultPartitionOptionsUsed(defs []model.PartitionDefinition) bool { + for i := range defs { + orgDef := defs[i] + if orgDef.Name.O != fmt.Sprintf("p%d", i) { + return true + } + if len(orgDef.Comment) > 0 { + return true + } + if orgDef.PlacementPolicyRef != nil { + return true + } + } + return false +} + +func buildHashPartitionDefinitions(_ sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo, numParts uint64) ([]model.PartitionDefinition, error) { + if err := checkAddPartitionTooManyPartitions(tbInfo.Partition.Num); err != nil { + return nil, err + } + + definitions := make([]model.PartitionDefinition, numParts) + oldParts := uint64(len(tbInfo.Partition.Definitions)) + for i := uint64(0); i < numParts; i++ { + if i < oldParts { + // Use the existing definitions + def := tbInfo.Partition.Definitions[i] + definitions[i].Name = def.Name + definitions[i].Comment = def.Comment + definitions[i].PlacementPolicyRef = def.PlacementPolicyRef + } else if i < oldParts+uint64(len(defs)) { + // Use the new defs + def := defs[i-oldParts] + definitions[i].Name = def.Name + definitions[i].Comment, _ = def.Comment() + if err := setPartitionPlacementFromOptions(&definitions[i], def.Options); err != nil { + return nil, err + } + } else { + // Use the default + definitions[i].Name = model.NewCIStr(fmt.Sprintf("p%d", i)) + } + } + return definitions, nil +} + +func buildListPartitionDefinitions(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo) ([]model.PartitionDefinition, error) { + definitions := make([]model.PartitionDefinition, 0, len(defs)) + exprChecker := newPartitionExprChecker(ctx, nil, checkPartitionExprAllowed) + colTypes := collectColumnsType(tbInfo) + if len(colTypes) != len(tbInfo.Partition.Columns) { + return nil, dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") + } + for _, def := range defs { + if err := def.Clause.Validate(model.PartitionTypeList, len(tbInfo.Partition.Columns)); err != nil { + return nil, err + } + clause := def.Clause.(*ast.PartitionDefinitionClauseIn) + if len(tbInfo.Partition.Columns) > 0 { + for _, vs := range clause.Values { + // TODO: use the generated strings / normalized partition values + _, err := checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, vs) + if err != nil { + return nil, err + } + } + } else { + for _, vs := range clause.Values { + if err := checkPartitionValuesIsInt(ctx, def.Name, vs, tbInfo); err != nil { + return nil, err + } + } + } + comment, _ := def.Comment() + err := checkTooLongTable(def.Name) + if err != nil { + return nil, err + } + piDef := model.PartitionDefinition{ + Name: def.Name, + Comment: comment, + } + + if err = setPartitionPlacementFromOptions(&piDef, def.Options); err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + for _, vs := range clause.Values { + inValue := make([]string, 0, len(vs)) + for i := range vs { + vs[i].Accept(exprChecker) + if exprChecker.err != nil { + return nil, exprChecker.err + } + buf.Reset() + vs[i].Format(buf) + inValue = append(inValue, buf.String()) + } + piDef.InValues = append(piDef.InValues, inValue) + buf.Reset() + } + definitions = append(definitions, piDef) + } + return definitions, nil +} + +func collectColumnsType(tbInfo *model.TableInfo) []types.FieldType { + if len(tbInfo.Partition.Columns) > 0 { + colTypes := make([]types.FieldType, 0, len(tbInfo.Partition.Columns)) + for _, col := range tbInfo.Partition.Columns { + c := findColumnByName(col.L, tbInfo) + if c == nil { + return nil + } + colTypes = append(colTypes, c.FieldType) + } + + return colTypes + } + + return nil +} + +func buildRangePartitionDefinitions(ctx sessionctx.Context, defs []*ast.PartitionDefinition, tbInfo *model.TableInfo) ([]model.PartitionDefinition, error) { + definitions := make([]model.PartitionDefinition, 0, len(defs)) + exprChecker := newPartitionExprChecker(ctx, nil, checkPartitionExprAllowed) + colTypes := collectColumnsType(tbInfo) + if len(colTypes) != len(tbInfo.Partition.Columns) { + return nil, dbterror.ErrWrongPartitionName.GenWithStack("partition column name cannot be found") + } + for _, def := range defs { + if err := def.Clause.Validate(model.PartitionTypeRange, len(tbInfo.Partition.Columns)); err != nil { + return nil, err + } + clause := def.Clause.(*ast.PartitionDefinitionClauseLessThan) + var partValStrings []string + if len(tbInfo.Partition.Columns) > 0 { + var err error + if partValStrings, err = checkAndGetColumnsTypeAndValuesMatch(ctx, colTypes, clause.Exprs); err != nil { + return nil, err + } + } else { + if err := checkPartitionValuesIsInt(ctx, def.Name, clause.Exprs, tbInfo); err != nil { + return nil, err + } + } + comment, _ := def.Comment() + comment, err := validateCommentLength(ctx.GetSessionVars(), def.Name.L, &comment, dbterror.ErrTooLongTablePartitionComment) + if err != nil { + return nil, err + } + err = checkTooLongTable(def.Name) + if err != nil { + return nil, err + } + piDef := model.PartitionDefinition{ + Name: def.Name, + Comment: comment, + } + + if err = setPartitionPlacementFromOptions(&piDef, def.Options); err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + // Range columns partitions support multi-column partitions. + for i, expr := range clause.Exprs { + expr.Accept(exprChecker) + if exprChecker.err != nil { + return nil, exprChecker.err + } + // If multi-column use new evaluated+normalized output, instead of just formatted expression + if len(partValStrings) > i && len(colTypes) > 1 { + partVal := partValStrings[i] + switch colTypes[i].EvalType() { + case types.ETInt: + // no wrapping + case types.ETDatetime, types.ETString, types.ETDuration: + if _, ok := clause.Exprs[i].(*ast.MaxValueExpr); !ok { + // Don't wrap MAXVALUE + partVal = driver.WrapInSingleQuotes(partVal) + } + default: + return nil, dbterror.ErrWrongTypeColumnValue.GenWithStackByArgs() + } + piDef.LessThan = append(piDef.LessThan, partVal) + } else { + expr.Format(buf) + piDef.LessThan = append(piDef.LessThan, buf.String()) + buf.Reset() + } + } + definitions = append(definitions, piDef) + } + return definitions, nil +} + +func checkPartitionValuesIsInt(ctx sessionctx.Context, defName interface{}, exprs []ast.ExprNode, tbInfo *model.TableInfo) error { + tp := types.NewFieldType(mysql.TypeLonglong) + if isPartExprUnsigned(tbInfo) { + tp.AddFlag(mysql.UnsignedFlag) + } + for _, exp := range exprs { + if _, ok := exp.(*ast.MaxValueExpr); ok { + continue + } + if d, ok := exp.(*ast.DefaultExpr); ok { + if d.Name != nil { + return dbterror.ErrPartitionConstDomain.GenWithStackByArgs() + } + continue + } + val, err := expression.EvalAstExpr(ctx, exp) + if err != nil { + return err + } + switch val.Kind() { + case types.KindUint64, types.KindNull: + case types.KindInt64: + if mysql.HasUnsignedFlag(tp.GetFlag()) && val.GetInt64() < 0 { + return dbterror.ErrPartitionConstDomain.GenWithStackByArgs() + } + default: + return dbterror.ErrValuesIsNotIntType.GenWithStackByArgs(defName) + } + + _, err = val.ConvertTo(ctx.GetSessionVars().StmtCtx, tp) + if err != nil && !types.ErrOverflow.Equal(err) { + return dbterror.ErrWrongTypeColumnValue.GenWithStackByArgs() + } + } + + return nil +} + +func checkPartitionNameUnique(pi *model.PartitionInfo) error { + newPars := pi.Definitions + partNames := make(map[string]struct{}, len(newPars)) + for _, newPar := range newPars { + if _, ok := partNames[newPar.Name.L]; ok { + return dbterror.ErrSameNamePartition.GenWithStackByArgs(newPar.Name) + } + partNames[newPar.Name.L] = struct{}{} + } + return nil +} + +func checkAddPartitionNameUnique(tbInfo *model.TableInfo, pi *model.PartitionInfo) error { + partNames := make(map[string]struct{}) + if tbInfo.Partition != nil { + oldPars := tbInfo.Partition.Definitions + for _, oldPar := range oldPars { + partNames[oldPar.Name.L] = struct{}{} + } + } + newPars := pi.Definitions + for _, newPar := range newPars { + if _, ok := partNames[newPar.Name.L]; ok { + return dbterror.ErrSameNamePartition.GenWithStackByArgs(newPar.Name) + } + partNames[newPar.Name.L] = struct{}{} + } + return nil +} + +func checkReorgPartitionNames(p *model.PartitionInfo, droppedNames []string, pi *model.PartitionInfo) error { + partNames := make(map[string]struct{}) + oldDefs := p.Definitions + for _, oldDef := range oldDefs { + partNames[oldDef.Name.L] = struct{}{} + } + for _, delName := range droppedNames { + droppedName := strings.ToLower(delName) + if _, ok := partNames[droppedName]; !ok { + return dbterror.ErrSameNamePartition.GenWithStackByArgs(delName) + } + delete(partNames, droppedName) + } + newDefs := pi.Definitions + for _, newDef := range newDefs { + if _, ok := partNames[newDef.Name.L]; ok { + return dbterror.ErrSameNamePartition.GenWithStackByArgs(newDef.Name) + } + partNames[newDef.Name.L] = struct{}{} + } + return nil +} + +func checkAndOverridePartitionID(newTableInfo, oldTableInfo *model.TableInfo) error { + // If any old partitionInfo has lost, that means the partition ID lost too, so did the data, repair failed. + if newTableInfo.Partition == nil { + return nil + } + if oldTableInfo.Partition == nil { + return dbterror.ErrRepairTableFail.GenWithStackByArgs("Old table doesn't have partitions") + } + if newTableInfo.Partition.Type != oldTableInfo.Partition.Type { + return dbterror.ErrRepairTableFail.GenWithStackByArgs("Partition type should be the same") + } + // Check whether partitionType is hash partition. + if newTableInfo.Partition.Type == model.PartitionTypeHash { + if newTableInfo.Partition.Num != oldTableInfo.Partition.Num { + return dbterror.ErrRepairTableFail.GenWithStackByArgs("Hash partition num should be the same") + } + } + for i, newOne := range newTableInfo.Partition.Definitions { + found := false + for _, oldOne := range oldTableInfo.Partition.Definitions { + // Fix issue 17952 which wanna substitute partition range expr. + // So eliminate stringSliceEqual(newOne.LessThan, oldOne.LessThan) here. + if newOne.Name.L == oldOne.Name.L { + newTableInfo.Partition.Definitions[i].ID = oldOne.ID + found = true + break + } + } + if !found { + return dbterror.ErrRepairTableFail.GenWithStackByArgs("Partition " + newOne.Name.L + " has lost") + } + } + return nil +} + +// checkPartitionFuncValid checks partition function validly. +func checkPartitionFuncValid(ctx sessionctx.Context, tblInfo *model.TableInfo, expr ast.ExprNode) error { + if expr == nil { + return nil + } + exprChecker := newPartitionExprChecker(ctx, tblInfo, checkPartitionExprArgs, checkPartitionExprAllowed) + expr.Accept(exprChecker) + if exprChecker.err != nil { + return errors.Trace(exprChecker.err) + } + if len(exprChecker.columns) == 0 { + return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) + } + return nil +} + +// checkResultOK derives from https://github.com/mysql/mysql-server/blob/5.7/sql/item_timefunc +// For partition tables, mysql do not support Constant, random or timezone-dependent expressions +// Based on mysql code to check whether field is valid, every time related type has check_valid_arguments_processor function. +func checkResultOK(ok bool) error { + if !ok { + return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) + } + + return nil +} + +// checkPartitionFuncType checks partition function return type. +func checkPartitionFuncType(ctx sessionctx.Context, expr ast.ExprNode, tblInfo *model.TableInfo) error { + if expr == nil { + return nil + } + + e, err := expression.RewriteSimpleExprWithTableInfo(ctx, tblInfo, expr, false) + if err != nil { + return errors.Trace(err) + } + if e.GetType().EvalType() == types.ETInt { + return nil + } + + if col, ok := expr.(*ast.ColumnNameExpr); ok { + return errors.Trace(dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(col.Name.Name.L)) + } + + return errors.Trace(dbterror.ErrPartitionFuncNotAllowed.GenWithStackByArgs("PARTITION")) +} + +// checkRangePartitionValue checks whether `less than value` is strictly increasing for each partition. +// Side effect: it may simplify the partition range definition from a constant expression to an integer. +func checkRangePartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) error { + pi := tblInfo.Partition + defs := pi.Definitions + if len(defs) == 0 { + return nil + } + + if strings.EqualFold(defs[len(defs)-1].LessThan[0], partitionMaxValue) { + defs = defs[:len(defs)-1] + } + isUnsigned := isPartExprUnsigned(tblInfo) + var prevRangeValue interface{} + for i := 0; i < len(defs); i++ { + if strings.EqualFold(defs[i].LessThan[0], partitionMaxValue) { + return errors.Trace(dbterror.ErrPartitionMaxvalue) + } + + currentRangeValue, fromExpr, err := getRangeValue(ctx, defs[i].LessThan[0], isUnsigned) + if err != nil { + return errors.Trace(err) + } + if fromExpr { + // Constant fold the expression. + defs[i].LessThan[0] = fmt.Sprintf("%d", currentRangeValue) + } + + if i == 0 { + prevRangeValue = currentRangeValue + continue + } + + if isUnsigned { + if currentRangeValue.(uint64) <= prevRangeValue.(uint64) { + return errors.Trace(dbterror.ErrRangeNotIncreasing) + } + } else { + if currentRangeValue.(int64) <= prevRangeValue.(int64) { + return errors.Trace(dbterror.ErrRangeNotIncreasing) + } + } + prevRangeValue = currentRangeValue + } + return nil +} + +func checkListPartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) error { + pi := tblInfo.Partition + if len(pi.Definitions) == 0 { + return ast.ErrPartitionsMustBeDefined.GenWithStackByArgs("LIST") + } + expStr, err := formatListPartitionValue(ctx, tblInfo) + if err != nil { + return errors.Trace(err) + } + + partitionsValuesMap := make(map[string]struct{}) + for _, s := range expStr { + if _, ok := partitionsValuesMap[s]; ok { + return errors.Trace(dbterror.ErrMultipleDefConstInListPart) + } + partitionsValuesMap[s] = struct{}{} + } + + return nil +} + +func formatListPartitionValue(ctx sessionctx.Context, tblInfo *model.TableInfo) ([]string, error) { + defs := tblInfo.Partition.Definitions + pi := tblInfo.Partition + var colTps []*types.FieldType + cols := make([]*model.ColumnInfo, 0, len(pi.Columns)) + if len(pi.Columns) == 0 { + tp := types.NewFieldType(mysql.TypeLonglong) + if isPartExprUnsigned(tblInfo) { + tp.AddFlag(mysql.UnsignedFlag) + } + colTps = []*types.FieldType{tp} + } else { + colTps = make([]*types.FieldType, 0, len(pi.Columns)) + for _, colName := range pi.Columns { + colInfo := findColumnByName(colName.L, tblInfo) + if colInfo == nil { + return nil, errors.Trace(dbterror.ErrFieldNotFoundPart) + } + colTps = append(colTps, colInfo.FieldType.Clone()) + cols = append(cols, colInfo) + } + } + + haveDefault := false + exprStrs := make([]string, 0) + inValueStrs := make([]string, 0, mathutil.Max(len(pi.Columns), 1)) + for i := range defs { + inValuesLoop: + for j, vs := range defs[i].InValues { + inValueStrs = inValueStrs[:0] + for k, v := range vs { + // if DEFAULT would be given as string, like "DEFAULT", + // it would be stored as "'DEFAULT'", + if strings.EqualFold(v, "DEFAULT") && k == 0 && len(vs) == 1 { + if haveDefault { + return nil, dbterror.ErrMultipleDefConstInListPart + } + haveDefault = true + continue inValuesLoop + } + if strings.EqualFold(v, "MAXVALUE") { + return nil, errors.Trace(dbterror.ErrMaxvalueInValuesIn) + } + expr, err := expression.ParseSimpleExprCastWithTableInfo(ctx, v, &model.TableInfo{}, colTps[k]) + if err != nil { + return nil, errors.Trace(err) + } + eval, err := expr.Eval(chunk.Row{}) + if err != nil { + return nil, errors.Trace(err) + } + s, err := eval.ToString() + if err != nil { + return nil, errors.Trace(err) + } + if eval.IsNull() { + s = "NULL" + } else { + if colTps[k].EvalType() == types.ETInt { + defs[i].InValues[j][k] = s + } + if colTps[k].EvalType() == types.ETString { + s = string(hack.String(collate.GetCollator(cols[k].GetCollate()).Key(s))) + s = driver.WrapInSingleQuotes(s) + } + } + inValueStrs = append(inValueStrs, s) + } + exprStrs = append(exprStrs, strings.Join(inValueStrs, ",")) + } + } + return exprStrs, nil +} + +// getRangeValue gets an integer from the range value string. +// The returned boolean value indicates whether the input string is a constant expression. +func getRangeValue(ctx sessionctx.Context, str string, unsigned bool) (interface{}, bool, error) { + // Unsigned bigint was converted to uint64 handle. + if unsigned { + if value, err := strconv.ParseUint(str, 10, 64); err == nil { + return value, false, nil + } + + e, err1 := expression.ParseSimpleExprWithTableInfo(ctx, str, &model.TableInfo{}) + if err1 != nil { + return 0, false, err1 + } + res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) + if err2 == nil && !isNull { + return uint64(res), true, nil + } + } else { + if value, err := strconv.ParseInt(str, 10, 64); err == nil { + return value, false, nil + } + // The range value maybe not an integer, it could be a constant expression. + // For example, the following two cases are the same: + // PARTITION p0 VALUES LESS THAN (TO_SECONDS('2004-01-01')) + // PARTITION p0 VALUES LESS THAN (63340531200) + e, err1 := expression.ParseSimpleExprWithTableInfo(ctx, str, &model.TableInfo{}) + if err1 != nil { + return 0, false, err1 + } + res, isNull, err2 := e.EvalInt(ctx, chunk.Row{}) + if err2 == nil && !isNull { + return res, true, nil + } + } + return 0, false, dbterror.ErrNotAllowedTypeInPartition.GenWithStackByArgs(str) +} + +// CheckDropTablePartition checks if the partition exists and does not allow deleting the last existing partition in the table. +func CheckDropTablePartition(meta *model.TableInfo, partLowerNames []string) error { + pi := meta.Partition + if pi.Type != model.PartitionTypeRange && pi.Type != model.PartitionTypeList { + return dbterror.ErrOnlyOnRangeListPartition.GenWithStackByArgs("DROP") + } + + // To be error compatible with MySQL, we need to do this first! + // see https://github.com/pingcap/tidb/issues/31681#issuecomment-1015536214 + oldDefs := pi.Definitions + if len(oldDefs) <= len(partLowerNames) { + return errors.Trace(dbterror.ErrDropLastPartition) + } + + dupCheck := make(map[string]bool) + for _, pn := range partLowerNames { + found := false + for _, def := range oldDefs { + if def.Name.L == pn { + if _, ok := dupCheck[pn]; ok { + return errors.Trace(dbterror.ErrDropPartitionNonExistent.GenWithStackByArgs("DROP")) + } + dupCheck[pn] = true + found = true + break + } + } + if !found { + return errors.Trace(dbterror.ErrDropPartitionNonExistent.GenWithStackByArgs("DROP")) + } + } + return nil +} + +// updateDroppingPartitionInfo move dropping partitions to DroppingDefinitions, and return partitionIDs +func updateDroppingPartitionInfo(tblInfo *model.TableInfo, partLowerNames []string) []int64 { + oldDefs := tblInfo.Partition.Definitions + newDefs := make([]model.PartitionDefinition, 0, len(oldDefs)-len(partLowerNames)) + droppingDefs := make([]model.PartitionDefinition, 0, len(partLowerNames)) + pids := make([]int64, 0, len(partLowerNames)) + + // consider using a map to probe partLowerNames if too many partLowerNames + for i := range oldDefs { + found := false + for _, partName := range partLowerNames { + if oldDefs[i].Name.L == partName { + found = true + break + } + } + if found { + pids = append(pids, oldDefs[i].ID) + droppingDefs = append(droppingDefs, oldDefs[i]) + } else { + newDefs = append(newDefs, oldDefs[i]) + } + } + + tblInfo.Partition.Definitions = newDefs + tblInfo.Partition.DroppingDefinitions = droppingDefs + return pids +} + +func getPartitionDef(tblInfo *model.TableInfo, partName string) (index int, def *model.PartitionDefinition, _ error) { + defs := tblInfo.Partition.Definitions + for i := 0; i < len(defs); i++ { + if strings.EqualFold(defs[i].Name.L, strings.ToLower(partName)) { + return i, &(defs[i]), nil + } + } + return index, nil, table.ErrUnknownPartition.GenWithStackByArgs(partName, tblInfo.Name.O) +} + +func getPartitionIDsFromDefinitions(defs []model.PartitionDefinition) []int64 { + pids := make([]int64, 0, len(defs)) + for _, def := range defs { + pids = append(pids, def.ID) + } + return pids +} + +func hasGlobalIndex(tblInfo *model.TableInfo) bool { + for _, idxInfo := range tblInfo.Indices { + if idxInfo.Global { + return true + } + } + return false +} + +// getTableInfoWithDroppingPartitions builds oldTableInfo including dropping partitions, only used by onDropTablePartition. +func getTableInfoWithDroppingPartitions(t *model.TableInfo) *model.TableInfo { + p := t.Partition + nt := t.Clone() + np := *p + npd := make([]model.PartitionDefinition, 0, len(p.Definitions)+len(p.DroppingDefinitions)) + npd = append(npd, p.Definitions...) + npd = append(npd, p.DroppingDefinitions...) + np.Definitions = npd + np.DroppingDefinitions = nil + nt.Partition = &np + return nt +} + +// getTableInfoWithOriginalPartitions builds oldTableInfo including truncating partitions, only used by onTruncateTablePartition. +func getTableInfoWithOriginalPartitions(t *model.TableInfo, oldIDs []int64, newIDs []int64) *model.TableInfo { + nt := t.Clone() + np := nt.Partition + + // reconstruct original definitions + for _, oldDef := range np.DroppingDefinitions { + var newID int64 + for i := range newIDs { + if oldDef.ID == oldIDs[i] { + newID = newIDs[i] + break + } + } + for i := range np.Definitions { + newDef := &np.Definitions[i] + if newDef.ID == newID { + newDef.ID = oldDef.ID + break + } + } + } + + np.DroppingDefinitions = nil + np.NewPartitionIDs = nil + return nt +} + +func dropLabelRules(_ *ddlCtx, schemaName, tableName string, partNames []string) error { + deleteRules := make([]string, 0, len(partNames)) + for _, partName := range partNames { + deleteRules = append(deleteRules, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, schemaName, tableName, partName)) + } + // delete batch rules + patch := label.NewRulePatch([]*label.Rule{}, deleteRules) + return infosync.UpdateLabelRules(context.TODO(), patch) +} + +// onDropTablePartition deletes old partition meta. +func (w *worker) onDropTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var partNames []string + partInfo := model.PartitionInfo{} + if err := job.DecodeArgs(&partNames, &partInfo); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + if job.Type == model.ActionAddTablePartition || job.Type == model.ActionReorganizePartition || + job.Type == model.ActionRemovePartitioning || job.Type == model.ActionAlterTablePartitioning { + // It is rollback from reorganize partition, just remove DroppingDefinitions from tableInfo + tblInfo.Partition.DroppingDefinitions = nil + // It is rollbacked from adding table partition, just remove addingDefinitions from tableInfo. + physicalTableIDs, pNames, rollbackBundles := rollbackAddingPartitionInfo(tblInfo) + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), rollbackBundles) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + // TODO: Will this drop LabelRules for existing partitions, if the new partitions have the same name? + err = dropLabelRules(d, job.SchemaName, tblInfo.Name.L, pNames) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the label rules") + } + + if _, err := alterTableLabelRule(job.SchemaName, tblInfo, getIDs([]*model.TableInfo{tblInfo})); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + if partInfo.DDLType != model.PartitionTypeNone { + // Also remove anything with the new table id + physicalTableIDs = append(physicalTableIDs, tblInfo.Partition.NewTableID) + // Reset if it was normal table before + if tblInfo.Partition.Type == model.PartitionTypeNone { + tblInfo.Partition = nil + } else { + tblInfo.Partition.NewTableID = 0 + tblInfo.Partition.DDLExpr = "" + tblInfo.Partition.DDLColumns = nil + tblInfo.Partition.DDLType = model.PartitionTypeNone + } + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateRollbackDone, model.StateNone, ver, tblInfo) + job.Args = []interface{}{physicalTableIDs} + return ver, nil + } + + var physicalTableIDs []int64 + // In order to skip maintaining the state check in partitionDefinition, TiDB use droppingDefinition instead of state field. + // So here using `job.SchemaState` to judge what the stage of this job is. + originalState := job.SchemaState + switch job.SchemaState { + case model.StatePublic: + // If an error occurs, it returns that it cannot delete all partitions or that the partition doesn't exist. + err = CheckDropTablePartition(tblInfo, partNames) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + physicalTableIDs = updateDroppingPartitionInfo(tblInfo, partNames) + err = dropLabelRules(d, job.SchemaName, tblInfo.Name.L, partNames) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the label rules") + } + + if _, err := alterTableLabelRule(job.SchemaName, tblInfo, getIDs([]*model.TableInfo{tblInfo})); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + var bundles []*placement.Bundle + // create placement groups for each dropped partition to keep the data's placement before GC + // These placements groups will be deleted after GC + bundles, err = droppedPartitionBundles(t, tblInfo, tblInfo.Partition.DroppingDefinitions) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + var tableBundle *placement.Bundle + // Recompute table bundle to remove dropped partitions rules from its group + tableBundle, err = placement.NewTableBundle(t, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if tableBundle != nil { + bundles = append(bundles, tableBundle) + } + + if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + job.SchemaState = model.StateDeleteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != job.SchemaState) + case model.StateDeleteOnly: + // This state is not a real 'DeleteOnly' state, because tidb does not maintaining the state check in partitionDefinition. + // Insert this state to confirm all servers can not see the old partitions when reorg is running, + // so that no new data will be inserted into old partitions when reorganizing. + job.SchemaState = model.StateDeleteReorganization + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != job.SchemaState) + case model.StateDeleteReorganization: + oldTblInfo := getTableInfoWithDroppingPartitions(tblInfo) + physicalTableIDs = getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) + tbl, err := getTable(d.store, job.SchemaID, oldTblInfo) + if err != nil { + return ver, errors.Trace(err) + } + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + // If table has global indexes, we need reorg to clean up them. + if pt, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { + // Build elements for compatible with modify column type. elements will not be used when reorganizing. + elements := make([]*meta.Element, 0, len(tblInfo.Indices)) + for _, idxInfo := range tblInfo.Indices { + if idxInfo.Global { + elements = append(elements, &meta.Element{ID: idxInfo.ID, TypeKey: meta.IndexElementKey}) + } + } + sctx, err1 := w.sessPool.Get() + if err1 != nil { + return ver, err1 + } + defer w.sessPool.Put(sctx) + rh := newReorgHandler(sess.NewSession(sctx)) + reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, pt, physicalTableIDs, elements) + + if err != nil || reorgInfo.first { + // If we run reorg firstly, we should update the job snapshot version + // and then run the reorg next time. + return ver, errors.Trace(err) + } + err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (dropIndexErr error) { + defer tidbutil.Recover(metrics.LabelDDL, "onDropTablePartition", + func() { + dropIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("drop partition panic") + }, false) + return w.cleanupGlobalIndexes(pt, physicalTableIDs, reorgInfo) + }) + if err != nil { + if dbterror.ErrWaitReorgTimeout.Equal(err) { + // if timeout, we should return, check for the owner and re-wait job done. + return ver, nil + } + if dbterror.ErrPausedDDLJob.Equal(err) { + // if ErrPausedDDLJob, we should return, check for the owner and re-wait job done. + return ver, nil + } + return ver, errors.Trace(err) + } + } + if tblInfo.TiFlashReplica != nil { + removeTiFlashAvailablePartitionIDs(tblInfo, physicalTableIDs) + } + droppedDefs := tblInfo.Partition.DroppingDefinitions + tblInfo.Partition.DroppingDefinitions = nil + // used by ApplyDiff in updateSchemaVersion + job.CtxVars = []interface{}{physicalTableIDs} + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateNone + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: droppedDefs}}) + // A background job will be created to delete old partition data. + job.Args = []interface{}{physicalTableIDs} + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) + } + return ver, errors.Trace(err) +} + +func removeTiFlashAvailablePartitionIDs(tblInfo *model.TableInfo, pids []int64) { + // Remove the partitions + ids := tblInfo.TiFlashReplica.AvailablePartitionIDs + // Rarely called, so OK to take some time, to make it easy + for _, id := range pids { + for i, avail := range ids { + if id == avail { + tmp := ids[:i] + tmp = append(tmp, ids[i+1:]...) + ids = tmp + break + } + } + } + tblInfo.TiFlashReplica.AvailablePartitionIDs = ids +} + +// onTruncateTablePartition truncates old partition meta. +func (w *worker) onTruncateTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { + var ver int64 + var oldIDs, newIDs []int64 + if err := job.DecodeArgs(&oldIDs, &newIDs); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + if len(oldIDs) != len(newIDs) { + job.State = model.JobStateCancelled + return ver, errors.Trace(errors.New("len(oldIDs) must be the same as len(newIDs)")) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + pi := tblInfo.GetPartitionInfo() + if pi == nil { + return ver, errors.Trace(dbterror.ErrPartitionMgmtOnNonpartitioned) + } + + if !hasGlobalIndex(tblInfo) { + oldPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) + newPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) + for k, oldID := range oldIDs { + for i := 0; i < len(pi.Definitions); i++ { + def := &pi.Definitions[i] + if def.ID == oldID { + oldPartitions = append(oldPartitions, def.Clone()) + def.ID = newIDs[k] + // Shallow copy only use the def.ID in event handle. + newPartitions = append(newPartitions, *def) + break + } + } + } + if len(newPartitions) == 0 { + job.State = model.JobStateCancelled + return ver, table.ErrUnknownPartition.GenWithStackByArgs(fmt.Sprintf("pid:%v", oldIDs), tblInfo.Name.O) + } + + if err = clearTruncatePartitionTiflashStatus(tblInfo, newPartitions, oldIDs); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + if err = updateTruncatePartitionLabelRules(job, t, oldPartitions, newPartitions, tblInfo, oldIDs); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + preSplitAndScatter(w.sess.Context, d.store, tblInfo, newPartitions) + + job.CtxVars = []interface{}{oldIDs, newIDs} + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: newPartitions}}) + // A background job will be created to delete old partition data. + job.Args = []interface{}{oldIDs} + + return ver, err + } + + // When table has global index, public->deleteOnly->deleteReorg->none schema changes should be handled. + switch job.SchemaState { + case model.StatePublic: + // Step1: generate new partition ids + truncatingDefinitions := make([]model.PartitionDefinition, 0, len(oldIDs)) + for i, oldID := range oldIDs { + for j := 0; j < len(pi.Definitions); j++ { + def := &pi.Definitions[j] + if def.ID == oldID { + truncatingDefinitions = append(truncatingDefinitions, def.Clone()) + def.ID = newIDs[i] + break + } + } + } + pi.DroppingDefinitions = truncatingDefinitions + pi.NewPartitionIDs = newIDs[:] + + job.SchemaState = model.StateDeleteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + case model.StateDeleteOnly: + // This state is not a real 'DeleteOnly' state, because tidb does not maintaining the state check in partitionDefinition. + // Insert this state to confirm all servers can not see the old partitions when reorg is running, + // so that no new data will be inserted into old partitions when reorganizing. + job.SchemaState = model.StateDeleteReorganization + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + case model.StateDeleteReorganization: + // Step2: clear global index rows. + physicalTableIDs := oldIDs + oldTblInfo := getTableInfoWithOriginalPartitions(tblInfo, oldIDs, newIDs) + + tbl, err := getTable(d.store, job.SchemaID, oldTblInfo) + if err != nil { + return ver, errors.Trace(err) + } + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + // If table has global indexes, we need reorg to clean up them. + if pt, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { + // Build elements for compatible with modify column type. elements will not be used when reorganizing. + elements := make([]*meta.Element, 0, len(tblInfo.Indices)) + for _, idxInfo := range tblInfo.Indices { + if idxInfo.Global { + elements = append(elements, &meta.Element{ID: idxInfo.ID, TypeKey: meta.IndexElementKey}) + } + } + sctx, err1 := w.sessPool.Get() + if err1 != nil { + return ver, err1 + } + defer w.sessPool.Put(sctx) + rh := newReorgHandler(sess.NewSession(sctx)) + reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, pt, physicalTableIDs, elements) + + if err != nil || reorgInfo.first { + // If we run reorg firstly, we should update the job snapshot version + // and then run the reorg next time. + return ver, errors.Trace(err) + } + err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (dropIndexErr error) { + defer tidbutil.Recover(metrics.LabelDDL, "onDropTablePartition", + func() { + dropIndexErr = dbterror.ErrCancelledDDLJob.GenWithStack("drop partition panic") + }, false) + return w.cleanupGlobalIndexes(pt, physicalTableIDs, reorgInfo) + }) + if err != nil { + if dbterror.ErrWaitReorgTimeout.Equal(err) { + // if timeout, we should return, check for the owner and re-wait job done. + return ver, nil + } + return ver, errors.Trace(err) + } + } + + // Step3: generate new partition ids and finish rest works + oldPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) + newPartitions := make([]model.PartitionDefinition, 0, len(oldIDs)) + for _, oldDef := range pi.DroppingDefinitions { + var newID int64 + for i := range oldIDs { + if oldDef.ID == oldIDs[i] { + newID = newIDs[i] + break + } + } + for i := 0; i < len(pi.Definitions); i++ { + def := &pi.Definitions[i] + if newID == def.ID { + oldPartitions = append(oldPartitions, oldDef.Clone()) + newPartitions = append(newPartitions, def.Clone()) + break + } + } + } + if len(newPartitions) == 0 { + job.State = model.JobStateCancelled + return ver, table.ErrUnknownPartition.GenWithStackByArgs(fmt.Sprintf("pid:%v", oldIDs), tblInfo.Name.O) + } + + if err = clearTruncatePartitionTiflashStatus(tblInfo, newPartitions, oldIDs); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + if err = updateTruncatePartitionLabelRules(job, t, oldPartitions, newPartitions, tblInfo, oldIDs); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + // Step4: clear DroppingDefinitions and finish job. + tblInfo.Partition.DroppingDefinitions = nil + tblInfo.Partition.NewPartitionIDs = nil + + preSplitAndScatter(w.sess.Context, d.store, tblInfo, newPartitions) + + // used by ApplyDiff in updateSchemaVersion + job.CtxVars = []interface{}{oldIDs, newIDs} + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTablePartition, TableInfo: tblInfo, PartInfo: &model.PartitionInfo{Definitions: newPartitions}}) + // A background job will be created to delete old partition data. + job.Args = []interface{}{oldIDs} + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) + } + + return ver, errors.Trace(err) +} + +func clearTruncatePartitionTiflashStatus(tblInfo *model.TableInfo, newPartitions []model.PartitionDefinition, oldIDs []int64) error { + // Clear the tiflash replica available status. + if tblInfo.TiFlashReplica != nil { + e := infosync.ConfigureTiFlashPDForPartitions(true, &newPartitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID) + failpoint.Inject("FailTiFlashTruncatePartition", func() { + e = errors.New("enforced error") + }) + if e != nil { + logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(e)) + return e + } + tblInfo.TiFlashReplica.Available = false + // Set partition replica become unavailable. + removeTiFlashAvailablePartitionIDs(tblInfo, oldIDs) + } + return nil +} + +func updateTruncatePartitionLabelRules(job *model.Job, t *meta.Meta, oldPartitions, newPartitions []model.PartitionDefinition, tblInfo *model.TableInfo, oldIDs []int64) error { + bundles, err := placement.NewPartitionListBundles(t, newPartitions) + if err != nil { + return errors.Trace(err) + } + + tableBundle, err := placement.NewTableBundle(t, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + + if tableBundle != nil { + bundles = append(bundles, tableBundle) + } + + // create placement groups for each dropped partition to keep the data's placement before GC + // These placements groups will be deleted after GC + keepDroppedBundles, err := droppedPartitionBundles(t, tblInfo, oldPartitions) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + bundles = append(bundles, keepDroppedBundles...) + + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) + if err != nil { + return errors.Wrapf(err, "failed to notify PD the placement rules") + } + + tableID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L) + oldPartRules := make([]string, 0, len(oldIDs)) + for _, newPartition := range newPartitions { + oldPartRuleID := fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L, newPartition.Name.L) + oldPartRules = append(oldPartRules, oldPartRuleID) + } + + rules, err := infosync.GetLabelRules(context.TODO(), append(oldPartRules, tableID)) + if err != nil { + return errors.Wrapf(err, "failed to get label rules from PD") + } + + newPartIDs := getPartitionIDs(tblInfo) + newRules := make([]*label.Rule, 0, len(oldIDs)+1) + if tr, ok := rules[tableID]; ok { + newRules = append(newRules, tr.Clone().Reset(job.SchemaName, tblInfo.Name.L, "", append(newPartIDs, tblInfo.ID)...)) + } + + for idx, newPartition := range newPartitions { + if pr, ok := rules[oldPartRules[idx]]; ok { + newRules = append(newRules, pr.Clone().Reset(job.SchemaName, tblInfo.Name.L, newPartition.Name.L, newPartition.ID)) + } + } + + patch := label.NewRulePatch(newRules, []string{}) + err = infosync.UpdateLabelRules(context.TODO(), patch) + if err != nil { + return errors.Wrapf(err, "failed to notify PD the label rules") + } + + return nil +} + +// onExchangeTablePartition exchange partition data +func (w *worker) onExchangeTablePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var ( + // defID only for updateSchemaVersion + defID int64 + ptSchemaID int64 + ptID int64 + partName string + withValidation bool + ) + + if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + ntDbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + ptDbInfo, err := t.GetDatabase(ptSchemaID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + nt, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + if job.IsRollingback() { + return rollbackExchangeTablePartition(d, t, job, nt) + } + pt, err := getTableInfo(t, ptID, ptSchemaID) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableNotExists.Equal(err) { + job.State = model.JobStateCancelled + } + return ver, errors.Trace(err) + } + + _, partDef, err := getPartitionDef(pt, partName) + if err != nil { + return ver, errors.Trace(err) + } + if job.SchemaState == model.StateNone { + if pt.State != model.StatePublic { + job.State = model.JobStateCancelled + return ver, dbterror.ErrInvalidDDLState.GenWithStack("table %s is not in public, but %s", pt.Name, pt.State) + } + err = checkExchangePartition(pt, nt) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkTableDefCompatible(pt, nt) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkExchangePartitionPlacementPolicy(t, nt.PlacementPolicyRef, pt.PlacementPolicyRef, partDef.PlacementPolicyRef) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if defID != partDef.ID { + logutil.BgLogger().Info("Exchange partition id changed, updating to actual id", zap.String("category", "ddl"), + zap.String("job", job.String()), zap.Int64("defID", defID), zap.Int64("partDef.ID", partDef.ID)) + job.Args[0] = partDef.ID + defID = partDef.ID + err = updateDDLJob2Table(w.sess, job, true) + if err != nil { + return ver, errors.Trace(err) + } + } + var ptInfo []schemaIDAndTableInfo + if len(nt.Constraints) > 0 { + pt.ExchangePartitionInfo = &model.ExchangePartitionInfo{ + ExchangePartitionTableID: nt.ID, + ExchangePartitionDefID: defID, + } + ptInfo = append(ptInfo, schemaIDAndTableInfo{ + schemaID: ptSchemaID, + tblInfo: pt, + }) + } + nt.ExchangePartitionInfo = &model.ExchangePartitionInfo{ + ExchangePartitionTableID: ptID, + ExchangePartitionDefID: defID, + } + // We need an interim schema version, + // so there are no non-matching rows inserted + // into the table using the schema version + // before the exchange is made. + job.SchemaState = model.StateWriteOnly + return updateVersionAndTableInfoWithCheck(d, t, job, nt, true, ptInfo...) + } + // From now on, nt (the non-partitioned table) has + // ExchangePartitionInfo set, meaning it is restricted + // to only allow writes that would match the + // partition to be exchange with. + // So we need to rollback that change, instead of just cancelling. + + if d.lease > 0 { + delayForAsyncCommit() + } + + if defID != partDef.ID { + // Should never happen, should have been updated above, in previous state! + logutil.BgLogger().Error("Exchange partition id changed, updating to actual id", zap.String("category", "ddl"), + zap.String("job", job.String()), zap.Int64("defID", defID), zap.Int64("partDef.ID", partDef.ID)) + job.Args[0] = partDef.ID + defID = partDef.ID + err = updateDDLJob2Table(w.sess, job, true) + if err != nil { + return ver, errors.Trace(err) + } + } + + if withValidation { + ntbl, err := getTable(d.store, job.SchemaID, nt) + if err != nil { + return ver, errors.Trace(err) + } + ptbl, err := getTable(d.store, ptSchemaID, pt) + if err != nil { + return ver, errors.Trace(err) + } + err = checkExchangePartitionRecordValidation(w, ptbl, ntbl, ptDbInfo.Name.L, ntDbInfo.Name.L, partName) + if err != nil { + job.State = model.JobStateRollingback + return ver, errors.Trace(err) + } + } + + // partition table auto IDs. + ptAutoIDs, err := t.GetAutoIDAccessors(ptSchemaID, ptID).Get() + if err != nil { + return ver, errors.Trace(err) + } + // non-partition table auto IDs. + ntAutoIDs, err := t.GetAutoIDAccessors(job.SchemaID, nt.ID).Get() + if err != nil { + return ver, errors.Trace(err) + } + + if pt.TiFlashReplica != nil { + for i, id := range pt.TiFlashReplica.AvailablePartitionIDs { + if id == partDef.ID { + pt.TiFlashReplica.AvailablePartitionIDs[i] = nt.ID + break + } + } + } + + // Recreate non-partition table meta info, + // by first delete it with the old table id + err = t.DropTableOrView(job.SchemaID, nt.ID) + if err != nil { + return ver, errors.Trace(err) + } + + // exchange table meta id + pt.ExchangePartitionInfo = nil + partDef.ID, nt.ID = nt.ID, partDef.ID + + err = t.UpdateTable(ptSchemaID, pt) + if err != nil { + return ver, errors.Trace(err) + } + + err = t.CreateTableOrView(job.SchemaID, nt) + if err != nil { + return ver, errors.Trace(err) + } + + failpoint.Inject("exchangePartitionErr", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(ver, errors.New("occur an error after updating partition id")) + } + }) + + // Set both tables to the maximum auto IDs between normal table and partitioned table. + newAutoIDs := meta.AutoIDGroup{ + RowID: mathutil.Max(ptAutoIDs.RowID, ntAutoIDs.RowID), + IncrementID: mathutil.Max(ptAutoIDs.IncrementID, ntAutoIDs.IncrementID), + RandomID: mathutil.Max(ptAutoIDs.RandomID, ntAutoIDs.RandomID), + } + err = t.GetAutoIDAccessors(ptSchemaID, pt.ID).Put(newAutoIDs) + if err != nil { + return ver, errors.Trace(err) + } + err = t.GetAutoIDAccessors(job.SchemaID, nt.ID).Put(newAutoIDs) + if err != nil { + return ver, errors.Trace(err) + } + + failpoint.Inject("exchangePartitionAutoID", func(val failpoint.Value) { + if val.(bool) { + seCtx, err := w.sessPool.Get() + defer w.sessPool.Put(seCtx) + if err != nil { + failpoint.Return(ver, err) + } + se := sess.NewSession(seCtx) + _, err = se.Execute(context.Background(), "insert ignore into test.pt values (40000000)", "exchange_partition_test") + if err != nil { + failpoint.Return(ver, err) + } + } + }) + + // the follow code is a swap function for rules of two partitions + // though partitions has exchanged their ID, swap still take effect + + bundles, err := bundlesForExchangeTablePartition(t, pt, partDef, nt) + if err != nil { + return ver, errors.Trace(err) + } + + if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + ntrID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, nt.Name.L) + ptrID := fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, job.SchemaName, pt.Name.L, partDef.Name.L) + + rules, err := infosync.GetLabelRules(context.TODO(), []string{ntrID, ptrID}) + if err != nil { + return 0, errors.Wrapf(err, "failed to get PD the label rules") + } + + ntr := rules[ntrID] + ptr := rules[ptrID] + + // This must be a bug, nt cannot be partitioned! + partIDs := getPartitionIDs(nt) + + var setRules []*label.Rule + var deleteRules []string + if ntr != nil && ptr != nil { + setRules = append(setRules, ntr.Clone().Reset(job.SchemaName, pt.Name.L, partDef.Name.L, partDef.ID)) + setRules = append(setRules, ptr.Clone().Reset(job.SchemaName, nt.Name.L, "", append(partIDs, nt.ID)...)) + } else if ptr != nil { + setRules = append(setRules, ptr.Clone().Reset(job.SchemaName, nt.Name.L, "", append(partIDs, nt.ID)...)) + // delete ptr + deleteRules = append(deleteRules, ptrID) + } else if ntr != nil { + setRules = append(setRules, ntr.Clone().Reset(job.SchemaName, pt.Name.L, partDef.Name.L, partDef.ID)) + // delete ntr + deleteRules = append(deleteRules, ntrID) + } + + patch := label.NewRulePatch(setRules, deleteRules) + err = infosync.UpdateLabelRules(context.TODO(), patch) + if err != nil { + return ver, errors.Wrapf(err, "failed to notify PD the label rules") + } + + job.SchemaState = model.StatePublic + nt.ExchangePartitionInfo = nil + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, nt, true) + if err != nil { + return ver, errors.Trace(err) + } + + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, pt) + return ver, nil +} + +func getReorgPartitionInfo(t *meta.Meta, job *model.Job) (*model.TableInfo, []string, *model.PartitionInfo, []model.PartitionDefinition, []model.PartitionDefinition, error) { + schemaID := job.SchemaID + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return nil, nil, nil, nil, nil, errors.Trace(err) + } + partInfo := &model.PartitionInfo{} + var partNames []string + err = job.DecodeArgs(&partNames, &partInfo) + if err != nil { + job.State = model.JobStateCancelled + return nil, nil, nil, nil, nil, errors.Trace(err) + } + var addingDefs, droppingDefs []model.PartitionDefinition + if tblInfo.Partition != nil { + addingDefs = tblInfo.Partition.AddingDefinitions + droppingDefs = tblInfo.Partition.DroppingDefinitions + tblInfo.Partition.NewTableID = partInfo.NewTableID + tblInfo.Partition.DDLType = partInfo.Type + tblInfo.Partition.DDLExpr = partInfo.Expr + tblInfo.Partition.DDLColumns = partInfo.Columns + } else { + tblInfo.Partition = getPartitionInfoTypeNone() + tblInfo.Partition.NewTableID = partInfo.NewTableID + tblInfo.Partition.Definitions[0].ID = tblInfo.ID + tblInfo.Partition.DDLType = partInfo.Type + tblInfo.Partition.DDLExpr = partInfo.Expr + tblInfo.Partition.DDLColumns = partInfo.Columns + } + if len(addingDefs) == 0 { + addingDefs = []model.PartitionDefinition{} + } + if len(droppingDefs) == 0 { + droppingDefs = []model.PartitionDefinition{} + } + return tblInfo, partNames, partInfo, droppingDefs, addingDefs, nil +} + +func (w *worker) onReorganizePartition(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + // Handle the rolling back job + if job.IsRollingback() { + ver, err := w.onDropTablePartition(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + return ver, nil + } + + tblInfo, partNames, partInfo, _, addingDefinitions, err := getReorgPartitionInfo(t, job) + if err != nil { + return ver, err + } + + switch job.SchemaState { + case model.StateNone: + // job.SchemaState == model.StateNone means the job is in the initial state of reorg partition. + // Here should use partInfo from job directly and do some check action. + // In case there was a race for queueing different schema changes on the same + // table and the checks was not done on the current schema version. + // The partInfo may have been checked against an older schema version for example. + // If the check is done here, it does not need to be repeated, since no other + // DDL on the same table can be run concurrently. + num := len(partInfo.Definitions) - len(partNames) + len(tblInfo.Partition.Definitions) + err = checkAddPartitionTooManyPartitions(uint64(num)) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkReorgPartitionNames(tblInfo.Partition, partNames, partInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + // Re-check that the dropped/added partitions are compatible with current definition + firstPartIdx, lastPartIdx, idMap, err := getReplacedPartitionIDs(partNames, tblInfo.Partition) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + sctx := w.sess.Context + if err = checkReorgPartitionDefs(sctx, job.Type, tblInfo, partInfo, firstPartIdx, lastPartIdx, idMap); err != nil { + job.State = model.JobStateCancelled + return ver, err + } + + // move the adding definition into tableInfo. + updateAddingPartitionInfo(partInfo, tblInfo) + orgDefs := tblInfo.Partition.Definitions + _ = updateDroppingPartitionInfo(tblInfo, partNames) + // Reset original partitions, and keep DroppedDefinitions + tblInfo.Partition.Definitions = orgDefs + + // modify placement settings + for _, def := range tblInfo.Partition.AddingDefinitions { + if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, def.PlacementPolicyRef); err != nil { + // job.State = model.JobStateCancelled may be set depending on error in function above. + return ver, errors.Trace(err) + } + } + + // From now on we cannot just cancel the DDL, we must roll back if changesMade! + changesMade := false + if tblInfo.TiFlashReplica != nil { + // Must set placement rule, and make sure it succeeds. + if err := infosync.ConfigureTiFlashPDForPartitions(true, &tblInfo.Partition.AddingDefinitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); err != nil { + logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + changesMade = true + // In the next step, StateDeleteOnly, wait to verify the TiFlash replicas are OK + } + + bundles, err := alterTablePartitionBundles(t, tblInfo, tblInfo.Partition.AddingDefinitions) + if err != nil { + if !changesMade { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + + if len(bundles) > 0 { + if err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles); err != nil { + if !changesMade { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + changesMade = true + } + + ids := getIDs([]*model.TableInfo{tblInfo}) + for _, p := range tblInfo.Partition.AddingDefinitions { + ids = append(ids, p.ID) + } + changed, err := alterTableLabelRule(job.SchemaName, tblInfo, ids) + changesMade = changesMade || changed + if err != nil { + if !changesMade { + job.State = model.JobStateCancelled + return ver, err + } + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + + // Doing the preSplitAndScatter here, since all checks are completed, + // and we will soon start writing to the new partitions. + if s, ok := d.store.(kv.SplittableStore); ok && s != nil { + // partInfo only contains the AddingPartitions + splitPartitionTableRegion(w.sess.Context, s, tblInfo, partInfo.Definitions, true) + } + + // Assume we cannot have more than MaxUint64 rows, set the progress to 1/10 of that. + metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.1 / float64(math.MaxUint64)) + job.SchemaState = model.StateDeleteOnly + tblInfo.Partition.DDLState = model.StateDeleteOnly + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + // Is really both StateDeleteOnly AND StateWriteOnly needed? + // If transaction A in WriteOnly inserts row 1 (into both new and old partition set) + // and then transaction B in DeleteOnly deletes that row (in both new and old) + // does really transaction B need to do the delete in the new partition? + // Yes, otherwise it would still be there when the WriteReorg happens, + // and WriteReorg would only copy existing rows to the new table, so unless it is + // deleted it would result in a ghost row! + // What about update then? + // Updates also need to be handled for new partitions in DeleteOnly, + // since it would not be overwritten during Reorganize phase. + // BUT if the update results in adding in one partition and deleting in another, + // THEN only the delete must happen in the new partition set, not the insert! + case model.StateDeleteOnly: + // This state is to confirm all servers can not see the new partitions when reorg is running, + // so that all deletes will be done in both old and new partitions when in either DeleteOnly + // or WriteOnly state. + // Also using the state for checking that the optional TiFlash replica is available, making it + // in a state without (much) data and easy to retry without side effects. + + // Reason for having it here, is to make it easy for retry, and better to make sure it is in-sync + // as early as possible, to avoid a long wait after the data copying. + if tblInfo.TiFlashReplica != nil && tblInfo.TiFlashReplica.Available { + // For available state, the new added partition should wait its replica to + // be finished, otherwise the query to this partition will be blocked. + count := tblInfo.TiFlashReplica.Count + needRetry, err := checkPartitionReplica(count, addingDefinitions, d) + if err != nil { + // need to rollback, since we tried to register the new + // partitions before! + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + if needRetry { + // The new added partition hasn't been replicated. + // Do nothing to the job this time, wait next worker round. + time.Sleep(tiflashCheckTiDBHTTPAPIHalfInterval) + // Set the error here which will lead this job exit when it's retry times beyond the limitation. + return ver, errors.Errorf("[ddl] add partition wait for tiflash replica to complete") + } + + // When TiFlash Replica is ready, we must move them into `AvailablePartitionIDs`. + // Since onUpdateFlashReplicaStatus cannot see the partitions yet (not public) + for _, d := range addingDefinitions { + tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, d.ID) + } + } + + tblInfo.Partition.DDLState = model.StateWriteOnly + metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.2 / float64(math.MaxUint64)) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + // Insert this state to confirm all servers can see the new partitions when reorg is running, + // so that new data will be updated in both old and new partitions when reorganizing. + job.SnapshotVer = 0 + tblInfo.Partition.DDLState = model.StateWriteReorganization + metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, job.SchemaName, tblInfo.Name.String()).Set(0.3 / float64(math.MaxUint64)) + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + job.SchemaState = model.StateWriteReorganization + case model.StateWriteReorganization: + physicalTableIDs := getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) + tbl, err2 := getTable(d.store, job.SchemaID, tblInfo) + if err2 != nil { + return ver, errors.Trace(err2) + } + // TODO: If table has global indexes, we need reorg to clean up them. + // and then add the new partition ids back... + if _, ok := tbl.(table.PartitionedTable); ok && hasGlobalIndex(tblInfo) { + err = errors.Trace(dbterror.ErrCancelledDDLJob.GenWithStack("global indexes is not supported yet for reorganize partition")) + return convertAddTablePartitionJob2RollbackJob(d, t, job, err, tblInfo) + } + var done bool + done, ver, err = doPartitionReorgWork(w, d, t, job, tbl, physicalTableIDs) + + if !done { + return ver, err + } + + firstPartIdx, lastPartIdx, idMap, err2 := getReplacedPartitionIDs(partNames, tblInfo.Partition) + failpoint.Inject("reorgPartWriteReorgReplacedPartIDsFail", func(val failpoint.Value) { + if val.(bool) { + err2 = errors.New("Injected error by reorgPartWriteReorgReplacedPartIDsFail") + } + }) + if err2 != nil { + return ver, err2 + } + newDefs := getReorganizedDefinitions(tblInfo.Partition, firstPartIdx, lastPartIdx, idMap) + + // From now on, use the new partitioning, but keep the Adding and Dropping for double write + tblInfo.Partition.Definitions = newDefs + tblInfo.Partition.Num = uint64(len(newDefs)) + if job.Type == model.ActionAlterTablePartitioning || + job.Type == model.ActionRemovePartitioning { + tblInfo.Partition.Type, tblInfo.Partition.DDLType = tblInfo.Partition.DDLType, tblInfo.Partition.Type + tblInfo.Partition.Expr, tblInfo.Partition.DDLExpr = tblInfo.Partition.DDLExpr, tblInfo.Partition.Expr + tblInfo.Partition.Columns, tblInfo.Partition.DDLColumns = tblInfo.Partition.DDLColumns, tblInfo.Partition.Columns + } + + // Now all the data copying is done, but we cannot simply remove the droppingDefinitions + // since they are a part of the normal Definitions that other nodes with + // the current schema version. So we need to double write for one more schema version + tblInfo.Partition.DDLState = model.StateDeleteReorganization + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + job.SchemaState = model.StateDeleteReorganization + + case model.StateDeleteReorganization: + // Drop the droppingDefinitions and finish the DDL + // This state is needed for the case where client A sees the schema + // with version of StateWriteReorg and would not see updates of + // client B that writes to the new partitions, previously + // addingDefinitions, since it would not double write to + // the droppingDefinitions during this time + // By adding StateDeleteReorg state, client B will write to both + // the new (previously addingDefinitions) AND droppingDefinitions + + // Register the droppingDefinitions ids for rangeDelete + // and the addingDefinitions for handling in the updateSchemaVersion + physicalTableIDs := getPartitionIDsFromDefinitions(tblInfo.Partition.DroppingDefinitions) + newIDs := getPartitionIDsFromDefinitions(partInfo.Definitions) + statisticsPartInfo := &model.PartitionInfo{Definitions: tblInfo.Partition.AddingDefinitions} + + tblInfo.Partition.DroppingDefinitions = nil + tblInfo.Partition.AddingDefinitions = nil + tblInfo.Partition.DDLState = model.StateNone + + if job.Type != model.ActionReorganizePartition { + // ALTER TABLE ... PARTITION BY + // REMOVE PARTITIONING + // New Table ID, so needs to recreate the table by drop+create. + oldTblID := tblInfo.ID + // Overloading the NewTableID here with the oldTblID instead, + // for keeping the old global statistics + statisticsPartInfo.NewTableID = oldTblID + // TODO: Handle bundles? + // TODO: Add concurrent test! + // TODO: Will this result in big gaps? + // TODO: How to carrie over AUTO_INCREMENT etc.? + // Check if they are carried over in ApplyDiff?!? + autoIDs, err := t.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Get() + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + err = t.DropTableOrView(job.SchemaID, tblInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + tblInfo.ID = partInfo.NewTableID + if partInfo.DDLType != model.PartitionTypeNone { + // if partitioned before, then also add the old table ID, + // otherwise it will be the already included first partition + physicalTableIDs = append(physicalTableIDs, oldTblID) + } + if job.Type == model.ActionRemovePartitioning { + tblInfo.Partition = nil + } else { + // ALTER TABLE ... PARTITION BY + tblInfo.Partition.DDLType = model.PartitionTypeNone + tblInfo.Partition.DDLExpr = "" + tblInfo.Partition.DDLColumns = nil + tblInfo.Partition.NewTableID = 0 + } + err = t.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Put(autoIDs) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // TODO: Add failpoint here? + err = t.CreateTableOrView(job.SchemaID, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + job.CtxVars = []interface{}{physicalTableIDs, newIDs} + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + failpoint.Inject("reorgPartWriteReorgSchemaVersionUpdateFail", func(val failpoint.Value) { + if val.(bool) { + err = errors.New("Injected error by reorgPartWriteReorgSchemaVersionUpdateFail") + } + }) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + // How to handle this? + // Seems to only trigger asynchronous update of statistics. + // Should it actually be synchronous? + // Include the old table ID, if changed, which may contain global statistics, + // so it can be reused for the new (non)partitioned table. + asyncNotifyEvent(d, &util.Event{Tp: job.Type, TableInfo: tblInfo, PartInfo: statisticsPartInfo}) + // A background job will be created to delete old partition data. + job.Args = []interface{}{physicalTableIDs} + + default: + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("partition", job.SchemaState) + } + + return ver, errors.Trace(err) +} + +func doPartitionReorgWork(w *worker, d *ddlCtx, t *meta.Meta, job *model.Job, tbl table.Table, physTblIDs []int64) (done bool, ver int64, err error) { + job.ReorgMeta.ReorgTp = model.ReorgTypeTxn + sctx, err1 := w.sessPool.Get() + if err1 != nil { + return done, ver, err1 + } + defer w.sessPool.Put(sctx) + rh := newReorgHandler(sess.NewSession(sctx)) + elements := BuildElements(tbl.Meta().Columns[0], tbl.Meta().Indices) + partTbl, ok := tbl.(table.PartitionedTable) + if !ok { + return false, ver, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() + } + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return false, ver, errors.Trace(err) + } + reorgInfo, err := getReorgInfoFromPartitions(d.jobContext(job.ID, job.ReorgMeta), d, rh, job, dbInfo, partTbl, physTblIDs, elements) + err = w.runReorgJob(reorgInfo, tbl.Meta(), d.lease, func() (reorgErr error) { + defer tidbutil.Recover(metrics.LabelDDL, "doPartitionReorgWork", + func() { + reorgErr = dbterror.ErrCancelledDDLJob.GenWithStack("reorganize partition for table `%v` panic", tbl.Meta().Name) + }, false) + return w.reorgPartitionDataAndIndex(tbl, reorgInfo) + }) + if err != nil { + if dbterror.ErrPausedDDLJob.Equal(err) { + return false, ver, nil + } + + if dbterror.ErrWaitReorgTimeout.Equal(err) { + // If timeout, we should return, check for the owner and re-wait job done. + return false, ver, nil + } + if kv.IsTxnRetryableError(err) { + return false, ver, errors.Trace(err) + } + if err1 := rh.RemoveDDLReorgHandle(job, reorgInfo.elements); err1 != nil { + logutil.BgLogger().Warn("reorg partition job failed, RemoveDDLReorgHandle failed, can't convert job to rollback", zap.String("category", "ddl"), + zap.String("job", job.String()), zap.Error(err1)) + } + logutil.BgLogger().Warn("reorg partition job failed, convert job to rollback", zap.String("category", "ddl"), zap.String("job", job.String()), zap.Error(err)) + ver, err = convertAddTablePartitionJob2RollbackJob(d, t, job, err, tbl.Meta()) + return false, ver, errors.Trace(err) + } + return true, ver, err +} + +type reorgPartitionWorker struct { + *backfillCtx + // Static allocated to limit memory allocations + rowRecords []*rowRecord + rowDecoder *decoder.RowDecoder + rowMap map[int64]types.Datum + writeColOffsetMap map[int64]int + maxOffset int + reorgedTbl table.PartitionedTable +} + +func newReorgPartitionWorker(sessCtx sessionctx.Context, i int, t table.PhysicalTable, decodeColMap map[int64]decoder.Column, reorgInfo *reorgInfo, jc *JobContext) (*reorgPartitionWorker, error) { + reorgedTbl, err := tables.GetReorganizedPartitionedTable(t) + if err != nil { + return nil, errors.Trace(err) + } + pt := t.GetPartitionedTable() + if pt == nil { + return nil, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() + } + partColIDs := reorgedTbl.GetPartitionColumnIDs() + writeColOffsetMap := make(map[int64]int, len(partColIDs)) + maxOffset := 0 + for _, id := range partColIDs { + var offset int + for _, col := range pt.Cols() { + if col.ID == id { + offset = col.Offset + break + } + } + writeColOffsetMap[id] = offset + maxOffset = mathutil.Max[int](maxOffset, offset) + } + return &reorgPartitionWorker{ + backfillCtx: newBackfillCtx(reorgInfo.d, i, sessCtx, reorgInfo.SchemaName, t, jc, "reorg_partition_rate", false), + rowDecoder: decoder.NewRowDecoder(t, t.WritableCols(), decodeColMap), + rowMap: make(map[int64]types.Datum, len(decodeColMap)), + writeColOffsetMap: writeColOffsetMap, + maxOffset: maxOffset, + reorgedTbl: reorgedTbl, + }, nil +} + +func (w *reorgPartitionWorker) BackfillData(handleRange reorgBackfillTask) (taskCtx backfillTaskContext, errInTxn error) { + oprStartTime := time.Now() + ctx := kv.WithInternalSourceAndTaskType(context.Background(), w.jobContext.ddlJobSourceType(), kvutil.ExplicitTypeDDL) + errInTxn = kv.RunInNewTxn(ctx, w.sessCtx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + taskCtx.addedCount = 0 + taskCtx.scanCount = 0 + txn.SetOption(kv.Priority, handleRange.priority) + if tagger := w.GetCtx().getResourceGroupTaggerForTopSQL(handleRange.getJobID()); tagger != nil { + txn.SetOption(kv.ResourceGroupTagger, tagger) + } + txn.SetOption(kv.ResourceGroupName, w.jobContext.resourceGroupName) + + rowRecords, nextKey, taskDone, err := w.fetchRowColVals(txn, handleRange) + if err != nil { + return errors.Trace(err) + } + taskCtx.nextKey = nextKey + taskCtx.done = taskDone + + warningsMap := make(map[errors.ErrorID]*terror.Error) + warningsCountMap := make(map[errors.ErrorID]int64) + for _, prr := range rowRecords { + taskCtx.scanCount++ + + err = txn.Set(prr.key, prr.vals) + if err != nil { + return errors.Trace(err) + } + taskCtx.addedCount++ + if prr.warning != nil { + if _, ok := warningsCountMap[prr.warning.ID()]; ok { + warningsCountMap[prr.warning.ID()]++ + } else { + warningsCountMap[prr.warning.ID()] = 1 + warningsMap[prr.warning.ID()] = prr.warning + } + } + // TODO: Future optimization: also write the indexes here? + // What if the transaction limit is just enough for a single row, without index? + // Hmm, how could that be in the first place? + // For now, implement the batch-txn w.addTableIndex, + // since it already exists and is in use + } + + // Collect the warnings. + taskCtx.warnings, taskCtx.warningsCount = warningsMap, warningsCountMap + + // also add the index entries here? And make sure they are not added somewhere else + + return nil + }) + logSlowOperations(time.Since(oprStartTime), "BackfillData", 3000) + + return +} + +func (w *reorgPartitionWorker) fetchRowColVals(txn kv.Transaction, taskRange reorgBackfillTask) ([]*rowRecord, kv.Key, bool, error) { + w.rowRecords = w.rowRecords[:0] + startTime := time.Now() + + // taskDone means that the added handle is out of taskRange.endHandle. + taskDone := false + sysTZ := w.sessCtx.GetSessionVars().StmtCtx.TimeZone() + + tmpRow := make([]types.Datum, w.maxOffset+1) + var lastAccessedHandle kv.Key + oprStartTime := startTime + err := iterateSnapshotKeys(w.jobContext, w.sessCtx.GetStore(), taskRange.priority, w.table.RecordPrefix(), txn.StartTS(), taskRange.startKey, taskRange.endKey, + func(handle kv.Handle, recordKey kv.Key, rawRow []byte) (bool, error) { + oprEndTime := time.Now() + logSlowOperations(oprEndTime.Sub(oprStartTime), "iterateSnapshotKeys in reorgPartitionWorker fetchRowColVals", 0) + oprStartTime = oprEndTime + + taskDone = recordKey.Cmp(taskRange.endKey) >= 0 + + if taskDone || len(w.rowRecords) >= w.batchCnt { + return false, nil + } + + // TODO: Extend for normal tables + // TODO: Extend for REMOVE PARTITIONING + _, err := w.rowDecoder.DecodeTheExistedColumnMap(w.sessCtx, handle, rawRow, sysTZ, w.rowMap) + if err != nil { + return false, errors.Trace(err) + } + + // Set the partitioning columns and calculate which partition to write to + for colID, offset := range w.writeColOffsetMap { + d, ok := w.rowMap[colID] + if !ok { + return false, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() + } + tmpRow[offset] = d + } + p, err := w.reorgedTbl.GetPartitionByRow(w.sessCtx, tmpRow) + if err != nil { + return false, errors.Trace(err) + } + pid := p.GetPhysicalID() + newKey := tablecodec.EncodeTablePrefix(pid) + newKey = append(newKey, recordKey[len(newKey):]...) + w.rowRecords = append(w.rowRecords, &rowRecord{ + key: newKey, vals: rawRow, + }) + + w.cleanRowMap() + lastAccessedHandle = recordKey + if recordKey.Cmp(taskRange.endKey) == 0 { + taskDone = true + return false, nil + } + return true, nil + }) + + if len(w.rowRecords) == 0 { + taskDone = true + } + + logutil.BgLogger().Debug("txn fetches handle info", zap.String("category", "ddl"), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("taskRange", taskRange.String()), zap.Duration("takeTime", time.Since(startTime))) + return w.rowRecords, getNextHandleKey(taskRange, taskDone, lastAccessedHandle), taskDone, errors.Trace(err) +} + +func (w *reorgPartitionWorker) cleanRowMap() { + for id := range w.rowMap { + delete(w.rowMap, id) + } +} + +func (w *reorgPartitionWorker) AddMetricInfo(cnt float64) { + w.metricCounter.Add(cnt) +} + +func (*reorgPartitionWorker) String() string { + return typeReorgPartitionWorker.String() +} + +func (w *reorgPartitionWorker) GetCtx() *backfillCtx { + return w.backfillCtx +} + +func (w *worker) reorgPartitionDataAndIndex(t table.Table, reorgInfo *reorgInfo) error { + // First copy all table data to the new partitions + // from each of the DroppingDefinitions partitions. + // Then create all indexes on the AddingDefinitions partitions + // for each new index, one partition at a time. + + // Copy the data from the DroppingDefinitions to the AddingDefinitions + if bytes.Equal(reorgInfo.currElement.TypeKey, meta.ColumnElementKey) { + err := w.updatePhysicalTableRow(t, reorgInfo) + if err != nil { + return errors.Trace(err) + } + } + + failpoint.Inject("reorgPartitionAfterDataCopy", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + panic("panic test in reorgPartitionAfterDataCopy") + } + }) + + // Rewrite this to do all indexes at once in addTableIndex + // instead of calling it once per index (meaning reading the table multiple times) + // But for now, try to understand how it works... + firstNewPartitionID := t.Meta().Partition.AddingDefinitions[0].ID + startElementOffset := 0 + //startElementOffsetToResetHandle := -1 + // This backfill job starts with backfilling index data, whose index ID is currElement.ID. + if !bytes.Equal(reorgInfo.currElement.TypeKey, meta.IndexElementKey) { + // First run, have not yet started backfilling index data + // Restart with the first new partition. + // TODO: handle remove partitioning + reorgInfo.PhysicalTableID = firstNewPartitionID + } else { + // The job was interrupted and has been restarted, + // reset and start from where it was done + for i, element := range reorgInfo.elements[1:] { + if reorgInfo.currElement.ID == element.ID { + startElementOffset = i + //startElementOffsetToResetHandle = i + break + } + } + } + + for i := startElementOffset; i < len(reorgInfo.elements[1:]); i++ { + // Now build the indexes in the new partitions + var physTbl table.PhysicalTable + if tbl, ok := t.(table.PartitionedTable); ok { + physTbl = tbl.GetPartition(reorgInfo.PhysicalTableID) + } else if tbl, ok := t.(table.PhysicalTable); ok { + // This may be used when partitioning a non-partitioned table + physTbl = tbl + } + // Get the original start handle and end handle. + currentVer, err := getValidCurrentVersion(reorgInfo.d.store) + if err != nil { + return errors.Trace(err) + } + // TODO: Can we improve this in case of a crash? + // like where the regInfo PhysicalTableID and element is the same, + // and the tableid in the key-prefix regInfo.StartKey and regInfo.EndKey matches with PhysicalTableID + // do not change the reorgInfo start/end key + startHandle, endHandle, err := getTableRange(reorgInfo.NewJobContext(), reorgInfo.d, physTbl, currentVer.Ver, reorgInfo.Job.Priority) + if err != nil { + return errors.Trace(err) + } + + // Always (re)start with the full PhysicalTable range + reorgInfo.StartKey, reorgInfo.EndKey = startHandle, endHandle + + // Update the element in the reorgInfo for updating the reorg meta below. + reorgInfo.currElement = reorgInfo.elements[i+1] + // Write the reorg info to store so the whole reorganize process can recover from panic. + err = reorgInfo.UpdateReorgMeta(reorgInfo.StartKey, w.sessPool) + logutil.BgLogger().Info("update column and indexes", zap.String("category", "ddl"), + zap.Int64("jobID", reorgInfo.Job.ID), + zap.ByteString("elementType", reorgInfo.currElement.TypeKey), + zap.Int64("elementID", reorgInfo.currElement.ID), + zap.Int64("partitionTableId", physTbl.GetPhysicalID()), + zap.String("startHandle", hex.EncodeToString(reorgInfo.StartKey)), + zap.String("endHandle", hex.EncodeToString(reorgInfo.EndKey))) + if err != nil { + return errors.Trace(err) + } + err = w.addTableIndex(t, reorgInfo) + if err != nil { + return errors.Trace(err) + } + reorgInfo.PhysicalTableID = firstNewPartitionID + } + failpoint.Inject("reorgPartitionAfterIndex", func(val failpoint.Value) { + //nolint:forcetypeassert + if val.(bool) { + panic("panic test in reorgPartitionAfterIndex") + } + }) + return nil +} + +func bundlesForExchangeTablePartition(t *meta.Meta, pt *model.TableInfo, newPar *model.PartitionDefinition, nt *model.TableInfo) ([]*placement.Bundle, error) { + bundles := make([]*placement.Bundle, 0, 3) + + ptBundle, err := placement.NewTableBundle(t, pt) + if err != nil { + return nil, errors.Trace(err) + } + if ptBundle != nil { + bundles = append(bundles, ptBundle) + } + + parBundle, err := placement.NewPartitionBundle(t, *newPar) + if err != nil { + return nil, errors.Trace(err) + } + if parBundle != nil { + bundles = append(bundles, parBundle) + } + + ntBundle, err := placement.NewTableBundle(t, nt) + if err != nil { + return nil, errors.Trace(err) + } + if ntBundle != nil { + bundles = append(bundles, ntBundle) + } + + if parBundle == nil && ntBundle != nil { + // newPar.ID is the ID of old table to exchange, so ntBundle != nil means it has some old placement settings. + // We should remove it in this situation + bundles = append(bundles, placement.NewBundle(newPar.ID)) + } + + if parBundle != nil && ntBundle == nil { + // nt.ID is the ID of old partition to exchange, so parBundle != nil means it has some old placement settings. + // We should remove it in this situation + bundles = append(bundles, placement.NewBundle(nt.ID)) + } + + return bundles, nil +} + +func checkExchangePartitionRecordValidation(w *worker, ptbl, ntbl table.Table, pschemaName, nschemaName, partitionName string) error { + verifyFunc := func(sql string, params ...interface{}) error { + var ctx sessionctx.Context + ctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + rows, _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(w.ctx, nil, sql, params...) + if err != nil { + return errors.Trace(err) + } + rowCount := len(rows) + if rowCount != 0 { + return errors.Trace(dbterror.ErrRowDoesNotMatchPartition) + } + // Check warnings! + // Is it possible to check how many rows where checked as well? + return nil + } + genConstraintCondition := func(constraints []*table.Constraint) string { + var buf strings.Builder + buf.WriteString("not (") + for i, cons := range constraints { + if i != 0 { + buf.WriteString(" and ") + } + buf.WriteString(fmt.Sprintf("(%s)", cons.ExprString)) + } + buf.WriteString(")") + return buf.String() + } + type CheckConstraintTable interface { + WritableConstraint() []*table.Constraint + } + + pt := ptbl.Meta() + index, _, err := getPartitionDef(pt, partitionName) + if err != nil { + return errors.Trace(err) + } + + var buf strings.Builder + buf.WriteString("select 1 from %n.%n where ") + paramList := []interface{}{nschemaName, ntbl.Meta().Name.L} + checkNt := true + + pi := pt.Partition + switch pi.Type { + case model.PartitionTypeHash: + if pi.Num == 1 { + checkNt = false + } else { + buf.WriteString("mod(") + buf.WriteString(pi.Expr) + buf.WriteString(", %?) != %?") + paramList = append(paramList, pi.Num, index) + } + case model.PartitionTypeRange: + // Table has only one partition and has the maximum value + if len(pi.Definitions) == 1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { + checkNt = false + } else { + // For range expression and range columns + if len(pi.Columns) == 0 { + conds, params := buildCheckSQLConditionForRangeExprPartition(pi, index) + buf.WriteString(conds) + paramList = append(paramList, params...) + } else { + conds, params := buildCheckSQLConditionForRangeColumnsPartition(pi, index) + buf.WriteString(conds) + paramList = append(paramList, params...) + } + } + case model.PartitionTypeList: + if len(pi.Columns) == 0 { + conds := buildCheckSQLConditionForListPartition(pi, index) + buf.WriteString(conds) + } else { + conds := buildCheckSQLConditionForListColumnsPartition(pi, index) + buf.WriteString(conds) + } + default: + return dbterror.ErrUnsupportedPartitionType.GenWithStackByArgs(pt.Name.O) + } + + if variable.EnableCheckConstraint.Load() { + pcc, ok := ptbl.(CheckConstraintTable) + if !ok { + return errors.Errorf("exchange partition process assert table partition failed") + } + pCons := pcc.WritableConstraint() + if len(pCons) > 0 { + if !checkNt { + checkNt = true + } else { + buf.WriteString(" or ") + } + buf.WriteString(genConstraintCondition(pCons)) + } + } + // Check non-partition table records. + if checkNt { + buf.WriteString(" limit 1") + err = verifyFunc(buf.String(), paramList...) + if err != nil { + return errors.Trace(err) + } + } + + // Check partition table records. + if variable.EnableCheckConstraint.Load() { + ncc, ok := ntbl.(CheckConstraintTable) + if !ok { + return errors.Errorf("exchange partition process assert table partition failed") + } + nCons := ncc.WritableConstraint() + if len(nCons) > 0 { + buf.Reset() + buf.WriteString("select 1 from %n.%n partition(%n) where ") + buf.WriteString(genConstraintCondition(nCons)) + buf.WriteString(" limit 1") + err = verifyFunc(buf.String(), pschemaName, pt.Name.L, partitionName) + if err != nil { + return errors.Trace(err) + } + } + } + return nil +} + +func checkExchangePartitionPlacementPolicy(t *meta.Meta, ntPPRef, ptPPRef, partPPRef *model.PolicyRefInfo) error { + partitionPPRef := partPPRef + if partitionPPRef == nil { + partitionPPRef = ptPPRef + } + + if ntPPRef == nil && partitionPPRef == nil { + return nil + } + if ntPPRef == nil || partitionPPRef == nil { + return dbterror.ErrTablesDifferentMetadata + } + + ptPlacementPolicyInfo, _ := getPolicyInfo(t, partitionPPRef.ID) + ntPlacementPolicyInfo, _ := getPolicyInfo(t, ntPPRef.ID) + if ntPlacementPolicyInfo == nil && ptPlacementPolicyInfo == nil { + return nil + } + if ntPlacementPolicyInfo == nil || ptPlacementPolicyInfo == nil { + return dbterror.ErrTablesDifferentMetadata + } + if ntPlacementPolicyInfo.Name.L != ptPlacementPolicyInfo.Name.L { + return dbterror.ErrTablesDifferentMetadata + } + + return nil +} + +func buildCheckSQLConditionForRangeExprPartition(pi *model.PartitionInfo, index int) (string, []interface{}) { + var buf strings.Builder + paramList := make([]interface{}, 0, 2) + // Since the pi.Expr string may contain the identifier, which couldn't be escaped in our ParseWithParams(...) + // So we write it to the origin sql string here. + if index == 0 { + buf.WriteString(pi.Expr) + buf.WriteString(" >= %?") + paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) + } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { + buf.WriteString(pi.Expr) + buf.WriteString(" < %?") + paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0])) + } else { + buf.WriteString(pi.Expr) + buf.WriteString(" < %? or ") + buf.WriteString(pi.Expr) + buf.WriteString(" >= %?") + paramList = append(paramList, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0]), driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) + } + return buf.String(), paramList +} + +func buildCheckSQLConditionForRangeColumnsPartition(pi *model.PartitionInfo, index int) (string, []interface{}) { + paramList := make([]interface{}, 0, 2) + colName := pi.Columns[0].L + if index == 0 { + paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) + return "%n >= %?", paramList + } else if index == len(pi.Definitions)-1 && strings.EqualFold(pi.Definitions[index].LessThan[0], partitionMaxValue) { + paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0])) + return "%n < %?", paramList + } else { + paramList = append(paramList, colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index-1].LessThan[0]), colName, driver.UnwrapFromSingleQuotes(pi.Definitions[index].LessThan[0])) + return "%n < %? or %n >= %?", paramList + } +} + +func buildCheckSQLConditionForListPartition(pi *model.PartitionInfo, index int) string { + var buf strings.Builder + buf.WriteString("not (") + for i, inValue := range pi.Definitions[index].InValues { + if i != 0 { + buf.WriteString(" OR ") + } + // AND has higher priority than OR, so no need for parentheses + for j, val := range inValue { + if j != 0 { + // Should never happen, since there should be no multi-columns, only a single expression :) + buf.WriteString(" AND ") + } + // null-safe compare '<=>' + buf.WriteString(fmt.Sprintf("(%s) <=> %s", pi.Expr, val)) + } + } + buf.WriteString(")") + return buf.String() +} + +func buildCheckSQLConditionForListColumnsPartition(pi *model.PartitionInfo, index int) string { + var buf strings.Builder + // How to find a match? + // (row <=> vals1) OR (row <=> vals2) + // How to find a non-matching row: + // NOT ( (row <=> vals1) OR (row <=> vals2) ... ) + buf.WriteString("not (") + colNames := make([]string, 0, len(pi.Columns)) + for i := range pi.Columns { + // TODO: check if there are no proper quoting function for this? + n := "`" + strings.ReplaceAll(pi.Columns[i].O, "`", "``") + "`" + colNames = append(colNames, n) + } + for i, colValues := range pi.Definitions[index].InValues { + if i != 0 { + buf.WriteString(" OR ") + } + // AND has higher priority than OR, so no need for parentheses + for j, val := range colValues { + if j != 0 { + buf.WriteString(" AND ") + } + // null-safe compare '<=>' + buf.WriteString(fmt.Sprintf("%s <=> %s", colNames[j], val)) + } + } + buf.WriteString(")") + return buf.String() +} + +func checkAddPartitionTooManyPartitions(piDefs uint64) error { + if piDefs > uint64(mysql.PartitionCountLimit) { + return errors.Trace(dbterror.ErrTooManyPartitions) + } + return nil +} + +func checkAddPartitionOnTemporaryMode(tbInfo *model.TableInfo) error { + if tbInfo.Partition != nil && tbInfo.TempTableType != model.TempTableNone { + return dbterror.ErrPartitionNoTemporary + } + return nil +} + +func checkPartitionColumnsUnique(tbInfo *model.TableInfo) error { + if len(tbInfo.Partition.Columns) <= 1 { + return nil + } + var columnsMap = make(map[string]struct{}) + for _, col := range tbInfo.Partition.Columns { + if _, ok := columnsMap[col.L]; ok { + return dbterror.ErrSameNamePartitionField.GenWithStackByArgs(col.L) + } + columnsMap[col.L] = struct{}{} + } + return nil +} + +func checkNoHashPartitions(_ sessionctx.Context, partitionNum uint64) error { + if partitionNum == 0 { + return ast.ErrNoParts.GenWithStackByArgs("partitions") + } + return nil +} + +func getPartitionIDs(table *model.TableInfo) []int64 { + if table.GetPartitionInfo() == nil { + return []int64{} + } + physicalTableIDs := make([]int64, 0, len(table.Partition.Definitions)) + for _, def := range table.Partition.Definitions { + physicalTableIDs = append(physicalTableIDs, def.ID) + } + return physicalTableIDs +} + +func getPartitionRuleIDs(dbName string, table *model.TableInfo) []string { + if table.GetPartitionInfo() == nil { + return []string{} + } + partRuleIDs := make([]string, 0, len(table.Partition.Definitions)) + for _, def := range table.Partition.Definitions { + partRuleIDs = append(partRuleIDs, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, dbName, table.Name.L, def.Name.L)) + } + return partRuleIDs +} + +// checkPartitioningKeysConstraints checks that the range partitioning key is included in the table constraint. +func checkPartitioningKeysConstraints(sctx sessionctx.Context, s *ast.CreateTableStmt, tblInfo *model.TableInfo) error { + // Returns directly if there are no unique keys in the table. + if len(tblInfo.Indices) == 0 && !tblInfo.PKIsHandle { + return nil + } + + partCols, err := getPartitionColSlices(sctx, tblInfo, s.Partition) + if err != nil { + return errors.Trace(err) + } + + // Checks that the partitioning key is included in the constraint. + // Every unique key on the table must use every column in the table's partitioning expression. + // See https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html + for _, index := range tblInfo.Indices { + if index.Unique && !checkUniqueKeyIncludePartKey(partCols, index.Columns) { + if index.Primary { + // not support global index with clustered index + if tblInfo.IsCommonHandle { + return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("CLUSTERED INDEX") + } + if !config.GetGlobalConfig().EnableGlobalIndex { + return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("PRIMARY KEY") + } + } + if !config.GetGlobalConfig().EnableGlobalIndex { + return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("UNIQUE INDEX") + } + } + } + // when PKIsHandle, tblInfo.Indices will not contain the primary key. + if tblInfo.PKIsHandle { + indexCols := []*model.IndexColumn{{ + Name: tblInfo.GetPkName(), + Length: types.UnspecifiedLength, + }} + if !checkUniqueKeyIncludePartKey(partCols, indexCols) { + return dbterror.ErrUniqueKeyNeedAllFieldsInPf.GenWithStackByArgs("CLUSTERED INDEX") + } + } + return nil +} + +func checkPartitionKeysConstraint(pi *model.PartitionInfo, indexColumns []*model.IndexColumn, tblInfo *model.TableInfo) (bool, error) { + var ( + partCols []*model.ColumnInfo + err error + ) + // The expr will be an empty string if the partition is defined by: + // CREATE TABLE t (...) PARTITION BY RANGE COLUMNS(...) + if partExpr := pi.Expr; partExpr != "" { + // Parse partitioning key, extract the column names in the partitioning key to slice. + partCols, err = extractPartitionColumns(partExpr, tblInfo) + if err != nil { + return false, err + } + } else { + partCols = make([]*model.ColumnInfo, 0, len(pi.Columns)) + for _, col := range pi.Columns { + colInfo := tblInfo.FindPublicColumnByName(col.L) + if colInfo == nil { + return false, infoschema.ErrColumnNotExists.GenWithStackByArgs(col, tblInfo.Name) + } + partCols = append(partCols, colInfo) + } + } + + // In MySQL, every unique key on the table must use every column in the table's partitioning expression.(This + // also includes the table's primary key.) + // In TiDB, global index will be built when this constraint is not satisfied and EnableGlobalIndex is set. + // See https://dev.mysql.com/doc/refman/5.7/en/partitioning-limitations-partitioning-keys-unique-keys.html + return checkUniqueKeyIncludePartKey(columnInfoSlice(partCols), indexColumns), nil +} + +type columnNameExtractor struct { + extractedColumns []*model.ColumnInfo + tblInfo *model.TableInfo + err error +} + +func (*columnNameExtractor) Enter(node ast.Node) (ast.Node, bool) { + return node, false +} + +func (cne *columnNameExtractor) Leave(node ast.Node) (ast.Node, bool) { + if c, ok := node.(*ast.ColumnNameExpr); ok { + info := findColumnByName(c.Name.Name.L, cne.tblInfo) + if info != nil { + cne.extractedColumns = append(cne.extractedColumns, info) + return node, true + } + cne.err = dbterror.ErrBadField.GenWithStackByArgs(c.Name.Name.O, "expression") + return nil, false + } + return node, true +} + +func findColumnByName(colName string, tblInfo *model.TableInfo) *model.ColumnInfo { + if tblInfo == nil { + return nil + } + for _, info := range tblInfo.Columns { + if info.Name.L == colName { + return info + } + } + return nil +} + +func extractPartitionColumns(partExpr string, tblInfo *model.TableInfo) ([]*model.ColumnInfo, error) { + partExpr = "select " + partExpr + stmts, _, err := parser.New().ParseSQL(partExpr) + if err != nil { + return nil, errors.Trace(err) + } + extractor := &columnNameExtractor{ + tblInfo: tblInfo, + extractedColumns: make([]*model.ColumnInfo, 0), + } + stmts[0].Accept(extractor) + if extractor.err != nil { + return nil, errors.Trace(extractor.err) + } + return extractor.extractedColumns, nil +} + +// stringSlice is defined for checkUniqueKeyIncludePartKey. +// if Go supports covariance, the code shouldn't be so complex. +type stringSlice interface { + Len() int + At(i int) string +} + +// checkUniqueKeyIncludePartKey checks that the partitioning key is included in the constraint. +func checkUniqueKeyIncludePartKey(partCols stringSlice, idxCols []*model.IndexColumn) bool { + for i := 0; i < partCols.Len(); i++ { + partCol := partCols.At(i) + _, idxCol := model.FindIndexColumnByName(idxCols, partCol) + if idxCol == nil { + // Partition column is not found in the index columns. + return false + } + if idxCol.Length > 0 { + // The partition column is found in the index columns, but the index column is a prefix index + return false + } + } + return true +} + +// columnInfoSlice implements the stringSlice interface. +type columnInfoSlice []*model.ColumnInfo + +func (cis columnInfoSlice) Len() int { + return len(cis) +} + +func (cis columnInfoSlice) At(i int) string { + return cis[i].Name.L +} + +// columnNameSlice implements the stringSlice interface. +type columnNameSlice []*ast.ColumnName + +func (cns columnNameSlice) Len() int { + return len(cns) +} + +func (cns columnNameSlice) At(i int) string { + return cns[i].Name.L +} + +func isPartExprUnsigned(tbInfo *model.TableInfo) bool { + // We should not rely on any configuration, system or session variables, so use a mock ctx! + // Same as in tables.newPartitionExpr + ctx := mock.NewContext() + expr, err := expression.ParseSimpleExprWithTableInfo(ctx, tbInfo.Partition.Expr, tbInfo) + if err != nil { + logutil.BgLogger().Error("isPartExpr failed parsing expression!", zap.Error(err)) + return false + } + if mysql.HasUnsignedFlag(expr.GetType().GetFlag()) { + return true + } + return false +} + +// truncateTableByReassignPartitionIDs reassigns new partition ids. +func truncateTableByReassignPartitionIDs(t *meta.Meta, tblInfo *model.TableInfo, pids []int64) (err error) { + if len(pids) < len(tblInfo.Partition.Definitions) { + // To make it compatible with older versions when pids was not given + // and if there has been any add/reorganize partition increasing the number of partitions + morePids, err := t.GenGlobalIDs(len(tblInfo.Partition.Definitions) - len(pids)) + if err != nil { + return errors.Trace(err) + } + pids = append(pids, morePids...) + } + newDefs := make([]model.PartitionDefinition, 0, len(tblInfo.Partition.Definitions)) + for i, def := range tblInfo.Partition.Definitions { + newDef := def + newDef.ID = pids[i] + newDefs = append(newDefs, newDef) + } + tblInfo.Partition.Definitions = newDefs + return nil +} + +type partitionExprProcessor func(sessionctx.Context, *model.TableInfo, ast.ExprNode) error + +type partitionExprChecker struct { + processors []partitionExprProcessor + ctx sessionctx.Context + tbInfo *model.TableInfo + err error + + columns []*model.ColumnInfo +} + +func newPartitionExprChecker(ctx sessionctx.Context, tbInfo *model.TableInfo, processor ...partitionExprProcessor) *partitionExprChecker { + p := &partitionExprChecker{processors: processor, ctx: ctx, tbInfo: tbInfo} + p.processors = append(p.processors, p.extractColumns) + return p +} + +func (p *partitionExprChecker) Enter(n ast.Node) (node ast.Node, skipChildren bool) { + expr, ok := n.(ast.ExprNode) + if !ok { + return n, true + } + for _, processor := range p.processors { + if err := processor(p.ctx, p.tbInfo, expr); err != nil { + p.err = err + return n, true + } + } + + return n, false +} + +func (p *partitionExprChecker) Leave(n ast.Node) (node ast.Node, ok bool) { + return n, p.err == nil +} + +func (p *partitionExprChecker) extractColumns(_ sessionctx.Context, _ *model.TableInfo, expr ast.ExprNode) error { + columnNameExpr, ok := expr.(*ast.ColumnNameExpr) + if !ok { + return nil + } + colInfo := findColumnByName(columnNameExpr.Name.Name.L, p.tbInfo) + if colInfo == nil { + return errors.Trace(dbterror.ErrBadField.GenWithStackByArgs(columnNameExpr.Name.Name.L, "partition function")) + } + + p.columns = append(p.columns, colInfo) + return nil +} + +func checkPartitionExprAllowed(_ sessionctx.Context, tb *model.TableInfo, e ast.ExprNode) error { + switch v := e.(type) { + case *ast.FuncCallExpr: + if _, ok := expression.AllowedPartitionFuncMap[v.FnName.L]; ok { + return nil + } + case *ast.BinaryOperationExpr: + if _, ok := expression.AllowedPartition4BinaryOpMap[v.Op]; ok { + return errors.Trace(checkNoTimestampArgs(tb, v.L, v.R)) + } + case *ast.UnaryOperationExpr: + if _, ok := expression.AllowedPartition4UnaryOpMap[v.Op]; ok { + return errors.Trace(checkNoTimestampArgs(tb, v.V)) + } + case *ast.ColumnNameExpr, *ast.ParenthesesExpr, *driver.ValueExpr, *ast.MaxValueExpr, + *ast.DefaultExpr, *ast.TimeUnitExpr: + return nil + } + return errors.Trace(dbterror.ErrPartitionFunctionIsNotAllowed) +} + +func checkPartitionExprArgs(_ sessionctx.Context, tblInfo *model.TableInfo, e ast.ExprNode) error { + expr, ok := e.(*ast.FuncCallExpr) + if !ok { + return nil + } + argsType, err := collectArgsType(tblInfo, expr.Args...) + if err != nil { + return errors.Trace(err) + } + switch expr.FnName.L { + case ast.ToDays, ast.ToSeconds, ast.DayOfMonth, ast.Month, ast.DayOfYear, ast.Quarter, ast.YearWeek, + ast.Year, ast.Weekday, ast.DayOfWeek, ast.Day: + return errors.Trace(checkResultOK(hasDateArgs(argsType...))) + case ast.Hour, ast.Minute, ast.Second, ast.TimeToSec, ast.MicroSecond: + return errors.Trace(checkResultOK(hasTimeArgs(argsType...))) + case ast.UnixTimestamp: + return errors.Trace(checkResultOK(hasTimestampArgs(argsType...))) + case ast.FromDays: + return errors.Trace(checkResultOK(hasDateArgs(argsType...) || hasTimeArgs(argsType...))) + case ast.Extract: + switch expr.Args[0].(*ast.TimeUnitExpr).Unit { + case ast.TimeUnitYear, ast.TimeUnitYearMonth, ast.TimeUnitQuarter, ast.TimeUnitMonth, ast.TimeUnitDay: + return errors.Trace(checkResultOK(hasDateArgs(argsType...))) + case ast.TimeUnitDayMicrosecond, ast.TimeUnitDayHour, ast.TimeUnitDayMinute, ast.TimeUnitDaySecond: + return errors.Trace(checkResultOK(hasDatetimeArgs(argsType...))) + case ast.TimeUnitHour, ast.TimeUnitHourMinute, ast.TimeUnitHourSecond, ast.TimeUnitMinute, ast.TimeUnitMinuteSecond, + ast.TimeUnitSecond, ast.TimeUnitMicrosecond, ast.TimeUnitHourMicrosecond, ast.TimeUnitMinuteMicrosecond, ast.TimeUnitSecondMicrosecond: + return errors.Trace(checkResultOK(hasTimeArgs(argsType...))) + default: + return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) + } + case ast.DateDiff: + return errors.Trace(checkResultOK(slice.AllOf(argsType, func(i int) bool { + return hasDateArgs(argsType[i]) + }))) + + case ast.Abs, ast.Ceiling, ast.Floor, ast.Mod: + has := hasTimestampArgs(argsType...) + if has { + return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) + } + } + return nil +} + +func collectArgsType(tblInfo *model.TableInfo, exprs ...ast.ExprNode) ([]byte, error) { + ts := make([]byte, 0, len(exprs)) + for _, arg := range exprs { + col, ok := arg.(*ast.ColumnNameExpr) + if !ok { + continue + } + columnInfo := findColumnByName(col.Name.Name.L, tblInfo) + if columnInfo == nil { + return nil, errors.Trace(dbterror.ErrBadField.GenWithStackByArgs(col.Name.Name.L, "partition function")) + } + ts = append(ts, columnInfo.GetType()) + } + + return ts, nil +} + +func hasDateArgs(argsType ...byte) bool { + return slice.AnyOf(argsType, func(i int) bool { + return argsType[i] == mysql.TypeDate || argsType[i] == mysql.TypeDatetime + }) +} + +func hasTimeArgs(argsType ...byte) bool { + return slice.AnyOf(argsType, func(i int) bool { + return argsType[i] == mysql.TypeDuration || argsType[i] == mysql.TypeDatetime + }) +} + +func hasTimestampArgs(argsType ...byte) bool { + return slice.AnyOf(argsType, func(i int) bool { + return argsType[i] == mysql.TypeTimestamp + }) +} + +func hasDatetimeArgs(argsType ...byte) bool { + return slice.AnyOf(argsType, func(i int) bool { + return argsType[i] == mysql.TypeDatetime + }) +} + +func checkNoTimestampArgs(tbInfo *model.TableInfo, exprs ...ast.ExprNode) error { + argsType, err := collectArgsType(tbInfo, exprs...) + if err != nil { + return err + } + if hasTimestampArgs(argsType...) { + return errors.Trace(dbterror.ErrWrongExprInPartitionFunc) + } + return nil +} + +// hexIfNonPrint checks if printable UTF-8 characters from a single quoted string, +// if so, just returns the string +// else returns a hex string of the binary string (i.e. actual encoding, not unicode code points!) +func hexIfNonPrint(s string) string { + isPrint := true + // https://go.dev/blog/strings `for range` of string converts to runes! + for _, runeVal := range s { + if !strconv.IsPrint(runeVal) { + isPrint = false + break + } + } + if isPrint { + return s + } + // To avoid 'simple' MySQL accepted escape characters, to be showed as hex, just escape them + // \0 \b \n \r \t \Z, see https://dev.mysql.com/doc/refman/8.0/en/string-literals.html + isPrint = true + res := "" + for _, runeVal := range s { + switch runeVal { + case 0: // Null + res += `\0` + case 7: // Bell + res += `\b` + case '\t': // 9 + res += `\t` + case '\n': // 10 + res += `\n` + case '\r': // 13 + res += `\r` + case 26: // ctrl-z / Substitute + res += `\Z` + default: + if !strconv.IsPrint(runeVal) { + isPrint = false + break + } + res += string(runeVal) + } + } + if isPrint { + return res + } + // Not possible to create an easy interpreted MySQL string, return as hex string + // Can be converted to string in MySQL like: CAST(UNHEX('') AS CHAR(255)) + return "0x" + hex.EncodeToString([]byte(driver.UnwrapFromSingleQuotes(s))) +} + +func writeColumnListToBuffer(partitionInfo *model.PartitionInfo, sqlMode mysql.SQLMode, buf *bytes.Buffer) { + for i, col := range partitionInfo.Columns { + buf.WriteString(stringutil.Escape(col.O, sqlMode)) + if i < len(partitionInfo.Columns)-1 { + buf.WriteString(",") + } + } +} + +// AppendPartitionInfo is used in SHOW CREATE TABLE as well as generation the SQL syntax +// for the PartitionInfo during validation of various DDL commands +func AppendPartitionInfo(partitionInfo *model.PartitionInfo, buf *bytes.Buffer, sqlMode mysql.SQLMode) { + if partitionInfo == nil { + return + } + // Since MySQL 5.1/5.5 is very old and TiDB aims for 5.7/8.0 compatibility, we will not + // include the /*!50100 or /*!50500 comments for TiDB. + // This also solves the issue with comments within comments that would happen for + // PLACEMENT POLICY options. + defaultPartitionDefinitions := true + if partitionInfo.Type == model.PartitionTypeHash || + partitionInfo.Type == model.PartitionTypeKey { + for i, def := range partitionInfo.Definitions { + if def.Name.O != fmt.Sprintf("p%d", i) { + defaultPartitionDefinitions = false + break + } + if len(def.Comment) > 0 || def.PlacementPolicyRef != nil { + defaultPartitionDefinitions = false + break + } + } + + if defaultPartitionDefinitions { + if partitionInfo.Type == model.PartitionTypeHash { + fmt.Fprintf(buf, "\nPARTITION BY HASH (%s) PARTITIONS %d", partitionInfo.Expr, partitionInfo.Num) + } else { + buf.WriteString("\nPARTITION BY KEY (") + writeColumnListToBuffer(partitionInfo, sqlMode, buf) + buf.WriteString(")") + fmt.Fprintf(buf, " PARTITIONS %d", partitionInfo.Num) + } + return + } + } + // this if statement takes care of lists/range/key columns case + if len(partitionInfo.Columns) > 0 { + // partitionInfo.Type == model.PartitionTypeRange || partitionInfo.Type == model.PartitionTypeList + // || partitionInfo.Type == model.PartitionTypeKey + // Notice that MySQL uses two spaces between LIST and COLUMNS... + if partitionInfo.Type == model.PartitionTypeKey { + fmt.Fprintf(buf, "\nPARTITION BY %s (", partitionInfo.Type.String()) + } else { + fmt.Fprintf(buf, "\nPARTITION BY %s COLUMNS(", partitionInfo.Type.String()) + } + writeColumnListToBuffer(partitionInfo, sqlMode, buf) + buf.WriteString(")\n(") + } else { + fmt.Fprintf(buf, "\nPARTITION BY %s (%s)\n(", partitionInfo.Type.String(), partitionInfo.Expr) + } + + AppendPartitionDefs(partitionInfo, buf, sqlMode) + buf.WriteString(")") +} + +// AppendPartitionDefs generates a list of partition definitions needed for SHOW CREATE TABLE (in executor/show.go) +// as well as needed for generating the ADD PARTITION query for INTERVAL partitioning of ALTER TABLE t LAST PARTITION +// and generating the CREATE TABLE query from CREATE TABLE ... INTERVAL +func AppendPartitionDefs(partitionInfo *model.PartitionInfo, buf *bytes.Buffer, sqlMode mysql.SQLMode) { + for i, def := range partitionInfo.Definitions { + if i > 0 { + fmt.Fprintf(buf, ",\n ") + } + fmt.Fprintf(buf, "PARTITION %s", stringutil.Escape(def.Name.O, sqlMode)) + // PartitionTypeHash and PartitionTypeKey do not have any VALUES definition + if partitionInfo.Type == model.PartitionTypeRange { + lessThans := make([]string, len(def.LessThan)) + for idx, v := range def.LessThan { + lessThans[idx] = hexIfNonPrint(v) + } + fmt.Fprintf(buf, " VALUES LESS THAN (%s)", strings.Join(lessThans, ",")) + } else if partitionInfo.Type == model.PartitionTypeList { + if len(def.InValues) == 0 { + fmt.Fprintf(buf, " DEFAULT") + } else if len(def.InValues) == 1 && + len(def.InValues[0]) == 1 && + strings.EqualFold(def.InValues[0][0], "DEFAULT") { + fmt.Fprintf(buf, " DEFAULT") + } else { + values := bytes.NewBuffer(nil) + for j, inValues := range def.InValues { + if j > 0 { + values.WriteString(",") + } + if len(inValues) > 1 { + values.WriteString("(") + tmpVals := make([]string, len(inValues)) + for idx, v := range inValues { + tmpVals[idx] = hexIfNonPrint(v) + } + values.WriteString(strings.Join(tmpVals, ",")) + values.WriteString(")") + } else if len(inValues) == 1 { + values.WriteString(hexIfNonPrint(inValues[0])) + } + } + fmt.Fprintf(buf, " VALUES IN (%s)", values.String()) + } + } + if len(def.Comment) > 0 { + fmt.Fprintf(buf, " COMMENT '%s'", format.OutputFormat(def.Comment)) + } + if def.PlacementPolicyRef != nil { + // add placement ref info here + fmt.Fprintf(buf, " /*T![placement] PLACEMENT POLICY=%s */", stringutil.Escape(def.PlacementPolicyRef.Name.O, sqlMode)) + } + } +} diff --git a/pkg/ddl/partition_test.go b/pkg/ddl/partition_test.go new file mode 100644 index 0000000000000..bbfa6b93c7abd --- /dev/null +++ b/pkg/ddl/partition_test.go @@ -0,0 +1,248 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestDropAndTruncatePartition(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + dbInfo, err := testSchemaInfo(store, "test_partition") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) + // generate 5 partition in tableInfo. + tblInfo, partIDs := buildTableInfoWithPartition(t, store) + ctx := testkit.NewTestKit(t, store).Session() + testCreateTable(t, ctx, d, dbInfo, tblInfo) + testDropPartition(t, ctx, d, dbInfo, tblInfo, []string{"p0", "p1"}) + + newIDs, err := genGlobalIDs(store, 2) + require.NoError(t, err) + testTruncatePartition(t, ctx, d, dbInfo, tblInfo, []int64{partIDs[3], partIDs[4]}, newIDs) +} + +func buildTableInfoWithPartition(t *testing.T, store kv.Storage) (*model.TableInfo, []int64) { + tbl := &model.TableInfo{ + Name: model.NewCIStr("t"), + } + tbl.MaxColumnID++ + col := &model.ColumnInfo{ + Name: model.NewCIStr("c"), + Offset: 0, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLong), + ID: tbl.MaxColumnID, + } + genIDs, err := genGlobalIDs(store, 1) + require.NoError(t, err) + tbl.ID = genIDs[0] + tbl.Columns = []*model.ColumnInfo{col} + tbl.Charset = "utf8" + tbl.Collate = "utf8_bin" + + partIDs, err := genGlobalIDs(store, 5) + require.NoError(t, err) + partInfo := &model.PartitionInfo{ + Type: model.PartitionTypeRange, + Expr: tbl.Columns[0].Name.L, + Enable: true, + Definitions: []model.PartitionDefinition{ + { + ID: partIDs[0], + Name: model.NewCIStr("p0"), + LessThan: []string{"100"}, + }, + { + ID: partIDs[1], + Name: model.NewCIStr("p1"), + LessThan: []string{"200"}, + }, + { + ID: partIDs[2], + Name: model.NewCIStr("p2"), + LessThan: []string{"300"}, + }, + { + ID: partIDs[3], + Name: model.NewCIStr("p3"), + LessThan: []string{"400"}, + }, + { + ID: partIDs[4], + Name: model.NewCIStr("p4"), + LessThan: []string{"500"}, + }, + }, + } + tbl.Partition = partInfo + return tbl, partIDs +} + +func buildDropPartitionJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, partNames []string) *model.Job { + return &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + SchemaState: model.StatePublic, + Type: model.ActionDropTablePartition, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{partNames}, + } +} + +func testDropPartition(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, partNames []string) *model.Job { + job := buildDropPartitionJob(dbInfo, tblInfo, partNames) + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + return job +} + +func buildTruncatePartitionJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, pids []int64, newIDs []int64) *model.Job { + return &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionTruncateTablePartition, + SchemaState: model.StatePublic, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{pids, newIDs}, + } +} + +func testTruncatePartition(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo, pids []int64, newIDs []int64) *model.Job { + job := buildTruncatePartitionJob(dbInfo, tblInfo, pids, newIDs) + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + return job +} + +func TestReorganizePartitionRollback(t *testing.T) { + // See issue: https://github.com/pingcap/tidb/issues/42448 + store, do := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE TABLE `t1` (\n" + + " `id` bigint(20) NOT NULL AUTO_INCREMENT,\n" + + " `k` int(11) NOT NULL DEFAULT '0',\n" + + " `c` char(120) NOT NULL DEFAULT '',\n" + + " `pad` char(60) NOT NULL DEFAULT '',\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `k_1` (`k`)\n" + + " ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + " PARTITION BY RANGE (`id`)\n" + + " (PARTITION `p0` VALUES LESS THAN (2000000),\n" + + " PARTITION `p1` VALUES LESS THAN (4000000),\n" + + " PARTITION `p2` VALUES LESS THAN (6000000),\n" + + " PARTITION `p3` VALUES LESS THAN (8000000),\n" + + " PARTITION `p4` VALUES LESS THAN (10000000),\n" + + " PARTITION `p5` VALUES LESS THAN (MAXVALUE))") + tk.MustExec("insert into t1(k, c, pad) values (1, 'a', 'beijing'), (2, 'b', 'chengdu')") + + wait := make(chan struct{}) + defer close(wait) + ddlDone := make(chan error) + defer close(ddlDone) + hook := &callback.TestDDLCallback{Do: do} + hook.OnJobRunAfterExported = func(job *model.Job) { + if job.Type == model.ActionReorganizePartition && job.SchemaState == model.StateWriteReorganization { + <-wait + <-wait + } + } + do.DDL().SetHook(hook) + + go func() { + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + err := tk2.ExecToErr("alter table t1 reorganize partition p0, p1, p2, p3, p4 into( partition pnew values less than (10000000))") + ddlDone <- err + }() + + jobID := "" + + // wait DDL job reaches hook and then cancel + select { + case wait <- struct{}{}: + rows := tk.MustQuery("admin show ddl jobs where JOB_TYPE='alter table reorganize partition'").Rows() + require.Equal(t, 1, len(rows)) + jobID = rows[0][0].(string) + tk.MustExec("admin cancel ddl jobs " + jobID) + case <-time.After(time.Minute): + require.FailNow(t, "timeout") + } + + // continue to run DDL + select { + case wait <- struct{}{}: + case <-time.After(time.Minute): + require.FailNow(t, "timeout") + } + + // wait ddl done + select { + case err := <-ddlDone: + require.Error(t, err) + case <-time.After(time.Minute): + require.FailNow(t, "wait ddl cancelled timeout") + } + + // check job rollback finished + rows := tk.MustQuery("admin show ddl jobs where JOB_ID=" + jobID).Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "rollback done", rows[0][len(rows[0])-1]) + + // check table meta after rollback + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `id` bigint(20) NOT NULL AUTO_INCREMENT,\n" + + " `k` int(11) NOT NULL DEFAULT '0',\n" + + " `c` char(120) NOT NULL DEFAULT '',\n" + + " `pad` char(60) NOT NULL DEFAULT '',\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `k_1` (`k`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=5001\n" + + "PARTITION BY RANGE (`id`)\n" + + "(PARTITION `p0` VALUES LESS THAN (2000000),\n" + + " PARTITION `p1` VALUES LESS THAN (4000000),\n" + + " PARTITION `p2` VALUES LESS THAN (6000000),\n" + + " PARTITION `p3` VALUES LESS THAN (8000000),\n" + + " PARTITION `p4` VALUES LESS THAN (10000000),\n" + + " PARTITION `p5` VALUES LESS THAN (MAXVALUE))")) + tbl, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + require.NotNil(t, tbl.Meta().Partition) + require.Nil(t, tbl.Meta().Partition.AddingDefinitions) + require.Nil(t, tbl.Meta().Partition.DroppingDefinitions) + + // test then add index should success + tk.MustExec("alter table t1 add index idx_kc (k, c)") +} diff --git a/pkg/ddl/placement/BUILD.bazel b/pkg/ddl/placement/BUILD.bazel new file mode 100644 index 0000000000000..894d93ab0a3c8 --- /dev/null +++ b/pkg/ddl/placement/BUILD.bazel @@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "placement", + srcs = [ + "bundle.go", + "common.go", + "constraint.go", + "constraints.go", + "errors.go", + "rule.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/placement", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/model", + "//pkg/tablecodec", + "//pkg/util/codec", + "@com_github_pingcap_failpoint//:failpoint", + "@in_gopkg_yaml_v2//:yaml_v2", + ], +) + +go_test( + name = "placement_test", + timeout = "short", + srcs = [ + "bundle_test.go", + "common_test.go", + "constraint_test.go", + "constraints_test.go", + "meta_bundle_test.go", + "rule_test.go", + ], + embed = [":placement"], + flaky = True, + race = "on", + shard_count = 25, + deps = [ + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/util/codec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + ], +) diff --git a/ddl/placement/bundle.go b/pkg/ddl/placement/bundle.go similarity index 99% rename from ddl/placement/bundle.go rename to pkg/ddl/placement/bundle.go index 6f4c564e7dbf8..110a39f397ca3 100644 --- a/ddl/placement/bundle.go +++ b/pkg/ddl/placement/bundle.go @@ -26,9 +26,9 @@ import ( "strings" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "gopkg.in/yaml.v2" ) diff --git a/ddl/placement/bundle_test.go b/pkg/ddl/placement/bundle_test.go similarity index 99% rename from ddl/placement/bundle_test.go rename to pkg/ddl/placement/bundle_test.go index 01f8600830a3a..960a37b9236ec 100644 --- a/ddl/placement/bundle_test.go +++ b/pkg/ddl/placement/bundle_test.go @@ -21,9 +21,9 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) @@ -350,9 +350,9 @@ func TestString(t *testing.T) { require.Equal(t, "{\"group_id\":\"TiDB_DDL_1\",\"group_index\":0,\"group_override\":false,\"rules\":[{\"group_id\":\"\",\"id\":\"\",\"start_key\":\"\",\"end_key\":\"\",\"role\":\"voter\",\"count\":3,\"label_constraints\":[{\"key\":\"zone\",\"op\":\"in\",\"values\":[\"sh\"]}]},{\"group_id\":\"\",\"id\":\"\",\"start_key\":\"\",\"end_key\":\"\",\"role\":\"voter\",\"count\":4,\"label_constraints\":[{\"key\":\"zone\",\"op\":\"notIn\",\"values\":[\"sh\"]},{\"key\":\"zone\",\"op\":\"in\",\"values\":[\"bj\"]}]}]}", bundle.String()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/placement/MockMarshalFailure", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/placement/MockMarshalFailure", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/placement/MockMarshalFailure")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/placement/MockMarshalFailure")) }() require.Equal(t, "", bundle.String()) } diff --git a/ddl/placement/common.go b/pkg/ddl/placement/common.go similarity index 100% rename from ddl/placement/common.go rename to pkg/ddl/placement/common.go diff --git a/ddl/placement/common_test.go b/pkg/ddl/placement/common_test.go similarity index 100% rename from ddl/placement/common_test.go rename to pkg/ddl/placement/common_test.go diff --git a/ddl/placement/constraint.go b/pkg/ddl/placement/constraint.go similarity index 100% rename from ddl/placement/constraint.go rename to pkg/ddl/placement/constraint.go diff --git a/ddl/placement/constraint_test.go b/pkg/ddl/placement/constraint_test.go similarity index 100% rename from ddl/placement/constraint_test.go rename to pkg/ddl/placement/constraint_test.go diff --git a/ddl/placement/constraints.go b/pkg/ddl/placement/constraints.go similarity index 100% rename from ddl/placement/constraints.go rename to pkg/ddl/placement/constraints.go diff --git a/ddl/placement/constraints_test.go b/pkg/ddl/placement/constraints_test.go similarity index 100% rename from ddl/placement/constraints_test.go rename to pkg/ddl/placement/constraints_test.go diff --git a/ddl/placement/errors.go b/pkg/ddl/placement/errors.go similarity index 100% rename from ddl/placement/errors.go rename to pkg/ddl/placement/errors.go diff --git a/ddl/placement/meta_bundle_test.go b/pkg/ddl/placement/meta_bundle_test.go similarity index 97% rename from ddl/placement/meta_bundle_test.go rename to pkg/ddl/placement/meta_bundle_test.go index 4a2d6f645b136..093a3651e7f23 100644 --- a/ddl/placement/meta_bundle_test.go +++ b/pkg/ddl/placement/meta_bundle_test.go @@ -21,13 +21,13 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/pkg/ddl/placement/rule.go b/pkg/ddl/placement/rule.go new file mode 100644 index 0000000000000..f21588b664682 --- /dev/null +++ b/pkg/ddl/placement/rule.go @@ -0,0 +1,263 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package placement + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "regexp" + "strings" + + "github.com/pingcap/tidb/pkg/util/codec" + "gopkg.in/yaml.v2" +) + +// PeerRoleType is the expected peer type of the placement rule. +type PeerRoleType string + +const ( + // Voter can either match a leader peer or follower peer. + Voter PeerRoleType = "voter" + // Leader matches a leader. + Leader PeerRoleType = "leader" + // Follower matches a follower. + Follower PeerRoleType = "follower" + // Learner matches a learner. + Learner PeerRoleType = "learner" +) + +const ( + attributePrefix = "#" + // AttributeEvictLeader is used to evict leader from a store. + attributeEvictLeader = "evict-leader" +) + +// RuleGroupConfig defines basic config of rule group +type RuleGroupConfig struct { + ID string `json:"id"` + Index int `json:"index"` + Override bool `json:"override"` +} + +// Rule is the core placement rule struct. Check https://github.com/tikv/pd/blob/master/server/schedule/placement/rule.go. +type Rule struct { + GroupID string `json:"group_id"` + ID string `json:"id"` + Index int `json:"index,omitempty"` + Override bool `json:"override,omitempty"` + StartKeyHex string `json:"start_key"` + EndKeyHex string `json:"end_key"` + Role PeerRoleType `json:"role"` + Count int `json:"count"` + Constraints Constraints `json:"label_constraints,omitempty"` + LocationLabels []string `json:"location_labels,omitempty"` +} + +var _ json.Marshaler = (*TiFlashRule)(nil) +var _ json.Unmarshaler = (*TiFlashRule)(nil) + +// TiFlashRule extends Rule with other necessary fields. +type TiFlashRule struct { + GroupID string + ID string + Index int + Override bool + Role PeerRoleType + Count int + Constraints Constraints + LocationLabels []string + IsolationLevel string + StartKey []byte + EndKey []byte +} + +type tiFlashRule struct { + GroupID string `json:"group_id"` + ID string `json:"id"` + Index int `json:"index,omitempty"` + Override bool `json:"override,omitempty"` + Role PeerRoleType `json:"role"` + Count int `json:"count"` + Constraints Constraints `json:"label_constraints,omitempty"` + LocationLabels []string `json:"location_labels,omitempty"` + IsolationLevel string `json:"isolation_level,omitempty"` + StartKeyHex string `json:"start_key"` + EndKeyHex string `json:"end_key"` +} + +// MarshalJSON implements json.Marshaler interface for TiFlashRule. +func (r *TiFlashRule) MarshalJSON() ([]byte, error) { + return json.Marshal(&tiFlashRule{ + GroupID: r.GroupID, + ID: r.ID, + Index: r.Index, + Override: r.Override, + Role: r.Role, + Count: r.Count, + Constraints: r.Constraints, + LocationLabels: r.LocationLabels, + IsolationLevel: r.IsolationLevel, + StartKeyHex: hex.EncodeToString(codec.EncodeBytes(nil, r.StartKey)), + EndKeyHex: hex.EncodeToString(codec.EncodeBytes(nil, r.EndKey)), + }) +} + +// UnmarshalJSON implements json.Unmarshaler interface for TiFlashRule. +func (r *TiFlashRule) UnmarshalJSON(bytes []byte) error { + var rule tiFlashRule + if err := json.Unmarshal(bytes, &rule); err != nil { + return err + } + *r = TiFlashRule{ + GroupID: rule.GroupID, + ID: rule.ID, + Index: rule.Index, + Override: rule.Override, + Role: rule.Role, + Count: rule.Count, + Constraints: rule.Constraints, + LocationLabels: rule.LocationLabels, + IsolationLevel: rule.IsolationLevel, + } + + startKey, err := hex.DecodeString(rule.StartKeyHex) + if err != nil { + return err + } + + endKey, err := hex.DecodeString(rule.EndKeyHex) + if err != nil { + return err + } + + _, r.StartKey, err = codec.DecodeBytes(startKey, nil) + if err != nil { + return err + } + + _, r.EndKey, err = codec.DecodeBytes(endKey, nil) + + return err +} + +// NewRule constructs *Rule from role, count, and constraints. It is here to +// consistent the behavior of creating new rules. +func NewRule(role PeerRoleType, replicas uint64, cnst Constraints) *Rule { + return &Rule{ + Role: role, + Count: int(replicas), + Constraints: cnst, + } +} + +var wrongSeparatorRegexp = regexp.MustCompile(`[^"':]+:\d`) + +func getYamlMapFormatError(str string) error { + if !strings.Contains(str, ":") { + return ErrInvalidConstraintsMappingNoColonFound + } + if wrongSeparatorRegexp.MatchString(str) { + return ErrInvalidConstraintsMappingWrongSeparator + } + return nil +} + +// NewRules constructs []*Rule from a yaml-compatible representation of +// 'array' or 'dict' constraints. +// Refer to https://github.com/pingcap/tidb/blob/master/docs/design/2020-06-24-placement-rules-in-sql.md. +func NewRules(role PeerRoleType, replicas uint64, cnstr string) (rules []*Rule, err error) { + cnstbytes := []byte(cnstr) + constraints1, err1 := NewConstraintsFromYaml(cnstbytes) + if err1 == nil { + if replicas == 0 { + if len(cnstr) > 0 { + return nil, fmt.Errorf("%w: count of replicas should be positive, but got %d, constraint %s", ErrInvalidConstraintsReplicas, replicas, cnstr) + } + return nil, nil + } + rules = append(rules, NewRule(role, replicas, constraints1)) + err = err1 + return + } + // check if is dict constraints + constraints2 := map[string]int{} + if err2 := yaml.UnmarshalStrict(cnstbytes, &constraints2); err2 != nil { + err = fmt.Errorf("%w: should be [constraint1, ...] (error %s), {constraint1: cnt1, ...} (error %s), or any yaml compatible representation", ErrInvalidConstraintsFormat, err1, err2) + return + } + + rules, err = NewRulesWithDictConstraints(role, cnstr) + // check if replicas is consistent + if err == nil { + totalCnt := 0 + for _, rule := range rules { + totalCnt += rule.Count + } + if replicas != 0 && replicas != uint64(totalCnt) { + err = fmt.Errorf("%w: count of replicas in dict constrains is %d, but got %d", ErrInvalidConstraintsReplicas, totalCnt, replicas) + } + } + return +} + +// NewRulesWithDictConstraints constructs []*Rule from a yaml-compatible representation of +// 'dict' constraints. +func NewRulesWithDictConstraints(role PeerRoleType, cnstr string) ([]*Rule, error) { + rules := []*Rule{} + cnstbytes := []byte(cnstr) + constraints2 := map[string]int{} + err2 := yaml.UnmarshalStrict(cnstbytes, &constraints2) + if err2 == nil { + for labels, cnt := range constraints2 { + if cnt <= 0 { + if err := getYamlMapFormatError(string(cnstbytes)); err != nil { + return rules, err + } + return rules, fmt.Errorf("%w: count of labels '%s' should be positive, but got %d", ErrInvalidConstraintsMapcnt, labels, cnt) + } + } + + for labels, cnt := range constraints2 { + lbs, overrideRole, err := preCheckDictConstraintStr(labels, role) + if err != nil { + return rules, err + } + labelConstraints, err := NewConstraints(lbs) + if err != nil { + return rules, err + } + if cnt == 0 { + return nil, fmt.Errorf("%w: count of replicas should be positive, but got %d", ErrInvalidConstraintsReplicas, cnt) + } + rules = append(rules, NewRule(overrideRole, uint64(cnt), labelConstraints)) + } + return rules, nil + } + + return nil, fmt.Errorf("%w: should be [constraint1, ...] or {constraint1: cnt1, ...}, error %s, or any yaml compatible representation", ErrInvalidConstraintsFormat, err2) +} + +// Clone is used to duplicate a RuleOp for safe modification. +// Note that it is a shallow copy: Constraints is not cloned. +func (r *Rule) Clone() *Rule { + n := &Rule{} + *n = *r + return n +} + +func (r *Rule) String() string { + return fmt.Sprintf("%+v", *r) +} diff --git a/ddl/placement/rule_test.go b/pkg/ddl/placement/rule_test.go similarity index 100% rename from ddl/placement/rule_test.go rename to pkg/ddl/placement/rule_test.go diff --git a/ddl/placement_policy.go b/pkg/ddl/placement_policy.go similarity index 98% rename from ddl/placement_policy.go rename to pkg/ddl/placement_policy.go index bd155bcc3fb0a..4ab6495754f12 100644 --- a/ddl/placement_policy.go +++ b/pkg/ddl/placement_policy.go @@ -20,12 +20,12 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/dbterror" ) func onCreatePlacementPolicy(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { diff --git a/ddl/placement_policy_ddl_test.go b/pkg/ddl/placement_policy_ddl_test.go similarity index 94% rename from ddl/placement_policy_ddl_test.go rename to pkg/ddl/placement_policy_ddl_test.go index 0c0793e711d8e..f25aa4be0419b 100644 --- a/ddl/placement_policy_ddl_test.go +++ b/pkg/ddl/placement_policy_ddl_test.go @@ -17,14 +17,14 @@ import ( "context" "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) diff --git a/ddl/placement_policy_test.go b/pkg/ddl/placement_policy_test.go similarity index 97% rename from ddl/placement_policy_test.go rename to pkg/ddl/placement_policy_test.go index 14a4f52c3bd38..efa873e0b69de 100644 --- a/ddl/placement_policy_test.go +++ b/pkg/ddl/placement_policy_test.go @@ -23,21 +23,21 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" "github.com/stretchr/testify/require" ) @@ -1272,9 +1272,9 @@ func TestDatabasePlacement(t *testing.T) { func TestDropDatabaseGCPlacement(t *testing.T) { // clearAllBundles(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -1331,9 +1331,9 @@ func TestDropDatabaseGCPlacement(t *testing.T) { func TestDropTableGCPlacement(t *testing.T) { // clearAllBundles(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -1462,9 +1462,9 @@ func TestAlterTablePlacement(t *testing.T) { func TestDropTablePartitionGCPlacement(t *testing.T) { // clearAllBundles(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -1710,9 +1710,9 @@ func TestAddPartitionWithPlacement(t *testing.T) { } func TestTruncateTableWithPlacement(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -1837,9 +1837,9 @@ func TestTruncateTableWithPlacement(t *testing.T) { } func TestTruncateTablePartitionWithPlacement(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -1952,9 +1952,9 @@ func TestTruncateTablePartitionWithPlacement(t *testing.T) { } func TestDropTableWithPlacement(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -2006,9 +2006,9 @@ func TestDropTableWithPlacement(t *testing.T) { } func TestDropPartitionWithPlacement(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { @@ -2189,7 +2189,7 @@ func TestExchangePartitionWithPlacement(t *testing.T) { func TestPDFail(t *testing.T) { defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError")) }() store := testkit.CreateMockStore(t) // clearAllBundles(t) @@ -2216,7 +2216,7 @@ func TestPDFail(t *testing.T) { existBundles, err := infosync.GetAllRuleBundles(context.TODO()) require.NoError(t, err) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError", "return(true)")) // alter policy err = tk.ExecToErr("alter placement policy p1 primary_region='rx' regions='rx'") @@ -2280,9 +2280,9 @@ func TestPDFail(t *testing.T) { func TestRecoverTableWithPlacementPolicy(t *testing.T) { // clearAllBundles(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func(originGC bool) { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) if originGC { util.EmulatorGCEnable() } else { diff --git a/ddl/placement_sql_test.go b/pkg/ddl/placement_sql_test.go similarity index 98% rename from ddl/placement_sql_test.go rename to pkg/ddl/placement_sql_test.go index afc2cfdd4fe79..f8432c14e421b 100644 --- a/ddl/placement_sql_test.go +++ b/pkg/ddl/placement_sql_test.go @@ -19,13 +19,13 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -597,9 +597,9 @@ func checkTiflashReplicaSet(t *testing.T, do *domain.Domain, db, tb string, cnt func TestPlacementTiflashCheck(t *testing.T) { store, do := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) }() diff --git a/ddl/primary_key_handle_test.go b/pkg/ddl/primary_key_handle_test.go similarity index 96% rename from ddl/primary_key_handle_test.go rename to pkg/ddl/primary_key_handle_test.go index f49e3c3bba825..6dd3a76108152 100644 --- a/ddl/primary_key_handle_test.go +++ b/pkg/ddl/primary_key_handle_test.go @@ -20,16 +20,16 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" ) diff --git a/pkg/ddl/reorg.go b/pkg/ddl/reorg.go new file mode 100644 index 0000000000000..909f2d414a9ab --- /dev/null +++ b/pkg/ddl/reorg.go @@ -0,0 +1,896 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "encoding/hex" + "fmt" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/ingest" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tipb/go-tipb" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +// reorgCtx is for reorganization. +type reorgCtx struct { + // doneCh is used to notify. + // If the reorganization job is done, we will use this channel to notify outer. + // TODO: Now we use goroutine to simulate reorganization jobs, later we may + // use a persistent job list. + doneCh chan error + // rowCount is used to simulate a job's row count. + rowCount int64 + jobState model.JobState + + mu struct { + sync.Mutex + // warnings are used to store the warnings when doing the reorg job under certain SQL modes. + warnings map[errors.ErrorID]*terror.Error + warningsCount map[errors.ErrorID]int64 + } + + references atomicutil.Int32 +} + +// newContext gets a context. It is only used for adding column in reorganization state. +func newContext(store kv.Storage) sessionctx.Context { + c := mock.NewContext() + c.Store = store + c.GetSessionVars().SetStatusFlag(mysql.ServerStatusAutocommit, false) + + tz := *time.UTC + c.GetSessionVars().TimeZone = &tz + c.GetSessionVars().StmtCtx.SetTimeZone(&tz) + return c +} + +const defaultWaitReorgTimeout = 10 * time.Second + +// ReorgWaitTimeout is the timeout that wait ddl in write reorganization stage. +var ReorgWaitTimeout = 5 * time.Second + +func (rc *reorgCtx) notifyJobState(state model.JobState) { + atomic.StoreInt32((*int32)(&rc.jobState), int32(state)) +} + +func (rc *reorgCtx) isReorgCanceled() bool { + return int32(model.JobStateCancelled) == atomic.LoadInt32((*int32)(&rc.jobState)) || + int32(model.JobStateCancelling) == atomic.LoadInt32((*int32)(&rc.jobState)) +} + +func (rc *reorgCtx) isReorgPaused() bool { + return int32(model.JobStatePaused) == atomic.LoadInt32((*int32)(&rc.jobState)) || + int32(model.JobStatePausing) == atomic.LoadInt32((*int32)(&rc.jobState)) +} + +func (rc *reorgCtx) setRowCount(count int64) { + atomic.StoreInt64(&rc.rowCount, count) +} + +func (rc *reorgCtx) mergeWarnings(warnings map[errors.ErrorID]*terror.Error, warningsCount map[errors.ErrorID]int64) { + if len(warnings) == 0 || len(warningsCount) == 0 { + return + } + rc.mu.Lock() + defer rc.mu.Unlock() + rc.mu.warnings, rc.mu.warningsCount = mergeWarningsAndWarningsCount(warnings, rc.mu.warnings, warningsCount, rc.mu.warningsCount) +} + +func (rc *reorgCtx) resetWarnings() { + rc.mu.Lock() + defer rc.mu.Unlock() + rc.mu.warnings = make(map[errors.ErrorID]*terror.Error) + rc.mu.warningsCount = make(map[errors.ErrorID]int64) +} + +func (rc *reorgCtx) increaseRowCount(count int64) { + atomic.AddInt64(&rc.rowCount, count) +} + +func (rc *reorgCtx) getRowCount() int64 { + row := atomic.LoadInt64(&rc.rowCount) + return row +} + +// runReorgJob is used as a portal to do the reorganization work. +// eg: +// 1: add index +// 2: alter column type +// 3: clean global index +// 4: reorganize partitions +/* + ddl goroutine >---------+ + ^ | + | | + | | + | | <---(doneCh)--- f() + HandleDDLQueue(...) | <---(regular timeout) + | | <---(ctx done) + | | + | | + A more ddl round <-----+ +*/ +// How can we cancel reorg job? +// +// The background reorg is continuously running except for several factors, for instances, ddl owner change, +// logic error (kv duplicate when insert index / cast error when alter column), ctx done, and cancel signal. +// +// When `admin cancel ddl jobs xxx` takes effect, we will give this kind of reorg ddl one more round. +// because we should pull the result from doneCh out, otherwise, the reorg worker will hang on `f()` logic, +// which is a kind of goroutine leak. +// +// That's why we couldn't set the job to rollingback state directly in `convertJob2RollbackJob`, which is a +// cancelling portal for admin cancel action. +// +// In other words, the cancelling signal is informed from the bottom up, we set the atomic cancel variable +// in the cancelling portal to notify the lower worker goroutine, and fetch the cancel error from them in +// the additional ddl round. +// +// After that, we can make sure that the worker goroutine is correctly shut down. +func (w *worker) runReorgJob(reorgInfo *reorgInfo, tblInfo *model.TableInfo, + lease time.Duration, f func() error) error { + job := reorgInfo.Job + d := reorgInfo.d + // This is for tests compatible, because most of the early tests try to build the reorg job manually + // without reorg meta info, which will cause nil pointer in here. + if job.ReorgMeta == nil { + job.ReorgMeta = &model.DDLReorgMeta{ + SQLMode: mysql.ModeNone, + Warnings: make(map[errors.ErrorID]*terror.Error), + WarningsCount: make(map[errors.ErrorID]int64), + Location: &model.TimeZoneLocation{Name: time.UTC.String(), Offset: 0}, + } + } + + rc := w.getReorgCtx(job.ID) + if rc == nil { + // This job is cancelling, we should return ErrCancelledDDLJob directly. + // + // Q: Is there any possibility that the job is cancelling and has no reorgCtx? + // A: Yes, consider the case that : + // - we cancel the job when backfilling the last batch of data, the cancel txn is commit first, + // - and then the backfill workers send signal to the `doneCh` of the reorgCtx, + // - and then the DDL worker will remove the reorgCtx + // - and update the DDL job to `done` + // - but at the commit time, the DDL txn will raise a "write conflict" error and retry, and it happens. + if job.IsCancelling() { + return dbterror.ErrCancelledDDLJob + } + + rc = w.newReorgCtx(reorgInfo.Job.ID, reorgInfo.Job.GetRowCount()) + w.wg.Add(1) + go func() { + defer w.wg.Done() + rc.doneCh <- f() + }() + } + + waitTimeout := defaultWaitReorgTimeout + // if lease is 0, we are using a local storage, + // and we can wait the reorganization to be done here. + // if lease > 0, we don't need to wait here because + // we should update some job's progress context and try checking again, + // so we use a very little timeout here. + if lease > 0 { + waitTimeout = ReorgWaitTimeout + } + + // wait reorganization job done or timeout + select { + case err := <-rc.doneCh: + // Since job is cancelled,we don't care about its partial counts. + if rc.isReorgCanceled() || terror.ErrorEqual(err, dbterror.ErrCancelledDDLJob) { + d.removeReorgCtx(job.ID) + return dbterror.ErrCancelledDDLJob + } + rowCount := rc.getRowCount() + job.SetRowCount(rowCount) + if err != nil { + logutil.BgLogger().Warn("run reorg job done", zap.String("category", "ddl"), zap.Int64("handled rows", rowCount), zap.Error(err)) + } else { + logutil.BgLogger().Info("run reorg job done", zap.String("category", "ddl"), zap.Int64("handled rows", rowCount)) + } + + // Update a job's warnings. + w.mergeWarningsIntoJob(job) + + d.removeReorgCtx(job.ID) + + updateBackfillProgress(w, reorgInfo, tblInfo, rowCount) + + // For other errors, even err is not nil here, we still wait the partial counts to be collected. + // since in the next round, the startKey is brand new which is stored by last time. + if err != nil { + return errors.Trace(err) + } + case <-w.ctx.Done(): + logutil.BgLogger().Info("run reorg job quit", zap.String("category", "ddl")) + d.removeReorgCtx(job.ID) + // We return dbterror.ErrWaitReorgTimeout here too, so that outer loop will break. + return dbterror.ErrWaitReorgTimeout + case <-time.After(waitTimeout): + rowCount := rc.getRowCount() + job.SetRowCount(rowCount) + updateBackfillProgress(w, reorgInfo, tblInfo, rowCount) + + // Update a job's warnings. + w.mergeWarningsIntoJob(job) + + rc.resetWarnings() + + logutil.BgLogger().Info("run reorg job wait timeout", zap.String("category", "ddl"), + zap.Duration("wait time", waitTimeout), + zap.Int64("total added row count", rowCount)) + // If timeout, we will return, check the owner and retry to wait job done again. + return dbterror.ErrWaitReorgTimeout + } + return nil +} + +func overwriteReorgInfoFromGlobalCheckpoint(w *worker, sess *sess.Session, job *model.Job, reorgInfo *reorgInfo) error { + if job.ReorgMeta.ReorgTp != model.ReorgTypeLitMerge { + // Only used for the ingest mode job. + return nil + } + if reorgInfo.mergingTmpIdx { + // Merging the temporary index uses txn mode, so we don't need to consider the checkpoint. + return nil + } + if job.ReorgMeta.IsDistReorg { + // The global checkpoint is not used in distributed tasks. + return nil + } + if w.getReorgCtx(job.ID) != nil { + // We only overwrite from checkpoint when the job runs for the first time on this TiDB instance. + return nil + } + bc, ok := ingest.LitBackCtxMgr.Load(job.ID) + if ok { + // We create the checkpoint manager here because we need to wait for the reorg meta to be initialized. + if bc.GetCheckpointManager() == nil { + mgr, err := ingest.NewCheckpointManager(w.ctx, bc, w.sessPool, job.ID, reorgInfo.currElement.ID) + if err != nil { + logutil.BgLogger().Warn("create checkpoint manager failed", zap.String("category", "ddl-ingest"), zap.Error(err)) + } + bc.AttachCheckpointManager(mgr) + } + } + start, end, pid, err := getCheckpointReorgHandle(sess, job) + if err != nil { + return errors.Trace(err) + } + if pid > 0 { + reorgInfo.StartKey = start + reorgInfo.EndKey = end + reorgInfo.PhysicalTableID = pid + } + return nil +} + +func (w *worker) mergeWarningsIntoJob(job *model.Job) { + rc := w.getReorgCtx(job.ID) + rc.mu.Lock() + partWarnings := rc.mu.warnings + partWarningsCount := rc.mu.warningsCount + rc.mu.Unlock() + warnings, warningsCount := job.GetWarnings() + warnings, warningsCount = mergeWarningsAndWarningsCount(partWarnings, warnings, partWarningsCount, warningsCount) + job.SetWarnings(warnings, warningsCount) +} + +func updateBackfillProgress(w *worker, reorgInfo *reorgInfo, tblInfo *model.TableInfo, + addedRowCount int64) { + if tblInfo == nil { + return + } + progress := float64(0) + if addedRowCount != 0 { + totalCount := getTableTotalCount(w, tblInfo) + if totalCount > 0 { + progress = float64(addedRowCount) / float64(totalCount) + } else { + progress = 0 + } + if progress > 1 { + progress = 1 + } + logutil.BgLogger().Debug("update progress", zap.String("category", "ddl"), + zap.Float64("progress", progress), + zap.Int64("addedRowCount", addedRowCount), + zap.Int64("totalCount", totalCount)) + } + switch reorgInfo.Type { + case model.ActionAddIndex, model.ActionAddPrimaryKey: + var label string + if reorgInfo.mergingTmpIdx { + label = metrics.LblAddIndexMerge + } else { + label = metrics.LblAddIndex + } + metrics.GetBackfillProgressByLabel(label, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) + case model.ActionModifyColumn: + metrics.GetBackfillProgressByLabel(metrics.LblModifyColumn, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) + case model.ActionReorganizePartition, model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning: + metrics.GetBackfillProgressByLabel(metrics.LblReorgPartition, reorgInfo.SchemaName, tblInfo.Name.String()).Set(progress * 100) + } +} + +func getTableTotalCount(w *worker, tblInfo *model.TableInfo) int64 { + var ctx sessionctx.Context + ctx, err := w.sessPool.Get() + if err != nil { + return statistics.PseudoRowCount + } + defer w.sessPool.Put(ctx) + + executor, ok := ctx.(sqlexec.RestrictedSQLExecutor) + // `mock.Context` is used in tests, which doesn't implement RestrictedSQLExecutor + if !ok { + return statistics.PseudoRowCount + } + var rows []chunk.Row + if tblInfo.Partition != nil && len(tblInfo.Partition.DroppingDefinitions) > 0 { + // if Reorganize Partition, only select number of rows from the selected partitions! + defs := tblInfo.Partition.DroppingDefinitions + partIDs := make([]string, 0, len(defs)) + for _, def := range defs { + partIDs = append(partIDs, strconv.FormatInt(def.ID, 10)) + } + sql := "select sum(table_rows) from information_schema.partitions where tidb_partition_id in (%?);" + rows, _, err = executor.ExecRestrictedSQL(w.ctx, nil, sql, strings.Join(partIDs, ",")) + } else { + sql := "select table_rows from information_schema.tables where tidb_table_id=%?;" + rows, _, err = executor.ExecRestrictedSQL(w.ctx, nil, sql, tblInfo.ID) + } + if err != nil { + return statistics.PseudoRowCount + } + if len(rows) != 1 { + return statistics.PseudoRowCount + } + return rows[0].GetInt64(0) +} + +func (dc *ddlCtx) isReorgCancelled(jobID int64) bool { + return dc.getReorgCtx(jobID).isReorgCanceled() +} +func (dc *ddlCtx) isReorgPaused(jobID int64) bool { + return dc.getReorgCtx(jobID).isReorgPaused() +} + +func (dc *ddlCtx) isReorgRunnable(jobID int64, isDistReorg bool) error { + if isChanClosed(dc.ctx.Done()) { + // Worker is closed. So it can't do the reorganization. + return dbterror.ErrInvalidWorker.GenWithStack("worker is closed") + } + + if dc.isReorgCancelled(jobID) { + // Job is cancelled. So it can't be done. + return dbterror.ErrCancelledDDLJob + } + + if dc.isReorgPaused(jobID) { + logutil.BgLogger().Warn("job paused by user", zap.String("category", "ddl"), zap.String("ID", dc.uuid)) + return dbterror.ErrPausedDDLJob.GenWithStackByArgs(jobID) + } + + // If isDistReorg is true, we needn't check if it is owner. + if isDistReorg { + return nil + } + if !dc.isOwner() { + // If it's not the owner, we will try later, so here just returns an error. + logutil.BgLogger().Info("DDL is not the DDL owner", zap.String("category", "ddl"), zap.String("ID", dc.uuid)) + return errors.Trace(dbterror.ErrNotOwner) + } + return nil +} + +type reorgInfo struct { + *model.Job + + StartKey kv.Key + EndKey kv.Key + d *ddlCtx + first bool + mergingTmpIdx bool + // PhysicalTableID is used for partitioned table. + // DDL reorganize for a partitioned table will handle partitions one by one, + // PhysicalTableID is used to trace the current partition we are handling. + // If the table is not partitioned, PhysicalTableID would be TableID. + PhysicalTableID int64 + dbInfo *model.DBInfo + elements []*meta.Element + currElement *meta.Element +} + +func (r *reorgInfo) NewJobContext() *JobContext { + return r.d.jobContext(r.Job.ID, r.Job.ReorgMeta) +} + +func (r *reorgInfo) String() string { + var isEnabled bool + if ingest.LitInitialized { + _, isEnabled = ingest.LitBackCtxMgr.Load(r.Job.ID) + } + return "CurrElementType:" + string(r.currElement.TypeKey) + "," + + "CurrElementID:" + strconv.FormatInt(r.currElement.ID, 10) + "," + + "StartKey:" + hex.EncodeToString(r.StartKey) + "," + + "EndKey:" + hex.EncodeToString(r.EndKey) + "," + + "First:" + strconv.FormatBool(r.first) + "," + + "PhysicalTableID:" + strconv.FormatInt(r.PhysicalTableID, 10) + "," + + "Ingest mode:" + strconv.FormatBool(isEnabled) +} + +func constructDescTableScanPB(physicalTableID int64, tblInfo *model.TableInfo, handleCols []*model.ColumnInfo) *tipb.Executor { + tblScan := tables.BuildTableScanFromInfos(tblInfo, handleCols) + tblScan.TableId = physicalTableID + tblScan.Desc = true + return &tipb.Executor{Tp: tipb.ExecType_TypeTableScan, TblScan: tblScan} +} + +func constructLimitPB(count uint64) *tipb.Executor { + limitExec := &tipb.Limit{ + Limit: count, + } + return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} +} + +func buildDescTableScanDAG(ctx sessionctx.Context, tbl table.PhysicalTable, handleCols []*model.ColumnInfo, limit uint64) (*tipb.DAGRequest, error) { + dagReq := &tipb.DAGRequest{} + _, timeZoneOffset := time.Now().In(time.UTC).Zone() + dagReq.TimeZoneOffset = int64(timeZoneOffset) + for i := range handleCols { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) + } + dagReq.Flags |= model.FlagInSelectStmt + + tblScanExec := constructDescTableScanPB(tbl.GetPhysicalID(), tbl.Meta(), handleCols) + dagReq.Executors = append(dagReq.Executors, tblScanExec) + dagReq.Executors = append(dagReq.Executors, constructLimitPB(limit)) + distsql.SetEncodeType(ctx, dagReq) + return dagReq, nil +} + +func getColumnsTypes(columns []*model.ColumnInfo) []*types.FieldType { + colTypes := make([]*types.FieldType, 0, len(columns)) + for _, col := range columns { + colTypes = append(colTypes, &col.FieldType) + } + return colTypes +} + +// buildDescTableScan builds a desc table scan upon tblInfo. +func (dc *ddlCtx) buildDescTableScan(ctx *JobContext, startTS uint64, tbl table.PhysicalTable, + handleCols []*model.ColumnInfo, limit uint64) (distsql.SelectResult, error) { + sctx := newContext(dc.store) + dagPB, err := buildDescTableScanDAG(sctx, tbl, handleCols, limit) + if err != nil { + return nil, errors.Trace(err) + } + var b distsql.RequestBuilder + var builder *distsql.RequestBuilder + var ranges []*ranger.Range + if tbl.Meta().IsCommonHandle { + ranges = ranger.FullNotNullRange() + } else { + ranges = ranger.FullIntRange(false) + } + builder = b.SetHandleRanges(sctx.GetSessionVars().StmtCtx, tbl.GetPhysicalID(), tbl.Meta().IsCommonHandle, ranges) + builder.SetDAGRequest(dagPB). + SetStartTS(startTS). + SetKeepOrder(true). + SetConcurrency(1). + SetDesc(true). + SetResourceGroupTagger(ctx.getResourceGroupTaggerForTopSQL()). + SetResourceGroupName(ctx.resourceGroupName) + + builder.Request.NotFillCache = true + builder.Request.Priority = kv.PriorityLow + builder.RequestSource.RequestSourceInternal = true + builder.RequestSource.RequestSourceType = ctx.ddlJobSourceType() + + kvReq, err := builder.Build() + if err != nil { + return nil, errors.Trace(err) + } + + result, err := distsql.Select(ctx.ddlJobCtx, sctx, kvReq, getColumnsTypes(handleCols)) + if err != nil { + return nil, errors.Trace(err) + } + return result, nil +} + +// GetTableMaxHandle gets the max handle of a PhysicalTable. +func (dc *ddlCtx) GetTableMaxHandle(ctx *JobContext, startTS uint64, tbl table.PhysicalTable) (maxHandle kv.Handle, emptyTable bool, err error) { + var handleCols []*model.ColumnInfo + var pkIdx *model.IndexInfo + tblInfo := tbl.Meta() + switch { + case tblInfo.PKIsHandle: + for _, col := range tbl.Meta().Columns { + if mysql.HasPriKeyFlag(col.GetFlag()) { + handleCols = []*model.ColumnInfo{col} + break + } + } + case tblInfo.IsCommonHandle: + pkIdx = tables.FindPrimaryIndex(tblInfo) + cols := tblInfo.Cols() + for _, idxCol := range pkIdx.Columns { + handleCols = append(handleCols, cols[idxCol.Offset]) + } + default: + handleCols = []*model.ColumnInfo{model.NewExtraHandleColInfo()} + } + + // build a desc scan of tblInfo, which limit is 1, we can use it to retrieve the last handle of the table. + result, err := dc.buildDescTableScan(ctx, startTS, tbl, handleCols, 1) + if err != nil { + return nil, false, errors.Trace(err) + } + defer terror.Call(result.Close) + + chk := chunk.New(getColumnsTypes(handleCols), 1, 1) + err = result.Next(ctx.ddlJobCtx, chk) + if err != nil { + return nil, false, errors.Trace(err) + } + + if chk.NumRows() == 0 { + // empty table + return nil, true, nil + } + sessCtx := newContext(dc.store) + row := chk.GetRow(0) + if tblInfo.IsCommonHandle { + maxHandle, err = buildCommonHandleFromChunkRow(sessCtx.GetSessionVars().StmtCtx, tblInfo, pkIdx, handleCols, row) + return maxHandle, false, err + } + return kv.IntHandle(row.GetInt64(0)), false, nil +} + +func buildCommonHandleFromChunkRow(sctx *stmtctx.StatementContext, tblInfo *model.TableInfo, idxInfo *model.IndexInfo, + cols []*model.ColumnInfo, row chunk.Row) (kv.Handle, error) { + fieldTypes := make([]*types.FieldType, 0, len(cols)) + for _, col := range cols { + fieldTypes = append(fieldTypes, &col.FieldType) + } + datumRow := row.GetDatumRow(fieldTypes) + tablecodec.TruncateIndexValues(tblInfo, idxInfo, datumRow) + + var handleBytes []byte + handleBytes, err := codec.EncodeKey(sctx, nil, datumRow...) + if err != nil { + return nil, err + } + return kv.NewCommonHandle(handleBytes) +} + +// getTableRange gets the start and end handle of a table (or partition). +func getTableRange(ctx *JobContext, d *ddlCtx, tbl table.PhysicalTable, snapshotVer uint64, priority int) (startHandleKey, endHandleKey kv.Key, err error) { + // Get the start handle of this partition. + err = iterateSnapshotKeys(ctx, d.store, priority, tbl.RecordPrefix(), snapshotVer, nil, nil, + func(h kv.Handle, rowKey kv.Key, rawRecord []byte) (bool, error) { + startHandleKey = rowKey + return false, nil + }) + if err != nil { + return startHandleKey, endHandleKey, errors.Trace(err) + } + maxHandle, isEmptyTable, err := d.GetTableMaxHandle(ctx, snapshotVer, tbl) + if err != nil { + return startHandleKey, nil, errors.Trace(err) + } + if maxHandle != nil { + endHandleKey = tablecodec.EncodeRecordKey(tbl.RecordPrefix(), maxHandle).Next() + } + if isEmptyTable || endHandleKey.Cmp(startHandleKey) <= 0 { + logutil.BgLogger().Info("get noop table range", zap.String("category", "ddl"), + zap.String("table", fmt.Sprintf("%v", tbl.Meta())), + zap.Int64("table/partition ID", tbl.GetPhysicalID()), + zap.String("start key", hex.EncodeToString(startHandleKey)), + zap.String("end key", hex.EncodeToString(endHandleKey)), + zap.Bool("is empty table", isEmptyTable)) + if startHandleKey == nil { + endHandleKey = nil + } else { + endHandleKey = startHandleKey.Next() + } + } + return +} + +func getValidCurrentVersion(store kv.Storage) (ver kv.Version, err error) { + ver, err = store.CurrentVersion(kv.GlobalTxnScope) + if err != nil { + return ver, errors.Trace(err) + } else if ver.Ver <= 0 { + return ver, dbterror.ErrInvalidStoreVer.GenWithStack("invalid storage current version %d", ver.Ver) + } + return ver, nil +} + +func getReorgInfo(ctx *JobContext, d *ddlCtx, rh *reorgHandler, job *model.Job, dbInfo *model.DBInfo, + tbl table.Table, elements []*meta.Element, mergingTmpIdx bool) (*reorgInfo, error) { + var ( + element *meta.Element + start kv.Key + end kv.Key + pid int64 + info reorgInfo + ) + + if job.SnapshotVer == 0 { + // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. + // Third step, we need to remove the element information to make sure we can save the reorganized information to storage. + failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { + if val.(string) == "addIdxNotOwnerErr" && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 3, 4) { + if err := rh.RemoveReorgElementFailPoint(job); err != nil { + failpoint.Return(nil, errors.Trace(err)) + } + info.first = true + failpoint.Return(&info, nil) + } + }) + + info.first = true + if d.lease > 0 { // Only delay when it's not in test. + delayForAsyncCommit() + } + ver, err := getValidCurrentVersion(d.store) + if err != nil { + return nil, errors.Trace(err) + } + tblInfo := tbl.Meta() + pid = tblInfo.ID + var tb table.PhysicalTable + if pi := tblInfo.GetPartitionInfo(); pi != nil { + pid = pi.Definitions[0].ID + tb = tbl.(table.PartitionedTable).GetPartition(pid) + } else { + tb = tbl.(table.PhysicalTable) + } + if mergingTmpIdx { + start, end = tablecodec.GetTableIndexKeyRange(pid, tablecodec.TempIndexPrefix|elements[0].ID) + } else { + start, end, err = getTableRange(ctx, d, tb, ver.Ver, job.Priority) + if err != nil { + return nil, errors.Trace(err) + } + } + logutil.BgLogger().Info("job get table range", zap.String("category", "ddl"), + zap.Int64("jobID", job.ID), zap.Int64("physicalTableID", pid), + zap.String("startKey", hex.EncodeToString(start)), + zap.String("endKey", hex.EncodeToString(end))) + + failpoint.Inject("errorUpdateReorgHandle", func() (*reorgInfo, error) { + return &info, errors.New("occur an error when update reorg handle") + }) + err = rh.InitDDLReorgHandle(job, start, end, pid, elements[0]) + if err != nil { + return &info, errors.Trace(err) + } + // Update info should after data persistent. + job.SnapshotVer = ver.Ver + element = elements[0] + } else { + failpoint.Inject("MockGetIndexRecordErr", func(val failpoint.Value) { + // For the case of the old TiDB version(do not exist the element information) is upgraded to the new TiDB version. + // Second step, we need to remove the element information to make sure we can get the error of "ErrDDLReorgElementNotExist". + // However, since "txn.Reset()" will be called later, the reorganized information cannot be saved to storage. + if val.(string) == "addIdxNotOwnerErr" && atomic.CompareAndSwapUint32(&mockNotOwnerErrOnce, 2, 3) { + if err := rh.RemoveReorgElementFailPoint(job); err != nil { + failpoint.Return(nil, errors.Trace(err)) + } + } + }) + + var err error + element, start, end, pid, err = rh.GetDDLReorgHandle(job) + if err != nil { + // If the reorg element doesn't exist, this reorg info should be saved by the older TiDB versions. + // It's compatible with the older TiDB versions. + // We'll try to remove it in the next major TiDB version. + if meta.ErrDDLReorgElementNotExist.Equal(err) { + job.SnapshotVer = 0 + logutil.BgLogger().Warn("get reorg info, the element does not exist", zap.String("category", "ddl"), zap.String("job", job.String())) + if job.IsCancelling() { + return nil, nil + } + } + return &info, errors.Trace(err) + } + } + info.Job = job + info.d = d + info.StartKey = start + info.EndKey = end + info.PhysicalTableID = pid + info.currElement = element + info.elements = elements + info.mergingTmpIdx = mergingTmpIdx + info.dbInfo = dbInfo + + return &info, nil +} + +func getReorgInfoFromPartitions(ctx *JobContext, d *ddlCtx, rh *reorgHandler, job *model.Job, dbInfo *model.DBInfo, tbl table.PartitionedTable, partitionIDs []int64, elements []*meta.Element) (*reorgInfo, error) { + var ( + element *meta.Element + start kv.Key + end kv.Key + pid int64 + info reorgInfo + ) + if job.SnapshotVer == 0 { + info.first = true + if d.lease > 0 { // Only delay when it's not in test. + delayForAsyncCommit() + } + ver, err := getValidCurrentVersion(d.store) + if err != nil { + return nil, errors.Trace(err) + } + pid = partitionIDs[0] + physTbl := tbl.GetPartition(pid) + + start, end, err = getTableRange(ctx, d, physTbl, ver.Ver, job.Priority) + if err != nil { + return nil, errors.Trace(err) + } + logutil.BgLogger().Info("job get table range", zap.String("category", "ddl"), + zap.Int64("job ID", job.ID), zap.Int64("physical table ID", pid), + zap.String("start key", hex.EncodeToString(start)), + zap.String("end key", hex.EncodeToString(end))) + + err = rh.InitDDLReorgHandle(job, start, end, pid, elements[0]) + if err != nil { + return &info, errors.Trace(err) + } + // Update info should after data persistent. + job.SnapshotVer = ver.Ver + element = elements[0] + } else { + var err error + element, start, end, pid, err = rh.GetDDLReorgHandle(job) + if err != nil { + // If the reorg element doesn't exist, this reorg info should be saved by the older TiDB versions. + // It's compatible with the older TiDB versions. + // We'll try to remove it in the next major TiDB version. + if meta.ErrDDLReorgElementNotExist.Equal(err) { + job.SnapshotVer = 0 + logutil.BgLogger().Warn("get reorg info, the element does not exist", zap.String("category", "ddl"), zap.String("job", job.String())) + } + return &info, errors.Trace(err) + } + } + info.Job = job + info.d = d + info.StartKey = start + info.EndKey = end + info.PhysicalTableID = pid + info.currElement = element + info.elements = elements + info.dbInfo = dbInfo + + return &info, nil +} + +// UpdateReorgMeta creates a new transaction and updates tidb_ddl_reorg table, +// so the reorg can restart in case of issues. +func (r *reorgInfo) UpdateReorgMeta(startKey kv.Key, pool *sess.Pool) (err error) { + if startKey == nil && r.EndKey == nil { + return nil + } + sctx, err := pool.Get() + if err != nil { + return + } + defer pool.Put(sctx) + + se := sess.NewSession(sctx) + err = se.Begin() + if err != nil { + return + } + rh := newReorgHandler(se) + err = updateDDLReorgHandle(rh.s, r.Job.ID, startKey, r.EndKey, r.PhysicalTableID, r.currElement) + err1 := se.Commit() + if err == nil { + err = err1 + } + return errors.Trace(err) +} + +// reorgHandler is used to handle the reorg information duration reorganization DDL job. +type reorgHandler struct { + s *sess.Session +} + +// NewReorgHandlerForTest creates a new reorgHandler, only used in test. +func NewReorgHandlerForTest(se sessionctx.Context) *reorgHandler { + return newReorgHandler(sess.NewSession(se)) +} + +func newReorgHandler(sess *sess.Session) *reorgHandler { + return &reorgHandler{s: sess} +} + +// InitDDLReorgHandle initializes the job reorganization information. +func (r *reorgHandler) InitDDLReorgHandle(job *model.Job, startKey, endKey kv.Key, physicalTableID int64, element *meta.Element) error { + return initDDLReorgHandle(r.s, job.ID, startKey, endKey, physicalTableID, element) +} + +// RemoveReorgElementFailPoint removes the element of the reorganization information. +func (r *reorgHandler) RemoveReorgElementFailPoint(job *model.Job) error { + return removeReorgElement(r.s, job) +} + +// RemoveDDLReorgHandle removes the job reorganization related handles. +func (r *reorgHandler) RemoveDDLReorgHandle(job *model.Job, elements []*meta.Element) error { + return removeDDLReorgHandle(r.s, job, elements) +} + +// CleanupDDLReorgHandles removes the job reorganization related handles. +func CleanupDDLReorgHandles(job *model.Job, s *sess.Session) { + if job != nil && !job.IsFinished() && !job.IsSynced() { + // Job is given, but it is neither finished nor synced; do nothing + return + } + + err := cleanDDLReorgHandles(s, job) + if err != nil { + // ignore error, cleanup is not that critical + logutil.BgLogger().Warn("Failed removing the DDL reorg entry in tidb_ddl_reorg", zap.String("job", job.String()), zap.Error(err)) + } +} + +// GetDDLReorgHandle gets the latest processed DDL reorganize position. +func (r *reorgHandler) GetDDLReorgHandle(job *model.Job) (element *meta.Element, startKey, endKey kv.Key, physicalTableID int64, err error) { + return getDDLReorgHandle(r.s, job) +} diff --git a/ddl/reorg_partition_test.go b/pkg/ddl/reorg_partition_test.go similarity index 96% rename from ddl/reorg_partition_test.go rename to pkg/ddl/reorg_partition_test.go index 2a1f5d2391419..bab9b060637db 100644 --- a/ddl/reorg_partition_test.go +++ b/pkg/ddl/reorg_partition_test.go @@ -22,17 +22,17 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -547,18 +547,18 @@ func TestReorgPartitionRollback(t *testing.T) { // TODO: Check that there are no additional placement rules, // bundles, or ranges with non-completed tableIDs // (partitions used during reorg, but was dropped) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr", `return(true)`)) tk.MustExecToErr("alter table t reorganize partition p1 into (partition p1a values less than (15), partition p1b values less than (20))") tk.MustExec(`admin check table t`) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr")) ctx := tk.Session() is := domain.GetDomain(ctx).InfoSchema() tbl, err := is.TableByName(model.NewCIStr(schemaName), model.NewCIStr("t")) require.NoError(t, err) noNewTablesAfter(t, tk, ctx, tbl) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/reorgPartitionAfterDataCopy", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/reorgPartitionAfterDataCopy", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/ddl/reorgPartitionAfterDataCopy") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/reorgPartitionAfterDataCopy") require.NoError(t, err) }() tk.MustExecToErr("alter table t reorganize partition p1 into (partition p1a values less than (15), partition p1b values less than (20))") diff --git a/ddl/repair_table_test.go b/pkg/ddl/repair_table_test.go similarity index 94% rename from ddl/repair_table_test.go rename to pkg/ddl/repair_table_test.go index b951a4f61f8c8..0246b3bb6e5ad 100644 --- a/ddl/repair_table_test.go +++ b/pkg/ddl/repair_table_test.go @@ -20,23 +20,23 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/domainutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/domainutil" "github.com/stretchr/testify/require" ) const repairTableLease = 600 * time.Millisecond func TestRepairTable(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/repairFetchCreateTable", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/repairFetchCreateTable", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/repairFetchCreateTable")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/repairFetchCreateTable")) }() store, dom := testkit.CreateMockStoreAndDomainWithSchemaLease(t, repairTableLease) @@ -164,9 +164,9 @@ func turnRepairModeAndInit(on bool) { } func TestRepairTableWithPartition(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/repairFetchCreateTable", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/repairFetchCreateTable", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/repairFetchCreateTable")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/repairFetchCreateTable")) }() store := testkit.CreateMockStoreWithSchemaLease(t, repairTableLease) tk := testkit.NewTestKit(t, store) diff --git a/ddl/resource_group.go b/pkg/ddl/resource_group.go similarity index 93% rename from ddl/resource_group.go rename to pkg/ddl/resource_group.go index 463b885d50786..c3419bcd60531 100644 --- a/ddl/resource_group.go +++ b/pkg/ddl/resource_group.go @@ -20,14 +20,14 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/resourcegroup" - "github.com/pingcap/tidb/domain/infosync" - rg "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/resourcegroup" + "github.com/pingcap/tidb/pkg/domain/infosync" + rg "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/ddl/resourcegroup/BUILD.bazel b/pkg/ddl/resourcegroup/BUILD.bazel new file mode 100644 index 0000000000000..3c18dcc7a024e --- /dev/null +++ b/pkg/ddl/resourcegroup/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "resourcegroup", + srcs = [ + "errors.go", + "group.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/resourcegroup", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/model", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/resource_manager", + ], +) diff --git a/ddl/resourcegroup/errors.go b/pkg/ddl/resourcegroup/errors.go similarity index 100% rename from ddl/resourcegroup/errors.go rename to pkg/ddl/resourcegroup/errors.go diff --git a/pkg/ddl/resourcegroup/group.go b/pkg/ddl/resourcegroup/group.go new file mode 100644 index 0000000000000..18bb42a602541 --- /dev/null +++ b/pkg/ddl/resourcegroup/group.go @@ -0,0 +1,83 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package resourcegroup + +import ( + rmpb "github.com/pingcap/kvproto/pkg/resource_manager" + "github.com/pingcap/tidb/pkg/parser/model" +) + +// MaxGroupNameLength is max length of the name of a resource group +const MaxGroupNameLength = 32 + +// NewGroupFromOptions creates a new resource group from the given options. +func NewGroupFromOptions(groupName string, options *model.ResourceGroupSettings) (*rmpb.ResourceGroup, error) { + if options == nil { + return nil, ErrInvalidGroupSettings + } + if len(groupName) > MaxGroupNameLength { + return nil, ErrTooLongResourceGroupName + } + + group := &rmpb.ResourceGroup{ + Name: groupName, + } + + group.Priority = uint32(options.Priority) + if options.Runaway != nil { + runaway := &rmpb.RunawaySettings{ + Rule: &rmpb.RunawayRule{}, + } + if options.Runaway.ExecElapsedTimeMs == 0 { + return nil, ErrInvalidResourceGroupRunawayExecElapsedTime + } + runaway.Rule.ExecElapsedTimeMs = options.Runaway.ExecElapsedTimeMs + if options.Runaway.Action == model.RunawayActionNone { + return nil, ErrUnknownResourceGroupRunawayAction + } + runaway.Action = rmpb.RunawayAction(options.Runaway.Action) + if options.Runaway.WatchType != model.WatchNone { + runaway.Watch = &rmpb.RunawayWatch{} + runaway.Watch.Type = rmpb.RunawayWatchType(options.Runaway.WatchType) + runaway.Watch.LastingDurationMs = options.Runaway.WatchDurationMs + } + group.RunawaySettings = runaway + } + + if options.Background != nil { + group.BackgroundSettings = &rmpb.BackgroundSettings{ + JobTypes: options.Background.JobTypes, + } + } + + if options.RURate > 0 { + group.Mode = rmpb.GroupMode_RUMode + group.RUSettings = &rmpb.GroupRequestUnitSettings{ + RU: &rmpb.TokenBucket{ + Settings: &rmpb.TokenLimitSettings{ + FillRate: options.RURate, + BurstLimit: options.BurstLimit, + }, + }, + } + if len(options.CPULimiter) > 0 || len(options.IOReadBandwidth) > 0 || len(options.IOWriteBandwidth) > 0 { + return nil, ErrInvalidResourceGroupDuplicatedMode + } + return group, nil + } + + // Only support RU mode now + return nil, ErrUnknownResourceGroupMode +} diff --git a/ddl/restart_test.go b/pkg/ddl/restart_test.go similarity index 96% rename from ddl/restart_test.go rename to pkg/ddl/restart_test.go index fa4e21b5f1c05..075c9bb01e52d 100644 --- a/ddl/restart_test.go +++ b/pkg/ddl/restart_test.go @@ -21,12 +21,12 @@ import ( "time" "github.com/ngaut/pools" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/ddl/rollingback.go b/pkg/ddl/rollingback.go similarity index 98% rename from ddl/rollingback.go rename to pkg/ddl/rollingback.go index 3de7b27540a42..3ea1082a28b35 100644 --- a/ddl/rollingback.go +++ b/pkg/ddl/rollingback.go @@ -19,14 +19,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/dbterror" "go.uber.org/zap" ) diff --git a/ddl/rollingback_test.go b/pkg/ddl/rollingback_test.go similarity index 86% rename from ddl/rollingback_test.go rename to pkg/ddl/rollingback_test.go index 8b2836bd9b1da..bbd78be112372 100644 --- a/ddl/rollingback_test.go +++ b/pkg/ddl/rollingback_test.go @@ -21,12 +21,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) @@ -43,9 +43,9 @@ func TestCancelAddIndexJobError(t *testing.T) { tk.MustExec("insert into t_cancel_add_index values(1),(2),(3)") tk.MustExec("set @@global.tidb_ddl_error_count_limit=3") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockConvertAddIdxJob2RollbackJobError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockConvertAddIdxJob2RollbackJobError", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockConvertAddIdxJob2RollbackJobError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockConvertAddIdxJob2RollbackJobError")) }() tbl := external.GetTableByName(t, tk, "test", "t_cancel_add_index") //nolint:typecheck diff --git a/ddl/sanity_check.go b/pkg/ddl/sanity_check.go similarity index 95% rename from ddl/sanity_check.go rename to pkg/ddl/sanity_check.go index c116d124b7eac..d8108e71869cd 100644 --- a/ddl/sanity_check.go +++ b/pkg/ddl/sanity_check.go @@ -20,16 +20,16 @@ import ( "strings" "github.com/pingcap/errors" - sess "github.com/pingcap/tidb/ddl/internal/session" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sqlexec" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/pkg/ddl/schema.go b/pkg/ddl/schema.go new file mode 100644 index 0000000000000..e0425a5a9c618 --- /dev/null +++ b/pkg/ddl/schema.go @@ -0,0 +1,367 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "fmt" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" +) + +func onCreateSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + dbInfo := &model.DBInfo{} + if err := job.DecodeArgs(dbInfo); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + dbInfo.ID = schemaID + dbInfo.State = model.StateNone + + err := checkSchemaNotExists(d, t, schemaID, dbInfo) + if err != nil { + if infoschema.ErrDatabaseExists.Equal(err) { + // The database already exists, can't create it, we should cancel this job now. + job.State = model.JobStateCancelled + } + return ver, errors.Trace(err) + } + + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + + switch dbInfo.State { + case model.StateNone: + // none -> public + dbInfo.State = model.StatePublic + err = t.CreateDatabase(dbInfo) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) + return ver, nil + default: + // We can't enter here. + return ver, errors.Errorf("invalid db state %v", dbInfo.State) + } +} + +func checkSchemaNotExists(d *ddlCtx, t *meta.Meta, schemaID int64, dbInfo *model.DBInfo) error { + // Try to use memory schema info to check first. + currVer, err := t.GetSchemaVersion() + if err != nil { + return err + } + is := d.infoCache.GetLatest() + if is.SchemaMetaVersion() == currVer { + return checkSchemaNotExistsFromInfoSchema(is, schemaID, dbInfo) + } + return checkSchemaNotExistsFromStore(t, schemaID, dbInfo) +} + +func checkSchemaNotExistsFromInfoSchema(is infoschema.InfoSchema, schemaID int64, dbInfo *model.DBInfo) error { + // Check database exists by name. + if is.SchemaExists(dbInfo.Name) { + return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbInfo.Name) + } + // Check database exists by ID. + if _, ok := is.SchemaByID(schemaID); ok { + return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbInfo.Name) + } + return nil +} + +func checkSchemaNotExistsFromStore(t *meta.Meta, schemaID int64, dbInfo *model.DBInfo) error { + dbs, err := t.ListDatabases() + if err != nil { + return errors.Trace(err) + } + + for _, db := range dbs { + if db.Name.L == dbInfo.Name.L { + if db.ID != schemaID { + return infoschema.ErrDatabaseExists.GenWithStackByArgs(db.Name) + } + dbInfo = db + } + } + return nil +} + +func onModifySchemaCharsetAndCollate(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var toCharset, toCollate string + if err := job.DecodeArgs(&toCharset, &toCollate); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return ver, errors.Trace(err) + } + + if dbInfo.Charset == toCharset && dbInfo.Collate == toCollate { + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) + return ver, nil + } + + dbInfo.Charset = toCharset + dbInfo.Collate = toCollate + + if err = t.UpdateDatabase(dbInfo); err != nil { + return ver, errors.Trace(err) + } + if ver, err = updateSchemaVersion(d, t, job); err != nil { + return ver, errors.Trace(err) + } + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) + return ver, nil +} + +func onModifySchemaDefaultPlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var placementPolicyRef *model.PolicyRefInfo + if err := job.DecodeArgs(&placementPolicyRef); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return ver, errors.Trace(err) + } + // Double Check if policy exits while ddl executing + if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, placementPolicyRef); err != nil { + return ver, errors.Trace(err) + } + + // Notice: dbInfo.DirectPlacementOpts and dbInfo.PlacementPolicyRef can not be both not nil, which checked before constructing ddl job. + // So that we can just check the two situation that do not need ddl: 1. DB.DP == DDL.DP && nil == nil 2. nil == nil && DB.PP == DDL.PP + if placementPolicyRef != nil && dbInfo.PlacementPolicyRef != nil && *dbInfo.PlacementPolicyRef == *placementPolicyRef { + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) + return ver, nil + } + + // If placementPolicyRef and directPlacementOpts are both nil, And placement of dbInfo is not nil, it will remove all placement options. + dbInfo.PlacementPolicyRef = placementPolicyRef + + if err = t.UpdateDatabase(dbInfo); err != nil { + return ver, errors.Trace(err) + } + if ver, err = updateSchemaVersion(d, t, job); err != nil { + return ver, errors.Trace(err) + } + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, dbInfo) + return ver, nil +} + +func onDropSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return ver, errors.Trace(err) + } + if dbInfo.State == model.StatePublic { + err = checkDatabaseHasForeignKeyReferredInOwner(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + } + + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + switch dbInfo.State { + case model.StatePublic: + // public -> write only + dbInfo.State = model.StateWriteOnly + err = t.UpdateDatabase(dbInfo) + if err != nil { + return ver, errors.Trace(err) + } + var tables []*model.TableInfo + tables, err = t.ListTables(job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + var ruleIDs []string + for _, tblInfo := range tables { + rules := append(getPartitionRuleIDs(job.SchemaName, tblInfo), fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L)) + ruleIDs = append(ruleIDs, rules...) + } + patch := label.NewRulePatch([]*label.Rule{}, ruleIDs) + err = infosync.UpdateLabelRules(context.TODO(), patch) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + case model.StateWriteOnly: + // write only -> delete only + dbInfo.State = model.StateDeleteOnly + err = t.UpdateDatabase(dbInfo) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateDeleteOnly: + dbInfo.State = model.StateNone + var tables []*model.TableInfo + tables, err = t.ListTables(job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + err = t.UpdateDatabase(dbInfo) + if err != nil { + return ver, errors.Trace(err) + } + if err = t.DropDatabase(dbInfo.ID); err != nil { + break + } + + // Finish this job. + if len(tables) > 0 { + job.Args = append(job.Args, getIDs(tables)) + } + job.FinishDBJob(model.JobStateDone, model.StateNone, ver, dbInfo) + default: + // We can't enter here. + return ver, errors.Trace(errors.Errorf("invalid db state %v", dbInfo.State)) + } + job.SchemaState = dbInfo.State + return ver, errors.Trace(err) +} + +func (w *worker) onRecoverSchema(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var ( + recoverSchemaInfo *RecoverSchemaInfo + recoverSchemaCheckFlag int64 + ) + if err := job.DecodeArgs(&recoverSchemaInfo, &recoverSchemaCheckFlag); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + schemaInfo := recoverSchemaInfo.DBInfo + // check GC and safe point + gcEnable, err := checkGCEnable(w) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + switch schemaInfo.State { + case model.StateNone: + // none -> write only + // check GC enable and update flag. + if gcEnable { + job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagEnableGC + } else { + job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagDisableGC + } + // Clear all placement when recover + for _, recoverTabInfo := range recoverSchemaInfo.RecoverTabsInfo { + err = clearTablePlacementAndBundles(recoverTabInfo.TableInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + } + schemaInfo.State = model.StateWriteOnly + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + // write only -> public + // do recover schema and tables. + if gcEnable { + err = disableGC(w) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Errorf("disable gc failed, try again later. err: %v", err) + } + } + dbInfo := schemaInfo.Clone() + dbInfo.State = model.StatePublic + err = t.CreateDatabase(dbInfo) + if err != nil { + return ver, errors.Trace(err) + } + // check GC safe point + err = checkSafePoint(w, recoverSchemaInfo.SnapshotTS) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + for _, recoverInfo := range recoverSchemaInfo.RecoverTabsInfo { + if recoverInfo.TableInfo.TTLInfo != nil { + // force disable TTL job schedule for recovered table + recoverInfo.TableInfo.TTLInfo.Enable = false + } + ver, err = w.recoverTable(t, job, recoverInfo) + if err != nil { + return ver, errors.Trace(err) + } + } + schemaInfo.State = model.StatePublic + for _, recoverInfo := range recoverSchemaInfo.RecoverTabsInfo { + recoverInfo.TableInfo.State = model.StatePublic + recoverInfo.TableInfo.UpdateTS = t.StartTS + } + // use to update InfoSchema + job.SchemaID = schemaInfo.ID + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishDBJob(model.JobStateDone, model.StatePublic, ver, schemaInfo) + return ver, nil + default: + // We can't enter here. + return ver, errors.Errorf("invalid db state %v", schemaInfo.State) + } + return ver, errors.Trace(err) +} + +func checkSchemaExistAndCancelNotExistJob(t *meta.Meta, job *model.Job) (*model.DBInfo, error) { + dbInfo, err := t.GetDatabase(job.SchemaID) + if err != nil { + return nil, errors.Trace(err) + } + if dbInfo == nil { + job.State = model.JobStateCancelled + return nil, infoschema.ErrDatabaseDropExists.GenWithStackByArgs("") + } + return dbInfo, nil +} + +func getIDs(tables []*model.TableInfo) []int64 { + ids := make([]int64, 0, len(tables)) + for _, t := range tables { + ids = append(ids, t.ID) + if t.GetPartitionInfo() != nil { + ids = append(ids, getPartitionIDs(t)...) + } + } + + return ids +} diff --git a/pkg/ddl/schema_test.go b/pkg/ddl/schema_test.go new file mode 100644 index 0000000000000..ea93743b5cef4 --- /dev/null +++ b/pkg/ddl/schema_test.go @@ -0,0 +1,433 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func testCreateTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo) *model.Job { + job := &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionCreateTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{tblInfo}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v := getSchemaVer(t, ctx) + tblInfo.State = model.StatePublic + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + tblInfo.State = model.StateNone + return job +} + +func testCheckTableState(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo, state model.SchemaState) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + info, err := m.GetTable(dbInfo.ID, tblInfo.ID) + require.NoError(t, err) + + if state == model.StateNone { + require.NoError(t, err) + return nil + } + + require.Equal(t, info.Name, tblInfo.Name) + require.Equal(t, info.State, state) + return nil + })) +} + +// testTableInfo creates a test table with num int columns and with no index. +func testTableInfo(store kv.Storage, name string, num int) (*model.TableInfo, error) { + tblInfo := &model.TableInfo{ + Name: model.NewCIStr(name), + } + genIDs, err := genGlobalIDs(store, 1) + + if err != nil { + return nil, err + } + tblInfo.ID = genIDs[0] + + cols := make([]*model.ColumnInfo, num) + for i := range cols { + col := &model.ColumnInfo{ + Name: model.NewCIStr(fmt.Sprintf("c%d", i+1)), + Offset: i, + DefaultValue: i + 1, + State: model.StatePublic, + } + + col.FieldType = *types.NewFieldType(mysql.TypeLong) + tblInfo.MaxColumnID++ + col.ID = tblInfo.MaxColumnID + cols[i] = col + } + tblInfo.Columns = cols + tblInfo.Charset = "utf8" + tblInfo.Collate = "utf8_bin" + return tblInfo, nil +} + +func genGlobalIDs(store kv.Storage, count int) ([]int64, error) { + var ret []int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + var err error + ret, err = m.GenGlobalIDs(count) + return err + }) + return ret, err +} + +func testSchemaInfo(store kv.Storage, name string) (*model.DBInfo, error) { + dbInfo := &model.DBInfo{ + Name: model.NewCIStr(name), + } + + genIDs, err := genGlobalIDs(store, 1) + if err != nil { + return nil, err + } + dbInfo.ID = genIDs[0] + return dbInfo, nil +} + +func testCreateSchema(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo) *model.Job { + job := &model.Job{ + SchemaID: dbInfo.ID, + Type: model.ActionCreateSchema, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{dbInfo}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v := getSchemaVer(t, ctx) + dbInfo.State = model.StatePublic + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, db: dbInfo}) + dbInfo.State = model.StateNone + return job +} + +func buildDropSchemaJob(dbInfo *model.DBInfo) *model.Job { + return &model.Job{ + SchemaID: dbInfo.ID, + Type: model.ActionDropSchema, + BinlogInfo: &model.HistoryInfo{}, + } +} + +func testDropSchema(t *testing.T, ctx sessionctx.Context, d ddl.DDL, dbInfo *model.DBInfo) (*model.Job, int64) { + job := buildDropSchemaJob(dbInfo) + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + ver := getSchemaVer(t, ctx) + return job, ver +} + +func isDDLJobDone(test *testing.T, t *meta.Meta, store kv.Storage) bool { + tk := testkit.NewTestKit(test, store) + rows := tk.MustQuery("select * from mysql.tidb_ddl_job").Rows() + + if len(rows) == 0 { + return true + } + time.Sleep(testLease) + return false +} + +func testCheckSchemaState(test *testing.T, store kv.Storage, dbInfo *model.DBInfo, state model.SchemaState) { + isDropped := true + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + for { + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + info, err := t.GetDatabase(dbInfo.ID) + require.NoError(test, err) + + if state == model.StateNone { + isDropped = isDDLJobDone(test, t, store) + if !isDropped { + return nil + } + require.Nil(test, info) + return nil + } + + require.Equal(test, info.Name, dbInfo.Name) + require.Equal(test, info.State, state) + return nil + }) + require.NoError(test, err) + + if isDropped { + break + } + } +} + +func TestSchema(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + dbInfo, err := testSchemaInfo(store, "test_schema") + require.NoError(t, err) + + // create a database. + tk := testkit.NewTestKit(t, store) + d := domain.DDL() + job := testCreateSchema(t, tk.Session(), d, dbInfo) + testCheckSchemaState(t, store, dbInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + /*** to drop the schema with two tables. ***/ + // create table t with 100 records. + tblInfo1, err := testTableInfo(store, "t", 3) + require.NoError(t, err) + tJob1 := testCreateTable(t, tk.Session(), d, dbInfo, tblInfo1) + testCheckTableState(t, store, dbInfo, tblInfo1, model.StatePublic) + testCheckJobDone(t, store, tJob1.ID, true) + tbl1 := testGetTable(t, domain, tblInfo1.ID) + err = sessiontxn.NewTxn(context.Background(), tk.Session()) + require.NoError(t, err) + for i := 1; i <= 100; i++ { + _, err := tbl1.AddRecord(tk.Session(), types.MakeDatums(i, i, i)) + require.NoError(t, err) + } + // create table t1 with 1034 records. + tblInfo2, err := testTableInfo(store, "t1", 3) + require.NoError(t, err) + tk2 := testkit.NewTestKit(t, store) + tJob2 := testCreateTable(t, tk2.Session(), d, dbInfo, tblInfo2) + testCheckTableState(t, store, dbInfo, tblInfo2, model.StatePublic) + testCheckJobDone(t, store, tJob2.ID, true) + tbl2 := testGetTable(t, domain, tblInfo2.ID) + err = sessiontxn.NewTxn(context.Background(), tk2.Session()) + require.NoError(t, err) + for i := 1; i <= 1034; i++ { + _, err := tbl2.AddRecord(tk2.Session(), types.MakeDatums(i, i, i)) + require.NoError(t, err) + } + tk3 := testkit.NewTestKit(t, store) + job, v := testDropSchema(t, tk3.Session(), d, dbInfo) + testCheckSchemaState(t, store, dbInfo, model.StateNone) + ids := make(map[int64]struct{}) + ids[tblInfo1.ID] = struct{}{} + ids[tblInfo2.ID] = struct{}{} + checkHistoryJobArgs(t, tk3.Session(), job.ID, &historyJobArgs{ver: v, db: dbInfo, tblIDs: ids}) + + // Drop a non-existent database. + job = &model.Job{ + SchemaID: dbInfo.ID, + Type: model.ActionDropSchema, + BinlogInfo: &model.HistoryInfo{}, + } + ctx := testkit.NewTestKit(t, store).Session() + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.True(t, terror.ErrorEqual(err, infoschema.ErrDatabaseDropExists), "err %v", err) + + // Drop a database without a table. + dbInfo1, err := testSchemaInfo(store, "test1") + require.NoError(t, err) + job = testCreateSchema(t, ctx, d, dbInfo1) + testCheckSchemaState(t, store, dbInfo1, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + job, _ = testDropSchema(t, ctx, d, dbInfo1) + testCheckSchemaState(t, store, dbInfo1, model.StateNone) + testCheckJobDone(t, store, job.ID, false) +} + +func TestSchemaWaitJob(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + require.True(t, domain.DDL().OwnerManager().IsOwner()) + + d2 := ddl.NewDDL(context.Background(), + ddl.WithEtcdClient(domain.EtcdClient()), + ddl.WithStore(store), + ddl.WithInfoCache(domain.InfoCache()), + ddl.WithLease(testLease), + ) + err := d2.Start(pools.NewResourcePool(func() (pools.Resource, error) { + session := testkit.NewTestKit(t, store).Session() + session.GetSessionVars().CommonGlobalLoaded = true + return session, nil + }, 20, 20, 5)) + require.NoError(t, err) + defer func() { + err := d2.Stop() + require.NoError(t, err) + }() + + // d2 must not be owner. + d2.OwnerManager().RetireOwner() + // wait one-second makes d2 stop pick up jobs. + time.Sleep(1 * time.Second) + + dbInfo, err := testSchemaInfo(store, "test_schema") + require.NoError(t, err) + se := testkit.NewTestKit(t, store).Session() + testCreateSchema(t, se, d2, dbInfo) + testCheckSchemaState(t, store, dbInfo, model.StatePublic) + + // d2 must not be owner. + require.False(t, d2.OwnerManager().IsOwner()) + + genIDs, err := genGlobalIDs(store, 1) + require.NoError(t, err) + schemaID := genIDs[0] + doDDLJobErr(t, schemaID, 0, model.ActionCreateSchema, []interface{}{dbInfo}, testkit.NewTestKit(t, store).Session(), d2, store) +} + +func doDDLJobErr(t *testing.T, schemaID, tableID int64, tp model.ActionType, args []interface{}, ctx sessionctx.Context, d ddl.DDL, store kv.Storage) *model.Job { + job := &model.Job{ + SchemaID: schemaID, + TableID: tableID, + Type: tp, + Args: args, + BinlogInfo: &model.HistoryInfo{}, + } + // TODO: check error detail + ctx.SetValue(sessionctx.QueryString, "skip") + require.Error(t, d.DoDDLJob(ctx, job)) + testCheckJobCancelled(t, store, job, nil) + + return job +} + +func testCheckJobCancelled(t *testing.T, store kv.Storage, job *model.Job, state *model.SchemaState) { + se := testkit.NewTestKit(t, store).Session() + historyJob, err := ddl.GetHistoryJobByID(se, job.ID) + require.NoError(t, err) + require.True(t, historyJob.IsCancelled() || historyJob.IsRollbackDone(), "history job %s", historyJob) + if state != nil { + require.Equal(t, historyJob.SchemaState, *state) + } +} + +func TestRenameTableAutoIDs(t *testing.T) { + store := testkit.CreateMockStore(t) + tk1 := testkit.NewTestKit(t, store) + + dbName := "RenameTableAutoIDs" + tk1.MustExec(`create schema ` + dbName) + tk1.MustExec(`create schema ` + dbName + "2") + tk1.MustExec(`use ` + dbName) + tk1.MustExec(`CREATE TABLE t (a int auto_increment primary key nonclustered, b varchar(255), key (b))`) + tk1.MustExec(`insert into t values (11,11),(2,2),(null,12)`) + tk1.MustExec(`insert into t values (null,18)`) + tk1.MustQuery(`select _tidb_rowid, a, b from t`).Sort().Check(testkit.Rows("13 11 11", "14 2 2", "15 12 12", "17 16 18")) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec(`use ` + dbName) + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec(`use ` + dbName) + waitFor := func(col int, tableName, s string) { + for { + tk4 := testkit.NewTestKit(t, store) + tk4.MustExec(`use test`) + sql := `admin show ddl jobs where db_name like '` + strings.ToLower(dbName) + `%' and table_name like '` + tableName + `%' and job_type = 'rename table'` + res := tk4.MustQuery(sql).Rows() + if len(res) == 1 && res[0][col] == s { + break + } + for i := range res { + strs := make([]string, 0, len(res[i])) + for j := range res[i] { + strs = append(strs, res[i][j].(string)) + } + logutil.BgLogger().Info("ddl jobs", zap.Strings("jobs", strs)) + } + time.Sleep(10 * time.Millisecond) + } + } + alterChan := make(chan error) + tk2.MustExec(`set @@session.innodb_lock_wait_timeout = 0`) + tk2.MustExec(`BEGIN`) + tk2.MustExec(`insert into t values (null, 4)`) + go func() { + alterChan <- tk1.ExecToErr(`rename table t to ` + dbName + `2.t2`) + }() + waitFor(11, "t", "running") + waitFor(4, "t", "public") + tk3.MustExec(`BEGIN`) + tk3.MustExec(`insert into ` + dbName + `2.t2 values (20, 5)`) + + // TODO: Fix https://github.com/pingcap/tidb/issues/46904 + tk2.MustContainErrMsg(`insert into t values (null, 6)`, "[tikv:1205]Lock wait timeout exceeded; try restarting transaction") + tk2.MustExec(`rollback`) + tk3.MustExec(`rollback`) + /* + tk3.MustExec(`insert into ` + dbName + `2.t2 values (null, 7)`) + tk2.MustExec(`COMMIT`) + + waitFor(11, "t", "done") + tk2.MustExec(`BEGIN`) + tk2.MustExec(`insert into ` + dbName + `2.t2 values (null, 8)`) + + tk3.MustExec(`insert into ` + dbName + `2.t2 values (null, 9)`) + tk2.MustExec(`insert into ` + dbName + `2.t2 values (null, 10)`) + tk3.MustExec(`COMMIT`) + + waitFor(11, "t", "synced") + tk2.MustExec(`COMMIT`) + tk3.MustQuery(`select _tidb_rowid, a, b from ` + dbName + `2.t2`).Sort().Check(testkit.Rows(""+ + "13 11 11", + "14 2 2", + "15 12 12", + "17 16 18", + "19 18 4", + "21 20 6", + "5013 5012 5", + "5015 5014 7", + )) + + require.NoError(t, <-alterChan) + tk2.MustQuery(`select _tidb_rowid, a, b from ` + dbName + `2.t2`).Sort().Check(testkit.Rows( + "13 11 11", "14 2 2", "15 12 12", "17 16 18", + "19 18 4", "21 20 6", "5013 5012 5", "5015 5014 7")) + */ + require.NoError(t, <-alterChan) +} diff --git a/pkg/ddl/schematracker/BUILD.bazel b/pkg/ddl/schematracker/BUILD.bazel new file mode 100644 index 0000000000000..3df075c1c3a17 --- /dev/null +++ b/pkg/ddl/schematracker/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "schematracker", + srcs = [ + "checker.go", + "dm_tracker.go", + "info_store.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/schematracker", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/ddl/syncer", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/owner", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tidb-binlog/pump_client", + "//pkg/util/collate", + "//pkg/util/dbterror", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "schematracker_test", + timeout = "short", + srcs = [ + "dm_tracker_test.go", + "info_store_test.go", + ], + embed = [":schematracker"], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/executor", + "//pkg/infoschema", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/util/chunk", + "//pkg/util/mock", + "//pkg/util/sqlexec", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/ddl/schematracker/checker.go b/pkg/ddl/schematracker/checker.go new file mode 100644 index 0000000000000..bc63e50639b93 --- /dev/null +++ b/pkg/ddl/schematracker/checker.go @@ -0,0 +1,608 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schematracker + +import ( + "bytes" + "context" + "crypto/tls" + "fmt" + "strings" + "sync/atomic" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" +) + +var ( + // ConstructResultOfShowCreateDatabase should be assigned to executor.ConstructResultOfShowCreateDatabase. + // It is used to break cycle import. + ConstructResultOfShowCreateDatabase func(sessionctx.Context, *model.DBInfo, bool, *bytes.Buffer) error + // ConstructResultOfShowCreateTable should be assigned to executor.ConstructResultOfShowCreateTable. + // It is used to break cycle import. + ConstructResultOfShowCreateTable func(sessionctx.Context, *model.TableInfo, autoid.Allocators, *bytes.Buffer) error +) + +func init() { + mockstore.DDLCheckerInjector = NewStorageDDLInjector +} + +// Checker is used to check the result of SchemaTracker is same as real DDL. +type Checker struct { + realDDL ddl.DDL + tracker SchemaTracker + closed atomic.Bool +} + +// NewChecker creates a Checker. +func NewChecker(realDDL ddl.DDL) *Checker { + return &Checker{ + realDDL: realDDL, + tracker: NewSchemaTracker(2), + } +} + +// Disable turns off check. +func (d *Checker) Disable() { + d.closed.Store(true) +} + +// Enable turns on check. +func (d *Checker) Enable() { + d.closed.Store(false) +} + +// CreateTestDB creates a `test` database like the default behaviour of TiDB. +func (d *Checker) CreateTestDB(ctx sessionctx.Context) { + d.tracker.CreateTestDB(ctx) +} + +func (d *Checker) checkDBInfo(ctx sessionctx.Context, dbName model.CIStr) { + if d.closed.Load() { + return + } + dbInfo, _ := d.realDDL.GetInfoSchemaWithInterceptor(ctx).SchemaByName(dbName) + dbInfo2 := d.tracker.SchemaByName(dbName) + + if dbInfo == nil || dbInfo2 == nil { + if dbInfo == nil && dbInfo2 == nil { + return + } + errStr := fmt.Sprintf("inconsistent dbInfo, dbName: %s, real ddl: %p, schematracker:%p", dbName, dbInfo, dbInfo2) + panic(errStr) + } + + result := bytes.NewBuffer(make([]byte, 0, 512)) + err := ConstructResultOfShowCreateDatabase(ctx, dbInfo, false, result) + if err != nil { + panic(err) + } + result2 := bytes.NewBuffer(make([]byte, 0, 512)) + err = ConstructResultOfShowCreateDatabase(ctx, dbInfo2, false, result2) + if err != nil { + panic(err) + } + s1 := result.String() + s2 := result2.String() + if s1 != s2 { + errStr := fmt.Sprintf("%s != %s", s1, s2) + panic(errStr) + } +} + +func (d *Checker) checkTableInfo(ctx sessionctx.Context, dbName, tableName model.CIStr) { + if d.closed.Load() { + return + } + + if dbName.L == mysql.SystemDB { + // no need to check system tables. + return + } + + tableInfo, _ := d.realDDL.GetInfoSchemaWithInterceptor(ctx).TableByName(dbName, tableName) + tableInfo2, _ := d.tracker.TableByName(dbName, tableName) + + if tableInfo == nil || tableInfo2 == nil { + if tableInfo == nil && tableInfo2 == nil { + return + } + errStr := fmt.Sprintf("inconsistent tableInfo, dbName: %s, tableName: %s, real ddl: %p, schematracker:%p", + dbName, tableName, tableInfo, tableInfo2) + panic(errStr) + } + + result := bytes.NewBuffer(make([]byte, 0, 512)) + err := ConstructResultOfShowCreateTable(ctx, tableInfo.Meta(), autoid.Allocators{}, result) + if err != nil { + panic(err) + } + result2 := bytes.NewBuffer(make([]byte, 0, 512)) + err = ConstructResultOfShowCreateTable(ctx, tableInfo2, autoid.Allocators{}, result2) + if err != nil { + panic(err) + } + + // SchemaTracker will always use NONCLUSTERED so it can support more types of DDL. + removeClusteredIndexComment := func(s string) string { + ret := strings.ReplaceAll(s, " /*T![clustered_index] NONCLUSTERED */", "") + ret = strings.ReplaceAll(ret, " /*T![clustered_index] CLUSTERED */", "") + return ret + } + + s1 := removeClusteredIndexComment(result.String()) + s2 := removeClusteredIndexComment(result2.String()) + + if s1 != s2 { + errStr := fmt.Sprintf("%s != %s", s1, s2) + panic(errStr) + } +} + +// CreateSchema implements the DDL interface. +func (d *Checker) CreateSchema(ctx sessionctx.Context, stmt *ast.CreateDatabaseStmt) error { + err := d.realDDL.CreateSchema(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.CreateSchema(ctx, stmt) + if err != nil { + panic(err) + } + + d.checkDBInfo(ctx, stmt.Name) + return nil +} + +// AlterSchema implements the DDL interface. +func (d *Checker) AlterSchema(sctx sessionctx.Context, stmt *ast.AlterDatabaseStmt) error { + err := d.realDDL.AlterSchema(sctx, stmt) + if err != nil { + return err + } + err = d.tracker.AlterSchema(sctx, stmt) + if err != nil { + panic(err) + } + + d.checkDBInfo(sctx, stmt.Name) + return nil +} + +// DropSchema implements the DDL interface. +func (d *Checker) DropSchema(ctx sessionctx.Context, stmt *ast.DropDatabaseStmt) error { + err := d.realDDL.DropSchema(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.DropSchema(ctx, stmt) + if err != nil { + panic(err) + } + + d.checkDBInfo(ctx, stmt.Name) + return nil +} + +// RecoverSchema implements the DDL interface. +func (*Checker) RecoverSchema(_ sessionctx.Context, _ *ddl.RecoverSchemaInfo) (err error) { + return nil +} + +// CreateTable implements the DDL interface. +func (d *Checker) CreateTable(ctx sessionctx.Context, stmt *ast.CreateTableStmt) error { + err := d.realDDL.CreateTable(ctx, stmt) + if err != nil { + return err + } + + // some unit test will also check warnings, we reset the warnings after SchemaTracker use session context again. + count := ctx.GetSessionVars().StmtCtx.WarningCount() + // backup old session variables because CreateTable will change them. + strictSQLMode := ctx.GetSessionVars().StrictSQLMode + enableClusteredIndex := ctx.GetSessionVars().EnableClusteredIndex + + err = d.tracker.CreateTable(ctx, stmt) + if err != nil { + panic(err) + } + + ctx.GetSessionVars().StrictSQLMode = strictSQLMode + ctx.GetSessionVars().EnableClusteredIndex = enableClusteredIndex + ctx.GetSessionVars().StmtCtx.TruncateWarnings(int(count)) + + d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) + return nil +} + +// CreateView implements the DDL interface. +func (d *Checker) CreateView(ctx sessionctx.Context, stmt *ast.CreateViewStmt) error { + err := d.realDDL.CreateView(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.CreateView(ctx, stmt) + if err != nil { + panic(err) + } + + d.checkTableInfo(ctx, stmt.ViewName.Schema, stmt.ViewName.Name) + return nil +} + +// DropTable implements the DDL interface. +func (d *Checker) DropTable(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) { + err = d.realDDL.DropTable(ctx, stmt) + _ = d.tracker.DropTable(ctx, stmt) + + for _, tableName := range stmt.Tables { + d.checkTableInfo(ctx, tableName.Schema, tableName.Name) + } + return err +} + +// RecoverTable implements the DDL interface. +func (*Checker) RecoverTable(_ sessionctx.Context, _ *ddl.RecoverInfo) (err error) { + //TODO implement me + panic("implement me") +} + +// FlashbackCluster implements the DDL interface. +func (*Checker) FlashbackCluster(_ sessionctx.Context, _ uint64) (err error) { + //TODO implement me + panic("implement me") +} + +// DropView implements the DDL interface. +func (d *Checker) DropView(ctx sessionctx.Context, stmt *ast.DropTableStmt) (err error) { + err = d.realDDL.DropView(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.DropView(ctx, stmt) + if err != nil { + panic(err) + } + + for _, tableName := range stmt.Tables { + d.checkTableInfo(ctx, tableName.Schema, tableName.Name) + } + return nil +} + +// CreateIndex implements the DDL interface. +func (d *Checker) CreateIndex(ctx sessionctx.Context, stmt *ast.CreateIndexStmt) error { + err := d.realDDL.CreateIndex(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.CreateIndex(ctx, stmt) + if err != nil { + panic(err) + } + + d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) + return nil +} + +// DropIndex implements the DDL interface. +func (d *Checker) DropIndex(ctx sessionctx.Context, stmt *ast.DropIndexStmt) error { + err := d.realDDL.DropIndex(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.DropIndex(ctx, stmt) + if err != nil { + panic(err) + } + + d.checkTableInfo(ctx, stmt.Table.Schema, stmt.Table.Name) + return nil +} + +// AlterTable implements the DDL interface. +func (d *Checker) AlterTable(ctx context.Context, sctx sessionctx.Context, stmt *ast.AlterTableStmt) error { + err := d.realDDL.AlterTable(ctx, sctx, stmt) + if err != nil { + return err + } + + // some unit test will also check warnings, we reset the warnings after SchemaTracker use session context again. + count := sctx.GetSessionVars().StmtCtx.WarningCount() + err = d.tracker.AlterTable(ctx, sctx, stmt) + if err != nil { + panic(err) + } + sctx.GetSessionVars().StmtCtx.TruncateWarnings(int(count)) + + d.checkTableInfo(sctx, stmt.Table.Schema, stmt.Table.Name) + return nil +} + +// TruncateTable implements the DDL interface. +func (*Checker) TruncateTable(_ sessionctx.Context, _ ast.Ident) error { + //TODO implement me + panic("implement me") +} + +// RenameTable implements the DDL interface. +func (d *Checker) RenameTable(ctx sessionctx.Context, stmt *ast.RenameTableStmt) error { + err := d.realDDL.RenameTable(ctx, stmt) + if err != nil { + return err + } + err = d.tracker.RenameTable(ctx, stmt) + if err != nil { + panic(err) + } + + for _, tableName := range stmt.TableToTables { + d.checkTableInfo(ctx, tableName.OldTable.Schema, tableName.OldTable.Name) + d.checkTableInfo(ctx, tableName.NewTable.Schema, tableName.NewTable.Name) + } + return nil +} + +// LockTables implements the DDL interface. +func (d *Checker) LockTables(ctx sessionctx.Context, stmt *ast.LockTablesStmt) error { + return d.realDDL.LockTables(ctx, stmt) +} + +// UnlockTables implements the DDL interface. +func (d *Checker) UnlockTables(ctx sessionctx.Context, lockedTables []model.TableLockTpInfo) error { + return d.realDDL.UnlockTables(ctx, lockedTables) +} + +// CleanupTableLock implements the DDL interface. +func (d *Checker) CleanupTableLock(ctx sessionctx.Context, tables []*ast.TableName) error { + return d.realDDL.CleanupTableLock(ctx, tables) +} + +// UpdateTableReplicaInfo implements the DDL interface. +func (*Checker) UpdateTableReplicaInfo(_ sessionctx.Context, _ int64, _ bool) error { + //TODO implement me + panic("implement me") +} + +// RepairTable implements the DDL interface. +func (*Checker) RepairTable(_ sessionctx.Context, _ *ast.CreateTableStmt) error { + //TODO implement me + panic("implement me") +} + +// CreateSequence implements the DDL interface. +func (*Checker) CreateSequence(_ sessionctx.Context, _ *ast.CreateSequenceStmt) error { + //TODO implement me + panic("implement me") +} + +// DropSequence implements the DDL interface. +func (*Checker) DropSequence(_ sessionctx.Context, _ *ast.DropSequenceStmt) (err error) { + //TODO implement me + panic("implement me") +} + +// AlterSequence implements the DDL interface. +func (*Checker) AlterSequence(_ sessionctx.Context, _ *ast.AlterSequenceStmt) error { + //TODO implement me + panic("implement me") +} + +// CreatePlacementPolicy implements the DDL interface. +func (*Checker) CreatePlacementPolicy(_ sessionctx.Context, _ *ast.CreatePlacementPolicyStmt) error { + //TODO implement me + panic("implement me") +} + +// DropPlacementPolicy implements the DDL interface. +func (*Checker) DropPlacementPolicy(_ sessionctx.Context, _ *ast.DropPlacementPolicyStmt) error { + //TODO implement me + panic("implement me") +} + +// AlterPlacementPolicy implements the DDL interface. +func (*Checker) AlterPlacementPolicy(_ sessionctx.Context, _ *ast.AlterPlacementPolicyStmt) error { + //TODO implement me + panic("implement me") +} + +// AddResourceGroup implements the DDL interface. +// ResourceGroup do not affect the transaction. +func (*Checker) AddResourceGroup(_ sessionctx.Context, _ *ast.CreateResourceGroupStmt) error { + return nil +} + +// DropResourceGroup implements the DDL interface. +func (*Checker) DropResourceGroup(_ sessionctx.Context, _ *ast.DropResourceGroupStmt) error { + return nil +} + +// AlterResourceGroup implements the DDL interface. +func (*Checker) AlterResourceGroup(_ sessionctx.Context, _ *ast.AlterResourceGroupStmt) error { + return nil +} + +// CreateSchemaWithInfo implements the DDL interface. +func (d *Checker) CreateSchemaWithInfo(ctx sessionctx.Context, info *model.DBInfo, onExist ddl.OnExist) error { + err := d.realDDL.CreateSchemaWithInfo(ctx, info, onExist) + if err != nil { + return err + } + err = d.tracker.CreateSchemaWithInfo(ctx, info, onExist) + if err != nil { + panic(err) + } + + d.checkDBInfo(ctx, info.Name) + return nil +} + +// CreateTableWithInfo implements the DDL interface. +func (*Checker) CreateTableWithInfo(_ sessionctx.Context, _ model.CIStr, _ *model.TableInfo, _ ...ddl.CreateTableWithInfoConfigurier) error { + //TODO implement me + panic("implement me") +} + +// BatchCreateTableWithInfo implements the DDL interface. +func (*Checker) BatchCreateTableWithInfo(_ sessionctx.Context, _ model.CIStr, _ []*model.TableInfo, _ ...ddl.CreateTableWithInfoConfigurier) error { + //TODO implement me + panic("implement me") +} + +// CreatePlacementPolicyWithInfo implements the DDL interface. +func (*Checker) CreatePlacementPolicyWithInfo(_ sessionctx.Context, _ *model.PolicyInfo, _ ddl.OnExist) error { + //TODO implement me + panic("implement me") +} + +// Start implements the DDL interface. +func (d *Checker) Start(ctxPool *pools.ResourcePool) error { + return d.realDDL.Start(ctxPool) +} + +// GetLease implements the DDL interface. +func (d *Checker) GetLease() time.Duration { + return d.realDDL.GetLease() +} + +// Stats implements the DDL interface. +func (d *Checker) Stats(vars *variable.SessionVars) (map[string]interface{}, error) { + return d.realDDL.Stats(vars) +} + +// GetScope implements the DDL interface. +func (d *Checker) GetScope(status string) variable.ScopeFlag { + return d.realDDL.GetScope(status) +} + +// Stop implements the DDL interface. +func (d *Checker) Stop() error { + return d.realDDL.Stop() +} + +// RegisterStatsHandle implements the DDL interface. +func (d *Checker) RegisterStatsHandle(h *handle.Handle) { + d.realDDL.RegisterStatsHandle(h) +} + +// SchemaSyncer implements the DDL interface. +func (d *Checker) SchemaSyncer() syncer.SchemaSyncer { + return d.realDDL.SchemaSyncer() +} + +// StateSyncer implements the DDL interface. +func (d *Checker) StateSyncer() syncer.StateSyncer { + return d.realDDL.StateSyncer() +} + +// OwnerManager implements the DDL interface. +func (d *Checker) OwnerManager() owner.Manager { + return d.realDDL.OwnerManager() +} + +// GetID implements the DDL interface. +func (d *Checker) GetID() string { + return d.realDDL.GetID() +} + +// GetTableMaxHandle implements the DDL interface. +func (d *Checker) GetTableMaxHandle(ctx *ddl.JobContext, startTS uint64, tbl table.PhysicalTable) (kv.Handle, bool, error) { + return d.realDDL.GetTableMaxHandle(ctx, startTS, tbl) +} + +// SetBinlogClient implements the DDL interface. +func (d *Checker) SetBinlogClient(client *pumpcli.PumpsClient) { + d.realDDL.SetBinlogClient(client) +} + +// GetHook implements the DDL interface. +func (d *Checker) GetHook() ddl.Callback { + return d.realDDL.GetHook() +} + +// SetHook implements the DDL interface. +func (d *Checker) SetHook(h ddl.Callback) { + d.realDDL.SetHook(h) +} + +// GetInfoSchemaWithInterceptor implements the DDL interface. +func (d *Checker) GetInfoSchemaWithInterceptor(ctx sessionctx.Context) infoschema.InfoSchema { + return d.realDDL.GetInfoSchemaWithInterceptor(ctx) +} + +// DoDDLJob implements the DDL interface. +func (d *Checker) DoDDLJob(ctx sessionctx.Context, job *model.Job) error { + return d.realDDL.DoDDLJob(ctx, job) +} + +// StorageDDLInjector wraps kv.Storage to inject checker to domain's DDL in bootstrap time. +type StorageDDLInjector struct { + kv.Storage + kv.EtcdBackend + Injector func(ddl.DDL) *Checker +} + +var _ kv.EtcdBackend = StorageDDLInjector{} + +// EtcdAddrs implements the kv.EtcdBackend interface. +func (s StorageDDLInjector) EtcdAddrs() ([]string, error) { + return s.EtcdBackend.EtcdAddrs() +} + +// TLSConfig implements the kv.EtcdBackend interface. +func (s StorageDDLInjector) TLSConfig() *tls.Config { + return s.EtcdBackend.TLSConfig() +} + +// StartGCWorker implements the kv.EtcdBackend interface. +func (s StorageDDLInjector) StartGCWorker() error { + return s.EtcdBackend.StartGCWorker() +} + +// NewStorageDDLInjector creates a new StorageDDLInjector to inject Checker. +func NewStorageDDLInjector(s kv.Storage) kv.Storage { + ret := StorageDDLInjector{ + Storage: s, + Injector: NewChecker, + } + if ebd, ok := s.(kv.EtcdBackend); ok { + ret.EtcdBackend = ebd + } + return ret +} + +// UnwrapStorage unwraps StorageDDLInjector for one level. +func UnwrapStorage(s kv.Storage) kv.Storage { + injector, ok := s.(StorageDDLInjector) + if !ok { + return s + } + return injector.Storage +} diff --git a/ddl/schematracker/dm_tracker.go b/pkg/ddl/schematracker/dm_tracker.go similarity index 98% rename from ddl/schematracker/dm_tracker.go rename to pkg/ddl/schematracker/dm_tracker.go index 925a1a566a0a1..e25b623210077 100644 --- a/ddl/schematracker/dm_tracker.go +++ b/pkg/ddl/schematracker/dm_tracker.go @@ -25,24 +25,24 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - field_types "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + field_types "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" ) var _ ddl.DDL = SchemaTracker{} diff --git a/ddl/schematracker/dm_tracker_test.go b/pkg/ddl/schematracker/dm_tracker_test.go similarity index 97% rename from ddl/schematracker/dm_tracker_test.go rename to pkg/ddl/schematracker/dm_tracker_test.go index 060f90b50c0e5..227bfa6f52f67 100644 --- a/ddl/schematracker/dm_tracker_test.go +++ b/pkg/ddl/schematracker/dm_tracker_test.go @@ -24,17 +24,17 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/ddl/schematracker/info_store.go b/pkg/ddl/schematracker/info_store.go similarity index 97% rename from ddl/schematracker/info_store.go rename to pkg/ddl/schematracker/info_store.go index d6bb970591c8b..80021eda0cb9a 100644 --- a/ddl/schematracker/info_store.go +++ b/pkg/ddl/schematracker/info_store.go @@ -15,10 +15,10 @@ package schematracker import ( - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" ) // InfoStore is a simple structure that stores DBInfo and TableInfo. It's modifiable and not thread-safe. diff --git a/ddl/schematracker/info_store_test.go b/pkg/ddl/schematracker/info_store_test.go similarity index 98% rename from ddl/schematracker/info_store_test.go rename to pkg/ddl/schematracker/info_store_test.go index 30d05e6dde2ad..6cce3b416271f 100644 --- a/ddl/schematracker/info_store_test.go +++ b/pkg/ddl/schematracker/info_store_test.go @@ -18,8 +18,8 @@ import ( "sort" "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/ddl/sequence.go b/pkg/ddl/sequence.go similarity index 96% rename from ddl/sequence.go rename to pkg/ddl/sequence.go index ad8390bdf2cc2..9772322d90f06 100644 --- a/ddl/sequence.go +++ b/pkg/ddl/sequence.go @@ -19,13 +19,13 @@ import ( "reflect" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/mathutil" ) func onCreateSequence(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { diff --git a/ddl/sequence_test.go b/pkg/ddl/sequence_test.go similarity index 98% rename from ddl/sequence_test.go rename to pkg/ddl/sequence_test.go index 942bf23f0ee1f..2f5aeb833186e 100644 --- a/ddl/sequence_test.go +++ b/pkg/ddl/sequence_test.go @@ -18,13 +18,13 @@ import ( "testing" "time" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" "github.com/stretchr/testify/require" ) diff --git a/ddl/split_region.go b/pkg/ddl/split_region.go similarity index 95% rename from ddl/split_region.go rename to pkg/ddl/split_region.go index ccadc3e601c4e..bbd97b89e4571 100644 --- a/ddl/split_region.go +++ b/pkg/ddl/split_region.go @@ -18,14 +18,14 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" tikverr "github.com/tikv/client-go/v2/error" "go.uber.org/zap" ) diff --git a/pkg/ddl/stat.go b/pkg/ddl/stat.go new file mode 100644 index 0000000000000..7ecb4c6bc9414 --- /dev/null +++ b/pkg/ddl/stat.go @@ -0,0 +1,87 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "encoding/hex" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx/variable" +) + +var ( + serverID = "server_id" + ddlSchemaVersion = "ddl_schema_version" + ddlJobID = "ddl_job_id" + ddlJobAction = "ddl_job_action" + ddlJobStartTS = "ddl_job_start_ts" + ddlJobState = "ddl_job_state" + ddlJobError = "ddl_job_error" + ddlJobRows = "ddl_job_row_count" + ddlJobSchemaState = "ddl_job_schema_state" + ddlJobSchemaID = "ddl_job_schema_id" + ddlJobTableID = "ddl_job_table_id" + ddlJobSnapshotVer = "ddl_job_snapshot_ver" + ddlJobReorgHandle = "ddl_job_reorg_handle" + ddlJobArgs = "ddl_job_args" +) + +// GetScope gets the status variables scope. +func (*ddl) GetScope(_ string) variable.ScopeFlag { + // Now ddl status variables scope are all default scope. + return variable.DefaultStatusVarScopeFlag +} + +// Stats returns the DDL statistics. +func (d *ddl) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { + m := make(map[string]interface{}) + m[serverID] = d.uuid + var ddlInfo *Info + + s, err := d.sessPool.Get() + if err != nil { + return nil, errors.Trace(err) + } + defer d.sessPool.Put(s) + ddlInfo, err = GetDDLInfoWithNewTxn(s) + if err != nil { + return nil, errors.Trace(err) + } + + m[ddlSchemaVersion] = ddlInfo.SchemaVer + // TODO: Get the owner information. + if len(ddlInfo.Jobs) == 0 { + return m, nil + } + // TODO: Add all job information if needed. + job := ddlInfo.Jobs[0] + m[ddlJobID] = job.ID + m[ddlJobAction] = job.Type.String() + m[ddlJobStartTS] = job.StartTS + m[ddlJobState] = job.State.String() + m[ddlJobRows] = job.RowCount + if job.Error == nil { + m[ddlJobError] = "" + } else { + m[ddlJobError] = job.Error.Error() + } + m[ddlJobSchemaState] = job.SchemaState.String() + m[ddlJobSchemaID] = job.SchemaID + m[ddlJobTableID] = job.TableID + m[ddlJobSnapshotVer] = job.SnapshotVer + m[ddlJobReorgHandle] = hex.EncodeToString(ddlInfo.ReorgHandle) + m[ddlJobArgs] = job.Args + return m, nil +} diff --git a/pkg/ddl/stat_test.go b/pkg/ddl/stat_test.go new file mode 100644 index 0000000000000..291b01e79f13e --- /dev/null +++ b/pkg/ddl/stat_test.go @@ -0,0 +1,210 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "encoding/hex" + "fmt" + "strconv" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestDDLStatsInfo(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + d := domain.DDL() + + tk := testkit.NewTestKit(t, store) + ctx := tk.Session() + dbInfo, err := testSchemaInfo(store, "test_stat") + require.NoError(t, err) + testCreateSchema(t, ctx, d, dbInfo) + tblInfo, err := testTableInfo(store, "t", 2) + require.NoError(t, err) + testCreateTable(t, ctx, d, dbInfo, tblInfo) + err = sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + + m := testGetTable(t, domain, tblInfo.ID) + // insert t values (1, 1), (2, 2), (3, 3) + _, err = m.AddRecord(ctx, types.MakeDatums(1, 1)) + require.NoError(t, err) + _, err = m.AddRecord(ctx, types.MakeDatums(2, 2)) + require.NoError(t, err) + _, err = m.AddRecord(ctx, types.MakeDatums(3, 3)) + require.NoError(t, err) + ctx.StmtCommit(context.Background()) + require.NoError(t, ctx.CommitTxn(context.Background())) + + job := buildCreateIdxJob(dbInfo, tblInfo, true, "idx", "c1") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum")) + }() + + ctx = testkit.NewTestKit(t, store).Session() + done := make(chan error, 1) + go func() { + ctx.SetValue(sessionctx.QueryString, "skip") + done <- d.DoDDLJob(ctx, job) + }() + + exit := false + // a copy of ddl.ddlJobReorgHandle + ddlJobReorgHandle := "ddl_job_reorg_handle" + for !exit { + select { + case err := <-done: + require.NoError(t, err) + exit = true + case wg := <-ddl.TestCheckWorkerNumCh: + varMap, err := d.Stats(nil) + wg.Done() + require.NoError(t, err) + _, err = hex.DecodeString(varMap[ddlJobReorgHandle].(string)) + require.NoError(t, err) + } + } +} + +func TestGetDDLInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + sess := tk.Session() + tk.MustExec("begin") + txn, err := sess.Txn(true) + require.NoError(t, err) + + dbInfo2 := &model.DBInfo{ + ID: 2, + Name: model.NewCIStr("b"), + State: model.StateNone, + } + job := &model.Job{ + ID: 1, + SchemaID: dbInfo2.ID, + Type: model.ActionCreateSchema, + RowCount: 0, + } + job1 := &model.Job{ + ID: 2, + SchemaID: dbInfo2.ID, + Type: model.ActionAddIndex, + RowCount: 0, + } + + err = addDDLJobs(sess, txn, job) + require.NoError(t, err) + + info, err := ddl.GetDDLInfo(sess) + require.NoError(t, err) + require.Len(t, info.Jobs, 1) + require.Equal(t, job, info.Jobs[0]) + require.Nil(t, info.ReorgHandle) + + // two jobs + err = addDDLJobs(sess, txn, job1) + require.NoError(t, err) + + info, err = ddl.GetDDLInfo(sess) + require.NoError(t, err) + require.Len(t, info.Jobs, 2) + require.Equal(t, job, info.Jobs[0]) + require.Equal(t, job1, info.Jobs[1]) + require.Nil(t, info.ReorgHandle) + + tk.MustExec("rollback") +} + +func addDDLJobs(sess session.Session, txn kv.Transaction, job *model.Job) error { + b, err := job.Encode(true) + if err != nil { + return err + } + _, err = sess.Execute(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), fmt.Sprintf("insert into mysql.tidb_ddl_job(job_id, reorg, schema_ids, table_ids, job_meta, type, processing) values (%d, %t, %s, %s, %s, %d, %t)", + job.ID, job.MayNeedReorg(), strconv.Quote(strconv.FormatInt(job.SchemaID, 10)), strconv.Quote(strconv.FormatInt(job.TableID, 10)), util.WrapKey2String(b), job.Type, false)) + return err +} + +func buildCreateIdxJob(dbInfo *model.DBInfo, tblInfo *model.TableInfo, unique bool, indexName string, colName string) *model.Job { + return &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionAddIndex, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{unique, model.NewCIStr(indexName), + []*ast.IndexPartSpecification{{ + Column: &ast.ColumnName{Name: model.NewCIStr(colName)}, + Length: types.UnspecifiedLength}}}, + ReorgMeta: &model.DDLReorgMeta{ // Add index job must have this field. + SQLMode: mysql.SQLMode(0), + Warnings: make(map[errors.ErrorID]*terror.Error), + WarningsCount: make(map[errors.ErrorID]int64), + }, + } +} + +func TestIssue42268(t *testing.T) { + // issue 42268 missing table name in 'admin show ddl' result during drop table + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_0") + tk.MustExec("create table t_0 (c1 int, c2 int)") + + tbl := external.GetTableByName(t, tk, "test", "t_0") + require.NotNil(t, tbl) + require.Equal(t, 2, len(tbl.Cols())) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + + hook := &callback.TestDDLCallback{Do: dom} + hook.OnJobRunBeforeExported = func(job *model.Job) { + if tbl.Meta().ID != job.TableID { + return + } + switch job.SchemaState { + case model.StateNone: + case model.StateDeleteOnly, model.StateWriteOnly, model.StateWriteReorganization: + rs := tk1.MustQuery("admin show ddl jobs") + tblName := fmt.Sprintf("%s", rs.Rows()[0][2]) + require.Equal(t, tblName, "t_0") + } + } + dom.DDL().SetHook(hook) + + tk.MustExec("drop table t_0") +} diff --git a/pkg/ddl/syncer/BUILD.bazel b/pkg/ddl/syncer/BUILD.bazel new file mode 100644 index 0000000000000..672aca31a4870 --- /dev/null +++ b/pkg/ddl/syncer/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "syncer", + srcs = [ + "state_syncer.go", + "syncer.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/syncer", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/util", + "//pkg/domain/infosync", + "//pkg/metrics", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@io_etcd_go_etcd_api_v3//mvccpb", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "syncer_test", + timeout = "short", + srcs = [ + "state_syncer_test.go", + "syncer_test.go", + ], + flaky = True, + deps = [ + ":syncer", + "//pkg/ddl", + "//pkg/ddl/util", + "//pkg/infoschema", + "//pkg/parser/terror", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/util", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_api_v3//mvccpb", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_server_v3//etcdserver", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//status", + ], +) diff --git a/ddl/syncer/state_syncer.go b/pkg/ddl/syncer/state_syncer.go similarity index 97% rename from ddl/syncer/state_syncer.go rename to pkg/ddl/syncer/state_syncer.go index f80c0d67ca460..da5785a2e5566 100644 --- a/ddl/syncer/state_syncer.go +++ b/pkg/ddl/syncer/state_syncer.go @@ -21,10 +21,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/metrics" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/metrics" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" diff --git a/ddl/syncer/state_syncer_test.go b/pkg/ddl/syncer/state_syncer_test.go similarity index 92% rename from ddl/syncer/state_syncer_test.go rename to pkg/ddl/syncer/state_syncer_test.go index 107e0d0e36faf..1a4d984744c37 100644 --- a/ddl/syncer/state_syncer_test.go +++ b/pkg/ddl/syncer/state_syncer_test.go @@ -20,13 +20,13 @@ import ( "testing" "time" - . "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - util2 "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" + . "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + util2 "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "go.etcd.io/etcd/tests/v3/integration" ) diff --git a/ddl/syncer/syncer.go b/pkg/ddl/syncer/syncer.go similarity index 98% rename from ddl/syncer/syncer.go rename to pkg/ddl/syncer/syncer.go index c7f13b68d2ead..da2ce6b438f53 100644 --- a/ddl/syncer/syncer.go +++ b/pkg/ddl/syncer/syncer.go @@ -27,12 +27,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/sessionctx/variable" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/zap" diff --git a/ddl/syncer/syncer_test.go b/pkg/ddl/syncer/syncer_test.go similarity index 93% rename from ddl/syncer/syncer_test.go rename to pkg/ddl/syncer/syncer_test.go index a0b9d2a051551..0b2afaf87bd49 100644 --- a/ddl/syncer/syncer_test.go +++ b/pkg/ddl/syncer/syncer_test.go @@ -22,14 +22,14 @@ import ( "time" "github.com/pingcap/errors" - . "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - util2 "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" + . "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + util2 "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" diff --git a/pkg/ddl/table.go b/pkg/ddl/table.go new file mode 100644 index 0000000000000..578c348cc7929 --- /dev/null +++ b/pkg/ddl/table.go @@ -0,0 +1,1934 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + sess "github.com/pingcap/tidb/pkg/ddl/internal/session" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + field_types "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + tidb_util "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +const tiflashCheckTiDBHTTPAPIHalfInterval = 2500 * time.Millisecond + +// DANGER: it is an internal function used by onCreateTable and onCreateTables, for reusing code. Be careful. +// 1. it expects the argument of job has been deserialized. +// 2. it won't call updateSchemaVersion, FinishTableJob and asyncNotifyEvent. +func createTable(d *ddlCtx, t *meta.Meta, job *model.Job, fkCheck bool) (*model.TableInfo, error) { + schemaID := job.SchemaID + tbInfo := job.Args[0].(*model.TableInfo) + + tbInfo.State = model.StateNone + err := checkTableNotExists(d, t, schemaID, tbInfo.Name.L) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { + job.State = model.JobStateCancelled + } + return tbInfo, errors.Trace(err) + } + retryable, err := checkTableForeignKeyValidInOwner(d, t, job, tbInfo, fkCheck) + if err != nil { + if !retryable { + job.State = model.JobStateCancelled + } + return tbInfo, errors.Trace(err) + } + // Allocate foreign key ID. + for _, fkInfo := range tbInfo.ForeignKeys { + fkInfo.ID = allocateFKIndexID(tbInfo) + fkInfo.State = model.StatePublic + } + switch tbInfo.State { + case model.StateNone: + // none -> public + tbInfo.State = model.StatePublic + tbInfo.UpdateTS = t.StartTS + err = createTableOrViewWithCheck(t, job, schemaID, tbInfo) + if err != nil { + return tbInfo, errors.Trace(err) + } + + failpoint.Inject("checkOwnerCheckAllVersionsWaitTime", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(tbInfo, errors.New("mock create table error")) + } + }) + + // build table & partition bundles if any. + if err = checkAllTablePlacementPoliciesExistAndCancelNonExistJob(t, job, tbInfo); err != nil { + return tbInfo, errors.Trace(err) + } + + if tbInfo.TiFlashReplica != nil { + replicaInfo := tbInfo.TiFlashReplica + if pi := tbInfo.GetPartitionInfo(); pi != nil { + logutil.BgLogger().Info("Set TiFlash replica pd rule for partitioned table when creating", zap.Int64("tableID", tbInfo.ID)) + if e := infosync.ConfigureTiFlashPDForPartitions(false, &pi.Definitions, replicaInfo.Count, &replicaInfo.LocationLabels, tbInfo.ID); e != nil { + job.State = model.JobStateCancelled + return tbInfo, errors.Trace(e) + } + // Partitions that in adding mid-state. They have high priorities, so we should set accordingly pd rules. + if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.AddingDefinitions, replicaInfo.Count, &replicaInfo.LocationLabels, tbInfo.ID); e != nil { + job.State = model.JobStateCancelled + return tbInfo, errors.Trace(e) + } + } else { + logutil.BgLogger().Info("Set TiFlash replica pd rule when creating", zap.Int64("tableID", tbInfo.ID)) + if e := infosync.ConfigureTiFlashPDForTable(tbInfo.ID, replicaInfo.Count, &replicaInfo.LocationLabels); e != nil { + job.State = model.JobStateCancelled + return tbInfo, errors.Trace(e) + } + } + } + + bundles, err := placement.NewFullTableBundles(t, tbInfo) + if err != nil { + job.State = model.JobStateCancelled + return tbInfo, errors.Trace(err) + } + + // Send the placement bundle to PD. + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) + if err != nil { + job.State = model.JobStateCancelled + return tbInfo, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + return tbInfo, nil + default: + return tbInfo, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tbInfo.State) + } +} + +func onCreateTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + failpoint.Inject("mockExceedErrorLimit", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(ver, errors.New("mock do job error")) + } + }) + + // just decode, createTable will use it as Args[0] + tbInfo := &model.TableInfo{} + fkCheck := false + if err := job.DecodeArgs(tbInfo, &fkCheck); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if len(tbInfo.ForeignKeys) > 0 { + return createTableWithForeignKeys(d, t, job, tbInfo, fkCheck) + } + + tbInfo, err := createTable(d, t, job, fkCheck) + if err != nil { + return ver, errors.Trace(err) + } + + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateTable, TableInfo: tbInfo}) + return ver, errors.Trace(err) +} + +func createTableWithForeignKeys(d *ddlCtx, t *meta.Meta, job *model.Job, tbInfo *model.TableInfo, fkCheck bool) (ver int64, err error) { + switch tbInfo.State { + case model.StateNone: + // create table in non-public state + tbInfo, err = createTable(d, t, job, fkCheck) + if err != nil { + return ver, errors.Trace(err) + } + tbInfo.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tbInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StateWriteOnly + case model.StateWriteOnly: + tbInfo.State = model.StatePublic + ver, err = updateVersionAndTableInfo(d, t, job, tbInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + return ver, nil + default: + return ver, errors.Trace(dbterror.ErrInvalidDDLJob.GenWithStackByArgs("table", tbInfo.State)) + } + return ver, errors.Trace(err) +} + +func onCreateTables(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { + var ver int64 + + var args []*model.TableInfo + fkCheck := false + err := job.DecodeArgs(&args, &fkCheck) + if err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + // We don't construct jobs for every table, but only tableInfo + // The following loop creates a stub job for every table + // + // &*job clones a stub job from the ActionCreateTables job + stubJob := &*job + stubJob.Args = make([]interface{}, 1) + for i := range args { + stubJob.TableID = args[i].ID + stubJob.Args[0] = args[i] + tbInfo, err := createTable(d, t, stubJob, fkCheck) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + args[i] = tbInfo + } + + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + + job.State = model.JobStateDone + job.SchemaState = model.StatePublic + job.BinlogInfo.SetTableInfos(ver, args) + + for i := range args { + asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateTable, TableInfo: args[i]}) + } + + return ver, errors.Trace(err) +} + +func createTableOrViewWithCheck(t *meta.Meta, job *model.Job, schemaID int64, tbInfo *model.TableInfo) error { + err := checkTableInfoValid(tbInfo) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + return t.CreateTableOrView(schemaID, tbInfo) +} + +func repairTableOrViewWithCheck(t *meta.Meta, job *model.Job, schemaID int64, tbInfo *model.TableInfo) error { + err := checkTableInfoValid(tbInfo) + if err != nil { + job.State = model.JobStateCancelled + return errors.Trace(err) + } + return t.UpdateTable(schemaID, tbInfo) +} + +func onCreateView(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + tbInfo := &model.TableInfo{} + var orReplace bool + var oldTbInfoID int64 + if err := job.DecodeArgs(tbInfo, &orReplace, &oldTbInfoID); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + tbInfo.State = model.StateNone + err := checkTableNotExists(d, t, schemaID, tbInfo.Name.L) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } else if !infoschema.ErrTableExists.Equal(err) { + return ver, errors.Trace(err) + } + if !orReplace { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + switch tbInfo.State { + case model.StateNone: + // none -> public + tbInfo.State = model.StatePublic + tbInfo.UpdateTS = t.StartTS + if oldTbInfoID > 0 && orReplace { + err = t.DropTableOrView(schemaID, oldTbInfoID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + err = t.GetAutoIDAccessors(schemaID, oldTbInfoID).Del() + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + err = createTableOrViewWithCheck(t, job, schemaID, tbInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionCreateView, TableInfo: tbInfo}) + return ver, nil + default: + return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tbInfo.State) + } +} + +func onDropTableOrView(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + tblInfo, err := checkTableExistAndCancelNonExistJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + originalState := job.SchemaState + switch tblInfo.State { + case model.StatePublic: + // public -> write only + if job.Type == model.ActionDropTable { + err = checkDropTableHasForeignKeyReferredInOwner(d, t, job) + if err != nil { + return ver, err + } + } + tblInfo.State = model.StateWriteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateWriteOnly: + // write only -> delete only + tblInfo.State = model.StateDeleteOnly + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + case model.StateDeleteOnly: + tblInfo.State = model.StateNone + oldIDs := getPartitionIDs(tblInfo) + ruleIDs := append(getPartitionRuleIDs(job.SchemaName, tblInfo), fmt.Sprintf(label.TableIDFormat, label.IDPrefix, job.SchemaName, tblInfo.Name.L)) + job.CtxVars = []interface{}{oldIDs} + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, originalState != tblInfo.State) + if err != nil { + return ver, errors.Trace(err) + } + if tblInfo.IsSequence() { + if err = t.DropSequence(job.SchemaID, job.TableID); err != nil { + return ver, errors.Trace(err) + } + } else { + if err = t.DropTableOrView(job.SchemaID, job.TableID); err != nil { + return ver, errors.Trace(err) + } + if err = t.GetAutoIDAccessors(job.SchemaID, job.TableID).Del(); err != nil { + return ver, errors.Trace(err) + } + } + if tblInfo.TiFlashReplica != nil { + e := infosync.DeleteTiFlashTableSyncProgress(tblInfo) + if e != nil { + logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(e)) + } + } + // Placement rules cannot be removed immediately after drop table / truncate table, because the + // tables can be flashed back or recovered, therefore it moved to doGCPlacementRules in gc_worker.go. + + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StateNone, ver, tblInfo) + startKey := tablecodec.EncodeTablePrefix(job.TableID) + job.Args = append(job.Args, startKey, oldIDs, ruleIDs) + if tblInfo.IsSequence() { + asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropSequence, TableInfo: tblInfo}) + } else if !tblInfo.IsView() { + asyncNotifyEvent(d, &util.Event{Tp: model.ActionDropTable, TableInfo: tblInfo}) + } + default: + return ver, errors.Trace(dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State)) + } + job.SchemaState = tblInfo.State + return ver, errors.Trace(err) +} + +func (w *worker) onRecoverTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + var ( + recoverInfo *RecoverInfo + recoverTableCheckFlag int64 + ) + if err = job.DecodeArgs(&recoverInfo, &recoverTableCheckFlag); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + schemaID := recoverInfo.SchemaID + tblInfo := recoverInfo.TableInfo + if tblInfo.TTLInfo != nil { + // force disable TTL job schedule for recovered table + tblInfo.TTLInfo.Enable = false + } + + // check GC and safe point + gcEnable, err := checkGCEnable(w) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = checkTableNotExists(d, t, schemaID, tblInfo.Name.L) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { + job.State = model.JobStateCancelled + } + return ver, errors.Trace(err) + } + + err = checkTableIDNotExists(t, schemaID, tblInfo.ID) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { + job.State = model.JobStateCancelled + } + return ver, errors.Trace(err) + } + + // Recover table divide into 2 steps: + // 1. Check GC enable status, to decided whether enable GC after recover table. + // a. Why not disable GC before put the job to DDL job queue? + // Think about concurrency problem. If a recover job-1 is doing and already disabled GC, + // then, another recover table job-2 check GC enable will get disable before into the job queue. + // then, after recover table job-2 finished, the GC will be disabled. + // b. Why split into 2 steps? 1 step also can finish this job: check GC -> disable GC -> recover table -> finish job. + // What if the transaction commit failed? then, the job will retry, but the GC already disabled when first running. + // So, after this job retry succeed, the GC will be disabled. + // 2. Do recover table job. + // a. Check whether GC enabled, if enabled, disable GC first. + // b. Check GC safe point. If drop table time if after safe point time, then can do recover. + // otherwise, can't recover table, because the records of the table may already delete by gc. + // c. Remove GC task of the table from gc_delete_range table. + // d. Create table and rebase table auto ID. + // e. Finish. + switch tblInfo.State { + case model.StateNone: + // none -> write only + // check GC enable and update flag. + if gcEnable { + job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagEnableGC + } else { + job.Args[checkFlagIndexInJobArgs] = recoverCheckFlagDisableGC + } + + // Clear all placement when recover + err = clearTablePlacementAndBundles(tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + job.SchemaState = model.StateWriteOnly + tblInfo.State = model.StateWriteOnly + case model.StateWriteOnly: + // write only -> public + // do recover table. + if gcEnable { + err = disableGC(w) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Errorf("disable gc failed, try again later. err: %v", err) + } + } + // check GC safe point + err = checkSafePoint(w, recoverInfo.SnapshotTS) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + ver, err = w.recoverTable(t, job, recoverInfo) + if err != nil { + return ver, errors.Trace(err) + } + tableInfo := tblInfo.Clone() + tableInfo.State = model.StatePublic + tableInfo.UpdateTS = t.StartTS + ver, err = updateVersionAndTableInfo(d, t, job, tableInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + tblInfo.State = model.StatePublic + tblInfo.UpdateTS = t.StartTS + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + default: + return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State) + } + return ver, nil +} + +func (w *worker) recoverTable(t *meta.Meta, job *model.Job, recoverInfo *RecoverInfo) (ver int64, err error) { + var tids []int64 + if recoverInfo.TableInfo.GetPartitionInfo() != nil { + tids = getPartitionIDs(recoverInfo.TableInfo) + tids = append(tids, recoverInfo.TableInfo.ID) + } else { + tids = []int64{recoverInfo.TableInfo.ID} + } + tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err := getOldLabelRules(recoverInfo.TableInfo, recoverInfo.OldSchemaName, recoverInfo.OldTableName) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to get old label rules from PD") + } + // Remove dropped table DDL job from gc_delete_range table. + err = w.delRangeManager.removeFromGCDeleteRange(w.ctx, recoverInfo.DropJobID) + if err != nil { + return ver, errors.Trace(err) + } + tableInfo := recoverInfo.TableInfo.Clone() + tableInfo.State = model.StatePublic + tableInfo.UpdateTS = t.StartTS + err = t.CreateTableAndSetAutoID(recoverInfo.SchemaID, tableInfo, recoverInfo.AutoIDs.RowID, recoverInfo.AutoIDs.RandomID) + if err != nil { + return ver, errors.Trace(err) + } + + failpoint.Inject("mockRecoverTableCommitErr", func(val failpoint.Value) { + if val.(bool) && atomic.CompareAndSwapUint32(&mockRecoverTableCommitErrOnce, 0, 1) { + err = failpoint.Enable(`tikvclient/mockCommitErrorOpt`, "return(true)") + if err != nil { + return + } + } + }) + + err = updateLabelRules(job, recoverInfo.TableInfo, oldRules, tableRuleID, partRuleIDs, oldRuleIDs, recoverInfo.TableInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to update the label rule to PD") + } + job.CtxVars = []interface{}{tids} + return ver, nil +} + +func clearTablePlacementAndBundles(tblInfo *model.TableInfo) error { + var bundles []*placement.Bundle + if tblInfo.PlacementPolicyRef != nil { + tblInfo.PlacementPolicyRef = nil + bundles = append(bundles, placement.NewBundle(tblInfo.ID)) + } + + if tblInfo.Partition != nil { + for i := range tblInfo.Partition.Definitions { + par := &tblInfo.Partition.Definitions[i] + if par.PlacementPolicyRef != nil { + par.PlacementPolicyRef = nil + bundles = append(bundles, placement.NewBundle(par.ID)) + } + } + } + + if len(bundles) == 0 { + return nil + } + + return infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) +} + +// mockRecoverTableCommitErrOnce uses to make sure +// `mockRecoverTableCommitErr` only mock error once. +var mockRecoverTableCommitErrOnce uint32 + +func enableGC(w *worker) error { + ctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + return gcutil.EnableGC(ctx) +} + +func disableGC(w *worker) error { + ctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + return gcutil.DisableGC(ctx) +} + +func checkGCEnable(w *worker) (enable bool, err error) { + ctx, err := w.sessPool.Get() + if err != nil { + return false, errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + return gcutil.CheckGCEnable(ctx) +} + +func checkSafePoint(w *worker, snapshotTS uint64) error { + ctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + return gcutil.ValidateSnapshot(ctx, snapshotTS) +} + +func getTable(store kv.Storage, schemaID int64, tblInfo *model.TableInfo) (table.Table, error) { + allocs := autoid.NewAllocatorsFromTblInfo(store, schemaID, tblInfo) + tbl, err := table.TableFromMeta(allocs, tblInfo) + return tbl, errors.Trace(err) +} + +// GetTableInfoAndCancelFaultJob is exported for test. +func GetTableInfoAndCancelFaultJob(t *meta.Meta, job *model.Job, schemaID int64) (*model.TableInfo, error) { + tblInfo, err := checkTableExistAndCancelNonExistJob(t, job, schemaID) + if err != nil { + return nil, errors.Trace(err) + } + + if tblInfo.State != model.StatePublic { + job.State = model.JobStateCancelled + return nil, dbterror.ErrInvalidDDLState.GenWithStack("table %s is not in public, but %s", tblInfo.Name, tblInfo.State) + } + + return tblInfo, nil +} + +func checkTableExistAndCancelNonExistJob(t *meta.Meta, job *model.Job, schemaID int64) (*model.TableInfo, error) { + tblInfo, err := getTableInfo(t, job.TableID, schemaID) + if err == nil { + // Check if table name is renamed. + if job.TableName != "" && tblInfo.Name.L != job.TableName && job.Type != model.ActionRepairTable { + job.State = model.JobStateCancelled + return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(job.SchemaName, job.TableName) + } + return tblInfo, nil + } + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableNotExists.Equal(err) { + job.State = model.JobStateCancelled + } + return nil, err +} + +func getTableInfo(t *meta.Meta, tableID, schemaID int64) (*model.TableInfo, error) { + // Check this table's database. + tblInfo, err := t.GetTable(schemaID, tableID) + if err != nil { + if meta.ErrDBNotExists.Equal(err) { + return nil, errors.Trace(infoschema.ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", schemaID), + )) + } + return nil, errors.Trace(err) + } + + // Check the table. + if tblInfo == nil { + return nil, errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", schemaID), + fmt.Sprintf("(Table ID %d)", tableID), + )) + } + return tblInfo, nil +} + +// onTruncateTable delete old table meta, and creates a new table identical to old table except for table ID. +// As all the old data is encoded with old table ID, it can not be accessed anymore. +// A background job will be created to delete old data. +func (w *worker) onTruncateTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + tableID := job.TableID + var newTableID int64 + var fkCheck bool + var newPartitionIDs []int64 + err := job.DecodeArgs(&newTableID, &fkCheck, &newPartitionIDs) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return ver, errors.Trace(err) + } + if tblInfo.IsView() || tblInfo.IsSequence() { + job.State = model.JobStateCancelled + return ver, infoschema.ErrTableNotExists.GenWithStackByArgs(job.SchemaName, tblInfo.Name.O) + } + err = checkTruncateTableHasForeignKeyReferredInOwner(d, t, job, tblInfo, fkCheck) + if err != nil { + return ver, err + } + err = t.DropTableOrView(schemaID, tblInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + err = t.GetAutoIDAccessors(schemaID, tblInfo.ID).Del() + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + failpoint.Inject("truncateTableErr", func(val failpoint.Value) { + if val.(bool) { + job.State = model.JobStateCancelled + failpoint.Return(ver, errors.New("occur an error after dropping table")) + } + }) + + // Clear the TiFlash replica progress from ETCD. + if tblInfo.TiFlashReplica != nil { + e := infosync.DeleteTiFlashTableSyncProgress(tblInfo) + if e != nil { + logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(e)) + } + } + + var oldPartitionIDs []int64 + if tblInfo.GetPartitionInfo() != nil { + oldPartitionIDs = getPartitionIDs(tblInfo) + // We use the new partition ID because all the old data is encoded with the old partition ID, it can not be accessed anymore. + err = truncateTableByReassignPartitionIDs(t, tblInfo, newPartitionIDs) + if err != nil { + return ver, errors.Trace(err) + } + } + + if pi := tblInfo.GetPartitionInfo(); pi != nil { + oldIDs := make([]int64, 0, len(oldPartitionIDs)) + newIDs := make([]int64, 0, len(oldPartitionIDs)) + newDefs := pi.Definitions + for i := range oldPartitionIDs { + newDef := &newDefs[i] + newID := newDef.ID + if newDef.PlacementPolicyRef != nil { + oldIDs = append(oldIDs, oldPartitionIDs[i]) + newIDs = append(newIDs, newID) + } + } + job.CtxVars = []interface{}{oldIDs, newIDs} + } + + tableRuleID, partRuleIDs, _, oldRules, err := getOldLabelRules(tblInfo, job.SchemaName, tblInfo.Name.L) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Wrapf(err, "failed to get old label rules from PD") + } + + err = updateLabelRules(job, tblInfo, oldRules, tableRuleID, partRuleIDs, []string{}, newTableID) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Wrapf(err, "failed to update the label rule to PD") + } + + // Clear the TiFlash replica available status. + if tblInfo.TiFlashReplica != nil { + // Set PD rules for TiFlash + if pi := tblInfo.GetPartitionInfo(); pi != nil { + if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.Definitions, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels, tblInfo.ID); e != nil { + logutil.BgLogger().Error("ConfigureTiFlashPDForPartitions fails", zap.Error(err)) + job.State = model.JobStateCancelled + return ver, errors.Trace(e) + } + } else { + if e := infosync.ConfigureTiFlashPDForTable(newTableID, tblInfo.TiFlashReplica.Count, &tblInfo.TiFlashReplica.LocationLabels); e != nil { + logutil.BgLogger().Error("ConfigureTiFlashPDForTable fails", zap.Error(err)) + job.State = model.JobStateCancelled + return ver, errors.Trace(e) + } + } + tblInfo.TiFlashReplica.AvailablePartitionIDs = nil + tblInfo.TiFlashReplica.Available = false + } + + tblInfo.ID = newTableID + + // build table & partition bundles if any. + bundles, err := placement.NewFullTableBundles(t, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), bundles) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + err = t.CreateTableOrView(schemaID, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + failpoint.Inject("mockTruncateTableUpdateVersionError", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(ver, errors.New("mock update version error")) + } + }) + + var partitions []model.PartitionDefinition + if pi := tblInfo.GetPartitionInfo(); pi != nil { + partitions = tblInfo.GetPartitionInfo().Definitions + } + preSplitAndScatter(w.sess.Context, d.store, tblInfo, partitions) + + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionTruncateTable, TableInfo: tblInfo}) + startKey := tablecodec.EncodeTablePrefix(tableID) + job.Args = []interface{}{startKey, oldPartitionIDs} + return ver, nil +} + +func onRebaseAutoIncrementIDType(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + return onRebaseAutoID(d, d.store, t, job, autoid.AutoIncrementType) +} + +func onRebaseAutoRandomType(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + return onRebaseAutoID(d, d.store, t, job, autoid.AutoRandomType) +} + +func onRebaseAutoID(d *ddlCtx, store kv.Storage, t *meta.Meta, job *model.Job, tp autoid.AllocatorType) (ver int64, _ error) { + schemaID := job.SchemaID + var ( + newBase int64 + force bool + ) + err := job.DecodeArgs(&newBase, &force) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + return ver, nil + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tbl, err := getTable(store, schemaID, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if !force { + newBaseTemp, err := adjustNewBaseToNextGlobalID(nil, tbl, tp, newBase) + if err != nil { + return ver, errors.Trace(err) + } + if newBase != newBaseTemp { + job.Warning = toTError(fmt.Errorf("Can't reset AUTO_INCREMENT to %d without FORCE option, using %d instead", + newBase, newBaseTemp, + )) + } + newBase = newBaseTemp + } + + if tp == autoid.AutoIncrementType { + tblInfo.AutoIncID = newBase + } else { + tblInfo.AutoRandID = newBase + } + + if alloc := tbl.Allocators(nil).Get(tp); alloc != nil { + // The next value to allocate is `newBase`. + newEnd := newBase - 1 + if force { + err = alloc.ForceRebase(newEnd) + } else { + err = alloc.Rebase(context.Background(), newEnd, false) + } + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func onModifyTableAutoIDCache(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { + var cache int64 + if err := job.DecodeArgs(&cache); err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, errors.Trace(err) + } + + tblInfo.AutoIdCache = cache + ver, err := updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func (w *worker) onShardRowID(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var shardRowIDBits uint64 + err := job.DecodeArgs(&shardRowIDBits) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + if shardRowIDBits < tblInfo.ShardRowIDBits { + tblInfo.ShardRowIDBits = shardRowIDBits + } else { + tbl, err := getTable(d.store, job.SchemaID, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + err = verifyNoOverflowShardBits(w.sessPool, tbl, shardRowIDBits) + if err != nil { + job.State = model.JobStateCancelled + return ver, err + } + tblInfo.ShardRowIDBits = shardRowIDBits + // MaxShardRowIDBits use to check the overflow of auto ID. + tblInfo.MaxShardRowIDBits = shardRowIDBits + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func verifyNoOverflowShardBits(s *sess.Pool, tbl table.Table, shardRowIDBits uint64) error { + if shardRowIDBits == 0 { + return nil + } + ctx, err := s.Get() + if err != nil { + return errors.Trace(err) + } + defer s.Put(ctx) + // Check next global max auto ID first. + autoIncID, err := tbl.Allocators(ctx).Get(autoid.RowIDAllocType).NextGlobalAutoID() + if err != nil { + return errors.Trace(err) + } + if tables.OverflowShardBits(autoIncID, shardRowIDBits, autoid.RowIDBitLength, true) { + return autoid.ErrAutoincReadFailed.GenWithStack("shard_row_id_bits %d will cause next global auto ID %v overflow", shardRowIDBits, autoIncID) + } + return nil +} + +func onRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var oldSchemaID int64 + var oldSchemaName model.CIStr + var tableName model.CIStr + if err := job.DecodeArgs(&oldSchemaID, &tableName, &oldSchemaName); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if job.SchemaState == model.StatePublic { + return finishJobRenameTable(d, t, job) + } + newSchemaID := job.SchemaID + err := checkTableNotExists(d, t, newSchemaID, tableName.L) + if err != nil { + if infoschema.ErrDatabaseNotExists.Equal(err) || infoschema.ErrTableExists.Equal(err) { + job.State = model.JobStateCancelled + } + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, oldSchemaID) + if err != nil { + return ver, errors.Trace(err) + } + oldTableName := tblInfo.Name + ver, err = checkAndRenameTables(t, job, tblInfo, oldSchemaID, job.SchemaID, &oldSchemaName, &tableName) + if err != nil { + return ver, errors.Trace(err) + } + fkh := newForeignKeyHelper() + err = adjustForeignKeyChildTableInfoAfterRenameTable(d, t, job, &fkh, tblInfo, oldSchemaName, oldTableName, tableName, newSchemaID) + if err != nil { + return ver, errors.Trace(err) + } + ver, err = updateSchemaVersion(d, t, job, fkh.getLoadedTables()...) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StatePublic + return ver, nil +} + +func onRenameTables(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + oldSchemaIDs := []int64{} + newSchemaIDs := []int64{} + tableNames := []*model.CIStr{} + tableIDs := []int64{} + oldSchemaNames := []*model.CIStr{} + oldTableNames := []*model.CIStr{} + if err := job.DecodeArgs(&oldSchemaIDs, &newSchemaIDs, &tableNames, &tableIDs, &oldSchemaNames, &oldTableNames); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if job.SchemaState == model.StatePublic { + return finishJobRenameTables(d, t, job, tableNames, tableIDs, newSchemaIDs) + } + + var tblInfos = make([]*model.TableInfo, 0, len(tableNames)) + var err error + fkh := newForeignKeyHelper() + for i, oldSchemaID := range oldSchemaIDs { + job.TableID = tableIDs[i] + job.TableName = oldTableNames[i].L + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, oldSchemaID) + if err != nil { + return ver, errors.Trace(err) + } + ver, err := checkAndRenameTables(t, job, tblInfo, oldSchemaID, newSchemaIDs[i], oldSchemaNames[i], tableNames[i]) + if err != nil { + return ver, errors.Trace(err) + } + err = adjustForeignKeyChildTableInfoAfterRenameTable(d, t, job, &fkh, tblInfo, *oldSchemaNames[i], *oldTableNames[i], *tableNames[i], newSchemaIDs[i]) + if err != nil { + return ver, errors.Trace(err) + } + tblInfos = append(tblInfos, tblInfo) + } + + ver, err = updateSchemaVersion(d, t, job, fkh.getLoadedTables()...) + if err != nil { + return ver, errors.Trace(err) + } + job.SchemaState = model.StatePublic + return ver, nil +} + +func checkAndRenameTables(t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, oldSchemaID, newSchemaID int64, oldSchemaName, tableName *model.CIStr) (ver int64, _ error) { + err := t.DropTableOrView(oldSchemaID, tblInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + failpoint.Inject("renameTableErr", func(val failpoint.Value) { + if valStr, ok := val.(string); ok { + if tableName.L == valStr { + job.State = model.JobStateCancelled + failpoint.Return(ver, errors.New("occur an error after renaming table")) + } + } + }) + + oldTableName := tblInfo.Name + tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err := getOldLabelRules(tblInfo, oldSchemaName.L, oldTableName.L) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to get old label rules from PD") + } + + tblInfo.Name = *tableName + err = t.CreateTableOrView(newSchemaID, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if newSchemaID != oldSchemaID { + oldDBID := tblInfo.GetDBID(oldSchemaID) + err := meta.BackupAndRestoreAutoIDs(t, oldDBID, tblInfo.ID, newSchemaID, tblInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // It's compatible with old version. + // TODO: Remove it. + tblInfo.OldSchemaID = 0 + } + + err = updateLabelRules(job, tblInfo, oldRules, tableRuleID, partRuleIDs, oldRuleIDs, tblInfo.ID) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to update the label rule to PD") + } + + return ver, nil +} + +func adjustForeignKeyChildTableInfoAfterRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job, fkh *foreignKeyHelper, tblInfo *model.TableInfo, oldSchemaName, oldTableName, newTableName model.CIStr, newSchemaID int64) error { + if !variable.EnableForeignKey.Load() || newTableName.L == oldTableName.L { + return nil + } + is, err := getAndCheckLatestInfoSchema(d, t) + if err != nil { + return err + } + newDB, ok := is.SchemaByID(newSchemaID) + if !ok { + job.State = model.JobStateCancelled + return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(fmt.Sprintf("schema-ID: %v", newSchemaID)) + } + referredFKs := is.GetTableReferredForeignKeys(oldSchemaName.L, oldTableName.L) + if len(referredFKs) == 0 { + return nil + } + fkh.addLoadedTable(oldSchemaName.L, oldTableName.L, newDB.ID, tblInfo) + for _, referredFK := range referredFKs { + childTableInfo, err := fkh.getTableFromStorage(is, t, referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + if infoschema.ErrTableNotExists.Equal(err) || infoschema.ErrDatabaseNotExists.Equal(err) { + continue + } + return err + } + childFKInfo := model.FindFKInfoByName(childTableInfo.tblInfo.ForeignKeys, referredFK.ChildFKName.L) + if childFKInfo == nil { + continue + } + childFKInfo.RefSchema = newDB.Name + childFKInfo.RefTable = newTableName + } + for _, info := range fkh.loaded { + err = updateTable(t, info.schemaID, info.tblInfo) + if err != nil { + return err + } + } + return nil +} + +// We split the renaming table job into two steps: +// 1. rename table and update the schema version. +// 2. update the job state to JobStateDone. +// This is the requirement from TiCDC because +// - it uses the job state to check whether the DDL is finished. +// - there is a gap between schema reloading and job state updating: +// when the job state is updated to JobStateDone, before the new schema reloaded, +// there may be DMLs that use the old schema. +// - TiCDC cannot handle the DMLs that use the old schema, because +// the commit TS of the DMLs are greater than the job state updating TS. +func finishJobRenameTable(d *ddlCtx, t *meta.Meta, job *model.Job) (int64, error) { + tblInfo, err := getTableInfo(t, job.TableID, job.SchemaID) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + // Before updating the schema version, we need to reset the old schema ID to new schema ID, so that + // the table info can be dropped normally in `ApplyDiff`. This is because renaming table requires two + // schema versions to complete. + oldRawArgs := job.RawArgs + job.Args[0] = job.SchemaID + job.RawArgs, err = json.Marshal(job.Args) + if err != nil { + return 0, errors.Trace(err) + } + ver, err := updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + job.RawArgs = oldRawArgs + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func finishJobRenameTables(d *ddlCtx, t *meta.Meta, job *model.Job, + tableNames []*model.CIStr, tableIDs, newSchemaIDs []int64) (int64, error) { + tblSchemaIDs := make(map[int64]int64, len(tableIDs)) + for i := range tableIDs { + tblSchemaIDs[tableIDs[i]] = newSchemaIDs[i] + } + tblInfos := make([]*model.TableInfo, 0, len(tableNames)) + for i := range tableIDs { + tblID := tableIDs[i] + tblInfo, err := getTableInfo(t, tblID, tblSchemaIDs[tblID]) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + tblInfos = append(tblInfos, tblInfo) + } + // Before updating the schema version, we need to reset the old schema ID to new schema ID, so that + // the table info can be dropped normally in `ApplyDiff`. This is because renaming table requires two + // schema versions to complete. + var err error + oldRawArgs := job.RawArgs + job.Args[0] = newSchemaIDs + job.RawArgs, err = json.Marshal(job.Args) + if err != nil { + return 0, errors.Trace(err) + } + ver, err := updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + job.RawArgs = oldRawArgs + job.FinishMultipleTableJob(model.JobStateDone, model.StatePublic, ver, tblInfos) + return ver, nil +} + +func onModifyTableComment(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var comment string + if err := job.DecodeArgs(&comment); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + return ver, nil + } + + tblInfo.Comment = comment + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func onModifyTableCharsetAndCollate(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var toCharset, toCollate string + var needsOverwriteCols bool + if err := job.DecodeArgs(&toCharset, &toCollate, &needsOverwriteCols); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + dbInfo, err := checkSchemaExistAndCancelNotExistJob(t, job) + if err != nil { + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + // double check. + _, err = checkAlterTableCharset(tblInfo, dbInfo, toCharset, toCollate, needsOverwriteCols) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if job.MultiSchemaInfo != nil && job.MultiSchemaInfo.Revertible { + job.MarkNonRevertible() + return ver, nil + } + + tblInfo.Charset = toCharset + tblInfo.Collate = toCollate + + if needsOverwriteCols { + // update column charset. + for _, col := range tblInfo.Columns { + if field_types.HasCharset(&col.FieldType) { + col.SetCharset(toCharset) + col.SetCollate(toCollate) + } else { + col.SetCharset(charset.CharsetBin) + col.SetCollate(charset.CharsetBin) + } + } + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func (w *worker) onSetTableFlashReplica(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var replicaInfo ast.TiFlashReplicaSpec + if err := job.DecodeArgs(&replicaInfo); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + // Ban setting replica count for tables in system database. + if tidb_util.IsMemOrSysDB(job.SchemaName) { + return ver, errors.Trace(dbterror.ErrUnsupportedTiFlashOperationForSysOrMemTable) + } + + err = w.checkTiFlashReplicaCount(replicaInfo.Count) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + // We should check this first, in order to avoid creating redundant DDL jobs. + if pi := tblInfo.GetPartitionInfo(); pi != nil { + logutil.BgLogger().Info("Set TiFlash replica pd rule for partitioned table", zap.Int64("tableID", tblInfo.ID)) + if e := infosync.ConfigureTiFlashPDForPartitions(false, &pi.Definitions, replicaInfo.Count, &replicaInfo.Labels, tblInfo.ID); e != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(e) + } + // Partitions that in adding mid-state. They have high priorities, so we should set accordingly pd rules. + if e := infosync.ConfigureTiFlashPDForPartitions(true, &pi.AddingDefinitions, replicaInfo.Count, &replicaInfo.Labels, tblInfo.ID); e != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(e) + } + } else { + logutil.BgLogger().Info("Set TiFlash replica pd rule", zap.Int64("tableID", tblInfo.ID)) + if e := infosync.ConfigureTiFlashPDForTable(tblInfo.ID, replicaInfo.Count, &replicaInfo.Labels); e != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(e) + } + } + + available := false + if tblInfo.TiFlashReplica != nil { + available = tblInfo.TiFlashReplica.Available + } + if replicaInfo.Count > 0 { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: replicaInfo.Count, + LocationLabels: replicaInfo.Labels, + Available: available, + } + } else { + if tblInfo.TiFlashReplica != nil { + err = infosync.DeleteTiFlashTableSyncProgress(tblInfo) + if err != nil { + logutil.BgLogger().Error("DeleteTiFlashTableSyncProgress fails", zap.Error(err)) + } + } + tblInfo.TiFlashReplica = nil + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func (w *worker) checkTiFlashReplicaCount(replicaCount uint64) error { + ctx, err := w.sessPool.Get() + if err != nil { + return errors.Trace(err) + } + defer w.sessPool.Put(ctx) + + return checkTiFlashReplicaCount(ctx, replicaCount) +} + +func onUpdateFlashReplicaStatus(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + var available bool + var physicalID int64 + if err := job.DecodeArgs(&available, &physicalID); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + if tblInfo.TiFlashReplica == nil || (tblInfo.ID == physicalID && tblInfo.TiFlashReplica.Available == available) || + (tblInfo.ID != physicalID && available == tblInfo.TiFlashReplica.IsPartitionAvailable(physicalID)) { + job.State = model.JobStateCancelled + return ver, errors.Errorf("the replica available status of table %s is already updated", tblInfo.Name.String()) + } + + if tblInfo.ID == physicalID { + tblInfo.TiFlashReplica.Available = available + } else if pi := tblInfo.GetPartitionInfo(); pi != nil { + // Partition replica become available. + if available { + allAvailable := true + for _, p := range pi.Definitions { + if p.ID == physicalID { + tblInfo.TiFlashReplica.AvailablePartitionIDs = append(tblInfo.TiFlashReplica.AvailablePartitionIDs, physicalID) + } + allAvailable = allAvailable && tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID) + } + tblInfo.TiFlashReplica.Available = allAvailable + } else { + // Partition replica become unavailable. + for i, id := range tblInfo.TiFlashReplica.AvailablePartitionIDs { + if id == physicalID { + newIDs := tblInfo.TiFlashReplica.AvailablePartitionIDs[:i] + newIDs = append(newIDs, tblInfo.TiFlashReplica.AvailablePartitionIDs[i+1:]...) + tblInfo.TiFlashReplica.AvailablePartitionIDs = newIDs + tblInfo.TiFlashReplica.Available = false + logutil.BgLogger().Info("TiFlash replica become unavailable", zap.Int64("tableID", tblInfo.ID), zap.Int64("partitionID", id)) + break + } + } + } + } else { + job.State = model.JobStateCancelled + return ver, errors.Errorf("unknown physical ID %v in table %v", physicalID, tblInfo.Name.O) + } + + if tblInfo.TiFlashReplica.Available { + logutil.BgLogger().Info("TiFlash replica available", zap.Int64("tableID", tblInfo.ID)) + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func checkTableNotExists(d *ddlCtx, t *meta.Meta, schemaID int64, tableName string) error { + // Try to use memory schema info to check first. + currVer, err := t.GetSchemaVersion() + if err != nil { + return err + } + is := d.infoCache.GetLatest() + if is.SchemaMetaVersion() == currVer { + return checkTableNotExistsFromInfoSchema(is, schemaID, tableName) + } + + return checkTableNotExistsFromStore(t, schemaID, tableName) +} + +func checkTableIDNotExists(t *meta.Meta, schemaID, tableID int64) error { + tbl, err := t.GetTable(schemaID, tableID) + if err != nil { + if meta.ErrDBNotExists.Equal(err) { + return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") + } + return errors.Trace(err) + } + if tbl != nil { + return infoschema.ErrTableExists.GenWithStackByArgs(tbl.Name) + } + return nil +} + +func checkTableNotExistsFromInfoSchema(is infoschema.InfoSchema, schemaID int64, tableName string) error { + // Check this table's database. + schema, ok := is.SchemaByID(schemaID) + if !ok { + return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") + } + if is.TableExists(schema.Name, model.NewCIStr(tableName)) { + return infoschema.ErrTableExists.GenWithStackByArgs(tableName) + } + return nil +} + +func checkTableNotExistsFromStore(t *meta.Meta, schemaID int64, tableName string) error { + // Check this table's database. + tbls, err := t.ListTables(schemaID) + if err != nil { + if meta.ErrDBNotExists.Equal(err) { + return infoschema.ErrDatabaseNotExists.GenWithStackByArgs("") + } + return errors.Trace(err) + } + + // Check the table. + for _, tbl := range tbls { + if tbl.Name.L == tableName { + return infoschema.ErrTableExists.GenWithStackByArgs(tbl.Name) + } + } + + return nil +} + +// updateVersionAndTableInfoWithCheck checks table info validate and updates the schema version and the table information +func updateVersionAndTableInfoWithCheck(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, shouldUpdateVer bool, multiInfos ...schemaIDAndTableInfo) ( + ver int64, err error) { + err = checkTableInfoValid(tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + for _, info := range multiInfos { + err = checkTableInfoValid(info.tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + } + return updateVersionAndTableInfo(d, t, job, tblInfo, shouldUpdateVer, multiInfos...) +} + +// updateVersionAndTableInfo updates the schema version and the table information. +func updateVersionAndTableInfo(d *ddlCtx, t *meta.Meta, job *model.Job, tblInfo *model.TableInfo, shouldUpdateVer bool, multiInfos ...schemaIDAndTableInfo) ( + ver int64, err error) { + failpoint.Inject("mockUpdateVersionAndTableInfoErr", func(val failpoint.Value) { + switch val.(int) { + case 1: + failpoint.Return(ver, errors.New("mock update version and tableInfo error")) + case 2: + // We change it cancelled directly here, because we want to get the original error with the job id appended. + // The job ID will be used to get the job from history queue and we will assert it's args. + job.State = model.JobStateCancelled + failpoint.Return(ver, errors.New("mock update version and tableInfo error, jobID="+strconv.Itoa(int(job.ID)))) + default: + } + }) + if shouldUpdateVer && (job.MultiSchemaInfo == nil || !job.MultiSchemaInfo.SkipVersion) { + ver, err = updateSchemaVersion(d, t, job, multiInfos...) + if err != nil { + return 0, errors.Trace(err) + } + } + + err = updateTable(t, job.SchemaID, tblInfo) + if err != nil { + return 0, errors.Trace(err) + } + for _, info := range multiInfos { + err = updateTable(t, info.schemaID, info.tblInfo) + if err != nil { + return 0, errors.Trace(err) + } + } + return ver, nil +} + +func updateTable(t *meta.Meta, schemaID int64, tblInfo *model.TableInfo) error { + if tblInfo.State == model.StatePublic { + tblInfo.UpdateTS = t.StartTS + } + return t.UpdateTable(schemaID, tblInfo) +} + +type schemaIDAndTableInfo struct { + schemaID int64 + tblInfo *model.TableInfo +} + +func onRepairTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, _ error) { + schemaID := job.SchemaID + tblInfo := &model.TableInfo{} + + if err := job.DecodeArgs(tblInfo); err != nil { + // Invalid arguments, cancel this job. + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tblInfo.State = model.StateNone + + // Check the old DB and old table exist. + _, err := GetTableInfoAndCancelFaultJob(t, job, schemaID) + if err != nil { + return ver, errors.Trace(err) + } + + // When in repair mode, the repaired table in a server is not access to user, + // the table after repairing will be removed from repair list. Other server left + // behind alive may need to restart to get the latest schema version. + ver, err = updateSchemaVersion(d, t, job) + if err != nil { + return ver, errors.Trace(err) + } + switch tblInfo.State { + case model.StateNone: + // none -> public + tblInfo.State = model.StatePublic + tblInfo.UpdateTS = t.StartTS + err = repairTableOrViewWithCheck(t, job, schemaID, tblInfo) + if err != nil { + return ver, errors.Trace(err) + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + asyncNotifyEvent(d, &util.Event{Tp: model.ActionRepairTable, TableInfo: tblInfo}) + return ver, nil + default: + return ver, dbterror.ErrInvalidDDLState.GenWithStackByArgs("table", tblInfo.State) + } +} + +func onAlterTableAttributes(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + rule := label.NewRule() + err = job.DecodeArgs(rule) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, err + } + + if len(rule.Labels) == 0 { + patch := label.NewRulePatch([]*label.Rule{}, []string{rule.ID}) + err = infosync.UpdateLabelRules(context.TODO(), patch) + } else { + err = infosync.PutLabelRule(context.TODO(), rule) + } + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Wrapf(err, "failed to notify PD the label rules") + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + + return ver, nil +} + +func onAlterTablePartitionAttributes(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + var partitionID int64 + rule := label.NewRule() + err = job.DecodeArgs(&partitionID, rule) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, err + } + + ptInfo := tblInfo.GetPartitionInfo() + if ptInfo.GetNameByID(partitionID) == "" { + job.State = model.JobStateCancelled + return 0, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O)) + } + + if len(rule.Labels) == 0 { + patch := label.NewRulePatch([]*label.Rule{}, []string{rule.ID}) + err = infosync.UpdateLabelRules(context.TODO(), patch) + } else { + err = infosync.PutLabelRule(context.TODO(), rule) + } + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Wrapf(err, "failed to notify PD the label rules") + } + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + + return ver, nil +} + +func onAlterTablePartitionPlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + var partitionID int64 + policyRefInfo := &model.PolicyRefInfo{} + err = job.DecodeArgs(&partitionID, &policyRefInfo) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, err + } + + ptInfo := tblInfo.GetPartitionInfo() + var partitionDef *model.PartitionDefinition + definitions := ptInfo.Definitions + oldPartitionEnablesPlacement := false + for i := range definitions { + if partitionID == definitions[i].ID { + def := &definitions[i] + oldPartitionEnablesPlacement = def.PlacementPolicyRef != nil + def.PlacementPolicyRef = policyRefInfo + partitionDef = &definitions[i] + break + } + } + + if partitionDef == nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs("drop?", tblInfo.Name.O)) + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, partitionDef.PlacementPolicyRef); err != nil { + return ver, errors.Trace(err) + } + + bundle, err := placement.NewPartitionBundle(t, *partitionDef) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if bundle == nil && oldPartitionEnablesPlacement { + bundle = placement.NewBundle(partitionDef.ID) + } + + // Send the placement bundle to PD. + if bundle != nil { + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), []*placement.Bundle{bundle}) + } + + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Wrapf(err, "failed to notify PD the placement rules") + } + + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func onAlterTablePlacement(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + policyRefInfo := &model.PolicyRefInfo{} + err = job.DecodeArgs(&policyRefInfo) + if err != nil { + job.State = model.JobStateCancelled + return 0, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, err + } + + if _, err = checkPlacementPolicyRefValidAndCanNonValidJob(t, job, policyRefInfo); err != nil { + return 0, errors.Trace(err) + } + + oldTableEnablesPlacement := tblInfo.PlacementPolicyRef != nil + tblInfo.PlacementPolicyRef = policyRefInfo + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + + bundle, err := placement.NewTableBundle(t, tblInfo) + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + if bundle == nil && oldTableEnablesPlacement { + bundle = placement.NewBundle(tblInfo.ID) + } + + // Send the placement bundle to PD. + if bundle != nil { + err = infosync.PutRuleBundlesWithDefaultRetry(context.TODO(), []*placement.Bundle{bundle}) + } + + if err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + + return ver, nil +} + +func getOldLabelRules(tblInfo *model.TableInfo, oldSchemaName, oldTableName string) (string, []string, []string, map[string]*label.Rule, error) { + tableRuleID := fmt.Sprintf(label.TableIDFormat, label.IDPrefix, oldSchemaName, oldTableName) + oldRuleIDs := []string{tableRuleID} + var partRuleIDs []string + if tblInfo.GetPartitionInfo() != nil { + for _, def := range tblInfo.GetPartitionInfo().Definitions { + partRuleIDs = append(partRuleIDs, fmt.Sprintf(label.PartitionIDFormat, label.IDPrefix, oldSchemaName, oldTableName, def.Name.L)) + } + } + + oldRuleIDs = append(oldRuleIDs, partRuleIDs...) + oldRules, err := infosync.GetLabelRules(context.TODO(), oldRuleIDs) + return tableRuleID, partRuleIDs, oldRuleIDs, oldRules, err +} + +func updateLabelRules(job *model.Job, tblInfo *model.TableInfo, oldRules map[string]*label.Rule, tableRuleID string, partRuleIDs, oldRuleIDs []string, tID int64) error { + if oldRules == nil { + return nil + } + var newRules []*label.Rule + if tblInfo.GetPartitionInfo() != nil { + for idx, def := range tblInfo.GetPartitionInfo().Definitions { + if r, ok := oldRules[partRuleIDs[idx]]; ok { + newRules = append(newRules, r.Clone().Reset(job.SchemaName, tblInfo.Name.L, def.Name.L, def.ID)) + } + } + } + ids := []int64{tID} + if r, ok := oldRules[tableRuleID]; ok { + if tblInfo.GetPartitionInfo() != nil { + for _, def := range tblInfo.GetPartitionInfo().Definitions { + ids = append(ids, def.ID) + } + } + newRules = append(newRules, r.Clone().Reset(job.SchemaName, tblInfo.Name.L, "", ids...)) + } + + patch := label.NewRulePatch(newRules, oldRuleIDs) + return infosync.UpdateLabelRules(context.TODO(), patch) +} + +func onAlterCacheTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + tbInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, errors.Trace(err) + } + // If the table is already in the cache state + if tbInfo.TableCacheStatusType == model.TableCacheStatusEnable { + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + return ver, nil + } + + if tbInfo.TempTableType != model.TempTableNone { + return ver, errors.Trace(dbterror.ErrOptOnTemporaryTable.GenWithStackByArgs("alter temporary table cache")) + } + + if tbInfo.Partition != nil { + return ver, errors.Trace(dbterror.ErrOptOnCacheTable.GenWithStackByArgs("partition mode")) + } + + switch tbInfo.TableCacheStatusType { + case model.TableCacheStatusDisable: + // disable -> switching + tbInfo.TableCacheStatusType = model.TableCacheStatusSwitching + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) + if err != nil { + return ver, err + } + case model.TableCacheStatusSwitching: + // switching -> enable + tbInfo.TableCacheStatusType = model.TableCacheStatusEnable + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) + if err != nil { + return ver, err + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + default: + job.State = model.JobStateCancelled + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("alter table cache", tbInfo.TableCacheStatusType.String()) + } + return ver, err +} + +func onAlterNoCacheTable(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + tbInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return 0, errors.Trace(err) + } + // If the table is not in the cache state + if tbInfo.TableCacheStatusType == model.TableCacheStatusDisable { + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + return ver, nil + } + + switch tbInfo.TableCacheStatusType { + case model.TableCacheStatusEnable: + // enable -> switching + tbInfo.TableCacheStatusType = model.TableCacheStatusSwitching + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) + if err != nil { + return ver, err + } + case model.TableCacheStatusSwitching: + // switching -> disable + tbInfo.TableCacheStatusType = model.TableCacheStatusDisable + ver, err = updateVersionAndTableInfoWithCheck(d, t, job, tbInfo, true) + if err != nil { + return ver, err + } + // Finish this job. + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tbInfo) + default: + job.State = model.JobStateCancelled + err = dbterror.ErrInvalidDDLState.GenWithStackByArgs("alter table no cache", tbInfo.TableCacheStatusType.String()) + } + return ver, err +} diff --git a/ddl/table_lock.go b/pkg/ddl/table_lock.go similarity index 97% rename from ddl/table_lock.go rename to pkg/ddl/table_lock.go index f6dc766768a7f..0a60ab62d9681 100644 --- a/ddl/table_lock.go +++ b/pkg/ddl/table_lock.go @@ -16,10 +16,10 @@ package ddl import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/dbterror" ) func onLockTables(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { diff --git a/ddl/table_modify_test.go b/pkg/ddl/table_modify_test.go similarity index 93% rename from ddl/table_modify_test.go rename to pkg/ddl/table_modify_test.go index b7781a5fef4a1..975c09f518a55 100644 --- a/ddl/table_modify_test.go +++ b/pkg/ddl/table_modify_test.go @@ -19,17 +19,17 @@ import ( "testing" "time" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/ddl/table_split_test.go b/pkg/ddl/table_split_test.go similarity index 91% rename from ddl/table_split_test.go rename to pkg/ddl/table_split_test.go index ad944215a6550..0540f481f5e67 100644 --- a/ddl/table_split_test.go +++ b/pkg/ddl/table_split_test.go @@ -20,12 +20,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" ) diff --git a/pkg/ddl/table_test.go b/pkg/ddl/table_test.go new file mode 100644 index 0000000000000..a3a8bb5d39d5b --- /dev/null +++ b/pkg/ddl/table_test.go @@ -0,0 +1,580 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testRenameTable( + t *testing.T, + ctx sessionctx.Context, + d ddl.DDL, + newSchemaID, oldSchemaID int64, + oldSchemaName model.CIStr, + tblInfo *model.TableInfo, +) *model.Job { + job := &model.Job{ + SchemaID: newSchemaID, + TableID: tblInfo.ID, + Type: model.ActionRenameTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{oldSchemaID, tblInfo.Name, oldSchemaName}, + CtxVars: []interface{}{[]int64{oldSchemaID, newSchemaID}, []int64{tblInfo.ID}}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v := getSchemaVer(t, ctx) + tblInfo.State = model.StatePublic + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + tblInfo.State = model.StateNone + return job +} + +func testRenameTables(t *testing.T, ctx sessionctx.Context, d ddl.DDL, oldSchemaIDs, newSchemaIDs []int64, newTableNames []*model.CIStr, oldTableIDs []int64, oldSchemaNames, oldTableNames []*model.CIStr) *model.Job { + job := &model.Job{ + Type: model.ActionRenameTables, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{oldSchemaIDs, newSchemaIDs, newTableNames, oldTableIDs, oldSchemaNames, oldTableNames}, + CtxVars: []interface{}{append(oldSchemaIDs, newSchemaIDs...), oldTableIDs}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) + return job +} + +func testLockTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo, lockTp model.TableLockType) *model.Job { + arg := &ddl.LockTablesArg{ + LockTables: []model.TableLockTpInfo{{SchemaID: newSchemaID, TableID: tblInfo.ID, Tp: lockTp}}, + SessionInfo: model.SessionInfo{ + ServerID: d.GetID(), + SessionID: ctx.GetSessionVars().ConnectionID, + }, + } + job := &model.Job{ + SchemaID: newSchemaID, + TableID: tblInfo.ID, + Type: model.ActionLockTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{arg}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) + return job +} + +func checkTableLockedTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo, serverID string, sessionID uint64, lockTp model.TableLockType) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + tt := meta.NewMeta(txn) + info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) + require.NoError(t, err) + + require.NotNil(t, info) + require.NotNil(t, info.Lock) + require.Len(t, info.Lock.Sessions, 1) + require.Equal(t, serverID, info.Lock.Sessions[0].ServerID) + require.Equal(t, sessionID, info.Lock.Sessions[0].SessionID) + require.Equal(t, lockTp, info.Lock.Tp) + require.Equal(t, lockTp, info.Lock.Tp) + require.Equal(t, model.TableLockStatePublic, info.Lock.State) + return nil + }) + require.NoError(t, err) +} + +func testTruncateTable(t *testing.T, ctx sessionctx.Context, store kv.Storage, d ddl.DDL, dbInfo *model.DBInfo, tblInfo *model.TableInfo) *model.Job { + genIDs, err := genGlobalIDs(store, 1) + require.NoError(t, err) + newTableID := genIDs[0] + job := &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionTruncateTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{newTableID}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v := getSchemaVer(t, ctx) + tblInfo.ID = newTableID + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: tblInfo}) + return job +} + +func testGetTableWithError(store kv.Storage, schemaID, tableID int64) (table.Table, error) { + var tblInfo *model.TableInfo + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + var err1 error + tblInfo, err1 = t.GetTable(schemaID, tableID) + if err1 != nil { + return errors.Trace(err1) + } + return nil + }) + if err != nil { + return nil, errors.Trace(err) + } + if tblInfo == nil { + return nil, errors.New("table not found") + } + alloc := autoid.NewAllocator(store, schemaID, tblInfo.ID, false, autoid.RowIDAllocType) + tbl, err := table.TableFromMeta(autoid.NewAllocators(false, alloc), tblInfo) + if err != nil { + return nil, errors.Trace(err) + } + return tbl, nil +} + +func TestTable(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + dbInfo, err := testSchemaInfo(store, "test_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), domain.DDL(), dbInfo) + + ctx := testkit.NewTestKit(t, store).Session() + + tblInfo, err := testTableInfo(store, "t", 3) + require.NoError(t, err) + job := testCreateTable(t, ctx, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // Create an existing table. + newTblInfo, err := testTableInfo(store, "t", 3) + require.NoError(t, err) + doDDLJobErr(t, dbInfo.ID, newTblInfo.ID, model.ActionCreateTable, []interface{}{newTblInfo}, ctx, d, store) + + ctx = testkit.NewTestKit(t, store).Session() + require.NoError(t, sessiontxn.NewTxn(context.Background(), ctx)) + count := 2000 + tbl := testGetTable(t, domain, tblInfo.ID) + for i := 1; i <= count; i++ { + _, err := tbl.AddRecord(ctx, types.MakeDatums(i, i, i)) + require.NoError(t, err) + } + require.NoError(t, ctx.CommitTxn(context.Background())) + + jobID := testDropTable(testkit.NewTestKit(t, store), t, dbInfo.Name.L, tblInfo.Name.L, domain) + testCheckJobDone(t, store, jobID, false) + + // for truncate table + tblInfo, err = testTableInfo(store, "tt", 3) + require.NoError(t, err) + job = testCreateTable(t, ctx, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + job = testTruncateTable(t, ctx, store, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // for rename table + dbInfo1, err := testSchemaInfo(store, "test_rename_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo1) + job = testRenameTable(t, ctx, d, dbInfo1.ID, dbInfo.ID, dbInfo.Name, tblInfo) + testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + job = testLockTable(t, ctx, d, dbInfo1.ID, tblInfo, model.TableLockWrite) + testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + checkTableLockedTest(t, store, dbInfo1, tblInfo, d.GetID(), ctx.GetSessionVars().ConnectionID, model.TableLockWrite) + // for alter cache table + job = testAlterCacheTable(t, ctx, d, dbInfo1.ID, tblInfo) + testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + checkTableCacheTest(t, store, dbInfo1, tblInfo) + // for alter no cache table + job = testAlterNoCacheTable(t, ctx, d, dbInfo1.ID, tblInfo) + testCheckTableState(t, store, dbInfo1, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + checkTableNoCacheTest(t, store, dbInfo1, tblInfo) + + testDropSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) +} + +func TestCreateView(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + dbInfo, err := testSchemaInfo(store, "test_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), domain.DDL(), dbInfo) + + ctx := testkit.NewTestKit(t, store).Session() + + tblInfo, err := testTableInfo(store, "t", 3) + require.NoError(t, err) + job := testCreateTable(t, ctx, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // Create a view + newTblInfo0, err := testTableInfo(store, "v", 3) + require.NoError(t, err) + job = &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionCreateView, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{newTblInfo0}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v := getSchemaVer(t, ctx) + tblInfo.State = model.StatePublic + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: newTblInfo0}) + tblInfo.State = model.StateNone + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // Replace a view + newTblInfo1, err := testTableInfo(store, "v", 3) + require.NoError(t, err) + job = &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionCreateView, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{newTblInfo1, true, newTblInfo0.ID}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v = getSchemaVer(t, ctx) + tblInfo.State = model.StatePublic + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: newTblInfo1}) + tblInfo.State = model.StateNone + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // Replace a view with a non-existing table id + newTblInfo2, err := testTableInfo(store, "v", 3) + require.NoError(t, err) + job = &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionCreateView, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{newTblInfo2, true, newTblInfo0.ID}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.Error(t, err) +} + +func checkTableCacheTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + tt := meta.NewMeta(txn) + info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) + require.NoError(t, err) + require.NotNil(t, info) + require.NotNil(t, info.TableCacheStatusType) + require.Equal(t, model.TableCacheStatusEnable, info.TableCacheStatusType) + return nil + })) +} + +func checkTableNoCacheTest(t *testing.T, store kv.Storage, dbInfo *model.DBInfo, tblInfo *model.TableInfo) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + tt := meta.NewMeta(txn) + info, err := tt.GetTable(dbInfo.ID, tblInfo.ID) + require.NoError(t, err) + require.NotNil(t, info) + require.Equal(t, model.TableCacheStatusDisable, info.TableCacheStatusType) + return nil + })) +} + +func testAlterCacheTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { + job := &model.Job{ + SchemaID: newSchemaID, + TableID: tblInfo.ID, + Type: model.ActionAlterCacheTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err := d.DoDDLJob(ctx, job) + require.NoError(t, err) + + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) + return job +} + +func testAlterNoCacheTable(t *testing.T, ctx sessionctx.Context, d ddl.DDL, newSchemaID int64, tblInfo *model.TableInfo) *model.Job { + job := &model.Job{ + SchemaID: newSchemaID, + TableID: tblInfo.ID, + Type: model.ActionAlterNoCacheTable, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v}) + return job +} + +func TestRenameTables(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + + dbInfo, err := testSchemaInfo(store, "test_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) + + ctx := testkit.NewTestKit(t, store).Session() + var tblInfos = make([]*model.TableInfo, 0, 2) + var newTblInfos = make([]*model.TableInfo, 0, 2) + for i := 1; i < 3; i++ { + tableName := fmt.Sprintf("t%d", i) + tblInfo, err := testTableInfo(store, tableName, 3) + require.NoError(t, err) + job := testCreateTable(t, ctx, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + tblInfos = append(tblInfos, tblInfo) + + newTableName := fmt.Sprintf("tt%d", i) + tblInfo, err = testTableInfo(store, newTableName, 3) + require.NoError(t, err) + newTblInfos = append(newTblInfos, tblInfo) + } + + job := testRenameTables(t, ctx, d, []int64{dbInfo.ID, dbInfo.ID}, []int64{dbInfo.ID, dbInfo.ID}, []*model.CIStr{&newTblInfos[0].Name, &newTblInfos[1].Name}, []int64{tblInfos[0].ID, tblInfos[1].ID}, []*model.CIStr{&dbInfo.Name, &dbInfo.Name}, []*model.CIStr{&tblInfos[0].Name, &tblInfos[1].Name}) + + historyJob, err := ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) + require.NoError(t, err) + wantTblInfos := historyJob.BinlogInfo.MultipleTableInfos + require.Equal(t, wantTblInfos[0].Name.L, "tt1") + require.Equal(t, wantTblInfos[1].Name.L, "tt2") +} + +func TestCreateTables(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + + dbInfo, err := testSchemaInfo(store, "test_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) + + ctx := testkit.NewTestKit(t, store).Session() + + var infos []*model.TableInfo + genIDs, err := genGlobalIDs(store, 3) + require.NoError(t, err) + + infos = append(infos, &model.TableInfo{ + ID: genIDs[0], + Name: model.NewCIStr("s1"), + }) + infos = append(infos, &model.TableInfo{ + ID: genIDs[1], + Name: model.NewCIStr("s2"), + }) + infos = append(infos, &model.TableInfo{ + ID: genIDs[2], + Name: model.NewCIStr("s3"), + }) + + job := &model.Job{ + SchemaID: dbInfo.ID, + Type: model.ActionCreateTables, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{infos}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + err = d.DoDDLJob(ctx, job) + require.NoError(t, err) + + testGetTable(t, domain, genIDs[0]) + testGetTable(t, domain, genIDs[1]) + testGetTable(t, domain, genIDs[2]) +} + +func TestAlterTTL(t *testing.T) { + store, domain := testkit.CreateMockStoreAndDomainWithSchemaLease(t, testLease) + + d := domain.DDL() + + dbInfo, err := testSchemaInfo(store, "test_table") + require.NoError(t, err) + testCreateSchema(t, testkit.NewTestKit(t, store).Session(), d, dbInfo) + + ctx := testkit.NewTestKit(t, store).Session() + + // initialize a table with ttlInfo + tableName := "t" + tblInfo, err := testTableInfo(store, tableName, 2) + require.NoError(t, err) + tblInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeDatetime) + tblInfo.Columns[1].FieldType = *types.NewFieldType(mysql.TypeDatetime) + tblInfo.TTLInfo = &model.TTLInfo{ + ColumnName: tblInfo.Columns[0].Name, + IntervalExprStr: "5", + IntervalTimeUnit: int(ast.TimeUnitDay), + } + + // create table + job := testCreateTable(t, ctx, d, dbInfo, tblInfo) + testCheckTableState(t, store, dbInfo, tblInfo, model.StatePublic) + testCheckJobDone(t, store, job.ID, true) + + // submit ddl job to modify ttlInfo + tableInfoAfterAlterTTLInfo := tblInfo.Clone() + require.NoError(t, err) + tableInfoAfterAlterTTLInfo.TTLInfo = &model.TTLInfo{ + ColumnName: tblInfo.Columns[1].Name, + IntervalExprStr: "1", + IntervalTimeUnit: int(ast.TimeUnitYear), + } + + job = &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionAlterTTLInfo, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{&model.TTLInfo{ + ColumnName: tblInfo.Columns[1].Name, + IntervalExprStr: "1", + IntervalTimeUnit: int(ast.TimeUnitYear), + }}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v := getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) + + // assert the ddlInfo as expected + historyJob, err := ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) + require.NoError(t, err) + require.Equal(t, tableInfoAfterAlterTTLInfo.TTLInfo, historyJob.BinlogInfo.TableInfo.TTLInfo) + + // submit a ddl job to modify ttlEnabled + job = &model.Job{ + SchemaID: dbInfo.ID, + TableID: tblInfo.ID, + Type: model.ActionAlterTTLRemove, + BinlogInfo: &model.HistoryInfo{}, + Args: []interface{}{true}, + } + ctx.SetValue(sessionctx.QueryString, "skip") + require.NoError(t, d.DoDDLJob(ctx, job)) + + v = getSchemaVer(t, ctx) + checkHistoryJobArgs(t, ctx, job.ID, &historyJobArgs{ver: v, tbl: nil}) + + // assert the ddlInfo as expected + historyJob, err = ddl.GetHistoryJobByID(testkit.NewTestKit(t, store).Session(), job.ID) + require.NoError(t, err) + require.Empty(t, historyJob.BinlogInfo.TableInfo.TTLInfo) +} + +func TestRenameTableIntermediateState(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + originHook := dom.DDL().GetHook() + tk.MustExec("create database db1;") + tk.MustExec("create database db2;") + tk.MustExec("create table db1.t(a int);") + + testCases := []struct { + renameSQL string + insertSQL string + errMsg string + finalDB string + }{ + {"rename table db1.t to db1.t1;", "insert into db1.t values(1);", "[schema:1146]Table 'db1.t' doesn't exist", "db1.t1"}, + {"rename table db1.t1 to db1.t;", "insert into db1.t values(1);", "", "db1.t"}, + {"rename table db1.t to db2.t;", "insert into db1.t values(1);", "[schema:1146]Table 'db1.t' doesn't exist", "db2.t"}, + {"rename table db2.t to db1.t;", "insert into db1.t values(1);", "", "db1.t"}, + } + + for _, tc := range testCases { + hook := &callback.TestDDLCallback{Do: dom} + runInsert := false + fn := func(job *model.Job) { + if job.Type == model.ActionRenameTable && + job.SchemaState == model.StatePublic && !runInsert && !t.Failed() { + _, err := tk2.Exec(tc.insertSQL) + if len(tc.errMsg) > 0 { + assert.NotNil(t, err) + assert.Equal(t, tc.errMsg, err.Error()) + } else { + assert.NoError(t, err) + } + runInsert = true + } + } + hook.OnJobUpdatedExported.Store(&fn) + dom.DDL().SetHook(hook) + tk.MustExec(tc.renameSQL) + result := tk.MustQuery(fmt.Sprintf("select * from %s;", tc.finalDB)) + if len(tc.errMsg) > 0 { + result.Check(testkit.Rows()) + } else { + result.Check(testkit.Rows("1")) + } + tk.MustExec(fmt.Sprintf("delete from %s;", tc.finalDB)) + } + dom.DDL().SetHook(originHook) +} diff --git a/pkg/ddl/tests/adminpause/BUILD.bazel b/pkg/ddl/tests/adminpause/BUILD.bazel new file mode 100644 index 0000000000000..f5917cb6181fb --- /dev/null +++ b/pkg/ddl/tests/adminpause/BUILD.bazel @@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "adminpause", + srcs = [ + "ddl_data_generation.go", + "ddl_stmt_cases.go", + "global.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/tests/adminpause", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/domain", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/util/logutil", + ], +) + +go_test( + name = "adminpause_test", + timeout = "moderate", + srcs = [ + "main_test.go", + "pause_cancel_test.go", + "pause_negative_test.go", + "pause_resume_test.go", + ], + embed = [":adminpause"], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/testutil", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/errno", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/sqlexec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/ddl/tests/adminpause/ddl_data_generation.go b/pkg/ddl/tests/adminpause/ddl_data_generation.go similarity index 99% rename from ddl/tests/adminpause/ddl_data_generation.go rename to pkg/ddl/tests/adminpause/ddl_data_generation.go index ca68e3e03af05..f135847942db6 100644 --- a/ddl/tests/adminpause/ddl_data_generation.go +++ b/pkg/ddl/tests/adminpause/ddl_data_generation.go @@ -19,7 +19,7 @@ import ( "math/rand" "time" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" ) // AgeMax limits the max number of tuple generated for t_user.age diff --git a/ddl/tests/adminpause/ddl_stmt_cases.go b/pkg/ddl/tests/adminpause/ddl_stmt_cases.go similarity index 99% rename from ddl/tests/adminpause/ddl_stmt_cases.go rename to pkg/ddl/tests/adminpause/ddl_stmt_cases.go index e92fb7e81bba7..e592b19e19e86 100644 --- a/ddl/tests/adminpause/ddl_stmt_cases.go +++ b/pkg/ddl/tests/adminpause/ddl_stmt_cases.go @@ -17,8 +17,8 @@ package adminpause import ( "sync" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" ) type autoIncrsedID struct { diff --git a/ddl/tests/adminpause/global.go b/pkg/ddl/tests/adminpause/global.go similarity index 88% rename from ddl/tests/adminpause/global.go rename to pkg/ddl/tests/adminpause/global.go index 18e9b045d4b26..3c7ddb2a5eb25 100644 --- a/ddl/tests/adminpause/global.go +++ b/pkg/ddl/tests/adminpause/global.go @@ -18,10 +18,10 @@ import ( "testing" "time" - ddlctrl "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" + ddlctrl "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" ) const dbTestLease = 600 * time.Millisecond diff --git a/pkg/ddl/tests/adminpause/main_test.go b/pkg/ddl/tests/adminpause/main_test.go new file mode 100644 index 0000000000000..a51ffa9e80dc7 --- /dev/null +++ b/pkg/ddl/tests/adminpause/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package adminpause + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/ddl/tests/adminpause/pause_cancel_test.go b/pkg/ddl/tests/adminpause/pause_cancel_test.go similarity index 96% rename from ddl/tests/adminpause/pause_cancel_test.go rename to pkg/ddl/tests/adminpause/pause_cancel_test.go index 871c00da3df7d..c526eab013236 100644 --- a/ddl/tests/adminpause/pause_cancel_test.go +++ b/pkg/ddl/tests/adminpause/pause_cancel_test.go @@ -21,13 +21,13 @@ import ( "sync/atomic" "testing" - testddlutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" + testddlutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/ddl/tests/adminpause/pause_negative_test.go b/pkg/ddl/tests/adminpause/pause_negative_test.go similarity index 88% rename from ddl/tests/adminpause/pause_negative_test.go rename to pkg/ddl/tests/adminpause/pause_negative_test.go index 6023fdda37d58..f283d96d2c2ba 100644 --- a/ddl/tests/adminpause/pause_negative_test.go +++ b/pkg/ddl/tests/adminpause/pause_negative_test.go @@ -22,12 +22,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" "go.uber.org/atomic" ) @@ -58,10 +58,10 @@ func TestPauseOnWriteConflict(t *testing.T) { if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { require.NoError(t, failpoint.Enable( - "github.com/pingcap/tidb/ddl/mockFailedCommandOnConcurencyDDL", `return(true)`)) + "github.com/pingcap/tidb/pkg/ddl/mockFailedCommandOnConcurencyDDL", `return(true)`)) defer func() { require.NoError(t, failpoint.Disable( - "github.com/pingcap/tidb/ddl/mockFailedCommandOnConcurencyDDL")) + "github.com/pingcap/tidb/pkg/ddl/mockFailedCommandOnConcurencyDDL")) }() jobID.Store(job.ID) @@ -126,9 +126,9 @@ func TestPauseFailedOnCommit(t *testing.T) { if job.Type == model.ActionAddIndex && job.State == model.JobStateRunning && job.SchemaState == model.StateWriteReorganization { require.NoError(t, failpoint.Enable( - "github.com/pingcap/tidb/ddl/mockCommitFailedOnDDLCommand", `return(true)`)) + "github.com/pingcap/tidb/pkg/ddl/mockCommitFailedOnDDLCommand", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockCommitFailedOnDDLCommand")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockCommitFailedOnDDLCommand")) }() jobID.Store(job.ID) jobErrs, pauseErr = ddl.PauseJobs(tk2.Session(), []int64{jobID.Load()}) diff --git a/ddl/tests/adminpause/pause_resume_test.go b/pkg/ddl/tests/adminpause/pause_resume_test.go similarity index 97% rename from ddl/tests/adminpause/pause_resume_test.go rename to pkg/ddl/tests/adminpause/pause_resume_test.go index 176ffc6ce3f1a..95d7ee22bad8c 100644 --- a/ddl/tests/adminpause/pause_resume_test.go +++ b/pkg/ddl/tests/adminpause/pause_resume_test.go @@ -21,13 +21,13 @@ import ( "sync" "testing" - testddlutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" + testddlutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/pkg/ddl/tests/fail/BUILD.bazel b/pkg/ddl/tests/fail/BUILD.bazel new file mode 100644 index 0000000000000..37f402867ca26 --- /dev/null +++ b/pkg/ddl/tests/fail/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "fail_test", + timeout = "short", + srcs = [ + "fail_db_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 12, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/schematracker", + "//pkg/ddl/testutil", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/ddl/tests/fail/fail_db_test.go b/pkg/ddl/tests/fail/fail_db_test.go similarity index 88% rename from ddl/tests/fail/fail_db_test.go rename to pkg/ddl/tests/fail/fail_db_test.go index d9b32487e754e..da4a9018c18a5 100644 --- a/ddl/tests/fail/fail_db_test.go +++ b/pkg/ddl/tests/fail/fail_db_test.go @@ -23,18 +23,18 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/ddl/testutil" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/ddl/testutil" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" "go.opencensus.io/stats/view" @@ -72,9 +72,9 @@ func createFailDBSuite(t *testing.T) (s *failedSuite) { // TestHalfwayCancelOperations tests the case that the schema is correct after the execution of operations are cancelled halfway. func TestHalfwayCancelOperations(t *testing.T) { s := createFailDBSuite(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/truncateTableErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/truncateTableErr", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/truncateTableErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/truncateTableErr")) }() tk := testkit.NewTestKit(t, s.store) tk.MustExec("create database cancel_job_db") @@ -98,9 +98,9 @@ func TestHalfwayCancelOperations(t *testing.T) { // Test schema is correct. tk.MustExec("select * from t") // test for renaming table - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/renameTableErr", `return("ty")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/renameTableErr", `return("ty")`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/renameTableErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/renameTableErr")) }() tk.MustExec("create table tx(a int)") tk.MustExec("insert into tx values(1)") @@ -126,9 +126,9 @@ func TestHalfwayCancelOperations(t *testing.T) { tk.MustExec("use cancel_job_db") tk.MustExec("select * from tx") // test for exchanging partition - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/exchangePartitionErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/exchangePartitionErr", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/exchangePartitionErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/exchangePartitionErr")) }() tk.MustExec("create table pt(a int) partition by hash (a) partitions 2") tk.MustExec("insert into pt values(1), (3), (5)") @@ -162,16 +162,16 @@ func TestInitializeOffsetAndState(t *testing.T) { tk.MustExec("create table t(a int, b int, c int)") defer tk.MustExec("drop table t") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/uninitializedOffsetAndState", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/uninitializedOffsetAndState", `return(true)`)) tk.MustExec("ALTER TABLE t MODIFY COLUMN b int FIRST;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/uninitializedOffsetAndState")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/uninitializedOffsetAndState")) } func TestUpdateHandleFailed(t *testing.T) { s := createFailDBSuite(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/errorUpdateReorgHandle", `1*return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/errorUpdateReorgHandle", `1*return`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/errorUpdateReorgHandle")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/errorUpdateReorgHandle")) }() tk := testkit.NewTestKit(t, s.store) tk.MustExec("create database if not exists test_handle_failed") @@ -187,9 +187,9 @@ func TestUpdateHandleFailed(t *testing.T) { func TestAddIndexFailed(t *testing.T) { s := createFailDBSuite(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockBackfillRunErr", `1*return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockBackfillRunErr", `1*return`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockBackfillRunErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockBackfillRunErr")) }() tk := testkit.NewTestKit(t, s.store) tk.MustExec("create database if not exists test_add_index_failed") @@ -236,7 +236,7 @@ func TestFailSchemaSyncer(t *testing.T) { require.True(t, ok) // make reload failed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed", `return(true)`)) mockSyncer.CloseSession() // wait the schemaValidator is stopped. for i := 0; i < 50; i++ { @@ -250,7 +250,7 @@ func TestFailSchemaSyncer(t *testing.T) { _, err := tk.Exec("insert into t values(1)") require.Error(t, err) require.EqualError(t, err, "[domain:8027]Information schema is out of date: schema failed to update in 1 lease, please make sure TiDB can connect to TiKV") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed")) // wait the schemaValidator is started. for i := 0; i < 50; i++ { if s.dom.SchemaValidator.IsStarted() { @@ -266,7 +266,7 @@ func TestFailSchemaSyncer(t *testing.T) { func TestGenGlobalIDFail(t *testing.T) { s := createFailDBSuite(t) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockGenGlobalIDFail")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockGenGlobalIDFail")) }() tk := testkit.NewTestKit(t, s.store) tk.MustExec("create database if not exists gen_global_id_fail") @@ -298,11 +298,11 @@ func TestGenGlobalIDFail(t *testing.T) { for idx, test := range testcases { if test.mockErr { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockGenGlobalIDFail", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockGenGlobalIDFail", `return(true)`)) _, err := tk.Exec(test.sql) require.Errorf(t, err, "the %dth test case '%s' fail", idx, test.sql) } else { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockGenGlobalIDFail", `return(false)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockGenGlobalIDFail", `return(false)`)) tk.MustExec(test.sql) tk.MustExec(fmt.Sprintf("insert into %s values (%d, 42)", test.table, rand.Intn(65536))) tk.MustExec(fmt.Sprintf("admin check table %s", test.table)) @@ -376,7 +376,7 @@ func testAddIndexWorkerNum(t *testing.T, s *failedSuite, test func(*testkit.Test ddl.TestCheckWorkerNumber = lastSetWorkerCnt defer tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_reorg_worker_cnt=%d", originDDLAddIndexWorkerCnt)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum", `return(true)`)) testutil.SessionExecInGoroutine(s.store, "test_db", "create index c3_index on test_add_index (c3)", done) checkNum := 0 @@ -397,7 +397,7 @@ func testAddIndexWorkerNum(t *testing.T, s *failedSuite, test func(*testkit.Test } require.Greater(t, checkNum, 1) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum")) tk.MustExec("admin check table test_add_index") tk.MustExec("drop table test_add_index") } @@ -406,12 +406,12 @@ func testAddIndexWorkerNum(t *testing.T, s *failedSuite, test func(*testkit.Test func TestRunDDLJobPanic(t *testing.T) { s := createFailDBSuite(t) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockPanicInRunDDLJob")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockPanicInRunDDLJob")) }() tk := testkit.NewTestKit(t, s.store) tk.MustExec("use test") tk.MustExec("drop table if exists t") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockPanicInRunDDLJob", `1*panic("panic test")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockPanicInRunDDLJob", `1*panic("panic test")`)) _, err := tk.Exec("create table t(c1 int, c2 int)") require.Error(t, err) require.EqualError(t, err, "[ddl:8214]Cancelled DDL job") @@ -432,9 +432,9 @@ func TestPartitionAddIndexGC(t *testing.T) { );`) tk.MustExec("insert into partition_add_idx values(1, '2010-01-01'), (2, '1990-01-01'), (3, '2001-01-01')") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpdateCachedSafePoint", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpdateCachedSafePoint", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpdateCachedSafePoint")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpdateCachedSafePoint")) }() tk.MustExec("alter table partition_add_idx add index idx (id, hired)") } @@ -542,9 +542,9 @@ func TestPartitionAddPanic(t *testing.T) { tk.MustExec(`use test;`) tk.MustExec(`drop table if exists t;`) tk.MustExec(`create table t (a int) partition by range(a) (partition p0 values less than (10));`) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/CheckPartitionByRangeErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/CheckPartitionByRangeErr", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/CheckPartitionByRangeErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/CheckPartitionByRangeErr")) }() _, err := tk.Exec(`alter table t add partition (partition p1 values less than (20));`) diff --git a/pkg/ddl/tests/fail/main_test.go b/pkg/ddl/tests/fail/main_test.go new file mode 100644 index 0000000000000..f30c742849f03 --- /dev/null +++ b/pkg/ddl/tests/fail/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/tests/fk/BUILD.bazel b/pkg/ddl/tests/fk/BUILD.bazel new file mode 100644 index 0000000000000..22a339f714056 --- /dev/null +++ b/pkg/ddl/tests/fk/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "fk_test", + timeout = "short", + srcs = [ + "foreign_key_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 25, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/infoschema", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/sessiontxn", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/dbterror", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/tests/fk/foreign_key_test.go b/pkg/ddl/tests/fk/foreign_key_test.go new file mode 100644 index 0000000000000..210867ccc7529 --- /dev/null +++ b/pkg/ddl/tests/fk/foreign_key_test.go @@ -0,0 +1,1837 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "bytes" + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/stretchr/testify/require" +) + +func TestCreateTableWithForeignKeyMetaInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int,b int as (a) virtual);") + tk.MustExec("create database test2") + tk.MustExec("use test2") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id) ON UPDATE RESTRICT ON DELETE CASCADE)") + tb1Info := getTableInfo(t, dom, "test", "t1") + tb2Info := getTableInfo(t, dom, "test2", "t2") + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test", "t1"))) + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) + require.Equal(t, 0, len(tb1Info.ForeignKeys)) + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t2"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb1ReferredFKs[0]) + tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tb2Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_b"), + RefSchema: model.NewCIStr("test"), + RefTable: model.NewCIStr("t1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("b")}, + OnDelete: 2, + OnUpdate: 1, + State: model.StatePublic, + Version: 1, + }, *tb2Info.ForeignKeys[0]) + // Auto create index for foreign key usage. + require.Equal(t, 1, len(tb2Info.Indices)) + require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) + require.Equal(t, "`test2`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb2Info.ForeignKeys[0].String("test2", "t2")) + + tk.MustExec("create table t3 (id int, b int, index idx_b(b), foreign key fk_b(b) references t2(id) ON UPDATE SET NULL ON DELETE NO ACTION)") + tb2Info = getTableInfo(t, dom, "test2", "t2") + tb3Info := getTableInfo(t, dom, "test2", "t3") + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) + require.Equal(t, 1, len(tb2Info.ForeignKeys)) + tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test2", "t2") + require.Equal(t, 1, len(tb2ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t3"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb2ReferredFKs[0]) + tb3ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t3") + require.Equal(t, 0, len(tb3ReferredFKs)) + require.Equal(t, 1, len(tb3Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_b"), + RefSchema: model.NewCIStr("test2"), + RefTable: model.NewCIStr("t2"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("b")}, + OnDelete: 4, + OnUpdate: 3, + State: model.StatePublic, + Version: 1, + }, *tb3Info.ForeignKeys[0]) + require.Equal(t, 1, len(tb3Info.Indices)) + require.Equal(t, "idx_b", tb3Info.Indices[0].Name.L) + require.Equal(t, "`test2`.`t3`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `t2` (`id`) ON DELETE NO ACTION ON UPDATE SET NULL", tb3Info.ForeignKeys[0].String("test2", "t3")) + + tk.MustExec("create table t5 (id int key, a int, b int, foreign key (a) references t5(id));") + tb5Info := getTableInfo(t, dom, "test2", "t5") + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) + require.Equal(t, 1, len(tb5Info.ForeignKeys)) + tb5ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t5") + require.Equal(t, 1, len(tb5ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t5"), + ChildFKName: model.NewCIStr("fk_1"), + }, *tb5ReferredFKs[0]) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_1"), + RefSchema: model.NewCIStr("test2"), + RefTable: model.NewCIStr("t5"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + State: model.StatePublic, + Version: 1, + }, *tb5Info.ForeignKeys[0]) + require.Equal(t, 1, len(tb5Info.Indices)) + require.Equal(t, "fk_1", tb5Info.Indices[0].Name.L) + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test", "t1"))) + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) + require.Equal(t, 1, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) + + tk.MustExec("set @@global.tidb_enable_foreign_key=0") + tk.MustExec("drop database test2") + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t2"))) + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t3"))) + require.Equal(t, 0, len(dom.InfoSchema().GetTableReferredForeignKeys("test2", "t5"))) +} + +func TestCreateTableWithForeignKeyMetaInfo2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("create database test2") + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("use test2") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id) ON UPDATE RESTRICT ON DELETE CASCADE)") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") + tb1Info := getTableInfo(t, dom, "test", "t1") + tb2Info := getTableInfo(t, dom, "test2", "t2") + require.Equal(t, 0, len(tb1Info.ForeignKeys)) + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t2"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb1ReferredFKs[0]) + tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tb2Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_b"), + RefSchema: model.NewCIStr("test"), + RefTable: model.NewCIStr("t1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("b")}, + OnDelete: 2, + OnUpdate: 1, + State: model.StatePublic, + Version: 1, + }, *tb2Info.ForeignKeys[0]) + // Auto create index for foreign key usage. + require.Equal(t, 1, len(tb2Info.Indices)) + require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) + require.Equal(t, "`test2`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb2Info.ForeignKeys[0].String("test2", "t2")) + + tk.MustExec("create table t3 (id int key, a int, foreign key fk_a(a) references test.t1(id) ON DELETE CASCADE ON UPDATE RESTRICT, foreign key fk_a2(a) references test2.t2(id))") + tb1Info = getTableInfo(t, dom, "test", "t1") + tb3Info := getTableInfo(t, dom, "test", "t3") + require.Equal(t, 0, len(tb1Info.ForeignKeys)) + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 2, len(tb1ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test"), + ChildTable: model.NewCIStr("t3"), + ChildFKName: model.NewCIStr("fk_a"), + }, *tb1ReferredFKs[0]) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t2"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb1ReferredFKs[1]) + tb3ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t3") + require.Equal(t, 0, len(tb3ReferredFKs)) + require.Equal(t, 2, len(tb3Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_a"), + RefSchema: model.NewCIStr("test"), + RefTable: model.NewCIStr("t1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + OnDelete: 2, + OnUpdate: 1, + State: model.StatePublic, + Version: 1, + }, *tb3Info.ForeignKeys[0]) + require.Equal(t, model.FKInfo{ + ID: 2, + Name: model.NewCIStr("fk_a2"), + RefSchema: model.NewCIStr("test2"), + RefTable: model.NewCIStr("t2"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + State: model.StatePublic, + Version: 1, + }, *tb3Info.ForeignKeys[1]) + // Auto create index for foreign key usage. + require.Equal(t, 1, len(tb3Info.Indices)) + require.Equal(t, "fk_a", tb3Info.Indices[0].Name.L) + require.Equal(t, "`test`.`t3`, CONSTRAINT `fk_a` FOREIGN KEY (`a`) REFERENCES `t1` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT", tb3Info.ForeignKeys[0].String("test", "t3")) + require.Equal(t, "`test`.`t3`, CONSTRAINT `fk_a2` FOREIGN KEY (`a`) REFERENCES `test2`.`t2` (`id`)", tb3Info.ForeignKeys[1].String("test", "t3")) + + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("drop table test2.t2") + tb1Info = getTableInfo(t, dom, "test", "t1") + tb3Info = getTableInfo(t, dom, "test", "t3") + require.Equal(t, 0, len(tb1Info.ForeignKeys)) + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test"), + ChildTable: model.NewCIStr("t3"), + ChildFKName: model.NewCIStr("fk_a"), + }, *tb1ReferredFKs[0]) + tb3ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t3") + require.Equal(t, 0, len(tb3ReferredFKs)) + require.Equal(t, 2, len(tb3Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_a"), + RefSchema: model.NewCIStr("test"), + RefTable: model.NewCIStr("t1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + OnDelete: 2, + OnUpdate: 1, + State: model.StatePublic, + Version: 1, + }, *tb3Info.ForeignKeys[0]) + require.Equal(t, model.FKInfo{ + ID: 2, + Name: model.NewCIStr("fk_a2"), + RefSchema: model.NewCIStr("test2"), + RefTable: model.NewCIStr("t2"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + State: model.StatePublic, + Version: 1, + }, *tb3Info.ForeignKeys[1]) +} + +func TestCreateTableWithForeignKeyMetaInfo3(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + tk.MustExec("create table t3 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + tk.MustExec("create table t4 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + tk.MustExec("drop table t3") + tk.MustExec("create table t5 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + require.Equal(t, 3, len(tb1ReferredFKs)) + require.Equal(t, "t2", tb1ReferredFKs[0].ChildTable.L) + require.Equal(t, "t3", tb1ReferredFKs[1].ChildTable.L) + require.Equal(t, "t4", tb1ReferredFKs[2].ChildTable.L) +} + +func TestCreateTableWithForeignKeyPrivilegeCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create user 'u1'@'%' identified by '';") + tk.MustExec("grant create on *.* to 'u1'@'%';") + tk.MustExec("create table t1 (id int key);") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + err := tk2.ExecToErr("create table t2 (a int, foreign key fk(a) references t1(id));") + require.Error(t, err) + require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) + + tk.MustExec("grant references on test.t1 to 'u1'@'%';") + tk2.MustExec("create table t2 (a int, foreign key fk(a) references t1(id));") + tk2.MustExec("create table t3 (id int key)") + err = tk2.ExecToErr("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") + require.Error(t, err) + require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't3'", err.Error()) + + tk.MustExec("grant references on test.t3 to 'u1'@'%';") + tk2.MustExec("create table t4 (a int, foreign key fk(a) references t1(id), foreign key (a) references t3(id));") +} + +func TestAlterTableWithForeignKeyPrivilegeCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create user 'u1'@'%' identified by '';") + tk.MustExec("grant create,alter on *.* to 'u1'@'%';") + tk.MustExec("create table t1 (id int key);") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + tk2.MustExec("create table t2 (a int)") + err := tk2.ExecToErr("alter table t2 add foreign key (a) references t1 (id) on update cascade") + require.Error(t, err) + require.Equal(t, "[planner:1142]REFERENCES command denied to user 'u1'@'%' for table 't1'", err.Error()) + tk.MustExec("grant references on test.t1 to 'u1'@'%';") + tk2.MustExec("alter table t2 add foreign key (a) references t1 (id) on update cascade") +} + +func TestRenameTableWithForeignKeyMetaInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("create database test2") + tk.MustExec("create database test3") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))") + tk.MustExec("rename table test.t1 to test2.t2") + // check the schema diff + diff := getLatestSchemaDiff(t, tk) + require.Equal(t, model.ActionRenameTable, diff.Type) + require.Equal(t, 0, len(diff.AffectedOpts)) + tk.MustQuery("show create table test2.t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `id` int(11) NOT NULL,\n" + + " `a` int(11) DEFAULT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `fk` (`a`),\n" + + " CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `test2`.`t2` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tblInfo := getTableInfo(t, dom, "test2", "t2") + tbReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "t2") + require.Equal(t, 1, len(tblInfo.ForeignKeys)) + require.Equal(t, 1, len(tbReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("t2"), + ChildFKName: model.NewCIStr("fk"), + }, *tbReferredFKs[0]) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk"), + RefSchema: model.NewCIStr("test2"), + RefTable: model.NewCIStr("t2"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("a")}, + State: model.StatePublic, + Version: 1, + }, *tblInfo.ForeignKeys[0]) + + tk.MustExec("drop table test2.t2") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int as (a) virtual);") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + tk.MustExec("use test2") + tk.MustExec("rename table test.t2 to test2.tt2") + // check the schema diff + diff = getLatestSchemaDiff(t, tk) + require.Equal(t, model.ActionRenameTable, diff.Type) + require.Equal(t, 0, len(diff.AffectedOpts)) + tb1Info := getTableInfo(t, dom, "test", "t1") + tb2Info := getTableInfo(t, dom, "test2", "tt2") + require.Equal(t, 0, len(tb1Info.ForeignKeys)) + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("tt2"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb1ReferredFKs[0]) + tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tb2Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_b"), + RefSchema: model.NewCIStr("test"), + RefTable: model.NewCIStr("t1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("b")}, + State: model.StatePublic, + Version: 1, + }, *tb2Info.ForeignKeys[0]) + // Auto create index for foreign key usage. + require.Equal(t, 1, len(tb2Info.Indices)) + require.Equal(t, "fk_b", tb2Info.Indices[0].Name.L) + require.Equal(t, "`test2`.`tt2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`)", tb2Info.ForeignKeys[0].String("test2", "tt2")) + + tk.MustExec("rename table test.t1 to test3.tt1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test3", "tt1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) + // check the schema diff + diff = getLatestSchemaDiff(t, tk) + require.Equal(t, model.ActionRenameTable, diff.Type) + require.Equal(t, 0, len(diff.AffectedOpts)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("tt2"), + ChildFKName: model.NewCIStr("fk_b"), + }, *tb1ReferredFKs[0]) + tbl2Info := getTableInfo(t, dom, "test2", "tt2") + tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys)) + require.Equal(t, model.FKInfo{ + ID: 1, + Name: model.NewCIStr("fk_b"), + RefSchema: model.NewCIStr("test3"), + RefTable: model.NewCIStr("tt1"), + RefCols: []model.CIStr{model.NewCIStr("id")}, + Cols: []model.CIStr{model.NewCIStr("b")}, + State: model.StatePublic, + Version: 1, + }, *tbl2Info.ForeignKeys[0]) + tk.MustQuery("show create table test2.tt2").Check(testkit.Rows("tt2 CREATE TABLE `tt2` (\n" + + " `id` int(11) NOT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `fk_b` (`b`),\n" + + " CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `test3`.`tt1` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func TestCreateTableWithForeignKeyDML(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int);") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1)") + tk.MustExec("update t1 set a = 2 where id = 1") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references test.t1(id))") + + tk.MustExec("commit") +} + +func TestCreateTableWithForeignKeyError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + + cases := []struct { + prepare []string + refer string + create string + err string + }{ + { + refer: "create table t1 (id int, a int, b int);", + create: "create table t2 (a int, b int, foreign key fk_b(b) references T_unknown(b));", + err: "[schema:1824]Failed to open the referenced table 'T_unknown'", + }, + { + refer: "create table t1 (id int, a int, b int);", + create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(c_unknown));", + err: "[schema:3734]Failed to add the foreign key constraint. Missing column 'c_unknown' for constraint 'fk_b' in the referenced table 't1'", + }, + { + refer: "create table t1 (id int key, a int, b int);", + create: "create table t2 (a int, b int, foreign key fk(c_unknown) references t1(id));", + err: "[ddl:1072]Key column 'c_unknown' doesn't exist in table", + }, + { + refer: "create table t1 (id int, a int, b int);", + create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + refer: "create table t1 (id int, a int, b int not null, index(b));", + create: "create table t2 (a int, b int not null, foreign key fk_b(b) references t1(b) on update set null);", + err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", + }, + { + refer: "create table t1 (id int, a int, b int not null, index(b));", + create: "create table t2 (a int, b int not null, foreign key fk_b(b) references t1(b) on delete set null);", + err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", + }, + { + refer: "create table t1 (id int key, a int, b int as (a) virtual, index(b));", + create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", + err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", + }, + { + refer: "create table t1 (id int key, a int, b int, index(b));", + create: "create table t2 (a int, b int as (a) virtual, foreign key fk_b(b) references t1(b));", + err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", + }, + { + refer: "create table t1 (id int key, a int);", + create: "create table t2 (a int, b varchar(10), foreign key fk(b) references t1(id));", + err: "[ddl:3780]Referencing column 'b' and referenced column 'id' in foreign key constraint 'fk' are incompatible.", + }, + { + refer: "create table t1 (id int key, a int not null, index(a));", + create: "create table t2 (a int, b int unsigned, foreign key fk_b(b) references t1(a));", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + refer: "create table t1 (id int key, a bigint, index(a));", + create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(a));", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + refer: "create table t1 (id int key, a varchar(10) charset utf8, index(a));", + create: "create table t2 (a int, b varchar(10) charset utf8mb4, foreign key fk_b(b) references t1(a));", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + refer: "create table t1 (id int key, a varchar(10) collate utf8_bin, index(a));", + create: "create table t2 (a int, b varchar(10) collate utf8mb4_bin, foreign key fk_b(b) references t1(a));", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + refer: "create table t1 (id int key, a varchar(10));", + create: "create table t2 (a int, b varchar(10), foreign key fk_b(b) references t1(a));", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + refer: "create table t1 (id int key, a varchar(10), index (a(5)));", + create: "create table t2 (a int, b varchar(10), foreign key fk_b(b) references t1(a));", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + refer: "create table t1 (id int key, a int, index(a));", + create: "create table t2 (a int, b int, foreign key fk_b(b) references t1(id, a));", + err: "[schema:1239]Incorrect foreign key definition for 'fk_b': Key reference and table reference don't match", + }, + { + create: "create table t2 (a int key, foreign key (a) references t2(a));", + err: "[schema:1215]Cannot add foreign key constraint", + }, + { + create: "create table t2 (a int, b int, index(a,b), index(b,a), foreign key (a,b) references t2(a,b));", + err: "[schema:1215]Cannot add foreign key constraint", + }, + { + create: "create table t2 (a int, b int, index(a,b), foreign key (a,b) references t2(b,a));", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't2'", + }, + { + prepare: []string{ + "set @@foreign_key_checks=0;", + "create table t2 (a int, b int, index(a), foreign key (a) references t1(id));", + }, + create: "create table t1 (id int, a int);", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't1'", + }, + { + prepare: []string{ + "set @@foreign_key_checks=0;", + "create table t2 (a int, b int, index(a), foreign key (a) references t1(id));", + }, + create: "create table t1 (id bigint key, a int);", + err: "[ddl:3780]Referencing column 'a' and referenced column 'id' in foreign key constraint 'fk_1' are incompatible.", + }, + { + // foreign key is not support in temporary table. + refer: "create temporary table t1 (id int key, b int, index(b))", + create: "create table t2 (a int, b int, foreign key fk(b) references t1(b))", + err: "[schema:1824]Failed to open the referenced table 't1'", + }, + { + // foreign key is not support in temporary table. + refer: "create global temporary table t1 (id int key, b int, index(b)) on commit delete rows", + create: "create table t2 (a int, b int, foreign key fk(b) references t1(b))", + err: "[schema:1215]Cannot add foreign key constraint", + }, + { + // foreign key is not support in temporary table. + refer: "create table t1 (id int key, b int, index(b))", + create: "create temporary table t2 (a int, b int, foreign key fk(b) references t1(b))", + err: "[schema:1215]Cannot add foreign key constraint", + }, + { + // foreign key is not support in temporary table. + refer: "create table t1 (id int key, b int, index(b))", + create: "create global temporary table t2 (a int, b int, foreign key fk(b) references t1(b)) on commit delete rows", + err: "[schema:1215]Cannot add foreign key constraint", + }, + { + create: "create table t1 (a int, foreign key ``(a) references t1(a));", + err: "[ddl:1280]Incorrect index name ''", + }, + { + create: "create table t1 (a int, constraint `` foreign key (a) references t1(a));", + err: "[ddl:1280]Incorrect index name ''", + }, + { + create: "create table t1 (a int, constraint `fk` foreign key (a,a) references t1(a, b));", + err: "[schema:1060]Duplicate column name 'a'", + }, + { + refer: "create table t1(a int, b int, index(a,b));", + create: "create table t2 (a int, b int, foreign key (a,b) references t1(a,a));", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_1' in the referenced table 't1'", + }, + { + refer: "create table t1 (id int key, b int, index(b))", + create: "create table t2 (a int, b int, index fk_1(a), foreign key (b) references t1(b));", + err: "[ddl:1061]duplicate key name fk_1", + }, + { + refer: "create table t1 (id int key);", + create: "create table t2 (id int key, foreign key name5678901234567890123456789012345678901234567890123456789012345(id) references t1(id));", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + { + refer: "create table t1 (id int key);", + create: "create table t2 (id int key, constraint name5678901234567890123456789012345678901234567890123456789012345 foreign key (id) references t1(id));", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + { + create: "create table t2 (id int key, constraint fk foreign key (id) references name5678901234567890123456789012345678901234567890123456789012345.t1(id));", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + { + prepare: []string{ + "set @@foreign_key_checks=0;", + }, + create: "create table t2 (id int key, constraint fk foreign key (id) references name5678901234567890123456789012345678901234567890123456789012345(id));", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + { + prepare: []string{ + "set @@foreign_key_checks=0;", + }, + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(name5678901234567890123456789012345678901234567890123456789012345));", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + // Test foreign key with temporary table + { + refer: "create temporary table t1 (id int key);", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1824]Failed to open the referenced table 't1'", + }, + { + refer: "create table t1 (id int key);", + create: "create temporary table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1215]Cannot add foreign key constraint", + }, + // Test foreign key with partition table + { + refer: "create table t1 (id int key) partition by hash(id) partitions 3;", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id));", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + { + refer: "create table t1 (id int key);", + create: "create table t2 (id int key, constraint fk foreign key (id) references t1(id)) partition by hash(id) partitions 3;", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + } + for _, ca := range cases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + tk.MustExec("set @@foreign_key_checks=1") + for _, sql := range ca.prepare { + tk.MustExec(sql) + } + if ca.refer != "" { + tk.MustExec(ca.refer) + } + err := tk.ExecToErr(ca.create) + require.Error(t, err, ca.create) + require.Equal(t, ca.err, err.Error(), ca.create) + } + + passCases := [][]string{ + { + "create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))", + }, + { + "create table t1 (id int key, b int not null, index(b))", + "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", + }, + { + "create table t1 (id int key, a varchar(10), index(a));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + }, + { + "create table t1 (id int key, a decimal(10,5), index(a));", + "create table t2 (a int, b decimal(20, 10), foreign key fk_b(b) references t1(a));", + }, + { + "create table t1 (id int key, a varchar(10), index (a(10)));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + }, + { + "set @@foreign_key_checks=0;", + "create table t2 (a int, b int, foreign key fk_b(b) references t_unknown(b));", + "set @@foreign_key_checks=1;", + }, + { + "create table t2 (a int, b int, index(a,b), index(b,a), foreign key (a,b) references t2(b,a));", + }, + { + "create table t1 (a int key, b int, index(b))", + "create table t2 (a int, b int, foreign key (a) references t1(a), foreign key (b) references t1(b));", + }, + { + "create table t1 (id int key);", + "create table t2 (id int key, foreign key name567890123456789012345678901234567890123456789012345678901234(id) references t1(id));", + }, + } + for _, ca := range passCases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca { + tk.MustExec(sql) + } + } +} + +func TestModifyColumnWithForeignKey(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + + tk.MustExec("create table t1 (id int key, b varchar(10), index(b));") + tk.MustExec("create table t2 (a varchar(10), constraint fk foreign key (a) references t1(b));") + tk.MustExec("insert into t1 values (1, '123456789');") + tk.MustExec("insert into t2 values ('123456789');") + tk.MustGetErrMsg("alter table t1 modify column b varchar(5);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustGetErrMsg("alter table t1 modify column b bigint;", "[ddl:3780]Referencing column 'a' and referenced column 'b' in foreign key constraint 'fk' are incompatible.") + tk.MustExec("alter table t1 modify column b varchar(20);") + tk.MustGetErrMsg("alter table t1 modify column b varchar(10);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustExec("alter table t2 modify column a varchar(20);") + tk.MustExec("alter table t2 modify column a varchar(21);") + tk.MustGetErrMsg("alter table t2 modify column a varchar(5);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") + tk.MustGetErrMsg("alter table t2 modify column a bigint;", "[ddl:3780]Referencing column 'a' and referenced column 'b' in foreign key constraint 'fk' are incompatible.") + + tk.MustExec("drop table t2") + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key, b decimal(10, 5), index(b));") + tk.MustExec("create table t2 (a decimal(10, 5), constraint fk foreign key (a) references t1(b));") + tk.MustExec("insert into t1 values (1, 12345.67891);") + tk.MustExec("insert into t2 values (12345.67891);") + tk.MustGetErrMsg("alter table t1 modify column b decimal(10, 6);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustGetErrMsg("alter table t1 modify column b decimal(10, 3);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustGetErrMsg("alter table t1 modify column b decimal(5, 2);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustGetErrMsg("alter table t1 modify column b decimal(20, 10);", "[ddl:1833]Cannot change column 'b': used in a foreign key constraint 'fk' of table 'test.t2'") + tk.MustGetErrMsg("alter table t2 modify column a decimal(30, 15);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") + tk.MustGetErrMsg("alter table t2 modify column a decimal(5, 2);", "[ddl:1832]Cannot change column 'a': used in a foreign key constraint 'fk'") +} + +func TestDropChildTableForeignKeyMetaInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int, CONSTRAINT fk foreign key (a) references t1(id))") + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + tk.MustExec("drop table t1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 0, len(tb1ReferredFKs)) + + tk.MustExec("create table t1 (id int key, b int, index(b))") + tk.MustExec("create table t2 (a int, b int, foreign key fk (a) references t1(b));") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + tk.MustExec("drop table t2") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 0, len(tb1ReferredFKs)) +} + +func TestDropForeignKeyMetaInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, a int, b int, CONSTRAINT fk foreign key (a) references t1(id))") + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + tk.MustExec("alter table t1 drop foreign key fk") + tbl1Info := getTableInfo(t, dom, "test", "t1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 0, len(tbl1Info.ForeignKeys)) + require.Equal(t, 0, len(tb1ReferredFKs)) + + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key, b int, index(b))") + tk.MustExec("create table t2 (a int, b int, foreign key fk (a) references t1(b));") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + tk.MustExec("alter table t2 drop foreign key fk") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 0, len(tb1ReferredFKs)) + tbl2Info := getTableInfo(t, dom, "test", "t2") + require.Equal(t, 0, len(tbl2Info.ForeignKeys)) +} + +func TestTruncateOrDropTableWithForeignKeyReferred(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("use test") + + cases := []struct { + prepares []string + tbl string + truncateErr string + dropErr string + }{ + { + prepares: []string{ + "create table t1 (id int key, b int not null, index(b))", + "create table t2 (a int, b int, foreign key fk_b(b) references t1(b));", + }, + tbl: "t1", + truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", + dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10), index(a));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + }, + tbl: "t1", + truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", + dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10), index (a(10)));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + }, + tbl: "t1", + truncateErr: "[ddl:1701]Cannot truncate a table referenced in a foreign key constraint (`test`.`t2` CONSTRAINT `fk_b`)", + dropErr: "[ddl:3730]Cannot drop table 't1' referenced by a foreign key constraint 'fk_b' on table 't2'.", + }, + } + + for _, ca := range cases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca.prepares { + tk.MustExec(sql) + } + truncateSQL := fmt.Sprintf("truncate table %v", ca.tbl) + tk.MustExec("set @@foreign_key_checks=1;") + err := tk.ExecToErr(truncateSQL) + require.Error(t, err) + require.Equal(t, ca.truncateErr, err.Error()) + dropSQL := fmt.Sprintf("drop table %v", ca.tbl) + err = tk.ExecToErr(dropSQL) + require.Error(t, err) + require.Equal(t, ca.dropErr, err.Error()) + + tk.MustExec("set @@foreign_key_checks=0;") + tk.MustExec(truncateSQL) + } + passCases := [][]string{ + { + "create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))", + "truncate table t1", + "drop table t1", + }, + { + "create table t1 (id int key, a varchar(10), index (a(10)));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + "drop table t1, t2", + }, + { + "set @@foreign_key_checks=0;", + "create table t1 (id int key, a varchar(10), index (a(10)));", + "create table t2 (a int, b varchar(20), foreign key fk_b(b) references t1(a));", + "truncate table t1", + "drop table t1", + }, + } + for _, ca := range passCases { + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("set @@foreign_key_checks=1;") + for _, sql := range ca { + tk.MustExec(sql) + } + } +} + +func TestDropTableWithForeignKeyReferred(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") + tk.MustExec("create table t3 (id int key, b int, foreign key fk_b(b) references t2(id));") + err := tk.ExecToErr("drop table if exists t1,t2;") + require.Error(t, err) + require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) + tk.MustQuery("show tables").Check(testkit.Rows("t1", "t2", "t3")) +} + +func TestDropIndexNeededInForeignKey(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + cases := []struct { + prepares []string + drops []string + err string + }{ + { + prepares: []string{ + "create table t1 (id int key, b int, index idx (b))", + "create table t2 (a int, b int, index idx (b), foreign key fk_b(b) references t1(b));", + }, + drops: []string{ + "alter table t1 drop index idx", + "alter table t2 drop index idx", + }, + err: "[ddl:1553]Cannot drop index 'idx': needed in a foreign key constraint", + }, + { + prepares: []string{ + "create table t1 (id int, b int, index idx (id, b))", + "create table t2 (a int, b int, index idx (b, a), foreign key fk_b(b) references t1(id));", + }, + drops: []string{ + "alter table t1 drop index idx", + "alter table t2 drop index idx", + }, + err: "[ddl:1553]Cannot drop index 'idx': needed in a foreign key constraint", + }, + } + + for _, ca := range cases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca.prepares { + tk.MustExec(sql) + } + for _, drop := range ca.drops { + // even disable foreign key check, still can't drop the index used by foreign key. + tk.MustExec("set @@foreign_key_checks=0;") + err := tk.ExecToErr(drop) + require.Error(t, err) + require.Equal(t, ca.err, err.Error()) + tk.MustExec("set @@foreign_key_checks=1;") + err = tk.ExecToErr(drop) + require.Error(t, err) + require.Equal(t, ca.err, err.Error()) + } + } + passCases := [][]string{ + { + "create table t1 (id int key, b int, index idxb (b))", + "create table t2 (a int, b int key, index idxa (a),index idxb (b), foreign key fk_b(b) references t1(id));", + "alter table t1 drop index idxb", + "alter table t2 drop index idxa", + "alter table t2 drop index idxb", + }, + { + "create table t1 (id int key, b int, index idxb (b), unique index idx(b, id))", + "create table t2 (a int, b int key, index idx (b, a),index idxb (b), index idxab(a, b), foreign key fk_b(b) references t1(b));", + "alter table t1 drop index idxb", + "alter table t1 add index idxb (b)", + "alter table t1 drop index idx", + "alter table t2 drop index idx", + "alter table t2 add index idx (b, a)", + "alter table t2 drop index idxb", + "alter table t2 drop index idxab", + }, + } + tk.MustExec("set @@foreign_key_checks=1;") + for _, ca := range passCases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca { + tk.MustExec(sql) + } + } +} + +func getTableInfo(t *testing.T, dom *domain.Domain, db, tb string) *model.TableInfo { + err := dom.Reload() + require.NoError(t, err) + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr(db), model.NewCIStr(tb)) + require.NoError(t, err) + _, exist := is.TableByID(tbl.Meta().ID) + require.True(t, exist) + return tbl.Meta() +} + +func getTableInfoReferredForeignKeys(t *testing.T, dom *domain.Domain, db, tb string) []*model.ReferredFKInfo { + err := dom.Reload() + require.NoError(t, err) + return dom.InfoSchema().GetTableReferredForeignKeys(db, tb) +} + +func TestDropColumnWithForeignKey(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + + tk.MustExec("create table t1 (id int key, a int, b int, index(b), CONSTRAINT fk foreign key (a) references t1(b))") + tk.MustGetErrMsg("alter table t1 drop column a;", "[ddl:1828]Cannot drop column 'a': needed in a foreign key constraint 'fk'") + tk.MustGetErrMsg("alter table t1 drop column b;", "[ddl:1829]Cannot drop column 'b': needed in a foreign key constraint 'fk' of table 't1'") + + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (a int, b int, constraint fk foreign key (a) references t1(b));") + tk.MustGetErrMsg("alter table t1 drop column b;", "[ddl:1829]Cannot drop column 'b': needed in a foreign key constraint 'fk' of table 't2'") + tk.MustGetErrMsg("alter table t2 drop column a;", "[ddl:1828]Cannot drop column 'a': needed in a foreign key constraint 'fk'") +} + +func TestRenameColumnWithForeignKeyMetaInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + + tk.MustExec("create table t1 (id int key, a int, b int, foreign key fk(a) references t1(id))") + tk.MustExec("alter table t1 change id kid int") + tk.MustExec("alter table t1 rename column a to aa") + tbl1Info := getTableInfo(t, dom, "test", "t1") + tb1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tbl1Info.ForeignKeys)) + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, "kid", tb1ReferredFKs[0].Cols[0].L) + require.Equal(t, "kid", tbl1Info.ForeignKeys[0].RefCols[0].L) + require.Equal(t, "aa", tbl1Info.ForeignKeys[0].Cols[0].L) + + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key, b int, index(b))") + tk.MustExec("create table t2 (a int, b int, foreign key fk(a) references t1(b));") + tk.MustExec("alter table t2 change a aa int") + tbl1Info = getTableInfo(t, dom, "test", "t1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) + require.Equal(t, "b", tb1ReferredFKs[0].Cols[0].L) + tbl2Info := getTableInfo(t, dom, "test", "t2") + tb2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) + require.Equal(t, "aa", tbl2Info.ForeignKeys[0].Cols[0].L) + require.Equal(t, "b", tbl2Info.ForeignKeys[0].RefCols[0].L) + + tk.MustExec("alter table t1 change id kid int") + tk.MustExec("alter table t1 change b bb int") + tbl1Info = getTableInfo(t, dom, "test", "t1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 1, len(tb1ReferredFKs)) + require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) + require.Equal(t, "bb", tb1ReferredFKs[0].Cols[0].L) + tbl2Info = getTableInfo(t, dom, "test", "t2") + tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) + require.Equal(t, "aa", tbl2Info.ForeignKeys[0].Cols[0].L) + require.Equal(t, "bb", tbl2Info.ForeignKeys[0].RefCols[0].L) + + tk.MustExec("drop table t1, t2") + tk.MustExec("create table t1 (id int key, b int, index(b))") + tk.MustExec("create table t2 (a int, b int, foreign key (a) references t1(b), foreign key (b) references t1(b));") + tk.MustExec("alter table t1 change b bb int") + tbl1Info = getTableInfo(t, dom, "test", "t1") + tb1ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t1") + require.Equal(t, 2, len(tb1ReferredFKs)) + require.Equal(t, 1, len(tb1ReferredFKs[0].Cols)) + require.Equal(t, 1, len(tb1ReferredFKs[1].Cols)) + require.Equal(t, "bb", tb1ReferredFKs[0].Cols[0].L) + require.Equal(t, "bb", tb1ReferredFKs[1].Cols[0].L) + tbl2Info = getTableInfo(t, dom, "test", "t2") + tb2ReferredFKs = getTableInfoReferredForeignKeys(t, dom, "test", "t2") + require.Equal(t, 0, len(tb2ReferredFKs)) + require.Equal(t, 2, len(tbl2Info.ForeignKeys)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].Cols)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[0].RefCols)) + require.Equal(t, "a", tbl2Info.ForeignKeys[0].Cols[0].L) + require.Equal(t, "bb", tbl2Info.ForeignKeys[0].RefCols[0].L) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[1].Cols)) + require.Equal(t, 1, len(tbl2Info.ForeignKeys[1].RefCols)) + require.Equal(t, "b", tbl2Info.ForeignKeys[1].Cols[0].L) + require.Equal(t, "bb", tbl2Info.ForeignKeys[1].RefCols[0].L) + tk.MustExec("alter table t2 rename column a to aa") + tk.MustExec("alter table t2 change b bb int") + tk.MustQuery("show create table t2"). + Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `aa` int(11) DEFAULT NULL,\n" + + " `bb` int(11) DEFAULT NULL,\n" + + " KEY `fk_1` (`aa`),\n KEY `fk_2` (`bb`),\n" + + " CONSTRAINT `fk_1` FOREIGN KEY (`aa`) REFERENCES `test`.`t1` (`bb`),\n" + + " CONSTRAINT `fk_2` FOREIGN KEY (`bb`) REFERENCES `test`.`t1` (`bb`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func TestDropDatabaseWithForeignKeyReferred(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") + tk.MustExec("create database test2") + tk.MustExec("create table test2.t3 (id int key, b int, foreign key fk_b(b) references test.t2(id));") + err := tk.ExecToErr("drop database test;") + require.Error(t, err) + require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) + tk.MustExec("set @@foreign_key_checks=0;") + tk.MustExec("drop database test") + + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("create database test") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int, foreign key fk_b(b) references t1(id));") + err = tk.ExecToErr("drop database test;") + require.Error(t, err) + require.Equal(t, "[ddl:3730]Cannot drop table 't2' referenced by a foreign key constraint 'fk_b' on table 't3'.", err.Error()) + tk.MustExec("drop table test2.t3") + tk.MustExec("drop database test") +} + +func TestAddForeignKey(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int);") + tk.MustExec("create table t2 (id int key, b int);") + tk.MustExec("alter table t2 add index(b)") + tk.MustExec("alter table t2 add foreign key (b) references t1(id);") + tbl2Info := getTableInfo(t, dom, "test", "t2") + require.Equal(t, int64(1), tbl2Info.MaxForeignKeyID) + tk.MustGetDBError("alter table t2 add foreign key (b) references t1(b);", infoschema.ErrForeignKeyNoIndexInParent) + tk.MustExec("alter table t1 add index(b)") + tk.MustExec("alter table t2 add foreign key (b) references t1(b);") + tk.MustGetDBError("alter table t2 add foreign key (b) references t2(b);", infoschema.ErrCannotAddForeign) + // Test auto-create index when create foreign key constraint. + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (id int key, b int, index(b));") + tk.MustExec("create table t2 (id int key, b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(b);") + tbl2Info = getTableInfo(t, dom, "test", "t2") + require.Equal(t, 1, len(tbl2Info.Indices)) + require.Equal(t, "fk", tbl2Info.Indices[0].Name.L) + require.Equal(t, model.StatePublic, tbl2Info.Indices[0].State) + tk.MustQuery("select b from t2 use index(fk)").Check(testkit.Rows()) + res := tk.MustQuery("explain select b from t2 use index(fk)") + plan := bytes.NewBuffer(nil) + rows := res.Rows() + for _, row := range rows { + for _, c := range row { + plan.WriteString(c.(string)) + plan.WriteString(" ") + } + } + require.Regexp(t, ".*IndexReader.*index:fk.*", plan.String()) + + // Test add multiple foreign key constraint in one statement. + tk.MustExec("alter table t2 add column c int, add column d int, add column e int;") + tk.MustExec("alter table t2 add index idx_c(c, d, e)") + tk.MustExec("alter table t2 add constraint fk_c foreign key (c) references t1(b), " + + "add constraint fk_d foreign key (d) references t1(b)," + + "add constraint fk_e foreign key (e) references t1(b)") + tbl2Info = getTableInfo(t, dom, "test", "t2") + require.Equal(t, 4, len(tbl2Info.Indices)) + names := []string{"fk", "idx_c", "fk_d", "fk_e"} + for i, idx := range tbl2Info.Indices { + require.Equal(t, names[i], idx.Name.L) + require.Equal(t, model.StatePublic, idx.State) + } + names = []string{"fk", "fk_c", "fk_d", "fk_e"} + for i, fkInfo := range tbl2Info.ForeignKeys { + require.Equal(t, names[i], fkInfo.Name.L) + require.Equal(t, model.StatePublic, fkInfo.State) + } + tk.MustGetDBError("insert into t2 (id, b) values (1,1)", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 (id, c) values (1,1)", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 (id, d) values (1,1)", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 (id, e) values (1,1)", plannercore.ErrNoReferencedRow2) + + // Test add multiple foreign key constraint in one statement but failed. + tk.MustExec("alter table t2 drop foreign key fk") + tk.MustExec("alter table t2 drop foreign key fk_c") + tk.MustExec("alter table t2 drop foreign key fk_d") + tk.MustExec("alter table t2 drop foreign key fk_e") + tk.MustGetDBError("alter table t2 add constraint fk_c foreign key (c) references t1(b), "+ + "add constraint fk_d foreign key (d) references t1(b),"+ + "add constraint fk_e foreign key (e) references t1(unknown_col)", infoschema.ErrForeignKeyNoColumnInParent) + tbl2Info = getTableInfo(t, dom, "test", "t2") + require.Equal(t, 0, len(tbl2Info.ForeignKeys)) + tk.MustGetDBError("alter table t2 drop index idx_c, add constraint fk_c foreign key (c) references t1(b)", dbterror.ErrDropIndexNeededInForeignKey) + + // Test circular dependency add foreign key failed. + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (id int key,a int, index(a));") + tk.MustExec("create table t2 (id int key,a int, foreign key fk(a) references t1(id) ON DELETE CASCADE);") + tk.MustExec("insert into t1 values (1,1);") + err := tk.ExecToErr("ALTER TABLE t1 ADD foreign key fk(a) references t2(id) ON DELETE CASCADE;") + require.Error(t, err) + require.Equal(t, "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t2` (`id`) ON DELETE CASCADE)", err.Error()) + tbl1Info := getTableInfo(t, dom, "test", "t1") + require.Equal(t, 0, len(tbl1Info.ForeignKeys)) + referredFKs := dom.InfoSchema().GetTableReferredForeignKeys("test", "t2") + require.Equal(t, 0, len(referredFKs)) + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `id` int(11) NOT NULL,\n" + + " `a` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `a` (`a`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // Test add foreign key with auto-create index failed. + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (id int key,a int);") + tk.MustExec("create table t2 (id int key);") + tk.MustExec("insert into t1 values (1,1);") + err = tk.ExecToErr("ALTER TABLE t1 ADD foreign key fk(a) references t2(id) ON DELETE CASCADE;") + require.Error(t, err) + require.Equal(t, "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t1`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t2` (`id`) ON DELETE CASCADE)", err.Error()) + tbl1Info = getTableInfo(t, dom, "test", "t1") + require.Equal(t, 0, len(tbl1Info.ForeignKeys)) + referredFKs = dom.InfoSchema().GetTableReferredForeignKeys("test", "t2") + require.Equal(t, 0, len(referredFKs)) + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `id` int(11) NOT NULL,\n" + + " `a` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func TestAlterTableAddForeignKeyError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + cases := []struct { + prepares []string + alter string + err string + }{ + { + prepares: []string{ + "create table t1 (id int, a int, b int);", + "create table t2 (a int, b int);", + }, + alter: "alter table t2 add foreign key fk(b) references t_unknown(id)", + err: "[schema:1824]Failed to open the referenced table 't_unknown'", + }, + { + prepares: []string{ + "create table t1 (id int, a int, b int);", + "create table t2 (a int, b int);", + }, + alter: "alter table t2 add foreign key fk(b) references t1(c_unknown)", + err: "[schema:3734]Failed to add the foreign key constraint. Missing column 'c_unknown' for constraint 'fk' in the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int, a int, b int);", + "create table t2 (a int, b int);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(b)", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int, a int, b int not null, index(b));", + "create table t2 (a int, b int not null);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(b) on update set null", + err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", + }, + { + prepares: []string{ + "create table t1 (id int, a int, b int not null, index(b));", + "create table t2 (a int, b int not null);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(b) on delete set null", + err: "[schema:1830]Column 'b' cannot be NOT NULL: needed in a foreign key constraint 'fk_b' SET NULL", + }, + { + prepares: []string{ + "create table t1 (id int key, a int, b int as (a) virtual, index(b));", + "create table t2 (a int, b int);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(b)", + err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", + }, + { + prepares: []string{ + "create table t1 (id int key, a int, b int, index(b));", + "create table t2 (a int, b int as (a) virtual);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(b)", + err: "[schema:3733]Foreign key 'fk_b' uses virtual column 'b' which is not supported.", + }, + { + prepares: []string{ + "create table t1 (id int key, a int);", + "create table t2 (a int, b varchar(10));", + }, + alter: "alter table t2 add foreign key fk(b) references t1(id)", + err: "[ddl:3780]Referencing column 'b' and referenced column 'id' in foreign key constraint 'fk' are incompatible.", + }, + { + prepares: []string{ + "create table t1 (id int key, a int not null, index(a));", + "create table t2 (a int, b int unsigned);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + prepares: []string{ + "create table t1 (id int key, a bigint, index(a));", + "create table t2 (a int, b int);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10) charset utf8, index(a));", + "create table t2 (a int, b varchar(10) charset utf8mb4);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10) collate utf8_bin, index(a));", + "create table t2 (a int, b varchar(10) collate utf8mb4_bin);", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[ddl:3780]Referencing column 'b' and referenced column 'a' in foreign key constraint 'fk_b' are incompatible.", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10));", + "create table t2 (a int, b varchar(10));", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int key, a varchar(10), index (a(5)));", + "create table t2 (a int, b varchar(10));", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(a)", + err: "[schema:1822]Failed to add the foreign key constraint. Missing index for constraint 'fk_b' in the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int key, a int)", + "create table t2 (id int, b int, index(b))", + "insert into t2 values (1,1)", + }, + alter: "alter table t2 add foreign key fk_b(b) references t1(id)", + err: "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`b`) REFERENCES `t1` (`id`))", + }, + { + prepares: []string{ + "create table t1 (id int, a int, b int, index(a,b))", + "create table t2 (id int, a int, b int, index(a,b))", + "insert into t2 values (1, 1, null), (2, null, 1), (3, null, null), (4, 1, 1)", + }, + alter: "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", + err: "[ddl:1452]Cannot add or update a child row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk_b` FOREIGN KEY (`a`, `b`) REFERENCES `t1` (`a`, `b`))", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create table t2 (a int, b int unique);", + }, + alter: "alter table t2 add foreign key name5678901234567890123456789012345678901234567890123456789012345(b) references t1(id)", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create table t2 (a int, b int unique);", + }, + alter: "alter table t2 add constraint name5678901234567890123456789012345678901234567890123456789012345 foreign key (b) references t1(id)", + err: "[ddl:1059]Identifier name 'name5678901234567890123456789012345678901234567890123456789012345' is too long", + }, + // Test foreign key with temporary table. + { + prepares: []string{ + "create temporary table t1 (id int key);", + "create table t2 (a int, b int unique);", + }, + alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", + err: "[schema:1824]Failed to open the referenced table 't1'", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create temporary table t2 (a int, b int unique);", + }, + alter: "alter table t2 add constraint fk foreign key (b) references t1(id)", + err: "[ddl:8200]TiDB doesn't support ALTER TABLE for local temporary table", + }, + // Test foreign key with partition table + { + prepares: []string{ + "create table t1 (id int key) partition by hash(id) partitions 3;", + "create table t2 (id int key);", + }, + alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + { + prepares: []string{ + "create table t1 (id int key);", + "create table t2 (id int key) partition by hash(id) partitions 3;;", + }, + alter: "alter table t2 add constraint fk foreign key (id) references t1(id)", + err: "[schema:1506]Foreign key clause is not yet supported in conjunction with partitioning", + }, + } + for i, ca := range cases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca.prepares { + tk.MustExec(sql) + } + err := tk.ExecToErr(ca.alter) + require.Error(t, err, fmt.Sprintf("%v, %v", i, ca.err)) + require.Equal(t, ca.err, err.Error()) + } + + passCases := [][]string{ + { + "create table t1 (id int key, a int, b int, index(a))", + "alter table t1 add foreign key fk(a) references t1(id)", + }, + { + "create table t1 (id int key, b int not null, index(b))", + "create table t2 (a int, b int, index(b));", + "alter table t2 add foreign key fk_b(b) references t1(b)", + }, + { + "create table t1 (id int key, a varchar(10), index(a));", + "create table t2 (a int, b varchar(20), index(b));", + "alter table t2 add foreign key fk_b(b) references t1(a)", + }, + { + "create table t1 (id int key, a decimal(10,5), index(a));", + "create table t2 (a int, b decimal(20, 10), index(b));", + "alter table t2 add foreign key fk_b(b) references t1(a)", + }, + { + "create table t1 (id int key, a varchar(10), index (a(10)));", + "create table t2 (a int, b varchar(20), index(b));", + "alter table t2 add foreign key fk_b(b) references t1(a)", + }, + { + "create table t1 (id int key, a int)", + "create table t2 (id int, b int, index(b))", + "insert into t2 values (1, null)", + "alter table t2 add foreign key fk_b(b) references t1(id)", + }, + { + "create table t1 (id int, a int, b int, index(a,b))", + "create table t2 (id int, a int, b int, index(a,b))", + "insert into t2 values (1, 1, null), (2, null, 1), (3, null, null)", + "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", + }, + { + "set @@foreign_key_checks=0;", + "create table t1 (id int, a int, b int, index(a,b))", + "create table t2 (id int, a int, b int, index(a,b))", + "insert into t2 values (1, 1, 1)", + "alter table t2 add foreign key fk_b(a, b) references t1(a, b)", + "set @@foreign_key_checks=1;", + }, + { + "set @@foreign_key_checks=0;", + "create table t2 (a int, b int, index(b));", + "alter table t2 add foreign key fk_b(b) references t_unknown(a)", + "set @@foreign_key_checks=1;", + }, + { + "create table t1 (id int key);", + "create table t2 (a int, b int unique);", + "alter table t2 add foreign key name567890123456789012345678901234567890123456789012345678901234(b) references t1(id)", + }, + } + for _, ca := range passCases { + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t1") + for _, sql := range ca { + tk.MustExec(sql) + } + } +} + +func TestRenameTablesWithForeignKey(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=0;") + tk.MustExec("create database test1") + tk.MustExec("create database test2") + tk.MustExec("use test") + tk.MustExec("create table t0 (id int key, b int);") + tk.MustExec("create table t1 (id int key, b int, index(b), foreign key fk(b) references t2(id));") + tk.MustExec("create table t2 (id int key, b int, index(b), foreign key fk(b) references t1(id));") + tk.MustExec("rename table test.t1 to test1.tt1, test.t2 to test2.tt2, test.t0 to test.tt0") + + // check the schema diff + diff := getLatestSchemaDiff(t, tk) + require.Equal(t, model.ActionRenameTables, diff.Type) + require.Equal(t, 2, len(diff.AffectedOpts)) + + // check referred foreign key information. + t1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t1") + t2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test", "t2") + require.Equal(t, 0, len(t1ReferredFKs)) + require.Equal(t, 0, len(t2ReferredFKs)) + tt1ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test1", "tt1") + tt2ReferredFKs := getTableInfoReferredForeignKeys(t, dom, "test2", "tt2") + require.Equal(t, 1, len(tt1ReferredFKs)) + require.Equal(t, 1, len(tt2ReferredFKs)) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test2"), + ChildTable: model.NewCIStr("tt2"), + ChildFKName: model.NewCIStr("fk"), + }, *tt1ReferredFKs[0]) + require.Equal(t, model.ReferredFKInfo{ + Cols: []model.CIStr{model.NewCIStr("id")}, + ChildSchema: model.NewCIStr("test1"), + ChildTable: model.NewCIStr("tt1"), + ChildFKName: model.NewCIStr("fk"), + }, *tt2ReferredFKs[0]) + + // check show create table information + tk.MustQuery("show create table test1.tt1").Check(testkit.Rows("tt1 CREATE TABLE `tt1` (\n" + + " `id` int(11) NOT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`),\n" + + " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test2`.`tt2` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustQuery("show create table test2.tt2").Check(testkit.Rows("tt2 CREATE TABLE `tt2` (\n" + + " `id` int(11) NOT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`),\n" + + " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test1`.`tt1` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func getLatestSchemaDiff(t *testing.T, tk *testkit.TestKit) *model.SchemaDiff { + ctx := tk.Session() + err := sessiontxn.NewTxn(context.Background(), ctx) + require.NoError(t, err) + txn, err := ctx.Txn(true) + require.NoError(t, err) + m := meta.NewMeta(txn) + ver, err := m.GetSchemaVersion() + require.NoError(t, err) + diff, err := m.GetSchemaDiff(ver) + require.NoError(t, err) + return diff +} + +func TestMultiSchemaAddForeignKey(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (a int, b int);") + tk.MustExec("alter table t2 add foreign key (a) references t1(id), add foreign key (b) references t1(id)") + tk.MustExec("alter table t2 add column c int, add column d int") + tk.MustExec("alter table t2 add foreign key (c) references t1(id), add foreign key (d) references t1(id), add index(c), add index(d)") + tk.MustExec("drop table t2") + tk.MustExec("create table t2 (a int, b int, index idx1(a), index idx2(b));") + tk.MustGetErrMsg("alter table t2 drop index idx1, drop index idx2, add foreign key (a) references t1(id), add foreign key (b) references t1(id)", + "[ddl:1553]Cannot drop index 'idx1': needed in a foreign key constraint") + tk.MustExec("alter table t2 drop index idx1, drop index idx2") + tk.MustExec("alter table t2 add foreign key (a) references t1(id), add foreign key (b) references t1(id)") + tk.MustQuery("show create table t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `a` int(11) DEFAULT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " KEY `fk_1` (`a`),\n" + + " KEY `fk_2` (`b`),\n" + + " CONSTRAINT `fk_1` FOREIGN KEY (`a`) REFERENCES `test`.`t1` (`id`),\n" + + " CONSTRAINT `fk_2` FOREIGN KEY (`b`) REFERENCES `test`.`t1` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("drop table t2") + tk.MustExec("create table t2 (a int, b int, index idx0(a,b), index idx1(a), index idx2(b));") + tk.MustExec("alter table t2 drop index idx1, add foreign key (a) references t1(id), add foreign key (b) references t1(id)") +} + +func TestAddForeignKeyInBigTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk.MustExec("create table employee (id bigint auto_increment key, pid bigint)") + tk.MustExec("insert into employee (id) values (1),(2),(3),(4),(5),(6),(7),(8)") + for i := 0; i < 14; i++ { + tk.MustExec("insert into employee (pid) select pid from employee") + } + tk.MustExec("update employee set pid=id-1 where id>1") + start := time.Now() + tk.MustExec("alter table employee add foreign key fk_1(pid) references employee(id)") + require.Less(t, time.Since(start), time.Minute) +} + +func TestForeignKeyWithCacheTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + // Test foreign key refer cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("alter table t1 cache;") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t1 nocache;") + tk.MustExec("drop table t1,t2;") + + // Test add foreign key on cache table. + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (b int);") + tk.MustExec("alter table t2 add constraint fk foreign key (b) references t1(id) on delete cascade on update cascade") + tk.MustExec("alter table t2 cache;") + tk.MustExec("insert into t1 values (1),(2),(3),(4)") + tk.MustExec("insert into t2 values (1),(2),(3),(4)") + tk.MustGetDBError("insert into t2 values (5)", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t1 set id = id+10 where id=1") + tk.MustExec("delete from t1 where id<10") + tk.MustQuery("select * from t1").Check(testkit.Rows("11")) + tk.MustQuery("select * from t2").Check(testkit.Rows("11")) + tk.MustExec("alter table t2 nocache;") + tk.MustExec("drop table t1,t2;") +} + +func TestForeignKeyAndConcurrentDDL(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + // Test foreign key refer cache table. + tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") + tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@foreign_key_checks=1;") + tk2.MustExec("use test") + passCases := []struct { + prepare []string + ddl1 string + ddl2 string + }{ + { + ddl1: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", + }, + { + ddl1: "alter table t2 drop foreign key fk_1", + ddl2: "alter table t2 drop foreign key fk_2", + }, + { + prepare: []string{ + "alter table t2 drop index a", + }, + ddl1: "alter table t2 add index(a)", + ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + }, + { + ddl1: "alter table t2 drop index c", + ddl2: "alter table t2 add constraint fk_2 foreign key (b) references t1(b)", + }, + } + for _, ca := range passCases { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + tk.MustExec(ca.ddl1) + }() + go func() { + defer wg.Done() + tk2.MustExec(ca.ddl2) + }() + wg.Wait() + } + errorCases := []struct { + prepare []string + ddl1 string + err1 string + ddl2 string + err2 string + }{ + { + ddl1: "alter table t2 add constraint fk foreign key (a) references t1(a)", + err1: "[ddl:1826]Duplicate foreign key constraint name 'fk'", + ddl2: "alter table t2 add constraint fk foreign key (b) references t1(b)", + err2: "[ddl:1826]Duplicate foreign key constraint name 'fk'", + }, + { + prepare: []string{ + "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + }, + ddl1: "alter table t2 drop foreign key fk_1", + err1: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", + ddl2: "alter table t2 drop foreign key fk_1", + err2: "[schema:1091]Can't DROP 'fk_1'; check that column/key exists", + }, + { + ddl1: "alter table t2 drop index a", + err1: "[ddl:1553]Cannot drop index 'a': needed in a foreign key constraint", + ddl2: "alter table t2 add constraint fk_1 foreign key (a) references t1(a)", + err2: "[ddl:-1]Failed to add the foreign key constraint. Missing index for 'fk_1' foreign key columns in the table 't2'", + }, + } + tk.MustExec("drop table t1,t2") + tk.MustExec("create table t1 (a int, b int, c int, index(a), index(b), index(c));") + tk.MustExec("create table t2 (a int, b int, c int, index(a), index(b), index(c));") + for i, ca := range errorCases { + for _, sql := range ca.prepare { + tk.MustExec(sql) + } + var wg sync.WaitGroup + var err1, err2 error + wg.Add(2) + go func() { + defer wg.Done() + err1 = tk.ExecToErr(ca.ddl1) + }() + go func() { + defer wg.Done() + err2 = tk2.ExecToErr(ca.ddl2) + }() + wg.Wait() + if (err1 == nil && err2 == nil) || (err1 != nil && err2 != nil) { + require.Failf(t, "both ddl1 and ddl2 execute success, but expect 1 error", fmt.Sprintf("idx: %v, err1: %v, err2: %v", i, err1, err2)) + } + if err1 != nil { + require.Equal(t, ca.err1, err1.Error()) + } + if err2 != nil { + require.Equal(t, ca.err2, err2.Error()) + } + } +} + +func TestForeignKeyAndRenameIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1;") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, b int, index idx1(b));") + tk.MustExec("create table t2 (id int key, b int, constraint fk foreign key (b) references t1(b));") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustExec("insert into t2 values (1,1),(2,2)") + tk.MustGetDBError("insert into t2 values (3,3)", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("delete from t1 where id=1", plannercore.ErrRowIsReferenced2) + tk.MustExec("alter table t1 rename index idx1 to idx2") + tk.MustExec("alter table t2 rename index fk to idx") + tk.MustGetDBError("insert into t2 values (3,3)", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("delete from t1 where id=1", plannercore.ErrRowIsReferenced2) + tk.MustExec("alter table t2 drop foreign key fk") + tk.MustExec("alter table t2 add foreign key fk (b) references t1(b) on delete cascade on update cascade") + tk.MustExec("alter table t1 rename index idx2 to idx3") + tk.MustExec("alter table t2 rename index idx to idx0") + tk.MustExec("delete from t1 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows("2 2")) + tk.MustQuery("select * from t2").Check(testkit.Rows("2 2")) + tk.MustExec("admin check table t1,t2") +} diff --git a/pkg/ddl/tests/fk/main_test.go b/pkg/ddl/tests/fk/main_test.go new file mode 100644 index 0000000000000..63b5889afb3ca --- /dev/null +++ b/pkg/ddl/tests/fk/main_test.go @@ -0,0 +1,56 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl_test + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + + domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) + domain.SchemaOutOfDateRetryTimes.Store(50) + + autoid.SetStep(5000) + ddl.RunInGoTest = true + + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 10000 + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/tests/indexmerge/BUILD.bazel b/pkg/ddl/tests/indexmerge/BUILD.bazel new file mode 100644 index 0000000000000..79bdb381923cb --- /dev/null +++ b/pkg/ddl/tests/indexmerge/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "indexmerge_test", + timeout = "short", + srcs = [ + "main_test.go", + "merge_test.go", + ], + flaky = True, + race = "on", + shard_count = 20, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/ingest", + "//pkg/ddl/testutil", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/errno", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/tests/indexmerge/main_test.go b/pkg/ddl/tests/indexmerge/main_test.go new file mode 100644 index 0000000000000..6208e106b9793 --- /dev/null +++ b/pkg/ddl/tests/indexmerge/main_test.go @@ -0,0 +1,56 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package indexmergetest + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + + domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) + domain.SchemaOutOfDateRetryTimes.Store(50) + + autoid.SetStep(5000) + ddl.RunInGoTest = true + + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 10000 + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/ddl/tests/indexmerge/merge_test.go b/pkg/ddl/tests/indexmerge/merge_test.go similarity index 93% rename from ddl/tests/indexmerge/merge_test.go rename to pkg/ddl/tests/indexmerge/merge_test.go index 2d80bb7a01906..5b8445524d334 100644 --- a/ddl/tests/indexmerge/merge_test.go +++ b/pkg/ddl/tests/indexmerge/merge_test.go @@ -20,15 +20,15 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -412,11 +412,11 @@ func TestAddIndexMergeDeleteNullUnique(t *testing.T) { _, err := tk1.Exec("delete from t where id = 2;") assert.NoError(t, err) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecution", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution", "1*return(true)->return(false)")) tk.MustExec("alter table t add unique index idx(a);") tk.MustQuery("select count(1) from t;").Check(testkit.Rows("1")) tk.MustExec("admin check table t;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution")) } func TestAddIndexMergeDoubleDelete(t *testing.T) { @@ -454,11 +454,11 @@ func TestAddIndexMergeDoubleDelete(t *testing.T) { _, err = tk1.Exec("delete from t where id = 2;") assert.NoError(t, err) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecution", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution", "1*return(true)->return(false)")) tk.MustExec("alter table t add unique index idx(a);") tk.MustQuery("select count(1) from t;").Check(testkit.Rows("0")) tk.MustExec("admin check table t;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution")) } func TestAddIndexMergeConflictWithPessimistic(t *testing.T) { @@ -571,11 +571,11 @@ func TestAddIndexMergeInsertOnMerging(t *testing.T) { assert.NoError(t, err) // The row should be normally updated to (6, 5). ddl.MockDMLExecutionStateMerging = nil } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateMerging", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateMerging", "return(true)")) tk.MustExec("alter table t add unique index idx(a);") tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows("6 5")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateMerging")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateMerging")) } func TestAddIndexMergeReplaceOnMerging(t *testing.T) { @@ -599,13 +599,13 @@ func TestAddIndexMergeReplaceOnMerging(t *testing.T) { assert.NoError(t, err) ddl.MockDMLExecutionStateMerging = nil } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecution", "1*return(true)->return(false)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateMerging", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateMerging", "return(true)")) tk.MustExec("alter table t add unique index idx(a);") tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows("5 8")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecution")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateMerging")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateMerging")) } func TestAddIndexMergeInsertToDeletedTempIndex(t *testing.T) { @@ -685,11 +685,11 @@ func TestAddIndexMergeReplaceDelete(t *testing.T) { _, err = tk1.Exec("delete from t where id = 2;") assert.NoError(t, err) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionMerging", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionMerging", "1*return(true)->return(false)")) tk.MustExec("alter table t add unique index idx(a);") tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows()) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionMerging")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionMerging")) } func TestAddIndexMergeDeleteDifferentHandle(t *testing.T) { @@ -732,11 +732,11 @@ func TestAddIndexMergeDeleteDifferentHandle(t *testing.T) { _, err := tk1.Exec("delete from t where id = 1;") assert.NoError(t, err) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecution", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution", "1*return(true)->return(false)")) tk.MustGetErrCode("alter table t add unique index idx(c);", errno.ErrDupEntry) tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows("3 a")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution")) } func TestAddIndexDecodeTempIndexCommonHandle(t *testing.T) { @@ -852,11 +852,11 @@ func TestAddIndexMultipleDelete(t *testing.T) { _, err := tk1.Exec("delete from t where id = 1;") assert.NoError(t, err) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecution", "1*return(true)->return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution", "1*return(true)->return(false)")) tk.MustExec("alter table t add unique index idx(b);") tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows()) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecution")) } func TestAddIndexDuplicateAndWriteConflict(t *testing.T) { diff --git a/pkg/ddl/tests/metadatalock/BUILD.bazel b/pkg/ddl/tests/metadatalock/BUILD.bazel new file mode 100644 index 0000000000000..a73297ca95700 --- /dev/null +++ b/pkg/ddl/tests/metadatalock/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "metadatalock_test", + timeout = "short", + srcs = [ + "main_test.go", + "mdl_test.go", + ], + flaky = True, + shard_count = 32, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/errno", + "//pkg/server", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/tests/metadatalock/main_test.go b/pkg/ddl/tests/metadatalock/main_test.go new file mode 100644 index 0000000000000..93d3d794dbb8c --- /dev/null +++ b/pkg/ddl/tests/metadatalock/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package metadatalocktest + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/ddl/tests/metadatalock/mdl_test.go b/pkg/ddl/tests/metadatalock/mdl_test.go similarity index 99% rename from ddl/tests/metadatalock/mdl_test.go rename to pkg/ddl/tests/metadatalock/mdl_test.go index be97f9e1f3f50..8ad85021fa2bf 100644 --- a/ddl/tests/metadatalock/mdl_test.go +++ b/pkg/ddl/tests/metadatalock/mdl_test.go @@ -21,9 +21,9 @@ import ( "time" "github.com/pingcap/failpoint" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -1174,9 +1174,9 @@ func TestMDLUpdateEtcdFail(t *testing.T) { tk.MustExec("use test") tk.MustExec("create table t(a int);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpdateMDLToETCDError", `3*return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpdateMDLToETCDError", `3*return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpdateMDLToETCDError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpdateMDLToETCDError")) }() tk.MustExec("alter table test.t add column c int") diff --git a/pkg/ddl/tests/multivaluedindex/BUILD.bazel b/pkg/ddl/tests/multivaluedindex/BUILD.bazel new file mode 100644 index 0000000000000..bf3d650763386 --- /dev/null +++ b/pkg/ddl/tests/multivaluedindex/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "multivaluedindex_test", + timeout = "short", + srcs = [ + "main_test.go", + "multi_valued_index_test.go", + ], + flaky = True, + deps = [ + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/tests/multivaluedindex/main_test.go b/pkg/ddl/tests/multivaluedindex/main_test.go new file mode 100644 index 0000000000000..7a7880f7bac16 --- /dev/null +++ b/pkg/ddl/tests/multivaluedindex/main_test.go @@ -0,0 +1,35 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package multivaluedindex + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/tests/multivaluedindex/multi_valued_index_test.go b/pkg/ddl/tests/multivaluedindex/multi_valued_index_test.go new file mode 100644 index 0000000000000..58b9fc7a2bdb9 --- /dev/null +++ b/pkg/ddl/tests/multivaluedindex/multi_valued_index_test.go @@ -0,0 +1,47 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package multivaluedindex + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestCreateMultiValuedIndexHasBinaryCollation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create table test.t (pk varchar(4) primary key clustered, j json, str varchar(255), value int, key idx((cast(j as char(100) array)), str));") + is := tk.Session().GetDomainInfoSchema().(infoschema.InfoSchema) + require.NotNil(t, is) + + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + foundIndex := false + for _, c := range tbl.Cols() { + if c.Hidden { + foundIndex = true + require.True(t, c.FieldType.IsArray()) + require.Equal(t, c.FieldType.GetCharset(), "binary") + require.Equal(t, c.FieldType.GetCollate(), "binary") + } + } + require.True(t, foundIndex) +} diff --git a/pkg/ddl/tests/partition/BUILD.bazel b/pkg/ddl/tests/partition/BUILD.bazel new file mode 100644 index 0000000000000..6f9ba066a2d5c --- /dev/null +++ b/pkg/ddl/tests/partition/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "partition_test", + timeout = "short", + srcs = [ + "db_partition_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 47, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/testutil", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/ddl/tests/partition/db_partition_test.go b/pkg/ddl/tests/partition/db_partition_test.go similarity index 98% rename from ddl/tests/partition/db_partition_test.go rename to pkg/ddl/tests/partition/db_partition_test.go index 4bb911bca79d3..83acd8251234f 100644 --- a/ddl/tests/partition/db_partition_test.go +++ b/pkg/ddl/tests/partition/db_partition_test.go @@ -27,31 +27,31 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -1990,9 +1990,9 @@ func TestAlterTableExchangePartition(t *testing.T) { tk.MustGetErrCode("alter table e12 exchange partition p0 with table e14", errno.ErrPartitionExchangeDifferentOption) // test for tiflash replica - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) }() @@ -2443,9 +2443,9 @@ func TestExchangePartitionAutoID(t *testing.T) { tk.MustExec(`insert into pt values (0), (4)`) tk.MustExec("insert into nt values (1)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/exchangePartitionAutoID", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/exchangePartitionAutoID", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/exchangePartitionAutoID")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/exchangePartitionAutoID")) }() tk.MustExec("alter table pt exchange partition p0 with table nt") @@ -3152,12 +3152,12 @@ func TestAddPartitionReplicaBiggerThanTiFlashStores(t *testing.T) { tk.MustExec("use test_partition2") tk.MustExec("drop table if exists t1") // Build a tableInfo with replica count = 1 while there is no real tiFlash store. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) tk.MustExec(`create table t1 (c int) partition by range(c) ( partition p0 values less than (100), partition p1 values less than (200))`) tk.MustExec("alter table t1 set tiflash replica 1") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) // Mock partitions replica as available. t1 := external.GetTableByName(t, tk, "test_partition2", "t1") partition := t1.Meta().Partition @@ -3178,9 +3178,9 @@ func TestAddPartitionReplicaBiggerThanTiFlashStores(t *testing.T) { defer func() { tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_error_count_limit = %v", originErrCountLimit)) }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockWaitTiFlashReplica", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockWaitTiFlashReplica", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockWaitTiFlashReplica")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockWaitTiFlashReplica")) }() require.True(t, t1.Meta().TiFlashReplica.Available) err = tk.ExecToErr("alter table t1 add partition (partition p3 values less than (300));") @@ -3201,9 +3201,9 @@ func TestReorgPartitionTiFlash(t *testing.T) { ` partition p2 values in (12,34,9))`) tk.MustExec(`insert into t values (1,"1",1), (12,"12",21),(23,"23",32),(34,"34",43),(45,"45",54),(56,"56",65)`) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) }() @@ -3237,9 +3237,9 @@ func TestReorgPartitionTiFlash(t *testing.T) { availablePids := tbl.Meta().TiFlashReplica.AvailablePartitionIDs sort.Slice(availablePids, func(i, j int) bool { return availablePids[i] < availablePids[j] }) require.Equal(t, pids, availablePids) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockWaitTiFlashReplicaOK", `return(true)`)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockWaitTiFlashReplicaOK", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/ddl/mockWaitTiFlashReplicaOK") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockWaitTiFlashReplicaOK") require.NoError(t, err) }() tk.MustExec(`alter table t reorganize partition p1, p2 into (partition p1 values in (34,2,23), partition p2 values in (12,56,9),partition p3 values in (1,8,19))`) diff --git a/pkg/ddl/tests/partition/main_test.go b/pkg/ddl/tests/partition/main_test.go new file mode 100644 index 0000000000000..7bac6d15f2c99 --- /dev/null +++ b/pkg/ddl/tests/partition/main_test.go @@ -0,0 +1,72 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package partition + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +const ( + // waitForCleanDataRound indicates how many times should we check data is cleaned or not. + waitForCleanDataRound = 150 + // waitForCleanDataInterval is a min duration between 2 check for data clean. + waitForCleanDataInterval = time.Millisecond * 100 +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} + +func backgroundExec(s kv.Storage, schema, sql string, done chan error) { + se, err := session.CreateSession4Test(s) + if err != nil { + done <- errors.Trace(err) + return + } + defer se.Close() + _, err = se.Execute(context.Background(), "use "+schema) + if err != nil { + done <- errors.Trace(err) + return + } + _, err = se.Execute(context.Background(), sql) + done <- errors.Trace(err) +} diff --git a/pkg/ddl/tests/resourcegroup/BUILD.bazel b/pkg/ddl/tests/resourcegroup/BUILD.bazel new file mode 100644 index 0000000000000..c29f275b5b85d --- /dev/null +++ b/pkg/ddl/tests/resourcegroup/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "resourcegroup_test", + timeout = "short", + srcs = ["resource_group_test.go"], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/ddl/resourcegroup", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/testkit", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/ddl/tests/resourcegroup/resource_group_test.go b/pkg/ddl/tests/resourcegroup/resource_group_test.go new file mode 100644 index 0000000000000..edee6d95d90ea --- /dev/null +++ b/pkg/ddl/tests/resourcegroup/resource_group_test.go @@ -0,0 +1,466 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package resourcegrouptest_test + +import ( + "context" + "fmt" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/failpoint" + rmpb "github.com/pingcap/kvproto/pkg/resource_manager" + "github.com/pingcap/tidb/pkg/ddl/resourcegroup" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestResourceGroupBasic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + re := require.New(t) + + hook := &callback.TestDDLCallback{Do: dom} + var groupID atomic.Int64 + onJobUpdatedExportedFunc := func(job *model.Job) { + // job.SchemaID will be assigned when the group is created. + if (job.SchemaName == "x" || job.SchemaName == "y") && job.Type == model.ActionCreateResourceGroup && job.SchemaID != 0 { + groupID.Store(job.SchemaID) + return + } + } + hook.OnJobUpdatedExported.Store(&onJobUpdatedExportedFunc) + dom.DDL().SetHook(hook) + + tk.MustExec("set global tidb_enable_resource_control = 'off'") + tk.MustGetErrCode("create user usr1 resource group rg1", mysql.ErrResourceGroupSupportDisabled) + tk.MustExec("create user usr1") + tk.MustGetErrCode("alter user usr1 resource group rg1", mysql.ErrResourceGroupSupportDisabled) + tk.MustGetErrCode("create resource group x RU_PER_SEC=1000 ", mysql.ErrResourceGroupSupportDisabled) + + tk.MustExec("set global tidb_enable_resource_control = 'on'") + + // test default resource group. + tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default UNLIMITED MEDIUM YES ")) + tk.MustExec("alter resource group `default` PRIORITY=LOW") + tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default UNLIMITED LOW YES ")) + tk.MustExec("alter resource group `default` ru_per_sec=1000") + tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default 1000 LOW YES ")) + tk.MustContainErrMsg("drop resource group `default`", "can't drop reserved resource group") + + tk.MustExec("create resource group x RU_PER_SEC=1000") + checkFunc := func(groupInfo *model.ResourceGroupInfo) { + require.Equal(t, true, groupInfo.ID != 0) + require.Equal(t, "x", groupInfo.Name.L) + require.Equal(t, groupID.Load(), groupInfo.ID) + require.Equal(t, uint64(1000), groupInfo.RURate) + require.Nil(t, groupInfo.Runaway) + } + // Check the group is correctly reloaded in the information schema. + g := testResourceGroupNameFromIS(t, tk.Session(), "x") + checkFunc(g) + + // test create if not exists + tk.MustExec("create resource group if not exists x RU_PER_SEC=10000") + // Check the resource group is not changed + g = testResourceGroupNameFromIS(t, tk.Session(), "x") + checkFunc(g) + // Check warning message + res := tk.MustQuery("show warnings") + res.Check(testkit.Rows("Note 8248 Resource group 'x' already exists")) + + tk.MustExec("set global tidb_enable_resource_control = off") + tk.MustGetErrCode("alter resource group x RU_PER_SEC=2000 ", mysql.ErrResourceGroupSupportDisabled) + tk.MustGetErrCode("drop resource group x ", mysql.ErrResourceGroupSupportDisabled) + + tk.MustExec("set global tidb_enable_resource_control = DEFAULT") + + tk.MustGetErrCode("create resource group x RU_PER_SEC=1000 ", mysql.ErrResourceGroupExists) + + tk.MustExec("alter resource group x RU_PER_SEC=2000 BURSTABLE QUERY_LIMIT=(EXEC_ELAPSED='15s' ACTION DRYRUN WATCH SIMILAR DURATION '10m0s')") + g = testResourceGroupNameFromIS(t, tk.Session(), "x") + re.Equal(uint64(2000), g.RURate) + re.Equal(int64(-1), g.BurstLimit) + re.Equal(uint64(time.Second*15/time.Millisecond), g.Runaway.ExecElapsedTimeMs) + re.Equal(model.RunawayActionDryRun, g.Runaway.Action) + re.Equal(model.WatchSimilar, g.Runaway.WatchType) + re.Equal(int64(time.Minute*10/time.Millisecond), g.Runaway.WatchDurationMs) + + tk.MustExec("alter resource group x QUERY_LIMIT=(EXEC_ELAPSED='20s' ACTION DRYRUN WATCH SIMILAR)") + g = testResourceGroupNameFromIS(t, tk.Session(), "x") + re.Equal(uint64(2000), g.RURate) + re.Equal(int64(-1), g.BurstLimit) + re.Equal(uint64(time.Second*20/time.Millisecond), g.Runaway.ExecElapsedTimeMs) + re.Equal(model.RunawayActionDryRun, g.Runaway.Action) + re.Equal(model.WatchSimilar, g.Runaway.WatchType) + re.Equal(int64(0), g.Runaway.WatchDurationMs) + + tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 2000 MEDIUM YES EXEC_ELAPSED='20s', ACTION=DRYRUN, WATCH=SIMILAR DURATION=UNLIMITED ")) + + tk.MustExec("drop resource group x") + g = testResourceGroupNameFromIS(t, tk.Session(), "x") + re.Nil(g) + + tk.MustExec("alter resource group if exists not_exists RU_PER_SEC=2000") + // Check warning message + res = tk.MustQuery("show warnings") + res.Check(testkit.Rows("Note 8249 Unknown resource group 'not_exists'")) + + tk.MustExec("create resource group y RU_PER_SEC=4000") + checkFunc = func(groupInfo *model.ResourceGroupInfo) { + re.Equal(true, groupInfo.ID != 0) + re.Equal("y", groupInfo.Name.L) + re.Equal(groupID.Load(), groupInfo.ID) + re.Equal(uint64(4000), groupInfo.RURate) + re.Equal(int64(4000), groupInfo.BurstLimit) + } + g = testResourceGroupNameFromIS(t, tk.Session(), "y") + checkFunc(g) + tk.MustExec("alter resource group y BURSTABLE RU_PER_SEC=5000 QUERY_LIMIT=(EXEC_ELAPSED='15s' ACTION KILL)") + checkFunc = func(groupInfo *model.ResourceGroupInfo) { + re.Equal(true, groupInfo.ID != 0) + re.Equal("y", groupInfo.Name.L) + re.Equal(groupID.Load(), groupInfo.ID) + re.Equal(uint64(5000), groupInfo.RURate) + re.Equal(int64(-1), groupInfo.BurstLimit) + re.Equal(uint64(time.Second*15/time.Millisecond), groupInfo.Runaway.ExecElapsedTimeMs) + re.Equal(model.RunawayActionKill, groupInfo.Runaway.Action) + re.Equal(int64(0), groupInfo.Runaway.WatchDurationMs) + } + g = testResourceGroupNameFromIS(t, tk.Session(), "y") + checkFunc(g) + tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 5000 MEDIUM YES EXEC_ELAPSED='15s', ACTION=KILL ")) + tk.MustExec("drop resource group y") + g = testResourceGroupNameFromIS(t, tk.Session(), "y") + re.Nil(g) + + tk.MustGetErrCode("create resource group x ru_per_sec=1000 ru_per_sec=200", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 ru_per_sec=200, ru_per_sec=300", "Dupliated options specified") + tk.MustGetErrCode("create resource group x burstable, burstable", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x burstable, burstable", "Dupliated options specified") + tk.MustGetErrCode("create resource group x ru_per_sec=1000, burstable, burstable", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x ru_per_sec=1000, burstable, burstable", "Dupliated options specified") + tk.MustGetErrCode("create resource group x burstable, ru_per_sec=1000, burstable", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x burstable, ru_per_sec=1000, burstable", "Dupliated options specified") + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 burstable QUERY_LIMIT=(EXEC_ELAPSED='15s' action kill action cooldown)", "Dupliated runaway options specified") + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s') burstable priority=Low, QUERY_LIMIT=(EXEC_ELAPSED='15s')", "Dupliated options specified") + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s') QUERY_LIMIT=(EXEC_ELAPSED='15s')", "Dupliated options specified") + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(action kill)", "invalid exec elapsed time") + tk.MustGetErrCode("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s' action kil)", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15s')", "unknown resource group runaway action") + tk.MustGetErrCode("create resource group x ru_per_sec=1000 EXEC_ELAPSED='15s' action kill", mysql.ErrParse) + tk.MustContainErrMsg("create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED='15d' action kill)", "unknown unit \"d\"") + groups, err := infosync.ListResourceGroups(context.TODO()) + re.Equal(1, len(groups)) + re.NoError(err) + + // Check information schema table information_schema.resource_groups + tk.MustExec("create resource group x RU_PER_SEC=1000 PRIORITY=LOW") + tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 1000 LOW NO ")) + tk.MustExec("alter resource group x RU_PER_SEC=2000 BURSTABLE QUERY_LIMIT=(EXEC_ELAPSED='15s' action kill)") + tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 2000 LOW YES EXEC_ELAPSED='15s', ACTION=KILL ")) + tk.MustQuery("show create resource group x").Check(testkit.Rows("x CREATE RESOURCE GROUP `x` RU_PER_SEC=2000, PRIORITY=LOW, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)")) + tk.MustExec("CREATE RESOURCE GROUP `x_new` RU_PER_SEC=2000 PRIORITY=LOW BURSTABLE=true QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)") + tk.MustQuery("select * from information_schema.resource_groups where name = 'x_new'").Check(testkit.Rows("x_new 2000 LOW YES EXEC_ELAPSED='15s', ACTION=KILL ")) + tk.MustExec("alter resource group x BURSTABLE=false RU_PER_SEC=3000") + tk.MustQuery("select * from information_schema.resource_groups where name = 'x'").Check(testkit.Rows("x 3000 LOW NO EXEC_ELAPSED='15s', ACTION=KILL ")) + tk.MustQuery("show create resource group x").Check(testkit.Rows("x CREATE RESOURCE GROUP `x` RU_PER_SEC=3000, PRIORITY=LOW, QUERY_LIMIT=(EXEC_ELAPSED=\"15s\" ACTION=KILL)")) + + tk.MustExec("create resource group y BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='1s' action COOLDOWN WATCH EXACT duration '1h')") + tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 2000 MEDIUM YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) + tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=2000, PRIORITY=MEDIUM, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) + tk.MustExec("CREATE RESOURCE GROUP `y_new` RU_PER_SEC=2000 PRIORITY=MEDIUM QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH EXACT DURATION=\"1h0m0s\")") + tk.MustQuery("select * from information_schema.resource_groups where name = 'y_new'").Check(testkit.Rows("y_new 2000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) + tk.MustExec("alter resource group y_new RU_PER_SEC=3000") + tk.MustQuery("select * from information_schema.resource_groups where name = 'y_new'").Check(testkit.Rows("y_new 3000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) + + tk.MustExec("CREATE RESOURCE GROUP `z` RU_PER_SEC=2000 PRIORITY=MEDIUM QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH PLAN DURATION=\"1h0m0s\")") + tk.MustQuery("select * from information_schema.resource_groups where name = 'z'").Check(testkit.Rows("z 2000 MEDIUM NO EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=PLAN DURATION='1h0m0s' ")) + + tk.MustExec("alter resource group y RU_PER_SEC=4000") + tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 4000 MEDIUM YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) + tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=4000, PRIORITY=MEDIUM, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) + + tk.MustExec("alter resource group y RU_PER_SEC=4000 PRIORITY=HIGH BURSTABLE") + tk.MustQuery("select * from information_schema.resource_groups where name = 'y'").Check(testkit.Rows("y 4000 HIGH YES EXEC_ELAPSED='1s', ACTION=COOLDOWN, WATCH=EXACT DURATION='1h0m0s' ")) + tk.MustQuery("show create resource group y").Check(testkit.Rows("y CREATE RESOURCE GROUP `y` RU_PER_SEC=4000, PRIORITY=HIGH, BURSTABLE, QUERY_LIMIT=(EXEC_ELAPSED=\"1s\" ACTION=COOLDOWN WATCH=EXACT DURATION=\"1h0m0s\")")) + + tk.MustQuery("select count(*) from information_schema.resource_groups").Check(testkit.Rows("6")) + tk.MustGetErrCode("create user usr_fail resource group nil_group", mysql.ErrResourceGroupNotExists) + tk.MustContainErrMsg("create user usr_fail resource group nil_group", "Unknown resource group 'nil_group'") + tk.MustExec("create user user2") + tk.MustGetErrCode("alter user user2 resource group nil_group", mysql.ErrResourceGroupNotExists) + tk.MustContainErrMsg("alter user user2 resource group nil_group", "Unknown resource group 'nil_group'") + + tk.MustExec("create resource group do_not_delete_rg ru_per_sec=100") + tk.MustExec("create user usr3 resource group do_not_delete_rg") + tk.MustQuery("select user_attributes from mysql.user where user = 'usr3'").Check(testkit.Rows(`{"resource_group": "do_not_delete_rg"}`)) + tk.MustContainErrMsg("drop resource group do_not_delete_rg", "user [usr3] depends on the resource group to drop") + tk.MustExec("alter user usr3 resource group `default`") + tk.MustExec("alter user usr3 resource group ``") + tk.MustExec("alter user usr3 resource group `DeFault`") + tk.MustQuery("select user_attributes from mysql.user where user = 'usr3'").Check(testkit.Rows(`{"resource_group": "default"}`)) + + tk.MustExec("alter resource group default ru_per_sec = 1000, priority = medium, background = (task_types = 'lightning, BR');") + tk.MustQuery("select * from information_schema.resource_groups where name = 'default'").Check(testkit.Rows("default 1000 MEDIUM YES TASK_TYPES='lightning,br'")) + tk.MustQuery("show create resource group default").Check(testkit.Rows("default CREATE RESOURCE GROUP `default` RU_PER_SEC=1000, PRIORITY=MEDIUM, BURSTABLE, BACKGROUND=(TASK_TYPES='lightning,br')")) + g = testResourceGroupNameFromIS(t, tk.Session(), "default") + require.EqualValues(t, g.Background.JobTypes, []string{"lightning", "br"}) + + tk.MustContainErrMsg("create resource group bg ru_per_sec = 1000 background = (task_types = 'lightning')", "unsupported operation") + tk.MustContainErrMsg("alter resource group x background=(task_types='')", "unsupported operation") + tk.MustGetErrCode("alter resource group default background=(task_types='a,b,c')", mysql.ErrResourceGroupInvalidBackgroundTaskName) +} + +func testResourceGroupNameFromIS(t *testing.T, ctx sessionctx.Context, name string) *model.ResourceGroupInfo { + dom := domain.GetDomain(ctx) + // Make sure the table schema is the new schema. + err := dom.Reload() + require.NoError(t, err) + g, _ := dom.InfoSchema().ResourceGroupByName(model.NewCIStr(name)) + return g +} + +func TestResourceGroupRunaway(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/FastRunawayGC", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/FastRunawayGC")) + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1)") + + tk.MustExec("set global tidb_enable_resource_control='on'") + tk.MustExec("create resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=KILL)") + tk.MustExec("create resource group rg2 BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' action KILL WATCH EXACT duration '1s')") + tk.MustExec("create resource group rg3 BURSTABLE RU_PER_SEC=2000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' action KILL WATCH EXACT)") + tk.MustQuery("select * from information_schema.resource_groups where name = 'rg2'").Check(testkit.Rows("rg2 2000 MEDIUM YES EXEC_ELAPSED='50ms', ACTION=KILL, WATCH=EXACT DURATION='1s' ")) + tk.MustQuery("select * from information_schema.resource_groups where name = 'rg3'").Check(testkit.Rows("rg3 2000 MEDIUM YES EXEC_ELAPSED='50ms', ACTION=KILL, WATCH=EXACT DURATION=UNLIMITED ")) + tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) + tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) + tk.MustQuery("select /*+ resource_group(rg3) */ * from t").Check(testkit.Rows("1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/sleepCoprRequest", fmt.Sprintf("return(%d)", 60))) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/sleepCoprRequest")) + }() + err := tk.QueryToErr("select /*+ resource_group(rg1) */ * from t") + require.ErrorContains(t, err, "[executor:8253]Query execution was interrupted, identified as runaway query") + + tryInterval := time.Millisecond * 200 + maxWaitDuration := time.Second * 5 + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, match_type from mysql.tidb_runaway_queries", nil, + testkit.Rows("rg1 select /*+ resource_group(rg1) */ * from t identify"), maxWaitDuration, tryInterval) + // require.Len(t, tk.MustQuery("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries").Rows(), 0) + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries", nil, + nil, maxWaitDuration, tryInterval) + tk.MustExec("alter resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='100ms' ACTION=COOLDOWN)") + tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) + + tk.MustExec("alter resource group rg1 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='100ms' ACTION=DRYRUN)") + tk.MustQuery("select /*+ resource_group(rg1) */ * from t").Check(testkit.Rows("1")) + + err = tk.QueryToErr("select /*+ resource_group(rg2) */ * from t") + require.ErrorContains(t, err, "Query execution was interrupted, identified as runaway query") + tk.MustGetErrCode("select /*+ resource_group(rg2) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, match_type from mysql.tidb_runaway_queries", nil, + testkit.Rows("rg2 select /*+ resource_group(rg2) */ * from t identify", + "rg2 select /*+ resource_group(rg2) */ * from t watch"), maxWaitDuration, tryInterval) + tk.MustQuery("select SQL_NO_CACHE resource_group_name, watch_text from mysql.tidb_runaway_watch"). + Check(testkit.Rows("rg2 select /*+ resource_group(rg2) */ * from t")) + + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, original_sql, time from mysql.tidb_runaway_queries", nil, + nil, maxWaitDuration, tryInterval) + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, watch_text, end_time from mysql.tidb_runaway_watch", nil, + nil, maxWaitDuration, tryInterval) + err = tk.QueryToErr("select /*+ resource_group(rg3) */ * from t") + require.ErrorContains(t, err, "Query execution was interrupted, identified as runaway query") + tk.MustGetErrCode("select /*+ resource_group(rg3) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) + tk.EventuallyMustQueryAndCheck("select SQL_NO_CACHE resource_group_name, watch_text from mysql.tidb_runaway_watch", nil, + testkit.Rows("rg3 select /*+ resource_group(rg3) */ * from t"), maxWaitDuration, tryInterval) + + tk.MustExec("alter resource group rg2 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=COOLDOWN)") + tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) + tk.MustExec("alter resource group rg2 RU_PER_SEC=1000 QUERY_LIMIT=(EXEC_ELAPSED='50ms' ACTION=DRYRUN)") + tk.MustQuery("select /*+ resource_group(rg2) */ * from t").Check(testkit.Rows("1")) + tk.MustGetErrCode("select /*+ resource_group(rg3) */ * from t", mysql.ErrResourceGroupQueryRunawayQuarantine) +} + +func TestAlreadyExistsDefaultResourceGroup(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/managerAlreadyCreateSomeGroups", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/managerAlreadyCreateSomeGroups")) + }() + testkit.CreateMockStoreAndDomain(t) + groups, _ := infosync.ListResourceGroups(context.TODO()) + require.Equal(t, 2, len(groups)) +} + +func TestNewResourceGroupFromOptions(t *testing.T) { + type TestCase struct { + name string + groupName string + input *model.ResourceGroupSettings + output *rmpb.ResourceGroup + err error + } + var tests []TestCase + groupName := "test" + tests = append(tests, TestCase{ + name: "empty 1", + input: &model.ResourceGroupSettings{}, + err: resourcegroup.ErrUnknownResourceGroupMode, + }) + + tests = append(tests, TestCase{ + name: "empty 2", + input: nil, + err: resourcegroup.ErrInvalidGroupSettings, + }) + + tests = append(tests, TestCase{ + name: "normal case: ru case 1", + input: &model.ResourceGroupSettings{ + RURate: 2000, + Priority: 0, + }, + output: &rmpb.ResourceGroup{ + Name: groupName, + Mode: rmpb.GroupMode_RUMode, + Priority: 0, + RUSettings: &rmpb.GroupRequestUnitSettings{ + RU: &rmpb.TokenBucket{Settings: &rmpb.TokenLimitSettings{FillRate: 2000}}, + }, + }, + }) + + tests = append(tests, TestCase{ + name: "normal case: ru case 2", + input: &model.ResourceGroupSettings{ + RURate: 5000, + Priority: 8, + }, + output: &rmpb.ResourceGroup{ + Name: groupName, + Priority: 8, + Mode: rmpb.GroupMode_RUMode, + RUSettings: &rmpb.GroupRequestUnitSettings{ + RU: &rmpb.TokenBucket{Settings: &rmpb.TokenLimitSettings{FillRate: 5000}}, + }, + }, + }) + + tests = append(tests, TestCase{ + name: "error case: native case 1", + input: &model.ResourceGroupSettings{ + CPULimiter: "8", + IOReadBandwidth: "3000MB/s", + IOWriteBandwidth: "3000Mi", + }, + err: resourcegroup.ErrUnknownResourceGroupMode, + }) + + tests = append(tests, TestCase{ + name: "error case: native case 2", + input: &model.ResourceGroupSettings{ + CPULimiter: "8c", + IOReadBandwidth: "3000Mi", + IOWriteBandwidth: "3000Mi", + }, + err: resourcegroup.ErrUnknownResourceGroupMode, + }) + + tests = append(tests, TestCase{ + name: "error case: native case 3", + input: &model.ResourceGroupSettings{ + CPULimiter: "8", + IOReadBandwidth: "3000G", + IOWriteBandwidth: "3000MB", + }, + err: resourcegroup.ErrUnknownResourceGroupMode, + }) + + tests = append(tests, TestCase{ + name: "error case: duplicated mode", + input: &model.ResourceGroupSettings{ + CPULimiter: "8", + IOReadBandwidth: "3000Mi", + IOWriteBandwidth: "3000Mi", + RURate: 1000, + }, + err: resourcegroup.ErrInvalidResourceGroupDuplicatedMode, + }) + + tests = append(tests, TestCase{ + name: "error case: duplicated mode", + groupName: "test_group_too_looooooooooooooooooooooooooooooooooooooooooooooooong", + input: &model.ResourceGroupSettings{ + CPULimiter: "8", + IOReadBandwidth: "3000Mi", + IOWriteBandwidth: "3000Mi", + RURate: 1000, + }, + err: resourcegroup.ErrTooLongResourceGroupName, + }) + + for _, test := range tests { + name := groupName + if len(test.groupName) > 0 { + name = test.groupName + } + group, err := resourcegroup.NewGroupFromOptions(name, test.input) + comment := fmt.Sprintf("[%s]\nerr1 %s\nerr2 %s", test.name, err, test.err) + if test.err != nil { + require.ErrorIs(t, err, test.err, comment) + } else { + require.NoError(t, err, comment) + require.Equal(t, test.output, group) + } + } +} + +func TestBindHints(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + re := require.New(t) + + tk.MustExec("drop resource group if exists rg1") + tk.MustExec("create resource group rg1 RU_PER_SEC=1000") + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + + tk.MustExec("create global binding for select * from t using select /*+ resource_group(rg1) */ * from t") + tk.MustQuery("select * from t") + re.Equal("rg1", tk.Session().GetSessionVars().StmtCtx.ResourceGroup) + re.Equal("default", tk.Session().GetSessionVars().ResourceGroupName) + tk.MustQuery("select a, b from t") + re.Equal("", tk.Session().GetSessionVars().StmtCtx.ResourceGroup) + re.Equal("default", tk.Session().GetSessionVars().ResourceGroupName) +} diff --git a/pkg/ddl/tests/serial/BUILD.bazel b/pkg/ddl/tests/serial/BUILD.bazel new file mode 100644 index 0000000000000..b0c9469e57e28 --- /dev/null +++ b/pkg/ddl/tests/serial/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "serial_test", + timeout = "short", + srcs = [ + "main_test.go", + "serial_test.go", + ], + flaky = True, + shard_count = 20, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/util", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/infoschema", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/util/dbterror", + "//pkg/util/gcutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/tests/serial/main_test.go b/pkg/ddl/tests/serial/main_test.go new file mode 100644 index 0000000000000..bb582f02785d0 --- /dev/null +++ b/pkg/ddl/tests/serial/main_test.go @@ -0,0 +1,78 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package serial + +import ( + "context" + "fmt" + "os" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + + domain.SchemaOutOfDateRetryInterval.Store(50 * time.Millisecond) + domain.SchemaOutOfDateRetryTimes.Store(50) + + autoid.SetStep(5000) + ddl.ReorgWaitTimeout = 30 * time.Millisecond + ddl.CheckBackfillJobFinishInterval = 50 * time.Millisecond + ddl.RunInGoTest = true + ddl.SetBatchInsertDeleteRangeSize(2) + + config.UpdateGlobal(func(conf *config.Config) { + // Test for table lock. + conf.EnableTableLock = true + conf.Instance.SlowThreshold = 10000 + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + + _, err := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) + if err != nil { + _, _ = fmt.Fprintf(os.Stderr, "ddl: infosync.GlobalInfoSyncerInit: %v\n", err) + os.Exit(1) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/ddl/tests/serial/serial_test.go b/pkg/ddl/tests/serial/serial_test.go similarity index 96% rename from ddl/tests/serial/serial_test.go rename to pkg/ddl/tests/serial/serial_test.go index 02e16f8545f49..c309da045d59e 100644 --- a/ddl/tests/serial/serial_test.go +++ b/pkg/ddl/tests/serial/serial_test.go @@ -25,29 +25,29 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/gcutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/gcutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" ) @@ -425,9 +425,9 @@ func createMockStoreAndDomain(t *testing.T) (store kv.Storage, dom *domain.Domai func TestCancelAddIndexPanic(t *testing.T) { store, dom := createMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/errorMockPanic", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/errorMockPanic", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/errorMockPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/errorMockPanic")) }() tk.MustExec("use test") tk.MustExec("drop table if exists t") @@ -685,7 +685,7 @@ func TestRecoverTableByJobIDFail(t *testing.T) { hook.OnJobRunBeforeExported = func(job *model.Job) { if job.Type == model.ActionRecoverTable { require.NoError(t, failpoint.Enable("tikvclient/mockCommitError", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockRecoverTableCommitErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockRecoverTableCommitErr", `return(true)`)) } } dom.DDL().SetHook(hook) @@ -693,7 +693,7 @@ func TestRecoverTableByJobIDFail(t *testing.T) { // do recover table. tk.MustExec(fmt.Sprintf("recover table by job %d", jobID)) require.NoError(t, failpoint.Disable("tikvclient/mockCommitError")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockRecoverTableCommitErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockRecoverTableCommitErr")) // make sure enable GC after recover table. enable, err := gcutil.CheckGCEnable(tk.Session()) @@ -744,7 +744,7 @@ func TestRecoverTableByTableNameFail(t *testing.T) { hook.OnJobRunBeforeExported = func(job *model.Job) { if job.Type == model.ActionRecoverTable { require.NoError(t, failpoint.Enable("tikvclient/mockCommitError", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockRecoverTableCommitErr", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockRecoverTableCommitErr", `return(true)`)) } } dom.DDL().SetHook(hook) @@ -752,7 +752,7 @@ func TestRecoverTableByTableNameFail(t *testing.T) { // do recover table. tk.MustExec("recover table t_recover") require.NoError(t, failpoint.Disable("tikvclient/mockCommitError")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockRecoverTableCommitErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockRecoverTableCommitErr")) // make sure enable GC after recover table. enable, err := gcutil.CheckGCEnable(tk.Session()) @@ -769,9 +769,9 @@ func TestRecoverTableByTableNameFail(t *testing.T) { func TestCancelJobByErrorCountLimit(t *testing.T) { store, _ := createMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockExceedErrorLimit", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockExceedErrorLimit")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockExceedErrorLimit")) }() tk.MustExec("use test") tk.MustExec("drop table if exists t") @@ -789,7 +789,7 @@ func TestCancelJobByErrorCountLimit(t *testing.T) { func TestTruncateTableUpdateSchemaVersionErr(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockTruncateTableUpdateVersionError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockTruncateTableUpdateVersionError", `return(true)`)) tk.MustExec("use test") tk.MustExec("drop table if exists t") @@ -803,7 +803,7 @@ func TestTruncateTableUpdateSchemaVersionErr(t *testing.T) { err = tk.ExecToErr("truncate table t") require.EqualError(t, err, "[ddl:-1]DDL job rollback, error msg: mock update version error") // Disable fail point. - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockTruncateTableUpdateVersionError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockTruncateTableUpdateVersionError")) tk.MustExec("truncate table t") } @@ -1177,9 +1177,9 @@ func TestForbidUnsupportedCollations(t *testing.T) { func TestCreateTableNoBlock(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/checkOwnerCheckAllVersionsWaitTime", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkOwnerCheckAllVersionsWaitTime", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/checkOwnerCheckAllVersionsWaitTime")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkOwnerCheckAllVersionsWaitTime")) }() save := variable.GetDDLErrorCountLimit() tk.MustExec("set @@global.tidb_ddl_error_count_limit = 1") diff --git a/pkg/ddl/tests/tiflash/BUILD.bazel b/pkg/ddl/tests/tiflash/BUILD.bazel new file mode 100644 index 0000000000000..c7855a35a4dfb --- /dev/null +++ b/pkg/ddl/tests/tiflash/BUILD.bazel @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tiflash_test", + timeout = "short", + srcs = [ + "ddl_tiflash_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 32, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/store/gcworker", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/ddl/tests/tiflash/ddl_tiflash_test.go b/pkg/ddl/tests/tiflash/ddl_tiflash_test.go similarity index 92% rename from ddl/tests/tiflash/ddl_tiflash_test.go rename to pkg/ddl/tests/tiflash/ddl_tiflash_test.go index e889e4c9da61e..7da565ffd680d 100644 --- a/ddl/tests/tiflash/ddl_tiflash_test.go +++ b/pkg/ddl/tests/tiflash/ddl_tiflash_test.go @@ -29,24 +29,24 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/placement" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/placement" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/testutils" @@ -203,9 +203,9 @@ func TestTiFlashNoRedundantPDRules(t *testing.T) { defer fCancel() // Disable emulator GC, otherwise delete range will be automatically called. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed")) }() fCancelPD := s.SetPdLoop(10000) @@ -326,9 +326,9 @@ func TestTiFlashReplicaPartitionTableBlock(t *testing.T) { lessThan := "40" // Stop loop - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/BeforeRefreshTiFlashTickeLoop", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/BeforeRefreshTiFlashTickeLoop", `return`)) defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/ddl/BeforeRefreshTiFlashTickeLoop") + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/BeforeRefreshTiFlashTickeLoop") }() tk.MustExec(fmt.Sprintf("ALTER TABLE ddltiflash ADD PARTITION (PARTITION pn VALUES LESS THAN (%v))", lessThan)) @@ -414,9 +414,9 @@ func TestTiFlashFailTruncatePartition(t *testing.T) { tk.MustExec("create table ddltiflash(i int not null, s varchar(255)) partition by range (i) (partition p0 values less than (10), partition p1 values less than (20))") tk.MustExec("alter table ddltiflash set tiflash replica 1") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/FailTiFlashTruncatePartition", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/FailTiFlashTruncatePartition", `return`)) defer func() { - failpoint.Disable("github.com/pingcap/tidb/ddl/FailTiFlashTruncatePartition") + failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/FailTiFlashTruncatePartition") }() time.Sleep(ddl.PollTiFlashInterval * RoundToBeAvailablePartitionTable) @@ -461,8 +461,8 @@ func TestTiFlashFlashbackCluster(t *testing.T) { CheckTableAvailableWithTableName(s.dom, t, 1, []string{}, "test", "t") injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) ChangeGCSafePoint(tk, time.Now().Add(-10*time.Second), "true", "10m0s") @@ -474,8 +474,8 @@ func TestTiFlashFlashbackCluster(t *testing.T) { model.ActionSetTiFlashReplica.String(), oracle.GetTimeFromTS(ts).String()) tk.MustGetErrMsg(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts)), errorMsg) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } func CheckTableAvailableWithTableName(dom *domain.Domain, t *testing.T, count uint64, labels []string, db string, table string) { @@ -591,9 +591,9 @@ func TestSetPlacementRuleWithGCWorker(t *testing.T) { for _, store := range s.cluster.GetAllStores() { cluster.AddStore(store.Id, store.Address, store.Labels...) } - failpoint.Enable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed", `return`) + failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed", `return`) defer func() { - failpoint.Disable("github.com/pingcap/tidb/store/gcworker/ignoreDeleteRangeFailed") + failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/ignoreDeleteRangeFailed") }() fCancelPD := s.SetPdLoop(10000) defer fCancelPD() @@ -743,8 +743,8 @@ func TestTiFlashBackoff(t *testing.T) { // Not available for all tables ddl.DisableTiFlashPoll(s.dom.DDL()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplacePrevAvailableValue", `return(false)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplacePrevAvailableValue", `return(false)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) ddl.EnableTiFlashPoll(s.dom.DDL()) tk.MustExec("alter table ddltiflash set tiflash replica 1") @@ -756,8 +756,8 @@ func TestTiFlashBackoff(t *testing.T) { require.NotNil(t, tb) require.False(t, tb.Meta().TiFlashReplica.Available) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplacePrevAvailableValue")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplacePrevAvailableValue")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) time.Sleep(ddl.PollTiFlashInterval * 3) tb, err = s.dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("ddltiflash")) @@ -819,11 +819,11 @@ func execWithTimeout(t *testing.T, tk *testkit.TestKit, to time.Duration, sql st case <-ctx.Done(): // Exceed given timeout logutil.BgLogger().Info("execWithTimeout meet timeout", zap.String("sql", sql)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/BatchAddTiFlashSendDone", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/BatchAddTiFlashSendDone", "return(true)")) } e := <-doneCh - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/BatchAddTiFlashSendDone")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/BatchAddTiFlashSendDone")) return true, e } @@ -838,9 +838,9 @@ func TestTiFlashBatchRateLimiter(t *testing.T) { for i := 0; i < threshold; i++ { tk.MustExec(fmt.Sprintf("create table tiflash_ddl_limit.t%v(z int)", i)) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) }() tk.MustExec("alter database tiflash_ddl_limit set tiflash replica 1") @@ -870,9 +870,9 @@ func TestTiFlashBatchRateLimiter(t *testing.T) { check(3, 3) loop := 3 - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/FastFailCheckTiFlashPendingTables", fmt.Sprintf("return(%v)", loop))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/FastFailCheckTiFlashPendingTables", fmt.Sprintf("return(%v)", loop))) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/FastFailCheckTiFlashPendingTables")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/FastFailCheckTiFlashPendingTables")) }() // We will force trigger its DDL to update schema cache. tk.MustExec(fmt.Sprintf("create table tiflash_ddl_limit.t%v(z int)", threshold+1)) @@ -923,9 +923,9 @@ func TestTiFlashBatchKill(t *testing.T) { atomic.StoreUint32(&sessVars.Killed, 1) }) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/FastFailCheckTiFlashPendingTables", `return(2)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/FastFailCheckTiFlashPendingTables", `return(2)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/FastFailCheckTiFlashPendingTables")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/FastFailCheckTiFlashPendingTables")) }() timeOut, err := execWithTimeout(t, tk, time.Second*2000, "alter database tiflash_ddl_limit set tiflash replica 1") require.Error(t, err, "[executor:1317]Query execution was interrupted") @@ -1213,9 +1213,9 @@ func TestTiFlashProgressAvailableList(t *testing.T) { require.NotNil(t, tbls[i]) s.tiflash.ResetSyncStatus(int(tbls[i].Meta().ID), false) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/PollAvailableTableProgressMaxCount", `return(2)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/PollAvailableTableProgressMaxCount", `return(2)`)) defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/ddl/PollAvailableTableProgressMaxCount") + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/PollAvailableTableProgressMaxCount") }() time.Sleep(ddl.PollTiFlashInterval * RoundToBeAvailable) @@ -1257,9 +1257,9 @@ func TestTiFlashAvailableAfterResetReplica(t *testing.T) { time.Sleep(ddl.PollTiFlashInterval * RoundToBeAvailable * 3) CheckTableAvailable(s.dom, t, 1, []string{}) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() tk.MustExec("alter table ddltiflash set tiflash replica 2") @@ -1332,13 +1332,13 @@ func TestTiFlashAvailableAfterAddPartition(t *testing.T) { require.NotNil(t, tb) // still available after adding partition. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/sleepBeforeReplicaOnly", `return(2)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/waitForAddPartition", `return(3)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/sleepBeforeReplicaOnly", `return(2)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/waitForAddPartition", `return(3)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue", `return(false)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/sleepBeforeReplicaOnly")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/waitForAddPartition")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/sleepBeforeReplicaOnly")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/waitForAddPartition")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/PollTiFlashReplicaStatusReplaceCurAvailableValue")) }() tk.MustExec("ALTER TABLE ddltiflash ADD PARTITION (PARTITION pn VALUES LESS THAN (20))") time.Sleep(ddl.PollTiFlashInterval * RoundToBeAvailable * 3) @@ -1358,11 +1358,11 @@ func TestTiFlashAvailableAfterDownOneStore(t *testing.T) { tk.MustExec("use test") tk.MustExec("drop table if exists ddltiflash") tk.MustExec("create table ddltiflash(z int) PARTITION BY RANGE(z) (PARTITION p0 VALUES LESS THAN (10))") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/OneTiFlashStoreDown", `return`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/OneTiFlashStoreDown", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/OneTiFlashStoreDown", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/OneTiFlashStoreDown", `return`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/OneTiFlashStoreDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/OneTiFlashStoreDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/OneTiFlashStoreDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/OneTiFlashStoreDown")) }() tk.MustExec("alter table ddltiflash set tiflash replica 1") diff --git a/pkg/ddl/tests/tiflash/main_test.go b/pkg/ddl/tests/tiflash/main_test.go new file mode 100644 index 0000000000000..d76564038e9de --- /dev/null +++ b/pkg/ddl/tests/tiflash/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package tiflashtest + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + ddl.SetWaitTimeWhenErrorOccurred(time.Microsecond) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ddl/testutil/BUILD.bazel b/pkg/ddl/testutil/BUILD.bazel new file mode 100644 index 0000000000000..12628e9c102f2 --- /dev/null +++ b/pkg/ddl/testutil/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/ddl/testutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/sessiontxn", + "//pkg/table", + "//pkg/table/tables", + "//pkg/types", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/ddl/testutil/testutil.go b/pkg/ddl/testutil/testutil.go new file mode 100644 index 0000000000000..0fe58f7c34762 --- /dev/null +++ b/pkg/ddl/testutil/testutil.go @@ -0,0 +1,130 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package testutil + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +// SessionExecInGoroutine export for testing. +func SessionExecInGoroutine(s kv.Storage, dbName, sql string, done chan error) { + ExecMultiSQLInGoroutine(s, dbName, []string{sql}, done) +} + +// ExecMultiSQLInGoroutine exports for testing. +func ExecMultiSQLInGoroutine(s kv.Storage, dbName string, multiSQL []string, done chan error) { + go func() { + se, err := session.CreateSession4Test(s) + if err != nil { + done <- errors.Trace(err) + return + } + defer se.Close() + _, err = se.Execute(context.Background(), "use "+dbName) + if err != nil { + done <- errors.Trace(err) + return + } + for _, sql := range multiSQL { + rs, err := se.Execute(context.Background(), sql) + if err != nil { + done <- errors.Trace(err) + return + } + if rs != nil { + done <- errors.Errorf("RecordSet should be empty") + return + } + done <- nil + } + }() +} + +// ExtractAllTableHandles extracts all handles of a given table. +func ExtractAllTableHandles(se session.Session, dbName, tbName string) ([]int64, error) { + dom := domain.GetDomain(se) + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tbName)) + if err != nil { + return nil, err + } + err = sessiontxn.NewTxn(context.Background(), se) + if err != nil { + return nil, err + } + + var allHandles []int64 + err = tables.IterRecords(tbl, se, nil, + func(h kv.Handle, _ []types.Datum, _ []*table.Column) (more bool, err error) { + allHandles = append(allHandles, h.IntValue()) + return true, nil + }) + return allHandles, err +} + +// FindIdxInfo is to get IndexInfo by index name. +func FindIdxInfo(dom *domain.Domain, dbName, tbName, idxName string) *model.IndexInfo { + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(dbName), model.NewCIStr(tbName)) + if err != nil { + logutil.BgLogger().Warn("cannot find table", zap.String("dbName", dbName), zap.String("tbName", tbName)) + return nil + } + return tbl.Meta().FindIndexByName(idxName) +} + +// SubStates is a slice of SchemaState. +type SubStates = []model.SchemaState + +// TestMatchCancelState is used to test whether the cancel state matches. +func TestMatchCancelState(t *testing.T, job *model.Job, cancelState interface{}, sql string) bool { + switch v := cancelState.(type) { + case model.SchemaState: + if job.Type == model.ActionMultiSchemaChange { + msg := fmt.Sprintf("unexpected multi-schema change(sql: %s, cancel state: %s)", sql, v) + require.Failf(t, msg, "use []model.SchemaState as cancel states instead") + return false + } + return job.SchemaState == v + case SubStates: // For multi-schema change sub-jobs. + if job.MultiSchemaInfo == nil { + msg := fmt.Sprintf("not multi-schema change(sql: %s, cancel state: %v)", sql, v) + require.Failf(t, msg, "use model.SchemaState as the cancel state instead") + return false + } + require.Equal(t, len(job.MultiSchemaInfo.SubJobs), len(v), sql) + for i, subJobSchemaState := range v { + if job.MultiSchemaInfo.SubJobs[i].SchemaState != subJobSchemaState { + return false + } + } + return true + default: + return false + } +} diff --git a/ddl/tiflash_replica_test.go b/pkg/ddl/tiflash_replica_test.go similarity index 92% rename from ddl/tiflash_replica_test.go rename to pkg/ddl/tiflash_replica_test.go index 1e35febf3e7b6..52ae97a73626e 100644 --- a/ddl/tiflash_replica_test.go +++ b/pkg/ddl/tiflash_replica_test.go @@ -25,19 +25,19 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -45,7 +45,7 @@ import ( const tiflashReplicaLease = 600 * time.Millisecond func TestSetTableFlashReplica(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) store := testkit.CreateMockStoreWithSchemaLease(t, tiflashReplicaLease) tk := testkit.NewTestKit(t, store) @@ -134,7 +134,7 @@ func TestSetTableFlashReplica(t *testing.T) { tbl, dbInfo, _ = is.FindTableByPartitionID(tbl.Meta().ID) require.Nil(t, tbl) require.Nil(t, dbInfo) - err = failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) // Test for set replica count more than the tiflash store count. @@ -161,9 +161,9 @@ func setUpRPCService(t *testing.T, addr string, dom *domain.Domain, sm util.Sess } func TestInfoSchemaForTiFlashReplica(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() store := testkit.CreateMockStore(t) @@ -183,9 +183,9 @@ func TestInfoSchemaForTiFlashReplica(t *testing.T) { func TestSetTiFlashReplicaForTemporaryTable(t *testing.T) { // test for tiflash replica - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() store := testkit.CreateMockStoreWithSchemaLease(t, tiflashReplicaLease) @@ -240,9 +240,9 @@ func TestSetTableFlashReplicaForSystemTable(t *testing.T) { } func TestSkipSchemaChecker(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) }() @@ -348,9 +348,9 @@ func TestCreateTableWithLike2(t *testing.T) { checkTbl2() // Test for table has tiflash replica. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount") require.NoError(t, err) }() @@ -432,9 +432,9 @@ func TestTruncateTable2(t *testing.T) { require.False(t, hasOldTableData) // Test for truncate table should clear the tiflash available status. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() tk.MustExec("drop table if exists t1;") diff --git a/pkg/ddl/ttl.go b/pkg/ddl/ttl.go new file mode 100644 index 0000000000000..661c9b52d42e6 --- /dev/null +++ b/pkg/ddl/ttl.go @@ -0,0 +1,234 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +func onTTLInfoRemove(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + tblInfo.TTLInfo = nil + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func onTTLInfoChange(d *ddlCtx, t *meta.Meta, job *model.Job) (ver int64, err error) { + // at least one for them is not nil + var ttlInfo *model.TTLInfo + var ttlInfoEnable *bool + var ttlInfoJobInterval *string + + if err := job.DecodeArgs(&ttlInfo, &ttlInfoEnable, &ttlInfoJobInterval); err != nil { + job.State = model.JobStateCancelled + return ver, errors.Trace(err) + } + + tblInfo, err := GetTableInfoAndCancelFaultJob(t, job, job.SchemaID) + if err != nil { + return ver, errors.Trace(err) + } + + if ttlInfo != nil { + // if the TTL_ENABLE is not set explicitly, use the original value + if ttlInfoEnable == nil && tblInfo.TTLInfo != nil { + ttlInfo.Enable = tblInfo.TTLInfo.Enable + } + if ttlInfoJobInterval == nil && tblInfo.TTLInfo != nil { + ttlInfo.JobInterval = tblInfo.TTLInfo.JobInterval + } + tblInfo.TTLInfo = ttlInfo + } + if ttlInfoEnable != nil { + if tblInfo.TTLInfo == nil { + return ver, errors.Trace(dbterror.ErrSetTTLOptionForNonTTLTable.FastGenByArgs("TTL_ENABLE")) + } + + tblInfo.TTLInfo.Enable = *ttlInfoEnable + } + if ttlInfoJobInterval != nil { + if tblInfo.TTLInfo == nil { + return ver, errors.Trace(dbterror.ErrSetTTLOptionForNonTTLTable.FastGenByArgs("TTL_JOB_INTERVAL")) + } + + tblInfo.TTLInfo.JobInterval = *ttlInfoJobInterval + } + + ver, err = updateVersionAndTableInfo(d, t, job, tblInfo, true) + if err != nil { + return ver, errors.Trace(err) + } + job.FinishTableJob(model.JobStateDone, model.StatePublic, ver, tblInfo) + return ver, nil +} + +func checkTTLInfoValid(ctx sessionctx.Context, schema model.CIStr, tblInfo *model.TableInfo) error { + if err := checkTTLIntervalExpr(ctx, tblInfo.TTLInfo); err != nil { + return err + } + + if err := checkTTLTableSuitable(ctx, schema, tblInfo); err != nil { + return err + } + + return checkTTLInfoColumnType(tblInfo) +} + +func checkTTLIntervalExpr(ctx sessionctx.Context, ttlInfo *model.TTLInfo) error { + // FIXME: use a better way to validate the interval expression in ttl + var nowAddIntervalExpr ast.ExprNode + + unit := ast.TimeUnitType(ttlInfo.IntervalTimeUnit) + expr := fmt.Sprintf("select NOW() + INTERVAL %s %s", ttlInfo.IntervalExprStr, unit.String()) + stmts, _, err := parser.New().ParseSQL(expr) + if err != nil { + // FIXME: the error information can be wrong, as it could indicate an unknown position to user. + return errors.Trace(err) + } + nowAddIntervalExpr = stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr + _, err = expression.EvalAstExpr(ctx, nowAddIntervalExpr) + return err +} + +func checkTTLInfoColumnType(tblInfo *model.TableInfo) error { + colInfo := findColumnByName(tblInfo.TTLInfo.ColumnName.L, tblInfo) + if colInfo == nil { + return dbterror.ErrBadField.GenWithStackByArgs(tblInfo.TTLInfo.ColumnName.O, "TTL config") + } + if !types.IsTypeTime(colInfo.FieldType.GetType()) { + return dbterror.ErrUnsupportedColumnInTTLConfig.GenWithStackByArgs(tblInfo.TTLInfo.ColumnName.O) + } + + return nil +} + +// checkTTLTableSuitable returns whether this table is suitable to be a TTL table +// A temporary table or a parent table referenced by a foreign key cannot be TTL table +func checkTTLTableSuitable(ctx sessionctx.Context, schema model.CIStr, tblInfo *model.TableInfo) error { + if tblInfo.TempTableType != model.TempTableNone { + return dbterror.ErrTempTableNotAllowedWithTTL + } + + if err := checkPrimaryKeyForTTLTable(tblInfo); err != nil { + return err + } + + // checks even when the foreign key check is not enabled, to keep safe + is := sessiontxn.GetTxnManager(ctx).GetTxnInfoSchema() + if referredFK := checkTableHasForeignKeyReferred(is, schema.L, tblInfo.Name.L, nil, true); referredFK != nil { + return dbterror.ErrUnsupportedTTLReferencedByFK + } + + return nil +} + +func checkDropColumnWithTTLConfig(tblInfo *model.TableInfo, colName string) error { + if tblInfo.TTLInfo != nil { + if tblInfo.TTLInfo.ColumnName.L == colName { + return dbterror.ErrTTLColumnCannotDrop.GenWithStackByArgs(colName) + } + } + + return nil +} + +// We should forbid creating a TTL table with clustered primary key that contains a column with type float/double. +// This is because currently we are using SQL to delete expired rows and when the primary key contains float/double column, +// it is hard to use condition `WHERE PK in (...)` to delete specified rows because some precision will be lost when comparing. +func checkPrimaryKeyForTTLTable(tblInfo *model.TableInfo) error { + if !tblInfo.IsCommonHandle { + // only check the primary keys when it is common handle + return nil + } + + pk := tblInfo.GetPrimaryKey() + if pk == nil { + return nil + } + + for _, colDef := range pk.Columns { + col := tblInfo.Columns[colDef.Offset] + switch col.GetType() { + case mysql.TypeFloat, mysql.TypeDouble: + return dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL + } + } + + return nil +} + +// getTTLInfoInOptions returns the aggregated ttlInfo, the ttlEnable, or an error. +// if TTL, TTL_ENABLE or TTL_JOB_INTERVAL is not set in the config, the corresponding return value will be nil. +// if both of TTL and TTL_ENABLE are set, the `ttlInfo.Enable` will be equal with `ttlEnable`. +// if both of TTL and TTL_JOB_INTERVAL are set, the `ttlInfo.JobInterval` will be equal with `ttlCronJobSchedule`. +func getTTLInfoInOptions(options []*ast.TableOption) (ttlInfo *model.TTLInfo, ttlEnable *bool, ttlCronJobSchedule *string, err error) { + for _, op := range options { + switch op.Tp { + case ast.TableOptionTTL: + var sb strings.Builder + restoreFlags := format.RestoreStringSingleQuotes | format.RestoreNameBackQuotes + restoreCtx := format.NewRestoreCtx(restoreFlags, &sb) + err := op.Value.Restore(restoreCtx) + if err != nil { + return nil, nil, nil, err + } + + intervalExpr := sb.String() + ttlInfo = &model.TTLInfo{ + ColumnName: op.ColumnName.Name, + IntervalExprStr: intervalExpr, + IntervalTimeUnit: int(op.TimeUnitValue.Unit), + Enable: true, + JobInterval: "1h", + } + case ast.TableOptionTTLEnable: + ttlEnable = &op.BoolValue + case ast.TableOptionTTLJobInterval: + ttlCronJobSchedule = &op.StrValue + } + } + + if ttlInfo != nil { + if ttlEnable != nil { + ttlInfo.Enable = *ttlEnable + } + if ttlCronJobSchedule != nil { + ttlInfo.JobInterval = *ttlCronJobSchedule + } + } + return ttlInfo, ttlEnable, ttlCronJobSchedule, nil +} diff --git a/ddl/ttl_test.go b/pkg/ddl/ttl_test.go similarity index 97% rename from ddl/ttl_test.go rename to pkg/ddl/ttl_test.go index fafc9e240a710..1fa6ca6acc269 100644 --- a/ddl/ttl_test.go +++ b/pkg/ddl/ttl_test.go @@ -17,8 +17,8 @@ package ddl import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/assert" ) diff --git a/pkg/ddl/util/BUILD.bazel b/pkg/ddl/util/BUILD.bazel new file mode 100644 index 0000000000000..198b5249f4cf2 --- /dev/null +++ b/pkg/ddl/util/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "util", + srcs = [ + "dead_table_lock_checker.go", + "event.go", + "mock.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ddl/util", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//tikvrpc", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "util_test", + timeout = "short", + srcs = ["main_test.go"], + embed = [":util"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ddl/util/callback/BUILD.bazel b/pkg/ddl/util/callback/BUILD.bazel new file mode 100644 index 0000000000000..c656f29eb348c --- /dev/null +++ b/pkg/ddl/util/callback/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "callback", + srcs = ["callback.go"], + importpath = "github.com/pingcap/tidb/pkg/ddl/util/callback", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "callback_test", + timeout = "short", + srcs = ["callback_test.go"], + embed = [":callback"], + flaky = True, + deps = [ + "//pkg/ddl", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/ddl/util/callback/callback.go b/pkg/ddl/util/callback/callback.go new file mode 100644 index 0000000000000..c4dade9f55d49 --- /dev/null +++ b/pkg/ddl/util/callback/callback.go @@ -0,0 +1,173 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package callback + +import ( + "context" + "sync/atomic" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// TestInterceptor is a test interceptor in the ddl +type TestInterceptor struct { + *ddl.BaseInterceptor + + OnGetInfoSchemaExported func(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema +} + +// OnGetInfoSchema is to run when to call GetInfoSchema +func (ti *TestInterceptor) OnGetInfoSchema(ctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { + if ti.OnGetInfoSchemaExported != nil { + return ti.OnGetInfoSchemaExported(ctx, is) + } + + return ti.BaseInterceptor.OnGetInfoSchema(ctx, is) +} + +// TestDDLCallback is used to customize user callback themselves. +type TestDDLCallback struct { + *ddl.BaseCallback + // We recommended to pass the domain parameter to the test ddl callback, it will ensure + // domain to reload schema before your ddl stepping into the next state change. + Do ddl.DomainReloader + + onJobRunBefore func(*model.Job) + OnJobRunBeforeExported func(*model.Job) + OnJobRunAfterExported func(*model.Job) + onJobUpdated func(*model.Job) + OnJobUpdatedExported atomic.Pointer[func(*model.Job)] + onWatched func(ctx context.Context) + OnGetJobBeforeExported func(string) + OnGetJobAfterExported func(string, *model.Job) + OnJobSchemaStateChanged func(int64) + + OnUpdateReorgInfoExported func(job *model.Job, pid int64) +} + +// OnChanged mock the same behavior with the main DDL hook. +func (tc *TestDDLCallback) OnChanged(err error) error { + if err != nil { + return err + } + logutil.BgLogger().Info("performing DDL change, must reload") + if tc.Do != nil { + err = tc.Do.Reload() + if err != nil { + logutil.BgLogger().Error("performing DDL change failed", zap.Error(err)) + } + } + return nil +} + +// OnSchemaStateChanged mock the same behavior with the main ddl hook. +func (tc *TestDDLCallback) OnSchemaStateChanged(schemaVer int64) { + if tc.Do != nil { + if err := tc.Do.Reload(); err != nil { + logutil.BgLogger().Warn("reload failed on schema state changed", zap.Error(err)) + } + } + + if tc.OnJobSchemaStateChanged != nil { + tc.OnJobSchemaStateChanged(schemaVer) + return + } +} + +// OnJobRunBefore is used to run the user customized logic of `onJobRunBefore` first. +func (tc *TestDDLCallback) OnJobRunBefore(job *model.Job) { + logutil.BgLogger().Info("on job run before", zap.String("job", job.String())) + if tc.OnJobRunBeforeExported != nil { + tc.OnJobRunBeforeExported(job) + return + } + if tc.onJobRunBefore != nil { + tc.onJobRunBefore(job) + return + } + + tc.BaseCallback.OnJobRunBefore(job) +} + +// OnJobRunAfter is used to run the user customized logic of `OnJobRunAfter` first. +func (tc *TestDDLCallback) OnJobRunAfter(job *model.Job) { + logutil.BgLogger().Info("on job run after", zap.String("job", job.String())) + if tc.OnJobRunAfterExported != nil { + tc.OnJobRunAfterExported(job) + return + } + + tc.BaseCallback.OnJobRunAfter(job) +} + +// OnJobUpdated is used to run the user customized logic of `OnJobUpdated` first. +func (tc *TestDDLCallback) OnJobUpdated(job *model.Job) { + logutil.BgLogger().Info("on job updated", zap.String("job", job.String())) + if onJobUpdatedExportedFunc := tc.OnJobUpdatedExported.Load(); onJobUpdatedExportedFunc != nil { + (*onJobUpdatedExportedFunc)(job) + return + } + if tc.onJobUpdated != nil { + tc.onJobUpdated(job) + return + } + + tc.BaseCallback.OnJobUpdated(job) +} + +// OnWatched is used to run the user customized logic of `OnWatched` first. +func (tc *TestDDLCallback) OnWatched(ctx context.Context) { + if tc.onWatched != nil { + tc.onWatched(ctx) + return + } + + tc.BaseCallback.OnWatched(ctx) +} + +// OnGetJobBefore implements Callback.OnGetJobBefore interface. +func (tc *TestDDLCallback) OnGetJobBefore(jobType string) { + if tc.OnGetJobBeforeExported != nil { + tc.OnGetJobBeforeExported(jobType) + return + } + tc.BaseCallback.OnGetJobBefore(jobType) +} + +// OnGetJobAfter implements Callback.OnGetJobAfter interface. +func (tc *TestDDLCallback) OnGetJobAfter(jobType string, job *model.Job) { + if tc.OnGetJobAfterExported != nil { + tc.OnGetJobAfterExported(jobType, job) + return + } + tc.BaseCallback.OnGetJobAfter(jobType, job) +} + +// Clone copies the callback and take its reference +func (tc *TestDDLCallback) Clone() *TestDDLCallback { + return &*tc +} + +// OnUpdateReorgInfo mock the same behavior with the main DDL reorg hook. +func (tc *TestDDLCallback) OnUpdateReorgInfo(job *model.Job, pid int64) { + if tc.OnUpdateReorgInfoExported != nil { + tc.OnUpdateReorgInfoExported(job, pid) + } +} diff --git a/ddl/util/callback/callback_test.go b/pkg/ddl/util/callback/callback_test.go similarity index 96% rename from ddl/util/callback/callback_test.go rename to pkg/ddl/util/callback/callback_test.go index f611394909e48..f1bf6ce3ee15c 100644 --- a/ddl/util/callback/callback_test.go +++ b/pkg/ddl/util/callback/callback_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - "github.com/pingcap/tidb/ddl" + "github.com/pingcap/tidb/pkg/ddl" "github.com/stretchr/testify/require" ) diff --git a/ddl/util/dead_table_lock_checker.go b/pkg/ddl/util/dead_table_lock_checker.go similarity index 97% rename from ddl/util/dead_table_lock_checker.go rename to pkg/ddl/util/dead_table_lock_checker.go index f598171b0f523..0dd404dc7fb8e 100644 --- a/ddl/util/dead_table_lock_checker.go +++ b/pkg/ddl/util/dead_table_lock_checker.go @@ -20,8 +20,8 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/ddl/util/event.go b/pkg/ddl/util/event.go similarity index 97% rename from ddl/util/event.go rename to pkg/ddl/util/event.go index 9f0171254720a..512a702e09416 100644 --- a/ddl/util/event.go +++ b/pkg/ddl/util/event.go @@ -17,7 +17,7 @@ package util import ( "fmt" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) // Event is an event that a ddl operation happened. diff --git a/pkg/ddl/util/main_test.go b/pkg/ddl/util/main_test.go new file mode 100644 index 0000000000000..e5becaafd566e --- /dev/null +++ b/pkg/ddl/util/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package util + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/ddl/util/mock.go b/pkg/ddl/util/mock.go similarity index 100% rename from ddl/util/mock.go rename to pkg/ddl/util/mock.go diff --git a/pkg/ddl/util/util.go b/pkg/ddl/util/util.go new file mode 100644 index 0000000000000..a4906dedee339 --- /dev/null +++ b/pkg/ddl/util/util.go @@ -0,0 +1,364 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package util + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/tikvrpc" + clientv3 "go.etcd.io/etcd/client/v3" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +const ( + deleteRangesTable = `gc_delete_range` + doneDeleteRangesTable = `gc_delete_range_done` + loadDeleteRangeSQL = `SELECT HIGH_PRIORITY job_id, element_id, start_key, end_key FROM mysql.%n WHERE ts < %?` + recordDoneDeletedRangeSQL = `INSERT IGNORE INTO mysql.gc_delete_range_done SELECT * FROM mysql.gc_delete_range WHERE job_id = %? AND element_id = %?` + completeDeleteRangeSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %? AND element_id = %?` + completeDeleteMultiRangesSQL = `DELETE FROM mysql.gc_delete_range WHERE job_id = %?` + updateDeleteRangeSQL = `UPDATE mysql.gc_delete_range SET start_key = %? WHERE job_id = %? AND element_id = %? AND start_key = %?` + deleteDoneRecordSQL = `DELETE FROM mysql.gc_delete_range_done WHERE job_id = %? AND element_id = %?` + loadGlobalVars = `SELECT HIGH_PRIORITY variable_name, variable_value from mysql.global_variables where variable_name in (` // + nameList + ")" + // KeyOpDefaultTimeout is the default timeout for each key operation. + KeyOpDefaultTimeout = 2 * time.Second + // KeyOpRetryInterval is the interval between two key operations. + KeyOpRetryInterval = 30 * time.Millisecond + // DDLAllSchemaVersions is the path on etcd that is used to store all servers current schema versions. + DDLAllSchemaVersions = "/tidb/ddl/all_schema_versions" + // DDLAllSchemaVersionsByJob is the path on etcd that is used to store all servers current schema versions. + DDLAllSchemaVersionsByJob = "/tidb/ddl/all_schema_by_job_versions" + // DDLGlobalSchemaVersion is the path on etcd that is used to store the latest schema versions. + DDLGlobalSchemaVersion = "/tidb/ddl/global_schema_version" + // ServerGlobalState is the path on etcd that is used to store the server global state. + ServerGlobalState = "/tidb/server/global_state" + // SessionTTL is the etcd session's TTL in seconds. + SessionTTL = 90 +) + +// DelRangeTask is for run delete-range command in gc_worker. +type DelRangeTask struct { + StartKey kv.Key + EndKey kv.Key + JobID int64 + ElementID int64 +} + +// Range returns the range [start, end) to delete. +func (t DelRangeTask) Range() (kv.Key, kv.Key) { + return t.StartKey, t.EndKey +} + +// LoadDeleteRanges loads delete range tasks from gc_delete_range table. +func LoadDeleteRanges(ctx context.Context, sctx sessionctx.Context, safePoint uint64) (ranges []DelRangeTask, _ error) { + return loadDeleteRangesFromTable(ctx, sctx, deleteRangesTable, safePoint) +} + +// LoadDoneDeleteRanges loads deleted ranges from gc_delete_range_done table. +func LoadDoneDeleteRanges(ctx context.Context, sctx sessionctx.Context, safePoint uint64) (ranges []DelRangeTask, _ error) { + return loadDeleteRangesFromTable(ctx, sctx, doneDeleteRangesTable, safePoint) +} + +func loadDeleteRangesFromTable(ctx context.Context, sctx sessionctx.Context, table string, safePoint uint64) (ranges []DelRangeTask, _ error) { + rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, loadDeleteRangeSQL, table, safePoint) + if rs != nil { + defer terror.Call(rs.Close) + } + if err != nil { + return nil, errors.Trace(err) + } + + req := rs.NewChunk(nil) + it := chunk.NewIterator4Chunk(req) + for { + err = rs.Next(context.TODO(), req) + if err != nil { + return nil, errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + + for row := it.Begin(); row != it.End(); row = it.Next() { + startKey, err := hex.DecodeString(row.GetString(2)) + if err != nil { + return nil, errors.Trace(err) + } + endKey, err := hex.DecodeString(row.GetString(3)) + if err != nil { + return nil, errors.Trace(err) + } + ranges = append(ranges, DelRangeTask{ + JobID: row.GetInt64(0), + ElementID: row.GetInt64(1), + StartKey: startKey, + EndKey: endKey, + }) + } + } + return ranges, nil +} + +// CompleteDeleteRange moves a record from gc_delete_range table to gc_delete_range_done table. +func CompleteDeleteRange(sctx sessionctx.Context, dr DelRangeTask, needToRecordDone bool) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "BEGIN") + if err != nil { + return errors.Trace(err) + } + + if needToRecordDone { + _, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, recordDoneDeletedRangeSQL, dr.JobID, dr.ElementID) + if err != nil { + return errors.Trace(err) + } + } + + err = RemoveFromGCDeleteRange(sctx, dr.JobID, dr.ElementID) + if err != nil { + return errors.Trace(err) + } + _, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "COMMIT") + return errors.Trace(err) +} + +// RemoveFromGCDeleteRange is exported for ddl pkg to use. +func RemoveFromGCDeleteRange(sctx sessionctx.Context, jobID, elementID int64) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, completeDeleteRangeSQL, jobID, elementID) + return errors.Trace(err) +} + +// RemoveMultiFromGCDeleteRange is exported for ddl pkg to use. +func RemoveMultiFromGCDeleteRange(ctx context.Context, sctx sessionctx.Context, jobID int64) error { + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, completeDeleteMultiRangesSQL, jobID) + return errors.Trace(err) +} + +// DeleteDoneRecord removes a record from gc_delete_range_done table. +func DeleteDoneRecord(sctx sessionctx.Context, dr DelRangeTask) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, deleteDoneRecordSQL, dr.JobID, dr.ElementID) + return errors.Trace(err) +} + +// UpdateDeleteRange is only for emulator. +func UpdateDeleteRange(sctx sessionctx.Context, dr DelRangeTask, newStartKey, oldStartKey kv.Key) error { + newStartKeyHex := hex.EncodeToString(newStartKey) + oldStartKeyHex := hex.EncodeToString(oldStartKey) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, updateDeleteRangeSQL, newStartKeyHex, dr.JobID, dr.ElementID, oldStartKeyHex) + return errors.Trace(err) +} + +// LoadDDLReorgVars loads ddl reorg variable from mysql.global_variables. +func LoadDDLReorgVars(ctx context.Context, sctx sessionctx.Context) error { + // close issue #21391 + // variable.TiDBRowFormatVersion is used to encode the new row for column type change. + return LoadGlobalVars(ctx, sctx, []string{variable.TiDBDDLReorgWorkerCount, variable.TiDBDDLReorgBatchSize, variable.TiDBRowFormatVersion}) +} + +// LoadDDLVars loads ddl variable from mysql.global_variables. +func LoadDDLVars(ctx sessionctx.Context) error { + return LoadGlobalVars(context.Background(), ctx, []string{variable.TiDBDDLErrorCountLimit}) +} + +// LoadGlobalVars loads global variable from mysql.global_variables. +func LoadGlobalVars(ctx context.Context, sctx sessionctx.Context, varNames []string) error { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) + if e, ok := sctx.(sqlexec.RestrictedSQLExecutor); ok { + var buf strings.Builder + buf.WriteString(loadGlobalVars) + paramNames := make([]interface{}, 0, len(varNames)) + for i, name := range varNames { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString("%?") + paramNames = append(paramNames, name) + } + buf.WriteString(")") + rows, _, err := e.ExecRestrictedSQL(ctx, nil, buf.String(), paramNames...) + if err != nil { + return errors.Trace(err) + } + for _, row := range rows { + varName := row.GetString(0) + varValue := row.GetString(1) + if err = sctx.GetSessionVars().SetSystemVarWithoutValidation(varName, varValue); err != nil { + return err + } + } + } + return nil +} + +// GetTimeZone gets the session location's zone name and offset. +func GetTimeZone(sctx sessionctx.Context) (string, int) { + loc := sctx.GetSessionVars().Location() + name := loc.String() + if name != "" { + _, err := time.LoadLocation(name) + if err == nil { + return name, 0 + } + } + _, offset := time.Now().In(loc).Zone() + return "", offset +} + +// enableEmulatorGC means whether to enable emulator GC. The default is enable. +// In some unit tests, we want to stop emulator GC, then wen can set enableEmulatorGC to 0. +var emulatorGCEnable = atomicutil.NewInt32(1) + +// EmulatorGCEnable enables emulator gc. It exports for testing. +func EmulatorGCEnable() { + emulatorGCEnable.Store(1) +} + +// EmulatorGCDisable disables emulator gc. It exports for testing. +func EmulatorGCDisable() { + emulatorGCEnable.Store(0) +} + +// IsEmulatorGCEnable indicates whether emulator GC enabled. It exports for testing. +func IsEmulatorGCEnable() bool { + return emulatorGCEnable.Load() == 1 +} + +var internalResourceGroupTag = []byte{0} + +// GetInternalResourceGroupTaggerForTopSQL only use for testing. +func GetInternalResourceGroupTaggerForTopSQL() tikvrpc.ResourceGroupTagger { + tagger := func(req *tikvrpc.Request) { + req.ResourceGroupTag = internalResourceGroupTag + } + return tagger +} + +// IsInternalResourceGroupTaggerForTopSQL use for testing. +func IsInternalResourceGroupTaggerForTopSQL(tag []byte) bool { + return bytes.Equal(tag, internalResourceGroupTag) +} + +// DeleteKeyFromEtcd deletes key value from etcd. +func DeleteKeyFromEtcd(key string, etcdCli *clientv3.Client, retryCnt int, timeout time.Duration) error { + var err error + ctx := context.Background() + for i := 0; i < retryCnt; i++ { + childCtx, cancel := context.WithTimeout(ctx, timeout) + _, err = etcdCli.Delete(childCtx, key) + cancel() + if err == nil { + return nil + } + logutil.BgLogger().Warn("etcd-cli delete key failed", zap.String("category", "ddl"), zap.String("key", key), zap.Error(err), zap.Int("retryCnt", i)) + } + return errors.Trace(err) +} + +// PutKVToEtcd puts key value to etcd. +// etcdCli is client of etcd. +// retryCnt is retry time when an error occurs. +// opts are configures of etcd Operations. +func PutKVToEtcd(ctx context.Context, etcdCli *clientv3.Client, retryCnt int, key, val string, + opts ...clientv3.OpOption) error { + var err error + for i := 0; i < retryCnt; i++ { + if IsContextDone(ctx) { + return errors.Trace(ctx.Err()) + } + + childCtx, cancel := context.WithTimeout(ctx, KeyOpDefaultTimeout) + _, err = etcdCli.Put(childCtx, key, val, opts...) + cancel() + if err == nil { + return nil + } + logutil.BgLogger().Warn("etcd-cli put kv failed", zap.String("category", "ddl"), zap.String("key", key), zap.String("value", val), zap.Error(err), zap.Int("retryCnt", i)) + time.Sleep(KeyOpRetryInterval) + } + return errors.Trace(err) +} + +// IsContextDone checks if context is done. +func IsContextDone(ctx context.Context) bool { + select { + case <-ctx.Done(): + return true + default: + } + return false +} + +// WrapKey2String wraps the key to a string. +func WrapKey2String(key []byte) string { + if len(key) == 0 { + return "''" + } + return fmt.Sprintf("0x%x", key) +} + +const ( + getRaftKvVersionSQL = "show config where type = 'tikv' and name = 'storage.engine'" + raftKv2 = "raft-kv2" +) + +// IsRaftKv2 checks whether the raft-kv2 is enabled +func IsRaftKv2(ctx context.Context, sctx sessionctx.Context) (bool, error) { + // Mock store does not support `show config` now, so we use failpoint here + // to control whether we are in raft-kv2 + failpoint.Inject("IsRaftKv2", func(v failpoint.Value) (bool, error) { + v2, _ := v.(bool) + return v2, nil + }) + + rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, getRaftKvVersionSQL) + if err != nil { + return false, err + } + if rs == nil { + return false, nil + } + + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, sctx.GetSessionVars().MaxChunkSize) + if err != nil { + return false, errors.Trace(err) + } + if len(rows) == 0 { + return false, nil + } + + // All nodes should have the same type of engine + raftVersion := rows[0].GetString(3) + return raftVersion == raftKv2, nil +} diff --git a/pkg/distsql/BUILD.bazel b/pkg/distsql/BUILD.bazel new file mode 100644 index 0000000000000..e6acd81c960b6 --- /dev/null +++ b/pkg/distsql/BUILD.bazel @@ -0,0 +1,98 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "distsql", + srcs = [ + "distsql.go", + "request_builder.go", + "select_result.go", + ], + importpath = "github.com/pingcap/tidb/pkg/distsql", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl/placement", + "//pkg/errno", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/util", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/tablecodec", + "//pkg/telemetry", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/execdetails", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/ranger", + "//pkg/util/tracing", + "//pkg/util/trxevents", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//metrics", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@org_golang_google_grpc//metadata", + "@org_golang_x_exp//maps", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "distsql_test", + timeout = "short", + srcs = [ + "bench_test.go", + "distsql_test.go", + "main_test.go", + "request_builder_test.go", + "select_result_test.go", + ], + embed = [":distsql"], + flaky = True, + race = "on", + shard_count = 24, + deps = [ + "//pkg/domain/resourcegroup", + "//pkg/kv", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/disk", + "//pkg/util/execdetails", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/paging", + "//pkg/util/ranger", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/distsql/OWNERS b/pkg/distsql/OWNERS similarity index 100% rename from distsql/OWNERS rename to pkg/distsql/OWNERS diff --git a/pkg/distsql/bench_test.go b/pkg/distsql/bench_test.go new file mode 100644 index 0000000000000..9c7850d54a917 --- /dev/null +++ b/pkg/distsql/bench_test.go @@ -0,0 +1,74 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package distsql + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/chunk" +) + +func BenchmarkSelectResponseChunk_BigResponse(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + sctx := newMockSessionContext() + sctx.GetSessionVars().InitChunkSize = 32 + sctx.GetSessionVars().MaxChunkSize = 1024 + selectResult, colTypes := createSelectNormalByBenchmarkTest(4000, 20000, sctx) + chk := chunk.NewChunkWithCapacity(colTypes, 1024) + b.StartTimer() + for { + err := selectResult.Next(context.TODO(), chk) + if err != nil { + panic(err) + } + if chk.NumRows() == 0 { + break + } + chk.Reset() + } + } +} + +func BenchmarkSelectResponseChunk_SmallResponse(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + sctx := newMockSessionContext() + sctx.GetSessionVars().InitChunkSize = 32 + sctx.GetSessionVars().MaxChunkSize = 1024 + selectResult, colTypes := createSelectNormalByBenchmarkTest(32, 3200, sctx) + chk := chunk.NewChunkWithCapacity(colTypes, 1024) + b.StartTimer() + for { + err := selectResult.Next(context.TODO(), chk) + if err != nil { + panic(err) + } + if chk.NumRows() == 0 { + break + } + chk.Reset() + } + } +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkSelectResponseChunk_BigResponse, + BenchmarkSelectResponseChunk_SmallResponse, + ) +} diff --git a/pkg/distsql/distsql.go b/pkg/distsql/distsql.go new file mode 100644 index 0000000000000..6f913436ce2f1 --- /dev/null +++ b/pkg/distsql/distsql.go @@ -0,0 +1,261 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package distsql + +import ( + "context" + "strconv" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tidb/pkg/util/trxevents" + "github.com/pingcap/tipb/go-tipb" + "github.com/tikv/client-go/v2/tikvrpc/interceptor" + "go.uber.org/zap" + "google.golang.org/grpc/metadata" +) + +// GenSelectResultFromResponse generates an iterator from response. +func GenSelectResultFromResponse(sctx sessionctx.Context, fieldTypes []*types.FieldType, planIDs []int, rootID int, resp kv.Response) SelectResult { + // TODO: Add metric label and set open tracing. + return &selectResult{ + label: "mpp", + resp: resp, + rowLen: len(fieldTypes), + fieldTypes: fieldTypes, + ctx: sctx, + copPlanIDs: planIDs, + rootPlanID: rootID, + storeType: kv.TiFlash, + } +} + +// Select sends a DAG request, returns SelectResult. +// In kvReq, KeyRanges is required, Concurrency/KeepOrder/Desc/IsolationLevel/Priority are optional. +func Select(ctx context.Context, sctx sessionctx.Context, kvReq *kv.Request, fieldTypes []*types.FieldType) (SelectResult, error) { + r, ctx := tracing.StartRegionEx(ctx, "distsql.Select") + defer r.End() + + // For testing purpose. + if hook := ctx.Value("CheckSelectRequestHook"); hook != nil { + hook.(func(*kv.Request))(kvReq) + } + + enabledRateLimitAction := sctx.GetSessionVars().EnabledRateLimitAction + originalSQL := sctx.GetSessionVars().StmtCtx.OriginalSQL + eventCb := func(event trxevents.TransactionEvent) { + // Note: Do not assume this callback will be invoked within the same goroutine. + if copMeetLock := event.GetCopMeetLock(); copMeetLock != nil { + logutil.Logger(ctx).Debug("coprocessor encounters lock", + zap.Uint64("startTS", kvReq.StartTs), + zap.Stringer("lock", copMeetLock.LockInfo), + zap.String("stmt", originalSQL)) + } + } + + ctx = WithSQLKvExecCounterInterceptor(ctx, sctx.GetSessionVars().StmtCtx) + option := &kv.ClientSendOption{ + SessionMemTracker: sctx.GetSessionVars().MemTracker, + EnabledRateLimitAction: enabledRateLimitAction, + EventCb: eventCb, + EnableCollectExecutionInfo: config.GetGlobalConfig().Instance.EnableCollectExecutionInfo.Load(), + } + + if kvReq.StoreType == kv.TiFlash { + ctx = SetTiFlashConfVarsInContext(ctx, sctx) + option.TiFlashReplicaRead = sctx.GetSessionVars().TiFlashReplicaRead + option.AppendWarning = sctx.GetSessionVars().StmtCtx.AppendWarning + } + + resp := sctx.GetClient().Send(ctx, kvReq, sctx.GetSessionVars().KVVars, option) + if resp == nil { + return nil, errors.New("client returns nil response") + } + + label := metrics.LblGeneral + if sctx.GetSessionVars().InRestrictedSQL { + label = metrics.LblInternal + } + + // kvReq.MemTracker is used to trace and control memory usage in DistSQL layer; + // for selectResult, we just use the kvReq.MemTracker prepared for co-processor + // instead of creating a new one for simplification. + return &selectResult{ + label: "dag", + resp: resp, + rowLen: len(fieldTypes), + fieldTypes: fieldTypes, + ctx: sctx, + sqlType: label, + memTracker: kvReq.MemTracker, + storeType: kvReq.StoreType, + paging: kvReq.Paging.Enable, + distSQLConcurrency: kvReq.Concurrency, + }, nil +} + +// SetTiFlashConfVarsInContext set some TiFlash config variables in context. +func SetTiFlashConfVarsInContext(ctx context.Context, sctx sessionctx.Context) context.Context { + if sctx.GetSessionVars().TiFlashMaxThreads != -1 { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxTiFlashThreads, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxThreads, 10)) + } + if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalJoin != -1 { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalJoin, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalJoin, 10)) + } + if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalGroupBy != -1 { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalGroupBy, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalGroupBy, 10)) + } + if sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalSort != -1 { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiDBMaxBytesBeforeTiFlashExternalSort, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxBytesBeforeExternalSort, 10)) + } + if sctx.GetSessionVars().TiFlashMaxQueryMemoryPerNode <= 0 { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashMemQuotaQueryPerNode, "0") + } else { + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashMemQuotaQueryPerNode, strconv.FormatInt(sctx.GetSessionVars().TiFlashMaxQueryMemoryPerNode, 10)) + } + ctx = metadata.AppendToOutgoingContext(ctx, variable.TiFlashQuerySpillRatio, strconv.FormatFloat(sctx.GetSessionVars().TiFlashQuerySpillRatio, 'f', -1, 64)) + return ctx +} + +// SelectWithRuntimeStats sends a DAG request, returns SelectResult. +// The difference from Select is that SelectWithRuntimeStats will set copPlanIDs into selectResult, +// which can help selectResult to collect runtime stats. +func SelectWithRuntimeStats(ctx context.Context, sctx sessionctx.Context, kvReq *kv.Request, + fieldTypes []*types.FieldType, copPlanIDs []int, rootPlanID int) (SelectResult, error) { + sr, err := Select(ctx, sctx, kvReq, fieldTypes) + if err != nil { + return nil, err + } + if selectResult, ok := sr.(*selectResult); ok { + selectResult.copPlanIDs = copPlanIDs + selectResult.rootPlanID = rootPlanID + } + return sr, nil +} + +// Analyze do a analyze request. +func Analyze(ctx context.Context, client kv.Client, kvReq *kv.Request, vars interface{}, + isRestrict bool, stmtCtx *stmtctx.StatementContext) (SelectResult, error) { + ctx = WithSQLKvExecCounterInterceptor(ctx, stmtCtx) + kvReq.RequestSource.RequestSourceInternal = true + kvReq.RequestSource.RequestSourceType = kv.InternalTxnStats + resp := client.Send(ctx, kvReq, vars, &kv.ClientSendOption{}) + if resp == nil { + return nil, errors.New("client returns nil response") + } + label := metrics.LblGeneral + if isRestrict { + label = metrics.LblInternal + } + result := &selectResult{ + label: "analyze", + resp: resp, + sqlType: label, + storeType: kvReq.StoreType, + } + return result, nil +} + +// Checksum sends a checksum request. +func Checksum(ctx context.Context, client kv.Client, kvReq *kv.Request, vars interface{}) (SelectResult, error) { + // FIXME: As BR have dependency of `Checksum` and TiDB also introduced BR as dependency, Currently we can't edit + // Checksum function signature. The two-way dependence should be removed in the future. + resp := client.Send(ctx, kvReq, vars, &kv.ClientSendOption{}) + if resp == nil { + return nil, errors.New("client returns nil response") + } + result := &selectResult{ + label: "checksum", + resp: resp, + sqlType: metrics.LblGeneral, + storeType: kvReq.StoreType, + } + return result, nil +} + +// SetEncodeType sets the encoding method for the DAGRequest. The supported encoding +// methods are: +// 1. TypeChunk: the result is encoded using the Chunk format, refer util/chunk/chunk.go +// 2. TypeDefault: the result is encoded row by row +func SetEncodeType(ctx sessionctx.Context, dagReq *tipb.DAGRequest) { + if canUseChunkRPC(ctx) { + dagReq.EncodeType = tipb.EncodeType_TypeChunk + setChunkMemoryLayout(dagReq) + } else { + dagReq.EncodeType = tipb.EncodeType_TypeDefault + } +} + +func canUseChunkRPC(ctx sessionctx.Context) bool { + if !ctx.GetSessionVars().EnableChunkRPC { + return false + } + if !checkAlignment() { + return false + } + return true +} + +var supportedAlignment = unsafe.Sizeof(types.MyDecimal{}) == 40 + +// checkAlignment checks the alignment in current system environment. +// The alignment is influenced by system, machine and Golang version. +// Using this function can guarantee the alignment is we want. +func checkAlignment() bool { + return supportedAlignment +} + +var systemEndian tipb.Endian + +// setChunkMemoryLayout sets the chunk memory layout for the DAGRequest. +func setChunkMemoryLayout(dagReq *tipb.DAGRequest) { + dagReq.ChunkMemoryLayout = &tipb.ChunkMemoryLayout{Endian: GetSystemEndian()} +} + +// GetSystemEndian gets the system endian. +func GetSystemEndian() tipb.Endian { + return systemEndian +} + +func init() { + i := 0x0100 + ptr := unsafe.Pointer(&i) + if 0x01 == *(*byte)(ptr) { + systemEndian = tipb.Endian_BigEndian + } else { + systemEndian = tipb.Endian_LittleEndian + } +} + +// WithSQLKvExecCounterInterceptor binds an interceptor for client-go to count the +// number of SQL executions of each TiKV (if any). +func WithSQLKvExecCounterInterceptor(ctx context.Context, stmtCtx *stmtctx.StatementContext) context.Context { + if stmtCtx.KvExecCounter != nil { + // Unlike calling Transaction or Snapshot interface, in distsql package we directly + // face tikv Request. So we need to manually bind RPCInterceptor to ctx. Instead of + // calling SetRPCInterceptor on Transaction or Snapshot. + return interceptor.WithRPCInterceptor(ctx, stmtCtx.KvExecCounter.RPCInterceptor()) + } + return ctx +} diff --git a/pkg/distsql/distsql_test.go b/pkg/distsql/distsql_test.go new file mode 100644 index 0000000000000..b49a99accd2f6 --- /dev/null +++ b/pkg/distsql/distsql_test.go @@ -0,0 +1,435 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package distsql + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tipb/go-tipb" + "github.com/stretchr/testify/require" + tikvstore "github.com/tikv/client-go/v2/kv" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" +) + +func TestSelectNormal(t *testing.T) { + response, colTypes := createSelectNormal(t, 1, 2, nil, nil) + + // Test Next. + chk := chunk.New(colTypes, 32, 32) + numAllRows := 0 + for { + err := response.Next(context.TODO(), chk) + require.NoError(t, err) + numAllRows += chk.NumRows() + if chk.NumRows() == 0 { + break + } + } + require.Equal(t, 2, numAllRows) + require.NoError(t, response.Close()) + require.Equal(t, int64(0), response.memTracker.BytesConsumed()) +} + +func TestSelectMemTracker(t *testing.T) { + response, colTypes := createSelectNormal(t, 2, 6, nil, nil) + + // Test Next. + chk := chunk.New(colTypes, 3, 3) + err := response.Next(context.TODO(), chk) + require.NoError(t, err) + require.True(t, chk.IsFull()) + require.NoError(t, response.Close()) + require.Equal(t, int64(0), response.memTracker.BytesConsumed()) +} + +func TestSelectNormalChunkSize(t *testing.T) { + sctx := newMockSessionContext() + sctx.GetSessionVars().EnableChunkRPC = false + response, colTypes := createSelectNormal(t, 100, 1000000, nil, sctx) + testChunkSize(t, response, colTypes) + require.NoError(t, response.Close()) + require.Equal(t, int64(0), response.memTracker.BytesConsumed()) +} + +func TestSelectWithRuntimeStats(t *testing.T) { + planIDs := []int{1, 2, 3} + response, colTypes := createSelectNormal(t, 1, 2, planIDs, nil) + + require.Equal(t, len(planIDs), len(response.copPlanIDs), "invalid copPlanIDs") + for i := range planIDs { + require.Equal(t, planIDs[i], response.copPlanIDs[i], "invalid copPlanIDs") + } + + // Test Next. + chk := chunk.New(colTypes, 32, 32) + numAllRows := 0 + for { + err := response.Next(context.TODO(), chk) + require.NoError(t, err) + numAllRows += chk.NumRows() + if chk.NumRows() == 0 { + break + } + } + require.Equal(t, 2, numAllRows) + require.NoError(t, response.Close()) +} + +func TestSelectResultRuntimeStats(t *testing.T) { + stmtStats := execdetails.NewRuntimeStatsColl(nil) + basic := stmtStats.GetBasicRuntimeStats(1) + basic.Record(time.Second, 20) + s1 := &selectResultRuntimeStats{ + backoffSleep: map[string]time.Duration{"RegionMiss": time.Millisecond}, + totalProcessTime: time.Second, + totalWaitTime: time.Second, + rpcStat: tikv.NewRegionRequestRuntimeStats(), + distSQLConcurrency: 15, + } + s1.copRespTime.Add(execdetails.Duration(time.Second)) + s1.copRespTime.Add(execdetails.Duration(time.Millisecond)) + s1.procKeys.Add(100) + s1.procKeys.Add(200) + + s2 := *s1 + stmtStats.RegisterStats(1, s1) + stmtStats.RegisterStats(1, &s2) + stats := stmtStats.GetRootStats(1) + expect := "time:1s, loops:1, cop_task: {num: 4, max: 1s, min: 1ms, avg: 500.5ms, p95: 1s, max_proc_keys: 200, p95_proc_keys: 200, tot_proc: 2s, tot_wait: 2s, copr_cache_hit_ratio: 0.00, max_distsql_concurrency: 15}, backoff{RegionMiss: 2ms}" + require.Equal(t, expect, stats.String()) + // Test for idempotence. + require.Equal(t, expect, stats.String()) + + s1.rpcStat.Stats[tikvrpc.CmdCop] = &tikv.RPCRuntimeStats{ + Count: 1, + Consume: int64(time.Second), + } + stmtStats.RegisterStats(2, s1) + stats = stmtStats.GetRootStats(2) + expect = "cop_task: {num: 2, max: 1s, min: 1ms, avg: 500.5ms, p95: 1s, max_proc_keys: 200, p95_proc_keys: 200, tot_proc: 1s, tot_wait: 1s, rpc_num: 1, rpc_time: 1s, copr_cache_hit_ratio: 0.00, max_distsql_concurrency: 15}, backoff{RegionMiss: 1ms}" + require.Equal(t, expect, stats.String()) + // Test for idempotence. + require.Equal(t, expect, stats.String()) + + s1 = &selectResultRuntimeStats{ + backoffSleep: map[string]time.Duration{"RegionMiss": time.Millisecond}, + totalProcessTime: time.Second, + totalWaitTime: time.Second, + rpcStat: tikv.NewRegionRequestRuntimeStats(), + } + s1.copRespTime.Add(execdetails.Duration(time.Second)) + s1.procKeys.Add(100) + expect = "cop_task: {num: 1, max: 1s, proc_keys: 100, tot_proc: 1s, tot_wait: 1s, copr_cache_hit_ratio: 0.00}, backoff{RegionMiss: 1ms}" + require.Equal(t, expect, s1.String()) +} + +func TestAnalyze(t *testing.T) { + sctx := newMockSessionContext() + sctx.GetSessionVars().EnableChunkRPC = false + request, err := (&RequestBuilder{}).SetKeyRanges(nil). + SetAnalyzeRequest(&tipb.AnalyzeReq{}, kv.RC). + SetKeepOrder(true). + Build() + require.NoError(t, err) + + response, err := Analyze(context.TODO(), sctx.GetClient(), request, tikvstore.DefaultVars, true, sctx.GetSessionVars().StmtCtx) + require.NoError(t, err) + + result, ok := response.(*selectResult) + require.True(t, ok) + + require.Equal(t, "analyze", result.label) + require.Equal(t, "internal", result.sqlType) + + bytes, err := response.NextRaw(context.TODO()) + require.NoError(t, err) + require.Len(t, bytes, 16) + + require.NoError(t, response.Close()) +} + +func TestChecksum(t *testing.T) { + sctx := newMockSessionContext() + sctx.GetSessionVars().EnableChunkRPC = false + request, err := (&RequestBuilder{}).SetKeyRanges(nil). + SetChecksumRequest(&tipb.ChecksumRequest{}). + Build() + require.NoError(t, err) + + response, err := Checksum(context.TODO(), sctx.GetClient(), request, tikvstore.DefaultVars) + require.NoError(t, err) + + result, ok := response.(*selectResult) + require.True(t, ok) + require.Equal(t, "checksum", result.label) + require.Equal(t, "general", result.sqlType) + + bytes, err := response.NextRaw(context.TODO()) + require.NoError(t, err) + require.Len(t, bytes, 16) + + require.NoError(t, response.Close()) +} + +// mockResponse implements kv.Response interface. +// Used only for test. +type mockResponse struct { + count int + total int + batch int + ctx sessionctx.Context + sync.Mutex +} + +// Close implements kv.Response interface. +func (resp *mockResponse) Close() error { + resp.Lock() + defer resp.Unlock() + + resp.count = 0 + return nil +} + +// Next implements kv.Response interface. +func (resp *mockResponse) Next(context.Context) (kv.ResultSubset, error) { + resp.Lock() + defer resp.Unlock() + + if resp.count >= resp.total { + return nil, nil + } + numRows := mathutil.Min(resp.batch, resp.total-resp.count) + resp.count += numRows + + var chunks []tipb.Chunk + if !canUseChunkRPC(resp.ctx) { + datum := types.NewIntDatum(1) + bytes := make([]byte, 0, 100) + bytes, _ = codec.EncodeValue(nil, bytes, datum, datum, datum, datum) + chunks = make([]tipb.Chunk, numRows) + for i := range chunks { + chkData := make([]byte, len(bytes)) + copy(chkData, bytes) + chunks[i] = tipb.Chunk{RowsData: chkData} + } + } else { + chunks = make([]tipb.Chunk, 0) + for numRows > 0 { + rows := mathutil.Min(numRows, 1024) + numRows -= rows + + colTypes := make([]*types.FieldType, 4) + for i := 0; i < 4; i++ { + colTypes[i] = types.NewFieldTypeBuilder().SetType(mysql.TypeLonglong).BuildP() + } + chk := chunk.New(colTypes, numRows, numRows) + + for rowOrdinal := 0; rowOrdinal < rows; rowOrdinal++ { + for colOrdinal := 0; colOrdinal < 4; colOrdinal++ { + chk.AppendInt64(colOrdinal, 123) + } + } + + codec := chunk.NewCodec(colTypes) + buffer := codec.Encode(chk) + chunks = append(chunks, tipb.Chunk{RowsData: buffer}) + } + } + + respPB := &tipb.SelectResponse{ + Chunks: chunks, + OutputCounts: []int64{1}, + } + if canUseChunkRPC(resp.ctx) { + respPB.EncodeType = tipb.EncodeType_TypeChunk + } else { + respPB.EncodeType = tipb.EncodeType_TypeDefault + } + respBytes, err := respPB.Marshal() + if err != nil { + panic(err) + } + return &mockResultSubset{respBytes}, nil +} + +// mockResultSubset implements kv.ResultSubset interface. +// Used only for test. +type mockResultSubset struct{ data []byte } + +// GetData implements kv.ResultSubset interface. +func (r *mockResultSubset) GetData() []byte { return r.data } + +// GetStartKey implements kv.ResultSubset interface. +func (r *mockResultSubset) GetStartKey() kv.Key { return nil } + +// MemSize implements kv.ResultSubset interface. +func (r *mockResultSubset) MemSize() int64 { return int64(cap(r.data)) } + +// RespTime implements kv.ResultSubset interface. +func (r *mockResultSubset) RespTime() time.Duration { return 0 } + +func newMockSessionContext() sessionctx.Context { + ctx := mock.NewContext() + ctx.GetSessionVars().StmtCtx = stmtctx.NewStmtCtx() + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) + + ctx.Store = &mock.Store{ + Client: &mock.Client{ + MockResponse: &mockResponse{ + ctx: ctx, + batch: 1, + total: 2, + }, + }, + } + return ctx +} + +func createSelectNormalByBenchmarkTest(batch, totalRows int, ctx sessionctx.Context) (*selectResult, []*types.FieldType) { + request, _ := (&RequestBuilder{}).SetKeyRanges(nil). + SetDAGRequest(&tipb.DAGRequest{}). + SetDesc(false). + SetKeepOrder(false). + SetFromSessionVars(variable.NewSessionVars(nil)). + SetMemTracker(memory.NewTracker(-1, -1)). + Build() + + // 4 int64 types. + ftb := types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) + colTypes := []*types.FieldType{ + ftb.BuildP(), + } + colTypes = append(colTypes, colTypes[0]) + colTypes = append(colTypes, colTypes[0]) + colTypes = append(colTypes, colTypes[0]) + + // Test Next. + var response SelectResult + response, _ = Select(context.TODO(), ctx, request, colTypes) + + result, _ := response.(*selectResult) + resp, _ := result.resp.(*mockResponse) + resp.total = totalRows + resp.batch = batch + + return result, colTypes +} + +func testChunkSize(t *testing.T, response SelectResult, colTypes []*types.FieldType) { + chk := chunk.New(colTypes, 32, 32) + + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) + + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) + + chk.SetRequiredRows(1, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 1, chk.NumRows()) + + chk.SetRequiredRows(2, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 2, chk.NumRows()) + + chk.SetRequiredRows(17, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 17, chk.NumRows()) + + chk.SetRequiredRows(170, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) + + chk.SetRequiredRows(32, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) + + chk.SetRequiredRows(0, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) + + chk.SetRequiredRows(-1, 32) + require.NoError(t, response.Next(context.TODO(), chk)) + require.Equal(t, 32, chk.NumRows()) +} + +func createSelectNormal(t *testing.T, batch, totalRows int, planIDs []int, sctx sessionctx.Context) (*selectResult, []*types.FieldType) { + request, err := (&RequestBuilder{}).SetKeyRanges(nil). + SetDAGRequest(&tipb.DAGRequest{}). + SetDesc(false). + SetKeepOrder(false). + SetFromSessionVars(variable.NewSessionVars(nil)). + SetMemTracker(memory.NewTracker(-1, -1)). + Build() + require.NoError(t, err) + + // 4 int64 types. + ftb := types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) + colTypes := []*types.FieldType{ + ftb.BuildP(), + } + colTypes = append(colTypes, colTypes[0]) + colTypes = append(colTypes, colTypes[0]) + colTypes = append(colTypes, colTypes[0]) + + if sctx == nil { + sctx = newMockSessionContext() + } + + // Test Next. + var response SelectResult + if planIDs == nil { + response, err = Select(context.TODO(), sctx, request, colTypes) + } else { + response, err = SelectWithRuntimeStats(context.TODO(), sctx, request, colTypes, planIDs, 1) + } + + require.NoError(t, err) + result, ok := response.(*selectResult) + + require.True(t, ok) + require.Equal(t, "general", result.sqlType) + require.Equal(t, "dag", result.label) + require.Len(t, colTypes, result.rowLen) + + resp, ok := result.resp.(*mockResponse) + require.True(t, ok) + + resp.total = totalRows + resp.batch = batch + + return result, colTypes +} diff --git a/pkg/distsql/main_test.go b/pkg/distsql/main_test.go new file mode 100644 index 0000000000000..42ebd76393c1f --- /dev/null +++ b/pkg/distsql/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package distsql + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/distsql/request_builder.go b/pkg/distsql/request_builder.go similarity index 98% rename from distsql/request_builder.go rename to pkg/distsql/request_builder.go index 981eefe11711a..b4d5b81b880bb 100644 --- a/distsql/request_builder.go +++ b/pkg/distsql/request_builder.go @@ -22,18 +22,18 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/distsql/request_builder_test.go b/pkg/distsql/request_builder_test.go similarity index 98% rename from distsql/request_builder_test.go rename to pkg/distsql/request_builder_test.go index 8db7e8598df11..096bb905f55f1 100644 --- a/distsql/request_builder_test.go +++ b/pkg/distsql/request_builder_test.go @@ -17,17 +17,17 @@ package distsql import ( "testing" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/paging" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/distsql/select_result.go b/pkg/distsql/select_result.go similarity index 97% rename from distsql/select_result.go rename to pkg/distsql/select_result.go index 2287cc439e21c..6890f88a7ac2a 100644 --- a/distsql/select_result.go +++ b/pkg/distsql/select_result.go @@ -25,24 +25,24 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/pingcap/tipb/go-tipb" tikvmetrics "github.com/tikv/client-go/v2/metrics" "github.com/tikv/client-go/v2/tikv" diff --git a/distsql/select_result_test.go b/pkg/distsql/select_result_test.go similarity index 91% rename from distsql/select_result_test.go rename to pkg/distsql/select_result_test.go index 636f0d46467ae..63ae252a836a7 100644 --- a/distsql/select_result_test.go +++ b/pkg/distsql/select_result_test.go @@ -18,11 +18,11 @@ import ( "context" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/disttask/framework/BUILD.bazel b/pkg/disttask/framework/BUILD.bazel new file mode 100644 index 0000000000000..600e605ce4383 --- /dev/null +++ b/pkg/disttask/framework/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "framework_test", + timeout = "short", + srcs = [ + "framework_dynamic_dispatch_test.go", + "framework_err_handling_test.go", + "framework_ha_test.go", + "framework_pause_and_resume_test.go", + "framework_rollback_test.go", + "framework_test.go", + ], + flaky = True, + race = "off", + shard_count = 31, + deps = [ + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/handle", + "//pkg/disttask/framework/mock", + "//pkg/disttask/framework/mock/execute", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler", + "//pkg/disttask/framework/storage", + "//pkg/domain/infosync", + "//pkg/testkit", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_mock//gomock", + ], +) diff --git a/pkg/disttask/framework/dispatcher/BUILD.bazel b/pkg/disttask/framework/dispatcher/BUILD.bazel new file mode 100644 index 0000000000000..9daece14686ae --- /dev/null +++ b/pkg/disttask/framework/dispatcher/BUILD.bazel @@ -0,0 +1,59 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "dispatcher", + srcs = [ + "dispatcher.go", + "dispatcher_manager.go", + "interface.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/domain/infosync", + "//pkg/metrics", + "//pkg/resourcemanager/pool/spool", + "//pkg/resourcemanager/util", + "//pkg/sessionctx", + "//pkg/util", + "//pkg/util/disttask", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/syncutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "dispatcher_test", + timeout = "short", + srcs = [ + "dispatcher_test.go", + "main_test.go", + ], + embed = [":dispatcher"], + flaky = True, + race = "off", + shard_count = 14, + deps = [ + "//pkg/disttask/framework/mock", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/logutil", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_mock//gomock", + ], +) diff --git a/pkg/disttask/framework/dispatcher/dispatcher.go b/pkg/disttask/framework/dispatcher/dispatcher.go new file mode 100644 index 0000000000000..cc526dd3c4b95 --- /dev/null +++ b/pkg/disttask/framework/dispatcher/dispatcher.go @@ -0,0 +1,767 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package dispatcher + +import ( + "context" + "math/rand" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/sessionctx" + disttaskutil "github.com/pingcap/tidb/pkg/util/disttask" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +const ( + // DefaultSubtaskConcurrency is the default concurrency for handling subtask. + DefaultSubtaskConcurrency = 16 + // MaxSubtaskConcurrency is the maximum concurrency for handling subtask. + MaxSubtaskConcurrency = 256 + // DefaultLiveNodesCheckInterval is the tick interval of fetching all server infos from etcd. + DefaultLiveNodesCheckInterval = 2 +) + +var ( + checkTaskFinishedInterval = 500 * time.Millisecond + nonRetrySQLTime = 1 + // RetrySQLTimes is the max retry times when executing SQL. + RetrySQLTimes = 30 + // RetrySQLInterval is the initial interval between two SQL retries. + RetrySQLInterval = 3 * time.Second + // RetrySQLMaxInterval is the max interval between two SQL retries. + RetrySQLMaxInterval = 30 * time.Second +) + +// TaskHandle provides the interface for operations needed by Dispatcher. +// Then we can use dispatcher's function in Dispatcher interface. +type TaskHandle interface { + // GetPreviousSchedulerIDs gets previous scheduler IDs. + GetPreviousSchedulerIDs(_ context.Context, taskID int64, step int64) ([]string, error) + // GetPreviousSubtaskMetas gets previous subtask metas. + GetPreviousSubtaskMetas(taskID int64, step int64) ([][]byte, error) + storage.SessionExecutor +} + +// Dispatcher manages the lifetime of a task +// including submitting subtasks and updating the status of a task. +type Dispatcher interface { + // Init initializes the dispatcher, should be called before ExecuteTask. + // if Init returns error, dispatcher manager will fail the task directly, + // so the returned error should be a fatal error. + Init() error + // ExecuteTask start to schedule a task. + ExecuteTask() + // Close closes the dispatcher, should be called if Init returns nil. + Close() +} + +// BaseDispatcher is the base struct for Dispatcher. +// each task type embed this struct and implement the Extension interface. +type BaseDispatcher struct { + ctx context.Context + taskMgr *storage.TaskManager + Task *proto.Task + logCtx context.Context + // serverID, it's value is ip:port now. + serverID string + // when RegisterDispatcherFactory, the factory MUST initialize this field. + Extension + + // for HA + // liveNodes will fetch and store all live nodes every liveNodeInterval ticks. + liveNodes []*infosync.ServerInfo + liveNodeFetchInterval int + // liveNodeFetchTick is the tick variable. + liveNodeFetchTick int + // taskNodes stores the id of current scheduler nodes. + taskNodes []string + // rand is for generating random selection of nodes. + rand *rand.Rand +} + +// MockOwnerChange mock owner change in tests. +var MockOwnerChange func() + +// NewBaseDispatcher creates a new BaseDispatcher. +func NewBaseDispatcher(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) *BaseDispatcher { + logCtx := logutil.WithFields(context.Background(), zap.Int64("task-id", task.ID), + zap.String("task-type", task.Type)) + return &BaseDispatcher{ + ctx: ctx, + taskMgr: taskMgr, + Task: task, + logCtx: logCtx, + serverID: serverID, + liveNodes: nil, + liveNodeFetchInterval: DefaultLiveNodesCheckInterval, + liveNodeFetchTick: 0, + taskNodes: nil, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +// Init implements the Dispatcher interface. +func (*BaseDispatcher) Init() error { + return nil +} + +// ExecuteTask implements the Dispatcher interface. +func (d *BaseDispatcher) ExecuteTask() { + logutil.Logger(d.logCtx).Info("execute one task", + zap.String("state", d.Task.State), zap.Uint64("concurrency", d.Task.Concurrency)) + d.scheduleTask() +} + +// Close closes the dispatcher. +func (*BaseDispatcher) Close() { +} + +// refreshTask fetch task state from tidb_global_task table. +func (d *BaseDispatcher) refreshTask() error { + newTask, err := d.taskMgr.GetGlobalTaskByID(d.Task.ID) + if err != nil { + logutil.Logger(d.logCtx).Error("refresh task failed", zap.Error(err)) + return err + } + // newTask might be nil when GC routine move the task into history table. + if newTask != nil { + d.Task = newTask + } + return nil +} + +// scheduleTask schedule the task execution step by step. +func (d *BaseDispatcher) scheduleTask() { + ticker := time.NewTicker(checkTaskFinishedInterval) + defer ticker.Stop() + for { + select { + case <-d.ctx.Done(): + logutil.Logger(d.logCtx).Info("schedule task exits", zap.Error(d.ctx.Err())) + return + case <-ticker.C: + err := d.refreshTask() + if err != nil { + continue + } + failpoint.Inject("cancelTaskAfterRefreshTask", func(val failpoint.Value) { + if val.(bool) && d.Task.State == proto.TaskStateRunning { + err := d.taskMgr.CancelGlobalTask(d.Task.ID) + if err != nil { + logutil.Logger(d.logCtx).Error("cancel task failed", zap.Error(err)) + } + } + }) + + failpoint.Inject("pausePendingTask", func(val failpoint.Value) { + if val.(bool) && d.Task.State == proto.TaskStatePending { + _, err := d.taskMgr.PauseTask(d.Task.Key) + if err != nil { + logutil.Logger(d.logCtx).Error("pause task failed", zap.Error(err)) + } + d.Task.State = proto.TaskStatePausing + } + }) + + failpoint.Inject("pauseTaskAfterRefreshTask", func(val failpoint.Value) { + if val.(bool) && d.Task.State == proto.TaskStateRunning { + _, err := d.taskMgr.PauseTask(d.Task.Key) + if err != nil { + logutil.Logger(d.logCtx).Error("pause task failed", zap.Error(err)) + } + d.Task.State = proto.TaskStatePausing + } + }) + + switch d.Task.State { + case proto.TaskStateCancelling: + err = d.onCancelling() + case proto.TaskStatePausing: + err = d.onPausing() + case proto.TaskStatePaused: + err = d.onPaused() + // close the dispatcher. + if err == nil { + return + } + case proto.TaskStateResuming: + err = d.onResuming() + case proto.TaskStateReverting: + err = d.onReverting() + case proto.TaskStatePending: + err = d.onPending() + case proto.TaskStateRunning: + err = d.onRunning() + case proto.TaskStateSucceed, proto.TaskStateReverted, proto.TaskStateFailed: + if err := d.onFinished(); err != nil { + logutil.Logger(d.logCtx).Error("schedule task meet error", zap.String("state", d.Task.State), zap.Error(err)) + } + return + } + if err != nil { + logutil.Logger(d.logCtx).Info("schedule task meet err, reschedule it", zap.Error(err)) + } + + failpoint.Inject("mockOwnerChange", func(val failpoint.Value) { + if val.(bool) { + logutil.Logger(d.logCtx).Info("mockOwnerChange called") + MockOwnerChange() + time.Sleep(time.Second) + } + }) + } + } +} + +// handle task in cancelling state, dispatch revert subtasks. +func (d *BaseDispatcher) onCancelling() error { + logutil.Logger(d.logCtx).Info("on cancelling state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + errs := []error{errors.New("cancel")} + return d.onErrHandlingStage(errs) +} + +// handle task in pausing state, cancel all running subtasks. +func (d *BaseDispatcher) onPausing() error { + logutil.Logger(d.logCtx).Info("on pausing state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStateRunning, proto.TaskStatePending) + if err != nil { + logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) + return err + } + if cnt == 0 { + logutil.Logger(d.logCtx).Info("all running subtasks paused, update the task to paused state") + return d.updateTask(proto.TaskStatePaused, nil, RetrySQLTimes) + } + logutil.Logger(d.logCtx).Debug("on pausing state, this task keeps current state", zap.String("state", d.Task.State)) + return nil +} + +// MockDMLExecutionOnPausedState is used to mock DML execution when tasks paused. +var MockDMLExecutionOnPausedState func(task *proto.Task) + +// handle task in paused state +func (d *BaseDispatcher) onPaused() error { + logutil.Logger(d.logCtx).Info("on paused state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + failpoint.Inject("mockDMLExecutionOnPausedState", func(val failpoint.Value) { + if val.(bool) { + MockDMLExecutionOnPausedState(d.Task) + } + }) + return nil +} + +// TestSyncChan is used to sync the test. +var TestSyncChan = make(chan struct{}) + +// handle task in resuming state +func (d *BaseDispatcher) onResuming() error { + logutil.Logger(d.logCtx).Info("on resuming state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStatePaused) + if err != nil { + logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) + return err + } + if cnt == 0 { + // Finish the resuming process. + logutil.Logger(d.logCtx).Info("all paused tasks converted to pending state, update the task to running state") + err := d.updateTask(proto.TaskStateRunning, nil, RetrySQLTimes) + failpoint.Inject("syncAfterResume", func() { + TestSyncChan <- struct{}{} + }) + return err + } + + return d.taskMgr.ResumeSubtasks(d.Task.ID) +} + +// handle task in reverting state, check all revert subtasks finished. +func (d *BaseDispatcher) onReverting() error { + logutil.Logger(d.logCtx).Debug("on reverting state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStateRevertPending, proto.TaskStateReverting) + if err != nil { + logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) + return err + } + if cnt == 0 { + // Finish the rollback step. + logutil.Logger(d.logCtx).Info("all reverting tasks finished, update the task to reverted state") + return d.updateTask(proto.TaskStateReverted, nil, RetrySQLTimes) + } + // Wait all subtasks in this stage finished. + d.OnTick(d.ctx, d.Task) + logutil.Logger(d.logCtx).Debug("on reverting state, this task keeps current state", zap.String("state", d.Task.State)) + return nil +} + +// handle task in pending state, dispatch subtasks. +func (d *BaseDispatcher) onPending() error { + logutil.Logger(d.logCtx).Debug("on pending state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + return d.onNextStage() +} + +// handle task in running state, check all running subtasks finished. +// If subtasks finished, run into the next stage. +func (d *BaseDispatcher) onRunning() error { + logutil.Logger(d.logCtx).Debug("on running state", zap.String("state", d.Task.State), zap.Int64("stage", d.Task.Step)) + subTaskErrs, err := d.taskMgr.CollectSubTaskError(d.Task.ID) + if err != nil { + logutil.Logger(d.logCtx).Warn("collect subtask error failed", zap.Error(err)) + return err + } + if len(subTaskErrs) > 0 { + logutil.Logger(d.logCtx).Warn("subtasks encounter errors") + return d.onErrHandlingStage(subTaskErrs) + } + // check current stage finished. + cnt, err := d.taskMgr.GetSubtaskInStatesCnt(d.Task.ID, proto.TaskStatePending, proto.TaskStateRunning) + if err != nil { + logutil.Logger(d.logCtx).Warn("check task failed", zap.Error(err)) + return err + } + + if cnt == 0 { + return d.onNextStage() + } + // Check if any node are down. + if err := d.replaceDeadNodesIfAny(); err != nil { + return err + } + // Wait all subtasks in this stage finished. + d.OnTick(d.ctx, d.Task) + logutil.Logger(d.logCtx).Debug("on running state, this task keeps current state", zap.String("state", d.Task.State)) + return nil +} + +func (d *BaseDispatcher) onFinished() error { + metrics.UpdateMetricsForFinishTask(d.Task) + logutil.Logger(d.logCtx).Debug("schedule task, task is finished", zap.String("state", d.Task.State)) + return d.taskMgr.TransferSubTasks2History(d.Task.ID) +} + +func (d *BaseDispatcher) replaceDeadNodesIfAny() error { + if len(d.taskNodes) == 0 { + var err error + d.taskNodes, err = d.taskMgr.GetSchedulerIDsByTaskIDAndStep(d.Task.ID, d.Task.Step) + if err != nil { + return err + } + } + d.liveNodeFetchTick++ + if d.liveNodeFetchTick == d.liveNodeFetchInterval { + d.liveNodeFetchTick = 0 + serverInfos, err := GenerateSchedulerNodes(d.ctx) + if err != nil { + return err + } + eligibleServerInfos, err := d.GetEligibleInstances(d.ctx, d.Task) + if err != nil { + return err + } + newInfos := serverInfos[:0] + for _, m := range serverInfos { + found := false + for _, n := range eligibleServerInfos { + if m.ID == n.ID { + found = true + break + } + } + if found { + newInfos = append(newInfos, m) + } + } + d.liveNodes = newInfos + } + if len(d.liveNodes) > 0 { + replaceNodes := make(map[string]string) + for _, nodeID := range d.taskNodes { + if ok := disttaskutil.MatchServerInfo(d.liveNodes, nodeID); !ok { + n := d.liveNodes[d.rand.Int()%len(d.liveNodes)] //nolint:gosec + replaceNodes[nodeID] = disttaskutil.GenerateExecID(n.IP, n.Port) + } + } + if err := d.taskMgr.UpdateFailedSchedulerIDs(d.Task.ID, replaceNodes); err != nil { + return err + } + // replace local cache. + for k, v := range replaceNodes { + for m, n := range d.taskNodes { + if n == k { + d.taskNodes[m] = v + break + } + } + } + } + return nil +} + +// updateTask update the task in tidb_global_task table. +func (d *BaseDispatcher) updateTask(taskState string, newSubTasks []*proto.Subtask, retryTimes int) (err error) { + prevState := d.Task.State + d.Task.State = taskState + if !VerifyTaskStateTransform(prevState, taskState) { + return errors.Errorf("invalid task state transform, from %s to %s", prevState, taskState) + } + + failpoint.Inject("cancelBeforeUpdate", func() { + err := d.taskMgr.CancelGlobalTask(d.Task.ID) + if err != nil { + logutil.Logger(d.logCtx).Error("cancel task failed", zap.Error(err)) + } + }) + + var retryable bool + for i := 0; i < retryTimes; i++ { + retryable, err = d.taskMgr.UpdateGlobalTaskAndAddSubTasks(d.Task, newSubTasks, prevState) + if err == nil || !retryable { + break + } + if i%10 == 0 { + logutil.Logger(d.logCtx).Warn("updateTask first failed", zap.String("from", prevState), zap.String("to", d.Task.State), + zap.Int("retry times", i), zap.Error(err)) + } + time.Sleep(RetrySQLInterval) + } + if err != nil && retryTimes != nonRetrySQLTime { + logutil.Logger(d.logCtx).Warn("updateTask failed", + zap.String("from", prevState), zap.String("to", d.Task.State), zap.Int("retry times", retryTimes), zap.Error(err)) + } + return err +} + +func (d *BaseDispatcher) onErrHandlingStage(receiveErr []error) error { + // 1. generate the needed task meta and subTask meta (dist-plan). + meta, err := d.OnErrStage(d.ctx, d, d.Task, receiveErr) + if err != nil { + // OnErrStage must be retryable, if not, there will have resource leak for tasks. + logutil.Logger(d.logCtx).Warn("handle error failed", zap.Error(err)) + return err + } + + // 2. dispatch revert dist-plan to EligibleInstances. + return d.dispatchSubTask4Revert(meta) +} + +func (d *BaseDispatcher) dispatchSubTask4Revert(meta []byte) error { + instanceIDs, err := d.GetAllSchedulerIDs(d.ctx, d.Task) + if err != nil { + logutil.Logger(d.logCtx).Warn("get task's all instances failed", zap.Error(err)) + return err + } + + subTasks := make([]*proto.Subtask, 0, len(instanceIDs)) + for _, id := range instanceIDs { + // reverting subtasks belong to the same step as current active step. + subTasks = append(subTasks, proto.NewSubtask(d.Task.Step, d.Task.ID, d.Task.Type, id, meta)) + } + return d.updateTask(proto.TaskStateReverting, subTasks, RetrySQLTimes) +} + +func (*BaseDispatcher) nextStepSubtaskDispatched(*proto.Task) bool { + // TODO: will implement it when we we support dispatch subtask by batch. + // since subtask meta might be too large to save in one transaction. + return true +} + +func (d *BaseDispatcher) onNextStage() (err error) { + /// dynamic dispatch subtasks. + failpoint.Inject("mockDynamicDispatchErr", func() { + failpoint.Return(errors.New("mockDynamicDispatchErr")) + }) + + nextStep := d.GetNextStep(d, d.Task) + logutil.Logger(d.logCtx).Info("onNextStage", + zap.Int64("current-step", d.Task.Step), + zap.Int64("next-step", nextStep)) + + // 1. Adjust the global task's concurrency. + if d.Task.State == proto.TaskStatePending { + if d.Task.Concurrency == 0 { + d.Task.Concurrency = DefaultSubtaskConcurrency + } + if d.Task.Concurrency > MaxSubtaskConcurrency { + d.Task.Concurrency = MaxSubtaskConcurrency + } + } + defer func() { + if err != nil { + return + } + // invariant: task.Step always means the most recent step that all + // corresponding subtasks have been saved to system table. + // + // when all subtasks of task.Step is finished, we call OnNextSubtasksBatch + // to generate subtasks of next step. after all subtasks of next step are + // saved to system table, we will update task.Step to next step, so the + // invariant hold. + // see nextStepSubtaskDispatched for why we don't update task and subtasks + // in a single transaction. + if d.nextStepSubtaskDispatched(d.Task) { + currStep := d.Task.Step + d.Task.Step = nextStep + // When all subtasks dispatched and processed, mark task as succeed. + taskState := proto.TaskStateRunning + if d.Task.Step == proto.StepDone { + taskState = proto.TaskStateSucceed + logutil.Logger(d.logCtx).Info("all subtasks dispatched and processed, finish the task") + } else { + logutil.Logger(d.logCtx).Info("move to next stage", + zap.Int64("from", currStep), zap.Int64("to", d.Task.Step)) + } + d.Task.StateUpdateTime = time.Now().UTC() + err = d.updateTask(taskState, nil, RetrySQLTimes) + } + }() + + for { + // 3. generate a batch of subtasks. + metas, err := d.OnNextSubtasksBatch(d.ctx, d, d.Task, nextStep) + if err != nil { + logutil.Logger(d.logCtx).Warn("generate part of subtasks failed", zap.Error(err)) + return d.handlePlanErr(err) + } + + failpoint.Inject("mockDynamicDispatchErr1", func() { + failpoint.Return(errors.New("mockDynamicDispatchErr1")) + }) + + // 4. dispatch batch of subtasks to EligibleInstances. + err = d.dispatchSubTask(nextStep, metas) + if err != nil { + return err + } + + if d.nextStepSubtaskDispatched(d.Task) { + break + } + + failpoint.Inject("mockDynamicDispatchErr2", func() { + failpoint.Return(errors.New("mockDynamicDispatchErr2")) + }) + } + return nil +} + +func (d *BaseDispatcher) dispatchSubTask(subtaskStep int64, metas [][]byte) error { + logutil.Logger(d.logCtx).Info("dispatch subtasks", zap.String("state", d.Task.State), zap.Int64("step", d.Task.Step), zap.Uint64("concurrency", d.Task.Concurrency), zap.Int("subtasks", len(metas))) + + // select all available TiDB nodes for task. + serverNodes, err := d.GetEligibleInstances(d.ctx, d.Task) + logutil.Logger(d.logCtx).Debug("eligible instances", zap.Int("num", len(serverNodes))) + + if err != nil { + return err + } + // 4. filter by role. + serverNodes, err = d.filterByRole(serverNodes) + if err != nil { + return err + } + + logutil.Logger(d.logCtx).Info("eligible instances", zap.Int("num", len(serverNodes))) + + if len(serverNodes) == 0 { + return errors.New("no available TiDB node to dispatch subtasks") + } + d.taskNodes = make([]string, len(serverNodes)) + for i := range serverNodes { + d.taskNodes[i] = disttaskutil.GenerateExecID(serverNodes[i].IP, serverNodes[i].Port) + } + subTasks := make([]*proto.Subtask, 0, len(metas)) + for i, meta := range metas { + // we assign the subtask to the instance in a round-robin way. + // TODO: assign the subtask to the instance according to the system load of each nodes + pos := i % len(serverNodes) + instanceID := disttaskutil.GenerateExecID(serverNodes[pos].IP, serverNodes[pos].Port) + logutil.Logger(d.logCtx).Debug("create subtasks", zap.String("instanceID", instanceID)) + subTasks = append(subTasks, proto.NewSubtask(subtaskStep, d.Task.ID, d.Task.Type, instanceID, meta)) + } + return d.updateTask(d.Task.State, subTasks, RetrySQLTimes) +} + +func (d *BaseDispatcher) handlePlanErr(err error) error { + logutil.Logger(d.logCtx).Warn("generate plan failed", zap.Error(err), zap.String("state", d.Task.State)) + if d.IsRetryableErr(err) { + return err + } + d.Task.Error = err + // state transform: pending -> failed. + return d.updateTask(proto.TaskStateFailed, nil, RetrySQLTimes) +} + +// GenerateSchedulerNodes generate a eligible TiDB nodes. +func GenerateSchedulerNodes(ctx context.Context) (serverNodes []*infosync.ServerInfo, err error) { + var serverInfos map[string]*infosync.ServerInfo + _, etcd := ctx.Value("etcd").(bool) + if intest.InTest && !etcd { + serverInfos = infosync.MockGlobalServerInfoManagerEntry.GetAllServerInfo() + } else { + serverInfos, err = infosync.GetAllServerInfo(ctx) + } + if err != nil { + return nil, err + } + if len(serverInfos) == 0 { + return nil, errors.New("not found instance") + } + + serverNodes = make([]*infosync.ServerInfo, 0, len(serverInfos)) + for _, serverInfo := range serverInfos { + serverNodes = append(serverNodes, serverInfo) + } + return serverNodes, nil +} + +func (d *BaseDispatcher) filterByRole(infos []*infosync.ServerInfo) ([]*infosync.ServerInfo, error) { + nodes, err := d.taskMgr.GetNodesByRole("background") + if err != nil { + return nil, err + } + + if len(nodes) == 0 { + nodes, err = d.taskMgr.GetNodesByRole("") + } + + if err != nil { + return nil, err + } + + res := make([]*infosync.ServerInfo, 0, len(nodes)) + for _, info := range infos { + _, ok := nodes[disttaskutil.GenerateExecID(info.IP, info.Port)] + if ok { + res = append(res, info) + } + } + return res, nil +} + +// GetAllSchedulerIDs gets all the scheduler IDs. +func (d *BaseDispatcher) GetAllSchedulerIDs(ctx context.Context, task *proto.Task) ([]string, error) { + serverInfos, err := d.GetEligibleInstances(ctx, task) + if err != nil { + return nil, err + } + if len(serverInfos) == 0 { + return nil, nil + } + + schedulerIDs, err := d.taskMgr.GetSchedulerIDsByTaskID(task.ID) + if err != nil { + return nil, err + } + ids := make([]string, 0, len(schedulerIDs)) + for _, id := range schedulerIDs { + if ok := disttaskutil.MatchServerInfo(serverInfos, id); ok { + ids = append(ids, id) + } + } + return ids, nil +} + +// GetPreviousSubtaskMetas get subtask metas from specific step. +func (d *BaseDispatcher) GetPreviousSubtaskMetas(taskID int64, step int64) ([][]byte, error) { + previousSubtasks, err := d.taskMgr.GetSucceedSubtasksByStep(taskID, step) + if err != nil { + logutil.Logger(d.logCtx).Warn("get previous succeed subtask failed", zap.Int64("step", step)) + return nil, err + } + previousSubtaskMetas := make([][]byte, 0, len(previousSubtasks)) + for _, subtask := range previousSubtasks { + previousSubtaskMetas = append(previousSubtaskMetas, subtask.Meta) + } + return previousSubtaskMetas, nil +} + +// GetPreviousSchedulerIDs gets scheduler IDs that run previous step. +func (d *BaseDispatcher) GetPreviousSchedulerIDs(_ context.Context, taskID int64, step int64) ([]string, error) { + return d.taskMgr.GetSchedulerIDsByTaskIDAndStep(taskID, step) +} + +// WithNewSession executes the function with a new session. +func (d *BaseDispatcher) WithNewSession(fn func(se sessionctx.Context) error) error { + return d.taskMgr.WithNewSession(fn) +} + +// WithNewTxn executes the fn in a new transaction. +func (d *BaseDispatcher) WithNewTxn(ctx context.Context, fn func(se sessionctx.Context) error) error { + return d.taskMgr.WithNewTxn(ctx, fn) +} + +// VerifyTaskStateTransform verifies whether the task state transform is valid. +func VerifyTaskStateTransform(from, to string) bool { + rules := map[string][]string{ + proto.TaskStatePending: { + proto.TaskStateRunning, + proto.TaskStateCancelling, + proto.TaskStatePausing, + proto.TaskStateSucceed, + proto.TaskStateFailed, + }, + proto.TaskStateRunning: { + proto.TaskStateSucceed, + proto.TaskStateReverting, + proto.TaskStateFailed, + proto.TaskStateCancelling, + proto.TaskStatePausing, + }, + proto.TaskStateSucceed: {}, + proto.TaskStateReverting: { + proto.TaskStateReverted, + // no revert_failed now + // proto.TaskStateRevertFailed, + }, + proto.TaskStateFailed: {}, + proto.TaskStateRevertFailed: {}, + proto.TaskStateCancelling: { + proto.TaskStateReverting, + // no canceled now + // proto.TaskStateCanceled, + }, + proto.TaskStateCanceled: {}, + proto.TaskStatePausing: { + proto.TaskStatePaused, + }, + proto.TaskStatePaused: { + proto.TaskStateResuming, + }, + proto.TaskStateResuming: { + proto.TaskStateRunning, + }, + proto.TaskStateRevertPending: {}, + proto.TaskStateReverted: {}, + } + logutil.BgLogger().Info("task state transform", zap.String("from", from), zap.String("to", to)) + + if from == to { + return true + } + + for _, state := range rules[from] { + if state == to { + return true + } + } + return false +} diff --git a/disttask/framework/dispatcher/dispatcher_manager.go b/pkg/disttask/framework/dispatcher/dispatcher_manager.go similarity index 96% rename from disttask/framework/dispatcher/dispatcher_manager.go rename to pkg/disttask/framework/dispatcher/dispatcher_manager.go index 82ebeb2ef0be3..737dfecdf3597 100644 --- a/disttask/framework/dispatcher/dispatcher_manager.go +++ b/pkg/disttask/framework/dispatcher/dispatcher_manager.go @@ -20,14 +20,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/resourcemanager/pool/spool" - "github.com/pingcap/tidb/resourcemanager/util" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/spool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/syncutil" "go.uber.org/zap" ) diff --git a/pkg/disttask/framework/dispatcher/dispatcher_test.go b/pkg/disttask/framework/dispatcher/dispatcher_test.go new file mode 100644 index 0000000000000..7786777003a96 --- /dev/null +++ b/pkg/disttask/framework/dispatcher/dispatcher_test.go @@ -0,0 +1,560 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package dispatcher_test + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/util" + "go.uber.org/mock/gomock" +) + +var ( + _ dispatcher.Extension = (*testDispatcherExt)(nil) + _ dispatcher.Extension = (*numberExampleDispatcherExt)(nil) +) + +const ( + subtaskCnt = 3 +) + +type testDispatcherExt struct{} + +func (*testDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { +} + +func (*testDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ int64) (metas [][]byte, err error) { + return nil, nil +} + +func (*testDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { + return nil, nil +} + +var mockedAllServerInfos = []*infosync.ServerInfo{} + +func (*testDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { + return mockedAllServerInfos, nil +} + +func (*testDispatcherExt) IsRetryableErr(error) bool { + return true +} + +func (*testDispatcherExt) GetNextStep(dispatcher.TaskHandle, *proto.Task) int64 { + return proto.StepDone +} + +type numberExampleDispatcherExt struct{} + +func (*numberExampleDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { +} + +func (n *numberExampleDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, task *proto.Task, _ int64) (metas [][]byte, err error) { + switch task.Step { + case proto.StepInit: + for i := 0; i < subtaskCnt; i++ { + metas = append(metas, []byte{'1'}) + } + logutil.BgLogger().Info("progress step init") + case proto.StepOne: + logutil.BgLogger().Info("progress step one") + return nil, nil + default: + return nil, errors.New("unknown step") + } + return metas, nil +} + +func (n *numberExampleDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { + // Don't handle not. + return nil, nil +} + +func (*numberExampleDispatcherExt) GetEligibleInstances(ctx context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { + return dispatcher.GenerateSchedulerNodes(ctx) +} + +func (*numberExampleDispatcherExt) IsRetryableErr(error) bool { + return true +} + +func (*numberExampleDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { + switch task.Step { + case proto.StepInit: + return proto.StepOne + default: + return proto.StepDone + } +} + +func MockDispatcherManager(t *testing.T, pool *pools.ResourcePool) (*dispatcher.Manager, *storage.TaskManager) { + ctx := context.WithValue(context.Background(), "etcd", true) + mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) + storage.SetTaskManager(mgr) + dsp, err := dispatcher.NewManager(util.WithInternalSourceType(ctx, "dispatcher"), mgr, "host:port") + require.NoError(t, err) + dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, + func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { + mockDispatcher := dsp.MockDispatcher(task) + mockDispatcher.Extension = &testDispatcherExt{} + return mockDispatcher + }) + return dsp, mgr +} + +func deleteTasks(t *testing.T, store kv.Storage, taskID int64) { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf("delete from mysql.tidb_global_task where id = %d", taskID)) +} + +func TestGetInstance(t *testing.T) { + ctx := context.Background() + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + + dspManager, mgr := MockDispatcherManager(t, pool) + // test no server + task := &proto.Task{ID: 1, Type: proto.TaskTypeExample} + dsp := dspManager.MockDispatcher(task) + dsp.Extension = &testDispatcherExt{} + instanceIDs, err := dsp.GetAllSchedulerIDs(ctx, task) + require.Lenf(t, instanceIDs, 0, "GetAllSchedulerIDs when there's no subtask") + require.NoError(t, err) + + // test 2 servers + // server ids: uuid0, uuid1 + // subtask instance ids: nil + uuids := []string{"ddl_id_1", "ddl_id_2"} + serverIDs := []string{"10.123.124.10:32457", "[ABCD:EF01:2345:6789:ABCD:EF01:2345:6789]:65535"} + + mockedAllServerInfos = []*infosync.ServerInfo{ + { + ID: uuids[0], + IP: "10.123.124.10", + Port: 32457, + }, + { + ID: uuids[1], + IP: "ABCD:EF01:2345:6789:ABCD:EF01:2345:6789", + Port: 65535, + }, + } + instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) + require.Lenf(t, instanceIDs, 0, "GetAllSchedulerIDs") + require.NoError(t, err) + + // server ids: uuid0, uuid1 + // subtask instance ids: uuid1 + subtask := &proto.Subtask{ + Type: proto.TaskTypeExample, + TaskID: task.ID, + SchedulerID: serverIDs[1], + } + err = mgr.AddNewSubTask(task.ID, proto.StepInit, subtask.SchedulerID, nil, subtask.Type, true) + require.NoError(t, err) + instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) + require.NoError(t, err) + require.Equal(t, []string{serverIDs[1]}, instanceIDs) + // server ids: uuid0, uuid1 + // subtask instance ids: uuid0, uuid1 + subtask = &proto.Subtask{ + Type: proto.TaskTypeExample, + TaskID: task.ID, + SchedulerID: serverIDs[0], + } + err = mgr.AddNewSubTask(task.ID, proto.StepInit, subtask.SchedulerID, nil, subtask.Type, true) + require.NoError(t, err) + instanceIDs, err = dsp.GetAllSchedulerIDs(ctx, task) + require.NoError(t, err) + require.Len(t, instanceIDs, len(serverIDs)) + require.ElementsMatch(t, instanceIDs, serverIDs) +} + +func TestTaskFailInManager(t *testing.T) { + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockDispatcher := mock.NewMockDispatcher(ctrl) + mockDispatcher.EXPECT().Init().Return(errors.New("mock dispatcher init error")) + + dspManager, mgr := MockDispatcherManager(t, pool) + dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, + func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { + return mockDispatcher + }) + dspManager.Start() + defer dspManager.Stop() + + // unknown task type + taskID, err := mgr.AddNewGlobalTask("test", "test-type", 1, nil) + require.NoError(t, err) + require.Eventually(t, func() bool { + task, err := mgr.GetGlobalTaskByID(taskID) + require.NoError(t, err) + return task.State == proto.TaskStateFailed && + strings.Contains(task.Error.Error(), "unknown task type") + }, time.Second*10, time.Millisecond*300) + + // dispatcher init error + taskID, err = mgr.AddNewGlobalTask("test2", proto.TaskTypeExample, 1, nil) + require.NoError(t, err) + require.Eventually(t, func() bool { + task, err := mgr.GetGlobalTaskByID(taskID) + require.NoError(t, err) + return task.State == proto.TaskStateFailed && + strings.Contains(task.Error.Error(), "mock dispatcher init error") + }, time.Second*10, time.Millisecond*300) +} + +func checkDispatch(t *testing.T, taskCnt int, isSucc, isCancel, isSubtaskCancel, isPauseAndResume bool) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/MockDisableDistTask", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/MockDisableDistTask")) + }() + // test DispatchTaskLoop + // test parallelism control + var originalConcurrency int + if taskCnt == 1 { + originalConcurrency = dispatcher.DefaultDispatchConcurrency + dispatcher.DefaultDispatchConcurrency = 1 + } + + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + + dsp, mgr := MockDispatcherManager(t, pool) + dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, + func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { + mockDispatcher := dsp.MockDispatcher(task) + mockDispatcher.Extension = &numberExampleDispatcherExt{} + return mockDispatcher + }) + dsp.Start() + defer func() { + dsp.Stop() + // make data race happy + if taskCnt == 1 { + dispatcher.DefaultDispatchConcurrency = originalConcurrency + } + }() + + require.NoError(t, mgr.StartManager(":4000", "background")) + + // 3s + cnt := 60 + checkGetRunningTaskCnt := func(expected int) { + require.Eventually(t, func() bool { + return dsp.GetRunningTaskCnt() == expected + }, time.Second, 50*time.Millisecond) + } + + checkTaskRunningCnt := func() []*proto.Task { + var tasks []*proto.Task + require.Eventually(t, func() bool { + var err error + tasks, err = mgr.GetGlobalTasksInStates(proto.TaskStateRunning) + require.NoError(t, err) + return len(tasks) == taskCnt + }, time.Second, 50*time.Millisecond) + return tasks + } + + checkSubtaskCnt := func(tasks []*proto.Task, taskIDs []int64) { + for i, taskID := range taskIDs { + require.Equal(t, int64(i+1), tasks[i].ID) + require.Eventually(t, func() bool { + cnt, err := mgr.GetSubtaskInStatesCnt(taskID, proto.TaskStatePending) + require.NoError(t, err) + return int64(subtaskCnt) == cnt + }, time.Second, 50*time.Millisecond) + } + } + + // Mock add tasks. + taskIDs := make([]int64, 0, taskCnt) + for i := 0; i < taskCnt; i++ { + taskID, err := mgr.AddNewGlobalTask(fmt.Sprintf("%d", i), proto.TaskTypeExample, 0, nil) + require.NoError(t, err) + taskIDs = append(taskIDs, taskID) + } + // test OnNextSubtasksBatch. + checkGetRunningTaskCnt(taskCnt) + tasks := checkTaskRunningCnt() + checkSubtaskCnt(tasks, taskIDs) + // test parallelism control + if taskCnt == 1 { + taskID, err := mgr.AddNewGlobalTask(fmt.Sprintf("%d", taskCnt), proto.TaskTypeExample, 0, nil) + require.NoError(t, err) + checkGetRunningTaskCnt(taskCnt) + // Clean the task. + deleteTasks(t, store, taskID) + dsp.DelRunningTask(taskID) + } + + // test DetectTaskLoop + checkGetTaskState := func(expectedState string) { + i := 0 + for ; i < cnt; i++ { + tasks, err := mgr.GetGlobalTasksInStates(expectedState) + require.NoError(t, err) + if len(tasks) == taskCnt { + break + } + historyTasks, err := mgr.GetGlobalTasksFromHistoryInStates(expectedState) + require.NoError(t, err) + if len(tasks)+len(historyTasks) == taskCnt { + break + } + time.Sleep(time.Millisecond * 50) + } + require.Less(t, i, cnt) + } + // Test all subtasks are successful. + var err error + if isSucc { + // Mock subtasks succeed. + for i := 1; i <= subtaskCnt*taskCnt; i++ { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) + require.NoError(t, err) + } + checkGetTaskState(proto.TaskStateSucceed) + require.Len(t, tasks, taskCnt) + + checkGetRunningTaskCnt(0) + return + } + + if isCancel { + for i := 1; i <= taskCnt; i++ { + err = mgr.CancelGlobalTask(int64(i)) + require.NoError(t, err) + } + } else if isPauseAndResume { + for i := 0; i < taskCnt; i++ { + found, err := mgr.PauseTask(fmt.Sprintf("%d", i)) + require.Equal(t, true, found) + require.NoError(t, err) + } + for i := 1; i <= subtaskCnt*taskCnt; i++ { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStatePaused, nil) + require.NoError(t, err) + } + checkGetTaskState(proto.TaskStatePaused) + for i := 0; i < taskCnt; i++ { + found, err := mgr.ResumeTask(fmt.Sprintf("%d", i)) + require.Equal(t, true, found) + require.NoError(t, err) + } + + // Mock subtasks succeed. + for i := 1; i <= subtaskCnt*taskCnt; i++ { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) + require.NoError(t, err) + } + checkGetTaskState(proto.TaskStateSucceed) + return + } else { + // Test each task has a subtask failed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/storage/MockUpdateTaskErr", "1*return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/storage/MockUpdateTaskErr")) + }() + + if isSubtaskCancel { + // Mock a subtask canceled + for i := 1; i <= subtaskCnt*taskCnt; i += subtaskCnt { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateCanceled, nil) + require.NoError(t, err) + } + } else { + // Mock a subtask fails. + for i := 1; i <= subtaskCnt*taskCnt; i += subtaskCnt { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateFailed, nil) + require.NoError(t, err) + } + } + } + + checkGetTaskState(proto.TaskStateReverting) + require.Len(t, tasks, taskCnt) + // Mock all subtask reverted. + start := subtaskCnt * taskCnt + for i := start; i <= start+subtaskCnt*taskCnt; i++ { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateReverted, nil) + require.NoError(t, err) + } + checkGetTaskState(proto.TaskStateReverted) + require.Len(t, tasks, taskCnt) +} + +func TestSimple(t *testing.T) { + checkDispatch(t, 1, true, false, false, false) +} + +func TestSimpleErrStage(t *testing.T) { + checkDispatch(t, 1, false, false, false, false) +} + +func TestSimpleCancel(t *testing.T) { + checkDispatch(t, 1, false, true, false, false) +} + +func TestSimpleSubtaskCancel(t *testing.T) { + checkDispatch(t, 1, false, false, true, false) +} + +func TestParallel(t *testing.T) { + checkDispatch(t, 3, true, false, false, false) +} + +func TestParallelErrStage(t *testing.T) { + checkDispatch(t, 3, false, false, false, false) +} + +func TestParallelCancel(t *testing.T) { + checkDispatch(t, 3, false, true, false, false) +} + +func TestParallelSubtaskCancel(t *testing.T) { + checkDispatch(t, 3, false, false, true, false) +} + +func TestPause(t *testing.T) { + checkDispatch(t, 1, false, false, false, true) +} + +func TestParallelPause(t *testing.T) { + checkDispatch(t, 3, false, false, false, true) +} + +func TestVerifyTaskStateTransform(t *testing.T) { + testCases := []struct { + oldState string + newState string + expect bool + }{ + {proto.TaskStateRunning, proto.TaskStateRunning, true}, + {proto.TaskStatePending, proto.TaskStateRunning, true}, + {proto.TaskStatePending, proto.TaskStateReverting, false}, + {proto.TaskStateRunning, proto.TaskStateReverting, true}, + {proto.TaskStateReverting, proto.TaskStateReverted, true}, + {proto.TaskStateReverting, proto.TaskStateSucceed, false}, + {proto.TaskStateRunning, proto.TaskStatePausing, true}, + {proto.TaskStateRunning, proto.TaskStateResuming, false}, + {proto.TaskStateCancelling, proto.TaskStateRunning, false}, + {proto.TaskStateCanceled, proto.TaskStateRunning, false}, + } + for _, tc := range testCases { + require.Equal(t, tc.expect, dispatcher.VerifyTaskStateTransform(tc.oldState, tc.newState)) + } +} + +func TestCleanUpRoutine(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/MockDisableDistTask", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/MockDisableDistTask")) + }() + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + dsp, mgr := MockDispatcherManager(t, pool) + mockCleanupRountine := mock.NewMockCleanUpRoutine(ctrl) + mockCleanupRountine.EXPECT().CleanUp(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + dispatcher.RegisterDispatcherFactory(proto.TaskTypeExample, + func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) dispatcher.Dispatcher { + mockDispatcher := dsp.MockDispatcher(task) + mockDispatcher.Extension = &numberExampleDispatcherExt{} + return mockDispatcher + }) + dispatcher.RegisterDispatcherCleanUpFactory(proto.TaskTypeExample, + func() dispatcher.CleanUpRoutine { + return mockCleanupRountine + }) + dsp.Start() + defer dsp.Stop() + require.NoError(t, mgr.StartManager(":4000", "background")) + + taskID, err := mgr.AddNewGlobalTask("test", proto.TaskTypeExample, 1, nil) + require.NoError(t, err) + + checkTaskRunningCnt := func() []*proto.Task { + var tasks []*proto.Task + require.Eventually(t, func() bool { + var err error + tasks, err = mgr.GetGlobalTasksInStates(proto.TaskStateRunning) + require.NoError(t, err) + return len(tasks) == 1 + }, time.Second, 50*time.Millisecond) + return tasks + } + + checkSubtaskCnt := func(tasks []*proto.Task, taskID int64) { + require.Eventually(t, func() bool { + cnt, err := mgr.GetSubtaskInStatesCnt(taskID, proto.TaskStatePending) + require.NoError(t, err) + return int64(subtaskCnt) == cnt + }, time.Second, 50*time.Millisecond) + } + + tasks := checkTaskRunningCnt() + checkSubtaskCnt(tasks, taskID) + for i := 1; i <= subtaskCnt; i++ { + err = mgr.UpdateSubtaskStateAndError(int64(i), proto.TaskStateSucceed, nil) + require.NoError(t, err) + } + dsp.DoCleanUpRoutine() + require.Eventually(t, func() bool { + tasks, err := mgr.GetGlobalTasksFromHistoryInStates(proto.TaskStateSucceed) + require.NoError(t, err) + return len(tasks) != 0 + }, time.Second*10, time.Millisecond*300) +} diff --git a/pkg/disttask/framework/dispatcher/interface.go b/pkg/disttask/framework/dispatcher/interface.go new file mode 100644 index 0000000000000..cafa1304c63e2 --- /dev/null +++ b/pkg/disttask/framework/dispatcher/interface.go @@ -0,0 +1,133 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package dispatcher + +import ( + "context" + + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/util/syncutil" +) + +// Extension is used to control the process operations for each task. +// it's used to extend functions of BaseDispatcher. +// as golang doesn't support inheritance, we embed this interface in Dispatcher +// to simulate abstract method as in other OO languages. +type Extension interface { + // OnTick is used to handle the ticker event, if business impl need to do some periodical work, you can + // do it here, but don't do too much work here, because the ticker interval is small, and it will block + // the event is generated every checkTaskRunningInterval, and only when the task NOT FINISHED and NO ERROR. + OnTick(ctx context.Context, task *proto.Task) + + // OnNextSubtasksBatch is used to generate batch of subtasks for next stage + // NOTE: don't change gTask.State inside, framework will manage it. + // it's called when: + // 1. task is pending and entering it's first step. + // 2. subtasks dispatched has all finished with no error. + // when next step is StepDone, it should return nil, nil. + OnNextSubtasksBatch(ctx context.Context, h TaskHandle, task *proto.Task, step int64) (subtaskMetas [][]byte, err error) + + // OnErrStage is called when: + // 1. subtask is finished with error. + // 2. task is cancelled after we have dispatched some subtasks. + OnErrStage(ctx context.Context, h TaskHandle, task *proto.Task, receiveErr []error) (subtaskMeta []byte, err error) + + // GetEligibleInstances is used to get the eligible instances for the task. + // on certain condition we may want to use some instances to do the task, such as instances with more disk. + GetEligibleInstances(ctx context.Context, task *proto.Task) ([]*infosync.ServerInfo, error) + + // IsRetryableErr is used to check whether the error occurred in dispatcher is retryable. + IsRetryableErr(err error) bool + + // GetNextStep is used to get the next step for the task. + // if task runs successfully, it should go from StepInit to business steps, + // then to StepDone, then dispatcher will mark it as finished. + GetNextStep(h TaskHandle, task *proto.Task) int64 +} + +// dispatcherFactoryFn is used to create a dispatcher. +type dispatcherFactoryFn func(ctx context.Context, taskMgr *storage.TaskManager, serverID string, task *proto.Task) Dispatcher + +var dispatcherFactoryMap = struct { + syncutil.RWMutex + m map[string]dispatcherFactoryFn +}{ + m: make(map[string]dispatcherFactoryFn), +} + +// RegisterDispatcherFactory is used to register the dispatcher factory. +// normally dispatcher ctor should be registered before the server start. +// and should be called in a single routine, such as in init(). +// after the server start, there's should be no write to the map. +// but for index backfill, the register call stack is so deep, not sure +// if it's safe to do so, so we use a lock here. +func RegisterDispatcherFactory(taskType string, ctor dispatcherFactoryFn) { + dispatcherFactoryMap.Lock() + defer dispatcherFactoryMap.Unlock() + dispatcherFactoryMap.m[taskType] = ctor +} + +// getDispatcherFactory is used to get the dispatcher factory. +func getDispatcherFactory(taskType string) dispatcherFactoryFn { + dispatcherFactoryMap.RLock() + defer dispatcherFactoryMap.RUnlock() + return dispatcherFactoryMap.m[taskType] +} + +// ClearDispatcherFactory is only used in test. +func ClearDispatcherFactory() { + dispatcherFactoryMap.Lock() + defer dispatcherFactoryMap.Unlock() + dispatcherFactoryMap.m = make(map[string]dispatcherFactoryFn) +} + +// CleanUpRoutine is used for the framework to do some clean up work if the task is finished. +type CleanUpRoutine interface { + // CleanUp do the clean up work. + CleanUp(ctx context.Context, task *proto.Task) error +} +type cleanUpFactoryFn func() CleanUpRoutine + +var cleanUpFactoryMap = struct { + syncutil.RWMutex + m map[string]cleanUpFactoryFn +}{ + m: make(map[string]cleanUpFactoryFn), +} + +// RegisterDispatcherCleanUpFactory is used to register the dispatcher clean up factory. +// normally dispatcher cleanup is used in the dispatcher_manager gcTaskLoop to do clean up +// works when tasks are finished. +func RegisterDispatcherCleanUpFactory(taskType string, ctor cleanUpFactoryFn) { + cleanUpFactoryMap.Lock() + defer cleanUpFactoryMap.Unlock() + cleanUpFactoryMap.m[taskType] = ctor +} + +// getDispatcherCleanUpFactory is used to get the dispatcher factory. +func getDispatcherCleanUpFactory(taskType string) cleanUpFactoryFn { + cleanUpFactoryMap.RLock() + defer cleanUpFactoryMap.RUnlock() + return cleanUpFactoryMap.m[taskType] +} + +// ClearDispatcherCleanUpFactory is only used in test. +func ClearDispatcherCleanUpFactory() { + cleanUpFactoryMap.Lock() + defer cleanUpFactoryMap.Unlock() + cleanUpFactoryMap.m = make(map[string]cleanUpFactoryFn) +} diff --git a/pkg/disttask/framework/dispatcher/main_test.go b/pkg/disttask/framework/dispatcher/main_test.go new file mode 100644 index 0000000000000..160acfeca215d --- /dev/null +++ b/pkg/disttask/framework/dispatcher/main_test.go @@ -0,0 +1,62 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package dispatcher + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +// DispatcherForTest exports for testing. +type DispatcherManagerForTest interface { + GetRunningTaskCnt() int + DelRunningTask(globalTaskID int64) + DoCleanUpRoutine() +} + +// GetRunningGTaskCnt implements Dispatcher.GetRunningGTaskCnt interface. +func (dm *Manager) GetRunningTaskCnt() int { + return dm.getRunningTaskCnt() +} + +// DelRunningGTask implements Dispatcher.DelRunningGTask interface. +func (dm *Manager) DelRunningTask(globalTaskID int64) { + dm.delRunningTask(globalTaskID) +} + +// DoCleanUpRoutine implements Dispatcher.DoCleanUpRoutine interface. +func (dm *Manager) DoCleanUpRoutine() { + dm.doCleanUpRoutine() +} + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + // Make test more fast. + checkTaskRunningInterval = checkTaskRunningInterval / 10 + checkTaskFinishedInterval = checkTaskFinishedInterval / 10 + RetrySQLInterval = RetrySQLInterval / 20 + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("syscall.syscall"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/disttask/framework/framework_dynamic_dispatch_test.go b/pkg/disttask/framework/framework_dynamic_dispatch_test.go new file mode 100644 index 0000000000000..043fd7d43a229 --- /dev/null +++ b/pkg/disttask/framework/framework_dynamic_dispatch_test.go @@ -0,0 +1,111 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package framework_test + +import ( + "context" + "fmt" + "sync" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +type testDynamicDispatcherExt struct { + cnt int +} + +var _ dispatcher.Extension = (*testDynamicDispatcherExt)(nil) + +func (*testDynamicDispatcherExt) OnTick(_ context.Context, _ *proto.Task) {} + +func (dsp *testDynamicDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, gTask *proto.Task, _ int64) (metas [][]byte, err error) { + // step1 + if gTask.Step == proto.StepInit { + dsp.cnt++ + return [][]byte{ + []byte(fmt.Sprintf("task%d", dsp.cnt)), + []byte(fmt.Sprintf("task%d", dsp.cnt)), + }, nil + } + + // step2 + if gTask.Step == proto.StepOne { + dsp.cnt++ + return [][]byte{ + []byte(fmt.Sprintf("task%d", dsp.cnt)), + }, nil + } + return nil, nil +} + +func (*testDynamicDispatcherExt) OnErrStage(_ context.Context, _ dispatcher.TaskHandle, _ *proto.Task, _ []error) (meta []byte, err error) { + return nil, nil +} + +func (dsp *testDynamicDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { + switch task.Step { + case proto.StepInit: + return proto.StepOne + case proto.StepOne: + return proto.StepTwo + default: + return proto.StepDone + } +} + +func (*testDynamicDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { + return generateSchedulerNodes4Test() +} + +func (*testDynamicDispatcherExt) IsRetryableErr(error) bool { + return true +} + +func TestFrameworkDynamicBasic(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &testDynamicDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 3) + DispatchTaskAndCheckSuccess("key1", t, &m) + distContext.Close() +} + +func TestFrameworkDynamicHA(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &testDynamicDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 3) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr", "5*return()")) + DispatchTaskAndCheckSuccess("key1", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr1", "5*return()")) + DispatchTaskAndCheckSuccess("key2", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr2", "5*return()")) + DispatchTaskAndCheckSuccess("key3", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDynamicDispatchErr2")) + distContext.Close() +} diff --git a/disttask/framework/framework_err_handling_test.go b/pkg/disttask/framework/framework_err_handling_test.go similarity index 95% rename from disttask/framework/framework_err_handling_test.go rename to pkg/disttask/framework/framework_err_handling_test.go index 90a3510263c4b..9de9099d767f3 100644 --- a/disttask/framework/framework_err_handling_test.go +++ b/pkg/disttask/framework/framework_err_handling_test.go @@ -20,10 +20,10 @@ import ( "sync" "testing" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/testkit" "go.uber.org/mock/gomock" ) diff --git a/pkg/disttask/framework/framework_ha_test.go b/pkg/disttask/framework/framework_ha_test.go new file mode 100644 index 0000000000000..1972980cb97c1 --- /dev/null +++ b/pkg/disttask/framework/framework_ha_test.go @@ -0,0 +1,191 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package framework_test + +import ( + "context" + "sync" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +type haTestDispatcherExt struct { + cnt int +} + +var _ dispatcher.Extension = (*haTestDispatcherExt)(nil) + +func (*haTestDispatcherExt) OnTick(_ context.Context, _ *proto.Task) { +} + +func (dsp *haTestDispatcherExt) OnNextSubtasksBatch(_ context.Context, _ dispatcher.TaskHandle, gTask *proto.Task, _ int64) (metas [][]byte, err error) { + if gTask.Step == proto.StepInit { + dsp.cnt = 10 + return [][]byte{ + []byte("task1"), + []byte("task2"), + []byte("task3"), + []byte("task4"), + []byte("task5"), + []byte("task6"), + []byte("task7"), + []byte("task8"), + []byte("task9"), + []byte("task10"), + }, nil + } + if gTask.Step == proto.StepOne { + dsp.cnt = 15 + return [][]byte{ + []byte("task11"), + []byte("task12"), + []byte("task13"), + []byte("task14"), + []byte("task15"), + }, nil + } + return nil, nil +} + +func (*haTestDispatcherExt) OnErrStage(ctx context.Context, h dispatcher.TaskHandle, gTask *proto.Task, receiveErr []error) (subtaskMeta []byte, err error) { + return nil, nil +} + +func (*haTestDispatcherExt) GetEligibleInstances(_ context.Context, _ *proto.Task) ([]*infosync.ServerInfo, error) { + return generateSchedulerNodes4Test() +} + +func (*haTestDispatcherExt) IsRetryableErr(error) bool { + return true +} + +func (dsp *haTestDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { + switch task.Step { + case proto.StepInit: + return proto.StepOne + case proto.StepOne: + return proto.StepTwo + default: + return proto.StepDone + } +} + +func TestHABasic(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 4) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "4*return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + distContext.Close() +} + +func TestHAManyNodes(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 30) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "30*return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + distContext.Close() +} + +func TestHAFailInDifferentStage(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 6) + // stage1 : server num from 6 to 3. + // stage2 : server num from 3 to 2. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "6*return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown2", "return()")) + + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown2")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + distContext.Close() +} + +func TestHAFailInDifferentStageManyNodes(t *testing.T) { + var m sync.Map + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 30) + // stage1 : server num from 30 to 27. + // stage2 : server num from 27 to 26. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "30*return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown2", "return()")) + + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown2")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + distContext.Close() +} + +func TestHAReplacedButRunning(t *testing.T) { + var m sync.Map + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 4) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBPartitionThenResume", "10*return(true)")) + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBPartitionThenResume")) + distContext.Close() +} + +func TestHAReplacedButRunningManyNodes(t *testing.T) { + var m sync.Map + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &haTestDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 30) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBPartitionThenResume", "30*return(true)")) + DispatchTaskAndCheckSuccess("😊", t, &m) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBPartitionThenResume")) + distContext.Close() +} diff --git a/pkg/disttask/framework/framework_pause_and_resume_test.go b/pkg/disttask/framework/framework_pause_and_resume_test.go new file mode 100644 index 0000000000000..05300e555714a --- /dev/null +++ b/pkg/disttask/framework/framework_pause_and_resume_test.go @@ -0,0 +1,83 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package framework_test + +import ( + "sync" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func CheckSubtasksState(t *testing.T, taskID int64, state string, expectedCnt int64) { + mgr, err := storage.GetTaskManager() + require.NoError(t, err) + mgr.PrintSubtaskInfo(taskID) + cnt, err := mgr.GetSubtaskInStatesCnt(taskID, state) + require.NoError(t, err) + historySubTasksCnt, err := storage.GetSubtasksFromHistoryByTaskIDForTest(mgr, taskID) + require.NoError(t, err) + require.Equal(t, expectedCnt, cnt+int64(historySubTasksCnt)) +} + +func TestFrameworkPauseAndResume(t *testing.T) { + var m sync.Map + ctrl := gomock.NewController(t) + defer ctrl.Finish() + RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) + distContext := testkit.NewDistExecutionContext(t, 3) + // 1. dispatch and pause one running task. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/pauseTaskAfterRefreshTask", "2*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/syncAfterResume", "return()")) + DispatchTaskAndCheckState("key1", t, &m, proto.TaskStatePaused) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/pauseTaskAfterRefreshTask")) + // 4 subtask dispatched. + require.NoError(t, handle.ResumeTask("key1")) + <-dispatcher.TestSyncChan + WaitTaskExit(t, "key1") + CheckSubtasksState(t, 1, proto.TaskStateSucceed, 4) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/syncAfterResume")) + + mgr, err := storage.GetTaskManager() + require.NoError(t, err) + errs, err := mgr.CollectSubTaskError(1) + require.NoError(t, err) + require.Empty(t, errs) + + // 2. pause pending task. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/pausePendingTask", "2*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/syncAfterResume", "1*return()")) + DispatchTaskAndCheckState("key2", t, &m, proto.TaskStatePaused) + + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/pausePendingTask")) + // 4 subtask dispatched. + require.NoError(t, handle.ResumeTask("key2")) + <-dispatcher.TestSyncChan + WaitTaskExit(t, "key2") + CheckSubtasksState(t, 1, proto.TaskStateSucceed, 4) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/syncAfterResume")) + + errs, err = mgr.CollectSubTaskError(1) + require.NoError(t, err) + require.Empty(t, errs) + distContext.Close() +} diff --git a/disttask/framework/framework_rollback_test.go b/pkg/disttask/framework/framework_rollback_test.go similarity index 86% rename from disttask/framework/framework_rollback_test.go rename to pkg/disttask/framework/framework_rollback_test.go index c05ee5e0c03e7..5e88cce7b4bdc 100644 --- a/disttask/framework/framework_rollback_test.go +++ b/pkg/disttask/framework/framework_rollback_test.go @@ -21,12 +21,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/mock" - mockexecute "github.com/pingcap/tidb/disttask/framework/mock/execute" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + mockexecute "github.com/pingcap/tidb/pkg/disttask/framework/mock/execute" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -105,9 +105,9 @@ func TestFrameworkRollback(t *testing.T) { defer ctrl.Finish() registerRollbackTaskMeta(t, ctrl, &m) distContext := testkit.NewDistExecutionContext(t, 2) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/cancelTaskAfterRefreshTask", "2*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/cancelTaskAfterRefreshTask", "2*return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/cancelTaskAfterRefreshTask")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/cancelTaskAfterRefreshTask")) }() DispatchTaskAndCheckState("key1", t, &m, proto.TaskStateReverted) diff --git a/disttask/framework/framework_test.go b/pkg/disttask/framework/framework_test.go similarity index 82% rename from disttask/framework/framework_test.go rename to pkg/disttask/framework/framework_test.go index bea0170ec4b19..68c462c0d70a5 100644 --- a/disttask/framework/framework_test.go +++ b/pkg/disttask/framework/framework_test.go @@ -23,14 +23,14 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/mock" - mockexecute "github.com/pingcap/tidb/disttask/framework/mock/execute" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + mockexecute "github.com/pingcap/tidb/pkg/disttask/framework/mock/execute" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) @@ -239,9 +239,9 @@ func DispatchTaskAndCheckSuccess(taskKey string, t *testing.T, m *sync.Map) { } func DispatchAndCancelTask(taskKey string, t *testing.T, m *sync.Map) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunCancel", "1*return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunCancel", "1*return(1)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunCancel")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunCancel")) }() task := DispatchTask(taskKey, t) require.Equal(t, proto.TaskStateReverted, task.State) @@ -272,7 +272,7 @@ func DispatchMultiTasksAndOneFail(t *testing.T, num int, m []sync.Map) []*proto. for i := 0; i < num; i++ { if i == 0 { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunErr", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunErr", "1*return(true)")) taskID[0], err = mgr.AddNewGlobalTask("key0", "Example", 8, nil) require.NoError(t, err) start[0] = time.Now() @@ -290,7 +290,7 @@ func DispatchMultiTasksAndOneFail(t *testing.T, num int, m []sync.Map) []*proto. break } } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunErr")) } else { taskID[i], err = mgr.AddNewGlobalTask(fmt.Sprintf("key%d", i), proto.Int2Type(i+2), 8, nil) require.NoError(t, err) @@ -428,9 +428,9 @@ func TestFrameworkSubTaskFailed(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunErr", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunErr", "1*return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockExecutorRunErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockExecutorRunErr")) }() DispatchTaskAndCheckState("key1", t, &m, proto.TaskStateReverted) @@ -444,9 +444,9 @@ func TestFrameworkSubTaskInitEnvFailed(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockExecSubtaskInitEnvErr", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockExecSubtaskInitEnvErr", "return()")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockExecSubtaskInitEnvErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockExecSubtaskInitEnvErr")) }() DispatchTaskAndCheckState("key1", t, &m, proto.TaskStateReverted) distContext.Close() @@ -463,9 +463,9 @@ func TestOwnerChange(t *testing.T) { dispatcher.MockOwnerChange = func() { distContext.SetOwner(0) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockOwnerChange", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockOwnerChange", "1*return(true)")) DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockOwnerChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockOwnerChange")) distContext.Close() } @@ -476,9 +476,9 @@ func TestFrameworkCancelThenSubmitSubTask(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 3) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/cancelBeforeUpdate", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/cancelBeforeUpdate", "return()")) DispatchTaskAndCheckState("😊", t, &m, proto.TaskStateReverted) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/cancelBeforeUpdate")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/cancelBeforeUpdate")) distContext.Close() } @@ -490,13 +490,13 @@ func TestSchedulerDownBasic(t *testing.T) { RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 4) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "4*return(\":4000\")")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "4*return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) distContext.Close() } @@ -508,13 +508,13 @@ func TestSchedulerDownManyNodes(t *testing.T) { RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 30) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager", "30*return(\":4000\")")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager", "30*return(\":4000\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown", "return(\":4000\")")) DispatchTaskAndCheckSuccess("😊", t, &m) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockCleanScheduler")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockTiDBDown")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/mockStopManager")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockCleanScheduler")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockTiDBDown")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/mockStopManager")) distContext.Close() } @@ -591,11 +591,11 @@ func TestGC(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/storage/subtaskHistoryKeepSeconds", "return(1)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/historySubtaskTableGcInterval", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/storage/subtaskHistoryKeepSeconds", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/historySubtaskTableGcInterval", "return(1)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/storage/subtaskHistoryKeepSeconds")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/historySubtaskTableGcInterval")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/storage/subtaskHistoryKeepSeconds")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/historySubtaskTableGcInterval")) }() distContext := testkit.NewDistExecutionContext(t, 3) @@ -633,9 +633,9 @@ func TestFrameworkSubtaskFinishedCancel(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 3) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockSubtaskFinishedCancel", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockSubtaskFinishedCancel", "1*return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockSubtaskFinishedCancel")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockSubtaskFinishedCancel")) }() DispatchTaskAndCheckState("key1", t, &m, proto.TaskStateReverted) distContext.Close() @@ -648,9 +648,9 @@ func TestFrameworkRunSubtaskCancel(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 3) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockRunSubtaskCancel", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockRunSubtaskCancel", "1*return(true)")) DispatchTaskAndCheckState("key1", t, &m, proto.TaskStateReverted) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockRunSubtaskCancel")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockRunSubtaskCancel")) distContext.Close() } @@ -660,7 +660,7 @@ func TestFrameworkCleanUpRoutine(t *testing.T) { defer ctrl.Finish() RegisterTaskMeta(t, ctrl, &m, &testDispatcherExt{}) distContext := testkit.NewDistExecutionContext(t, 3) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished", "return()")) DispatchTaskAndCheckSuccess("key1", t, &m) <-dispatcher.WaitCleanUpFinished mgr, err := storage.GetTaskManager() diff --git a/pkg/disttask/framework/handle/BUILD.bazel b/pkg/disttask/framework/handle/BUILD.bazel new file mode 100644 index 0000000000000..dd0697e8d2f72 --- /dev/null +++ b/pkg/disttask/framework/handle/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "handle", + srcs = ["handle.go"], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/handle", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/metrics", + "//pkg/util/backoff", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "handle_test", + timeout = "short", + srcs = ["handle_test.go"], + flaky = True, + deps = [ + ":handle", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/testkit", + "//pkg/util/backoff", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + ], +) diff --git a/pkg/disttask/framework/handle/handle.go b/pkg/disttask/framework/handle/handle.go new file mode 100644 index 0000000000000..58a8020229d31 --- /dev/null +++ b/pkg/disttask/framework/handle/handle.go @@ -0,0 +1,188 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "context" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/backoff" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +var ( + checkTaskFinishInterval = 300 * time.Millisecond +) + +// SubmitGlobalTask submits a global task. +func SubmitGlobalTask(taskKey, taskType string, concurrency int, taskMeta []byte) (*proto.Task, error) { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return nil, err + } + globalTask, err := globalTaskManager.GetGlobalTaskByKey(taskKey) + if err != nil { + return nil, err + } + + if globalTask == nil { + taskID, err := globalTaskManager.AddNewGlobalTask(taskKey, taskType, concurrency, taskMeta) + if err != nil { + return nil, err + } + + globalTask, err = globalTaskManager.GetGlobalTaskByID(taskID) + if err != nil { + return nil, err + } + + if globalTask == nil { + return nil, errors.Errorf("cannot find global task with ID %d", taskID) + } + metrics.UpdateMetricsForAddTask(globalTask) + } + return globalTask, nil +} + +// WaitGlobalTask waits for a global task to finish. +func WaitGlobalTask(ctx context.Context, globalTask *proto.Task) error { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + ticker := time.NewTicker(checkTaskFinishInterval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return ctx.Err() + case <-ticker.C: + found, err := globalTaskManager.GetTaskByIDWithHistory(globalTask.ID) + if err != nil { + logutil.Logger(ctx).Error("cannot get global task during waiting", + zap.Int64("task-id", globalTask.ID), + zap.Error(err)) + continue + } + if found == nil { + return errors.Errorf("cannot find global task with ID %d", globalTask.ID) + } + + switch found.State { + case proto.TaskStateSucceed: + return nil + case proto.TaskStateReverted: + logutil.BgLogger().Error("global task reverted", zap.Int64("task-id", globalTask.ID), zap.Error(found.Error)) + return found.Error + case proto.TaskStatePaused: + logutil.BgLogger().Error("global task paused", zap.Int64("task-id", globalTask.ID)) + return nil + case proto.TaskStateFailed, proto.TaskStateCanceled: + return errors.Errorf("task stopped with state %s, err %v", found.State, found.Error) + } + } + } +} + +// SubmitAndRunGlobalTask submits a global task and wait for it to finish. +func SubmitAndRunGlobalTask(ctx context.Context, taskKey, taskType string, concurrency int, taskMeta []byte) error { + globalTask, err := SubmitGlobalTask(taskKey, taskType, concurrency, taskMeta) + if err != nil { + return err + } + return WaitGlobalTask(ctx, globalTask) +} + +// CancelGlobalTask cancels a global task. +func CancelGlobalTask(taskKey string) error { + taskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + task, err := taskManager.GetGlobalTaskByKey(taskKey) + if err != nil { + return err + } + if task == nil { + logutil.BgLogger().Info("task not exist", zap.String("taskKey", taskKey)) + + return nil + } + return taskManager.CancelGlobalTask(task.ID) +} + +// PauseTask pauses a task. +func PauseTask(taskKey string) error { + taskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + found, err := taskManager.PauseTask(taskKey) + if !found { + logutil.BgLogger().Info("task not pausable", zap.String("taskKey", taskKey)) + return nil + } + return err +} + +// ResumeTask resumes a task. +func ResumeTask(taskKey string) error { + taskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + found, err := taskManager.ResumeTask(taskKey) + if !found { + logutil.BgLogger().Info("task not resumable", zap.String("taskKey", taskKey)) + return nil + } + return err +} + +// RunWithRetry runs a function with retry, when retry exceed max retry time, it +// returns the last error met. +// if the function fails with err, it should return a bool to indicate whether +// the error is retryable. +// if context done, it will stop early and return ctx.Err(). +func RunWithRetry( + ctx context.Context, + maxRetry int, + backoffer backoff.Backoffer, + logger *zap.Logger, + f func(context.Context) (bool, error), +) error { + var lastErr error + for i := 0; i < maxRetry; i++ { + retryable, err := f(ctx) + if err == nil || !retryable { + return err + } + lastErr = err + logger.Warn("met retryable error", zap.Int("retry-count", i), + zap.Int("max-retry", maxRetry), zap.Error(err)) + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(backoffer.Backoff(i)): + } + } + return lastErr +} diff --git a/pkg/disttask/framework/handle/handle_test.go b/pkg/disttask/framework/handle/handle_test.go new file mode 100644 index 0000000000000..89ffc31f1d821 --- /dev/null +++ b/pkg/disttask/framework/handle/handle_test.go @@ -0,0 +1,129 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package handle_test + +import ( + "context" + "math" + "sync/atomic" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/backoff" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/util" +) + +func TestHandle(t *testing.T) { + ctx := context.Background() + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) + storage.SetTaskManager(mgr) + + // no dispatcher registered + err := handle.SubmitAndRunGlobalTask(ctx, "1", proto.TaskTypeExample, 2, []byte("byte")) + require.Error(t, err) + + task, err := mgr.GetGlobalTaskByID(1) + require.NoError(t, err) + require.Equal(t, int64(1), task.ID) + require.Equal(t, "1", task.Key) + require.Equal(t, proto.TaskTypeExample, task.Type) + // no dispatcher registered + require.Equal(t, proto.TaskStateFailed, task.State) + require.Equal(t, proto.StepInit, task.Step) + require.Equal(t, uint64(2), task.Concurrency) + require.Equal(t, []byte("byte"), task.Meta) + + require.NoError(t, handle.CancelGlobalTask("1")) + + task, err = handle.SubmitGlobalTask("2", proto.TaskTypeExample, 2, []byte("byte")) + require.NoError(t, err) + require.NoError(t, handle.PauseTask("2")) + require.NoError(t, handle.ResumeTask("2")) +} + +func TestRunWithRetry(t *testing.T) { + ctx := context.Background() + + // retry count exceed + backoffer := backoff.NewExponential(100*time.Millisecond, 1, time.Second) + err := handle.RunWithRetry(ctx, 3, backoffer, log.L(), + func(ctx context.Context) (bool, error) { + return true, errors.New("mock error") + }, + ) + require.ErrorContains(t, err, "mock error") + + // non-retryable error + var end atomic.Bool + go func() { + defer end.Store(true) + backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) + err = handle.RunWithRetry(ctx, math.MaxInt, backoffer, log.L(), + func(ctx context.Context) (bool, error) { + return false, errors.New("mock error") + }, + ) + require.Error(t, err) + }() + require.Eventually(t, func() bool { + return end.Load() + }, 5*time.Second, 100*time.Millisecond) + + // fail with retryable error once, then success + end.Store(false) + go func() { + defer end.Store(true) + backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) + var i int + err = handle.RunWithRetry(ctx, math.MaxInt, backoffer, log.L(), + func(ctx context.Context) (bool, error) { + if i == 0 { + i++ + return true, errors.New("mock error") + } + return false, nil + }, + ) + require.NoError(t, err) + }() + require.Eventually(t, func() bool { + return end.Load() + }, 5*time.Second, 100*time.Millisecond) + + // context done + subctx, cancel := context.WithCancel(ctx) + cancel() + backoffer = backoff.NewExponential(100*time.Millisecond, 1, time.Second) + err = handle.RunWithRetry(subctx, math.MaxInt, backoffer, log.L(), + func(ctx context.Context) (bool, error) { + return true, errors.New("mock error") + }, + ) + require.ErrorIs(t, err, context.Canceled) +} diff --git a/pkg/disttask/framework/mock/BUILD.bazel b/pkg/disttask/framework/mock/BUILD.bazel new file mode 100644 index 0000000000000..edb39910e5290 --- /dev/null +++ b/pkg/disttask/framework/mock/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mock", + srcs = [ + "dispatcher_mock.go", + "plan_mock.go", + "scheduler_mock.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/mock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/planner", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler/execute", + "@org_uber_go_mock//gomock", + ], +) diff --git a/disttask/framework/mock/dispatcher_mock.go b/pkg/disttask/framework/mock/dispatcher_mock.go similarity index 95% rename from disttask/framework/mock/dispatcher_mock.go rename to pkg/disttask/framework/mock/dispatcher_mock.go index 3a820029df9b0..c353e94a3e9af 100644 --- a/disttask/framework/mock/dispatcher_mock.go +++ b/pkg/disttask/framework/mock/dispatcher_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/disttask/framework/dispatcher (interfaces: Dispatcher,CleanUpRoutine) +// Source: github.com/pingcap/tidb/pkg/disttask/framework/dispatcher (interfaces: Dispatcher,CleanUpRoutine) // Package mock is a generated GoMock package. package mock @@ -8,7 +8,7 @@ import ( context "context" reflect "reflect" - proto "github.com/pingcap/tidb/disttask/framework/proto" + proto "github.com/pingcap/tidb/pkg/disttask/framework/proto" gomock "go.uber.org/mock/gomock" ) diff --git a/pkg/disttask/framework/mock/execute/BUILD.bazel b/pkg/disttask/framework/mock/execute/BUILD.bazel new file mode 100644 index 0000000000000..5d4e46271fe38 --- /dev/null +++ b/pkg/disttask/framework/mock/execute/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "execute", + srcs = ["execute_mock.go"], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/mock/execute", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/proto", + "@org_uber_go_mock//gomock", + ], +) diff --git a/disttask/framework/mock/execute/execute_mock.go b/pkg/disttask/framework/mock/execute/execute_mock.go similarity index 95% rename from disttask/framework/mock/execute/execute_mock.go rename to pkg/disttask/framework/mock/execute/execute_mock.go index b29614ee792e4..b3e6b249c0e44 100644 --- a/disttask/framework/mock/execute/execute_mock.go +++ b/pkg/disttask/framework/mock/execute/execute_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/disttask/framework/scheduler/execute (interfaces: SubtaskExecutor) +// Source: github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute (interfaces: SubtaskExecutor) // Package execute is a generated GoMock package. package execute @@ -8,7 +8,7 @@ import ( context "context" reflect "reflect" - proto "github.com/pingcap/tidb/disttask/framework/proto" + proto "github.com/pingcap/tidb/pkg/disttask/framework/proto" gomock "go.uber.org/mock/gomock" ) diff --git a/disttask/framework/mock/plan_mock.go b/pkg/disttask/framework/mock/plan_mock.go similarity index 95% rename from disttask/framework/mock/plan_mock.go rename to pkg/disttask/framework/mock/plan_mock.go index 59dd785138615..f6ce74c24ee68 100644 --- a/disttask/framework/mock/plan_mock.go +++ b/pkg/disttask/framework/mock/plan_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/disttask/framework/planner (interfaces: LogicalPlan,PipelineSpec) +// Source: github.com/pingcap/tidb/pkg/disttask/framework/planner (interfaces: LogicalPlan,PipelineSpec) // Package mock is a generated GoMock package. package mock @@ -7,7 +7,7 @@ package mock import ( reflect "reflect" - planner "github.com/pingcap/tidb/disttask/framework/planner" + planner "github.com/pingcap/tidb/pkg/disttask/framework/planner" gomock "go.uber.org/mock/gomock" ) diff --git a/disttask/framework/mock/scheduler_mock.go b/pkg/disttask/framework/mock/scheduler_mock.go similarity index 98% rename from disttask/framework/mock/scheduler_mock.go rename to pkg/disttask/framework/mock/scheduler_mock.go index 74981b0f00384..41019859b2e22 100644 --- a/disttask/framework/mock/scheduler_mock.go +++ b/pkg/disttask/framework/mock/scheduler_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/disttask/framework/scheduler (interfaces: TaskTable,Pool,Scheduler,Extension) +// Source: github.com/pingcap/tidb/pkg/disttask/framework/scheduler (interfaces: TaskTable,Pool,Scheduler,Extension) // Package mock is a generated GoMock package. package mock @@ -8,8 +8,8 @@ import ( context "context" reflect "reflect" - proto "github.com/pingcap/tidb/disttask/framework/proto" - execute "github.com/pingcap/tidb/disttask/framework/scheduler/execute" + proto "github.com/pingcap/tidb/pkg/disttask/framework/proto" + execute "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" gomock "go.uber.org/mock/gomock" ) diff --git a/pkg/disttask/framework/planner/BUILD.bazel b/pkg/disttask/framework/planner/BUILD.bazel new file mode 100644 index 0000000000000..6c3941c018d46 --- /dev/null +++ b/pkg/disttask/framework/planner/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "planner", + srcs = [ + "plan.go", + "planner.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/planner", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/storage", + "//pkg/sessionctx", + ], +) + +go_test( + name = "planner_test", + timeout = "short", + srcs = [ + "plan_test.go", + "planner_test.go", + ], + flaky = True, + deps = [ + ":planner", + "//pkg/disttask/framework/mock", + "//pkg/disttask/framework/storage", + "//pkg/testkit", + "@com_github_ngaut_pools//:pools", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_mock//gomock", + ], +) diff --git a/pkg/disttask/framework/planner/plan.go b/pkg/disttask/framework/planner/plan.go new file mode 100644 index 0000000000000..7132eeec96d7e --- /dev/null +++ b/pkg/disttask/framework/planner/plan.go @@ -0,0 +1,115 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package planner + +import ( + "context" + + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// PlanCtx is the context for planning. +type PlanCtx struct { + Ctx context.Context + + // integrate with current distribute framework + SessionCtx sessionctx.Context + TaskID int64 + TaskKey string + TaskType string + ThreadCnt int + + // PreviousSubtaskMetas is subtask metas of previous steps. + // We can remove this field if we find a better way to pass the result between steps. + PreviousSubtaskMetas map[int64][][]byte + GlobalSort bool + NextTaskStep int64 +} + +// LogicalPlan represents a logical plan in distribute framework. +// A normal flow of distribute framework is: logical plan -> physical plan -> pipelines. +// To integrate with current distribute framework, the flow becomes: +// logical plan -> task meta -> physical plan -> subtaskmetas -> pipelines. +type LogicalPlan interface { + ToTaskMeta() ([]byte, error) + FromTaskMeta([]byte) error + ToPhysicalPlan(PlanCtx) (*PhysicalPlan, error) +} + +// PhysicalPlan is a DAG of processors in distribute framework. +// Each processor is a node process the task with a pipeline, +// and receive/pass the result to other processors via input and output links. +type PhysicalPlan struct { + Processors []ProcessorSpec +} + +// AddProcessor adds a node to the DAG. +func (p *PhysicalPlan) AddProcessor(processor ProcessorSpec) { + p.Processors = append(p.Processors, processor) +} + +// ToSubtaskMetas converts the physical plan to a list of subtask metas. +func (p *PhysicalPlan) ToSubtaskMetas(ctx PlanCtx, step int64) ([][]byte, error) { + subtaskMetas := make([][]byte, 0, len(p.Processors)) + for _, processor := range p.Processors { + if processor.Step != step { + continue + } + subtaskMeta, err := processor.Pipeline.ToSubtaskMeta(ctx) + if err != nil { + return nil, err + } + subtaskMetas = append(subtaskMetas, subtaskMeta) + } + return subtaskMetas, nil +} + +// ProcessorSpec is the specification of a processor. +// A processor is a node in the DAG. +// It contains input links from other processors, as well as output links to other processors. +// It also contains an pipeline which is the actual logic of the processor. +type ProcessorSpec struct { + ID int + Input InputSpec + Pipeline PipelineSpec + Output OutputSpec + // We can remove this field if we find a better way to pass the result between steps. + Step int64 +} + +// InputSpec is the specification of an input. +type InputSpec struct { + ColumnTypes []byte + Links []LinkSpec +} + +// OutputSpec is the specification of an output. +type OutputSpec struct { + Links []LinkSpec +} + +// LinkSpec is the specification of a link. +// Link connects pipelines between different nodes. +type LinkSpec struct { + ProcessorID int + // Support endpoint for communication between processors. + // Endpoint string +} + +// PipelineSpec is the specification of an pipeline. +type PipelineSpec interface { + // ToSubtaskMeta converts the pipeline to a subtask meta + ToSubtaskMeta(PlanCtx) ([]byte, error) +} diff --git a/pkg/disttask/framework/planner/plan_test.go b/pkg/disttask/framework/planner/plan_test.go new file mode 100644 index 0000000000000..dec837a9c787c --- /dev/null +++ b/pkg/disttask/framework/planner/plan_test.go @@ -0,0 +1,38 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package planner_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func TestPhysicalPlan(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockPipelineSpec := mock.NewMockPipelineSpec(ctrl) + + plan := &planner.PhysicalPlan{} + planCtx := planner.PlanCtx{} + plan.AddProcessor(planner.ProcessorSpec{Pipeline: mockPipelineSpec, Step: 1}) + mockPipelineSpec.EXPECT().ToSubtaskMeta(gomock.Any()).Return([]byte("mock"), nil) + subtaskMetas, err := plan.ToSubtaskMetas(planCtx, 1) + require.NoError(t, err) + require.Equal(t, [][]byte{[]byte("mock")}, subtaskMetas) +} diff --git a/pkg/disttask/framework/planner/planner.go b/pkg/disttask/framework/planner/planner.go new file mode 100644 index 0000000000000..d2d5988d7b695 --- /dev/null +++ b/pkg/disttask/framework/planner/planner.go @@ -0,0 +1,46 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package planner + +import "github.com/pingcap/tidb/pkg/disttask/framework/storage" + +// Planner represents a distribute plan planner. +type Planner struct{} + +// NewPlanner creates a new planer instance. +func NewPlanner() *Planner { + return &Planner{} +} + +// Run runs the distribute plan. +func (*Planner) Run(planCtx PlanCtx, plan LogicalPlan) (int64, error) { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return 0, err + } + + taskMeta, err := plan.ToTaskMeta() + if err != nil { + return 0, err + } + + return globalTaskManager.AddGlobalTaskWithSession( + planCtx.SessionCtx, + planCtx.TaskKey, + planCtx.TaskType, + planCtx.ThreadCnt, + taskMeta, + ) +} diff --git a/pkg/disttask/framework/planner/planner_test.go b/pkg/disttask/framework/planner/planner_test.go new file mode 100644 index 0000000000000..22e68787857c8 --- /dev/null +++ b/pkg/disttask/framework/planner/planner_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package planner_test + +import ( + "context" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/util" + "go.uber.org/mock/gomock" +) + +func TestPlanner(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + store := testkit.CreateMockStore(t) + gtk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return gtk.Session(), nil + }, 1, 1, time.Second) + defer pool.Close() + mgr := storage.NewTaskManager(util.WithInternalSourceType(ctx, "taskManager"), pool) + storage.SetTaskManager(mgr) + + p := &planner.Planner{} + pCtx := planner.PlanCtx{ + Ctx: ctx, + SessionCtx: gtk.Session(), + TaskKey: "1", + TaskType: "example", + ThreadCnt: 1, + } + mockLogicalPlan := mock.NewMockLogicalPlan(ctrl) + mockLogicalPlan.EXPECT().ToTaskMeta().Return([]byte("mock"), nil) + taskID, err := p.Run(pCtx, mockLogicalPlan) + require.NoError(t, err) + require.Equal(t, int64(1), taskID) +} diff --git a/pkg/disttask/framework/proto/BUILD.bazel b/pkg/disttask/framework/proto/BUILD.bazel new file mode 100644 index 0000000000000..a03b24ec142c0 --- /dev/null +++ b/pkg/disttask/framework/proto/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "proto", + srcs = ["task.go"], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/proto", + visibility = ["//visibility:public"], +) + +go_test( + name = "proto_test", + timeout = "short", + srcs = ["task_test.go"], + embed = [":proto"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/disttask/framework/proto/task.go b/pkg/disttask/framework/proto/task.go similarity index 100% rename from disttask/framework/proto/task.go rename to pkg/disttask/framework/proto/task.go diff --git a/disttask/framework/proto/task_test.go b/pkg/disttask/framework/proto/task_test.go similarity index 100% rename from disttask/framework/proto/task_test.go rename to pkg/disttask/framework/proto/task_test.go diff --git a/pkg/disttask/framework/scheduler/BUILD.bazel b/pkg/disttask/framework/scheduler/BUILD.bazel new file mode 100644 index 0000000000000..b72f51527a99e --- /dev/null +++ b/pkg/disttask/framework/scheduler/BUILD.bazel @@ -0,0 +1,55 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "scheduler", + srcs = [ + "interface.go", + "manager.go", + "register.go", + "scheduler.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/scheduler", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/common", + "//pkg/config", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/handle", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler/execute", + "//pkg/disttask/framework/storage", + "//pkg/domain/infosync", + "//pkg/metrics", + "//pkg/resourcemanager/pool/spool", + "//pkg/resourcemanager/util", + "//pkg/util/backoff", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "scheduler_test", + timeout = "short", + srcs = [ + "manager_test.go", + "register_test.go", + "scheduler_test.go", + ], + embed = [":scheduler"], + flaky = True, + race = "on", + shard_count = 8, + deps = [ + "//pkg/disttask/framework/mock", + "//pkg/disttask/framework/mock/execute", + "//pkg/disttask/framework/proto", + "//pkg/resourcemanager/pool/spool", + "//pkg/resourcemanager/util", + "@com_github_pkg_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_mock//gomock", + ], +) diff --git a/pkg/disttask/framework/scheduler/execute/BUILD.bazel b/pkg/disttask/framework/scheduler/execute/BUILD.bazel new file mode 100644 index 0000000000000..92a3187687263 --- /dev/null +++ b/pkg/disttask/framework/scheduler/execute/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "execute", + srcs = [ + "interface.go", + "summary.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/disttask/framework/scheduler/execute/interface.go b/pkg/disttask/framework/scheduler/execute/interface.go new file mode 100644 index 0000000000000..d016dabc3c603 --- /dev/null +++ b/pkg/disttask/framework/scheduler/execute/interface.go @@ -0,0 +1,37 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package execute + +import ( + "context" + + "github.com/pingcap/tidb/pkg/disttask/framework/proto" +) + +// SubtaskExecutor defines the executor of a subtask. +type SubtaskExecutor interface { + // Init is used to initialize the environment for the subtask executor. + Init(context.Context) error + // RunSubtask is used to run the subtask. + RunSubtask(ctx context.Context, subtask *proto.Subtask) error + // Cleanup is used to clean up the environment for the subtask executor. + Cleanup(context.Context) error + // OnFinished is used to handle the subtask when it is finished. + // The subtask meta can be updated in place. + OnFinished(ctx context.Context, subtask *proto.Subtask) error + // Rollback is used to roll back all subtasks. + // TODO: right now all impl of Rollback is empty, maybe we can remove it. + Rollback(context.Context) error +} diff --git a/pkg/disttask/framework/scheduler/execute/summary.go b/pkg/disttask/framework/scheduler/execute/summary.go new file mode 100644 index 0000000000000..bc0c4bf4cb46d --- /dev/null +++ b/pkg/disttask/framework/scheduler/execute/summary.go @@ -0,0 +1,93 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package execute + +import ( + "context" + "sync" + "time" + + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// Summary is used to collect the summary of subtasks execution. +type Summary struct { + mu struct { + sync.Mutex + RowCount map[int64]int64 // subtask ID -> row count + } +} + +// NewSummary creates a new Summary. +func NewSummary() *Summary { + return &Summary{ + mu: struct { + sync.Mutex + RowCount map[int64]int64 + }{ + RowCount: map[int64]int64{}, + }, + } +} + +// UpdateRowCount updates the row count of the subtask. +func (s *Summary) UpdateRowCount(subtaskID int64, rowCount int64) { + s.mu.Lock() + defer s.mu.Unlock() + s.mu.RowCount[subtaskID] = rowCount +} + +// UpdateRowCountLoop updates the row count of the subtask periodically. +func (s *Summary) UpdateRowCountLoop(ctx context.Context, taskMgr *storage.TaskManager) { + ticker := time.NewTicker(3 * time.Second) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + s.PersistRowCount(ctx, taskMgr) + } + } +} + +// PersistRowCount persists the row count of the subtask to the storage. +func (s *Summary) PersistRowCount(ctx context.Context, taskMgr *storage.TaskManager) { + var copiedRowCount map[int64]int64 + s.mu.Lock() + if len(s.mu.RowCount) == 0 { + s.mu.Unlock() + return + } + copiedRowCount = make(map[int64]int64, len(s.mu.RowCount)) + for subtaskID, rowCount := range s.mu.RowCount { + copiedRowCount[subtaskID] = rowCount + } + s.mu.Unlock() + + for subtaskID, rowCount := range copiedRowCount { + err := taskMgr.UpdateSubtaskRowCount(subtaskID, rowCount) + if err != nil { + logutil.Logger(ctx).Warn("update subtask row count failed", zap.Error(err)) + } + } + s.mu.Lock() + for subtaskID := range copiedRowCount { + delete(s.mu.RowCount, subtaskID) + } + s.mu.Unlock() +} diff --git a/pkg/disttask/framework/scheduler/interface.go b/pkg/disttask/framework/scheduler/interface.go new file mode 100644 index 0000000000000..817959de64b96 --- /dev/null +++ b/pkg/disttask/framework/scheduler/interface.go @@ -0,0 +1,102 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package scheduler + +import ( + "context" + + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" +) + +// TaskTable defines the interface to access task table. +type TaskTable interface { + GetGlobalTasksInStates(states ...interface{}) (task []*proto.Task, err error) + GetGlobalTaskByID(taskID int64) (task *proto.Task, err error) + + GetSubtasksInStates(tidbID string, taskID int64, step int64, states ...interface{}) ([]*proto.Subtask, error) + GetFirstSubtaskInStates(instanceID string, taskID int64, step int64, states ...interface{}) (*proto.Subtask, error) + StartManager(tidbID string, role string) error + StartSubtask(subtaskID int64) error + UpdateSubtaskStateAndError(subtaskID int64, state string, err error) error + FinishSubtask(subtaskID int64, meta []byte) error + + HasSubtasksInStates(tidbID string, taskID int64, step int64, states ...interface{}) (bool, error) + UpdateErrorToSubtask(tidbID string, taskID int64, err error) error + IsSchedulerCanceled(tidbID string, taskID int64) (bool, error) + PauseSubtasks(tidbID string, taskID int64) error +} + +// Pool defines the interface of a pool. +type Pool interface { + Run(func()) error + RunWithConcurrency(chan func(), uint32) error + ReleaseAndWait() +} + +// Scheduler is the subtask scheduler for a task. +// Each task type should implement this interface. +type Scheduler interface { + Init(context.Context) error + Run(context.Context, *proto.Task) error + Rollback(context.Context, *proto.Task) error + Pause(context.Context, *proto.Task) error + Close() +} + +// Extension extends the scheduler. +// each task type should implement this interface. +type Extension interface { + // IsIdempotent returns whether the subtask is idempotent. + // when tidb restart, the subtask might be left in the running state. + // if it's idempotent, the scheduler can rerun the subtask, else + // the scheduler will mark the subtask as failed. + IsIdempotent(subtask *proto.Subtask) bool + // GetSubtaskExecutor returns the subtask executor for the subtask. + // Note: summary is the summary manager of all subtask of the same type now. + GetSubtaskExecutor(ctx context.Context, task *proto.Task, summary *execute.Summary) (execute.SubtaskExecutor, error) +} + +// EmptySubtaskExecutor is an empty scheduler. +// it can be used for the task that does not need to split into subtasks. +type EmptySubtaskExecutor struct { +} + +var _ execute.SubtaskExecutor = &EmptySubtaskExecutor{} + +// Init implements the SubtaskExecutor interface. +func (*EmptySubtaskExecutor) Init(context.Context) error { + return nil +} + +// RunSubtask implements the SubtaskExecutor interface. +func (*EmptySubtaskExecutor) RunSubtask(context.Context, *proto.Subtask) error { + return nil +} + +// Cleanup implements the SubtaskExecutor interface. +func (*EmptySubtaskExecutor) Cleanup(context.Context) error { + return nil +} + +// OnFinished implements the SubtaskExecutor interface. +func (*EmptySubtaskExecutor) OnFinished(_ context.Context, _ *proto.Subtask) error { + return nil +} + +// Rollback implements the SubtaskExecutor interface. +func (*EmptySubtaskExecutor) Rollback(context.Context) error { + return nil +} diff --git a/pkg/disttask/framework/scheduler/manager.go b/pkg/disttask/framework/scheduler/manager.go new file mode 100644 index 0000000000000..fe88a69c02780 --- /dev/null +++ b/pkg/disttask/framework/scheduler/manager.go @@ -0,0 +1,394 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package scheduler + +import ( + "context" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/spool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +var ( + schedulerPoolSize int32 = 4 + // same as dispatcher + checkTime = 300 * time.Millisecond + retrySQLTimes = 3 + retrySQLInterval = 500 * time.Millisecond +) + +// ManagerBuilder is used to build a Manager. +type ManagerBuilder struct { + newPool func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) +} + +// NewManagerBuilder creates a new ManagerBuilder. +func NewManagerBuilder() *ManagerBuilder { + return &ManagerBuilder{ + newPool: func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { + return spool.NewPool(name, size, component, options...) + }, + } +} + +// setPoolFactory sets the poolFactory to mock the Pool in unit test. +func (b *ManagerBuilder) setPoolFactory(poolFactory func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error)) { + b.newPool = poolFactory +} + +// Manager monitors the global task table and manages the schedulers. +type Manager struct { + taskTable TaskTable + schedulerPool Pool + mu struct { + sync.RWMutex + // taskID -> CancelCauseFunc. + // CancelCauseFunc is used to fast cancel the scheduler.Run. + handlingTasks map[int64]context.CancelCauseFunc + } + // id, it's the same as server id now, i.e. host:port. + id string + wg sync.WaitGroup + ctx context.Context + cancel context.CancelFunc + logCtx context.Context + newPool func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) +} + +// BuildManager builds a Manager. +func (b *ManagerBuilder) BuildManager(ctx context.Context, id string, taskTable TaskTable) (*Manager, error) { + m := &Manager{ + id: id, + taskTable: taskTable, + logCtx: logutil.WithFields(context.Background()), + newPool: b.newPool, + } + m.ctx, m.cancel = context.WithCancel(ctx) + m.mu.handlingTasks = make(map[int64]context.CancelCauseFunc) + + schedulerPool, err := m.newPool("scheduler_pool", schedulerPoolSize, util.DistTask) + if err != nil { + return nil, err + } + m.schedulerPool = schedulerPool + + return m, nil +} + +// Start starts the Manager. +func (m *Manager) Start() error { + logutil.Logger(m.logCtx).Debug("manager start") + var err error + for i := 0; i < retrySQLTimes; i++ { + err = m.taskTable.StartManager(m.id, config.GetGlobalConfig().Instance.TiDBServiceScope) + if err == nil { + break + } + if i%10 == 0 { + logutil.Logger(m.logCtx).Warn("start manager failed", zap.String("scope", config.GetGlobalConfig().Instance.TiDBServiceScope), + zap.Int("retry times", retrySQLTimes), zap.Error(err)) + } + time.Sleep(retrySQLInterval) + } + if err != nil { + return err + } + + m.wg.Add(1) + go func() { + defer m.wg.Done() + m.fetchAndHandleRunnableTasks(m.ctx) + }() + + m.wg.Add(1) + go func() { + defer m.wg.Done() + m.fetchAndFastCancelTasks(m.ctx) + }() + return nil +} + +// Stop stops the Manager. +func (m *Manager) Stop() { + m.cancel() + m.schedulerPool.ReleaseAndWait() + m.wg.Wait() +} + +// fetchAndHandleRunnableTasks fetches the runnable tasks from the global task table and handles them. +func (m *Manager) fetchAndHandleRunnableTasks(ctx context.Context) { + ticker := time.NewTicker(checkTime) + for { + select { + case <-ctx.Done(): + logutil.Logger(m.logCtx).Info("fetchAndHandleRunnableTasks done") + return + case <-ticker.C: + tasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStateRunning, proto.TaskStateReverting) + if err != nil { + m.logErr(err) + continue + } + m.onRunnableTasks(ctx, tasks) + } + } +} + +// fetchAndFastCancelTasks fetches the reverting/pausing tasks from the global task table and fast cancels them. +func (m *Manager) fetchAndFastCancelTasks(ctx context.Context) { + ticker := time.NewTicker(checkTime) + for { + select { + case <-ctx.Done(): + m.cancelAllRunningTasks() + logutil.Logger(m.logCtx).Info("fetchAndFastCancelTasks done") + return + case <-ticker.C: + tasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStateReverting) + if err != nil { + m.logErr(err) + continue + } + m.onCanceledTasks(ctx, tasks) + + // cancel pending/running subtasks, and mark them as paused. + pausingTasks, err := m.taskTable.GetGlobalTasksInStates(proto.TaskStatePausing) + if err != nil { + m.logErr(err) + continue + } + if err := m.onPausingTasks(pausingTasks); err != nil { + m.logErr(err) + continue + } + } + } +} + +// onRunnableTasks handles runnable tasks. +func (m *Manager) onRunnableTasks(ctx context.Context, tasks []*proto.Task) { + tasks = m.filterAlreadyHandlingTasks(tasks) + for _, task := range tasks { + exist, err := m.taskTable.HasSubtasksInStates(m.id, task.ID, task.Step, + proto.TaskStatePending, proto.TaskStateRevertPending, + // for the case that the tidb is restarted when the subtask is running. + proto.TaskStateRunning, proto.TaskStateReverting) + if err != nil { + logutil.Logger(m.logCtx).Error("check subtask exist failed", zap.Error(err)) + m.logErr(err) + continue + } + if !exist { + continue + } + logutil.Logger(m.logCtx).Info("detect new subtask", zap.Int64("task-id", task.ID)) + m.addHandlingTask(task.ID) + t := task + err = m.schedulerPool.Run(func() { + m.onRunnableTask(ctx, t) + m.removeHandlingTask(t.ID) + }) + // pool closed. + if err != nil { + m.removeHandlingTask(task.ID) + m.logErr(err) + return + } + } +} + +// onCanceledTasks cancels the running subtasks. +func (m *Manager) onCanceledTasks(_ context.Context, tasks []*proto.Task) { + m.mu.RLock() + defer m.mu.RUnlock() + for _, task := range tasks { + logutil.Logger(m.logCtx).Info("onCanceledTasks", zap.Int64("task-id", task.ID)) + if cancel, ok := m.mu.handlingTasks[task.ID]; ok && cancel != nil { + // subtask needs to change its state to canceled. + cancel(ErrCancelSubtask) + } + } +} + +// onPausingTasks pauses/cancels the pending/running subtasks. +func (m *Manager) onPausingTasks(tasks []*proto.Task) error { + m.mu.RLock() + defer m.mu.RUnlock() + for _, task := range tasks { + logutil.Logger(m.logCtx).Info("onPausingTasks", zap.Any("task_id", task.ID)) + if cancel, ok := m.mu.handlingTasks[task.ID]; ok && cancel != nil { + // Pause all running subtasks, don't mark subtasks as canceled. + // Should not change the subtask's state. + cancel(nil) + } + if err := m.taskTable.PauseSubtasks(m.id, task.ID); err != nil { + return err + } + } + return nil +} + +// cancelAllRunningTasks cancels all running tasks. +func (m *Manager) cancelAllRunningTasks() { + m.mu.RLock() + defer m.mu.RUnlock() + for id, cancel := range m.mu.handlingTasks { + logutil.Logger(m.logCtx).Info("cancelAllRunningTasks", zap.Int64("task-id", id)) + if cancel != nil { + // tidb shutdown, don't mark subtask as canceled. + // Should not change the subtask's state. + cancel(nil) + } + } +} + +// filterAlreadyHandlingTasks filters the tasks that are already handled. +func (m *Manager) filterAlreadyHandlingTasks(tasks []*proto.Task) []*proto.Task { + m.mu.RLock() + defer m.mu.RUnlock() + + var i int + for _, task := range tasks { + if _, ok := m.mu.handlingTasks[task.ID]; !ok { + tasks[i] = task + i++ + } + } + return tasks[:i] +} + +// TestContext only used in tests. +type TestContext struct { + TestSyncSubtaskRun chan struct{} + mockDown atomic.Bool +} + +var testContexts sync.Map + +// onRunnableTask handles a runnable task. +func (m *Manager) onRunnableTask(ctx context.Context, task *proto.Task) { + logutil.Logger(m.logCtx).Info("onRunnableTask", zap.Int64("task-id", task.ID), zap.String("type", task.Type)) + // runCtx only used in scheduler.Run, cancel in m.fetchAndFastCancelTasks. + factory := getSchedulerFactory(task.Type) + if factory == nil { + err := errors.Errorf("task type %s not found", task.Type) + m.logErrAndPersist(err, task.ID) + return + } + scheduler := factory(ctx, m.id, task, m.taskTable) + err := scheduler.Init(ctx) + if err != nil { + m.logErrAndPersist(err, task.ID) + return + } + defer scheduler.Close() + for { + select { + case <-ctx.Done(): + return + case <-time.After(checkTime): + } + failpoint.Inject("mockStopManager", func() { + testContexts.Store(m.id, &TestContext{make(chan struct{}), atomic.Bool{}}) + go func() { + v, ok := testContexts.Load(m.id) + if ok { + <-v.(*TestContext).TestSyncSubtaskRun + _ = infosync.MockGlobalServerInfoManagerEntry.DeleteByID(m.id) + m.Stop() + } + }() + }) + task, err := m.taskTable.GetGlobalTaskByID(task.ID) + if err != nil { + m.logErr(err) + return + } + if task == nil { + return + } + if task.State != proto.TaskStateRunning && task.State != proto.TaskStateReverting { + logutil.Logger(m.logCtx).Info("onRunnableTask exit", + zap.Int64("task-id", task.ID), zap.Int64("step", task.Step), zap.String("state", task.State)) + return + } + if exist, err := m.taskTable.HasSubtasksInStates(m.id, task.ID, task.Step, + proto.TaskStatePending, proto.TaskStateRevertPending, + // for the case that the tidb is restarted when the subtask is running. + proto.TaskStateRunning, proto.TaskStateReverting); err != nil { + m.logErr(err) + return + } else if !exist { + continue + } + switch task.State { + case proto.TaskStateRunning: + runCtx, runCancel := context.WithCancelCause(ctx) + m.registerCancelFunc(task.ID, runCancel) + err = scheduler.Run(runCtx, task) + runCancel(nil) + case proto.TaskStatePausing: + err = scheduler.Pause(ctx, task) + case proto.TaskStateReverting: + err = scheduler.Rollback(ctx, task) + } + if err != nil { + logutil.Logger(m.logCtx).Error("failed to handle task", zap.Error(err)) + } + } +} + +// addHandlingTask adds a task to the handling task set. +func (m *Manager) addHandlingTask(id int64) { + m.mu.Lock() + defer m.mu.Unlock() + m.mu.handlingTasks[id] = nil +} + +// registerCancelFunc registers a cancel function for a task. +func (m *Manager) registerCancelFunc(id int64, cancel context.CancelCauseFunc) { + m.mu.Lock() + defer m.mu.Unlock() + m.mu.handlingTasks[id] = cancel +} + +// removeHandlingTask removes a task from the handling task set. +func (m *Manager) removeHandlingTask(id int64) { + m.mu.Lock() + defer m.mu.Unlock() + delete(m.mu.handlingTasks, id) +} + +func (m *Manager) logErr(err error) { + logutil.Logger(m.logCtx).Error("task manager error", zap.Error(err), zap.Stack("stack")) +} + +func (m *Manager) logErrAndPersist(err error, taskID int64) { + m.logErr(err) + err1 := m.taskTable.UpdateErrorToSubtask(m.id, taskID, err) + if err1 != nil { + logutil.Logger(m.logCtx).Error("update to subtask failed", zap.Error(err1), zap.Stack("stack")) + } +} diff --git a/pkg/disttask/framework/scheduler/manager_test.go b/pkg/disttask/framework/scheduler/manager_test.go new file mode 100644 index 0000000000000..7c87843f315dd --- /dev/null +++ b/pkg/disttask/framework/scheduler/manager_test.go @@ -0,0 +1,248 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package scheduler + +import ( + "context" + "errors" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/spool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +var unfinishedSubtaskStates = []interface{}{ + proto.TaskStatePending, proto.TaskStateRevertPending, + proto.TaskStateRunning, proto.TaskStateReverting, +} + +func getPoolRunFn() (*sync.WaitGroup, func(f func()) error) { + wg := &sync.WaitGroup{} + return wg, func(f func()) error { + wg.Add(1) + go func() { + defer wg.Done() + f() + }() + return nil + } +} + +func TestManageTask(t *testing.T) { + b := NewManagerBuilder() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockTaskTable := mock.NewMockTaskTable(ctrl) + m, err := b.BuildManager(context.Background(), "test", mockTaskTable) + require.NoError(t, err) + tasks := []*proto.Task{{ID: 1}, {ID: 2}} + newTasks := m.filterAlreadyHandlingTasks(tasks) + require.Equal(t, tasks, newTasks) + + m.addHandlingTask(1) + tasks = []*proto.Task{{ID: 1}, {ID: 2}} + newTasks = m.filterAlreadyHandlingTasks(tasks) + require.Equal(t, []*proto.Task{{ID: 2}}, newTasks) + + m.addHandlingTask(2) + tasks = []*proto.Task{{ID: 1}, {ID: 2}} + newTasks = m.filterAlreadyHandlingTasks(tasks) + require.Equal(t, []*proto.Task{}, newTasks) + + m.removeHandlingTask(1) + tasks = []*proto.Task{{ID: 1}, {ID: 2}} + newTasks = m.filterAlreadyHandlingTasks(tasks) + require.Equal(t, []*proto.Task{{ID: 1}}, newTasks) + + ctx1, cancel1 := context.WithCancelCause(context.Background()) + m.registerCancelFunc(2, cancel1) + m.cancelAllRunningTasks() + require.Equal(t, context.Canceled, ctx1.Err()) + + // test cancel. + m.addHandlingTask(1) + ctx2, cancel2 := context.WithCancelCause(context.Background()) + m.registerCancelFunc(1, cancel2) + ctx3, cancel3 := context.WithCancelCause(context.Background()) + m.registerCancelFunc(2, cancel3) + m.onCanceledTasks(context.Background(), []*proto.Task{{ID: 1}}) + require.Equal(t, context.Canceled, ctx2.Err()) + require.NoError(t, ctx3.Err()) + + // test pause. + m.addHandlingTask(3) + ctx4, cancel4 := context.WithCancelCause(context.Background()) + m.registerCancelFunc(1, cancel4) + mockTaskTable.EXPECT().PauseSubtasks("test", int64(1)).Return(nil) + m.onPausingTasks([]*proto.Task{{ID: 1}}) + require.Equal(t, context.Canceled, ctx4.Err()) +} + +func TestOnRunnableTasks(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockTaskTable := mock.NewMockTaskTable(ctrl) + mockInternalScheduler := mock.NewMockScheduler(ctrl) + mockPool := mock.NewMockPool(ctrl) + + b := NewManagerBuilder() + b.setPoolFactory(func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { + return mockPool, nil + }) + id := "test" + taskID := int64(1) + task := &proto.Task{ID: taskID, State: proto.TaskStateRunning, Step: proto.StepOne, Type: "type"} + + m, err := b.BuildManager(context.Background(), id, mockTaskTable) + require.NoError(t, err) + + // no task + m.onRunnableTasks(context.Background(), nil) + + RegisterTaskType("type", + func(ctx context.Context, id string, task *proto.Task, taskTable TaskTable) Scheduler { + return mockInternalScheduler + }) + + // get subtask failed + mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, + unfinishedSubtaskStates...). + Return(false, errors.New("get subtask failed")) + mockInternalScheduler.EXPECT().Close() + m.onRunnableTasks(context.Background(), []*proto.Task{task}) + + // no subtask + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, + unfinishedSubtaskStates...).Return(false, nil) + m.onRunnableTasks(context.Background(), []*proto.Task{task}) + + // pool error + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, + unfinishedSubtaskStates...).Return(true, nil) + mockPool.EXPECT().Run(gomock.Any()).Return(errors.New("pool error")) + m.onRunnableTasks(context.Background(), []*proto.Task{task}) + + // StepOne succeed + wg, runFn := getPoolRunFn() + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, + unfinishedSubtaskStates...).Return(true, nil) + mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task, nil) + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepOne, + unfinishedSubtaskStates...).Return(true, nil) + mockInternalScheduler.EXPECT().Run(gomock.Any(), task).Return(nil) + + // StepTwo failed + task1 := &proto.Task{ID: taskID, State: proto.TaskStateRunning, Step: proto.StepTwo} + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task1, nil) + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepTwo, + unfinishedSubtaskStates...).Return(true, nil) + mockInternalScheduler.EXPECT().Run(gomock.Any(), task1).Return(errors.New("run err")) + + task2 := &proto.Task{ID: taskID, State: proto.TaskStateReverting, Step: proto.StepTwo} + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task2, nil) + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID, proto.StepTwo, + unfinishedSubtaskStates...).Return(true, nil) + mockInternalScheduler.EXPECT().Rollback(gomock.Any(), task2).Return(nil) + + task3 := &proto.Task{ID: taskID, State: proto.TaskStateReverted, Step: proto.StepTwo} + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID).Return(task3, nil) + + m.onRunnableTasks(context.Background(), []*proto.Task{task}) + + wg.Wait() +} + +func TestManager(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockTaskTable := mock.NewMockTaskTable(ctrl) + mockInternalScheduler := mock.NewMockScheduler(ctrl) + mockPool := mock.NewMockPool(ctrl) + b := NewManagerBuilder() + b.setPoolFactory(func(name string, size int32, component util.Component, options ...spool.Option) (Pool, error) { + return mockPool, nil + }) + RegisterTaskType("type", + func(ctx context.Context, id string, task *proto.Task, taskTable TaskTable) Scheduler { + return mockInternalScheduler + }) + id := "test" + taskID1 := int64(1) + taskID2 := int64(2) + taskID3 := int64(3) + task1 := &proto.Task{ID: taskID1, State: proto.TaskStateRunning, Step: proto.StepOne, Type: "type"} + task2 := &proto.Task{ID: taskID2, State: proto.TaskStateReverting, Step: proto.StepOne, Type: "type"} + task3 := &proto.Task{ID: taskID3, State: proto.TaskStatePausing, Step: proto.StepOne, Type: "type"} + + mockTaskTable.EXPECT().StartManager("test", "").Return(nil).Times(1) + mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStateRunning, proto.TaskStateReverting). + Return([]*proto.Task{task1, task2}, nil).AnyTimes() + mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStateReverting). + Return([]*proto.Task{task2}, nil).AnyTimes() + mockTaskTable.EXPECT().GetGlobalTasksInStates(proto.TaskStatePausing). + Return([]*proto.Task{task3}, nil).AnyTimes() + mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) + // task1 + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, + unfinishedSubtaskStates...). + Return(true, nil) + wg, runFn := getPoolRunFn() + mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID1).Return(task1, nil).AnyTimes() + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, + unfinishedSubtaskStates...). + Return(true, nil) + mockInternalScheduler.EXPECT().Run(gomock.Any(), task1).Return(nil) + + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID1, proto.StepOne, + unfinishedSubtaskStates...). + Return(false, nil).AnyTimes() + mockInternalScheduler.EXPECT().Close() + // task2 + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, + unfinishedSubtaskStates...). + Return(true, nil) + mockPool.EXPECT().Run(gomock.Any()).DoAndReturn(runFn) + mockTaskTable.EXPECT().GetGlobalTaskByID(taskID2).Return(task2, nil).AnyTimes() + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, + unfinishedSubtaskStates...). + Return(true, nil) + mockInternalScheduler.EXPECT().Init(gomock.Any()).Return(nil) + mockInternalScheduler.EXPECT().Rollback(gomock.Any(), task2).Return(nil) + mockTaskTable.EXPECT().HasSubtasksInStates(id, taskID2, proto.StepOne, + unfinishedSubtaskStates...). + Return(false, nil).AnyTimes() + mockInternalScheduler.EXPECT().Close() + // task3 + mockTaskTable.EXPECT().PauseSubtasks(id, taskID3).Return(nil).AnyTimes() + + // for scheduler pool + mockPool.EXPECT().ReleaseAndWait().Do(func() { + wg.Wait() + }) + m, err := b.BuildManager(context.Background(), id, mockTaskTable) + require.NoError(t, err) + require.NoError(t, m.Start()) + time.Sleep(5 * time.Second) + m.Stop() +} diff --git a/disttask/framework/scheduler/register.go b/pkg/disttask/framework/scheduler/register.go similarity index 93% rename from disttask/framework/scheduler/register.go rename to pkg/disttask/framework/scheduler/register.go index 9d55455f94a2b..159b38b06510c 100644 --- a/disttask/framework/scheduler/register.go +++ b/pkg/disttask/framework/scheduler/register.go @@ -17,8 +17,8 @@ package scheduler import ( "context" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler/execute" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" ) type taskTypeOptions struct { diff --git a/disttask/framework/scheduler/register_test.go b/pkg/disttask/framework/scheduler/register_test.go similarity index 95% rename from disttask/framework/scheduler/register_test.go rename to pkg/disttask/framework/scheduler/register_test.go index 5111a936a7a76..33fd626bf6440 100644 --- a/disttask/framework/scheduler/register_test.go +++ b/pkg/disttask/framework/scheduler/register_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - "github.com/pingcap/tidb/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" "github.com/stretchr/testify/require" ) diff --git a/pkg/disttask/framework/scheduler/scheduler.go b/pkg/disttask/framework/scheduler/scheduler.go new file mode 100644 index 0000000000000..592c785220622 --- /dev/null +++ b/pkg/disttask/framework/scheduler/scheduler.go @@ -0,0 +1,609 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package scheduler + +import ( + "context" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/backoff" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +const ( + // DefaultCheckSubtaskCanceledInterval is the default check interval for cancel cancelled subtasks. + DefaultCheckSubtaskCanceledInterval = 2 * time.Second +) + +var ( + // ErrCancelSubtask is the cancel cause when cancelling subtasks. + ErrCancelSubtask = errors.New("cancel subtasks") + // ErrFinishSubtask is the cancel cause when scheduler successfully processed subtasks. + ErrFinishSubtask = errors.New("finish subtasks") + // ErrFinishRollback is the cancel cause when scheduler rollback successfully. + ErrFinishRollback = errors.New("finish rollback") + + // TestSyncChan is used to sync the test. + TestSyncChan = make(chan struct{}) +) + +// BaseScheduler is the base implementation of Scheduler. +type BaseScheduler struct { + // id, it's the same as server id now, i.e. host:port. + id string + taskID int64 + taskTable TaskTable + logCtx context.Context + Extension + + mu struct { + sync.RWMutex + err error + // handled indicates whether the error has been updated to one of the subtask. + handled bool + // runtimeCancel is used to cancel the Run/Rollback when error occurs. + runtimeCancel context.CancelCauseFunc + } +} + +// NewBaseScheduler creates a new BaseScheduler. +func NewBaseScheduler(_ context.Context, id string, taskID int64, taskTable TaskTable) *BaseScheduler { + schedulerImpl := &BaseScheduler{ + id: id, + taskID: taskID, + taskTable: taskTable, + logCtx: logutil.WithFields(context.Background(), zap.Int64("task-id", taskID)), + } + return schedulerImpl +} + +func (s *BaseScheduler) startCancelCheck(ctx context.Context, wg *sync.WaitGroup, cancelFn context.CancelCauseFunc) { + wg.Add(1) + go func() { + defer wg.Done() + ticker := time.NewTicker(DefaultCheckSubtaskCanceledInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + logutil.Logger(s.logCtx).Info("scheduler exits", zap.Error(ctx.Err())) + return + case <-ticker.C: + canceled, err := s.taskTable.IsSchedulerCanceled(s.id, s.taskID) + if err != nil { + continue + } + if canceled { + logutil.Logger(s.logCtx).Info("scheduler canceled") + if cancelFn != nil { + // subtask transferred to other tidb, don't mark subtask as canceled. + // Should not change the subtask's state. + cancelFn(nil) + } + } + } + } + }() +} + +// Init implements the Scheduler interface. +func (*BaseScheduler) Init(_ context.Context) error { + return nil +} + +// Run runs the scheduler task. +func (s *BaseScheduler) Run(ctx context.Context, task *proto.Task) (err error) { + defer func() { + if r := recover(); r != nil { + logutil.Logger(ctx).Error("BaseScheduler panicked", zap.Any("recover", r), zap.Stack("stack")) + err4Panic := errors.Errorf("%v", r) + err1 := s.updateErrorToSubtask(ctx, task.ID, err4Panic) + if err == nil { + err = err1 + } + } + }() + err = s.run(ctx, task) + if s.mu.handled { + return err + } + if err == nil { + return nil + } + return s.updateErrorToSubtask(ctx, task.ID, err) +} + +func (s *BaseScheduler) run(ctx context.Context, task *proto.Task) error { + if ctx.Err() != nil { + s.onError(ctx.Err()) + return s.getError() + } + runCtx, runCancel := context.WithCancelCause(ctx) + defer runCancel(ErrFinishSubtask) + s.registerCancelFunc(runCancel) + s.resetError() + logutil.Logger(s.logCtx).Info("scheduler run a step", zap.Any("step", task.Step), zap.Any("concurrency", task.Concurrency)) + + summary, cleanup, err := runSummaryCollectLoop(ctx, task, s.taskTable) + if err != nil { + s.onError(err) + return s.getError() + } + defer cleanup() + + executor, err := s.GetSubtaskExecutor(ctx, task, summary) + if err != nil { + s.onError(err) + return s.getError() + } + + failpoint.Inject("mockExecSubtaskInitEnvErr", func() { + failpoint.Return(errors.New("mockExecSubtaskInitEnvErr")) + }) + if err := executor.Init(runCtx); err != nil { + s.onError(err) + return s.getError() + } + + var wg sync.WaitGroup + cancelCtx, checkCancel := context.WithCancel(ctx) + s.startCancelCheck(cancelCtx, &wg, runCancel) + + defer func() { + err := executor.Cleanup(runCtx) + if err != nil { + logutil.Logger(s.logCtx).Error("cleanup subtask exec env failed", zap.Error(err)) + } + checkCancel() + wg.Wait() + }() + + subtasks, err := s.taskTable.GetSubtasksInStates(s.id, task.ID, task.Step, + proto.TaskStatePending, proto.TaskStateRunning) + if err != nil { + s.onError(err) + if common.IsRetryableError(err) { + logutil.Logger(s.logCtx).Warn("met retryable error", zap.Error(err)) + return nil + } + return s.getError() + } + for _, subtask := range subtasks { + metrics.IncDistTaskSubTaskCnt(subtask) + metrics.StartDistTaskSubTask(subtask) + } + + for { + // check if any error occurs. + if err := s.getError(); err != nil { + break + } + + subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, + proto.TaskStatePending, proto.TaskStateRunning) + if err != nil { + logutil.Logger(s.logCtx).Warn("GetFirstSubtaskInStates meets error", zap.Error(err)) + continue + } + if subtask == nil { + newTask, err := s.taskTable.GetGlobalTaskByID(task.ID) + if err != nil { + logutil.Logger(s.logCtx).Warn("GetGlobalTaskByID meets error", zap.Error(err)) + continue + } + // When the task move to next step or task state changes, the scheduler should exit. + if newTask.Step != task.Step || newTask.State != task.State { + break + } + continue + } + + if subtask.State == proto.TaskStateRunning { + if !s.IsIdempotent(subtask) { + logutil.Logger(s.logCtx).Info("subtask in running state and is not idempotent, fail it", + zap.Int64("subtask-id", subtask.ID)) + subtaskErr := errors.New("subtask in running state and is not idempotent") + s.onError(subtaskErr) + s.updateSubtaskStateAndError(subtask, proto.TaskStateFailed, subtaskErr) + s.markErrorHandled() + break + } + } else { + // subtask.State == proto.TaskStatePending + s.startSubtaskAndUpdateState(ctx, subtask) + if err := s.getError(); err != nil { + logutil.Logger(s.logCtx).Warn("startSubtaskAndUpdateState meets error", zap.Error(err)) + continue + } + } + + failpoint.Inject("mockCleanScheduler", func() { + v, ok := testContexts.Load(s.id) + if ok { + if v.(*TestContext).mockDown.Load() { + failpoint.Break() + } + } + }) + + s.runSubtask(runCtx, executor, subtask) + } + return s.getError() +} + +func (s *BaseScheduler) runSubtask(ctx context.Context, executor execute.SubtaskExecutor, subtask *proto.Subtask) { + err := executor.RunSubtask(ctx, subtask) + failpoint.Inject("MockRunSubtaskCancel", func(val failpoint.Value) { + if val.(bool) { + err = ErrCancelSubtask + } + }) + + failpoint.Inject("MockRunSubtaskContextCanceled", func(val failpoint.Value) { + if val.(bool) { + err = context.Canceled + } + }) + + if err != nil { + s.onError(err) + } + + finished := s.markSubTaskCanceledOrFailed(ctx, subtask) + if finished { + return + } + + failpoint.Inject("mockTiDBDown", func(val failpoint.Value) { + logutil.Logger(s.logCtx).Info("trigger mockTiDBDown") + if s.id == val.(string) || s.id == ":4001" || s.id == ":4002" { + v, ok := testContexts.Load(s.id) + if ok { + v.(*TestContext).TestSyncSubtaskRun <- struct{}{} + v.(*TestContext).mockDown.Store(true) + logutil.Logger(s.logCtx).Info("mockTiDBDown") + time.Sleep(2 * time.Second) + failpoint.Return() + } + } + }) + failpoint.Inject("mockTiDBDown2", func() { + if s.id == ":4003" && subtask.Step == proto.StepTwo { + v, ok := testContexts.Load(s.id) + if ok { + v.(*TestContext).TestSyncSubtaskRun <- struct{}{} + v.(*TestContext).mockDown.Store(true) + time.Sleep(2 * time.Second) + return + } + } + }) + + failpoint.Inject("mockTiDBPartitionThenResume", func(val failpoint.Value) { + if val.(bool) && (s.id == ":4000" || s.id == ":4001" || s.id == ":4002") { + _ = infosync.MockGlobalServerInfoManagerEntry.DeleteByID(s.id) + time.Sleep(20 * time.Second) + } + }) + + failpoint.Inject("MockExecutorRunErr", func(val failpoint.Value) { + if val.(bool) { + s.onError(errors.New("MockExecutorRunErr")) + } + }) + failpoint.Inject("MockExecutorRunCancel", func(val failpoint.Value) { + if taskID, ok := val.(int); ok { + mgr, err := storage.GetTaskManager() + if err != nil { + logutil.BgLogger().Error("get task manager failed", zap.Error(err)) + } else { + err = mgr.CancelGlobalTask(int64(taskID)) + if err != nil { + logutil.BgLogger().Error("cancel global task failed", zap.Error(err)) + } + } + } + }) + s.onSubtaskFinished(ctx, executor, subtask) +} + +func (s *BaseScheduler) onSubtaskFinished(ctx context.Context, executor execute.SubtaskExecutor, subtask *proto.Subtask) { + if err := s.getError(); err == nil { + if err = executor.OnFinished(ctx, subtask); err != nil { + s.onError(err) + } + } + failpoint.Inject("MockSubtaskFinishedCancel", func(val failpoint.Value) { + if val.(bool) { + s.onError(ErrCancelSubtask) + } + }) + + finished := s.markSubTaskCanceledOrFailed(ctx, subtask) + if finished { + return + } + + s.finishSubtaskAndUpdateState(ctx, subtask) + + finished = s.markSubTaskCanceledOrFailed(ctx, subtask) + if finished { + return + } + + failpoint.Inject("syncAfterSubtaskFinish", func() { + TestSyncChan <- struct{}{} + <-TestSyncChan + }) +} + +// Rollback rollbacks the scheduler task. +func (s *BaseScheduler) Rollback(ctx context.Context, task *proto.Task) error { + rollbackCtx, rollbackCancel := context.WithCancelCause(ctx) + defer rollbackCancel(ErrFinishRollback) + s.registerCancelFunc(rollbackCancel) + + s.resetError() + logutil.Logger(s.logCtx).Info("scheduler rollback a step", zap.Any("step", task.Step)) + + // We should cancel all subtasks before rolling back + for { + subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, + proto.TaskStatePending, proto.TaskStateRunning) + if err != nil { + s.onError(err) + return s.getError() + } + + if subtask == nil { + break + } + + s.updateSubtaskStateAndError(subtask, proto.TaskStateCanceled, nil) + if err = s.getError(); err != nil { + return err + } + } + + executor, err := s.GetSubtaskExecutor(ctx, task, nil) + if err != nil { + s.onError(err) + return s.getError() + } + subtask, err := s.taskTable.GetFirstSubtaskInStates(s.id, task.ID, task.Step, + proto.TaskStateRevertPending, proto.TaskStateReverting) + if err != nil { + s.onError(err) + return s.getError() + } + if subtask == nil { + logutil.BgLogger().Warn("scheduler rollback a step, but no subtask in revert_pending state", zap.Any("step", task.Step)) + return nil + } + if subtask.State == proto.TaskStateRevertPending { + s.updateSubtaskStateAndError(subtask, proto.TaskStateReverting, nil) + } + if err := s.getError(); err != nil { + return err + } + + // right now all impl of Rollback is empty, so we don't check idempotent here. + // will try to remove this rollback completely in the future. + err = executor.Rollback(rollbackCtx) + if err != nil { + s.updateSubtaskStateAndError(subtask, proto.TaskStateRevertFailed, nil) + s.onError(err) + } else { + s.updateSubtaskStateAndError(subtask, proto.TaskStateReverted, nil) + } + return s.getError() +} + +// Pause pause the scheduler task. +func (s *BaseScheduler) Pause(_ context.Context, task *proto.Task) error { + logutil.Logger(s.logCtx).Info("scheduler pause subtasks") + // pause all running subtasks. + if err := s.taskTable.PauseSubtasks(s.id, task.ID); err != nil { + s.onError(err) + return s.getError() + } + return nil +} + +// Close closes the scheduler when all the subtasks are complete. +func (*BaseScheduler) Close() { +} + +func runSummaryCollectLoop( + ctx context.Context, + task *proto.Task, + taskTable TaskTable, +) (summary *execute.Summary, cleanup func(), err error) { + taskMgr, ok := taskTable.(*storage.TaskManager) + if !ok { + return nil, func() {}, nil + } + opt, ok := taskTypes[task.Type] + if !ok { + return nil, func() {}, errors.Errorf("scheduler option for type %s not found", task.Type) + } + if opt.Summary != nil { + go opt.Summary.UpdateRowCountLoop(ctx, taskMgr) + return opt.Summary, func() { + opt.Summary.PersistRowCount(ctx, taskMgr) + }, nil + } + return nil, func() {}, nil +} + +func (s *BaseScheduler) registerCancelFunc(cancel context.CancelCauseFunc) { + s.mu.Lock() + defer s.mu.Unlock() + s.mu.runtimeCancel = cancel +} + +func (s *BaseScheduler) onError(err error) { + if err == nil { + return + } + err = errors.Trace(err) + logutil.Logger(s.logCtx).Error("onError", zap.Error(err)) + s.mu.Lock() + defer s.mu.Unlock() + + if s.mu.err == nil { + s.mu.err = err + logutil.Logger(s.logCtx).Error("scheduler error", zap.Error(err)) + } + + if s.mu.runtimeCancel != nil { + s.mu.runtimeCancel(err) + } +} + +func (s *BaseScheduler) markErrorHandled() { + s.mu.Lock() + defer s.mu.Unlock() + s.mu.handled = true +} + +func (s *BaseScheduler) getError() error { + s.mu.RLock() + defer s.mu.RUnlock() + return s.mu.err +} + +func (s *BaseScheduler) resetError() { + s.mu.Lock() + defer s.mu.Unlock() + s.mu.err = nil + s.mu.handled = false +} + +func (s *BaseScheduler) startSubtaskAndUpdateState(ctx context.Context, subtask *proto.Subtask) { + metrics.DecDistTaskSubTaskCnt(subtask) + metrics.EndDistTaskSubTask(subtask) + s.startSubtask(ctx, subtask.ID) + subtask.State = proto.TaskStateRunning + metrics.IncDistTaskSubTaskCnt(subtask) + metrics.StartDistTaskSubTask(subtask) +} + +func (s *BaseScheduler) updateSubtaskStateAndErrorImpl(subtaskID int64, state string, subTaskErr error) { + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + logger := logutil.Logger(s.logCtx) + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + ctx := context.Background() + err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, s.taskTable.UpdateSubtaskStateAndError(subtaskID, state, subTaskErr) + }, + ) + if err != nil { + s.onError(err) + } +} + +func (s *BaseScheduler) startSubtask(ctx context.Context, subtaskID int64) { + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + logger := logutil.Logger(s.logCtx) + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, s.taskTable.StartSubtask(subtaskID) + }, + ) + if err != nil { + s.onError(err) + } +} + +func (s *BaseScheduler) finishSubtask(ctx context.Context, subtask *proto.Subtask) { + logger := logutil.Logger(s.logCtx) + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, s.taskTable.FinishSubtask(subtask.ID, subtask.Meta) + }, + ) + if err != nil { + s.onError(err) + } +} + +func (s *BaseScheduler) updateSubtaskStateAndError(subtask *proto.Subtask, state string, subTaskErr error) { + metrics.DecDistTaskSubTaskCnt(subtask) + metrics.EndDistTaskSubTask(subtask) + s.updateSubtaskStateAndErrorImpl(subtask.ID, state, subTaskErr) + subtask.State = state + metrics.IncDistTaskSubTaskCnt(subtask) + if !subtask.IsFinished() { + metrics.StartDistTaskSubTask(subtask) + } +} + +func (s *BaseScheduler) finishSubtaskAndUpdateState(ctx context.Context, subtask *proto.Subtask) { + metrics.DecDistTaskSubTaskCnt(subtask) + metrics.EndDistTaskSubTask(subtask) + s.finishSubtask(ctx, subtask) + subtask.State = proto.TaskStateSucceed + metrics.IncDistTaskSubTaskCnt(subtask) +} + +// markSubTaskCanceledOrFailed check the error type and decide the subtasks' state. +// 1. Only cancel subtasks when meet ErrCancelSubtask. +// 2. Only fail subtasks when meet non retryable error. +// 3. When meet other errors, don't change subtasks' state. +func (s *BaseScheduler) markSubTaskCanceledOrFailed(ctx context.Context, subtask *proto.Subtask) bool { + if err := s.getError(); err != nil { + if ctx.Err() != nil && context.Cause(ctx) == ErrCancelSubtask { + logutil.Logger(s.logCtx).Warn("subtask canceled", zap.Error(err)) + s.updateSubtaskStateAndError(subtask, proto.TaskStateCanceled, nil) + } else if common.IsRetryableError(err) { + logutil.Logger(s.logCtx).Warn("met retryable error", zap.Error(err)) + } else if errors.Cause(err) != context.Canceled { + logutil.Logger(s.logCtx).Warn("subtask failed", zap.Error(err)) + s.updateSubtaskStateAndError(subtask, proto.TaskStateFailed, err) + } else { + logutil.Logger(s.logCtx).Info("met context canceled for gracefully shutdown", zap.Error(err)) + } + s.markErrorHandled() + return true + } + return false +} + +func (s *BaseScheduler) updateErrorToSubtask(ctx context.Context, taskID int64, err error) error { + logger := logutil.Logger(s.logCtx) + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + err1 := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, s.taskTable.UpdateErrorToSubtask(s.id, taskID, err) + }, + ) + return err1 +} diff --git a/disttask/framework/scheduler/scheduler_test.go b/pkg/disttask/framework/scheduler/scheduler_test.go similarity index 99% rename from disttask/framework/scheduler/scheduler_test.go rename to pkg/disttask/framework/scheduler/scheduler_test.go index 0f3707fee0618..9a591860ef04c 100644 --- a/disttask/framework/scheduler/scheduler_test.go +++ b/pkg/disttask/framework/scheduler/scheduler_test.go @@ -18,9 +18,9 @@ import ( "context" "testing" - "github.com/pingcap/tidb/disttask/framework/mock" - mockexecute "github.com/pingcap/tidb/disttask/framework/mock/execute" - "github.com/pingcap/tidb/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/mock" + mockexecute "github.com/pingcap/tidb/pkg/disttask/framework/mock/execute" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" "github.com/pkg/errors" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" diff --git a/pkg/disttask/framework/storage/BUILD.bazel b/pkg/disttask/framework/storage/BUILD.bazel new file mode 100644 index 0000000000000..5a3ff2519b80e --- /dev/null +++ b/pkg/disttask/framework/storage/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "storage", + srcs = [ + "task_table.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/framework/storage", + visibility = ["//visibility:public"], + deps = [ + "//pkg/disttask/framework/proto", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/util/chunk", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "storage_test", + timeout = "short", + srcs = ["table_test.go"], + flaky = True, + race = "on", + shard_count = 7, + deps = [ + ":storage", + "//pkg/disttask/framework/proto", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/disttask/framework/storage/table_test.go b/pkg/disttask/framework/storage/table_test.go new file mode 100644 index 0000000000000..0cb4c7fce2821 --- /dev/null +++ b/pkg/disttask/framework/storage/table_test.go @@ -0,0 +1,575 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package storage_test + +import ( + "context" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} + +func GetResourcePool(t *testing.T) *pools.ResourcePool { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + pool := pools.NewResourcePool(func() (pools.Resource, error) { + return tk.Session(), nil + }, 1, 1, time.Second) + return pool +} + +func GetTaskManager(t *testing.T, pool *pools.ResourcePool) *storage.TaskManager { + manager := storage.NewTaskManager(context.Background(), pool) + storage.SetTaskManager(manager) + manager, err := storage.GetTaskManager() + require.NoError(t, err) + return manager +} + +func TestGlobalTaskTable(t *testing.T) { + pool := GetResourcePool(t) + gm := GetTaskManager(t, pool) + defer pool.Close() + id, err := gm.AddNewGlobalTask("key1", "test", 4, []byte("test")) + require.NoError(t, err) + require.Equal(t, int64(1), id) + + task, err := gm.GetNewGlobalTask() + require.NoError(t, err) + require.Equal(t, int64(1), task.ID) + require.Equal(t, "key1", task.Key) + require.Equal(t, "test", task.Type) + require.Equal(t, proto.TaskStatePending, task.State) + require.Equal(t, uint64(4), task.Concurrency) + require.Equal(t, []byte("test"), task.Meta) + + task2, err := gm.GetGlobalTaskByID(1) + require.NoError(t, err) + require.Equal(t, task, task2) + + task3, err := gm.GetGlobalTasksInStates(proto.TaskStatePending) + require.NoError(t, err) + require.Len(t, task3, 1) + require.Equal(t, task, task3[0]) + + task4, err := gm.GetGlobalTasksInStates(proto.TaskStatePending, proto.TaskStateRunning) + require.NoError(t, err) + require.Len(t, task4, 1) + require.Equal(t, task, task4[0]) + require.GreaterOrEqual(t, task4[0].StateUpdateTime, task.StateUpdateTime) + + prevState := task.State + task.State = proto.TaskStateRunning + retryable, err := gm.UpdateGlobalTaskAndAddSubTasks(task, nil, prevState) + require.NoError(t, err) + require.Equal(t, true, retryable) + + task5, err := gm.GetGlobalTasksInStates(proto.TaskStateRunning) + require.NoError(t, err) + require.Len(t, task5, 1) + require.Equal(t, task.State, task5[0].State) + + task6, err := gm.GetGlobalTaskByKey("key1") + require.NoError(t, err) + require.Len(t, task5, 1) + require.Equal(t, task.State, task6.State) + + // test cannot insert task with dup key + _, err = gm.AddNewGlobalTask("key1", "test2", 4, []byte("test2")) + require.EqualError(t, err, "[kv:1062]Duplicate entry 'key1' for key 'tidb_global_task.task_key'") + + // test cancel global task + id, err = gm.AddNewGlobalTask("key2", "test", 4, []byte("test")) + require.NoError(t, err) + + cancelling, err := gm.IsGlobalTaskCancelling(id) + require.NoError(t, err) + require.False(t, cancelling) + + require.NoError(t, gm.CancelGlobalTask(id)) + cancelling, err = gm.IsGlobalTaskCancelling(id) + require.NoError(t, err) + require.True(t, cancelling) +} + +func TestSubTaskTable(t *testing.T) { + pool := GetResourcePool(t) + sm := GetTaskManager(t, pool) + defer pool.Close() + + err := sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false) + require.NoError(t, err) + + nilTask, err := sm.GetFirstSubtaskInStates("tidb2", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.Nil(t, nilTask) + + subtask, err := sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, proto.TaskTypeExample, subtask.Type) + require.Equal(t, int64(1), subtask.TaskID) + require.Equal(t, proto.TaskStatePending, subtask.State) + require.Equal(t, "tidb1", subtask.SchedulerID) + require.Equal(t, []byte("test"), subtask.Meta) + require.Zero(t, subtask.StartTime) + require.Zero(t, subtask.UpdateTime) + + subtask2, err := sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending, proto.TaskStateReverted) + require.NoError(t, err) + require.Equal(t, subtask, subtask2) + + ids, err := sm.GetSchedulerIDsByTaskID(1) + require.NoError(t, err) + require.Len(t, ids, 1) + require.Equal(t, "tidb1", ids[0]) + + ids, err = sm.GetSchedulerIDsByTaskID(3) + require.NoError(t, err) + require.Len(t, ids, 0) + + cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(1), cnt) + + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(1), cnt) + + ok, err := sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.True(t, ok) + + ts := time.Now() + time.Sleep(time.Second) + require.NoError(t, sm.StartSubtask(1)) + + subtask, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.Nil(t, subtask) + + subtask, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStateRunning) + require.NoError(t, err) + require.Equal(t, proto.TaskTypeExample, subtask.Type) + require.Equal(t, int64(1), subtask.TaskID) + require.Equal(t, proto.TaskStateRunning, subtask.State) + require.Equal(t, "tidb1", subtask.SchedulerID) + require.Equal(t, []byte("test"), subtask.Meta) + require.GreaterOrEqual(t, subtask.StartTime, ts) + require.GreaterOrEqual(t, subtask.UpdateTime, ts) + + // check update time after state change to cancel + time.Sleep(time.Second) + require.NoError(t, sm.UpdateSubtaskStateAndError(1, proto.TaskStateCancelling, nil)) + subtask2, err = sm.GetFirstSubtaskInStates("tidb1", 1, proto.StepInit, proto.TaskStateCancelling) + require.NoError(t, err) + require.Equal(t, proto.TaskStateCancelling, subtask2.State) + require.Greater(t, subtask2.UpdateTime, subtask.UpdateTime) + + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(0), cnt) + + ok, err = sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.False(t, ok) + + err = sm.DeleteSubtasksByTaskID(1) + require.NoError(t, err) + + ok, err = sm.HasSubtasksInStates("tidb1", 1, proto.StepInit, proto.TaskStatePending, proto.TaskStateRunning) + require.NoError(t, err) + require.False(t, ok) + + err = sm.AddNewSubTask(2, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, true) + require.NoError(t, err) + + cnt, err = sm.GetSubtaskInStatesCnt(2, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(1), cnt) + + subtasks, err := sm.GetSucceedSubtasksByStep(2, proto.StepInit) + require.NoError(t, err) + require.Len(t, subtasks, 0) + + err = sm.FinishSubtask(2, []byte{}) + require.NoError(t, err) + + subtasks, err = sm.GetSucceedSubtasksByStep(2, proto.StepInit) + require.NoError(t, err) + require.Len(t, subtasks, 1) + + rowCount, err := sm.GetSubtaskRowCount(2, proto.StepInit) + require.NoError(t, err) + require.Equal(t, int64(0), rowCount) + err = sm.UpdateSubtaskRowCount(2, 100) + require.NoError(t, err) + rowCount, err = sm.GetSubtaskRowCount(2, proto.StepInit) + require.NoError(t, err) + require.Equal(t, int64(100), rowCount) + + // test UpdateErrorToSubtask do update start/update time + err = sm.AddNewSubTask(3, proto.StepInit, "for_test", []byte("test"), proto.TaskTypeExample, false) + require.NoError(t, err) + require.NoError(t, sm.UpdateErrorToSubtask("for_test", 3, errors.New("fail"))) + subtask, err = sm.GetFirstSubtaskInStates("for_test", 3, proto.StepInit, proto.TaskStateFailed) + require.NoError(t, err) + require.Equal(t, proto.TaskStateFailed, subtask.State) + require.Greater(t, subtask.StartTime, ts) + require.Greater(t, subtask.UpdateTime, ts) + + // test FinishSubtask do update update time + err = sm.AddNewSubTask(4, proto.StepInit, "for_test1", []byte("test"), proto.TaskTypeExample, false) + require.NoError(t, err) + subtask, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.NoError(t, sm.StartSubtask(subtask.ID)) + subtask, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStateRunning) + require.NoError(t, err) + require.Greater(t, subtask.StartTime, ts) + require.Greater(t, subtask.UpdateTime, ts) + time.Sleep(time.Second) + require.NoError(t, sm.FinishSubtask(subtask.ID, []byte{})) + subtask2, err = sm.GetFirstSubtaskInStates("for_test1", 4, proto.StepInit, proto.TaskStateSucceed) + require.NoError(t, err) + require.Equal(t, subtask2.StartTime, subtask.StartTime) + require.Greater(t, subtask2.UpdateTime, subtask.UpdateTime) + + // test UpdateFailedSchedulerIDs and IsSchedulerCanceled + canceled, err := sm.IsSchedulerCanceled("for_test999", 4) + require.NoError(t, err) + require.True(t, canceled) + canceled, err = sm.IsSchedulerCanceled("for_test1", 4) + require.NoError(t, err) + require.False(t, canceled) + canceled, err = sm.IsSchedulerCanceled("for_test2", 4) + require.NoError(t, err) + require.True(t, canceled) + + require.NoError(t, sm.UpdateSubtaskStateAndError(4, proto.TaskStateRunning, nil)) + require.NoError(t, sm.UpdateFailedSchedulerIDs(4, map[string]string{ + "for_test1": "for_test999", + "for_test2": "for_test999", + })) + + canceled, err = sm.IsSchedulerCanceled("for_test1", 4) + require.NoError(t, err) + require.True(t, canceled) + canceled, err = sm.IsSchedulerCanceled("for_test2", 4) + require.NoError(t, err) + require.True(t, canceled) + canceled, err = sm.IsSchedulerCanceled("for_test999", 4) + require.NoError(t, err) + require.False(t, canceled) +} + +func TestBothGlobalAndSubTaskTable(t *testing.T) { + pool := GetResourcePool(t) + sm := GetTaskManager(t, pool) + defer pool.Close() + + id, err := sm.AddNewGlobalTask("key1", "test", 4, []byte("test")) + require.NoError(t, err) + require.Equal(t, int64(1), id) + + task, err := sm.GetNewGlobalTask() + require.NoError(t, err) + require.Equal(t, proto.TaskStatePending, task.State) + + // isSubTaskRevert: false + prevState := task.State + task.State = proto.TaskStateRunning + subTasks := []*proto.Subtask{ + { + Step: proto.StepInit, + Type: proto.TaskTypeExample, + SchedulerID: "instance1", + Meta: []byte("m1"), + }, + { + Step: proto.StepInit, + Type: proto.TaskTypeExample, + SchedulerID: "instance2", + Meta: []byte("m2"), + }, + } + retryable, err := sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) + require.NoError(t, err) + require.Equal(t, true, retryable) + + task, err = sm.GetGlobalTaskByID(1) + require.NoError(t, err) + require.Equal(t, proto.TaskStateRunning, task.State) + + subtask1, err := sm.GetFirstSubtaskInStates("instance1", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(1), subtask1.ID) + require.Equal(t, proto.TaskTypeExample, subtask1.Type) + require.Equal(t, []byte("m1"), subtask1.Meta) + + subtask2, err := sm.GetFirstSubtaskInStates("instance2", 1, proto.StepInit, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(2), subtask2.ID) + require.Equal(t, proto.TaskTypeExample, subtask2.Type) + require.Equal(t, []byte("m2"), subtask2.Meta) + + cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(2), cnt) + + // isSubTaskRevert: true + prevState = task.State + task.State = proto.TaskStateReverting + subTasks = []*proto.Subtask{ + { + Step: proto.StepInit, + Type: proto.TaskTypeExample, + SchedulerID: "instance3", + Meta: []byte("m3"), + }, + { + Step: proto.StepInit, + Type: proto.TaskTypeExample, + SchedulerID: "instance4", + Meta: []byte("m4"), + }, + } + retryable, err = sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) + require.NoError(t, err) + require.Equal(t, true, retryable) + + task, err = sm.GetGlobalTaskByID(1) + require.NoError(t, err) + require.Equal(t, proto.TaskStateReverting, task.State) + + subtask1, err = sm.GetFirstSubtaskInStates("instance3", 1, proto.StepInit, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(3), subtask1.ID) + require.Equal(t, proto.TaskTypeExample, subtask1.Type) + require.Equal(t, []byte("m3"), subtask1.Meta) + + subtask2, err = sm.GetFirstSubtaskInStates("instance4", 1, proto.StepInit, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(4), subtask2.ID) + require.Equal(t, proto.TaskTypeExample, subtask2.Type) + require.Equal(t, []byte("m4"), subtask2.Meta) + + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(2), cnt) + + // test transactional + require.NoError(t, sm.DeleteSubtasksByTaskID(1)) + failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/storage/MockUpdateTaskErr", "1*return(true)") + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/storage/MockUpdateTaskErr")) + }() + prevState = task.State + task.State = proto.TaskStateFailed + retryable, err = sm.UpdateGlobalTaskAndAddSubTasks(task, subTasks, prevState) + require.EqualError(t, err, "updateTaskErr") + require.Equal(t, true, retryable) + + task, err = sm.GetGlobalTaskByID(1) + require.NoError(t, err) + require.Equal(t, proto.TaskStateReverting, task.State) + + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStateRevertPending) + require.NoError(t, err) + require.Equal(t, int64(0), cnt) +} + +func TestDistFrameworkMeta(t *testing.T) { + pool := GetResourcePool(t) + sm := GetTaskManager(t, pool) + defer pool.Close() + + require.NoError(t, sm.StartManager(":4000", "background")) + require.NoError(t, sm.StartManager(":4001", "")) + require.NoError(t, sm.StartManager(":4002", "background")) + nodes, err := sm.GetNodesByRole("background") + require.NoError(t, err) + require.Equal(t, map[string]bool{ + ":4000": true, + ":4002": true, + }, nodes) + + nodes, err = sm.GetNodesByRole("") + require.NoError(t, err) + require.Equal(t, map[string]bool{ + ":4001": true, + }, nodes) +} + +func TestSubtaskHistoryTable(t *testing.T) { + pool := GetResourcePool(t) + sm := GetTaskManager(t, pool) + defer pool.Close() + + const ( + taskID = 1 + taskID2 = 2 + subTask1 = 1 + subTask2 = 2 + subTask3 = 3 + subTask4 = 4 // taskID2 + tidb1 = "tidb1" + tidb2 = "tidb2" + tidb3 = "tidb3" + meta = "test" + finishedMeta = "finished" + ) + + require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb1, []byte(meta), proto.TaskTypeExample, false)) + require.NoError(t, sm.FinishSubtask(subTask1, []byte(finishedMeta))) + require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb2, []byte(meta), proto.TaskTypeExample, false)) + require.NoError(t, sm.UpdateSubtaskStateAndError(subTask2, proto.TaskStateCanceled, nil)) + require.NoError(t, sm.AddNewSubTask(taskID, proto.StepInit, tidb3, []byte(meta), proto.TaskTypeExample, false)) + require.NoError(t, sm.UpdateSubtaskStateAndError(subTask3, proto.TaskStateFailed, nil)) + + subTasks, err := storage.GetSubtasksByTaskIDForTest(sm, taskID) + require.NoError(t, err) + require.Len(t, subTasks, 3) + historySubTasksCnt, err := storage.GetSubtasksFromHistoryForTest(sm) + require.NoError(t, err) + require.Equal(t, 0, historySubTasksCnt) + subTasks, err = sm.GetSubtasksForImportInto(taskID, proto.StepInit) + require.NoError(t, err) + require.Len(t, subTasks, 3) + + // test TransferSubTasks2History + require.NoError(t, sm.TransferSubTasks2History(taskID)) + + subTasks, err = storage.GetSubtasksByTaskIDForTest(sm, taskID) + require.NoError(t, err) + require.Len(t, subTasks, 0) + historySubTasksCnt, err = storage.GetSubtasksFromHistoryForTest(sm) + require.NoError(t, err) + require.Equal(t, 3, historySubTasksCnt) + subTasks, err = sm.GetSubtasksForImportInto(taskID, proto.StepInit) + require.NoError(t, err) + require.Len(t, subTasks, 3) + + // test GC history table. + failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/storage/subtaskHistoryKeepSeconds", "return(1)") + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/storage/subtaskHistoryKeepSeconds")) + }() + time.Sleep(2 * time.Second) + + require.NoError(t, sm.AddNewSubTask(taskID2, proto.StepInit, tidb1, []byte(meta), proto.TaskTypeExample, false)) + require.NoError(t, sm.UpdateSubtaskStateAndError(subTask4, proto.TaskStateFailed, nil)) + require.NoError(t, sm.TransferSubTasks2History(taskID2)) + + require.NoError(t, sm.GCSubtasks()) + + historySubTasksCnt, err = storage.GetSubtasksFromHistoryForTest(sm) + require.NoError(t, err) + require.Equal(t, 1, historySubTasksCnt) +} + +func TestTaskHistoryTable(t *testing.T) { + pool := GetResourcePool(t) + gm := GetTaskManager(t, pool) + defer pool.Close() + + _, err := gm.AddNewGlobalTask("1", proto.TaskTypeExample, 1, nil) + require.NoError(t, err) + taskID, err := gm.AddNewGlobalTask("2", proto.TaskTypeExample, 1, nil) + require.NoError(t, err) + + tasks, err := gm.GetGlobalTasksInStates(proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, 2, len(tasks)) + + require.NoError(t, gm.TransferTasks2History(tasks)) + + tasks, err = gm.GetGlobalTasksInStates(proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, 0, len(tasks)) + num, err := storage.GetTasksFromHistoryForTest(gm) + require.NoError(t, err) + require.Equal(t, 2, num) + + task, err := gm.GetTaskByIDWithHistory(taskID) + require.NoError(t, err) + require.NotNil(t, task) + + task, err = gm.GetGlobalTaskByKeyWithHistory("1") + require.NoError(t, err) + require.NotNil(t, task) + + // task with fail transfer + _, err = gm.AddNewGlobalTask("3", proto.TaskTypeExample, 1, nil) + require.NoError(t, err) + tasks, err = gm.GetGlobalTasksInStates(proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, 1, len(tasks)) + tasks[0].Error = errors.New("mock err") + require.NoError(t, gm.TransferTasks2History(tasks)) + num, err = storage.GetTasksFromHistoryForTest(gm) + require.NoError(t, err) + require.Equal(t, 3, num) +} + +func TestPauseAndResume(t *testing.T) { + pool := GetResourcePool(t) + sm := GetTaskManager(t, pool) + defer pool.Close() + require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) + require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) + require.NoError(t, sm.AddNewSubTask(1, proto.StepInit, "tidb1", []byte("test"), proto.TaskTypeExample, false)) + // 1.1 pause all subtasks. + require.NoError(t, sm.PauseSubtasks("tidb1", 1)) + cnt, err := sm.GetSubtaskInStatesCnt(1, proto.TaskStatePaused) + require.NoError(t, err) + require.Equal(t, int64(3), cnt) + // 1.2 resume all subtasks. + require.NoError(t, sm.ResumeSubtasks(1)) + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(3), cnt) + + // 2.1 pause 2 subtasks. + sm.UpdateSubtaskStateAndError(1, proto.TaskStateSucceed, nil) + require.NoError(t, sm.PauseSubtasks("tidb1", 1)) + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePaused) + require.NoError(t, err) + require.Equal(t, int64(2), cnt) + // 2.2 resume 2 subtasks. + require.NoError(t, sm.ResumeSubtasks(1)) + cnt, err = sm.GetSubtaskInStatesCnt(1, proto.TaskStatePending) + require.NoError(t, err) + require.Equal(t, int64(2), cnt) +} diff --git a/disttask/framework/storage/task_table.go b/pkg/disttask/framework/storage/task_table.go similarity index 98% rename from disttask/framework/storage/task_table.go rename to pkg/disttask/framework/storage/task_table.go index 5faa2583d1619..b60d130de8487 100644 --- a/disttask/framework/storage/task_table.go +++ b/pkg/disttask/framework/storage/task_table.go @@ -25,14 +25,14 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" ) diff --git a/pkg/disttask/framework/storage/util.go b/pkg/disttask/framework/storage/util.go new file mode 100644 index 0000000000000..cdd181950f5cf --- /dev/null +++ b/pkg/disttask/framework/storage/util.go @@ -0,0 +1,64 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package storage + +import "github.com/pingcap/tidb/pkg/disttask/framework/proto" + +// GetSubtasksFromHistoryForTest gets subtasks from history table for test. +func GetSubtasksFromHistoryForTest(stm *TaskManager) (int, error) { + rs, err := stm.executeSQLWithNewSession(stm.ctx, + "select * from mysql.tidb_background_subtask_history") + if err != nil { + return 0, err + } + return len(rs), nil +} + +// GetSubtasksFromHistoryByTaskIDForTest gets subtasks by taskID from history table for test. +func GetSubtasksFromHistoryByTaskIDForTest(stm *TaskManager, taskID int64) (int, error) { + rs, err := stm.executeSQLWithNewSession(stm.ctx, + "select * from mysql.tidb_background_subtask_history where task_key = %?", taskID) + if err != nil { + return 0, err + } + return len(rs), nil +} + +// GetSubtasksByTaskIDForTest gets subtasks by taskID for test. +func GetSubtasksByTaskIDForTest(stm *TaskManager, taskID int64) ([]*proto.Subtask, error) { + rs, err := stm.executeSQLWithNewSession(stm.ctx, + "select * from mysql.tidb_background_subtask where task_key = %?", taskID) + if err != nil { + return nil, err + } + if len(rs) == 0 { + return nil, nil + } + subtasks := make([]*proto.Subtask, 0, len(rs)) + for _, r := range rs { + subtasks = append(subtasks, row2SubTask(r)) + } + return subtasks, nil +} + +// GetTasksFromHistoryForTest gets tasks from history table for test. +func GetTasksFromHistoryForTest(stm *TaskManager) (int, error) { + rs, err := stm.executeSQLWithNewSession(stm.ctx, + "select * from mysql.tidb_global_task_history") + if err != nil { + return 0, err + } + return len(rs), nil +} diff --git a/pkg/disttask/importinto/BUILD.bazel b/pkg/disttask/importinto/BUILD.bazel new file mode 100644 index 0000000000000..88e9d12db7f35 --- /dev/null +++ b/pkg/disttask/importinto/BUILD.bazel @@ -0,0 +1,129 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "importinto", + srcs = [ + "clean_s3.go", + "dispatcher.go", + "encode_and_sort_operator.go", + "job.go", + "metrics.go", + "planner.go", + "proto.go", + "scheduler.go", + "subtask_executor.go", + "wrapper.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/importinto", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/backend", + "//br/pkg/lightning/backend/external", + "//br/pkg/lightning/backend/kv", + "//br/pkg/lightning/backend/local", + "//br/pkg/lightning/checkpoints", + "//br/pkg/lightning/common", + "//br/pkg/lightning/config", + "//br/pkg/lightning/log", + "//br/pkg/lightning/metric", + "//br/pkg/lightning/mydump", + "//br/pkg/lightning/verification", + "//br/pkg/storage", + "//br/pkg/utils", + "//pkg/config", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/handle", + "//pkg/disttask/framework/planner", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler", + "//pkg/disttask/framework/scheduler/execute", + "//pkg/disttask/framework/storage", + "//pkg/disttask/operator", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor/asyncloaddata", + "//pkg/executor/importer", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/resourcemanager/pool/workerpool", + "//pkg/resourcemanager/util", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table/tables", + "//pkg/util", + "//pkg/util/backoff", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/etcd", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/promutil", + "//pkg/util/size", + "//pkg/util/sqlexec", + "@com_github_docker_go_units//:go-units", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "importinto_test", + timeout = "short", + srcs = [ + "dispatcher_test.go", + "dispatcher_testkit_test.go", + "encode_and_sort_operator_test.go", + "job_testkit_test.go", + "metrics_test.go", + "planner_test.go", + "subtask_executor_test.go", + "wrapper_test.go", + ], + embed = [":importinto"], + flaky = True, + race = "on", + shard_count = 14, + deps = [ + "//br/pkg/lightning/backend", + "//br/pkg/lightning/backend/external", + "//br/pkg/lightning/checkpoints", + "//br/pkg/lightning/mydump", + "//br/pkg/lightning/verification", + "//pkg/ddl", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/planner", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/disttask/importinto/mock", + "//pkg/disttask/operator", + "//pkg/domain/infosync", + "//pkg/executor/importer", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/sqlexec", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_stretchr_testify//require", + "@com_github_stretchr_testify//suite", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_mock//gomock", + "@org_uber_go_zap//:zap", + ], +) diff --git a/disttask/importinto/clean_s3.go b/pkg/disttask/importinto/clean_s3.go similarity index 93% rename from disttask/importinto/clean_s3.go rename to pkg/disttask/importinto/clean_s3.go index d4c05cf893bae..5ae75137c46e7 100644 --- a/disttask/importinto/clean_s3.go +++ b/pkg/disttask/importinto/clean_s3.go @@ -21,9 +21,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/lightning/log" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/disttask/importinto/dispatcher.go b/pkg/disttask/importinto/dispatcher.go new file mode 100644 index 0000000000000..a89ea8ef5cd1f --- /dev/null +++ b/pkg/disttask/importinto/dispatcher.go @@ -0,0 +1,757 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/json" + "strconv" + "strings" + "sync" + "time" + + dmysql "github.com/go-sql-driver/mysql" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/br/pkg/lightning/config" + "github.com/pingcap/tidb/br/pkg/lightning/metric" + "github.com/pingcap/tidb/br/pkg/utils" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/backoff" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +const ( + registerTaskTTL = 10 * time.Minute + refreshTaskTTLInterval = 3 * time.Minute + registerTimeout = 5 * time.Second +) + +// NewTaskRegisterWithTTL is the ctor for TaskRegister. +// It is exported for testing. +var NewTaskRegisterWithTTL = utils.NewTaskRegisterWithTTL + +type taskInfo struct { + taskID int64 + + // operation on taskInfo is run inside detect-task goroutine, so no need to synchronize. + lastRegisterTime time.Time + + // initialized lazily in register() + etcdClient *etcd.Client + taskRegister utils.TaskRegister +} + +func (t *taskInfo) register(ctx context.Context) { + if time.Since(t.lastRegisterTime) < refreshTaskTTLInterval { + return + } + + if time.Since(t.lastRegisterTime) < refreshTaskTTLInterval { + return + } + logger := logutil.BgLogger().With(zap.Int64("task-id", t.taskID)) + if t.taskRegister == nil { + client, err := importer.GetEtcdClient() + if err != nil { + logger.Warn("get etcd client failed", zap.Error(err)) + return + } + t.etcdClient = client + t.taskRegister = NewTaskRegisterWithTTL(client.GetClient(), registerTaskTTL, + utils.RegisterImportInto, strconv.FormatInt(t.taskID, 10)) + } + timeoutCtx, cancel := context.WithTimeout(ctx, registerTimeout) + defer cancel() + if err := t.taskRegister.RegisterTaskOnce(timeoutCtx); err != nil { + logger.Warn("register task failed", zap.Error(err)) + } else { + logger.Info("register task to pd or refresh lease success") + } + // we set it even if register failed, TTL is 10min, refresh interval is 3min, + // we can try 2 times before the lease is expired. + t.lastRegisterTime = time.Now() +} + +func (t *taskInfo) close(ctx context.Context) { + logger := logutil.BgLogger().With(zap.Int64("task-id", t.taskID)) + if t.taskRegister != nil { + timeoutCtx, cancel := context.WithTimeout(ctx, registerTimeout) + defer cancel() + if err := t.taskRegister.Close(timeoutCtx); err != nil { + logger.Warn("unregister task failed", zap.Error(err)) + } else { + logger.Info("unregister task success") + } + t.taskRegister = nil + } + if t.etcdClient != nil { + if err := t.etcdClient.Close(); err != nil { + logger.Warn("close etcd client failed", zap.Error(err)) + } + t.etcdClient = nil + } +} + +// ImportDispatcherExt is an extension of ImportDispatcher, exported for test. +type ImportDispatcherExt struct { + GlobalSort bool + mu sync.RWMutex + // NOTE: there's no need to sync for below 2 fields actually, since we add a restriction that only one + // task can be running at a time. but we might support task queuing in the future, leave it for now. + // the last time we switch TiKV into IMPORT mode, this is a global operation, do it for one task makes + // no difference to do it for all tasks. So we do not need to record the switch time for each task. + lastSwitchTime atomic.Time + // taskInfoMap is a map from taskID to taskInfo + taskInfoMap sync.Map + + // currTaskID is the taskID of the current running task. + // It may be changed when we switch to a new task or switch to a new owner. + currTaskID atomic.Int64 + disableTiKVImportMode atomic.Bool +} + +var _ dispatcher.Extension = (*ImportDispatcherExt)(nil) + +// OnTick implements dispatcher.Extension interface. +func (dsp *ImportDispatcherExt) OnTick(ctx context.Context, task *proto.Task) { + // only switch TiKV mode or register task when task is running + if task.State != proto.TaskStateRunning { + return + } + dsp.switchTiKVMode(ctx, task) + dsp.registerTask(ctx, task) +} + +func (*ImportDispatcherExt) isImporting2TiKV(task *proto.Task) bool { + return task.Step == StepImport || task.Step == StepWriteAndIngest +} + +func (dsp *ImportDispatcherExt) switchTiKVMode(ctx context.Context, task *proto.Task) { + dsp.updateCurrentTask(task) + // only import step need to switch to IMPORT mode, + // If TiKV is in IMPORT mode during checksum, coprocessor will time out. + if dsp.disableTiKVImportMode.Load() || !dsp.isImporting2TiKV(task) { + return + } + + if time.Since(dsp.lastSwitchTime.Load()) < config.DefaultSwitchTiKVModeInterval { + return + } + + dsp.mu.Lock() + defer dsp.mu.Unlock() + if time.Since(dsp.lastSwitchTime.Load()) < config.DefaultSwitchTiKVModeInterval { + return + } + + logger := logutil.BgLogger().With(zap.Int64("task-id", task.ID)) + pdCli, switcher, err := importer.GetTiKVModeSwitcherWithPDClient(ctx, logger) + if err != nil { + logger.Warn("get tikv mode switcher failed", zap.Error(err)) + return + } + switcher.ToImportMode(ctx) + pdCli.Close() + dsp.lastSwitchTime.Store(time.Now()) +} + +func (dsp *ImportDispatcherExt) registerTask(ctx context.Context, task *proto.Task) { + val, _ := dsp.taskInfoMap.LoadOrStore(task.ID, &taskInfo{taskID: task.ID}) + info := val.(*taskInfo) + info.register(ctx) +} + +func (dsp *ImportDispatcherExt) unregisterTask(ctx context.Context, task *proto.Task) { + if val, loaded := dsp.taskInfoMap.LoadAndDelete(task.ID); loaded { + info := val.(*taskInfo) + info.close(ctx) + } +} + +// OnNextSubtasksBatch generate batch of next stage's plan. +func (dsp *ImportDispatcherExt) OnNextSubtasksBatch( + ctx context.Context, + taskHandle dispatcher.TaskHandle, + gTask *proto.Task, + nextStep int64, +) ( + resSubtaskMeta [][]byte, err error) { + logger := logutil.BgLogger().With( + zap.String("type", gTask.Type), + zap.Int64("task-id", gTask.ID), + zap.String("curr-step", stepStr(gTask.Step)), + zap.String("next-step", stepStr(nextStep)), + ) + taskMeta := &TaskMeta{} + err = json.Unmarshal(gTask.Meta, taskMeta) + if err != nil { + return nil, errors.Trace(err) + } + logger.Info("on next subtasks batch") + + defer func() { + taskFinished := err == nil && nextStep == proto.StepDone + if taskFinished { + // todo: we're not running in a transaction with task update + if err2 := dsp.finishJob(ctx, logger, taskHandle, gTask, taskMeta); err2 != nil { + err = err2 + } + } else if err != nil && !dsp.IsRetryableErr(err) { + if err2 := dsp.failJob(ctx, taskHandle, gTask, taskMeta, logger, err.Error()); err2 != nil { + // todo: we're not running in a transaction with task update, there might be case + // failJob return error, but task update succeed. + logger.Error("call failJob failed", zap.Error(err2)) + } + } + }() + + previousSubtaskMetas := make(map[int64][][]byte, 1) + switch nextStep { + case StepImport, StepEncodeAndSort: + if metrics, ok := metric.GetCommonMetric(ctx); ok { + metrics.BytesCounter.WithLabelValues(metric.StateTotalRestore).Add(float64(taskMeta.Plan.TotalFileSize)) + } + jobStep := importer.JobStepImporting + if dsp.GlobalSort { + jobStep = importer.JobStepGlobalSorting + } + if err = startJob(ctx, logger, taskHandle, taskMeta, jobStep); err != nil { + return nil, err + } + case StepMergeSort: + sortAndEncodeMeta, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepEncodeAndSort) + if err != nil { + return nil, err + } + previousSubtaskMetas[StepEncodeAndSort] = sortAndEncodeMeta + case StepWriteAndIngest: + failpoint.Inject("failWhenDispatchWriteIngestSubtask", func() { + failpoint.Return(nil, errors.New("injected error")) + }) + // merge sort might be skipped for some kv groups, so we need to get all + // subtask metas of StepEncodeAndSort step too. + encodeAndSortMetas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepEncodeAndSort) + if err != nil { + return nil, err + } + mergeSortMetas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, StepMergeSort) + if err != nil { + return nil, err + } + previousSubtaskMetas[StepEncodeAndSort] = encodeAndSortMetas + previousSubtaskMetas[StepMergeSort] = mergeSortMetas + if err = job2Step(ctx, logger, taskMeta, importer.JobStepImporting); err != nil { + return nil, err + } + case StepPostProcess: + dsp.switchTiKV2NormalMode(ctx, gTask, logger) + failpoint.Inject("clearLastSwitchTime", func() { + dsp.lastSwitchTime.Store(time.Time{}) + }) + if err = job2Step(ctx, logger, taskMeta, importer.JobStepValidating); err != nil { + return nil, err + } + failpoint.Inject("failWhenDispatchPostProcessSubtask", func() { + failpoint.Return(nil, errors.New("injected error after StepImport")) + }) + // we need get metas where checksum is stored. + if err := updateResult(taskHandle, gTask, taskMeta, dsp.GlobalSort); err != nil { + return nil, err + } + step := getStepOfEncode(dsp.GlobalSort) + metas, err := taskHandle.GetPreviousSubtaskMetas(gTask.ID, step) + if err != nil { + return nil, err + } + previousSubtaskMetas[step] = metas + logger.Info("move to post-process step ", zap.Any("result", taskMeta.Result)) + case proto.StepDone: + return nil, nil + default: + return nil, errors.Errorf("unknown step %d", gTask.Step) + } + + planCtx := planner.PlanCtx{ + Ctx: ctx, + TaskID: gTask.ID, + PreviousSubtaskMetas: previousSubtaskMetas, + GlobalSort: dsp.GlobalSort, + NextTaskStep: nextStep, + } + logicalPlan := &LogicalPlan{} + if err := logicalPlan.FromTaskMeta(gTask.Meta); err != nil { + return nil, err + } + physicalPlan, err := logicalPlan.ToPhysicalPlan(planCtx) + if err != nil { + return nil, err + } + metaBytes, err := physicalPlan.ToSubtaskMetas(planCtx, nextStep) + if err != nil { + return nil, err + } + logger.Info("generate subtasks", zap.Int("subtask-count", len(metaBytes))) + return metaBytes, nil +} + +// OnErrStage implements dispatcher.Extension interface. +func (dsp *ImportDispatcherExt) OnErrStage(ctx context.Context, handle dispatcher.TaskHandle, gTask *proto.Task, receiveErrs []error) ([]byte, error) { + logger := logutil.BgLogger().With( + zap.String("type", gTask.Type), + zap.Int64("task-id", gTask.ID), + zap.String("step", stepStr(gTask.Step)), + ) + logger.Info("on error stage", zap.Errors("errors", receiveErrs)) + taskMeta := &TaskMeta{} + err := json.Unmarshal(gTask.Meta, taskMeta) + if err != nil { + return nil, errors.Trace(err) + } + errStrs := make([]string, 0, len(receiveErrs)) + for _, receiveErr := range receiveErrs { + errStrs = append(errStrs, receiveErr.Error()) + } + if err = dsp.failJob(ctx, handle, gTask, taskMeta, logger, strings.Join(errStrs, "; ")); err != nil { + return nil, err + } + + gTask.Error = receiveErrs[0] + + errStr := receiveErrs[0].Error() + // do nothing if the error is resumable + if isResumableErr(errStr) { + return nil, nil + } + + if gTask.Step == StepImport { + err = rollback(ctx, handle, gTask, logger) + if err != nil { + // TODO: add error code according to spec. + gTask.Error = errors.New(errStr + ", " + err.Error()) + } + } + return nil, err +} + +// GetEligibleInstances implements dispatcher.Extension interface. +func (*ImportDispatcherExt) GetEligibleInstances(ctx context.Context, gTask *proto.Task) ([]*infosync.ServerInfo, error) { + taskMeta := &TaskMeta{} + err := json.Unmarshal(gTask.Meta, taskMeta) + if err != nil { + return nil, errors.Trace(err) + } + if len(taskMeta.EligibleInstances) > 0 { + return taskMeta.EligibleInstances, nil + } + return dispatcher.GenerateSchedulerNodes(ctx) +} + +// IsRetryableErr implements dispatcher.Extension interface. +func (*ImportDispatcherExt) IsRetryableErr(error) bool { + // TODO: check whether the error is retryable. + return false +} + +// GetNextStep implements dispatcher.Extension interface. +func (dsp *ImportDispatcherExt) GetNextStep(_ dispatcher.TaskHandle, task *proto.Task) int64 { + switch task.Step { + case proto.StepInit: + if dsp.GlobalSort { + return StepEncodeAndSort + } + return StepImport + case StepEncodeAndSort: + return StepMergeSort + case StepMergeSort: + return StepWriteAndIngest + case StepImport, StepWriteAndIngest: + return StepPostProcess + default: + // current step must be StepPostProcess + return proto.StepDone + } +} + +func (dsp *ImportDispatcherExt) switchTiKV2NormalMode(ctx context.Context, task *proto.Task, logger *zap.Logger) { + dsp.updateCurrentTask(task) + if dsp.disableTiKVImportMode.Load() { + return + } + + dsp.mu.Lock() + defer dsp.mu.Unlock() + + pdCli, switcher, err := importer.GetTiKVModeSwitcherWithPDClient(ctx, logger) + if err != nil { + logger.Warn("get tikv mode switcher failed", zap.Error(err)) + return + } + switcher.ToNormalMode(ctx) + pdCli.Close() + + // clear it, so next task can switch TiKV mode again. + dsp.lastSwitchTime.Store(time.Time{}) +} + +func (dsp *ImportDispatcherExt) updateCurrentTask(task *proto.Task) { + if dsp.currTaskID.Swap(task.ID) != task.ID { + taskMeta := &TaskMeta{} + if err := json.Unmarshal(task.Meta, taskMeta); err == nil { + // for raftkv2, switch mode in local backend + dsp.disableTiKVImportMode.Store(taskMeta.Plan.DisableTiKVImportMode || taskMeta.Plan.IsRaftKV2) + } + } +} + +type importDispatcher struct { + *dispatcher.BaseDispatcher +} + +func newImportDispatcher(ctx context.Context, taskMgr *storage.TaskManager, + serverID string, task *proto.Task) dispatcher.Dispatcher { + metrics := metricsManager.getOrCreateMetrics(task.ID) + subCtx := metric.WithCommonMetric(ctx, metrics) + dsp := importDispatcher{ + BaseDispatcher: dispatcher.NewBaseDispatcher(subCtx, taskMgr, serverID, task), + } + return &dsp +} + +func (dsp *importDispatcher) Init() (err error) { + defer func() { + if err != nil { + // if init failed, close is not called, so we need to unregister here. + metricsManager.unregister(dsp.Task.ID) + } + }() + taskMeta := &TaskMeta{} + if err = json.Unmarshal(dsp.BaseDispatcher.Task.Meta, taskMeta); err != nil { + return errors.Annotate(err, "unmarshal task meta failed") + } + + dsp.BaseDispatcher.Extension = &ImportDispatcherExt{ + GlobalSort: taskMeta.Plan.CloudStorageURI != "", + } + return dsp.BaseDispatcher.Init() +} + +func (dsp *importDispatcher) Close() { + metricsManager.unregister(dsp.Task.ID) + dsp.BaseDispatcher.Close() +} + +// nolint:deadcode +func dropTableIndexes(ctx context.Context, handle dispatcher.TaskHandle, taskMeta *TaskMeta, logger *zap.Logger) error { + tblInfo := taskMeta.Plan.TableInfo + tableName := common.UniqueTable(taskMeta.Plan.DBName, tblInfo.Name.L) + + remainIndexes, dropIndexes := common.GetDropIndexInfos(tblInfo) + for _, idxInfo := range dropIndexes { + sqlStr := common.BuildDropIndexSQL(tableName, idxInfo) + if err := executeSQL(ctx, handle, logger, sqlStr); err != nil { + if merr, ok := errors.Cause(err).(*dmysql.MySQLError); ok { + switch merr.Number { + case errno.ErrCantDropFieldOrKey, errno.ErrDropIndexNeededInForeignKey: + remainIndexes = append(remainIndexes, idxInfo) + logger.Warn("can't drop index, skip", zap.String("index", idxInfo.Name.O), zap.Error(err)) + continue + } + } + return err + } + } + if len(remainIndexes) < len(tblInfo.Indices) { + taskMeta.Plan.TableInfo = taskMeta.Plan.TableInfo.Clone() + taskMeta.Plan.TableInfo.Indices = remainIndexes + } + return nil +} + +// nolint:deadcode +func createTableIndexes(ctx context.Context, executor storage.SessionExecutor, taskMeta *TaskMeta, logger *zap.Logger) error { + tableName := common.UniqueTable(taskMeta.Plan.DBName, taskMeta.Plan.TableInfo.Name.L) + singleSQL, multiSQLs := common.BuildAddIndexSQL(tableName, taskMeta.Plan.TableInfo, taskMeta.Plan.DesiredTableInfo) + logger.Info("build add index sql", zap.String("singleSQL", singleSQL), zap.Strings("multiSQLs", multiSQLs)) + if len(multiSQLs) == 0 { + return nil + } + + err := executeSQL(ctx, executor, logger, singleSQL) + if err == nil { + return nil + } + if !common.IsDupKeyError(err) { + // TODO: refine err msg and error code according to spec. + return errors.Errorf("Failed to create index: %v, please execute the SQL manually, sql: %s", err, singleSQL) + } + if len(multiSQLs) == 1 { + return nil + } + logger.Warn("cannot add all indexes in one statement, try to add them one by one", zap.Strings("sqls", multiSQLs), zap.Error(err)) + + for i, ddl := range multiSQLs { + err := executeSQL(ctx, executor, logger, ddl) + if err != nil && !common.IsDupKeyError(err) { + // TODO: refine err msg and error code according to spec. + return errors.Errorf("Failed to create index: %v, please execute the SQLs manually, sqls: %s", err, strings.Join(multiSQLs[i:], ";")) + } + } + return nil +} + +// TODO: return the result of sql. +func executeSQL(ctx context.Context, executor storage.SessionExecutor, logger *zap.Logger, sql string, args ...interface{}) (err error) { + logger.Info("execute sql", zap.String("sql", sql), zap.Any("args", args)) + return executor.WithNewSession(func(se sessionctx.Context) error { + _, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql, args...) + return err + }) +} + +func updateMeta(gTask *proto.Task, taskMeta *TaskMeta) error { + bs, err := json.Marshal(taskMeta) + if err != nil { + return errors.Trace(err) + } + gTask.Meta = bs + + return nil +} + +// todo: converting back and forth, we should unify struct and remove this function later. +func toChunkMap(engineCheckpoints map[int32]*checkpoints.EngineCheckpoint) map[int32][]Chunk { + chunkMap := make(map[int32][]Chunk, len(engineCheckpoints)) + for id, ecp := range engineCheckpoints { + chunkMap[id] = make([]Chunk, 0, len(ecp.Chunks)) + for _, chunkCheckpoint := range ecp.Chunks { + chunkMap[id] = append(chunkMap[id], toChunk(*chunkCheckpoint)) + } + } + return chunkMap +} + +func getStepOfEncode(globalSort bool) int64 { + if globalSort { + return StepEncodeAndSort + } + return StepImport +} + +// we will update taskMeta in place and make gTask.Meta point to the new taskMeta. +func updateResult(handle dispatcher.TaskHandle, gTask *proto.Task, taskMeta *TaskMeta, globalSort bool) error { + stepOfEncode := getStepOfEncode(globalSort) + metas, err := handle.GetPreviousSubtaskMetas(gTask.ID, stepOfEncode) + if err != nil { + return err + } + + subtaskMetas := make([]*ImportStepMeta, 0, len(metas)) + for _, bs := range metas { + var subtaskMeta ImportStepMeta + if err := json.Unmarshal(bs, &subtaskMeta); err != nil { + return errors.Trace(err) + } + subtaskMetas = append(subtaskMetas, &subtaskMeta) + } + columnSizeMap := make(map[int64]int64) + for _, subtaskMeta := range subtaskMetas { + taskMeta.Result.LoadedRowCnt += subtaskMeta.Result.LoadedRowCnt + for key, val := range subtaskMeta.Result.ColSizeMap { + columnSizeMap[key] += val + } + } + taskMeta.Result.ColSizeMap = columnSizeMap + + if globalSort { + taskMeta.Result.LoadedRowCnt, err = getLoadedRowCountOnGlobalSort(handle, gTask) + if err != nil { + return err + } + } + + return updateMeta(gTask, taskMeta) +} + +func getLoadedRowCountOnGlobalSort(handle dispatcher.TaskHandle, gTask *proto.Task) (uint64, error) { + metas, err := handle.GetPreviousSubtaskMetas(gTask.ID, StepWriteAndIngest) + if err != nil { + return 0, err + } + + var loadedRowCount uint64 + for _, bs := range metas { + var subtaskMeta WriteIngestStepMeta + if err = json.Unmarshal(bs, &subtaskMeta); err != nil { + return 0, errors.Trace(err) + } + loadedRowCount += subtaskMeta.Result.LoadedRowCnt + } + return loadedRowCount, nil +} + +func startJob(ctx context.Context, logger *zap.Logger, taskHandle dispatcher.TaskHandle, taskMeta *TaskMeta, jobStep string) error { + failpoint.Inject("syncBeforeJobStarted", func() { + TestSyncChan <- struct{}{} + <-TestSyncChan + }) + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + // we consider all errors as retryable errors, except context done. + // the errors include errors happened when communicate with PD and TiKV. + // we didn't consider system corrupt cases like system table dropped/altered. + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + err := handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { + exec := se.(sqlexec.SQLExecutor) + return importer.StartJob(ctx, exec, taskMeta.JobID, jobStep) + }) + }, + ) + failpoint.Inject("syncAfterJobStarted", func() { + TestSyncChan <- struct{}{} + }) + return err +} + +func job2Step(ctx context.Context, logger *zap.Logger, taskMeta *TaskMeta, step string) error { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return err + } + // todo: use dispatcher.TaskHandle + // we might call this in scheduler later, there's no dispatcher.TaskHandle, so we use globalTaskManager here. + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, globalTaskManager.WithNewSession(func(se sessionctx.Context) error { + exec := se.(sqlexec.SQLExecutor) + return importer.Job2Step(ctx, exec, taskMeta.JobID, step) + }) + }, + ) +} + +func (dsp *ImportDispatcherExt) finishJob(ctx context.Context, logger *zap.Logger, + taskHandle dispatcher.TaskHandle, gTask *proto.Task, taskMeta *TaskMeta) error { + dsp.unregisterTask(ctx, gTask) + summary := &importer.JobSummary{ImportedRows: taskMeta.Result.LoadedRowCnt} + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { + exec := se.(sqlexec.SQLExecutor) + return importer.FinishJob(ctx, exec, taskMeta.JobID, summary) + }) + }, + ) +} + +func (dsp *ImportDispatcherExt) failJob(ctx context.Context, taskHandle dispatcher.TaskHandle, gTask *proto.Task, + taskMeta *TaskMeta, logger *zap.Logger, errorMsg string) error { + dsp.switchTiKV2NormalMode(ctx, gTask, logger) + dsp.unregisterTask(ctx, gTask) + // retry for 3+6+12+24+(30-4)*30 ~= 825s ~= 14 minutes + backoffer := backoff.NewExponential(dispatcher.RetrySQLInterval, 2, dispatcher.RetrySQLMaxInterval) + return handle.RunWithRetry(ctx, dispatcher.RetrySQLTimes, backoffer, logger, + func(ctx context.Context) (bool, error) { + return true, taskHandle.WithNewSession(func(se sessionctx.Context) error { + exec := se.(sqlexec.SQLExecutor) + return importer.FailJob(ctx, exec, taskMeta.JobID, errorMsg) + }) + }, + ) +} + +func redactSensitiveInfo(gTask *proto.Task, taskMeta *TaskMeta) { + taskMeta.Stmt = "" + taskMeta.Plan.Path = ast.RedactURL(taskMeta.Plan.Path) + if taskMeta.Plan.CloudStorageURI != "" { + taskMeta.Plan.CloudStorageURI = ast.RedactURL(taskMeta.Plan.CloudStorageURI) + } + if err := updateMeta(gTask, taskMeta); err != nil { + // marshal failed, should not happen + logutil.BgLogger().Warn("failed to update task meta", zap.Error(err)) + } +} + +// isResumableErr checks whether it's possible to rely on checkpoint to re-import data after the error has been fixed. +func isResumableErr(string) bool { + // TODO: add more cases + return false +} + +func rollback(ctx context.Context, handle dispatcher.TaskHandle, gTask *proto.Task, logger *zap.Logger) (err error) { + taskMeta := &TaskMeta{} + err = json.Unmarshal(gTask.Meta, taskMeta) + if err != nil { + return errors.Trace(err) + } + + logger.Info("rollback") + + // // TODO: create table indexes depends on the option. + // // create table indexes even if the rollback is failed. + // defer func() { + // err2 := createTableIndexes(ctx, handle, taskMeta, logger) + // err = multierr.Append(err, err2) + // }() + + tableName := common.UniqueTable(taskMeta.Plan.DBName, taskMeta.Plan.TableInfo.Name.L) + // truncate the table + return executeSQL(ctx, handle, logger, "TRUNCATE "+tableName) +} + +func stepStr(step int64) string { + switch step { + case proto.StepInit: + return "init" + case StepImport: + return "import" + case StepPostProcess: + return "post-process" + case StepEncodeAndSort: + return "encode&sort" + case StepMergeSort: + return "merge-sort" + case StepWriteAndIngest: + return "write&ingest" + case proto.StepDone: + return "done" + default: + return "unknown" + } +} + +func init() { + dispatcher.RegisterDispatcherFactory(proto.ImportInto, newImportDispatcher) +} diff --git a/pkg/disttask/importinto/dispatcher_test.go b/pkg/disttask/importinto/dispatcher_test.go new file mode 100644 index 0000000000000..7924757921cc6 --- /dev/null +++ b/pkg/disttask/importinto/dispatcher_test.go @@ -0,0 +1,185 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" +) + +type importIntoSuite struct { + suite.Suite +} + +func TestImportInto(t *testing.T) { + suite.Run(t, &importIntoSuite{}) +} + +func (s *importIntoSuite) enableFailPoint(path, term string) { + require.NoError(s.T(), failpoint.Enable(path, term)) + s.T().Cleanup(func() { + _ = failpoint.Disable(path) + }) +} + +func (s *importIntoSuite) TestDispatcherGetEligibleInstances() { + makeFailpointRes := func(v interface{}) string { + bytes, err := json.Marshal(v) + s.NoError(err) + return fmt.Sprintf("return(`%s`)", string(bytes)) + } + uuids := []string{"ddl_id_1", "ddl_id_2"} + serverInfoMap := map[string]*infosync.ServerInfo{ + uuids[0]: { + ID: uuids[0], + }, + uuids[1]: { + ID: uuids[1], + }, + } + mockedAllServerInfos := makeFailpointRes(serverInfoMap) + + dsp := ImportDispatcherExt{} + gTask := &proto.Task{Meta: []byte("{}")} + ctx := context.WithValue(context.Background(), "etcd", true) + s.enableFailPoint("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo", mockedAllServerInfos) + eligibleInstances, err := dsp.GetEligibleInstances(ctx, gTask) + s.NoError(err) + // order of slice is not stable, change to map + resultMap := map[string]*infosync.ServerInfo{} + for _, ins := range eligibleInstances { + resultMap[ins.ID] = ins + } + s.Equal(serverInfoMap, resultMap) + + gTask.Meta = []byte(`{"EligibleInstances":[{"ip": "1.1.1.1", "listening_port": 4000}]}`) + eligibleInstances, err = dsp.GetEligibleInstances(ctx, gTask) + s.NoError(err) + s.Equal([]*infosync.ServerInfo{{IP: "1.1.1.1", Port: 4000}}, eligibleInstances) +} + +func (s *importIntoSuite) TestUpdateCurrentTask() { + taskMeta := TaskMeta{ + Plan: importer.Plan{ + DisableTiKVImportMode: true, + }, + } + bs, err := json.Marshal(taskMeta) + require.NoError(s.T(), err) + + dsp := ImportDispatcherExt{} + require.Equal(s.T(), int64(0), dsp.currTaskID.Load()) + require.False(s.T(), dsp.disableTiKVImportMode.Load()) + + dsp.updateCurrentTask(&proto.Task{ + ID: 1, + Meta: bs, + }) + require.Equal(s.T(), int64(1), dsp.currTaskID.Load()) + require.True(s.T(), dsp.disableTiKVImportMode.Load()) + + dsp.updateCurrentTask(&proto.Task{ + ID: 1, + Meta: bs, + }) + require.Equal(s.T(), int64(1), dsp.currTaskID.Load()) + require.True(s.T(), dsp.disableTiKVImportMode.Load()) +} + +func (s *importIntoSuite) TestDispatcherInit() { + meta := TaskMeta{ + Plan: importer.Plan{ + CloudStorageURI: "", + }, + } + bytes, err := json.Marshal(meta) + s.NoError(err) + dsp := importDispatcher{ + BaseDispatcher: &dispatcher.BaseDispatcher{ + Task: &proto.Task{ + Meta: bytes, + }, + }, + } + s.NoError(dsp.Init()) + s.False(dsp.Extension.(*ImportDispatcherExt).GlobalSort) + + meta.Plan.CloudStorageURI = "s3://test" + bytes, err = json.Marshal(meta) + s.NoError(err) + dsp = importDispatcher{ + BaseDispatcher: &dispatcher.BaseDispatcher{ + Task: &proto.Task{ + Meta: bytes, + }, + }, + } + s.NoError(dsp.Init()) + s.True(dsp.Extension.(*ImportDispatcherExt).GlobalSort) +} + +func (s *importIntoSuite) TestGetNextStep() { + task := &proto.Task{ + Step: proto.StepInit, + } + ext := &ImportDispatcherExt{} + for _, nextStep := range []int64{StepImport, StepPostProcess, proto.StepDone} { + s.Equal(nextStep, ext.GetNextStep(nil, task)) + task.Step = nextStep + } + + task.Step = proto.StepInit + ext = &ImportDispatcherExt{GlobalSort: true} + for _, nextStep := range []int64{StepEncodeAndSort, StepMergeSort, + StepWriteAndIngest, StepPostProcess, proto.StepDone} { + s.Equal(nextStep, ext.GetNextStep(nil, task)) + task.Step = nextStep + } +} + +func (s *importIntoSuite) TestStr() { + s.Equal("init", stepStr(proto.StepInit)) + s.Equal("import", stepStr(StepImport)) + s.Equal("post-process", stepStr(StepPostProcess)) + s.Equal("merge-sort", stepStr(StepMergeSort)) + s.Equal("encode&sort", stepStr(StepEncodeAndSort)) + s.Equal("write&ingest", stepStr(StepWriteAndIngest)) + s.Equal("done", stepStr(proto.StepDone)) + s.Equal("unknown", stepStr(111)) +} + +func (s *importIntoSuite) TestGetStepOfEncode() { + s.Equal(StepImport, getStepOfEncode(false)) + s.Equal(StepEncodeAndSort, getStepOfEncode(true)) +} + +func TestIsImporting2TiKV(t *testing.T) { + ext := &ImportDispatcherExt{} + require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepEncodeAndSort})) + require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepMergeSort})) + require.False(t, ext.isImporting2TiKV(&proto.Task{Step: StepPostProcess})) + require.True(t, ext.isImporting2TiKV(&proto.Task{Step: StepImport})) + require.True(t, ext.isImporting2TiKV(&proto.Task{Step: StepWriteAndIngest})) +} diff --git a/disttask/importinto/dispatcher_testkit_test.go b/pkg/disttask/importinto/dispatcher_testkit_test.go similarity index 94% rename from disttask/importinto/dispatcher_testkit_test.go rename to pkg/disttask/importinto/dispatcher_testkit_test.go index c779d70f524ac..2bd7c9b138d3e 100644 --- a/disttask/importinto/dispatcher_testkit_test.go +++ b/pkg/disttask/importinto/dispatcher_testkit_test.go @@ -24,15 +24,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" ) @@ -270,9 +270,9 @@ func TestDispatcherExtGlobalSort(t *testing.T) { } // to merge-sort stage - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort", `return("data")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort", `return("data")`)) t.Cleanup(func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/forceMergeSort")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort")) }) subtaskMetas, err = ext.OnNextSubtasksBatch(ctx, d, task, ext.GetNextStep(nil, task)) require.NoError(t, err) @@ -316,9 +316,9 @@ func TestDispatcherExtGlobalSort(t *testing.T) { } // to write-and-ingest stage - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/importinto/mockWriteIngestSpecs", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/importinto/mockWriteIngestSpecs", "return(true)")) t.Cleanup(func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/mockWriteIngestSpecs")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/mockWriteIngestSpecs")) }) subtaskMetas, err = ext.OnNextSubtasksBatch(ctx, d, task, ext.GetNextStep(nil, task)) require.NoError(t, err) diff --git a/disttask/importinto/encode_and_sort_operator.go b/pkg/disttask/importinto/encode_and_sort_operator.go similarity index 96% rename from disttask/importinto/encode_and_sort_operator.go rename to pkg/disttask/importinto/encode_and_sort_operator.go index 45de327c75921..61af716c657e5 100644 --- a/disttask/importinto/encode_and_sort_operator.go +++ b/pkg/disttask/importinto/encode_and_sort_operator.go @@ -24,13 +24,13 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/resourcemanager/pool/workerpool" - "github.com/pingcap/tidb/resourcemanager/util" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/size" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/disttask/importinto/encode_and_sort_operator_test.go b/pkg/disttask/importinto/encode_and_sort_operator_test.go similarity index 94% rename from disttask/importinto/encode_and_sort_operator_test.go rename to pkg/disttask/importinto/encode_and_sort_operator_test.go index ac6eed53c7492..2ed7967dc06e7 100644 --- a/disttask/importinto/encode_and_sort_operator_test.go +++ b/pkg/disttask/importinto/encode_and_sort_operator_test.go @@ -26,14 +26,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/tidb/br/pkg/lightning/backend" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/disttask/importinto/mock" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - utilmock "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/disttask/importinto/mock" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + utilmock "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" "go.uber.org/zap" diff --git a/pkg/disttask/importinto/job.go b/pkg/disttask/importinto/job.go new file mode 100644 index 0000000000000..ec12961fe45f0 --- /dev/null +++ b/pkg/disttask/importinto/job.go @@ -0,0 +1,292 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/google/uuid" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" + "github.com/pingcap/tidb/pkg/disttask/framework/handle" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// DistImporter is a JobImporter for distributed IMPORT INTO. +type DistImporter struct { + *importer.JobImportParam + plan *importer.Plan + stmt string + logger *zap.Logger + // the instance to import data, used for single-node import, nil means import data on all instances. + instance *infosync.ServerInfo + // the files to import, when import from server file, we need to pass those file to the framework. + chunkMap map[int32][]Chunk + sourceFileSize int64 + // only set after submit task + jobID int64 + taskID int64 +} + +// NewDistImporter creates a new DistImporter. +func NewDistImporter(param *importer.JobImportParam, plan *importer.Plan, stmt string, sourceFileSize int64) (*DistImporter, error) { + return &DistImporter{ + JobImportParam: param, + plan: plan, + stmt: stmt, + logger: logutil.BgLogger(), + sourceFileSize: sourceFileSize, + }, nil +} + +// NewDistImporterCurrNode creates a new DistImporter to import data on current node. +func NewDistImporterCurrNode(param *importer.JobImportParam, plan *importer.Plan, stmt string, sourceFileSize int64) (*DistImporter, error) { + serverInfo, err := infosync.GetServerInfo() + if err != nil { + return nil, err + } + return &DistImporter{ + JobImportParam: param, + plan: plan, + stmt: stmt, + logger: logutil.BgLogger(), + instance: serverInfo, + sourceFileSize: sourceFileSize, + }, nil +} + +// NewDistImporterServerFile creates a new DistImporter to import given files on current node. +// we also run import on current node. +// todo: merge all 3 ctor into one. +func NewDistImporterServerFile(param *importer.JobImportParam, plan *importer.Plan, stmt string, ecp map[int32]*checkpoints.EngineCheckpoint, sourceFileSize int64) (*DistImporter, error) { + distImporter, err := NewDistImporterCurrNode(param, plan, stmt, sourceFileSize) + if err != nil { + return nil, err + } + distImporter.chunkMap = toChunkMap(ecp) + return distImporter, nil +} + +// Param implements JobImporter.Param. +func (ti *DistImporter) Param() *importer.JobImportParam { + return ti.JobImportParam +} + +// Import implements JobImporter.Import. +func (*DistImporter) Import() { + // todo: remove it +} + +// ImportTask import task. +func (ti *DistImporter) ImportTask(task *proto.Task) { + ti.logger.Info("start distribute IMPORT INTO") + ti.Group.Go(func() error { + defer close(ti.Done) + // task is run using distribute framework, so we only wait for the task to finish. + return handle.WaitGlobalTask(ti.GroupCtx, task) + }) +} + +// Result implements JobImporter.Result. +func (ti *DistImporter) Result() importer.JobImportResult { + var result importer.JobImportResult + taskMeta, err := getTaskMeta(ti.jobID) + if err != nil { + return result + } + + return importer.JobImportResult{ + Affected: taskMeta.Result.LoadedRowCnt, + ColSizeMap: taskMeta.Result.ColSizeMap, + } +} + +// Close implements the io.Closer interface. +func (*DistImporter) Close() error { + return nil +} + +// SubmitTask submits a task to the distribute framework. +func (ti *DistImporter) SubmitTask(ctx context.Context) (int64, *proto.Task, error) { + var instances []*infosync.ServerInfo + if ti.instance != nil { + instances = append(instances, ti.instance) + } + // we use globalTaskManager to submit task, user might not have the privilege to system tables. + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return 0, nil, err + } + + var jobID, taskID int64 + plan := ti.plan + if err = globalTaskManager.WithNewTxn(ctx, func(se sessionctx.Context) error { + var err2 error + exec := se.(sqlexec.SQLExecutor) + // If 2 client try to execute IMPORT INTO concurrently, there's chance that both of them will pass the check. + // We can enforce ONLY one import job running by: + // - using LOCK TABLES, but it requires enable-table-lock=true, it's not enabled by default. + // - add a key to PD as a distributed lock, but it's a little complex, and we might support job queuing later. + // So we only add this simple soft check here and doc it. + activeJobCnt, err2 := importer.GetActiveJobCnt(ctx, exec) + if err2 != nil { + return err2 + } + if activeJobCnt > 0 { + return exeerrors.ErrLoadDataPreCheckFailed.FastGenByArgs("there's pending or running jobs") + } + jobID, err2 = importer.CreateJob(ctx, exec, plan.DBName, plan.TableInfo.Name.L, plan.TableInfo.ID, + plan.User, plan.Parameters, ti.sourceFileSize) + if err2 != nil { + return err2 + } + + // TODO: use planner.Run to run the logical plan + // now creating import job and submitting distributed task should be in the same transaction. + logicalPlan := &LogicalPlan{ + JobID: jobID, + Plan: *plan, + Stmt: ti.stmt, + EligibleInstances: instances, + ChunkMap: ti.chunkMap, + } + planCtx := planner.PlanCtx{ + Ctx: ctx, + SessionCtx: se, + TaskKey: TaskKey(jobID), + TaskType: proto.ImportInto, + ThreadCnt: int(plan.ThreadCnt), + } + p := planner.NewPlanner() + taskID, err2 = p.Run(planCtx, logicalPlan) + if err2 != nil { + return err2 + } + return nil + }); err != nil { + return 0, nil, err + } + + globalTask, err := globalTaskManager.GetGlobalTaskByID(taskID) + if err != nil { + return 0, nil, err + } + if globalTask == nil { + return 0, nil, errors.Errorf("cannot find global task with ID %d", taskID) + } + // update logger with task id. + ti.jobID = jobID + ti.taskID = taskID + ti.logger = ti.logger.With(zap.Int64("task-id", globalTask.ID)) + + ti.logger.Info("job submitted to global task queue", + zap.Int64("job-id", jobID), zap.Int64("thread-cnt", plan.ThreadCnt)) + + return jobID, globalTask, nil +} + +func (*DistImporter) taskKey() string { + // task key is meaningless to IMPORT INTO, so we use a random uuid. + return fmt.Sprintf("%s/%s", proto.ImportInto, uuid.New().String()) +} + +// JobID returns the job id. +func (ti *DistImporter) JobID() int64 { + return ti.jobID +} + +func getTaskMeta(jobID int64) (*TaskMeta, error) { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return nil, err + } + taskKey := TaskKey(jobID) + globalTask, err := globalTaskManager.GetGlobalTaskByKey(taskKey) + if err != nil { + return nil, err + } + if globalTask == nil { + return nil, errors.Errorf("cannot find global task with key %s", taskKey) + } + var taskMeta TaskMeta + if err := json.Unmarshal(globalTask.Meta, &taskMeta); err != nil { + return nil, errors.Trace(err) + } + return &taskMeta, nil +} + +// GetTaskImportedRows gets the number of imported rows of a job. +// Note: for finished job, we can get the number of imported rows from task meta. +func GetTaskImportedRows(jobID int64) (uint64, error) { + globalTaskManager, err := storage.GetTaskManager() + if err != nil { + return 0, err + } + taskKey := TaskKey(jobID) + task, err := globalTaskManager.GetGlobalTaskByKey(taskKey) + if err != nil { + return 0, err + } + if task == nil { + return 0, errors.Errorf("cannot find global task with key %s", taskKey) + } + taskMeta := TaskMeta{} + if err = json.Unmarshal(task.Meta, &taskMeta); err != nil { + return 0, errors.Trace(err) + } + var importedRows uint64 + if taskMeta.Plan.CloudStorageURI == "" { + subtasks, err := globalTaskManager.GetSubtasksForImportInto(task.ID, StepImport) + if err != nil { + return 0, err + } + for _, subtask := range subtasks { + var subtaskMeta ImportStepMeta + if err2 := json.Unmarshal(subtask.Meta, &subtaskMeta); err2 != nil { + return 0, errors.Trace(err2) + } + importedRows += subtaskMeta.Result.LoadedRowCnt + } + } else { + subtasks, err := globalTaskManager.GetSubtasksForImportInto(task.ID, StepWriteAndIngest) + if err != nil { + return 0, err + } + for _, subtask := range subtasks { + var subtaskMeta WriteIngestStepMeta + if err2 := json.Unmarshal(subtask.Meta, &subtaskMeta); err2 != nil { + return 0, errors.Trace(err2) + } + importedRows += subtaskMeta.Result.LoadedRowCnt + } + } + return importedRows, nil +} + +// TaskKey returns the task key for a job. +func TaskKey(jobID int64) string { + return fmt.Sprintf("%s/%d", proto.ImportInto, jobID) +} diff --git a/disttask/importinto/job_testkit_test.go b/pkg/disttask/importinto/job_testkit_test.go similarity index 91% rename from disttask/importinto/job_testkit_test.go rename to pkg/disttask/importinto/job_testkit_test.go index 3638feb2d59d6..3b2873d470f7a 100644 --- a/disttask/importinto/job_testkit_test.go +++ b/pkg/disttask/importinto/job_testkit_test.go @@ -21,11 +21,11 @@ import ( "time" "github.com/ngaut/pools" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" ) diff --git a/pkg/disttask/importinto/metrics.go b/pkg/disttask/importinto/metrics.go new file mode 100644 index 0000000000000..0d583d9e6ebe9 --- /dev/null +++ b/pkg/disttask/importinto/metrics.go @@ -0,0 +1,81 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "strconv" + "sync" + + "github.com/pingcap/tidb/br/pkg/lightning/metric" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + tidbmetrics "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/promutil" + "github.com/prometheus/client_golang/prometheus" +) + +type taskMetrics struct { + metrics *metric.Common + counter int +} + +// taskMetricManager manages the metrics of IMPORT INTO tasks. +// we have a set of metrics for each task, with different task_id const label. +// metrics is passed by context value to avoid passing parameters everywhere. +// both dispatcher and scheduler might use it, to avoid registered again, +// we add a manager to manage lifecycle of metrics for tasks. +type taskMetricManager struct { + sync.RWMutex + metricsMap map[int64]*taskMetrics +} + +var metricsManager = &taskMetricManager{ + metricsMap: make(map[int64]*taskMetrics), +} + +// getOrCreateMetrics gets or creates the metrics for the task. +// if the metrics has been created, the counter will be increased. +func (m *taskMetricManager) getOrCreateMetrics(taskID int64) *metric.Common { + m.Lock() + defer m.Unlock() + tm, ok := m.metricsMap[taskID] + if !ok { + metrics := tidbmetrics.GetRegisteredImportMetrics(promutil.NewDefaultFactory(), + prometheus.Labels{ + proto.TaskIDLabelName: strconv.FormatInt(taskID, 10), + }) + tm = &taskMetrics{ + metrics: metrics, + } + m.metricsMap[taskID] = tm + } + + tm.counter++ + + return tm.metrics +} + +// unregister count down the metrics for the task. +// if the counter is 0, the metrics will be unregistered. +func (m *taskMetricManager) unregister(taskID int64) { + m.Lock() + defer m.Unlock() + if tm, ok := m.metricsMap[taskID]; ok { + tm.counter-- + if tm.counter == 0 { + tidbmetrics.UnregisterImportMetrics(tm.metrics) + delete(m.metricsMap, taskID) + } + } +} diff --git a/disttask/importinto/metrics_test.go b/pkg/disttask/importinto/metrics_test.go similarity index 100% rename from disttask/importinto/metrics_test.go rename to pkg/disttask/importinto/metrics_test.go diff --git a/pkg/disttask/importinto/mock/BUILD.bazel b/pkg/disttask/importinto/mock/BUILD.bazel new file mode 100644 index 0000000000000..111da2bb715eb --- /dev/null +++ b/pkg/disttask/importinto/mock/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mock", + srcs = ["import_mock.go"], + importpath = "github.com/pingcap/tidb/pkg/disttask/importinto/mock", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/backend", + "@org_uber_go_mock//gomock", + ], +) diff --git a/disttask/importinto/mock/import_mock.go b/pkg/disttask/importinto/mock/import_mock.go similarity index 94% rename from disttask/importinto/mock/import_mock.go rename to pkg/disttask/importinto/mock/import_mock.go index e5db685538d32..bf82a01bb63c3 100644 --- a/disttask/importinto/mock/import_mock.go +++ b/pkg/disttask/importinto/mock/import_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/disttask/importinto (interfaces: MiniTaskExecutor) +// Source: github.com/pingcap/tidb/pkg/disttask/importinto (interfaces: MiniTaskExecutor) // Package mock is a generated GoMock package. package mock diff --git a/pkg/disttask/importinto/planner.go b/pkg/disttask/importinto/planner.go new file mode 100644 index 0000000000000..292ef218b9a07 --- /dev/null +++ b/pkg/disttask/importinto/planner.go @@ -0,0 +1,506 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/hex" + "encoding/json" + "math" + "strconv" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/br/pkg/lightning/backend/external" + "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/br/pkg/lightning/config" + verify "github.com/pingcap/tidb/br/pkg/lightning/verification" + "github.com/pingcap/tidb/br/pkg/storage" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +var ( + _ planner.LogicalPlan = &LogicalPlan{} + _ planner.PipelineSpec = &ImportSpec{} + _ planner.PipelineSpec = &PostProcessSpec{} +) + +// LogicalPlan represents a logical plan for import into. +type LogicalPlan struct { + JobID int64 + Plan importer.Plan + Stmt string + EligibleInstances []*infosync.ServerInfo + ChunkMap map[int32][]Chunk +} + +// ToTaskMeta converts the logical plan to task meta. +func (p *LogicalPlan) ToTaskMeta() ([]byte, error) { + taskMeta := TaskMeta{ + JobID: p.JobID, + Plan: p.Plan, + Stmt: p.Stmt, + EligibleInstances: p.EligibleInstances, + ChunkMap: p.ChunkMap, + } + return json.Marshal(taskMeta) +} + +// FromTaskMeta converts the task meta to logical plan. +func (p *LogicalPlan) FromTaskMeta(bs []byte) error { + var taskMeta TaskMeta + if err := json.Unmarshal(bs, &taskMeta); err != nil { + return errors.Trace(err) + } + p.JobID = taskMeta.JobID + p.Plan = taskMeta.Plan + p.Stmt = taskMeta.Stmt + p.EligibleInstances = taskMeta.EligibleInstances + p.ChunkMap = taskMeta.ChunkMap + return nil +} + +// ToPhysicalPlan converts the logical plan to physical plan. +func (p *LogicalPlan) ToPhysicalPlan(planCtx planner.PlanCtx) (*planner.PhysicalPlan, error) { + physicalPlan := &planner.PhysicalPlan{} + inputLinks := make([]planner.LinkSpec, 0) + addSpecs := func(specs []planner.PipelineSpec) { + for i, spec := range specs { + physicalPlan.AddProcessor(planner.ProcessorSpec{ + ID: i, + Pipeline: spec, + Output: planner.OutputSpec{ + Links: []planner.LinkSpec{ + { + ProcessorID: len(specs), + }, + }, + }, + Step: planCtx.NextTaskStep, + }) + inputLinks = append(inputLinks, planner.LinkSpec{ + ProcessorID: i, + }) + } + } + // physical plan only needs to be generated once. + // However, our current implementation requires generating it for each step. + // we only generate needed plans for the next step. + switch planCtx.NextTaskStep { + case StepImport, StepEncodeAndSort: + specs, err := generateImportSpecs(planCtx.Ctx, p) + if err != nil { + return nil, err + } + + addSpecs(specs) + case StepMergeSort: + specs, err := generateMergeSortSpecs(planCtx) + if err != nil { + return nil, err + } + + addSpecs(specs) + case StepWriteAndIngest: + specs, err := generateWriteIngestSpecs(planCtx, p) + if err != nil { + return nil, err + } + + addSpecs(specs) + case StepPostProcess: + physicalPlan.AddProcessor(planner.ProcessorSpec{ + ID: len(inputLinks), + Input: planner.InputSpec{ + ColumnTypes: []byte{ + // Checksum_crc64_xor, Total_kvs, Total_bytes, ReadRowCnt, LoadedRowCnt, ColSizeMap + mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeLonglong, mysql.TypeJSON, + }, + Links: inputLinks, + }, + Pipeline: &PostProcessSpec{ + Schema: p.Plan.DBName, + Table: p.Plan.TableInfo.Name.L, + }, + Step: planCtx.NextTaskStep, + }) + } + + return physicalPlan, nil +} + +// ImportSpec is the specification of an import pipeline. +type ImportSpec struct { + ID int32 + Plan importer.Plan + Chunks []Chunk +} + +// ToSubtaskMeta converts the import spec to subtask meta. +func (s *ImportSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { + importStepMeta := ImportStepMeta{ + ID: s.ID, + Chunks: s.Chunks, + } + return json.Marshal(importStepMeta) +} + +// WriteIngestSpec is the specification of a write-ingest pipeline. +type WriteIngestSpec struct { + *WriteIngestStepMeta +} + +// ToSubtaskMeta converts the write-ingest spec to subtask meta. +func (s *WriteIngestSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { + return json.Marshal(s.WriteIngestStepMeta) +} + +// MergeSortSpec is the specification of a merge-sort pipeline. +type MergeSortSpec struct { + *MergeSortStepMeta +} + +// ToSubtaskMeta converts the merge-sort spec to subtask meta. +func (s *MergeSortSpec) ToSubtaskMeta(planner.PlanCtx) ([]byte, error) { + return json.Marshal(s.MergeSortStepMeta) +} + +// PostProcessSpec is the specification of a post process pipeline. +type PostProcessSpec struct { + // for checksum request + Schema string + Table string +} + +// ToSubtaskMeta converts the post process spec to subtask meta. +func (*PostProcessSpec) ToSubtaskMeta(planCtx planner.PlanCtx) ([]byte, error) { + encodeStep := getStepOfEncode(planCtx.GlobalSort) + subtaskMetas := make([]*ImportStepMeta, 0, len(planCtx.PreviousSubtaskMetas)) + for _, bs := range planCtx.PreviousSubtaskMetas[encodeStep] { + var subtaskMeta ImportStepMeta + if err := json.Unmarshal(bs, &subtaskMeta); err != nil { + return nil, errors.Trace(err) + } + subtaskMetas = append(subtaskMetas, &subtaskMeta) + } + var localChecksum verify.KVChecksum + maxIDs := make(map[autoid.AllocatorType]int64, 3) + for _, subtaskMeta := range subtaskMetas { + checksum := verify.MakeKVChecksum(subtaskMeta.Checksum.Size, subtaskMeta.Checksum.KVs, subtaskMeta.Checksum.Sum) + localChecksum.Add(&checksum) + + for key, val := range subtaskMeta.MaxIDs { + if maxIDs[key] < val { + maxIDs[key] = val + } + } + } + postProcessStepMeta := &PostProcessStepMeta{ + Checksum: Checksum{ + Size: localChecksum.SumSize(), + KVs: localChecksum.SumKVS(), + Sum: localChecksum.Sum(), + }, + MaxIDs: maxIDs, + } + return json.Marshal(postProcessStepMeta) +} + +func buildControllerForPlan(p *LogicalPlan) (*importer.LoadDataController, error) { + return buildController(&p.Plan, p.Stmt) +} + +func buildController(plan *importer.Plan, stmt string) (*importer.LoadDataController, error) { + idAlloc := kv.NewPanickingAllocators(0) + tbl, err := tables.TableFromMeta(idAlloc, plan.TableInfo) + if err != nil { + return nil, err + } + + astArgs, err := importer.ASTArgsFromStmt(stmt) + if err != nil { + return nil, err + } + controller, err := importer.NewLoadDataController(plan, tbl, astArgs) + if err != nil { + return nil, err + } + return controller, nil +} + +func generateImportSpecs(ctx context.Context, p *LogicalPlan) ([]planner.PipelineSpec, error) { + var chunkMap map[int32][]Chunk + if len(p.ChunkMap) > 0 { + chunkMap = p.ChunkMap + } else { + controller, err2 := buildControllerForPlan(p) + if err2 != nil { + return nil, err2 + } + if err2 = controller.InitDataFiles(ctx); err2 != nil { + return nil, err2 + } + + engineCheckpoints, err2 := controller.PopulateChunks(ctx) + if err2 != nil { + return nil, err2 + } + chunkMap = toChunkMap(engineCheckpoints) + } + importSpecs := make([]planner.PipelineSpec, 0, len(chunkMap)) + for id := range chunkMap { + if id == common.IndexEngineID { + continue + } + importSpec := &ImportSpec{ + ID: id, + Plan: p.Plan, + Chunks: chunkMap[id], + } + importSpecs = append(importSpecs, importSpec) + } + return importSpecs, nil +} + +func skipMergeSort(kvGroup string, stats []external.MultipleFilesStat) bool { + failpoint.Inject("forceMergeSort", func(val failpoint.Value) { + in := val.(string) + if in == kvGroup || in == "*" { + failpoint.Return(false) + } + }) + return external.GetMaxOverlappingTotal(stats) <= external.MergeSortOverlapThreshold +} + +func generateMergeSortSpecs(planCtx planner.PlanCtx) ([]planner.PipelineSpec, error) { + step := external.MergeSortFileCountStep + result := make([]planner.PipelineSpec, 0, 16) + kvMetas, err := getSortedKVMetasOfEncodeStep(planCtx.PreviousSubtaskMetas[StepEncodeAndSort]) + if err != nil { + return nil, err + } + for kvGroup, kvMeta := range kvMetas { + length := len(kvMeta.DataFiles) + if skipMergeSort(kvGroup, kvMeta.MultipleFilesStats) { + logutil.Logger(planCtx.Ctx).Info("skip merge sort for kv group", + zap.Int64("task-id", planCtx.TaskID), + zap.String("kv-group", kvGroup)) + continue + } + for start := 0; start < length; start += step { + end := start + step + if end > length { + end = length + } + result = append(result, &MergeSortSpec{ + MergeSortStepMeta: &MergeSortStepMeta{ + KVGroup: kvGroup, + DataFiles: kvMeta.DataFiles[start:end], + }, + }) + } + } + return result, nil +} + +func generateWriteIngestSpecs(planCtx planner.PlanCtx, p *LogicalPlan) ([]planner.PipelineSpec, error) { + ctx := planCtx.Ctx + controller, err2 := buildControllerForPlan(p) + if err2 != nil { + return nil, err2 + } + if err2 = controller.InitDataStore(ctx); err2 != nil { + return nil, err2 + } + // kvMetas contains data kv meta and all index kv metas. + // each kvMeta will be split into multiple range group individually, + // i.e. data and index kv will NOT be in the same subtask. + kvMetas, err := getSortedKVMetasForIngest(planCtx) + if err != nil { + return nil, err + } + failpoint.Inject("mockWriteIngestSpecs", func() { + failpoint.Return([]planner.PipelineSpec{ + &WriteIngestSpec{ + WriteIngestStepMeta: &WriteIngestStepMeta{ + KVGroup: dataKVGroup, + }, + }, + &WriteIngestSpec{ + WriteIngestStepMeta: &WriteIngestStepMeta{ + KVGroup: "1", + }, + }, + }, nil) + }) + specs := make([]planner.PipelineSpec, 0, 16) + for kvGroup, kvMeta := range kvMetas { + splitter, err1 := getRangeSplitter(ctx, controller.GlobalSortStore, kvMeta) + if err1 != nil { + return nil, err1 + } + + err1 = func() error { + defer func() { + err2 := splitter.Close() + if err2 != nil { + logutil.Logger(ctx).Warn("close range splitter failed", zap.Error(err2)) + } + }() + startKey := tidbkv.Key(kvMeta.MinKey) + var endKey tidbkv.Key + for { + endKeyOfGroup, dataFiles, statFiles, rangeSplitKeys, err2 := splitter.SplitOneRangesGroup() + if err2 != nil { + return err2 + } + if len(endKeyOfGroup) == 0 { + endKey = tidbkv.Key(kvMeta.MaxKey).Next() + } else { + endKey = tidbkv.Key(endKeyOfGroup).Clone() + } + logutil.Logger(ctx).Info("kv range as subtask", + zap.String("startKey", hex.EncodeToString(startKey)), + zap.String("endKey", hex.EncodeToString(endKey))) + if startKey.Cmp(endKey) >= 0 { + return errors.Errorf("invalid kv range, startKey: %s, endKey: %s", + hex.EncodeToString(startKey), hex.EncodeToString(endKey)) + } + // each subtask will write and ingest one range group + m := &WriteIngestStepMeta{ + KVGroup: kvGroup, + SortedKVMeta: external.SortedKVMeta{ + MinKey: startKey, + MaxKey: endKey, + DataFiles: dataFiles, + StatFiles: statFiles, + // this is actually an estimate, we don't know the exact size of the data + TotalKVSize: uint64(config.DefaultBatchSize), + }, + RangeSplitKeys: rangeSplitKeys, + RangeSplitSize: splitter.GetRangeSplitSize(), + } + specs = append(specs, &WriteIngestSpec{m}) + + startKey = endKey + if len(endKeyOfGroup) == 0 { + break + } + } + return nil + }() + if err1 != nil { + return nil, err1 + } + } + return specs, nil +} + +func getSortedKVMetasOfEncodeStep(subTaskMetas [][]byte) (map[string]*external.SortedKVMeta, error) { + dataKVMeta := &external.SortedKVMeta{} + indexKVMetas := make(map[int64]*external.SortedKVMeta) + for _, subTaskMeta := range subTaskMetas { + var stepMeta ImportStepMeta + err := json.Unmarshal(subTaskMeta, &stepMeta) + if err != nil { + return nil, errors.Trace(err) + } + dataKVMeta.Merge(stepMeta.SortedDataMeta) + for indexID, sortedIndexMeta := range stepMeta.SortedIndexMetas { + if item, ok := indexKVMetas[indexID]; !ok { + indexKVMetas[indexID] = sortedIndexMeta + } else { + item.Merge(sortedIndexMeta) + } + } + } + res := make(map[string]*external.SortedKVMeta, 1+len(indexKVMetas)) + res[dataKVGroup] = dataKVMeta + for indexID, item := range indexKVMetas { + res[strconv.Itoa(int(indexID))] = item + } + return res, nil +} + +func getSortedKVMetasOfMergeStep(subTaskMetas [][]byte) (map[string]*external.SortedKVMeta, error) { + result := make(map[string]*external.SortedKVMeta, len(subTaskMetas)) + for _, subTaskMeta := range subTaskMetas { + var stepMeta MergeSortStepMeta + err := json.Unmarshal(subTaskMeta, &stepMeta) + if err != nil { + return nil, errors.Trace(err) + } + meta, ok := result[stepMeta.KVGroup] + if !ok { + result[stepMeta.KVGroup] = &stepMeta.SortedKVMeta + continue + } + meta.Merge(&stepMeta.SortedKVMeta) + } + return result, nil +} + +func getSortedKVMetasForIngest(planCtx planner.PlanCtx) (map[string]*external.SortedKVMeta, error) { + kvMetasOfMergeSort, err := getSortedKVMetasOfMergeStep(planCtx.PreviousSubtaskMetas[StepMergeSort]) + if err != nil { + return nil, err + } + kvMetasOfEncodeStep, err := getSortedKVMetasOfEncodeStep(planCtx.PreviousSubtaskMetas[StepEncodeAndSort]) + if err != nil { + return nil, err + } + for kvGroup, kvMeta := range kvMetasOfEncodeStep { + // only part of kv files are merge sorted. we need to merge kv metas that + // are not merged into the kvMetasOfMergeSort. + if skipMergeSort(kvGroup, kvMeta.MultipleFilesStats) { + if _, ok := kvMetasOfMergeSort[kvGroup]; ok { + // this should not happen, because we only generate merge sort + // subtasks for those kv groups with MaxOverlappingTotal > MergeSortOverlapThreshold + logutil.Logger(planCtx.Ctx).Error("kv group of encode step conflict with merge sort step") + return nil, errors.New("kv group of encode step conflict with merge sort step") + } + kvMetasOfMergeSort[kvGroup] = kvMeta + } + } + return kvMetasOfMergeSort, nil +} + +func getRangeSplitter(ctx context.Context, store storage.ExternalStorage, kvMeta *external.SortedKVMeta) ( + *external.RangeSplitter, error) { + regionSplitSize, regionSplitKeys, err := importer.GetRegionSplitSizeKeys(ctx) + if err != nil { + logutil.Logger(ctx).Warn("fail to get region split size and keys", zap.Error(err)) + } + regionSplitSize = max(regionSplitSize, int64(config.SplitRegionSize)) + regionSplitKeys = max(regionSplitKeys, int64(config.SplitRegionKeys)) + logutil.Logger(ctx).Info("split kv range with split size and keys", + zap.Int64("region-split-size", regionSplitSize), + zap.Int64("region-split-keys", regionSplitKeys)) + + return external.NewRangeSplitter( + ctx, kvMeta.DataFiles, kvMeta.StatFiles, store, + int64(config.DefaultBatchSize), int64(math.MaxInt64), + regionSplitSize, regionSplitKeys, + ) +} diff --git a/pkg/disttask/importinto/planner_test.go b/pkg/disttask/importinto/planner_test.go new file mode 100644 index 0000000000000..a88e444ddd49c --- /dev/null +++ b/pkg/disttask/importinto/planner_test.go @@ -0,0 +1,262 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/br/pkg/lightning/backend/external" + "github.com/pingcap/tidb/pkg/disttask/framework/planner" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/stretchr/testify/require" +) + +func TestLogicalPlan(t *testing.T) { + logicalPlan := &LogicalPlan{ + JobID: 1, + Plan: importer.Plan{}, + Stmt: `IMPORT INTO db.tb FROM 'gs://test-load/*.csv?endpoint=xxx'`, + EligibleInstances: []*infosync.ServerInfo{{ID: "1"}}, + ChunkMap: map[int32][]Chunk{1: {{Path: "gs://test-load/1.csv"}}}, + } + bs, err := logicalPlan.ToTaskMeta() + require.NoError(t, err) + plan := &LogicalPlan{} + require.NoError(t, plan.FromTaskMeta(bs)) + require.Equal(t, logicalPlan, plan) +} + +func TestToPhysicalPlan(t *testing.T) { + chunkID := int32(1) + logicalPlan := &LogicalPlan{ + JobID: 1, + Plan: importer.Plan{ + DBName: "db", + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("tb"), + }, + }, + Stmt: `IMPORT INTO db.tb FROM 'gs://test-load/*.csv?endpoint=xxx'`, + EligibleInstances: []*infosync.ServerInfo{{ID: "1"}}, + ChunkMap: map[int32][]Chunk{chunkID: {{Path: "gs://test-load/1.csv"}}}, + } + planCtx := planner.PlanCtx{ + NextTaskStep: StepImport, + } + physicalPlan, err := logicalPlan.ToPhysicalPlan(planCtx) + require.NoError(t, err) + plan := &planner.PhysicalPlan{ + Processors: []planner.ProcessorSpec{ + { + ID: 0, + Pipeline: &ImportSpec{ + ID: chunkID, + Plan: logicalPlan.Plan, + Chunks: logicalPlan.ChunkMap[chunkID], + }, + Output: planner.OutputSpec{ + Links: []planner.LinkSpec{ + { + ProcessorID: 1, + }, + }, + }, + Step: StepImport, + }, + }, + } + require.Equal(t, plan, physicalPlan) + + subtaskMetas1, err := physicalPlan.ToSubtaskMetas(planCtx, StepImport) + require.NoError(t, err) + subtaskMeta1 := ImportStepMeta{ + ID: chunkID, + Chunks: logicalPlan.ChunkMap[chunkID], + } + bs, err := json.Marshal(subtaskMeta1) + require.NoError(t, err) + require.Equal(t, [][]byte{bs}, subtaskMetas1) + + subtaskMeta1.Checksum = Checksum{Size: 1, KVs: 2, Sum: 3} + bs, err = json.Marshal(subtaskMeta1) + require.NoError(t, err) + planCtx = planner.PlanCtx{ + NextTaskStep: StepPostProcess, + } + physicalPlan, err = logicalPlan.ToPhysicalPlan(planCtx) + require.NoError(t, err) + subtaskMetas2, err := physicalPlan.ToSubtaskMetas(planner.PlanCtx{ + PreviousSubtaskMetas: map[int64][][]byte{ + StepImport: {bs}, + }, + }, StepPostProcess) + require.NoError(t, err) + subtaskMeta2 := PostProcessStepMeta{ + Checksum: Checksum{Size: 1, KVs: 2, Sum: 3}, + MaxIDs: map[autoid.AllocatorType]int64{}, + } + bs, err = json.Marshal(subtaskMeta2) + require.NoError(t, err) + require.Equal(t, [][]byte{bs}, subtaskMetas2) +} + +func genEncodeStepMetas(t *testing.T, cnt int) [][]byte { + stepMetaBytes := make([][]byte, 0, cnt) + for i := 0; i < cnt; i++ { + prefix := fmt.Sprintf("d_%d_", i) + idxPrefix := fmt.Sprintf("i1_%d_", i) + meta := &ImportStepMeta{ + SortedDataMeta: &external.SortedKVMeta{ + MinKey: []byte(prefix + "a"), + MaxKey: []byte(prefix + "c"), + TotalKVSize: 12, + DataFiles: []string{prefix + "/1"}, + StatFiles: []string{prefix + "/1.stat"}, + MultipleFilesStats: []external.MultipleFilesStat{ + { + Filenames: [][2]string{ + {prefix + "/1", prefix + "/1.stat"}, + }, + }, + }, + }, + SortedIndexMetas: map[int64]*external.SortedKVMeta{ + 1: { + MinKey: []byte(idxPrefix + "a"), + MaxKey: []byte(idxPrefix + "c"), + TotalKVSize: 12, + DataFiles: []string{idxPrefix + "/1"}, + StatFiles: []string{idxPrefix + "/1.stat"}, + MultipleFilesStats: []external.MultipleFilesStat{ + { + Filenames: [][2]string{ + {idxPrefix + "/1", idxPrefix + "/1.stat"}, + }, + }, + }, + }, + }, + } + bytes, err := json.Marshal(meta) + require.NoError(t, err) + stepMetaBytes = append(stepMetaBytes, bytes) + } + return stepMetaBytes +} + +func TestGenerateMergeSortSpecs(t *testing.T) { + stepBak := external.MergeSortFileCountStep + external.MergeSortFileCountStep = 2 + t.Cleanup(func() { + external.MergeSortFileCountStep = stepBak + }) + // force merge sort for data kv + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort", `return("data")`)) + t.Cleanup(func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort")) + }) + encodeStepMetaBytes := genEncodeStepMetas(t, 3) + planCtx := planner.PlanCtx{ + Ctx: context.Background(), + TaskID: 1, + PreviousSubtaskMetas: map[int64][][]byte{ + StepEncodeAndSort: encodeStepMetaBytes, + }, + } + specs, err := generateMergeSortSpecs(planCtx) + require.NoError(t, err) + require.Len(t, specs, 2) + require.Len(t, specs[0].(*MergeSortSpec).DataFiles, 2) + require.Equal(t, "data", specs[0].(*MergeSortSpec).KVGroup) + require.Equal(t, "d_0_/1", specs[0].(*MergeSortSpec).DataFiles[0]) + require.Equal(t, "d_1_/1", specs[0].(*MergeSortSpec).DataFiles[1]) + require.Equal(t, "data", specs[1].(*MergeSortSpec).KVGroup) + require.Len(t, specs[1].(*MergeSortSpec).DataFiles, 1) + require.Equal(t, "d_2_/1", specs[1].(*MergeSortSpec).DataFiles[0]) +} + +func genMergeStepMetas(t *testing.T, cnt int) [][]byte { + stepMetaBytes := make([][]byte, 0, cnt) + for i := 0; i < cnt; i++ { + prefix := fmt.Sprintf("x_%d_", i) + meta := &MergeSortStepMeta{ + KVGroup: "data", + SortedKVMeta: external.SortedKVMeta{ + MinKey: []byte(prefix + "a"), + MaxKey: []byte(prefix + "c"), + TotalKVSize: 12, + DataFiles: []string{prefix + "/1"}, + StatFiles: []string{prefix + "/1.stat"}, + MultipleFilesStats: []external.MultipleFilesStat{ + { + Filenames: [][2]string{ + {prefix + "/1", prefix + "/1.stat"}, + }, + }, + }, + }, + } + bytes, err := json.Marshal(meta) + require.NoError(t, err) + stepMetaBytes = append(stepMetaBytes, bytes) + } + return stepMetaBytes +} + +func TestGetSortedKVMetas(t *testing.T) { + encodeStepMetaBytes := genEncodeStepMetas(t, 3) + kvMetas, err := getSortedKVMetasOfEncodeStep(encodeStepMetaBytes) + require.NoError(t, err) + require.Len(t, kvMetas, 2) + require.Contains(t, kvMetas, "data") + require.Contains(t, kvMetas, "1") + // just check meta is merged, won't check all fields + require.Equal(t, []byte("d_0_a"), kvMetas["data"].MinKey) + require.Equal(t, []byte("d_2_c"), kvMetas["data"].MaxKey) + require.Equal(t, []byte("i1_0_a"), kvMetas["1"].MinKey) + require.Equal(t, []byte("i1_2_c"), kvMetas["1"].MaxKey) + + mergeStepMetas := genMergeStepMetas(t, 3) + kvMetas2, err := getSortedKVMetasOfMergeStep(mergeStepMetas) + require.NoError(t, err) + require.Len(t, kvMetas2, 1) + require.Equal(t, []byte("x_0_a"), kvMetas2["data"].MinKey) + require.Equal(t, []byte("x_2_c"), kvMetas2["data"].MaxKey) + + // force merge sort for data kv + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort", `return("data")`)) + t.Cleanup(func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort")) + }) + allKVMetas, err := getSortedKVMetasForIngest(planner.PlanCtx{ + PreviousSubtaskMetas: map[int64][][]byte{ + StepEncodeAndSort: encodeStepMetaBytes, + StepMergeSort: mergeStepMetas, + }, + }) + require.NoError(t, err) + require.Len(t, allKVMetas, 2) + require.Equal(t, []byte("x_0_a"), allKVMetas["data"].MinKey) + require.Equal(t, []byte("x_2_c"), allKVMetas["data"].MaxKey) + require.Equal(t, []byte("i1_0_a"), allKVMetas["1"].MinKey) + require.Equal(t, []byte("i1_2_c"), allKVMetas["1"].MaxKey) +} diff --git a/disttask/importinto/proto.go b/pkg/disttask/importinto/proto.go similarity index 97% rename from disttask/importinto/proto.go rename to pkg/disttask/importinto/proto.go index 81ceb31093c99..8f3ba5019bf77 100644 --- a/disttask/importinto/proto.go +++ b/pkg/disttask/importinto/proto.go @@ -22,10 +22,10 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/meta/autoid" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/meta/autoid" ) // Steps of IMPORT INTO, each step is represented by one or multiple subtasks. diff --git a/pkg/disttask/importinto/scheduler.go b/pkg/disttask/importinto/scheduler.go new file mode 100644 index 0000000000000..1609242efc52c --- /dev/null +++ b/pkg/disttask/importinto/scheduler.go @@ -0,0 +1,519 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importinto + +import ( + "context" + "encoding/json" + "sync" + "time" + + "github.com/docker/go-units" + "github.com/google/uuid" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/br/pkg/lightning/backend" + "github.com/pingcap/tidb/br/pkg/lightning/backend/external" + "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/br/pkg/lightning/config" + "github.com/pingcap/tidb/br/pkg/lightning/log" + "github.com/pingcap/tidb/br/pkg/lightning/metric" + "github.com/pingcap/tidb/br/pkg/lightning/verification" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler/execute" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +// importStepExecutor is a executor for import step. +// SubtaskExecutor is equivalent to a Lightning instance. +type importStepExecutor struct { + taskID int64 + taskMeta *TaskMeta + tableImporter *importer.TableImporter + sharedVars sync.Map + logger *zap.Logger + + indexMemorySizeLimit uint64 + + importCtx context.Context + importCancel context.CancelFunc + wg sync.WaitGroup +} + +func getTableImporter(ctx context.Context, taskID int64, taskMeta *TaskMeta) (*importer.TableImporter, error) { + idAlloc := kv.NewPanickingAllocators(0) + tbl, err := tables.TableFromMeta(idAlloc, taskMeta.Plan.TableInfo) + if err != nil { + return nil, err + } + astArgs, err := importer.ASTArgsFromStmt(taskMeta.Stmt) + if err != nil { + return nil, err + } + controller, err := importer.NewLoadDataController(&taskMeta.Plan, tbl, astArgs) + if err != nil { + return nil, err + } + if err = controller.InitDataStore(ctx); err != nil { + return nil, err + } + + return importer.NewTableImporter(&importer.JobImportParam{ + GroupCtx: ctx, + Progress: asyncloaddata.NewProgress(false), + Job: &asyncloaddata.Job{}, + }, controller, taskID) +} + +func (s *importStepExecutor) Init(ctx context.Context) error { + s.logger.Info("init subtask env") + tableImporter, err := getTableImporter(ctx, s.taskID, s.taskMeta) + if err != nil { + return err + } + s.tableImporter = tableImporter + + // we need this sub context since Cleanup which wait on this routine is called + // before parent context is canceled in normal flow. + s.importCtx, s.importCancel = context.WithCancel(ctx) + // only need to check disk quota when we are using local sort. + if s.tableImporter.IsLocalSort() { + s.wg.Add(1) + go func() { + defer s.wg.Done() + s.tableImporter.CheckDiskQuota(s.importCtx) + }() + } + s.indexMemorySizeLimit = getWriterMemorySizeLimit(s.tableImporter.Plan) + s.logger.Info("memory size limit per index writer per concurrency", + zap.String("limit", units.BytesSize(float64(s.indexMemorySizeLimit)))) + return nil +} + +func (s *importStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { + logger := s.logger.With(zap.Int64("subtask-id", subtask.ID)) + task := log.BeginTask(logger, "run subtask") + defer func() { + task.End(zapcore.ErrorLevel, err) + }() + bs := subtask.Meta + var subtaskMeta ImportStepMeta + err = json.Unmarshal(bs, &subtaskMeta) + if err != nil { + return errors.Trace(err) + } + + var dataEngine, indexEngine *backend.OpenedEngine + if s.tableImporter.IsLocalSort() { + dataEngine, err = s.tableImporter.OpenDataEngine(ctx, subtaskMeta.ID) + if err != nil { + return err + } + // Unlike in Lightning, we start an index engine for each subtask, + // whereas previously there was only a single index engine globally. + // This is because the scheduler currently does not have a post-processing mechanism. + // If we import the index in `cleanupSubtaskEnv`, the dispatcher will not wait for the import to complete. + // Multiple index engines may suffer performance degradation due to range overlap. + // These issues will be alleviated after we integrate s3 sorter. + // engineID = -1, -2, -3, ... + indexEngine, err = s.tableImporter.OpenIndexEngine(ctx, common.IndexEngineID-subtaskMeta.ID) + if err != nil { + return err + } + } + sharedVars := &SharedVars{ + TableImporter: s.tableImporter, + DataEngine: dataEngine, + IndexEngine: indexEngine, + Progress: asyncloaddata.NewProgress(false), + Checksum: &verification.KVChecksum{}, + SortedDataMeta: &external.SortedKVMeta{}, + SortedIndexMetas: make(map[int64]*external.SortedKVMeta), + } + s.sharedVars.Store(subtaskMeta.ID, sharedVars) + + source := operator.NewSimpleDataChannel(make(chan *importStepMinimalTask)) + op := newEncodeAndSortOperator(ctx, s, sharedVars, subtask.ID, s.indexMemorySizeLimit) + op.SetSource(source) + pipeline := operator.NewAsyncPipeline(op) + if err = pipeline.Execute(); err != nil { + return err + } + +outer: + for _, chunk := range subtaskMeta.Chunks { + // TODO: current workpool impl doesn't drain the input channel, it will + // just return on context cancel(error happened), so we add this select. + select { + case source.Channel() <- &importStepMinimalTask{ + Plan: s.taskMeta.Plan, + Chunk: chunk, + SharedVars: sharedVars, + }: + case <-op.Done(): + break outer + } + } + source.Finish() + + return pipeline.Close() +} + +func (s *importStepExecutor) OnFinished(ctx context.Context, subtask *proto.Subtask) error { + var subtaskMeta ImportStepMeta + if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { + return errors.Trace(err) + } + s.logger.Info("on subtask finished", zap.Int32("engine-id", subtaskMeta.ID)) + + val, ok := s.sharedVars.Load(subtaskMeta.ID) + if !ok { + return errors.Errorf("sharedVars %d not found", subtaskMeta.ID) + } + sharedVars, ok := val.(*SharedVars) + if !ok { + return errors.Errorf("sharedVars %d not found", subtaskMeta.ID) + } + + var dataKVCount int64 + if s.tableImporter.IsLocalSort() { + // TODO: we should close and cleanup engine in all case, since there's no checkpoint. + s.logger.Info("import data engine", zap.Int32("engine-id", subtaskMeta.ID)) + closedDataEngine, err := sharedVars.DataEngine.Close(ctx) + if err != nil { + return err + } + dataKVCount, err = s.tableImporter.ImportAndCleanup(ctx, closedDataEngine) + if err != nil { + return err + } + + s.logger.Info("import index engine", zap.Int32("engine-id", subtaskMeta.ID)) + if closedEngine, err := sharedVars.IndexEngine.Close(ctx); err != nil { + return err + } else if _, err := s.tableImporter.ImportAndCleanup(ctx, closedEngine); err != nil { + return err + } + } + // there's no imported dataKVCount on this stage when using global sort. + + sharedVars.mu.Lock() + defer sharedVars.mu.Unlock() + subtaskMeta.Checksum.Sum = sharedVars.Checksum.Sum() + subtaskMeta.Checksum.KVs = sharedVars.Checksum.SumKVS() + subtaskMeta.Checksum.Size = sharedVars.Checksum.SumSize() + subtaskMeta.Result = Result{ + LoadedRowCnt: uint64(dataKVCount), + ColSizeMap: sharedVars.Progress.GetColSize(), + } + allocators := sharedVars.TableImporter.Allocators() + subtaskMeta.MaxIDs = map[autoid.AllocatorType]int64{ + autoid.RowIDAllocType: allocators.Get(autoid.RowIDAllocType).Base(), + autoid.AutoIncrementType: allocators.Get(autoid.AutoIncrementType).Base(), + autoid.AutoRandomType: allocators.Get(autoid.AutoRandomType).Base(), + } + subtaskMeta.SortedDataMeta = sharedVars.SortedDataMeta + subtaskMeta.SortedIndexMetas = sharedVars.SortedIndexMetas + s.sharedVars.Delete(subtaskMeta.ID) + newMeta, err := json.Marshal(subtaskMeta) + if err != nil { + return errors.Trace(err) + } + subtask.Meta = newMeta + return nil +} + +func (s *importStepExecutor) Cleanup(_ context.Context) (err error) { + s.logger.Info("cleanup subtask env") + s.importCancel() + s.wg.Wait() + return s.tableImporter.Close() +} + +func (s *importStepExecutor) Rollback(context.Context) error { + // TODO: add rollback + s.logger.Info("rollback") + return nil +} + +type mergeSortStepExecutor struct { + scheduler.EmptySubtaskExecutor + taskID int64 + taskMeta *TaskMeta + logger *zap.Logger + controller *importer.LoadDataController + // subtask of a task is run in serial now, so we don't need lock here. + // change to SyncMap when we support parallel subtask in the future. + subtaskSortedKVMeta *external.SortedKVMeta +} + +var _ execute.SubtaskExecutor = &mergeSortStepExecutor{} + +func (m *mergeSortStepExecutor) Init(ctx context.Context) error { + controller, err := buildController(&m.taskMeta.Plan, m.taskMeta.Stmt) + if err != nil { + return err + } + if err = controller.InitDataStore(ctx); err != nil { + return err + } + m.controller = controller + return nil +} + +func (m *mergeSortStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { + logger := m.logger.With(zap.Int64("subtask-id", subtask.ID)) + task := log.BeginTask(logger, "run subtask") + defer func() { + task.End(zapcore.ErrorLevel, err) + }() + + sm := &MergeSortStepMeta{} + err = json.Unmarshal(subtask.Meta, sm) + if err != nil { + return errors.Trace(err) + } + + var mu sync.Mutex + m.subtaskSortedKVMeta = &external.SortedKVMeta{} + onClose := func(summary *external.WriterSummary) { + mu.Lock() + defer mu.Unlock() + m.subtaskSortedKVMeta.MergeSummary(summary) + } + + writerID := uuid.New().String() + prefix := subtaskPrefix(m.taskID, subtask.ID) + + return external.MergeOverlappingFiles( + ctx, + sm.DataFiles, + m.controller.GlobalSortStore, + 64*1024, + prefix, + writerID, + 256*size.MB, + 8*1024, + 1*size.MB, + 8*1024, + onClose) +} + +func (m *mergeSortStepExecutor) OnFinished(_ context.Context, subtask *proto.Subtask) error { + var subtaskMeta MergeSortStepMeta + if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { + return errors.Trace(err) + } + subtaskMeta.SortedKVMeta = *m.subtaskSortedKVMeta + m.subtaskSortedKVMeta = nil + newMeta, err := json.Marshal(subtaskMeta) + if err != nil { + return errors.Trace(err) + } + subtask.Meta = newMeta + return nil +} + +type writeAndIngestStepExecutor struct { + taskID int64 + taskMeta *TaskMeta + logger *zap.Logger + tableImporter *importer.TableImporter +} + +var _ execute.SubtaskExecutor = &writeAndIngestStepExecutor{} + +func (e *writeAndIngestStepExecutor) Init(ctx context.Context) error { + tableImporter, err := getTableImporter(ctx, e.taskID, e.taskMeta) + if err != nil { + return err + } + e.tableImporter = tableImporter + return nil +} + +func (e *writeAndIngestStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { + sm := &WriteIngestStepMeta{} + err = json.Unmarshal(subtask.Meta, sm) + if err != nil { + return errors.Trace(err) + } + + logger := e.logger.With(zap.Int64("subtask-id", subtask.ID), + zap.String("kv-group", sm.KVGroup)) + task := log.BeginTask(logger, "run subtask") + defer func() { + task.End(zapcore.ErrorLevel, err) + }() + + _, engineUUID := backend.MakeUUID("", subtask.ID) + localBackend := e.tableImporter.Backend() + err = localBackend.CloseEngine(ctx, &backend.EngineConfig{ + External: &backend.ExternalEngineConfig{ + StorageURI: e.taskMeta.Plan.CloudStorageURI, + DataFiles: sm.DataFiles, + StatFiles: sm.StatFiles, + MinKey: sm.MinKey, + MaxKey: sm.MaxKey, + SplitKeys: sm.RangeSplitKeys, + RegionSplitSize: sm.RangeSplitSize, + TotalFileSize: int64(sm.TotalKVSize), + TotalKVCount: 0, + }, + }, engineUUID) + if err != nil { + return err + } + return localBackend.ImportEngine(ctx, engineUUID, int64(config.SplitRegionSize), int64(config.SplitRegionKeys)) +} + +func (e *writeAndIngestStepExecutor) OnFinished(_ context.Context, subtask *proto.Subtask) error { + var subtaskMeta WriteIngestStepMeta + if err := json.Unmarshal(subtask.Meta, &subtaskMeta); err != nil { + return errors.Trace(err) + } + if subtaskMeta.KVGroup != dataKVGroup { + return nil + } + + // only data kv group has loaded row count + _, engineUUID := backend.MakeUUID("", subtask.ID) + localBackend := e.tableImporter.Backend() + _, kvCount := localBackend.GetExternalEngineKVStatistics(engineUUID) + subtaskMeta.Result.LoadedRowCnt = uint64(kvCount) + + newMeta, err := json.Marshal(subtaskMeta) + if err != nil { + return errors.Trace(err) + } + subtask.Meta = newMeta + return nil +} + +func (e *writeAndIngestStepExecutor) Cleanup(_ context.Context) (err error) { + e.logger.Info("cleanup subtask env") + return e.tableImporter.Close() +} + +func (e *writeAndIngestStepExecutor) Rollback(context.Context) error { + e.logger.Info("rollback") + return nil +} + +type postStepExecutor struct { + scheduler.EmptySubtaskExecutor + taskID int64 + taskMeta *TaskMeta + logger *zap.Logger +} + +var _ execute.SubtaskExecutor = &postStepExecutor{} + +func (p *postStepExecutor) RunSubtask(ctx context.Context, subtask *proto.Subtask) (err error) { + logger := p.logger.With(zap.Int64("subtask-id", subtask.ID)) + task := log.BeginTask(logger, "run subtask") + defer func() { + task.End(zapcore.ErrorLevel, err) + }() + stepMeta := PostProcessStepMeta{} + if err = json.Unmarshal(subtask.Meta, &stepMeta); err != nil { + return errors.Trace(err) + } + failpoint.Inject("waitBeforePostProcess", func() { + time.Sleep(5 * time.Second) + }) + return postProcess(ctx, p.taskMeta, &stepMeta, logger) +} + +type importScheduler struct { + *scheduler.BaseScheduler +} + +func newImportScheduler(ctx context.Context, id string, task *proto.Task, taskTable scheduler.TaskTable) scheduler.Scheduler { + s := &importScheduler{ + BaseScheduler: scheduler.NewBaseScheduler(ctx, id, task.ID, taskTable), + } + s.BaseScheduler.Extension = s + return s +} + +func (s *importScheduler) Run(ctx context.Context, task *proto.Task) error { + metrics := metricsManager.getOrCreateMetrics(task.ID) + defer metricsManager.unregister(task.ID) + subCtx := metric.WithCommonMetric(ctx, metrics) + return s.BaseScheduler.Run(subCtx, task) +} + +func (*importScheduler) IsIdempotent(*proto.Subtask) bool { + // import don't have conflict detection and resolution now, so it's ok + // to import data twice. + return true +} + +func (*importScheduler) GetSubtaskExecutor(_ context.Context, task *proto.Task, _ *execute.Summary) (execute.SubtaskExecutor, error) { + taskMeta := TaskMeta{} + if err := json.Unmarshal(task.Meta, &taskMeta); err != nil { + return nil, errors.Trace(err) + } + logger := logutil.BgLogger().With( + zap.String("type", proto.ImportInto), + zap.Int64("task-id", task.ID), + zap.String("step", stepStr(task.Step)), + ) + logger.Info("create step scheduler") + + switch task.Step { + case StepImport, StepEncodeAndSort: + return &importStepExecutor{ + taskID: task.ID, + taskMeta: &taskMeta, + logger: logger, + }, nil + case StepMergeSort: + return &mergeSortStepExecutor{ + taskID: task.ID, + taskMeta: &taskMeta, + logger: logger, + }, nil + case StepWriteAndIngest: + return &writeAndIngestStepExecutor{ + taskID: task.ID, + taskMeta: &taskMeta, + logger: logger, + }, nil + case StepPostProcess: + return &postStepExecutor{ + taskID: task.ID, + taskMeta: &taskMeta, + logger: logger, + }, nil + default: + return nil, errors.Errorf("unknown step %d for import task %d", task.Step, task.ID) + } +} + +func init() { + scheduler.RegisterTaskType(proto.ImportInto, newImportScheduler) +} diff --git a/disttask/importinto/subtask_executor.go b/pkg/disttask/importinto/subtask_executor.go similarity index 95% rename from disttask/importinto/subtask_executor.go rename to pkg/disttask/importinto/subtask_executor.go index 9f964c49182a6..5b99ad8b0508f 100644 --- a/disttask/importinto/subtask_executor.go +++ b/pkg/disttask/importinto/subtask_executor.go @@ -28,15 +28,15 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/log" verify "github.com/pingcap/tidb/br/pkg/lightning/verification" - tidb "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + tidb "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" ) diff --git a/disttask/importinto/subtask_executor_test.go b/pkg/disttask/importinto/subtask_executor_test.go similarity index 81% rename from disttask/importinto/subtask_executor_test.go rename to pkg/disttask/importinto/subtask_executor_test.go index 4596ffc795aa2..61fb3bc0602db 100644 --- a/disttask/importinto/subtask_executor_test.go +++ b/pkg/disttask/importinto/subtask_executor_test.go @@ -22,12 +22,12 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/failpoint" verify "github.com/pingcap/tidb/br/pkg/lightning/verification" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" ) @@ -63,9 +63,9 @@ func TestChecksumTable(t *testing.T) { require.NoError(t, err) require.True(t, remoteChecksum.IsEqual(&localChecksum)) - _ = failpoint.Enable("github.com/pingcap/tidb/disttask/importinto/errWhenChecksum", `return(true)`) + _ = failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/importinto/errWhenChecksum", `return(true)`) defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/errWhenChecksum") + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/errWhenChecksum") }() remoteChecksum, err = importinto.TestChecksumTable(ctx, mgr, taskMeta, logutil.BgLogger()) require.NoError(t, err) diff --git a/disttask/importinto/wrapper.go b/pkg/disttask/importinto/wrapper.go similarity index 100% rename from disttask/importinto/wrapper.go rename to pkg/disttask/importinto/wrapper.go diff --git a/disttask/importinto/wrapper_test.go b/pkg/disttask/importinto/wrapper_test.go similarity index 100% rename from disttask/importinto/wrapper_test.go rename to pkg/disttask/importinto/wrapper_test.go diff --git a/pkg/disttask/operator/BUILD.bazel b/pkg/disttask/operator/BUILD.bazel new file mode 100644 index 0000000000000..3d2ee19362566 --- /dev/null +++ b/pkg/disttask/operator/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "operator", + srcs = [ + "compose.go", + "operator.go", + "pipeline.go", + "wrapper.go", + ], + importpath = "github.com/pingcap/tidb/pkg/disttask/operator", + visibility = ["//visibility:public"], + deps = [ + "//pkg/resourcemanager/pool/workerpool", + "//pkg/resourcemanager/util", + "@org_golang_x_sync//errgroup", + ], +) + +go_test( + name = "operator_test", + timeout = "short", + srcs = ["pipeline_test.go"], + embed = [":operator"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/disttask/operator/compose.go b/pkg/disttask/operator/compose.go similarity index 100% rename from disttask/operator/compose.go rename to pkg/disttask/operator/compose.go diff --git a/disttask/operator/operator.go b/pkg/disttask/operator/operator.go similarity index 96% rename from disttask/operator/operator.go rename to pkg/disttask/operator/operator.go index c1a3d43bb805c..56a3d9f62f81a 100644 --- a/disttask/operator/operator.go +++ b/pkg/disttask/operator/operator.go @@ -18,8 +18,8 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/resourcemanager/pool/workerpool" - "github.com/pingcap/tidb/resourcemanager/util" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" ) // Operator is the basic operation unit in the task execution. diff --git a/disttask/operator/pipeline.go b/pkg/disttask/operator/pipeline.go similarity index 100% rename from disttask/operator/pipeline.go rename to pkg/disttask/operator/pipeline.go diff --git a/disttask/operator/pipeline_test.go b/pkg/disttask/operator/pipeline_test.go similarity index 100% rename from disttask/operator/pipeline_test.go rename to pkg/disttask/operator/pipeline_test.go diff --git a/disttask/operator/wrapper.go b/pkg/disttask/operator/wrapper.go similarity index 100% rename from disttask/operator/wrapper.go rename to pkg/disttask/operator/wrapper.go diff --git a/pkg/domain/BUILD.bazel b/pkg/domain/BUILD.bazel new file mode 100644 index 0000000000000..ff23c7c574850 --- /dev/null +++ b/pkg/domain/BUILD.bazel @@ -0,0 +1,166 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "domain", + srcs = [ + "domain.go", + "domain_sysvars.go", + "domainctx.go", + "extract.go", + "historical_stats.go", + "optimize_trace.go", + "plan_replayer.go", + "plan_replayer_dump.go", + "runaway.go", + "schema_checker.go", + "schema_validator.go", + "sysvar_cache.go", + "test_helper.go", + "topn_slow_query.go", + ], + importpath = "github.com/pingcap/tidb/pkg/domain", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/streamhelper", + "//br/pkg/streamhelper/daemon", + "//pkg/bindinfo", + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/placement", + "//pkg/ddl/schematracker", + "//pkg/ddl/util", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/scheduler", + "//pkg/disttask/framework/storage", + "//pkg/domain/globalconfigsync", + "//pkg/domain/infosync", + "//pkg/domain/metrics", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/infoschema", + "//pkg/infoschema/metrics", + "//pkg/infoschema/perfschema", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/metrics", + "//pkg/owner", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/privilege/privileges", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle", + "//pkg/statistics/handle/storage", + "//pkg/store/helper", + "//pkg/telemetry", + "//pkg/ttl/cache", + "//pkg/ttl/sqlbuilder", + "//pkg/ttl/ttlworker", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/dbterror", + "//pkg/util/disttask", + "//pkg/util/domainutil", + "//pkg/util/engine", + "//pkg/util/etcd", + "//pkg/util/execdetails", + "//pkg/util/expensivequery", + "//pkg/util/gctuner", + "//pkg/util/globalconn", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/memoryusagealarm", + "//pkg/util/printer", + "//pkg/util/replayer", + "//pkg/util/servermemorylimit", + "//pkg/util/sqlexec", + "//pkg/util/syncutil", + "@com_github_burntsushi_toml//:toml", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/pdpb", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//txnkv/transaction", + "@com_github_tikv_pd_client//:client", + "@com_github_tikv_pd_client//resource_group/controller", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//backoff", + "@org_golang_google_grpc//keepalive", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "domain_test", + timeout = "short", + srcs = [ + "db_test.go", + "domain_test.go", + "domain_utils_test.go", + "domainctx_test.go", + "extract_test.go", + "main_test.go", + "plan_replayer_handle_test.go", + "plan_replayer_test.go", + "schema_checker_test.go", + "schema_validator_test.go", + "session_pool_test.go", + "topn_slow_query_test.go", + ], + embed = [":domain"], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/mock", + "//pkg/util/replayer", + "//pkg/util/stmtsummary/v2:stmtsummary", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//txnkv/transaction", + "@com_github_tikv_pd_client//:client", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/domain/db_test.go b/pkg/domain/db_test.go new file mode 100644 index 0000000000000..bcb0d3f53c3dc --- /dev/null +++ b/pkg/domain/db_test.go @@ -0,0 +1,127 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package domain_test + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/stretchr/testify/require" +) + +func TestDomainSession(t *testing.T) { + lease := 50 * time.Millisecond + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + session.SetSchemaLease(lease) + domain, err := session.BootstrapSession(store) + require.NoError(t, err) + ddl.DisableTiFlashPoll(domain.DDL()) + defer domain.Close() + + // for NotifyUpdatePrivilege + createRoleSQL := `CREATE ROLE 'test'@'localhost';` + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + _, err = se.Execute(context.Background(), createRoleSQL) + require.NoError(t, err) + + // for BindHandle + _, err = se.Execute(context.Background(), "use test") + require.NoError(t, err) + _, err = se.Execute(context.Background(), "drop table if exists t") + require.NoError(t, err) + _, err = se.Execute(context.Background(), "create table t(i int, s varchar(20), index index_t(i, s))") + require.NoError(t, err) + _, err = se.Execute(context.Background(), "create global binding for select * from t where i>100 using select * from t use index(index_t) where i>100") + require.NoError(t, err) +} + +func TestNormalSessionPool(t *testing.T) { + lease := 100 * time.Millisecond + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + session.SetSchemaLease(lease) + domain, err := session.BootstrapSession(store) + require.NoError(t, err) + defer domain.Close() + info, err1 := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) + require.NoError(t, err1) + conf := config.GetGlobalConfig() + conf.Socket = "" + conf.Port = 0 + conf.Status.ReportStatus = false + svr, err := server.NewServer(conf, nil) + require.NoError(t, err) + svr.SetDomain(domain) + info.SetSessionManager(svr) + + pool := domain.SysSessionPool() + se, err := pool.Get() + require.NoError(t, err) + require.NotEmpty(t, se) + require.Equal(t, svr.InternalSessionExists(se), true) + + pool.Put(se) + require.Equal(t, svr.InternalSessionExists(se), false) +} + +func TestAbnormalSessionPool(t *testing.T) { + lease := 100 * time.Millisecond + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + session.SetSchemaLease(lease) + domain, err := session.BootstrapSession(store) + require.NoError(t, err) + defer domain.Close() + info, err1 := infosync.GlobalInfoSyncerInit(context.Background(), "t", func() uint64 { return 1 }, nil, nil, nil, keyspace.CodecV1, true) + require.NoError(t, err1) + conf := config.GetGlobalConfig() + conf.Socket = "" + conf.Port = 0 + conf.Status.ReportStatus = false + svr, err := server.NewServer(conf, nil) + require.NoError(t, err) + svr.SetDomain(domain) + info.SetSessionManager(svr) + + pool := domain.SysSessionPool() + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/mockSessionPoolReturnError", "return") + se, err := pool.Get() + require.Error(t, err) + failpoint.Disable("github.com/pingcap/tidb/pkg/domain/mockSessionPoolReturnError") + require.Equal(t, svr.InternalSessionExists(se), false) +} diff --git a/domain/domain.go b/pkg/domain/domain.go similarity index 97% rename from domain/domain.go rename to pkg/domain/domain.go index 6b81aa77c86ff..c97a18e3c7e95 100644 --- a/domain/domain.go +++ b/pkg/domain/domain.go @@ -32,59 +32,59 @@ import ( "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" "github.com/pingcap/log" - "github.com/pingcap/tidb/bindinfo" "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/streamhelper/daemon" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/schematracker" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/domain/globalconfigsync" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - infoschema_metrics "github.com/pingcap/tidb/infoschema/metrics" - "github.com/pingcap/tidb/infoschema/perfschema" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/ttl/ttlworker" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - disttaskutil "github.com/pingcap/tidb/util/disttask" - "github.com/pingcap/tidb/util/domainutil" - "github.com/pingcap/tidb/util/engine" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/expensivequery" - "github.com/pingcap/tidb/util/gctuner" - "github.com/pingcap/tidb/util/globalconn" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/memoryusagealarm" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/servermemorylimit" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/domain/globalconfigsync" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + infoschema_metrics "github.com/pingcap/tidb/pkg/infoschema/metrics" + "github.com/pingcap/tidb/pkg/infoschema/perfschema" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/ttl/ttlworker" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + disttaskutil "github.com/pingcap/tidb/pkg/util/disttask" + "github.com/pingcap/tidb/pkg/util/domainutil" + "github.com/pingcap/tidb/pkg/util/engine" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/expensivequery" + "github.com/pingcap/tidb/pkg/util/gctuner" + "github.com/pingcap/tidb/pkg/util/globalconn" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/memoryusagealarm" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/servermemorylimit" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/transaction" pd "github.com/tikv/pd/client" diff --git a/domain/domain_sysvars.go b/pkg/domain/domain_sysvars.go similarity index 98% rename from domain/domain_sysvars.go rename to pkg/domain/domain_sysvars.go index 53efea57c9426..fd60670e57e60 100644 --- a/domain/domain_sysvars.go +++ b/pkg/domain/domain_sysvars.go @@ -19,7 +19,7 @@ import ( "strconv" "time" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessionctx/variable" pd "github.com/tikv/pd/client" ) diff --git a/domain/domain_test.go b/pkg/domain/domain_test.go similarity index 81% rename from domain/domain_test.go rename to pkg/domain/domain_test.go index 259e50804fa11..00e365015998a 100644 --- a/domain/domain_test.go +++ b/pkg/domain/domain_test.go @@ -28,18 +28,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" @@ -59,7 +59,7 @@ func TestInfo(t *testing.T) { t.Skip("ETCD use ip:port as unix socket address, skip when it is unavailable.") } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/FailPlacement", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/FailPlacement", `return(true)`)) s, err := mockstore.NewMockStore() require.NoError(t, err) @@ -90,11 +90,11 @@ func TestInfo(t *testing.T) { ddl.WithLease(ddlLease), ) ddl.DisableTiFlashPoll(dom.ddl) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/MockReplaceDDL", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/NoDDLDispatchLoop", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/MockReplaceDDL", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/NoDDLDispatchLoop", `return(true)`)) require.NoError(t, dom.Init(ddlLease, sysMockFactory, nil)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/NoDDLDispatchLoop")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/MockReplaceDDL")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/NoDDLDispatchLoop")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/MockReplaceDDL")) // Test for GetServerInfo and GetServerInfoByID. ddlID := dom.ddl.GetID() @@ -119,9 +119,9 @@ func TestInfo(t *testing.T) { require.Equalf(t, info.ID, infos[ddlID].ID, "server one info %v, info %v", infos[ddlID], info) // Test the scene where syncer.Done() gets the information. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/syncer/ErrorMockSessionDone", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/syncer/ErrorMockSessionDone", `return(true)`)) <-dom.ddl.SchemaSyncer().Done() - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/syncer/ErrorMockSessionDone")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/syncer/ErrorMockSessionDone")) time.Sleep(15 * time.Millisecond) syncerStarted := false for i := 0; i < 1000; i++ { @@ -166,7 +166,7 @@ func TestInfo(t *testing.T) { err = dom.refreshServerIDTTL(goCtx) require.NoError(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/FailPlacement")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/FailPlacement")) } func TestStatWorkRecoverFromPanic(t *testing.T) { @@ -284,8 +284,8 @@ func TestClosestReplicaReadChecker(t *testing.T) { }, } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetServerInfo", makeFailpointRes(mockedAllServerInfos["s2"]))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetServerInfo", makeFailpointRes(mockedAllServerInfos["s2"]))) stores := []*metapb.Store{ { @@ -376,7 +376,7 @@ func TestClosestReplicaReadChecker(t *testing.T) { }, } pdClient.stores = stores - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) cases := []struct { id string matches bool @@ -403,7 +403,7 @@ func TestClosestReplicaReadChecker(t *testing.T) { }, } for _, c := range cases { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetServerInfo", makeFailpointRes(mockedAllServerInfos[c.id]))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetServerInfo", makeFailpointRes(mockedAllServerInfos[c.id]))) variable.SetEnableAdaptiveReplicaRead(!c.matches) err = dom.checkReplicaRead(ctx, pdClient) require.Nil(t, err) @@ -411,8 +411,8 @@ func TestClosestReplicaReadChecker(t *testing.T) { } variable.SetEnableAdaptiveReplicaRead(true) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/mockGetServerInfo")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetServerInfo")) } type mockInfoPdClient struct { diff --git a/domain/domain_utils_test.go b/pkg/domain/domain_utils_test.go similarity index 92% rename from domain/domain_utils_test.go rename to pkg/domain/domain_utils_test.go index 6af9e1b3fc98a..8b80684c85349 100644 --- a/domain/domain_utils_test.go +++ b/pkg/domain/domain_utils_test.go @@ -17,8 +17,8 @@ package domain import ( "testing" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/stretchr/testify/require" ) diff --git a/domain/domainctx.go b/pkg/domain/domainctx.go similarity index 96% rename from domain/domainctx.go rename to pkg/domain/domainctx.go index e972b7a7c5b9d..67aeac5876bb5 100644 --- a/domain/domainctx.go +++ b/pkg/domain/domainctx.go @@ -15,7 +15,7 @@ package domain import ( - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx" ) // domainKeyType is a dummy type to avoid naming collision in context. diff --git a/domain/domainctx_test.go b/pkg/domain/domainctx_test.go similarity index 95% rename from domain/domainctx_test.go rename to pkg/domain/domainctx_test.go index b8d971b948ee4..0efd628665169 100644 --- a/domain/domainctx_test.go +++ b/pkg/domain/domainctx_test.go @@ -17,7 +17,7 @@ package domain import ( "testing" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/domain/extract.go b/pkg/domain/extract.go new file mode 100644 index 0000000000000..391adb46b6f42 --- /dev/null +++ b/pkg/domain/extract.go @@ -0,0 +1,526 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package domain + +import ( + "archive/zip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "math/rand" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/BurntSushi/toml" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +const ( + // ExtractMetaFile indicates meta file for extract + ExtractMetaFile = "extract_meta.txt" +) + +const ( + // ExtractTaskType indicates type of extract task + ExtractTaskType = "taskType" + // ExtractPlanTaskSkipStats indicates skip stats for extract plan task + ExtractPlanTaskSkipStats = "SkipStats" +) + +// ExtractType indicates type +type ExtractType uint8 + +const ( + // ExtractPlanType indicates extract plan task + ExtractPlanType ExtractType = iota +) + +func taskTypeToString(t ExtractType) string { + switch t { + case ExtractPlanType: + return "Plan" + } + return "Unknown" +} + +// ExtractHandle handles the extractWorker to run extract the information task like Plan or any others. +// extractHandle will provide 2 mode for extractWorker: +// 1. submit a background extract task, the response will be returned after the task is started to be solved +// 2. submit a task and wait until the task is solved, the result will be returned to the response. +type ExtractHandle struct { + worker *extractWorker +} + +// NewExtractHandler new extract handler +func NewExtractHandler(sctxs []sessionctx.Context) *ExtractHandle { + h := &ExtractHandle{} + h.worker = newExtractWorker(sctxs[0], false) + return h +} + +// ExtractTask extract tasks +func (h *ExtractHandle) ExtractTask(ctx context.Context, task *ExtractTask) (string, error) { + // TODO: support background job later + if task.IsBackgroundJob { + return "", nil + } + return h.worker.extractTask(ctx, task) +} + +type extractWorker struct { + ctx context.Context + sctx sessionctx.Context + isBackgroundWorker bool + sync.Mutex +} + +// ExtractTask indicates task +type ExtractTask struct { + ExtractType ExtractType + IsBackgroundJob bool + + // Param for Extract Plan + SkipStats bool + UseHistoryView bool + + // variables for plan task type + Begin time.Time + End time.Time +} + +// NewExtractPlanTask returns extract plan task +func NewExtractPlanTask(begin, end time.Time) *ExtractTask { + return &ExtractTask{ + Begin: begin, + End: end, + ExtractType: ExtractPlanType, + } +} + +func newExtractWorker(sctx sessionctx.Context, isBackgroundWorker bool) *extractWorker { + return &extractWorker{ + sctx: sctx, + isBackgroundWorker: isBackgroundWorker, + } +} + +func (w *extractWorker) extractTask(ctx context.Context, task *ExtractTask) (string, error) { + switch task.ExtractType { + case ExtractPlanType: + return w.extractPlanTask(ctx, task) + } + return "", errors.New("unknown extract task") +} + +func (w *extractWorker) extractPlanTask(ctx context.Context, task *ExtractTask) (string, error) { + if task.UseHistoryView && !config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return "", errors.New("tidb_stmt_summary_enable_persistent should be enabled for extract task") + } + records, err := w.collectRecords(ctx, task) + if err != nil { + logutil.BgLogger().Error("collect stmt summary records failed for extract plan task", zap.Error(err)) + return "", err + } + p, err := w.packageExtractPlanRecords(ctx, records) + if err != nil { + logutil.BgLogger().Error("package stmt summary records failed for extract plan task", zap.Error(err)) + return "", err + } + return w.dumpExtractPlanPackage(task, p) +} + +func (w *extractWorker) collectRecords(ctx context.Context, task *ExtractTask) (map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, error) { + w.Lock() + defer w.Unlock() + exec := w.sctx.(sqlexec.RestrictedSQLExecutor) + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + sourceTable := "STATEMENTS_SUMMARY_HISTORY" + if !task.UseHistoryView { + sourceTable = "STATEMENTS_SUMMARY" + } + rows, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("SELECT STMT_TYPE, DIGEST, PLAN_DIGEST,QUERY_SAMPLE_TEXT, BINARY_PLAN, TABLE_NAMES, SAMPLE_USER FROM INFORMATION_SCHEMA.%s WHERE SUMMARY_END_TIME > '%s' AND SUMMARY_BEGIN_TIME < '%s'", + sourceTable, task.Begin.Format(types.TimeFormat), task.End.Format(types.TimeFormat))) + if err != nil { + return nil, err + } + collectMap := make(map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, 0) + for _, row := range rows { + record := &stmtSummaryHistoryRecord{} + record.stmtType = row.GetString(0) + record.digest = row.GetString(1) + record.planDigest = row.GetString(2) + record.sql = row.GetString(3) + record.binaryPlan = row.GetString(4) + tableNames := row.GetString(5) + key := stmtSummaryHistoryKey{ + digest: record.digest, + planDigest: record.planDigest, + } + record.userName = row.GetString(6) + record.tables = make([]tableNamePair, 0) + setRecord, err := w.handleTableNames(tableNames, record) + if err != nil { + return nil, err + } + if setRecord && checkRecordValid(record) { + collectMap[key] = record + } + } + return collectMap, nil +} + +func (w *extractWorker) handleTableNames(tableNames string, record *stmtSummaryHistoryRecord) (bool, error) { + is := GetDomain(w.sctx).InfoSchema() + for _, t := range strings.Split(tableNames, ",") { + names := strings.Split(t, ".") + if len(names) != 2 { + return false, nil + } + dbName := names[0] + tblName := names[1] + record.schemaName = dbName + // skip internal schema record + switch strings.ToLower(record.schemaName) { + case util.PerformanceSchemaName.L, util.InformationSchemaName.L, util.MetricSchemaName.L, "mysql": + return false, nil + } + exists := is.TableExists(model.NewCIStr(dbName), model.NewCIStr(tblName)) + if !exists { + return false, nil + } + t, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) + if err != nil { + return false, err + } + record.tables = append(record.tables, tableNamePair{DBName: dbName, TableName: tblName, IsView: t.Meta().IsView()}) + } + return true, nil +} + +func checkRecordValid(r *stmtSummaryHistoryRecord) bool { + if r.stmtType != "Select" { + return false + } + if r.schemaName == "" { + return false + } + if r.planDigest == "" { + return false + } + return true +} + +func (w *extractWorker) packageExtractPlanRecords(ctx context.Context, records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord) (*extractPlanPackage, error) { + p := &extractPlanPackage{} + p.records = records + p.tables = make(map[tableNamePair]struct{}, 0) + for _, record := range records { + // skip the sql which has been cut off + if strings.Contains(record.sql, "(len:") { + record.skip = true + continue + } + plan, err := w.decodeBinaryPlan(ctx, record.binaryPlan) + if err != nil { + return nil, err + } + record.plan = plan + for _, tbl := range record.tables { + p.tables[tbl] = struct{}{} + } + } + if err := w.handleIsView(ctx, p); err != nil { + return nil, err + } + return p, nil +} + +func (w *extractWorker) handleIsView(ctx context.Context, p *extractPlanPackage) error { + is := GetDomain(w.sctx).InfoSchema() + tne := &tableNameExtractor{ + ctx: ctx, + executor: w.sctx.(sqlexec.RestrictedSQLExecutor), + is: is, + curDB: model.NewCIStr(""), + names: make(map[tableNamePair]struct{}), + cteNames: make(map[string]struct{}), + } + for v := range p.tables { + if v.IsView { + v, err := is.TableByName(model.NewCIStr(v.DBName), model.NewCIStr(v.TableName)) + if err != nil { + return err + } + sql := v.Meta().View.SelectStmt + node, err := tne.executor.ParseWithParams(tne.ctx, sql) + if err != nil { + return err + } + node.Accept(tne) + } + } + if tne.err != nil { + return tne.err + } + r := tne.getTablesAndViews() + for t := range r { + p.tables[t] = struct{}{} + } + return nil +} + +func (w *extractWorker) decodeBinaryPlan(ctx context.Context, bPlan string) (string, error) { + exec := w.sctx.(sqlexec.RestrictedSQLExecutor) + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + rows, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("SELECT tidb_decode_binary_plan('%s')", bPlan)) + if err != nil { + return "", err + } + plan := rows[0].GetString(0) + return strings.Trim(plan, "\n"), nil +} + +// dumpExtractPlanPackage will dump the information about sqls collected in stmt_summary_history +// The files will be organized into the following format: +/* + |-extract_meta.txt + |-meta.txt + |-config.toml + |-variables.toml + |-bindings.sql + |-schema + | |-schema_meta.txt + | |-db1.table1.schema.txt + | |-db2.table2.schema.txt + | |-.... + |-view + | |-db1.view1.view.txt + | |-db2.view2.view.txt + | |-.... + |-stats + | |-stats1.json + | |-stats2.json + | |-.... + |-table_tiflash_replica.txt + |-sql + | |-digest1.sql + | |-digest2.sql + | |-.... + |-skippedSQLs + | |-digest1.sql + | |-... +*/ +func (w *extractWorker) dumpExtractPlanPackage(task *ExtractTask, p *extractPlanPackage) (name string, err error) { + f, name, err := GenerateExtractFile() + if err != nil { + return "", err + } + zw := zip.NewWriter(f) + defer func() { + if err != nil { + logutil.BgLogger().Error("dump extract plan task failed", zap.Error(err)) + } + if err1 := zw.Close(); err1 != nil { + logutil.BgLogger().Warn("close zip file failed", zap.String("file", name), zap.Error(err)) + } + if err1 := f.Close(); err1 != nil { + logutil.BgLogger().Warn("close file failed", zap.String("file", name), zap.Error(err)) + } + }() + + // Dump config + if err = dumpConfig(zw); err != nil { + return "", err + } + // Dump meta + if err = dumpMeta(zw); err != nil { + return "", err + } + // dump extract plan task meta + if err = dumpExtractMeta(task, zw); err != nil { + return "", err + } + // Dump Schema and View + if err = dumpSchemas(w.sctx, zw, p.tables); err != nil { + return "", err + } + // Dump tables tiflash replicas + if err = dumpTiFlashReplica(w.sctx, zw, p.tables); err != nil { + return "", err + } + // Dump variables + if err = dumpVariables(w.sctx, w.sctx.GetSessionVars(), zw); err != nil { + return "", err + } + // Dump global bindings + if err = dumpGlobalBindings(w.sctx, zw); err != nil { + return "", err + } + // Dump stats + if !task.SkipStats { + if _, err = dumpStats(zw, p.tables, GetDomain(w.sctx), 0); err != nil { + return "", err + } + } + // Dump sqls and plan + if err = dumpSQLRecords(p.records, zw); err != nil { + return "", err + } + return name, nil +} + +func dumpSQLRecords(records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord, zw *zip.Writer) error { + for key, record := range records { + if record.skip { + err := dumpSQLRecord(record, fmt.Sprintf("skippedSQLs/%v.json", key.digest), zw) + if err != nil { + return err + } + } else { + err := dumpSQLRecord(record, fmt.Sprintf("SQLs/%v.json", key.digest), zw) + if err != nil { + return err + } + } + } + return nil +} + +type singleSQLRecord struct { + Schema string `json:"schema"` + Plan string `json:"plan"` + SQL string `json:"sql"` + Digest string `json:"digest"` + BinaryPlan string `json:"binaryPlan"` + UserName string `json:"userName"` +} + +// dumpSQLRecord dumps sql records into one file for each record, the format is in json. +func dumpSQLRecord(record *stmtSummaryHistoryRecord, path string, zw *zip.Writer) error { + zf, err := zw.Create(path) + if err != nil { + return err + } + singleSQLRecord := &singleSQLRecord{ + Schema: record.schemaName, + Plan: record.plan, + SQL: record.sql, + Digest: record.digest, + BinaryPlan: record.binaryPlan, + UserName: record.userName, + } + content, err := json.Marshal(singleSQLRecord) + if err != nil { + return err + } + _, err = zf.Write(content) + if err != nil { + return err + } + return nil +} + +func dumpExtractMeta(task *ExtractTask, zw *zip.Writer) error { + cf, err := zw.Create(ExtractMetaFile) + if err != nil { + return errors.AddStack(err) + } + varMap := make(map[string]string) + varMap[ExtractTaskType] = taskTypeToString(task.ExtractType) + switch task.ExtractType { + case ExtractPlanType: + varMap[ExtractPlanTaskSkipStats] = strconv.FormatBool(task.SkipStats) + } + + if err := toml.NewEncoder(cf).Encode(varMap); err != nil { + return errors.AddStack(err) + } + return nil +} + +type extractPlanPackage struct { + tables map[tableNamePair]struct{} + records map[stmtSummaryHistoryKey]*stmtSummaryHistoryRecord +} + +type stmtSummaryHistoryKey struct { + digest string + planDigest string +} + +type stmtSummaryHistoryRecord struct { + stmtType string + schemaName string + tables []tableNamePair + digest string + planDigest string + sql string + binaryPlan string + userName string + + plan string + skip bool +} + +// GenerateExtractFile generates extract stmt file +func GenerateExtractFile() (*os.File, string, error) { + path := GetExtractTaskDirName() + err := os.MkdirAll(path, os.ModePerm) + if err != nil { + return nil, "", errors.AddStack(err) + } + fileName, err := generateExtractStmtFile() + if err != nil { + return nil, "", errors.AddStack(err) + } + zf, err := os.Create(filepath.Join(path, fileName)) + if err != nil { + return nil, "", errors.AddStack(err) + } + return zf, fileName, err +} + +func generateExtractStmtFile() (string, error) { + // Generate key and create zip file + time := time.Now().UnixNano() + b := make([]byte, 16) + //nolint: gosec + _, err := rand.Read(b) + if err != nil { + return "", err + } + key := base64.URLEncoding.EncodeToString(b) + return fmt.Sprintf("extract_%v_%v.zip", key, time), nil +} + +// GetExtractTaskDirName get extract dir name +func GetExtractTaskDirName() string { + tidbLogDir := filepath.Dir(config.GetGlobalConfig().Log.File.Filename) + return filepath.Join(tidbLogDir, "extract") +} diff --git a/pkg/domain/extract_test.go b/pkg/domain/extract_test.go new file mode 100644 index 0000000000000..02d0aa898238b --- /dev/null +++ b/pkg/domain/extract_test.go @@ -0,0 +1,109 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package domain_test + +import ( + "context" + "os" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/testkit" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + "github.com/stretchr/testify/require" +) + +func TestExtractPlanWithoutHistoryView(t *testing.T) { + _, dom := testkit.CreateMockStoreAndDomain(t) + extractHandler := dom.GetExtractHandle() + task := domain.NewExtractPlanTask(time.Now(), time.Now()) + task.UseHistoryView = false + _, err := extractHandler.ExtractTask(context.Background(), task) + require.NoError(t, err) +} + +func TestExtractWithoutStmtSummaryPersistedEnabled(t *testing.T) { + setupStmtSummary() + closeStmtSummary() + _, dom := testkit.CreateMockStoreAndDomain(t) + extractHandler := dom.GetExtractHandle() + task := domain.NewExtractPlanTask(time.Now(), time.Now()) + task.UseHistoryView = true + _, err := extractHandler.ExtractTask(context.Background(), task) + require.Error(t, err) +} + +func TestExtractHandlePlanTask(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := newTestKitWithRoot(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id int);") + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) + + // new testkit + tk = newTestKitWithRoot(t, store) + tk.MustExec("use test") + tk.MustQuery("select * from t") + startTime := time.Now() + time.Sleep(time.Second) + tk.MustQuery("select COUNT(*) FROM INFORMATION_SCHEMA.STATEMENTS_SUMMARY_HISTORY WHERE STMT_TYPE = 'Select' AND SCHEMA_NAME = 'test' AND TABLE_NAMES = 'test.t'").Check(testkit.Rows("1")) + time.Sleep(time.Second) + end := time.Now() + extractHandler := dom.GetExtractHandle() + task := domain.NewExtractPlanTask(startTime, end) + task.UseHistoryView = true + name, err := extractHandler.ExtractTask(context.Background(), task) + require.NoError(t, err) + require.True(t, len(name) > 0) +} + +func setupStmtSummary() { + stmtsummaryv2.Setup(&stmtsummaryv2.Config{ + Filename: "tidb-statements.log", + }) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = true + }) +} + +func closeStmtSummary() { + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = false + }) + stmtsummaryv2.GlobalStmtSummary.Close() + _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) +} + +func newTestKit(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + return tk +} + +func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := newTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + return tk +} diff --git a/pkg/domain/globalconfigsync/BUILD.bazel b/pkg/domain/globalconfigsync/BUILD.bazel new file mode 100644 index 0000000000000..e8c5604a8f7a6 --- /dev/null +++ b/pkg/domain/globalconfigsync/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "globalconfigsync", + srcs = ["globalconfig.go"], + importpath = "github.com/pingcap/tidb/pkg/domain/globalconfigsync", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/logutil", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "globalconfigsync_test", + timeout = "short", + srcs = ["globalconfig_test.go"], + flaky = True, + deps = [ + ":globalconfigsync", + "//pkg/kv", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_pd_client//:client", + "@io_etcd_go_etcd_tests_v3//integration", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/domain/globalconfigsync/globalconfig.go b/pkg/domain/globalconfigsync/globalconfig.go similarity index 97% rename from domain/globalconfigsync/globalconfig.go rename to pkg/domain/globalconfigsync/globalconfig.go index 35b4447189884..6d3bad4e88f66 100644 --- a/domain/globalconfigsync/globalconfig.go +++ b/pkg/domain/globalconfigsync/globalconfig.go @@ -17,7 +17,7 @@ package globalconfigsync import ( "context" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" pd "github.com/tikv/pd/client" "go.uber.org/zap" ) diff --git a/domain/globalconfigsync/globalconfig_test.go b/pkg/domain/globalconfigsync/globalconfig_test.go similarity index 94% rename from domain/globalconfigsync/globalconfig_test.go rename to pkg/domain/globalconfigsync/globalconfig_test.go index 086c38d626ec3..2ed7e341c8b07 100644 --- a/domain/globalconfigsync/globalconfig_test.go +++ b/pkg/domain/globalconfigsync/globalconfig_test.go @@ -20,11 +20,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain/globalconfigsync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/domain/globalconfigsync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" "go.etcd.io/etcd/tests/v3/integration" diff --git a/domain/historical_stats.go b/pkg/domain/historical_stats.go similarity index 92% rename from domain/historical_stats.go rename to pkg/domain/historical_stats.go index 8ac7b900c5748..d53739ddd7c32 100644 --- a/domain/historical_stats.go +++ b/pkg/domain/historical_stats.go @@ -17,11 +17,11 @@ package domain import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - domain_metrics "github.com/pingcap/tidb/domain/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/util/logutil" + domain_metrics "github.com/pingcap/tidb/pkg/domain/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/domain/infosync/BUILD.bazel b/pkg/domain/infosync/BUILD.bazel new file mode 100644 index 0000000000000..b128dfc06ee37 --- /dev/null +++ b/pkg/domain/infosync/BUILD.bazel @@ -0,0 +1,78 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "infosync", + srcs = [ + "error.go", + "info.go", + "label_manager.go", + "mock_info.go", + "placement_manager.go", + "region.go", + "resource_manager_client.go", + "schedule_manager.go", + "tiflash_manager.go", + ], + importpath = "github.com/pingcap/tidb/pkg/domain/infosync", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl/label", + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/variable", + "//pkg/store/helper", + "//pkg/tablecodec", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/pdapi", + "//pkg/util/syncutil", + "//pkg/util/versioninfo", + "@com_github_golang_protobuf//proto", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/meta_storagepb", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_pd_client//:client", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "infosync_test", + timeout = "short", + srcs = ["info_test.go"], + embed = [":infosync"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/keyspace", + "//pkg/parser/model", + "//pkg/testkit/testsetup", + "//pkg/util", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/domain/infosync/error.go b/pkg/domain/infosync/error.go new file mode 100644 index 0000000000000..d1d0ff0e36820 --- /dev/null +++ b/pkg/domain/infosync/error.go @@ -0,0 +1,28 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package infosync + +import ( + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +var ( + // ErrHTTPServiceError means we got a http response with a status code which is not '2xx' + ErrHTTPServiceError = dbterror.ClassDomain.NewStdErr( + errno.ErrHTTPServiceError, mysql.Message("HTTP request failed with status %s", nil), + ) +) diff --git a/domain/infosync/info.go b/pkg/domain/infosync/info.go similarity index 97% rename from domain/infosync/info.go rename to pkg/domain/infosync/info.go index 4eb1fc7e8b1c0..23ae93fdbb502 100644 --- a/domain/infosync/info.go +++ b/pkg/domain/infosync/info.go @@ -34,26 +34,26 @@ import ( "github.com/pingcap/failpoint" rmpb "github.com/pingcap/kvproto/pkg/resource_manager" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/helper" - util2 "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/helper" + util2 "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/versioninfo" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" pd "github.com/tikv/pd/client" diff --git a/domain/infosync/info_test.go b/pkg/domain/infosync/info_test.go similarity index 90% rename from domain/infosync/info_test.go rename to pkg/domain/infosync/info_test.go index ad3957481c282..e56aebb054d7a 100644 --- a/domain/infosync/info_test.go +++ b/pkg/domain/infosync/info_test.go @@ -26,12 +26,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit/testsetup" - util2 "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + util2 "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "go.etcd.io/etcd/tests/v3/integration" "go.uber.org/goleak" @@ -63,9 +63,9 @@ func TestTopology(t *testing.T) { client := cluster.RandClient() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockServerInfo", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockServerInfo", "return(true)")) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/domain/infosync/mockServerInfo") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/mockServerInfo") require.NoError(t, err) }() @@ -163,9 +163,9 @@ func TestPutBundlesRetry(t *testing.T) { t.Run("serviceErrorShouldNotRetry", func(t *testing.T) { require.NoError(t, PutRuleBundles(context.TODO(), []*placement.Bundle{{ID: bundle.ID}})) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError", "1*return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError")) }() err := PutRuleBundlesWithRetry(context.TODO(), []*placement.Bundle{bundle}, 3, time.Millisecond) @@ -179,9 +179,9 @@ func TestPutBundlesRetry(t *testing.T) { t.Run("nonServiceErrorShouldRetry", func(t *testing.T) { require.NoError(t, PutRuleBundles(context.TODO(), []*placement.Bundle{{ID: bundle.ID}})) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError", "3*return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError", "3*return(false)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError")) }() err := PutRuleBundlesWithRetry(context.TODO(), []*placement.Bundle{bundle}, 3, time.Millisecond) @@ -201,9 +201,9 @@ func TestPutBundlesRetry(t *testing.T) { t.Run("nonServiceErrorRetryAndFail", func(t *testing.T) { require.NoError(t, PutRuleBundles(context.TODO(), []*placement.Bundle{{ID: bundle.ID}})) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError", "4*return(false)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError", "4*return(false)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/infosync/putRuleBundlesError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/putRuleBundlesError")) }() err := PutRuleBundlesWithRetry(context.TODO(), []*placement.Bundle{bundle}, 3, time.Millisecond) diff --git a/domain/infosync/label_manager.go b/pkg/domain/infosync/label_manager.go similarity index 98% rename from domain/infosync/label_manager.go rename to pkg/domain/infosync/label_manager.go index 84babc3380f1e..30560346244e3 100644 --- a/domain/infosync/label_manager.go +++ b/pkg/domain/infosync/label_manager.go @@ -21,8 +21,8 @@ import ( "path" "sync" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/util/pdapi" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/domain/infosync/mock_info.go b/pkg/domain/infosync/mock_info.go similarity index 94% rename from domain/infosync/mock_info.go rename to pkg/domain/infosync/mock_info.go index 452267ab103cf..7e9dd2f057996 100644 --- a/domain/infosync/mock_info.go +++ b/pkg/domain/infosync/mock_info.go @@ -20,10 +20,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/util/versioninfo" ) // MockGlobalServerInfoManagerEntry is a mock global ServerInfoManager entry. diff --git a/domain/infosync/placement_manager.go b/pkg/domain/infosync/placement_manager.go similarity index 97% rename from domain/infosync/placement_manager.go rename to pkg/domain/infosync/placement_manager.go index 223dafd0467a3..3368869a4e38d 100644 --- a/domain/infosync/placement_manager.go +++ b/pkg/domain/infosync/placement_manager.go @@ -22,8 +22,8 @@ import ( "sync" "github.com/pingcap/log" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/util/pdapi" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/pkg/domain/infosync/region.go b/pkg/domain/infosync/region.go new file mode 100644 index 0000000000000..76134557a8595 --- /dev/null +++ b/pkg/domain/infosync/region.go @@ -0,0 +1,86 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package infosync + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/util/pdapi" +) + +// PlacementScheduleState is the returned third-valued state from GetReplicationState(). For convenience, the string of PD is deserialized into an enum first. +type PlacementScheduleState int + +const ( + // PlacementScheduleStatePending corresponds to "PENDING" from PD. + PlacementScheduleStatePending PlacementScheduleState = iota + // PlacementScheduleStateInProgress corresponds to "INPROGRESS" from PD. + PlacementScheduleStateInProgress + // PlacementScheduleStateScheduled corresponds to "REPLICATED" from PD. + PlacementScheduleStateScheduled +) + +func (t PlacementScheduleState) String() string { + switch t { + case PlacementScheduleStateScheduled: + return "SCHEDULED" + case PlacementScheduleStateInProgress: + return "INPROGRESS" + case PlacementScheduleStatePending: + return "PENDING" + default: + return "PENDING" + } +} + +// GetReplicationState is used to check if regions in the given keyranges are replicated from PD. +func GetReplicationState(ctx context.Context, startKey []byte, endKey []byte) (PlacementScheduleState, error) { + is, err := getGlobalInfoSyncer() + if err != nil { + return PlacementScheduleStatePending, err + } + + if is.etcdCli == nil { + return PlacementScheduleStatePending, nil + } + + addrs := is.etcdCli.Endpoints() + + if len(addrs) == 0 { + return PlacementScheduleStatePending, errors.Errorf("pd unavailable") + } + + res, err := doRequest(ctx, "GetReplicationState", addrs, fmt.Sprintf("%s/replicated?startKey=%s&endKey=%s", pdapi.Regions, hex.EncodeToString(startKey), hex.EncodeToString(endKey)), "GET", nil) + if err == nil && res != nil { + st := PlacementScheduleStatePending + // it should not fail + var state string + _ = json.Unmarshal(res, &state) + switch state { + case "REPLICATED": + st = PlacementScheduleStateScheduled + case "INPROGRESS": + st = PlacementScheduleStateInProgress + case "PENDING": + st = PlacementScheduleStatePending + } + return st, nil + } + return PlacementScheduleStatePending, err +} diff --git a/domain/infosync/resource_manager_client.go b/pkg/domain/infosync/resource_manager_client.go similarity index 98% rename from domain/infosync/resource_manager_client.go rename to pkg/domain/infosync/resource_manager_client.go index 72096094f0629..b183daebece5a 100644 --- a/domain/infosync/resource_manager_client.go +++ b/pkg/domain/infosync/resource_manager_client.go @@ -23,7 +23,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/pingcap/kvproto/pkg/meta_storagepb" rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" pd "github.com/tikv/pd/client" ) diff --git a/domain/infosync/schedule_manager.go b/pkg/domain/infosync/schedule_manager.go similarity index 98% rename from domain/infosync/schedule_manager.go rename to pkg/domain/infosync/schedule_manager.go index c237e266aea6e..7b71a8423edf7 100644 --- a/domain/infosync/schedule_manager.go +++ b/pkg/domain/infosync/schedule_manager.go @@ -22,7 +22,7 @@ import ( "sync" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/util/pdapi" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/domain/infosync/tiflash_manager.go b/pkg/domain/infosync/tiflash_manager.go similarity index 99% rename from domain/infosync/tiflash_manager.go rename to pkg/domain/infosync/tiflash_manager.go index 59ac0cf683baf..23c98b15df6d7 100644 --- a/domain/infosync/tiflash_manager.go +++ b/pkg/domain/infosync/tiflash_manager.go @@ -33,13 +33,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/tikv/client-go/v2/tikv" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" diff --git a/pkg/domain/main_test.go b/pkg/domain/main_test.go new file mode 100644 index 0000000000000..fe07ac0592f6f --- /dev/null +++ b/pkg/domain/main_test.go @@ -0,0 +1,36 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package domain_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + server.RunInGoTest = true + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/domain/metrics/BUILD.bazel b/pkg/domain/metrics/BUILD.bazel new file mode 100644 index 0000000000000..189665ed62daf --- /dev/null +++ b/pkg/domain/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/domain/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/domain/metrics/metrics.go b/pkg/domain/metrics/metrics.go new file mode 100644 index 0000000000000..2b19aaba82742 --- /dev/null +++ b/pkg/domain/metrics/metrics.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// domain metrics vars +var ( + GenerateHistoricalStatsSuccessCounter prometheus.Counter + GenerateHistoricalStatsFailedCounter prometheus.Counter + + PlanReplayerDumpTaskSuccess prometheus.Counter + PlanReplayerDumpTaskFailed prometheus.Counter + + PlanReplayerCaptureTaskSendCounter prometheus.Counter + PlanReplayerCaptureTaskDiscardCounter prometheus.Counter + + PlanReplayerRegisterTaskGauge prometheus.Gauge +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init domain metrics vars. +func InitMetricsVars() { + GenerateHistoricalStatsSuccessCounter = metrics.HistoricalStatsCounter.WithLabelValues("generate", "success") + GenerateHistoricalStatsFailedCounter = metrics.HistoricalStatsCounter.WithLabelValues("generate", "fail") + + PlanReplayerDumpTaskSuccess = metrics.PlanReplayerTaskCounter.WithLabelValues("dump", "success") + PlanReplayerDumpTaskFailed = metrics.PlanReplayerTaskCounter.WithLabelValues("dump", "fail") + + PlanReplayerCaptureTaskSendCounter = metrics.PlanReplayerTaskCounter.WithLabelValues("capture", "send") + PlanReplayerCaptureTaskDiscardCounter = metrics.PlanReplayerTaskCounter.WithLabelValues("capture", "discard") + + PlanReplayerRegisterTaskGauge = metrics.PlanReplayerRegisterTaskGauge +} diff --git a/domain/optimize_trace.go b/pkg/domain/optimize_trace.go similarity index 100% rename from domain/optimize_trace.go rename to pkg/domain/optimize_trace.go diff --git a/pkg/domain/plan_replayer.go b/pkg/domain/plan_replayer.go new file mode 100644 index 0000000000000..4f77d63b009f5 --- /dev/null +++ b/pkg/domain/plan_replayer.go @@ -0,0 +1,572 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package domain + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/domain/infosync" + domain_metrics "github.com/pingcap/tidb/pkg/domain/metrics" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// dumpFileGcChecker is used to gc dump file in circle +// For now it is used by `plan replayer` and `trace plan` statement +type dumpFileGcChecker struct { + sync.Mutex + gcLease time.Duration + paths []string + sctx sessionctx.Context + planReplayerTaskStatus *planReplayerDumpTaskStatus +} + +func parseType(s string) string { + return strings.Split(s, "_")[0] +} + +func parseTime(s string) (time.Time, error) { + startIdx := strings.LastIndex(s, "_") + if startIdx == -1 { + return time.Time{}, errors.New("failed to parse the file :" + s) + } + endIdx := strings.LastIndex(s, ".") + if endIdx == -1 || endIdx <= startIdx+1 { + return time.Time{}, errors.New("failed to parse the file :" + s) + } + i, err := strconv.ParseInt(s[startIdx+1:endIdx], 10, 64) + if err != nil { + return time.Time{}, errors.New("failed to parse the file :" + s) + } + return time.Unix(0, i), nil +} + +// GCDumpFiles periodically cleans the outdated files for plan replayer and plan trace. +func (p *dumpFileGcChecker) GCDumpFiles(gcDurationDefault, gcDurationForCapture time.Duration) { + p.Lock() + defer p.Unlock() + for _, path := range p.paths { + p.gcDumpFilesByPath(path, gcDurationDefault, gcDurationForCapture) + } +} + +func (p *dumpFileGcChecker) setupSctx(sctx sessionctx.Context) { + p.sctx = sctx +} + +func (p *dumpFileGcChecker) gcDumpFilesByPath(path string, gcDurationDefault, gcDurationForCapture time.Duration) { + entries, err := os.ReadDir(path) + if err != nil { + if !os.IsNotExist(err) { + absPath, err2 := filepath.Abs(path) + if err2 != nil { + logutil.BgLogger().Warn("failed to get absolute path", + zap.Error(err2), zap.String("category", "dumpFileGcChecker")) + absPath = path + } + logutil.BgLogger().Warn("open plan replayer directory failed", + zap.Error(err), zap.String("category", "dumpFileGcChecker"), + zap.String("path", absPath)) + } + } + + gcTargetTimeDefault := time.Now().Add(-gcDurationDefault) + gcTargetTimeForCapture := time.Now().Add(-gcDurationForCapture) + for _, entry := range entries { + f, err := entry.Info() + if err != nil { + logutil.BgLogger().Warn("open plan replayer directory failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err)) + } + fileName := f.Name() + createTime, err := parseTime(fileName) + if err != nil { + logutil.BgLogger().Error("parseTime failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err), zap.String("filename", fileName)) + continue + } + isPlanReplayer := strings.Contains(fileName, "replayer") + isPlanReplayerCapture := strings.Contains(fileName, "capture") + canGC := false + if isPlanReplayer && isPlanReplayerCapture { + canGC = !createTime.After(gcTargetTimeForCapture) + } else { + canGC = !createTime.After(gcTargetTimeDefault) + } + if canGC { + err := os.Remove(filepath.Join(path, f.Name())) + if err != nil { + logutil.BgLogger().Warn("remove file failed", zap.String("category", "dumpFileGcChecker"), zap.Error(err), zap.String("filename", fileName)) + continue + } + logutil.BgLogger().Info("dumpFileGcChecker successful", zap.String("filename", fileName)) + if isPlanReplayer && p.sctx != nil { + deletePlanReplayerStatus(context.Background(), p.sctx, fileName) + p.planReplayerTaskStatus.clearFinishedTask() + } + } + } +} + +func deletePlanReplayerStatus(ctx context.Context, sctx sessionctx.Context, token string) { + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + exec := sctx.(sqlexec.RestrictedSQLExecutor) + _, _, err := exec.ExecRestrictedSQL(ctx1, nil, "delete from mysql.plan_replayer_status where token = %?", token) + if err != nil { + logutil.BgLogger().Warn("delete mysql.plan_replayer_status record failed", zap.String("token", token), zap.Error(err)) + } +} + +// insertPlanReplayerStatus insert mysql.plan_replayer_status record +func insertPlanReplayerStatus(ctx context.Context, sctx sessionctx.Context, records []PlanReplayerStatusRecord) { + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + var instance string + serverInfo, err := infosync.GetServerInfo() + if err != nil { + logutil.BgLogger().Error("failed to get server info", zap.Error(err)) + instance = "unknown" + } else { + instance = fmt.Sprintf("%s:%d", serverInfo.IP, serverInfo.Port) + } + for _, record := range records { + if len(record.FailedReason) > 0 { + insertPlanReplayerErrorStatusRecord(ctx1, sctx, instance, record) + } else { + insertPlanReplayerSuccessStatusRecord(ctx1, sctx, instance, record) + } + } +} + +func insertPlanReplayerErrorStatusRecord(ctx context.Context, sctx sessionctx.Context, instance string, record PlanReplayerStatusRecord) { + exec := sctx.(sqlexec.RestrictedSQLExecutor) + _, _, err := exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( + "insert into mysql.plan_replayer_status (sql_digest, plan_digest, origin_sql, fail_reason, instance) values ('%s','%s','%s','%s','%s')", + record.SQLDigest, record.PlanDigest, record.OriginSQL, record.FailedReason, instance)) + if err != nil { + logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", + zap.Error(err)) + } +} + +func insertPlanReplayerSuccessStatusRecord(ctx context.Context, sctx sessionctx.Context, instance string, record PlanReplayerStatusRecord) { + exec := sctx.(sqlexec.RestrictedSQLExecutor) + _, _, err := exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( + "insert into mysql.plan_replayer_status (sql_digest, plan_digest, origin_sql, token, instance) values ('%s','%s','%s','%s','%s')", + record.SQLDigest, record.PlanDigest, record.OriginSQL, record.Token, instance)) + if err != nil { + logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", + zap.String("sql", record.OriginSQL), + zap.Error(err)) + // try insert record without original sql + _, _, err = exec.ExecRestrictedSQL(ctx, nil, fmt.Sprintf( + "insert into mysql.plan_replayer_status (sql_digest, plan_digest, token, instance) values ('%s','%s','%s','%s')", + record.SQLDigest, record.PlanDigest, record.Token, instance)) + if err != nil { + logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", + zap.String("sqlDigest", record.SQLDigest), + zap.String("planDigest", record.PlanDigest), + zap.Error(err)) + } + } +} + +type planReplayerHandle struct { + *planReplayerTaskCollectorHandle + *planReplayerTaskDumpHandle +} + +// SendTask send dumpTask in background task handler +func (h *planReplayerHandle) SendTask(task *PlanReplayerDumpTask) bool { + select { + case h.planReplayerTaskDumpHandle.taskCH <- task: + // we directly remove the task key if we put task in channel successfully, if the task was failed to dump, + // the task handle will re-add the task in next loop + if !task.IsContinuesCapture { + h.planReplayerTaskCollectorHandle.removeTask(task.PlanReplayerTaskKey) + } + domain_metrics.PlanReplayerCaptureTaskSendCounter.Inc() + return true + default: + domain_metrics.PlanReplayerCaptureTaskDiscardCounter.Inc() + // directly discard the task if the task channel is full in order not to block the query process + logutil.BgLogger().Warn("discard one plan replayer dump task", + zap.String("sql-digest", task.SQLDigest), zap.String("plan-digest", task.PlanDigest)) + return false + } +} + +type planReplayerTaskCollectorHandle struct { + taskMu struct { + sync.RWMutex + tasks map[replayer.PlanReplayerTaskKey]struct{} + } + ctx context.Context + sctx sessionctx.Context +} + +// CollectPlanReplayerTask collects all unhandled plan replayer task +func (h *planReplayerTaskCollectorHandle) CollectPlanReplayerTask() error { + allKeys, err := h.collectAllPlanReplayerTask(h.ctx) + if err != nil { + return err + } + tasks := make([]replayer.PlanReplayerTaskKey, 0) + for _, key := range allKeys { + unhandled, err := checkUnHandledReplayerTask(h.ctx, h.sctx, key) + if err != nil { + logutil.BgLogger().Warn("collect plan replayer task failed", zap.String("category", "plan-replayer-task"), zap.Error(err)) + return err + } + if unhandled { + logutil.BgLogger().Debug("collect plan replayer task success", zap.String("category", "plan-replayer-task"), + zap.String("sql-digest", key.SQLDigest), + zap.String("plan-digest", key.PlanDigest)) + tasks = append(tasks, key) + } + } + h.setupTasks(tasks) + domain_metrics.PlanReplayerRegisterTaskGauge.Set(float64(len(tasks))) + return nil +} + +// GetTasks get all tasks +func (h *planReplayerTaskCollectorHandle) GetTasks() []replayer.PlanReplayerTaskKey { + tasks := make([]replayer.PlanReplayerTaskKey, 0) + h.taskMu.RLock() + defer h.taskMu.RUnlock() + for taskKey := range h.taskMu.tasks { + tasks = append(tasks, taskKey) + } + return tasks +} + +func (h *planReplayerTaskCollectorHandle) setupTasks(tasks []replayer.PlanReplayerTaskKey) { + r := make(map[replayer.PlanReplayerTaskKey]struct{}) + for _, task := range tasks { + r[task] = struct{}{} + } + h.taskMu.Lock() + defer h.taskMu.Unlock() + h.taskMu.tasks = r +} + +func (h *planReplayerTaskCollectorHandle) removeTask(taskKey replayer.PlanReplayerTaskKey) { + h.taskMu.Lock() + defer h.taskMu.Unlock() + delete(h.taskMu.tasks, taskKey) +} + +func (h *planReplayerTaskCollectorHandle) collectAllPlanReplayerTask(ctx context.Context) ([]replayer.PlanReplayerTaskKey, error) { + exec := h.sctx.(sqlexec.SQLExecutor) + rs, err := exec.ExecuteInternal(ctx, "select sql_digest, plan_digest from mysql.plan_replayer_task") + if err != nil { + return nil, err + } + if rs == nil { + return nil, nil + } + var rows []chunk.Row + defer terror.Call(rs.Close) + if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { + return nil, errors.Trace(err) + } + allKeys := make([]replayer.PlanReplayerTaskKey, 0, len(rows)) + for _, row := range rows { + sqlDigest, planDigest := row.GetString(0), row.GetString(1) + allKeys = append(allKeys, replayer.PlanReplayerTaskKey{ + SQLDigest: sqlDigest, + PlanDigest: planDigest, + }) + } + return allKeys, nil +} + +type planReplayerDumpTaskStatus struct { + // running task records the task running by all workers in order to avoid multi workers running the same task key + runningTaskMu struct { + sync.RWMutex + runningTasks map[replayer.PlanReplayerTaskKey]struct{} + } + + // finished task records the finished task in order to avoid running finished task key + finishedTaskMu struct { + sync.RWMutex + finishedTask map[replayer.PlanReplayerTaskKey]struct{} + } +} + +// GetRunningTaskStatusLen used for unit test +func (r *planReplayerDumpTaskStatus) GetRunningTaskStatusLen() int { + r.runningTaskMu.RLock() + defer r.runningTaskMu.RUnlock() + return len(r.runningTaskMu.runningTasks) +} + +// CleanFinishedTaskStatus clean then finished tasks, only used for unit test +func (r *planReplayerDumpTaskStatus) CleanFinishedTaskStatus() { + r.finishedTaskMu.Lock() + defer r.finishedTaskMu.Unlock() + r.finishedTaskMu.finishedTask = map[replayer.PlanReplayerTaskKey]struct{}{} +} + +// GetFinishedTaskStatusLen used for unit test +func (r *planReplayerDumpTaskStatus) GetFinishedTaskStatusLen() int { + r.finishedTaskMu.RLock() + defer r.finishedTaskMu.RUnlock() + return len(r.finishedTaskMu.finishedTask) +} + +func (r *planReplayerDumpTaskStatus) occupyRunningTaskKey(task *PlanReplayerDumpTask) bool { + r.runningTaskMu.Lock() + defer r.runningTaskMu.Unlock() + _, ok := r.runningTaskMu.runningTasks[task.PlanReplayerTaskKey] + if ok { + return false + } + r.runningTaskMu.runningTasks[task.PlanReplayerTaskKey] = struct{}{} + return true +} + +func (r *planReplayerDumpTaskStatus) releaseRunningTaskKey(task *PlanReplayerDumpTask) { + r.runningTaskMu.Lock() + defer r.runningTaskMu.Unlock() + delete(r.runningTaskMu.runningTasks, task.PlanReplayerTaskKey) +} + +func (r *planReplayerDumpTaskStatus) checkTaskKeyFinishedBefore(task *PlanReplayerDumpTask) bool { + r.finishedTaskMu.RLock() + defer r.finishedTaskMu.RUnlock() + _, ok := r.finishedTaskMu.finishedTask[task.PlanReplayerTaskKey] + return ok +} + +func (r *planReplayerDumpTaskStatus) setTaskFinished(task *PlanReplayerDumpTask) { + r.finishedTaskMu.Lock() + defer r.finishedTaskMu.Unlock() + r.finishedTaskMu.finishedTask[task.PlanReplayerTaskKey] = struct{}{} +} + +func (r *planReplayerDumpTaskStatus) clearFinishedTask() { + r.finishedTaskMu.Lock() + defer r.finishedTaskMu.Unlock() + r.finishedTaskMu.finishedTask = map[replayer.PlanReplayerTaskKey]struct{}{} +} + +type planReplayerTaskDumpWorker struct { + ctx context.Context + sctx sessionctx.Context + taskCH <-chan *PlanReplayerDumpTask + status *planReplayerDumpTaskStatus +} + +func (w *planReplayerTaskDumpWorker) run() { + logutil.BgLogger().Info("planReplayerTaskDumpWorker started.") + for task := range w.taskCH { + w.handleTask(task) + } + logutil.BgLogger().Info("planReplayerTaskDumpWorker exited.") +} + +func (w *planReplayerTaskDumpWorker) handleTask(task *PlanReplayerDumpTask) { + sqlDigest := task.SQLDigest + planDigest := task.PlanDigest + check := true + occupy := true + handleTask := true + defer func() { + logutil.BgLogger().Debug("handle task", zap.String("category", "plan-replayer-capture"), + zap.String("sql-digest", sqlDigest), + zap.String("plan-digest", planDigest), + zap.Bool("check", check), + zap.Bool("occupy", occupy), + zap.Bool("handle", handleTask)) + }() + defer util.Recover(metrics.LabelDomain, "PlanReplayerTaskDumpWorker", nil, false) + + if task.IsContinuesCapture { + if w.status.checkTaskKeyFinishedBefore(task) { + check = false + return + } + } + occupy = w.status.occupyRunningTaskKey(task) + if !occupy { + return + } + handleTask = w.HandleTask(task) + w.status.releaseRunningTaskKey(task) +} + +// HandleTask handled task +func (w *planReplayerTaskDumpWorker) HandleTask(task *PlanReplayerDumpTask) (success bool) { + defer func() { + if success && task.IsContinuesCapture { + w.status.setTaskFinished(task) + } + }() + taskKey := task.PlanReplayerTaskKey + unhandled, err := checkUnHandledReplayerTask(w.ctx, w.sctx, taskKey) + if err != nil { + logutil.BgLogger().Warn("check task failed", zap.String("category", "plan-replayer-capture"), + zap.String("sqlDigest", taskKey.SQLDigest), + zap.String("planDigest", taskKey.PlanDigest), + zap.Error(err)) + return false + } + // the task is processed, thus we directly skip it. + if !unhandled { + return true + } + + file, fileName, err := replayer.GeneratePlanReplayerFile(task.IsCapture, task.IsContinuesCapture, variable.EnableHistoricalStatsForCapture.Load()) + if err != nil { + logutil.BgLogger().Warn("generate task file failed", zap.String("category", "plan-replayer-capture"), + zap.String("sqlDigest", taskKey.SQLDigest), + zap.String("planDigest", taskKey.PlanDigest), + zap.Error(err)) + return false + } + task.Zf = file + task.FileName = fileName + err = DumpPlanReplayerInfo(w.ctx, w.sctx, task) + if err != nil { + logutil.BgLogger().Warn("dump task result failed", zap.String("category", "plan-replayer-capture"), + zap.String("sqlDigest", taskKey.SQLDigest), + zap.String("planDigest", taskKey.PlanDigest), + zap.Error(err)) + return false + } + return true +} + +type planReplayerTaskDumpHandle struct { + taskCH chan *PlanReplayerDumpTask + status *planReplayerDumpTaskStatus + workers []*planReplayerTaskDumpWorker +} + +// GetTaskStatus used for test +func (h *planReplayerTaskDumpHandle) GetTaskStatus() *planReplayerDumpTaskStatus { + return h.status +} + +// GetWorker used for test +func (h *planReplayerTaskDumpHandle) GetWorker() *planReplayerTaskDumpWorker { + return h.workers[0] +} + +// Close make finished flag ture +func (h *planReplayerTaskDumpHandle) Close() { + close(h.taskCH) +} + +// DrainTask drain a task for unit test +func (h *planReplayerTaskDumpHandle) DrainTask() *PlanReplayerDumpTask { + return <-h.taskCH +} + +func checkUnHandledReplayerTask(ctx context.Context, sctx sessionctx.Context, task replayer.PlanReplayerTaskKey) (bool, error) { + exec := sctx.(sqlexec.SQLExecutor) + rs, err := exec.ExecuteInternal(ctx, fmt.Sprintf("select * from mysql.plan_replayer_status where sql_digest = '%v' and plan_digest = '%v' and fail_reason is null", task.SQLDigest, task.PlanDigest)) + if err != nil { + return false, err + } + if rs == nil { + return true, nil + } + var rows []chunk.Row + defer terror.Call(rs.Close) + if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { + return false, errors.Trace(err) + } + if len(rows) > 0 { + return false, nil + } + return true, nil +} + +// CheckPlanReplayerTaskExists checks whether plan replayer capture task exists already +func CheckPlanReplayerTaskExists(ctx context.Context, sctx sessionctx.Context, sqlDigest, planDigest string) (bool, error) { + exec := sctx.(sqlexec.SQLExecutor) + rs, err := exec.ExecuteInternal(ctx, fmt.Sprintf("select * from mysql.plan_replayer_task where sql_digest = '%v' and plan_digest = '%v'", + sqlDigest, planDigest)) + if err != nil { + return false, err + } + if rs == nil { + return false, nil + } + var rows []chunk.Row + defer terror.Call(rs.Close) + if rows, err = sqlexec.DrainRecordSet(ctx, rs, 8); err != nil { + return false, errors.Trace(err) + } + if len(rows) > 0 { + return true, nil + } + return false, nil +} + +// PlanReplayerStatusRecord indicates record in mysql.plan_replayer_status +type PlanReplayerStatusRecord struct { + SQLDigest string + PlanDigest string + OriginSQL string + Token string + FailedReason string +} + +// PlanReplayerDumpTask wrap the params for plan replayer dump +type PlanReplayerDumpTask struct { + replayer.PlanReplayerTaskKey + + // tmp variables stored during the query + TblStats map[int64]interface{} + + // variables used to dump the plan + StartTS uint64 + SessionBindings []*bindinfo.BindRecord + EncodedPlan string + SessionVars *variable.SessionVars + ExecStmts []ast.StmtNode + Analyze bool + HistoricalStatsTS uint64 + DebugTrace []interface{} + + FileName string + Zf *os.File + + // IsCapture indicates whether the task is from capture + IsCapture bool + // IsContinuesCapture indicates whether the task is from continues capture + IsContinuesCapture bool +} diff --git a/domain/plan_replayer_dump.go b/pkg/domain/plan_replayer_dump.go similarity index 97% rename from domain/plan_replayer_dump.go rename to pkg/domain/plan_replayer_dump.go index 64d2df86ce19d..6f03f74f209b1 100644 --- a/domain/plan_replayer_dump.go +++ b/pkg/domain/plan_replayer_dump.go @@ -26,19 +26,19 @@ import ( "github.com/BurntSushi/toml" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/config" - domain_metrics "github.com/pingcap/tidb/domain/metrics" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/printer" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + domain_metrics "github.com/pingcap/tidb/pkg/domain/metrics" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/printer" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/domain/plan_replayer_handle_test.go b/pkg/domain/plan_replayer_handle_test.go similarity index 98% rename from domain/plan_replayer_handle_test.go rename to pkg/domain/plan_replayer_handle_test.go index f56a250683463..48e8b5a55f402 100644 --- a/domain/plan_replayer_handle_test.go +++ b/pkg/domain/plan_replayer_handle_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/replayer" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/replayer" "github.com/stretchr/testify/require" ) diff --git a/pkg/domain/plan_replayer_test.go b/pkg/domain/plan_replayer_test.go new file mode 100644 index 0000000000000..1298b89306ce9 --- /dev/null +++ b/pkg/domain/plan_replayer_test.go @@ -0,0 +1,136 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package domain + +import ( + "fmt" + "path/filepath" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/stretchr/testify/require" +) + +func TestPlanReplayerDifferentGC(t *testing.T) { + dirName := replayer.GetPlanReplayerDirName() + + time1 := time.Now().Add(-7 * 25 * time.Hour).UnixNano() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time1))) + file1, fileName1, err := replayer.GeneratePlanReplayerFile(true, false, false) + require.NoError(t, err) + require.NoError(t, file1.Close()) + filePath1 := filepath.Join(dirName, fileName1) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField")) + + time2 := time.Now().Add(-7 * 23 * time.Hour).UnixNano() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time2))) + file2, fileName2, err := replayer.GeneratePlanReplayerFile(true, false, false) + require.NoError(t, err) + require.NoError(t, file2.Close()) + filePath2 := filepath.Join(dirName, fileName2) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField")) + + time3 := time.Now().Add(-2 * time.Hour).UnixNano() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time3))) + file3, fileName3, err := replayer.GeneratePlanReplayerFile(false, false, false) + require.NoError(t, err) + require.NoError(t, file3.Close()) + filePath3 := filepath.Join(dirName, fileName3) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField")) + + time4 := time.Now().UnixNano() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField", fmt.Sprintf("return(%d)", time4))) + file4, fileName4, err := replayer.GeneratePlanReplayerFile(false, false, false) + require.NoError(t, err) + require.NoError(t, file4.Close()) + filePath4 := filepath.Join(dirName, fileName4) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/replayer/InjectPlanReplayerFileNameTimeField")) + + handler := &dumpFileGcChecker{ + paths: []string{dirName}, + } + handler.GCDumpFiles(time.Hour, time.Hour*24*7) + require.NoFileExists(t, filePath1) + require.FileExists(t, filePath2) + require.NoFileExists(t, filePath3) + require.FileExists(t, filePath4) + + handler.GCDumpFiles(0, 0) + require.NoFileExists(t, filePath2) + require.NoFileExists(t, filePath4) +} + +func TestDumpGCFileParseTime(t *testing.T) { + nowTime := time.Now() + name1 := fmt.Sprintf("replayer_single_xxxxxx_%v.zip", nowTime.UnixNano()) + pt, err := parseTime(name1) + require.NoError(t, err) + require.True(t, pt.Equal(nowTime)) + + name2 := fmt.Sprintf("replayer_single_xxxxxx_%v1.zip", nowTime.UnixNano()) + _, err = parseTime(name2) + require.NotNil(t, err) + + name3 := fmt.Sprintf("replayer_single_xxxxxx_%v._zip", nowTime.UnixNano()) + _, err = parseTime(name3) + require.NotNil(t, err) + + name4 := "extract_-brq6zKMarD9ayaifkHc4A==_1678168728477502000.zip" + _, err = parseTime(name4) + require.NoError(t, err) + + var pName string + pName, err = replayer.GeneratePlanReplayerFileName(false, false, false) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(true, false, false) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(false, true, false) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(true, true, false) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(false, false, true) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(true, false, true) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(false, true, true) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) + + pName, err = replayer.GeneratePlanReplayerFileName(true, true, true) + require.NoError(t, err) + _, err = parseTime(pName) + require.NoError(t, err) +} diff --git a/pkg/domain/resourcegroup/BUILD.bazel b/pkg/domain/resourcegroup/BUILD.bazel new file mode 100644 index 0000000000000..354460a47532b --- /dev/null +++ b/pkg/domain/resourcegroup/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "resourcegroup", + srcs = ["runaway.go"], + importpath = "github.com/pingcap/tidb/pkg/domain/resourcegroup", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/dbterror/exeerrors", + "//pkg/util/logutil", + "@com_github_jellydator_ttlcache_v3//:ttlcache", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_pd_client//resource_group/controller", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/domain/resourcegroup/runaway.go b/pkg/domain/resourcegroup/runaway.go new file mode 100644 index 0000000000000..0b5d62a5711c5 --- /dev/null +++ b/pkg/domain/resourcegroup/runaway.go @@ -0,0 +1,568 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package resourcegroup + +import ( + "context" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/jellydator/ttlcache/v3" + rmpb "github.com/pingcap/kvproto/pkg/resource_manager" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + rmclient "github.com/tikv/pd/client/resource_group/controller" + "go.uber.org/zap" +) + +const ( + // DefaultResourceGroupName is the default resource group name. + DefaultResourceGroupName = "default" + // ManualSource shows the item added manually. + ManualSource = "manual" + // RunawayWatchTableName is the name of system table which save runaway watch items. + RunawayWatchTableName = "mysql.tidb_runaway_watch" + // RunawayWatchDoneTableName is the name of system table which save done runaway watch items. + RunawayWatchDoneTableName = "mysql.tidb_runaway_watch_done" + + // MaxWaitDuration is the max duration to wait for acquiring token buckets. + MaxWaitDuration = time.Second * 30 + maxWatchListCap = 10000 + maxWatchRecordChannelSize = 1024 +) + +// NullTime is a zero time.Time. +var NullTime time.Time + +// RunawayMatchType is used to indicate whether query was interrupted by runaway identification or quarantine watch. +type RunawayMatchType uint + +const ( + // RunawayMatchTypeWatch shows quarantine watch. + RunawayMatchTypeWatch RunawayMatchType = iota + // RunawayMatchTypeIdentify shows identification. + RunawayMatchTypeIdentify +) + +func (t RunawayMatchType) String() string { + switch t { + case RunawayMatchTypeWatch: + return "watch" + case RunawayMatchTypeIdentify: + return "identify" + default: + panic("unknown type") + } +} + +// RunawayRecord is used to save records which will be insert into mysql.tidb_runaway_queries. +type RunawayRecord struct { + ResourceGroupName string + Time time.Time + Match string + Action string + SQLText string + PlanDigest string + Source string +} + +// GenRunawayQueriesStmt generates statement with given RunawayRecords. +func GenRunawayQueriesStmt(records []*RunawayRecord) (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, len(records)*7) + builder.WriteString("insert into mysql.tidb_runaway_queries VALUES ") + for count, r := range records { + if count > 0 { + builder.WriteByte(',') + } + builder.WriteString("(%?, %?, %?, %?, %?, %?, %?)") + params = append(params, r.ResourceGroupName) + params = append(params, r.Time) + params = append(params, r.Match) + params = append(params, r.Action) + params = append(params, r.SQLText) + params = append(params, r.PlanDigest) + params = append(params, r.Source) + } + return builder.String(), params +} + +// QuarantineRecord is used to save records which will be insert into mysql.tidb_runaway_watch. +type QuarantineRecord struct { + ID int64 + ResourceGroupName string + StartTime time.Time + EndTime time.Time + Watch rmpb.RunawayWatchType + WatchText string + Source string + Action rmpb.RunawayAction +} + +// GetRecordKey is used to get the key in ttl cache. +func (r *QuarantineRecord) GetRecordKey() string { + return r.ResourceGroupName + "/" + r.WatchText +} + +func writeInsert(builder *strings.Builder, tableName string) { + builder.WriteString("insert into ") + builder.WriteString(tableName) + builder.WriteString(" VALUES ") +} + +// GenInsertionStmt is used to generate insertion sql. +func (r *QuarantineRecord) GenInsertionStmt() (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, 6) + writeInsert(&builder, RunawayWatchTableName) + builder.WriteString("(null, %?, %?, %?, %?, %?, %?, %?)") + params = append(params, r.ResourceGroupName) + params = append(params, r.StartTime) + if r.EndTime.Equal(NullTime) { + params = append(params, nil) + } else { + params = append(params, r.EndTime) + } + params = append(params, r.Watch) + params = append(params, r.WatchText) + params = append(params, r.Source) + params = append(params, r.Action) + return builder.String(), params +} + +// GenInsertionDoneStmt is used to generate insertion sql for runaway watch done record. +func (r *QuarantineRecord) GenInsertionDoneStmt() (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, 9) + writeInsert(&builder, RunawayWatchDoneTableName) + builder.WriteString("(null, %?, %?, %?, %?, %?, %?, %?, %?, %?)") + params = append(params, r.ID) + params = append(params, r.ResourceGroupName) + params = append(params, r.StartTime) + if r.EndTime.Equal(NullTime) { + params = append(params, nil) + } else { + params = append(params, r.EndTime) + } + params = append(params, r.Watch) + params = append(params, r.WatchText) + params = append(params, r.Source) + params = append(params, r.Action) + params = append(params, time.Now().UTC()) + return builder.String(), params +} + +// GenDeletionStmt is used to generate deletion sql. +func (r *QuarantineRecord) GenDeletionStmt() (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, 1) + builder.WriteString("delete from ") + builder.WriteString(RunawayWatchTableName) + builder.WriteString(" where id = %?") + params = append(params, r.ID) + return builder.String(), params +} + +// RunawayManager is used to detect and record runaway queries. +type RunawayManager struct { + // queryLock is used to avoid repeated additions. Since we will add new items to the system table, + // in order to avoid repeated additions, we need a lock to ensure that + // action "judging whether there is this record in the current watch list and adding records" have atomicity. + queryLock sync.Mutex + watchList *ttlcache.Cache[string, *QuarantineRecord] + // activeGroup is used to manage the active runaway watches of resource group + activeGroup map[string]int64 + activeLock sync.RWMutex + + resourceGroupCtl *rmclient.ResourceGroupsController + serverID string + runawayQueriesChan chan *RunawayRecord + quarantineChan chan *QuarantineRecord + // staleQuarantineRecord is used to clean outdated record. There are three scenarios: + // 1. Record is expired in watch list. + // 2. The record that will be added is itself out of date. + // Like that tidb cluster is paused, and record is expired when restarting. + // 3. Duplicate added records. + // It replaces clean up loop. + staleQuarantineRecord chan *QuarantineRecord + evictionCancel func() + insertionCancel func() +} + +// NewRunawayManager creates a new RunawayManager. +func NewRunawayManager(resourceGroupCtl *rmclient.ResourceGroupsController, serverAddr string) *RunawayManager { + watchList := ttlcache.New[string, *QuarantineRecord]( + ttlcache.WithTTL[string, *QuarantineRecord](ttlcache.NoTTL), + ttlcache.WithCapacity[string, *QuarantineRecord](maxWatchListCap), + ttlcache.WithDisableTouchOnHit[string, *QuarantineRecord](), + ) + go watchList.Start() + staleQuarantineChan := make(chan *QuarantineRecord, maxWatchRecordChannelSize) + m := &RunawayManager{ + resourceGroupCtl: resourceGroupCtl, + watchList: watchList, + serverID: serverAddr, + runawayQueriesChan: make(chan *RunawayRecord, maxWatchRecordChannelSize), + quarantineChan: make(chan *QuarantineRecord, maxWatchRecordChannelSize), + staleQuarantineRecord: staleQuarantineChan, + activeGroup: make(map[string]int64), + } + m.insertionCancel = watchList.OnInsertion(func(ctx context.Context, i *ttlcache.Item[string, *QuarantineRecord]) { + m.activeLock.Lock() + m.activeGroup[i.Value().ResourceGroupName]++ + m.activeLock.Unlock() + }) + m.evictionCancel = watchList.OnEviction(func(ctx context.Context, er ttlcache.EvictionReason, i *ttlcache.Item[string, *QuarantineRecord]) { + m.activeLock.Lock() + m.activeGroup[i.Value().ResourceGroupName]-- + m.activeLock.Unlock() + if i.Value().ID == 0 { + return + } + staleQuarantineChan <- i.Value() + }) + return m +} + +// DeriveChecker derives a RunawayChecker from the given resource group +func (rm *RunawayManager) DeriveChecker(resourceGroupName, originalSQL, sqlDigest, planDigest string) *RunawayChecker { + group, err := rm.resourceGroupCtl.GetResourceGroup(resourceGroupName) + if err != nil || group == nil { + logutil.BgLogger().Warn("cannot setup up runaway checker", zap.Error(err)) + return nil + } + rm.activeLock.RLock() + defer rm.activeLock.RUnlock() + if group.RunawaySettings == nil && rm.activeGroup[resourceGroupName] == 0 { + return nil + } + return newRunawayChecker(rm, resourceGroupName, group.RunawaySettings, originalSQL, sqlDigest, planDigest) +} + +func (rm *RunawayManager) markQuarantine(resourceGroupName, convict string, watchType rmpb.RunawayWatchType, action rmpb.RunawayAction, ttl time.Duration, now *time.Time) { + var endTime time.Time + if ttl > 0 { + endTime = now.UTC().Add(ttl) + } + record := &QuarantineRecord{ + ResourceGroupName: resourceGroupName, + StartTime: now.UTC(), + EndTime: endTime, + Watch: watchType, + WatchText: convict, + Source: rm.serverID, + Action: action, + } + // Add record without ID into watch list in this TiDB right now. + rm.addWatchList(record, ttl, false) + select { + case rm.quarantineChan <- record: + default: + // TODO: add warning for discard flush records + } +} + +func (rm *RunawayManager) addWatchList(record *QuarantineRecord, ttl time.Duration, force bool) { + key := record.GetRecordKey() + // This is a pre-check, because we generally believe that in most cases, we will not add a watch list to a key repeatedly. + item := rm.getWatchFromWatchList(key) + if force { + rm.queryLock.Lock() + defer rm.queryLock.Unlock() + if item != nil { + // check the ID because of the eariler scan. + if item.ID == record.ID { + return + } + rm.watchList.Delete(key) + } + rm.watchList.Set(key, record, ttl) + } else { + if item == nil { + rm.queryLock.Lock() + // When watchlist get record, it will check whether the record is stale, so add new record if returns nil. + if rm.watchList.Get(key) == nil { + rm.watchList.Set(key, record, ttl) + } else { + rm.staleQuarantineRecord <- record + } + rm.queryLock.Unlock() + } else if item.ID == 0 { + // to replace the record without ID. + rm.queryLock.Lock() + defer rm.queryLock.Unlock() + rm.watchList.Set(key, record, ttl) + } else if item.ID != record.ID { + // check the ID because of the eariler scan. + rm.staleQuarantineRecord <- record + } + } +} + +// GetWatchByKey is used to get a watch item by given key. +func (rm *RunawayManager) GetWatchByKey(key string) *QuarantineRecord { + return rm.getWatchFromWatchList(key) +} + +// GetWatchList is used to get all watch items. +func (rm *RunawayManager) GetWatchList() []*QuarantineRecord { + items := rm.watchList.Items() + ret := make([]*QuarantineRecord, 0, len(items)) + for _, item := range items { + ret = append(ret, item.Value()) + } + return ret +} + +// AddWatch is used to add watch items from system table. +func (rm *RunawayManager) AddWatch(record *QuarantineRecord) { + ttl := time.Until(record.EndTime) + if record.EndTime.Equal(NullTime) { + ttl = 0 + } else if ttl <= 0 { + rm.staleQuarantineRecord <- record + return + } + + force := false + // The manual record replaces the old record. + force = record.Source == ManualSource + rm.addWatchList(record, ttl, force) +} + +// RemoveWatch is used to remove watch item, and this action is triggered by reading done watch system table. +func (rm *RunawayManager) RemoveWatch(record *QuarantineRecord) { + // we should check whether the cached record is not the same as the removing record. + rm.queryLock.Lock() + defer rm.queryLock.Unlock() + item := rm.getWatchFromWatchList(record.GetRecordKey()) + if item == nil { + return + } + if item.ID == record.ID { + rm.watchList.Delete(record.GetRecordKey()) + } +} +func (rm *RunawayManager) getWatchFromWatchList(key string) *QuarantineRecord { + item := rm.watchList.Get(key) + if item != nil { + return item.Value() + } + return nil +} + +func (rm *RunawayManager) markRunaway(resourceGroupName, originalSQL, planDigest string, action string, matchType RunawayMatchType, now *time.Time) { + source := rm.serverID + select { + case rm.runawayQueriesChan <- &RunawayRecord{ + ResourceGroupName: resourceGroupName, + Time: *now, + Match: matchType.String(), + Action: action, + SQLText: originalSQL, + PlanDigest: planDigest, + Source: source, + }: + default: + // TODO: add warning for discard flush records + } +} + +// FlushThreshold specifies the threshold for the number of records in trigger flush +func (rm *RunawayManager) FlushThreshold() int { + return maxWatchRecordChannelSize / 2 +} + +// RunawayRecordChan returns the channel of RunawayRecord +func (rm *RunawayManager) RunawayRecordChan() <-chan *RunawayRecord { + return rm.runawayQueriesChan +} + +// QuarantineRecordChan returns the channel of QuarantineRecord +func (rm *RunawayManager) QuarantineRecordChan() <-chan *QuarantineRecord { + return rm.quarantineChan +} + +// StaleQuarantineRecordChan returns the channel of staleQuarantineRecord +func (rm *RunawayManager) StaleQuarantineRecordChan() <-chan *QuarantineRecord { + return rm.staleQuarantineRecord +} + +// examineWatchList check whether the query is in watch list. +func (rm *RunawayManager) examineWatchList(resourceGroupName string, convict string) (bool, rmpb.RunawayAction) { + item := rm.getWatchFromWatchList(resourceGroupName + "/" + convict) + if item == nil { + return false, 0 + } + return true, item.Action +} + +// Stop stops the watchList which is a ttlcache. +func (rm *RunawayManager) Stop() { + if rm.watchList != nil { + rm.watchList.Stop() + } +} + +// RunawayChecker is used to check if the query is runaway. +type RunawayChecker struct { + manager *RunawayManager + resourceGroupName string + originalSQL string + sqlDigest string + planDigest string + + deadline time.Time + setting *rmpb.RunawaySettings + + marked atomic.Bool +} + +func newRunawayChecker(manager *RunawayManager, resourceGroupName string, setting *rmpb.RunawaySettings, originalSQL, sqlDigest, planDigest string) *RunawayChecker { + c := &RunawayChecker{ + manager: manager, + resourceGroupName: resourceGroupName, + originalSQL: originalSQL, + sqlDigest: sqlDigest, + planDigest: planDigest, + setting: setting, + marked: atomic.Bool{}, + } + if setting != nil { + c.deadline = time.Now().Add(time.Duration(setting.Rule.ExecElapsedTimeMs) * time.Millisecond) + } + return c +} + +// BeforeExecutor checks whether query is in watch list before executing and after compiling. +func (r *RunawayChecker) BeforeExecutor() error { + if r == nil { + return nil + } + for _, convict := range r.getConvictIdentifiers() { + watched, action := r.manager.examineWatchList(r.resourceGroupName, convict) + if watched { + if action == rmpb.RunawayAction_NoneAction && r.setting != nil { + action = r.setting.Action + } + if r.marked.CompareAndSwap(false, true) { + now := time.Now() + r.markRunaway(RunawayMatchTypeWatch, action, &now) + } + // If no match action, it will do nothing. + switch action { + case rmpb.RunawayAction_Kill: + return exeerrors.ErrResourceGroupQueryRunawayQuarantine + case rmpb.RunawayAction_CoolDown: + return nil + case rmpb.RunawayAction_DryRun: + return nil + default: + } + } + } + return nil +} + +// BeforeCopRequest checks runaway and modifies the request if necessary before sending coprocessor request. +func (r *RunawayChecker) BeforeCopRequest(req *tikvrpc.Request) error { + if r.setting == nil { + return nil + } + marked := r.marked.Load() + if !marked { + // note: now we don't check whether query is in watch list again. + until := time.Until(r.deadline) + if until > 0 { + if r.setting.Action == rmpb.RunawayAction_Kill { + // if the execution time is close to the threshold, set a timeout + if until < tikv.ReadTimeoutMedium { + req.Context.MaxExecutionDurationMs = uint64(until.Milliseconds()) + } + } + return nil + } + // execution time exceeds the threshold, mark the query as runaway + if r.marked.CompareAndSwap(false, true) { + now := time.Now() + r.markRunaway(RunawayMatchTypeIdentify, r.setting.Action, &now) + r.markQuarantine(&now) + } + } + switch r.setting.Action { + case rmpb.RunawayAction_Kill: + return exeerrors.ErrResourceGroupQueryRunawayInterrupted + case rmpb.RunawayAction_CoolDown: + req.ResourceControlContext.OverridePriority = 1 // set priority to lowest + return nil + case rmpb.RunawayAction_DryRun: + return nil + default: + return nil + } +} + +// AfterCopRequest checks runaway after receiving coprocessor response. +func (r *RunawayChecker) AfterCopRequest() { + if r.setting == nil { + return + } + // Do not perform action here as it may be the last cop request and just let it finish. If it's not the last cop request, action would be performed in `BeforeCopRequest` when handling the next cop request. + // Here only marks the query as runaway + if !r.marked.Load() && r.deadline.Before(time.Now()) { + if r.marked.CompareAndSwap(false, true) { + now := time.Now() + r.markRunaway(RunawayMatchTypeIdentify, r.setting.Action, &now) + r.markQuarantine(&now) + } + } +} + +func (r *RunawayChecker) markQuarantine(now *time.Time) { + if r.setting.Watch == nil { + return + } + ttl := time.Duration(r.setting.Watch.LastingDurationMs) * time.Millisecond + + r.manager.markQuarantine(r.resourceGroupName, r.getSettingConvictIdentifier(), r.setting.Watch.Type, r.setting.Action, ttl, now) +} + +func (r *RunawayChecker) markRunaway(matchType RunawayMatchType, action rmpb.RunawayAction, now *time.Time) { + r.manager.markRunaway(r.resourceGroupName, r.originalSQL, r.planDigest, strings.ToLower(rmpb.RunawayAction_name[int32(action)]), matchType, now) +} + +func (r *RunawayChecker) getSettingConvictIdentifier() string { + if r.setting.Watch == nil { + return "" + } + switch r.setting.Watch.Type { + case rmpb.RunawayWatchType_Plan: + return r.planDigest + case rmpb.RunawayWatchType_Similar: + return r.sqlDigest + case rmpb.RunawayWatchType_Exact: + return r.originalSQL + default: + return "" + } +} + +func (r *RunawayChecker) getConvictIdentifiers() []string { + return []string{r.originalSQL, r.sqlDigest, r.planDigest} +} diff --git a/pkg/domain/runaway.go b/pkg/domain/runaway.go new file mode 100644 index 0000000000000..2d9cab548cd22 --- /dev/null +++ b/pkg/domain/runaway.go @@ -0,0 +1,592 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package domain + +import ( + "context" + "net" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + rmpb "github.com/pingcap/kvproto/pkg/resource_manager" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/sqlbuilder" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/tikv" + pd "github.com/tikv/pd/client" + rmclient "github.com/tikv/pd/client/resource_group/controller" + "go.uber.org/zap" +) + +const ( + runawayRecordFlushInterval = time.Second + runawayRecordGCInterval = time.Hour * 24 + runawayRecordExpiredDuration = time.Hour * 24 * 7 + runawayWatchSyncInterval = time.Second + + runawayRecordGCBatchSize = 100 + runawayRecordGCSelectBatchSize = runawayRecordGCBatchSize * 5 +) + +var systemSchemaCIStr = model.NewCIStr("mysql") + +func (do *Domain) deleteExpiredRows(tableName, colName string, expiredDuration time.Duration) { + if !do.DDL().OwnerManager().IsOwner() { + return + } + failpoint.Inject("FastRunawayGC", func() { + expiredDuration = time.Second * 1 + }) + expiredTime := time.Now().Add(-expiredDuration) + tbCIStr := model.NewCIStr(tableName) + tbl, err := do.InfoSchema().TableByName(systemSchemaCIStr, tbCIStr) + if err != nil { + logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) + return + } + tbInfo := tbl.Meta() + col := tbInfo.FindPublicColumnByName(colName) + if col == nil { + logutil.BgLogger().Error("time column is not public in table", zap.String("table", tableName), zap.String("column", colName)) + return + } + tb, err := cache.NewBasePhysicalTable(systemSchemaCIStr, tbInfo, model.NewCIStr(""), col) + if err != nil { + logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) + return + } + generator, err := sqlbuilder.NewScanQueryGenerator(tb, expiredTime, nil, nil) + if err != nil { + logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) + return + } + var leftRows [][]types.Datum + for { + sql := "" + if sql, err = generator.NextSQL(leftRows, runawayRecordGCSelectBatchSize); err != nil { + logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) + return + } + // to remove + if len(sql) == 0 { + return + } + + rows, sqlErr := do.execRestrictedSQL(sql, nil) + if sqlErr != nil { + logutil.BgLogger().Error("delete system table failed", zap.String("table", tableName), zap.Error(err)) + return + } + leftRows = make([][]types.Datum, len(rows)) + for i, row := range rows { + leftRows[i] = row.GetDatumRow(tb.KeyColumnTypes) + } + + for len(leftRows) > 0 { + var delBatch [][]types.Datum + if len(leftRows) < runawayRecordGCBatchSize { + delBatch = leftRows + leftRows = nil + } else { + delBatch = leftRows[0:runawayRecordGCBatchSize] + leftRows = leftRows[runawayRecordGCBatchSize:] + } + sql, err := sqlbuilder.BuildDeleteSQL(tb, delBatch, expiredTime) + if err != nil { + logutil.BgLogger().Error( + "build delete SQL failed when deleting system table", + zap.Error(err), + zap.String("table", tb.Schema.O+"."+tb.Name.O), + ) + return + } + + _, err = do.execRestrictedSQL(sql, nil) + if err != nil { + logutil.BgLogger().Error( + "delete SQL failed when deleting system table", zap.Error(err), zap.String("SQL", sql), + ) + } + } + } +} + +func (do *Domain) updateNewAndDoneWatch() error { + do.runawaySyncer.mu.Lock() + defer do.runawaySyncer.mu.Unlock() + records, err := do.runawaySyncer.getNewWatchRecords() + if err != nil { + logutil.BgLogger().Error("try to get new runaway watch", zap.Error(err)) + return err + } + for _, r := range records { + do.runawayManager.AddWatch(r) + } + doneRecords, err := do.runawaySyncer.getNewWatchDoneRecords() + if err != nil { + logutil.BgLogger().Error("try to get done runaway watch", zap.Error(err)) + return err + } + for _, r := range doneRecords { + do.runawayManager.RemoveWatch(r) + } + return nil +} + +func (do *Domain) runawayWatchSyncLoop() { + defer util.Recover(metrics.LabelDomain, "runawayWatchSyncLoop", nil, false) + runawayWatchSyncTicker := time.NewTicker(runawayWatchSyncInterval) + for { + select { + case <-do.exit: + return + case <-runawayWatchSyncTicker.C: + err := do.updateNewAndDoneWatch() + if err != nil { + logutil.BgLogger().Warn("get runaway watch record failed", zap.Error(err)) + } + } + } +} + +// AddRunawayWatch is used to add runaway watch item manually. +func (do *Domain) AddRunawayWatch(record *resourcegroup.QuarantineRecord) error { + return do.handleRunawayWatch(record) +} + +// GetRunawayWatchList is used to get all items from runaway watch list. +func (do *Domain) GetRunawayWatchList() []*resourcegroup.QuarantineRecord { + return do.runawayManager.GetWatchList() +} + +// TryToUpdateRunawayWatch is used to to update watch list including +// creation and deletion by manual trigger. +func (do *Domain) TryToUpdateRunawayWatch() error { + return do.updateNewAndDoneWatch() +} + +// RemoveRunawayWatch is used to remove runaway watch item manually. +func (do *Domain) RemoveRunawayWatch(recordID int64) error { + do.runawaySyncer.mu.Lock() + defer do.runawaySyncer.mu.Unlock() + records, err := do.runawaySyncer.getWatchRecordByID(recordID) + if err != nil { + return err + } + if len(records) != 1 { + return errors.Errorf("no runaway watch with the specific ID") + } + err = do.handleRunawayWatchDone(records[0]) + return err +} + +func (do *Domain) runawayRecordFlushLoop() { + defer util.Recover(metrics.LabelDomain, "runawayRecordFlushLoop", nil, false) + + // this times is used to batch flushing rocords, with 1s duration, + // we can guarantee a watch record can be seen by the user within 1s. + runawayRecordFluashTimer := time.NewTimer(runawayRecordFlushInterval) + runawayRecordGCTicker := time.NewTicker(runawayRecordGCInterval) + failpoint.Inject("FastRunawayGC", func() { + runawayRecordFluashTimer.Stop() + runawayRecordGCTicker.Stop() + runawayRecordFluashTimer = time.NewTimer(time.Millisecond * 50) + runawayRecordGCTicker = time.NewTicker(time.Millisecond * 200) + }) + + fired := false + recordCh := do.RunawayManager().RunawayRecordChan() + quarantineRecordCh := do.RunawayManager().QuarantineRecordChan() + staleQuarantineRecordCh := do.RunawayManager().StaleQuarantineRecordChan() + flushThrehold := do.runawayManager.FlushThreshold() + records := make([]*resourcegroup.RunawayRecord, 0, flushThrehold) + + flushRunawayRecords := func() { + if len(records) == 0 { + return + } + sql, params := resourcegroup.GenRunawayQueriesStmt(records) + if _, err := do.execRestrictedSQL(sql, params); err != nil { + logutil.BgLogger().Error("flush runaway records failed", zap.Error(err), zap.Int("count", len(records))) + } + records = records[:0] + } + + for { + select { + case <-do.exit: + return + case <-runawayRecordFluashTimer.C: + flushRunawayRecords() + fired = true + case r := <-recordCh: + records = append(records, r) + failpoint.Inject("FastRunawayGC", func() { + flushRunawayRecords() + }) + if len(records) >= flushThrehold { + flushRunawayRecords() + } else if fired { + fired = false + // meet a new record, reset the timer. + runawayRecordFluashTimer.Reset(runawayRecordFlushInterval) + } + case <-runawayRecordGCTicker.C: + go do.deleteExpiredRows("tidb_runaway_queries", "time", runawayRecordExpiredDuration) + case r := <-quarantineRecordCh: + go func() { + err := do.handleRunawayWatch(r) + if err != nil { + logutil.BgLogger().Error("add runaway watch", zap.Error(err)) + } + }() + case r := <-staleQuarantineRecordCh: + go func() { + for i := 0; i < 3; i++ { + err := do.handleRemoveStaleRunawayWatch(r) + if err == nil { + break + } + logutil.BgLogger().Error("remove stale runaway watch", zap.Error(err)) + time.Sleep(time.Second) + } + }() + } + } +} + +func (do *Domain) handleRunawayWatch(record *resourcegroup.QuarantineRecord) error { + se, err := do.sysSessionPool.Get() + defer func() { + do.sysSessionPool.Put(se) + }() + if err != nil { + return errors.Annotate(err, "get session failed") + } + exec, _ := se.(sqlexec.SQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + _, err = exec.ExecuteInternal(ctx, "BEGIN") + if err != nil { + return errors.Trace(err) + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + }() + sql, params := record.GenInsertionStmt() + _, err = exec.ExecuteInternal(ctx, sql, params...) + return err +} + +func (do *Domain) handleRunawayWatchDone(record *resourcegroup.QuarantineRecord) error { + se, err := do.sysSessionPool.Get() + defer func() { + do.sysSessionPool.Put(se) + }() + if err != nil { + return errors.Annotate(err, "get session failed") + } + exec, _ := se.(sqlexec.SQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + _, err = exec.ExecuteInternal(ctx, "BEGIN") + if err != nil { + return errors.Trace(err) + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + }() + sql, params := record.GenInsertionDoneStmt() + _, err = exec.ExecuteInternal(ctx, sql, params...) + if err != nil { + return err + } + sql, params = record.GenDeletionStmt() + _, err = exec.ExecuteInternal(ctx, sql, params...) + return err +} + +func (do *Domain) handleRemoveStaleRunawayWatch(record *resourcegroup.QuarantineRecord) error { + se, err := do.sysSessionPool.Get() + defer func() { + do.sysSessionPool.Put(se) + }() + if err != nil { + return errors.Annotate(err, "get session failed") + } + exec, _ := se.(sqlexec.SQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + _, err = exec.ExecuteInternal(ctx, "BEGIN") + if err != nil { + return errors.Trace(err) + } + defer func() { + if err != nil { + _, err1 := exec.ExecuteInternal(ctx, "ROLLBACK") + terror.Log(err1) + return + } + _, err = exec.ExecuteInternal(ctx, "COMMIT") + if err != nil { + return + } + }() + sql, params := record.GenDeletionStmt() + _, err = exec.ExecuteInternal(ctx, sql, params...) + return err +} + +func (do *Domain) execRestrictedSQL(sql string, params []interface{}) ([]chunk.Row, error) { + se, err := do.sysSessionPool.Get() + defer func() { + do.sysSessionPool.Put(se) + }() + if err != nil { + return nil, errors.Annotate(err, "get session failed") + } + exec := se.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + r, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, + sql, params..., + ) + return r, err +} + +func (do *Domain) initResourceGroupsController(ctx context.Context, pdClient pd.Client, uniqueID uint64) error { + if pdClient == nil { + logutil.BgLogger().Warn("cannot setup up resource controller, not using tikv storage") + // return nil as unistore doesn't support it + return nil + } + + control, err := rmclient.NewResourceGroupController(ctx, uniqueID, pdClient, nil, rmclient.WithMaxWaitDuration(resourcegroup.MaxWaitDuration)) + if err != nil { + return err + } + control.Start(ctx) + serverInfo, err := infosync.GetServerInfo() + if err != nil { + return err + } + serverAddr := net.JoinHostPort(serverInfo.IP, strconv.Itoa(int(serverInfo.Port))) + do.runawayManager = resourcegroup.NewRunawayManager(control, serverAddr) + do.runawaySyncer = newRunawaySyncer(do.sysSessionPool) + do.resourceGroupsController = control + tikv.SetResourceControlInterceptor(control) + return nil +} + +type runawaySyncer struct { + newWatchReader *SystemTableReader + deletionWatchReader *SystemTableReader + sysSessionPool *sessionPool + mu sync.Mutex +} + +func newRunawaySyncer(sysSessionPool *sessionPool) *runawaySyncer { + return &runawaySyncer{ + sysSessionPool: sysSessionPool, + newWatchReader: &SystemTableReader{ + resourcegroup.RunawayWatchTableName, + "start_time", + resourcegroup.NullTime}, + deletionWatchReader: &SystemTableReader{resourcegroup.RunawayWatchDoneTableName, + "done_time", + resourcegroup.NullTime}, + } +} + +func (s *runawaySyncer) getWatchRecordByID(id int64) ([]*resourcegroup.QuarantineRecord, error) { + return s.getWatchRecord(s.newWatchReader, s.newWatchReader.genSelectByIDStmt(id), false) +} + +func (s *runawaySyncer) getNewWatchRecords() ([]*resourcegroup.QuarantineRecord, error) { + return s.getWatchRecord(s.newWatchReader, s.newWatchReader.genSelectStmt, true) +} + +func (s *runawaySyncer) getNewWatchDoneRecords() ([]*resourcegroup.QuarantineRecord, error) { + return s.getWatchDoneRecord(s.deletionWatchReader, s.deletionWatchReader.genSelectStmt, true) +} + +func (s *runawaySyncer) getWatchRecord(reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { + se, err := s.sysSessionPool.Get() + defer func() { + s.sysSessionPool.Put(se) + }() + if err != nil { + return nil, errors.Annotate(err, "get session failed") + } + exec := se.(sqlexec.RestrictedSQLExecutor) + return getRunawayWatchRecord(exec, reader, sqlGenFn, push) +} + +func (s *runawaySyncer) getWatchDoneRecord(reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { + se, err := s.sysSessionPool.Get() + defer func() { + s.sysSessionPool.Put(se) + }() + if err != nil { + return nil, errors.Annotate(err, "get session failed") + } + exec := se.(sqlexec.RestrictedSQLExecutor) + return getRunawayWatchDoneRecord(exec, reader, sqlGenFn, push) +} + +func getRunawayWatchRecord(exec sqlexec.RestrictedSQLExecutor, reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { + rs, err := reader.Read(exec, sqlGenFn) + if err != nil { + return nil, err + } + ret := make([]*resourcegroup.QuarantineRecord, 0, len(rs)) + now := time.Now().UTC() + for _, r := range rs { + startTime, err := r.GetTime(2).GoTime(time.UTC) + if err != nil { + continue + } + var endTime time.Time + if !r.IsNull(3) { + endTime, err = r.GetTime(3).GoTime(time.UTC) + if err != nil { + continue + } + } + qr := &resourcegroup.QuarantineRecord{ + ID: r.GetInt64(0), + ResourceGroupName: r.GetString(1), + StartTime: startTime, + EndTime: endTime, + Watch: rmpb.RunawayWatchType(r.GetInt64(4)), + WatchText: r.GetString(5), + Source: r.GetString(6), + Action: rmpb.RunawayAction(r.GetInt64(7)), + } + // If a TiDB write record slow, it will occur that the record which has earlier start time is inserted later than others. + // So we start the scan a little earlier. + if push { + reader.CheckPoint = now.Add(-3 * runawayWatchSyncInterval) + } + ret = append(ret, qr) + } + return ret, nil +} + +func getRunawayWatchDoneRecord(exec sqlexec.RestrictedSQLExecutor, reader *SystemTableReader, sqlGenFn func() (string, []interface{}), push bool) ([]*resourcegroup.QuarantineRecord, error) { + rs, err := reader.Read(exec, sqlGenFn) + if err != nil { + return nil, err + } + length := len(rs) + ret := make([]*resourcegroup.QuarantineRecord, 0, length) + now := time.Now().UTC() + for _, r := range rs { + startTime, err := r.GetTime(3).GoTime(time.UTC) + if err != nil { + continue + } + var endTime time.Time + if !r.IsNull(4) { + endTime, err = r.GetTime(4).GoTime(time.UTC) + if err != nil { + continue + } + } + qr := &resourcegroup.QuarantineRecord{ + ID: r.GetInt64(1), + ResourceGroupName: r.GetString(2), + StartTime: startTime, + EndTime: endTime, + Watch: rmpb.RunawayWatchType(r.GetInt64(5)), + WatchText: r.GetString(6), + Source: r.GetString(7), + Action: rmpb.RunawayAction(r.GetInt64(8)), + } + // Ditto as getRunawayWatchRecord. + if push { + reader.CheckPoint = now.Add(-3 * runawayWatchSyncInterval) + } + ret = append(ret, qr) + } + return ret, nil +} + +// SystemTableReader is used to read table `runaway_watch` and `runaway_watch_done`. +type SystemTableReader struct { + TableName string + KeyCol string + CheckPoint time.Time +} + +func (r *SystemTableReader) genSelectByIDStmt(id int64) func() (string, []interface{}) { + return func() (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, 1) + builder.WriteString("select * from ") + builder.WriteString(r.TableName) + builder.WriteString(" where id = %?") + params = append(params, id) + return builder.String(), params + } +} + +func (r *SystemTableReader) genSelectStmt() (string, []interface{}) { + var builder strings.Builder + params := make([]interface{}, 0, 1) + builder.WriteString("select * from ") + builder.WriteString(r.TableName) + builder.WriteString(" where ") + builder.WriteString(r.KeyCol) + builder.WriteString(" > %? order by ") + builder.WriteString(r.KeyCol) + params = append(params, r.CheckPoint) + return builder.String(), params +} + +func (r *SystemTableReader) Read(exec sqlexec.RestrictedSQLExecutor, genFn func() (string, []interface{})) ([]chunk.Row, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + sql, params := genFn() + rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession}, + sql, params..., + ) + return rows, err +} diff --git a/domain/schema_checker.go b/pkg/domain/schema_checker.go similarity index 98% rename from domain/schema_checker.go rename to pkg/domain/schema_checker.go index 30e6366c9d26e..e2a652372beb7 100644 --- a/domain/schema_checker.go +++ b/pkg/domain/schema_checker.go @@ -17,7 +17,7 @@ package domain import ( "time" - "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/pkg/metrics" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/transaction" atomicutil "go.uber.org/atomic" diff --git a/domain/schema_checker_test.go b/pkg/domain/schema_checker_test.go similarity index 98% rename from domain/schema_checker_test.go rename to pkg/domain/schema_checker_test.go index b2b2b8a8d870e..213109163d4fd 100644 --- a/domain/schema_checker_test.go +++ b/pkg/domain/schema_checker_test.go @@ -18,7 +18,7 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/txnkv/transaction" ) diff --git a/domain/schema_validator.go b/pkg/domain/schema_validator.go similarity index 97% rename from domain/schema_validator.go rename to pkg/domain/schema_validator.go index 854e4a8c81d62..830da3c1b2058 100644 --- a/domain/schema_validator.go +++ b/pkg/domain/schema_validator.go @@ -19,11 +19,11 @@ import ( "sync" "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/txnkv/transaction" "go.uber.org/zap" diff --git a/domain/schema_validator_test.go b/pkg/domain/schema_validator_test.go similarity index 99% rename from domain/schema_validator_test.go rename to pkg/domain/schema_validator_test.go index ddcc57634ab60..e1c241d3b4f37 100644 --- a/domain/schema_validator_test.go +++ b/pkg/domain/schema_validator_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/txnkv/transaction" diff --git a/domain/session_pool_test.go b/pkg/domain/session_pool_test.go similarity index 100% rename from domain/session_pool_test.go rename to pkg/domain/session_pool_test.go diff --git a/domain/sysvar_cache.go b/pkg/domain/sysvar_cache.go similarity index 95% rename from domain/sysvar_cache.go rename to pkg/domain/sysvar_cache.go index 6364747cb8355..854a9fc820fb3 100644 --- a/domain/sysvar_cache.go +++ b/pkg/domain/sysvar_cache.go @@ -18,12 +18,12 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" "go.uber.org/zap" "golang.org/x/exp/maps" ) diff --git a/domain/test_helper.go b/pkg/domain/test_helper.go similarity index 95% rename from domain/test_helper.go rename to pkg/domain/test_helper.go index e8c106c29d23b..5b4a605546f25 100644 --- a/domain/test_helper.go +++ b/pkg/domain/test_helper.go @@ -17,8 +17,8 @@ package domain import ( "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/domain/topn_slow_query.go b/pkg/domain/topn_slow_query.go similarity index 98% rename from domain/topn_slow_query.go rename to pkg/domain/topn_slow_query.go index 316639bb5d3c2..29bcd8614a866 100644 --- a/domain/topn_slow_query.go +++ b/pkg/domain/topn_slow_query.go @@ -20,8 +20,8 @@ import ( "sync" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/execdetails" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/execdetails" ) type slowQueryHeap struct { diff --git a/domain/topn_slow_query_test.go b/pkg/domain/topn_slow_query_test.go similarity index 100% rename from domain/topn_slow_query_test.go rename to pkg/domain/topn_slow_query_test.go diff --git a/pkg/errno/BUILD.bazel b/pkg/errno/BUILD.bazel new file mode 100644 index 0000000000000..7afa4a3cc4c39 --- /dev/null +++ b/pkg/errno/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "errno", + srcs = [ + "errcode.go", + "errname.go", + "infoschema.go", + ], + importpath = "github.com/pingcap/tidb/pkg/errno", + visibility = ["//visibility:public"], + deps = ["//pkg/parser/mysql"], +) + +go_test( + name = "errno_test", + timeout = "short", + srcs = [ + "infoschema_test.go", + "main_test.go", + ], + embed = [":errno"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + ], +) diff --git a/errno/errcode.go b/pkg/errno/errcode.go similarity index 100% rename from errno/errcode.go rename to pkg/errno/errcode.go diff --git a/errno/errname.go b/pkg/errno/errname.go similarity index 99% rename from errno/errname.go rename to pkg/errno/errname.go index ed061072408b6..719f23d253868 100644 --- a/errno/errname.go +++ b/pkg/errno/errname.go @@ -14,7 +14,7 @@ package errno -import "github.com/pingcap/tidb/parser/mysql" +import "github.com/pingcap/tidb/pkg/parser/mysql" // MySQLErrName maps error code to MySQL error messages. // Note: all ErrMessage to be added should be considered about the log redaction diff --git a/errno/infoschema.go b/pkg/errno/infoschema.go similarity index 100% rename from errno/infoschema.go rename to pkg/errno/infoschema.go diff --git a/errno/infoschema_test.go b/pkg/errno/infoschema_test.go similarity index 100% rename from errno/infoschema_test.go rename to pkg/errno/infoschema_test.go diff --git a/errno/logredaction.md b/pkg/errno/logredaction.md similarity index 100% rename from errno/logredaction.md rename to pkg/errno/logredaction.md diff --git a/pkg/errno/main_test.go b/pkg/errno/main_test.go new file mode 100644 index 0000000000000..1b13e50f544f8 --- /dev/null +++ b/pkg/errno/main_test.go @@ -0,0 +1,27 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package errno + +import ( + "os" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + os.Exit(m.Run()) +} diff --git a/pkg/executor/BUILD.bazel b/pkg/executor/BUILD.bazel new file mode 100644 index 0000000000000..9a2762c24367d --- /dev/null +++ b/pkg/executor/BUILD.bazel @@ -0,0 +1,478 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "executor", + srcs = [ + "adapter.go", + "admin.go", + "admin_plugins.go", + "admin_telemetry.go", + "analyze.go", + "analyze_col.go", + "analyze_col_v2.go", + "analyze_global_stats.go", + "analyze_idx.go", + "analyze_utils.go", + "analyze_worker.go", + "batch_checker.go", + "batch_point_get.go", + "bind.go", + "brie.go", + "builder.go", + "change.go", + "checksum.go", + "compact_table.go", + "compiler.go", + "concurrent_map.go", + "coprocessor.go", + "cte.go", + "cte_table_reader.go", + "ddl.go", + "delete.go", + "distsql.go", + "executor.go", + "explain.go", + "foreign_key.go", + "grant.go", + "hash_table.go", + "import_into.go", + "index_advise.go", + "index_lookup_hash_join.go", + "index_lookup_join.go", + "index_lookup_merge_join.go", + "index_merge_reader.go", + "infoschema_reader.go", + "insert.go", + "insert_common.go", + "inspection_common.go", + "inspection_profile.go", + "inspection_result.go", + "inspection_summary.go", + "join.go", + "joiner.go", + "load_data.go", + "load_stats.go", + "mem_reader.go", + "memtable_reader.go", + "merge_join.go", + "metrics_reader.go", + "mpp_gather.go", + "opt_rule_blacklist.go", + "parallel_apply.go", + "pipelined_window.go", + "plan_replayer.go", + "point_get.go", + "prepared.go", + "projection.go", + "reload_expr_pushdown_blacklist.go", + "replace.go", + "revoke.go", + "sample.go", + "select_into.go", + "set.go", + "set_config.go", + "show.go", + "show_placement.go", + "show_stats.go", + "shuffle.go", + "simple.go", + "slow_query.go", + "sort.go", + "split.go", + "stmtsummary.go", + "table_reader.go", + "trace.go", + "union_scan.go", + "update.go", + "utils.go", + "window.go", + "write.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/glue", + "//br/pkg/lightning/mydump", + "//br/pkg/storage", + "//br/pkg/task", + "//br/pkg/task/show", + "//br/pkg/utils", + "//pkg/bindinfo", + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/label", + "//pkg/ddl/placement", + "//pkg/ddl/schematracker", + "//pkg/distsql", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/storage", + "//pkg/disttask/importinto", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/executor/aggfuncs", + "//pkg/executor/aggregate", + "//pkg/executor/asyncloaddata", + "//pkg/executor/importer", + "//pkg/executor/internal/applycache", + "//pkg/executor/internal/builder", + "//pkg/executor/internal/calibrateresource", + "//pkg/executor/internal/exec", + "//pkg/executor/internal/mpp", + "//pkg/executor/internal/pdhelper", + "//pkg/executor/internal/querywatch", + "//pkg/executor/internal/util", + "//pkg/executor/internal/vecgroupchecker", + "//pkg/executor/lockstats", + "//pkg/executor/metrics", + "//pkg/executor/mppcoordmanager", + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/infoschema", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/tidb", + "//pkg/parser/types", + "//pkg/planner", + "//pkg/planner/cardinality", + "//pkg/planner/core", + "//pkg/planner/util", + "//pkg/plugin", + "//pkg/privilege", + "//pkg/privilege/privileges", + "//pkg/resourcemanager/pool/workerpool", + "//pkg/resourcemanager/util", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/staleread", + "//pkg/statistics", + "//pkg/statistics/handle", + "//pkg/statistics/handle/cache", + "//pkg/statistics/handle/storage", + "//pkg/store/driver/backoff", + "//pkg/store/driver/txn", + "//pkg/store/helper", + "//pkg/table", + "//pkg/table/tables", + "//pkg/table/temptable", + "//pkg/tablecodec", + "//pkg/telemetry", + "//pkg/tidb-binlog/node", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/admin", + "//pkg/util/bitmap", + "//pkg/util/breakpoint", + "//pkg/util/channel", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/cteutil", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/deadlockhistory", + "//pkg/util/disk", + "//pkg/util/disttask", + "//pkg/util/etcd", + "//pkg/util/execdetails", + "//pkg/util/filter", + "//pkg/util/format", + "//pkg/util/gcutil", + "//pkg/util/globalconn", + "//pkg/util/hack", + "//pkg/util/hint", + "//pkg/util/intest", + "//pkg/util/keydecoder", + "//pkg/util/logutil", + "//pkg/util/logutil/consistency", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/mvmap", + "//pkg/util/password-validation", + "//pkg/util/pdapi", + "//pkg/util/plancodec", + "//pkg/util/printer", + "//pkg/util/ranger", + "//pkg/util/replayer", + "//pkg/util/resourcegrouptag", + "//pkg/util/rowDecoder", + "//pkg/util/rowcodec", + "//pkg/util/sem", + "//pkg/util/servermemorylimit", + "//pkg/util/set", + "//pkg/util/size", + "//pkg/util/sqlexec", + "//pkg/util/stmtsummary", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/stringutil", + "//pkg/util/syncutil", + "//pkg/util/table-filter", + "//pkg/util/tiflash", + "//pkg/util/timeutil", + "//pkg/util/tls", + "//pkg/util/topsql", + "//pkg/util/topsql/state", + "//pkg/util/tracing", + "@com_github_burntsushi_toml//:toml", + "@com_github_docker_go_units//:go-units", + "@com_github_gogo_protobuf//proto", + "@com_github_ngaut_pools//:pools", + "@com_github_opentracing_basictracer_go//:basictracer-go", + "@com_github_opentracing_opentracing_go//:opentracing-go", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/brpb", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_kvproto//pkg/encryptionpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_pingcap_kvproto//pkg/tikvpb", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_sysutil//:sysutil", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_prometheus_client_golang//api", + "@com_github_prometheus_client_golang//api/prometheus/v1:prometheus", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_common//model", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//txnkv", + "@com_github_tikv_client_go_v2//txnkv/txnlock", + "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@com_github_twmb_murmur3//:murmur3", + "@com_sourcegraph_sourcegraph_appdash//:appdash", + "@com_sourcegraph_sourcegraph_appdash//opentracing", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//credentials", + "@org_golang_google_grpc//credentials/insecure", + "@org_golang_google_grpc//status", + "@org_golang_x_exp//maps", + "@org_golang_x_sync//errgroup", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "executor_test", + timeout = "moderate", + srcs = [ + "adapter_test.go", + "analyze_test.go", + "analyze_utils_test.go", + "batch_point_get_test.go", + "benchmark_test.go", + "brie_test.go", + "charset_test.go", + "chunk_size_control_test.go", + "cluster_table_test.go", + "compact_table_test.go", + "concurrent_map_test.go", + "copr_cache_test.go", + "cte_test.go", + "delete_test.go", + "distsql_test.go", + "executor_failpoint_test.go", + "executor_pkg_test.go", + "executor_required_rows_test.go", + "executor_test.go", + "executor_txn_test.go", + "explain_test.go", + "explain_unit_test.go", + "explainfor_test.go", + "grant_test.go", + "hash_table_test.go", + "historical_stats_test.go", + "hot_regions_history_table_test.go", + "import_into_test.go", + "index_advise_test.go", + "index_lookup_join_test.go", + "index_lookup_merge_join_test.go", + "infoschema_cluster_table_test.go", + "infoschema_reader_internal_test.go", + "infoschema_reader_test.go", + "insert_test.go", + "inspection_common_test.go", + "inspection_result_test.go", + "inspection_summary_test.go", + "join_pkg_test.go", + "join_test.go", + "joiner_test.go", + "main_test.go", + "memtable_reader_test.go", + "merge_join_test.go", + "metrics_reader_test.go", + "parallel_apply_test.go", + "partition_table_test.go", + "pkg_test.go", + "point_get_test.go", + "prepared_test.go", + "recover_test.go", + "resource_tag_test.go", + "revoke_test.go", + "rowid_test.go", + "sample_test.go", + "select_into_test.go", + "set_test.go", + "show_placement_labels_test.go", + "show_placement_test.go", + "show_stats_test.go", + "show_test.go", + "shuffle_test.go", + "simple_test.go", + "slow_query_sql_test.go", + "slow_query_test.go", + "sort_test.go", + "split_test.go", + "stale_txn_test.go", + "statement_context_test.go", + "stmtsummary_test.go", + "table_readers_required_rows_test.go", + "temporary_table_test.go", + "tikv_regions_peers_table_test.go", + "trace_test.go", + "union_scan_test.go", + "update_test.go", + "utils_test.go", + "window_test.go", + "write_concurrent_test.go", + ], + data = glob(["testdata/**"]), + embed = [":executor"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/distsql", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor/aggfuncs", + "//pkg/executor/aggregate", + "//pkg/executor/importer", + "//pkg/executor/internal/builder", + "//pkg/executor/internal/exec", + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/planner/property", + "//pkg/planner/util", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/staleread", + "//pkg/statistics", + "//pkg/statistics/handle/globalstats", + "//pkg/statistics/handle/storage", + "//pkg/store/copr", + "//pkg/store/driver/error", + "//pkg/store/helper", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/deadlockhistory", + "//pkg/util/disk", + "//pkg/util/execdetails", + "//pkg/util/gcutil", + "//pkg/util/globalconn", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/paging", + "//pkg/util/pdapi", + "//pkg/util/plancodec", + "//pkg/util/ranger", + "//pkg/util/sem", + "//pkg/util/set", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/stringutil", + "//pkg/util/syncutil", + "//pkg/util/tableutil", + "//pkg/util/topsql/state", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_fn//:fn", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_sysutil//:sysutil", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_model//go", + "@com_github_prometheus_common//model", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//util", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//zapcore", + ], +) diff --git a/executor/adapter.go b/pkg/executor/adapter.go similarity index 97% rename from executor/adapter.go rename to pkg/executor/adapter.go index a281c9913dc8c..100cb2cffaf57 100644 --- a/executor/adapter.go +++ b/pkg/executor/adapter.go @@ -28,48 +28,48 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - executor_metrics "github.com/pingcap/tidb/executor/metrics" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/breakpoint" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stmtsummary" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + executor_metrics "github.com/pingcap/tidb/pkg/executor/metrics" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/breakpoint" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/prometheus/client_golang/prometheus" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/oracle" diff --git a/executor/adapter_test.go b/pkg/executor/adapter_test.go similarity index 92% rename from executor/adapter_test.go rename to pkg/executor/adapter_test.go index 012075c8e4a0c..1956fa4bda6d6 100644 --- a/executor/adapter_test.go +++ b/pkg/executor/adapter_test.go @@ -18,9 +18,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/admin.go b/pkg/executor/admin.go new file mode 100644 index 0000000000000..7741bbdd7680f --- /dev/null +++ b/pkg/executor/admin.go @@ -0,0 +1,890 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "math" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +var ( + _ exec.Executor = &CheckIndexRangeExec{} + _ exec.Executor = &RecoverIndexExec{} + _ exec.Executor = &CleanupIndexExec{} +) + +// CheckIndexRangeExec outputs the index values which has handle between begin and end. +type CheckIndexRangeExec struct { + exec.BaseExecutor + + table *model.TableInfo + index *model.IndexInfo + is infoschema.InfoSchema + startKey []types.Datum + + handleRanges []ast.HandleRange + srcChunk *chunk.Chunk + + result distsql.SelectResult + cols []*model.ColumnInfo +} + +// Next implements the Executor Next interface. +func (e *CheckIndexRangeExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + handleIdx := e.GetSchema().Len() - 1 + for { + err := e.result.Next(ctx, e.srcChunk) + if err != nil { + return err + } + if e.srcChunk.NumRows() == 0 { + return nil + } + iter := chunk.NewIterator4Chunk(e.srcChunk) + appendRows := make([]chunk.Row, 0, e.srcChunk.NumRows()) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + handle := row.GetInt64(handleIdx) + for _, hr := range e.handleRanges { + if handle >= hr.Begin && handle < hr.End { + appendRows = append(appendRows, row) + break + } + } + } + if len(appendRows) > 0 { + req.AppendRows(appendRows) + } + if req.NumRows() > 0 { + return nil + } + } +} + +// Open implements the Executor Open interface. +func (e *CheckIndexRangeExec) Open(ctx context.Context) error { + tCols := e.table.Cols() + for _, ic := range e.index.Columns { + col := tCols[ic.Offset] + e.cols = append(e.cols, col) + } + + colTypeForHandle := e.GetSchema().Columns[len(e.cols)].RetType + e.cols = append(e.cols, &model.ColumnInfo{ + ID: model.ExtraHandleID, + Name: model.ExtraHandleName, + FieldType: *colTypeForHandle, + }) + + e.srcChunk = exec.TryNewCacheChunk(e) + dagPB, err := e.buildDAGPB() + if err != nil { + return err + } + sc := e.Ctx().GetSessionVars().StmtCtx + txn, err := e.Ctx().Txn(true) + if err != nil { + return nil + } + var builder distsql.RequestBuilder + kvReq, err := builder.SetIndexRanges(sc, e.table.ID, e.index.ID, ranger.FullRange()). + SetDAGRequest(dagPB). + SetStartTS(txn.StartTS()). + SetKeepOrder(true). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetConnID(e.Ctx().GetSessionVars().ConnectionID). + Build() + if err != nil { + return err + } + + e.result, err = distsql.Select(ctx, e.Ctx(), kvReq, e.RetFieldTypes()) + if err != nil { + return err + } + return nil +} + +func (e *CheckIndexRangeExec) buildDAGPB() (*tipb.DAGRequest, error) { + dagReq := &tipb.DAGRequest{} + dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) + sc := e.Ctx().GetSessionVars().StmtCtx + dagReq.Flags = sc.PushDownFlags() + for i := range e.Schema().Columns { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) + } + execPB := e.constructIndexScanPB() + dagReq.Executors = append(dagReq.Executors, execPB) + + err := tables.SetPBColumnsDefaultValue(e.Ctx(), dagReq.Executors[0].IdxScan.Columns, e.cols) + if err != nil { + return nil, err + } + distsql.SetEncodeType(e.Ctx(), dagReq) + return dagReq, nil +} + +func (e *CheckIndexRangeExec) constructIndexScanPB() *tipb.Executor { + idxExec := &tipb.IndexScan{ + TableId: e.table.ID, + IndexId: e.index.ID, + Columns: util.ColumnsToProto(e.cols, e.table.PKIsHandle, true), + } + return &tipb.Executor{Tp: tipb.ExecType_TypeIndexScan, IdxScan: idxExec} +} + +// Close implements the Executor Close interface. +func (*CheckIndexRangeExec) Close() error { + return nil +} + +// RecoverIndexExec represents a recover index executor. +// It is built from "admin recover index" statement, is used to backfill +// corrupted index. +type RecoverIndexExec struct { + exec.BaseExecutor + + done bool + + index table.Index + table table.Table + physicalID int64 + batchSize int + + columns []*model.ColumnInfo + colFieldTypes []*types.FieldType + srcChunk *chunk.Chunk + handleCols plannercore.HandleCols + + containsGenedCol bool + cols []*expression.Column + + // below buf is used to reduce allocations. + recoverRows []recoverRows + idxValsBufs [][]types.Datum + idxKeyBufs [][]byte + batchKeys []kv.Key +} + +func (e *RecoverIndexExec) columnsTypes() []*types.FieldType { + if e.colFieldTypes != nil { + return e.colFieldTypes + } + + e.colFieldTypes = make([]*types.FieldType, 0, len(e.columns)) + for _, col := range e.columns { + e.colFieldTypes = append(e.colFieldTypes, &col.FieldType) + } + return e.colFieldTypes +} + +// Open implements the Executor Open interface. +func (e *RecoverIndexExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + + e.srcChunk = chunk.New(e.columnsTypes(), e.InitCap(), e.MaxChunkSize()) + e.batchSize = 2048 + e.recoverRows = make([]recoverRows, 0, e.batchSize) + e.idxValsBufs = make([][]types.Datum, e.batchSize) + e.idxKeyBufs = make([][]byte, e.batchSize) + return nil +} + +func (e *RecoverIndexExec) constructTableScanPB(tblInfo *model.TableInfo, colInfos []*model.ColumnInfo) (*tipb.Executor, error) { + tblScan := tables.BuildTableScanFromInfos(tblInfo, colInfos) + tblScan.TableId = e.physicalID + err := tables.SetPBColumnsDefaultValue(e.Ctx(), tblScan.Columns, colInfos) + return &tipb.Executor{Tp: tipb.ExecType_TypeTableScan, TblScan: tblScan}, err +} + +func (*RecoverIndexExec) constructLimitPB(count uint64) *tipb.Executor { + limitExec := &tipb.Limit{ + Limit: count, + } + return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} +} + +func (e *RecoverIndexExec) buildDAGPB(_ kv.Transaction, limitCnt uint64) (*tipb.DAGRequest, error) { + dagReq := &tipb.DAGRequest{} + dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) + sc := e.Ctx().GetSessionVars().StmtCtx + dagReq.Flags = sc.PushDownFlags() + for i := range e.columns { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) + } + + tblScanExec, err := e.constructTableScanPB(e.table.Meta(), e.columns) + if err != nil { + return nil, err + } + dagReq.Executors = append(dagReq.Executors, tblScanExec) + + limitExec := e.constructLimitPB(limitCnt) + dagReq.Executors = append(dagReq.Executors, limitExec) + distsql.SetEncodeType(e.Ctx(), dagReq) + return dagReq, nil +} + +func (e *RecoverIndexExec) buildTableScan(ctx context.Context, txn kv.Transaction, startHandle kv.Handle, limitCnt uint64) (distsql.SelectResult, error) { + dagPB, err := e.buildDAGPB(txn, limitCnt) + if err != nil { + return nil, err + } + var builder distsql.RequestBuilder + keyRanges, err := buildRecoverIndexKeyRanges(e.physicalID, startHandle) + if err != nil { + return nil, err + } + builder.KeyRanges = kv.NewNonParitionedKeyRanges(keyRanges) + kvReq, err := builder. + SetDAGRequest(dagPB). + SetStartTS(txn.StartTS()). + SetKeepOrder(true). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetConnID(e.Ctx().GetSessionVars().ConnectionID). + Build() + if err != nil { + return nil, err + } + + // Actually, with limitCnt, the match datas maybe only in one region, so let the concurrency to be 1, + // avoid unnecessary region scan. + kvReq.Concurrency = 1 + result, err := distsql.Select(ctx, e.Ctx(), kvReq, e.columnsTypes()) + if err != nil { + return nil, err + } + return result, nil +} + +// buildRecoverIndexKeyRanges build a KeyRange: (startHandle, unlimited). +func buildRecoverIndexKeyRanges(tid int64, startHandle kv.Handle) ([]kv.KeyRange, error) { + var startKey []byte + if startHandle == nil { + startKey = tablecodec.GenTableRecordPrefix(tid).Next() + } else { + startKey = tablecodec.EncodeRowKey(tid, startHandle.Encoded()).PrefixNext() + } + endKey := tablecodec.GenTableRecordPrefix(tid).PrefixNext() + return []kv.KeyRange{{StartKey: startKey, EndKey: endKey}}, nil +} + +type backfillResult struct { + currentHandle kv.Handle + addedCount int64 + scanRowCount int64 +} + +func (e *RecoverIndexExec) backfillIndex(ctx context.Context) (totalAddedCnt, totalScanCnt int64, err error) { + var ( + currentHandle kv.Handle + lastLogCnt int64 + result backfillResult + ) + for { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) + errInTxn := kv.RunInNewTxn(ctx, e.Ctx().GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + setOptionForTopSQL(e.Ctx().GetSessionVars().StmtCtx, txn) + var err error + result, err = e.backfillIndexInTxn(ctx, txn, currentHandle) + return err + }) + if errInTxn != nil { + return totalAddedCnt, totalScanCnt, errInTxn + } + totalAddedCnt += result.addedCount + totalScanCnt += result.scanRowCount + if totalScanCnt-lastLogCnt >= 50000 { + lastLogCnt = totalScanCnt + logutil.Logger(ctx).Info("recover index", zap.String("table", e.table.Meta().Name.O), + zap.String("index", e.index.Meta().Name.O), zap.Int64("totalAddedCnt", totalAddedCnt), + zap.Int64("totalScanCnt", totalScanCnt), zap.Stringer("currentHandle", result.currentHandle)) + } + + // no more rows + if result.scanRowCount == 0 { + break + } + currentHandle = result.currentHandle + if currentHandle.Next().Compare(result.currentHandle) <= 0 { + break // There is no more handles in the table. + } + } + return totalAddedCnt, totalScanCnt, nil +} + +type recoverRows struct { + handle kv.Handle + idxVals []types.Datum + rsData []types.Datum + skip bool +} + +func (e *RecoverIndexExec) fetchRecoverRows(ctx context.Context, srcResult distsql.SelectResult, result *backfillResult) ([]recoverRows, error) { + e.recoverRows = e.recoverRows[:0] + idxValLen := len(e.index.Meta().Columns) + result.scanRowCount = 0 + + for { + err := srcResult.Next(ctx, e.srcChunk) + if err != nil { + return nil, err + } + + if e.srcChunk.NumRows() == 0 { + break + } + iter := chunk.NewIterator4Chunk(e.srcChunk) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + if result.scanRowCount >= int64(e.batchSize) { + return e.recoverRows, nil + } + handle, err := e.handleCols.BuildHandle(row) + if err != nil { + return nil, err + } + idxVals, err := e.buildIndexedValues(row, e.idxValsBufs[result.scanRowCount], e.colFieldTypes, idxValLen) + if err != nil { + return nil, err + } + e.idxValsBufs[result.scanRowCount] = idxVals + rsData := tables.TryGetHandleRestoredDataWrapper(e.table.Meta(), plannercore.GetCommonHandleDatum(e.handleCols, row), nil, e.index.Meta()) + e.recoverRows = append(e.recoverRows, recoverRows{handle: handle, idxVals: idxVals, rsData: rsData, skip: true}) + result.scanRowCount++ + result.currentHandle = handle + } + } + + return e.recoverRows, nil +} + +func (e *RecoverIndexExec) buildIndexedValues(row chunk.Row, idxVals []types.Datum, fieldTypes []*types.FieldType, idxValLen int) ([]types.Datum, error) { + if !e.containsGenedCol { + return extractIdxVals(row, idxVals, fieldTypes, idxValLen), nil + } + + if e.cols == nil { + columns, _, err := expression.ColumnInfos2ColumnsAndNames(e.Ctx(), model.NewCIStr("mock"), e.table.Meta().Name, e.table.Meta().Columns, e.table.Meta()) + if err != nil { + return nil, err + } + e.cols = columns + } + + if cap(idxVals) < idxValLen { + idxVals = make([]types.Datum, idxValLen) + } else { + idxVals = idxVals[:idxValLen] + } + + for i, col := range e.index.Meta().Columns { + if e.table.Meta().Columns[col.Offset].IsGenerated() { + val, err := e.cols[col.Offset].EvalVirtualColumn(row) + if err != nil { + return nil, err + } + val.Copy(&idxVals[i]) + } else { + val := row.GetDatum(col.Offset, &(e.table.Meta().Columns[col.Offset].FieldType)) + val.Copy(&idxVals[i]) + } + } + return idxVals, nil +} + +func (e *RecoverIndexExec) batchMarkDup(txn kv.Transaction, rows []recoverRows) error { + if len(rows) == 0 { + return nil + } + e.batchKeys = e.batchKeys[:0] + sc := e.Ctx().GetSessionVars().StmtCtx + distinctFlags := make([]bool, 0, len(rows)) + rowIdx := make([]int, 0, len(rows)) + cnt := 0 + for i, row := range rows { + iter := e.index.GenIndexKVIter(sc, row.idxVals, row.handle, nil) + for iter.Valid() { + var buf []byte + if cnt < len(e.idxKeyBufs) { + buf = e.idxKeyBufs[cnt] + } + key, _, distinct, err := iter.Next(buf) + if err != nil { + return err + } + if cnt < len(e.idxKeyBufs) { + e.idxKeyBufs[cnt] = key + } else { + e.idxKeyBufs = append(e.idxKeyBufs, key) + } + + cnt++ + e.batchKeys = append(e.batchKeys, key) + distinctFlags = append(distinctFlags, distinct) + rowIdx = append(rowIdx, i) + } + } + + values, err := txn.BatchGet(context.Background(), e.batchKeys) + if err != nil { + return err + } + + // 1. unique-key is duplicate and the handle is equal, skip it. + // 2. unique-key is duplicate and the handle is not equal, data is not consistent, log it and skip it. + // 3. non-unique-key is duplicate, skip it. + isCommonHandle := e.table.Meta().IsCommonHandle + for i, key := range e.batchKeys { + val, found := values[string(key)] + if found { + if distinctFlags[i] { + handle, err1 := tablecodec.DecodeHandleInUniqueIndexValue(val, isCommonHandle) + if err1 != nil { + return err1 + } + + if handle.Compare(rows[rowIdx[i]].handle) != 0 { + logutil.BgLogger().Warn("recover index: the constraint of unique index is broken, handle in index is not equal to handle in table", + zap.String("index", e.index.Meta().Name.O), zap.ByteString("indexKey", key), + zap.Stringer("handleInTable", rows[rowIdx[i]].handle), zap.Stringer("handleInIndex", handle)) + } + } + } + rows[rowIdx[i]].skip = found && rows[rowIdx[i]].skip + } + return nil +} + +func (e *RecoverIndexExec) backfillIndexInTxn(ctx context.Context, txn kv.Transaction, currentHandle kv.Handle) (result backfillResult, err error) { + srcResult, err := e.buildTableScan(ctx, txn, currentHandle, uint64(e.batchSize)) + if err != nil { + return result, err + } + defer terror.Call(srcResult.Close) + + rows, err := e.fetchRecoverRows(ctx, srcResult, &result) + if err != nil { + return result, err + } + + err = e.batchMarkDup(txn, rows) + if err != nil { + return result, err + } + + // Constrains is already checked. + e.Ctx().GetSessionVars().StmtCtx.BatchCheck = true + for _, row := range rows { + if row.skip { + continue + } + + recordKey := tablecodec.EncodeRecordKey(e.table.RecordPrefix(), row.handle) + err := txn.LockKeys(ctx, new(kv.LockCtx), recordKey) + if err != nil { + return result, err + } + + _, err = e.index.Create(e.Ctx(), txn, row.idxVals, row.handle, row.rsData, table.WithIgnoreAssertion) + if err != nil { + return result, err + } + result.addedCount++ + } + return result, nil +} + +// Next implements the Executor Next interface. +func (e *RecoverIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + + recoveringClusteredIndex := e.index.Meta().Primary && e.table.Meta().IsCommonHandle + if recoveringClusteredIndex { + req.AppendInt64(0, 0) + req.AppendInt64(1, 0) + e.done = true + return nil + } + var totalAddedCnt, totalScanCnt int64 + var err error + if tbl, ok := e.table.(table.PartitionedTable); ok { + pi := e.table.Meta().GetPartitionInfo() + for _, p := range pi.Definitions { + e.table = tbl.GetPartition(p.ID) + e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) + e.physicalID = p.ID + addedCnt, scanCnt, err := e.backfillIndex(ctx) + totalAddedCnt += addedCnt + totalScanCnt += scanCnt + if err != nil { + return err + } + } + } else { + totalAddedCnt, totalScanCnt, err = e.backfillIndex(ctx) + if err != nil { + return err + } + } + + req.AppendInt64(0, totalAddedCnt) + req.AppendInt64(1, totalScanCnt) + e.done = true + return nil +} + +// CleanupIndexExec represents a cleanup index executor. +// It is built from "admin cleanup index" statement, is used to delete +// dangling index data. +type CleanupIndexExec struct { + exec.BaseExecutor + + done bool + removeCnt uint64 + + index table.Index + table table.Table + physicalID int64 + + columns []*model.ColumnInfo + idxColFieldTypes []*types.FieldType + idxChunk *chunk.Chunk + handleCols plannercore.HandleCols + + idxValues *kv.HandleMap // kv.Handle -> [][]types.Datum + batchSize uint64 + batchKeys []kv.Key + idxValsBufs [][]types.Datum + lastIdxKey []byte + scanRowCnt uint64 +} + +func (e *CleanupIndexExec) getIdxColTypes() []*types.FieldType { + if e.idxColFieldTypes != nil { + return e.idxColFieldTypes + } + e.idxColFieldTypes = make([]*types.FieldType, 0, len(e.columns)) + for _, col := range e.columns { + e.idxColFieldTypes = append(e.idxColFieldTypes, col.FieldType.ArrayType()) + } + return e.idxColFieldTypes +} + +func (e *CleanupIndexExec) batchGetRecord(txn kv.Transaction) (map[string][]byte, error) { + e.idxValues.Range(func(h kv.Handle, _ interface{}) bool { + e.batchKeys = append(e.batchKeys, tablecodec.EncodeRecordKey(e.table.RecordPrefix(), h)) + return true + }) + values, err := txn.BatchGet(context.Background(), e.batchKeys) + if err != nil { + return nil, err + } + return values, nil +} + +func (e *CleanupIndexExec) deleteDanglingIdx(txn kv.Transaction, values map[string][]byte) error { + for _, k := range e.batchKeys { + if _, found := values[string(k)]; !found { + _, handle, err := tablecodec.DecodeRecordKey(k) + if err != nil { + return err + } + handleIdxValsGroup, ok := e.idxValues.Get(handle) + if !ok { + return errors.Trace(errors.Errorf("batch keys are inconsistent with handles")) + } + for _, handleIdxVals := range handleIdxValsGroup.([][]types.Datum) { + if err := e.index.Delete(e.Ctx().GetSessionVars().StmtCtx, txn, handleIdxVals, handle); err != nil { + return err + } + e.removeCnt++ + if e.removeCnt%e.batchSize == 0 { + logutil.BgLogger().Info("clean up dangling index", zap.String("table", e.table.Meta().Name.String()), + zap.String("index", e.index.Meta().Name.String()), zap.Uint64("count", e.removeCnt)) + } + } + } + } + return nil +} + +func extractIdxVals(row chunk.Row, idxVals []types.Datum, + fieldTypes []*types.FieldType, idxValLen int) []types.Datum { + if cap(idxVals) < idxValLen { + idxVals = make([]types.Datum, idxValLen) + } else { + idxVals = idxVals[:idxValLen] + } + + for i := 0; i < idxValLen; i++ { + colVal := row.GetDatum(i, fieldTypes[i]) + colVal.Copy(&idxVals[i]) + } + return idxVals +} + +func (e *CleanupIndexExec) fetchIndex(ctx context.Context, txn kv.Transaction) error { + result, err := e.buildIndexScan(ctx, txn) + if err != nil { + return err + } + defer terror.Call(result.Close) + + sc := e.Ctx().GetSessionVars().StmtCtx + idxColLen := len(e.index.Meta().Columns) + for { + err := result.Next(ctx, e.idxChunk) + if err != nil { + return err + } + if e.idxChunk.NumRows() == 0 { + return nil + } + iter := chunk.NewIterator4Chunk(e.idxChunk) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + handle, err := e.handleCols.BuildHandle(row) + if err != nil { + return err + } + idxVals := extractIdxVals(row, e.idxValsBufs[e.scanRowCnt], e.idxColFieldTypes, idxColLen) + e.idxValsBufs[e.scanRowCnt] = idxVals + existingIdxVals, ok := e.idxValues.Get(handle) + if ok { + updatedIdxVals := append(existingIdxVals.([][]types.Datum), idxVals) + e.idxValues.Set(handle, updatedIdxVals) + } else { + e.idxValues.Set(handle, [][]types.Datum{idxVals}) + } + idxKey, _, err := e.index.GenIndexKey(sc, idxVals, handle, nil) + if err != nil { + return err + } + e.scanRowCnt++ + e.lastIdxKey = idxKey + if e.scanRowCnt >= e.batchSize { + return nil + } + } + } +} + +// Next implements the Executor Next interface. +func (e *CleanupIndexExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + cleaningClusteredPrimaryKey := e.table.Meta().IsCommonHandle && e.index.Meta().Primary + if cleaningClusteredPrimaryKey { + e.done = true + req.AppendUint64(0, 0) + return nil + } + + var err error + if tbl, ok := e.table.(table.PartitionedTable); ok { + pi := e.table.Meta().GetPartitionInfo() + for _, p := range pi.Definitions { + e.table = tbl.GetPartition(p.ID) + e.index = tables.GetWritableIndexByName(e.index.Meta().Name.L, e.table) + e.physicalID = p.ID + err = e.init() + if err != nil { + return err + } + err = e.cleanTableIndex(ctx) + if err != nil { + return err + } + } + } else { + err = e.cleanTableIndex(ctx) + if err != nil { + return err + } + } + e.done = true + req.AppendUint64(0, e.removeCnt) + return nil +} + +func (e *CleanupIndexExec) cleanTableIndex(ctx context.Context) error { + for { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) + errInTxn := kv.RunInNewTxn(ctx, e.Ctx().GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + txn.SetDiskFullOpt(kvrpcpb.DiskFullOpt_AllowedOnAlmostFull) + setOptionForTopSQL(e.Ctx().GetSessionVars().StmtCtx, txn) + err := e.fetchIndex(ctx, txn) + if err != nil { + return err + } + values, err := e.batchGetRecord(txn) + if err != nil { + return err + } + err = e.deleteDanglingIdx(txn, values) + if err != nil { + return err + } + return nil + }) + if errInTxn != nil { + return errInTxn + } + if e.scanRowCnt == 0 { + break + } + e.scanRowCnt = 0 + e.batchKeys = e.batchKeys[:0] + e.idxValues.Range(func(h kv.Handle, val interface{}) bool { + e.idxValues.Delete(h) + return true + }) + } + return nil +} + +func (e *CleanupIndexExec) buildIndexScan(ctx context.Context, txn kv.Transaction) (distsql.SelectResult, error) { + dagPB, err := e.buildIdxDAGPB() + if err != nil { + return nil, err + } + sc := e.Ctx().GetSessionVars().StmtCtx + var builder distsql.RequestBuilder + ranges := ranger.FullRange() + keyRanges, err := distsql.IndexRangesToKVRanges(sc, e.physicalID, e.index.Meta().ID, ranges) + if err != nil { + return nil, err + } + err = keyRanges.SetToNonPartitioned() + if err != nil { + return nil, err + } + keyRanges.FirstPartitionRange()[0].StartKey = kv.Key(e.lastIdxKey).PrefixNext() + kvReq, err := builder.SetWrappedKeyRanges(keyRanges). + SetDAGRequest(dagPB). + SetStartTS(txn.StartTS()). + SetKeepOrder(true). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetConnID(e.Ctx().GetSessionVars().ConnectionID). + Build() + if err != nil { + return nil, err + } + + kvReq.Concurrency = 1 + result, err := distsql.Select(ctx, e.Ctx(), kvReq, e.getIdxColTypes()) + if err != nil { + return nil, err + } + return result, nil +} + +// Open implements the Executor Open interface. +func (e *CleanupIndexExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + return e.init() +} + +func (e *CleanupIndexExec) init() error { + e.idxChunk = chunk.New(e.getIdxColTypes(), e.InitCap(), e.MaxChunkSize()) + e.idxValues = kv.NewHandleMap() + e.batchKeys = make([]kv.Key, 0, e.batchSize) + e.idxValsBufs = make([][]types.Datum, e.batchSize) + sc := e.Ctx().GetSessionVars().StmtCtx + idxKey, _, err := e.index.GenIndexKey(sc, []types.Datum{{}}, kv.IntHandle(math.MinInt64), nil) + if err != nil { + return err + } + e.lastIdxKey = idxKey + return nil +} + +func (e *CleanupIndexExec) buildIdxDAGPB() (*tipb.DAGRequest, error) { + dagReq := &tipb.DAGRequest{} + dagReq.TimeZoneName, dagReq.TimeZoneOffset = timeutil.Zone(e.Ctx().GetSessionVars().Location()) + sc := e.Ctx().GetSessionVars().StmtCtx + dagReq.Flags = sc.PushDownFlags() + for i := range e.columns { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) + } + + execPB := e.constructIndexScanPB() + dagReq.Executors = append(dagReq.Executors, execPB) + err := tables.SetPBColumnsDefaultValue(e.Ctx(), dagReq.Executors[0].IdxScan.Columns, e.columns) + if err != nil { + return nil, err + } + + limitExec := e.constructLimitPB() + dagReq.Executors = append(dagReq.Executors, limitExec) + distsql.SetEncodeType(e.Ctx(), dagReq) + return dagReq, nil +} + +func (e *CleanupIndexExec) constructIndexScanPB() *tipb.Executor { + idxExec := &tipb.IndexScan{ + TableId: e.physicalID, + IndexId: e.index.Meta().ID, + Columns: util.ColumnsToProto(e.columns, e.table.Meta().PKIsHandle, true), + PrimaryColumnIds: tables.TryGetCommonPkColumnIds(e.table.Meta()), + } + return &tipb.Executor{Tp: tipb.ExecType_TypeIndexScan, IdxScan: idxExec} +} + +func (e *CleanupIndexExec) constructLimitPB() *tipb.Executor { + limitExec := &tipb.Limit{ + Limit: e.batchSize, + } + return &tipb.Executor{Tp: tipb.ExecType_TypeLimit, Limit: limitExec} +} + +// Close implements the Executor Close interface. +func (*CleanupIndexExec) Close() error { + return nil +} diff --git a/executor/admin_plugins.go b/pkg/executor/admin_plugins.go similarity index 86% rename from executor/admin_plugins.go rename to pkg/executor/admin_plugins.go index ddd1ab52d0d6f..150602789b27d 100644 --- a/executor/admin_plugins.go +++ b/pkg/executor/admin_plugins.go @@ -17,11 +17,11 @@ package executor import ( "context" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/util/chunk" ) // AdminPluginsExec indicates AdminPlugins executor. diff --git a/executor/admin_telemetry.go b/pkg/executor/admin_telemetry.go similarity index 91% rename from executor/admin_telemetry.go rename to pkg/executor/admin_telemetry.go index 7689a3ac99772..ce7448b52235c 100644 --- a/executor/admin_telemetry.go +++ b/pkg/executor/admin_telemetry.go @@ -17,10 +17,10 @@ package executor import ( "context" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/util/chunk" ) // AdminShowTelemetryExec is an executor for ADMIN SHOW TELEMETRY. diff --git a/pkg/executor/aggfuncs/BUILD.bazel b/pkg/executor/aggfuncs/BUILD.bazel new file mode 100644 index 0000000000000..89eaa4eced8b9 --- /dev/null +++ b/pkg/executor/aggfuncs/BUILD.bazel @@ -0,0 +1,117 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "aggfuncs", + srcs = [ + "aggfuncs.go", + "builder.go", + "func_avg.go", + "func_bitfuncs.go", + "func_count.go", + "func_count_distinct.go", + "func_cume_dist.go", + "func_first_row.go", + "func_group_concat.go", + "func_json_arrayagg.go", + "func_json_objectagg.go", + "func_lead_lag.go", + "func_max_min.go", + "func_ntile.go", + "func_percent_rank.go", + "func_percentile.go", + "func_rank.go", + "func_stddevpop.go", + "func_stddevsamp.go", + "func_sum.go", + "func_value.go", + "func_varpop.go", + "func_varsamp.go", + "row_number.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/aggfuncs", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/planner/util", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/selection", + "//pkg/util/set", + "//pkg/util/stringutil", + "@com_github_dgryski_go_farm//:go-farm", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "aggfuncs_test", + timeout = "moderate", + srcs = [ + "aggfunc_test.go", + "export_test.go", + "func_avg_test.go", + "func_bitfuncs_test.go", + "func_count_test.go", + "func_cume_dist_test.go", + "func_first_row_test.go", + "func_group_concat_test.go", + "func_json_arrayagg_test.go", + "func_json_objectagg_test.go", + "func_lead_lag_test.go", + "func_max_min_test.go", + "func_ntile_test.go", + "func_percent_rank_test.go", + "func_percentile_test.go", + "func_rank_test.go", + "func_stddevpop_test.go", + "func_stddevsamp_test.go", + "func_sum_test.go", + "func_value_test.go", + "func_varpop_test.go", + "func_varsamp_test.go", + "main_test.go", + "row_number_test.go", + "window_func_test.go", + ], + embed = [":aggfuncs"], + flaky = True, + race = "on", + shard_count = 48, + deps = [ + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/planner/util", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/mock", + "//pkg/util/set", + "@com_github_dgryski_go_farm//:go-farm", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/aggfuncs/aggfunc_test.go b/pkg/executor/aggfuncs/aggfunc_test.go similarity index 98% rename from executor/aggfuncs/aggfunc_test.go rename to pkg/executor/aggfuncs/aggfunc_test.go index 537b5af7ac753..9b6c1ad5acb74 100644 --- a/executor/aggfuncs/aggfunc_test.go +++ b/pkg/executor/aggfuncs/aggfunc_test.go @@ -23,20 +23,20 @@ import ( "github.com/dgryski/go-farm" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) diff --git a/executor/aggfuncs/aggfuncs.go b/pkg/executor/aggfuncs/aggfuncs.go similarity index 98% rename from executor/aggfuncs/aggfuncs.go rename to pkg/executor/aggfuncs/aggfuncs.go index 87b75c532e7a2..344bacacb6339 100644 --- a/executor/aggfuncs/aggfuncs.go +++ b/pkg/executor/aggfuncs/aggfuncs.go @@ -17,10 +17,10 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // All the AggFunc implementations are listed here for navigation. diff --git a/pkg/executor/aggfuncs/builder.go b/pkg/executor/aggfuncs/builder.go new file mode 100644 index 0000000000000..487f5a56626aa --- /dev/null +++ b/pkg/executor/aggfuncs/builder.go @@ -0,0 +1,727 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package aggfuncs + +import ( + "context" + "fmt" + "strconv" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// Build is used to build a specific AggFunc implementation according to the +// input aggFuncDesc. +func Build(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + switch aggFuncDesc.Name { + case ast.AggFuncCount: + return buildCount(aggFuncDesc, ordinal) + case ast.AggFuncSum: + return buildSum(ctx, aggFuncDesc, ordinal) + case ast.AggFuncAvg: + return buildAvg(ctx, aggFuncDesc, ordinal) + case ast.AggFuncFirstRow: + return buildFirstRow(aggFuncDesc, ordinal) + case ast.AggFuncMax: + return buildMaxMin(aggFuncDesc, ordinal, true) + case ast.AggFuncMin: + return buildMaxMin(aggFuncDesc, ordinal, false) + case ast.AggFuncGroupConcat: + return buildGroupConcat(ctx, aggFuncDesc, ordinal) + case ast.AggFuncBitOr: + return buildBitOr(aggFuncDesc, ordinal) + case ast.AggFuncBitXor: + return buildBitXor(aggFuncDesc, ordinal) + case ast.AggFuncBitAnd: + return buildBitAnd(aggFuncDesc, ordinal) + case ast.AggFuncVarPop: + return buildVarPop(aggFuncDesc, ordinal) + case ast.AggFuncStddevPop: + return buildStdDevPop(aggFuncDesc, ordinal) + case ast.AggFuncJsonArrayagg: + return buildJSONArrayagg(aggFuncDesc, ordinal) + case ast.AggFuncJsonObjectAgg: + return buildJSONObjectAgg(aggFuncDesc, ordinal) + case ast.AggFuncApproxCountDistinct: + return buildApproxCountDistinct(aggFuncDesc, ordinal) + case ast.AggFuncApproxPercentile: + return buildApproxPercentile(ctx, aggFuncDesc, ordinal) + case ast.AggFuncVarSamp: + return buildVarSamp(aggFuncDesc, ordinal) + case ast.AggFuncStddevSamp: + return buildStddevSamp(aggFuncDesc, ordinal) + } + return nil +} + +// BuildWindowFunctions builds specific window function according to function description and order by columns. +func BuildWindowFunctions(ctx sessionctx.Context, windowFuncDesc *aggregation.AggFuncDesc, ordinal int, orderByCols []*expression.Column) AggFunc { + switch windowFuncDesc.Name { + case ast.WindowFuncRank: + return buildRank(ordinal, orderByCols, false) + case ast.WindowFuncDenseRank: + return buildRank(ordinal, orderByCols, true) + case ast.WindowFuncRowNumber: + return buildRowNumber(windowFuncDesc, ordinal) + case ast.WindowFuncFirstValue: + return buildFirstValue(windowFuncDesc, ordinal) + case ast.WindowFuncLastValue: + return buildLastValue(windowFuncDesc, ordinal) + case ast.WindowFuncCumeDist: + return buildCumeDist(ordinal, orderByCols) + case ast.WindowFuncNthValue: + return buildNthValue(windowFuncDesc, ordinal) + case ast.WindowFuncNtile: + return buildNtile(windowFuncDesc, ordinal) + case ast.WindowFuncPercentRank: + return buildPercentRank(ordinal, orderByCols) + case ast.WindowFuncLead: + return buildLead(ctx, windowFuncDesc, ordinal) + case ast.WindowFuncLag: + return buildLag(ctx, windowFuncDesc, ordinal) + case ast.AggFuncMax: + // The max/min aggFunc using in the window function will using the sliding window algo. + return buildMaxMinInWindowFunction(windowFuncDesc, ordinal, true) + case ast.AggFuncMin: + return buildMaxMinInWindowFunction(windowFuncDesc, ordinal, false) + default: + return Build(ctx, windowFuncDesc, ordinal) + } +} + +func buildApproxCountDistinct(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseApproxCountDistinct{baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + }} + + // In partition table, union need to compute partial result into partial result. + // We can detect and handle this case by checking whether return type is string. + + switch aggFuncDesc.RetTp.GetType() { + case mysql.TypeLonglong: + switch aggFuncDesc.Mode { + case aggregation.CompleteMode: + return &approxCountDistinctOriginal{base} + case aggregation.Partial1Mode: + return &approxCountDistinctPartial1{approxCountDistinctOriginal{base}} + case aggregation.Partial2Mode: + return &approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}} + case aggregation.FinalMode: + return &approxCountDistinctFinal{approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}}} + } + case mysql.TypeString: + switch aggFuncDesc.Mode { + case aggregation.CompleteMode, aggregation.Partial1Mode: + return &approxCountDistinctPartial1{approxCountDistinctOriginal{base}} + case aggregation.Partial2Mode, aggregation.FinalMode: + return &approxCountDistinctPartial2{approxCountDistinctPartial1{approxCountDistinctOriginal{base}}} + } + } + + return nil +} + +func buildApproxPercentile(sctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + if aggFuncDesc.Mode == aggregation.DedupMode { + return nil + } + + // Checked while building descriptor + percent, _, err := aggFuncDesc.Args[1].EvalInt(sctx, chunk.Row{}) + if err != nil { + // Should not reach here + logutil.BgLogger().Error("Error happened when buildApproxPercentile", zap.Error(err)) + return nil + } + + base := basePercentile{percent: int(percent), baseAggFunc: baseAggFunc{args: aggFuncDesc.Args, ordinal: ordinal}} + + evalType := aggFuncDesc.Args[0].GetType().EvalType() + if aggFuncDesc.Args[0].GetType().GetType() == mysql.TypeBit { + evalType = types.ETString // same as other aggregate function + } + switch aggFuncDesc.Mode { + case aggregation.CompleteMode, aggregation.Partial1Mode, aggregation.FinalMode: + switch evalType { + case types.ETInt: + return &percentileOriginal4Int{base} + case types.ETReal: + return &percentileOriginal4Real{base} + case types.ETDecimal: + return &percentileOriginal4Decimal{base} + case types.ETDatetime, types.ETTimestamp: + return &percentileOriginal4Time{base} + case types.ETDuration: + return &percentileOriginal4Duration{base} + default: + // Return NULL in any case + return &base + } + } + + return nil +} + +// buildCount builds the AggFunc implementation for function "COUNT". +func buildCount(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + // If mode is DedupMode, we return nil for not implemented. + if aggFuncDesc.Mode == aggregation.DedupMode { + return nil // not implemented yet. + } + + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + retTp: aggFuncDesc.RetTp, + } + + // If HasDistinct and mode is CompleteMode or Partial1Mode, we should + // use countOriginalWithDistinct. + if aggFuncDesc.HasDistinct && + (aggFuncDesc.Mode == aggregation.CompleteMode || aggFuncDesc.Mode == aggregation.Partial1Mode) { + if len(base.args) == 1 { + // optimize with single column + // TODO: because Time and JSON does not have `hashcode()` or similar method + // so they're in exception for now. + // TODO: add hashCode method for all evaluate types (decimal, Time, Duration, JSON). + // https://github.com/pingcap/tidb/issues/15857 + switch aggFuncDesc.Args[0].GetType().EvalType() { + case types.ETInt: + return &countOriginalWithDistinct4Int{baseCount{base}} + case types.ETReal: + return &countOriginalWithDistinct4Real{baseCount{base}} + case types.ETDecimal: + return &countOriginalWithDistinct4Decimal{baseCount{base}} + case types.ETDuration: + return &countOriginalWithDistinct4Duration{baseCount{base}} + case types.ETString: + return &countOriginalWithDistinct4String{baseCount{base}} + } + } + return &countOriginalWithDistinct{baseCount{base}} + } + + switch aggFuncDesc.Mode { + case aggregation.CompleteMode, aggregation.Partial1Mode: + switch aggFuncDesc.Args[0].GetType().EvalType() { + case types.ETInt: + return &countOriginal4Int{baseCount{base}} + case types.ETReal: + return &countOriginal4Real{baseCount{base}} + case types.ETDecimal: + return &countOriginal4Decimal{baseCount{base}} + case types.ETTimestamp, types.ETDatetime: + return &countOriginal4Time{baseCount{base}} + case types.ETDuration: + return &countOriginal4Duration{baseCount{base}} + case types.ETJson: + return &countOriginal4JSON{baseCount{base}} + case types.ETString: + return &countOriginal4String{baseCount{base}} + } + case aggregation.Partial2Mode, aggregation.FinalMode: + return &countPartial{baseCount{base}} + } + + return nil +} + +// buildSum builds the AggFunc implementation for function "SUM". +func buildSum(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseSumAggFunc{ + baseAggFunc: baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + retTp: aggFuncDesc.RetTp, + }, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + switch aggFuncDesc.RetTp.EvalType() { + case types.ETDecimal: + if aggFuncDesc.HasDistinct { + return &sum4DistinctDecimal{base} + } + return &sum4Decimal{base} + default: + if aggFuncDesc.HasDistinct { + return &sum4DistinctFloat64{base} + } + if ctx.GetSessionVars().WindowingUseHighPrecision { + return &sum4Float64HighPrecision{baseSum4Float64{base}} + } + return &sum4Float64{baseSum4Float64{base}} + } + } +} + +// buildAvg builds the AggFunc implementation for function "AVG". +func buildAvg(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + retTp: aggFuncDesc.RetTp, + } + switch aggFuncDesc.Mode { + // Build avg functions which consume the original data and remove the + // duplicated input of the same group. + case aggregation.DedupMode: + return nil // not implemented yet. + + // Build avg functions which consume the original data and update their + // partial results. + case aggregation.CompleteMode, aggregation.Partial1Mode: + switch aggFuncDesc.RetTp.EvalType() { + case types.ETDecimal: + if aggFuncDesc.HasDistinct { + return &avgOriginal4DistinctDecimal{base} + } + return &avgOriginal4Decimal{baseAvgDecimal{base}} + default: + if aggFuncDesc.HasDistinct { + return &avgOriginal4DistinctFloat64{base} + } + if ctx.GetSessionVars().WindowingUseHighPrecision { + return &avgOriginal4Float64HighPrecision{baseAvgFloat64{base}} + } + return &avgOriginal4Float64{avgOriginal4Float64HighPrecision{baseAvgFloat64{base}}} + } + + // Build avg functions which consume the partial result of other avg + // functions and update their partial results. + case aggregation.Partial2Mode, aggregation.FinalMode: + switch aggFuncDesc.RetTp.GetType() { + case mysql.TypeNewDecimal: + return &avgPartial4Decimal{baseAvgDecimal{base}} + case mysql.TypeDouble: + return &avgPartial4Float64{baseAvgFloat64{base}} + } + } + return nil +} + +// buildFirstRow builds the AggFunc implementation for function "FIRST_ROW". +func buildFirstRow(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + retTp: aggFuncDesc.RetTp, + } + evalType, fieldType := aggFuncDesc.RetTp.EvalType(), aggFuncDesc.RetTp + if fieldType.GetType() == mysql.TypeBit { + evalType = types.ETString + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + default: + switch fieldType.GetType() { + case mysql.TypeEnum: + return &firstRow4Enum{base} + case mysql.TypeSet: + return &firstRow4Set{base} + } + + switch evalType { + case types.ETInt: + return &firstRow4Int{base} + case types.ETReal: + switch fieldType.GetType() { + case mysql.TypeFloat: + return &firstRow4Float32{base} + case mysql.TypeDouble: + return &firstRow4Float64{base} + } + case types.ETDecimal: + return &firstRow4Decimal{base} + case types.ETDatetime, types.ETTimestamp: + return &firstRow4Time{base} + case types.ETDuration: + return &firstRow4Duration{base} + case types.ETString: + return &firstRow4String{base} + case types.ETJson: + return &firstRow4JSON{base} + } + } + return nil +} + +// buildMaxMin builds the AggFunc implementation for function "MAX" and "MIN". +func buildMaxMin(aggFuncDesc *aggregation.AggFuncDesc, ordinal int, isMax bool) AggFunc { + base := baseMaxMinAggFunc{ + baseAggFunc: baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + retTp: aggFuncDesc.RetTp, + }, + isMax: isMax, + collator: collate.GetCollator(aggFuncDesc.RetTp.GetCollate()), + } + evalType, fieldType := aggFuncDesc.RetTp.EvalType(), aggFuncDesc.RetTp + if fieldType.GetType() == mysql.TypeBit { + evalType = types.ETString + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + default: + switch fieldType.GetType() { + case mysql.TypeEnum: + return &maxMin4Enum{base} + case mysql.TypeSet: + return &maxMin4Set{base} + } + + switch evalType { + case types.ETInt: + if mysql.HasUnsignedFlag(fieldType.GetFlag()) { + return &maxMin4Uint{base} + } + return &maxMin4Int{base} + case types.ETReal: + switch fieldType.GetType() { + case mysql.TypeFloat: + return &maxMin4Float32{base} + case mysql.TypeDouble: + return &maxMin4Float64{base} + } + case types.ETDecimal: + return &maxMin4Decimal{base} + case types.ETString: + return &maxMin4String{baseMaxMinAggFunc: base, retTp: aggFuncDesc.RetTp} + case types.ETDatetime, types.ETTimestamp: + return &maxMin4Time{base} + case types.ETDuration: + return &maxMin4Duration{base} + case types.ETJson: + return &maxMin4JSON{base} + } + } + return nil +} + +// buildMaxMin builds the AggFunc implementation for function "MAX" and "MIN" using by window function. +func buildMaxMinInWindowFunction(aggFuncDesc *aggregation.AggFuncDesc, ordinal int, isMax bool) AggFunc { + base := buildMaxMin(aggFuncDesc, ordinal, isMax) + // build max/min aggFunc for window function using sliding window + switch baseAggFunc := base.(type) { + case *maxMin4Int: + return &maxMin4IntSliding{*baseAggFunc, windowInfo{}} + case *maxMin4Uint: + return &maxMin4UintSliding{*baseAggFunc, windowInfo{}} + case *maxMin4Float32: + return &maxMin4Float32Sliding{*baseAggFunc, windowInfo{}} + case *maxMin4Float64: + return &maxMin4Float64Sliding{*baseAggFunc, windowInfo{}} + case *maxMin4Decimal: + return &maxMin4DecimalSliding{*baseAggFunc, windowInfo{}} + case *maxMin4String: + return &maxMin4StringSliding{*baseAggFunc, windowInfo{}} + case *maxMin4Time: + return &maxMin4TimeSliding{*baseAggFunc, windowInfo{}} + case *maxMin4Duration: + return &maxMin4DurationSliding{*baseAggFunc, windowInfo{}} + } + return base +} + +// buildGroupConcat builds the AggFunc implementation for function "GROUP_CONCAT". +func buildGroupConcat(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + // The last arg is promised to be a not-null string constant, so the error can be ignored. + c, _ := aggFuncDesc.Args[len(aggFuncDesc.Args)-1].(*expression.Constant) + sep, _, err := c.EvalString(nil, chunk.Row{}) + // This err should never happen. + if err != nil { + panic(fmt.Sprintf("Error happened when buildGroupConcat: %s", err.Error())) + } + var s string + s, err = ctx.GetSessionVars().GetSessionOrGlobalSystemVar(context.Background(), variable.GroupConcatMaxLen) + if err != nil { + panic(fmt.Sprintf("Error happened when buildGroupConcat: no system variable named '%s'", variable.GroupConcatMaxLen)) + } + maxLen, err := strconv.ParseUint(s, 10, 64) + // Should never happen + if err != nil { + panic(fmt.Sprintf("Error happened when buildGroupConcat: %s", err.Error())) + } + var truncated int32 + base := baseGroupConcat4String{ + baseAggFunc: baseAggFunc{ + args: aggFuncDesc.Args[:len(aggFuncDesc.Args)-1], + ordinal: ordinal, + }, + byItems: aggFuncDesc.OrderByItems, + sep: sep, + maxLen: maxLen, + truncated: &truncated, + } + if aggFuncDesc.HasDistinct { + if len(aggFuncDesc.OrderByItems) > 0 { + return &groupConcatDistinctOrder{base} + } + return &groupConcatDistinct{base} + } + if len(aggFuncDesc.OrderByItems) > 0 { + return &groupConcatOrder{base} + } + return &groupConcat{base} + } +} + +// buildBitOr builds the AggFunc implementation for function "BIT_OR". +func buildBitOr(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &bitOrUint64{baseBitAggFunc{base}} +} + +// buildBitXor builds the AggFunc implementation for function "BIT_XOR". +func buildBitXor(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &bitXorUint64{baseBitAggFunc{base}} +} + +// buildBitAnd builds the AggFunc implementation for function "BIT_AND". +func buildBitAnd(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &bitAndUint64{baseBitAggFunc{base}} +} + +// buildVarPop builds the AggFunc implementation for function "VAR_POP". +func buildVarPop(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseVarPopAggFunc{ + baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + }, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + if aggFuncDesc.HasDistinct { + return &varPop4DistinctFloat64{base} + } + return &varPop4Float64{base} + } +} + +// buildStdDevPop builds the AggFunc implementation for function "STD()/STDDEV()/STDDEV_POP()" +func buildStdDevPop(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseVarPopAggFunc{ + baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + }, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + if aggFuncDesc.HasDistinct { + return &stdDevPop4DistinctFloat64{varPop4DistinctFloat64{base}} + } + return &stdDevPop4Float64{varPop4Float64{base}} + } +} + +// buildVarSamp builds the AggFunc implementation for function "VAR_SAMP()" +func buildVarSamp(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseVarPopAggFunc{ + baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + }, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + if aggFuncDesc.HasDistinct { + return &varSamp4DistinctFloat64{varPop4DistinctFloat64{base}} + } + return &varSamp4Float64{varPop4Float64{base}} + } +} + +// buildStddevSamp builds the AggFunc implementation for function "STDDEV_SAMP()" +func buildStddevSamp(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseVarPopAggFunc{ + baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + }, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + if aggFuncDesc.HasDistinct { + return &stddevSamp4DistinctFloat64{varPop4DistinctFloat64{base}} + } + return &stddevSamp4Float64{varPop4Float64{base}} + } +} + +// buildJSONArrayagg builds the AggFunc implementation for function "json_arrayagg". +func buildJSONArrayagg(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + return &jsonArrayagg{base} + } +} + +// buildJSONObjectAgg builds the AggFunc implementation for function "json_objectagg". +func buildJSONObjectAgg(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + switch aggFuncDesc.Mode { + case aggregation.DedupMode: + return nil + default: + return &jsonObjectAgg{base} + } +} + +// buildRowNumber builds the AggFunc implementation for function "ROW_NUMBER". +func buildRowNumber(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &rowNumber{base} +} + +func buildRank(ordinal int, orderByCols []*expression.Column, isDense bool) AggFunc { + base := baseAggFunc{ + ordinal: ordinal, + } + r := &rank{baseAggFunc: base, isDense: isDense, rowComparer: buildRowComparer(orderByCols)} + return r +} + +func buildFirstValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &firstValue{baseAggFunc: base, tp: aggFuncDesc.RetTp} +} + +func buildLastValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + return &lastValue{baseAggFunc: base, tp: aggFuncDesc.RetTp} +} + +func buildCumeDist(ordinal int, orderByCols []*expression.Column) AggFunc { + base := baseAggFunc{ + ordinal: ordinal, + } + r := &cumeDist{baseAggFunc: base, rowComparer: buildRowComparer(orderByCols)} + return r +} + +func buildNthValue(aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + // Already checked when building the function description. + nth, _, _ := expression.GetUint64FromConstant(aggFuncDesc.Args[1]) + return &nthValue{baseAggFunc: base, tp: aggFuncDesc.RetTp, nth: nth} +} + +func buildNtile(aggFuncDes *aggregation.AggFuncDesc, ordinal int) AggFunc { + base := baseAggFunc{ + args: aggFuncDes.Args, + ordinal: ordinal, + } + n, _, _ := expression.GetUint64FromConstant(aggFuncDes.Args[0]) + return &ntile{baseAggFunc: base, n: n} +} + +func buildPercentRank(ordinal int, orderByCols []*expression.Column) AggFunc { + base := baseAggFunc{ + ordinal: ordinal, + } + return &percentRank{baseAggFunc: base, rowComparer: buildRowComparer(orderByCols)} +} + +func buildLeadLag(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) baseLeadLag { + offset := uint64(1) + if len(aggFuncDesc.Args) >= 2 { + offset, _, _ = expression.GetUint64FromConstant(aggFuncDesc.Args[1]) + } + var defaultExpr expression.Expression + defaultExpr = expression.NewNull() + if len(aggFuncDesc.Args) == 3 { + defaultExpr = aggFuncDesc.Args[2] + if et, ok := defaultExpr.(*expression.Constant); ok { + res, err1 := et.Value.ConvertTo(ctx.GetSessionVars().StmtCtx, aggFuncDesc.RetTp) + if err1 == nil { + defaultExpr = &expression.Constant{Value: res, RetType: aggFuncDesc.RetTp} + } + } + } + base := baseAggFunc{ + args: aggFuncDesc.Args, + ordinal: ordinal, + } + ve, _ := buildValueEvaluator(aggFuncDesc.RetTp) + return baseLeadLag{baseAggFunc: base, offset: offset, defaultExpr: defaultExpr, valueEvaluator: ve} +} + +func buildLead(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + return &lead{buildLeadLag(ctx, aggFuncDesc, ordinal)} +} + +func buildLag(ctx sessionctx.Context, aggFuncDesc *aggregation.AggFuncDesc, ordinal int) AggFunc { + return &lag{buildLeadLag(ctx, aggFuncDesc, ordinal)} +} diff --git a/executor/aggfuncs/export_test.go b/pkg/executor/aggfuncs/export_test.go similarity index 100% rename from executor/aggfuncs/export_test.go rename to pkg/executor/aggfuncs/export_test.go diff --git a/executor/aggfuncs/func_avg.go b/pkg/executor/aggfuncs/func_avg.go similarity index 98% rename from executor/aggfuncs/func_avg.go rename to pkg/executor/aggfuncs/func_avg.go index 28bf4156ef438..af6969e6a3f03 100644 --- a/executor/aggfuncs/func_avg.go +++ b/pkg/executor/aggfuncs/func_avg.go @@ -18,12 +18,12 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/set" ) const ( diff --git a/executor/aggfuncs/func_avg_test.go b/pkg/executor/aggfuncs/func_avg_test.go similarity index 91% rename from executor/aggfuncs/func_avg_test.go rename to pkg/executor/aggfuncs/func_avg_test.go index 0b1e1201559c3..a3533491f3922 100644 --- a/executor/aggfuncs/func_avg_test.go +++ b/pkg/executor/aggfuncs/func_avg_test.go @@ -17,11 +17,11 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mock" ) func TestMergePartialResult4Avg(t *testing.T) { diff --git a/executor/aggfuncs/func_bitfuncs.go b/pkg/executor/aggfuncs/func_bitfuncs.go similarity index 98% rename from executor/aggfuncs/func_bitfuncs.go rename to pkg/executor/aggfuncs/func_bitfuncs.go index 90f7b1b7b40ec..d3ff75417a608 100644 --- a/executor/aggfuncs/func_bitfuncs.go +++ b/pkg/executor/aggfuncs/func_bitfuncs.go @@ -18,8 +18,8 @@ import ( "math" "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_bitfuncs_test.go b/pkg/executor/aggfuncs/func_bitfuncs_test.go similarity index 91% rename from executor/aggfuncs/func_bitfuncs_test.go rename to pkg/executor/aggfuncs/func_bitfuncs_test.go index d4a442c1a61e0..33da4082d20b7 100644 --- a/executor/aggfuncs/func_bitfuncs_test.go +++ b/pkg/executor/aggfuncs/func_bitfuncs_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMergePartialResult4BitFuncs(t *testing.T) { diff --git a/executor/aggfuncs/func_count.go b/pkg/executor/aggfuncs/func_count.go similarity index 99% rename from executor/aggfuncs/func_count.go rename to pkg/executor/aggfuncs/func_count.go index 3857897e49f02..e24e373e172fd 100644 --- a/executor/aggfuncs/func_count.go +++ b/pkg/executor/aggfuncs/func_count.go @@ -17,8 +17,8 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_count_distinct.go b/pkg/executor/aggfuncs/func_count_distinct.go similarity index 98% rename from executor/aggfuncs/func_count_distinct.go rename to pkg/executor/aggfuncs/func_count_distinct.go index f99efbc7cdfce..d33064b07d9a0 100644 --- a/executor/aggfuncs/func_count_distinct.go +++ b/pkg/executor/aggfuncs/func_count_distinct.go @@ -21,16 +21,16 @@ import ( "github.com/dgryski/go-farm" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stringutil" ) const ( diff --git a/executor/aggfuncs/func_count_test.go b/pkg/executor/aggfuncs/func_count_test.go similarity index 97% rename from executor/aggfuncs/func_count_test.go rename to pkg/executor/aggfuncs/func_count_test.go index c55c07fdb5f6b..ce9a45dbcf540 100644 --- a/executor/aggfuncs/func_count_test.go +++ b/pkg/executor/aggfuncs/func_count_test.go @@ -20,13 +20,13 @@ import ( "testing" "github.com/dgryski/go-farm" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/executor/aggfuncs/func_cume_dist.go b/pkg/executor/aggfuncs/func_cume_dist.go similarity index 95% rename from executor/aggfuncs/func_cume_dist.go rename to pkg/executor/aggfuncs/func_cume_dist.go index 8a671b279a4b0..33e888d929d55 100644 --- a/executor/aggfuncs/func_cume_dist.go +++ b/pkg/executor/aggfuncs/func_cume_dist.go @@ -17,8 +17,8 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_cume_dist_test.go b/pkg/executor/aggfuncs/func_cume_dist_test.go similarity index 89% rename from executor/aggfuncs/func_cume_dist_test.go rename to pkg/executor/aggfuncs/func_cume_dist_test.go index 77997453f5a23..f6bddec7ad4cb 100644 --- a/executor/aggfuncs/func_cume_dist_test.go +++ b/pkg/executor/aggfuncs/func_cume_dist_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMemCumeDist(t *testing.T) { diff --git a/executor/aggfuncs/func_first_row.go b/pkg/executor/aggfuncs/func_first_row.go similarity index 98% rename from executor/aggfuncs/func_first_row.go rename to pkg/executor/aggfuncs/func_first_row.go index e3cad76bee846..d7188987702e0 100644 --- a/executor/aggfuncs/func_first_row.go +++ b/pkg/executor/aggfuncs/func_first_row.go @@ -18,11 +18,11 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/stringutil" ) const ( diff --git a/executor/aggfuncs/func_first_row_test.go b/pkg/executor/aggfuncs/func_first_row_test.go similarity index 95% rename from executor/aggfuncs/func_first_row_test.go rename to pkg/executor/aggfuncs/func_first_row_test.go index 0137a7b1a0ff7..cff71a9ab7e8c 100644 --- a/executor/aggfuncs/func_first_row_test.go +++ b/pkg/executor/aggfuncs/func_first_row_test.go @@ -18,11 +18,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func TestMergePartialResult4FirstRow(t *testing.T) { diff --git a/executor/aggfuncs/func_group_concat.go b/pkg/executor/aggfuncs/func_group_concat.go similarity index 97% rename from executor/aggfuncs/func_group_concat.go rename to pkg/executor/aggfuncs/func_group_concat.go index 7cab9801b5a1e..6c9e9739e50b6 100644 --- a/executor/aggfuncs/func_group_concat.go +++ b/pkg/executor/aggfuncs/func_group_concat.go @@ -21,15 +21,15 @@ import ( "sync/atomic" "unsafe" - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/set" ) const ( diff --git a/executor/aggfuncs/func_group_concat_test.go b/pkg/executor/aggfuncs/func_group_concat_test.go similarity index 93% rename from executor/aggfuncs/func_group_concat_test.go rename to pkg/executor/aggfuncs/func_group_concat_test.go index d30ce7e44c246..ba564372d4e5d 100644 --- a/executor/aggfuncs/func_group_concat_test.go +++ b/pkg/executor/aggfuncs/func_group_concat_test.go @@ -19,17 +19,17 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) diff --git a/executor/aggfuncs/func_json_arrayagg.go b/pkg/executor/aggfuncs/func_json_arrayagg.go similarity index 95% rename from executor/aggfuncs/func_json_arrayagg.go rename to pkg/executor/aggfuncs/func_json_arrayagg.go index 242f70087a81c..46c33aa3b3ab8 100644 --- a/executor/aggfuncs/func_json_arrayagg.go +++ b/pkg/executor/aggfuncs/func_json_arrayagg.go @@ -18,9 +18,9 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_json_arrayagg_test.go b/pkg/executor/aggfuncs/func_json_arrayagg_test.go similarity index 95% rename from executor/aggfuncs/func_json_arrayagg_test.go rename to pkg/executor/aggfuncs/func_json_arrayagg_test.go index 528c24be3d6aa..456a39bbf0bdb 100644 --- a/executor/aggfuncs/func_json_arrayagg_test.go +++ b/pkg/executor/aggfuncs/func_json_arrayagg_test.go @@ -18,11 +18,11 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func TestMergePartialResult4JsonArrayagg(t *testing.T) { diff --git a/executor/aggfuncs/func_json_objectagg.go b/pkg/executor/aggfuncs/func_json_objectagg.go similarity index 95% rename from executor/aggfuncs/func_json_objectagg.go rename to pkg/executor/aggfuncs/func_json_objectagg.go index 819744e3a0f3c..1df217e11f3ae 100644 --- a/executor/aggfuncs/func_json_objectagg.go +++ b/pkg/executor/aggfuncs/func_json_objectagg.go @@ -19,12 +19,12 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" ) const ( diff --git a/executor/aggfuncs/func_json_objectagg_test.go b/pkg/executor/aggfuncs/func_json_objectagg_test.go similarity index 95% rename from executor/aggfuncs/func_json_objectagg_test.go rename to pkg/executor/aggfuncs/func_json_objectagg_test.go index 8da945bf1fd92..ef6cfc73ec8be 100644 --- a/executor/aggfuncs/func_json_objectagg_test.go +++ b/pkg/executor/aggfuncs/func_json_objectagg_test.go @@ -18,15 +18,15 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/mock" ) func getJSONValue(secondArg types.Datum, valueType *types.FieldType) interface{} { diff --git a/executor/aggfuncs/func_lead_lag.go b/pkg/executor/aggfuncs/func_lead_lag.go similarity index 95% rename from executor/aggfuncs/func_lead_lag.go rename to pkg/executor/aggfuncs/func_lead_lag.go index 8b7ff2284c0cc..d4859ffd7e287 100644 --- a/executor/aggfuncs/func_lead_lag.go +++ b/pkg/executor/aggfuncs/func_lead_lag.go @@ -17,9 +17,9 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_lead_lag_test.go b/pkg/executor/aggfuncs/func_lead_lag_test.go similarity index 97% rename from executor/aggfuncs/func_lead_lag_test.go rename to pkg/executor/aggfuncs/func_lead_lag_test.go index 31ca5da97912c..46ddcb39d9709 100644 --- a/executor/aggfuncs/func_lead_lag_test.go +++ b/pkg/executor/aggfuncs/func_lead_lag_test.go @@ -17,11 +17,11 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) func TestLeadLag(t *testing.T) { diff --git a/executor/aggfuncs/func_max_min.go b/pkg/executor/aggfuncs/func_max_min.go similarity index 99% rename from executor/aggfuncs/func_max_min.go rename to pkg/executor/aggfuncs/func_max_min.go index a9bc2b6b69692..17ec3de9cdb5a 100644 --- a/executor/aggfuncs/func_max_min.go +++ b/pkg/executor/aggfuncs/func_max_min.go @@ -19,12 +19,12 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // NewDeque inits a new MinMaxDeque diff --git a/executor/aggfuncs/func_max_min_test.go b/pkg/executor/aggfuncs/func_max_min_test.go similarity index 98% rename from executor/aggfuncs/func_max_min_test.go rename to pkg/executor/aggfuncs/func_max_min_test.go index 4eff13e3ceabf..662a90b4b9da3 100644 --- a/executor/aggfuncs/func_max_min_test.go +++ b/pkg/executor/aggfuncs/func_max_min_test.go @@ -20,12 +20,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/executor/aggfuncs/func_ntile.go b/pkg/executor/aggfuncs/func_ntile.go similarity index 96% rename from executor/aggfuncs/func_ntile.go rename to pkg/executor/aggfuncs/func_ntile.go index 1be13a66fc533..e4de7fbce972d 100644 --- a/executor/aggfuncs/func_ntile.go +++ b/pkg/executor/aggfuncs/func_ntile.go @@ -17,8 +17,8 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_ntile_test.go b/pkg/executor/aggfuncs/func_ntile_test.go similarity index 89% rename from executor/aggfuncs/func_ntile_test.go rename to pkg/executor/aggfuncs/func_ntile_test.go index 5ec251d3a6bac..b7a8934e2f196 100644 --- a/executor/aggfuncs/func_ntile_test.go +++ b/pkg/executor/aggfuncs/func_ntile_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMemNtile(t *testing.T) { diff --git a/executor/aggfuncs/func_percent_rank.go b/pkg/executor/aggfuncs/func_percent_rank.go similarity index 96% rename from executor/aggfuncs/func_percent_rank.go rename to pkg/executor/aggfuncs/func_percent_rank.go index 468d4642fb94a..9dc3484492c70 100644 --- a/executor/aggfuncs/func_percent_rank.go +++ b/pkg/executor/aggfuncs/func_percent_rank.go @@ -15,8 +15,8 @@ package aggfuncs import ( - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) // percentRank calculates the percentage of partition values less than the value in the current row, excluding the highest value. diff --git a/executor/aggfuncs/func_percent_rank_test.go b/pkg/executor/aggfuncs/func_percent_rank_test.go similarity index 89% rename from executor/aggfuncs/func_percent_rank_test.go rename to pkg/executor/aggfuncs/func_percent_rank_test.go index 25d8bbef73712..06580e73d2a03 100644 --- a/executor/aggfuncs/func_percent_rank_test.go +++ b/pkg/executor/aggfuncs/func_percent_rank_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMemPercentRank(t *testing.T) { diff --git a/executor/aggfuncs/func_percentile.go b/pkg/executor/aggfuncs/func_percentile.go similarity index 98% rename from executor/aggfuncs/func_percentile.go rename to pkg/executor/aggfuncs/func_percentile.go index 4bc0031482623..8be99f4528370 100644 --- a/executor/aggfuncs/func_percentile.go +++ b/pkg/executor/aggfuncs/func_percentile.go @@ -19,10 +19,10 @@ import ( "sort" "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/selection" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/selection" ) const ( diff --git a/executor/aggfuncs/func_percentile_test.go b/pkg/executor/aggfuncs/func_percentile_test.go similarity index 91% rename from executor/aggfuncs/func_percentile_test.go rename to pkg/executor/aggfuncs/func_percentile_test.go index dff7c4b6dfaf0..65f22466fd235 100644 --- a/executor/aggfuncs/func_percentile_test.go +++ b/pkg/executor/aggfuncs/func_percentile_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/executor/aggfuncs/func_rank.go b/pkg/executor/aggfuncs/func_rank.go similarity index 95% rename from executor/aggfuncs/func_rank.go rename to pkg/executor/aggfuncs/func_rank.go index 7ec2030c1a447..57cd03cd33761 100644 --- a/executor/aggfuncs/func_rank.go +++ b/pkg/executor/aggfuncs/func_rank.go @@ -17,9 +17,9 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_rank_test.go b/pkg/executor/aggfuncs/func_rank_test.go similarity index 89% rename from executor/aggfuncs/func_rank_test.go rename to pkg/executor/aggfuncs/func_rank_test.go index 050069a0d31bc..1a58006764005 100644 --- a/executor/aggfuncs/func_rank_test.go +++ b/pkg/executor/aggfuncs/func_rank_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMemRank(t *testing.T) { diff --git a/executor/aggfuncs/func_stddevpop.go b/pkg/executor/aggfuncs/func_stddevpop.go similarity index 94% rename from executor/aggfuncs/func_stddevpop.go rename to pkg/executor/aggfuncs/func_stddevpop.go index c77dc444dfb5f..c099fdfa95a7d 100644 --- a/executor/aggfuncs/func_stddevpop.go +++ b/pkg/executor/aggfuncs/func_stddevpop.go @@ -17,8 +17,8 @@ package aggfuncs import ( "math" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) type stdDevPop4Float64 struct { diff --git a/executor/aggfuncs/func_stddevpop_test.go b/pkg/executor/aggfuncs/func_stddevpop_test.go similarity index 92% rename from executor/aggfuncs/func_stddevpop_test.go rename to pkg/executor/aggfuncs/func_stddevpop_test.go index e9b08422a0a39..1db4d2c245815 100644 --- a/executor/aggfuncs/func_stddevpop_test.go +++ b/pkg/executor/aggfuncs/func_stddevpop_test.go @@ -17,8 +17,8 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMergePartialResult4Stddevpop(t *testing.T) { diff --git a/executor/aggfuncs/func_stddevsamp.go b/pkg/executor/aggfuncs/func_stddevsamp.go similarity index 94% rename from executor/aggfuncs/func_stddevsamp.go rename to pkg/executor/aggfuncs/func_stddevsamp.go index ad28c75574b7c..dfbffbf5c5984 100644 --- a/executor/aggfuncs/func_stddevsamp.go +++ b/pkg/executor/aggfuncs/func_stddevsamp.go @@ -17,8 +17,8 @@ package aggfuncs import ( "math" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) type stddevSamp4Float64 struct { diff --git a/executor/aggfuncs/func_stddevsamp_test.go b/pkg/executor/aggfuncs/func_stddevsamp_test.go similarity index 92% rename from executor/aggfuncs/func_stddevsamp_test.go rename to pkg/executor/aggfuncs/func_stddevsamp_test.go index d896fa640b5b5..176e0004e3b64 100644 --- a/executor/aggfuncs/func_stddevsamp_test.go +++ b/pkg/executor/aggfuncs/func_stddevsamp_test.go @@ -17,8 +17,8 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMergePartialResult4Stddevsamp(t *testing.T) { diff --git a/executor/aggfuncs/func_sum.go b/pkg/executor/aggfuncs/func_sum.go similarity index 97% rename from executor/aggfuncs/func_sum.go rename to pkg/executor/aggfuncs/func_sum.go index 67fe2be68b724..32573eacc6fcd 100644 --- a/executor/aggfuncs/func_sum.go +++ b/pkg/executor/aggfuncs/func_sum.go @@ -18,12 +18,12 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/set" ) const ( diff --git a/executor/aggfuncs/func_sum_test.go b/pkg/executor/aggfuncs/func_sum_test.go similarity index 91% rename from executor/aggfuncs/func_sum_test.go rename to pkg/executor/aggfuncs/func_sum_test.go index 396f039e8993e..ccdbb448fafb8 100644 --- a/executor/aggfuncs/func_sum_test.go +++ b/pkg/executor/aggfuncs/func_sum_test.go @@ -18,11 +18,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" ) func TestMergePartialResult4Sum(t *testing.T) { diff --git a/executor/aggfuncs/func_value.go b/pkg/executor/aggfuncs/func_value.go similarity index 98% rename from executor/aggfuncs/func_value.go rename to pkg/executor/aggfuncs/func_value.go index a1045c5a648bf..364e2673d6760 100644 --- a/executor/aggfuncs/func_value.go +++ b/pkg/executor/aggfuncs/func_value.go @@ -17,11 +17,11 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/func_value_test.go b/pkg/executor/aggfuncs/func_value_test.go similarity index 95% rename from executor/aggfuncs/func_value_test.go rename to pkg/executor/aggfuncs/func_value_test.go index cb7e6b5b9311f..3624b600d0a84 100644 --- a/executor/aggfuncs/func_value_test.go +++ b/pkg/executor/aggfuncs/func_value_test.go @@ -17,11 +17,11 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func getEvaluatedMemDelta(row *chunk.Row, dataType *types.FieldType) (memDelta int64) { diff --git a/executor/aggfuncs/func_varpop.go b/pkg/executor/aggfuncs/func_varpop.go similarity index 97% rename from executor/aggfuncs/func_varpop.go rename to pkg/executor/aggfuncs/func_varpop.go index 0612e3cb1dc41..d1d8813b2d067 100644 --- a/executor/aggfuncs/func_varpop.go +++ b/pkg/executor/aggfuncs/func_varpop.go @@ -18,9 +18,9 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/set" ) const ( diff --git a/executor/aggfuncs/func_varpop_test.go b/pkg/executor/aggfuncs/func_varpop_test.go similarity index 89% rename from executor/aggfuncs/func_varpop_test.go rename to pkg/executor/aggfuncs/func_varpop_test.go index 83aa051cea0fc..6b78f80870a7f 100644 --- a/executor/aggfuncs/func_varpop_test.go +++ b/pkg/executor/aggfuncs/func_varpop_test.go @@ -18,11 +18,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" ) func TestMergePartialResult4Varpop(t *testing.T) { diff --git a/executor/aggfuncs/func_varsamp.go b/pkg/executor/aggfuncs/func_varsamp.go similarity index 94% rename from executor/aggfuncs/func_varsamp.go rename to pkg/executor/aggfuncs/func_varsamp.go index 34ca7578201ed..6d311dfc3ff00 100644 --- a/executor/aggfuncs/func_varsamp.go +++ b/pkg/executor/aggfuncs/func_varsamp.go @@ -15,8 +15,8 @@ package aggfuncs import ( - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) type varSamp4Float64 struct { diff --git a/executor/aggfuncs/func_varsamp_test.go b/pkg/executor/aggfuncs/func_varsamp_test.go similarity index 92% rename from executor/aggfuncs/func_varsamp_test.go rename to pkg/executor/aggfuncs/func_varsamp_test.go index 32396b654f540..9b514e98b58b8 100644 --- a/executor/aggfuncs/func_varsamp_test.go +++ b/pkg/executor/aggfuncs/func_varsamp_test.go @@ -17,8 +17,8 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMergePartialResult4Varsamp(t *testing.T) { diff --git a/pkg/executor/aggfuncs/main_test.go b/pkg/executor/aggfuncs/main_test.go new file mode 100644 index 0000000000000..16c14822a421f --- /dev/null +++ b/pkg/executor/aggfuncs/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package aggfuncs + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/aggfuncs/row_number.go b/pkg/executor/aggfuncs/row_number.go similarity index 95% rename from executor/aggfuncs/row_number.go rename to pkg/executor/aggfuncs/row_number.go index f5f359a80b81f..9636cccade6f8 100644 --- a/executor/aggfuncs/row_number.go +++ b/pkg/executor/aggfuncs/row_number.go @@ -17,8 +17,8 @@ package aggfuncs import ( "unsafe" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) const ( diff --git a/executor/aggfuncs/row_number_test.go b/pkg/executor/aggfuncs/row_number_test.go similarity index 87% rename from executor/aggfuncs/row_number_test.go rename to pkg/executor/aggfuncs/row_number_test.go index 361096daf00de..9cfd19ec23306 100644 --- a/executor/aggfuncs/row_number_test.go +++ b/pkg/executor/aggfuncs/row_number_test.go @@ -17,9 +17,9 @@ package aggfuncs_test import ( "testing" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func TestMemRowNumber(t *testing.T) { diff --git a/executor/aggfuncs/window_func_test.go b/pkg/executor/aggfuncs/window_func_test.go similarity index 95% rename from executor/aggfuncs/window_func_test.go rename to pkg/executor/aggfuncs/window_func_test.go index a44541e872b1d..3c109aae4a95a 100644 --- a/executor/aggfuncs/window_func_test.go +++ b/pkg/executor/aggfuncs/window_func_test.go @@ -18,15 +18,15 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/aggregate/BUILD.bazel b/pkg/executor/aggregate/BUILD.bazel new file mode 100644 index 0000000000000..8eb96d7e058cc --- /dev/null +++ b/pkg/executor/aggregate/BUILD.bazel @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "aggregate", + srcs = [ + "agg_hash_base_worker.go", + "agg_hash_executor.go", + "agg_hash_final_worker.go", + "agg_hash_partial_worker.go", + "agg_spill.go", + "agg_stream_executor.go", + "agg_util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/aggregate", + visibility = ["//visibility:public"], + deps = [ + "//pkg/executor/aggfuncs", + "//pkg/executor/internal/exec", + "//pkg/executor/internal/vecgroupchecker", + "//pkg/expression", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/channel", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/disk", + "//pkg/util/execdetails", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/set", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_twmb_murmur3//:murmur3", + "@org_uber_go_zap//:zap", + ], +) diff --git a/executor/aggregate/agg_hash_base_worker.go b/pkg/executor/aggregate/agg_hash_base_worker.go similarity index 92% rename from executor/aggregate/agg_hash_base_worker.go rename to pkg/executor/aggregate/agg_hash_base_worker.go index bd6e031c4c616..67fa32f356a26 100644 --- a/executor/aggregate/agg_hash_base_worker.go +++ b/pkg/executor/aggregate/agg_hash_base_worker.go @@ -16,11 +16,11 @@ package aggregate import ( "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/memory" ) // AggPartialResultMapper contains aggregate function results diff --git a/executor/aggregate/agg_hash_executor.go b/pkg/executor/aggregate/agg_hash_executor.go similarity index 97% rename from executor/aggregate/agg_hash_executor.go rename to pkg/executor/aggregate/agg_hash_executor.go index 83d38c50519f8..69d2faca949fa 100644 --- a/executor/aggregate/agg_hash_executor.go +++ b/pkg/executor/aggregate/agg_hash_executor.go @@ -22,19 +22,19 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/set" ) // HashAggInput indicates the input of hash agg exec. diff --git a/executor/aggregate/agg_hash_final_worker.go b/pkg/executor/aggregate/agg_hash_final_worker.go similarity index 95% rename from executor/aggregate/agg_hash_final_worker.go rename to pkg/executor/aggregate/agg_hash_final_worker.go index 0f78d9e6d50bf..a2e9f2edae1f5 100644 --- a/executor/aggregate/agg_hash_final_worker.go +++ b/pkg/executor/aggregate/agg_hash_final_worker.go @@ -19,12 +19,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/set" "go.uber.org/zap" ) diff --git a/executor/aggregate/agg_hash_partial_worker.go b/pkg/executor/aggregate/agg_hash_partial_worker.go similarity index 96% rename from executor/aggregate/agg_hash_partial_worker.go rename to pkg/executor/aggregate/agg_hash_partial_worker.go index 93476826ac1c1..1988d48eab784 100644 --- a/executor/aggregate/agg_hash_partial_worker.go +++ b/pkg/executor/aggregate/agg_hash_partial_worker.go @@ -19,10 +19,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/twmb/murmur3" ) diff --git a/executor/aggregate/agg_spill.go b/pkg/executor/aggregate/agg_spill.go similarity index 95% rename from executor/aggregate/agg_spill.go rename to pkg/executor/aggregate/agg_spill.go index b510cd237321b..4e8ebad4ebd56 100644 --- a/executor/aggregate/agg_spill.go +++ b/pkg/executor/aggregate/agg_spill.go @@ -17,8 +17,8 @@ package aggregate import ( "sync/atomic" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" ) diff --git a/executor/aggregate/agg_stream_executor.go b/pkg/executor/aggregate/agg_stream_executor.go similarity index 96% rename from executor/aggregate/agg_stream_executor.go rename to pkg/executor/aggregate/agg_stream_executor.go index 4ea2db6c197c7..ceb785d1745ff 100644 --- a/executor/aggregate/agg_stream_executor.go +++ b/pkg/executor/aggregate/agg_stream_executor.go @@ -19,11 +19,11 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/memory" ) // StreamAggExec deals with all the aggregate functions. diff --git a/executor/aggregate/agg_util.go b/pkg/executor/aggregate/agg_util.go similarity index 93% rename from executor/aggregate/agg_util.go rename to pkg/executor/aggregate/agg_util.go index 4a87c783ae33b..95bd88b779022 100644 --- a/executor/aggregate/agg_util.go +++ b/pkg/executor/aggregate/agg_util.go @@ -23,18 +23,18 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/pkg/executor/analyze.go b/pkg/executor/analyze.go new file mode 100644 index 0000000000000..1e8802815c370 --- /dev/null +++ b/pkg/executor/analyze.go @@ -0,0 +1,721 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "fmt" + "math" + "net" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" +) + +var _ exec.Executor = &AnalyzeExec{} + +// AnalyzeExec represents Analyze executor. +type AnalyzeExec struct { + exec.BaseExecutor + tasks []*analyzeTask + wg util.WaitGroupWrapper + opts map[ast.AnalyzeOptionType]uint64 + OptionsMap map[int64]core.V2AnalyzeOptions +} + +var ( + // RandSeed is the seed for randing package. + // It's public for test. + RandSeed = int64(1) + + // MaxRegionSampleSize is the max sample size for one region when analyze v1 collects samples from table. + // It's public for test. + MaxRegionSampleSize = int64(1000) +) + +const ( + maxSketchSize = 10000 +) + +type taskType int + +const ( + colTask taskType = iota + idxTask +) + +// Next implements the Executor Next interface. +// It will collect all the sample task and run them concurrently. +func (e *AnalyzeExec) Next(ctx context.Context, _ *chunk.Chunk) error { + statsHandle := domain.GetDomain(e.Ctx()).StatsHandle() + infoSchema := sessiontxn.GetTxnManager(e.Ctx()).GetTxnInfoSchema() + sessionVars := e.Ctx().GetSessionVars() + + // Filter the locked tables. + tasks, needAnalyzeTableCnt, skippedTables, err := filterAndCollectTasks(e.tasks, statsHandle, infoSchema) + if err != nil { + return err + } + warnLockedTableMsg(sessionVars, needAnalyzeTableCnt, skippedTables) + + if len(tasks) == 0 { + return nil + } + + // Get the min number of goroutines for parallel execution. + concurrency, err := getBuildStatsConcurrency(e.Ctx()) + if err != nil { + return err + } + concurrency = min(len(tasks), concurrency) + + // Start workers with channel to collect results. + taskCh := make(chan *analyzeTask, concurrency) + resultChLen := min(concurrency*2, len(tasks)) + resultsCh := make(chan *statistics.AnalyzeResults, resultChLen) + for i := 0; i < concurrency; i++ { + e.wg.Run(func() { e.analyzeWorker(taskCh, resultsCh) }) + } + pruneMode := variable.PartitionPruneMode(sessionVars.PartitionPruneMode.Load()) + // needGlobalStats used to indicate whether we should merge the partition-level stats to global-level stats. + needGlobalStats := pruneMode == variable.Dynamic + globalStatsMap := make(map[globalStatsKey]globalStatsInfo) + g, _ := errgroup.WithContext(ctx) + g.Go(func() error { + return e.handleResultsError(ctx, concurrency, needGlobalStats, globalStatsMap, resultsCh, len(tasks)) + }) + + for _, task := range tasks { + prepareV2AnalyzeJobInfo(task.colExec, false) + AddNewAnalyzeJob(e.Ctx(), task.job) + } + failpoint.Inject("mockKillPendingAnalyzeJob", func() { + dom := domain.GetDomain(e.Ctx()) + dom.SysProcTracker().KillSysProcess(dom.GetAutoAnalyzeProcID()) + }) + + for _, task := range tasks { + taskCh <- task + } + close(taskCh) + + // Wait all workers done and close the results channel. + e.wg.Wait() + close(resultsCh) + err = g.Wait() + for _, task := range tasks { + if task.colExec != nil && task.colExec.memTracker != nil { + task.colExec.memTracker.Detach() + } + } + if err != nil { + return err + } + failpoint.Inject("mockKillFinishedAnalyzeJob", func() { + dom := domain.GetDomain(e.Ctx()) + dom.SysProcTracker().KillSysProcess(dom.GetAutoAnalyzeProcID()) + }) + // If we enabled dynamic prune mode, then we need to generate global stats here for partition tables. + if needGlobalStats { + err = e.handleGlobalStats(ctx, globalStatsMap) + if err != nil { + return err + } + } + + // Update analyze options to mysql.analyze_options for auto analyze. + err = e.saveV2AnalyzeOpts() + if err != nil { + sessionVars.StmtCtx.AppendWarning(err) + } + return statsHandle.Update(infoSchema) +} + +// filterAndCollectTasks filters the tasks that are not locked and collects the table IDs. +func filterAndCollectTasks(tasks []*analyzeTask, statsHandle *handle.Handle, infoSchema infoschema.InfoSchema) ([]*analyzeTask, uint, []string, error) { + var ( + filteredTasks []*analyzeTask + skippedTables []string + needAnalyzeTableCnt uint + // tidMap is used to deduplicate table IDs. + // In stats v1, analyze for each index is a single task, and they have the same table id. + tidAndPidsMap = make(map[int64]struct{}, len(tasks)) + ) + + lockedTableAndPartitionIDs, err := getLockedTableAndPartitionIDs(statsHandle, tasks) + if err != nil { + return nil, 0, nil, err + } + + for _, task := range tasks { + // Check if the table or partition is locked. + tableID := getTableIDFromTask(task) + _, isLocked := lockedTableAndPartitionIDs[tableID.TableID] + // If the whole table is not locked, we should check whether the partition is locked. + if !isLocked && tableID.IsPartitionTable() { + _, isLocked = lockedTableAndPartitionIDs[tableID.PartitionID] + } + + // Only analyze the table that is not locked. + if !isLocked { + filteredTasks = append(filteredTasks, task) + } + + // Get the physical table ID. + physicalTableID := tableID.TableID + if tableID.IsPartitionTable() { + physicalTableID = tableID.PartitionID + } + if _, ok := tidAndPidsMap[physicalTableID]; !ok { + if isLocked { + if tableID.IsPartitionTable() { + tbl, _, def := infoSchema.FindTableByPartitionID(tableID.PartitionID) + if def == nil { + logutil.BgLogger().Warn("Unknown partition ID in analyze task", zap.Int64("pid", tableID.PartitionID)) + } else { + schema, _ := infoSchema.SchemaByTable(tbl.Meta()) + skippedTables = append(skippedTables, fmt.Sprintf("%s.%s partition (%s)", schema.Name, tbl.Meta().Name.O, def.Name.O)) + } + } else { + tbl, ok := infoSchema.TableByID(physicalTableID) + if !ok { + logutil.BgLogger().Warn("Unknown table ID in analyze task", zap.Int64("tid", physicalTableID)) + } else { + schema, _ := infoSchema.SchemaByTable(tbl.Meta()) + skippedTables = append(skippedTables, fmt.Sprintf("%s.%s", schema.Name, tbl.Meta().Name.O)) + } + } + } else { + needAnalyzeTableCnt++ + } + tidAndPidsMap[physicalTableID] = struct{}{} + } + } + + return filteredTasks, needAnalyzeTableCnt, skippedTables, nil +} + +// getLockedTableAndPartitionIDs queries the locked tables and partitions. +func getLockedTableAndPartitionIDs(statsHandle *handle.Handle, tasks []*analyzeTask) (map[int64]struct{}, error) { + tidAndPids := make([]int64, 0, len(tasks)) + // Check the locked tables in one transaction. + // We need to check all tables and its partitions. + // Because if the whole table is locked, we should skip all partitions. + for _, task := range tasks { + tableID := getTableIDFromTask(task) + tidAndPids = append(tidAndPids, tableID.TableID) + if tableID.IsPartitionTable() { + tidAndPids = append(tidAndPids, tableID.PartitionID) + } + } + return statsHandle.GetLockedTables(tidAndPids...) +} + +// warnLockedTableMsg warns the locked table IDs. +func warnLockedTableMsg(sessionVars *variable.SessionVars, needAnalyzeTableCnt uint, skippedTables []string) { + if len(skippedTables) > 0 { + tables := strings.Join(skippedTables, ", ") + var msg string + if len(skippedTables) > 1 { + msg = "skip analyze locked tables: %s" + if needAnalyzeTableCnt > 0 { + msg = "skip analyze locked tables: %s, other tables will be analyzed" + } + } else { + msg = "skip analyze locked table: %s" + } + sessionVars.StmtCtx.AppendWarning(errors.Errorf(msg, tables)) + } +} + +func getTableIDFromTask(task *analyzeTask) statistics.AnalyzeTableID { + switch task.taskType { + case colTask: + return task.colExec.tableID + case idxTask: + return task.idxExec.tableID + } + + panic("unreachable") +} + +func (e *AnalyzeExec) saveV2AnalyzeOpts() error { + if !variable.PersistAnalyzeOptions.Load() || len(e.OptionsMap) == 0 { + return nil + } + // only to save table options if dynamic prune mode + dynamicPrune := variable.PartitionPruneMode(e.Ctx().GetSessionVars().PartitionPruneMode.Load()) == variable.Dynamic + toSaveMap := make(map[int64]core.V2AnalyzeOptions) + for id, opts := range e.OptionsMap { + if !opts.IsPartition || !dynamicPrune { + toSaveMap[id] = opts + } + } + sql := new(strings.Builder) + sqlexec.MustFormatSQL(sql, "REPLACE INTO mysql.analyze_options (table_id,sample_num,sample_rate,buckets,topn,column_choice,column_ids) VALUES ") + idx := 0 + for _, opts := range toSaveMap { + sampleNum := opts.RawOpts[ast.AnalyzeOptNumSamples] + sampleRate := float64(0) + if val, ok := opts.RawOpts[ast.AnalyzeOptSampleRate]; ok { + sampleRate = math.Float64frombits(val) + } + buckets := opts.RawOpts[ast.AnalyzeOptNumBuckets] + topn := int64(-1) + if val, ok := opts.RawOpts[ast.AnalyzeOptNumTopN]; ok { + topn = int64(val) + } + colChoice := opts.ColChoice.String() + colIDs := make([]string, len(opts.ColumnList)) + for i, colInfo := range opts.ColumnList { + colIDs[i] = strconv.FormatInt(colInfo.ID, 10) + } + colIDStrs := strings.Join(colIDs, ",") + sqlexec.MustFormatSQL(sql, "(%?,%?,%?,%?,%?,%?,%?)", opts.PhyTableID, sampleNum, sampleRate, buckets, topn, colChoice, colIDStrs) + if idx < len(toSaveMap)-1 { + sqlexec.MustFormatSQL(sql, ",") + } + idx++ + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) + _, _, err := exec.ExecRestrictedSQL(ctx, nil, sql.String()) + if err != nil { + return err + } + return nil +} + +func recordHistoricalStats(sctx sessionctx.Context, tableID int64) error { + statsHandle := domain.GetDomain(sctx).StatsHandle() + historicalStatsEnabled, err := statsHandle.CheckHistoricalStatsEnable() + if err != nil { + return errors.Errorf("check tidb_enable_historical_stats failed: %v", err) + } + if !historicalStatsEnabled { + return nil + } + historicalStatsWorker := domain.GetDomain(sctx).GetHistoricalStatsWorker() + historicalStatsWorker.SendTblToDumpHistoricalStats(tableID) + return nil +} + +// handleResultsError will handle the error fetch from resultsCh and record it in log +func (e *AnalyzeExec) handleResultsError( + ctx context.Context, + concurrency int, + needGlobalStats bool, + globalStatsMap globalStatsMap, + resultsCh <-chan *statistics.AnalyzeResults, + taskNum int, +) error { + partitionStatsConcurrency := e.Ctx().GetSessionVars().AnalyzePartitionConcurrency + // the concurrency of handleResultsError cannot be more than partitionStatsConcurrency + partitionStatsConcurrency = min(taskNum, partitionStatsConcurrency) + // If partitionStatsConcurrency > 1, we will try to demand extra session from Domain to save Analyze results in concurrency. + // If there is no extra session we can use, we will save analyze results in single-thread. + if partitionStatsConcurrency > 1 { + dom := domain.GetDomain(e.Ctx()) + subSctxs := dom.FetchAnalyzeExec(partitionStatsConcurrency) + if len(subSctxs) > 0 { + defer func() { + dom.ReleaseAnalyzeExec(subSctxs) + }() + internalCtx := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + err := e.handleResultsErrorWithConcurrency(internalCtx, concurrency, needGlobalStats, subSctxs, globalStatsMap, resultsCh) + return err + } + } + + tableIDs := map[int64]struct{}{} + + // save analyze results in single-thread. + statsHandle := domain.GetDomain(e.Ctx()).StatsHandle() + panicCnt := 0 + var err error + for panicCnt < concurrency { + results, ok := <-resultsCh + if !ok { + break + } + if results.Err != nil { + err = results.Err + if isAnalyzeWorkerPanic(err) { + panicCnt++ + } else { + logutil.Logger(ctx).Error("analyze failed", zap.Error(err)) + } + finishJobWithLog(e.Ctx(), results.Job, err) + continue + } + handleGlobalStats(needGlobalStats, globalStatsMap, results) + tableIDs[results.TableID.GetStatisticsID()] = struct{}{} + + if err1 := statsHandle.SaveTableStatsToStorage(results, e.Ctx().GetSessionVars().EnableAnalyzeSnapshot, handle.StatsMetaHistorySourceAnalyze); err1 != nil { + tableID := results.TableID.TableID + err = err1 + logutil.Logger(ctx).Error("save table stats to storage failed", zap.Error(err), zap.Int64("tableID", tableID)) + finishJobWithLog(e.Ctx(), results.Job, err) + } else { + finishJobWithLog(e.Ctx(), results.Job, nil) + } + if atomic.LoadUint32(&e.Ctx().GetSessionVars().Killed) == 1 { + finishJobWithLog(e.Ctx(), results.Job, exeerrors.ErrQueryInterrupted) + return errors.Trace(exeerrors.ErrQueryInterrupted) + } + } + // Dump stats to historical storage. + for tableID := range tableIDs { + if err := recordHistoricalStats(e.Ctx(), tableID); err != nil { + logutil.BgLogger().Error("record historical stats failed", zap.Error(err)) + } + } + + return err +} + +func (e *AnalyzeExec) handleResultsErrorWithConcurrency(ctx context.Context, statsConcurrency int, needGlobalStats bool, + subSctxs []sessionctx.Context, + globalStatsMap globalStatsMap, resultsCh <-chan *statistics.AnalyzeResults) error { + partitionStatsConcurrency := len(subSctxs) + + var wg util.WaitGroupWrapper + saveResultsCh := make(chan *statistics.AnalyzeResults, partitionStatsConcurrency) + errCh := make(chan error, partitionStatsConcurrency) + for i := 0; i < partitionStatsConcurrency; i++ { + worker := newAnalyzeSaveStatsWorker(saveResultsCh, subSctxs[i], errCh, &e.Ctx().GetSessionVars().Killed) + ctx1 := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + wg.Run(func() { + worker.run(ctx1, e.Ctx().GetSessionVars().EnableAnalyzeSnapshot) + }) + } + tableIDs := map[int64]struct{}{} + panicCnt := 0 + var err error + for panicCnt < statsConcurrency { + if atomic.LoadUint32(&e.Ctx().GetSessionVars().Killed) == 1 { + close(saveResultsCh) + return errors.Trace(exeerrors.ErrQueryInterrupted) + } + results, ok := <-resultsCh + if !ok { + break + } + if results.Err != nil { + err = results.Err + if isAnalyzeWorkerPanic(err) { + panicCnt++ + } else { + logutil.Logger(ctx).Error("analyze failed", zap.Error(err)) + } + finishJobWithLog(e.Ctx(), results.Job, err) + continue + } + handleGlobalStats(needGlobalStats, globalStatsMap, results) + tableIDs[results.TableID.GetStatisticsID()] = struct{}{} + saveResultsCh <- results + } + close(saveResultsCh) + wg.Wait() + close(errCh) + if len(errCh) > 0 { + errMsg := make([]string, 0) + for err1 := range errCh { + errMsg = append(errMsg, err1.Error()) + } + err = errors.New(strings.Join(errMsg, ",")) + } + for tableID := range tableIDs { + // Dump stats to historical storage. + if err := recordHistoricalStats(e.Ctx(), tableID); err != nil { + logutil.BgLogger().Error("record historical stats failed", zap.Error(err)) + } + } + return err +} + +func (e *AnalyzeExec) analyzeWorker(taskCh <-chan *analyzeTask, resultsCh chan<- *statistics.AnalyzeResults) { + var task *analyzeTask + defer func() { + if r := recover(); r != nil { + logutil.BgLogger().Error("analyze worker panicked", zap.Any("recover", r), zap.Stack("stack")) + metrics.PanicCounter.WithLabelValues(metrics.LabelAnalyze).Inc() + resultsCh <- &statistics.AnalyzeResults{ + Err: getAnalyzePanicErr(r), + Job: task.job, + } + } + }() + for { + var ok bool + task, ok = <-taskCh + if !ok { + break + } + StartAnalyzeJob(e.Ctx(), task.job) + switch task.taskType { + case colTask: + resultsCh <- analyzeColumnsPushDownEntry(task.colExec) + case idxTask: + resultsCh <- analyzeIndexPushdown(task.idxExec) + } + } +} + +type analyzeTask struct { + taskType taskType + idxExec *AnalyzeIndexExec + colExec *AnalyzeColumnsExec + job *statistics.AnalyzeJob +} + +type baseAnalyzeExec struct { + ctx sessionctx.Context + tableID statistics.AnalyzeTableID + concurrency int + analyzePB *tipb.AnalyzeReq + opts map[ast.AnalyzeOptionType]uint64 + job *statistics.AnalyzeJob + snapshot uint64 +} + +// AddNewAnalyzeJob records the new analyze job. +func AddNewAnalyzeJob(ctx sessionctx.Context, job *statistics.AnalyzeJob) { + if job == nil { + return + } + var instance string + serverInfo, err := infosync.GetServerInfo() + if err != nil { + logutil.BgLogger().Error("failed to get server info", zap.Error(err)) + instance = "unknown" + } else { + instance = net.JoinHostPort(serverInfo.IP, strconv.Itoa(int(serverInfo.Port))) + } + statsHandle := domain.GetDomain(ctx).StatsHandle() + err = statsHandle.InsertAnalyzeJob(job, instance, ctx.GetSessionVars().ConnectionID) + if err != nil { + logutil.BgLogger().Error("failed to insert analyze job", zap.Error(err)) + } +} + +// StartAnalyzeJob marks the state of the analyze job as running and sets the start time. +func StartAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob) { + if job == nil || job.ID == nil { + return + } + job.StartTime = time.Now() + job.Progress.SetLastDumpTime(job.StartTime) + exec := sctx.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + const sql = "UPDATE mysql.analyze_jobs SET start_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %? WHERE id = %?" + _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, job.StartTime.UTC().Format(types.TimeFormat), statistics.AnalyzeRunning, *job.ID) + if err != nil { + logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzePending, statistics.AnalyzeRunning)), zap.Error(err)) + } + failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { + if val.(bool) { + logutil.BgLogger().Info("StartAnalyzeJob", + zap.Time("start_time", job.StartTime), + zap.Uint64("job id", *job.ID), + ) + } + }) +} + +// UpdateAnalyzeJob updates count of the processed rows when increment reaches a threshold. +func UpdateAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, rowCount int64) { + if job == nil || job.ID == nil { + return + } + delta := job.Progress.Update(rowCount) + if delta == 0 { + return + } + exec := sctx.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + const sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %? WHERE id = %?" + _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, delta, *job.ID) + if err != nil { + logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("process %v rows", delta)), zap.Error(err)) + } + failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { + if val.(bool) { + logutil.BgLogger().Info("UpdateAnalyzeJob", + zap.Int64("increase processed_rows", delta), + zap.Uint64("job id", *job.ID), + ) + } + }) +} + +// FinishAnalyzeMergeJob finishes analyze merge job +func FinishAnalyzeMergeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { + if job == nil || job.ID == nil { + return + } + + job.EndTime = time.Now() + var sql string + var args []interface{} + if analyzeErr != nil { + failReason := analyzeErr.Error() + const textMaxLength = 65535 + if len(failReason) > textMaxLength { + failReason = failReason[:textMaxLength] + } + sql = "UPDATE mysql.analyze_jobs SET end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, fail_reason = %?, process_id = NULL WHERE id = %?" + args = []interface{}{job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFailed, failReason, *job.ID} + } else { + sql = "UPDATE mysql.analyze_jobs SET end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, process_id = NULL WHERE id = %?" + args = []interface{}{job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFinished, *job.ID} + } + exec := sctx.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, args...) + if err != nil { + var state string + if analyzeErr != nil { + state = statistics.AnalyzeFailed + } else { + state = statistics.AnalyzeFinished + } + logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzeRunning, state)), zap.Error(err)) + } + failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { + if val.(bool) { + logutil.BgLogger().Info("FinishAnalyzeMergeJob", + zap.Time("end_time", job.EndTime), + zap.Uint64("job id", *job.ID), + ) + } + }) +} + +// FinishAnalyzeJob updates the state of the analyze job to finished/failed according to `meetError` and sets the end time. +func FinishAnalyzeJob(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { + if job == nil || job.ID == nil { + return + } + job.EndTime = time.Now() + var sql string + var args []interface{} + // process_id is used to see which process is running the analyze job and kill the analyze job. After the analyze job + // is finished(or failed), process_id is useless and we set it to NULL to avoid `kill tidb process_id` wrongly. + if analyzeErr != nil { + failReason := analyzeErr.Error() + const textMaxLength = 65535 + if len(failReason) > textMaxLength { + failReason = failReason[:textMaxLength] + } + sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %?, end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, fail_reason = %?, process_id = NULL WHERE id = %?" + args = []interface{}{job.Progress.GetDeltaCount(), job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFailed, failReason, *job.ID} + } else { + sql = "UPDATE mysql.analyze_jobs SET processed_rows = processed_rows + %?, end_time = CONVERT_TZ(%?, '+00:00', @@TIME_ZONE), state = %?, process_id = NULL WHERE id = %?" + args = []interface{}{job.Progress.GetDeltaCount(), job.EndTime.UTC().Format(types.TimeFormat), statistics.AnalyzeFinished, *job.ID} + } + exec := sctx.(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + _, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseSessionPool}, sql, args...) + if err != nil { + var state string + if analyzeErr != nil { + state = statistics.AnalyzeFailed + } else { + state = statistics.AnalyzeFinished + } + logutil.BgLogger().Warn("failed to update analyze job", zap.String("update", fmt.Sprintf("%s->%s", statistics.AnalyzeRunning, state)), zap.Error(err)) + } + failpoint.Inject("DebugAnalyzeJobOperations", func(val failpoint.Value) { + if val.(bool) { + logutil.BgLogger().Info("FinishAnalyzeJob", + zap.Int64("increase processed_rows", job.Progress.GetDeltaCount()), + zap.Time("end_time", job.EndTime), + zap.Uint64("job id", *job.ID), + zap.Error(analyzeErr), + ) + } + }) +} + +func finishJobWithLog(sctx sessionctx.Context, job *statistics.AnalyzeJob, analyzeErr error) { + FinishAnalyzeJob(sctx, job, analyzeErr) + if job != nil { + var state string + if analyzeErr != nil { + state = statistics.AnalyzeFailed + } else { + state = statistics.AnalyzeFinished + } + logutil.BgLogger().Info(fmt.Sprintf("analyze table `%s`.`%s` has %s", job.DBName, job.TableName, state), + zap.String("partition", job.PartitionName), + zap.String("job info", job.JobInfo), + zap.Time("start time", job.StartTime), + zap.Time("end time", job.EndTime), + zap.String("cost", job.EndTime.Sub(job.StartTime).String()), + zap.String("sample rate reason", job.SampleRateReason)) + } +} + +func handleGlobalStats(needGlobalStats bool, globalStatsMap globalStatsMap, results *statistics.AnalyzeResults) { + if results.TableID.IsPartitionTable() && needGlobalStats { + for _, result := range results.Ars { + if result.IsIndex == 0 { + // If it does not belong to the statistics of index, we need to set it to -1 to distinguish. + globalStatsID := globalStatsKey{tableID: results.TableID.TableID, indexID: int64(-1)} + histIDs := make([]int64, 0, len(result.Hist)) + for _, hg := range result.Hist { + // It's normal virtual column, skip. + if hg == nil { + continue + } + histIDs = append(histIDs, hg.ID) + } + globalStatsMap[globalStatsID] = globalStatsInfo{isIndex: result.IsIndex, histIDs: histIDs, statsVersion: results.StatsVer} + } else { + for _, hg := range result.Hist { + globalStatsID := globalStatsKey{tableID: results.TableID.TableID, indexID: hg.ID} + globalStatsMap[globalStatsID] = globalStatsInfo{isIndex: result.IsIndex, histIDs: []int64{hg.ID}, statsVersion: results.StatsVer} + } + } + } + } +} diff --git a/executor/analyze_col.go b/pkg/executor/analyze_col.go similarity index 95% rename from executor/analyze_col.go rename to pkg/executor/analyze_col.go index a47ebecce5651..5946401ca7aa0 100644 --- a/executor/analyze_col.go +++ b/pkg/executor/analyze_col.go @@ -24,21 +24,21 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" ) diff --git a/executor/analyze_col_v2.go b/pkg/executor/analyze_col_v2.go similarity index 97% rename from executor/analyze_col_v2.go rename to pkg/executor/analyze_col_v2.go index 2b9157fc2a37e..9597a384049e1 100644 --- a/executor/analyze_col_v2.go +++ b/pkg/executor/analyze_col_v2.go @@ -24,27 +24,27 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/executor/analyze_global_stats.go b/pkg/executor/analyze_global_stats.go similarity index 95% rename from executor/analyze_global_stats.go rename to pkg/executor/analyze_global_stats.go index 540f658dc1a40..eb3c51b5b9b07 100644 --- a/executor/analyze_global_stats.go +++ b/pkg/executor/analyze_global_stats.go @@ -18,12 +18,12 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "golang.org/x/exp/maps" ) diff --git a/executor/analyze_idx.go b/pkg/executor/analyze_idx.go similarity index 95% rename from executor/analyze_idx.go rename to pkg/executor/analyze_idx.go index ba82dfe937ea7..64531547ee5eb 100644 --- a/executor/analyze_idx.go +++ b/pkg/executor/analyze_idx.go @@ -22,18 +22,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/pkg/executor/analyze_test.go b/pkg/executor/analyze_test.go new file mode 100644 index 0000000000000..1127f5a426952 --- /dev/null +++ b/pkg/executor/analyze_test.go @@ -0,0 +1,243 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "os" + "strconv" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/stretchr/testify/require" +) + +func checkHistogram(sc *stmtctx.StatementContext, hg *statistics.Histogram) (bool, error) { + for i := 0; i < len(hg.Buckets); i++ { + lower, upper := hg.GetLower(i), hg.GetUpper(i) + cmp, err := upper.Compare(sc, lower, collate.GetBinaryCollator()) + if cmp < 0 || err != nil { + return false, err + } + if i == 0 { + continue + } + previousUpper := hg.GetUpper(i - 1) + cmp, err = lower.Compare(sc, previousUpper, collate.GetBinaryCollator()) + if cmp <= 0 || err != nil { + return false, err + } + } + return true, nil +} + +func TestAnalyzeIndexExtractTopN(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + var dom *domain.Domain + session.DisableStats4Test() + session.SetSchemaLease(0) + dom, err = session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create database test_index_extract_topn") + tk.MustExec("use test_index_extract_topn") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx(a, b))") + tk.MustExec("insert into t values(1, 1), (1, 1), (1, 2), (1, 2)") + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("analyze table t") + + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err := is.TableByName(model.NewCIStr("test_index_extract_topn"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + tbl := dom.StatsHandle().GetTableStats(tableInfo) + + // Construct TopN, should be (1, 1) -> 2 and (1, 2) -> 2 + topn := statistics.NewTopN(2) + { + key1, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, types.NewIntDatum(1), types.NewIntDatum(1)) + require.NoError(t, err) + topn.AppendTopN(key1, 2) + key2, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, types.NewIntDatum(1), types.NewIntDatum(2)) + require.NoError(t, err) + topn.AppendTopN(key2, 2) + } + for _, idx := range tbl.Indices { + ok, err := checkHistogram(tk.Session().GetSessionVars().StmtCtx, &idx.Histogram) + require.NoError(t, err) + require.True(t, ok) + require.True(t, idx.TopN.Equal(topn)) + } +} + +func TestAnalyzePartitionTableForFloat(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t1 ( id bigint(20) unsigned NOT NULL AUTO_INCREMENT, num float(9,8) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH (id) PARTITIONS 128;") + // To reproduce the error we meet in https://github.com/pingcap/tidb/issues/35910, we should use the data provided in this issue + b, err := os.ReadFile("testdata/analyze_test_data.sql") + require.NoError(t, err) + sqls := strings.Split(string(b), ";") + for _, sql := range sqls { + if len(sql) < 1 { + continue + } + tk.MustExec(sql) + } + tk.MustExec("analyze table t1") +} + +func TestAnalyzePartitionTableByConcurrencyInDynamic(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk.MustExec("use test") + tk.MustExec("create table t(id int) partition by hash(id) partitions 4") + testcases := []struct { + concurrency string + }{ + { + concurrency: "1", + }, + { + concurrency: "2", + }, + { + concurrency: "3", + }, + { + concurrency: "4", + }, + { + concurrency: "5", + }, + } + // assert empty table + for _, tc := range testcases { + concurrency := tc.concurrency + fmt.Println("testcase ", concurrency) + tk.MustExec(fmt.Sprintf("set @@global.tidb_merge_partition_stats_concurrency=%v", concurrency)) + tk.MustQuery("select @@global.tidb_merge_partition_stats_concurrency").Check(testkit.Rows(concurrency)) + tk.MustExec(fmt.Sprintf("set @@tidb_analyze_partition_concurrency=%v", concurrency)) + tk.MustQuery("select @@tidb_analyze_partition_concurrency").Check(testkit.Rows(concurrency)) + + tk.MustExec("analyze table t") + tk.MustQuery("show stats_topn where partition_name = 'global' and table_name = 't'") + } + + for i := 1; i <= 500; i++ { + for j := 1; j <= 20; j++ { + tk.MustExec(fmt.Sprintf("insert into t (id) values (%v)", j)) + } + } + var expected [][]interface{} + for i := 1; i <= 20; i++ { + expected = append(expected, []interface{}{ + strconv.FormatInt(int64(i), 10), "500", + }) + } + testcases = []struct { + concurrency string + }{ + { + concurrency: "1", + }, + { + concurrency: "2", + }, + { + concurrency: "3", + }, + { + concurrency: "4", + }, + { + concurrency: "5", + }, + } + for _, tc := range testcases { + concurrency := tc.concurrency + fmt.Println("testcase ", concurrency) + tk.MustExec(fmt.Sprintf("set @@tidb_merge_partition_stats_concurrency=%v", concurrency)) + tk.MustQuery("select @@tidb_merge_partition_stats_concurrency").Check(testkit.Rows(concurrency)) + tk.MustExec("analyze table t") + tk.MustQuery("show stats_topn where partition_name = 'global' and table_name = 't'").CheckAt([]int{5, 6}, expected) + } +} + +func TestMergeGlobalStatsWithUnAnalyzedPartition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_partition_prune_mode=dynamic;") + tk.MustExec("CREATE TABLE `t` ( `id` int(11) DEFAULT NULL, `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL ) PARTITION BY RANGE (`id`) (PARTITION `p0` VALUES LESS THAN (3), PARTITION `p1` VALUES LESS THAN (7), PARTITION `p2` VALUES LESS THAN (11));") + tk.MustExec("insert into t values (1,1,1,1),(2,2,2,2),(4,4,4,4),(5,5,5,5),(6,6,6,6),(8,8,8,8),(9,9,9,9);") + tk.MustExec("create index idxa on t (a);") + tk.MustExec("create index idxb on t (b);") + tk.MustExec("create index idxc on t (c);") + tk.MustExec("analyze table t partition p0 index idxa;") + tk.MustExec("analyze table t partition p1 index idxb;") + tk.MustExec("analyze table t partition p2 index idxc;") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1105 The version 2 would collect all statistics not only the selected indexes", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p2, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"")) + tk.MustExec("analyze table t partition p0;") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/2) as the sample-rate=1\"")) +} + +func TestSetFastAnalyzeSystemVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@session.tidb_enable_fast_analyze=1") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1105 the fast analyze feature has already been removed in TiDB v7.5.0, so this will have no effect")) +} + +func TestIncrementalAnalyze(t *testing.T) { + msg := "the incremental analyze feature has already been removed in TiDB v7.5.0, so this will have no effect" + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, primary key(a), index idx(b))") + tk.MustMatchErrMsg("analyze incremental table t index", msg) + // Create a partition table. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, primary key(a), index idx(b)) partition by range(a) (partition p0 values less than (10), partition p1 values less than (20))") + tk.MustMatchErrMsg("analyze incremental table t partition p0 index idx", msg) +} diff --git a/executor/analyze_utils.go b/pkg/executor/analyze_utils.go similarity index 94% rename from executor/analyze_utils.go rename to pkg/executor/analyze_utils.go index f13507ebc0796..089f34bd4c7b4 100644 --- a/executor/analyze_utils.go +++ b/pkg/executor/analyze_utils.go @@ -21,11 +21,11 @@ import ( "sync" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/atomic" ) diff --git a/executor/analyze_utils_test.go b/pkg/executor/analyze_utils_test.go similarity index 95% rename from executor/analyze_utils_test.go rename to pkg/executor/analyze_utils_test.go index f409fa66c79de..eb764ecb3f6d2 100644 --- a/executor/analyze_utils_test.go +++ b/pkg/executor/analyze_utils_test.go @@ -18,7 +18,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) diff --git a/executor/analyze_worker.go b/pkg/executor/analyze_worker.go similarity index 89% rename from executor/analyze_worker.go rename to pkg/executor/analyze_worker.go index 1a8bb7485d41f..0cace481fc141 100644 --- a/executor/analyze_worker.go +++ b/pkg/executor/analyze_worker.go @@ -19,11 +19,11 @@ import ( "sync/atomic" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/executor/asyncloaddata/BUILD.bazel b/pkg/executor/asyncloaddata/BUILD.bazel new file mode 100644 index 0000000000000..c8b1906431eaa --- /dev/null +++ b/pkg/executor/asyncloaddata/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "asyncloaddata", + srcs = [ + "progress.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/asyncloaddata", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//util", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "asyncloaddata_test", + timeout = "short", + srcs = [ + "main_test.go", + "progress_test.go", + "util_test.go", + ], + embed = [":asyncloaddata"], + flaky = True, + race = "on", + shard_count = 6, + deps = [ + "//pkg/testkit", + "//pkg/util/sqlexec", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/asyncloaddata/main_test.go b/pkg/executor/asyncloaddata/main_test.go similarity index 100% rename from executor/asyncloaddata/main_test.go rename to pkg/executor/asyncloaddata/main_test.go diff --git a/executor/asyncloaddata/progress.go b/pkg/executor/asyncloaddata/progress.go similarity index 100% rename from executor/asyncloaddata/progress.go rename to pkg/executor/asyncloaddata/progress.go diff --git a/executor/asyncloaddata/progress_test.go b/pkg/executor/asyncloaddata/progress_test.go similarity index 100% rename from executor/asyncloaddata/progress_test.go rename to pkg/executor/asyncloaddata/progress_test.go diff --git a/pkg/executor/asyncloaddata/util.go b/pkg/executor/asyncloaddata/util.go new file mode 100644 index 0000000000000..1b3681038a274 --- /dev/null +++ b/pkg/executor/asyncloaddata/util.go @@ -0,0 +1,595 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package asyncloaddata + +import ( + "context" + "fmt" + "net/url" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +// vars used for test. +var ( + // TestSyncCh is used in unit test to synchronize the execution of LOAD DATA. + TestSyncCh = make(chan struct{}) + // TestLastLoadDataJobID last created job id, used in unit test. + TestLastLoadDataJobID atomic.Int64 +) + +// Job import job. +type Job struct { + ID int64 + // Job don't manage the life cycle of the connection. + Conn sqlexec.SQLExecutor + User string +} + +// NewJob returns new Job. +func NewJob(id int64, conn sqlexec.SQLExecutor, user string) *Job { + return &Job{ID: id, Conn: conn, User: user} +} + +// CreateLoadDataJob creates a load data job by insert a record to system table. +// The AUTO_INCREMENT value will be returned as jobID. +func CreateLoadDataJob( + ctx context.Context, + conn sqlexec.SQLExecutor, + dataSource, db, table string, + importMode string, + user string, +) (*Job, error) { + // remove the params in data source URI because it may contains AK/SK + u, err := url.Parse(dataSource) + if err == nil && u.Scheme != "" { + u.RawQuery = "" + u.Fragment = "" + dataSource = u.String() + } + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err = conn.ExecuteInternal(ctx, + `INSERT INTO mysql.load_data_jobs + (data_source, table_schema, table_name, import_mode, create_user) + VALUES (%?, %?, %?, %?, %?);`, + dataSource, db, table, importMode, user) + if err != nil { + return nil, err + } + rs, err := conn.ExecuteInternal(ctx, `SELECT LAST_INSERT_ID();`) + if err != nil { + return nil, err + } + //nolint: errcheck + defer rs.Close() + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return nil, err + } + if len(rows) != 1 { + return nil, errors.Errorf("unexpected result length: %d", len(rows)) + } + + failpoint.Inject("SaveLastLoadDataJobID", func() { + TestLastLoadDataJobID.Store(rows[0].GetInt64(0)) + }) + return NewJob(rows[0].GetInt64(0), conn, user), nil +} + +// StartJob tries to start a not-yet-started job with jobID. It will not return +// error when there's no matched job. +func (j *Job) StartJob(ctx context.Context) error { + failpoint.Inject("AfterCreateLoadDataJob", nil) + failpoint.Inject("SyncAfterCreateLoadDataJob", func() { + TestSyncCh <- struct{}{} + <-TestSyncCh + }) + + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err := j.Conn.ExecuteInternal(ctx, + `UPDATE mysql.load_data_jobs + SET start_time = CURRENT_TIMESTAMP(6), update_time = CURRENT_TIMESTAMP(6) + WHERE job_id = %? AND start_time IS NULL AND end_time IS NULL;`, + j.ID) + if err != nil { + return err + } + + failpoint.Inject("AfterStartJob", nil) + failpoint.Inject("SyncAfterStartJob", func() { + TestSyncCh <- struct{}{} + <-TestSyncCh + }) + return nil +} + +var ( + // HeartBeatInSec is the interval of heartbeat. + HeartBeatInSec = 5 + // OfflineThresholdInSec means after failing to update heartbeat for 3 times, + // we treat the worker of the job as offline. + OfflineThresholdInSec = HeartBeatInSec * 3 +) + +// UpdateJobProgress updates the progress of a load data job. It should be called +// periodically as heartbeat after StartJob. +// The returned bool indicates whether the keepalive is succeeded. If not, the +// caller should call FailJob soon. +// TODO: Currently if the node is crashed after CreateLoadDataJob and before StartJob, +// it will always be in the status of pending. Maybe we should unify CreateLoadDataJob +// and StartJob. +func (j *Job) UpdateJobProgress(ctx context.Context, progress string) (bool, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + // let TiDB handle heartbeat check for concurrent SQL + // we tolerate 2 times of failure/timeout when updating heartbeat + _, err := j.Conn.ExecuteInternal(ctx, + `UPDATE mysql.load_data_jobs + SET progress = %?, update_time = CURRENT_TIMESTAMP(6) + WHERE job_id = %? + AND end_time IS NULL + AND (update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) + OR update_time IS NULL);`, + progress, j.ID, OfflineThresholdInSec) + if err != nil { + return false, err + } + return j.Conn.GetSessionVars().StmtCtx.AffectedRows() == 1, nil +} + +// FinishJob finishes a load data job. A job can only be finished once. +func (j *Job) FinishJob(ctx context.Context, result string) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err := j.Conn.ExecuteInternal(ctx, + `UPDATE mysql.load_data_jobs + SET end_time = CURRENT_TIMESTAMP(6), result_message = %? + WHERE job_id = %? AND result_message IS NULL AND error_message IS NULL;`, + result, j.ID) + return err +} + +// FailJob fails a load data job. A job can only be failed once. +func (j *Job) FailJob(ctx context.Context, result string) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err := j.Conn.ExecuteInternal(ctx, + `UPDATE mysql.load_data_jobs + SET end_time = CURRENT_TIMESTAMP(6), error_message = %? + WHERE job_id = %? AND result_message IS NULL AND error_message IS NULL;`, + result, j.ID) + return err +} + +// CancelJob cancels a load data job. Only a running/paused job can be canceled. +func (j *Job) CancelJob(ctx context.Context) (err error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err = j.Conn.ExecuteInternal(ctx, "BEGIN PESSIMISTIC;") + if err != nil { + return err + } + defer func() { + if err != nil { + _, err1 := j.Conn.ExecuteInternal(ctx, "ROLLBACK;") + terror.Log(err1) + return + } + + _, err = j.Conn.ExecuteInternal(ctx, "COMMIT;") + if err != nil { + return + } + }() + + var ( + rs sqlexec.RecordSet + rows []chunk.Row + ) + rs, err = j.Conn.ExecuteInternal(ctx, + `SELECT expected_status, end_time, error_message FROM mysql.load_data_jobs + WHERE job_id = %? AND create_user = %?;`, + j.ID, j.User) + if err != nil { + return err + } + defer terror.Call(rs.Close) + rows, err = sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return err + } + + if len(rows) < 1 { + return exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) + } + status := rows[0].GetEnum(0).String() + if status != "running" && status != "paused" { + return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs(fmt.Sprintf("need status running or paused, but got %s", status)) + } + endTimeIsNull := rows[0].IsNull(1) + if !endTimeIsNull { + hasError := !rows[0].IsNull(2) + if hasError { + return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs("need status running or paused, but got failed") + } + return exeerrors.ErrLoadDataInvalidOperation.GenWithStackByArgs("need status running or paused, but got finished") + } + + _, err = j.Conn.ExecuteInternal(ctx, + `UPDATE mysql.load_data_jobs + SET expected_status = 'canceled', + end_time = CURRENT_TIMESTAMP(6), + error_message = 'canceled by user' + WHERE job_id = %?;`, + j.ID) + return err +} + +// DropJob drops a load data job. +func (j *Job) DropJob(ctx context.Context) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + _, err := j.Conn.ExecuteInternal(ctx, + `DELETE FROM mysql.load_data_jobs + WHERE job_id = %? AND create_user = %?;`, + j.ID, j.User) + if err == nil { + return err + } + if j.Conn.GetSessionVars().StmtCtx.AffectedRows() < 1 { + return exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) + } + return nil +} + +// OnComplete is called when a job is finished or failed. +func (j *Job) OnComplete(inErr error, msg string) { + // write the ending status even if user context is canceled. + ctx2 := context.Background() + ctx2 = kv.WithInternalSourceType(ctx2, kv.InternalLoadData) + if inErr == nil { + err2 := j.FinishJob(ctx2, msg) + terror.Log(err2) + return + } + errMsg := inErr.Error() + if errImpl, ok := errors.Cause(inErr).(*errors.Error); ok { + b, marshalErr := errImpl.MarshalJSON() + if marshalErr == nil { + errMsg = string(b) + } + } + + err2 := j.FailJob(ctx2, errMsg) + terror.Log(err2) +} + +// ProgressUpdateRoutineFn job progress update routine. +func (j *Job) ProgressUpdateRoutineFn(ctx context.Context, finishCh chan struct{}, errCh <-chan struct{}, progress *Progress) error { + ticker := time.NewTicker(time.Duration(HeartBeatInSec) * time.Second) + defer ticker.Stop() + + for { + select { + case <-finishCh: + // When done, try to update progress to reach 100% + ok, err2 := j.UpdateJobProgress(ctx, progress.String()) + if !ok || err2 != nil { + logutil.Logger(ctx).Warn("failed to update job progress when finished", + zap.Bool("ok", ok), zap.Error(err2)) + } + return nil + case <-errCh: + return nil + case <-ticker.C: + ok, err2 := j.UpdateJobProgress(ctx, progress.String()) + if err2 != nil { + return err2 + } + if !ok { + return errors.Errorf("failed to update job progress, the job %d is interrupted by user or failed to keepalive", j.ID) + } + } + } +} + +// JobExpectedStatus is the expected status of a load data job. User can set the +// expected status of a job and worker will respect it. +type JobExpectedStatus int + +const ( + // JobExpectedRunning means the job is expected to be running. + JobExpectedRunning JobExpectedStatus = iota + // JobExpectedPaused means the job is expected to be paused. + JobExpectedPaused + // JobExpectedCanceled means the job is expected to be canceled. + JobExpectedCanceled +) + +// UpdateJobExpectedStatus updates the expected status of a load data job. +// TODO: remove it? +func UpdateJobExpectedStatus( + ctx context.Context, + conn sqlexec.SQLExecutor, + jobID int64, + status JobExpectedStatus, +) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + var sql string + switch status { + case JobExpectedRunning: + sql = `UPDATE mysql.load_data_jobs + SET expected_status = 'running' + WHERE job_id = %? AND expected_status = 'paused';` + case JobExpectedPaused: + sql = `UPDATE mysql.load_data_jobs + SET expected_status = 'paused' + WHERE job_id = %? AND expected_status = 'running';` + case JobExpectedCanceled: + sql = `UPDATE mysql.load_data_jobs + SET expected_status = 'canceled' + WHERE job_id = %? AND expected_status != 'canceled';` + } + _, err := conn.ExecuteInternal(ctx, sql, jobID) + return err +} + +// JobStatus represents the status of a load data job. +type JobStatus int + +const ( + // JobFailed means the job is failed and can't be resumed. + JobFailed JobStatus = iota + // JobCanceled means the job is canceled by user and can't be resumed. It + // will finally convert to JobFailed with a message indicating the reason + // is canceled. + JobCanceled + // JobPaused means the job is paused by user and can be resumed. + JobPaused + // JobFinished means the job is finished. + JobFinished + // JobPending means the job is pending to be started. + JobPending + // JobRunning means the job is running. + JobRunning +) + +func (s JobStatus) String() string { + switch s { + case JobFailed: + return "failed" + case JobCanceled: + return "canceled" + case JobPaused: + return "paused" + case JobFinished: + return "finished" + case JobPending: + return "pending" + case JobRunning: + return "running" + default: + return "unknown JobStatus" + } +} + +// GetJobStatus gets the status of a load data job. The returned error means +// something wrong when querying the database. Other business logic errors are +// returned as JobFailed with message. +func (j *Job) GetJobStatus(ctx context.Context) (JobStatus, string, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + rs, err := j.Conn.ExecuteInternal(ctx, + `SELECT + expected_status, + update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, + end_time, + result_message, + error_message, + start_time + FROM mysql.load_data_jobs + WHERE job_id = %?;`, + OfflineThresholdInSec, j.ID) + if err != nil { + return JobFailed, "", err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return JobFailed, "", err + } + if len(rows) != 1 { + return JobFailed, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID).Error(), nil + } + + return getJobStatus(rows[0]) +} + +// getJobStatus expected the first 6 columns of input row is (expected_status, +// is_alive (derived from update_time), end_time, result_message, error_message, +// start_time). +func getJobStatus(row chunk.Row) (JobStatus, string, error) { + // ending status has the highest priority + expectedStatus := row.GetEnum(0).String() + endTimeIsNull := row.IsNull(2) + if !endTimeIsNull { + resultMsgIsNull := row.IsNull(3) + if !resultMsgIsNull { + resultMessage := row.GetString(3) + return JobFinished, resultMessage, nil + } + + errorMessage := row.GetString(4) + if expectedStatus == "canceled" { + return JobCanceled, errorMessage, nil + } + return JobFailed, errorMessage, nil + } + + isAlive := row.GetInt64(1) == 1 + startTimeIsNull := row.IsNull(5) + + switch expectedStatus { + case "canceled": + return JobCanceled, "", nil + case "paused": + if startTimeIsNull || isAlive { + return JobPaused, "", nil + } + return JobFailed, "job expected paused but the node is timeout", nil + case "running": + if startTimeIsNull { + return JobPending, "", nil + } + if isAlive { + return JobRunning, "", nil + } + return JobFailed, "job expected running but the node is timeout", nil + default: + return JobFailed, fmt.Sprintf("unexpected job status %s", expectedStatus), nil + } +} + +// JobInfo is the information of a load data job. +type JobInfo struct { + JobID int64 + User string + DataSource string + TableSchema string + TableName string + ImportMode string + Progress string + Status JobStatus + StatusMessage string + CreateTime types.Time + StartTime types.Time + EndTime types.Time +} + +// GetJobInfo gets all needed information of a load data job. +func (j *Job) GetJobInfo(ctx context.Context) (*JobInfo, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + rs, err := j.Conn.ExecuteInternal(ctx, + `SELECT + expected_status, + update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, + end_time, + result_message, + error_message, + start_time, + + job_id, + data_source, + table_schema, + table_name, + import_mode, + progress, + create_user, + create_time + FROM mysql.load_data_jobs + WHERE job_id = %? AND create_user = %?;`, + OfflineThresholdInSec, j.ID, j.User) + if err != nil { + return nil, err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return nil, err + } + if len(rows) != 1 { + return nil, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(j.ID) + } + + return getJobInfo(rows[0]) +} + +// getJobInfo expected the columns of input row is (expected_status, +// is_alive (derived from update_time), end_time, result_message, error_message, +// start_time, job_id, data_source, table_schema, table_name, import_mode, +// progress, create_user). +func getJobInfo(row chunk.Row) (*JobInfo, error) { + var err error + jobInfo := JobInfo{ + JobID: row.GetInt64(6), + DataSource: row.GetString(7), + TableSchema: row.GetString(8), + TableName: row.GetString(9), + ImportMode: row.GetString(10), + Progress: row.GetString(11), + User: row.GetString(12), + CreateTime: row.GetTime(13), + StartTime: row.GetTime(5), + EndTime: row.GetTime(2), + } + jobInfo.Status, jobInfo.StatusMessage, err = getJobStatus(row) + if err != nil { + return nil, err + } + return &jobInfo, nil +} + +// GetAllJobInfo gets all jobs status of a user. +func GetAllJobInfo( + ctx context.Context, + conn sqlexec.SQLExecutor, + user string, +) ([]*JobInfo, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalLoadData) + rs, err := conn.ExecuteInternal(ctx, + `SELECT + expected_status, + update_time >= DATE_SUB(CURRENT_TIMESTAMP(6), INTERVAL %? SECOND) AS is_alive, + end_time, + result_message, + error_message, + start_time, + + job_id, + data_source, + table_schema, + table_name, + import_mode, + progress, + create_user, + create_time + FROM mysql.load_data_jobs + WHERE create_user = %?;`, + OfflineThresholdInSec, user) + if err != nil { + return nil, err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return nil, err + } + ret := make([]*JobInfo, 0, len(rows)) + for _, row := range rows { + jobInfo, err := getJobInfo(row) + if err != nil { + return nil, err + } + ret = append(ret, jobInfo) + } + + return ret, nil +} diff --git a/pkg/executor/asyncloaddata/util_test.go b/pkg/executor/asyncloaddata/util_test.go new file mode 100644 index 0000000000000..0f5a224ef2b08 --- /dev/null +++ b/pkg/executor/asyncloaddata/util_test.go @@ -0,0 +1,345 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package asyncloaddata_test + +import ( + "context" + "testing" + "time" + + . "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" +) + +func checkEqualIgnoreTimes(t *testing.T, expected, got *JobInfo) { + cloned := *expected + cloned.CreateTime = got.CreateTime + cloned.StartTime = got.StartTime + cloned.EndTime = got.EndTime + require.Equal(t, &cloned, got) +} + +func createJob(t *testing.T, conn sqlexec.SQLExecutor, user string) (*Job, *JobInfo) { + job, err := CreateLoadDataJob(context.Background(), conn, "/tmp/test.csv", "test", "t", "logical", user) + require.NoError(t, err) + info, err := job.GetJobInfo(context.Background()) + require.NoError(t, err) + expected := &JobInfo{ + JobID: job.ID, + User: user, + DataSource: "/tmp/test.csv", + TableSchema: "test", + TableName: "t", + ImportMode: "logical", + Progress: "", + Status: JobPending, + StatusMessage: "", + } + checkEqualIgnoreTimes(t, expected, info) + return job, info +} + +func TestHappyPath(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + ctx := context.Background() + + // job is created + + job, expected := createJob(t, tk.Session(), "user") + + // job is started by a worker + + backup := OfflineThresholdInSec + OfflineThresholdInSec = 1000 + t.Cleanup(func() { + OfflineThresholdInSec = backup + }) + err := job.StartJob(ctx) + require.NoError(t, err) + info, err := job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobRunning + checkEqualIgnoreTimes(t, expected, info) + + // job is periodically updated by worker + + ok, err := job.UpdateJobProgress(ctx, "imported 10%") + require.NoError(t, err) + require.True(t, ok) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Progress = "imported 10%" + checkEqualIgnoreTimes(t, expected, info) + + // job is paused + + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobPaused + checkEqualIgnoreTimes(t, expected, info) + + // worker still can update progress, maybe response to pausing is delayed + + ok, err = job.UpdateJobProgress(ctx, "imported 20%") + require.NoError(t, err) + require.True(t, ok) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Progress = "imported 20%" + checkEqualIgnoreTimes(t, expected, info) + + // job is resumed + + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobRunning + checkEqualIgnoreTimes(t, expected, info) + + // job is finished + + err = job.FinishJob(ctx, "finished message") + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobFinished + expected.StatusMessage = "finished message" + checkEqualIgnoreTimes(t, expected, info) +} + +func TestKeepAlive(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + ctx := context.Background() + + // job is created + + job, expected := createJob(t, tk.Session(), "user") + + backup := OfflineThresholdInSec + OfflineThresholdInSec = 1 + t.Cleanup(func() { + OfflineThresholdInSec = backup + }) + + // before job is started, worker don't need to keepalive + // TODO:👆not correct! + + time.Sleep(2 * time.Second) + info, err := job.GetJobInfo(ctx) + require.NoError(t, err) + checkEqualIgnoreTimes(t, expected, info) + + err = job.StartJob(ctx) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobRunning + checkEqualIgnoreTimes(t, expected, info) + + // if worker failed to keepalive, job will fail + + time.Sleep(2 * time.Second) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobFailed + expected.StatusMessage = "job expected running but the node is timeout" + checkEqualIgnoreTimes(t, expected, info) + + // after the worker is failed to keepalive, further keepalive will fail + + ok, err := job.UpdateJobProgress(ctx, "imported 20%") + require.NoError(t, err) + require.False(t, ok) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + checkEqualIgnoreTimes(t, expected, info) + + // when worker fails to keepalive, before it calls FailJob, it still can + // change expected status to some extent. + + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.StatusMessage = "job expected paused but the node is timeout" + checkEqualIgnoreTimes(t, expected, info) + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.StatusMessage = "job expected running but the node is timeout" + checkEqualIgnoreTimes(t, expected, info) + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedCanceled) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobCanceled + expected.StatusMessage = "" + checkEqualIgnoreTimes(t, expected, info) + + // Now the worker calls FailJob, but the status should still be canceled, + // that's more friendly. + + err = job.FailJob(ctx, "failed to keepalive") + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobCanceled + expected.StatusMessage = "failed to keepalive" + checkEqualIgnoreTimes(t, expected, info) +} + +func TestJobIsFailedAndGetAllJobs(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + ctx := context.Background() + + // job is created + + job, expected := createJob(t, tk.Session(), "user") + + // job can be failed directly when it's pending + + err := job.FailJob(ctx, "failed message") + require.NoError(t, err) + info, err := job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobFailed + expected.StatusMessage = "failed message" + checkEqualIgnoreTimes(t, expected, info) + + // create another job and fail it + + job, expected = createJob(t, tk.Session(), "user") + + err = job.StartJob(ctx) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobRunning + checkEqualIgnoreTimes(t, expected, info) + + err = job.FailJob(ctx, "failed message") + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + expected.Status = JobFailed + expected.StatusMessage = "failed message" + checkEqualIgnoreTimes(t, expected, info) + + // test change expected status of a failed job. + + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedPaused) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + checkEqualIgnoreTimes(t, expected, info) + err = UpdateJobExpectedStatus(ctx, tk.Session(), job.ID, JobExpectedRunning) + require.NoError(t, err) + info, err = job.GetJobInfo(ctx) + require.NoError(t, err) + checkEqualIgnoreTimes(t, expected, info) + err = job.CancelJob(ctx) + require.ErrorContains(t, err, "The current job status cannot perform the operation. need status running or paused, but got failed") + + // add job of another user and test GetAllJobInfo + + job, _ = createJob(t, tk.Session(), "user2") + + jobs, err := GetAllJobInfo(ctx, tk.Session(), "user") + require.NoError(t, err) + require.Equal(t, 2, len(jobs)) + require.Equal(t, JobFailed, jobs[0].Status) + require.Equal(t, JobFailed, jobs[1].Status) + + jobs, err = GetAllJobInfo(ctx, tk.Session(), "user2") + require.NoError(t, err) + require.Equal(t, 1, len(jobs)) + require.Equal(t, JobPending, jobs[0].Status) + require.Equal(t, job.ID, jobs[0].JobID) + + job.User = "wrong_user" + _, err = job.GetJobInfo(ctx) + require.ErrorContains(t, err, "doesn't exist") +} + +func TestGetJobStatus(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + ctx := context.Background() + + // job is created + + job, _ := createJob(t, tk.Session(), "user") + + // job is pending + + status, msg, err := job.GetJobStatus(ctx) + require.NoError(t, err) + require.Equal(t, JobPending, status) + require.Equal(t, "", msg) + + // job is running + + backup := OfflineThresholdInSec + OfflineThresholdInSec = 1000 + t.Cleanup(func() { + OfflineThresholdInSec = backup + }) + err = job.StartJob(ctx) + require.NoError(t, err) + status, msg, err = job.GetJobStatus(ctx) + require.NoError(t, err) + require.Equal(t, JobRunning, status) + require.Equal(t, "", msg) + + // job is finished + + err = job.FinishJob(ctx, "finished message") + require.NoError(t, err) + status, msg, err = job.GetJobStatus(ctx) + require.NoError(t, err) + require.Equal(t, JobFinished, status) + require.Equal(t, "finished message", msg) + + // wrong ID + + job.ID += 1 + status, msg, err = job.GetJobStatus(ctx) + require.NoError(t, err) + require.Equal(t, JobFailed, status) + require.Contains(t, msg, "doesn't exist") +} + +func TestCreateLoadDataJobRedact(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + ctx := context.Background() + + _, err := CreateLoadDataJob(ctx, tk.Session(), + "s3://bucket/a.csv?access-key=hideme&secret-access-key=hideme", + "db", "table", "mode", "user") + require.NoError(t, err) + result := tk.MustQuery("SELECT * FROM mysql.load_data_jobs;") + result.CheckContain("a.csv") + result.CheckNotContain("hideme") +} diff --git a/executor/batch_checker.go b/pkg/executor/batch_checker.go similarity index 93% rename from executor/batch_checker.go rename to pkg/executor/batch_checker.go index fb8a36ec6b3b6..5463ea8916586 100644 --- a/executor/batch_checker.go +++ b/pkg/executor/batch_checker.go @@ -20,20 +20,20 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/stringutil" ) type keyValueWithDupInfo struct { diff --git a/executor/batch_point_get.go b/pkg/executor/batch_point_get.go similarity index 95% rename from executor/batch_point_get.go rename to pkg/executor/batch_point_get.go index ec458bcd52bce..06afa7285fa8f 100644 --- a/executor/batch_point_get.go +++ b/pkg/executor/batch_point_get.go @@ -21,23 +21,23 @@ import ( "sync/atomic" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - driver "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + driver "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/rowcodec" ) // BatchPointGetExec executes a bunch of point select queries. diff --git a/executor/batch_point_get_test.go b/pkg/executor/batch_point_get_test.go similarity index 97% rename from executor/batch_point_get_test.go rename to pkg/executor/batch_point_get_test.go index 3309afd3be6b6..c51d41601e5ca 100644 --- a/executor/batch_point_get_test.go +++ b/pkg/executor/batch_point_get_test.go @@ -22,10 +22,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" ) @@ -357,9 +357,9 @@ func TestPointGetForTemporaryTable(t *testing.T) { tk.MustQuery("explain format = 'brief' select * from t1 where id in (1, 2, 3)"). Check(testkit.Rows("Batch_Point_Get 3.00 root table:t1 handle:[1 2 3], keep order:false, desc:false")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy")) }() // Batch point get. diff --git a/pkg/executor/benchmark_test.go b/pkg/executor/benchmark_test.go new file mode 100644 index 0000000000000..54b7b5f71027d --- /dev/null +++ b/pkg/executor/benchmark_test.go @@ -0,0 +1,2188 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "encoding/base64" + "fmt" + "math/rand" + "os" + "sort" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/stringutil" + "go.uber.org/zap/zapcore" +) + +var ( + _ exec.Executor = &mockDataSource{} + _ core.PhysicalPlan = &mockDataPhysicalPlan{} + wideString = strings.Repeat("x", 5*1024) +) + +type mockDataSourceParameters struct { + schema *expression.Schema + genDataFunc func(row int, typ *types.FieldType) interface{} + ndvs []int // number of distinct values on columns[i] and zero represents no limit + orders []bool // columns[i] should be ordered if orders[i] is true + rows int // number of rows the DataSource should output + ctx sessionctx.Context +} + +type mockDataSource struct { + exec.BaseExecutor + p mockDataSourceParameters + genData []*chunk.Chunk + chunks []*chunk.Chunk + chunkPtr int +} + +type mockDataPhysicalPlan struct { + MockPhysicalPlan + schema *expression.Schema + exec exec.Executor +} + +func (mp *mockDataPhysicalPlan) GetExecutor() exec.Executor { + return mp.exec +} + +func (mp *mockDataPhysicalPlan) Schema() *expression.Schema { + return mp.schema +} + +func (mp *mockDataPhysicalPlan) ExplainID() fmt.Stringer { + return stringutil.MemoizeStr(func() string { + return "mockData_0" + }) +} + +func (mp *mockDataPhysicalPlan) ID() int { + return 0 +} + +func (mp *mockDataPhysicalPlan) Stats() *property.StatsInfo { + return nil +} + +func (mp *mockDataPhysicalPlan) SelectBlockOffset() int { + return 0 +} + +// MemoryUsage of mockDataPhysicalPlan is only for testing +func (mp *mockDataPhysicalPlan) MemoryUsage() (sum int64) { + return +} + +func buildMockDataPhysicalPlan(ctx sessionctx.Context, srcExec exec.Executor) *mockDataPhysicalPlan { + return &mockDataPhysicalPlan{ + schema: srcExec.Schema(), + exec: srcExec, + } +} + +func (mds *mockDataSource) genColDatums(col int) (results []interface{}) { + typ := mds.RetFieldTypes()[col] + order := false + if col < len(mds.p.orders) { + order = mds.p.orders[col] + } + rows := mds.p.rows + NDV := 0 + if col < len(mds.p.ndvs) { + NDV = mds.p.ndvs[col] + } + results = make([]interface{}, 0, rows) + if NDV == 0 { + if mds.p.genDataFunc == nil { + for i := 0; i < rows; i++ { + results = append(results, mds.randDatum(typ)) + } + } else { + for i := 0; i < rows; i++ { + results = append(results, mds.p.genDataFunc(i, typ)) + } + } + } else { + datumSet := make(map[string]bool, NDV) + datums := make([]interface{}, 0, NDV) + for len(datums) < NDV { + d := mds.randDatum(typ) + str := fmt.Sprintf("%v", d) + if datumSet[str] { + continue + } + datumSet[str] = true + datums = append(datums, d) + } + + for i := 0; i < rows; i++ { + results = append(results, datums[rand.Intn(NDV)]) + } + } + + if order { + sort.Slice(results, func(i, j int) bool { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return results[i].(int64) < results[j].(int64) + case mysql.TypeDouble: + return results[i].(float64) < results[j].(float64) + case mysql.TypeVarString: + return results[i].(string) < results[j].(string) + default: + panic("not implement") + } + }) + } + + return +} + +func (mds *mockDataSource) randDatum(typ *types.FieldType) interface{} { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(rand.Int()) + case mysql.TypeFloat: + return rand.Float32() + case mysql.TypeDouble: + return rand.Float64() + case mysql.TypeNewDecimal: + var d types.MyDecimal + return d.FromInt(int64(rand.Int())) + case mysql.TypeVarString: + buff := make([]byte, 10) + rand.Read(buff) + return base64.RawURLEncoding.EncodeToString(buff) + default: + panic("not implement") + } +} + +func (mds *mockDataSource) prepareChunks() { + mds.chunks = make([]*chunk.Chunk, len(mds.genData)) + for i := range mds.chunks { + mds.chunks[i] = mds.genData[i].CopyConstruct() + } + mds.chunkPtr = 0 +} + +func (mds *mockDataSource) Next(ctx context.Context, req *chunk.Chunk) error { + if mds.chunkPtr >= len(mds.chunks) { + req.Reset() + return nil + } + dataChk := mds.chunks[mds.chunkPtr] + dataChk.SwapColumns(req) + mds.chunkPtr++ + return nil +} + +func buildMockDataSource(opt mockDataSourceParameters) *mockDataSource { + baseExec := exec.NewBaseExecutor(opt.ctx, opt.schema, 0) + m := &mockDataSource{baseExec, opt, nil, nil, 0} + rTypes := exec.RetTypes(m) + colData := make([][]interface{}, len(rTypes)) + for i := 0; i < len(rTypes); i++ { + colData[i] = m.genColDatums(i) + } + + m.genData = make([]*chunk.Chunk, (m.p.rows+m.MaxChunkSize()-1)/m.MaxChunkSize()) + for i := range m.genData { + m.genData[i] = chunk.NewChunkWithCapacity(exec.RetTypes(m), m.MaxChunkSize()) + } + + for i := 0; i < m.p.rows; i++ { + idx := i / m.MaxChunkSize() + retTypes := exec.RetTypes(m) + for colIdx := 0; colIdx < len(rTypes); colIdx++ { + switch retTypes[colIdx].GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + m.genData[idx].AppendInt64(colIdx, colData[colIdx][i].(int64)) + case mysql.TypeFloat: + m.genData[idx].AppendFloat32(colIdx, colData[colIdx][i].(float32)) + case mysql.TypeDouble: + m.genData[idx].AppendFloat64(colIdx, colData[colIdx][i].(float64)) + case mysql.TypeNewDecimal: + m.genData[idx].AppendMyDecimal(colIdx, colData[colIdx][i].(*types.MyDecimal)) + case mysql.TypeVarString: + m.genData[idx].AppendString(colIdx, colData[colIdx][i].(string)) + default: + panic("not implement") + } + } + } + return m +} + +func buildMockDataSourceWithIndex(opt mockDataSourceParameters, index []int) *mockDataSource { + opt.orders = make([]bool, len(opt.schema.Columns)) + for _, idx := range index { + opt.orders[idx] = true + } + return buildMockDataSource(opt) +} + +// aggTestCase has a fixed schema (aggCol Double, groupBy LongLong). +type aggTestCase struct { + execType string // "hash" or "stream" + aggFunc string // sum, avg, count .... + groupByNDV int // the number of distinct group-by keys + hasDistinct bool + rows int + concurrency int + dataSourceSorted bool + ctx sessionctx.Context +} + +func (a aggTestCase) columns() []*expression.Column { + return []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeDouble)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, + } +} + +func (a aggTestCase) String() string { + return fmt.Sprintf("(execType:%v, aggFunc:%v, ndv:%v, hasDistinct:%v, rows:%v, concurrency:%v, sorted:%v)", + a.execType, a.aggFunc, a.groupByNDV, a.hasDistinct, a.rows, a.concurrency, a.dataSourceSorted) +} + +func defaultAggTestCase(exec string) *aggTestCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + return &aggTestCase{exec, ast.AggFuncSum, 1000, false, 10000000, 4, true, ctx} +} + +func buildHashAggExecutor(ctx sessionctx.Context, src exec.Executor, schema *expression.Schema, + aggFuncs []*aggregation.AggFuncDesc, groupItems []expression.Expression) exec.Executor { + plan := new(core.PhysicalHashAgg) + plan.AggFuncs = aggFuncs + plan.GroupByItems = groupItems + plan.SetSchema(schema) + plan.Init(ctx, nil, 0) + plan.SetChildren(nil) + b := newExecutorBuilder(ctx, nil, nil) + exec := b.build(plan) + hashAgg := exec.(*aggregate.HashAggExec) + hashAgg.SetChildren(0, src) + return exec +} + +func buildStreamAggExecutor(ctx sessionctx.Context, srcExec exec.Executor, schema *expression.Schema, + aggFuncs []*aggregation.AggFuncDesc, groupItems []expression.Expression, concurrency int, dataSourceSorted bool) exec.Executor { + src := buildMockDataPhysicalPlan(ctx, srcExec) + + sg := new(core.PhysicalStreamAgg) + sg.AggFuncs = aggFuncs + sg.GroupByItems = groupItems + sg.SetSchema(schema) + sg.Init(ctx, nil, 0) + + var tail core.PhysicalPlan = sg + // if data source is not sorted, we have to attach sort, to make the input of stream-agg sorted + if !dataSourceSorted { + byItems := make([]*util.ByItems, 0, len(sg.GroupByItems)) + for _, col := range sg.GroupByItems { + byItems = append(byItems, &util.ByItems{Expr: col, Desc: false}) + } + sortPP := &core.PhysicalSort{ByItems: byItems} + sortPP.SetChildren(src) + sg.SetChildren(sortPP) + tail = sortPP + } else { + sg.SetChildren(src) + } + + var ( + plan core.PhysicalPlan + splitter core.PartitionSplitterType = core.PartitionHashSplitterType + ) + if concurrency > 1 { + if dataSourceSorted { + splitter = core.PartitionRangeSplitterType + } + plan = core.PhysicalShuffle{ + Concurrency: concurrency, + Tails: []core.PhysicalPlan{tail}, + DataSources: []core.PhysicalPlan{src}, + SplitterType: splitter, + ByItemArrays: [][]expression.Expression{sg.GroupByItems}, + }.Init(ctx, nil, 0) + plan.SetChildren(sg) + } else { + plan = sg + } + + b := newExecutorBuilder(ctx, nil, nil) + return b.build(plan) +} + +func buildAggExecutor(b *testing.B, testCase *aggTestCase, child exec.Executor) exec.Executor { + ctx := testCase.ctx + if testCase.execType == "stream" { + if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBStreamAggConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { + b.Fatal(err) + } + } else { + if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBHashAggFinalConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { + b.Fatal(err) + } + if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBHashAggPartialConcurrency, fmt.Sprintf("%v", testCase.concurrency)); err != nil { + b.Fatal(err) + } + } + + childCols := testCase.columns() + schema := expression.NewSchema(childCols...) + groupBy := []expression.Expression{childCols[1]} + aggFunc, err := aggregation.NewAggFuncDesc(testCase.ctx, testCase.aggFunc, []expression.Expression{childCols[0]}, testCase.hasDistinct) + if err != nil { + b.Fatal(err) + } + aggFuncs := []*aggregation.AggFuncDesc{aggFunc} + + var aggExec exec.Executor + switch testCase.execType { + case "hash": + aggExec = buildHashAggExecutor(testCase.ctx, child, schema, aggFuncs, groupBy) + case "stream": + aggExec = buildStreamAggExecutor(testCase.ctx, child, schema, aggFuncs, groupBy, testCase.concurrency, testCase.dataSourceSorted) + default: + b.Fatal("not implement") + } + return aggExec +} + +func benchmarkAggExecWithCase(b *testing.B, casTest *aggTestCase) { + if err := casTest.ctx.GetSessionVars().SetSystemVar(variable.TiDBStreamAggConcurrency, fmt.Sprintf("%v", casTest.concurrency)); err != nil { + b.Fatal(err) + } + + cols := casTest.columns() + dataSource := buildMockDataSource(mockDataSourceParameters{ + schema: expression.NewSchema(cols...), + ndvs: []int{0, casTest.groupByNDV}, + orders: []bool{false, casTest.dataSourceSorted}, + rows: casTest.rows, + ctx: casTest.ctx, + }) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() // prepare a new agg-executor + aggExec := buildAggExecutor(b, casTest, dataSource) + tmpCtx := context.Background() + chk := exec.NewFirstChunk(aggExec) + dataSource.prepareChunks() + + b.StartTimer() + if err := aggExec.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := aggExec.Next(tmpCtx, chk); err != nil { + b.Fatal(b) + } + if chk.NumRows() == 0 { + break + } + } + + if err := aggExec.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkShuffleStreamAggRows(b *testing.B) { + b.ReportAllocs() + sortTypes := []bool{false, true} + rows := []int{10000, 100000, 1000000, 10000000} + concurrencies := []int{1, 2, 4, 8} + for _, row := range rows { + for _, con := range concurrencies { + for _, sorted := range sortTypes { + cas := defaultAggTestCase("stream") + cas.rows = row + cas.dataSourceSorted = sorted + cas.concurrency = con + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkAggExecWithCase(b, cas) + }) + } + } + } +} + +func BenchmarkHashAggRows(b *testing.B) { + rows := []int{100000, 1000000, 10000000} + concurrencies := []int{1, 4, 8, 15, 20, 30, 40} + for _, row := range rows { + for _, con := range concurrencies { + cas := defaultAggTestCase("hash") + cas.rows = row + cas.concurrency = con + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkAggExecWithCase(b, cas) + }) + } + } +} + +func BenchmarkAggGroupByNDV(b *testing.B) { + NDVs := []int{10, 100, 1000, 10000, 100000, 1000000, 10000000} + for _, NDV := range NDVs { + for _, exec := range []string{"hash", "stream"} { + cas := defaultAggTestCase(exec) + cas.groupByNDV = NDV + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkAggExecWithCase(b, cas) + }) + } + } +} + +func BenchmarkAggConcurrency(b *testing.B) { + concs := []int{1, 4, 8, 15, 20, 30, 40} + for _, con := range concs { + for _, exec := range []string{"hash", "stream"} { + cas := defaultAggTestCase(exec) + cas.concurrency = con + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkAggExecWithCase(b, cas) + }) + } + } +} + +func BenchmarkAggDistinct(b *testing.B) { + rows := []int{100000, 1000000, 10000000} + distincts := []bool{false, true} + for _, row := range rows { + for _, exec := range []string{"hash", "stream"} { + for _, distinct := range distincts { + cas := defaultAggTestCase(exec) + cas.rows = row + cas.hasDistinct = distinct + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkAggExecWithCase(b, cas) + }) + } + } + } +} + +func buildWindowExecutor(ctx sessionctx.Context, windowFunc string, funcs int, frame *core.WindowFrame, srcExec exec.Executor, schema *expression.Schema, partitionBy []*expression.Column, concurrency int, dataSourceSorted bool) exec.Executor { + src := buildMockDataPhysicalPlan(ctx, srcExec) + win := new(core.PhysicalWindow) + win.WindowFuncDescs = make([]*aggregation.WindowFuncDesc, 0) + winSchema := schema.Clone() + for i := 0; i < funcs; i++ { + var args []expression.Expression + switch windowFunc { + case ast.WindowFuncNtile: + args = append(args, &expression.Constant{Value: types.NewUintDatum(2)}) + case ast.WindowFuncNthValue: + args = append(args, partitionBy[0], &expression.Constant{Value: types.NewUintDatum(2)}) + case ast.AggFuncSum: + args = append(args, src.Schema().Columns[0]) + case ast.AggFuncAvg: + args = append(args, src.Schema().Columns[0]) + case ast.AggFuncBitXor: + args = append(args, src.Schema().Columns[0]) + case ast.AggFuncMax, ast.AggFuncMin: + args = append(args, src.Schema().Columns[0]) + default: + args = append(args, partitionBy[0]) + } + desc, _ := aggregation.NewWindowFuncDesc(ctx, windowFunc, args, false) + + win.WindowFuncDescs = append(win.WindowFuncDescs, desc) + winSchema.Append(&expression.Column{ + UniqueID: 10 + (int64)(i), + RetType: types.NewFieldType(mysql.TypeLonglong), + }) + } + for _, col := range partitionBy { + win.PartitionBy = append(win.PartitionBy, property.SortItem{Col: col}) + } + win.Frame = frame + win.OrderBy = nil + + win.SetSchema(winSchema) + win.Init(ctx, nil, 0) + + var tail core.PhysicalPlan = win + if !dataSourceSorted { + byItems := make([]*util.ByItems, 0, len(partitionBy)) + for _, col := range partitionBy { + byItems = append(byItems, &util.ByItems{Expr: col, Desc: false}) + } + sort := &core.PhysicalSort{ByItems: byItems} + sort.SetChildren(src) + win.SetChildren(sort) + tail = sort + } else { + win.SetChildren(src) + } + + var plan core.PhysicalPlan + if concurrency > 1 { + byItems := make([]expression.Expression, 0, len(win.PartitionBy)) + for _, item := range win.PartitionBy { + byItems = append(byItems, item.Col) + } + + plan = core.PhysicalShuffle{ + Concurrency: concurrency, + Tails: []core.PhysicalPlan{tail}, + DataSources: []core.PhysicalPlan{src}, + SplitterType: core.PartitionHashSplitterType, + ByItemArrays: [][]expression.Expression{byItems}, + }.Init(ctx, nil, 0) + plan.SetChildren(win) + } else { + plan = win + } + + b := newExecutorBuilder(ctx, nil, nil) + exec := b.build(plan) + return exec +} + +// windowTestCase has a fixed schema (col Double, partitionBy LongLong, rawData VarString(16), col LongLong). +type windowTestCase struct { + windowFunc string + numFunc int // The number of windowFuncs. Default: 1. + frame *core.WindowFrame + ndv int // the number of distinct group-by keys + rows int + concurrency int + pipelined int + dataSourceSorted bool + ctx sessionctx.Context + rawDataSmall string + columns []*expression.Column // the columns of mock schema +} + +func (a windowTestCase) String() string { + return fmt.Sprintf("(func:%v, aggColType:%s, numFunc:%v, ndv:%v, rows:%v, sorted:%v, concurrency:%v, pipelined:%v)", + a.windowFunc, a.columns[0].RetType, a.numFunc, a.ndv, a.rows, a.dataSourceSorted, a.concurrency, a.pipelined) +} + +func defaultWindowTestCase() *windowTestCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + return &windowTestCase{ast.WindowFuncRowNumber, 1, nil, 1000, 10000000, 1, 0, true, ctx, strings.Repeat("x", 16), + []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeDouble)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 2, RetType: types.NewFieldType(mysql.TypeVarString)}, + {Index: 3, RetType: types.NewFieldType(mysql.TypeLonglong)}, + }} +} + +func benchmarkWindowExecWithCase(b *testing.B, casTest *windowTestCase) { + ctx := casTest.ctx + if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBWindowConcurrency, fmt.Sprintf("%v", casTest.concurrency)); err != nil { + b.Fatal(err) + } + if err := ctx.GetSessionVars().SetSystemVar(variable.TiDBEnablePipelinedWindowFunction, fmt.Sprintf("%v", casTest.pipelined)); err != nil { + b.Fatal(err) + } + + cols := casTest.columns + dataSource := buildMockDataSource(mockDataSourceParameters{ + schema: expression.NewSchema(cols...), + ndvs: []int{0, casTest.ndv, 0, 0}, + orders: []bool{false, casTest.dataSourceSorted, false, false}, + rows: casTest.rows, + ctx: casTest.ctx, + }) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() // prepare a new window-executor + childCols := casTest.columns + schema := expression.NewSchema(childCols...) + windowExec := buildWindowExecutor(casTest.ctx, casTest.windowFunc, casTest.numFunc, casTest.frame, dataSource, schema, childCols[1:2], casTest.concurrency, casTest.dataSourceSorted) + tmpCtx := context.Background() + chk := exec.NewFirstChunk(windowExec) + dataSource.prepareChunks() + + b.StartTimer() + if err := windowExec.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := windowExec.Next(tmpCtx, chk); err != nil { + b.Fatal(b) + } + if chk.NumRows() == 0 { + break + } + } + + if err := windowExec.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func baseBenchmarkWindowRows(b *testing.B, pipelined int) { + b.ReportAllocs() + rows := []int{1000, 100000} + ndvs := []int{1, 10, 1000} + concs := []int{1, 2, 4} + for _, row := range rows { + for _, ndv := range ndvs { + for _, con := range concs { + cas := defaultWindowTestCase() + cas.rows = row + cas.ndv = ndv + cas.concurrency = con + cas.dataSourceSorted = false + cas.windowFunc = ast.WindowFuncRowNumber // cheapest + cas.pipelined = pipelined + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkWindowExecWithCase(b, cas) + }) + } + } + } +} + +func BenchmarkWindowRows(b *testing.B) { + baseBenchmarkWindowRows(b, 0) + baseBenchmarkWindowRows(b, 1) +} + +func baseBenchmarkWindowFunctions(b *testing.B, pipelined int) { + b.ReportAllocs() + windowFuncs := []string{ + // ast.WindowFuncRowNumber, + // ast.WindowFuncRank, + // ast.WindowFuncDenseRank, + // ast.WindowFuncCumeDist, + // ast.WindowFuncPercentRank, + // ast.WindowFuncNtile, + // ast.WindowFuncLead, + ast.WindowFuncLag, + // ast.WindowFuncFirstValue, + // ast.WindowFuncLastValue, + // ast.WindowFuncNthValue, + } + concs := []int{1, 4} + for _, windowFunc := range windowFuncs { + for _, con := range concs { + cas := defaultWindowTestCase() + cas.rows = 100000 + cas.ndv = 1000 + cas.concurrency = con + cas.dataSourceSorted = false + cas.windowFunc = windowFunc + cas.pipelined = pipelined + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkWindowExecWithCase(b, cas) + }) + } + } +} + +func BenchmarkWindowFunctions(b *testing.B) { + baseBenchmarkWindowFunctions(b, 0) + baseBenchmarkWindowFunctions(b, 1) +} + +func baseBenchmarkWindowFunctionsWithFrame(b *testing.B, pipelined int) { + b.ReportAllocs() + windowFuncs := []string{ + ast.WindowFuncRowNumber, + ast.AggFuncBitXor, + } + numFuncs := []int{1, 5} + frames := []*core.WindowFrame{ + {Type: ast.Rows, Start: &core.FrameBound{UnBounded: true}, End: &core.FrameBound{Type: ast.CurrentRow}}, + } + sortTypes := []bool{false, true} + concs := []int{1, 2, 3, 4, 5, 6} + for i, windowFunc := range windowFuncs { + for _, sorted := range sortTypes { + for _, numFunc := range numFuncs { + for _, con := range concs { + cas := defaultWindowTestCase() + cas.rows = 100000 + cas.ndv = 1000 + cas.concurrency = con + cas.dataSourceSorted = sorted + cas.windowFunc = windowFunc + cas.numFunc = numFunc + if i < len(frames) { + cas.frame = frames[i] + } + cas.pipelined = pipelined + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkWindowExecWithCase(b, cas) + }) + } + } + } + } +} + +func BenchmarkWindowFunctionsWithFrame(b *testing.B) { + baseBenchmarkWindowFunctionsWithFrame(b, 0) + baseBenchmarkWindowFunctionsWithFrame(b, 1) +} + +func baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b *testing.B, pipelined int) { + b.ReportAllocs() + windowFunc := ast.AggFuncMax + frame := &core.WindowFrame{Type: ast.Rows, Start: &core.FrameBound{UnBounded: true}, End: &core.FrameBound{UnBounded: true}} + cas := defaultWindowTestCase() + cas.rows = 10000 + cas.ndv = 10 + cas.concurrency = 1 + cas.dataSourceSorted = false + cas.windowFunc = windowFunc + cas.numFunc = 1 + cas.frame = frame + cas.pipelined = pipelined + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkWindowExecWithCase(b, cas) + }) +} + +func BenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b *testing.B) { + baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b, 0) + baseBenchmarkWindowFunctionsAggWindowProcessorAboutFrame(b, 1) +} + +func baseBenchmarkWindowFunctionsWithSlidingWindow(b *testing.B, frameType ast.FrameType, pipelined int) { + b.ReportAllocs() + windowFuncs := []struct { + aggFunc string + aggColTypes byte + }{ + {ast.AggFuncSum, mysql.TypeFloat}, + {ast.AggFuncSum, mysql.TypeNewDecimal}, + {ast.AggFuncCount, mysql.TypeLong}, + {ast.AggFuncAvg, mysql.TypeFloat}, + {ast.AggFuncAvg, mysql.TypeNewDecimal}, + {ast.AggFuncBitXor, mysql.TypeLong}, + {ast.AggFuncMax, mysql.TypeLong}, + {ast.AggFuncMax, mysql.TypeFloat}, + {ast.AggFuncMin, mysql.TypeLong}, + {ast.AggFuncMin, mysql.TypeFloat}, + } + row := 100000 + ndv := 100 + frame := &core.WindowFrame{ + Type: frameType, + Start: &core.FrameBound{Type: ast.Preceding, Num: 10}, + End: &core.FrameBound{Type: ast.Following, Num: 10}, + } + for _, windowFunc := range windowFuncs { + cas := defaultWindowTestCase() + cas.ctx.GetSessionVars().WindowingUseHighPrecision = false + cas.rows = row + cas.ndv = ndv + cas.windowFunc = windowFunc.aggFunc + cas.frame = frame + cas.columns[0].RetType.SetType(windowFunc.aggColTypes) + cas.pipelined = pipelined + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkWindowExecWithCase(b, cas) + }) + } +} + +func BenchmarkWindowFunctionsWithSlidingWindow(b *testing.B) { + baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Rows, 0) + baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Ranges, 0) + baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Rows, 1) + baseBenchmarkWindowFunctionsWithSlidingWindow(b, ast.Ranges, 1) +} + +type hashJoinTestCase struct { + rows int + cols []*types.FieldType + concurrency int + ctx sessionctx.Context + keyIdx []int + joinType core.JoinType + disk bool + useOuterToBuild bool + rawData string + childrenUsedSchema [][]bool +} + +func (tc hashJoinTestCase) columns() []*expression.Column { + ret := make([]*expression.Column, 0) + for i, t := range tc.cols { + column := &expression.Column{Index: i, RetType: t, UniqueID: tc.ctx.GetSessionVars().AllocPlanColumnID()} + ret = append(ret, column) + } + return ret +} + +func (tc hashJoinTestCase) String() string { + return fmt.Sprintf("(rows:%v, cols:%v, concurency:%v, joinKeyIdx: %v, disk:%v)", + tc.rows, tc.cols, tc.concurrency, tc.keyIdx, tc.disk) +} + +func defaultHashJoinTestCase(cols []*types.FieldType, joinType core.JoinType, useOuterToBuild bool) *hashJoinTestCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) + ctx.GetSessionVars().SetIndexLookupJoinConcurrency(4) + tc := &hashJoinTestCase{rows: 100000, concurrency: 4, ctx: ctx, keyIdx: []int{0, 1}, rawData: wideString} + tc.cols = cols + tc.useOuterToBuild = useOuterToBuild + tc.joinType = joinType + return tc +} + +func prepare4HashJoin(testCase *hashJoinTestCase, innerExec, outerExec exec.Executor) *HashJoinExec { + if testCase.useOuterToBuild { + innerExec, outerExec = outerExec, innerExec + } + cols0 := innerExec.Schema().Columns + cols1 := outerExec.Schema().Columns + + joinSchema := expression.NewSchema() + if testCase.childrenUsedSchema != nil { + for i, used := range testCase.childrenUsedSchema[0] { + if used { + joinSchema.Append(cols0[i]) + } + } + for i, used := range testCase.childrenUsedSchema[1] { + if used { + joinSchema.Append(cols1[i]) + } + } + } else { + joinSchema.Append(cols0...) + joinSchema.Append(cols1...) + } + + joinKeysColIdx := make([]int, 0, len(testCase.keyIdx)) + joinKeysColIdx = append(joinKeysColIdx, testCase.keyIdx...) + probeKeysColIdx := make([]int, 0, len(testCase.keyIdx)) + probeKeysColIdx = append(probeKeysColIdx, testCase.keyIdx...) + e := &HashJoinExec{ + BaseExecutor: exec.NewBaseExecutor(testCase.ctx, joinSchema, 5, innerExec, outerExec), + hashJoinCtx: &hashJoinCtx{ + sessCtx: testCase.ctx, + joinType: testCase.joinType, // 0 for InnerJoin, 1 for LeftOutersJoin, 2 for RightOuterJoin + isOuterJoin: false, + useOuterToBuild: testCase.useOuterToBuild, + concurrency: uint(testCase.concurrency), + probeTypes: exec.RetTypes(outerExec), + buildTypes: exec.RetTypes(innerExec), + }, + probeSideTupleFetcher: &probeSideTupleFetcher{ + probeSideExec: outerExec, + }, + probeWorkers: make([]*probeWorker, testCase.concurrency), + buildWorker: &buildWorker{ + buildKeyColIdx: joinKeysColIdx, + buildSideExec: innerExec, + }, + } + + childrenUsedSchema := markChildrenUsedColsForTest(e.Schema(), e.Children(0).Schema(), e.Children(1).Schema()) + defaultValues := make([]types.Datum, e.buildWorker.buildSideExec.Schema().Len()) + lhsTypes, rhsTypes := exec.RetTypes(innerExec), exec.RetTypes(outerExec) + for i := uint(0); i < e.concurrency; i++ { + e.probeWorkers[i] = &probeWorker{ + workerID: i, + hashJoinCtx: e.hashJoinCtx, + joiner: newJoiner(testCase.ctx, e.joinType, true, defaultValues, + nil, lhsTypes, rhsTypes, childrenUsedSchema, false), + probeKeyColIdx: probeKeysColIdx, + } + } + e.buildWorker.hashJoinCtx = e.hashJoinCtx + memLimit := int64(-1) + if testCase.disk { + memLimit = 1 + } + t := memory.NewTracker(-1, memLimit) + t.SetActionOnExceed(nil) + t2 := disk.NewTracker(-1, -1) + e.Ctx().GetSessionVars().MemTracker = t + e.Ctx().GetSessionVars().StmtCtx.MemTracker.AttachTo(t) + e.Ctx().GetSessionVars().DiskTracker = t2 + e.Ctx().GetSessionVars().StmtCtx.DiskTracker.AttachTo(t2) + return e +} + +// markChildrenUsedColsForTest compares each child with the output schema, and mark +// each column of the child is used by output or not. +func markChildrenUsedColsForTest(outputSchema *expression.Schema, childSchemas ...*expression.Schema) (childrenUsed [][]bool) { + childrenUsed = make([][]bool, 0, len(childSchemas)) + markedOffsets := make(map[int]struct{}) + for _, col := range outputSchema.Columns { + markedOffsets[col.Index] = struct{}{} + } + prefixLen := 0 + for _, childSchema := range childSchemas { + used := make([]bool, len(childSchema.Columns)) + for i := range childSchema.Columns { + if _, ok := markedOffsets[prefixLen+i]; ok { + used[i] = true + } + } + childrenUsed = append(childrenUsed, used) + } + for _, child := range childSchemas { + used := expression.GetUsedList(outputSchema.Columns, child) + childrenUsed = append(childrenUsed, used) + } + return +} + +func benchmarkHashJoinExecWithCase(b *testing.B, casTest *hashJoinTestCase) { + opt1 := mockDataSourceParameters{ + rows: casTest.rows, + ctx: casTest.ctx, + genDataFunc: func(row int, typ *types.FieldType) interface{} { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(row) + case mysql.TypeVarString: + return casTest.rawData + case mysql.TypeDouble: + return float64(row) + default: + panic("not implement") + } + }, + } + opt2 := opt1 + opt1.schema = expression.NewSchema(casTest.columns()...) + opt2.schema = expression.NewSchema(casTest.columns()...) + dataSource1 := buildMockDataSource(opt1) + dataSource2 := buildMockDataSource(opt2) + // Test spill result. + benchmarkHashJoinExec(b, casTest, dataSource1, dataSource2, true) + b.ResetTimer() + for i := 0; i < b.N; i++ { + benchmarkHashJoinExec(b, casTest, dataSource1, dataSource2, false) + } +} + +func benchmarkHashJoinExec(b *testing.B, casTest *hashJoinTestCase, opt1, opt2 *mockDataSource, testResult bool) { + b.StopTimer() + executor := prepare4HashJoin(casTest, opt1, opt2) + tmpCtx := context.Background() + chk := exec.NewFirstChunk(executor) + opt1.prepareChunks() + opt2.prepareChunks() + + totalRow := 0 + b.StartTimer() + if err := executor.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := executor.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + totalRow += chk.NumRows() + } + + if testResult { + time.Sleep(200 * time.Millisecond) + if spilled := executor.rowContainer.alreadySpilledSafeForTest(); spilled != casTest.disk { + b.Fatal("wrong usage with disk:", spilled, casTest.disk) + } + } + + if err := executor.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + if totalRow == 0 { + b.Fatal("totalRow == 0") + } +} + +func BenchmarkHashJoinInlineProjection(b *testing.B) { + cols := []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeVarString), + } + + b.ReportAllocs() + + { + cas := defaultHashJoinTestCase(cols, 0, false) + cas.keyIdx = []int{0} + cas.childrenUsedSchema = [][]bool{ + {false, true}, + {false, false}, + } + b.Run("InlineProjection:ON", func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + } + + { + cas := defaultHashJoinTestCase(cols, 0, false) + cas.keyIdx = []int{0} + b.Run("InlineProjection:OFF", func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + } +} + +func BenchmarkHashJoinExec(b *testing.B) { + lvl := log.GetLevel() + log.SetLevel(zapcore.ErrorLevel) + defer log.SetLevel(lvl) + + cols := []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeVarString), + } + + b.ReportAllocs() + cas := defaultHashJoinTestCase(cols, 0, false) + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + cas.disk = true + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + // Replace the wide string column with double column + cols = []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeDouble), + } + + cas = defaultHashJoinTestCase(cols, 0, false) + cas.keyIdx = []int{0} + cas.rows = 5 + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas = defaultHashJoinTestCase(cols, 0, false) + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) +} + +func BenchmarkOuterHashJoinExec(b *testing.B) { + lvl := log.GetLevel() + log.SetLevel(zapcore.ErrorLevel) + defer log.SetLevel(lvl) + + cols := []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeVarString), + } + + b.ReportAllocs() + cas := defaultHashJoinTestCase(cols, 2, true) + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + cas.disk = true + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + // Replace the wide string column with double column + cols = []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeDouble), + } + + cas = defaultHashJoinTestCase(cols, 2, true) + cas.keyIdx = []int{0} + cas.rows = 5 + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas = defaultHashJoinTestCase(cols, 2, true) + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) + + cas.keyIdx = []int{0} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkHashJoinExecWithCase(b, cas) + }) +} + +func benchmarkBuildHashTableForList(b *testing.B, casTest *hashJoinTestCase) { + opt := mockDataSourceParameters{ + schema: expression.NewSchema(casTest.columns()...), + rows: casTest.rows, + ctx: casTest.ctx, + genDataFunc: func(row int, typ *types.FieldType) interface{} { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(row) + case mysql.TypeVarString: + return casTest.rawData + default: + panic("not implement") + } + }, + } + dataSource1 := buildMockDataSource(opt) + dataSource2 := buildMockDataSource(opt) + + dataSource1.prepareChunks() + benchmarkBuildHashTable(b, casTest, dataSource1, dataSource2, true) + b.ResetTimer() + for i := 0; i < b.N; i++ { + benchmarkBuildHashTable(b, casTest, dataSource1, dataSource2, false) + } +} + +func benchmarkBuildHashTable(b *testing.B, casTest *hashJoinTestCase, dataSource1, dataSource2 *mockDataSource, testResult bool) { + b.StopTimer() + exec := prepare4HashJoin(casTest, dataSource1, dataSource2) + tmpCtx := context.Background() + if err := exec.Open(tmpCtx); err != nil { + b.Fatal(err) + } + exec.prepared = true + + innerResultCh := make(chan *chunk.Chunk, len(dataSource1.chunks)) + for _, chk := range dataSource1.chunks { + innerResultCh <- chk + } + close(innerResultCh) + + b.StartTimer() + if err := exec.buildWorker.buildHashTableForList(innerResultCh); err != nil { + b.Fatal(err) + } + + if testResult { + time.Sleep(200 * time.Millisecond) + if exec.rowContainer.alreadySpilledSafeForTest() != casTest.disk { + b.Fatal("wrong usage with disk") + } + } + + if err := exec.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() +} + +func BenchmarkBuildHashTableForList(b *testing.B) { + lvl := log.GetLevel() + log.SetLevel(zapcore.ErrorLevel) + defer log.SetLevel(lvl) + + cols := []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeVarString), + } + + b.ReportAllocs() + cas := defaultHashJoinTestCase(cols, 0, false) + rows := []int{10, 100000} + keyIdxs := [][]int{{0, 1}, {0}} + disks := []bool{false, true} + for _, row := range rows { + for _, keyIdx := range keyIdxs { + for _, disk := range disks { + cas.rows = row + cas.keyIdx = keyIdx + cas.disk = disk + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkBuildHashTableForList(b, cas) + }) + } + } + } +} + +type indexJoinTestCase struct { + outerRows int + innerRows int + concurrency int + ctx sessionctx.Context + outerJoinKeyIdx []int + innerJoinKeyIdx []int + outerHashKeyIdx []int + innerHashKeyIdx []int + innerIdx []int + needOuterSort bool + rawData string +} + +func (tc indexJoinTestCase) columns() []*expression.Column { + return []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeDouble)}, + {Index: 2, RetType: types.NewFieldType(mysql.TypeVarString)}, + } +} + +func defaultIndexJoinTestCase() *indexJoinTestCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().SnapshotTS = 1 + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) + tc := &indexJoinTestCase{ + outerRows: 100000, + innerRows: variable.DefMaxChunkSize * 100, + concurrency: 4, + ctx: ctx, + outerJoinKeyIdx: []int{0, 1}, + innerJoinKeyIdx: []int{0, 1}, + outerHashKeyIdx: []int{0, 1}, + innerHashKeyIdx: []int{0, 1}, + innerIdx: []int{0, 1}, + rawData: wideString, + } + return tc +} + +func (tc indexJoinTestCase) String() string { + return fmt.Sprintf("(outerRows:%v, innerRows:%v, concurency:%v, outerJoinKeyIdx: %v, innerJoinKeyIdx: %v, NeedOuterSort:%v)", + tc.outerRows, tc.innerRows, tc.concurrency, tc.outerJoinKeyIdx, tc.innerJoinKeyIdx, tc.needOuterSort) +} +func (tc indexJoinTestCase) getMockDataSourceOptByRows(rows int) mockDataSourceParameters { + return mockDataSourceParameters{ + schema: expression.NewSchema(tc.columns()...), + rows: rows, + ctx: tc.ctx, + genDataFunc: func(row int, typ *types.FieldType) interface{} { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(row) + case mysql.TypeDouble: + return float64(row) + case mysql.TypeVarString: + return tc.rawData + default: + panic("not implement") + } + }, + } +} + +func prepare4IndexInnerHashJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { + outerCols, innerCols := tc.columns(), tc.columns() + joinSchema := expression.NewSchema(outerCols...) + joinSchema.Append(innerCols...) + leftTypes, rightTypes := exec.RetTypes(outerDS), exec.RetTypes(innerDS) + defaultValues := make([]types.Datum, len(innerCols)) + colLens := make([]int, len(innerCols)) + for i := range colLens { + colLens[i] = types.UnspecifiedLength + } + keyOff2IdxOff := make([]int, len(tc.outerJoinKeyIdx)) + for i := range keyOff2IdxOff { + keyOff2IdxOff[i] = i + } + + readerBuilder, err := newExecutorBuilder(tc.ctx, nil, nil). + newDataReaderBuilder(&mockPhysicalIndexReader{e: innerDS}) + if err != nil { + return nil, err + } + + e := &IndexLookUpJoin{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 1, outerDS), + outerCtx: outerCtx{ + rowTypes: leftTypes, + keyCols: tc.outerJoinKeyIdx, + hashCols: tc.outerHashKeyIdx, + }, + innerCtx: innerCtx{ + readerBuilder: readerBuilder, + rowTypes: rightTypes, + colLens: colLens, + keyCols: tc.innerJoinKeyIdx, + hashCols: tc.innerHashKeyIdx, + }, + workerWg: new(sync.WaitGroup), + joiner: newJoiner(tc.ctx, 0, false, defaultValues, nil, leftTypes, rightTypes, nil, false), + isOuterJoin: false, + keyOff2IdxOff: keyOff2IdxOff, + lastColHelper: nil, + } + e.joinResult = exec.NewFirstChunk(e) + return e, nil +} + +func prepare4IndexOuterHashJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { + e, err := prepare4IndexInnerHashJoin(tc, outerDS, innerDS) + if err != nil { + return nil, err + } + idxHash := &IndexNestedLoopHashJoin{IndexLookUpJoin: *e.(*IndexLookUpJoin)} + concurrency := tc.concurrency + idxHash.joiners = make([]joiner, concurrency) + for i := 0; i < concurrency; i++ { + idxHash.joiners[i] = e.(*IndexLookUpJoin).joiner.Clone() + } + return idxHash, nil +} + +func prepare4IndexMergeJoin(tc *indexJoinTestCase, outerDS *mockDataSource, innerDS *mockDataSource) (exec.Executor, error) { + outerCols, innerCols := tc.columns(), tc.columns() + joinSchema := expression.NewSchema(outerCols...) + joinSchema.Append(innerCols...) + outerJoinKeys := make([]*expression.Column, 0, len(tc.outerJoinKeyIdx)) + innerJoinKeys := make([]*expression.Column, 0, len(tc.innerJoinKeyIdx)) + for _, keyIdx := range tc.outerJoinKeyIdx { + outerJoinKeys = append(outerJoinKeys, outerCols[keyIdx]) + } + for _, keyIdx := range tc.innerJoinKeyIdx { + innerJoinKeys = append(innerJoinKeys, innerCols[keyIdx]) + } + leftTypes, rightTypes := exec.RetTypes(outerDS), exec.RetTypes(innerDS) + defaultValues := make([]types.Datum, len(innerCols)) + colLens := make([]int, len(innerCols)) + for i := range colLens { + colLens[i] = types.UnspecifiedLength + } + keyOff2IdxOff := make([]int, len(outerJoinKeys)) + for i := range keyOff2IdxOff { + keyOff2IdxOff[i] = i + } + + compareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) + outerCompareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) + for i := range outerJoinKeys { + compareFuncs = append(compareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], innerJoinKeys[i])) + outerCompareFuncs = append(outerCompareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], outerJoinKeys[i])) + } + + readerBuilder, err := newExecutorBuilder(tc.ctx, nil, nil). + newDataReaderBuilder(&mockPhysicalIndexReader{e: innerDS}) + if err != nil { + return nil, err + } + + e := &IndexLookUpMergeJoin{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 2, outerDS), + outerMergeCtx: outerMergeCtx{ + rowTypes: leftTypes, + keyCols: tc.outerJoinKeyIdx, + joinKeys: outerJoinKeys, + needOuterSort: tc.needOuterSort, + compareFuncs: outerCompareFuncs, + }, + innerMergeCtx: innerMergeCtx{ + readerBuilder: readerBuilder, + rowTypes: rightTypes, + joinKeys: innerJoinKeys, + colLens: colLens, + keyCols: tc.innerJoinKeyIdx, + compareFuncs: compareFuncs, + }, + workerWg: new(sync.WaitGroup), + isOuterJoin: false, + keyOff2IdxOff: keyOff2IdxOff, + lastColHelper: nil, + } + concurrency := e.Ctx().GetSessionVars().IndexLookupJoinConcurrency() + joiners := make([]joiner, concurrency) + for i := 0; i < concurrency; i++ { + joiners[i] = newJoiner(tc.ctx, 0, false, defaultValues, nil, leftTypes, rightTypes, nil, false) + } + e.joiners = joiners + return e, nil +} + +type indexJoinType int8 + +const ( + indexInnerHashJoin indexJoinType = iota + indexOuterHashJoin + indexMergeJoin +) + +func benchmarkIndexJoinExecWithCase( + b *testing.B, + tc *indexJoinTestCase, + outerDS *mockDataSource, + innerDS *mockDataSource, + execType indexJoinType, +) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + var executor exec.Executor + var err error + switch execType { + case indexInnerHashJoin: + executor, err = prepare4IndexInnerHashJoin(tc, outerDS, innerDS) + case indexOuterHashJoin: + executor, err = prepare4IndexOuterHashJoin(tc, outerDS, innerDS) + case indexMergeJoin: + executor, err = prepare4IndexMergeJoin(tc, outerDS, innerDS) + } + + if err != nil { + b.Fatal(err) + } + + tmpCtx := context.Background() + chk := exec.NewFirstChunk(executor) + outerDS.prepareChunks() + innerDS.prepareChunks() + + b.StartTimer() + if err = executor.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := executor.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + } + + if err := executor.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkIndexJoinExec(b *testing.B) { + lvl := log.GetLevel() + log.SetLevel(zapcore.ErrorLevel) + defer log.SetLevel(lvl) + + b.ReportAllocs() + tc := defaultIndexJoinTestCase() + outerOpt := tc.getMockDataSourceOptByRows(tc.outerRows) + innerOpt := tc.getMockDataSourceOptByRows(tc.innerRows) + outerDS := buildMockDataSourceWithIndex(outerOpt, tc.innerIdx) + innerDS := buildMockDataSourceWithIndex(innerOpt, tc.innerIdx) + + tc.needOuterSort = true + b.Run(fmt.Sprintf("index merge join need outer sort %v", tc), func(b *testing.B) { + benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexMergeJoin) + }) + + tc.needOuterSort = false + b.Run(fmt.Sprintf("index merge join %v", tc), func(b *testing.B) { + benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexMergeJoin) + }) + + b.Run(fmt.Sprintf("index inner hash join %v", tc), func(b *testing.B) { + benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexInnerHashJoin) + }) + + b.Run(fmt.Sprintf("index outer hash join %v", tc), func(b *testing.B) { + benchmarkIndexJoinExecWithCase(b, tc, outerDS, innerDS, indexOuterHashJoin) + }) +} + +type mergeJoinTestCase struct { + indexJoinTestCase + childrenUsedSchema [][]bool +} + +func prepareMergeJoinExec(tc *mergeJoinTestCase, joinSchema *expression.Schema, leftExec, rightExec exec.Executor, defaultValues []types.Datum, + compareFuncs []expression.CompareFunc, innerJoinKeys []*expression.Column, outerJoinKeys []*expression.Column) *MergeJoinExec { + // only benchmark inner join + mergeJoinExec := &MergeJoinExec{ + stmtCtx: tc.ctx.GetSessionVars().StmtCtx, + BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 3, leftExec, rightExec), + compareFuncs: compareFuncs, + isOuterJoin: false, + } + + mergeJoinExec.joiner = newJoiner( + tc.ctx, + 0, + false, + defaultValues, + nil, + exec.RetTypes(leftExec), + exec.RetTypes(rightExec), + tc.childrenUsedSchema, + false, + ) + + mergeJoinExec.innerTable = &mergeJoinTable{ + isInner: true, + childIndex: 1, + joinKeys: innerJoinKeys, + } + + mergeJoinExec.outerTable = &mergeJoinTable{ + childIndex: 0, + filters: nil, + joinKeys: outerJoinKeys, + } + + return mergeJoinExec +} + +func prepare4MergeJoin(tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource, sorted bool, concurrency int) exec.Executor { + outerCols, innerCols := tc.columns(), tc.columns() + + joinSchema := expression.NewSchema() + if tc.childrenUsedSchema != nil { + for i, used := range tc.childrenUsedSchema[0] { + if used { + joinSchema.Append(outerCols[i]) + } + } + for i, used := range tc.childrenUsedSchema[1] { + if used { + joinSchema.Append(innerCols[i]) + } + } + } else { + joinSchema.Append(outerCols...) + joinSchema.Append(innerCols...) + } + + outerJoinKeys := make([]*expression.Column, 0, len(tc.outerJoinKeyIdx)) + innerJoinKeys := make([]*expression.Column, 0, len(tc.innerJoinKeyIdx)) + for _, keyIdx := range tc.outerJoinKeyIdx { + outerJoinKeys = append(outerJoinKeys, outerCols[keyIdx]) + } + for _, keyIdx := range tc.innerJoinKeyIdx { + innerJoinKeys = append(innerJoinKeys, innerCols[keyIdx]) + } + compareFuncs := make([]expression.CompareFunc, 0, len(outerJoinKeys)) + for i := range outerJoinKeys { + compareFuncs = append(compareFuncs, expression.GetCmpFunction(nil, outerJoinKeys[i], innerJoinKeys[i])) + } + + defaultValues := make([]types.Datum, len(innerCols)) + + var leftExec, rightExec exec.Executor + if sorted { + leftSortExec := &SortExec{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, innerDS.Schema(), 3, innerDS), + ByItems: make([]*util.ByItems, 0, len(tc.innerJoinKeyIdx)), + schema: innerDS.Schema(), + } + for _, key := range innerJoinKeys { + leftSortExec.ByItems = append(leftSortExec.ByItems, &util.ByItems{Expr: key}) + } + leftExec = leftSortExec + + rightSortExec := &SortExec{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, outerDS.Schema(), 4, outerDS), + ByItems: make([]*util.ByItems, 0, len(tc.outerJoinKeyIdx)), + schema: outerDS.Schema(), + } + for _, key := range outerJoinKeys { + rightSortExec.ByItems = append(rightSortExec.ByItems, &util.ByItems{Expr: key}) + } + rightExec = rightSortExec + } else { + leftExec = innerDS + rightExec = outerDS + } + + var e exec.Executor + if concurrency == 1 { + e = prepareMergeJoinExec(tc, joinSchema, leftExec, rightExec, defaultValues, compareFuncs, innerJoinKeys, outerJoinKeys) + } else { + // build dataSources + dataSources := []exec.Executor{leftExec, rightExec} + // build splitters + innerByItems := make([]expression.Expression, 0, len(innerJoinKeys)) + for _, innerJoinKey := range innerJoinKeys { + innerByItems = append(innerByItems, innerJoinKey) + } + outerByItems := make([]expression.Expression, 0, len(outerJoinKeys)) + for _, outerJoinKey := range outerJoinKeys { + outerByItems = append(outerByItems, outerJoinKey) + } + splitters := []partitionSplitter{ + &partitionHashSplitter{ + byItems: innerByItems, + numWorkers: concurrency, + }, + &partitionHashSplitter{ + byItems: outerByItems, + numWorkers: concurrency, + }, + } + // build ShuffleMergeJoinExec + shuffle := &ShuffleExec{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, joinSchema, 4), + concurrency: concurrency, + dataSources: dataSources, + splitters: splitters, + } + + // build workers, only benchmark inner join + shuffle.workers = make([]*shuffleWorker, shuffle.concurrency) + for i := range shuffle.workers { + leftReceiver := shuffleReceiver{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, leftExec.Schema(), 0), + } + rightReceiver := shuffleReceiver{ + BaseExecutor: exec.NewBaseExecutor(tc.ctx, rightExec.Schema(), 0), + } + w := &shuffleWorker{ + receivers: []*shuffleReceiver{&leftReceiver, &rightReceiver}, + } + w.childExec = prepareMergeJoinExec(tc, joinSchema, &leftReceiver, &rightReceiver, defaultValues, compareFuncs, innerJoinKeys, outerJoinKeys) + + shuffle.workers[i] = w + } + e = shuffle + } + + return e +} + +func newMergeJoinBenchmark(numOuterRows, numInnerDup, numInnerRedundant int) (tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource) { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().SnapshotTS = 1 + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + ctx.GetSessionVars().StmtCtx.DiskTracker = disk.NewTracker(-1, -1) + + numInnerRows := numOuterRows*numInnerDup + numInnerRedundant + itc := &indexJoinTestCase{ + outerRows: numOuterRows, + innerRows: numInnerRows, + concurrency: 4, + ctx: ctx, + outerJoinKeyIdx: []int{0, 1}, + innerJoinKeyIdx: []int{0, 1}, + outerHashKeyIdx: []int{0, 1}, + innerHashKeyIdx: []int{0, 1}, + innerIdx: []int{0, 1}, + rawData: wideString, + } + tc = &mergeJoinTestCase{*itc, nil} + outerOpt := mockDataSourceParameters{ + schema: expression.NewSchema(tc.columns()...), + rows: numOuterRows, + ctx: tc.ctx, + genDataFunc: func(row int, typ *types.FieldType) interface{} { + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(row) + case mysql.TypeDouble: + return float64(row) + case mysql.TypeVarString: + return tc.rawData + default: + panic("not implement") + } + }, + } + + innerOpt := mockDataSourceParameters{ + schema: expression.NewSchema(tc.columns()...), + rows: numInnerRows, + ctx: tc.ctx, + genDataFunc: func(row int, typ *types.FieldType) interface{} { + row = row / numInnerDup + switch typ.GetType() { + case mysql.TypeLong, mysql.TypeLonglong: + return int64(row) + case mysql.TypeDouble: + return float64(row) + case mysql.TypeVarString: + return tc.rawData + default: + panic("not implement") + } + }, + } + + innerDS = buildMockDataSource(innerOpt) + outerDS = buildMockDataSource(outerOpt) + + return +} + +type mergeJoinType int8 + +const ( + innerMergeJoin mergeJoinType = iota +) + +func benchmarkMergeJoinExecWithCase(b *testing.B, tc *mergeJoinTestCase, innerDS, outerDS *mockDataSource, joinType mergeJoinType) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + var executor exec.Executor + switch joinType { + case innerMergeJoin: + executor = prepare4MergeJoin(tc, innerDS, outerDS, true, 2) + } + + tmpCtx := context.Background() + chk := exec.NewFirstChunk(executor) + outerDS.prepareChunks() + innerDS.prepareChunks() + + b.StartTimer() + if err := executor.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := executor.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + } + + if err := executor.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkMergeJoinExec(b *testing.B) { + lvl := log.GetLevel() + log.SetLevel(zapcore.ErrorLevel) + defer log.SetLevel(lvl) + b.ReportAllocs() + + totalRows := 300000 + + innerDupAndRedundant := [][]int{ + {1, 0}, + {100, 0}, + {10000, 0}, + {1, 30000}, + } + + childrenUsedSchemas := [][][]bool{ + nil, + { + {true, false, false}, + {false, true, false}, + }, + } + + for _, params := range innerDupAndRedundant { + numInnerDup, numInnerRedundant := params[0], params[1] + for _, childrenUsedSchema := range childrenUsedSchemas { + tc, innerDS, outerDS := newMergeJoinBenchmark(totalRows/numInnerDup, numInnerDup, numInnerRedundant) + inlineProj := false + if childrenUsedSchema != nil { + inlineProj = true + tc.childrenUsedSchema = childrenUsedSchema + } + + b.Run(fmt.Sprintf("merge join %v InlineProj:%v", tc, inlineProj), func(b *testing.B) { + benchmarkMergeJoinExecWithCase(b, tc, outerDS, innerDS, innerMergeJoin) + }) + } + } +} + +type sortCase struct { + rows int + orderByIdx []int + ndvs []int + ctx sessionctx.Context +} + +func (tc sortCase) columns() []*expression.Column { + return []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, + } +} + +func (tc sortCase) String() string { + return fmt.Sprintf("(rows:%v, orderBy:%v, ndvs: %v)", tc.rows, tc.orderByIdx, tc.ndvs) +} + +func defaultSortTestCase() *sortCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + tc := &sortCase{rows: 300000, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx} + return tc +} + +func sortTestCaseWithMemoryLimit(bytesLimit int64) *sortCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().MemTracker = memory.NewTracker(-1, bytesLimit) + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, bytesLimit) + ctx.GetSessionVars().StmtCtx.MemTracker.AttachTo(ctx.GetSessionVars().MemTracker) + tc := &sortCase{rows: 300000, orderByIdx: []int{0, 1}, ndvs: []int{0, 0}, ctx: ctx} + return tc +} + +func benchmarkSortExec(b *testing.B, cas *sortCase) { + opt := mockDataSourceParameters{ + schema: expression.NewSchema(cas.columns()...), + rows: cas.rows, + ctx: cas.ctx, + ndvs: cas.ndvs, + } + dataSource := buildMockDataSource(opt) + executor := &SortExec{ + BaseExecutor: exec.NewBaseExecutor(cas.ctx, dataSource.Schema(), 4, dataSource), + ByItems: make([]*util.ByItems, 0, len(cas.orderByIdx)), + schema: dataSource.Schema(), + } + for _, idx := range cas.orderByIdx { + executor.ByItems = append(executor.ByItems, &util.ByItems{Expr: cas.columns()[idx]}) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + tmpCtx := context.Background() + chk := exec.NewFirstChunk(executor) + dataSource.prepareChunks() + + b.StartTimer() + if err := executor.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := executor.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + } + + if err := executor.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkSortExec(b *testing.B) { + b.ReportAllocs() + cas := defaultSortTestCase() + benchmarkSortExecDerivateCases(b, cas) +} + +func BenchmarkSortExecSpillToDisk(b *testing.B) { + enableTmpStorageOnOOMCurrentVal := variable.EnableTmpStorageOnOOM.Load() + variable.EnableTmpStorageOnOOM.Store(true) + defer variable.EnableTmpStorageOnOOM.Store(enableTmpStorageOnOOMCurrentVal) + + b.ReportAllocs() + cas := sortTestCaseWithMemoryLimit(1) + benchmarkSortExecDerivateCases(b, cas) +} + +func benchmarkSortExecDerivateCases(b *testing.B, cas *sortCase) { + cas.ndvs = []int{0, 0} + cas.orderByIdx = []int{0, 1} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkSortExec(b, cas) + }) + + ndvs := []int{1, 10000} + for _, ndv := range ndvs { + cas.ndvs = []int{ndv, 0} + cas.orderByIdx = []int{0, 1} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkSortExec(b, cas) + }) + + cas.ndvs = []int{ndv, 0} + cas.orderByIdx = []int{0} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkSortExec(b, cas) + }) + + cas.ndvs = []int{ndv, 0} + cas.orderByIdx = []int{1} + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkSortExec(b, cas) + }) + } +} + +type limitCase struct { + rows int + offset int + count int + childUsedSchema []bool + usingInlineProjection bool + ctx sessionctx.Context +} + +func (tc limitCase) columns() []*expression.Column { + return []*expression.Column{ + {Index: 0, RetType: types.NewFieldType(mysql.TypeLonglong)}, + {Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)}, + } +} + +func (tc limitCase) String() string { + return fmt.Sprintf("(rows:%v, offset:%v, count:%v, inline_projection:%v)", + tc.rows, tc.offset, tc.count, tc.usingInlineProjection) +} + +func defaultLimitTestCase() *limitCase { + ctx := mock.NewContext() + ctx.GetSessionVars().InitChunkSize = variable.DefInitChunkSize + ctx.GetSessionVars().MaxChunkSize = variable.DefMaxChunkSize + ctx.GetSessionVars().StmtCtx.MemTracker = memory.NewTracker(-1, -1) + tc := &limitCase{ + rows: 30000, + offset: 10000, + count: 10000, + childUsedSchema: []bool{false, true}, + usingInlineProjection: false, + ctx: ctx, + } + return tc +} + +func benchmarkLimitExec(b *testing.B, cas *limitCase) { + opt := mockDataSourceParameters{ + schema: expression.NewSchema(cas.columns()...), + rows: cas.rows, + ctx: cas.ctx, + } + dataSource := buildMockDataSource(opt) + var exe exec.Executor + limit := &LimitExec{ + BaseExecutor: exec.NewBaseExecutor(cas.ctx, dataSource.Schema(), 4, dataSource), + begin: uint64(cas.offset), + end: uint64(cas.offset + cas.count), + } + if cas.usingInlineProjection { + if len(cas.childUsedSchema) > 0 { + limit.columnIdxsUsedByChild = make([]int, 0, len(cas.childUsedSchema)) + for i, used := range cas.childUsedSchema { + if used { + limit.columnIdxsUsedByChild = append(limit.columnIdxsUsedByChild, i) + } + } + } + exe = limit + } else { + columns := cas.columns() + usedCols := make([]*expression.Column, 0, len(columns)) + exprs := make([]expression.Expression, 0, len(columns)) + for i, used := range cas.childUsedSchema { + if used { + usedCols = append(usedCols, columns[i]) + exprs = append(exprs, columns[i]) + } + } + proj := &ProjectionExec{ + BaseExecutor: exec.NewBaseExecutor(cas.ctx, expression.NewSchema(usedCols...), 0, limit), + numWorkers: 1, + evaluatorSuit: expression.NewEvaluatorSuite(exprs, false), + } + exe = proj + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + tmpCtx := context.Background() + chk := exec.NewFirstChunk(exe) + dataSource.prepareChunks() + + b.StartTimer() + if err := exe.Open(tmpCtx); err != nil { + b.Fatal(err) + } + for { + if err := exe.Next(tmpCtx, chk); err != nil { + b.Fatal(err) + } + if chk.NumRows() == 0 { + break + } + } + + if err := exe.Close(); err != nil { + b.Fatal(err) + } + b.StopTimer() + } +} + +func BenchmarkLimitExec(b *testing.B) { + b.ReportAllocs() + cas := defaultLimitTestCase() + usingInlineProjection := []bool{false, true} + for _, inlineProjection := range usingInlineProjection { + cas.usingInlineProjection = inlineProjection + b.Run(fmt.Sprintf("%v", cas), func(b *testing.B) { + benchmarkLimitExec(b, cas) + }) + } +} + +func BenchmarkReadLastLinesOfHugeLine(b *testing.B) { + // step 1. initial a huge line log file + hugeLine := make([]byte, 1024*1024*10) + for i := range hugeLine { + hugeLine[i] = 'a' + byte(i%26) + } + fileName := "tidb.log" + err := os.WriteFile(fileName, hugeLine, 0644) + if err != nil { + b.Fatal(err) + } + file, err := os.OpenFile(fileName, os.O_RDONLY, os.ModePerm) + if err != nil { + b.Fatal(err) + } + defer func() { + file.Close() + os.Remove(fileName) + }() + stat, _ := file.Stat() + filesize := stat.Size() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, n, err := readLastLines(context.Background(), file, filesize) + if err != nil { + b.Fatal(err) + } + if n != len(hugeLine) { + b.Fatalf("len %v, expected: %v", n, len(hugeLine)) + } + } +} + +func BenchmarkAggPartialResultMapperMemoryUsage(b *testing.B) { + b.ReportAllocs() + type testCase struct { + rowNum int + } + cases := []testCase{ + { + rowNum: 0, + }, + { + rowNum: 100, + }, + { + rowNum: 10000, + }, + { + rowNum: 1000000, + }, + { + rowNum: 851968, // 6.5 * (1 << 17) + }, + { + rowNum: 851969, // 6.5 * (1 << 17) + 1 + }, + { + rowNum: 425984, // 6.5 * (1 << 16) + }, + { + rowNum: 425985, // 6.5 * (1 << 16) + 1 + }, + } + + for _, c := range cases { + b.Run(fmt.Sprintf("MapRows %v", c.rowNum), func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + aggMap := make(aggregate.AggPartialResultMapper) + tempSlice := make([]aggfuncs.PartialResult, 10) + for num := 0; num < c.rowNum; num++ { + aggMap[strconv.Itoa(num)] = tempSlice + } + } + }) + } +} + +func BenchmarkPipelinedRowNumberWindowFunctionExecution(b *testing.B) { + b.ReportAllocs() +} diff --git a/executor/bind.go b/pkg/executor/bind.go similarity index 95% rename from executor/bind.go rename to pkg/executor/bind.go index 56cb6e15ef526..d4d30e74a1362 100644 --- a/executor/bind.go +++ b/pkg/executor/bind.go @@ -18,12 +18,12 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/parser/ast" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/parser/ast" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/util/chunk" ) // SQLBindExec represents a bind executor. diff --git a/executor/brie.go b/pkg/executor/brie.go similarity index 95% rename from executor/brie.go rename to pkg/executor/brie.go index 1ef1f3abadca9..44e91899795c9 100644 --- a/executor/brie.go +++ b/pkg/executor/brie.go @@ -32,28 +32,28 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/task" "github.com/pingcap/tidb/br/pkg/task/show" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/printer" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" - filter "github.com/pingcap/tidb/util/table-filter" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/printer" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/tikv/client-go/v2/oracle" pd "github.com/tikv/pd/client" "go.uber.org/zap" diff --git a/executor/brie_test.go b/pkg/executor/brie_test.go similarity index 91% rename from executor/brie_test.go rename to pkg/executor/brie_test.go index 41919d6e3a060..0fbe566d02422 100644 --- a/executor/brie_test.go +++ b/pkg/executor/brie_test.go @@ -22,16 +22,16 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/builder.go b/pkg/executor/builder.go new file mode 100644 index 0000000000000..b20006c5f5267 --- /dev/null +++ b/pkg/executor/builder.go @@ -0,0 +1,5481 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "cmp" + "context" + "fmt" + "math" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/diagnosticspb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/calibrateresource" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/pdhelper" + "github.com/pingcap/tidb/pkg/executor/internal/querywatch" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/executor/lockstats" + executor_metrics "github.com/pingcap/tidb/pkg/executor/metrics" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + plannerutil "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/cteutil" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tipb/go-tipb" + clientkv "github.com/tikv/client-go/v2/kv" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/txnkv" + "github.com/tikv/client-go/v2/txnkv/txnsnapshot" + clientutil "github.com/tikv/client-go/v2/util" +) + +// executorBuilder builds an Executor from a Plan. +// The InfoSchema must not change during execution. +type executorBuilder struct { + ctx sessionctx.Context + is infoschema.InfoSchema + err error // err is set when there is error happened during Executor building process. + hasLock bool + Ti *TelemetryInfo + // isStaleness means whether this statement use stale read. + isStaleness bool + txnScope string + readReplicaScope string + inUpdateStmt bool + inDeleteStmt bool + inInsertStmt bool + inSelectLockStmt bool + + // forDataReaderBuilder indicates whether the builder is used by a dataReaderBuilder. + // When forDataReader is true, the builder should use the dataReaderTS as the executor read ts. This is because + // dataReaderBuilder can be used in concurrent goroutines, so we must ensure that getting the ts should be thread safe and + // can return a correct value even if the session context has already been destroyed + forDataReaderBuilder bool + dataReaderTS uint64 + + // Used when building MPPGather. + encounterUnionScan bool +} + +// CTEStorages stores resTbl and iterInTbl for CTEExec. +// There will be a map[CTEStorageID]*CTEStorages in StmtCtx, +// which will store all CTEStorages to make all shared CTEs use same the CTEStorages. +type CTEStorages struct { + ResTbl cteutil.Storage + IterInTbl cteutil.Storage + Producer *cteProducer +} + +func newExecutorBuilder(ctx sessionctx.Context, is infoschema.InfoSchema, ti *TelemetryInfo) *executorBuilder { + txnManager := sessiontxn.GetTxnManager(ctx) + return &executorBuilder{ + ctx: ctx, + is: is, + Ti: ti, + isStaleness: staleread.IsStmtStaleness(ctx), + txnScope: txnManager.GetTxnScope(), + readReplicaScope: txnManager.GetReadReplicaScope(), + } +} + +// MockPhysicalPlan is used to return a specified executor in when build. +// It is mainly used for testing. +type MockPhysicalPlan interface { + plannercore.PhysicalPlan + GetExecutor() exec.Executor +} + +// MockExecutorBuilder is a wrapper for executorBuilder. +// ONLY used in test. +type MockExecutorBuilder struct { + *executorBuilder +} + +// NewMockExecutorBuilderForTest is ONLY used in test. +func NewMockExecutorBuilderForTest(ctx sessionctx.Context, is infoschema.InfoSchema, ti *TelemetryInfo) *MockExecutorBuilder { + return &MockExecutorBuilder{ + executorBuilder: newExecutorBuilder(ctx, is, ti)} +} + +// Build builds an executor tree according to `p`. +func (b *MockExecutorBuilder) Build(p plannercore.Plan) exec.Executor { + return b.build(p) +} + +func (b *executorBuilder) build(p plannercore.Plan) exec.Executor { + switch v := p.(type) { + case nil: + return nil + case *plannercore.Change: + return b.buildChange(v) + case *plannercore.CheckTable: + return b.buildCheckTable(v) + case *plannercore.RecoverIndex: + return b.buildRecoverIndex(v) + case *plannercore.CleanupIndex: + return b.buildCleanupIndex(v) + case *plannercore.CheckIndexRange: + return b.buildCheckIndexRange(v) + case *plannercore.ChecksumTable: + return b.buildChecksumTable(v) + case *plannercore.ReloadExprPushdownBlacklist: + return b.buildReloadExprPushdownBlacklist(v) + case *plannercore.ReloadOptRuleBlacklist: + return b.buildReloadOptRuleBlacklist(v) + case *plannercore.AdminPlugins: + return b.buildAdminPlugins(v) + case *plannercore.DDL: + return b.buildDDL(v) + case *plannercore.Deallocate: + return b.buildDeallocate(v) + case *plannercore.Delete: + return b.buildDelete(v) + case *plannercore.Execute: + return b.buildExecute(v) + case *plannercore.Trace: + return b.buildTrace(v) + case *plannercore.Explain: + return b.buildExplain(v) + case *plannercore.PointGetPlan: + return b.buildPointGet(v) + case *plannercore.BatchPointGetPlan: + return b.buildBatchPointGet(v) + case *plannercore.Insert: + return b.buildInsert(v) + case *plannercore.ImportInto: + return b.buildImportInto(v) + case *plannercore.LoadData: + return b.buildLoadData(v) + case *plannercore.LoadStats: + return b.buildLoadStats(v) + case *plannercore.LockStats: + return b.buildLockStats(v) + case *plannercore.UnlockStats: + return b.buildUnlockStats(v) + case *plannercore.IndexAdvise: + return b.buildIndexAdvise(v) + case *plannercore.PlanReplayer: + return b.buildPlanReplayer(v) + case *plannercore.PhysicalLimit: + return b.buildLimit(v) + case *plannercore.Prepare: + return b.buildPrepare(v) + case *plannercore.PhysicalLock: + return b.buildSelectLock(v) + case *plannercore.CancelDDLJobs: + return b.buildCancelDDLJobs(v) + case *plannercore.PauseDDLJobs: + return b.buildPauseDDLJobs(v) + case *plannercore.ResumeDDLJobs: + return b.buildResumeDDLJobs(v) + case *plannercore.ShowNextRowID: + return b.buildShowNextRowID(v) + case *plannercore.ShowDDL: + return b.buildShowDDL(v) + case *plannercore.PhysicalShowDDLJobs: + return b.buildShowDDLJobs(v) + case *plannercore.ShowDDLJobQueries: + return b.buildShowDDLJobQueries(v) + case *plannercore.ShowDDLJobQueriesWithRange: + return b.buildShowDDLJobQueriesWithRange(v) + case *plannercore.ShowSlow: + return b.buildShowSlow(v) + case *plannercore.PhysicalShow: + return b.buildShow(v) + case *plannercore.Simple: + return b.buildSimple(v) + case *plannercore.PhysicalSimpleWrapper: + return b.buildSimple(&v.Inner) + case *plannercore.Set: + return b.buildSet(v) + case *plannercore.SetConfig: + return b.buildSetConfig(v) + case *plannercore.PhysicalSort: + return b.buildSort(v) + case *plannercore.PhysicalTopN: + return b.buildTopN(v) + case *plannercore.PhysicalUnionAll: + return b.buildUnionAll(v) + case *plannercore.Update: + return b.buildUpdate(v) + case *plannercore.PhysicalUnionScan: + return b.buildUnionScanExec(v) + case *plannercore.PhysicalHashJoin: + return b.buildHashJoin(v) + case *plannercore.PhysicalMergeJoin: + return b.buildMergeJoin(v) + case *plannercore.PhysicalIndexJoin: + return b.buildIndexLookUpJoin(v) + case *plannercore.PhysicalIndexMergeJoin: + return b.buildIndexLookUpMergeJoin(v) + case *plannercore.PhysicalIndexHashJoin: + return b.buildIndexNestedLoopHashJoin(v) + case *plannercore.PhysicalSelection: + return b.buildSelection(v) + case *plannercore.PhysicalHashAgg: + return b.buildHashAgg(v) + case *plannercore.PhysicalStreamAgg: + return b.buildStreamAgg(v) + case *plannercore.PhysicalProjection: + return b.buildProjection(v) + case *plannercore.PhysicalMemTable: + return b.buildMemTable(v) + case *plannercore.PhysicalTableDual: + return b.buildTableDual(v) + case *plannercore.PhysicalApply: + return b.buildApply(v) + case *plannercore.PhysicalMaxOneRow: + return b.buildMaxOneRow(v) + case *plannercore.Analyze: + return b.buildAnalyze(v) + case *plannercore.PhysicalTableReader: + return b.buildTableReader(v) + case *plannercore.PhysicalTableSample: + return b.buildTableSample(v) + case *plannercore.PhysicalIndexReader: + return b.buildIndexReader(v) + case *plannercore.PhysicalIndexLookUpReader: + return b.buildIndexLookUpReader(v) + case *plannercore.PhysicalWindow: + return b.buildWindow(v) + case *plannercore.PhysicalShuffle: + return b.buildShuffle(v) + case *plannercore.PhysicalShuffleReceiverStub: + return b.buildShuffleReceiverStub(v) + case *plannercore.SQLBindPlan: + return b.buildSQLBindExec(v) + case *plannercore.SplitRegion: + return b.buildSplitRegion(v) + case *plannercore.PhysicalIndexMergeReader: + return b.buildIndexMergeReader(v) + case *plannercore.SelectInto: + return b.buildSelectInto(v) + case *plannercore.AdminShowTelemetry: + return b.buildAdminShowTelemetry(v) + case *plannercore.AdminResetTelemetryID: + return b.buildAdminResetTelemetryID(v) + case *plannercore.PhysicalCTE: + return b.buildCTE(v) + case *plannercore.PhysicalCTETable: + return b.buildCTETableReader(v) + case *plannercore.CompactTable: + return b.buildCompactTable(v) + default: + if mp, ok := p.(MockPhysicalPlan); ok { + return mp.GetExecutor() + } + + b.err = exeerrors.ErrUnknownPlan.GenWithStack("Unknown Plan %T", p) + return nil + } +} + +func (b *executorBuilder) buildCancelDDLJobs(v *plannercore.CancelDDLJobs) exec.Executor { + e := &CancelDDLJobsExec{ + CommandDDLJobsExec: &CommandDDLJobsExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + jobIDs: v.JobIDs, + execute: ddl.CancelJobs, + }, + } + return e +} + +func (b *executorBuilder) buildPauseDDLJobs(v *plannercore.PauseDDLJobs) exec.Executor { + e := &PauseDDLJobsExec{ + CommandDDLJobsExec: &CommandDDLJobsExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + jobIDs: v.JobIDs, + execute: ddl.PauseJobs, + }, + } + return e +} + +func (b *executorBuilder) buildResumeDDLJobs(v *plannercore.ResumeDDLJobs) exec.Executor { + e := &ResumeDDLJobsExec{ + CommandDDLJobsExec: &CommandDDLJobsExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + jobIDs: v.JobIDs, + execute: ddl.ResumeJobs, + }, + } + return e +} + +func (b *executorBuilder) buildChange(v *plannercore.Change) exec.Executor { + return &ChangeExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + ChangeStmt: v.ChangeStmt, + } +} + +func (b *executorBuilder) buildShowNextRowID(v *plannercore.ShowNextRowID) exec.Executor { + e := &ShowNextRowIDExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + tblName: v.TableName, + } + return e +} + +func (b *executorBuilder) buildShowDDL(v *plannercore.ShowDDL) exec.Executor { + // We get Info here because for Executors that returns result set, + // next will be called after transaction has been committed. + // We need the transaction to get Info. + e := &ShowDDLExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + } + + var err error + ownerManager := domain.GetDomain(e.Ctx()).DDL().OwnerManager() + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + e.ddlOwnerID, err = ownerManager.GetOwnerID(ctx) + cancel() + if err != nil { + b.err = err + return nil + } + + session, err := e.GetSysSession() + if err != nil { + b.err = err + return nil + } + ddlInfo, err := ddl.GetDDLInfoWithNewTxn(session) + e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) + if err != nil { + b.err = err + return nil + } + e.ddlInfo = ddlInfo + e.selfID = ownerManager.ID() + return e +} + +func (b *executorBuilder) buildShowDDLJobs(v *plannercore.PhysicalShowDDLJobs) exec.Executor { + loc := b.ctx.GetSessionVars().Location() + ddlJobRetriever := DDLJobRetriever{TZLoc: loc} + e := &ShowDDLJobsExec{ + jobNumber: int(v.JobNumber), + is: b.is, + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + DDLJobRetriever: ddlJobRetriever, + } + return e +} + +func (b *executorBuilder) buildShowDDLJobQueries(v *plannercore.ShowDDLJobQueries) exec.Executor { + e := &ShowDDLJobQueriesExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + jobIDs: v.JobIDs, + } + return e +} + +func (b *executorBuilder) buildShowDDLJobQueriesWithRange(v *plannercore.ShowDDLJobQueriesWithRange) exec.Executor { + e := &ShowDDLJobQueriesWithRangeExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + offset: v.Offset, + limit: v.Limit, + } + return e +} + +func (b *executorBuilder) buildShowSlow(v *plannercore.ShowSlow) exec.Executor { + e := &ShowSlowExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + ShowSlow: v.ShowSlow, + } + return e +} + +// buildIndexLookUpChecker builds check information to IndexLookUpReader. +func buildIndexLookUpChecker(b *executorBuilder, p *plannercore.PhysicalIndexLookUpReader, + e *IndexLookUpExecutor) { + is := p.IndexPlans[0].(*plannercore.PhysicalIndexScan) + fullColLen := len(is.Index.Columns) + len(p.CommonHandleCols) + if !e.isCommonHandle() { + fullColLen++ + } + e.dagPB.OutputOffsets = make([]uint32, fullColLen) + for i := 0; i < fullColLen; i++ { + e.dagPB.OutputOffsets[i] = uint32(i) + } + + ts := p.TablePlans[0].(*plannercore.PhysicalTableScan) + e.handleIdx = ts.HandleIdx + + e.ranges = ranger.FullRange() + + tps := make([]*types.FieldType, 0, fullColLen) + for _, col := range is.Columns { + // tps is used to decode the index, we should use the element type of the array if any. + tps = append(tps, col.FieldType.ArrayType()) + } + + if !e.isCommonHandle() { + tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) + } + + e.checkIndexValue = &checkIndexValue{idxColTps: tps} + + colNames := make([]string, 0, len(is.IdxCols)) + for i := range is.IdxCols { + colNames = append(colNames, is.Columns[i].Name.L) + } + if cols, missingColOffset := table.FindColumns(e.table.Cols(), colNames, true); missingColOffset >= 0 { + b.err = plannercore.ErrUnknownColumn.GenWithStack("Unknown column %s", is.Columns[missingColOffset].Name.O) + } else { + e.idxTblCols = cols + } +} + +func (b *executorBuilder) buildCheckTable(v *plannercore.CheckTable) exec.Executor { + noMVIndexOrPrefixIndex := true + for _, idx := range v.IndexInfos { + if idx.MVIndex { + noMVIndexOrPrefixIndex = false + break + } + for _, col := range idx.Columns { + if col.Length != types.UnspecifiedLength { + noMVIndexOrPrefixIndex = false + break + } + } + if !noMVIndexOrPrefixIndex { + break + } + } + if b.ctx.GetSessionVars().FastCheckTable && noMVIndexOrPrefixIndex { + e := &FastCheckTableExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dbName: v.DBName, + table: v.Table, + indexInfos: v.IndexInfos, + is: b.is, + err: &atomic.Pointer[error]{}, + } + return e + } + + readerExecs := make([]*IndexLookUpExecutor, 0, len(v.IndexLookUpReaders)) + for _, readerPlan := range v.IndexLookUpReaders { + readerExec, err := buildNoRangeIndexLookUpReader(b, readerPlan) + if err != nil { + b.err = errors.Trace(err) + return nil + } + buildIndexLookUpChecker(b, readerPlan, readerExec) + + readerExecs = append(readerExecs, readerExec) + } + + e := &CheckTableExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dbName: v.DBName, + table: v.Table, + indexInfos: v.IndexInfos, + is: b.is, + srcs: readerExecs, + exitCh: make(chan struct{}), + retCh: make(chan error, len(readerExecs)), + checkIndex: v.CheckIndex, + } + return e +} + +func buildIdxColsConcatHandleCols(tblInfo *model.TableInfo, indexInfo *model.IndexInfo, hasGenedCol bool) []*model.ColumnInfo { + var pkCols []*model.IndexColumn + if tblInfo.IsCommonHandle { + pkIdx := tables.FindPrimaryIndex(tblInfo) + pkCols = pkIdx.Columns + } + + columns := make([]*model.ColumnInfo, 0, len(indexInfo.Columns)+len(pkCols)) + if hasGenedCol { + columns = tblInfo.Columns + } else { + for _, idxCol := range indexInfo.Columns { + if tblInfo.PKIsHandle && tblInfo.GetPkColInfo().Offset == idxCol.Offset { + continue + } + columns = append(columns, tblInfo.Columns[idxCol.Offset]) + } + } + + if tblInfo.IsCommonHandle { + for _, c := range pkCols { + columns = append(columns, tblInfo.Columns[c.Offset]) + } + return columns + } + if tblInfo.PKIsHandle { + columns = append(columns, tblInfo.Columns[tblInfo.GetPkColInfo().Offset]) + return columns + } + handleOffset := len(columns) + handleColsInfo := &model.ColumnInfo{ + ID: model.ExtraHandleID, + Name: model.ExtraHandleName, + Offset: handleOffset, + } + handleColsInfo.FieldType = *types.NewFieldType(mysql.TypeLonglong) + columns = append(columns, handleColsInfo) + return columns +} + +func (b *executorBuilder) buildRecoverIndex(v *plannercore.RecoverIndex) exec.Executor { + tblInfo := v.Table.TableInfo + t, err := b.is.TableByName(v.Table.Schema, tblInfo.Name) + if err != nil { + b.err = err + return nil + } + idxName := strings.ToLower(v.IndexName) + index := tables.GetWritableIndexByName(idxName, t) + if index == nil { + b.err = errors.Errorf("secondary index `%v` is not found in table `%v`", v.IndexName, v.Table.Name.O) + return nil + } + var hasGenedCol bool + for _, iCol := range index.Meta().Columns { + if tblInfo.Columns[iCol.Offset].IsGenerated() { + hasGenedCol = true + } + } + cols := buildIdxColsConcatHandleCols(tblInfo, index.Meta(), hasGenedCol) + e := &RecoverIndexExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + columns: cols, + containsGenedCol: hasGenedCol, + index: index, + table: t, + physicalID: t.Meta().ID, + } + sessCtx := e.Ctx().GetSessionVars().StmtCtx + e.handleCols = buildHandleColsForExec(sessCtx, tblInfo, index.Meta(), e.columns) + return e +} + +func buildHandleColsForExec(sctx *stmtctx.StatementContext, tblInfo *model.TableInfo, + idxInfo *model.IndexInfo, allColInfo []*model.ColumnInfo) plannercore.HandleCols { + if !tblInfo.IsCommonHandle { + extraColPos := len(allColInfo) - 1 + intCol := &expression.Column{ + Index: extraColPos, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + return plannercore.NewIntHandleCols(intCol) + } + tblCols := make([]*expression.Column, len(tblInfo.Columns)) + for i := 0; i < len(tblInfo.Columns); i++ { + c := tblInfo.Columns[i] + tblCols[i] = &expression.Column{ + RetType: &c.FieldType, + ID: c.ID, + } + } + pkIdx := tables.FindPrimaryIndex(tblInfo) + for i, c := range pkIdx.Columns { + tblCols[c.Offset].Index = len(idxInfo.Columns) + i + } + return plannercore.NewCommonHandleCols(sctx, tblInfo, pkIdx, tblCols) +} + +func (b *executorBuilder) buildCleanupIndex(v *plannercore.CleanupIndex) exec.Executor { + tblInfo := v.Table.TableInfo + t, err := b.is.TableByName(v.Table.Schema, tblInfo.Name) + if err != nil { + b.err = err + return nil + } + idxName := strings.ToLower(v.IndexName) + var index table.Index + for _, idx := range t.Indices() { + if idx.Meta().State != model.StatePublic { + continue + } + if idxName == idx.Meta().Name.L { + index = idx + break + } + } + + if index == nil { + b.err = errors.Errorf("secondary index `%v` is not found in table `%v`", v.IndexName, v.Table.Name.O) + return nil + } + e := &CleanupIndexExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + columns: buildIdxColsConcatHandleCols(tblInfo, index.Meta(), false), + index: index, + table: t, + physicalID: t.Meta().ID, + batchSize: 20000, + } + sessCtx := e.Ctx().GetSessionVars().StmtCtx + e.handleCols = buildHandleColsForExec(sessCtx, tblInfo, index.Meta(), e.columns) + return e +} + +func (b *executorBuilder) buildCheckIndexRange(v *plannercore.CheckIndexRange) exec.Executor { + tb, err := b.is.TableByName(v.Table.Schema, v.Table.Name) + if err != nil { + b.err = err + return nil + } + e := &CheckIndexRangeExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + handleRanges: v.HandleRanges, + table: tb.Meta(), + is: b.is, + } + idxName := strings.ToLower(v.IndexName) + for _, idx := range tb.Indices() { + if idx.Meta().Name.L == idxName { + e.index = idx.Meta() + e.startKey = make([]types.Datum, len(e.index.Columns)) + break + } + } + return e +} + +func (b *executorBuilder) buildChecksumTable(v *plannercore.ChecksumTable) exec.Executor { + e := &ChecksumTableExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + tables: make(map[int64]*checksumContext), + done: false, + } + startTs, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + for _, t := range v.Tables { + e.tables[t.TableInfo.ID] = newChecksumContext(t.DBInfo, t.TableInfo, startTs) + } + return e +} + +func (b *executorBuilder) buildReloadExprPushdownBlacklist(_ *plannercore.ReloadExprPushdownBlacklist) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, nil, 0) + return &ReloadExprPushdownBlacklistExec{base} +} + +func (b *executorBuilder) buildReloadOptRuleBlacklist(_ *plannercore.ReloadOptRuleBlacklist) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, nil, 0) + return &ReloadOptRuleBlacklistExec{BaseExecutor: base} +} + +func (b *executorBuilder) buildAdminPlugins(v *plannercore.AdminPlugins) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, nil, 0) + return &AdminPluginsExec{BaseExecutor: base, Action: v.Action, Plugins: v.Plugins} +} + +func (b *executorBuilder) buildDeallocate(v *plannercore.Deallocate) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, nil, v.ID()) + base.SetInitCap(chunk.ZeroCapacity) + e := &DeallocateExec{ + BaseExecutor: base, + Name: v.Name, + } + return e +} + +func (b *executorBuilder) buildSelectLock(v *plannercore.PhysicalLock) exec.Executor { + if !b.inSelectLockStmt { + b.inSelectLockStmt = true + defer func() { b.inSelectLockStmt = false }() + } + b.hasLock = true + if b.err = b.updateForUpdateTS(); b.err != nil { + return nil + } + + src := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + if !b.ctx.GetSessionVars().InTxn() { + // Locking of rows for update using SELECT FOR UPDATE only applies when autocommit + // is disabled (either by beginning transaction with START TRANSACTION or by setting + // autocommit to 0. If autocommit is enabled, the rows matching the specification are not locked. + // See https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html + return src + } + e := &SelectLockExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), + Lock: v.Lock, + tblID2Handle: v.TblID2Handle, + tblID2PhysTblIDCol: v.TblID2PhysTblIDCol, + } + + // filter out temporary tables because they do not store any record in tikv and should not write any lock + is := e.Ctx().GetInfoSchema().(infoschema.InfoSchema) + for tblID := range e.tblID2Handle { + tblInfo, ok := is.TableByID(tblID) + if !ok { + b.err = errors.Errorf("Can not get table %d", tblID) + } + + if tblInfo.Meta().TempTableType != model.TempTableNone { + delete(e.tblID2Handle, tblID) + } + } + + return e +} + +func (b *executorBuilder) buildLimit(v *plannercore.PhysicalLimit) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + n := int(mathutil.Min(v.Count, uint64(b.ctx.GetSessionVars().MaxChunkSize))) + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) + base.SetInitCap(n) + e := &LimitExec{ + BaseExecutor: base, + begin: v.Offset, + end: v.Offset + v.Count, + } + + childUsedSchema := markChildrenUsedCols(v.Schema().Columns, v.Children()[0].Schema())[0] + e.columnIdxsUsedByChild = make([]int, 0, len(childUsedSchema)) + for i, used := range childUsedSchema { + if used { + e.columnIdxsUsedByChild = append(e.columnIdxsUsedByChild, i) + } + } + if len(e.columnIdxsUsedByChild) == len(childUsedSchema) { + e.columnIdxsUsedByChild = nil // indicates that all columns are used. LimitExec will improve performance for this condition. + } + return e +} + +func (b *executorBuilder) buildPrepare(v *plannercore.Prepare) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(chunk.ZeroCapacity) + return &PrepareExec{ + BaseExecutor: base, + name: v.Name, + sqlText: v.SQLText, + } +} + +func (b *executorBuilder) buildExecute(v *plannercore.Execute) exec.Executor { + e := &ExecuteExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + is: b.is, + name: v.Name, + usingVars: v.Params, + stmt: v.Stmt, + plan: v.Plan, + outputNames: v.OutputNames(), + } + + failpoint.Inject("assertExecutePrepareStatementStalenessOption", func(val failpoint.Value) { + vs := strings.Split(val.(string), "_") + assertTS, assertReadReplicaScope := vs[0], vs[1] + staleread.AssertStmtStaleness(b.ctx, true) + ts, err := sessiontxn.GetTxnManager(b.ctx).GetStmtReadTS() + if err != nil { + panic(e) + } + + if strconv.FormatUint(ts, 10) != assertTS || + assertReadReplicaScope != b.readReplicaScope { + panic("execute prepare statement have wrong staleness option") + } + }) + + return e +} + +func (b *executorBuilder) buildShow(v *plannercore.PhysicalShow) exec.Executor { + e := &ShowExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + Tp: v.Tp, + CountWarningsOrErrors: v.CountWarningsOrErrors, + DBName: model.NewCIStr(v.DBName), + Table: v.Table, + Partition: v.Partition, + Column: v.Column, + IndexName: v.IndexName, + ResourceGroupName: model.NewCIStr(v.ResourceGroupName), + Flag: v.Flag, + Roles: v.Roles, + User: v.User, + is: b.is, + Full: v.Full, + IfNotExists: v.IfNotExists, + GlobalScope: v.GlobalScope, + Extended: v.Extended, + Extractor: v.Extractor, + ImportJobID: v.ImportJobID, + } + if e.Tp == ast.ShowMasterStatus { + // show master status need start ts. + if _, err := e.Ctx().Txn(true); err != nil { + b.err = err + } + } + return e +} + +func (b *executorBuilder) buildSimple(v *plannercore.Simple) exec.Executor { + switch s := v.Statement.(type) { + case *ast.GrantStmt: + return b.buildGrant(s) + case *ast.RevokeStmt: + return b.buildRevoke(s) + case *ast.BRIEStmt: + return b.buildBRIE(s, v.Schema()) + case *ast.CreateUserStmt, *ast.AlterUserStmt: + var lockOptions []*ast.PasswordOrLockOption + if b.Ti.AccountLockTelemetry == nil { + b.Ti.AccountLockTelemetry = &AccountLockTelemetryInfo{} + } + b.Ti.AccountLockTelemetry.CreateOrAlterUser++ + if stmt, ok := v.Statement.(*ast.CreateUserStmt); ok { + lockOptions = stmt.PasswordOrLockOptions + } else if stmt, ok := v.Statement.(*ast.AlterUserStmt); ok { + lockOptions = stmt.PasswordOrLockOptions + } + if len(lockOptions) > 0 { + // Multiple lock options are supported for the parser, but only the last one option takes effect. + for i := len(lockOptions) - 1; i >= 0; i-- { + if lockOptions[i].Type == ast.Lock { + b.Ti.AccountLockTelemetry.LockUser++ + break + } else if lockOptions[i].Type == ast.Unlock { + b.Ti.AccountLockTelemetry.UnlockUser++ + break + } + } + } + case *ast.CalibrateResourceStmt: + return &calibrateresource.Executor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), 0), + WorkloadType: s.Tp, + OptionList: s.DynamicCalibrateResourceOptionList, + } + case *ast.AddQueryWatchStmt: + return &querywatch.AddExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), 0), + QueryWatchOptionList: s.QueryWatchOptionList, + } + case *ast.LoadDataActionStmt: + return &LoadDataActionExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), + tp: s.Tp, + jobID: s.JobID, + } + case *ast.ImportIntoActionStmt: + return &ImportIntoActionExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), + tp: s.Tp, + jobID: s.JobID, + } + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(chunk.ZeroCapacity) + e := &SimpleExec{ + BaseExecutor: base, + Statement: v.Statement, + IsFromRemote: v.IsFromRemote, + is: b.is, + staleTxnStartTS: v.StaleTxnStartTS, + } + return e +} + +func (b *executorBuilder) buildSet(v *plannercore.Set) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(chunk.ZeroCapacity) + e := &SetExecutor{ + BaseExecutor: base, + vars: v.VarAssigns, + } + return e +} + +func (b *executorBuilder) buildSetConfig(v *plannercore.SetConfig) exec.Executor { + return &SetConfigExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + p: v, + } +} + +func (b *executorBuilder) buildInsert(v *plannercore.Insert) exec.Executor { + b.inInsertStmt = true + if b.err = b.updateForUpdateTS(); b.err != nil { + return nil + } + + selectExec := b.build(v.SelectPlan) + if b.err != nil { + return nil + } + var baseExec exec.BaseExecutor + if selectExec != nil { + baseExec = exec.NewBaseExecutor(b.ctx, nil, v.ID(), selectExec) + } else { + baseExec = exec.NewBaseExecutor(b.ctx, nil, v.ID()) + } + baseExec.SetInitCap(chunk.ZeroCapacity) + + ivs := &InsertValues{ + BaseExecutor: baseExec, + Table: v.Table, + Columns: v.Columns, + Lists: v.Lists, + GenExprs: v.GenCols.Exprs, + allAssignmentsAreConstant: v.AllAssignmentsAreConstant, + hasRefCols: v.NeedFillDefaultValue, + SelectExec: selectExec, + rowLen: v.RowLen, + } + err := ivs.initInsertColumns() + if err != nil { + b.err = err + return nil + } + ivs.fkChecks, b.err = buildFKCheckExecs(b.ctx, ivs.Table, v.FKChecks) + if b.err != nil { + return nil + } + ivs.fkCascades, b.err = b.buildFKCascadeExecs(ivs.Table, v.FKCascades) + if b.err != nil { + return nil + } + + if v.IsReplace { + return b.buildReplace(ivs) + } + insert := &InsertExec{ + InsertValues: ivs, + OnDuplicate: append(v.OnDuplicate, v.GenCols.OnDuplicates...), + } + return insert +} + +func (b *executorBuilder) buildImportInto(v *plannercore.ImportInto) exec.Executor { + tbl, ok := b.is.TableByID(v.Table.TableInfo.ID) + if !ok { + b.err = errors.Errorf("Can not get table %d", v.Table.TableInfo.ID) + return nil + } + if !tbl.Meta().IsBaseTable() { + b.err = plannercore.ErrNonUpdatableTable.GenWithStackByArgs(tbl.Meta().Name.O, "LOAD") + return nil + } + + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + exec, err := newImportIntoExec(base, b.ctx, v, tbl) + if err != nil { + b.err = err + return nil + } + + return exec +} + +func (b *executorBuilder) buildLoadData(v *plannercore.LoadData) exec.Executor { + tbl, ok := b.is.TableByID(v.Table.TableInfo.ID) + if !ok { + b.err = errors.Errorf("Can not get table %d", v.Table.TableInfo.ID) + return nil + } + if !tbl.Meta().IsBaseTable() { + b.err = plannercore.ErrNonUpdatableTable.GenWithStackByArgs(tbl.Meta().Name.O, "LOAD") + return nil + } + + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + worker, err := NewLoadDataWorker(b.ctx, v, tbl) + if err != nil { + b.err = err + return nil + } + + return &LoadDataExec{ + BaseExecutor: base, + loadDataWorker: worker, + FileLocRef: v.FileLocRef, + } +} + +func (b *executorBuilder) buildLoadStats(v *plannercore.LoadStats) exec.Executor { + e := &LoadStatsExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + info: &LoadStatsInfo{v.Path, b.ctx}, + } + return e +} + +func (b *executorBuilder) buildLockStats(v *plannercore.LockStats) exec.Executor { + e := &lockstats.LockExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + Tables: v.Tables, + } + return e +} + +func (b *executorBuilder) buildUnlockStats(v *plannercore.UnlockStats) exec.Executor { + e := &lockstats.UnlockExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + Tables: v.Tables, + } + return e +} + +func (b *executorBuilder) buildIndexAdvise(v *plannercore.IndexAdvise) exec.Executor { + e := &IndexAdviseExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + IsLocal: v.IsLocal, + indexAdviseInfo: &IndexAdviseInfo{ + Path: v.Path, + MaxMinutes: v.MaxMinutes, + MaxIndexNum: v.MaxIndexNum, + LineFieldsInfo: v.LineFieldsInfo, + Ctx: b.ctx, + }, + } + return e +} + +func (b *executorBuilder) buildPlanReplayer(v *plannercore.PlanReplayer) exec.Executor { + if v.Load { + e := &PlanReplayerLoadExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + info: &PlanReplayerLoadInfo{Path: v.File, Ctx: b.ctx}, + } + return e + } + if v.Capture { + e := &PlanReplayerExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + CaptureInfo: &PlanReplayerCaptureInfo{ + SQLDigest: v.SQLDigest, + PlanDigest: v.PlanDigest, + }, + } + return e + } + if v.Remove { + e := &PlanReplayerExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, v.ID()), + CaptureInfo: &PlanReplayerCaptureInfo{ + SQLDigest: v.SQLDigest, + PlanDigest: v.PlanDigest, + Remove: true, + }, + } + return e + } + + e := &PlanReplayerExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + DumpInfo: &PlanReplayerDumpInfo{ + Analyze: v.Analyze, + Path: v.File, + ctx: b.ctx, + HistoricalStatsTS: v.HistoricalStatsTS, + }, + } + if v.ExecStmt != nil { + e.DumpInfo.ExecStmts = []ast.StmtNode{v.ExecStmt} + } else { + e.BaseExecutor = exec.NewBaseExecutor(b.ctx, nil, v.ID()) + } + return e +} + +func (*executorBuilder) buildReplace(vals *InsertValues) exec.Executor { + replaceExec := &ReplaceExec{ + InsertValues: vals, + } + return replaceExec +} + +func (b *executorBuilder) buildGrant(grant *ast.GrantStmt) exec.Executor { + e := &GrantExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), + Privs: grant.Privs, + ObjectType: grant.ObjectType, + Level: grant.Level, + Users: grant.Users, + WithGrant: grant.WithGrant, + AuthTokenOrTLSOptions: grant.AuthTokenOrTLSOptions, + is: b.is, + } + return e +} + +func (b *executorBuilder) buildRevoke(revoke *ast.RevokeStmt) exec.Executor { + e := &RevokeExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, nil, 0), + ctx: b.ctx, + Privs: revoke.Privs, + ObjectType: revoke.ObjectType, + Level: revoke.Level, + Users: revoke.Users, + is: b.is, + } + return e +} + +func (b *executorBuilder) setTelemetryInfo(v *plannercore.DDL) { + if v == nil || b.Ti == nil { + return + } + switch s := v.Statement.(type) { + case *ast.AlterTableStmt: + if len(s.Specs) > 1 { + b.Ti.UseMultiSchemaChange = true + } + for _, spec := range s.Specs { + switch spec.Tp { + case ast.AlterTableDropFirstPartition: + if b.Ti.PartitionTelemetry == nil { + b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} + } + b.Ti.PartitionTelemetry.UseDropIntervalPartition = true + case ast.AlterTableAddLastPartition: + if b.Ti.PartitionTelemetry == nil { + b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} + } + b.Ti.PartitionTelemetry.UseAddIntervalPartition = true + case ast.AlterTableExchangePartition: + b.Ti.UseExchangePartition = true + case ast.AlterTableReorganizePartition: + if b.Ti.PartitionTelemetry == nil { + b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} + } + b.Ti.PartitionTelemetry.UseReorganizePartition = true + } + } + case *ast.CreateTableStmt: + if s.Partition == nil || strings.EqualFold(b.ctx.GetSessionVars().EnableTablePartition, "OFF") { + break + } + + p := s.Partition + if b.Ti.PartitionTelemetry == nil { + b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} + } + b.Ti.PartitionTelemetry.TablePartitionMaxPartitionsNum = mathutil.Max(p.Num, uint64(len(p.Definitions))) + b.Ti.PartitionTelemetry.UseTablePartition = true + + switch p.Tp { + case model.PartitionTypeRange: + if p.Sub == nil { + if len(p.ColumnNames) > 0 { + b.Ti.PartitionTelemetry.UseTablePartitionRangeColumns = true + if len(p.ColumnNames) > 1 { + b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt1 = true + } + if len(p.ColumnNames) > 2 { + b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt2 = true + } + if len(p.ColumnNames) > 3 { + b.Ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt3 = true + } + } else { + b.Ti.PartitionTelemetry.UseTablePartitionRange = true + } + if p.Interval != nil { + b.Ti.PartitionTelemetry.UseCreateIntervalPartition = true + } + } + case model.PartitionTypeHash: + if p.Sub == nil { + b.Ti.PartitionTelemetry.UseTablePartitionHash = true + } + case model.PartitionTypeList: + enable := b.ctx.GetSessionVars().EnableListTablePartition + if p.Sub == nil && enable { + if len(p.ColumnNames) > 0 { + b.Ti.PartitionTelemetry.UseTablePartitionListColumns = true + } else { + b.Ti.PartitionTelemetry.UseTablePartitionList = true + } + } + } + case *ast.FlashBackToTimestampStmt: + b.Ti.UseFlashbackToCluster = true + } +} + +func (b *executorBuilder) buildDDL(v *plannercore.DDL) exec.Executor { + b.setTelemetryInfo(v) + + e := &DDLExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + stmt: v.Statement, + is: b.is, + tempTableDDL: temptable.GetTemporaryTableDDL(b.ctx), + } + return e +} + +// buildTrace builds a TraceExec for future executing. This method will be called +// at build(). +func (b *executorBuilder) buildTrace(v *plannercore.Trace) exec.Executor { + t := &TraceExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + stmtNode: v.StmtNode, + builder: b, + format: v.Format, + + optimizerTrace: v.OptimizerTrace, + optimizerTraceTarget: v.OptimizerTraceTarget, + } + if t.format == plannercore.TraceFormatLog && !t.optimizerTrace { + return &SortExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), t), + ByItems: []*plannerutil.ByItems{ + {Expr: &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeTimestamp), + }}, + }, + schema: v.Schema(), + } + } + return t +} + +// buildExplain builds a explain executor. `e.rows` collects final result to `ExplainExec`. +func (b *executorBuilder) buildExplain(v *plannercore.Explain) exec.Executor { + explainExec := &ExplainExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + explain: v, + } + if v.Analyze { + if b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl == nil { + b.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(nil) + } + // If the resource group name is not empty, we could collect and display the RU + // runtime stats for analyze executor. + resourceGroupName := b.ctx.GetSessionVars().ResourceGroupName + // Try to register the RU runtime stats for analyze executor. + if store, ok := b.ctx.GetStore().(interface { + CreateRURuntimeStats(uint64) *clientutil.RURuntimeStats + }); len(resourceGroupName) > 0 && ok { + // StartTS will be used to identify this SQL, so that the runtime stats could + // aggregate the RU stats beneath the KV storage client. + startTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + explainExec.ruRuntimeStats = store.CreateRURuntimeStats(startTS) + } + explainExec.analyzeExec = b.build(v.TargetPlan) + } + return explainExec +} + +func (b *executorBuilder) buildSelectInto(v *plannercore.SelectInto) exec.Executor { + child := b.build(v.TargetPlan) + if b.err != nil { + return nil + } + return &SelectIntoExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), child), + intoOpt: v.IntoOpt, + LineFieldsInfo: v.LineFieldsInfo, + } +} + +func (b *executorBuilder) buildUnionScanExec(v *plannercore.PhysicalUnionScan) exec.Executor { + oriEncounterUnionScan := b.encounterUnionScan + b.encounterUnionScan = true + defer func() { + b.encounterUnionScan = oriEncounterUnionScan + }() + reader := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + + return b.buildUnionScanFromReader(reader, v) +} + +// buildUnionScanFromReader builds union scan executor from child executor. +// Note that this function may be called by inner workers of index lookup join concurrently. +// Be careful to avoid data race. +func (b *executorBuilder) buildUnionScanFromReader(reader exec.Executor, v *plannercore.PhysicalUnionScan) exec.Executor { + // If reader is union, it means a partition table and we should transfer as above. + if x, ok := reader.(*UnionExec); ok { + for i, child := range x.AllChildren() { + x.SetChildren(i, b.buildUnionScanFromReader(child, v)) + if b.err != nil { + return nil + } + } + return x + } + us := &UnionScanExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), reader)} + // Get the handle column index of the below Plan. + us.handleCols = v.HandleCols + us.mutableRow = chunk.MutRowFromTypes(exec.RetTypes(us)) + + // If the push-downed condition contains virtual column, we may build a selection upon reader + originReader := reader + if sel, ok := reader.(*SelectionExec); ok { + reader = sel.Children(0) + } + + us.collators = make([]collate.Collator, 0, len(us.columns)) + for _, tp := range exec.RetTypes(us) { + us.collators = append(us.collators, collate.GetCollator(tp.GetCollate())) + } + + startTS, err := b.getSnapshotTS() + sessionVars := b.ctx.GetSessionVars() + if err != nil { + b.err = err + return nil + } + + switch x := reader.(type) { + case *MPPGather: + us.desc = false + us.keepOrder = false + us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) + us.columns = x.columns + us.table = x.table + us.virtualColumnIndex = x.virtualColumnIndex + us.handleCachedTable(b, x, sessionVars, startTS) + case *TableReaderExecutor: + us.desc = x.desc + us.keepOrder = x.keepOrder + us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) + us.columns = x.columns + us.table = x.table + us.virtualColumnIndex = x.virtualColumnIndex + us.handleCachedTable(b, x, sessionVars, startTS) + case *IndexReaderExecutor: + us.desc = x.desc + us.keepOrder = x.keepOrder + for _, ic := range x.index.Columns { + for i, col := range x.columns { + if col.Name.L == ic.Name.L { + us.usedIndex = append(us.usedIndex, i) + break + } + } + } + us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) + us.columns = x.columns + us.table = x.table + us.handleCachedTable(b, x, sessionVars, startTS) + case *IndexLookUpExecutor: + us.desc = x.desc + us.keepOrder = x.keepOrder + for _, ic := range x.index.Columns { + for i, col := range x.columns { + if col.Name.L == ic.Name.L { + us.usedIndex = append(us.usedIndex, i) + break + } + } + } + us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) + us.columns = x.columns + us.table = x.table + us.virtualColumnIndex = buildVirtualColumnIndex(us.Schema(), us.columns) + us.handleCachedTable(b, x, sessionVars, startTS) + case *IndexMergeReaderExecutor: + if len(x.byItems) != 0 { + us.keepOrder = x.keepOrder + us.desc = x.byItems[0].Desc + for _, item := range x.byItems { + c, ok := item.Expr.(*expression.Column) + if !ok { + b.err = errors.Errorf("Not support non-column in orderBy pushed down") + return nil + } + for i, col := range x.columns { + if col.ID == c.ID { + us.usedIndex = append(us.usedIndex, i) + break + } + } + } + } + us.conditions, us.conditionsWithVirCol = plannercore.SplitSelCondsWithVirtualColumn(v.Conditions) + us.columns = x.columns + us.table = x.table + us.virtualColumnIndex = buildVirtualColumnIndex(us.Schema(), us.columns) + default: + // The mem table will not be written by sql directly, so we can omit the union scan to avoid err reporting. + return originReader + } + return us +} + +type bypassDataSourceExecutor interface { + dataSourceExecutor + setDummy() +} + +func (us *UnionScanExec) handleCachedTable(b *executorBuilder, x bypassDataSourceExecutor, vars *variable.SessionVars, startTS uint64) { + tbl := x.Table() + if tbl.Meta().TableCacheStatusType == model.TableCacheStatusEnable { + cachedTable := tbl.(table.CachedTable) + // Determine whether the cache can be used. + leaseDuration := time.Duration(variable.TableCacheLease.Load()) * time.Second + cacheData, loading := cachedTable.TryReadFromCache(startTS, leaseDuration) + if cacheData != nil { + vars.StmtCtx.ReadFromTableCache = true + x.setDummy() + us.cacheTable = cacheData + } else if loading { + return + } else { + if !b.inUpdateStmt && !b.inDeleteStmt && !b.inInsertStmt && !vars.StmtCtx.InExplainStmt { + store := b.ctx.GetStore() + cachedTable.UpdateLockForRead(context.Background(), store, startTS, leaseDuration) + } + } + } +} + +// buildMergeJoin builds MergeJoinExec executor. +func (b *executorBuilder) buildMergeJoin(v *plannercore.PhysicalMergeJoin) exec.Executor { + leftExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + + rightExec := b.build(v.Children()[1]) + if b.err != nil { + return nil + } + + defaultValues := v.DefaultValues + if defaultValues == nil { + if v.JoinType == plannercore.RightOuterJoin { + defaultValues = make([]types.Datum, leftExec.Schema().Len()) + } else { + defaultValues = make([]types.Datum, rightExec.Schema().Len()) + } + } + + colsFromChildren := v.Schema().Columns + if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { + colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] + } + + e := &MergeJoinExec{ + stmtCtx: b.ctx.GetSessionVars().StmtCtx, + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), leftExec, rightExec), + compareFuncs: v.CompareFuncs, + joiner: newJoiner( + b.ctx, + v.JoinType, + v.JoinType == plannercore.RightOuterJoin, + defaultValues, + v.OtherConditions, + exec.RetTypes(leftExec), + exec.RetTypes(rightExec), + markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()), + false, + ), + isOuterJoin: v.JoinType.IsOuterJoin(), + desc: v.Desc, + } + + leftTable := &mergeJoinTable{ + childIndex: 0, + joinKeys: v.LeftJoinKeys, + filters: v.LeftConditions, + } + rightTable := &mergeJoinTable{ + childIndex: 1, + joinKeys: v.RightJoinKeys, + filters: v.RightConditions, + } + + if v.JoinType == plannercore.RightOuterJoin { + e.innerTable = leftTable + e.outerTable = rightTable + } else { + e.innerTable = rightTable + e.outerTable = leftTable + } + e.innerTable.isInner = true + + // optimizer should guarantee that filters on inner table are pushed down + // to tikv or extracted to a Selection. + if len(e.innerTable.filters) != 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "merge join's inner filter should be empty.") + return nil + } + + executor_metrics.ExecutorCounterMergeJoinExec.Inc() + return e +} + +func (b *executorBuilder) buildHashJoin(v *plannercore.PhysicalHashJoin) exec.Executor { + leftExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + + rightExec := b.build(v.Children()[1]) + if b.err != nil { + return nil + } + + e := &HashJoinExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), leftExec, rightExec), + probeSideTupleFetcher: &probeSideTupleFetcher{}, + probeWorkers: make([]*probeWorker, v.Concurrency), + buildWorker: &buildWorker{}, + hashJoinCtx: &hashJoinCtx{ + sessCtx: b.ctx, + isOuterJoin: v.JoinType.IsOuterJoin(), + useOuterToBuild: v.UseOuterToBuild, + joinType: v.JoinType, + concurrency: v.Concurrency, + }, + } + e.hashJoinCtx.allocPool = e.AllocPool + defaultValues := v.DefaultValues + lhsTypes, rhsTypes := exec.RetTypes(leftExec), exec.RetTypes(rightExec) + if v.InnerChildIdx == 1 { + if len(v.RightConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } else { + if len(v.LeftConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } + + leftIsBuildSide := true + + e.isNullEQ = v.IsNullEQ + var probeKeys, probeNAKeys, buildKeys, buildNAKeys []*expression.Column + var buildSideExec exec.Executor + if v.UseOuterToBuild { + // update the buildSideEstCount due to changing the build side + if v.InnerChildIdx == 1 { + buildSideExec, buildKeys, buildNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys + e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys + e.outerFilter = v.LeftConditions + } else { + buildSideExec, buildKeys, buildNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys + e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys + e.outerFilter = v.RightConditions + leftIsBuildSide = false + } + if defaultValues == nil { + defaultValues = make([]types.Datum, e.probeSideTupleFetcher.probeSideExec.Schema().Len()) + } + } else { + if v.InnerChildIdx == 0 { + buildSideExec, buildKeys, buildNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys + e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys + e.outerFilter = v.RightConditions + } else { + buildSideExec, buildKeys, buildNAKeys = rightExec, v.RightJoinKeys, v.RightNAJoinKeys + e.probeSideTupleFetcher.probeSideExec, probeKeys, probeNAKeys = leftExec, v.LeftJoinKeys, v.LeftNAJoinKeys + e.outerFilter = v.LeftConditions + leftIsBuildSide = false + } + if defaultValues == nil { + defaultValues = make([]types.Datum, buildSideExec.Schema().Len()) + } + } + probeKeyColIdx := make([]int, len(probeKeys)) + probeNAKeColIdx := make([]int, len(probeNAKeys)) + buildKeyColIdx := make([]int, len(buildKeys)) + buildNAKeyColIdx := make([]int, len(buildNAKeys)) + for i := range buildKeys { + buildKeyColIdx[i] = buildKeys[i].Index + } + for i := range buildNAKeys { + buildNAKeyColIdx[i] = buildNAKeys[i].Index + } + for i := range probeKeys { + probeKeyColIdx[i] = probeKeys[i].Index + } + for i := range probeNAKeys { + probeNAKeColIdx[i] = probeNAKeys[i].Index + } + isNAJoin := len(v.LeftNAJoinKeys) > 0 + colsFromChildren := v.Schema().Columns + if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { + colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] + } + childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) + for i := uint(0); i < e.concurrency; i++ { + e.probeWorkers[i] = &probeWorker{ + hashJoinCtx: e.hashJoinCtx, + workerID: i, + joiner: newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, lhsTypes, rhsTypes, childrenUsedSchema, isNAJoin), + probeKeyColIdx: probeKeyColIdx, + probeNAKeyColIdx: probeNAKeColIdx, + } + } + e.buildWorker.buildKeyColIdx, e.buildWorker.buildNAKeyColIdx, e.buildWorker.buildSideExec, e.buildWorker.hashJoinCtx = buildKeyColIdx, buildNAKeyColIdx, buildSideExec, e.hashJoinCtx + e.hashJoinCtx.isNullAware = isNAJoin + executor_metrics.ExecutorCountHashJoinExec.Inc() + + // We should use JoinKey to construct the type information using by hashing, instead of using the child's schema directly. + // When a hybrid type column is hashed multiple times, we need to distinguish what field types are used. + // For example, the condition `enum = int and enum = string`, we should use ETInt to hash the first column, + // and use ETString to hash the second column, although they may be the same column. + leftExecTypes, rightExecTypes := exec.RetTypes(leftExec), exec.RetTypes(rightExec) + leftTypes, rightTypes := make([]*types.FieldType, 0, len(v.LeftJoinKeys)+len(v.LeftNAJoinKeys)), make([]*types.FieldType, 0, len(v.RightJoinKeys)+len(v.RightNAJoinKeys)) + // set left types and right types for joiner. + for i, col := range v.LeftJoinKeys { + leftTypes = append(leftTypes, leftExecTypes[col.Index].Clone()) + leftTypes[i].SetFlag(col.RetType.GetFlag()) + } + offset := len(v.LeftJoinKeys) + for i, col := range v.LeftNAJoinKeys { + leftTypes = append(leftTypes, leftExecTypes[col.Index].Clone()) + leftTypes[i+offset].SetFlag(col.RetType.GetFlag()) + } + for i, col := range v.RightJoinKeys { + rightTypes = append(rightTypes, rightExecTypes[col.Index].Clone()) + rightTypes[i].SetFlag(col.RetType.GetFlag()) + } + offset = len(v.RightJoinKeys) + for i, col := range v.RightNAJoinKeys { + rightTypes = append(rightTypes, rightExecTypes[col.Index].Clone()) + rightTypes[i+offset].SetFlag(col.RetType.GetFlag()) + } + + // consider collations + for i := range v.EqualConditions { + chs, coll := v.EqualConditions[i].CharsetAndCollation() + leftTypes[i].SetCharset(chs) + leftTypes[i].SetCollate(coll) + rightTypes[i].SetCharset(chs) + rightTypes[i].SetCollate(coll) + } + offset = len(v.EqualConditions) + for i := range v.NAEqualConditions { + chs, coll := v.NAEqualConditions[i].CharsetAndCollation() + leftTypes[i+offset].SetCharset(chs) + leftTypes[i+offset].SetCollate(coll) + rightTypes[i+offset].SetCharset(chs) + rightTypes[i+offset].SetCollate(coll) + } + if leftIsBuildSide { + e.buildTypes, e.probeTypes = leftTypes, rightTypes + } else { + e.buildTypes, e.probeTypes = rightTypes, leftTypes + } + return e +} + +func (b *executorBuilder) buildHashAgg(v *plannercore.PhysicalHashAgg) exec.Executor { + src := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + sessionVars := b.ctx.GetSessionVars() + e := &aggregate.HashAggExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), + Sc: sessionVars.StmtCtx, + PartialAggFuncs: make([]aggfuncs.AggFunc, 0, len(v.AggFuncs)), + GroupByItems: v.GroupByItems, + } + // We take `create table t(a int, b int);` as example. + // + // 1. If all the aggregation functions are FIRST_ROW, we do not need to set the defaultVal for them: + // e.g. + // mysql> select distinct a, b from t; + // 0 rows in set (0.00 sec) + // + // 2. If there exists group by items, we do not need to set the defaultVal for them either: + // e.g. + // mysql> select avg(a) from t group by b; + // Empty set (0.00 sec) + // + // mysql> select avg(a) from t group by a; + // +--------+ + // | avg(a) | + // +--------+ + // | NULL | + // +--------+ + // 1 row in set (0.00 sec) + if len(v.GroupByItems) != 0 || aggregation.IsAllFirstRow(v.AggFuncs) { + e.DefaultVal = nil + } else { + if v.IsFinalAgg() { + e.DefaultVal = e.Ctx().GetSessionVars().GetNewChunkWithCapacity(exec.RetTypes(e), 1, 1, e.AllocPool) + } + } + for _, aggDesc := range v.AggFuncs { + if aggDesc.HasDistinct || len(aggDesc.OrderByItems) > 0 { + e.IsUnparallelExec = true + } + } + // When we set both tidb_hashagg_final_concurrency and tidb_hashagg_partial_concurrency to 1, + // we do not need to parallelly execute hash agg, + // and this action can be a workaround when meeting some unexpected situation using parallelExec. + if finalCon, partialCon := sessionVars.HashAggFinalConcurrency(), sessionVars.HashAggPartialConcurrency(); finalCon <= 0 || partialCon <= 0 || finalCon == 1 && partialCon == 1 { + e.IsUnparallelExec = true + } + partialOrdinal := 0 + for i, aggDesc := range v.AggFuncs { + if e.IsUnparallelExec { + e.PartialAggFuncs = append(e.PartialAggFuncs, aggfuncs.Build(b.ctx, aggDesc, i)) + } else { + ordinal := []int{partialOrdinal} + partialOrdinal++ + if aggDesc.Name == ast.AggFuncAvg { + ordinal = append(ordinal, partialOrdinal+1) + partialOrdinal++ + } + partialAggDesc, finalDesc := aggDesc.Split(ordinal) + partialAggFunc := aggfuncs.Build(b.ctx, partialAggDesc, i) + finalAggFunc := aggfuncs.Build(b.ctx, finalDesc, i) + e.PartialAggFuncs = append(e.PartialAggFuncs, partialAggFunc) + e.FinalAggFuncs = append(e.FinalAggFuncs, finalAggFunc) + if partialAggDesc.Name == ast.AggFuncGroupConcat { + // For group_concat, finalAggFunc and partialAggFunc need shared `truncate` flag to do duplicate. + finalAggFunc.(interface{ SetTruncated(t *int32) }).SetTruncated( + partialAggFunc.(interface{ GetTruncated() *int32 }).GetTruncated(), + ) + } + } + if e.DefaultVal != nil { + value := aggDesc.GetDefaultValue() + e.DefaultVal.AppendDatum(i, &value) + } + } + + executor_metrics.ExecutorCounterHashAggExec.Inc() + return e +} + +func (b *executorBuilder) buildStreamAgg(v *plannercore.PhysicalStreamAgg) exec.Executor { + src := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + e := &aggregate.StreamAggExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), src), + GroupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, v.GroupByItems), + AggFuncs: make([]aggfuncs.AggFunc, 0, len(v.AggFuncs)), + } + + if len(v.GroupByItems) != 0 || aggregation.IsAllFirstRow(v.AggFuncs) { + e.DefaultVal = nil + } else { + // Only do this for final agg, see issue #35295, #30923 + if v.IsFinalAgg() { + e.DefaultVal = e.Ctx().GetSessionVars().GetNewChunkWithCapacity(exec.RetTypes(e), 1, 1, e.AllocPool) + } + } + for i, aggDesc := range v.AggFuncs { + aggFunc := aggfuncs.Build(b.ctx, aggDesc, i) + e.AggFuncs = append(e.AggFuncs, aggFunc) + if e.DefaultVal != nil { + value := aggDesc.GetDefaultValue() + e.DefaultVal.AppendDatum(i, &value) + } + } + + executor_metrics.ExecutorStreamAggExec.Inc() + return e +} + +func (b *executorBuilder) buildSelection(v *plannercore.PhysicalSelection) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + e := &SelectionExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), + filters: v.Conditions, + } + return e +} + +func (b *executorBuilder) buildProjection(v *plannercore.PhysicalProjection) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + e := &ProjectionExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), + numWorkers: int64(b.ctx.GetSessionVars().ProjectionConcurrency()), + evaluatorSuit: expression.NewEvaluatorSuite(v.Exprs, v.AvoidColumnEvaluator), + calculateNoDelay: v.CalculateNoDelay, + } + + // If the calculation row count for this Projection operator is smaller + // than a Chunk size, we turn back to the un-parallel Projection + // implementation to reduce the goroutine overhead. + if int64(v.StatsCount()) < int64(b.ctx.GetSessionVars().MaxChunkSize) { + e.numWorkers = 0 + } + + // Use un-parallel projection for query that write on memdb to avoid data race. + // See also https://github.com/pingcap/tidb/issues/26832 + if b.inUpdateStmt || b.inDeleteStmt || b.inInsertStmt || b.hasLock { + e.numWorkers = 0 + } + return e +} + +func (b *executorBuilder) buildTableDual(v *plannercore.PhysicalTableDual) exec.Executor { + if v.RowCount != 0 && v.RowCount != 1 { + b.err = errors.Errorf("buildTableDual failed, invalid row count for dual table: %v", v.RowCount) + return nil + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(v.RowCount) + e := &TableDualExec{ + BaseExecutor: base, + numDualRows: v.RowCount, + } + return e +} + +// `getSnapshotTS` returns for-update-ts if in insert/update/delete/lock statement otherwise the isolation read ts +// Please notice that in RC isolation, the above two ts are the same +func (b *executorBuilder) getSnapshotTS() (ts uint64, err error) { + if b.forDataReaderBuilder { + return b.dataReaderTS, nil + } + + txnManager := sessiontxn.GetTxnManager(b.ctx) + if b.inInsertStmt || b.inUpdateStmt || b.inDeleteStmt || b.inSelectLockStmt { + return txnManager.GetStmtForUpdateTS() + } + return txnManager.GetStmtReadTS() +} + +// getSnapshot get the appropriate snapshot from txnManager and set +// the relevant snapshot options before return. +func (b *executorBuilder) getSnapshot() (kv.Snapshot, error) { + var snapshot kv.Snapshot + var err error + + txnManager := sessiontxn.GetTxnManager(b.ctx) + if b.inInsertStmt || b.inUpdateStmt || b.inDeleteStmt || b.inSelectLockStmt { + snapshot, err = txnManager.GetSnapshotWithStmtForUpdateTS() + } else { + snapshot, err = txnManager.GetSnapshotWithStmtReadTS() + } + if err != nil { + return nil, err + } + + sessVars := b.ctx.GetSessionVars() + replicaReadType := sessVars.GetReplicaRead() + snapshot.SetOption(kv.ReadReplicaScope, b.readReplicaScope) + snapshot.SetOption(kv.TaskID, sessVars.StmtCtx.TaskID) + snapshot.SetOption(kv.TiKVClientReadTimeout, sessVars.GetTiKVClientReadTimeout()) + snapshot.SetOption(kv.ResourceGroupName, sessVars.ResourceGroupName) + snapshot.SetOption(kv.ExplicitRequestSourceType, sessVars.ExplicitRequestSourceType) + + if replicaReadType.IsClosestRead() && b.readReplicaScope != kv.GlobalTxnScope { + snapshot.SetOption(kv.MatchStoreLabels, []*metapb.StoreLabel{ + { + Key: placement.DCLabelKey, + Value: b.readReplicaScope, + }, + }) + } + + return snapshot, nil +} + +func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) exec.Executor { + switch v.DBName.L { + case util.MetricSchemaName.L: + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &MetricRetriever{ + table: v.Table, + extractor: v.Extractor.(*plannercore.MetricTableExtractor), + }, + } + case util.InformationSchemaName.L: + switch v.Table.Name.L { + case strings.ToLower(infoschema.TableClusterConfig): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &clusterConfigRetriever{ + extractor: v.Extractor.(*plannercore.ClusterTableExtractor), + }, + } + case strings.ToLower(infoschema.TableClusterLoad): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &clusterServerInfoRetriever{ + extractor: v.Extractor.(*plannercore.ClusterTableExtractor), + serverInfoType: diagnosticspb.ServerInfoType_LoadInfo, + }, + } + case strings.ToLower(infoschema.TableClusterHardware): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &clusterServerInfoRetriever{ + extractor: v.Extractor.(*plannercore.ClusterTableExtractor), + serverInfoType: diagnosticspb.ServerInfoType_HardwareInfo, + }, + } + case strings.ToLower(infoschema.TableClusterSystemInfo): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &clusterServerInfoRetriever{ + extractor: v.Extractor.(*plannercore.ClusterTableExtractor), + serverInfoType: diagnosticspb.ServerInfoType_SystemInfo, + }, + } + case strings.ToLower(infoschema.TableClusterLog): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &clusterLogRetriever{ + extractor: v.Extractor.(*plannercore.ClusterLogTableExtractor), + }, + } + case strings.ToLower(infoschema.TableTiDBHotRegionsHistory): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &hotRegionsHistoryRetriver{ + extractor: v.Extractor.(*plannercore.HotRegionsHistoryTableExtractor), + }, + } + case strings.ToLower(infoschema.TableInspectionResult): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &inspectionResultRetriever{ + extractor: v.Extractor.(*plannercore.InspectionResultTableExtractor), + timeRange: v.QueryTimeRange, + }, + } + case strings.ToLower(infoschema.TableInspectionSummary): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &inspectionSummaryRetriever{ + table: v.Table, + extractor: v.Extractor.(*plannercore.InspectionSummaryTableExtractor), + timeRange: v.QueryTimeRange, + }, + } + case strings.ToLower(infoschema.TableInspectionRules): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &inspectionRuleRetriever{ + extractor: v.Extractor.(*plannercore.InspectionRuleTableExtractor), + }, + } + case strings.ToLower(infoschema.TableMetricSummary): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &MetricsSummaryRetriever{ + table: v.Table, + extractor: v.Extractor.(*plannercore.MetricSummaryTableExtractor), + timeRange: v.QueryTimeRange, + }, + } + case strings.ToLower(infoschema.TableMetricSummaryByLabel): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &MetricsSummaryByLabelRetriever{ + table: v.Table, + extractor: v.Extractor.(*plannercore.MetricSummaryTableExtractor), + timeRange: v.QueryTimeRange, + }, + } + case strings.ToLower(infoschema.TableTiKVRegionPeers): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &tikvRegionPeersRetriever{ + extractor: v.Extractor.(*plannercore.TikvRegionPeersExtractor), + }, + } + case strings.ToLower(infoschema.TableSchemata), + strings.ToLower(infoschema.TableStatistics), + strings.ToLower(infoschema.TableTiDBIndexes), + strings.ToLower(infoschema.TableViews), + strings.ToLower(infoschema.TableTables), + strings.ToLower(infoschema.TableReferConst), + strings.ToLower(infoschema.TableSequences), + strings.ToLower(infoschema.TablePartitions), + strings.ToLower(infoschema.TableEngines), + strings.ToLower(infoschema.TableCollations), + strings.ToLower(infoschema.TableAnalyzeStatus), + strings.ToLower(infoschema.TableClusterInfo), + strings.ToLower(infoschema.TableProfiling), + strings.ToLower(infoschema.TableCharacterSets), + strings.ToLower(infoschema.TableKeyColumn), + strings.ToLower(infoschema.TableUserPrivileges), + strings.ToLower(infoschema.TableMetricTables), + strings.ToLower(infoschema.TableCollationCharacterSetApplicability), + strings.ToLower(infoschema.TableProcesslist), + strings.ToLower(infoschema.ClusterTableProcesslist), + strings.ToLower(infoschema.TableTiKVRegionStatus), + strings.ToLower(infoschema.TableTiDBHotRegions), + strings.ToLower(infoschema.TableSessionVar), + strings.ToLower(infoschema.TableConstraints), + strings.ToLower(infoschema.TableTiFlashReplica), + strings.ToLower(infoschema.TableTiDBServersInfo), + strings.ToLower(infoschema.TableTiKVStoreStatus), + strings.ToLower(infoschema.TableClientErrorsSummaryGlobal), + strings.ToLower(infoschema.TableClientErrorsSummaryByUser), + strings.ToLower(infoschema.TableClientErrorsSummaryByHost), + strings.ToLower(infoschema.TableAttributes), + strings.ToLower(infoschema.TablePlacementPolicies), + strings.ToLower(infoschema.TableTrxSummary), + strings.ToLower(infoschema.TableVariablesInfo), + strings.ToLower(infoschema.TableUserAttributes), + strings.ToLower(infoschema.ClusterTableTrxSummary), + strings.ToLower(infoschema.TableMemoryUsage), + strings.ToLower(infoschema.TableMemoryUsageOpsHistory), + strings.ToLower(infoschema.ClusterTableMemoryUsage), + strings.ToLower(infoschema.ClusterTableMemoryUsageOpsHistory), + strings.ToLower(infoschema.TableResourceGroups), + strings.ToLower(infoschema.TableRunawayWatches), + strings.ToLower(infoschema.TableCheckConstraints): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &memtableRetriever{ + table: v.Table, + columns: v.Columns, + extractor: v.Extractor, + }, + } + case strings.ToLower(infoschema.TableTiDBTrx), + strings.ToLower(infoschema.ClusterTableTiDBTrx): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &tidbTrxTableRetriever{ + table: v.Table, + columns: v.Columns, + }, + } + case strings.ToLower(infoschema.TableDataLockWaits): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &dataLockWaitsTableRetriever{ + table: v.Table, + columns: v.Columns, + }, + } + case strings.ToLower(infoschema.TableDeadlocks), + strings.ToLower(infoschema.ClusterTableDeadlocks): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &deadlocksTableRetriever{ + table: v.Table, + columns: v.Columns, + }, + } + case strings.ToLower(infoschema.TableStatementsSummary), + strings.ToLower(infoschema.TableStatementsSummaryHistory), + strings.ToLower(infoschema.TableStatementsSummaryEvicted), + strings.ToLower(infoschema.ClusterTableStatementsSummary), + strings.ToLower(infoschema.ClusterTableStatementsSummaryHistory), + strings.ToLower(infoschema.ClusterTableStatementsSummaryEvicted): + var extractor *plannercore.StatementsSummaryExtractor + if v.Extractor != nil { + extractor = v.Extractor.(*plannercore.StatementsSummaryExtractor) + } + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: buildStmtSummaryRetriever(v.Table, v.Columns, extractor), + } + case strings.ToLower(infoschema.TableColumns): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &hugeMemTableRetriever{ + table: v.Table, + columns: v.Columns, + extractor: v.Extractor.(*plannercore.ColumnsTableExtractor), + viewSchemaMap: make(map[int64]*expression.Schema), + viewOutputNamesMap: make(map[int64]types.NameSlice), + }, + } + case strings.ToLower(infoschema.TableSlowQuery), strings.ToLower(infoschema.ClusterTableSlowLog): + memTracker := memory.NewTracker(v.ID(), -1) + memTracker.AttachTo(b.ctx.GetSessionVars().StmtCtx.MemTracker) + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &slowQueryRetriever{ + table: v.Table, + outputCols: v.Columns, + extractor: v.Extractor.(*plannercore.SlowQueryExtractor), + memTracker: memTracker, + }, + } + case strings.ToLower(infoschema.TableStorageStats): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &tableStorageStatsRetriever{ + table: v.Table, + outputCols: v.Columns, + extractor: v.Extractor.(*plannercore.TableStorageStatsExtractor), + }, + } + case strings.ToLower(infoschema.TableDDLJobs): + loc := b.ctx.GetSessionVars().Location() + ddlJobRetriever := DDLJobRetriever{TZLoc: loc} + return &DDLJobsReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + is: b.is, + DDLJobRetriever: ddlJobRetriever, + } + case strings.ToLower(infoschema.TableTiFlashTables), + strings.ToLower(infoschema.TableTiFlashSegments): + return &MemTableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.Table, + retriever: &TiFlashSystemTableRetriever{ + table: v.Table, + outputCols: v.Columns, + extractor: v.Extractor.(*plannercore.TiFlashSystemTableExtractor), + }, + } + } + } + tb, _ := b.is.TableByID(v.Table.ID) + return &TableScanExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + t: tb, + columns: v.Columns, + } +} + +func (b *executorBuilder) buildSort(v *plannercore.PhysicalSort) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + sortExec := SortExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), + ByItems: v.ByItems, + schema: v.Schema(), + } + executor_metrics.ExecutorCounterSortExec.Inc() + return &sortExec +} + +func (b *executorBuilder) buildTopN(v *plannercore.PhysicalTopN) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + sortExec := SortExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec), + ByItems: v.ByItems, + schema: v.Schema(), + } + executor_metrics.ExecutorCounterTopNExec.Inc() + return &TopNExec{ + SortExec: sortExec, + limit: &plannercore.PhysicalLimit{Count: v.Count, Offset: v.Offset}, + } +} + +func (b *executorBuilder) buildApply(v *plannercore.PhysicalApply) exec.Executor { + var ( + innerPlan plannercore.PhysicalPlan + outerPlan plannercore.PhysicalPlan + ) + if v.InnerChildIdx == 0 { + innerPlan = v.Children()[0] + outerPlan = v.Children()[1] + } else { + innerPlan = v.Children()[1] + outerPlan = v.Children()[0] + } + v.OuterSchema = plannercore.ExtractCorColumnsBySchema4PhysicalPlan(innerPlan, outerPlan.Schema()) + leftChild := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + rightChild := b.build(v.Children()[1]) + if b.err != nil { + return nil + } + // test is in the explain/naaj.test#part5. + // although we prepared the NAEqualConditions, but for Apply mode, we still need move it to other conditions like eq condition did here. + otherConditions := append(expression.ScalarFuncs2Exprs(v.EqualConditions), expression.ScalarFuncs2Exprs(v.NAEqualConditions)...) + otherConditions = append(otherConditions, v.OtherConditions...) + defaultValues := v.DefaultValues + if defaultValues == nil { + defaultValues = make([]types.Datum, v.Children()[v.InnerChildIdx].Schema().Len()) + } + outerExec, innerExec := leftChild, rightChild + outerFilter, innerFilter := v.LeftConditions, v.RightConditions + if v.InnerChildIdx == 0 { + outerExec, innerExec = rightChild, leftChild + outerFilter, innerFilter = v.RightConditions, v.LeftConditions + } + tupleJoiner := newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, + defaultValues, otherConditions, exec.RetTypes(leftChild), exec.RetTypes(rightChild), nil, false) + serialExec := &NestedLoopApplyExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec, innerExec), + innerExec: innerExec, + outerExec: outerExec, + outerFilter: outerFilter, + innerFilter: innerFilter, + outer: v.JoinType != plannercore.InnerJoin, + joiner: tupleJoiner, + outerSchema: v.OuterSchema, + ctx: b.ctx, + canUseCache: v.CanUseCache, + } + executor_metrics.ExecutorCounterNestedLoopApplyExec.Inc() + + // try parallel mode + if v.Concurrency > 1 { + innerExecs := make([]exec.Executor, 0, v.Concurrency) + innerFilters := make([]expression.CNFExprs, 0, v.Concurrency) + corCols := make([][]*expression.CorrelatedColumn, 0, v.Concurrency) + joiners := make([]joiner, 0, v.Concurrency) + for i := 0; i < v.Concurrency; i++ { + clonedInnerPlan, err := plannercore.SafeClone(innerPlan) + if err != nil { + b.err = nil + return serialExec + } + corCol := plannercore.ExtractCorColumnsBySchema4PhysicalPlan(clonedInnerPlan, outerPlan.Schema()) + clonedInnerExec := b.build(clonedInnerPlan) + if b.err != nil { + b.err = nil + return serialExec + } + innerExecs = append(innerExecs, clonedInnerExec) + corCols = append(corCols, corCol) + innerFilters = append(innerFilters, innerFilter.Clone()) + joiners = append(joiners, newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, + defaultValues, otherConditions, exec.RetTypes(leftChild), exec.RetTypes(rightChild), nil, false)) + } + + allExecs := append([]exec.Executor{outerExec}, innerExecs...) + + return &ParallelNestedLoopApplyExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), allExecs...), + innerExecs: innerExecs, + outerExec: outerExec, + outerFilter: outerFilter, + innerFilter: innerFilters, + outer: v.JoinType != plannercore.InnerJoin, + joiners: joiners, + corCols: corCols, + concurrency: v.Concurrency, + useCache: v.CanUseCache, + } + } + return serialExec +} + +func (b *executorBuilder) buildMaxOneRow(v *plannercore.PhysicalMaxOneRow) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) + base.SetInitCap(2) + base.SetMaxChunkSize(2) + e := &MaxOneRowExec{BaseExecutor: base} + return e +} + +func (b *executorBuilder) buildUnionAll(v *plannercore.PhysicalUnionAll) exec.Executor { + childExecs := make([]exec.Executor, len(v.Children())) + for i, child := range v.Children() { + childExecs[i] = b.build(child) + if b.err != nil { + return nil + } + } + e := &UnionExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExecs...), + concurrency: b.ctx.GetSessionVars().UnionConcurrency(), + } + return e +} + +func buildHandleColsForSplit(sc *stmtctx.StatementContext, tbInfo *model.TableInfo) plannercore.HandleCols { + if tbInfo.IsCommonHandle { + primaryIdx := tables.FindPrimaryIndex(tbInfo) + tableCols := make([]*expression.Column, len(tbInfo.Columns)) + for i, col := range tbInfo.Columns { + tableCols[i] = &expression.Column{ + ID: col.ID, + RetType: &col.FieldType, + } + } + for i, pkCol := range primaryIdx.Columns { + tableCols[pkCol.Offset].Index = i + } + return plannercore.NewCommonHandleCols(sc, tbInfo, primaryIdx, tableCols) + } + intCol := &expression.Column{ + RetType: types.NewFieldType(mysql.TypeLonglong), + } + return plannercore.NewIntHandleCols(intCol) +} + +func (b *executorBuilder) buildSplitRegion(v *plannercore.SplitRegion) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(1) + base.SetMaxChunkSize(1) + if v.IndexInfo != nil { + return &SplitIndexRegionExec{ + BaseExecutor: base, + tableInfo: v.TableInfo, + partitionNames: v.PartitionNames, + indexInfo: v.IndexInfo, + lower: v.Lower, + upper: v.Upper, + num: v.Num, + valueLists: v.ValueLists, + } + } + handleCols := buildHandleColsForSplit(b.ctx.GetSessionVars().StmtCtx, v.TableInfo) + if len(v.ValueLists) > 0 { + return &SplitTableRegionExec{ + BaseExecutor: base, + tableInfo: v.TableInfo, + partitionNames: v.PartitionNames, + handleCols: handleCols, + valueLists: v.ValueLists, + } + } + return &SplitTableRegionExec{ + BaseExecutor: base, + tableInfo: v.TableInfo, + partitionNames: v.PartitionNames, + handleCols: handleCols, + lower: v.Lower, + upper: v.Upper, + num: v.Num, + } +} + +func (b *executorBuilder) buildUpdate(v *plannercore.Update) exec.Executor { + b.inUpdateStmt = true + tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) + multiUpdateOnSameTable := make(map[int64]bool) + for _, info := range v.TblColPosInfos { + tbl, _ := b.is.TableByID(info.TblID) + if _, ok := tblID2table[info.TblID]; ok { + multiUpdateOnSameTable[info.TblID] = true + } + tblID2table[info.TblID] = tbl + if len(v.PartitionedTable) > 0 { + // The v.PartitionedTable collects the partitioned table. + // Replace the original table with the partitioned table to support partition selection. + // e.g. update t partition (p0, p1), the new values are not belong to the given set p0, p1 + // Using the table in v.PartitionedTable returns a proper error, while using the original table can't. + for _, p := range v.PartitionedTable { + if info.TblID == p.Meta().ID { + tblID2table[info.TblID] = p + } + } + } + } + if b.err = b.updateForUpdateTS(); b.err != nil { + return nil + } + + selExec := b.build(v.SelectPlan) + if b.err != nil { + return nil + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), selExec) + base.SetInitCap(chunk.ZeroCapacity) + var assignFlag []int + assignFlag, b.err = getAssignFlag(b.ctx, v, selExec.Schema().Len()) + if b.err != nil { + return nil + } + // should use the new tblID2table, since the update's schema may have been changed in Execstmt. + b.err = plannercore.CheckUpdateList(assignFlag, v, tblID2table) + if b.err != nil { + return nil + } + updateExec := &UpdateExec{ + BaseExecutor: base, + OrderedList: v.OrderedList, + allAssignmentsAreConstant: v.AllAssignmentsAreConstant, + virtualAssignmentsOffset: v.VirtualAssignmentsOffset, + multiUpdateOnSameTable: multiUpdateOnSameTable, + tblID2table: tblID2table, + tblColPosInfos: v.TblColPosInfos, + assignFlag: assignFlag, + } + updateExec.fkChecks, b.err = buildTblID2FKCheckExecs(b.ctx, tblID2table, v.FKChecks) + if b.err != nil { + return nil + } + updateExec.fkCascades, b.err = b.buildTblID2FKCascadeExecs(tblID2table, v.FKCascades) + if b.err != nil { + return nil + } + return updateExec +} + +func getAssignFlag(ctx sessionctx.Context, v *plannercore.Update, schemaLen int) ([]int, error) { + assignFlag := make([]int, schemaLen) + for i := range assignFlag { + assignFlag[i] = -1 + } + for _, assign := range v.OrderedList { + if !ctx.GetSessionVars().AllowWriteRowID && assign.Col.ID == model.ExtraHandleID { + return nil, errors.Errorf("insert, update and replace statements for _tidb_rowid are not supported") + } + tblIdx, found := v.TblColPosInfos.FindTblIdx(assign.Col.Index) + if found { + colIdx := assign.Col.Index + assignFlag[colIdx] = tblIdx + } + } + return assignFlag, nil +} + +func (b *executorBuilder) buildDelete(v *plannercore.Delete) exec.Executor { + b.inDeleteStmt = true + tblID2table := make(map[int64]table.Table, len(v.TblColPosInfos)) + for _, info := range v.TblColPosInfos { + tblID2table[info.TblID], _ = b.is.TableByID(info.TblID) + } + + if b.err = b.updateForUpdateTS(); b.err != nil { + return nil + } + + selExec := b.build(v.SelectPlan) + if b.err != nil { + return nil + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), selExec) + base.SetInitCap(chunk.ZeroCapacity) + deleteExec := &DeleteExec{ + BaseExecutor: base, + tblID2Table: tblID2table, + IsMultiTable: v.IsMultiTable, + tblColPosInfos: v.TblColPosInfos, + } + deleteExec.fkChecks, b.err = buildTblID2FKCheckExecs(b.ctx, tblID2table, v.FKChecks) + if b.err != nil { + return nil + } + deleteExec.fkCascades, b.err = b.buildTblID2FKCascadeExecs(tblID2table, v.FKCascades) + if b.err != nil { + return nil + } + return deleteExec +} + +func (b *executorBuilder) updateForUpdateTS() error { + // GetStmtForUpdateTS will auto update the for update ts if it is necessary + _, err := sessiontxn.GetTxnManager(b.ctx).GetStmtForUpdateTS() + return err +} + +func (b *executorBuilder) buildAnalyzeIndexPushdown(task plannercore.AnalyzeIndexTask, opts map[ast.AnalyzeOptionType]uint64, autoAnalyze string) *analyzeTask { + job := &statistics.AnalyzeJob{DBName: task.DBName, TableName: task.TableName, PartitionName: task.PartitionName, JobInfo: autoAnalyze + "analyze index " + task.IndexInfo.Name.O} + _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) + sc := b.ctx.GetSessionVars().StmtCtx + startTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { + startTS = uint64(val.(int)) + }) + + base := baseAnalyzeExec{ + ctx: b.ctx, + tableID: task.TableID, + concurrency: b.ctx.GetSessionVars().IndexSerialScanConcurrency(), + analyzePB: &tipb.AnalyzeReq{ + Tp: tipb.AnalyzeType_TypeIndex, + Flags: sc.PushDownFlags(), + TimeZoneOffset: offset, + }, + opts: opts, + job: job, + snapshot: startTS, + } + e := &AnalyzeIndexExec{ + baseAnalyzeExec: base, + isCommonHandle: task.TblInfo.IsCommonHandle, + idxInfo: task.IndexInfo, + } + topNSize := new(int32) + *topNSize = int32(opts[ast.AnalyzeOptNumTopN]) + statsVersion := new(int32) + *statsVersion = int32(task.StatsVersion) + e.analyzePB.IdxReq = &tipb.AnalyzeIndexReq{ + BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), + NumColumns: int32(len(task.IndexInfo.Columns)), + TopNSize: topNSize, + Version: statsVersion, + SketchSize: maxSketchSize, + } + if e.isCommonHandle && e.idxInfo.Primary { + e.analyzePB.Tp = tipb.AnalyzeType_TypeCommonHandle + } + depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) + width := int32(opts[ast.AnalyzeOptCMSketchWidth]) + e.analyzePB.IdxReq.CmsketchDepth = &depth + e.analyzePB.IdxReq.CmsketchWidth = &width + return &analyzeTask{taskType: idxTask, idxExec: e, job: job} +} + +func (b *executorBuilder) buildAnalyzeSamplingPushdown( + task plannercore.AnalyzeColumnsTask, + opts map[ast.AnalyzeOptionType]uint64, + schemaForVirtualColEval *expression.Schema, +) *analyzeTask { + if task.V2Options != nil { + opts = task.V2Options.FilledOpts + } + availableIdx := make([]*model.IndexInfo, 0, len(task.Indexes)) + colGroups := make([]*tipb.AnalyzeColumnGroup, 0, len(task.Indexes)) + if len(task.Indexes) > 0 { + for _, idx := range task.Indexes { + availableIdx = append(availableIdx, idx) + colGroup := &tipb.AnalyzeColumnGroup{ + ColumnOffsets: make([]int64, 0, len(idx.Columns)), + } + for _, col := range idx.Columns { + colGroup.ColumnOffsets = append(colGroup.ColumnOffsets, int64(col.Offset)) + } + colGroups = append(colGroups, colGroup) + } + } + + _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) + sc := b.ctx.GetSessionVars().StmtCtx + startTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { + startTS = uint64(val.(int)) + }) + statsHandle := domain.GetDomain(b.ctx).StatsHandle() + count, modifyCount, err := statsHandle.StatsMetaCountAndModifyCount(task.TableID.GetStatisticsID()) + if err != nil { + b.err = err + return nil + } + failpoint.Inject("injectBaseCount", func(val failpoint.Value) { + count = int64(val.(int)) + }) + failpoint.Inject("injectBaseModifyCount", func(val failpoint.Value) { + modifyCount = int64(val.(int)) + }) + sampleRate := new(float64) + var sampleRateReason string + if opts[ast.AnalyzeOptNumSamples] == 0 { + *sampleRate = math.Float64frombits(opts[ast.AnalyzeOptSampleRate]) + if *sampleRate < 0 { + *sampleRate, sampleRateReason = b.getAdjustedSampleRate(task) + if task.PartitionName != "" { + sc.AppendNote(errors.Errorf( + `Analyze use auto adjusted sample rate %f for table %s.%s's partition %s, reason to use this rate is "%s"`, + *sampleRate, + task.DBName, + task.TableName, + task.PartitionName, + sampleRateReason, + )) + } else { + sc.AppendNote(errors.Errorf( + `Analyze use auto adjusted sample rate %f for table %s.%s, reason to use this rate is "%s"`, + *sampleRate, + task.DBName, + task.TableName, + sampleRateReason, + )) + } + } + } + job := &statistics.AnalyzeJob{ + DBName: task.DBName, + TableName: task.TableName, + PartitionName: task.PartitionName, + SampleRateReason: sampleRateReason, + } + + base := baseAnalyzeExec{ + ctx: b.ctx, + tableID: task.TableID, + concurrency: b.ctx.GetSessionVars().DistSQLScanConcurrency(), + analyzePB: &tipb.AnalyzeReq{ + Tp: tipb.AnalyzeType_TypeFullSampling, + Flags: sc.PushDownFlags(), + TimeZoneOffset: offset, + }, + opts: opts, + job: job, + snapshot: startTS, + } + e := &AnalyzeColumnsExec{ + baseAnalyzeExec: base, + tableInfo: task.TblInfo, + colsInfo: task.ColsInfo, + handleCols: task.HandleCols, + indexes: availableIdx, + AnalyzeInfo: task.AnalyzeInfo, + schemaForVirtualColEval: schemaForVirtualColEval, + baseCount: count, + baseModifyCnt: modifyCount, + } + e.analyzePB.ColReq = &tipb.AnalyzeColumnsReq{ + BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), + SampleSize: int64(opts[ast.AnalyzeOptNumSamples]), + SampleRate: sampleRate, + SketchSize: maxSketchSize, + ColumnsInfo: util.ColumnsToProto(task.ColsInfo, task.TblInfo.PKIsHandle, false), + ColumnGroups: colGroups, + } + if task.TblInfo != nil { + e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) + if task.TblInfo.IsCommonHandle { + e.analyzePB.ColReq.PrimaryPrefixColumnIds = tables.PrimaryPrefixColumnIDs(task.TblInfo) + } + } + b.err = tables.SetPBColumnsDefaultValue(b.ctx, e.analyzePB.ColReq.ColumnsInfo, task.ColsInfo) + return &analyzeTask{taskType: colTask, colExec: e, job: job} +} + +// getAdjustedSampleRate calculate the sample rate by the table size. If we cannot get the table size. We use the 0.001 as the default sample rate. +// From the paper "Random sampling for histogram construction: how much is enough?"'s Corollary 1 to Theorem 5, +// for a table size n, histogram size k, maximum relative error in bin size f, and error probability gamma, +// the minimum random sample size is +// +// r = 4 * k * ln(2*n/gamma) / f^2 +// +// If we take f = 0.5, gamma = 0.01, n =1e6, we would got r = 305.82* k. +// Since the there's log function over the table size n, the r grows slowly when the n increases. +// If we take n = 1e12, a 300*k sample still gives <= 0.66 bin size error with probability 0.99. +// So if we don't consider the top-n values, we can keep the sample size at 300*256. +// But we may take some top-n before building the histogram, so we increase the sample a little. +func (b *executorBuilder) getAdjustedSampleRate(task plannercore.AnalyzeColumnsTask) (sampleRate float64, reason string) { + statsHandle := domain.GetDomain(b.ctx).StatsHandle() + defaultRate := 0.001 + if statsHandle == nil { + return defaultRate, fmt.Sprintf("statsHandler is nil, use the default-rate=%v", defaultRate) + } + var statsTbl *statistics.Table + tid := task.TableID.GetStatisticsID() + if tid == task.TblInfo.ID { + statsTbl = statsHandle.GetTableStats(task.TblInfo) + } else { + statsTbl = statsHandle.GetPartitionStats(task.TblInfo, tid) + } + approxiCount, hasPD := b.getApproximateTableCountFromStorage(tid, task) + // If there's no stats meta and no pd, return the default rate. + if statsTbl == nil && !hasPD { + return defaultRate, fmt.Sprintf("TiDB cannot get the row count of the table, use the default-rate=%v", defaultRate) + } + // If the count in stats_meta is still 0 and there's no information from pd side, we scan all rows. + if statsTbl.RealtimeCount == 0 && !hasPD { + return 1, "TiDB assumes that the table is empty and cannot get row count from PD, use sample-rate=1" + } + // we have issue https://github.com/pingcap/tidb/issues/29216. + // To do a workaround for this issue, we check the approxiCount from the pd side to do a comparison. + // If the count from the stats_meta is extremely smaller than the approximate count from the pd, + // we think that we meet this issue and use the approximate count to calculate the sample rate. + if float64(statsTbl.RealtimeCount*5) < approxiCount { + // Confirmed by TiKV side, the experience error rate of the approximate count is about 20%. + // So we increase the number to 150000 to reduce this error rate. + sampleRate = math.Min(1, 150000/approxiCount) + return sampleRate, fmt.Sprintf("Row count in stats_meta is much smaller compared with the row count got by PD, use min(1, 15000/%v) as the sample-rate=%v", approxiCount, sampleRate) + } + // If we don't go into the above if branch and we still detect the count is zero. Return 1 to prevent the dividing zero. + if statsTbl.RealtimeCount == 0 { + return 1, "TiDB assumes that the table is empty, use sample-rate=1" + } + // We are expected to scan about 100000 rows or so. + // Since there's tiny error rate around the count from the stats meta, we use 110000 to get a little big result + sampleRate = math.Min(1, config.DefRowsForSampleRate/float64(statsTbl.RealtimeCount)) + return sampleRate, fmt.Sprintf("use min(1, %v/%v) as the sample-rate=%v", config.DefRowsForSampleRate, statsTbl.RealtimeCount, sampleRate) +} + +func (b *executorBuilder) getApproximateTableCountFromStorage(tid int64, task plannercore.AnalyzeColumnsTask) (float64, bool) { + return pdhelper.GlobalPDHelper.GetApproximateTableCountFromStorage(b.ctx, tid, task.DBName, task.TableName, task.PartitionName) +} + +func (b *executorBuilder) buildAnalyzeColumnsPushdown( + task plannercore.AnalyzeColumnsTask, + opts map[ast.AnalyzeOptionType]uint64, + autoAnalyze string, + schemaForVirtualColEval *expression.Schema, +) *analyzeTask { + if task.StatsVersion == statistics.Version2 { + return b.buildAnalyzeSamplingPushdown(task, opts, schemaForVirtualColEval) + } + job := &statistics.AnalyzeJob{DBName: task.DBName, TableName: task.TableName, PartitionName: task.PartitionName, JobInfo: autoAnalyze + "analyze columns"} + cols := task.ColsInfo + if hasPkHist(task.HandleCols) { + colInfo := task.TblInfo.Columns[task.HandleCols.GetCol(0).Index] + cols = append([]*model.ColumnInfo{colInfo}, cols...) + } else if task.HandleCols != nil && !task.HandleCols.IsInt() { + cols = make([]*model.ColumnInfo, 0, len(task.ColsInfo)+task.HandleCols.NumCols()) + for i := 0; i < task.HandleCols.NumCols(); i++ { + cols = append(cols, task.TblInfo.Columns[task.HandleCols.GetCol(i).Index]) + } + cols = append(cols, task.ColsInfo...) + task.ColsInfo = cols + } + + _, offset := timeutil.Zone(b.ctx.GetSessionVars().Location()) + sc := b.ctx.GetSessionVars().StmtCtx + startTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + failpoint.Inject("injectAnalyzeSnapshot", func(val failpoint.Value) { + startTS = uint64(val.(int)) + }) + + base := baseAnalyzeExec{ + ctx: b.ctx, + tableID: task.TableID, + concurrency: b.ctx.GetSessionVars().DistSQLScanConcurrency(), + analyzePB: &tipb.AnalyzeReq{ + Tp: tipb.AnalyzeType_TypeColumn, + Flags: sc.PushDownFlags(), + TimeZoneOffset: offset, + }, + opts: opts, + job: job, + snapshot: startTS, + } + e := &AnalyzeColumnsExec{ + baseAnalyzeExec: base, + colsInfo: task.ColsInfo, + handleCols: task.HandleCols, + AnalyzeInfo: task.AnalyzeInfo, + } + depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) + width := int32(opts[ast.AnalyzeOptCMSketchWidth]) + e.analyzePB.ColReq = &tipb.AnalyzeColumnsReq{ + BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), + SampleSize: MaxRegionSampleSize, + SketchSize: maxSketchSize, + ColumnsInfo: util.ColumnsToProto(cols, task.HandleCols != nil && task.HandleCols.IsInt(), false), + CmsketchDepth: &depth, + CmsketchWidth: &width, + } + if task.TblInfo != nil { + e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) + if task.TblInfo.IsCommonHandle { + e.analyzePB.ColReq.PrimaryPrefixColumnIds = tables.PrimaryPrefixColumnIDs(task.TblInfo) + } + } + if task.CommonHandleInfo != nil { + topNSize := new(int32) + *topNSize = int32(opts[ast.AnalyzeOptNumTopN]) + statsVersion := new(int32) + *statsVersion = int32(task.StatsVersion) + e.analyzePB.IdxReq = &tipb.AnalyzeIndexReq{ + BucketSize: int64(opts[ast.AnalyzeOptNumBuckets]), + NumColumns: int32(len(task.CommonHandleInfo.Columns)), + TopNSize: topNSize, + Version: statsVersion, + } + depth := int32(opts[ast.AnalyzeOptCMSketchDepth]) + width := int32(opts[ast.AnalyzeOptCMSketchWidth]) + e.analyzePB.IdxReq.CmsketchDepth = &depth + e.analyzePB.IdxReq.CmsketchWidth = &width + e.analyzePB.IdxReq.SketchSize = maxSketchSize + e.analyzePB.ColReq.PrimaryColumnIds = tables.TryGetCommonPkColumnIds(task.TblInfo) + e.analyzePB.Tp = tipb.AnalyzeType_TypeMixed + e.commonHandle = task.CommonHandleInfo + } + b.err = tables.SetPBColumnsDefaultValue(b.ctx, e.analyzePB.ColReq.ColumnsInfo, cols) + return &analyzeTask{taskType: colTask, colExec: e, job: job} +} + +func (b *executorBuilder) buildAnalyze(v *plannercore.Analyze) exec.Executor { + e := &AnalyzeExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + tasks: make([]*analyzeTask, 0, len(v.ColTasks)+len(v.IdxTasks)), + opts: v.Opts, + OptionsMap: v.OptionsMap, + } + autoAnalyze := "" + if b.ctx.GetSessionVars().InRestrictedSQL { + autoAnalyze = "auto " + } + for _, task := range v.ColTasks { + columns, _, err := expression.ColumnInfos2ColumnsAndNames( + b.ctx, + model.NewCIStr(task.AnalyzeInfo.DBName), + task.TblInfo.Name, + task.ColsInfo, + task.TblInfo, + ) + if err != nil { + b.err = err + return nil + } + schema := expression.NewSchema(columns...) + e.tasks = append(e.tasks, b.buildAnalyzeColumnsPushdown(task, v.Opts, autoAnalyze, schema)) + // Other functions may set b.err, so we need to check it here. + if b.err != nil { + return nil + } + } + for _, task := range v.IdxTasks { + e.tasks = append(e.tasks, b.buildAnalyzeIndexPushdown(task, v.Opts, autoAnalyze)) + if b.err != nil { + return nil + } + } + return e +} + +// markChildrenUsedCols compares each child with the output schema, and mark +// each column of the child is used by output or not. +func markChildrenUsedCols(outputCols []*expression.Column, childSchemas ...*expression.Schema) (childrenUsed [][]bool) { + childrenUsed = make([][]bool, 0, len(childSchemas)) + markedOffsets := make(map[int]struct{}) + for _, col := range outputCols { + markedOffsets[col.Index] = struct{}{} + } + prefixLen := 0 + for _, childSchema := range childSchemas { + used := make([]bool, len(childSchema.Columns)) + for i := range childSchema.Columns { + if _, ok := markedOffsets[prefixLen+i]; ok { + used[i] = true + } + } + childrenUsed = append(childrenUsed, used) + prefixLen += childSchema.Len() + } + return +} + +func (*executorBuilder) corColInDistPlan(plans []plannercore.PhysicalPlan) bool { + for _, p := range plans { + x, ok := p.(*plannercore.PhysicalSelection) + if !ok { + continue + } + for _, cond := range x.Conditions { + if len(expression.ExtractCorColumns(cond)) > 0 { + return true + } + } + } + return false +} + +// corColInAccess checks whether there's correlated column in access conditions. +func (*executorBuilder) corColInAccess(p plannercore.PhysicalPlan) bool { + var access []expression.Expression + switch x := p.(type) { + case *plannercore.PhysicalTableScan: + access = x.AccessCondition + case *plannercore.PhysicalIndexScan: + access = x.AccessCondition + } + for _, cond := range access { + if len(expression.ExtractCorColumns(cond)) > 0 { + return true + } + } + return false +} + +func (b *executorBuilder) newDataReaderBuilder(p plannercore.PhysicalPlan) (*dataReaderBuilder, error) { + ts, err := b.getSnapshotTS() + if err != nil { + return nil, err + } + + builderForDataReader := *b + builderForDataReader.forDataReaderBuilder = true + builderForDataReader.dataReaderTS = ts + + return &dataReaderBuilder{ + Plan: p, + executorBuilder: &builderForDataReader, + }, nil +} + +func (b *executorBuilder) buildIndexLookUpJoin(v *plannercore.PhysicalIndexJoin) exec.Executor { + outerExec := b.build(v.Children()[1-v.InnerChildIdx]) + if b.err != nil { + return nil + } + outerTypes := exec.RetTypes(outerExec) + innerPlan := v.Children()[v.InnerChildIdx] + innerTypes := make([]*types.FieldType, innerPlan.Schema().Len()) + for i, col := range innerPlan.Schema().Columns { + innerTypes[i] = col.RetType.Clone() + // The `innerTypes` would be called for `Datum.ConvertTo` when converting the columns from outer table + // to build hash map or construct lookup keys. So we need to modify its flen otherwise there would be + // truncate error. See issue https://github.com/pingcap/tidb/issues/21232 for example. + if innerTypes[i].EvalType() == types.ETString { + innerTypes[i].SetFlen(types.UnspecifiedLength) + } + } + + // Use the probe table's collation. + for i, col := range v.OuterHashKeys { + outerTypes[col.Index] = outerTypes[col.Index].Clone() + outerTypes[col.Index].SetCollate(innerTypes[v.InnerHashKeys[i].Index].GetCollate()) + outerTypes[col.Index].SetFlag(col.RetType.GetFlag()) + } + + // We should use JoinKey to construct the type information using by hashing, instead of using the child's schema directly. + // When a hybrid type column is hashed multiple times, we need to distinguish what field types are used. + // For example, the condition `enum = int and enum = string`, we should use ETInt to hash the first column, + // and use ETString to hash the second column, although they may be the same column. + innerHashTypes := make([]*types.FieldType, len(v.InnerHashKeys)) + outerHashTypes := make([]*types.FieldType, len(v.OuterHashKeys)) + for i, col := range v.InnerHashKeys { + innerHashTypes[i] = innerTypes[col.Index].Clone() + innerHashTypes[i].SetFlag(col.RetType.GetFlag()) + } + for i, col := range v.OuterHashKeys { + outerHashTypes[i] = outerTypes[col.Index].Clone() + outerHashTypes[i].SetFlag(col.RetType.GetFlag()) + } + + var ( + outerFilter []expression.Expression + leftTypes, rightTypes []*types.FieldType + ) + + if v.InnerChildIdx == 0 { + leftTypes, rightTypes = innerTypes, outerTypes + outerFilter = v.RightConditions + if len(v.LeftConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } else { + leftTypes, rightTypes = outerTypes, innerTypes + outerFilter = v.LeftConditions + if len(v.RightConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } + defaultValues := v.DefaultValues + if defaultValues == nil { + defaultValues = make([]types.Datum, len(innerTypes)) + } + hasPrefixCol := false + for _, l := range v.IdxColLens { + if l != types.UnspecifiedLength { + hasPrefixCol = true + break + } + } + + readerBuilder, err := b.newDataReaderBuilder(innerPlan) + if err != nil { + b.err = err + return nil + } + + e := &IndexLookUpJoin{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec), + outerCtx: outerCtx{ + rowTypes: outerTypes, + hashTypes: outerHashTypes, + filter: outerFilter, + }, + innerCtx: innerCtx{ + readerBuilder: readerBuilder, + rowTypes: innerTypes, + hashTypes: innerHashTypes, + colLens: v.IdxColLens, + hasPrefixCol: hasPrefixCol, + }, + workerWg: new(sync.WaitGroup), + isOuterJoin: v.JoinType.IsOuterJoin(), + indexRanges: v.Ranges, + keyOff2IdxOff: v.KeyOff2IdxOff, + lastColHelper: v.CompareFilters, + finished: &atomic.Value{}, + } + colsFromChildren := v.Schema().Columns + if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { + colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] + } + childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) + e.joiner = newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, leftTypes, rightTypes, childrenUsedSchema, false) + outerKeyCols := make([]int, len(v.OuterJoinKeys)) + for i := 0; i < len(v.OuterJoinKeys); i++ { + outerKeyCols[i] = v.OuterJoinKeys[i].Index + } + innerKeyCols := make([]int, len(v.InnerJoinKeys)) + innerKeyColIDs := make([]int64, len(v.InnerJoinKeys)) + keyCollators := make([]collate.Collator, 0, len(v.InnerJoinKeys)) + for i := 0; i < len(v.InnerJoinKeys); i++ { + innerKeyCols[i] = v.InnerJoinKeys[i].Index + innerKeyColIDs[i] = v.InnerJoinKeys[i].ID + keyCollators = append(keyCollators, collate.GetCollator(v.InnerJoinKeys[i].RetType.GetCollate())) + } + e.outerCtx.keyCols = outerKeyCols + e.innerCtx.keyCols = innerKeyCols + e.innerCtx.keyColIDs = innerKeyColIDs + e.innerCtx.keyCollators = keyCollators + + outerHashCols, innerHashCols := make([]int, len(v.OuterHashKeys)), make([]int, len(v.InnerHashKeys)) + hashCollators := make([]collate.Collator, 0, len(v.InnerHashKeys)) + for i := 0; i < len(v.OuterHashKeys); i++ { + outerHashCols[i] = v.OuterHashKeys[i].Index + } + for i := 0; i < len(v.InnerHashKeys); i++ { + innerHashCols[i] = v.InnerHashKeys[i].Index + hashCollators = append(hashCollators, collate.GetCollator(v.InnerHashKeys[i].RetType.GetCollate())) + } + e.outerCtx.hashCols = outerHashCols + e.innerCtx.hashCols = innerHashCols + e.innerCtx.hashCollators = hashCollators + + e.joinResult = exec.TryNewCacheChunk(e) + executor_metrics.ExecutorCounterIndexLookUpJoin.Inc() + return e +} + +func (b *executorBuilder) buildIndexLookUpMergeJoin(v *plannercore.PhysicalIndexMergeJoin) exec.Executor { + outerExec := b.build(v.Children()[1-v.InnerChildIdx]) + if b.err != nil { + return nil + } + outerTypes := exec.RetTypes(outerExec) + innerPlan := v.Children()[v.InnerChildIdx] + innerTypes := make([]*types.FieldType, innerPlan.Schema().Len()) + for i, col := range innerPlan.Schema().Columns { + innerTypes[i] = col.RetType.Clone() + // The `innerTypes` would be called for `Datum.ConvertTo` when converting the columns from outer table + // to build hash map or construct lookup keys. So we need to modify its flen otherwise there would be + // truncate error. See issue https://github.com/pingcap/tidb/issues/21232 for example. + if innerTypes[i].EvalType() == types.ETString { + innerTypes[i].SetFlen(types.UnspecifiedLength) + } + } + var ( + outerFilter []expression.Expression + leftTypes, rightTypes []*types.FieldType + ) + if v.InnerChildIdx == 0 { + leftTypes, rightTypes = innerTypes, outerTypes + outerFilter = v.RightConditions + if len(v.LeftConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } else { + leftTypes, rightTypes = outerTypes, innerTypes + outerFilter = v.LeftConditions + if len(v.RightConditions) > 0 { + b.err = errors.Annotate(exeerrors.ErrBuildExecutor, "join's inner condition should be empty") + return nil + } + } + defaultValues := v.DefaultValues + if defaultValues == nil { + defaultValues = make([]types.Datum, len(innerTypes)) + } + outerKeyCols := make([]int, len(v.OuterJoinKeys)) + for i := 0; i < len(v.OuterJoinKeys); i++ { + outerKeyCols[i] = v.OuterJoinKeys[i].Index + } + innerKeyCols := make([]int, len(v.InnerJoinKeys)) + keyCollators := make([]collate.Collator, 0, len(v.InnerJoinKeys)) + for i := 0; i < len(v.InnerJoinKeys); i++ { + innerKeyCols[i] = v.InnerJoinKeys[i].Index + keyCollators = append(keyCollators, collate.GetCollator(v.InnerJoinKeys[i].RetType.GetCollate())) + } + executor_metrics.ExecutorCounterIndexLookUpJoin.Inc() + + readerBuilder, err := b.newDataReaderBuilder(innerPlan) + if err != nil { + b.err = err + return nil + } + + e := &IndexLookUpMergeJoin{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), outerExec), + outerMergeCtx: outerMergeCtx{ + rowTypes: outerTypes, + filter: outerFilter, + joinKeys: v.OuterJoinKeys, + keyCols: outerKeyCols, + needOuterSort: v.NeedOuterSort, + compareFuncs: v.OuterCompareFuncs, + }, + innerMergeCtx: innerMergeCtx{ + readerBuilder: readerBuilder, + rowTypes: innerTypes, + joinKeys: v.InnerJoinKeys, + keyCols: innerKeyCols, + keyCollators: keyCollators, + compareFuncs: v.CompareFuncs, + colLens: v.IdxColLens, + desc: v.Desc, + keyOff2KeyOffOrderByIdx: v.KeyOff2KeyOffOrderByIdx, + }, + workerWg: new(sync.WaitGroup), + isOuterJoin: v.JoinType.IsOuterJoin(), + indexRanges: v.Ranges, + keyOff2IdxOff: v.KeyOff2IdxOff, + lastColHelper: v.CompareFilters, + } + colsFromChildren := v.Schema().Columns + if v.JoinType == plannercore.LeftOuterSemiJoin || v.JoinType == plannercore.AntiLeftOuterSemiJoin { + colsFromChildren = colsFromChildren[:len(colsFromChildren)-1] + } + childrenUsedSchema := markChildrenUsedCols(colsFromChildren, v.Children()[0].Schema(), v.Children()[1].Schema()) + joiners := make([]joiner, e.Ctx().GetSessionVars().IndexLookupJoinConcurrency()) + for i := 0; i < len(joiners); i++ { + joiners[i] = newJoiner(b.ctx, v.JoinType, v.InnerChildIdx == 0, defaultValues, v.OtherConditions, leftTypes, rightTypes, childrenUsedSchema, false) + } + e.joiners = joiners + return e +} + +func (b *executorBuilder) buildIndexNestedLoopHashJoin(v *plannercore.PhysicalIndexHashJoin) exec.Executor { + join := b.buildIndexLookUpJoin(&(v.PhysicalIndexJoin)) + if b.err != nil { + return nil + } + e := join.(*IndexLookUpJoin) + idxHash := &IndexNestedLoopHashJoin{ + IndexLookUpJoin: *e, + keepOuterOrder: v.KeepOuterOrder, + } + concurrency := e.Ctx().GetSessionVars().IndexLookupJoinConcurrency() + idxHash.joiners = make([]joiner, concurrency) + for i := 0; i < concurrency; i++ { + idxHash.joiners[i] = e.joiner.Clone() + } + return idxHash +} + +func buildNoRangeTableReader(b *executorBuilder, v *plannercore.PhysicalTableReader) (*TableReaderExecutor, error) { + tablePlans := v.TablePlans + if v.StoreType == kv.TiFlash { + tablePlans = []plannercore.PhysicalPlan{v.GetTablePlan()} + } + dagReq, err := builder.ConstructDAGReq(b.ctx, tablePlans, v.StoreType) + if err != nil { + return nil, err + } + ts, err := v.GetTableScan() + if err != nil { + return nil, err + } + if err = b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { + return nil, err + } + + tbl, _ := b.is.TableByID(ts.Table.ID) + isPartition, physicalTableID := ts.IsPartition() + if isPartition { + pt := tbl.(table.PartitionedTable) + tbl = pt.GetPartition(physicalTableID) + } + startTS, err := b.getSnapshotTS() + if err != nil { + return nil, err + } + paging := b.ctx.GetSessionVars().EnablePaging + e := &TableReaderExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dagPB: dagReq, + startTS: startTS, + txnScope: b.txnScope, + readReplicaScope: b.readReplicaScope, + isStaleness: b.isStaleness, + netDataSize: v.GetNetDataSize(), + table: tbl, + keepOrder: ts.KeepOrder, + desc: ts.Desc, + byItems: ts.ByItems, + columns: ts.Columns, + paging: paging, + corColInFilter: b.corColInDistPlan(v.TablePlans), + corColInAccess: b.corColInAccess(v.TablePlans[0]), + plans: v.TablePlans, + tablePlan: v.GetTablePlan(), + storeType: v.StoreType, + batchCop: v.ReadReqType == plannercore.BatchCop, + } + e.buildVirtualColumnInfo() + + if v.StoreType == kv.TiDB && b.ctx.GetSessionVars().User != nil { + // User info is used to do privilege check. It is only used in TiDB cluster memory table. + e.dagPB.User = &tipb.UserIdentity{ + UserName: b.ctx.GetSessionVars().User.Username, + UserHost: b.ctx.GetSessionVars().User.Hostname, + } + } + + for i := range v.Schema().Columns { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(i)) + } + + return e, nil +} + +func (b *executorBuilder) buildMPPGather(v *plannercore.PhysicalTableReader) exec.Executor { + startTs, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + + gather := &MPPGather{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + is: b.is, + originalPlan: v.GetTablePlan(), + startTS: startTs, + mppQueryID: kv.MPPQueryID{QueryTs: getMPPQueryTS(b.ctx), LocalQueryID: getMPPQueryID(b.ctx), ServerID: domain.GetDomain(b.ctx).ServerID()}, + memTracker: memory.NewTracker(v.ID(), -1), + + columns: []*model.ColumnInfo{}, + virtualColumnIndex: []int{}, + virtualColumnRetFieldTypes: []*types.FieldType{}, + } + + gather.memTracker.AttachTo(b.ctx.GetSessionVars().StmtCtx.MemTracker) + + var hasVirtualCol bool + for _, col := range v.Schema().Columns { + if col.VirtualExpr != nil { + hasVirtualCol = true + break + } + } + + var isSingleDataSource bool + tableScans := v.GetTableScans() + if len(tableScans) == 1 { + isSingleDataSource = true + } + + // 1. hasVirtualCol: when got virtual column in TableScan, will generate plan like the following, + // and there will be no other operators in the MPP fragment. + // MPPGather + // ExchangeSender + // PhysicalTableScan + // 2. UnionScan: there won't be any operators like Join between UnionScan and TableScan. + // and UnionScan cannot push down to tiflash. + if !isSingleDataSource { + if hasVirtualCol || b.encounterUnionScan { + b.err = errors.Errorf("should only have one TableScan in MPP fragment(hasVirtualCol: %v, encounterUnionScan: %v)", hasVirtualCol, b.encounterUnionScan) + return nil + } + return gather + } + + // Setup MPPGather.table if isSingleDataSource. + // Virtual Column or UnionScan need to use it. + ts := tableScans[0] + gather.columns = ts.Columns + if hasVirtualCol { + gather.virtualColumnIndex, gather.virtualColumnRetFieldTypes = buildVirtualColumnInfo(gather.Schema(), gather.columns) + } + tbl, _ := b.is.TableByID(ts.Table.ID) + isPartition, physicalTableID := ts.IsPartition() + if isPartition { + // Only for static pruning partition table. + pt := tbl.(table.PartitionedTable) + tbl = pt.GetPartition(physicalTableID) + } + gather.table = tbl + return gather +} + +// buildTableReader builds a table reader executor. It first build a no range table reader, +// and then update it ranges from table scan plan. +func (b *executorBuilder) buildTableReader(v *plannercore.PhysicalTableReader) exec.Executor { + failpoint.Inject("checkUseMPP", func(val failpoint.Value) { + if !b.ctx.GetSessionVars().InRestrictedSQL && val.(bool) != useMPPExecution(b.ctx, v) { + if val.(bool) { + b.err = errors.New("expect mpp but not used") + } else { + b.err = errors.New("don't expect mpp but we used it") + } + failpoint.Return(nil) + } + }) + useMPP := useMPPExecution(b.ctx, v) + useTiFlashBatchCop := v.ReadReqType == plannercore.BatchCop + useTiFlash := useMPP || useTiFlashBatchCop + if useTiFlash { + if _, isTiDBZoneLabelSet := config.GetGlobalConfig().Labels[placement.DCLabelKey]; b.ctx.GetSessionVars().TiFlashReplicaRead != tiflash.AllReplicas && !isTiDBZoneLabelSet { + b.ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("the variable tiflash_replica_read is ignored, because the entry TiDB[%s] does not set the zone attribute and tiflash_replica_read is '%s'", config.GetGlobalConfig().AdvertiseAddress, tiflash.GetTiFlashReplicaRead(b.ctx.GetSessionVars().TiFlashReplicaRead))) + } + } + if useMPP { + return b.buildMPPGather(v) + } + ts, err := v.GetTableScan() + if err != nil { + b.err = err + return nil + } + ret, err := buildNoRangeTableReader(b, v) + if err != nil { + b.err = err + return nil + } + if err = b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { + b.err = err + return nil + } + + if ret.table.Meta().TempTableType != model.TempTableNone { + ret.dummy = true + } + + ret.ranges = ts.Ranges + sctx := b.ctx.GetSessionVars().StmtCtx + sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) + + if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + return ret + } + // When isPartition is set, it means the union rewriting is done, so a partition reader is preferred. + if ok, _ := ts.IsPartition(); ok { + return ret + } + + pi := ts.Table.GetPartitionInfo() + if pi == nil { + return ret + } + + tmp, _ := b.is.TableByID(ts.Table.ID) + tbl := tmp.(table.PartitionedTable) + partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) + if err != nil { + b.err = err + return nil + } + if v.StoreType == kv.TiFlash { + sctx.IsTiFlash.Store(true) + } + + if len(partitions) == 0 { + return &TableDualExec{BaseExecutor: *ret.Base()} + } + + // Sort the partition is necessary to make the final multiple partition key ranges ordered. + slices.SortFunc(partitions, func(i, j table.PhysicalTable) int { + return cmp.Compare(i.GetPhysicalID(), j.GetPhysicalID()) + }) + ret.kvRangeBuilder = kvRangeBuilderFromRangeAndPartition{ + sctx: b.ctx, + partitions: partitions, + } + + return ret +} + +func buildIndexRangeForEachPartition(ctx sessionctx.Context, usedPartitions []table.PhysicalTable, contentPos []int64, + lookUpContent []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager) (map[int64][]*ranger.Range, error) { + contentBucket := make(map[int64][]*indexJoinLookUpContent) + for _, p := range usedPartitions { + contentBucket[p.GetPhysicalID()] = make([]*indexJoinLookUpContent, 0, 8) + } + for i, pos := range contentPos { + if _, ok := contentBucket[pos]; ok { + contentBucket[pos] = append(contentBucket[pos], lookUpContent[i]) + } + } + nextRange := make(map[int64][]*ranger.Range) + for _, p := range usedPartitions { + ranges, err := buildRangesForIndexJoin(ctx, contentBucket[p.GetPhysicalID()], indexRanges, keyOff2IdxOff, cwc) + if err != nil { + return nil, err + } + nextRange[p.GetPhysicalID()] = ranges + } + return nextRange, nil +} + +func getPartitionKeyColOffsets(keyColIDs []int64, pt table.PartitionedTable) []int { + keyColOffsets := make([]int, len(keyColIDs)) + for i, colID := range keyColIDs { + offset := -1 + for j, col := range pt.Cols() { + if colID == col.ID { + offset = j + break + } + } + if offset == -1 { + return nil + } + keyColOffsets[i] = offset + } + + t, ok := pt.(interface { + PartitionExpr() *tables.PartitionExpr + }) + if !ok { + return nil + } + pe := t.PartitionExpr() + if pe == nil { + return nil + } + + offsetMap := make(map[int]struct{}) + for _, offset := range keyColOffsets { + offsetMap[offset] = struct{}{} + } + for _, offset := range pe.ColumnOffset { + if _, ok := offsetMap[offset]; !ok { + return nil + } + } + return keyColOffsets +} + +func (builder *dataReaderBuilder) prunePartitionForInnerExecutor(tbl table.Table, partitionInfo *plannercore.PartitionInfo, + lookUpContent []*indexJoinLookUpContent) (usedPartition []table.PhysicalTable, canPrune bool, contentPos []int64, err error) { + partitionTbl := tbl.(table.PartitionedTable) + + // In index join, this is called by multiple goroutines simultaneously, but partitionPruning is not thread-safe. + // Use once.Do to avoid DATA RACE here. + // TODO: condition based pruning can be do in advance. + condPruneResult, err := builder.partitionPruning(partitionTbl, partitionInfo.PruningConds, partitionInfo.PartitionNames, partitionInfo.Columns, partitionInfo.ColumnNames) + if err != nil { + return nil, false, nil, err + } + + // recalculate key column offsets + if len(lookUpContent) == 0 { + return nil, false, nil, nil + } + if lookUpContent[0].keyColIDs == nil { + return nil, false, nil, plannercore.ErrInternal.GenWithStack("cannot get column IDs when dynamic pruning") + } + keyColOffsets := getPartitionKeyColOffsets(lookUpContent[0].keyColIDs, partitionTbl) + if len(keyColOffsets) == 0 { + return condPruneResult, false, nil, nil + } + + locateKey := make([]types.Datum, len(partitionTbl.Cols())) + partitions := make(map[int64]table.PhysicalTable) + contentPos = make([]int64, len(lookUpContent)) + for idx, content := range lookUpContent { + for i, data := range content.keys { + locateKey[keyColOffsets[i]] = data + } + p, err := partitionTbl.GetPartitionByRow(builder.ctx, locateKey) + if table.ErrNoPartitionForGivenValue.Equal(err) { + continue + } + if err != nil { + return nil, false, nil, err + } + if _, ok := partitions[p.GetPhysicalID()]; !ok { + partitions[p.GetPhysicalID()] = p + } + contentPos[idx] = p.GetPhysicalID() + } + + usedPartition = make([]table.PhysicalTable, 0, len(partitions)) + for _, p := range condPruneResult { + if _, ok := partitions[p.GetPhysicalID()]; ok { + usedPartition = append(usedPartition, p) + } + } + + // To make the final key ranges involving multiple partitions ordered. + slices.SortFunc(usedPartition, func(i, j table.PhysicalTable) int { + return cmp.Compare(i.GetPhysicalID(), j.GetPhysicalID()) + }) + return usedPartition, true, contentPos, nil +} + +func buildNoRangeIndexReader(b *executorBuilder, v *plannercore.PhysicalIndexReader) (*IndexReaderExecutor, error) { + dagReq, err := builder.ConstructDAGReq(b.ctx, v.IndexPlans, kv.TiKV) + if err != nil { + return nil, err + } + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + tbl, _ := b.is.TableByID(is.Table.ID) + isPartition, physicalTableID := is.IsPartition() + if isPartition { + pt := tbl.(table.PartitionedTable) + tbl = pt.GetPartition(physicalTableID) + } else { + physicalTableID = is.Table.ID + } + startTS, err := b.getSnapshotTS() + if err != nil { + return nil, err + } + paging := b.ctx.GetSessionVars().EnablePaging + e := &IndexReaderExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dagPB: dagReq, + startTS: startTS, + txnScope: b.txnScope, + readReplicaScope: b.readReplicaScope, + isStaleness: b.isStaleness, + netDataSize: v.GetNetDataSize(), + physicalTableID: physicalTableID, + table: tbl, + index: is.Index, + keepOrder: is.KeepOrder, + desc: is.Desc, + columns: is.Columns, + byItems: is.ByItems, + paging: paging, + corColInFilter: b.corColInDistPlan(v.IndexPlans), + corColInAccess: b.corColInAccess(v.IndexPlans[0]), + idxCols: is.IdxCols, + colLens: is.IdxColLens, + plans: v.IndexPlans, + outputColumns: v.OutputColumns, + } + + for _, col := range v.OutputColumns { + dagReq.OutputOffsets = append(dagReq.OutputOffsets, uint32(col.Index)) + } + + return e, nil +} + +func (b *executorBuilder) buildIndexReader(v *plannercore.PhysicalIndexReader) exec.Executor { + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + if err := b.validCanReadTemporaryOrCacheTable(is.Table); err != nil { + b.err = err + return nil + } + + ret, err := buildNoRangeIndexReader(b, v) + if err != nil { + b.err = err + return nil + } + + if ret.table.Meta().TempTableType != model.TempTableNone { + ret.dummy = true + } + + ret.ranges = is.Ranges + sctx := b.ctx.GetSessionVars().StmtCtx + sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) + + if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + return ret + } + // When isPartition is set, it means the union rewriting is done, so a partition reader is preferred. + if ok, _ := is.IsPartition(); ok { + return ret + } + + pi := is.Table.GetPartitionInfo() + if pi == nil { + return ret + } + + if is.Index.Global { + tmp, ok := b.is.TableByID(ret.table.Meta().ID) + if !ok { + b.err = infoschema.ErrTableNotExists + return nil + } + tbl, ok := tmp.(table.PartitionedTable) + if !ok { + b.err = exeerrors.ErrBuildExecutor + return nil + } + ret.partitionIDMap, err = getPartitionIdsAfterPruning(b.ctx, tbl, &v.PartitionInfo) + if err != nil { + b.err = err + return nil + } + return ret + } + + tmp, _ := b.is.TableByID(is.Table.ID) + tbl := tmp.(table.PartitionedTable) + partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) + if err != nil { + b.err = err + return nil + } + ret.partitions = partitions + return ret +} + +func buildTableReq(b *executorBuilder, schemaLen int, plans []plannercore.PhysicalPlan) (dagReq *tipb.DAGRequest, val table.Table, err error) { + tableReq, err := builder.ConstructDAGReq(b.ctx, plans, kv.TiKV) + if err != nil { + return nil, nil, err + } + for i := 0; i < schemaLen; i++ { + tableReq.OutputOffsets = append(tableReq.OutputOffsets, uint32(i)) + } + ts := plans[0].(*plannercore.PhysicalTableScan) + tbl, _ := b.is.TableByID(ts.Table.ID) + isPartition, physicalTableID := ts.IsPartition() + if isPartition { + pt := tbl.(table.PartitionedTable) + tbl = pt.GetPartition(physicalTableID) + } + return tableReq, tbl, err +} + +// buildIndexReq is designed to create a DAG for index request. +// If len(ByItems) != 0 means index request should return related columns +// to sort result rows in TiDB side for parition tables. +func buildIndexReq(ctx sessionctx.Context, columns []*model.IndexColumn, handleLen int, plans []plannercore.PhysicalPlan) (dagReq *tipb.DAGRequest, err error) { + indexReq, err := builder.ConstructDAGReq(ctx, plans, kv.TiKV) + if err != nil { + return nil, err + } + + indexReq.OutputOffsets = []uint32{} + idxScan := plans[0].(*plannercore.PhysicalIndexScan) + if len(idxScan.ByItems) != 0 { + schema := idxScan.Schema() + for _, item := range idxScan.ByItems { + c, ok := item.Expr.(*expression.Column) + if !ok { + return nil, errors.Errorf("Not support non-column in orderBy pushed down") + } + find := false + for i, schemaColumn := range schema.Columns { + if schemaColumn.ID == c.ID { + indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(i)) + find = true + break + } + } + if !find { + return nil, errors.Errorf("Not found order by related columns in indexScan.schema") + } + } + } + + for i := 0; i < handleLen; i++ { + indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(len(columns)+i)) + } + + if idxScan.NeedExtraOutputCol() { + // need add one more column for pid or physical table id + indexReq.OutputOffsets = append(indexReq.OutputOffsets, uint32(len(columns)+handleLen)) + } + return indexReq, err +} + +func buildNoRangeIndexLookUpReader(b *executorBuilder, v *plannercore.PhysicalIndexLookUpReader) (*IndexLookUpExecutor, error) { + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + var handleLen int + if len(v.CommonHandleCols) != 0 { + handleLen = len(v.CommonHandleCols) + } else { + handleLen = 1 + } + indexReq, err := buildIndexReq(b.ctx, is.Index.Columns, handleLen, v.IndexPlans) + if err != nil { + return nil, err + } + indexPaging := false + if v.Paging { + indexPaging = true + } + tableReq, tbl, err := buildTableReq(b, v.Schema().Len(), v.TablePlans) + if err != nil { + return nil, err + } + ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) + startTS, err := b.getSnapshotTS() + if err != nil { + return nil, err + } + + readerBuilder, err := b.newDataReaderBuilder(nil) + if err != nil { + return nil, err + } + + e := &IndexLookUpExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dagPB: indexReq, + startTS: startTS, + table: tbl, + index: is.Index, + keepOrder: is.KeepOrder, + byItems: is.ByItems, + desc: is.Desc, + tableRequest: tableReq, + columns: ts.Columns, + indexPaging: indexPaging, + dataReaderBuilder: readerBuilder, + corColInIdxSide: b.corColInDistPlan(v.IndexPlans), + corColInTblSide: b.corColInDistPlan(v.TablePlans), + corColInAccess: b.corColInAccess(v.IndexPlans[0]), + idxCols: is.IdxCols, + colLens: is.IdxColLens, + idxPlans: v.IndexPlans, + tblPlans: v.TablePlans, + PushedLimit: v.PushedLimit, + idxNetDataSize: v.GetAvgTableRowSize(), + avgRowSize: v.GetAvgTableRowSize(), + } + + if v.ExtraHandleCol != nil { + e.handleIdx = append(e.handleIdx, v.ExtraHandleCol.Index) + e.handleCols = []*expression.Column{v.ExtraHandleCol} + } else { + for _, handleCol := range v.CommonHandleCols { + e.handleIdx = append(e.handleIdx, handleCol.Index) + } + e.handleCols = v.CommonHandleCols + e.primaryKeyIndex = tables.FindPrimaryIndex(tbl.Meta()) + } + return e, nil +} + +func (b *executorBuilder) buildIndexLookUpReader(v *plannercore.PhysicalIndexLookUpReader) exec.Executor { + if b.Ti != nil { + b.Ti.UseTableLookUp.Store(true) + } + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + if err := b.validCanReadTemporaryOrCacheTable(is.Table); err != nil { + b.err = err + return nil + } + + ret, err := buildNoRangeIndexLookUpReader(b, v) + if err != nil { + b.err = err + return nil + } + + if ret.table.Meta().TempTableType != model.TempTableNone { + ret.dummy = true + } + + ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) + + ret.ranges = is.Ranges + executor_metrics.ExecutorCounterIndexLookUpExecutor.Inc() + + sctx := b.ctx.GetSessionVars().StmtCtx + sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) + sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) + + if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + return ret + } + + if pi := is.Table.GetPartitionInfo(); pi == nil { + return ret + } + + if is.Index.Global || len(is.ByItems) != 0 { + tmp, ok := b.is.TableByID(ts.Table.ID) + if !ok { + b.err = err + return nil + } + tbl, ok := tmp.(table.PartitionedTable) + if !ok { + b.err = exeerrors.ErrBuildExecutor + return nil + } + ret.partitionIDMap, err = getPartitionIdsAfterPruning(b.ctx, tbl, &v.PartitionInfo) + if err != nil { + b.err = err + return nil + } + + if is.Index.Global { + return ret + } + } + if ok, _ := is.IsPartition(); ok { + // Already pruned when translated to logical union. + return ret + } + + tmp, _ := b.is.TableByID(is.Table.ID) + tbl := tmp.(table.PartitionedTable) + partitions, err := partitionPruning(b.ctx, tbl, v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) + if err != nil { + b.err = err + return nil + } + ret.partitionTableMode = true + ret.prunedPartitions = partitions + return ret +} + +func buildNoRangeIndexMergeReader(b *executorBuilder, v *plannercore.PhysicalIndexMergeReader) (*IndexMergeReaderExecutor, error) { + partialPlanCount := len(v.PartialPlans) + partialReqs := make([]*tipb.DAGRequest, 0, partialPlanCount) + partialDataSizes := make([]float64, 0, partialPlanCount) + indexes := make([]*model.IndexInfo, 0, partialPlanCount) + descs := make([]bool, 0, partialPlanCount) + ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) + isCorColInPartialFilters := make([]bool, 0, partialPlanCount) + isCorColInPartialAccess := make([]bool, 0, partialPlanCount) + for i := 0; i < partialPlanCount; i++ { + var tempReq *tipb.DAGRequest + var err error + + if is, ok := v.PartialPlans[i][0].(*plannercore.PhysicalIndexScan); ok { + tempReq, err = buildIndexReq(b.ctx, is.Index.Columns, ts.HandleCols.NumCols(), v.PartialPlans[i]) + descs = append(descs, is.Desc) + indexes = append(indexes, is.Index) + } else { + ts := v.PartialPlans[i][0].(*plannercore.PhysicalTableScan) + tempReq, _, err = buildTableReq(b, len(ts.Columns), v.PartialPlans[i]) + descs = append(descs, ts.Desc) + indexes = append(indexes, nil) + } + if err != nil { + return nil, err + } + collect := false + tempReq.CollectRangeCounts = &collect + partialReqs = append(partialReqs, tempReq) + isCorColInPartialFilters = append(isCorColInPartialFilters, b.corColInDistPlan(v.PartialPlans[i])) + isCorColInPartialAccess = append(isCorColInPartialAccess, b.corColInAccess(v.PartialPlans[i][0])) + partialDataSizes = append(partialDataSizes, v.GetPartialReaderNetDataSize(v.PartialPlans[i][0])) + } + tableReq, tblInfo, err := buildTableReq(b, v.Schema().Len(), v.TablePlans) + isCorColInTableFilter := b.corColInDistPlan(v.TablePlans) + if err != nil { + return nil, err + } + startTS, err := b.getSnapshotTS() + if err != nil { + return nil, err + } + + readerBuilder, err := b.newDataReaderBuilder(nil) + if err != nil { + return nil, err + } + + paging := b.ctx.GetSessionVars().EnablePaging + e := &IndexMergeReaderExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + dagPBs: partialReqs, + startTS: startTS, + table: tblInfo, + indexes: indexes, + descs: descs, + tableRequest: tableReq, + columns: ts.Columns, + partialPlans: v.PartialPlans, + tblPlans: v.TablePlans, + partialNetDataSizes: partialDataSizes, + dataAvgRowSize: v.GetAvgTableRowSize(), + dataReaderBuilder: readerBuilder, + paging: paging, + handleCols: v.HandleCols, + isCorColInPartialFilters: isCorColInPartialFilters, + isCorColInTableFilter: isCorColInTableFilter, + isCorColInPartialAccess: isCorColInPartialAccess, + isIntersection: v.IsIntersectionType, + byItems: v.ByItems, + pushedLimit: v.PushedLimit, + keepOrder: v.KeepOrder, + } + collectTable := false + e.tableRequest.CollectRangeCounts = &collectTable + return e, nil +} + +func (b *executorBuilder) buildIndexMergeReader(v *plannercore.PhysicalIndexMergeReader) exec.Executor { + if b.Ti != nil { + b.Ti.UseIndexMerge = true + b.Ti.UseTableLookUp.Store(true) + } + ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) + if err := b.validCanReadTemporaryOrCacheTable(ts.Table); err != nil { + b.err = err + return nil + } + + ret, err := buildNoRangeIndexMergeReader(b, v) + if err != nil { + b.err = err + return nil + } + ret.ranges = make([][]*ranger.Range, 0, len(v.PartialPlans)) + sctx := b.ctx.GetSessionVars().StmtCtx + for i := 0; i < len(v.PartialPlans); i++ { + if is, ok := v.PartialPlans[i][0].(*plannercore.PhysicalIndexScan); ok { + ret.ranges = append(ret.ranges, is.Ranges) + sctx.IndexNames = append(sctx.IndexNames, is.Table.Name.O+":"+is.Index.Name.O) + } else { + ret.ranges = append(ret.ranges, v.PartialPlans[i][0].(*plannercore.PhysicalTableScan).Ranges) + if ret.table.Meta().IsCommonHandle { + tblInfo := ret.table.Meta() + sctx.IndexNames = append(sctx.IndexNames, tblInfo.Name.O+":"+tables.FindPrimaryIndex(tblInfo).Name.O) + } + } + } + sctx.TableIDs = append(sctx.TableIDs, ts.Table.ID) + executor_metrics.ExecutorCounterIndexMergeReaderExecutor.Inc() + + if !b.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + return ret + } + + if pi := ts.Table.GetPartitionInfo(); pi == nil { + return ret + } + + tmp, _ := b.is.TableByID(ts.Table.ID) + partitions, err := partitionPruning(b.ctx, tmp.(table.PartitionedTable), v.PartitionInfo.PruningConds, v.PartitionInfo.PartitionNames, v.PartitionInfo.Columns, v.PartitionInfo.ColumnNames) + if err != nil { + b.err = err + return nil + } + ret.partitionTableMode, ret.prunedPartitions = true, partitions + return ret +} + +// dataReaderBuilder build an executor. +// The executor can be used to read data in the ranges which are constructed by datums. +// Differences from executorBuilder: +// 1. dataReaderBuilder calculate data range from argument, rather than plan. +// 2. the result executor is already opened. +type dataReaderBuilder struct { + plannercore.Plan + *executorBuilder + + selectResultHook // for testing + once struct { + sync.Once + condPruneResult []table.PhysicalTable + err error + } +} + +type mockPhysicalIndexReader struct { + plannercore.PhysicalPlan + + e exec.Executor +} + +// MemoryUsage of mockPhysicalIndexReader is only for testing +func (*mockPhysicalIndexReader) MemoryUsage() (sum int64) { + return +} + +func (builder *dataReaderBuilder) buildExecutorForIndexJoin(ctx context.Context, lookUpContents []*indexJoinLookUpContent, + indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + return builder.buildExecutorForIndexJoinInternal(ctx, builder.Plan, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) +} + +func (builder *dataReaderBuilder) buildExecutorForIndexJoinInternal(ctx context.Context, plan plannercore.Plan, lookUpContents []*indexJoinLookUpContent, + indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + switch v := plan.(type) { + case *plannercore.PhysicalTableReader: + return builder.buildTableReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) + case *plannercore.PhysicalIndexReader: + return builder.buildIndexReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + case *plannercore.PhysicalIndexLookUpReader: + return builder.buildIndexLookUpReaderForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + case *plannercore.PhysicalUnionScan: + return builder.buildUnionScanForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) + // The inner child of IndexJoin might be Projection when a combination of the following conditions is true: + // 1. The inner child fetch data using indexLookupReader + // 2. PK is not handle + // 3. The inner child needs to keep order + // In this case, an extra column tidb_rowid will be appended in the output result of IndexLookupReader(see copTask.doubleReadNeedProj). + // Then we need a Projection upon IndexLookupReader to prune the redundant column. + case *plannercore.PhysicalProjection: + return builder.buildProjectionForIndexJoin(ctx, v, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + // Need to support physical selection because after PR 16389, TiDB will push down all the expr supported by TiKV or TiFlash + // in predicate push down stage, so if there is an expr which only supported by TiFlash, a physical selection will be added after index read + case *plannercore.PhysicalSelection: + childExec, err := builder.buildExecutorForIndexJoinInternal(ctx, v.Children()[0], lookUpContents, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) + if err != nil { + return nil, err + } + exec := &SelectionExec{ + BaseExecutor: exec.NewBaseExecutor(builder.ctx, v.Schema(), v.ID(), childExec), + filters: v.Conditions, + } + err = exec.open(ctx) + return exec, err + case *mockPhysicalIndexReader: + return v.e, nil + } + return nil, errors.New("Wrong plan type for dataReaderBuilder") +} + +func (builder *dataReaderBuilder) buildUnionScanForIndexJoin(ctx context.Context, v *plannercore.PhysicalUnionScan, + values []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, + cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + childBuilder, err := builder.newDataReaderBuilder(v.Children()[0]) + if err != nil { + return nil, err + } + + reader, err := childBuilder.buildExecutorForIndexJoin(ctx, values, indexRanges, keyOff2IdxOff, cwc, canReorderHandles, memTracker, interruptSignal) + if err != nil { + return nil, err + } + + ret := builder.buildUnionScanFromReader(reader, v) + if us, ok := ret.(*UnionScanExec); ok { + err = us.open(ctx) + } + return ret, err +} + +func (builder *dataReaderBuilder) buildTableReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalTableReader, + lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, + cwc *plannercore.ColWithCmpFuncManager, canReorderHandles bool, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + e, err := buildNoRangeTableReader(builder.executorBuilder, v) + if !canReorderHandles { + // `canReorderHandles` is set to false only in IndexMergeJoin. IndexMergeJoin will trigger a dead loop problem + // when enabling paging(tidb/issues/35831). But IndexMergeJoin is not visible to the user and is deprecated + // for now. Thus, we disable paging here. + e.paging = false + } + if err != nil { + return nil, err + } + tbInfo := e.table.Meta() + if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + if v.IsCommonHandle { + kvRanges, err := buildKvRangesForIndexJoin(e.Ctx(), getPhysicalTableID(e.table), -1, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + if err != nil { + return nil, err + } + return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) + } + handles, _ := dedupHandles(lookUpContents) + return builder.buildTableReaderFromHandles(ctx, e, handles, canReorderHandles) + } + tbl, _ := builder.is.TableByID(tbInfo.ID) + pt := tbl.(table.PartitionedTable) + partitionInfo := &v.PartitionInfo + usedPartitionList, err := builder.partitionPruning(pt, partitionInfo.PruningConds, partitionInfo.PartitionNames, partitionInfo.Columns, partitionInfo.ColumnNames) + if err != nil { + return nil, err + } + usedPartitions := make(map[int64]table.PhysicalTable, len(usedPartitionList)) + for _, p := range usedPartitionList { + usedPartitions[p.GetPhysicalID()] = p + } + var kvRanges []kv.KeyRange + var keyColOffsets []int + if len(lookUpContents) > 0 { + keyColOffsets = getPartitionKeyColOffsets(lookUpContents[0].keyColIDs, pt) + } + if v.IsCommonHandle { + if len(keyColOffsets) > 0 { + locateKey := make([]types.Datum, len(pt.Cols())) + kvRanges = make([]kv.KeyRange, 0, len(lookUpContents)) + // lookUpContentsByPID groups lookUpContents by pid(partition) so that kv ranges for same partition can be merged. + lookUpContentsByPID := make(map[int64][]*indexJoinLookUpContent) + for _, content := range lookUpContents { + for i, data := range content.keys { + locateKey[keyColOffsets[i]] = data + } + p, err := pt.GetPartitionByRow(e.Ctx(), locateKey) + if table.ErrNoPartitionForGivenValue.Equal(err) { + continue + } + if err != nil { + return nil, err + } + pid := p.GetPhysicalID() + if _, ok := usedPartitions[pid]; !ok { + continue + } + lookUpContentsByPID[pid] = append(lookUpContentsByPID[pid], content) + } + for pid, contents := range lookUpContentsByPID { + // buildKvRanges for each partition. + tmp, err := buildKvRangesForIndexJoin(e.Ctx(), pid, -1, contents, indexRanges, keyOff2IdxOff, cwc, nil, interruptSignal) + if err != nil { + return nil, err + } + kvRanges = append(kvRanges, tmp...) + } + } else { + kvRanges = make([]kv.KeyRange, 0, len(usedPartitions)*len(lookUpContents)) + for _, p := range usedPartitionList { + tmp, err := buildKvRangesForIndexJoin(e.Ctx(), p.GetPhysicalID(), -1, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + if err != nil { + return nil, err + } + kvRanges = append(tmp, kvRanges...) + } + } + // The key ranges should be ordered. + slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { + return bytes.Compare(i.StartKey, j.StartKey) + }) + return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) + } + + handles, lookUpContents := dedupHandles(lookUpContents) + + if len(keyColOffsets) > 0 { + locateKey := make([]types.Datum, len(pt.Cols())) + kvRanges = make([]kv.KeyRange, 0, len(lookUpContents)) + for _, content := range lookUpContents { + for i, data := range content.keys { + locateKey[keyColOffsets[i]] = data + } + p, err := pt.GetPartitionByRow(e.Ctx(), locateKey) + if table.ErrNoPartitionForGivenValue.Equal(err) { + continue + } + if err != nil { + return nil, err + } + pid := p.GetPhysicalID() + if _, ok := usedPartitions[pid]; !ok { + continue + } + handle := kv.IntHandle(content.keys[0].GetInt64()) + ranges, _ := distsql.TableHandlesToKVRanges(pid, []kv.Handle{handle}) + kvRanges = append(kvRanges, ranges...) + } + } else { + for _, p := range usedPartitionList { + ranges, _ := distsql.TableHandlesToKVRanges(p.GetPhysicalID(), handles) + kvRanges = append(kvRanges, ranges...) + } + } + + // The key ranges should be ordered. + slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { + return bytes.Compare(i.StartKey, j.StartKey) + }) + return builder.buildTableReaderFromKvRanges(ctx, e, kvRanges) +} + +func dedupHandles(lookUpContents []*indexJoinLookUpContent) ([]kv.Handle, []*indexJoinLookUpContent) { + handles := make([]kv.Handle, 0, len(lookUpContents)) + validLookUpContents := make([]*indexJoinLookUpContent, 0, len(lookUpContents)) + for _, content := range lookUpContents { + isValidHandle := true + handle := kv.IntHandle(content.keys[0].GetInt64()) + for _, key := range content.keys { + if handle.IntValue() != key.GetInt64() { + isValidHandle = false + break + } + } + if isValidHandle { + handles = append(handles, handle) + validLookUpContents = append(validLookUpContents, content) + } + } + return handles, validLookUpContents +} + +type kvRangeBuilderFromRangeAndPartition struct { + sctx sessionctx.Context + partitions []table.PhysicalTable +} + +func (h kvRangeBuilderFromRangeAndPartition) buildKeyRangeSeparately(ranges []*ranger.Range) ([]int64, [][]kv.KeyRange, error) { + ret := make([][]kv.KeyRange, len(h.partitions)) + pids := make([]int64, 0, len(h.partitions)) + for i, p := range h.partitions { + pid := p.GetPhysicalID() + pids = append(pids, pid) + meta := p.Meta() + if len(ranges) == 0 { + continue + } + kvRange, err := distsql.TableHandleRangesToKVRanges(h.sctx.GetSessionVars().StmtCtx, []int64{pid}, meta != nil && meta.IsCommonHandle, ranges) + if err != nil { + return nil, nil, err + } + ret[i] = kvRange.AppendSelfTo(ret[i]) + } + return pids, ret, nil +} + +func (h kvRangeBuilderFromRangeAndPartition) buildKeyRange(ranges []*ranger.Range) ([][]kv.KeyRange, error) { + ret := make([][]kv.KeyRange, len(h.partitions)) + if len(ranges) == 0 { + return ret, nil + } + for i, p := range h.partitions { + pid := p.GetPhysicalID() + meta := p.Meta() + kvRange, err := distsql.TableHandleRangesToKVRanges(h.sctx.GetSessionVars().StmtCtx, []int64{pid}, meta != nil && meta.IsCommonHandle, ranges) + if err != nil { + return nil, err + } + ret[i] = kvRange.AppendSelfTo(ret[i]) + } + return ret, nil +} + +// newClosestReadAdjuster let the request be sent to closest replica(within the same zone) +// if response size exceeds certain threshold. +func newClosestReadAdjuster(ctx sessionctx.Context, req *kv.Request, netDataSize float64) kv.CoprRequestAdjuster { + if req.ReplicaRead != kv.ReplicaReadClosestAdaptive { + return nil + } + return func(req *kv.Request, copTaskCount int) bool { + // copTaskCount is the number of coprocessor requests + if int64(netDataSize/float64(copTaskCount)) >= ctx.GetSessionVars().ReplicaClosestReadThreshold { + req.MatchStoreLabels = append(req.MatchStoreLabels, &metapb.StoreLabel{ + Key: placement.DCLabelKey, + Value: config.GetTxnScopeFromConfig(), + }) + return true + } + // reset to read from leader when the data size is small. + req.ReplicaRead = kv.ReplicaReadLeader + return false + } +} + +func (builder *dataReaderBuilder) buildTableReaderBase(ctx context.Context, e *TableReaderExecutor, reqBuilderWithRange distsql.RequestBuilder) (*TableReaderExecutor, error) { + startTS, err := builder.getSnapshotTS() + if err != nil { + return nil, err + } + kvReq, err := reqBuilderWithRange. + SetDAGRequest(e.dagPB). + SetStartTS(startTS). + SetDesc(e.desc). + SetKeepOrder(e.keepOrder). + SetTxnScope(e.txnScope). + SetReadReplicaScope(e.readReplicaScope). + SetIsStaleness(e.isStaleness). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &reqBuilderWithRange.Request, e.netDataSize)). + SetPaging(e.paging). + SetConnID(e.Ctx().GetSessionVars().ConnectionID). + Build() + if err != nil { + return nil, err + } + e.kvRanges = kvReq.KeyRanges.AppendSelfTo(e.kvRanges) + e.resultHandler = &tableResultHandler{} + result, err := builder.SelectResult(ctx, builder.ctx, kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) + if err != nil { + return nil, err + } + e.resultHandler.open(nil, result) + return e, nil +} + +func (builder *dataReaderBuilder) buildTableReaderFromHandles(ctx context.Context, e *TableReaderExecutor, handles []kv.Handle, canReorderHandles bool) (*TableReaderExecutor, error) { + if canReorderHandles { + slices.SortFunc(handles, func(i, j kv.Handle) int { + return i.Compare(j) + }) + } + var b distsql.RequestBuilder + if len(handles) > 0 { + if _, ok := handles[0].(kv.PartitionHandle); ok { + b.SetPartitionsAndHandles(handles) + } else { + b.SetTableHandles(getPhysicalTableID(e.table), handles) + } + } else { + b.SetKeyRanges(nil) + } + return builder.buildTableReaderBase(ctx, e, b) +} + +func (builder *dataReaderBuilder) buildTableReaderFromKvRanges(ctx context.Context, e *TableReaderExecutor, ranges []kv.KeyRange) (exec.Executor, error) { + var b distsql.RequestBuilder + b.SetKeyRanges(ranges) + return builder.buildTableReaderBase(ctx, e, b) +} + +func (builder *dataReaderBuilder) buildIndexReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalIndexReader, + lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memoryTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + e, err := buildNoRangeIndexReader(builder.executorBuilder, v) + if err != nil { + return nil, err + } + tbInfo := e.table.Meta() + if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + kvRanges, err := buildKvRangesForIndexJoin(e.Ctx(), e.physicalTableID, e.index.ID, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memoryTracker, interruptSignal) + if err != nil { + return nil, err + } + err = e.open(ctx, kvRanges) + return e, err + } + + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + if is.Index.Global { + tmp, ok := builder.is.TableByID(tbInfo.ID) + if !ok { + return nil, infoschema.ErrTableNotExists + } + tbl, ok := tmp.(table.PartitionedTable) + if !ok { + return nil, exeerrors.ErrBuildExecutor + } + e.partitionIDMap, err = getPartitionIdsAfterPruning(builder.ctx, tbl, &v.PartitionInfo) + if err != nil { + return nil, err + } + + if e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc); err != nil { + return nil, err + } + if err := e.Open(ctx); err != nil { + return nil, err + } + return e, nil + } + + tbl, _ := builder.executorBuilder.is.TableByID(tbInfo.ID) + usedPartition, canPrune, contentPos, err := builder.prunePartitionForInnerExecutor(tbl, &v.PartitionInfo, lookUpContents) + if err != nil { + return nil, err + } + if len(usedPartition) != 0 { + if canPrune { + rangeMap, err := buildIndexRangeForEachPartition(e.Ctx(), usedPartition, contentPos, lookUpContents, indexRanges, keyOff2IdxOff, cwc) + if err != nil { + return nil, err + } + e.partitions = usedPartition + e.ranges = indexRanges + e.partRangeMap = rangeMap + } else { + e.partitions = usedPartition + if e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc); err != nil { + return nil, err + } + } + if err := e.Open(ctx); err != nil { + return nil, err + } + return e, nil + } + ret := &TableDualExec{BaseExecutor: *e.Base()} + err = ret.Open(ctx) + return ret, err +} + +func (builder *dataReaderBuilder) buildIndexLookUpReaderForIndexJoin(ctx context.Context, v *plannercore.PhysicalIndexLookUpReader, + lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + if builder.Ti != nil { + builder.Ti.UseTableLookUp.Store(true) + } + e, err := buildNoRangeIndexLookUpReader(builder.executorBuilder, v) + if err != nil { + return nil, err + } + + tbInfo := e.table.Meta() + if tbInfo.GetPartitionInfo() == nil || !builder.ctx.GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + e.kvRanges, err = buildKvRangesForIndexJoin(e.Ctx(), getPhysicalTableID(e.table), e.index.ID, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal) + if err != nil { + return nil, err + } + err = e.open(ctx) + return e, err + } + + is := v.IndexPlans[0].(*plannercore.PhysicalIndexScan) + ts := v.TablePlans[0].(*plannercore.PhysicalTableScan) + if is.Index.Global { + tmp, ok := builder.is.TableByID(ts.Table.ID) + if !ok { + return nil, infoschema.ErrTableNotExists + } + tbl, ok := tmp.(table.PartitionedTable) + if !ok { + return nil, exeerrors.ErrBuildExecutor + } + e.partitionIDMap, err = getPartitionIdsAfterPruning(builder.ctx, tbl, &v.PartitionInfo) + if err != nil { + return nil, err + } + e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc) + if err != nil { + return nil, err + } + if err := e.Open(ctx); err != nil { + return nil, err + } + return e, err + } + + tbl, _ := builder.executorBuilder.is.TableByID(tbInfo.ID) + usedPartition, canPrune, contentPos, err := builder.prunePartitionForInnerExecutor(tbl, &v.PartitionInfo, lookUpContents) + if err != nil { + return nil, err + } + if len(usedPartition) != 0 { + if canPrune { + rangeMap, err := buildIndexRangeForEachPartition(e.Ctx(), usedPartition, contentPos, lookUpContents, indexRanges, keyOff2IdxOff, cwc) + if err != nil { + return nil, err + } + e.prunedPartitions = usedPartition + e.ranges = indexRanges + e.partitionRangeMap = rangeMap + } else { + e.prunedPartitions = usedPartition + e.ranges, err = buildRangesForIndexJoin(e.Ctx(), lookUpContents, indexRanges, keyOff2IdxOff, cwc) + if err != nil { + return nil, err + } + } + e.partitionTableMode = true + if err := e.Open(ctx); err != nil { + return nil, err + } + return e, err + } + ret := &TableDualExec{BaseExecutor: *e.Base()} + err = ret.Open(ctx) + return ret, err +} + +func (builder *dataReaderBuilder) buildProjectionForIndexJoin(ctx context.Context, v *plannercore.PhysicalProjection, + lookUpContents []*indexJoinLookUpContent, indexRanges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (exec.Executor, error) { + var ( + childExec exec.Executor + err error + ) + switch op := v.Children()[0].(type) { + case *plannercore.PhysicalIndexLookUpReader: + if childExec, err = builder.buildIndexLookUpReaderForIndexJoin(ctx, op, lookUpContents, indexRanges, keyOff2IdxOff, cwc, memTracker, interruptSignal); err != nil { + return nil, err + } + case *plannercore.PhysicalTableReader: + if childExec, err = builder.buildTableReaderForIndexJoin(ctx, op, lookUpContents, indexRanges, keyOff2IdxOff, cwc, true, memTracker, interruptSignal); err != nil { + return nil, err + } + default: + return nil, errors.Errorf("inner child of Projection should be IndexLookupReader/TableReader, but got %T", v.Children()[0]) + } + + e := &ProjectionExec{ + BaseExecutor: exec.NewBaseExecutor(builder.ctx, v.Schema(), v.ID(), childExec), + numWorkers: int64(builder.ctx.GetSessionVars().ProjectionConcurrency()), + evaluatorSuit: expression.NewEvaluatorSuite(v.Exprs, v.AvoidColumnEvaluator), + calculateNoDelay: v.CalculateNoDelay, + } + + // If the calculation row count for this Projection operator is smaller + // than a Chunk size, we turn back to the un-parallel Projection + // implementation to reduce the goroutine overhead. + if int64(v.StatsCount()) < int64(builder.ctx.GetSessionVars().MaxChunkSize) { + e.numWorkers = 0 + } + err = e.open(ctx) + + return e, err +} + +// buildRangesForIndexJoin builds kv ranges for index join when the inner plan is index scan plan. +func buildRangesForIndexJoin(ctx sessionctx.Context, lookUpContents []*indexJoinLookUpContent, + ranges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager) ([]*ranger.Range, error) { + retRanges := make([]*ranger.Range, 0, len(ranges)*len(lookUpContents)) + lastPos := len(ranges[0].LowVal) - 1 + tmpDatumRanges := make([]*ranger.Range, 0, len(lookUpContents)) + for _, content := range lookUpContents { + for _, ran := range ranges { + for keyOff, idxOff := range keyOff2IdxOff { + ran.LowVal[idxOff] = content.keys[keyOff] + ran.HighVal[idxOff] = content.keys[keyOff] + } + } + if cwc == nil { + // A deep copy is need here because the old []*range.Range is overwriten + for _, ran := range ranges { + retRanges = append(retRanges, ran.Clone()) + } + continue + } + nextColRanges, err := cwc.BuildRangesByRow(ctx, content.row) + if err != nil { + return nil, err + } + for _, nextColRan := range nextColRanges { + for _, ran := range ranges { + ran.LowVal[lastPos] = nextColRan.LowVal[0] + ran.HighVal[lastPos] = nextColRan.HighVal[0] + ran.LowExclude = nextColRan.LowExclude + ran.HighExclude = nextColRan.HighExclude + ran.Collators = nextColRan.Collators + tmpDatumRanges = append(tmpDatumRanges, ran.Clone()) + } + } + } + + if cwc == nil { + return retRanges, nil + } + + return ranger.UnionRanges(ctx, tmpDatumRanges, true) +} + +// buildKvRangesForIndexJoin builds kv ranges for index join when the inner plan is index scan plan. +func buildKvRangesForIndexJoin(ctx sessionctx.Context, tableID, indexID int64, lookUpContents []*indexJoinLookUpContent, + ranges []*ranger.Range, keyOff2IdxOff []int, cwc *plannercore.ColWithCmpFuncManager, memTracker *memory.Tracker, interruptSignal *atomic.Value) (_ []kv.KeyRange, err error) { + kvRanges := make([]kv.KeyRange, 0, len(ranges)*len(lookUpContents)) + if len(ranges) == 0 { + return []kv.KeyRange{}, nil + } + lastPos := len(ranges[0].LowVal) - 1 + sc := ctx.GetSessionVars().StmtCtx + tmpDatumRanges := make([]*ranger.Range, 0, len(lookUpContents)) + for _, content := range lookUpContents { + for _, ran := range ranges { + for keyOff, idxOff := range keyOff2IdxOff { + ran.LowVal[idxOff] = content.keys[keyOff] + ran.HighVal[idxOff] = content.keys[keyOff] + } + } + if cwc == nil { + // Index id is -1 means it's a common handle. + var tmpKvRanges *kv.KeyRanges + var err error + if indexID == -1 { + tmpKvRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{tableID}, ranges) + } else { + tmpKvRanges, err = distsql.IndexRangesToKVRangesWithInterruptSignal(sc, tableID, indexID, ranges, memTracker, interruptSignal) + } + if err != nil { + return nil, err + } + kvRanges = tmpKvRanges.AppendSelfTo(kvRanges) + continue + } + nextColRanges, err := cwc.BuildRangesByRow(ctx, content.row) + if err != nil { + return nil, err + } + for _, nextColRan := range nextColRanges { + for _, ran := range ranges { + ran.LowVal[lastPos] = nextColRan.LowVal[0] + ran.HighVal[lastPos] = nextColRan.HighVal[0] + ran.LowExclude = nextColRan.LowExclude + ran.HighExclude = nextColRan.HighExclude + ran.Collators = nextColRan.Collators + tmpDatumRanges = append(tmpDatumRanges, ran.Clone()) + } + } + } + if len(kvRanges) != 0 && memTracker != nil { + memTracker.Consume(int64(2 * cap(kvRanges[0].StartKey) * len(kvRanges))) + } + if len(tmpDatumRanges) != 0 && memTracker != nil { + memTracker.Consume(2 * int64(len(tmpDatumRanges)) * types.EstimatedMemUsage(tmpDatumRanges[0].LowVal, len(tmpDatumRanges))) + } + if cwc == nil { + slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { + return bytes.Compare(i.StartKey, j.StartKey) + }) + return kvRanges, nil + } + + tmpDatumRanges, err = ranger.UnionRanges(ctx, tmpDatumRanges, true) + if err != nil { + return nil, err + } + // Index id is -1 means it's a common handle. + if indexID == -1 { + tmpKeyRanges, err := distsql.CommonHandleRangesToKVRanges(ctx.GetSessionVars().StmtCtx, []int64{tableID}, tmpDatumRanges) + return tmpKeyRanges.FirstPartitionRange(), err + } + tmpKeyRanges, err := distsql.IndexRangesToKVRangesWithInterruptSignal(ctx.GetSessionVars().StmtCtx, tableID, indexID, tmpDatumRanges, memTracker, interruptSignal) + return tmpKeyRanges.FirstPartitionRange(), err +} + +func (b *executorBuilder) buildWindow(v *plannercore.PhysicalWindow) exec.Executor { + childExec := b.build(v.Children()[0]) + if b.err != nil { + return nil + } + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID(), childExec) + groupByItems := make([]expression.Expression, 0, len(v.PartitionBy)) + for _, item := range v.PartitionBy { + groupByItems = append(groupByItems, item.Col) + } + orderByCols := make([]*expression.Column, 0, len(v.OrderBy)) + for _, item := range v.OrderBy { + orderByCols = append(orderByCols, item.Col) + } + windowFuncs := make([]aggfuncs.AggFunc, 0, len(v.WindowFuncDescs)) + partialResults := make([]aggfuncs.PartialResult, 0, len(v.WindowFuncDescs)) + resultColIdx := v.Schema().Len() - len(v.WindowFuncDescs) + for _, desc := range v.WindowFuncDescs { + aggDesc, err := aggregation.NewAggFuncDescForWindowFunc(b.ctx, desc, false) + if err != nil { + b.err = err + return nil + } + agg := aggfuncs.BuildWindowFunctions(b.ctx, aggDesc, resultColIdx, orderByCols) + windowFuncs = append(windowFuncs, agg) + partialResult, _ := agg.AllocPartialResult() + partialResults = append(partialResults, partialResult) + resultColIdx++ + } + + var err error + if b.ctx.GetSessionVars().EnablePipelinedWindowExec { + exec := &PipelinedWindowExec{ + BaseExecutor: base, + groupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, groupByItems), + numWindowFuncs: len(v.WindowFuncDescs), + } + + exec.windowFuncs = windowFuncs + exec.partialResults = partialResults + if v.Frame == nil { + exec.start = &plannercore.FrameBound{ + Type: ast.Preceding, + UnBounded: true, + } + exec.end = &plannercore.FrameBound{ + Type: ast.Following, + UnBounded: true, + } + } else { + exec.start = v.Frame.Start + exec.end = v.Frame.End + if v.Frame.Type == ast.Ranges { + cmpResult := int64(-1) + if len(v.OrderBy) > 0 && v.OrderBy[0].Desc { + cmpResult = 1 + } + exec.orderByCols = orderByCols + exec.expectedCmpResult = cmpResult + exec.isRangeFrame = true + err = exec.start.UpdateCompareCols(b.ctx, exec.orderByCols) + if err != nil { + return nil + } + err = exec.end.UpdateCompareCols(b.ctx, exec.orderByCols) + if err != nil { + return nil + } + } + } + return exec + } + var processor windowProcessor + if v.Frame == nil { + processor = &aggWindowProcessor{ + windowFuncs: windowFuncs, + partialResults: partialResults, + } + } else if v.Frame.Type == ast.Rows { + processor = &rowFrameWindowProcessor{ + windowFuncs: windowFuncs, + partialResults: partialResults, + start: v.Frame.Start, + end: v.Frame.End, + } + } else { + cmpResult := int64(-1) + if len(v.OrderBy) > 0 && v.OrderBy[0].Desc { + cmpResult = 1 + } + tmpProcessor := &rangeFrameWindowProcessor{ + windowFuncs: windowFuncs, + partialResults: partialResults, + start: v.Frame.Start, + end: v.Frame.End, + orderByCols: orderByCols, + expectedCmpResult: cmpResult, + } + + err = tmpProcessor.start.UpdateCompareCols(b.ctx, orderByCols) + if err != nil { + return nil + } + err = tmpProcessor.end.UpdateCompareCols(b.ctx, orderByCols) + if err != nil { + return nil + } + + processor = tmpProcessor + } + return &WindowExec{BaseExecutor: base, + processor: processor, + groupChecker: vecgroupchecker.NewVecGroupChecker(b.ctx, groupByItems), + numWindowFuncs: len(v.WindowFuncDescs), + } +} + +func (b *executorBuilder) buildShuffle(v *plannercore.PhysicalShuffle) *ShuffleExec { + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + shuffle := &ShuffleExec{ + BaseExecutor: base, + concurrency: v.Concurrency, + } + + // 1. initialize the splitters + splitters := make([]partitionSplitter, len(v.ByItemArrays)) + switch v.SplitterType { + case plannercore.PartitionHashSplitterType: + for i, byItems := range v.ByItemArrays { + splitters[i] = buildPartitionHashSplitter(shuffle.concurrency, byItems) + } + case plannercore.PartitionRangeSplitterType: + for i, byItems := range v.ByItemArrays { + splitters[i] = buildPartitionRangeSplitter(b.ctx, shuffle.concurrency, byItems) + } + default: + panic("Not implemented. Should not reach here.") + } + shuffle.splitters = splitters + + // 2. initialize the data sources (build the data sources from physical plan to executors) + shuffle.dataSources = make([]exec.Executor, len(v.DataSources)) + for i, dataSource := range v.DataSources { + shuffle.dataSources[i] = b.build(dataSource) + if b.err != nil { + return nil + } + } + + // 3. initialize the workers + head := v.Children()[0] + // A `PhysicalShuffleReceiverStub` for every worker have the same `DataSource` but different `Receiver`. + // We preallocate `PhysicalShuffleReceiverStub`s here and reuse them below. + stubs := make([]*plannercore.PhysicalShuffleReceiverStub, 0, len(v.DataSources)) + for _, dataSource := range v.DataSources { + stub := plannercore.PhysicalShuffleReceiverStub{ + DataSource: dataSource, + }.Init(b.ctx, dataSource.StatsInfo(), dataSource.SelectBlockOffset(), nil) + stub.SetSchema(dataSource.Schema()) + stubs = append(stubs, stub) + } + shuffle.workers = make([]*shuffleWorker, shuffle.concurrency) + for i := range shuffle.workers { + receivers := make([]*shuffleReceiver, len(v.DataSources)) + for j, dataSource := range v.DataSources { + receivers[j] = &shuffleReceiver{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, dataSource.Schema(), stubs[j].ID()), + } + } + + w := &shuffleWorker{ + receivers: receivers, + } + + for j := range v.DataSources { + stub := stubs[j] + stub.Receiver = (unsafe.Pointer)(receivers[j]) + v.Tails[j].SetChildren(stub) + } + + w.childExec = b.build(head) + if b.err != nil { + return nil + } + + shuffle.workers[i] = w + } + + return shuffle +} + +func (*executorBuilder) buildShuffleReceiverStub(v *plannercore.PhysicalShuffleReceiverStub) *shuffleReceiver { + return (*shuffleReceiver)(v.Receiver) +} + +func (b *executorBuilder) buildSQLBindExec(v *plannercore.SQLBindPlan) exec.Executor { + base := exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()) + base.SetInitCap(chunk.ZeroCapacity) + + e := &SQLBindExec{ + BaseExecutor: base, + sqlBindOp: v.SQLBindOp, + normdOrigSQL: v.NormdOrigSQL, + bindSQL: v.BindSQL, + charset: v.Charset, + collation: v.Collation, + db: v.Db, + isGlobal: v.IsGlobal, + bindAst: v.BindStmt, + newStatus: v.NewStatus, + source: v.Source, + sqlDigest: v.SQLDigest, + planDigest: v.PlanDigest, + } + return e +} + +// NewRowDecoder creates a chunk decoder for new row format row value decode. +func NewRowDecoder(ctx sessionctx.Context, schema *expression.Schema, tbl *model.TableInfo) *rowcodec.ChunkDecoder { + getColInfoByID := func(tbl *model.TableInfo, colID int64) *model.ColumnInfo { + for _, col := range tbl.Columns { + if col.ID == colID { + return col + } + } + return nil + } + var pkCols []int64 + reqCols := make([]rowcodec.ColInfo, len(schema.Columns)) + for i := range schema.Columns { + idx, col := i, schema.Columns[i] + isPK := (tbl.PKIsHandle && mysql.HasPriKeyFlag(col.RetType.GetFlag())) || col.ID == model.ExtraHandleID + if isPK { + pkCols = append(pkCols, col.ID) + } + isGeneratedCol := false + if col.VirtualExpr != nil { + isGeneratedCol = true + } + reqCols[idx] = rowcodec.ColInfo{ + ID: col.ID, + VirtualGenCol: isGeneratedCol, + Ft: col.RetType, + } + } + if len(pkCols) == 0 { + pkCols = tables.TryGetCommonPkColumnIds(tbl) + if len(pkCols) == 0 { + pkCols = []int64{-1} + } + } + defVal := func(i int, chk *chunk.Chunk) error { + if reqCols[i].ID < 0 { + // model.ExtraHandleID, ExtraPidColID, ExtraPhysTblID... etc + // Don't set the default value for that column. + chk.AppendNull(i) + return nil + } + + ci := getColInfoByID(tbl, reqCols[i].ID) + d, err := table.GetColOriginDefaultValue(ctx, ci) + if err != nil { + return err + } + chk.AppendDatum(i, &d) + return nil + } + return rowcodec.NewChunkDecoder(reqCols, pkCols, defVal, ctx.GetSessionVars().Location()) +} + +func (b *executorBuilder) buildBatchPointGet(plan *plannercore.BatchPointGetPlan) exec.Executor { + var err error + if err = b.validCanReadTemporaryOrCacheTable(plan.TblInfo); err != nil { + b.err = err + return nil + } + + if plan.Lock && !b.inSelectLockStmt { + b.inSelectLockStmt = true + defer func() { + b.inSelectLockStmt = false + }() + } + + decoder := NewRowDecoder(b.ctx, plan.Schema(), plan.TblInfo) + e := &BatchPointGetExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, plan.Schema(), plan.ID()), + tblInfo: plan.TblInfo, + idxInfo: plan.IndexInfo, + rowDecoder: decoder, + keepOrder: plan.KeepOrder, + desc: plan.Desc, + lock: plan.Lock, + waitTime: plan.LockWaitTime, + partExpr: plan.PartitionExpr, + partPos: plan.PartitionColPos, + planPhysIDs: plan.PartitionIDs, + singlePart: plan.SinglePart, + partTblID: plan.PartTblID, + columns: plan.Columns, + } + + e.snapshot, err = b.getSnapshot() + if err != nil { + b.err = err + return nil + } + if e.Ctx().GetSessionVars().IsReplicaReadClosestAdaptive() { + e.snapshot.SetOption(kv.ReplicaReadAdjuster, newReplicaReadAdjuster(e.Ctx(), plan.GetAvgRowSize())) + } + if e.RuntimeStats() != nil { + snapshotStats := &txnsnapshot.SnapshotRuntimeStats{} + e.stats = &runtimeStatsWithSnapshot{ + SnapshotRuntimeStats: snapshotStats, + } + e.snapshot.SetOption(kv.CollectRuntimeStats, snapshotStats) + } + + if plan.IndexInfo != nil { + sctx := b.ctx.GetSessionVars().StmtCtx + sctx.IndexNames = append(sctx.IndexNames, plan.TblInfo.Name.O+":"+plan.IndexInfo.Name.O) + } + + failpoint.Inject("assertBatchPointReplicaOption", func(val failpoint.Value) { + assertScope := val.(string) + if e.Ctx().GetSessionVars().GetReplicaRead().IsClosestRead() && assertScope != b.readReplicaScope { + panic("batch point get replica option fail") + } + }) + + snapshotTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + if plan.TblInfo.TableCacheStatusType == model.TableCacheStatusEnable { + if cacheTable := b.getCacheTable(plan.TblInfo, snapshotTS); cacheTable != nil { + e.snapshot = cacheTableSnapshot{e.snapshot, cacheTable} + } + } + + if plan.TblInfo.TempTableType != model.TempTableNone { + // Temporary table should not do any lock operations + e.lock = false + e.waitTime = 0 + } + + if e.lock { + b.hasLock = true + } + + var capacity int + if plan.IndexInfo != nil && !isCommonHandleRead(plan.TblInfo, plan.IndexInfo) { + e.idxVals = plan.IndexValues + capacity = len(e.idxVals) + } else { + // `SELECT a FROM t WHERE a IN (1, 1, 2, 1, 2)` should not return duplicated rows + handles := make([]kv.Handle, 0, len(plan.Handles)) + dedup := kv.NewHandleMap() + // Used for clear paritionIDs of duplicated rows. + dupPartPos := 0 + if plan.IndexInfo == nil { + for idx, handle := range plan.Handles { + if _, found := dedup.Get(handle); found { + continue + } + dedup.Set(handle, true) + handles = append(handles, handle) + if len(plan.PartitionIDs) > 0 { + e.planPhysIDs[dupPartPos] = e.planPhysIDs[idx] + dupPartPos++ + } + } + } else { + for idx, value := range plan.IndexValues { + if datumsContainNull(value) { + continue + } + handleBytes, err := EncodeUniqueIndexValuesForKey(e.Ctx(), e.tblInfo, plan.IndexInfo, value) + if err != nil { + if kv.ErrNotExist.Equal(err) { + continue + } + b.err = err + return nil + } + handle, err := kv.NewCommonHandle(handleBytes) + if err != nil { + b.err = err + return nil + } + if _, found := dedup.Get(handle); found { + continue + } + dedup.Set(handle, true) + handles = append(handles, handle) + if len(plan.PartitionIDs) > 0 { + e.planPhysIDs[dupPartPos] = e.planPhysIDs[idx] + dupPartPos++ + } + } + } + e.handles = handles + if dupPartPos > 0 { + e.planPhysIDs = e.planPhysIDs[:dupPartPos] + } + capacity = len(e.handles) + } + e.Base().SetInitCap(capacity) + e.Base().SetMaxChunkSize(capacity) + e.buildVirtualColumnInfo() + return e +} + +func newReplicaReadAdjuster(ctx sessionctx.Context, avgRowSize float64) txnkv.ReplicaReadAdjuster { + return func(count int) (tikv.StoreSelectorOption, clientkv.ReplicaReadType) { + if int64(avgRowSize*float64(count)) >= ctx.GetSessionVars().ReplicaClosestReadThreshold { + return tikv.WithMatchLabels([]*metapb.StoreLabel{ + { + Key: placement.DCLabelKey, + Value: config.GetTxnScopeFromConfig(), + }, + }), clientkv.ReplicaReadMixed + } + // fallback to read from leader if the request is small + return nil, clientkv.ReplicaReadLeader + } +} + +func isCommonHandleRead(tbl *model.TableInfo, idx *model.IndexInfo) bool { + return tbl.IsCommonHandle && idx.Primary +} + +func getPhysicalTableID(t table.Table) int64 { + if p, ok := t.(table.PhysicalTable); ok { + return p.GetPhysicalID() + } + return t.Meta().ID +} + +func (b *executorBuilder) buildAdminShowTelemetry(v *plannercore.AdminShowTelemetry) exec.Executor { + return &AdminShowTelemetryExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID())} +} + +func (b *executorBuilder) buildAdminResetTelemetryID(v *plannercore.AdminResetTelemetryID) exec.Executor { + return &AdminResetTelemetryIDExec{BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID())} +} + +func (builder *dataReaderBuilder) partitionPruning(tbl table.PartitionedTable, conds []expression.Expression, partitionNames []model.CIStr, + columns []*expression.Column, columnNames types.NameSlice) ([]table.PhysicalTable, error) { + builder.once.Do(func() { + condPruneResult, err := partitionPruning(builder.executorBuilder.ctx, tbl, conds, partitionNames, columns, columnNames) + builder.once.condPruneResult = condPruneResult + builder.once.err = err + }) + return builder.once.condPruneResult, builder.once.err +} + +func partitionPruning(ctx sessionctx.Context, tbl table.PartitionedTable, conds []expression.Expression, partitionNames []model.CIStr, + columns []*expression.Column, columnNames types.NameSlice) ([]table.PhysicalTable, error) { + idxArr, err := plannercore.PartitionPruning(ctx, tbl, conds, partitionNames, columns, columnNames) + if err != nil { + return nil, err + } + + pi := tbl.Meta().GetPartitionInfo() + var ret []table.PhysicalTable + if fullRangePartition(idxArr) { + ret = make([]table.PhysicalTable, 0, len(pi.Definitions)) + for _, def := range pi.Definitions { + p := tbl.GetPartition(def.ID) + ret = append(ret, p) + } + } else { + ret = make([]table.PhysicalTable, 0, len(idxArr)) + for _, idx := range idxArr { + pid := pi.Definitions[idx].ID + p := tbl.GetPartition(pid) + ret = append(ret, p) + } + } + return ret, nil +} + +func getPartitionIdsAfterPruning(ctx sessionctx.Context, tbl table.PartitionedTable, partInfo *plannercore.PartitionInfo) (map[int64]struct{}, error) { + if partInfo == nil { + return nil, errors.New("partInfo in getPartitionIdsAfterPruning must not be nil") + } + idxArr, err := plannercore.PartitionPruning(ctx, tbl, partInfo.PruningConds, partInfo.PartitionNames, partInfo.Columns, partInfo.ColumnNames) + if err != nil { + return nil, err + } + + var ret map[int64]struct{} + + pi := tbl.Meta().GetPartitionInfo() + if fullRangePartition(idxArr) { + ret = make(map[int64]struct{}, len(pi.Definitions)) + for _, def := range pi.Definitions { + ret[def.ID] = struct{}{} + } + } else { + ret = make(map[int64]struct{}, len(idxArr)) + for _, idx := range idxArr { + pid := pi.Definitions[idx].ID + ret[pid] = struct{}{} + } + } + return ret, nil +} + +func fullRangePartition(idxArr []int) bool { + return len(idxArr) == 1 && idxArr[0] == plannercore.FullRange +} + +type emptySampler struct{} + +func (*emptySampler) writeChunk(_ *chunk.Chunk) error { + return nil +} + +func (*emptySampler) finished() bool { + return true +} + +func (b *executorBuilder) buildTableSample(v *plannercore.PhysicalTableSample) *TableSampleExecutor { + startTS, err := b.getSnapshotTS() + if err != nil { + b.err = err + return nil + } + e := &TableSampleExecutor{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + table: v.TableInfo, + startTS: startTS, + } + + tblInfo := v.TableInfo.Meta() + if tblInfo.TempTableType != model.TempTableNone { + if tblInfo.TempTableType != model.TempTableGlobal { + b.err = errors.New("TABLESAMPLE clause can not be applied to local temporary tables") + return nil + } + e.sampler = &emptySampler{} + } else if v.TableSampleInfo.AstNode.SampleMethod == ast.SampleMethodTypeTiDBRegion { + e.sampler = newTableRegionSampler( + b.ctx, v.TableInfo, startTS, v.TableSampleInfo.Partitions, v.Schema(), + v.TableSampleInfo.FullSchema, e.RetFieldTypes(), v.Desc) + } + + return e +} + +func (b *executorBuilder) buildCTE(v *plannercore.PhysicalCTE) exec.Executor { + if b.Ti != nil { + b.Ti.UseNonRecursive = true + } + if v.RecurPlan != nil && b.Ti != nil { + b.Ti.UseRecursive = true + } + + storageMap, ok := b.ctx.GetSessionVars().StmtCtx.CTEStorageMap.(map[int]*CTEStorages) + if !ok { + b.err = errors.New("type assertion for CTEStorageMap failed") + return nil + } + + chkSize := b.ctx.GetSessionVars().MaxChunkSize + // iterOutTbl will be constructed in CTEExec.Open(). + var resTbl cteutil.Storage + var iterInTbl cteutil.Storage + var producer *cteProducer + storages, ok := storageMap[v.CTE.IDForStorage] + if ok { + // Storage already setup. + resTbl = storages.ResTbl + iterInTbl = storages.IterInTbl + producer = storages.Producer + } else { + if v.SeedPlan == nil { + b.err = errors.New("cte.seedPlan cannot be nil") + return nil + } + // Build seed part. + corCols := plannercore.ExtractOuterApplyCorrelatedCols(v.SeedPlan) + seedExec := b.build(v.SeedPlan) + if b.err != nil { + return nil + } + + // Setup storages. + tps := seedExec.Base().RetFieldTypes() + resTbl = cteutil.NewStorageRowContainer(tps, chkSize) + if err := resTbl.OpenAndRef(); err != nil { + b.err = err + return nil + } + iterInTbl = cteutil.NewStorageRowContainer(tps, chkSize) + if err := iterInTbl.OpenAndRef(); err != nil { + b.err = err + return nil + } + storageMap[v.CTE.IDForStorage] = &CTEStorages{ResTbl: resTbl, IterInTbl: iterInTbl} + + // Build recursive part. + var recursiveExec exec.Executor + if v.RecurPlan != nil { + recursiveExec = b.build(v.RecurPlan) + if b.err != nil { + return nil + } + corCols = append(corCols, plannercore.ExtractOuterApplyCorrelatedCols(v.RecurPlan)...) + } + + var sel []int + if v.CTE.IsDistinct { + sel = make([]int, chkSize) + for i := 0; i < chkSize; i++ { + sel[i] = i + } + } + + var corColHashCodes [][]byte + for _, corCol := range corCols { + corColHashCodes = append(corColHashCodes, getCorColHashCode(corCol)) + } + + producer = &cteProducer{ + ctx: b.ctx, + seedExec: seedExec, + recursiveExec: recursiveExec, + resTbl: resTbl, + iterInTbl: iterInTbl, + isDistinct: v.CTE.IsDistinct, + sel: sel, + hasLimit: v.CTE.HasLimit, + limitBeg: v.CTE.LimitBeg, + limitEnd: v.CTE.LimitEnd, + corCols: corCols, + corColHashCodes: corColHashCodes, + } + storageMap[v.CTE.IDForStorage].Producer = producer + } + + return &CTEExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + producer: producer, + } +} + +func (b *executorBuilder) buildCTETableReader(v *plannercore.PhysicalCTETable) exec.Executor { + storageMap, ok := b.ctx.GetSessionVars().StmtCtx.CTEStorageMap.(map[int]*CTEStorages) + if !ok { + b.err = errors.New("type assertion for CTEStorageMap failed") + return nil + } + storages, ok := storageMap[v.IDForStorage] + if !ok { + b.err = errors.Errorf("iterInTbl should already be set up by CTEExec(id: %d)", v.IDForStorage) + return nil + } + return &CTETableReaderExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + iterInTbl: storages.IterInTbl, + chkIdx: 0, + } +} +func (b *executorBuilder) validCanReadTemporaryOrCacheTable(tbl *model.TableInfo) error { + err := b.validCanReadTemporaryTable(tbl) + if err != nil { + return err + } + return b.validCanReadCacheTable(tbl) +} + +func (b *executorBuilder) validCanReadCacheTable(tbl *model.TableInfo) error { + if tbl.TableCacheStatusType == model.TableCacheStatusDisable { + return nil + } + + sessionVars := b.ctx.GetSessionVars() + + // Temporary table can't switch into cache table. so the following code will not cause confusion + if sessionVars.TxnCtx.IsStaleness || b.isStaleness { + return errors.Trace(errors.New("can not stale read cache table")) + } + + return nil +} + +func (b *executorBuilder) validCanReadTemporaryTable(tbl *model.TableInfo) error { + if tbl.TempTableType == model.TempTableNone { + return nil + } + + // Some tools like dumpling use history read to dump all table's records and will be fail if we return an error. + // So we do not check SnapshotTS here + + sessionVars := b.ctx.GetSessionVars() + + if tbl.TempTableType == model.TempTableLocal && sessionVars.SnapshotTS != 0 { + return errors.New("can not read local temporary table when 'tidb_snapshot' is set") + } + + if sessionVars.TxnCtx.IsStaleness || b.isStaleness { + return errors.New("can not stale read temporary table") + } + + return nil +} + +func (b *executorBuilder) getCacheTable(tblInfo *model.TableInfo, startTS uint64) kv.MemBuffer { + tbl, ok := b.is.TableByID(tblInfo.ID) + if !ok { + b.err = errors.Trace(infoschema.ErrTableNotExists.GenWithStackByArgs(b.ctx.GetSessionVars().CurrentDB, tblInfo.Name)) + return nil + } + sessVars := b.ctx.GetSessionVars() + leaseDuration := time.Duration(variable.TableCacheLease.Load()) * time.Second + cacheData, loading := tbl.(table.CachedTable).TryReadFromCache(startTS, leaseDuration) + if cacheData != nil { + sessVars.StmtCtx.ReadFromTableCache = true + return cacheData + } else if loading { + return nil + } else { + if !b.ctx.GetSessionVars().StmtCtx.InExplainStmt && !b.inDeleteStmt && !b.inUpdateStmt { + tbl.(table.CachedTable).UpdateLockForRead(context.Background(), b.ctx.GetStore(), startTS, leaseDuration) + } + } + return nil +} + +func (b *executorBuilder) buildCompactTable(v *plannercore.CompactTable) exec.Executor { + if v.ReplicaKind != ast.CompactReplicaKindTiFlash && v.ReplicaKind != ast.CompactReplicaKindAll { + b.err = errors.Errorf("compact %v replica is not supported", strings.ToLower(string(v.ReplicaKind))) + return nil + } + + store := b.ctx.GetStore() + tikvStore, ok := store.(tikv.Storage) + if !ok { + b.err = errors.New("compact tiflash replica can only run with tikv compatible storage") + return nil + } + + var partitionIDs []int64 + if v.PartitionNames != nil { + if v.TableInfo.Partition == nil { + b.err = errors.Errorf("table:%s is not a partition table, but user specify partition name list:%+v", v.TableInfo.Name.O, v.PartitionNames) + return nil + } + // use map to avoid FindPartitionDefinitionByName + partitionMap := map[string]int64{} + for _, partition := range v.TableInfo.Partition.Definitions { + partitionMap[partition.Name.L] = partition.ID + } + + for _, partitionName := range v.PartitionNames { + partitionID, ok := partitionMap[partitionName.L] + if !ok { + b.err = table.ErrUnknownPartition.GenWithStackByArgs(partitionName.O, v.TableInfo.Name.O) + return nil + } + partitionIDs = append(partitionIDs, partitionID) + } + if b.Ti.PartitionTelemetry == nil { + b.Ti.PartitionTelemetry = &PartitionTelemetryInfo{} + } + b.Ti.PartitionTelemetry.UseCompactTablePartition = true + } + + return &CompactTableTiFlashExec{ + BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), + tableInfo: v.TableInfo, + partitionIDs: partitionIDs, + tikvStore: tikvStore, + } +} diff --git a/executor/change.go b/pkg/executor/change.go similarity index 88% rename from executor/change.go rename to pkg/executor/change.go index 7f09597741817..a2e4c403f6aa2 100644 --- a/executor/change.go +++ b/pkg/executor/change.go @@ -19,11 +19,11 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/tidb-binlog/node" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" + "github.com/pingcap/tidb/pkg/util/chunk" ) // ChangeExec represents a change executor. diff --git a/executor/charset_test.go b/pkg/executor/charset_test.go similarity index 97% rename from executor/charset_test.go rename to pkg/executor/charset_test.go index d40bc6a2fe296..a015494feb702 100644 --- a/executor/charset_test.go +++ b/pkg/executor/charset_test.go @@ -17,9 +17,9 @@ package executor_test import ( "testing" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" ) func TestCharsetFeature(t *testing.T) { diff --git a/pkg/executor/checksum.go b/pkg/executor/checksum.go new file mode 100644 index 0000000000000..0db26a3da129e --- /dev/null +++ b/pkg/executor/checksum.go @@ -0,0 +1,292 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "strconv" + + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +var _ exec.Executor = &ChecksumTableExec{} + +// ChecksumTableExec represents ChecksumTable executor. +type ChecksumTableExec struct { + exec.BaseExecutor + + tables map[int64]*checksumContext + done bool +} + +// Open implements the Executor Open interface. +func (e *ChecksumTableExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + + concurrency, err := getChecksumTableConcurrency(e.Ctx()) + if err != nil { + return err + } + + tasks, err := e.buildTasks() + if err != nil { + return err + } + + taskCh := make(chan *checksumTask, len(tasks)) + resultCh := make(chan *checksumResult, len(tasks)) + for i := 0; i < concurrency; i++ { + go e.checksumWorker(taskCh, resultCh) + } + + for _, task := range tasks { + taskCh <- task + } + close(taskCh) + + for i := 0; i < len(tasks); i++ { + result := <-resultCh + if result.Error != nil { + err = result.Error + logutil.Logger(ctx).Error("checksum failed", zap.Error(err)) + continue + } + e.handleResult(result) + } + if err != nil { + return err + } + + return nil +} + +// Next implements the Executor Next interface. +func (e *ChecksumTableExec) Next(_ context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + for _, t := range e.tables { + req.AppendString(0, t.DBInfo.Name.O) + req.AppendString(1, t.TableInfo.Name.O) + req.AppendUint64(2, t.Response.Checksum) + req.AppendUint64(3, t.Response.TotalKvs) + req.AppendUint64(4, t.Response.TotalBytes) + } + e.done = true + return nil +} + +func (e *ChecksumTableExec) buildTasks() ([]*checksumTask, error) { + var tasks []*checksumTask + for id, t := range e.tables { + reqs, err := t.BuildRequests(e.Ctx()) + if err != nil { + return nil, err + } + for _, req := range reqs { + tasks = append(tasks, &checksumTask{id, req}) + } + } + return tasks, nil +} + +func (e *ChecksumTableExec) handleResult(result *checksumResult) { + table := e.tables[result.TableID] + table.HandleResponse(result.Response) +} + +func (e *ChecksumTableExec) checksumWorker(taskCh <-chan *checksumTask, resultCh chan<- *checksumResult) { + for task := range taskCh { + result := &checksumResult{TableID: task.TableID} + result.Response, result.Error = e.handleChecksumRequest(task.Request) + resultCh <- result + } +} + +func (e *ChecksumTableExec) handleChecksumRequest(req *kv.Request) (resp *tipb.ChecksumResponse, err error) { + ctx := distsql.WithSQLKvExecCounterInterceptor(context.TODO(), e.Ctx().GetSessionVars().StmtCtx) + res, err := distsql.Checksum(ctx, e.Ctx().GetClient(), req, e.Ctx().GetSessionVars().KVVars) + if err != nil { + return nil, err + } + defer func() { + if err1 := res.Close(); err1 != nil { + err = err1 + } + }() + + resp = &tipb.ChecksumResponse{} + + for { + data, err := res.NextRaw(ctx) + if err != nil { + return nil, err + } + if data == nil { + break + } + checksum := &tipb.ChecksumResponse{} + if err = checksum.Unmarshal(data); err != nil { + return nil, err + } + updateChecksumResponse(resp, checksum) + } + + return resp, nil +} + +type checksumTask struct { + TableID int64 + Request *kv.Request +} + +type checksumResult struct { + Error error + TableID int64 + Response *tipb.ChecksumResponse +} + +type checksumContext struct { + DBInfo *model.DBInfo + TableInfo *model.TableInfo + StartTs uint64 + Response *tipb.ChecksumResponse +} + +func newChecksumContext(db *model.DBInfo, table *model.TableInfo, startTs uint64) *checksumContext { + return &checksumContext{ + DBInfo: db, + TableInfo: table, + StartTs: startTs, + Response: &tipb.ChecksumResponse{}, + } +} + +func (c *checksumContext) BuildRequests(ctx sessionctx.Context) ([]*kv.Request, error) { + var partDefs []model.PartitionDefinition + if part := c.TableInfo.Partition; part != nil { + partDefs = part.Definitions + } + + reqs := make([]*kv.Request, 0, (len(c.TableInfo.Indices)+1)*(len(partDefs)+1)) + if err := c.appendRequest(ctx, c.TableInfo.ID, &reqs); err != nil { + return nil, err + } + + for _, partDef := range partDefs { + if err := c.appendRequest(ctx, partDef.ID, &reqs); err != nil { + return nil, err + } + } + + return reqs, nil +} + +func (c *checksumContext) appendRequest(ctx sessionctx.Context, tableID int64, reqs *[]*kv.Request) error { + req, err := c.buildTableRequest(ctx, tableID) + if err != nil { + return err + } + + *reqs = append(*reqs, req) + for _, indexInfo := range c.TableInfo.Indices { + if indexInfo.State != model.StatePublic { + continue + } + req, err = c.buildIndexRequest(ctx, tableID, indexInfo) + if err != nil { + return err + } + *reqs = append(*reqs, req) + } + + return nil +} + +func (c *checksumContext) buildTableRequest(ctx sessionctx.Context, tableID int64) (*kv.Request, error) { + checksum := &tipb.ChecksumRequest{ + ScanOn: tipb.ChecksumScanOn_Table, + Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, + } + + var ranges []*ranger.Range + if c.TableInfo.IsCommonHandle { + ranges = ranger.FullNotNullRange() + } else { + ranges = ranger.FullIntRange(false) + } + + var builder distsql.RequestBuilder + builder.SetResourceGroupTagger(ctx.GetSessionVars().StmtCtx.GetResourceGroupTagger()) + return builder.SetHandleRanges(ctx.GetSessionVars().StmtCtx, tableID, c.TableInfo.IsCommonHandle, ranges). + SetChecksumRequest(checksum). + SetStartTS(c.StartTs). + SetConcurrency(ctx.GetSessionVars().DistSQLScanConcurrency()). + SetResourceGroupName(ctx.GetSessionVars().ResourceGroupName). + SetExplicitRequestSourceType(ctx.GetSessionVars().ExplicitRequestSourceType). + Build() +} + +func (c *checksumContext) buildIndexRequest(ctx sessionctx.Context, tableID int64, indexInfo *model.IndexInfo) (*kv.Request, error) { + checksum := &tipb.ChecksumRequest{ + ScanOn: tipb.ChecksumScanOn_Index, + Algorithm: tipb.ChecksumAlgorithm_Crc64_Xor, + } + + ranges := ranger.FullRange() + + var builder distsql.RequestBuilder + builder.SetResourceGroupTagger(ctx.GetSessionVars().StmtCtx.GetResourceGroupTagger()) + return builder.SetIndexRanges(ctx.GetSessionVars().StmtCtx, tableID, indexInfo.ID, ranges). + SetChecksumRequest(checksum). + SetStartTS(c.StartTs). + SetConcurrency(ctx.GetSessionVars().DistSQLScanConcurrency()). + SetResourceGroupName(ctx.GetSessionVars().ResourceGroupName). + SetExplicitRequestSourceType(ctx.GetSessionVars().ExplicitRequestSourceType). + Build() +} + +func (c *checksumContext) HandleResponse(update *tipb.ChecksumResponse) { + updateChecksumResponse(c.Response, update) +} + +func getChecksumTableConcurrency(ctx sessionctx.Context) (int, error) { + sessionVars := ctx.GetSessionVars() + concurrency, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), variable.TiDBChecksumTableConcurrency) + if err != nil { + return 0, err + } + c, err := strconv.ParseInt(concurrency, 10, 64) + return int(c), err +} + +func updateChecksumResponse(resp, update *tipb.ChecksumResponse) { + resp.Checksum ^= update.Checksum + resp.TotalKvs += update.TotalKvs + resp.TotalBytes += update.TotalBytes +} diff --git a/executor/chunk_size_control_test.go b/pkg/executor/chunk_size_control_test.go similarity index 95% rename from executor/chunk_size_control_test.go rename to pkg/executor/chunk_size_control_test.go index 72f9ae24c9f76..e49b3491d2f5e 100644 --- a/executor/chunk_size_control_test.go +++ b/pkg/executor/chunk_size_control_test.go @@ -22,15 +22,15 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikv" diff --git a/executor/cluster_table_test.go b/pkg/executor/cluster_table_test.go similarity index 98% rename from executor/cluster_table_test.go rename to pkg/executor/cluster_table_test.go index 2f7d0fa91ef1e..03b929bac1583 100644 --- a/executor/cluster_table_test.go +++ b/pkg/executor/cluster_table_test.go @@ -23,15 +23,15 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) diff --git a/executor/compact_table.go b/pkg/executor/compact_table.go similarity index 97% rename from executor/compact_table.go rename to pkg/executor/compact_table.go index 69a8bbb247ba2..966d5ca8265b1 100644 --- a/executor/compact_table.go +++ b/pkg/executor/compact_table.go @@ -23,13 +23,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/log" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/util/chunk" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/executor/compact_table_test.go b/pkg/executor/compact_table_test.go similarity index 99% rename from executor/compact_table_test.go rename to pkg/executor/compact_table_test.go index c54d60b81f55c..eae058bf4f78b 100644 --- a/executor/compact_table_test.go +++ b/pkg/executor/compact_table_test.go @@ -24,10 +24,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikv" diff --git a/executor/compiler.go b/pkg/executor/compiler.go similarity index 96% rename from executor/compiler.go rename to pkg/executor/compiler.go index c93e543d443ce..fd724588763b6 100644 --- a/executor/compiler.go +++ b/pkg/executor/compiler.go @@ -20,18 +20,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/tracing" "go.uber.org/zap" ) diff --git a/executor/concurrent_map.go b/pkg/executor/concurrent_map.go similarity index 97% rename from executor/concurrent_map.go rename to pkg/executor/concurrent_map.go index 0cf5c456623d5..c63bd0089b0a8 100644 --- a/executor/concurrent_map.go +++ b/pkg/executor/concurrent_map.go @@ -15,8 +15,8 @@ package executor import ( - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/syncutil" ) // ShardCount controls the shard maps within the concurrent map diff --git a/executor/concurrent_map_test.go b/pkg/executor/concurrent_map_test.go similarity index 97% rename from executor/concurrent_map_test.go rename to pkg/executor/concurrent_map_test.go index eb1538ab2548a..944d8f78259d6 100644 --- a/executor/concurrent_map_test.go +++ b/pkg/executor/concurrent_map_test.go @@ -19,8 +19,8 @@ import ( "sync/atomic" "testing" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" ) diff --git a/executor/copr_cache_test.go b/pkg/executor/copr_cache_test.go similarity index 87% rename from executor/copr_cache_test.go rename to pkg/executor/copr_cache_test.go index 9107651251b35..71ec852418056 100644 --- a/executor/copr_cache_test.go +++ b/pkg/executor/copr_cache_test.go @@ -20,11 +20,11 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikv" @@ -59,9 +59,9 @@ func TestIntegrationCopCache(t *testing.T) { tableStart := tablecodec.GenTableRecordPrefix(tid) cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 6) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/cophandler/mockCopCacheInUnistore", `return(123)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler/mockCopCacheInUnistore", `return(123)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/cophandler/mockCopCacheInUnistore")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler/mockCopCacheInUnistore")) }() rows := tk.MustQuery("explain analyze select * from t where t.a < 10").Rows() diff --git a/pkg/executor/coprocessor.go b/pkg/executor/coprocessor.go new file mode 100644 index 0000000000000..c835dde6d67f9 --- /dev/null +++ b/pkg/executor/coprocessor.go @@ -0,0 +1,304 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + + "github.com/gogo/protobuf/proto" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/tikvpb" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tipb/go-tipb" +) + +func copHandlerCtx(ctx context.Context, req *coprocessor.Request) context.Context { + source := req.Context.SourceStmt + if source == nil { + return ctx + } + + traceInfo := &model.TraceInfo{ + ConnectionID: source.ConnectionId, + SessionAlias: source.SessionAlias, + } + + ctx = tracing.ContextWithTraceInfo(ctx, traceInfo) + ctx = logutil.WithTraceFields(ctx, traceInfo) + return ctx +} + +// CoprocessorDAGHandler uses to handle cop dag request. +type CoprocessorDAGHandler struct { + sctx sessionctx.Context + dagReq *tipb.DAGRequest +} + +// NewCoprocessorDAGHandler creates a new CoprocessorDAGHandler. +func NewCoprocessorDAGHandler(sctx sessionctx.Context) *CoprocessorDAGHandler { + return &CoprocessorDAGHandler{ + sctx: sctx, + } +} + +// HandleRequest handles the coprocessor request. +func (h *CoprocessorDAGHandler) HandleRequest(ctx context.Context, req *coprocessor.Request) *coprocessor.Response { + ctx = copHandlerCtx(ctx, req) + + e, err := h.buildDAGExecutor(req) + if err != nil { + return h.buildErrorResponse(err) + } + + err = e.Open(ctx) + if err != nil { + return h.buildErrorResponse(err) + } + + chk := exec.TryNewCacheChunk(e) + tps := e.Base().RetFieldTypes() + var totalChunks, partChunks []tipb.Chunk + memTracker := h.sctx.GetSessionVars().StmtCtx.MemTracker + for { + chk.Reset() + err = exec.Next(ctx, e, chk) + if err != nil { + return h.buildErrorResponse(err) + } + if chk.NumRows() == 0 { + break + } + partChunks, err = h.buildChunk(chk, tps) + if err != nil { + return h.buildErrorResponse(err) + } + for _, ch := range partChunks { + memTracker.Consume(int64(ch.Size())) + } + totalChunks = append(totalChunks, partChunks...) + } + if err := e.Close(); err != nil { + return h.buildErrorResponse(err) + } + return h.buildUnaryResponse(totalChunks) +} + +// HandleStreamRequest handles the coprocessor stream request. +func (h *CoprocessorDAGHandler) HandleStreamRequest(ctx context.Context, req *coprocessor.Request, stream tikvpb.Tikv_CoprocessorStreamServer) error { + ctx = copHandlerCtx(ctx, req) + logutil.Logger(ctx).Debug("handle coprocessor stream request") + + e, err := h.buildDAGExecutor(req) + if err != nil { + return stream.Send(h.buildErrorResponse(err)) + } + + err = e.Open(ctx) + if err != nil { + return stream.Send(h.buildErrorResponse(err)) + } + + chk := exec.TryNewCacheChunk(e) + tps := e.Base().RetFieldTypes() + for { + chk.Reset() + if err = exec.Next(ctx, e, chk); err != nil { + return stream.Send(h.buildErrorResponse(err)) + } + if chk.NumRows() == 0 { + return h.buildResponseAndSendToStream(chk, tps, stream) + } + if err = h.buildResponseAndSendToStream(chk, tps, stream); err != nil { + return stream.Send(h.buildErrorResponse(err)) + } + } +} + +func (h *CoprocessorDAGHandler) buildResponseAndSendToStream(chk *chunk.Chunk, tps []*types.FieldType, stream tikvpb.Tikv_CoprocessorStreamServer) error { + chunks, err := h.buildChunk(chk, tps) + if err != nil { + return stream.Send(h.buildErrorResponse(err)) + } + + for i := range chunks { + resp := h.buildStreamResponse(&chunks[i]) + if err = stream.Send(resp); err != nil { + return err + } + } + return nil +} + +func (h *CoprocessorDAGHandler) buildDAGExecutor(req *coprocessor.Request) (exec.Executor, error) { + if req.GetTp() != kv.ReqTypeDAG { + return nil, errors.Errorf("unsupported request type %d", req.GetTp()) + } + dagReq := new(tipb.DAGRequest) + err := proto.Unmarshal(req.Data, dagReq) + if err != nil { + return nil, errors.Trace(err) + } + + if dagReq.User != nil { + pm := privilege.GetPrivilegeManager(h.sctx) + if pm != nil { + h.sctx.GetSessionVars().User = &auth.UserIdentity{ + Username: dagReq.User.UserName, + Hostname: dagReq.User.UserHost, + } + authName, authHost, success := pm.MatchIdentity(dagReq.User.UserName, dagReq.User.UserHost, false) + if success && pm.GetAuthWithoutVerification(authName, authHost) { + h.sctx.GetSessionVars().User.AuthUsername = authName + h.sctx.GetSessionVars().User.AuthHostname = authHost + h.sctx.GetSessionVars().ActiveRoles = pm.GetDefaultRoles(authName, authHost) + } + } + } + + stmtCtx := h.sctx.GetSessionVars().StmtCtx + stmtCtx.SetFlagsFromPBFlag(dagReq.Flags) + tz, err := timeutil.ConstructTimeZone(dagReq.TimeZoneName, int(dagReq.TimeZoneOffset)) + if err != nil { + return nil, errors.Trace(err) + } + + stmtCtx.SetTimeZone(tz) + h.sctx.GetSessionVars().TimeZone = tz + h.dagReq = dagReq + is := h.sctx.GetInfoSchema().(infoschema.InfoSchema) + // Build physical plan. + bp := core.NewPBPlanBuilder(h.sctx, is, req.Ranges) + plan, err := bp.Build(dagReq.Executors) + if err != nil { + return nil, errors.Trace(err) + } + plan = core.InjectExtraProjection(plan) + // Build executor. + b := newExecutorBuilder(h.sctx, is, nil) + return b.build(plan), nil +} + +func (h *CoprocessorDAGHandler) buildChunk(chk *chunk.Chunk, tps []*types.FieldType) (chunks []tipb.Chunk, err error) { + switch h.dagReq.EncodeType { + case tipb.EncodeType_TypeDefault: + chunks, err = h.encodeDefault(chk, tps) + case tipb.EncodeType_TypeChunk: + chunks, err = h.encodeChunk(chk, tps) + default: + return nil, errors.Errorf("unknown DAG encode type: %v", h.dagReq.EncodeType) + } + return chunks, err +} + +func (h *CoprocessorDAGHandler) buildUnaryResponse(chunks []tipb.Chunk) *coprocessor.Response { + selResp := tipb.SelectResponse{ + Chunks: chunks, + EncodeType: h.dagReq.EncodeType, + } + if h.dagReq.CollectExecutionSummaries != nil && *h.dagReq.CollectExecutionSummaries { + execSummary := make([]*tipb.ExecutorExecutionSummary, len(h.dagReq.Executors)) + for i := range execSummary { + // TODO: Add real executor execution summary information. + execSummary[i] = &tipb.ExecutorExecutionSummary{} + } + selResp.ExecutionSummaries = execSummary + } + data, err := proto.Marshal(&selResp) + if err != nil { + return h.buildErrorResponse(err) + } + return &coprocessor.Response{ + Data: data, + } +} + +func (h *CoprocessorDAGHandler) buildStreamResponse(chunk *tipb.Chunk) *coprocessor.Response { + data, err := chunk.Marshal() + if err != nil { + return h.buildErrorResponse(err) + } + streamResponse := tipb.StreamResponse{ + Data: data, + } + var resp = &coprocessor.Response{} + resp.Data, err = proto.Marshal(&streamResponse) + if err != nil { + resp.OtherError = err.Error() + } + return resp +} + +func (*CoprocessorDAGHandler) buildErrorResponse(err error) *coprocessor.Response { + return &coprocessor.Response{ + OtherError: err.Error(), + } +} + +func (h *CoprocessorDAGHandler) encodeChunk(chk *chunk.Chunk, colTypes []*types.FieldType) ([]tipb.Chunk, error) { + colOrdinal := h.dagReq.OutputOffsets + respColTypes := make([]*types.FieldType, 0, len(colOrdinal)) + for _, ordinal := range colOrdinal { + respColTypes = append(respColTypes, colTypes[ordinal]) + } + encoder := chunk.NewCodec(respColTypes) + cur := tipb.Chunk{} + cur.RowsData = append(cur.RowsData, encoder.Encode(chk)...) + return []tipb.Chunk{cur}, nil +} + +func (h *CoprocessorDAGHandler) encodeDefault(chk *chunk.Chunk, tps []*types.FieldType) ([]tipb.Chunk, error) { + colOrdinal := h.dagReq.OutputOffsets + stmtCtx := h.sctx.GetSessionVars().StmtCtx + requestedRow := make([]byte, 0) + chunks := []tipb.Chunk{} + for i := 0; i < chk.NumRows(); i++ { + requestedRow = requestedRow[:0] + row := chk.GetRow(i) + for _, ordinal := range colOrdinal { + data, err := codec.EncodeValue(stmtCtx, nil, row.GetDatum(int(ordinal), tps[ordinal])) + if err != nil { + return nil, err + } + requestedRow = append(requestedRow, data...) + } + chunks = h.appendRow(chunks, requestedRow, i) + } + return chunks, nil +} + +const rowsPerChunk = 64 + +func (*CoprocessorDAGHandler) appendRow(chunks []tipb.Chunk, data []byte, rowCnt int) []tipb.Chunk { + if rowCnt%rowsPerChunk == 0 { + chunks = append(chunks, tipb.Chunk{}) + } + cur := &chunks[len(chunks)-1] + cur.RowsData = append(cur.RowsData, data...) + return chunks +} diff --git a/executor/cte.go b/pkg/executor/cte.go similarity index 97% rename from executor/cte.go rename to pkg/executor/cte.go index 5914d9a84e6b6..9928bc652b639 100644 --- a/executor/cte.go +++ b/pkg/executor/cte.go @@ -20,16 +20,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/cteutil" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/cteutil" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/memory" ) var _ exec.Executor = &CTEExec{} diff --git a/executor/cte_table_reader.go b/pkg/executor/cte_table_reader.go similarity index 94% rename from executor/cte_table_reader.go rename to pkg/executor/cte_table_reader.go index 3c93eb6d12393..18896057f10b3 100644 --- a/executor/cte_table_reader.go +++ b/pkg/executor/cte_table_reader.go @@ -18,9 +18,9 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/cteutil" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/cteutil" ) // CTETableReaderExec scans data in iterInTbl, which is filled by corresponding CTEExec. diff --git a/pkg/executor/cte_test.go b/pkg/executor/cte_test.go new file mode 100644 index 0000000000000..4133e4401ed97 --- /dev/null +++ b/pkg/executor/cte_test.go @@ -0,0 +1,507 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "math/rand" + "slices" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestBasicCTE(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + rows := tk.MustQuery("with recursive cte1 as (" + + "select 1 c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < 5) " + + "select * from cte1") + rows.Check(testkit.Rows("1", "2", "3", "4", "5")) + + // Two seed parts. + rows = tk.MustQuery("with recursive cte1 as (" + + "select 1 c1 " + + "union all " + + "select 2 c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < 10) " + + "select * from cte1 order by c1") + rows.Check(testkit.Rows("1", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "9", "10", "10")) + + // Two recursive parts. + rows = tk.MustQuery("with recursive cte1 as (" + + "select 1 c1 " + + "union all " + + "select 2 c1 " + + "union all " + + "select c1 + 1 c1 from cte1 where c1 < 3 " + + "union all " + + "select c1 + 2 c1 from cte1 where c1 < 5) " + + "select * from cte1 order by c1") + rows.Check(testkit.Rows("1", "2", "2", "3", "3", "3", "4", "4", "5", "5", "5", "6", "6")) + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(a int);") + tk.MustExec("insert into t1 values(1);") + tk.MustExec("insert into t1 values(2);") + rows = tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS(WITH RECURSIVE qn AS (SELECT a*0 AS b UNION ALL SELECT b+1 FROM qn WHERE b=0) SELECT * FROM qn WHERE b=a);") + rows.Check(testkit.Rows("1")) + rows = tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS( WITH RECURSIVE qn AS (SELECT a*0 AS b UNION ALL SELECT b+1 FROM qn WHERE b=0 or b = 1) SELECT * FROM qn WHERE b=a );") + rows.Check(testkit.Rows("1", "2")) + + rows = tk.MustQuery("with recursive c(p) as (select 1), cte(a, b) as (select 1, 1 union select a+1, 1 from cte, c where a < 5) select * from cte order by 1, 2;") + rows.Check(testkit.Rows("1 1", "2 1", "3 1", "4 1", "5 1")) +} + +func TestUnionDistinct(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + // Basic test. UNION/UNION ALL intersects. + rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select 1 union select 1 union all select c1 + 1 from cte1 where c1 < 3) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2", "3")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union all select 1 union select 1 union all select c1 + 1 from cte1 where c1 < 3) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int, c2 int);") + tk.MustExec("insert into t1 values(1, 1), (1, 2), (2, 2);") + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from t1) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec("insert into t1 values(1), (1), (1), (2), (2), (2);") + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 4) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2", "3", "4")) +} + +func TestCTEMaxRecursionDepth(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + tk.MustExec("set @@cte_max_recursion_depth = -1;") + err := tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 100) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + // If there is no recursive part, query runs ok. + rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) + rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) + + tk.MustExec("set @@cte_max_recursion_depth = 0;") + err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 0) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 1) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + // If there is no recursive part, query runs ok. + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) + rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) + + tk.MustExec("set @@cte_max_recursion_depth = 1;") + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 0) select * from cte1;") + rows.Check(testkit.Rows("1")) + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 1) select * from cte1;") + rows.Check(testkit.Rows("1")) + err = tk.QueryToErr("with recursive cte1(c1) as (select 1 union select c1 + 1 c1 from cte1 where c1 < 2) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 2 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + // If there is no recursive part, query runs ok. + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) + rows = tk.MustQuery("with cte1(c1) as (select 1 union select 2) select * from cte1 order by c1;") + rows.Check(testkit.Rows("1", "2")) +} + +func TestCTEWithLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + // Basic recursive tests. + rows := tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 0) select * from cte1") + rows.Check(testkit.Rows("1", "2", "3", "4", "5")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 1) select * from cte1") + rows.Check(testkit.Rows("2", "3", "4", "5", "6")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 10) select * from cte1") + rows.Check(testkit.Rows("11", "12", "13", "14", "15")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 995) select * from cte1") + rows.Check(testkit.Rows("996", "997", "998", "999", "1000")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 6) select * from cte1;") + rows.Check(testkit.Rows("7", "8", "9", "10", "11")) + + // Test with cte_max_recursion_depth + tk.MustExec("set cte_max_recursion_depth=2;") + rows = tk.MustQuery("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") + rows.Check(testkit.Rows("2")) + + err := tk.QueryToErr("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 3 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + + tk.MustExec("set cte_max_recursion_depth=1000;") + rows = tk.MustQuery("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 5 offset 996) select * from cte1;") + rows.Check(testkit.Rows("996", "997", "998", "999", "1000")) + + err = tk.QueryToErr("with recursive cte1(c1) as (select 0 union select c1 + 1 from cte1 limit 5 offset 997) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1001 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 0 offset 1) select * from cte1") + rows.Check(testkit.Rows()) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 0 offset 10) select * from cte1") + rows.Check(testkit.Rows()) + + // Test join. + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 order by dt1.c1, dt2.c1;") + rows.Check(testkit.Rows("2 2", "2 3", "3 2", "3 3")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1 order by dt1.c1, dt1.c1;") + rows.Check(testkit.Rows("2 2", "3 3")) + + // Test subquery. + // Different with mysql, maybe it's mysql bug?(https://bugs.mysql.com/bug.php?id=103890&thanks=4) + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 where c1 in (select 2);") + rows.Check(testkit.Rows("2")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 dt where c1 in (select c1 from cte1 where 1 = dt.c1 - 1);") + rows.Check(testkit.Rows("2")) + + // Test Apply. + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 2 offset 1) select c1 from cte1 where cte1.c1 = (select dt1.c1 from cte1 dt1 where dt1.c1 = cte1.c1);") + rows.Check(testkit.Rows("2", "3")) + + // Recursive tests with table. + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec("insert into t1 values(1), (2), (3);") + + // Error: ERROR 1221 (HY000): Incorrect usage of UNION and LIMIT. + // Limit can only be at the end of SQL stmt. + err = tk.ExecToErr("with recursive cte1(c1) as (select c1 from t1 limit 1 offset 1 union select c1 + 1 from cte1 limit 0 offset 1) select * from cte1") + require.EqualError(t, err, "[planner:1221]Incorrect usage of UNION and LIMIT") + + // Basic non-recusive tests. + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 1 offset 1) select * from cte1") + rows.Check(testkit.Rows("2")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 0 offset 1) select * from cte1") + rows.Check(testkit.Rows()) + + rows = tk.MustQuery("with recursive cte1(c1) as (select 1 union select 2 order by 1 limit 2 offset 0) select * from cte1") + rows.Check(testkit.Rows("1", "2")) + + // Test with table. + tk.MustExec("drop table if exists t1;") + insertStr := "insert into t1 values(0)" + for i := 1; i < 300; i++ { + insertStr += fmt.Sprintf(", (%d)", i) + } + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec(insertStr) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 1) select * from cte1") + rows.Check(testkit.Rows("0")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 1 offset 100) select * from cte1") + rows.Check(testkit.Rows("100")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 c1 from cte1 limit 5 offset 100) select * from cte1") + rows.Check(testkit.Rows("100", "101", "102", "103", "104")) + + // Basic non-recursive tests. + rows = tk.MustQuery("with cte1 as (select c1 from t1 limit 2 offset 1) select * from cte1") + rows.Check(testkit.Rows("1", "2")) + + rows = tk.MustQuery("with cte1 as (select c1 from t1 limit 2 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") + rows.Check(testkit.Rows("1 1", "2 2")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 0 offset 1) select * from cte1") + rows.Check(testkit.Rows()) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 0 offset 1) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") + rows.Check(testkit.Rows()) + + // rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select 2 limit 5 offset 100) select * from cte1") + // rows.Check(testkit.Rows("100", "101", "102", "103", "104")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 limit 3 offset 100) select * from cte1") + rows.Check(testkit.Rows("100", "101", "102")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 limit 3 offset 100) select * from cte1 dt1 join cte1 dt2 on dt1.c1 = dt2.c1") + rows.Check(testkit.Rows("100 100", "101 101", "102 102")) + + // Test limit 0. + tk.MustExec("set cte_max_recursion_depth = 0;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec("insert into t1 values(0);") + rows = tk.MustQuery("with recursive cte1 as (select 1/c1 c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 2 limit 0) select * from cte1;") + rows.Check(testkit.Rows()) + // MySQL err: ERROR 1365 (22012): Division by 0. Because it gives error when computing 1/c1. + err = tk.QueryToErr("with recursive cte1 as (select 1/c1 c1 from t1 union select c1 + 1 c1 from cte1 where c1 < 2 limit 1) select * from cte1;") + require.EqualError(t, err, "[executor:3636]Recursive query aborted after 1 iterations. Try increasing @@cte_max_recursion_depth to a larger value") + + tk.MustExec("set cte_max_recursion_depth = 1000;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec("insert into t1 values(1), (2), (3);") + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 2) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "4")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "4", "5")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "4", "5", "6")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 3) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") + rows.Check(testkit.Rows("4")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 3) select * from cte1;") + rows.Check(testkit.Rows("4", "5")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 3) select * from cte1;") + rows.Check(testkit.Rows("4", "5", "6")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 3) select * from cte1;") + rows.Check(testkit.Rows("4", "5", "6", "7")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 0 offset 4) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 1 offset 4) select * from cte1;") + rows.Check(testkit.Rows("5")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 2 offset 4) select * from cte1;") + rows.Check(testkit.Rows("5", "6")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 3 offset 4) select * from cte1;") + rows.Check(testkit.Rows("5", "6", "7")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union select c1 + 1 from cte1 limit 4 offset 4) select * from cte1;") + rows.Check(testkit.Rows("5", "6", "7", "8")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 2) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "2")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "2", "3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 2) select * from cte1;") + rows.Check(testkit.Rows("3", "2", "3", "4")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 3) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 3) select * from cte1;") + rows.Check(testkit.Rows("2")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 3) select * from cte1;") + rows.Check(testkit.Rows("2", "3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 3) select * from cte1;") + rows.Check(testkit.Rows("2", "3", "4")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 3) select * from cte1;") + rows.Check(testkit.Rows("2", "3", "4", "3")) + + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 0 offset 4) select * from cte1;") + rows.Check(testkit.Rows()) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 1 offset 4) select * from cte1;") + rows.Check(testkit.Rows("3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 2 offset 4) select * from cte1;") + rows.Check(testkit.Rows("3", "4")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 3 offset 4) select * from cte1;") + rows.Check(testkit.Rows("3", "4", "3")) + rows = tk.MustQuery("with recursive cte1(c1) as (select c1 from t1 union all select c1 + 1 from cte1 limit 4 offset 4) select * from cte1;") + rows.Check(testkit.Rows("3", "4", "3", "4")) +} + +func TestSpillToDisk(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("SET GLOBAL tidb_enable_tmp_storage_on_oom = 1") + defer tk.MustExec("SET GLOBAL tidb_enable_tmp_storage_on_oom = 0") + tk.MustExec("use test;") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testCTEStorageSpill", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testCTEStorageSpill")) + tk.MustExec("set tidb_mem_quota_query = 1073741824;") + }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill")) + }() + + // Use duplicated rows to test UNION DISTINCT. + tk.MustExec("set tidb_mem_quota_query = 1073741824;") + insertStr := "insert into t1 values(0)" + rowNum := 1000 + vals := make([]int, rowNum) + vals[0] = 0 + for i := 1; i < rowNum; i++ { + v := rand.Intn(100) + vals[i] = v + insertStr += fmt.Sprintf(", (%d)", v) + } + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int);") + tk.MustExec(insertStr) + tk.MustExec("set tidb_mem_quota_query = 40000;") + tk.MustExec("set cte_max_recursion_depth = 500000;") + sql := fmt.Sprintf("with recursive cte1 as ( "+ + "select c1 from t1 "+ + "union "+ + "select c1 + 1 c1 from cte1 where c1 < %d) "+ + "select c1 from cte1 order by c1;", rowNum) + rows := tk.MustQuery(sql) + + memTracker := tk.Session().GetSessionVars().StmtCtx.MemTracker + diskTracker := tk.Session().GetSessionVars().StmtCtx.DiskTracker + require.Greater(t, memTracker.MaxConsumed(), int64(0)) + require.Greater(t, diskTracker.MaxConsumed(), int64(0)) + + slices.Sort(vals) + resRows := make([]string, 0, rowNum) + for i := vals[0]; i <= rowNum; i++ { + resRows = append(resRows, fmt.Sprintf("%d", i)) + } + rows.Check(testkit.Rows(resRows...)) +} + +func TestCTEExecError(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists src;") + tk.MustExec("create table src(first int, second int);") + + insertStr := fmt.Sprintf("insert into src values (%d, %d)", rand.Intn(1000), rand.Intn(1000)) + for i := 0; i < 1000; i++ { + insertStr += fmt.Sprintf(",(%d, %d)", rand.Intn(1000), rand.Intn(1000)) + } + insertStr += ";" + tk.MustExec(insertStr) + + // Increase projection concurrency and decrease chunk size + // to increase the probability of reproducing the problem. + tk.MustExec("set tidb_max_chunk_size = 32") + tk.MustExec("set tidb_projection_concurrency = 20") + for i := 0; i < 10; i++ { + err := tk.QueryToErr("with recursive cte(iter, first, second, result) as " + + "(select 1, first, second, first+second from src " + + " union all " + + "select iter+1, second, result, second+result from cte where iter < 80 )" + + "select * from cte") + require.True(t, terror.ErrorEqual(err, types.ErrOverflow)) + } +} + +// https://github.com/pingcap/tidb/issues/33965. +func TestCTEsInView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + tk.MustExec("create database if not exists test1;") + tk.MustExec("create table test.t (a int);") + tk.MustExec("create table test1.t (a int);") + tk.MustExec("insert into test.t values (1);") + tk.MustExec("insert into test1.t values (2);") + + tk.MustExec("use test;") + tk.MustExec("create definer='root'@'localhost' view test.v as with tt as (select * from t) select * from tt;") + tk.MustQuery("select * from test.v;").Check(testkit.Rows("1")) + tk.MustExec("use test1;") + tk.MustQuery("select * from test.v;").Check(testkit.Rows("1")) +} + +func TestCTEPanic(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("create table t1(c1 int)") + tk.MustExec("insert into t1 values(1), (2), (3)") + + fpPathPrefix := "github.com/pingcap/tidb/pkg/executor/" + fp := "testCTESeedPanic" + require.NoError(t, failpoint.Enable(fpPathPrefix+fp, fmt.Sprintf(`panic("%s")`, fp))) + err := tk.QueryToErr("with recursive cte1 as (select c1 from t1 union all select c1 + 1 from cte1 where c1 < 5) select t_alias_1.c1 from cte1 as t_alias_1 inner join cte1 as t_alias_2 on t_alias_1.c1 = t_alias_2.c1 order by c1") + require.Contains(t, err.Error(), fp) + require.NoError(t, failpoint.Disable(fpPathPrefix+fp)) + + fp = "testCTERecursivePanic" + require.NoError(t, failpoint.Enable(fpPathPrefix+fp, fmt.Sprintf(`panic("%s")`, fp))) + err = tk.QueryToErr("with recursive cte1 as (select c1 from t1 union all select c1 + 1 from cte1 where c1 < 5) select t_alias_1.c1 from cte1 as t_alias_1 inner join cte1 as t_alias_2 on t_alias_1.c1 = t_alias_2.c1 order by c1") + require.Contains(t, err.Error(), fp) + require.NoError(t, failpoint.Disable(fpPathPrefix+fp)) +} + +func TestCTEDelSpillFile(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1(c1 int, c2 int);") + tk.MustExec("create table t2(c1 int);") + tk.MustExec("set @@cte_max_recursion_depth = 1000000;") + tk.MustExec("set global tidb_mem_oom_action = 'log';") + tk.MustExec("set @@tidb_mem_quota_query = 100;") + tk.MustExec("insert into t2 values(1);") + tk.MustExec("insert into t1 (c1, c2) with recursive cte1 as (select c1 from t2 union select cte1.c1 + 1 from cte1 where cte1.c1 < 100000) select cte1.c1, cte1.c1+1 from cte1;") + require.Nil(t, tk.Session().GetSessionVars().StmtCtx.CTEStorageMap) +} + +func TestCTEShareCorColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1(c1 int, c2 varchar(100));") + tk.MustExec("insert into t1 values(1, '2020-10-10');") + tk.MustExec("create table t2(c1 int, c2 date);") + tk.MustExec("insert into t2 values(1, '2020-10-10');") + for i := 0; i < 100; i++ { + tk.MustQuery("with cte1 as (select t1.c1, (select t2.c2 from t2 where t2.c2 = str_to_date(t1.c2, '%Y-%m-%d')) from t1 inner join t2 on t1.c1 = t2.c1) select /*+ hash_join_build(alias1) */ * from cte1 alias1 inner join cte1 alias2 on alias1.c1 = alias2.c1;").Check(testkit.Rows("1 2020-10-10 1 2020-10-10")) + tk.MustQuery("with cte1 as (select t1.c1, (select t2.c2 from t2 where t2.c2 = str_to_date(t1.c2, '%Y-%m-%d')) from t1 inner join t2 on t1.c1 = t2.c1) select /*+ hash_join_build(alias2) */ * from cte1 alias1 inner join cte1 alias2 on alias1.c1 = alias2.c1;").Check(testkit.Rows("1 2020-10-10 1 2020-10-10")) + } + + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(a int);") + tk.MustExec("insert into t1 values(1), (2);") + tk.MustQuery("SELECT * FROM t1 dt WHERE EXISTS( WITH RECURSIVE qn AS (SELECT a AS b UNION ALL SELECT b+1 FROM qn WHERE b=0 or b = 1) SELECT * FROM qn dtqn1 where exists (select /*+ NO_DECORRELATE() */ b from qn where dtqn1.b+1));").Check(testkit.Rows("1", "2")) +} diff --git a/pkg/executor/ddl.go b/pkg/executor/ddl.go new file mode 100644 index 0000000000000..9cf577ea31688 --- /dev/null +++ b/pkg/executor/ddl.go @@ -0,0 +1,766 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// DDLExec represents a DDL executor. +// It grabs a DDL instance from Domain, calling the DDL methods to do the work. +type DDLExec struct { + exec.BaseExecutor + + stmt ast.StmtNode + is infoschema.InfoSchema + tempTableDDL temptable.TemporaryTableDDL + done bool +} + +// toErr converts the error to the ErrInfoSchemaChanged when the schema is outdated. +func (e *DDLExec) toErr(err error) error { + // The err may be cause by schema changed, here we distinguish the ErrInfoSchemaChanged error from other errors. + dom := domain.GetDomain(e.Ctx()) + checker := domain.NewSchemaChecker(dom, e.is.SchemaMetaVersion(), nil, true) + txn, err1 := e.Ctx().Txn(true) + if err1 != nil { + logutil.BgLogger().Error("active txn failed", zap.Error(err1)) + return err + } + _, schemaInfoErr := checker.Check(txn.StartTS()) + if schemaInfoErr != nil { + return errors.Trace(schemaInfoErr) + } + return err +} + +func (e *DDLExec) getLocalTemporaryTable(schema model.CIStr, table model.CIStr) (table.Table, bool) { + tbl, err := e.Ctx().GetInfoSchema().(infoschema.InfoSchema).TableByName(schema, table) + if infoschema.ErrTableNotExists.Equal(err) { + return nil, false + } + + if tbl.Meta().TempTableType != model.TempTableLocal { + return nil, false + } + + return tbl, true +} + +// Next implements the Executor Next interface. +func (e *DDLExec) Next(ctx context.Context, _ *chunk.Chunk) (err error) { + if e.done { + return nil + } + e.done = true + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnDDL) + // For each DDL, we should commit the previous transaction and create a new transaction. + // Following cases are exceptions + var localTempTablesToDrop []*ast.TableName + switch s := e.stmt.(type) { + case *ast.CreateTableStmt: + if s.TemporaryKeyword == ast.TemporaryLocal { + return e.createSessionTemporaryTable(s) + } + case *ast.DropTableStmt: + if s.IsView { + break + } + + for tbIdx := len(s.Tables) - 1; tbIdx >= 0; tbIdx-- { + if _, ok := e.getLocalTemporaryTable(s.Tables[tbIdx].Schema, s.Tables[tbIdx].Name); ok { + localTempTablesToDrop = append(localTempTablesToDrop, s.Tables[tbIdx]) + s.Tables = append(s.Tables[:tbIdx], s.Tables[tbIdx+1:]...) + } + } + + // Statement `DROP TEMPORARY TABLE ...` should not have non-local temporary tables + if s.TemporaryKeyword == ast.TemporaryLocal && len(s.Tables) > 0 { + nonExistsTables := make([]string, 0, len(s.Tables)) + for _, tn := range s.Tables { + nonExistsTables = append(nonExistsTables, ast.Ident{Schema: tn.Schema, Name: tn.Name}.String()) + } + err = infoschema.ErrTableDropExists.GenWithStackByArgs(strings.Join(nonExistsTables, ",")) + if s.IfExists { + e.Ctx().GetSessionVars().StmtCtx.AppendNote(err) + return nil + } + return err + } + + // if all tables are local temporary, directly drop those tables. + if len(s.Tables) == 0 { + return e.dropLocalTemporaryTables(localTempTablesToDrop) + } + } + + if err = sessiontxn.NewTxnInStmt(ctx, e.Ctx()); err != nil { + return err + } + + defer func() { + e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue = false + e.Ctx().GetSessionVars().StmtCtx.DDLJobID = 0 + }() + + switch x := e.stmt.(type) { + case *ast.AlterDatabaseStmt: + err = e.executeAlterDatabase(x) + case *ast.AlterTableStmt: + err = e.executeAlterTable(ctx, x) + case *ast.CreateIndexStmt: + err = e.executeCreateIndex(x) + case *ast.CreateDatabaseStmt: + err = e.executeCreateDatabase(x) + case *ast.FlashBackDatabaseStmt: + err = e.executeFlashbackDatabase(x) + case *ast.CreateTableStmt: + err = e.executeCreateTable(x) + case *ast.CreateViewStmt: + err = e.executeCreateView(ctx, x) + case *ast.DropIndexStmt: + err = e.executeDropIndex(x) + case *ast.DropDatabaseStmt: + err = e.executeDropDatabase(x) + case *ast.DropTableStmt: + if x.IsView { + err = e.executeDropView(x) + } else { + err = e.executeDropTable(x) + if err == nil { + err = e.dropLocalTemporaryTables(localTempTablesToDrop) + } + } + case *ast.RecoverTableStmt: + err = e.executeRecoverTable(x) + case *ast.FlashBackTableStmt: + err = e.executeFlashbackTable(x) + case *ast.FlashBackToTimestampStmt: + if len(x.Tables) != 0 { + err = dbterror.ErrGeneralUnsupportedDDL.GenWithStack("Unsupported FLASHBACK table TO TIMESTAMP") + } else if x.DBName.O != "" { + err = dbterror.ErrGeneralUnsupportedDDL.GenWithStack("Unsupported FLASHBACK database TO TIMESTAMP") + } else { + err = e.executeFlashBackCluster(x) + } + case *ast.RenameTableStmt: + err = e.executeRenameTable(x) + case *ast.TruncateTableStmt: + err = e.executeTruncateTable(x) + case *ast.LockTablesStmt: + err = e.executeLockTables(x) + case *ast.UnlockTablesStmt: + err = e.executeUnlockTables(x) + case *ast.CleanupTableLockStmt: + err = e.executeCleanupTableLock(x) + case *ast.RepairTableStmt: + err = e.executeRepairTable(x) + case *ast.CreateSequenceStmt: + err = e.executeCreateSequence(x) + case *ast.DropSequenceStmt: + err = e.executeDropSequence(x) + case *ast.AlterSequenceStmt: + err = e.executeAlterSequence(x) + case *ast.CreatePlacementPolicyStmt: + err = e.executeCreatePlacementPolicy(x) + case *ast.DropPlacementPolicyStmt: + err = e.executeDropPlacementPolicy(x) + case *ast.AlterPlacementPolicyStmt: + err = e.executeAlterPlacementPolicy(x) + case *ast.CreateResourceGroupStmt: + err = e.executeCreateResourceGroup(x) + case *ast.DropResourceGroupStmt: + err = e.executeDropResourceGroup(x) + case *ast.AlterResourceGroupStmt: + err = e.executeAlterResourceGroup(x) + } + if err != nil { + // If the owner return ErrTableNotExists error when running this DDL, it may be caused by schema changed, + // otherwise, ErrTableNotExists can be returned before putting this DDL job to the job queue. + if (e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue && infoschema.ErrTableNotExists.Equal(err)) || + !e.Ctx().GetSessionVars().StmtCtx.IsDDLJobInQueue { + return e.toErr(err) + } + return err + } + + dom := domain.GetDomain(e.Ctx()) + // Update InfoSchema in TxnCtx, so it will pass schema check. + is := dom.InfoSchema() + txnCtx := e.Ctx().GetSessionVars().TxnCtx + txnCtx.InfoSchema = is + // DDL will force commit old transaction, after DDL, in transaction status should be false. + e.Ctx().GetSessionVars().SetInTxn(false) + return nil +} + +func (e *DDLExec) executeTruncateTable(s *ast.TruncateTableStmt) error { + ident := ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name} + if _, exist := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); exist { + return e.tempTableDDL.TruncateLocalTemporaryTable(s.Table.Schema, s.Table.Name) + } + err := domain.GetDomain(e.Ctx()).DDL().TruncateTable(e.Ctx(), ident) + return err +} + +func (e *DDLExec) executeRenameTable(s *ast.RenameTableStmt) error { + for _, tables := range s.TableToTables { + if _, ok := e.getLocalTemporaryTable(tables.OldTable.Schema, tables.OldTable.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("RENAME TABLE") + } + } + return domain.GetDomain(e.Ctx()).DDL().RenameTable(e.Ctx(), s) +} + +func (e *DDLExec) executeCreateDatabase(s *ast.CreateDatabaseStmt) error { + err := domain.GetDomain(e.Ctx()).DDL().CreateSchema(e.Ctx(), s) + return err +} + +func (e *DDLExec) executeAlterDatabase(s *ast.AlterDatabaseStmt) error { + err := domain.GetDomain(e.Ctx()).DDL().AlterSchema(e.Ctx(), s) + return err +} + +func (e *DDLExec) executeCreateTable(s *ast.CreateTableStmt) error { + err := domain.GetDomain(e.Ctx()).DDL().CreateTable(e.Ctx(), s) + return err +} + +func (e *DDLExec) createSessionTemporaryTable(s *ast.CreateTableStmt) error { + is := e.Ctx().GetInfoSchema().(infoschema.InfoSchema) + dbInfo, ok := is.SchemaByName(s.Table.Schema) + if !ok { + return infoschema.ErrDatabaseNotExists.GenWithStackByArgs(s.Table.Schema.O) + } + + _, exists := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name) + if exists { + err := infoschema.ErrTableExists.GenWithStackByArgs(ast.Ident{Schema: s.Table.Schema, Name: s.Table.Name}) + if s.IfNotExists { + e.Ctx().GetSessionVars().StmtCtx.AppendNote(err) + return nil + } + return err + } + + tbInfo, err := ddl.BuildSessionTemporaryTableInfo(e.Ctx(), is, s, dbInfo.Charset, dbInfo.Collate, dbInfo.PlacementPolicyRef) + if err != nil { + return err + } + + if err = e.tempTableDDL.CreateLocalTemporaryTable(dbInfo, tbInfo); err != nil { + return err + } + + sessiontxn.GetTxnManager(e.Ctx()).OnLocalTemporaryTableCreated() + return nil +} + +func (e *DDLExec) executeCreateView(ctx context.Context, s *ast.CreateViewStmt) error { + ret := &core.PreprocessorReturn{} + err := core.Preprocess(ctx, e.Ctx(), s.Select, core.WithPreprocessorReturn(ret)) + if err != nil { + return errors.Trace(err) + } + if ret.IsStaleness { + return exeerrors.ErrViewInvalid.GenWithStackByArgs(s.ViewName.Schema.L, s.ViewName.Name.L) + } + + return domain.GetDomain(e.Ctx()).DDL().CreateView(e.Ctx(), s) +} + +func (e *DDLExec) executeCreateIndex(s *ast.CreateIndexStmt) error { + if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("CREATE INDEX") + } + + return domain.GetDomain(e.Ctx()).DDL().CreateIndex(e.Ctx(), s) +} + +func (e *DDLExec) executeDropDatabase(s *ast.DropDatabaseStmt) error { + dbName := s.Name + + // Protect important system table from been dropped by a mistake. + // I can hardly find a case that a user really need to do this. + if dbName.L == "mysql" { + return errors.New("Drop 'mysql' database is forbidden") + } + + err := domain.GetDomain(e.Ctx()).DDL().DropSchema(e.Ctx(), s) + sessionVars := e.Ctx().GetSessionVars() + if err == nil && strings.ToLower(sessionVars.CurrentDB) == dbName.L { + sessionVars.CurrentDB = "" + err = sessionVars.SetSystemVar(variable.CharsetDatabase, mysql.DefaultCharset) + if err != nil { + return err + } + err = sessionVars.SetSystemVar(variable.CollationDatabase, mysql.DefaultCollationName) + if err != nil { + return err + } + } + return err +} + +func (e *DDLExec) executeDropTable(s *ast.DropTableStmt) error { + return domain.GetDomain(e.Ctx()).DDL().DropTable(e.Ctx(), s) +} + +func (e *DDLExec) executeDropView(s *ast.DropTableStmt) error { + return domain.GetDomain(e.Ctx()).DDL().DropView(e.Ctx(), s) +} + +func (e *DDLExec) executeDropSequence(s *ast.DropSequenceStmt) error { + return domain.GetDomain(e.Ctx()).DDL().DropSequence(e.Ctx(), s) +} + +func (e *DDLExec) dropLocalTemporaryTables(localTempTables []*ast.TableName) error { + if len(localTempTables) == 0 { + return nil + } + + for _, tb := range localTempTables { + err := e.tempTableDDL.DropLocalTemporaryTable(tb.Schema, tb.Name) + if err != nil { + return err + } + } + + return nil +} + +func (e *DDLExec) executeDropIndex(s *ast.DropIndexStmt) error { + if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("DROP INDEX") + } + + return domain.GetDomain(e.Ctx()).DDL().DropIndex(e.Ctx(), s) +} + +func (e *DDLExec) executeAlterTable(ctx context.Context, s *ast.AlterTableStmt) error { + if _, ok := e.getLocalTemporaryTable(s.Table.Schema, s.Table.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("ALTER TABLE") + } + + return domain.GetDomain(e.Ctx()).DDL().AlterTable(ctx, e.Ctx(), s) +} + +// executeRecoverTable represents a recover table executor. +// It is built from "recover table" statement, +// is used to recover the table that deleted by mistake. +func (e *DDLExec) executeRecoverTable(s *ast.RecoverTableStmt) error { + dom := domain.GetDomain(e.Ctx()) + var job *model.Job + var err error + var tblInfo *model.TableInfo + if s.JobID != 0 { + job, tblInfo, err = e.getRecoverTableByJobID(s, dom) + } else { + job, tblInfo, err = e.getRecoverTableByTableName(s.Table) + } + if err != nil { + return err + } + // Check the table ID was not exists. + tbl, ok := dom.InfoSchema().TableByID(tblInfo.ID) + if ok { + return infoschema.ErrTableExists.GenWithStack("Table '%-.192s' already been recover to '%-.192s', can't be recover repeatedly", s.Table.Name.O, tbl.Meta().Name.O) + } + + m, err := domain.GetDomain(e.Ctx()).GetSnapshotMeta(job.StartTS) + if err != nil { + return err + } + autoIDs, err := m.GetAutoIDAccessors(job.SchemaID, job.TableID).Get() + if err != nil { + return err + } + + recoverInfo := &ddl.RecoverInfo{ + SchemaID: job.SchemaID, + TableInfo: tblInfo, + DropJobID: job.ID, + SnapshotTS: job.StartTS, + AutoIDs: autoIDs, + OldSchemaName: job.SchemaName, + OldTableName: tblInfo.Name.L, + } + // Call DDL RecoverTable. + err = domain.GetDomain(e.Ctx()).DDL().RecoverTable(e.Ctx(), recoverInfo) + return err +} + +func (e *DDLExec) getRecoverTableByJobID(s *ast.RecoverTableStmt, dom *domain.Domain) (*model.Job, *model.TableInfo, error) { + se, err := e.GetSysSession() + if err != nil { + return nil, nil, err + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + defer e.ReleaseSysSession(ctx, se) + job, err := ddl.GetHistoryJobByID(se, s.JobID) + if err != nil { + return nil, nil, err + } + if job == nil { + return nil, nil, dbterror.ErrDDLJobNotFound.GenWithStackByArgs(s.JobID) + } + if job.Type != model.ActionDropTable && job.Type != model.ActionTruncateTable { + return nil, nil, errors.Errorf("Job %v type is %v, not dropped/truncated table", job.ID, job.Type) + } + + // Check GC safe point for getting snapshot infoSchema. + err = gcutil.ValidateSnapshot(e.Ctx(), job.StartTS) + if err != nil { + return nil, nil, err + } + + // Get the snapshot infoSchema before drop table. + snapInfo, err := dom.GetSnapshotInfoSchema(job.StartTS) + if err != nil { + return nil, nil, err + } + // Get table meta from snapshot infoSchema. + table, ok := snapInfo.TableByID(job.TableID) + if !ok { + return nil, nil, infoschema.ErrTableNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", job.SchemaID), + fmt.Sprintf("(Table ID %d)", job.TableID), + ) + } + return job, table.Meta(), nil +} + +// GetDropOrTruncateTableInfoFromJobs gets the dropped/truncated table information from DDL jobs, +// it will use the `start_ts` of DDL job as snapshot to get the dropped/truncated table information. +func GetDropOrTruncateTableInfoFromJobs(jobs []*model.Job, gcSafePoint uint64, dom *domain.Domain, fn func(*model.Job, *model.TableInfo) (bool, error)) (bool, error) { + getTable := func(startTS uint64, schemaID int64, tableID int64) (*model.TableInfo, error) { + snapMeta, err := dom.GetSnapshotMeta(startTS) + if err != nil { + return nil, err + } + tbl, err := snapMeta.GetTable(schemaID, tableID) + return tbl, err + } + return ddl.GetDropOrTruncateTableInfoFromJobsByStore(jobs, gcSafePoint, getTable, fn) +} + +func (e *DDLExec) getRecoverTableByTableName(tableName *ast.TableName) (*model.Job, *model.TableInfo, error) { + txn, err := e.Ctx().Txn(true) + if err != nil { + return nil, nil, err + } + schemaName := tableName.Schema.L + if schemaName == "" { + schemaName = strings.ToLower(e.Ctx().GetSessionVars().CurrentDB) + } + if schemaName == "" { + return nil, nil, errors.Trace(core.ErrNoDB) + } + gcSafePoint, err := gcutil.GetGCSafePoint(e.Ctx()) + if err != nil { + return nil, nil, err + } + var jobInfo *model.Job + var tableInfo *model.TableInfo + dom := domain.GetDomain(e.Ctx()) + handleJobAndTableInfo := func(job *model.Job, tblInfo *model.TableInfo) (bool, error) { + if tblInfo.Name.L != tableName.Name.L { + return false, nil + } + schema, ok := dom.InfoSchema().SchemaByID(job.SchemaID) + if !ok { + return false, nil + } + if schema.Name.L == schemaName { + tableInfo = tblInfo + jobInfo = job + return true, nil + } + return false, nil + } + fn := func(jobs []*model.Job) (bool, error) { + return GetDropOrTruncateTableInfoFromJobs(jobs, gcSafePoint, dom, handleJobAndTableInfo) + } + err = ddl.IterHistoryDDLJobs(txn, fn) + if err != nil { + if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { + return nil, nil, errors.Errorf("Can't find dropped/truncated table '%s' in GC safe point %s", tableName.Name.O, model.TSConvert2Time(gcSafePoint).String()) + } + return nil, nil, err + } + if tableInfo == nil || jobInfo == nil { + return nil, nil, errors.Errorf("Can't find localTemporary/dropped/truncated table: %v in DDL history jobs", tableName.Name) + } + // Dropping local temporary tables won't appear in DDL jobs. + if tableInfo.TempTableType == model.TempTableGlobal { + return nil, nil, exeerrors.ErrUnsupportedFlashbackTmpTable + } + return jobInfo, tableInfo, nil +} + +func (e *DDLExec) executeFlashBackCluster(s *ast.FlashBackToTimestampStmt) error { + flashbackTS, err := staleread.CalculateAsOfTsExpr(context.Background(), e.Ctx(), s.FlashbackTS) + if err != nil { + return err + } + + return domain.GetDomain(e.Ctx()).DDL().FlashbackCluster(e.Ctx(), flashbackTS) +} + +func (e *DDLExec) executeFlashbackTable(s *ast.FlashBackTableStmt) error { + job, tblInfo, err := e.getRecoverTableByTableName(s.Table) + if err != nil { + return err + } + if len(s.NewName) != 0 { + tblInfo.Name = model.NewCIStr(s.NewName) + } + // Check the table ID was not exists. + is := domain.GetDomain(e.Ctx()).InfoSchema() + tbl, ok := is.TableByID(tblInfo.ID) + if ok { + return infoschema.ErrTableExists.GenWithStack("Table '%-.192s' already been flashback to '%-.192s', can't be flashback repeatedly", s.Table.Name.O, tbl.Meta().Name.O) + } + + m, err := domain.GetDomain(e.Ctx()).GetSnapshotMeta(job.StartTS) + if err != nil { + return err + } + autoIDs, err := m.GetAutoIDAccessors(job.SchemaID, job.TableID).Get() + if err != nil { + return err + } + + recoverInfo := &ddl.RecoverInfo{ + SchemaID: job.SchemaID, + TableInfo: tblInfo, + DropJobID: job.ID, + SnapshotTS: job.StartTS, + AutoIDs: autoIDs, + OldSchemaName: job.SchemaName, + OldTableName: s.Table.Name.L, + } + // Call DDL RecoverTable. + err = domain.GetDomain(e.Ctx()).DDL().RecoverTable(e.Ctx(), recoverInfo) + return err +} + +// executeFlashbackDatabase represents a restore schema executor. +// It is built from "flashback schema" statement, +// is used to recover the schema that deleted by mistake. +func (e *DDLExec) executeFlashbackDatabase(s *ast.FlashBackDatabaseStmt) error { + dbName := s.DBName + if len(s.NewName) > 0 { + dbName = model.NewCIStr(s.NewName) + } + // Check the Schema Name was not exists. + is := domain.GetDomain(e.Ctx()).InfoSchema() + if is.SchemaExists(dbName) { + return infoschema.ErrDatabaseExists.GenWithStackByArgs(dbName) + } + recoverSchemaInfo, err := e.getRecoverDBByName(s.DBName) + if err != nil { + return err + } + // Check the Schema ID was not exists. + if schema, ok := is.SchemaByID(recoverSchemaInfo.ID); ok { + return infoschema.ErrDatabaseExists.GenWithStack("Schema '%-.192s' already been recover to '%-.192s', can't be recover repeatedly", s.DBName, schema.Name.O) + } + recoverSchemaInfo.Name = dbName + // Call DDL RecoverSchema. + err = domain.GetDomain(e.Ctx()).DDL().RecoverSchema(e.Ctx(), recoverSchemaInfo) + return err +} + +func (e *DDLExec) getRecoverDBByName(schemaName model.CIStr) (recoverSchemaInfo *ddl.RecoverSchemaInfo, err error) { + txn, err := e.Ctx().Txn(true) + if err != nil { + return nil, err + } + gcSafePoint, err := gcutil.GetGCSafePoint(e.Ctx()) + if err != nil { + return nil, err + } + dom := domain.GetDomain(e.Ctx()) + fn := func(jobs []*model.Job) (bool, error) { + for _, job := range jobs { + // Check GC safe point for getting snapshot infoSchema. + err = gcutil.ValidateSnapshotWithGCSafePoint(job.StartTS, gcSafePoint) + if err != nil { + return false, err + } + if job.Type != model.ActionDropSchema { + continue + } + snapMeta, err := dom.GetSnapshotMeta(job.StartTS) + if err != nil { + return false, err + } + schemaInfo, err := snapMeta.GetDatabase(job.SchemaID) + if err != nil { + return false, err + } + if schemaInfo == nil { + // The dropped DDL maybe execute failed that caused by the parallel DDL execution, + // then can't find the schema from the snapshot info-schema. Should just ignore error here, + // see more in TestParallelDropSchemaAndDropTable. + continue + } + if schemaInfo.Name.L != schemaName.L { + continue + } + tables, err := snapMeta.ListTables(job.SchemaID) + if err != nil { + return false, err + } + recoverTabsInfo := make([]*ddl.RecoverInfo, 0) + for _, tblInfo := range tables { + autoIDs, err := snapMeta.GetAutoIDAccessors(job.SchemaID, tblInfo.ID).Get() + if err != nil { + return false, err + } + recoverTabsInfo = append(recoverTabsInfo, &ddl.RecoverInfo{ + SchemaID: job.SchemaID, + TableInfo: tblInfo, + DropJobID: job.ID, + SnapshotTS: job.StartTS, + AutoIDs: autoIDs, + OldSchemaName: schemaName.L, + OldTableName: tblInfo.Name.L, + }) + } + recoverSchemaInfo = &ddl.RecoverSchemaInfo{DBInfo: schemaInfo, RecoverTabsInfo: recoverTabsInfo, DropJobID: job.ID, SnapshotTS: job.StartTS, OldSchemaName: schemaName} + return true, nil + } + return false, nil + } + err = ddl.IterHistoryDDLJobs(txn, fn) + if err != nil { + if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { + return nil, errors.Errorf("Can't find dropped database '%s' in GC safe point %s", schemaName.O, model.TSConvert2Time(gcSafePoint).String()) + } + return nil, err + } + if recoverSchemaInfo == nil { + return nil, errors.Errorf("Can't find dropped database: %v in DDL history jobs", schemaName.O) + } + return +} + +func (e *DDLExec) executeLockTables(s *ast.LockTablesStmt) error { + if !config.TableLockEnabled() { + e.Ctx().GetSessionVars().StmtCtx.AppendWarning(exeerrors.ErrFuncNotEnabled.GenWithStackByArgs("LOCK TABLES", "enable-table-lock")) + return nil + } + + for _, tb := range s.TableLocks { + if _, ok := e.getLocalTemporaryTable(tb.Table.Schema, tb.Table.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("LOCK TABLES") + } + } + + return domain.GetDomain(e.Ctx()).DDL().LockTables(e.Ctx(), s) +} + +func (e *DDLExec) executeUnlockTables(_ *ast.UnlockTablesStmt) error { + if !config.TableLockEnabled() { + e.Ctx().GetSessionVars().StmtCtx.AppendWarning(exeerrors.ErrFuncNotEnabled.GenWithStackByArgs("UNLOCK TABLES", "enable-table-lock")) + return nil + } + lockedTables := e.Ctx().GetAllTableLocks() + return domain.GetDomain(e.Ctx()).DDL().UnlockTables(e.Ctx(), lockedTables) +} + +func (e *DDLExec) executeCleanupTableLock(s *ast.CleanupTableLockStmt) error { + for _, tb := range s.Tables { + if _, ok := e.getLocalTemporaryTable(tb.Schema, tb.Name); ok { + return dbterror.ErrUnsupportedLocalTempTableDDL.GenWithStackByArgs("ADMIN CLEANUP TABLE LOCK") + } + } + return domain.GetDomain(e.Ctx()).DDL().CleanupTableLock(e.Ctx(), s.Tables) +} + +func (e *DDLExec) executeRepairTable(s *ast.RepairTableStmt) error { + return domain.GetDomain(e.Ctx()).DDL().RepairTable(e.Ctx(), s.CreateStmt) +} + +func (e *DDLExec) executeCreateSequence(s *ast.CreateSequenceStmt) error { + return domain.GetDomain(e.Ctx()).DDL().CreateSequence(e.Ctx(), s) +} + +func (e *DDLExec) executeAlterSequence(s *ast.AlterSequenceStmt) error { + return domain.GetDomain(e.Ctx()).DDL().AlterSequence(e.Ctx(), s) +} + +func (e *DDLExec) executeCreatePlacementPolicy(s *ast.CreatePlacementPolicyStmt) error { + return domain.GetDomain(e.Ctx()).DDL().CreatePlacementPolicy(e.Ctx(), s) +} + +func (e *DDLExec) executeDropPlacementPolicy(s *ast.DropPlacementPolicyStmt) error { + return domain.GetDomain(e.Ctx()).DDL().DropPlacementPolicy(e.Ctx(), s) +} + +func (e *DDLExec) executeAlterPlacementPolicy(s *ast.AlterPlacementPolicyStmt) error { + return domain.GetDomain(e.Ctx()).DDL().AlterPlacementPolicy(e.Ctx(), s) +} + +func (e *DDLExec) executeCreateResourceGroup(s *ast.CreateResourceGroupStmt) error { + if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { + return infoschema.ErrResourceGroupSupportDisabled + } + return domain.GetDomain(e.Ctx()).DDL().AddResourceGroup(e.Ctx(), s) +} + +func (e *DDLExec) executeAlterResourceGroup(s *ast.AlterResourceGroupStmt) error { + if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { + return infoschema.ErrResourceGroupSupportDisabled + } + return domain.GetDomain(e.Ctx()).DDL().AlterResourceGroup(e.Ctx(), s) +} + +func (e *DDLExec) executeDropResourceGroup(s *ast.DropResourceGroupStmt) error { + if !variable.EnableResourceControl.Load() && !e.Ctx().GetSessionVars().InRestrictedSQL { + return infoschema.ErrResourceGroupSupportDisabled + } + return domain.GetDomain(e.Ctx()).DDL().DropResourceGroup(e.Ctx(), s) +} diff --git a/executor/delete.go b/pkg/executor/delete.go similarity index 94% rename from executor/delete.go rename to pkg/executor/delete.go index bd90891509b77..3a70d190bae9e 100644 --- a/executor/delete.go +++ b/pkg/executor/delete.go @@ -18,19 +18,19 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" ) diff --git a/executor/delete_test.go b/pkg/executor/delete_test.go similarity index 97% rename from executor/delete_test.go rename to pkg/executor/delete_test.go index 68edbc805f7cf..c76b3b8e3da79 100644 --- a/executor/delete_test.go +++ b/pkg/executor/delete_test.go @@ -19,8 +19,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" ) func TestDeleteLockKey(t *testing.T) { diff --git a/pkg/executor/distsql.go b/pkg/executor/distsql.go new file mode 100644 index 0000000000000..90c2566880d9f --- /dev/null +++ b/pkg/executor/distsql.go @@ -0,0 +1,1570 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "context" + "fmt" + "runtime/trace" + "slices" + "sort" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + plannerutil "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +var ( + _ exec.Executor = &TableReaderExecutor{} + _ exec.Executor = &IndexReaderExecutor{} + _ exec.Executor = &IndexLookUpExecutor{} +) + +// LookupTableTaskChannelSize represents the channel size of the index double read taskChan. +var LookupTableTaskChannelSize int32 = 50 + +// lookupTableTask is created from a partial result of an index request which +// contains the handles in those index keys. +type lookupTableTask struct { + handles []kv.Handle + rowIdx []int // rowIdx represents the handle index for every row. Only used when keep order. + rows []chunk.Row + idxRows *chunk.Chunk + cursor int + + // after the cop task is built, buildDone will be set to the current instant, for Next wait duration statistic. + buildDoneTime time.Time + doneCh chan error + + // indexOrder map is used to save the original index order for the handles. + // Without this map, the original index order might be lost. + // The handles fetched from index is originally ordered by index, but we need handles to be ordered by itself + // to do table request. + indexOrder *kv.HandleMap + // duplicatedIndexOrder map likes indexOrder. But it's used when checkIndexValue isn't nil and + // the same handle of index has multiple values. + duplicatedIndexOrder *kv.HandleMap + + // partitionTable indicates whether this task belongs to a partition table and which partition table it is. + partitionTable table.PhysicalTable + + // memUsage records the memory usage of this task calculated by table worker. + // memTracker is used to release memUsage after task is done and unused. + // + // The sequence of function calls are: + // 1. calculate task.memUsage. + // 2. task.memTracker = tableWorker.memTracker + // 3. task.memTracker.Consume(task.memUsage) + // 4. task.memTracker.Consume(-task.memUsage) + // + // Step 1~3 are completed in "tableWorker.executeTask". + // Step 4 is completed in "IndexLookUpExecutor.Next". + memUsage int64 + memTracker *memory.Tracker +} + +func (task *lookupTableTask) Len() int { + return len(task.rows) +} + +func (task *lookupTableTask) Less(i, j int) bool { + return task.rowIdx[i] < task.rowIdx[j] +} + +func (task *lookupTableTask) Swap(i, j int) { + task.rowIdx[i], task.rowIdx[j] = task.rowIdx[j], task.rowIdx[i] + task.rows[i], task.rows[j] = task.rows[j], task.rows[i] +} + +// Closeable is a interface for closeable structures. +type Closeable interface { + // Close closes the object. + Close() error +} + +// closeAll closes all objects even if an object returns an error. +// If multiple objects returns error, the first error will be returned. +func closeAll(objs ...Closeable) error { + var err error + for _, obj := range objs { + if obj != nil { + err1 := obj.Close() + if err == nil && err1 != nil { + err = err1 + } + } + } + if err != nil { + return errors.Trace(err) + } + return nil +} + +// rebuildIndexRanges will be called if there's correlated column in access conditions. We will rebuild the range +// by substituting correlated column with the constant. +func rebuildIndexRanges(ctx sessionctx.Context, is *plannercore.PhysicalIndexScan, idxCols []*expression.Column, colLens []int) (ranges []*ranger.Range, err error) { + access := make([]expression.Expression, 0, len(is.AccessCondition)) + for _, cond := range is.AccessCondition { + newCond, err1 := expression.SubstituteCorCol2Constant(cond) + if err1 != nil { + return nil, err1 + } + access = append(access, newCond) + } + // All of access conditions must be used to build ranges, so we don't limit range memory usage. + ranges, _, err = ranger.DetachSimpleCondAndBuildRangeForIndex(ctx, access, idxCols, colLens, 0) + return ranges, err +} + +// IndexReaderExecutor sends dag request and reads index data from kv layer. +type IndexReaderExecutor struct { + exec.BaseExecutor + + // For a partitioned table, the IndexReaderExecutor works on a partition, so + // the type of this table field is actually `table.PhysicalTable`. + table table.Table + index *model.IndexInfo + physicalTableID int64 + ranges []*ranger.Range + partitions []table.PhysicalTable + partRangeMap map[int64][]*ranger.Range // each partition may have different ranges + partitionIDMap map[int64]struct{} // partitionIDs that global index access + + // kvRanges are only used for union scan. + kvRanges []kv.KeyRange + dagPB *tipb.DAGRequest + startTS uint64 + txnScope string + readReplicaScope string + isStaleness bool + netDataSize float64 + // result returns one or more distsql.PartialResult and each PartialResult is returned by one region. + result distsql.SelectResult + // columns are only required by union scan. + columns []*model.ColumnInfo + // outputColumns are only required by union scan. + outputColumns []*expression.Column + + paging bool + + keepOrder bool + desc bool + // byItems only for partition table with orderBy + pushedLimit + byItems []*plannerutil.ByItems + + corColInFilter bool + corColInAccess bool + idxCols []*expression.Column + colLens []int + plans []plannercore.PhysicalPlan + + memTracker *memory.Tracker + + selectResultHook // for testing + + // If dummy flag is set, this is not a real IndexReader, it just provides the KV ranges for UnionScan. + // Used by the temporary table, cached table. + dummy bool +} + +// Table implements the dataSourceExecutor interface. +func (e *IndexReaderExecutor) Table() table.Table { + return e.table +} + +func (e *IndexReaderExecutor) setDummy() { + e.dummy = true +} + +// Close clears all resources hold by current object. +func (e *IndexReaderExecutor) Close() (err error) { + if e.result != nil { + err = e.result.Close() + } + e.result = nil + e.kvRanges = e.kvRanges[:0] + if e.dummy { + return nil + } + return err +} + +// Next implements the Executor Next interface. +func (e *IndexReaderExecutor) Next(ctx context.Context, req *chunk.Chunk) error { + if e.dummy { + req.Reset() + return nil + } + + return e.result.Next(ctx, req) +} + +// TODO: cleanup this method. +func (e *IndexReaderExecutor) buildKeyRanges(sc *stmtctx.StatementContext, ranges []*ranger.Range, physicalID int64) ([]kv.KeyRange, error) { + var ( + rRanges *kv.KeyRanges + err error + ) + if e.index.ID == -1 { + rRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, ranges) + } else { + rRanges, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, ranges) + } + return rRanges.FirstPartitionRange(), err +} + +// Open implements the Executor Open interface. +func (e *IndexReaderExecutor) Open(ctx context.Context) error { + var err error + if e.corColInAccess { + e.ranges, err = rebuildIndexRanges(e.Ctx(), e.plans[0].(*plannercore.PhysicalIndexScan), e.idxCols, e.colLens) + if err != nil { + return err + } + } + + sc := e.Ctx().GetSessionVars().StmtCtx + var kvRanges []kv.KeyRange + if len(e.partitions) > 0 { + for _, p := range e.partitions { + partRange := e.ranges + if pRange, ok := e.partRangeMap[p.GetPhysicalID()]; ok { + partRange = pRange + } + kvRange, err := e.buildKeyRanges(sc, partRange, p.GetPhysicalID()) + if err != nil { + return err + } + kvRanges = append(kvRanges, kvRange...) + } + } else { + kvRanges, err = e.buildKeyRanges(sc, e.ranges, e.physicalTableID) + } + if err != nil { + return err + } + + return e.open(ctx, kvRanges) +} + +func (e *IndexReaderExecutor) buildKVReq(r []kv.KeyRange) (*kv.Request, error) { + var builder distsql.RequestBuilder + builder.SetKeyRanges(r). + SetDAGRequest(e.dagPB). + SetStartTS(e.startTS). + SetDesc(e.desc). + SetKeepOrder(e.keepOrder). + SetTxnScope(e.txnScope). + SetReadReplicaScope(e.readReplicaScope). + SetIsStaleness(e.isStaleness). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetMemTracker(e.memTracker). + SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &builder.Request, e.netDataSize)). + SetConnID(e.Ctx().GetSessionVars().ConnectionID) + kvReq, err := builder.Build() + return kvReq, err +} + +func (e *IndexReaderExecutor) open(ctx context.Context, kvRanges []kv.KeyRange) error { + var err error + if e.corColInFilter { + e.dagPB.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.plans) + if err != nil { + return err + } + } + + if e.index.Global { + idxScanExec := e.dagPB.Executors[0] + args := make([]expression.Expression, 0, len(e.partitionIDMap)) + column := &expression.Column{ + UniqueID: model.ExtraPidColID, + RetType: types.NewFieldType(mysql.TypeLonglong), + Index: len(idxScanExec.IdxScan.Columns) - 1, + OrigName: model.ExtraPartitionIdName.L, + } + args = append(args, column) + for pid := range e.partitionIDMap { + args = append(args, expression.NewInt64Const(pid)) + } + + inCondition, err := expression.NewFunction(e.Ctx(), ast.In, types.NewFieldType(mysql.TypeLonglong), args...) + if err != nil { + return err + } + pbConditions, err := expression.ExpressionsToPBList(e.Ctx().GetSessionVars().StmtCtx, []expression.Expression{inCondition}, e.Ctx().GetClient()) + if err != nil { + return err + } + if len(e.dagPB.Executors) > 1 && e.dagPB.Executors[1].Tp == tipb.ExecType_TypeSelection { + e.dagPB.Executors[1].Selection.Conditions = append(e.dagPB.Executors[1].Selection.Conditions, pbConditions...) + } else { + selExec := &tipb.Selection{ + Conditions: pbConditions, + } + executors := make([]*tipb.Executor, 0, len(e.dagPB.Executors)+1) + executors = append(executors, e.dagPB.Executors[0]) + executors = append(executors, &tipb.Executor{Tp: tipb.ExecType_TypeSelection, Selection: selExec}) + executors = append(executors, e.dagPB.Executors[1:]...) + e.dagPB.Executors = executors + } + } + + if e.RuntimeStats() != nil { + collExec := true + e.dagPB.CollectExecutionSummaries = &collExec + } + e.kvRanges = kvRanges + // Treat temporary table as dummy table, avoid sending distsql request to TiKV. + // In a test case IndexReaderExecutor is mocked and e.table is nil. + // Avoid sending distsql request to TIKV. + if e.dummy { + return nil + } + + if e.memTracker != nil { + e.memTracker.Reset() + } else { + e.memTracker = memory.NewTracker(e.ID(), -1) + } + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + slices.SortFunc(kvRanges, func(i, j kv.KeyRange) int { + return bytes.Compare(i.StartKey, j.StartKey) + }) + // use sortedSelectResults only when byItems pushed down and partition numbers > 1 + if e.byItems == nil || len(e.partitions) <= 1 { + kvReq, err := e.buildKVReq(kvRanges) + if err != nil { + return err + } + e.result, err = e.SelectResult(ctx, e.Ctx(), kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) + if err != nil { + return err + } + } else { + kvReqs := make([]*kv.Request, 0, len(kvRanges)) + for _, kvRange := range kvRanges { + kvReq, err := e.buildKVReq([]kv.KeyRange{kvRange}) + if err != nil { + return err + } + kvReqs = append(kvReqs, kvReq) + } + var results []distsql.SelectResult + for _, kvReq := range kvReqs { + result, err := e.SelectResult(ctx, e.Ctx(), kvReq, exec.RetTypes(e), getPhysicalPlanIDs(e.plans), e.ID()) + if err != nil { + return err + } + results = append(results, result) + } + e.result = distsql.NewSortedSelectResults(results, e.Schema(), e.byItems, e.memTracker) + } + return nil +} + +// IndexLookUpExecutor implements double read for index scan. +type IndexLookUpExecutor struct { + exec.BaseExecutor + + table table.Table + index *model.IndexInfo + ranges []*ranger.Range + dagPB *tipb.DAGRequest + startTS uint64 + // handleIdx is the index of handle, which is only used for case of keeping order. + handleIdx []int + handleCols []*expression.Column + primaryKeyIndex *model.IndexInfo + tableRequest *tipb.DAGRequest + // columns are only required by union scan. + columns []*model.ColumnInfo + *dataReaderBuilder + idxNetDataSize float64 + avgRowSize float64 + + // fields about accessing partition tables + partitionTableMode bool // if this executor is accessing a partition table + prunedPartitions []table.PhysicalTable // partition tables need to access + partitionIDMap map[int64]struct{} // partitionIDs that global index access + partitionRangeMap map[int64][]*ranger.Range + partitionKVRanges [][]kv.KeyRange // kvRanges of each prunedPartitions + + // All fields above are immutable. + + idxWorkerWg sync.WaitGroup + tblWorkerWg sync.WaitGroup + finished chan struct{} + + resultCh chan *lookupTableTask + resultCurr *lookupTableTask + + // memTracker is used to track the memory usage of this executor. + memTracker *memory.Tracker + + // checkIndexValue is used to check the consistency of the index data. + *checkIndexValue + + kvRanges []kv.KeyRange + workerStarted bool + + byItems []*plannerutil.ByItems + keepOrder bool + desc bool + + indexPaging bool + + corColInIdxSide bool + corColInTblSide bool + corColInAccess bool + idxPlans []plannercore.PhysicalPlan + tblPlans []plannercore.PhysicalPlan + idxCols []*expression.Column + colLens []int + // PushedLimit is used to skip the preceding and tailing handles when Limit is sunk into IndexLookUpReader. + PushedLimit *plannercore.PushedDownLimit + + stats *IndexLookUpRunTimeStats + + // cancelFunc is called when close the executor + cancelFunc context.CancelFunc + + // If dummy flag is set, this is not a real IndexLookUpReader, it just provides the KV ranges for UnionScan. + // Used by the temporary table, cached table. + dummy bool +} + +type getHandleType int8 + +const ( + getHandleFromIndex getHandleType = iota + getHandleFromTable +) + +// nolint:structcheck +type checkIndexValue struct { + idxColTps []*types.FieldType + idxTblCols []*table.Column +} + +// Table implements the dataSourceExecutor interface. +func (e *IndexLookUpExecutor) Table() table.Table { + return e.table +} + +func (e *IndexLookUpExecutor) setDummy() { + e.dummy = true +} + +// Open implements the Executor Open interface. +func (e *IndexLookUpExecutor) Open(ctx context.Context) error { + var err error + if e.corColInAccess { + e.ranges, err = rebuildIndexRanges(e.Ctx(), e.idxPlans[0].(*plannercore.PhysicalIndexScan), e.idxCols, e.colLens) + if err != nil { + return err + } + } + err = e.buildTableKeyRanges() + if err != nil { + return err + } + + // Treat temporary table as dummy table, avoid sending distsql request to TiKV. + if e.dummy { + return nil + } + + return e.open(ctx) +} + +func (e *IndexLookUpExecutor) buildTableKeyRanges() (err error) { + sc := e.Ctx().GetSessionVars().StmtCtx + if e.partitionTableMode { + e.partitionKVRanges = make([][]kv.KeyRange, 0, len(e.prunedPartitions)) + for _, p := range e.prunedPartitions { + // TODO: prune and adjust e.ranges for each partition again, since not all e.ranges are suitable for all e.prunedPartitions. + // For example, a table partitioned by range(a), and p0=(1, 10), p1=(11, 20), for the condition "(a>1 and a<10) or (a>11 and a<20)", + // the first range is only suitable to p0 and the second is to p1, but now we'll also build kvRange for range0+p1 and range1+p0. + physicalID := p.GetPhysicalID() + ranges := e.ranges + if e.partitionRangeMap != nil && e.partitionRangeMap[physicalID] != nil { + ranges = e.partitionRangeMap[physicalID] + } + var kvRange *kv.KeyRanges + if e.index.ID == -1 { + kvRange, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, ranges) + } else { + kvRange, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, ranges) + } + if err != nil { + return err + } + e.partitionKVRanges = append(e.partitionKVRanges, kvRange.FirstPartitionRange()) + } + } else { + physicalID := getPhysicalTableID(e.table) + var kvRanges *kv.KeyRanges + if e.index.ID == -1 { + kvRanges, err = distsql.CommonHandleRangesToKVRanges(sc, []int64{physicalID}, e.ranges) + } else { + kvRanges, err = distsql.IndexRangesToKVRanges(sc, physicalID, e.index.ID, e.ranges) + } + e.kvRanges = kvRanges.FirstPartitionRange() + } + return err +} + +func (e *IndexLookUpExecutor) open(_ context.Context) error { + // We have to initialize "memTracker" and other execution resources in here + // instead of in function "Open", because this "IndexLookUpExecutor" may be + // constructed by a "IndexLookUpJoin" and "Open" will not be called in that + // situation. + e.initRuntimeStats() + e.memTracker = memory.NewTracker(e.ID(), -1) + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + + e.finished = make(chan struct{}) + e.resultCh = make(chan *lookupTableTask, atomic.LoadInt32(&LookupTableTaskChannelSize)) + + var err error + if e.corColInIdxSide { + e.dagPB.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.idxPlans) + if err != nil { + return err + } + } + + if e.corColInTblSide { + e.tableRequest.Executors, err = builder.ConstructListBasedDistExec(e.Ctx(), e.tblPlans) + if err != nil { + return err + } + } + return nil +} + +func (e *IndexLookUpExecutor) startWorkers(ctx context.Context, initBatchSize int) error { + // indexWorker will write to workCh and tableWorker will read from workCh, + // so fetching index and getting table data can run concurrently. + ctx, cancel := context.WithCancel(ctx) + e.cancelFunc = cancel + workCh := make(chan *lookupTableTask, 1) + if err := e.startIndexWorker(ctx, workCh, initBatchSize); err != nil { + return err + } + e.startTableWorker(ctx, workCh) + e.workerStarted = true + return nil +} + +func (e *IndexLookUpExecutor) needPartitionHandle(tp getHandleType) (bool, error) { + var col *expression.Column + var needPartitionHandle, hasExtraCol bool + if tp == getHandleFromIndex { + cols := e.idxPlans[0].Schema().Columns + outputOffsets := e.dagPB.OutputOffsets + col = cols[outputOffsets[len(outputOffsets)-1]] + // For indexScan, need partitionHandle when global index or keepOrder with partitionTable + needPartitionHandle = e.index.Global || e.partitionTableMode && e.keepOrder + hasExtraCol = col.ID == model.ExtraPhysTblID || col.ID == model.ExtraPidColID + } else { + cols := e.tblPlans[0].Schema().Columns + outputOffsets := e.tableRequest.OutputOffsets + col = cols[outputOffsets[len(outputOffsets)-1]] + + // For TableScan, need partitionHandle in `indexOrder` when e.keepOrder == true + needPartitionHandle = (e.index.Global || e.partitionTableMode) && e.keepOrder + // no ExtraPidColID here, because TableScan shouldn't contain them. + hasExtraCol = col.ID == model.ExtraPhysTblID + } + + // TODO: fix global index related bugs later + // There will be two needPartitionHandle != hasExtraCol situations. + // Only `needPartitionHandle` == true and `hasExtraCol` == false are not allowed. + // `ExtraPhysTblID` will be used in `SelectLock` when `needPartitionHandle` == false and `hasExtraCol` == true. + if needPartitionHandle && !hasExtraCol && !e.index.Global { + return needPartitionHandle, errors.Errorf("Internal error, needPartitionHandle != ret, tp(%d)", tp) + } + return needPartitionHandle, nil +} + +func (e *IndexLookUpExecutor) isCommonHandle() bool { + return !(len(e.handleCols) == 1 && e.handleCols[0].ID == model.ExtraHandleID) && e.table.Meta() != nil && e.table.Meta().IsCommonHandle +} + +func (e *IndexLookUpExecutor) getRetTpsForIndexReader() []*types.FieldType { + if e.checkIndexValue != nil { + return e.idxColTps + } + var tps []*types.FieldType + if len(e.byItems) != 0 { + for _, item := range e.byItems { + tps = append(tps, item.Expr.GetType()) + } + } + if e.isCommonHandle() { + for _, handleCol := range e.handleCols { + tps = append(tps, handleCol.RetType) + } + } else { + tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) + } + if ok, _ := e.needPartitionHandle(getHandleFromIndex); ok { + tps = append(tps, types.NewFieldType(mysql.TypeLonglong)) + } + return tps +} + +// startIndexWorker launch a background goroutine to fetch handles, send the results to workCh. +func (e *IndexLookUpExecutor) startIndexWorker(ctx context.Context, workCh chan<- *lookupTableTask, initBatchSize int) error { + if e.RuntimeStats() != nil { + collExec := true + e.dagPB.CollectExecutionSummaries = &collExec + } + tracker := memory.NewTracker(memory.LabelForIndexWorker, -1) + tracker.AttachTo(e.memTracker) + + kvRanges := [][]kv.KeyRange{e.kvRanges} + if e.partitionTableMode { + kvRanges = e.partitionKVRanges + } + // When len(kvrange) = 1, no sorting is required, + // so remove byItems and non-necessary output colums + if len(kvRanges) == 1 { + e.dagPB.OutputOffsets = e.dagPB.OutputOffsets[len(e.byItems):] + e.byItems = nil + } + tps := e.getRetTpsForIndexReader() + idxID := e.getIndexPlanRootID() + e.idxWorkerWg.Add(1) + go func() { + defer trace.StartRegion(ctx, "IndexLookUpIndexWorker").End() + worker := &indexWorker{ + idxLookup: e, + workCh: workCh, + finished: e.finished, + resultCh: e.resultCh, + keepOrder: e.keepOrder, + checkIndexValue: e.checkIndexValue, + maxBatchSize: e.Ctx().GetSessionVars().IndexLookupSize, + maxChunkSize: e.MaxChunkSize(), + PushedLimit: e.PushedLimit, + } + var builder distsql.RequestBuilder + builder.SetDAGRequest(e.dagPB). + SetStartTS(e.startTS). + SetDesc(e.desc). + SetKeepOrder(e.keepOrder). + SetPaging(e.indexPaging). + SetTxnScope(e.txnScope). + SetReadReplicaScope(e.readReplicaScope). + SetIsStaleness(e.isStaleness). + SetFromSessionVars(e.Ctx().GetSessionVars()). + SetFromInfoSchema(e.Ctx().GetInfoSchema()). + SetClosestReplicaReadAdjuster(newClosestReadAdjuster(e.Ctx(), &builder.Request, e.idxNetDataSize/float64(len(kvRanges)))). + SetMemTracker(tracker). + SetConnID(e.Ctx().GetSessionVars().ConnectionID) + + results := make([]distsql.SelectResult, 0, len(kvRanges)) + for _, kvRange := range kvRanges { + // check if executor is closed + finished := false + select { + case <-e.finished: + finished = true + default: + } + if finished { + break + } + + // init kvReq, result and worker for this partition + // The key ranges should be ordered. + slices.SortFunc(kvRange, func(i, j kv.KeyRange) int { + return bytes.Compare(i.StartKey, j.StartKey) + }) + kvReq, err := builder.SetKeyRanges(kvRange).Build() + if err != nil { + worker.syncErr(err) + break + } + result, err := distsql.SelectWithRuntimeStats(ctx, e.Ctx(), kvReq, tps, getPhysicalPlanIDs(e.idxPlans), idxID) + if err != nil { + worker.syncErr(err) + break + } + results = append(results, result) + } + worker.batchSize = mathutil.Min(initBatchSize, worker.maxBatchSize) + if len(results) > 1 && len(e.byItems) != 0 { + // e.Schema() not the output schema for indexReader, and we put byItems related column at first in `buildIndexReq`, so use nil here. + ssr := distsql.NewSortedSelectResults(results, nil, e.byItems, e.memTracker) + results = []distsql.SelectResult{ssr} + } + ctx1, cancel := context.WithCancel(ctx) + // this error is synced in fetchHandles(), don't sync it again + _ = worker.fetchHandles(ctx1, results) + cancel() + for _, result := range results { + if err := result.Close(); err != nil { + logutil.Logger(ctx).Error("close Select result failed", zap.Error(err)) + } + } + close(workCh) + close(e.resultCh) + e.idxWorkerWg.Done() + }() + return nil +} + +// startTableWorker launchs some background goroutines which pick tasks from workCh and execute the task. +func (e *IndexLookUpExecutor) startTableWorker(ctx context.Context, workCh <-chan *lookupTableTask) { + lookupConcurrencyLimit := e.Ctx().GetSessionVars().IndexLookupConcurrency() + e.tblWorkerWg.Add(lookupConcurrencyLimit) + for i := 0; i < lookupConcurrencyLimit; i++ { + workerID := i + worker := &tableWorker{ + idxLookup: e, + workCh: workCh, + finished: e.finished, + keepOrder: e.keepOrder, + handleIdx: e.handleIdx, + checkIndexValue: e.checkIndexValue, + memTracker: memory.NewTracker(workerID, -1), + } + worker.memTracker.AttachTo(e.memTracker) + ctx1, cancel := context.WithCancel(ctx) + go func() { + defer trace.StartRegion(ctx1, "IndexLookUpTableWorker").End() + worker.pickAndExecTask(ctx1) + cancel() + e.tblWorkerWg.Done() + }() + } +} + +func (e *IndexLookUpExecutor) buildTableReader(ctx context.Context, task *lookupTableTask) (exec.Executor, error) { + table := e.table + if e.partitionTableMode && task.partitionTable != nil { + table = task.partitionTable + } + tableReaderExec := &TableReaderExecutor{ + BaseExecutor: exec.NewBaseExecutor(e.Ctx(), e.Schema(), e.getTableRootPlanID()), + table: table, + dagPB: e.tableRequest, + startTS: e.startTS, + txnScope: e.txnScope, + readReplicaScope: e.readReplicaScope, + isStaleness: e.isStaleness, + columns: e.columns, + corColInFilter: e.corColInTblSide, + plans: e.tblPlans, + netDataSize: e.avgRowSize * float64(len(task.handles)), + byItems: e.byItems, + } + tableReaderExec.buildVirtualColumnInfo() + tableReader, err := e.dataReaderBuilder.buildTableReaderFromHandles(ctx, tableReaderExec, task.handles, true) + if err != nil { + logutil.Logger(ctx).Error("build table reader from handles failed", zap.Error(err)) + return nil, err + } + return tableReader, nil +} + +// Close implements Exec Close interface. +func (e *IndexLookUpExecutor) Close() error { + if e.stats != nil { + defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) + } + e.kvRanges = e.kvRanges[:0] + if e.dummy { + return nil + } + + if !e.workerStarted || e.finished == nil { + return nil + } + + if e.cancelFunc != nil { + e.cancelFunc() + e.cancelFunc = nil + } + close(e.finished) + // Drain the resultCh and discard the result, in case that Next() doesn't fully + // consume the data, background worker still writing to resultCh and block forever. + channel.Clear(e.resultCh) + e.idxWorkerWg.Wait() + e.tblWorkerWg.Wait() + e.finished = nil + e.workerStarted = false + e.memTracker = nil + e.resultCurr = nil + return nil +} + +// Next implements Exec Next interface. +func (e *IndexLookUpExecutor) Next(ctx context.Context, req *chunk.Chunk) error { + if e.dummy { + req.Reset() + return nil + } + + if !e.workerStarted { + if err := e.startWorkers(ctx, req.RequiredRows()); err != nil { + return err + } + } + req.Reset() + for { + resultTask, err := e.getResultTask() + if err != nil { + return err + } + if resultTask == nil { + return nil + } + if resultTask.cursor < len(resultTask.rows) { + numToAppend := mathutil.Min(len(resultTask.rows)-resultTask.cursor, req.RequiredRows()-req.NumRows()) + req.AppendRows(resultTask.rows[resultTask.cursor : resultTask.cursor+numToAppend]) + resultTask.cursor += numToAppend + if req.IsFull() { + return nil + } + } + } +} + +func (e *IndexLookUpExecutor) getResultTask() (*lookupTableTask, error) { + if e.resultCurr != nil && e.resultCurr.cursor < len(e.resultCurr.rows) { + return e.resultCurr, nil + } + var ( + enableStats = e.stats != nil + start time.Time + indexFetchedInstant time.Time + ) + if enableStats { + start = time.Now() + } + task, ok := <-e.resultCh + if !ok { + return nil, nil + } + if enableStats { + indexFetchedInstant = time.Now() + } + if err := <-task.doneCh; err != nil { + return nil, err + } + if enableStats { + e.stats.NextWaitIndexScan += indexFetchedInstant.Sub(start) + if task.buildDoneTime.After(indexFetchedInstant) { + e.stats.NextWaitTableLookUpBuild += task.buildDoneTime.Sub(indexFetchedInstant) + indexFetchedInstant = task.buildDoneTime + } + e.stats.NextWaitTableLookUpResp += time.Since(indexFetchedInstant) + } + + // Release the memory usage of last task before we handle a new task. + if e.resultCurr != nil { + e.resultCurr.memTracker.Consume(-e.resultCurr.memUsage) + } + e.resultCurr = task + return e.resultCurr, nil +} + +func (e *IndexLookUpExecutor) initRuntimeStats() { + if e.RuntimeStats() != nil { + e.stats = &IndexLookUpRunTimeStats{ + indexScanBasicStats: &execdetails.BasicRuntimeStats{}, + Concurrency: e.Ctx().GetSessionVars().IndexLookupConcurrency(), + } + } +} + +func (e *IndexLookUpExecutor) getIndexPlanRootID() int { + if len(e.idxPlans) > 0 { + return e.idxPlans[len(e.idxPlans)-1].ID() + } + return e.ID() +} + +func (e *IndexLookUpExecutor) getTableRootPlanID() int { + if len(e.tblPlans) > 0 { + return e.tblPlans[len(e.tblPlans)-1].ID() + } + return e.ID() +} + +// indexWorker is used by IndexLookUpExecutor to maintain index lookup background goroutines. +type indexWorker struct { + idxLookup *IndexLookUpExecutor + workCh chan<- *lookupTableTask + finished <-chan struct{} + resultCh chan<- *lookupTableTask + keepOrder bool + + // batchSize is for lightweight startup. It will be increased exponentially until reaches the max batch size value. + batchSize int + maxBatchSize int + maxChunkSize int + + // checkIndexValue is used to check the consistency of the index data. + *checkIndexValue + // PushedLimit is used to skip the preceding and tailing handles when Limit is sunk into IndexLookUpReader. + PushedLimit *plannercore.PushedDownLimit + // scannedKeys indicates how many keys be scanned + scannedKeys uint64 +} + +func (w *indexWorker) syncErr(err error) { + doneCh := make(chan error, 1) + doneCh <- err + w.resultCh <- &lookupTableTask{ + doneCh: doneCh, + } +} + +// fetchHandles fetches a batch of handles from index data and builds the index lookup tasks. +// The tasks are sent to workCh to be further processed by tableWorker, and sent to e.resultCh +// at the same time to keep data ordered. +func (w *indexWorker) fetchHandles(ctx context.Context, results []distsql.SelectResult) (err error) { + defer func() { + if r := recover(); r != nil { + logutil.Logger(ctx).Error("indexWorker in IndexLookupExecutor panicked", zap.Any("recover", r), zap.Stack("stack")) + err4Panic := errors.Errorf("%v", r) + w.syncErr(err4Panic) + if err != nil { + err = errors.Trace(err4Panic) + } + } + }() + chk := w.idxLookup.Ctx().GetSessionVars().GetNewChunkWithCapacity(w.idxLookup.getRetTpsForIndexReader(), w.idxLookup.MaxChunkSize(), w.idxLookup.MaxChunkSize(), w.idxLookup.AllocPool) + idxID := w.idxLookup.getIndexPlanRootID() + if w.idxLookup.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl != nil { + if idxID != w.idxLookup.ID() && w.idxLookup.stats != nil { + w.idxLookup.stats.indexScanBasicStats = w.idxLookup.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.GetBasicRuntimeStats(idxID) + } + } + for i := 0; i < len(results); { + result := results[i] + if w.PushedLimit != nil && w.scannedKeys >= w.PushedLimit.Count+w.PushedLimit.Offset { + break + } + startTime := time.Now() + handles, retChunk, err := w.extractTaskHandles(ctx, chk, result) + finishFetch := time.Now() + if err != nil { + w.syncErr(err) + return err + } + if len(handles) == 0 { + i++ + continue + } + task := w.buildTableTask(handles, retChunk) + finishBuild := time.Now() + if w.idxLookup.partitionTableMode { + task.partitionTable = w.idxLookup.prunedPartitions[i] + } + select { + case <-ctx.Done(): + return nil + case <-w.finished: + return nil + case w.workCh <- task: + w.resultCh <- task + } + if w.idxLookup.stats != nil { + atomic.AddInt64(&w.idxLookup.stats.FetchHandle, int64(finishFetch.Sub(startTime))) + atomic.AddInt64(&w.idxLookup.stats.TaskWait, int64(time.Since(finishBuild))) + atomic.AddInt64(&w.idxLookup.stats.FetchHandleTotal, int64(time.Since(startTime))) + } + } + return nil +} + +func (w *indexWorker) extractTaskHandles(ctx context.Context, chk *chunk.Chunk, idxResult distsql.SelectResult) ( + handles []kv.Handle, retChk *chunk.Chunk, err error) { + numColsWithoutPid := chk.NumCols() + ok, err := w.idxLookup.needPartitionHandle(getHandleFromIndex) + if err != nil { + return nil, nil, err + } + if ok { + numColsWithoutPid = numColsWithoutPid - 1 + } + handleOffset := make([]int, 0, len(w.idxLookup.handleCols)) + for i := range w.idxLookup.handleCols { + handleOffset = append(handleOffset, numColsWithoutPid-len(w.idxLookup.handleCols)+i) + } + if len(handleOffset) == 0 { + handleOffset = []int{numColsWithoutPid - 1} + } + handles = make([]kv.Handle, 0, w.batchSize) + // PushedLimit would always be nil for CheckIndex or CheckTable, we add this check just for insurance. + checkLimit := (w.PushedLimit != nil) && (w.checkIndexValue == nil) + for len(handles) < w.batchSize { + requiredRows := w.batchSize - len(handles) + if checkLimit { + if w.PushedLimit.Offset+w.PushedLimit.Count <= w.scannedKeys { + return handles, nil, nil + } + leftCnt := w.PushedLimit.Offset + w.PushedLimit.Count - w.scannedKeys + if uint64(requiredRows) > leftCnt { + requiredRows = int(leftCnt) + } + } + chk.SetRequiredRows(requiredRows, w.maxChunkSize) + startTime := time.Now() + err = errors.Trace(idxResult.Next(ctx, chk)) + if err != nil { + return handles, nil, err + } + if w.idxLookup.stats != nil { + w.idxLookup.stats.indexScanBasicStats.Record(time.Since(startTime), chk.NumRows()) + } + if chk.NumRows() == 0 { + return handles, retChk, nil + } + for i := 0; i < chk.NumRows(); i++ { + w.scannedKeys++ + if checkLimit { + if w.scannedKeys <= w.PushedLimit.Offset { + continue + } + if w.scannedKeys > (w.PushedLimit.Offset + w.PushedLimit.Count) { + // Skip the handles after Offset+Count. + return handles, nil, nil + } + } + h, err := w.idxLookup.getHandle(chk.GetRow(i), handleOffset, w.idxLookup.isCommonHandle(), getHandleFromIndex) + if err != nil { + return handles, retChk, err + } + if ph, ok := h.(kv.PartitionHandle); ok { + if _, exist := w.idxLookup.partitionIDMap[ph.PartitionID]; !exist { + continue + } + } + handles = append(handles, h) + } + if w.checkIndexValue != nil { + if retChk == nil { + retChk = chunk.NewChunkWithCapacity(w.idxColTps, w.batchSize) + } + retChk.Append(chk, 0, chk.NumRows()) + } + } + w.batchSize *= 2 + if w.batchSize > w.maxBatchSize { + w.batchSize = w.maxBatchSize + } + return handles, retChk, nil +} + +func (w *indexWorker) buildTableTask(handles []kv.Handle, retChk *chunk.Chunk) *lookupTableTask { + var indexOrder *kv.HandleMap + var duplicatedIndexOrder *kv.HandleMap + if w.keepOrder { + // Save the index order. + indexOrder = kv.NewHandleMap() + for i, h := range handles { + indexOrder.Set(h, i) + } + } + + if w.checkIndexValue != nil { + // Save the index order. + indexOrder = kv.NewHandleMap() + duplicatedIndexOrder = kv.NewHandleMap() + for i, h := range handles { + if _, ok := indexOrder.Get(h); ok { + duplicatedIndexOrder.Set(h, i) + } else { + indexOrder.Set(h, i) + } + } + } + + task := &lookupTableTask{ + handles: handles, + indexOrder: indexOrder, + duplicatedIndexOrder: duplicatedIndexOrder, + idxRows: retChk, + } + + task.doneCh = make(chan error, 1) + return task +} + +// tableWorker is used by IndexLookUpExecutor to maintain table lookup background goroutines. +type tableWorker struct { + idxLookup *IndexLookUpExecutor + workCh <-chan *lookupTableTask + finished <-chan struct{} + keepOrder bool + handleIdx []int + + // memTracker is used to track the memory usage of this executor. + memTracker *memory.Tracker + + // checkIndexValue is used to check the consistency of the index data. + *checkIndexValue +} + +// pickAndExecTask picks tasks from workCh, and execute them. +func (w *tableWorker) pickAndExecTask(ctx context.Context) { + var task *lookupTableTask + var ok bool + defer func() { + if r := recover(); r != nil { + logutil.Logger(ctx).Error("tableWorker in IndexLookUpExecutor panicked", zap.Any("recover", r), zap.Stack("stack")) + task.doneCh <- errors.Errorf("%v", r) + } + }() + for { + // Don't check ctx.Done() on purpose. If background worker get the signal and all + // exit immediately, session's goroutine doesn't know this and still calling Next(), + // it may block reading task.doneCh forever. + select { + case task, ok = <-w.workCh: + if !ok { + return + } + case <-w.finished: + return + } + startTime := time.Now() + err := w.executeTask(ctx, task) + if w.idxLookup.stats != nil { + atomic.AddInt64(&w.idxLookup.stats.TableRowScan, int64(time.Since(startTime))) + atomic.AddInt64(&w.idxLookup.stats.TableTaskNum, 1) + } + task.doneCh <- err + } +} + +func (e *IndexLookUpExecutor) getHandle(row chunk.Row, handleIdx []int, + isCommonHandle bool, tp getHandleType) (handle kv.Handle, err error) { + if isCommonHandle { + var handleEncoded []byte + var datums []types.Datum + for i, idx := range handleIdx { + // If the new collation is enabled and the handle contains non-binary string, + // the handle in the index is encoded as "sortKey". So we cannot restore its + // original value(the primary key) here. + // We use a trick to avoid encoding the "sortKey" again by changing the charset + // collation to `binary`. + rtp := e.handleCols[i].RetType + if collate.NewCollationEnabled() && e.table.Meta().CommonHandleVersion == 0 && rtp.EvalType() == types.ETString && + !mysql.HasBinaryFlag(rtp.GetFlag()) && tp == getHandleFromIndex { + rtp = rtp.Clone() + rtp.SetCollate(charset.CollationBin) + datums = append(datums, row.GetDatum(idx, rtp)) + continue + } + datums = append(datums, row.GetDatum(idx, e.handleCols[i].RetType)) + } + tablecodec.TruncateIndexValues(e.table.Meta(), e.primaryKeyIndex, datums) + handleEncoded, err = codec.EncodeKey(e.Ctx().GetSessionVars().StmtCtx, nil, datums...) + if err != nil { + return nil, err + } + handle, err = kv.NewCommonHandle(handleEncoded) + if err != nil { + return nil, err + } + } else { + if len(handleIdx) == 0 { + handle = kv.IntHandle(row.GetInt64(0)) + } else { + handle = kv.IntHandle(row.GetInt64(handleIdx[0])) + } + } + ok, err := e.needPartitionHandle(tp) + if err != nil { + return nil, err + } + if ok { + pid := row.GetInt64(row.Len() - 1) + handle = kv.NewPartitionHandle(pid, handle) + } + return +} + +// IndexLookUpRunTimeStats record the indexlookup runtime stat +type IndexLookUpRunTimeStats struct { + // indexScanBasicStats uses to record basic runtime stats for index scan. + indexScanBasicStats *execdetails.BasicRuntimeStats + FetchHandleTotal int64 + FetchHandle int64 + TaskWait int64 + TableRowScan int64 + TableTaskNum int64 + Concurrency int + // Record the `Next` call affected wait duration details. + NextWaitIndexScan time.Duration + NextWaitTableLookUpBuild time.Duration + NextWaitTableLookUpResp time.Duration +} + +func (e *IndexLookUpRunTimeStats) String() string { + var buf bytes.Buffer + fetchHandle := atomic.LoadInt64(&e.FetchHandleTotal) + indexScan := atomic.LoadInt64(&e.FetchHandle) + taskWait := atomic.LoadInt64(&e.TaskWait) + tableScan := atomic.LoadInt64(&e.TableRowScan) + tableTaskNum := atomic.LoadInt64(&e.TableTaskNum) + concurrency := e.Concurrency + if indexScan != 0 { + buf.WriteString(fmt.Sprintf("index_task: {total_time: %s, fetch_handle: %s, build: %s, wait: %s}", + execdetails.FormatDuration(time.Duration(fetchHandle)), + execdetails.FormatDuration(time.Duration(indexScan)), + execdetails.FormatDuration(time.Duration(fetchHandle-indexScan-taskWait)), + execdetails.FormatDuration(time.Duration(taskWait)))) + } + if tableScan != 0 { + if buf.Len() > 0 { + buf.WriteByte(',') + } + buf.WriteString(fmt.Sprintf(" table_task: {total_time: %v, num: %d, concurrency: %d}", execdetails.FormatDuration(time.Duration(tableScan)), tableTaskNum, concurrency)) + } + if e.NextWaitIndexScan > 0 || e.NextWaitTableLookUpBuild > 0 || e.NextWaitTableLookUpResp > 0 { + if buf.Len() > 0 { + buf.WriteByte(',') + fmt.Fprintf(&buf, " next: {wait_index: %s, wait_table_lookup_build: %s, wait_table_lookup_resp: %s}", + execdetails.FormatDuration(e.NextWaitIndexScan), + execdetails.FormatDuration(e.NextWaitTableLookUpBuild), + execdetails.FormatDuration(e.NextWaitTableLookUpResp)) + } + } + return buf.String() +} + +// Clone implements the RuntimeStats interface. +func (e *IndexLookUpRunTimeStats) Clone() execdetails.RuntimeStats { + newRs := *e + return &newRs +} + +// Merge implements the RuntimeStats interface. +func (e *IndexLookUpRunTimeStats) Merge(other execdetails.RuntimeStats) { + tmp, ok := other.(*IndexLookUpRunTimeStats) + if !ok { + return + } + e.FetchHandleTotal += tmp.FetchHandleTotal + e.FetchHandle += tmp.FetchHandle + e.TaskWait += tmp.TaskWait + e.TableRowScan += tmp.TableRowScan + e.TableTaskNum += tmp.TableTaskNum + e.NextWaitIndexScan += tmp.NextWaitIndexScan + e.NextWaitTableLookUpBuild += tmp.NextWaitTableLookUpBuild + e.NextWaitTableLookUpResp += tmp.NextWaitTableLookUpResp +} + +// Tp implements the RuntimeStats interface. +func (*IndexLookUpRunTimeStats) Tp() int { + return execdetails.TpIndexLookUpRunTimeStats +} + +func (w *tableWorker) compareData(ctx context.Context, task *lookupTableTask, tableReader exec.Executor) error { + chk := exec.TryNewCacheChunk(tableReader) + tblInfo := w.idxLookup.table.Meta() + vals := make([]types.Datum, 0, len(w.idxTblCols)) + + // Prepare collator for compare. + collators := make([]collate.Collator, 0, len(w.idxColTps)) + for _, tp := range w.idxColTps { + collators = append(collators, collate.GetCollator(tp.GetCollate())) + } + + ir := func() *consistency.Reporter { + return &consistency.Reporter{ + HandleEncode: func(handle kv.Handle) kv.Key { + return tablecodec.EncodeRecordKey(w.idxLookup.table.RecordPrefix(), handle) + }, + IndexEncode: func(idxRow *consistency.RecordData) kv.Key { + var idx table.Index + for _, v := range w.idxLookup.table.Indices() { + if strings.EqualFold(v.Meta().Name.String(), w.idxLookup.index.Name.O) { + idx = v + break + } + } + if idx == nil { + return nil + } + k, _, err := idx.GenIndexKey(w.idxLookup.Ctx().GetSessionVars().StmtCtx, idxRow.Values[:len(idx.Meta().Columns)], idxRow.Handle, nil) + if err != nil { + return nil + } + return k + }, + Tbl: tblInfo, + Idx: w.idxLookup.index, + Sctx: w.idxLookup.Ctx(), + } + } + + for { + err := exec.Next(ctx, tableReader, chk) + if err != nil { + return errors.Trace(err) + } + + // If ctx is cancelled, `Next` may return empty result when the actual data is not empty. To avoid producing + // false-positive error logs that cause confusion, exit in this case. + select { + case <-ctx.Done(): + return nil + default: + } + + if chk.NumRows() == 0 { + task.indexOrder.Range(func(h kv.Handle, val interface{}) bool { + idxRow := task.idxRows.GetRow(val.(int)) + err = ir().ReportAdminCheckInconsistent(ctx, h, &consistency.RecordData{Handle: h, Values: getDatumRow(&idxRow, w.idxColTps)}, nil) + return false + }) + if err != nil { + return err + } + break + } + + iter := chunk.NewIterator4Chunk(chk) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + handle, err := w.idxLookup.getHandle(row, w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) + if err != nil { + return err + } + v, ok := task.indexOrder.Get(handle) + if !ok { + v, _ = task.duplicatedIndexOrder.Get(handle) + } + offset, _ := v.(int) + task.indexOrder.Delete(handle) + idxRow := task.idxRows.GetRow(offset) + vals = vals[:0] + for i, col := range w.idxTblCols { + vals = append(vals, row.GetDatum(i, &col.FieldType)) + } + tablecodec.TruncateIndexValues(tblInfo, w.idxLookup.index, vals) + sctx := w.idxLookup.Ctx().GetSessionVars().StmtCtx + for i := range vals { + col := w.idxTblCols[i] + idxVal := idxRow.GetDatum(i, w.idxColTps[i]) + tablecodec.TruncateIndexValue(&idxVal, w.idxLookup.index.Columns[i], col.ColumnInfo) + cmpRes, err := tables.CompareIndexAndVal(sctx, vals[i], idxVal, collators[i], col.FieldType.IsArray() && vals[i].Kind() == types.KindMysqlJSON) + if err != nil { + return ir().ReportAdminCheckInconsistentWithColInfo(ctx, + handle, + col.Name.O, + idxVal, + vals[i], + err, + &consistency.RecordData{Handle: handle, Values: getDatumRow(&idxRow, w.idxColTps)}, + ) + } + if cmpRes != 0 { + return ir().ReportAdminCheckInconsistentWithColInfo(ctx, + handle, + col.Name.O, + idxRow.GetDatum(i, w.idxColTps[i]), + vals[i], + err, + &consistency.RecordData{Handle: handle, Values: getDatumRow(&idxRow, w.idxColTps)}, + ) + } + } + } + } + return nil +} + +func getDatumRow(r *chunk.Row, fields []*types.FieldType) []types.Datum { + datumRow := make([]types.Datum, 0, r.Chunk().NumCols()) + for colIdx := 0; colIdx < r.Chunk().NumCols(); colIdx++ { + if colIdx >= len(fields) { + break + } + datum := r.GetDatum(colIdx, fields[colIdx]) + datumRow = append(datumRow, datum) + } + return datumRow +} + +// executeTask executes the table look up tasks. We will construct a table reader and send request by handles. +// Then we hold the returning rows and finish this task. +func (w *tableWorker) executeTask(ctx context.Context, task *lookupTableTask) error { + tableReader, err := w.idxLookup.buildTableReader(ctx, task) + task.buildDoneTime = time.Now() + if err != nil { + logutil.Logger(ctx).Error("build table reader failed", zap.Error(err)) + return err + } + defer terror.Call(tableReader.Close) + + if w.checkIndexValue != nil { + return w.compareData(ctx, task, tableReader) + } + + task.memTracker = w.memTracker + memUsage := int64(cap(task.handles) * 8) + task.memUsage = memUsage + task.memTracker.Consume(memUsage) + handleCnt := len(task.handles) + task.rows = make([]chunk.Row, 0, handleCnt) + for { + chk := exec.TryNewCacheChunk(tableReader) + err = exec.Next(ctx, tableReader, chk) + if err != nil { + logutil.Logger(ctx).Error("table reader fetch next chunk failed", zap.Error(err)) + return err + } + if chk.NumRows() == 0 { + break + } + memUsage = chk.MemoryUsage() + task.memUsage += memUsage + task.memTracker.Consume(memUsage) + iter := chunk.NewIterator4Chunk(chk) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + task.rows = append(task.rows, row) + } + } + + defer trace.StartRegion(ctx, "IndexLookUpTableCompute").End() + memUsage = int64(cap(task.rows)) * int64(unsafe.Sizeof(chunk.Row{})) + task.memUsage += memUsage + task.memTracker.Consume(memUsage) + if w.keepOrder { + task.rowIdx = make([]int, 0, len(task.rows)) + for i := range task.rows { + handle, err := w.idxLookup.getHandle(task.rows[i], w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) + if err != nil { + return err + } + rowIdx, _ := task.indexOrder.Get(handle) + task.rowIdx = append(task.rowIdx, rowIdx.(int)) + } + memUsage = int64(cap(task.rowIdx) * 4) + task.memUsage += memUsage + task.memTracker.Consume(memUsage) + sort.Sort(task) + } + + if handleCnt != len(task.rows) && !util.HasCancelled(ctx) && + !w.idxLookup.Ctx().GetSessionVars().StmtCtx.WeakConsistency { + if len(w.idxLookup.tblPlans) == 1 { + obtainedHandlesMap := kv.NewHandleMap() + for _, row := range task.rows { + handle, err := w.idxLookup.getHandle(row, w.handleIdx, w.idxLookup.isCommonHandle(), getHandleFromTable) + if err != nil { + return err + } + obtainedHandlesMap.Set(handle, true) + } + missHds := GetLackHandles(task.handles, obtainedHandlesMap) + return (&consistency.Reporter{ + HandleEncode: func(hd kv.Handle) kv.Key { + return tablecodec.EncodeRecordKey(w.idxLookup.table.RecordPrefix(), hd) + }, + Tbl: w.idxLookup.table.Meta(), + Idx: w.idxLookup.index, + Sctx: w.idxLookup.Ctx(), + }).ReportLookupInconsistent(ctx, + handleCnt, + len(task.rows), + missHds, + task.handles, + nil, + //missRecords, + ) + } + } + + return nil +} + +// GetLackHandles gets the handles in expectedHandles but not in obtainedHandlesMap. +func GetLackHandles(expectedHandles []kv.Handle, obtainedHandlesMap *kv.HandleMap) []kv.Handle { + diffCnt := len(expectedHandles) - obtainedHandlesMap.Len() + diffHandles := make([]kv.Handle, 0, diffCnt) + var cnt int + for _, handle := range expectedHandles { + isExist := false + if _, ok := obtainedHandlesMap.Get(handle); ok { + obtainedHandlesMap.Delete(handle) + isExist = true + } + if !isExist { + diffHandles = append(diffHandles, handle) + cnt++ + if cnt == diffCnt { + break + } + } + } + + return diffHandles +} + +func getPhysicalPlanIDs(plans []plannercore.PhysicalPlan) []int { + planIDs := make([]int, 0, len(plans)) + for _, p := range plans { + planIDs = append(planIDs, p.ID()) + } + return planIDs +} diff --git a/pkg/executor/distsql_test.go b/pkg/executor/distsql_test.go new file mode 100644 index 0000000000000..64402e8911f2d --- /dev/null +++ b/pkg/executor/distsql_test.go @@ -0,0 +1,700 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "bytes" + "context" + "fmt" + "math/rand" + "regexp" + "runtime/pprof" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" +) + +// checkGoroutineExists +// nolint:unused +func checkGoroutineExists(keyword string) bool { + buf := new(bytes.Buffer) + profile := pprof.Lookup("goroutine") + err := profile.WriteTo(buf, 1) + if err != nil { + panic(err) + } + str := buf.String() + return strings.Contains(str, keyword) +} + +func TestCopClientSend(t *testing.T) { + t.Skip("not stable") + var cluster testutils.Cluster + store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cluster = c + })) + if _, ok := store.GetClient().(*copr.CopClient); !ok { + // Make sure the store is tikv store. + return + } + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table copclient (id int primary key)") + + // Insert 1000 rows. + var values []string + for i := 0; i < 1000; i++ { + values = append(values, fmt.Sprintf("(%d)", i)) + } + tk.MustExec("insert copclient values " + strings.Join(values, ",")) + + // Get table ID for split. + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("copclient")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + // Split the table. + tableStart := tablecodec.GenTableRecordPrefix(tblID) + cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 100) + + ctx := context.Background() + // Send coprocessor request when the table split. + rs, err := tk.Exec("select sum(id) from copclient") + require.NoError(t, err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, "499500", req.GetRow(0).GetMyDecimal(0).String()) + require.NoError(t, rs.Close()) + + // Split one region. + key := tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(500)) + region, _, _ := cluster.GetRegionByKey(key) + peerID := cluster.AllocID() + cluster.Split(region.GetId(), cluster.AllocID(), key, []uint64{peerID}, peerID) + + // Check again. + rs, err = tk.Exec("select sum(id) from copclient") + require.NoError(t, err) + req = rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, "499500", req.GetRow(0).GetMyDecimal(0).String()) + require.NoError(t, rs.Close()) + + // Check there is no goroutine leak. + rs, err = tk.Exec("select * from copclient order by id") + require.NoError(t, err) + req = rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + require.NoError(t, rs.Close()) + keyword := "(*copIterator).work" + require.False(t, checkGoroutineExists(keyword)) +} + +func TestGetLackHandles(t *testing.T) { + expectedHandles := []kv.Handle{kv.IntHandle(1), kv.IntHandle(2), kv.IntHandle(3), kv.IntHandle(4), + kv.IntHandle(5), kv.IntHandle(6), kv.IntHandle(7), kv.IntHandle(8), kv.IntHandle(9), kv.IntHandle(10)} + handlesMap := kv.NewHandleMap() + for _, h := range expectedHandles { + handlesMap.Set(h, true) + } + + // expected handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + // obtained handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + diffHandles := executor.GetLackHandles(expectedHandles, handlesMap) + require.Len(t, diffHandles, 0) + require.Equal(t, 0, handlesMap.Len()) + + // expected handles 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + // obtained handles 2, 3, 4, 6, 7, 8, 9 + retHandles := []kv.Handle{kv.IntHandle(2), kv.IntHandle(3), kv.IntHandle(4), kv.IntHandle(6), + kv.IntHandle(7), kv.IntHandle(8), kv.IntHandle(9)} + handlesMap = kv.NewHandleMap() + handlesMap.Set(kv.IntHandle(1), true) + handlesMap.Set(kv.IntHandle(5), true) + handlesMap.Set(kv.IntHandle(10), true) + diffHandles = executor.GetLackHandles(expectedHandles, handlesMap) + require.Equal(t, diffHandles, retHandles) // deep equal +} + +func TestBigIntPK(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(a bigint unsigned primary key, b int, c int, index idx(a, b))") + tk.MustExec("insert into t values(1, 1, 1), (9223372036854775807, 2, 2)") + tk.MustQuery("select * from t use index(idx) order by a").Check(testkit.Rows("1 1 1", "9223372036854775807 2 2")) +} + +func TestCorColToRanges(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only-full-group-by + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b int, c int, index idx(b))") + tk.MustExec("insert into t values(1, 1, 1), (2, 2 ,2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9)") + tk.MustExec("analyze table t") + // Test single read on table. + tk.MustQuery("select t.c in (select count(*) from t s ignore index(idx), t t1 where s.a = t.a and s.a = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) + // Test single read on index. + tk.MustQuery("select t.c in (select count(*) from t s use index(idx), t t1 where s.b = t.a and s.a = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) + // Test IndexLookUpReader. + tk.MustQuery("select t.c in (select count(*) from t s use index(idx), t t1 where s.b = t.a and s.c = t1.a) from t order by 1 desc").Check(testkit.Rows("1", "0", "0", "0", "0", "0", "0", "0", "0")) +} + +func TestUniqueKeyNullValueSelect(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + // test null in unique-key + tk.MustExec("create table t (id int default null, c varchar(20), unique id (id));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res := tk.MustQuery("select * from t where id is null;") + res.Check(testkit.Rows(" a", " b", " c")) + + // test null in mul unique-key + tk.MustExec("drop table t") + tk.MustExec("create table t (id int default null, b int default 1, c varchar(20), unique id_c(id, b));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res = tk.MustQuery("select * from t where id is null and b = 1;") + res.Check(testkit.Rows(" 1 a", " 1 b", " 1 c")) + + tk.MustExec("drop table t") + // test null in non-unique-key + tk.MustExec("create table t (id int default null, c varchar(20), key id (id));") + tk.MustExec("insert t (c) values ('a'), ('b'), ('c');") + res = tk.MustQuery("select * from t where id is null;") + res.Check(testkit.Rows(" a", " b", " c")) +} + +// TestIssue10178 contains tests for https://github.com/pingcap/tidb/issues/10178 . +func TestIssue10178(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a bigint unsigned primary key)") + tk.MustExec("insert into t values(9223372036854775807), (18446744073709551615)") + tk.MustQuery("select max(a) from t").Check(testkit.Rows("18446744073709551615")) + tk.MustQuery("select * from t where a > 9223372036854775807").Check(testkit.Rows("18446744073709551615")) + tk.MustQuery("select * from t where a < 9223372036854775808").Check(testkit.Rows("9223372036854775807")) +} + +func TestInconsistentIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx_a(a))") + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + idx := tbl.Meta().FindIndexByName("idx_a") + idxOp := tables.NewIndex(tbl.Meta().ID, tbl.Meta(), idx) + ctx := mock.NewContext() + ctx.Store = store + + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", i+10, i)) + require.NoError(t, tk.QueryToErr("select * from t where a>=0")) + } + + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("update t set a=%d where a=%d", i, i+10)) + require.NoError(t, tk.QueryToErr("select * from t where a>=0")) + } + + for i := 0; i < 10; i++ { + txn, err := store.Begin() + require.NoError(t, err) + _, err = idxOp.Create(ctx, txn, types.MakeDatums(i+10), kv.IntHandle(100+i), nil) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + err = tk.QueryToErr("select * from t use index(idx_a) where a >= 0") + require.Equal(t, fmt.Sprintf("[executor:8133]data inconsistency in table: t, index: idx_a, index-count:%d != record-count:10", i+11), err.Error()) + // if has other conditions, the inconsistent index check doesn't work. + err = tk.QueryToErr("select * from t where a>=0 and b<10") + require.NoError(t, err) + } + + // fix inconsistent problem to pass CI + for i := 0; i < 10; i++ { + txn, err := store.Begin() + require.NoError(t, err) + err = idxOp.Delete(ctx.GetSessionVars().StmtCtx, txn, types.MakeDatums(i+10), kv.IntHandle(100+i)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + } +} + +func TestPartitionTableIndexLookUpReader(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`create table t (a int, b int, key(a)) + partition by range (a) ( + partition p1 values less than (10), + partition p2 values less than (20), + partition p3 values less than (30), + partition p4 values less than (40))`) + tk.MustExec(`insert into t values (1, 1), (2, 2), (11, 11), (12, 12), (21, 21), (22, 22), (31, 31), (32, 32)`) + tk.MustExec(`set tidb_partition_prune_mode='dynamic'`) + + tk.MustQuery("select * from t where a>=1 and a<=1").Sort().Check(testkit.Rows("1 1")) + tk.MustQuery("select * from t where a>=1 and a<=2").Sort().Check(testkit.Rows("1 1", "2 2")) + tk.MustQuery("select * from t where a>=1 and a<12").Sort().Check(testkit.Rows("1 1", "11 11", "2 2")) + tk.MustQuery("select * from t where a>=1 and a<15").Sort().Check(testkit.Rows("1 1", "11 11", "12 12", "2 2")) + tk.MustQuery("select * from t where a>15 and a<32").Sort().Check(testkit.Rows("21 21", "22 22", "31 31")) + tk.MustQuery("select * from t where a>30").Sort().Check(testkit.Rows("31 31", "32 32")) + tk.MustQuery("select * from t where a>=1 and a<15 order by a").Check(testkit.Rows("1 1", "2 2", "11 11", "12 12")) + tk.MustQuery("select * from t where a>=1 and a<15 order by a limit 1").Check(testkit.Rows("1 1")) + tk.MustQuery("select * from t where a>=1 and a<15 order by a limit 3").Check(testkit.Rows("1 1", "2 2", "11 11")) + tk.MustQuery("select * from t where a between 1 and 15 order by a limit 3").Check(testkit.Rows("1 1", "2 2", "11 11")) + tk.MustQuery("select * from t where a between 1 and 15 order by a limit 3 offset 1").Check(testkit.Rows("2 2", "11 11", "12 12")) +} + +func TestPartitionTableRandomlyIndexLookUpReader(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`create table t (a int, b int, key(a)) + partition by range (a) ( + partition p1 values less than (10), + partition p2 values less than (20), + partition p3 values less than (30), + partition p4 values less than (40))`) + tk.MustExec("create table tnormal (a int, b int, key(a))") + values := make([]string, 0, 128) + for i := 0; i < 128; i++ { + values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(40), rand.Intn(40))) + } + tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) + tk.MustExec(fmt.Sprintf("insert into tnormal values %v", strings.Join(values, ", "))) + + randRange := func() (int, int) { + a, b := rand.Intn(40), rand.Intn(40) + if a > b { + return b, a + } + return a, b + } + for i := 0; i < 256; i++ { + la, ra := randRange() + lb, rb := randRange() + cond := fmt.Sprintf("(a between %v and %v) or (b between %v and %v)", la, ra, lb, rb) + tk.MustQuery("select * from t use index(a) where " + cond).Sort().Check( + tk.MustQuery("select * from tnormal where " + cond).Sort().Rows()) + } +} + +func TestIndexLookUpStats(t *testing.T) { + stats := &executor.IndexLookUpRunTimeStats{ + FetchHandleTotal: int64(5 * time.Second), + FetchHandle: int64(2 * time.Second), + TaskWait: int64(2 * time.Second), + TableRowScan: int64(2 * time.Second), + TableTaskNum: 2, + Concurrency: 1, + NextWaitIndexScan: time.Second, + NextWaitTableLookUpBuild: 2 * time.Second, + NextWaitTableLookUpResp: 3 * time.Second, + } + require.Equal(t, "index_task: {total_time: 5s, fetch_handle: 2s, build: 1s, wait: 2s}"+ + ", table_task: {total_time: 2s, num: 2, concurrency: 1}"+ + ", next: {wait_index: 1s, wait_table_lookup_build: 2s, wait_table_lookup_resp: 3s}", stats.String()) + require.Equal(t, stats.Clone().String(), stats.String()) + stats.Merge(stats.Clone()) + require.Equal(t, "index_task: {total_time: 10s, fetch_handle: 4s, build: 2s, wait: 4s}"+ + ", table_task: {total_time: 4s, num: 4, concurrency: 1}"+ + ", next: {wait_index: 2s, wait_table_lookup_build: 4s, wait_table_lookup_resp: 6s}", stats.String()) +} + +func TestIndexLookUpGetResultChunk(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists tbl") + tk.MustExec("create table tbl(a int, b int, c int, key idx_a(a))") + for i := 0; i < 101; i++ { + tk.MustExec(fmt.Sprintf("insert into tbl values(%d,%d,%d)", i, i, i)) + } + tk.MustQuery("select * from tbl use index(idx_a) where a > 99 order by a asc limit 1").Check(testkit.Rows("100 100 100")) + tk.MustQuery("select * from tbl use index(idx_a) where a > 10 order by a asc limit 4,1").Check(testkit.Rows("15 15 15")) +} + +func TestPartitionTableIndexJoinIndexLookUp(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk.MustExec(`create table t (a int, b int, key(a)) partition by hash(a) partitions 4`) + tk.MustExec("create table tnormal (a int, b int, key(a), key(b))") + nRows := 512 + values := make([]string, 0, nRows) + for i := 0; i < nRows; i++ { + values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(nRows), rand.Intn(nRows))) + } + tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) + tk.MustExec(fmt.Sprintf("insert into tnormal values %v", strings.Join(values, ", "))) + + randRange := func() (int, int) { + a, b := rand.Intn(nRows), rand.Intn(nRows) + if a > b { + return b, a + } + return a, b + } + for i := 0; i < nRows; i++ { + lb, rb := randRange() + cond := fmt.Sprintf("(t2.b between %v and %v)", lb, rb) + result := tk.MustQuery("select t1.* from tnormal t1, tnormal t2 use index(a) where t1.a=t2.b and " + cond).Sort().Rows() + tk.MustQuery("select /*+ TIDB_INLJ(t1, t2) */ t1.* from t t1, t t2 use index(a) where t1.a=t2.b and " + cond).Sort().Check(result) + } +} + +func TestCoprocessorPagingSize(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t_paging (a int, b int, key(a), key(b))") + nRows := 512 + values := make([]string, 0, nRows) + for i := 0; i < nRows; i++ { + values = append(values, fmt.Sprintf("(%v, %v)", rand.Intn(nRows), rand.Intn(nRows))) + } + tk.MustExec(fmt.Sprintf("insert into t_paging values %v", strings.Join(values, ", "))) + tk.MustQuery("select @@tidb_min_paging_size").Check(testkit.Rows(strconv.FormatUint(paging.MinPagingSize, 10))) + + // Enable the coprocessor paging protocol. + tk.MustExec("set @@tidb_enable_paging = on") + + // When the min paging size is small, we need more RPC roundtrip! + // Check 'rpc_num' in the execution information + // + // mysql> explain analyze select * from t_paging; + // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + // | id |task | execution info | + // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + // | TableReader_5 |root | time:7.27ms, loops:2, cop_task: {num: 10, max: 1.57ms, min: 313.3µs, avg: 675.9µs, p95: 1.57ms, tot_proc: 2ms, rpc_num: 10, rpc_time: 6.69ms, copr_cache_hit_ratio: 0.00, distsql_concurrency: 15} | + // | └─TableFullScan_4 |cop[tikv] | tikv_task:{proc max:1.48ms, min:294µs, avg: 629µs, p80:1.21ms, p95:1.48ms, iters:0, tasks:10} | + // +--------------------+----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ + // 2 rows in set (0.01 sec) + + getRPCNumFromExplain := func(rows [][]interface{}) (res uint64) { + re := regexp.MustCompile("rpc_num: ([0-9]+)") + for _, row := range rows { + buf := bytes.NewBufferString("") + _, _ = fmt.Fprintf(buf, "%s\n", row) + if matched := re.FindStringSubmatch(buf.String()); matched != nil { + require.Equal(t, len(matched), 2) + c, err := strconv.ParseUint(matched[1], 10, 64) + require.NoError(t, err) + return c + } + } + return res + } + + // This is required here because only the chunk encoding collect the execution information and contains 'rpc_num'. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + tk.MustExec("set @@tidb_min_paging_size = 1") + rows := tk.MustQuery("explain analyze select * from t_paging").Rows() + rpcNum := getRPCNumFromExplain(rows) + require.Greater(t, rpcNum, uint64(2)) + + tk.MustExec("set @@tidb_min_paging_size = 1000") + rows = tk.MustQuery("explain analyze select * from t_paging").Rows() + rpcNum = getRPCNumFromExplain(rows) + require.Equal(t, rpcNum, uint64(1)) +} + +func TestAdaptiveClosestRead(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect this UT + tk.MustExec("drop table if exists t") + // the avg row size is more accurate in check_rpc mode when unistre is used. + // See: https://github.com/pingcap/tidb/issues/31744#issuecomment-1016309883 + tk.MustExec("set @@tidb_enable_chunk_rpc = '1'") + + readCounter := func(counter prometheus.Counter) float64 { + var metric dto.Metric + require.Nil(t, counter.Write(&metric)) + return metric.Counter.GetValue() + } + + checkMetrics := func(q string, hit, miss int) { + beforeHit := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("hit")) + beforeMiss := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("miss")) + tk.MustQuery(q) + afterHit := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("hit")) + afterMiss := readCounter(metrics.DistSQLCoprClosestReadCounter.WithLabelValues("miss")) + require.Equal(t, hit, int(afterHit-beforeHit), "exec query '%s' check hit failed", q) + require.Equal(t, miss, int(afterMiss-beforeMiss), "exec query '%s' check miss failed", q) + } + + tk.MustExec("create table t(id int primary key, s varchar(8), p varchar(16));") + tk.MustExec("insert into t values (1, '00000001', '0000000000000001'), (2, '00000003', '0000000000000002'), (3, '00000011', '0000000000000003');") + tk.MustExec("analyze table t;") + + tk.MustExec("set @@tidb_partition_prune_mode ='static';") + tk.MustExec("set tidb_replica_read = 'closest-adaptive';") + tk.MustExec("set tidb_adaptive_closest_read_threshold = 25;") + + // table reader + // estimate cost is 19 + checkMetrics("select s from t where id >= 1 and id < 2;", 0, 1) + // estimate cost is 37 + checkMetrics("select * from t where id >= 1 and id < 2;", 1, 0) + tk.MustExec("set tidb_adaptive_closest_read_threshold = 50;") + checkMetrics("select * from t where id >= 1 and id < 2;", 0, 1) + // estimate cost is 74 + checkMetrics("select * from t where id >= 1 and id <= 2;", 1, 0) + + partitionDef := "PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (3), PARTITION p3 VALUES LESS THAN MAXVALUE);" + + // test TableReader with partition + tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int primary key, s varchar(8), p varchar(16)) " + partitionDef) + tk.MustExec("insert into t values (1, '00000001', '0000000000000001'), (2, '00000003', '0000000000000002'), (3, '00000011', '0000000000000003'), (4, '00000044', '0000000000000004');") + tk.MustExec("analyze table t;") + // estimate cost is 38 + checkMetrics("select s from t where id >= 1 and id < 3;", 1, 0) + // estimate cost is 39 with 2 cop request + checkMetrics("select s from t where id >= 2 and id < 4;", 0, 2) + + // index reader + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (id int, s varchar(8), p varchar(8), key `idx_s_p`(`s`, `p`));") + tk.MustExec("insert into t values (1, 'test1000', '11111111'), (2, 'test2000', '11111111');") + tk.MustExec("analyze table t;") + // avg row size = 27.91 + checkMetrics("select p from t where s >= 'test' and s < 'test11'", 0, 1) + checkMetrics("select p from t where s >= 'test' and s < 'test22'", 1, 0) + + // index reader with partitions + tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (v int, id int, p varchar(8), key `idx_id_p`(`id`, `p`)) " + partitionDef) + tk.MustExec("insert into t values (1, 1, '11111111'), (2, 2, '22222222'), (3, 3, '33333333'), (4, 4, '44444444');") + tk.MustExec("analyze table t;") + // avg row size = 19 + checkMetrics("select p from t where id >= 1 and id < 3", 1, 0) + checkMetrics("select p from t where id >= 2 and id < 4", 0, 2) + checkMetrics("select p from t where id >= 1 and id < 4", 1, 1) + + // index lookup reader + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (id int, s varchar(8), p varchar(50), key `idx_s`(`s`));") + str := "this_is_a_string_with_length_of_50________________" + tk.MustExec(fmt.Sprintf("insert into t values (1, 'test1000', '%s'), (2, 'test2000', '%s');", str, str)) + tk.MustExec("analyze table t;") + tk.MustExec("set tidb_adaptive_closest_read_threshold = 80;") + // IndexReader cost is 22, TableReader cost (1 row) is 67 + checkMetrics("select/*+ FORCE_INDEX(t, idx_s) */ p from t where s >= 'test' and s < 'test11'", 0, 2) + tk.MustExec("set tidb_adaptive_closest_read_threshold = 100;") + checkMetrics("select/*+ FORCE_INDEX(t, idx_s) */ p from t where s >= 'test' and s < 'test22'", 1, 1) + + // index merge reader + tk.MustExec("drop table if exists t;") + // use int field to avoid the planer estimation with big random fluctuation. + tk.MustExec("create table t (id int, v bigint not null, s1 int not null, s2 int not null, key `idx_v_s1`(`s1`, `v`), key `idx_s2`(`s2`));") + tk.MustExec("insert into t values (1, 1, 1, 1), (2, 2, 2, 2), (3, 3, 3, 3);") + tk.MustExec("analyze table t;") + tk.MustExec("set tidb_adaptive_closest_read_threshold = 30;") + // 2 IndexScan with cost 19/56, 2 TableReader with cost 32.5/65. + checkMetrics("select/* +USE_INDEX_MERGE(t) */ id from t use index(`idx_v_s1`) use index(idx_s2) where (s1 < 3 and v > 0) or s2 = 3;", 3, 1) +} + +func TestCoprocessorPagingReqKeyRangeSorted(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/checkKeyRangeSortedForPaging", "return")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/checkKeyRangeSortedForPaging")) + }() + + tk.MustExec("use test") + tk.MustExec("CREATE TABLE `UK_COLLATION19523` (" + + "`COL1` binary(1) DEFAULT NULL," + + "`COL2` varchar(20) COLLATE utf8_general_ci DEFAULT NULL," + + "`COL4` datetime DEFAULT NULL," + + "`COL3` bigint(20) DEFAULT NULL," + + "`COL5` float DEFAULT NULL," + + "UNIQUE KEY `U_COL1` (`COL1`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci") + + tk.MustExec("prepare stmt from 'SELECT/*+ HASH_JOIN(t1, t2) */ * FROM UK_COLLATION19523 t1 JOIN UK_COLLATION19523 t2 ON t1.col1 > t2.col1 WHERE t1.col1 IN (?, ?, ?) AND t2.col1 < ?;';") + tk.MustExec("set @a=0x4F, @b=0xF8, @c=NULL, @d=0xBF;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + tk.MustExec("set @a=0x00, @b=0xD2, @c=9179987834981541375, @d=0xF8;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + + tk.MustExec("CREATE TABLE `IDT_COLLATION26873` (" + + "`COL1` varbinary(20) DEFAULT NULL," + + "`COL2` varchar(20) COLLATE utf8_general_ci DEFAULT NULL," + + "`COL4` datetime DEFAULT NULL," + + "`COL3` bigint(20) DEFAULT NULL," + + "`COL5` float DEFAULT NULL," + + "KEY `U_COL1` (`COL1`))") + tk.MustExec("prepare stmt from 'SELECT/*+ INL_JOIN(t1, t2) */ t2.* FROM IDT_COLLATION26873 t1 LEFT JOIN IDT_COLLATION26873 t2 ON t1.col1 = t2.col1 WHERE t1.col1 < ? AND t1.col1 IN (?, ?, ?);';") + tk.MustExec("set @a=NULL, @b=NULL, @c=NULL, @d=NULL;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + tk.MustExec("set @a=0xE3253A6AC72A3A168EAF0E34A4779A947872CCCD, @b=0xD67BB26504EE152C2C356D7F6CAD897F03462963, @c=NULL, @d=0xDE735FEB375A4CF33479A39CA925470BFB229DB4;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + tk.MustExec("set @a=2606738829406840179, @b=1468233589368287363, @c=5174008984061521089, @d=7727946571160309462;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + tk.MustExec("set @a=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @b=NULL, @c=6864108002939154648, @d=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + tk.MustExec("set @a=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @b=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @c=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE, @d=0xFCABFE6198B6323EE8A46247EDD33830453B1BDE;") + tk.MustExec("execute stmt using @a,@b,@c,@d;") + + tk.MustExec("CREATE TABLE `PK_SNPRE10114` (" + + "`COL1` varbinary(10) NOT NULL DEFAULT 'S'," + + "`COL2` varchar(20) DEFAULT NULL," + + "`COL4` datetime DEFAULT NULL," + + "`COL3` bigint(20) DEFAULT NULL," + + "`COL5` float DEFAULT NULL," + + "PRIMARY KEY (`COL1`) CLUSTERED)") + tk.MustExec(`prepare stmt from 'SELECT * FROM PK_SNPRE10114 WHERE col1 IN (?, ?, ?) AND (col2 IS NULL OR col2 IN (?, ?)) AND (col3 IS NULL OR col4 IS NULL);';`) + tk.MustExec(`set @a=0x0D5BDAEB79074756F203, @b=NULL, @c=0x6A911AAAC728F1ED3B4F, @d="鏖秿垙麜濇凗辯Ũ卮伄幖轒ƀ漭蝏雓轊恿磔徵", @e="訇廵纹髺釖寒近槩靏詗膦潳陒錃粓悧闒摔)乀";`) + tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) + tk.MustExec(`set @a=7775448739068993371, @b=5641728652098016210, @c=6774432238941172824, @d="HqpP5rN", @e="8Fy";`) + tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) + tk.MustExec(`set @a=0x61219F79C90D3541F70E, @b=5501707547099269248, @c=0xEC43EFD30131DEA2CB8B, @d="呣丼蒢咿卻鹻铴础湜僂頃dž縍套衞陀碵碼幓9", @e="鹹楞睕堚尛鉌翡佾搁紟精廬姆燵藝潐楻翇慸嵊";`) + tk.MustExec(`execute stmt using @a,@b,@c,@d,@e;`) +} + +func TestCoprocessorBatchByStore(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t(id int primary key, c1 int, c2 int, key i(c1))") + tk.MustExec(`create table t1(id int primary key, c1 int, c2 int, key i(c1)) partition by range(id) ( + partition p0 values less than(10000), + partition p1 values less than (50000), + partition p2 values less than (100000))`) + for i := 0; i < 10; i++ { + tk.MustExec("insert into t values(?, ?, ?)", i*10000, i*10000, i%2) + tk.MustExec("insert into t1 values(?, ?, ?)", i*10000, i*10000, i%2) + } + tk.MustQuery("split table t between (0) and (100000) regions 20").Check(testkit.Rows("20 1")) + tk.MustQuery("split table t1 between (0) and (100000) regions 20").Check(testkit.Rows("60 1")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/setRangesPerTask", "return(1)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/setRangesPerTask")) + }() + ranges := []string{ + "(c1 >= 0 and c1 < 5000)", + "(c1 >= 10000 and c1 < 15000)", + "(c1 >= 20000 and c1 < 25000)", + "(c1 >= 30000 and c1 < 35000)", + "(c1 >= 40000 and c1 < 45000)", + "(c1 >= 50000 and c1 < 55000)", + "(c1 >= 60000 and c1 < 65000)", + "(c1 >= 70000 and c1 < 75000)", + "(c1 >= 80000 and c1 < 85000)", + "(c1 >= 90000 and c1 < 95000)", + } + evenRows := testkit.Rows("0 0 0", "20000 20000 0", "40000 40000 0", "60000 60000 0", "80000 80000 0") + oddRows := testkit.Rows("10000 10000 1", "30000 30000 1", "50000 50000 1", "70000 70000 1", "90000 90000 1") + reverseOddRows := testkit.Rows("90000 90000 1", "70000 70000 1", "50000 50000 1", "30000 30000 1", "10000 10000 1") + for _, table := range []string{"t", "t1"} { + baseSQL := fmt.Sprintf("select * from %s force index(i) where id < 100000 and (%s)", table, strings.Join(ranges, " or ")) + for _, paging := range []string{"on", "off"} { + tk.MustExec("set session tidb_enable_paging=?", paging) + for size := 0; size < 10; size++ { + tk.MustExec("set session tidb_store_batch_size=?", size) + tk.MustQuery(baseSQL + " and c2 = 0").Sort().Check(evenRows) + tk.MustQuery(baseSQL + " and c2 = 1").Sort().Check(oddRows) + tk.MustQuery(baseSQL + " and c2 = 0 order by c1 asc").Check(evenRows) + tk.MustQuery(baseSQL + " and c2 = 1 order by c1 desc").Check(reverseOddRows) + // every batched task will get region error and fallback. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/batchCopRegionError", "return")) + tk.MustQuery(baseSQL + " and c2 = 0").Sort().Check(evenRows) + tk.MustQuery(baseSQL + " and c2 = 1").Sort().Check(oddRows) + tk.MustQuery(baseSQL + " and c2 = 0 order by c1 asc").Check(evenRows) + tk.MustQuery(baseSQL + " and c2 = 1 order by c1 desc").Check(reverseOddRows) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/batchCopRegionError")) + } + } + } +} + +func TestIndexLookUpWithSelectForUpdateOnPartitionTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index k(b)) PARTITION BY HASH(a) partitions 4") + tk.MustExec("insert into t(a, b) values (1,1),(2,2),(3,3),(4,4),(5,5),(6,6),(7,7),(8,8)") + tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "PartitionUnion") + tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "IndexLookUp") + tk.MustQuery("select b from t use index(k) where b > 2 order by b limit 1 for update").Check(testkit.Rows("3")) + + tk.MustExec("analyze table t") + tk.MustHavePlan("select b from t use index(k) where b > 2 order by b limit 1 for update", "IndexLookUp") + tk.MustQuery("select b from t use index(k) where b > 2 order by b limit 1 for update").Check(testkit.Rows("3")) +} diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go new file mode 100644 index 0000000000000..d94a7b924fed4 --- /dev/null +++ b/pkg/executor/executor.go @@ -0,0 +1,2693 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "cmp" + "context" + "fmt" + "math" + "runtime/pprof" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/opentracing/opentracing-go" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/pdhelper" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool" + poolutil "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/admin" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/tracing" + tikverr "github.com/tikv/client-go/v2/error" + tikvstore "github.com/tikv/client-go/v2/kv" + tikvutil "github.com/tikv/client-go/v2/util" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +var ( + _ exec.Executor = &CheckTableExec{} + _ exec.Executor = &aggregate.HashAggExec{} + _ exec.Executor = &HashJoinExec{} + _ exec.Executor = &IndexLookUpExecutor{} + _ exec.Executor = &IndexReaderExecutor{} + _ exec.Executor = &LimitExec{} + _ exec.Executor = &MaxOneRowExec{} + _ exec.Executor = &MergeJoinExec{} + _ exec.Executor = &ProjectionExec{} + _ exec.Executor = &SelectionExec{} + _ exec.Executor = &SelectLockExec{} + _ exec.Executor = &ShowNextRowIDExec{} + _ exec.Executor = &ShowDDLExec{} + _ exec.Executor = &ShowDDLJobsExec{} + _ exec.Executor = &ShowDDLJobQueriesExec{} + _ exec.Executor = &SortExec{} + _ exec.Executor = &aggregate.StreamAggExec{} + _ exec.Executor = &TableDualExec{} + _ exec.Executor = &TableReaderExecutor{} + _ exec.Executor = &TableScanExec{} + _ exec.Executor = &TopNExec{} + _ exec.Executor = &UnionExec{} + _ exec.Executor = &FastCheckTableExec{} + + // GlobalMemoryUsageTracker is the ancestor of all the Executors' memory tracker and GlobalMemory Tracker + GlobalMemoryUsageTracker *memory.Tracker + // GlobalDiskUsageTracker is the ancestor of all the Executors' disk tracker + GlobalDiskUsageTracker *disk.Tracker + // GlobalAnalyzeMemoryTracker is the ancestor of all the Analyze jobs' memory tracker and child of global Tracker + GlobalAnalyzeMemoryTracker *memory.Tracker +) + +var ( + _ dataSourceExecutor = &TableReaderExecutor{} + _ dataSourceExecutor = &IndexReaderExecutor{} + _ dataSourceExecutor = &IndexLookUpExecutor{} + _ dataSourceExecutor = &IndexMergeReaderExecutor{} + + // CheckTableFastBucketSize is the bucket size of fast check table. + CheckTableFastBucketSize = atomic.Int64{} +) + +// dataSourceExecutor is a table DataSource converted Executor. +// Currently, there are TableReader/IndexReader/IndexLookUp/IndexMergeReader. +// Note, partition reader is special and the caller should handle it carefully. +type dataSourceExecutor interface { + exec.Executor + Table() table.Table +} + +const ( + // globalPanicStorageExceed represents the panic message when out of storage quota. + globalPanicStorageExceed string = "Out Of Quota For Local Temporary Space!" + // globalPanicMemoryExceed represents the panic message when out of memory limit. + globalPanicMemoryExceed string = "Out Of Global Memory Limit!" + // globalPanicAnalyzeMemoryExceed represents the panic message when out of analyze memory limit. + globalPanicAnalyzeMemoryExceed string = "Out Of Global Analyze Memory Limit!" +) + +// globalPanicOnExceed panics when GlobalDisTracker storage usage exceeds storage quota. +type globalPanicOnExceed struct { + memory.BaseOOMAction + mutex syncutil.Mutex // For synchronization. +} + +func init() { + action := &globalPanicOnExceed{} + GlobalMemoryUsageTracker = memory.NewGlobalTracker(memory.LabelForGlobalMemory, -1) + GlobalMemoryUsageTracker.SetActionOnExceed(action) + GlobalDiskUsageTracker = disk.NewGlobalTrcaker(memory.LabelForGlobalStorage, -1) + GlobalDiskUsageTracker.SetActionOnExceed(action) + GlobalAnalyzeMemoryTracker = memory.NewTracker(memory.LabelForGlobalAnalyzeMemory, -1) + GlobalAnalyzeMemoryTracker.SetActionOnExceed(action) + // register quota funcs + variable.SetMemQuotaAnalyze = GlobalAnalyzeMemoryTracker.SetBytesLimit + variable.GetMemQuotaAnalyze = GlobalAnalyzeMemoryTracker.GetBytesLimit + // TODO: do not attach now to avoid impact to global, will attach later when analyze memory track is stable + //GlobalAnalyzeMemoryTracker.AttachToGlobalTracker(GlobalMemoryUsageTracker) + + schematracker.ConstructResultOfShowCreateDatabase = ConstructResultOfShowCreateDatabase + schematracker.ConstructResultOfShowCreateTable = ConstructResultOfShowCreateTable + + // CheckTableFastBucketSize is used to set the fast analyze bucket size for check table. + CheckTableFastBucketSize.Store(1024) +} + +// Start the backend components +func Start() { + pdhelper.GlobalPDHelper.Start() +} + +// Stop the backend components +func Stop() { + pdhelper.GlobalPDHelper.Stop() +} + +// Action panics when storage usage exceeds storage quota. +func (a *globalPanicOnExceed) Action(t *memory.Tracker) { + a.mutex.Lock() + defer a.mutex.Unlock() + msg := "" + switch t.Label() { + case memory.LabelForGlobalStorage: + msg = globalPanicStorageExceed + case memory.LabelForGlobalMemory: + msg = globalPanicMemoryExceed + case memory.LabelForGlobalAnalyzeMemory: + msg = globalPanicAnalyzeMemoryExceed + default: + msg = "Out of Unknown Resource Quota!" + } + panic(msg) +} + +// GetPriority get the priority of the Action +func (*globalPanicOnExceed) GetPriority() int64 { + return memory.DefPanicPriority +} + +// newList creates a new List to buffer current executor's result. +func newList(e exec.Executor) *chunk.List { + base := e.Base() + return chunk.NewList(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize()) +} + +// CommandDDLJobsExec is the general struct for Cancel/Pause/Resume commands on +// DDL jobs. These command currently by admin have the very similar struct and +// operations, it should be a better idea to have them in the same struct. +type CommandDDLJobsExec struct { + exec.BaseExecutor + + cursor int + jobIDs []int64 + errs []error + + execute func(se sessionctx.Context, ids []int64) (errs []error, err error) +} + +// Open implements the Executor for all Cancel/Pause/Resume command on DDL jobs +// just with different processes. And, it should not be called directly by the +// Executor. +func (e *CommandDDLJobsExec) Open(context.Context) error { + // We want to use a global transaction to execute the admin command, so we don't use e.Ctx() here. + newSess, err := e.GetSysSession() + if err != nil { + return err + } + e.errs, err = e.execute(newSess, e.jobIDs) + e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), newSess) + return err +} + +// Next implements the Executor Next interface for Cancel/Pause/Resume +func (e *CommandDDLJobsExec) Next(_ context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if e.cursor >= len(e.jobIDs) { + return nil + } + numCurBatch := mathutil.Min(req.Capacity(), len(e.jobIDs)-e.cursor) + for i := e.cursor; i < e.cursor+numCurBatch; i++ { + req.AppendString(0, strconv.FormatInt(e.jobIDs[i], 10)) + if e.errs != nil && e.errs[i] != nil { + req.AppendString(1, fmt.Sprintf("error: %v", e.errs[i])) + } else { + req.AppendString(1, "successful") + } + } + e.cursor += numCurBatch + return nil +} + +// CancelDDLJobsExec represents a cancel DDL jobs executor. +type CancelDDLJobsExec struct { + *CommandDDLJobsExec +} + +// PauseDDLJobsExec indicates an Executor for Pause a DDL Job. +type PauseDDLJobsExec struct { + *CommandDDLJobsExec +} + +// ResumeDDLJobsExec indicates an Executor for Resume a DDL Job. +type ResumeDDLJobsExec struct { + *CommandDDLJobsExec +} + +// ShowNextRowIDExec represents a show the next row ID executor. +type ShowNextRowIDExec struct { + exec.BaseExecutor + tblName *ast.TableName + done bool +} + +// Next implements the Executor Next interface. +func (e *ShowNextRowIDExec) Next(_ context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + is := domain.GetDomain(e.Ctx()).InfoSchema() + tbl, err := is.TableByName(e.tblName.Schema, e.tblName.Name) + if err != nil { + return err + } + tblMeta := tbl.Meta() + + allocators := tbl.Allocators(e.Ctx()) + for _, alloc := range allocators.Allocs { + nextGlobalID, err := alloc.NextGlobalAutoID() + if err != nil { + return err + } + + var colName, idType string + switch alloc.GetType() { + case autoid.RowIDAllocType: + idType = "_TIDB_ROWID" + if tblMeta.PKIsHandle { + if col := tblMeta.GetAutoIncrementColInfo(); col != nil { + colName = col.Name.O + } + } else { + colName = model.ExtraHandleName.O + } + case autoid.AutoIncrementType: + idType = "AUTO_INCREMENT" + if tblMeta.PKIsHandle { + if col := tblMeta.GetAutoIncrementColInfo(); col != nil { + colName = col.Name.O + } + } else { + colName = model.ExtraHandleName.O + } + case autoid.AutoRandomType: + idType = "AUTO_RANDOM" + colName = tblMeta.GetPkName().O + case autoid.SequenceType: + idType = "SEQUENCE" + colName = "" + default: + return autoid.ErrInvalidAllocatorType.GenWithStackByArgs() + } + + req.AppendString(0, e.tblName.Schema.O) + req.AppendString(1, e.tblName.Name.O) + req.AppendString(2, colName) + req.AppendInt64(3, nextGlobalID) + req.AppendString(4, idType) + } + + e.done = true + return nil +} + +// ShowDDLExec represents a show DDL executor. +type ShowDDLExec struct { + exec.BaseExecutor + + ddlOwnerID string + selfID string + ddlInfo *ddl.Info + done bool +} + +// Next implements the Executor Next interface. +func (e *ShowDDLExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + + ddlJobs := "" + query := "" + l := len(e.ddlInfo.Jobs) + for i, job := range e.ddlInfo.Jobs { + ddlJobs += job.String() + query += job.Query + if i != l-1 { + ddlJobs += "\n" + query += "\n" + } + } + + serverInfo, err := infosync.GetServerInfoByID(ctx, e.ddlOwnerID) + if err != nil { + return err + } + + serverAddress := serverInfo.IP + ":" + + strconv.FormatUint(uint64(serverInfo.Port), 10) + + req.AppendInt64(0, e.ddlInfo.SchemaVer) + req.AppendString(1, e.ddlOwnerID) + req.AppendString(2, serverAddress) + req.AppendString(3, ddlJobs) + req.AppendString(4, e.selfID) + req.AppendString(5, query) + + e.done = true + return nil +} + +// ShowDDLJobsExec represent a show DDL jobs executor. +type ShowDDLJobsExec struct { + exec.BaseExecutor + DDLJobRetriever + + jobNumber int + is infoschema.InfoSchema + sess sessionctx.Context +} + +// DDLJobRetriever retrieve the DDLJobs. +// nolint:structcheck +type DDLJobRetriever struct { + runningJobs []*model.Job + historyJobIter meta.LastJobIterator + cursor int + is infoschema.InfoSchema + activeRoles []*auth.RoleIdentity + cacheJobs []*model.Job + TZLoc *time.Location +} + +func (e *DDLJobRetriever) initial(txn kv.Transaction, sess sessionctx.Context) error { + m := meta.NewMeta(txn) + jobs, err := ddl.GetAllDDLJobs(sess) + if err != nil { + return err + } + e.historyJobIter, err = ddl.GetLastHistoryDDLJobsIterator(m) + if err != nil { + return err + } + e.runningJobs = jobs + e.cursor = 0 + return nil +} + +func (e *DDLJobRetriever) appendJobToChunk(req *chunk.Chunk, job *model.Job, checker privilege.Manager) { + schemaName := job.SchemaName + tableName := "" + finishTS := uint64(0) + if job.BinlogInfo != nil { + finishTS = job.BinlogInfo.FinishedTS + if job.BinlogInfo.TableInfo != nil { + tableName = job.BinlogInfo.TableInfo.Name.L + } + if job.BinlogInfo.MultipleTableInfos != nil { + tablenames := new(strings.Builder) + for i, affect := range job.BinlogInfo.MultipleTableInfos { + if i > 0 { + fmt.Fprintf(tablenames, ",") + } + fmt.Fprintf(tablenames, "%s", affect.Name.L) + } + tableName = tablenames.String() + } + if len(schemaName) == 0 && job.BinlogInfo.DBInfo != nil { + schemaName = job.BinlogInfo.DBInfo.Name.L + } + } + if len(tableName) == 0 { + tableName = job.TableName + } + // For compatibility, the old version of DDL Job wasn't store the schema name and table name. + if len(schemaName) == 0 { + schemaName = getSchemaName(e.is, job.SchemaID) + } + if len(tableName) == 0 { + tableName = getTableName(e.is, job.TableID) + } + + createTime := ts2Time(job.StartTS, e.TZLoc) + startTime := ts2Time(job.RealStartTS, e.TZLoc) + finishTime := ts2Time(finishTS, e.TZLoc) + + // Check the privilege. + if checker != nil && !checker.RequestVerification(e.activeRoles, strings.ToLower(schemaName), strings.ToLower(tableName), "", mysql.AllPrivMask) { + return + } + + req.AppendInt64(0, job.ID) + req.AppendString(1, schemaName) + req.AppendString(2, tableName) + req.AppendString(3, job.Type.String()+showAddIdxReorgTp(job)) + req.AppendString(4, job.SchemaState.String()) + req.AppendInt64(5, job.SchemaID) + req.AppendInt64(6, job.TableID) + req.AppendInt64(7, job.RowCount) + req.AppendTime(8, createTime) + if job.RealStartTS > 0 { + req.AppendTime(9, startTime) + } else { + req.AppendNull(9) + } + if finishTS > 0 { + req.AppendTime(10, finishTime) + } else { + req.AppendNull(10) + } + req.AppendString(11, job.State.String()) + if job.Type == model.ActionMultiSchemaChange { + for _, subJob := range job.MultiSchemaInfo.SubJobs { + req.AppendInt64(0, job.ID) + req.AppendString(1, schemaName) + req.AppendString(2, tableName) + req.AppendString(3, subJob.Type.String()+" /* subjob */"+showAddIdxReorgTpInSubJob(subJob)) + req.AppendString(4, subJob.SchemaState.String()) + req.AppendInt64(5, job.SchemaID) + req.AppendInt64(6, job.TableID) + req.AppendInt64(7, subJob.RowCount) + req.AppendTime(8, createTime) + if subJob.RealStartTS > 0 { + realStartTS := ts2Time(subJob.RealStartTS, e.TZLoc) + req.AppendTime(9, realStartTS) + } else { + req.AppendNull(9) + } + if finishTS > 0 { + req.AppendTime(10, finishTime) + } else { + req.AppendNull(10) + } + req.AppendString(11, subJob.State.String()) + } + } +} + +func showAddIdxReorgTp(job *model.Job) string { + if job.Type == model.ActionAddIndex || job.Type == model.ActionAddPrimaryKey { + if job.ReorgMeta != nil { + tp := job.ReorgMeta.ReorgTp.String() + if len(tp) > 0 { + return " /* " + tp + " */" + } + } + } + return "" +} + +func showAddIdxReorgTpInSubJob(subJob *model.SubJob) string { + if subJob.Type == model.ActionAddIndex || subJob.Type == model.ActionAddPrimaryKey { + tp := subJob.ReorgTp.String() + if len(tp) > 0 { + return " /* " + tp + " */" + } + } + return "" +} + +func ts2Time(timestamp uint64, loc *time.Location) types.Time { + duration := time.Duration(math.Pow10(9-types.DefaultFsp)) * time.Nanosecond + t := model.TSConvert2Time(timestamp) + t.Truncate(duration) + return types.NewTime(types.FromGoTime(t.In(loc)), mysql.TypeDatetime, types.DefaultFsp) +} + +// ShowDDLJobQueriesExec represents a show DDL job queries executor. +// The jobs id that is given by 'admin show ddl job queries' statement, +// only be searched in the latest 10 history jobs. +type ShowDDLJobQueriesExec struct { + exec.BaseExecutor + + cursor int + jobs []*model.Job + jobIDs []int64 +} + +// Open implements the Executor Open interface. +func (e *ShowDDLJobQueriesExec) Open(ctx context.Context) error { + var err error + var jobs []*model.Job + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + session, err := e.GetSysSession() + if err != nil { + return err + } + err = sessiontxn.NewTxn(context.Background(), session) + if err != nil { + return err + } + defer func() { + // ReleaseSysSession will rollbacks txn automatically. + e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) + }() + txn, err := session.Txn(true) + if err != nil { + return err + } + session.GetSessionVars().SetInTxn(true) + + m := meta.NewMeta(txn) + jobs, err = ddl.GetAllDDLJobs(session) + if err != nil { + return err + } + + historyJobs, err := ddl.GetLastNHistoryDDLJobs(m, ddl.DefNumHistoryJobs) + if err != nil { + return err + } + + appendedJobID := make(map[int64]struct{}) + // deduplicate job results + // for situations when this operation happens at the same time with new DDLs being executed + for _, job := range jobs { + if _, ok := appendedJobID[job.ID]; !ok { + appendedJobID[job.ID] = struct{}{} + e.jobs = append(e.jobs, job) + } + } + for _, historyJob := range historyJobs { + if _, ok := appendedJobID[historyJob.ID]; !ok { + appendedJobID[historyJob.ID] = struct{}{} + e.jobs = append(e.jobs, historyJob) + } + } + + return nil +} + +// Next implements the Executor Next interface. +func (e *ShowDDLJobQueriesExec) Next(_ context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if e.cursor >= len(e.jobs) { + return nil + } + if len(e.jobIDs) >= len(e.jobs) { + return nil + } + numCurBatch := mathutil.Min(req.Capacity(), len(e.jobs)-e.cursor) + for _, id := range e.jobIDs { + for i := e.cursor; i < e.cursor+numCurBatch; i++ { + if id == e.jobs[i].ID { + req.AppendString(0, e.jobs[i].Query) + } + } + } + e.cursor += numCurBatch + return nil +} + +// ShowDDLJobQueriesWithRangeExec represents a show DDL job queries with range executor. +// The jobs id that is given by 'admin show ddl job queries' statement, +// can be searched within a specified range in history jobs using offset and limit. +type ShowDDLJobQueriesWithRangeExec struct { + exec.BaseExecutor + + cursor int + jobs []*model.Job + offset uint64 + limit uint64 +} + +// Open implements the Executor Open interface. +func (e *ShowDDLJobQueriesWithRangeExec) Open(ctx context.Context) error { + var err error + var jobs []*model.Job + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + session, err := e.GetSysSession() + if err != nil { + return err + } + err = sessiontxn.NewTxn(context.Background(), session) + if err != nil { + return err + } + defer func() { + // ReleaseSysSession will rollbacks txn automatically. + e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), session) + }() + txn, err := session.Txn(true) + if err != nil { + return err + } + session.GetSessionVars().SetInTxn(true) + + m := meta.NewMeta(txn) + jobs, err = ddl.GetAllDDLJobs(session) + if err != nil { + return err + } + + historyJobs, err := ddl.GetLastNHistoryDDLJobs(m, int(e.offset+e.limit)) + if err != nil { + return err + } + + appendedJobID := make(map[int64]struct{}) + // deduplicate job results + // for situations when this operation happens at the same time with new DDLs being executed + for _, job := range jobs { + if _, ok := appendedJobID[job.ID]; !ok { + appendedJobID[job.ID] = struct{}{} + e.jobs = append(e.jobs, job) + } + } + for _, historyJob := range historyJobs { + if _, ok := appendedJobID[historyJob.ID]; !ok { + appendedJobID[historyJob.ID] = struct{}{} + e.jobs = append(e.jobs, historyJob) + } + } + + if e.cursor < int(e.offset) { + e.cursor = int(e.offset) + } + + return nil +} + +// Next implements the Executor Next interface. +func (e *ShowDDLJobQueriesWithRangeExec) Next(_ context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if e.cursor >= len(e.jobs) { + return nil + } + if int(e.offset) > len(e.jobs) { + return nil + } + numCurBatch := mathutil.Min(req.Capacity(), len(e.jobs)-e.cursor) + for i := e.cursor; i < e.cursor+numCurBatch; i++ { + // i is make true to be >= int(e.offset) + if i >= int(e.offset+e.limit) { + break + } + req.AppendString(0, strconv.FormatInt(e.jobs[i].ID, 10)) + req.AppendString(1, e.jobs[i].Query) + } + e.cursor += numCurBatch + return nil +} + +// Open implements the Executor Open interface. +func (e *ShowDDLJobsExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + e.DDLJobRetriever.is = e.is + if e.jobNumber == 0 { + e.jobNumber = ddl.DefNumHistoryJobs + } + sess, err := e.GetSysSession() + if err != nil { + return err + } + e.sess = sess + err = sessiontxn.NewTxn(context.Background(), sess) + if err != nil { + return err + } + txn, err := sess.Txn(true) + if err != nil { + return err + } + sess.GetSessionVars().SetInTxn(true) + err = e.DDLJobRetriever.initial(txn, sess) + return err +} + +// Next implements the Executor Next interface. +func (e *ShowDDLJobsExec) Next(_ context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if (e.cursor - len(e.runningJobs)) >= e.jobNumber { + return nil + } + count := 0 + + // Append running ddl jobs. + if e.cursor < len(e.runningJobs) { + numCurBatch := mathutil.Min(req.Capacity(), len(e.runningJobs)-e.cursor) + for i := e.cursor; i < e.cursor+numCurBatch; i++ { + e.appendJobToChunk(req, e.runningJobs[i], nil) + } + e.cursor += numCurBatch + count += numCurBatch + } + + // Append history ddl jobs. + var err error + if count < req.Capacity() { + num := req.Capacity() - count + remainNum := e.jobNumber - (e.cursor - len(e.runningJobs)) + num = mathutil.Min(num, remainNum) + e.cacheJobs, err = e.historyJobIter.GetLastJobs(num, e.cacheJobs) + if err != nil { + return err + } + for _, job := range e.cacheJobs { + e.appendJobToChunk(req, job, nil) + } + e.cursor += len(e.cacheJobs) + } + return nil +} + +// Close implements the Executor Close interface. +func (e *ShowDDLJobsExec) Close() error { + e.ReleaseSysSession(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), e.sess) + return e.BaseExecutor.Close() +} + +func getSchemaName(is infoschema.InfoSchema, id int64) string { + var schemaName string + dbInfo, ok := is.SchemaByID(id) + if ok { + schemaName = dbInfo.Name.O + return schemaName + } + + return schemaName +} + +func getTableName(is infoschema.InfoSchema, id int64) string { + var tableName string + table, ok := is.TableByID(id) + if ok { + tableName = table.Meta().Name.O + return tableName + } + + return tableName +} + +// CheckTableExec represents a check table executor. +// It is built from the "admin check table" statement, and it checks if the +// index matches the records in the table. +type CheckTableExec struct { + exec.BaseExecutor + + dbName string + table table.Table + indexInfos []*model.IndexInfo + srcs []*IndexLookUpExecutor + done bool + is infoschema.InfoSchema + exitCh chan struct{} + retCh chan error + checkIndex bool +} + +// Open implements the Executor Open interface. +func (e *CheckTableExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + for _, src := range e.srcs { + if err := src.Open(ctx); err != nil { + return errors.Trace(err) + } + } + e.done = false + return nil +} + +// Close implements the Executor Close interface. +func (e *CheckTableExec) Close() error { + var firstErr error + close(e.exitCh) + for _, src := range e.srcs { + if err := src.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + return firstErr +} + +func (e *CheckTableExec) checkTableIndexHandle(ctx context.Context, idxInfo *model.IndexInfo) error { + // For partition table, there will be multi same index indexLookUpReaders on different partitions. + for _, src := range e.srcs { + if src.index.Name.L == idxInfo.Name.L { + err := e.checkIndexHandle(ctx, src) + if err != nil { + return err + } + } + } + return nil +} + +func (e *CheckTableExec) checkIndexHandle(ctx context.Context, src *IndexLookUpExecutor) error { + cols := src.Schema().Columns + retFieldTypes := make([]*types.FieldType, len(cols)) + for i := range cols { + retFieldTypes[i] = cols[i].RetType + } + chk := chunk.New(retFieldTypes, e.InitCap(), e.MaxChunkSize()) + + var err error + for { + err = exec.Next(ctx, src, chk) + if err != nil { + e.retCh <- errors.Trace(err) + break + } + if chk.NumRows() == 0 { + break + } + } + return errors.Trace(err) +} + +func (e *CheckTableExec) handlePanic(r interface{}) { + if r != nil { + e.retCh <- errors.Errorf("%v", r) + } +} + +// Next implements the Executor Next interface. +func (e *CheckTableExec) Next(ctx context.Context, _ *chunk.Chunk) error { + if e.done || len(e.srcs) == 0 { + return nil + } + defer func() { e.done = true }() + + idxNames := make([]string, 0, len(e.indexInfos)) + for _, idx := range e.indexInfos { + if idx.MVIndex { + continue + } + idxNames = append(idxNames, idx.Name.O) + } + greater, idxOffset, err := admin.CheckIndicesCount(e.Ctx(), e.dbName, e.table.Meta().Name.O, idxNames) + if err != nil { + // For admin check index statement, for speed up and compatibility, doesn't do below checks. + if e.checkIndex { + return errors.Trace(err) + } + if greater == admin.IdxCntGreater { + err = e.checkTableIndexHandle(ctx, e.indexInfos[idxOffset]) + } else if greater == admin.TblCntGreater { + err = e.checkTableRecord(ctx, idxOffset) + } + return errors.Trace(err) + } + + // The number of table rows is equal to the number of index rows. + // TODO: Make the value of concurrency adjustable. And we can consider the number of records. + if len(e.srcs) == 1 { + err = e.checkIndexHandle(ctx, e.srcs[0]) + if err == nil && e.srcs[0].index.MVIndex { + err = e.checkTableRecord(ctx, 0) + } + if err != nil { + return err + } + } + taskCh := make(chan *IndexLookUpExecutor, len(e.srcs)) + failure := atomicutil.NewBool(false) + concurrency := mathutil.Min(3, len(e.srcs)) + var wg util.WaitGroupWrapper + for _, src := range e.srcs { + taskCh <- src + } + for i := 0; i < concurrency; i++ { + wg.Run(func() { + util.WithRecovery(func() { + for { + if fail := failure.Load(); fail { + return + } + select { + case src := <-taskCh: + err1 := e.checkIndexHandle(ctx, src) + if err1 == nil && src.index.MVIndex { + for offset, idx := range e.indexInfos { + if idx.ID == src.index.ID { + err1 = e.checkTableRecord(ctx, offset) + break + } + } + } + if err1 != nil { + failure.Store(true) + logutil.Logger(ctx).Info("check index handle failed", zap.Error(err1)) + return + } + case <-e.exitCh: + return + default: + return + } + } + }, e.handlePanic) + }) + } + wg.Wait() + select { + case err := <-e.retCh: + return errors.Trace(err) + default: + return nil + } +} + +func (e *CheckTableExec) checkTableRecord(ctx context.Context, idxOffset int) error { + idxInfo := e.indexInfos[idxOffset] + txn, err := e.Ctx().Txn(true) + if err != nil { + return err + } + if e.table.Meta().GetPartitionInfo() == nil { + idx := tables.NewIndex(e.table.Meta().ID, e.table.Meta(), idxInfo) + return admin.CheckRecordAndIndex(ctx, e.Ctx(), txn, e.table, idx) + } + + info := e.table.Meta().GetPartitionInfo() + for _, def := range info.Definitions { + pid := def.ID + partition := e.table.(table.PartitionedTable).GetPartition(pid) + idx := tables.NewIndex(def.ID, e.table.Meta(), idxInfo) + if err := admin.CheckRecordAndIndex(ctx, e.Ctx(), txn, partition, idx); err != nil { + return errors.Trace(err) + } + } + return nil +} + +// ShowSlowExec represents the executor of showing the slow queries. +// It is build from the "admin show slow" statement: +// +// admin show slow top [internal | all] N +// admin show slow recent N +type ShowSlowExec struct { + exec.BaseExecutor + + ShowSlow *ast.ShowSlow + result []*domain.SlowQueryInfo + cursor int +} + +// Open implements the Executor Open interface. +func (e *ShowSlowExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + + dom := domain.GetDomain(e.Ctx()) + e.result = dom.ShowSlowQuery(e.ShowSlow) + return nil +} + +// Next implements the Executor Next interface. +func (e *ShowSlowExec) Next(_ context.Context, req *chunk.Chunk) error { + req.Reset() + if e.cursor >= len(e.result) { + return nil + } + + for e.cursor < len(e.result) && req.NumRows() < e.MaxChunkSize() { + slow := e.result[e.cursor] + req.AppendString(0, slow.SQL) + req.AppendTime(1, types.NewTime(types.FromGoTime(slow.Start), mysql.TypeTimestamp, types.MaxFsp)) + req.AppendDuration(2, types.Duration{Duration: slow.Duration, Fsp: types.MaxFsp}) + req.AppendString(3, slow.Detail.String()) + if slow.Succ { + req.AppendInt64(4, 1) + } else { + req.AppendInt64(4, 0) + } + req.AppendUint64(5, slow.ConnID) + req.AppendUint64(6, slow.TxnTS) + req.AppendString(7, slow.User) + req.AppendString(8, slow.DB) + req.AppendString(9, slow.TableIDs) + req.AppendString(10, slow.IndexNames) + if slow.Internal { + req.AppendInt64(11, 1) + } else { + req.AppendInt64(11, 0) + } + req.AppendString(12, slow.Digest) + req.AppendString(13, slow.SessAlias) + e.cursor++ + } + return nil +} + +// SelectLockExec represents a select lock executor. +// It is built from the "SELECT .. FOR UPDATE" or the "SELECT .. LOCK IN SHARE MODE" statement. +// For "SELECT .. FOR UPDATE" statement, it locks every row key from source Executor. +// After the execution, the keys are buffered in transaction, and will be sent to KV +// when doing commit. If there is any key already locked by another transaction, +// the transaction will rollback and retry. +type SelectLockExec struct { + exec.BaseExecutor + + Lock *ast.SelectLockInfo + keys []kv.Key + + // The children may be a join of multiple tables, so we need a map. + tblID2Handle map[int64][]plannercore.HandleCols + + // When SelectLock work on a partition table, we need the partition ID + // (Physical Table ID) instead of the 'logical' table ID to calculate + // the lock KV. In that case, the Physical Table ID is extracted + // from the row key in the store and as an extra column in the chunk row. + + // tblID2PhyTblIDCol is used for partitioned tables. + // The child executor need to return an extra column containing + // the Physical Table ID (i.e. from which partition the row came from) + // Used during building + tblID2PhysTblIDCol map[int64]*expression.Column + + // Used during execution + // Map from logic tableID to column index where the physical table id is stored + // For dynamic prune mode, model.ExtraPhysTblID columns are requested from + // storage and used for physical table id + // For static prune mode, model.ExtraPhysTblID is still sent to storage/Protobuf + // but could be filled in by the partitions TableReaderExecutor + // due to issues with chunk handling between the TableReaderExecutor and the + // SelectReader result. + tblID2PhysTblIDColIdx map[int64]int +} + +// Open implements the Executor Open interface. +func (e *SelectLockExec) Open(ctx context.Context) error { + if len(e.tblID2PhysTblIDCol) > 0 { + e.tblID2PhysTblIDColIdx = make(map[int64]int) + cols := e.Schema().Columns + for i := len(cols) - 1; i >= 0; i-- { + if cols[i].ID == model.ExtraPhysTblID { + for tblID, col := range e.tblID2PhysTblIDCol { + if cols[i].UniqueID == col.UniqueID { + e.tblID2PhysTblIDColIdx[tblID] = i + break + } + } + } + } + } + return e.BaseExecutor.Open(ctx) +} + +// Next implements the Executor Next interface. +func (e *SelectLockExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + err := exec.Next(ctx, e.Children(0), req) + if err != nil { + return err + } + // If there's no handle or it's not a `SELECT FOR UPDATE` statement. + if len(e.tblID2Handle) == 0 || (!plannercore.IsSelectForUpdateLockType(e.Lock.LockType)) { + return nil + } + + if req.NumRows() > 0 { + iter := chunk.NewIterator4Chunk(req) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + for tblID, cols := range e.tblID2Handle { + for _, col := range cols { + handle, err := col.BuildHandle(row) + if err != nil { + return err + } + physTblID := tblID + if physTblColIdx, ok := e.tblID2PhysTblIDColIdx[tblID]; ok { + physTblID = row.GetInt64(physTblColIdx) + if physTblID == 0 { + // select * from t1 left join t2 on t1.c = t2.c for update + // The join right side might be added NULL in left join + // In that case, physTblID is 0, so skip adding the lock. + // + // Note, we can't distinguish whether it's the left join case, + // or a bug that TiKV return without correct physical ID column. + continue + } + } + e.keys = append(e.keys, tablecodec.EncodeRowKeyWithHandle(physTblID, handle)) + } + } + } + return nil + } + lockWaitTime := e.Ctx().GetSessionVars().LockWaitTimeout + if e.Lock.LockType == ast.SelectLockForUpdateNoWait { + lockWaitTime = tikvstore.LockNoWait + } else if e.Lock.LockType == ast.SelectLockForUpdateWaitN { + lockWaitTime = int64(e.Lock.WaitSec) * 1000 + } + + for id := range e.tblID2Handle { + e.UpdateDeltaForTableID(id) + } + lockCtx, err := newLockCtx(e.Ctx(), lockWaitTime, len(e.keys)) + if err != nil { + return err + } + return doLockKeys(ctx, e.Ctx(), lockCtx, e.keys...) +} + +func newLockCtx(sctx sessionctx.Context, lockWaitTime int64, numKeys int) (*tikvstore.LockCtx, error) { + seVars := sctx.GetSessionVars() + forUpdateTS, err := sessiontxn.GetTxnManager(sctx).GetStmtForUpdateTS() + if err != nil { + return nil, err + } + lockCtx := tikvstore.NewLockCtx(forUpdateTS, lockWaitTime, seVars.StmtCtx.GetLockWaitStartTime()) + lockCtx.Killed = &seVars.Killed + lockCtx.PessimisticLockWaited = &seVars.StmtCtx.PessimisticLockWaited + lockCtx.LockKeysDuration = &seVars.StmtCtx.LockKeysDuration + lockCtx.LockKeysCount = &seVars.StmtCtx.LockKeysCount + lockCtx.LockExpired = &seVars.TxnCtx.LockExpire + lockCtx.ResourceGroupTagger = func(req *kvrpcpb.PessimisticLockRequest) []byte { + if req == nil { + return nil + } + if len(req.Mutations) == 0 { + return nil + } + if mutation := req.Mutations[0]; mutation != nil { + label := resourcegrouptag.GetResourceGroupLabelByKey(mutation.Key) + normalized, digest := seVars.StmtCtx.SQLDigest() + if len(normalized) == 0 { + return nil + } + _, planDigest := seVars.StmtCtx.GetPlanDigest() + return resourcegrouptag.EncodeResourceGroupTag(digest, planDigest, label) + } + return nil + } + lockCtx.OnDeadlock = func(deadlock *tikverr.ErrDeadlock) { + cfg := config.GetGlobalConfig() + if deadlock.IsRetryable && !cfg.PessimisticTxn.DeadlockHistoryCollectRetryable { + return + } + rec := deadlockhistory.ErrDeadlockToDeadlockRecord(deadlock) + deadlockhistory.GlobalDeadlockHistory.Push(rec) + } + if lockCtx.ForUpdateTS > 0 && seVars.AssertionLevel != variable.AssertionLevelOff { + lockCtx.InitCheckExistence(numKeys) + } + return lockCtx, nil +} + +// doLockKeys is the main entry for pessimistic lock keys +// waitTime means the lock operation will wait in milliseconds if target key is already +// locked by others. used for (select for update nowait) situation +func doLockKeys(ctx context.Context, se sessionctx.Context, lockCtx *tikvstore.LockCtx, keys ...kv.Key) error { + sessVars := se.GetSessionVars() + sctx := sessVars.StmtCtx + if !sctx.InUpdateStmt && !sctx.InDeleteStmt { + atomic.StoreUint32(&se.GetSessionVars().TxnCtx.ForUpdate, 1) + } + // Lock keys only once when finished fetching all results. + txn, err := se.Txn(true) + if err != nil { + return err + } + + // Skip the temporary table keys. + keys = filterTemporaryTableKeys(sessVars, keys) + + keys = filterLockTableKeys(sessVars.StmtCtx, keys) + var lockKeyStats *tikvutil.LockKeysDetails + ctx = context.WithValue(ctx, tikvutil.LockKeysDetailCtxKey, &lockKeyStats) + err = txn.LockKeys(tikvutil.SetSessionID(ctx, se.GetSessionVars().ConnectionID), lockCtx, keys...) + if lockKeyStats != nil { + sctx.MergeLockKeysExecDetails(lockKeyStats) + } + return err +} + +func filterTemporaryTableKeys(vars *variable.SessionVars, keys []kv.Key) []kv.Key { + txnCtx := vars.TxnCtx + if txnCtx == nil || txnCtx.TemporaryTables == nil { + return keys + } + + newKeys := keys[:0:len(keys)] + for _, key := range keys { + tblID := tablecodec.DecodeTableID(key) + if _, ok := txnCtx.TemporaryTables[tblID]; !ok { + newKeys = append(newKeys, key) + } + } + return newKeys +} + +func filterLockTableKeys(stmtCtx *stmtctx.StatementContext, keys []kv.Key) []kv.Key { + if len(stmtCtx.LockTableIDs) == 0 { + return keys + } + newKeys := keys[:0:len(keys)] + for _, key := range keys { + tblID := tablecodec.DecodeTableID(key) + if _, ok := stmtCtx.LockTableIDs[tblID]; ok { + newKeys = append(newKeys, key) + } + } + return newKeys +} + +// LimitExec represents limit executor +// It ignores 'Offset' rows from src, then returns 'Count' rows at maximum. +type LimitExec struct { + exec.BaseExecutor + + begin uint64 + end uint64 + cursor uint64 + + // meetFirstBatch represents whether we have met the first valid Chunk from child. + meetFirstBatch bool + + childResult *chunk.Chunk + + // columnIdxsUsedByChild keep column indexes of child executor used for inline projection + columnIdxsUsedByChild []int + + // Log the close time when opentracing is enabled. + span opentracing.Span +} + +// Next implements the Executor Next interface. +func (e *LimitExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.cursor >= e.end { + return nil + } + for !e.meetFirstBatch { + // transfer req's requiredRows to childResult and then adjust it in childResult + e.childResult = e.childResult.SetRequiredRows(req.RequiredRows(), e.MaxChunkSize()) + err := exec.Next(ctx, e.Children(0), e.adjustRequiredRows(e.childResult)) + if err != nil { + return err + } + batchSize := uint64(e.childResult.NumRows()) + // no more data. + if batchSize == 0 { + return nil + } + if newCursor := e.cursor + batchSize; newCursor >= e.begin { + e.meetFirstBatch = true + begin, end := e.begin-e.cursor, batchSize + if newCursor > e.end { + end = e.end - e.cursor + } + e.cursor += end + if begin == end { + break + } + if e.columnIdxsUsedByChild != nil { + req.Append(e.childResult.Prune(e.columnIdxsUsedByChild), int(begin), int(end)) + } else { + req.Append(e.childResult, int(begin), int(end)) + } + return nil + } + e.cursor += batchSize + } + e.childResult.Reset() + e.childResult = e.childResult.SetRequiredRows(req.RequiredRows(), e.MaxChunkSize()) + e.adjustRequiredRows(e.childResult) + err := exec.Next(ctx, e.Children(0), e.childResult) + if err != nil { + return err + } + batchSize := uint64(e.childResult.NumRows()) + // no more data. + if batchSize == 0 { + return nil + } + if e.cursor+batchSize > e.end { + e.childResult.TruncateTo(int(e.end - e.cursor)) + batchSize = e.end - e.cursor + } + e.cursor += batchSize + + if e.columnIdxsUsedByChild != nil { + for i, childIdx := range e.columnIdxsUsedByChild { + if err = req.SwapColumn(i, e.childResult, childIdx); err != nil { + return err + } + } + } else { + req.SwapColumns(e.childResult) + } + return nil +} + +// Open implements the Executor Open interface. +func (e *LimitExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + e.childResult = exec.TryNewCacheChunk(e.Children(0)) + e.cursor = 0 + e.meetFirstBatch = e.begin == 0 + if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { + e.span = span + } + return nil +} + +// Close implements the Executor Close interface. +func (e *LimitExec) Close() error { + start := time.Now() + + e.childResult = nil + err := e.BaseExecutor.Close() + + elapsed := time.Since(start) + if elapsed > time.Millisecond { + logutil.BgLogger().Info("limit executor close takes a long time", + zap.Duration("elapsed", elapsed)) + if e.span != nil { + span1 := e.span.Tracer().StartSpan("limitExec.Close", opentracing.ChildOf(e.span.Context()), opentracing.StartTime(start)) + defer span1.Finish() + } + } + return err +} + +func (e *LimitExec) adjustRequiredRows(chk *chunk.Chunk) *chunk.Chunk { + // the limit of maximum number of rows the LimitExec should read + limitTotal := int(e.end - e.cursor) + + var limitRequired int + if e.cursor < e.begin { + // if cursor is less than begin, it have to read (begin-cursor) rows to ignore + // and then read chk.RequiredRows() rows to return, + // so the limit is (begin-cursor)+chk.RequiredRows(). + limitRequired = int(e.begin) - int(e.cursor) + chk.RequiredRows() + } else { + // if cursor is equal or larger than begin, just read chk.RequiredRows() rows to return. + limitRequired = chk.RequiredRows() + } + + return chk.SetRequiredRows(mathutil.Min(limitTotal, limitRequired), e.MaxChunkSize()) +} + +func init() { + // While doing optimization in the plan package, we need to execute uncorrelated subquery, + // but the plan package cannot import the executor package because of the dependency cycle. + // So we assign a function implemented in the executor package to the plan package to avoid the dependency cycle. + plannercore.EvalSubqueryFirstRow = func(ctx context.Context, p plannercore.PhysicalPlan, is infoschema.InfoSchema, sctx sessionctx.Context) ([]types.Datum, error) { + defer func(begin time.Time) { + s := sctx.GetSessionVars() + s.StmtCtx.SetSkipPlanCache(errors.New("query has uncorrelated sub-queries is un-cacheable")) + s.RewritePhaseInfo.PreprocessSubQueries++ + s.RewritePhaseInfo.DurationPreprocessSubQuery += time.Since(begin) + }(time.Now()) + + r, ctx := tracing.StartRegionEx(ctx, "executor.EvalSubQuery") + defer r.End() + + e := newExecutorBuilder(sctx, is, nil) + executor := e.build(p) + if e.err != nil { + return nil, e.err + } + err := executor.Open(ctx) + defer terror.Call(executor.Close) + if err != nil { + return nil, err + } + if pi, ok := sctx.(processinfoSetter); ok { + // Before executing the sub-query, we need update the processinfo to make the progress bar more accurate. + // because the sub-query may take a long time. + pi.UpdateProcessInfo() + } + chk := exec.TryNewCacheChunk(executor) + err = exec.Next(ctx, executor, chk) + if err != nil { + return nil, err + } + if chk.NumRows() == 0 { + return nil, nil + } + row := chk.GetRow(0).GetDatumRow(exec.RetTypes(executor)) + return row, err + } +} + +// TableDualExec represents a dual table executor. +type TableDualExec struct { + exec.BaseExecutor + + // numDualRows can only be 0 or 1. + numDualRows int + numReturned int +} + +// Open implements the Executor Open interface. +func (e *TableDualExec) Open(context.Context) error { + e.numReturned = 0 + return nil +} + +// Next implements the Executor Next interface. +func (e *TableDualExec) Next(_ context.Context, req *chunk.Chunk) error { + req.Reset() + if e.numReturned >= e.numDualRows { + return nil + } + if e.Schema().Len() == 0 { + req.SetNumVirtualRows(1) + } else { + for i := range e.Schema().Columns { + req.AppendNull(i) + } + } + e.numReturned = e.numDualRows + return nil +} + +// SelectionExec represents a filter executor. +type SelectionExec struct { + exec.BaseExecutor + + batched bool + filters []expression.Expression + selected []bool + inputIter *chunk.Iterator4Chunk + inputRow chunk.Row + childResult *chunk.Chunk + + memTracker *memory.Tracker +} + +// Open implements the Executor Open interface. +func (e *SelectionExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + failpoint.Inject("mockSelectionExecBaseExecutorOpenReturnedError", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(errors.New("mock SelectionExec.baseExecutor.Open returned error")) + } + }) + return e.open(ctx) +} + +func (e *SelectionExec) open(context.Context) error { + if e.memTracker != nil { + e.memTracker.Reset() + } else { + e.memTracker = memory.NewTracker(e.ID(), -1) + } + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + e.childResult = exec.TryNewCacheChunk(e.Children(0)) + e.memTracker.Consume(e.childResult.MemoryUsage()) + e.batched = expression.Vectorizable(e.filters) + if e.batched { + e.selected = make([]bool, 0, chunk.InitialCapacity) + } + e.inputIter = chunk.NewIterator4Chunk(e.childResult) + e.inputRow = e.inputIter.End() + return nil +} + +// Close implements plannercore.Plan Close interface. +func (e *SelectionExec) Close() error { + if e.childResult != nil { + e.memTracker.Consume(-e.childResult.MemoryUsage()) + e.childResult = nil + } + e.selected = nil + return e.BaseExecutor.Close() +} + +// Next implements the Executor Next interface. +func (e *SelectionExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + + if !e.batched { + return e.unBatchedNext(ctx, req) + } + + for { + for ; e.inputRow != e.inputIter.End(); e.inputRow = e.inputIter.Next() { + if req.IsFull() { + return nil + } + + if !e.selected[e.inputRow.Idx()] { + continue + } + + req.AppendRow(e.inputRow) + } + mSize := e.childResult.MemoryUsage() + err := exec.Next(ctx, e.Children(0), e.childResult) + e.memTracker.Consume(e.childResult.MemoryUsage() - mSize) + if err != nil { + return err + } + // no more data. + if e.childResult.NumRows() == 0 { + return nil + } + e.selected, err = expression.VectorizedFilter(e.Ctx(), e.filters, e.inputIter, e.selected) + if err != nil { + return err + } + e.inputRow = e.inputIter.Begin() + } +} + +// unBatchedNext filters input rows one by one and returns once an input row is selected. +// For sql with "SETVAR" in filter and "GETVAR" in projection, for example: "SELECT @a FROM t WHERE (@a := 2) > 0", +// we have to set batch size to 1 to do the evaluation of filter and projection. +func (e *SelectionExec) unBatchedNext(ctx context.Context, chk *chunk.Chunk) error { + for { + for ; e.inputRow != e.inputIter.End(); e.inputRow = e.inputIter.Next() { + selected, _, err := expression.EvalBool(e.Ctx(), e.filters, e.inputRow) + if err != nil { + return err + } + if selected { + chk.AppendRow(e.inputRow) + e.inputRow = e.inputIter.Next() + return nil + } + } + mSize := e.childResult.MemoryUsage() + err := exec.Next(ctx, e.Children(0), e.childResult) + e.memTracker.Consume(e.childResult.MemoryUsage() - mSize) + if err != nil { + return err + } + e.inputRow = e.inputIter.Begin() + // no more data. + if e.childResult.NumRows() == 0 { + return nil + } + } +} + +// TableScanExec is a table scan executor without result fields. +type TableScanExec struct { + exec.BaseExecutor + + t table.Table + columns []*model.ColumnInfo + virtualTableChunkList *chunk.List + virtualTableChunkIdx int +} + +// Next implements the Executor Next interface. +func (e *TableScanExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + return e.nextChunk4InfoSchema(ctx, req) +} + +func (e *TableScanExec) nextChunk4InfoSchema(ctx context.Context, chk *chunk.Chunk) error { + chk.GrowAndReset(e.MaxChunkSize()) + if e.virtualTableChunkList == nil { + e.virtualTableChunkList = chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) + columns := make([]*table.Column, e.Schema().Len()) + for i, colInfo := range e.columns { + columns[i] = table.ToColumn(colInfo) + } + mutableRow := chunk.MutRowFromTypes(exec.RetTypes(e)) + type tableIter interface { + IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error + } + err := (e.t.(tableIter)).IterRecords(ctx, e.Ctx(), columns, func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { + mutableRow.SetDatums(rec...) + e.virtualTableChunkList.AppendRow(mutableRow.ToRow()) + return true, nil + }) + if err != nil { + return err + } + } + // no more data. + if e.virtualTableChunkIdx >= e.virtualTableChunkList.NumChunks() { + return nil + } + virtualTableChunk := e.virtualTableChunkList.GetChunk(e.virtualTableChunkIdx) + e.virtualTableChunkIdx++ + chk.SwapColumns(virtualTableChunk) + return nil +} + +// Open implements the Executor Open interface. +func (e *TableScanExec) Open(context.Context) error { + e.virtualTableChunkList = nil + return nil +} + +// MaxOneRowExec checks if the number of rows that a query returns is at maximum one. +// It's built from subquery expression. +type MaxOneRowExec struct { + exec.BaseExecutor + + evaluated bool +} + +// Open implements the Executor Open interface. +func (e *MaxOneRowExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + e.evaluated = false + return nil +} + +// Next implements the Executor Next interface. +func (e *MaxOneRowExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.evaluated { + return nil + } + e.evaluated = true + err := exec.Next(ctx, e.Children(0), req) + if err != nil { + return err + } + + if num := req.NumRows(); num == 0 { + for i := range e.Schema().Columns { + req.AppendNull(i) + } + return nil + } else if num != 1 { + return exeerrors.ErrSubqueryMoreThan1Row + } + + childChunk := exec.TryNewCacheChunk(e.Children(0)) + err = exec.Next(ctx, e.Children(0), childChunk) + if err != nil { + return err + } + if childChunk.NumRows() != 0 { + return exeerrors.ErrSubqueryMoreThan1Row + } + + return nil +} + +// UnionExec pulls all it's children's result and returns to its parent directly. +// A "resultPuller" is started for every child to pull result from that child and push it to the "resultPool", the used +// "Chunk" is obtained from the corresponding "resourcePool". All resultPullers are running concurrently. +// +// +----------------+ +// +---> resourcePool 1 ---> | resultPuller 1 |-----+ +// | +----------------+ | +// | | +// | +----------------+ v +// +---> resourcePool 2 ---> | resultPuller 2 |-----> resultPool ---+ +// | +----------------+ ^ | +// | ...... | | +// | +----------------+ | | +// +---> resourcePool n ---> | resultPuller n |-----+ | +// | +----------------+ | +// | | +// | +-------------+ | +// |--------------------------| main thread | <---------------------+ +// +-------------+ +type UnionExec struct { + exec.BaseExecutor + concurrency int + childIDChan chan int + + stopFetchData atomic.Value + + finished chan struct{} + resourcePools []chan *chunk.Chunk + resultPool chan *unionWorkerResult + + results []*chunk.Chunk + wg sync.WaitGroup + initialized bool + mu struct { + *syncutil.Mutex + maxOpenedChildID int + } + + childInFlightForTest int32 +} + +// unionWorkerResult stores the result for a union worker. +// A "resultPuller" is started for every child to pull result from that child, unionWorkerResult is used to store that pulled result. +// "src" is used for Chunk reuse: after pulling result from "resultPool", main-thread must push a valid unused Chunk to "src" to +// enable the corresponding "resultPuller" continue to work. +type unionWorkerResult struct { + chk *chunk.Chunk + err error + src chan<- *chunk.Chunk +} + +func (e *UnionExec) waitAllFinished() { + e.wg.Wait() + close(e.resultPool) +} + +// Open implements the Executor Open interface. +func (e *UnionExec) Open(context.Context) error { + e.stopFetchData.Store(false) + e.initialized = false + e.finished = make(chan struct{}) + e.mu.Mutex = &syncutil.Mutex{} + e.mu.maxOpenedChildID = -1 + return nil +} + +func (e *UnionExec) initialize(ctx context.Context) { + if e.concurrency > e.ChildrenLen() { + e.concurrency = e.ChildrenLen() + } + for i := 0; i < e.concurrency; i++ { + e.results = append(e.results, exec.NewFirstChunk(e.Children(0))) + } + e.resultPool = make(chan *unionWorkerResult, e.concurrency) + e.resourcePools = make([]chan *chunk.Chunk, e.concurrency) + e.childIDChan = make(chan int, e.ChildrenLen()) + for i := 0; i < e.concurrency; i++ { + e.resourcePools[i] = make(chan *chunk.Chunk, 1) + e.resourcePools[i] <- e.results[i] + e.wg.Add(1) + go e.resultPuller(ctx, i) + } + for i := 0; i < e.ChildrenLen(); i++ { + e.childIDChan <- i + } + close(e.childIDChan) + go e.waitAllFinished() +} + +func (e *UnionExec) resultPuller(ctx context.Context, workerID int) { + result := &unionWorkerResult{ + err: nil, + chk: nil, + src: e.resourcePools[workerID], + } + defer func() { + if r := recover(); r != nil { + logutil.Logger(ctx).Error("resultPuller panicked", zap.Any("recover", r), zap.Stack("stack")) + result.err = errors.Errorf("%v", r) + e.resultPool <- result + e.stopFetchData.Store(true) + } + e.wg.Done() + }() + for childID := range e.childIDChan { + e.mu.Lock() + if childID > e.mu.maxOpenedChildID { + e.mu.maxOpenedChildID = childID + } + e.mu.Unlock() + if err := e.Children(childID).Open(ctx); err != nil { + result.err = err + e.stopFetchData.Store(true) + e.resultPool <- result + } + failpoint.Inject("issue21441", func() { + atomic.AddInt32(&e.childInFlightForTest, 1) + }) + for { + if e.stopFetchData.Load().(bool) { + return + } + select { + case <-e.finished: + return + case result.chk = <-e.resourcePools[workerID]: + } + result.err = exec.Next(ctx, e.Children(childID), result.chk) + if result.err == nil && result.chk.NumRows() == 0 { + e.resourcePools[workerID] <- result.chk + break + } + failpoint.Inject("issue21441", func() { + if int(atomic.LoadInt32(&e.childInFlightForTest)) > e.concurrency { + panic("the count of child in flight is larger than e.concurrency unexpectedly") + } + }) + e.resultPool <- result + if result.err != nil { + e.stopFetchData.Store(true) + return + } + } + failpoint.Inject("issue21441", func() { + atomic.AddInt32(&e.childInFlightForTest, -1) + }) + } +} + +// Next implements the Executor Next interface. +func (e *UnionExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if !e.initialized { + e.initialize(ctx) + e.initialized = true + } + result, ok := <-e.resultPool + if !ok { + return nil + } + if result.err != nil { + return errors.Trace(result.err) + } + + if result.chk.NumCols() != req.NumCols() { + return errors.Errorf("Internal error: UnionExec chunk column count mismatch, req: %d, result: %d", + req.NumCols(), result.chk.NumCols()) + } + req.SwapColumns(result.chk) + result.src <- result.chk + return nil +} + +// Close implements the Executor Close interface. +func (e *UnionExec) Close() error { + if e.finished != nil { + close(e.finished) + } + e.results = nil + if e.resultPool != nil { + channel.Clear(e.resultPool) + } + e.resourcePools = nil + if e.childIDChan != nil { + channel.Clear(e.childIDChan) + } + // We do not need to acquire the e.mu.Lock since all the resultPuller can be + // promised to exit when reaching here (e.childIDChan been closed). + var firstErr error + for i := 0; i <= e.mu.maxOpenedChildID; i++ { + if err := e.Children(i).Close(); err != nil && firstErr == nil { + firstErr = err + } + } + return firstErr +} + +// ResetContextOfStmt resets the StmtContext and session variables. +// Before every execution, we must clear statement context. +func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) { + vars := ctx.GetSessionVars() + for name, val := range vars.StmtCtx.SetVarHintRestore { + err := vars.SetSystemVar(name, val) + if err != nil { + logutil.BgLogger().Warn("Failed to restore the variable after SET_VAR hint", zap.String("variable name", name), zap.String("expected value", val)) + } + } + vars.StmtCtx.SetVarHintRestore = nil + var sc *stmtctx.StatementContext + if vars.TxnCtx.CouldRetry || mysql.HasCursorExistsFlag(vars.Status) { + // Must construct new statement context object, the retry history need context for every statement. + // TODO: Maybe one day we can get rid of transaction retry, then this logic can be deleted. + sc = stmtctx.NewStmtCtx() + } else { + sc = vars.InitStatementContext() + } + sc.SetTimeZone(vars.Location()) + sc.TaskID = stmtctx.AllocateTaskID() + sc.CTEStorageMap = map[int]*CTEStorages{} + sc.IsStaleness = false + sc.LockTableIDs = make(map[int64]struct{}) + sc.EnableOptimizeTrace = false + sc.OptimizeTracer = nil + sc.OptimizerCETrace = nil + sc.IsSyncStatsFailed = false + sc.IsExplainAnalyzeDML = false + // Firstly we assume that UseDynamicPruneMode can be enabled according session variable, then we will check other conditions + // in PlanBuilder.buildDataSource + if ctx.GetSessionVars().IsDynamicPartitionPruneEnabled() { + sc.UseDynamicPruneMode = true + } else { + sc.UseDynamicPruneMode = false + } + + sc.StatsLoad.Timeout = 0 + sc.StatsLoad.NeededItems = nil + sc.StatsLoad.ResultCh = nil + + sc.SysdateIsNow = ctx.GetSessionVars().SysdateIsNow + + vars.MemTracker.Detach() + vars.MemTracker.UnbindActions() + vars.MemTracker.SetBytesLimit(vars.MemQuotaQuery) + vars.MemTracker.ResetMaxConsumed() + vars.DiskTracker.Detach() + vars.DiskTracker.ResetMaxConsumed() + vars.MemTracker.SessionID.Store(vars.ConnectionID) + vars.StmtCtx.TableStats = make(map[int64]interface{}) + + isAnalyze := false + if execStmt, ok := s.(*ast.ExecuteStmt); ok { + prepareStmt, err := plannercore.GetPreparedStmt(execStmt, vars) + if err != nil { + return err + } + _, isAnalyze = prepareStmt.PreparedAst.Stmt.(*ast.AnalyzeTableStmt) + } else if _, ok := s.(*ast.AnalyzeTableStmt); ok { + isAnalyze = true + } + if isAnalyze { + sc.InitMemTracker(memory.LabelForAnalyzeMemory, -1) + vars.MemTracker.SetBytesLimit(-1) + vars.MemTracker.AttachTo(GlobalAnalyzeMemoryTracker) + } else { + sc.InitMemTracker(memory.LabelForSQLText, -1) + } + logOnQueryExceedMemQuota := domain.GetDomain(ctx).ExpensiveQueryHandle().LogOnQueryExceedMemQuota + switch variable.OOMAction.Load() { + case variable.OOMActionCancel: + action := &memory.PanicOnExceed{ConnID: vars.ConnectionID} + action.SetLogHook(logOnQueryExceedMemQuota) + vars.MemTracker.SetActionOnExceed(action) + case variable.OOMActionLog: + fallthrough + default: + action := &memory.LogOnExceed{ConnID: vars.ConnectionID} + action.SetLogHook(logOnQueryExceedMemQuota) + vars.MemTracker.SetActionOnExceed(action) + } + sc.MemTracker.SessionID.Store(vars.ConnectionID) + sc.MemTracker.AttachTo(vars.MemTracker) + sc.InitDiskTracker(memory.LabelForSQLText, -1) + globalConfig := config.GetGlobalConfig() + if variable.EnableTmpStorageOnOOM.Load() && sc.DiskTracker != nil { + sc.DiskTracker.AttachTo(vars.DiskTracker) + if GlobalDiskUsageTracker != nil { + vars.DiskTracker.AttachTo(GlobalDiskUsageTracker) + } + } + if execStmt, ok := s.(*ast.ExecuteStmt); ok { + prepareStmt, err := plannercore.GetPreparedStmt(execStmt, vars) + if err != nil { + return err + } + s = prepareStmt.PreparedAst.Stmt + sc.InitSQLDigest(prepareStmt.NormalizedSQL, prepareStmt.SQLDigest) + // For `execute stmt` SQL, should reset the SQL digest with the prepare SQL digest. + goCtx := context.Background() + if variable.EnablePProfSQLCPU.Load() && len(prepareStmt.NormalizedSQL) > 0 { + goCtx = pprof.WithLabels(goCtx, pprof.Labels("sql", FormatSQL(prepareStmt.NormalizedSQL).String())) + pprof.SetGoroutineLabels(goCtx) + } + if topsqlstate.TopSQLEnabled() && prepareStmt.SQLDigest != nil { + sc.IsSQLRegistered.Store(true) + topsql.AttachAndRegisterSQLInfo(goCtx, prepareStmt.NormalizedSQL, prepareStmt.SQLDigest, vars.InRestrictedSQL) + } + if s, ok := prepareStmt.PreparedAst.Stmt.(*ast.SelectStmt); ok { + if s.LockInfo == nil { + sc.WeakConsistency = isWeakConsistencyRead(ctx, execStmt) + } + } + } + // execute missed stmtID uses empty sql + sc.OriginalSQL = s.Text() + if explainStmt, ok := s.(*ast.ExplainStmt); ok { + sc.InExplainStmt = true + sc.ExplainFormat = explainStmt.Format + sc.InExplainAnalyzeStmt = explainStmt.Analyze + sc.IgnoreExplainIDSuffix = strings.ToLower(explainStmt.Format) == types.ExplainFormatBrief + sc.InVerboseExplain = strings.ToLower(explainStmt.Format) == types.ExplainFormatVerbose + s = explainStmt.Stmt + } else { + sc.ExplainFormat = "" + } + if explainForStmt, ok := s.(*ast.ExplainForStmt); ok { + sc.InExplainStmt = true + sc.InExplainAnalyzeStmt = true + sc.InVerboseExplain = strings.ToLower(explainForStmt.Format) == types.ExplainFormatVerbose + } + + // TODO: Many same bool variables here. + // We should set only two variables ( + // IgnoreErr and StrictSQLMode) to avoid setting the same bool variables and + // pushing them down to TiKV as flags. + + sc.InRestrictedSQL = vars.InRestrictedSQL + switch stmt := s.(type) { + case *ast.UpdateStmt: + ResetUpdateStmtCtx(sc, stmt, vars) + case *ast.DeleteStmt: + ResetDeleteStmtCtx(sc, stmt, vars) + case *ast.InsertStmt: + sc.InInsertStmt = true + // For insert statement (not for update statement), disabling the StrictSQLMode + // should make TruncateAsWarning and DividedByZeroAsWarning, + // but should not make DupKeyAsWarning. + sc.DupKeyAsWarning = stmt.IgnoreErr + sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.IgnoreNoPartition = stmt.IgnoreErr + sc.ErrAutoincReadFailedAsWarning = stmt.IgnoreErr + sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate + sc.Priority = stmt.Priority + case *ast.CreateTableStmt, *ast.AlterTableStmt: + sc.InCreateOrAlterStmt = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.StrictSQLMode || sc.AllowInvalidDate + sc.NoZeroDate = vars.SQLMode.HasNoZeroDateMode() + sc.TruncateAsWarning = !vars.StrictSQLMode + case *ast.LoadDataStmt: + sc.InLoadDataStmt = true + // return warning instead of error when load data meet no partition for value + sc.IgnoreNoPartition = true + case *ast.SelectStmt: + sc.InSelectStmt = true + + // see https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sql-mode-strict + // said "For statements such as SELECT that do not change data, invalid values + // generate a warning in strict mode, not an error." + // and https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html + sc.OverflowAsWarning = true + + // Return warning for truncate error in selection. + sc.TruncateAsWarning = true + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + if opts := stmt.SelectStmtOpts; opts != nil { + sc.Priority = opts.Priority + sc.NotFillCache = !opts.SQLCache + } + sc.WeakConsistency = isWeakConsistencyRead(ctx, stmt) + case *ast.SetOprStmt: + sc.InSelectStmt = true + sc.OverflowAsWarning = true + sc.TruncateAsWarning = true + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + case *ast.ShowStmt: + sc.IgnoreTruncate.Store(true) + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + if stmt.Tp == ast.ShowWarnings || stmt.Tp == ast.ShowErrors || stmt.Tp == ast.ShowSessionStates { + sc.InShowWarning = true + sc.SetWarnings(vars.StmtCtx.GetWarnings()) + } + case *ast.SplitRegionStmt: + sc.IgnoreTruncate.Store(false) + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + case *ast.SetSessionStatesStmt: + sc.InSetSessionStatesStmt = true + sc.IgnoreTruncate.Store(true) + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + default: + sc.IgnoreTruncate.Store(true) + sc.IgnoreZeroInDate = true + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + } + + sc.UpdateTypeFlags(func(flags types.Flags) types.Flags { + return flags. + WithSkipUTF8Check(vars.SkipUTF8Check). + WithSkipSACIICheck(vars.SkipASCIICheck). + WithSkipUTF8MB4Check(!globalConfig.Instance.CheckMb4ValueInUTF8.Load()) + }) + + vars.PlanCacheParams.Reset() + if priority := mysql.PriorityEnum(atomic.LoadInt32(&variable.ForcePriority)); priority != mysql.NoPriority { + sc.Priority = priority + } + if vars.StmtCtx.LastInsertID > 0 { + sc.PrevLastInsertID = vars.StmtCtx.LastInsertID + } else { + sc.PrevLastInsertID = vars.StmtCtx.PrevLastInsertID + } + sc.PrevAffectedRows = 0 + if vars.StmtCtx.InUpdateStmt || vars.StmtCtx.InDeleteStmt || vars.StmtCtx.InInsertStmt || vars.StmtCtx.InSetSessionStatesStmt { + sc.PrevAffectedRows = int64(vars.StmtCtx.AffectedRows()) + } else if vars.StmtCtx.InSelectStmt { + sc.PrevAffectedRows = -1 + } + if globalConfig.Instance.EnableCollectExecutionInfo.Load() { + // In ExplainFor case, RuntimeStatsColl should not be reset for reuse, + // because ExplainFor need to display the last statement information. + reuseObj := vars.StmtCtx.RuntimeStatsColl + if _, ok := s.(*ast.ExplainForStmt); ok { + reuseObj = nil + } + sc.RuntimeStatsColl = execdetails.NewRuntimeStatsColl(reuseObj) + } + + sc.TblInfo2UnionScan = make(map[*model.TableInfo]bool) + errCount, warnCount := vars.StmtCtx.NumErrorWarnings() + vars.SysErrorCount = errCount + vars.SysWarningCount = warnCount + vars.ExchangeChunkStatus() + vars.StmtCtx = sc + vars.PrevFoundInPlanCache = vars.FoundInPlanCache + vars.FoundInPlanCache = false + vars.ClearStmtVars() + vars.PrevFoundInBinding = vars.FoundInBinding + vars.FoundInBinding = false + vars.DurationWaitTS = 0 + vars.CurrInsertBatchExtraCols = nil + vars.CurrInsertValues = chunk.Row{} + + return +} + +// ResetUpdateStmtCtx resets statement context for UpdateStmt. +func ResetUpdateStmtCtx(sc *stmtctx.StatementContext, stmt *ast.UpdateStmt, vars *variable.SessionVars) { + sc.InUpdateStmt = true + sc.DupKeyAsWarning = stmt.IgnoreErr + sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate + sc.Priority = stmt.Priority + sc.IgnoreNoPartition = stmt.IgnoreErr +} + +// ResetDeleteStmtCtx resets statement context for DeleteStmt. +func ResetDeleteStmtCtx(sc *stmtctx.StatementContext, stmt *ast.DeleteStmt, vars *variable.SessionVars) { + sc.InDeleteStmt = true + sc.DupKeyAsWarning = stmt.IgnoreErr + sc.BadNullAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.TruncateAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.DividedByZeroAsWarning = !vars.StrictSQLMode || stmt.IgnoreErr + sc.AllowInvalidDate = vars.SQLMode.HasAllowInvalidDatesMode() + sc.IgnoreZeroInDate = !vars.SQLMode.HasNoZeroInDateMode() || !vars.SQLMode.HasNoZeroDateMode() || !vars.StrictSQLMode || stmt.IgnoreErr || sc.AllowInvalidDate + sc.Priority = stmt.Priority +} + +func setOptionForTopSQL(sc *stmtctx.StatementContext, snapshot kv.Snapshot) { + if snapshot == nil { + return + } + snapshot.SetOption(kv.ResourceGroupTagger, sc.GetResourceGroupTagger()) + if sc.KvExecCounter != nil { + snapshot.SetOption(kv.RPCInterceptor, sc.KvExecCounter.RPCInterceptor()) + } +} + +func isWeakConsistencyRead(ctx sessionctx.Context, node ast.Node) bool { + sessionVars := ctx.GetSessionVars() + return sessionVars.ConnectionID > 0 && sessionVars.ReadConsistency.IsWeak() && + plannercore.IsAutoCommitTxn(ctx) && plannercore.IsReadOnly(node, sessionVars) +} + +// FastCheckTableExec represents a check table executor. +// It is built from the "admin check table" statement, and it checks if the +// index matches the records in the table. +// It uses a new algorithms to check table data, which is faster than the old one(CheckTableExec). +type FastCheckTableExec struct { + exec.BaseExecutor + + dbName string + table table.Table + indexInfos []*model.IndexInfo + done bool + is infoschema.InfoSchema + err *atomic.Pointer[error] + wg sync.WaitGroup + contextCtx context.Context +} + +// Open implements the Executor Open interface. +func (e *FastCheckTableExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + return err + } + + e.done = false + e.contextCtx = ctx + return nil +} + +type checkIndexTask struct { + indexOffset int +} + +type checkIndexWorker struct { + sctx sessionctx.Context + dbName string + table table.Table + indexInfos []*model.IndexInfo + e *FastCheckTableExec +} + +type groupByChecksum struct { + bucket uint64 + checksum uint64 + count int64 +} + +func getCheckSum(ctx context.Context, se sessionctx.Context, sql string) ([]groupByChecksum, error) { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnAdmin) + rs, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) + if err != nil { + return nil, err + } + defer func(rs sqlexec.RecordSet) { + err := rs.Close() + if err != nil { + logutil.BgLogger().Error("close record set failed", zap.Error(err)) + } + }(rs) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 256) + if err != nil { + return nil, err + } + checksums := make([]groupByChecksum, 0, len(rows)) + for _, row := range rows { + checksums = append(checksums, groupByChecksum{bucket: row.GetUint64(1), checksum: row.GetUint64(0), count: row.GetInt64(2)}) + } + return checksums, nil +} + +// HandleTask implements the Worker interface. +func (w *checkIndexWorker) HandleTask(task checkIndexTask, _ func(workerpool.None)) { + defer w.e.wg.Done() + idxInfo := w.indexInfos[task.indexOffset] + bucketSize := int(CheckTableFastBucketSize.Load()) + + ctx := kv.WithInternalSourceType(w.e.contextCtx, kv.InternalTxnAdmin) + + trySaveErr := func(err error) { + w.e.err.CompareAndSwap(nil, &err) + } + + se, err := w.e.Base().GetSysSession() + if err != nil { + trySaveErr(err) + return + } + se.GetSessionVars().OptimizerUseInvisibleIndexes = true + defer func() { + se.GetSessionVars().OptimizerUseInvisibleIndexes = false + w.e.Base().ReleaseSysSession(ctx, se) + }() + + var pkCols []string + var pkTypes []*types.FieldType + switch { + case w.e.table.Meta().IsCommonHandle: + pkColsInfo := w.e.table.Meta().GetPrimaryKey().Columns + for _, colInfo := range pkColsInfo { + colStr := colInfo.Name.O + pkCols = append(pkCols, colStr) + pkTypes = append(pkTypes, &w.e.table.Meta().Columns[colInfo.Offset].FieldType) + } + case w.e.table.Meta().PKIsHandle: + pkCols = append(pkCols, w.e.table.Meta().GetPkName().O) + default: // support decoding _tidb_rowid. + pkCols = append(pkCols, model.ExtraHandleName.O) + } + + // CheckSum of (handle + index columns). + var md5HandleAndIndexCol strings.Builder + md5HandleAndIndexCol.WriteString("crc32(md5(concat_ws(0x2, ") + for _, col := range pkCols { + md5HandleAndIndexCol.WriteString(ColumnName(col)) + md5HandleAndIndexCol.WriteString(", ") + } + for offset, col := range idxInfo.Columns { + tblCol := w.table.Meta().Columns[col.Offset] + if tblCol.IsGenerated() && !tblCol.GeneratedStored { + md5HandleAndIndexCol.WriteString(tblCol.GeneratedExprString) + } else { + md5HandleAndIndexCol.WriteString(ColumnName(col.Name.O)) + } + if offset != len(idxInfo.Columns)-1 { + md5HandleAndIndexCol.WriteString(", ") + } + } + md5HandleAndIndexCol.WriteString(")))") + + // Used to group by and order. + var md5Handle strings.Builder + md5Handle.WriteString("crc32(md5(concat_ws(0x2, ") + for i, col := range pkCols { + md5Handle.WriteString(ColumnName(col)) + if i != len(pkCols)-1 { + md5Handle.WriteString(", ") + } + } + md5Handle.WriteString(")))") + + handleColumnField := strings.Join(pkCols, ", ") + var indexColumnField strings.Builder + for offset, col := range idxInfo.Columns { + indexColumnField.WriteString(ColumnName(col.Name.O)) + if offset != len(idxInfo.Columns)-1 { + indexColumnField.WriteString(", ") + } + } + + tableRowCntToCheck := int64(0) + + offset := 0 + mod := 1 + meetError := false + + lookupCheckThreshold := int64(100) + checkOnce := false + + if w.e.Ctx().GetSessionVars().SnapshotTS != 0 { + se.GetSessionVars().SnapshotTS = w.e.Ctx().GetSessionVars().SnapshotTS + defer func() { + se.GetSessionVars().SnapshotTS = 0 + }() + } + _, err = se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "begin") + if err != nil { + trySaveErr(err) + return + } + + times := 0 + const maxTimes = 10 + for tableRowCntToCheck > lookupCheckThreshold || !checkOnce { + times++ + if times == maxTimes { + logutil.BgLogger().Warn("compare checksum by group reaches time limit", zap.Int("times", times)) + break + } + whereKey := fmt.Sprintf("((cast(%s as signed) - %d) %% %d)", md5Handle.String(), offset, mod) + groupByKey := fmt.Sprintf("((cast(%s as signed) - %d) div %d %% %d)", md5Handle.String(), offset, mod, bucketSize) + if !checkOnce { + whereKey = "0" + } + checkOnce = true + + tblQuery := fmt.Sprintf("select /*+ read_from_storage(tikv[%s]) */ bit_xor(%s), %s, count(*) from %s use index() where %s = 0 group by %s", TableName(w.e.dbName, w.e.table.Meta().Name.String()), md5HandleAndIndexCol.String(), groupByKey, TableName(w.e.dbName, w.e.table.Meta().Name.String()), whereKey, groupByKey) + idxQuery := fmt.Sprintf("select bit_xor(%s), %s, count(*) from %s use index(`%s`) where %s = 0 group by %s", md5HandleAndIndexCol.String(), groupByKey, TableName(w.e.dbName, w.e.table.Meta().Name.String()), idxInfo.Name, whereKey, groupByKey) + + logutil.BgLogger().Info("fast check table by group", zap.String("table name", w.table.Meta().Name.String()), zap.String("index name", idxInfo.Name.String()), zap.Int("times", times), zap.Int("current offset", offset), zap.Int("current mod", mod), zap.String("table sql", tblQuery), zap.String("index sql", idxQuery)) + + // compute table side checksum. + tableChecksum, err := getCheckSum(w.e.contextCtx, se, tblQuery) + if err != nil { + trySaveErr(err) + return + } + slices.SortFunc(tableChecksum, func(i, j groupByChecksum) int { + return cmp.Compare(i.bucket, j.bucket) + }) + + // compute index side checksum. + indexChecksum, err := getCheckSum(w.e.contextCtx, se, idxQuery) + if err != nil { + trySaveErr(err) + return + } + slices.SortFunc(indexChecksum, func(i, j groupByChecksum) int { + return cmp.Compare(i.bucket, j.bucket) + }) + + currentOffset := 0 + + // Every checksum in table side should be the same as the index side. + i := 0 + for i < len(tableChecksum) && i < len(indexChecksum) { + if tableChecksum[i].bucket != indexChecksum[i].bucket || tableChecksum[i].checksum != indexChecksum[i].checksum { + if tableChecksum[i].bucket <= indexChecksum[i].bucket { + currentOffset = int(tableChecksum[i].bucket) + tableRowCntToCheck = tableChecksum[i].count + } else { + currentOffset = int(indexChecksum[i].bucket) + tableRowCntToCheck = indexChecksum[i].count + } + meetError = true + break + } + i++ + } + + if !meetError && i < len(indexChecksum) && i == len(tableChecksum) { + // Table side has fewer buckets. + currentOffset = int(indexChecksum[i].bucket) + tableRowCntToCheck = indexChecksum[i].count + meetError = true + } else if !meetError && i < len(tableChecksum) && i == len(indexChecksum) { + // Index side has fewer buckets. + currentOffset = int(tableChecksum[i].bucket) + tableRowCntToCheck = tableChecksum[i].count + meetError = true + } + + if !meetError { + if times != 1 { + logutil.BgLogger().Error("unexpected result, no error detected in this round, but an error is detected in the previous round", zap.Int("times", times), zap.Int("offset", offset), zap.Int("mod", mod)) + } + break + } + + offset += currentOffset * mod + mod *= bucketSize + } + + queryToRow := func(se sessionctx.Context, sql string) ([]chunk.Row, error) { + rs, err := se.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) + if err != nil { + return nil, err + } + row, err := sqlexec.DrainRecordSet(ctx, rs, 4096) + if err != nil { + return nil, err + } + err = rs.Close() + if err != nil { + logutil.BgLogger().Warn("close result set failed", zap.Error(err)) + } + return row, nil + } + + if meetError { + groupByKey := fmt.Sprintf("((cast(%s as signed) - %d) %% %d)", md5Handle.String(), offset, mod) + indexSQL := fmt.Sprintf("select %s, %s, %s from %s use index(`%s`) where %s = 0 order by %s", handleColumnField, indexColumnField.String(), md5HandleAndIndexCol.String(), TableName(w.e.dbName, w.e.table.Meta().Name.String()), idxInfo.Name, groupByKey, handleColumnField) + tableSQL := fmt.Sprintf("select /*+ read_from_storage(tikv[%s]) */ %s, %s, %s from %s use index() where %s = 0 order by %s", TableName(w.e.dbName, w.e.table.Meta().Name.String()), handleColumnField, indexColumnField.String(), md5HandleAndIndexCol.String(), TableName(w.e.dbName, w.e.table.Meta().Name.String()), groupByKey, handleColumnField) + + idxRow, err := queryToRow(se, indexSQL) + if err != nil { + trySaveErr(err) + return + } + tblRow, err := queryToRow(se, tableSQL) + if err != nil { + trySaveErr(err) + return + } + + getHandleFromRow := func(row chunk.Row) (kv.Handle, error) { + handleDatum := make([]types.Datum, 0) + for i, t := range pkTypes { + handleDatum = append(handleDatum, row.GetDatum(i, t)) + } + if w.table.Meta().IsCommonHandle { + handleBytes, err := codec.EncodeKey(w.sctx.GetSessionVars().StmtCtx, nil, handleDatum...) + if err != nil { + return nil, err + } + return kv.NewCommonHandle(handleBytes) + } + return kv.IntHandle(row.GetInt64(0)), nil + } + getValueFromRow := func(row chunk.Row) ([]types.Datum, error) { + valueDatum := make([]types.Datum, 0) + for i, t := range idxInfo.Columns { + valueDatum = append(valueDatum, row.GetDatum(i+len(pkCols), &w.table.Meta().Columns[t.Offset].FieldType)) + } + return valueDatum, nil + } + + ir := func() *consistency.Reporter { + return &consistency.Reporter{ + HandleEncode: func(handle kv.Handle) kv.Key { + return tablecodec.EncodeRecordKey(w.table.RecordPrefix(), handle) + }, + IndexEncode: func(idxRow *consistency.RecordData) kv.Key { + var idx table.Index + for _, v := range w.table.Indices() { + if strings.EqualFold(v.Meta().Name.String(), idxInfo.Name.O) { + idx = v + break + } + } + if idx == nil { + return nil + } + k, _, err := idx.GenIndexKey(w.sctx.GetSessionVars().StmtCtx, idxRow.Values[:len(idx.Meta().Columns)], idxRow.Handle, nil) + if err != nil { + return nil + } + return k + }, + Tbl: w.table.Meta(), + Idx: idxInfo, + Sctx: w.sctx, + } + } + + getCheckSum := func(row chunk.Row) uint64 { + return row.GetUint64(len(pkCols) + len(idxInfo.Columns)) + } + + var handle kv.Handle + var tableRecord *consistency.RecordData + var lastTableRecord *consistency.RecordData + var indexRecord *consistency.RecordData + i := 0 + for i < len(tblRow) || i < len(idxRow) { + if i == len(tblRow) { + // No more rows in table side. + tableRecord = nil + } else { + handle, err = getHandleFromRow(tblRow[i]) + if err != nil { + trySaveErr(err) + return + } + value, err := getValueFromRow(tblRow[i]) + if err != nil { + trySaveErr(err) + return + } + tableRecord = &consistency.RecordData{Handle: handle, Values: value} + } + if i == len(idxRow) { + // No more rows in index side. + indexRecord = nil + } else { + indexHandle, err := getHandleFromRow(idxRow[i]) + if err != nil { + trySaveErr(err) + return + } + indexValue, err := getValueFromRow(idxRow[i]) + if err != nil { + trySaveErr(err) + return + } + indexRecord = &consistency.RecordData{Handle: indexHandle, Values: indexValue} + } + + if tableRecord == nil { + if lastTableRecord != nil && lastTableRecord.Handle.Equal(indexRecord.Handle) { + tableRecord = lastTableRecord + } + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, tableRecord) + } else if indexRecord == nil { + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, indexRecord, tableRecord) + } else if tableRecord.Handle.Equal(indexRecord.Handle) && getCheckSum(tblRow[i]) != getCheckSum(idxRow[i]) { + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, indexRecord, tableRecord) + } else if !tableRecord.Handle.Equal(indexRecord.Handle) { + if tableRecord.Handle.Compare(indexRecord.Handle) < 0 { + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, tableRecord.Handle, nil, tableRecord) + } else { + if lastTableRecord != nil && lastTableRecord.Handle.Equal(indexRecord.Handle) { + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, lastTableRecord) + } else { + err = ir().ReportAdminCheckInconsistent(w.e.contextCtx, indexRecord.Handle, indexRecord, nil) + } + } + } + if err != nil { + trySaveErr(err) + return + } + i++ + if tableRecord != nil { + lastTableRecord = &consistency.RecordData{Handle: tableRecord.Handle, Values: tableRecord.Values} + } else { + lastTableRecord = nil + } + } + } +} + +// Close implements the Worker interface. +func (*checkIndexWorker) Close() {} + +func (e *FastCheckTableExec) createWorker() workerpool.Worker[checkIndexTask, workerpool.None] { + return &checkIndexWorker{sctx: e.Ctx(), dbName: e.dbName, table: e.table, indexInfos: e.indexInfos, e: e} +} + +// Next implements the Executor Next interface. +func (e *FastCheckTableExec) Next(ctx context.Context, _ *chunk.Chunk) error { + if e.done || len(e.indexInfos) == 0 { + return nil + } + defer func() { e.done = true }() + + // Here we need check all indexes, includes invisible index + e.Ctx().GetSessionVars().OptimizerUseInvisibleIndexes = true + defer func() { + e.Ctx().GetSessionVars().OptimizerUseInvisibleIndexes = false + }() + + workerPool := workerpool.NewWorkerPool[checkIndexTask]("checkIndex", + poolutil.CheckTable, 3, e.createWorker) + workerPool.Start(ctx) + + e.wg.Add(len(e.indexInfos)) + for i := range e.indexInfos { + workerPool.AddTask(checkIndexTask{indexOffset: i}) + } + + e.wg.Wait() + workerPool.ReleaseAndWait() + + p := e.err.Load() + if p == nil { + return nil + } + return *p +} + +// TableName returns `schema`.`table` +func TableName(schema, table string) string { + return fmt.Sprintf("`%s`.`%s`", escapeName(schema), escapeName(table)) +} + +// ColumnName returns `column` +func ColumnName(column string) string { + return fmt.Sprintf("`%s`", escapeName(column)) +} + +func escapeName(name string) string { + return strings.ReplaceAll(name, "`", "``") +} diff --git a/executor/executor_failpoint_test.go b/pkg/executor/executor_failpoint_test.go similarity index 90% rename from executor/executor_failpoint_test.go rename to pkg/executor/executor_failpoint_test.go index ecec2a35aa38e..16e10453c2c1b 100644 --- a/executor/executor_failpoint_test.go +++ b/pkg/executor/executor_failpoint_test.go @@ -25,14 +25,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" "github.com/stretchr/testify/require" ) @@ -120,8 +120,8 @@ func TestPointGetRepeatableRead(t *testing.T) { tk2.MustExec("use test") var ( - step1 = "github.com/pingcap/tidb/executor/pointGetRepeatableReadTest-step1" - step2 = "github.com/pingcap/tidb/executor/pointGetRepeatableReadTest-step2" + step1 = "github.com/pingcap/tidb/pkg/executor/pointGetRepeatableReadTest-step1" + step2 = "github.com/pingcap/tidb/pkg/executor/pointGetRepeatableReadTest-step2" ) require.NoError(t, failpoint.Enable(step1, "return")) @@ -156,8 +156,8 @@ func TestBatchPointGetRepeatableRead(t *testing.T) { tk2.MustExec("use test") var ( - step1 = "github.com/pingcap/tidb/executor/batchPointGetRepeatableReadTest-step1" - step2 = "github.com/pingcap/tidb/executor/batchPointGetRepeatableReadTest-step2" + step1 = "github.com/pingcap/tidb/pkg/executor/batchPointGetRepeatableReadTest-step1" + step2 = "github.com/pingcap/tidb/pkg/executor/batchPointGetRepeatableReadTest-step2" ) require.NoError(t, failpoint.Enable(step1, "return")) @@ -224,13 +224,13 @@ func TestTSOFail(t *testing.T) { tk.MustExec(`drop table if exists t`) tk.MustExec(`create table t(a int)`) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockGetTSFail", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockGetTSFail", "return")) ctx := failpoint.WithHook(context.Background(), func(ctx context.Context, fpname string) bool { - return fpname == "github.com/pingcap/tidb/session/mockGetTSFail" + return fpname == "github.com/pingcap/tidb/pkg/session/mockGetTSFail" }) _, err := tk.Session().Execute(ctx, `select * from t`) require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockGetTSFail")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockGetTSFail")) } func TestKillTableReader(t *testing.T) { @@ -334,29 +334,29 @@ func TestCoprocessorOOMTiCase(t *testing.T) { } // ticase-4169, trigger oom action twice after workers consuming all the data - err := failpoint.Enable("github.com/pingcap/tidb/store/copr/ticase-4169", `return(true)`) + err := failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/ticase-4169", `return(true)`) require.NoError(t, err) f() - err = failpoint.Disable("github.com/pingcap/tidb/store/copr/ticase-4169") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/ticase-4169") require.NoError(t, err) // ticase-4170, trigger oom action twice after iterator receiving all the data. - err = failpoint.Enable("github.com/pingcap/tidb/store/copr/ticase-4170", `return(true)`) + err = failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/ticase-4170", `return(true)`) require.NoError(t, err) f() - err = failpoint.Disable("github.com/pingcap/tidb/store/copr/ticase-4170") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/ticase-4170") require.NoError(t, err) // ticase-4171, trigger oom before reading or consuming any data - err = failpoint.Enable("github.com/pingcap/tidb/store/copr/ticase-4171", `return(true)`) + err = failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/ticase-4171", `return(true)`) require.NoError(t, err) f() - err = failpoint.Disable("github.com/pingcap/tidb/store/copr/ticase-4171") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/ticase-4171") require.NoError(t, err) } func TestIssue21441(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/issue21441", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/issue21441", `return`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/issue21441")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/issue21441")) }() store := testkit.CreateMockStore(t) @@ -401,9 +401,9 @@ func TestTxnWriteThroughputSLI(t *testing.T) { tk.MustExec("use test") tk.MustExec("drop table if exists t") tk.MustExec("create table t (a int key, b int)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/sli/CheckTxnWriteThroughput", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/sli/CheckTxnWriteThroughput", "return(true)")) defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/util/sli/CheckTxnWriteThroughput") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/util/sli/CheckTxnWriteThroughput") require.NoError(t, err) }() @@ -465,14 +465,14 @@ func TestTxnWriteThroughputSLI(t *testing.T) { tk.Session().GetTxnWriteThroughputSLI().Reset() // Test clean last failed transaction information. - err := failpoint.Disable("github.com/pingcap/tidb/util/sli/CheckTxnWriteThroughput") + err := failpoint.Disable("github.com/pingcap/tidb/pkg/util/sli/CheckTxnWriteThroughput") require.NoError(t, err) mustExec("begin") mustExec("insert into t values (1,3),(2,4)") errExec("commit") require.Equal(t, "invalid: false, affectRow: 0, writeSize: 0, readKeys: 0, writeKeys: 0, writeTime: 0s", tk.Session().GetTxnWriteThroughputSLI().String()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/sli/CheckTxnWriteThroughput", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/sli/CheckTxnWriteThroughput", "return(true)")) mustExec("begin") mustExec("insert into t values (5, 6)") mustExec("commit") @@ -538,9 +538,9 @@ func TestDeadlocksTable(t *testing.T) { id1 := strconv.FormatUint(rec.ID, 10) id2 := strconv.FormatUint(rec2.ID, 10) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/sqlDigestRetrieverSkipRetrieveGlobal", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/sqlDigestRetrieverSkipRetrieveGlobal", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/sqlDigestRetrieverSkipRetrieveGlobal")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/sqlDigestRetrieverSkipRetrieveGlobal")) }() store := testkit.CreateMockStore(t) @@ -564,9 +564,9 @@ func TestTiKVClientReadTimeout(t *testing.T) { tk := testkit.NewTestKit(t, store) tk.MustExec("use test") tk.MustExec("create table t (a int primary key, b int)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCDeadlineExceeded", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCDeadlineExceeded", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCDeadlineExceeded")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCDeadlineExceeded")) }() // Test for point_get request rows := tk.MustQuery("explain analyze select /*+ set_var(tikv_client_read_timeout=1) */ * from t where a = 1").Rows() diff --git a/executor/executor_pkg_test.go b/pkg/executor/executor_pkg_test.go similarity index 93% rename from executor/executor_pkg_test.go rename to pkg/executor/executor_pkg_test.go index 8741cf26471af..7bdf60a0310eb 100644 --- a/executor/executor_pkg_test.go +++ b/pkg/executor/executor_pkg_test.go @@ -23,20 +23,20 @@ import ( "unsafe" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - plannerutil "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/tableutil" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + plannerutil "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/tableutil" "github.com/stretchr/testify/require" ) @@ -214,9 +214,9 @@ func TestFilterTemporaryTableKeys(t *testing.T) { } func TestSortSpillDisk(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill")) }() ctx := mock.NewContext() ctx.GetSessionVars().MemQuota.MemQuotaQuery = 1 diff --git a/executor/executor_required_rows_test.go b/pkg/executor/executor_required_rows_test.go similarity index 97% rename from executor/executor_required_rows_test.go rename to pkg/executor/executor_required_rows_test.go index 6f31bbd948ad9..fd836f9cfa007 100644 --- a/executor/executor_required_rows_test.go +++ b/pkg/executor/executor_required_rows_test.go @@ -22,22 +22,22 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/executor_test.go b/pkg/executor/executor_test.go new file mode 100644 index 0000000000000..250f506d2bcc8 --- /dev/null +++ b/pkg/executor/executor_test.go @@ -0,0 +1,169 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func TestSetOperationOnDiffColType(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t1, t2, t3`) + tk.MustExec(`create table t1(a int, b int)`) + tk.MustExec(`create table t2(a int, b varchar(20))`) + tk.MustExec(`create table t3(a int, b decimal(30,10))`) + tk.MustExec(`insert into t1 values (1,1),(1,1),(2,2),(3,3),(null,null)`) + tk.MustExec(`insert into t2 values (1,'1'),(2,'2'),(null,null),(null,'3')`) + tk.MustExec(`insert into t3 values (2,2.1),(3,3)`) + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + executorSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } +} + +// issue-23038: wrong key range of index scan for year column +func TestIndexScanWithYearCol(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (c1 year(4), c2 int, key(c1));") + tk.MustExec("insert into t values(2001, 1);") + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + executorSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } +} + +func TestSetOperation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`drop table if exists t1, t2, t3`) + tk.MustExec(`create table t1(a int)`) + tk.MustExec(`create table t2 like t1`) + tk.MustExec(`create table t3 like t1`) + tk.MustExec(`insert into t1 values (1),(1),(2),(3),(null)`) + tk.MustExec(`insert into t2 values (1),(2),(null),(null)`) + tk.MustExec(`insert into t3 values (2),(3)`) + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + executorSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } + + // from https://github.com/pingcap/tidb/issues/40279 + tk.MustExec("CREATE TABLE `issue40279` (`a` char(155) NOT NULL DEFAULT 'on1unvbxp5sko6mbetn3ku26tuiyju7w3wc0olzto9ew7gsrx',`b` mediumint(9) NOT NULL DEFAULT '2525518',PRIMARY KEY (`b`,`a`) /*T![clustered_index] CLUSTERED */);") + tk.MustExec("insert into `issue40279` values ();") + tk.MustQuery("( select `issue40279`.`b` as r0 , from_base64( `issue40279`.`a` ) as r1 from `issue40279` ) " + + "except ( " + + "select `issue40279`.`a` as r0 , elt(2, `issue40279`.`a` , `issue40279`.`a` ) as r1 from `issue40279`);"). + Check(testkit.Rows("2525518 ")) + tk.MustExec("drop table if exists t2") + + tk.MustExec("CREATE TABLE `t2` ( `a` varchar(20) CHARACTER SET gbk COLLATE gbk_chinese_ci DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin") + tk.MustExec("insert into t2 values(0xCED2)") + result := tk.MustQuery("(select elt(2,t2.a,t2.a) from t2) except (select 0xCED2 from t2)") + rows := result.Rows() + require.Len(t, rows, 0) +} + +func TestCompareIssue38361(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("drop database if exists TEST1") + tk.MustExec("create database TEST1") + tk.MustExec("use TEST1") + tk.MustExec("create table t(a datetime, b bigint, c bigint)") + tk.MustExec("insert into t values(cast('2023-08-09 00:00:00' as datetime), 20230809, 20231310)") + + tk.MustQuery("select a > 20230809 from t").Check(testkit.Rows("0")) + tk.MustQuery("select a = 20230809 from t").Check(testkit.Rows("1")) + tk.MustQuery("select a < 20230810 from t").Check(testkit.Rows("1")) + // 20231310 can't be converted to valid datetime, thus should be compared using real date type,and datetime will be + // converted to something like 'YYYYMMDDHHMMSS', bigger than 20231310 + tk.MustQuery("select a < 20231310 from t").Check(testkit.Rows("0")) + tk.MustQuery("select 20230809 < a from t").Check(testkit.Rows("0")) + tk.MustQuery("select 20230809 = a from t").Check(testkit.Rows("1")) + tk.MustQuery("select 20230810 > a from t").Check(testkit.Rows("1")) + tk.MustQuery("select 20231310 > a from t").Check(testkit.Rows("0")) + + // constant datetime cmp numeric constant should be compared as real data type + tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) > 20230809 from t").Check(testkit.Rows("1")) + tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) = 20230809 from t").Check(testkit.Rows("0")) + tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) < 20230810 from t").Check(testkit.Rows("0")) + tk.MustQuery("select cast('2023-08-09 00:00:00' as datetime) < 20231310 from t").Check(testkit.Rows("0")) + tk.MustQuery("select 20230809 < cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("1")) + tk.MustQuery("select 20230809 = cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) + tk.MustQuery("select 20230810 > cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) + tk.MustQuery("select 20231310 > cast('2023-08-09 00:00:00' as datetime) from t").Check(testkit.Rows("0")) + + // datetime column cmp numeric column should be compared as real data type + tk.MustQuery("select a > b from t").Check(testkit.Rows("1")) + tk.MustQuery("select a = b from t").Check(testkit.Rows("0")) + tk.MustQuery("select a < b + 1 from t").Check(testkit.Rows("0")) + tk.MustQuery("select a < c from t").Check(testkit.Rows("0")) + tk.MustQuery("select b < a from t").Check(testkit.Rows("1")) + tk.MustQuery("select b = a from t").Check(testkit.Rows("0")) + tk.MustQuery("select b > a from t").Check(testkit.Rows("0")) + tk.MustQuery("select c > a from t").Check(testkit.Rows("0")) +} diff --git a/executor/executor_txn_test.go b/pkg/executor/executor_txn_test.go similarity index 99% rename from executor/executor_txn_test.go rename to pkg/executor/executor_txn_test.go index cf078200321e2..d20da73198b8e 100644 --- a/executor/executor_txn_test.go +++ b/pkg/executor/executor_txn_test.go @@ -21,9 +21,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/explain.go b/pkg/executor/explain.go new file mode 100644 index 0000000000000..0946874bd0773 --- /dev/null +++ b/pkg/executor/explain.go @@ -0,0 +1,361 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "fmt" + "os" + "path/filepath" + "runtime" + rpprof "runtime/pprof" + "sort" + "strconv" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/size" + clientutil "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +// ExplainExec represents an explain executor. +type ExplainExec struct { + exec.BaseExecutor + + explain *core.Explain + analyzeExec exec.Executor + executed bool + ruRuntimeStats *clientutil.RURuntimeStats + rows [][]string + cursor int +} + +// Open implements the Executor Open interface. +func (e *ExplainExec) Open(ctx context.Context) error { + if e.analyzeExec != nil { + return e.analyzeExec.Open(ctx) + } + return nil +} + +// Close implements the Executor Close interface. +func (e *ExplainExec) Close() error { + e.rows = nil + if e.analyzeExec != nil && !e.executed { + // Open(), but Next() is not called. + return e.analyzeExec.Close() + } + return nil +} + +// Next implements the Executor Next interface. +func (e *ExplainExec) Next(ctx context.Context, req *chunk.Chunk) error { + if e.rows == nil { + var err error + e.rows, err = e.generateExplainInfo(ctx) + if err != nil { + return err + } + } + + req.GrowAndReset(e.MaxChunkSize()) + if e.cursor >= len(e.rows) { + return nil + } + + numCurRows := mathutil.Min(req.Capacity(), len(e.rows)-e.cursor) + for i := e.cursor; i < e.cursor+numCurRows; i++ { + for j := range e.rows[i] { + req.AppendString(j, e.rows[i][j]) + } + } + e.cursor += numCurRows + return nil +} + +func (e *ExplainExec) executeAnalyzeExec(ctx context.Context) (err error) { + if e.analyzeExec != nil && !e.executed { + defer func() { + err1 := e.analyzeExec.Close() + if err1 != nil { + if err != nil { + err = errors.New(err.Error() + ", " + err1.Error()) + } else { + err = err1 + } + } + }() + if minHeapInUse, alarmRatio := e.Ctx().GetSessionVars().MemoryDebugModeMinHeapInUse, e.Ctx().GetSessionVars().MemoryDebugModeAlarmRatio; minHeapInUse != 0 && alarmRatio != 0 { + memoryDebugModeCtx, cancel := context.WithCancel(ctx) + waitGroup := sync.WaitGroup{} + waitGroup.Add(1) + defer func() { + // Notify and wait debug goroutine exit. + cancel() + waitGroup.Wait() + }() + go (&memoryDebugModeHandler{ + ctx: memoryDebugModeCtx, + minHeapInUse: mathutil.Abs(minHeapInUse), + alarmRatio: alarmRatio, + autoGC: minHeapInUse > 0, + memTracker: e.Ctx().GetSessionVars().MemTracker, + wg: &waitGroup, + }).run() + } + e.executed = true + chk := exec.TryNewCacheChunk(e.analyzeExec) + for { + err = exec.Next(ctx, e.analyzeExec, chk) + if err != nil || chk.NumRows() == 0 { + break + } + } + } + // Register the RU runtime stats to the runtime stats collection after the analyze executor has been executed. + if e.analyzeExec != nil && e.executed { + if coll := e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl; coll != nil { + coll.RegisterStats(e.explain.TargetPlan.ID(), &ruRuntimeStats{e.ruRuntimeStats}) + } + } + return err +} + +func (e *ExplainExec) generateExplainInfo(ctx context.Context) (rows [][]string, err error) { + if err = e.executeAnalyzeExec(ctx); err != nil { + return nil, err + } + if err = e.explain.RenderResult(); err != nil { + return nil, err + } + return e.explain.Rows, nil +} + +// getAnalyzeExecToExecutedNoDelay gets the analyze DML executor to execute in handleNoDelay function. +// For explain analyze insert/update/delete statement, the analyze executor should be executed in handleNoDelay +// function and then commit transaction if needed. +// Otherwise, in autocommit transaction, the table record change of analyze executor(insert/update/delete...) +// will not be committed. +func (e *ExplainExec) getAnalyzeExecToExecutedNoDelay() exec.Executor { + if e.analyzeExec != nil && !e.executed && e.analyzeExec.Schema().Len() == 0 { + e.executed = true + return e.analyzeExec + } + return nil +} + +type memoryDebugModeHandler struct { + ctx context.Context + minHeapInUse int64 + alarmRatio int64 + autoGC bool + wg *sync.WaitGroup + memTracker *memory.Tracker + + infoField []zap.Field +} + +func (h *memoryDebugModeHandler) fetchCurrentMemoryUsage(gc bool) (heapInUse, trackedMem uint64) { + if gc { + runtime.GC() //nolint: revive + } + instanceStats := memory.ForceReadMemStats() + heapInUse = instanceStats.HeapInuse + trackedMem = uint64(h.memTracker.BytesConsumed()) + return +} + +func (h *memoryDebugModeHandler) genInfo(status string, needProfile bool, heapInUse, trackedMem int64) (fields []zap.Field, err error) { + var fileName string + h.infoField = h.infoField[:0] + h.infoField = append(h.infoField, zap.String("sql", status)) + h.infoField = append(h.infoField, zap.String("heap in use", memory.FormatBytes(heapInUse))) + h.infoField = append(h.infoField, zap.String("tracked memory", memory.FormatBytes(trackedMem))) + if needProfile { + fileName, err = getHeapProfile() + h.infoField = append(h.infoField, zap.String("heap profile", fileName)) + } + return h.infoField, err +} + +func (h *memoryDebugModeHandler) getTrackerTreeMemUseLogs() []zap.Field { + trackerMemUseMap := h.memTracker.CountAllChildrenMemUse() + logs := make([]zap.Field, 0, len(trackerMemUseMap)) + keys := make([]string, 0, len(trackerMemUseMap)) + for k := range trackerMemUseMap { + keys = append(keys, k) + } + sort.Strings(keys) + for _, k := range keys { + logs = append(logs, zap.String("TrackerTree "+k, memory.FormatBytes(trackerMemUseMap[k]))) + } + return logs +} + +func updateTriggerIntervalByHeapInUse(heapInUse uint64) (time.Duration, int) { + if heapInUse < 30*size.GB { + return 5 * time.Second, 6 + } else if heapInUse < 40*size.GB { + return 15 * time.Second, 2 + } else { + return 30 * time.Second, 1 + } +} + +func (h *memoryDebugModeHandler) run() { + var err error + var fields []zap.Field + defer func() { + heapInUse, trackedMem := h.fetchCurrentMemoryUsage(true) + if err == nil { + fields, err := h.genInfo("finished", true, int64(heapInUse), int64(trackedMem)) + logutil.BgLogger().Info("Memory Debug Mode", fields...) + if err != nil { + logutil.BgLogger().Error("Memory Debug Mode Exit", zap.Error(err)) + } + } else { + fields, err := h.genInfo("debug_mode_error", false, int64(heapInUse), int64(trackedMem)) + logutil.BgLogger().Error("Memory Debug Mode", fields...) + logutil.BgLogger().Error("Memory Debug Mode Exit", zap.Error(err)) + } + h.wg.Done() + }() + + logutil.BgLogger().Info("Memory Debug Mode", + zap.String("sql", "started"), + zap.Bool("autoGC", h.autoGC), + zap.String("minHeapInUse", memory.FormatBytes(h.minHeapInUse)), + zap.Int64("alarmRatio", h.alarmRatio), + ) + triggerInterval := 5 * time.Second + printMod := 6 + ticker, loop := time.NewTicker(triggerInterval), 0 + for { + select { + case <-h.ctx.Done(): + return + case <-ticker.C: + heapInUse, trackedMem := h.fetchCurrentMemoryUsage(h.autoGC) + loop++ + if loop%printMod == 0 { + fields, err = h.genInfo("running", false, int64(heapInUse), int64(trackedMem)) + logutil.BgLogger().Info("Memory Debug Mode", fields...) + if err != nil { + return + } + } + triggerInterval, printMod = updateTriggerIntervalByHeapInUse(heapInUse) + ticker.Reset(triggerInterval) + + if !h.autoGC { + if heapInUse > uint64(h.minHeapInUse) && trackedMem/100*uint64(100+h.alarmRatio) < heapInUse { + fields, err = h.genInfo("warning", true, int64(heapInUse), int64(trackedMem)) + logutil.BgLogger().Warn("Memory Debug Mode", fields...) + if err != nil { + return + } + } + } else { + if heapInUse > uint64(h.minHeapInUse) && trackedMem/100*uint64(100+h.alarmRatio) < heapInUse { + fields, err = h.genInfo("warning", true, int64(heapInUse), int64(trackedMem)) + logutil.BgLogger().Warn("Memory Debug Mode", fields...) + if err != nil { + return + } + ts := h.memTracker.SearchTrackerConsumedMoreThanNBytes(h.minHeapInUse / 5) + logs := make([]zap.Field, 0, len(ts)) + for _, t := range ts { + logs = append(logs, zap.String("Executor_"+strconv.Itoa(t.Label()), memory.FormatBytes(t.BytesConsumed()))) + } + logutil.BgLogger().Warn("Memory Debug Mode, Log all executors that consumes more than threshold * 20%", logs...) + logutil.BgLogger().Warn("Memory Debug Mode, Log the tracker tree", h.getTrackerTreeMemUseLogs()...) + } + } + } + } +} + +func getHeapProfile() (fileName string, err error) { + tempDir := filepath.Join(config.GetGlobalConfig().TempStoragePath, "record") + timeString := time.Now().Format(time.RFC3339) + fileName = filepath.Join(tempDir, "heapGC"+timeString) + f, err := os.Create(fileName) + if err != nil { + return "", err + } + p := rpprof.Lookup("heap") + err = p.WriteTo(f, 0) + if err != nil { + return "", err + } + err = f.Close() + if err != nil { + return "", err + } + return fileName, nil +} + +// ruRuntimeStats is a wrapper of clientutil.RURuntimeStats, +// which implements the RuntimeStats interface. +type ruRuntimeStats struct { + *clientutil.RURuntimeStats +} + +// String implements the RuntimeStats interface. +func (e *ruRuntimeStats) String() string { + if e.RURuntimeStats != nil { + return fmt.Sprintf("RU:%f", e.RURuntimeStats.RRU()+e.RURuntimeStats.WRU()) + } + return "" +} + +// Clone implements the RuntimeStats interface. +func (e *ruRuntimeStats) Clone() execdetails.RuntimeStats { + newRs := &ruRuntimeStats{} + if e.RURuntimeStats != nil { + newRs.RURuntimeStats = e.RURuntimeStats.Clone() + } + return newRs +} + +// Merge implements the RuntimeStats interface. +func (e *ruRuntimeStats) Merge(other execdetails.RuntimeStats) { + tmp, ok := other.(*ruRuntimeStats) + if !ok { + return + } + if tmp.RURuntimeStats != nil { + if e.RURuntimeStats == nil { + e.RURuntimeStats = tmp.RURuntimeStats.Clone() + return + } + e.RURuntimeStats.Merge(tmp.RURuntimeStats) + } +} + +// Tp implements the RuntimeStats interface. +func (*ruRuntimeStats) Tp() int { + return execdetails.TpRURuntimeStats +} diff --git a/executor/explain_test.go b/pkg/executor/explain_test.go similarity index 99% rename from executor/explain_test.go rename to pkg/executor/explain_test.go index 902f7c96eb2de..3c720f836f4e0 100644 --- a/executor/explain_test.go +++ b/pkg/executor/explain_test.go @@ -24,13 +24,13 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/auth" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/auth" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/executor/explain_unit_test.go b/pkg/executor/explain_unit_test.go similarity index 88% rename from executor/explain_unit_test.go rename to pkg/executor/explain_unit_test.go index 1b3c50d656ed6..4761e7d3a337e 100644 --- a/executor/explain_unit_test.go +++ b/pkg/executor/explain_unit_test.go @@ -19,13 +19,13 @@ import ( "errors" "testing" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/executor/explainfor_test.go b/pkg/executor/explainfor_test.go similarity index 99% rename from executor/explainfor_test.go rename to pkg/executor/explainfor_test.go index 73a9ec02f40dd..a98697852ac2f 100644 --- a/executor/explainfor_test.go +++ b/pkg/executor/explainfor_test.go @@ -21,11 +21,11 @@ import ( "strconv" "testing" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/foreign_key.go b/pkg/executor/foreign_key.go new file mode 100644 index 0000000000000..512399c9931a6 --- /dev/null +++ b/pkg/executor/foreign_key.go @@ -0,0 +1,974 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "context" + "strconv" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/tikv/client-go/v2/txnkv/txnsnapshot" +) + +// WithForeignKeyTrigger indicates the executor has foreign key check or cascade. +type WithForeignKeyTrigger interface { + GetFKChecks() []*FKCheckExec + GetFKCascades() []*FKCascadeExec + HasFKCascades() bool +} + +// FKCheckExec uses to check foreign key constraint. +// When insert/update child table, need to check the row has related row exists in refer table. +// When insert/update parent table, need to check the row doesn't have related row exists in refer table. +type FKCheckExec struct { + *plannercore.FKCheck + *fkValueHelper + ctx sessionctx.Context + + toBeCheckedKeys []kv.Key + toBeCheckedPrefixKeys []kv.Key + toBeLockedKeys []kv.Key + + checkRowsCache map[string]bool + stats *FKCheckRuntimeStats +} + +// FKCheckRuntimeStats contains the FKCheckExec runtime stats. +type FKCheckRuntimeStats struct { + Total time.Duration + Check time.Duration + Lock time.Duration + Keys int +} + +// FKCascadeExec uses to execute foreign key cascade behaviour. +type FKCascadeExec struct { + *fkValueHelper + plan *plannercore.FKCascade + b *executorBuilder + tp plannercore.FKCascadeType + referredFK *model.ReferredFKInfo + childTable *model.TableInfo + fk *model.FKInfo + fkCols []*model.ColumnInfo + fkIdx *model.IndexInfo + // On delete statement, fkValues stores the delete foreign key values. + // On update statement and the foreign key cascade is `SET NULL`, fkValues stores the old foreign key values. + fkValues [][]types.Datum + // new-value-key => UpdatedValuesCouple + fkUpdatedValuesMap map[string]*UpdatedValuesCouple + + stats *FKCascadeRuntimeStats +} + +// UpdatedValuesCouple contains the updated new row the old rows, exporting for test. +type UpdatedValuesCouple struct { + NewValues []types.Datum + OldValuesList [][]types.Datum +} + +// FKCascadeRuntimeStats contains the FKCascadeExec runtime stats. +type FKCascadeRuntimeStats struct { + Total time.Duration + Keys int +} + +func buildTblID2FKCheckExecs(sctx sessionctx.Context, tblID2Table map[int64]table.Table, tblID2FKChecks map[int64][]*plannercore.FKCheck) (map[int64][]*FKCheckExec, error) { + fkChecksMap := make(map[int64][]*FKCheckExec) + for tid, tbl := range tblID2Table { + fkChecks, err := buildFKCheckExecs(sctx, tbl, tblID2FKChecks[tid]) + if err != nil { + return nil, err + } + if len(fkChecks) > 0 { + fkChecksMap[tid] = fkChecks + } + } + return fkChecksMap, nil +} + +func buildFKCheckExecs(sctx sessionctx.Context, tbl table.Table, fkChecks []*plannercore.FKCheck) ([]*FKCheckExec, error) { + fkCheckExecs := make([]*FKCheckExec, 0, len(fkChecks)) + for _, fkCheck := range fkChecks { + fkCheckExec, err := buildFKCheckExec(sctx, tbl, fkCheck) + if err != nil { + return nil, err + } + if fkCheckExec != nil { + fkCheckExecs = append(fkCheckExecs, fkCheckExec) + } + } + return fkCheckExecs, nil +} + +func buildFKCheckExec(sctx sessionctx.Context, tbl table.Table, fkCheck *plannercore.FKCheck) (*FKCheckExec, error) { + var cols []model.CIStr + if fkCheck.FK != nil { + cols = fkCheck.FK.Cols + } else if fkCheck.ReferredFK != nil { + cols = fkCheck.ReferredFK.Cols + } + colsOffsets, err := getFKColumnsOffsets(tbl.Meta(), cols) + if err != nil { + return nil, err + } + helper := &fkValueHelper{ + colsOffsets: colsOffsets, + fkValuesSet: set.NewStringSet(), + } + return &FKCheckExec{ + ctx: sctx, + FKCheck: fkCheck, + fkValueHelper: helper, + }, nil +} + +func (fkc *FKCheckExec) insertRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { + if fkc.ReferredFK != nil { + // Insert into parent table doesn't need to do foreign key check. + return nil + } + return fkc.addRowNeedToCheck(sc, row) +} + +func (fkc *FKCheckExec) updateRowNeedToCheck(sc *stmtctx.StatementContext, oldRow, newRow []types.Datum) error { + newVals, err := fkc.fetchFKValues(newRow) + if err != nil { + return err + } + oldVals, err := fkc.fetchFKValues(oldRow) + if err != nil { + return err + } + if len(oldVals) == len(newVals) { + isSameValue := true + for i := range oldVals { + cmp, err := oldVals[i].Compare(sc, &newVals[i], collate.GetCollator(oldVals[i].Collation())) + if err != nil || cmp != 0 { + isSameValue = false + break + } + } + if isSameValue { + // If the old fk value and the new fk value are the same, no need to check. + return nil + } + } + + if fkc.FK != nil { + return fkc.addRowNeedToCheck(sc, newRow) + } else if fkc.ReferredFK != nil { + return fkc.addRowNeedToCheck(sc, oldRow) + } + return nil +} + +func (fkc *FKCheckExec) deleteRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { + return fkc.addRowNeedToCheck(sc, row) +} + +func (fkc *FKCheckExec) addRowNeedToCheck(sc *stmtctx.StatementContext, row []types.Datum) error { + vals, err := fkc.fetchFKValuesWithCheck(sc, row) + if err != nil || len(vals) == 0 { + return err + } + key, isPrefix, err := fkc.buildCheckKeyFromFKValue(sc, vals) + if err != nil { + return err + } + if isPrefix { + fkc.toBeCheckedPrefixKeys = append(fkc.toBeCheckedPrefixKeys, key) + } else { + fkc.toBeCheckedKeys = append(fkc.toBeCheckedKeys, key) + } + return nil +} + +func (fkc *FKCheckExec) doCheck(ctx context.Context) error { + if fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { + fkc.stats = &FKCheckRuntimeStats{} + defer fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(fkc.ID(), fkc.stats) + } + if len(fkc.toBeCheckedKeys) == 0 && len(fkc.toBeCheckedPrefixKeys) == 0 { + return nil + } + start := time.Now() + if fkc.stats != nil { + defer func() { + fkc.stats.Keys = len(fkc.toBeCheckedKeys) + len(fkc.toBeCheckedPrefixKeys) + fkc.stats.Total = time.Since(start) + }() + } + txn, err := fkc.ctx.Txn(false) + if err != nil { + return err + } + err = fkc.checkKeys(ctx, txn) + if err != nil { + return err + } + err = fkc.checkIndexKeys(ctx, txn) + if err != nil { + return err + } + if fkc.stats != nil { + fkc.stats.Check = time.Since(start) + } + if len(fkc.toBeLockedKeys) == 0 { + return nil + } + sessVars := fkc.ctx.GetSessionVars() + lockCtx, err := newLockCtx(fkc.ctx, sessVars.LockWaitTimeout, len(fkc.toBeLockedKeys)) + if err != nil { + return err + } + // WARN: Since tidb current doesn't support `LOCK IN SHARE MODE`, therefore, performance will be very poor in concurrency cases. + // TODO(crazycs520):After TiDB support `LOCK IN SHARE MODE`, use `LOCK IN SHARE MODE` here. + forUpdate := atomic.LoadUint32(&sessVars.TxnCtx.ForUpdate) + err = doLockKeys(ctx, fkc.ctx, lockCtx, fkc.toBeLockedKeys...) + // doLockKeys may set TxnCtx.ForUpdate to 1, then if the lock meet write conflict, TiDB can't retry for update. + // So reset TxnCtx.ForUpdate to 0 then can be retry if meet write conflict. + atomic.StoreUint32(&sessVars.TxnCtx.ForUpdate, forUpdate) + if fkc.stats != nil { + fkc.stats.Lock = time.Since(start) - fkc.stats.Check + } + return err +} + +func (fkc *FKCheckExec) buildCheckKeyFromFKValue(sc *stmtctx.StatementContext, vals []types.Datum) (key kv.Key, isPrefix bool, err error) { + if fkc.IdxIsPrimaryKey { + handleKey, err := fkc.buildHandleFromFKValues(sc, vals) + if err != nil { + return nil, false, err + } + key := tablecodec.EncodeRecordKey(fkc.Tbl.RecordPrefix(), handleKey) + if fkc.IdxIsExclusive { + return key, false, nil + } + return key, true, nil + } + key, distinct, err := fkc.Idx.GenIndexKey(sc, vals, nil, nil) + if err != nil { + return nil, false, err + } + if distinct && fkc.IdxIsExclusive { + return key, false, nil + } + return key, true, nil +} + +func (fkc *FKCheckExec) buildHandleFromFKValues(sc *stmtctx.StatementContext, vals []types.Datum) (kv.Handle, error) { + if len(vals) == 1 && fkc.Idx == nil { + return kv.IntHandle(vals[0].GetInt64()), nil + } + handleBytes, err := codec.EncodeKey(sc, nil, vals...) + if err != nil { + return nil, err + } + return kv.NewCommonHandle(handleBytes) +} + +func (fkc *FKCheckExec) checkKeys(ctx context.Context, txn kv.Transaction) error { + if len(fkc.toBeCheckedKeys) == 0 { + return nil + } + err := fkc.prefetchKeys(ctx, txn, fkc.toBeCheckedKeys) + if err != nil { + return err + } + for _, k := range fkc.toBeCheckedKeys { + err = fkc.checkKey(ctx, txn, k) + if err != nil { + return err + } + } + return nil +} + +func (*FKCheckExec) prefetchKeys(ctx context.Context, txn kv.Transaction, keys []kv.Key) error { + // Fill cache using BatchGet + _, err := txn.BatchGet(ctx, keys) + if err != nil { + return err + } + return nil +} + +func (fkc *FKCheckExec) checkKey(ctx context.Context, txn kv.Transaction, k kv.Key) error { + if fkc.CheckExist { + return fkc.checkKeyExist(ctx, txn, k) + } + return fkc.checkKeyNotExist(ctx, txn, k) +} + +func (fkc *FKCheckExec) checkKeyExist(ctx context.Context, txn kv.Transaction, k kv.Key) error { + _, err := txn.Get(ctx, k) + if err == nil { + fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, k) + return nil + } + if kv.IsErrNotFound(err) { + return fkc.FailedErr + } + return err +} + +func (fkc *FKCheckExec) checkKeyNotExist(ctx context.Context, txn kv.Transaction, k kv.Key) error { + _, err := txn.Get(ctx, k) + if err == nil { + return fkc.FailedErr + } + if kv.IsErrNotFound(err) { + return nil + } + return err +} + +func (fkc *FKCheckExec) checkIndexKeys(ctx context.Context, txn kv.Transaction) error { + if len(fkc.toBeCheckedPrefixKeys) == 0 { + return nil + } + memBuffer := txn.GetMemBuffer() + snap := txn.GetSnapshot() + snap.SetOption(kv.ScanBatchSize, 2) + defer func() { + snap.SetOption(kv.ScanBatchSize, txnsnapshot.DefaultScanBatchSize) + }() + for _, key := range fkc.toBeCheckedPrefixKeys { + err := fkc.checkPrefixKey(ctx, memBuffer, snap, key) + if err != nil { + return err + } + } + return nil +} + +func (fkc *FKCheckExec) checkPrefixKey(ctx context.Context, memBuffer kv.MemBuffer, snap kv.Snapshot, key kv.Key) error { + key, value, err := fkc.getIndexKeyValueInTable(ctx, memBuffer, snap, key) + if err != nil { + return err + } + if fkc.CheckExist { + return fkc.checkPrefixKeyExist(key, value) + } + if len(value) > 0 { + // If check not exist, but the key is exist, return failedErr. + return fkc.FailedErr + } + return nil +} + +func (fkc *FKCheckExec) checkPrefixKeyExist(key kv.Key, value []byte) error { + exist := len(value) > 0 + if !exist { + return fkc.FailedErr + } + if fkc.Idx != nil && fkc.Idx.Meta().Primary && fkc.Tbl.Meta().IsCommonHandle { + fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, key) + } else { + handle, err := tablecodec.DecodeIndexHandle(key, value, len(fkc.Idx.Meta().Columns)) + if err != nil { + return err + } + handleKey := tablecodec.EncodeRecordKey(fkc.Tbl.RecordPrefix(), handle) + fkc.toBeLockedKeys = append(fkc.toBeLockedKeys, handleKey) + } + return nil +} + +func (*FKCheckExec) getIndexKeyValueInTable(ctx context.Context, memBuffer kv.MemBuffer, snap kv.Snapshot, key kv.Key) (k []byte, v []byte, _ error) { + select { + case <-ctx.Done(): + return nil, nil, ctx.Err() + default: + } + memIter, err := memBuffer.Iter(key, key.PrefixNext()) + if err != nil { + return nil, nil, err + } + deletedKeys := set.NewStringSet() + defer memIter.Close() + for ; memIter.Valid(); err = memIter.Next() { + if err != nil { + return nil, nil, err + } + k := memIter.Key() + if !k.HasPrefix(key) { + break + } + // check whether the key was been deleted. + if len(memIter.Value()) > 0 { + return k, memIter.Value(), nil + } + deletedKeys.Insert(string(k)) + } + + it, err := snap.Iter(key, key.PrefixNext()) + if err != nil { + return nil, nil, err + } + defer it.Close() + for ; it.Valid(); err = it.Next() { + if err != nil { + return nil, nil, err + } + k := it.Key() + if !k.HasPrefix(key) { + break + } + if !deletedKeys.Exist(string(k)) { + return k, it.Value(), nil + } + } + return nil, nil, nil +} + +type fkValueHelper struct { + colsOffsets []int + fkValuesSet set.StringSet +} + +func (h *fkValueHelper) fetchFKValuesWithCheck(sc *stmtctx.StatementContext, row []types.Datum) ([]types.Datum, error) { + vals, err := h.fetchFKValues(row) + if err != nil || h.hasNullValue(vals) { + return nil, err + } + keyBuf, err := codec.EncodeKey(sc, nil, vals...) + if err != nil { + return nil, err + } + key := string(keyBuf) + if h.fkValuesSet.Exist(key) { + return nil, nil + } + h.fkValuesSet.Insert(key) + return vals, nil +} + +func (h *fkValueHelper) fetchFKValues(row []types.Datum) ([]types.Datum, error) { + vals := make([]types.Datum, len(h.colsOffsets)) + for i, offset := range h.colsOffsets { + if offset >= len(row) { + return nil, table.ErrIndexOutBound.GenWithStackByArgs("", offset, row) + } + vals[i] = row[offset] + } + return vals, nil +} + +func (*fkValueHelper) hasNullValue(vals []types.Datum) bool { + // If any foreign key column value is null, no need to check this row. + // test case: + // create table t1 (id int key,a int, b int, index(a, b)); + // create table t2 (id int key,a int, b int, foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE); + // > insert into t2 values (2, null, 1); + // Query OK, 1 row affected + // > insert into t2 values (3, 1, null); + // Query OK, 1 row affected + // > insert into t2 values (4, null, null); + // Query OK, 1 row affected + // > select * from t2; + // +----+--------+--------+ + // | id | a | b | + // +----+--------+--------+ + // | 4 | | | + // | 2 | | 1 | + // | 3 | 1 | | + // +----+--------+--------+ + for _, val := range vals { + if val.IsNull() { + return true + } + } + return false +} + +func getFKColumnsOffsets(tbInfo *model.TableInfo, cols []model.CIStr) ([]int, error) { + colsOffsets := make([]int, len(cols)) + for i, col := range cols { + offset := -1 + for i := range tbInfo.Columns { + if tbInfo.Columns[i].Name.L == col.L { + offset = tbInfo.Columns[i].Offset + break + } + } + if offset < 0 { + return nil, table.ErrUnknownColumn.GenWithStackByArgs(col.L) + } + colsOffsets[i] = offset + } + return colsOffsets, nil +} + +type fkCheckKey struct { + k kv.Key + isPrefix bool +} + +func (fkc FKCheckExec) checkRows(ctx context.Context, sc *stmtctx.StatementContext, txn kv.Transaction, rows []toBeCheckedRow) error { + if fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { + fkc.stats = &FKCheckRuntimeStats{} + defer fkc.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(fkc.ID(), fkc.stats) + } + if len(rows) == 0 { + return nil + } + if fkc.checkRowsCache == nil { + fkc.checkRowsCache = map[string]bool{} + } + fkCheckKeys := make([]*fkCheckKey, len(rows)) + prefetchKeys := make([]kv.Key, 0, len(rows)) + for i, r := range rows { + if r.ignored { + continue + } + vals, err := fkc.fetchFKValues(r.row) + if err != nil { + return err + } + if fkc.hasNullValue(vals) { + continue + } + key, isPrefix, err := fkc.buildCheckKeyFromFKValue(sc, vals) + if err != nil { + return err + } + fkCheckKeys[i] = &fkCheckKey{key, isPrefix} + if !isPrefix { + prefetchKeys = append(prefetchKeys, key) + } + } + if len(prefetchKeys) > 0 { + err := fkc.prefetchKeys(ctx, txn, prefetchKeys) + if err != nil { + return err + } + } + memBuffer := txn.GetMemBuffer() + snap := txn.GetSnapshot() + snap.SetOption(kv.ScanBatchSize, 2) + defer func() { + snap.SetOption(kv.ScanBatchSize, 256) + }() + for i, fkCheckKey := range fkCheckKeys { + if fkCheckKey == nil { + continue + } + k := fkCheckKey.k + if ignore, ok := fkc.checkRowsCache[string(k)]; ok { + if ignore { + rows[i].ignored = true + sc.AppendWarning(fkc.FailedErr) + } + continue + } + var err error + if fkCheckKey.isPrefix { + err = fkc.checkPrefixKey(ctx, memBuffer, snap, k) + } else { + err = fkc.checkKey(ctx, txn, k) + } + if err != nil { + rows[i].ignored = true + sc.AppendWarning(fkc.FailedErr) + fkc.checkRowsCache[string(k)] = true + } else { + fkc.checkRowsCache[string(k)] = false + } + if fkc.stats != nil { + fkc.stats.Keys++ + } + } + return nil +} + +func (b *executorBuilder) buildTblID2FKCascadeExecs(tblID2Table map[int64]table.Table, tblID2FKCascades map[int64][]*plannercore.FKCascade) (map[int64][]*FKCascadeExec, error) { + fkCascadesMap := make(map[int64][]*FKCascadeExec) + for tid, tbl := range tblID2Table { + fkCascades, err := b.buildFKCascadeExecs(tbl, tblID2FKCascades[tid]) + if err != nil { + return nil, err + } + if len(fkCascades) > 0 { + fkCascadesMap[tid] = fkCascades + } + } + return fkCascadesMap, nil +} + +func (b *executorBuilder) buildFKCascadeExecs(tbl table.Table, fkCascades []*plannercore.FKCascade) ([]*FKCascadeExec, error) { + fkCascadeExecs := make([]*FKCascadeExec, 0, len(fkCascades)) + for _, fkCascade := range fkCascades { + fkCascadeExec, err := b.buildFKCascadeExec(tbl, fkCascade) + if err != nil { + return nil, err + } + if fkCascadeExec != nil { + fkCascadeExecs = append(fkCascadeExecs, fkCascadeExec) + } + } + return fkCascadeExecs, nil +} + +func (b *executorBuilder) buildFKCascadeExec(tbl table.Table, fkCascade *plannercore.FKCascade) (*FKCascadeExec, error) { + colsOffsets, err := getFKColumnsOffsets(tbl.Meta(), fkCascade.ReferredFK.Cols) + if err != nil { + return nil, err + } + helper := &fkValueHelper{ + colsOffsets: colsOffsets, + fkValuesSet: set.NewStringSet(), + } + return &FKCascadeExec{ + b: b, + fkValueHelper: helper, + plan: fkCascade, + tp: fkCascade.Tp, + referredFK: fkCascade.ReferredFK, + childTable: fkCascade.ChildTable.Meta(), + fk: fkCascade.FK, + fkCols: fkCascade.FKCols, + fkIdx: fkCascade.FKIdx, + fkUpdatedValuesMap: make(map[string]*UpdatedValuesCouple), + }, nil +} + +func (fkc *FKCascadeExec) onDeleteRow(sc *stmtctx.StatementContext, row []types.Datum) error { + vals, err := fkc.fetchFKValuesWithCheck(sc, row) + if err != nil || len(vals) == 0 { + return err + } + fkc.fkValues = append(fkc.fkValues, vals) + return nil +} + +func (fkc *FKCascadeExec) onUpdateRow(sc *stmtctx.StatementContext, oldRow, newRow []types.Datum) error { + oldVals, err := fkc.fetchFKValuesWithCheck(sc, oldRow) + if err != nil || len(oldVals) == 0 { + return err + } + if model.ReferOptionType(fkc.fk.OnUpdate) == model.ReferOptionSetNull { + fkc.fkValues = append(fkc.fkValues, oldVals) + return nil + } + newVals, err := fkc.fetchFKValues(newRow) + if err != nil { + return err + } + newValsKey, err := codec.EncodeKey(sc, nil, newVals...) + if err != nil { + return err + } + couple := fkc.fkUpdatedValuesMap[string(newValsKey)] + if couple == nil { + couple = &UpdatedValuesCouple{ + NewValues: newVals, + } + } + couple.OldValuesList = append(couple.OldValuesList, oldVals) + fkc.fkUpdatedValuesMap[string(newValsKey)] = couple + return nil +} + +func (fkc *FKCascadeExec) buildExecutor(ctx context.Context) (exec.Executor, error) { + p, err := fkc.buildFKCascadePlan(ctx) + if err != nil || p == nil { + return nil, err + } + fkc.plan.CascadePlans = append(fkc.plan.CascadePlans, p) + e := fkc.b.build(p) + return e, fkc.b.err +} + +// maxHandleFKValueInOneCascade uses to limit the max handle fk value in one cascade executor, +// this is to avoid performance issue, see: https://github.com/pingcap/tidb/issues/38631 +var maxHandleFKValueInOneCascade = 1024 + +func (fkc *FKCascadeExec) buildFKCascadePlan(ctx context.Context) (plannercore.Plan, error) { + if len(fkc.fkValues) == 0 && len(fkc.fkUpdatedValuesMap) == 0 { + return nil, nil + } + var indexName model.CIStr + if fkc.fkIdx != nil { + indexName = fkc.fkIdx.Name + } + var stmtNode ast.StmtNode + switch fkc.tp { + case plannercore.FKCascadeOnDelete: + fkValues := fkc.fetchOnDeleteOrUpdateFKValues() + switch model.ReferOptionType(fkc.fk.OnDelete) { + case model.ReferOptionCascade: + stmtNode = GenCascadeDeleteAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) + case model.ReferOptionSetNull: + stmtNode = GenCascadeSetNullAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) + } + case plannercore.FKCascadeOnUpdate: + switch model.ReferOptionType(fkc.fk.OnUpdate) { + case model.ReferOptionCascade: + couple := fkc.fetchUpdatedValuesCouple() + if couple != nil && len(couple.NewValues) != 0 { + if fkc.stats != nil { + fkc.stats.Keys += len(couple.OldValuesList) + } + stmtNode = GenCascadeUpdateAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, couple) + } + case model.ReferOptionSetNull: + fkValues := fkc.fetchOnDeleteOrUpdateFKValues() + stmtNode = GenCascadeSetNullAST(fkc.referredFK.ChildSchema, fkc.childTable.Name, indexName, fkc.fkCols, fkValues) + } + } + if stmtNode == nil { + return nil, errors.Errorf("generate foreign key cascade ast failed, %v", fkc.tp) + } + sctx := fkc.b.ctx + err := plannercore.Preprocess(ctx, sctx, stmtNode) + if err != nil { + return nil, err + } + finalPlan, err := planner.OptimizeForForeignKeyCascade(ctx, sctx, stmtNode, fkc.b.is) + if err != nil { + return nil, err + } + return finalPlan, err +} + +func (fkc *FKCascadeExec) fetchOnDeleteOrUpdateFKValues() [][]types.Datum { + var fkValues [][]types.Datum + if len(fkc.fkValues) <= maxHandleFKValueInOneCascade { + fkValues = fkc.fkValues + fkc.fkValues = nil + } else { + fkValues = fkc.fkValues[:maxHandleFKValueInOneCascade] + fkc.fkValues = fkc.fkValues[maxHandleFKValueInOneCascade:] + } + if fkc.stats != nil { + fkc.stats.Keys += len(fkValues) + } + return fkValues +} + +func (fkc *FKCascadeExec) fetchUpdatedValuesCouple() *UpdatedValuesCouple { + for k, couple := range fkc.fkUpdatedValuesMap { + if len(couple.OldValuesList) <= maxHandleFKValueInOneCascade { + delete(fkc.fkUpdatedValuesMap, k) + return couple + } + result := &UpdatedValuesCouple{ + NewValues: couple.NewValues, + OldValuesList: couple.OldValuesList[:maxHandleFKValueInOneCascade], + } + couple.OldValuesList = couple.OldValuesList[maxHandleFKValueInOneCascade:] + return result + } + return nil +} + +// GenCascadeDeleteAST uses to generate cascade delete ast, export for test. +func GenCascadeDeleteAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, fkValues [][]types.Datum) *ast.DeleteStmt { + deleteStmt := &ast.DeleteStmt{ + TableRefs: genTableRefsAST(schema, table, idx), + Where: genWhereConditionAst(cols, fkValues), + } + return deleteStmt +} + +// GenCascadeSetNullAST uses to generate foreign key `SET NULL` ast, export for test. +func GenCascadeSetNullAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, fkValues [][]types.Datum) *ast.UpdateStmt { + newValues := make([]types.Datum, len(cols)) + for i := range cols { + newValues[i] = types.NewDatum(nil) + } + couple := &UpdatedValuesCouple{ + NewValues: newValues, + OldValuesList: fkValues, + } + return GenCascadeUpdateAST(schema, table, idx, cols, couple) +} + +// GenCascadeUpdateAST uses to generate cascade update ast, export for test. +func GenCascadeUpdateAST(schema, table, idx model.CIStr, cols []*model.ColumnInfo, couple *UpdatedValuesCouple) *ast.UpdateStmt { + list := make([]*ast.Assignment, 0, len(cols)) + for i, col := range cols { + v := &driver.ValueExpr{Datum: couple.NewValues[i]} + v.Type = col.FieldType + assignment := &ast.Assignment{ + Column: &ast.ColumnName{Name: col.Name}, + Expr: v, + } + list = append(list, assignment) + } + updateStmt := &ast.UpdateStmt{ + TableRefs: genTableRefsAST(schema, table, idx), + Where: genWhereConditionAst(cols, couple.OldValuesList), + List: list, + } + return updateStmt +} + +func genTableRefsAST(schema, table, idx model.CIStr) *ast.TableRefsClause { + tn := &ast.TableName{Schema: schema, Name: table} + if idx.L != "" { + tn.IndexHints = []*ast.IndexHint{{ + IndexNames: []model.CIStr{idx}, + HintType: ast.HintUse, + HintScope: ast.HintForScan, + }} + } + join := &ast.Join{Left: &ast.TableSource{Source: tn}} + return &ast.TableRefsClause{TableRefs: join} +} + +func genWhereConditionAst(cols []*model.ColumnInfo, fkValues [][]types.Datum) ast.ExprNode { + if len(cols) > 1 { + return genWhereConditionAstForMultiColumn(cols, fkValues) + } + valueList := make([]ast.ExprNode, 0, len(fkValues)) + for _, fkVals := range fkValues { + v := &driver.ValueExpr{Datum: fkVals[0]} + v.Type = cols[0].FieldType + valueList = append(valueList, v) + } + return &ast.PatternInExpr{ + Expr: &ast.ColumnNameExpr{Name: &ast.ColumnName{Name: cols[0].Name}}, + List: valueList, + } +} + +func genWhereConditionAstForMultiColumn(cols []*model.ColumnInfo, fkValues [][]types.Datum) ast.ExprNode { + colValues := make([]ast.ExprNode, len(cols)) + for i := range cols { + col := &ast.ColumnNameExpr{Name: &ast.ColumnName{Name: cols[i].Name}} + colValues[i] = col + } + valueList := make([]ast.ExprNode, 0, len(fkValues)) + for _, fkVals := range fkValues { + values := make([]ast.ExprNode, len(fkVals)) + for i, v := range fkVals { + val := &driver.ValueExpr{Datum: v} + val.Type = cols[i].FieldType + values[i] = val + } + row := &ast.RowExpr{Values: values} + valueList = append(valueList, row) + } + return &ast.PatternInExpr{ + Expr: &ast.RowExpr{Values: colValues}, + List: valueList, + } +} + +// String implements the RuntimeStats interface. +func (s *FKCheckRuntimeStats) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString("total:") + buf.WriteString(execdetails.FormatDuration(s.Total)) + if s.Check > 0 { + buf.WriteString(", check:") + buf.WriteString(execdetails.FormatDuration(s.Check)) + } + if s.Lock > 0 { + buf.WriteString(", lock:") + buf.WriteString(execdetails.FormatDuration(s.Lock)) + } + if s.Keys > 0 { + buf.WriteString(", foreign_keys:") + buf.WriteString(strconv.Itoa(s.Keys)) + } + return buf.String() +} + +// Clone implements the RuntimeStats interface. +func (s *FKCheckRuntimeStats) Clone() execdetails.RuntimeStats { + newRs := &FKCheckRuntimeStats{ + Total: s.Total, + Check: s.Check, + Lock: s.Lock, + Keys: s.Keys, + } + return newRs +} + +// Merge implements the RuntimeStats interface. +func (s *FKCheckRuntimeStats) Merge(other execdetails.RuntimeStats) { + tmp, ok := other.(*FKCheckRuntimeStats) + if !ok { + return + } + s.Total += tmp.Total + s.Check += tmp.Check + s.Lock += tmp.Lock + s.Keys += tmp.Keys +} + +// Tp implements the RuntimeStats interface. +func (*FKCheckRuntimeStats) Tp() int { + return execdetails.TpFKCheckRuntimeStats +} + +// String implements the RuntimeStats interface. +func (s *FKCascadeRuntimeStats) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 32)) + buf.WriteString("total:") + buf.WriteString(execdetails.FormatDuration(s.Total)) + if s.Keys > 0 { + buf.WriteString(", foreign_keys:") + buf.WriteString(strconv.Itoa(s.Keys)) + } + return buf.String() +} + +// Clone implements the RuntimeStats interface. +func (s *FKCascadeRuntimeStats) Clone() execdetails.RuntimeStats { + newRs := &FKCascadeRuntimeStats{ + Total: s.Total, + Keys: s.Keys, + } + return newRs +} + +// Merge implements the RuntimeStats interface. +func (s *FKCascadeRuntimeStats) Merge(other execdetails.RuntimeStats) { + tmp, ok := other.(*FKCascadeRuntimeStats) + if !ok { + return + } + s.Total += tmp.Total + s.Keys += tmp.Keys +} + +// Tp implements the RuntimeStats interface. +func (*FKCascadeRuntimeStats) Tp() int { + return execdetails.TpFKCascadeRuntimeStats +} diff --git a/executor/grant.go b/pkg/executor/grant.go similarity index 97% rename from executor/grant.go rename to pkg/executor/grant.go index 34f25e70d003e..452f5a20db07c 100644 --- a/executor/grant.go +++ b/pkg/executor/grant.go @@ -20,24 +20,24 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/executor/grant_test.go b/pkg/executor/grant_test.go similarity index 99% rename from executor/grant_test.go rename to pkg/executor/grant_test.go index d0e413a0d2aab..3a10b7aa5f13b 100644 --- a/executor/grant_test.go +++ b/pkg/executor/grant_test.go @@ -19,13 +19,13 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" ) diff --git a/executor/hash_table.go b/pkg/executor/hash_table.go similarity index 98% rename from executor/hash_table.go rename to pkg/executor/hash_table.go index e14e1a67be4dd..ffed29de0da4c 100644 --- a/executor/hash_table.go +++ b/pkg/executor/hash_table.go @@ -23,16 +23,16 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/bitmap" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/bitmap" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/memory" ) // hashContext keeps the needed hash context of a db table in hash join. diff --git a/executor/hash_table_test.go b/pkg/executor/hash_table_test.go similarity index 96% rename from executor/hash_table_test.go rename to pkg/executor/hash_table_test.go index 0a387e0e7e5b6..243f46295b0bc 100644 --- a/executor/hash_table_test.go +++ b/pkg/executor/hash_table_test.go @@ -21,12 +21,12 @@ import ( "sync" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/executor/historical_stats_test.go b/pkg/executor/historical_stats_test.go similarity index 92% rename from executor/historical_stats_test.go rename to pkg/executor/historical_stats_test.go index 89a34c7043de0..65e399487c0d2 100644 --- a/executor/historical_stats_test.go +++ b/pkg/executor/historical_stats_test.go @@ -22,18 +22,18 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle/globalstats" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle/globalstats" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) func TestRecordHistoryStatsAfterAnalyze(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) @@ -154,8 +154,8 @@ func TestRecordHistoryStatsMetaAfterAnalyze(t *testing.T) { } func TestGCHistoryStatsAfterDropTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set global tidb_enable_historical_stats = 1") @@ -191,8 +191,8 @@ func TestGCHistoryStatsAfterDropTable(t *testing.T) { } func TestAssertHistoricalStatsAfterAlterTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set global tidb_enable_historical_stats = 1") @@ -238,8 +238,8 @@ func TestAssertHistoricalStatsAfterAlterTable(t *testing.T) { } func TestGCOutdatedHistoryStats(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set global tidb_enable_historical_stats = 1") @@ -277,8 +277,8 @@ func TestGCOutdatedHistoryStats(t *testing.T) { } func TestPartitionTableHistoricalStats(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set global tidb_enable_historical_stats = 1") @@ -306,8 +306,8 @@ PARTITION p0 VALUES LESS THAN (6) } func TestDumpHistoricalStatsByTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)") - defer failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats") + failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats") store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set global tidb_enable_historical_stats = 1") diff --git a/executor/hot_regions_history_table_test.go b/pkg/executor/hot_regions_history_table_test.go similarity index 98% rename from executor/hot_regions_history_table_test.go rename to pkg/executor/hot_regions_history_table_test.go index 1b44cca26fae1..ce6138458eda3 100644 --- a/executor/hot_regions_history_table_test.go +++ b/pkg/executor/hot_regions_history_table_test.go @@ -29,15 +29,15 @@ import ( "github.com/gorilla/mux" "github.com/pingcap/fn" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/pdapi" "github.com/stretchr/testify/require" ) diff --git a/executor/import_into.go b/pkg/executor/import_into.go similarity index 91% rename from executor/import_into.go rename to pkg/executor/import_into.go index a02a3bb12d232..bf23de1e84387 100644 --- a/executor/import_into.go +++ b/pkg/executor/import_into.go @@ -21,25 +21,25 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/disttask/framework/proto" - fstorage "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + fstorage "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/executor/import_into_test.go b/pkg/executor/import_into_test.go similarity index 97% rename from executor/import_into_test.go rename to pkg/executor/import_into_test.go index 53ffdded23176..f048fe7fc7391 100644 --- a/executor/import_into_test.go +++ b/pkg/executor/import_into_test.go @@ -18,10 +18,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sem" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/importer/BUILD.bazel b/pkg/executor/importer/BUILD.bazel new file mode 100644 index 0000000000000..470c2bedb051a --- /dev/null +++ b/pkg/executor/importer/BUILD.bazel @@ -0,0 +1,136 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "importer", + srcs = [ + "chunk_process.go", + "engine_process.go", + "import.go", + "job.go", + "kv_encode.go", + "precheck.go", + "table_import.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/importer", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/backend", + "//br/pkg/lightning/backend/encode", + "//br/pkg/lightning/backend/external", + "//br/pkg/lightning/backend/kv", + "//br/pkg/lightning/backend/local", + "//br/pkg/lightning/checkpoints", + "//br/pkg/lightning/common", + "//br/pkg/lightning/config", + "//br/pkg/lightning/log", + "//br/pkg/lightning/metric", + "//br/pkg/lightning/mydump", + "//br/pkg/lightning/verification", + "//br/pkg/storage", + "//br/pkg/streamhelper", + "//br/pkg/utils", + "//pkg/config", + "//pkg/ddl/util", + "//pkg/executor/asyncloaddata", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/etcd", + "//pkg/util/filter", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "//pkg/util/stringutil", + "//pkg/util/syncutil", + "@com_github_docker_go_units//:go-units", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@org_golang_x_sync//errgroup", + "@org_uber_go_multierr//:multierr", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "importer_test", + timeout = "short", + srcs = [ + "chunk_process_test.go", + "chunk_process_testkit_test.go", + "import_test.go", + "job_test.go", + "precheck_test.go", + "table_import_test.go", + ], + embed = [":importer"], + flaky = True, + race = "on", + shard_count = 18, + deps = [ + "//br/pkg/errors", + "//br/pkg/lightning/backend/encode", + "//br/pkg/lightning/backend/external", + "//br/pkg/lightning/checkpoints", + "//br/pkg/lightning/config", + "//br/pkg/lightning/log", + "//br/pkg/lightning/mydump", + "//br/pkg/mock", + "//br/pkg/streamhelper", + "//br/pkg/utils", + "//pkg/config", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/types", + "//pkg/util", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/etcd", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/sqlexec", + "//pkg/util/syncutil", + "@com_github_johannesboyne_gofakes3//:gofakes3", + "@com_github_johannesboyne_gofakes3//backend/s3mem", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_server_v3//embed", + "@org_uber_go_mock//gomock", + "@org_uber_go_zap//:zap", + ], +) diff --git a/executor/importer/chunk_process.go b/pkg/executor/importer/chunk_process.go similarity index 99% rename from executor/importer/chunk_process.go rename to pkg/executor/importer/chunk_process.go index b7a7ab367eba6..cdb355fac0d5c 100644 --- a/executor/importer/chunk_process.go +++ b/pkg/executor/importer/chunk_process.go @@ -32,9 +32,9 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/metric" "github.com/pingcap/tidb/br/pkg/lightning/mydump" verify "github.com/pingcap/tidb/br/pkg/lightning/verification" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/syncutil" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/prometheus/client_golang/prometheus" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" diff --git a/executor/importer/chunk_process_test.go b/pkg/executor/importer/chunk_process_test.go similarity index 97% rename from executor/importer/chunk_process_test.go rename to pkg/executor/importer/chunk_process_test.go index ccf95a484a973..9d7e132195ff8 100644 --- a/executor/importer/chunk_process_test.go +++ b/pkg/executor/importer/chunk_process_test.go @@ -20,7 +20,7 @@ import ( "time" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/executor/importer/chunk_process_testkit_test.go b/pkg/executor/importer/chunk_process_testkit_test.go similarity index 94% rename from executor/importer/chunk_process_testkit_test.go rename to pkg/executor/importer/chunk_process_testkit_test.go index dbe5a86a120ef..cc3f18af27a93 100644 --- a/executor/importer/chunk_process_testkit_test.go +++ b/pkg/executor/importer/chunk_process_testkit_test.go @@ -26,11 +26,11 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/mock" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" "go.uber.org/mock/gomock" diff --git a/executor/importer/engine_process.go b/pkg/executor/importer/engine_process.go similarity index 98% rename from executor/importer/engine_process.go rename to pkg/executor/importer/engine_process.go index b22b269afca17..e28daa75599da 100644 --- a/executor/importer/engine_process.go +++ b/pkg/executor/importer/engine_process.go @@ -20,7 +20,7 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend" "github.com/pingcap/tidb/br/pkg/lightning/checkpoints" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" "go.uber.org/zap" ) diff --git a/pkg/executor/importer/import.go b/pkg/executor/importer/import.go new file mode 100644 index 0000000000000..fa1a4e19ef890 --- /dev/null +++ b/pkg/executor/importer/import.go @@ -0,0 +1,1324 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importer + +import ( + "context" + "io" + "math" + "net/url" + "os" + "path/filepath" + "runtime" + "slices" + "strings" + "sync" + "unicode/utf8" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/log" + "github.com/pingcap/tidb/br/pkg/lightning/backend/local" + "github.com/pingcap/tidb/br/pkg/lightning/common" + "github.com/pingcap/tidb/br/pkg/lightning/config" + litlog "github.com/pingcap/tidb/br/pkg/lightning/log" + "github.com/pingcap/tidb/br/pkg/lightning/mydump" + "github.com/pingcap/tidb/br/pkg/storage" + tidb "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/expression" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + pformat "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/filter" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/stringutil" + kvconfig "github.com/tikv/client-go/v2/config" + "go.uber.org/zap" + "golang.org/x/sync/errgroup" +) + +const ( + // DataFormatCSV represents the data source file of IMPORT INTO is csv. + DataFormatCSV = "csv" + // DataFormatDelimitedData delimited data. + DataFormatDelimitedData = "delimited data" + // DataFormatSQL represents the data source file of IMPORT INTO is mydumper-format DML file. + DataFormatSQL = "sql" + // DataFormatParquet represents the data source file of IMPORT INTO is parquet. + DataFormatParquet = "parquet" + + // DefaultDiskQuota is the default disk quota for IMPORT INTO + DefaultDiskQuota = config.ByteSize(50 << 30) // 50GiB + + // 0 means no limit + unlimitedWriteSpeed = config.ByteSize(0) + + characterSetOption = "character_set" + fieldsTerminatedByOption = "fields_terminated_by" + fieldsEnclosedByOption = "fields_enclosed_by" + fieldsEscapedByOption = "fields_escaped_by" + fieldsDefinedNullByOption = "fields_defined_null_by" + linesTerminatedByOption = "lines_terminated_by" + skipRowsOption = "skip_rows" + splitFileOption = "split_file" + diskQuotaOption = "disk_quota" + threadOption = "thread" + maxWriteSpeedOption = "max_write_speed" + checksumTableOption = "checksum_table" + recordErrorsOption = "record_errors" + detachedOption = "detached" + disableTiKVImportModeOption = "disable_tikv_import_mode" + cloudStorageURIOption = "cloud_storage_uri" + // used for test + maxEngineSizeOption = "__max_engine_size" +) + +var ( + // name -> whether the option has value + supportedOptions = map[string]bool{ + characterSetOption: true, + fieldsTerminatedByOption: true, + fieldsEnclosedByOption: true, + fieldsEscapedByOption: true, + fieldsDefinedNullByOption: true, + linesTerminatedByOption: true, + skipRowsOption: true, + splitFileOption: false, + diskQuotaOption: true, + threadOption: true, + maxWriteSpeedOption: true, + checksumTableOption: true, + recordErrorsOption: true, + detachedOption: false, + disableTiKVImportModeOption: false, + maxEngineSizeOption: true, + cloudStorageURIOption: true, + } + + csvOnlyOptions = map[string]struct{}{ + characterSetOption: {}, + fieldsTerminatedByOption: {}, + fieldsEnclosedByOption: {}, + fieldsEscapedByOption: {}, + fieldsDefinedNullByOption: {}, + linesTerminatedByOption: {}, + skipRowsOption: {}, + splitFileOption: {}, + } + + // LoadDataReadBlockSize is exposed for test. + LoadDataReadBlockSize = int64(config.ReadBlockSize) +) + +// GetKVStore returns a kv.Storage. +// kv encoder of physical mode needs it. +var GetKVStore func(path string, tls kvconfig.Security) (tidbkv.Storage, error) + +// FieldMapping indicates the relationship between input field and table column or user variable +type FieldMapping struct { + Column *table.Column + UserVar *ast.VariableExpr +} + +// LoadDataReaderInfo provides information for a data reader of LOAD DATA. +type LoadDataReaderInfo struct { + // Opener can be called at needed to get a io.ReadSeekCloser. It will only + // be called once. + Opener func(ctx context.Context) (io.ReadSeekCloser, error) + // Remote is not nil only if load from cloud storage. + Remote *mydump.SourceFileMeta +} + +// Plan describes the plan of LOAD DATA and IMPORT INTO. +type Plan struct { + DBName string + DBID int64 + // TableInfo is the table info we used during import, we might change it + // if add index by SQL is enabled(it's disabled now). + TableInfo *model.TableInfo + // DesiredTableInfo is the table info before import, and the desired table info + // after import. + DesiredTableInfo *model.TableInfo + + Path string + Format string + // Data interpretation is restrictive if the SQL mode is restrictive and neither + // the IGNORE nor the LOCAL modifier is specified. Errors terminate the load + // operation. + // ref https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-column-assignments + Restrictive bool + + SQLMode mysql.SQLMode + // Charset is the charset of the data file when file is CSV or TSV. + // it might be nil when using LOAD DATA and no charset is specified. + // for IMPORT INTO, it is always non-nil. + Charset *string + ImportantSysVars map[string]string + + // used for LOAD DATA and CSV format of IMPORT INTO + FieldNullDef []string + // this is not used in IMPORT INTO + NullValueOptEnclosed bool + // LinesStartingBy is not used in IMPORT INTO + // FieldsOptEnclosed is not used in either IMPORT INTO or LOAD DATA + plannercore.LineFieldsInfo + IgnoreLines uint64 + + DiskQuota config.ByteSize + Checksum config.PostOpLevel + ThreadCnt int64 + MaxWriteSpeed config.ByteSize + SplitFile bool + MaxRecordedErrors int64 + Detached bool + DisableTiKVImportMode bool + MaxEngineSize config.ByteSize + CloudStorageURI string + + // used for checksum in physical mode + DistSQLScanConcurrency int + + // todo: remove it when load data code is reverted. + InImportInto bool + // only initialized for IMPORT INTO, used when creating job. + Parameters *ImportParameters `json:"-"` + // the user who executes the statement, in the form of user@host + // only initialized for IMPORT INTO + User string `json:"-"` + + IsRaftKV2 bool + // total data file size in bytes. + TotalFileSize int64 +} + +// ASTArgs is the arguments for ast.LoadDataStmt. +// TODO: remove this struct and use the struct which can be serialized. +type ASTArgs struct { + FileLocRef ast.FileLocRefTp + ColumnsAndUserVars []*ast.ColumnNameOrUserVar + ColumnAssignments []*ast.Assignment + OnDuplicate ast.OnDuplicateKeyHandlingType + FieldsInfo *ast.FieldsClause + LinesInfo *ast.LinesClause +} + +// LoadDataController load data controller. +// todo: need a better name +type LoadDataController struct { + *Plan + *ASTArgs + + // used for sync column assignment expression generation. + colAssignMu sync.Mutex + + Table table.Table + + // how input field(or input column) from data file is mapped, either to a column or variable. + // if there's NO column list clause in SQL statement, then it's table's columns + // else it's user defined list. + FieldMappings []*FieldMapping + // see InsertValues.InsertColumns + // Note: our behavior is different with mysql. such as for table t(a,b) + // - "...(a,a) set a=100" is allowed in mysql, but not in tidb + // - "...(a,b) set b=100" will set b=100 in mysql, but in tidb the set is ignored. + // - ref columns in set clause is allowed in mysql, but not in tidb + InsertColumns []*table.Column + + logger *zap.Logger + dataStore storage.ExternalStorage + dataFiles []*mydump.SourceFileMeta + // GlobalSortStore is used to store sorted data when using global sort. + GlobalSortStore storage.ExternalStorage +} + +func getImportantSysVars(sctx sessionctx.Context) map[string]string { + res := map[string]string{} + for k, defVal := range common.DefaultImportantVariables { + if val, ok := sctx.GetSessionVars().GetSystemVar(k); ok { + res[k] = val + } else { + res[k] = defVal + } + } + for k, defVal := range common.DefaultImportVariablesTiDB { + if val, ok := sctx.GetSessionVars().GetSystemVar(k); ok { + res[k] = val + } else { + res[k] = defVal + } + } + return res +} + +// NewPlanFromLoadDataPlan creates a import plan from LOAD DATA. +func NewPlanFromLoadDataPlan(userSctx sessionctx.Context, plan *plannercore.LoadData) (*Plan, error) { + fullTableName := common.UniqueTable(plan.Table.Schema.L, plan.Table.Name.L) + logger := log.L().With(zap.String("table", fullTableName)) + charset := plan.Charset + if charset == nil { + // https://dev.mysql.com/doc/refman/8.0/en/load-data.html#load-data-character-set + d, err2 := userSctx.GetSessionVars().GetSessionOrGlobalSystemVar( + context.Background(), variable.CharsetDatabase) + if err2 != nil { + logger.Error("LOAD DATA get charset failed", zap.Error(err2)) + } else { + charset = &d + } + } + restrictive := userSctx.GetSessionVars().SQLMode.HasStrictMode() && + plan.OnDuplicate != ast.OnDuplicateKeyHandlingIgnore + + var ignoreLines uint64 + if plan.IgnoreLines != nil { + ignoreLines = *plan.IgnoreLines + } + + var ( + nullDef []string + nullValueOptEnclosed = false + ) + + lineFieldsInfo := plannercore.NewLineFieldsInfo(plan.FieldsInfo, plan.LinesInfo) + // todo: move null defined into plannercore.LineFieldsInfo + // in load data, there maybe multiple null def, but in SELECT ... INTO OUTFILE there's only one + if plan.FieldsInfo != nil && plan.FieldsInfo.DefinedNullBy != nil { + nullDef = append(nullDef, *plan.FieldsInfo.DefinedNullBy) + nullValueOptEnclosed = plan.FieldsInfo.NullValueOptEnclosed + } else if len(lineFieldsInfo.FieldsEnclosedBy) != 0 { + nullDef = append(nullDef, "NULL") + } + if len(lineFieldsInfo.FieldsEscapedBy) != 0 { + nullDef = append(nullDef, string([]byte{lineFieldsInfo.FieldsEscapedBy[0], 'N'})) + } + + return &Plan{ + DBName: plan.Table.Schema.O, + DBID: plan.Table.DBInfo.ID, + + Path: plan.Path, + Format: DataFormatDelimitedData, + Restrictive: restrictive, + FieldNullDef: nullDef, + NullValueOptEnclosed: nullValueOptEnclosed, + LineFieldsInfo: lineFieldsInfo, + IgnoreLines: ignoreLines, + + SQLMode: userSctx.GetSessionVars().SQLMode, + Charset: charset, + ImportantSysVars: getImportantSysVars(userSctx), + + DistSQLScanConcurrency: userSctx.GetSessionVars().DistSQLScanConcurrency(), + }, nil +} + +// NewImportPlan creates a new import into plan. +func NewImportPlan(userSctx sessionctx.Context, plan *plannercore.ImportInto, tbl table.Table) (*Plan, error) { + var format string + if plan.Format != nil { + format = strings.ToLower(*plan.Format) + } else { + // without FORMAT 'xxx' clause, default to CSV + format = DataFormatCSV + } + restrictive := userSctx.GetSessionVars().SQLMode.HasStrictMode() + // those are the default values for lightning CSV format too + lineFieldsInfo := plannercore.LineFieldsInfo{ + FieldsTerminatedBy: `,`, + FieldsEnclosedBy: `"`, + FieldsEscapedBy: `\`, + LinesStartingBy: ``, + // csv_parser will determine it automatically(either '\r' or '\n' or '\r\n') + // But user cannot set this to empty explicitly. + LinesTerminatedBy: ``, + } + + p := &Plan{ + TableInfo: tbl.Meta(), + DesiredTableInfo: tbl.Meta(), + DBName: plan.Table.Schema.O, + DBID: plan.Table.DBInfo.ID, + + Path: plan.Path, + Format: format, + Restrictive: restrictive, + FieldNullDef: []string{`\N`}, + LineFieldsInfo: lineFieldsInfo, + + SQLMode: userSctx.GetSessionVars().SQLMode, + ImportantSysVars: getImportantSysVars(userSctx), + + DistSQLScanConcurrency: userSctx.GetSessionVars().DistSQLScanConcurrency(), + InImportInto: true, + User: userSctx.GetSessionVars().User.String(), + } + if err := p.initOptions(userSctx, plan.Options); err != nil { + return nil, err + } + if err := p.initParameters(plan); err != nil { + return nil, err + } + return p, nil +} + +// InitTiKVConfigs initializes some TiKV related configs. +func (p *Plan) InitTiKVConfigs(ctx context.Context, sctx sessionctx.Context) error { + isRaftKV2, err := util.IsRaftKv2(ctx, sctx) + if err != nil { + return err + } + p.IsRaftKV2 = isRaftKV2 + return nil +} + +// ASTArgsFromPlan creates ASTArgs from plan. +func ASTArgsFromPlan(plan *plannercore.LoadData) *ASTArgs { + return &ASTArgs{ + FileLocRef: plan.FileLocRef, + ColumnsAndUserVars: plan.ColumnsAndUserVars, + ColumnAssignments: plan.ColumnAssignments, + OnDuplicate: plan.OnDuplicate, + FieldsInfo: plan.FieldsInfo, + LinesInfo: plan.LinesInfo, + } +} + +// ASTArgsFromImportPlan creates ASTArgs from plan. +func ASTArgsFromImportPlan(plan *plannercore.ImportInto) *ASTArgs { + // FileLocRef are not used in ImportIntoStmt, OnDuplicate not used now. + return &ASTArgs{ + FileLocRef: ast.FileLocServerOrRemote, + ColumnsAndUserVars: plan.ColumnsAndUserVars, + ColumnAssignments: plan.ColumnAssignments, + OnDuplicate: ast.OnDuplicateKeyHandlingReplace, + } +} + +// ASTArgsFromStmt creates ASTArgs from statement. +func ASTArgsFromStmt(stmt string) (*ASTArgs, error) { + stmtNode, err := parser.New().ParseOneStmt(stmt, "", "") + if err != nil { + return nil, err + } + importIntoStmt, ok := stmtNode.(*ast.ImportIntoStmt) + if !ok { + return nil, errors.Errorf("stmt %s is not import into stmt", stmt) + } + // FileLocRef are not used in ImportIntoStmt, OnDuplicate not used now. + return &ASTArgs{ + FileLocRef: ast.FileLocServerOrRemote, + ColumnsAndUserVars: importIntoStmt.ColumnsAndUserVars, + ColumnAssignments: importIntoStmt.ColumnAssignments, + OnDuplicate: ast.OnDuplicateKeyHandlingReplace, + }, nil +} + +// NewLoadDataController create new controller. +func NewLoadDataController(plan *Plan, tbl table.Table, astArgs *ASTArgs) (*LoadDataController, error) { + fullTableName := tbl.Meta().Name.String() + logger := log.L().With(zap.String("table", fullTableName)) + c := &LoadDataController{ + Plan: plan, + ASTArgs: astArgs, + Table: tbl, + logger: logger, + } + if err := c.checkFieldParams(); err != nil { + return nil, err + } + + columnNames := c.initFieldMappings() + if err := c.initLoadColumns(columnNames); err != nil { + return nil, err + } + return c, nil +} + +func (e *LoadDataController) checkFieldParams() error { + if e.Path == "" { + return exeerrors.ErrLoadDataEmptyPath + } + if e.InImportInto { + if e.Format != DataFormatCSV && e.Format != DataFormatParquet && e.Format != DataFormatSQL { + return exeerrors.ErrLoadDataUnsupportedFormat.GenWithStackByArgs(e.Format) + } + } else { + if e.NullValueOptEnclosed && len(e.FieldsEnclosedBy) == 0 { + return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("must specify FIELDS [OPTIONALLY] ENCLOSED BY when use NULL DEFINED BY OPTIONALLY ENCLOSED") + } + // NOTE: IMPORT INTO also don't support user set empty LinesTerminatedBy or FieldsTerminatedBy, + // but it's check in initOptions. + if len(e.LinesTerminatedBy) == 0 { + return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("LINES TERMINATED BY is empty") + } + // see https://github.com/pingcap/tidb/issues/33298 + if len(e.FieldsTerminatedBy) == 0 { + return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("load data with empty field terminator") + } + } + if len(e.FieldsEnclosedBy) > 0 && + (strings.HasPrefix(e.FieldsEnclosedBy, e.FieldsTerminatedBy) || strings.HasPrefix(e.FieldsTerminatedBy, e.FieldsEnclosedBy)) { + return exeerrors.ErrLoadDataWrongFormatConfig.GenWithStackByArgs("FIELDS ENCLOSED BY and TERMINATED BY must not be prefix of each other") + } + + return nil +} + +func (p *Plan) initDefaultOptions() { + threadCnt := runtime.GOMAXPROCS(0) + failpoint.Inject("mockNumCpu", func(val failpoint.Value) { + threadCnt = val.(int) + }) + threadCnt = int(math.Max(1, float64(threadCnt)*0.5)) + + p.Checksum = config.OpLevelRequired + p.ThreadCnt = int64(threadCnt) + p.MaxWriteSpeed = unlimitedWriteSpeed + p.SplitFile = false + p.MaxRecordedErrors = 100 + p.Detached = false + p.DisableTiKVImportMode = false + p.MaxEngineSize = config.ByteSize(defaultMaxEngineSize) + p.CloudStorageURI = variable.CloudStorageURI.Load() + + v := "utf8mb4" + p.Charset = &v +} + +func (p *Plan) initOptions(seCtx sessionctx.Context, options []*plannercore.LoadDataOpt) error { + p.initDefaultOptions() + + specifiedOptions := map[string]*plannercore.LoadDataOpt{} + for _, opt := range options { + hasValue, ok := supportedOptions[opt.Name] + if !ok { + return exeerrors.ErrUnknownOption.FastGenByArgs(opt.Name) + } + if hasValue && opt.Value == nil || !hasValue && opt.Value != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + if _, ok = specifiedOptions[opt.Name]; ok { + return exeerrors.ErrDuplicateOption.FastGenByArgs(opt.Name) + } + specifiedOptions[opt.Name] = opt + } + + if p.Format != DataFormatCSV { + for k := range csvOnlyOptions { + if _, ok := specifiedOptions[k]; ok { + return exeerrors.ErrLoadDataUnsupportedOption.FastGenByArgs(k, "non-CSV format") + } + } + } + + optAsString := func(opt *plannercore.LoadDataOpt) (string, error) { + if opt.Value.GetType().GetType() != mysql.TypeVarString { + return "", exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + val, isNull, err2 := opt.Value.EvalString(seCtx, chunk.Row{}) + if err2 != nil || isNull { + return "", exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + return val, nil + } + optAsInt64 := func(opt *plannercore.LoadDataOpt) (int64, error) { + // current parser takes integer and bool as mysql.TypeLonglong + if opt.Value.GetType().GetType() != mysql.TypeLonglong || mysql.HasIsBooleanFlag(opt.Value.GetType().GetFlag()) { + return 0, exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + val, isNull, err2 := opt.Value.EvalInt(seCtx, chunk.Row{}) + if err2 != nil || isNull { + return 0, exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + return val, nil + } + if opt, ok := specifiedOptions[characterSetOption]; ok { + v, err := optAsString(opt) + if err != nil || v == "" { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + _, err = config.ParseCharset(v) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.Charset = &v + } + if opt, ok := specifiedOptions[fieldsTerminatedByOption]; ok { + v, err := optAsString(opt) + if err != nil || v == "" { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.FieldsTerminatedBy = v + } + if opt, ok := specifiedOptions[fieldsEnclosedByOption]; ok { + v, err := optAsString(opt) + if err != nil || len(v) > 1 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.FieldsEnclosedBy = v + } + if opt, ok := specifiedOptions[fieldsEscapedByOption]; ok { + v, err := optAsString(opt) + if err != nil || len(v) > 1 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.FieldsEscapedBy = v + } + if opt, ok := specifiedOptions[fieldsDefinedNullByOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.FieldNullDef = []string{v} + } + if opt, ok := specifiedOptions[linesTerminatedByOption]; ok { + v, err := optAsString(opt) + // cannot set terminator to empty string explicitly + if err != nil || v == "" { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.LinesTerminatedBy = v + } + if opt, ok := specifiedOptions[skipRowsOption]; ok { + vInt, err := optAsInt64(opt) + if err != nil || vInt < 0 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.IgnoreLines = uint64(vInt) + } + if _, ok := specifiedOptions[splitFileOption]; ok { + p.SplitFile = true + } + if opt, ok := specifiedOptions[diskQuotaOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + if err = p.DiskQuota.UnmarshalText([]byte(v)); err != nil || p.DiskQuota <= 0 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + } + if opt, ok := specifiedOptions[threadOption]; ok { + vInt, err := optAsInt64(opt) + if err != nil || vInt <= 0 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.ThreadCnt = vInt + } + if opt, ok := specifiedOptions[maxWriteSpeedOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + if err = p.MaxWriteSpeed.UnmarshalText([]byte(v)); err != nil || p.MaxWriteSpeed < 0 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + } + if opt, ok := specifiedOptions[checksumTableOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + if err = p.Checksum.FromStringValue(v); err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + } + if opt, ok := specifiedOptions[recordErrorsOption]; ok { + vInt, err := optAsInt64(opt) + if err != nil || vInt < -1 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + p.MaxRecordedErrors = vInt + // todo: set a max value for this param? + } + if _, ok := specifiedOptions[detachedOption]; ok { + p.Detached = true + } + if _, ok := specifiedOptions[disableTiKVImportModeOption]; ok { + p.DisableTiKVImportMode = true + } + if opt, ok := specifiedOptions[cloudStorageURIOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + // set cloud storage uri to empty string to force uses local sort when + // the global variable is set. + if v != "" { + b, err := storage.ParseBackend(v, nil) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + // only support s3 and gcs now. + if b.GetS3() == nil && b.GetGcs() == nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + } + p.CloudStorageURI = v + } + if opt, ok := specifiedOptions[maxEngineSizeOption]; ok { + v, err := optAsString(opt) + if err != nil { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + if err = p.MaxEngineSize.UnmarshalText([]byte(v)); err != nil || p.MaxEngineSize < 0 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs(opt.Name) + } + } + + // when split-file is set, data file will be split into chunks of 256 MiB. + // skip_rows should be 0 or 1, we add this restriction to simplify skip_rows + // logic, so we only need to skip on the first chunk for each data file. + // CSV parser limit each row size to LargestEntryLimit(120M), the first row + // will NOT cross file chunk. + if p.SplitFile && p.IgnoreLines > 1 { + return exeerrors.ErrInvalidOptionVal.FastGenByArgs("skip_rows, should be <= 1 when split-file is enabled") + } + + p.adjustOptions() + return nil +} + +func (p *Plan) adjustOptions() { + // max value is cpu-count + numCPU := int64(runtime.GOMAXPROCS(0)) + if p.ThreadCnt > numCPU { + log.L().Info("IMPORT INTO thread count is larger than cpu-count, set to cpu-count") + p.ThreadCnt = numCPU + } +} + +func (p *Plan) initParameters(plan *plannercore.ImportInto) error { + redactURL := ast.RedactURL(p.Path) + var columnsAndVars, setClause string + var sb strings.Builder + formatCtx := pformat.NewRestoreCtx(pformat.DefaultRestoreFlags, &sb) + if len(plan.ColumnsAndUserVars) > 0 { + sb.WriteString("(") + for i, col := range plan.ColumnsAndUserVars { + if i > 0 { + sb.WriteString(", ") + } + _ = col.Restore(formatCtx) + } + sb.WriteString(")") + columnsAndVars = sb.String() + } + if len(plan.ColumnAssignments) > 0 { + sb.Reset() + for i, assign := range plan.ColumnAssignments { + if i > 0 { + sb.WriteString(", ") + } + _ = assign.Restore(formatCtx) + } + setClause = sb.String() + } + optionMap := make(map[string]interface{}, len(plan.Options)) + for _, opt := range plan.Options { + if opt.Value != nil { + val := opt.Value.String() + if opt.Name == cloudStorageURIOption { + val = ast.RedactURL(val) + } + optionMap[opt.Name] = val + } else { + optionMap[opt.Name] = nil + } + } + p.Parameters = &ImportParameters{ + ColumnsAndVars: columnsAndVars, + SetClause: setClause, + FileLocation: redactURL, + Format: p.Format, + Options: optionMap, + } + return nil +} + +// initFieldMappings make a field mapping slice to implicitly map input field to table column or user defined variable +// the slice's order is the same as the order of the input fields. +// Returns a slice of same ordered column names without user defined variable names. +func (e *LoadDataController) initFieldMappings() []string { + columns := make([]string, 0, len(e.ColumnsAndUserVars)+len(e.ColumnAssignments)) + tableCols := e.Table.VisibleCols() + + if len(e.ColumnsAndUserVars) == 0 { + for _, v := range tableCols { + // Data for generated column is generated from the other rows rather than from the parsed data. + fieldMapping := &FieldMapping{ + Column: v, + } + e.FieldMappings = append(e.FieldMappings, fieldMapping) + columns = append(columns, v.Name.O) + } + + return columns + } + + var column *table.Column + + for _, v := range e.ColumnsAndUserVars { + if v.ColumnName != nil { + column = table.FindCol(tableCols, v.ColumnName.Name.O) + columns = append(columns, v.ColumnName.Name.O) + } else { + column = nil + } + + fieldMapping := &FieldMapping{ + Column: column, + UserVar: v.UserVar, + } + e.FieldMappings = append(e.FieldMappings, fieldMapping) + } + + return columns +} + +// initLoadColumns sets columns which the input fields loaded to. +func (e *LoadDataController) initLoadColumns(columnNames []string) error { + var cols []*table.Column + var missingColName string + var err error + tableCols := e.Table.VisibleCols() + + if len(columnNames) != len(tableCols) { + for _, v := range e.ColumnAssignments { + columnNames = append(columnNames, v.Column.Name.O) + } + } + + cols, missingColName = table.FindCols(tableCols, columnNames, e.Table.Meta().PKIsHandle) + if missingColName != "" { + return dbterror.ErrBadField.GenWithStackByArgs(missingColName, "field list") + } + + e.InsertColumns = append(e.InsertColumns, cols...) + + // e.InsertColumns is appended according to the original tables' column sequence. + // We have to reorder it to follow the use-specified column order which is shown in the columnNames. + if err = e.reorderColumns(columnNames); err != nil { + return err + } + + // Check column whether is specified only once. + err = table.CheckOnce(cols) + if err != nil { + return err + } + + return nil +} + +// reorderColumns reorder the e.InsertColumns according to the order of columnNames +// Note: We must ensure there must be one-to-one mapping between e.InsertColumns and columnNames in terms of column name. +func (e *LoadDataController) reorderColumns(columnNames []string) error { + cols := e.InsertColumns + + if len(cols) != len(columnNames) { + return exeerrors.ErrColumnsNotMatched + } + + reorderedColumns := make([]*table.Column, len(cols)) + + if columnNames == nil { + return nil + } + + mapping := make(map[string]int) + for idx, colName := range columnNames { + mapping[strings.ToLower(colName)] = idx + } + + for _, col := range cols { + idx := mapping[col.Name.L] + reorderedColumns[idx] = col + } + + e.InsertColumns = reorderedColumns + + return nil +} + +// GetFieldCount get field count. +func (e *LoadDataController) GetFieldCount() int { + return len(e.FieldMappings) +} + +// GenerateCSVConfig generates a CSV config for parser from LoadDataWorker. +func (e *LoadDataController) GenerateCSVConfig() *config.CSVConfig { + csvConfig := &config.CSVConfig{ + Separator: e.FieldsTerminatedBy, + // ignore optionally enclosed + Delimiter: e.FieldsEnclosedBy, + Terminator: e.LinesTerminatedBy, + NotNull: false, + Null: e.FieldNullDef, + Header: false, + TrimLastSep: false, + EscapedBy: e.FieldsEscapedBy, + StartingBy: e.LinesStartingBy, + } + if !e.InImportInto { + // for load data + csvConfig.AllowEmptyLine = true + csvConfig.QuotedNullIsText = !e.NullValueOptEnclosed + csvConfig.UnescapedQuote = true + } + return csvConfig +} + +// InitDataStore initializes the data store. +func (e *LoadDataController) InitDataStore(ctx context.Context) error { + u, err2 := storage.ParseRawURL(e.Path) + if err2 != nil { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + err2.Error()) + } + + if storage.IsLocal(u) { + u.Path = filepath.Dir(e.Path) + } else { + u.Path = "" + } + s, err := e.initExternalStore(ctx, u, plannercore.ImportIntoDataSource) + if err != nil { + return err + } + e.dataStore = s + + if e.IsGlobalSort() { + target := "cloud storage" + cloudStorageURL, err3 := storage.ParseRawURL(e.Plan.CloudStorageURI) + if err3 != nil { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(target, + err3.Error()) + } + s, err = e.initExternalStore(ctx, cloudStorageURL, target) + if err != nil { + return err + } + e.GlobalSortStore = s + } + return nil +} +func (*LoadDataController) initExternalStore(ctx context.Context, u *url.URL, target string) (storage.ExternalStorage, error) { + b, err2 := storage.ParseBackendFromURL(u, nil) + if err2 != nil { + return nil, exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(target, GetMsgFromBRError(err2)) + } + + opt := &storage.ExternalStorageOptions{} + if intest.InTest { + opt.NoCredentials = true + } + s, err := storage.New(ctx, b, opt) + if err != nil { + return nil, exeerrors.ErrLoadDataCantAccess.GenWithStackByArgs(target, GetMsgFromBRError(err)) + } + return s, nil +} + +// InitDataFiles initializes the data store and files. +// it will call InitDataStore internally. +func (e *LoadDataController) InitDataFiles(ctx context.Context) error { + u, err2 := storage.ParseRawURL(e.Path) + if err2 != nil { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + err2.Error()) + } + + var fileNameKey string + if storage.IsLocal(u) { + // LOAD DATA don't support server file. + if !e.InImportInto { + return exeerrors.ErrLoadDataFromServerDisk.GenWithStackByArgs(e.Path) + } + + if !filepath.IsAbs(e.Path) { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + "file location should be absolute path when import from server disk") + } + // we add this check for security, we don't want user import any sensitive system files, + // most of which is readable text file and don't have a suffix, such as /etc/passwd + if !slices.Contains([]string{".csv", ".sql", ".parquet"}, strings.ToLower(filepath.Ext(e.Path))) { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + "the file suffix is not supported when import from server disk") + } + dir := filepath.Dir(e.Path) + _, err := os.Stat(dir) + if err != nil { + // permission denied / file not exist error, etc. + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + err.Error()) + } + + fileNameKey = filepath.Base(e.Path) + } else { + fileNameKey = strings.Trim(u.Path, "/") + } + // try to find pattern error in advance + _, err2 = filepath.Match(stringutil.EscapeGlobExceptAsterisk(fileNameKey), "") + if err2 != nil { + return exeerrors.ErrLoadDataInvalidURI.GenWithStackByArgs(plannercore.ImportIntoDataSource, + "Glob pattern error: "+err2.Error()) + } + + if err2 = e.InitDataStore(ctx); err2 != nil { + return err2 + } + + s := e.dataStore + var totalSize int64 + dataFiles := []*mydump.SourceFileMeta{} + idx := strings.IndexByte(fileNameKey, '*') + // simple path when the path represent one file + sourceType := e.getSourceType() + if idx == -1 { + fileReader, err2 := s.Open(ctx, fileNameKey, nil) + if err2 != nil { + return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "Please check the file location is correct") + } + defer func() { + terror.Log(fileReader.Close()) + }() + size, err3 := fileReader.Seek(0, io.SeekEnd) + if err3 != nil { + return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "failed to read file size by seek") + } + compressTp := mydump.ParseCompressionOnFileExtension(fileNameKey) + fileMeta := mydump.SourceFileMeta{ + Path: fileNameKey, + FileSize: size, + Compression: compressTp, + Type: sourceType, + } + fileMeta.RealSize = e.getFileRealSize(ctx, fileMeta, s) + dataFiles = append(dataFiles, &fileMeta) + totalSize = size + } else { + var commonPrefix string + if !storage.IsLocal(u) { + // for local directory, we're walking the parent directory, + // so we don't have a common prefix as cloud storage do. + commonPrefix = fileNameKey[:idx] + } + // when import from server disk, all entries in parent directory should have READ + // access, else walkDir will fail + // we only support '*', in order to reuse glob library manually escape the path + escapedPath := stringutil.EscapeGlobExceptAsterisk(fileNameKey) + err := s.WalkDir(ctx, &storage.WalkOption{ObjPrefix: commonPrefix, SkipSubDir: true}, + func(remotePath string, size int64) error { + // we have checked in LoadDataExec.Next + //nolint: errcheck + match, _ := filepath.Match(escapedPath, remotePath) + if !match { + return nil + } + compressTp := mydump.ParseCompressionOnFileExtension(remotePath) + fileMeta := mydump.SourceFileMeta{ + Path: remotePath, + FileSize: size, + Compression: compressTp, + Type: sourceType, + } + fileMeta.RealSize = e.getFileRealSize(ctx, fileMeta, s) + dataFiles = append(dataFiles, &fileMeta) + totalSize += size + return nil + }) + if err != nil { + return exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err), "failed to walk dir") + } + } + + e.dataFiles = dataFiles + e.TotalFileSize = totalSize + return nil +} + +func (e *LoadDataController) getFileRealSize(ctx context.Context, + fileMeta mydump.SourceFileMeta, store storage.ExternalStorage) int64 { + if fileMeta.Compression == mydump.CompressionNone { + return fileMeta.FileSize + } + compressRatio, err := mydump.SampleFileCompressRatio(ctx, fileMeta, store) + if err != nil { + e.logger.Warn("failed to get compress ratio", zap.String("file", fileMeta.Path), zap.Error(err)) + return fileMeta.FileSize + } + return int64(compressRatio * float64(fileMeta.FileSize)) +} + +func (e *LoadDataController) getSourceType() mydump.SourceType { + switch e.Format { + case DataFormatParquet: + return mydump.SourceTypeParquet + case DataFormatDelimitedData, DataFormatCSV: + return mydump.SourceTypeCSV + default: + // DataFormatSQL + return mydump.SourceTypeSQL + } +} + +// GetLoadDataReaderInfos returns the LoadDataReaderInfo for each data file. +func (e *LoadDataController) GetLoadDataReaderInfos() []LoadDataReaderInfo { + result := make([]LoadDataReaderInfo, 0, len(e.dataFiles)) + for i := range e.dataFiles { + f := e.dataFiles[i] + result = append(result, LoadDataReaderInfo{ + Opener: func(ctx context.Context) (io.ReadSeekCloser, error) { + fileReader, err2 := mydump.OpenReader(ctx, f, e.dataStore, storage.DecompressConfig{}) + if err2 != nil { + return nil, exeerrors.ErrLoadDataCantRead.GenWithStackByArgs(GetMsgFromBRError(err2), "Please check the INFILE path is correct") + } + return fileReader, nil + }, + Remote: f, + }) + } + return result +} + +// GetParser returns a parser for the data file. +func (e *LoadDataController) GetParser( + ctx context.Context, + dataFileInfo LoadDataReaderInfo, +) (parser mydump.Parser, err error) { + reader, err2 := dataFileInfo.Opener(ctx) + if err2 != nil { + return nil, err2 + } + defer func() { + if err != nil { + if err3 := reader.Close(); err3 != nil { + e.logger.Warn("failed to close reader", zap.Error(err3)) + } + } + }() + switch e.Format { + case DataFormatDelimitedData, DataFormatCSV: + var charsetConvertor *mydump.CharsetConvertor + if e.Charset != nil { + charsetConvertor, err = mydump.NewCharsetConvertor(*e.Charset, string(utf8.RuneError)) + if err != nil { + return nil, err + } + } + if err != nil { + return nil, err + } + parser, err = mydump.NewCSVParser( + ctx, + e.GenerateCSVConfig(), + reader, + LoadDataReadBlockSize, + nil, + false, + charsetConvertor) + case DataFormatSQL: + parser = mydump.NewChunkParser( + ctx, + e.SQLMode, + reader, + LoadDataReadBlockSize, + nil, + ) + case DataFormatParquet: + parser, err = mydump.NewParquetParser( + ctx, + e.dataStore, + reader, + dataFileInfo.Remote.Path, + ) + } + if err != nil { + return nil, exeerrors.ErrLoadDataWrongFormatConfig.GenWithStack(err.Error()) + } + parser.SetLogger(litlog.Logger{Logger: logutil.Logger(ctx)}) + + return parser, nil +} + +// HandleSkipNRows skips the first N rows of the data file. +func (e *LoadDataController) HandleSkipNRows(parser mydump.Parser) error { + // handle IGNORE N LINES + ignoreOneLineFn := parser.ReadRow + if csvParser, ok := parser.(*mydump.CSVParser); ok { + ignoreOneLineFn = func() error { + _, _, err3 := csvParser.ReadUntilTerminator() + return err3 + } + } + + ignoreLineCnt := e.IgnoreLines + for ignoreLineCnt > 0 { + err := ignoreOneLineFn() + if err != nil { + if errors.Cause(err) == io.EOF { + return nil + } + return err + } + + ignoreLineCnt-- + } + return nil +} + +func (e *LoadDataController) toMyDumpFiles() []mydump.FileInfo { + tbl := filter.Table{ + Schema: e.DBName, + Name: e.Table.Meta().Name.O, + } + res := []mydump.FileInfo{} + for _, f := range e.dataFiles { + res = append(res, mydump.FileInfo{ + TableName: tbl, + FileMeta: *f, + }) + } + return res +} + +// IsLocalSort returns true if we sort data on local disk. +func (e *LoadDataController) IsLocalSort() bool { + return e.Plan.CloudStorageURI == "" +} + +// IsGlobalSort returns true if we sort data on global storage. +func (e *LoadDataController) IsGlobalSort() bool { + return !e.IsLocalSort() +} + +// CreateColAssignExprs creates the column assignment expressions using session context. +// RewriteAstExpr will write ast node in place(due to xxNode.Accept), but it doesn't change node content, +// so we sync it. +func (e *LoadDataController) CreateColAssignExprs(sctx sessionctx.Context) ([]expression.Expression, []stmtctx.SQLWarn, error) { + e.colAssignMu.Lock() + defer e.colAssignMu.Unlock() + res := make([]expression.Expression, 0, len(e.ColumnAssignments)) + allWarnings := []stmtctx.SQLWarn{} + for _, assign := range e.ColumnAssignments { + newExpr, err := expression.RewriteAstExpr(sctx, assign.Expr, nil, nil, false) + // col assign expr warnings is static, we should generate it for each row processed. + // so we save it and clear it here. + allWarnings = append(allWarnings, sctx.GetSessionVars().StmtCtx.GetWarnings()...) + sctx.GetSessionVars().StmtCtx.SetWarnings(nil) + if err != nil { + return nil, nil, err + } + res = append(res, newExpr) + } + return res, allWarnings, nil +} + +func (e *LoadDataController) getLocalBackendCfg(pdAddr, dataDir string) local.BackendConfig { + backendConfig := local.BackendConfig{ + PDAddr: pdAddr, + LocalStoreDir: dataDir, + MaxConnPerStore: config.DefaultRangeConcurrency, + ConnCompressType: config.CompressionNone, + WorkerConcurrency: config.DefaultRangeConcurrency * 2, + KVWriteBatchSize: config.KVWriteBatchSize, + RegionSplitBatchSize: config.DefaultRegionSplitBatchSize, + RegionSplitConcurrency: runtime.GOMAXPROCS(0), + // enable after we support checkpoint + CheckpointEnabled: false, + MemTableSize: config.DefaultEngineMemCacheSize, + LocalWriterMemCacheSize: int64(config.DefaultLocalWriterMemCacheSize), + ShouldCheckTiKV: true, + DupeDetectEnabled: false, + DuplicateDetectOpt: common.DupDetectOpt{ReportErrOnDup: false}, + StoreWriteBWLimit: int(e.MaxWriteSpeed), + MaxOpenFiles: int(tidbutil.GenRLimit("table_import")), + KeyspaceName: tidb.GetGlobalKeyspaceName(), + PausePDSchedulerScope: config.PausePDSchedulerScopeTable, + DisableAutomaticCompactions: true, + } + if e.IsRaftKV2 { + backendConfig.RaftKV2SwitchModeDuration = config.DefaultSwitchTiKVModeInterval + } + return backendConfig +} + +// JobImportParam is the param of the job import. +type JobImportParam struct { + Job *asyncloaddata.Job + Group *errgroup.Group + GroupCtx context.Context + // should be closed in the end of the job. + Done chan struct{} + + Progress *asyncloaddata.Progress +} + +// JobImportResult is the result of the job import. +type JobImportResult struct { + Affected uint64 + Warnings []stmtctx.SQLWarn + ColSizeMap map[int64]int64 +} + +// JobImporter is the interface for importing a job. +type JobImporter interface { + // Param returns the param of the job import. + Param() *JobImportParam + // Import imports the job. + // import should run in routines using param.Group, when import finished, it should close param.Done. + // during import, we should use param.GroupCtx, so this method has no context param. + Import() + // Result returns the result of the job import. + Result() JobImportResult + io.Closer +} + +// GetMsgFromBRError get msg from BR error. +// TODO: add GetMsg() to errors package to replace this function. +// see TestGetMsgFromBRError for more details. +func GetMsgFromBRError(err error) string { + if err == nil { + return "" + } + if berr, ok := err.(*errors.Error); ok { + return berr.GetMsg() + } + raw := err.Error() + berrMsg := errors.Cause(err).Error() + if len(raw) <= len(berrMsg)+len(": ") { + return raw + } + return raw[:len(raw)-len(berrMsg)-len(": ")] +} + +// TestSyncCh is used in unit test to synchronize the execution. +var TestSyncCh = make(chan struct{}) diff --git a/executor/importer/import_test.go b/pkg/executor/importer/import_test.go similarity index 94% rename from executor/importer/import_test.go rename to pkg/executor/importer/import_test.go index 93b62a8e5cf02..baf3c0e25178d 100644 --- a/executor/importer/import_test.go +++ b/pkg/executor/importer/import_test.go @@ -28,20 +28,20 @@ import ( berrors "github.com/pingcap/tidb/br/pkg/errors" "github.com/pingcap/tidb/br/pkg/lightning/config" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) func TestInitDefaultOptions(t *testing.T) { plan := &Plan{} - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/importer/mockNumCpu", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/importer/mockNumCpu", "return(1)")) variable.CloudStorageURI.Store("s3://bucket/path") t.Cleanup(func() { variable.CloudStorageURI.Store("") @@ -59,7 +59,7 @@ func TestInitDefaultOptions(t *testing.T) { require.Equal(t, config.ByteSize(defaultMaxEngineSize), plan.MaxEngineSize) require.Equal(t, "s3://bucket/path", plan.CloudStorageURI) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/importer/mockNumCpu", "return(10)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/importer/mockNumCpu", "return(10)")) plan.initDefaultOptions() require.Equal(t, int64(5), plan.ThreadCnt) } diff --git a/pkg/executor/importer/job.go b/pkg/executor/importer/job.go new file mode 100644 index 0000000000000..1ce3d9c6283d6 --- /dev/null +++ b/pkg/executor/importer/job.go @@ -0,0 +1,366 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package importer + +import ( + "context" + "encoding/json" + "fmt" + "sync/atomic" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/util" +) + +// vars used for test. +var ( + // TestLastImportJobID last created job id, used in unit test. + TestLastImportJobID atomic.Int64 +) + +// constants for job status and step. +const ( + // JobStatus + // ┌───────┐ ┌───────┐ ┌────────┐ + // │pending├────►│running├───►│finished│ + // └────┬──┘ └────┬──┘ └────────┘ + // │ │ ┌──────┐ + // │ ├──────►│failed│ + // │ │ └──────┘ + // │ │ ┌─────────┐ + // └─────────────┴──────►│cancelled│ + // └─────────┘ + jobStatusPending = "pending" + // JobStatusRunning exported since it's used in show import jobs + JobStatusRunning = "running" + jogStatusCancelled = "cancelled" + jobStatusFailed = "failed" + jobStatusFinished = "finished" + + // when the job is finished, step will be set to none. + jobStepNone = "" + // JobStepGlobalSorting is the first step when using global sort, + // step goes from none -> global-sorting -> importing -> validating -> none. + JobStepGlobalSorting = "global-sorting" + // JobStepImporting is the first step when using local sort, + // step goes from none -> importing -> validating -> none. + // when used in global sort, it means importing the sorted data. + // when used in local sort, it means encode&sort data and then importing the data. + JobStepImporting = "importing" + JobStepValidating = "validating" + + baseQuerySQL = `SELECT + id, create_time, start_time, end_time, + table_schema, table_name, table_id, created_by, parameters, source_file_size, + status, step, summary, error_message + FROM mysql.tidb_import_jobs` +) + +// ImportParameters is the parameters for import into statement. +// it's a minimal meta info to store in tidb_import_jobs for diagnose. +// for detailed info, see tidb_global_tasks. +type ImportParameters struct { + ColumnsAndVars string `json:"columns-and-vars,omitempty"` + SetClause string `json:"set-clause,omitempty"` + // for s3 URL, AK/SK is redacted for security + FileLocation string `json:"file-location"` + Format string `json:"format"` + // only include what user specified, not include default value. + Options map[string]interface{} `json:"options,omitempty"` +} + +var _ fmt.Stringer = &ImportParameters{} + +// String implements fmt.Stringer interface. +func (ip *ImportParameters) String() string { + b, _ := json.Marshal(ip) + return string(b) +} + +// JobSummary is the summary info of import into job. +type JobSummary struct { + // ImportedRows is the number of rows imported into TiKV. + ImportedRows uint64 `json:"imported-rows,omitempty"` +} + +// JobInfo is the information of import into job. +type JobInfo struct { + ID int64 + CreateTime types.Time + StartTime types.Time + EndTime types.Time + TableSchema string + TableName string + TableID int64 + CreatedBy string + Parameters ImportParameters + SourceFileSize int64 + Status string + // in SHOW IMPORT JOB, we name it as phase. + // here, we use the same name as in distributed framework. + Step string + // the summary info of the job, it's updated only when the job is finished. + // for running job, we should query the progress from the distributed framework. + Summary *JobSummary + ErrorMessage string +} + +// CanCancel returns whether the job can be cancelled. +func (j *JobInfo) CanCancel() bool { + return j.Status == jobStatusPending || j.Status == JobStatusRunning +} + +// GetJob returns the job with the given id if the user has privilege. +// hasSuperPriv: whether the user has super privilege. +// If the user has super privilege, the user can show or operate all jobs, +// else the user can only show or operate his own jobs. +func GetJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, user string, hasSuperPriv bool) (*JobInfo, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + + sql := baseQuerySQL + ` WHERE id = %?` + rs, err := conn.ExecuteInternal(ctx, sql, jobID) + if err != nil { + return nil, err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return nil, err + } + if len(rows) != 1 { + return nil, exeerrors.ErrLoadDataJobNotFound.GenWithStackByArgs(jobID) + } + + info, err := convert2JobInfo(rows[0]) + if err != nil { + return nil, err + } + if !hasSuperPriv && info.CreatedBy != user { + return nil, core.ErrSpecificAccessDenied.GenWithStackByArgs("SUPER") + } + return info, nil +} + +// GetActiveJobCnt returns the count of active import jobs. +// Active import jobs include pending and running jobs. +func GetActiveJobCnt(ctx context.Context, conn sqlexec.SQLExecutor) (int64, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + + sql := `select count(1) from mysql.tidb_import_jobs where status in (%?, %?)` + rs, err := conn.ExecuteInternal(ctx, sql, jobStatusPending, JobStatusRunning) + if err != nil { + return 0, err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return 0, err + } + cnt := rows[0].GetInt64(0) + return cnt, nil +} + +// CreateJob creates import into job by insert a record to system table. +// The AUTO_INCREMENT value will be returned as jobID. +func CreateJob( + ctx context.Context, + conn sqlexec.SQLExecutor, + db, table string, + tableID int64, + user string, + parameters *ImportParameters, + sourceFileSize int64, +) (int64, error) { + bytes, err := json.Marshal(parameters) + if err != nil { + return 0, err + } + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + _, err = conn.ExecuteInternal(ctx, `INSERT INTO mysql.tidb_import_jobs + (table_schema, table_name, table_id, created_by, parameters, source_file_size, status, step) + VALUES (%?, %?, %?, %?, %?, %?, %?, %?);`, + db, table, tableID, user, bytes, sourceFileSize, jobStatusPending, jobStepNone) + if err != nil { + return 0, err + } + rs, err := conn.ExecuteInternal(ctx, `SELECT LAST_INSERT_ID();`) + if err != nil { + return 0, err + } + defer terror.Call(rs.Close) + + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return 0, err + } + if len(rows) != 1 { + return 0, errors.Errorf("unexpected result length: %d", len(rows)) + } + + failpoint.Inject("setLastImportJobID", func() { + TestLastImportJobID.Store(rows[0].GetInt64(0)) + }) + return rows[0].GetInt64(0), nil +} + +// StartJob tries to start a pending job with jobID, change its status/step to running/input step. +// It will not return error when there's no matched job or the job has already started. +func StartJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, step string) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs + SET update_time = CURRENT_TIMESTAMP(6), start_time = CURRENT_TIMESTAMP(6), status = %?, step = %? + WHERE id = %? AND status = %?;`, + JobStatusRunning, step, jobID, jobStatusPending) + + return err +} + +// Job2Step tries to change the step of a running job with jobID. +// It will not return error when there's no matched job. +func Job2Step(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, step string) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs + SET update_time = CURRENT_TIMESTAMP(6), step = %? + WHERE id = %? AND status = %?;`, + step, jobID, JobStatusRunning) + + return err +} + +// FinishJob tries to finish a running job with jobID, change its status to finished, clear its step. +// It will not return error when there's no matched job. +func FinishJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, summary *JobSummary) error { + bytes, err := json.Marshal(summary) + if err != nil { + return err + } + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + _, err = conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs + SET update_time = CURRENT_TIMESTAMP(6), end_time = CURRENT_TIMESTAMP(6), status = %?, step = %?, summary = %? + WHERE id = %? AND status = %?;`, + jobStatusFinished, jobStepNone, bytes, jobID, JobStatusRunning) + return err +} + +// FailJob fails import into job. A job can only be failed once. +// It will not return error when there's no matched job. +func FailJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64, errorMsg string) error { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + _, err := conn.ExecuteInternal(ctx, `UPDATE mysql.tidb_import_jobs + SET update_time = CURRENT_TIMESTAMP(6), end_time = CURRENT_TIMESTAMP(6), status = %?, error_message = %? + WHERE id = %? AND status = %?;`, + jobStatusFailed, errorMsg, jobID, JobStatusRunning) + return err +} + +func convert2JobInfo(row chunk.Row) (*JobInfo, error) { + // start_time, end_time, summary, error_message can be NULL, need to use row.IsNull() to check. + startTime, endTime := types.ZeroTime, types.ZeroTime + if !row.IsNull(2) { + startTime = row.GetTime(2) + } + if !row.IsNull(3) { + endTime = row.GetTime(3) + } + + parameters := ImportParameters{} + parametersStr := row.GetString(8) + if err := json.Unmarshal([]byte(parametersStr), ¶meters); err != nil { + return nil, errors.Trace(err) + } + + var summary *JobSummary + var summaryStr string + if !row.IsNull(12) { + summaryStr = row.GetString(12) + } + if len(summaryStr) > 0 { + summary = &JobSummary{} + if err := json.Unmarshal([]byte(summaryStr), summary); err != nil { + return nil, errors.Trace(err) + } + } + + var errMsg string + if !row.IsNull(13) { + errMsg = row.GetString(13) + } + return &JobInfo{ + ID: row.GetInt64(0), + CreateTime: row.GetTime(1), + StartTime: startTime, + EndTime: endTime, + TableSchema: row.GetString(4), + TableName: row.GetString(5), + TableID: row.GetInt64(6), + CreatedBy: row.GetString(7), + Parameters: parameters, + SourceFileSize: row.GetInt64(9), + Status: row.GetString(10), + Step: row.GetString(11), + Summary: summary, + ErrorMessage: errMsg, + }, nil +} + +// GetAllViewableJobs gets all viewable jobs. +func GetAllViewableJobs(ctx context.Context, conn sqlexec.SQLExecutor, user string, hasSuperPriv bool) ([]*JobInfo, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + sql := baseQuerySQL + args := []interface{}{} + if !hasSuperPriv { + sql += " WHERE created_by = %?" + args = append(args, user) + } + rs, err := conn.ExecuteInternal(ctx, sql, args...) + if err != nil { + return nil, err + } + defer terror.Call(rs.Close) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + if err != nil { + return nil, err + } + ret := make([]*JobInfo, 0, len(rows)) + for _, row := range rows { + jobInfo, err2 := convert2JobInfo(row) + if err2 != nil { + return nil, err2 + } + ret = append(ret, jobInfo) + } + + return ret, nil +} + +// CancelJob cancels import into job. Only a running/paused job can be canceled. +// check privileges using get before calling this method. +func CancelJob(ctx context.Context, conn sqlexec.SQLExecutor, jobID int64) (err error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalImportInto) + sql := `UPDATE mysql.tidb_import_jobs + SET update_time = CURRENT_TIMESTAMP(6), status = %?, error_message = 'cancelled by user' + WHERE id = %? AND status IN (%?, %?);` + args := []interface{}{jogStatusCancelled, jobID, jobStatusPending, JobStatusRunning} + _, err = conn.ExecuteInternal(ctx, sql, args...) + return err +} diff --git a/executor/importer/job_test.go b/pkg/executor/importer/job_test.go similarity index 98% rename from executor/importer/job_test.go rename to pkg/executor/importer/job_test.go index 1b1b4b002f312..0e31c4135a113 100644 --- a/executor/importer/job_test.go +++ b/pkg/executor/importer/job_test.go @@ -18,11 +18,11 @@ import ( "context" "testing" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/executor/importer/kv_encode.go b/pkg/executor/importer/kv_encode.go similarity index 94% rename from executor/importer/kv_encode.go rename to pkg/executor/importer/kv_encode.go index a0f2dd5be9a67..65e1931380a29 100644 --- a/executor/importer/kv_encode.go +++ b/pkg/executor/importer/kv_encode.go @@ -23,14 +23,14 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/backend/encode" "github.com/pingcap/tidb/br/pkg/lightning/backend/kv" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" //nolint: goimports - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" //nolint: goimports + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // KVEncoder encodes a row of data into a KV pair. diff --git a/executor/importer/precheck.go b/pkg/executor/importer/precheck.go similarity index 94% rename from executor/importer/precheck.go rename to pkg/executor/importer/precheck.go index e88882e480b25..1cb1c6f75a445 100644 --- a/executor/importer/precheck.go +++ b/pkg/executor/importer/precheck.go @@ -24,13 +24,13 @@ import ( "github.com/pingcap/tidb/br/pkg/storage" "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/utils" - tidb "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/sqlexec" + tidb "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) const ( diff --git a/executor/importer/precheck_test.go b/pkg/executor/importer/precheck_test.go similarity index 94% rename from executor/importer/precheck_test.go rename to pkg/executor/importer/precheck_test.go index 2643c585d3594..802d3801893dd 100644 --- a/executor/importer/precheck_test.go +++ b/pkg/executor/importer/precheck_test.go @@ -27,14 +27,14 @@ import ( "github.com/johannesboyne/gofakes3/backend/s3mem" "github.com/pingcap/tidb/br/pkg/streamhelper" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" clientv3 "go.etcd.io/etcd/client/v3" diff --git a/executor/importer/table_import.go b/pkg/executor/importer/table_import.go similarity index 98% rename from executor/importer/table_import.go rename to pkg/executor/importer/table_import.go index b0ecefd173805..ace1df765e66c 100644 --- a/executor/importer/table_import.go +++ b/pkg/executor/importer/table_import.go @@ -39,12 +39,12 @@ import ( "github.com/pingcap/tidb/br/pkg/lightning/log" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/storage" - tidb "github.com/pingcap/tidb/config" - tidbkv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/syncutil" + tidb "github.com/pingcap/tidb/pkg/config" + tidbkv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/syncutil" pd "github.com/tikv/pd/client" "go.uber.org/multierr" "go.uber.org/zap" diff --git a/executor/importer/table_import_test.go b/pkg/executor/importer/table_import_test.go similarity index 98% rename from executor/importer/table_import_test.go rename to pkg/executor/importer/table_import_test.go index 7cfcae4dde30a..3fc5688a7d0db 100644 --- a/executor/importer/table_import_test.go +++ b/pkg/executor/importer/table_import_test.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/lightning/config" - tidb "github.com/pingcap/tidb/config" + tidb "github.com/pingcap/tidb/pkg/config" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/executor/index_advise.go b/pkg/executor/index_advise.go similarity index 93% rename from executor/index_advise.go rename to pkg/executor/index_advise.go index a0b6d773a667a..6f1f94cab1852 100644 --- a/executor/index_advise.go +++ b/pkg/executor/index_advise.go @@ -19,13 +19,13 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" ) // IndexAdviseExec represents a index advise executor. diff --git a/executor/index_advise_test.go b/pkg/executor/index_advise_test.go similarity index 98% rename from executor/index_advise_test.go rename to pkg/executor/index_advise_test.go index 92dac62b12345..20d5b94bf0fc0 100644 --- a/executor/index_advise_test.go +++ b/pkg/executor/index_advise_test.go @@ -18,9 +18,9 @@ import ( "os" "testing" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/executor/index_lookup_hash_join.go b/pkg/executor/index_lookup_hash_join.go similarity index 98% rename from executor/index_lookup_hash_join.go rename to pkg/executor/index_lookup_hash_join.go index bd7a5b3ccc1ba..22eb3a16ae366 100644 --- a/executor/index_lookup_hash_join.go +++ b/pkg/executor/index_lookup_hash_join.go @@ -26,16 +26,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" ) // numResChkHold indicates the number of resource chunks that an inner worker diff --git a/executor/index_lookup_join.go b/pkg/executor/index_lookup_join.go similarity index 97% rename from executor/index_lookup_join.go rename to pkg/executor/index_lookup_join.go index 1b63553daacb2..ec6e6220bbea9 100644 --- a/executor/index_lookup_join.go +++ b/pkg/executor/index_lookup_join.go @@ -27,22 +27,22 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/mvmap" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mvmap" + "github.com/pingcap/tidb/pkg/util/ranger" "go.uber.org/zap" ) diff --git a/executor/index_lookup_join_test.go b/pkg/executor/index_lookup_join_test.go similarity index 98% rename from executor/index_lookup_join_test.go rename to pkg/executor/index_lookup_join_test.go index 8a72e796fd20e..6e0d8e939535e 100644 --- a/executor/index_lookup_join_test.go +++ b/pkg/executor/index_lookup_join_test.go @@ -22,7 +22,7 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -344,8 +344,8 @@ func TestIssue23722(t *testing.T) { } func TestIssue27138(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -493,8 +493,8 @@ func TestIssue45716(t *testing.T) { tk.MustExec("insert into t1 values (1), (2);") tk.MustExec("insert into t2 values (1),(1),(2),(2);") - failpoint.Enable("github.com/pingcap/tidb/executor/inlNewInnerPanic", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/executor/inlNewInnerPanic") + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/inlNewInnerPanic", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/executor/inlNewInnerPanic") err := tk.QueryToErr("select /*+ inl_join(t2) */ * from t1 join t2 on t1.a = t2.a;") tk.MustContainErrMsg(err.Error(), "test inlNewInnerPanic") } diff --git a/executor/index_lookup_merge_join.go b/pkg/executor/index_lookup_merge_join.go similarity index 97% rename from executor/index_lookup_merge_join.go rename to pkg/executor/index_lookup_merge_join.go index f759d469830ce..2ff7729f239c2 100644 --- a/executor/index_lookup_merge_join.go +++ b/pkg/executor/index_lookup_merge_join.go @@ -24,20 +24,20 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" "go.uber.org/zap" ) diff --git a/executor/index_lookup_merge_join_test.go b/pkg/executor/index_lookup_merge_join_test.go similarity index 95% rename from executor/index_lookup_merge_join_test.go rename to pkg/executor/index_lookup_merge_join_test.go index 86d88b0a0ab38..a7a8d5ce7e0c5 100644 --- a/executor/index_lookup_merge_join_test.go +++ b/pkg/executor/index_lookup_merge_join_test.go @@ -19,17 +19,17 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/plancodec" "github.com/stretchr/testify/require" ) func TestIndexLookupMergeJoinHang(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/IndexMergeJoinMockOOM", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/IndexMergeJoinMockOOM", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/IndexMergeJoinMockOOM")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/IndexMergeJoinMockOOM")) }() store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -63,9 +63,9 @@ func TestIssue28052(t *testing.T) { } func TestIssue18068(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIssue18068", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIssue18068", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIssue18068")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIssue18068")) }() store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) diff --git a/executor/index_merge_reader.go b/pkg/executor/index_merge_reader.go similarity index 98% rename from executor/index_merge_reader.go rename to pkg/executor/index_merge_reader.go index db5963d2f2b87..aaac0ae06f062 100644 --- a/executor/index_merge_reader.go +++ b/pkg/executor/index_merge_reader.go @@ -30,27 +30,27 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - plannerutil "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + plannerutil "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/executor/infoschema_cluster_table_test.go b/pkg/executor/infoschema_cluster_table_test.go similarity index 95% rename from executor/infoschema_cluster_table_test.go rename to pkg/executor/infoschema_cluster_table_test.go index 3164352b2ba46..5170ea8671ced 100644 --- a/executor/infoschema_cluster_table_test.go +++ b/pkg/executor/infoschema_cluster_table_test.go @@ -27,16 +27,16 @@ import ( "github.com/gorilla/mux" "github.com/pingcap/failpoint" "github.com/pingcap/fn" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/pdapi" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -224,9 +224,9 @@ func TestTiDBClusterInfo(t *testing.T) { row("tikv", "store1", ""), )) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockStoreTombstone", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockStoreTombstone", `return(true)`)) tk.MustQuery("select type, instance, start_time from information_schema.cluster_info where type = 'tikv'").Check(testkit.Rows()) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockStoreTombstone")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockStoreTombstone")) // information_schema.cluster_config instances := []string{ @@ -235,8 +235,10 @@ func TestTiDBClusterInfo(t *testing.T) { "tikv,127.0.0.1:11080," + mockAddr + ",mock-version,mock-githash,0", } fpExpr := `return("` + strings.Join(instances, ";") + `")` - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockClusterInfo", fpExpr)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockClusterInfo")) }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockClusterInfo", fpExpr)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockClusterInfo")) + }() tk.MustQuery("select type, instance, status_address, version, git_hash, server_id from information_schema.cluster_info").Check(testkit.Rows( row("pd", "127.0.0.1:11080", mockAddr, "mock-version", "mock-githash", "0"), row("tidb", "127.0.0.1:11080", mockAddr, "mock-version", "mock-githash", "1001"), diff --git a/executor/infoschema_reader.go b/pkg/executor/infoschema_reader.go similarity index 98% rename from executor/infoschema_reader.go rename to pkg/executor/infoschema_reader.go index 1b2be9f2b9d63..449d41edb460f 100644 --- a/executor/infoschema_reader.go +++ b/pkg/executor/infoschema_reader.go @@ -31,54 +31,54 @@ import ( "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/pdhelper" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/keydecoder" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/servermemorylimit" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/pdhelper" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/keydecoder" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/servermemorylimit" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" "github.com/tikv/client-go/v2/txnkv/txnlock" diff --git a/executor/infoschema_reader_internal_test.go b/pkg/executor/infoschema_reader_internal_test.go similarity index 96% rename from executor/infoschema_reader_internal_test.go rename to pkg/executor/infoschema_reader_internal_test.go index b942fca493a19..3585c50e3787d 100644 --- a/executor/infoschema_reader_internal_test.go +++ b/pkg/executor/infoschema_reader_internal_test.go @@ -17,8 +17,8 @@ package executor import ( "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/executor/infoschema_reader_test.go b/pkg/executor/infoschema_reader_test.go similarity index 98% rename from executor/infoschema_reader_test.go rename to pkg/executor/infoschema_reader_test.go index 3468cbc080e10..51ecb3b77974d 100644 --- a/executor/infoschema_reader_test.go +++ b/pkg/executor/infoschema_reader_test.go @@ -25,13 +25,13 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" @@ -45,7 +45,7 @@ func TestInspectionTables(t *testing.T) { "tidb,127.0.0.1:11080,127.0.0.1:10080,mock-version,mock-githash,1001", "tikv,127.0.0.1:11080,127.0.0.1:10080,mock-version,mock-githash,0", } - fpName := "github.com/pingcap/tidb/infoschema/mockClusterInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockClusterInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -654,7 +654,7 @@ func TestTiFlashSystemTableWithTiFlashV620(t *testing.T) { "tiflash,127.0.0.1:3933,127.0.0.1:7777,,", "tikv,127.0.0.1:11080,127.0.0.1:10080,,", } - fpName := "github.com/pingcap/tidb/infoschema/mockStoreServerInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockStoreServerInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -699,7 +699,7 @@ func TestTiFlashSystemTableWithTiFlashV630(t *testing.T) { "tiflash,127.0.0.1:3933,127.0.0.1:7777,,", "tikv,127.0.0.1:11080,127.0.0.1:10080,,", } - fpName := "github.com/pingcap/tidb/infoschema/mockStoreServerInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockStoreServerInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -735,7 +735,7 @@ func TestTiFlashSystemTableWithTiFlashV640(t *testing.T) { "tiflash,127.0.0.1:3933,127.0.0.1:7777,,", "tikv,127.0.0.1:11080,127.0.0.1:10080,,", } - fpName := "github.com/pingcap/tidb/infoschema/mockStoreServerInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockStoreServerInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() diff --git a/executor/insert.go b/pkg/executor/insert.go similarity index 95% rename from executor/insert.go rename to pkg/executor/insert.go index bd8f269670993..c10c7810f01e4 100644 --- a/executor/insert.go +++ b/pkg/executor/insert.go @@ -22,22 +22,22 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tracing" "go.uber.org/zap" ) diff --git a/executor/insert_common.go b/pkg/executor/insert_common.go similarity index 97% rename from executor/insert_common.go rename to pkg/executor/insert_common.go index f222e21427102..e8d48aaf45709 100644 --- a/executor/insert_common.go +++ b/pkg/executor/insert_common.go @@ -22,30 +22,30 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/tikv/client-go/v2/txnkv/txnsnapshot" "go.uber.org/zap" ) diff --git a/executor/insert_test.go b/pkg/executor/insert_test.go similarity index 99% rename from executor/insert_test.go rename to pkg/executor/insert_test.go index a72bb308ac03e..7f8b62812437e 100644 --- a/executor/insert_test.go +++ b/pkg/executor/insert_test.go @@ -23,13 +23,13 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/execdetails" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/execdetails" "github.com/stretchr/testify/require" ) @@ -42,8 +42,8 @@ func TestInsertOnDuplicateKey(t *testing.T) { func TestInsertOnDuplicateKeyWithBinlog(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - failpoint.Enable("github.com/pingcap/tidb/table/tables/forceWriteBinlog", "return") - defer failpoint.Disable("github.com/pingcap/tidb/table/tables/forceWriteBinlog") + failpoint.Enable("github.com/pingcap/tidb/pkg/table/tables/forceWriteBinlog", "return") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/table/tables/forceWriteBinlog") testInsertOnDuplicateKey(t, tk) } diff --git a/executor/inspection_common.go b/pkg/executor/inspection_common.go similarity index 93% rename from executor/inspection_common.go rename to pkg/executor/inspection_common.go index 913e3563d38b8..5aecca67c189a 100644 --- a/executor/inspection_common.go +++ b/pkg/executor/inspection_common.go @@ -18,9 +18,9 @@ import ( "context" "slices" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" ) type inspectionRuleRetriever struct { diff --git a/executor/inspection_common_test.go b/pkg/executor/inspection_common_test.go similarity index 93% rename from executor/inspection_common_test.go rename to pkg/executor/inspection_common_test.go index f4086e587542e..9a63a1ed9f9fc 100644 --- a/executor/inspection_common_test.go +++ b/pkg/executor/inspection_common_test.go @@ -18,9 +18,9 @@ import ( "context" "testing" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/executor/inspection_profile.go b/pkg/executor/inspection_profile.go similarity index 99% rename from executor/inspection_profile.go rename to pkg/executor/inspection_profile.go index 9670f4ac626d0..595c0a96cdecf 100644 --- a/executor/inspection_profile.go +++ b/pkg/executor/inspection_profile.go @@ -23,10 +23,10 @@ import ( "strings" "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) const ( diff --git a/executor/inspection_result.go b/pkg/executor/inspection_result.go similarity index 98% rename from executor/inspection_result.go rename to pkg/executor/inspection_result.go index a0dbdb4b9afa3..7beb5eaa78741 100644 --- a/executor/inspection_result.go +++ b/pkg/executor/inspection_result.go @@ -25,17 +25,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) type ( diff --git a/executor/inspection_result_test.go b/pkg/executor/inspection_result_test.go similarity index 98% rename from executor/inspection_result_test.go rename to pkg/executor/inspection_result_test.go index 4d5da7ba287cc..29dd578ced407 100644 --- a/executor/inspection_result_test.go +++ b/pkg/executor/inspection_result_test.go @@ -25,12 +25,12 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/diagnosticspb" "github.com/pingcap/sysutil" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "google.golang.org/grpc" ) @@ -223,11 +223,11 @@ func createInspectionContext(t *testing.T, mockData map[string][][]types.Datum, }, } } - fpName0 := "github.com/pingcap/tidb/executor/mockMergeMockInspectionTables" + fpName0 := "github.com/pingcap/tidb/pkg/executor/mockMergeMockInspectionTables" require.NoError(t, failpoint.Enable(fpName0, "return")) // Mock for metric table data. - fpName1 := "github.com/pingcap/tidb/executor/mockMetricsTableData" + fpName1 := "github.com/pingcap/tidb/pkg/executor/mockMetricsTableData" require.NoError(t, failpoint.Enable(fpName1, "return")) ctx := context.WithValue(context.Background(), "__mockInspectionTables", configurations) @@ -518,7 +518,7 @@ func TestCriticalErrorInspection(t *testing.T) { for _, s := range testServers { servers = append(servers, strings.Join([]string{s.typ, s.address, s.address}, ",")) } - fpName2 := "github.com/pingcap/tidb/executor/mockClusterLogServerInfo" + fpName2 := "github.com/pingcap/tidb/pkg/executor/mockClusterLogServerInfo" fpExpr := strings.Join(servers, ";") require.NoError(t, failpoint.Enable(fpName2, fmt.Sprintf(`return("%s")`, fpExpr))) defer func() { require.NoError(t, failpoint.Disable(fpName2)) }() diff --git a/executor/inspection_summary.go b/pkg/executor/inspection_summary.go similarity index 97% rename from executor/inspection_summary.go rename to pkg/executor/inspection_summary.go index f943b074b2f57..43fb8312613d9 100644 --- a/executor/inspection_summary.go +++ b/pkg/executor/inspection_summary.go @@ -20,14 +20,14 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) type inspectionSummaryRetriever struct { diff --git a/executor/inspection_summary_test.go b/pkg/executor/inspection_summary_test.go similarity index 93% rename from executor/inspection_summary_test.go rename to pkg/executor/inspection_summary_test.go index bad9cfe1b21ba..f199c06afac59 100644 --- a/executor/inspection_summary_test.go +++ b/pkg/executor/inspection_summary_test.go @@ -19,12 +19,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) @@ -46,7 +46,7 @@ func TestInspectionSummary(t *testing.T) { tk := testkit.NewTestKit(t, store) - fpName := "github.com/pingcap/tidb/executor/mockMetricsTableData" + fpName := "github.com/pingcap/tidb/pkg/executor/mockMetricsTableData" require.NoError(t, failpoint.Enable(fpName, "return")) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() diff --git a/pkg/executor/internal/BUILD.bazel b/pkg/executor/internal/BUILD.bazel new file mode 100644 index 0000000000000..240510f070017 --- /dev/null +++ b/pkg/executor/internal/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = ["testkit.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal", + visibility = ["//pkg/executor:__subpackages__"], + deps = ["//pkg/testkit"], +) diff --git a/pkg/executor/internal/applycache/BUILD.bazel b/pkg/executor/internal/applycache/BUILD.bazel new file mode 100644 index 0000000000000..968dae7accc5b --- /dev/null +++ b/pkg/executor/internal/applycache/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "applycache", + srcs = ["apply_cache.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/applycache", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/sessionctx", + "//pkg/util/chunk", + "//pkg/util/kvcache", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/syncutil", + ], +) + +go_test( + name = "applycache_test", + timeout = "short", + srcs = [ + "apply_cache_test.go", + "main_test.go", + ], + embed = [":applycache"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/parser/mysql", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/internal/applycache/apply_cache.go b/pkg/executor/internal/applycache/apply_cache.go similarity index 92% rename from executor/internal/applycache/apply_cache.go rename to pkg/executor/internal/applycache/apply_cache.go index 7f6ba5b6df938..cb5ee694f3bce 100644 --- a/executor/internal/applycache/apply_cache.go +++ b/pkg/executor/internal/applycache/apply_cache.go @@ -15,12 +15,12 @@ package applycache import ( - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/syncutil" ) // ApplyCache is used in the apply executor. When we get the same value of the outer row. diff --git a/executor/internal/applycache/apply_cache_test.go b/pkg/executor/internal/applycache/apply_cache_test.go similarity index 93% rename from executor/internal/applycache/apply_cache_test.go rename to pkg/executor/internal/applycache/apply_cache_test.go index 770a661cea66d..b20495a2a7369 100644 --- a/executor/internal/applycache/apply_cache_test.go +++ b/pkg/executor/internal/applycache/apply_cache_test.go @@ -19,10 +19,10 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/internal/applycache/main_test.go b/pkg/executor/internal/applycache/main_test.go new file mode 100644 index 0000000000000..ef36cc3a4f59c --- /dev/null +++ b/pkg/executor/internal/applycache/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package applycache + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/internal/builder/BUILD.bazel b/pkg/executor/internal/builder/BUILD.bazel new file mode 100644 index 0000000000000..bc9afd8e240cc --- /dev/null +++ b/pkg/executor/internal/builder/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "builder", + srcs = ["builder_utils.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/builder", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/distsql", + "//pkg/kv", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/util/timeutil", + "@com_github_pingcap_tipb//go-tipb", + ], +) diff --git a/executor/internal/builder/builder_utils.go b/pkg/executor/internal/builder/builder_utils.go similarity index 90% rename from executor/internal/builder/builder_utils.go rename to pkg/executor/internal/builder/builder_utils.go index 14b5bc7ad52ee..6d2bb03f4fa31 100644 --- a/executor/internal/builder/builder_utils.go +++ b/pkg/executor/internal/builder/builder_utils.go @@ -15,11 +15,11 @@ package builder import ( - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/kv" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/kv" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/executor/internal/calibrateresource/BUILD.bazel b/pkg/executor/internal/calibrateresource/BUILD.bazel new file mode 100644 index 0000000000000..8fde4e3a197de --- /dev/null +++ b/pkg/executor/internal/calibrateresource/BUILD.bazel @@ -0,0 +1,55 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "calibrateresource", + srcs = ["calibrate_resource.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/calibrateresource", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/domain", + "//pkg/executor/internal/exec", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/duration", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn/staleread", + "//pkg/util/chunk", + "//pkg/util/mathutil", + "//pkg/util/sqlexec", + "@com_github_docker_go_units//:go-units", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_pd_client//resource_group/controller", + ], +) + +go_test( + name = "calibrateresource_test", + timeout = "short", + srcs = [ + "calibrate_resource_test.go", + "main_test.go", + ], + embed = [":calibrateresource"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/meta/autoid", + "//pkg/parser/mysql", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_pd_client//:client", + "@com_github_tikv_pd_client//resource_group/controller", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/internal/calibrateresource/calibrate_resource.go b/pkg/executor/internal/calibrateresource/calibrate_resource.go similarity index 97% rename from executor/internal/calibrateresource/calibrate_resource.go rename to pkg/executor/internal/calibrateresource/calibrate_resource.go index 0b8bc18b28f93..e8b15e191b216 100644 --- a/executor/internal/calibrateresource/calibrate_resource.go +++ b/pkg/executor/internal/calibrateresource/calibrate_resource.go @@ -24,19 +24,19 @@ import ( "github.com/docker/go-units" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/duration" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/duration" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/oracle" resourceControlClient "github.com/tikv/pd/client/resource_group/controller" ) diff --git a/executor/internal/calibrateresource/calibrate_resource_test.go b/pkg/executor/internal/calibrateresource/calibrate_resource_test.go similarity index 99% rename from executor/internal/calibrateresource/calibrate_resource_test.go rename to pkg/executor/internal/calibrateresource/calibrate_resource_test.go index f8a4e71f2bae3..a54d462c5245e 100644 --- a/executor/internal/calibrateresource/calibrate_resource_test.go +++ b/pkg/executor/internal/calibrateresource/calibrate_resource_test.go @@ -22,10 +22,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" rmclient "github.com/tikv/pd/client/resource_group/controller" @@ -78,12 +78,12 @@ func TestCalibrateResource(t *testing.T) { require.Error(t, err) // Mock for metric table data. - fpName := "github.com/pingcap/tidb/executor/mockMetricsTableData" + fpName := "github.com/pingcap/tidb/pkg/executor/mockMetricsTableData" require.NoError(t, failpoint.Enable(fpName, "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/internal/calibrateresource/mockMetricsDataFilter", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/calibrateresource/mockMetricsDataFilter", "return(true)")) defer func() { require.NoError(t, failpoint.Disable(fpName)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/internal/calibrateresource/mockMetricsDataFilter")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/calibrateresource/mockMetricsDataFilter")) }() datetime := func(s string) types.Time { diff --git a/pkg/executor/internal/calibrateresource/main_test.go b/pkg/executor/internal/calibrateresource/main_test.go new file mode 100644 index 0000000000000..a450e9ccd4dcb --- /dev/null +++ b/pkg/executor/internal/calibrateresource/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package calibrateresource + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/internal/exec/BUILD.bazel b/pkg/executor/internal/exec/BUILD.bazel new file mode 100644 index 0000000000000..bce42f4d9e191 --- /dev/null +++ b/pkg/executor/internal/exec/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "exec", + srcs = ["executor.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/exec", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/domain", + "//pkg/expression", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/execdetails", + "//pkg/util/sqlexec", + "//pkg/util/topsql", + "//pkg/util/topsql/state", + "//pkg/util/tracing", + "@com_github_ngaut_pools//:pools", + ], +) diff --git a/pkg/executor/internal/exec/executor.go b/pkg/executor/internal/exec/executor.go new file mode 100644 index 0000000000000..8058f69327820 --- /dev/null +++ b/pkg/executor/internal/exec/executor.go @@ -0,0 +1,308 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package exec + +import ( + "context" + "fmt" + "sync/atomic" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/tracing" +) + +// Executor is the physical implementation of an algebra operator. +// +// In TiDB, all algebra operators are implemented as iterators, i.e., they +// support a simple Open-Next-Close protocol. See this paper for more details: +// +// "Volcano-An Extensible and Parallel Query Evaluation System" +// +// Different from Volcano's execution model, a "Next" function call in TiDB will +// return a batch of rows, other than a single row in Volcano. +// NOTE: Executors must call "chk.Reset()" before appending their results to it. +type Executor interface { + Base() *BaseExecutor + Open(context.Context) error + Next(ctx context.Context, req *chunk.Chunk) error + Close() error + Schema() *expression.Schema + RetFieldTypes() []*types.FieldType + InitCap() int + MaxChunkSize() int +} + +var _ Executor = &BaseExecutor{} + +// BaseExecutor holds common information for executors. +type BaseExecutor struct { + ctx sessionctx.Context + AllocPool chunk.Allocator + schema *expression.Schema // output schema + runtimeStats *execdetails.BasicRuntimeStats + children []Executor + retFieldTypes []*types.FieldType + id int + initCap int + maxChunkSize int +} + +// NewBaseExecutor creates a new BaseExecutor instance. +func NewBaseExecutor(ctx sessionctx.Context, schema *expression.Schema, id int, children ...Executor) BaseExecutor { + e := BaseExecutor{ + children: children, + ctx: ctx, + id: id, + schema: schema, + initCap: ctx.GetSessionVars().InitChunkSize, + maxChunkSize: ctx.GetSessionVars().MaxChunkSize, + AllocPool: ctx.GetSessionVars().ChunkPool.Alloc, + } + if ctx.GetSessionVars().StmtCtx.RuntimeStatsColl != nil { + if e.id > 0 { + e.runtimeStats = e.ctx.GetSessionVars().StmtCtx.RuntimeStatsColl.GetBasicRuntimeStats(id) + } + } + if schema != nil { + cols := schema.Columns + e.retFieldTypes = make([]*types.FieldType, len(cols)) + for i := range cols { + e.retFieldTypes[i] = cols[i].RetType + } + } + return e +} + +// RuntimeStats returns the runtime stats of an executor. +func (e *BaseExecutor) RuntimeStats() *execdetails.BasicRuntimeStats { + return e.runtimeStats +} + +// ID returns the id of an executor. +func (e *BaseExecutor) ID() int { + return e.id +} + +// AllChildren returns all children. +func (e *BaseExecutor) AllChildren() []Executor { + return e.children +} + +// ChildrenLen returns the length of children. +func (e *BaseExecutor) ChildrenLen() int { + return len(e.children) +} + +// EmptyChildren judges whether the children is empty. +func (e *BaseExecutor) EmptyChildren() bool { + return len(e.children) == 0 +} + +// SetChildren sets the children for an executor. +func (e *BaseExecutor) SetChildren(idx int, ex Executor) { + e.children[idx] = ex +} + +// Children returns the children for an executor. +func (e *BaseExecutor) Children(idx int) Executor { + return e.children[idx] +} + +// RetFieldTypes returns the return field types of an executor. +func (e *BaseExecutor) RetFieldTypes() []*types.FieldType { + return e.retFieldTypes +} + +// InitCap returns the initial capacity for chunk +func (e *BaseExecutor) InitCap() int { + return e.initCap +} + +// SetInitCap sets the initial capacity for chunk +func (e *BaseExecutor) SetInitCap(c int) { + e.initCap = c +} + +// MaxChunkSize returns the max chunk size. +func (e *BaseExecutor) MaxChunkSize() int { + return e.maxChunkSize +} + +// SetMaxChunkSize sets the max chunk size. +func (e *BaseExecutor) SetMaxChunkSize(size int) { + e.maxChunkSize = size +} + +// Base returns the BaseExecutor of an executor, don't override this method! +func (e *BaseExecutor) Base() *BaseExecutor { + return e +} + +// Open initializes children recursively and "childrenResults" according to children's schemas. +func (e *BaseExecutor) Open(ctx context.Context) error { + for _, child := range e.children { + err := child.Open(ctx) + if err != nil { + return err + } + } + return nil +} + +// Close closes all executors and release all resources. +func (e *BaseExecutor) Close() error { + var firstErr error + for _, src := range e.children { + if err := src.Close(); err != nil && firstErr == nil { + firstErr = err + } + } + return firstErr +} + +// Schema returns the current BaseExecutor's schema. If it is nil, then create and return a new one. +func (e *BaseExecutor) Schema() *expression.Schema { + if e.schema == nil { + return expression.NewSchema() + } + return e.schema +} + +// Next fills multiple rows into a chunk. +func (*BaseExecutor) Next(_ context.Context, _ *chunk.Chunk) error { + return nil +} + +// Ctx return ```sessionctx.Context``` of Executor +func (e *BaseExecutor) Ctx() sessionctx.Context { + return e.ctx +} + +// GetSchema gets the schema. +func (e *BaseExecutor) GetSchema() *expression.Schema { + return e.schema +} + +// UpdateDeltaForTableID updates the delta info for the table with tableID. +func (e *BaseExecutor) UpdateDeltaForTableID(id int64) { + txnCtx := e.ctx.GetSessionVars().TxnCtx + txnCtx.UpdateDeltaForTable(id, 0, 0, map[int64]int64{}) +} + +// GetSysSession gets a system session context from executor. +func (e *BaseExecutor) GetSysSession() (sessionctx.Context, error) { + dom := domain.GetDomain(e.Ctx()) + sysSessionPool := dom.SysSessionPool() + ctx, err := sysSessionPool.Get() + if err != nil { + return nil, err + } + restrictedCtx := ctx.(sessionctx.Context) + restrictedCtx.GetSessionVars().InRestrictedSQL = true + return restrictedCtx, nil +} + +// ReleaseSysSession releases a system session context to executor. +func (e *BaseExecutor) ReleaseSysSession(ctx context.Context, sctx sessionctx.Context) { + if sctx == nil { + return + } + dom := domain.GetDomain(e.Ctx()) + sysSessionPool := dom.SysSessionPool() + if _, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, "rollback"); err != nil { + sctx.(pools.Resource).Close() + return + } + sysSessionPool.Put(sctx.(pools.Resource)) +} + +// TryNewCacheChunk tries to get a cached chunk +func TryNewCacheChunk(e Executor) *chunk.Chunk { + base := e.Base() + s := base.Ctx().GetSessionVars() + return s.GetNewChunkWithCapacity(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize(), base.AllocPool) +} + +// RetTypes returns all output column types. +func RetTypes(e Executor) []*types.FieldType { + base := e.Base() + return base.RetFieldTypes() +} + +// NewFirstChunk creates a new chunk to buffer current executor's result. +func NewFirstChunk(e Executor) *chunk.Chunk { + base := e.Base() + return chunk.New(base.RetFieldTypes(), base.InitCap(), base.MaxChunkSize()) +} + +// Next is a wrapper function on e.Next(), it handles some common codes. +func Next(ctx context.Context, e Executor, req *chunk.Chunk) error { + base := e.Base() + if base.RuntimeStats() != nil { + start := time.Now() + defer func() { base.RuntimeStats().Record(time.Since(start), req.NumRows()) }() + } + sessVars := base.Ctx().GetSessionVars() + if atomic.LoadUint32(&sessVars.Killed) == 2 { + return exeerrors.ErrMaxExecTimeExceeded + } + if atomic.LoadUint32(&sessVars.Killed) == 1 { + return exeerrors.ErrQueryInterrupted + } + + r, ctx := tracing.StartRegionEx(ctx, fmt.Sprintf("%T.Next", e)) + defer r.End() + + if topsqlstate.TopSQLEnabled() && sessVars.StmtCtx.IsSQLAndPlanRegistered.CompareAndSwap(false, true) { + RegisterSQLAndPlanInExecForTopSQL(sessVars) + } + err := e.Next(ctx, req) + + if err != nil { + return err + } + // recheck whether the session/query is killed during the Next() + if atomic.LoadUint32(&sessVars.Killed) == 2 { + err = exeerrors.ErrMaxExecTimeExceeded + } + if atomic.LoadUint32(&sessVars.Killed) == 1 { + err = exeerrors.ErrQueryInterrupted + } + return err +} + +// RegisterSQLAndPlanInExecForTopSQL register the sql and plan information if it doesn't register before execution. +// This uses to catch the running SQL when Top SQL is enabled in execution. +func RegisterSQLAndPlanInExecForTopSQL(sessVars *variable.SessionVars) { + stmtCtx := sessVars.StmtCtx + normalizedSQL, sqlDigest := stmtCtx.SQLDigest() + topsql.RegisterSQL(normalizedSQL, sqlDigest, sessVars.InRestrictedSQL) + normalizedPlan, planDigest := stmtCtx.GetPlanDigest() + if len(normalizedPlan) > 0 { + topsql.RegisterPlan(normalizedPlan, planDigest) + } +} diff --git a/pkg/executor/internal/mpp/BUILD.bazel b/pkg/executor/internal/mpp/BUILD.bazel new file mode 100644 index 0000000000000..5e1990700694b --- /dev/null +++ b/pkg/executor/internal/mpp/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mpp", + srcs = ["local_mpp_coordinator.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/mpp", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/config", + "//pkg/distsql", + "//pkg/executor/internal/builder", + "//pkg/executor/internal/util", + "//pkg/executor/metrics", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/store/driver/backoff", + "//pkg/store/driver/error", + "//pkg/util/execdetails", + "//pkg/util/logutil", + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "mpp_test", + timeout = "short", + srcs = ["local_mpp_coordinator_test.go"], + embed = [":mpp"], + flaky = True, + deps = [ + "//pkg/planner/core", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + ], +) diff --git a/executor/internal/mpp/local_mpp_coordinator.go b/pkg/executor/internal/mpp/local_mpp_coordinator.go similarity index 97% rename from executor/internal/mpp/local_mpp_coordinator.go rename to pkg/executor/internal/mpp/local_mpp_coordinator.go index 779dd3fae4276..b4f4228937b8f 100644 --- a/executor/internal/mpp/local_mpp_coordinator.go +++ b/pkg/executor/internal/mpp/local_mpp_coordinator.go @@ -26,22 +26,22 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/util" - "github.com/pingcap/tidb/executor/metrics" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/driver/backoff" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/util" + "github.com/pingcap/tidb/pkg/executor/metrics" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" diff --git a/executor/internal/mpp/local_mpp_coordinator_test.go b/pkg/executor/internal/mpp/local_mpp_coordinator_test.go similarity index 95% rename from executor/internal/mpp/local_mpp_coordinator_test.go rename to pkg/executor/internal/mpp/local_mpp_coordinator_test.go index 44d4f04999529..265b180e2b26c 100644 --- a/executor/internal/mpp/local_mpp_coordinator_test.go +++ b/pkg/executor/internal/mpp/local_mpp_coordinator_test.go @@ -17,7 +17,7 @@ package mpp import ( "testing" - plannercore "github.com/pingcap/tidb/planner/core" + plannercore "github.com/pingcap/tidb/pkg/planner/core" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/internal/pdhelper/BUILD.bazel b/pkg/executor/internal/pdhelper/BUILD.bazel new file mode 100644 index 0000000000000..862390e4a143e --- /dev/null +++ b/pkg/executor/internal/pdhelper/BUILD.bazel @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "pdhelper", + srcs = ["pd.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/pdhelper", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/kv", + "//pkg/sessionctx", + "//pkg/store/helper", + "//pkg/util", + "//pkg/util/sqlexec", + "@com_github_jellydator_ttlcache_v3//:ttlcache", + "@com_github_pingcap_failpoint//:failpoint", + ], +) + +go_test( + name = "pdhelper_test", + timeout = "short", + srcs = [ + "main_test.go", + "pd_test.go", + ], + embed = [":pdhelper"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/sessionctx", + "//pkg/testkit/testsetup", + "@com_github_jellydator_ttlcache_v3//:ttlcache", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/internal/pdhelper/main_test.go b/pkg/executor/internal/pdhelper/main_test.go new file mode 100644 index 0000000000000..909d20e2a3367 --- /dev/null +++ b/pkg/executor/internal/pdhelper/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package pdhelper + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/internal/pdhelper/pd.go b/pkg/executor/internal/pdhelper/pd.go new file mode 100644 index 0000000000000..2ad8dd8ad52e9 --- /dev/null +++ b/pkg/executor/internal/pdhelper/pd.go @@ -0,0 +1,124 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package pdhelper + +import ( + "context" + "strconv" + "strings" + "sync" + "time" + + "github.com/jellydator/ttlcache/v3" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +// GlobalPDHelper is the global variable for PDHelper. +var GlobalPDHelper = defaultPDHelper() +var globalPDHelperOnce sync.Once + +// PDHelper is used to get some information from PD. +type PDHelper struct { + cacheForApproximateTableCountFromStorage *ttlcache.Cache[string, float64] + + getApproximateTableCountFromStorageFunc func(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) + wg util.WaitGroupWrapper +} + +func defaultPDHelper() *PDHelper { + cache := ttlcache.New[string, float64]( + ttlcache.WithTTL[string, float64](30*time.Second), + ttlcache.WithCapacity[string, float64](1024*1024), + ) + return &PDHelper{ + cacheForApproximateTableCountFromStorage: cache, + getApproximateTableCountFromStorageFunc: getApproximateTableCountFromStorage, + } +} + +// Start is used to start the background task of PDHelper. Currently, the background task is used to clean up TTL cache. +func (p *PDHelper) Start() { + globalPDHelperOnce.Do(func() { + p.wg.Run(p.cacheForApproximateTableCountFromStorage.Start) + }) +} + +// Stop stops the background task of PDHelper. +func (p *PDHelper) Stop() { + p.cacheForApproximateTableCountFromStorage.Stop() + p.wg.Wait() +} + +func approximateTableCountKey(tid int64, dbName, tableName, partitionName string) string { + return strings.Join([]string{strconv.FormatInt(tid, 10), dbName, tableName, partitionName}, "_") +} + +// GetApproximateTableCountFromStorage gets the approximate count of the table. +func (p *PDHelper) GetApproximateTableCountFromStorage(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) { + key := approximateTableCountKey(tid, dbName, tableName, partitionName) + if item := p.cacheForApproximateTableCountFromStorage.Get(key); item != nil { + return item.Value(), true + } + result, hasPD := p.getApproximateTableCountFromStorageFunc(sctx, tid, dbName, tableName, partitionName) + p.cacheForApproximateTableCountFromStorage.Set(key, result, ttlcache.DefaultTTL) + return result, hasPD +} + +func getApproximateTableCountFromStorage(sctx sessionctx.Context, tid int64, dbName, tableName, partitionName string) (float64, bool) { + tikvStore, ok := sctx.GetStore().(helper.Storage) + if !ok { + return 0, false + } + regionStats := &helper.PDRegionStats{} + pdHelper := helper.NewHelper(tikvStore) + err := pdHelper.GetPDRegionStats(tid, regionStats, true) + failpoint.Inject("calcSampleRateByStorageCount", func() { + // Force the TiDB thinking that there's PD and the count of region is small. + err = nil + regionStats.Count = 1 + // Set a very large approximate count. + regionStats.StorageKeys = 1000000 + }) + if err != nil { + return 0, false + } + // If this table is not small, we directly use the count from PD, + // since for a small table, it's possible that it's data is in the same region with part of another large table. + // Thus, we use the number of the regions of the table's table KV to decide whether the table is small. + if regionStats.Count > 2 { + return float64(regionStats.StorageKeys), true + } + // Otherwise, we use count(*) to calc it's size, since it's very small, the table data can be filled in no more than 2 regions. + sql := new(strings.Builder) + sqlexec.MustFormatSQL(sql, "select count(*) from %n.%n", dbName, tableName) + if partitionName != "" { + sqlexec.MustFormatSQL(sql, " partition(%n)", partitionName) + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + rows, _, err := sctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, nil, sql.String()) + if err != nil { + return 0, false + } + // If the record set is nil, there's something wrong with the execution. The COUNT(*) would always return one row. + if len(rows) == 0 || rows[0].Len() == 0 { + return 0, false + } + return float64(rows[0].GetInt64(0)), true +} diff --git a/executor/internal/pdhelper/pd_test.go b/pkg/executor/internal/pdhelper/pd_test.go similarity index 98% rename from executor/internal/pdhelper/pd_test.go rename to pkg/executor/internal/pdhelper/pd_test.go index 5ca14a9e0a7c8..134cbdced5786 100644 --- a/executor/internal/pdhelper/pd_test.go +++ b/pkg/executor/internal/pdhelper/pd_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/jellydator/ttlcache/v3" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/internal/querywatch/BUILD.bazel b/pkg/executor/internal/querywatch/BUILD.bazel new file mode 100644 index 0000000000000..50e7adc9fb6e5 --- /dev/null +++ b/pkg/executor/internal/querywatch/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "querywatch", + srcs = ["query_watch.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/querywatch", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/domain", + "//pkg/domain/resourcegroup", + "//pkg/executor/internal/exec", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_tikv_pd_client//resource_group/controller", + ], +) + +go_test( + name = "querywatch_test", + timeout = "short", + srcs = [ + "main_test.go", + "query_watch_test.go", + ], + embed = [":querywatch"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/internal/querywatch/main_test.go b/pkg/executor/internal/querywatch/main_test.go new file mode 100644 index 0000000000000..326c42b520fa8 --- /dev/null +++ b/pkg/executor/internal/querywatch/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package querywatch + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/internal/querywatch/query_watch.go b/pkg/executor/internal/querywatch/query_watch.go similarity index 92% rename from executor/internal/querywatch/query_watch.go rename to pkg/executor/internal/querywatch/query_watch.go index e1237d19a4377..aab1fb0d3647f 100644 --- a/executor/internal/querywatch/query_watch.go +++ b/pkg/executor/internal/querywatch/query_watch.go @@ -21,17 +21,17 @@ import ( "github.com/pingcap/errors" rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" rmclient "github.com/tikv/pd/client/resource_group/controller" ) diff --git a/executor/internal/querywatch/query_watch_test.go b/pkg/executor/internal/querywatch/query_watch_test.go similarity index 98% rename from executor/internal/querywatch/query_watch_test.go rename to pkg/executor/internal/querywatch/query_watch_test.go index 88e4dc143d954..9dd0c2f1a1558 100644 --- a/executor/internal/querywatch/query_watch_test.go +++ b/pkg/executor/internal/querywatch/query_watch_test.go @@ -19,9 +19,9 @@ import ( "testing" "time" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/internal/testkit.go b/pkg/executor/internal/testkit.go new file mode 100644 index 0000000000000..96c1ecdbfa8bc --- /dev/null +++ b/pkg/executor/internal/testkit.go @@ -0,0 +1,31 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "fmt" + + "github.com/pingcap/tidb/pkg/testkit" +) + +// FillData fill data into table +func FillData(tk *testkit.TestKit, table string) { + tk.MustExec("use test") + tk.MustExec(fmt.Sprintf("create table %s(id int not null default 1, name varchar(255), PRIMARY KEY(id));", table)) + + // insert data + tk.MustExec(fmt.Sprintf("insert INTO %s VALUES (1, \"hello\");", table)) + tk.MustExec(fmt.Sprintf("insert into %s values (2, \"hello\");", table)) +} diff --git a/pkg/executor/internal/util/BUILD.bazel b/pkg/executor/internal/util/BUILD.bazel new file mode 100644 index 0000000000000..eef9d73ffc4ba --- /dev/null +++ b/pkg/executor/internal/util/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "util", + srcs = [ + "partition_table.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/util", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_tipb//go-tipb", + ], +) diff --git a/executor/internal/util/partition_table.go b/pkg/executor/internal/util/partition_table.go similarity index 100% rename from executor/internal/util/partition_table.go rename to pkg/executor/internal/util/partition_table.go diff --git a/executor/internal/util/util.go b/pkg/executor/internal/util/util.go similarity index 100% rename from executor/internal/util/util.go rename to pkg/executor/internal/util/util.go diff --git a/pkg/executor/internal/vecgroupchecker/BUILD.bazel b/pkg/executor/internal/vecgroupchecker/BUILD.bazel new file mode 100644 index 0000000000000..2c2cdcab18b5d --- /dev/null +++ b/pkg/executor/internal/vecgroupchecker/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "vecgroupchecker", + srcs = ["vec_group_checker.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker", + visibility = ["//pkg/executor:__subpackages__"], + deps = [ + "//pkg/expression", + "//pkg/sessionctx", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + ], +) + +go_test( + name = "vecgroupchecker_test", + timeout = "short", + srcs = [ + "main_test.go", + "vec_group_checker_test.go", + ], + embed = [":vecgroupchecker"], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/config", + "//pkg/expression", + "//pkg/meta/autoid", + "//pkg/parser/mysql", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/internal/vecgroupchecker/main_test.go b/pkg/executor/internal/vecgroupchecker/main_test.go new file mode 100644 index 0000000000000..7dac8fefafa8d --- /dev/null +++ b/pkg/executor/internal/vecgroupchecker/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package vecgroupchecker + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/internal/vecgroupchecker/vec_group_checker.go b/pkg/executor/internal/vecgroupchecker/vec_group_checker.go similarity index 98% rename from executor/internal/vecgroupchecker/vec_group_checker.go rename to pkg/executor/internal/vecgroupchecker/vec_group_checker.go index 557c3e8f914ba..65c2707b9722e 100644 --- a/executor/internal/vecgroupchecker/vec_group_checker.go +++ b/pkg/executor/internal/vecgroupchecker/vec_group_checker.go @@ -18,11 +18,11 @@ import ( "bytes" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" ) // VecGroupChecker is used to split a given chunk according to the `group by` expression in a vectorized manner diff --git a/executor/internal/vecgroupchecker/vec_group_checker_test.go b/pkg/executor/internal/vecgroupchecker/vec_group_checker_test.go similarity index 97% rename from executor/internal/vecgroupchecker/vec_group_checker_test.go rename to pkg/executor/internal/vecgroupchecker/vec_group_checker_test.go index 602132bbc6749..9dda0d27b6acc 100644 --- a/executor/internal/vecgroupchecker/vec_group_checker_test.go +++ b/pkg/executor/internal/vecgroupchecker/vec_group_checker_test.go @@ -20,11 +20,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/join.go b/pkg/executor/join.go new file mode 100644 index 0000000000000..efaa228b74168 --- /dev/null +++ b/pkg/executor/join.go @@ -0,0 +1,1660 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "context" + "fmt" + "runtime/trace" + "strconv" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/applycache" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/bitmap" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/memory" +) + +var ( + _ exec.Executor = &HashJoinExec{} + _ exec.Executor = &NestedLoopApplyExec{} +) + +type hashJoinCtx struct { + sessCtx sessionctx.Context + allocPool chunk.Allocator + // concurrency is the number of partition, build and join workers. + concurrency uint + joinResultCh chan *hashjoinWorkerResult + // closeCh add a lock for closing executor. + closeCh chan struct{} + finished atomic.Bool + useOuterToBuild bool + isOuterJoin bool + isNullEQ []bool + buildFinished chan error + rowContainer *hashRowContainer + joinType plannercore.JoinType + outerMatchedStatus []*bitmap.ConcurrentBitmap + stats *hashJoinRuntimeStats + probeTypes []*types.FieldType + buildTypes []*types.FieldType + outerFilter expression.CNFExprs + isNullAware bool + memTracker *memory.Tracker // track memory usage. + diskTracker *disk.Tracker // track disk usage. +} + +// probeSideTupleFetcher reads tuples from probeSideExec and send them to probeWorkers. +type probeSideTupleFetcher struct { + *hashJoinCtx + + probeSideExec exec.Executor + probeChkResourceCh chan *probeChkResource + probeResultChs []chan *chunk.Chunk + requiredRows int64 +} + +type probeWorker struct { + hashJoinCtx *hashJoinCtx + workerID uint + + probeKeyColIdx []int + probeNAKeyColIdx []int + // We pre-alloc and reuse the Rows and RowPtrs for each probe goroutine, to avoid allocation frequently + buildSideRows []chunk.Row + buildSideRowPtrs []chunk.RowPtr + + // We build individual joiner for each join worker when use chunk-based + // execution, to avoid the concurrency of joiner.chk and joiner.selected. + joiner joiner + rowIters *chunk.Iterator4Slice + rowContainerForProbe *hashRowContainer + // for every naaj probe worker, pre-allocate the int slice for store the join column index to check. + needCheckBuildColPos []int + needCheckProbeColPos []int + needCheckBuildTypes []*types.FieldType + needCheckProbeTypes []*types.FieldType + probeChkResourceCh chan *probeChkResource + joinChkResourceCh chan *chunk.Chunk + probeResultCh chan *chunk.Chunk +} + +type buildWorker struct { + hashJoinCtx *hashJoinCtx + buildSideExec exec.Executor + buildKeyColIdx []int + buildNAKeyColIdx []int +} + +// HashJoinExec implements the hash join algorithm. +type HashJoinExec struct { + exec.BaseExecutor + *hashJoinCtx + + probeSideTupleFetcher *probeSideTupleFetcher + probeWorkers []*probeWorker + buildWorker *buildWorker + + workerWg util.WaitGroupWrapper + waiterWg util.WaitGroupWrapper + + prepared bool +} + +// probeChkResource stores the result of the join probe side fetch worker, +// `dest` is for Chunk reuse: after join workers process the probe side chunk which is read from `dest`, +// they'll store the used chunk as `chk`, and then the probe side fetch worker will put new data into `chk` and write `chk` into dest. +type probeChkResource struct { + chk *chunk.Chunk + dest chan<- *chunk.Chunk +} + +// hashjoinWorkerResult stores the result of join workers, +// `src` is for Chunk reuse: the main goroutine will get the join result chunk `chk`, +// and push `chk` into `src` after processing, join worker goroutines get the empty chunk from `src` +// and push new data into this chunk. +type hashjoinWorkerResult struct { + chk *chunk.Chunk + err error + src chan<- *chunk.Chunk +} + +// Close implements the Executor Close interface. +func (e *HashJoinExec) Close() error { + if e.closeCh != nil { + close(e.closeCh) + } + e.finished.Store(true) + if e.prepared { + if e.buildFinished != nil { + channel.Clear(e.buildFinished) + } + if e.joinResultCh != nil { + channel.Clear(e.joinResultCh) + } + if e.probeSideTupleFetcher.probeChkResourceCh != nil { + close(e.probeSideTupleFetcher.probeChkResourceCh) + channel.Clear(e.probeSideTupleFetcher.probeChkResourceCh) + } + for i := range e.probeSideTupleFetcher.probeResultChs { + channel.Clear(e.probeSideTupleFetcher.probeResultChs[i]) + } + for i := range e.probeWorkers { + close(e.probeWorkers[i].joinChkResourceCh) + channel.Clear(e.probeWorkers[i].joinChkResourceCh) + } + e.probeSideTupleFetcher.probeChkResourceCh = nil + terror.Call(e.rowContainer.Close) + e.waiterWg.Wait() + } + e.outerMatchedStatus = e.outerMatchedStatus[:0] + for _, w := range e.probeWorkers { + w.buildSideRows = nil + w.buildSideRowPtrs = nil + w.needCheckBuildColPos = nil + w.needCheckProbeColPos = nil + w.needCheckBuildTypes = nil + w.needCheckProbeTypes = nil + w.joinChkResourceCh = nil + } + + if e.stats != nil && e.rowContainer != nil { + e.stats.hashStat = *e.rowContainer.stat + } + if e.stats != nil { + defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) + } + err := e.BaseExecutor.Close() + return err +} + +// Open implements the Executor Open interface. +func (e *HashJoinExec) Open(ctx context.Context) error { + if err := e.BaseExecutor.Open(ctx); err != nil { + e.closeCh = nil + e.prepared = false + return err + } + e.prepared = false + if e.hashJoinCtx.memTracker != nil { + e.hashJoinCtx.memTracker.Reset() + } else { + e.hashJoinCtx.memTracker = memory.NewTracker(e.ID(), -1) + } + e.hashJoinCtx.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + + e.diskTracker = disk.NewTracker(e.ID(), -1) + e.diskTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.DiskTracker) + + e.workerWg = util.WaitGroupWrapper{} + e.waiterWg = util.WaitGroupWrapper{} + e.closeCh = make(chan struct{}) + e.finished.Store(false) + + if e.RuntimeStats() != nil { + e.stats = &hashJoinRuntimeStats{ + concurrent: int(e.concurrency), + } + } + return nil +} + +// fetchProbeSideChunks get chunks from fetches chunks from the big table in a background goroutine +// and sends the chunks to multiple channels which will be read by multiple join workers. +func (fetcher *probeSideTupleFetcher) fetchProbeSideChunks(ctx context.Context, maxChunkSize int) { + hasWaitedForBuild := false + for { + if fetcher.finished.Load() { + return + } + + var probeSideResource *probeChkResource + var ok bool + select { + case <-fetcher.closeCh: + return + case probeSideResource, ok = <-fetcher.probeChkResourceCh: + if !ok { + return + } + } + probeSideResult := probeSideResource.chk + if fetcher.isOuterJoin { + required := int(atomic.LoadInt64(&fetcher.requiredRows)) + probeSideResult.SetRequiredRows(required, maxChunkSize) + } + err := exec.Next(ctx, fetcher.probeSideExec, probeSideResult) + failpoint.Inject("ConsumeRandomPanic", nil) + if err != nil { + fetcher.joinResultCh <- &hashjoinWorkerResult{ + err: err, + } + return + } + if !hasWaitedForBuild { + failpoint.Inject("issue30289", func(val failpoint.Value) { + if val.(bool) { + probeSideResult.Reset() + } + }) + if probeSideResult.NumRows() == 0 && !fetcher.useOuterToBuild { + fetcher.finished.Store(true) + } + emptyBuild, buildErr := fetcher.wait4BuildSide() + if buildErr != nil { + fetcher.joinResultCh <- &hashjoinWorkerResult{ + err: buildErr, + } + return + } else if emptyBuild { + return + } + hasWaitedForBuild = true + } + + if probeSideResult.NumRows() == 0 { + return + } + + probeSideResource.dest <- probeSideResult + } +} + +func (fetcher *probeSideTupleFetcher) wait4BuildSide() (emptyBuild bool, err error) { + select { + case <-fetcher.closeCh: + return true, nil + case err := <-fetcher.buildFinished: + if err != nil { + return false, err + } + } + if fetcher.rowContainer.Len() == uint64(0) && (fetcher.joinType == plannercore.InnerJoin || fetcher.joinType == plannercore.SemiJoin) { + return true, nil + } + return false, nil +} + +// fetchBuildSideRows fetches all rows from build side executor, and append them +// to e.buildSideResult. +func (w *buildWorker) fetchBuildSideRows(ctx context.Context, chkCh chan<- *chunk.Chunk, errCh chan<- error, doneCh <-chan struct{}) { + defer close(chkCh) + var err error + failpoint.Inject("issue30289", func(val failpoint.Value) { + if val.(bool) { + err = errors.Errorf("issue30289 build return error") + errCh <- errors.Trace(err) + return + } + }) + failpoint.Inject("issue42662_1", func(val failpoint.Value) { + if val.(bool) { + if w.hashJoinCtx.sessCtx.GetSessionVars().ConnectionID != 0 { + // consume 170MB memory, this sql should be tracked into MemoryTop1Tracker + w.hashJoinCtx.memTracker.Consume(170 * 1024 * 1024) + } + return + } + }) + sessVars := w.hashJoinCtx.sessCtx.GetSessionVars() + for { + if w.hashJoinCtx.finished.Load() { + return + } + chk := sessVars.GetNewChunkWithCapacity(w.buildSideExec.Base().RetFieldTypes(), sessVars.MaxChunkSize, sessVars.MaxChunkSize, w.hashJoinCtx.allocPool) + err = exec.Next(ctx, w.buildSideExec, chk) + if err != nil { + errCh <- errors.Trace(err) + return + } + failpoint.Inject("errorFetchBuildSideRowsMockOOMPanic", nil) + failpoint.Inject("ConsumeRandomPanic", nil) + if chk.NumRows() == 0 { + return + } + select { + case <-doneCh: + return + case <-w.hashJoinCtx.closeCh: + return + case chkCh <- chk: + } + } +} + +func (e *HashJoinExec) initializeForProbe() { + // e.joinResultCh is for transmitting the join result chunks to the main + // thread. + e.joinResultCh = make(chan *hashjoinWorkerResult, e.concurrency+1) + + e.probeSideTupleFetcher.hashJoinCtx = e.hashJoinCtx + // e.probeSideTupleFetcher.probeResultChs is for transmitting the chunks which store the data of + // probeSideExec, it'll be written by probe side worker goroutine, and read by join + // workers. + e.probeSideTupleFetcher.probeResultChs = make([]chan *chunk.Chunk, e.concurrency) + for i := uint(0); i < e.concurrency; i++ { + e.probeSideTupleFetcher.probeResultChs[i] = make(chan *chunk.Chunk, 1) + e.probeWorkers[i].probeResultCh = e.probeSideTupleFetcher.probeResultChs[i] + } + + // e.probeChkResourceCh is for transmitting the used probeSideExec chunks from + // join workers to probeSideExec worker. + e.probeSideTupleFetcher.probeChkResourceCh = make(chan *probeChkResource, e.concurrency) + for i := uint(0); i < e.concurrency; i++ { + e.probeSideTupleFetcher.probeChkResourceCh <- &probeChkResource{ + chk: exec.NewFirstChunk(e.probeSideTupleFetcher.probeSideExec), + dest: e.probeSideTupleFetcher.probeResultChs[i], + } + } + + // e.probeWorker.joinChkResourceCh is for transmitting the reused join result chunks + // from the main thread to probe worker goroutines. + for i := uint(0); i < e.concurrency; i++ { + e.probeWorkers[i].joinChkResourceCh = make(chan *chunk.Chunk, 1) + e.probeWorkers[i].joinChkResourceCh <- exec.NewFirstChunk(e) + e.probeWorkers[i].probeChkResourceCh = e.probeSideTupleFetcher.probeChkResourceCh + } +} + +func (e *HashJoinExec) fetchAndProbeHashTable(ctx context.Context) { + e.initializeForProbe() + e.workerWg.RunWithRecover(func() { + defer trace.StartRegion(ctx, "HashJoinProbeSideFetcher").End() + e.probeSideTupleFetcher.fetchProbeSideChunks(ctx, e.MaxChunkSize()) + }, e.probeSideTupleFetcher.handleProbeSideFetcherPanic) + + for i := uint(0); i < e.concurrency; i++ { + workerID := i + e.workerWg.RunWithRecover(func() { + defer trace.StartRegion(ctx, "HashJoinWorker").End() + e.probeWorkers[workerID].runJoinWorker() + }, e.probeWorkers[workerID].handleProbeWorkerPanic) + } + e.waiterWg.RunWithRecover(e.waitJoinWorkersAndCloseResultChan, nil) +} + +func (fetcher *probeSideTupleFetcher) handleProbeSideFetcherPanic(r interface{}) { + for i := range fetcher.probeResultChs { + close(fetcher.probeResultChs[i]) + } + if r != nil { + fetcher.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("%v", r)} + } +} + +func (w *probeWorker) handleProbeWorkerPanic(r interface{}) { + if r != nil { + w.hashJoinCtx.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("probeWorker[%d] meets error: %v", w.workerID, r)} + } +} + +func (e *HashJoinExec) handleJoinWorkerPanic(r interface{}) { + if r != nil { + e.joinResultCh <- &hashjoinWorkerResult{err: errors.Errorf("%v", r)} + } +} + +// Concurrently handling unmatched rows from the hash table +func (w *probeWorker) handleUnmatchedRowsFromHashTable() { + ok, joinResult := w.getNewJoinResult() + if !ok { + return + } + numChks := w.rowContainerForProbe.NumChunks() + for i := int(w.workerID); i < numChks; i += int(w.hashJoinCtx.concurrency) { + chk, err := w.rowContainerForProbe.GetChunk(i) + if err != nil { + // Catching the error and send it + joinResult.err = err + w.hashJoinCtx.joinResultCh <- joinResult + return + } + for j := 0; j < chk.NumRows(); j++ { + if !w.hashJoinCtx.outerMatchedStatus[i].UnsafeIsSet(j) { // process unmatched outer rows + w.joiner.onMissMatch(false, chk.GetRow(j), joinResult.chk) + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return + } + } + } + } + + if joinResult == nil { + return + } else if joinResult.err != nil || (joinResult.chk != nil && joinResult.chk.NumRows() > 0) { + w.hashJoinCtx.joinResultCh <- joinResult + } +} + +func (e *HashJoinExec) waitJoinWorkersAndCloseResultChan() { + e.workerWg.Wait() + if e.useOuterToBuild { + // Concurrently handling unmatched rows from the hash table at the tail + for i := uint(0); i < e.concurrency; i++ { + var workerID = i + e.workerWg.RunWithRecover(func() { e.probeWorkers[workerID].handleUnmatchedRowsFromHashTable() }, e.handleJoinWorkerPanic) + } + e.workerWg.Wait() + } + close(e.joinResultCh) +} + +func (w *probeWorker) runJoinWorker() { + probeTime := int64(0) + if w.hashJoinCtx.stats != nil { + start := time.Now() + defer func() { + t := time.Since(start) + atomic.AddInt64(&w.hashJoinCtx.stats.probe, probeTime) + atomic.AddInt64(&w.hashJoinCtx.stats.fetchAndProbe, int64(t)) + w.hashJoinCtx.stats.setMaxFetchAndProbeTime(int64(t)) + }() + } + + var ( + probeSideResult *chunk.Chunk + selected = make([]bool, 0, chunk.InitialCapacity) + ) + ok, joinResult := w.getNewJoinResult() + if !ok { + return + } + + // Read and filter probeSideResult, and join the probeSideResult with the build side rows. + emptyProbeSideResult := &probeChkResource{ + dest: w.probeResultCh, + } + hCtx := &hashContext{ + allTypes: w.hashJoinCtx.probeTypes, + keyColIdx: w.probeKeyColIdx, + naKeyColIdx: w.probeNAKeyColIdx, + } + for ok := true; ok; { + if w.hashJoinCtx.finished.Load() { + break + } + select { + case <-w.hashJoinCtx.closeCh: + return + case probeSideResult, ok = <-w.probeResultCh: + } + failpoint.Inject("ConsumeRandomPanic", nil) + if !ok { + break + } + start := time.Now() + if w.hashJoinCtx.useOuterToBuild { + ok, joinResult = w.join2ChunkForOuterHashJoin(probeSideResult, hCtx, joinResult) + } else { + ok, joinResult = w.join2Chunk(probeSideResult, hCtx, joinResult, selected) + } + probeTime += int64(time.Since(start)) + if !ok { + break + } + probeSideResult.Reset() + emptyProbeSideResult.chk = probeSideResult + w.probeChkResourceCh <- emptyProbeSideResult + } + // note joinResult.chk may be nil when getNewJoinResult fails in loops + if joinResult == nil { + return + } else if joinResult.err != nil || (joinResult.chk != nil && joinResult.chk.NumRows() > 0) { + w.hashJoinCtx.joinResultCh <- joinResult + } else if joinResult.chk != nil && joinResult.chk.NumRows() == 0 { + w.joinChkResourceCh <- joinResult.chk + } +} + +func (w *probeWorker) joinMatchedProbeSideRow2ChunkForOuterHashJoin(probeKey uint64, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { + var err error + w.buildSideRows, w.buildSideRowPtrs, err = w.rowContainerForProbe.GetMatchedRowsAndPtrs(probeKey, probeSideRow, hCtx, w.buildSideRows, w.buildSideRowPtrs, true) + buildSideRows, rowsPtrs := w.buildSideRows, w.buildSideRowPtrs + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + return true, joinResult + } + + iter := w.rowIters + iter.Reset(buildSideRows) + var outerMatchStatus []outerRowStatusFlag + rowIdx, ok := 0, false + for iter.Begin(); iter.Current() != iter.End(); { + outerMatchStatus, err = w.joiner.tryToMatchOuters(iter, probeSideRow, joinResult.chk, outerMatchStatus) + if err != nil { + joinResult.err = err + return false, joinResult + } + for i := range outerMatchStatus { + if outerMatchStatus[i] == outerRowMatched { + w.hashJoinCtx.outerMatchedStatus[rowsPtrs[rowIdx+i].ChkIdx].Set(int(rowsPtrs[rowIdx+i].RowIdx)) + } + } + rowIdx += len(outerMatchStatus) + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + return true, joinResult +} + +// joinNAALOSJMatchProbeSideRow2Chunk implement the matching logic for NA-AntiLeftOuterSemiJoin +func (w *probeWorker) joinNAALOSJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { + var ( + err error + ok bool + ) + if probeKeyNullBits == nil { + // step1: match the same key bucket first. + // because AntiLeftOuterSemiJoin cares about the scalar value. If we both have a match from null + // bucket and same key bucket, we should return the result as from same-key bucket + // rather than from null bucket. + w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) + buildSideRows := w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) != 0 { + iter1 := w.rowIters + iter1.Reset(buildSideRows) + for iter1.Begin(); iter1.Current() != iter1.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk, LeftNotNullRightNotNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid same-key bucket row from right side. + // as said in the comment, once we meet a same key (NOT IN semantic) in CNF, we can determine the result as . + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + } + // step2: match the null bucket secondly. + w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows = w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + // when reach here, it means we couldn't find a valid same key match from same-key bucket yet + // and the null bucket is empty. so the result should be . + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + iter2 := w.rowIters + iter2.Reset(buildSideRows) + for iter2.Begin(); iter2.Current() != iter2.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk, LeftNotNullRightHasNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid null bucket row from right side. + // as said in the comment, once we meet a null in CNF, we can determine the result as . + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + // step3: if we couldn't return it quickly in null bucket and same key bucket, here means two cases: + // case1: x NOT IN (empty set): if other key bucket don't have the valid rows yet. + // case2: x NOT IN (l,m,n...): if other key bucket do have the valid rows. + // both cases mean the result should be + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + // when left side has null values, all we want is to find a valid build side rows (past other condition) + // so we can return it as soon as possible. here means two cases: + // case1: NOT IN (empty set): ----------------------> result is . + // case2: NOT IN (at least a valid inner row) ------------------> result is . + // Step1: match null bucket (assumption that null bucket is quite smaller than all hash table bucket rows) + w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows := w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) != 0 { + iter1 := w.rowIters + iter1.Reset(buildSideRows) + for iter1.Begin(); iter1.Current() != iter1.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk, LeftHasNullRightHasNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid null bucket row from right side. (not empty) + // as said in the comment, once we found at least a valid row, we can determine the result as . + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + } + // Step2: match all hash table bucket build rows (use probeKeyNullBits to filter if any). + w.buildSideRows, err = w.rowContainerForProbe.GetAllMatchedRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows = w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, + // which means x NOT IN (empty set) or x NOT IN (l,m,n), the result should be + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + iter2 := w.rowIters + iter2.Reset(buildSideRows) + for iter2.Begin(); iter2.Current() != iter2.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk, LeftHasNullRightNotNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid same key bucket row from right side. (not empty) + // as said in the comment, once we found at least a valid row, we can determine the result as . + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + // step3: if we couldn't return it quickly in null bucket and all hash bucket, here means only one cases: + // case1: NOT IN (empty set): + // empty set comes from no rows from all bucket can pass other condition. the result should be + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult +} + +// joinNAASJMatchProbeSideRow2Chunk implement the matching logic for NA-AntiSemiJoin +func (w *probeWorker) joinNAASJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { + var ( + err error + ok bool + ) + if probeKeyNullBits == nil { + // step1: match null bucket first. + // need fetch the "valid" rows every time. (nullBits map check is necessary) + w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows := w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) != 0 { + iter1 := w.rowIters + iter1.Reset(buildSideRows) + for iter1.Begin(); iter1.Current() != iter1.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid null bucket row from right side. + // as said in the comment, once we meet a rhs null in CNF, we can determine the reject of lhs row. + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + } + // step2: then same key bucket. + w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) + buildSideRows = w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, + // which means x NOT IN (empty set), accept the rhs row. + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + iter2 := w.rowIters + iter2.Reset(buildSideRows) + for iter2.Begin(); iter2.Current() != iter2.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid same key bucket row from right side. + // as said in the comment, once we meet a false in CNF, we can determine the reject of lhs row. + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + // step3: if we couldn't return it quickly in null bucket and same key bucket, here means two cases: + // case1: x NOT IN (empty set): if other key bucket don't have the valid rows yet. + // case2: x NOT IN (l,m,n...): if other key bucket do have the valid rows. + // both cases should accept the rhs row. + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + // when left side has null values, all we want is to find a valid build side rows (passed from other condition) + // so we can return it as soon as possible. here means two cases: + // case1: NOT IN (empty set): ----------------------> accept rhs row. + // case2: NOT IN (at least a valid inner row) ------------------> unknown result, refuse rhs row. + // Step1: match null bucket (assumption that null bucket is quite smaller than all hash table bucket rows) + w.buildSideRows, err = w.rowContainerForProbe.GetNullBucketRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows := w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) != 0 { + iter1 := w.rowIters + iter1.Reset(buildSideRows) + for iter1.Begin(); iter1.Current() != iter1.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter1, joinResult.chk) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid null bucket row from right side. (not empty) + // as said in the comment, once we found at least a valid row, we can determine the reject of lhs row. + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + } + // Step2: match all hash table bucket build rows. + w.buildSideRows, err = w.rowContainerForProbe.GetAllMatchedRows(hCtx, probeSideRow, probeKeyNullBits, w.buildSideRows, w.needCheckBuildColPos, w.needCheckProbeColPos, w.needCheckBuildTypes, w.needCheckProbeTypes) + buildSideRows = w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + // when reach here, it means we couldn't return it quickly in null bucket, and same-bucket is empty, + // which means NOT IN (empty set) or NOT IN (no valid rows) accept the rhs row. + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + iter2 := w.rowIters + iter2.Reset(buildSideRows) + for iter2.Begin(); iter2.Current() != iter2.End(); { + matched, _, err := w.joiner.tryToMatchInners(probeSideRow, iter2, joinResult.chk) + if err != nil { + joinResult.err = err + return false, joinResult + } + // here matched means: there is a valid key row from right side. (not empty) + // as said in the comment, once we found at least a valid row, we can determine the reject of lhs row. + if matched { + return true, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + // step3: if we couldn't return it quickly in null bucket and all hash bucket, here means only one cases: + // case1: NOT IN (empty set): + // empty set comes from no rows from all bucket can pass other condition. we should accept the rhs row. + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult +} + +// joinNAAJMatchProbeSideRow2Chunk implement the matching priority logic for NA-AntiSemiJoin and NA-AntiLeftOuterSemiJoin +// there are some bucket-matching priority difference between them. +// +// Since NA-AntiSemiJoin don't need to append the scalar value with the left side row, there is a quick matching path. +// 1: lhs row has null: +// lhs row has null can't determine its result in advance, we should judge whether the right valid set is empty +// or not. For semantic like x NOT IN(y set), If y set is empty, the scalar result is 1; Otherwise, the result +// is 0. Since NA-AntiSemiJoin don't care about the scalar value, we just try to find a valid row from right side, +// once we found it then just return the left side row instantly. (same as NA-AntiLeftOuterSemiJoin) +// +// 2: lhs row without null: +// same-key bucket and null-bucket which should be the first to match? For semantic like x NOT IN(y set), once y +// set has a same key x, the scalar value is 0; else if y set has a null key, then the scalar value is null. Both +// of them lead the refuse of the lhs row without any difference. Since NA-AntiSemiJoin don't care about the scalar +// value, we can just match the null bucket first and refuse the lhs row as quickly as possible, because a null of +// yi in the CNF (x NA-EQ yi) can always determine a negative value (refuse lhs row) in advance here. +// +// For NA-AntiLeftOuterSemiJoin, we couldn't match null-bucket first, because once y set has a same key x and null +// key, we should return the result as left side row appended with a scalar value 0 which is from same key matching failure. +func (w *probeWorker) joinNAAJMatchProbeSideRow2Chunk(probeKey uint64, probeKeyNullBits *bitmap.ConcurrentBitmap, probeSideRow chunk.Row, hCtx *hashContext, joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { + naAntiSemiJoin := w.hashJoinCtx.joinType == plannercore.AntiSemiJoin && w.hashJoinCtx.isNullAware + naAntiLeftOuterSemiJoin := w.hashJoinCtx.joinType == plannercore.AntiLeftOuterSemiJoin && w.hashJoinCtx.isNullAware + if naAntiSemiJoin { + return w.joinNAASJMatchProbeSideRow2Chunk(probeKey, probeKeyNullBits, probeSideRow, hCtx, joinResult) + } + if naAntiLeftOuterSemiJoin { + return w.joinNAALOSJMatchProbeSideRow2Chunk(probeKey, probeKeyNullBits, probeSideRow, hCtx, joinResult) + } + // shouldn't be here, not a valid NAAJ. + return false, joinResult +} + +func (w *probeWorker) joinMatchedProbeSideRow2Chunk(probeKey uint64, probeSideRow chunk.Row, hCtx *hashContext, + joinResult *hashjoinWorkerResult) (bool, *hashjoinWorkerResult) { + var err error + w.buildSideRows, err = w.rowContainerForProbe.GetMatchedRows(probeKey, probeSideRow, hCtx, w.buildSideRows) + buildSideRows := w.buildSideRows + if err != nil { + joinResult.err = err + return false, joinResult + } + if len(buildSideRows) == 0 { + w.joiner.onMissMatch(false, probeSideRow, joinResult.chk) + return true, joinResult + } + iter := w.rowIters + iter.Reset(buildSideRows) + hasMatch, hasNull, ok := false, false, false + for iter.Begin(); iter.Current() != iter.End(); { + matched, isNull, err := w.joiner.tryToMatchInners(probeSideRow, iter, joinResult.chk) + if err != nil { + joinResult.err = err + return false, joinResult + } + hasMatch = hasMatch || matched + hasNull = hasNull || isNull + + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + if !hasMatch { + w.joiner.onMissMatch(hasNull, probeSideRow, joinResult.chk) + } + return true, joinResult +} + +func (w *probeWorker) getNewJoinResult() (bool, *hashjoinWorkerResult) { + joinResult := &hashjoinWorkerResult{ + src: w.joinChkResourceCh, + } + ok := true + select { + case <-w.hashJoinCtx.closeCh: + ok = false + case joinResult.chk, ok = <-w.joinChkResourceCh: + } + return ok, joinResult +} + +func (w *probeWorker) join2Chunk(probeSideChk *chunk.Chunk, hCtx *hashContext, joinResult *hashjoinWorkerResult, + selected []bool) (ok bool, _ *hashjoinWorkerResult) { + var err error + selected, err = expression.VectorizedFilter(w.hashJoinCtx.sessCtx, w.hashJoinCtx.outerFilter, chunk.NewIterator4Chunk(probeSideChk), selected) + if err != nil { + joinResult.err = err + return false, joinResult + } + + numRows := probeSideChk.NumRows() + hCtx.initHash(numRows) + // By now, path 1 and 2 won't be conducted at the same time. + // 1: write the row data of join key to hashVals. (normal EQ key should ignore the null values.) null-EQ for Except statement is an exception. + for keyIdx, i := range hCtx.keyColIdx { + ignoreNull := len(w.hashJoinCtx.isNullEQ) > keyIdx && w.hashJoinCtx.isNullEQ[keyIdx] + err = codec.HashChunkSelected(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull, selected, ignoreNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + } + // 2: write the row data of NA join key to hashVals. (NA EQ key should collect all row including null value, store null value in a special position) + isNAAJ := len(hCtx.naKeyColIdx) > 0 + for keyIdx, i := range hCtx.naKeyColIdx { + // NAAJ won't ignore any null values, but collect them up to probe. + err = codec.HashChunkSelected(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull, selected, false) + if err != nil { + joinResult.err = err + return false, joinResult + } + // after fetch one NA column, collect the null value to null bitmap for every row. (use hasNull flag to accelerate) + // eg: if a NA Join cols is (a, b, c), for every build row here we maintained a 3-bit map to mark which column is null for them. + for rowIdx := 0; rowIdx < numRows; rowIdx++ { + if hCtx.hasNull[rowIdx] { + hCtx.naColNullBitMap[rowIdx].UnsafeSet(keyIdx) + // clean and try fetch next NA join col. + hCtx.hasNull[rowIdx] = false + hCtx.naHasNull[rowIdx] = true + } + } + } + + for i := range selected { + killed := atomic.LoadUint32(&w.hashJoinCtx.sessCtx.GetSessionVars().Killed) == 1 + failpoint.Inject("killedInJoin2Chunk", func(val failpoint.Value) { + if val.(bool) { + killed = true + } + }) + if killed { + joinResult.err = exeerrors.ErrQueryInterrupted + return false, joinResult + } + if isNAAJ { + if !selected[i] { + // since this is the case of using inner to build, so for an outer row unselected, we should fill the result when it's outer join. + w.joiner.onMissMatch(false, probeSideChk.GetRow(i), joinResult.chk) + } + if hCtx.naHasNull[i] { + // here means the probe join connecting column has null value in it and this is special for matching all the hash buckets + // for it. (probeKey is not necessary here) + probeRow := probeSideChk.GetRow(i) + ok, joinResult = w.joinNAAJMatchProbeSideRow2Chunk(0, hCtx.naColNullBitMap[i].Clone(), probeRow, hCtx, joinResult) + if !ok { + return false, joinResult + } + } else { + // here means the probe join connecting column without null values, where we should match same key bucket and null bucket for it at its order. + // step1: process same key matched probe side rows + probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) + ok, joinResult = w.joinNAAJMatchProbeSideRow2Chunk(probeKey, nil, probeRow, hCtx, joinResult) + if !ok { + return false, joinResult + } + } + } else { + // since this is the case of using inner to build, so for an outer row unselected, we should fill the result when it's outer join. + if !selected[i] || hCtx.hasNull[i] { // process unmatched probe side rows + w.joiner.onMissMatch(false, probeSideChk.GetRow(i), joinResult.chk) + } else { // process matched probe side rows + probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) + ok, joinResult = w.joinMatchedProbeSideRow2Chunk(probeKey, probeRow, hCtx, joinResult) + if !ok { + return false, joinResult + } + } + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + return true, joinResult +} + +// join2ChunkForOuterHashJoin joins chunks when using the outer to build a hash table (refer to outer hash join) +func (w *probeWorker) join2ChunkForOuterHashJoin(probeSideChk *chunk.Chunk, hCtx *hashContext, joinResult *hashjoinWorkerResult) (ok bool, _ *hashjoinWorkerResult) { + hCtx.initHash(probeSideChk.NumRows()) + for keyIdx, i := range hCtx.keyColIdx { + err := codec.HashChunkColumns(w.rowContainerForProbe.sc, hCtx.hashVals, probeSideChk, hCtx.allTypes[keyIdx], i, hCtx.buf, hCtx.hasNull) + if err != nil { + joinResult.err = err + return false, joinResult + } + } + for i := 0; i < probeSideChk.NumRows(); i++ { + killed := atomic.LoadUint32(&w.hashJoinCtx.sessCtx.GetSessionVars().Killed) == 1 + failpoint.Inject("killedInJoin2ChunkForOuterHashJoin", func(val failpoint.Value) { + if val.(bool) { + killed = true + } + }) + if killed { + joinResult.err = exeerrors.ErrQueryInterrupted + return false, joinResult + } + probeKey, probeRow := hCtx.hashVals[i].Sum64(), probeSideChk.GetRow(i) + ok, joinResult = w.joinMatchedProbeSideRow2ChunkForOuterHashJoin(probeKey, probeRow, hCtx, joinResult) + if !ok { + return false, joinResult + } + if joinResult.chk.IsFull() { + w.hashJoinCtx.joinResultCh <- joinResult + ok, joinResult = w.getNewJoinResult() + if !ok { + return false, joinResult + } + } + } + return true, joinResult +} + +// Next implements the Executor Next interface. +// hash join constructs the result following these steps: +// step 1. fetch data from build side child and build a hash table; +// step 2. fetch data from probe child in a background goroutine and probe the hash table in multiple join workers. +func (e *HashJoinExec) Next(ctx context.Context, req *chunk.Chunk) (err error) { + if !e.prepared { + e.buildFinished = make(chan error, 1) + hCtx := &hashContext{ + allTypes: e.buildTypes, + keyColIdx: e.buildWorker.buildKeyColIdx, + naKeyColIdx: e.buildWorker.buildNAKeyColIdx, + } + e.rowContainer = newHashRowContainer(e.Ctx(), hCtx, exec.RetTypes(e.buildWorker.buildSideExec)) + // we shallow copies rowContainer for each probe worker to avoid lock contention + for i := uint(0); i < e.concurrency; i++ { + if i == 0 { + e.probeWorkers[i].rowContainerForProbe = e.rowContainer + } else { + e.probeWorkers[i].rowContainerForProbe = e.rowContainer.ShallowCopy() + } + } + for i := uint(0); i < e.concurrency; i++ { + e.probeWorkers[i].rowIters = chunk.NewIterator4Slice([]chunk.Row{}).(*chunk.Iterator4Slice) + } + e.workerWg.RunWithRecover(func() { + defer trace.StartRegion(ctx, "HashJoinHashTableBuilder").End() + e.fetchAndBuildHashTable(ctx) + }, e.handleFetchAndBuildHashTablePanic) + e.fetchAndProbeHashTable(ctx) + e.prepared = true + } + if e.isOuterJoin { + atomic.StoreInt64(&e.probeSideTupleFetcher.requiredRows, int64(req.RequiredRows())) + } + req.Reset() + + result, ok := <-e.joinResultCh + if !ok { + return nil + } + if result.err != nil { + e.finished.Store(true) + return result.err + } + req.SwapColumns(result.chk) + result.src <- result.chk + return nil +} + +func (e *HashJoinExec) handleFetchAndBuildHashTablePanic(r interface{}) { + if r != nil { + e.buildFinished <- errors.Errorf("%v", r) + } + close(e.buildFinished) +} + +func (e *HashJoinExec) fetchAndBuildHashTable(ctx context.Context) { + if e.stats != nil { + start := time.Now() + defer func() { + e.stats.fetchAndBuildHashTable = time.Since(start) + }() + } + // buildSideResultCh transfers build side chunk from build side fetch to build hash table. + buildSideResultCh := make(chan *chunk.Chunk, 1) + doneCh := make(chan struct{}) + fetchBuildSideRowsOk := make(chan error, 1) + e.workerWg.RunWithRecover( + func() { + defer trace.StartRegion(ctx, "HashJoinBuildSideFetcher").End() + e.buildWorker.fetchBuildSideRows(ctx, buildSideResultCh, fetchBuildSideRowsOk, doneCh) + }, + func(r interface{}) { + if r != nil { + fetchBuildSideRowsOk <- errors.Errorf("%v", r) + } + close(fetchBuildSideRowsOk) + }, + ) + + // TODO: Parallel build hash table. Currently not support because `unsafeHashTable` is not thread-safe. + err := e.buildWorker.buildHashTableForList(buildSideResultCh) + if err != nil { + e.buildFinished <- errors.Trace(err) + close(doneCh) + } + // Wait fetchBuildSideRows be finished. + // 1. if buildHashTableForList fails + // 2. if probeSideResult.NumRows() == 0, fetchProbeSideChunks will not wait for the build side. + channel.Clear(buildSideResultCh) + // Check whether err is nil to avoid sending redundant error into buildFinished. + if err == nil { + if err = <-fetchBuildSideRowsOk; err != nil { + e.buildFinished <- err + } + } +} + +// buildHashTableForList builds hash table from `list`. +func (w *buildWorker) buildHashTableForList(buildSideResultCh <-chan *chunk.Chunk) error { + var err error + var selected []bool + rowContainer := w.hashJoinCtx.rowContainer + rowContainer.GetMemTracker().AttachTo(w.hashJoinCtx.memTracker) + rowContainer.GetMemTracker().SetLabel(memory.LabelForBuildSideResult) + rowContainer.GetDiskTracker().AttachTo(w.hashJoinCtx.diskTracker) + rowContainer.GetDiskTracker().SetLabel(memory.LabelForBuildSideResult) + if variable.EnableTmpStorageOnOOM.Load() { + actionSpill := rowContainer.ActionSpill() + failpoint.Inject("testRowContainerSpill", func(val failpoint.Value) { + if val.(bool) { + actionSpill = rowContainer.rowContainer.ActionSpillForTest() + defer actionSpill.(*chunk.SpillDiskAction).WaitForTest() + } + }) + w.hashJoinCtx.sessCtx.GetSessionVars().MemTracker.FallbackOldAndSetNewAction(actionSpill) + } + for chk := range buildSideResultCh { + if w.hashJoinCtx.finished.Load() { + return nil + } + if !w.hashJoinCtx.useOuterToBuild { + err = rowContainer.PutChunk(chk, w.hashJoinCtx.isNullEQ) + } else { + var bitMap = bitmap.NewConcurrentBitmap(chk.NumRows()) + w.hashJoinCtx.outerMatchedStatus = append(w.hashJoinCtx.outerMatchedStatus, bitMap) + w.hashJoinCtx.memTracker.Consume(bitMap.BytesConsumed()) + if len(w.hashJoinCtx.outerFilter) == 0 { + err = w.hashJoinCtx.rowContainer.PutChunk(chk, w.hashJoinCtx.isNullEQ) + } else { + selected, err = expression.VectorizedFilter(w.hashJoinCtx.sessCtx, w.hashJoinCtx.outerFilter, chunk.NewIterator4Chunk(chk), selected) + if err != nil { + return err + } + err = rowContainer.PutChunkSelected(chk, selected, w.hashJoinCtx.isNullEQ) + } + } + failpoint.Inject("ConsumeRandomPanic", nil) + if err != nil { + return err + } + } + return nil +} + +// NestedLoopApplyExec is the executor for apply. +type NestedLoopApplyExec struct { + exec.BaseExecutor + + ctx sessionctx.Context + innerRows []chunk.Row + cursor int + innerExec exec.Executor + outerExec exec.Executor + innerFilter expression.CNFExprs + outerFilter expression.CNFExprs + + joiner joiner + + cache *applycache.ApplyCache + canUseCache bool + cacheHitCounter int + cacheAccessCounter int + + outerSchema []*expression.CorrelatedColumn + + outerChunk *chunk.Chunk + outerChunkCursor int + outerSelected []bool + innerList *chunk.List + innerChunk *chunk.Chunk + innerSelected []bool + innerIter chunk.Iterator + outerRow *chunk.Row + hasMatch bool + hasNull bool + + outer bool + + memTracker *memory.Tracker // track memory usage. +} + +// Close implements the Executor interface. +func (e *NestedLoopApplyExec) Close() error { + e.innerRows = nil + e.memTracker = nil + if e.RuntimeStats() != nil { + runtimeStats := newJoinRuntimeStats() + if e.canUseCache { + var hitRatio float64 + if e.cacheAccessCounter > 0 { + hitRatio = float64(e.cacheHitCounter) / float64(e.cacheAccessCounter) + } + runtimeStats.setCacheInfo(true, hitRatio) + } else { + runtimeStats.setCacheInfo(false, 0) + } + runtimeStats.SetConcurrencyInfo(execdetails.NewConcurrencyInfo("Concurrency", 0)) + defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), runtimeStats) + } + return e.outerExec.Close() +} + +// Open implements the Executor interface. +func (e *NestedLoopApplyExec) Open(ctx context.Context) error { + err := e.outerExec.Open(ctx) + if err != nil { + return err + } + e.cursor = 0 + e.innerRows = e.innerRows[:0] + e.outerChunk = exec.TryNewCacheChunk(e.outerExec) + e.innerChunk = exec.TryNewCacheChunk(e.innerExec) + e.innerList = chunk.NewList(exec.RetTypes(e.innerExec), e.InitCap(), e.MaxChunkSize()) + + e.memTracker = memory.NewTracker(e.ID(), -1) + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + + e.innerList.GetMemTracker().SetLabel(memory.LabelForInnerList) + e.innerList.GetMemTracker().AttachTo(e.memTracker) + + if e.canUseCache { + e.cache, err = applycache.NewApplyCache(e.ctx) + if err != nil { + return err + } + e.cacheHitCounter = 0 + e.cacheAccessCounter = 0 + e.cache.GetMemTracker().AttachTo(e.memTracker) + } + return nil +} + +// aggExecutorTreeInputEmpty checks whether the executor tree returns empty if without aggregate operators. +// Note that, the prerequisite is that this executor tree has been executed already and it returns one row. +func aggExecutorTreeInputEmpty(e exec.Executor) bool { + children := e.Base().AllChildren() + if len(children) == 0 { + return false + } + if len(children) > 1 { + _, ok := e.(*UnionExec) + if !ok { + // It is a Join executor. + return false + } + for _, child := range children { + if !aggExecutorTreeInputEmpty(child) { + return false + } + } + return true + } + // Single child executors. + if aggExecutorTreeInputEmpty(children[0]) { + return true + } + if hashAgg, ok := e.(*aggregate.HashAggExec); ok { + return hashAgg.IsChildReturnEmpty + } + if streamAgg, ok := e.(*aggregate.StreamAggExec); ok { + return streamAgg.IsChildReturnEmpty + } + return false +} + +func (e *NestedLoopApplyExec) fetchSelectedOuterRow(ctx context.Context, chk *chunk.Chunk) (*chunk.Row, error) { + outerIter := chunk.NewIterator4Chunk(e.outerChunk) + for { + if e.outerChunkCursor >= e.outerChunk.NumRows() { + err := exec.Next(ctx, e.outerExec, e.outerChunk) + if err != nil { + return nil, err + } + if e.outerChunk.NumRows() == 0 { + return nil, nil + } + e.outerSelected, err = expression.VectorizedFilter(e.ctx, e.outerFilter, outerIter, e.outerSelected) + if err != nil { + return nil, err + } + // For cases like `select count(1), (select count(1) from s where s.a > t.a) as sub from t where t.a = 1`, + // if outer child has no row satisfying `t.a = 1`, `sub` should be `null` instead of `0` theoretically; however, the + // outer `count(1)` produces one row <0, null> over the empty input, we should specially mark this outer row + // as not selected, to trigger the mismatch join procedure. + if e.outerChunkCursor == 0 && e.outerChunk.NumRows() == 1 && e.outerSelected[0] && aggExecutorTreeInputEmpty(e.outerExec) { + e.outerSelected[0] = false + } + e.outerChunkCursor = 0 + } + outerRow := e.outerChunk.GetRow(e.outerChunkCursor) + selected := e.outerSelected[e.outerChunkCursor] + e.outerChunkCursor++ + if selected { + return &outerRow, nil + } else if e.outer { + e.joiner.onMissMatch(false, outerRow, chk) + if chk.IsFull() { + return nil, nil + } + } + } +} + +// fetchAllInners reads all data from the inner table and stores them in a List. +func (e *NestedLoopApplyExec) fetchAllInners(ctx context.Context) error { + err := e.innerExec.Open(ctx) + defer terror.Call(e.innerExec.Close) + if err != nil { + return err + } + + if e.canUseCache { + // create a new one since it may be in the cache + e.innerList = chunk.NewList(exec.RetTypes(e.innerExec), e.InitCap(), e.MaxChunkSize()) + } else { + e.innerList.Reset() + } + innerIter := chunk.NewIterator4Chunk(e.innerChunk) + for { + err := exec.Next(ctx, e.innerExec, e.innerChunk) + if err != nil { + return err + } + if e.innerChunk.NumRows() == 0 { + return nil + } + + e.innerSelected, err = expression.VectorizedFilter(e.ctx, e.innerFilter, innerIter, e.innerSelected) + if err != nil { + return err + } + for row := innerIter.Begin(); row != innerIter.End(); row = innerIter.Next() { + if e.innerSelected[row.Idx()] { + e.innerList.AppendRow(row) + } + } + } +} + +// Next implements the Executor interface. +func (e *NestedLoopApplyExec) Next(ctx context.Context, req *chunk.Chunk) (err error) { + req.Reset() + for { + if e.innerIter == nil || e.innerIter.Current() == e.innerIter.End() { + if e.outerRow != nil && !e.hasMatch { + e.joiner.onMissMatch(e.hasNull, *e.outerRow, req) + } + e.outerRow, err = e.fetchSelectedOuterRow(ctx, req) + if e.outerRow == nil || err != nil { + return err + } + e.hasMatch = false + e.hasNull = false + + if e.canUseCache { + var key []byte + for _, col := range e.outerSchema { + *col.Data = e.outerRow.GetDatum(col.Index, col.RetType) + key, err = codec.EncodeKey(e.Ctx().GetSessionVars().StmtCtx, key, *col.Data) + if err != nil { + return err + } + } + e.cacheAccessCounter++ + value, err := e.cache.Get(key) + if err != nil { + return err + } + if value != nil { + e.innerList = value + e.cacheHitCounter++ + } else { + err = e.fetchAllInners(ctx) + if err != nil { + return err + } + if _, err := e.cache.Set(key, e.innerList); err != nil { + return err + } + } + } else { + for _, col := range e.outerSchema { + *col.Data = e.outerRow.GetDatum(col.Index, col.RetType) + } + err = e.fetchAllInners(ctx) + if err != nil { + return err + } + } + e.innerIter = chunk.NewIterator4List(e.innerList) + e.innerIter.Begin() + } + + matched, isNull, err := e.joiner.tryToMatchInners(*e.outerRow, e.innerIter, req) + e.hasMatch = e.hasMatch || matched + e.hasNull = e.hasNull || isNull + + if err != nil || req.IsFull() { + return err + } + } +} + +// cacheInfo is used to save the concurrency information of the executor operator +type cacheInfo struct { + hitRatio float64 + useCache bool +} + +type joinRuntimeStats struct { + *execdetails.RuntimeStatsWithConcurrencyInfo + + applyCache bool + cache cacheInfo + hasHashStat bool + hashStat hashStatistic +} + +func newJoinRuntimeStats() *joinRuntimeStats { + stats := &joinRuntimeStats{ + RuntimeStatsWithConcurrencyInfo: &execdetails.RuntimeStatsWithConcurrencyInfo{}, + } + return stats +} + +// setCacheInfo sets the cache information. Only used for apply executor. +func (e *joinRuntimeStats) setCacheInfo(useCache bool, hitRatio float64) { + e.Lock() + e.applyCache = true + e.cache.useCache = useCache + e.cache.hitRatio = hitRatio + e.Unlock() +} + +func (e *joinRuntimeStats) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 16)) + buf.WriteString(e.RuntimeStatsWithConcurrencyInfo.String()) + if e.applyCache { + if e.cache.useCache { + fmt.Fprintf(buf, ", cache:ON, cacheHitRatio:%.3f%%", e.cache.hitRatio*100) + } else { + buf.WriteString(", cache:OFF") + } + } + if e.hasHashStat { + buf.WriteString(", " + e.hashStat.String()) + } + return buf.String() +} + +// Tp implements the RuntimeStats interface. +func (*joinRuntimeStats) Tp() int { + return execdetails.TpJoinRuntimeStats +} + +func (e *joinRuntimeStats) Clone() execdetails.RuntimeStats { + newJRS := &joinRuntimeStats{ + RuntimeStatsWithConcurrencyInfo: e.RuntimeStatsWithConcurrencyInfo, + applyCache: e.applyCache, + cache: e.cache, + hasHashStat: e.hasHashStat, + hashStat: e.hashStat, + } + return newJRS +} + +type hashJoinRuntimeStats struct { + fetchAndBuildHashTable time.Duration + hashStat hashStatistic + fetchAndProbe int64 + probe int64 + concurrent int + maxFetchAndProbe int64 +} + +func (e *hashJoinRuntimeStats) setMaxFetchAndProbeTime(t int64) { + for { + value := atomic.LoadInt64(&e.maxFetchAndProbe) + if t <= value { + return + } + if atomic.CompareAndSwapInt64(&e.maxFetchAndProbe, value, t) { + return + } + } +} + +// Tp implements the RuntimeStats interface. +func (*hashJoinRuntimeStats) Tp() int { + return execdetails.TpHashJoinRuntimeStats +} + +func (e *hashJoinRuntimeStats) String() string { + buf := bytes.NewBuffer(make([]byte, 0, 128)) + if e.fetchAndBuildHashTable > 0 { + buf.WriteString("build_hash_table:{total:") + buf.WriteString(execdetails.FormatDuration(e.fetchAndBuildHashTable)) + buf.WriteString(", fetch:") + buf.WriteString(execdetails.FormatDuration(e.fetchAndBuildHashTable - e.hashStat.buildTableElapse)) + buf.WriteString(", build:") + buf.WriteString(execdetails.FormatDuration(e.hashStat.buildTableElapse)) + buf.WriteString("}") + } + if e.probe > 0 { + buf.WriteString(", probe:{concurrency:") + buf.WriteString(strconv.Itoa(e.concurrent)) + buf.WriteString(", total:") + buf.WriteString(execdetails.FormatDuration(time.Duration(e.fetchAndProbe))) + buf.WriteString(", max:") + buf.WriteString(execdetails.FormatDuration(time.Duration(atomic.LoadInt64(&e.maxFetchAndProbe)))) + buf.WriteString(", probe:") + buf.WriteString(execdetails.FormatDuration(time.Duration(e.probe))) + buf.WriteString(", fetch:") + buf.WriteString(execdetails.FormatDuration(time.Duration(e.fetchAndProbe - e.probe))) + if e.hashStat.probeCollision > 0 { + buf.WriteString(", probe_collision:") + buf.WriteString(strconv.FormatInt(e.hashStat.probeCollision, 10)) + } + buf.WriteString("}") + } + return buf.String() +} + +func (e *hashJoinRuntimeStats) Clone() execdetails.RuntimeStats { + return &hashJoinRuntimeStats{ + fetchAndBuildHashTable: e.fetchAndBuildHashTable, + hashStat: e.hashStat, + fetchAndProbe: e.fetchAndProbe, + probe: e.probe, + concurrent: e.concurrent, + maxFetchAndProbe: e.maxFetchAndProbe, + } +} + +func (e *hashJoinRuntimeStats) Merge(rs execdetails.RuntimeStats) { + tmp, ok := rs.(*hashJoinRuntimeStats) + if !ok { + return + } + e.fetchAndBuildHashTable += tmp.fetchAndBuildHashTable + e.hashStat.buildTableElapse += tmp.hashStat.buildTableElapse + e.hashStat.probeCollision += tmp.hashStat.probeCollision + e.fetchAndProbe += tmp.fetchAndProbe + e.probe += tmp.probe + if e.maxFetchAndProbe < tmp.maxFetchAndProbe { + e.maxFetchAndProbe = tmp.maxFetchAndProbe + } +} diff --git a/executor/join_pkg_test.go b/pkg/executor/join_pkg_test.go similarity index 93% rename from executor/join_pkg_test.go rename to pkg/executor/join_pkg_test.go index 0140b3e5a127b..1599204e2335b 100644 --- a/executor/join_pkg_test.go +++ b/pkg/executor/join_pkg_test.go @@ -20,17 +20,17 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) func TestJoinExec(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testRowContainerSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testRowContainerSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testRowContainerSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testRowContainerSpill")) }() colTypes := []*types.FieldType{ types.NewFieldType(mysql.TypeLonglong), diff --git a/pkg/executor/join_test.go b/pkg/executor/join_test.go new file mode 100644 index 0000000000000..51203954716ca --- /dev/null +++ b/pkg/executor/join_test.go @@ -0,0 +1,100 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" +) + +func TestNaturalJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (a int, b int)") + tk.MustExec("create table t2 (a int, c int)") + tk.MustExec("insert t1 values (1,2), (10,20), (0,0)") + tk.MustExec("insert t2 values (1,3), (100,200), (0,0)") + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + executorSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } +} + +func TestUsingAndNaturalJoinSchema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3, t4") + tk.MustExec("create table t1 (c int, b int);") + tk.MustExec("create table t2 (a int, b int);") + tk.MustExec("create table t3 (b int, c int);") + tk.MustExec("create table t4 (y int, c int);") + + tk.MustExec("insert into t1 values (10,1);") + tk.MustExec("insert into t1 values (3 ,1);") + tk.MustExec("insert into t1 values (3 ,2);") + tk.MustExec("insert into t2 values (2, 1);") + tk.MustExec("insert into t3 values (1, 3);") + tk.MustExec("insert into t3 values (1,10);") + tk.MustExec("insert into t4 values (11,3);") + tk.MustExec("insert into t4 values (2, 3);") + + var input []string + var output []struct { + SQL string + Res []string + } + executorSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } +} + +func TestTiDBNAAJ(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@session.tidb_enable_null_aware_anti_join=0;") + tk.MustExec("create table t(a decimal(40,0), b bigint(20) not null);") + tk.MustExec("insert into t values(7,8),(7,8),(3,4),(3,4),(9,2),(9,2),(2,0),(2,0),(0,4),(0,4),(8,8),(8,8),(6,1),(6,1),(NULL, 0),(NULL,0);") + tk.MustQuery("select ( table1 . a , table1 . b ) NOT IN ( SELECT 3 , 2 UNION SELECT 9, 2 ) AS field2 from t as table1 order by field2;").Check(testkit.Rows( + "0", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")) + tk.MustExec("set @@session.tidb_enable_null_aware_anti_join=1;") + tk.MustQuery("select ( table1 . a , table1 . b ) NOT IN ( SELECT 3 , 2 UNION SELECT 9, 2 ) AS field2 from t as table1 order by field2;").Check(testkit.Rows( + "0", "0", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")) +} diff --git a/executor/joiner.go b/pkg/executor/joiner.go similarity index 99% rename from executor/joiner.go rename to pkg/executor/joiner.go index 907823c928882..db46d6ccebb3f 100644 --- a/executor/joiner.go +++ b/pkg/executor/joiner.go @@ -15,12 +15,12 @@ package executor import ( - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/executor/joiner_test.go b/pkg/executor/joiner_test.go similarity index 94% rename from executor/joiner_test.go rename to pkg/executor/joiner_test.go index ea408c869b22a..9cedbb8f95f93 100644 --- a/executor/joiner_test.go +++ b/pkg/executor/joiner_test.go @@ -18,10 +18,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/executor/load_data.go b/pkg/executor/load_data.go similarity index 96% rename from executor/load_data.go rename to pkg/executor/load_data.go index 201235f637441..e882fae1cbe9e 100644 --- a/executor/load_data.go +++ b/pkg/executor/load_data.go @@ -27,24 +27,24 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/mydump" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/executor/asyncloaddata" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/asyncloaddata" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/executor/load_stats.go b/pkg/executor/load_stats.go similarity index 90% rename from executor/load_stats.go rename to pkg/executor/load_stats.go index 3b1e63904ec2e..e3e552d67ec55 100644 --- a/executor/load_stats.go +++ b/pkg/executor/load_stats.go @@ -19,12 +19,12 @@ import ( "encoding/json" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/util/chunk" ) var _ exec.Executor = &LoadStatsExec{} diff --git a/pkg/executor/lockstats/BUILD.bazel b/pkg/executor/lockstats/BUILD.bazel new file mode 100644 index 0000000000000..7e9bb0b700c7c --- /dev/null +++ b/pkg/executor/lockstats/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lockstats", + srcs = [ + "lock_stats_executor.go", + "unlock_stats_executor.go", + ], + importpath = "github.com/pingcap/tidb/pkg/executor/lockstats", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/executor/internal/exec", + "//pkg/infoschema", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/statistics/handle/util", + "//pkg/table/tables", + "//pkg/util/chunk", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "lockstats_test", + timeout = "short", + srcs = ["lock_stats_executor_test.go"], + embed = [":lockstats"], + flaky = True, + deps = [ + "//pkg/infoschema", + "//pkg/parser/ast", + "//pkg/parser/model", + "@com_github_stretchr_testify//require", + ], +) diff --git a/executor/lockstats/lock_stats_executor.go b/pkg/executor/lockstats/lock_stats_executor.go similarity index 92% rename from executor/lockstats/lock_stats_executor.go rename to pkg/executor/lockstats/lock_stats_executor.go index 39660c05ae576..50852883882ed 100644 --- a/executor/lockstats/lock_stats_executor.go +++ b/pkg/executor/lockstats/lock_stats_executor.go @@ -19,14 +19,14 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/chunk" ) var _ exec.Executor = &LockExec{} diff --git a/executor/lockstats/lock_stats_executor_test.go b/pkg/executor/lockstats/lock_stats_executor_test.go similarity index 95% rename from executor/lockstats/lock_stats_executor_test.go rename to pkg/executor/lockstats/lock_stats_executor_test.go index 356576dc171db..9e9d35cd8030e 100644 --- a/executor/lockstats/lock_stats_executor_test.go +++ b/pkg/executor/lockstats/lock_stats_executor_test.go @@ -17,9 +17,9 @@ package lockstats import ( "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" ) diff --git a/executor/lockstats/unlock_stats_executor.go b/pkg/executor/lockstats/unlock_stats_executor.go similarity index 93% rename from executor/lockstats/unlock_stats_executor.go rename to pkg/executor/lockstats/unlock_stats_executor.go index 80ff2a7f6e827..499745ef65e75 100644 --- a/executor/lockstats/unlock_stats_executor.go +++ b/pkg/executor/lockstats/unlock_stats_executor.go @@ -19,10 +19,10 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/chunk" ) var _ exec.Executor = &UnlockExec{} diff --git a/pkg/executor/main_test.go b/pkg/executor/main_test.go new file mode 100644 index 0000000000000..54f2c305a8ab5 --- /dev/null +++ b/pkg/executor/main_test.go @@ -0,0 +1,74 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) +var prepareMergeSuiteData testdata.TestData +var executorSuiteData testdata.TestData +var pointGetSuiteData testdata.TestData +var slowQuerySuiteData testdata.TestData + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + testDataMap.LoadTestSuiteData("testdata", "executor_suite") + testDataMap.LoadTestSuiteData("testdata", "prepare_suite") + testDataMap.LoadTestSuiteData("testdata", "point_get_suite") + testDataMap.LoadTestSuiteData("testdata", "slow_query_suite") + executorSuiteData = testDataMap["executor_suite"] + prepareMergeSuiteData = testDataMap["prepare_suite"] + pointGetSuiteData = testDataMap["point_get_suite"] + slowQuerySuiteData = testDataMap["slow_query_suite"] + + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + variable.StatsCacheMemQuota.Store(5000) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/executor/mem_reader.go b/pkg/executor/mem_reader.go similarity index 97% rename from executor/mem_reader.go rename to pkg/executor/mem_reader.go index 55d404cf3f19c..6fad3d09b9a26 100644 --- a/executor/mem_reader.go +++ b/pkg/executor/mem_reader.go @@ -19,22 +19,22 @@ import ( "slices" "github.com/pingcap/errors" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - transaction "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + transaction "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/tracing" ) type memReader interface { diff --git a/executor/memtable_reader.go b/pkg/executor/memtable_reader.go similarity index 97% rename from executor/memtable_reader.go rename to pkg/executor/memtable_reader.go index a0f0e9b1625b3..e1e21c456797e 100644 --- a/executor/memtable_reader.go +++ b/pkg/executor/memtable_reader.go @@ -32,23 +32,23 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/diagnosticspb" "github.com/pingcap/sysutil" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/set" "google.golang.org/grpc" "google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials/insecure" diff --git a/executor/memtable_reader_test.go b/pkg/executor/memtable_reader_test.go similarity index 98% rename from executor/memtable_reader_test.go rename to pkg/executor/memtable_reader_test.go index f6d98d4ec24fc..f968b319bbaee 100644 --- a/executor/memtable_reader_test.go +++ b/pkg/executor/memtable_reader_test.go @@ -29,9 +29,9 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/fn" "github.com/pingcap/sysutil" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/pdapi" pmodel "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -40,7 +40,7 @@ import ( func TestMetricTableData(t *testing.T) { store := testkit.CreateMockStore(t) - fpName := "github.com/pingcap/tidb/executor/mockMetricsPromData" + fpName := "github.com/pingcap/tidb/pkg/executor/mockMetricsPromData" require.NoError(t, failpoint.Enable(fpName, "return")) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -161,7 +161,7 @@ func TestTiDBClusterConfig(t *testing.T) { } } - fpName := "github.com/pingcap/tidb/executor/mockClusterConfigServerInfo" + fpName := "github.com/pingcap/tidb/pkg/executor/mockClusterConfigServerInfo" fpExpr := strings.Join(servers, ";") require.NoError(t, failpoint.Enable(fpName, fmt.Sprintf(`return("%s")`, fpExpr))) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -853,7 +853,7 @@ func TestTiDBClusterLog(t *testing.T) { for _, s := range testServers { servers = append(servers, strings.Join([]string{s.typ, s.address, s.address}, ",")) } - fpName := "github.com/pingcap/tidb/executor/mockClusterLogServerInfo" + fpName := "github.com/pingcap/tidb/pkg/executor/mockClusterLogServerInfo" fpExpr := strings.Join(servers, ";") require.NoError(t, failpoint.Enable(fpName, fmt.Sprintf(`return("%s")`, fpExpr))) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() @@ -886,7 +886,7 @@ func TestTiDBClusterLog(t *testing.T) { func TestTiDBClusterLogError(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - fpName := "github.com/pingcap/tidb/executor/mockClusterLogServerInfo" + fpName := "github.com/pingcap/tidb/pkg/executor/mockClusterLogServerInfo" require.NoError(t, failpoint.Enable(fpName, `return("")`)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() diff --git a/executor/merge_join.go b/pkg/executor/merge_join.go similarity index 96% rename from executor/merge_join.go rename to pkg/executor/merge_join.go index 7d6c1e5bd5ee0..ef38c2486ee2e 100644 --- a/executor/merge_join.go +++ b/pkg/executor/merge_join.go @@ -18,14 +18,14 @@ import ( "context" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/memory" ) // MergeJoinExec implements the merge join algorithm. diff --git a/executor/merge_join_test.go b/pkg/executor/merge_join_test.go similarity index 98% rename from executor/merge_join_test.go rename to pkg/executor/merge_join_test.go index c41e2bf3d798b..02b7725715ead 100644 --- a/executor/merge_join_test.go +++ b/pkg/executor/merge_join_test.go @@ -23,10 +23,10 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) @@ -240,9 +240,9 @@ func checkPlanAndRun(tk *testkit.TestKit, t *testing.T, plan string, sql string) } func TestShuffleMergeJoinInDisk(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testMergeJoinRowContainerSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testMergeJoinRowContainerSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testMergeJoinRowContainerSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testMergeJoinRowContainerSpill")) }() store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) @@ -286,9 +286,9 @@ func TestMergeJoinInDisk(t *testing.T) { conf.TempStoragePath = t.TempDir() }) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testMergeJoinRowContainerSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testMergeJoinRowContainerSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testMergeJoinRowContainerSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testMergeJoinRowContainerSpill")) }() store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) diff --git a/pkg/executor/metrics/BUILD.bazel b/pkg/executor/metrics/BUILD.bazel new file mode 100644 index 0000000000000..f321d8cc5de9c --- /dev/null +++ b/pkg/executor/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/executor/metrics/metrics.go b/pkg/executor/metrics/metrics.go new file mode 100644 index 0000000000000..efce08ca3d2ea --- /dev/null +++ b/pkg/executor/metrics/metrics.go @@ -0,0 +1,257 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// phases +const ( + PhaseBuildLocking = "build:locking" + PhaseOpenLocking = "open:locking" + PhaseNextLocking = "next:locking" + PhaseLockLocking = "lock:locking" + PhaseBuildFinal = "build:final" + PhaseOpenFinal = "open:final" + PhaseNextFinal = "next:final" + PhaseLockFinal = "lock:final" + PhaseCommitPrewrite = "commit:prewrite" + PhaseCommitCommit = "commit:commit" + PhaseCommitWaitCommitTS = "commit:wait:commit-ts" + PhaseCommitWaitLatestTS = "commit:wait:latest-ts" + PhaseCommitWaitLatch = "commit:wait:local-latch" + PhaseCommitWaitBinlog = "commit:wait:prewrite-binlog" + PhaseWriteResponse = "write-response" +) + +// executor metrics vars +var ( + TotalQueryProcHistogramGeneral prometheus.Observer + TotalCopProcHistogramGeneral prometheus.Observer + TotalCopWaitHistogramGeneral prometheus.Observer + CopMVCCRatioHistogramGeneral prometheus.Observer + TotalQueryProcHistogramInternal prometheus.Observer + TotalCopProcHistogramInternal prometheus.Observer + TotalCopWaitHistogramInternal prometheus.Observer + + SelectForUpdateFirstAttemptDuration prometheus.Observer + SelectForUpdateRetryDuration prometheus.Observer + DmlFirstAttemptDuration prometheus.Observer + DmlRetryDuration prometheus.Observer + + // FairLockingTxnUsedCount counts transactions where at least one statement has fair locking enabled. + FairLockingTxnUsedCount prometheus.Counter + // FairLockingStmtUsedCount counts statements that have fair locking enabled. + FairLockingStmtUsedCount prometheus.Counter + // FairLockingTxnEffectiveCount counts transactions where at least one statement has fair locking enabled, + // and it takes effect (which is determined according to whether lock-with-conflict has occurred during execution). + FairLockingTxnEffectiveCount prometheus.Counter + // FairLockingStmtEffectiveCount counts statements where at least one statement has fair locking enabled, + // and it takes effect (which is determined according to whether lock-with-conflict has occurred during execution). + FairLockingStmtEffectiveCount prometheus.Counter + + ExecutorCounterMergeJoinExec prometheus.Counter + ExecutorCountHashJoinExec prometheus.Counter + ExecutorCounterHashAggExec prometheus.Counter + ExecutorStreamAggExec prometheus.Counter + ExecutorCounterSortExec prometheus.Counter + ExecutorCounterTopNExec prometheus.Counter + ExecutorCounterNestedLoopApplyExec prometheus.Counter + ExecutorCounterIndexLookUpJoin prometheus.Counter + ExecutorCounterIndexLookUpExecutor prometheus.Counter + ExecutorCounterIndexMergeReaderExecutor prometheus.Counter + + SessionExecuteRunDurationInternal prometheus.Observer + SessionExecuteRunDurationGeneral prometheus.Observer + TotalTiFlashQuerySuccCounter prometheus.Counter + + // pre-define observers for non-internal queries + ExecBuildLocking prometheus.Observer + ExecOpenLocking prometheus.Observer + ExecNextLocking prometheus.Observer + ExecLockLocking prometheus.Observer + ExecBuildFinal prometheus.Observer + ExecOpenFinal prometheus.Observer + ExecNextFinal prometheus.Observer + ExecLockFinal prometheus.Observer + ExecCommitPrewrite prometheus.Observer + ExecCommitCommit prometheus.Observer + ExecCommitWaitCommitTS prometheus.Observer + ExecCommitWaitLatestTS prometheus.Observer + ExecCommitWaitLatch prometheus.Observer + ExecCommitWaitBinlog prometheus.Observer + ExecWriteResponse prometheus.Observer + ExecUnknown prometheus.Observer + + // pre-define observers for internal queries + ExecBuildLockingInternal prometheus.Observer + ExecOpenLockingInternal prometheus.Observer + ExecNextLockingInternal prometheus.Observer + ExecLockLockingInternal prometheus.Observer + ExecBuildFinalInternal prometheus.Observer + ExecOpenFinalInternal prometheus.Observer + ExecNextFinalInternal prometheus.Observer + ExecLockFinalInternal prometheus.Observer + ExecCommitPrewriteInternal prometheus.Observer + ExecCommitCommitInternal prometheus.Observer + ExecCommitWaitCommitTSInternal prometheus.Observer + ExecCommitWaitLatestTSInternal prometheus.Observer + ExecCommitWaitLatchInternal prometheus.Observer + ExecCommitWaitBinlogInternal prometheus.Observer + ExecWriteResponseInternal prometheus.Observer + ExecUnknownInternal prometheus.Observer + + TransactionDurationPessimisticRollbackInternal prometheus.Observer + TransactionDurationPessimisticRollbackGeneral prometheus.Observer + TransactionDurationOptimisticRollbackInternal prometheus.Observer + TransactionDurationOptimisticRollbackGeneral prometheus.Observer + + PhaseDurationObserverMap map[string]prometheus.Observer + PhaseDurationObserverMapInternal map[string]prometheus.Observer + + MppCoordinatorStatsTotalRegisteredNumber prometheus.Gauge + MppCoordinatorStatsActiveNumber prometheus.Gauge + MppCoordinatorStatsOverTimeNumber prometheus.Gauge + MppCoordinatorStatsReportNotReceived prometheus.Gauge + + MppCoordinatorLatencyRcvReport prometheus.Observer +) + +func init() { + InitMetricsVars() + InitPhaseDurationObserverMap() +} + +// InitMetricsVars init executor metrics vars. +func InitMetricsVars() { + TotalQueryProcHistogramGeneral = metrics.TotalQueryProcHistogram.WithLabelValues(metrics.LblGeneral) + TotalCopProcHistogramGeneral = metrics.TotalCopProcHistogram.WithLabelValues(metrics.LblGeneral) + TotalCopWaitHistogramGeneral = metrics.TotalCopWaitHistogram.WithLabelValues(metrics.LblGeneral) + CopMVCCRatioHistogramGeneral = metrics.CopMVCCRatioHistogram.WithLabelValues(metrics.LblGeneral) + TotalQueryProcHistogramInternal = metrics.TotalQueryProcHistogram.WithLabelValues(metrics.LblInternal) + TotalCopProcHistogramInternal = metrics.TotalCopProcHistogram.WithLabelValues(metrics.LblInternal) + TotalCopWaitHistogramInternal = metrics.TotalCopWaitHistogram.WithLabelValues(metrics.LblInternal) + + SelectForUpdateFirstAttemptDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("select-for-update", "first-attempt") + SelectForUpdateRetryDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("select-for-update", "retry") + DmlFirstAttemptDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("dml", "first-attempt") + DmlRetryDuration = metrics.PessimisticDMLDurationByAttempt.WithLabelValues("dml", "retry") + + FairLockingTxnUsedCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingTxnUsed) + FairLockingStmtUsedCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingStmtUsed) + FairLockingTxnEffectiveCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingTxnEffective) + FairLockingStmtEffectiveCount = metrics.FairLockingUsageCount.WithLabelValues(metrics.LblFairLockingStmtEffective) + + ExecutorCounterMergeJoinExec = metrics.ExecutorCounter.WithLabelValues("MergeJoinExec") + ExecutorCountHashJoinExec = metrics.ExecutorCounter.WithLabelValues("HashJoinExec") + ExecutorCounterHashAggExec = metrics.ExecutorCounter.WithLabelValues("HashAggExec") + ExecutorStreamAggExec = metrics.ExecutorCounter.WithLabelValues("StreamAggExec") + ExecutorCounterSortExec = metrics.ExecutorCounter.WithLabelValues("SortExec") + ExecutorCounterTopNExec = metrics.ExecutorCounter.WithLabelValues("TopNExec") + ExecutorCounterNestedLoopApplyExec = metrics.ExecutorCounter.WithLabelValues("NestedLoopApplyExec") + ExecutorCounterIndexLookUpJoin = metrics.ExecutorCounter.WithLabelValues("IndexLookUpJoin") + ExecutorCounterIndexLookUpExecutor = metrics.ExecutorCounter.WithLabelValues("IndexLookUpExecutor") + ExecutorCounterIndexMergeReaderExecutor = metrics.ExecutorCounter.WithLabelValues("IndexMergeReaderExecutor") + + SessionExecuteRunDurationInternal = metrics.SessionExecuteRunDuration.WithLabelValues(metrics.LblInternal) + SessionExecuteRunDurationGeneral = metrics.SessionExecuteRunDuration.WithLabelValues(metrics.LblGeneral) + TotalTiFlashQuerySuccCounter = metrics.TiFlashQueryTotalCounter.WithLabelValues("", metrics.LblOK) + + ExecBuildLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildLocking, "0") + ExecOpenLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenLocking, "0") + ExecNextLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextLocking, "0") + ExecLockLocking = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockLocking, "0") + ExecBuildFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildFinal, "0") + ExecOpenFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenFinal, "0") + ExecNextFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextFinal, "0") + ExecLockFinal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockFinal, "0") + ExecCommitPrewrite = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitPrewrite, "0") + ExecCommitCommit = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitCommit, "0") + ExecCommitWaitCommitTS = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitCommitTS, "0") + ExecCommitWaitLatestTS = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatestTS, "0") + ExecCommitWaitLatch = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatch, "0") + ExecCommitWaitBinlog = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitBinlog, "0") + ExecWriteResponse = metrics.ExecPhaseDuration.WithLabelValues(PhaseWriteResponse, "0") + ExecUnknown = metrics.ExecPhaseDuration.WithLabelValues("unknown", "0") + + ExecBuildLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildLocking, "1") + ExecOpenLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenLocking, "1") + ExecNextLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextLocking, "1") + ExecLockLockingInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockLocking, "1") + ExecBuildFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseBuildFinal, "1") + ExecOpenFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseOpenFinal, "1") + ExecNextFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseNextFinal, "1") + ExecLockFinalInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseLockFinal, "1") + ExecCommitPrewriteInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitPrewrite, "1") + ExecCommitCommitInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitCommit, "1") + ExecCommitWaitCommitTSInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitCommitTS, "1") + ExecCommitWaitLatestTSInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatestTS, "1") + ExecCommitWaitLatchInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitLatch, "1") + ExecCommitWaitBinlogInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseCommitWaitBinlog, "1") + ExecWriteResponseInternal = metrics.ExecPhaseDuration.WithLabelValues(PhaseWriteResponse, "1") + ExecUnknownInternal = metrics.ExecPhaseDuration.WithLabelValues("unknown", "1") + + TransactionDurationPessimisticRollbackInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblRollback, metrics.LblInternal) + TransactionDurationPessimisticRollbackGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblRollback, metrics.LblGeneral) + TransactionDurationOptimisticRollbackInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblRollback, metrics.LblInternal) + TransactionDurationOptimisticRollbackGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblRollback, metrics.LblGeneral) + + MppCoordinatorStatsTotalRegisteredNumber = metrics.MppCoordinatorStats.WithLabelValues("total") + MppCoordinatorStatsActiveNumber = metrics.MppCoordinatorStats.WithLabelValues("active") + MppCoordinatorStatsOverTimeNumber = metrics.MppCoordinatorStats.WithLabelValues("overTime") + MppCoordinatorStatsReportNotReceived = metrics.MppCoordinatorStats.WithLabelValues("reportNotRcv") + + MppCoordinatorLatencyRcvReport = metrics.MppCoordinatorLatency.WithLabelValues("rcvReports") +} + +// InitPhaseDurationObserverMap init observer map +func InitPhaseDurationObserverMap() { + PhaseDurationObserverMap = map[string]prometheus.Observer{ + PhaseBuildLocking: ExecBuildLocking, + PhaseOpenLocking: ExecOpenLocking, + PhaseNextLocking: ExecNextLocking, + PhaseLockLocking: ExecLockLocking, + PhaseBuildFinal: ExecBuildFinal, + PhaseOpenFinal: ExecOpenFinal, + PhaseNextFinal: ExecNextFinal, + PhaseLockFinal: ExecLockFinal, + PhaseCommitPrewrite: ExecCommitPrewrite, + PhaseCommitCommit: ExecCommitCommit, + PhaseCommitWaitCommitTS: ExecCommitWaitCommitTS, + PhaseCommitWaitLatestTS: ExecCommitWaitLatestTS, + PhaseCommitWaitLatch: ExecCommitWaitLatch, + PhaseCommitWaitBinlog: ExecCommitWaitBinlog, + PhaseWriteResponse: ExecWriteResponse, + } + PhaseDurationObserverMapInternal = map[string]prometheus.Observer{ + PhaseBuildLocking: ExecBuildLockingInternal, + PhaseOpenLocking: ExecOpenLockingInternal, + PhaseNextLocking: ExecNextLockingInternal, + PhaseLockLocking: ExecLockLockingInternal, + PhaseBuildFinal: ExecBuildFinalInternal, + PhaseOpenFinal: ExecOpenFinalInternal, + PhaseNextFinal: ExecNextFinalInternal, + PhaseLockFinal: ExecLockFinalInternal, + PhaseCommitPrewrite: ExecCommitPrewriteInternal, + PhaseCommitCommit: ExecCommitCommitInternal, + PhaseCommitWaitCommitTS: ExecCommitWaitCommitTSInternal, + PhaseCommitWaitLatestTS: ExecCommitWaitLatestTSInternal, + PhaseCommitWaitLatch: ExecCommitWaitLatchInternal, + PhaseCommitWaitBinlog: ExecCommitWaitBinlogInternal, + PhaseWriteResponse: ExecWriteResponseInternal, + } +} diff --git a/executor/metrics_reader.go b/pkg/executor/metrics_reader.go similarity index 96% rename from executor/metrics_reader.go rename to pkg/executor/metrics_reader.go index 1d1d6f4a2931d..f611ca62c90b9 100644 --- a/executor/metrics_reader.go +++ b/pkg/executor/metrics_reader.go @@ -24,16 +24,16 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/prometheus/client_golang/api" promv1 "github.com/prometheus/client_golang/api/prometheus/v1" pmodel "github.com/prometheus/common/model" diff --git a/executor/metrics_reader_test.go b/pkg/executor/metrics_reader_test.go similarity index 92% rename from executor/metrics_reader_test.go rename to pkg/executor/metrics_reader_test.go index 9205957f833c8..07b5bfd5d855f 100644 --- a/executor/metrics_reader_test.go +++ b/pkg/executor/metrics_reader_test.go @@ -19,11 +19,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/executor/mpp_gather.go b/pkg/executor/mpp_gather.go similarity index 89% rename from executor/mpp_gather.go rename to pkg/executor/mpp_gather.go index 096d15a65051e..a53c093611762 100644 --- a/executor/mpp_gather.go +++ b/pkg/executor/mpp_gather.go @@ -19,19 +19,19 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/mpp" - "github.com/pingcap/tidb/executor/mppcoordmanager" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/mpp" + "github.com/pingcap/tidb/pkg/executor/mppcoordmanager" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/memory" ) func useMPPExecution(ctx sessionctx.Context, tr *plannercore.PhysicalTableReader) bool { diff --git a/pkg/executor/mppcoordmanager/BUILD.bazel b/pkg/executor/mppcoordmanager/BUILD.bazel new file mode 100644 index 0000000000000..0f48b512489a8 --- /dev/null +++ b/pkg/executor/mppcoordmanager/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mppcoordmanager", + srcs = ["mpp_coordinator_manager.go"], + importpath = "github.com/pingcap/tidb/pkg/executor/mppcoordmanager", + visibility = ["//visibility:public"], + deps = [ + "//pkg/executor/metrics", + "//pkg/kv", + "//pkg/store/copr", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/mpp", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "mppcoordmanager_test", + timeout = "short", + srcs = ["mpp_coordinator_manager_test.go"], + embed = [":mppcoordmanager"], + flaky = True, + race = "on", + deps = [ + "//pkg/kv", + "//pkg/store/copr", + "@com_github_stretchr_testify//require", + ], +) diff --git a/executor/mppcoordmanager/mpp_coordinator_manager.go b/pkg/executor/mppcoordmanager/mpp_coordinator_manager.go similarity index 97% rename from executor/mppcoordmanager/mpp_coordinator_manager.go rename to pkg/executor/mppcoordmanager/mpp_coordinator_manager.go index 96208c78b4937..c37514e23af1f 100644 --- a/executor/mppcoordmanager/mpp_coordinator_manager.go +++ b/pkg/executor/mppcoordmanager/mpp_coordinator_manager.go @@ -22,10 +22,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/executor/metrics" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/executor/metrics" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/executor/mppcoordmanager/mpp_coordinator_manager_test.go b/pkg/executor/mppcoordmanager/mpp_coordinator_manager_test.go similarity index 97% rename from executor/mppcoordmanager/mpp_coordinator_manager_test.go rename to pkg/executor/mppcoordmanager/mpp_coordinator_manager_test.go index 0090a47ae96b9..aa62196b633f3 100644 --- a/executor/mppcoordmanager/mpp_coordinator_manager_test.go +++ b/pkg/executor/mppcoordmanager/mpp_coordinator_manager_test.go @@ -19,8 +19,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/copr" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/copr" "github.com/stretchr/testify/require" ) diff --git a/executor/opt_rule_blacklist.go b/pkg/executor/opt_rule_blacklist.go similarity index 83% rename from executor/opt_rule_blacklist.go rename to pkg/executor/opt_rule_blacklist.go index b06c87cf616ee..2c36cac07f6ea 100644 --- a/executor/opt_rule_blacklist.go +++ b/pkg/executor/opt_rule_blacklist.go @@ -17,13 +17,13 @@ package executor import ( "context" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) // ReloadOptRuleBlacklistExec indicates ReloadOptRuleBlacklist executor. diff --git a/executor/parallel_apply.go b/pkg/executor/parallel_apply.go similarity index 96% rename from executor/parallel_apply.go rename to pkg/executor/parallel_apply.go index f1c06304a5134..cd9b1b28321ae 100644 --- a/executor/parallel_apply.go +++ b/pkg/executor/parallel_apply.go @@ -22,15 +22,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/applycache" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/internal/applycache" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" ) diff --git a/executor/parallel_apply_test.go b/pkg/executor/parallel_apply_test.go similarity index 98% rename from executor/parallel_apply_test.go rename to pkg/executor/parallel_apply_test.go index 27c6b356f89d2..b05fe3c7bcdcf 100644 --- a/executor/parallel_apply_test.go +++ b/pkg/executor/parallel_apply_test.go @@ -20,9 +20,9 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) @@ -610,13 +610,13 @@ func TestApplyGoroutinePanic(t *testing.T) { tk.MustQuery(sql).Sort().Check(testkit.Rows("4", "4", "4", "4", "4", "4", "6", "6", "6", "6")) // panic in a inner worker - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/parallelApplyInnerWorkerPanic", "panic")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/parallelApplyInnerWorkerPanic", "panic")) err := tk.QueryToErr(sql) require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/parallelApplyInnerWorkerPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/parallelApplyInnerWorkerPanic")) for _, panicName := range []string{"parallelApplyInnerWorkerPanic", "parallelApplyOuterWorkerPanic", "parallelApplyGetCachePanic", "parallelApplySetCachePanic"} { - panicPath := fmt.Sprintf("github.com/pingcap/tidb/executor/%v", panicName) + panicPath := fmt.Sprintf("github.com/pingcap/tidb/pkg/executor/%v", panicName) require.NoError(t, failpoint.Enable(panicPath, "panic")) require.Error(t, tk.QueryToErr(sql)) require.NoError(t, failpoint.Disable(panicPath)) diff --git a/pkg/executor/partition_table_test.go b/pkg/executor/partition_table_test.go new file mode 100644 index 0000000000000..3c9dda3fe799c --- /dev/null +++ b/pkg/executor/partition_table_test.go @@ -0,0 +1,4296 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/require" +) + +func TestSetPartitionPruneMode(t *testing.T) { + store := testkit.CreateMockStore(t) + + tkInit := testkit.NewTestKit(t, store) + tkInit.MustExec(`set @@session.tidb_partition_prune_mode = DEFAULT`) + tkInit.MustQuery("show warnings").Check(testkit.Rows()) + tkInit.MustExec(`set @@global.tidb_partition_prune_mode = DEFAULT`) + tkInit.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats")) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) + tk.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) + tk.MustExec(`set @@session.tidb_partition_prune_mode = "static"`) + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec(`set @@global.tidb_partition_prune_mode = "static"`) + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk2 := testkit.NewTestKit(t, store) + tk2.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("static")) + tk2.MustQuery("show warnings").Check(testkit.Rows()) + tk2.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("static")) + tk2.MustExec(`set @@session.tidb_partition_prune_mode = "dynamic"`) + tk2.MustQuery("show warnings").Sort().Check(testkit.Rows( + `Warning 1105 Please analyze all partition tables again for consistency between partition and global stats`, + `Warning 1105 Please avoid setting partition prune mode to dynamic at session level and set partition prune mode to dynamic at global level`)) + tk2.MustExec(`set @@global.tidb_partition_prune_mode = "dynamic"`) + tk2.MustQuery("show warnings").Check(testkit.Rows(`Warning 1105 Please analyze all partition tables again for consistency between partition and global stats`)) + tk3 := testkit.NewTestKit(t, store) + tk3.MustQuery("select @@global.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) + tk3.MustQuery("select @@session.tidb_partition_prune_mode").Check(testkit.Rows("dynamic")) +} + +func TestFourReader(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists pt") + tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("insert into pt values (0, 0), (2, 2), (4, 4), (6, 6), (7, 7), (9, 9), (null, null)") + + // Table reader + tk.MustQuery("select * from pt").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6", "7 7", "9 9", " ")) + // Table reader: table dual + tk.MustQuery("select * from pt where c > 10").Check(testkit.Rows()) + // Table reader: one partition + tk.MustQuery("select * from pt where c > 8").Check(testkit.Rows("9 9")) + // Table reader: more than one partition + tk.MustQuery("select * from pt where c < 2 or c >= 9").Sort().Check(testkit.Rows("0 0", "9 9")) + + // Index reader + tk.MustQuery("select c from pt").Sort().Check(testkit.Rows("0", "2", "4", "6", "7", "9", "")) + tk.MustQuery("select c from pt where c > 10").Check(testkit.Rows()) + tk.MustQuery("select c from pt where c > 8").Check(testkit.Rows("9")) + tk.MustQuery("select c from pt where c < 2 or c >= 9").Sort().Check(testkit.Rows("0", "9")) + + // Index lookup + tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6", "7 7", "9 9", " ")) + tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 4 and c > 10").Check(testkit.Rows()) + tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 10 and c > 8").Check(testkit.Rows("9 9")) + tk.MustQuery("select /*+ use_index(pt, i_id) */ * from pt where id < 10 and c < 2 or c >= 9").Sort().Check(testkit.Rows("0 0", "9 9")) + + // Index Merge + tk.MustExec("set @@tidb_enable_index_merge = 1") + tk.MustQuery("select /*+ use_index(i_c, i_id) */ * from pt where id = 4 or c < 7").Sort().Check(testkit.Rows("0 0", "2 2", "4 4", "6 6")) +} + +func TestPartitionIndexJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_enable_table_partition = 1") + tk.MustExec("set @@session.tidb_enable_list_partition = 1") + for i := 0; i < 3; i++ { + tk.MustExec("drop table if exists p, t") + if i == 0 { + // Test for range partition + tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( + partition p0 values less than (4), + partition p1 values less than (7), + partition p2 values less than (10))`) + } else if i == 1 { + // Test for list partition + tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by list (c) ( + partition p0 values in (1,2,3,4), + partition p1 values in (5,6,7), + partition p2 values in (8, 9,10))`) + } else { + // Test for hash partition + tk.MustExec(`create table p (id int, c int, key i_id(id), key i_c(c)) partition by hash(c) partitions 5;`) + } + + tk.MustExec("create table t (id int)") + tk.MustExec("insert into p values (3,3), (4,4), (6,6), (9,9)") + tk.MustExec("insert into t values (4), (9)") + + // Build indexLookUp in index join + tk.MustQuery("select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id").Sort().Check(testkit.Rows("4 4 4", "9 9 9")) + // Build index reader in index join + tk.MustQuery("select /*+ INL_JOIN(p) */ p.id from p, t where p.id = t.id").Sort().Check(testkit.Rows("4", "9")) + } +} + +func TestPartitionUnionScanIndexJoin(t *testing.T) { + // For issue https://github.com/pingcap/tidb/issues/19152 + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (c_int int, c_str varchar(40), primary key (c_int)) partition by range (c_int) ( partition p0 values less than (10), partition p1 values less than maxvalue)") + tk.MustExec("create table t2 (c_int int, c_str varchar(40), primary key (c_int, c_str)) partition by hash (c_int) partitions 4") + tk.MustExec("insert into t1 values (10, 'interesting neumann')") + tk.MustExec("insert into t2 select * from t1") + tk.MustExec("begin") + tk.MustExec("insert into t2 values (11, 'hopeful hoover');") + tk.MustQuery("select /*+ INL_JOIN(t1,t2) */ * from t1 join t2 on t1.c_int = t2.c_int and t1.c_str = t2.c_str where t1.c_int in (10, 11)").Check(testkit.Rows("10 interesting neumann 10 interesting neumann")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t1,t2) */ * from t1 join t2 on t1.c_int = t2.c_int and t1.c_str = t2.c_str where t1.c_int in (10, 11)").Check(testkit.Rows("10 interesting neumann 10 interesting neumann")) + tk.MustExec("commit") +} + +func TestPointGetwithRangeAndListPartitionTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set @@session.tidb_enable_list_partition = ON") + + // list partition table + tk.MustExec(`create table tlist(a int, b int, unique index idx_a(a), index idx_b(b)) partition by list(a)( + partition p0 values in (NULL, 1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8), + partition p2 values in (9, 10, 11, 12));`) + + // range partition table + tk.MustExec(`create table trange1(a int, unique key(a)) partition by range(a) ( + partition p0 values less than (30), + partition p1 values less than (60), + partition p2 values less than (90), + partition p3 values less than (120));`) + + // range partition table + unsigned int + tk.MustExec(`create table trange2(a int unsigned, unique key(a)) partition by range(a) ( + partition p0 values less than (30), + partition p1 values less than (60), + partition p2 values less than (90), + partition p3 values less than (120));`) + + // insert data into list partition table + tk.MustExec("insert into tlist values(1,1), (2,2), (3, 3), (4, 4), (5,5), (6, 6), (7,7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12), (NULL, NULL);") + + vals := make([]string, 0, 100) + // insert data into range partition table and hash partition table + for i := 0; i < 100; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i+1)) + } + tk.MustExec("insert into trange1 values " + strings.Join(vals, ",")) + tk.MustExec("insert into trange2 values " + strings.Join(vals, ",")) + + // test PointGet + for i := 0; i < 100; i++ { + // explain select a from t where a = {x}; // x >= 1 and x <= 100 Check if PointGet is used + // select a from t where a={x}; // the result is {x} + x := rand.Intn(100) + 1 + queryRange1 := fmt.Sprintf("select a from trange1 where a=%v", x) + tk.MustHavePlan(queryRange1, "Point_Get") // check if PointGet is used + tk.MustQuery(queryRange1).Check(testkit.Rows(fmt.Sprintf("%v", x))) + + queryRange2 := fmt.Sprintf("select a from trange1 where a=%v", x) + tk.MustHavePlan(queryRange2, "Point_Get") // check if PointGet is used + tk.MustQuery(queryRange2).Check(testkit.Rows(fmt.Sprintf("%v", x))) + + y := rand.Intn(12) + 1 + queryList := fmt.Sprintf("select a from tlist where a=%v", y) + tk.MustHavePlan(queryList, "Point_Get") // check if PointGet is used + tk.MustQuery(queryList).Check(testkit.Rows(fmt.Sprintf("%v", y))) + } + + // test table dual + queryRange1 := "select a from trange1 where a=200" + tk.MustHavePlan(queryRange1, "TableDual") // check if TableDual is used + tk.MustQuery(queryRange1).Check(testkit.Rows()) + + queryRange2 := "select a from trange2 where a=200" + tk.MustHavePlan(queryRange2, "TableDual") // check if TableDual is used + tk.MustQuery(queryRange2).Check(testkit.Rows()) + + queryList := "select a from tlist where a=200" + tk.MustHavePlan(queryList, "TableDual") // check if TableDual is used + tk.MustQuery(queryList).Check(testkit.Rows()) + + // test PointGet for one partition + queryOnePartition := "select a from t where a = -1" + tk.MustExec("create table t(a int primary key, b int) PARTITION BY RANGE (a) (partition p0 values less than(1))") + tk.MustExec("insert into t values (-1, 1), (-2, 1)") + tk.MustExec("analyze table t") + tk.MustHavePlan(queryOnePartition, "Point_Get") + tk.MustQuery(queryOnePartition).Check(testkit.Rows(fmt.Sprintf("%v", -1))) + + tk.MustExec("drop table t") + tk.MustExec("create table t(a int primary key, b int) PARTITION BY list (a) (partition p0 values in (-1, -2))") + tk.MustExec("insert into t values (-1, 1), (-2, 1)") + tk.MustExec("analyze table t") + tk.MustHavePlan(queryOnePartition, "Point_Get") + tk.MustQuery(queryOnePartition).Check(testkit.Rows(fmt.Sprintf("%v", -1))) +} + +func TestPartitionReaderUnderApply(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // For issue 19458. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(c_int int)") + tk.MustExec("insert into t values(1), (2), (3), (4), (5), (6), (7), (8), (9)") + tk.MustExec("DROP TABLE IF EXISTS `t1`") + tk.MustExec(`CREATE TABLE t1 ( + c_int int NOT NULL, + c_str varchar(40) NOT NULL, + c_datetime datetime NOT NULL, + c_timestamp timestamp NULL DEFAULT NULL, + c_double double DEFAULT NULL, + c_decimal decimal(12,6) DEFAULT NULL, + PRIMARY KEY (c_int,c_str,c_datetime) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci + PARTITION BY RANGE (c_int) + (PARTITION p0 VALUES LESS THAN (2) ENGINE = InnoDB, + PARTITION p1 VALUES LESS THAN (4) ENGINE = InnoDB, + PARTITION p2 VALUES LESS THAN (6) ENGINE = InnoDB, + PARTITION p3 VALUES LESS THAN (8) ENGINE = InnoDB, + PARTITION p4 VALUES LESS THAN (10) ENGINE = InnoDB, + PARTITION p5 VALUES LESS THAN (20) ENGINE = InnoDB, + PARTITION p6 VALUES LESS THAN (50) ENGINE = InnoDB, + PARTITION p7 VALUES LESS THAN (1000000000) ENGINE = InnoDB)`) + tk.MustExec("INSERT INTO `t1` VALUES (19,'nifty feistel','2020-02-28 04:01:28','2020-02-04 06:11:57',32.430079,1.284000),(20,'objective snyder','2020-04-15 17:55:04','2020-05-30 22:04:13',37.690874,9.372000)") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (22, 'wizardly saha', '2020-05-03 16:35:22', '2020-05-03 02:18:42', 96.534810, 0.088)") + tk.MustQuery("select c_int from t where (select min(t1.c_int) from t1 where t1.c_int > t.c_int) > (select count(*) from t1 where t1.c_int > t.c_int) order by c_int").Check(testkit.Rows( + "1", "2", "3", "4", "5", "6", "7", "8", "9")) + tk.MustExec("rollback") + + // For issue 19450. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (c_int int, c_str varchar(40), c_decimal decimal(12, 6), primary key (c_int))") + tk.MustExec("create table t2 (c_int int, c_str varchar(40), c_decimal decimal(12, 6), primary key (c_int)) partition by hash (c_int) partitions 4") + tk.MustExec("insert into t1 values (1, 'romantic robinson', 4.436), (2, 'stoic chaplygin', 9.826), (3, 'vibrant shamir', 6.300), (4, 'hungry wilson', 4.900), (5, 'naughty swartz', 9.524)") + tk.MustExec("insert into t2 select * from t1") + tk.MustQuery("select * from t1 where c_decimal in (select c_decimal from t2 where t1.c_int = t2.c_int or t1.c_int = t2.c_int and t1.c_str > t2.c_str)").Check(testkit.Rows( + "1 romantic robinson 4.436000", + "2 stoic chaplygin 9.826000", + "3 vibrant shamir 6.300000", + "4 hungry wilson 4.900000", + "5 naughty swartz 9.524000")) + + // For issue 19450 release-4.0 + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + tk.MustQuery("select * from t1 where c_decimal in (select c_decimal from t2 where t1.c_int = t2.c_int or t1.c_int = t2.c_int and t1.c_str > t2.c_str)").Check(testkit.Rows( + "1 romantic robinson 4.436000", + "2 stoic chaplygin 9.826000", + "3 vibrant shamir 6.300000", + "4 hungry wilson 4.900000", + "5 naughty swartz 9.524000")) +} + +func TestImproveCoverage(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table coverage_rr ( +pk1 varchar(35) NOT NULL, +pk2 int NOT NULL, +c int, +PRIMARY KEY (pk1,pk2)) partition by hash(pk2) partitions 4;`) + tk.MustExec("create table coverage_dt (pk1 varchar(35), pk2 int)") + tk.MustExec("insert into coverage_rr values ('ios', 3, 2),('android', 4, 7),('linux',5,1)") + tk.MustExec("insert into coverage_dt values ('apple',3),('ios',3),('linux',5)") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery("select /*+ INL_JOIN(dt, rr) */ * from coverage_dt dt join coverage_rr rr on (dt.pk1 = rr.pk1 and dt.pk2 = rr.pk2);").Sort().Check(testkit.Rows("ios 3 ios 3 2", "linux 5 linux 5 1")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(dt, rr) */ * from coverage_dt dt join coverage_rr rr on (dt.pk1 = rr.pk1 and dt.pk2 = rr.pk2);").Sort().Check(testkit.Rows("ios 3 ios 3 2", "linux 5 linux 5 1")) +} + +func TestPartitionInfoDisable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_info_null") + tk.MustExec(`CREATE TABLE t_info_null ( + id bigint(20) unsigned NOT NULL AUTO_INCREMENT, + date date NOT NULL, + media varchar(32) NOT NULL DEFAULT '0', + app varchar(32) NOT NULL DEFAULT '', + xxx bigint(20) NOT NULL DEFAULT '0', + PRIMARY KEY (id, date), + UNIQUE KEY idx_media_id (media, date, app) +) PARTITION BY RANGE COLUMNS(date) ( + PARTITION p201912 VALUES LESS THAN ("2020-01-01"), + PARTITION p202001 VALUES LESS THAN ("2020-02-01"), + PARTITION p202002 VALUES LESS THAN ("2020-03-01"), + PARTITION p202003 VALUES LESS THAN ("2020-04-01"), + PARTITION p202004 VALUES LESS THAN ("2020-05-01"), + PARTITION p202005 VALUES LESS THAN ("2020-06-01"), + PARTITION p202006 VALUES LESS THAN ("2020-07-01"), + PARTITION p202007 VALUES LESS THAN ("2020-08-01"), + PARTITION p202008 VALUES LESS THAN ("2020-09-01"), + PARTITION p202009 VALUES LESS THAN ("2020-10-01"), + PARTITION p202010 VALUES LESS THAN ("2020-11-01"), + PARTITION p202011 VALUES LESS THAN ("2020-12-01") +)`) + is := tk.Session().GetInfoSchema().(infoschema.InfoSchema) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t_info_null")) + require.NoError(t, err) + + tbInfo := tbl.Meta() + // Mock for a case that the tableInfo.Partition is not nil, but tableInfo.Partition.Enable is false. + // That may happen when upgrading from a old version TiDB. + tbInfo.Partition.Enable = false + tbInfo.Partition.Num = 0 + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustQuery("explain select * from t_info_null where (date = '2020-10-02' or date = '2020-10-06') and app = 'xxx' and media = '19003006'").Check(testkit.Rows("Batch_Point_Get_5 2.00 root table:t_info_null, index:idx_media_id(media, date, app) keep order:false, desc:false")) + tk.MustQuery("explain select * from t_info_null").Check(testkit.Rows("TableReader_5 10000.00 root data:TableFullScan_4", + "└─TableFullScan_4 10000.00 cop[tikv] table:t_info_null keep order:false, stats:pseudo")) + // No panic. + tk.MustQuery("select * from t_info_null where (date = '2020-10-02' or date = '2020-10-06') and app = 'xxx' and media = '19003006'").Check(testkit.Rows()) +} + +func TestOrderByAndLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_orderby_limit") + tk.MustExec("use test_orderby_limit") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // range partition table + tk.MustExec(`create table trange(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than (500), + partition p2 values less than(1100));`) + + // hash partition table + tk.MustExec("create table thash(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by hash(a) partitions 4;") + + // regular table + tk.MustExec("create table tregular(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b))") + + // range partition table with int pk + tk.MustExec(`create table trange_intpk(a int primary key, b int) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than (500), + partition p2 values less than(1100));`) + + // hash partition table with int pk + tk.MustExec("create table thash_intpk(a int primary key, b int) partition by hash(a) partitions 4;") + + // regular table with int pk + tk.MustExec("create table tregular_intpk(a int primary key, b int)") + + // range partition table with clustered index + tk.MustExec(`create table trange_clustered(a int, b int, primary key(a, b) clustered) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than (500), + partition p2 values less than(1100));`) + + // hash partition table with clustered index + tk.MustExec("create table thash_clustered(a int, b int, primary key(a, b) clustered) partition by hash(a) partitions 4;") + + // regular table with clustered index + tk.MustExec("create table tregular_clustered(a int, b int, primary key(a, b) clustered)") + + listVals := make([]int, 0, 1000) + + for i := 0; i < 1000; i++ { + listVals = append(listVals, i) + } + rand.Shuffle(len(listVals), func(i, j int) { + listVals[i], listVals[j] = listVals[j], listVals[i] + }) + + var listVals1, listVals2, listVals3 string + + for i := 0; i <= 300; i++ { + listVals1 += strconv.Itoa(listVals[i]) + if i != 300 { + listVals1 += "," + } + } + for i := 301; i <= 600; i++ { + listVals2 += strconv.Itoa(listVals[i]) + if i != 600 { + listVals2 += "," + } + } + for i := 601; i <= 999; i++ { + listVals3 += strconv.Itoa(listVals[i]) + if i != 999 { + listVals3 += "," + } + } + + tk.MustExec(fmt.Sprintf(`create table tlist_intpk(a int primary key, b int) partition by list(a)( + partition p1 values in (%s), + partition p2 values in (%s), + partition p3 values in (%s) + )`, listVals1, listVals2, listVals3)) + tk.MustExec(fmt.Sprintf(`create table tlist(a int, b int, index idx_a(a), index idx_b(b), index idx_ab(a, b)) partition by list(a)( + partition p1 values in (%s), + partition p2 values in (%s), + partition p3 values in (%s) + )`, listVals1, listVals2, listVals3)) + tk.MustExec(fmt.Sprintf(`create table tlist_clustered(a int, b int, primary key(a, b)) partition by list(a)( + partition p1 values in (%s), + partition p2 values in (%s), + partition p3 values in (%s) + )`, listVals1, listVals2, listVals3)) + + // generate some random data to be inserted + vals := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(550), rand.Intn(1000))) + } + + dedupValsA := make([]string, 0, 1000) + dedupMapA := make(map[int]struct{}, 1000) + for i := 0; i < 1000; i++ { + valA := rand.Intn(550) + if _, ok := dedupMapA[valA]; ok { + continue + } + dedupValsA = append(dedupValsA, fmt.Sprintf("(%v, %v)", valA, rand.Intn(1000))) + dedupMapA[valA] = struct{}{} + } + + dedupValsAB := make([]string, 0, 1000) + dedupMapAB := make(map[string]struct{}, 1000) + for i := 0; i < 1000; i++ { + val := fmt.Sprintf("(%v, %v)", rand.Intn(550), rand.Intn(1000)) + if _, ok := dedupMapAB[val]; ok { + continue + } + dedupValsAB = append(dedupValsAB, val) + dedupMapAB[val] = struct{}{} + } + + valInserted := strings.Join(vals, ",") + valDedupAInserted := strings.Join(dedupValsA, ",") + valDedupABInserted := strings.Join(dedupValsAB, ",") + + tk.MustExec("insert into trange values " + valInserted) + tk.MustExec("insert into thash values " + valInserted) + tk.MustExec("insert into tlist values" + valInserted) + tk.MustExec("insert into tregular values " + valInserted) + tk.MustExec("insert into trange_intpk values " + valDedupAInserted) + tk.MustExec("insert into thash_intpk values " + valDedupAInserted) + tk.MustExec("insert into tlist_intpk values " + valDedupAInserted) + tk.MustExec("insert into tregular_intpk values " + valDedupAInserted) + tk.MustExec("insert into trange_clustered values " + valDedupABInserted) + tk.MustExec("insert into thash_clustered values " + valDedupABInserted) + tk.MustExec("insert into tlist_clustered values " + valDedupABInserted) + tk.MustExec("insert into tregular_clustered values " + valDedupABInserted) + + tk.MustExec("analyze table trange") + tk.MustExec("analyze table trange_intpk") + tk.MustExec("analyze table trange_clustered") + tk.MustExec("analyze table thash") + tk.MustExec("analyze table thash_intpk") + tk.MustExec("analyze table thash_clustered") + tk.MustExec("analyze table tregular") + tk.MustExec("analyze table tregular_intpk") + tk.MustExec("analyze table tregular_clustered") + tk.MustExec("analyze table tlist") + tk.MustExec("analyze table tlist_intpk") + tk.MustExec("analyze table tlist_clustered") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test_orderby_limit")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if strings.HasPrefix(tblInfo.Name.L, "tr") || strings.HasPrefix(tblInfo.Name.L, "thash") || strings.HasPrefix(tblInfo.Name.L, "tlist") { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tikv\"") + + // test indexLookUp + for i := 0; i < 50; i++ { + // explain select * from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select * from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryPartition := fmt.Sprintf("select * from trange use index(idx_a) where a > %v order by a, b limit %v;", x, y) + queryRegular := fmt.Sprintf("select * from tregular use index(idx_a) where a > %v order by a, b limit %v;", x, y) + tk.MustHavePlan(queryPartition, "IndexLookUp") // check if IndexLookUp is used + tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) + } + + // test indexLookUp with order property pushed down. + for i := 0; i < 50; i++ { + if i%2 == 0 { + tk.MustExec("set tidb_partition_prune_mode = `static-only`") + } else { + tk.MustExec("set tidb_partition_prune_mode = `dynamic-only`") + } + // explain select * from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select * from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(1000) + 1 + // Since we only use order by a not order by a, b, the result is not stable when we read both a and b. + // We cut the max element so that the result can be stable. + maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(a), 1100) from (select * from tregular use index(idx_a) where a > %v order by a limit %v) t", x, y)).Rows()[0][0] + queryRangePartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) + queryHashPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) + queryListPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist use index(idx_a) where a > %v and a < %v order by a limit %v", x, maxEle, y) + queryRegular := fmt.Sprintf("select * from tregular use index(idx_a) where a > %v and a < %v order by a limit %v;", x, maxEle, y) + + regularResult := tk.MustQuery(queryRegular).Sort().Rows() + if len(regularResult) > 0 { + tk.MustHavePlan(queryRangePartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryRangePartitionWithLimitHint, "IndexLookUp") + tk.MustHavePlan(queryHashPartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryHashPartitionWithLimitHint, "IndexLookUp") + tk.MustHavePlan(queryListPartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryListPartitionWithLimitHint, "IndexLookUp") + } + if i%2 != 0 { + tk.MustNotHavePlan(queryRangePartitionWithLimitHint, "TopN") // fully pushed + tk.MustNotHavePlan(queryHashPartitionWithLimitHint, "TopN") + tk.MustNotHavePlan(queryListPartitionWithLimitHint, "TopN") + } + tk.MustQuery(queryRangePartitionWithLimitHint).Sort().Check(regularResult) + tk.MustQuery(queryHashPartitionWithLimitHint).Sort().Check(regularResult) + tk.MustQuery(queryListPartitionWithLimitHint).Sort().Check(regularResult) + } + + // test indexLookUp with order property pushed down. + for i := 0; i < 50; i++ { + if i%2 == 0 { + tk.MustExec("set tidb_partition_prune_mode = `static-only`") + } else { + tk.MustExec("set tidb_partition_prune_mode = `dynamic-only`") + } + // explain select * from t where b > {y} use index(idx_b) order by b limit {x}; // check if IndexLookUp is used + // select * from t where b > {y} use index(idx_b) order by b limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(b), 2000) from (select * from tregular use index(idx_b) where b > %v order by b limit %v) t", x, y)).Rows()[0][0] + queryRangePartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) + queryHashPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) + queryListPartitionWithLimitHint := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist use index(idx_b) where b > %v and b < %v order by b limit %v", x, maxEle, y) + queryRegular := fmt.Sprintf("select * from tregular use index(idx_b) where b > %v and b < %v order by b limit %v;", x, maxEle, y) + + regularResult := tk.MustQuery(queryRegular).Sort().Rows() + if len(regularResult) > 0 { + tk.MustHavePlan(queryRangePartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryRangePartitionWithLimitHint, "IndexLookUp") + tk.MustHavePlan(queryHashPartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryHashPartitionWithLimitHint, "IndexLookUp") + tk.MustHavePlan(queryListPartitionWithLimitHint, "Limit") + tk.MustHavePlan(queryListPartitionWithLimitHint, "IndexLookUp") + } + if i%2 != 0 { + tk.MustNotHavePlan(queryRangePartitionWithLimitHint, "TopN") // fully pushed + tk.MustNotHavePlan(queryHashPartitionWithLimitHint, "TopN") + tk.MustNotHavePlan(queryListPartitionWithLimitHint, "TopN") + } + tk.MustQuery(queryRangePartitionWithLimitHint).Sort().Check(regularResult) + tk.MustQuery(queryHashPartitionWithLimitHint).Sort().Check(regularResult) + tk.MustQuery(queryListPartitionWithLimitHint).Sort().Check(regularResult) + } + + tk.MustExec("set tidb_partition_prune_mode = default") + + // test tableReader + for i := 0; i < 50; i++ { + // explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryPartition := fmt.Sprintf("select * from trange ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) + queryRegular := fmt.Sprintf("select * from tregular ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) + tk.MustHavePlan(queryPartition, "TableReader") // check if tableReader is used + tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) + } + + // test tableReader with order property pushed down. + for i := 0; i < 50; i++ { + // explain select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select * from t where a > {y} ignore index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) + queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) + queryListPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist ignore index(idx_a, idx_ab) where a > %v order by a, b limit %v;", x, y) + queryRegular := fmt.Sprintf("select * from tregular ignore index(idx_a) where a > %v order by a, b limit %v;", x, y) + tk.MustHavePlan(queryRangePartition, "TableReader") // check if tableReader is used + tk.MustHavePlan(queryHashPartition, "TableReader") + tk.MustHavePlan(queryListPartition, "TableReader") + tk.MustNotHavePlan(queryRangePartition, "Limit") // check if order property is not pushed + tk.MustNotHavePlan(queryHashPartition, "Limit") + tk.MustNotHavePlan(queryListPartition, "Limit") + regularResult := tk.MustQuery(queryRegular).Rows() + tk.MustQuery(queryRangePartition).Check(regularResult) + tk.MustQuery(queryHashPartition).Check(regularResult) + tk.MustQuery(queryListPartition).Check(regularResult) + + // test int pk + // To be simplified, we only read column a. + queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange_intpk use index(primary) where a > %v order by a limit %v", x, y) + queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash_intpk use index(primary) where a > %v order by a limit %v", x, y) + queryListPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from tlist_intpk use index(primary) where a > %v order by a limit %v", x, y) + queryRegular = fmt.Sprintf("select a from tregular_intpk where a > %v order by a limit %v", x, y) + tk.MustHavePlan(queryRangePartition, "TableReader") + tk.MustHavePlan(queryHashPartition, "TableReader") + tk.MustHavePlan(queryListPartition, "TableReader") + tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed + tk.MustNotHavePlan(queryRangePartition, "TopN") // and is fully pushed + tk.MustHavePlan(queryHashPartition, "Limit") + tk.MustNotHavePlan(queryHashPartition, "TopN") + tk.MustHavePlan(queryListPartition, "Limit") + tk.MustNotHavePlan(queryListPartition, "TopN") + regularResult = tk.MustQuery(queryRegular).Rows() + tk.MustQuery(queryRangePartition).Check(regularResult) + tk.MustQuery(queryHashPartition).Check(regularResult) + tk.MustQuery(queryListPartition).Check(regularResult) + + // test clustered index + queryRangePartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from trange_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) + queryHashPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from thash_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) + queryListPartition = fmt.Sprintf("select /*+ LIMIT_TO_COP() */ * from tlist_clustered use index(primary) where a > %v order by a, b limit %v;", x, y) + queryRegular = fmt.Sprintf("select * from tregular_clustered where a > %v order by a, b limit %v;", x, y) + tk.MustHavePlan(queryRangePartition, "TableReader") // check if tableReader is used + tk.MustHavePlan(queryHashPartition, "TableReader") + tk.MustHavePlan(queryListPartition, "TableReader") + tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed + tk.MustHavePlan(queryHashPartition, "Limit") + tk.MustHavePlan(queryListPartition, "Limit") + tk.MustNotHavePlan(queryRangePartition, "TopN") // could fully pushed for TableScan executor + tk.MustNotHavePlan(queryHashPartition, "TopN") + tk.MustNotHavePlan(queryListPartition, "TopN") + regularResult = tk.MustQuery(queryRegular).Rows() + tk.MustQuery(queryRangePartition).Check(regularResult) + tk.MustQuery(queryHashPartition).Check(regularResult) + tk.MustQuery(queryListPartition).Check(regularResult) + + tk.MustExec(" set @@tidb_allow_mpp=1;") + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash,tikv\"") + queryPartitionWithTiFlash := fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_intpk]) */ * from trange_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_intpk]) */ /*+ LIMIT_TO_COP() */ * from trange_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_clustered]) */ * from trange_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[trange_clustered]) */ /*+ LIMIT_TO_COP() */ * from trange_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_intpk]) */ * from thash_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_intpk]) */ /*+ LIMIT_TO_COP() */ * from thash_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_clustered]) */ * from thash_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[thash_clustered]) */ /*+ LIMIT_TO_COP() */ * from thash_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_intpk]) */ * from tlist_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_intpk]) */ /*+ LIMIT_TO_COP() */ * from tlist_intpk where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_clustered]) */ * from tlist_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash), fmt.Sprintf("%v", tk.MustQuery("explain "+queryPartitionWithTiFlash).Rows())) + queryPartitionWithTiFlash = fmt.Sprintf("select /*+ read_from_storage(tiflash[tlist_clustered]) */ /*+ LIMIT_TO_COP() */ * from tlist_clustered where a > %v order by a limit %v", x, y) + // check if tiflash is used + require.True(t, tk.HasTiFlashPlan(queryPartitionWithTiFlash)) + // but order is not pushed + tk.MustNotHavePlan(queryPartitionWithTiFlash, "Limit") + tk.MustExec(" set @@tidb_allow_mpp=0;") + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tikv\"") + } + + // test indexReader + for i := 0; i < 50; i++ { + // explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select a from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryPartition := fmt.Sprintf("select a from trange use index(idx_a) where a > %v order by a limit %v;", x, y) + queryRegular := fmt.Sprintf("select a from tregular use index(idx_a) where a > %v order by a limit %v;", x, y) + tk.MustHavePlan(queryPartition, "IndexReader") // check if indexReader is used + tk.MustQuery(queryPartition).Check(tk.MustQuery(queryRegular).Rows()) + } + + // test indexReader with order property pushed down. + for i := 0; i < 50; i++ { + // explain select a from t where a > {y} use index(idx_a) order by a limit {x}; // check if IndexLookUp is used + // select a from t where a > {y} use index(idx_a) order by a limit {x}; // it can return the correct result + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > %v order by a limit %v;", x, y) + queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash use index(idx_a) where a > %v order by a limit %v;", x, y) + queryRegular := fmt.Sprintf("select a from tregular use index(idx_a) where a > %v order by a limit %v;", x, y) + tk.MustHavePlan(queryRangePartition, "IndexReader") // check if indexReader is used + tk.MustHavePlan(queryHashPartition, "IndexReader") + tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed + tk.MustHavePlan(queryHashPartition, "Limit") + tk.MustNotHavePlan(queryRangePartition, "TopN") // fully pushed limit + tk.MustNotHavePlan(queryHashPartition, "TopN") + regularResult := tk.MustQuery(queryRegular).Rows() + tk.MustQuery(queryRangePartition).Check(regularResult) + tk.MustQuery(queryHashPartition).Check(regularResult) + } + + // test indexReader use idx_ab(a, b) with a = {x} order by b limit {y} + for i := 0; i < 50; i++ { + x := rand.Intn(549) + y := rand.Intn(500) + 1 + queryRangePartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_ab) where a = %v order by b limit %v;", x, y) + queryHashPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from thash use index(idx_ab) where a = %v order by b limit %v;", x, y) + queryListPartition := fmt.Sprintf("select /*+ LIMIT_TO_COP() */ a from tlist use index(idx_ab) where a = %v order by b limit %v;", x, y) + queryRegular := fmt.Sprintf("select a from tregular use index(idx_ab) where a = %v order by b limit %v;", x, y) + tk.MustHavePlan(queryRangePartition, "IndexReader") // check if indexReader is used + tk.MustHavePlan(queryHashPartition, "IndexReader") + tk.MustHavePlan(queryListPartition, "IndexReader") + tk.MustHavePlan(queryRangePartition, "Limit") // check if order property is pushed + tk.MustHavePlan(queryHashPartition, "Limit") + tk.MustHavePlan(queryListPartition, "Limit") + tk.MustNotHavePlan(queryRangePartition, "TopN") // fully pushed limit + tk.MustNotHavePlan(queryHashPartition, "TopN") + tk.MustNotHavePlan(queryListPartition, "TopN") + regularResult := tk.MustQuery(queryRegular).Rows() + tk.MustQuery(queryRangePartition).Check(regularResult) + tk.MustQuery(queryHashPartition).Check(regularResult) + tk.MustQuery(queryListPartition).Check(regularResult) + } + + // test indexMerge + for i := 0; i < 50; i++ { + // explain select /*+ use_index_merge(t) */ * from t where a > 2 or b < 5 order by a, b limit {x}; // check if IndexMerge is used + // select /*+ use_index_merge(t) */ * from t where a > 2 or b < 5 order by a, b limit {x}; // can return the correct value + y := rand.Intn(500) + 1 + queryHashPartition := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > 2 or b < 5 order by a, b limit %v;", y) + queryRegular := fmt.Sprintf("select * from tregular where a > 2 or b < 5 order by a, b limit %v;", y) + tk.MustHavePlan(queryHashPartition, "IndexMerge") // check if indexMerge is used + tk.MustQuery(queryHashPartition).Check(tk.MustQuery(queryRegular).Rows()) + } + + // test sql killed when memory exceed `tidb_mem_quota_query` + originMemQuota := tk.MustQuery("show variables like 'tidb_mem_quota_query'").Rows()[0][1].(string) + originOOMAction := tk.MustQuery("show variables like 'tidb_mem_oom_action'").Rows()[0][1].(string) + tk.MustExec("set session tidb_mem_quota_query=128") + tk.MustExec("set global tidb_mem_oom_action=CANCEL") + err := tk.QueryToErr("select /*+ LIMIT_TO_COP() */ a from trange use index(idx_a) where a > 1 order by a limit 2000") + require.Error(t, err) + require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) + tk.MustExec(fmt.Sprintf("set session tidb_mem_quota_query=%s", originMemQuota)) + tk.MustExec(fmt.Sprintf("set global tidb_mem_oom_action=%s", originOOMAction)) +} + +func TestOrderByOnUnsignedPk(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table tunsigned_hash(a bigint unsigned primary key) partition by hash(a) partitions 6") + tk.MustExec("insert into tunsigned_hash values(25), (9279808998424041135)") + tk.MustQuery("select min(a) from tunsigned_hash").Check(testkit.Rows("25")) + tk.MustQuery("select max(a) from tunsigned_hash").Check(testkit.Rows("9279808998424041135")) +} + +func TestPartitionHandleWithKeepOrder(t *testing.T) { + // https://github.com/pingcap/tidb/issues/44312 + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (id int not null, store_id int not null )" + + "partition by range (store_id)" + + "(partition p0 values less than (6)," + + "partition p1 values less than (11)," + + "partition p2 values less than (16)," + + "partition p3 values less than (21))") + tk.MustExec("create table t1(id int not null, store_id int not null)") + tk.MustExec("insert into t values (1, 1)") + tk.MustExec("insert into t values (2, 17)") + tk.MustExec("insert into t1 values (0, 18)") + tk.MustExec("alter table t exchange partition p3 with table t1") + tk.MustExec("alter table t add index idx(id)") + tk.MustExec("analyze table t") + tk.MustQuery("select *,_tidb_rowid from t use index(idx) order by id limit 2").Check(testkit.Rows("0 18 1", "1 1 1")) + + tk.MustExec("drop table t, t1") + tk.MustExec("create table t (a int, b int, c int, key `idx_ac`(a, c), key `idx_bc`(b, c))" + + "partition by range (b)" + + "(partition p0 values less than (6)," + + "partition p1 values less than (11)," + + "partition p2 values less than (16)," + + "partition p3 values less than (21))") + tk.MustExec("create table t1 (a int, b int, c int, key `idx_ac`(a, c), key `idx_bc`(b, c))") + tk.MustExec("insert into t values (1,2,3), (2,3,4), (3,4,5)") + tk.MustExec("insert into t1 values (1,18,3)") + tk.MustExec("alter table t exchange partition p3 with table t1") + tk.MustExec("analyze table t") + tk.MustQuery("select * from t where a = 1 or b = 5 order by c limit 2").Sort().Check(testkit.Rows("1 18 3", "1 2 3")) +} + +func TestOrderByOnHandle(t *testing.T) { + // https://github.com/pingcap/tidb/issues/44266 + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for i := 0; i < 2; i++ { + // indexLookUp + _tidb_rowid + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t`(" + + "`a` int(11) NOT NULL," + + "`b` int(11) DEFAULT NULL," + + "`c` int(11) DEFAULT NULL," + + "KEY `idx_b` (`b`)) PARTITION BY HASH (`a`) PARTITIONS 2;") + tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") + if i == 1 { + tk.MustExec("analyze table t") + } + tk.MustQuery("select * from t use index(idx_b) order by b, _tidb_rowid limit 10;").Check(testkit.Rows("2 -1 3", "1 1 1", "3 2 2")) + + // indexLookUp + pkIsHandle + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t`(" + + "`a` int(11) NOT NULL," + + "`b` int(11) DEFAULT NULL," + + "`c` int(11) DEFAULT NULL," + + "primary key(`a`)," + + "KEY `idx_b` (`b`)) PARTITION BY HASH (`a`) PARTITIONS 2;") + tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") + if i == 1 { + tk.MustExec("analyze table t") + } + tk.MustQuery("select * from t use index(idx_b) order by b, a limit 10;").Check(testkit.Rows("2 -1 3", "1 1 1", "3 2 2")) + + // indexMerge + _tidb_rowid + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t`(" + + "`a` int(11) NOT NULL," + + "`b` int(11) DEFAULT NULL," + + "`c` int(11) DEFAULT NULL," + + "KEY `idx_b` (`b`)," + + "KEY `idx_c` (`c`)) PARTITION BY HASH (`a`) PARTITIONS 2;") + tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") + if i == 1 { + tk.MustExec("analyze table t") + } + tk.MustQuery("select * from t use index(idx_b, idx_c) where b = 1 or c = 2 order by _tidb_rowid limit 10;").Check(testkit.Rows("3 2 2", "1 1 1")) + + // indexMerge + pkIsHandle + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t`(" + + "`a` int(11) NOT NULL," + + "`b` int(11) DEFAULT NULL," + + "`c` int(11) DEFAULT NULL," + + "KEY `idx_b` (`b`)," + + "KEY `idx_c` (`c`)," + + "PRIMARY KEY (`a`)) PARTITION BY HASH (`a`) PARTITIONS 2;") + tk.MustExec("insert into t values (2,-1,3), (3,2,2), (1,1,1);") + if i == 1 { + tk.MustExec("analyze table t") + } + tk.MustQuery("select * from t use index(idx_b, idx_c) where b = 1 or c = 2 order by a limit 10;").Check(testkit.Rows("1 1 1", "3 2 2")) + } +} + +func TestBatchGetandPointGetwithHashPartition(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_batchget_pointget") + tk.MustExec("use test_batchget_pointget") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // hash partition table + tk.MustExec("create table thash(a int, unique key(a)) partition by hash(a) partitions 4;") + + // regular partition table + tk.MustExec("create table tregular(a int, unique key(a));") + + vals := make([]string, 0, 100) + // insert data into range partition table and hash partition table + for i := 0; i < 100; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i+1)) + } + tk.MustExec("insert into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular values " + strings.Join(vals, ",")) + + // test PointGet + for i := 0; i < 100; i++ { + // explain select a from t where a = {x}; // x >= 1 and x <= 100 Check if PointGet is used + // select a from t where a={x}; // the result is {x} + x := rand.Intn(100) + 1 + queryHash := fmt.Sprintf("select a from thash where a=%v", x) + queryRegular := fmt.Sprintf("select a from tregular where a=%v", x) + tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used + tk.MustQuery(queryHash).Check(tk.MustQuery(queryRegular).Rows()) + } + + // test empty PointGet + queryHash := "select a from thash where a=200" + tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used + tk.MustQuery(queryHash).Check(testkit.Rows()) + + // test BatchGet + for i := 0; i < 100; i++ { + // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used + // select a from t where where a in ({x1}, {x2}, ... {x10}); + points := make([]string, 0, 10) + for i := 0; i < 10; i++ { + x := rand.Intn(100) + 1 + points = append(points, fmt.Sprintf("%v", x)) + } + + queryHash := fmt.Sprintf("select a from thash where a in (%v)", strings.Join(points, ",")) + queryRegular := fmt.Sprintf("select a from tregular where a in (%v)", strings.Join(points, ",")) + tk.MustHavePlan(queryHash, "Point_Get") // check if PointGet is used + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + } +} + +func TestView(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_view") + tk.MustExec("use test_view") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a varchar(10), b varchar(10), key(a)) partition by range columns(a) ( + partition p0 values less than ('300'), + partition p1 values less than ('600'), + partition p2 values less than ('900'), + partition p3 values less than ('9999'))`) + tk.MustExec(`create table t1 (a int, b int, key(a))`) + tk.MustExec(`create table t2 (a varchar(10), b varchar(10), key(a))`) + + // insert the same data into thash and t1 + vals := make([]string, 0, 3000) + for i := 0; i < 3000; i++ { + vals = append(vals, fmt.Sprintf(`(%v, %v)`, rand.Intn(10000), rand.Intn(10000))) + } + tk.MustExec(fmt.Sprintf(`insert into thash values %v`, strings.Join(vals, ", "))) + tk.MustExec(fmt.Sprintf(`insert into t1 values %v`, strings.Join(vals, ", "))) + + // insert the same data into trange and t2 + vals = vals[:0] + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf(`("%v", "%v")`, rand.Intn(1000), rand.Intn(1000))) + } + tk.MustExec(fmt.Sprintf(`insert into trange values %v`, strings.Join(vals, ", "))) + tk.MustExec(fmt.Sprintf(`insert into t2 values %v`, strings.Join(vals, ", "))) + + // test views on a single table + tk.MustExec(`create definer='root'@'localhost' view vhash as select a*2 as a, a+b as b from thash`) + tk.MustExec(`create definer='root'@'localhost' view v1 as select a*2 as a, a+b as b from t1`) + tk.MustExec(`create definer='root'@'localhost' view vrange as select concat(a, b) as a, a+b as b from trange`) + tk.MustExec(`create definer='root'@'localhost' view v2 as select concat(a, b) as a, a+b as b from t2`) + for i := 0; i < 100; i++ { + xhash := rand.Intn(10000) + tk.MustQuery(fmt.Sprintf(`select * from vhash where a>=%v`, xhash)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v1 where a>=%v`, xhash)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vhash where b>=%v`, xhash)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v1 where b>=%v`, xhash)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vhash where a>=%v and b>=%v`, xhash, xhash)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v1 where a>=%v and b>=%v`, xhash, xhash)).Sort().Rows()) + + xrange := fmt.Sprintf(`"%v"`, rand.Intn(1000)) + tk.MustQuery(fmt.Sprintf(`select * from vrange where a>=%v`, xrange)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v2 where a>=%v`, xrange)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vrange where b>=%v`, xrange)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v2 where b>=%v`, xrange)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vrange where a>=%v and b<=%v`, xrange, xrange)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from v2 where a>=%v and b<=%v`, xrange, xrange)).Sort().Rows()) + } + + // test views on both tables + tk.MustExec(`create definer='root'@'localhost' view vboth as select thash.a+trange.a as a, thash.b+trange.b as b from thash, trange where thash.a=trange.a`) + tk.MustExec(`create definer='root'@'localhost' view vt as select t1.a+t2.a as a, t1.b+t2.b as b from t1, t2 where t1.a=t2.a`) + for i := 0; i < 100; i++ { + x := rand.Intn(10000) + tk.MustQuery(fmt.Sprintf(`select * from vboth where a>=%v`, x)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from vt where a>=%v`, x)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vboth where b>=%v`, x)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from vt where b>=%v`, x)).Sort().Rows()) + tk.MustQuery(fmt.Sprintf(`select * from vboth where a>=%v and b>=%v`, x, x)).Sort().Check( + tk.MustQuery(fmt.Sprintf(`select * from vt where a>=%v and b>=%v`, x, x)).Sort().Rows()) + } +} + +func TestDirectReadingwithIndexJoin(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_dr_join") + tk.MustExec("use test_dr_join") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // hash and range partition + tk.MustExec("create table thash (a int, b int, c int, primary key(a), index idx_b(b)) partition by hash(a) partitions 4;") + tk.MustExec(`create table trange (a int, b int, c int, primary key(a), index idx_b(b)) partition by range(a) ( + partition p0 values less than(1000), + partition p1 values less than(2000), + partition p2 values less than(3000), + partition p3 values less than(4000));`) + + // regualr table + tk.MustExec(`create table tnormal (a int, b int, c int, primary key(a), index idx_b(b));`) + tk.MustExec(`create table touter (a int, b int, c int);`) + + // generate some random data to be inserted + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v, %v)", rand.Intn(4000), rand.Intn(4000), rand.Intn(4000))) + } + tk.MustExec("insert ignore into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into tnormal values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into touter values " + strings.Join(vals, ",")) + + // test indexLookUp + hash + queryPartition := "select /*+ INL_JOIN(touter, thash) */ * from touter join thash use index(idx_b) on touter.b = thash.b" + queryRegular := "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal use index(idx_b) on touter.b = tnormal.b" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:test_dr_join.touter.b, inner key:test_dr_join.thash.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.thash.b)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12487.50 root partition:all ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test_dr_join.thash.b))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(test_dr_join.thash.b, test_dr_join.touter.b)], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:thash keep order:false, stats:pseudo")) // check if IndexLookUp is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // test tableReader + hash + queryPartition = "select /*+ INL_JOIN(touter, thash) */ * from touter join thash on touter.a = thash.a" + queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal on touter.a = tnormal.a" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:TableReader, outer key:test_dr_join.touter.a, inner key:test_dr_join.thash.a, equal cond:eq(test_dr_join.touter.a, test_dr_join.thash.a)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan", + " └─TableRangeScan 9990.00 cop[tikv] table:thash range: decided by [test_dr_join.touter.a], keep order:false, stats:pseudo")) // check if tableReader is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // test indexReader + hash + queryPartition = "select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b;" + queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test_dr_join.touter.b, inner key:test_dr_join.thash.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.thash.b)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─IndexReader(Probe) 12487.50 root partition:all index:Selection", + " └─Selection 12487.50 cop[tikv] not(isnull(test_dr_join.thash.b))", + " └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(test_dr_join.thash.b, test_dr_join.touter.b)], keep order:false, stats:pseudo")) // check if indexReader is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // test indexLookUp + range + // explain select /*+ INL_JOIN(touter, tinner) */ * from touter join tinner use index(a) on touter.a = tinner.a; + queryPartition = "select /*+ INL_JOIN(touter, trange) */ * from touter join trange use index(idx_b) on touter.b = trange.b;" + queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:test_dr_join.touter.b, inner key:test_dr_join.trange.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.trange.b)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─IndexLookUp(Probe) 12487.50 root partition:all ", + " ├─Selection(Build) 12487.50 cop[tikv] not(isnull(test_dr_join.trange.b))", + " │ └─IndexRangeScan 12500.00 cop[tikv] table:trange, index:idx_b(b) range: decided by [eq(test_dr_join.trange.b, test_dr_join.touter.b)], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:trange keep order:false, stats:pseudo")) // check if IndexLookUp is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // test tableReader + range + queryPartition = "select /*+ INL_JOIN(touter, trange) */ * from touter join trange on touter.a = trange.a;" + queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ * from touter join tnormal on touter.a = tnormal.a;" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:TableReader, outer key:test_dr_join.touter.a, inner key:test_dr_join.trange.a, equal cond:eq(test_dr_join.touter.a, test_dr_join.trange.a)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.a))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan", + " └─TableRangeScan 9990.00 cop[tikv] table:trange range: decided by [test_dr_join.touter.a], keep order:false, stats:pseudo")) // check if tableReader is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // test indexReader + range + // explain select /*+ INL_JOIN(touter, tinner) */ tinner.a from touter join tinner on touter.a = tinner.a; + queryPartition = "select /*+ INL_JOIN(touter, trange) */ trange.b from touter join trange use index(idx_b) on touter.b = trange.b;" + queryRegular = "select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b;" + tk.MustQuery("explain format = 'brief' " + queryPartition).Check(testkit.Rows( + "IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:test_dr_join.touter.b, inner key:test_dr_join.trange.b, equal cond:eq(test_dr_join.touter.b, test_dr_join.trange.b)", + "├─TableReader(Build) 9990.00 root data:Selection", + "│ └─Selection 9990.00 cop[tikv] not(isnull(test_dr_join.touter.b))", + "│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo", + "└─IndexReader(Probe) 12487.50 root partition:all index:Selection", + " └─Selection 12487.50 cop[tikv] not(isnull(test_dr_join.trange.b))", + " └─IndexRangeScan 12500.00 cop[tikv] table:trange, index:idx_b(b) range: decided by [eq(test_dr_join.trange.b, test_dr_join.touter.b)], keep order:false, stats:pseudo")) // check if indexReader is used + tk.MustQuery(queryPartition).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) +} + +func TestDynamicPruningUnderIndexJoin(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create database pruing_under_index_join") + tk.MustExec("use pruing_under_index_join") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table tnormal (a int, b int, c int, primary key(a), index idx_b(b))`) + tk.MustExec(`create table thash (a int, b int, c int, primary key(a), index idx_b(b)) partition by hash(a) partitions 4`) + tk.MustExec(`create table touter (a int, b int, c int)`) + + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v, %v)", i, rand.Intn(10000), rand.Intn(10000))) + } + tk.MustExec(`insert into tnormal values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into touter values ` + strings.Join(vals, ", ")) + + // case 1: IndexReader in the inner side + tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b`).Check(testkit.Rows( + `IndexJoin 12487.50 root inner join, inner:IndexReader, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.b, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.b)`, + `├─TableReader(Build) 9990.00 root data:Selection`, + `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, + `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + `└─IndexReader(Probe) 12487.50 root partition:all index:Selection`, + ` └─Selection 12487.50 cop[tikv] not(isnull(pruing_under_index_join.thash.b))`, + ` └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(pruing_under_index_join.thash.b, pruing_under_index_join.touter.b)], keep order:false, stats:pseudo`)) + tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.b from touter join thash use index(idx_b) on touter.b = thash.b`).Sort().Check( + tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.b from touter join tnormal use index(idx_b) on touter.b = tnormal.b`).Sort().Rows()) + + // case 2: TableReader in the inner side + tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(primary) on touter.b = thash.a`).Check(testkit.Rows( + `IndexJoin 12487.50 root inner join, inner:TableReader, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.a, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.a)`, + `├─TableReader(Build) 9990.00 root data:Selection`, + `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, + `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + `└─TableReader(Probe) 9990.00 root partition:all data:TableRangeScan`, + ` └─TableRangeScan 9990.00 cop[tikv] table:thash range: decided by [pruing_under_index_join.touter.b], keep order:false, stats:pseudo`)) + tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(primary) on touter.b = thash.a`).Sort().Check( + tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.* from touter join tnormal use index(primary) on touter.b = tnormal.a`).Sort().Rows()) + + // case 3: IndexLookUp in the inner side + read all inner columns + tk.MustQuery(`explain format='brief' select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(idx_b) on touter.b = thash.b`).Check(testkit.Rows( + `IndexJoin 12487.50 root inner join, inner:IndexLookUp, outer key:pruing_under_index_join.touter.b, inner key:pruing_under_index_join.thash.b, equal cond:eq(pruing_under_index_join.touter.b, pruing_under_index_join.thash.b)`, + `├─TableReader(Build) 9990.00 root data:Selection`, + `│ └─Selection 9990.00 cop[tikv] not(isnull(pruing_under_index_join.touter.b))`, + `│ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + `└─IndexLookUp(Probe) 12487.50 root partition:all `, + ` ├─Selection(Build) 12487.50 cop[tikv] not(isnull(pruing_under_index_join.thash.b))`, + ` │ └─IndexRangeScan 12500.00 cop[tikv] table:thash, index:idx_b(b) range: decided by [eq(pruing_under_index_join.thash.b, pruing_under_index_join.touter.b)], keep order:false, stats:pseudo`, + ` └─TableRowIDScan(Probe) 12487.50 cop[tikv] table:thash keep order:false, stats:pseudo`)) + tk.MustQuery(`select /*+ INL_JOIN(touter, thash) */ thash.* from touter join thash use index(idx_b) on touter.b = thash.b`).Sort().Check( + tk.MustQuery(`select /*+ INL_JOIN(touter, tnormal) */ tnormal.* from touter join tnormal use index(idx_b) on touter.b = tnormal.b`).Sort().Rows()) +} + +func TestIssue25527(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_issue_25527") + tk.MustExec("use test_issue_25527") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set @@session.tidb_enable_list_partition = ON") + + // the original case + tk.MustExec(`CREATE TABLE t ( + col1 tinyint(4) primary key + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH( COL1 DIV 80 ) + PARTITIONS 6`) + tk.MustExec(`insert into t values(-128), (107)`) + tk.MustExec(`prepare stmt from 'select col1 from t where col1 in (?, ?, ?)'`) + tk.MustExec(`set @a=-128, @b=107, @c=-128`) + tk.MustQuery(`execute stmt using @a,@b,@c`).Sort().Check(testkit.Rows("-128", "107")) + + // the minimal reproducible case for hash partitioning + tk.MustExec(`CREATE TABLE t0 (a int primary key) PARTITION BY HASH( a DIV 80 ) PARTITIONS 2`) + tk.MustExec(`insert into t0 values (1)`) + tk.MustQuery(`select a from t0 where a in (1)`).Check(testkit.Rows("1")) + + // the minimal reproducible case for range partitioning + tk.MustExec(`create table t1 (a int primary key) partition by range (a+5) ( + partition p0 values less than(10), partition p1 values less than(20))`) + tk.MustExec(`insert into t1 values (5)`) + tk.MustQuery(`select a from t1 where a in (5)`).Check(testkit.Rows("5")) + + // the minimal reproducible case for list partitioning + tk.MustExec(`create table t2 (a int primary key) partition by list (a+5) ( + partition p0 values in (5, 6, 7, 8), partition p1 values in (9, 10, 11, 12))`) + tk.MustExec(`insert into t2 values (5)`) + tk.MustQuery(`select a from t2 where a in (5)`).Check(testkit.Rows("5")) +} + +func TestIssue25598(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_issue_25598") + tk.MustExec("use test_issue_25598") + tk.MustExec(`CREATE TABLE UK_HP16726 ( + COL1 bigint(16) DEFAULT NULL, + COL2 varchar(20) DEFAULT NULL, + COL4 datetime DEFAULT NULL, + COL3 bigint(20) DEFAULT NULL, + COL5 float DEFAULT NULL, + UNIQUE KEY UK_COL1 (COL1) /*!80000 INVISIBLE */ + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin + PARTITION BY HASH( COL1 ) + PARTITIONS 25`) + + tk.MustQuery(`select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`).Check(testkit.Rows()) + tk.MustExec(`explain select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`) + + tk.MustExec(`set @@tidb_partition_prune_mode = 'dynamic'`) + tk.MustQuery(`select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`).Check(testkit.Rows()) + tk.MustExec(`explain select t1. col1, t2. col1 from UK_HP16726 as t1 inner join UK_HP16726 as t2 on t1.col1 = t2.col1 where t1.col1 > -9223372036854775808 group by t1.col1, t2.col1 having t1.col1 != 9223372036854775807`) +} + +func TestBatchGetforRangeandListPartitionTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_pointget") + tk.MustExec("use test_pointget") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set @@session.tidb_enable_list_partition = ON") + + // list partition table + tk.MustExec(`create table tlist(a int, b int, unique index idx_a(a), index idx_b(b)) partition by list(a)( + partition p0 values in (1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8), + partition p2 values in (9, 10, 11, 12));`) + + // range partition table + tk.MustExec(`create table trange(a int, unique key(a)) partition by range(a) ( + partition p0 values less than (30), + partition p1 values less than (60), + partition p2 values less than (90), + partition p3 values less than (120));`) + + // hash partition table + tk.MustExec("create table thash(a int unsigned, unique key(a)) partition by hash(a) partitions 4;") + + // insert data into list partition table + tk.MustExec("insert into tlist values(1,1), (2,2), (3, 3), (4, 4), (5,5), (6, 6), (7,7), (8, 8), (9, 9), (10, 10), (11, 11), (12, 12);") + // regular partition table + tk.MustExec("create table tregular1(a int, unique key(a));") + tk.MustExec("create table tregular2(a int, unique key(a));") + + vals := make([]string, 0, 100) + // insert data into range partition table and hash partition table + for i := 0; i < 100; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i+1)) + } + tk.MustExec("insert into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular2 values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)") + + // test BatchGet + for i := 0; i < 100; i++ { + // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used + // select a from t where where a in ({x1}, {x2}, ... {x10}); + points := make([]string, 0, 10) + for i := 0; i < 10; i++ { + x := rand.Intn(100) + 1 + points = append(points, fmt.Sprintf("%v", x)) + } + queryRegular1 := fmt.Sprintf("select a from tregular1 where a in (%v)", strings.Join(points, ",")) + + queryHash := fmt.Sprintf("select a from thash where a in (%v)", strings.Join(points, ",")) + tk.MustHavePlan(queryHash, "Batch_Point_Get") // check if BatchGet is used + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryRange := fmt.Sprintf("select a from trange where a in (%v)", strings.Join(points, ",")) + tk.MustHavePlan(queryRange, "Batch_Point_Get") // check if BatchGet is used + tk.MustQuery(queryRange).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + points = make([]string, 0, 10) + for i := 0; i < 10; i++ { + x := rand.Intn(12) + 1 + points = append(points, fmt.Sprintf("%v", x)) + } + queryRegular2 := fmt.Sprintf("select a from tregular2 where a in (%v)", strings.Join(points, ",")) + queryList := fmt.Sprintf("select a from tlist where a in (%v)", strings.Join(points, ",")) + tk.MustHavePlan(queryList, "Batch_Point_Get") // check if BatchGet is used + tk.MustQuery(queryList).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + } + + // test different data type + // unsigned flag + // partition table and reguar table pair + tk.MustExec(`create table trange3(a int unsigned, unique key(a)) partition by range(a) ( + partition p0 values less than (30), + partition p1 values less than (60), + partition p2 values less than (90), + partition p3 values less than (120));`) + tk.MustExec("create table tregular3(a int unsigned, unique key(a));") + vals = make([]string, 0, 100) + // insert data into range partition table and hash partition table + for i := 0; i < 100; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i+1)) + } + tk.MustExec("insert into trange3 values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular3 values " + strings.Join(vals, ",")) + // test BatchGet + // explain select a from t where a in ({x1}, {x2}, ... {x10}); // BatchGet is used + // select a from t where where a in ({x1}, {x2}, ... {x10}); + points := make([]string, 0, 10) + for i := 0; i < 10; i++ { + x := rand.Intn(100) + 1 + points = append(points, fmt.Sprintf("%v", x)) + } + queryRegular := fmt.Sprintf("select a from tregular3 where a in (%v)", strings.Join(points, ",")) + queryRange := fmt.Sprintf("select a from trange3 where a in (%v)", strings.Join(points, ",")) + tk.MustHavePlan(queryRange, "Batch_Point_Get") // check if BatchGet is used + tk.MustQuery(queryRange).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) +} + +func TestGlobalStatsAndSQLBinding(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_global_stats") + tk.MustExec("use test_global_stats") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set tidb_cost_model_version=2") + + // hash and range and list partition + tk.MustExec("create table thash(a int, b int, key(a)) partition by hash(a) partitions 4") + tk.MustExec(`create table trange(a int, b int, key(a)) partition by range(a) ( + partition p0 values less than (200), + partition p1 values less than (400), + partition p2 values less than (600), + partition p3 values less than (800), + partition p4 values less than (1001))`) + tk.MustExec(`create table tlist (a int, b int, key(a)) partition by list (a) ( + partition p0 values in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9), + partition p1 values in (10, 11, 12, 13, 14, 15, 16, 17, 18, 19), + partition p2 values in (20, 21, 22, 23, 24, 25, 26, 27, 28, 29), + partition p3 values in (30, 31, 32, 33, 34, 35, 36, 37, 38, 39), + partition p4 values in (40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50))`) + + // construct some special data distribution + vals := make([]string, 0, 1000) + listVals := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + if i < 10 { + // for hash and range partition, 1% of records are in [0, 100) + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100))) + // for list partition, 1% of records are equal to 0 + listVals = append(listVals, "(0, 0)") + } else { + vals = append(vals, fmt.Sprintf("(%v, %v)", 100+rand.Intn(900), 100+rand.Intn(900))) + listVals = append(listVals, fmt.Sprintf("(%v, %v)", 1+rand.Intn(50), 1+rand.Intn(50))) + } + } + tk.MustExec("insert into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert into tlist values " + strings.Join(listVals, ",")) + + // before analyzing, the planner will choose TableScan to access the 1% of records + tk.MustHavePlan("select * from thash where a<100", "TableFullScan") + tk.MustHavePlan("select * from trange where a<100", "TableFullScan") + tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") + + tk.MustExec("analyze table thash") + tk.MustExec("analyze table trange") + tk.MustExec("analyze table tlist") + + tk.MustHavePlan("select * from thash where a<100", "TableFullScan") + tk.MustHavePlan("select * from trange where a<100", "TableFullScan") + tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") + + // create SQL bindings + tk.MustExec("create session binding for select * from thash where a<100 using select * from thash ignore index(a) where a<100") + tk.MustExec("create session binding for select * from trange where a<100 using select * from trange ignore index(a) where a<100") + tk.MustExec("create session binding for select * from tlist where a<100 using select * from tlist ignore index(a) where a<100") + + // use TableScan again since the Index(a) is ignored + tk.MustHavePlan("select * from thash where a<100", "TableFullScan") + tk.MustHavePlan("select * from trange where a<100", "TableFullScan") + tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") + + // drop SQL bindings + tk.MustExec("drop session binding for select * from thash where a<100") + tk.MustExec("drop session binding for select * from trange where a<100") + tk.MustExec("drop session binding for select * from tlist where a<100") + + tk.MustHavePlan("select * from thash where a<100", "TableFullScan") + tk.MustHavePlan("select * from trange where a<100", "TableFullScan") + tk.MustHavePlan("select * from tlist where a<1", "TableFullScan") +} + +func TestPartitionTableWithDifferentJoin(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_partition_joins") + tk.MustExec("use test_partition_joins") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // hash and range partition + tk.MustExec("create table thash(a int, b int, key(a)) partition by hash(a) partitions 4") + tk.MustExec("create table tregular1(a int, b int, key(a))") + + tk.MustExec(`create table trange(a int, b int, key(a)) partition by range(a) ( + partition p0 values less than (200), + partition p1 values less than (400), + partition p2 values less than (600), + partition p3 values less than (800), + partition p4 values less than (1001))`) + tk.MustExec("create table tregular2(a int, b int, key(a))") + + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) + } + tk.MustExec("insert into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) + + vals = make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) + } + tk.MustExec("insert into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular2 values " + strings.Join(vals, ",")) + + // random params + x1 := rand.Intn(1000) + x2 := rand.Intn(1000) + x3 := rand.Intn(1000) + x4 := rand.Intn(1000) + + // group 1 + // hash_join range partition and hash partition + queryHash := fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.b=thash.b and thash.a = %v and trange.a > %v;", x1, x2) + queryRegular := fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.b=tregular1.b and tregular1.a = %v and tregular2.a > %v;", x1, x2) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a > %v;", x1) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a > %v;", x1) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and trange.b = thash.b and thash.a > %v;", x1) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.b = tregular2.b and tregular1.a > %v;", x1) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ hash_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a = %v;", x1) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a = %v;", x1) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // group 2 + // hash_join range partition and regular table + queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a >= %v and tregular1.a > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a >= %v and tregular1.a > %v;", x1, x2) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a in (%v, %v, %v);", x1, x2, x3) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a in (%v, %v, %v);", x1, x2, x3) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ hash_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and tregular1.a >= %v;", x1) + queryRegular = fmt.Sprintf("select /*+ hash_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular1.a >= %v;", x1) + tk.MustHavePlan(queryHash, "HashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // group 3 + // merge_join range partition and hash partition + queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.b=thash.b and thash.a = %v and trange.a > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.b=tregular1.b and tregular1.a = %v and tregular2.a > %v;", x1, x2) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a > %v;", x1) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a > %v;", x1) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and trange.b = thash.b and thash.a > %v;", x1) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.b = tregular2.b and tregular1.a > %v;", x1) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ merge_join(trange, thash) */ * from trange, thash where trange.a=thash.a and thash.a = %v;", x1) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a=tregular1.a and tregular1.a = %v;", x1) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // group 4 + // merge_join range partition and regular table + queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a >= %v and tregular1.a > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a >= %v and tregular1.a > %v;", x1, x2) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and trange.a in (%v, %v, %v);", x1, x2, x3) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular2.a in (%v, %v, %v);", x1, x2, x3) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ merge_join(trange, tregular1) */ * from trange, tregular1 where trange.a = tregular1.a and tregular1.a >= %v;", x1) + queryRegular = fmt.Sprintf("select /*+ merge_join(tregular2, tregular1) */ * from tregular2, tregular1 where tregular2.a = tregular1.a and tregular1.a >= %v;", x1) + tk.MustHavePlan(queryHash, "MergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // new table instances + tk.MustExec("create table thash2(a int, b int, index idx(a)) partition by hash(a) partitions 4") + tk.MustExec("create table tregular3(a int, b int, index idx(a))") + + tk.MustExec(`create table trange2(a int, b int, index idx(a)) partition by range(a) ( + partition p0 values less than (200), + partition p1 values less than (400), + partition p2 values less than (600), + partition p3 values less than (800), + partition p4 values less than (1001))`) + tk.MustExec("create table tregular4(a int, b int, index idx(a))") + + vals = make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) + } + tk.MustExec("insert into thash2 values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular3 values " + strings.Join(vals, ",")) + + vals = make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1000), rand.Intn(1000))) + } + tk.MustExec("insert into trange2 values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular4 values " + strings.Join(vals, ",")) + + // group 5 + // index_merge_join range partition and range partition + // Currently don't support index merge join on two partition tables. Only test warning. + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v;", x1) + // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v;", x1) + // tk.MustHavePlan(queryHash, "IndexMergeJoin") + // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + tk.MustQuery(queryHash) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange2.a > %v;", x1, x2) + // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.a > %v;", x1, x2) + // tk.MustHavePlan(queryHash, "IndexMergeJoin") + // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + tk.MustQuery(queryHash) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange.b > %v;", x1, x2) + // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular2.b > %v;", x1, x2) + // tk.MustHavePlan(queryHash, "IndexMergeJoin") + // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + tk.MustQuery(queryHash) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, trange2) */ * from trange, trange2 where trange.a=trange2.a and trange.a > %v and trange2.b > %v;", x1, x2) + // queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.b > %v;", x1, x2) + // tk.MustHavePlan(queryHash, "IndexMergeJoin") + // tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + tk.MustQuery(queryHash) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1815|Optimizer Hint /*+ INL_MERGE_JOIN(trange, trange2) */ is inapplicable")) + + // group 6 + // index_merge_join range partition and regualr table + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v;", x1) + queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v;", x1) + tk.MustHavePlan(queryHash, "IndexMergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and tregular4.a > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.a > %v;", x1, x2) + tk.MustHavePlan(queryHash, "IndexMergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and trange.b > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular2.b > %v;", x1, x2) + tk.MustHavePlan(queryHash, "IndexMergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_merge_join(trange, tregular4) */ * from trange, tregular4 where trange.a=tregular4.a and trange.a > %v and tregular4.b > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_merge_join(tregular2, tregular4) */ * from tregular2, tregular4 where tregular2.a=tregular4.a and tregular2.a > %v and tregular4.b > %v;", x1, x2) + tk.MustHavePlan(queryHash, "IndexMergeJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // group 7 + // index_hash_join hash partition and hash partition + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a in (%v, %v);", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v);", x1, x2) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a in (%v, %v) and thash2.a in (%v, %v);", x1, x2, x3, x4) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, thash2) */ * from thash, thash2 where thash.a = thash2.a and thash.a > %v and thash2.b > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a > %v and tregular3.b > %v;", x1, x2) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + // group 8 + // index_hash_join hash partition and hash partition + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a in (%v, %v);", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v);", x1, x2) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a in (%v, %v) and tregular3.a in (%v, %v);", x1, x2, x3, x4) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) + + queryHash = fmt.Sprintf("select /*+ inl_hash_join(thash, tregular3) */ * from thash, tregular3 where thash.a = tregular3.a and thash.a > %v and tregular3.b > %v;", x1, x2) + queryRegular = fmt.Sprintf("select /*+ inl_hash_join(tregular1, tregular3) */ * from tregular1, tregular3 where tregular1.a = tregular3.a and tregular1.a > %v and tregular3.b > %v;", x1, x2) + tk.MustHavePlan(queryHash, "IndexHashJoin") + tk.MustQuery(queryHash).Sort().Check(tk.MustQuery(queryRegular).Sort().Rows()) +} + +func createTable4DynamicPruneModeTestWithExpression(tk *testkit.TestKit) { + tk.MustExec("create table trange(a int, b int) partition by range(a) (partition p0 values less than(3), partition p1 values less than (5), partition p2 values less than(11));") + tk.MustExec("create table thash(a int, b int) partition by hash(a) partitions 4;") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into trange values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") + tk.MustExec("insert into thash values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") + tk.MustExec("insert into t values(1, NULL), (1, NULL), (1, 1), (2, 1), (3, 2), (4, 3), (5, 5), (6, 7), (7, 7), (7, 7), (10, NULL), (NULL, NULL), (NULL, 1);") + tk.MustExec("set session tidb_partition_prune_mode='dynamic'") + tk.MustExec("analyze table trange") + tk.MustExec("analyze table thash") + tk.MustExec("analyze table t") +} + +type testData4Expression struct { + sql string + partitions []string +} + +func TestDateColWithUnequalExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_datetime_unequal_expression") + tk.MustExec("create database db_datetime_unequal_expression") + tk.MustExec("use db_datetime_unequal_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec(`create table tp(a datetime, b int) partition by range columns (a) (partition p0 values less than("2012-12-10 00:00:00"), partition p1 values less than("2022-12-30 00:00:00"), partition p2 values less than("2025-12-12 00:00:00"))`) + tk.MustExec(`create table t(a datetime, b int) partition by range columns (a) (partition p0 values less than("2012-12-10 00:00:00"), partition p1 values less than("2022-12-30 00:00:00"), partition p2 values less than("2025-12-12 00:00:00"))`) + tk.MustExec(`insert into tp values("2015-09-09 00:00:00", 1), ("2020-08-08 19:00:01", 2), ("2024-01-01 01:01:01", 3)`) + tk.MustExec(`insert into t values("2015-09-09 00:00:00", 1), ("2020-08-08 19:00:01", 2), ("2024-01-01 01:01:01", 3)`) + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a != '2024-01-01 01:01:01'", + partitions: []string{"all"}, + }, + { + sql: "select * from %s where a != '2024-01-01 01:01:01' and a > '2015-09-09 00:00:00'", + partitions: []string{"p1,p2"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestToDaysColWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_to_days_expression") + tk.MustExec("create database db_to_days_expression") + tk.MustExec("use db_to_days_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a date, b int) partition by range(to_days(a)) (partition p0 values less than (737822), partition p1 values less than (738019), partition p2 values less than (738154))") + tk.MustExec("create table t(a date, b int)") + tk.MustExec("insert into tp values('2020-01-01', 1), ('2020-03-02', 2), ('2020-05-05', 3), ('2020-11-11', 4)") + tk.MustExec("insert into t values('2020-01-01', 1), ('2020-03-02', 2), ('2020-05-05', 3), ('2020-11-11', 4)") + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a < '2020-08-16'", + partitions: []string{"p0,p1"}, + }, + { + sql: "select * from %s where a between '2020-05-01' and '2020-10-01'", + partitions: []string{"p1,p2"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestWeekdayWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_weekday_expression") + tk.MustExec("create database db_weekday_expression") + tk.MustExec("use db_weekday_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a datetime, b int) partition by range(weekday(a)) (partition p0 values less than(3), partition p1 values less than(5), partition p2 values less than(8))") + tk.MustExec("create table t(a datetime, b int)") + tk.MustExec(`insert into tp values("2020-08-17 00:00:00", 1), ("2020-08-18 00:00:00", 2), ("2020-08-19 00:00:00", 4), ("2020-08-20 00:00:00", 5), ("2020-08-21 00:00:00", 6), ("2020-08-22 00:00:00", 0)`) + tk.MustExec(`insert into t values("2020-08-17 00:00:00", 1), ("2020-08-18 00:00:00", 2), ("2020-08-19 00:00:00", 4), ("2020-08-20 00:00:00", 5), ("2020-08-21 00:00:00", 6), ("2020-08-22 00:00:00", 0)`) + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a = '2020-08-17 00:00:00'", + partitions: []string{"p0"}, + }, + { + sql: "select * from %s where a= '2020-08-20 00:00:00' and a < '2020-08-22 00:00:00'", + partitions: []string{"p1"}, + }, + { + sql: " select * from %s where a < '2020-08-19 00:00:00'", + partitions: []string{"all"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestFloorUnixTimestampAndIntColWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_floor_unix_timestamp_int_expression") + tk.MustExec("create database db_floor_unix_timestamp_int_expression") + tk.MustExec("use db_floor_unix_timestamp_int_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a timestamp, b int) partition by range(floor(unix_timestamp(a))) (partition p0 values less than(1580670000), partition p1 values less than(1597622400), partition p2 values less than(1629158400))") + tk.MustExec("create table t(a timestamp, b int)") + tk.MustExec("insert into tp values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") + tk.MustExec("insert into t values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a > '2020-09-11 00:00:00'", + partitions: []string{"p2"}, + }, + { + sql: "select * from %s where a < '2020-07-07 01:00:00'", + partitions: []string{"p0,p1"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestUnixTimestampAndIntColWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_unix_timestamp_int_expression") + tk.MustExec("create database db_unix_timestamp_int_expression") + tk.MustExec("use db_unix_timestamp_int_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a timestamp, b int) partition by range(unix_timestamp(a)) (partition p0 values less than(1580670000), partition p1 values less than(1597622400), partition p2 values less than(1629158400))") + tk.MustExec("create table t(a timestamp, b int)") + tk.MustExec("insert into tp values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") + tk.MustExec("insert into t values('2020-01-01 19:00:00', 1),('2020-08-15 00:00:00', -1), ('2020-08-18 05:00:01', 2), ('2020-10-01 14:13:15', 3)") + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a > '2020-09-11 00:00:00'", + partitions: []string{"p2"}, + }, + { + sql: "select * from %s where a < '2020-07-07 01:00:00'", + partitions: []string{"p0,p1"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestDatetimeColAndIntColWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_datetime_int_expression") + tk.MustExec("create database db_datetime_int_expression") + tk.MustExec("use db_datetime_int_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a datetime, b int) partition by range columns(a) (partition p0 values less than('2020-02-02 00:00:00'), partition p1 values less than('2020-09-01 00:00:00'), partition p2 values less than('2020-12-20 00:00:00'))") + tk.MustExec("create table t(a datetime, b int)") + tk.MustExec("insert into tp values('2020-01-01 12:00:00', 1), ('2020-08-22 10:00:00', 2), ('2020-09-09 11:00:00', 3), ('2020-10-01 00:00:00', 4)") + tk.MustExec("insert into t values('2020-01-01 12:00:00', 1), ('2020-08-22 10:00:00', 2), ('2020-09-09 11:00:00', 3), ('2020-10-01 00:00:00', 4)") + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a < '2020-09-01 00:00:00'", + partitions: []string{"p0,p1"}, + }, + { + sql: "select * from %s where a > '2020-07-07 01:00:00'", + partitions: []string{"p1,p2"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestVarcharColAndIntColWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_varchar_int_expression") + tk.MustExec("create database db_varchar_int_expression") + tk.MustExec("use db_varchar_int_expression") + tk.MustExec("set tidb_partition_prune_mode='dynamic'") + tk.MustExec("create table tp(a varchar(255), b int) partition by range columns(a) (partition p0 values less than('ddd'), partition p1 values less than('ggggg'), partition p2 values less than('mmmmmm'))") + tk.MustExec("create table t(a varchar(255), b int)") + tk.MustExec("insert into tp values('aaa', 1), ('bbbb', 2), ('ccc', 3), ('dfg', 4), ('kkkk', 5), ('10', 6)") + tk.MustExec("insert into t values('aaa', 1), ('bbbb', 2), ('ccc', 3), ('dfg', 4), ('kkkk', 5), ('10', 6)") + tk.MustExec("analyze table tp") + tk.MustExec("analyze table t") + + tests := []testData4Expression{ + { + sql: "select * from %s where a < '10'", + partitions: []string{"p0"}, + }, + { + sql: "select * from %s where a > 0", + partitions: []string{"all"}, + }, + { + sql: "select * from %s where a < 0", + partitions: []string{"all"}, + }, + } + + for _, t := range tests { + tpSQL := fmt.Sprintf(t.sql, "tp") + tSQL := fmt.Sprintf(t.sql, "t") + tk.MustPartition(tpSQL, t.partitions[0]).Sort().Check(tk.MustQuery(tSQL).Sort().Rows()) + } +} + +func TestDynamicPruneModeWithExpression(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists db_equal_expression") + tk.MustExec("create database db_equal_expression") + tk.MustExec("use db_equal_expression") + createTable4DynamicPruneModeTestWithExpression(tk) + + tables := []string{"trange", "thash"} + tests := []testData4Expression{ + { + sql: "select * from %s where a = 2", + partitions: []string{ + "p0", + "p2", + }, + }, + { + sql: "select * from %s where a = 4 or a = 1", + partitions: []string{ + "p0,p1", + "p0,p1", + }, + }, + { + sql: "select * from %s where a = -1", + partitions: []string{ + "p0", + "p1", + }, + }, + { + sql: "select * from %s where a is NULL", + partitions: []string{ + "p0", + "p0", + }, + }, + { + sql: "select * from %s where b is NULL", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a > -1", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a >= 4 and a <= 5", + partitions: []string{ + "p1,p2", + "p0,p1", + }, + }, + { + sql: "select * from %s where a > 10", + partitions: []string{ + "dual", + "all", + }, + }, + { + sql: "select * from %s where a >=2 and a <= 3", + partitions: []string{ + "p0,p1", + "p2,p3", + }, + }, + { + sql: "select * from %s where a between 2 and 3", + partitions: []string{ + "p0,p1", + "p2,p3", + }, + }, + { + sql: "select * from %s where a < 2", + partitions: []string{ + "p0", + "all", + }, + }, + { + sql: "select * from %s where a <= 3", + partitions: []string{ + "p0,p1", + "all", + }, + }, + { + sql: "select * from %s where a in (2, 3)", + partitions: []string{ + "p0,p1", + "p2,p3", + }, + }, + { + sql: "select * from %s where a in (1, 5)", + partitions: []string{ + "p0,p2", + "p1", + }, + }, + { + sql: "select * from %s where a not in (1, 5)", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a = 2 and a = 2", + partitions: []string{ + "p0", + "p2", + }, + }, + { + sql: "select * from %s where a = 2 and a = 3", + partitions: []string{ + // This means that we have no partition-read plan + "", + "", + }, + }, + { + sql: "select * from %s where a < 2 and a > 0", + partitions: []string{ + "p0", + "p1", + }, + }, + { + sql: "select * from %s where a < 2 and a < 3", + partitions: []string{ + "p0", + "all", + }, + }, + { + sql: "select * from %s where a > 1 and a > 2", + partitions: []string{ + "p1,p2", + "all", + }, + }, + { + sql: "select * from %s where a = 2 or a = 3", + partitions: []string{ + "p0,p1", + "p2,p3", + }, + }, + { + sql: "select * from %s where a = 2 or a in (3)", + partitions: []string{ + "p0,p1", + "p2,p3", + }, + }, + { + sql: "select * from %s where a = 2 or a > 3", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a = 2 or a <= 1", + partitions: []string{ + "p0", + "all", + }, + }, + { + sql: "select * from %s where a = 2 or a between 2 and 2", + partitions: []string{ + "p0", + "p2", + }, + }, + { + sql: "select * from %s where a != 2", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a != 2 and a > 4", + partitions: []string{ + "p2", + "all", + }, + }, + { + sql: "select * from %s where a != 2 and a != 3", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a != 2 and a = 3", + partitions: []string{ + "p1", + "p3", + }, + }, + { + sql: "select * from %s where not (a = 2)", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where not (a > 2)", + partitions: []string{ + "p0", + "all", + }, + }, + { + sql: "select * from %s where not (a < 2)", + partitions: []string{ + "all", + "all", + }, + }, + // cases that partition pruning can not work + { + sql: "select * from %s where a + 1 > 4", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a - 1 > 0", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a * 2 < 0", + partitions: []string{ + "all", + "all", + }, + }, + { + sql: "select * from %s where a << 1 < 0", + partitions: []string{ + "all", + "all", + }, + }, + // comparison between int column and string column + { + sql: "select * from %s where a > '10'", + partitions: []string{ + "dual", + "all", + }, + }, + { + sql: "select * from %s where a > '10ab'", + partitions: []string{ + "dual", + "all", + }, + }, + } + + for _, t := range tests { + for i := range t.partitions { + sql := fmt.Sprintf(t.sql, tables[i]) + tk.MustPartition(sql, t.partitions[i]).Sort().Check(tk.MustQuery(fmt.Sprintf(t.sql, "t")).Sort().Rows()) + } + } +} + +func TestAddDropPartitions(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_add_drop_partition") + tk.MustExec("use test_add_drop_partition") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table t(a int) partition by range(a) ( + partition p0 values less than (5), + partition p1 values less than (10), + partition p2 values less than (15))`) + tk.MustExec(`insert into t values (2), (7), (12)`) + tk.MustPartition(`select * from t where a < 3`, "p0").Sort().Check(testkit.Rows("2")) + tk.MustPartition(`select * from t where a < 8`, "p0,p1").Sort().Check(testkit.Rows("2", "7")) + tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "2", "7")) + + // remove p0 + tk.MustExec(`alter table t drop partition p0`) + tk.MustPartition(`select * from t where a < 3`, "p1").Sort().Check(testkit.Rows()) + tk.MustPartition(`select * from t where a < 8`, "p1").Sort().Check(testkit.Rows("7")) + tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "7")) + + // add 2 more partitions + tk.MustExec(`alter table t add partition (partition p3 values less than (20))`) + tk.MustExec(`alter table t add partition (partition p4 values less than (40))`) + tk.MustExec(`insert into t values (15), (25)`) + tk.MustPartition(`select * from t where a < 3`, "p1").Sort().Check(testkit.Rows()) + tk.MustPartition(`select * from t where a < 8`, "p1").Sort().Check(testkit.Rows("7")) + tk.MustPartition(`select * from t where a < 20`, "p1,p2,p3").Sort().Check(testkit.Rows("12", "15", "7")) +} + +func TestMPPQueryExplainInfo(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database tiflash_partition_test") + tk.MustExec("use tiflash_partition_test") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table t(a int) partition by range(a) ( + partition p0 values less than (5), + partition p1 values less than (10), + partition p2 values less than (15))`) + tb := external.GetTableByName(t, tk, "tiflash_partition_test", "t") + for _, partition := range tb.Meta().GetPartitionInfo().Definitions { + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), partition.ID, true) + require.NoError(t, err) + } + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + tk.MustExec(`insert into t values (2), (7), (12)`) + tk.MustExec("set tidb_enforce_mpp=1") + tk.MustPartition(`select * from t where a < 3`, "p0").Sort().Check(testkit.Rows("2")) + tk.MustPartition(`select * from t where a < 8`, "p0,p1").Sort().Check(testkit.Rows("2", "7")) + tk.MustPartition(`select * from t where a < 20`, "all").Sort().Check(testkit.Rows("12", "2", "7")) + tk.MustPartition(`select * from t where a < 5 union all select * from t where a > 10`, "p0").Sort().Check(testkit.Rows("12", "2")) + tk.MustPartition(`select * from t where a < 5 union all select * from t where a > 10`, "p2").Sort().Check(testkit.Rows("12", "2")) +} + +func TestPartitionPruningInTransaction(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_pruning_transaction") + defer tk.MustExec(`drop database test_pruning_transaction`) + tk.MustExec("use test_pruning_transaction") + tk.MustExec(`create table t(a int, b int) partition by range(a) (partition p0 values less than(3), partition p1 values less than (5), partition p2 values less than(11))`) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec(`begin`) + tk.MustPartitionByList(`select * from t`, []string{"p0", "p1", "p2"}) + tk.MustPartitionByList(`select * from t where a > 3`, []string{"p1", "p2"}) // partition pruning can work in transactions + tk.MustPartitionByList(`select * from t where a > 7`, []string{"p2"}) + tk.MustExec(`rollback`) + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec(`begin`) + tk.MustPartition(`select * from t`, "all") + tk.MustPartition(`select * from t where a > 3`, "p1,p2") // partition pruning can work in transactions + tk.MustPartition(`select * from t where a > 7`, "p2") + tk.MustExec(`rollback`) + tk.MustExec("set @@tidb_partition_prune_mode = default") +} + +func TestIssue25253(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database issue25253") + defer tk.MustExec("drop database issue25253") + tk.MustExec("use issue25253") + + tk.MustExec(`CREATE TABLE IDT_HP23902 ( + COL1 smallint DEFAULT NULL, + COL2 varchar(20) DEFAULT NULL, + COL4 datetime DEFAULT NULL, + COL3 bigint DEFAULT NULL, + COL5 float DEFAULT NULL, + KEY UK_COL1 (COL1) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin + PARTITION BY HASH( COL1+30 ) + PARTITIONS 6`) + tk.MustExec(`insert ignore into IDT_HP23902 partition(p0, p1)(col1, col3) values(-10355, 1930590137900568573), (13810, -1332233145730692137)`) + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1748 Found a row not matching the given partition set", + "Warning 1748 Found a row not matching the given partition set")) + tk.MustQuery(`select * from IDT_HP23902`).Check(testkit.Rows()) + + tk.MustExec(`create table t ( + a int + ) partition by range(a) ( + partition p0 values less than (10), + partition p1 values less than (20))`) + tk.MustExec(`insert ignore into t partition(p0)(a) values(12)`) + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1748 Found a row not matching the given partition set")) + tk.MustQuery(`select * from t`).Check(testkit.Rows()) +} + +func TestDML(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_DML") + defer tk.MustExec(`drop database test_DML`) + tk.MustExec("use test_DML") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table tinner (a int, b int)`) + tk.MustExec(`create table thash (a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a int, b int) partition by range(a) ( + partition p0 values less than(10000), + partition p1 values less than(20000), + partition p2 values less than(30000), + partition p3 values less than(40000), + partition p4 values less than MAXVALUE)`) + + vals := make([]string, 0, 50) + for i := 0; i < 50; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec(`insert into tinner values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) + + // delete, insert, replace, update + for i := 0; i < 200; i++ { + var pattern string + switch rand.Intn(4) { + case 0: // delete + col := []string{"a", "b"}[rand.Intn(2)] + l := rand.Intn(40000) + r := l + rand.Intn(5000) + pattern = fmt.Sprintf(`delete from %%v where %v>%v and %v<%v`, col, l, col, r) + case 1: // insert + a, b := rand.Intn(40000), rand.Intn(40000) + pattern = fmt.Sprintf(`insert into %%v values (%v, %v)`, a, b) + case 2: // replace + a, b := rand.Intn(40000), rand.Intn(40000) + pattern = fmt.Sprintf(`replace into %%v(a, b) values (%v, %v)`, a, b) + case 3: // update + col := []string{"a", "b"}[rand.Intn(2)] + l := rand.Intn(40000) + r := l + rand.Intn(5000) + x := rand.Intn(1000) - 500 + pattern = fmt.Sprintf(`update %%v set %v=%v+%v where %v>%v and %v<%v`, col, col, x, col, l, col, r) + } + for _, tbl := range []string{"tinner", "thash", "trange"} { + tk.MustExec(fmt.Sprintf(pattern, tbl)) + } + + // check + r := tk.MustQuery(`select * from tinner`).Sort().Rows() + tk.MustQuery(`select * from thash`).Sort().Check(r) + tk.MustQuery(`select * from trange`).Sort().Check(r) + } +} + +func TestUnion(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_union") + defer tk.MustExec(`drop database test_union`) + tk.MustExec("use test_union") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table t(a int, b int, key(a))`) + tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a int, b int, key(a)) partition by range(a) ( + partition p0 values less than (10000), + partition p1 values less than (20000), + partition p2 values less than (30000), + partition p3 values less than (40000))`) + + vals := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec(`insert into t values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) + + randRange := func() (int, int) { + l, r := rand.Intn(40000), rand.Intn(40000) + if l > r { + l, r = r, l + } + return l, r + } + + for i := 0; i < 100; i++ { + a1l, a1r := randRange() + a2l, a2r := randRange() + b1l, b1r := randRange() + b2l, b2r := randRange() + for _, utype := range []string{"union all", "union distinct"} { + pattern := fmt.Sprintf(`select * from %%v where a>=%v and a<=%v and b>=%v and b<=%v + %v select * from %%v where a>=%v and a<=%v and b>=%v and b<=%v`, a1l, a1r, b1l, b1r, utype, a2l, a2r, b2l, b2r) + r := tk.MustQuery(fmt.Sprintf(pattern, "t", "t")).Sort().Rows() + tk.MustQuery(fmt.Sprintf(pattern, "thash", "thash")).Sort().Check(r) // hash + hash + tk.MustQuery(fmt.Sprintf(pattern, "trange", "trange")).Sort().Check(r) // range + range + tk.MustQuery(fmt.Sprintf(pattern, "trange", "thash")).Sort().Check(r) // range + hash + } + } +} + +func TestSubqueries(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_subquery") + defer tk.MustExec(`drop database test_subquery`) + tk.MustExec("use test_subquery") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table touter (a int, b int, index(a))`) + tk.MustExec(`create table tinner (a int, b int, c int, index(a))`) + tk.MustExec(`create table thash (a int, b int, c int, index(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a int, b int, c int, index(a)) partition by range(a) ( + partition p0 values less than(10000), + partition p1 values less than(20000), + partition p2 values less than(30000), + partition p3 values less than(40000))`) + + outerVals := make([]string, 0, 100) + for i := 0; i < 100; i++ { + outerVals = append(outerVals, fmt.Sprintf(`(%v, %v)`, rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec(`insert into touter values ` + strings.Join(outerVals, ", ")) + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf(`(%v, %v, %v)`, rand.Intn(40000), rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec(`insert into tinner values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) + + // in + for i := 0; i < 50; i++ { + for _, op := range []string{"in", "not in"} { + x := rand.Intn(40000) + var r [][]interface{} + for _, t := range []string{"tinner", "thash", "trange"} { + q := fmt.Sprintf(`select * from touter where touter.a %v (select %v.b from %v where %v.a > touter.b and %v.c > %v)`, op, t, t, t, t, x) + if r == nil { + r = tk.MustQuery(q).Sort().Rows() + } else { + tk.MustQuery(q).Sort().Check(r) + } + } + } + } + + // exist + for i := 0; i < 50; i++ { + for _, op := range []string{"exists", "not exists"} { + x := rand.Intn(40000) + var r [][]interface{} + for _, t := range []string{"tinner", "thash", "trange"} { + q := fmt.Sprintf(`select * from touter where %v (select %v.b from %v where %v.a > touter.b and %v.c > %v)`, op, t, t, t, t, x) + if r == nil { + r = tk.MustQuery(q).Sort().Rows() + } else { + tk.MustQuery(q).Sort().Check(r) + } + } + } + } +} + +func TestSplitRegion(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_split_region") + tk.MustExec("use test_split_region") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table tnormal (a int, b int)`) + tk.MustExec(`create table thash (a int, b int, index(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a int, b int, index(a)) partition by range(a) ( + partition p0 values less than (10000), + partition p1 values less than (20000), + partition p2 values less than (30000), + partition p3 values less than (40000))`) + vals := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec(`insert into tnormal values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into thash values ` + strings.Join(vals, ", ")) + tk.MustExec(`insert into trange values ` + strings.Join(vals, ", ")) + + tk.MustExec(`SPLIT TABLE thash INDEX a BETWEEN (1) AND (25000) REGIONS 10`) + tk.MustExec(`SPLIT TABLE trange INDEX a BETWEEN (1) AND (25000) REGIONS 10`) + + result := tk.MustQuery(`select * from tnormal where a>=1 and a<=15000`).Sort().Rows() + tk.MustPartition(`select * from trange where a>=1 and a<=15000`, "p0,p1").Sort().Check(result) + tk.MustPartition(`select * from thash where a>=1 and a<=15000`, "all").Sort().Check(result) + + result = tk.MustQuery(`select * from tnormal where a in (1, 10001, 20001)`).Sort().Rows() + tk.MustPartition(`select * from trange where a in (1, 10001, 20001)`, "p0,p1,p2").Sort().Check(result) + tk.MustPartition(`select * from thash where a in (1, 10001, 20001)`, "p1").Sort().Check(result) +} + +func TestParallelApply(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create database test_parallel_apply") + tk.MustExec("use test_parallel_apply") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set tidb_enable_parallel_apply=true") + + tk.MustExec(`create table touter (a int, b int)`) + tk.MustExec(`create table tinner (a int, b int, key(a))`) + tk.MustExec(`create table thash (a int, b int, key(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table trange (a int, b int, key(a)) partition by range(a) ( + partition p0 values less than(10000), + partition p1 values less than(20000), + partition p2 values less than(30000), + partition p3 values less than(40000))`) + + vouter := make([]string, 0, 100) + for i := 0; i < 100; i++ { + vouter = append(vouter, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec("insert into touter values " + strings.Join(vouter, ", ")) + + vals := make([]string, 0, 2000) + for i := 0; i < 100; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(40000), rand.Intn(40000))) + } + tk.MustExec("insert into tinner values " + strings.Join(vals, ", ")) + tk.MustExec("insert into thash values " + strings.Join(vals, ", ")) + tk.MustExec("insert into trange values " + strings.Join(vals, ", ")) + + // parallel apply + hash partition + IndexReader as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(thash.a) from thash use index(a) where thash.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, + ` └─IndexReader 10000.00 root partition:all index:HashAgg`, // IndexReader is a inner child of Apply + ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.thash.a)->Column#8`, + ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.thash.a, test_parallel_apply.touter.b)`, + ` └─IndexFullScan 100000000.00 cop[tikv] table:thash, index:a(a) keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(thash.a) from thash use index(a) where thash.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.a) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // parallel apply + hash partition + TableReader as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(thash.b) from thash ignore index(a) where thash.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, + ` └─TableReader 10000.00 root partition:all data:HashAgg`, // TableReader is a inner child of Apply + ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.thash.b)->Column#8`, + ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.thash.a, test_parallel_apply.touter.b)`, + ` └─TableFullScan 100000000.00 cop[tikv] table:thash keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(thash.b) from thash ignore index(a) where thash.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner ignore index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // parallel apply + hash partition + IndexLookUp as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#9)->Column#7`, + ` └─IndexLookUp 10000.00 root `, // IndexLookUp is a inner child of Apply + ` ├─Selection(Build) 80000000.00 cop[tikv] gt(test_parallel_apply.tinner.a, test_parallel_apply.touter.b)`, + ` │ └─IndexFullScan 100000000.00 cop[tikv] table:tinner, index:a(a) keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 cop[tikv] funcs:sum(test_parallel_apply.tinner.b)->Column#9`, + ` └─TableRowIDScan 80000000.00 cop[tikv] table:tinner keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(thash.b) from thash use index(a) where thash.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // parallel apply + range partition + IndexReader as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(trange.a) from trange use index(a) where trange.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, + ` └─IndexReader 10000.00 root partition:all index:HashAgg`, // IndexReader is a inner child of Apply + ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.trange.a)->Column#8`, + ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.trange.a, test_parallel_apply.touter.b)`, + ` └─IndexFullScan 100000000.00 cop[tikv] table:trange, index:a(a) keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(trange.a) from trange use index(a) where trange.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.a) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // parallel apply + range partition + TableReader as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(trange.b) from trange ignore index(a) where trange.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#8)->Column#7`, + ` └─TableReader 10000.00 root partition:all data:HashAgg`, // TableReader is a inner child of Apply + ` └─HashAgg 10000.00 cop[tikv] funcs:sum(test_parallel_apply.trange.b)->Column#8`, + ` └─Selection 80000000.00 cop[tikv] gt(test_parallel_apply.trange.a, test_parallel_apply.touter.b)`, + ` └─TableFullScan 100000000.00 cop[tikv] table:trange keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(trange.b) from trange ignore index(a) where trange.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner ignore index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // parallel apply + range partition + IndexLookUp as its inner child + tk.MustQuery(`explain format='brief' select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Check(testkit.Rows( + `Projection 10000.00 root test_parallel_apply.touter.a, test_parallel_apply.touter.b`, + `└─Apply 10000.00 root CARTESIAN inner join, other cond:gt(cast(test_parallel_apply.touter.a, decimal(10,0) BINARY), Column#7)`, + ` ├─TableReader(Build) 10000.00 root data:TableFullScan`, + ` │ └─TableFullScan 10000.00 cop[tikv] table:touter keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 root funcs:sum(Column#9)->Column#7`, + ` └─IndexLookUp 10000.00 root `, // IndexLookUp is a inner child of Apply + ` ├─Selection(Build) 80000000.00 cop[tikv] gt(test_parallel_apply.tinner.a, test_parallel_apply.touter.b)`, + ` │ └─IndexFullScan 100000000.00 cop[tikv] table:tinner, index:a(a) keep order:false, stats:pseudo`, + ` └─HashAgg(Probe) 10000.00 cop[tikv] funcs:sum(test_parallel_apply.tinner.b)->Column#9`, + ` └─TableRowIDScan 80000000.00 cop[tikv] table:tinner keep order:false, stats:pseudo`)) + tk.MustQuery(`select * from touter where touter.a > (select sum(trange.b) from trange use index(a) where trange.a>touter.b)`).Sort().Check( + tk.MustQuery(`select * from touter where touter.a > (select sum(tinner.b) from tinner use index(a) where tinner.a>touter.b)`).Sort().Rows()) + + // random queries + ops := []string{"!=", ">", "<", ">=", "<="} + aggFuncs := []string{"sum", "count", "max", "min"} + tbls := []string{"tinner", "thash", "trange"} + for i := 0; i < 50; i++ { + var r [][]interface{} + op := ops[rand.Intn(len(ops))] + agg := aggFuncs[rand.Intn(len(aggFuncs))] + x := rand.Intn(10000) + for _, tbl := range tbls { + q := fmt.Sprintf(`select * from touter where touter.a > (select %v(%v.b) from %v where %v.a%vtouter.b-%v)`, agg, tbl, tbl, tbl, op, x) + if r == nil { + r = tk.MustQuery(q).Sort().Rows() + } else { + tk.MustQuery(q).Sort().Check(r) + } + } + } +} + +func TestDirectReadingWithUnionScan(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_unionscan") + defer tk.MustExec(`drop database test_unionscan`) + tk.MustExec("use test_unionscan") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table trange(a int, b int, index idx_a(a)) partition by range(a) ( + partition p0 values less than (10), + partition p1 values less than (30), + partition p2 values less than (50))`) + tk.MustExec(`create table thash(a int, b int, index idx_a(a)) partition by hash(a) partitions 4`) + tk.MustExec(`create table tnormal(a int, b int, index idx_a(a))`) + + vals := make([]string, 0, 1000) + for i := 0; i < 1000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(50), rand.Intn(50))) + } + for _, tb := range []string{`trange`, `tnormal`, `thash`} { + sql := fmt.Sprintf(`insert into %v values `+strings.Join(vals, ", "), tb) + tk.MustExec(sql) + } + + randCond := func(col string) string { + la, ra := rand.Intn(50), rand.Intn(50) + if la > ra { + la, ra = ra, la + } + return fmt.Sprintf(`%v>=%v and %v<=%v`, col, la, col, ra) + } + + tk.MustExec(`begin`) + for i := 0; i < 1000; i++ { + if i == 0 || rand.Intn(2) == 0 { // insert some inflight rows + val := fmt.Sprintf("(%v, %v)", rand.Intn(50), rand.Intn(50)) + for _, tb := range []string{`trange`, `tnormal`, `thash`} { + sql := fmt.Sprintf(`insert into %v values `+val, tb) + tk.MustExec(sql) + } + } else { + var sql string + switch rand.Intn(3) { + case 0: // table scan + sql = `select * from %v ignore index(idx_a) where ` + randCond(`b`) + case 1: // index reader + sql = `select a from %v use index(idx_a) where ` + randCond(`a`) + case 2: // index lookup + sql = `select * from %v use index(idx_a) where ` + randCond(`a`) + ` and ` + randCond(`b`) + } + switch rand.Intn(2) { + case 0: // order by a + sql += ` order by a` + case 1: // order by b + sql += ` order by b` + } + + var result [][]interface{} + for _, tb := range []string{`trange`, `tnormal`, `thash`} { + q := fmt.Sprintf(sql, tb) + tk.MustHavePlan(q, `UnionScan`) + if result == nil { + result = tk.MustQuery(q).Sort().Rows() + } else { + tk.MustQuery(q).Sort().Check(result) + } + } + } + } + tk.MustExec(`rollback`) +} + +func TestIssue25030(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_issue_25030") + tk.MustExec("use test_issue_25030") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`CREATE TABLE tbl_936 ( + col_5410 smallint NOT NULL, + col_5411 double, + col_5412 boolean NOT NULL DEFAULT 1, + col_5413 set('Alice', 'Bob', 'Charlie', 'David') NOT NULL DEFAULT 'Charlie', + col_5414 varbinary(147) COLLATE 'binary' DEFAULT 'bvpKgYWLfyuTiOYSkj', + col_5415 timestamp NOT NULL DEFAULT '2021-07-06', + col_5416 decimal(6, 6) DEFAULT 0.49, + col_5417 text COLLATE utf8_bin, + col_5418 float DEFAULT 2048.0762299371554, + col_5419 int UNSIGNED NOT NULL DEFAULT 3152326370, + PRIMARY KEY (col_5419) ) + PARTITION BY HASH (col_5419) PARTITIONS 3`) + tk.MustQuery(`SELECT last_value(col_5414) OVER w FROM tbl_936 + WINDOW w AS (ORDER BY col_5410, col_5411, col_5412, col_5413, col_5414, col_5415, col_5416, col_5417, col_5418, col_5419) + ORDER BY col_5410, col_5411, col_5412, col_5413, col_5414, col_5415, col_5416, col_5417, col_5418, col_5419, nth_value(col_5412, 5) OVER w`). + Check(testkit.Rows()) // can work properly without any error or panic +} + +func TestUnsignedPartitionColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_unsigned_partition") + tk.MustExec("use test_unsigned_partition") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`create table thash_pk (a int unsigned, b int, primary key(a)) partition by hash (a) partitions 3`) + tk.MustExec(`create table trange_pk (a int unsigned, b int, primary key(a)) partition by range (a) ( + partition p1 values less than (100000), + partition p2 values less than (200000), + partition p3 values less than (300000), + partition p4 values less than (400000))`) + tk.MustExec(`create table tnormal_pk (a int unsigned, b int, primary key(a))`) + tk.MustExec(`create table thash_uniq (a int unsigned, b int, unique key(a)) partition by hash (a) partitions 3`) + tk.MustExec(`create table trange_uniq (a int unsigned, b int, unique key(a)) partition by range (a) ( + partition p1 values less than (100000), + partition p2 values less than (200000), + partition p3 values less than (300000), + partition p4 values less than (400000))`) + tk.MustExec(`create table tnormal_uniq (a int unsigned, b int, unique key(a))`) + + valColA := make(map[int]struct{}, 1000) + vals := make([]string, 0, 1000) + for len(vals) < 1000 { + a := rand.Intn(400000) + if _, ok := valColA[a]; ok { + continue + } + valColA[a] = struct{}{} + vals = append(vals, fmt.Sprintf("(%v, %v)", a, rand.Intn(400000))) + } + valStr := strings.Join(vals, ", ") + for _, tbl := range []string{"thash_pk", "trange_pk", "tnormal_pk", "thash_uniq", "trange_uniq", "tnormal_uniq"} { + tk.MustExec(fmt.Sprintf("insert into %v values %v", tbl, valStr)) + } + + for i := 0; i < 100; i++ { + scanCond := fmt.Sprintf("a %v %v", []string{">", "<"}[rand.Intn(2)], rand.Intn(400000)) + pointCond := fmt.Sprintf("a = %v", rand.Intn(400000)) + batchCond := fmt.Sprintf("a in (%v, %v, %v)", rand.Intn(400000), rand.Intn(400000), rand.Intn(400000)) + + var rScan, rPoint, rBatch [][]interface{} + for tid, tbl := range []string{"tnormal_pk", "trange_pk", "thash_pk"} { + // unsigned + TableReader + scanSQL := fmt.Sprintf("select * from %v use index(primary) where %v", tbl, scanCond) + tk.MustHavePlan(scanSQL, "TableReader") + r := tk.MustQuery(scanSQL).Sort() + if tid == 0 { + rScan = r.Rows() + } else { + r.Check(rScan) + } + + // unsigned + PointGet on PK + pointSQL := fmt.Sprintf("select * from %v use index(primary) where %v", tbl, pointCond) + tk.MustPointGet(pointSQL) + r = tk.MustQuery(pointSQL).Sort() + if tid == 0 { + rPoint = r.Rows() + } else { + r.Check(rPoint) + } + + // unsigned + BatchGet on PK + batchSQL := fmt.Sprintf("select * from %v where %v", tbl, batchCond) + tk.MustHavePlan(batchSQL, "Batch_Point_Get") + r = tk.MustQuery(batchSQL).Sort() + if tid == 0 { + rBatch = r.Rows() + } else { + r.Check(rBatch) + } + } + + lookupCond := fmt.Sprintf("a %v %v", []string{">", "<"}[rand.Intn(2)], rand.Intn(400000)) + var rLookup [][]interface{} + for tid, tbl := range []string{"tnormal_uniq", "trange_uniq", "thash_uniq"} { + // unsigned + IndexReader + scanSQL := fmt.Sprintf("select a from %v use index(a) where %v", tbl, scanCond) + tk.MustHavePlan(scanSQL, "IndexReader") + r := tk.MustQuery(scanSQL).Sort() + if tid == 0 { + rScan = r.Rows() + } else { + r.Check(rScan) + } + + // unsigned + IndexLookUp + lookupSQL := fmt.Sprintf("select * from %v use index(a) where %v", tbl, lookupCond) + tk.MustIndexLookup(lookupSQL) + r = tk.MustQuery(lookupSQL).Sort() + if tid == 0 { + rLookup = r.Rows() + } else { + r.Check(rLookup) + } + + // unsigned + PointGet on UniqueIndex + pointSQL := fmt.Sprintf("select * from %v use index(a) where %v", tbl, pointCond) + tk.MustPointGet(pointSQL) + r = tk.MustQuery(pointSQL).Sort() + if tid == 0 { + rPoint = r.Rows() + } else { + r.Check(rPoint) + } + + // unsigned + BatchGet on UniqueIndex + batchSQL := fmt.Sprintf("select * from %v where %v", tbl, batchCond) + tk.MustHavePlan(batchSQL, "Batch_Point_Get") + r = tk.MustQuery(batchSQL).Sort() + if tid == 0 { + rBatch = r.Rows() + } else { + r.Check(rBatch) + } + } + } +} + +func TestDirectReadingWithAgg(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_dr_agg") + tk.MustExec("use test_dr_agg") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // list partition table + tk.MustExec(`create table tlist(a int, b int, index idx_a(a), index idx_b(b)) partition by list(a)( + partition p0 values in (1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8), + partition p2 values in (9, 10, 11, 12));`) + + // range partition table + tk.MustExec(`create table trange(a int, b int, index idx_a(a), index idx_b(b)) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than (500), + partition p2 values less than(1100));`) + + // hash partition table + tk.MustExec(`create table thash(a int, b int) partition by hash(a) partitions 4;`) + + // regular table + tk.MustExec("create table tregular1(a int, b int, index idx_a(a))") + tk.MustExec("create table tregular2(a int, b int, index idx_a(a))") + + // generate some random data to be inserted + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000))) + } + + tk.MustExec("insert into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular1 values " + strings.Join(vals, ",")) + + vals = make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(12)+1, rand.Intn(20))) + } + + tk.MustExec("insert into tlist values " + strings.Join(vals, ",")) + tk.MustExec("insert into tregular2 values " + strings.Join(vals, ",")) + + // test range partition + for i := 0; i < 200; i++ { + // select /*+ stream_agg() */ a from t where a > ? group by a; + // select /*+ hash_agg() */ a from t where a > ? group by a; + // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; + // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; + x := rand.Intn(1099) + + queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from trange where a > %v group by a;", x) + queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from trange where a > %v group by a;", x) + queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + + y := rand.Intn(1099) + z := rand.Intn(1099) + + queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from trange where a in(%v, %v, %v) group by a;", x, y, z) + queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a in(%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) + + queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from trange where a in (%v, %v, %v) group by a;", x, y, z) + queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a in (%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) + } + + // test hash partition + for i := 0; i < 200; i++ { + // select /*+ stream_agg() */ a from t where a > ? group by a; + // select /*+ hash_agg() */ a from t where a > ? group by a; + // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; + // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; + x := rand.Intn(1099) + + queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from thash where a > %v group by a;", x) + queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from thash where a > %v group by a;", x) + queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + + y := rand.Intn(1099) + z := rand.Intn(1099) + + queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from thash where a in(%v, %v, %v) group by a;", x, y, z) + queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular1 where a in(%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) + + queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from thash where a in (%v, %v, %v) group by a;", x, y, z) + queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular1 where a in (%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) + } + + // test list partition + for i := 0; i < 200; i++ { + // select /*+ stream_agg() */ a from t where a > ? group by a; + // select /*+ hash_agg() */ a from t where a > ? group by a; + // select /*+ stream_agg() */ a from t where a in(?, ?, ?) group by a; + // select /*+ hash_agg() */ a from t where a in (?, ?, ?) group by a; + x := rand.Intn(12) + 1 + + queryPartition1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tlist where a > %v group by a;", x) + queryRegular1 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular2 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition1, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tlist where a > %v group by a;", x) + queryRegular2 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular2 where a > %v group by a;", x) + tk.MustHavePlan(queryPartition2, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + + y := rand.Intn(12) + 1 + z := rand.Intn(12) + 1 + + queryPartition3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tlist where a in(%v, %v, %v) group by a;", x, y, z) + queryRegular3 := fmt.Sprintf("select /*+ stream_agg() */ count(*), sum(b), max(b), a from tregular2 where a in(%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition3, "StreamAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition3).Sort().Check(tk.MustQuery(queryRegular3).Sort().Rows()) + + queryPartition4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tlist where a in (%v, %v, %v) group by a;", x, y, z) + queryRegular4 := fmt.Sprintf("select /*+ hash_agg() */ count(*), sum(b), max(b), a from tregular2 where a in (%v, %v, %v) group by a;", x, y, z) + tk.MustHavePlan(queryPartition4, "HashAgg") // check if IndexLookUp is used + tk.MustQuery(queryPartition4).Sort().Check(tk.MustQuery(queryRegular4).Sort().Rows()) + } +} + +func TestDynamicModeByDefault(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_dynamic_by_default") + + tk.MustExec(`create table trange(a int, b int, primary key(a) clustered, index idx_b(b)) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than(500), + partition p2 values less than(1100));`) + tk.MustExec(`create table thash(a int, b int, primary key(a) clustered, index idx_b(b)) partition by hash(a) partitions 4;`) + + for _, q := range []string{ + "explain select * from trange where a>400", + "explain select * from thash where a>=100", + } { + for _, r := range tk.MustQuery(q).Rows() { + require.NotContains(t, strings.ToLower(r[0].(string)), "partitionunion") + } + } +} + +func TestIssue24636(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_issue_24636") + tk.MustExec("use test_issue_24636") + + tk.MustExec(`CREATE TABLE t (a int, b date, c int, PRIMARY KEY (a,b)) + PARTITION BY RANGE ( TO_DAYS(b) ) ( + PARTITION p0 VALUES LESS THAN (737821), + PARTITION p1 VALUES LESS THAN (738289) + )`) + tk.MustExec(`INSERT INTO t (a, b, c) VALUES(0, '2021-05-05', 0)`) + tk.MustQuery(`select c from t use index(primary) where a=0 limit 1`).Check(testkit.Rows("0")) + + tk.MustExec(` + CREATE TABLE test_partition ( + a varchar(100) NOT NULL, + b date NOT NULL, + c varchar(100) NOT NULL, + d datetime DEFAULT NULL, + e datetime DEFAULT NULL, + f bigint(20) DEFAULT NULL, + g bigint(20) DEFAULT NULL, + h bigint(20) DEFAULT NULL, + i bigint(20) DEFAULT NULL, + j bigint(20) DEFAULT NULL, + k bigint(20) DEFAULT NULL, + l bigint(20) DEFAULT NULL, + PRIMARY KEY (a,b,c) /*T![clustered_index] NONCLUSTERED */ + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin + PARTITION BY RANGE ( TO_DAYS(b) ) ( + PARTITION pmin VALUES LESS THAN (737821), + PARTITION p20200601 VALUES LESS THAN (738289))`) + tk.MustExec(`INSERT INTO test_partition (a, b, c, d, e, f, g, h, i, j, k, l) VALUES('aaa', '2021-05-05', '428ff6a1-bb37-42ac-9883-33d7a29961e6', '2021-05-06 08:13:38', '2021-05-06 13:28:08', 0, 8, 3, 0, 9, 1, 0)`) + tk.MustQuery(`select c,j,l from test_partition where c='428ff6a1-bb37-42ac-9883-33d7a29961e6' and a='aaa' limit 0, 200`).Check(testkit.Rows("428ff6a1-bb37-42ac-9883-33d7a29961e6 9 0")) +} + +func TestIdexMerge(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_idx_merge") + tk.MustExec("use test_idx_merge") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + // list partition table + tk.MustExec(`create table tlist(a int, b int, primary key(a) clustered, index idx_b(b)) partition by list(a)( + partition p0 values in (1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8), + partition p2 values in (9, 10, 11, 12));`) + + // range partition table + tk.MustExec(`create table trange(a int, b int, primary key(a) clustered, index idx_b(b)) partition by range(a) ( + partition p0 values less than(300), + partition p1 values less than (500), + partition p2 values less than(1100));`) + + // hash partition table + tk.MustExec(`create table thash(a int, b int, primary key(a) clustered, index idx_b(b)) partition by hash(a) partitions 4;`) + + // regular table + tk.MustExec("create table tregular1(a int, b int, primary key(a) clustered)") + tk.MustExec("create table tregular2(a int, b int, primary key(a) clustered)") + + // generate some random data to be inserted + vals := make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(1100), rand.Intn(2000))) + } + + tk.MustExec("insert ignore into trange values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into thash values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into tregular1 values " + strings.Join(vals, ",")) + + vals = make([]string, 0, 2000) + for i := 0; i < 2000; i++ { + vals = append(vals, fmt.Sprintf("(%v, %v)", rand.Intn(12)+1, rand.Intn(20))) + } + + tk.MustExec("insert ignore into tlist values " + strings.Join(vals, ",")) + tk.MustExec("insert ignore into tregular2 values " + strings.Join(vals, ",")) + + // test range partition + for i := 0; i < 100; i++ { + x1 := rand.Intn(1099) + x2 := rand.Intn(1099) + + queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(trange) */ * from trange where a > %v or b < %v;", x1, x2) + queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b < %v;", x1, x2) + tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(trange) */ * from trange where a > %v or b > %v;", x1, x2) + queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b > %v;", x1, x2) + tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + } + + // test hash partition + for i := 0; i < 100; i++ { + x1 := rand.Intn(1099) + x2 := rand.Intn(1099) + + queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > %v or b < %v;", x1, x2) + queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregualr1) */ * from tregular1 where a > %v or b < %v;", x1, x2) + tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(thash) */ * from thash where a > %v or b > %v;", x1, x2) + queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular1) */ * from tregular1 where a > %v or b > %v;", x1, x2) + tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + } + + // test list partition + for i := 0; i < 100; i++ { + x1 := rand.Intn(12) + 1 + x2 := rand.Intn(12) + 1 + queryPartition1 := fmt.Sprintf("select /*+ use_index_merge(tlist) */ * from tlist where a > %v or b < %v;", x1, x2) + queryRegular1 := fmt.Sprintf("select /*+ use_index_merge(tregular2) */ * from tregular2 where a > %v or b < %v;", x1, x2) + tk.MustHavePlan(queryPartition1, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition1).Sort().Check(tk.MustQuery(queryRegular1).Sort().Rows()) + + queryPartition2 := fmt.Sprintf("select /*+ use_index_merge(tlist) */ * from tlist where a > %v or b > %v;", x1, x2) + queryRegular2 := fmt.Sprintf("select /*+ use_index_merge(tregular2) */ * from tregular2 where a > %v or b > %v;", x1, x2) + tk.MustHavePlan(queryPartition2, "IndexMerge") // check if IndexLookUp is used + tk.MustQuery(queryPartition2).Sort().Check(tk.MustQuery(queryRegular2).Sort().Rows()) + } +} + +func TestIssue25309(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create database test_issue_25309") + tk.MustExec("use test_issue_25309") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + tk.MustExec(`CREATE TABLE tbl_500 ( + col_20 tinyint(4) NOT NULL, + col_21 varchar(399) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + col_22 json DEFAULT NULL, + col_23 blob DEFAULT NULL, + col_24 mediumint(9) NOT NULL, + col_25 float NOT NULL DEFAULT '7306.384497585912', + col_26 binary(196) NOT NULL, + col_27 timestamp DEFAULT '1976-12-08 00:00:00', + col_28 bigint(20) NOT NULL, + col_29 tinyint(1) NOT NULL DEFAULT '1', + PRIMARY KEY (col_29,col_20) /*T![clustered_index] NONCLUSTERED */, + KEY idx_7 (col_28,col_20,col_26,col_27,col_21,col_24), + KEY idx_8 (col_25,col_29,col_24) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`) + + tk.MustExec(`CREATE TABLE tbl_600 ( + col_60 int(11) NOT NULL DEFAULT '-776833487', + col_61 tinyint(1) NOT NULL DEFAULT '1', + col_62 tinyint(4) NOT NULL DEFAULT '-125', + PRIMARY KEY (col_62,col_60,col_61) /*T![clustered_index] NONCLUSTERED */, + KEY idx_19 (col_60) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci + PARTITION BY HASH( col_60 ) + PARTITIONS 1`) + + tk.MustExec(`insert into tbl_500 select -34, 'lrfGPPPUuZjtT', '{"obj1": {"sub_obj0": 100}}', 0x6C47636D, 1325624, 7306.3843, 'abc', '1976-12-08', 4757891479624162031, 0`) + tk.MustQuery(`select tbl_5.* from tbl_500 tbl_5 where col_24 in ( select col_62 from tbl_600 where tbl_5.col_26 < 'hSvHLdQeGBNIyOFXStV' )`).Check(testkit.Rows()) +} + +func TestGlobalIndexScan(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") + tk.MustExec("analyze table p") + tk.MustQuery("select id from p use index (idx) order by id").Check(testkit.Rows("1", "3", "5", "7")) +} + +func TestAggWithGlobalIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") + tk.MustExec("analyze table p") + tk.MustQuery("select count(*) from p use index (idx)").Check(testkit.Rows("4")) + tk.MustQuery("select count(*) from p partition(p0) use index (idx)").Check(testkit.Rows("1")) +} + +func TestGlobalIndexDoubleRead(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") + tk.MustQuery("select * from p use index (idx)").Sort().Check(testkit.Rows("1 3", "3 4", "5 6", "7 9")) +} + +func TestDropGlobalIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + + failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkDropGlobalIndex", `return(true)`) + tk.MustExec("alter table p drop index idx") + failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkDropGlobalIndex") +} + +func TestIssue20028(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("set @@tidb_partition_prune_mode='static-only'") + tk.MustExec(`create table t1 (c_datetime datetime, primary key (c_datetime)) +partition by range (to_days(c_datetime)) ( partition p0 values less than (to_days('2020-02-01')), +partition p1 values less than (to_days('2020-04-01')), +partition p2 values less than (to_days('2020-06-01')), +partition p3 values less than maxvalue)`) + tk.MustExec("create table t2 (c_datetime datetime, unique key(c_datetime))") + tk.MustExec("insert into t1 values ('2020-06-26 03:24:00'), ('2020-02-21 07:15:33'), ('2020-04-27 13:50:58')") + tk.MustExec("insert into t2 values ('2020-01-10 09:36:00'), ('2020-02-04 06:00:00'), ('2020-06-12 03:45:18')") + tk.MustExec("begin") + tk.MustQuery("select * from t1 join t2 on t1.c_datetime >= t2.c_datetime for update"). + Sort(). + Check(testkit.Rows( + "2020-02-21 07:15:33 2020-01-10 09:36:00", + "2020-02-21 07:15:33 2020-02-04 06:00:00", + "2020-04-27 13:50:58 2020-01-10 09:36:00", + "2020-04-27 13:50:58 2020-02-04 06:00:00", + "2020-06-26 03:24:00 2020-01-10 09:36:00", + "2020-06-26 03:24:00 2020-02-04 06:00:00", + "2020-06-26 03:24:00 2020-06-12 03:45:18")) + tk.MustExec("rollback") +} + +func TestSelectLockOnPartitionTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists pt") + tk.MustExec(`create table pt (id int primary key, k int, c int, index(k)) +partition by range (id) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (11))`) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + optimisticTableReader := func() { + tk.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk.MustExec("begin") + tk.MustQuery("select id, k from pt ignore index (k) where k = 5 for update").Check(testkit.Rows("5 5")) + tk2.MustExec("update pt set c = c + 1 where k = 5") + _, err := tk.Exec("commit") + require.Error(t, err) // Write conflict + } + + optimisticIndexReader := func() { + tk.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk.MustExec("begin") + // This is not index reader actually. + tk.MustQuery("select k from pt where k = 5 for update").Check(testkit.Rows("5")) + tk2.MustExec("update pt set c = c + 1 where k = 5") + _, err := tk.Exec("commit") + require.Error(t, err) + } + + optimisticIndexLookUp := func() { + tk.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk.MustExec("begin") + tk.MustQuery("select c, k from pt use index (k) where k = 5 for update").Check(testkit.Rows("5 5")) + tk2.MustExec("update pt set c = c + 1 where k = 5") + _, err := tk.Exec("commit") + require.Error(t, err) + } + + pessimisticTableReader := func() { + tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk.MustExec("begin") + tk.MustQuery("select id, k from pt ignore index (k) where k = 5 for update").Check(testkit.Rows("5 5")) + ch := make(chan int, 2) + go func() { + tk2.MustExec("update pt set c = c + 1 where k = 5") + ch <- 1 + }() + time.Sleep(100 * time.Millisecond) + ch <- 2 + + // Check the operation in the goroutine is blocked, if not the first result in + // the channel should be 1. + require.Equal(t, 2, <-ch) + + tk.MustExec("commit") + <-ch + tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) + } + + pessimisticIndexReader := func() { + tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk.MustExec("begin") + // This is not index reader actually. + tk.MustQuery("select k from pt where k = 5 for update").Check(testkit.Rows("5")) + ch := make(chan int, 2) + go func() { + tk2.MustExec("update pt set c = c + 1 where k = 5") + ch <- 1 + }() + time.Sleep(100 * time.Millisecond) + ch <- 2 + + // Check the operation in the goroutine is blocked, + require.Equal(t, 2, <-ch) + + tk.MustExec("commit") + <-ch + tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) + } + + pessimisticIndexLookUp := func() { + tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk.MustExec("begin") + tk.MustQuery("select c, k from pt use index (k) where k = 5 for update").Check(testkit.Rows("5 5")) + ch := make(chan int, 2) + go func() { + tk2.MustExec("update pt set c = c + 1 where k = 5") + ch <- 1 + }() + time.Sleep(100 * time.Millisecond) + ch <- 2 + + // Check the operation in the goroutine is blocked, + require.Equal(t, 2, <-ch) + + tk.MustExec("commit") + <-ch + tk.MustQuery("select c from pt where k = 5").Check(testkit.Rows("6")) + } + + partitionModes := []string{ + "'dynamic'", + "'static'", + } + testCases := []func(){ + optimisticTableReader, + optimisticIndexLookUp, + optimisticIndexReader, + pessimisticTableReader, + pessimisticIndexReader, + pessimisticIndexLookUp, + } + + for _, mode := range partitionModes { + tk.MustExec("set @@tidb_partition_prune_mode=" + mode) + for _, c := range testCases { + tk.MustExec("replace into pt values (5, 5, 5)") + c() + } + } +} + +func TestIssue21731(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p, t") + tk.MustExec("set @@tidb_enable_list_partition = OFF") + // Notice that this does not really test the issue #21731 + tk.MustExec("create table t (a int, b int, unique index idx(a)) partition by list columns(b) (partition p0 values in (1), partition p1 values in (2));") +} + +type testOutput struct { + SQL string + Plan []string + Res []string +} + +func verifyPartitionResult(tk *testkit.TestKit, input []string, output []testOutput) { + for i, tt := range input { + var isSelect = false + if strings.HasPrefix(strings.ToLower(tt), "select ") { + isSelect = true + } + testdata.OnRecord(func() { + output[i].SQL = tt + if isSelect { + output[i].Plan = testdata.ConvertRowsToStrings(tk.UsedPartitions(tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + } else { + // Just verify SELECT (also avoid double INSERTs during record) + output[i].Res = nil + output[i].Plan = nil + } + }) + if isSelect { + tk.UsedPartitions(tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } else { + tk.MustExec(tt) + } + } +} + +func TestRangePartitionBoundariesEq(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("SET @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("CREATE DATABASE TestRangePartitionBoundaries") + defer tk.MustExec("DROP DATABASE TestRangePartitionBoundaries") + tk.MustExec("USE TestRangePartitionBoundaries") + tk.MustExec("DROP TABLE IF EXISTS t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1000000), + PARTITION p1 VALUES LESS THAN (2000000), + PARTITION p2 VALUES LESS THAN (3000000)); +`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestRangePartitionBoundariesNe(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("SET @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("CREATE DATABASE TestRangePartitionBoundariesNe") + defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesNe") + tk.MustExec("USE TestRangePartitionBoundariesNe") + tk.MustExec("DROP TABLE IF EXISTS t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1), + PARTITION p1 VALUES LESS THAN (2), + PARTITION p2 VALUES LESS THAN (3), + PARTITION p3 VALUES LESS THAN (4), + PARTITION p4 VALUES LESS THAN (5), + PARTITION p5 VALUES LESS THAN (6), + PARTITION p6 VALUES LESS THAN (7))`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestRangePartitionBoundariesBetweenM(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("CREATE DATABASE IF NOT EXISTS TestRangePartitionBoundariesBetweenM") + defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesBetweenM") + tk.MustExec("USE TestRangePartitionBoundariesBetweenM") + tk.MustExec("DROP TABLE IF EXISTS t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1000000), + PARTITION p1 VALUES LESS THAN (2000000), + PARTITION p2 VALUES LESS THAN (3000000))`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestRangePartitionBoundariesBetweenS(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("CREATE DATABASE IF NOT EXISTS TestRangePartitionBoundariesBetweenS") + defer tk.MustExec("DROP DATABASE TestRangePartitionBoundariesBetweenS") + tk.MustExec("USE TestRangePartitionBoundariesBetweenS") + tk.MustExec("DROP TABLE IF EXISTS t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1), + PARTITION p1 VALUES LESS THAN (2), + PARTITION p2 VALUES LESS THAN (3), + PARTITION p3 VALUES LESS THAN (4), + PARTITION p4 VALUES LESS THAN (5), + PARTITION p5 VALUES LESS THAN (6), + PARTITION p6 VALUES LESS THAN (7))`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestRangePartitionBoundariesLtM(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("create database TestRangePartitionBoundariesLtM") + defer tk.MustExec("drop database TestRangePartitionBoundariesLtM") + tk.MustExec("use TestRangePartitionBoundariesLtM") + tk.MustExec("drop table if exists t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1000000), + PARTITION p1 VALUES LESS THAN (2000000), + PARTITION p2 VALUES LESS THAN (3000000))`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestRangePartitionBoundariesLtS(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("create database TestRangePartitionBoundariesLtS") + defer tk.MustExec("drop database TestRangePartitionBoundariesLtS") + tk.MustExec("use TestRangePartitionBoundariesLtS") + tk.MustExec("drop table if exists t") + tk.MustExec(`CREATE TABLE t +(a INT, b varchar(255)) +PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (1), + PARTITION p1 VALUES LESS THAN (2), + PARTITION p2 VALUES LESS THAN (3), + PARTITION p3 VALUES LESS THAN (4), + PARTITION p4 VALUES LESS THAN (5), + PARTITION p5 VALUES LESS THAN (6), + PARTITION p6 VALUES LESS THAN (7))`) + + var input []string + var output []testOutput + executorSuiteData.LoadTestCases(t, &input, &output) + verifyPartitionResult(tk, input, output) +} + +func TestIssue25528(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec("use test") + tk.MustExec("create table issue25528 (id int primary key, balance DECIMAL(10, 2), balance2 DECIMAL(10, 2) GENERATED ALWAYS AS (-balance) VIRTUAL, created_at TIMESTAMP) PARTITION BY HASH(id) PARTITIONS 8") + tk.MustExec("insert into issue25528 (id, balance, created_at) values(1, 100, '2021-06-17 22:35:20')") + tk.MustExec("begin pessimistic") + tk.MustQuery("select * from issue25528 where id = 1 for update").Check(testkit.Rows("1 100.00 -100.00 2021-06-17 22:35:20")) + + tk.MustExec("drop table if exists issue25528") + tk.MustExec("CREATE TABLE `issue25528` ( `c1` int(11) NOT NULL, `c2` int(11) DEFAULT NULL, `c3` int(11) DEFAULT NULL, `c4` int(11) DEFAULT NULL, PRIMARY KEY (`c1`) /*T![clustered_index] CLUSTERED */, KEY `k2` (`c2`), KEY `k3` (`c3`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY HASH( `c1` ) PARTITIONS 10;") + tk.MustExec("INSERT INTO issue25528 (`c1`, `c2`, `c3`, `c4`) VALUES (1, 1, 1, 1) , (3, 3, 3, 3) , (2, 2, 2, 2) , (4, 4, 4, 4);") + tk.MustQuery("select * from issue25528 where c1 in (3, 4) order by c2 for update;").Check(testkit.Rows("3 3 3 3", "4 4 4 4")) +} + +func TestIssue26251(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1 := testkit.NewTestKit(t, store) + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + tk1.MustExec("use test") + tk1.MustExec("create table tp (id int primary key) partition by range (id) (partition p0 values less than (100));") + tk1.MustExec("create table tn (id int primary key);") + tk1.MustExec("insert into tp values(1),(2);") + tk1.MustExec("insert into tn values(1),(2);") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + tk1.MustExec("begin pessimistic") + tk1.MustQuery("select * from tp,tn where tp.id=tn.id and tn.id<=1 for update;").Check(testkit.Rows("1 1")) + + ch := make(chan struct{}, 1) + tk2.MustExec("begin pessimistic") + go func() { + // This query should block. + tk2.MustQuery("select * from tn where id=1 for update;").Check(testkit.Rows("1")) + ch <- struct{}{} + }() + + select { + case <-time.After(100 * time.Millisecond): + // Expected, query blocked, not finish within 100ms. + tk1.MustExec("rollback") + case <-ch: + // Unexpected, test fail. + t.Fail() + } + + // Clean up + <-ch + tk2.MustExec("rollback") +} + +func TestLeftJoinForUpdate(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("create database TestLeftJoinForUpdate") + defer tk1.MustExec("drop database TestLeftJoinForUpdate") + tk1.MustExec("use TestLeftJoinForUpdate") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use TestLeftJoinForUpdate") + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec("use TestLeftJoinForUpdate") + + tk1.MustExec("drop table if exists nt, pt") + tk1.MustExec("create table nt (id int, col varchar(32), primary key (id))") + tk1.MustExec("create table pt (id int, col varchar(32), primary key (id)) partition by hash(id) partitions 4") + + resetData := func() { + tk1.MustExec("truncate table nt") + tk1.MustExec("truncate table pt") + tk1.MustExec("insert into nt values (1, 'hello')") + tk1.MustExec("insert into pt values (2, 'test')") + } + + // ========================== First round of test ================== + // partition table left join normal table. + // ================================================================= + resetData() + ch := make(chan int, 10) + tk1.MustExec("begin pessimistic") + // No union scan + tk1.MustQuery("select * from pt left join nt on pt.id = nt.id for update").Check(testkit.Rows("2 test ")) + go func() { + // Check the key is locked. + tk2.MustExec("update pt set col = 'xxx' where id = 2") + ch <- 2 + }() + + // Union scan + tk1.MustExec("insert into pt values (1, 'world')") + tk1.MustQuery("select * from pt left join nt on pt.id = nt.id for update").Sort().Check(testkit.Rows("1 world 1 hello", "2 test ")) + go func() { + // Check the key is locked. + tk3.MustExec("update nt set col = 'yyy' where id = 1") + ch <- 3 + }() + + // Give chance for the goroutines to run first. + time.Sleep(80 * time.Millisecond) + ch <- 1 + tk1.MustExec("rollback") + + checkOrder := func() { + require.Equal(t, <-ch, 1) + v1 := <-ch + v2 := <-ch + require.True(t, (v1 == 2 && v2 == 3) || (v1 == 3 && v2 == 2)) + } + checkOrder() + + // ========================== Another round of test ================== + // normal table left join partition table. + // =================================================================== + resetData() + tk1.MustExec("begin pessimistic") + // No union scan + tk1.MustQuery("select * from nt left join pt on pt.id = nt.id for update").Check(testkit.Rows("1 hello ")) + + // Union scan + tk1.MustExec("insert into pt values (1, 'world')") + tk1.MustQuery("select * from nt left join pt on pt.id = nt.id for update").Check(testkit.Rows("1 hello 1 world")) + go func() { + tk2.MustExec("replace into pt values (1, 'aaa')") + ch <- 2 + }() + go func() { + tk3.MustExec("update nt set col = 'bbb' where id = 1") + ch <- 3 + }() + time.Sleep(80 * time.Millisecond) + ch <- 1 + tk1.MustExec("rollback") + checkOrder() +} + +func TestIssue31024(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("create database TestIssue31024") + defer tk1.MustExec("drop database TestIssue31024") + tk1.MustExec("use TestIssue31024") + tk1.MustExec("create table t1 (c_datetime datetime, c1 int, c2 int, primary key (c_datetime), key(c1), key(c2))" + + " partition by range (to_days(c_datetime)) " + + "( partition p0 values less than (to_days('2020-02-01'))," + + " partition p1 values less than (to_days('2020-04-01'))," + + " partition p2 values less than (to_days('2020-06-01'))," + + " partition p3 values less than maxvalue)") + tk1.MustExec("create table t2 (c_datetime datetime, unique key(c_datetime))") + tk1.MustExec("insert into t1 values ('2020-06-26 03:24:00', 1, 1), ('2020-02-21 07:15:33', 2, 2), ('2020-04-27 13:50:58', 3, 3)") + tk1.MustExec("insert into t2 values ('2020-01-10 09:36:00'), ('2020-02-04 06:00:00'), ('2020-06-12 03:45:18')") + tk1.MustExec("SET GLOBAL tidb_txn_mode = 'pessimistic'") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use TestIssue31024") + + ch := make(chan int, 10) + tk1.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk1.MustExec("begin pessimistic") + tk1.MustQuery("select /*+ use_index_merge(t1) */ * from t1 join t2 on t1.c_datetime >= t2.c_datetime where t1.c1 < 10 or t1.c2 < 10 for update") + + go func() { + // Check the key is locked. + tk2.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk2.MustExec("begin pessimistic") + tk2.MustExec("update t1 set c_datetime = '2020-06-26 03:24:00' where c1 = 1") + ch <- 2 + }() + + // Give chance for the goroutines to run first. + time.Sleep(80 * time.Millisecond) + ch <- 1 + tk1.MustExec("rollback") + + require.Equal(t, <-ch, 1) + require.Equal(t, <-ch, 2) + + tk2.MustExec("rollback") +} + +func TestIssue27346(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("create database TestIssue27346") + defer tk1.MustExec("drop database TestIssue27346") + tk1.MustExec("use TestIssue27346") + + tk1.MustExec("set @@tidb_enable_index_merge=1,@@tidb_partition_prune_mode='dynamic'") + + tk1.MustExec("DROP TABLE IF EXISTS `tbl_18`") + tk1.MustExec("CREATE TABLE `tbl_18` (`col_119` binary(16) NOT NULL DEFAULT 'skPoKiwYUi',`col_120` int(10) unsigned NOT NULL,`col_121` timestamp NOT NULL,`col_122` double NOT NULL DEFAULT '3937.1887880628115',`col_123` bigint(20) NOT NULL DEFAULT '3550098074891542725',PRIMARY KEY (`col_123`,`col_121`,`col_122`,`col_120`) CLUSTERED,UNIQUE KEY `idx_103` (`col_123`,`col_119`,`col_120`),UNIQUE KEY `idx_104` (`col_122`,`col_120`),UNIQUE KEY `idx_105` (`col_119`,`col_120`),KEY `idx_106` (`col_121`,`col_120`,`col_122`,`col_119`),KEY `idx_107` (`col_121`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci PARTITION BY HASH( `col_120` ) PARTITIONS 3") + tk1.MustExec("INSERT INTO tbl_18 (`col_119`, `col_120`, `col_121`, `col_122`, `col_123`) VALUES (X'736b506f4b6977595569000000000000', 672436701, '1974-02-24 00:00:00', 3937.1887880628115e0, -7373106839136381229), (X'736b506f4b6977595569000000000000', 2637316689, '1993-10-29 00:00:00', 3937.1887880628115e0, -4522626077860026631), (X'736b506f4b6977595569000000000000', 831809724, '1995-11-20 00:00:00', 3937.1887880628115e0, -4426441253940231780), (X'736b506f4b6977595569000000000000', 1588592628, '2001-03-28 00:00:00', 3937.1887880628115e0, 1329207475772244999), (X'736b506f4b6977595569000000000000', 3908038471, '2031-06-06 00:00:00', 3937.1887880628115e0, -6562815696723135786), (X'736b506f4b6977595569000000000000', 1674237178, '2001-10-24 00:00:00', 3937.1887880628115e0, -6459065549188938772), (X'736b506f4b6977595569000000000000', 3507075493, '2010-03-25 00:00:00', 3937.1887880628115e0, -4329597025765326929), (X'736b506f4b6977595569000000000000', 1276461709, '2019-07-20 00:00:00', 3937.1887880628115e0, 3550098074891542725)") + + tk1.MustQuery("select col_120,col_122,col_123 from tbl_18 where tbl_18.col_122 = 4763.320888074281 and not( tbl_18.col_121 in ( '2032-11-01' , '1975-05-21' , '1994-05-16' , '1984-01-15' ) ) or not( tbl_18.col_121 >= '2008-10-24' ) order by tbl_18.col_119,tbl_18.col_120,tbl_18.col_121,tbl_18.col_122,tbl_18.col_123 limit 919 for update").Sort().Check(testkit.Rows( + "1588592628 3937.1887880628115 1329207475772244999", + "1674237178 3937.1887880628115 -6459065549188938772", + "2637316689 3937.1887880628115 -4522626077860026631", + "672436701 3937.1887880628115 -7373106839136381229", + "831809724 3937.1887880628115 -4426441253940231780")) + tk1.MustQuery("select /*+ use_index_merge( tbl_18 ) */ col_120,col_122,col_123 from tbl_18 where tbl_18.col_122 = 4763.320888074281 and not( tbl_18.col_121 in ( '2032-11-01' , '1975-05-21' , '1994-05-16' , '1984-01-15' ) ) or not( tbl_18.col_121 >= '2008-10-24' ) order by tbl_18.col_119,tbl_18.col_120,tbl_18.col_121,tbl_18.col_122,tbl_18.col_123 limit 919 for update").Sort().Check(testkit.Rows( + "1588592628 3937.1887880628115 1329207475772244999", + "1674237178 3937.1887880628115 -6459065549188938772", + "2637316689 3937.1887880628115 -4522626077860026631", + "672436701 3937.1887880628115 -7373106839136381229", + "831809724 3937.1887880628115 -4426441253940231780")) +} + +func TestIssue35181(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database TestIssue35181") + tk.MustExec("use TestIssue35181") + tk.MustExec("CREATE TABLE `t` (`a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (2021), PARTITION `p1` VALUES LESS THAN (3000))") + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec(`insert into t select * from t where a=3000`) + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec(`insert into t select * from t where a=3000`) +} + +func TestIssue21732(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database TestIssue21732") + tk.MustExec("use TestIssue21732") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (a int, b int GENERATED ALWAYS AS (3*a-2*a) VIRTUAL) partition by hash(b) partitions 2;`) + tk.MustExec("alter table p add unique index idx (a);") + tk.MustExec("insert into p (a) values (1),(2),(3);") + tk.MustQuery("select * from p use index (idx)").Sort().Check(testkit.Rows("1 1", "2 2", "3 3")) + tk.MustExec("drop database TestIssue21732") +} + +func TestGlobalIndexSelectSpecifiedPartition(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") + tk.MustQuery("select * from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1 3")) +} + +func TestGlobalIndexScanSpecifiedPartition(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3), (3,4), (5,6), (7,9)") + tk.MustExec("analyze table p") + tk.MustQuery("select id from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1")) +} + +func TestGlobalIndexScanForClusteredSpecifiedPartition(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table p (id int, c int, d int, e int, primary key(d, c) clustered) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table p add unique idx(id)") + tk.MustExec("insert into p values (1,3,1,1), (3,4,3,3), (5,6,5,5), (7,9,7,7)") + tk.MustExec("analyze table p") + tk.MustQuery("select id from p partition(p0) use index (idx)").Sort().Check(testkit.Rows("1")) +} + +func TestGlobalIndexJoin(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table t1 (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table t1 add unique idx(id)") + tk.MustExec("insert into t1 values (1,3), (3,4), (5,6), (7,9)") + + tk.MustExec(`create table t2 (id int, c int)`) + tk.MustExec("insert into t2 values (1, 3)") + + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 3")) + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) +} + +func TestGlobalIndexJoinSpecifiedPartition(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table t1 (id int, c int) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table t1 add unique idx(id)") + tk.MustExec("insert into t1 values (1,3), (3,4), (5,6), (7,9)") + + tk.MustExec(`create table t2 (id int, c int)`) + tk.MustExec("insert into t2 values (1, 3)") + + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 3")) + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) +} + +func TestGlobalIndexJoinForClusteredSpecifiedPartition(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists p") + tk.MustExec(`create table t1 (id int, c int, d int, e int, primary key(d, c) clustered) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + tk.MustExec("alter table t1 add unique idx(id)") + tk.MustExec("insert into t1 values (1,3,1,1), (3,4,3,3), (5,6,5,5), (7,9,7,7)") + + tk.MustExec(`create table t2 (id int, c int)`) + tk.MustExec("insert into t2 values (1, 3)") + + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ * from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1 3 1 1 1 3")) + tk.MustQuery("select /*+ INL_JOIN(t1, t2) */ t1.id from t1 partition(p0) inner join t2 on t1.id = t2.id").Sort().Check(testkit.Rows("1")) +} + +func TestGlobalIndexForIssue40149(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + for _, opt := range []string{"true", "false"} { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(`+opt+`)`) + tk.MustExec("use test") + tk.MustExec("drop table if exists test_t1") + tk.MustExec(`CREATE TABLE test_t1 ( + a int(11) NOT NULL, + b int(11) DEFAULT NULL, + c int(11) DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY RANGE (c) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (MAXVALUE));`) + tk.MustExec("alter table test_t1 add unique p_a (a);") + tk.MustExec("insert into test_t1 values (1,1,1);") + tk.MustQuery("select * from test_t1 where a = 1;").Sort().Check(testkit.Rows("1 1 1")) + failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + } +} + +func TestGlobalIndexMerge(t *testing.T) { + restoreConfig := config.RestoreFunc() + defer restoreConfig() + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableGlobalIndex = true + }) + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("SET session tidb_enable_index_merge = ON;") + tk.MustExec("drop table if exists t") + tk.MustExec(`CREATE TABLE t ( + a int(11) NOT NULL, + b int(11) DEFAULT NULL, + c int(11) DEFAULT NULL, + d int(11) NOT NULL AUTO_INCREMENT, + KEY idx_bd (b, c), + UNIQUE KEY uidx_ac(a) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin PARTITION BY RANGE (c) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (MAXVALUE));`) + tk.MustExec("insert into t values (1,1,1,1),(2,2,2,2),(3,3,3,3),(4,4,4,4),(5,5,5,5),(6,6,6,6),(7,7,7,7),(8,8,8,8);") + tk.MustExec("analyze table t") + // when index_merge has global index as its partial path, ignore it. + require.False(t, tk.MustUseIndex("select /*+ use_index_merge(t, uidx_ac, idx_bc) */ * from t where a=1 or b=2", "uidx_ac")) + tk.MustQuery("select /*+ use_index_merge(t, uidx_ac, idx_bc) */ * from t where a=1 or b=2").Sort().Check( + testkit.Rows("1 1 1 1", "2 2 2 2")) +} + +func TestIssue39999(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec(`create schema test39999`) + tk.MustExec(`use test39999`) + tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) + tk.MustExec(`drop table if exists c, t`) + tk.MustExec("CREATE TABLE `c` (" + + "`serial_id` varchar(24)," + + "`occur_trade_date` date," + + "`txt_account_id` varchar(24)," + + "`capital_sub_class` varchar(10)," + + "`occur_amount` decimal(16,2)," + + "`broker` varchar(10)," + + "PRIMARY KEY (`txt_account_id`,`occur_trade_date`,`serial_id`) /*T![clustered_index] CLUSTERED */," + + "KEY `idx_serial_id` (`serial_id`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci " + + "PARTITION BY RANGE COLUMNS(`serial_id`) (" + + "PARTITION `p202209` VALUES LESS THAN ('20221001')," + + "PARTITION `p202210` VALUES LESS THAN ('20221101')," + + "PARTITION `p202211` VALUES LESS THAN ('20221201')" + + ")") + + tk.MustExec("CREATE TABLE `t` ( " + + "`txn_account_id` varchar(24), " + + "`account_id` varchar(32), " + + "`broker` varchar(10), " + + "PRIMARY KEY (`txn_account_id`) /*T![clustered_index] CLUSTERED */ " + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci") + + tk.MustExec("INSERT INTO `c` (serial_id, txt_account_id, capital_sub_class, occur_trade_date, occur_amount, broker) VALUES ('2022111700196920','04482786','CUST','2022-11-17',-2.01,'0009')") + tk.MustExec("INSERT INTO `t` VALUES ('04482786','1142927','0009')") + + tk.MustExec(`set tidb_partition_prune_mode='dynamic'`) + tk.MustExec(`analyze table c`) + tk.MustExec(`analyze table t`) + query := `select + /*+ inl_join(c) */ + c.occur_amount +from + c + join t on c.txt_account_id = t.txn_account_id + and t.broker = '0009' + and c.occur_trade_date = '2022-11-17'` + tk.MustQuery("explain " + query).Check(testkit.Rows(""+ + "IndexJoin_22 1.00 root inner join, inner:TableReader_21, outer key:test39999.t.txn_account_id, inner key:test39999.c.txt_account_id, equal cond:eq(test39999.t.txn_account_id, test39999.c.txt_account_id)", + "├─TableReader_27(Build) 1.00 root data:Selection_26", + "│ └─Selection_26 1.00 cop[tikv] eq(test39999.t.broker, \"0009\")", + "│ └─TableFullScan_25 1.00 cop[tikv] table:t keep order:false", + "└─TableReader_21(Probe) 1.00 root partition:all data:Selection_20", + " └─Selection_20 1.00 cop[tikv] eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)", + " └─TableRangeScan_19 1.00 cop[tikv] table:c range: decided by [eq(test39999.c.txt_account_id, test39999.t.txn_account_id) eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)], keep order:false")) + tk.MustQuery(query).Check(testkit.Rows("-2.01")) + + // Add the missing partition key part. + tk.MustExec(`alter table t add column serial_id varchar(24) default '2022111700196920'`) + query += ` and c.serial_id = t.serial_id` + tk.MustQuery(query).Check(testkit.Rows("-2.01")) + tk.MustQuery("explain " + query).Check(testkit.Rows(""+ + `IndexJoin_20 0.80 root inner join, inner:TableReader_19, outer key:test39999.t.txn_account_id, test39999.t.serial_id, inner key:test39999.c.txt_account_id, test39999.c.serial_id, equal cond:eq(test39999.t.serial_id, test39999.c.serial_id), eq(test39999.t.txn_account_id, test39999.c.txt_account_id)`, + `├─TableReader_25(Build) 0.80 root data:Selection_24`, + `│ └─Selection_24 0.80 cop[tikv] eq(test39999.t.broker, "0009"), not(isnull(test39999.t.serial_id))`, + `│ └─TableFullScan_23 1.00 cop[tikv] table:t keep order:false`, + `└─TableReader_19(Probe) 0.80 root partition:all data:Selection_18`, + ` └─Selection_18 0.80 cop[tikv] eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)`, + ` └─TableRangeScan_17 0.80 cop[tikv] table:c range: decided by [eq(test39999.c.txt_account_id, test39999.t.txn_account_id) eq(test39999.c.serial_id, test39999.t.serial_id) eq(test39999.c.occur_trade_date, 2022-11-17 00:00:00.000000)], keep order:false`)) +} diff --git a/executor/pipelined_window.go b/pkg/executor/pipelined_window.go similarity index 96% rename from executor/pipelined_window.go rename to pkg/executor/pipelined_window.go index 020ad2bec11a3..519762892b6b3 100644 --- a/executor/pipelined_window.go +++ b/pkg/executor/pipelined_window.go @@ -18,15 +18,15 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) type dataInfo struct { diff --git a/executor/pkg_test.go b/pkg/executor/pkg_test.go similarity index 91% rename from executor/pkg_test.go rename to pkg/executor/pkg_test.go index 5325ab99f17a6..05a20d6e43814 100644 --- a/executor/pkg_test.go +++ b/pkg/executor/pkg_test.go @@ -19,14 +19,14 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/plan_replayer.go b/pkg/executor/plan_replayer.go new file mode 100644 index 0000000000000..47cf477855afc --- /dev/null +++ b/pkg/executor/plan_replayer.go @@ -0,0 +1,531 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "archive/zip" + "bytes" + "context" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/BurntSushi/toml" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +var _ exec.Executor = &PlanReplayerExec{} +var _ exec.Executor = &PlanReplayerLoadExec{} + +// PlanReplayerExec represents a plan replayer executor. +type PlanReplayerExec struct { + exec.BaseExecutor + CaptureInfo *PlanReplayerCaptureInfo + DumpInfo *PlanReplayerDumpInfo + endFlag bool +} + +// PlanReplayerCaptureInfo indicates capture info +type PlanReplayerCaptureInfo struct { + SQLDigest string + PlanDigest string + Remove bool +} + +// PlanReplayerDumpInfo indicates dump info +type PlanReplayerDumpInfo struct { + ExecStmts []ast.StmtNode + Analyze bool + HistoricalStatsTS uint64 + StartTS uint64 + Path string + File *os.File + FileName string + ctx sessionctx.Context +} + +// Next implements the Executor Next interface. +func (e *PlanReplayerExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if e.endFlag { + return nil + } + if e.CaptureInfo != nil { + if e.CaptureInfo.Remove { + return e.removeCaptureTask(ctx) + } + return e.registerCaptureTask(ctx) + } + err := e.createFile() + if err != nil { + return err + } + // Note: + // For the dumping for SQL file case (len(e.DumpInfo.Path) > 0), the DumpInfo.dump() is called in + // handleFileTransInConn(), which is after TxnManager.OnTxnEnd(), where we can't access the TxnManager anymore. + // So we must fetch the startTS now. + startTS, err := sessiontxn.GetTxnManager(e.Ctx()).GetStmtReadTS() + if err != nil { + return err + } + e.DumpInfo.StartTS = startTS + if len(e.DumpInfo.Path) > 0 { + err = e.prepare() + if err != nil { + return err + } + // As we can only read file from handleSpecialQuery, thus we store the file token in the session var during `dump` + // and return nil here. + e.endFlag = true + return nil + } + if e.DumpInfo.ExecStmts == nil { + return errors.New("plan replayer: sql is empty") + } + err = e.DumpInfo.dump(ctx) + if err != nil { + return err + } + req.AppendString(0, e.DumpInfo.FileName) + e.endFlag = true + return nil +} + +func (e *PlanReplayerExec) removeCaptureTask(ctx context.Context) error { + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) + _, _, err := exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("delete from mysql.plan_replayer_task where sql_digest = '%s' and plan_digest = '%s'", + e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest)) + if err != nil { + logutil.BgLogger().Warn("remove mysql.plan_replayer_status record failed", + zap.Error(err)) + return err + } + err = domain.GetDomain(e.Ctx()).GetPlanReplayerHandle().CollectPlanReplayerTask() + if err != nil { + logutil.BgLogger().Warn("collect task failed", zap.Error(err)) + } + logutil.BgLogger().Info("collect plan replayer task success") + e.endFlag = true + return nil +} + +func (e *PlanReplayerExec) registerCaptureTask(ctx context.Context) error { + ctx1 := kv.WithInternalSourceType(ctx, kv.InternalTxnStats) + exists, err := domain.CheckPlanReplayerTaskExists(ctx1, e.Ctx(), e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest) + if err != nil { + return err + } + if exists { + return errors.New("plan replayer capture task already exists") + } + exec := e.Ctx().(sqlexec.RestrictedSQLExecutor) + _, _, err = exec.ExecRestrictedSQL(ctx1, nil, fmt.Sprintf("insert into mysql.plan_replayer_task (sql_digest, plan_digest) values ('%s','%s')", + e.CaptureInfo.SQLDigest, e.CaptureInfo.PlanDigest)) + if err != nil { + logutil.BgLogger().Warn("insert mysql.plan_replayer_status record failed", + zap.Error(err)) + return err + } + err = domain.GetDomain(e.Ctx()).GetPlanReplayerHandle().CollectPlanReplayerTask() + if err != nil { + logutil.BgLogger().Warn("collect task failed", zap.Error(err)) + } + logutil.BgLogger().Info("collect plan replayer task success") + e.endFlag = true + return nil +} + +func (e *PlanReplayerExec) createFile() error { + var err error + e.DumpInfo.File, e.DumpInfo.FileName, err = replayer.GeneratePlanReplayerFile(false, false, false) + if err != nil { + return err + } + return nil +} + +func (e *PlanReplayerDumpInfo) dump(ctx context.Context) (err error) { + fileName := e.FileName + zf := e.File + task := &domain.PlanReplayerDumpTask{ + StartTS: e.StartTS, + FileName: fileName, + Zf: zf, + SessionVars: e.ctx.GetSessionVars(), + TblStats: nil, + ExecStmts: e.ExecStmts, + Analyze: e.Analyze, + HistoricalStatsTS: e.HistoricalStatsTS, + } + err = domain.DumpPlanReplayerInfo(ctx, e.ctx, task) + if err != nil { + return err + } + e.ctx.GetSessionVars().LastPlanReplayerToken = e.FileName + return nil +} + +func (e *PlanReplayerExec) prepare() error { + val := e.Ctx().Value(PlanReplayerDumpVarKey) + if val != nil { + e.Ctx().SetValue(PlanReplayerDumpVarKey, nil) + return errors.New("plan replayer: previous plan replayer dump option isn't closed normally, please try again") + } + e.Ctx().SetValue(PlanReplayerDumpVarKey, e.DumpInfo) + return nil +} + +// DumpSQLsFromFile dumps plan replayer results for sqls from file +func (e *PlanReplayerDumpInfo) DumpSQLsFromFile(ctx context.Context, b []byte) error { + sqls := strings.Split(string(b), ";") + e.ExecStmts = make([]ast.StmtNode, 0) + for _, sql := range sqls { + s := strings.Trim(sql, "\n") + if len(s) < 1 { + continue + } + node, err := e.ctx.(sqlexec.RestrictedSQLExecutor).ParseWithParams(ctx, s) + if err != nil { + return fmt.Errorf("parse sql error, sql:%v, err:%v", s, err) + } + e.ExecStmts = append(e.ExecStmts, node) + } + return e.dump(ctx) +} + +// PlanReplayerLoadExec represents a plan replayer load executor. +type PlanReplayerLoadExec struct { + exec.BaseExecutor + info *PlanReplayerLoadInfo +} + +// PlanReplayerLoadInfo contains file path and session context. +type PlanReplayerLoadInfo struct { + Path string + Ctx sessionctx.Context +} + +type planReplayerDumpKeyType int + +func (planReplayerDumpKeyType) String() string { + return "plan_replayer_dump_var" +} + +type planReplayerLoadKeyType int + +func (planReplayerLoadKeyType) String() string { + return "plan_replayer_load_var" +} + +// PlanReplayerLoadVarKey is a variable key for plan replayer load. +const PlanReplayerLoadVarKey planReplayerLoadKeyType = 0 + +// PlanReplayerDumpVarKey is a variable key for plan replayer dump. +const PlanReplayerDumpVarKey planReplayerDumpKeyType = 1 + +// Next implements the Executor Next interface. +func (e *PlanReplayerLoadExec) Next(_ context.Context, req *chunk.Chunk) error { + req.GrowAndReset(e.MaxChunkSize()) + if len(e.info.Path) == 0 { + return errors.New("plan replayer: file path is empty") + } + val := e.Ctx().Value(PlanReplayerLoadVarKey) + if val != nil { + e.Ctx().SetValue(PlanReplayerLoadVarKey, nil) + return errors.New("plan replayer: previous plan replayer load option isn't closed normally, please try again") + } + e.Ctx().SetValue(PlanReplayerLoadVarKey, e.info) + return nil +} + +func loadSetTiFlashReplica(ctx sessionctx.Context, z *zip.Reader) error { + for _, zipFile := range z.File { + if strings.Compare(zipFile.Name, domain.PlanReplayerTiFlashReplicasFile) == 0 { + v, err := zipFile.Open() + if err != nil { + return errors.AddStack(err) + } + //nolint: errcheck,all_revive,revive + defer v.Close() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(v) + if err != nil { + return errors.AddStack(err) + } + rows := strings.Split(buf.String(), "\n") + for _, row := range rows { + if len(row) < 1 { + continue + } + r := strings.Split(row, "\t") + if len(r) < 3 { + logutil.BgLogger().Debug("plan replayer: skip error", + zap.Error(errors.New("setting tiflash replicas failed"))) + continue + } + dbName := r[0] + tableName := r[1] + c := context.Background() + // Though we record tiflash replica in txt, we only set 1 tiflash replica as it's enough for reproduce the plan + sql := fmt.Sprintf("alter table %s.%s set tiflash replica 1", dbName, tableName) + _, err = ctx.(sqlexec.SQLExecutor).Execute(c, sql) + logutil.BgLogger().Debug("plan replayer: skip error", zap.Error(err)) + } + } + } + return nil +} + +func loadAllBindings(ctx sessionctx.Context, z *zip.Reader) error { + for _, f := range z.File { + if strings.Compare(f.Name, domain.PlanReplayerSessionBindingFile) == 0 { + err := loadBindings(ctx, f, true) + if err != nil { + return err + } + } else if strings.Compare(f.Name, domain.PlanReplayerGlobalBindingFile) == 0 { + err := loadBindings(ctx, f, false) + if err != nil { + return err + } + } + } + return nil +} + +func loadBindings(ctx sessionctx.Context, f *zip.File, isSession bool) error { + r, err := f.Open() + if err != nil { + return errors.AddStack(err) + } + //nolint: errcheck + defer r.Close() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + if err != nil { + return errors.AddStack(err) + } + if len(buf.String()) < 1 { + return nil + } + bindings := strings.Split(buf.String(), "\n") + for _, binding := range bindings { + cols := strings.Split(binding, "\t") + if len(cols) < 3 { + continue + } + originSQL := cols[0] + bindingSQL := cols[1] + enabled := cols[3] + if strings.Compare(enabled, "enabled") == 0 { + sql := fmt.Sprintf("CREATE %s BINDING FOR %s USING %s", func() string { + if isSession { + return "SESSION" + } + return "GLOBAL" + }(), originSQL, bindingSQL) + c := context.Background() + _, err = ctx.(sqlexec.SQLExecutor).Execute(c, sql) + if err != nil { + return err + } + } + } + return nil +} + +func loadVariables(ctx sessionctx.Context, z *zip.Reader) error { + unLoadVars := make([]string, 0) + for _, zipFile := range z.File { + if strings.Compare(zipFile.Name, domain.PlanReplayerVariablesFile) == 0 { + varMap := make(map[string]string) + v, err := zipFile.Open() + if err != nil { + return errors.AddStack(err) + } + //nolint: errcheck,all_revive,revive + defer v.Close() + _, err = toml.NewDecoder(v).Decode(&varMap) + if err != nil { + return errors.AddStack(err) + } + vars := ctx.GetSessionVars() + for name, value := range varMap { + sysVar := variable.GetSysVar(name) + if sysVar == nil { + unLoadVars = append(unLoadVars, name) + logutil.BgLogger().Warn(fmt.Sprintf("skip set variable %s:%s", name, value), zap.Error(err)) + continue + } + sVal, err := sysVar.Validate(vars, value, variable.ScopeSession) + if err != nil { + unLoadVars = append(unLoadVars, name) + logutil.BgLogger().Warn(fmt.Sprintf("skip variable %s:%s", name, value), zap.Error(err)) + continue + } + err = vars.SetSystemVar(name, sVal) + if err != nil { + unLoadVars = append(unLoadVars, name) + logutil.BgLogger().Warn(fmt.Sprintf("skip set variable %s:%s", name, value), zap.Error(err)) + continue + } + } + } + } + if len(unLoadVars) > 0 { + ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("variables set failed:%s", strings.Join(unLoadVars, ","))) + } + return nil +} + +// createSchemaAndItems creates schema and tables or views +func createSchemaAndItems(ctx sessionctx.Context, f *zip.File) error { + r, err := f.Open() + if err != nil { + return errors.AddStack(err) + } + //nolint: errcheck + defer r.Close() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + if err != nil { + return errors.AddStack(err) + } + originText := buf.String() + index1 := strings.Index(originText, ";") + createDatabaseSQL := originText[:index1+1] + index2 := strings.Index(originText[index1+1:], ";") + useDatabaseSQL := originText[index1+1:][:index2+1] + createTableSQL := originText[index1+1:][index2+1:] + c := context.Background() + // create database if not exists + _, err = ctx.(sqlexec.SQLExecutor).Execute(c, createDatabaseSQL) + logutil.BgLogger().Debug("plan replayer: skip error", zap.Error(err)) + // use database + _, err = ctx.(sqlexec.SQLExecutor).Execute(c, useDatabaseSQL) + if err != nil { + return err + } + // create table or view + _, err = ctx.(sqlexec.SQLExecutor).Execute(c, createTableSQL) + if err != nil { + return err + } + return nil +} + +func loadStats(ctx sessionctx.Context, f *zip.File) error { + jsonTbl := &storage.JSONTable{} + r, err := f.Open() + if err != nil { + return errors.AddStack(err) + } + //nolint: errcheck + defer r.Close() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + if err != nil { + return errors.AddStack(err) + } + if err := json.Unmarshal(buf.Bytes(), jsonTbl); err != nil { + return errors.AddStack(err) + } + do := domain.GetDomain(ctx) + h := do.StatsHandle() + if h == nil { + return errors.New("plan replayer: hanlde is nil") + } + return h.LoadStatsFromJSON(context.Background(), ctx.GetInfoSchema().(infoschema.InfoSchema), jsonTbl, 0) +} + +// Update updates the data of the corresponding table. +func (e *PlanReplayerLoadInfo) Update(data []byte) error { + b := bytes.NewReader(data) + z, err := zip.NewReader(b, int64(len(data))) + if err != nil { + return errors.AddStack(err) + } + + // load variable + err = loadVariables(e.Ctx, z) + if err != nil { + return err + } + + // build schema and table first + for _, zipFile := range z.File { + if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) { + continue + } + path := strings.Split(zipFile.Name, "/") + if len(path) == 2 && strings.Compare(path[0], "schema") == 0 && zipFile.Mode().IsRegular() { + err = createSchemaAndItems(e.Ctx, zipFile) + if err != nil { + return err + } + } + } + + // set tiflash replica if exists + err = loadSetTiFlashReplica(e.Ctx, z) + if err != nil { + return err + } + + // build view next + for _, zipFile := range z.File { + path := strings.Split(zipFile.Name, "/") + if len(path) == 2 && strings.Compare(path[0], "view") == 0 && zipFile.Mode().IsRegular() { + err = createSchemaAndItems(e.Ctx, zipFile) + if err != nil { + return err + } + } + } + + // load stats + for _, zipFile := range z.File { + path := strings.Split(zipFile.Name, "/") + if len(path) == 2 && strings.Compare(path[0], "stats") == 0 && zipFile.Mode().IsRegular() { + err = loadStats(e.Ctx, zipFile) + if err != nil { + return err + } + } + } + + err = loadAllBindings(e.Ctx, z) + if err != nil { + logutil.BgLogger().Warn("load bindings failed", zap.Error(err)) + e.Ctx.GetSessionVars().StmtCtx.AppendWarning(fmt.Errorf("load bindings failed, err:%v", err)) + } + return nil +} diff --git a/executor/point_get.go b/pkg/executor/point_get.go similarity index 96% rename from executor/point_get.go rename to pkg/executor/point_get.go index 10c4d35e9bbea..688872de67c51 100644 --- a/executor/point_get.go +++ b/pkg/executor/point_get.go @@ -20,25 +20,25 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/tikv/client-go/v2/txnkv/txnsnapshot" ) diff --git a/executor/point_get_test.go b/pkg/executor/point_get_test.go similarity index 98% rename from executor/point_get_test.go rename to pkg/executor/point_get_test.go index 3c273ac202bda..a88cd64632f31 100644 --- a/executor/point_get_test.go +++ b/pkg/executor/point_get_test.go @@ -21,20 +21,20 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" ) diff --git a/executor/prepared.go b/pkg/executor/prepared.go similarity index 90% rename from executor/prepared.go rename to pkg/executor/prepared.go index f55166b328a37..3a002f3e8822f 100644 --- a/executor/prepared.go +++ b/pkg/executor/prepared.go @@ -19,21 +19,21 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "go.uber.org/zap" ) diff --git a/pkg/executor/prepared_test.go b/pkg/executor/prepared_test.go new file mode 100644 index 0000000000000..0cba4e45bebc8 --- /dev/null +++ b/pkg/executor/prepared_test.go @@ -0,0 +1,1275 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "strconv" + "strings" + "sync/atomic" + "testing" + + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestPreparedNameResolver(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int, KEY id (id))") + tk.MustExec("prepare stmt from 'select * from t limit ? offset ?'") + tk.MustGetErrMsg("prepare stmt from 'select b from t'", + "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("prepare stmt from '(select * FROM t) union all (select * FROM t) order by a limit ?'", + "[planner:1054]Unknown column 'a' in 'order clause'") +} + +// a 'create table' DDL statement should be accepted if it has no parameters. +func TestPreparedDDL(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("prepare stmt from 'create table t (id int, KEY id (id))'") +} + +// TestUnsupportedStmtForPrepare is related to https://github.com/pingcap/tidb/issues/17412 +func TestUnsupportedStmtForPrepare(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`prepare stmt0 from "create table t0(a int primary key)"`) + tk.MustGetErrCode(`prepare stmt1 from "execute stmt0"`, mysql.ErrUnsupportedPs) + tk.MustGetErrCode(`prepare stmt2 from "deallocate prepare stmt0"`, mysql.ErrUnsupportedPs) + tk.MustGetErrCode(`prepare stmt4 from "prepare stmt3 from 'create table t1(a int, b int)'"`, mysql.ErrUnsupportedPs) +} + +func TestIgnorePlanCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + + tk.MustExec("create table t (id int primary key, num int)") + tk.MustExec("insert into t values (1, 1)") + tk.MustExec("insert into t values (2, 2)") + tk.MustExec("insert into t values (3, 3)") + tk.MustExec("prepare stmt from 'select /*+ IGNORE_PLAN_CACHE() */ * from t where id=?'") + tk.MustExec("set @ignore_plan_doma = 1") + tk.MustExec("execute stmt using @ignore_plan_doma") + require.False(t, tk.Session().GetSessionVars().StmtCtx.UseCache) +} + +func TestPreparedStmtWithHint(t *testing.T) { + // see https://github.com/pingcap/tidb/issues/18535 + store, dom := testkit.CreateMockStoreAndDomain(t) + sv := server.CreateMockServer(t, store) + sv.SetDomain(dom) + defer sv.Close() + + conn1 := server.CreateMockConn(t, sv) + tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) + + go dom.ExpensiveQueryHandle().SetSessionManager(sv).Run() + tk.MustExec("prepare stmt from \"select /*+ max_execution_time(100) */ sleep(10)\"") + tk.MustQuery("execute stmt").Check(testkit.Rows("1")) + + // see https://github.com/pingcap/tidb/issues/46817 + tk.MustExec("use test") + tk.MustExec("create table if not exists t (i int)") + tk.MustExec("prepare stmt from 'with a as (select /*+ qb_name(qb1) */ * from t) select /*+ leading(@qb1)*/ * from a;'") +} + +func TestPreparedNullParam(t *testing.T) { + store := testkit.CreateMockStore(t) + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set tidb_enable_prepared_plan_cache=%v`, flag)) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int not null, KEY id (id))") + tk.MustExec("insert into t values (1), (2), (3)") + + tk.MustExec("prepare stmt from 'select * from t where id = ?'") + tk.MustExec("set @a= null") + tk.MustQuery("execute stmt using @a").Check(testkit.Rows()) + + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( + "TableDual_5 0.00 root rows:0")) + } +} + +func TestIssue29850(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec(`set tidb_enable_clustered_index=on`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0") + tk.MustExec(`use test`) + tk.MustExec(`CREATE TABLE customer ( + c_id int(11) NOT NULL, + c_d_id int(11) NOT NULL, + c_first varchar(16) DEFAULT NULL, + c_w_id int(11) NOT NULL, + c_last varchar(16) DEFAULT NULL, + c_credit char(2) DEFAULT NULL, + c_discount decimal(4,4) DEFAULT NULL, + PRIMARY KEY (c_w_id,c_d_id,c_id), + KEY idx_customer (c_w_id,c_d_id,c_last,c_first))`) + tk.MustExec(`CREATE TABLE warehouse ( + w_id int(11) NOT NULL, + w_tax decimal(4,4) DEFAULT NULL, + PRIMARY KEY (w_id))`) + tk.MustExec(`prepare stmt from 'SELECT c_discount, c_last, c_credit, w_tax + FROM customer, warehouse + WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?'`) + tk.MustExec(`set @w_id=1262`) + tk.MustExec(`set @c_d_id=7`) + tk.MustExec(`set @c_id=1549`) + tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows()) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use PointGet + `Projection_7 0.00 root test.customer.c_discount, test.customer.c_last, test.customer.c_credit, test.warehouse.w_tax`, + `└─HashJoin_8 0.00 root CARTESIAN inner join`, + ` ├─Point_Get_11(Build) 1.00 root table:warehouse handle:1262`, + ` └─Point_Get_10(Probe) 1.00 root table:customer, clustered index:PRIMARY(c_w_id, c_d_id, c_id) `)) + tk.MustQuery(`execute stmt using @w_id, @c_d_id, @c_id`).Check(testkit.Rows()) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the cached plan + + tk.MustExec(`create table t (a int primary key)`) + tk.MustExec(`insert into t values (1), (2)`) + tk.MustExec(`prepare stmt from 'select * from t where a>=? and a<=?'`) + tk.MustExec(`set @a1=1, @a2=2`) + tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( + `Point_Get_5 1.00 root table:t handle:1`)) + tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + + tk.MustExec(`prepare stmt from 'select * from t where a=? or a=?'`) + tk.MustQuery(`execute stmt using @a1, @a1`).Check(testkit.Rows("1")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // cannot use PointGet since it contains a or condition + `Point_Get_5 1.00 root table:t handle:1`)) + tk.MustQuery(`execute stmt using @a1, @a2`).Check(testkit.Rows("1", "2")) +} + +func TestIssue28064(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("drop table if exists t28064") + tk.MustExec("CREATE TABLE `t28064` (" + + "`a` decimal(10,0) DEFAULT NULL," + + "`b` decimal(10,0) DEFAULT NULL," + + "`c` decimal(10,0) DEFAULT NULL," + + "`d` decimal(10,0) DEFAULT NULL," + + "KEY `iabc` (`a`,`b`,`c`));") + tk.MustExec("set @a='123', @b='234', @c='345';") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("prepare stmt1 from 'select * from t28064 use index (iabc) where a = ? and b = ? and c = ?';") + + tk.MustExec("execute stmt1 using @a, @b, @c;") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + rows.Check(testkit.Rows( + "IndexLookUp_7 0.00 root ", + "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", + "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) + + tk.MustExec("execute stmt1 using @a, @b, @c;") + rows = tk.MustQuery("select @@last_plan_from_cache") + rows.Check(testkit.Rows("1")) + + tk.MustExec("execute stmt1 using @a, @b, @c;") + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + rows.Check(testkit.Rows( + "IndexLookUp_7 0.00 root ", + "├─IndexRangeScan_5(Build) 0.00 cop[tikv] table:t28064, index:iabc(a, b, c) range:[123 234 345,123 234 345], keep order:false, stats:pseudo", + "└─TableRowIDScan_6(Probe) 0.00 cop[tikv] table:t28064 keep order:false, stats:pseudo")) +} + +func TestPreparePlanCache4Blacklist(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + + // test the blacklist of optimization rules + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("prepare stmt from 'select min(a) from t;';") + tk.MustExec("execute stmt;") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + require.Contains(t, res.Rows()[1][0], "TopN") + + res = tk.MustQuery("explain format = 'brief' select min(a) from t") + require.Contains(t, res.Rows()[1][0], "TopN") + + tk.MustExec("INSERT INTO mysql.opt_rule_blacklist VALUES('max_min_eliminate');") + tk.MustExec("ADMIN reload opt_rule_blacklist;") + + tk.MustExec("execute stmt;") + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + tk.MustExec("execute stmt;") + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + // Plans that have been cached will not be affected by the blacklist. + require.Contains(t, res.Rows()[1][0], "TopN") + + res = tk.MustQuery("explain format = 'brief' select min(a) from t") + require.Contains(t, res.Rows()[0][0], "HashAgg") + + // test the blacklist of Expression Pushdown + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("prepare stmt from 'SELECT * FROM t WHERE a < 2 and a > 2;';") + tk.MustExec("execute stmt;") + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + require.Equal(t, 3, len(res.Rows())) + require.Contains(t, res.Rows()[1][0], "Selection") + require.Equal(t, "gt(test.t.a, 2), lt(test.t.a, 2)", res.Rows()[1][4]) + + res = tk.MustQuery("explain format = 'brief' SELECT * FROM t WHERE a < 2 and a > 2;") + require.Equal(t, 3, len(res.Rows())) + require.Equal(t, "gt(test.t.a, 2), lt(test.t.a, 2)", res.Rows()[1][4]) + + tk.MustExec("INSERT INTO mysql.expr_pushdown_blacklist VALUES('<','tikv','');") + tk.MustExec("ADMIN reload expr_pushdown_blacklist;") + + tk.MustExec("execute stmt;") + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustExec("execute stmt;") + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + // The expressions can not be pushed down to tikv. + require.Equal(t, 4, len(res.Rows())) + + res = tk.MustQuery("explain format = 'brief' SELECT * FROM t WHERE a < 2 and a > 2;") + require.Equal(t, 4, len(res.Rows())) + require.Contains(t, res.Rows()[0][0], "Selection") + require.Equal(t, "lt(test.t.a, 2)", res.Rows()[0][4]) + require.Contains(t, res.Rows()[2][0], "Selection") + require.Equal(t, "gt(test.t.a, 2)", res.Rows()[2][4]) + + tk.MustExec("DELETE FROM mysql.expr_pushdown_blacklist;") + tk.MustExec("ADMIN reload expr_pushdown_blacklist;") +} + +func TestPlanCacheClusterIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("create table t1(a varchar(20), b varchar(20), c varchar(20), primary key(a, b))") + tk.MustExec("insert into t1 values('1','1','111'),('2','2','222'),('3','3','333')") + + // For table scan + tk.MustExec(`prepare stmt1 from "select * from t1 where t1.a = ? and t1.b > ?"`) + tk.MustExec("set @v1 = '1'") + tk.MustExec("set @v2 = '0'") + tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("1 1 111")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustExec("set @v1 = '2'") + tk.MustExec("set @v2 = '1'") + tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("2 2 222")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("set @v1 = '3'") + tk.MustExec("set @v2 = '2'") + tk.MustQuery("execute stmt1 using @v1,@v2").Check(testkit.Rows("3 3 333")) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + require.Equal(t, 0, strings.Index(rows[len(rows)-1][4].(string), `range:("3" "2","3" +inf]`)) + // For point get + tk.MustExec(`prepare stmt2 from "select * from t1 where t1.a = ? and t1.b = ?"`) + tk.MustExec("set @v1 = '1'") + tk.MustExec("set @v2 = '1'") + tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("1 1 111")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustExec("set @v1 = '2'") + tk.MustExec("set @v2 = '2'") + tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("2 2 222")) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("set @v1 = '3'") + tk.MustExec("set @v2 = '3'") + tk.MustQuery("execute stmt2 using @v1,@v2").Check(testkit.Rows("3 3 333")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + require.Equal(t, 0, strings.Index(rows[len(rows)-1][0].(string), `Point_Get`)) + // For CBO point get and batch point get + // case 1: + tk.MustExec(`drop table if exists ta, tb`) + tk.MustExec(`create table ta (a varchar(8) primary key, b int)`) + tk.MustExec(`insert ta values ('a', 1), ('b', 2)`) + tk.MustExec(`create table tb (a varchar(8) primary key, b int)`) + tk.MustExec(`insert tb values ('a', 1), ('b', 2)`) + tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.a = tb.a and ta.a = ?"`) + tk.MustExec(`set @v1 = 'a', @v2 = 'b'`) + tk.MustQuery(`execute stmt1 using @v1`).Check(testkit.Rows("a 1 a 1")) + tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 b 2")) + + // case 2: + tk.MustExec(`drop table if exists ta, tb`) + tk.MustExec(`create table ta (a varchar(10) primary key, b int not null)`) + tk.MustExec(`insert ta values ('a', 1), ('b', 2)`) + tk.MustExec(`create table tb (b int primary key, c int)`) + tk.MustExec(`insert tb values (1, 1), (2, 2)`) + tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.b = tb.b and ta.a = ?"`) + tk.MustExec(`set @v1 = 'a', @v2 = 'b'`) + tk.MustQuery(`execute stmt1 using @v1`).Check(testkit.Rows("a 1 1 1")) + tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 2 2")) + tk.MustQuery(`execute stmt1 using @v2`).Check(testkit.Rows("b 2 2 2")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + require.True(t, strings.Contains(rows[3][0].(string), `TableRangeScan`)) + + // case 3: + tk.MustExec(`drop table if exists ta, tb`) + tk.MustExec(`create table ta (a varchar(10), b varchar(10), c int, primary key (a, b))`) + tk.MustExec(`insert ta values ('a', 'a', 1), ('b', 'b', 2), ('c', 'c', 3)`) + tk.MustExec(`create table tb (b int primary key, c int)`) + tk.MustExec(`insert tb values (1, 1), (2, 2), (3,3)`) + tk.MustExec(`prepare stmt1 from "select * from ta, tb where ta.c = tb.b and ta.a = ? and ta.b = ?"`) + tk.MustExec(`set @v1 = 'a', @v2 = 'b', @v3 = 'c'`) + tk.MustQuery(`execute stmt1 using @v1, @v1`).Check(testkit.Rows("a a 1 1 1")) + tk.MustQuery(`execute stmt1 using @v2, @v2`).Check(testkit.Rows("b b 2 2 2")) + tk.MustExec(`prepare stmt2 from "select * from ta, tb where ta.c = tb.b and (ta.a, ta.b) in ((?, ?), (?, ?))"`) + tk.MustQuery(`execute stmt2 using @v1, @v1, @v2, @v2`).Check(testkit.Rows("a a 1 1 1", "b b 2 2 2")) + tk.MustQuery(`execute stmt2 using @v2, @v2, @v3, @v3`).Check(testkit.Rows("b b 2 2 2", "c c 3 3 3")) + + // For issue 19002 + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec(`drop table if exists t1`) + tk.MustExec(`create table t1(a int, b int, c int, primary key(a, b))`) + tk.MustExec(`insert into t1 values(1,1,111),(2,2,222),(3,3,333)`) + // Point Get: + tk.MustExec(`prepare stmt1 from "select * from t1 where t1.a = ? and t1.b = ?"`) + tk.MustExec(`set @v1=1, @v2=1`) + tk.MustQuery(`execute stmt1 using @v1,@v2`).Check(testkit.Rows("1 1 111")) + tk.MustExec(`set @v1=2, @v2=2`) + tk.MustQuery(`execute stmt1 using @v1,@v2`).Check(testkit.Rows("2 2 222")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + // Batch Point Get: + tk.MustExec(`prepare stmt2 from "select * from t1 where (t1.a,t1.b) in ((?,?),(?,?))"`) + tk.MustExec(`set @v1=1, @v2=1, @v3=2, @v4=2`) + tk.MustQuery(`execute stmt2 using @v1,@v2,@v3,@v4`).Check(testkit.Rows("1 1 111", "2 2 222")) + tk.MustExec(`set @v1=2, @v2=2, @v3=3, @v4=3`) + tk.MustQuery(`execute stmt2 using @v1,@v2,@v3,@v4`).Check(testkit.Rows("2 2 222", "3 3 333")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) +} + +func TestPlanCacheWithDifferentVariableTypes(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("create table t1(a varchar(20), b int, c float, key(b, a))") + tk.MustExec("insert into t1 values('1',1,1.1),('2',2,222),('3',3,333)") + tk.MustExec("create table t2(a varchar(20), b int, c float, key(b, a))") + tk.MustExec("insert into t2 values('3',3,3.3),('2',2,222),('3',3,333)") + + var input []struct { + PrepareStmt string + Executes []struct { + Vars []struct { + Name string + Value string + } + ExecuteSQL string + } + } + var output []struct { + PrepareStmt string + Executes []struct { + SQL string + Vars []struct { + Name string + Value string + } + Plan []string + LastPlanUseCache string + Result []string + } + } + prepareMergeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + tk.MustExec(tt.PrepareStmt) + testdata.OnRecord(func() { + output[i].PrepareStmt = tt.PrepareStmt + output[i].Executes = make([]struct { + SQL string + Vars []struct { + Name string + Value string + } + Plan []string + LastPlanUseCache string + Result []string + }, len(tt.Executes)) + }) + require.Equal(t, tt.PrepareStmt, output[i].PrepareStmt) + for j, exec := range tt.Executes { + for _, v := range exec.Vars { + tk.MustExec(fmt.Sprintf(`set @%s = %s`, v.Name, v.Value)) + } + res := tk.MustQuery(exec.ExecuteSQL) + lastPlanUseCache := tk.MustQuery("select @@last_plan_from_cache").Rows()[0][0] + tk.MustQuery(exec.ExecuteSQL) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + plan := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + testdata.OnRecord(func() { + output[i].Executes[j].SQL = exec.ExecuteSQL + output[i].Executes[j].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + output[i].Executes[j].Vars = exec.Vars + output[i].Executes[j].LastPlanUseCache = lastPlanUseCache.(string) + output[i].Executes[j].Result = testdata.ConvertRowsToStrings(res.Rows()) + }) + + require.Equal(t, exec.ExecuteSQL, output[i].Executes[j].SQL) + plan.Check(testkit.Rows(output[i].Executes[j].Plan...)) + require.Equal(t, exec.Vars, output[i].Executes[j].Vars) + require.Equal(t, lastPlanUseCache.(string), output[i].Executes[j].LastPlanUseCache) + res.Check(testkit.Rows(output[i].Executes[j].Result...)) + } + } +} + +func TestPlanCacheOperators(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + type ExecCase struct { + Parameters []string + UseCache bool + } + type PrepCase struct { + PrepStmt string + ExecCases []ExecCase + } + + cases := []PrepCase{ + {"use test", nil}, + + // cases for TableReader on PK + {"create table t (a int, b int, primary key(a))", nil}, + {"insert into t values (1,1), (2,2), (3,3), (4,4), (5,5), (6,null)", nil}, + {"select a from t where a=?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, true}, + {[]string{"3"}, true}, + }}, + {"select a from t where a in (?,?,?)", []ExecCase{ + {[]string{"1", "1", "1"}, false}, + {[]string{"2", "3", "4"}, true}, + {[]string{"3", "5", "7"}, true}, + }}, + {"select a from t where a>? and a? and a? and a? and a? and a?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ HASH_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ HASH_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ MERGE_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ MERGE_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ INL_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t2.b>?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, true}, + {[]string{"5"}, true}, + }}, + {"select /*+ INL_JOIN(t1, t2) */ * from t t1, t t2 where t1.a=t2.a and t1.b>? and t2.b? and t1.a > (select min(t2.a) from t t2 where t2.b < t1.b)", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, false}, // plans with sub-queries cannot be cached, but the result must be correct + {[]string{"5"}, false}, + }}, + {"select * from t t1 where t1.a > (select min(t2.a) from t t2 where t2.b < t1.b+?)", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"3"}, false}, + {[]string{"5"}, false}, + }}, + {"select * from t t1 where t1.b>? and t1.a > (select min(t2.a) from t t2 where t2.b < t1.b+?)", []ExecCase{ + {[]string{"1", "1"}, false}, + {[]string{"3", "2"}, false}, + {[]string{"5", "3"}, false}, + }}, + {"drop table t", nil}, + + // cases for Window + {"create table t (name varchar(50), y int, sale decimal(14,2))", nil}, + {"insert into t values ('Bob',2016,2.4), ('Bob',2017,3.2), ('Bob',2018,2.1), ('Alice',2016,1.4), ('Alice',2017,2), ('Alice',2018,3.3), ('John',2016,4), ('John',2017,2.1), ('John',2018,5)", nil}, + {"select *, sum(sale) over (partition by y order by sale) total from t where sale>? order by y", []ExecCase{ + {[]string{"0.1"}, false}, + {[]string{"0.5"}, true}, + {[]string{"1.5"}, true}, + {[]string{"3.5"}, true}, + }}, + {"select *, sum(sale) over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ + {[]string{"0.1"}, false}, + {[]string{"0.5"}, true}, + {[]string{"1.5"}, true}, + {[]string{"3.5"}, true}, + }}, + {"select *, rank() over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ + {[]string{"0.1"}, false}, + {[]string{"0.5"}, true}, + {[]string{"1.5"}, true}, + {[]string{"3.5"}, true}, + }}, + {"select *, first_value(sale) over (partition by y order by sale+? rows 2 preceding) total from t order by y", []ExecCase{ + {[]string{"0.1"}, false}, + {[]string{"0.5"}, true}, + {[]string{"1.5"}, true}, + {[]string{"3.5"}, true}, + }}, + {"select *, first_value(sale) over (partition by y order by sale rows ? preceding) total from t order by y", []ExecCase{ + {[]string{"1"}, false}, // window plans with parameters in frame cannot be cached + {[]string{"2"}, false}, + {[]string{"3"}, false}, + {[]string{"4"}, false}, + }}, + {"drop table t", nil}, + + // cases for Limit + {"create table t (a int)", nil}, + {"insert into t values (1), (1), (2), (2), (3), (4), (5), (6), (7), (8), (9), (0), (0)", nil}, + {"select * from t limit ?", []ExecCase{ + {[]string{"20"}, false}, + {[]string{"30"}, false}, + }}, + {"select * from t limit 40, ?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, false}, + }}, + {"select * from t limit ?, 10", []ExecCase{ + {[]string{"20"}, false}, + {[]string{"30"}, false}, + }}, + {"select * from t limit ?, ?", []ExecCase{ + {[]string{"20", "20"}, false}, + {[]string{"20", "40"}, false}, + }}, + {"select * from t where a? order by mod(a, 3)", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, true}, + {[]string{"3"}, true}, + }}, + + // cases for topN + {"select * from t order by b limit ?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, false}, + }}, + {"select * from t order by b limit 10, ?", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, false}, + }}, + {"select * from t order by ? limit 10", []ExecCase{ + {[]string{"1"}, false}, + {[]string{"2"}, false}, + }}, + {"select * from t order by ? limit ?", []ExecCase{ + {[]string{"1", "10"}, false}, + {[]string{"2", "20"}, false}, + }}, + } + + for _, prepCase := range cases { + isQuery := strings.Contains(prepCase.PrepStmt, "select") + if !isQuery { + tk.MustExec(prepCase.PrepStmt) + continue + } + + tk.MustExec(fmt.Sprintf(`prepare stmt from '%v'`, prepCase.PrepStmt)) + for _, execCase := range prepCase.ExecCases { + // set all parameters + usingStmt := "" + if len(execCase.Parameters) > 0 { + setStmt := "set " + usingStmt = "using " + for i, parameter := range execCase.Parameters { + if i > 0 { + setStmt += ", " + usingStmt += ", " + } + setStmt += fmt.Sprintf("@x%v=%v", i, parameter) + usingStmt += fmt.Sprintf("@x%v", i) + } + tk.MustExec(setStmt) + } + + // execute this statement and check whether it uses a cached plan + results := tk.MustQuery("execute stmt " + usingStmt).Sort().Rows() + + // check whether the result is correct + tmp := strings.Split(prepCase.PrepStmt, "?") + require.Equal(t, len(execCase.Parameters)+1, len(tmp)) + query := "" + for i := range tmp { + query += tmp[i] + if i < len(execCase.Parameters) { + query += execCase.Parameters[i] + } + } + tk.MustQuery(query).Sort().Check(results) + } + } +} + +func TestIssue28782(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("prepare stmt from 'SELECT IF(?, 1, 0);';") + tk.MustExec("set @a=1, @b=null, @c=0") + + tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("0")) + // TODO(Reminiscent): Support cache more tableDual plan. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt using @c;").Check(testkit.Rows("0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func TestIssue29101(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) + tk.MustExec(`use test`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec(`CREATE TABLE customer ( + c_id int(11) NOT NULL, + c_d_id int(11) NOT NULL, + c_w_id int(11) NOT NULL, + c_first varchar(16) DEFAULT NULL, + c_last varchar(16) DEFAULT NULL, + c_credit char(2) DEFAULT NULL, + c_discount decimal(4,4) DEFAULT NULL, + PRIMARY KEY (c_w_id,c_d_id,c_id) NONCLUSTERED, + KEY idx_customer (c_w_id,c_d_id,c_last,c_first) + )`) + tk.MustExec(`CREATE TABLE warehouse ( + w_id int(11) NOT NULL, + w_tax decimal(4,4) DEFAULT NULL, + PRIMARY KEY (w_id) + )`) + tk.MustExec(`prepare s1 from 'SELECT /*+ TIDB_INLJ(customer,warehouse) */ c_discount, c_last, c_credit, w_tax FROM customer, warehouse WHERE w_id = ? AND c_w_id = w_id AND c_d_id = ? AND c_id = ?'`) + tk.MustExec(`set @a=936,@b=7,@c=158`) + tk.MustQuery(`execute s1 using @a,@b,@c`).Check(testkit.Rows()) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use PK + `Projection_6 1.00 root test.customer.c_discount, test.customer.c_last, test.customer.c_credit, test.warehouse.w_tax`, + `└─HashJoin_7 1.00 root CARTESIAN inner join`, + ` ├─Point_Get_10(Build) 1.00 root table:warehouse handle:936`, + ` └─Point_Get_9(Probe) 1.00 root table:customer, index:PRIMARY(c_w_id, c_d_id, c_id) `)) + tk.MustQuery(`execute s1 using @a,@b,@c`).Check(testkit.Rows()) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the plan-cache + + tk.MustExec(`CREATE TABLE order_line ( + ol_o_id int(11) NOT NULL, + ol_d_id int(11) NOT NULL, + ol_w_id int(11) NOT NULL, + ol_number int(11) NOT NULL, + ol_i_id int(11) NOT NULL, + PRIMARY KEY (ol_w_id,ol_d_id,ol_o_id,ol_number) NONCLUSTERED)`) + tk.MustExec(`CREATE TABLE stock ( + s_i_id int(11) NOT NULL, + s_w_id int(11) NOT NULL, + s_quantity int(11) DEFAULT NULL, + PRIMARY KEY (s_w_id,s_i_id) NONCLUSTERED)`) + tk.MustExec(`prepare s1 from 'SELECT /*+ TIDB_INLJ(order_line,stock) */ COUNT(DISTINCT (s_i_id)) stock_count FROM order_line, stock WHERE ol_w_id = ? AND ol_d_id = ? AND ol_o_id < ? AND ol_o_id >= ? - 20 AND s_w_id = ? AND s_i_id = ol_i_id AND s_quantity < ?'`) + tk.MustExec(`set @a=391,@b=1,@c=3058,@d=18`) + tk.MustExec(`execute s1 using @a,@b,@c,@c,@a,@d`) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( // can use index-join + `StreamAgg_9 1.00 root funcs:count(distinct test.stock.s_i_id)->Column#11`, + `└─IndexJoin_14 0.03 root inner join, inner:IndexLookUp_13, outer key:test.order_line.ol_i_id, inner key:test.stock.s_i_id, equal cond:eq(test.order_line.ol_i_id, test.stock.s_i_id)`, + ` ├─IndexLookUp_28(Build) 0.03 root `, + ` │ ├─IndexRangeScan_26(Build) 0.03 cop[tikv] table:order_line, index:PRIMARY(ol_w_id, ol_d_id, ol_o_id, ol_number) range:[391 1 3038,391 1 3058), keep order:false, stats:pseudo`, + ` │ └─TableRowIDScan_27(Probe) 0.03 cop[tikv] table:order_line keep order:false, stats:pseudo`, + ` └─IndexLookUp_13(Probe) 0.03 root `, + ` ├─IndexRangeScan_10(Build) 0.03 cop[tikv] table:stock, index:PRIMARY(s_w_id, s_i_id) range: decided by [eq(test.stock.s_i_id, test.order_line.ol_i_id) eq(test.stock.s_w_id, 391)], keep order:false, stats:pseudo`, + ` └─Selection_12(Probe) 0.03 cop[tikv] lt(test.stock.s_quantity, 18)`, + ` └─TableRowIDScan_11 0.03 cop[tikv] table:stock keep order:false, stats:pseudo`)) + tk.MustExec(`execute s1 using @a,@b,@c,@c,@a,@d`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) // can use the plan-cache +} + +func TestIssue28087And28162(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + // issue 28087 + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists IDT_26207`) + tk.MustExec(`CREATE TABLE IDT_26207 (col1 bit(1))`) + tk.MustExec(`insert into IDT_26207 values(0x0), (0x1)`) + tk.MustExec(`prepare stmt from 'select t1.col1 from IDT_26207 as t1 left join IDT_26207 as t2 on t1.col1 = t2.col1 where t1.col1 in (?, ?, ?)'`) + tk.MustExec(`set @a=0x01, @b=0x01, @c=0x01`) + tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x01")) + tk.MustExec(`set @a=0x00, @b=0x00, @c=0x01`) + tk.MustQuery(`execute stmt using @a,@b,@c`).Check(testkit.Rows("\x00", "\x01")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + + // issue 28162 + tk.MustExec(`drop table if exists IDT_MC21780`) + tk.MustExec(`CREATE TABLE IDT_MC21780 ( + COL1 timestamp NULL DEFAULT NULL, + COL2 timestamp NULL DEFAULT NULL, + COL3 timestamp NULL DEFAULT NULL, + KEY U_M_COL (COL1,COL2) + )`) + tk.MustExec(`insert into IDT_MC21780 values("1970-12-18 10:53:28", "1970-12-18 10:53:28", "1970-12-18 10:53:28")`) + tk.MustExec(`prepare stmt from 'select/*+ hash_join(t1) */ * from IDT_MC21780 t1 join IDT_MC21780 t2 on t1.col1 = t2.col1 where t1. col1 < ? and t2. col1 in (?, ?, ?);'`) + tk.MustExec(`set @a="2038-01-19 03:14:07", @b="2038-01-19 03:14:07", @c="2038-01-19 03:14:07", @d="2038-01-19 03:14:07"`) + tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows()) + tk.MustExec(`set @a="1976-09-09 20:21:11", @b="2021-07-14 09:28:16", @c="1982-01-09 03:36:39", @d="1970-12-18 10:53:28"`) + tk.MustQuery(`execute stmt using @a,@b,@c,@d`).Check(testkit.Rows("1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28 1970-12-18 10:53:28")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) +} + +func TestParameterPushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t (a int, b int, c int, key(a))`) + tk.MustExec(`insert into t values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5), (6, 6, 6)`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec(`set @x1=1,@x5=5,@x10=10,@x20=20`) + + var input []struct { + SQL string + } + var output []struct { + Result []string + Plan []string + FromCache string + } + prepareMergeSuiteData.LoadTestCases(t, &input, &output) + + for i, tt := range input { + if strings.HasPrefix(tt.SQL, "execute") { + res := tk.MustQuery(tt.SQL).Sort() + fromCache := tk.MustQuery("select @@last_plan_from_cache") + tk.MustQuery(tt.SQL) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + plan := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)) + + testdata.OnRecord(func() { + output[i].Result = testdata.ConvertRowsToStrings(res.Rows()) + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + output[i].FromCache = fromCache.Rows()[0][0].(string) + }) + + res.Check(testkit.Rows(output[i].Result...)) + plan.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, fromCache.Rows()[0][0].(string), output[i].FromCache) + } else { + tk.MustExec(tt.SQL) + testdata.OnRecord(func() { + output[i].Result = nil + }) + } + } +} + +func TestPreparePlanCache4Function(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + + // Testing for non-deterministic functions + tk.MustExec("prepare stmt from 'select rand()';") + res := tk.MustQuery("execute stmt;") + require.Equal(t, 1, len(res.Rows())) + + res1 := tk.MustQuery("execute stmt;") + require.Equal(t, 1, len(res1.Rows())) + require.NotEqual(t, res.Rows()[0][0], res1.Rows()[0][0]) + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + + // Testing for control functions + tk.MustExec("prepare stmt from 'SELECT IFNULL(?,0);';") + tk.MustExec("set @a = 1, @b = null;") + tk.MustQuery("execute stmt using @a;").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @b;").Check(testkit.Rows("0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("prepare stmt from 'select a, case when a = ? then 0 when a <=> ? then 1 else 2 end b from t order by a;';") + tk.MustExec("insert into t values(0), (1), (2), (null);") + tk.MustExec("set @a = 0, @b = 1, @c = 2, @d = null;") + tk.MustQuery("execute stmt using @a, @b;").Check(testkit.Rows(" 2", "0 0", "1 1", "2 2")) + tk.MustQuery("execute stmt using @c, @d;").Check(testkit.Rows(" 1", "0 2", "1 2", "2 0")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func TestPreparePlanCache4DifferentSystemVars(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + + // Testing for 'sql_select_limit' + tk.MustExec("set @@sql_select_limit = 1") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(0), (1), (null);") + tk.MustExec("prepare stmt from 'select a from t order by a;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows("")) + + tk.MustExec("set @@sql_select_limit = 2") + tk.MustQuery("execute stmt;").Check(testkit.Rows("", "0")) + // The 'sql_select_limit' will be stored in the cache key. So if the `sql_select_limit` + // have been changed, the plan cache can not be reused. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("set @@sql_select_limit = 18446744073709551615") + tk.MustQuery("execute stmt;").Check(testkit.Rows("", "0", "1")) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + // test for 'tidb_enable_index_merge' + tk.MustExec("set @@tidb_enable_index_merge = 1;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int, index idx_a(a), index idx_b(b));") + tk.MustExec("prepare stmt from 'select * from t use index(idx_a, idx_b) where a > 1 or b > 1;';") + tk.MustExec("execute stmt;") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res := tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Equal(t, 4, len(res.Rows())) + require.Contains(t, res.Rows()[0][0], "IndexMerge") + + tk.MustExec("set @@tidb_enable_index_merge = 0;") + tk.MustExec("execute stmt;") + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Equal(t, 4, len(res.Rows())) + require.Contains(t, res.Rows()[0][0], "IndexMerge") + tk.MustExec("execute stmt;") + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) + + // test for 'tidb_enable_parallel_apply' + tk.MustExec("set @@tidb_enable_collect_execution_info=1;") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (null, null)") + + tk.MustExec("set tidb_enable_parallel_apply=true") + tk.MustExec("prepare stmt from 'select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a);';") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Contains(t, res.Rows()[1][0], "Apply") + require.Contains(t, res.Rows()[1][5], "Concurrency") + + tk.MustExec("set tidb_enable_parallel_apply=false") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Contains(t, res.Rows()[1][0], "Apply") + executionInfo := fmt.Sprintf("%v", res.Rows()[1][4]) + // Do not use the parallel apply. + require.False(t, strings.Contains(executionInfo, "Concurrency")) + tk.MustExec("execute stmt;") + // The subquery plan with PhysicalApply can't be cached. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustExec("execute stmt;") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: PhysicalApply plan is un-cacheable")) + + // test for apply cache + tk.MustExec("set @@tidb_enable_collect_execution_info=1;") + tk.MustExec("set tidb_mem_quota_apply_cache=33554432") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), (9, 9), (null, null)") + + tk.MustExec("prepare stmt from 'select t1.b from t t1 where t1.b > (select max(b) from t t2 where t1.a > t2.a);';") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Contains(t, res.Rows()[1][0], "Apply") + require.Contains(t, res.Rows()[1][5], "cache:ON") + + tk.MustExec("set tidb_mem_quota_apply_cache=0") + tk.MustQuery("execute stmt;").Sort().Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + res = tk.MustQuery("explain for connection " + strconv.FormatUint(tkProcess.ID, 10)) + require.Contains(t, res.Rows()[1][0], "Apply") + executionInfo = fmt.Sprintf("%v", res.Rows()[1][5]) + // Do not use the apply cache. + require.True(t, strings.Contains(executionInfo, "cache:OFF")) + tk.MustExec("execute stmt;") + // The subquery plan can not be cached. + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func TestTemporaryTable4PlanCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("drop table if exists tmp2") + tk.MustExec("create temporary table tmp2 (a int, b int, key(a), key(b));") + tk.MustExec("prepare stmt from 'select * from tmp2;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + + tk.MustExec("drop table if exists tmp_t;") + tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") + tk.MustExec("prepare stmt from 'select * from tmp_t;';") + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("execute stmt;").Check(testkit.Rows()) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) +} + +func TestPrepareStmtAfterIsolationReadChange(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=0`) + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // create virtual tiflash replica. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@session.tidb_isolation_read_engines='tikv'") + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustExec("prepare stmt from \"select * from t\"") + tk.MustQuery("execute stmt") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows := tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + require.Equal(t, "cop[tikv]", rows[len(rows)-1][2]) + + tk.MustExec("set @@session.tidb_isolation_read_engines='tiflash'") + // allowing mpp will generate mpp[tiflash] plan, the test framework will time out due to + // "retry for TiFlash peer with region missing", so disable mpp mode to use cop mode instead. + tk.MustExec("set @@session.tidb_allow_mpp=0") + tk.MustExec("execute stmt") + tkProcess = tk.Session().ShowProcess() + ps = []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + require.Equal(t, rows[len(rows)-1][2], "cop[tiflash]") + + require.Equal(t, 1, len(tk.Session().GetSessionVars().PreparedStmts)) + require.Equal(t, "select * from `t`", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedSQL) + require.Equal(t, "", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedPlan) +} + +func TestPreparePC4Binding(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + + tk.MustExec("prepare stmt from \"select * from t\"") + require.Equal(t, 1, len(tk.Session().GetSessionVars().PreparedStmts)) + require.Equal(t, "select * from `test` . `t`", tk.Session().GetSessionVars().PreparedStmts[1].(*plannercore.PlanCacheStmt).NormalizedSQL4PC) + + tk.MustQuery("execute stmt") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("create binding for select * from t using select * from t") + res := tk.MustQuery("show session bindings") + require.Equal(t, 1, len(res.Rows())) + + tk.MustQuery("execute stmt") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + tk.MustQuery("execute stmt") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustQuery("execute stmt") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) +} + +func TestIssue31141(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set @@tidb_txn_mode = 'pessimistic'") + + // No panic here. + tk.MustExec("prepare stmt1 from 'do 1'") + + tk.MustExec("set @@tidb_txn_mode = 'optimistic'") + tk.MustExec("prepare stmt1 from 'do 1'") +} + +func TestMaxPreparedStmtCount(t *testing.T) { + oldVal := atomic.LoadInt64(&variable.PreparedStmtCount) + atomic.StoreInt64(&variable.PreparedStmtCount, 0) + defer func() { + atomic.StoreInt64(&variable.PreparedStmtCount, oldVal) + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.max_prepared_stmt_count = 2") + tk.MustExec("prepare stmt1 from 'select ? as num from dual'") + tk.MustExec("prepare stmt2 from 'select ? as num from dual'") + err := tk.ExecToErr("prepare stmt3 from 'select ? as num from dual'") + require.True(t, terror.ErrorEqual(err, variable.ErrMaxPreparedStmtCountReached)) +} diff --git a/executor/projection.go b/pkg/executor/projection.go similarity index 97% rename from executor/projection.go rename to pkg/executor/projection.go index bf9fb465fedaf..a55294239dc4b 100644 --- a/executor/projection.go +++ b/pkg/executor/projection.go @@ -23,13 +23,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" ) diff --git a/executor/recover_test.go b/pkg/executor/recover_test.go similarity index 90% rename from executor/recover_test.go rename to pkg/executor/recover_test.go index 6fd3426f9c7b6..7c678510d28bb 100644 --- a/executor/recover_test.go +++ b/pkg/executor/recover_test.go @@ -21,23 +21,23 @@ import ( "time" "github.com/pingcap/failpoint" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/gcutil" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/gcutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" tikvutil "github.com/tikv/client-go/v2/util" ) func TestRecoverTable(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) }() store := testkit.CreateMockStore(t) @@ -125,9 +125,9 @@ func TestRecoverTable(t *testing.T) { } func TestFlashbackTable(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) }() store := testkit.CreateMockStore(t) @@ -290,9 +290,9 @@ func TestRecoverTableMeetError(t *testing.T) { tk.MustQuery("select * from t_recover").Check(testkit.Rows("1", "2", "3")) tk.MustExec("drop table t_recover") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr", `return(1)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr", `return(1)`)) tk.MustContainErrMsg("recover table t_recover", "mock update version and tableInfo error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockUpdateVersionAndTableInfoErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockUpdateVersionAndTableInfoErr")) tk.MustContainErrMsg("select * from t_recover", "Table 'test_recover.t_recover' doesn't exist") } @@ -343,8 +343,8 @@ func TestRecoverClusterMeetError(t *testing.T) { flashbackTs := oracle.GetTimeFromTS(ts) injectSafeTS := oracle.GoTimeToTS(flashbackTs.Add(10 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) // Get GC safe point error. @@ -381,16 +381,16 @@ func TestRecoverClusterMeetError(t *testing.T) { errorMsg = fmt.Sprintf("[ddl:-1]Detected TiDB upgrade during [%s, now), can't do flashback", oracle.GetTimeFromTS(nowTS).String()) tk.MustGetErrMsg(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(nowTS)), errorMsg) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) } func TestFlashbackWithSafeTs(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/changeFlashbackGetMinSafeTimeTimeout", `return(0)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/changeFlashbackGetMinSafeTimeTimeout", `return(0)`)) timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) defer resetGC() @@ -429,7 +429,7 @@ func TestFlashbackWithSafeTs(t *testing.T) { } for _, testcase := range testcases { t.Log(testcase.name) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", testcase.injectSafeTS))) if testcase.compareWithSafeTS == 1 { start := time.Now() @@ -441,16 +441,16 @@ func TestFlashbackWithSafeTs(t *testing.T) { tk.MustExec(testcase.sql) } } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/changeFlashbackGetMinSafeTimeTimeout")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/changeFlashbackGetMinSafeTimeTimeout")) } func TestFlashbackRetryGetMinSafeTime(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockFlashbackTest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest", `return(true)`)) timeBeforeDrop, _, safePointSQL, resetGC := MockGC(tk) defer resetGC() @@ -462,12 +462,12 @@ func TestFlashbackRetryGetMinSafeTime(t *testing.T) { ts, _ := tk.Session().GetStore().GetOracle().GetTimestamp(context.Background(), &oracle.Option{}) flashbackTs := oracle.GetTimeFromTS(ts) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", oracle.GoTimeToTS(flashbackTs.Add(-10*time.Minute))))) go func() { time.Sleep(2 * time.Second) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", oracle.GoTimeToTS(flashbackTs.Add(10*time.Minute))))) }() @@ -477,14 +477,14 @@ func TestFlashbackRetryGetMinSafeTime(t *testing.T) { require.Greater(t, duration, 2*time.Second) require.Less(t, duration, 5*time.Second) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockFlashbackTest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockFlashbackTest")) } func TestFlashbackSchema(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) }() store := testkit.CreateMockStore(t) diff --git a/executor/reload_expr_pushdown_blacklist.go b/pkg/executor/reload_expr_pushdown_blacklist.go similarity index 97% rename from executor/reload_expr_pushdown_blacklist.go rename to pkg/executor/reload_expr_pushdown_blacklist.go index a991f32144ab8..527c43ecf1690 100644 --- a/executor/reload_expr_pushdown_blacklist.go +++ b/pkg/executor/reload_expr_pushdown_blacklist.go @@ -19,13 +19,13 @@ import ( "strings" "time" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) // ReloadExprPushdownBlacklistExec indicates ReloadExprPushdownBlacklist executor. diff --git a/executor/replace.go b/pkg/executor/replace.go similarity index 95% rename from executor/replace.go rename to pkg/executor/replace.go index ef7ee918ee14f..037cbc8b9ddcf 100644 --- a/executor/replace.go +++ b/pkg/executor/replace.go @@ -20,14 +20,14 @@ import ( "runtime/trace" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/memory" ) // ReplaceExec represents a replace executor. diff --git a/executor/resource_tag_test.go b/pkg/executor/resource_tag_test.go similarity index 91% rename from executor/resource_tag_test.go rename to pkg/executor/resource_tag_test.go index d0f5e4d65ed9d..23fecdad837c0 100644 --- a/executor/resource_tag_test.go +++ b/pkg/executor/resource_tag_test.go @@ -20,14 +20,14 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikvrpc" @@ -49,9 +49,9 @@ func TestResourceGroupTag(t *testing.T) { conf.TopSQL.ReceiverAddress = "mock-agent" }) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCClientSendHook", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCClientSendHook", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCClientSendHook")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCClientSendHook")) }() var sqlDigest, planDigest *parser.Digest diff --git a/executor/revoke.go b/pkg/executor/revoke.go similarity index 95% rename from executor/revoke.go rename to pkg/executor/revoke.go index 3f13811ccafb1..3aaed4377f905 100644 --- a/executor/revoke.go +++ b/pkg/executor/revoke.go @@ -19,21 +19,21 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/executor/revoke_test.go b/pkg/executor/revoke_test.go similarity index 98% rename from executor/revoke_test.go rename to pkg/executor/revoke_test.go index ff9768a153c8c..0cfb9e006c5d7 100644 --- a/executor/revoke_test.go +++ b/pkg/executor/revoke_test.go @@ -19,11 +19,11 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" ) diff --git a/executor/rowid_test.go b/pkg/executor/rowid_test.go similarity index 98% rename from executor/rowid_test.go rename to pkg/executor/rowid_test.go index e4fbe32e5b142..b966479cc1042 100644 --- a/executor/rowid_test.go +++ b/pkg/executor/rowid_test.go @@ -17,8 +17,8 @@ package executor_test import ( "testing" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" ) func TestExportRowID(t *testing.T) { diff --git a/pkg/executor/sample.go b/pkg/executor/sample.go new file mode 100644 index 0000000000000..2b1213129ca54 --- /dev/null +++ b/pkg/executor/sample.go @@ -0,0 +1,400 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "slices" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/tikv/client-go/v2/tikv" +) + +var _ exec.Executor = &TableSampleExecutor{} + +// TableSampleExecutor fetches a few rows through kv.Scan +// according to the specific sample method. +type TableSampleExecutor struct { + exec.BaseExecutor + + table table.Table + startTS uint64 + + sampler rowSampler +} + +// Open initializes necessary variables for using this executor. +func (*TableSampleExecutor) Open(ctx context.Context) error { + defer tracing.StartRegion(ctx, "TableSampleExecutor.Open").End() + return nil +} + +// Next fills data into the chunk passed by its caller. +// The task was actually done by sampler. +func (e *TableSampleExecutor) Next(_ context.Context, req *chunk.Chunk) error { + req.Reset() + if e.sampler.finished() { + return nil + } + // TODO(tangenta): add runtime stat & memory tracing + return e.sampler.writeChunk(req) +} + +// Close implements the Executor Close interface. +func (*TableSampleExecutor) Close() error { + return nil +} + +type rowSampler interface { + writeChunk(req *chunk.Chunk) error + finished() bool +} + +type tableRegionSampler struct { + ctx sessionctx.Context + table table.Table + startTS uint64 + partTables []table.PartitionedTable + schema *expression.Schema + fullSchema *expression.Schema + isDesc bool + retTypes []*types.FieldType + + rowMap map[int64]types.Datum + restKVRanges []kv.KeyRange + isFinished bool +} + +func newTableRegionSampler(ctx sessionctx.Context, t table.Table, startTs uint64, partTables []table.PartitionedTable, + schema *expression.Schema, fullSchema *expression.Schema, retTypes []*types.FieldType, desc bool) *tableRegionSampler { + return &tableRegionSampler{ + ctx: ctx, + table: t, + startTS: startTs, + partTables: partTables, + schema: schema, + fullSchema: fullSchema, + isDesc: desc, + retTypes: retTypes, + rowMap: make(map[int64]types.Datum), + } +} + +func (s *tableRegionSampler) writeChunk(req *chunk.Chunk) error { + err := s.initRanges() + if err != nil { + return err + } + expectedRowCount := req.RequiredRows() + for expectedRowCount > 0 && len(s.restKVRanges) > 0 { + ranges, err := s.pickRanges(expectedRowCount) + if err != nil { + return err + } + err = s.writeChunkFromRanges(ranges, req) + if err != nil { + return err + } + expectedRowCount = req.RequiredRows() - req.NumRows() + } + if len(s.restKVRanges) == 0 { + s.isFinished = true + } + return nil +} + +func (s *tableRegionSampler) initRanges() error { + if s.restKVRanges == nil { + var err error + s.restKVRanges, err = s.splitTableRanges() + if err != nil { + return err + } + sortRanges(s.restKVRanges, s.isDesc) + } + return nil +} + +func (s *tableRegionSampler) pickRanges(count int) ([]kv.KeyRange, error) { + var regionKeyRanges []kv.KeyRange + cutPoint := count + if len(s.restKVRanges) < cutPoint { + cutPoint = len(s.restKVRanges) + } + regionKeyRanges, s.restKVRanges = s.restKVRanges[:cutPoint], s.restKVRanges[cutPoint:] + return regionKeyRanges, nil +} + +func (s *tableRegionSampler) writeChunkFromRanges(ranges []kv.KeyRange, req *chunk.Chunk) error { + decLoc := s.ctx.GetSessionVars().Location() + cols, decColMap, err := s.buildSampleColAndDecodeColMap() + if err != nil { + return err + } + rowDecoder := decoder.NewRowDecoder(s.table, cols, decColMap) + err = s.scanFirstKVForEachRange(ranges, func(handle kv.Handle, value []byte) error { + _, err := rowDecoder.DecodeAndEvalRowWithMap(s.ctx, handle, value, decLoc, s.rowMap) + if err != nil { + return err + } + currentRow := rowDecoder.CurrentRowWithDefaultVal() + mutRow := chunk.MutRowFromTypes(s.retTypes) + for i, col := range s.schema.Columns { + offset := decColMap[col.ID].Col.Offset + target := currentRow.GetDatum(offset, s.retTypes[i]) + mutRow.SetDatum(i, target) + } + req.AppendRow(mutRow.ToRow()) + s.resetRowMap() + return nil + }) + return err +} + +func (s *tableRegionSampler) splitTableRanges() ([]kv.KeyRange, error) { + if len(s.partTables) != 0 { + var ranges []kv.KeyRange + for _, t := range s.partTables { + for _, pid := range t.GetAllPartitionIDs() { + start := tablecodec.GenTableRecordPrefix(pid) + end := start.PrefixNext() + rs, err := splitIntoMultiRanges(s.ctx.GetStore(), start, end) + if err != nil { + return nil, err + } + ranges = append(ranges, rs...) + } + } + return ranges, nil + } + startKey, endKey := s.table.RecordPrefix(), s.table.RecordPrefix().PrefixNext() + return splitIntoMultiRanges(s.ctx.GetStore(), startKey, endKey) +} + +func splitIntoMultiRanges(store kv.Storage, startKey, endKey kv.Key) ([]kv.KeyRange, error) { + kvRange := kv.KeyRange{StartKey: startKey, EndKey: endKey} + + s, ok := store.(tikv.Storage) + if !ok { + return []kv.KeyRange{kvRange}, nil + } + + maxSleep := 10000 // ms + bo := tikv.NewBackofferWithVars(context.Background(), maxSleep, nil) + regions, err := s.GetRegionCache().LoadRegionsInKeyRange(bo, startKey, endKey) + if err != nil { + return nil, errors.Trace(err) + } + var ranges = make([]kv.KeyRange, 0, len(regions)) + for _, r := range regions { + start, end := r.StartKey(), r.EndKey() + if kv.Key(start).Cmp(startKey) < 0 { + start = startKey + } + if end == nil || kv.Key(end).Cmp(endKey) > 0 { + end = endKey + } + ranges = append(ranges, kv.KeyRange{StartKey: start, EndKey: end}) + } + if len(ranges) == 0 { + return nil, errors.Trace(errors.Errorf("no regions found")) + } + return ranges, nil +} + +func sortRanges(ranges []kv.KeyRange, isDesc bool) { + slices.SortFunc(ranges, func(i, j kv.KeyRange) int { + ir, jr := i.StartKey, j.StartKey + if !isDesc { + return ir.Cmp(jr) + } + return -ir.Cmp(jr) + }) +} + +func (s *tableRegionSampler) buildSampleColAndDecodeColMap() ([]*table.Column, map[int64]decoder.Column, error) { + schemaCols := s.schema.Columns + cols := make([]*table.Column, 0, len(schemaCols)) + colMap := make(map[int64]decoder.Column, len(schemaCols)) + tableCols := s.table.Cols() + + for _, schemaCol := range schemaCols { + for _, tableCol := range tableCols { + if tableCol.ID != schemaCol.ID { + continue + } + // The `MutRow` produced by `DecodeAndEvalRowWithMap` used `ColumnInfo.Offset` as indices. + // To evaluate the columns in virtual generated expression properly, + // indices of column(Column.Index) needs to be resolved against full column's schema. + if schemaCol.VirtualExpr != nil { + var err error + schemaCol.VirtualExpr, err = schemaCol.VirtualExpr.ResolveIndices(s.fullSchema) + if err != nil { + return nil, nil, err + } + } + colMap[tableCol.ID] = decoder.Column{ + Col: tableCol, + GenExpr: schemaCol.VirtualExpr, + } + cols = append(cols, tableCol) + } + } + // Schema columns contain _tidb_rowid, append extra handle column info. + if len(cols) < len(schemaCols) && schemaCols[len(schemaCols)-1].ID == model.ExtraHandleID { + extraHandle := model.NewExtraHandleColInfo() + extraHandle.Offset = len(cols) + tableCol := &table.Column{ColumnInfo: extraHandle} + colMap[model.ExtraHandleID] = decoder.Column{ + Col: tableCol, + } + cols = append(cols, tableCol) + } + return cols, colMap, nil +} + +func (s *tableRegionSampler) scanFirstKVForEachRange(ranges []kv.KeyRange, + fn func(handle kv.Handle, value []byte) error) error { + ver := kv.Version{Ver: s.startTS} + snap := s.ctx.GetStore().GetSnapshot(ver) + setOptionForTopSQL(s.ctx.GetSessionVars().StmtCtx, snap) + concurrency := s.ctx.GetSessionVars().ExecutorConcurrency + if len(ranges) < concurrency { + concurrency = len(ranges) + } + + fetchers := make([]*sampleFetcher, concurrency) + for i := 0; i < concurrency; i++ { + fetchers[i] = &sampleFetcher{ + workerID: i, + concurrency: concurrency, + kvChan: make(chan *sampleKV), + snapshot: snap, + ranges: ranges, + } + go fetchers[i].run() + } + syncer := sampleSyncer{ + fetchers: fetchers, + totalCount: len(ranges), + consumeFn: fn, + } + return syncer.sync() +} + +func (s *tableRegionSampler) resetRowMap() { + if s.rowMap == nil { + colLen := len(s.schema.Columns) + s.rowMap = make(map[int64]types.Datum, colLen) + return + } + for id := range s.rowMap { + delete(s.rowMap, id) + } +} + +func (s *tableRegionSampler) finished() bool { + return s.isFinished +} + +type sampleKV struct { + handle kv.Handle + value []byte +} + +type sampleFetcher struct { + workerID int + concurrency int + kvChan chan *sampleKV + err error + snapshot kv.Snapshot + ranges []kv.KeyRange +} + +func (s *sampleFetcher) run() { + defer close(s.kvChan) + for i, r := range s.ranges { + if i%s.concurrency != s.workerID { + continue + } + it, err := s.snapshot.Iter(r.StartKey, r.EndKey) + if err != nil { + s.err = err + return + } + hasValue := false + for it.Valid() { + if !tablecodec.IsRecordKey(it.Key()) { + if err = it.Next(); err != nil { + s.err = err + return + } + continue + } + handle, err := tablecodec.DecodeRowKey(it.Key()) + if err != nil { + s.err = err + return + } + hasValue = true + s.kvChan <- &sampleKV{handle: handle, value: it.Value()} + break + } + if !hasValue { + s.kvChan <- nil + } + } +} + +type sampleSyncer struct { + fetchers []*sampleFetcher + totalCount int + consumeFn func(handle kv.Handle, value []byte) error +} + +func (s *sampleSyncer) sync() error { + defer func() { + for _, f := range s.fetchers { + // Cleanup channels to terminate fetcher goroutines. + channel.Clear(f.kvChan) + } + }() + for i := 0; i < s.totalCount; i++ { + f := s.fetchers[i%len(s.fetchers)] + v, ok := <-f.kvChan + if f.err != nil { + return f.err + } + if ok && v != nil { + err := s.consumeFn(v.handle, v.value) + if err != nil { + return err + } + } + } + return nil +} diff --git a/pkg/executor/sample_test.go b/pkg/executor/sample_test.go new file mode 100644 index 0000000000000..4a2a110f35367 --- /dev/null +++ b/pkg/executor/sample_test.go @@ -0,0 +1,282 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "sync/atomic" + "testing" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func createSampleTestkit(t *testing.T, store kv.Storage) *testkit.TestKit { + atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) + tk := testkit.NewTestKit(t, store) + tk.MustExec("drop database if exists test_table_sample;") + tk.MustExec("create database test_table_sample;") + tk.MustExec("use test_table_sample;") + tk.MustExec("set @@global.tidb_scatter_region=1;") + return tk +} + +func TestTableSampleBasic(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int);") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows()) + + tk.MustExec("insert into t values (0), (1000), (2000);") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("0")) + tk.MustExec("alter table t add column b varchar(255) not null default 'abc';") + tk.MustQuery("select b from t tablesample regions();").Check(testkit.Rows("abc")) + tk.MustExec("alter table t add column c int as (a + 1);") + tk.MustQuery("select c from t tablesample regions();").Check(testkit.Rows("1")) + tk.MustQuery("select c, _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1 1")) + tk.MustHavePlan("select * from t tablesample regions();", "TableSample") + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a BIGINT PRIMARY KEY AUTO_RANDOM(3), b int auto_increment, key(b)) pre_split_regions=8;") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows()) + for i := 0; i < 1000; i++ { + tk.MustExec("insert into t values();") + } + tk.MustQuery("select count(*) from t tablesample regions();").Check(testkit.Rows("8")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a varchar(30) collate utf8mb4_general_ci primary key);") + tk.MustQuery("split table t between ('a') and ('z') regions 100;").Check(testkit.Rows("99 1")) + tk.MustExec("insert into t values ('a'), ('b'), ('c'), ('d'), ('e');") + tk.MustQuery("select a from t tablesample regions() limit 2;").Check(testkit.Rows("a", "b")) +} + +func TestTableSampleMultiRegions(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int) shard_row_id_bits = 2 pre_split_regions = 2;") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?);", i) + } + rows := tk.MustQuery("select * from t tablesample regions();").Rows() + require.Len(t, rows, 4) + tk.MustQuery("select a from t tablesample regions() order by a limit 1;").Check(testkit.Rows("0")) + tk.MustQuery("select a from t tablesample regions() where a = 0;").Check(testkit.Rows("0")) + + tk.MustExec("create table t2 (a int) shard_row_id_bits = 2 pre_split_regions = 2;") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t2 values (?);", i) + } + rows = tk.MustQuery("select * from t tablesample regions(), t2 tablesample regions();").Rows() + require.Len(t, rows, 16) + tk.MustQuery("select count(*) from t tablesample regions();").Check(testkit.Rows("4")) + tk.MustExec("drop table t2;") +} + +func TestTableSampleNoSplitTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + atomic.StoreUint32(&ddl.EnableSplitTableRegion, 0) + tk.MustExec("drop table if exists t1;") + tk.MustExec("drop table if exists t2;") + tk.MustExec("create table t1 (id int primary key);") + tk.MustExec("create table t2 (id int primary key);") + tk.MustExec("insert into t2 values(1);") + rows := tk.MustQuery("select * from t1 tablesample regions();").Rows() + rows2 := tk.MustQuery("select * from t2 tablesample regions();").Rows() + require.Len(t, rows, 0) + require.Len(t, rows2, 1) +} + +func TestTableSamplePlan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a bigint, b int default 10);") + tk.MustExec("split table t between (0) and (100000) regions 4;") + tk.MustExec("insert into t(a) values (1), (2), (3);") + rows := tk.MustQuery("explain analyze select a from t tablesample regions();").Rows() + require.Len(t, rows, 2) + tableSample := fmt.Sprintf("%v", rows[1]) + require.Regexp(t, ".*TableSample.*", tableSample) +} + +func TestTableSampleSchema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + // Clustered index + tk.MustExec("create table t (a varchar(255) primary key, b bigint);") + tk.MustExec("insert into t values ('b', 100), ('y', 100);") + tk.MustQuery("split table t between ('a') and ('z') regions 2;").Check(testkit.Rows("1 1")) + tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("b", "y")) + + tk.MustExec("drop table t;") + tk.MustExec("create table t (a varchar(255), b int, c decimal, primary key (a, b, c));") + tk.MustQuery("split table t between ('a', 0, 0) and ('z', 100, 100) regions 2;").Check(testkit.Rows("1 1")) + tk.MustExec("insert into t values ('b', 10, 100), ('y', 100, 10);") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("b 10 100", "y 100 10")) + + // PKIsHandle + tk.MustExec("drop table t;") + tk.MustExec("create table t (a bigint primary key, b int default 10);") + tk.MustQuery("split table t between (1) and (100000) regions 4;").Check(testkit.Rows("3 1")) + tk.MustExec("insert into t(a) values (200), (25600), (50300), (99900), (99901)") + tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("200", "25600", "50300", "99900")) + + // _tidb_rowid + tk.MustExec("drop table t;") + tk.MustExec("create table t (a bigint, b int default 10);") + tk.MustQuery("split table t between (0) and (100000) regions 4;").Check(testkit.Rows("3 1")) + tk.MustExec("insert into t(a) values (1), (2), (3);") + tk.MustQuery("select a from t tablesample regions();").Check(testkit.Rows("1")) +} + +func TestTableSampleInvalid(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int, b varchar(255));") + tk.MustExec("insert into t values (1, 'abc');") + tk.MustExec("create view v as select * from t;") + tk.MustGetErrCode("select * from v tablesample regions();", errno.ErrInvalidTableSample) + tk.MustGetErrCode("select * from information_schema.tables tablesample regions();", errno.ErrInvalidTableSample) + + tk.MustGetErrCode("select a from t tablesample system();", errno.ErrInvalidTableSample) + tk.MustGetErrCode("select a from t tablesample bernoulli(10 percent);", errno.ErrInvalidTableSample) + tk.MustGetErrCode("select a from t as t1 tablesample regions(), t as t2 tablesample system();", errno.ErrInvalidTableSample) + tk.MustGetErrCode("select a from t tablesample ();", errno.ErrInvalidTableSample) +} + +func TestTableSampleWithTiDBRowID(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int, b varchar(255));") + tk.MustExec("insert into t values (1, 'abc');") + tk.MustQuery("select _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1")) + tk.MustQuery("select a, _tidb_rowid from t tablesample regions();").Check(testkit.Rows("1 1")) + tk.MustQuery("select _tidb_rowid, b from t tablesample regions();").Check(testkit.Rows("1 abc")) + tk.MustQuery("select b, _tidb_rowid, a from t tablesample regions();").Check(testkit.Rows("abc 1 1")) +} + +func TestTableSampleWithPartition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int, b varchar(255), primary key (a)) partition by hash(a) partitions 2;") + tk.MustExec("insert into t values (1, '1'), (2, '2'), (3, '3');") + rows := tk.MustQuery("select * from t tablesample regions();").Rows() + require.Len(t, rows, 2) + + tk.MustExec("delete from t;") + tk.MustExec("insert into t values (1, '1');") + rows = tk.MustQuery("select * from t partition (p0) tablesample regions();").Rows() + require.Len(t, rows, 0) + rows = tk.MustQuery("select * from t partition (p1) tablesample regions();").Rows() + require.Len(t, rows, 1) + + // Test https://github.com/pingcap/tidb/issues/27349. + tk.MustExec("drop table if exists t;") + tk.MustExec(`create table t (a int, b int, unique key idx(a)) partition by range (a) ( + partition p0 values less than (0), + partition p1 values less than (10), + partition p2 values less than (30), + partition p3 values less than (maxvalue));`) + tk.MustExec("insert into t values (2, 2), (31, 31), (12, 12);") + tk.MustQuery("select _tidb_rowid from t tablesample regions() order by _tidb_rowid;"). + Check(testkit.Rows("1", "2", "3")) // The order of _tidb_rowid should be correct. +} + +func TestTableSampleGeneratedColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int primary key, b int as (a + 1), c int as (b + 1), d int as (c + 1));") + tk.MustQuery("split table t between (0) and (10000) regions 4;").Check(testkit.Rows("3 1")) + tk.MustExec("insert into t(a) values (1), (2), (2999), (4999), (9999);") + tk.MustQuery("select a from t tablesample regions()").Check(testkit.Rows("1", "2999", "9999")) + tk.MustQuery("select c from t tablesample regions()").Check(testkit.Rows("3", "3001", "10001")) + tk.MustQuery("select a, b from t tablesample regions()").Check( + testkit.Rows("1 2", "2999 3000", "9999 10000")) + tk.MustQuery("select d, c from t tablesample regions()").Check( + testkit.Rows("4 3", "3002 3001", "10002 10001")) + tk.MustQuery("select a, d from t tablesample regions()").Check( + testkit.Rows("1 4", "2999 3002", "9999 10002")) +} + +func TestTableSampleUnionScanIgnorePendingKV(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int primary key);") + tk.MustQuery("split table t between (0) and (40000) regions 4;").Check(testkit.Rows("3 1")) + tk.MustExec("insert into t values (1), (1000), (10002);") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) + + tk.MustExec("begin;") + tk.MustExec("insert into t values (20006), (50000);") + // The memory DB values in transactions are ignored. + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) + tk.MustExec("delete from t where a = 1;") + // The memory DB values in transactions are ignored. + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) + tk.MustExec("commit;") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1000", "10002", "20006", "50000")) +} + +func TestTableSampleTransactionConsistency(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk2 := createSampleTestkit(t, store) + + tk.MustExec("create table t (a int primary key);") + tk.MustQuery("split table t between (0) and (40000) regions 4;").Check(testkit.Rows("3 1")) + tk.MustExec("insert into t values (1), (1000), (10002);") + + tk.MustExec("begin;") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) + tk2.MustExec("insert into t values (20006), (50000);") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002")) + tk.MustExec("commit;") + tk.MustQuery("select * from t tablesample regions();").Check(testkit.Rows("1", "10002", "20006", "50000")) +} + +func TestTableSampleNotSupportedPlanWarning(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int primary key, b int, c varchar(255));") + tk.MustQuery("split table t between (0) and (10000) regions 5;").Check(testkit.Rows("4 1")) + tk.MustExec("insert into t values (1000, 1, '1'), (1001, 1, '1'), (2100, 2, '2'), (4500, 3, '3');") + + tk.MustExec("create index idx_0 on t (b);") + tk.MustQuery("select a from t tablesample regions() order by a;").Check( + testkit.Rows("1000", "2100", "4500")) + tk.MustQuery("select a from t use index (idx_0) tablesample regions() order by a;").Check( + testkit.Rows("1000", "1001", "2100", "4500")) + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 8128 Invalid TABLESAMPLE: plan not supported")) +} + +func TestMaxChunkSize(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := createSampleTestkit(t, store) + tk.MustExec("create table t (a int) shard_row_id_bits = 2 pre_split_regions = 2;") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?);", i) + } + tk.Session().GetSessionVars().MaxChunkSize = 1 + rows := tk.MustQuery("select * from t tablesample regions();").Rows() + require.Len(t, rows, 4) +} diff --git a/executor/select_into.go b/pkg/executor/select_into.go similarity index 96% rename from executor/select_into.go rename to pkg/executor/select_into.go index 5c35be5d2e678..e4d0ab450d85b 100644 --- a/executor/select_into.go +++ b/pkg/executor/select_into.go @@ -23,12 +23,12 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // SelectIntoExec represents a SelectInto executor. diff --git a/executor/select_into_test.go b/pkg/executor/select_into_test.go similarity index 98% rename from executor/select_into_test.go rename to pkg/executor/select_into_test.go index 7ed460bcd7ebe..0737683fe1b44 100644 --- a/executor/select_into_test.go +++ b/pkg/executor/select_into_test.go @@ -22,10 +22,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/set.go b/pkg/executor/set.go new file mode 100644 index 0000000000000..aa6cfaf796d3d --- /dev/null +++ b/pkg/executor/set.go @@ -0,0 +1,331 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + disttaskutil "github.com/pingcap/tidb/pkg/util/disttask" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// SetExecutor executes set statement. +type SetExecutor struct { + exec.BaseExecutor + + vars []*expression.VarAssignment + done bool +} + +// Next implements the Executor Next interface. +func (e *SetExecutor) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.done { + return nil + } + e.done = true + sessionVars := e.Ctx().GetSessionVars() + for _, v := range e.vars { + // Variable is case insensitive, we use lower case. + if v.Name == ast.SetNames || v.Name == ast.SetCharset { + // This is set charset stmt. + if v.IsDefault { + err := e.setCharset(mysql.DefaultCharset, "", v.Name == ast.SetNames) + if err != nil { + return err + } + continue + } + dt, err := v.Expr.(*expression.Constant).Eval(chunk.Row{}) + if err != nil { + return err + } + cs := dt.GetString() + var co string + if v.ExtendValue != nil { + co = v.ExtendValue.Value.GetString() + } + err = e.setCharset(cs, co, v.Name == ast.SetNames) + if err != nil { + return err + } + continue + } + name := strings.ToLower(v.Name) + if !v.IsSystem { + // Set user variable. + value, err := v.Expr.Eval(chunk.Row{}) + if err != nil { + return err + } + if value.IsNull() { + sessionVars.UnsetUserVar(name) + } else { + sessionVars.SetUserVarVal(name, value) + sessionVars.SetUserVarType(name, v.Expr.GetType()) + } + continue + } + + if err := e.setSysVariable(ctx, name, v); err != nil { + return err + } + } + return nil +} + +func (e *SetExecutor) setSysVariable(ctx context.Context, name string, v *expression.VarAssignment) error { + sessionVars := e.Ctx().GetSessionVars() + sysVar := variable.GetSysVar(name) + if sysVar == nil { + if variable.IsRemovedSysVar(name) { + return nil // removed vars permit parse-but-ignore + } + return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + + if sysVar.RequireDynamicPrivileges != nil { + semEnabled := sem.IsEnabled() + pm := privilege.GetPrivilegeManager(e.Ctx()) + privs := sysVar.RequireDynamicPrivileges(v.IsGlobal, semEnabled) + for _, priv := range privs { + if !pm.RequestDynamicVerification(sessionVars.ActiveRoles, priv, false) { + msg := priv + if !semEnabled { + msg = "SUPER or " + msg + } + return core.ErrSpecificAccessDenied.GenWithStackByArgs(msg) + } + } + } + + if sysVar.IsNoop && !variable.EnableNoopVariables.Load() { + // The variable is a noop. For compatibility we allow it to still + // be changed, but we append a warning since users might be expecting + // something that's not going to happen. + sessionVars.StmtCtx.AppendWarning(exeerrors.ErrSettingNoopVariable.GenWithStackByArgs(sysVar.Name)) + } + if sysVar.HasInstanceScope() && !v.IsGlobal && sessionVars.EnableLegacyInstanceScope { + // For backward compatibility we will change the v.IsGlobal to true, + // and append a warning saying this will not be supported in future. + v.IsGlobal = true + sessionVars.StmtCtx.AppendWarning(exeerrors.ErrInstanceScope.GenWithStackByArgs(sysVar.Name)) + } + + if v.IsGlobal { + valStr, err := e.getVarValue(ctx, v, sysVar) + if err != nil { + return err + } + err = sessionVars.GlobalVarsAccessor.SetGlobalSysVar(ctx, name, valStr) + if err != nil { + return err + } + err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { + auditPlugin := plugin.DeclareAuditManifest(p.Manifest) + if auditPlugin.OnGlobalVariableEvent != nil { + auditPlugin.OnGlobalVariableEvent(context.Background(), e.Ctx().GetSessionVars(), name, valStr) + } + return nil + }) + logutil.BgLogger().Info("set global var", zap.Uint64("conn", sessionVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) + if name == variable.TiDBServiceScope { + dom := domain.GetDomain(e.Ctx()) + serverID := disttaskutil.GenerateSubtaskExecID(ctx, dom.DDL().GetID()) + _, err = e.Ctx().(sqlexec.SQLExecutor).ExecuteInternal(ctx, + `update mysql.dist_framework_meta + set role = %? + where host = %?`, valStr, serverID) + } + return err + } + // Set session variable + valStr, err := e.getVarValue(ctx, v, nil) + if err != nil { + return err + } + getSnapshotTSByName := func() uint64 { + if name == variable.TiDBSnapshot { + return sessionVars.SnapshotTS + } else if name == variable.TiDBTxnReadTS { + return sessionVars.TxnReadTS.PeakTxnReadTS() + } + return 0 + } + oldSnapshotTS := getSnapshotTSByName() + fallbackOldSnapshotTS := func() { + if name == variable.TiDBSnapshot { + sessionVars.SnapshotTS = oldSnapshotTS + } else if name == variable.TiDBTxnReadTS { + sessionVars.TxnReadTS.SetTxnReadTS(oldSnapshotTS) + } + } + if sessionVars.InTxn() { + if name == variable.TxnIsolationOneShot || + name == variable.TiDBTxnReadTS { + return errors.Trace(exeerrors.ErrCantChangeTxCharacteristics) + } + if name == variable.TiDBSnapshot && sessionVars.TxnCtx.IsStaleness { + return errors.Trace(exeerrors.ErrCantChangeTxCharacteristics) + } + } + err = sessionVars.SetSystemVar(name, valStr) + if err != nil { + return err + } + newSnapshotTS := getSnapshotTSByName() + newSnapshotIsSet := newSnapshotTS > 0 && newSnapshotTS != oldSnapshotTS + if newSnapshotIsSet { + if name == variable.TiDBTxnReadTS { + err = sessionctx.ValidateStaleReadTS(ctx, e.Ctx(), newSnapshotTS) + } else { + err = sessionctx.ValidateSnapshotReadTS(ctx, e.Ctx(), newSnapshotTS) + // Also check gc safe point for snapshot read. + // We don't check snapshot with gc safe point for read_ts + // Client-go will automatically check the snapshotTS with gc safe point. It's unnecessary to check gc safe point during set executor. + if err == nil { + err = gcutil.ValidateSnapshot(e.Ctx(), newSnapshotTS) + } + } + if err != nil { + fallbackOldSnapshotTS() + return err + } + } + + err = e.loadSnapshotInfoSchemaIfNeeded(name, newSnapshotTS) + if err != nil { + fallbackOldSnapshotTS() + return err + } + // Clients are often noisy in setting session variables such as + // autocommit, timezone, query cache + logutil.BgLogger().Debug("set session var", zap.Uint64("conn", sessionVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) + return nil +} + +func (e *SetExecutor) setCharset(cs, co string, isSetName bool) error { + var err error + sessionVars := e.Ctx().GetSessionVars() + if co == "" { + if co, err = charset.GetDefaultCollation(cs); err != nil { + return err + } + } else { + var coll *charset.Collation + if coll, err = collate.GetCollationByName(co); err != nil { + return err + } + if coll.CharsetName != cs { + return charset.ErrCollationCharsetMismatch.GenWithStackByArgs(coll.Name, cs) + } + } + if isSetName { + for _, v := range variable.SetNamesVariables { + if err = sessionVars.SetSystemVar(v, cs); err != nil { + return errors.Trace(err) + } + } + return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, co)) + } + // Set charset statement, see also https://dev.mysql.com/doc/refman/8.0/en/set-character-set.html. + for _, v := range variable.SetCharsetVariables { + if err = sessionVars.SetSystemVar(v, cs); err != nil { + return errors.Trace(err) + } + } + csDB, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CharsetDatabase) + if err != nil { + return err + } + coDB, err := sessionVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CollationDatabase) + if err != nil { + return err + } + err = sessionVars.SetSystemVar(variable.CharacterSetConnection, csDB) + if err != nil { + return errors.Trace(err) + } + return errors.Trace(sessionVars.SetSystemVar(variable.CollationConnection, coDB)) +} + +func (e *SetExecutor) getVarValue(ctx context.Context, v *expression.VarAssignment, sysVar *variable.SysVar) (value string, err error) { + if v.IsDefault { + // To set a SESSION variable to the GLOBAL value or a GLOBAL value + // to the compiled-in MySQL default value, use the DEFAULT keyword. + // See http://dev.mysql.com/doc/refman/5.7/en/set-statement.html + if sysVar != nil { + return sysVar.Value, nil + } + return e.Ctx().GetSessionVars().GetGlobalSystemVar(ctx, v.Name) + } + nativeVal, err := v.Expr.Eval(chunk.Row{}) + if err != nil || nativeVal.IsNull() { + return "", err + } + + value, err = nativeVal.ToString() + if err != nil { + return "", err + } + + // We need to clone the string because the value is constructed by `hack.String` in Datum which reuses the under layer `[]byte` + // instead of allocating some new spaces. The `[]byte` in Datum will be reused in `chunk.Chunk` by different statements in session. + // If we do not clone the value, the system variable will have a risk to be modified by other statements. + return strings.Clone(value), nil +} + +func (e *SetExecutor) loadSnapshotInfoSchemaIfNeeded(name string, snapshotTS uint64) error { + if name != variable.TiDBSnapshot && name != variable.TiDBTxnReadTS { + return nil + } + vars := e.Ctx().GetSessionVars() + if snapshotTS == 0 { + vars.SnapshotInfoschema = nil + return nil + } + logutil.BgLogger().Info("load snapshot info schema", + zap.Uint64("conn", vars.ConnectionID), + zap.Uint64("SnapshotTS", snapshotTS)) + dom := domain.GetDomain(e.Ctx()) + snapInfo, err := dom.GetSnapshotInfoSchema(snapshotTS) + if err != nil { + return err + } + + vars.SnapshotInfoschema = temptable.AttachLocalTemporaryTableInfoSchema(e.Ctx(), snapInfo) + return nil +} diff --git a/executor/set_config.go b/pkg/executor/set_config.go similarity index 92% rename from executor/set_config.go rename to pkg/executor/set_config.go index 8f9f73af0c47a..ca9cebd4dd949 100644 --- a/executor/set_config.go +++ b/pkg/executor/set_config.go @@ -24,18 +24,18 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // SetConfigExec executes 'SET CONFIG' statement. diff --git a/pkg/executor/set_test.go b/pkg/executor/set_test.go new file mode 100644 index 0000000000000..8850d53eeea74 --- /dev/null +++ b/pkg/executor/set_test.go @@ -0,0 +1,2171 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "strconv" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/stretchr/testify/require" +) + +func TestSetVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("SET @a = 1;") + tk.MustExec(`SET @a = "1";`) + tk.MustExec("SET @a = null;") + tk.MustExec("SET @@global.autocommit = 1;") + + // TODO: this test case should returns error. + // err := tk.ExecToErr("SET @@global.autocommit = null;") + // c.Assert(err, NotNil) + + tk.MustExec("SET @@autocommit = 1;") + require.Error(t, tk.ExecToErr("SET @@autocommit = null;")) + require.Error(t, tk.ExecToErr("SET @@date_format = 1;")) + require.Error(t, tk.ExecToErr("SET @@rewriter_enabled = 1;")) + require.Error(t, tk.ExecToErr("SET xxx = abcd;")) + require.Error(t, tk.ExecToErr("SET @@global.a = 1;")) + require.Error(t, tk.ExecToErr("SET @@global.timestamp = 1;")) + + // For issue 998 + tk.MustExec("SET @issue998a=1, @issue998b=5;") + tk.MustQuery(`select @issue998a, @issue998b;`).Check(testkit.Rows("1 5")) + tk.MustExec("SET @@autocommit=0, @issue998a=2;") + tk.MustQuery(`select @issue998a, @@autocommit;`).Check(testkit.Rows("2 0")) + tk.MustExec("SET @@global.autocommit=1, @issue998b=6;") + tk.MustQuery(`select @issue998b, @@global.autocommit;`).Check(testkit.Rows("6 1")) + + // For issue 4302 + tk.MustExec("use test;drop table if exists x;create table x(a int);insert into x value(1);") + tk.MustExec("SET @issue4302=(select a from x limit 1);") + tk.MustQuery(`select @issue4302;`).Check(testkit.Rows("1")) + + // Set default + // {ScopeGlobal | ScopeSession, "low_priority_updates", "OFF"}, + // For global var + tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("0")) + tk.MustExec(`set @@global.low_priority_updates="ON";`) + tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("1")) + tk.MustExec(`set @@global.low_priority_updates=DEFAULT;`) // It will be set to default var value. + tk.MustQuery(`select @@global.low_priority_updates;`).Check(testkit.Rows("0")) + // For session + tk.MustQuery(`select @@session.low_priority_updates;`).Check(testkit.Rows("0")) + tk.MustExec(`set @@global.low_priority_updates="ON";`) + tk.MustExec(`set @@session.low_priority_updates=DEFAULT;`) // It will be set to global var value. + tk.MustQuery(`select @@session.low_priority_updates;`).Check(testkit.Rows("1")) + + // For mysql jdbc driver issue. + tk.MustQuery(`select @@session.tx_read_only;`).Check(testkit.Rows("0")) + + // Test session variable states. + vars := tk.Session().(sessionctx.Context).GetSessionVars() + require.NoError(t, tk.Session().CommitTxn(context.TODO())) + tk.MustExec("set @@autocommit = 1") + require.False(t, vars.InTxn()) + require.True(t, vars.IsAutocommit()) + tk.MustExec("set @@autocommit = 0") + require.False(t, vars.IsAutocommit()) + + tk.MustExec("set @@sql_mode = 'strict_trans_tables'") + require.True(t, vars.StrictSQLMode) + tk.MustExec("set @@sql_mode = ''") + require.False(t, vars.StrictSQLMode) + + tk.MustExec("set names utf8") + charset, collation := vars.GetCharsetInfo() + require.Equal(t, "utf8", charset) + require.Equal(t, "utf8_bin", collation) + + tk.MustExec("set names latin1 collate latin1_bin") + charset, collation = vars.GetCharsetInfo() + require.Equal(t, "latin1", charset) + require.Equal(t, "latin1_bin", collation) + + tk.MustExec("set names utf8 collate default") + charset, collation = vars.GetCharsetInfo() + require.Equal(t, "utf8", charset) + require.Equal(t, "utf8_bin", collation) + + expectErrMsg := "[ddl:1273]Unknown collation: 'non_exist_collation'" + tk.MustGetErrMsg("set names utf8 collate non_exist_collation", expectErrMsg) + tk.MustGetErrMsg("set @@session.collation_server='non_exist_collation'", expectErrMsg) + tk.MustGetErrMsg("set @@session.collation_database='non_exist_collation'", expectErrMsg) + tk.MustGetErrMsg("set @@session.collation_connection='non_exist_collation'", expectErrMsg) + tk.MustGetErrMsg("set @@global.collation_server='non_exist_collation'", expectErrMsg) + tk.MustGetErrMsg("set @@global.collation_database='non_exist_collation'", expectErrMsg) + tk.MustGetErrMsg("set @@global.collation_connection='non_exist_collation'", expectErrMsg) + + expectErrMsg = "[parser:1115]Unknown character set: 'boguscharsetname'" + tk.MustGetErrMsg("set names boguscharsetname", expectErrMsg) + + tk.MustExec("set character_set_results = NULL") + tk.MustQuery("select @@character_set_results").Check(testkit.Rows("")) + + tk.MustExec("set @@global.ddl_slow_threshold=12345") + tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows("12345")) + require.Equal(t, uint32(12345), variable.DDLSlowOprThreshold) + tk.MustExec("set session ddl_slow_threshold=\"54321\"") + tk.MustQuery("show variables like 'ddl_slow_threshold'").Check(testkit.Rows("ddl_slow_threshold 54321")) + require.Equal(t, uint32(54321), variable.DDLSlowOprThreshold) + tk.MustExec("set @@global.ddl_slow_threshold=-1") + tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBDDLSlowOprThreshold))) + require.Equal(t, uint32(variable.DefTiDBDDLSlowOprThreshold), variable.DDLSlowOprThreshold) + require.Error(t, tk.ExecToErr("set @@global.ddl_slow_threshold=abc")) + tk.MustQuery("select @@global.ddl_slow_threshold").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBDDLSlowOprThreshold))) + require.Equal(t, uint32(variable.DefTiDBDDLSlowOprThreshold), variable.DDLSlowOprThreshold) + + // Test set transaction isolation level, which is equivalent to setting variable "tx_isolation". + tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + // error + err := tk.ExecToErr("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + // Fails + err = tk.ExecToErr("SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) + + // test synonyms variables + tk.MustExec("SET SESSION tx_isolation = 'READ-COMMITTED'") + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + err = tk.ExecToErr("SET SESSION tx_isolation = 'READ-UNCOMMITTED'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + // fails + err = tk.ExecToErr("SET SESSION transaction_isolation = 'SERIALIZABLE'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + // fails + err = tk.ExecToErr("SET GLOBAL transaction_isolation = 'SERIALIZABLE'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) + + err = tk.ExecToErr("SET GLOBAL transaction_isolation = 'READ-UNCOMMITTED'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) + + err = tk.ExecToErr("SET GLOBAL tx_isolation = 'SERIALIZABLE'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("REPEATABLE-READ")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("REPEATABLE-READ")) + + // Even the transaction fail, set session variable would success. + tk.MustExec("BEGIN") + tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") + require.Error(t, tk.ExecToErr(`INSERT INTO t VALUES ("sdfsdf")`)) + tk.MustExec("COMMIT") + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + + tk.MustExec("set global avoid_temporal_upgrade = on") + tk.MustQuery(`select @@global.avoid_temporal_upgrade;`).Check(testkit.Rows("1")) + tk.MustExec("set @@global.avoid_temporal_upgrade = off") + tk.MustQuery(`select @@global.avoid_temporal_upgrade;`).Check(testkit.Rows("0")) + tk.MustExec("set session sql_log_bin = on") + tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("1")) + tk.MustExec("set sql_log_bin = off") + tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("0")) + tk.MustExec("set @@sql_log_bin = on") + tk.MustQuery(`select @@session.sql_log_bin;`).Check(testkit.Rows("1")) + + binlogValue := "0" + if config.GetGlobalConfig().Binlog.Enable { + binlogValue = "1" + } + tk.MustQuery(`select @@global.log_bin;`).Check(testkit.Rows(binlogValue)) + tk.MustQuery(`select @@log_bin;`).Check(testkit.Rows(binlogValue)) + + tk.MustExec("set @@tidb_general_log = 1") + tk.MustExec("set @@tidb_general_log = 0") + + tk.MustExec("set @@tidb_pprof_sql_cpu = 1") + tk.MustExec("set @@tidb_pprof_sql_cpu = 0") + + tk.MustExec(`set @@block_encryption_mode = "aes-128-ecb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-ecb")) + tk.MustExec(`set @@block_encryption_mode = "aes-192-ecb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-ecb")) + tk.MustExec(`set @@block_encryption_mode = "aes-256-ecb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-ecb")) + tk.MustExec(`set @@block_encryption_mode = "aes-128-cbc"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-cbc")) + tk.MustExec(`set @@block_encryption_mode = "aes-192-cbc"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-cbc")) + tk.MustExec(`set @@block_encryption_mode = "aes-256-cbc"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cbc")) + tk.MustExec(`set @@block_encryption_mode = "aes-128-ofb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-ofb")) + tk.MustExec(`set @@block_encryption_mode = "aes-192-ofb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-ofb")) + tk.MustExec(`set @@block_encryption_mode = "aes-256-ofb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-ofb")) + tk.MustExec(`set @@block_encryption_mode = "aes-128-cfb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-128-cfb")) + tk.MustExec(`set @@block_encryption_mode = "aes-192-cfb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-192-cfb")) + tk.MustExec(`set @@block_encryption_mode = "aes-256-cfb"`) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cfb")) + require.Error(t, tk.ExecToErr("set @@block_encryption_mode = 'abc'")) + tk.MustQuery(`select @@block_encryption_mode;`).Check(testkit.Rows("aes-256-cfb")) + + tk.MustExec(`set @@global.tidb_force_priority = "no_priority"`) + tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("NO_PRIORITY")) + tk.MustExec(`set @@global.tidb_force_priority = "low_priority"`) + tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("LOW_PRIORITY")) + tk.MustExec(`set @@global.tidb_force_priority = "high_priority"`) + tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("HIGH_PRIORITY")) + tk.MustExec(`set @@global.tidb_force_priority = "delayed"`) + tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("DELAYED")) + require.Error(t, tk.ExecToErr("set global tidb_force_priority = 'abc'")) + tk.MustQuery(`select @@global.tidb_force_priority;`).Check(testkit.Rows("DELAYED")) + + tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_low"`) + tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_LOW")) + tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_normal"`) + tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_NORMAL")) + tk.MustExec(`set @@session.tidb_ddl_reorg_priority = "priority_high"`) + tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_HIGH")) + require.Error(t, tk.ExecToErr("set session tidb_ddl_reorg_priority = 'abc'")) + tk.MustQuery(`select @@session.tidb_ddl_reorg_priority;`).Check(testkit.Rows("PRIORITY_HIGH")) + + tk.MustExec("set tidb_opt_write_row_id = 1") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_opt_write_row_id = 0") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) + tk.MustExec("set tidb_opt_write_row_id = true") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_opt_write_row_id = false") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) + tk.MustExec("set tidb_opt_write_row_id = On") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_opt_write_row_id = Off") + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) + require.Error(t, tk.ExecToErr("set tidb_opt_write_row_id = 'abc'")) + tk.MustQuery(`select @@session.tidb_opt_write_row_id;`).Check(testkit.Rows("0")) + + tk.MustExec("set tidb_checksum_table_concurrency = 42") + tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows("42")) + require.Error(t, tk.ExecToErr("set tidb_checksum_table_concurrency = 'abc'")) + tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows("42")) + tk.MustExec("set tidb_checksum_table_concurrency = 257") + tk.MustQuery(`select @@tidb_checksum_table_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) + + tk.MustExec("set tidb_build_stats_concurrency = 42") + tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows("42")) + tk.MustExec("set tidb_build_sampling_stats_concurrency = 42") + tk.MustQuery(`select @@tidb_build_sampling_stats_concurrency;`).Check(testkit.Rows("42")) + require.Error(t, tk.ExecToErr("set tidb_build_sampling_stats_concurrency = 'abc'")) + require.Error(t, tk.ExecToErr("set tidb_build_stats_concurrency = 'abc'")) + tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows("42")) + tk.MustExec("set tidb_build_stats_concurrency = 257") + tk.MustQuery(`select @@tidb_build_stats_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) + tk.MustExec("set tidb_build_sampling_stats_concurrency = 257") + tk.MustQuery(`select @@tidb_build_sampling_stats_concurrency;`).Check(testkit.Rows(strconv.Itoa(variable.MaxConfigurableConcurrency))) + + tk.MustExec(`set tidb_partition_prune_mode = "static"`) + tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("static")) + tk.MustExec(`set tidb_partition_prune_mode = "dynamic"`) + tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) + tk.MustExec(`set tidb_partition_prune_mode = "static-only"`) + tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("static")) + tk.MustExec(`set tidb_partition_prune_mode = "dynamic-only"`) + tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) + require.Error(t, tk.ExecToErr("set tidb_partition_prune_mode = 'abc'")) + tk.MustQuery(`select @@tidb_partition_prune_mode;`).Check(testkit.Rows("dynamic")) + + tk.MustExec("set tidb_constraint_check_in_place = 1") + tk.MustQuery(`select @@session.tidb_constraint_check_in_place;`).Check(testkit.Rows("1")) + tk.MustExec("set global tidb_constraint_check_in_place = 0") + tk.MustQuery(`select @@global.tidb_constraint_check_in_place;`).Check(testkit.Rows("0")) + + tk.MustExec("set tidb_batch_commit = 0") + tk.MustQuery("select @@session.tidb_batch_commit;").Check(testkit.Rows("0")) + tk.MustExec("set tidb_batch_commit = 1") + tk.MustQuery("select @@session.tidb_batch_commit;").Check(testkit.Rows("1")) + require.Error(t, tk.ExecToErr("set global tidb_batch_commit = 0")) + require.Error(t, tk.ExecToErr("set global tidb_batch_commit = 2")) + + // test skip isolation level check: init + tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") + tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") + tk.MustExec("SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED") + tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED") + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + // test skip isolation level check: error + err = tk.ExecToErr("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + err = tk.ExecToErr("SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-COMMITTED")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-COMMITTED")) + + // test skip isolation level check: success + tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 1") + tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 1") + tk.MustExec("SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 8048 The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error")) + tk.MustQuery("select @@session.tx_isolation").Check(testkit.Rows("SERIALIZABLE")) + tk.MustQuery("select @@session.transaction_isolation").Check(testkit.Rows("SERIALIZABLE")) + + // test skip isolation level check: success + tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") + tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 1") + tk.MustExec("SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 8048 The isolation level 'READ-UNCOMMITTED' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error")) + tk.MustQuery("select @@global.tx_isolation").Check(testkit.Rows("READ-UNCOMMITTED")) + tk.MustQuery("select @@global.transaction_isolation").Check(testkit.Rows("READ-UNCOMMITTED")) + + // test skip isolation level check: reset + tk.MustExec("SET GLOBAL transaction_isolation='REPEATABLE-READ'") // should reset tx_isolation back to rr before reset tidb_skip_isolation_level_check + tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") + tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") + + // test for tidb_wait_split_region_finish + tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_wait_split_region_finish = 1") + tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_wait_split_region_finish = 0") + tk.MustQuery(`select @@session.tidb_wait_split_region_finish;`).Check(testkit.Rows("0")) + + // test for tidb_scatter_region + tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("0")) + tk.MustExec("set global tidb_scatter_region = 1") + tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("1")) + tk.MustExec("set global tidb_scatter_region = 0") + tk.MustQuery(`select @@global.tidb_scatter_region;`).Check(testkit.Rows("0")) + require.Error(t, tk.ExecToErr("set session tidb_scatter_region = 0")) + require.Error(t, tk.ExecToErr(`select @@session.tidb_scatter_region;`)) + + // test for tidb_wait_split_region_timeout + tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows(strconv.Itoa(variable.DefWaitSplitRegionTimeout))) + tk.MustExec("set tidb_wait_split_region_timeout = 1") + tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows("1")) + tk.MustExec("set tidb_wait_split_region_timeout = 0") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_wait_split_region_timeout value: '0'")) + tk.MustQuery(`select @@tidb_wait_split_region_timeout`).Check(testkit.Rows("1")) + + tk.MustQuery(`select @@session.tidb_wait_split_region_timeout;`).Check(testkit.Rows("1")) + + tk.MustExec("set session tidb_backoff_weight = 3") + tk.MustQuery("select @@session.tidb_backoff_weight;").Check(testkit.Rows("3")) + tk.MustExec("set session tidb_backoff_weight = 20") + tk.MustQuery("select @@session.tidb_backoff_weight;").Check(testkit.Rows("20")) + tk.MustExec("set session tidb_backoff_weight = -1") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_backoff_weight value: '-1'")) + tk.MustExec("set global tidb_backoff_weight = 0") + tk.MustQuery("select @@global.tidb_backoff_weight;").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_backoff_weight = 10") + tk.MustQuery("select @@global.tidb_backoff_weight;").Check(testkit.Rows("10")) + + tk.MustExec("set @@tidb_expensive_query_time_threshold=70") + tk.MustQuery("select @@tidb_expensive_query_time_threshold;").Check(testkit.Rows("70")) + + tk.MustExec("set @@tidb_expensive_txn_time_threshold=120") + tk.MustQuery("select @@tidb_expensive_txn_time_threshold;").Check(testkit.Rows("120")) + + tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("0")) + tk.MustExec("set @@global.tidb_store_limit = 100") + tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("100")) + tk.MustExec("set @@global.tidb_store_limit = 0") + tk.MustExec("set global tidb_store_limit = 10000") + tk.MustQuery("select @@global.tidb_store_limit;").Check(testkit.Rows("10000")) + + tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("16384")) + tk.MustExec("set @@global.tidb_txn_commit_batch_size = 100") + tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("100")) + tk.MustExec("set @@global.tidb_txn_commit_batch_size = 0") + tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_txn_commit_batch_size = 100") + tk.MustQuery("select @@global.tidb_txn_commit_batch_size;").Check(testkit.Rows("100")) + + tk.MustQuery("select @@session.tidb_metric_query_step;").Check(testkit.Rows("60")) + tk.MustExec("set @@session.tidb_metric_query_step = 120") + tk.MustExec("set @@session.tidb_metric_query_step = 9") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_metric_query_step value: '9'")) + tk.MustQuery("select @@session.tidb_metric_query_step;").Check(testkit.Rows("10")) + + tk.MustQuery("select @@session.tidb_metric_query_range_duration;").Check(testkit.Rows("60")) + tk.MustExec("set @@session.tidb_metric_query_range_duration = 120") + tk.MustExec("set @@session.tidb_metric_query_range_duration = 9") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_metric_query_range_duration value: '9'")) + tk.MustQuery("select @@session.tidb_metric_query_range_duration;").Check(testkit.Rows("10")) + + tk.MustExec("set @@cte_max_recursion_depth=100") + tk.MustQuery("select @@cte_max_recursion_depth").Check(testkit.Rows("100")) + tk.MustExec("set @@global.cte_max_recursion_depth=100") + tk.MustQuery("select @@global.cte_max_recursion_depth").Check(testkit.Rows("100")) + tk.MustExec("set @@cte_max_recursion_depth=-1") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect cte_max_recursion_depth value: '-1'")) + tk.MustQuery("select @@cte_max_recursion_depth").Check(testkit.Rows("0")) + + // test for tidb_redact_log + tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("0")) + tk.MustExec("set global tidb_redact_log = 1") + tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("1")) + tk.MustExec("set global tidb_redact_log = 0") + tk.MustQuery(`select @@global.tidb_redact_log;`).Check(testkit.Rows("0")) + tk.MustExec("set session tidb_redact_log = 0") + tk.MustQuery(`select @@session.tidb_redact_log;`).Check(testkit.Rows("0")) + tk.MustExec("set session tidb_redact_log = 1") + tk.MustQuery(`select @@session.tidb_redact_log;`).Check(testkit.Rows("1")) + + tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("0")) + tk.MustExec("set @@session.tidb_dml_batch_size = 120") + tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("120")) + tk.MustExec("set @@session.tidb_dml_batch_size = -120") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_dml_batch_size value: '?'")) // redacted because of tidb_redact_log = 1 above + tk.MustQuery("select @@session.tidb_dml_batch_size").Check(testkit.Rows("0")) + tk.MustExec("set session tidb_redact_log = 0") + tk.MustExec("set session tidb_dml_batch_size = -120") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_dml_batch_size value: '-120'")) // without redaction + + tk.MustExec("set @@session.tidb_dml_batch_size = 120") + tk.MustExec("set @@global.tidb_dml_batch_size = 200") // now permitted due to TiDB #19809 + tk.MustQuery("select @@tidb_dml_batch_size;").Check(testkit.Rows("120")) // global only applies to new sessions + + err = tk.ExecToErr("set tidb_enable_parallel_apply=-1") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar)) + + // test for tidb_mem_quota_apply_cache + defVal := fmt.Sprintf("%v", variable.DefTiDBMemQuotaApplyCache) + tk.MustQuery(`select @@tidb_mem_quota_apply_cache`).Check(testkit.Rows(defVal)) + tk.MustExec(`set global tidb_mem_quota_apply_cache = 1`) + tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("1")) + tk.MustExec(`set global tidb_mem_quota_apply_cache = 0`) + tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("0")) + tk.MustExec(`set tidb_mem_quota_apply_cache = 123`) + tk.MustQuery(`select @@global.tidb_mem_quota_apply_cache`).Check(testkit.Rows("0")) + tk.MustQuery(`select @@tidb_mem_quota_apply_cache`).Check(testkit.Rows("123")) + + // test for tidb_mem_quota_bind_cache + defVal = fmt.Sprintf("%v", variable.DefTiDBMemQuotaBindingCache) + tk.MustQuery(`select @@tidb_mem_quota_binding_cache`).Check(testkit.Rows(defVal)) + tk.MustExec(`set global tidb_mem_quota_binding_cache = 1`) + tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("1")) + tk.MustExec(`set global tidb_mem_quota_binding_cache = 0`) + tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("0")) + tk.MustExec(`set global tidb_mem_quota_binding_cache = 123`) + tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("123")) + tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("123")) + + // test for tidb_enable_parallel_apply + tk.MustQuery(`select @@tidb_enable_parallel_apply`).Check(testkit.Rows("0")) + tk.MustExec(`set global tidb_enable_parallel_apply = 1`) + tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("1")) + tk.MustExec(`set global tidb_enable_parallel_apply = 0`) + tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("0")) + tk.MustExec(`set tidb_enable_parallel_apply=1`) + tk.MustQuery(`select @@global.tidb_enable_parallel_apply`).Check(testkit.Rows("0")) + tk.MustQuery(`select @@tidb_enable_parallel_apply`).Check(testkit.Rows("1")) + + tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) + tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) + tk.MustExec("set tidb_general_log = 1") + tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("1")) + tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log ON")) + tk.MustExec("set tidb_general_log = 0") + tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) + tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) + tk.MustExec("set tidb_general_log = on") + tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("1")) + tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log ON")) + tk.MustExec("set tidb_general_log = off") + tk.MustQuery(`select @@global.tidb_general_log;`).Check(testkit.Rows("0")) + tk.MustQuery(`show variables like 'tidb_general_log';`).Check(testkit.Rows("tidb_general_log OFF")) + require.Error(t, tk.ExecToErr("set tidb_general_log = abc")) + require.Error(t, tk.ExecToErr("set tidb_general_log = 123")) + + tk.MustExec(`SET @@character_set_results = NULL;`) + tk.MustQuery(`select @@character_set_results;`).Check(testkit.Rows("")) + + varList := []string{"character_set_server", "character_set_client", "character_set_filesystem", "character_set_database"} + for _, v := range varList { + tk.MustGetErrCode(fmt.Sprintf("SET @@global.%s = @global_start_value;", v), mysql.ErrWrongValueForVar) + tk.MustGetErrCode(fmt.Sprintf("SET @@%s = @global_start_value;", v), mysql.ErrWrongValueForVar) + tk.MustGetErrCode(fmt.Sprintf("SET @@%s = NULL;", v), mysql.ErrWrongValueForVar) + tk.MustGetErrCode(fmt.Sprintf("SET @@%s = \"\";", v), mysql.ErrWrongValueForVar) + tk.MustGetErrMsg(fmt.Sprintf("SET @@%s = \"somecharset\";", v), "Unknown charset somecharset") + // we do not support set character_set_xxx or collation_xxx to a collation id. + tk.MustGetErrMsg(fmt.Sprintf("SET @@global.%s = 46;", v), "Unknown charset 46") + tk.MustGetErrMsg(fmt.Sprintf("SET @@%s = 46;", v), "Unknown charset 46") + } + + tk.MustExec("SET SESSION tidb_enable_extended_stats = on") + tk.MustQuery("select @@session.tidb_enable_extended_stats").Check(testkit.Rows("1")) + tk.MustExec("SET SESSION tidb_enable_extended_stats = off") + tk.MustQuery("select @@session.tidb_enable_extended_stats").Check(testkit.Rows("0")) + tk.MustExec("SET GLOBAL tidb_enable_extended_stats = on") + tk.MustQuery("select @@global.tidb_enable_extended_stats").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL tidb_enable_extended_stats = off") + tk.MustQuery("select @@global.tidb_enable_extended_stats").Check(testkit.Rows("0")) + + tk.MustExec("SET SESSION tidb_allow_fallback_to_tikv = 'tiflash'") + tk.MustQuery("select @@session.tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) + tk.MustExec("SET SESSION tidb_allow_fallback_to_tikv = ''") + tk.MustQuery("select @@session.tidb_allow_fallback_to_tikv").Check(testkit.Rows("")) + tk.MustExec("SET GLOBAL tidb_allow_fallback_to_tikv = 'tiflash'") + tk.MustQuery("select @@global.tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) + tk.MustExec("SET GLOBAL tidb_allow_fallback_to_tikv = ''") + tk.MustQuery("select @@global.tidb_allow_fallback_to_tikv").Check(testkit.Rows("")) + tk.MustExec("set @@tidb_allow_fallback_to_tikv = 'tiflash, tiflash, tiflash'") + tk.MustQuery("select @@tidb_allow_fallback_to_tikv").Check(testkit.Rows("tiflash")) + + tk.MustGetErrMsg("SET SESSION tidb_allow_fallback_to_tikv = 'tikv,tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tikv,tiflash'") + tk.MustGetErrMsg("SET GLOBAL tidb_allow_fallback_to_tikv = 'tikv,tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tikv,tiflash'") + tk.MustGetErrMsg("set @@tidb_allow_fallback_to_tikv = 'tidb, tiflash, tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'tidb, tiflash, tiflash'") + tk.MustGetErrMsg("set @@tidb_allow_fallback_to_tikv = 'unknown, tiflash, tiflash'", "[variable:1231]Variable 'tidb_allow_fallback_to_tikv' can't be set to the value of 'unknown, tiflash, tiflash'") + + // Test issue #22145 + tk.MustExec(`set global sync_relay_log = "'"`) + + tk.MustExec(`set @@global.tidb_enable_clustered_index = 'int_only'`) + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1287 'INT_ONLY' is deprecated and will be removed in a future release. Please use 'ON' or 'OFF' instead")) + tk.MustExec(`set @@global.tidb_enable_clustered_index = 'off'`) + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec("set @@tidb_enable_clustered_index = 'off'") + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec("set @@tidb_enable_clustered_index = 'on'") + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec("set @@tidb_enable_clustered_index = 'int_only'") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1287 'INT_ONLY' is deprecated and will be removed in a future release. Please use 'ON' or 'OFF' instead")) + + // test for tidb_enable_ordered_result_mode + tk.MustQuery(`select @@tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) + tk.MustExec(`set global tidb_enable_ordered_result_mode = 1`) + tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("1")) + tk.MustExec(`set global tidb_enable_ordered_result_mode = 0`) + tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) + tk.MustExec(`set tidb_enable_ordered_result_mode=1`) + tk.MustQuery(`select @@global.tidb_enable_ordered_result_mode`).Check(testkit.Rows("0")) + tk.MustQuery(`select @@tidb_enable_ordered_result_mode`).Check(testkit.Rows("1")) + + // test for tidb_opt_enable_correlation_adjustment + tk.MustQuery(`select @@tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) + tk.MustExec(`set global tidb_opt_enable_correlation_adjustment = 0`) + tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("0")) + tk.MustExec(`set global tidb_opt_enable_correlation_adjustment = 1`) + tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) + tk.MustExec(`set tidb_opt_enable_correlation_adjustment=0`) + tk.MustQuery(`select @@global.tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("1")) + tk.MustQuery(`select @@tidb_opt_enable_correlation_adjustment`).Check(testkit.Rows("0")) + + // test for tidb_opt_limit_push_down_threshold + tk.MustQuery(`select @@tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) + tk.MustExec(`set global tidb_opt_limit_push_down_threshold = 20`) + tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("20")) + tk.MustExec(`set global tidb_opt_limit_push_down_threshold = 100`) + tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) + tk.MustExec(`set tidb_opt_limit_push_down_threshold = 20`) + tk.MustQuery(`select @@global.tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("100")) + tk.MustQuery(`select @@tidb_opt_limit_push_down_threshold`).Check(testkit.Rows("20")) + + tk.MustQuery("select @@tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_opt_prefer_range_scan = 1") + tk.MustQuery("select @@global.tidb_opt_prefer_range_scan").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_opt_prefer_range_scan = 0") + tk.MustQuery("select @@global.tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) + tk.MustExec("set session tidb_opt_prefer_range_scan = 1") + tk.MustQuery("select @@session.tidb_opt_prefer_range_scan").Check(testkit.Rows("1")) + tk.MustExec("set session tidb_opt_prefer_range_scan = 0") + tk.MustQuery("select @@session.tidb_opt_prefer_range_scan").Check(testkit.Rows("0")) + + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 0.5") + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0.5")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 1") + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 1.5") + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("1.5")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10") + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = -1") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '-1'")) + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = -0.01") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '-0.01'")) + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10.01") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '10.01'")) + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) + tk.MustExec("set global tidb_tso_client_batch_max_wait_time = 10.1") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_tso_client_batch_max_wait_time value: '10.1'")) + tk.MustQuery("select @@tidb_tso_client_batch_max_wait_time").Check(testkit.Rows("10")) + require.Error(t, tk.ExecToErr("set tidb_tso_client_batch_max_wait_time = 1")) + + tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_enable_tso_follower_proxy = 1") + tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_tso_follower_proxy = 0") + tk.MustQuery("select @@tidb_enable_tso_follower_proxy").Check(testkit.Rows("0")) + require.Error(t, tk.ExecToErr("set tidb_enable_tso_follower_proxy = 1")) + + tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_historical_stats = 1") + tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_historical_stats = 0") + tk.MustQuery("select @@tidb_enable_historical_stats").Check(testkit.Rows("0")) + + // test for tidb_enable_column_tracking + tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_column_tracking = 0") + tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("0")) + // When set tidb_enable_column_tracking off, we record the time of the setting operation. + tk.MustQuery("select count(1) from mysql.tidb where variable_name = 'tidb_disable_column_tracking_time' and variable_value is not null").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustQuery("select @@tidb_enable_column_tracking").Check(testkit.Rows("1")) + require.Error(t, tk.ExecToErr("select @@session.tidb_enable_column_tracking")) + require.Error(t, tk.ExecToErr("set tidb_enable_column_tracking = 0")) + require.Error(t, tk.ExecToErr("set global tidb_enable_column_tracking = -1")) + + // test for tidb_ignore_prepared_cache_close_stmt + tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("0")) // default value is 0 + tk.MustExec("set global tidb_ignore_prepared_cache_close_stmt=1") + tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("1")) + tk.MustQuery("show global variables like 'tidb_ignore_prepared_cache_close_stmt'").Check(testkit.Rows("tidb_ignore_prepared_cache_close_stmt ON")) + tk.MustExec("set global tidb_ignore_prepared_cache_close_stmt=0") + tk.MustQuery("select @@global.tidb_ignore_prepared_cache_close_stmt").Check(testkit.Rows("0")) + tk.MustQuery("show global variables like 'tidb_ignore_prepared_cache_close_stmt'").Check(testkit.Rows("tidb_ignore_prepared_cache_close_stmt OFF")) + + // test for tidb_enable_new_cost_interface + tk.MustQuery("select @@global.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set global tidb_enable_new_cost_interface=0") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1287 'OFF' is deprecated and will be removed in a future release. Please use ON instead")) + tk.MustQuery("select @@global.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) + tk.MustExec("set tidb_enable_new_cost_interface=0") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1287 'OFF' is deprecated and will be removed in a future release. Please use ON instead")) + tk.MustQuery("select @@session.tidb_enable_new_cost_interface").Check(testkit.Rows("1")) + + // test for tidb_remove_orderby_in_subquery + tk.MustQuery("select @@session.tidb_remove_orderby_in_subquery").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set session tidb_remove_orderby_in_subquery=0") + tk.MustQuery("select @@session.tidb_remove_orderby_in_subquery").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.tidb_remove_orderby_in_subquery").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set global tidb_remove_orderby_in_subquery=0") + tk.MustQuery("select @@global.tidb_remove_orderby_in_subquery").Check(testkit.Rows("0")) + + // test for tidb_opt_skew_distinct_agg + tk.MustQuery("select @@session.tidb_opt_skew_distinct_agg").Check(testkit.Rows("0")) // default value is 0 + tk.MustExec("set session tidb_opt_skew_distinct_agg=1") + tk.MustQuery("select @@session.tidb_opt_skew_distinct_agg").Check(testkit.Rows("1")) + tk.MustQuery("select @@global.tidb_opt_skew_distinct_agg").Check(testkit.Rows("0")) // default value is 0 + tk.MustExec("set global tidb_opt_skew_distinct_agg=1") + tk.MustQuery("select @@global.tidb_opt_skew_distinct_agg").Check(testkit.Rows("1")) + + // test for tidb_opt_three_stage_distinct_agg + tk.MustQuery("select @@session.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set session tidb_opt_three_stage_distinct_agg=0") + tk.MustQuery("select @@session.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set global tidb_opt_three_stage_distinct_agg=0") + tk.MustQuery("select @@global.tidb_opt_three_stage_distinct_agg").Check(testkit.Rows("0")) + + // the value of max_allowed_packet should be a multiple of 1024 + tk.MustExec("set @@global.max_allowed_packet=16385") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '16385'")) + result := tk.MustQuery("select @@global.max_allowed_packet;") + result.Check(testkit.Rows("16384")) + tk.MustExec("set @@global.max_allowed_packet=2047") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '2047'")) + result = tk.MustQuery("select @@global.max_allowed_packet;") + result.Check(testkit.Rows("1024")) + tk.MustExec("set @@global.max_allowed_packet=0") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '0'")) + result = tk.MustQuery("select @@global.max_allowed_packet;") + result.Check(testkit.Rows("1024")) + + // test value of tidb_stats_cache_mem_quota + tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_stats_cache_mem_quota = 200") + tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("200")) + // assert quota must larger than -1 + tk.MustExec("set global tidb_stats_cache_mem_quota = -1") + tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("0")) + // assert quota muster smaller than 1TB + tk.MustExec("set global tidb_stats_cache_mem_quota = 1099511627777") + tk.MustQuery("select @@global.tidb_stats_cache_mem_quota").Check(testkit.Rows("1099511627776")) + // for read-only instance scoped system variables. + tk.MustGetErrCode("set @@global.plugin_load = ''", errno.ErrIncorrectGlobalLocalVar) + tk.MustGetErrCode("set @@global.plugin_dir = ''", errno.ErrIncorrectGlobalLocalVar) + + // test for tidb_max_auto_analyze_time + tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBMaxAutoAnalyzeTime))) + tk.MustExec("set global tidb_max_auto_analyze_time = 60") + tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows("60")) + tk.MustExec("set global tidb_max_auto_analyze_time = -1") + tk.MustQuery("select @@tidb_max_auto_analyze_time").Check(testkit.Rows("0")) + + // test variables for cost model ver2 + tk.MustQuery("select @@tidb_cost_model_version").Check(testkit.Rows(fmt.Sprintf("%v", variable.DefTiDBCostModelVer))) + tk.MustExec("set tidb_cost_model_version=3") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_cost_model_version value: '3'")) + tk.MustExec("set tidb_cost_model_version=0") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_cost_model_version value: '0'")) + tk.MustExec("set tidb_cost_model_version=2") + tk.MustQuery("select @@tidb_cost_model_version").Check(testkit.Rows("2")) + + tk.MustQuery("select @@tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_enable_analyze_snapshot = 1") + tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("1")) + tk.MustExec("set global tidb_enable_analyze_snapshot = 0") + tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) + tk.MustExec("set session tidb_enable_analyze_snapshot = 1") + tk.MustQuery("select @@session.tidb_enable_analyze_snapshot").Check(testkit.Rows("1")) + tk.MustExec("set session tidb_enable_analyze_snapshot = 0") + tk.MustQuery("select @@session.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) + + // test variables `init_connect' + tk.MustGetErrCode("set global init_connect = '-1'", mysql.ErrWrongTypeForVar) + tk.MustGetErrCode("set global init_connect = 'invalidstring'", mysql.ErrWrongTypeForVar) + tk.MustExec("set global init_connect = 'select now(); select timestamp()'") + + // test variable 'tidb_session_plan_cache_size' + // global scope + tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value + tk.MustExec("set global tidb_session_plan_cache_size = 1") + tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("1")) + // session scope + tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value + tk.MustExec("set session tidb_session_plan_cache_size = 1") + tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("1")) + + // test variable 'foreign_key_checks' + // global scope + tk.MustQuery("select @@global.foreign_key_checks").Check(testkit.Rows("1")) // default value + tk.MustExec("set global foreign_key_checks = 0") + tk.MustQuery("select @@global.foreign_key_checks").Check(testkit.Rows("0")) + // session scope + tk.MustQuery("select @@session.foreign_key_checks").Check(testkit.Rows("1")) // default value + tk.MustExec("set session foreign_key_checks = 0") + tk.MustQuery("select @@session.foreign_key_checks").Check(testkit.Rows("0")) + + // test variable 'tidb_enable_foreign_key' + // global scope + tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("1")) // default value + tk.MustExec("set global tidb_enable_foreign_key = 0") + tk.MustQuery("select @@global.tidb_enable_foreign_key").Check(testkit.Rows("0")) + + // test variable 'tidb_opt_force_inline_cte' + tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0 + tk.MustExec("set session tidb_opt_force_inline_cte=1") + tk.MustQuery("select @@session.tidb_opt_force_inline_cte").Check(testkit.Rows("1")) + tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("0")) // default value is 0 + tk.MustExec("set global tidb_opt_force_inline_cte=1") + tk.MustQuery("select @@global.tidb_opt_force_inline_cte").Check(testkit.Rows("1")) + + // test tidb_auto_analyze_partition_batch_size + tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1")) // default value is 1 + tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 2") + tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("2")) + tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 0") + tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1")) // min value is 1 + tk.MustExec("set global tidb_auto_analyze_partition_batch_size = 9999") + tk.MustQuery("select @@global.tidb_auto_analyze_partition_batch_size").Check(testkit.Rows("1024")) // max value is 1024 + + // test variable 'tidb_opt_prefix_index_single_scan' + // global scope + tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) // default value + tk.MustExec("set global tidb_opt_prefix_index_single_scan = 0") + tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_opt_prefix_index_single_scan = 1") + tk.MustQuery("select @@global.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) + // session scope + tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) // default value + tk.MustExec("set session tidb_opt_prefix_index_single_scan = 0") + tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("0")) + tk.MustExec("set session tidb_opt_prefix_index_single_scan = 1") + tk.MustQuery("select @@session.tidb_opt_prefix_index_single_scan").Check(testkit.Rows("1")) + + // test tidb_opt_range_max_size + tk.MustQuery("select @@tidb_opt_range_max_size").Check(testkit.Rows("67108864")) + tk.MustExec("set global tidb_opt_range_max_size = -1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_opt_range_max_size value: '-1'")) + tk.MustQuery("select @@global.tidb_opt_range_max_size").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_opt_range_max_size = 1048576") + tk.MustQuery("select @@global.tidb_opt_range_max_size").Check(testkit.Rows("1048576")) + tk.MustExec("set session tidb_opt_range_max_size = 2097152") + tk.MustQuery("select @@session.tidb_opt_range_max_size").Check(testkit.Rows("2097152")) + + // test for password validation + tk.MustQuery("SELECT @@GLOBAL.validate_password.enable").Check(testkit.Rows("0")) + tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("8")) + tk.MustExec("SET GLOBAL validate_password.length = 3") + tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("4")) + tk.MustExec("SET GLOBAL validate_password.mixed_case_count = 2") + tk.MustQuery("SELECT @@GLOBAL.validate_password.length").Check(testkit.Rows("6")) + + // test tidb_cdc_write_source + require.Equal(t, uint64(0), tk.Session().GetSessionVars().CDCWriteSource) + tk.MustQuery("select @@tidb_cdc_write_source").Check(testkit.Rows("0")) + tk.MustExec("set @@session.tidb_cdc_write_source = 2") + tk.MustQuery("select @@tidb_cdc_write_source").Check(testkit.Rows("2")) + require.Equal(t, uint64(2), tk.Session().GetSessionVars().CDCWriteSource) + tk.MustExec("set @@session.tidb_cdc_write_source = 0") + require.Equal(t, uint64(0), tk.Session().GetSessionVars().CDCWriteSource) + + tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("json,blob,mediumblob,longblob")) + tk.MustExec("set @@session.tidb_analyze_skip_column_types = 'json, text, blob'") + tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("json,text,blob")) + tk.MustExec("set @@session.tidb_analyze_skip_column_types = ''") + tk.MustQuery("select @@session.tidb_analyze_skip_column_types").Check(testkit.Rows("")) + tk.MustGetErrMsg("set @@session.tidb_analyze_skip_column_types = 'int,json'", "[variable:1231]Variable 'tidb_analyze_skip_column_types' can't be set to the value of 'int,json'") + + tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("json,blob,mediumblob,longblob")) + tk.MustExec("set @@global.tidb_analyze_skip_column_types = 'json, text, blob'") + tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("json,text,blob")) + tk.MustExec("set @@global.tidb_analyze_skip_column_types = ''") + tk.MustQuery("select @@global.tidb_analyze_skip_column_types").Check(testkit.Rows("")) + tk.MustGetErrMsg("set @@global.tidb_analyze_skip_column_types = 'int,json'", "[variable:1231]Variable 'tidb_analyze_skip_column_types' can't be set to the value of 'int,json'") + + // test tidb_skip_missing_partition_stats + // global scope + tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) // default value + tk.MustExec("set global tidb_skip_missing_partition_stats = 0") + tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_skip_missing_partition_stats = 1") + tk.MustQuery("select @@global.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) + // session scope + tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) // default value + tk.MustExec("set session tidb_skip_missing_partition_stats = 0") + tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("0")) + tk.MustExec("set session tidb_skip_missing_partition_stats = 1") + tk.MustQuery("select @@session.tidb_skip_missing_partition_stats").Check(testkit.Rows("1")) + + // test tidb_schema_version_cache_limit + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("16")) + tk.MustExec("set @@global.tidb_schema_version_cache_limit=64;") + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("64")) + tk.MustExec("set @@global.tidb_schema_version_cache_limit=2;") + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) + tk.MustExec("set @@global.tidb_schema_version_cache_limit=256;") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_schema_version_cache_limit value: '256'")) + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("255")) + tk.MustExec("set @@global.tidb_schema_version_cache_limit=0;") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_schema_version_cache_limit value: '0'")) + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) + tk.MustGetErrMsg("set @@global.tidb_schema_version_cache_limit='x';", "[variable:1232]Incorrect argument type to variable 'tidb_schema_version_cache_limit'") + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("2")) + tk.MustExec("set @@global.tidb_schema_version_cache_limit=64;") + tk.MustQuery("select @@global.tidb_schema_version_cache_limit").Check(testkit.Rows("64")) +} + +func TestGetSetNoopVars(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // By default you can get/set noop sysvars without issue. + tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("OFF")) + tk.MustQuery("SHOW VARIABLES LIKE 'query_cache_type'").Check(testkit.Rows("query_cache_type OFF")) + tk.MustExec("SET query_cache_type=2") + tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("DEMAND")) + // When tidb_enable_noop_variables is OFF, you can GET in @@ context + // and always SET. But you can't see in SHOW VARIABLES. + // Warnings are also returned. + tk.MustExec("SET GLOBAL tidb_enable_noop_variables = OFF") + defer tk.MustExec("SET GLOBAL tidb_enable_noop_variables = ON") + tk.MustQuery("SELECT @@global.tidb_enable_noop_variables").Check(testkit.Rows("OFF")) + tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("DEMAND")) + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 8145 variable query_cache_type has no effect in TiDB")) + tk.MustQuery("SHOW VARIABLES LIKE 'query_cache_type'").Check(testkit.Rows()) + tk.MustExec("SET query_cache_type = OFF") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 8144 setting query_cache_type has no effect in TiDB")) + // but the change is still effective. + tk.MustQuery("SELECT @@query_cache_type").Check(testkit.Rows("OFF")) + + // Only ON and OFF supported + err := tk.ExecToErr("SET GLOBAL tidb_enable_noop_variables = 2") + require.Error(t, err) + require.Equal(t, "[variable:1231]Variable 'tidb_enable_noop_variables' can't be set to the value of '2'", err.Error()) + + err = tk.ExecToErr("SET GLOBAL tidb_enable_noop_variables = 'warn'") + require.Error(t, err) + require.Equal(t, "[variable:1231]Variable 'tidb_enable_noop_variables' can't be set to the value of 'warn'", err.Error()) +} + +func TestTruncateIncorrectIntSessionVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + testCases := []struct { + sessionVarName string + minValue int + maxValue int + }{ + {"auto_increment_increment", 1, 65535}, + {"auto_increment_offset", 1, 65535}, + } + + for _, tc := range testCases { + name := tc.sessionVarName + selectSQL := fmt.Sprintf("select @@%s;", name) + validValue := tc.minValue + (tc.maxValue-tc.minValue)/2 + tk.MustExec(fmt.Sprintf("set @@%s = %d", name, validValue)) + tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", validValue))) + + tk.MustExec(fmt.Sprintf("set @@%s = %d", name, tc.minValue-1)) + warnMsg := fmt.Sprintf("Warning 1292 Truncated incorrect %s value: '%d'", name, tc.minValue-1) + tk.MustQuery("show warnings").Check(testkit.Rows(warnMsg)) + tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", tc.minValue))) + + tk.MustExec(fmt.Sprintf("set @@%s = %d", name, tc.maxValue+1)) + warnMsg = fmt.Sprintf("Warning 1292 Truncated incorrect %s value: '%d'", name, tc.maxValue+1) + tk.MustQuery("show warnings").Check(testkit.Rows(warnMsg)) + tk.MustQuery(selectSQL).Check(testkit.Rows(fmt.Sprintf("%d", tc.maxValue))) + } +} + +func TestSetCharset(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + sessionVars := tk.Session().GetSessionVars() + + var characterSetVariables = []string{ + "character_set_client", + "character_set_connection", + "character_set_results", + "character_set_server", + "character_set_database", + "character_set_system", + "character_set_filesystem", + } + + check := func(args ...string) { + for i, v := range characterSetVariables { + sVar, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), v) + require.NoError(t, err) + require.Equal(t, args[i], sVar, fmt.Sprintf("%d: %s", i, characterSetVariables[i])) + } + } + + check( + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + tk.MustExec(`SET NAMES latin1`) + check( + "latin1", + "latin1", + "latin1", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + tk.MustExec(`SET NAMES default`) + check( + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + // Issue #1523 + tk.MustExec(`SET NAMES binary`) + check( + "binary", + "binary", + "binary", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + tk.MustExec(`SET NAMES utf8`) + check( + "utf8", + "utf8", + "utf8", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + tk.MustExec(`SET CHARACTER SET latin1`) + check( + "latin1", + "utf8mb4", + "latin1", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) + + tk.MustExec(`SET CHARACTER SET default`) + check( + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8mb4", + "utf8", + "binary", + ) +} + +func TestSetCollationAndCharset(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + ctx := tk.Session().(sessionctx.Context) + sessionVars := ctx.GetSessionVars() + + cases := []struct { + charset string + collation string + expectCharset string + expectCollation string + }{ + {variable.CharacterSetConnection, variable.CollationConnection, "utf8", "utf8_bin"}, + {variable.CharsetDatabase, variable.CollationDatabase, "utf8", "utf8_bin"}, + {variable.CharacterSetServer, variable.CollationServer, "utf8", "utf8_bin"}, + } + + for _, c := range cases { + tk.MustExec(fmt.Sprintf("set %s = %s;", c.charset, c.expectCharset)) + sVar, ok := sessionVars.GetSystemVar(c.charset) + require.True(t, ok) + require.Equal(t, c.expectCharset, sVar) + sVar, ok = sessionVars.GetSystemVar(c.collation) + require.True(t, ok) + require.Equal(t, c.expectCollation, sVar) + } + + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + ctx = tk.Session().(sessionctx.Context) + sessionVars = ctx.GetSessionVars() + + for _, c := range cases { + tk.MustExec(fmt.Sprintf("set %s = %s;", c.collation, c.expectCollation)) + sVar, ok := sessionVars.GetSystemVar(c.charset) + require.True(t, ok) + require.Equal(t, c.expectCharset, sVar) + sVar, ok = sessionVars.GetSystemVar(c.collation) + require.True(t, ok) + require.Equal(t, c.expectCollation, sVar) + } +} + +func TestValidateSetVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + err := tk.ExecToErr("set global tidb_distsql_scan_concurrency='fff';") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set global tidb_distsql_scan_concurrency=-2;") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_distsql_scan_concurrency value: '-2'")) + + err = tk.ExecToErr("set @@tidb_distsql_scan_concurrency='fff';") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@tidb_distsql_scan_concurrency=-2;") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_distsql_scan_concurrency value: '-2'")) + + err = tk.ExecToErr("set @@tidb_batch_delete='ok';") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@tidb_batch_delete='On';") + tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("1")) + tk.MustExec("set @@tidb_batch_delete='oFf';") + tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("0")) + tk.MustExec("set @@tidb_batch_delete=1;") + tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("1")) + tk.MustExec("set @@tidb_batch_delete=0;") + tk.MustQuery("select @@tidb_batch_delete;").Check(testkit.Rows("0")) + + tk.MustExec("set @@tidb_opt_agg_push_down=off;") + tk.MustQuery("select @@tidb_opt_agg_push_down;").Check(testkit.Rows("0")) + + tk.MustExec("set @@tidb_constraint_check_in_place=on;") + tk.MustQuery("select @@tidb_constraint_check_in_place;").Check(testkit.Rows("1")) + + tk.MustExec("set @@tidb_general_log=0;") + tk.MustQuery(`show warnings`).Check(testkit.Rows(fmt.Sprintf("Warning %d modifying tidb_general_log will require SET GLOBAL in a future version of TiDB", errno.ErrInstanceScope))) + tk.MustQuery("select @@tidb_general_log;").Check(testkit.Rows("0")) + + tk.MustExec("set @@tidb_pprof_sql_cpu=1;") + tk.MustQuery("select @@tidb_pprof_sql_cpu;").Check(testkit.Rows("1")) + tk.MustExec("set @@tidb_pprof_sql_cpu=0;") + tk.MustQuery("select @@tidb_pprof_sql_cpu;").Check(testkit.Rows("0")) + + err = tk.ExecToErr("set @@tidb_batch_delete=3;") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@group_concat_max_len=1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect group_concat_max_len value: '1'")) + result := tk.MustQuery("select @@group_concat_max_len;") + result.Check(testkit.Rows("4")) + + err = tk.ExecToErr("set @@group_concat_max_len = 18446744073709551616") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) + + // Test illegal type + err = tk.ExecToErr("set @@group_concat_max_len='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@default_week_format=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect default_week_format value: '-1'")) + result = tk.MustQuery("select @@default_week_format;") + result.Check(testkit.Rows("0")) + + tk.MustExec("set @@default_week_format=9") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect default_week_format value: '9'")) + result = tk.MustQuery("select @@default_week_format;") + result.Check(testkit.Rows("7")) + + err = tk.ExecToErr("set @@error_count = 0") + require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err %v", err)) + + err = tk.ExecToErr("set @@warning_count = 0") + require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err %v", err)) + + tk.MustExec("set time_zone='SySTeM'") + result = tk.MustQuery("select @@time_zone;") + result.Check(testkit.Rows("SYSTEM")) + + // The following cases test value out of range and illegal type when setting system variables. + // See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html for more details. + tk.MustExec("set @@global.max_connections=100001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '100001'")) + result = tk.MustQuery("select @@global.max_connections;") + result.Check(testkit.Rows("100000")) + + // "max_connections == 0" means there is no limitation on the number of connections. + tk.MustExec("set @@global.max_connections=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) + result = tk.MustQuery("select @@global.max_connections;") + result.Check(testkit.Rows("0")) + + err = tk.ExecToErr("set @@global.max_connections='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@global.thread_pool_size=65") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect thread_pool_size value: '65'")) + result = tk.MustQuery("select @@global.thread_pool_size;") + result.Check(testkit.Rows("64")) + + tk.MustExec("set @@global.thread_pool_size=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect thread_pool_size value: '-1'")) + result = tk.MustQuery("select @@global.thread_pool_size;") + result.Check(testkit.Rows("1")) + + err = tk.ExecToErr("set @@global.thread_pool_size='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@global.max_allowed_packet=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_allowed_packet value: '-1'")) + result = tk.MustQuery("select @@global.max_allowed_packet;") + result.Check(testkit.Rows("1024")) + + err = tk.ExecToErr("set @@global.max_allowed_packet='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + err = tk.ExecToErr("set @@max_allowed_packet=default") + require.True(t, terror.ErrorEqual(err, variable.ErrReadOnly)) + + tk.MustExec("set @@global.max_connect_errors=18446744073709551615") + + tk.MustExec("set @@global.max_connect_errors=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connect_errors value: '-1'")) + result = tk.MustQuery("select @@global.max_connect_errors;") + result.Check(testkit.Rows("1")) + + err = tk.ExecToErr("set @@global.max_connect_errors=18446744073709551616") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@global.max_connections=100001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '100001'")) + result = tk.MustQuery("select @@global.max_connections;") + result.Check(testkit.Rows("100000")) + + tk.MustExec("set @@global.max_connections=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_connections value: '-1'")) + result = tk.MustQuery("select @@global.max_connections;") + result.Check(testkit.Rows("0")) + + err = tk.ExecToErr("set @@global.max_connections='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@max_sort_length=1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '1'")) + result = tk.MustQuery("select @@max_sort_length;") + result.Check(testkit.Rows("4")) + + tk.MustExec("set @@max_sort_length=-100") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '-100'")) + result = tk.MustQuery("select @@max_sort_length;") + result.Check(testkit.Rows("4")) + + tk.MustExec("set @@max_sort_length=8388609") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect max_sort_length value: '8388609'")) + result = tk.MustQuery("select @@max_sort_length;") + result.Check(testkit.Rows("8388608")) + + err = tk.ExecToErr("set @@max_sort_length='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@global.table_definition_cache=399") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '399'")) + result = tk.MustQuery("select @@global.table_definition_cache;") + result.Check(testkit.Rows("400")) + + tk.MustExec("set @@global.table_definition_cache=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '-1'")) + result = tk.MustQuery("select @@global.table_definition_cache;") + result.Check(testkit.Rows("400")) + + tk.MustExec("set @@global.table_definition_cache=524289") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect table_definition_cache value: '524289'")) + result = tk.MustQuery("select @@global.table_definition_cache;") + result.Check(testkit.Rows("524288")) + + err = tk.ExecToErr("set @@global.table_definition_cache='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@old_passwords=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect old_passwords value: '-1'")) + result = tk.MustQuery("select @@old_passwords;") + result.Check(testkit.Rows("0")) + + tk.MustExec("set @@old_passwords=3") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect old_passwords value: '3'")) + result = tk.MustQuery("select @@old_passwords;") + result.Check(testkit.Rows("2")) + + err = tk.ExecToErr("set @@old_passwords='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@tmp_table_size=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tmp_table_size value: '-1'")) + result = tk.MustQuery("select @@tmp_table_size;") + result.Check(testkit.Rows("1024")) + + tk.MustExec("set @@tmp_table_size=1020") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tmp_table_size value: '1020'")) + result = tk.MustQuery("select @@tmp_table_size;") + result.Check(testkit.Rows("1024")) + + tk.MustExec("set @@tmp_table_size=167772161") + result = tk.MustQuery("select @@tmp_table_size;") + result.Check(testkit.Rows("167772161")) + + tk.MustExec("set @@tmp_table_size=18446744073709551615") + result = tk.MustQuery("select @@tmp_table_size;") + result.Check(testkit.Rows("18446744073709551615")) + + err = tk.ExecToErr("set @@tmp_table_size=18446744073709551616") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + err = tk.ExecToErr("set @@tmp_table_size='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@tidb_tmp_table_max_size=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '-1'")) + result = tk.MustQuery("select @@tidb_tmp_table_max_size;") + result.Check(testkit.Rows("1048576")) + + tk.MustExec("set @@tidb_tmp_table_max_size=1048575") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '1048575'")) + result = tk.MustQuery("select @@tidb_tmp_table_max_size;") + result.Check(testkit.Rows("1048576")) + + tk.MustExec("set @@tidb_tmp_table_max_size=167772161") + result = tk.MustQuery("select @@tidb_tmp_table_max_size;") + result.Check(testkit.Rows("167772161")) + + tk.MustExec("set @@tidb_tmp_table_max_size=137438953472") + result = tk.MustQuery("select @@tidb_tmp_table_max_size;") + result.Check(testkit.Rows("137438953472")) + + tk.MustExec("set @@tidb_tmp_table_max_size=137438953473") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect tidb_tmp_table_max_size value: '137438953473'")) + result = tk.MustQuery("select @@tidb_tmp_table_max_size;") + result.Check(testkit.Rows("137438953472")) + + err = tk.ExecToErr("set @@tidb_tmp_table_max_size='hello'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongTypeForVar)) + + tk.MustExec("set @@global.connect_timeout=1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect connect_timeout value: '1'")) + result = tk.MustQuery("select @@global.connect_timeout;") + result.Check(testkit.Rows("2")) + + tk.MustExec("set @@global.connect_timeout=31536000") + result = tk.MustQuery("select @@global.connect_timeout;") + result.Check(testkit.Rows("31536000")) + + tk.MustExec("set @@global.connect_timeout=31536001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect connect_timeout value: '31536001'")) + result = tk.MustQuery("select @@global.connect_timeout;") + result.Check(testkit.Rows("31536000")) + + result = tk.MustQuery("select @@sql_select_limit;") + result.Check(testkit.Rows("18446744073709551615")) + tk.MustExec("set @@sql_select_limit=default") + result = tk.MustQuery("select @@sql_select_limit;") + result.Check(testkit.Rows("18446744073709551615")) + + tk.MustExec("set @@sql_auto_is_null=00") + result = tk.MustQuery("select @@sql_auto_is_null;") + result.Check(testkit.Rows("0")) + + tk.MustExec("set @@sql_warnings=001") + result = tk.MustQuery("select @@sql_warnings;") + result.Check(testkit.Rows("1")) + + tk.MustExec("set @@sql_warnings=000") + result = tk.MustQuery("select @@sql_warnings;") + result.Check(testkit.Rows("0")) + + tk.MustExec("set @@global.super_read_only=-0") + result = tk.MustQuery("select @@global.super_read_only;") + result.Check(testkit.Rows("0")) + + err = tk.ExecToErr("set @@global.super_read_only=-1") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@global.innodb_status_output_locks=-1") + result = tk.MustQuery("select @@global.innodb_status_output_locks;") + result.Check(testkit.Rows("1")) + + tk.MustExec("set @@global.innodb_ft_enable_stopword=0000000") + result = tk.MustQuery("select @@global.innodb_ft_enable_stopword;") + result.Check(testkit.Rows("0")) + + tk.MustExec("set @@global.innodb_stats_on_metadata=1") + result = tk.MustQuery("select @@global.innodb_stats_on_metadata;") + result.Check(testkit.Rows("1")) + + tk.MustExec("set @@global.innodb_file_per_table=-50") + result = tk.MustQuery("select @@global.innodb_file_per_table;") + result.Check(testkit.Rows("1")) + + err = tk.ExecToErr("set @@global.innodb_ft_enable_stopword=2") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@query_cache_type=0") + result = tk.MustQuery("select @@query_cache_type;") + result.Check(testkit.Rows("OFF")) + + tk.MustExec("set @@query_cache_type=2") + result = tk.MustQuery("select @@query_cache_type;") + result.Check(testkit.Rows("DEMAND")) + + tk.MustExec("set @@global.sync_binlog=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect sync_binlog value: '-1'")) + + tk.MustExec("set @@global.sync_binlog=4294967299") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect sync_binlog value: '4294967299'")) + + tk.MustExec("set @@global.flush_time=31536001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect flush_time value: '31536001'")) + + tk.MustExec("set @@global.interactive_timeout=31536001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect interactive_timeout value: '31536001'")) + + tk.MustExec("set @@global.innodb_commit_concurrency = -1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_commit_concurrency value: '-1'")) + + tk.MustExec("set @@global.innodb_commit_concurrency = 1001") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_commit_concurrency value: '1001'")) + + tk.MustExec("set @@global.innodb_fast_shutdown = -1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_fast_shutdown value: '-1'")) + + tk.MustExec("set @@global.innodb_fast_shutdown = 3") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_fast_shutdown value: '3'")) + + tk.MustExec("set @@global.innodb_lock_wait_timeout = 0") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '0'")) + + tk.MustExec("set @@global.innodb_lock_wait_timeout = 1073741825") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '1073741825'")) + + tk.MustExec("set @@innodb_lock_wait_timeout = 0") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '0'")) + + tk.MustExec("set @@innodb_lock_wait_timeout = 1073741825") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect innodb_lock_wait_timeout value: '1073741825'")) + + tk.MustExec("set @@global.validate_password.number_count=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect validate_password.number_count value: '-1'")) + + tk.MustExec("set @@global.validate_password.length=-1") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect validate_password.length value: '-1'")) + + err = tk.ExecToErr("set @@tx_isolation=''") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + err = tk.ExecToErr("set global tx_isolation=''") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + err = tk.ExecToErr("set @@transaction_isolation=''") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + err = tk.ExecToErr("set global transaction_isolation=''") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + err = tk.ExecToErr("set global tx_isolation='REPEATABLE-READ1'") + require.True(t, terror.ErrorEqual(err, variable.ErrWrongValueForVar), fmt.Sprintf("err %v", err)) + + tk.MustExec("set @@tx_isolation='READ-COMMITTED'") + result = tk.MustQuery("select @@tx_isolation;") + result.Check(testkit.Rows("READ-COMMITTED")) + + tk.MustExec("set @@tx_isolation='read-COMMITTED'") + result = tk.MustQuery("select @@tx_isolation;") + result.Check(testkit.Rows("READ-COMMITTED")) + + tk.MustExec("set @@tx_isolation='REPEATABLE-READ'") + result = tk.MustQuery("select @@tx_isolation;") + result.Check(testkit.Rows("REPEATABLE-READ")) + + tk.MustExec("SET GLOBAL tidb_skip_isolation_level_check = 0") + tk.MustExec("SET SESSION tidb_skip_isolation_level_check = 0") + err = tk.ExecToErr("set @@tx_isolation='SERIALIZABLE'") + require.True(t, terror.ErrorEqual(err, variable.ErrUnsupportedIsolationLevel), fmt.Sprintf("err %v", err)) + + tk.MustExec("set global allow_auto_random_explicit_insert=on;") + tk.MustQuery("select @@global.allow_auto_random_explicit_insert;").Check(testkit.Rows("1")) +} + +func TestSelectGlobalVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("0")) + tk.MustQuery("select @@max_connections;").Check(testkit.Rows("0")) + + tk.MustExec("set @@global.max_connections=100;") + + tk.MustQuery("select @@global.max_connections;").Check(testkit.Rows("100")) + tk.MustQuery("select @@max_connections;").Check(testkit.Rows("100")) + + tk.MustExec("set @@global.max_connections=0;") + + // test for unknown variable. + err := tk.ExecToErr("select @@invalid") + require.True(t, terror.ErrorEqual(err, variable.ErrUnknownSystemVar), fmt.Sprintf("err %v", err)) + err = tk.ExecToErr("select @@global.invalid") + require.True(t, terror.ErrorEqual(err, variable.ErrUnknownSystemVar), fmt.Sprintf("err %v", err)) +} + +func TestSetConcurrency(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test default value + tk.MustQuery("select @@tidb_executor_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefExecutorConcurrency))) + + tk.MustQuery("select @@tidb_index_lookup_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_index_lookup_join_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_hash_join_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_hashagg_partial_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_hashagg_final_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_window_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_streamagg_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefTiDBStreamAggConcurrency))) + tk.MustQuery("select @@tidb_projection_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.ConcurrencyUnset))) + tk.MustQuery("select @@tidb_distsql_scan_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefDistSQLScanConcurrency))) + + tk.MustQuery("select @@tidb_index_serial_scan_concurrency;").Check(testkit.Rows(strconv.Itoa(variable.DefIndexSerialScanConcurrency))) + + vars := tk.Session().GetSessionVars() + require.Equal(t, variable.DefExecutorConcurrency, vars.ExecutorConcurrency) + require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupJoinConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashJoinConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggPartialConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggFinalConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.WindowConcurrency()) + require.Equal(t, variable.DefTiDBStreamAggConcurrency, vars.StreamAggConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.ProjectionConcurrency()) + require.Equal(t, variable.DefDistSQLScanConcurrency, vars.DistSQLScanConcurrency()) + + require.Equal(t, variable.DefIndexSerialScanConcurrency, vars.IndexSerialScanConcurrency()) + + // test setting deprecated variables + warnTpl := "Warning 1287 '%s' is deprecated and will be removed in a future release. Please use tidb_executor_concurrency instead" + + checkSet := func(v string) { + tk.MustExec(fmt.Sprintf("set @@%s=1;", v)) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", fmt.Sprintf(warnTpl, v))) + tk.MustQuery(fmt.Sprintf("select @@%s;", v)).Check(testkit.Rows("1")) + } + + checkSet(variable.TiDBIndexLookupConcurrency) + require.Equal(t, 1, vars.IndexLookupConcurrency()) + + checkSet(variable.TiDBIndexLookupJoinConcurrency) + require.Equal(t, 1, vars.IndexLookupJoinConcurrency()) + + checkSet(variable.TiDBHashJoinConcurrency) + require.Equal(t, 1, vars.HashJoinConcurrency()) + + checkSet(variable.TiDBHashAggPartialConcurrency) + require.Equal(t, 1, vars.HashAggPartialConcurrency()) + + checkSet(variable.TiDBHashAggFinalConcurrency) + require.Equal(t, 1, vars.HashAggFinalConcurrency()) + + checkSet(variable.TiDBProjectionConcurrency) + require.Equal(t, 1, vars.ProjectionConcurrency()) + + checkSet(variable.TiDBWindowConcurrency) + require.Equal(t, 1, vars.WindowConcurrency()) + + checkSet(variable.TiDBStreamAggConcurrency) + require.Equal(t, 1, vars.StreamAggConcurrency()) + + tk.MustExec(fmt.Sprintf("set @@%s=1;", variable.TiDBDistSQLScanConcurrency)) + tk.MustQuery(fmt.Sprintf("select @@%s;", variable.TiDBDistSQLScanConcurrency)).Check(testkit.Rows("1")) + require.Equal(t, 1, vars.DistSQLScanConcurrency()) + + tk.MustExec("set @@tidb_index_serial_scan_concurrency=4") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustQuery("select @@tidb_index_serial_scan_concurrency;").Check(testkit.Rows("4")) + require.Equal(t, 4, vars.IndexSerialScanConcurrency()) + + // test setting deprecated value unset + tk.MustExec("set @@tidb_index_lookup_concurrency=-1;") + tk.MustExec("set @@tidb_index_lookup_join_concurrency=-1;") + tk.MustExec("set @@tidb_hash_join_concurrency=-1;") + tk.MustExec("set @@tidb_hashagg_partial_concurrency=-1;") + tk.MustExec("set @@tidb_hashagg_final_concurrency=-1;") + tk.MustExec("set @@tidb_window_concurrency=-1;") + tk.MustExec("set @@tidb_streamagg_concurrency=-1;") + tk.MustExec("set @@tidb_projection_concurrency=-1;") + + require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.IndexLookupJoinConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashJoinConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggPartialConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.HashAggFinalConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.WindowConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.StreamAggConcurrency()) + require.Equal(t, variable.DefExecutorConcurrency, vars.ProjectionConcurrency()) + + tk.MustExec("set @@tidb_executor_concurrency=-1;") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_executor_concurrency value: '-1'")) + require.Equal(t, 1, vars.ExecutorConcurrency) +} + +func TestEnableNoopFunctionsVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + defer func() { + // Ensure global settings are reset. + tk.MustExec("SET GLOBAL tx_read_only = 0") + tk.MustExec("SET GLOBAL transaction_read_only = 0") + tk.MustExec("SET GLOBAL read_only = 0") + tk.MustExec("SET GLOBAL super_read_only = 0") + tk.MustExec("SET GLOBAL offline_mode = 0") + tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") + }() + + // test for tidb_enable_noop_functions + tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + + // change session var to 1 + tk.MustExec(`set tidb_enable_noop_functions=1;`) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("ON")) + tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + + // restore to 0 + tk.MustExec(`set tidb_enable_noop_functions=0;`) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + tk.MustQuery(`select @@global.tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + + // set test + require.Error(t, tk.ExecToErr(`set tidb_enable_noop_functions='abc'`)) + require.Error(t, tk.ExecToErr(`set tidb_enable_noop_functions=11`)) + tk.MustExec(`set tidb_enable_noop_functions="off";`) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + tk.MustExec(`set tidb_enable_noop_functions="on";`) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("ON")) + tk.MustExec(`set tidb_enable_noop_functions=0;`) + tk.MustQuery(`select @@tidb_enable_noop_functions;`).Check(testkit.Rows("OFF")) + + err := tk.ExecToErr("SET SESSION tx_read_only = 1") + require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) + + tk.MustExec("SET SESSION tx_read_only = 0") + tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("0")) + tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("0")) + + err = tk.ExecToErr("SET GLOBAL tx_read_only = 1") // should fail. + require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) + tk.MustExec("SET GLOBAL tx_read_only = 0") + tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("0")) + + err = tk.ExecToErr("SET SESSION transaction_read_only = 1") + require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) + tk.MustExec("SET SESSION transaction_read_only = 0") + tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("0")) + tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("0")) + + // works on SESSION because SESSION tidb_enable_noop_functions=1 + tk.MustExec("SET tidb_enable_noop_functions = 1") + tk.MustExec("SET SESSION transaction_read_only = 1") + tk.MustQuery("select @@session.tx_read_only").Check(testkit.Rows("1")) + tk.MustQuery("select @@session.transaction_read_only").Check(testkit.Rows("1")) + + // fails on GLOBAL because GLOBAL.tidb_enable_noop_functions still=0 + err = tk.ExecToErr("SET GLOBAL transaction_read_only = 1") + require.True(t, terror.ErrorEqual(err, variable.ErrFunctionsNoopImpl), fmt.Sprintf("err %v", err)) + tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 1") + // now works + tk.MustExec("SET GLOBAL transaction_read_only = 1") + tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("1")) + tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL transaction_read_only = 0") + tk.MustQuery("select @@global.tx_read_only").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.transaction_read_only").Check(testkit.Rows("0")) + + require.Error(t, tk.ExecToErr("SET tidb_enable_noop_functions = 0")) // fails because transaction_read_only/tx_read_only = 1 + + tk.MustExec("SET transaction_read_only = 0") + tk.MustExec("SET tidb_enable_noop_functions = 0") // now works. + + // setting session doesn't change global, which succeeds because global.transaction_read_only/tx_read_only = 0 + tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") + + // but if global.transaction_read_only=1, it would fail + tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 1") + tk.MustExec("SET GLOBAL transaction_read_only = 1") + // fails + require.Error(t, tk.ExecToErr("SET GLOBAL tidb_enable_noop_functions = 0")) + + // reset for rest of tests. + tk.MustExec("SET GLOBAL transaction_read_only = 0") + tk.MustExec("SET GLOBAL tidb_enable_noop_functions = 0") + + tk.MustExec("set global read_only = 0") + tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("0")) + tk.MustExec("set global read_only = off") + tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("0")) + tk.MustExec("SET global tidb_enable_noop_functions = 1") + tk.MustExec("set global read_only = 1") + tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("1")) + tk.MustExec("set global read_only = on") + tk.MustQuery("select @@global.read_only;").Check(testkit.Rows("1")) + require.Error(t, tk.ExecToErr("set global read_only = abc")) +} + +// https://github.com/pingcap/tidb/issues/29670 +func TestDefaultBehavior(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("InnoDB")) + tk.MustExec("SET GLOBAL default_storage_engine = 'somethingweird'") + tk.MustExec("SET default_storage_engine = 'MyISAM'") + tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("MyISAM")) + tk.MustExec("SET default_storage_engine = DEFAULT") // reads from global value + tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird")) + tk.MustExec("SET @@SESSION.default_storage_engine = @@GLOBAL.default_storage_engine") // example from MySQL manual + tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird")) + tk.MustExec("SET GLOBAL default_storage_engine = 'somethingweird2'") + tk.MustExec("SET default_storage_engine = @@GLOBAL.default_storage_engine") // variation of example + tk.MustQuery("SELECT @@default_storage_engine").Check(testkit.Rows("somethingweird2")) + tk.MustExec("SET default_storage_engine = DEFAULT") // restore default again for session global + tk.MustExec("SET GLOBAL default_storage_engine = DEFAULT") // restore default for global + tk.MustQuery("SELECT @@SESSION.default_storage_engine, @@GLOBAL.default_storage_engine").Check(testkit.Rows("somethingweird2 InnoDB")) + + // Try sql_mode option which has validation + err := tk.ExecToErr("SET GLOBAL sql_mode = 'DEFAULT'") // illegal now + require.EqualError(t, err, `ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of 'DEFAULT'`) + tk.MustExec("SET GLOBAL sql_mode = DEFAULT") +} + +func TestTiDBReadOnly(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // turn on tidb_restricted_read_only should turn on tidb_super_read_only + tk.MustExec("SET GLOBAL tidb_restricted_read_only = ON") + tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("1")) + + // can't turn off tidb_super_read_only if tidb_restricted_read_only is on + err := tk.ExecToErr("SET GLOBAL tidb_super_read_only = OFF") + require.Error(t, err) + require.Equal(t, "can't turn off tidb_super_read_only when tidb_restricted_read_only is on", err.Error()) + + // turn off tidb_restricted_read_only won't affect tidb_super_read_only + tk.MustExec("SET GLOBAL tidb_restricted_read_only = OFF") + tk.MustQuery("SELECT @@GLOBAL.tidb_restricted_read_only").Check(testkit.Rows("0")) + tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("1")) + + // it is ok to turn off tidb_super_read_only now + tk.MustExec("SET GLOBAL tidb_super_read_only = OFF") + tk.MustQuery("SELECT @@GLOBAL.tidb_super_read_only").Check(testkit.Rows("0")) +} + +func TestRemovedSysVars(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test for tidb_enable_noop_functions + // In SET context, it just noops: + tk.MustExec(`SET tidb_enable_global_temporary_table = 1`) + tk.MustExec(`SET tidb_slow_log_masking = 1`) + tk.MustExec(`SET GLOBAL tidb_enable_global_temporary_table = 1`) + tk.MustExec(`SET GLOBAL tidb_slow_log_masking = 1`) + + // In SELECT context it returns a specifc error + // (to avoid presenting dummy data) + tk.MustGetErrCode("SELECT @@tidb_slow_log_masking", errno.ErrVariableNoLongerSupported) + tk.MustGetErrCode("SELECT @@tidb_enable_global_temporary_table", errno.ErrVariableNoLongerSupported) +} + +func TestSetClusterConfig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + serversInfo := []infoschema.ServerInfo{ + {ServerType: "tidb", Address: "127.0.0.1:1111", StatusAddr: "127.0.0.1:1111"}, + {ServerType: "tidb", Address: "127.0.0.1:2222", StatusAddr: "127.0.0.1:2222"}, + {ServerType: "pd", Address: "127.0.0.1:3333", StatusAddr: "127.0.0.1:3333"}, + {ServerType: "pd", Address: "127.0.0.1:4444", StatusAddr: "127.0.0.1:4444"}, + {ServerType: "tikv", Address: "127.0.0.1:5555", StatusAddr: "127.0.0.1:5555"}, + {ServerType: "tikv", Address: "127.0.0.1:6666", StatusAddr: "127.0.0.1:6666"}, + {ServerType: "tiflash", Address: "127.0.0.1:3933", StatusAddr: "127.0.0.1:7777"}, + } + var serverInfoErr error + serverInfoFunc := func(sessionctx.Context) ([]infoschema.ServerInfo, error) { + return serversInfo, serverInfoErr + } + tk.Session().SetValue(executor.TestSetConfigServerInfoKey, serverInfoFunc) + + require.EqualError(t, tk.ExecToErr("set config xxx log.level='info'"), "unknown type xxx") + require.EqualError(t, tk.ExecToErr("set config tidb log.level='info'"), "TiDB doesn't support to change configs online, please use SQL variables") + require.EqualError(t, tk.ExecToErr("set config '127.0.0.1:1111' log.level='info'"), "TiDB doesn't support to change configs online, please use SQL variables") + require.EqualError(t, tk.ExecToErr("set config '127.a.b.c:1234' log.level='info'"), "invalid instance 127.a.b.c:1234") // name doesn't resolve. + require.EqualError(t, tk.ExecToErr("set config 'example.com:1111' log.level='info'"), "instance example.com:1111 is not found in this cluster") // name resolves. + require.EqualError(t, tk.ExecToErr("set config tikv log.level=null"), "can't set config to null") + require.EqualError(t, tk.ExecToErr("set config '1.1.1.1:1111' log.level='info'"), "instance 1.1.1.1:1111 is not found in this cluster") + require.EqualError(t, tk.ExecToErr("set config tikv `raftstore.max-peer-down-duration`=DEFAULT"), "Unknown DEFAULT for SET CONFIG") + require.ErrorContains(t, tk.ExecToErr("set config tiflash `server.snap-max-write-bytes-per-sec`='500MB'"), "This command can only change config items begin with 'raftstore-proxy'") + + httpCnt := 0 + tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { + httpCnt++ + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }) + tk.MustExec("set config tikv log.level='info'") + require.Equal(t, 2, httpCnt) + + httpCnt = 0 + tk.MustExec("set config '127.0.0.1:5555' log.level='info'") + require.Equal(t, 1, httpCnt) + + httpCnt = 0 + tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(req *http.Request) (*http.Response, error) { + httpCnt++ + body, err := io.ReadAll(req.Body) + require.NoError(t, err) + // The `raftstore.` prefix is stripped. + require.JSONEq(t, `{"server.snap-max-write-bytes-per-sec":"500MB"}`, string(body)) + return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(nil)}, nil + }) + tk.MustExec("set config tiflash `raftstore-proxy.server.snap-max-write-bytes-per-sec`='500MB'") + require.Equal(t, 1, httpCnt) + + httpCnt = 0 + tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { + return nil, errors.New("something wrong") + }) + tk.MustExec("set config tikv log.level='info'") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1105 something wrong", "Warning 1105 something wrong")) + + tk.Session().SetValue(executor.TestSetConfigHTTPHandlerKey, func(*http.Request) (*http.Response, error) { + return &http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewBufferString("WRONG"))}, nil + }) + tk.MustExec("set config tikv log.level='info'") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1105 bad request to http://127.0.0.1:5555/config: WRONG", "Warning 1105 bad request to http://127.0.0.1:6666/config: WRONG")) +} + +func TestSetClusterConfigJSONData(t *testing.T) { + var d types.MyDecimal + require.NoError(t, d.FromFloat64(123.456)) + tyBool := types.NewFieldType(mysql.TypeTiny) + tyBool.AddFlag(mysql.IsBooleanFlag) + cases := []struct { + val expression.Expression + result string + succ bool + }{ + {&expression.Constant{Value: types.NewIntDatum(1), RetType: tyBool}, `{"k":true}`, true}, + {&expression.Constant{Value: types.NewIntDatum(0), RetType: tyBool}, `{"k":false}`, true}, + {&expression.Constant{Value: types.NewIntDatum(2333), RetType: types.NewFieldType(mysql.TypeLong)}, `{"k":2333}`, true}, + {&expression.Constant{Value: types.NewFloat64Datum(23.33), RetType: types.NewFieldType(mysql.TypeDouble)}, `{"k":23.33}`, true}, + {&expression.Constant{Value: types.NewStringDatum("abcd"), RetType: types.NewFieldType(mysql.TypeString)}, `{"k":"abcd"}`, true}, + {&expression.Constant{Value: types.NewDecimalDatum(&d), RetType: types.NewFieldType(mysql.TypeNewDecimal)}, `{"k":123.456}`, true}, + {&expression.Constant{Value: types.NewDatum(nil), RetType: types.NewFieldType(mysql.TypeLonglong)}, "", false}, + {&expression.Constant{RetType: types.NewFieldType(mysql.TypeJSON)}, "", false}, // unsupported type + {nil, "", false}, + {&expression.Constant{Value: types.NewDatum(`["no","no","lz4","lz4","lz4","zstd","zstd"]`), RetType: types.NewFieldType(mysql.TypeString)}, `{"k":"[\"no\",\"no\",\"lz4\",\"lz4\",\"lz4\",\"zstd\",\"zstd\"]"}`, true}, + } + + ctx := mock.NewContext() + for _, c := range cases { + result, err := executor.ConvertConfigItem2JSON(ctx, "k", c.val) + if c.succ { + require.Equal(t, result, c.result) + } else { + require.Error(t, err) + } + } +} + +func TestSetTopSQLVariables(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop")) + }() + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_top_sql='On';") + tk.MustQuery("select @@global.tidb_enable_top_sql;").Check(testkit.Rows("1")) + tk.MustExec("set @@global.tidb_enable_top_sql='off';") + tk.MustQuery("select @@global.tidb_enable_top_sql;").Check(testkit.Rows("0")) + + tk.MustExec("set @@global.tidb_top_sql_max_time_series_count=20;") + tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("20")) + require.Equal(t, int64(20), topsqlstate.GlobalState.MaxStatementCount.Load()) + err := tk.ExecToErr("set @@global.tidb_top_sql_max_time_series_count='abc';") + require.EqualError(t, err, "[variable:1232]Incorrect argument type to variable 'tidb_top_sql_max_time_series_count'") + tk.MustExec("set @@global.tidb_top_sql_max_time_series_count='-1';") + tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("1")) + tk.MustExec("set @@global.tidb_top_sql_max_time_series_count='5001';") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_time_series_count value: '5001'")) + tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("5000")) + + tk.MustExec("set @@global.tidb_top_sql_max_time_series_count=20;") + tk.MustQuery("select @@global.tidb_top_sql_max_time_series_count;").Check(testkit.Rows("20")) + require.Equal(t, int64(20), topsqlstate.GlobalState.MaxStatementCount.Load()) + + tk.MustExec("set @@global.tidb_top_sql_max_meta_count=10000;") + tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("10000")) + require.Equal(t, int64(10000), topsqlstate.GlobalState.MaxCollect.Load()) + err = tk.ExecToErr("set @@global.tidb_top_sql_max_meta_count='abc';") + require.EqualError(t, err, "[variable:1232]Incorrect argument type to variable 'tidb_top_sql_max_meta_count'") + tk.MustExec("set @@global.tidb_top_sql_max_meta_count='-1';") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_meta_count value: '-1'")) + tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("1")) + + tk.MustExec("set @@global.tidb_top_sql_max_meta_count='10001';") + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_top_sql_max_meta_count value: '10001'")) + tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("10000")) + + tk.MustExec("set @@global.tidb_top_sql_max_meta_count=5000;") + tk.MustQuery("select @@global.tidb_top_sql_max_meta_count;").Check(testkit.Rows("5000")) + require.Equal(t, int64(5000), topsqlstate.GlobalState.MaxCollect.Load()) + + tk.MustQuery("show variables like '%top_sql%'").Check(testkit.Rows("tidb_enable_top_sql OFF", "tidb_top_sql_max_meta_count 5000", "tidb_top_sql_max_time_series_count 20")) + tk.MustQuery("show global variables like '%top_sql%'").Check(testkit.Rows("tidb_enable_top_sql OFF", "tidb_top_sql_max_meta_count 5000", "tidb_top_sql_max_time_series_count 20")) +} + +func TestPreparePlanCacheValid(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // global scope + tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value + tk.MustExec("SET GLOBAL tidb_session_plan_cache_size = 0") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1292 Truncated incorrect tidb_session_plan_cache_size value: '0'")) + tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL tidb_session_plan_cache_size = 2") + tk.MustQuery("select @@global.tidb_session_plan_cache_size").Check(testkit.Rows("2")) + // session scope + tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("100")) // default value + tk.MustExec("SET SESSION tidb_session_plan_cache_size = 0") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1292 Truncated incorrect tidb_session_plan_cache_size value: '0'")) + tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("1")) + tk.MustExec("SET SESSION tidb_session_plan_cache_size = 2") + tk.MustQuery("select @@session.tidb_session_plan_cache_size").Check(testkit.Rows("2")) + + tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = -0.1") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1292 Truncated incorrect tidb_prepared_plan_cache_memory_guard_ratio value: '-0.1'")) + tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("0")) + tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = 2.2") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1292 Truncated incorrect tidb_prepared_plan_cache_memory_guard_ratio value: '2.2'")) + tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL tidb_prepared_plan_cache_memory_guard_ratio = 0.5") + tk.MustQuery("select @@global.tidb_prepared_plan_cache_memory_guard_ratio").Check(testkit.Rows("0.5")) + + tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 0") + tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("0")) + tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 1") + tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL tidb_enable_prepared_plan_cache = 0") + tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache").Check(testkit.Rows("0")) +} + +func TestInstanceScopeSwitching(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // enable 'switching' to SESSION variables + tk.MustExec("set tidb_enable_legacy_instance_scope = 1") + tk.MustExec("set tidb_general_log = 1") + tk.MustQuery(`show warnings`).Check(testkit.Rows(fmt.Sprintf("Warning %d modifying tidb_general_log will require SET GLOBAL in a future version of TiDB", errno.ErrInstanceScope))) + + // disable 'switching' to SESSION variables + tk.MustExec("set tidb_enable_legacy_instance_scope = 0") + tk.MustGetErrCode("set tidb_general_log = 1", errno.ErrGlobalVariable) +} + +func TestGcMaxWaitTime(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("set global tidb_gc_max_wait_time = 1000") + tk.MustExec("set global tidb_gc_life_time = \"72h\"") + tk.MustExec("set global tidb_gc_life_time = \"24h\"") + tk.MustExec("set global tidb_gc_life_time = \"10m\"") + + tk.MustExec("set global tidb_gc_max_wait_time = 86400") + tk.MustExec("set global tidb_gc_life_time = \"72h\"") + tk.MustExec("set global tidb_gc_max_wait_time = 1000") +} + +func TestTiFlashFineGrainedShuffle(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // Default is 0. + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) + + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) + // Min val is -1. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = -2") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("-1")) + + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("0")) + + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 1024") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("1024")) + // Max val is 1024. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 1025") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_stream_count;").Check(testkit.Rows("1024")) + + // Default is 8192. + tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("8192")) + + // Min is 1. + tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = 0") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("1")) + tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = -1") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("1")) + + // Max is uint64_max. + tk.MustExec("set @@tiflash_fine_grained_shuffle_batch_size = 18446744073709551615") + tk.MustQuery("select @@tiflash_fine_grained_shuffle_batch_size;").Check(testkit.Rows("18446744073709551615")) + + // Test set global. + tk.MustExec("set global tiflash_fine_grained_shuffle_stream_count = -1") + tk.MustExec("set global tiflash_fine_grained_shuffle_batch_size = 8192") +} + +func TestSetTiFlashFastScanVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(1);") + + // check the default tiflash read mode + tk.MustQuery("select @@session.tiflash_fastscan").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.tiflash_fastscan").Check(testkit.Rows("0")) + + tk.MustExec("set @@tiflash_fastscan=ON;") + tk.MustQuery("select @@session.tiflash_fastscan").Check(testkit.Rows("1")) + + tk.MustExec("set GLOBAL tiflash_fastscan=OFF;") + tk.MustQuery("select @@global.tiflash_fastscan").Check(testkit.Rows("0")) +} + +func TestSetPlanCacheMemoryMonitor(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) + tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) + + tk.MustExec("set @@session.tidb_enable_prepared_plan_cache_memory_monitor=OFF;") + tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("0")) + + tk.MustExec("set @@session.tidb_enable_prepared_plan_cache_memory_monitor=1;") + tk.MustQuery("select @@session.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("1")) + + tk.MustExec("set @@global.tidb_enable_prepared_plan_cache_memory_monitor=off;") + tk.MustQuery("select @@global.tidb_enable_prepared_plan_cache_memory_monitor").Check(testkit.Rows("0")) +} + +func TestSetChunkReuseVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_enable_reuse_chunk=ON;") + tk.MustQuery("select @@session.tidb_enable_reuse_chunk").Check(testkit.Rows("1")) + tk.MustExec("set GLOBAL tidb_enable_reuse_chunk=ON;") + tk.MustQuery("select @@global.tidb_enable_reuse_chunk").Check(testkit.Rows("1")) + + tk.MustExec("set @@tidb_enable_reuse_chunk=OFF;") + tk.MustQuery("select @@session.tidb_enable_reuse_chunk").Check(testkit.Rows("0")) + tk.MustExec("set GLOBAL tidb_enable_reuse_chunk=OFF;") + tk.MustQuery("select @@global.tidb_enable_reuse_chunk").Check(testkit.Rows("0")) + + // error value + tk.MustGetErrCode("set @@tidb_enable_reuse_chunk=s;", errno.ErrWrongValueForVar) +} + +func TestSetMppVersionVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("UNSPECIFIED")) + tk.MustExec("SET SESSION mpp_version = -1") + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("-1")) + tk.MustExec("SET SESSION mpp_version = 0") + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("0")) + tk.MustExec("SET SESSION mpp_version = 1") + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("1")) + tk.MustExec("SET SESSION mpp_version = 2") + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("2")) + tk.MustExec("SET SESSION mpp_version = unspecified") + tk.MustQuery("select @@session.mpp_version").Check(testkit.Rows("unspecified")) + { + tk.MustGetErrMsg("SET SESSION mpp_version = 3", "incorrect value: 3. mpp_version options: -1 (unspecified), 0, 1, 2") + } + { + tk.MustExec("SET GLOBAL mpp_version = 1") + tk.MustQuery("select @@global.mpp_version").Check(testkit.Rows("1")) + tk.MustExec("SET GLOBAL mpp_version = -1") + tk.MustQuery("select @@global.mpp_version").Check(testkit.Rows("-1")) + } +} + +func TestSetMppExchangeCompressionModeVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustGetErrMsg( + "SET SESSION mpp_exchange_compression_mode = 123", + "incorrect value: `123`. mpp_exchange_compression_mode options: NONE, FAST, HIGH_COMPRESSION, UNSPECIFIED") + tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("UNSPECIFIED")) + + tk.MustExec("SET SESSION mpp_exchange_compression_mode = none") + tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("none")) + tk.MustExec("SET SESSION mpp_exchange_compression_mode = fast") + tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("fast")) + tk.MustExec("SET SESSION mpp_exchange_compression_mode = HIGH_COMPRESSION") + tk.MustQuery("select @@session.mpp_exchange_compression_mode").Check(testkit.Rows("HIGH_COMPRESSION")) + + { + tk.MustExec("SET GLOBAL mpp_exchange_compression_mode = none") + tk.MustQuery("select @@global.mpp_exchange_compression_mode").Check(testkit.Rows("none")) + } + { + tk.MustExec("SET mpp_version = 0") + tk.MustExec("SET mpp_exchange_compression_mode = unspecified") + require.Equal(t, len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()), 0) + } + { + tk.MustExec("SET mpp_version = 0") + tk.MustExec("SET mpp_exchange_compression_mode = HIGH_COMPRESSION") + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Equal(t, len(warnings), 1) + require.Equal(t, warnings[0].Err.Error(), "mpp exchange compression won't work under current mpp version 0") + } +} + +func TestDeprecateEnableTiFlashPipelineModel(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@global.tidb_enable_tiflash_pipeline_model = 1`) + tk.MustQuery(`show warnings`).Check(testkit.Rows("Warning 1681 tidb_enable_tiflash_pipeline_model is deprecated and will be removed in a future release.")) +} diff --git a/executor/show.go b/pkg/executor/show.go similarity index 97% rename from executor/show.go rename to pkg/executor/show.go index 4af89c8c9d73a..4202603a45c03 100644 --- a/executor/show.go +++ b/pkg/executor/show.go @@ -28,59 +28,59 @@ import ( "github.com/docker/go-units" "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - fstorage "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - parserformat "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/parser/tidb" - field_types "github.com/pingcap/tidb/parser/types" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tidb-binlog/node" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/etcd" - "github.com/pingcap/tidb/util/filter" - "github.com/pingcap/tidb/util/format" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + fstorage "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + parserformat "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/parser/tidb" + field_types "github.com/pingcap/tidb/pkg/parser/types" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/filter" + "github.com/pingcap/tidb/pkg/util/format" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/tikv/client-go/v2/oracle" ) diff --git a/executor/show_placement.go b/pkg/executor/show_placement.go similarity index 96% rename from executor/show_placement.go rename to pkg/executor/show_placement.go index 56972be7f7a6b..6af87d39bf3ce 100644 --- a/executor/show_placement.go +++ b/pkg/executor/show_placement.go @@ -24,19 +24,19 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) type showPlacementLabelsResultBuilder struct { diff --git a/executor/show_placement_labels_test.go b/pkg/executor/show_placement_labels_test.go similarity index 96% rename from executor/show_placement_labels_test.go rename to pkg/executor/show_placement_labels_test.go index e1970ba62d80c..33bf42b611d55 100644 --- a/executor/show_placement_labels_test.go +++ b/pkg/executor/show_placement_labels_test.go @@ -18,8 +18,8 @@ import ( gjson "encoding/json" "testing" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/executor/show_placement_test.go b/pkg/executor/show_placement_test.go similarity index 99% rename from executor/show_placement_test.go rename to pkg/executor/show_placement_test.go index db5b35fb8e0c5..6fbd6a52135e3 100644 --- a/executor/show_placement_test.go +++ b/pkg/executor/show_placement_test.go @@ -18,10 +18,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" ) diff --git a/executor/show_stats.go b/pkg/executor/show_stats.go similarity index 98% rename from executor/show_stats.go rename to pkg/executor/show_stats.go index d017e871f143f..70d55a0896cda 100644 --- a/executor/show_stats.go +++ b/pkg/executor/show_stats.go @@ -22,14 +22,14 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/tikv/client-go/v2/oracle" ) diff --git a/executor/show_stats_test.go b/pkg/executor/show_stats_test.go similarity index 99% rename from executor/show_stats_test.go rename to pkg/executor/show_stats_test.go index e3e3a0d13d635..673c63f76a21f 100644 --- a/executor/show_stats_test.go +++ b/pkg/executor/show_stats_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/show_test.go b/pkg/executor/show_test.go new file mode 100644 index 0000000000000..17c44771cea84 --- /dev/null +++ b/pkg/executor/show_test.go @@ -0,0 +1,66 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/stretchr/testify/require" +) + +func Test_fillOneImportJobInfo(t *testing.T) { + typeBytes := []byte{mysql.TypeLonglong, mysql.TypeString, mysql.TypeString, mysql.TypeLonglong, + mysql.TypeString, mysql.TypeString, mysql.TypeString, mysql.TypeLonglong, + mysql.TypeString, mysql.TypeTimestamp, mysql.TypeTimestamp, mysql.TypeTimestamp, mysql.TypeString} + fieldTypes := make([]*types.FieldType, 0, len(typeBytes)) + for _, tp := range typeBytes { + fieldType := types.NewFieldType(tp) + flen, decimal := mysql.GetDefaultFieldLengthAndDecimal(tp) + fieldType.SetFlen(flen) + fieldType.SetDecimal(decimal) + charset, collate := types.DefaultCharsetForType(tp) + fieldType.SetCharset(charset) + fieldType.SetCollate(collate) + fieldTypes = append(fieldTypes, fieldType) + } + c := chunk.New(fieldTypes, 10, 10) + jobInfo := &importer.JobInfo{ + Parameters: importer.ImportParameters{}, + } + fillOneImportJobInfo(jobInfo, c, -1) + require.True(t, c.GetRow(0).IsNull(7)) + require.True(t, c.GetRow(0).IsNull(10)) + require.True(t, c.GetRow(0).IsNull(11)) + + fillOneImportJobInfo(jobInfo, c, 0) + require.False(t, c.GetRow(1).IsNull(7)) + require.Equal(t, uint64(0), c.GetRow(1).GetUint64(7)) + require.True(t, c.GetRow(1).IsNull(10)) + require.True(t, c.GetRow(1).IsNull(11)) + + jobInfo.Summary = &importer.JobSummary{ImportedRows: 123} + jobInfo.StartTime = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 0) + jobInfo.EndTime = types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 0) + fillOneImportJobInfo(jobInfo, c, 0) + require.False(t, c.GetRow(2).IsNull(7)) + require.Equal(t, uint64(123), c.GetRow(2).GetUint64(7)) + require.False(t, c.GetRow(2).IsNull(10)) + require.False(t, c.GetRow(2).IsNull(11)) +} diff --git a/executor/shuffle.go b/pkg/executor/shuffle.go similarity index 96% rename from executor/shuffle.go rename to pkg/executor/shuffle.go index 8b78fedd9a767..c1b9a4a0faecd 100644 --- a/executor/shuffle.go +++ b/pkg/executor/shuffle.go @@ -20,15 +20,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/channel" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/channel" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/twmb/murmur3" "go.uber.org/zap" ) diff --git a/executor/shuffle_test.go b/pkg/executor/shuffle_test.go similarity index 89% rename from executor/shuffle_test.go rename to pkg/executor/shuffle_test.go index b816023c958b2..0bb134256dab4 100644 --- a/executor/shuffle_test.go +++ b/pkg/executor/shuffle_test.go @@ -17,11 +17,11 @@ package executor import ( "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/executor/simple.go b/pkg/executor/simple.go similarity index 98% rename from executor/simple.go rename to pkg/executor/simple.go index 2e54dc2dc78b9..19e52a9f3ae16 100644 --- a/executor/simple.go +++ b/pkg/executor/simple.go @@ -28,44 +28,44 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/querywatch" - executor_metrics "github.com/pingcap/tidb/executor/metrics" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/globalconn" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - pwdValidator "github.com/pingcap/tidb/util/password-validation" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tidb/util/tls" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/querywatch" + executor_metrics "github.com/pingcap/tidb/pkg/executor/metrics" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/globalconn" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + pwdValidator "github.com/pingcap/tidb/pkg/util/password-validation" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tidb/pkg/util/tls" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/pkg/executor/simple_test.go b/pkg/executor/simple_test.go new file mode 100644 index 0000000000000..39460920a616d --- /dev/null +++ b/pkg/executor/simple_test.go @@ -0,0 +1,163 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "context" + "fmt" + "strconv" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/globalconn" + "github.com/stretchr/testify/require" +) + +func TestKillStmt(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + sv := server.CreateMockServer(t, store) + sv.SetDomain(dom) + defer sv.Close() + + conn1 := server.CreateMockConn(t, sv) + tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) + + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + newCfg.EnableGlobalKill = false + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + }() + + connID := conn1.ID() + + tk.MustExec("use test") + tk.MustExec(fmt.Sprintf("kill %d", connID)) + result := tk.MustQuery("show warnings") + result.Check(testkit.Rows("Warning 1105 Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] [connectionID | CONNECTION_ID()]' instead")) + + newCfg2 := *originCfg + newCfg2.EnableGlobalKill = true + config.StoreGlobalConfig(&newCfg2) + + // ZERO serverID, treated as truncated. + tk.MustExec("kill 1") + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows("Warning 1105 Kill failed: Received a 32bits truncated ConnectionID, expect 64bits. Please execute 'KILL [CONNECTION | QUERY] ConnectionID' to send a Kill without truncating ConnectionID.")) + + // truncated + tk.MustExec("kill 101") + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows("Warning 1105 Kill failed: Received a 32bits truncated ConnectionID, expect 64bits. Please execute 'KILL [CONNECTION | QUERY] ConnectionID' to send a Kill without truncating ConnectionID.")) + + // excceed int64 + tk.MustExec("kill 9223372036854775808") // 9223372036854775808 == 2^63 + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows("Warning 1105 Parse ConnectionID failed: unexpected connectionID exceeds int64")) + + // local kill + connIDAllocator := globalconn.NewGlobalAllocator(dom.ServerID, false) + killConnID := connIDAllocator.NextID() + tk.MustExec("kill " + strconv.FormatUint(killConnID, 10)) + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows()) + + tk.MustExecToErr("kill rand()", "Invalid operation. Please use 'KILL TIDB [CONNECTION | QUERY] [connectionID | CONNECTION_ID()]' instead") + // remote kill is tested in `tests/globalkilltest` +} + +func TestUserAttributes(t *testing.T) { + store := testkit.CreateMockStore(t) + rootTK := testkit.NewTestKit(t, store) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + + // https://dev.mysql.com/doc/refman/8.0/en/create-user.html#create-user-comments-attributes + rootTK.MustExec(`CREATE USER testuser COMMENT '1234'`) + rootTK.MustExec(`CREATE USER testuser1 ATTRIBUTE '{"name": "Tom", "age": 19}'`) + _, err := rootTK.Exec(`CREATE USER testuser2 ATTRIBUTE '{"name": "Tom", age: 19}'`) + rootTK.MustExec(`CREATE USER testuser2`) + require.Error(t, err) + rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser'`).Check(testkit.Rows(`{"metadata": {"comment": "1234"}}`)) + rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser1'`).Check(testkit.Rows(`{"metadata": {"age": 19, "name": "Tom"}}`)) + rootTK.MustQuery(`SELECT user_attributes FROM mysql.user WHERE user = 'testuser2'`).Check(testkit.Rows(`{}`)) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser'`).Check(testkit.Rows(`{"comment": "1234"}`)) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 19, "name": "Tom"}`)) + rootTK.MustQueryWithContext(ctx, `SELECT attribute->>"$.age" AS age, attribute->>"$.name" AS name FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`19 Tom`)) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser2'`).Check(testkit.Rows(``)) + + // https://dev.mysql.com/doc/refman/8.0/en/alter-user.html#alter-user-comments-attributes + rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"age": 20, "sex": "male"}'`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom", "sex": "male"}`)) + rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"hobby": "soccer"}'`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "hobby": "soccer", "name": "Tom", "sex": "male"}`)) + rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"sex": null, "hobby": null}'`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`)) + rootTK.MustExec(`ALTER USER testuser1 COMMENT '5678'`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "5678", "name": "Tom"}`)) + rootTK.MustExec(`ALTER USER testuser1 COMMENT ''`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "comment": "", "name": "Tom"}`)) + rootTK.MustExec(`ALTER USER testuser1 ATTRIBUTE '{"comment": null}'`) + rootTK.MustQueryWithContext(ctx, `SELECT attribute FROM information_schema.user_attributes WHERE user = 'testuser1'`).Check(testkit.Rows(`{"age": 20, "name": "Tom"}`)) + + // Non-root users could access COMMENT or ATTRIBUTE of all users via the view, + // but not via the mysql.user table. + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "testuser1"}, nil, nil, nil)) + tk.MustQueryWithContext(ctx, `SELECT user, host, attribute FROM information_schema.user_attributes ORDER BY user`).Check( + testkit.Rows("root % ", "testuser % {\"comment\": \"1234\"}", "testuser1 % {\"age\": 20, \"name\": \"Tom\"}", "testuser2 % ")) + tk.MustGetErrCode(`SELECT user, host, user_attributes FROM mysql.user ORDER BY user`, mysql.ErrTableaccessDenied) + + // https://github.com/pingcap/tidb/issues/39207 + rootTK.MustExec("create user usr1@'%' identified by 'passord'") + rootTK.MustExec("alter user usr1 comment 'comment1'") + rootTK.MustQuery("select user_attributes from mysql.user where user = 'usr1'").Check(testkit.Rows(`{"metadata": {"comment": "comment1"}}`)) + rootTK.MustExec("set global tidb_enable_resource_control = 'on'") + rootTK.MustExec("CREATE RESOURCE GROUP rg1 ru_per_sec = 100") + rootTK.MustExec("alter user usr1 resource group rg1") + rootTK.MustQuery("select user_attributes from mysql.user where user = 'usr1'").Check(testkit.Rows(`{"metadata": {"comment": "comment1"}, "resource_group": "rg1"}`)) +} + +func TestSetResourceGroup(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("SET GLOBAL tidb_enable_resource_control='on'") + + tk.MustContainErrMsg("SET RESOURCE GROUP rg1", "Unknown resource group 'rg1'") + + tk.MustExec("CREATE RESOURCE GROUP rg1 ru_per_sec = 100") + tk.MustExec("ALTER USER `root` RESOURCE GROUP `rg1`") + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg1")) + + tk.MustExec("CREATE RESOURCE GROUP rg2 ru_per_sec = 200") + tk.MustExec("SET RESOURCE GROUP `rg2`") + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg2")) + tk.MustExec("SET RESOURCE GROUP ``") + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) + tk.MustExec("SET RESOURCE GROUP default") + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("default")) + + tk.RefreshSession() + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustQuery("SELECT CURRENT_RESOURCE_GROUP()").Check(testkit.Rows("rg1")) +} diff --git a/executor/slow_query.go b/pkg/executor/slow_query.go similarity index 97% rename from executor/slow_query.go rename to pkg/executor/slow_query.go index f057b99e7fb03..7034d4511feef 100644 --- a/executor/slow_query.go +++ b/pkg/executor/slow_query.go @@ -32,23 +32,23 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/plancodec" "go.uber.org/zap" ) diff --git a/executor/slow_query_sql_test.go b/pkg/executor/slow_query_sql_test.go similarity index 98% rename from executor/slow_query_sql_test.go rename to pkg/executor/slow_query_sql_test.go index 4764e0ae08038..417a3aa59f726 100644 --- a/executor/slow_query_sql_test.go +++ b/pkg/executor/slow_query_sql_test.go @@ -20,13 +20,13 @@ import ( "os" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" ) diff --git a/executor/slow_query_test.go b/pkg/executor/slow_query_test.go similarity index 96% rename from executor/slow_query_test.go rename to pkg/executor/slow_query_test.go index 73b3bd4dee638..fa15d46b59689 100644 --- a/executor/slow_query_test.go +++ b/pkg/executor/slow_query_test.go @@ -26,17 +26,17 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) @@ -100,9 +100,9 @@ func TestParseSlowLogPanic(t *testing.T) { # Prev_stmt: update t set i = 1; use test; select * from t;` - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/errorMockParseSlowLogPanic", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/errorMockParseSlowLogPanic", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/errorMockParseSlowLogPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/errorMockParseSlowLogPanic")) }() reader := bufio.NewReader(bytes.NewBufferString(slowLogStr)) loc, err := time.LoadLocation("Asia/Shanghai") @@ -678,10 +678,10 @@ select * from t;` var signal1, signal2 = make(chan int, 1), make(chan int, 1) ctx := context.WithValue(context.Background(), signalsKey{}, []chan int{signal1, signal2}) ctx, cancel := context.WithCancel(ctx) - err = failpoint.Enable("github.com/pingcap/tidb/executor/mockReadSlowLogSlow", "return(true)") + err = failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockReadSlowLogSlow", "return(true)") require.NoError(t, err) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockReadSlowLogSlow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockReadSlowLogSlow")) }() go func() { _, err := retriever.retrieve(ctx, sctx) diff --git a/pkg/executor/sort.go b/pkg/executor/sort.go new file mode 100644 index 0000000000000..793e6f2aa2545 --- /dev/null +++ b/pkg/executor/sort.go @@ -0,0 +1,550 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "container/heap" + "context" + "errors" + "slices" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" +) + +// SortExec represents sorting executor. +type SortExec struct { + exec.BaseExecutor + + ByItems []*util.ByItems + Idx int + fetched bool + schema *expression.Schema + + // keyColumns is the column index of the by items. + keyColumns []int + // keyCmpFuncs is used to compare each ByItem. + keyCmpFuncs []chunk.CompareFunc + // rowChunks is the chunks to store row values. + rowChunks *chunk.SortedRowContainer + + memTracker *memory.Tracker + diskTracker *disk.Tracker + + // partitionList is the chunks to store row values for partitions. Every partition is a sorted list. + partitionList []*chunk.SortedRowContainer + + // multiWayMerge uses multi-way merge for spill disk. + // The multi-way merge algorithm can refer to https://en.wikipedia.org/wiki/K-way_merge_algorithm + multiWayMerge *multiWayMerge + // spillAction save the Action for spill disk. + spillAction *chunk.SortAndSpillDiskAction +} + +// Close implements the Executor Close interface. +func (e *SortExec) Close() error { + for _, container := range e.partitionList { + err := container.Close() + if err != nil { + return err + } + } + e.partitionList = e.partitionList[:0] + + if e.rowChunks != nil { + e.memTracker.Consume(-e.rowChunks.GetMemTracker().BytesConsumed()) + e.rowChunks = nil + } + e.memTracker = nil + e.diskTracker = nil + e.multiWayMerge = nil + if e.spillAction != nil { + e.spillAction.SetFinished() + } + e.spillAction = nil + return e.Children(0).Close() +} + +// Open implements the Executor Open interface. +func (e *SortExec) Open(ctx context.Context) error { + e.fetched = false + e.Idx = 0 + + // To avoid duplicated initialization for TopNExec. + if e.memTracker == nil { + e.memTracker = memory.NewTracker(e.ID(), -1) + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + e.diskTracker = memory.NewTracker(e.ID(), -1) + e.diskTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.DiskTracker) + } + e.partitionList = e.partitionList[:0] + return e.Children(0).Open(ctx) +} + +// Next implements the Executor Next interface. +// Sort constructs the result following these step: +// 1. Read as mush as rows into memory. +// 2. If memory quota is triggered, sort these rows in memory and put them into disk as partition 1, then reset +// the memory quota trigger and return to step 1 +// 3. If memory quota is not triggered and child is consumed, sort these rows in memory as partition N. +// 4. Merge sort if the count of partitions is larger than 1. If there is only one partition in step 4, it works +// just like in-memory sort before. +func (e *SortExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if !e.fetched { + e.initCompareFuncs() + e.buildKeyColumns() + err := e.fetchRowChunks(ctx) + if err != nil { + return err + } + e.fetched = true + } + + if len(e.partitionList) == 0 { + return nil + } + if len(e.partitionList) > 1 { + if err := e.externalSorting(req); err != nil { + return err + } + } else { + for !req.IsFull() && e.Idx < e.partitionList[0].NumRow() { + _, _, err := e.partitionList[0].GetSortedRowAndAlwaysAppendToChunk(e.Idx, req) + if err != nil { + return err + } + e.Idx++ + } + } + return nil +} + +func (e *SortExec) externalSorting(req *chunk.Chunk) (err error) { + if e.multiWayMerge == nil { + e.multiWayMerge = &multiWayMerge{e.lessRow, e.compressRow, make([]partitionPointer, 0, len(e.partitionList))} + for i := 0; i < len(e.partitionList); i++ { + chk := chunk.New(exec.RetTypes(e), 1, 1) + + row, _, err := e.partitionList[i].GetSortedRowAndAlwaysAppendToChunk(0, chk) + if err != nil { + return err + } + e.multiWayMerge.elements = append(e.multiWayMerge.elements, partitionPointer{chk: chk, row: row, partitionID: i, consumed: 0}) + } + heap.Init(e.multiWayMerge) + } + + for !req.IsFull() && e.multiWayMerge.Len() > 0 { + partitionPtr := e.multiWayMerge.elements[0] + req.AppendRow(partitionPtr.row) + partitionPtr.consumed++ + partitionPtr.chk.Reset() + if partitionPtr.consumed >= e.partitionList[partitionPtr.partitionID].NumRow() { + heap.Remove(e.multiWayMerge, 0) + continue + } + + partitionPtr.row, _, err = e.partitionList[partitionPtr.partitionID]. + GetSortedRowAndAlwaysAppendToChunk(partitionPtr.consumed, partitionPtr.chk) + if err != nil { + return err + } + e.multiWayMerge.elements[0] = partitionPtr + heap.Fix(e.multiWayMerge, 0) + } + return nil +} + +func (e *SortExec) fetchRowChunks(ctx context.Context) error { + fields := exec.RetTypes(e) + byItemsDesc := make([]bool, len(e.ByItems)) + for i, byItem := range e.ByItems { + byItemsDesc[i] = byItem.Desc + } + e.rowChunks = chunk.NewSortedRowContainer(fields, e.MaxChunkSize(), byItemsDesc, e.keyColumns, e.keyCmpFuncs) + e.rowChunks.GetMemTracker().AttachTo(e.memTracker) + e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) + if variable.EnableTmpStorageOnOOM.Load() { + e.spillAction = e.rowChunks.ActionSpill() + failpoint.Inject("testSortedRowContainerSpill", func(val failpoint.Value) { + if val.(bool) { + e.spillAction = e.rowChunks.ActionSpillForTest() + defer e.spillAction.WaitForTest() + } + }) + e.Ctx().GetSessionVars().MemTracker.FallbackOldAndSetNewAction(e.spillAction) + e.rowChunks.GetDiskTracker().AttachTo(e.diskTracker) + e.rowChunks.GetDiskTracker().SetLabel(memory.LabelForRowChunks) + } + for { + chk := exec.TryNewCacheChunk(e.Children(0)) + err := exec.Next(ctx, e.Children(0), chk) + if err != nil { + return err + } + rowCount := chk.NumRows() + if rowCount == 0 { + break + } + if err := e.rowChunks.Add(chk); err != nil { + if errors.Is(err, chunk.ErrCannotAddBecauseSorted) { + e.partitionList = append(e.partitionList, e.rowChunks) + e.rowChunks = chunk.NewSortedRowContainer(fields, e.MaxChunkSize(), byItemsDesc, e.keyColumns, e.keyCmpFuncs) + e.rowChunks.GetMemTracker().AttachTo(e.memTracker) + e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) + e.rowChunks.GetDiskTracker().AttachTo(e.diskTracker) + e.rowChunks.GetDiskTracker().SetLabel(memory.LabelForRowChunks) + e.spillAction = e.rowChunks.ActionSpill() + failpoint.Inject("testSortedRowContainerSpill", func(val failpoint.Value) { + if val.(bool) { + e.spillAction = e.rowChunks.ActionSpillForTest() + defer e.spillAction.WaitForTest() + } + }) + e.Ctx().GetSessionVars().MemTracker.FallbackOldAndSetNewAction(e.spillAction) + err = e.rowChunks.Add(chk) + } + if err != nil { + return err + } + } + } + failpoint.Inject("SignalCheckpointForSort", func(val failpoint.Value) { + if val.(bool) { + if e.Ctx().GetSessionVars().ConnectionID == 123456 { + e.Ctx().GetSessionVars().MemTracker.NeedKill.Store(true) + } + } + }) + if e.rowChunks.NumRow() > 0 { + err := e.rowChunks.Sort() + if err != nil { + return err + } + e.partitionList = append(e.partitionList, e.rowChunks) + } + return nil +} + +func (e *SortExec) initCompareFuncs() { + e.keyCmpFuncs = make([]chunk.CompareFunc, len(e.ByItems)) + for i := range e.ByItems { + keyType := e.ByItems[i].Expr.GetType() + e.keyCmpFuncs[i] = chunk.GetCompareFunc(keyType) + } +} + +func (e *SortExec) buildKeyColumns() { + e.keyColumns = make([]int, 0, len(e.ByItems)) + for _, by := range e.ByItems { + col := by.Expr.(*expression.Column) + e.keyColumns = append(e.keyColumns, col.Index) + } +} + +func (e *SortExec) lessRow(rowI, rowJ chunk.Row) bool { + for i, colIdx := range e.keyColumns { + cmpFunc := e.keyCmpFuncs[i] + cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) + if e.ByItems[i].Desc { + cmp = -cmp + } + if cmp < 0 { + return true + } else if cmp > 0 { + return false + } + } + return false +} + +func (e *SortExec) compressRow(rowI, rowJ chunk.Row) int { + for i, colIdx := range e.keyColumns { + cmpFunc := e.keyCmpFuncs[i] + cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) + if e.ByItems[i].Desc { + cmp = -cmp + } + if cmp != 0 { + return cmp + } + } + return 0 +} + +type partitionPointer struct { + chk *chunk.Chunk + row chunk.Row + partitionID int + consumed int +} + +type multiWayMerge struct { + lessRowFunction func(rowI chunk.Row, rowJ chunk.Row) bool + compressRowFunction func(rowI chunk.Row, rowJ chunk.Row) int + elements []partitionPointer +} + +func (h *multiWayMerge) Less(i, j int) bool { + rowI := h.elements[i].row + rowJ := h.elements[j].row + return h.lessRowFunction(rowI, rowJ) +} + +func (h *multiWayMerge) Len() int { + return len(h.elements) +} + +func (*multiWayMerge) Push(interface{}) { + // Should never be called. +} + +func (h *multiWayMerge) Pop() interface{} { + h.elements = h.elements[:len(h.elements)-1] + return nil +} + +func (h *multiWayMerge) Swap(i, j int) { + h.elements[i], h.elements[j] = h.elements[j], h.elements[i] +} + +// TopNExec implements a Top-N algorithm and it is built from a SELECT statement with ORDER BY and LIMIT. +// Instead of sorting all the rows fetched from the table, it keeps the Top-N elements only in a heap to reduce memory usage. +type TopNExec struct { + SortExec + limit *plannercore.PhysicalLimit + totalLimit uint64 + + // rowChunks is the chunks to store row values. + rowChunks *chunk.List + // rowPointer store the chunk index and row index for each row. + rowPtrs []chunk.RowPtr + + chkHeap *topNChunkHeap +} + +// topNChunkHeap implements heap.Interface. +type topNChunkHeap struct { + *TopNExec +} + +// Less implement heap.Interface, but since we mantains a max heap, +// this function returns true if row i is greater than row j. +func (h *topNChunkHeap) Less(i, j int) bool { + rowI := h.rowChunks.GetRow(h.rowPtrs[i]) + rowJ := h.rowChunks.GetRow(h.rowPtrs[j]) + return h.greaterRow(rowI, rowJ) +} + +func (h *topNChunkHeap) greaterRow(rowI, rowJ chunk.Row) bool { + for i, colIdx := range h.keyColumns { + cmpFunc := h.keyCmpFuncs[i] + cmp := cmpFunc(rowI, colIdx, rowJ, colIdx) + if h.ByItems[i].Desc { + cmp = -cmp + } + if cmp > 0 { + return true + } else if cmp < 0 { + return false + } + } + return false +} + +func (h *topNChunkHeap) Len() int { + return len(h.rowPtrs) +} + +func (*topNChunkHeap) Push(interface{}) { + // Should never be called. +} + +func (h *topNChunkHeap) Pop() interface{} { + h.rowPtrs = h.rowPtrs[:len(h.rowPtrs)-1] + // We don't need the popped value, return nil to avoid memory allocation. + return nil +} + +func (h *topNChunkHeap) Swap(i, j int) { + h.rowPtrs[i], h.rowPtrs[j] = h.rowPtrs[j], h.rowPtrs[i] +} + +// keyColumnsLess is the less function for key columns. +func (e *TopNExec) keyColumnsLess(i, j chunk.RowPtr) bool { + rowI := e.rowChunks.GetRow(i) + rowJ := e.rowChunks.GetRow(j) + return e.lessRow(rowI, rowJ) +} + +func (e *TopNExec) keyColumnsCompare(i, j chunk.RowPtr) int { + rowI := e.rowChunks.GetRow(i) + rowJ := e.rowChunks.GetRow(j) + return e.compressRow(rowI, rowJ) +} + +func (e *TopNExec) initPointers() { + e.rowPtrs = make([]chunk.RowPtr, 0, e.rowChunks.Len()) + e.memTracker.Consume(int64(8 * e.rowChunks.Len())) + for chkIdx := 0; chkIdx < e.rowChunks.NumChunks(); chkIdx++ { + rowChk := e.rowChunks.GetChunk(chkIdx) + for rowIdx := 0; rowIdx < rowChk.NumRows(); rowIdx++ { + e.rowPtrs = append(e.rowPtrs, chunk.RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)}) + } + } +} + +// Open implements the Executor Open interface. +func (e *TopNExec) Open(ctx context.Context) error { + e.memTracker = memory.NewTracker(e.ID(), -1) + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + + e.fetched = false + e.Idx = 0 + + return e.Children(0).Open(ctx) +} + +// Next implements the Executor Next interface. +func (e *TopNExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if !e.fetched { + e.totalLimit = e.limit.Offset + e.limit.Count + e.Idx = int(e.limit.Offset) + err := e.loadChunksUntilTotalLimit(ctx) + if err != nil { + return err + } + err = e.executeTopN(ctx) + if err != nil { + return err + } + e.fetched = true + } + if e.Idx >= len(e.rowPtrs) { + return nil + } + if !req.IsFull() { + numToAppend := mathutil.Min(len(e.rowPtrs)-e.Idx, req.RequiredRows()-req.NumRows()) + rows := make([]chunk.Row, numToAppend) + for index := 0; index < numToAppend; index++ { + rows[index] = e.rowChunks.GetRow(e.rowPtrs[e.Idx]) + e.Idx++ + } + req.AppendRows(rows) + } + return nil +} + +func (e *TopNExec) loadChunksUntilTotalLimit(ctx context.Context) error { + e.chkHeap = &topNChunkHeap{e} + e.rowChunks = chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) + e.rowChunks.GetMemTracker().AttachTo(e.memTracker) + e.rowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) + for uint64(e.rowChunks.Len()) < e.totalLimit { + srcChk := exec.TryNewCacheChunk(e.Children(0)) + // adjust required rows by total limit + srcChk.SetRequiredRows(int(e.totalLimit-uint64(e.rowChunks.Len())), e.MaxChunkSize()) + err := exec.Next(ctx, e.Children(0), srcChk) + if err != nil { + return err + } + if srcChk.NumRows() == 0 { + break + } + e.rowChunks.Add(srcChk) + } + e.initPointers() + e.initCompareFuncs() + e.buildKeyColumns() + return nil +} + +const topNCompactionFactor = 4 + +func (e *TopNExec) executeTopN(ctx context.Context) error { + heap.Init(e.chkHeap) + for uint64(len(e.rowPtrs)) > e.totalLimit { + // The number of rows we loaded may exceeds total limit, remove greatest rows by Pop. + heap.Pop(e.chkHeap) + } + childRowChk := exec.TryNewCacheChunk(e.Children(0)) + for { + err := exec.Next(ctx, e.Children(0), childRowChk) + if err != nil { + return err + } + if childRowChk.NumRows() == 0 { + break + } + err = e.processChildChk(childRowChk) + if err != nil { + return err + } + if e.rowChunks.Len() > len(e.rowPtrs)*topNCompactionFactor { + err = e.doCompaction() + if err != nil { + return err + } + } + } + slices.SortFunc(e.rowPtrs, e.keyColumnsCompare) + return nil +} + +func (e *TopNExec) processChildChk(childRowChk *chunk.Chunk) error { + for i := 0; i < childRowChk.NumRows(); i++ { + heapMaxPtr := e.rowPtrs[0] + var heapMax, next chunk.Row + heapMax = e.rowChunks.GetRow(heapMaxPtr) + next = childRowChk.GetRow(i) + if e.chkHeap.greaterRow(heapMax, next) { + // Evict heap max, keep the next row. + e.rowPtrs[0] = e.rowChunks.AppendRow(childRowChk.GetRow(i)) + heap.Fix(e.chkHeap, 0) + } + } + return nil +} + +// doCompaction rebuild the chunks and row pointers to release memory. +// If we don't do compaction, in a extreme case like the child data is already ascending sorted +// but we want descending top N, then we will keep all data in memory. +// But if data is distributed randomly, this function will be called log(n) times. +func (e *TopNExec) doCompaction() error { + newRowChunks := chunk.NewList(exec.RetTypes(e), e.InitCap(), e.MaxChunkSize()) + newRowPtrs := make([]chunk.RowPtr, 0, e.rowChunks.Len()) + for _, rowPtr := range e.rowPtrs { + newRowPtr := newRowChunks.AppendRow(e.rowChunks.GetRow(rowPtr)) + newRowPtrs = append(newRowPtrs, newRowPtr) + } + newRowChunks.GetMemTracker().SetLabel(memory.LabelForRowChunks) + e.memTracker.ReplaceChild(e.rowChunks.GetMemTracker(), newRowChunks.GetMemTracker()) + e.rowChunks = newRowChunks + + e.memTracker.Consume(int64(-8 * len(e.rowPtrs))) + e.memTracker.Consume(int64(8 * len(newRowPtrs))) + e.rowPtrs = newRowPtrs + return nil +} diff --git a/executor/sort_test.go b/pkg/executor/sort_test.go similarity index 86% rename from executor/sort_test.go rename to pkg/executor/sort_test.go index 4a983fe3d7044..019a52d147338 100644 --- a/executor/sort_test.go +++ b/pkg/executor/sort_test.go @@ -22,10 +22,10 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) @@ -41,9 +41,9 @@ func testSortInDisk(t *testing.T, removeDir bool) { conf.TempStoragePath = t.TempDir() conf.Performance.EnableStatsCacheMemQuota = true }) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill")) }() store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) @@ -104,10 +104,10 @@ func TestIssue16696(t *testing.T) { variable.MemoryUsageAlarmRatio.Store(0.0) defer variable.MemoryUsageAlarmRatio.Store(alarmRatio) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill", "return(true)")) - defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testSortedRowContainerSpill")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testRowContainerSpill", "return(true)")) - defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testRowContainerSpill")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill", "return(true)")) + defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testSortedRowContainerSpill")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testRowContainerSpill", "return(true)")) + defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testRowContainerSpill")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") diff --git a/executor/split.go b/pkg/executor/split.go similarity index 97% rename from executor/split.go rename to pkg/executor/split.go index 005090aa638b6..afa1a93ee37ec 100644 --- a/executor/split.go +++ b/pkg/executor/split.go @@ -23,21 +23,21 @@ import ( "time" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" ) diff --git a/pkg/executor/split_test.go b/pkg/executor/split_test.go new file mode 100644 index 0000000000000..187df0d26b50f --- /dev/null +++ b/pkg/executor/split_test.go @@ -0,0 +1,493 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "encoding/binary" + "math" + "math/rand" + "sort" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestLongestCommonPrefixLen(t *testing.T) { + cases := []struct { + s1 string + s2 string + l int + }{ + {"", "", 0}, + {"", "a", 0}, + {"a", "", 0}, + {"a", "a", 1}, + {"ab", "a", 1}, + {"a", "ab", 1}, + {"b", "ab", 0}, + {"ba", "ab", 0}, + } + + for _, ca := range cases { + re := longestCommonPrefixLen([]byte(ca.s1), []byte(ca.s2)) + require.Equal(t, ca.l, re) + } +} + +func TestGetStepValue(t *testing.T) { + cases := []struct { + lower []byte + upper []byte + l int + v uint64 + }{ + {[]byte{}, []byte{}, 0, math.MaxUint64}, + {[]byte{0}, []byte{128}, 0, binary.BigEndian.Uint64([]byte{128, 255, 255, 255, 255, 255, 255, 255})}, + {[]byte{'a'}, []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255, 255, 255, 255, 255, 255, 255})}, + {[]byte("abc"), []byte{'z'}, 0, binary.BigEndian.Uint64([]byte{'z' - 'a', 255 - 'b', 255 - 'c', 255, 255, 255, 255, 255})}, + {[]byte("abc"), []byte("xyz"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 255, 255, 255, 255, 255})}, + {[]byte("abc"), []byte("axyz"), 1, binary.BigEndian.Uint64([]byte{'x' - 'b', 'y' - 'c', 'z', 255, 255, 255, 255, 255})}, + {[]byte("abc0123456"), []byte("xyz01234"), 0, binary.BigEndian.Uint64([]byte{'x' - 'a', 'y' - 'b', 'z' - 'c', 0, 0, 0, 0, 0})}, + } + + for _, ca := range cases { + l := longestCommonPrefixLen(ca.lower, ca.upper) + require.Equal(t, ca.l, l) + v0 := getStepValue(ca.lower[l:], ca.upper[l:], 1) + require.Equal(t, v0, ca.v) + } +} + +func TestSplitIndex(t *testing.T) { + tbInfo := &model.TableInfo{ + Name: model.NewCIStr("t1"), + ID: rand.Int63(), + Columns: []*model.ColumnInfo{ + { + Name: model.NewCIStr("c0"), + ID: 1, + Offset: 1, + DefaultValue: 0, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLong), + }, + }, + } + idxCols := []*model.IndexColumn{{Name: tbInfo.Columns[0].Name, Offset: 0, Length: types.UnspecifiedLength}} + idxInfo := &model.IndexInfo{ + ID: 2, + Name: model.NewCIStr("idx1"), + Table: model.NewCIStr("t1"), + Columns: idxCols, + State: model.StatePublic, + } + firstIdxInfo0 := idxInfo.Clone() + firstIdxInfo0.ID = 1 + firstIdxInfo0.Name = model.NewCIStr("idx") + tbInfo.Indices = []*model.IndexInfo{firstIdxInfo0, idxInfo} + + // Test for int index. + // range is 0 ~ 100, and split into 10 region. + // So 10 regions range is like below, left close right open interval: + // region1: [-inf ~ 10) + // region2: [10 ~ 20) + // region3: [20 ~ 30) + // region4: [30 ~ 40) + // region5: [40 ~ 50) + // region6: [50 ~ 60) + // region7: [60 ~ 70) + // region8: [70 ~ 80) + // region9: [80 ~ 90) + // region10: [90 ~ +inf) + ctx := mock.NewContext() + e := &SplitIndexRegionExec{ + BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), + tableInfo: tbInfo, + indexInfo: idxInfo, + lower: []types.Datum{types.NewDatum(0)}, + upper: []types.Datum{types.NewDatum(100)}, + num: 10, + } + valueList, err := e.getSplitIdxKeys() + sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) + require.NoError(t, err) + require.Len(t, valueList, e.num+1) + + cases := []struct { + value int + lessEqualIdx int + }{ + {-1, 0}, + {0, 0}, + {1, 0}, + {10, 1}, + {11, 1}, + {20, 2}, + {21, 2}, + {31, 3}, + {41, 4}, + {51, 5}, + {61, 6}, + {71, 7}, + {81, 8}, + {91, 9}, + {100, 9}, + {1000, 9}, + } + + index := tables.NewIndex(tbInfo.ID, tbInfo, idxInfo) + for _, ca := range cases { + // test for minInt64 handle + idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MinInt64), nil) + require.NoError(t, err) + idx := searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + + // Test for max int64 handle. + idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MaxInt64), nil) + require.NoError(t, err) + idx = searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + } + // Test for varchar index. + // range is a ~ z, and split into 26 region. + // So 26 regions range is like below: + // region1: [-inf ~ b) + // region2: [b ~ c) + // . + // . + // . + // region26: [y ~ +inf) + e.lower = []types.Datum{types.NewDatum("a")} + e.upper = []types.Datum{types.NewDatum("z")} + e.num = 26 + // change index column type to varchar + tbInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeVarchar) + + valueList, err = e.getSplitIdxKeys() + sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) + require.NoError(t, err) + require.Len(t, valueList, e.num+1) + + cases2 := []struct { + value string + lessEqualIdx int + }{ + {"", 0}, + {"a", 0}, + {"abcde", 0}, + {"b", 1}, + {"bzzzz", 1}, + {"c", 2}, + {"czzzz", 2}, + {"z", 25}, + {"zabcd", 25}, + } + + for _, ca := range cases2 { + // test for minInt64 handle + idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MinInt64), nil) + require.NoError(t, err) + idx := searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + + // Test for max int64 handle. + idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(ca.value)}, kv.IntHandle(math.MaxInt64), nil) + require.NoError(t, err) + idx = searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + } + + // Test for timestamp index. + // range is 2010-01-01 00:00:00 ~ 2020-01-01 00:00:00, and split into 10 region. + // So 10 regions range is like below: + // region1: [-inf ~ 2011-01-01 00:00:00) + // region2: [2011-01-01 00:00:00 ~ 2012-01-01 00:00:00) + // . + // . + // . + // region10: [2019-01-01 00:00:00 ~ +inf) + lowerTime := types.NewTime(types.FromDate(2010, 1, 1, 0, 0, 0, 0), mysql.TypeTimestamp, types.DefaultFsp) + upperTime := types.NewTime(types.FromDate(2020, 1, 1, 0, 0, 0, 0), mysql.TypeTimestamp, types.DefaultFsp) + e.lower = []types.Datum{types.NewDatum(lowerTime)} + e.upper = []types.Datum{types.NewDatum(upperTime)} + e.num = 10 + + // change index column type to timestamp + tbInfo.Columns[0].FieldType = *types.NewFieldType(mysql.TypeTimestamp) + + valueList, err = e.getSplitIdxKeys() + sort.Slice(valueList, func(i, j int) bool { return bytes.Compare(valueList[i], valueList[j]) < 0 }) + require.NoError(t, err) + require.Len(t, valueList, e.num+1) + + cases3 := []struct { + value types.CoreTime + lessEqualIdx int + }{ + {types.FromDate(2009, 11, 20, 12, 50, 59, 0), 0}, + {types.FromDate(2010, 1, 1, 0, 0, 0, 0), 0}, + {types.FromDate(2011, 12, 31, 23, 59, 59, 0), 1}, + {types.FromDate(2011, 2, 1, 0, 0, 0, 0), 1}, + {types.FromDate(2012, 3, 1, 0, 0, 0, 0), 2}, + {types.FromDate(2013, 4, 1, 0, 0, 0, 0), 3}, + {types.FromDate(2014, 5, 1, 0, 0, 0, 0), 4}, + {types.FromDate(2015, 6, 1, 0, 0, 0, 0), 5}, + {types.FromDate(2016, 8, 1, 0, 0, 0, 0), 6}, + {types.FromDate(2017, 9, 1, 0, 0, 0, 0), 7}, + {types.FromDate(2018, 10, 1, 0, 0, 0, 0), 8}, + {types.FromDate(2019, 11, 1, 0, 0, 0, 0), 9}, + {types.FromDate(2020, 12, 1, 0, 0, 0, 0), 9}, + {types.FromDate(2030, 12, 1, 0, 0, 0, 0), 9}, + } + + for _, ca := range cases3 { + value := types.NewTime(ca.value, mysql.TypeTimestamp, types.DefaultFsp) + // test for min int64 handle + idxValue, _, err := index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(value)}, kv.IntHandle(math.MinInt64), nil) + require.NoError(t, err) + idx := searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + + // Test for max int64 handle. + idxValue, _, err = index.GenIndexKey(ctx.GetSessionVars().StmtCtx, []types.Datum{types.NewDatum(value)}, kv.IntHandle(math.MaxInt64), nil) + require.NoError(t, err) + idx = searchLessEqualIdx(valueList, idxValue) + require.Equal(t, idx, ca.lessEqualIdx) + } +} + +func TestSplitTable(t *testing.T) { + tbInfo := &model.TableInfo{ + Name: model.NewCIStr("t1"), + ID: rand.Int63(), + Columns: []*model.ColumnInfo{ + { + Name: model.NewCIStr("c0"), + ID: 1, + Offset: 1, + DefaultValue: 0, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLong), + }, + }, + } + defer func(originValue int64) { + minRegionStepValue = originValue + }(minRegionStepValue) + minRegionStepValue = 10 + // range is 0 ~ 100, and split into 10 region. + // So 10 regions range is like below: + // region1: [-inf ~ 10) + // region2: [10 ~ 20) + // region3: [20 ~ 30) + // region4: [30 ~ 40) + // region5: [40 ~ 50) + // region6: [50 ~ 60) + // region7: [60 ~ 70) + // region8: [70 ~ 80) + // region9: [80 ~ 90 ) + // region10: [90 ~ +inf) + ctx := mock.NewContext() + e := &SplitTableRegionExec{ + BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), + tableInfo: tbInfo, + handleCols: core.NewIntHandleCols(&expression.Column{RetType: types.NewFieldType(mysql.TypeLonglong)}), + lower: []types.Datum{types.NewDatum(0)}, + upper: []types.Datum{types.NewDatum(100)}, + num: 10, + } + valueList, err := e.getSplitTableKeys() + require.NoError(t, err) + require.Len(t, valueList, e.num-1) + + cases := []struct { + value int + lessEqualIdx int + }{ + {-1, -1}, + {0, -1}, + {1, -1}, + {10, 0}, + {11, 0}, + {20, 1}, + {21, 1}, + {31, 2}, + {41, 3}, + {51, 4}, + {61, 5}, + {71, 6}, + {81, 7}, + {91, 8}, + {100, 8}, + {1000, 8}, + } + + recordPrefix := tablecodec.GenTableRecordPrefix(e.tableInfo.ID) + for _, ca := range cases { + // test for minInt64 handle + key := tablecodec.EncodeRecordKey(recordPrefix, kv.IntHandle(ca.value)) + require.NoError(t, err) + idx := searchLessEqualIdx(valueList, key) + require.Equal(t, idx, ca.lessEqualIdx) + } +} + +func TestStepShouldLargeThanMinStep(t *testing.T) { + ctx := mock.NewContext() + tbInfo := &model.TableInfo{ + Name: model.NewCIStr("t1"), + ID: rand.Int63(), + Columns: []*model.ColumnInfo{ + { + Name: model.NewCIStr("c0"), + ID: 1, + Offset: 1, + DefaultValue: 0, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLong), + }, + }, + } + e1 := &SplitTableRegionExec{ + BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), + tableInfo: tbInfo, + handleCols: core.NewIntHandleCols(&expression.Column{RetType: types.NewFieldType(mysql.TypeLonglong)}), + lower: []types.Datum{types.NewDatum(0)}, + upper: []types.Datum{types.NewDatum(1000)}, + num: 10, + } + _, err := e1.getSplitTableKeys() + require.Equal(t, "[executor:8212]Failed to split region ranges: the region size is too small, expected at least 1000, but got 100", err.Error()) +} + +func TestClusterIndexSplitTable(t *testing.T) { + tbInfo := &model.TableInfo{ + Name: model.NewCIStr("t"), + ID: 1, + IsCommonHandle: true, + CommonHandleVersion: 1, + Indices: []*model.IndexInfo{ + { + ID: 1, + Primary: true, + State: model.StatePublic, + Columns: []*model.IndexColumn{ + {Offset: 1}, + {Offset: 2}, + }, + }, + }, + Columns: []*model.ColumnInfo{ + { + Name: model.NewCIStr("c0"), + ID: 1, + Offset: 0, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeDouble), + }, + { + Name: model.NewCIStr("c1"), + ID: 2, + Offset: 1, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLonglong), + }, + { + Name: model.NewCIStr("c2"), + ID: 3, + Offset: 2, + State: model.StatePublic, + FieldType: *types.NewFieldType(mysql.TypeLonglong), + }, + }, + } + defer func(originValue int64) { + minRegionStepValue = originValue + }(minRegionStepValue) + minRegionStepValue = 3 + ctx := mock.NewContext() + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + e := &SplitTableRegionExec{ + BaseExecutor: exec.NewBaseExecutor(ctx, nil, 0), + tableInfo: tbInfo, + handleCols: buildHandleColsForSplit(sc, tbInfo), + lower: types.MakeDatums(1, 0), + upper: types.MakeDatums(1, 100), + num: 10, + } + valueList, err := e.getSplitTableKeys() + require.NoError(t, err) + require.Len(t, valueList, e.num-1) + + cases := []struct { + value []types.Datum + lessEqualIdx int + }{ + // For lower-bound and upper-bound, because 0 and 100 are padding with 7 zeros, + // the split points are not (i * 10) but approximation. + {types.MakeDatums(1, -1), -1}, + {types.MakeDatums(1, 0), -1}, + {types.MakeDatums(1, 10), -1}, + {types.MakeDatums(1, 11), 0}, + {types.MakeDatums(1, 20), 0}, + {types.MakeDatums(1, 21), 1}, + + {types.MakeDatums(1, 31), 2}, + {types.MakeDatums(1, 41), 3}, + {types.MakeDatums(1, 51), 4}, + {types.MakeDatums(1, 61), 5}, + {types.MakeDatums(1, 71), 6}, + {types.MakeDatums(1, 81), 7}, + {types.MakeDatums(1, 91), 8}, + {types.MakeDatums(1, 100), 8}, + {types.MakeDatums(1, 101), 8}, + } + + recordPrefix := tablecodec.GenTableRecordPrefix(e.tableInfo.ID) + for _, ca := range cases { + h, err := e.handleCols.BuildHandleByDatums(ca.value) + require.NoError(t, err) + key := tablecodec.EncodeRecordKey(recordPrefix, h) + require.NoError(t, err) + idx := searchLessEqualIdx(valueList, key) + require.Equal(t, idx, ca.lessEqualIdx) + } +} + +func searchLessEqualIdx(valueList [][]byte, value []byte) int { + idx := -1 + for i, v := range valueList { + if bytes.Compare(value, v) >= 0 { + idx = i + continue + } + break + } + return idx +} diff --git a/executor/stale_txn_test.go b/pkg/executor/stale_txn_test.go similarity index 93% rename from executor/stale_txn_test.go rename to pkg/executor/stale_txn_test.go index ea35a45982b87..ca743c9bd55a0 100644 --- a/executor/stale_txn_test.go +++ b/pkg/executor/stale_txn_test.go @@ -22,12 +22,12 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) @@ -155,7 +155,7 @@ func TestSelectAsOf(t *testing.T) { if len(testcase.setTxnSQL) > 0 { tk.MustExec(testcase.setTxnSQL) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, testcase.expectPhysicalTS))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, testcase.expectPhysicalTS))) rs, err := tk.Exec(testcase.sql) if len(testcase.errorStr) != 0 { require.Regexp(t, testcase.errorStr, err.Error()) @@ -165,7 +165,7 @@ func TestSelectAsOf(t *testing.T) { if rs != nil { rs.Close() } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertStaleTSO")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO")) if len(testcase.setTxnSQL) > 0 { require.Equal(t, uint64(0), tk.Session().GetSessionVars().TxnReadTS.PeakTxnReadTS()) } @@ -229,8 +229,8 @@ func TestSelectAsOf(t *testing.T) { for _, testcase := range testcases2 { t.Log(testcase.name) if testcase.preSec > 0 { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-testcase.preSec))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-testcase.preSec))) } rs, err := tk.Exec(testcase.sql) if len(testcase.errorStr) != 0 { @@ -242,8 +242,8 @@ func TestSelectAsOf(t *testing.T) { rs.Close() } if testcase.preSec > 0 { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectNow")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertStaleTSO")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectNow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO")) } } } @@ -281,17 +281,17 @@ func TestStaleReadKVRequest(t *testing.T) { { name: "coprocessor read", sql: "select * from t", - assert: "github.com/pingcap/tidb/distsql/assertRequestBuilderReplicaOption", + assert: "github.com/pingcap/tidb/pkg/distsql/assertRequestBuilderReplicaOption", }, { name: "point get read", sql: "select * from t where id = 1", - assert: "github.com/pingcap/tidb/executor/assertPointReplicaOption", + assert: "github.com/pingcap/tidb/pkg/executor/assertPointReplicaOption", }, { name: "batch point get read", sql: "select * from t where id in (1,2,3)", - assert: "github.com/pingcap/tidb/executor/assertBatchPointReplicaOption", + assert: "github.com/pingcap/tidb/pkg/executor/assertBatchPointReplicaOption", }, } tk.MustExec("set @@tidb_replica_read='closest-replicas'") @@ -474,7 +474,7 @@ func TestTimeBoundedStalenessTxn(t *testing.T) { t.Log(testcase.name) require.NoError(t, failpoint.Enable("tikvclient/injectSafeTS", fmt.Sprintf("return(%v)", testcase.injectSafeTS))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", fmt.Sprintf("return(%v)", testcase.injectSafeTS))) tk.MustExec(testcase.sql) if testcase.compareWithSafeTS == 1 { @@ -486,7 +486,7 @@ func TestTimeBoundedStalenessTxn(t *testing.T) { } tk.MustExec("commit") } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectSafeTS")) require.NoError(t, failpoint.Disable("tikvclient/injectSafeTS")) } @@ -550,7 +550,7 @@ func TestSetTransactionReadOnlyAsOf(t *testing.T) { } for _, testcase := range testcases { if testcase.injectSafeTS > 0 { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", fmt.Sprintf("return(%v)", testcase.injectSafeTS))) } tk.MustExec(testcase.sql) @@ -563,7 +563,7 @@ func TestSetTransactionReadOnlyAsOf(t *testing.T) { require.NotEqual(t, tk.Session().GetSessionVars().TxnCtx.StartTS, testcase.expectedTS) tk.MustExec("commit") - failpoint.Disable("github.com/pingcap/tidb/expression/injectSafeTS") + failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectSafeTS") } err = tk.ExecToErr(`SET TRANSACTION READ ONLY as of timestamp tidb_bounded_staleness(invalid1, invalid2')`) @@ -1013,7 +1013,7 @@ func TestStaleReadPrepare(t *testing.T) { time1 := time.Now() tso := oracle.ComposeTS(time1.Unix()*1000, 0) time.Sleep(200 * time.Millisecond) - failpoint.Enable("github.com/pingcap/tidb/executor/assertExecutePrepareStatementStalenessOption", + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertExecutePrepareStatementStalenessOption", fmt.Sprintf(`return("%v_%v")`, tso, "sh")) tk.MustExec(fmt.Sprintf(`prepare p1 from "select * from t as of timestamp '%v'"`, time1.Format("2006-1-2 15:04:05"))) tk.MustExec("execute p1") @@ -1026,7 +1026,7 @@ func TestStaleReadPrepare(t *testing.T) { // assert execute prepared statement in stale read txn tk.MustExec(fmt.Sprintf("set transaction read only as of timestamp '%v'", time1.Format("2006-1-2 15:04:05"))) tk.MustExec("execute p2") - failpoint.Disable("github.com/pingcap/tidb/executor/assertExecutePrepareStatementStalenessOption") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertExecutePrepareStatementStalenessOption") // test prepared stale select in stale txn tk.MustExec(fmt.Sprintf(`start transaction read only as of timestamp '%s'`, time1.Format("2006-1-2 15:04:05.000"))) @@ -1165,22 +1165,22 @@ func TestStaleSessionQuery(t *testing.T) { now := time.Now() tk.MustExec(`set @@tidb_read_staleness="-1"`) // query will use stale read - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-1))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-1))) require.Len(t, tk.MustQuery("select * from t10;").Rows(), 1) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertStaleTSO")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectNow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectNow")) // begin transaction won't be affected by read staleness tk.MustExec("begin") tk.MustExec("insert into t10(id) values (2);") tk.MustExec("commit") tk.MustExec("insert into t10(id) values (3);") // query will still use staleness read - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-1))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO", fmt.Sprintf(`return(%d)`, now.Unix()-1))) require.Len(t, tk.MustQuery("select * from t10").Rows(), 1) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertStaleTSO")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectNow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertStaleTSO")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectNow")) // assert stale read is not exist after empty the variable tk.MustExec(`set @@tidb_read_staleness=""`) require.Len(t, tk.MustQuery("select * from t10").Rows(), 3) @@ -1209,7 +1209,7 @@ func TestStaleReadCompatibility(t *testing.T) { require.Len(t, tk.MustQuery("select * from t;").Rows(), 3) // enable tidb_read_staleness tk.MustExec("set @@tidb_read_staleness='-1'") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectNow", fmt.Sprintf(`return(%d)`, t1.Unix()))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectNow", fmt.Sprintf(`return(%d)`, t1.Unix()))) require.Len(t, tk.MustQuery("select * from t;").Rows(), 1) // assert select as of timestamp during tidb_read_staleness require.Len(t, tk.MustQuery(fmt.Sprintf("select * from t as of timestamp '%s'", t2.Format("2006-1-2 15:04:05"))).Rows(), 2) @@ -1228,7 +1228,7 @@ func TestStaleReadCompatibility(t *testing.T) { // disable tidb_read_staleness tk.MustExec("set @@tidb_read_staleness=''") require.Len(t, tk.MustQuery("select * from t;").Rows(), 3) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectNow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectNow")) } func TestStaleReadNoExtraTSORequest(t *testing.T) { @@ -1247,36 +1247,36 @@ func TestStaleReadNoExtraTSORequest(t *testing.T) { tk.MustExec("create table t (id int);") time.Sleep(3 * time.Second) // statement stale read - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTSONotRequest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest", `return(true)`)) tk.MustQuery("select * from t as of timestamp NOW() - INTERVAL 2 SECOND") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTSONotRequest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest")) // set and statement stale read tk.MustExec("set transaction read only as of timestamp NOW() - INTERVAL 2 SECOND") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTSONotRequest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest", `return(true)`)) tk.MustQuery("select * from t") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTSONotRequest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest")) // stale read transaction - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTSONotRequest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest", `return(true)`)) tk.MustExec("start transaction read only as of timestamp NOW() - INTERVAL 2 SECOND") tk.MustQuery("select * from t") tk.MustExec("commit") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTSONotRequest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest")) // set and stale read transaction tk.MustExec("set transaction read only as of timestamp NOW() - INTERVAL 2 SECOND") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTSONotRequest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest", `return(true)`)) tk.MustExec("begin") tk.MustQuery("select * from t") tk.MustExec("commit") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTSONotRequest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest")) // use tidb_read_staleness tk.MustExec(`set @@tidb_read_staleness='-1'`) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTSONotRequest", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest", `return(true)`)) tk.MustQuery("select * from t") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTSONotRequest")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTSONotRequest")) } func TestPlanCacheWithStaleReadByBinaryProto(t *testing.T) { @@ -1417,8 +1417,8 @@ func TestStaleTSO(t *testing.T) { } nextTSO := oracle.GoTimeToTS(time.Now().Add(2 * time.Second)) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/staleread/mockStaleReadTSO", fmt.Sprintf("return(%d)", nextTSO))) - defer failpoint.Disable("github.com/pingcap/tidb/sessiontxn/staleread/mockStaleReadTSO") + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/staleread/mockStaleReadTSO", fmt.Sprintf("return(%d)", nextTSO))) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/staleread/mockStaleReadTSO") for _, expr := range asOfExprs { // Make sure the now() expr is evaluated from the stale ts provider. tk.MustQuery("select * from t as of timestamp " + expr + " order by id asc").Check(testkit.Rows("1")) diff --git a/executor/statement_context_test.go b/pkg/executor/statement_context_test.go similarity index 97% rename from executor/statement_context_test.go rename to pkg/executor/statement_context_test.go index 1ee4dbd8afed1..39400a3cff9d0 100644 --- a/executor/statement_context_test.go +++ b/pkg/executor/statement_context_test.go @@ -19,9 +19,9 @@ import ( "testing" "unicode/utf8" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/stmtsummary.go b/pkg/executor/stmtsummary.go new file mode 100644 index 0000000000000..46414f6ee4841 --- /dev/null +++ b/pkg/executor/stmtsummary.go @@ -0,0 +1,407 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" +) + +const ( + defaultRetrieveCount = 1024 +) + +func buildStmtSummaryRetriever( + table *model.TableInfo, + columns []*model.ColumnInfo, + extractor *plannercore.StatementsSummaryExtractor, +) memTableRetriever { + if extractor == nil { + extractor = &plannercore.StatementsSummaryExtractor{} + } + if extractor.Digests.Empty() { + extractor.Digests = nil + } + + var retriever memTableRetriever + if extractor.SkipRequest { + retriever = &dummyRetriever{} + } else if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + retriever = &stmtSummaryRetrieverV2{ + stmtSummary: stmtsummaryv2.GlobalStmtSummary, + table: table, + columns: columns, + digests: extractor.Digests, + timeRanges: buildTimeRanges(extractor.CoarseTimeRange), + } + } else { + retriever = &stmtSummaryRetriever{ + table: table, + columns: columns, + digests: extractor.Digests, + } + } + + return retriever +} + +type dummyRetriever struct { + dummyCloser +} + +func (*dummyRetriever) retrieve(_ context.Context, _ sessionctx.Context) ([][]types.Datum, error) { + return nil, nil +} + +// stmtSummaryRetriever is used to retrieve statements summary. +type stmtSummaryRetriever struct { + table *model.TableInfo + columns []*model.ColumnInfo + digests set.StringSet + + // lazily initialized + rowsReader *rowsReader +} + +func (e *stmtSummaryRetriever) retrieve(_ context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { + if err := e.ensureRowsReader(sctx); err != nil { + return nil, err + } + return e.rowsReader.read(defaultRetrieveCount) +} + +func (e *stmtSummaryRetriever) close() error { + if e.rowsReader != nil { + return e.rowsReader.close() + } + return nil +} + +func (*stmtSummaryRetriever) getRuntimeStats() execdetails.RuntimeStats { + return nil +} + +func (e *stmtSummaryRetriever) ensureRowsReader(sctx sessionctx.Context) error { + if e.rowsReader != nil { + return nil + } + + var err error + if isEvictedTable(e.table.Name.O) { + e.rowsReader, err = e.initEvictedRowsReader(sctx) + } else { + e.rowsReader, err = e.initSummaryRowsReader(sctx) + } + + return err +} + +func (e *stmtSummaryRetriever) initEvictedRowsReader(sctx sessionctx.Context) (*rowsReader, error) { + if err := checkPrivilege(sctx); err != nil { + return nil, err + } + + rows := stmtsummary.StmtSummaryByDigestMap.ToEvictedCountDatum() + if !isClusterTable(e.table.Name.O) { + // rows are full-columned, so we need to adjust them to the required columns. + return newSimpleRowsReader(adjustColumns(rows, e.columns, e.table)), nil + } + + // Additional column `INSTANCE` for cluster table + rows, err := infoschema.AppendHostInfoToRows(sctx, rows) + if err != nil { + return nil, err + } + // rows are full-columned, so we need to adjust them to the required columns. + return newSimpleRowsReader(adjustColumns(rows, e.columns, e.table)), nil +} + +func (e *stmtSummaryRetriever) initSummaryRowsReader(sctx sessionctx.Context) (*rowsReader, error) { + vars := sctx.GetSessionVars() + user := vars.User + tz := vars.StmtCtx.TimeZone() + columns := e.columns + priv := hasPriv(sctx, mysql.ProcessPriv) + instanceAddr, err := clusterTableInstanceAddr(sctx, e.table.Name.O) + if err != nil { + return nil, err + } + + reader := stmtsummary.NewStmtSummaryReader(user, priv, columns, instanceAddr, tz) + if e.digests != nil { + // set checker to filter out statements not matching the given digests + checker := stmtsummary.NewStmtSummaryChecker(e.digests) + reader.SetChecker(checker) + } + + var rows [][]types.Datum + if isCurrentTable(e.table.Name.O) { + rows = reader.GetStmtSummaryCurrentRows() + } + if isHistoryTable(e.table.Name.O) { + rows = reader.GetStmtSummaryHistoryRows() + } + return newSimpleRowsReader(rows), nil +} + +// stmtSummaryRetriever is used to retrieve statements summary when +// config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent is true +type stmtSummaryRetrieverV2 struct { + stmtSummary *stmtsummaryv2.StmtSummary + table *model.TableInfo + columns []*model.ColumnInfo + digests set.StringSet + timeRanges []*stmtsummaryv2.StmtTimeRange + + // lazily initialized + rowsReader *rowsReader +} + +func (r *stmtSummaryRetrieverV2) retrieve(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { + if err := r.ensureRowsReader(ctx, sctx); err != nil { + return nil, err + } + return r.rowsReader.read(defaultRetrieveCount) +} + +func (r *stmtSummaryRetrieverV2) close() error { + if r.rowsReader != nil { + return r.rowsReader.close() + } + return nil +} + +func (*stmtSummaryRetrieverV2) getRuntimeStats() execdetails.RuntimeStats { + return nil +} + +func (r *stmtSummaryRetrieverV2) ensureRowsReader(ctx context.Context, sctx sessionctx.Context) error { + if r.rowsReader != nil { + return nil + } + + var err error + if isEvictedTable(r.table.Name.O) { + r.rowsReader, err = r.initEvictedRowsReader(sctx) + } else { + r.rowsReader, err = r.initSummaryRowsReader(ctx, sctx) + } + + return err +} + +func (r *stmtSummaryRetrieverV2) initEvictedRowsReader(sctx sessionctx.Context) (*rowsReader, error) { + if err := checkPrivilege(sctx); err != nil { + return nil, err + } + + var rows [][]types.Datum + + row := r.stmtSummary.Evicted() + if row != nil { + rows = append(rows, row) + } + if !isClusterTable(r.table.Name.O) { + // rows are full-columned, so we need to adjust them to the required columns. + return newSimpleRowsReader(adjustColumns(rows, r.columns, r.table)), nil + } + + // Additional column `INSTANCE` for cluster table + rows, err := infoschema.AppendHostInfoToRows(sctx, rows) + if err != nil { + return nil, err + } + // rows are full-columned, so we need to adjust them to the required columns. + return newSimpleRowsReader(adjustColumns(rows, r.columns, r.table)), nil +} + +func (r *stmtSummaryRetrieverV2) initSummaryRowsReader(ctx context.Context, sctx sessionctx.Context) (*rowsReader, error) { + vars := sctx.GetSessionVars() + user := vars.User + tz := vars.StmtCtx.TimeZone() + stmtSummary := r.stmtSummary + columns := r.columns + timeRanges := r.timeRanges + digests := r.digests + priv := hasPriv(sctx, mysql.ProcessPriv) + instanceAddr, err := clusterTableInstanceAddr(sctx, r.table.Name.O) + if err != nil { + return nil, err + } + + mem := stmtsummaryv2.NewMemReader(stmtSummary, columns, instanceAddr, tz, user, priv, digests, timeRanges) + memRows := mem.Rows() + + var rowsReader *rowsReader + if isCurrentTable(r.table.Name.O) { + rowsReader = newSimpleRowsReader(memRows) + } + if isHistoryTable(r.table.Name.O) { + // history table should return all rows including mem and disk + concurrent := sctx.GetSessionVars().Concurrency.DistSQLScanConcurrency() + history, err := stmtsummaryv2.NewHistoryReader(ctx, columns, instanceAddr, tz, user, priv, digests, timeRanges, concurrent) + if err != nil { + return nil, err + } + rowsReader = newRowsReader(memRows, history) + } + + return rowsReader, nil +} + +type rowsPuller interface { + Closeable + Rows() ([][]types.Datum, error) +} + +type rowsReader struct { + puller rowsPuller + rows [][]types.Datum +} + +func newSimpleRowsReader(rows [][]types.Datum) *rowsReader { + return &rowsReader{rows: rows} +} + +func newRowsReader(rows [][]types.Datum, puller rowsPuller) *rowsReader { + return &rowsReader{puller: puller, rows: rows} +} + +func (r *rowsReader) read(maxCount int) ([][]types.Datum, error) { + if err := r.pull(); err != nil { + return nil, err + } + + if maxCount >= len(r.rows) { + ret := r.rows + r.rows = nil + return ret, nil + } + ret := r.rows[:maxCount] + r.rows = r.rows[maxCount:] + return ret, nil +} + +func (r *rowsReader) pull() error { + if r.puller == nil { + return nil + } + // there are remaining rows + if len(r.rows) > 0 { + return nil + } + + rows, err := r.puller.Rows() + if err != nil { + return err + } + // pulled new rows from the puller + if len(rows) != 0 { + r.rows = rows + return nil + } + + // reach the end of the puller + err = r.puller.Close() + if err != nil { + return err + } + r.puller = nil + return nil +} + +func (r *rowsReader) close() error { + if r.puller != nil { + return r.puller.Close() + } + return nil +} + +func isClusterTable(originalTableName string) bool { + switch originalTableName { + case infoschema.ClusterTableStatementsSummary, + infoschema.ClusterTableStatementsSummaryHistory, + infoschema.ClusterTableStatementsSummaryEvicted: + return true + } + + return false +} + +func isCurrentTable(originalTableName string) bool { + switch originalTableName { + case infoschema.TableStatementsSummary, + infoschema.ClusterTableStatementsSummary: + return true + } + + return false +} + +func isHistoryTable(originalTableName string) bool { + switch originalTableName { + case infoschema.TableStatementsSummaryHistory, + infoschema.ClusterTableStatementsSummaryHistory: + return true + } + + return false +} + +func isEvictedTable(originalTableName string) bool { + switch originalTableName { + case infoschema.TableStatementsSummaryEvicted, + infoschema.ClusterTableStatementsSummaryEvicted: + return true + } + + return false +} + +func checkPrivilege(sctx sessionctx.Context) error { + if !hasPriv(sctx, mysql.ProcessPriv) { + return plannercore.ErrSpecificAccessDenied.GenWithStackByArgs("PROCESS") + } + return nil +} + +func clusterTableInstanceAddr(sctx sessionctx.Context, originalTableName string) (string, error) { + if isClusterTable(originalTableName) { + return infoschema.GetInstanceAddr(sctx) + } + return "", nil +} + +func buildTimeRanges(tr *plannercore.TimeRange) []*stmtsummaryv2.StmtTimeRange { + if tr == nil { + return nil + } + + return []*stmtsummaryv2.StmtTimeRange{{ + Begin: tr.StartTime.Unix(), + End: tr.EndTime.Unix(), + }} +} diff --git a/executor/stmtsummary_test.go b/pkg/executor/stmtsummary_test.go similarity index 95% rename from executor/stmtsummary_test.go rename to pkg/executor/stmtsummary_test.go index a09966c5f3d59..ea11b1c768417 100644 --- a/executor/stmtsummary_test.go +++ b/pkg/executor/stmtsummary_test.go @@ -20,12 +20,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mock" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mock" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" "github.com/stretchr/testify/require" ) diff --git a/executor/table_reader.go b/pkg/executor/table_reader.go similarity index 94% rename from executor/table_reader.go rename to pkg/executor/table_reader.go index 519832f9efff6..8998226f7dda1 100644 --- a/executor/table_reader.go +++ b/pkg/executor/table_reader.go @@ -22,28 +22,28 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/exec" - internalutil "github.com/pingcap/tidb/executor/internal/util" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + internalutil "github.com/pingcap/tidb/pkg/executor/internal/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/pingcap/tipb/go-tipb" ) diff --git a/executor/table_readers_required_rows_test.go b/pkg/executor/table_readers_required_rows_test.go similarity index 92% rename from executor/table_readers_required_rows_test.go rename to pkg/executor/table_readers_required_rows_test.go index 01218db21ede0..fe313668c2ecf 100644 --- a/executor/table_readers_required_rows_test.go +++ b/pkg/executor/table_readers_required_rows_test.go @@ -20,19 +20,19 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/executor/internal/builder" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/executor/internal/builder" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/temporary_table_test.go b/pkg/executor/temporary_table_test.go new file mode 100644 index 0000000000000..e0f7f3d9c2cab --- /dev/null +++ b/pkg/executor/temporary_table_test.go @@ -0,0 +1,158 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestNormalGlobalTemporaryTableNoNetwork(t *testing.T) { + assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { + tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") + tk.MustExec("begin") + }) +} + +func TestGlobalTemporaryTableNoNetworkWithCreateAndTruncate(t *testing.T) { + assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { + tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") + tk.MustExec("truncate table tmp_t") + tk.MustExec("begin") + }) +} + +func TestGlobalTemporaryTableNoNetworkWithCreateAndThenCreateNormalTable(t *testing.T) { + assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { + tk.MustExec("create global temporary table tmp_t (id int primary key, a int, b int, index(a)) on commit delete rows") + tk.MustExec("create table txx(a int)") + tk.MustExec("begin") + }) +} + +func TestLocalTemporaryTableNoNetworkWithCreateOutsideTxn(t *testing.T) { + assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { + tk.MustExec("create temporary table tmp_t (id int primary key, a int, b int, index(a))") + tk.MustExec("begin") + }) +} + +func TestLocalTemporaryTableNoNetworkWithInsideTxn(t *testing.T) { + assertTemporaryTableNoNetwork(t, func(tk *testkit.TestKit) { + tk.MustExec("begin") + tk.MustExec("create temporary table tmp_t (id int primary key, a int, b int, index(a))") + }) +} + +func assertTemporaryTableNoNetwork(t *testing.T, createTable func(*testkit.TestKit)) { + var done sync.WaitGroup + defer done.Wait() + + store := testkit.CreateMockStore(t) + + // Test that table reader/index reader/index lookup on the temporary table do not need to visit TiKV. + tk := testkit.NewTestKit(t, store) + tk1 := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk1.MustExec("use test") + tk.MustExec("drop table if exists normal, tmp_t") + tk.MustExec("create table normal (id int, a int, index(a))") + createTable(tk) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy")) + }() + + tk.MustExec("insert into tmp_t values (1, 1, 1)") + tk.MustExec("insert into tmp_t values (2, 2, 2)") + + // Make sure the fail point works. + // With that failpoint, all requests to the TiKV is discard. + rs, err := tk1.Exec("select * from normal") + require.NoError(t, err) + + blocked := make(chan struct{}, 1) + ctx, cancelFunc := context.WithCancel(context.Background()) + done.Add(1) + go func() { + defer done.Done() + _, _ = session.ResultSetToStringSlice(ctx, tk1.Session(), rs) + blocked <- struct{}{} + }() + + select { + case <-blocked: + cancelFunc() + require.FailNow(t, "The query should block when the failpoint is enabled.") + case <-time.After(200 * time.Millisecond): + cancelFunc() + } + + // Check the temporary table do not send request to TiKV. + // PointGet + tk.MustHavePlan("select * from tmp_t where id=1", "Point_Get") + tk.MustQuery("select * from tmp_t where id=1").Check(testkit.Rows("1 1 1")) + + // BatchPointGet + tk.MustHavePlan("select * from tmp_t where id in (1, 2)", "Batch_Point_Get") + tk.MustQuery("select * from tmp_t where id in (1, 2)").Check(testkit.Rows("1 1 1", "2 2 2")) + + // Table reader + tk.MustHavePlan("select * from tmp_t", "TableReader") + tk.MustQuery("select * from tmp_t").Check(testkit.Rows("1 1 1", "2 2 2")) + + // Index reader + tk.MustHavePlan("select /*+ USE_INDEX(tmp_t, a) */ a from tmp_t", "IndexReader") + tk.MustQuery("select /*+ USE_INDEX(tmp_t, a) */ a from tmp_t").Check(testkit.Rows("1", "2")) + + // Index lookup + tk.MustHavePlan("select /*+ USE_INDEX(tmp_t, a) */ b from tmp_t where a = 1", "IndexLookUp") + tk.MustQuery("select /*+ USE_INDEX(tmp_t, a) */ b from tmp_t where a = 1").Check(testkit.Rows("1")) + tk.MustExec("rollback") + + // prepare some data for local temporary table, when for global temporary table, the below operations have no effect. + tk.MustExec("insert into tmp_t value(10, 10, 10)") + tk.MustExec("insert into tmp_t value(11, 11, 11)") + + // Pessimistic lock + tk.MustExec("begin pessimistic") + tk.MustExec("insert into tmp_t values (3, 3, 3)") + tk.MustExec("insert ignore into tmp_t values (4, 4, 4)") + tk.MustExec("insert into tmp_t values (5, 5, 5) on duplicate key update a=100") + tk.MustExec("insert into tmp_t values (10, 10, 10) on duplicate key update a=100") + tk.MustExec("insert ignore into tmp_t values (10, 10, 10) on duplicate key update id=11") + tk.MustExec("replace into tmp_t values(6, 6, 6)") + tk.MustExec("replace into tmp_t values(11, 100, 100)") + tk.MustExec("update tmp_t set id = id + 1 where a = 1") + tk.MustExec("delete from tmp_t where a > 1") + tk.MustQuery("select count(*) from tmp_t where a >= 1 for update") + tk.MustExec("rollback") + + // Check 'for update' will not write any lock too when table is unmodified + tk.MustExec("begin pessimistic") + tk.MustExec("select * from tmp_t where id=1 for update") + tk.MustExec("select * from tmp_t where id in (1, 2, 3) for update") + tk.MustExec("select * from tmp_t where id > 1 for update") + tk.MustExec("rollback") +} diff --git a/pkg/executor/test/admintest/BUILD.bazel b/pkg/executor/test/admintest/BUILD.bazel new file mode 100644 index 0000000000000..7d217e0fa3d1d --- /dev/null +++ b/pkg/executor/test/admintest/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "admintest_test", + timeout = "short", + srcs = [ + "admin_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 21, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/executor", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/session", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/logutil", + "//pkg/util/logutil/consistency", + "//pkg/util/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/executor/test/admintest/admin_test.go b/pkg/executor/test/admintest/admin_test.go similarity index 98% rename from executor/test/admintest/admin_test.go rename to pkg/executor/test/admintest/admin_test.go index 1aff97da3f8f1..3b4b599c233a0 100644 --- a/executor/test/admintest/admin_test.go +++ b/pkg/executor/test/admintest/admin_test.go @@ -24,23 +24,23 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/domain" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/pkg/executor/test/admintest/main_test.go b/pkg/executor/test/admintest/main_test.go new file mode 100644 index 0000000000000..de4c1237291a4 --- /dev/null +++ b/pkg/executor/test/admintest/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package admintest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + conf.Performance.EnableStatsCacheMemQuota = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/aggregate/BUILD.bazel b/pkg/executor/test/aggregate/BUILD.bazel new file mode 100644 index 0000000000000..06b9638828051 --- /dev/null +++ b/pkg/executor/test/aggregate/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "aggregate_test", + timeout = "short", + srcs = [ + "aggregate_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 9, + deps = [ + "//pkg/config", + "//pkg/executor/aggregate", + "//pkg/executor/internal", + "//pkg/session", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testsetup", + "//pkg/util/sqlexec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/aggregate/aggregate_test.go b/pkg/executor/test/aggregate/aggregate_test.go similarity index 98% rename from executor/test/aggregate/aggregate_test.go rename to pkg/executor/test/aggregate/aggregate_test.go index 253f8bb6cb3f9..4e793a2d283bd 100644 --- a/executor/test/aggregate/aggregate_test.go +++ b/pkg/executor/test/aggregate/aggregate_test.go @@ -26,12 +26,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor/aggregate" - "github.com/pingcap/tidb/executor/internal" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/executor/aggregate" + "github.com/pingcap/tidb/pkg/executor/internal" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) @@ -479,12 +479,12 @@ func TestRandomPanicConsume(t *testing.T) { tk.MustExec(fmt.Sprintf("insert into s(a,b) values(%v,%v),(%v,%v),(%v,%v)", i, i, i, i, i, i)) } - fpName := "github.com/pingcap/tidb/executor/aggregate/ConsumeRandomPanic" + fpName := "github.com/pingcap/tidb/pkg/executor/aggregate/ConsumeRandomPanic" require.NoError(t, failpoint.Enable(fpName, "5%panic(\"ERROR 1105 (HY000): Out Of Memory Quota![conn=1]\")")) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - fpName2 := "github.com/pingcap/tidb/store/copr/ConsumeRandomPanic" + fpName2 := "github.com/pingcap/tidb/pkg/store/copr/ConsumeRandomPanic" require.NoError(t, failpoint.Enable(fpName2, "3%panic(\"ERROR 1105 (HY000): Out Of Memory Quota![conn=1]\")")) defer func() { require.NoError(t, failpoint.Disable(fpName2)) diff --git a/pkg/executor/test/aggregate/main_test.go b/pkg/executor/test/aggregate/main_test.go new file mode 100644 index 0000000000000..2b4aee21617d4 --- /dev/null +++ b/pkg/executor/test/aggregate/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package aggregate + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var aggMergeSuiteData testdata.TestData +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + testDataMap.LoadTestSuiteData("testdata", "agg_suite") + aggMergeSuiteData = testDataMap["agg_suite"] + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/aggregate/testdata/agg_suite_in.json b/pkg/executor/test/aggregate/testdata/agg_suite_in.json similarity index 100% rename from executor/test/aggregate/testdata/agg_suite_in.json rename to pkg/executor/test/aggregate/testdata/agg_suite_in.json diff --git a/executor/test/aggregate/testdata/agg_suite_out.json b/pkg/executor/test/aggregate/testdata/agg_suite_out.json similarity index 100% rename from executor/test/aggregate/testdata/agg_suite_out.json rename to pkg/executor/test/aggregate/testdata/agg_suite_out.json diff --git a/pkg/executor/test/analyzetest/BUILD.bazel b/pkg/executor/test/analyzetest/BUILD.bazel new file mode 100644 index 0000000000000..f3504f59f6e80 --- /dev/null +++ b/pkg/executor/test/analyzetest/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "analyzetest_test", + timeout = "short", + srcs = [ + "analyze_bench_test.go", + "analyze_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/autoanalyze", + "//pkg/testkit", + "//pkg/util/dbterror/exeerrors", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/analyzetest/analyze_bench_test.go b/pkg/executor/test/analyzetest/analyze_bench_test.go similarity index 97% rename from executor/test/analyzetest/analyze_bench_test.go rename to pkg/executor/test/analyzetest/analyze_bench_test.go index a8159cb621e9c..67200d28c2349 100644 --- a/executor/test/analyzetest/analyze_bench_test.go +++ b/pkg/executor/test/analyzetest/analyze_bench_test.go @@ -18,7 +18,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" ) const ( diff --git a/pkg/executor/test/analyzetest/analyze_test.go b/pkg/executor/test/analyzetest/analyze_test.go new file mode 100644 index 0000000000000..59de964d96ace --- /dev/null +++ b/pkg/executor/test/analyzetest/analyze_test.go @@ -0,0 +1,3216 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package analyzetest + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/stretchr/testify/require" +) + +func TestAnalyzePartition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + testkit.WithPruneMode(tk, variable.Static, func() { + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version=2") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21) +)` + tk.MustExec(createTable) + for i := 1; i < 21; i++ { + tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d, "hello")`, i, i)) + } + tk.MustExec("analyze table t") + + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + pi := table.Meta().GetPartitionInfo() + require.NotNil(t, pi) + do, err := session.GetDomain(store) + require.NoError(t, err) + handle := do.StatsHandle() + for _, def := range pi.Definitions { + statsTbl := handle.GetPartitionStats(table.Meta(), def.ID) + require.False(t, statsTbl.Pseudo) + require.Len(t, statsTbl.Columns, 3) + require.Len(t, statsTbl.Indices, 1) + for _, col := range statsTbl.Columns { + require.Greater(t, col.Len()+col.TopN.Num(), 0) + } + for _, idx := range statsTbl.Indices { + require.Greater(t, idx.Len()+idx.TopN.Num(), 0) + } + } + + tk.MustExec("drop table t") + tk.MustExec(createTable) + for i := 1; i < 21; i++ { + tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d, "hello")`, i, i)) + } + tk.MustExec("alter table t analyze partition p0") + is = tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + pi = table.Meta().GetPartitionInfo() + require.NotNil(t, pi) + + for i, def := range pi.Definitions { + statsTbl := handle.GetPartitionStats(table.Meta(), def.ID) + if i == 0 { + require.False(t, statsTbl.Pseudo) + require.Len(t, statsTbl.Columns, 3) + require.Len(t, statsTbl.Indices, 1) + } else { + require.True(t, statsTbl.Pseudo) + } + } + }) +} + +func TestAnalyzeReplicaReadFollower(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + ctx := tk.Session().(sessionctx.Context) + ctx.GetSessionVars().SetReplicaRead(kv.ReplicaReadFollower) + tk.MustExec("analyze table t") +} + +func TestClusterIndexAnalyze(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("drop database if exists test_cluster_index_analyze;") + tk.MustExec("create database test_cluster_index_analyze;") + tk.MustExec("use test_cluster_index_analyze;") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + + tk.MustExec("create table t (a int, b int, c int, primary key(a, b));") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?, ?, ?)", i, i, i) + } + tk.MustExec("analyze table t;") + tk.MustExec("drop table t;") + + tk.MustExec("create table t (a varchar(255), b int, c float, primary key(c, a));") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?, ?, ?)", strconv.Itoa(i), i, i) + } + tk.MustExec("analyze table t;") + tk.MustExec("drop table t;") + + tk.MustExec("create table t (a char(10), b decimal(5, 3), c int, primary key(a, c, b));") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?, ?, ?)", strconv.Itoa(i), i, i) + } + tk.MustExec("analyze table t;") + tk.MustExec("drop table t;") +} + +func TestAnalyzeRestrict(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + rs, err := tk.Session().ExecuteInternal(ctx, "analyze table t") + require.Nil(t, err) + require.Nil(t, rs) +} + +func TestAnalyzeParameters(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + for i := 0; i < 20; i++ { + tk.MustExec(fmt.Sprintf("insert into t values (%d)", i)) + } + tk.MustExec("insert into t values (19), (19), (19)") + + tk.MustExec("set @@tidb_analyze_version = 1") + tk.MustExec("analyze table t with 30 samples") + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + tbl := dom.StatsHandle().GetTableStats(tableInfo) + col := tbl.Columns[1] + require.Equal(t, 20, col.Len()) + require.Len(t, col.TopN.TopN, 1) + width, depth := col.CMSketch.GetWidthAndDepth() + require.Equal(t, int32(5), depth) + require.Equal(t, int32(2048), width) + + tk.MustExec("analyze table t with 4 buckets, 0 topn, 4 cmsketch width, 4 cmsketch depth") + tbl = dom.StatsHandle().GetTableStats(tableInfo) + col = tbl.Columns[1] + require.Equal(t, 4, col.Len()) + require.Nil(t, col.TopN) + width, depth = col.CMSketch.GetWidthAndDepth() + require.Equal(t, int32(4), depth) + require.Equal(t, int32(4), width) + + // Test very large cmsketch + tk.MustExec(fmt.Sprintf("analyze table t with %d cmsketch width, %d cmsketch depth", core.CMSketchSizeLimit, 1)) + tbl = dom.StatsHandle().GetTableStats(tableInfo) + col = tbl.Columns[1] + require.Equal(t, 20, col.Len()) + + require.Len(t, col.TopN.TopN, 1) + width, depth = col.CMSketch.GetWidthAndDepth() + require.Equal(t, int32(1), depth) + require.Equal(t, int32(core.CMSketchSizeLimit), width) + + // Test very large cmsketch + tk.MustExec("analyze table t with 20480 cmsketch width, 50 cmsketch depth") + tbl = dom.StatsHandle().GetTableStats(tableInfo) + col = tbl.Columns[1] + require.Equal(t, 20, col.Len()) + require.Len(t, col.TopN.TopN, 1) + width, depth = col.CMSketch.GetWidthAndDepth() + require.Equal(t, int32(50), depth) + require.Equal(t, int32(20480), width) +} + +func TestAnalyzeTooLongColumns(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a json)") + value := fmt.Sprintf(`{"x":"%s"}`, strings.Repeat("x", mysql.MaxFieldVarCharLength)) + tk.MustExec(fmt.Sprintf("insert into t values ('%s')", value)) + + tk.MustExec("set @@session.tidb_analyze_skip_column_types = ''") + tk.MustExec("analyze table t") + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + tbl := dom.StatsHandle().GetTableStats(tableInfo) + require.Equal(t, 0, tbl.Columns[1].Len()) + require.Equal(t, 0, tbl.Columns[1].TopN.Num()) + require.Equal(t, int64(65559), tbl.Columns[1].TotColSize) +} + +func TestAnlyzeIssue(t *testing.T) { + // Issue15993 + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t0") + tk.MustExec("CREATE TABLE t0(c0 INT PRIMARY KEY);") + tk.MustExec("ANALYZE TABLE t0 INDEX PRIMARY;") + // Issue15751 + tk.MustExec("drop table if exists t0") + tk.MustExec("CREATE TABLE t0(c0 INT, c1 INT, PRIMARY KEY(c0, c1))") + tk.MustExec("INSERT INTO t0 VALUES (0, 0)") + tk.MustExec("ANALYZE TABLE t0") + // Issue15752 + tk.MustExec("drop table if exists t0") + tk.MustExec("CREATE TABLE t0(c0 INT)") + tk.MustExec("INSERT INTO t0 VALUES (0)") + tk.MustExec("CREATE INDEX i0 ON t0(c0)") + tk.MustExec("ANALYZE TABLE t0 INDEX i0") +} + +func TestFailedAnalyzeRequest(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b int, index index_b(b))") + tk.MustExec("set @@tidb_analyze_version = 1") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/buildStatsFromResult", `return(true)`)) + _, err := tk.Exec("analyze table t") + require.NotNil(t, err) + require.Equal(t, "mock buildStatsFromResult error", err.Error()) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/buildStatsFromResult")) +} + +func TestExtractTopN(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database if not exists test_extract_topn") + tk.MustExec("use test_extract_topn") + tk.MustExec("drop table if exists test_extract_topn") + tk.MustExec("create table test_extract_topn(a int primary key, b int, index index_b(b))") + tk.MustExec("set @@session.tidb_analyze_version=2") + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into test_extract_topn values (%d, %d)", i, i)) + } + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into test_extract_topn values (%d, 0)", i+10)) + } + tk.MustExec("analyze table test_extract_topn") + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test_extract_topn"), model.NewCIStr("test_extract_topn")) + require.NoError(t, err) + tblInfo := table.Meta() + tblStats := dom.StatsHandle().GetTableStats(tblInfo) + colStats := tblStats.Columns[tblInfo.Columns[1].ID] + require.Len(t, colStats.TopN.TopN, 10) + item := colStats.TopN.TopN[0] + require.Equal(t, uint64(11), item.Count) + idxStats := tblStats.Indices[tblInfo.Indices[0].ID] + require.Len(t, idxStats.TopN.TopN, 10) + idxItem := idxStats.TopN.TopN[0] + require.Equal(t, uint64(11), idxItem.Count) + // The columns are: DBName, table name, column name, is index, value, count. + tk.MustQuery("show stats_topn where column_name in ('b', 'index_b')").Sort().Check(testkit.Rows("test_extract_topn test_extract_topn b 0 0 11", + "test_extract_topn test_extract_topn b 0 1 1", + "test_extract_topn test_extract_topn b 0 2 1", + "test_extract_topn test_extract_topn b 0 3 1", + "test_extract_topn test_extract_topn b 0 4 1", + "test_extract_topn test_extract_topn b 0 5 1", + "test_extract_topn test_extract_topn b 0 6 1", + "test_extract_topn test_extract_topn b 0 7 1", + "test_extract_topn test_extract_topn b 0 8 1", + "test_extract_topn test_extract_topn b 0 9 1", + "test_extract_topn test_extract_topn index_b 1 0 11", + "test_extract_topn test_extract_topn index_b 1 1 1", + "test_extract_topn test_extract_topn index_b 1 2 1", + "test_extract_topn test_extract_topn index_b 1 3 1", + "test_extract_topn test_extract_topn index_b 1 4 1", + "test_extract_topn test_extract_topn index_b 1 5 1", + "test_extract_topn test_extract_topn index_b 1 6 1", + "test_extract_topn test_extract_topn index_b 1 7 1", + "test_extract_topn test_extract_topn index_b 1 8 1", + "test_extract_topn test_extract_topn index_b 1 9 1", + )) +} + +func TestNormalAnalyzeOnCommonHandle(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3, t4") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("CREATE TABLE t1 (a int primary key, b int)") + tk.MustExec("insert into t1 values(1,1), (2,2), (3,3)") + tk.MustExec("CREATE TABLE t2 (a varchar(255) primary key, b int)") + tk.MustExec("insert into t2 values(\"111\",1), (\"222\",2), (\"333\",3)") + tk.MustExec("CREATE TABLE t3 (a int, b int, c int, primary key (a, b), key(c))") + tk.MustExec("insert into t3 values(1,1,1), (2,2,2), (3,3,3)") + + // Version2 is tested in TestStatsVer2. + tk.MustExec("set@@tidb_analyze_version=1") + tk.MustExec("analyze table t1, t2, t3") + + tk.MustQuery(`show stats_buckets where table_name in ("t1", "t2", "t3")`).Sort().Check(testkit.Rows( + "test t1 a 0 0 1 1 1 1 0", + "test t1 a 0 1 2 1 2 2 0", + "test t1 a 0 2 3 1 3 3 0", + "test t1 b 0 0 1 1 1 1 0", + "test t1 b 0 1 2 1 2 2 0", + "test t1 b 0 2 3 1 3 3 0", + "test t2 PRIMARY 1 0 1 1 111 111 0", + "test t2 PRIMARY 1 1 2 1 222 222 0", + "test t2 PRIMARY 1 2 3 1 333 333 0", + "test t2 a 0 0 1 1 111 111 0", + "test t2 a 0 1 2 1 222 222 0", + "test t2 a 0 2 3 1 333 333 0", + "test t2 b 0 0 1 1 1 1 0", + "test t2 b 0 1 2 1 2 2 0", + "test t2 b 0 2 3 1 3 3 0", + "test t3 PRIMARY 1 0 1 1 (1, 1) (1, 1) 0", + "test t3 PRIMARY 1 1 2 1 (2, 2) (2, 2) 0", + "test t3 PRIMARY 1 2 3 1 (3, 3) (3, 3) 0", + "test t3 a 0 0 1 1 1 1 0", + "test t3 a 0 1 2 1 2 2 0", + "test t3 a 0 2 3 1 3 3 0", + "test t3 b 0 0 1 1 1 1 0", + "test t3 b 0 1 2 1 2 2 0", + "test t3 b 0 2 3 1 3 3 0", + "test t3 c 0 0 1 1 1 1 0", + "test t3 c 0 1 2 1 2 2 0", + "test t3 c 0 2 3 1 3 3 0", + "test t3 c 1 0 1 1 1 1 0", + "test t3 c 1 1 2 1 2 2 0", + "test t3 c 1 2 3 1 3 3 0")) +} + +func TestDefaultValForAnalyze(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version=1") + defer tk.MustExec("set @@tidb_analyze_version=2") + originalSampleSize := executor.MaxRegionSampleSize + // Increase MaxRegionSampleSize to ensure all samples are collected for building histogram, otherwise the test will be unstable. + executor.MaxRegionSampleSize = 10000 + defer func() { + executor.MaxRegionSampleSize = originalSampleSize + }() + tk.MustExec("drop database if exists test_default_val_for_analyze;") + tk.MustExec("create database test_default_val_for_analyze;") + tk.MustExec("use test_default_val_for_analyze") + + tk.MustExec("create table t (a int, key(a));") + for i := 0; i < 256; i++ { + tk.MustExec("insert into t values (0),(0),(0),(0),(0),(0),(0),(0)") + } + for i := 1; i < 4; i++ { + tk.MustExec("insert into t values (?)", i) + } + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + tk.MustQuery("select @@tidb_enable_fast_analyze").Check(testkit.Rows("0")) + tk.MustQuery("select @@session.tidb_enable_fast_analyze").Check(testkit.Rows("0")) + tk.MustExec("analyze table t with 0 topn, 2 buckets, 10000 samples") + tk.MustQuery("explain format = 'brief' select * from t where a = 1").Check(testkit.Rows("IndexReader 512.00 root index:IndexRangeScan", + "└─IndexRangeScan 512.00 cop[tikv] table:t, index:a(a) range:[1,1], keep order:false")) + tk.MustQuery("explain format = 'brief' select * from t where a = 999").Check(testkit.Rows("IndexReader 0.00 root index:IndexRangeScan", + "└─IndexRangeScan 0.00 cop[tikv] table:t, index:a(a) range:[999,999], keep order:false")) + + tk.MustExec("drop table t;") + tk.MustExec("create table t (a int, key(a));") + for i := 0; i < 256; i++ { + tk.MustExec("insert into t values (0),(0),(0),(0),(0),(0),(0),(0)") + } + for i := 1; i < 2049; i += 8 { + vals := make([]string, 0, 8) + for j := i; j < i+8; j += 1 { + vals = append(vals, fmt.Sprintf("(%v)", j)) + } + tk.MustExec("insert into t values " + strings.Join(vals, ",")) + } + tk.MustExec("analyze table t with 0 topn;") + tk.MustQuery("explain format = 'brief' select * from t where a = 1").Check(testkit.Rows("IndexReader 1.00 root index:IndexRangeScan", + "└─IndexRangeScan 1.00 cop[tikv] table:t, index:a(a) range:[1,1], keep order:false")) +} + +func TestAnalyzeFullSamplingOnIndexWithVirtualColumnOrPrefixColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists sampling_index_virtual_col") + tk.MustExec("create table sampling_index_virtual_col(a int, b int as (a+1), index idx(b))") + tk.MustExec("insert into sampling_index_virtual_col (a) values (1), (2), (null), (3), (4), (null), (5), (5), (5), (5)") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("analyze table sampling_index_virtual_col with 1 topn") + tk.MustQuery("show stats_buckets where table_name = 'sampling_index_virtual_col' and column_name = 'idx'").Check(testkit.Rows( + "test sampling_index_virtual_col idx 1 0 1 1 2 2 0", + "test sampling_index_virtual_col idx 1 1 2 1 3 3 0", + "test sampling_index_virtual_col idx 1 2 3 1 4 4 0", + "test sampling_index_virtual_col idx 1 3 4 1 5 5 0")) + tk.MustQuery("show stats_topn where table_name = 'sampling_index_virtual_col' and column_name = 'idx'").Check(testkit.Rows("test sampling_index_virtual_col idx 1 6 4")) + row := tk.MustQuery(`show stats_histograms where db_name = "test" and table_name = "sampling_index_virtual_col"`).Rows()[0] + // The NDV. + require.Equal(t, "5", row[6]) + // The NULLs. + require.Equal(t, "2", row[7]) + tk.MustExec("drop table if exists sampling_index_prefix_col") + tk.MustExec("create table sampling_index_prefix_col(a varchar(3), index idx(a(1)))") + tk.MustExec("insert into sampling_index_prefix_col (a) values ('aa'), ('ab'), ('ac'), ('bb')") + tk.MustExec("analyze table sampling_index_prefix_col with 1 topn") + tk.MustQuery("show stats_buckets where table_name = 'sampling_index_prefix_col' and column_name = 'idx'").Check(testkit.Rows( + "test sampling_index_prefix_col idx 1 0 1 1 b b 0", + )) + tk.MustQuery("show stats_topn where table_name = 'sampling_index_prefix_col' and column_name = 'idx'").Check(testkit.Rows("test sampling_index_prefix_col idx 1 a 3")) +} + +func testSnapshotAnalyzeAndMaxTSAnalyzeHelper(analyzeSnapshot bool) func(t *testing.T) { + return func(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + if analyzeSnapshot { + tk.MustExec("set @@session.tidb_enable_analyze_snapshot = on") + } else { + tk.MustExec("set @@session.tidb_enable_analyze_snapshot = off") + } + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index index_a(a))") + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + tid := tblInfo.ID + tk.MustExec("insert into t values(1),(1),(1)") + tk.MustExec("begin") + txn, err := tk.Session().Txn(false) + require.NoError(t, err) + startTS1 := txn.StartTS() + tk.MustExec("commit") + tk.MustExec("insert into t values(2),(2),(2)") + tk.MustExec("begin") + txn, err = tk.Session().Txn(false) + require.NoError(t, err) + startTS2 := txn.StartTS() + tk.MustExec("commit") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS1))) + tk.MustExec("analyze table t") + rows := tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() + require.Len(t, rows, 1) + if analyzeSnapshot { + // Analyze cannot see the second insert if it reads the snapshot. + require.Equal(t, "3", rows[0][0]) + } else { + // Analyze can see the second insert if it reads the latest data. + require.Equal(t, "6", rows[0][0]) + } + s1Str := rows[0][1].(string) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS2))) + tk.MustExec("analyze table t") + rows = tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() + require.Len(t, rows, 1) + require.Equal(t, "6", rows[0][0]) + s2Str := rows[0][1].(string) + require.True(t, s1Str != s2Str) + tk.MustExec("set @@session.tidb_analyze_version = 2") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS1))) + tk.MustExec("analyze table t") + rows = tk.MustQuery(fmt.Sprintf("select count, snapshot from mysql.stats_meta where table_id = %d", tid)).Rows() + require.Len(t, rows, 1) + require.Equal(t, "6", rows[0][0]) + s3Str := rows[0][1].(string) + // The third analyze doesn't write results into mysql.stats_xxx because its snapshot is smaller than the second analyze. + require.Equal(t, s2Str, s3Str) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot")) + } +} + +func TestSnapshotAnalyzeAndMaxTSAnalyze(t *testing.T) { + for _, analyzeSnapshot := range []bool{true, false} { + t.Run(fmt.Sprintf("%s-%t", t.Name(), analyzeSnapshot), testSnapshotAnalyzeAndMaxTSAnalyzeHelper(analyzeSnapshot)) + } +} + +func TestAdjustSampleRateNote(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + statsHandle := domain.GetDomain(tk.Session().(sessionctx.Context)).StatsHandle() + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index index_a(a))") + require.NoError(t, statsHandle.HandleDDLEvent(<-statsHandle.DDLEventCh())) + is := tk.Session().(sessionctx.Context).GetInfoSchema().(infoschema.InfoSchema) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + tid := tblInfo.ID + tk.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 220000 where table_id=%d", tid)) + require.NoError(t, statsHandle.Update(is)) + result := tk.MustQuery("show stats_meta where table_name = 't'") + require.Equal(t, "220000", result.Rows()[0][5]) + tk.MustExec("analyze table t") + tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 0.500000 for table test.t, reason to use this rate is \"use min(1, 110000/220000) as the sample-rate=0.5\"")) + tk.MustExec("insert into t values(1),(1),(1)") + require.NoError(t, statsHandle.DumpStatsDeltaToKV(true)) + require.NoError(t, statsHandle.Update(is)) + result = tk.MustQuery("show stats_meta where table_name = 't'") + require.Equal(t, "3", result.Rows()[0][5]) + tk.MustExec("analyze table t") + tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) +} + +func TestAnalyzeIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (id int, v int, primary key(id), index k(v))") + tk.MustExec("insert into t1(id, v) values(1, 2), (2, 2), (3, 2), (4, 2), (5, 1), (6, 3), (7, 4)") + tk.MustExec("set @@tidb_analyze_version=1") + tk.MustExec("analyze table t1 index k") + require.Greater(t, len(tk.MustQuery("show stats_buckets where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 0) + tk.MustExec("set @@tidb_analyze_version=default") + tk.MustExec("analyze table t1") + require.Greater(t, len(tk.MustQuery("show stats_topn where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 0) + + tk.MustExec("drop stats t1") + tk.MustExec("set @@tidb_analyze_version=1") + tk.MustExec("analyze table t1 index k") + require.Greater(t, len(tk.MustQuery("show stats_buckets where table_name = 't1' and column_name = 'k' and is_index = 1").Rows()), 1) +} + +func TestIssue20874(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("delete from mysql.stats_histograms") + tk.MustExec("create table t (a char(10) collate utf8mb4_unicode_ci not null, b char(20) collate utf8mb4_general_ci not null, key idxa(a), key idxb(b))") + tk.MustExec("insert into t values ('#', 'C'), ('$', 'c'), ('a', 'a')") + tk.MustExec("set @@tidb_analyze_version=1") + tk.MustExec("analyze table t") + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check(testkit.Rows( + "test t a 0 0 1 1 \x02\xd2 \x02\xd2 0", + "test t a 0 1 2 1 \x0e\x0f \x0e\x0f 0", + "test t a 0 2 3 1 \x0e3 \x0e3 0", + "test t b 0 0 1 1 \x00A \x00A 0", + "test t b 0 1 3 2 \x00C \x00C 0", + "test t idxa 1 0 1 1 \x02\xd2 \x02\xd2 0", + "test t idxa 1 1 2 1 \x0e\x0f \x0e\x0f 0", + "test t idxa 1 2 3 1 \x0e3 \x0e3 0", + "test t idxb 1 0 1 1 \x00A \x00A 0", + "test t idxb 1 1 3 2 \x00C \x00C 0", + )) + tk.MustQuery("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, correlation from mysql.stats_histograms").Sort().Check(testkit.Rows( + "0 1 3 0 9 1 1", + "0 2 2 0 9 1 -0.5", + "1 1 3 0 0 1 0", + "1 2 2 0 0 1 0", + )) + tk.MustExec("set @@tidb_analyze_version=2") + tk.MustExec("analyze table t") + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check(testkit.Rows( + "test t a 0 \x02\xd2 1", + "test t a 0 \x0e\x0f 1", + "test t a 0 \x0e3 1", + "test t b 0 \x00A 1", + "test t b 0 \x00C 2", + "test t idxa 1 \x02\xd2 1", + "test t idxa 1 \x0e\x0f 1", + "test t idxa 1 \x0e3 1", + "test t idxb 1 \x00A 1", + "test t idxb 1 \x00C 2", + )) + tk.MustQuery("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, correlation from mysql.stats_histograms").Sort().Check(testkit.Rows( + "0 1 3 0 6 2 1", + "0 2 2 0 6 2 -0.5", + "1 1 3 0 6 2 0", + "1 2 2 0 6 2 0", + )) +} + +func TestAnalyzeClusteredIndexPrimary(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t0") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t0(a varchar(20), primary key(a) clustered)") + tk.MustExec("create table t1(a varchar(20), primary key(a))") + tk.MustExec("insert into t0 values('1111')") + tk.MustExec("insert into t1 values('1111')") + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("analyze table t0 index primary") + tk.MustExec("analyze table t1 index primary") + tk.MustQuery("show stats_buckets").Check(testkit.Rows( + "test t0 PRIMARY 1 0 1 1 1111 1111 0", + "test t1 PRIMARY 1 0 1 1 1111 1111 0")) + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("analyze table t0") + tk.MustExec("analyze table t1") + tk.MustQuery("show stats_topn").Sort().Check(testkit.Rows(""+ + "test t0 PRIMARY 1 1111 1", + "test t0 a 0 1111 1", + "test t1 PRIMARY 1 1111 1", + "test t1 a 0 1111 1")) +} + +func TestAnalyzeSamplingWorkPanic(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12)") + tk.MustExec("split table t between (-9223372036854775808) and (9223372036854775807) regions 12") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeSamplingBuildWorkerPanic", "return(1)")) + err := tk.ExecToErr("analyze table t") + require.NotNil(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeSamplingBuildWorkerPanic")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeSamplingMergeWorkerPanic", "return(1)")) + err = tk.ExecToErr("analyze table t") + require.NotNil(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeSamplingMergeWorkerPanic")) +} + +func TestSmallTableAnalyzeV2(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/calcSampleRateByStorageCount", "return(1)")) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("create table small_table_inject_pd(a int)") + tk.MustExec("insert into small_table_inject_pd values(1), (2), (3), (4), (5)") + tk.MustExec("analyze table small_table_inject_pd") + tk.MustQuery("show warnings").Check(testkit.Rows("Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"")) + tk.MustExec(` +create table small_table_inject_pd_with_partition( + a int +) partition by range(a) ( + partition p0 values less than (5), + partition p1 values less than (10), + partition p2 values less than (15) +)`) + tk.MustExec("insert into small_table_inject_pd_with_partition values(1), (6), (11)") + tk.MustExec("analyze table small_table_inject_pd_with_partition") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.small_table_inject_pd_with_partition's partition p2, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + )) + rows := [][]interface{}{ + {"global", "a"}, + {"p0", "a"}, + {"p1", "a"}, + {"p2", "a"}, + } + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 'small_table_inject_pd_with_partition' and last_analyzed_at is not null").Sort().CheckAt([]int{2, 3}, rows) + rows = [][]interface{}{ + {"global", "0", "3"}, + {"p0", "0", "1"}, + {"p1", "0", "1"}, + {"p2", "0", "1"}, + } + tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 'small_table_inject_pd_with_partition'").Sort().CheckAt([]int{2, 4, 5}, rows) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/calcSampleRateByStorageCount")) +} + +func TestSavedAnalyzeOptions(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + originalVal2 := tk.MustQuery("select @@tidb_auto_analyze_ratio").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) + }() + tk.MustExec("set global tidb_auto_analyze_ratio = 0.01") + originalVal3 := autoanalyze.AutoAnalyzeMinCnt + defer func() { + autoanalyze.AutoAnalyzeMinCnt = originalVal3 + }() + autoanalyze.AutoAnalyzeMinCnt = 0 + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("create table t(a int, b int, c int, primary key(a), key idx(b))") + tk.MustExec("insert into t values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") + + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + tk.MustExec("analyze table t with 1 topn, 2 buckets") + is := dom.InfoSchema() + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + tbl := h.GetTableStats(tableInfo) + lastVersion := tbl.Version + col0 := tbl.Columns[tableInfo.Columns[0].ID] + require.Equal(t, 2, len(col0.Buckets)) + col1 := tbl.Columns[tableInfo.Columns[1].ID] + require.Equal(t, 1, len(col1.TopN.TopN)) + require.Equal(t, 2, len(col1.Buckets)) + col2 := tbl.Columns[tableInfo.Columns[2].ID] + require.Equal(t, 2, len(col2.Buckets)) + rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + + // auto-analyze uses the table-level options + tk.MustExec("insert into t values (10,10,10)") + require.Nil(t, h.DumpStatsDeltaToKV(true)) + require.Nil(t, h.Update(is)) + h.HandleAutoAnalyze(is) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + col0 = tbl.Columns[tableInfo.Columns[0].ID] + require.Equal(t, 2, len(col0.Buckets)) + + // manual analyze uses the table-level persisted options by merging the new options + tk.MustExec("analyze table t columns a,b with 1 samplerate, 3 buckets") + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + col0 = tbl.Columns[tableInfo.Columns[0].ID] + require.Equal(t, 3, len(col0.Buckets)) + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + col1 = tbl.Columns[tableInfo.Columns[1].ID] + require.Equal(t, 1, len(col1.TopN.TopN)) + col2 = tbl.Columns[tableInfo.Columns[2].ID] + require.Less(t, col2.LastUpdateVersion, col0.LastUpdateVersion) // not updated since removed from list + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "1", rs.Rows()[0][0]) + require.Equal(t, "3", rs.Rows()[0][1]) + require.Equal(t, "1", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + colIDStrs := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10)}, ",") + require.Equal(t, colIDStrs, rs.Rows()[0][4]) + + // disable option persistence + tk.MustExec("set global tidb_persist_analyze_options = false") + // manual analyze will neither use the pre-persisted options nor persist new options + tk.MustExec("analyze table t with 2 topn") + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + col0 = tbl.Columns[tableInfo.Columns[0].ID] + require.NotEqual(t, 3, len(col0.Buckets)) + rs = tk.MustQuery("select topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tbl.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.NotEqual(t, "2", rs.Rows()[0][0]) +} + +func TestSavedPartitionAnalyzeOptions(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9),(10,10,10),(11,11,11),(12,12,12),(13,13,13),(14,14,14)") + + h := dom.StatsHandle() + + // analyze partition only sets options of partition + tk.MustExec("analyze table t partition p0 with 1 topn, 3 buckets") + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + lastVersion := p0.Version + require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "-1", rs.Rows()[0][1]) + + // merge partition & table level options + tk.MustExec("analyze table t columns a,b with 0 topn, 2 buckets") + p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + p1 := h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) + require.Greater(t, p0.Version, lastVersion) + lastVersion = p0.Version + require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 2, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) + // check column c is not analyzed + require.Less(t, p0.Columns[tableInfo.Columns[2].ID].LastUpdateVersion, p0.Columns[tableInfo.Columns[0].ID].LastUpdateVersion) + require.Less(t, p1.Columns[tableInfo.Columns[2].ID].LastUpdateVersion, p1.Columns[tableInfo.Columns[0].ID].LastUpdateVersion) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + colIDStrsAB := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10)}, ",") + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + + // analyze partition only updates this partition, and set different collist + tk.MustExec("analyze table t partition p1 columns a,c with 1 buckets") + p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + p1 = h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) + require.Equal(t, p0.Version, lastVersion) + require.Greater(t, p1.Version, lastVersion) + lastVersion = p1.Version + require.Equal(t, 1, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + // only column c of p1 is re-analyzed + require.Equal(t, 1, len(p1.Columns[tableInfo.Columns[2].ID].Buckets)) + require.NotEqual(t, 1, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) + colIDStrsABC := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[1].ID, 10), strconv.FormatInt(tableInfo.Columns[2].ID, 10)}, ",") + rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "1", rs.Rows()[0][0]) + require.Equal(t, colIDStrsABC, rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][1]) + + // analyze partition without options uses saved partition options + tk.MustExec("analyze table t partition p0") + p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Greater(t, p0.Version, lastVersion) + lastVersion = p0.Version + require.Equal(t, 2, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + rs = tk.MustQuery("select buckets from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + rs = tk.MustQuery("select buckets from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + + // merge options of statement's, partition's and table's + tk.MustExec("analyze table t partition p0 with 3 buckets") + p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Greater(t, p0.Version, lastVersion) + require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "3", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + + // add new partitions, use table options as default + tk.MustExec("ALTER TABLE t ADD PARTITION (PARTITION p2 VALUES LESS THAN (30))") + tk.MustExec("insert into t values (21,21,21),(22,22,22),(23,23,23),(24,24,24)") + tk.MustExec("analyze table t partition p2") + is = dom.InfoSchema() + table, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = table.Meta() + pi = tableInfo.GetPartitionInfo() + p2 := h.GetPartitionStats(tableInfo, pi.Definitions[2].ID) + require.Equal(t, 2, len(p2.Columns[tableInfo.Columns[0].ID].Buckets)) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p2.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + rs = tk.MustQuery("select sample_rate,buckets,topn,column_choice,column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + require.Equal(t, "0", rs.Rows()[0][2]) + require.Equal(t, "LIST", rs.Rows()[0][3]) + require.Equal(t, colIDStrsAB, rs.Rows()[0][4]) + + // set analyze version back to 1, will not use persisted + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("analyze table t partition p2") + pi = tableInfo.GetPartitionInfo() + p2 = h.GetPartitionStats(tableInfo, pi.Definitions[2].ID) + require.NotEqual(t, 2, len(p2.Columns[tableInfo.Columns[0].ID].Buckets)) + + // drop column + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("alter table t drop column b") + tk.MustExec("analyze table t") + colIDStrsA := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10)}, ",") + colIDStrsAC := strings.Join([]string{strconv.FormatInt(tableInfo.Columns[0].ID, 10), strconv.FormatInt(tableInfo.Columns[2].ID, 10)}, ",") + rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, colIDStrsA, rs.Rows()[0][0]) + rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, colIDStrsA, rs.Rows()[0][0]) + rs = tk.MustQuery("select column_ids from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, colIDStrsAC, rs.Rows()[0][0]) + + // drop partition + tk.MustExec("alter table t drop partition p1") + is = dom.InfoSchema() // refresh infoschema + require.Nil(t, h.GCStats(is, time.Duration(0))) + rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 1, len(rs.Rows())) + rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p1.PhysicalID, 10)) + require.Equal(t, 0, len(rs.Rows())) + + // drop table + tk.MustExec("drop table t") + is = dom.InfoSchema() // refresh infoschema + require.Nil(t, h.GCStats(is, time.Duration(0))) + rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + //require.Equal(t, len(rs.Rows()), 0) TODO + rs = tk.MustQuery("select * from mysql.analyze_options where table_id=" + strconv.FormatInt(p0.PhysicalID, 10)) + require.Equal(t, 0, len(rs.Rows())) +} + +func TestSavedAnalyzeOptionsForMultipleTables(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + tk.MustExec("create table t1(a int, b int, c int, primary key(a), key idx(b))") + tk.MustExec("insert into t1 values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") + tk.MustExec("create table t2(a int, b int, c int, primary key(a), key idx(b))") + tk.MustExec("insert into t2 values (1,1,1),(2,1,2),(3,1,3),(4,1,4),(5,1,5),(6,1,6),(7,7,7),(8,8,8),(9,9,9)") + + h := dom.StatsHandle() + + tk.MustExec("analyze table t1 with 1 topn, 3 buckets") + tk.MustExec("analyze table t2 with 0 topn, 2 buckets") + tk.MustExec("analyze table t1,t2 with 2 topn") + is := dom.InfoSchema() + table1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + table2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tableInfo1 := table1.Meta() + tableInfo2 := table2.Meta() + tblStats1 := h.GetTableStats(tableInfo1) + tblStats2 := h.GetTableStats(tableInfo2) + tbl1Col0 := tblStats1.Columns[tableInfo1.Columns[0].ID] + tbl2Col0 := tblStats2.Columns[tableInfo2.Columns[0].ID] + require.Equal(t, 3, len(tbl1Col0.Buckets)) + require.Equal(t, 2, len(tbl2Col0.Buckets)) + rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo1.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo2.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) +} + +func TestSavedAnalyzeColumnOptions(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + originalVal2 := tk.MustQuery("select @@tidb_auto_analyze_ratio").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) + }() + tk.MustExec("set global tidb_auto_analyze_ratio = 0.01") + originalVal3 := autoanalyze.AutoAnalyzeMinCnt + defer func() { + autoanalyze.AutoAnalyzeMinCnt = originalVal3 + }() + autoanalyze.AutoAnalyzeMinCnt = 0 + originalVal4 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal4)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4)") + + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + tk.MustExec("select * from t where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + tk.MustExec("analyze table t predicate columns") + require.NoError(t, h.LoadNeededHistograms()) + tblStats := h.GetTableStats(tblInfo) + lastVersion := tblStats.Version + // column b is analyzed + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) + tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows("PREDICATE ")) + + tk.MustExec("select * from t where c > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + // manually analyze uses the saved option(predicate columns). + tk.MustExec("analyze table t") + require.NoError(t, h.LoadNeededHistograms()) + tblStats = h.GetTableStats(tblInfo) + require.Less(t, lastVersion, tblStats.Version) + lastVersion = tblStats.Version + // column b, c are analyzed + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) + + tk.MustExec("insert into t values (5,5,5),(6,6,6)") + require.Nil(t, h.DumpStatsDeltaToKV(true)) + require.Nil(t, h.Update(is)) + // auto analyze uses the saved option(predicate columns). + h.HandleAutoAnalyze(is) + tblStats = h.GetTableStats(tblInfo) + require.Less(t, lastVersion, tblStats.Version) + lastVersion = tblStats.Version + // column b, c are analyzed + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) + + tk.MustExec("analyze table t columns a") + tblStats = h.GetTableStats(tblInfo) + require.Less(t, lastVersion, tblStats.Version) + lastVersion = tblStats.Version + // column a is analyzed + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) + require.Greater(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) + tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows(fmt.Sprintf("LIST %v", tblInfo.Columns[0].ID))) + + tk.MustExec("analyze table t all columns") + tblStats = h.GetTableStats(tblInfo) + require.Less(t, lastVersion, tblStats.Version) + lastVersion = tblStats.Version + // column a, b, c are analyzed + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[0].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[1].ID].LastUpdateVersion) + require.Equal(t, lastVersion, tblStats.Columns[tblInfo.Columns[2].ID].LastUpdateVersion) + tk.MustQuery(fmt.Sprintf("select column_choice, column_ids from mysql.analyze_options where table_id = %v", tblInfo.ID)).Check(testkit.Rows("ALL ")) +} + +func TestAnalyzeColumnsWithPrimaryKey(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int, c int primary key)") + tk.MustExec("insert into t values (1,1,1), (1,1,2), (2,2,3), (2,2,4), (3,3,5), (4,3,6), (5,4,7), (6,4,8), (null,null,9)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where a > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "a", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 2, len(rows)) + require.Equal(t, "a", rows[0][3]) + require.Equal(t, "c", rows[1][3]) + + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 9")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t a 0 1 2", + "test t a 0 2 2", + "test t c 0 1 1", + "test t c 0 2 1")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 6 1 8 2 1", + "0 2 0 0 8 0 0", // column b is not analyzed + "0 3 9 0 9 2 1", + )) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t a 0 0 3 1 3 5 0", + "test t a 0 1 4 1 6 6 0", + "test t c 0 0 4 1 3 6 0", + "test t c 0 1 7 1 7 9 0")) + }(val) + } +} + +func TestAnalyzeColumnsWithIndex(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int, c int, d int, index idx_b_d(b, d))") + tk.MustExec("insert into t values (1,1,null,1), (2,1,9,1), (1,1,8,1), (2,2,7,2), (1,3,7,3), (2,4,6,4), (1,4,6,5), (2,4,6,5), (1,5,6,5)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns b,d are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where c > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "c", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 3, len(rows)) + require.Equal(t, "b", rows[0][3]) + require.Equal(t, "c", rows[1][3]) + require.Equal(t, "d", rows[2][3]) + + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 9")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t b 0 1 3", + "test t b 0 4 3", + "test t c 0 6 4", + "test t c 0 7 2", + "test t d 0 1 3", + "test t d 0 5 3", + "test t idx_b_d 1 (1, 1) 3", + "test t idx_b_d 1 (4, 5) 2")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 0 0 9 0 0", // column a is not analyzed + "0 2 5 0 9 2 1", + "0 3 4 1 8 2 -0.07", + "0 4 5 0 9 2 1", + "1 1 6 0 18 2 0")) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t b 0 0 2 1 2 3 0", + "test t b 0 1 3 1 5 5 0", + "test t c 0 0 2 1 8 9 0", + "test t d 0 0 2 1 2 3 0", + "test t d 0 1 3 1 4 4 0", + "test t idx_b_d 1 0 3 1 (2, 2) (4, 4) 0", + "test t idx_b_d 1 1 4 1 (5, 5) (5, 5) 0")) + }(val) + } +} + +func TestAnalyzeColumnsWithClusteredIndex(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int, c int, d int, primary key(b, d) clustered)") + tk.MustExec("insert into t values (1,1,null,1), (2,2,9,2), (1,3,8,3), (2,4,7,4), (1,5,7,5), (2,6,6,6), (1,7,6,7), (2,8,6,8), (1,9,6,9)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns b,d are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where c > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "c", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 3, len(rows)) + require.Equal(t, "b", rows[0][3]) + require.Equal(t, "c", rows[1][3]) + require.Equal(t, "d", rows[2][3]) + + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 9")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t PRIMARY 1 (1, 1) 1", + "test t PRIMARY 1 (2, 2) 1", + "test t b 0 1 1", + "test t b 0 2 1", + "test t c 0 6 4", + "test t c 0 7 2", + "test t d 0 1 1", + "test t d 0 2 1")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 0 0 9 0 0", // column a is not analyzed + "0 2 9 0 9 2 1", + "0 3 4 1 8 2 -0.07", + "0 4 9 0 9 2 1", + "1 1 9 0 18 2 0")) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t PRIMARY 1 0 4 1 (3, 3) (6, 6) 0", + "test t PRIMARY 1 1 7 1 (7, 7) (9, 9) 0", + "test t b 0 0 4 1 3 6 0", + "test t b 0 1 7 1 7 9 0", + "test t c 0 0 2 1 8 9 0", + "test t d 0 0 4 1 3 6 0", + "test t d 0 1 7 1 7 9 0")) + }(val) + } +} + +func TestAnalyzeColumnsWithDynamicPartitionTable(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("create table t (a int, b int, c int, index idx(c)) partition by range (a) (partition p0 values less than (10), partition p1 values less than maxvalue)") + tk.MustExec("insert into t values (1,2,1), (2,4,1), (3,6,1), (4,8,2), (4,8,2), (5,10,3), (5,10,4), (5,10,5), (null,null,6), (11,22,7), (12,24,8), (13,26,9), (14,28,10), (15,30,11), (16,32,12), (16,32,13), (16,32,13), (16,32,14), (17,34,14), (17,34,14)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + defs := tbl.Meta().Partition.Definitions + p0ID := defs[0].ID + p1ID := defs[1].ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where a < 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 6, len(rows)) + require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) + require.Equal(t, []interface{}{"test", "t", "global", "c"}, rows[1][:4]) + require.Equal(t, []interface{}{"test", "t", "p0", "a"}, rows[2][:4]) + require.Equal(t, []interface{}{"test", "t", "p0", "c"}, rows[3][:4]) + require.Equal(t, []interface{}{"test", "t", "p1", "a"}, rows[4][:4]) + require.Equal(t, []interface{}{"test", "t", "p1", "c"}, rows[5][:4]) + + rows = tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 't'").Sort().Rows() + require.Equal(t, 3, len(rows)) + require.Equal(t, []interface{}{"test", "t", "global", "0", "20"}, append(rows[0][:3], rows[0][4:]...)) + require.Equal(t, []interface{}{"test", "t", "p0", "0", "9"}, append(rows[1][:3], rows[1][4:]...)) + require.Equal(t, []interface{}{"test", "t", "p1", "0", "11"}, append(rows[2][:3], rows[2][4:]...)) + + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t global a 0 16 4", + "test t global a 0 5 3", + "test t global c 0 1 3", + "test t global c 0 14 3", + "test t p0 a 0 4 2", + "test t p0 a 0 5 3", + "test t p0 c 0 1 3", + "test t p0 c 0 2 2", + "test t p1 a 0 16 4", + "test t p1 a 0 17 2", + "test t p1 c 0 13 2", + "test t p1 c 0 14 3")) + + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t global idx 1 1 3", + "test t global idx 1 14 3", + "test t p0 idx 1 1 3", + "test t p0 idx 1 2 2", + "test t p1 idx 1 13 2", + "test t p1 idx 1 14 3")) + + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t global a 0 0 5 2 1 4 0", + "test t global a 0 1 12 2 17 17 0", + "test t global c 0 0 6 1 2 6 0", + "test t global c 0 1 14 2 13 13 0", + "test t p0 a 0 0 2 1 1 2 0", + "test t p0 a 0 1 3 1 3 3 0", + "test t p0 c 0 0 3 1 3 5 0", + "test t p0 c 0 1 4 1 6 6 0", + "test t p1 a 0 0 3 1 11 13 0", + "test t p1 a 0 1 5 1 14 15 0", + "test t p1 c 0 0 4 1 7 10 0", + "test t p1 c 0 1 6 1 11 12 0")) + + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t global idx 1 0 6 1 2 6 0", + "test t global idx 1 1 14 2 13 13 0", + "test t p0 idx 1 0 3 1 3 5 0", + "test t p0 idx 1 1 4 1 6 6 0", + "test t p1 idx 1 0 4 1 7 10 0", + "test t p1 idx 1 1 6 1 11 12 0")) + + tk.MustQuery("select table_id, is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms order by table_id, is_index, hist_id asc").Check( + testkit.Rows(fmt.Sprintf("%d 0 1 12 1 19 2 0", tblID), // global, a + fmt.Sprintf("%d 0 3 14 0 20 2 0", tblID), // global, c + fmt.Sprintf("%d 1 1 14 0 0 2 0", tblID), // global, idx + fmt.Sprintf("%d 0 1 5 1 8 2 1", p0ID), // p0, a + fmt.Sprintf("%d 0 2 0 0 8 0 0", p0ID), // p0, b, not analyzed + fmt.Sprintf("%d 0 3 6 0 9 2 1", p0ID), // p0, c + fmt.Sprintf("%d 1 1 6 0 9 2 0", p0ID), // p0, idx + fmt.Sprintf("%d 0 1 7 0 11 2 1", p1ID), // p1, a + fmt.Sprintf("%d 0 2 0 0 11 0 0", p1ID), // p1, b, not analyzed + fmt.Sprintf("%d 0 3 8 0 11 2 1", p1ID), // p1, c + fmt.Sprintf("%d 1 1 8 0 11 2 0", p1ID), // p1, idx + )) + }(val) + } +} + +func TestIssue34228(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`USE test`) + tk.MustExec(`DROP TABLE IF EXISTS Issue34228`) + tk.MustExec(`CREATE TABLE Issue34228 (id bigint NOT NULL, dt datetime NOT NULL) PARTITION BY RANGE COLUMNS(dt) (PARTITION p202201 VALUES LESS THAN ("2022-02-01"), PARTITION p202202 VALUES LESS THAN ("2022-03-01"))`) + tk.MustExec(`INSERT INTO Issue34228 VALUES (1, '2022-02-01 00:00:02'), (2, '2022-02-01 00:00:02')`) + tk.MustExec(`SET @@global.tidb_analyze_version = 1`) + tk.MustExec(`SET @@session.tidb_partition_prune_mode = 'static'`) + tk.MustExec(`ANALYZE TABLE Issue34228`) + tk.MustExec(`SET @@session.tidb_partition_prune_mode = 'dynamic'`) + tk.MustExec(`ANALYZE TABLE Issue34228`) + tk.MustQuery(`SELECT * FROM Issue34228`).Sort().Check(testkit.Rows("1 2022-02-01 00:00:02", "2 2022-02-01 00:00:02")) + // Needs a second run to hit the issue + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec(`USE test`) + tk2.MustExec(`DROP TABLE IF EXISTS Issue34228`) + tk2.MustExec(`CREATE TABLE Issue34228 (id bigint NOT NULL, dt datetime NOT NULL) PARTITION BY RANGE COLUMNS(dt) (PARTITION p202201 VALUES LESS THAN ("2022-02-01"), PARTITION p202202 VALUES LESS THAN ("2022-03-01"))`) + tk2.MustExec(`INSERT INTO Issue34228 VALUES (1, '2022-02-01 00:00:02'), (2, '2022-02-01 00:00:02')`) + tk2.MustExec(`SET @@global.tidb_analyze_version = 1`) + tk2.MustExec(`SET @@session.tidb_partition_prune_mode = 'static'`) + tk2.MustExec(`ANALYZE TABLE Issue34228`) + tk2.MustExec(`SET @@session.tidb_partition_prune_mode = 'dynamic'`) + tk2.MustExec(`ANALYZE TABLE Issue34228`) + tk2.MustQuery(`SELECT * FROM Issue34228`).Sort().Check(testkit.Rows("1 2022-02-01 00:00:02", "2 2022-02-01 00:00:02")) +} + +func TestAnalyzeColumnsWithStaticPartitionTable(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec("create table t (a int, b int, c int, index idx(c)) partition by range (a) (partition p0 values less than (10), partition p1 values less than maxvalue)") + tk.MustExec("insert into t values (1,2,1), (2,4,1), (3,6,1), (4,8,2), (4,8,2), (5,10,3), (5,10,4), (5,10,5), (null,null,6), (11,22,7), (12,24,8), (13,26,9), (14,28,10), (15,30,11), (16,32,12), (16,32,13), (16,32,13), (16,32,14), (17,34,14), (17,34,14)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + defs := tbl.Meta().Partition.Definitions + p0ID := defs[0].ID + p1ID := defs[1].ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns a with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where a < 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, []interface{}{"test", "t", "global", "a"}, rows[0][:4]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 4, len(rows)) + require.Equal(t, []interface{}{"test", "t", "p0", "a"}, rows[0][:4]) + require.Equal(t, []interface{}{"test", "t", "p0", "c"}, rows[1][:4]) + require.Equal(t, []interface{}{"test", "t", "p1", "a"}, rows[2][:4]) + require.Equal(t, []interface{}{"test", "t", "p1", "c"}, rows[3][:4]) + + rows = tk.MustQuery("show stats_meta where db_name = 'test' and table_name = 't'").Sort().Rows() + require.Equal(t, 2, len(rows)) + require.Equal(t, []interface{}{"test", "t", "p0", "0", "9"}, append(rows[0][:3], rows[0][4:]...)) + require.Equal(t, []interface{}{"test", "t", "p1", "0", "11"}, append(rows[1][:3], rows[1][4:]...)) + + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t p0 a 0 4 2", + "test t p0 a 0 5 3", + "test t p0 c 0 1 3", + "test t p0 c 0 2 2", + "test t p1 a 0 16 4", + "test t p1 a 0 17 2", + "test t p1 c 0 13 2", + "test t p1 c 0 14 3")) + + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t p0 idx 1 1 3", + "test t p0 idx 1 2 2", + "test t p1 idx 1 13 2", + "test t p1 idx 1 14 3")) + + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 0").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t p0 a 0 0 2 1 1 2 0", + "test t p0 a 0 1 3 1 3 3 0", + "test t p0 c 0 0 3 1 3 5 0", + "test t p0 c 0 1 4 1 6 6 0", + "test t p1 a 0 0 3 1 11 13 0", + "test t p1 a 0 1 5 1 14 15 0", + "test t p1 c 0 0 4 1 7 10 0", + "test t p1 c 0 1 6 1 11 12 0")) + + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't' and is_index = 1").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t p0 idx 1 0 3 1 3 5 0", + "test t p0 idx 1 1 4 1 6 6 0", + "test t p1 idx 1 0 4 1 7 10 0", + "test t p1 idx 1 1 6 1 11 12 0")) + + tk.MustQuery("select table_id, is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms order by table_id, is_index, hist_id asc").Check( + testkit.Rows(fmt.Sprintf("%d 0 1 5 1 8 2 1", p0ID), // p0, a + fmt.Sprintf("%d 0 2 0 0 8 0 0", p0ID), // p0, b, not analyzed + fmt.Sprintf("%d 0 3 6 0 9 2 1", p0ID), // p0, c + fmt.Sprintf("%d 1 1 6 0 9 2 0", p0ID), // p0, idx + fmt.Sprintf("%d 0 1 7 0 11 2 1", p1ID), // p1, a + fmt.Sprintf("%d 0 2 0 0 11 0 0", p1ID), // p1, b, not analyzed + fmt.Sprintf("%d 0 3 8 0 11 2 1", p1ID), // p1, c + fmt.Sprintf("%d 1 1 8 0 11 2 0", p1ID), // p1, idx + )) + }(val) + } +} + +func TestAnalyzeColumnsWithExtendedStats(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_enable_extended_stats = on") + tk.MustExec("create table t (a int, b int, c int)") + tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") + tk.MustExec("insert into t values (5,1,1), (4,2,2), (3,3,3), (2,4,4), (1,5,5)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "b", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Sort().Rows() + require.Equal(t, 2, len(rows)) + require.Equal(t, "b", rows[0][3]) + require.Equal(t, "c", rows[1][3]) + + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 5")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t b 0 1 1", + "test t b 0 2 1", + "test t c 0 1 1", + "test t c 0 2 1")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 0 0 5 0 0", // column a is not analyzed + "0 2 5 0 5 2 1", + "0 3 5 0 5 2 1", + )) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t b 0 0 2 1 3 4 0", + "test t b 0 1 3 1 5 5 0", + "test t c 0 0 2 1 3 4 0", + "test t c 0 1 3 1 5 5 0")) + rows = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, []interface{}{"test", "t", "s1", "[b,c]", "correlation", "1.000000"}, rows[0][:len(rows[0])-1]) + }(val) + } +} + +func TestAnalyzeColumnsWithVirtualColumnIndex(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int, c int as (b+1), index idx(c))") + tk.MustExec("insert into t (a,b) values (1,1), (2,2), (3,3), (4,4), (5,4), (6,5), (7,5), (8,5), (null,null)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns c are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + )) + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "b", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + // virtual column c is skipped when dumping stats into disk, so only the stats of column b are updated + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "b", rows[0][3]) + + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 9")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t b 0 4 2", + "test t b 0 5 3", + "test t idx 1 5 2", + "test t idx 1 6 3")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 0 0 0 0", // column a is not analyzed + "0 2 5 1 2 1", + "0 3 0 0 0 0", // column c is not analyzed + "1 1 5 1 2 0")) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t b 0 0 2 1 1 2 0", + "test t b 0 1 3 1 3 3 0", + "test t idx 1 0 2 1 2 3 0", + "test t idx 1 1 3 1 4 4 0")) + }(val) + } +} + +func TestAnalyzeColumnsAfterAnalyzeAll(t *testing.T) { + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t (a,b) values (1,1), (1,1), (2,2), (2,2), (3,3), (4,4)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblID := tbl.Meta().ID + + tk.MustExec("analyze table t with 2 topn, 2 buckets") + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 6")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t a 0 1 2", + "test t a 0 2 2", + "test t b 0 1 2", + "test t b 0 2 2")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 4 0 6 2 1", + "0 2 4 0 6 2 1")) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t a 0 0 2 1 3 4 0", + "test t b 0 0 2 1 3 4 0")) + + tk.MustExec("insert into t (a,b) values (1,1), (6,6)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns b with 2 topn, 2 buckets") + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, "b", rows[0][3]) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + } + + // Column a is not analyzed in second ANALYZE. We keep the outdated stats of column a rather than delete them. + tk.MustQuery(fmt.Sprintf("select modify_count, count from mysql.stats_meta where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 8")) + tk.MustQuery("show stats_topn where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_idx, value, count + testkit.Rows("test t a 0 1 2", + "test t a 0 2 2", + "test t b 0 1 3", + "test t b 0 2 2")) + tk.MustQuery(fmt.Sprintf("select is_index, hist_id, distinct_count, null_count, tot_col_size, stats_ver, truncate(correlation,2) from mysql.stats_histograms where table_id = %d", tblID)).Sort().Check( + testkit.Rows("0 1 4 0 8 2 1", // tot_col_size of column a is updated to 8 by DumpStatsDeltaToKV + "0 2 5 0 8 2 0.76")) + tk.MustQuery("show stats_buckets where db_name = 'test' and table_name = 't'").Sort().Check( + // db, tbl, part, col, is_index, bucket_id, count, repeats, lower, upper, ndv + testkit.Rows("test t a 0 0 2 1 3 4 0", + "test t b 0 0 2 1 3 4 0", + "test t b 0 1 3 1 6 6 0")) + tk.MustQuery(fmt.Sprintf("select hist_id from mysql.stats_histograms where version = (select version from mysql.stats_meta where table_id = %d)", tblID)).Check(testkit.Rows("2")) + }(val) + } +} + +func TestAnalyzeSampleRateReason(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) + + tk.MustExec(`analyze table t`) + tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows( + `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "use min(1, 110000/10000) as the sample-rate=1"`)) + + tk.MustExec(`insert into t values (1, 1), (2, 2), (3, 3)`) + require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) + tk.MustExec(`analyze table t`) + tk.MustQuery(`show warnings`).Sort().Check(testkit.Rows( + `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "TiDB assumes that the table is empty, use sample-rate=1"`)) +} + +func TestAnalyzeColumnsErrorAndWarning(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + + // analyze version 1 doesn't support `ANALYZE COLUMNS c1, ..., cn`/`ANALYZE PREDICATE COLUMNS` currently + tk.MustExec("set @@tidb_analyze_version = 1") + err := tk.ExecToErr("analyze table t columns a") + require.Equal(t, "Only the version 2 of analyze supports analyzing the specified columns", err.Error()) + err = tk.ExecToErr("analyze table t predicate columns") + require.Equal(t, "Only the version 2 of analyze supports analyzing predicate columns", err.Error()) + + tk.MustExec("set @@tidb_analyze_version = 2") + // invalid column + err = tk.ExecToErr("analyze table t columns c") + terr := errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrAnalyzeMissColumn), terr.Code()) + + // If no predicate column is collected, analyze predicate columns gives a warning and falls back to analyze all columns. + tk.MustExec("analyze table t predicate columns") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "use min(1, 110000/10000) as the sample-rate=1"`, + "Warning 1105 No predicate column has been collected yet for table test.t so all columns are analyzed", + )) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_analyzed_at is not null").Rows() + require.Equal(t, 2, len(rows)) + + for _, val := range []model.ColumnChoice{model.ColumnList, model.PredicateColumns} { + func(choice model.ColumnChoice) { + tk.MustExec("set @@tidb_analyze_version = 1") + tk.MustExec("analyze table t") + tk.MustExec("set @@tidb_analyze_version = 2") + switch choice { + case model.ColumnList: + tk.MustExec("analyze table t columns b") + case model.PredicateColumns: + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b > 1") + require.NoError(t, dom.StatsHandle().DumpColStatsUsageToKV()) + tk.MustExec("analyze table t predicate columns") + } + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + `Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is "TiDB assumes that the table is empty, use sample-rate=1"`, + "Warning 1105 Table test.t has version 1 statistics so all the columns must be analyzed to overwrite the current statistics", + )) + }(val) + } +} + +func checkAnalyzeStatus(t *testing.T, tk *testkit.TestKit, jobInfo, status, failReason, comment string, timeLimit int64) { + rows := tk.MustQuery("show analyze status where table_schema = 'test' and table_name = 't' and partition_name = ''").Rows() + require.Equal(t, 1, len(rows), comment) + require.Equal(t, jobInfo, rows[0][3], comment) + require.Equal(t, status, rows[0][7], comment) + require.Equal(t, failReason, rows[0][8], comment) + if timeLimit <= 0 { + return + } + const layout = time.DateTime + startTime, err := time.Parse(layout, rows[0][5].(string)) + require.NoError(t, err, comment) + endTime, err := time.Parse(layout, rows[0][6].(string)) + require.NoError(t, err, comment) + require.Less(t, endTime.Sub(startTime), time.Duration(timeLimit)*time.Second, comment) +} + +func testKillAutoAnalyze(t *testing.T, ver int) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + tk.MustExec(fmt.Sprintf("set @@tidb_analyze_version = %v", ver)) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (1,2), (3,4)") + is := dom.InfoSchema() + h := dom.StatsHandle() + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t") + tk.MustExec("insert into t values (5,6), (7,8), (9, 10)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + lastVersion := h.GetTableStats(tableInfo).Version + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + jobInfo := "auto analyze " + if ver == 1 { + jobInfo += "columns" + } else { + jobInfo += "table all columns with 256 buckets, 500 topn, 1 samplerate" + } + // kill auto analyze when it is pending/running/finished + for _, status := range []string{ + "pending", + "running", + "finished", + } { + func() { + comment := fmt.Sprintf("kill %v analyze job", status) + tk.MustExec("delete from mysql.analyze_jobs") + mockAnalyzeStatus := "github.com/pingcap/tidb/pkg/executor/mockKill" + strings.Title(status) + if status == "running" { + mockAnalyzeStatus += "V" + strconv.Itoa(ver) + } + mockAnalyzeStatus += "AnalyzeJob" + require.NoError(t, failpoint.Enable(mockAnalyzeStatus, "return")) + defer func() { + require.NoError(t, failpoint.Disable(mockAnalyzeStatus)) + }() + if status == "pending" || status == "running" { + mockSlowAnalyze := "github.com/pingcap/tidb/pkg/executor/mockSlowAnalyzeV" + strconv.Itoa(ver) + require.NoError(t, failpoint.Enable(mockSlowAnalyze, "return")) + defer func() { + require.NoError(t, failpoint.Disable(mockSlowAnalyze)) + }() + } + require.True(t, h.HandleAutoAnalyze(is), comment) + currentVersion := h.GetTableStats(tableInfo).Version + if status == "finished" { + // If we kill a finished job, after kill command the status is still finished and the table stats are updated. + checkAnalyzeStatus(t, tk, jobInfo, "finished", "", comment, -1) + require.Greater(t, currentVersion, lastVersion, comment) + } else { + // If we kill a pending/running job, after kill command the status is failed and the table stats are not updated. + // We expect the killed analyze stops quickly. Specifically, end_time - start_time < 10s. + checkAnalyzeStatus(t, tk, jobInfo, "failed", exeerrors.ErrQueryInterrupted.Error(), comment, 10) + require.Equal(t, currentVersion, lastVersion, comment) + } + }() + } +} + +func TestKillAutoAnalyzeV1(t *testing.T) { + testKillAutoAnalyze(t, 1) +} + +func TestKillAutoAnalyzeV2(t *testing.T) { + testKillAutoAnalyze(t, 2) +} + +func TestKillAutoAnalyzeIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + tk.MustExec("set @@tidb_analyze_version = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (1,2), (3,4)") + is := dom.InfoSchema() + h := dom.StatsHandle() + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t") + tk.MustExec("alter table t add index idx(b)") + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + lastVersion := h.GetTableStats(tblInfo).Version + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + const jobInfo = "auto analyze index idx" + // kill auto analyze when it is pending/running/finished + for _, status := range []string{"pending", "running", "finished"} { + func() { + comment := fmt.Sprintf("kill %v analyze job", status) + tk.MustExec("delete from mysql.analyze_jobs") + mockAnalyzeStatus := "github.com/pingcap/tidb/pkg/executor/mockKill" + strings.Title(status) + if status == "running" { + mockAnalyzeStatus += "AnalyzeIndexJob" + } else { + mockAnalyzeStatus += "AnalyzeJob" + } + require.NoError(t, failpoint.Enable(mockAnalyzeStatus, "return")) + defer func() { + require.NoError(t, failpoint.Disable(mockAnalyzeStatus)) + }() + if status == "pending" || status == "running" { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockSlowAnalyzeIndex", "return")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockSlowAnalyzeIndex")) + }() + } + require.True(t, h.HandleAutoAnalyze(dom.InfoSchema()), comment) + currentVersion := h.GetTableStats(tblInfo).Version + if status == "finished" { + // If we kill a finished job, after kill command the status is still finished and the index stats are updated. + checkAnalyzeStatus(t, tk, jobInfo, "finished", "", comment, -1) + require.Greater(t, currentVersion, lastVersion, comment) + } else { + // If we kill a pending/running job, after kill command the status is failed and the index stats are not updated. + // We expect the killed analyze stops quickly. Specifically, end_time - start_time < 10s. + checkAnalyzeStatus(t, tk, jobInfo, "failed", exeerrors.ErrQueryInterrupted.Error(), comment, 10) + require.Equal(t, currentVersion, lastVersion, comment) + } + }() + } +} + +func TestAnalyzeJob(t *testing.T) { + store := testkit.CreateMockStore(t) + for _, result := range []string{statistics.AnalyzeFinished, statistics.AnalyzeFailed} { + tk := testkit.NewTestKit(t, store) + tk.MustExec("delete from mysql.analyze_jobs") + se := tk.Session() + job := &statistics.AnalyzeJob{ + DBName: "test", + TableName: "t", + PartitionName: "", + JobInfo: "table all columns with 256 buckets, 500 topn, 1 samplerate", + } + executor.AddNewAnalyzeJob(se, job) + require.NotNil(t, job.ID) + rows := tk.MustQuery("show analyze status").Rows() + require.Len(t, rows, 1) + require.Equal(t, job.DBName, rows[0][0]) + require.Equal(t, job.TableName, rows[0][1]) + require.Equal(t, job.PartitionName, rows[0][2]) + require.Equal(t, job.JobInfo, rows[0][3]) + require.Equal(t, "0", rows[0][4]) + require.Equal(t, "", rows[0][5]) + require.Equal(t, "", rows[0][6]) + require.Equal(t, statistics.AnalyzePending, rows[0][7]) + require.Equal(t, "", rows[0][8]) + serverInfo, err := infosync.GetServerInfo() + require.NoError(t, err) + addr := fmt.Sprintf("%s:%d", serverInfo.IP, serverInfo.Port) + require.Equal(t, addr, rows[0][9]) + connID := strconv.FormatUint(tk.Session().GetSessionVars().ConnectionID, 10) + require.Equal(t, connID, rows[0][10]) + + executor.StartAnalyzeJob(se, job) + ctx := context.WithValue(context.Background(), executor.AnalyzeProgressTest, 100) + rows = tk.MustQueryWithContext(ctx, "show analyze status").Rows() + checkTime := func(val interface{}) { + str, ok := val.(string) + require.True(t, ok) + _, err := time.Parse(time.DateTime, str) + require.NoError(t, err) + } + checkTime(rows[0][5]) + require.Equal(t, statistics.AnalyzeRunning, rows[0][7]) + require.Equal(t, "9m0s", rows[0][11]) // REMAINING_SECONDS + require.Equal(t, "0.1", rows[0][12]) // PROGRESS + require.Equal(t, "0", rows[0][13]) // ESTIMATED_TOTAL_ROWS + + // UpdateAnalyzeJob requires the interval between two updates to mysql.analyze_jobs is more than 5 second. + // Hence we fake last dump time as 10 second ago in order to make update to mysql.analyze_jobs happen. + lastDumpTime := time.Now().Add(-10 * time.Second) + job.Progress.SetLastDumpTime(lastDumpTime) + const smallCount int64 = 100 + executor.UpdateAnalyzeJob(se, job, smallCount) + // Delta count doesn't reach threshold so we don't dump it to mysql.analyze_jobs + require.Equal(t, smallCount, job.Progress.GetDeltaCount()) + require.Equal(t, lastDumpTime, job.Progress.GetLastDumpTime()) + rows = tk.MustQuery("show analyze status").Rows() + require.Equal(t, "0", rows[0][4]) + + const largeCount int64 = 15000000 + executor.UpdateAnalyzeJob(se, job, largeCount) + // Delta count reaches threshold so we dump it to mysql.analyze_jobs and update last dump time. + require.Equal(t, int64(0), job.Progress.GetDeltaCount()) + require.True(t, job.Progress.GetLastDumpTime().After(lastDumpTime)) + lastDumpTime = job.Progress.GetLastDumpTime() + rows = tk.MustQuery("show analyze status").Rows() + require.Equal(t, strconv.FormatInt(smallCount+largeCount, 10), rows[0][4]) + + executor.UpdateAnalyzeJob(se, job, largeCount) + // We have just updated mysql.analyze_jobs in the previous step so we don't update it until 5 second passes or the analyze job is over. + require.Equal(t, largeCount, job.Progress.GetDeltaCount()) + require.Equal(t, lastDumpTime, job.Progress.GetLastDumpTime()) + rows = tk.MustQuery("show analyze status").Rows() + require.Equal(t, strconv.FormatInt(smallCount+largeCount, 10), rows[0][4]) + + var analyzeErr error + if result == statistics.AnalyzeFailed { + analyzeErr = errors.Errorf("analyze meets error") + } + executor.FinishAnalyzeJob(se, job, analyzeErr) + rows = tk.MustQuery("show analyze status").Rows() + require.Equal(t, strconv.FormatInt(smallCount+2*largeCount, 10), rows[0][4]) + checkTime(rows[0][6]) + require.Equal(t, result, rows[0][7]) + if result == statistics.AnalyzeFailed { + require.Equal(t, analyzeErr.Error(), rows[0][8]) + } else { + require.Equal(t, "", rows[0][8]) + } + // process_id is set to NULL after the analyze job is finished/failed. + require.Equal(t, "", rows[0][10]) + } +} + +func TestInsertAnalyzeJobWithLongInstance(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("delete from mysql.analyze_jobs") + job := &statistics.AnalyzeJob{ + DBName: "test", + TableName: "t", + PartitionName: "", + JobInfo: "table all columns with 256 buckets, 500 topn, 1 samplerate", + } + h := dom.StatsHandle() + instance := "xxxtidb-tidb-0.xxxtidb-tidb-peer.xxxx-xx-1234-xxx-123456-1-321.xyz:4000" + require.NoError(t, h.InsertAnalyzeJob(job, instance, 1)) + rows := tk.MustQuery("show analyze status").Rows() + require.Len(t, rows, 1) + require.Equal(t, instance, rows[0][9]) +} + +func TestShowAanalyzeStatusJobInfo(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + originalVal2 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal2)) + }() + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set global tidb_persist_analyze_options = 0") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, c int, d int, index idx_b_d(b, d))") + tk.MustExec("insert into t values (1,1,null,1), (2,1,9,1), (1,1,8,1), (2,2,7,2), (1,3,7,3), (2,4,6,4), (1,4,6,5), (2,4,6,5), (1,5,6,5)") + tk.MustExec("analyze table t columns c with 2 topn, 2 buckets") + checkJobInfo := func(expected string) { + rows := tk.MustQuery("show analyze status where table_schema = 'test' and table_name = 't'").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, expected, rows[0][3]) + tk.MustExec("delete from mysql.analyze_jobs") + } + checkJobInfo("analyze table columns b, c, d with 2 buckets, 2 topn, 1 samplerate") + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where c > 1") + h := dom.StatsHandle() + require.NoError(t, h.DumpColStatsUsageToKV()) + tk.MustExec("analyze table t predicate columns with 2 topn, 2 buckets") + checkJobInfo("analyze table columns b, c, d with 2 buckets, 2 topn, 1 samplerate") + tk.MustExec("analyze table t") + checkJobInfo("analyze table all columns with 256 buckets, 500 topn, 1 samplerate") + tk.MustExec("set global tidb_persist_analyze_options = 1") + tk.MustExec("analyze table t columns a with 1 topn, 3 buckets") + checkJobInfo("analyze table columns a, b, d with 3 buckets, 1 topn, 1 samplerate") + tk.MustExec("analyze table t") + checkJobInfo("analyze table columns a, b, d with 3 buckets, 1 topn, 1 samplerate") +} + +func TestAnalyzePartitionTableWithDynamicMode(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") + tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + + // analyze table only sets table options and gen globalStats + tk.MustExec("analyze table t columns a,c with 1 topn, 3 buckets") + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl := h.GetTableStats(tableInfo) + lastVersion := tbl.Version + // both globalStats and partition stats generated and options saved for column a,c + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) + rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + + // analyze table with persisted table-level options + tk.MustExec("analyze table t") + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + + // analyze table with merged table-level options + tk.MustExec("analyze table t with 2 topn, 2 buckets") + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[0].ID].TopN.TopN)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].TopN.TopN)) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) +} + +func TestAnalyzePartitionTableStaticToDynamic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") + tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + + // analyze partition under static mode with options + tk.MustExec("analyze table t partition p0 columns a,c with 1 topn, 3 buckets") + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl := h.GetTableStats(tableInfo) + p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + p1 := h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) + lastVersion := tbl.Version + require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) + require.Equal(t, 0, len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, 0, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets)) + rs := tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + // The columns are: table_id, sample_num, sample_rate, buckets, topn, column_choice, column_ids. + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + + // analyze table in dynamic mode will ignore partition-level options and use default + tk.MustExec("analyze table t") + tk.MustQuery("select * from t where b > 1 and c > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + p0 = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + p1 = h.GetPartitionStats(tableInfo, pi.Definitions[1].ID) + require.NotEqual(t, 3, len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets), len(p0.Columns[tableInfo.Columns[0].ID].Buckets)) + require.Equal(t, len(tbl.Columns[tableInfo.Columns[0].ID].Buckets), len(p1.Columns[tableInfo.Columns[0].ID].Buckets)) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "0", rs.Rows()[0][0]) + + // analyze table under dynamic mode with specified options with old partition-level options + tk.MustExec("analyze table t columns b,d with 2 topn, 2 buckets") + tk.MustQuery("select * from t where b > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[1].ID].Buckets)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, "2", rs.Rows()[0][1]) + + // analyze table under dynamic mode without options with old table-level & partition-level options + tk.MustExec("analyze table t") + tk.MustQuery("select * from t where b > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + lastVersion = tbl.Version + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].TopN.TopN)) + + // analyze table under dynamic mode with specified options with old table-level & partition-level options + tk.MustExec("analyze table t with 1 topn") + tk.MustQuery("select * from t where b > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[1].ID].Buckets)) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[1].ID].TopN.TopN)) + require.Equal(t, 1, len(tbl.Columns[tableInfo.Columns[3].ID].TopN.TopN)) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[0].ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "3", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(pi.Definitions[1].ID, 10)) + require.Equal(t, 0, len(rs.Rows())) + rs = tk.MustQuery("select buckets,topn from mysql.analyze_options where table_id=" + strconv.FormatInt(tableInfo.ID, 10)) + require.Equal(t, 1, len(rs.Rows())) + require.Equal(t, "2", rs.Rows()[0][0]) + require.Equal(t, "1", rs.Rows()[0][1]) +} + +func TestAnalyzePartitionUnderDynamic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + tk.MustExec("set global tidb_persist_analyze_options = true") + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") + tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + + // analyze partition with options under dynamic mode + tk.MustExec("analyze table t partition p0 columns a,b,c with 1 topn, 3 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", + )) + tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl := h.GetTableStats(tableInfo) + lastVersion := tbl.Version + require.NotEqual(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) + require.NotEqual(t, 3, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) + + tk.MustExec("analyze table t partition p0") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/9) as the sample-rate=1\"", + )) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) // global stats updated +} + +func TestAnalyzePartitionStaticToDynamic(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("set @@session.tidb_skip_missing_partition_stats = 0") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") + tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + + // generate old partition stats + tk.MustExec("set global tidb_persist_analyze_options = false") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + tk.MustExec("analyze table t partition p0 columns a,c with 1 topn, 3 buckets") + tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + p0 := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, 3, len(p0.Columns[tableInfo.Columns[2].ID].Buckets)) + + // analyze partition with existing stats of other partitions under dynamic + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", + )) + + // analyze partition with existing table-level options and existing partition stats under dynamic + tk.MustExec("insert into mysql.analyze_options values (?,?,?,?,?,?,?)", tableInfo.ID, 0, 0, 2, 2, "DEFAULT", "") + tk.MustExec("set global tidb_persist_analyze_options = true") + tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/5) as the sample-rate=1\"", + "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", + "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", + )) + + // analyze partition with existing table-level & partition-level options and existing partition stats under dynamic + tk.MustExec("insert into mysql.analyze_options values (?,?,?,?,?,?,?)", pi.Definitions[1].ID, 0, 0, 1, 1, "DEFAULT", "") + tk.MustExec("analyze table t partition p1 columns a,b,d with 1 topn, 3 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p1, reason to use this rate is \"use min(1, 110000/5) as the sample-rate=1\"", + "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", + "Warning 8244 Build global-level stats failed due to missing partition-level column stats: table `t` partition `p0` column `d`, please run analyze table to refresh columns of all partitions", + )) + // flaky test, fix it later + //tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") + //require.NoError(t, h.LoadNeededHistograms()) + //tbl := h.GetTableStats(tableInfo) + //require.Equal(t, 0, len(tbl.Columns)) + + // ignore both p0's 3 buckets, persisted-partition-options' 1 bucket, just use table-level 2 buckets + tk.MustExec("analyze table t partition p0") + tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl := h.GetTableStats(tableInfo) + require.Equal(t, 2, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) +} + +func TestAnalyzePartitionUnderV1Dynamic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal)) + }() + + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("set @@session.tidb_stats_load_sync_wait = 20000") // to stabilise test + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + createTable := `CREATE TABLE t (a int, b int, c varchar(10), d int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1,1,1,1),(2,1,2,2),(3,1,3,3),(4,1,4,4),(5,1,5,5),(6,1,6,6),(7,7,7,7),(8,8,8,8),(9,9,9,9)") + tk.MustExec("insert into t values (10,10,10,10),(11,11,11,11),(12,12,12,12),(13,13,13,13),(14,14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + + // analyze partition with index and with options are allowed under dynamic V1 + tk.MustExec("analyze table t partition p0 with 1 topn, 3 buckets") + rows := tk.MustQuery("show warnings").Rows() + require.Len(t, rows, 0) + tk.MustExec("analyze table t partition p1 with 1 topn, 3 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows()) + tk.MustQuery("select * from t where a > 1 and b > 1 and c > 1 and d > 1") + require.NoError(t, h.LoadNeededHistograms()) + tbl := h.GetTableStats(tableInfo) + lastVersion := tbl.Version + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[2].ID].Buckets)) + require.Equal(t, 3, len(tbl.Columns[tableInfo.Columns[3].ID].Buckets)) + + tk.MustExec("analyze table t partition p1 index idx with 1 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows()) + tbl = h.GetTableStats(tableInfo) + require.Greater(t, tbl.Version, lastVersion) + require.Equal(t, 2, len(tbl.Indices[tableInfo.Indices[0].ID].Buckets)) +} + +func TestIssue35056(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 1") + createTable := `CREATE TABLE t (id int, a int, b varchar(10)) +PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(7,7,7),(9,9,9)") + tk.MustExec("insert into t values (11,11,11),(12,12,12),(14,14,14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + h.HandleAutoAnalyze(is) + tk.MustExec("create index idxa on t (a)") + tk.MustExec("create index idxb on t (b)") + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + tk.MustExec("analyze table t partition p0 index idxa") + tk.MustExec("analyze table t partition p1 index idxb") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t partition p0") // no panic +} + +func TestIssue35056Related(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + createTable := `CREATE TABLE t (id int) +PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + tk.MustExec("insert into t values (1),(2),(3),(4),(7),(9)") + tk.MustExec("insert into t values (11),(12),(14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + h.HandleAutoAnalyze(is) + tk.MustExec("alter table t add column a int") + tk.MustExec("alter table t add column b int") + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + tk.MustExec("analyze table t partition p0 columns id,a") + tk.MustExec("analyze table t partition p1 columns id,b") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t partition p0") // no panic +} + +func TestIssue35044(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'static'") + createTable := `CREATE TABLE t (a int) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (20) +)` + tk.MustExec(createTable) + tk.MustExec("insert into t values (1),(2),(3)") + tk.MustExec("insert into t values (11),(12),(14)") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + is := dom.InfoSchema() + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + require.NotNil(t, pi) + tk.MustExec("analyze table t partition p0 columns a") + tk.MustExec("analyze table t partition p1 columns a") + tk.MustExec("set @@session.tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t partition p0") + tbl := h.GetTableStats(tableInfo) + require.Equal(t, int64(6), tbl.Columns[tableInfo.Columns[0].ID].Histogram.NDV) +} + +func TestAutoAnalyzeAwareGlobalVariableChange(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select @@global.tidb_enable_analyze_snapshot").Check(testkit.Rows("0")) + // We want to test that HandleAutoAnalyze is aware of setting @@global.tidb_enable_analyze_snapshot to 1 and reads data from snapshot. + tk.MustExec("set @@global.tidb_enable_analyze_snapshot = 1") + tk.MustExec("set @@global.tidb_analyze_version = 2") + tk.MustExec("create table t(a int)") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tid := tbl.Meta().ID + tk.MustExec("insert into t values(1),(2),(3)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + err = h.Update(dom.InfoSchema()) + require.NoError(t, err) + tk.MustExec("analyze table t") + tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( + "3 0", + )) + + originalVal1 := autoanalyze.AutoAnalyzeMinCnt + originalVal2 := tk.MustQuery("select @@global.tidb_auto_analyze_ratio").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + tk.MustExec("set global tidb_auto_analyze_ratio = 0.001") + defer func() { + autoanalyze.AutoAnalyzeMinCnt = originalVal1 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_ratio = %v", originalVal2)) + }() + + tk.MustExec("begin") + txn, err := tk.Session().Txn(false) + require.NoError(t, err) + startTS := txn.StartTS() + tk.MustExec("commit") + + tk.MustExec("insert into t values(4),(5),(6)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + err = h.Update(dom.InfoSchema()) + require.NoError(t, err) + + // Simulate that the analyze would start before and finish after the second insert. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectBaseCount", "return(3)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectBaseModifyCount", "return(0)")) + require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) + // Check the count / modify_count changes during the analyze are not lost. + tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( + "6 3", + )) + // Check the histogram is correct for the snapshot analyze. + tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( + "3", + )) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectBaseCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectBaseModifyCount")) +} + +func TestAnalyzeColumnsSkipMVIndexJsonCol(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("create table t (a int, b int, c json, index idx_b(b), index idx_c((cast(json_extract(c, _utf8mb4'$') as char(32) array))))") + tk.MustExec(`insert into t values (1, 1, '["a1", "a2"]'), (2, 2, '["b1", "b2"]'), (3, 3, '["c1", "c2"]'), (2, 2, '["c1", "c2"]')`) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + tk.MustExec("analyze table t columns a") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows(""+ + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Columns b are missing in ANALYZE but their stats are needed for calculating stats for indexes/primary key/extended stats", + "Warning 1105 analyzing multi-valued indexes is not supported, skip idx_c")) + tk.MustQuery("select job_info from mysql.analyze_jobs where table_schema = 'test' and table_name = 't'").Check(testkit.Rows( + "analyze table columns a, b with 256 buckets, 500 topn, 1 samplerate")) + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + stats := h.GetTableStats(tblInfo) + require.True(t, stats.Columns[tblInfo.Columns[0].ID].IsStatsInitialized()) + require.True(t, stats.Columns[tblInfo.Columns[1].ID].IsStatsInitialized()) + require.False(t, stats.Columns[tblInfo.Columns[2].ID].IsStatsInitialized()) + require.True(t, stats.Indices[tblInfo.Indices[0].ID].IsStatsInitialized()) + require.False(t, stats.Indices[tblInfo.Indices[1].ID].IsStatsInitialized()) +} + +func TestManualAnalyzeSkipColumnTypes(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c json, d text, e mediumtext, f blob, g mediumblob, index idx(d(10)))") + tk.MustExec("set @@session.tidb_analyze_skip_column_types = 'json,blob,mediumblob,text,mediumtext'") + tk.MustExec("analyze table t") + tk.MustQuery("select job_info from mysql.analyze_jobs where job_info like '%analyze table%'").Check(testkit.Rows("analyze table columns a, b, d with 256 buckets, 500 topn, 1 samplerate")) + tk.MustExec("delete from mysql.analyze_jobs") + tk.MustExec("analyze table t columns a, e") + tk.MustQuery("select job_info from mysql.analyze_jobs where job_info like '%analyze table%'").Check(testkit.Rows("analyze table columns a, d with 256 buckets, 500 topn, 1 samplerate")) +} + +// TestAnalyzeMVIndex tests analyzing the mv index use some real data in the table. +// It checks the analyze jobs, async loading and the stats content in the memory. +func TestAnalyzeMVIndex(t *testing.T) { + t.Skip() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/DebugAnalyzeJobOperations", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/statistics/handle/DebugAnalyzeJobOperations", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/DebugAnalyzeJobOperations")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/statistics/handle/DebugAnalyzeJobOperations")) + }() + // 1. prepare the table and insert data + store, dom := testkit.CreateMockStoreAndDomain(t) + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { + h.SetLease(oriLease) + }() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, j json, index ia(a)," + + "index ij_signed((cast(j->'$.signed' as signed array)))," + + "index ij_unsigned((cast(j->'$.unsigned' as unsigned array)))," + + // date currently incompatible with mysql + //"index ij_date((cast(j->'$.dt' as date array)))," + + // datetime currently incompatible with mysql + //"index ij_datetime((cast(j->'$.dttm' as datetime(6) array)))," + + // time currently incompatible with mysql + //"index ij_time((cast(j->'$.tm' as time(6) array)))," + + "index ij_double((cast(j->'$.dbl' as double array)))," + + // decimal not supported yet + //"index ij_decimal((cast(j->'$.dcm' as decimal(15,5) array)))," + + "index ij_binary((cast(j->'$.bin' as binary(50) array)))," + + "index ij_char((cast(j->'$.char' as char(50) array)))" + + ")") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + jsonData := []map[string]interface{}{ + { + "signed": []int64{1, 2, 300, 300, 0, 4, 5, -40000}, + "unsigned": []uint64{0, 3, 4, 600, 12}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16"}, + "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, -1000.56789}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "yuiop", "1234", "5678", "0000", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, + }, + { + "signed": []int64{1, 2, 300, 300, 0, 4, 5, -40000}, + "unsigned": []uint64{0, 3, 4, 600, 12}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10"}, + "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "ghjk", "0000", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, + }, + { + "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, + "unsigned": []uint64{0, 3, 4, 600, 3112}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2340-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, + "dbl": []float64{-21.5, 2.15, 10.555555, -12.000005, 0.00, 10.9876}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "asdf", "qwer", "1234", "0000", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "qwer", "yuiop", "!@#$"}, + }, + { + "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, + "unsigned": []uint64{0, 3, 4, 600, 3112}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2110-01-01 18:17:16", "2340-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, + "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, -123.654}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "0000", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "k!@cvd", "yuiop", "%*$%#@qwe"}, + }, + { + "signed": []int64{1, 2, 300, -300, 0, 100, -5, 13245}, + "unsigned": []uint64{0, 3, 4, 600, 3112}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2110-01-01 22:17:16", "2340-01-22 15:16:17", "1950-01-01 00:12:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:5.10", "12:4:43"}, + "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "3796", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "kicvd", "yuiop", "%*asdf@"}, + }, + { + "signed": []int64{1, 2, 300, 300, 0, 4, -5, 13245}, + "unsigned": []uint64{0, 3, 4, 600, 3112}, + "dt": []string{"2020-01-23", "2021-03-21", "2011-11-11", "2015-06-18", "1990-03-21", "2050-12-12"}, + "dttm": []string{"2021-01-11 12:00:00.123456", "2025-05-15 15:50:00.5", "2020-01-01 18:17:16.555", "2100-01-01 15:16:17", "1950-01-01 00:00:00.00008"}, + "tm": []string{"100:00:30.5", "-321:00:01.16", "09:11:47", "8:50.10", "1:10:43"}, + "dbl": []float64{-21.5, 2.15, 10.555555, 0.000005, 0.00, 10.9876}, + "dcm": []float64{1.1, 2.2, 10.1234, -12.34, 987.654}, + "bin": []string{"aaaaaa", "bbbb", "ppp", "ccc", "egfb", "nfre", "1234", "0000", "zzzz"}, + "char": []string{"aaa", "cccccc", "eee", "asdf", "k!@cvd", "yuiop", "%*$%#@qwe"}, + }, + } + for i := 0; i < 3; i++ { + jsonValue := jsonData[i] + jsonValueStr, err := json.Marshal(jsonValue) + require.NoError(t, err) + tk.MustExec(fmt.Sprintf("insert into t values (%d, '%s')", 1, jsonValueStr)) + } + tk.MustExec("insert into t select * from t") + tk.MustExec("insert into t select * from t") + tk.MustExec("insert into t select * from t") + for i := 3; i < 6; i++ { + jsonValue := jsonData[i] + jsonValueStr, err := json.Marshal(jsonValue) + require.NoError(t, err) + tk.MustExec(fmt.Sprintf("insert into t values (%d, '%s')", 1, jsonValueStr)) + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + + // 2. analyze and check analyze jobs + tk.MustExec("analyze table t with 1 samplerate, 3 topn") + tk.MustQuery("select id, table_schema, table_name, partition_name, job_info, processed_rows, state from mysql.analyze_jobs order by id"). + Check(testkit.Rows("1 test t analyze table columns a with 256 buckets, 3 topn, 1 samplerate 27 finished", + "2 test t analyze index ij_signed 190 finished", + "3 test t analyze index ij_unsigned 135 finished", + "4 test t analyze index ij_double 154 finished", + "5 test t analyze index ij_binary 259 finished", + "6 test t analyze index ij_char 189 finished", + )) + + // 3. test stats loading + // 3.1. turn off sync loading, stats on all indexes should be allEvicted, but these queries should trigger async loading + tk.MustExec("set session tidb_stats_load_sync_wait = 0") + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, ij_signed:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_signed:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, ij_unsigned:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_unsigned:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where 10.01 member of (j->'$.dbl')").Check(testkit.Rows( + "TableReader 21.60 root data:Selection", + "└─Selection 21.60 cop[tikv] json_memberof(cast(10.01, json BINARY), json_extract(test.t.j, \"$.dbl\"))", + " └─TableFullScan 27.00 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, ij_binary:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_binary:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, ij_char:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, ij_char:allEvicted, j:unInitialized]", + )) + // 3.2. emulate the background async loading + require.NoError(t, h.LoadNeededHistograms()) + // 3.3. now, stats on all indexes should be loaded + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where 10.01 member of (j->'$.dbl')").Check(testkit.Rows( + "TableReader 21.60 root data:Selection", + "└─Selection 21.60 cop[tikv] json_memberof(cast(10.01, json BINARY), json_extract(test.t.j, \"$.dbl\"))", + " └─TableFullScan 27.00 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[j:unInitialized]", + )) + + // 3.4. clean up the stats and re-analyze the table + tk.MustExec("drop stats t") + tk.MustExec("analyze table t with 1 samplerate, 3 topn") + // 3.5. turn on the sync loading, stats on mv indexes should be loaded + tk.MustExec("set session tidb_stats_load_sync_wait = 1000") + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.signed')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_signed(cast(json_extract(`j`, _utf8mb4'$.signed') as signed array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where 1 member of (j->'$.unsigned')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_unsigned(cast(json_extract(`j`, _utf8mb4'$.unsigned') as unsigned array)) range:[1,1], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.bin')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_binary(cast(json_extract(`j`, _utf8mb4'$.bin') as binary(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + )) + tk.MustQuery("explain format = brief select * from t where '1' member of (j->'$.char')").Check(testkit.Rows( + "IndexMerge 0.03 root type: union", + "├─IndexRangeScan(Build) 0.03 cop[tikv] table:t, index:ij_char(cast(json_extract(`j`, _utf8mb4'$.char') as char(50) array)) range:[0x31,0x31], keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + "└─TableRowIDScan(Probe) 0.03 cop[tikv] table:t keep order:false, stats:partial[ia:allEvicted, j:unInitialized]", + )) + + // 4. check stats content in the memory + require.NoError(t, h.LoadNeededHistograms()) + tk.MustQuery("show stats_meta").CheckAt([]int{0, 1, 4, 5}, testkit.Rows("test t 0 27")) + tk.MustQuery("show stats_histograms").CheckAt([]int{0, 1, 3, 4, 6, 7, 8, 9, 10}, testkit.Rows( + // db_name, table_name, column_name, is_index, distinct_count, null_count, avg_col_size, correlation, load_status + "test t a 0 1 0 1 1 allEvicted", + "test t ia 1 1 0 0 0 allLoaded", + "test t ij_signed 1 11 0 0 0 allLoaded", + "test t ij_unsigned 1 6 0 0 0 allLoaded", + "test t ij_double 1 7 0 0 0 allLoaded", + "test t ij_binary 1 15 0 0 0 allLoaded", + "test t ij_char 1 11 0 0 0 allLoaded", + )) + tk.MustQuery("show stats_topn").Check(testkit.Rows( + // db_name, table_name, partition_name, column_name, is_index, value, count + "test t ia 1 1 27", + "test t ij_signed 1 -40000 16", + "test t ij_signed 1 -300 1", + "test t ij_signed 1 -5 11", + "test t ij_unsigned 1 0 27", + "test t ij_unsigned 1 3 27", + "test t ij_unsigned 1 4 27", + "test t ij_double 1 -21.5 27", + "test t ij_double 1 -12.000005 8", + "test t ij_double 1 0 27", + "test t ij_binary 1 0000 26", + "test t ij_binary 1 1234 19", + "test t ij_binary 1 3796 1", + "test t ij_char 1 !@#$ 24", + "test t ij_char 1 %*$%#@qwe 2", + "test t ij_char 1 %*asdf@ 1", + )) + tk.MustQuery("show stats_buckets").Check(testkit.Rows( + // db_name, table_name, partition_name, column_name, is_index, bucket_id, count, repeats, lower_bound, upper_bound, ndv + "test t ij_signed 1 0 27 27 0 0 0", + "test t ij_signed 1 1 54 27 1 1 0", + "test t ij_signed 1 2 81 27 2 2 0", + "test t ij_signed 1 3 107 26 4 4 0", + "test t ij_signed 1 4 123 16 5 5 0", + "test t ij_signed 1 5 124 1 100 100 0", + "test t ij_signed 1 6 151 27 300 300 0", + "test t ij_signed 1 7 162 11 13245 13245 0", + "test t ij_unsigned 1 0 16 16 12 12 0", + "test t ij_unsigned 1 1 43 27 600 600 0", + "test t ij_unsigned 1 2 54 11 3112 3112 0", + "test t ij_double 1 0 19 19 0.000005 0.000005 0", + "test t ij_double 1 1 46 27 2.15 2.15 0", + "test t ij_double 1 2 73 27 10.555555 10.555555 0", + "test t ij_double 1 3 92 19 10.9876 10.9876 0", + "test t ij_binary 1 0 8 8 5678 5678 0", + "test t ij_binary 1 1 35 27 aaaaaa aaaaaa 0", + "test t ij_binary 1 2 59 24 asdf asdf 0", + "test t ij_binary 1 3 86 27 bbbb bbbb 0", + "test t ij_binary 1 4 113 27 ccc ccc 0", + "test t ij_binary 1 5 116 3 egfb egfb 0", + "test t ij_binary 1 6 124 8 ghjk ghjk 0", + "test t ij_binary 1 7 127 3 nfre nfre 0", + "test t ij_binary 1 8 154 27 ppp ppp 0", + "test t ij_binary 1 9 178 24 qwer qwer 0", + "test t ij_binary 1 10 186 8 yuiop yuiop 0", + "test t ij_binary 1 11 213 27 zzzz zzzz 0", + "test t ij_char 1 0 27 27 aaa aaa 0", + "test t ij_char 1 1 54 27 asdf asdf 0", + "test t ij_char 1 2 81 27 cccccc cccccc 0", + "test t ij_char 1 3 108 27 eee eee 0", + "test t ij_char 1 4 110 2 k!@cvd k!@cvd 0", + "test t ij_char 1 5 111 1 kicvd kicvd 0", + "test t ij_char 1 6 135 24 qwer qwer 0", + "test t ij_char 1 7 162 27 yuiop yuiop 0", + )) +} + +func TestAnalyzePartitionVerify(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + sql := "create table t(a int,b varchar(100),c int,INDEX idx_c(c)) PARTITION BY RANGE ( a ) (" + for n := 100; n < 1000; n = n + 100 { + sql += "PARTITION p" + fmt.Sprint(n) + " VALUES LESS THAN (" + fmt.Sprint(n) + ")," + } + sql += "PARTITION p" + fmt.Sprint(1000) + " VALUES LESS THAN MAXVALUE)" + tk.MustExec(sql) + // insert random data into table t + insertStr := "insert into t (a,b,c) values(0, 'abc', 0)" + for i := 1; i < 1000; i++ { + insertStr += fmt.Sprintf(" ,(%d, '%s', %d)", i, "abc", i) + } + insertStr += ";" + tk.MustExec(insertStr) + tk.MustExec("analyze table t") + + result := tk.MustQuery("show stats_histograms where Db_name='test'").Sort() + require.NotNil(t, result) + require.Len(t, result.Rows(), 4+4*10) // 4 columns * 10 partiion+ 4 global columns + for _, row := range result.Rows() { + if row[2] == "global" { + if row[3] == "b" { + // global column b has 1 distinct value + require.Equal(t, "1", row[6]) + } else { + require.Equal(t, "1000", row[6]) + } + } else { + if row[3] == "b" { + require.Equal(t, "1", row[6]) + } else { + require.Equal(t, "100", row[6]) + } + } + } +} diff --git a/pkg/executor/test/analyzetest/main_test.go b/pkg/executor/test/analyzetest/main_test.go new file mode 100644 index 0000000000000..1e2b58a7767d0 --- /dev/null +++ b/pkg/executor/test/analyzetest/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package analyzetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/analyzetest/memorycontrol/BUILD.bazel b/pkg/executor/test/analyzetest/memorycontrol/BUILD.bazel new file mode 100644 index 0000000000000..f06e27b983375 --- /dev/null +++ b/pkg/executor/test/analyzetest/memorycontrol/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "memorycontrol_test", + timeout = "short", + srcs = [ + "main_test.go", + "memory_control_test.go", + ], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/config", + "//pkg/executor", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle/autoanalyze", + "//pkg/testkit", + "//pkg/util", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/analyzetest/memorycontrol/main_test.go b/pkg/executor/test/analyzetest/memorycontrol/main_test.go new file mode 100644 index 0000000000000..9afe586d1eeb5 --- /dev/null +++ b/pkg/executor/test/analyzetest/memorycontrol/main_test.go @@ -0,0 +1,36 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package memorycontrol + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + variable.StatsCacheMemQuota.Store(1000000) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/analyzetest/memorycontrol/memory_control_test.go b/pkg/executor/test/analyzetest/memorycontrol/memory_control_test.go similarity index 80% rename from executor/test/analyzetest/memorycontrol/memory_control_test.go rename to pkg/executor/test/analyzetest/memorycontrol/memory_control_test.go index bacc1eaa0c569..59ad3717ef8e9 100644 --- a/executor/test/analyzetest/memorycontrol/memory_control_test.go +++ b/pkg/executor/test/analyzetest/memorycontrol/memory_control_test.go @@ -21,11 +21,11 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) @@ -50,13 +50,13 @@ func TestGlobalMemoryControlForAnalyze(t *testing.T) { tk0.MustExec("insert into t select * from t") // 256 Lines } sql := "analyze table t with 1.0 samplerate;" // Need about 100MB - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/memory/ReadMemStats", `return(536870912)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats", `return(536870912)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) _, err := tk0.Exec(sql) require.True(t, strings.Contains(err.Error(), memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForInstance)) runtime.GC() - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/memory/ReadMemStats")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume")) tk0.MustExec(sql) } @@ -82,9 +82,9 @@ func TestGlobalMemoryControlForPrepareAnalyze(t *testing.T) { tk0.MustExec("insert into t select * from t") // 256 Lines } sqlPrepare := "prepare stmt from 'analyze table t with 1.0 samplerate';" - sqlExecute := "execute stmt;" // Need about 100MB - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/memory/ReadMemStats", `return(536870912)`)) // 512MB - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) + sqlExecute := "execute stmt;" // Need about 100MB + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats", `return(536870912)`)) // 512MB + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) runtime.GC() // won't be killed by tidb_mem_quota_query tk0.MustExec(sqlPrepare) @@ -98,8 +98,8 @@ func TestGlobalMemoryControlForPrepareAnalyze(t *testing.T) { // Killed and the WarnMsg is WarnMsgSuffixForInstance instead of WarnMsgSuffixForSingleQuery require.True(t, strings.Contains(err1.Error(), memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForInstance)) runtime.GC() - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/memory/ReadMemStats")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume")) tk0.MustExec(sqlPrepare) tk0.MustExec(sqlExecute) } @@ -165,11 +165,11 @@ func TestGlobalMemoryControlForAutoAnalyze(t *testing.T) { err := h.Update(dom.InfoSchema()) require.NoError(t, err) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/memory/ReadMemStats", `return(536870912)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats", `return(536870912)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume", `return(100)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/memory/ReadMemStats")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockAnalyzeMergeWorkerSlowConsume")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/memory/ReadMemStats")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockAnalyzeMergeWorkerSlowConsume")) }() tk.MustQuery("select 1") childTrackers = executor.GlobalAnalyzeMemoryTracker.GetChildrenForTest() diff --git a/pkg/executor/test/autoidtest/BUILD.bazel b/pkg/executor/test/autoidtest/BUILD.bazel new file mode 100644 index 0000000000000..61c420b71bfff --- /dev/null +++ b/pkg/executor/test/autoidtest/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "autoidtest_test", + timeout = "moderate", + srcs = [ + "autoid_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 10, + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/ddl/testutil", + "//pkg/meta/autoid", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/autoidtest/autoid_test.go b/pkg/executor/test/autoidtest/autoid_test.go new file mode 100644 index 0000000000000..06bf8bc80c215 --- /dev/null +++ b/pkg/executor/test/autoidtest/autoid_test.go @@ -0,0 +1,808 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package autoid_test + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/pingcap/failpoint" + _ "github.com/pingcap/tidb/pkg/autoid_service" + ddltestutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/stretchr/testify/require" +) + +// Test filter different kind of allocators. +// In special ddl type, for example: +// 1: ActionRenameTable : it will abandon all the old allocators. +// 2: ActionRebaseAutoID : it will drop row-id-type allocator. +// 3: ActionModifyTableAutoIdCache : it will drop row-id-type allocator. +// 3: ActionRebaseAutoRandomBase : it will drop auto-rand-type allocator. +func TestFilterDifferentAllocators(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + + for _, str := range []string{"", " AUTO_ID_CACHE 1"} { + tk.MustExec("create table t(a bigint auto_random(5) key, b int auto_increment unique)" + str) + tk.MustExec("insert into t values()") + tk.MustQuery("select b from t").Check(testkit.Rows("1")) + allHandles, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") + require.NoError(t, err) + require.Equal(t, 1, len(allHandles)) + orderedHandles := testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + require.Equal(t, int64(1), orderedHandles[0]) + tk.MustExec("delete from t") + + // Test rebase auto_increment. + tk.MustExec("alter table t auto_increment 3000000") + tk.MustExec("insert into t values()") + tk.MustQuery("select b from t").Check(testkit.Rows("3000000")) + allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") + require.NoError(t, err) + require.Equal(t, 1, len(allHandles)) + orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + require.Equal(t, int64(2), orderedHandles[0]) + tk.MustExec("delete from t") + + // Test rebase auto_random. + tk.MustExec("alter table t auto_random_base 3000000") + tk.MustExec("insert into t values()") + tk.MustQuery("select b from t").Check(testkit.Rows("3000001")) + allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t") + require.NoError(t, err) + require.Equal(t, 1, len(allHandles)) + orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + require.Equal(t, int64(3000000), orderedHandles[0]) + tk.MustExec("delete from t") + + // Test rename table. + tk.MustExec("rename table t to t1") + tk.MustExec("insert into t1 values()") + res := tk.MustQuery("select b from t1") + strInt64, err := strconv.ParseInt(res.Rows()[0][0].(string), 10, 64) + require.NoError(t, err) + require.GreaterOrEqual(t, strInt64, int64(3000002)) + allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "t1") + require.NoError(t, err) + require.Equal(t, 1, len(allHandles)) + orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + require.Greater(t, orderedHandles[0], int64(3000001)) + + tk.MustExec("drop table t1") + } +} + +func TestAutoIncrementInsertMinMax(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + cases := []struct { + t string + s string + vals []int64 + expect [][]interface{} + }{ + {"tinyint", "signed", []int64{-128, 0, 127}, testkit.Rows("-128", "1", "2", "3", "127")}, + {"tinyint", "unsigned", []int64{0, 127, 255}, testkit.Rows("1", "2", "127", "128", "255")}, + {"smallint", "signed", []int64{-32768, 0, 32767}, testkit.Rows("-32768", "1", "2", "3", "32767")}, + {"smallint", "unsigned", []int64{0, 32767, 65535}, testkit.Rows("1", "2", "32767", "32768", "65535")}, + {"mediumint", "signed", []int64{-8388608, 0, 8388607}, testkit.Rows("-8388608", "1", "2", "3", "8388607")}, + {"mediumint", "unsigned", []int64{0, 8388607, 16777215}, testkit.Rows("1", "2", "8388607", "8388608", "16777215")}, + {"integer", "signed", []int64{-2147483648, 0, 2147483647}, testkit.Rows("-2147483648", "1", "2", "3", "2147483647")}, + {"integer", "unsigned", []int64{0, 2147483647, 4294967295}, testkit.Rows("1", "2", "2147483647", "2147483648", "4294967295")}, + {"bigint", "signed", []int64{-9223372036854775808, 0, 9223372036854775807}, testkit.Rows("-9223372036854775808", "1", "2", "3", "9223372036854775807")}, + {"bigint", "unsigned", []int64{0, 9223372036854775807}, testkit.Rows("1", "2", "9223372036854775807", "9223372036854775808")}, + } + + for _, option := range []string{"", "auto_id_cache 1", "auto_id_cache 100"} { + for idx, c := range cases { + sql := fmt.Sprintf("create table t%d (a %s %s key auto_increment) %s", idx, c.t, c.s, option) + tk.MustExec(sql) + + for _, val := range c.vals { + tk.MustExec(fmt.Sprintf("insert into t%d values (%d)", idx, val)) + tk.Exec(fmt.Sprintf("insert into t%d values ()", idx)) // ignore error + } + + tk.MustQuery(fmt.Sprintf("select * from t%d order by a", idx)).Check(c.expect) + + tk.MustExec(fmt.Sprintf("drop table t%d", idx)) + } + } + + tk.MustExec("create table t10 (a integer key auto_increment) auto_id_cache 1") + err := tk.ExecToErr("insert into t10 values (2147483648)") + require.Error(t, err) + err = tk.ExecToErr("insert into t10 values (-2147483649)") + require.Error(t, err) +} + +func TestInsertWithAutoidSchema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`create table t1(id int primary key auto_increment, n int);`) + tk.MustExec(`create table t2(id int unsigned primary key auto_increment, n int);`) + tk.MustExec(`create table t3(id tinyint primary key auto_increment, n int);`) + tk.MustExec(`create table t4(id int primary key, n float auto_increment, key I_n(n));`) + tk.MustExec(`create table t5(id int primary key, n float unsigned auto_increment, key I_n(n));`) + tk.MustExec(`create table t6(id int primary key, n double auto_increment, key I_n(n));`) + tk.MustExec(`create table t7(id int primary key, n double unsigned auto_increment, key I_n(n));`) + // test for inserting multiple values + tk.MustExec(`create table t8(id int primary key auto_increment, n int);`) + + testInsertWithAutoidSchema(t, tk) +} + +func TestInsertWithAutoidSchemaCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`create table t1(id int primary key auto_increment, n int) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t2(id int unsigned primary key auto_increment, n int) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t3(id tinyint primary key auto_increment, n int) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t4(id int primary key, n float auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t5(id int primary key, n float unsigned auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t6(id int primary key, n double auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) + tk.MustExec(`create table t7(id int primary key, n double unsigned auto_increment, key I_n(n)) AUTO_ID_CACHE 1;`) + // test for inserting multiple values + tk.MustExec(`create table t8(id int primary key auto_increment, n int);`) + + testInsertWithAutoidSchema(t, tk) +} + +func testInsertWithAutoidSchema(t *testing.T, tk *testkit.TestKit) { + tests := []struct { + insert string + query string + result [][]interface{} + }{ + { + `insert into t1(id, n) values(1, 1)`, + `select * from t1 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t1(n) values(2)`, + `select * from t1 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t1(n) values(3)`, + `select * from t1 where id = 3`, + testkit.Rows(`3 3`), + }, + { + `insert into t1(id, n) values(-1, 4)`, + `select * from t1 where id = -1`, + testkit.Rows(`-1 4`), + }, + { + `insert into t1(n) values(5)`, + `select * from t1 where id = 4`, + testkit.Rows(`4 5`), + }, + { + `insert into t1(id, n) values('5', 6)`, + `select * from t1 where id = 5`, + testkit.Rows(`5 6`), + }, + { + `insert into t1(n) values(7)`, + `select * from t1 where id = 6`, + testkit.Rows(`6 7`), + }, + { + `insert into t1(id, n) values(7.4, 8)`, + `select * from t1 where id = 7`, + testkit.Rows(`7 8`), + }, + { + `insert into t1(id, n) values(7.5, 9)`, + `select * from t1 where id = 8`, + testkit.Rows(`8 9`), + }, + { + `insert into t1(n) values(9)`, + `select * from t1 where id = 9`, + testkit.Rows(`9 9`), + }, + // test last insert id + { + `insert into t1 values(3000, -1), (null, -2)`, + `select * from t1 where id = 3000`, + testkit.Rows(`3000 -1`), + }, + { + `;`, + `select * from t1 where id = 3001`, + testkit.Rows(`3001 -2`), + }, + { + `;`, + `select last_insert_id()`, + testkit.Rows(`3001`), + }, + { + `insert into t2(id, n) values(1, 1)`, + `select * from t2 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t2(n) values(2)`, + `select * from t2 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t2(n) values(3)`, + `select * from t2 where id = 3`, + testkit.Rows(`3 3`), + }, + { + `insert into t3(id, n) values(1, 1)`, + `select * from t3 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t3(n) values(2)`, + `select * from t3 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t3(n) values(3)`, + `select * from t3 where id = 3`, + testkit.Rows(`3 3`), + }, + { + `insert into t3(id, n) values(-1, 4)`, + `select * from t3 where id = -1`, + testkit.Rows(`-1 4`), + }, + { + `insert into t3(n) values(5)`, + `select * from t3 where id = 4`, + testkit.Rows(`4 5`), + }, + { + `insert into t4(id, n) values(1, 1)`, + `select * from t4 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t4(id) values(2)`, + `select * from t4 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t4(id, n) values(3, -1)`, + `select * from t4 where id = 3`, + testkit.Rows(`3 -1`), + }, + { + `insert into t4(id) values(4)`, + `select * from t4 where id = 4`, + testkit.Rows(`4 3`), + }, + { + `insert into t4(id, n) values(5, 5.5)`, + `select * from t4 where id = 5`, + testkit.Rows(`5 5.5`), + }, + { + `insert into t4(id) values(6)`, + `select * from t4 where id = 6`, + testkit.Rows(`6 7`), + }, + { + `insert into t4(id, n) values(7, '7.7')`, + `select * from t4 where id = 7`, + testkit.Rows(`7 7.7`), + }, + { + `insert into t4(id) values(8)`, + `select * from t4 where id = 8`, + testkit.Rows(`8 9`), + }, + { + `insert into t4(id, n) values(9, 10.4)`, + `select * from t4 where id = 9`, + testkit.Rows(`9 10.4`), + }, + { + `insert into t4(id) values(10)`, + `select * from t4 where id = 10`, + testkit.Rows(`10 11`), + }, + { + `insert into t5(id, n) values(1, 1)`, + `select * from t5 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t5(id) values(2)`, + `select * from t5 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t5(id) values(3)`, + `select * from t5 where id = 3`, + testkit.Rows(`3 3`), + }, + { + `insert into t6(id, n) values(1, 1)`, + `select * from t6 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t6(id) values(2)`, + `select * from t6 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t6(id, n) values(3, -1)`, + `select * from t6 where id = 3`, + testkit.Rows(`3 -1`), + }, + { + `insert into t6(id) values(4)`, + `select * from t6 where id = 4`, + testkit.Rows(`4 3`), + }, + { + `insert into t6(id, n) values(5, 5.5)`, + `select * from t6 where id = 5`, + testkit.Rows(`5 5.5`), + }, + { + `insert into t6(id) values(6)`, + `select * from t6 where id = 6`, + testkit.Rows(`6 7`), + }, + { + `insert into t6(id, n) values(7, '7.7')`, + `select * from t4 where id = 7`, + testkit.Rows(`7 7.7`), + }, + { + `insert into t6(id) values(8)`, + `select * from t4 where id = 8`, + testkit.Rows(`8 9`), + }, + { + `insert into t6(id, n) values(9, 10.4)`, + `select * from t6 where id = 9`, + testkit.Rows(`9 10.4`), + }, + { + `insert into t6(id) values(10)`, + `select * from t6 where id = 10`, + testkit.Rows(`10 11`), + }, + { + `insert into t7(id, n) values(1, 1)`, + `select * from t7 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `insert into t7(id) values(2)`, + `select * from t7 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `insert into t7(id) values(3)`, + `select * from t7 where id = 3`, + testkit.Rows(`3 3`), + }, + + // the following is test for insert multiple values. + { + `insert into t8(n) values(1),(2)`, + `select * from t8 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `;`, + `select * from t8 where id = 2`, + testkit.Rows(`2 2`), + }, + { + `;`, + `select last_insert_id();`, + testkit.Rows(`1`), + }, + // test user rebase and auto alloc mixture. + { + `insert into t8 values(null, 3),(-1, -1),(null,4),(null, 5)`, + `select * from t8 where id = 3`, + testkit.Rows(`3 3`), + }, + // -1 won't rebase allocator here cause -1 < base. + { + `;`, + `select * from t8 where id = -1`, + testkit.Rows(`-1 -1`), + }, + { + `;`, + `select * from t8 where id = 4`, + testkit.Rows(`4 4`), + }, + { + `;`, + `select * from t8 where id = 5`, + testkit.Rows(`5 5`), + }, + { + `;`, + `select last_insert_id();`, + testkit.Rows(`3`), + }, + { + `insert into t8 values(null, 6),(10, 7),(null, 8)`, + `select * from t8 where id = 6`, + testkit.Rows(`6 6`), + }, + // 10 will rebase allocator here. + { + `;`, + `select * from t8 where id = 10`, + testkit.Rows(`10 7`), + }, + { + `;`, + `select * from t8 where id = 11`, + testkit.Rows(`11 8`), + }, + { + `;`, + `select last_insert_id()`, + testkit.Rows(`6`), + }, + // fix bug for last_insert_id should be first allocated id in insert rows (skip the rebase id). + { + `insert into t8 values(100, 9),(null,10),(null,11)`, + `select * from t8 where id = 100`, + testkit.Rows(`100 9`), + }, + { + `;`, + `select * from t8 where id = 101`, + testkit.Rows(`101 10`), + }, + { + `;`, + `select * from t8 where id = 102`, + testkit.Rows(`102 11`), + }, + { + `;`, + `select last_insert_id()`, + testkit.Rows(`101`), + }, + // test with sql_mode: NO_AUTO_VALUE_ON_ZERO. + { + `;`, + `select @@sql_mode`, + testkit.Rows(`ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`), + }, + { + `;`, + "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO`", + nil, + }, + { + `insert into t8 values (0, 12), (null, 13)`, + `select * from t8 where id = 0`, + testkit.Rows(`0 12`), + }, + { + `;`, + `select * from t8 where id = 103`, + testkit.Rows(`103 13`), + }, + { + `;`, + `select last_insert_id()`, + testkit.Rows(`103`), + }, + // test without sql_mode: NO_AUTO_VALUE_ON_ZERO. + { + `;`, + "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`", + nil, + }, + // value 0 will be substitute by autoid. + { + `insert into t8 values (0, 14), (null, 15)`, + `select * from t8 where id = 104`, + testkit.Rows(`104 14`), + }, + { + `;`, + `select * from t8 where id = 105`, + testkit.Rows(`105 15`), + }, + { + `;`, + `select last_insert_id()`, + testkit.Rows(`104`), + }, + // last test : auto increment allocation can find in retryInfo. + { + `retry : insert into t8 values (null, 16), (null, 17)`, + `select * from t8 where id = 1000`, + testkit.Rows(`1000 16`), + }, + { + `;`, + `select * from t8 where id = 1001`, + testkit.Rows(`1001 17`), + }, + { + `;`, + `select last_insert_id()`, + // this insert doesn't has the last_insert_id, should be same as the last insert case. + testkit.Rows(`104`), + }, + } + + for _, tt := range tests { + if strings.HasPrefix(tt.insert, "retry : ") { + // it's the last retry insert case, change the sessionVars. + retryInfo := &variable.RetryInfo{Retrying: true} + retryInfo.AddAutoIncrementID(1000) + retryInfo.AddAutoIncrementID(1001) + tk.Session().GetSessionVars().RetryInfo = retryInfo + tk.MustExec(tt.insert[8:]) + tk.Session().GetSessionVars().RetryInfo = &variable.RetryInfo{} + } else { + tk.MustExec(tt.insert) + } + if tt.query == "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_AUTO_VALUE_ON_ZERO`" || + tt.query == "set session sql_mode = `ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION`" { + tk.MustExec(tt.query) + } else { + tk.MustQuery(tt.query).Check(tt.result) + } + } +} + +// TestAutoIDIncrementAndOffset There is a potential issue in MySQL: when the value of auto_increment_offset is greater +// than that of auto_increment_increment, the value of auto_increment_offset is ignored +// (https://dev.mysql.com/doc/refman/8.0/en/replication-options-master.html#sysvar_auto_increment_increment), +// This issue is a flaw of the implementation of MySQL and it doesn't exist in TiDB. +func TestAutoIDIncrementAndOffset(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + // Test for offset is larger than increment. + tk.Session().GetSessionVars().AutoIncrementIncrement = 5 + tk.Session().GetSessionVars().AutoIncrementOffset = 10 + + for _, str := range []string{"", " AUTO_ID_CACHE 1"} { + tk.MustExec(`create table io (a int key auto_increment)` + str) + tk.MustExec(`insert into io values (null),(null),(null)`) + tk.MustQuery(`select * from io`).Check(testkit.Rows("10", "15", "20")) + tk.MustExec(`drop table io`) + } + + // Test handle is PK. + for _, str := range []string{"", " AUTO_ID_CACHE 1"} { + tk.MustExec(`create table io (a int key auto_increment)` + str) + tk.Session().GetSessionVars().AutoIncrementOffset = 10 + tk.Session().GetSessionVars().AutoIncrementIncrement = 2 + tk.MustExec(`insert into io values (),(),()`) + tk.MustQuery(`select * from io`).Check(testkit.Rows("10", "12", "14")) + tk.MustExec(`delete from io`) + + // Test reset the increment. + tk.Session().GetSessionVars().AutoIncrementIncrement = 5 + tk.MustExec(`insert into io values (),(),()`) + tk.MustQuery(`select * from io`).Check(testkit.Rows("15", "20", "25")) + tk.MustExec(`delete from io`) + + tk.Session().GetSessionVars().AutoIncrementIncrement = 10 + tk.MustExec(`insert into io values (),(),()`) + tk.MustQuery(`select * from io`).Check(testkit.Rows("30", "40", "50")) + tk.MustExec(`delete from io`) + + tk.Session().GetSessionVars().AutoIncrementIncrement = 5 + tk.MustExec(`insert into io values (),(),()`) + tk.MustQuery(`select * from io`).Check(testkit.Rows("55", "60", "65")) + tk.MustExec(`drop table io`) + } + + // Test handle is not PK. + for _, str := range []string{"", " AUTO_ID_CACHE 1"} { + tk.Session().GetSessionVars().AutoIncrementIncrement = 2 + tk.Session().GetSessionVars().AutoIncrementOffset = 10 + tk.MustExec(`create table io (a int, b int auto_increment, key(b))` + str) + tk.MustExec(`insert into io(b) values (null),(null),(null)`) + // AutoID allocation will take increment and offset into consideration. + tk.MustQuery(`select b from io`).Check(testkit.Rows("10", "12", "14")) + if str == "" { + // HandleID allocation will ignore the increment and offset. + tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("15", "16", "17")) + } else { + // Separate row id and auto inc id, increment and offset works on auto inc id + tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("1", "2", "3")) + } + tk.MustExec(`delete from io`) + + tk.Session().GetSessionVars().AutoIncrementIncrement = 10 + tk.MustExec(`insert into io(b) values (null),(null),(null)`) + tk.MustQuery(`select b from io`).Check(testkit.Rows("20", "30", "40")) + if str == "" { + tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("41", "42", "43")) + } else { + tk.MustQuery(`select _tidb_rowid from io`).Check(testkit.Rows("4", "5", "6")) + } + + // Test invalid value. + tk.Session().GetSessionVars().AutoIncrementIncrement = -1 + tk.Session().GetSessionVars().AutoIncrementOffset = -2 + tk.MustGetErrMsg(`insert into io(b) values (null),(null),(null)`, + "[autoid:8060]Invalid auto_increment settings: auto_increment_increment: -1, auto_increment_offset: -2, both of them must be in range [1..65535]") + tk.MustExec(`delete from io`) + + tk.Session().GetSessionVars().AutoIncrementIncrement = 65536 + tk.Session().GetSessionVars().AutoIncrementOffset = 65536 + tk.MustGetErrMsg(`insert into io(b) values (null),(null),(null)`, + "[autoid:8060]Invalid auto_increment settings: auto_increment_increment: 65536, auto_increment_offset: 65536, both of them must be in range [1..65535]") + + tk.MustExec(`drop table io`) + } +} + +func TestRenameTableForAutoIncrement(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + tk.MustExec("drop table if exists t1, t2, t3;") + tk.MustExec("create table t1 (id int key auto_increment);") + tk.MustExec("insert into t1 values ()") + tk.MustExec("rename table t1 to t11") + tk.MustExec("insert into t11 values ()") + // TODO(tiancaiamao): fix bug and uncomment here, rename table should not discard the cached AUTO_ID. + // tk.MustQuery("select * from t11").Check(testkit.Rows("1", "2")) + + // auto_id_cache 1 use another implementation and do not have such bug. + tk.MustExec("create table t2 (id int key auto_increment) auto_id_cache 1;") + tk.MustExec("insert into t2 values ()") + tk.MustExec("rename table t2 to t22") + tk.MustExec("insert into t22 values ()") + tk.MustQuery("select * from t22").Check(testkit.Rows("1", "2")) + + tk.MustExec("create table t3 (id int key auto_increment) auto_id_cache 100;") + tk.MustExec("insert into t3 values ()") + tk.MustExec("rename table t3 to t33") + tk.MustExec("insert into t33 values ()") + // TODO(tiancaiamao): fix bug and uncomment here, rename table should not discard the cached AUTO_ID. + // tk.MustQuery("select * from t33").Check(testkit.Rows("1", "2")) +} + +func TestAlterTableAutoIDCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + tk.MustExec("drop table if exists t_473;") + tk.MustExec("create table t_473 (id int key auto_increment)") + tk.MustExec("insert into t_473 values ()") + tk.MustQuery("select * from t_473").Check(testkit.Rows("1")) + rs, err := tk.Exec("show table t_473 next_row_id") + require.NoError(t, err) + rows, err1 := session.ResultSetToStringSlice(context.Background(), tk.Session(), rs) + require.NoError(t, err1) + // "test t_473 id 1013608 AUTO_INCREMENT" + val, err2 := strconv.ParseUint(rows[0][3], 10, 64) + require.NoError(t, err2) + + tk.MustExec("alter table t_473 auto_id_cache = 100") + tk.MustQuery("show table t_473 next_row_id").Check(testkit.Rows( + fmt.Sprintf("test t_473 id %d _TIDB_ROWID", val), + "test t_473 id 1 AUTO_INCREMENT", + )) + tk.MustExec("insert into t_473 values ()") + tk.MustQuery("select * from t_473").Check(testkit.Rows("1", fmt.Sprintf("%d", val))) + tk.MustQuery("show table t_473 next_row_id").Check(testkit.Rows( + fmt.Sprintf("test t_473 id %d _TIDB_ROWID", val+100), + "test t_473 id 1 AUTO_INCREMENT", + )) + + // Note that auto_id_cache=1 use a different implementation, switch between them is not allowed. + // TODO: relax this restriction and update the test case. + tk.MustExecToErr("alter table t_473 auto_id_cache = 1") +} + +func TestMockAutoIDServiceError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + tk.MustExec("create table t_mock_err (id int key auto_increment) auto_id_cache 1") + + failpoint.Enable("github.com/pingcap/tidb/pkg/autoid_service/mockErr", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/autoid_service/mockErr") + // Cover a bug that the autoid client retry non-retryable errors forever cause dead loop. + tk.MustExecToErr("insert into t_mock_err values (),()") // mock error, instead of dead loop +} + +func TestIssue39528(t *testing.T) { + // When AUTO_ID_CACHE is 1, it should not affect row id setting when autoid and rowid are separated. + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("create table issue39528 (id int unsigned key nonclustered auto_increment) shard_row_id_bits=4 auto_id_cache 1;") + tk.MustExec("insert into issue39528 values ()") + tk.MustExec("insert into issue39528 values ()") + + ctx := context.Background() + var codeRun bool + ctx = context.WithValue(ctx, "testIssue39528", &codeRun) + _, err := tk.ExecWithContext(ctx, "insert into issue39528 values ()") + require.NoError(t, err) + // Make sure the code does not visit tikv on allocate path. + require.False(t, codeRun) +} + +func TestAutoIDConstraint(t *testing.T) { + // Remove the constraint that auto id column must be defined as a key + // See https://github.com/pingcap/tidb/issues/40580 + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + // Cover: create table with/without key constraint + template := `create table t%d (id int auto_increment,k int,c char(120)%s) %s` + keyDefs := []string{"", + ",PRIMARY KEY(k, id)", + ",key idx_1(id)", + ",PRIMARY KEY(`k`, `id`), key idx_1(id)", + } + engineDefs := []string{"", + "engine = MyISAM", + "engine = InnoDB", + "auto_id_cache 1", + "auto_id_cache 100", + } + i := 0 + for _, keyDef := range keyDefs { + for _, engineDef := range engineDefs { + tk.MustExec(fmt.Sprintf("drop table if exists t%d", i)) + sql := fmt.Sprintf(template, i, keyDef, engineDef) + tk.MustExec(sql) + i++ + } + } + + // alter table add auto id column is not supported, but cover it here to prevent regression + tk.MustExec("create table tt1 (id int)") + tk.MustExecToErr("alter table tt1 add column (c int auto_increment)") + + // Cover case: create table with auto id column as key, and remove it later + tk.MustExec("create table tt2 (id int, c int auto_increment, key c_idx(c))") + tk.MustExec("alter table tt2 drop index c_idx") +} diff --git a/pkg/executor/test/autoidtest/main_test.go b/pkg/executor/test/autoidtest/main_test.go new file mode 100644 index 0000000000000..f0dbd997a25ff --- /dev/null +++ b/pkg/executor/test/autoidtest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package autoid_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/ddl/BUILD.bazel b/pkg/executor/test/ddl/BUILD.bazel new file mode 100644 index 0000000000000..9ff2e29495c50 --- /dev/null +++ b/pkg/executor/test/ddl/BUILD.bazel @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "ddl_test", + timeout = "short", + srcs = [ + "ddl_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 27, + deps = [ + "//pkg/config", + "//pkg/ddl/schematracker", + "//pkg/ddl/testutil", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/errno", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/sessionctx/variable", + "//pkg/sessionctx/variable/featuretag/disttask", + "//pkg/sessiontxn", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/ddl/ddl_test.go b/pkg/executor/test/ddl/ddl_test.go new file mode 100644 index 0000000000000..2e1ed3c901cc1 --- /dev/null +++ b/pkg/executor/test/ddl/ddl_test.go @@ -0,0 +1,1378 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "context" + "fmt" + "math" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + ddltestutil "github.com/pingcap/tidb/pkg/ddl/testutil" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessionctx/variable/featuretag/disttask" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/stretchr/testify/require" +) + +// TestInTxnExecDDLFail tests the following case: +// 1. Execute the SQL of "begin"; +// 2. A SQL that will fail to execute; +// 3. Execute DDL. +func TestInTxnExecDDLFail(t *testing.T) { + store := testkit.CreateMockStore(t) + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (i int key);") + tk.MustExec("insert into t values (1);") + tk.MustExec("begin;") + tk.MustExec("insert into t values (1);") + tk.MustGetErrMsg("truncate table t;", "[kv:1062]Duplicate entry '1' for key 't.PRIMARY'") + tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) +} + +func TestInTxnExecDDLInvalid(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (c_int int, c_str varchar(40));") + tk.MustExec("insert into t values (1, 'quizzical hofstadter');") + tk.MustExec("begin;") + _ = tk.MustQuery("select c_int from t where c_str is not null for update;") + tk.MustExec("alter table t add index idx_4 (c_str);") +} + +func TestCreateTable(t *testing.T) { + store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // Test create an exist database + tk.MustExecToErr("CREATE database test") + + // Test create an exist table + tk.MustExec("CREATE TABLE create_test (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") + tk.MustExecToErr("CREATE TABLE create_test (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") + + // Test "if not exist" + tk.MustExec("CREATE TABLE if not exists test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") + + // Testcase for https://github.com/pingcap/tidb/issues/312 + tk.MustExec(`create table issue312_1 (c float(24));`) + tk.MustExec(`create table issue312_2 (c float(25));`) + rs, err := tk.Exec(`desc issue312_1`) + require.NoError(t, err) + ctx := context.Background() + req := rs.NewChunk(nil) + it := chunk.NewIterator4Chunk(req) + for { + err1 := rs.Next(ctx, req) + require.NoError(t, err1) + if req.NumRows() == 0 { + break + } + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, "float", row.GetString(1)) + } + } + rs, err = tk.Exec(`desc issue312_2`) + require.NoError(t, err) + req = rs.NewChunk(nil) + it = chunk.NewIterator4Chunk(req) + for { + err1 := rs.Next(ctx, req) + require.NoError(t, err1) + if req.NumRows() == 0 { + break + } + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, "double", req.GetRow(0).GetString(1)) + } + } + require.NoError(t, rs.Close()) + + // test multiple collate specified in column when create. + tk.MustExec("drop table if exists test_multiple_column_collate;") + tk.MustExec("create table test_multiple_column_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("test_multiple_column_collate")) + require.NoError(t, err) + require.Equal(t, "utf8", tt.Cols()[0].GetCharset()) + require.Equal(t, "utf8_general_ci", tt.Cols()[0].GetCollate()) + require.Equal(t, "utf8mb4", tt.Meta().Charset) + require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) + + tk.MustExec("drop table if exists test_multiple_column_collate;") + tk.MustExec("create table test_multiple_column_collate (a char(1) charset utf8 collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("test_multiple_column_collate")) + require.NoError(t, err) + require.Equal(t, "utf8", tt.Cols()[0].GetCharset()) + require.Equal(t, "utf8_general_ci", tt.Cols()[0].GetCollate()) + require.Equal(t, "utf8mb4", tt.Meta().Charset) + require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) + + // test Err case for multiple collate specified in column when create. + tk.MustExec("drop table if exists test_err_multiple_collate;") + tk.MustGetErrMsg("create table test_err_multiple_collate (a char(1) charset utf8mb4 collate utf8_unicode_ci collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin", + dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8_unicode_ci", "utf8mb4").Error()) + + tk.MustExec("drop table if exists test_err_multiple_collate;") + tk.MustGetErrMsg("create table test_err_multiple_collate (a char(1) collate utf8_unicode_ci collate utf8mb4_general_ci) charset utf8mb4 collate utf8mb4_bin", + dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8mb4_general_ci", "utf8").Error()) + + // table option is auto-increment + tk.MustExec("drop table if exists create_auto_increment_test;") + tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), primary key(id)) auto_increment = 999;") + tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") + tk.MustExec("insert into create_auto_increment_test (name) values ('bb')") + tk.MustExec("insert into create_auto_increment_test (name) values ('cc')") + r := tk.MustQuery("select * from create_auto_increment_test;") + r.Check(testkit.Rows("999 aa", "1000 bb", "1001 cc")) + tk.MustExec("drop table create_auto_increment_test") + tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), primary key(id)) auto_increment = 1999;") + tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") + tk.MustExec("insert into create_auto_increment_test (name) values ('bb')") + tk.MustExec("insert into create_auto_increment_test (name) values ('cc')") + r = tk.MustQuery("select * from create_auto_increment_test;") + r.Check(testkit.Rows("1999 aa", "2000 bb", "2001 cc")) + tk.MustExec("drop table create_auto_increment_test") + tk.MustExec("create table create_auto_increment_test (id int not null auto_increment, name varchar(255), key(id)) auto_increment = 1000;") + tk.MustExec("insert into create_auto_increment_test (name) values ('aa')") + r = tk.MustQuery("select * from create_auto_increment_test;") + r.Check(testkit.Rows("1000 aa")) + + // Test for `drop table if exists`. + tk.MustExec("drop table if exists t_if_exists;") + tk.MustQuery("show warnings;").Check(testkit.Rows("Note 1051 Unknown table 'test.t_if_exists'")) + tk.MustExec("create table if not exists t1_if_exists(c int)") + tk.MustExec("drop table if exists t1_if_exists,t2_if_exists,t3_if_exists") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1051|Unknown table 'test.t2_if_exists'", "Note|1051|Unknown table 'test.t3_if_exists'")) +} + +func TestCreateView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // create an source table + tk.MustExec("CREATE TABLE source_table (id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") + // test create a exist view + tk.MustExec("CREATE VIEW view_t AS select id , name from source_table") + defer tk.MustExec("DROP VIEW IF EXISTS view_t") + tk.MustGetErrMsg("CREATE VIEW view_t AS select id , name from source_table", "[schema:1050]Table 'test.view_t' already exists") + // create view on nonexistent table + tk.MustGetErrMsg("create view v1 (c,d) as select a,b from t1", "[schema:1146]Table 'test.t1' doesn't exist") + // simple view + tk.MustExec("create table t1 (a int ,b int)") + tk.MustExec("insert into t1 values (1,2), (1,3), (2,4), (2,5), (3,10)") + // view with colList and SelectFieldExpr + tk.MustExec("create view v1 (c) as select b+1 from t1") + // view with SelectFieldExpr + tk.MustExec("create view v2 as select b+1 from t1") + // view with SelectFieldExpr and AsName + tk.MustExec("create view v3 as select b+1 as c from t1") + // view with colList , SelectField and AsName + tk.MustExec("create view v4 (c) as select b+1 as d from t1") + // view with select wild card + tk.MustExec("create view v5 as select * from t1") + tk.MustExec("create view v6 (c,d) as select * from t1") + tk.MustGetErrCode("create view v7 (c,d,e) as select * from t1", errno.ErrViewWrongList) + // drop multiple views in a statement + tk.MustExec("drop view v1,v2,v3,v4,v5,v6") + // view with variable + tk.MustExec("create view v1 (c,d) as select a,b+@@global.max_user_connections from t1") + tk.MustGetErrMsg("create view v1 (c,d) as select a,b from t1 where a = @@global.max_user_connections", "[schema:1050]Table 'test.v1' already exists") + tk.MustExec("drop view v1") + // view with different col counts + tk.MustGetErrCode("create view v1 (c,d,e) as select a,b from t1 ", errno.ErrViewWrongList) + tk.MustGetErrCode("create view v1 (c) as select a,b from t1 ", errno.ErrViewWrongList) + // view with or_replace flag + tk.MustExec("drop view if exists v1") + tk.MustExec("create view v1 (c,d) as select a,b from t1") + tk.MustExec("create or replace view v1 (c,d) as select a,b from t1 ") + tk.MustExec("create table if not exists t1 (a int ,b int)") + err := tk.ExecToErr("create or replace view t1 as select * from t1") + require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "t1", "VIEW").Error(), err.Error()) + // create view using prepare + tk.MustExec(`prepare stmt from "create view v10 (x) as select 1";`) + tk.MustExec("execute stmt") + + // create view on union + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("drop view if exists v") + tk.MustGetDBError("create view v as select * from t1 union select * from t2", infoschema.ErrTableNotExists) + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int, b int)") + tk.MustExec("insert into t1 values(1,2), (1,1), (1,2)") + tk.MustExec("insert into t2 values(1,1),(1,3)") + tk.MustExec("create definer='root'@'localhost' view v as select * from t1 union select * from t2") + tk.MustQuery("select * from v").Sort().Check(testkit.Rows("1 1", "1 2", "1 3")) + tk.MustExec("alter table t1 drop column a") + tk.MustGetDBError("select * from v", plannercore.ErrViewInvalid) + tk.MustExec("alter table t1 add column a int") + tk.MustQuery("select * from v").Sort().Check(testkit.Rows("1 1", "1 3", " 1", " 2")) + tk.MustExec("alter table t1 drop column a") + tk.MustExec("alter table t2 drop column b") + tk.MustGetDBError("select * from v", plannercore.ErrViewInvalid) + tk.MustExec("drop view v") + + tk.MustExec("create view v as (select * from t1)") + tk.MustExec("drop view v") + tk.MustExec("create view v as (select * from t1 union select * from t2)") + tk.MustExec("drop view v") + + // Test for `drop view if exists`. + tk.MustExec("drop view if exists v_if_exists;") + tk.MustQuery("show warnings;").Check(testkit.Rows("Note 1051 Unknown table 'test.v_if_exists'")) + tk.MustExec("create view v1_if_exists as (select * from t1)") + tk.MustExec("drop view if exists v1_if_exists,v2_if_exists,v3_if_exists") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1051|Unknown table 'test.v2_if_exists'", "Note|1051|Unknown table 'test.v3_if_exists'")) + + // Test for create nested view. + tk.MustExec("create table test_v_nested(a int)") + tk.MustExec("create definer='root'@'localhost' view v_nested as select * from test_v_nested") + tk.MustExec("create definer='root'@'localhost' view v_nested2 as select * from v_nested") + tk.MustGetDBError("create or replace definer='root'@'localhost' view v_nested as select * from v_nested2", plannercore.ErrNoSuchTable) + tk.MustExec("drop table test_v_nested") + tk.MustExec("drop view v_nested, v_nested2") + + // Refer https://github.com/pingcap/tidb/issues/25876 + err = tk.ExecToErr("create view v_stale as select * from source_table as of timestamp current_timestamp(3)") + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrViewInvalid), "err %s", err) + + // Refer https://github.com/pingcap/tidb/issues/32682 + tk.MustExec("drop view if exists v1,v2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("CREATE TABLE t1(a INT, b INT);") + err = tk.ExecToErr("CREATE DEFINER=1234567890abcdefGHIKL1234567890abcdefGHIKL@localhost VIEW v1 AS SELECT a FROM t1;") + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrWrongStringLength), "ERROR 1470 (HY000): String '1234567890abcdefGHIKL1234567890abcdefGHIKL' is too long for user name (should be no longer than 32)") + err = tk.ExecToErr("CREATE DEFINER=some_user_name@host_1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890X VIEW v2 AS SELECT b FROM t1;") + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrWrongStringLength), "ERROR 1470 (HY000): String 'host_1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij12345' is too long for host name (should be no longer than 255)") +} + +func TestCreateViewWithOverlongColName(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + defer tk.MustExec("drop table t") + tk.MustExec("create view v as select distinct'" + strings.Repeat("a", 65) + "', " + + "max('" + strings.Repeat("b", 65) + "'), " + + "'cccccccccc', '" + strings.Repeat("d", 65) + "';") + resultCreateStmt := "CREATE ALGORITHM=UNDEFINED DEFINER=``@`` SQL SECURITY DEFINER VIEW `v` (`name_exp_1`, `name_exp_2`, `cccccccccc`, `name_exp_4`) AS " + + "SELECT DISTINCT _UTF8MB4'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' AS `aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`," + + "MAX(_UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb') AS `max('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb')`," + + "_UTF8MB4'cccccccccc' AS `cccccccccc`,_UTF8MB4'ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd' AS `ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd`" + tk.MustQuery("select * from v") + tk.MustQuery("select name_exp_1, name_exp_2, cccccccccc, name_exp_4 from v") + tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) + tk.MustExec("drop view v;") + tk.MustExec(resultCreateStmt) + + tk.MustExec("drop view v ") + tk.MustExec("create definer='root'@'localhost' view v as select 'a', '" + strings.Repeat("b", 65) + "' from t " + + "union select '" + strings.Repeat("c", 65) + "', " + + "count(distinct '" + strings.Repeat("b", 65) + "', " + + "'c');") + resultCreateStmt = "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`a`, `name_exp_2`) AS " + + "SELECT _UTF8MB4'a' AS `a`,_UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' AS `bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb` FROM `test`.`t` " + + "UNION SELECT _UTF8MB4'ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc' AS `ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc`," + + "COUNT(DISTINCT _UTF8MB4'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', _UTF8MB4'c') AS `count(distinct 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb', 'c')`" + tk.MustQuery("select * from v") + tk.MustQuery("select a, name_exp_2 from v") + tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) + tk.MustExec("drop view v;") + tk.MustExec(resultCreateStmt) + + tk.MustExec("drop view v ") + tk.MustExec("create definer='root'@'localhost' view v as select 'a' as '" + strings.Repeat("b", 65) + "' from t;") + tk.MustQuery("select * from v") + tk.MustQuery("select name_exp_1 from v") + resultCreateStmt = "CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v` (`name_exp_1`) AS SELECT _UTF8MB4'a' AS `" + strings.Repeat("b", 65) + "` FROM `test`.`t`" + tk.MustQuery("show create view v").Check(testkit.Rows("v " + resultCreateStmt + " utf8mb4 utf8mb4_bin")) + tk.MustExec("drop view v;") + tk.MustExec(resultCreateStmt) + + tk.MustExec("drop view v ") + err := tk.ExecToErr("create view v(`" + strings.Repeat("b", 65) + "`) as select a from t;") + require.EqualError(t, err, "[ddl:1059]Identifier name 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb' is too long") +} + +func TestCreateDropDatabase(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t, mockstore.WithDDLChecker()) + + ddlChecker := dom.DDL().(*schematracker.Checker) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database if not exists drop_test;") + tk.MustExec("drop database if exists drop_test;") + tk.MustExec("create database drop_test;") + tk.MustExec("use drop_test;") + tk.MustExec("drop database drop_test;") + tk.MustGetDBError("drop table t;", plannercore.ErrNoDB) + tk.MustGetDBError("select * from t;", plannercore.ErrNoDB) + + tk.MustExecToErr("drop database mysql") + + tk.MustExec("create database charset_test charset ascii;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET ascii */", + )) + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test charset binary;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET binary */", + )) + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test collate utf8_general_ci;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci */", + )) + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test charset utf8 collate utf8_general_ci;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci */", + )) + tk.MustGetErrMsg("create database charset_test charset utf8 collate utf8mb4_unicode_ci;", "[ddl:1253]COLLATION 'utf8mb4_unicode_ci' is not valid for CHARACTER SET 'utf8'") + + // ddl.SchemaTracker will not respect session charset + ddlChecker.Disable() + + tk.MustExec("SET SESSION character_set_server='ascii'") + tk.MustExec("SET SESSION collation_server='ascii_bin'") + + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET ascii */", + )) + + ddlChecker.Enable() + + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test collate utf8mb4_general_ci;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */", + )) + + tk.MustExec("drop database charset_test;") + tk.MustExec("create database charset_test charset utf8mb4;") + tk.MustQuery("show create database charset_test;").Check(testkit.RowsWithSep("|", + "charset_test|CREATE DATABASE `charset_test` /*!40100 DEFAULT CHARACTER SET utf8mb4 */", + )) +} + +func TestCreateDropTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table if not exists drop_test (a int)") + tk.MustExec("drop table if exists drop_test") + tk.MustExec("create table drop_test (a int)") + tk.MustExec("drop table drop_test") + tk.MustExecToErr("drop table mysql.gc_delete_range") +} + +func TestCreateDropView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create or replace view drop_test as select 1,2") + tk.MustGetErrMsg("drop table drop_test", "[schema:1051]Unknown table 'test.drop_test'") + + tk.MustExec("drop view if exists drop_test") + + tk.MustGetErrMsg("drop view mysql.gc_delete_range", "Drop tidb system table 'mysql.gc_delete_range' is forbidden") + tk.MustGetErrMsg("drop view drop_test", "[schema:1051]Unknown table 'test.drop_test'") + tk.MustExec("create table t_v(a int)") + tk.MustGetErrMsg("drop view t_v", "[ddl:1347]'test.t_v' is not VIEW") + + tk.MustExec("create table t_v1(a int, b int);") + tk.MustExec("create table t_v2(a int, b int);") + tk.MustExec("create view v as select * from t_v1;") + tk.MustExec("create or replace view v as select * from t_v2;") + tk.MustQuery("select * from information_schema.views where table_name ='v';").Check( + testkit.Rows("def test v SELECT `test`.`t_v2`.`a` AS `a`,`test`.`t_v2`.`b` AS `b` FROM `test`.`t_v2` CASCADED NO @ DEFINER utf8mb4 utf8mb4_bin")) +} + +func TestAlterTableAddColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table if not exists alter_test (c1 int)") + tk.MustExec("insert into alter_test values(1)") + tk.MustExec("alter table alter_test add column c2 timestamp default current_timestamp") + time.Sleep(1 * time.Millisecond) + now := time.Now().Add(-1 * time.Millisecond).Format(types.TimeFormat) + r, err := tk.Exec("select c2 from alter_test") + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(context.Background(), req) + require.NoError(t, err) + row := req.GetRow(0) + require.Equal(t, 1, row.Len()) + require.GreaterOrEqual(t, now, row.GetTime(0).String()) + require.Nil(t, r.Close()) + tk.MustExec("alter table alter_test add column c3 varchar(50) default 'CURRENT_TIMESTAMP'") + tk.MustQuery("select c3 from alter_test").Check(testkit.Rows("CURRENT_TIMESTAMP")) + tk.MustExec("create or replace view alter_view as select c1,c2 from alter_test") + err = tk.ExecToErr("alter table alter_view add column c4 varchar(50)") + require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error(), err.Error()) + tk.MustExec("drop view alter_view") + tk.MustExec("create sequence alter_seq") + err = tk.ExecToErr("alter table alter_seq add column c int") + require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error(), err.Error()) + tk.MustExec("alter table alter_test add column c4 date default current_date") + now = time.Now().Format(types.DateFormat) + r, err = tk.Exec("select c4 from alter_test") + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(context.Background(), req) + require.NoError(t, err) + row = req.GetRow(0) + require.Equal(t, 1, row.Len()) + require.GreaterOrEqual(t, now, row.GetTime(0).String()) + require.Nil(t, r.Close()) + tk.MustExec("drop sequence alter_seq") +} + +func TestAlterTableAddColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table if not exists alter_test (c1 int)") + tk.MustExec("insert into alter_test values(1)") + tk.MustExec("alter table alter_test add column c2 timestamp default current_timestamp, add column c8 varchar(50) default 'CURRENT_TIMESTAMP'") + tk.MustExec("alter table alter_test add column (c7 timestamp default current_timestamp, c3 varchar(50) default 'CURRENT_TIMESTAMP')") + r, err := tk.Exec("select c2 from alter_test") + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(context.Background(), req) + require.NoError(t, err) + row := req.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Nil(t, r.Close()) + tk.MustQuery("select c3 from alter_test").Check(testkit.Rows("CURRENT_TIMESTAMP")) + tk.MustExec("create or replace view alter_view as select c1,c2 from alter_test") + err = tk.ExecToErr("alter table alter_view add column (c4 varchar(50), c5 varchar(50))") + require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error(), err.Error()) + tk.MustExec("drop view alter_view") + tk.MustExec("create sequence alter_seq") + err = tk.ExecToErr("alter table alter_seq add column (c1 int, c2 varchar(10))") + require.Equal(t, dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error(), err.Error()) + tk.MustExec("drop sequence alter_seq") +} + +func TestAddNotNullColumnNoDefault(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table nn (c1 int)") + tk.MustExec("insert nn values (1), (2)") + tk.MustExec("alter table nn add column c2 int not null") + + tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("nn")) + require.NoError(t, err) + col2 := tbl.Meta().Columns[1] + require.Nil(t, col2.DefaultValue) + require.Equal(t, "0", col2.OriginDefaultValue) + + tk.MustQuery("select * from nn").Check(testkit.Rows("1 0", "2 0")) + tk.MustExecToErr("insert nn (c1) values (3)") + tk.MustExec("set sql_mode=''") + tk.MustExec("insert nn (c1) values (3)") + tk.MustQuery("select * from nn").Check(testkit.Rows("1 0", "2 0", "3 0")) +} + +func TestAlterTableModifyColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists mc") + tk.MustExec("create table mc(c1 int, c2 varchar(10), c3 bit)") + tk.MustExecToErr("alter table mc modify column c1 short") + tk.MustExec("alter table mc modify column c1 bigint") + + tk.MustExecToErr("alter table mc modify column c2 blob") + tk.MustExec("alter table mc modify column c2 varchar(8)") + tk.MustExec("alter table mc modify column c2 varchar(11)") + tk.MustExec("alter table mc modify column c2 text(13)") + tk.MustExec("alter table mc modify column c2 text") + tk.MustExec("alter table mc modify column c3 bit") + result := tk.MustQuery("show create table mc") + createSQL := result.Rows()[0][1] + expected := "CREATE TABLE `mc` (\n `c1` bigint(20) DEFAULT NULL,\n `c2` text DEFAULT NULL,\n `c3` bit(1) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" + require.Equal(t, expected, createSQL) + tk.MustExec("create or replace view alter_view as select c1,c2 from mc") + tk.MustGetErrMsg("alter table alter_view modify column c2 text", + dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_view", "BASE TABLE").Error()) + tk.MustExec("drop view alter_view") + tk.MustExec("create sequence alter_seq") + tk.MustGetErrMsg("alter table alter_seq modify column c int", + dbterror.ErrWrongObject.GenWithStackByArgs("test", "alter_seq", "BASE TABLE").Error()) + tk.MustExec("drop sequence alter_seq") + + // test multiple collate modification in column. + tk.MustExec("drop table if exists modify_column_multiple_collate") + tk.MustExec("create table modify_column_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tk.MustExec("alter table modify_column_multiple_collate modify column a char(1) collate utf8mb4_bin;") + tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("modify_column_multiple_collate")) + require.NoError(t, err) + require.Equal(t, "utf8mb4", tt.Cols()[0].GetCharset()) + require.Equal(t, "utf8mb4_bin", tt.Cols()[0].GetCollate()) + require.Equal(t, "utf8mb4", tt.Meta().Charset) + require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) + + tk.MustExec("drop table if exists modify_column_multiple_collate;") + tk.MustExec("create table modify_column_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tk.MustExec("alter table modify_column_multiple_collate modify column a char(1) charset utf8mb4 collate utf8mb4_bin;") + tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("modify_column_multiple_collate")) + require.NoError(t, err) + require.Equal(t, "utf8mb4", tt.Cols()[0].GetCharset()) + require.Equal(t, "utf8mb4_bin", tt.Cols()[0].GetCollate()) + require.Equal(t, "utf8mb4", tt.Meta().Charset) + require.Equal(t, "utf8mb4_bin", tt.Meta().Collate) + + // test Err case for multiple collate modification in column. + tk.MustExec("drop table if exists err_modify_multiple_collate;") + tk.MustExec("create table err_modify_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tk.MustGetErrMsg("alter table err_modify_multiple_collate modify column a char(1) charset utf8mb4 collate utf8_bin;", dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8_bin", "utf8mb4").Error()) + + tk.MustExec("drop table if exists err_modify_multiple_collate;") + tk.MustExec("create table err_modify_multiple_collate (a char(1) collate utf8_bin collate utf8_general_ci) charset utf8mb4 collate utf8mb4_bin") + tk.MustGetErrMsg("alter table err_modify_multiple_collate modify column a char(1) collate utf8_bin collate utf8mb4_bin;", dbterror.ErrCollationCharsetMismatch.GenWithStackByArgs("utf8mb4_bin", "utf8").Error()) +} + +func TestColumnCharsetAndCollate(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + dbName := "col_charset_collate" + tk.MustExec("create database " + dbName) + tk.MustExec("use " + dbName) + tests := []struct { + colType string + charset string + collates string + exptCharset string + exptCollate string + errMsg string + }{ + { + colType: "varchar(10)", + charset: "charset utf8", + collates: "collate utf8_bin", + exptCharset: "utf8", + exptCollate: "utf8_bin", + errMsg: "", + }, + { + colType: "varchar(10)", + charset: "charset utf8mb4", + collates: "", + exptCharset: "utf8mb4", + exptCollate: "utf8mb4_bin", + errMsg: "", + }, + { + colType: "varchar(10)", + charset: "charset utf16", + collates: "", + exptCharset: "", + exptCollate: "", + errMsg: "Unknown charset utf16", + }, + { + colType: "varchar(10)", + charset: "charset latin1", + collates: "", + exptCharset: "latin1", + exptCollate: "latin1_bin", + errMsg: "", + }, + { + colType: "varchar(10)", + charset: "charset binary", + collates: "", + exptCharset: "binary", + exptCollate: "binary", + errMsg: "", + }, + { + colType: "varchar(10)", + charset: "charset ascii", + collates: "", + exptCharset: "ascii", + exptCollate: "ascii_bin", + errMsg: "", + }, + } + sctx := tk.Session() + dm := domain.GetDomain(sctx) + for i, tt := range tests { + tblName := fmt.Sprintf("t%d", i) + sql := fmt.Sprintf("create table %s (a %s %s %s)", tblName, tt.colType, tt.charset, tt.collates) + if tt.errMsg == "" { + tk.MustExec(sql) + is := dm.InfoSchema() + require.NotNil(t, is) + + tb, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) + require.NoError(t, err) + require.Equalf(t, tt.exptCharset, tb.Meta().Columns[0].GetCharset(), sql) + require.Equalf(t, tt.exptCollate, tb.Meta().Columns[0].GetCollate(), sql) + } else { + err := tk.ExecToErr(sql) + require.Errorf(t, err, sql) + } + } + tk.MustExec("drop database " + dbName) +} + +func TestTooLargeIdentifierLength(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // for database. + dbName1, dbName2 := strings.Repeat("a", mysql.MaxDatabaseNameLength), strings.Repeat("a", mysql.MaxDatabaseNameLength+1) + tk.MustExec(fmt.Sprintf("create database %s", dbName1)) + tk.MustExec(fmt.Sprintf("drop database %s", dbName1)) + tk.MustGetErrMsg(fmt.Sprintf("create database %s", dbName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", dbName2)) + + // for table. + tk.MustExec("use test") + tableName1, tableName2 := strings.Repeat("b", mysql.MaxTableNameLength), strings.Repeat("b", mysql.MaxTableNameLength+1) + tk.MustExec(fmt.Sprintf("create table %s(c int)", tableName1)) + tk.MustExec(fmt.Sprintf("drop table %s", tableName1)) + tk.MustGetErrMsg(fmt.Sprintf("create table %s(c int)", tableName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", tableName2)) + + // for column. + tk.MustExec("drop table if exists t;") + columnName1, columnName2 := strings.Repeat("c", mysql.MaxColumnNameLength), strings.Repeat("c", mysql.MaxColumnNameLength+1) + tk.MustExec(fmt.Sprintf("create table t(%s int)", columnName1)) + tk.MustExec("drop table t") + tk.MustGetErrMsg(fmt.Sprintf("create table t(%s int)", columnName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", columnName2)) + + // for index. + tk.MustExec("create table t(c int);") + indexName1, indexName2 := strings.Repeat("d", mysql.MaxIndexIdentifierLen), strings.Repeat("d", mysql.MaxIndexIdentifierLen+1) + tk.MustExec(fmt.Sprintf("create index %s on t(c)", indexName1)) + tk.MustExec(fmt.Sprintf("drop index %s on t", indexName1)) + tk.MustGetErrMsg(fmt.Sprintf("create index %s on t(c)", indexName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", indexName2)) + + // for create table with index. + tk.MustExec("drop table t;") + tk.MustGetErrMsg(fmt.Sprintf("create table t(c int, index %s(c));", indexName2), fmt.Sprintf("[ddl:1059]Identifier name '%s' is too long", indexName2)) +} + +func TestShardRowIDBits(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t (a int) shard_row_id_bits = 15") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t values (?)", i) + } + + dom := domain.GetDomain(tk.Session()) + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + assertCountAndShard := func(tt table.Table, expectCount int) { + var hasShardedID bool + var count int + require.NoError(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + err = tables.IterRecords(tt, tk.Session(), nil, func(h kv.Handle, rec []types.Datum, cols []*table.Column) (more bool, err error) { + require.GreaterOrEqual(t, h.IntValue(), int64(0)) + first8bits := h.IntValue() >> 56 + if first8bits > 0 { + hasShardedID = true + } + count++ + return true, nil + }) + require.NoError(t, err) + require.Equal(t, expectCount, count) + require.True(t, hasShardedID) + } + + assertCountAndShard(tbl, 100) + + // After PR 10759, shard_row_id_bits is supported with tables with auto_increment column. + tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 4") + tk.MustExec("alter table auto shard_row_id_bits = 5") + tk.MustExec("drop table auto") + tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 0") + tk.MustExec("alter table auto shard_row_id_bits = 5") + tk.MustExec("drop table auto") + tk.MustExec("create table auto (id int not null auto_increment unique)") + tk.MustExec("alter table auto shard_row_id_bits = 5") + tk.MustExec("drop table auto") + tk.MustExec("create table auto (id int not null auto_increment unique) shard_row_id_bits = 4") + tk.MustExec("alter table auto shard_row_id_bits = 0") + tk.MustExec("drop table auto") + + errMsg := "[ddl:8200]Unsupported shard_row_id_bits for table with primary key as row id" + tk.MustGetErrMsg("create table auto (id varchar(255) primary key clustered, b int) shard_row_id_bits = 4;", errMsg) + tk.MustExec("create table auto (id varchar(255) primary key clustered, b int) shard_row_id_bits = 0;") + tk.MustGetErrMsg("alter table auto shard_row_id_bits = 5;", errMsg) + tk.MustExec("alter table auto shard_row_id_bits = 0;") + tk.MustExec("drop table if exists auto;") + + // After PR 10759, shard_row_id_bits is not supported with pk_is_handle tables. + tk.MustGetErrMsg("create table auto (id int not null auto_increment primary key, b int) shard_row_id_bits = 4", errMsg) + tk.MustExec("create table auto (id int not null auto_increment primary key, b int) shard_row_id_bits = 0") + tk.MustGetErrMsg("alter table auto shard_row_id_bits = 5", errMsg) + tk.MustExec("alter table auto shard_row_id_bits = 0") + + // Hack an existing table with shard_row_id_bits and primary key as handle + db, ok := dom.InfoSchema().SchemaByName(model.NewCIStr("test")) + require.True(t, ok) + tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto")) + tblInfo := tbl.Meta() + tblInfo.ShardRowIDBits = 5 + tblInfo.MaxShardRowIDBits = 5 + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + _, err = m.GenSchemaVersion() + require.NoError(t, err) + require.Nil(t, m.UpdateTable(db.ID, tblInfo)) + return nil + }) + require.NoError(t, err) + err = dom.Reload() + require.NoError(t, err) + + tk.MustExec("insert auto(b) values (1), (3), (5)") + tk.MustQuery("select id from auto order by id").Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("alter table auto shard_row_id_bits = 0") + tk.MustExec("drop table auto") + + // Test shard_row_id_bits with auto_increment column + tk.MustExec("create table auto (a int, b int auto_increment unique) shard_row_id_bits = 15") + for i := 0; i < 100; i++ { + tk.MustExec("insert into auto(a) values (?)", i) + } + tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto")) + assertCountAndShard(tbl, 100) + prevB, err := strconv.Atoi(tk.MustQuery("select b from auto where a=0").Rows()[0][0].(string)) + require.NoError(t, err) + for i := 1; i < 100; i++ { + b, err := strconv.Atoi(tk.MustQuery(fmt.Sprintf("select b from auto where a=%d", i)).Rows()[0][0].(string)) + require.NoError(t, err) + require.Greater(t, b, prevB) + prevB = b + } + + // Test overflow + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int) shard_row_id_bits = 15") + defer tk.MustExec("drop table if exists t1") + + tbl, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + maxID := 1<<(64-15-1) - 1 + alloc := tbl.Allocators(tk.Session()).Get(autoid.RowIDAllocType) + err = alloc.Rebase(context.Background(), int64(maxID)-1, false) + require.NoError(t, err) + tk.MustExec("insert into t1 values(1)") + + // continue inserting will fail. + tk.MustGetDBError("insert into t1 values(2)", autoid.ErrAutoincReadFailed) + tk.MustGetDBError("insert into t1 values(3)", autoid.ErrAutoincReadFailed) +} + +func TestAutoRandomBitsData(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create database if not exists test_auto_random_bits") + defer tk.MustExec("drop database if exists test_auto_random_bits") + tk.MustExec("use test_auto_random_bits") + tk.MustExec("drop table if exists t") + + extractAllHandles := func() []int64 { + allHds, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test_auto_random_bits", "t") + require.NoError(t, err) + return allHds + } + + tk.MustExec("set @@allow_auto_random_explicit_insert = true") + + tk.MustExec("create table t (a bigint primary key clustered auto_random(15), b int)") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t(b) values (?)", i) + } + allHandles := extractAllHandles() + tk.MustExec("drop table t") + + // Test auto random id number. + require.Equal(t, 100, len(allHandles)) + // Test the handles are not all zero. + allZero := true + for _, h := range allHandles { + allZero = allZero && (h>>(64-16)) == 0 + } + require.False(t, allZero) + // Test non-shard-bits part of auto random id is monotonic increasing and continuous. + orderedHandles := testutil.MaskSortHandles(allHandles, 15, mysql.TypeLonglong) + size := int64(len(allHandles)) + for i := int64(1); i <= size; i++ { + require.Equal(t, orderedHandles[i-1], i) + } + + // Test explicit insert. + autoRandBitsUpperBound := 2<<47 - 1 + tk.MustExec("create table t (a bigint primary key clustered auto_random(15), b int)") + for i := -10; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into t values(%d, %d)", i+autoRandBitsUpperBound, i)) + } + tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) + tk.MustExec("drop table t") + + // Test overflow. + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") + // Here we cannot fill the all values for a `bigint` column, + // so firstly we rebase auto_rand to the position before overflow. + tk.MustExec(fmt.Sprintf("insert into t values (%d, %d)", autoRandBitsUpperBound, 1)) + tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) + tk.MustExec("drop table t") + + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") + tk.MustExec("insert into t values (1, 2)") + tk.MustExec(fmt.Sprintf("update t set a = %d where a = 1", autoRandBitsUpperBound)) + tk.MustGetErrMsg("insert into t (b) values (0)", autoid.ErrAutoRandReadFailed.GenWithStackByArgs().Error()) + tk.MustExec("drop table t") + + // Test insert negative integers explicitly won't trigger rebase. + tk.MustExec("create table t (a bigint primary key auto_random(15), b int)") + for i := 1; i <= 100; i++ { + tk.MustExec("insert into t(b) values (?)", i) + tk.MustExec("insert into t(a, b) values (?, ?)", -i, i) + } + // orderedHandles should be [-100, -99, ..., -2, -1, 1, 2, ..., 99, 100] + orderedHandles = testutil.MaskSortHandles(extractAllHandles(), 15, mysql.TypeLonglong) + size = int64(len(allHandles)) + for i := int64(0); i < 100; i++ { + require.Equal(t, i-100, orderedHandles[i]) + } + for i := int64(100); i < size; i++ { + require.Equal(t, i-99, orderedHandles[i]) + } + tk.MustExec("drop table t") + + // Test signed/unsigned types. + tk.MustExec("create table t (a bigint primary key auto_random(10), b int)") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t (b) values(?)", i) + } + for _, h := range extractAllHandles() { + // Sign bit should be reserved. + require.True(t, h > 0) + } + tk.MustExec("drop table t") + + tk.MustExec("create table t (a bigint unsigned primary key auto_random(10), b int)") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t (b) values(?)", i) + } + signBitUnused := true + for _, h := range extractAllHandles() { + signBitUnused = signBitUnused && (h > 0) + } + // Sign bit should be used for shard. + require.False(t, signBitUnused) + tk.MustExec("drop table t;") + + // Test rename table does not affect incremental part of auto_random ID. + tk.MustExec("create database test_auto_random_bits_rename;") + tk.MustExec("create table t (a bigint auto_random primary key);") + for i := 0; i < 10; i++ { + tk.MustExec("insert into t values ();") + } + tk.MustExec("alter table t rename to test_auto_random_bits_rename.t1;") + for i := 0; i < 10; i++ { + tk.MustExec("insert into test_auto_random_bits_rename.t1 values ();") + } + tk.MustExec("alter table test_auto_random_bits_rename.t1 rename to t;") + for i := 0; i < 10; i++ { + tk.MustExec("insert into t values ();") + } + uniqueHandles := make(map[int64]struct{}) + for _, h := range extractAllHandles() { + uniqueHandles[h&((1<<(63-5))-1)] = struct{}{} + } + require.Equal(t, 30, len(uniqueHandles)) + tk.MustExec("drop database test_auto_random_bits_rename;") + tk.MustExec("drop table t;") +} + +func TestAutoRandomTableOption(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // test table option is auto-random + tk.MustExec("drop table if exists auto_random_table_option") + tk.MustExec("create table auto_random_table_option (a bigint auto_random(5) key) auto_random_base = 1000") + tt, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("auto_random_table_option")) + require.NoError(t, err) + require.Equal(t, int64(1000), tt.Meta().AutoRandID) + tk.MustExec("insert into auto_random_table_option values (),(),(),(),()") + allHandles, err := ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "auto_random_table_option") + require.NoError(t, err) + require.Equal(t, 5, len(allHandles)) + // Test non-shard-bits part of auto random id is monotonic increasing and continuous. + orderedHandles := testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + size := int64(len(allHandles)) + for i := int64(0); i < size; i++ { + require.Equal(t, orderedHandles[i], i+1000) + } + + tk.MustExec("drop table if exists alter_table_auto_random_option") + tk.MustExec("create table alter_table_auto_random_option (a bigint primary key auto_random(4), b int)") + tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("alter_table_auto_random_option")) + require.NoError(t, err) + require.Equal(t, int64(0), tt.Meta().AutoRandID) + tk.MustExec("insert into alter_table_auto_random_option values(),(),(),(),()") + allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "alter_table_auto_random_option") + require.NoError(t, err) + orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + size = int64(len(allHandles)) + for i := int64(0); i < size; i++ { + require.Equal(t, i+1, orderedHandles[i]) + } + tk.MustExec("delete from alter_table_auto_random_option") + + // alter table to change the auto_random option (it will dismiss the local allocator cache) + // To avoid the new base is in the range of local cache, which will leading the next + // value is not what we rebased, because the local cache is dropped, here we choose + // a quite big value to do this. + tk.MustExec("alter table alter_table_auto_random_option auto_random_base = 3000000") + tt, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("alter_table_auto_random_option")) + require.NoError(t, err) + require.Equal(t, int64(3000000), tt.Meta().AutoRandID) + tk.MustExec("insert into alter_table_auto_random_option values(),(),(),(),()") + allHandles, err = ddltestutil.ExtractAllTableHandles(tk.Session(), "test", "alter_table_auto_random_option") + require.NoError(t, err) + orderedHandles = testutil.MaskSortHandles(allHandles, 5, mysql.TypeLonglong) + size = int64(len(allHandles)) + for i := int64(0); i < size; i++ { + require.Equal(t, i+3000000, orderedHandles[i]) + } + tk.MustExec("drop table alter_table_auto_random_option") + + // Alter auto_random_base on non auto_random table. + tk.MustExec("create table alter_auto_random_normal (a int)") + err = tk.ExecToErr("alter table alter_auto_random_normal auto_random_base = 100") + require.Error(t, err) + require.Contains(t, err.Error(), autoid.AutoRandomRebaseNotApplicable) +} + +func TestSetDDLReorgWorkerCnt(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + err := ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(variable.DefTiDBDDLReorgWorkerCount), variable.GetDDLReorgWorkerCounter()) + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 1") + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(1), variable.GetDDLReorgWorkerCounter()) + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(100), variable.GetDDLReorgWorkerCounter()) + tk.MustGetDBError("set @@global.tidb_ddl_reorg_worker_cnt = invalid_val", variable.ErrWrongTypeForVar) + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(100), variable.GetDDLReorgWorkerCounter()) + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = -1") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_worker_cnt value: '-1'")) + tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt").Check(testkit.Rows("1")) + + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") + res := tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") + res.Check(testkit.Rows("100")) + + res = tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") + res.Check(testkit.Rows("100")) + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 100") + res = tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt") + res.Check(testkit.Rows("100")) + + tk.MustExec("set @@global.tidb_ddl_reorg_worker_cnt = 257") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_worker_cnt value: '257'")) + tk.MustQuery("select @@global.tidb_ddl_reorg_worker_cnt").Check(testkit.Rows("256")) +} + +func TestSetDDLReorgBatchSize(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + err := ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(variable.DefTiDBDDLReorgBatchSize), variable.GetDDLReorgBatchSize()) + + tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 1") + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '1'")) + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, variable.MinDDLReorgBatchSize, variable.GetDDLReorgBatchSize()) + tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_reorg_batch_size = %v", variable.MaxDDLReorgBatchSize+1)) + tk.MustQuery("show warnings;").Check(testkit.Rows(fmt.Sprintf("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '%d'", variable.MaxDDLReorgBatchSize+1))) + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, variable.MaxDDLReorgBatchSize, variable.GetDDLReorgBatchSize()) + tk.MustGetDBError("set @@global.tidb_ddl_reorg_batch_size = invalid_val", variable.ErrWrongTypeForVar) + tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 100") + err = ddlutil.LoadDDLReorgVars(context.Background(), tk.Session()) + require.NoError(t, err) + require.Equal(t, int32(100), variable.GetDDLReorgBatchSize()) + tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = -1") + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_reorg_batch_size value: '-1'")) + + tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 100") + res := tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") + res.Check(testkit.Rows("100")) + + res = tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") + res.Check(testkit.Rows(fmt.Sprintf("%v", 100))) + tk.MustExec("set @@global.tidb_ddl_reorg_batch_size = 1000") + res = tk.MustQuery("select @@global.tidb_ddl_reorg_batch_size") + res.Check(testkit.Rows("1000")) +} + +func TestIllegalFunctionCall4GeneratedColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // Test create an exist database + tk.MustExecToErr("CREATE database test") + + tk.MustGetErrMsg("create table t1 (b double generated always as (rand()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) + tk.MustGetErrMsg("create table t1 (a varchar(64), b varchar(1024) generated always as (load_file(a)) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) + tk.MustGetErrMsg("create table t1 (a datetime generated always as (curdate()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) + tk.MustGetErrMsg("create table t1 (a datetime generated always as (current_time()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) + tk.MustGetErrMsg("create table t1 (a datetime generated always as (current_timestamp()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) + tk.MustGetErrMsg("create table t1 (a datetime, b varchar(10) generated always as (localtime()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) + tk.MustGetErrMsg("create table t1 (a varchar(1024) generated always as (uuid()) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("a").Error()) + tk.MustGetErrMsg("create table t1 (a varchar(1024), b varchar(1024) generated always as (is_free_lock(a)) virtual);", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("b").Error()) + + tk.MustExec("create table t1 (a bigint not null primary key auto_increment, b bigint, c bigint as (b + 1));") + + tk.MustGetErrMsg("alter table t1 add column d varchar(1024) generated always as (database());", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("d").Error()) + + tk.MustExec("alter table t1 add column d bigint generated always as (b + 1); ") + + tk.MustGetErrMsg("alter table t1 modify column d bigint generated always as (connection_id());", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("d").Error()) + tk.MustGetErrMsg("alter table t1 change column c cc bigint generated always as (connection_id());", + dbterror.ErrGeneratedColumnFunctionIsNotAllowed.GenWithStackByArgs("cc").Error()) +} + +func TestGeneratedColumnRelatedDDL(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // Test create an exist database + err := tk.ExecToErr("CREATE database test") + require.Error(t, err) + + tk.MustGetErrMsg("create table t1 (a bigint not null primary key auto_increment, b bigint as (a + 1));", + dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("b").Error()) + tk.MustExec("create table t1 (a bigint not null primary key auto_increment, b bigint, c bigint as (b + 1));") + tk.MustGetErrMsg("alter table t1 add column d bigint generated always as (a + 1);", + dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("d").Error()) + tk.MustExec("alter table t1 add column d bigint generated always as (b + 1);") + tk.MustGetErrMsg("alter table t1 modify column d bigint generated always as (a + 1);", + dbterror.ErrGeneratedColumnRefAutoInc.GenWithStackByArgs("d").Error()) + + // This mysql compatibility check can be disabled using tidb_enable_auto_increment_in_generated + tk.MustExec("set session tidb_enable_auto_increment_in_generated = 1;") + tk.MustExec("alter table t1 modify column d bigint generated always as (a + 1);") + + tk.MustGetErrMsg("alter table t1 add column e bigint as (z + 1);", + dbterror.ErrBadField.GenWithStackByArgs("z", "generated column function").Error()) + + tk.MustExec("drop table t1;") + + tk.MustExec("create table t1(a int, b int as (a+1), c int as (b+1));") + tk.MustExec("insert into t1 (a) values (1);") + tk.MustGetErrCode("alter table t1 modify column c int as (b+1) first;", mysql.ErrGeneratedColumnNonPrior) + tk.MustGetErrCode("alter table t1 modify column b int as (a+1) after c;", mysql.ErrGeneratedColumnNonPrior) + tk.MustQuery("select * from t1").Check(testkit.Rows("1 2 3")) +} + +func TestSetDDLErrorCountLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + err := ddlutil.LoadDDLVars(tk.Session()) + require.NoError(t, err) + require.Equal(t, int64(variable.DefTiDBDDLErrorCountLimit), variable.GetDDLErrorCountLimit()) + + tk.MustExec("set @@global.tidb_ddl_error_count_limit = -1") + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_ddl_error_count_limit value: '-1'")) + err = ddlutil.LoadDDLVars(tk.Session()) + require.NoError(t, err) + require.Equal(t, int64(0), variable.GetDDLErrorCountLimit()) + tk.MustExec(fmt.Sprintf("set @@global.tidb_ddl_error_count_limit = %v", uint64(math.MaxInt64)+1)) + tk.MustQuery("show warnings;").Check(testkit.Rows(fmt.Sprintf("Warning 1292 Truncated incorrect tidb_ddl_error_count_limit value: '%d'", uint64(math.MaxInt64)+1))) + err = ddlutil.LoadDDLVars(tk.Session()) + require.NoError(t, err) + require.Equal(t, int64(math.MaxInt64), variable.GetDDLErrorCountLimit()) + tk.MustGetDBError("set @@global.tidb_ddl_error_count_limit = invalid_val", variable.ErrWrongTypeForVar) + tk.MustExec("set @@global.tidb_ddl_error_count_limit = 100") + err = ddlutil.LoadDDLVars(tk.Session()) + require.NoError(t, err) + require.Equal(t, int64(100), variable.GetDDLErrorCountLimit()) + res := tk.MustQuery("select @@global.tidb_ddl_error_count_limit") + res.Check(testkit.Rows("100")) +} + +func TestLoadDDLDistributeVars(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.Equal(t, variable.DefTiDBEnableDistTask, disttask.TiDBEnableDistTask) + + tk.MustGetDBError("set @@global.tidb_enable_dist_task = invalid_val", variable.ErrWrongValueForVar) + require.Equal(t, disttask.TiDBEnableDistTask, variable.EnableDistTask.Load()) + tk.MustExec("set @@global.tidb_enable_dist_task = 'on'") + require.Equal(t, true, variable.EnableDistTask.Load()) + tk.MustExec(fmt.Sprintf("set @@global.tidb_enable_dist_task = %v", disttask.TiDBEnableDistTask)) + require.Equal(t, disttask.TiDBEnableDistTask, variable.EnableDistTask.Load()) +} + +// this test will change the fail-point `mockAutoIDChange`, so we move it to the `testRecoverTable` suite +func TestRenameTable(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("drop database if exists rename1") + tk.MustExec("drop database if exists rename2") + tk.MustExec("drop database if exists rename3") + + tk.MustExec("create database rename1") + tk.MustExec("create database rename2") + tk.MustExec("create database rename3") + tk.MustExec("create table rename1.t (a int primary key auto_increment)") + tk.MustExec("insert rename1.t values ()") + tk.MustExec("rename table rename1.t to rename2.t") + // Make sure the drop old database doesn't affect the rename3.t's operations. + tk.MustExec("drop database rename1") + tk.MustExec("insert rename2.t values ()") + tk.MustExec("rename table rename2.t to rename3.t") + tk.MustExec("insert rename3.t values ()") + tk.MustQuery("select * from rename3.t").Check(testkit.Rows("1", "5001", "10001")) + // Make sure the drop old database doesn't affect the rename3.t's operations. + tk.MustExec("drop database rename2") + tk.MustExec("insert rename3.t values ()") + tk.MustQuery("select * from rename3.t").Check(testkit.Rows("1", "5001", "10001", "10002")) + tk.MustExec("drop database rename3") + + tk.MustExec("create database rename1") + tk.MustExec("create database rename2") + tk.MustExec("create table rename1.t (a int primary key auto_increment)") + tk.MustExec("rename table rename1.t to rename2.t1") + tk.MustExec("insert rename2.t1 values ()") + result := tk.MustQuery("select * from rename2.t1") + result.Check(testkit.Rows("1")) + // Make sure the drop old database doesn't affect the t1's operations. + tk.MustExec("drop database rename1") + tk.MustExec("insert rename2.t1 values ()") + result = tk.MustQuery("select * from rename2.t1") + result.Check(testkit.Rows("1", "2")) + // Rename a table to another table in the same database. + tk.MustExec("rename table rename2.t1 to rename2.t2") + tk.MustExec("insert rename2.t2 values ()") + result = tk.MustQuery("select * from rename2.t2") + result.Check(testkit.Rows("1", "2", "5001")) + tk.MustExec("drop database rename2") + + tk.MustExec("create database rename1") + tk.MustExec("create database rename2") + tk.MustExec("create table rename1.t (a int primary key auto_increment)") + tk.MustExec("insert rename1.t values ()") + tk.MustExec("rename table rename1.t to rename2.t1") + // Make sure the value is greater than autoid.step. + tk.MustExec("insert rename2.t1 values (100000)") + tk.MustExec("insert rename2.t1 values ()") + result = tk.MustQuery("select * from rename2.t1") + result.Check(testkit.Rows("1", "100000", "100001")) + tk.MustExecToErr("insert rename1.t values ()") + tk.MustExec("drop database rename1") + tk.MustExec("drop database rename2") +} + +func TestAutoIncrementColumnErrorMessage(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // Test create an exist database + tk.MustExecToErr("CREATE database test") + + tk.MustExec("CREATE TABLE t1 (t1_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY);") + + tk.MustGetErrMsg("CREATE INDEX idx1 ON t1 ((t1_id + t1_id));", + dbterror.ErrExpressionIndexCanNotRefer.GenWithStackByArgs("idx1").Error()) + + // This mysql compatibility check can be disabled using tidb_enable_auto_increment_in_generated + tk.MustExec("SET SESSION tidb_enable_auto_increment_in_generated = 1;") + tk.MustExec("CREATE INDEX idx1 ON t1 ((t1_id + t1_id));") +} + +func TestRenameMultiTables(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + store := testkit.CreateMockStore(t, mockstore.WithDDLChecker()) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("drop database if exists rename1") + tk.MustExec("drop database if exists rename2") + tk.MustExec("drop database if exists rename3") + tk.MustExec("drop database if exists rename4") + + tk.MustExec("create database rename1") + tk.MustExec("create database rename2") + tk.MustExec("create database rename3") + tk.MustExec("create database rename4") + tk.MustExec("create table rename1.t1 (a int primary key auto_increment)") + tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") + tk.MustExec("insert rename1.t1 values ()") + tk.MustExec("insert rename3.t3 values ()") + tk.MustExec("rename table rename1.t1 to rename2.t2, rename3.t3 to rename4.t4") + // Make sure the drop old database doesn't affect t2,t4's operations. + tk.MustExec("drop database rename1") + tk.MustExec("insert rename2.t2 values ()") + tk.MustExec("drop database rename3") + tk.MustExec("insert rename4.t4 values ()") + tk.MustQuery("select * from rename2.t2").Check(testkit.Rows("1", "5001")) + tk.MustQuery("select * from rename4.t4").Check(testkit.Rows("1", "5001")) + // Rename a table to another table in the same database. + tk.MustExec("rename table rename2.t2 to rename2.t1, rename4.t4 to rename4.t3") + tk.MustExec("insert rename2.t1 values ()") + tk.MustQuery("select * from rename2.t1").Check(testkit.Rows("1", "5001", "10001")) + tk.MustExec("insert rename4.t3 values ()") + tk.MustQuery("select * from rename4.t3").Check(testkit.Rows("1", "5001", "10001")) + tk.MustExec("drop database rename2") + tk.MustExec("drop database rename4") + + tk.MustExec("create database rename1") + tk.MustExec("create database rename2") + tk.MustExec("create database rename3") + tk.MustExec("create table rename1.t1 (a int primary key auto_increment)") + tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") + tk.MustGetErrCode("rename table rename1.t1 to rename2.t2, rename3.t3 to rename2.t2", errno.ErrTableExists) + tk.MustExec("rename table rename1.t1 to rename2.t2, rename2.t2 to rename1.t1") + tk.MustExec("rename table rename1.t1 to rename2.t2, rename3.t3 to rename1.t1") + tk.MustExec("use rename1") + tk.MustQuery("show tables").Check(testkit.Rows("t1")) + tk.MustExec("use rename2") + tk.MustQuery("show tables").Check(testkit.Rows("t2")) + tk.MustExec("use rename3") + tk.MustExec("create table rename3.t3 (a int primary key auto_increment)") + tk.MustGetErrCode("rename table rename1.t1 to rename1.t2, rename1.t1 to rename3.t3", errno.ErrTableExists) + tk.MustGetErrCode("rename table rename1.t1 to rename1.t2, rename1.t1 to rename3.t4", errno.ErrNoSuchTable) + tk.MustExec("drop database rename1") + tk.MustExec("drop database rename2") + tk.MustExec("drop database rename3") +} + +func TestCheckPrimaryKeyForTTLTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // create table should fail when pk contains double/float + tk.MustGetDBError("create table t1(id float primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("create table t1(id float(10,2) primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("create table t1(id double primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("create table t1(id float(10,2) primary key, t timestamp) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("create table t1(id1 int, id2 float, t timestamp, primary key(id1, id2)) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("create table t1(id1 int, id2 double, t timestamp, primary key(id1, id2)) TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + + // alter table should fail when pk contains double/float + tk.MustExec("create table t1(id float primary key, t timestamp)") + tk.MustExec("create table t2(id double primary key, t timestamp)") + tk.MustExec("create table t3(id1 int, id2 float, primary key(id1, id2), t timestamp)") + tk.MustExec("create table t4(id1 int, id2 double, primary key(id1, id2), t timestamp)") + tk.MustGetDBError("alter table t1 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("alter table t2 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("alter table t3 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + tk.MustGetDBError("alter table t4 TTL=`t`+INTERVAL 1 DAY", dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL) + + // create table should not fail when the pk is not clustered + tk.MustExec("create table t11(id float primary key nonclustered, t timestamp) TTL=`t`+INTERVAL 1 DAY") + tk.MustExec("create table t12(id double primary key nonclustered, t timestamp) TTL=`t`+INTERVAL 1 DAY") + tk.MustExec("create table t13(id1 int, id2 float, t timestamp, primary key(id1, id2) nonclustered) TTL=`t`+INTERVAL 1 DAY") + + // alter table should not fail when the pk is not clustered + tk.MustExec("create table t21(id float primary key nonclustered, t timestamp)") + tk.MustExec("create table t22(id double primary key nonclustered, t timestamp)") + tk.MustExec("create table t23(id1 int, id2 float, t timestamp, primary key(id1, id2) nonclustered)") + tk.MustExec("alter table t21 TTL=`t`+INTERVAL 1 DAY") + tk.MustExec("alter table t22 TTL=`t`+INTERVAL 1 DAY") + tk.MustExec("alter table t23 TTL=`t`+INTERVAL 1 DAY") +} diff --git a/pkg/executor/test/ddl/main_test.go b/pkg/executor/test/ddl/main_test.go new file mode 100644 index 0000000000000..34aa611044890 --- /dev/null +++ b/pkg/executor/test/ddl/main_test.go @@ -0,0 +1,44 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package ddl + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/distsqltest/BUILD.bazel b/pkg/executor/test/distsqltest/BUILD.bazel new file mode 100644 index 0000000000000..e6ae1cc596001 --- /dev/null +++ b/pkg/executor/test/distsqltest/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "distsqltest_test", + timeout = "short", + srcs = [ + "distsql_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/distsqltest/distsql_test.go b/pkg/executor/test/distsqltest/distsql_test.go new file mode 100644 index 0000000000000..74440d94136b3 --- /dev/null +++ b/pkg/executor/test/distsqltest/distsql_test.go @@ -0,0 +1,77 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package distsql_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestDistsqlPartitionTableConcurrency(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1(id int primary key , val int)") + partitions := make([]string, 0, 20) + for i := 0; i < 20; i++ { + pid := i + 1 + partitions = append(partitions, fmt.Sprintf("PARTITION p%d VALUES LESS THAN (%d00)", pid, pid)) + } + tk.MustExec("create table t2(id int primary key, val int)" + + "partition by range(id)" + + "(" + strings.Join(partitions[:10], ",") + ")") + tk.MustExec("create table t3(id int primary key, val int)" + + "partition by range(id)" + + "(" + strings.Join(partitions, ",") + ")") + for i := 0; i < 20; i++ { + for _, tbl := range []string{"t1", "t2", "t3"} { + tk.MustExec(fmt.Sprintf("insert into %s values(%d, %d)", tbl, i*50, i*50)) + } + } + tk.MustExec("analyze table t1, t2, t3") + // non-partitioned table checker + ctx1 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { + require.Equal(t, req.KeyRanges.PartitionNum(), 1) + require.Equal(t, req.Concurrency, 1) + }) + // 10-ranges-partitioned table checker + ctx2 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { + require.Equal(t, req.KeyRanges.PartitionNum(), 10) + require.Equal(t, req.Concurrency, 10) + }) + // 20-ranges-partitioned table checker + ctx3 := context.WithValue(context.Background(), "CheckSelectRequestHook", func(req *kv.Request) { + require.Equal(t, req.KeyRanges.PartitionNum(), 20) + require.Equal(t, req.Concurrency, variable.DefDistSQLScanConcurrency) + }) + ctxs := []context.Context{ctx1, ctx2, ctx3} + for i, tbl := range []string{"t1", "t2", "t3"} { + ctx := ctxs[i] + // If order by is added here, the concurrency is always equal to 1. + // Because we will use different kv.Request for each partition in TableReader. + tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 1", tbl)) + tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 5", tbl)) + tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 1", tbl)) + tk.MustQueryWithContext(ctx, fmt.Sprintf("select * from %s limit 5", tbl)) + } +} diff --git a/pkg/executor/test/distsqltest/main_test.go b/pkg/executor/test/distsqltest/main_test.go new file mode 100644 index 0000000000000..2992444fb0ca4 --- /dev/null +++ b/pkg/executor/test/distsqltest/main_test.go @@ -0,0 +1,44 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package distsql_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/executor/BUILD.bazel b/pkg/executor/test/executor/BUILD.bazel new file mode 100644 index 0000000000000..50001434b57b9 --- /dev/null +++ b/pkg/executor/test/executor/BUILD.bazel @@ -0,0 +1,60 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "executor_test", + timeout = "short", + srcs = [ + "executor_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/executor", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/driver/error", + "//pkg/store/mockstore", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/types", + "//pkg/util", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/replayer", + "//pkg/util/rowcodec", + "//pkg/util/sqlexec", + "//pkg/util/timeutil", + "@com_github_golang_protobuf//proto", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/executor/executor_test.go b/pkg/executor/test/executor/executor_test.go new file mode 100644 index 0000000000000..ae6af5226a609 --- /dev/null +++ b/pkg/executor/test/executor/executor_test.go @@ -0,0 +1,4301 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "archive/zip" + "context" + "fmt" + "math" + "path/filepath" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/golang/protobuf/proto" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + error2 "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tipb/go-tipb" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/testutils" +) + +func checkFileName(s string) bool { + files := []string{ + "config.toml", + "debug_trace/debug_trace0.json", + "meta.txt", + "stats/test.t_dump_single.json", + "schema/test.t_dump_single.schema.txt", + "schema/schema_meta.txt", + "table_tiflash_replica.txt", + "variables.toml", + "session_bindings.sql", + "global_bindings.sql", + "sql/sql0.sql", + "explain.txt", + "statsMem/test.t_dump_single.txt", + "sql_meta.toml", + } + for _, f := range files { + if strings.Compare(f, s) == 0 { + return true + } + } + return false +} + +func TestBind(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists testbind") + + tk.MustExec("create table testbind(i int, s varchar(20))") + tk.MustExec("create index index_t on testbind(i,s)") + tk.MustExec("create global binding for select * from testbind using select * from testbind use index for join(index_t)") + require.Len(t, tk.MustQuery("show global bindings").Rows(), 1) + + tk.MustExec("create session binding for select * from testbind using select * from testbind use index for join(index_t)") + require.Len(t, tk.MustQuery("show session bindings").Rows(), 1) + tk.MustExec("drop session binding for select * from testbind") +} + +func TestLoadStats(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.Error(t, tk.ExecToErr("load stats")) + require.Error(t, tk.ExecToErr("load stats ./xxx.json")) +} + +func TestPlanReplayer(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx_a(a))") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustQuery("plan replayer dump explain select * from t where a=10") + tk.MustQuery("plan replayer dump explain select /*+ read_from_storage(tiflash[t]) */ * from t") + + tk.MustExec("create table t1 (a int)") + tk.MustExec("create table t2 (a int)") + tk.MustExec("create definer=`root`@`127.0.0.1` view v1 as select * from t1") + tk.MustExec("create definer=`root`@`127.0.0.1` view v2 as select * from v1") + tk.MustQuery("plan replayer dump explain with tmp as (select a from t1 group by t1.a) select * from tmp, t2 where t2.a=tmp.a;") + tk.MustQuery("plan replayer dump explain select * from t1 where t1.a > (with cte1 as (select 1) select count(1) from cte1);") + tk.MustQuery("plan replayer dump explain select * from v1") + tk.MustQuery("plan replayer dump explain select * from v2") + require.True(t, len(tk.Session().GetSessionVars().LastPlanReplayerToken) > 0) + + // clear the status table and assert + tk.MustExec("delete from mysql.plan_replayer_status") + tk.MustQuery("plan replayer dump explain select * from v2") + token := tk.Session().GetSessionVars().LastPlanReplayerToken + rows := tk.MustQuery(fmt.Sprintf("select * from mysql.plan_replayer_status where token = '%v'", token)).Rows() + require.Len(t, rows, 1) +} + +func TestPlanReplayerCaptureSEM(t *testing.T) { + originSEM := config.GetGlobalConfig().Security.EnableSEM + defer func() { + config.GetGlobalConfig().Security.EnableSEM = originSEM + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("plan replayer capture '123' '123';") + tk.MustExec("create table t(id int)") + tk.MustQuery("plan replayer dump explain select * from t") + tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1")) +} + +func TestPlanReplayerCapture(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("plan replayer capture '123' '123';") + tk.MustQuery("select sql_digest, plan_digest from mysql.plan_replayer_task;").Check(testkit.Rows("123 123")) + tk.MustGetErrMsg("plan replayer capture '123' '123';", "plan replayer capture task already exists") + tk.MustExec("plan replayer capture remove '123' '123'") + tk.MustQuery("select count(*) from mysql.plan_replayer_task;").Check(testkit.Rows("0")) + tk.MustExec("create table t(id int)") + tk.MustExec("prepare stmt from 'update t set id = ? where id = ? + 1';") + tk.MustExec("SET @number = 5;") + tk.MustExec("execute stmt using @number,@number") + _, sqlDigest := tk.Session().GetSessionVars().StmtCtx.SQLDigest() + _, planDigest := tk.Session().GetSessionVars().StmtCtx.GetPlanDigest() + tk.MustExec("SET @@tidb_enable_plan_replayer_capture = ON;") + tk.MustExec("SET @@global.tidb_enable_historical_stats_for_capture='ON'") + tk.MustExec(fmt.Sprintf("plan replayer capture '%v' '%v'", sqlDigest.String(), planDigest.String())) + err := dom.GetPlanReplayerHandle().CollectPlanReplayerTask() + require.NoError(t, err) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/shouldDumpStats", "return(true)")) + defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/shouldDumpStats")) + tk.MustExec("execute stmt using @number,@number") + task := dom.GetPlanReplayerHandle().DrainTask() + require.NotNil(t, task) +} + +func TestPlanReplayerContinuesCapture(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set @@global.tidb_enable_historical_stats='OFF'") + _, err := tk.Exec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'") + require.Error(t, err) + require.Equal(t, err.Error(), "tidb_enable_historical_stats should be enabled before enabling tidb_enable_plan_replayer_continuous_capture") + + tk.MustExec("set @@global.tidb_enable_historical_stats='ON'") + tk.MustExec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'") + + prHandle := dom.GetPlanReplayerHandle() + tk.MustExec("delete from mysql.plan_replayer_status;") + tk.MustExec("use test") + tk.MustExec("create table t(id int);") + tk.MustExec("set @@tidb_enable_plan_replayer_continuous_capture = 'ON'") + tk.MustQuery("select * from t;") + task := prHandle.DrainTask() + require.NotNil(t, task) + worker := prHandle.GetWorker() + success := worker.HandleTask(task) + require.True(t, success) + tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1")) +} + +func TestShow(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database test_show;") + tk.MustExec("use test_show") + + tk.MustQuery("show engines") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key)") + require.Len(t, tk.MustQuery("show index in t").Rows(), 1) + require.Len(t, tk.MustQuery("show index from t").Rows(), 1) + require.Len(t, tk.MustQuery("show master status").Rows(), 1) + + tk.MustQuery("show create database test_show").Check(testkit.Rows("test_show CREATE DATABASE `test_show` /*!40100 DEFAULT CHARACTER SET utf8mb4 */")) + tk.MustQuery("show privileges").Check(testkit.Rows("Alter Tables To alter the table", + "Alter routine Functions,Procedures To alter or drop stored functions/procedures", + "Config Server Admin To use SHOW CONFIG and SET CONFIG statements", + "Create Databases,Tables,Indexes To create new databases and tables", + "Create routine Databases To use CREATE FUNCTION/PROCEDURE", + "Create role Server Admin To create new roles", + "Create temporary tables Databases To use CREATE TEMPORARY TABLE", + "Create view Tables To create new views", + "Create user Server Admin To create new users", + "Delete Tables To delete existing rows", + "Drop Databases,Tables To drop databases, tables, and views", + "Drop role Server Admin To drop roles", + "Event Server Admin To create, alter, drop and execute events", + "Execute Functions,Procedures To execute stored routines", + "File File access on server To read and write files on the server", + "Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess", + "Index Tables To create or drop indexes", + "Insert Tables To insert data into tables", + "Lock tables Databases To use LOCK TABLES (together with SELECT privilege)", + "Process Server Admin To view the plain text of currently executing queries", + "Proxy Server Admin To make proxy user possible", + "References Databases,Tables To have references on tables", + "Reload Server Admin To reload or refresh tables, logs and privileges", + "Replication client Server Admin To ask where the slave or master servers are", + "Replication slave Server Admin To read binary log events from the master", + "Select Tables To retrieve rows from table", + "Show databases Server Admin To see all databases with SHOW DATABASES", + "Show view Tables To see views with SHOW CREATE VIEW", + "Shutdown Server Admin To shut down the server", + "Super Server Admin To use KILL thread, SET GLOBAL, CHANGE MASTER, etc.", + "Trigger Tables To use triggers", + "Create tablespace Server Admin To create/alter/drop tablespaces", + "Update Tables To update existing rows", + "Usage Server Admin No privileges - allow connect only", + "BACKUP_ADMIN Server Admin ", + "RESTORE_ADMIN Server Admin ", + "SYSTEM_USER Server Admin ", + "SYSTEM_VARIABLES_ADMIN Server Admin ", + "ROLE_ADMIN Server Admin ", + "CONNECTION_ADMIN Server Admin ", + "PLACEMENT_ADMIN Server Admin ", + "DASHBOARD_CLIENT Server Admin ", + "RESTRICTED_TABLES_ADMIN Server Admin ", + "RESTRICTED_STATUS_ADMIN Server Admin ", + "RESTRICTED_VARIABLES_ADMIN Server Admin ", + "RESTRICTED_USER_ADMIN Server Admin ", + "RESTRICTED_CONNECTION_ADMIN Server Admin ", + "RESTRICTED_REPLICA_WRITER_ADMIN Server Admin ", + "RESOURCE_GROUP_ADMIN Server Admin ", + )) + require.Len(t, tk.MustQuery("show table status").Rows(), 1) +} + +// TestSelectBackslashN Issue 3685. +func TestSelectBackslashN(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + sql := `select \N;` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err := tk.Exec(sql) + require.NoError(t, err) + fields := rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "NULL", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select "\N";` + tk.MustQuery(sql).Check(testkit.Rows("N")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + tk.MustExec("use test;") + tk.MustExec("create table test (`\\N` int);") + tk.MustExec("insert into test values (1);") + tk.CheckExecResult(1, 0) + sql = "select * from test;" + tk.MustQuery(sql).Check(testkit.Rows("1")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `\N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select \N from test;` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.NoError(t, err) + require.Len(t, fields, 1) + require.Equal(t, `NULL`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select (\N) from test;` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `NULL`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select `\\N` from test;" + tk.MustQuery(sql).Check(testkit.Rows("1")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `\N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select (`\\N`) from test;" + tk.MustQuery(sql).Check(testkit.Rows("1")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `\N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select '\N' from test;` + tk.MustQuery(sql).Check(testkit.Rows("N")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select ('\N') from test;` + tk.MustQuery(sql).Check(testkit.Rows("N")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `N`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) +} + +// TestSelectNull Issue #4053. +func TestSelectNull(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + sql := `select nUll;` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err := tk.Exec(sql) + require.NoError(t, err) + fields := rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `NULL`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select (null);` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `NULL`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select null+NULL;` + tk.MustQuery(sql).Check(testkit.Rows("")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.NoError(t, err) + require.Len(t, fields, 1) + require.Equal(t, `null+NULL`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) +} + +// TestSelectStringLiteral Issue #3686. +func TestSelectStringLiteral(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + sql := `select 'abc';` + tk.MustQuery(sql).Check(testkit.Rows("abc")) + rs, err := tk.Exec(sql) + require.NoError(t, err) + fields := rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `abc`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select (('abc'));` + tk.MustQuery(sql).Check(testkit.Rows("abc")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `abc`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select 'abc'+'def';` + tk.MustQuery(sql).Check(testkit.Rows("0")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, `'abc'+'def'`, fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + // Below checks whether leading invalid chars are trimmed. + sql = "select '\n';" + tk.MustQuery(sql).Check(testkit.Rows("\n")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select '\t col';" // Lowercased letter is a valid char. + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "col", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select '\t Col';" // Uppercased letter is a valid char. + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "Col", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select '\n\t 中文 col';" // Chinese char is a valid char. + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "中文 col", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select ' \r\n .col';" // Punctuation is a valid char. + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, ".col", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = "select ' 😆col';" // Emoji is a valid char. + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "😆col", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + // Below checks whether trailing invalid chars are preserved. + sql = `select 'abc ';` + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "abc ", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select ' abc 123 ';` + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "abc 123 ", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + // Issue #4239. + sql = `select 'a' ' ' 'string';` + tk.MustQuery(sql).Check(testkit.Rows("a string")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "a", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select 'a' " " "string";` + tk.MustQuery(sql).Check(testkit.Rows("a string")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "a", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select 'string' 'string';` + tk.MustQuery(sql).Check(testkit.Rows("stringstring")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "string", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select "ss" "a";` + tk.MustQuery(sql).Check(testkit.Rows("ssa")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "ss", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select "ss" "a" "b";` + tk.MustQuery(sql).Check(testkit.Rows("ssab")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "ss", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select "ss" "a" ' ' "b";` + tk.MustQuery(sql).Check(testkit.Rows("ssa b")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "ss", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) + + sql = `select "ss" "a" ' ' "b" ' ' "d";` + tk.MustQuery(sql).Check(testkit.Rows("ssa b d")) + rs, err = tk.Exec(sql) + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "ss", fields[0].Column.Name.O) + require.NoError(t, rs.Close()) +} + +func TestUpdateClustered(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + type resultChecker struct { + check string + assert []string + } + + for _, clustered := range []string{"", "clustered"} { + tests := []struct { + initSchema []string + initData []string + dml string + resultCheck []resultChecker + }{ + { // left join + update both + match & unmatched + pk + []string{ + "drop table if exists a, b", + "create table a (k1 int, k2 int, v int)", + fmt.Sprintf("create table b (a int not null, k1 int, k2 int, v int, primary key(k1, k2) %s)", clustered), + }, + []string{ + "insert into a values (1, 1, 1), (2, 2, 2)", // unmatched + matched + "insert into b values (2, 2, 2, 2)", + }, + "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.v = 20, b.v = 100, a.k1 = a.k1 + 1, b.k1 = b.k1 + 1, a.k2 = a.k2 + 2, b.k2 = b.k2 + 2", + []resultChecker{ + { + "select * from b", + []string{"2 3 4 100"}, + }, + { + "select * from a", + []string{"2 3 20", "3 4 20"}, + }, + }, + }, + { // left join + update both + match & unmatched + pk + []string{ + "drop table if exists a, b", + "create table a (k1 int, k2 int, v int)", + fmt.Sprintf("create table b (a int not null, k1 int, k2 int, v int, primary key(k1, k2) %s)", clustered), + }, + []string{ + "insert into a values (1, 1, 1), (2, 2, 2)", // unmatched + matched + "insert into b values (2, 2, 2, 2)", + }, + "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", + []resultChecker{ + { + "select * from b", + []string{"2 3 4 100"}, + }, + { + "select * from a", + []string{"2 3 20", "3 4 20"}, + }, + }, + }, + { // left join + update both + match & unmatched + prefix pk + []string{ + "drop table if exists a, b", + "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", + fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), + }, + []string{ + "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched + "insert into b values ('22', '22', '22', '22')", + }, + "update a left join b on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", + []resultChecker{ + { + "select * from b", + []string{"22 23 24 100"}, + }, + { + "select * from a", + []string{"12 13 20", "23 24 20"}, + }, + }, + }, + { // right join + update both + match & unmatched + prefix pk + []string{ + "drop table if exists a, b", + "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", + fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), + }, + []string{ + "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched + "insert into b values ('22', '22', '22', '22')", + }, + "update b right join a on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", + []resultChecker{ + { + "select * from b", + []string{"22 23 24 100"}, + }, + { + "select * from a", + []string{"12 13 20", "23 24 20"}, + }, + }, + }, + { // inner join + update both + match & unmatched + prefix pk + []string{ + "drop table if exists a, b", + "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", + fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), + }, + []string{ + "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched + "insert into b values ('22', '22', '22', '22')", + }, + "update b join a on a.k1 = b.k1 and a.k2 = b.k2 set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, b.k1 = b.k1 + 1, b.k2 = b.k2 + 2, a.v = 20, b.v = 100", + []resultChecker{ + { + "select * from b", + []string{"22 23 24 100"}, + }, + { + "select * from a", + []string{"11 11 11", "23 24 20"}, + }, + }, + }, + { + []string{ + "drop table if exists a, b", + "create table a (k1 varchar(100), k2 varchar(100), v varchar(100))", + fmt.Sprintf("create table b (a varchar(100) not null, k1 varchar(100), k2 varchar(100), v varchar(100), primary key(k1(1), k2(1)) %s, key kk1(k1(1), v(1)))", clustered), + }, + []string{ + "insert into a values ('11', '11', '11'), ('22', '22', '22')", // unmatched + matched + "insert into b values ('22', '22', '22', '22')", + }, + "update a set a.k1 = a.k1 + 1, a.k2 = a.k2 + 2, a.v = 20 where exists (select 1 from b where a.k1 = b.k1 and a.k2 = b.k2)", + []resultChecker{ + { + "select * from b", + []string{"22 22 22 22"}, + }, + { + "select * from a", + []string{"11 11 11", "23 24 20"}, + }, + }, + }, + } + + for _, test := range tests { + for _, s := range test.initSchema { + tk.MustExec(s) + } + for _, s := range test.initData { + tk.MustExec(s) + } + tk.MustExec(test.dml) + for _, checker := range test.resultCheck { + tk.MustQuery(checker.check).Check(testkit.Rows(checker.assert...)) + } + tk.MustExec("admin check table a") + tk.MustExec("admin check table b") + } + } +} + +func TestClusterIndexOuterJoinElimination(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("create table t (a int, b int, c int, primary key(a,b))") + rows := tk.MustQuery(`explain format = 'brief' select t1.a from t t1 left join t t2 on t1.a = t2.a and t1.b = t2.b`).Rows() + rowStrs := testdata.ConvertRowsToStrings(rows) + for _, row := range rowStrs { + // outer join has been eliminated. + require.NotContains(t, row, "Join") + } +} + +func TestPlanReplayerDumpSingle(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_dump_single") + tk.MustExec("create table t_dump_single(a int)") + res := tk.MustQuery("plan replayer dump explain select * from t_dump_single") + path := testdata.ConvertRowsToStrings(res.Rows()) + + reader, err := zip.OpenReader(filepath.Join(replayer.GetPlanReplayerDirName(), path[0])) + require.NoError(t, err) + defer func() { require.NoError(t, reader.Close()) }() + for _, file := range reader.File { + require.True(t, checkFileName(file.Name), file.Name) + } +} + +func TestTimezonePushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (ts timestamp)") + defer tk.MustExec("drop table t") + tk.MustExec(`insert into t values ("2018-09-13 10:02:06")`) + + systemTZ := timeutil.SystemLocation() + require.NotEqual(t, "System", systemTZ.String()) + require.NotEqual(t, "Local", systemTZ.String()) + ctx := context.Background() + count := 0 + ctx1 := context.WithValue(ctx, "CheckSelectRequestHook", func(req *kv.Request) { + count++ + dagReq := new(tipb.DAGRequest) + require.NoError(t, proto.Unmarshal(req.Data, dagReq)) + require.Equal(t, systemTZ.String(), dagReq.GetTimeZoneName()) + }) + rs, err := tk.Session().Execute(ctx1, `select * from t where ts = "2018-09-13 10:02:06"`) + require.NoError(t, err) + rs[0].Close() + + tk.MustExec(`set time_zone="System"`) + rs, err = tk.Session().Execute(ctx1, `select * from t where ts = "2018-09-13 10:02:06"`) + require.NoError(t, err) + rs[0].Close() + + require.Equal(t, 2, count) // Make sure the hook function is called. +} + +func TestNotFillCacheFlag(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (id int primary key)") + tk.MustExec("insert into t values (1)") + + tests := []struct { + sql string + expect bool + }{ + {"select SQL_NO_CACHE * from t", true}, + {"select SQL_CACHE * from t", false}, + {"select * from t", false}, + } + count := 0 + ctx := context.Background() + for _, test := range tests { + ctx1 := context.WithValue(ctx, "CheckSelectRequestHook", func(req *kv.Request) { + count++ + comment := fmt.Sprintf("sql=%s, expect=%v, get=%v", test.sql, test.expect, req.NotFillCache) + require.Equal(t, test.expect, req.NotFillCache, comment) + }) + rs, err := tk.Session().Execute(ctx1, test.sql) + require.NoError(t, err) + tk.ResultSetToResult(rs[0], fmt.Sprintf("sql: %v", test.sql)) + } + require.Equal(t, len(tests), count) // Make sure the hook function is called. +} + +func TestExecutorBit(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (c1 bit(2))") + tk.MustExec("insert into t values (0), (1), (2), (3)") + err := tk.ExecToErr("insert into t values (4)") + require.Error(t, err) + err = tk.ExecToErr("insert into t values ('a')") + require.Error(t, err) + r, err := tk.Exec("select * from t where c1 = 2") + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(context.Background(), req) + require.NoError(t, err) + require.Equal(t, types.NewBinaryLiteralFromUint(2, -1), types.BinaryLiteral(req.GetRow(0).GetBytes(0))) + require.NoError(t, r.Close()) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(31))") + tk.MustExec("insert into t values (0x7fffffff)") + err = tk.ExecToErr("insert into t values (0x80000000)") + require.Error(t, err) + err = tk.ExecToErr("insert into t values (0xffffffff)") + require.Error(t, err) + tk.MustExec("insert into t values ('123')") + tk.MustExec("insert into t values ('1234')") + err = tk.ExecToErr("insert into t values ('12345)") + require.Error(t, err) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(62))") + tk.MustExec("insert into t values ('12345678')") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(61))") + err = tk.ExecToErr("insert into t values ('12345678')") + require.Error(t, err) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(32))") + tk.MustExec("insert into t values (0x7fffffff)") + tk.MustExec("insert into t values (0xffffffff)") + err = tk.ExecToErr("insert into t values (0x1ffffffff)") + require.Error(t, err) + tk.MustExec("insert into t values ('1234')") + err = tk.ExecToErr("insert into t values ('12345')") + require.Error(t, err) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(64))") + tk.MustExec("insert into t values (0xffffffffffffffff)") + tk.MustExec("insert into t values ('12345678')") + err = tk.ExecToErr("insert into t values ('123456789')") + require.Error(t, err) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 bit(64))") + tk.MustExec("insert into t values (0xffffffffffffffff)") + tk.MustExec("insert into t values ('12345678')") + tk.MustQuery("select * from t where c1").Check(testkit.Rows("\xff\xff\xff\xff\xff\xff\xff\xff", "12345678")) +} + +func TestCheckIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + ctx := mock.NewContext() + ctx.Store = store + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + + _, err = se.Execute(context.Background(), "create database test_admin") + require.NoError(t, err) + _, err = se.Execute(context.Background(), "use test_admin") + require.NoError(t, err) + _, err = se.Execute(context.Background(), "create table t (pk int primary key, c int default 1, c1 int default 1, unique key c(c))") + require.NoError(t, err) + + is := dom.InfoSchema() + db := model.NewCIStr("test_admin") + dbInfo, ok := is.SchemaByName(db) + require.True(t, ok) + + tblName := model.NewCIStr("t") + tbl, err := is.TableByName(db, tblName) + require.NoError(t, err) + tbInfo := tbl.Meta() + + alloc := autoid.NewAllocator(store, dbInfo.ID, tbInfo.ID, false, autoid.RowIDAllocType) + tb, err := tables.TableFromMeta(autoid.NewAllocators(false, alloc), tbInfo) + require.NoError(t, err) + + _, err = se.Execute(context.Background(), "admin check index t c") + require.NoError(t, err) + + _, err = se.Execute(context.Background(), "admin check index t C") + require.NoError(t, err) + + // set data to: + // index data (handle, data): (1, 10), (2, 20) + // table data (handle, data): (1, 10), (2, 20) + recordVal1 := types.MakeDatums(int64(1), int64(10), int64(11)) + recordVal2 := types.MakeDatums(int64(2), int64(20), int64(21)) + require.NoError(t, sessiontxn.NewTxn(context.Background(), ctx)) + _, err = tb.AddRecord(ctx, recordVal1) + require.NoError(t, err) + _, err = tb.AddRecord(ctx, recordVal2) + require.NoError(t, err) + txn, err := ctx.Txn(true) + require.NoError(t, err) + require.NoError(t, txn.Commit(context.Background())) + + mockCtx := mock.NewContext() + idx := tb.Indices()[0] + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + + _, err = se.Execute(context.Background(), "admin check index t idx_inexistent") + require.Error(t, err) + require.Contains(t, err.Error(), "not exist") + + // set data to: + // index data (handle, data): (1, 10), (2, 20), (3, 30) + // table data (handle, data): (1, 10), (2, 20), (4, 40) + txn, err = store.Begin() + require.NoError(t, err) + _, err = idx.Create(mockCtx, txn, types.MakeDatums(int64(30)), kv.IntHandle(3), nil) + require.NoError(t, err) + key := tablecodec.EncodeRowKey(tb.Meta().ID, kv.IntHandle(4).Encoded()) + setColValue(t, txn, key, types.NewDatum(int64(40))) + err = txn.Commit(context.Background()) + require.NoError(t, err) + _, err = se.Execute(context.Background(), "admin check index t c") + require.Error(t, err) + require.Equal(t, "[admin:8223]data inconsistency in table: t, index: c, handle: 3, index-values:\"handle: 3, values: [KindInt64 30]\" != record-values:\"\"", err.Error()) + + // set data to: + // index data (handle, data): (1, 10), (2, 20), (3, 30), (4, 40) + // table data (handle, data): (1, 10), (2, 20), (4, 40) + txn, err = store.Begin() + require.NoError(t, err) + _, err = idx.Create(mockCtx, txn, types.MakeDatums(int64(40)), kv.IntHandle(4), nil) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + _, err = se.Execute(context.Background(), "admin check index t c") + require.Error(t, err) + require.EqualError(t, err, "[admin:8223]data inconsistency in table: t, index: c, handle: 3, index-values:\"handle: 3, values: [KindInt64 30]\" != record-values:\"\"") + + // set data to: + // index data (handle, data): (1, 10), (4, 40) + // table data (handle, data): (1, 10), (2, 20), (4, 40) + txn, err = store.Begin() + require.NoError(t, err) + err = idx.Delete(sc, txn, types.MakeDatums(int64(30)), kv.IntHandle(3)) + require.NoError(t, err) + err = idx.Delete(sc, txn, types.MakeDatums(int64(20)), kv.IntHandle(2)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + _, err = se.Execute(context.Background(), "admin check index t c") + require.Error(t, err) + require.EqualError(t, err, "[admin:8223]data inconsistency in table: t, index: c, handle: 2, index-values:\"\" != record-values:\"handle: 2, values: [KindInt64 20]\"") + + // TODO: pass the case below: + // set data to: + // index data (handle, data): (1, 10), (4, 40), (2, 30) + // table data (handle, data): (1, 10), (2, 20), (4, 40) +} + +func setColValue(t *testing.T, txn kv.Transaction, key kv.Key, v types.Datum) { + row := []types.Datum{v, {}} + colIDs := []int64{2, 3} + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + rd := rowcodec.Encoder{Enable: true} + value, err := tablecodec.EncodeRow(sc, row, colIDs, nil, nil, &rd) + require.NoError(t, err) + err = txn.Set(key, value) + require.NoError(t, err) +} + +func TestTimestampTimeZone(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (ts timestamp)") + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("insert into t values ('2017-04-27 22:40:42')") + // The timestamp will get different value if time_zone session variable changes. + tests := []struct { + timezone string + expect string + }{ + {"+10:00", "2017-04-28 08:40:42"}, + {"-6:00", "2017-04-27 16:40:42"}, + } + for _, tt := range tests { + tk.MustExec(fmt.Sprintf("set time_zone = '%s'", tt.timezone)) + tk.MustQuery("select * from t").Check(testkit.Rows(tt.expect)) + } + + // For issue https://github.com/pingcap/tidb/issues/3467 + tk.MustExec("drop table if exists t1") + tk.MustExec(`CREATE TABLE t1 ( + id bigint(20) NOT NULL AUTO_INCREMENT, + uid int(11) DEFAULT NULL, + datetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + ip varchar(128) DEFAULT NULL, + PRIMARY KEY (id), + KEY i_datetime (datetime), + KEY i_userid (uid) + );`) + tk.MustExec(`INSERT INTO t1 VALUES (123381351,1734,"2014-03-31 08:57:10","127.0.0.1");`) + r := tk.MustQuery("select datetime from t1;") // Cover TableReaderExec + r.Check(testkit.Rows("2014-03-31 08:57:10")) + r = tk.MustQuery("select datetime from t1 where datetime='2014-03-31 08:57:10';") + r.Check(testkit.Rows("2014-03-31 08:57:10")) // Cover IndexReaderExec + r = tk.MustQuery("select * from t1 where datetime='2014-03-31 08:57:10';") + r.Check(testkit.Rows("123381351 1734 2014-03-31 08:57:10 127.0.0.1")) // Cover IndexLookupExec + + // For issue https://github.com/pingcap/tidb/issues/3485 + tk.MustExec("set time_zone = 'Asia/Shanghai'") + tk.MustExec("drop table if exists t1") + tk.MustExec(`CREATE TABLE t1 ( + id bigint(20) NOT NULL AUTO_INCREMENT, + datetime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) + );`) + tk.MustExec(`INSERT INTO t1 VALUES (123381351,"2014-03-31 08:57:10");`) + r = tk.MustQuery(`select * from t1 where datetime="2014-03-31 08:57:10";`) + r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) + tk.MustExec(`alter table t1 add key i_datetime (datetime);`) + r = tk.MustQuery(`select * from t1 where datetime="2014-03-31 08:57:10";`) + r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) + r = tk.MustQuery(`select * from t1;`) + r.Check(testkit.Rows("123381351 2014-03-31 08:57:10")) + r = tk.MustQuery("select datetime from t1 where datetime='2014-03-31 08:57:10';") + r.Check(testkit.Rows("2014-03-31 08:57:10")) +} + +func TestTimestampDefaultValueTimeZone(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set time_zone = '+08:00'") + tk.MustExec(`create table t (a int, b timestamp default "2019-01-17 14:46:14")`) + tk.MustExec("insert into t set a=1") + r := tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 14:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("insert into t set a=2") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-17 06:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 2019-01-17 06:46:14", "2 2019-01-17 06:46:14")) + // Test the column's version is greater than ColumnInfoVersion1. + is := domain.GetDomain(tk.Session()).InfoSchema() + require.NotNil(t, is) + tb, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tb.Cols()[1].Version = model.ColumnInfoVersion1 + 1 + tk.MustExec("insert into t set a=3") + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 2019-01-17 06:46:14", "2 2019-01-17 06:46:14", "3 2019-01-17 06:46:14")) + tk.MustExec("delete from t where a=3") + // Change time zone back. + tk.MustExec("set time_zone = '+08:00'") + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 2019-01-17 14:46:14", "2 2019-01-17 14:46:14")) + tk.MustExec("set time_zone = '-08:00'") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '2019-01-16 22:46:14'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // test zero default value in multiple time zone. + defer tk.MustExec(fmt.Sprintf("set @@sql_mode='%s'", tk.MustQuery("select @@sql_mode").Rows()[0][0])) + tk.MustExec("set @@sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';") + tk.MustExec("drop table if exists t") + tk.MustExec("set time_zone = '+08:00'") + tk.MustExec(`create table t (a int, b timestamp default "0000-00-00 00")`) + tk.MustExec("insert into t set a=1") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("insert into t set a=2") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec("set time_zone = '-08:00'") + tk.MustExec("insert into t set a=3") + r = tk.MustQuery(`show create table t`) + r.Check(testkit.Rows("t CREATE TABLE `t` (\n" + " `a` int(11) DEFAULT NULL,\n" + " `b` timestamp DEFAULT '0000-00-00 00:00:00'\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + r = tk.MustQuery(`select a,b from t order by a`) + r.Check(testkit.Rows("1 0000-00-00 00:00:00", "2 0000-00-00 00:00:00", "3 0000-00-00 00:00:00")) + + // test add timestamp column default current_timestamp. + tk.MustExec(`drop table if exists t`) + tk.MustExec(`set time_zone = 'Asia/Shanghai'`) + tk.MustExec(`create table t (a int)`) + tk.MustExec(`insert into t set a=1`) + tk.MustExec(`alter table t add column b timestamp not null default current_timestamp;`) + timeIn8 := tk.MustQuery("select b from t").Rows()[0][0] + tk.MustExec(`set time_zone = '+00:00'`) + timeIn0 := tk.MustQuery("select b from t").Rows()[0][0] + require.NotEqual(t, timeIn8, timeIn0) + datumTimeIn8, err := expression.GetTimeValue(tk.Session(), timeIn8, mysql.TypeTimestamp, 0, nil) + require.NoError(t, err) + tIn8To0 := datumTimeIn8.GetMysqlTime() + timeZoneIn8, err := time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + err = tIn8To0.ConvertTimeZone(timeZoneIn8, time.UTC) + require.NoError(t, err) + require.Equal(t, tIn8To0.String(), timeIn0) + + // test add index. + tk.MustExec(`alter table t add index(b);`) + tk.MustExec("admin check table t") + tk.MustExec(`set time_zone = '+05:00'`) + tk.MustExec("admin check table t") + + // 1. add a timestamp general column + // 2. add the index + tk.MustExec(`drop table if exists t`) + // change timezone + tk.MustExec(`set time_zone = 'Asia/Shanghai'`) + tk.MustExec(`create table t(a timestamp default current_timestamp)`) + tk.MustExec(`insert into t set a="20220413154712"`) + tk.MustExec(`alter table t add column b timestamp as (a+1) virtual;`) + // change timezone + tk.MustExec(`set time_zone = '+05:00'`) + tk.MustExec(`insert into t set a="20220413154840"`) + tk.MustExec(`alter table t add index(b);`) + tk.MustExec("admin check table t") + tk.MustExec(`set time_zone = '-03:00'`) + tk.MustExec("admin check table t") +} + +func TestTiDBCurrentTS(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + tk.MustExec("begin") + rows := tk.MustQuery("select @@tidb_current_ts").Rows() + tsStr := rows[0][0].(string) + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("%d", txn.StartTS()), tsStr) + tk.MustExec("begin") + rows = tk.MustQuery("select @@tidb_current_ts").Rows() + newTsStr := rows[0][0].(string) + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, fmt.Sprintf("%d", txn.StartTS()), newTsStr) + require.NotEqual(t, tsStr, newTsStr) + tk.MustExec("commit") + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + + err = tk.ExecToErr("set @@tidb_current_ts = '1'") + require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err: %v", err)) +} + +func TestTiDBLastTxnInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int primary key)") + tk.MustQuery("select @@tidb_last_txn_info").Check(testkit.Rows("")) + + tk.MustExec("insert into t values (1)") + rows1 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() + require.Greater(t, rows1[0][0].(string), "0") + require.Less(t, rows1[0][0].(string), rows1[0][1].(string)) + + tk.MustExec("begin") + tk.MustQuery("select a from t where a = 1").Check(testkit.Rows("1")) + rows2 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), @@tidb_current_ts").Rows() + tk.MustExec("commit") + rows3 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() + require.Equal(t, rows1[0][0], rows2[0][0]) + require.Equal(t, rows1[0][1], rows2[0][1]) + require.Equal(t, rows1[0][0], rows3[0][0]) + require.Equal(t, rows1[0][1], rows3[0][1]) + require.Less(t, rows2[0][1], rows2[0][2]) + + tk.MustExec("begin") + tk.MustExec("update t set a = a + 1 where a = 1") + rows4 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), @@tidb_current_ts").Rows() + tk.MustExec("commit") + rows5 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() + require.Equal(t, rows1[0][0], rows4[0][0]) + require.Equal(t, rows1[0][1], rows4[0][1]) + require.Equal(t, rows5[0][0], rows4[0][2]) + require.Less(t, rows4[0][1], rows4[0][2]) + require.Less(t, rows4[0][2], rows5[0][1]) + + tk.MustExec("begin") + tk.MustExec("update t set a = a + 1 where a = 2") + tk.MustExec("rollback") + rows6 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() + require.Equal(t, rows5[0][0], rows6[0][0]) + require.Equal(t, rows5[0][1], rows6[0][1]) + + tk.MustExec("begin optimistic") + tk.MustExec("insert into t values (2)") + err := tk.ExecToErr("commit") + require.Error(t, err) + rows7 := tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts'), json_extract(@@tidb_last_txn_info, '$.error')").Rows() + require.Greater(t, rows7[0][0], rows5[0][0]) + require.Equal(t, "0", rows7[0][1]) + require.Contains(t, err.Error(), rows7[0][1]) + + err = tk.ExecToErr("set @@tidb_last_txn_info = '{}'") + require.True(t, terror.ErrorEqual(err, variable.ErrIncorrectScope), fmt.Sprintf("err: %v", err)) +} + +func TestTiDBLastQueryInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int primary key, v int)") + tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.start_ts')").Check(testkit.Rows("0 0")) + + toUint64 := func(str interface{}) uint64 { + res, err := strconv.ParseUint(str.(string), 10, 64) + require.NoError(t, err) + return res + } + + tk.MustExec("select * from t") + rows := tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Equal(t, rows[0][1], rows[0][0]) + + tk.MustExec("insert into t values (1, 10)") + rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Equal(t, rows[0][1], rows[0][0]) + // tidb_last_txn_info is still valid after checking query info. + rows = tk.MustQuery("select json_extract(@@tidb_last_txn_info, '$.start_ts'), json_extract(@@tidb_last_txn_info, '$.commit_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Less(t, rows[0][0].(string), rows[0][1].(string)) + + tk.MustExec("begin pessimistic") + tk.MustExec("select * from t") + rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Equal(t, rows[0][1], rows[0][0]) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("update t set v = 11 where a = 1") + + tk.MustExec("select * from t") + rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Equal(t, rows[0][1], rows[0][0]) + + tk.MustExec("update t set v = 12 where a = 1") + rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Less(t, toUint64(rows[0][0]), toUint64(rows[0][1])) + + tk.MustExec("commit") + + tk.MustExec("set transaction isolation level read committed") + tk.MustExec("begin pessimistic") + tk.MustExec("select * from t") + rows = tk.MustQuery("select json_extract(@@tidb_last_query_info, '$.start_ts'), json_extract(@@tidb_last_query_info, '$.for_update_ts')").Rows() + require.Greater(t, toUint64(rows[0][0]), uint64(0)) + require.Less(t, toUint64(rows[0][0]), toUint64(rows[0][1])) + + tk.MustExec("rollback") +} + +func TestSelectForUpdate(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + tk.MustExec("drop table if exists t, t1") + + txn, err := tk.Session().Txn(true) + require.True(t, kv.ErrInvalidTxn.Equal(err)) + require.False(t, txn.Valid()) + tk.MustExec("create table t (c1 int, c2 int, c3 int)") + tk.MustExec("insert t values (11, 2, 3)") + tk.MustExec("insert t values (12, 2, 3)") + tk.MustExec("insert t values (13, 2, 3)") + + tk.MustExec("create table t1 (c1 int)") + tk.MustExec("insert t1 values (11)") + + // conflict + tk1.MustExec("begin") + tk1.MustQuery("select * from t where c1=11 for update") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=211 where c1=11") + tk2.MustExec("commit") + + err = tk1.ExecToErr("commit") + require.Error(t, err) + + // no conflict for subquery. + tk1.MustExec("begin") + tk1.MustQuery("select * from t where exists(select null from t1 where t1.c1=t.c1) for update") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=211 where c1=12") + tk2.MustExec("commit") + + tk1.MustExec("commit") + + // not conflict + tk1.MustExec("begin") + tk1.MustQuery("select * from t where c1=11 for update") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=22 where c1=12") + tk2.MustExec("commit") + + tk1.MustExec("commit") + + // not conflict, auto commit + tk1.MustExec("set @@autocommit=1;") + tk1.MustQuery("select * from t where c1=11 for update") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=211 where c1=11") + tk2.MustExec("commit") + + tk1.MustExec("commit") + + // conflict + tk1.MustExec("begin") + tk1.MustQuery("select * from (select * from t for update) t join t1 for update") + + tk2.MustExec("begin") + tk2.MustExec("update t1 set c1 = 13") + tk2.MustExec("commit") + + err = tk1.ExecToErr("commit") + require.Error(t, err) +} + +func TestSelectForUpdateOf(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t (i int)") + tk.MustExec("create table t1 (i int)") + tk.MustExec("insert t values (1)") + tk.MustExec("insert t1 values (1)") + + tk.MustExec("begin pessimistic") + tk.MustQuery("select * from t, t1 where t.i = t1.i for update of t").Check(testkit.Rows("1 1")) + + tk1.MustExec("begin pessimistic") + + // no lock for t + tk1.MustQuery("select * from t1 for update").Check(testkit.Rows("1")) + + // meet lock for t1 + err := tk1.ExecToErr("select * from t for update nowait") + require.True(t, terror.ErrorEqual(err, error2.ErrLockAcquireFailAndNoWaitSet), fmt.Sprintf("err: %v", err)) + + // t1 rolled back, tk1 acquire the lock + tk.MustExec("rollback") + tk1.MustQuery("select * from t for update nowait").Check(testkit.Rows("1")) + + tk1.MustExec("rollback") +} + +func TestPartitionHashCode(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t(c1 bigint, c2 bigint, c3 bigint, primary key(c1)) partition by hash (c1) partitions 4;`) + var wg util.WaitGroupWrapper + for i := 0; i < 5; i++ { + wg.Run(func() { + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + for i := 0; i < 5; i++ { + tk1.MustExec("select * from t") + } + }) + } + wg.Wait() +} + +// this is from jira issue #5856 +func TestInsertValuesWithSubQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2(a int, b int, c int)") + defer tk.MustExec("drop table if exists t2") + + // should not reference upper scope + require.Error(t, tk.ExecToErr("insert into t2 values (11, 8, (select not b))")) + require.Error(t, tk.ExecToErr("insert into t2 set a = 11, b = 8, c = (select b))")) + + // subquery reference target table is allowed + tk.MustExec("insert into t2 values(1, 1, (select b from t2))") + tk.MustQuery("select * from t2").Check(testkit.Rows("1 1 ")) + tk.MustExec("insert into t2 set a = 1, b = 1, c = (select b+1 from t2)") + tk.MustQuery("select * from t2").Check(testkit.Rows("1 1 ", "1 1 2")) + + // insert using column should work normally + tk.MustExec("delete from t2") + tk.MustExec("insert into t2 values(2, 4, a)") + tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2")) + tk.MustExec("insert into t2 set a = 3, b = 5, c = b") + tk.MustQuery("select * from t2").Check(testkit.Rows("2 4 2", "3 5 5")) + + // issue #30626 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + // TODO: should insert success and get (81,1) from the table + tk.MustGetErrMsg( + "insert into t values ( 81, ( select ( SELECT '1' AS `c0` WHERE '1' >= `subq_0`.`c0` ) as `c1` FROM ( SELECT '1' AS `c0` ) AS `subq_0` ) );", + "Insert's SET operation or VALUES_LIST doesn't support complex subqueries now") + tk.MustGetErrMsg( + "insert into t set a = 81, b = (select ( SELECT '1' AS `c0` WHERE '1' >= `subq_0`.`c0` ) as `c1` FROM ( SELECT '1' AS `c0` ) AS `subq_0` );", + "Insert's SET operation or VALUES_LIST doesn't support complex subqueries now") +} + +// fix issue https://github.com/pingcap/tidb/issues/32871 +func TestBitColumnIn(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (id bit(16), key id(id))") + tk.MustExec("insert into t values (65)") + tk.MustQuery("select * from t where id not in (-1,2)").Check(testkit.Rows("\x00A")) + tk.MustGetErrMsg( + "select * from t where id in (-1, -2)", + "[expression:1582]Incorrect parameter count in the call to native function 'in'") +} + +func TestIndexLookupRuntimeStats(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int, index(a))") + tk.MustExec("insert into t1 values (1,2),(2,3),(3,4)") + rows := tk.MustQuery("explain analyze select * from t1 use index(a) where a > 1").Rows() + require.Len(t, rows, 3) + explain := fmt.Sprintf("%v", rows[0]) + require.Regexp(t, ".*time:.*loops:.*index_task:.*table_task: {total_time.*num.*concurrency.*}.*", explain) + indexExplain := fmt.Sprintf("%v", rows[1]) + tableExplain := fmt.Sprintf("%v", rows[2]) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) +} + +func TestHashAggRuntimeStats(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int)") + tk.MustExec("insert into t1 values (1,2),(2,3),(3,4)") + rows := tk.MustQuery("explain analyze SELECT /*+ HASH_AGG() */ count(*) FROM t1 WHERE a < 10;").Rows() + require.Len(t, rows, 5) + explain := fmt.Sprintf("%v", rows[0]) + pattern := ".*time:.*loops:.*partial_worker:{wall_time:.*concurrency:.*task_num:.*tot_wait:.*tot_exec:.*tot_time:.*max:.*p95:.*}.*final_worker:{wall_time:.*concurrency:.*task_num:.*tot_wait:.*tot_exec:.*tot_time:.*max:.*p95:.*}.*" + require.Regexp(t, pattern, explain) +} + +func TestIndexMergeRuntimeStats(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_index_merge = 1") + tk.MustExec("create table t1(id int primary key, a int, b int, c int, d int)") + tk.MustExec("create index t1a on t1(a)") + tk.MustExec("create index t1b on t1(b)") + tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") + rows := tk.MustQuery("explain analyze select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4;").Rows() + require.Len(t, rows, 4) + explain := fmt.Sprintf("%v", rows[0]) + pattern := ".*time:.*loops:.*index_task:{fetch_handle:.*, merge:.*}.*table_task:{num.*concurrency.*fetch_row.*wait_time.*}.*" + require.Regexp(t, pattern, explain) + tableRangeExplain := fmt.Sprintf("%v", rows[1]) + indexExplain := fmt.Sprintf("%v", rows[2]) + tableExplain := fmt.Sprintf("%v", rows[3]) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableRangeExplain) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustQuery("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by a").Check(testkit.Rows("1 1 1 1 1", "5 5 5 5 5")) +} + +func TestPrevStmtDesensitization(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec(fmt.Sprintf("set @@session.%v=1", variable.TiDBRedactLog)) + defer tk.MustExec(fmt.Sprintf("set @@session.%v=0", variable.TiDBRedactLog)) + tk.MustExec("create table t (a int, unique key (a))") + tk.MustExec("begin") + tk.MustExec("insert into t values (1),(2)") + require.Equal(t, "insert into `t` values ( ... )", tk.Session().GetSessionVars().PrevStmt.String()) + tk.MustGetErrMsg("insert into t values (1)", `[kv:1062]Duplicate entry '?' for key 't.a'`) +} + +func TestIssue19148(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a decimal(16, 2));") + tk.MustExec("select * from t where a > any_value(a);") + is := domain.GetDomain(tk.Session()).InfoSchema() + tblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + require.Zero(t, tblInfo.Meta().Columns[0].GetFlag()) +} + +func TestOOMActionPriority(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t0") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t3") + tk.MustExec("drop table if exists t4") + tk.MustExec("create table t0(a int)") + tk.MustExec("insert into t0 values(1)") + tk.MustExec("create table t1(a int)") + tk.MustExec("insert into t1 values(1)") + tk.MustExec("create table t2(a int)") + tk.MustExec("insert into t2 values(1)") + tk.MustExec("create table t3(a int)") + tk.MustExec("insert into t3 values(1)") + tk.MustExec("create table t4(a int)") + tk.MustExec("insert into t4 values(1)") + tk.MustQuery("select * from t0 join t1 join t2 join t3 join t4 order by t0.a").Check(testkit.Rows("1 1 1 1 1")) + action := tk.Session().GetSessionVars().StmtCtx.MemTracker.GetFallbackForTest(true) + // All actions are finished and removed. + require.Equal(t, action.GetPriority(), int64(memory.DefLogPriority)) +} + +func TestTrackAggMemoryUsage(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1)") + tk.MustExec("set tidb_track_aggregate_memory_usage = off;") + rows := tk.MustQuery("explain analyze select /*+ HASH_AGG() */ sum(a) from t").Rows() + require.Equal(t, "N/A", rows[0][7]) + rows = tk.MustQuery("explain analyze select /*+ STREAM_AGG() */ sum(a) from t").Rows() + require.Equal(t, "N/A", rows[0][7]) + tk.MustExec("set tidb_track_aggregate_memory_usage = on;") + rows = tk.MustQuery("explain analyze select /*+ HASH_AGG() */ sum(a) from t").Rows() + require.NotEqual(t, "N/A", rows[0][7]) + rows = tk.MustQuery("explain analyze select /*+ STREAM_AGG() */ sum(a) from t").Rows() + require.NotEqual(t, "N/A", rows[0][7]) +} + +func TestProjectionBitType(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(k1 int, v bit(34) DEFAULT b'111010101111001001100111101111111', primary key(k1) clustered);") + tk.MustExec("create table t1(k1 int, v bit(34) DEFAULT b'111010101111001001100111101111111', primary key(k1) nonclustered);") + tk.MustExec("insert into t(k1) select 1;") + tk.MustExec("insert into t1(k1) select 1;") + + tk.MustExec("set @@tidb_enable_vectorized_expression = 0;") + // following SQL should returns same result + tk.MustQuery("(select * from t where false) union(select * from t for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) + tk.MustQuery("(select * from t1 where false) union(select * from t1 for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) + + tk.MustExec("set @@tidb_enable_vectorized_expression = 1;") + tk.MustQuery("(select * from t where false) union(select * from t for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) + tk.MustQuery("(select * from t1 where false) union(select * from t1 for update);").Check(testkit.Rows("1 \x01\xd5\xe4\xcf\u007f")) +} + +func TestExprBlackListForEnum(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a enum('a','b','c'), b enum('a','b','c'), c int, index idx(b,a));") + tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3);") + + checkFuncPushDown := func(rows [][]interface{}, keyWord string) bool { + for _, line := range rows { + // Agg/Expr push down + if line[2].(string) == "cop[tikv]" && strings.Contains(line[4].(string), keyWord) { + return true + } + // access index + if line[2].(string) == "cop[tikv]" && strings.Contains(line[3].(string), keyWord) { + return true + } + } + return false + } + + // Test agg(enum) push down + tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows := tk.MustQuery("desc format='brief' select /*+ HASH_AGG() */ max(a) from t;").Rows() + require.False(t, checkFuncPushDown(rows, "max")) + rows = tk.MustQuery("desc format='brief' select /*+ STREAM_AGG() */ max(a) from t;").Rows() + require.False(t, checkFuncPushDown(rows, "max")) + + tk.MustExec("delete from mysql.expr_pushdown_blacklist;") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = tk.MustQuery("desc format='brief' select /*+ HASH_AGG() */ max(a) from t;").Rows() + require.True(t, checkFuncPushDown(rows, "max")) + rows = tk.MustQuery("desc format='brief' select /*+ STREAM_AGG() */ max(a) from t;").Rows() + require.True(t, checkFuncPushDown(rows, "max")) + + // Test expr(enum) push down + tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() + require.False(t, checkFuncPushDown(rows, "plus")) + rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() + require.False(t, checkFuncPushDown(rows, "plus")) + + tk.MustExec("delete from mysql.expr_pushdown_blacklist;") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() + require.True(t, checkFuncPushDown(rows, "plus")) + rows = tk.MustQuery("desc format='brief' select * from t where a + b;").Rows() + require.True(t, checkFuncPushDown(rows, "plus")) + + // Test enum index + tk.MustExec("insert into mysql.expr_pushdown_blacklist(name) values('enum');") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = tk.MustQuery("desc format='brief' select * from t where b = 1;").Rows() + require.False(t, checkFuncPushDown(rows, "index:idx(b)")) + rows = tk.MustQuery("desc format='brief' select * from t where b = 'a';").Rows() + require.False(t, checkFuncPushDown(rows, "index:idx(b)")) + rows = tk.MustQuery("desc format='brief' select * from t where b > 1;").Rows() + require.False(t, checkFuncPushDown(rows, "index:idx(b)")) + rows = tk.MustQuery("desc format='brief' select * from t where b > 'a';").Rows() + require.False(t, checkFuncPushDown(rows, "index:idx(b)")) + + tk.MustExec("delete from mysql.expr_pushdown_blacklist;") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a = 1;").Rows() + require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) + rows = tk.MustQuery("desc format='brief' select * from t where b = 'a' and a = 'a';").Rows() + require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) + rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a > 1;").Rows() + require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) + rows = tk.MustQuery("desc format='brief' select * from t where b = 1 and a > 'a'").Rows() + require.True(t, checkFuncPushDown(rows, "index:idx(b, a)")) +} + +// Test invoke Close without invoking Open before for each operators. +func TestUnreasonablyClose(t *testing.T) { + store := testkit.CreateMockStore(t) + + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable(), plannercore.MockUnsignedTable()}) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + // To enable the shuffleExec operator. + tk.MustExec("set @@tidb_merge_join_concurrency=4") + + var opsNeedsCovered = []plannercore.PhysicalPlan{ + &plannercore.PhysicalHashJoin{}, + &plannercore.PhysicalMergeJoin{}, + &plannercore.PhysicalIndexJoin{}, + &plannercore.PhysicalIndexHashJoin{}, + &plannercore.PhysicalTableReader{}, + &plannercore.PhysicalIndexReader{}, + &plannercore.PhysicalIndexLookUpReader{}, + &plannercore.PhysicalIndexMergeReader{}, + &plannercore.PhysicalApply{}, + &plannercore.PhysicalHashAgg{}, + &plannercore.PhysicalStreamAgg{}, + &plannercore.PhysicalLimit{}, + &plannercore.PhysicalSort{}, + &plannercore.PhysicalTopN{}, + &plannercore.PhysicalCTE{}, + &plannercore.PhysicalCTETable{}, + &plannercore.PhysicalMaxOneRow{}, + &plannercore.PhysicalProjection{}, + &plannercore.PhysicalSelection{}, + &plannercore.PhysicalTableDual{}, + &plannercore.PhysicalWindow{}, + &plannercore.PhysicalShuffle{}, + &plannercore.PhysicalUnionAll{}, + } + + opsNeedsCoveredMask := uint64(1< t1.a) AS a from t as t1) t", + "select /*+ hash_agg() */ count(f) from t group by a", + "select /*+ stream_agg() */ count(f) from t", + "select * from t order by a, f", + "select * from t order by a, f limit 1", + "select * from t limit 1", + "select (select t1.a from t t1 where t1.a > t2.a) as a from t t2;", + "select a + 1 from t", + "select count(*) a from t having a > 1", + "select * from t where a = 1.1", + "with recursive cte1(c1) as (select 1 union select c1 + 1 from cte1 limit 5 offset 0) select * from cte1", + "select /*+use_index_merge(t, c_d_e, f)*/ * from t where c < 1 or f > 2", + "select sum(f) over (partition by f) from t", + "select /*+ merge_join(t1)*/ * from t t1 join t t2 on t1.d = t2.d", + "select a from t union all select a from t", + } { + comment := fmt.Sprintf("case:%v sql:%s", i, tc) + stmt, err := p.ParseOneStmt(tc, "", "") + require.NoError(t, err, comment) + err = sessiontxn.NewTxn(context.Background(), tk.Session()) + require.NoError(t, err, comment) + + err = sessiontxn.GetTxnManager(tk.Session()).OnStmtStart(context.TODO(), stmt) + require.NoError(t, err, comment) + + executorBuilder := executor.NewMockExecutorBuilderForTest(tk.Session(), is, nil) + + p, _, _ := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NotNil(t, p) + + // This for loop level traverses the plan tree to get which operators are covered. + var hasCTE bool + for child := []plannercore.PhysicalPlan{p.(plannercore.PhysicalPlan)}; len(child) != 0; { + newChild := make([]plannercore.PhysicalPlan, 0, len(child)) + for _, ch := range child { + found := false + for k, t := range opsNeedsCovered { + if reflect.TypeOf(t) == reflect.TypeOf(ch) { + opsAlreadyCoveredMask |= 1 << k + found = true + break + } + } + require.True(t, found, fmt.Sprintf("case: %v sql: %s operator %v is not registered in opsNeedsCoveredMask", i, tc, reflect.TypeOf(ch))) + switch x := ch.(type) { + case *plannercore.PhysicalCTE: + newChild = append(newChild, x.RecurPlan) + newChild = append(newChild, x.SeedPlan) + hasCTE = true + continue + case *plannercore.PhysicalShuffle: + newChild = append(newChild, x.DataSources...) + newChild = append(newChild, x.Tails...) + continue + } + newChild = append(newChild, ch.Children()...) + } + child = newChild + } + + if hasCTE { + // Normally CTEStorages will be setup in ResetContextOfStmt. + // But the following case call e.Close() directly, instead of calling session.ExecStmt(), which calls ResetContextOfStmt. + // So need to setup CTEStorages manually. + tk.Session().GetSessionVars().StmtCtx.CTEStorageMap = map[int]*executor.CTEStorages{} + } + e := executorBuilder.Build(p) + + func() { + defer func() { + r := recover() + buf := make([]byte, 4096) + stackSize := runtime.Stack(buf, false) + buf = buf[:stackSize] + require.Nil(t, r, fmt.Sprintf("case: %v\n sql: %s\n error stack: %v", i, tc, string(buf))) + }() + require.NoError(t, e.Close(), comment) + }() + } + // The following code is used to make sure all the operators registered + // in opsNeedsCoveredMask are covered. + commentBuf := strings.Builder{} + if opsAlreadyCoveredMask != opsNeedsCoveredMask { + for i := range opsNeedsCovered { + if opsAlreadyCoveredMask&(1< t1.a) AS a from t as t1) t;") + require.Contains(t, result.Rows()[1][0], "Apply") + var ( + ind int + flag bool + ) + value := (result.Rows()[1][5]).(string) + for ind = 0; ind < len(value)-5; ind++ { + if value[ind:ind+5] == "cache" { + flag = true + break + } + } + require.True(t, flag) + require.Equal(t, "cache:ON, cacheHitRatio:88.889%", value[ind:]) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values (1),(2),(3),(4),(5),(6),(7),(8),(9);") + tk.MustExec("analyze table t;") + result = tk.MustQuery("explain analyze SELECT count(1) FROM (SELECT (SELECT min(a) FROM t as t2 WHERE t2.a > t1.a) AS a from t as t1) t;") + require.Contains(t, result.Rows()[1][0], "Apply") + flag = false + value = (result.Rows()[1][5]).(string) + for ind = 0; ind < len(value)-5; ind++ { + if value[ind:ind+5] == "cache" { + flag = true + break + } + } + require.True(t, flag) + require.Equal(t, "cache:OFF", value[ind:]) +} + +func TestCollectDMLRuntimeStats(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int, b int, unique index (a))") + + testSQLs := []string{ + "insert ignore into t1 values (5,5);", + "insert into t1 values (5,5) on duplicate key update a=a+1;", + "replace into t1 values (5,6),(6,7)", + "update t1 set a=a+1 where a=6;", + } + + getRootStats := func() string { + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(plannercore.Plan) + require.True(t, ok) + stats := tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl.GetRootStats(p.ID()) + return stats.String() + } + for _, sql := range testSQLs { + tk.MustExec(sql) + require.Regexp(t, "time.*loops.*Get.*num_rpc.*total_time.*", getRootStats()) + } + + // Test for lock keys stats. + tk.MustExec("begin pessimistic") + tk.MustExec("update t1 set b=b+1") + require.Regexp(t, "time.*lock_keys.*time.* region.* keys.* lock_rpc:.* rpc_count.*", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustQuery("select * from t1 for update").Check(testkit.Rows("5 6", "7 7")) + require.Regexp(t, "time.*lock_keys.*time.* region.* keys.* lock_rpc:.* rpc_count.*", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustExec("insert ignore into t1 values (9,9)") + require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:{BatchGet:{num_rpc:.*, total_time:.*}}}.*", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t1 values (10,10) on duplicate key update a=a+1") + require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:{BatchGet:{num_rpc:.*, total_time:.*}.*", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t1 values (1,2)") + require.Regexp(t, "time:.*, loops:.*, prepare:.*, insert:.*", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustExec("insert ignore into t1 values(11,11) on duplicate key update `a`=`a`+1") + require.Regexp(t, "time:.*, loops:.*, prepare:.*, check_insert: {total_time:.*, mem_insert_time:.*, prefetch:.*, rpc:.*}", getRootStats()) + tk.MustExec("rollback") + + tk.MustExec("begin pessimistic") + tk.MustExec("replace into t1 values (1,4)") + require.Regexp(t, "time:.*, loops:.*, prefetch:.*, rpc:.*", getRootStats()) + tk.MustExec("rollback") +} + +func TestIssue24933(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("drop view if exists v;") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(1), (2), (3);") + + tk.MustExec("create definer='root'@'localhost' view v as select count(*) as c1 from t;") + tk.MustQuery("select * from v;").Check(testkit.Rows("3")) + + // Test subquery and outer field is wildcard. + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(*) from t) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select avg(a) from t group by a) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1.0000", "2.0000", "3.0000")) + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select sum(a) from t group by a) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select group_concat(a) from t group by a) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) + + // Test alias names. + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(0) as c1 from t) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(*) as c1 from t) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("3")) + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select group_concat(a) as `concat(a)` from t group by a) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) + + // Test firstrow. + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select a from t group by a) s;") + tk.MustQuery("select * from v order by 1;").Check(testkit.Rows("1", "2", "3")) + + // Test direct select. + tk.MustGetErrMsg("SELECT `s`.`count(a)` FROM (SELECT COUNT(`a`) FROM `test`.`t`) AS `s`", "[planner:1054]Unknown column 's.count(a)' in 'field list'") + + tk.MustExec("drop view v;") + tk.MustExec("create definer='root'@'localhost' view v as select * from (select count(a) from t) s;") + tk.MustQuery("select * from v").Check(testkit.Rows("3")) + + // Test window function. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(c1 int);") + tk.MustExec("insert into t values(111), (222), (333);") + tk.MustExec("drop view if exists v;") + tk.MustExec("create definer='root'@'localhost' view v as (select * from (select row_number() over (order by c1) from t) s);") + tk.MustQuery("select * from v;").Check(testkit.Rows("1", "2", "3")) + tk.MustExec("drop view if exists v;") + tk.MustExec("create definer='root'@'localhost' view v as (select * from (select c1, row_number() over (order by c1) from t) s);") + tk.MustQuery("select * from v;").Check(testkit.Rows("111 1", "222 2", "333 3")) + + // Test simple expr. + tk.MustExec("drop view if exists v;") + tk.MustExec("create definer='root'@'localhost' view v as (select * from (select c1 or 0 from t) s)") + tk.MustQuery("select * from v;").Check(testkit.Rows("1", "1", "1")) + tk.MustQuery("select `c1 or 0` from v;").Check(testkit.Rows("1", "1", "1")) + + tk.MustExec("drop view v;") +} + +func TestTableSampleTemporaryTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. + safePointName := "tikv_gc_safe_point" + safePointValue := "20160102-15:04:05 -0700" + safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" + updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') + ON DUPLICATE KEY + UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) + tk.MustExec(updateSafePoint) + + tk.MustExec("use test") + tk.MustExec("drop table if exists tmp1") + tk.MustExec("create global temporary table tmp1 " + + "(id int not null primary key, code int not null, value int default null, unique key code(code))" + + "on commit delete rows") + + tk.MustExec("use test") + tk.MustExec("drop table if exists tmp2") + tk.MustExec("create temporary table tmp2 (id int not null primary key, code int not null, value int default null, unique key code(code));") + + // sleep 1us to make test stale + time.Sleep(time.Microsecond) + + // test tablesample return empty for global temporary table + tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) + + tk.MustExec("begin") + tk.MustExec("insert into tmp1 values (1, 1, 1)") + tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) + tk.MustExec("commit") + + // tablesample for global temporary table should not return error for compatibility of tools like dumpling + tk.MustExec("set @@tidb_snapshot=NOW(6)") + tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) + + tk.MustExec("begin") + tk.MustQuery("select * from tmp1 tablesample regions()").Check(testkit.Rows()) + tk.MustExec("commit") + tk.MustExec("set @@tidb_snapshot=''") + + // test tablesample returns error for local temporary table + tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") + + tk.MustExec("begin") + tk.MustExec("insert into tmp2 values (1, 1, 1)") + tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") + tk.MustExec("commit") + tk.MustGetErrMsg("select * from tmp2 tablesample regions()", "TABLESAMPLE clause can not be applied to local temporary tables") +} + +func TestCTEWithIndexLookupJoinDeadLock(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int(11) default null,b int(11) default null,key b (b),key ba (b))") + tk.MustExec("create table t1 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") + tk.MustExec("create table t2 (a int(11) default null,b int(11) default null,key idx_ab (a,b),key idx_a (a),key idx_b (b))") + // It's easy to reproduce this problem in 30 times execution of IndexLookUpJoin. + for i := 0; i < 30; i++ { + tk.MustExec("with cte as (with cte1 as (select * from t2 use index(idx_ab) where a > 1 and b > 1) select * from cte1) select /*+use_index(t1 idx_ab)*/ * from cte join t1 on t1.a=cte.a;") + } +} + +func TestGetResultRowsCount(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + for i := 1; i <= 10; i++ { + tk.MustExec(fmt.Sprintf("insert into t values (%v)", i)) + } + cases := []struct { + sql string + row int64 + }{ + {"select * from t", 10}, + {"select * from t where a < 0", 0}, + {"select * from t where a <= 3", 3}, + {"insert into t values (11)", 0}, + {"replace into t values (12)", 0}, + {"update t set a=13 where a=12", 0}, + } + + for _, ca := range cases { + if strings.HasPrefix(ca.sql, "select") { + tk.MustQuery(ca.sql) + } else { + tk.MustExec(ca.sql) + } + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(plannercore.Plan) + require.True(t, ok) + cnt := executor.GetResultRowsCount(tk.Session().GetSessionVars().StmtCtx, p) + require.Equal(t, ca.row, cnt, fmt.Sprintf("sql: %v", ca.sql)) + } +} + +func TestAdminShowDDLJobs(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database if not exists test_admin_show_ddl_jobs") + tk.MustExec("use test_admin_show_ddl_jobs") + tk.MustExec("create table t (a int);") + + re := tk.MustQuery("admin show ddl jobs 1") + row := re.Rows()[0] + require.Equal(t, "test_admin_show_ddl_jobs", row[1]) + jobID, err := strconv.Atoi(row[0].(string)) + require.NoError(t, err) + + job, err := ddl.GetHistoryJobByID(tk.Session(), int64(jobID)) + require.NoError(t, err) + require.NotNil(t, job) + // Test for compatibility. Old TiDB version doesn't have SchemaName field, and the BinlogInfo maybe nil. + // See PR: 11561. + job.BinlogInfo = nil + job.SchemaName = "" + err = sessiontxn.NewTxnInStmt(context.Background(), tk.Session()) + require.NoError(t, err) + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + err = meta.NewMeta(txn).AddHistoryDDLJob(job, true) + require.NoError(t, err) + tk.Session().StmtCommit(context.Background()) + + re = tk.MustQuery("admin show ddl jobs 1") + row = re.Rows()[0] + require.Equal(t, "test_admin_show_ddl_jobs", row[1]) + + re = tk.MustQuery("admin show ddl jobs 1 where job_type='create table'") + row = re.Rows()[0] + require.Equal(t, "test_admin_show_ddl_jobs", row[1]) + require.Equal(t, "", row[10]) + + // Test the START_TIME and END_TIME field. + tk.MustExec(`set @@time_zone = 'Asia/Shanghai'`) + re = tk.MustQuery("admin show ddl jobs where end_time is not NULL") + row = re.Rows()[0] + createTime, err := types.ParseDatetime(nil, row[8].(string)) + require.NoError(t, err) + startTime, err := types.ParseDatetime(nil, row[9].(string)) + require.NoError(t, err) + endTime, err := types.ParseDatetime(nil, row[10].(string)) + require.NoError(t, err) + tk.MustExec(`set @@time_zone = 'Europe/Amsterdam'`) + re = tk.MustQuery("admin show ddl jobs where end_time is not NULL") + row2 := re.Rows()[0] + require.NotEqual(t, row[8], row2[8]) + require.NotEqual(t, row[9], row2[9]) + require.NotEqual(t, row[10], row2[10]) + createTime2, err := types.ParseDatetime(nil, row2[8].(string)) + require.NoError(t, err) + startTime2, err := types.ParseDatetime(nil, row2[9].(string)) + require.NoError(t, err) + endTime2, err := types.ParseDatetime(nil, row2[10].(string)) + require.NoError(t, err) + loc, err := time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + loc2, err := time.LoadLocation("Europe/Amsterdam") + require.NoError(t, err) + tt, err := createTime.GoTime(loc) + require.NoError(t, err) + t2, err := createTime2.GoTime(loc2) + require.NoError(t, err) + require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) + tt, err = startTime.GoTime(loc) + require.NoError(t, err) + t2, err = startTime2.GoTime(loc2) + require.NoError(t, err) + require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) + tt, err = endTime.GoTime(loc) + require.NoError(t, err) + t2, err = endTime2.GoTime(loc2) + require.NoError(t, err) + require.Equal(t, t2.In(time.UTC), tt.In(time.UTC)) +} + +func TestAdminShowDDLJobsRowCount(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // Test for issue: https://github.com/pingcap/tidb/issues/25968 + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (id bigint key,b int);") + tk.MustExec("split table t by (10),(20),(30);") + tk.MustExec("insert into t values (0,0),(10,10),(20,20),(30,30);") + tk.MustExec("alter table t add index idx1(b);") + require.Equal(t, "4", tk.MustQuery("admin show ddl jobs 1").Rows()[0][7]) + + tk.MustExec("insert into t values (1,0),(2,10),(3,20),(4,30);") + tk.MustExec("alter table t add index idx2(b);") + require.Equal(t, "8", tk.MustQuery("admin show ddl jobs 1").Rows()[0][7]) +} + +func TestAdminShowDDLJobsInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // Test for issue: https://github.com/pingcap/tidb/issues/29915 + tk.MustExec("create placement policy x followers=4;") + tk.MustExec("create placement policy y " + + "PRIMARY_REGION=\"cn-east-1\" " + + "REGIONS=\"cn-east-1, cn-east-2\" " + + "FOLLOWERS=2") + tk.MustExec("create database if not exists test_admin_show_ddl_jobs") + tk.MustExec("use test_admin_show_ddl_jobs") + + tk.MustExec("create table t (a int);") + tk.MustExec("create table t1 (a int);") + + tk.MustExec("alter table t placement policy x;") + require.Equal(t, "alter table placement", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) + + tk.MustExec("rename table t to tt, t1 to tt1") + require.Equal(t, "rename tables", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) + + tk.MustExec("create table tt2 (c int) PARTITION BY RANGE (c) " + + "(PARTITION p0 VALUES LESS THAN (6)," + + "PARTITION p1 VALUES LESS THAN (11)," + + "PARTITION p2 VALUES LESS THAN (16)," + + "PARTITION p3 VALUES LESS THAN (21));") + tk.MustExec("alter table tt2 partition p0 placement policy y") + require.Equal(t, "alter table partition placement", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) + + tk.MustExec("alter table tt1 cache") + require.Equal(t, "alter table cache", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) + tk.MustExec("alter table tt1 nocache") + require.Equal(t, "alter table nocache", tk.MustQuery("admin show ddl jobs 1").Rows()[0][3]) +} + +func TestAdminChecksumOfPartitionedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + tk.MustExec("DROP TABLE IF EXISTS admin_checksum_partition_test;") + tk.MustExec("CREATE TABLE admin_checksum_partition_test (a INT) PARTITION BY HASH(a) PARTITIONS 4;") + tk.MustExec("INSERT INTO admin_checksum_partition_test VALUES (1), (2);") + + r := tk.MustQuery("ADMIN CHECKSUM TABLE admin_checksum_partition_test;") + r.Check(testkit.Rows("test admin_checksum_partition_test 1 5 5")) +} + +func TestUnion2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + testSQL := `drop table if exists union_test; create table union_test(id int);` + tk.MustExec(testSQL) + + testSQL = `drop table if exists union_test;` + tk.MustExec(testSQL) + testSQL = `create table union_test(id int);` + tk.MustExec(testSQL) + testSQL = `insert union_test values (1),(2)` + tk.MustExec(testSQL) + + testSQL = `select * from (select id from union_test union select id from union_test) t order by id;` + r := tk.MustQuery(testSQL) + r.Check(testkit.Rows("1", "2")) + + r = tk.MustQuery("select 1 union all select 1") + r.Check(testkit.Rows("1", "1")) + + r = tk.MustQuery("select 1 union all select 1 union select 1") + r.Check(testkit.Rows("1")) + + r = tk.MustQuery("select 1 as a union (select 2) order by a limit 1") + r.Check(testkit.Rows("1")) + + r = tk.MustQuery("select 1 as a union (select 2) order by a limit 1, 1") + r.Check(testkit.Rows("2")) + + r = tk.MustQuery("select id from union_test union all (select 1) order by id desc") + r.Check(testkit.Rows("2", "1", "1")) + + r = tk.MustQuery("select id as a from union_test union (select 1) order by a desc") + r.Check(testkit.Rows("2", "1")) + + r = tk.MustQuery(`select null as a union (select "abc") order by a`) + r.Check(testkit.Rows("", "abc")) + + r = tk.MustQuery(`select "abc" as a union (select 1) order by a`) + r.Check(testkit.Rows("1", "abc")) + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (c int, d int)") + tk.MustExec("insert t1 values (NULL, 1)") + tk.MustExec("insert t1 values (1, 1)") + tk.MustExec("insert t1 values (1, 2)") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (c int, d int)") + tk.MustExec("insert t2 values (1, 3)") + tk.MustExec("insert t2 values (1, 1)") + tk.MustExec("drop table if exists t3") + tk.MustExec("create table t3 (c int, d int)") + tk.MustExec("insert t3 values (3, 2)") + tk.MustExec("insert t3 values (4, 3)") + r = tk.MustQuery(`select sum(c1), c2 from (select c c1, d c2 from t1 union all select d c1, c c2 from t2 union all select c c1, d c2 from t3) x group by c2 order by c2`) + r.Check(testkit.Rows("5 1", "4 2", "4 3")) + + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1 (a int primary key)") + tk.MustExec("create table t2 (a int primary key)") + tk.MustExec("create table t3 (a int primary key)") + tk.MustExec("insert t1 values (7), (8)") + tk.MustExec("insert t2 values (1), (9)") + tk.MustExec("insert t3 values (2), (3)") + r = tk.MustQuery("select * from t1 union all select * from t2 union all (select * from t3) order by a limit 2") + r.Check(testkit.Rows("1", "2")) + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (a int)") + tk.MustExec("create table t2 (a int)") + tk.MustExec("insert t1 values (2), (1)") + tk.MustExec("insert t2 values (3), (4)") + r = tk.MustQuery("select * from t1 union all (select * from t2) order by a limit 1") + r.Check(testkit.Rows("1")) + r = tk.MustQuery("select (select * from t1 where a != t.a union all (select * from t2 where a != t.a) order by a limit 1) from t1 t") + r.Check(testkit.Rows("1", "2")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int unsigned primary key auto_increment, c1 int, c2 int, index c1_c2 (c1, c2))") + tk.MustExec("insert into t (c1, c2) values (1, 1)") + tk.MustExec("insert into t (c1, c2) values (1, 2)") + tk.MustExec("insert into t (c1, c2) values (2, 3)") + r = tk.MustQuery("select * from (select * from t where t.c1 = 1 union select * from t where t.id = 1) s order by s.id") + r.Check(testkit.Rows("1 1 1", "2 1 2")) + + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (f1 DATE)") + tk.MustExec("INSERT INTO t VALUES ('1978-11-26')") + r = tk.MustQuery("SELECT f1+0 FROM t UNION SELECT f1+0 FROM t") + r.Check(testkit.Rows("19781126")) + + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (a int, b int)") + tk.MustExec("INSERT INTO t VALUES ('1', '1')") + r = tk.MustQuery("select b from (SELECT * FROM t UNION ALL SELECT a, b FROM t order by a) t") + r.Check(testkit.Rows("1", "1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (a DECIMAL(4,2))") + tk.MustExec("INSERT INTO t VALUE(12.34)") + r = tk.MustQuery("SELECT 1 AS c UNION select a FROM t") + r.Sort().Check(testkit.Rows("1.00", "12.34")) + + // #issue3771 + r = tk.MustQuery("SELECT 'a' UNION SELECT CONCAT('a', -4)") + r.Sort().Check(testkit.Rows("a", "a-4")) + + // test race + tk.MustQuery("SELECT @x:=0 UNION ALL SELECT @x:=0 UNION ALL SELECT @x") + + // test field tp + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("CREATE TABLE t1 (a date)") + tk.MustExec("CREATE TABLE t2 (a date)") + tk.MustExec("SELECT a from t1 UNION select a FROM t2") + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE TABLE `t1` (\n" + " `a` date DEFAULT NULL\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // Move from session test. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (c double);") + tk.MustExec("create table t2 (c double);") + tk.MustExec("insert into t1 value (73);") + tk.MustExec("insert into t2 value (930);") + // If set unspecified column flen to 0, it will cause bug in union. + // This test is used to prevent the bug reappear. + tk.MustQuery("select c from t1 union (select c from t2) order by c").Check(testkit.Rows("73", "930")) + + // issue 5703 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a date)") + tk.MustExec("insert into t value ('2017-01-01'), ('2017-01-02')") + r = tk.MustQuery("(select a from t where a < 0) union (select a from t where a > 0) order by a") + r.Check(testkit.Rows("2017-01-01", "2017-01-02")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t value(0),(0)") + tk.MustQuery("select 1 from (select a from t union all select a from t) tmp").Check(testkit.Rows("1", "1", "1", "1")) + tk.MustQuery("select 10 as a from dual union select a from t order by a desc limit 1 ").Check(testkit.Rows("10")) + tk.MustQuery("select -10 as a from dual union select a from t order by a limit 1 ").Check(testkit.Rows("-10")) + tk.MustQuery("select count(1) from (select a from t union all select a from t) tmp").Check(testkit.Rows("4")) + + err := tk.ExecToErr("select 1 from (select a from t limit 1 union all select a from t limit 1) tmp") + require.Error(t, err) + terr := errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(mysql.ErrWrongUsage), terr.Code()) + + err = tk.ExecToErr("select 1 from (select a from t order by a union all select a from t limit 1) tmp") + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(mysql.ErrWrongUsage), terr.Code()) + + tk.MustGetDBError("(select a from t order by a) union all select a from t limit 1 union all select a from t limit 1", plannercore.ErrWrongUsage) + + tk.MustExec("(select a from t limit 1) union all select a from t limit 1") + tk.MustExec("(select a from t order by a) union all select a from t order by a") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t value(1),(2),(3)") + + tk.MustQuery("(select a from t order by a limit 2) union all (select a from t order by a desc limit 2) order by a desc limit 1,2").Check(testkit.Rows("2", "2")) + tk.MustQuery("select a from t union all select a from t order by a desc limit 5").Check(testkit.Rows("3", "3", "2", "2", "1")) + tk.MustQuery("(select a from t order by a desc limit 2) union all select a from t group by a order by a").Check(testkit.Rows("1", "2", "2", "3", "3")) + tk.MustQuery("(select a from t order by a desc limit 2) union all select 33 as a order by a desc limit 2").Check(testkit.Rows("33", "3")) + + tk.MustQuery("select 1 union select 1 union all select 1").Check(testkit.Rows("1", "1")) + tk.MustQuery("select 1 union all select 1 union select 1").Check(testkit.Rows("1")) + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(`create table t1(a bigint, b bigint);`) + tk.MustExec(`create table t2(a bigint, b bigint);`) + tk.MustExec(`insert into t1 values(1, 1);`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t1 select * from t1;`) + tk.MustExec(`insert into t2 values(1, 1);`) + tk.MustExec(`set @@tidb_init_chunk_size=2;`) + tk.MustExec(`set @@sql_mode="";`) + tk.MustQuery(`select count(*) from (select t1.a, t1.b from t1 left join t2 on t1.a=t2.a union all select t1.a, t1.a from t1 left join t2 on t1.a=t2.a) tmp;`).Check(testkit.Rows("128")) + tk.MustQuery(`select tmp.a, count(*) from (select t1.a, t1.b from t1 left join t2 on t1.a=t2.a union all select t1.a, t1.a from t1 left join t2 on t1.a=t2.a) tmp;`).Check(testkit.Rows("1 128")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t value(1 ,2)") + tk.MustQuery("select a, b from (select a, 0 as d, b from t union all select a, 0 as d, b from t) test;").Check(testkit.Rows("1 2", "1 2")) + + // #issue 8141 + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("insert into t1 value(1,2),(1,1),(2,2),(2,2),(3,2),(3,2)") + tk.MustExec("set @@tidb_init_chunk_size=2;") + tk.MustQuery("select count(*) from (select a as c, a as d from t1 union all select a, b from t1) t;").Check(testkit.Rows("12")) + + // #issue 8189 and #issue 8199 + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("CREATE TABLE t1 (a int not null, b char (10) not null)") + tk.MustExec("insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") + tk.MustExec("CREATE TABLE t2 (a int not null, b char (10) not null)") + tk.MustExec("insert into t2 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") + tk.MustQuery("select a from t1 union select a from t1 order by (select a+1);").Check(testkit.Rows("1", "2", "3")) + + // #issue 8201 + for i := 0; i < 4; i++ { + tk.MustQuery("SELECT(SELECT 0 AS a FROM dual UNION SELECT 1 AS a FROM dual ORDER BY a ASC LIMIT 1) AS dev").Check(testkit.Rows("0")) + } + + // #issue 8231 + tk.MustExec("drop table if exists t1") + tk.MustExec("CREATE TABLE t1 (uid int(1))") + tk.MustExec("INSERT INTO t1 SELECT 150") + tk.MustQuery("SELECT 'a' UNION SELECT uid FROM t1 order by 1 desc;").Check(testkit.Rows("a", "150")) + + // #issue 8196 + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("CREATE TABLE t1 (a int not null, b char (10) not null)") + tk.MustExec("insert into t1 values(1,'a'),(2,'b'),(3,'c'),(3,'c')") + tk.MustExec("CREATE TABLE t2 (a int not null, b char (10) not null)") + tk.MustExec("insert into t2 values(3,'c'),(4,'d'),(5,'f'),(6,'e')") + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustGetErrMsg("(select a,b from t1 limit 2) union all (select a,b from t2 order by a limit 1) order by t1.b", + "[planner:1250]Table 't1' from one of the SELECTs cannot be used in global ORDER clause") + + // #issue 9900 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b decimal(6, 3))") + tk.MustExec("insert into t values(1, 1.000)") + tk.MustQuery("select count(distinct a), sum(distinct a), avg(distinct a) from (select a from t union all select b from t) tmp;").Check(testkit.Rows("1 1.000 1.0000000")) + + // #issue 23832 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a bit(20), b float, c double, d int)") + tk.MustExec("insert into t values(10, 10, 10, 10), (1, -1, 2, -2), (2, -2, 1, 1), (2, 1.1, 2.1, 10.1)") + tk.MustQuery("select a from t union select 10 order by a").Check(testkit.Rows("1", "2", "10")) +} + +func TestUnionLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists union_limit") + tk.MustExec("create table union_limit (id int) partition by hash(id) partitions 30") + for i := 0; i < 60; i++ { + tk.MustExec(fmt.Sprintf("insert into union_limit values (%d)", i)) + } + // Cover the code for worker count limit in the union executor. + tk.MustQuery("select * from union_limit limit 10") +} + +func TestLowResolutionTSORead(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@autocommit=1") + tk.MustExec("use test") + tk.MustExec("create table low_resolution_tso(a int)") + tk.MustExec("insert low_resolution_tso values (1)") + + // enable low resolution tso + require.False(t, tk.Session().GetSessionVars().LowResolutionTSO) + tk.MustExec("set @@tidb_low_resolution_tso = 'on'") + require.True(t, tk.Session().GetSessionVars().LowResolutionTSO) + + time.Sleep(3 * time.Second) + tk.MustQuery("select * from low_resolution_tso").Check(testkit.Rows("1")) + err := tk.ExecToErr("update low_resolution_tso set a = 2") + require.Error(t, err) + tk.MustExec("set @@tidb_low_resolution_tso = 'off'") + tk.MustExec("update low_resolution_tso set a = 2") + tk.MustQuery("select * from low_resolution_tso").Check(testkit.Rows("2")) +} + +func TestStaleReadAtFutureTime(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + // Setting tx_read_ts to a time in the future will fail. (One day before the 2038 problem) + tk.MustGetErrMsg("set @@tx_read_ts = '2038-01-18 03:14:07'", "cannot set read timestamp to a future time") + // TxnReadTS Is not updated if check failed. + require.Zero(t, tk.Session().GetSessionVars().TxnReadTS.PeakTxnReadTS()) +} + +func TestSQLMode(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a tinyint not null)") + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + tk.ExecToErr("insert t values ()") + tk.ExecToErr("insert t values ('1000')") + + tk.MustExec("create table if not exists tdouble (a double(3,2))") + tk.ExecToErr("insert tdouble values (10.23)") + + tk.MustExec("set sql_mode = ''") + tk.MustExec("insert t values ()") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1364 Field 'a' doesn't have a default value")) + tk.MustExec("insert t values (null)") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) + tk.MustExec("insert ignore t values (null)") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) + tk.MustExec("insert t select null") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1048 Column 'a' cannot be null")) + tk.MustExec("insert t values (1000)") + tk.MustQuery("select * from t order by a").Check(testkit.Rows("0", "0", "0", "0", "127")) + + tk.MustExec("insert tdouble values (10.23)") + tk.MustQuery("select * from tdouble").Check(testkit.Rows("9.99")) + + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + tk.MustExec("set @@global.sql_mode = ''") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("drop table if exists t2") + tk2.MustExec("create table t2 (a varchar(3))") + tk2.MustExec("insert t2 values ('abcd')") + tk2.MustQuery("select * from t2").Check(testkit.Rows("abc")) + + // session1 is still in strict mode. + tk.ExecToErr("insert t2 values ('abcd')") + // Restore original global strict mode. + tk.MustExec("set @@global.sql_mode = 'STRICT_TRANS_TABLES'") +} + +func TestTableScan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use information_schema") + result := tk.MustQuery("select * from schemata") + // There must be these tables: information_schema, mysql, performance_schema and test. + require.GreaterOrEqual(t, len(result.Rows()), 4) + tk.MustExec("use test") + tk.MustExec("create database mytest") + rowStr1 := fmt.Sprintf("%s %s %s %s %v %v", "def", "mysql", "utf8mb4", "utf8mb4_bin", nil, nil) + rowStr2 := fmt.Sprintf("%s %s %s %s %v %v", "def", "mytest", "utf8mb4", "utf8mb4_bin", nil, nil) + tk.MustExec("use information_schema") + result = tk.MustQuery("select * from schemata where schema_name = 'mysql'") + result.Check(testkit.Rows(rowStr1)) + result = tk.MustQuery("select * from schemata where schema_name like 'my%'") + result.Check(testkit.Rows(rowStr1, rowStr2)) + result = tk.MustQuery("select 1 from tables limit 1") + result.Check(testkit.Rows("1")) +} + +func TestAdapterStatement(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.Session().GetSessionVars().TxnCtx.InfoSchema = domain.GetDomain(tk.Session()).InfoSchema() + compiler := &executor.Compiler{Ctx: tk.Session()} + s := parser.New() + stmtNode, err := s.ParseOneStmt("select 1", "", "") + require.NoError(t, err) + stmt, err := compiler.Compile(context.TODO(), stmtNode) + require.NoError(t, err) + require.Equal(t, "select 1", stmt.OriginText()) + + stmtNode, err = s.ParseOneStmt("create table test.t (a int)", "", "") + require.NoError(t, err) + stmt, err = compiler.Compile(context.TODO(), stmtNode) + require.NoError(t, err) + require.Equal(t, "create table test.t (a int)", stmt.OriginText()) +} + +func TestIsPointGet(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql") + ctx := tk.Session().(sessionctx.Context) + tests := map[string]bool{ + "select * from help_topic where name='aaa'": false, + "select 1 from help_topic where name='aaa'": false, + "select * from help_topic where help_topic_id=1": true, + "select * from help_topic where help_category_id=1": false, + } + s := parser.New() + for sqlStr, result := range tests { + stmtNode, err := s.ParseOneStmt(sqlStr, "", "") + require.NoError(t, err) + preprocessorReturn := &plannercore.PreprocessorReturn{} + err = plannercore.Preprocess(context.Background(), ctx, stmtNode, plannercore.WithPreprocessorReturn(preprocessorReturn)) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), ctx, stmtNode, preprocessorReturn.InfoSchema) + require.NoError(t, err) + ret, err := plannercore.IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, p) + require.NoError(t, err) + require.Equal(t, result, ret) + } +} + +func TestClusteredIndexIsPointGet(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("drop database if exists test_cluster_index_is_point_get;") + tk.MustExec("create database test_cluster_index_is_point_get;") + tk.MustExec("use test_cluster_index_is_point_get;") + + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a varchar(255), b int, c char(10), primary key (c, a));") + ctx := tk.Session().(sessionctx.Context) + + tests := map[string]bool{ + "select 1 from t where a='x'": false, + "select * from t where c='x'": false, + "select * from t where a='x' and c='x'": true, + "select * from t where a='x' and c='x' and b=1": false, + } + s := parser.New() + for sqlStr, result := range tests { + stmtNode, err := s.ParseOneStmt(sqlStr, "", "") + require.NoError(t, err) + preprocessorReturn := &plannercore.PreprocessorReturn{} + err = plannercore.Preprocess(context.Background(), ctx, stmtNode, plannercore.WithPreprocessorReturn(preprocessorReturn)) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), ctx, stmtNode, preprocessorReturn.InfoSchema) + require.NoError(t, err) + ret, err := plannercore.IsPointGetWithPKOrUniqueKeyByAutoCommit(ctx, p) + require.NoError(t, err) + require.Equal(t, result, ret) + } +} + +func TestColumnName(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c int, d int)") + // disable only full group by + tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") + rs, err := tk.Exec("select 1 + c, count(*) from t") + require.NoError(t, err) + fields := rs.Fields() + require.Len(t, fields, 2) + require.Equal(t, "1 + c", fields[0].Column.Name.L) + require.Equal(t, "1 + c", fields[0].ColumnAsName.L) + require.Equal(t, "count(*)", fields[1].Column.Name.L) + require.Equal(t, "count(*)", fields[1].ColumnAsName.L) + require.NoError(t, rs.Close()) + rs, err = tk.Exec("select (c) > all (select c from t) from t") + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 1) + require.Equal(t, "(c) > all (select c from t)", fields[0].Column.Name.L) + require.Equal(t, "(c) > all (select c from t)", fields[0].ColumnAsName.L) + require.NoError(t, rs.Close()) + tk.MustExec("begin") + tk.MustExec("insert t values(1,1)") + rs, err = tk.Exec("select c d, d c from t") + require.NoError(t, err) + fields = rs.Fields() + require.Len(t, fields, 2) + require.Equal(t, "c", fields[0].Column.Name.L) + require.Equal(t, "d", fields[0].ColumnAsName.L) + require.Equal(t, "d", fields[1].Column.Name.L) + require.Equal(t, "c", fields[1].ColumnAsName.L) + require.NoError(t, rs.Close()) + // Test case for query a column of a table. + // In this case, all attributes have values. + rs, err = tk.Exec("select c as a from t as t2") + require.NoError(t, err) + fields = rs.Fields() + require.Equal(t, "c", fields[0].Column.Name.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + require.Equal(t, "t", fields[0].Table.Name.L) + require.Equal(t, "t2", fields[0].TableAsName.L) + require.Equal(t, "test", fields[0].DBName.L) + require.Nil(t, rs.Close()) + // Test case for query a expression which only using constant inputs. + // In this case, the table, org_table and database attributes will all be empty. + rs, err = tk.Exec("select hour(1) as a from t as t2") + require.NoError(t, err) + fields = rs.Fields() + require.Equal(t, "a", fields[0].Column.Name.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + require.Equal(t, "", fields[0].Table.Name.L) + require.Equal(t, "", fields[0].TableAsName.L) + require.Equal(t, "", fields[0].DBName.L) + require.Nil(t, rs.Close()) + // Test case for query a column wrapped with parentheses and unary plus. + // In this case, the column name should be its original name. + rs, err = tk.Exec("select (c), (+c), +(c), +(+(c)), ++c from t") + require.NoError(t, err) + fields = rs.Fields() + for i := 0; i < 5; i++ { + require.Equal(t, "c", fields[i].Column.Name.L) + require.Equal(t, "c", fields[i].ColumnAsName.L) + } + require.Nil(t, rs.Close()) + + // Test issue https://github.com/pingcap/tidb/issues/9639 . + // Both window function and expression appear in final result field. + tk.MustExec("set @@tidb_enable_window_function = 1") + rs, err = tk.Exec("select 1+1, row_number() over() num from t") + require.NoError(t, err) + fields = rs.Fields() + require.Equal(t, "1+1", fields[0].Column.Name.L) + require.Equal(t, "1+1", fields[0].ColumnAsName.L) + require.Equal(t, "num", fields[1].Column.Name.L) + require.Equal(t, "num", fields[1].ColumnAsName.L) + tk.MustExec("set @@tidb_enable_window_function = 0") + require.Nil(t, rs.Close()) + + rs, err = tk.Exec("select if(1,c,c) from t;") + require.NoError(t, err) + fields = rs.Fields() + require.Equal(t, "if(1,c,c)", fields[0].Column.Name.L) + // It's a compatibility issue. Should be empty instead. + require.Equal(t, "if(1,c,c)", fields[0].ColumnAsName.L) + require.Nil(t, rs.Close()) +} + +func TestSelectVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (d int)") + tk.MustExec("insert into t values(1), (2), (1)") + // This behavior is different from MySQL. + result := tk.MustQuery("select @a, @a := d+1 from t") + result.Check(testkit.Rows(" 2", "2 3", "3 2")) + // Test for PR #10658. + tk.MustExec("select SQL_BIG_RESULT d from t group by d") + tk.MustExec("select SQL_SMALL_RESULT d from t group by d") + tk.MustExec("select SQL_BUFFER_RESULT d from t group by d") +} + +func TestHistoryRead(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists history_read") + tk.MustExec("create table history_read (a int)") + tk.MustExec("insert history_read values (1)") + + // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. + safePointName := "tikv_gc_safe_point" + safePointValue := "20060102-15:04:05 -0700" + safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" + updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') + ON DUPLICATE KEY + UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) + tk.MustExec(updateSafePoint) + + // Set snapshot to a time before save point will fail. + _, err := tk.Exec("set @@tidb_snapshot = '2006-01-01 15:04:05.999999'") + require.True(t, terror.ErrorEqual(err, variable.ErrSnapshotTooOld), "err %v", err) + // SnapshotTS Is not updated if check failed. + require.Equal(t, uint64(0), tk.Session().GetSessionVars().SnapshotTS) + + // Setting snapshot to a time in the future will fail. (One day before the 2038 problem) + _, err = tk.Exec("set @@tidb_snapshot = '2038-01-18 03:14:07'") + require.Regexp(t, "cannot set read timestamp to a future time", err) + // SnapshotTS Is not updated if check failed. + require.Equal(t, uint64(0), tk.Session().GetSessionVars().SnapshotTS) + + curVer1, _ := store.CurrentVersion(kv.GlobalTxnScope) + time.Sleep(time.Millisecond) + snapshotTime := time.Now() + time.Sleep(time.Millisecond) + curVer2, _ := store.CurrentVersion(kv.GlobalTxnScope) + tk.MustExec("insert history_read values (2)") + tk.MustQuery("select * from history_read").Check(testkit.Rows("1", "2")) + tk.MustExec("set @@tidb_snapshot = '" + snapshotTime.Format("2006-01-02 15:04:05.999999") + "'") + ctx := tk.Session().(sessionctx.Context) + snapshotTS := ctx.GetSessionVars().SnapshotTS + require.Greater(t, snapshotTS, curVer1.Ver) + require.Less(t, snapshotTS, curVer2.Ver) + tk.MustQuery("select * from history_read").Check(testkit.Rows("1")) + tk.MustExecToErr("insert history_read values (2)") + tk.MustExecToErr("update history_read set a = 3 where a = 1") + tk.MustExecToErr("delete from history_read where a = 1") + tk.MustExec("set @@tidb_snapshot = ''") + tk.MustQuery("select * from history_read").Check(testkit.Rows("1", "2")) + tk.MustExec("insert history_read values (3)") + tk.MustExec("update history_read set a = 4 where a = 3") + tk.MustExec("delete from history_read where a = 1") + + time.Sleep(time.Millisecond) + snapshotTime = time.Now() + time.Sleep(time.Millisecond) + tk.MustExec("alter table history_read add column b int") + tk.MustExec("insert history_read values (8, 8), (9, 9)") + tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2 ", "4 ", "8 8", "9 9")) + tk.MustExec("set @@tidb_snapshot = '" + snapshotTime.Format("2006-01-02 15:04:05.999999") + "'") + tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2", "4")) + tsoStr := strconv.FormatUint(oracle.GoTimeToTS(snapshotTime), 10) + + tk.MustExec("set @@tidb_snapshot = '" + tsoStr + "'") + tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2", "4")) + + tk.MustExec("set @@tidb_snapshot = ''") + tk.MustQuery("select * from history_read order by a").Check(testkit.Rows("2 ", "4 ", "8 8", "9 9")) +} + +func TestHistoryReadInTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // For mocktikv, safe point is not initialized, we manually insert it for snapshot to use. + safePointName := "tikv_gc_safe_point" + safePointValue := "20060102-15:04:05 -0700" + safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" + updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') + ON DUPLICATE KEY + UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) + tk.MustExec(updateSafePoint) + + tk.MustExec("drop table if exists his_t0, his_t1") + tk.MustExec("create table his_t0(id int primary key, v int)") + tk.MustExec("insert into his_t0 values(1, 10)") + + time.Sleep(time.Millisecond) + tk.MustExec("set @a=now(6)") + time.Sleep(time.Millisecond) + tk.MustExec("create table his_t1(id int primary key, v int)") + tk.MustExec("update his_t0 set v=v+1") + time.Sleep(time.Millisecond) + tk.MustExec("set tidb_snapshot=now(6)") + ts2 := tk.Session().GetSessionVars().SnapshotTS + tk.MustExec("set tidb_snapshot=''") + time.Sleep(time.Millisecond) + tk.MustExec("update his_t0 set v=v+1") + tk.MustExec("insert into his_t1 values(10, 100)") + + init := func(isolation string, setSnapshotBeforeTxn bool) { + if isolation == "none" { + tk.MustExec("set @@tidb_snapshot=@a") + return + } + + if setSnapshotBeforeTxn { + tk.MustExec("set @@tidb_snapshot=@a") + } + + if isolation == "optimistic" { + tk.MustExec("begin optimistic") + } else { + tk.MustExec(fmt.Sprintf("set @@tx_isolation='%s'", isolation)) + tk.MustExec("begin pessimistic") + } + + if !setSnapshotBeforeTxn { + tk.MustExec("set @@tidb_snapshot=@a") + } + } + + for _, isolation := range []string{ + "none", // not start an explicit txn + "optimistic", + "REPEATABLE-READ", + "READ-COMMITTED", + } { + for _, setSnapshotBeforeTxn := range []bool{false, true} { + t.Run(fmt.Sprintf("[%s] setSnapshotBeforeTxn[%v]", isolation, setSnapshotBeforeTxn), func(t *testing.T) { + tk.MustExec("rollback") + tk.MustExec("set @@tidb_snapshot=''") + + init(isolation, setSnapshotBeforeTxn) + // When tidb_snapshot is set, should use the snapshot info schema + tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0")) + + // When tidb_snapshot is set, select should use select ts + tk.MustQuery("select * from his_t0").Check(testkit.Rows("1 10")) + tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 10")) + + // When tidb_snapshot is set, write statements should not be allowed + if isolation != "none" && isolation != "optimistic" { + notAllowedSQLs := []string{ + "insert into his_t0 values(5, 1)", + "delete from his_t0 where id=1", + "update his_t0 set v=v+1", + "select * from his_t0 for update", + "select * from his_t0 where id=1 for update", + "create table his_t2(id int)", + } + + for _, sql := range notAllowedSQLs { + err := tk.ExecToErr(sql) + require.Errorf(t, err, "can not execute write statement when 'tidb_snapshot' is set") + } + } + + // After `ExecRestrictedSQL` with a specified snapshot and use current session, the original snapshot ts should not be reset + // See issue: https://github.com/pingcap/tidb/issues/34529 + exec := tk.Session().(sqlexec.RestrictedSQLExecutor) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionWithSnapshot(ts2), sqlexec.ExecOptionUseCurSession}, "select * from his_t0 where id=1") + require.NoError(t, err) + require.Equal(t, 1, len(rows)) + require.Equal(t, int64(1), rows[0].GetInt64(0)) + require.Equal(t, int64(11), rows[0].GetInt64(1)) + tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 10")) + tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0")) + + // CLEAR + tk.MustExec("set @@tidb_snapshot=''") + + // When tidb_snapshot is not set, should use the transaction's info schema + tk.MustQuery("show tables like 'his_%'").Check(testkit.Rows("his_t0", "his_t1")) + + // When tidb_snapshot is not set, select should use the transaction's ts + tk.MustQuery("select * from his_t0").Check(testkit.Rows("1 12")) + tk.MustQuery("select * from his_t0 where id=1").Check(testkit.Rows("1 12")) + tk.MustQuery("select * from his_t1").Check(testkit.Rows("10 100")) + tk.MustQuery("select * from his_t1 where id=10").Check(testkit.Rows("10 100")) + + // When tidb_snapshot is not set, select ... for update should not be effected + tk.MustQuery("select * from his_t0 for update").Check(testkit.Rows("1 12")) + tk.MustQuery("select * from his_t0 where id=1 for update").Check(testkit.Rows("1 12")) + tk.MustQuery("select * from his_t1 for update").Check(testkit.Rows("10 100")) + tk.MustQuery("select * from his_t1 where id=10 for update").Check(testkit.Rows("10 100")) + + tk.MustExec("rollback") + }) + } + } +} + +func TestCurrentTimestampValueSelection(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t,t1") + + tk.MustExec("create table t (id int, t0 timestamp null default current_timestamp, t1 timestamp(1) null default current_timestamp(1), t2 timestamp(2) null default current_timestamp(2) on update current_timestamp(2))") + tk.MustExec("insert into t (id) values (1)") + rs := tk.MustQuery("select t0, t1, t2 from t where id = 1") + t0 := rs.Rows()[0][0].(string) + t1 := rs.Rows()[0][1].(string) + t2 := rs.Rows()[0][2].(string) + require.Equal(t, 1, len(strings.Split(t0, "."))) + require.Equal(t, 1, len(strings.Split(t1, ".")[1])) + require.Equal(t, 2, len(strings.Split(t2, ".")[1])) + tk.MustQuery("select id from t where t0 = ?", t0).Check(testkit.Rows("1")) + tk.MustQuery("select id from t where t1 = ?", t1).Check(testkit.Rows("1")) + tk.MustQuery("select id from t where t2 = ?", t2).Check(testkit.Rows("1")) + time.Sleep(time.Second) + tk.MustExec("update t set t0 = now() where id = 1") + rs = tk.MustQuery("select t2 from t where id = 1") + newT2 := rs.Rows()[0][0].(string) + require.True(t, newT2 != t2) + + tk.MustExec("create table t1 (id int, a timestamp, b timestamp(2), c timestamp(3))") + tk.MustExec("insert into t1 (id, a, b, c) values (1, current_timestamp(2), current_timestamp, current_timestamp(3))") + rs = tk.MustQuery("select a, b, c from t1 where id = 1") + a := rs.Rows()[0][0].(string) + b := rs.Rows()[0][1].(string) + d := rs.Rows()[0][2].(string) + require.Equal(t, 1, len(strings.Split(a, "."))) + require.Equal(t, "00", strings.Split(b, ".")[1]) + require.Equal(t, 3, len(strings.Split(d, ".")[1])) +} + +func TestAddDateBuiltinWithWarnings(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@sql_mode='NO_ZERO_DATE'") + result := tk.MustQuery(`select date_add('2001-01-00', interval -2 hour);`) + result.Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '2001-01-00'")) +} + +func TestStrToDateBuiltinWithWarnings(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@sql_mode='NO_ZERO_DATE'") + tk.MustExec("use test") + tk.MustQuery(`SELECT STR_TO_DATE('0000-1-01', '%Y-%m-%d');`).Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1411 Incorrect datetime value: '0000-1-01' for function str_to_date")) + tk.MustQuery("SELECT CAST('4#,8?Q' AS DATE);").Check(testkit.Rows("")) + tk.MustQuery(`show warnings;`).Check(testkit.Rows( + `Warning 8034 Incorrect datetime value: '4#,8?Q'`, + )) + tk.MustExec("CREATE TABLE t1 (c1 INT, c2 TEXT);") + tk.MustExec("INSERT INTO t1 VALUES (1833458842, '0.3503490908550797');") + tk.MustQuery(`SELECT CAST(t1.c2 AS DATE) FROM t1`).Check(testkit.Rows("")) + tk.MustQuery(`show warnings;`).Check(testkit.Rows( + `Warning 1292 Incorrect datetime value: '0.3503490908550797'`, + )) +} + +func TestAdmin(t *testing.T) { + var cluster testutils.Cluster + store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cluster = c + })) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk.MustExec("drop table if exists admin_test") + tk.MustExec("create table admin_test (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("insert admin_test (c1) values (1),(2),(NULL)") + + ctx := context.Background() + // cancel DDL jobs test + r, err := tk.Exec("admin cancel ddl jobs 1") + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + row := req.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "1", row.GetString(0)) + require.Regexp(t, ".*DDL Job:1 not found", row.GetString(1)) + + // show ddl test; + r, err = tk.Exec("admin show ddl") + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + row = req.GetRow(0) + require.Equal(t, 6, row.Len()) + tk = testkit.NewTestKit(t, store) + tk.MustExec("begin") + sess := tk.Session() + ddlInfo, err := ddl.GetDDLInfo(sess) + require.NoError(t, err) + require.Equal(t, ddlInfo.SchemaVer, row.GetInt64(0)) + // TODO: Pass this test. + // rowOwnerInfos := strings.Split(row.Data[1].GetString(), ",") + // ownerInfos := strings.Split(ddlInfo.Owner.String(), ",") + // c.Assert(rowOwnerInfos[0], Equals, ownerInfos[0]) + serverInfo, err := infosync.GetServerInfoByID(ctx, row.GetString(1)) + require.NoError(t, err) + require.Equal(t, serverInfo.IP+":"+strconv.FormatUint(uint64(serverInfo.Port), 10), row.GetString(2)) + require.Equal(t, "", row.GetString(3)) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Zero(t, req.NumRows()) + tk.MustExec("rollback") + + // show DDL jobs test + r, err = tk.Exec("admin show ddl jobs") + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + row = req.GetRow(0) + require.Equal(t, 12, row.Len()) + txn, err := store.Begin() + require.NoError(t, err) + historyJobs, err := ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) + require.Greater(t, len(historyJobs), 1) + require.Greater(t, len(row.GetString(1)), 0) + require.NoError(t, err) + require.Equal(t, historyJobs[0].ID, row.GetInt64(0)) + require.NoError(t, err) + + r, err = tk.Exec("admin show ddl jobs 20") + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + row = req.GetRow(0) + require.Equal(t, 12, row.Len()) + require.Equal(t, historyJobs[0].ID, row.GetInt64(0)) + require.NoError(t, err) + + // show DDL job queries test + tk.MustExec("use test") + tk.MustExec("drop table if exists admin_test2") + tk.MustExec("create table admin_test2 (c1 int, c2 int, c3 int default 1, index (c1))") + result := tk.MustQuery(`admin show ddl job queries 1, 1, 1`) + result.Check(testkit.Rows()) + result = tk.MustQuery(`admin show ddl job queries 1, 2, 3, 4`) + result.Check(testkit.Rows()) + historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) + result = tk.MustQuery(fmt.Sprintf("admin show ddl job queries %d", historyJobs[0].ID)) + result.Check(testkit.Rows(historyJobs[0].Query)) + require.NoError(t, err) + + // show DDL job queries with range test + tk.MustExec("use test") + tk.MustExec("drop table if exists admin_test2") + tk.MustExec("create table admin_test2 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test3") + tk.MustExec("create table admin_test3 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test4") + tk.MustExec("create table admin_test4 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test5") + tk.MustExec("create table admin_test5 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test6") + tk.MustExec("create table admin_test6 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test7") + tk.MustExec("create table admin_test7 (c1 int, c2 int, c3 int default 1, index (c1))") + tk.MustExec("drop table if exists admin_test8") + tk.MustExec("create table admin_test8 (c1 int, c2 int, c3 int default 1, index (c1))") + historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), ddl.DefNumHistoryJobs) + result = tk.MustQuery(`admin show ddl job queries limit 3`) + result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[0].ID, historyJobs[0].Query), fmt.Sprintf("%d %s", historyJobs[1].ID, historyJobs[1].Query), fmt.Sprintf("%d %s", historyJobs[2].ID, historyJobs[2].Query))) + result = tk.MustQuery(`admin show ddl job queries limit 3, 2`) + result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[3].ID, historyJobs[3].Query), fmt.Sprintf("%d %s", historyJobs[4].ID, historyJobs[4].Query))) + result = tk.MustQuery(`admin show ddl job queries limit 3 offset 2`) + result.Check(testkit.Rows(fmt.Sprintf("%d %s", historyJobs[2].ID, historyJobs[2].Query), fmt.Sprintf("%d %s", historyJobs[3].ID, historyJobs[3].Query), fmt.Sprintf("%d %s", historyJobs[4].ID, historyJobs[4].Query))) + require.NoError(t, err) + + // check situations when `admin show ddl job 20` happens at the same time with new DDLs being executed + var wg sync.WaitGroup + wg.Add(2) + flag := true + go func() { + defer wg.Done() + for i := 0; i < 10; i++ { + tk.MustExec("drop table if exists admin_test9") + tk.MustExec("create table admin_test9 (c1 int, c2 int, c3 int default 1, index (c1))") + } + }() + go func() { + // check that the result set has no duplication + defer wg.Done() + for i := 0; i < 10; i++ { + result := tk2.MustQuery(`admin show ddl job queries 20`) + rows := result.Rows() + rowIDs := make(map[string]struct{}) + for _, row := range rows { + rowID := fmt.Sprintf("%v", row[0]) + if _, ok := rowIDs[rowID]; ok { + flag = false + return + } + rowIDs[rowID] = struct{}{} + } + } + }() + wg.Wait() + require.True(t, flag) + + // check situations when `admin show ddl job queries limit 3 offset 2` happens at the same time with new DDLs being executed + var wg2 sync.WaitGroup + wg2.Add(2) + flag = true + go func() { + defer wg2.Done() + for i := 0; i < 10; i++ { + tk.MustExec("drop table if exists admin_test9") + tk.MustExec("create table admin_test9 (c1 int, c2 int, c3 int default 1, index (c1))") + } + }() + go func() { + // check that the result set has no duplication + defer wg2.Done() + for i := 0; i < 10; i++ { + result := tk2.MustQuery(`admin show ddl job queries limit 3 offset 2`) + rows := result.Rows() + rowIDs := make(map[string]struct{}) + for _, row := range rows { + rowID := fmt.Sprintf("%v", row[0]) + if _, ok := rowIDs[rowID]; ok { + flag = false + return + } + rowIDs[rowID] = struct{}{} + } + } + }() + wg2.Wait() + require.True(t, flag) + + // check table test + tk.MustExec("create table admin_test1 (c1 int, c2 int default 1, index (c1))") + tk.MustExec("insert admin_test1 (c1) values (21),(22)") + r, err = tk.Exec("admin check table admin_test, admin_test1") + require.NoError(t, err) + require.Nil(t, r) + // error table name + require.Error(t, tk.ExecToErr("admin check table admin_test_error")) + // different index values + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + require.NotNil(t, is) + tb, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("admin_test")) + require.NoError(t, err) + require.Len(t, tb.Indices(), 1) + _, err = tb.Indices()[0].Create(mock.NewContext(), txn, types.MakeDatums(int64(10)), kv.IntHandle(1), nil) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + errAdmin := tk.ExecToErr("admin check table admin_test") + require.Error(t, errAdmin) + + if config.CheckTableBeforeDrop { + tk.MustGetErrMsg("drop table admin_test", errAdmin.Error()) + + // Drop inconsistency index. + tk.MustExec("alter table admin_test drop index c1") + tk.MustExec("admin check table admin_test") + } + // checksum table test + tk.MustExec("create table checksum_with_index (id int, count int, PRIMARY KEY(id), KEY(count))") + tk.MustExec("create table checksum_without_index (id int, count int, PRIMARY KEY(id))") + r, err = tk.Exec("admin checksum table checksum_with_index, checksum_without_index") + require.NoError(t, err) + res := tk.ResultSetToResult(r, "admin checksum table") + // Mocktikv returns 1 for every table/index scan, then we will xor the checksums of a table. + // For "checksum_with_index", we have two checksums, so the result will be 1^1 = 0. + // For "checksum_without_index", we only have one checksum, so the result will be 1. + res.Sort().Check(testkit.Rows("test checksum_with_index 0 2 2", "test checksum_without_index 1 1 1")) + + tk.MustExec("drop table if exists t1;") + tk.MustExec("CREATE TABLE t1 (c2 BOOL, PRIMARY KEY (c2));") + tk.MustExec("INSERT INTO t1 SET c2 = '0';") + tk.MustExec("ALTER TABLE t1 ADD COLUMN c3 DATETIME NULL DEFAULT '2668-02-03 17:19:31';") + tk.MustExec("ALTER TABLE t1 ADD INDEX idx2 (c3);") + tk.MustExec("ALTER TABLE t1 ADD COLUMN c4 bit(10) default 127;") + tk.MustExec("ALTER TABLE t1 ADD INDEX idx3 (c4);") + tk.MustExec("admin check table t1;") + + // Test admin show ddl jobs table name after table has been droped. + tk.MustExec("drop table if exists t1;") + re := tk.MustQuery("admin show ddl jobs 1") + rows := re.Rows() + require.Len(t, rows, 1) + require.Equal(t, "t1", rows[0][2]) + + // Test for reverse scan get history ddl jobs when ddl history jobs queue has multiple regions. + txn, err = store.Begin() + require.NoError(t, err) + historyJobs, err = ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), 20) + require.NoError(t, err) + + // Split region for history ddl job queues. + m := meta.NewMeta(txn) + startKey := meta.DDLJobHistoryKey(m, 0) + endKey := meta.DDLJobHistoryKey(m, historyJobs[0].ID) + cluster.SplitKeys(startKey, endKey, int(historyJobs[0].ID/5)) + + historyJobs2, err := ddl.GetLastNHistoryDDLJobs(meta.NewMeta(txn), 20) + require.NoError(t, err) + require.Equal(t, historyJobs2, historyJobs) +} + +func TestForSelectScopeInUnion(t *testing.T) { + store := testkit.CreateMockStore(t) + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + // A union B for update, the "for update" option belongs to union statement, so + // it should works on both A and B. + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("drop table if exists t") + tk1.MustExec("create table t(a int)") + tk1.MustExec("insert into t values (1)") + + tk1.MustExec("begin") + // 'For update' would act on the second select. + tk1.MustQuery("select 1 as a union select a from t for update") + + tk2.MustExec("use test") + tk2.MustExec("update t set a = a + 1") + + // As tk1 use select 'for update', it should detect conflict and fail. + _, err := tk1.Exec("commit") + require.Error(t, err) + + tk1.MustExec("begin") + tk1.MustQuery("select 1 as a union select a from t limit 5 for update") + tk1.MustQuery("select 1 as a union select a from t order by a for update") + + tk2.MustExec("update t set a = a + 1") + + _, err = tk1.Exec("commit") + require.Error(t, err) +} + +func TestUnsignedDecimalOverflow(t *testing.T) { + store := testkit.CreateMockStore(t) + + tests := []struct { + input interface{} + hasErr bool + err string + }{{ + -1, + true, + "Out of range value for column", + }, { + "-1.1e-1", + true, + "Out of range value for column", + }, { + -1.1, + true, + "Out of range value for column", + }, { + -0, + false, + "", + }, + } + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a decimal(10,2) unsigned)") + for _, test := range tests { + err := tk.ExecToErr("insert into t values (?)", test.input) + if test.hasErr { + require.Error(t, err) + require.Contains(t, err.Error(), test.err) + } else { + require.NoError(t, err) + } + } + + tk.MustExec("set sql_mode=''") + tk.MustExec("delete from t") + tk.MustExec("insert into t values (?)", -1) + tk.MustQuery("select a from t limit 1").Check(testkit.Rows("0.00")) +} + +func TestMaxOneRow(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t1`) + tk.MustExec(`drop table if exists t2`) + tk.MustExec(`create table t1(a double, b double);`) + tk.MustExec(`create table t2(a double, b double);`) + tk.MustExec(`insert into t1 values(1, 1), (2, 2), (3, 3);`) + tk.MustExec(`insert into t2 values(0, 0);`) + tk.MustExec(`set @@tidb_init_chunk_size=1;`) + rs, err := tk.Exec(`select (select t1.a from t1 where t1.a > t2.a) as a from t2;`) + require.NoError(t, err) + + err = rs.Next(context.TODO(), rs.NewChunk(nil)) + require.Error(t, err) + require.Equal(t, "[executor:1242]Subquery returns more than 1 row", err.Error()) + require.NoError(t, rs.Close()) +} + +func TestDoSubquery(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a int)`) + tk.MustExec(`do 1 in (select * from t)`) + tk.MustExec(`insert into t values(1)`) + r, err := tk.Exec(`do 1 in (select * from t)`) + require.NoError(t, err) + require.Nil(t, r) +} + +func TestSummaryFailedUpdate(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int as(-a))") + tk.MustExec("insert into t(a) values(1), (3), (7)") + sm := &testkit.MockSessionManager{ + PS: make([]*util.ProcessInfo, 0), + } + tk.Session().SetSessionManager(sm) + dom.ExpensiveQueryHandle().SetSessionManager(sm) + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustQuery("select variable_value from mysql.GLOBAL_VARIABLES where variable_name = 'tidb_mem_oom_action'").Check(testkit.Rows("LOG")) + + tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("set @@tidb_mem_quota_query=1") + tk.MustMatchErrMsg("update t set t.a = t.a - 1 where t.a in (select a from t where a < 4)", memory.PanicMemoryExceedWarnMsg) + tk.MustExec("set @@tidb_mem_quota_query=1000000000") + tk.MustQuery("select stmt_type from information_schema.statements_summary where digest_text = 'update `t` set `t` . `a` = `t` . `a` - ? where `t` . `a` in ( select `a` from `t` where `a` < ? )'").Check(testkit.Rows("Update")) +} + +func TestIsFastPlan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id int primary key, a int)") + + cases := []struct { + sql string + isFastPlan bool + }{ + {"select a from t where id=1", true}, + {"select a+id from t where id=1", true}, + {"select 1", true}, + {"select @@autocommit", true}, + {"set @@autocommit=1", true}, + {"set @a=1", true}, + {"select * from t where a=1", false}, + {"select * from t", false}, + } + + for _, ca := range cases { + if strings.HasPrefix(ca.sql, "select") { + tk.MustQuery(ca.sql) + } else { + tk.MustExec(ca.sql) + } + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(plannercore.Plan) + require.True(t, ok) + ok = executor.IsFastPlan(p) + require.Equal(t, ca.isFastPlan, ok) + } +} + +func TestCountDistinctJSON(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(j JSON)") + tk.MustExec("insert into t values('2010')") + tk.MustExec("insert into t values('2011')") + tk.MustExec("insert into t values('2012')") + tk.MustExec("insert into t values('2010.000')") + tk.MustExec("insert into t values(cast(? as JSON))", uint64(math.MaxUint64)) + tk.MustExec("insert into t values(cast(? as JSON))", float64(math.MaxUint64)) + + tk.MustQuery("select count(distinct j) from t").Check(testkit.Rows("5")) +} + +func TestHashJoinJSON(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int(11), j JSON, d DOUBLE)") + tk.MustExec("insert into t values(0, '2010', 2010)") + tk.MustExec("insert into t values(1, '2011', 2011)") + tk.MustExec("insert into t values(2, '2012', 2012)") + tk.MustExec("insert into t values(3, cast(? as JSON), ?)", uint64(math.MaxUint64), float64(math.MaxUint64)) + + tk.MustQuery("select /*+inl_hash_join(t2)*/ t1.id, t2.id from t t1 join t t2 on t1.j = t2.d;").Check(testkit.Rows("0 0", "1 1", "2 2")) +} + +func TestTableLockPrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tk.MustExec("create user 'testuser'@'localhost'") + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "testuser", Hostname: "localhost"}, nil, nil, nil)) + tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test'") + tk.MustExec("GRANT LOCK TABLES ON test.* to 'testuser'@'localhost'") + tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1142]SELECT command denied to user 'testuser'@'localhost' for table 't'") + tk.MustExec("REVOKE ALL ON test.* FROM 'testuser'@'localhost'") + tk.MustExec("GRANT SELECT ON test.* to 'testuser'@'localhost'") + tk2.MustGetErrMsg("LOCK TABLE test.t WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test'") + tk.MustExec("GRANT LOCK TABLES ON test.* to 'testuser'@'localhost'") + tk2.MustExec("LOCK TABLE test.t WRITE") + + tk.MustExec("create database test2") + tk.MustExec("create table test2.t2(a int)") + tk2.MustGetErrMsg("LOCK TABLE test.t WRITE, test2.t2 WRITE", "[planner:1044]Access denied for user 'testuser'@'localhost' to database 'test2'") + tk.MustExec("GRANT LOCK TABLES ON test2.* to 'testuser'@'localhost'") + tk2.MustGetErrMsg("LOCK TABLE test.t WRITE, test2.t2 WRITE", "[planner:1142]SELECT command denied to user 'testuser'@'localhost' for table 't2'") + tk.MustExec("GRANT SELECT ON test2.* to 'testuser'@'localhost'") + tk2.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") + tk.MustExec("LOCK TABLE test.t WRITE, test2.t2 WRITE") +} + +func TestGlobalMemoryControl2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk0 := testkit.NewTestKit(t, store) + tk0.MustExec("set global tidb_mem_oom_action = 'cancel'") + tk0.MustExec("set global tidb_server_memory_limit = 1 << 30") + tk0.MustExec("set global tidb_server_memory_limit_sess_min_size = 128") + + sm := &testkit.MockSessionManager{ + PS: []*util.ProcessInfo{tk0.Session().ShowProcess()}, + } + dom.ServerMemoryLimitHandle().SetSessionManager(sm) + go dom.ServerMemoryLimitHandle().Run() + + tk0.MustExec("use test") + tk0.MustExec("create table t(a int)") + tk0.MustExec("insert into t select 1") + for i := 1; i <= 8; i++ { + tk0.MustExec("insert into t select * from t") // 256 Lines + } + + var test []int + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + time.Sleep(100 * time.Millisecond) // Make sure the sql is running. + test = make([]int, 128<<20) // Keep 1GB HeapInuse + wg.Done() + }() + sql := "select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a;" // Need 500MB + require.True(t, strings.Contains(tk0.QueryToErr(sql).Error(), memory.PanicMemoryExceedWarnMsg)) + require.Equal(t, tk0.Session().GetSessionVars().DiskTracker.MaxConsumed(), int64(0)) + wg.Wait() + test[0] = 0 + runtime.GC() +} + +func TestCompileOutOfMemoryQuota(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // Test for issue: https://github.com/pingcap/tidb/issues/38322 + defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") + tk.MustExec("set global tidb_mem_oom_action='CANCEL'") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create table t1(a int, c int, index idx(a))") + tk.MustExec("set tidb_mem_quota_query=10") + err := tk.ExecToErr("select t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a") + require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg) +} + +func TestSignalCheckpointForSort(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/SignalCheckpointForSort", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/SignalCheckpointForSort")) + }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/chunk/SignalCheckpointForSort", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/chunk/SignalCheckpointForSort")) + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") + tk.MustExec("set global tidb_mem_oom_action='CANCEL'") + tk.MustExec("set tidb_mem_quota_query = 100000000") + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + for i := 0; i < 20; i++ { + tk.MustExec(fmt.Sprintf("insert into t values(%d)", i)) + } + tk.Session().GetSessionVars().ConnectionID = 123456 + + err := tk.QueryToErr("select * from t order by a") + require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg) +} + +func TestSessionRootTrackerDetach(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + defer tk.MustExec("set global tidb_mem_oom_action = DEFAULT") + tk.MustExec("set global tidb_mem_oom_action='CANCEL'") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("create table t1(a int, c int, index idx(a))") + tk.MustExec("set tidb_mem_quota_query=10") + tk.MustContainErrMsg("select /*+hash_join(t1)*/ t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a", memory.PanicMemoryExceedWarnMsg) + tk.MustExec("set tidb_mem_quota_query=1000") + rs, err := tk.Exec("select /*+hash_join(t1)*/ t.a, t1.a from t use index(idx), t1 use index(idx) where t.a = t1.a") + require.NoError(t, err) + require.NotNil(t, tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false)) + err = rs.Close() + require.NoError(t, err) + require.Nil(t, tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false)) +} + +func TestPlanReplayerDumpTPCDS(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table catalog_sales +( + cs_sold_date_sk int , + cs_sold_time_sk int , + cs_ship_date_sk int , + cs_bill_customer_sk int , + cs_bill_cdemo_sk int , + cs_bill_hdemo_sk int , + cs_bill_addr_sk int , + cs_ship_customer_sk int , + cs_ship_cdemo_sk int , + cs_ship_hdemo_sk int , + cs_ship_addr_sk int , + cs_call_center_sk int , + cs_catalog_page_sk int , + cs_ship_mode_sk int , + cs_warehouse_sk int , + cs_item_sk int not null, + cs_promo_sk int , + cs_order_number int not null, + cs_quantity int , + cs_wholesale_cost decimal(7,2) , + cs_list_price decimal(7,2) , + cs_sales_price decimal(7,2) , + cs_ext_discount_amt decimal(7,2) , + cs_ext_sales_price decimal(7,2) , + cs_ext_wholesale_cost decimal(7,2) , + cs_ext_list_price decimal(7,2) , + cs_ext_tax decimal(7,2) , + cs_coupon_amt decimal(7,2) , + cs_ext_ship_cost decimal(7,2) , + cs_net_paid decimal(7,2) , + cs_net_paid_inc_tax decimal(7,2) , + cs_net_paid_inc_ship decimal(7,2) , + cs_net_paid_inc_ship_tax decimal(7,2) , + cs_net_profit decimal(7,2) , + primary key (cs_item_sk, cs_order_number) +);`) + tk.MustExec(`create table store_sales +( + ss_sold_date_sk int , + ss_sold_time_sk int , + ss_item_sk int not null, + ss_customer_sk int , + ss_cdemo_sk int , + ss_hdemo_sk int , + ss_addr_sk int , + ss_store_sk int , + ss_promo_sk int , + ss_ticket_number int not null, + ss_quantity int , + ss_wholesale_cost decimal(7,2) , + ss_list_price decimal(7,2) , + ss_sales_price decimal(7,2) , + ss_ext_discount_amt decimal(7,2) , + ss_ext_sales_price decimal(7,2) , + ss_ext_wholesale_cost decimal(7,2) , + ss_ext_list_price decimal(7,2) , + ss_ext_tax decimal(7,2) , + ss_coupon_amt decimal(7,2) , + ss_net_paid decimal(7,2) , + ss_net_paid_inc_tax decimal(7,2) , + ss_net_profit decimal(7,2) , + primary key (ss_item_sk, ss_ticket_number) +);`) + tk.MustExec(`create table date_dim +( + d_date_sk int not null, + d_date_id char(16) not null, + d_date date , + d_month_seq int , + d_week_seq int , + d_quarter_seq int , + d_year int , + d_dow int , + d_moy int , + d_dom int , + d_qoy int , + d_fy_year int , + d_fy_quarter_seq int , + d_fy_week_seq int , + d_day_name char(9) , + d_quarter_name char(6) , + d_holiday char(1) , + d_weekend char(1) , + d_following_holiday char(1) , + d_first_dom int , + d_last_dom int , + d_same_day_ly int , + d_same_day_lq int , + d_current_day char(1) , + d_current_week char(1) , + d_current_month char(1) , + d_current_quarter char(1) , + d_current_year char(1) , + primary key (d_date_sk) +);`) + tk.MustQuery(`plan replayer dump explain with ssci as ( +select ss_customer_sk customer_sk + ,ss_item_sk item_sk +from store_sales,date_dim +where ss_sold_date_sk = d_date_sk + and d_month_seq between 1212 and 1212 + 11 +group by ss_customer_sk + ,ss_item_sk), +csci as( + select cs_bill_customer_sk customer_sk + ,cs_item_sk item_sk +from catalog_sales,date_dim +where cs_sold_date_sk = d_date_sk + and d_month_seq between 1212 and 1212 + 11 +group by cs_bill_customer_sk + ,cs_item_sk) + select sum(case when ssci.customer_sk is not null and csci.customer_sk is null then 1 else 0 end) store_only + ,sum(case when ssci.customer_sk is null and csci.customer_sk is not null then 1 else 0 end) catalog_only + ,sum(case when ssci.customer_sk is not null and csci.customer_sk is not null then 1 else 0 end) store_and_catalog +from ssci left join csci on (ssci.customer_sk=csci.customer_sk + and ssci.item_sk = csci.item_sk) +UNION + select sum(case when ssci.customer_sk is not null and csci.customer_sk is null then 1 else 0 end) store_only + ,sum(case when ssci.customer_sk is null and csci.customer_sk is not null then 1 else 0 end) catalog_only + ,sum(case when ssci.customer_sk is not null and csci.customer_sk is not null then 1 else 0 end) store_and_catalog +from ssci right join csci on (ssci.customer_sk=csci.customer_sk + and ssci.item_sk = csci.item_sk) +limit 100;`) +} + +func TestProcessInfoOfSubQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (i int, j int);") + var wg sync.WaitGroup + wg.Add(1) + go func() { + tk.MustQuery("select 1, (select sleep(count(1) + 2) from t);") + wg.Done() + }() + time.Sleep(time.Second) + tk2.MustQuery("select 1 from information_schema.processlist where TxnStart != '' and info like 'select%sleep% from t%'").Check(testkit.Rows("1")) + wg.Wait() +} diff --git a/pkg/executor/test/executor/main_test.go b/pkg/executor/test/executor/main_test.go new file mode 100644 index 0000000000000..5478aafb701ee --- /dev/null +++ b/pkg/executor/test/executor/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.Cleanup(func(_ int) { + view.Stop() + }), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/fktest/BUILD.bazel b/pkg/executor/test/fktest/BUILD.bazel new file mode 100644 index 0000000000000..27434096bbbe9 --- /dev/null +++ b/pkg/executor/test/fktest/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "fktest_test", + timeout = "short", + srcs = [ + "foreign_key_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 26, + deps = [ + "//pkg/config", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/testkit", + "//pkg/types", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/memory", + "//pkg/util/sqlexec", + "//tests/realtikvtest", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/fktest/foreign_key_test.go b/pkg/executor/test/fktest/foreign_key_test.go new file mode 100644 index 0000000000000..17e086f3154ad --- /dev/null +++ b/pkg/executor/test/fktest/foreign_key_test.go @@ -0,0 +1,2539 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package fk_test + +import ( + "bytes" + "context" + "fmt" + "strconv" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/tests/realtikvtest" + "github.com/stretchr/testify/require" +) + +var foreignKeyTestCase1 = []struct { + prepareSQLs []string + notNull bool +}{ + // Case-1: test unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, a int, b int, unique index(id), unique index(a, b));", + "create table t2 (b int, name varchar(10), a int, id int, unique index(id), unique index (a,b), foreign key fk(a, b) references t1(a, b));", + }, + }, + // Case-2: test unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key, a int, b int, unique index(id), unique index(a, b, id));", + "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b));", + }, + }, + // Case-3: test non-unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, unique index(id), index(a, b));", + "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b));", + }, + }, + // Case-4: test non-unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, unique index(id), index(a, b, id));", + "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b));", + }, + }, + //Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, unique index(id), primary key (a, b));", + "create table t2 (b int, name varchar(10), a int, id int, unique index(id), primary key (a, b), foreign key fk(a, b) references t1(a, b));", + }, + notNull: true, + }, + // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, unique index(id), primary key (a, b));", + "create table t2 (b int, a int, name varchar(10), id int, unique index(id), primary key (a, b), foreign key fk(a, b) references t1(a, b));", + }, + notNull: true, + }, + // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, unique index(id), primary key (a, b, id));", + "create table t2 (b int, a int, id int, name varchar(10), unique index(id), primary key (a, b, id), foreign key fk(a, b) references t1(a, b));", + }, + notNull: true, + }, + // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, unique index(id), primary key (a, b, id));", + "create table t2 (name varchar(10), b int, a int, id int, unique index(id), primary key (a, b, id), foreign key fk(a, b) references t1(a, b));", + }, + notNull: true, + }, +} + +func TestForeignKeyOnInsertChildTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + tk.MustExec("create table t_data (id int, a int, b int)") + tk.MustExec("insert into t_data (id, a, b) values (1, 1, 1), (2, 2, 2);") + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1);") + tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1)") + if !ca.notNull { + tk.MustExec("insert into t2 (id, a, b) values (2, null, 1)") + tk.MustExec("insert into t2 (id, a, b) values (3, 1, null)") + tk.MustExec("insert into t2 (id, a, b) values (4, null, null)") + } + tk.MustGetDBError("insert into t2 (id, a, b) values (5, 1, 0);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 (id, a, b) values (6, 0, 1);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 (id, a, b) values (7, 2, 2);", plannercore.ErrNoReferencedRow2) + // Test insert from select. + tk.MustExec("delete from t2") + tk.MustExec("insert into t2 (id, a, b) select id, a, b from t_data where t_data.id=1") + tk.MustGetDBError("insert into t2 (id, a, b) select id, a, b from t_data where t_data.id=2", plannercore.ErrNoReferencedRow2) + + // Test in txn + tk.MustExec("delete from t2") + tk.MustExec("begin") + tk.MustExec("delete from t1 where a=1") + tk.MustGetDBError("insert into t2 (id, a, b) values (1, 1, 1)", plannercore.ErrNoReferencedRow2) + tk.MustExec("insert into t1 (id, a, b) values (2, 2, 2)") + tk.MustExec("insert into t2 (id, a, b) values (2, 2, 2)") + tk.MustExec("rollback") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) + } + + // Case-10: test primary key is handle and contain foreign key column, and foreign key column has default value. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1 (id int,a int, primary key(id));") + tk.MustExec("create table t2 (id int key,a int not null default 0, index (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 values (1, 1);") + tk.MustExec("insert into t2 values (1, 1);") + tk.MustGetDBError("insert into t2 (id) values (10);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("insert into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) + + // Case-11: test primary key is handle and contain foreign key column, and foreign key column doesn't have default value. + tk.MustExec("drop table if exists t2;") + tk.MustExec("create table t2 (id int key,a int, index (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t2 values (1, 1);") + tk.MustExec("insert into t2 (id) values (10);") + tk.MustGetDBError("insert into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) +} + +func TestForeignKeyOnInsertDuplicateUpdateChildTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") + + sqls := []string{ + "insert into t2 (id, a, b, name) values (1, 12, 22, 'b') on duplicate key update a = 100", + "insert into t2 (id, a, b, name) values (1, 13, 23, 'c') on duplicate key update a = a+10", + "insert into t2 (id, a, b, name) values (1, 14, 24, 'd') on duplicate key update a = a + 100", + "insert into t2 (id, a, b, name) values (1, 14, 24, 'd') on duplicate key update a = 12, b = 23", + } + for _, sqlStr := range sqls { + tk.MustGetDBError(sqlStr, plannercore.ErrNoReferencedRow2) + } + tk.MustExec("insert into t2 (id, a, b, name) values (1, 14, 26, 'b') on duplicate key update a = 12, b = 22, name = 'x'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 12 22 x")) + if !ca.notNull { + tk.MustExec("insert into t2 (id, a, b, name) values (1, 14, 26, 'b') on duplicate key update a = null, b = 22, name = 'y'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 22 y")) + tk.MustExec("insert into t2 (id, a, b, name) values (1, 15, 26, 'b') on duplicate key update b = null, name = 'z'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 z")) + } + tk.MustExec("insert into t2 (id, a, b, name) values (1, 15, 26, 'b') on duplicate key update a=13,b=23, name = 'c'") + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 13 23 c")) + + // Test In txn. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (2, 11, 21, 'a')") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (2, 14, 26, 'b') on duplicate key update a = 12, b = 22, name = 'x'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 12 22 x")) + tk.MustExec("rollback") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 11 21 a")) + + tk.MustExec("begin") + tk.MustExec("delete from t1 where id=3") + tk.MustGetDBError("insert into t2 (id, a, b, name) values (2, 13, 23, 'y') on duplicate key update a = 13, b = 23, name = 'y'", plannercore.ErrNoReferencedRow2) + tk.MustExec("insert into t2 (id, a, b, name) values (2, 14, 24, 'z') on duplicate key update a = 14, b = 24, name = 'z'") + tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") + tk.MustExec("insert into t2 (id, a, b, name) values (2, 15, 25, 'o') on duplicate key update a = 15, b = 25, name = 'o'") + tk.MustExec("delete from t1 where id=1") + tk.MustGetDBError("insert into t2 (id, a, b, name) values (2, 11, 21, 'y') on duplicate key update a = 11, b = 21, name = 'p'", plannercore.ErrNoReferencedRow2) + tk.MustExec("commit") + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("2 15 25 o")) + } + + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") + tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") + + tk.MustExec("insert into t2 (id, a) values (11, 1) on duplicate key update a = 2, name = 'b'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 2 21 b")) + tk.MustExec("insert into t2 (id, a, b) values (11, 2, 22) on duplicate key update a = 3, name = 'c'") + tk.MustExec("insert into t2 (id, a, name) values (11, 3, 'b') on duplicate key update b = b+10, name = 'd'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 3 31 d")) + tk.MustExec("insert into t2 (id, a, name) values (11, 3, 'b') on duplicate key update id = 1, name = 'f'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 31 f")) + tk.MustGetDBError("insert into t2 (id, a, name) values (1, 3, 'b') on duplicate key update a = 10", plannercore.ErrNoReferencedRow2) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 31 f")) + + // Test In txn. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 21, 'a')") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a) values (11, 1) on duplicate key update a = 2, name = 'b'") + tk.MustExec("rollback") + + tk.MustExec("begin") + tk.MustExec("delete from t1 where id=2") + tk.MustGetDBError("insert into t2 (id, a) values (1, 1) on duplicate key update a = 2, name = 'b'", plannercore.ErrNoReferencedRow2) + tk.MustExec("insert into t2 (id, a) values (1, 1) on duplicate key update a = 3, name = 'c'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 21 c")) + tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") + tk.MustExec("insert into t2 (id, a) values (3, 3) on duplicate key update a = 5, name = 'd'") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 5 21 d")) + tk.MustExec("delete from t1 where id=1") + tk.MustGetDBError("insert into t2 (id, a) values (1, 5) on duplicate key update a = 1, name = 'e'", plannercore.ErrNoReferencedRow2) + tk.MustExec("commit") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 5 21 d")) +} + +func TestForeignKeyCheckAndLock(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@foreign_key_checks=1") + tk2.MustExec("use test") + + if !*realtikvtest.WithRealTiKV { + // Unistore doesn't write lock records on secondary keys with value unchanged, causing it incorrectly ignores + // conflicts between transactions on these kinds of keys. This may make the test fail if fair locking is + // enabled. So disable it if it's not running with real tikv. + tk.MustExec("set @@tidb_pessimistic_txn_fair_locking = 0") + tk2.MustExec("set @@tidb_pessimistic_txn_fair_locking = 0") + } + + cases := []struct { + prepareSQLs []string + }{ + // Case-1: test unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, name varchar(10), unique index (id))", + "create table t2 (a int, name varchar(10), unique index (a), foreign key fk(a) references t1(id))", + }, + }, + //Case-2: test unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int, name varchar(10), unique index (id, name))", + "create table t2 (name varchar(10), a int, unique index (a, name), foreign key fk(a) references t1(id))", + }, + }, + //Case-3: test non-unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, name varchar(10), index (id))", + "create table t2 (a int, name varchar(10), index (a), foreign key fk(a) references t1(id))", + }, + }, + //Case-4: test non-unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int, name varchar(10), index (id, name))", + "create table t2 (name varchar(10), a int, index (a, name), foreign key fk(a) references t1(id))", + }, + }, + //Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, name varchar(10), primary key (id))", + "create table t2 (a int, name varchar(10), primary key (a), foreign key fk(a) references t1(id))", + }, + }, + //Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, name varchar(10), primary key (id))", + "create table t2 (a int, name varchar(10), primary key (a), foreign key fk(a) references t1(id))", + }, + }, + //Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, name varchar(10), primary key (id, name))", + "create table t2 (a int, name varchar(10), primary key (a , name), foreign key fk(a) references t1(id))", + }, + }, + // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, name varchar(10), primary key (id, name))", + "create table t2 (a int, name varchar(10), primary key (a , name), foreign key fk(a) references t1(id))", + }, + }, + } + + for _, ca := range cases { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + // Test delete in optimistic txn + tk.MustExec("insert into t1 (id, name) values (1, 'a');") + // Test insert child table + tk.MustExec("begin optimistic") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + tk2.MustExec("delete from t1 where id = 1") + err := tk.ExecToErr("commit") + require.NotNil(t, err) + require.Contains(t, err.Error(), "Write conflict") + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows()) + tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows()) + + // Test update in optimistic txn + tk.MustExec("insert into t1 (id, name) values (1, 'a');") + tk.MustExec("begin optimistic") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + tk2.MustExec("update t1 set id=2 where id = 1") + err = tk.ExecToErr("commit") + require.NotNil(t, err) + require.Contains(t, err.Error(), "Write conflict") + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("2 a")) + tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows()) + + // Test update child table + tk.MustExec("delete from t1") + tk.MustExec("delete from t2") + tk.MustExec("insert into t1 (id, name) values (1, 'a'), (2, 'b');") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + tk.MustExec("begin optimistic") + tk.MustExec("update t2 set a=2 where a = 1") + tk2.MustExec("delete from t1 where id = 2") + err = tk.ExecToErr("commit") + require.Error(t, err) + require.Contains(t, err.Error(), "Write conflict") + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a")) + tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("1 a")) + + // Test in pessimistic txn + tk.MustExec("delete from t2") + // Test insert child table + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + err := tk2.ExecToErr("update t1 set id = 2 where id = 1") + require.NotNil(t, err) + require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 50) + tk.MustExec("commit") + wg.Wait() + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a")) + tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("1 a")) + + // Test update child table + tk.MustExec("insert into t1 (id, name) values (2, 'b');") + tk.MustExec("begin pessimistic") + tk.MustExec("update t2 set a=2 where a = 1") + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + err := tk2.ExecToErr("update t1 set id = 3 where id = 2") + require.NotNil(t, err) + require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 50) + tk.MustExec("commit") + wg.Wait() + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) + tk.MustQuery("select a, name from t2 order by name").Check(testkit.Rows("2 a")) + + // Test delete parent table in pessimistic txn + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + err := tk2.ExecToErr("delete from t1 where id = 1") + require.NotNil(t, err) + require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 50) + tk.MustExec("commit") + wg.Wait() + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) + tk.MustQuery("select a, name from t2 order by a").Check(testkit.Rows("1 a", "2 a")) + + tk.MustExec("delete from t2") + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + err := tk2.ExecToErr("delete from t1 where id < 5") // Also test the non-fast path + require.NotNil(t, err) + require.Equal(t, "[planner:1451]Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t2`, CONSTRAINT `fk` FOREIGN KEY (`a`) REFERENCES `t1` (`id`))", err.Error()) + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 50) + tk.MustExec("commit") + wg.Wait() + tk.MustQuery("select id, name from t1 order by name").Check(testkit.Rows("1 a", "2 b")) + tk.MustQuery("select a, name from t2 order by a").Check(testkit.Rows("1 a")) + + // Test delete parent table in auto-commit txn + // TODO(crazycs520): fix following test. + /* + tk.MustExec("delete from t2") + tk.MustExec("begin pessimistic") + tk.MustExec("delete from t2;") // active txn + tk.MustExec("insert into t2 (a, name) values (1, 'a');") + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + }() + time.Sleep(time.Millisecond * 50) + tk.MustExec("commit") + wg.Wait() + */ + } +} + +func TestForeignKeyOnInsertOnDuplicateParentTableCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + if !ca.notNull { + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") + + tk.MustExec("insert into t1 (id, a) values (2, 12) on duplicate key update a=a+100, b=b+200") + tk.MustExec("insert into t1 (id, a) values (3, 13), (2, 12) on duplicate key update a=a+1000, b=b+2000") + tk.MustExec("insert into t1 (id) values (5), (6), (7) on duplicate key update a=a+10000, b=b+20000") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) + + tk.MustGetDBError("insert into t1 (id, a) values (1, 11) on duplicate key update a=a+10, b=b+20", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) + } else { + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a');") + + tk.MustExec("insert into t1 (id, a, b) values (2, 12, 22) on duplicate key update a=a+100, b=b+200") + tk.MustExec("insert into t1 (id, a, b) values (3, 13, 23), (2, 12, 22) on duplicate key update a=a+1000, b=b+2000") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) + + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21) on duplicate key update id=11") + tk.MustGetDBError("insert into t1 (id, a, b) values (11, 11, 21) on duplicate key update a=a+10, b=b+20", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1112 2222", "3 1013 2023", "4 14 24", "11 11 21")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) + } + } + + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") + tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") + + tk.MustExec("insert into t1 (id, a, b) values (2, 0, 0), (3, 0, 0) on duplicate key update id=id+100") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) + + tk.MustExec("insert into t1 (id, a, b) values (1, 0, 0) on duplicate key update a=a+100") + tk.MustGetDBError("insert into t1 (id, a, b) values (1, 0, 0) on duplicate key update id=100+id", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 111 21", "4 14 24", "102 12 22", "103 13 23")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 1 21 a")) + + // Case-10: Test insert into parent table failed cause by foreign key check, see https://github.com/pingcap/tidb/issues/39200. + tk.MustExec("drop table if exists t1,t2;") + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (id int, foreign key fk(id) references t1(id));") + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("insert into t1 values (1) on duplicate key update id=2") +} + +func TestForeignKeyConcurrentInsertChildTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int, a int, primary key (id));") + tk.MustExec("create table t2 (id int, a int, index(a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 (id, a) values (1, 11),(2, 12), (3, 13), (4, 14)") + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + for cnt := 0; cnt < 20; cnt++ { + id := cnt%4 + 1 + sql := fmt.Sprintf("insert into t2 (id, a) values (%v, %v)", cnt, id) + tk.MustExec(sql) + } + }() + } + wg.Wait() +} + +func TestForeignKeyOnUpdateChildTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") + + sqls := []string{ + "update t2 set a=100, b = 200 where id = 1", + "update t2 set a=a+10, b = b+20 where a = 11", + "update t2 set a=a+100, b = b+200", + "update t2 set a=12, b = 23 where id = 1", + } + for _, sqlStr := range sqls { + tk.MustGetDBError(sqlStr, plannercore.ErrNoReferencedRow2) + } + tk.MustExec("update t2 set a=12, b = 22 where id = 1") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 12 22 a")) + if !ca.notNull { + tk.MustExec("update t2 set a=null, b = 22 where a = 12 ") + tk.MustExec("update t2 set b = null where b = 22 ") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a")) + } + tk.MustExec("update t2 set a=13, b=23 where id = 1") + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 13 23 a")) + + // Test In txn. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a')") + tk.MustExec("begin") + tk.MustExec("update t2 set a=12, b=22 where id=1") + tk.MustExec("rollback") + + tk.MustExec("begin") + tk.MustExec("delete from t1 where id=2") + tk.MustGetDBError("update t2 set a=12, b=22 where id=1", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t2 set a=13, b=23 where id=1") + tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") + tk.MustExec("update t2 set a=15, b=25 where id=1") + tk.MustExec("delete from t1 where id=1") + tk.MustGetDBError("update t2 set a=11, b=21 where id=1", plannercore.ErrNoReferencedRow2) + tk.MustExec("commit") + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 15 25 a")) + } + + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") + tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") + tk.MustExec("update t2 set a = 2 where id = 11") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 2 21 a")) + tk.MustExec("update t2 set a = 3 where id = 11") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 3 21 a")) + tk.MustExec("update t2 set b=b+1 where id = 11") + tk.MustQuery("select id, a, b , name from t2 order by id").Check(testkit.Rows("11 3 22 a")) + tk.MustExec("update t2 set id = 1 where id = 11") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 22 a")) + tk.MustGetDBError("update t2 set a = 10 where id = 1", plannercore.ErrNoReferencedRow2) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 3 22 a")) + + // Test In txn. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 21, 'a')") + tk.MustExec("begin") + tk.MustExec("update t2 set a=2, b=22 where id=1") + tk.MustExec("rollback") + + tk.MustExec("begin") + tk.MustExec("delete from t1 where id=2") + tk.MustGetDBError("update t2 set a=2, b=22 where id=1", plannercore.ErrNoReferencedRow2) + tk.MustExec("update t2 set a=3, b=23 where id=1") + tk.MustExec("insert into t1 (id, a, b) values (5, 15, 25)") + tk.MustExec("update t2 set a=5, b=25 where id=1") + tk.MustExec("delete from t1 where id=1") + tk.MustGetDBError("update t2 set a=1, b=21 where id=1", plannercore.ErrNoReferencedRow2) + tk.MustExec("commit") + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 5 25 a")) +} + +func TestForeignKeyOnUpdateParentTableCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + if !ca.notNull { + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") + + tk.MustExec("update t1 set a=a+100, b = b+200 where id = 2") + tk.MustExec("update t1 set a=a+1000, b = b+2000 where a = 13 or b=222") + tk.MustExec("update t1 set a=a+10000, b = b+20000 where id = 5 or a is null or b is null") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) + tk.MustGetDBError("update t1 set a=a+10, b = b+20 where id = 1 or a = 1112 or b = 24", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24", "5 10015 ", "6 20026", "7 ")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a", "5 15 e", "6 26 f", "7 g")) + } else { + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a');") + tk.MustExec("update t1 set a=a+100, b = b+200 where id = 2") + tk.MustExec("update t1 set a=a+1000, b = b+2000 where a = 13 or b=222") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) + tk.MustGetDBError("update t1 set a=a+10, b = b+20 where id = 1 or a = 1112 or b = 24", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "2 1112 2222", "3 1013 2023", "4 14 24")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 11 21 a")) + } + } + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") + tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a')") + tk.MustExec("update t1 set id = id + 100 where id =2 or a = 13") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) + tk.MustGetDBError("update t1 set id = id+10 where id = 1 or b = 24", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 11 21", "4 14 24", "102 12 22", "103 13 23")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 1 21 a")) +} + +func TestForeignKeyOnDeleteParentTableCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + if !ca.notNull { + tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1), (5, 5, null), (6, null, 6), (7, null, null);;") + + tk.MustExec("delete from t1 where id = 2") + tk.MustExec("delete from t1 where a = 3 or b = 4") + tk.MustExec("delete from t1 where a = 5 or b = 6 or a is null or b is null;") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) + } else { + tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4);") + tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1);") + + tk.MustExec("delete from t1 where id = 2") + tk.MustExec("delete from t1 where a = 3 or b = 4") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) + } + models := []string{"pessimistic", "optimistic"} + for _, model := range models { + // Test in transaction. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin " + model) + tk.MustExec("insert into t1 (id, a, b) values (1, 1, 1), (2, 2, 2);") + tk.MustExec("insert into t2 (id, a, b) values (1, 1, 1);") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + tk.MustExec("delete from t1 where id = 2") + tk.MustExec("delete from t2 where id = 1") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows()) + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) + } + } + + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1 (id int,a int, primary key(id));") + tk.MustExec("create table t2 (id int,a int, primary key(a), foreign key fk(a) references t1(id));") + tk.MustExec("insert into t1 values (1, 1), (2, 2), (3, 3), (4, 4);") + tk.MustExec("insert into t2 values (1, 1);") + tk.MustExec("delete from t1 where id = 2;") + tk.MustExec("delete from t1 where a = 3 or a = 4;") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select id, a from t1 order by id").Check(testkit.Rows("1 1")) +} + +func TestForeignKeyOnDeleteCascade(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + cases := []struct { + prepareSQLs []string + }{ + // Case-1: test unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, a int, b int, unique index(a, b));", + "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-2: test unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key, a int, b int, unique index(a, b, id));", + "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-3: test non-unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b));", + "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-4: test non-unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b, id));", + "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + } + + for idx, ca := range cases { + tk.MustExec("drop table if exists t1, t2;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("delete from t1 where id = 2 or a = 2") + tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7) or id=7") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("5 5 ")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("5 5 e", "6 6 f", "7 g")) + + // Test in transaction. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") + tk.MustExec("delete from t1 where id = 1 or a = 2") + tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("5 5 e", "6 6 f", "7 g")) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 2 2 b")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (1, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'c')") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 1 c", "2 2 2 b")) + tk.MustExec("delete from t1") + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + // only test in non-unique index + if idx >= 2 { + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("begin") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + tk.MustExec("delete from t1 where id = 2") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (3, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("3 1 1 e")) + + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows()) + } + } + + cases = []struct { + prepareSQLs []string + }{ + // Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, primary key (a, b));", + "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, primary key (a, b));", + "create table t2 (name varchar(10), b int, a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, primary key (a, b, id));", + "create table t2 (b int, a int, name varchar(10), id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, primary key (a, b, id));", + "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE CASCADE);", + }, + }, + // Case-9: test primary key is handle and contain foreign key column. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, primary key (id));", + "create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id) ON DELETE CASCADE);", + }, + }, + } + for _, ca := range cases { + tk.MustExec("drop table if exists t1, t2;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") + tk.MustExec("delete from t1 where id = 1 or a = 2") + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("3 3 3", "4 4 4")) + tk.MustExec("delete from t1 where a in (2,3) or b < 5") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + // test in transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") + tk.MustExec("delete from t1 where id = 1 or a = 2") + tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows()) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("2 2 2 b")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (1, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'c')") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 1 c", "2 2 2 b")) + tk.MustExec("delete from t1") + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + } +} + +func TestForeignKeyOnDeleteCascade2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + // Test cascade delete in self table. + tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") + tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("delete from t1 where id=11") + tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1", "10", "12", "100", "101", "102", "120", "121", "122", "1000")) + tk.MustExec("delete from t1 where id=1") + // The affect rows doesn't contain the cascade deleted rows, the behavior is compatible with MySQL. + require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) + tk.MustQuery("select id from t1 order by id").Check(testkit.Rows()) + + // Test explain analyze with foreign key cascade. + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("explain analyze delete from t1 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + + // Test string type foreign key. + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id varchar(10) key, name varchar(10), leader varchar(10), index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") + tk.MustExec("insert into t1 values (1, 'boss', null)") + tk.MustExec("insert into t1 values (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") + tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("delete from t1 where id=11") + tk.MustQuery("select id from t1 order by id").Check(testkit.Rows("1", "10", "100", "1000", "101", "102", "12", "120", "121", "122")) + tk.MustExec("delete from t1 where id=1") + require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) + tk.MustQuery("select id from t1 order by id").Check(testkit.Rows()) + + // Test cascade delete depth. + tk.MustExec("drop table t1") + tk.MustExec("create table t1(id int primary key, pid int, index(pid), foreign key(pid) references t1(id) on delete cascade);") + tk.MustExec("insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);") + tk.MustGetDBError("delete from t1 where id=0;", exeerrors.ErrForeignKeyCascadeDepthExceeded) + tk.MustExec("delete from t1 where id=15;") + tk.MustExec("delete from t1 where id=0;") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustExec("insert into t1 values(0,0)") + tk.MustExec("delete from t1 where id=0;") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + + // Test for cascade delete failed. + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key, foreign key (id) references t1 (id) on delete cascade)") + tk.MustExec("create table t3 (id int key, foreign key (id) references t2(id))") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("insert into t3 values (1)") + // test in autocommit transaction + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1")) + // Test in transaction and commit transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (2),(3),(4)") + tk.MustExec("insert into t2 values (2),(3)") + tk.MustExec("insert into t3 values (3)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 2") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + // Test in transaction and rollback transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (5), (6)") + tk.MustExec("insert into t2 values (4), (5), (6)") + tk.MustExec("insert into t3 values (5)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 4") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "5", "6")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3", "5", "6")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3", "5")) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + tk.MustExec("delete from t3 where id = 1") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select * from t1").Check(testkit.Rows("3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("3")) + // Test in autocommit=0 transaction + tk.MustExec("set autocommit=0") + tk.MustExec("insert into t1 values (1), (2)") + tk.MustExec("insert into t2 values (1), (2)") + tk.MustExec("insert into t3 values (1)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 2") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + tk.MustExec("set autocommit=1") + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + + // Test StmtCommit after fk cascade executor execute finish. + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("create table t0(id int primary key);") + tk.MustExec("create table t1(id int primary key, pid int, index(pid), a int, foreign key(pid) references t1(id) on delete cascade, foreign key(a) references t0(id) on delete cascade);") + tk.MustExec("insert into t0 values (0)") + tk.MustExec("insert into t1 values (0, 0, 0)") + tk.MustExec("insert into t1 (id, pid) values(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);") + tk.MustGetDBError("delete from t0 where id=0;", exeerrors.ErrForeignKeyCascadeDepthExceeded) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id=14;") + tk.MustExec("delete from t0 where id=0;") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t0").Check(testkit.Rows()) + tk.MustQuery("select * from t1").Check(testkit.Rows()) + + // Test multi-foreign key cascade in one table. + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key)") + tk.MustExec("create table t3 (id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete cascade, " + + "constraint fk_id2 foreign key (id2) references t2 (id) on delete cascade)") + tk.MustExec("insert into t1 values (1), (2), (3)") + tk.MustExec("insert into t2 values (1), (2), (3)") + tk.MustExec("insert into t3 values (1,1), (1, 2), (1, 3), (2, 1), (2, 2)") + tk.MustExec("delete from t1 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows("2 1", "2 2")) + tk.MustExec("create table t4 (id3 int key, constraint fk_id3 foreign key (id3) references t3 (id2))") + tk.MustExec("insert into t4 values (2)") + tk.MustGetDBError("delete from t1 where id = 2", plannercore.ErrRowIsReferenced2) + tk.MustGetDBError("delete from t2 where id = 2", plannercore.ErrRowIsReferenced2) + tk.MustExec("delete from t2 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) + tk.MustQuery("select * from t2").Check(testkit.Rows("2", "3")) + tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows("2 2")) + + // Test multi-foreign key cascade in one table. + tk.MustExec("drop table if exists t1,t2,t3, t4") + tk.MustExec(`create table t1 (c0 int, index(c0))`) + cnt := 20 + for i := 1; i < cnt; i++ { + tk.MustExec(fmt.Sprintf("alter table t1 add column c%v int", i)) + tk.MustExec(fmt.Sprintf("alter table t1 add index idx_%v (c%v) ", i, i)) + tk.MustExec(fmt.Sprintf("alter table t1 add foreign key (c%v) references t1 (c%v) on delete cascade", i, i-1)) + } + for i := 0; i < cnt; i++ { + vals := strings.Repeat(strconv.Itoa(i)+",", 20) + tk.MustExec(fmt.Sprintf("insert into t1 values (%v)", vals[:len(vals)-1])) + } + tk.MustExec("delete from t1 where c0 in (0, 1, 2, 3, 4)") + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("15")) + + // Test foreign key cascade execution meet lock and do retry. + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@global.tidb_enable_foreign_key=1") + tk2.MustExec("set @@foreign_key_checks=1") + tk2.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (id int key, name varchar(10), pid int, index(pid), constraint fk foreign key (pid) references t1 (id) on delete cascade)") + tk.MustExec("insert into t1 values (1, 'boss', null), (2, 'a', 1), (3, 'b', 1), (4, 'c', '2')") + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t1 values (5, 'd', 3)") + tk2.MustExec("begin pessimistic") + tk2.MustExec("insert into t1 values (6, 'e', 4)") + tk2.MustExec("delete from t1 where id=2") + tk2.MustExec("commit") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + + // Test handle many foreign key value in one cascade. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (id int auto_increment key, b int);") + tk.MustExec("create table t2 (id int, b int, foreign key fk(id) references t1(id) on delete cascade)") + tk.MustExec("insert into t1 (b) values (1),(1),(1),(1),(1),(1),(1),(1);") + for i := 0; i < 12; i++ { + tk.MustExec("insert into t1 (b) select b from t1") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) + tk.MustExec("insert into t2 select * from t1") + tk.MustExec("delete from t1") + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("0")) + tk.MustQuery("select count(*) from t2").Check(testkit.Rows("0")) +} + +func TestForeignKeyGenerateCascadeAST(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + fkValues := [][]types.Datum{ + {types.NewDatum(1), types.NewDatum("a")}, + {types.NewDatum(2), types.NewDatum("b")}, + } + cols := []*model.ColumnInfo{ + {ID: 1, Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeLonglong)}, + {ID: 2, Name: model.NewCIStr("name"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, + } + restoreFn := func(stmt ast.StmtNode) string { + var sb strings.Builder + fctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) + err := stmt.Restore(fctx) + require.NoError(t, err) + return sb.String() + } + checkStmtFn := func(stmt ast.StmtNode, sql string) { + exec, ok := tk.Session().(sqlexec.RestrictedSQLExecutor) + require.True(t, ok) + expectedStmt, err := exec.ParseWithParams(context.Background(), sql) + require.NoError(t, err) + require.Equal(t, restoreFn(expectedStmt), restoreFn(stmt)) + } + var stmt ast.StmtNode + stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) + checkStmtFn(stmt, "delete from test.t2 where (a,name) in ((1,'a'), (2,'b'))") + stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) + checkStmtFn(stmt, "delete from test.t2 use index(idx) where (a,name) in ((1,'a'), (2,'b'))") + stmt = executor.GenCascadeSetNullAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) + checkStmtFn(stmt, "update test.t2 set a = null, name = null where (a,name) in ((1,'a'), (2,'b'))") + stmt = executor.GenCascadeSetNullAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) + checkStmtFn(stmt, "update test.t2 use index(idx) set a = null, name = null where (a,name) in ((1,'a'), (2,'b'))") + newValue1 := []types.Datum{types.NewDatum(10), types.NewDatum("aa")} + couple := &executor.UpdatedValuesCouple{ + NewValues: newValue1, + OldValuesList: fkValues, + } + stmt = executor.GenCascadeUpdateAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, couple) + checkStmtFn(stmt, "update test.t2 set a = 10, name = 'aa' where (a,name) in ((1,'a'), (2,'b'))") + stmt = executor.GenCascadeUpdateAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, couple) + checkStmtFn(stmt, "update test.t2 use index(idx) set a = 10, name = 'aa' where (a,name) in ((1,'a'), (2,'b'))") + // Test for 1 fk column. + fkValues = [][]types.Datum{{types.NewDatum(1)}, {types.NewDatum(2)}} + cols = []*model.ColumnInfo{{ID: 1, Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeLonglong)}} + stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr(""), cols, fkValues) + checkStmtFn(stmt, "delete from test.t2 where a in (1,2)") + stmt = executor.GenCascadeDeleteAST(model.NewCIStr("test"), model.NewCIStr("t2"), model.NewCIStr("idx"), cols, fkValues) + checkStmtFn(stmt, "delete from test.t2 use index(idx) where a in (1,2)") +} + +func TestForeignKeyOnDeleteSetNull(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + cases := []struct { + prepareSQLs []string + }{ + // Case-1: test unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, a int, b int, unique index(a, b));", + "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", + }, + }, + // Case-2: test unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key, a int, b int, unique index(a, b, id));", + "create table t2 (b int, a int, id int key, name varchar(10), unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", + }, + }, + // Case-3: test non-unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b));", + "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", + }, + }, + // Case-4: test non-unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b, id));", + "create table t2 (name varchar(10), b int, a int, id int key, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON DELETE SET NULL);", + }, + }, + } + + for idx, ca := range cases { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") + tk.MustExec("delete from t1 where id = 1 or a = 2") + tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "3 c", "4 d", "5 5 e", "6 6 f", "7 g")) + + // Test in transaction. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") + tk.MustExec("delete from t1 where id = 1 or a = 2") + tk.MustExec("delete from t1 where a in (2,3,4) or b in (5,6,7)") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "3 c", "4 d", "5 5 e", "6 6 f", "7 g")) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 2 2 b")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (11, 1, 1, 'c')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (1, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 1, 'c')") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1", "2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 2 2 b", "11 1 1 c")) + tk.MustExec("delete from t1") + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b", "11 c")) + + // only test in non-unique index + if idx >= 2 { + tk.MustExec("delete from t2") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("begin") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + tk.MustExec("delete from t1 where id = 2") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 a")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (2, 1, 1, 'b')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (3, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "3 1 1 e")) + + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("2 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 a", "2 b")) + } + } +} + +func TestForeignKeyOnDeleteSetNull2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + // Test cascade delete in self table. + tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON DELETE SET NULL);") + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") + tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("delete from t1 where id=11") + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) + tk.MustExec("delete from t1 where id=1") + // The affect rows doesn't contain the cascade deleted rows, the behavior is compatible with MySQL. + require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("10 l1_a ", "12 l1_c ", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) + + // Test explain analyze with foreign key cascade. + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("explain analyze delete from t1 where id=1") + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("10 l1_a ", "11 l1_b ", "12 l1_c ")) + + // Test string type foreign key. + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id varchar(10) key, name varchar(10), leader varchar(10), index(leader), foreign key (leader) references t1(id) ON DELETE SET NULL);") + tk.MustExec("insert into t1 values (1, 'boss', null)") + tk.MustExec("insert into t1 values (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10), (101, 'l2_a2', 10), (102, 'l2_a3', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11), (111, 'l2_b2', 11), (112, 'l2_b3', 11)") + tk.MustExec("insert into t1 values (120, 'l2_c1', 12), (121, 'l2_c2', 12), (122, 'l2_c3', 12)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("delete from t1 where id=11") + tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) + tk.MustExec("delete from t1 where id=1") + require.Equal(t, uint64(1), tk.Session().GetSessionVars().StmtCtx.AffectedRows()) + tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("10 l1_a ", "12 l1_c ", "100 l2_a1 10", "101 l2_a2 10", "102 l2_a3 10", "110 l2_b1 ", "111 l2_b2 ", "112 l2_b3 ", "120 l2_c1 12", "121 l2_c2 12", "122 l2_c3 12", "1000 l3_a1 100")) + + // Test cascade set null depth. + tk.MustExec("drop table t1") + tk.MustExec("create table t1(id int primary key, pid int, index(pid), foreign key(pid) references t1(id) on delete set null);") + tk.MustExec("insert into t1 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13),(15,14);") + tk.MustExec("delete from t1 where id=0;") + tk.MustQuery("select id, pid from t1").Check(testkit.Rows("1 ", "2 1", "3 2", "4 3", "5 4", "6 5", "7 6", "8 7", "9 8", "10 9", "11 10", "12 11", "13 12", "14 13", "15 14")) + + // Test for cascade delete failed. + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int, foreign key (id) references t1 (id) on delete set null)") + tk.MustExec("create table t3 (id int, foreign key (id) references t2(id))") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("insert into t3 values (1)") + // test in autocommit transaction + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1")) + // Test in transaction and commit transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (2),(3),(4)") + tk.MustExec("insert into t2 values (2),(3)") + tk.MustExec("insert into t3 values (3)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 2") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + tk.MustExec("commit") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) + tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) + // Test in transaction and rollback transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (5), (6)") + tk.MustExec("insert into t2 values (4), (5), (6)") + tk.MustExec("insert into t3 values (5)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 4") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "5", "6")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "1", "3", "5", "6")) + tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3", "5")) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "1", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1", "3")) + tk.MustExec("delete from t3 where id = 1") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "3")) + tk.MustQuery("select * from t3").Check(testkit.Rows("3")) + + // Test in autocommit=0 transaction + tk.MustExec("set autocommit=0") + tk.MustExec("insert into t1 values (1), (2)") + tk.MustExec("insert into t2 values (1), (2)") + tk.MustExec("insert into t3 values (1)") + tk.MustGetDBError("delete from t1 where id = 1", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("delete from t1 where id = 2") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "", "1", "3")) + tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) + tk.MustExec("set autocommit=1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1", "3", "4")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("", "", "", "1", "3")) + tk.MustQuery("select * from t3 order by id").Check(testkit.Rows("1", "3")) + + // Test StmtCommit after fk cascade executor execute finish. + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("create table t0(id int primary key);") + tk.MustExec("create table t1(id int primary key, pid int, index(pid), a int, foreign key(pid) references t1(id) on delete set null, foreign key(a) references t0(id) on delete set null);") + tk.MustExec("insert into t0 values (0), (1)") + tk.MustExec("insert into t1 values (0, 0, 0)") + tk.MustExec("insert into t1 (id, pid) values(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);") + tk.MustExec("update t1 set a=1 where a is null") + tk.MustExec("delete from t0 where id=0;") + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustQuery("select * from t0").Check(testkit.Rows("1")) + tk.MustQuery("select id, pid, a from t1 order by id").Check(testkit.Rows("0 0 ", "1 0 1", "2 1 1", "3 2 1", "4 3 1", "5 4 1", "6 5 1", "7 6 1", "8 7 1", "9 8 1", "10 9 1", "11 10 1", "12 11 1", "13 12 1", "14 13 1")) + + // Test multi-foreign key set null in one table. + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key)") + tk.MustExec("create table t3 (id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete set null, " + + "constraint fk_id2 foreign key (id2) references t2 (id) on delete set null)") + tk.MustExec("insert into t1 values (1), (2), (3)") + tk.MustExec("insert into t2 values (1), (2), (3)") + tk.MustExec("insert into t3 values (1,1), (1, 2), (1, 3), (2, 1), (2, 2)") + tk.MustExec("delete from t1 where id=1") + tk.MustQuery("select * from t1").Check(testkit.Rows("2", "3")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select * from t3 order by id1").Check(testkit.Rows(" 1", " 2", " 3", "2 1", "2 2")) + tk.MustExec("create table t4 (id3 int key, constraint fk_id3 foreign key (id3) references t3 (id2))") + tk.MustExec("insert into t4 values (2)") + tk.MustExec("delete from t1 where id=2") + tk.MustGetDBError("delete from t2 where id = 2", plannercore.ErrRowIsReferenced2) + tk.MustQuery("select * from t1").Check(testkit.Rows("3")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1", "2", "3")) + tk.MustQuery("select * from t3 order by id1, id2").Check(testkit.Rows(" 1", " 1", " 2", " 2", " 3")) + + // Test foreign key set null execution meet lock and do retry. + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@global.tidb_enable_foreign_key=1") + tk2.MustExec("set @@foreign_key_checks=1") + tk2.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3, t4") + tk.MustExec("create table t1 (id int key, name varchar(10), pid int, index(pid), constraint fk foreign key (pid) references t1 (id) on delete set null)") + tk.MustExec("insert into t1 values (1, 'boss', null), (2, 'a', 1), (3, 'b', 1), (4, 'c', '2')") + tk.MustExec("begin pessimistic") + tk.MustExec("insert into t1 values (5, 'd', 3)") + tk2.MustExec("begin pessimistic") + tk2.MustExec("insert into t1 values (6, 'e', 4)") + tk2.MustExec("delete from t1 where id=2") + tk2.MustExec("commit") + tk.MustExec("delete from t1 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3 b ", "4 c ", "5 d 3", "6 e 4")) + + // Test foreign key cascade delete and set null in one row. + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (id int key, name varchar(10), pid int, ppid int, index(pid), index(ppid) , constraint fk_pid foreign key (pid) references t1 (id) on delete cascade, " + + "constraint fk_ppid foreign key (ppid) references t1 (id) on delete set null)") + tk.MustExec("insert into t1 values (1, 'boss', null, null), (2, 'a', 1, 1), (3, 'b', 1, 1), (4, 'c', '2', 1)") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows()) + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (id int key, name varchar(10), pid int, oid int, poid int, index(pid), index (oid), index(poid) , constraint fk_pid foreign key (pid) references t1 (id) on delete cascade, " + + "constraint fk_poid foreign key (poid) references t1 (oid) on delete set null)") + tk.MustExec("insert into t1 values (1, 'boss', null, 0, 0), (2, 'a', 1, 1, 0), (3, 'b', null, 2, 1), (4, 'c', 2, 3, 2)") + tk.MustExec("delete from t1 where id = 1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("3 b 2 ")) + + // Test handle many foreign key value in one cascade. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (id int auto_increment key, b int);") + tk.MustExec("create table t2 (id int, b int, foreign key fk(id) references t1(id) on delete set null)") + tk.MustExec("insert into t1 (b) values (1),(1),(1),(1),(1),(1),(1),(1);") + for i := 0; i < 12; i++ { + tk.MustExec("insert into t1 (b) select b from t1") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) + tk.MustExec("insert into t2 select * from t1") + tk.MustExec("delete from t1") + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("0")) + tk.MustQuery("select count(*) from t2 where id is null").Check(testkit.Rows("32768")) +} + +func TestForeignKeyOnUpdateCascade(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + cases := []struct { + prepareSQLs []string + }{ + // Case-1: test unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int, a int, b int, unique index(a, b));", + "create table t2 (b int, name varchar(10), a int, id int, unique index (a,b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-2: test unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key, a int, b int, unique index(a, b, id));", + "create table t2 (b int, name varchar(10), a int, id int key, unique index (a,b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-3: test non-unique index only contain foreign key columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b));", + "create table t2 (b int, a int, name varchar(10), id int key, index (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-4: test non-unique index contain foreign key columns and other columns. + { + prepareSQLs: []string{ + "create table t1 (id int key,a int, b int, index(a, b, id));", + "create table t2 (name varchar(10), b int, id int key, a int, index (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + } + + for idx, ca := range cases { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24), (5, 15, null), (6, null, 26), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'),(2, 12, 22, 'b'), (3, 13, 23, 'c'), (4, 14, 24, 'd'), (5, 15, null, 'e'), (6, null, 26, 'f'), (7, null, null, 'g');") + tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") + tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 111 221", "2 112 222")) + tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 111 221 a", "2 112 222 b", "3 13 23 c")) + // Test update fk column to null + tk.MustExec("update t1 set a=101, b=null where id = 1 or b = 222") + tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 101 ", "2 101 ")) + tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 101 a", "2 101 b", "3 13 23 c")) + tk.MustExec("update t1 set a=null where b is null") + tk.MustQuery("select id, a, b from t1 where b is null order by id").Check(testkit.Rows("1 ", "2 ", "5 ", "7 ")) + tk.MustQuery("select id, a, b, name from t2 where b is null order by id").Check(testkit.Rows("1 101 a", "2 101 b", "5 15 e", "7 g")) + // Test update fk column from null to not-null value + tk.MustExec("update t1 set a=0, b = 0 where id = 7") + tk.MustQuery("select id, a, b from t1 where a=0 and b=0 order by id").Check(testkit.Rows("7 0 0")) + tk.MustQuery("select id, a, b from t2 where a=0 and b=0 order by id").Check(testkit.Rows()) + + // Test in transaction. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, null), (6, null, 6), (7, null, null);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd'), (5, 5, null, 'e'), (6, null, 6, 'f'), (7, null, null, 'g');") + tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 102 202 b", "3 3 3 c", "4 4 4 d", "5 5 e", "6 6 f", "7 g")) + tk.MustExec("rollback") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") + tk.MustExec("update t1 set a=101 where a = 1") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 1 a", "2 2 2 b")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (3, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 2 2", "3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id, a").Check(testkit.Rows("1 101 1 a", "2 2 2 b", "3 1 1 c")) + tk.MustExec("update t1 set a=null, b=2000 where id in (1, 2)") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 2000", "2 2000", "3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 2000 a", "2 2000 b", "3 1 1 c")) + + // only test in non-unique index + if idx >= 2 { + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("begin") + tk.MustExec("update t1 set a=101 where id = 1") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a')") + tk.MustExec("update t1 set b=102 where id = 2") + tk.MustQuery("select * from t1").Check(testkit.Rows("1 101 1", "2 1 102")) + tk.MustQuery("select id, a, b, name from t2").Check(testkit.Rows("1 1 102 a")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (3, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 1", "2 1 102", "3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1 102 a", "3 1 1 e")) + + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'), (2, 1, 1, 'b')") + tk.MustExec("update t1 set a=101, b=102 where id = 1") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 102", "2 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 102 a", "2 101 102 b")) + } + } + + cases = []struct { + prepareSQLs []string + }{ + // Case-5: test primary key only contain foreign key columns, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, primary key (a, b));", + "create table t2 (b int, a int, name varchar(10), id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-6: test primary key only contain foreign key columns, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, primary key (a, b));", + "create table t2 (name varchar(10), b int, a int, id int, primary key (a, b), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-7: test primary key contain foreign key columns and other column, and disable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=0;", + "create table t1 (id int, a int, b int, primary key (a, b, id));", + "create table t2 (b int, name varchar(10), a int, id int, primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + // Case-8: test primary key contain foreign key columns and other column, and enable tidb_enable_clustered_index. + { + prepareSQLs: []string{ + "set @@tidb_enable_clustered_index=1;", + "create table t1 (id int, a int, b int, primary key (a, b, id));", + "create table t2 (b int, a int, id int, name varchar(10), primary key (a, b, id), foreign key fk(a, b) references t1(a, b) ON UPDATE CASCADE);", + }, + }, + } + for idx, ca := range cases { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 11, 21, 'a'),(2, 12, 22, 'b'), (3, 13, 23, 'c'), (4, 14, 24, 'd')") + tk.MustExec("update t1 set a=a+100, b = b+200 where id in (1, 2)") + tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 111 221", "2 112 222")) + tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 111 221 a", "2 112 222 b", "3 13 23 c")) + tk.MustExec("update t1 set a=101 where id = 1 or b = 222") + tk.MustQuery("select id, a, b from t1 where id in (1,2) order by id").Check(testkit.Rows("1 101 221", "2 101 222")) + tk.MustQuery("select id, a, b, name from t2 where id in (1,2,3) order by id").Check(testkit.Rows("1 101 221 a", "2 101 222 b", "3 13 23 c")) + + if idx < 2 { + tk.MustGetDBError("update t1 set b=200 where id in (1,2);", kv.ErrKeyExists) + } + + // test in transaction. + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2), (3, 3, 3), (4, 4, 4);") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b'), (3, 3, 3, 'c'), (4, 4, 4, 'd');") + tk.MustExec("update t1 set a=a+100, b=b+200 where id = 1 or a = 2") + tk.MustExec("update t1 set a=a+1000, b=b+2000 where a in (2,3,4) or b in (5,6,7) or id=2") + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("1 101 201", "2 1102 2202", "3 1003 2003", "4 1004 2004")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 1102 2202 b", "3 1003 2003 c", "4 1004 2004 d")) + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows("1 101 201", "2 1102 2202", "3 1003 2003", "4 1004 2004")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 1102 2202 b", "3 1003 2003 c", "4 1004 2004 d")) + + tk.MustExec("delete from t2") + tk.MustExec("delete from t1") + tk.MustExec("insert into t1 values (1, 1, 1),(2, 2, 2);") + tk.MustExec("begin") + tk.MustExec("insert into t2 (id, a, b, name) values (1, 1, 1, 'a'),(2, 2, 2, 'b')") + tk.MustExec("update t1 set a=a+100, b=b+200 where id = 1") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 201", "2 2 2")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 2 2 b")) + err := tk.ExecToErr("insert into t2 (id, a, b, name) values (3, 1, 1, 'e')") + require.Error(t, err) + require.True(t, plannercore.ErrNoReferencedRow2.Equal(err), err.Error()) + tk.MustExec("insert into t1 values (3, 1, 1);") + tk.MustExec("insert into t2 (id, a, b, name) values (3, 1, 1, 'c')") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 101 201", "2 2 2", "3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 101 201 a", "2 2 2 b", "3 1 1 c")) + tk.MustExec("update t1 set a=a+1000, b=b+2000 where a>1") + tk.MustExec("commit") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1101 2201", "2 1002 2002", "3 1 1")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("1 1101 2201 a", "2 1002 2002 b", "3 1 1 c")) + } + + // Case-9: test primary key is handle and contain foreign key column. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int, a int, b int, primary key (id));") + tk.MustExec("create table t2 (b int, a int, id int, name varchar(10), primary key (a), foreign key fk(a) references t1(id) ON UPDATE CASCADE);") + tk.MustExec("insert into t1 (id, a, b) values (1, 11, 21),(2, 12, 22), (3, 13, 23), (4, 14, 24)") + tk.MustExec("insert into t2 (id, a, b, name) values (11, 1, 21, 'a'),(12, 2, 22, 'b'), (13, 3, 23, 'c'), (14, 4, 24, 'd')") + tk.MustExec("update t1 set id = id + 100 where id in (1, 2, 3)") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("4 14 24", "101 11 21", "102 12 22", "103 13 23")) + tk.MustQuery("select id, a, b, name from t2 order by id").Check(testkit.Rows("11 101 21 a", "12 102 22 b", "13 103 23 c", "14 4 24 d")) +} + +func TestForeignKeyOnUpdateCascade2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + // Test update same old row in parent, but only the first old row do cascade update + tk.MustExec("create table t1 (id int key, a int, index (a));") + tk.MustExec("create table t2 (id int key, pid int, constraint fk_pid foreign key (pid) references t1(a) ON UPDATE CASCADE);") + tk.MustExec("insert into t1 (id, a) values (1,1), (2, 1)") + tk.MustExec("insert into t2 (id, pid) values (1,1), (2, 1)") + tk.MustExec("update t1 set a=id+1") + tk.MustQuery("select id, a from t1 order by id").Check(testkit.Rows("1 2", "2 3")) + tk.MustQuery("select id, pid from t2 order by id").Check(testkit.Rows("1 2", "2 2")) + + // Test cascade delete in self table. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (id int key, name varchar(10), leader int, index(leader), foreign key (leader) references t1(id) ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("update t1 set id=id+10000 where id=11") + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("1 boss ", "10 l1_a 1", "12 l1_c 1", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 1")) + tk.MustExec("update t1 set id=0 where id=1") + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("0 boss ", "10 l1_a 0", "12 l1_c 0", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 0")) + + // Test explain analyze with foreign key cascade. + tk.MustExec("explain analyze update t1 set id=1 where id=10") + tk.MustQuery("select id, name, leader from t1 order by id").Check(testkit.Rows("0 boss ", "1 l1_a 0", "12 l1_c 0", "100 l2_a1 1", "110 l2_b1 10011", "1000 l3_a1 100", "10011 l1_b 0")) + + // Test cascade delete in self table with string type foreign key. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (id varchar(100) key, name varchar(10), leader varchar(100), index(leader), foreign key (leader) references t1(id) ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1, 'boss', null), (10, 'l1_a', 1), (11, 'l1_b', 1), (12, 'l1_c', 1)") + tk.MustExec("insert into t1 values (100, 'l2_a1', 10)") + tk.MustExec("insert into t1 values (110, 'l2_b1', 11)") + tk.MustExec("insert into t1 values (1000,'l3_a1', 100)") + tk.MustExec("update t1 set id=id+10000 where id=11") + tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("1 boss ", "10 l1_a 1", "10011 l1_b 1", "12 l1_c 1", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100")) + tk.MustExec("update t1 set id=0 where id=1") + tk.MustQuery("select id, name, leader from t1 order by name").Check(testkit.Rows("0 boss ", "10 l1_a 0", "10011 l1_b 0", "12 l1_c 0", "100 l2_a1 10", "110 l2_b1 10011", "1000 l3_a1 100")) + + // Test cascade delete depth error. + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t0 (id int, unique index(id))") + tk.MustExec("insert into t0 values (1)") + for i := 1; i < 17; i++ { + tk.MustExec(fmt.Sprintf("create table t%v (id int, unique index(id), foreign key (id) references t%v(id) on update cascade)", i, i-1)) + tk.MustExec(fmt.Sprintf("insert into t%v values (1)", i)) + } + tk.MustGetDBError("update t0 set id=10 where id=1;", exeerrors.ErrForeignKeyCascadeDepthExceeded) + tk.MustQuery("select id from t0").Check(testkit.Rows("1")) + tk.MustQuery("select id from t15").Check(testkit.Rows("1")) + tk.MustExec("drop table if exists t16") + tk.MustExec("update t0 set id=10 where id=1;") + tk.MustQuery("select id from t0").Check(testkit.Rows("10")) + tk.MustQuery("select id from t15").Check(testkit.Rows("10")) + for i := 16; i > -1; i-- { + tk.MustExec("drop table if exists t" + strconv.Itoa(i)) + } + + // Test handle many foreign key value in one cascade. + tk.MustExec("create table t1 (id int auto_increment key, b int, index(b));") + tk.MustExec("create table t2 (id int, b int, foreign key fk(b) references t1(b) on update cascade)") + tk.MustExec("insert into t1 (b) values (1),(2),(3),(4),(5),(6),(7),(8);") + for i := 0; i < 12; i++ { + tk.MustExec("insert into t1 (b) select id from t1") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("32768")) + tk.MustExec("insert into t2 select * from t1") + tk.MustExec("update t1 set b=2") + tk.MustQuery("select count(*) from t1 join t2 where t1.id=t2.id and t1.b=t2.b").Check(testkit.Rows("32768")) +} + +func TestDMLExplainAnalyzeFKInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + + // Test for Insert ignore foreign check runtime stats. + tk.MustExec("drop table if exists t1,t2,t3") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key)") + tk.MustExec("create table t3 (id int key, id1 int, id2 int, constraint fk_id1 foreign key (id1) references t1 (id) on delete cascade, " + + "constraint fk_id2 foreign key (id2) references t2 (id) on delete cascade)") + tk.MustExec("insert into t1 values (1), (2)") + tk.MustExec("insert into t2 values (1)") + res := tk.MustQuery("explain analyze insert ignore into t3 values (1, 1, 1), (2, 1, 1), (3, 2, 1), (4, 1, 1), (5, 2, 1), (6, 2, 1)") + explain := getExplainResult(res) + require.Regexpf(t, "time:.* loops:.* prepare:.* check_insert: {total_time:.* mem_insert_time:.* prefetch:.* fk_check:.*", explain, "") + res = tk.MustQuery("explain analyze insert ignore into t3 values (7, null, null), (8, null, null)") + explain = getExplainResult(res) + require.Regexpf(t, "time:.* loops:.* prepare:.* check_insert: {total_time:.* mem_insert_time:.* prefetch:.* fk_check:.*", explain, "") +} + +func getExplainResult(res *testkit.Result) string { + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + _, _ = fmt.Fprintf(resBuff, "%s\t", row) + } + return resBuff.String() +} + +func TestForeignKeyOnInsertOnDuplicateUpdate(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key, name varchar(10));") + tk.MustExec("create table t2 (id int key, pid int, foreign key fk(pid) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);") + tk.MustExec("insert into t1 values (1, 'a'), (2, 'b')") + tk.MustExec("insert into t2 values (1, 1), (2, 2), (3, 1), (4, 2), (5, null)") + tk.MustExec("insert into t1 values (1, 'aa') on duplicate key update name = 'aa'") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("1 aa", "2 b")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 1", "2 2", "3 1", "4 2", "5 ")) + tk.MustExec("insert into t1 values (1, 'aaa') on duplicate key update id = 10") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("2 b", "10 aa")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 2", "3 10", "4 2", "5 ")) + // Test in transaction. + tk.MustExec("begin") + tk.MustExec("insert into t1 values (3, 'c')") + tk.MustExec("insert into t2 values (6, 3)") + tk.MustExec("insert into t1 values (2, 'bb'), (3, 'cc') on duplicate key update id =id*10") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("10 aa", "20 b", "30 c")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 20", "3 10", "4 20", "5 ", "6 30")) + tk.MustExec("commit") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("10 aa", "20 b", "30 c")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("1 10", "2 20", "3 10", "4 20", "5 ", "6 30")) + tk.MustExec("delete from t1") + tk.MustQuery("select * from t2").Check(testkit.Rows("5 ")) + // Test for cascade update failed. + tk.MustExec("drop table t1, t2") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key, foreign key (id) references t1 (id) on update cascade)") + tk.MustExec("create table t3 (id int key, foreign key (id) references t2(id))") + tk.MustExec("begin") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("insert into t3 values (1)") + tk.MustGetDBError("insert into t1 values (1) on duplicate key update id = 2", plannercore.ErrRowIsReferenced2) + require.Equal(t, 0, len(tk.Session().GetSessionVars().TxnCtx.Savepoints)) + tk.MustExec("commit") + tk.MustQuery("select * from t1").Check(testkit.Rows("1")) + tk.MustQuery("select * from t2").Check(testkit.Rows("1")) + tk.MustQuery("select * from t3").Check(testkit.Rows("1")) +} + +func TestExplainAnalyzeDMLWithFKInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (id int key, foreign key fk(id) references t1(id) ON UPDATE CASCADE ON DELETE CASCADE);") + tk.MustExec("create table t3 (id int, unique index idx(id));") + tk.MustExec("create table t4 (id int, index idx_id(id),foreign key fk(id) references t3(id));") + tk.MustExec("create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));") + tk.MustExec("create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), " + + "foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE SET NULL, " + + "foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, " + + "foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);") + tk.MustExec("create table t7(id int primary key, pid int, index(pid), foreign key(pid) references t7(id) on delete cascade);") + + cases := []struct { + prepare []string + sql string + plan string + }{ + // Test foreign key use primary key. + { + prepare: []string{ + "insert into t1 values (1),(2),(3),(4),(5)", + }, + sql: "explain analyze insert into t2 values (1),(2),(3);", + plan: "Insert_. N/A 0 root time:.*, loops:1, prepare:.*, insert:.*" + + "└─Foreign_Key_Check_. 0.00 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", + }, + { + sql: "explain analyze insert ignore into t2 values (10),(11),(12);", + plan: "Insert_.* fk_check.*" + + "└─Foreign_Key_Check_.* 0 root table:t1 total:0s, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", + }, + { + sql: "explain analyze update t2 set id=id+2 where id >1", + plan: "Update_.* 0 root time:.*, loops:1.*" + + "├─TableReader_.*" + + "│ └─TableRangeScan.*" + + "└─Foreign_Key_Check_.* 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:2 foreign_key:fk, check_exist N/A N/A", + }, + { + sql: "explain analyze delete from t1 where id>1", + plan: "Delete_.*" + + "├─TableReader_.*" + + "│ └─TableRangeScan_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:4 foreign_key:fk, on_delete:CASCADE N/A N/A.*" + + " └─Delete_.*" + + " └─Batch_Point_Get_.*", + }, + { + sql: "explain analyze update t1 set id=id+1 where id = 1", + plan: "Update_.*" + + "├─Point_Get_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:1 foreign_key:fk, on_update:CASCADE N/A N/A.*" + + " └─Update_.*" + + " ├─Point_Get_.*" + + " └─Foreign_Key_Check_.*", + }, + { + sql: "explain analyze insert into t1 values (1) on duplicate key update id = 100", + plan: "Insert_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t2 total:0s foreign_key:fk, on_update:CASCADE N/A N/A", + }, + { + sql: "explain analyze insert into t1 values (2) on duplicate key update id = 100", + plan: "Insert_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t2 total:.*, foreign_keys:1 foreign_key:fk, on_update:CASCADE N/A N/A.*" + + " └─Update_.*" + + " ├─Point_Get_.*" + + " └─Foreign_Key_Check_.* 0 root table:t1 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk, check_exist N/A N/A", + }, + // Test foreign key use index. + { + prepare: []string{ + "insert into t3 values (1),(2),(3),(4),(5)", + }, + sql: "explain analyze insert into t4 values (1),(2),(3);", + plan: "Insert_.*" + + "└─Foreign_Key_Check_.* 0 root table:t3, index:idx total:.*, check:.*, lock:.*, foreign_keys:3 foreign_key:fk, check_exist N/A N/A", + }, + { + sql: "explain analyze update t4 set id=id+2 where id >1", + plan: "Update_.*" + + "├─IndexReader_.*" + + "│ └─IndexRangeScan_.*" + + "└─Foreign_Key_Check_.* 0 root table:t3, index:idx total:.*, check:.*, lock:.*, foreign_keys:2 foreign_key:fk, check_exist N/A N/A", + }, + { + sql: "explain analyze delete from t3 where id in (2,3)", + plan: "Delete_.*" + + "├─Batch_Point_Get_.*" + + "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:2 foreign_key:fk, check_not_exist N/A N/A", + }, + { + prepare: []string{ + "insert into t3 values (2)", + }, + sql: "explain analyze update t3 set id=id+1 where id = 2", + plan: "Update_.*" + + "├─Point_Get_.*" + + "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:1 foreign_key:fk, check_not_exist N/A N/A", + }, + + { + sql: "explain analyze insert into t3 values (2) on duplicate key update id = 100", + plan: "Insert_.*" + + "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:0s foreign_key:fk, check_not_exist N/A N/A", + }, + { + sql: "explain analyze insert into t3 values (3) on duplicate key update id = 100", + plan: "Insert_.*" + + "└─Foreign_Key_Check_.* 0 root table:t4, index:idx_id total:.*, check:.*, foreign_keys:1 foreign_key:fk, check_not_exist N/A N/A", + }, + // Test multi-foreign keys in on table. + { + prepare: []string{ + "insert into t5 values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5)", + }, + sql: "explain analyze insert into t6 values (1,1,1)", + plan: "Insert_.*" + + "├─Foreign_Key_Check_.* 0 root table:t5 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + + "├─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_2, check_exist N/A N/A.*" + + "└─Foreign_Key_Check_.* 0 root table:t5, index:idx3 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", + }, + { + sql: "explain analyze insert ignore into t6 values (1,1,10)", + plan: "Insert_.* root time:.* loops:.* prepare:.* check_insert.* fk_check:.*" + + "├─Foreign_Key_Check.* 0 root table:t5 total:0s, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + + "├─Foreign_Key_Check.* 0 root table:t5, index:idx2 total:0s, foreign_keys:1 foreign_key:fk_2, check_exist N/A N/A.*" + + "└─Foreign_Key_Check.* 0 root table:t5, index:idx3 total:0s, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", + }, + { + sql: "explain analyze update t6 set id=id+1, id3=id2+1 where id = 1", + plan: "Update_.*" + + "├─IndexLookUp_.*" + + "│ ├─IndexRangeScan_.*" + + "│ └─TableRowIDScan_.*" + + "├─Foreign_Key_Check_.* 0 root table:t5 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_1, check_exist N/A N/A.*" + + "└─Foreign_Key_Check_.* 0 root table:t5, index:idx3 total:.*, check:.*, lock:.*, foreign_keys:1 foreign_key:fk_3, check_exist N/A N/A", + }, + { + sql: "explain analyze delete from t5 where id in (4,5)", + plan: "Delete_.*" + + "├─Batch_Point_Get_.*" + + "├─Foreign_Key_Check_.* 0 root table:t6, index:idx_id2 total:.*, check:.*, foreign_keys:2 foreign_key:fk_2, check_not_exist N/A N/A.*" + + "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:2 foreign_key:fk_1, on_delete:SET NULL N/A N/A.*" + + "│ └─Update_.*" + + "│ │ ├─IndexRangeScan_.*" + + "│ │ └─TableRowIDScan_.*" + + "│ └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t6, index:fk_3 total:.*, foreign_keys:2 foreign_key:fk_3, on_delete:CASCADE N/A N/A.*" + + " └─Delete_.*" + + " └─IndexLookUp_.*" + + " ├─IndexRangeScan_.*" + + " └─TableRowIDScan_.*", + }, + { + sql: "explain analyze update t5 set id=id+1, id2=id2+1 where id = 3", + plan: "Update_.*" + + "├─Point_Get_.*" + + "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + + "│ └─Update_.*" + + "│ ├─IndexLookUp_.*" + + "│ │ ├─IndexRangeScan_.*" + + "│ │ └─TableRowIDScan_.*" + + "│ └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id2 total:.*, foreign_keys:1 foreign_key:fk_2, on_update:CASCADE N/A N/A.*" + + " └─Update_.*" + + " ├─IndexLookUp_.*" + + " │ ├─IndexRangeScan_.*" + + " │ └─TableRowIDScan_.*" + + " └─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:0s foreign_key:fk_2, check_exist N/A N/A", + }, + { + prepare: []string{ + "insert into t5 values (10,10,10)", + }, + sql: "explain analyze update t5 set id=id+1, id2=id2+1, id3=id3+1 where id = 10", + plan: "Update_.*" + + "├─Point_Get_.*" + + "├─Foreign_Key_Check_.* 0 root table:t6, index:fk_3 total:.*, check:.*, foreign_keys:1 foreign_key:.*, check_not_exist N/A N/A.*" + + "├─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + + "│ └─Update_.*" + + "│ ├─IndexLookUp_.*" + + "│ │ ├─IndexRangeScan_.*" + + "│ │ └─TableRowIDScan_.*" + + "│ └─Foreign_Key_Check_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id2 total:.*, foreign_keys:1 foreign_key:fk_2, on_update:CASCADE N/A N/A.*" + + " └─Update_.*" + + " ├─IndexLookUp_.*" + + " │ ├─IndexRangeScan_.*" + + " │ └─TableRowIDScan_.*" + + " └─Foreign_Key_Check_.* 0 root table:t5, index:idx2 total:0s foreign_key:fk_2, check_exist N/A N/A", + }, + { + sql: "explain analyze insert into t5 values (1,1,1) on duplicate key update id = 100, id3=100", + plan: "Insert_.*" + + "├─Foreign_Key_Check_.* 0 root table:t6, index:fk_3 total:.*, check:.*, foreign_keys:1 foreign_key:fk_3, check_not_exist N/A N/A.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t6, index:idx_id total:.*, foreign_keys:1 foreign_key:fk_1, on_update:CASCADE N/A N/A.*" + + " └─Update_.*" + + " ├─IndexLookUp_.*" + + " │ ├─IndexRangeScan_.*" + + " │ └─TableRowIDScan_.*" + + " └─Foreign_Key_Check_.* 0 root table:t5 total:0s foreign_key:fk_1, check_exist N/A N/A", + }, + { + prepare: []string{ + "insert into t7 values(0,0),(1,0),(2,1),(3,2),(4,3),(5,4),(6,5),(7,6),(8,7),(9,8),(10,9),(11,10),(12,11),(13,12),(14,13);", + }, + sql: "explain analyze delete from t7 where id = 0;", + plan: "Delete_.*" + + "├─Point_Get_.*" + + "└─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.* foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.* foreign_keys:2 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:.*, foreign_keys:1 foreign_key:fk_1, on_delete:CASCADE.*" + + " └─Delete_.*" + + " ├─UnionScan_.*" + + " │ └─IndexReader_.*" + + " │ └─IndexRangeScan_.*" + + " └─Foreign_Key_Cascade_.* 0 root table:t7, index:pid total:0s foreign_key:fk_1, on_delete:CASCADE.*", + }, + } + for _, ca := range cases { + for _, sql := range ca.prepare { + tk.MustExec(sql) + } + res := tk.MustQuery(ca.sql) + explain := getExplainResult(res) + require.Regexp(t, ca.plan, explain) + } +} + +func TestForeignKeyRuntimeStats(t *testing.T) { + checkStats := executor.FKCheckRuntimeStats{ + Total: time.Second * 3, + Check: time.Second * 2, + Lock: time.Second, + Keys: 10, + } + require.Equal(t, "total:3s, check:2s, lock:1s, foreign_keys:10", checkStats.String()) + checkStats.Merge(checkStats.Clone()) + require.Equal(t, "total:6s, check:4s, lock:2s, foreign_keys:20", checkStats.String()) + cascadeStats := executor.FKCascadeRuntimeStats{ + Total: time.Second, + Keys: 10, + } + require.Equal(t, "total:1s, foreign_keys:10", cascadeStats.String()) + cascadeStats.Merge(cascadeStats.Clone()) + require.Equal(t, "total:2s, foreign_keys:20", cascadeStats.String()) +} + +func TestPrivilegeCheckInForeignKeyCascade(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (id int key, foreign key fk (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1), (2), (3);") + cases := []struct { + prepares []string + sql string + err error + t1Rows []string + t2Rows []string + }{ + { + prepares: []string{"grant insert on test.t2 to 'u1'@'%';"}, + sql: "insert into t2 values (1), (2), (3);", + t1Rows: []string{"1", "2", "3"}, + t2Rows: []string{"1", "2", "3"}, + }, + { + prepares: []string{"grant select, delete on test.t1 to 'u1'@'%';"}, + sql: "delete from t1 where id=1;", + t1Rows: []string{"2", "3"}, + t2Rows: []string{"2", "3"}, + }, + { + prepares: []string{"grant select, update on test.t1 to 'u1'@'%';"}, + sql: "update t1 set id=id+10 where id=2;", + t1Rows: []string{"3", "12"}, + t2Rows: []string{"3", "12"}, + }, + } + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("set @@foreign_key_checks=1") + for _, ca := range cases { + tk.MustExec("drop user if exists 'u1'@'%'") + tk.MustExec("create user 'u1'@'%' identified by '';") + for _, sql := range ca.prepares { + tk.MustExec(sql) + } + err := tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + require.NoError(t, err) + if ca.err == nil { + tk2.MustExec(ca.sql) + } else { + err = tk2.ExecToErr(ca.sql) + require.Error(t, err) + } + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows(ca.t1Rows...)) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows(ca.t2Rows...)) + } +} + +func TestTableLockInForeignKeyCascade(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("set @@foreign_key_checks=1") + // enable table lock + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableTableLock = true + }) + defer func() { + config.UpdateGlobal(func(conf *config.Config) { + conf.EnableTableLock = false + }) + }() + tk.MustExec("create table t1 (id int key);") + tk.MustExec("create table t2 (id int key, foreign key fk (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE);") + tk.MustExec("insert into t1 values (1), (2), (3);") + tk.MustExec("insert into t2 values (1), (2), (3);") + tk.MustExec("lock table t2 read;") + tk2.MustGetDBError("delete from t1 where id = 1", infoschema.ErrTableLocked) + tk.MustExec("unlock tables;") + tk2.MustExec("delete from t1 where id = 1") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows("2", "3")) + tk.MustQuery("select * from t2 order by id").Check(testkit.Rows("2", "3")) +} + +func TestForeignKeyIssue39732(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_stmt_summary=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create user 'u1'@'%' identified by '';") + tk.MustExec("GRANT ALL PRIVILEGES ON *.* TO 'u1'@'%'") + err := tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost", CurrentUser: true, AuthUsername: "u1", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + require.NoError(t, err) + tk.MustExec("create table t1 (id int key, leader int, index(leader), foreign key (leader) references t1(id) ON DELETE CASCADE);") + tk.MustExec("insert into t1 values (1, null), (10, 1), (11, 1), (20, 10)") + tk.MustExec(`prepare stmt1 from 'delete from t1 where id = ?';`) + tk.MustExec(`set @a = 1;`) + tk.MustExec("execute stmt1 using @a;") + tk.MustQuery("select * from t1 order by id").Check(testkit.Rows()) +} + +func TestForeignKeyOnReplaceIntoChildTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t_data (id int, a int, b int)") + tk.MustExec("insert into t_data (id, a, b) values (1, 1, 1), (2, 2, 2);") + for _, ca := range foreignKeyTestCase1 { + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + for _, sql := range ca.prepareSQLs { + tk.MustExec(sql) + } + tk.MustExec("replace into t1 (id, a, b) values (1, 1, 1);") + tk.MustExec("replace into t2 (id, a, b) values (1, 1, 1)") + tk.MustGetDBError("replace into t1 (id, a, b) values (1, 2, 3);", plannercore.ErrRowIsReferenced2) + if !ca.notNull { + tk.MustExec("replace into t2 (id, a, b) values (2, null, 1)") + tk.MustExec("replace into t2 (id, a, b) values (3, 1, null)") + tk.MustExec("replace into t2 (id, a, b) values (4, null, null)") + } + tk.MustGetDBError("replace into t2 (id, a, b) values (5, 1, 0);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("replace into t2 (id, a, b) values (6, 0, 1);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("replace into t2 (id, a, b) values (7, 2, 2);", plannercore.ErrNoReferencedRow2) + // Test replace into from select. + tk.MustExec("delete from t2") + tk.MustExec("replace into t2 (id, a, b) select id, a, b from t_data where t_data.id=1") + tk.MustGetDBError("replace into t2 (id, a, b) select id, a, b from t_data where t_data.id=2", plannercore.ErrNoReferencedRow2) + + // Test in txn + tk.MustExec("delete from t2") + tk.MustExec("begin") + tk.MustExec("delete from t1 where a=1") + tk.MustGetDBError("replace into t2 (id, a, b) values (1, 1, 1)", plannercore.ErrNoReferencedRow2) + tk.MustExec("replace into t1 (id, a, b) values (2, 2, 2)") + tk.MustExec("replace into t2 (id, a, b) values (2, 2, 2)") + tk.MustGetDBError("replace into t1 (id, a, b) values (2, 2, 3);", plannercore.ErrRowIsReferenced2) + tk.MustExec("rollback") + tk.MustQuery("select id, a, b from t1 order by id").Check(testkit.Rows("1 1 1")) + tk.MustQuery("select id, a, b from t2 order by id").Check(testkit.Rows()) + } + + // Case-10: test primary key is handle and contain foreign key column, and foreign key column has default value. + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("set @@tidb_enable_clustered_index=0;") + tk.MustExec("create table t1 (id int,a int, primary key(id));") + tk.MustExec("create table t2 (id int key,a int not null default 0, index (a), foreign key fk(a) references t1(id));") + tk.MustExec("replace into t1 values (1, 1);") + tk.MustExec("replace into t2 values (1, 1);") + tk.MustGetDBError("replace into t2 (id) values (10);", plannercore.ErrNoReferencedRow2) + tk.MustGetDBError("replace into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) + + // Case-11: test primary key is handle and contain foreign key column, and foreign key column doesn't have default value. + tk.MustExec("drop table if exists t2;") + tk.MustExec("create table t2 (id int key,a int, index (a), foreign key fk(a) references t1(id));") + tk.MustExec("replace into t2 values (1, 1);") + tk.MustExec("replace into t2 (id) values (10);") + tk.MustGetDBError("replace into t2 values (3, 2);", plannercore.ErrNoReferencedRow2) +} + +func TestForeignKeyLargeTxnErr(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") + tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz1234567890');") + for i := 0; i < 8; i++ { + tk.MustExec("insert into t1 (name) select name from t1;") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) + tk.MustExec("update t1 set pid=1 where id>1") + tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") + originLimit := kv.TxnTotalSizeLimit.Load() + defer func() { + kv.TxnTotalSizeLimit.Store(originLimit) + }() + // Set the limitation to a small value, make it easier to reach the limitation. + kv.TxnTotalSizeLimit.Store(10240) + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + // foreign key cascade behaviour will cause ErrTxnTooLarge. + tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + tk.MustGetDBError("update t1 set id=id+100000 where id=1", kv.ErrTxnTooLarge) + tk.MustQuery("select id,pid from t1 where id<3 order by id").Check(testkit.Rows("1 ", "2 1")) + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("update t1 set id=id+100000 where id=1") + tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) +} + +func TestForeignKeyAndLockView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (id int key)") + tk.MustExec("create table t2 (id int key, foreign key (id) references t1(id) ON DELETE CASCADE ON UPDATE CASCADE)") + tk.MustExec("insert into t1 values (1)") + tk.MustExec("insert into t2 values (1)") + tk.MustExec("begin pessimistic") + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec("update t2 set id=2") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("set @@foreign_key_checks=1") + tk2.MustExec("use test") + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + tk2.MustExec("begin pessimistic") + tk2.MustExec("update t1 set id=2 where id=1") + tk2.MustExec("commit") + }() + time.Sleep(time.Millisecond * 200) + _, digest := parser.NormalizeDigest("update t1 set id=2 where id=1") + tk.MustQuery("select CURRENT_SQL_DIGEST from information_schema.tidb_trx where state='LockWaiting' and db='test'").Check(testkit.Rows(digest.String())) + tk.MustGetErrMsg("update t1 set id=2", "[executor:1213]Deadlock found when trying to get lock; try restarting transaction") + wg.Wait() +} + +func TestForeignKeyAndMemoryTracker(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("use test") + tk.MustExec("create table t1 (id int auto_increment key, pid int, name varchar(200), index(pid));") + tk.MustExec("insert into t1 (name) values ('abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz');") + for i := 0; i < 8; i++ { + tk.MustExec("insert into t1 (name) select name from t1;") + } + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("256")) + tk.MustExec("update t1 set pid=1 where id>1") + tk.MustExec("alter table t1 add foreign key (pid) references t1 (id) on update cascade") + tk.MustQuery("select sum(id) from t1").Check(testkit.Rows("32896")) + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") + tk.MustExec("set @@tidb_mem_quota_query=40960;") + // foreign key cascade behaviour will exceed memory quota. + err := tk.ExecToErr("update t1 set id=id+100000 where id=1") + require.Error(t, err) + require.Contains(t, err.Error(), memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) + tk.MustQuery("select id,pid from t1 where id = 1").Check(testkit.Rows("1 ")) + tk.MustExec("set @@foreign_key_checks=0") + // After disable foreign_key_checks, following DML will execute successful. + tk.MustExec("update t1 set id=id+100000 where id=1") + tk.MustQuery("select id,pid from t1 where id<3 or pid is null order by id").Check(testkit.Rows("2 1", "100001 ")) +} diff --git a/pkg/executor/test/fktest/main_test.go b/pkg/executor/test/fktest/main_test.go new file mode 100644 index 0000000000000..5dd68c2adf893 --- /dev/null +++ b/pkg/executor/test/fktest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package fk_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/indexmergereadtest/BUILD.bazel b/pkg/executor/test/indexmergereadtest/BUILD.bazel new file mode 100644 index 0000000000000..8aaee06a26f1c --- /dev/null +++ b/pkg/executor/test/indexmergereadtest/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "indexmergereadtest_test", + timeout = "short", + srcs = [ + "index_merge_reader_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 36, + deps = [ + "//pkg/config", + "//pkg/executor", + "//pkg/meta/autoid", + "//pkg/session", + "//pkg/testkit", + "//pkg/testkit/testutil", + "//pkg/util", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/indexmergereadtest/index_merge_reader_test.go b/pkg/executor/test/indexmergereadtest/index_merge_reader_test.go similarity index 93% rename from executor/test/indexmergereadtest/index_merge_reader_test.go rename to pkg/executor/test/indexmergereadtest/index_merge_reader_test.go index 53930ab8a7d4d..92ef516dd58df 100644 --- a/executor/test/indexmergereadtest/index_merge_reader_test.go +++ b/pkg/executor/test/indexmergereadtest/index_merge_reader_test.go @@ -28,12 +28,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) @@ -69,9 +69,9 @@ func TestIndexMergePickAndExecTaskPanic(t *testing.T) { tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") tk.MustQuery("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by id").Check(testkit.Rows("1 1 1 1 1", "5 5 5 5 5")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergePickAndExecTaskPanic", "panic(\"pickAndExecTaskPanic\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergePickAndExecTaskPanic", "panic(\"pickAndExecTaskPanic\")")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergePickAndExecTaskPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergePickAndExecTaskPanic")) }() err := tk.QueryToErr("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by id") require.Contains(t, err.Error(), "pickAndExecTaskPanic") @@ -629,37 +629,37 @@ func TestIndexMergeIntersectionConcurrency(t *testing.T) { // Default is tidb_executor_concurrency. res = tk.MustQuery("select @@tidb_executor_concurrency;").Sort().Rows() defExecCon := res[0][0].(string) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", fmt.Sprintf("return(%s)", defExecCon))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", fmt.Sprintf("return(%s)", defExecCon))) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency")) }() tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) tk.MustExec("set tidb_executor_concurrency = 10") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(10)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(10)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) // workerCnt = min(part_num, concurrency) tk.MustExec("set tidb_executor_concurrency = 20") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(10)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(10)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) tk.MustExec("set tidb_executor_concurrency = 2") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(2)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(2)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) tk.MustExec("set tidb_index_merge_intersection_concurrency = 9") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(9)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(9)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) tk.MustExec("set tidb_index_merge_intersection_concurrency = 21") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(10)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(10)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) tk.MustExec("set tidb_index_merge_intersection_concurrency = 3") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(3)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(3)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) // Concurrency only works for dynamic pruning partition table, so real concurrency is 1. tk.MustExec("set tidb_partition_prune_mode = 'static'") tk.MustExec("set tidb_index_merge_intersection_concurrency = 9") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(1)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) // Concurrency only works for dynamic pruning partition table. so real concurrency is 1. @@ -667,7 +667,7 @@ func TestIndexMergeIntersectionConcurrency(t *testing.T) { tk.MustExec("create table t1(c1 int, c2 bigint, c3 bigint, primary key(c1), key(c2), key(c3));") tk.MustExec("insert into t1 values(1, 1, 3000), (2, 1, 1)") tk.MustExec("set tidb_index_merge_intersection_concurrency = 9") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionConcurrency", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionConcurrency", "return(1)")) tk.MustQuery("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024").Check(testkit.Rows("1")) } @@ -771,10 +771,10 @@ func TestIntersectionWorkerPanic(t *testing.T) { require.Contains(t, res[1][0], "IndexMerge") // Test panic in intersection. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionWorkerPanic", `panic("testIndexMergeIntersectionWorkerPanic")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionWorkerPanic", `panic("testIndexMergeIntersectionWorkerPanic")`)) err := tk.QueryToErr("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c2 < 1024 and c3 > 1024") require.Contains(t, err.Error(), "testIndexMergeIntersectionWorkerPanic") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeIntersectionWorkerPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeIntersectionWorkerPanic")) } func TestIntersectionMemQuota(t *testing.T) { @@ -826,24 +826,24 @@ func TestIndexMergeProcessWorkerHang(t *testing.T) { res := tk.MustQuery("explain " + sql).Rows() require.Contains(t, res[1][0], "IndexMerge") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeMainReturnEarly", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeProcessWorkerUnionHang", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeMainReturnEarly", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeProcessWorkerUnionHang", "return(true)")) err = tk.QueryToErr(sql) require.Contains(t, err.Error(), "testIndexMergeMainReturnEarly") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeMainReturnEarly")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeProcessWorkerUnionHang")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeMainReturnEarly")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeProcessWorkerUnionHang")) sql = "select /*+ use_index_merge(t1, c2, c3) */ c1 from t1 where c2 < 900 and c3 < 1000;" res = tk.MustQuery("explain " + sql).Rows() require.Contains(t, res[1][0], "IndexMerge") require.Contains(t, res[1][4], "intersection") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeMainReturnEarly", "return()")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeProcessWorkerIntersectionHang", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeMainReturnEarly", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeProcessWorkerIntersectionHang", "return(true)")) err = tk.QueryToErr(sql) require.Contains(t, err.Error(), "testIndexMergeMainReturnEarly") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeMainReturnEarly")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeProcessWorkerIntersectionHang")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeMainReturnEarly")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeProcessWorkerIntersectionHang")) } func TestIndexMergePanic1(t *testing.T) { @@ -855,9 +855,9 @@ func TestIndexMergePanic1(t *testing.T) { tk.MustExec("create table t1(c1 int, c2 bigint, c3 bigint, primary key(c1), key(c2), key(c3));") tk.MustExec("insert into t1 values(1, 1, 1), (100, 100, 100)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergeResultChCloseEarly", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergeResultChCloseEarly", "return(true)")) tk.MustExec("select /*+ use_index_merge(t1, primary, c2, c3) */ c1 from t1 where c1 < 100 or c2 < 100") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergeResultChCloseEarly")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergeResultChCloseEarly")) } var indexMergePanicRunSQL = func(t *testing.T, tk *testkit.TestKit, fp string) { @@ -882,7 +882,7 @@ func TestIndexMergePanicPartialIndexWorker(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicPartialIndexWorker" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicPartialIndexWorker" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -895,7 +895,7 @@ func TestIndexMergePanicPartialTableWorker(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicPartialTableWorker" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicPartialTableWorker" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -908,7 +908,7 @@ func TestIndexMergePanicPartialProcessWorkerUnion(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicProcessWorkerUnion" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicProcessWorkerUnion" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -921,7 +921,7 @@ func TestIndexMergePanicPartialProcessWorkerIntersection(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicProcessWorkerIntersection" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicProcessWorkerIntersection" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -934,7 +934,7 @@ func TestIndexMergePanicPartitionTableIntersectionWorker(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicPartitionTableIntersectionWorker" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicPartitionTableIntersectionWorker" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -947,7 +947,7 @@ func TestIndexMergePanicTableScanWorker(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - fp := "github.com/pingcap/tidb/executor/testIndexMergePanicTableScanWorker" + fp := "github.com/pingcap/tidb/pkg/executor/testIndexMergePanicTableScanWorker" for i := 0; i < 100; i++ { require.NoError(t, failpoint.Enable(fp, fmt.Sprintf(`panic("%s")`, fp))) indexMergePanicRunSQL(t, tk, fp) @@ -960,7 +960,7 @@ func TestIndexMergeError(t *testing.T) { tk := testkit.NewTestKit(t, store) setupPartitionTableHelper(tk) - packagePath := "github.com/pingcap/tidb/executor/" + packagePath := "github.com/pingcap/tidb/pkg/executor/" errFPPaths := []string{ packagePath + "testIndexMergeErrorPartialIndexWorker", packagePath + "testIndexMergeErrorPartialTableWorker", @@ -986,15 +986,15 @@ func TestIndexMergeCoprGoroutinesLeak(t *testing.T) { require.Contains(t, res[1][0], "IndexMerge") // If got goroutines leak in coprocessor, ci will fail. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergePartialTableWorkerCoprLeak", `panic("testIndexMergePartialTableWorkerCoprLeak")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergePartialTableWorkerCoprLeak", `panic("testIndexMergePartialTableWorkerCoprLeak")`)) err = tk.QueryToErr(sql) require.Contains(t, err.Error(), "testIndexMergePartialTableWorkerCoprLeak") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergePartialTableWorkerCoprLeak")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergePartialTableWorkerCoprLeak")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexMergePartialIndexWorkerCoprLeak", `panic("testIndexMergePartialIndexWorkerCoprLeak")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexMergePartialIndexWorkerCoprLeak", `panic("testIndexMergePartialIndexWorkerCoprLeak")`)) err = tk.QueryToErr(sql) require.Contains(t, err.Error(), "testIndexMergePartialIndexWorkerCoprLeak") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexMergePartialIndexWorkerCoprLeak")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexMergePartialIndexWorkerCoprLeak")) } type valueStruct struct { @@ -1184,7 +1184,7 @@ func TestIndexMergeReaderIssue45279(t *testing.T) { // This function should return successfully var ctx context.Context ctx, executor.IndexMergeCancelFuncForTest = context.WithCancel(context.Background()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testCancelContext", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testCancelContext", "return()")) rs, _ := tk.ExecWithContext(ctx, "select * from reproduce where c1 in (0, 1, 2, 3) or c2 in (0, 1, 2);") session.ResultSetToStringSlice(ctx, tk.Session(), rs) failpoint.Disable("github.com/pingcap/tidb/br/pkg/checksum/testCancelContext") @@ -1225,8 +1225,8 @@ func TestIndexMergeLimitNotPushedOnPartialSideButKeepOrder(t *testing.T) { } tk.MustExec("analyze table t") tk.MustExec("insert into t values " + strings.Join(valsInsert, ",")) - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceIndexMergeKeepOrder", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceIndexMergeKeepOrder") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceIndexMergeKeepOrder", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceIndexMergeKeepOrder") for i := 0; i < 100; i++ { valA, valB, valC, limit := rand.Intn(100), rand.Intn(100), rand.Intn(50), rand.Intn(100)+1 maxEle := tk.MustQuery(fmt.Sprintf("select ifnull(max(c), 100) from (select c from t use index(idx3) where (a = %d or b = %d) and c >= %d order by c limit %d) t", valA, valB, valC, limit)).Rows()[0][0] diff --git a/pkg/executor/test/indexmergereadtest/main_test.go b/pkg/executor/test/indexmergereadtest/main_test.go new file mode 100644 index 0000000000000..fbdacc7fb573c --- /dev/null +++ b/pkg/executor/test/indexmergereadtest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package indexmergereadtest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/issuetest/BUILD.bazel b/pkg/executor/test/issuetest/BUILD.bazel new file mode 100644 index 0000000000000..3810bcaefa89c --- /dev/null +++ b/pkg/executor/test/issuetest/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "issuetest_test", + timeout = "short", + srcs = [ + "executor_issue_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 17, + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/testkit", + "//pkg/util", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/issuetest/executor_issue_test.go b/pkg/executor/test/issuetest/executor_issue_test.go similarity index 92% rename from executor/test/issuetest/executor_issue_test.go rename to pkg/executor/test/issuetest/executor_issue_test.go index 204167ecd1100..f47d02e592c57 100644 --- a/executor/test/issuetest/executor_issue_test.go +++ b/pkg/executor/test/issuetest/executor_issue_test.go @@ -22,16 +22,16 @@ import ( "time" "github.com/pingcap/failpoint" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) @@ -42,28 +42,28 @@ func TestIssue24210(t *testing.T) { tk.MustExec("set tidb_cost_model_version=1") // for ProjectionExec - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockProjectionExecBaseExecutorOpenReturnedError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockProjectionExecBaseExecutorOpenReturnedError", `return(true)`)) err := tk.ExecToErr("select a from (select 1 as a, 2 as b) t") require.EqualError(t, err, "mock ProjectionExec.baseExecutor.Open returned error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockProjectionExecBaseExecutorOpenReturnedError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockProjectionExecBaseExecutorOpenReturnedError")) // for HashAggExec - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/aggregate/mockHashAggExecBaseExecutorOpenReturnedError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/aggregate/mockHashAggExecBaseExecutorOpenReturnedError", `return(true)`)) err = tk.ExecToErr("select sum(a) from (select 1 as a, 2 as b) t group by b") require.EqualError(t, err, "mock HashAggExec.baseExecutor.Open returned error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/aggregate/mockHashAggExecBaseExecutorOpenReturnedError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/aggregate/mockHashAggExecBaseExecutorOpenReturnedError")) // for StreamAggExec - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/aggregate/mockStreamAggExecBaseExecutorOpenReturnedError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/aggregate/mockStreamAggExecBaseExecutorOpenReturnedError", `return(true)`)) err = tk.ExecToErr("select sum(a) from (select 1 as a, 2 as b) t") require.EqualError(t, err, "mock StreamAggExec.baseExecutor.Open returned error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/aggregate/mockStreamAggExecBaseExecutorOpenReturnedError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/aggregate/mockStreamAggExecBaseExecutorOpenReturnedError")) // for SelectionExec - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/mockSelectionExecBaseExecutorOpenReturnedError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockSelectionExecBaseExecutorOpenReturnedError", `return(true)`)) err = tk.ExecToErr("select * from (select rand() as a) t where a > 0") require.EqualError(t, err, "mock SelectionExec.baseExecutor.Open returned error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/mockSelectionExecBaseExecutorOpenReturnedError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockSelectionExecBaseExecutorOpenReturnedError")) } func TestUnionIssue(t *testing.T) { @@ -168,7 +168,7 @@ func TestIssue28650(t *testing.T) { } func TestIssue30289(t *testing.T) { - fpName := "github.com/pingcap/tidb/executor/issue30289" + fpName := "github.com/pingcap/tidb/pkg/executor/issue30289" store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -334,9 +334,9 @@ func TestFix31038(t *testing.T) { tk.MustExec("use test") tk.MustExec("drop table if exists t123") tk.MustExec("create table t123 (id int);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/disable-collect-execution", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/disable-collect-execution", `return(true)`)) tk.MustQuery("select * from t123;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/disable-collect-execution")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/disable-collect-execution")) } func TestFix31537(t *testing.T) { @@ -387,8 +387,8 @@ func TestFix31537(t *testing.T) { } func TestIssue30382(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -673,18 +673,18 @@ func TestIssue42662(t *testing.T) { tk.MustExec("set global tidb_server_memory_limit='1600MB'") tk.MustExec("set global tidb_server_memory_limit_sess_min_size=128*1024*1024") tk.MustExec("set global tidb_mem_oom_action = 'cancel'") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/issue42662_1", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/issue42662_1", `return(true)`)) // tk.Session() should be marked as MemoryTop1Tracker but not killed. tk.MustQuery("select /*+ hash_join(t1)*/ * from t1 join t2 on t1.a = t2.a and t1.b = t2.b") // try to trigger the kill top1 logic - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/servermemorylimit/issue42662_2", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/servermemorylimit/issue42662_2", `return(true)`)) time.Sleep(1 * time.Second) // no error should be returned tk.MustQuery("select count(*) from t1") tk.MustQuery("select count(*) from t1") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/issue42662_1")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/servermemorylimit/issue42662_2")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/issue42662_1")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/servermemorylimit/issue42662_2")) } diff --git a/pkg/executor/test/issuetest/main_test.go b/pkg/executor/test/issuetest/main_test.go new file mode 100644 index 0000000000000..5abdfa76228db --- /dev/null +++ b/pkg/executor/test/issuetest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package issuetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/jointest/BUILD.bazel b/pkg/executor/test/jointest/BUILD.bazel new file mode 100644 index 0000000000000..81f76b3f6f938 --- /dev/null +++ b/pkg/executor/test/jointest/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "jointest_test", + timeout = "moderate", + srcs = [ + "join_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 11, + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/session", + "//pkg/testkit", + "//pkg/util", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/jointest/hashjoin/BUILD.bazel b/pkg/executor/test/jointest/hashjoin/BUILD.bazel new file mode 100644 index 0000000000000..9459bb1e3014b --- /dev/null +++ b/pkg/executor/test/jointest/hashjoin/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "hashjoin_test", + timeout = "short", + srcs = [ + "hash_join_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 14, + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/util/dbterror/exeerrors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/jointest/hashjoin/hash_join_test.go b/pkg/executor/test/jointest/hashjoin/hash_join_test.go similarity index 95% rename from executor/test/jointest/hashjoin/hash_join_test.go rename to pkg/executor/test/jointest/hashjoin/hash_join_test.go index a482cc32c68e6..c7a399892474c 100644 --- a/executor/test/jointest/hashjoin/hash_join_test.go +++ b/pkg/executor/test/jointest/hashjoin/hash_join_test.go @@ -22,11 +22,11 @@ import ( "testing" "github.com/pingcap/failpoint" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" ) @@ -67,9 +67,9 @@ func TestIndexNestedLoopHashJoin(t *testing.T) { } // index hash join with semi join - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/core/MockOnlyEnableIndexHashJoin", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/MockOnlyEnableIndexHashJoin", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/core/MockOnlyEnableIndexHashJoin")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/MockOnlyEnableIndexHashJoin")) }() tk.MustExec("drop table t") tk.MustExec("CREATE TABLE `t` ( `l_orderkey` int(11) NOT NULL,`l_linenumber` int(11) NOT NULL,`l_partkey` int(11) DEFAULT NULL,`l_suppkey` int(11) DEFAULT NULL,PRIMARY KEY (`l_orderkey`,`l_linenumber`))") @@ -304,9 +304,9 @@ func TestIssue18572_1(t *testing.T) { tk.MustExec("insert into t1 values(1, 1);") tk.MustExec("insert into t1 select * from t1;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexHashJoinInnerWorkerErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinInnerWorkerErr", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexHashJoinInnerWorkerErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinInnerWorkerErr")) }() rs, err := tk.Exec("select /*+ inl_hash_join(t1) */ * from t1 right join t1 t2 on t1.b=t2.b;") @@ -325,9 +325,9 @@ func TestIssue18572_2(t *testing.T) { tk.MustExec("insert into t1 values(1, 1);") tk.MustExec("insert into t1 select * from t1;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexHashJoinOuterWorkerErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinOuterWorkerErr", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexHashJoinOuterWorkerErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinOuterWorkerErr")) }() rs, err := tk.Exec("select /*+ inl_hash_join(t1) */ * from t1 right join t1 t2 on t1.b=t2.b;") @@ -346,9 +346,9 @@ func TestIssue18572_3(t *testing.T) { tk.MustExec("insert into t1 values(1, 1);") tk.MustExec("insert into t1 select * from t1;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexHashJoinBuildErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinBuildErr", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexHashJoinBuildErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinBuildErr")) }() rs, err := tk.Exec("select /*+ inl_hash_join(t1) */ * from t1 right join t1 t2 on t1.b=t2.b;") @@ -399,20 +399,20 @@ func TestIssue20270(t *testing.T) { tk.MustExec("create table t1(c1 int, c2 int)") tk.MustExec("insert into t values(1,1),(2,2)") tk.MustExec("insert into t1 values(2,3),(4,4)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/killedInJoin2Chunk", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/killedInJoin2Chunk", "return(true)")) err := tk.QueryToErr("select /*+ TIDB_HJ(t, t1) */ * from t left join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") require.Equal(t, exeerrors.ErrQueryInterrupted, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/killedInJoin2Chunk")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/killedInJoin2Chunk")) plannercore.ForceUseOuterBuild4Test.Store(true) defer func() { plannercore.ForceUseOuterBuild4Test.Store(false) }() - err = failpoint.Enable("github.com/pingcap/tidb/executor/killedInJoin2ChunkForOuterHashJoin", "return(true)") + err = failpoint.Enable("github.com/pingcap/tidb/pkg/executor/killedInJoin2ChunkForOuterHashJoin", "return(true)") require.NoError(t, err) tk.MustExec("insert into t1 values(1,30),(2,40)") err = tk.QueryToErr("select /*+ TIDB_HJ(t, t1) */ * from t left outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") require.Equal(t, exeerrors.ErrQueryInterrupted, err) - err = failpoint.Disable("github.com/pingcap/tidb/executor/killedInJoin2ChunkForOuterHashJoin") + err = failpoint.Disable("github.com/pingcap/tidb/pkg/executor/killedInJoin2ChunkForOuterHashJoin") require.NoError(t, err) } @@ -436,28 +436,28 @@ func TestIssue31129(t *testing.T) { tk.MustExec("analyze table s") // Test IndexNestedLoopHashJoin keepOrder. - fpName := "github.com/pingcap/tidb/executor/TestIssue31129" + fpName := "github.com/pingcap/tidb/pkg/executor/TestIssue31129" require.NoError(t, failpoint.Enable(fpName, "return")) err := tk.QueryToErr("select /*+ INL_HASH_JOIN(s) */ * from t left join s on t.a=s.a order by t.pk") require.True(t, strings.Contains(err.Error(), "TestIssue31129")) require.NoError(t, failpoint.Disable(fpName)) // Test IndexNestedLoopHashJoin build hash table panic. - fpName = "github.com/pingcap/tidb/executor/IndexHashJoinBuildHashTablePanic" + fpName = "github.com/pingcap/tidb/pkg/executor/IndexHashJoinBuildHashTablePanic" require.NoError(t, failpoint.Enable(fpName, `panic("IndexHashJoinBuildHashTablePanic")`)) err = tk.QueryToErr("select /*+ INL_HASH_JOIN(s) */ * from t left join s on t.a=s.a order by t.pk") require.True(t, strings.Contains(err.Error(), "IndexHashJoinBuildHashTablePanic")) require.NoError(t, failpoint.Disable(fpName)) // Test IndexNestedLoopHashJoin fetch inner fail. - fpName = "github.com/pingcap/tidb/executor/IndexHashJoinFetchInnerResultsErr" + fpName = "github.com/pingcap/tidb/pkg/executor/IndexHashJoinFetchInnerResultsErr" require.NoError(t, failpoint.Enable(fpName, "return")) err = tk.QueryToErr("select /*+ INL_HASH_JOIN(s) */ * from t left join s on t.a=s.a order by t.pk") require.True(t, strings.Contains(err.Error(), "IndexHashJoinFetchInnerResultsErr")) require.NoError(t, failpoint.Disable(fpName)) // Test IndexNestedLoopHashJoin build hash table panic and IndexNestedLoopHashJoin fetch inner fail at the same time. - fpName1, fpName2 := "github.com/pingcap/tidb/executor/IndexHashJoinBuildHashTablePanic", "github.com/pingcap/tidb/executor/IndexHashJoinFetchInnerResultsErr" + fpName1, fpName2 := "github.com/pingcap/tidb/pkg/executor/IndexHashJoinBuildHashTablePanic", "github.com/pingcap/tidb/pkg/executor/IndexHashJoinFetchInnerResultsErr" require.NoError(t, failpoint.Enable(fpName1, `panic("IndexHashJoinBuildHashTablePanic")`)) require.NoError(t, failpoint.Enable(fpName2, "return")) err = tk.QueryToErr("select /*+ INL_HASH_JOIN(s) */ * from t left join s on t.a=s.a order by t.pk") diff --git a/pkg/executor/test/jointest/hashjoin/main_test.go b/pkg/executor/test/jointest/hashjoin/main_test.go new file mode 100644 index 0000000000000..aa9044d74c55e --- /dev/null +++ b/pkg/executor/test/jointest/hashjoin/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package hashjoin + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/jointest/join_test.go b/pkg/executor/test/jointest/join_test.go new file mode 100644 index 0000000000000..c60f1a061785e --- /dev/null +++ b/pkg/executor/test/jointest/join_test.go @@ -0,0 +1,1437 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package jointest + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/require" +) + +func TestJoinInDisk(t *testing.T) { + origin := config.RestoreFunc() + defer origin() + + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustExec("SET GLOBAL tidb_mem_oom_action='LOG'") + tk.MustExec("use test") + + sm := &testkit.MockSessionManager{ + PS: make([]*util.ProcessInfo, 0), + } + tk.Session().SetSessionManager(sm) + dom.ExpensiveQueryHandle().SetSessionManager(sm) + + // TODO(fengliyuan): how to ensure that it is using disk really? + tk.MustExec("set @@tidb_mem_quota_query=1;") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int, c2 int)") + tk.MustExec("create table t1(c1 int, c2 int)") + tk.MustExec("insert into t values(1,1),(2,2)") + tk.MustExec("insert into t1 values(2,3),(4,4)") + result := tk.MustQuery("select /*+ TIDB_HJ(t, t2) */ * from t, t1 where t.c1 = t1.c1") + result.Check(testkit.Rows("2 2 2 3")) +} + +func TestJoin2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set @@tidb_index_lookup_join_concurrency = 200") + require.Equal(t, 200, tk.Session().GetSessionVars().IndexLookupJoinConcurrency()) + + tk.MustExec("set @@tidb_index_lookup_join_concurrency = 4") + require.Equal(t, 4, tk.Session().GetSessionVars().IndexLookupJoinConcurrency()) + + tk.MustExec("set @@tidb_index_lookup_size = 2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c int)") + tk.MustExec("insert t values (1)") + tests := []struct { + sql string + result [][]interface{} + }{ + { + "select 1 from t as a left join t as b on 0", + testkit.Rows("1"), + }, + { + "select 1 from t as a join t as b on 1", + testkit.Rows("1"), + }, + } + for _, tt := range tests { + result := tk.MustQuery(tt.sql) + result.Check(tt.result) + } + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int, c2 int)") + tk.MustExec("create table t1(c1 int, c2 int)") + tk.MustExec("insert into t values(1,1),(2,2)") + tk.MustExec("insert into t1 values(2,3),(4,4)") + result := tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") + result.Check(testkit.Rows("1 1 ")) + result = tk.MustQuery("select * from t1 right outer join t on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") + result.Check(testkit.Rows(" 1 1")) + result = tk.MustQuery("select * from t right outer join t1 on t.c1 = t1.c1 where t.c1 = 1 or t1.c2 > 20") + result.Check(testkit.Rows()) + result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 where t1.c1 = 3 or false") + result.Check(testkit.Rows()) + result = tk.MustQuery("select * from t left outer join t1 on t.c1 = t1.c1 and t.c1 != 1 order by t1.c1") + result.Check(testkit.Rows("1 1 ", "2 2 2 3")) + result = tk.MustQuery("select t.c1, t1.c1 from t left outer join t1 on t.c1 = t1.c1 and t.c2 + t1.c2 <= 5") + result.Check(testkit.Rows("1 ", "2 2")) + + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t3") + + tk.MustExec("create table t1 (c1 int, c2 int)") + tk.MustExec("create table t2 (c1 int, c2 int)") + tk.MustExec("create table t3 (c1 int, c2 int)") + + tk.MustExec("insert into t1 values (1,1), (2,2), (3,3)") + tk.MustExec("insert into t2 values (1,1), (3,3), (5,5)") + tk.MustExec("insert into t3 values (1,1), (5,5), (9,9)") + + result = tk.MustQuery("select * from t1 left join t2 on t1.c1 = t2.c1 right join t3 on t2.c1 = t3.c1 order by t1.c1, t1.c2, t2.c1, t2.c2, t3.c1, t3.c2;") + result.Check(testkit.Rows(" 5 5", " 9 9", "1 1 1 1 1 1")) + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (c1 int)") + tk.MustExec("insert into t1 values (1), (1), (1)") + result = tk.MustQuery("select * from t1 a join t1 b on a.c1 = b.c1;") + result.Check(testkit.Rows("1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1", "1 1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int, index k(c1))") + tk.MustExec("create table t1(c1 int)") + tk.MustExec("insert into t values (1),(2),(3),(4),(5),(6),(7)") + tk.MustExec("insert into t1 values (1),(2),(3),(4),(5),(6),(7)") + result = tk.MustQuery("select a.c1 from t a , t1 b where a.c1 = b.c1 order by a.c1;") + result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7")) + // Test race. + result = tk.MustQuery("select a.c1 from t a , t1 b where a.c1 = b.c1 and a.c1 + b.c1 > 5 order by b.c1") + result.Check(testkit.Rows("3", "4", "5", "6", "7")) + result = tk.MustQuery("select a.c1 from t a , (select * from t1 limit 3) b where a.c1 = b.c1 order by b.c1;") + result.Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("drop table if exists t,t2,t1") + tk.MustExec("create table t(c1 int)") + tk.MustExec("create table t1(c1 int, c2 int)") + tk.MustExec("create table t2(c1 int, c2 int)") + tk.MustExec("insert into t1 values(1,2),(2,3),(3,4)") + tk.MustExec("insert into t2 values(1,0),(2,0),(3,0)") + tk.MustExec("insert into t values(1),(2),(3)") + result = tk.MustQuery("select * from t1 , t2 where t2.c1 = t1.c1 and t2.c2 = 0 and t1.c2 in (select * from t)") + result.Sort().Check(testkit.Rows("1 2 1 0", "2 3 2 0")) + result = tk.MustQuery("select * from t1 , t2 where t2.c1 = t1.c1 and t2.c2 = 0 and t1.c1 = 1 order by t1.c2 limit 1") + result.Sort().Check(testkit.Rows("1 2 1 0")) + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t(a int primary key, b int)") + tk.MustExec("create table t1(a int, b int, key s(b))") + tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") + tk.MustExec("insert into t1 values(1, 2), (1, 3), (1, 4), (3, 4), (4, 5)") + + // The physical plans of the two sql are tested at physical_plan_test.go + tk.MustQuery("select /*+ INL_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ * from t join t1 on t.a=t1.a").Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4")) + tk.MustQuery("select /*+ INL_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Sort().Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ * from t1 join t on t.a=t1.a and t.a < t1.b").Check(testkit.Rows("1 2 1 1", "1 3 1 1", "1 4 1 1", "3 4 3 3")) + // Test single index reader. + tk.MustQuery("select /*+ INL_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Check(testkit.Rows("2", "3")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Sort().Check(testkit.Rows("2", "3")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ t1.b from t1 join t on t.b=t1.b").Check(testkit.Rows("2", "3")) + tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t right outer join t1 on t.a=t1.a").Sort().Check(testkit.Rows("1 1 1 2", "1 1 1 3", "1 1 1 4", "3 3 3 4", " 4 5")) + tk.MustQuery("select /*+ INL_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ avg(t.b) from t right outer join t1 on t.a=t1.a").Check(testkit.Rows("1.5000")) + + // Test that two conflict hints will return warning. + tk.MustExec("select /*+ TIDB_INLJ(t) TIDB_SMJ(t) */ * from t join t1 on t.a=t1.a") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + tk.MustExec("select /*+ TIDB_INLJ(t) TIDB_HJ(t) */ * from t join t1 on t.a=t1.a") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + tk.MustExec("select /*+ TIDB_SMJ(t) TIDB_HJ(t) */ * from t join t1 on t.a=t1.a") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1),(2), (3)") + tk.MustQuery("select @a := @a + 1 from t, (select @a := 0) b;").Check(testkit.Rows("1", "2", "3")) + + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t(a int primary key, b int, key s(b))") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("insert into t values(1, 3), (2, 2), (3, 1)") + tk.MustExec("insert into t1 values(0, 0), (1, 2), (1, 3), (3, 4)") + tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4")) + tk.MustQuery("select /*+ INL_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ t.a, t.b from t join t1 on t.a=t1.a where t1.b = 4 limit 1").Check(testkit.Rows("3 1")) + tk.MustQuery("select /*+ INL_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t, t1) */ * from t right join t1 on t.a=t1.a order by t.b").Sort().Check(testkit.Rows("1 3 1 2", "1 3 1 3", "3 1 3 4", " 0 0")) + + // join reorder will disorganize the resulting schema + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("insert into t values(1,2)") + tk.MustExec("insert into t1 values(3,4)") + tk.MustQuery("select (select t1.a from t1 , t where t.a = s.a limit 2) from t as s").Check(testkit.Rows("3")) + + // test index join bug + tk.MustExec("drop table if exists t, t1") + tk.MustExec("create table t(a int, b int, key s1(a,b), key s2(b))") + tk.MustExec("create table t1(a int)") + tk.MustExec("insert into t values(1,2), (5,3), (6,4)") + tk.MustExec("insert into t1 values(1), (2), (3)") + tk.MustQuery("select /*+ INL_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) + tk.MustQuery("select /*+ INL_HASH_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) + tk.MustQuery("select /*+ INL_MERGE_JOIN(t) */ t1.a from t1, t where t.a = 5 and t.b = t1.a").Check(testkit.Rows("3")) + + // test issue#4997 + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(` + CREATE TABLE t1 ( + pk int(11) NOT NULL AUTO_INCREMENT primary key, + a int(11) DEFAULT NULL, + b date DEFAULT NULL, + c varchar(1) DEFAULT NULL, + KEY a (a), + KEY b (b), + KEY c (c,a) + )`) + tk.MustExec(` + CREATE TABLE t2 ( + pk int(11) NOT NULL AUTO_INCREMENT primary key, + a int(11) DEFAULT NULL, + b date DEFAULT NULL, + c varchar(1) DEFAULT NULL, + KEY a (a), + KEY b (b), + KEY c (c,a) + )`) + tk.MustExec(`insert into t1 value(1,1,"2000-11-11", null);`) + result = tk.MustQuery(` + SELECT table2.b AS field2 FROM + ( + t1 AS table1 LEFT OUTER JOIN + (SELECT tmp_t2.* FROM ( t2 AS tmp_t1 RIGHT JOIN t1 AS tmp_t2 ON (tmp_t2.a = tmp_t1.a))) AS table2 + ON (table2.c = table1.c) + ) `) + result.Check(testkit.Rows("")) + + // test virtual rows are included (issue#5771) + result = tk.MustQuery(`SELECT 1 FROM (SELECT 1) t1, (SELECT 1) t2`) + result.Check(testkit.Rows("1")) + + result = tk.MustQuery(` + SELECT @NUM := @NUM + 1 as NUM FROM + ( SELECT 1 UNION ALL + SELECT 2 UNION ALL + SELECT 3 + ) a + INNER JOIN + ( SELECT 1 UNION ALL + SELECT 2 UNION ALL + SELECT 3 + ) b, + (SELECT @NUM := 0) d; + `) + result.Check(testkit.Rows("1", "2", "3", "4", "5", "6", "7", "8", "9")) + + // This case is for testing: + // when the main thread calls Executor.Close() while the out data fetch worker and join workers are still working, + // we need to stop the goroutines as soon as possible to avoid unexpected error. + tk.MustExec("set @@tidb_hash_join_concurrency=5") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int)") + for i := 0; i < 100; i++ { + tk.MustExec("insert into t value(1)") + } + result = tk.MustQuery("select /*+ TIDB_HJ(s, r) */ * from t as s join t as r on s.a = r.a limit 1;") + result.Check(testkit.Rows("1 1")) + + tk.MustExec("drop table if exists user, aa, bb") + tk.MustExec("create table aa(id int)") + tk.MustExec("insert into aa values(1)") + tk.MustExec("create table bb(id int)") + tk.MustExec("insert into bb values(1)") + tk.MustExec("create table user(id int, name varchar(20))") + tk.MustExec("insert into user values(1, 'a'), (2, 'b')") + tk.MustQuery("select user.id,user.name from user left join aa on aa.id = user.id left join bb on aa.id = bb.id where bb.id < 10;").Check(testkit.Rows("1 a")) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t (a bigint);`) + tk.MustExec(`insert into t values (1);`) + tk.MustQuery(`select t2.a, t1.a from t t1 inner join (select "1" as a) t2 on t2.a = t1.a;`).Check(testkit.Rows("1 1")) + tk.MustQuery(`select t2.a, t1.a from t t1 inner join (select "2" as b, "1" as a) t2 on t2.a = t1.a;`).Check(testkit.Rows("1 1")) + + tk.MustExec("drop table if exists t1, t2, t3, t4") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int, b int)") + tk.MustExec("create table t3(a int, b int)") + tk.MustExec("create table t4(a int, b int)") + tk.MustExec("insert into t1 values(1, 1)") + tk.MustExec("insert into t2 values(1, 1)") + tk.MustExec("insert into t3 values(1, 1)") + tk.MustExec("insert into t4 values(1, 1)") + tk.MustQuery("select min(t2.b) from t1 right join t2 on t2.a=t1.a right join t3 on t2.a=t3.a left join t4 on t3.a=t4.a").Check(testkit.Rows("1")) +} + +func TestJoinCast(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + var result *testkit.Result + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int)") + tk.MustExec("create table t1(c1 int unsigned)") + tk.MustExec("insert into t values (1)") + tk.MustExec("insert into t1 values (1)") + result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") + result.Check(testkit.Rows("1")) + + // int64(-1) != uint64(18446744073709551615) + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 bigint)") + tk.MustExec("create table t1(c1 bigint unsigned)") + tk.MustExec("insert into t values (-1)") + tk.MustExec("insert into t1 values (18446744073709551615)") + result = tk.MustQuery("select * from t , t1 where t.c1 = t1.c1") + result.Check(testkit.Rows()) + + // float(1) == double(1) + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 float)") + tk.MustExec("create table t1(c1 double)") + tk.MustExec("insert into t values (1.0)") + tk.MustExec("insert into t1 values (1.00)") + result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") + result.Check(testkit.Rows("1")) + + // varchar("x") == char("x") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 varchar(1))") + tk.MustExec("create table t1(c1 char(1))") + tk.MustExec(`insert into t values ("x")`) + tk.MustExec(`insert into t1 values ("x")`) + result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") + result.Check(testkit.Rows("x")) + + // varchar("x") != char("y") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 varchar(1))") + tk.MustExec("create table t1(c1 char(1))") + tk.MustExec(`insert into t values ("x")`) + tk.MustExec(`insert into t1 values ("y")`) + result = tk.MustQuery("select t.c1 from t , t1 where t.c1 = t1.c1") + result.Check(testkit.Rows()) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int,c2 double)") + tk.MustExec("create table t1(c1 double,c2 int)") + tk.MustExec("insert into t values (1, 2), (1, NULL)") + tk.MustExec("insert into t1 values (1, 2), (1, NULL)") + result = tk.MustQuery("select * from t a , t1 b where (a.c1, a.c2) = (b.c1, b.c2);") + result.Check(testkit.Rows("1 2 1 2")) + + /* Issue 11895 */ + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t(c1 bigint unsigned);") + tk.MustExec("create table t1(c1 bit(64));") + tk.MustExec("insert into t value(18446744073709551615);") + tk.MustExec("insert into t1 value(-1);") + result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") + require.Len(t, result.Rows(), 1) + + /* Issues 11896 */ + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t(c1 bigint);") + tk.MustExec("create table t1(c1 bit(64));") + tk.MustExec("insert into t value(1);") + tk.MustExec("insert into t1 value(1);") + result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") + require.Len(t, result.Rows(), 1) + + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t(c1 bigint);") + tk.MustExec("create table t1(c1 bit(64));") + tk.MustExec("insert into t value(-1);") + tk.MustExec("insert into t1 value(18446744073709551615);") + result = tk.MustQuery("select * from t, t1 where t.c1 = t1.c1;") + // TODO: MySQL will return one row, because c1 in t1 is 0xffffffff, which equals to -1. + require.Len(t, result.Rows(), 0) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t(c1 bigint)") + tk.MustExec("create table t1(c1 bigint unsigned)") + tk.MustExec("create table t2(c1 Date)") + tk.MustExec("insert into t value(20191111)") + tk.MustExec("insert into t1 value(20191111)") + tk.MustExec("insert into t2 value('2019-11-11')") + result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1") + result.Check(testkit.Rows("20191111 20191111 2019-11-11")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2;") + tk.MustExec("create table t(c1 bigint);") + tk.MustExec("create table t1(c1 bigint unsigned);") + tk.MustExec("create table t2(c1 enum('a', 'b', 'c', 'd'));") + tk.MustExec("insert into t value(3);") + tk.MustExec("insert into t1 value(3);") + tk.MustExec("insert into t2 value('c');") + result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1;") + result.Check(testkit.Rows("3 3 c")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("drop table if exists t2;") + tk.MustExec("create table t(c1 bigint);") + tk.MustExec("create table t1(c1 bigint unsigned);") + tk.MustExec("create table t2 (c1 SET('a', 'b', 'c', 'd'));") + tk.MustExec("insert into t value(9);") + tk.MustExec("insert into t1 value(9);") + tk.MustExec("insert into t2 value('a,d');") + result = tk.MustQuery("select * from t, t1, t2 where t.c1 = t2.c1 and t1.c1 = t2.c1;") + result.Check(testkit.Rows("9 9 a,d")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 int)") + tk.MustExec("create table t1(c1 decimal(4,2))") + tk.MustExec("insert into t values(0), (2)") + tk.MustExec("insert into t1 values(0), (9)") + result = tk.MustQuery("select * from t left join t1 on t1.c1 = t.c1") + result.Sort().Check(testkit.Rows("0 0.00", "2 ")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 decimal(4,1))") + tk.MustExec("create table t1(c1 decimal(4,2))") + tk.MustExec("insert into t values(0), (2)") + tk.MustExec("insert into t1 values(0), (9)") + result = tk.MustQuery("select * from t left join t1 on t1.c1 = t.c1") + result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t(c1 decimal(4,1))") + tk.MustExec("create table t1(c1 decimal(4,2))") + tk.MustExec("create index k1 on t1(c1)") + tk.MustExec("insert into t values(0), (2)") + tk.MustExec("insert into t1 values(0), (9)") + result = tk.MustQuery("select /*+ INL_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") + result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) + result = tk.MustQuery("select /*+ INL_HASH_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") + result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) + result = tk.MustQuery("select /*+ INL_MERGE_JOIN(t1) */ * from t left join t1 on t1.c1 = t.c1") + result.Sort().Check(testkit.Rows("0.0 0.00", "2.0 ")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t(c1 char(10))") + tk.MustExec("create table t1(c1 char(10))") + tk.MustExec("create table t2(c1 char(10))") + tk.MustExec("insert into t values('abd')") + tk.MustExec("insert into t1 values('abc')") + tk.MustExec("insert into t2 values('abc')") + result = tk.MustQuery("select * from (select * from t union all select * from t1) t1 join t2 on t1.c1 = t2.c1") + result.Sort().Check(testkit.Rows("abc abc")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a varchar(10), index idx(a))") + tk.MustExec("insert into t values('1'), ('2'), ('3')") + tk.MustExec("set @@tidb_init_chunk_size=1") + result = tk.MustQuery("select a from (select /*+ INL_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") + result.Sort().Check(testkit.Rows("1", "2", "3")) + result = tk.MustQuery("select a from (select /*+ INL_HASH_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") + result.Sort().Check(testkit.Rows("1", "2", "3")) + result = tk.MustQuery("select a from (select /*+ INL_MERGE_JOIN(t1, t2) */ t1.a from t t1 join t t2 on t1.a=t2.a) t group by a") + result.Sort().Check(testkit.Rows("1", "2", "3")) + tk.MustExec("set @@tidb_init_chunk_size=32") +} + +func TestUsing(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3, t4") + tk.MustExec("create table t1 (a int, c int)") + tk.MustExec("create table t2 (a int, d int)") + tk.MustExec("create table t3 (a int)") + tk.MustExec("create table t4 (a int)") + tk.MustExec("insert t1 values (2, 4), (1, 3)") + tk.MustExec("insert t2 values (2, 5), (3, 6)") + tk.MustExec("insert t3 values (1)") + + tk.MustQuery("select * from t1 join t2 using (a)").Check(testkit.Rows("2 4 5")) + tk.MustQuery("select t1.a, t2.a from t1 join t2 using (a)").Check(testkit.Rows("2 2")) + + tk.MustQuery("select * from t1 right join t2 using (a) order by a").Check(testkit.Rows("2 5 4", "3 6 ")) + tk.MustQuery("select t1.a, t2.a from t1 right join t2 using (a) order by t2.a").Check(testkit.Rows("2 2", " 3")) + + tk.MustQuery("select * from t1 left join t2 using (a) order by a").Check(testkit.Rows("1 3 ", "2 4 5")) + tk.MustQuery("select t1.a, t2.a from t1 left join t2 using (a) order by t1.a").Check(testkit.Rows("1 ", "2 2")) + + tk.MustQuery("select * from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows("1 ")) + tk.MustQuery("select * from t1 join t2 using (a) right join t3 on (t2.a = t3.a)").Check(testkit.Rows(" 1")) + tk.MustQuery("select t2.a from t1 join t2 using (a) right join t3 on (t1.a = t3.a)").Check(testkit.Rows("")) + tk.MustQuery("select t1.a, t2.a, t3.a from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows(" 1")) + tk.MustQuery("select t1.c, t2.d from t1 join t2 using (a) right join t3 using (a)").Check(testkit.Rows(" ")) + + tk.MustExec("alter table t1 add column b int default 1 after a") + tk.MustExec("alter table t2 add column b int default 1 after a") + tk.MustQuery("select * from t1 join t2 using (b, a)").Check(testkit.Rows("2 1 4 5")) + + tk.MustExec("select * from (t1 join t2 using (a)) join (t3 join t4 using (a)) on (t2.a = t4.a and t1.a = t3.a)") + + tk.MustExec("drop table if exists t, tt") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("create table tt(b int, a int)") + tk.MustExec("insert into t (a, b) values(1, 1)") + tk.MustExec("insert into tt (a, b) values(1, 2)") + tk.MustQuery("select * from t join tt using(a)").Check(testkit.Rows("1 1 2")) + + tk.MustExec("drop table if exists t, tt") + tk.MustExec("create table t(a float, b int)") + tk.MustExec("create table tt(b bigint, a int)") + // Check whether this sql can execute successfully. + tk.MustExec("select * from t join tt using(a)") + + tk.MustExec("drop table if exists t, s") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("create table s(b int, a int)") + tk.MustExec("insert into t values(1,1), (2,2), (3,3), (null,null)") + tk.MustExec("insert into s values(1,1), (3,3), (null,null)") + + // For issue 20477 + tk.MustQuery("select t.*, s.* from t join s using(a)").Check(testkit.Rows("1 1 1 1", "3 3 3 3")) + tk.MustQuery("select s.a from t join s using(a)").Check(testkit.Rows("1", "3")) + tk.MustQuery("select s.a from t join s using(a) where s.a > 1").Check(testkit.Rows("3")) + tk.MustQuery("select s.a from t join s using(a) order by s.a").Check(testkit.Rows("1", "3")) + tk.MustQuery("select s.a from t join s using(a) where s.a > 1 order by s.a").Check(testkit.Rows("3")) + tk.MustQuery("select s.a from t join s using(a) where s.a > 1 order by s.a limit 2").Check(testkit.Rows("3")) + + // For issue 20441 + tk.MustExec(`DROP TABLE if exists t1, t2, t3`) + tk.MustExec(`create table t1 (i int)`) + tk.MustExec(`create table t2 (i int)`) + tk.MustExec(`create table t3 (i int)`) + tk.MustExec(`select * from t1,t2 natural left join t3 order by t1.i,t2.i,t3.i`) + tk.MustExec(`select t1.i,t2.i,t3.i from t2 natural left join t3,t1 order by t1.i,t2.i,t3.i`) + tk.MustExec(`select * from t1,t2 natural right join t3 order by t1.i,t2.i,t3.i`) + tk.MustExec(`select t1.i,t2.i,t3.i from t2 natural right join t3,t1 order by t1.i,t2.i,t3.i`) + + // For issue 15844 + tk.MustExec(`DROP TABLE if exists t0, t1`) + tk.MustExec(`CREATE TABLE t0(c0 INT)`) + tk.MustExec(`CREATE TABLE t1(c0 INT)`) + tk.MustExec(`SELECT t0.c0 FROM t0 NATURAL RIGHT JOIN t1 WHERE t1.c0`) + + // For issue 20958 + tk.MustExec(`DROP TABLE if exists t1, t2`) + tk.MustExec(`create table t1(id int, name varchar(20));`) + tk.MustExec(`create table t2(id int, address varchar(30));`) + tk.MustExec(`insert into t1 values(1,'gangshen');`) + tk.MustExec(`insert into t2 values(1,'HangZhou');`) + tk.MustQuery(`select t2.* from t1 inner join t2 using (id) limit 1;`).Check(testkit.Rows("1 HangZhou")) + tk.MustQuery(`select t2.* from t1 inner join t2 on t1.id = t2.id limit 1;`).Check(testkit.Rows("1 HangZhou")) + + // For issue 20476 + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int)") + tk.MustExec("insert into t1 (a) values(1)") + tk.MustQuery("select t1.*, t2.* from t1 join t1 t2 using(a)").Check(testkit.Rows("1 1")) + tk.MustQuery("select * from t1 join t1 t2 using(a)").Check(testkit.Rows("1")) + + // For issue 18992 + tk.MustExec("drop table t") + tk.MustExec("CREATE TABLE t ( a varchar(55) NOT NULL, b varchar(55) NOT NULL, c int(11) DEFAULT NULL, d int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("update t t1 join t t2 using(a,b) set t1.c=t2.d;") + + // For issue 20467 + tk.MustExec(`DROP TABLE if exists t1,t2,t3,t4,t5`) + tk.MustExec(`CREATE TABLE t1 (a INT, b INT)`) + tk.MustExec(`CREATE TABLE t2 (a INT, b INT)`) + tk.MustExec(`CREATE TABLE t3 (a INT, b INT)`) + tk.MustExec(`INSERT INTO t1 VALUES (1,1)`) + tk.MustExec(`INSERT INTO t2 VALUES (1,1)`) + tk.MustExec(`INSERT INTO t3 VALUES (1,1)`) + tk.MustGetErrMsg(`SELECT * FROM t1 JOIN (t2 JOIN t3 USING (b)) USING (a)`, "[planner:1052]Column 'a' in from clause is ambiguous") + + // For issue 6712 + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (t1 int , t0 int)") + tk.MustExec("create table t2 (t2 int, t0 int)") + tk.MustExec("insert into t1 select 11, 1") + tk.MustExec("insert into t2 select 22, 1") + tk.MustQuery("select t1.t0, t2.t0 from t1 join t2 using(t0) group by t1.t0").Check(testkit.Rows("1 1")) + tk.MustQuery("select t1.t0, t2.t0 from t1 join t2 using(t0) having t1.t0 > 0").Check(testkit.Rows("1 1")) +} + +func TestSubquery(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_hash_join_concurrency=1") + tk.MustExec("set @@tidb_hashagg_partial_concurrency=1") + tk.MustExec("set @@tidb_hashagg_final_concurrency=1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c int, d int)") + tk.MustExec("insert t values (1, 1)") + tk.MustExec("insert t values (2, 2)") + tk.MustExec("insert t values (3, 4)") + tk.MustExec("commit") + + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + + result := tk.MustQuery("select * from t where exists(select * from t k where t.c = k.c having sum(c) = 1)") + result.Check(testkit.Rows("1 1")) + result = tk.MustQuery("select * from t where exists(select k.c, k.d from t k, t p where t.c = k.d)") + result.Check(testkit.Rows("1 1", "2 2")) + result = tk.MustQuery("select 1 = (select count(*) from t where t.c = k.d) from t k") + result.Check(testkit.Rows("1", "1", "0")) + result = tk.MustQuery("select 1 = (select count(*) from t where exists( select * from t m where t.c = k.d)) from t k") + result.Sort().Check(testkit.Rows("0", "1", "1")) + result = tk.MustQuery("select t.c = any (select count(*) from t) from t") + result.Sort().Check(testkit.Rows("0", "0", "1")) + result = tk.MustQuery("select * from t where (t.c, 6) = any (select count(*), sum(t.c) from t)") + result.Check(testkit.Rows("3 4")) + result = tk.MustQuery("select t.c from t where (t.c) < all (select count(*) from t)") + result.Check(testkit.Rows("1", "2")) + result = tk.MustQuery("select t.c from t where (t.c, t.d) = any (select * from t)") + result.Sort().Check(testkit.Rows("1", "2", "3")) + result = tk.MustQuery("select t.c from t where (t.c, t.d) != all (select * from t)") + result.Check(testkit.Rows()) + result = tk.MustQuery("select (select count(*) from t where t.c = k.d) from t k") + result.Sort().Check(testkit.Rows("0", "1", "1")) + result = tk.MustQuery("select t.c from t where (t.c, t.d) in (select * from t)") + result.Sort().Check(testkit.Rows("1", "2", "3")) + result = tk.MustQuery("select t.c from t where (t.c, t.d) not in (select * from t)") + result.Check(testkit.Rows()) + result = tk.MustQuery("select * from t A inner join t B on A.c = B.c and A.c > 100") + result.Check(testkit.Rows()) + // = all empty set is true + result = tk.MustQuery("select t.c from t where (t.c, t.d) != all (select * from t where d > 1000)") + result.Sort().Check(testkit.Rows("1", "2", "3")) + result = tk.MustQuery("select t.c from t where (t.c) < any (select c from t where d > 1000)") + result.Check(testkit.Rows()) + tk.MustExec("insert t values (NULL, NULL)") + result = tk.MustQuery("select (t.c) < any (select c from t) from t") + result.Sort().Check(testkit.Rows("1", "1", "", "")) + result = tk.MustQuery("select (10) > all (select c from t) from t") + result.Check(testkit.Rows("", "", "", "")) + result = tk.MustQuery("select (c) > all (select c from t) from t") + result.Check(testkit.Rows("0", "0", "0", "")) + + tk.MustExec("drop table if exists a") + tk.MustExec("create table a (c int, d int)") + tk.MustExec("insert a values (1, 2)") + tk.MustExec("drop table if exists b") + tk.MustExec("create table b (c int, d int)") + tk.MustExec("insert b values (2, 1)") + + result = tk.MustQuery("select * from a b where c = (select d from b a where a.c = 2 and b.c = 1)") + result.Check(testkit.Rows("1 2")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(c int)") + tk.MustExec("insert t values(10), (8), (7), (9), (11)") + result = tk.MustQuery("select * from t where 9 in (select c from t s where s.c < t.c limit 3)") + result.Check(testkit.Rows("10")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int, v int)") + tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") + result = tk.MustQuery("select * from t where v=(select min(t1.v) from t t1, t t2, t t3 where t1.id=t2.id and t2.id=t3.id and t1.id=t.id)") + result.Check(testkit.Rows("1 1", "2 2", "3 3")) + + result = tk.MustQuery("select exists (select t.id from t where s.id < 2 and t.id = s.id) from t s") + result.Sort().Check(testkit.Rows("0", "0", "1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(c int)") + result = tk.MustQuery("select exists(select count(*) from t)") + result.Check(testkit.Rows("1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int primary key, v int)") + tk.MustExec("insert into t values(1, 1), (2, 2), (3, 3)") + result = tk.MustQuery("select (select t.id from t where s.id < 2 and t.id = s.id) from t s") + result.Sort().Check(testkit.Rows("1", "", "")) + rs, err := tk.Exec("select (select t.id from t where t.id = t.v and t.v != s.id) from t s") + require.NoError(t, err) + _, err = session.GetRows4Test(context.Background(), tk.Session(), rs) + require.Error(t, err) + require.NoError(t, rs.Close()) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists s") + tk.MustExec("create table t(id int)") + tk.MustExec("create table s(id int)") + tk.MustExec("insert into t values(1), (2)") + tk.MustExec("insert into s values(2), (2)") + result = tk.MustQuery("select id from t where(select count(*) from s where s.id = t.id) > 0") + result.Check(testkit.Rows("2")) + result = tk.MustQuery("select *, (select count(*) from s where id = t.id limit 1, 1) from t") + result.Check(testkit.Rows("1 ", "2 ")) + + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists s") + tk.MustExec("create table t(id int primary key)") + tk.MustExec("create table s(id int)") + tk.MustExec("insert into t values(1), (2)") + tk.MustExec("insert into s values(2), (2)") + result = tk.MustQuery("select *, (select count(id) from s where id = t.id) from t") + result.Check(testkit.Rows("1 0", "2 2")) + result = tk.MustQuery("select *, 0 < any (select count(id) from s where id = t.id) from t") + result.Check(testkit.Rows("1 0", "2 1")) + result = tk.MustQuery("select (select count(*) from t k where t.id = id) from s, t where t.id = s.id limit 1") + result.Check(testkit.Rows("1")) + + tk.MustExec("drop table if exists t, s") + tk.MustExec("create table t(id int primary key)") + tk.MustExec("create table s(id int, index k(id))") + tk.MustExec("insert into t values(1), (2)") + tk.MustExec("insert into s values(2), (2)") + result = tk.MustQuery("select (select id from s where s.id = t.id order by s.id limit 1) from t") + result.Check(testkit.Rows("", "2")) + + tk.MustExec("drop table if exists t, s") + tk.MustExec("create table t(id int)") + tk.MustExec("create table s(id int)") + tk.MustExec("insert into t values(2), (2)") + tk.MustExec("insert into s values(2)") + result = tk.MustQuery("select (select id from s where s.id = t.id order by s.id) from t") + result.Check(testkit.Rows("2", "2")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(dt datetime)") + result = tk.MustQuery("select (select 1 from t where DATE_FORMAT(o.dt,'%Y-%m')) from t o") + result.Check(testkit.Rows()) + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(f1 int, f2 int)") + tk.MustExec("create table t2(fa int, fb int)") + tk.MustExec("insert into t1 values (1,1),(1,1),(1,2),(1,2),(1,2),(1,3)") + tk.MustExec("insert into t2 values (1,1),(1,2),(1,3)") + result = tk.MustQuery("select f1,f2 from t1 group by f1,f2 having count(1) >= all (select fb from t2 where fa = f1)") + result.Check(testkit.Rows("1 2")) + + tk.MustExec("DROP TABLE IF EXISTS t1, t2") + tk.MustExec("CREATE TABLE t1(a INT)") + tk.MustExec("CREATE TABLE t2 (d BINARY(2), PRIMARY KEY (d(1)), UNIQUE KEY (d))") + tk.MustExec("INSERT INTO t1 values(1)") + result = tk.MustQuery("SELECT 1 FROM test.t1, test.t2 WHERE 1 = (SELECT test.t2.d FROM test.t2 WHERE test.t1.a >= 1) and test.t2.d = 1;") + result.Check(testkit.Rows()) + + tk.MustExec("DROP TABLE IF EXISTS t1") + tk.MustExec("CREATE TABLE t1(a int, b int default 0)") + tk.MustExec("create index k1 on t1(a)") + tk.MustExec("INSERT INTO t1 (a) values(1), (2), (3), (4), (5)") + result = tk.MustQuery("select (select /*+ INL_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") + result.Check(testkit.Rows("1", "2", "3", "4", "5")) + result = tk.MustQuery("select (select /*+ INL_HASH_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") + result.Check(testkit.Rows("1", "2", "3", "4", "5")) + result = tk.MustQuery("select (select /*+ INL_MERGE_JOIN(x2) */ x2.a from t1 x1, t1 x2 where x1.a = t1.a and x1.a = x2.a) from t1") + result.Check(testkit.Rows("1", "2", "3", "4", "5")) + + // test left outer semi join & anti left outer semi join + tk.MustQuery("select 1 from (select t1.a in (select t1.a from t1) from t1) x;").Check(testkit.Rows("1", "1", "1", "1", "1")) + tk.MustQuery("select 1 from (select t1.a not in (select t1.a from t1) from t1) x;").Check(testkit.Rows("1", "1", "1", "1", "1")) + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int)") + tk.MustExec("create table t2(b int)") + tk.MustExec("insert into t1 values(1)") + tk.MustExec("insert into t2 values(1)") + tk.MustQuery("select * from t1 where a in (select a from t2)").Check(testkit.Rows("1")) + + tk.MustExec("insert into t2 value(null)") + tk.MustQuery("select * from t1 where 1 in (select b from t2)").Check(testkit.Rows("1")) + tk.MustQuery("select * from t1 where 1 not in (select b from t2)").Check(testkit.Rows()) + tk.MustQuery("select * from t1 where 2 not in (select b from t2)").Check(testkit.Rows()) + tk.MustQuery("select * from t1 where 2 in (select b from t2)").Check(testkit.Rows()) + tk.MustQuery("select 1 in (select b from t2) from t1").Check(testkit.Rows("1")) + tk.MustQuery("select 1 in (select 1 from t2) from t1").Check(testkit.Rows("1")) + tk.MustQuery("select 1 not in (select b from t2) from t1").Check(testkit.Rows("0")) + tk.MustQuery("select 1 not in (select 1 from t2) from t1").Check(testkit.Rows("0")) + + tk.MustExec("delete from t2 where b=1") + tk.MustQuery("select 1 in (select b from t2) from t1").Check(testkit.Rows("")) + tk.MustQuery("select 1 not in (select b from t2) from t1").Check(testkit.Rows("")) + tk.MustQuery("select 1 not in (select 1 from t2) from t1").Check(testkit.Rows("0")) + tk.MustQuery("select 1 in (select 1 from t2) from t1").Check(testkit.Rows("1")) + tk.MustQuery("select 1 not in (select null from t1) from t2").Check(testkit.Rows("")) + tk.MustQuery("select 1 in (select null from t1) from t2").Check(testkit.Rows("")) + + tk.MustExec("drop table if exists s") + tk.MustExec("create table s(a int not null, b int)") + tk.MustExec("set sql_mode = ''") + tk.MustQuery("select (2,0) in (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) not in (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) = any (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) != all (select s.a, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) in (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) not in (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) = any (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustQuery("select (2,0) != all (select s.b, min(s.b) from s) as f").Check(testkit.Rows("")) + tk.MustExec("insert into s values(1,null)") + tk.MustQuery("select 1 in (select b from s)").Check(testkit.Rows("")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1),(null)") + tk.MustQuery("select a not in (select 1) from t").Sort().Check(testkit.Rows( + "0", + "", + )) + tk.MustQuery("select 1 not in (select null from t t1) from t").Check(testkit.Rows( + "", + "", + )) + tk.MustQuery("select 1 in (select null from t t1) from t").Check(testkit.Rows( + "", + "", + )) + tk.MustQuery("select a in (select 0) xx from (select null as a) x").Check(testkit.Rows("")) + + tk.MustExec("drop table t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,null),(null, null),(null, 2)") + tk.MustQuery("select * from t t1 where (2 in (select a from t t2 where (t2.b=t1.b) is null))").Check(testkit.Rows()) + tk.MustQuery("select (t2.a in (select t1.a from t t1)) is true from t t2").Sort().Check(testkit.Rows( + "0", + "0", + "1", + )) + + tk.MustExec("set @@tidb_hash_join_concurrency=5") +} + +func TestJoinLeak(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_hash_join_concurrency=1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (d int)") + tk.MustExec("begin") + for i := 0; i < 1002; i++ { + tk.MustExec("insert t values (1)") + } + tk.MustExec("commit") + result, err := tk.Exec("select * from t t1 left join (select 1) t2 on 1") + require.NoError(t, err) + req := result.NewChunk(nil) + err = result.Next(context.Background(), req) + require.NoError(t, err) + time.Sleep(time.Millisecond) + require.NoError(t, result.Close()) + + tk.MustExec("set @@tidb_hash_join_concurrency=5") +} + +func TestNullEmptyAwareSemiJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, index idx_a(a), index idb_b(b), index idx_c(c))") + tk.MustExec("insert into t values(null, 1, 0), (1, 2, 0)") + tests := []struct { + sql string + }{ + { + "a, b from t t1 where a not in (select b from t t2)", + }, + { + "a, b from t t1 where a not in (select b from t t2 where t1.b = t2.a)", + }, + { + "a, b from t t1 where a not in (select a from t t2)", + }, + { + "a, b from t t1 where a not in (select a from t t2 where t1.b = t2.b)", + }, + { + "a, b from t t1 where a != all (select b from t t2)", + }, + { + "a, b from t t1 where a != all (select b from t t2 where t1.b = t2.a)", + }, + { + "a, b from t t1 where a != all (select a from t t2)", + }, + { + "a, b from t t1 where a != all (select a from t t2 where t1.b = t2.b)", + }, + { + "a, b from t t1 where not exists (select * from t t2 where t1.a = t2.b)", + }, + { + "a, b from t t1 where not exists (select * from t t2 where t1.a = t2.a)", + }, + } + results := []struct { + result [][]interface{} + }{ + { + testkit.Rows(), + }, + { + testkit.Rows("1 2"), + }, + { + testkit.Rows(), + }, + { + testkit.Rows(), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 2"), + }, + { + testkit.Rows(), + }, + { + testkit.Rows(), + }, + { + testkit.Rows(" 1"), + }, + { + testkit.Rows(" 1"), + }, + } + hints := [5]string{ + "/*+ HASH_JOIN(t1, t2) */", + "/*+ MERGE_JOIN(t1, t2) */", + "/*+ INL_JOIN(t1, t2) */", + "/*+ INL_HASH_JOIN(t1, t2) */", + "/*+ INL_MERGE_JOIN(t1, t2) */", + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(1, null, 0), (2, 1, 0)") + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows("2 1"), + }, + { + testkit.Rows(), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(1, null, 0), (2, 1, 0), (null, 2, 0)") + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(), + }, + { + testkit.Rows("1 "), + }, + { + testkit.Rows(" 2"), + }, + { + testkit.Rows(" 2"), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(1, null, 0), (2, null, 0)") + tests = []struct { + sql string + }{ + { + "a, b from t t1 where b not in (select a from t t2)", + }, + } + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows(), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(null, 1, 1), (2, 2, 2), (3, null, 3), (4, 4, 3)") + tests = []struct { + sql string + }{ + { + "a, b, a not in (select b from t t2) from t t1 order by a", + }, + { + "a, c, a not in (select c from t t2) from t t1 order by a", + }, + { + "a, b, a in (select b from t t2) from t t1 order by a", + }, + { + "a, c, a in (select c from t t2) from t t1 order by a", + }, + } + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows( + " 1 ", + "2 2 0", + "3 ", + "4 4 0", + ), + }, + { + testkit.Rows( + " 1 ", + "2 2 0", + "3 3 0", + "4 3 1", + ), + }, + { + testkit.Rows( + " 1 ", + "2 2 1", + "3 ", + "4 4 1", + ), + }, + { + testkit.Rows( + " 1 ", + "2 2 1", + "3 3 1", + "4 3 0", + ), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("drop table if exists s") + tk.MustExec("create table s(a int, b int)") + tk.MustExec("insert into s values(1, 2)") + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(null, null, 0)") + tests = []struct { + sql string + }{ + { + "a in (select b from t t2 where t2.a = t1.b) from s t1", + }, + { + "a in (select b from s t2 where t2.a = t1.b) from t t1", + }, + } + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows("0"), + }, + { + testkit.Rows("0"), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table s") + tk.MustExec("insert into s values(2, 2)") + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(null, 1, 0)") + tests = []struct { + sql string + }{ + { + "a in (select a from s t2 where t2.b = t1.b) from t t1", + }, + { + "a in (select a from s t2 where t2.b < t1.b) from t t1", + }, + } + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows("0"), + }, + { + testkit.Rows("0"), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("truncate table s") + tk.MustExec("insert into s values(null, 2)") + tk.MustExec("truncate table t") + tk.MustExec("insert into t values(1, 1, 0)") + tests = []struct { + sql string + }{ + { + "a in (select a from s t2 where t2.b = t1.b) from t t1", + }, + { + "b in (select a from s t2) from t t1", + }, + { + "* from t t1 where a not in (select a from s t2 where t2.b = t1.b)", + }, + { + "* from t t1 where a not in (select a from s t2)", + }, + { + "* from s t1 where a not in (select a from t t2)", + }, + } + results = []struct { + result [][]interface{} + }{ + { + testkit.Rows("0"), + }, + { + testkit.Rows(""), + }, + { + testkit.Rows("1 1 0"), + }, + { + testkit.Rows(), + }, + { + testkit.Rows(), + }, + } + for i, tt := range tests { + for _, hint := range hints { + sql := fmt.Sprintf("select %s %s", hint, tt.sql) + result := tk.MustQuery(sql) + result.Check(results[i].result) + } + } + + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int)") + tk.MustExec("create table t2(a int)") + tk.MustExec("insert into t1 values(1),(2)") + tk.MustExec("insert into t2 values(1),(null)") + tk.MustQuery("select * from t1 where a not in (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( + "2", + )) + tk.MustQuery("select * from t1 where a != all (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( + "2", + )) + tk.MustQuery("select * from t1 where a <> all (select a from t2 where t1.a = t2.a)").Check(testkit.Rows( + "2", + )) +} + +func TestIssue18070(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, index(a))") + tk.MustExec("create table t2(a int, index(a))") + tk.MustExec("insert into t1 values(1),(2)") + tk.MustExec("insert into t2 values(1),(1),(2),(2)") + tk.MustExec("set @@tidb_mem_quota_query=1000") + tk.MustContainErrMsg("select /*+ inl_hash_join(t1)*/ * from t1 join t2 on t1.a = t2.a;", memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) + + fpName := "github.com/pingcap/tidb/pkg/executor/mockIndexMergeJoinOOMPanic" + require.NoError(t, failpoint.Enable(fpName, `panic("ERROR 1105 (HY000): Out Of Memory Quota![conn=1]")`)) + defer func() { + require.NoError(t, failpoint.Disable(fpName)) + }() + tk.MustContainErrMsg("select /*+ inl_merge_join(t1)*/ * from t1 join t2 on t1.a = t2.a;", memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery) +} + +func TestIssue20779(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int, b int, index idx(b));") + tk.MustExec("insert into t1 values(1, 1);") + tk.MustExec("insert into t1 select * from t1;") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIssue20779", "return")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIssue20779")) + }() + + rs, err := tk.Exec("select /*+ inl_hash_join(t2) */ t1.b from t1 left join t1 t2 on t1.b=t2.b order by t1.b;") + require.NoError(t, err) + _, err = session.GetRows4Test(context.Background(), nil, rs) + require.EqualError(t, err, "testIssue20779") + require.NoError(t, rs.Close()) +} + +func TestIssue30211(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1(a int, index(a));") + tk.MustExec("create table t2(a int, index(a));") + func() { + fpName := "github.com/pingcap/tidb/pkg/executor/TestIssue30211" + require.NoError(t, failpoint.Enable(fpName, `panic("TestIssue30211 IndexJoinPanic")`)) + defer func() { + require.NoError(t, failpoint.Disable(fpName)) + }() + err := tk.QueryToErr("select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.a;") + require.EqualError(t, err, "failpoint panic: TestIssue30211 IndexJoinPanic") + + err = tk.QueryToErr("select /*+ inl_hash_join(t1) */ * from t1 join t2 on t1.a = t2.a;") + require.EqualError(t, err, "failpoint panic: TestIssue30211 IndexJoinPanic") + }() + tk.MustExec("insert into t1 values(1),(2);") + tk.MustExec("insert into t2 values(1),(1),(2),(2);") + tk.MustExec("set @@tidb_mem_quota_query=8000;") + tk.MustExec("set tidb_index_join_batch_size = 1;") + tk.MustExec("SET GLOBAL tidb_mem_oom_action = 'CANCEL'") + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action='LOG'") + err := tk.QueryToErr("select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.a;").Error() + require.True(t, strings.Contains(err, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery)) + err = tk.QueryToErr("select /*+ inl_hash_join(t1) */ * from t1 join t2 on t1.a = t2.a;").Error() + require.True(t, strings.Contains(err, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery)) +} + +func TestIssue37932(t *testing.T) { + store := testkit.CreateMockStore(t) + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + tk1.MustExec("create table tbl_1 ( col_1 set ( 'Alice','Bob','Charlie','David' ) not null default 'Alice' ,col_2 tinyint unsigned ,col_3 decimal ( 34 , 3 ) not null default 79 ,col_4 bigint unsigned not null ,col_5 bit ( 12 ) not null , unique key idx_1 ( col_2 ) ,unique key idx_2 ( col_2 ) ) charset utf8mb4 collate utf8mb4_bin ;") + tk1.MustExec("create table tbl_2 ( col_6 text ( 52 ) collate utf8_unicode_ci not null ,col_7 int unsigned not null ,col_8 blob ( 369 ) ,col_9 bit ( 51 ) ,col_10 decimal ( 38 , 16 ) , unique key idx_3 ( col_7 ) ,unique key idx_4 ( col_7 ) ) charset utf8 collate utf8_unicode_ci ;") + tk1.MustExec("create table tbl_3 ( col_11 set ( 'Alice','Bob','Charlie','David' ) not null ,col_12 bigint unsigned not null default 1678891638492596595 ,col_13 text ( 18 ) ,col_14 set ( 'Alice','Bob','Charlie','David' ) not null default 'Alice' ,col_15 mediumint , key idx_5 ( col_12 ) ,unique key idx_6 ( col_12 ) ) charset utf8mb4 collate utf8mb4_general_ci ;") + tk1.MustExec("create table tbl_4 ( col_16 set ( 'Alice','Bob','Charlie','David' ) not null ,col_17 tinyint unsigned ,col_18 int unsigned not null default 4279145838 ,col_19 varbinary ( 210 ) not null ,col_20 timestamp , primary key ( col_18 ) /*T![clustered_index] nonclustered */ ,key idx_8 ( col_19 ) ) charset utf8mb4 collate utf8mb4_unicode_ci ;") + tk1.MustExec("create table tbl_5 ( col_21 bigint ,col_22 set ( 'Alice','Bob','Charlie','David' ) ,col_23 blob ( 311 ) ,col_24 bigint unsigned not null default 3415443099312152509 ,col_25 time , unique key idx_9 ( col_21 ) ,unique key idx_10 ( col_21 ) ) charset gbk collate gbk_bin ;") + tk1.MustExec("insert into tbl_1 values ( 'Bob',null,0.04,2650749963804575036,4044 );") + tk1.MustExec("insert into tbl_1 values ( 'Alice',171,1838.2,6452757231340518222,1190 );") + tk1.MustExec("insert into tbl_1 values ( 'Bob',202,2.962,4304284252076747481,2112 );") + tk1.MustExec("insert into tbl_1 values ( 'David',155,32610.05,5899651588546531414,104 );") + tk1.MustExec("insert into tbl_1 values ( 'Charlie',52,4219.7,6151233689319516187,1246 );") + tk1.MustExec("insert into tbl_1 values ( 'Bob',55,3963.11,3614977408465893392,1188 );") + tk1.MustExec("insert into tbl_1 values ( 'Alice',203,72.01,1553550133494908281,1658 );") + tk1.MustExec("insert into tbl_1 values ( 'Bob',40,871.569,8114062926218465773,1397 );") + tk1.MustExec("insert into tbl_1 values ( 'Alice',165,7765,4481202107781982005,2089 );") + tk1.MustExec("insert into tbl_1 values ( 'David',79,7.02,993594504887208796,514 );") + tk1.MustExec("insert into tbl_2 values ( 'iB_%7c&q!6-gY4bkvg',2064909882,'dLN52t1YZSdJ',2251679806445488,32 );") + tk1.MustExec("insert into tbl_2 values ( 'h_',1478443689,'EqP+iN=',180492371752598,0.1 );") + tk1.MustExec("insert into tbl_2 values ( 'U@U&*WKfPzil=6YaDxp',4271201457,'QWuo24qkSSo',823931105457505,88514 );") + tk1.MustExec("insert into tbl_2 values ( 'FR4GA=',505128825,'RpEmV6ph5Z7',568030123046798,609381 );") + tk1.MustExec("insert into tbl_2 values ( '3GsU',166660047,'',1061132816887762,6.4605 );") + tk1.MustExec("insert into tbl_2 values ( 'BA4hPRD0lm*pbg#NE',3440634757,'7gUPe2',288001159469205,6664.9 );") + tk1.MustExec("insert into tbl_2 values ( '+z',2117152318,'WTkD(N',215697667226264,7.88 );") + tk1.MustExec("insert into tbl_2 values ( 'x@SPhy9lOomPa4LF',2881759652,'ETUXQQ0b4HnBSKgTWIU',153379720424625,null );") + tk1.MustExec("insert into tbl_2 values ( '',2075177391,'MPae!9%ufd',115899580476733,341.23 );") + tk1.MustExec("insert into tbl_2 values ( '~udi',1839363347,'iQj$$YsZc5ULTxG)yH',111454353417190,6.6 );") + tk1.MustExec("insert into tbl_3 values ( 'Alice',7032411265967085555,'P7*KBZ159','Alice',7516989 );") + tk1.MustExec("insert into tbl_3 values ( 'David',486417871670147038,'','Charlie',-2135446 );") + tk1.MustExec("insert into tbl_3 values ( 'Charlie',5784081664185069254,'7V_&YzKM~Q','Charlie',5583839 );") + tk1.MustExec("insert into tbl_3 values ( 'David',6346366522897598558,')Lp&$2)SC@','Bob',2522913 );") + tk1.MustExec("insert into tbl_3 values ( 'Charlie',224922711063053272,'gY','David',6624398 );") + tk1.MustExec("insert into tbl_3 values ( 'Alice',4678579167560495958,'fPIXY%R8WyY(=u&O','David',-3267160 );") + tk1.MustExec("insert into tbl_3 values ( 'David',8817108026311573677,'Cs0dZW*SPnKhV1','Alice',2359718 );") + tk1.MustExec("insert into tbl_3 values ( 'Bob',3177426155683033662,'o2=@zv2qQDhKUs)4y','Bob',-8091802 );") + tk1.MustExec("insert into tbl_3 values ( 'Bob',2543586640437235142,'hDa*CsOUzxmjf2m','Charlie',-8091935 );") + tk1.MustExec("insert into tbl_3 values ( 'Charlie',6204182067887668945,'DX-!=)dbGPQO','David',-1954600 );") + tk1.MustExec("insert into tbl_4 values ( 'David',167,576262750,'lX&x04W','2035-09-28' );") + tk1.MustExec("insert into tbl_4 values ( 'Charlie',236,2637776757,'92OhsL!w%7','2036-02-08' );") + tk1.MustExec("insert into tbl_4 values ( 'Bob',68,1077999933,'M0l','1997-09-16' );") + tk1.MustExec("insert into tbl_4 values ( 'Charlie',184,1280264753,'FhjkfeXsK1Q(','2030-03-16' );") + tk1.MustExec("insert into tbl_4 values ( 'Alice',10,2150711295,'Eqip)^tr*MoL','2032-07-02' );") + tk1.MustExec("insert into tbl_4 values ( 'Bob',108,2421602476,'Eul~~Df_Q8s&I3Y-7','2019-06-10' );") + tk1.MustExec("insert into tbl_4 values ( 'Alice',36,2811198561,'%XgRou0#iKtn*','2022-06-13' );") + tk1.MustExec("insert into tbl_4 values ( 'Charlie',115,330972286,'hKeJS','2000-11-15' );") + tk1.MustExec("insert into tbl_4 values ( 'Alice',6,2958326555,'c6+=1','2001-02-11' );") + tk1.MustExec("insert into tbl_4 values ( 'Alice',99,387404826,'figc(@9R*k3!QM_Vve','2036-02-17' );") + tk1.MustExec("insert into tbl_5 values ( -401358236474313609,'Charlie','4J$',701059766304691317,'08:19:10.00' );") + tk1.MustExec("insert into tbl_5 values ( 2759837898825557143,'Bob','E',5158554038674310466,'11:04:03.00' );") + tk1.MustExec("insert into tbl_5 values ( 273910054423832204,'Alice',null,8944547065167499612,'08:02:30.00' );") + tk1.MustExec("insert into tbl_5 values ( 2875669873527090798,'Alice','4^SpR84',4072881341903432150,'18:24:55.00' );") + tk1.MustExec("insert into tbl_5 values ( -8446590100588981557,'David','yBj8',8760380566452862549,'09:01:10.00' );") + tk1.MustExec("insert into tbl_5 values ( -1075861460175889441,'Charlie','ti11Pl0lJ',9139997565676405627,'08:30:14.00' );") + tk1.MustExec("insert into tbl_5 values ( 95663565223131772,'Alice','6$',8467839300407531400,'23:31:42.00' );") + tk1.MustExec("insert into tbl_5 values ( -5661709703968335255,'Charlie','',8122758569495329946,'19:36:24.00' );") + tk1.MustExec("insert into tbl_5 values ( 3338588216091909518,'Bob','',6558557574025196860,'15:22:56.00' );") + tk1.MustExec("insert into tbl_5 values ( 8918630521194612922,'David','I$w',5981981639362947650,'22:03:24.00' );") + tk1.MustExec("begin pessimistic;") + tk1.MustExec("insert ignore into tbl_1 set col_1 = 'David', col_2 = 110, col_3 = 37065, col_4 = 8164500960513474805, col_5 = 1264 on duplicate key update col_3 = 22151.5, col_4 = 6266058887081523571, col_5 = 3254, col_2 = 59, col_1 = 'Bob';") + tk1.MustExec("insert into tbl_4 (col_16,col_17,col_18,col_19,col_20) values ( 'Charlie',34,2499970462,'Z','1978-10-27' ) ,( 'David',217,1732485689,'*)~@@Q8ryi','2004-12-01' ) ,( 'Charlie',40,1360558255,'H(Y','1998-06-25' ) ,( 'Alice',108,2973455447,'%CcP4$','1979-03-28' ) ,( 'David',9,3835209932,'tdKXUzLmAzwFf$','2009-03-03' ) ,( 'David',68,163270003,'uimsclz@FQJN','1988-09-11' ) ,( 'Alice',76,297067264,'BzFF','1989-01-05' ) on duplicate key update col_16 = 'Charlie', col_17 = 14, col_18 = 4062155275, col_20 = '2002-03-07', col_19 = 'tmvchLzp*o8';") + tk2.MustExec("delete from tbl_3 where tbl_3.col_13 in ( null ,'' ,'g8EEzUU7LQ' ,'~fC3&B*cnOOx_' ,'%RF~AFto&x' ,'NlWkMWG^00' ,'e^4o2Ji^q_*Fa52Z' ) ;") + tk2.MustExec("delete from tbl_5 where not( tbl_5.col_21 between -1075861460175889441 and 3338588216091909518 ) ;") + tk1.MustExec("replace into tbl_1 (col_1,col_2,col_3,col_4,col_5) values ( 'Alice',83,8.33,4070808626051569664,455 ) ,( 'Alice',53,2.8,2763362085715461014,1912 ) ,( 'David',178,4242.8,962727993466011464,1844 ) ,( 'Alice',16,650054,5638988670318229867,565 ) ,( 'Alice',76,89783.1,3968605744540056024,2563 ) ,( 'Bob',120,0.89,1003144931151245839,2670 );") + tk1.MustExec("delete from tbl_5 where col_24 is null ;") + tk1.MustExec("delete from tbl_3 where tbl_3.col_11 in ( 'Alice' ,'Bob' ,'Alice' ) ;") + tk2.MustExec("insert into tbl_3 set col_11 = 'Bob', col_12 = 5701982550256146475, col_13 = 'Hhl)yCsQ2K3cfc^', col_14 = 'Alice', col_15 = -3718868 on duplicate key update col_15 = 7210750, col_12 = 6133680876296985245, col_14 = 'Alice', col_11 = 'David', col_13 = 'F+RMGE!_2^Cfr3Fw';") + tk2.MustExec("insert ignore into tbl_5 set col_21 = 2439343116426563397, col_22 = 'Charlie', col_23 = '~Spa2YzRFFom16XD', col_24 = 5571575017340582365, col_25 = '13:24:38.00' ;") + err := tk1.ExecToErr("update tbl_4 set tbl_4.col_20 = '2006-01-24' where tbl_4.col_18 in ( select col_11 from tbl_3 where IsNull( tbl_4.col_16 ) or not( tbl_4.col_19 in ( select col_3 from tbl_1 where tbl_4.col_16 between 'Alice' and 'David' and tbl_4.col_19 <= '%XgRou0#iKtn*' ) ) ) ;") + if err != nil { + print(err.Error()) + if strings.Contains(err.Error(), "Truncated incorrect DOUBLE value") { + t.Log("Truncated incorrect DOUBLE value is within expectations, skipping") + return + } + } + require.NoError(t, err) +} diff --git a/pkg/executor/test/jointest/main_test.go b/pkg/executor/test/jointest/main_test.go new file mode 100644 index 0000000000000..d8d63318a83e1 --- /dev/null +++ b/pkg/executor/test/jointest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package jointest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/kvtest/BUILD.bazel b/pkg/executor/test/kvtest/BUILD.bazel new file mode 100644 index 0000000000000..f8a86322d1842 --- /dev/null +++ b/pkg/executor/test/kvtest/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "kvtest_test", + timeout = "short", + srcs = [ + "kv_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/testkit", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/kvtest/kv_test.go b/pkg/executor/test/kvtest/kv_test.go similarity index 98% rename from executor/test/kvtest/kv_test.go rename to pkg/executor/test/kvtest/kv_test.go index 7c45fccc73a4c..8090700d4c062 100644 --- a/executor/test/kvtest/kv_test.go +++ b/pkg/executor/test/kvtest/kv_test.go @@ -17,7 +17,7 @@ package kvtest import ( "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/kvtest/main_test.go b/pkg/executor/test/kvtest/main_test.go new file mode 100644 index 0000000000000..7f1075ebdfb50 --- /dev/null +++ b/pkg/executor/test/kvtest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package kvtest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/loaddatatest/BUILD.bazel b/pkg/executor/test/loaddatatest/BUILD.bazel new file mode 100644 index 0000000000000..4f0f27f54363e --- /dev/null +++ b/pkg/executor/test/loaddatatest/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "loaddatatest_test", + timeout = "short", + srcs = [ + "load_data_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 10, + deps = [ + "//br/pkg/lightning/mydump", + "//pkg/config", + "//pkg/executor", + "//pkg/meta/autoid", + "//pkg/sessionctx", + "//pkg/testkit", + "//pkg/util/dbterror/exeerrors", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/loaddatatest/load_data_test.go b/pkg/executor/test/loaddatatest/load_data_test.go similarity index 99% rename from executor/test/loaddatatest/load_data_test.go rename to pkg/executor/test/loaddatatest/load_data_test.go index 9336457e11fa6..19cbb874e3d58 100644 --- a/executor/test/loaddatatest/load_data_test.go +++ b/pkg/executor/test/loaddatatest/load_data_test.go @@ -19,10 +19,10 @@ import ( "testing" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/loaddatatest/main_test.go b/pkg/executor/test/loaddatatest/main_test.go new file mode 100644 index 0000000000000..3faf8fd44e2c4 --- /dev/null +++ b/pkg/executor/test/loaddatatest/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package loaddatatest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.Cleanup(func(_ int) { + view.Stop() + }), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/loadremotetest/BUILD.bazel b/pkg/executor/test/loadremotetest/BUILD.bazel new file mode 100644 index 0000000000000..964522e3190db --- /dev/null +++ b/pkg/executor/test/loadremotetest/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "loadremotetest_test", + timeout = "short", + srcs = [ + "error_test.go", + "main_test.go", + "multi_file_test.go", + "one_csv_test.go", + "util_test.go", + ], + flaky = True, + deps = [ + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/testkit", + "@com_github_fsouza_fake_gcs_server//fakestorage", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@com_github_stretchr_testify//suite", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/loadremotetest/error_test.go b/pkg/executor/test/loadremotetest/error_test.go new file mode 100644 index 0000000000000..1c542e47222f2 --- /dev/null +++ b/pkg/executor/test/loadremotetest/error_test.go @@ -0,0 +1,308 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package loadremotetest + +import ( + "fmt" + "testing" + + "github.com/fsouza/fake-gcs-server/fakestorage" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func checkClientErrorMessage(t *testing.T, err error, msg string) { + require.Error(t, err) + cause := errors.Cause(err) + terr, ok := cause.(*errors.Error) + require.True(t, ok, "%T", cause) + require.Contains(t, terror.ToSQLError(terr).Error(), msg) +} + +func (s *mockGCSSuite) TestErrorMessage() { + s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") + + err := s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t") + checkClientErrorMessage(s.T(), err, "ERROR 1046 (3D000): No database selected") + err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE wrongdb.t") + checkClientErrorMessage(s.T(), err, "ERROR 1146 (42S02): Table 'wrongdb.t' doesn't exist") + + s.tk.MustExec("CREATE DATABASE load_csv;") + s.tk.MustExec("USE load_csv;") + s.tk.MustExec("CREATE TABLE t (i INT PRIMARY KEY, s varchar(32));") + + err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (wrong)") + checkClientErrorMessage(s.T(), err, "ERROR 1054 (42S22): Unknown column 'wrong' in 'field list'") + // This behaviour is different from MySQL + err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (i,i)") + checkClientErrorMessage(s.T(), err, "ERROR 1110 (42000): Column 'i' specified twice") + err = s.tk.ExecToErr("LOAD DATA INFILE 'gs://1' INTO TABLE t (@v) SET wrong=@v") + checkClientErrorMessage(s.T(), err, "ERROR 1054 (42S22): Unknown column 'wrong' in 'field list'") + err = s.tk.ExecToErr("LOAD DATA INFILE 'abc://1' INTO TABLE t;") + checkClientErrorMessage(s.T(), err, + "ERROR 8158 (HY000): The URI of data source is invalid. Reason: storage abc not support yet. Please provide a valid URI, such as 's3://import/test.csv?access_key_id={your_access_key_id ID}&secret_access_key={your_secret_access_key}&session_token={your_session_token}'") + err = s.tk.ExecToErr("LOAD DATA INFILE 's3://no-network' INTO TABLE t;") + checkClientErrorMessage(s.T(), err, + "ERROR 8159 (HY000): Access to the data source has been denied. Reason: failed to get region of bucket no-network. Please check the URI, access key and secret access key are correct") + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://wrong-bucket/p?endpoint=%s' + INTO TABLE t;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 8160 (HY000): Failed to read source files. Reason: the object doesn't exist, file info: input.bucket='wrong-bucket', input.key='p'. Please check the file location is correct") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "t.tsv", + }, + Content: []byte("1\t2\n" + + "1\t4\n"), + }) + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t.tsv?endpoint=%s' + INTO TABLE t LINES STARTING BY '\n';`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + `ERROR 8162 (HY000): STARTING BY ' +' cannot contain LINES TERMINATED BY ' +'`) +} + +func (s *mockGCSSuite) TestColumnNumMismatch() { + s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "t2.tsv", + }, + Content: []byte("1\t2\n" + + "1\t4\n"), + }) + + s.tk.MustExec("CREATE DATABASE load_csv;") + s.tk.MustExec("USE load_csv;") + + // table has fewer columns than data + + s.tk.MustExec("CREATE TABLE t (c INT);") + s.tk.MustExec("SET SESSION sql_mode = ''") + err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + INTO TABLE t;`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1262 Row 1 was truncated; it contained more data than there were input columns", + "Warning 1262 Row 2 was truncated; it contained more data than there were input columns")) + + s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + INTO TABLE t;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1262 (01000): Row 1 was truncated; it contained more data than there were input columns") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + REPLACE INTO TABLE t;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1262 (01000): Row 1 was truncated; it contained more data than there were input columns") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + IGNORE INTO TABLE t;`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1262 Row 1 was truncated; it contained more data than there were input columns", + "Warning 1262 Row 2 was truncated; it contained more data than there were input columns")) + + // table has more columns than data + + s.tk.MustExec("CREATE TABLE t2 (c1 INT, c2 INT, c3 INT);") + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + INTO TABLE t2;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1261 (01000): Row 1 doesn't contain data for all columns") + + // fill default value for missing columns + + s.tk.MustExec(`CREATE TABLE t3 ( + c1 INT NOT NULL, + c2 INT NOT NULL, + c3 INT NOT NULL DEFAULT 1);`) + s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t2.tsv?endpoint=%s' + INTO TABLE t3 (c1, c2);`, gcsEndpoint)) + s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( + "1 2 1", + "1 4 1")) +} + +func (s *mockGCSSuite) TestEvalError() { + s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "t3.tsv", + }, + Content: []byte("1\t2\n" + + "1\t4\n"), + }) + + s.tk.MustExec("CREATE DATABASE load_csv;") + s.tk.MustExec("USE load_csv;") + + s.tk.MustExec("CREATE TABLE t (c INT, c2 INT UNIQUE);") + s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' + INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'asd'") + + // REPLACE does not help + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' + REPLACE INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1292 (22007): Truncated incorrect DOUBLE value: 'asd'") + + // IGNORE helps + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t3.tsv?endpoint=%s' + IGNORE INTO TABLE t (@v1, c2) SET c=@v1+'asd';`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1292 Truncated incorrect DOUBLE value: 'asd'", + "Warning 1292 Truncated incorrect DOUBLE value: 'asd'")) + s.tk.MustQuery("SELECT * FROM t;").Check(testkit.Rows( + "1 2", + "1 4")) +} + +func (s *mockGCSSuite) TestDataError() { + s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "null.tsv", + }, + Content: []byte("1\t\\N\n" + + "1\t4\n"), + }) + + s.tk.MustExec("CREATE DATABASE load_csv;") + s.tk.MustExec("USE load_csv;") + + s.tk.MustExec("CREATE TABLE t (c INT NOT NULL, c2 INT NOT NULL);") + s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/null.tsv?endpoint=%s' + INTO TABLE t;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, + "ERROR 1263 (22004): Column set to default value; NULL supplied to NOT NULL column 'c2' at row 1") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/null.tsv?endpoint=%s' + IGNORE INTO TABLE t;`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 1", s.tk.Session().LastMessage()) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1263 Column set to default value; NULL supplied to NOT NULL column 'c2' at row 1")) + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "t4.tsv", + }, + Content: []byte("1\t2\n" + + "1\t2\n"), + }) + + s.tk.MustExec("CREATE TABLE t2 (c INT PRIMARY KEY, c2 INT NOT NULL);") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t4.tsv?endpoint=%s' + INTO TABLE t2;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, "ERROR 1062 (23000): Duplicate entry '1' for key 't2.PRIMARY'") + + s.tk.MustExec("CREATE TABLE t3 (c INT, c2 INT UNIQUE);") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t4.tsv?endpoint=%s' + INTO TABLE t3;`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, "ERROR 1062 (23000): Duplicate entry '2' for key 't3.c2'") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-tsv", + Name: "t5.tsv", + }, + Content: []byte("1\t100\n" + + "2\t100\n"), + }) + s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t5.tsv?endpoint=%s' + REPLACE INTO TABLE t3;`, gcsEndpoint)) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows()) + s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( + "2 100")) + + s.tk.MustExec("UPDATE t3 SET c = 3;") + s.tk.MustExec(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-tsv/t5.tsv?endpoint=%s' + IGNORE INTO TABLE t3;`, gcsEndpoint)) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1062 Duplicate entry '100' for key 't3.c2'", + "Warning 1062 Duplicate entry '100' for key 't3.c2'")) + s.tk.MustQuery("SELECT * FROM t3;").Check(testkit.Rows( + "3 100")) +} + +func (s *mockGCSSuite) TestIssue43555() { + s.tk.MustExec("DROP DATABASE IF EXISTS load_csv;") + + s.server.CreateObject(fakestorage.Object{ + ObjectAttrs: fakestorage.ObjectAttrs{ + BucketName: "test-csv", + Name: "43555.csv", + }, + Content: []byte("6\n" + + "7.1\n"), + }) + + s.tk.MustExec("CREATE DATABASE load_csv;") + s.tk.MustExec("USE load_csv;") + + s.tk.MustExec("CREATE TABLE t (id CHAR(1), id1 INT);") + s.tk.MustExec("SET SESSION sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + + err := s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' + IGNORE INTO TABLE t;`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 3", s.tk.Session().LastMessage()) + + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1261 Row 1 doesn't contain data for all columns", + "Warning 1261 Row 2 doesn't contain data for all columns", + "Warning 1265 Data truncated for column 'id' at row 2")) + s.tk.MustQuery("SELECT * FROM t;").Check(testkit.Rows( + "6 ", + "7 ")) + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' + INTO TABLE t (id);`, gcsEndpoint)) + checkClientErrorMessage(s.T(), err, "ERROR 1265 (01000): Data truncated for column 'id' at row 2") + + err = s.tk.ExecToErr(fmt.Sprintf(`LOAD DATA INFILE 'gs://test-csv/43555.csv?endpoint=%s' + IGNORE INTO TABLE t (id1) SET id='7.1';`, gcsEndpoint)) + require.NoError(s.T(), err) + require.Equal(s.T(), "Records: 2 Deleted: 0 Skipped: 0 Warnings: 2", s.tk.Session().LastMessage()) + s.tk.MustQuery("SHOW WARNINGS;").Check(testkit.Rows( + "Warning 1265 Data truncated for column 'id' at row 1", + "Warning 1265 Data truncated for column 'id' at row 2")) +} diff --git a/executor/test/loadremotetest/main_test.go b/pkg/executor/test/loadremotetest/main_test.go similarity index 100% rename from executor/test/loadremotetest/main_test.go rename to pkg/executor/test/loadremotetest/main_test.go diff --git a/executor/test/loadremotetest/multi_file_test.go b/pkg/executor/test/loadremotetest/multi_file_test.go similarity index 99% rename from executor/test/loadremotetest/multi_file_test.go rename to pkg/executor/test/loadremotetest/multi_file_test.go index ab80a199a843e..72a77f5ecf753 100644 --- a/executor/test/loadremotetest/multi_file_test.go +++ b/pkg/executor/test/loadremotetest/multi_file_test.go @@ -21,7 +21,7 @@ import ( "strconv" "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/executor/test/loadremotetest/one_csv_test.go b/pkg/executor/test/loadremotetest/one_csv_test.go similarity index 99% rename from executor/test/loadremotetest/one_csv_test.go rename to pkg/executor/test/loadremotetest/one_csv_test.go index 2f7e6b020b007..4a021ecebbe56 100644 --- a/executor/test/loadremotetest/one_csv_test.go +++ b/pkg/executor/test/loadremotetest/one_csv_test.go @@ -18,7 +18,7 @@ import ( "fmt" "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/loadremotetest/util_test.go b/pkg/executor/test/loadremotetest/util_test.go new file mode 100644 index 0000000000000..ff90be2b3d0ca --- /dev/null +++ b/pkg/executor/test/loadremotetest/util_test.go @@ -0,0 +1,64 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package loadremotetest + +import ( + "fmt" + "testing" + + "github.com/fsouza/fake-gcs-server/fakestorage" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/suite" +) + +type mockGCSSuite struct { + suite.Suite + + server *fakestorage.Server + store kv.Storage + tk *testkit.TestKit +} + +var ( + gcsHost = "127.0.0.1" + gcsPort = uint16(4443) + // for fake gcs server, we must use this endpoint format + // NOTE: must end with '/' + gcsEndpointFormat = "http://%s:%d/storage/v1/" + gcsEndpoint = fmt.Sprintf(gcsEndpointFormat, gcsHost, gcsPort) +) + +func TestLoadRemote(t *testing.T) { + suite.Run(t, &mockGCSSuite{}) +} + +func (s *mockGCSSuite) SetupSuite() { + var err error + opt := fakestorage.Options{ + Scheme: "http", + Host: gcsHost, + Port: gcsPort, + PublicHost: gcsHost, + } + s.server, err = fakestorage.NewServerWithOptions(opt) + s.Require().NoError(err) + s.store = testkit.CreateMockStore(s.T()) + s.tk = testkit.NewTestKit(s.T(), s.store) +} + +func (s *mockGCSSuite) TearDownSuite() { + s.server.Stop() +} diff --git a/pkg/executor/test/memtest/BUILD.bazel b/pkg/executor/test/memtest/BUILD.bazel new file mode 100644 index 0000000000000..ccd1db7843cc7 --- /dev/null +++ b/pkg/executor/test/memtest/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "memtest_test", + timeout = "short", + srcs = [ + "main_test.go", + "mem_test.go", + ], + flaky = True, + race = "on", + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/testkit", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/memtest/main_test.go b/pkg/executor/test/memtest/main_test.go new file mode 100644 index 0000000000000..2e8510e089f7e --- /dev/null +++ b/pkg/executor/test/memtest/main_test.go @@ -0,0 +1,45 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package memtest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/memtest/mem_test.go b/pkg/executor/test/memtest/mem_test.go similarity index 97% rename from executor/test/memtest/mem_test.go rename to pkg/executor/test/memtest/mem_test.go index 43cb738f07997..0134ebceaa985 100644 --- a/executor/test/memtest/mem_test.go +++ b/pkg/executor/test/memtest/mem_test.go @@ -17,7 +17,7 @@ package memtest import ( "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/oomtest/BUILD.bazel b/pkg/executor/test/oomtest/BUILD.bazel new file mode 100644 index 0000000000000..60916b8b81d27 --- /dev/null +++ b/pkg/executor/test/oomtest/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "oomtest_test", + timeout = "short", + srcs = ["oom_test.go"], + flaky = True, + race = "on", + shard_count = 3, + deps = [ + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/set", + "//pkg/util/syncutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) diff --git a/executor/test/oomtest/oom_test.go b/pkg/executor/test/oomtest/oom_test.go similarity index 96% rename from executor/test/oomtest/oom_test.go rename to pkg/executor/test/oomtest/oom_test.go index 50677dfa03189..1df45756c7446 100644 --- a/executor/test/oomtest/oom_test.go +++ b/pkg/executor/test/oomtest/oom_test.go @@ -23,10 +23,10 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/stretchr/testify/require" "go.uber.org/goleak" "go.uber.org/zap" @@ -195,9 +195,9 @@ func TestMemTracker4DeleteExec(t *testing.T) { oom.SetTracker("") oom.AddMessageFilter("memory exceeds quota, rateLimitAction delegate to fallback action") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/disableFixedRowCountHint", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/disableFixedRowCountHint", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/disableFixedRowCountHint")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/disableFixedRowCountHint")) }() tk.Session().GetSessionVars().EnabledRateLimitAction = true tk.Session().GetSessionVars().MemQuotaQuery = 10000 diff --git a/pkg/executor/test/partitiontest/BUILD.bazel b/pkg/executor/test/partitiontest/BUILD.bazel new file mode 100644 index 0000000000000..1f2f0adfeeb71 --- /dev/null +++ b/pkg/executor/test/partitiontest/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "partitiontest_test", + timeout = "short", + srcs = [ + "main_test.go", + "partition_test.go", + ], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/testkit", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + ], +) diff --git a/executor/test/partitiontest/main_test.go b/pkg/executor/test/partitiontest/main_test.go similarity index 100% rename from executor/test/partitiontest/main_test.go rename to pkg/executor/test/partitiontest/main_test.go diff --git a/pkg/executor/test/partitiontest/partition_test.go b/pkg/executor/test/partitiontest/partition_test.go new file mode 100644 index 0000000000000..28e9002060e27 --- /dev/null +++ b/pkg/executor/test/partitiontest/partition_test.go @@ -0,0 +1,503 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package partitiontest + +import ( + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestPartitionedTableReplace(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + testSQL := `drop table if exists replace_test; + create table replace_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1) + partition by range (id) ( + PARTITION p0 VALUES LESS THAN (3), + PARTITION p1 VALUES LESS THAN (5), + PARTITION p2 VALUES LESS THAN (7), + PARTITION p3 VALUES LESS THAN (9));` + tk.MustExec(testSQL) + testSQL = `replace replace_test (c1) values (1),(2),(NULL);` + tk.MustExec(testSQL) + require.Equal(t, tk.Session().LastMessage(), "Records: 3 Duplicates: 0 Warnings: 0") + + errReplaceSQL := `replace replace_test (c1) values ();` + tk.MustExec("begin") + err := tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test (c1, c2) values (1,2),(1);` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test (xxx) values (3);` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test_xxx (c1) values ();` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + replaceSetSQL := `replace replace_test set c1 = 3;` + tk.MustExec(replaceSetSQL) + require.Empty(t, tk.Session().LastMessage()) + + errReplaceSetSQL := `replace replace_test set c1 = 4, c1 = 5;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSetSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSetSQL = `replace replace_test set xxx = 6;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSetSQL) + require.Error(t, err) + tk.MustExec("rollback") + + tk.MustExec(`drop table if exists replace_test_1`) + tk.MustExec(`create table replace_test_1 (id int, c1 int) partition by range (id) ( + PARTITION p0 VALUES LESS THAN (4), + PARTITION p1 VALUES LESS THAN (6), + PARTITION p2 VALUES LESS THAN (8), + PARTITION p3 VALUES LESS THAN (10), + PARTITION p4 VALUES LESS THAN (100))`) + tk.MustExec(`replace replace_test_1 select id, c1 from replace_test;`) + require.Equal(t, tk.Session().LastMessage(), "Records: 4 Duplicates: 0 Warnings: 0") + + tk.MustExec(`drop table if exists replace_test_2`) + tk.MustExec(`create table replace_test_2 (id int, c1 int) partition by range (id) ( + PARTITION p0 VALUES LESS THAN (10), + PARTITION p1 VALUES LESS THAN (50), + PARTITION p2 VALUES LESS THAN (100), + PARTITION p3 VALUES LESS THAN (300))`) + tk.MustExec(`replace replace_test_1 select id, c1 from replace_test union select id * 10, c1 * 10 from replace_test;`) + require.Equal(t, tk.Session().LastMessage(), "Records: 8 Duplicates: 0 Warnings: 0") + + errReplaceSelectSQL := `replace replace_test_1 select c1 from replace_test;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSelectSQL) + require.Error(t, err) + tk.MustExec("rollback") + + tk.MustExec(`drop table if exists replace_test_3`) + replaceUniqueIndexSQL := `create table replace_test_3 (c1 int, c2 int, UNIQUE INDEX (c2)) partition by range (c2) ( + PARTITION p0 VALUES LESS THAN (4), + PARTITION p1 VALUES LESS THAN (7), + PARTITION p2 VALUES LESS THAN (11))` + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_3 set c2=8;` + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_3 set c2=8;` + tk.MustExec(replaceUniqueIndexSQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + require.Empty(t, tk.Session().LastMessage()) + replaceUniqueIndexSQL = `replace into replace_test_3 set c1=8, c2=8;` + tk.MustExec(replaceUniqueIndexSQL) + require.Equal(t, int64(2), int64(tk.Session().AffectedRows())) + require.Empty(t, tk.Session().LastMessage()) + + replaceUniqueIndexSQL = `replace into replace_test_3 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_3 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + require.Empty(t, tk.Session().LastMessage()) + + replaceUniqueIndexSQL = `create table replace_test_4 (c1 int, c2 int, c3 int, UNIQUE INDEX (c1, c2)) partition by range (c1) ( + PARTITION p0 VALUES LESS THAN (4), + PARTITION p1 VALUES LESS THAN (7), + PARTITION p2 VALUES LESS THAN (11));` + tk.MustExec(`drop table if exists replace_test_4`) + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + + replacePrimaryKeySQL := `create table replace_test_5 (c1 int, c2 int, c3 int, PRIMARY KEY (c1, c2)) partition by range (c2) ( + PARTITION p0 VALUES LESS THAN (4), + PARTITION p1 VALUES LESS THAN (7), + PARTITION p2 VALUES LESS THAN (11));` + tk.MustExec(replacePrimaryKeySQL) + replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` + tk.MustExec(replacePrimaryKeySQL) + replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` + tk.MustExec(replacePrimaryKeySQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + + issue989SQL := `CREATE TABLE tIssue989 (a int, b int, KEY(a), UNIQUE KEY(b)) partition by range (b) ( + PARTITION p1 VALUES LESS THAN (100), + PARTITION p2 VALUES LESS THAN (200))` + tk.MustExec(issue989SQL) + issue989SQL = `insert into tIssue989 (a, b) values (1, 2);` + tk.MustExec(issue989SQL) + issue989SQL = `replace into tIssue989(a, b) values (111, 2);` + tk.MustExec(issue989SQL) + r := tk.MustQuery("select * from tIssue989;") + r.Check(testkit.Rows("111 2")) +} + +func TestHashPartitionedTableReplace(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_enable_table_partition = '1';") + tk.MustExec("drop table if exists replace_test;") + testSQL := `create table replace_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1) + partition by hash(id) partitions 4;` + tk.MustExec(testSQL) + + testSQL = `replace replace_test (c1) values (1),(2),(NULL);` + tk.MustExec(testSQL) + + errReplaceSQL := `replace replace_test (c1) values ();` + tk.MustExec("begin") + err := tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test (c1, c2) values (1,2),(1);` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test (xxx) values (3);` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSQL = `replace replace_test_xxx (c1) values ();` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSetSQL := `replace replace_test set c1 = 4, c1 = 5;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSetSQL) + require.Error(t, err) + tk.MustExec("rollback") + + errReplaceSetSQL = `replace replace_test set xxx = 6;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSetSQL) + require.Error(t, err) + tk.MustExec("rollback") + + tk.MustExec(`replace replace_test set c1 = 3;`) + tk.MustExec(`replace replace_test set c1 = 4;`) + tk.MustExec(`replace replace_test set c1 = 5;`) + tk.MustExec(`replace replace_test set c1 = 6;`) + tk.MustExec(`replace replace_test set c1 = 7;`) + + tk.MustExec(`drop table if exists replace_test_1`) + tk.MustExec(`create table replace_test_1 (id int, c1 int) partition by hash(id) partitions 5;`) + tk.MustExec(`replace replace_test_1 select id, c1 from replace_test;`) + + tk.MustExec(`drop table if exists replace_test_2`) + tk.MustExec(`create table replace_test_2 (id int, c1 int) partition by hash(id) partitions 6;`) + + tk.MustExec(`replace replace_test_1 select id, c1 from replace_test union select id * 10, c1 * 10 from replace_test;`) + + errReplaceSelectSQL := `replace replace_test_1 select c1 from replace_test;` + tk.MustExec("begin") + err = tk.ExecToErr(errReplaceSelectSQL) + require.Error(t, err) + tk.MustExec("rollback") + + tk.MustExec(`drop table if exists replace_test_3`) + replaceUniqueIndexSQL := `create table replace_test_3 (c1 int, c2 int, UNIQUE INDEX (c2)) partition by hash(c2) partitions 7;` + tk.MustExec(replaceUniqueIndexSQL) + + tk.MustExec(`replace into replace_test_3 set c2=8;`) + tk.MustExec(`replace into replace_test_3 set c2=8;`) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + tk.MustExec(`replace into replace_test_3 set c1=8, c2=8;`) + require.Equal(t, int64(2), int64(tk.Session().AffectedRows())) + + tk.MustExec(`replace into replace_test_3 set c2=NULL;`) + tk.MustExec(`replace into replace_test_3 set c2=NULL;`) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + + for i := 0; i < 100; i++ { + sql := fmt.Sprintf("replace into replace_test_3 set c2=%d;", i) + tk.MustExec(sql) + } + result := tk.MustQuery("select count(*) from replace_test_3") + result.Check(testkit.Rows("102")) + + replaceUniqueIndexSQL = `create table replace_test_4 (c1 int, c2 int, c3 int, UNIQUE INDEX (c1, c2)) partition by hash(c1) partitions 8;` + tk.MustExec(`drop table if exists replace_test_4`) + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + replaceUniqueIndexSQL = `replace into replace_test_4 set c2=NULL;` + tk.MustExec(replaceUniqueIndexSQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + + replacePrimaryKeySQL := `create table replace_test_5 (c1 int, c2 int, c3 int, PRIMARY KEY (c1, c2)) partition by hash (c2) partitions 9;` + tk.MustExec(replacePrimaryKeySQL) + replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` + tk.MustExec(replacePrimaryKeySQL) + replacePrimaryKeySQL = `replace into replace_test_5 set c1=1, c2=2;` + tk.MustExec(replacePrimaryKeySQL) + require.Equal(t, int64(1), int64(tk.Session().AffectedRows())) + + issue989SQL := `CREATE TABLE tIssue989 (a int, b int, KEY(a), UNIQUE KEY(b)) partition by hash (b) partitions 10;` + tk.MustExec(issue989SQL) + issue989SQL = `insert into tIssue989 (a, b) values (1, 2);` + tk.MustExec(issue989SQL) + issue989SQL = `replace into tIssue989(a, b) values (111, 2);` + tk.MustExec(issue989SQL) + r := tk.MustQuery("select * from tIssue989;") + r.Check(testkit.Rows("111 2")) +} + +func TestPartitionedTableUpdate(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`create table t (id int not null default 1, name varchar(255)) + PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21))`) + + tk.MustExec(`insert INTO t VALUES (1, "hello");`) + tk.CheckExecResult(1, 0) + tk.MustExec(`insert INTO t VALUES (7, "hello");`) + tk.CheckExecResult(1, 0) + + // update non partition column + tk.MustExec(`UPDATE t SET name = "abc" where id > 0;`) + tk.CheckExecResult(2, 0) + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 2 Changed: 2 Warnings: 0") + r := tk.MustQuery(`SELECT * from t order by id limit 2;`) + r.Check(testkit.Rows("1 abc", "7 abc")) + + // update partition column + tk.MustExec(`update t set id = id + 1`) + tk.CheckExecResult(2, 0) + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 2 Changed: 2 Warnings: 0") + r = tk.MustQuery(`SELECT * from t order by id limit 2;`) + r.Check(testkit.Rows("2 abc", "8 abc")) + + // update partition column, old and new record locates on different partitions + tk.MustExec(`update t set id = 20 where id = 8`) + tk.CheckExecResult(1, 0) + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 1 Warnings: 0") + r = tk.MustQuery(`SELECT * from t order by id limit 2;`) + r.Check(testkit.Rows("2 abc", "20 abc")) + + // table option is auto-increment + tk.MustExec("drop table if exists t;") + tk.MustExec(`create table t (id int not null auto_increment, name varchar(255), primary key(id)) + PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21))`) + + tk.MustExec("insert into t(name) values ('aa')") + tk.MustExec("update t set id = 8 where name = 'aa'") + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 1 Warnings: 0") + tk.MustExec("insert into t(name) values ('bb')") + r = tk.MustQuery("select * from t;") + r.Check(testkit.Rows("8 aa", "9 bb")) + + err := tk.ExecToErr("update t set id = null where name = 'aa'") + require.EqualError(t, err, "[table:1048]Column 'id' cannot be null") + + // Test that in a transaction, when a constraint failed in an update statement, the record is not inserted. + tk.MustExec("drop table if exists t;") + tk.MustExec(`create table t (id int, name int unique) + PARTITION BY RANGE ( name ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21))`) + tk.MustExec("insert t values (1, 1), (2, 2);") + err = tk.ExecToErr("update t set name = 1 where id = 2") + require.Error(t, err) + tk.MustQuery("select * from t").Check(testkit.Rows("1 1", "2 2")) + + // test update ignore for pimary key + tk.MustExec("drop table if exists t;") + tk.MustExec(`create table t(a bigint, primary key (a)) + PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11))`) + tk.MustExec("insert into t values (5)") + tk.MustExec("insert into t values (7)") + err = tk.ExecToErr("update ignore t set a = 5 where a = 7;") + require.NoError(t, err) + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 0 Warnings: 1") + r = tk.MustQuery("SHOW WARNINGS;") + r.Check(testkit.Rows("Warning 1062 Duplicate entry '5' for key 't.PRIMARY'")) + tk.MustQuery("select * from t order by a").Check(testkit.Rows("5", "7")) + + // test update ignore for truncate as warning + err = tk.ExecToErr("update ignore t set a = 1 where a = (select '2a')") + require.NoError(t, err) + r = tk.MustQuery("SHOW WARNINGS;") + r.Check(testkit.Rows("Warning 1292 Truncated incorrect DOUBLE value: '2a'", "Warning 1292 Truncated incorrect DOUBLE value: '2a'")) + + // test update ignore for unique key + tk.MustExec("drop table if exists t;") + tk.MustExec(`create table t(a bigint, unique key I_uniq (a)) + PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11))`) + tk.MustExec("insert into t values (5)") + tk.MustExec("insert into t values (7)") + err = tk.ExecToErr("update ignore t set a = 5 where a = 7;") + require.NoError(t, err) + require.Equal(t, tk.Session().LastMessage(), "Rows matched: 1 Changed: 0 Warnings: 1") + r = tk.MustQuery("SHOW WARNINGS;") + r.Check(testkit.Rows("Warning 1062 Duplicate entry '5' for key 't.I_uniq'")) + tk.MustQuery("select * from t order by a").Check(testkit.Rows("5", "7")) +} + +func TestPartitionedTableDelete(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + createTable := `CREATE TABLE test.t (id int not null default 1, name varchar(255), index(id)) + PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21))` + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(createTable) + for i := 1; i < 21; i++ { + tk.MustExec(fmt.Sprintf(`insert into t values (%d, "hello")`, i)) + } + + tk.MustExec(`delete from t where id = 2 limit 1;`) + tk.CheckExecResult(1, 0) + + // Test delete with false condition + tk.MustExec(`delete from t where 0;`) + tk.CheckExecResult(0, 0) + + tk.MustExec("insert into t values (2, 'abc')") + tk.MustExec(`delete from t where t.id = 2 limit 1`) + tk.CheckExecResult(1, 0) + + // Test delete ignore + tk.MustExec("insert into t values (2, 'abc')") + err := tk.ExecToErr("delete from t where id = (select '2a')") + require.Error(t, err) + err = tk.ExecToErr("delete ignore from t where id = (select '2a')") + require.NoError(t, err) + tk.CheckExecResult(1, 0) + r := tk.MustQuery("SHOW WARNINGS;") + r.Check(testkit.Rows("Warning 1292 Truncated incorrect DOUBLE value: '2a'", "Warning 1292 Truncated incorrect DOUBLE value: '2a'")) + + // Test delete without using index, involve multiple partitions. + tk.MustExec("delete from t ignore index(id) where id >= 13 and id <= 17") + tk.CheckExecResult(5, 0) + + tk.MustExec("admin check table t") + tk.MustExec(`delete from t;`) + tk.CheckExecResult(14, 0) + + // Fix that partitioned table should not use PointGetPlan. + tk.MustExec(`create table t1 (c1 bigint, c2 bigint, c3 bigint, primary key(c1)) partition by range (c1) (partition p0 values less than (3440))`) + tk.MustExec("insert into t1 values (379, 379, 379)") + tk.MustExec("delete from t1 where c1 = 379") + tk.CheckExecResult(1, 0) + tk.MustExec(`drop table t1;`) +} + +func TestPartitionOnMissing(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create schema OnMissing") + tk.MustExec("use OnMissing") + tk.MustExec(`set global tidb_partition_prune_mode='dynamic'`) + tk.MustExec(`set session tidb_partition_prune_mode='dynamic'`) + + tk.MustExec(`CREATE TABLE tt1 ( + id INT NOT NULL, + listid INT, + name varchar(10), + primary key (listid) clustered + ) + PARTITION BY LIST (listid) ( + PARTITION p1 VALUES IN (1), + PARTITION p2 VALUES IN (2), + PARTITION p3 VALUES IN (3), + PARTITION p4 VALUES IN (4) + )`) + + tk.MustExec(`CREATE TABLE tt2 ( + id INT NOT NULL, + listid INT + )`) + + tk.MustExec(`create index idx_listid on tt1(id,listid)`) + tk.MustExec(`create index idx_listid on tt2(listid)`) + + tk.MustExec(`insert into tt1 values(1,1,1)`) + tk.MustExec(`insert into tt1 values(2,2,2)`) + tk.MustExec(`insert into tt1 values(3,3,3)`) + tk.MustExec(`insert into tt1 values(4,4,4)`) + tk.MustExec(`insert into tt2 values(1,1)`) + tk.MustExec(`insert into tt2 values(2,2)`) + tk.MustExec(`insert into tt2 values(3,3)`) + tk.MustExec(`insert into tt2 values(4,4)`) + tk.MustExec(`insert into tt2 values(5,5)`) + + tk.MustExec(`analyze table tt1`) + tk.MustExec(`analyze table tt2`) + + tk.MustQuery(`select /*+ inl_join(tt1)*/ count(*) from tt2 + left join tt1 on tt1.listid=tt2.listid and tt1.id=tt2.id`).Check(testkit.Rows("5")) + tk.MustQuery(`select /*+ inl_join(tt1)*/ count(*) from tt2 + left join tt1 on tt1.listid=tt2.listid`).Check(testkit.Rows("5")) + tk.MustQuery(`explain format = 'brief' select /*+ inl_join(tt1)*/ count(*) from tt2 + left join tt1 on tt1.listid=tt2.listid`).Check(testkit.Rows(""+ + "StreamAgg 1.00 root funcs:count(Column#13)->Column#7", + "└─IndexReader 1.00 root index:StreamAgg", + " └─StreamAgg 1.00 cop[tikv] funcs:count(1)->Column#13", + " └─IndexFullScan 5.00 cop[tikv] table:tt2, index:idx_listid(listid) keep order:false")) +} diff --git a/pkg/executor/test/passwordtest/BUILD.bazel b/pkg/executor/test/passwordtest/BUILD.bazel new file mode 100644 index 0000000000000..6d9d3c6cc187d --- /dev/null +++ b/pkg/executor/test/passwordtest/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "passwordtest_test", + timeout = "short", + srcs = [ + "main_test.go", + "password_management_test.go", + ], + flaky = True, + shard_count = 16, + deps = [ + "//pkg/domain", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/privilege/privileges", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/util/sqlexec", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/passwordtest/main_test.go b/pkg/executor/test/passwordtest/main_test.go similarity index 100% rename from executor/test/passwordtest/main_test.go rename to pkg/executor/test/passwordtest/main_test.go diff --git a/executor/test/passwordtest/password_management_test.go b/pkg/executor/test/passwordtest/password_management_test.go similarity index 99% rename from executor/test/passwordtest/password_management_test.go rename to pkg/executor/test/passwordtest/password_management_test.go index 840e8f32a830f..e1d884b92bc55 100644 --- a/executor/test/passwordtest/password_management_test.go +++ b/pkg/executor/test/passwordtest/password_management_test.go @@ -24,15 +24,15 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/seqtest/BUILD.bazel b/pkg/executor/test/seqtest/BUILD.bazel new file mode 100644 index 0000000000000..7367f81c9de15 --- /dev/null +++ b/pkg/executor/test/seqtest/BUILD.bazel @@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "seqtest_test", + timeout = "moderate", + srcs = [ + "main_test.go", + "prepared_test.go", + "seq_executor_test.go", + ], + flaky = True, + race = "on", + shard_count = 37, + deps = [ + "//pkg/config", + "//pkg/ddl/testutil", + "//pkg/ddl/util", + "//pkg/errno", + "//pkg/executor", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/util/gcutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/seqtest/main_test.go b/pkg/executor/test/seqtest/main_test.go new file mode 100644 index 0000000000000..74a205d7509f1 --- /dev/null +++ b/pkg/executor/test/seqtest/main_test.go @@ -0,0 +1,39 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/config" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/executor.readProjection[...]"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/seqtest/prepared_test.go b/pkg/executor/test/seqtest/prepared_test.go new file mode 100644 index 0000000000000..da1c1f2b1505e --- /dev/null +++ b/pkg/executor/test/seqtest/prepared_test.go @@ -0,0 +1,697 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "context" + "fmt" + "math" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +func TestPrepared(t *testing.T) { + store := testkit.CreateMockStore(t) + flags := []bool{false, true} + ctx := context.Background() + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + var err error + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1)") + tk.MustExec("insert prepare_test (c1) values (1),(2),(NULL)") + + tk.MustExec(`prepare stmt_test_1 from 'select id from prepare_test where id > ?';`) + tk.MustExec(`set @a = 1;`) + tk.MustExec(`execute stmt_test_1 using @a;`) + tk.MustExec(`prepare stmt_test_2 from 'select 1'`) + // Prepare multiple statement is not allowed. + tk.MustGetErrCode(`prepare stmt_test_3 from 'select id from prepare_test where id > ?;select id from prepare_test where id > ?;'`, errno.ErrPrepareMulti) + + // The variable count does not match. + tk.MustExec(`prepare stmt_test_4 from 'select id from prepare_test where id > ? and id < ?';`) + tk.MustExec(`set @a = 1;`) + tk.MustGetErrCode(`execute stmt_test_4 using @a;`, errno.ErrWrongParamCount) + // Prepare and deallocate prepared statement immediately. + tk.MustExec(`prepare stmt_test_5 from 'select id from prepare_test where id > ?';`) + tk.MustExec(`deallocate prepare stmt_test_5;`) + + // Statement not found. + err = tk.ExecToErr("deallocate prepare stmt_test_5") + require.True(t, plannercore.ErrStmtNotFound.Equal(err)) + + // incorrect SQLs in prepare. issue #3738, SQL in prepare stmt is parsed in DoPrepare. + tk.MustGetErrMsg(`prepare p from "delete from t where a = 7 or 1=1/*' and b = 'p'";`, + `[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use near '/*' and b = 'p'' at line 1`) + + // The `stmt_test5` should not be found. + err = tk.ExecToErr(`set @a = 1; execute stmt_test_5 using @a;`) + require.True(t, plannercore.ErrStmtNotFound.Equal(err)) + + // Use parameter marker with argument will run prepared statement. + result := tk.MustQuery("select distinct c1, c2 from prepare_test where c1 = ?", 1) + result.Check(testkit.Rows("1 ")) + + // Call Session PrepareStmt directly to get stmtID. + query := "select c1, c2 from prepare_test where c1 = ?" + stmtID, _, _, err := tk.Session().PrepareStmt(query) + require.NoError(t, err) + rs, err := tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1 ")) + + tk.MustExec("delete from prepare_test") + query = "select c1 from prepare_test where c1 = (select c1 from prepare_test where c1 = ?)" + stmtID, _, _, err = tk.Session().PrepareStmt(query) + require.NoError(t, err) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec(`set @@tidb_enable_prepared_plan_cache=true`) + tk1.MustExec("use test") + tk1.MustExec("insert prepare_test (c1) values (3)") + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) + require.NoError(t, err) + tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) + + tk.MustExec("delete from prepare_test") + query = "select c1 from prepare_test where c1 = (select c1 from prepare_test where c1 = ?)" + stmtID, _, _, err = tk.Session().PrepareStmt(query) + require.NoError(t, err) + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) + require.NoError(t, err) + require.NoError(t, rs.Close()) + tk1.MustExec("insert prepare_test (c1) values (3)") + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) + require.NoError(t, err) + tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) + + tk.MustExec("delete from prepare_test") + query = "select c1 from prepare_test where c1 in (select c1 from prepare_test where c1 = ?)" + stmtID, _, _, err = tk.Session().PrepareStmt(query) + require.NoError(t, err) + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) + require.NoError(t, err) + require.NoError(t, rs.Close()) + tk1.MustExec("insert prepare_test (c1) values (3)") + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(3)) + require.NoError(t, err) + tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("3")) + + tk.MustExec("begin") + tk.MustExec("insert prepare_test (c1) values (4)") + query = "select c1, c2 from prepare_test where c1 = ?" + stmtID, _, _, err = tk.Session().PrepareStmt(query) + require.NoError(t, err) + tk.MustExec("rollback") + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(4)) + require.NoError(t, err) + tk.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows()) + + prepStmt, err := tk.Session().GetSessionVars().GetPreparedStmtByID(stmtID) + require.NoError(t, err) + execStmt := &ast.ExecuteStmt{PrepStmt: prepStmt, BinaryArgs: expression.Args2Expressions4Test(1)} + // Check that ast.Statement created by compiler.Compile has query text. + compiler := executor.Compiler{Ctx: tk.Session()} + stmt, err := compiler.Compile(context.TODO(), execStmt) + require.NoError(t, err) + + // Check that rebuild plan works. + err = tk.Session().PrepareTxnCtx(ctx) + require.NoError(t, err) + _, err = stmt.RebuildPlan(ctx) + require.NoError(t, err) + rs, err = stmt.Exec(ctx) + require.NoError(t, err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + require.NoError(t, rs.Close()) + + // Make schema change. + tk.MustExec("drop table if exists prepare2") + tk.MustExec("create table prepare2 (a int)") + + // Should success as the changed schema do not affect the prepared statement. + rs, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + if rs != nil { + require.NoError(t, rs.Close()) + } + + // Drop a column so the prepared statement become invalid. + query = "select c1, c2 from prepare_test where c1 = ?" + stmtID, _, _, err = tk.Session().PrepareStmt(query) + require.NoError(t, err) + tk.MustExec("alter table prepare_test drop column c2") + + _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.True(t, plannercore.ErrUnknownColumn.Equal(err)) + + tk.MustExec("drop table prepare_test") + _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.True(t, plannercore.ErrSchemaChanged.Equal(err)) + + // issue 3381 + tk.MustExec("drop table if exists prepare3") + tk.MustExec("create table prepare3 (a decimal(1))") + tk.MustExec("prepare stmt from 'insert into prepare3 value(123)'") + tk.MustExecToErr("execute stmt") + + _, _, fields, err := tk.Session().PrepareStmt("select a from prepare3") + require.NoError(t, err) + require.Equal(t, "test", fields[0].DBName.L) + require.Equal(t, "prepare3", fields[0].TableAsName.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + + _, _, fields, err = tk.Session().PrepareStmt("select a from prepare3 where ?") + require.NoError(t, err) + require.Equal(t, "test", fields[0].DBName.L) + require.Equal(t, "prepare3", fields[0].TableAsName.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + + _, _, fields, err = tk.Session().PrepareStmt("select (1,1) in (select 1,1)") + require.NoError(t, err) + require.Equal(t, "", fields[0].DBName.L) + require.Equal(t, "", fields[0].TableAsName.L) + require.Equal(t, "(1,1) in (select 1,1)", fields[0].ColumnAsName.L) + + _, _, fields, err = tk.Session().PrepareStmt("select a from prepare3 where a = (" + + "select a from prepare2 where a = ?)") + require.NoError(t, err) + require.Equal(t, "test", fields[0].DBName.L) + require.Equal(t, "prepare3", fields[0].TableAsName.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + + _, _, fields, err = tk.Session().PrepareStmt("select * from prepare3 as t1 join prepare3 as t2") + require.NoError(t, err) + require.Equal(t, "test", fields[0].DBName.L) + require.Equal(t, "t1", fields[0].TableAsName.L) + require.Equal(t, "a", fields[0].ColumnAsName.L) + require.Equal(t, "test", fields[1].DBName.L) + require.Equal(t, "t2", fields[1].TableAsName.L) + require.Equal(t, "a", fields[1].ColumnAsName.L) + + _, _, fields, err = tk.Session().PrepareStmt("update prepare3 set a = ?") + require.NoError(t, err) + require.Len(t, fields, 0) + + // issue 8074 + tk.MustExec("drop table if exists prepare1;") + tk.MustExec("create table prepare1 (a decimal(1))") + tk.MustExec("insert into prepare1 values(1);") + tk.MustGetErrMsg("prepare stmt FROM @sql1", + "[parser:1064]You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 4 near \"NULL\" ") + tk.MustExec("SET @sql = 'update prepare1 set a=5 where a=?';") + tk.MustExec("prepare stmt FROM @sql") + tk.MustExec("set @var=1;") + tk.MustExec("execute stmt using @var") + tk.MustQuery("select a from prepare1;").Check(testkit.Rows("5")) + + // issue 19371 + tk.MustExec("SET @sql = 'update prepare1 set a=a+1';") + tk.MustExec("prepare stmt FROM @SQL") + tk.MustExec("execute stmt") + tk.MustQuery("select a from prepare1;").Check(testkit.Rows("6")) + tk.MustExec("prepare stmt FROM @Sql") + tk.MustExec("execute stmt") + tk.MustQuery("select a from prepare1;").Check(testkit.Rows("7")) + + // Coverage. + exec := &executor.ExecuteExec{} + err = exec.Next(ctx, nil) + require.NoError(t, err) + err = exec.Close() + require.NoError(t, err) + + // issue 8065 + stmtID, _, _, err = tk.Session().PrepareStmt("select ? from dual") + require.NoError(t, err) + _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + stmtID, _, _, err = tk.Session().PrepareStmt("update prepare1 set a = ? where a = ?") + require.NoError(t, err) + _, err = tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1, 1)) + require.NoError(t, err) + } +} + +func TestPreparedLimitOffset(t *testing.T) { + store := testkit.CreateMockStore(t) + flags := []bool{false, true} + ctx := context.Background() + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1)") + tk.MustExec("insert prepare_test (c1) values (1),(2),(NULL)") + tk.MustExec(`prepare stmt_test_1 from 'select id from prepare_test limit ? offset ?'; set @a = 1, @b=1;`) + r := tk.MustQuery(`execute stmt_test_1 using @a, @b;`) + r.Check(testkit.Rows("2")) + + tk.MustExec(`set @a=1.1`) + _, err := tk.Exec(`execute stmt_test_1 using @a, @b;`) + require.True(t, plannercore.ErrWrongArguments.Equal(err)) + + tk.MustExec(`set @c="-1"`) + _, err = tk.Exec("execute stmt_test_1 using @c, @c") + require.True(t, plannercore.ErrWrongArguments.Equal(err)) + + stmtID, _, _, err := tk.Session().PrepareStmt("select id from prepare_test limit ?") + require.NoError(t, err) + rs, err := tk.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + rs.Close() + } +} + +func TestPrepareWithAggregation(t *testing.T) { + store := testkit.CreateMockStore(t) + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + + se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ + PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), + }) + require.NoError(t, err) + tk.SetSession(se) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int primary key)") + tk.MustExec("insert into t values (1), (2), (3)") + tk.MustExec(`prepare stmt from 'select sum(id) from t where id = ?'`) + + tk.MustExec(`set @id="1"`) + r := tk.MustQuery(`execute stmt using @id;`) + r.Check(testkit.Rows("1")) + + r = tk.MustQuery(`execute stmt using @id;`) + r.Check(testkit.Rows("1")) + } +} + +func TestPreparedInsert(t *testing.T) { + store := testkit.CreateMockStore(t) + metrics.ResettablePlanCacheCounterFortTest = true + metrics.PlanCacheCounter.Reset() + counter := metrics.PlanCacheCounter.WithLabelValues("prepare") + pb := &dto.Metric{} + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + var err error + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") + tk.MustExec(`prepare stmt_insert from 'insert into prepare_test values (?, ?)'`) + tk.MustExec(`set @a=1,@b=1; execute stmt_insert using @a, @b;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(0), hit) + } + tk.MustExec(`set @a=2,@b=2; execute stmt_insert using @a, @b;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(1), hit) + } + tk.MustExec(`set @a=3,@b=3; execute stmt_insert using @a, @b;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(2), hit) + } + + result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) + result.Check(testkit.Rows("1 1")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) + result.Check(testkit.Rows("2 2")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) + result.Check(testkit.Rows("3 3")) + + tk.MustExec(`prepare stmt_insert_select from 'insert into prepare_test (id, c1) select id + 100, c1 + 100 from prepare_test where id = ?'`) + tk.MustExec(`set @a=1; execute stmt_insert_select using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(2), hit) + } + tk.MustExec(`set @a=2; execute stmt_insert_select using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(3), hit) + } + tk.MustExec(`set @a=3; execute stmt_insert_select using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(4), hit) + } + + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 101) + result.Check(testkit.Rows("101 101")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 102) + result.Check(testkit.Rows("102 102")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 103) + result.Check(testkit.Rows("103 103")) + } +} + +func TestPreparedUpdate(t *testing.T) { + store := testkit.CreateMockStore(t) + metrics.ResettablePlanCacheCounterFortTest = true + metrics.PlanCacheCounter.Reset() + counter := metrics.PlanCacheCounter.WithLabelValues("prepare") + pb := &dto.Metric{} + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect hit counter in this UT. + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + var err error + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") + tk.MustExec(`insert into prepare_test values (1, 1)`) + tk.MustExec(`insert into prepare_test values (2, 2)`) + tk.MustExec(`insert into prepare_test values (3, 3)`) + + tk.MustExec(`prepare stmt_update from 'update prepare_test set c1 = c1 + ? where id = ?'`) + tk.MustExec(`set @a=1,@b=100; execute stmt_update using @b,@a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(0), hit) + } + tk.MustExec(`set @a=2,@b=200; execute stmt_update using @b,@a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(1), hit) + } + tk.MustExec(`set @a=3,@b=300; execute stmt_update using @b,@a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(2), hit) + } + + result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) + result.Check(testkit.Rows("1 101")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) + result.Check(testkit.Rows("2 202")) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) + result.Check(testkit.Rows("3 303")) + } +} + +func TestIssue21884(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@tidb_enable_prepared_plan_cache=false`) + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test(a bigint primary key, status bigint, last_update_time datetime)") + tk.MustExec("insert into prepare_test values (100, 0, '2020-12-18 20:00:00')") + tk.MustExec("prepare stmt from 'update prepare_test set status = ?, last_update_time = now() where a = 100'") + tk.MustExec("set @status = 1") + tk.MustExec("execute stmt using @status") + updateTime := tk.MustQuery("select last_update_time from prepare_test").Rows()[0][0] + // Sleep 1 second to make sure `last_update_time` is updated. + time.Sleep(1 * time.Second) + tk.MustExec("execute stmt using @status") + newUpdateTime := tk.MustQuery("select last_update_time from prepare_test").Rows()[0][0] + require.NotEqual(t, newUpdateTime, updateTime) +} + +func TestPreparedDelete(t *testing.T) { + store := testkit.CreateMockStore(t) + metrics.ResettablePlanCacheCounterFortTest = true + metrics.PlanCacheCounter.Reset() + counter := metrics.PlanCacheCounter.WithLabelValues("prepare") + pb := &dto.Metric{} + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect hit counter in this UT. + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + var err error + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") + tk.MustExec(`insert into prepare_test values (1, 1)`) + tk.MustExec(`insert into prepare_test values (2, 2)`) + tk.MustExec(`insert into prepare_test values (3, 3)`) + + tk.MustExec(`prepare stmt_delete from 'delete from prepare_test where id = ?'`) + tk.MustExec(`set @a=1; execute stmt_delete using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(0), hit) + } + tk.MustExec(`set @a=2; execute stmt_delete using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(1), hit) + } + tk.MustExec(`set @a=3; execute stmt_delete using @a;`) + if flag { + err = counter.Write(pb) + require.NoError(t, err) + hit := pb.GetCounter().GetValue() + require.Equal(t, float64(2), hit) + } + + result := tk.MustQuery("select id, c1 from prepare_test where id = ?", 1) + result.Check(nil) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 2) + result.Check(nil) + result = tk.MustQuery("select id, c1 from prepare_test where id = ?", 3) + result.Check(nil) + } +} + +func TestPrepareDealloc(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@tidb_enable_prepared_plan_cache=true`) + + se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ + PreparedPlanCache: plannercore.NewLRUPlanCache(3, 0.1, math.MaxUint64, tk.Session(), false), + }) + require.NoError(t, err) + tk.SetSession(se) + + tk.MustExec("use test") + tk.MustExec("drop table if exists prepare_test") + tk.MustExec("create table prepare_test (id int PRIMARY KEY, c1 int)") + + require.Equal(t, 0, tk.Session().GetSessionPlanCache().Size()) + tk.MustExec(`prepare stmt1 from 'select id from prepare_test'`) + tk.MustExec("execute stmt1") + tk.MustExec(`prepare stmt2 from 'select c1 from prepare_test'`) + tk.MustExec("execute stmt2") + tk.MustExec(`prepare stmt3 from 'select id, c1 from prepare_test'`) + tk.MustExec("execute stmt3") + tk.MustExec(`prepare stmt4 from 'select * from prepare_test'`) + tk.MustExec("execute stmt4") + require.Equal(t, 3, tk.Session().GetSessionPlanCache().Size()) + + tk.MustExec("deallocate prepare stmt1") + require.Equal(t, 3, tk.Session().GetSessionPlanCache().Size()) + tk.MustExec("deallocate prepare stmt2") + tk.MustExec("deallocate prepare stmt3") + tk.MustExec("deallocate prepare stmt4") + require.Equal(t, 0, tk.Session().GetSessionPlanCache().Size()) + + tk.MustExec(`prepare stmt1 from 'select * from prepare_test'`) + tk.MustExec(`execute stmt1`) + tk.MustExec(`prepare stmt2 from 'select * from prepare_test'`) + tk.MustExec(`execute stmt2`) + require.Equal(t, 1, tk.Session().GetSessionPlanCache().Size()) // use the same cached plan since they have the same statement + + tk.MustExec(`drop database if exists plan_cache`) + tk.MustExec(`create database plan_cache`) + tk.MustExec(`use plan_cache`) + tk.MustExec(`create table prepare_test (id int PRIMARY KEY, c1 int)`) + tk.MustExec(`prepare stmt3 from 'select * from prepare_test'`) + tk.MustExec(`execute stmt3`) + require.Equal(t, 2, tk.Session().GetSessionPlanCache().Size()) // stmt3 has different DB +} + +func TestPreparedIssue8153(t *testing.T) { + store := testkit.CreateMockStore(t) + flags := []bool{false, true} + for _, flag := range flags { + tk := testkit.NewTestKit(t, store) + tk.MustExec(fmt.Sprintf(`set @@tidb_enable_prepared_plan_cache=%v`, flag)) + var err error + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t (a, b) values (1,3), (2,2), (3,1)") + + tk.MustExec(`prepare stmt from 'select * from t order by ? asc'`) + r := tk.MustQuery(`execute stmt using @param;`) + r.Check(testkit.Rows("1 3", "2 2", "3 1")) + + tk.MustExec(`set @param = 1`) + r = tk.MustQuery(`execute stmt using @param;`) + r.Check(testkit.Rows("1 3", "2 2", "3 1")) + + tk.MustExec(`set @param = 2`) + r = tk.MustQuery(`execute stmt using @param;`) + r.Check(testkit.Rows("3 1", "2 2", "1 3")) + + tk.MustExec(`set @param = 3`) + _, err = tk.Exec(`execute stmt using @param;`) + require.EqualError(t, err, "[planner:1054]Unknown column '?' in 'order clause'") + + tk.MustExec(`set @param = '##'`) + r = tk.MustQuery(`execute stmt using @param;`) + r.Check(testkit.Rows("1 3", "2 2", "3 1")) + + tk.MustExec("insert into t (a, b) values (1,1), (1,2), (2,1), (2,3), (3,2), (3,3)") + tk.MustExec(`prepare stmt from 'select ?, sum(a) from t group by ?'`) + + tk.MustExec(`set @a=1,@b=1`) + r = tk.MustQuery(`execute stmt using @a,@b;`) + r.Check(testkit.Rows("1 18")) + + tk.MustExec(`set @a=1,@b=2`) + _, err = tk.Exec(`execute stmt using @a,@b;`) + require.EqualError(t, err, "[planner:1056]Can't group on 'sum(a)'") + } +} + +func TestPreparedIssue17419(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + sv := server.CreateMockServer(t, store) + sv.SetDomain(dom) + defer sv.Close() + + conn1 := server.CreateMockConn(t, sv) + tk := testkit.NewTestKitWithSession(t, store, conn1.Context().Session) + ctx := context.Background() + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int)") + tk.MustExec("insert into t (a) values (1), (2), (3)") + + conn2 := server.CreateMockConn(t, sv) + tk1 := testkit.NewTestKitWithSession(t, store, conn2.Context().Session) + + query := "select * from test.t" + stmtID, _, _, err := tk1.Session().PrepareStmt(query) + require.NoError(t, err) + + dom.ExpensiveQueryHandle().SetSessionManager(sv) + + rs, err := tk1.Session().ExecutePreparedStmt(ctx, stmtID, expression.Args2Expressions4Test()) + require.NoError(t, err) + tk1.ResultSetToResult(rs, fmt.Sprintf("%v", rs)).Check(testkit.Rows("1", "2", "3")) + tk1.Session().SetProcessInfo("", time.Now(), mysql.ComStmtExecute, 0) + + dom.ExpensiveQueryHandle().LogOnQueryExceedMemQuota(tk.Session().GetSessionVars().ConnectionID) + + // After entirely fixing https://github.com/pingcap/tidb/issues/17419 + // require.NotNil(t, tk1.Session().ShowProcess().Plan) + // _, ok := tk1.Session().ShowProcess().Plan.(*plannercore.Execute) + // require.True(t, ok) +} + +func TestLimitUnsupportedCase(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, key(a))") + tk.MustExec("prepare stmt from 'select * from t limit ?'") + + tk.MustExec("set @a = 1.2") + tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") + tk.MustExec("set @a = 1.") + tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") + tk.MustExec("set @a = '0'") + tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") + tk.MustExec("set @a = '1'") + tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") + tk.MustExec("set @a = 1_2") + tk.MustGetErrMsg("execute stmt using @a", "[planner:1210]Incorrect arguments to LIMIT") +} + +func TestIssue38323(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int, k int);") + + tk.MustExec("prepare stmt from 'explain select * from t where id = ? and k = ? group by id, k';") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: not a SELECT/UPDATE/INSERT/DELETE/SET statement")) + tk.MustExec("set @a = 1;") + tk.MustExec("execute stmt using @a, @a") + tk.MustQuery("execute stmt using @a, @a").Check(tk.MustQuery("explain select * from t where id = 1 and k = 1 group by id, k").Rows()) + + tk.MustExec("prepare stmt from 'explain select * from t where ? = id and ? = k group by id, k';") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 skip prepared plan-cache: not a SELECT/UPDATE/INSERT/DELETE/SET statement")) + tk.MustExec("set @a = 1;") + tk.MustQuery("execute stmt using @a, @a").Check(tk.MustQuery("explain select * from t where 1 = id and 1 = k group by id, k").Rows()) +} diff --git a/executor/test/seqtest/seq_executor_test.go b/pkg/executor/test/seqtest/seq_executor_test.go similarity index 95% rename from executor/test/seqtest/seq_executor_test.go rename to pkg/executor/test/seqtest/seq_executor_test.go index 95cd341ebea1f..9b2885da5b97d 100644 --- a/executor/test/seqtest/seq_executor_test.go +++ b/pkg/executor/test/seqtest/seq_executor_test.go @@ -32,24 +32,24 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - ddltestutil "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/util/gcutil" + "github.com/pingcap/tidb/pkg/config" + ddltestutil "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/util/gcutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikv" @@ -96,9 +96,9 @@ func TestEarlyClose(t *testing.T) { } // Goroutine should not leak when error happen. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/handleTaskOnceError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/handleTaskOnceError", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/handleTaskOnceError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/handleTaskOnceError")) }() rss, err := tk.Session().Execute(ctx, "select * from earlyclose") require.NoError(t, err) @@ -682,9 +682,9 @@ func TestIndexMergeReaderClose(t *testing.T) { tk.MustExec("create table t (a int, b int)") tk.MustExec("create index idx1 on t(a)") tk.MustExec("create index idx2 on t(b)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/startPartialIndexWorkerErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/startPartialIndexWorkerErr", "return")) err := tk.QueryToErr("select /*+ USE_INDEX_MERGE(t, idx1, idx2) */ * from t where a > 10 or b < 100") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/startPartialIndexWorkerErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/startPartialIndexWorkerErr")) require.Error(t, err) require.Eventually(t, func() bool { return !checkGoroutineExists("fetchLoop") @@ -711,9 +711,9 @@ func TestParallelHashAggClose(t *testing.T) { // └─TableFullScan_10 | 3.00 | cop[tikv] | table:t, keep order:fa$se, stats:pseudo | // Goroutine should not leak when error happen. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/aggregate/parallelHashAggError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/aggregate/parallelHashAggError", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/aggregate/parallelHashAggError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/aggregate/parallelHashAggError")) }() ctx := context.Background() rss, err := tk.Session().Execute(ctx, "select sum(a) from (select cast(t.a as signed) as a, b from t) t group by b;") @@ -733,9 +733,9 @@ func TestUnparallelHashAggClose(t *testing.T) { tk.MustExec("insert into t values(1,1),(2,2)") // Goroutine should not leak when error happen. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/aggregate/unparallelHashAggError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/aggregate/unparallelHashAggError", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/aggregate/unparallelHashAggError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/aggregate/unparallelHashAggError")) }() ctx := context.Background() rss, err := tk.Session().Execute(ctx, "select sum(distinct a) from (select cast(t.a as signed) as a, b from t) t group by b;") @@ -765,9 +765,9 @@ func TestAdminShowNextID(t *testing.T) { } func HelperTestAdminShowNextID(t *testing.T, store kv.Storage, str string) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) }() step := int64(10) autoIDStep := autoid.GetStep() @@ -873,10 +873,10 @@ func TestNoHistoryWhenDisableRetry(t *testing.T) { tk.MustExec("set @@autocommit = 1") tk.MustExec("set @@tidb_retry_limit = 10") tk.MustExec("set @@tidb_disable_txn_auto_retry = 1") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/keepHistory", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/keepHistory", `return(true)`)) tk.MustExec("insert history values (1)") require.Equal(t, 1, session.GetHistory(tk.Session()).Count()) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/keepHistory")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/keepHistory")) tk.MustExec("begin") tk.MustExec("insert history values (1)") require.Equal(t, 0, session.GetHistory(tk.Session()).Count()) @@ -884,9 +884,9 @@ func TestNoHistoryWhenDisableRetry(t *testing.T) { // Enable auto_retry will add history for both. tk.MustExec("set @@tidb_disable_txn_auto_retry = 0") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/keepHistory", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/keepHistory", `return(true)`)) tk.MustExec("insert history values (1)") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/keepHistory")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/keepHistory")) require.Equal(t, 1, session.GetHistory(tk.Session()).Count()) tk.MustExec("begin") tk.MustExec("insert history values (1)") @@ -1270,9 +1270,9 @@ func TestAutoIncIDInRetry(t *testing.T) { tk.MustExec("insert into t values (),()") tk.MustExec("insert into t values ()") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockCommitRetryForAutoIncID", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockCommitRetryForAutoIncID", `return(true)`)) tk.MustExec("commit") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockCommitRetryForAutoIncID")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockCommitRetryForAutoIncID")) tk.MustExec("insert into t values ()") tk.MustQuery(`select * from t`).Check(testkit.Rows("1", "2", "3", "4", "5")) @@ -1391,7 +1391,7 @@ func TestAutoRandIDRetry(t *testing.T) { tk.MustExec("insert into t values ()") session.ResetMockAutoRandIDRetryCount(5) - fpName := "github.com/pingcap/tidb/session/mockCommitRetryForAutoRandID" + fpName := "github.com/pingcap/tidb/pkg/session/mockCommitRetryForAutoRandID" require.NoError(t, failpoint.Enable(fpName, `return(true)`)) tk.MustExec("commit") require.NoError(t, failpoint.Disable(fpName)) @@ -1441,9 +1441,9 @@ func TestAutoRandRecoverTable(t *testing.T) { err := gcutil.EnableGC(tk.Session()) require.NoError(t, err) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/meta/autoid/mockAutoIDChange")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) }() const autoRandIDStep = 5000 stp := autoid.GetStep() @@ -1498,7 +1498,7 @@ func TestOOMPanicInHashJoinWhenFetchBuildRows(t *testing.T) { tk.MustExec("drop table if exists t") tk.MustExec("create table t(c1 int, c2 int)") tk.MustExec("insert into t values(1,1),(2,2)") - fpName := "github.com/pingcap/tidb/executor/errorFetchBuildSideRowsMockOOMPanic" + fpName := "github.com/pingcap/tidb/pkg/executor/errorFetchBuildSideRowsMockOOMPanic" require.NoError(t, failpoint.Enable(fpName, `panic("ERROR 1105 (HY000): Out Of Memory Quota![conn=1]")`)) defer func() { require.NoError(t, failpoint.Disable(fpName)) @@ -1548,9 +1548,9 @@ func TestIssue18744(t *testing.T) { tk.MustExec(`insert into t values(1 , NULL , NULL , NULL , NULL , NULL , NULL);`) tk.MustExec(`insert into t values(2 , 2012 , "2012-01-01 01:01:00" , "2012-01-01 01:01:00" , 2012 , 2012 , 2012.000000);`) tk.MustExec(`set tidb_index_lookup_join_concurrency=1`) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/testIndexHashJoinOuterWorkerErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinOuterWorkerErr", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/testIndexHashJoinOuterWorkerErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/testIndexHashJoinOuterWorkerErr")) }() err := tk.QueryToErr(`select /*+ inl_hash_join(t2) */ t1.id, t2.id from t1 join t t2 on t1.a = t2.a order by t1.a ASC limit 1;`) require.EqualError(t, err, "mockIndexHashJoinOuterWorkerErr") @@ -1589,9 +1589,9 @@ func TestAnalyzeNextRawErrorNoLeak(t *testing.T) { tk.MustExec("create table t1(id int, c varchar(32))") tk.MustExec("set @@session.tidb_analyze_version = 2") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/distsql/mockNextRawError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/distsql/mockNextRawError", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/distsql/mockNextRawError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/distsql/mockNextRawError")) }() tk.MustGetErrMsg("analyze table t1", "mockNextRawError") } diff --git a/pkg/executor/test/showtest/BUILD.bazel b/pkg/executor/test/showtest/BUILD.bazel new file mode 100644 index 0000000000000..9c1a33744aa29 --- /dev/null +++ b/pkg/executor/test/showtest/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "showtest_test", + timeout = "moderate", + srcs = [ + "main_test.go", + "show_test.go", + ], + flaky = True, + shard_count = 45, + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/meta/autoid", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/planner/core", + "//pkg/privilege/privileges", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/types", + "//pkg/util/dbterror/exeerrors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/showtest/main_test.go b/pkg/executor/test/showtest/main_test.go new file mode 100644 index 0000000000000..a7ed167631652 --- /dev/null +++ b/pkg/executor/test/showtest/main_test.go @@ -0,0 +1,44 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package showtest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/executor/test/showtest/show_test.go b/pkg/executor/test/showtest/show_test.go new file mode 100644 index 0000000000000..ccbd562a35bf1 --- /dev/null +++ b/pkg/executor/test/showtest/show_test.go @@ -0,0 +1,1981 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package showtest + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + parsertypes "github.com/pingcap/tidb/pkg/parser/types" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/stretchr/testify/require" +) + +func TestShowHistogramsInFlight(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + result := tk.MustQuery("show histograms_in_flight") + rows := result.Rows() + require.Len(t, rows, 1) + require.Equal(t, rows[0][0], "0") +} + +func TestShowOpenTables(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("show open tables") + tk.MustQuery("show open tables in test") +} + +func TestShowCreateViewDefiner(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%", AuthUsername: "root", AuthHostname: "%"}, nil, nil, nil)) + + tk.MustExec("use test") + tk.MustExec("create or replace view v1 as select 1") + tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`%` SQL SECURITY DEFINER VIEW `v1` (`1`) AS SELECT 1 AS `1`|utf8mb4|utf8mb4_bin")) + tk.MustExec("drop view v1") +} + +func TestShowCreateTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int,b int)") + tk.MustExec("drop view if exists v1") + tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select * from t1") + tk.MustQuery("show create table v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) + tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a`, `b`) AS SELECT `test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) + tk.MustExec("drop view v1") + tk.MustExec("drop table t1") + + tk.MustExec("drop view if exists v") + tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v as select JSON_MERGE('{}', '{}') as col;") + tk.MustQuery("show create view v").Check(testkit.RowsWithSep("|", "v|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v` (`col`) AS SELECT JSON_MERGE(_UTF8MB4'{}', _UTF8MB4'{}') AS `col`|utf8mb4|utf8mb4_bin")) + tk.MustExec("drop view if exists v") + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int,b int)") + tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select avg(a),t1.* from t1 group by a") + tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`avg(a)`, `a`, `b`) AS SELECT AVG(`a`) AS `avg(a)`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b` FROM `test`.`t1` GROUP BY `a`|utf8mb4|utf8mb4_bin")) + tk.MustExec("drop view v1") + tk.MustExec("create or replace definer=`root`@`127.0.0.1` view v1 as select a+b, t1.* , a as c from t1") + tk.MustQuery("show create view v1").Check(testkit.RowsWithSep("|", "v1|CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`127.0.0.1` SQL SECURITY DEFINER VIEW `v1` (`a+b`, `a`, `b`, `c`) AS SELECT `a`+`b` AS `a+b`,`test`.`t1`.`a` AS `a`,`test`.`t1`.`b` AS `b`,`a` AS `c` FROM `test`.`t1`|utf8mb4|utf8mb4_bin")) + tk.MustExec("drop table t1") + tk.MustExec("drop view v1") + + // For issue #9211 + tk.MustExec("create table t(c int, b int as (c + 1))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `c` int(11) DEFAULT NULL,\n"+ + " `b` int(11) GENERATED ALWAYS AS (`c` + 1) VIRTUAL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + tk.MustExec("drop table t") + tk.MustExec("create table t(c int, b int as (c + 1) not null)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `c` int(11) DEFAULT NULL,\n"+ + " `b` int(11) GENERATED ALWAYS AS (`c` + 1) VIRTUAL NOT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec("drop table t") + tk.MustExec("create table t ( a char(10) charset utf8 collate utf8_bin, b char(10) as (rtrim(a)));") + tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` char(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,\n"+ + " `b` char(10) GENERATED ALWAYS AS (rtrim(`a`)) VIRTUAL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec("drop table t") + + tk.MustExec(`drop table if exists different_charset`) + tk.MustExec(`create table different_charset(ch1 varchar(10) charset utf8, ch2 varchar(10) charset binary);`) + tk.MustQuery(`show create table different_charset`).Check(testkit.RowsWithSep("|", + ""+ + "different_charset CREATE TABLE `different_charset` (\n"+ + " `ch1` varchar(10) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,\n"+ + " `ch2` varbinary(10) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table `t` (\n" + + "`a` timestamp not null default current_timestamp,\n" + + "`b` timestamp(3) default current_timestamp(3),\n" + + "`c` datetime default current_timestamp,\n" + + "`d` datetime(4) default current_timestamp(4),\n" + + "`e` varchar(20) default 'cUrrent_tImestamp',\n" + + "`f` datetime(2) default current_timestamp(2) on update current_timestamp(2),\n" + + "`g` timestamp(2) default current_timestamp(2) on update current_timestamp(2),\n" + + "`h` date default current_date )") + tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,\n"+ + " `b` timestamp(3) DEFAULT CURRENT_TIMESTAMP(3),\n"+ + " `c` datetime DEFAULT CURRENT_TIMESTAMP,\n"+ + " `d` datetime(4) DEFAULT CURRENT_TIMESTAMP(4),\n"+ + " `e` varchar(20) DEFAULT 'cUrrent_tImestamp',\n"+ + " `f` datetime(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2),\n"+ + " `g` timestamp(2) DEFAULT CURRENT_TIMESTAMP(2) ON UPDATE CURRENT_TIMESTAMP(2),\n"+ + " `h` date DEFAULT CURRENT_DATE\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec("drop table t") + + tk.MustExec("create table t (a int, b int) shard_row_id_bits = 4 pre_split_regions=3;") + tk.MustQuery("show create table `t`").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` int(11) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T! SHARD_ROW_ID_BITS=4 PRE_SPLIT_REGIONS=3 */", + )) + tk.MustExec("drop table t") + + // for issue #20446 + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c int unsigned default 0);") + tk.MustQuery("show create table `t1`").Check(testkit.RowsWithSep("|", + ""+ + "t1 CREATE TABLE `t1` (\n"+ + " `c` int(10) unsigned DEFAULT '0'\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec("drop table t1") + + tk.MustExec("CREATE TABLE `log` (" + + "`LOG_ID` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT," + + "`ROUND_ID` bigint(20) UNSIGNED NOT NULL," + + "`USER_ID` int(10) UNSIGNED NOT NULL," + + "`USER_IP` int(10) UNSIGNED DEFAULT NULL," + + "`END_TIME` datetime NOT NULL," + + "`USER_TYPE` int(11) DEFAULT NULL," + + "`APP_ID` int(11) DEFAULT NULL," + + "PRIMARY KEY (`LOG_ID`,`END_TIME`) NONCLUSTERED," + + "KEY `IDX_EndTime` (`END_TIME`)," + + "KEY `IDX_RoundId` (`ROUND_ID`)," + + "KEY `IDX_UserId_EndTime` (`USER_ID`,`END_TIME`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=505488 " + + "PARTITION BY RANGE ( month(`end_time`) ) (" + + "PARTITION `p1` VALUES LESS THAN (2)," + + "PARTITION `p2` VALUES LESS THAN (3)," + + "PARTITION `p3` VALUES LESS THAN (4)," + + "PARTITION `p4` VALUES LESS THAN (5)," + + "PARTITION `p5` VALUES LESS THAN (6)," + + "PARTITION `p6` VALUES LESS THAN (7)," + + "PARTITION `p7` VALUES LESS THAN (8)," + + "PARTITION `p8` VALUES LESS THAN (9)," + + "PARTITION `p9` VALUES LESS THAN (10)," + + "PARTITION `p10` VALUES LESS THAN (11)," + + "PARTITION `p11` VALUES LESS THAN (12)," + + "PARTITION `p12` VALUES LESS THAN (MAXVALUE))") + tk.MustQuery("show create table log").Check(testkit.RowsWithSep("|", + "log CREATE TABLE `log` (\n"+ + " `LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,\n"+ + " `ROUND_ID` bigint(20) unsigned NOT NULL,\n"+ + " `USER_ID` int(10) unsigned NOT NULL,\n"+ + " `USER_IP` int(10) unsigned DEFAULT NULL,\n"+ + " `END_TIME` datetime NOT NULL,\n"+ + " `USER_TYPE` int(11) DEFAULT NULL,\n"+ + " `APP_ID` int(11) DEFAULT NULL,\n"+ + " PRIMARY KEY (`LOG_ID`,`END_TIME`) /*T![clustered_index] NONCLUSTERED */,\n"+ + " KEY `IDX_EndTime` (`END_TIME`),\n"+ + " KEY `IDX_RoundId` (`ROUND_ID`),\n"+ + " KEY `IDX_UserId_EndTime` (`USER_ID`,`END_TIME`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=505488\n"+ + "PARTITION BY RANGE (MONTH(`end_time`))\n"+ + "(PARTITION `p1` VALUES LESS THAN (2),\n"+ + " PARTITION `p2` VALUES LESS THAN (3),\n"+ + " PARTITION `p3` VALUES LESS THAN (4),\n"+ + " PARTITION `p4` VALUES LESS THAN (5),\n"+ + " PARTITION `p5` VALUES LESS THAN (6),\n"+ + " PARTITION `p6` VALUES LESS THAN (7),\n"+ + " PARTITION `p7` VALUES LESS THAN (8),\n"+ + " PARTITION `p8` VALUES LESS THAN (9),\n"+ + " PARTITION `p9` VALUES LESS THAN (10),\n"+ + " PARTITION `p10` VALUES LESS THAN (11),\n"+ + " PARTITION `p11` VALUES LESS THAN (12),\n"+ + " PARTITION `p12` VALUES LESS THAN (MAXVALUE))")) + + // for issue #11831 + tk.MustExec("create table ttt4(a varchar(123) default null collate utf8mb4_unicode_ci)engine=innodb default charset=utf8mb4 collate=utf8mb4_unicode_ci;") + tk.MustQuery("show create table `ttt4`").Check(testkit.RowsWithSep("|", + ""+ + "ttt4 CREATE TABLE `ttt4` (\n"+ + " `a` varchar(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci", + )) + tk.MustExec("create table ttt5(a varchar(123) default null)engine=innodb default charset=utf8mb4 collate=utf8mb4_bin;") + tk.MustQuery("show create table `ttt5`").Check(testkit.RowsWithSep("|", + ""+ + "ttt5 CREATE TABLE `ttt5` (\n"+ + " `a` varchar(123) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // for expression index + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b real);") + tk.MustExec("alter table t add index expr_idx((a*b+1));") + tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` double DEFAULT NULL,\n"+ + " KEY `expr_idx` ((`a` * `b` + 1))\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // Fix issue #15175, show create table sequence_name. + tk.MustExec("drop sequence if exists seq") + tk.MustExec("create sequence seq") + tk.MustQuery("show create table seq;").Check(testkit.Rows("seq CREATE SEQUENCE `seq` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB")) + + // Test for issue #15633, 'binary' collation should be ignored in the result of 'show create table'. + tk.MustExec(`drop table if exists binary_collate`) + tk.MustExec(`create table binary_collate(a varchar(10)) default collate=binary;`) + tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", + ""+ + "binary_collate CREATE TABLE `binary_collate` (\n"+ + " `a` varbinary(10) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=binary", // binary collate is ignored + )) + tk.MustExec(`drop table if exists binary_collate`) + tk.MustExec(`create table binary_collate(a varchar(10)) default charset=binary collate=binary;`) + tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", + ""+ + "binary_collate CREATE TABLE `binary_collate` (\n"+ + " `a` varbinary(10) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=binary", // binary collate is ignored + )) + tk.MustExec(`drop table if exists binary_collate`) + tk.MustExec(`create table binary_collate(a varchar(10)) default charset=utf8mb4 collate=utf8mb4_bin;`) + tk.MustQuery(`show create table binary_collate`).Check(testkit.RowsWithSep("|", + ""+ + "binary_collate CREATE TABLE `binary_collate` (\n"+ + " `a` varchar(10) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", // non-binary collate is kept. + )) + // Test for issue #17 in bug competition, default num and sequence should be shown without quote. + tk.MustExec(`drop table if exists default_num`) + tk.MustExec("create table default_num(a int default 11)") + tk.MustQuery("show create table default_num").Check(testkit.RowsWithSep("|", + ""+ + "default_num CREATE TABLE `default_num` (\n"+ + " `a` int(11) DEFAULT '11'\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec(`drop table if exists default_varchar`) + tk.MustExec("create table default_varchar(a varchar(10) default \"haha\")") + tk.MustQuery("show create table default_varchar").Check(testkit.RowsWithSep("|", + ""+ + "default_varchar CREATE TABLE `default_varchar` (\n"+ + " `a` varchar(10) DEFAULT 'haha'\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec(`drop table if exists default_sequence`) + tk.MustExec("create table default_sequence(a int default nextval(seq))") + tk.MustQuery("show create table default_sequence").Check(testkit.RowsWithSep("|", + ""+ + "default_sequence CREATE TABLE `default_sequence` (\n"+ + " `a` int(11) DEFAULT nextval(`test`.`seq`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // set @@foreign_key_checks=0, + // This means that the child table can be created before the parent table. + // This behavior is required for mysqldump restores. + tk.MustExec("set @@foreign_key_checks=0") + tk.MustExec(`DROP TABLE IF EXISTS parent, child`) + tk.MustExec(`CREATE TABLE child (id INT NOT NULL PRIMARY KEY auto_increment, parent_id INT NOT NULL, INDEX par_ind (parent_id), CONSTRAINT child_ibfk_1 FOREIGN KEY (parent_id) REFERENCES parent(id))`) + tk.MustExec(`CREATE TABLE parent ( id INT NOT NULL PRIMARY KEY auto_increment )`) + tk.MustQuery(`show create table child`).Check(testkit.RowsWithSep("|", + ""+ + "child CREATE TABLE `child` (\n"+ + " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ + " `parent_id` int(11) NOT NULL,\n"+ + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ + " KEY `par_ind` (`parent_id`),\n"+ + " CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `test`.`parent` (`id`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // Test Foreign keys + ON DELETE / ON UPDATE + tk.MustExec(`DROP TABLE child`) + tk.MustExec(`CREATE TABLE child (id INT NOT NULL PRIMARY KEY auto_increment, parent_id INT NOT NULL, INDEX par_ind (parent_id), CONSTRAINT child_ibfk_1 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE RESTRICT ON UPDATE CASCADE)`) + tk.MustQuery(`show create table child`).Check(testkit.RowsWithSep("|", + ""+ + "child CREATE TABLE `child` (\n"+ + " `id` int(11) NOT NULL AUTO_INCREMENT,\n"+ + " `parent_id` int(11) NOT NULL,\n"+ + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ + " KEY `par_ind` (`parent_id`),\n"+ + " CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `test`.`parent` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // Test Foreign key refer other database table. + tk.MustExec("create database test1") + tk.MustExec("create database test2") + tk.MustExec("create table test1.t1 (id int key, b int, index(b));") + tk.MustExec("create table test2.t2 (id int key, b int, foreign key fk(b) references test1.t1(id));") + tk.MustQuery("show create table test2.t2").Check(testkit.Rows("t2 CREATE TABLE `t2` (\n" + + " `id` int(11) NOT NULL,\n" + + " `b` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `fk` (`b`),\n" + + " CONSTRAINT `fk` FOREIGN KEY (`b`) REFERENCES `test1`.`t1` (`id`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // Test issue #20327 + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b char(10) as ('a'));") + result := tk.MustQuery("show create table t;").Rows()[0][1] + require.Regexp(t, `(?s).*GENERATED ALWAYS AS \(_utf8mb4'a'\).*`, result) + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b char(10) as (_utf8'a'));") + result = tk.MustQuery("show create table t;").Rows()[0][1] + require.Regexp(t, `(?s).*GENERATED ALWAYS AS \(_utf8'a'\).*`, result) + // Test show list partition table + tk.MustExec("set @@session.tidb_enable_list_partition = ON") + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id)) partition by list (id) ( + partition p0 values in (3,5,6,9,17), + partition p1 values in (1,2,10,11,19,20), + partition p2 values in (4,12,13,14,18), + partition p3 values in (7,8,15,16,null) + );`) + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `id` int(11) DEFAULT NULL,\n"+ + " `name` varchar(10) DEFAULT NULL,\n"+ + " UNIQUE KEY `idx` (`id`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY LIST (`id`)\n"+ + "(PARTITION `p0` VALUES IN (3,5,6,9,17),\n"+ + " PARTITION `p1` VALUES IN (1,2,10,11,19,20),\n"+ + " PARTITION `p2` VALUES IN (4,12,13,14,18),\n"+ + " PARTITION `p3` VALUES IN (7,8,15,16,NULL))")) + // Test show list column partition table + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id)) partition by list columns (id) ( + partition p0 values in (3,5,6,9,17), + partition p1 values in (1,2,10,11,19,20), + partition p2 values in (4,12,13,14,18), + partition p3 values in (7,8,15,16,null) + );`) + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `id` int(11) DEFAULT NULL,\n"+ + " `name` varchar(10) DEFAULT NULL,\n"+ + " UNIQUE KEY `idx` (`id`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY LIST COLUMNS(`id`)\n"+ + "(PARTITION `p0` VALUES IN (3,5,6,9,17),\n"+ + " PARTITION `p1` VALUES IN (1,2,10,11,19,20),\n"+ + " PARTITION `p2` VALUES IN (4,12,13,14,18),\n"+ + " PARTITION `p3` VALUES IN (7,8,15,16,NULL))")) + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec(`create table t (id int, name varchar(10), unique index idx (id, name)) partition by list columns (id, name) ( + partition p0 values in ((3, '1'), (5, '5')), + partition p1 values in ((1, '1')));`) + // The strings are single quoted in MySQL even if sql_mode doesn't contain ANSI_QUOTES. + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `id` int(11) DEFAULT NULL,\n"+ + " `name` varchar(10) DEFAULT NULL,\n"+ + " UNIQUE KEY `idx` (`id`,`name`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY LIST COLUMNS(`id`,`name`)\n"+ + "(PARTITION `p0` VALUES IN ((3,'1'),(5,'5')),\n"+ + " PARTITION `p1` VALUES IN ((1,'1')))")) + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec(`create table t (id int primary key, v varchar(255) not null, key idx_v (v) comment 'foo\'bar')`) + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `id` int(11) NOT NULL,\n"+ + " `v` varchar(255) NOT NULL,\n"+ + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n"+ + " KEY `idx_v` (`v`) COMMENT 'foo''bar'\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // For issue #29922 + tk.MustExec("CREATE TABLE `thash` (\n `id` bigint unsigned NOT NULL,\n `data` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n)\nPARTITION BY HASH (`id`)\n(PARTITION pEven COMMENT = \"Even ids\",\n PARTITION pOdd COMMENT = \"Odd ids\");") + tk.MustQuery("show create table `thash`").Check(testkit.RowsWithSep("|", ""+ + "thash CREATE TABLE `thash` (\n"+ + " `id` bigint(20) unsigned NOT NULL,\n"+ + " `data` varchar(255) DEFAULT NULL,\n"+ + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY HASH (`id`)\n"+ + "(PARTITION `pEven` COMMENT 'Even ids',\n"+ + " PARTITION `pOdd` COMMENT 'Odd ids')", + )) + // empty edge case + tk.MustExec("drop table if exists `thash`") + tk.MustExec("CREATE TABLE `thash` (\n `id` bigint unsigned NOT NULL,\n `data` varchar(255) DEFAULT NULL,\n PRIMARY KEY (`id`)\n)\nPARTITION BY HASH (`id`);") + tk.MustQuery("show create table `thash`").Check(testkit.RowsWithSep("|", ""+ + "thash CREATE TABLE `thash` (\n"+ + " `id` bigint(20) unsigned NOT NULL,\n"+ + " `data` varchar(255) DEFAULT NULL,\n"+ + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY HASH (`id`) PARTITIONS 1", + )) + + // default value escape character '\\' display case + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int primary key, b varchar(20) default '\\\\');") + tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) NOT NULL,\n"+ + " `b` varchar(20) DEFAULT '\\\\',\n"+ + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(" + + "a set('a', 'b') charset binary," + + "b enum('a', 'b') charset ascii);") + tk.MustQuery("show create table t;").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` set('a','b') CHARACTER SET binary COLLATE binary DEFAULT NULL,\n"+ + " `b` enum('a','b') CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a bit default (rand()))`) + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` bit(1) DEFAULT rand()\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec(`drop table if exists t`) + err := tk.ExecToErr(`create table t (a varchar(255) character set ascii) partition by range columns (a) (partition p values less than (0xff))`) + require.ErrorContains(t, err, "[ddl:1654]Partition column values of incorrect type") + tk.MustExec(`create table t (a varchar(255) character set ascii) partition by range columns (a) (partition p values less than (0x7f))`) + tk.MustQuery(`show create table t`).Check(testkit.Rows( + "t CREATE TABLE `t` (\n" + + " `a` varchar(255) CHARACTER SET ascii COLLATE ascii_bin DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY RANGE COLUMNS(`a`)\n" + + "(PARTITION `p` VALUES LESS THAN (x'7f'))")) +} + +func TestShowCreateTablePlacement(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + defer tk.MustExec(`DROP TABLE IF EXISTS t`) + + // case for policy + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create placement policy x " + + "FOLLOWERS=2 " + + "CONSTRAINTS=\"[+disk=ssd]\" ") + defer tk.MustExec(`DROP PLACEMENT POLICY IF EXISTS x`) + tk.MustExec("create table t(a int)" + + "PLACEMENT POLICY=\"x\"") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin "+ + "/*T![placement] PLACEMENT POLICY=`x` */", + )) + + // case for policy with quotes + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int)" + + "/*T![placement] PLACEMENT POLICY=\"x\" */") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin "+ + "/*T![placement] PLACEMENT POLICY=`x` */", + )) + + // Partitioned tables + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("set @old_list_part = @@tidb_enable_list_partition") + defer tk.MustExec("set @@tidb_enable_list_partition = @old_list_part") + tk.MustExec("set tidb_enable_list_partition = 1") + tk.MustExec("create table t(a int, b varchar(255))" + + "/*T![placement] PLACEMENT POLICY=\"x\" */" + + "PARTITION BY LIST (a)\n" + + "(PARTITION pLow VALUES in (1,2,3,5,8) COMMENT 'a comment' placement policy 'x'," + + " PARTITION pMid VALUES in (9) COMMENT 'another comment'," + + "partition pMax values IN (10,11,12))") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n"+ + "PARTITION BY LIST (`a`)\n"+ + "(PARTITION `pLow` VALUES IN (1,2,3,5,8) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMid` VALUES IN (9) COMMENT 'another comment',\n"+ + " PARTITION `pMax` VALUES IN (10,11,12))", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "PARTITION BY LIST COLUMNS (b)\n" + + "(PARTITION pLow VALUES in ('1','2','3','5','8') COMMENT 'a comment' placement policy 'x'," + + "partition pMax values IN ('10','11','12'))") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY LIST COLUMNS(`b`)\n"+ + "(PARTITION `pLow` VALUES IN ('1','2','3','5','8') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMax` VALUES IN ('10','11','12'))", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "PARTITION BY LIST COLUMNS (a,b)\n" + + "(PARTITION pLow VALUES in ((1,'1'),(2,'2'),(3,'3'),(5,'5'),(8,'8')) COMMENT 'a comment' placement policy 'x'," + + "partition pMax values IN ((10,'10'),(11,'11'),(12,'12')))") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY LIST COLUMNS(`a`,`b`)\n"+ + "(PARTITION `pLow` VALUES IN ((1,'1'),(2,'2'),(3,'3'),(5,'5'),(8,'8')) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMax` VALUES IN ((10,'10'),(11,'11'),(12,'12')))", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "PARTITION BY RANGE (a)\n" + + "(PARTITION pLow VALUES less than (1000000) COMMENT 'a comment' placement policy 'x'," + + "partition pMax values LESS THAN (MAXVALUE))") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY RANGE (`a`)\n"+ + "(PARTITION `pLow` VALUES LESS THAN (1000000) COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMax` VALUES LESS THAN (MAXVALUE))", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "PARTITION BY RANGE COLUMNS (b)\n" + + "(PARTITION pLow VALUES less than ('1000000') COMMENT 'a comment' placement policy 'x'," + + "partition pMax values LESS THAN (MAXVALUE))") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY RANGE COLUMNS(`b`)\n"+ + "(PARTITION `pLow` VALUES LESS THAN ('1000000') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMax` VALUES LESS THAN (MAXVALUE))", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + + tk.MustExec("create table t(a int, b varchar(255))" + + "/*T![placement] PLACEMENT POLICY=\"x\" */" + + "PARTITION BY RANGE COLUMNS (a,b)\n" + + "(PARTITION pLow VALUES less than (1000000,'1000000') COMMENT 'a comment' placement policy 'x'," + + " PARTITION pMidLow VALUES less than (1000000,MAXVALUE) COMMENT 'another comment' placement policy 'x'," + + " PARTITION pMadMax VALUES less than (9000000,'1000000') COMMENT ='Not a comment' placement policy 'x'," + + "partition pMax values LESS THAN (MAXVALUE, 'Does not matter...'))") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec(`insert into t values (1,'1')`) + tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) + tk.MustQuery(`show create table t`).Check(testkit.Rows( + "t CREATE TABLE `t` (\n" + + " `a` int(11) DEFAULT NULL,\n" + + " `b` varchar(255) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n" + + "PARTITION BY RANGE COLUMNS(`a`,`b`)\n" + + "(PARTITION `pLow` VALUES LESS THAN (1000000,'1000000') COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + + " PARTITION `pMidLow` VALUES LESS THAN (1000000,MAXVALUE) COMMENT 'another comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + + " PARTITION `pMadMax` VALUES LESS THAN (9000000,'1000000') COMMENT 'Not a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n" + + " PARTITION `pMax` VALUES LESS THAN (MAXVALUE,'Does not matter...'))")) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "/*T![placement] PLACEMENT POLICY=\"x\" */" + + "PARTITION BY HASH (a) PARTITIONS 2") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`x` */\n"+ + "PARTITION BY HASH (`a`) PARTITIONS 2", + )) + + tk.MustExec(`DROP TABLE IF EXISTS t`) + tk.MustExec("create table t(a int, b varchar(255))" + + "PARTITION BY HASH (a)\n" + + "(PARTITION pLow COMMENT 'a comment' placement policy 'x'," + + "partition pMax)") + tk.MustQuery(`show create table t`).Check(testkit.RowsWithSep("|", ""+ + "t CREATE TABLE `t` (\n"+ + " `a` int(11) DEFAULT NULL,\n"+ + " `b` varchar(255) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n"+ + "PARTITION BY HASH (`a`)\n"+ + "(PARTITION `pLow` COMMENT 'a comment' /*T![placement] PLACEMENT POLICY=`x` */,\n"+ + " PARTITION `pMax`)", + )) + tk.MustExec(`DROP TABLE t`) +} + +func TestShowVisibility(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database showdatabase") + tk.MustExec("use showdatabase") + tk.MustExec("create table t1 (id int)") + tk.MustExec("create table t2 (id int)") + tk.MustExec(`create user 'show'@'%'`) + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show", Hostname: "%"}, nil, nil, nil)) + + // No ShowDatabases privilege, this user would see nothing except INFORMATION_SCHEMA. + tk.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) + + // After grant, the user can see the database. + tk.MustExec(`grant select on showdatabase.t1 to 'show'@'%'`) + tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA", "showdatabase")) + + // The user can see t1 but not t2. + tk1.MustExec("use showdatabase") + tk1.MustQuery("show tables").Check(testkit.Rows("t1")) + + // After revoke, show database result should be just except INFORMATION_SCHEMA. + tk.MustExec(`revoke select on showdatabase.t1 from 'show'@'%'`) + tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) + + // Grant any global privilege would make show databases available. + tk.MustExec(`grant CREATE on *.* to 'show'@'%'`) + rows := tk1.MustQuery("show databases").Rows() + require.GreaterOrEqual(t, len(rows), 2) + + tk.MustExec(`drop user 'show'@'%'`) + tk.MustExec("drop database showdatabase") +} + +func TestShowDatabasesInfoSchemaFirst(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA")) + tk.MustExec(`create user 'show'@'%'`) + + tk.MustExec(`create database AAAA`) + tk.MustExec(`create database BBBB`) + tk.MustExec(`grant select on AAAA.* to 'show'@'%'`) + tk.MustExec(`grant select on BBBB.* to 'show'@'%'`) + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show", Hostname: "%"}, nil, nil, nil)) + tk1.MustQuery("show databases").Check(testkit.Rows("INFORMATION_SCHEMA", "AAAA", "BBBB")) + + tk.MustExec(`drop user 'show'@'%'`) + tk.MustExec(`drop database AAAA`) + tk.MustExec(`drop database BBBB`) +} + +func TestShowWarnings(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + testSQL := `create table if not exists show_warnings (a int)` + tk.MustExec(testSQL) + tk.MustExec("set @@sql_mode=''") + tk.MustExec("insert show_warnings values ('a')") + require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1366|Incorrect int value: 'a' for column 'a' at row 1")) + require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1366|Incorrect int value: 'a' for column 'a' at row 1")) + require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + + // Test Warning level 'Error' + testSQL = `create table show_warnings (a int)` + _, _ = tk.Exec(testSQL) + // FIXME: Table 'test.show_warnings' already exists + require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Error|1050|Table 'test.show_warnings' already exists")) + tk.MustQuery("select @@error_count").Check(testkit.RowsWithSep("|", "1")) + + // Test Warning level 'Note' + testSQL = `create table show_warnings_2 (a int)` + tk.MustExec(testSQL) + testSQL = `create table if not exists show_warnings_2 like show_warnings` + _, err := tk.Exec(testSQL) + require.NoError(t, err) + require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|1050|Table 'test.show_warnings_2' already exists")) + tk.MustQuery("select @@warning_count").Check(testkit.RowsWithSep("|", "1")) + tk.MustQuery("select @@warning_count").Check(testkit.RowsWithSep("|", "0")) +} + +func TestShowErrors(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + testSQL := `create table if not exists show_errors (a int)` + tk.MustExec(testSQL) + testSQL = `create table show_errors (a int)` + // FIXME: 'test.show_errors' already exists + _, _ = tk.Exec(testSQL) + + tk.MustQuery("show errors").Check(testkit.RowsWithSep("|", "Error|1050|Table 'test.show_errors' already exists")) + + // eliminate previous errors + tk.MustExec("select 1") + _, _ = tk.Exec("create invalid") + tk.MustQuery("show errors").Check(testkit.RowsWithSep("|", "Error|1064|You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 14 near \"invalid\" ")) +} + +func TestShowWarningsForExprPushdown(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set tidb_cost_model_version=2`) + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + testSQL := `create table if not exists show_warnings_expr_pushdown (a int, value date)` + tk.MustExec(testSQL) + + // create tiflash replica + { + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "show_warnings_expr_pushdown" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + } + tk.MustExec("set tidb_allow_mpp=0") + tk.MustExec("explain select * from show_warnings_expr_pushdown t where md5(value) = '2020-01-01'") + require.Equal(t, uint16(1), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.")) + tk.MustExec("explain select /*+ read_from_storage(tiflash[show_warnings_expr_pushdown]) */ max(md5(value)) from show_warnings_expr_pushdown group by a") + require.Equal(t, uint16(2), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because arguments of AggFunc `max` contains unsupported exprs")) + tk.MustExec("explain select /*+ read_from_storage(tiflash[show_warnings_expr_pushdown]) */ max(a) from show_warnings_expr_pushdown group by md5(value)") + require.Equal(t, uint16(2), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Scalar function 'md5'(signature: MD5, return type: var_string(32)) is not supported to push down to tiflash now.", "Warning|1105|Aggregation can not be pushed to tiflash because groupByItems contain unsupported exprs")) + tk.MustExec("set tidb_opt_distinct_agg_push_down=0") + tk.MustExec("explain select max(distinct a) from show_warnings_expr_pushdown group by value") + require.Equal(t, uint16(0), tk.Session().GetSessionVars().StmtCtx.WarningCount()) + // tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1105|Aggregation can not be pushed to storage layer in non-mpp mode because it contains agg function with distinct")) +} + +func TestShowGrantsPrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user show_grants") + tk.MustExec("show grants for show_grants") + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show_grants", Hostname: "%"}, nil, nil, nil)) + err := tk1.QueryToErr("show grants for root") + require.EqualError(t, exeerrors.ErrDBaccessDenied.GenWithStackByArgs("show_grants", "%", mysql.SystemDB), err.Error()) + // Test show grants for user with auth host name `%`. + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "show_grants", Hostname: "127.0.0.1", AuthUsername: "show_grants", AuthHostname: "%"}, nil, nil, nil)) + tk2.MustQuery("show grants") +} + +func TestShowStatsPrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user show_stats") + tk1 := testkit.NewTestKit(t, store) + + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "show_stats", Hostname: "%"}, nil, nil, nil)) + e := "[planner:1142]SHOW command denied to user 'show_stats'@'%' for table" + err := tk1.ExecToErr("show stats_meta") + require.ErrorContains(t, err, e) + err = tk1.ExecToErr("SHOW STATS_BUCKETS") + require.ErrorContains(t, err, e) + err = tk1.ExecToErr("SHOW STATS_HISTOGRAMS") + require.ErrorContains(t, err, e) + + eqErr := plannercore.ErrDBaccessDenied.GenWithStackByArgs("show_stats", "%", mysql.SystemDB) + err = tk1.ExecToErr("SHOW STATS_HEALTHY") + require.EqualError(t, err, eqErr.Error()) + tk.MustExec("grant select on mysql.* to show_stats") + tk1.MustExec("show stats_meta") + tk1.MustExec("SHOW STATS_BUCKETS") + tk1.MustExec("SHOW STATS_HEALTHY") + tk1.MustExec("SHOW STATS_HISTOGRAMS") + + tk.MustExec("create user a@'%' identified by '';") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "a", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("grant select on mysql.stats_meta to a@'%';") + tk.MustExec("grant select on mysql.stats_buckets to a@'%';") + tk.MustExec("grant select on mysql.stats_histograms to a@'%';") + tk1.MustExec("show stats_meta") + tk1.MustExec("SHOW STATS_BUCKETS") + tk1.MustExec("SHOW STATS_HISTOGRAMS") +} + +func TestIssue18878(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "127.0.0.1", AuthHostname: "%"}, nil, nil, nil)) + tk.MustQuery("select user()").Check(testkit.Rows("root@127.0.0.1")) + tk.MustQuery("show grants") + tk.MustQuery("select user()").Check(testkit.Rows("root@127.0.0.1")) + err := tk.QueryToErr("show grants for root@127.0.0.1") + require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "127.0.0.1").Error(), err.Error()) + err = tk.QueryToErr("show grants for root@localhost") + require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "localhost").Error(), err.Error()) + err = tk.QueryToErr("show grants for root@1.1.1.1") + require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("root", "1.1.1.1").Error(), err.Error()) + tk.MustExec("create user `show_grants`@`127.0.%`") + err = tk.QueryToErr("show grants for `show_grants`@`127.0.0.1`") + require.Equal(t, privileges.ErrNonexistingGrant.FastGenByArgs("show_grants", "127.0.0.1").Error(), err.Error()) + tk.MustQuery("show grants for `show_grants`@`127.0.%`") +} + +func TestIssue17794(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER 'root'@'8.8.%'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "9.9.9.9", AuthHostname: "%"}, nil, nil, nil)) + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "8.8.8.8", AuthHostname: "8.8.%"}, nil, nil, nil)) + tk.MustQuery("show grants").Check(testkit.Rows("GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION")) + tk1.MustQuery("show grants").Check(testkit.Rows("GRANT USAGE ON *.* TO 'root'@'8.8.%'")) +} + +func TestIssue3641(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustGetErrCode("show tables;", mysql.ErrNoDB) + tk.MustGetErrCode("show tables;", mysql.ErrNoDB) +} + +func TestIssue10549(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE newdb;") + tk.MustExec("CREATE ROLE 'app_developer';") + tk.MustExec("GRANT ALL ON newdb.* TO 'app_developer';") + tk.MustExec("CREATE USER 'dev';") + tk.MustExec("GRANT 'app_developer' TO 'dev';") + tk.MustExec("SET DEFAULT ROLE app_developer TO 'dev';") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dev", Hostname: "%", AuthUsername: "dev", AuthHostname: "%"}, nil, nil, nil)) + tk.MustQuery("SHOW DATABASES;").Check(testkit.Rows("INFORMATION_SCHEMA", "newdb")) + tk.MustQuery("SHOW GRANTS;").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT ALL PRIVILEGES ON `newdb`.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) + tk.MustQuery("SHOW GRANTS FOR CURRENT_USER").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT ALL PRIVILEGES ON `newdb`.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) + tk.MustQuery("SHOW GRANTS FOR dev").Check(testkit.Rows("GRANT USAGE ON *.* TO 'dev'@'%'", "GRANT 'app_developer'@'%' TO 'dev'@'%'")) +} + +func TestIssue11165(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE ROLE 'r_manager';") + tk.MustExec("CREATE USER 'manager'@'localhost';") + tk.MustExec("GRANT 'r_manager' TO 'manager'@'localhost';") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "manager", Hostname: "localhost", AuthUsername: "manager", AuthHostname: "localhost"}, nil, nil, nil)) + tk.MustExec("SET DEFAULT ROLE ALL TO 'manager'@'localhost';") + tk.MustExec("SET DEFAULT ROLE NONE TO 'manager'@'localhost';") + tk.MustExec("SET DEFAULT ROLE 'r_manager' TO 'manager'@'localhost';") +} + +// TestShow2 is moved from session_test +func TestShow2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("set global autocommit=0") + tk1 := testkit.NewTestKit(t, store) + tk1.MustQuery("show global variables where variable_name = 'autocommit'").Check(testkit.Rows("autocommit OFF")) + tk.MustExec("set global autocommit = 1") + tk2 := testkit.NewTestKit(t, store) + tk2.MustQuery("show global variables where variable_name = 'autocommit'").Check(testkit.Rows("autocommit ON")) + + // TODO: Specifying the charset for national char/varchar should not be supported. + tk.MustExec("drop table if exists test_full_column") + tk.MustExec(`create table test_full_column( + c_int int, + c_float float, + c_bit bit, + c_bool bool, + c_char char(1) charset ascii collate ascii_bin, + c_nchar national char(1) charset ascii collate ascii_bin, + c_binary binary, + c_varchar varchar(1) charset ascii collate ascii_bin, + c_varchar_default varchar(20) charset ascii collate ascii_bin default 'cUrrent_tImestamp', + c_nvarchar national varchar(1) charset ascii collate ascii_bin, + c_varbinary varbinary(1), + c_year year, + c_date date, + c_time time, + c_datetime datetime, + c_datetime_default datetime default current_timestamp, + c_datetime_default_2 datetime(2) default current_timestamp(2), + c_timestamp timestamp, + c_timestamp_default timestamp default current_timestamp, + c_timestamp_default_3 timestamp(3) default current_timestamp(3), + c_timestamp_default_4 timestamp(3) default current_timestamp(3) on update current_timestamp(3), + c_date_default date default current_date, + c_date_default_2 date default (curdate()), + c_blob blob, + c_tinyblob tinyblob, + c_mediumblob mediumblob, + c_longblob longblob, + c_text text charset ascii collate ascii_bin, + c_tinytext tinytext charset ascii collate ascii_bin, + c_mediumtext mediumtext charset ascii collate ascii_bin, + c_longtext longtext charset ascii collate ascii_bin, + c_json json, + c_enum enum('1') charset ascii collate ascii_bin, + c_set set('1') charset ascii collate ascii_bin + );`) + + tk.MustQuery(`show full columns from test_full_column`).Check(testkit.Rows( + "" + + "c_int int(11) YES select,insert,update,references ]\n" + + "[c_float float YES select,insert,update,references ]\n" + + "[c_bit bit(1) YES select,insert,update,references ]\n" + + "[c_bool tinyint(1) YES select,insert,update,references ]\n" + + "[c_char char(1) ascii_bin YES select,insert,update,references ]\n" + + "[c_nchar char(1) ascii_bin YES select,insert,update,references ]\n" + + "[c_binary binary(1) YES select,insert,update,references ]\n" + + "[c_varchar varchar(1) ascii_bin YES select,insert,update,references ]\n" + + "[c_varchar_default varchar(20) ascii_bin YES cUrrent_tImestamp select,insert,update,references ]\n" + + "[c_nvarchar varchar(1) ascii_bin YES select,insert,update,references ]\n" + + "[c_varbinary varbinary(1) YES select,insert,update,references ]\n" + + "[c_year year(4) YES select,insert,update,references ]\n" + + "[c_date date YES select,insert,update,references ]\n" + + "[c_time time YES select,insert,update,references ]\n" + + "[c_datetime datetime YES select,insert,update,references ]\n" + + "[c_datetime_default datetime YES CURRENT_TIMESTAMP select,insert,update,references ]\n" + + "[c_datetime_default_2 datetime(2) YES CURRENT_TIMESTAMP(2) select,insert,update,references ]\n" + + "[c_timestamp timestamp YES select,insert,update,references ]\n" + + "[c_timestamp_default timestamp YES CURRENT_TIMESTAMP select,insert,update,references ]\n" + + "[c_timestamp_default_3 timestamp(3) YES CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" + + "[c_timestamp_default_4 timestamp(3) YES CURRENT_TIMESTAMP(3) DEFAULT_GENERATED on update CURRENT_TIMESTAMP(3) select,insert,update,references ]\n" + + "[c_date_default date YES CURRENT_DATE select,insert,update,references ]\n" + + "[c_date_default_2 date YES CURRENT_DATE select,insert,update,references ]\n" + + "[c_blob blob YES select,insert,update,references ]\n" + + "[c_tinyblob tinyblob YES select,insert,update,references ]\n" + + "[c_mediumblob mediumblob YES select,insert,update,references ]\n" + + "[c_longblob longblob YES select,insert,update,references ]\n" + + "[c_text text ascii_bin YES select,insert,update,references ]\n" + + "[c_tinytext tinytext ascii_bin YES select,insert,update,references ]\n" + + "[c_mediumtext mediumtext ascii_bin YES select,insert,update,references ]\n" + + "[c_longtext longtext ascii_bin YES select,insert,update,references ]\n" + + "[c_json json YES select,insert,update,references ]\n" + + "[c_enum enum('1') ascii_bin YES select,insert,update,references ]\n" + + "[c_set set('1') ascii_bin YES select,insert,update,references ")) + + tk.MustExec("drop table if exists test_full_column") + + tk.MustExec("drop table if exists t") + tk.MustExec(`create table if not exists t (c int) comment '注释'`) + tk.MustExec("create or replace definer='root'@'localhost' view v as select * from t") + tk.MustQuery(`show columns from t`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) + tk.MustQuery(`describe t`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) + tk.MustQuery(`show columns from v`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) + tk.MustQuery(`describe v`).Check(testkit.RowsWithSep(",", "c,int(11),YES,,,")) + tk.MustQuery("show collation where Charset = 'utf8' and Collation = 'utf8_bin'").Check(testkit.RowsWithSep(",", "utf8_bin,utf8,83,Yes,Yes,1")) + tk.MustExec(`drop sequence if exists seq`) + tk.MustExec(`create sequence seq`) + tk.MustQuery("show tables").Check(testkit.Rows("seq", "t", "v")) + tk.MustQuery("show full tables").Check(testkit.Rows("seq SEQUENCE", "t BASE TABLE", "v VIEW")) + + // Bug 19427 + tk.MustQuery("SHOW FULL TABLES in INFORMATION_SCHEMA like 'VIEWS'").Check(testkit.Rows("VIEWS SYSTEM VIEW")) + tk.MustQuery("SHOW FULL TABLES in information_schema like 'VIEWS'").Check(testkit.Rows("VIEWS SYSTEM VIEW")) + tk.MustQuery("SHOW FULL TABLES in metrics_schema like 'uptime'").Check(testkit.Rows("uptime SYSTEM VIEW")) + + is := dom.InfoSchema() + tblInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + createTime := model.TSConvert2Time(tblInfo.Meta().UpdateTS).Format(time.DateTime) + + // The Hostname is the actual host + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + + r := tk.MustQuery("show table status from test like 't'") + r.Check(testkit.Rows(fmt.Sprintf("t InnoDB 10 Compact 0 0 0 0 0 0 %s utf8mb4_bin 注释", createTime))) + + tk.MustQuery("show databases like 'test'").Check(testkit.Rows("test")) + + tk.MustExec(`grant all on *.* to 'root'@'%'`) + tk.MustQuery("show grants").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) + + tk.MustQuery("show grants for current_user()").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) + tk.MustQuery("show grants for current_user").Check(testkit.Rows(`GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION`)) +} + +func TestShowCreateUser(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // Create a new user. + tk.MustExec(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED BY 'root';`) + tk.MustQuery("show create user 'test_show_create_user'@'%'"). + Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*81F5E21E35407D884A6CD4A731AEBFB6AF209E1B' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + + tk.MustExec(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED BY 'test';`) + tk.MustQuery("show create user 'test_show_create_user'@'localhost';"). + Check(testkit.Rows(`CREATE USER 'test_show_create_user'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*94BDCEBE19083CE2A1F959FD02F964C7AF4CFC29' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + + // Case: the user exists but the host portion doesn't match + err := tk.QueryToErr("show create user 'test_show_create_user'@'asdf';") + require.Equal(t, exeerrors.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'test_show_create_user'@'asdf'").Error(), err.Error()) + + // Case: a user that doesn't exist + err = tk.QueryToErr("show create user 'aaa'@'localhost';") + require.Equal(t, exeerrors.ErrCannotUser.GenWithStackByArgs("SHOW CREATE USER", "'aaa'@'localhost'").Error(), err.Error()) + + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "127.0.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, nil, nil) + tk.MustQuery("show create user current_user"). + Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) + + tk.MustQuery("show create user current_user()"). + Check(testkit.Rows("CREATE USER 'root'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) + + tk.MustExec("create user 'check_priv'") + + // "show create user" for other user requires the SELECT privilege on mysql database. + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use mysql") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "check_priv", Hostname: "127.0.0.1", AuthUsername: "test_show", AuthHostname: "asdf"}, nil, nil, nil)) + err = tk1.QueryToErr("show create user 'root'@'%'") + require.Error(t, err) + + // "show create user" for current user doesn't check privileges. + tk1.MustQuery("show create user current_user"). + Check(testkit.Rows("CREATE USER 'check_priv'@'127.0.0.1' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT")) + + // Creating users with `IDENTIFIED WITH 'caching_sha2_password'`. + tk.MustExec("CREATE USER 'sha_test'@'%' IDENTIFIED WITH 'caching_sha2_password' BY 'temp_passwd'") + + // Compare only the start of the output as the salt changes every time. + rows := tk.MustQuery("SHOW CREATE USER 'sha_test'@'%'") + require.Equal(t, "CREATE USER 'sha_test'@'%' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$", rows.Rows()[0][0].(string)[:78]) + // Creating users with `IDENTIFIED WITH 'auth-socket'` + tk.MustExec("CREATE USER 'sock'@'%' IDENTIFIED WITH 'auth_socket'") + + // Compare only the start of the output as the salt changes every time. + rows = tk.MustQuery("SHOW CREATE USER 'sock'@'%'") + require.Equal(t, "CREATE USER 'sock'@'%' IDENTIFIED WITH 'auth_socket' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) + tk.MustExec("CREATE USER 'sock2'@'%' IDENTIFIED WITH 'auth_socket' AS 'sock3'") + + // Compare only the start of the output as the salt changes every time. + rows = tk.MustQuery("SHOW CREATE USER 'sock2'@'%'") + require.Equal(t, "CREATE USER 'sock2'@'%' IDENTIFIED WITH 'auth_socket' AS 'sock3' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) + + // Test ACCOUNT LOCK/UNLOCK. + tk.MustExec("CREATE USER 'lockness'@'%' IDENTIFIED BY 'monster' ACCOUNT LOCK") + rows = tk.MustQuery("SHOW CREATE USER 'lockness'@'%'") + require.Equal(t, "CREATE USER 'lockness'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*BC05309E7FE12AFD4EBB9FFE7E488A6320F12FF3' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT", rows.Rows()[0][0].(string)) + + // Test COMMENT and ATTRIBUTE. + tk.MustExec("CREATE USER commentUser COMMENT '1234'") + tk.MustQuery("SHOW CREATE USER commentUser").Check(testkit.Rows(`CREATE USER 'commentUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"comment": "1234"}'`)) + tk.MustExec(`CREATE USER attributeUser attribute '{"name": "Tom", "age": 19}'`) + tk.MustQuery("SHOW CREATE USER attributeUser").Check(testkit.Rows(`CREATE USER 'attributeUser'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"age": 19, "name": "Tom"}'`)) + + // Creating users with IDENTIFIED WITH 'tidb_auth_token'. + tk.MustExec(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' ATTRIBUTE '{"email": "user@pingcap.com"}'`) + tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"email": "user@pingcap.com"}'`)) + tk.MustExec(`ALTER USER 'token_user'@'%' REQUIRE token_issuer 'issuer-ABC'`) + tk.MustQuery("SHOW CREATE USER token_user").Check(testkit.Rows(`CREATE USER 'token_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE token_issuer issuer-ABC PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT ATTRIBUTE '{"email": "user@pingcap.com"}'`)) + + // create users with password reuse. + tk.MustExec(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 3 DAY`) + tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 3 DAY`)) + tk.MustExec(`ALTER USER 'reuse_user'@'%' PASSWORD HISTORY 50`) + tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 3 DAY`)) + tk.MustExec(`ALTER USER 'reuse_user'@'%' PASSWORD REUSE INTERVAL 31 DAY`) + tk.MustQuery("SHOW CREATE USER reuse_user").Check(testkit.Rows(`CREATE USER 'reuse_user'@'%' IDENTIFIED WITH 'tidb_auth_token' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY 50 PASSWORD REUSE INTERVAL 31 DAY`)) + + tk.MustExec("CREATE USER 'jeffrey1'@'localhost' PASSWORD EXPIRE") + tk.MustQuery("SHOW CREATE USER 'jeffrey1'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey1'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + tk.MustExec("CREATE USER 'jeffrey2'@'localhost' PASSWORD EXPIRE DEFAULT") + tk.MustQuery("SHOW CREATE USER 'jeffrey2'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey2'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + tk.MustExec("CREATE USER 'jeffrey3'@'localhost' PASSWORD EXPIRE NEVER") + tk.MustQuery("SHOW CREATE USER 'jeffrey3'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey3'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE NEVER ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + tk.MustExec("CREATE USER 'jeffrey4'@'localhost' PASSWORD EXPIRE INTERVAL 180 DAY") + tk.MustQuery("SHOW CREATE USER 'jeffrey4'@'localhost'").Check(testkit.Rows(`CREATE USER 'jeffrey4'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE INTERVAL 180 DAY ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + + tk.MustExec("CREATE USER failed_login_user") + tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT`)) + tk.MustExec("ALTER USER failed_login_user FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME 2") + tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME 2`)) + tk.MustExec("ALTER USER failed_login_user PASSWORD_LOCK_TIME UNBOUNDED") + tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED`)) + tk.MustExec("ALTER USER failed_login_user comment 'testcomment'") + tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED ATTRIBUTE '{"comment": "testcomment"}'`)) + tk.MustExec("ALTER USER failed_login_user ATTRIBUTE '{\"attribute\": \"testattribute\"}'") + tk.MustQuery("SHOW CREATE USER failed_login_user").Check(testkit.Rows(`CREATE USER 'failed_login_user'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT FAILED_LOGIN_ATTEMPTS 1 PASSWORD_LOCK_TIME UNBOUNDED ATTRIBUTE '{"attribute": "testattribute", "comment": "testcomment"}'`)) +} + +func TestUnprivilegedShow(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE testshow") + tk.MustExec("USE testshow") + tk.MustExec("CREATE TABLE t1 (a int)") + tk.MustExec("CREATE TABLE t2 (a int)") + + tk.MustExec(`CREATE USER 'lowprivuser'`) // no grants + + tk.Session().Auth(&auth.UserIdentity{Username: "lowprivuser", Hostname: "192.168.0.1", AuthUsername: "lowprivuser", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + rs, err := tk.Exec("SHOW TABLE STATUS FROM testshow") + require.NoError(t, err) + require.NotNil(t, rs) + + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + tk.MustExec("GRANT ALL ON testshow.t1 TO 'lowprivuser'") + tk.Session().Auth(&auth.UserIdentity{Username: "lowprivuser", Hostname: "192.168.0.1", AuthUsername: "lowprivuser", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + + is := dom.InfoSchema() + tblInfo, err := is.TableByName(model.NewCIStr("testshow"), model.NewCIStr("t1")) + require.NoError(t, err) + createTime := model.TSConvert2Time(tblInfo.Meta().UpdateTS).Format(time.DateTime) + + tk.MustQuery("show table status from testshow").Check(testkit.Rows(fmt.Sprintf("t1 InnoDB 10 Compact 0 0 0 0 0 0 %s utf8mb4_bin ", createTime))) +} + +func TestCollation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + rs, err := tk.Exec("show collation;") + require.NoError(t, err) + fields := rs.Fields() + require.Equal(t, mysql.TypeVarchar, fields[0].Column.GetType()) + require.Equal(t, mysql.TypeVarchar, fields[1].Column.GetType()) + require.Equal(t, mysql.TypeLonglong, fields[2].Column.GetType()) + require.Equal(t, mysql.TypeVarchar, fields[3].Column.GetType()) + require.Equal(t, mysql.TypeVarchar, fields[4].Column.GetType()) + require.Equal(t, mysql.TypeLonglong, fields[5].Column.GetType()) +} + +func TestShowTableStatus(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a bigint);`) + + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "192.168.0.1", AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + + // It's not easy to test the result contents because every time the test runs, "Create_time" changed. + tk.MustExec("show table status;") + rs, err := tk.Exec("show table status;") + require.NoError(t, err) + require.NotNil(t, rs) + rows, err := session.GetRows4Test(context.Background(), tk.Session(), rs) + require.NoError(t, err) + err = rs.Close() + require.NoError(t, err) + require.Equal(t, 1, len(rows)) + + for i := range rows { + row := rows[i] + require.Equal(t, "t", row.GetString(0)) + require.Equal(t, "InnoDB", row.GetString(1)) + require.Equal(t, int64(10), row.GetInt64(2)) + require.Equal(t, "Compact", row.GetString(3)) + } + tk.MustExec(`drop table if exists tp;`) + tk.MustExec(`create table tp (a int) + partition by range(a) + ( partition p0 values less than (10), + partition p1 values less than (20), + partition p2 values less than (maxvalue) + );`) + rs, err = tk.Exec("show table status from test like 'tp';") + require.NoError(t, err) + rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) + require.NoError(t, err) + require.Equal(t, "partitioned", rows[0].GetString(16)) + + tk.MustExec("create database UPPER_CASE") + tk.MustExec("use UPPER_CASE") + tk.MustExec("create table t (i int)") + rs, err = tk.Exec("show table status") + require.NoError(t, err) + require.NotNil(t, rs) + rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) + require.NoError(t, err) + err = rs.Close() + require.NoError(t, err) + require.Equal(t, 1, len(rows)) + + tk.MustExec("use upper_case") + rs, err = tk.Exec("show table status") + require.NoError(t, err) + require.NotNil(t, rs) + rows, err = session.GetRows4Test(context.Background(), tk.Session(), rs) + require.NoError(t, err) + err = rs.Close() + require.NoError(t, err) + require.Equal(t, 1, len(rows)) + + tk.MustExec("drop database UPPER_CASE") +} + +func TestShowSlow(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // The test result is volatile, because + // 1. Slow queries is stored in domain, which may be affected by other tests. + // 2. Collecting slow queries is a asynchronous process, check immediately may not get the expected result. + // 3. Make slow query like "select sleep(1)" would slow the CI. + // So, we just cover the code but do not check the result. + tk.MustQuery(`admin show slow recent 3`) + tk.MustQuery(`admin show slow top 3`) + tk.MustQuery(`admin show slow top internal 3`) + tk.MustQuery(`admin show slow top all 3`) +} + +func TestShowCreateStmtIgnoreLocalTemporaryTables(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // SHOW CREATE VIEW ignores local temporary table with the same name + tk.MustExec("drop view if exists v1") + tk.MustExec("create view v1 as select 1") + tk.MustExec("create temporary table v1 (a int)") + tk.MustQuery("show create table v1").Check(testkit.RowsWithSep("|", + ""+ + "v1 CREATE TEMPORARY TABLE `v1` (\n"+ + " `a` int(11) DEFAULT NULL\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + tk.MustExec("drop view v1") + err := tk.ExecToErr("show create view v1") + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + + // SHOW CREATE SEQUENCE ignores local temporary table with the same name + tk.MustExec("drop view if exists seq1") + tk.MustExec("create sequence seq1") + tk.MustExec("create temporary table seq1 (a int)") + tk.MustQuery("show create sequence seq1").Check(testkit.RowsWithSep("|", + "seq1 CREATE SEQUENCE `seq1` start with 1 minvalue 1 maxvalue 9223372036854775806 increment by 1 cache 1000 nocycle ENGINE=InnoDB", + )) + tk.MustExec("drop sequence seq1") + err = tk.ExecToErr("show create sequence seq1") + require.True(t, infoschema.ErrTableNotExists.Equal(err)) +} + +func TestAutoRandomBase(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@allow_auto_random_explicit_insert = true") + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a bigint primary key auto_random(5), b int unique key auto_increment) auto_random_base = 100, auto_increment = 100") + tk.MustQuery("show create table t").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` bigint(20) NOT NULL /*T![auto_rand] AUTO_RANDOM(5) */,\n"+ + " `b` int(11) NOT NULL AUTO_INCREMENT,\n"+ + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n"+ + " UNIQUE KEY `b` (`b`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=100 /*T![auto_rand_base] AUTO_RANDOM_BASE=100 */", + )) + + tk.MustExec("insert into t(`a`) values (1000)") + tk.MustQuery("show create table t").Check(testkit.RowsWithSep("|", + ""+ + "t CREATE TABLE `t` (\n"+ + " `a` bigint(20) NOT NULL /*T![auto_rand] AUTO_RANDOM(5) */,\n"+ + " `b` int(11) NOT NULL AUTO_INCREMENT,\n"+ + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n"+ + " UNIQUE KEY `b` (`b`)\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=5100 /*T![auto_rand_base] AUTO_RANDOM_BASE=6001 */", + )) +} + +func TestAutoRandomWithLargeSignedShowTableRegions(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database if not exists auto_random_db;") + defer tk.MustExec("drop database if exists auto_random_db;") + tk.MustExec("use auto_random_db;") + tk.MustExec("drop table if exists t;") + + tk.MustExec("create table t (a bigint unsigned auto_random primary key clustered);") + tk.MustExec("set @@global.tidb_scatter_region=1;") + // 18446744073709541615 is MaxUint64 - 10000. + // 18446744073709551615 is the MaxUint64. + tk.MustQuery("split table t between (18446744073709541615) and (18446744073709551615) regions 2;"). + Check(testkit.Rows("1 1")) + startKey := tk.MustQuery("show table t regions;").Rows()[1][1].(string) + idx := strings.Index(startKey, "_r_") + require.False(t, idx == -1) + require.Falsef(t, startKey[idx+3] == '-', "actual key: %s", startKey) +} + +func TestShowEscape(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists `t``abl\"e`") + tk.MustExec("create table `t``abl\"e`(`c``olum\"n` int(11) primary key)") + tk.MustQuery("show create table `t``abl\"e`").Check(testkit.RowsWithSep("|", + ""+ + "t`abl\"e CREATE TABLE `t``abl\"e` (\n"+ + " `c``olum\"n` int(11) NOT NULL,\n"+ + " PRIMARY KEY (`c``olum\"n`) /*T![clustered_index] CLUSTERED */\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + // ANSI_QUOTES will change the SHOW output + tk.MustExec("set @old_sql_mode=@@sql_mode") + tk.MustExec("set sql_mode=ansi_quotes") + tk.MustQuery("show create table \"t`abl\"\"e\"").Check(testkit.RowsWithSep("|", + ""+ + "t`abl\"e CREATE TABLE \"t`abl\"\"e\" (\n"+ + " \"c`olum\"\"n\" int(11) NOT NULL,\n"+ + " PRIMARY KEY (\"c`olum\"\"n\") /*T![clustered_index] CLUSTERED */\n"+ + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin", + )) + + tk.MustExec("rename table \"t`abl\"\"e\" to t") + tk.MustExec("set sql_mode=@old_sql_mode") +} + +func TestShowBuiltin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + res := tk.MustQuery("show builtins;") + require.NotNil(t, res) + rows := res.Rows() + const builtinFuncNum = 291 + require.Equal(t, builtinFuncNum, len(rows)) + require.Equal(t, rows[0][0].(string), "abs") + require.Equal(t, rows[builtinFuncNum-1][0].(string), "yearweek") +} + +func TestShowClusterConfig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + var confItems [][]types.Datum + var confErr error + var confFunc executor.TestShowClusterConfigFunc = func() ([][]types.Datum, error) { + return confItems, confErr + } + tk.Session().SetValue(executor.TestShowClusterConfigKey, confFunc) + strs2Items := func(strs ...string) []types.Datum { + items := make([]types.Datum, 0, len(strs)) + for _, s := range strs { + items = append(items, types.NewStringDatum(s)) + } + return items + } + confItems = append(confItems, strs2Items("tidb", "127.0.0.1:1111", "log.level", "info")) + confItems = append(confItems, strs2Items("pd", "127.0.0.1:2222", "log.level", "info")) + confItems = append(confItems, strs2Items("tikv", "127.0.0.1:3333", "log.level", "info")) + tk.MustQuery("show config").Check(testkit.Rows( + "tidb 127.0.0.1:1111 log.level info", + "pd 127.0.0.1:2222 log.level info", + "tikv 127.0.0.1:3333 log.level info")) + tk.MustQuery("show config where type='tidb'").Check(testkit.Rows( + "tidb 127.0.0.1:1111 log.level info")) + tk.MustQuery("show config where type like '%ti%'").Check(testkit.Rows( + "tidb 127.0.0.1:1111 log.level info", + "tikv 127.0.0.1:3333 log.level info")) + + confErr = fmt.Errorf("something unknown error") + require.EqualError(t, tk.QueryToErr("show config"), confErr.Error()) +} + +func TestInvisibleCoprCacheConfig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + rows := tk.MustQuery("show variables like '%config%'").Rows() + require.Equal(t, 1, len(rows)) + configValue := rows[0][1].(string) + coprCacheVal := + "\t\t\"copr-cache\": {\n" + + "\t\t\t\"capacity-mb\": 1000\n" + + "\t\t},\n" + require.Equal(t, true, strings.Contains(configValue, coprCacheVal)) +} + +func TestEnableGlobalKillConfig(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + rows := tk.MustQuery("show variables like '%config%'").Rows() + require.Equal(t, 1, len(rows)) + configValue := rows[0][1].(string) + globalKillVal := "\"enable-global-kill\": true" + require.True(t, strings.Contains(configValue, globalKillVal)) +} + +func TestShowCreateTableWithIntegerDisplayLengthWarnings(t *testing.T) { + parsertypes.TiDBStrictIntegerDisplayWidth = true + defer func() { + parsertypes.TiDBStrictIntegerDisplayWidth = false + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int(2), b varchar(2))") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` int DEFAULT NULL,\n" + + " `b` varchar(2) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a bigint(10), b bigint)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` bigint DEFAULT NULL,\n" + + " `b` bigint DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a tinyint(5), b tinyint(2), c tinyint)") + // Here it will occur 2 warnings. + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` tinyint DEFAULT NULL,\n" + + " `b` tinyint DEFAULT NULL,\n" + + " `c` tinyint DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a smallint(5), b smallint)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` smallint DEFAULT NULL,\n" + + " `b` smallint DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a mediumint(5), b mediumint)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` mediumint DEFAULT NULL,\n" + + " `b` mediumint DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int1(1), b int2(2), c int3, d int4, e int8)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `a` tinyint(1) DEFAULT NULL,\n" + + " `b` smallint DEFAULT NULL,\n" + + " `c` mediumint DEFAULT NULL,\n" + + " `d` int DEFAULT NULL,\n" + + " `e` bigint DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int primary key, c1 bool, c2 int(10) zerofill)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1681 Integer display width is deprecated and will be removed in a future release.", + "Warning 1681 The ZEROFILL attribute is deprecated and will be removed in a future release. Use the LPAD function to zero-pad numbers, or store the formatted numbers in a CHAR column.", + )) + tk.MustQuery("show create table t").Check(testkit.Rows("t CREATE TABLE `t` (\n" + + " `id` int NOT NULL,\n" + + " `c1` tinyint(1) DEFAULT NULL,\n" + + " `c2` int(10) unsigned zerofill DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func TestShowVar(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + var showSQL string + sessionVars := make([]string, 0, len(variable.GetSysVars())) + globalVars := make([]string, 0, len(variable.GetSysVars())) + for _, v := range variable.GetSysVars() { + if v.Scope == variable.ScopeSession { + sessionVars = append(sessionVars, v.Name) + } else { + globalVars = append(globalVars, v.Name) + } + } + + // When ScopeSession only. `show global variables` must return empty. + sessionVarsStr := strings.Join(sessionVars, "','") + showSQL = "show variables where variable_name in('" + sessionVarsStr + "')" + res := tk.MustQuery(showSQL) + require.Len(t, res.Rows(), len(sessionVars)) + showSQL = "show global variables where variable_name in('" + sessionVarsStr + "')" + res = tk.MustQuery(showSQL) + require.Len(t, res.Rows(), 0) + + globalVarsStr := strings.Join(globalVars, "','") + showSQL = "show variables where variable_name in('" + globalVarsStr + "')" + res = tk.MustQuery(showSQL) + require.Len(t, res.Rows(), len(globalVars)) + showSQL = "show global variables where variable_name in('" + globalVarsStr + "')" + res = tk.MustQuery(showSQL) + require.Len(t, res.Rows(), len(globalVars)) + + // Test versions' related variables + res = tk.MustQuery("show variables like 'version%'") + for _, row := range res.Rows() { + line := fmt.Sprint(row) + if strings.HasPrefix(line, "version ") { + require.Equal(t, mysql.ServerVersion, line[len("version "):]) + } else if strings.HasPrefix(line, "version_comment ") { + require.Equal(t, variable.GetSysVar(variable.VersionComment), line[len("version_comment "):]) + } + } + + // Test case insensitive case for show session variables + tk.MustExec("SET @@SQL_MODE='NO_BACKSLASH_ESCAPES'") + tk.MustQuery("SHOW SESSION VARIABLES like 'sql_mode'").Check( + testkit.RowsWithSep("|", "sql_mode|NO_BACKSLASH_ESCAPES")) + tk.MustQuery("SHOW SESSION VARIABLES like 'SQL_MODE'").Check( + testkit.RowsWithSep("|", "sql_mode|NO_BACKSLASH_ESCAPES")) +} + +// TestShowPerformanceSchema tests for Issue 19231 +func TestShowPerformanceSchema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // Ideally we should create a new performance_schema table here with indices that we run the tests on. + // However, its not possible to create a new performance_schema table since its a special in memory table. + // Instead the test below uses the default index on the table. + tk.MustQuery("SHOW INDEX FROM performance_schema.events_statements_summary_by_digest").Check( + testkit.Rows("events_statements_summary_by_digest 0 SCHEMA_NAME 1 SCHEMA_NAME A 0 YES BTREE YES NO", + "events_statements_summary_by_digest 0 SCHEMA_NAME 2 DIGEST A 0 YES BTREE YES NO")) +} + +func TestShowCreatePlacementPolicy(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE PLACEMENT POLICY xyz PRIMARY_REGION='us-east-1' REGIONS='us-east-1,us-east-2' FOLLOWERS=4") + tk.MustQuery("SHOW CREATE PLACEMENT POLICY xyz").Check(testkit.Rows("xyz CREATE PLACEMENT POLICY `xyz` PRIMARY_REGION=\"us-east-1\" REGIONS=\"us-east-1,us-east-2\" FOLLOWERS=4")) + // non existent policy + err := tk.QueryToErr("SHOW CREATE PLACEMENT POLICY doesnotexist") + require.Equal(t, infoschema.ErrPlacementPolicyNotExists.GenWithStackByArgs("doesnotexist").Error(), err.Error()) + // alter and try second example + tk.MustExec("ALTER PLACEMENT POLICY xyz FOLLOWERS=4") + tk.MustQuery("SHOW CREATE PLACEMENT POLICY xyz").Check(testkit.Rows("xyz CREATE PLACEMENT POLICY `xyz` FOLLOWERS=4")) + tk.MustExec("DROP PLACEMENT POLICY xyz") +} + +func TestShowTemporaryTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create global temporary table t1 (id int) on commit delete rows") + tk.MustExec("create global temporary table t3 (i int primary key, j int) on commit delete rows") + // For issue https://github.com/pingcap/tidb/issues/24752 + tk.MustQuery("show create table t1").Check(testkit.Rows("t1 CREATE GLOBAL TEMPORARY TABLE `t1` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS")) + // No panic, fix issue https://github.com/pingcap/tidb/issues/24788 + expect := "CREATE GLOBAL TEMPORARY TABLE `t3` (\n" + + " `i` int(11) NOT NULL,\n" + + " `j` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS" + tk.MustQuery("show create table t3").Check(testkit.Rows("t3 " + expect)) + + // Verify that the `show create table` result can be used to build the table. + createTable := strings.ReplaceAll(expect, "t3", "t4") + tk.MustExec(createTable) + + // Cover auto increment column. + tk.MustExec(`CREATE GLOBAL TEMPORARY TABLE t5 ( + id int(11) NOT NULL AUTO_INCREMENT, + b int(11) NOT NULL, + pad varbinary(255) DEFAULT NULL, + PRIMARY KEY (id), + KEY b (b)) ON COMMIT DELETE ROWS`) + expect = "CREATE GLOBAL TEMPORARY TABLE `t5` (\n" + + " `id` int(11) NOT NULL AUTO_INCREMENT,\n" + + " `b` int(11) NOT NULL,\n" + + " `pad` varbinary(255) DEFAULT NULL,\n" + + " PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ON COMMIT DELETE ROWS" + tk.MustQuery("show create table t5").Check(testkit.Rows("t5 " + expect)) + + tk.MustExec("create temporary table t6 (i int primary key, j int)") + expect = "CREATE TEMPORARY TABLE `t6` (\n" + + " `i` int(11) NOT NULL,\n" + + " `j` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin" + tk.MustQuery("show create table t6").Check(testkit.Rows("t6 " + expect)) + tk.MustExec("create temporary table t7 (i int primary key auto_increment, j int)") + defer func() { + tk.MustExec("commit;") + }() + tk.MustExec("begin;") + tk.MustExec("insert into t7 (j) values (14)") + tk.MustExec("insert into t7 (j) values (24)") + tk.MustQuery("select * from t7").Check(testkit.Rows("1 14", "2 24")) + expect = "CREATE TEMPORARY TABLE `t7` (\n" + + " `i` int(11) NOT NULL AUTO_INCREMENT,\n" + + " `j` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`i`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=3" + tk.MustQuery("show create table t7").Check(testkit.Rows("t7 " + expect)) +} + +func TestShowCachedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (id int)") + tk.MustExec("alter table t1 cache") + tk.MustQuery("show create table t1").Check( + testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /* CACHED ON */")) + tk.MustQuery("select create_options from information_schema.tables where table_schema = 'test' and table_name = 't1'").Check( + testkit.Rows("cached=on")) + + tk.MustExec("alter table t1 nocache") + tk.MustQuery("show create table t1").Check( + testkit.Rows("t1 CREATE TABLE `t1` (\n" + + " `id` int(11) DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustQuery("select create_options from information_schema.tables where table_schema = 'test' and table_name = 't1'").Check( + testkit.Rows("")) +} + +func TestShowBindingCache(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int)") + tk.MustExec(`set global tidb_mem_quota_binding_cache = 1`) + tk.MustQuery("select @@global.tidb_mem_quota_binding_cache").Check(testkit.Rows("1")) + tk.MustExec("admin reload bindings;") + res := tk.MustQuery("show global bindings") + require.Equal(t, 0, len(res.Rows())) + + tk.MustExec("create global binding for select * from t using select * from t") + res = tk.MustQuery("show global bindings") + require.Equal(t, 0, len(res.Rows())) + + tk.MustExec(`set global tidb_mem_quota_binding_cache = default`) + tk.MustQuery("select @@global.tidb_mem_quota_binding_cache").Check(testkit.Rows("67108864")) + tk.MustExec("admin reload bindings") + res = tk.MustQuery("show global bindings") + require.Equal(t, 1, len(res.Rows())) + + tk.MustExec("create global binding for select * from t where a > 1 using select * from t where a > 1") + res = tk.MustQuery("show global bindings") + require.Equal(t, 2, len(res.Rows())) +} + +func TestShowBindingCacheStatus(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "0 0 0 Bytes 64 MB")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx_a(a), index idx_b(b))") + result := tk.MustQuery("show global bindings") + rows := result.Rows() + require.Equal(t, len(rows), 0) + tk.MustExec("create global binding for select * from t using select * from t") + + result = tk.MustQuery("show global bindings") + rows = result.Rows() + require.Equal(t, len(rows), 1) + + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "1 1 159 Bytes 64 MB")) + + tk.MustExec(`set global tidb_mem_quota_binding_cache = 250`) + tk.MustQuery(`select @@global.tidb_mem_quota_binding_cache`).Check(testkit.Rows("250")) + tk.MustExec("admin reload bindings;") + tk.MustExec("create global binding for select * from t where a > 1 using select * from t where a > 1") + result = tk.MustQuery("show global bindings") + rows = result.Rows() + require.Equal(t, len(rows), 1) + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "1 2 187 Bytes 250 Bytes")) + + tk.MustExec("drop global binding for select * from t where a > 1") + result = tk.MustQuery("show global bindings") + rows = result.Rows() + require.Equal(t, len(rows), 0) + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "0 1 0 Bytes 250 Bytes")) + + tk.MustExec("admin reload bindings") + result = tk.MustQuery("show global bindings") + rows = result.Rows() + require.Equal(t, len(rows), 1) + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "1 1 159 Bytes 250 Bytes")) + + tk.MustExec("create global binding for select * from t using select * from t use index(idx_a)") + + result = tk.MustQuery("show global bindings") + rows = result.Rows() + require.Equal(t, len(rows), 1) + + tk.MustQuery("show binding_cache status").Check(testkit.Rows( + "1 1 198 Bytes 250 Bytes")) +} + +func TestShowDatabasesLike(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{ + Username: "root", Hostname: "%"}, nil, nil, nil)) + + tk.MustExec("DROP DATABASE IF EXISTS `TEST_$1`") + tk.MustExec("DROP DATABASE IF EXISTS `test_$2`") + tk.MustExec("CREATE DATABASE `TEST_$1`;") + tk.MustExec("CREATE DATABASE `test_$2`;") + + tk.MustQuery("SHOW DATABASES LIKE 'TEST_%'").Check(testkit.Rows("TEST_$1", "test_$2")) + tk.MustQuery("SHOW DATABASES LIKE 'test_%'").Check(testkit.Rows("TEST_$1", "test_$2")) +} + +func TestShowTableStatusLike(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("DROP table IF EXISTS `T1`") + tk.MustExec("CREATE table `T1` (a int);") + rows := tk.MustQuery("SHOW table status LIKE 't1'").Rows() + require.Equal(t, "T1", rows[0][0]) + + tk.MustExec("DROP table IF EXISTS `Li_1`") + tk.MustExec("DROP table IF EXISTS `li_2`") + + tk.MustExec("CREATE table `Li_1` (a int);") + tk.MustExec("CREATE table `li_2` (a int);") + + rows = tk.MustQuery("SHOW table status LIKE 'li%'").Rows() + require.Equal(t, "Li_1", rows[0][0]) + require.Equal(t, "li_2", rows[1][0]) +} + +func TestShowCollationsLike(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{ + Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustQuery("SHOW COLLATION LIKE 'UTF8MB4_BI%'").Check(testkit.Rows("utf8mb4_bin utf8mb4 46 Yes Yes 1")) + tk.MustQuery("SHOW COLLATION LIKE 'utf8mb4_bi%'").Check(testkit.Rows("utf8mb4_bin utf8mb4 46 Yes Yes 1")) +} + +func TestShowLimitReturnRow(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t1(a int, b int, c int, d int, index idx_a(a), index idx_b(b))") + tk.MustExec("create table t2(a int, b int, c int, d int, index idx_a(a), index idx_b(b))") + tk.MustExec("INSERT INTO t1 VALUES(1,2,3,4)") + tk.MustExec("INSERT INTO t1 VALUES(4,3,1,2)") + tk.MustExec("SET @@sql_select_limit=1") + tk.MustExec("PREPARE stmt FROM \"SHOW COLUMNS FROM t1\"") + result := tk.MustQuery("EXECUTE stmt") + rows := result.Rows() + require.Equal(t, len(rows), 1) + + tk.MustExec("PREPARE stmt FROM \"select * FROM t1\"") + result = tk.MustQuery("EXECUTE stmt") + rows = result.Rows() + require.Equal(t, len(rows), 1) + + // Test case for other scenarios. + result = tk.MustQuery("SHOW ENGINES") + rows = result.Rows() + require.Equal(t, len(rows), 1) + + tk.MustQuery("SHOW DATABASES like '%SCHEMA'").Check(testkit.RowsWithSep("|", "INFORMATION_SCHEMA")) + + tk.MustQuery("SHOW TABLES where tables_in_test='t2'").Check(testkit.RowsWithSep("|", "t2")) + + result = tk.MustQuery("SHOW TABLE STATUS where name='t2'") + rows = result.Rows() + require.Equal(t, rows[0][0], "t2") + + tk.MustQuery("SHOW COLUMNS FROM t1 where Field ='d'").Check(testkit.RowsWithSep("|", ""+ + "d int(11) YES ")) + + tk.MustQuery("Show Charset where charset='gbk'").Check(testkit.RowsWithSep("|", ""+ + "gbk Chinese Internal Code Specification gbk_chinese_ci 2")) + + tk.MustQuery("Show Variables where variable_name ='max_allowed_packet'").Check(testkit.RowsWithSep("|", ""+ + "max_allowed_packet 67108864")) + + result = tk.MustQuery("SHOW status where variable_name ='server_id'") + rows = result.Rows() + require.Equal(t, rows[0][0], "server_id") + + tk.MustQuery("Show Collation where collation='utf8_bin'").Check(testkit.RowsWithSep("|", ""+ + "utf8_bin utf8 83 Yes Yes 1")) + + result = tk.MustQuery("show index from t1 where key_name='idx_b'") + rows = result.Rows() + require.Equal(t, rows[0][2], "idx_b") +} + +func TestShowBindingDigestField(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(id int, key(id))") + tk.MustExec("create table t2(id int, key(id))") + tk.MustExec("create binding for select * from t1, t2 where t1.id = t2.id using select /*+ merge_join(t1, t2)*/ * from t1, t2 where t1.id = t2.id") + result := tk.MustQuery("show bindings;") + rows := result.Rows()[0] + require.Equal(t, len(rows), 11) + require.Equal(t, rows[9], "ac1ceb4eb5c01f7c03e29b7d0d6ab567e563f4c93164184cde218f20d07fd77c") + tk.MustExec("drop binding for select * from t1, t2 where t1.id = t2.id") + result = tk.MustQuery("show bindings;") + require.Equal(t, len(result.Rows()), 0) + + tk.MustExec("create global binding for select * from t1, t2 where t1.id = t2.id using select /*+ merge_join(t1, t2)*/ * from t1, t2 where t1.id = t2.id") + result = tk.MustQuery("show global bindings;") + rows = result.Rows()[0] + require.Equal(t, len(rows), 11) + require.Equal(t, rows[9], "ac1ceb4eb5c01f7c03e29b7d0d6ab567e563f4c93164184cde218f20d07fd77c") + tk.MustExec("drop global binding for select * from t1, t2 where t1.id = t2.id") + result = tk.MustQuery("show global bindings;") + require.Equal(t, len(result.Rows()), 0) +} + +func TestShowPasswordVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = ''") + rs, err := tk.Exec("show variables like 'authentication_ldap_sasl_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], "") + rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_sasl_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], "") + + tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = password") + defer func() { + tk.MustExec("SET GLOBAL authentication_ldap_sasl_bind_root_pwd = ''") + }() + rs, err = tk.Exec("show variables like 'authentication_ldap_sasl_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.MaskPwd) + rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_sasl_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], variable.MaskPwd) + + tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = ''") + rs, err = tk.Exec("show variables like 'authentication_ldap_simple_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], "") + rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_simple_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], "") + + tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = password") + defer func() { + tk.MustExec("SET GLOBAL authentication_ldap_simple_bind_root_pwd = ''") + }() + + rs, err = tk.Exec("show variables like 'authentication_ldap_simple_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][1], variable.MaskPwd) + rs, err = tk.Exec("SELECT current_value FROM information_schema.variables_info WHERE VARIABLE_NAME LIKE 'authentication_ldap_simple_bind_root_pwd'") + require.NoError(t, err) + require.Equal(t, tk.ResultSetToResult(rs, "").Rows()[0][0], variable.MaskPwd) +} diff --git a/pkg/executor/test/simpletest/BUILD.bazel b/pkg/executor/test/simpletest/BUILD.bazel new file mode 100644 index 0000000000000..a94c47e4958dd --- /dev/null +++ b/pkg/executor/test/simpletest/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "simpletest_test", + timeout = "short", + srcs = [ + "chunk_reuse_test.go", + "main_test.go", + "simple_test.go", + ], + flaky = True, + race = "on", + shard_count = 36, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/util/dbterror/exeerrors", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/simpletest/chunk_reuse_test.go b/pkg/executor/test/simpletest/chunk_reuse_test.go similarity index 99% rename from executor/test/simpletest/chunk_reuse_test.go rename to pkg/executor/test/simpletest/chunk_reuse_test.go index bd8dce08c82c5..9478ea6ffa302 100644 --- a/executor/test/simpletest/chunk_reuse_test.go +++ b/pkg/executor/test/simpletest/chunk_reuse_test.go @@ -17,7 +17,7 @@ package simpletest import ( "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/executor/test/simpletest/main_test.go b/pkg/executor/test/simpletest/main_test.go similarity index 100% rename from executor/test/simpletest/main_test.go rename to pkg/executor/test/simpletest/main_test.go diff --git a/pkg/executor/test/simpletest/simple_test.go b/pkg/executor/test/simpletest/simple_test.go new file mode 100644 index 0000000000000..698208bc0e5d7 --- /dev/null +++ b/pkg/executor/test/simpletest/simple_test.go @@ -0,0 +1,1152 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package simpletest + +import ( + "context" + "strconv" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/stretchr/testify/require" + "go.opencensus.io/stats/view" +) + +func TestFlushTables(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("FLUSH TABLES") + err := tk.ExecToErr("FLUSH TABLES WITH READ LOCK") + require.Error(t, err) +} + +func TestUseDB(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test") + err := tk.ExecToErr("USE ``") + require.Truef(t, terror.ErrorEqual(core.ErrNoDB, err), "err %v", err) +} + +func TestStmtAutoNewTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + // Some statements are like DDL, they commit the previous txn automically. + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // Fix issue https://github.com/pingcap/tidb/issues/10705 + tk.MustExec("begin") + tk.MustExec("create user 'xxx'@'%';") + tk.MustExec("grant all privileges on *.* to 'xxx'@'%';") + + tk.MustExec("create table auto_new (id int)") + tk.MustExec("begin") + tk.MustExec("insert into auto_new values (1)") + tk.MustExec("revoke all privileges on *.* from 'xxx'@'%'") + tk.MustExec("rollback") // insert statement has already committed + tk.MustQuery("select * from auto_new").Check(testkit.Rows("1")) + + // Test the behavior when autocommit is false. + tk.MustExec("set autocommit = 0") + tk.MustExec("insert into auto_new values (2)") + tk.MustExec("create user 'yyy'@'%'") + tk.MustExec("rollback") + tk.MustQuery("select * from auto_new").Check(testkit.Rows("1", "2")) + + tk.MustExec("drop user 'yyy'@'%'") + tk.MustExec("insert into auto_new values (3)") + tk.MustExec("rollback") + tk.MustQuery("select * from auto_new").Check(testkit.Rows("1", "2")) +} + +func TestIssue9111(t *testing.T) { + store := testkit.CreateMockStore(t) + // CREATE USER / DROP USER fails if admin doesn't have insert privilege on `mysql.user` table. + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user 'user_admin'@'localhost';") + tk.MustExec("grant create user on *.* to 'user_admin'@'localhost';") + + // Create a new session. + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "user_admin", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + _, err = se.Execute(ctx, `create user test_create_user`) + require.NoError(t, err) + _, err = se.Execute(ctx, `drop user test_create_user`) + require.NoError(t, err) + + tk.MustExec("revoke create user on *.* from 'user_admin'@'localhost';") + tk.MustExec("grant insert, delete on mysql.user to 'user_admin'@'localhost';") + + _, err = se.Execute(ctx, `create user test_create_user`) + require.NoError(t, err) + _, err = se.Execute(ctx, `drop user test_create_user`) + require.NoError(t, err) + + _, err = se.Execute(ctx, `create role test_create_user`) + require.NoError(t, err) + _, err = se.Execute(ctx, `drop role test_create_user`) + require.NoError(t, err) + + tk.MustExec("drop user 'user_admin'@'localhost';") +} + +func TestRoleAtomic(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create role r2;") + err := tk.ExecToErr("create role r1, r2, r3") + require.Error(t, err) + // Check atomic create role. + result := tk.MustQuery(`SELECT user FROM mysql.User WHERE user in ('r1', 'r2', 'r3')`) + result.Check(testkit.Rows("r2")) + // Check atomic drop role. + err = tk.ExecToErr("drop role r1, r2, r3") + require.Error(t, err) + result = tk.MustQuery(`SELECT user FROM mysql.User WHERE user in ('r1', 'r2', 'r3')`) + result.Check(testkit.Rows("r2")) + tk.MustExec("drop role r2;") +} + +func TestExtendedStatsPrivileges(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("create user 'u1'@'%'") + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "u1", Hostname: "%"}, nil, nil, nil)) + ctx := context.Background() + _, err = se.Execute(ctx, "set session tidb_enable_extended_stats = on") + require.NoError(t, err) + _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") + require.Error(t, err) + require.Equal(t, "[planner:1142]ALTER command denied to user 'u1'@'%' for table 't'", err.Error()) + tk.MustExec("grant alter on test.* to 'u1'@'%'") + _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") + require.Error(t, err) + require.Equal(t, "[planner:1142]ADD STATS_EXTENDED command denied to user 'u1'@'%' for table 't'", err.Error()) + tk.MustExec("grant select on test.* to 'u1'@'%'") + _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") + require.Error(t, err) + require.Equal(t, "[planner:1142]ADD STATS_EXTENDED command denied to user 'u1'@'%' for table 'stats_extended'", err.Error()) + tk.MustExec("grant insert on mysql.stats_extended to 'u1'@'%'") + _, err = se.Execute(ctx, "alter table test.t add stats_extended s1 correlation(a,b)") + require.NoError(t, err) + + _, err = se.Execute(ctx, "use test") + require.NoError(t, err) + _, err = se.Execute(ctx, "alter table t drop stats_extended s1") + require.Error(t, err) + require.Equal(t, "[planner:1142]DROP STATS_EXTENDED command denied to user 'u1'@'%' for table 'stats_extended'", err.Error()) + tk.MustExec("grant update on mysql.stats_extended to 'u1'@'%'") + _, err = se.Execute(ctx, "alter table t drop stats_extended s1") + require.NoError(t, err) + tk.MustExec("drop user 'u1'@'%'") +} + +func TestIssue17247(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user 'issue17247'") + tk.MustExec("grant CREATE USER on *.* to 'issue17247'") + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "issue17247", Hostname: "%"}, nil, nil, nil)) + tk1.MustExec("ALTER USER USER() IDENTIFIED BY 'xxx'") + tk1.MustExec("ALTER USER CURRENT_USER() IDENTIFIED BY 'yyy'") + tk1.MustExec("ALTER USER CURRENT_USER IDENTIFIED BY 'zzz'") + tk.MustExec("ALTER USER 'issue17247'@'%' IDENTIFIED BY 'kkk'") + tk.MustExec("ALTER USER 'issue17247'@'%' IDENTIFIED BY PASSWORD '*B50FBDB37F1256824274912F2A1CE648082C3F1F'") + // Wrong grammar + _, err := tk1.Exec("ALTER USER USER() IDENTIFIED BY PASSWORD '*B50FBDB37F1256824274912F2A1CE648082C3F1F'") + require.Error(t, err) +} + +// Close issue #23649. +// See https://github.com/pingcap/tidb/issues/23649 +func TestIssue23649(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("DROP USER IF EXISTS issue23649;") + tk.MustExec("CREATE USER issue23649;") + err := tk.ExecToErr("GRANT bogusrole to issue23649;") + require.Equal(t, "[executor:3523]Unknown authorization ID `bogusrole`@`%`", err.Error()) + err = tk.ExecToErr("GRANT bogusrole to nonexisting;") + require.Equal(t, "[executor:3523]Unknown authorization ID `bogusrole`@`%`", err.Error()) +} + +func TestSetCurrentUserPwd(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER issue28534;") + defer func() { + tk.MustExec("DROP USER IF EXISTS issue28534;") + }() + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "issue28534", Hostname: "localhost", CurrentUser: true, AuthUsername: "issue28534", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec(`SET PASSWORD FOR CURRENT_USER() = "43582eussi"`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="issue28534"`) + result.Check(testkit.Rows(auth.EncodePassword("43582eussi"))) +} + +func TestShowGrantsAfterDropRole(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER u29473") + defer tk.MustExec("DROP USER IF EXISTS u29473") + + tk.MustExec("CREATE ROLE r29473") + tk.MustExec("GRANT r29473 TO u29473") + tk.MustExec("GRANT CREATE USER ON *.* TO u29473") + + tk.Session().Auth(&auth.UserIdentity{Username: "u29473", Hostname: "%"}, nil, nil, nil) + tk.MustExec("SET ROLE r29473") + tk.MustExec("DROP ROLE r29473") + tk.MustQuery("SHOW GRANTS").Check(testkit.Rows("GRANT CREATE USER ON *.* TO 'u29473'@'%'")) +} + +func TestPrivilegesAfterDropUser(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(id int, v int)") + defer tk.MustExec("drop table t1") + + tk.MustExec("CREATE USER u1 require ssl") + defer tk.MustExec("DROP USER IF EXISTS u1") + + tk.MustExec("GRANT CREATE ON test.* TO u1") + tk.MustExec("GRANT UPDATE ON test.t1 TO u1") + tk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO u1") + tk.MustExec("GRANT SELECT(v), UPDATE(v) on test.t1 TO u1") + + tk.MustQuery("SELECT COUNT(1) FROM mysql.global_grants WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) + tk.MustQuery("SELECT COUNT(1) FROM mysql.global_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) + tk.MustQuery("SELECT COUNT(1) FROM mysql.tables_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) + tk.MustQuery("SELECT COUNT(1) FROM mysql.columns_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows("1")) + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) + tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'u1'@'%'", + "GRANT CREATE ON `test`.* TO 'u1'@'%'", + "GRANT UPDATE ON `test`.`t1` TO 'u1'@'%'", + "GRANT SELECT(v), UPDATE(v) ON `test`.`t1` TO 'u1'@'%'", + "GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'u1'@'%'", + )) + + tk.MustExec("DROP USER u1") + err := tk.QueryToErr("SHOW GRANTS FOR u1") + require.Equal(t, "[privilege:1141]There is no such grant defined for user 'u1' on host '%'", err.Error()) + tk.MustQuery("SELECT * FROM mysql.global_grants WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM mysql.global_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM mysql.tables_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) + tk.MustQuery("SELECT * FROM mysql.columns_priv WHERE USER='u1' AND HOST='%'").Check(testkit.Rows()) +} + +func TestDropRoleAfterRevoke(t *testing.T) { + store := testkit.CreateMockStore(t) + // issue 29781 + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil) + + tk.MustExec("create role r1, r2, r3;") + defer tk.MustExec("drop role if exists r1, r2, r3;") + tk.MustExec("grant r1,r2,r3 to current_user();") + tk.MustExec("set role all;") + tk.MustExec("revoke r1, r3 from root;") + tk.MustExec("drop role r1;") +} + +func TestUserWithSetNames(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("set names gbk;") + + tk.MustExec("drop user if exists '\xd2\xbb'@'localhost';") + tk.MustExec("create user '\xd2\xbb'@'localhost' IDENTIFIED BY '\xd2\xbb';") + + result := tk.MustQuery("SELECT authentication_string FROM mysql.User WHERE User='\xd2\xbb' and Host='localhost';") + result.Check(testkit.Rows(auth.EncodePassword("一"))) + + tk.MustExec("ALTER USER '\xd2\xbb'@'localhost' IDENTIFIED BY '\xd2\xbb\xd2\xbb';") + result = tk.MustQuery("SELECT authentication_string FROM mysql.User WHERE User='\xd2\xbb' and Host='localhost';") + result.Check(testkit.Rows(auth.EncodePassword("一一"))) + + tk.MustExec("RENAME USER '\xd2\xbb'@'localhost' to '\xd2\xbb'") + + tk.MustExec("drop user '\xd2\xbb';") +} + +func TestStatementsCauseImplicitCommit(t *testing.T) { + // Test some of the implicit commit statements. + // See https://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("create table ic (id int primary key)") + + cases := []string{ + "create table xx (id int)", + "create user 'xx'@'127.0.0.1'", + "grant SELECT on test.ic to 'xx'@'127.0.0.1'", + "flush privileges", + "analyze table ic", + } + for i, sql := range cases { + tk.MustExec("begin") + tk.MustExec("insert into ic values (?)", i) + tk.MustExec(sql) + tk.MustQuery("select * from ic where id = ?", i).Check(testkit.Rows(strconv.FormatInt(int64(i), 10))) + // Clean up data + tk.MustExec("delete from ic") + } +} + +func TestDo(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("do 1, @a:=1") + tk.MustQuery("select @a").Check(testkit.Rows("1")) + + tk.MustExec("use test") + tk.MustExec("create table t (i int)") + tk.MustExec("insert into t values (1)") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk.MustQuery("select * from t").Check(testkit.Rows("1")) + tk.MustExec("do @a := (select * from t where i = 1)") + tk2.MustExec("insert into t values (2)") + tk.MustQuery("select * from t").Check(testkit.Rows("1", "2")) +} + +func TestDoWithAggFunc(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("DO sum(1)") + tk.MustExec("DO avg(@e+@f)") + tk.MustExec("DO GROUP_CONCAT(NULLIF(ELT(1, @e), 2.0) ORDER BY 1)") +} + +func TestSetRoleAllCorner(t *testing.T) { + store := testkit.CreateMockStore(t) + // For user with no role, `SET ROLE ALL` should active + // a empty slice, rather than nil. + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user set_role_all") + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "set_role_all", Hostname: "localhost"}, nil, nil, nil)) + ctx := context.Background() + _, err = se.Execute(ctx, `set role all`) + require.NoError(t, err) + _, err = se.Execute(ctx, `select current_role`) + require.NoError(t, err) +} + +func TestCreateRole(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user testCreateRole;") + tk.MustExec("grant CREATE USER on *.* to testCreateRole;") + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testCreateRole", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + _, err = se.Execute(ctx, `create role test_create_role;`) + require.NoError(t, err) + tk.MustExec("revoke CREATE USER on *.* from testCreateRole;") + tk.MustExec("drop role test_create_role;") + tk.MustExec("grant CREATE ROLE on *.* to testCreateRole;") + _, err = se.Execute(ctx, `create role test_create_role;`) + require.NoError(t, err) + tk.MustExec("drop role test_create_role;") + _, err = se.Execute(ctx, `create user test_create_role;`) + require.Error(t, err) + tk.MustExec("drop user testCreateRole;") +} + +func TestDropRole(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user testCreateRole;") + tk.MustExec("create user test_create_role;") + tk.MustExec("grant CREATE USER on *.* to testCreateRole;") + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testCreateRole", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + _, err = se.Execute(ctx, `drop role test_create_role;`) + require.NoError(t, err) + tk.MustExec("revoke CREATE USER on *.* from testCreateRole;") + tk.MustExec("create role test_create_role;") + tk.MustExec("grant DROP ROLE on *.* to testCreateRole;") + _, err = se.Execute(ctx, `drop role test_create_role;`) + require.NoError(t, err) + tk.MustExec("create user test_create_role;") + _, err = se.Execute(ctx, `drop user test_create_role;`) + require.Error(t, err) + tk.MustExec("drop user testCreateRole;") + tk.MustExec("drop user test_create_role;") +} + +func TestTransaction(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("begin") + ctx := tk.Session() + require.True(t, inTxn(ctx)) + tk.MustExec("commit") + require.False(t, inTxn(ctx)) + tk.MustExec("begin") + require.True(t, inTxn(ctx)) + tk.MustExec("rollback") + require.False(t, inTxn(ctx)) + + // Test that begin implicitly commits previous transaction. + tk.MustExec("use test") + tk.MustExec("create table txn (a int)") + tk.MustExec("begin") + tk.MustExec("insert txn values (1)") + tk.MustExec("begin") + tk.MustExec("rollback") + tk.MustQuery("select * from txn").Check(testkit.Rows("1")) + + // Test that DDL implicitly commits previous transaction. + tk.MustExec("begin") + tk.MustExec("insert txn values (2)") + tk.MustExec("create table txn2 (a int)") + tk.MustExec("rollback") + tk.MustQuery("select * from txn").Check(testkit.Rows("1", "2")) +} + +func inTxn(ctx sessionctx.Context) bool { + return (ctx.GetSessionVars().Status & mysql.ServerStatusInTrans) > 0 +} + +func TestIssue33144(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + //Create role + tk.MustExec("create role 'r1' ;") + + sessionVars := tk.Session().GetSessionVars() + sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "%"} + + //Grant role to current_user() + tk.MustExec("grant 'r1' to current_user();") + //Revoke role from current_user() + tk.MustExec("revoke 'r1' from current_user();") + + //Grant role to current_user(),current_user() + tk.MustExec("grant 'r1' to current_user(),current_user();") + //Revoke role from current_user(),current_user() + tk.MustExec("revoke 'r1' from current_user(),current_user();") + + //Drop role + tk.MustExec("drop role 'r1' ;") +} + +func TestRole(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // Make sure user test not in mysql.User. + result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(nil) + + // Test for DROP ROLE. + createRoleSQL := `CREATE ROLE 'test'@'localhost';` + tk.MustExec(createRoleSQL) + // Make sure user test in mysql.User. + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword(""))) + // Insert relation into mysql.role_edges + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test','%','root')") + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test1','localhost','test1')") + // Insert relation into mysql.default_roles + tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('%','root','localhost','test')") + tk.MustExec("insert into mysql.default_roles (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) values ('localhost','test','%','test1')") + + dropUserSQL := `DROP ROLE IF EXISTS 'test'@'localhost' ;` + err := tk.ExecToErr(dropUserSQL) + require.NoError(t, err) + + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE TO_USER="test" and TO_HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.role_edges WHERE FROM_USER="test" and FROM_HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE USER="test" and HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE DEFAULT_ROLE_USER="test" and DEFAULT_ROLE_HOST="localhost"`) + result.Check(nil) + + // Test for GRANT ROLE + createRoleSQL = `CREATE ROLE 'r_1'@'localhost', 'r_2'@'localhost', 'r_3'@'localhost';` + tk.MustExec(createRoleSQL) + grantRoleSQL := `GRANT 'r_1'@'localhost' TO 'r_2'@'localhost';` + tk.MustExec(grantRoleSQL) + result = tk.MustQuery(`SELECT TO_USER FROM mysql.role_edges WHERE FROM_USER="r_1" and FROM_HOST="localhost"`) + result.Check(testkit.Rows("r_2")) + + grantRoleSQL = `GRANT 'r_1'@'localhost' TO 'r_3'@'localhost', 'r_4'@'localhost';` + err = tk.ExecToErr(grantRoleSQL) + require.Error(t, err) + + // Test grant role for current_user(); + sessionVars := tk.Session().GetSessionVars() + originUser := sessionVars.User + sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "%"} + tk.MustExec("grant 'r_1'@'localhost' to current_user();") + tk.MustExec("revoke 'r_1'@'localhost' from 'root'@'%';") + sessionVars.User = originUser + + result = tk.MustQuery(`SELECT FROM_USER FROM mysql.role_edges WHERE TO_USER="r_3" and TO_HOST="localhost"`) + result.Check(nil) + + dropRoleSQL := `DROP ROLE IF EXISTS 'r_1'@'localhost' ;` + tk.MustExec(dropRoleSQL) + dropRoleSQL = `DROP ROLE IF EXISTS 'r_2'@'localhost' ;` + tk.MustExec(dropRoleSQL) + dropRoleSQL = `DROP ROLE IF EXISTS 'r_3'@'localhost' ;` + tk.MustExec(dropRoleSQL) + + // Test for revoke role + createRoleSQL = `CREATE ROLE 'test'@'localhost', r_1, r_2;` + tk.MustExec(createRoleSQL) + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('localhost','test','%','root')") + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_1','%','root')") + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_2','%','root')") + tk.MustExec("flush privileges") + tk.MustExec("SET DEFAULT ROLE r_1, r_2 TO root") + err = tk.ExecToErr("revoke test@localhost, r_1 from root;") + require.NoError(t, err) + err = tk.ExecToErr("revoke `r_2`@`%` from root, u_2;") + require.Error(t, err) + err = tk.ExecToErr("revoke `r_2`@`%` from root;") + require.NoError(t, err) + err = tk.ExecToErr("revoke `r_1`@`%` from root;") + require.NoError(t, err) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE DEFAULT_ROLE_USER="test" and DEFAULT_ROLE_HOST="localhost"`) + result.Check(nil) + result = tk.MustQuery(`SELECT * FROM mysql.default_roles WHERE USER="root" and HOST="%"`) + result.Check(nil) + dropRoleSQL = `DROP ROLE 'test'@'localhost', r_1, r_2;` + tk.MustExec(dropRoleSQL) + + ctx := tk.Session().(sessionctx.Context) + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "test1", Hostname: "localhost"} + require.NotNil(t, tk.ExecToErr("SET ROLE role1, role2")) + tk.MustExec("SET ROLE ALL") + tk.MustExec("SET ROLE ALL EXCEPT role1, role2") + tk.MustExec("SET ROLE DEFAULT") + tk.MustExec("SET ROLE NONE") +} + +func TestRoleAdmin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER 'testRoleAdmin';") + tk.MustExec("CREATE ROLE 'targetRole';") + + // Create a new session. + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testRoleAdmin", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + _, err = se.Execute(ctx, "GRANT `targetRole` TO `testRoleAdmin`;") + require.Error(t, err) + + tk.MustExec("GRANT SUPER ON *.* TO `testRoleAdmin`;") + _, err = se.Execute(ctx, "GRANT `targetRole` TO `testRoleAdmin`;") + require.NoError(t, err) + _, err = se.Execute(ctx, "REVOKE `targetRole` FROM `testRoleAdmin`;") + require.NoError(t, err) + + tk.MustExec("DROP USER 'testRoleAdmin';") + tk.MustExec("DROP ROLE 'targetRole';") +} + +func TestDefaultRole(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + createRoleSQL := `CREATE ROLE r_1, r_2, r_3, u_1;` + tk.MustExec(createRoleSQL) + + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_1','%','u_1')") + tk.MustExec("insert into mysql.role_edges (FROM_HOST,FROM_USER,TO_HOST,TO_USER) values ('%','r_2','%','u_1')") + + tk.MustExec("flush privileges;") + + setRoleSQL := `SET DEFAULT ROLE r_3 TO u_1;` + err := tk.ExecToErr(setRoleSQL) + require.Error(t, err) + + setRoleSQL = `SET DEFAULT ROLE r_1 TO u_1000;` + err = tk.ExecToErr(setRoleSQL) + require.Error(t, err) + + setRoleSQL = `SET DEFAULT ROLE r_1, r_3 TO u_1;` + err = tk.ExecToErr(setRoleSQL) + require.Error(t, err) + + setRoleSQL = `SET DEFAULT ROLE r_1 TO u_1;` + err = tk.ExecToErr(setRoleSQL) + require.NoError(t, err) + result := tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) + result.Check(testkit.Rows("r_1")) + setRoleSQL = `SET DEFAULT ROLE r_2 TO u_1;` + err = tk.ExecToErr(setRoleSQL) + require.NoError(t, err) + result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) + result.Check(testkit.Rows("r_2")) + + setRoleSQL = `SET DEFAULT ROLE ALL TO u_1;` + err = tk.ExecToErr(setRoleSQL) + require.NoError(t, err) + result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) + result.Check(testkit.Rows("r_1", "r_2")) + + setRoleSQL = `SET DEFAULT ROLE NONE TO u_1;` + err = tk.ExecToErr(setRoleSQL) + require.NoError(t, err) + result = tk.MustQuery(`SELECT DEFAULT_ROLE_USER FROM mysql.default_roles WHERE USER="u_1"`) + result.Check(nil) + + dropRoleSQL := `DROP USER r_1, r_2, r_3, u_1;` + tk.MustExec(dropRoleSQL) +} + +func TestSetDefaultRoleAll(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user test_all;") + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "test_all", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + _, err = se.Execute(ctx, "set default role all to test_all;") + require.NoError(t, err) +} + +func TestUser(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + // Make sure user test not in mysql.User. + result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(nil) + // Create user test. + createUserSQL := `CREATE USER 'test'@'localhost' IDENTIFIED BY '123';` + tk.MustExec(createUserSQL) + // Make sure user test in mysql.User. + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("123"))) + // Create duplicate user with IfNotExists will be success. + createUserSQL = `CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY '123';` + tk.MustExec(createUserSQL) + + // Create duplicate user without IfNotExists will cause error. + createUserSQL = `CREATE USER 'test'@'localhost' IDENTIFIED BY '123';` + tk.MustGetErrCode(createUserSQL, mysql.ErrCannotUser) + createUserSQL = `CREATE USER IF NOT EXISTS 'test'@'localhost' IDENTIFIED BY '123';` + tk.MustExec(createUserSQL) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3163|User 'test'@'localhost' already exists.")) + dropUserSQL := `DROP USER IF EXISTS 'test'@'localhost' ;` + tk.MustExec(dropUserSQL) + // Create user test. + createUserSQL = `CREATE USER 'test1'@'localhost';` + tk.MustExec(createUserSQL) + // Make sure user test in mysql.User. + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword(""))) + dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost' ;` + tk.MustExec(dropUserSQL) + + // Test create/alter user with `tidb_auth_token` + tk.MustExec(`CREATE USER token_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) + tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-abc")) + tk.MustExec(`ALTER USER token_user REQUIRE token_issuer 'issuer-123'`) + tk.MustQuery(`SELECT plugin, token_issuer FROM mysql.user WHERE user = 'token_user'`).Check(testkit.Rows("tidb_auth_token issuer-123")) + tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'tidb_auth_token'`) + tk.MustExec(`CREATE USER token_user1 IDENTIFIED WITH 'tidb_auth_token'`) + tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is needed for 'tidb_auth_token' user, please use 'alter user' to declare it")) + tk.MustExec(`CREATE USER temp_user IDENTIFIED WITH 'mysql_native_password' BY '1234' REQUIRE token_issuer 'issuer-abc'`) + tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is not needed for 'mysql_native_password' user")) + tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'mysql_native_password' REQUIRE token_issuer 'issuer-abc'`) + tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|TOKEN_ISSUER is not needed for the auth plugin")) + tk.MustExec(`ALTER USER temp_user IDENTIFIED WITH 'tidb_auth_token'`) + tk.MustQuery(`show warnings`).Check(testkit.RowsWithSep("|", "Warning|1105|Auth plugin 'tidb_auth_plugin' needs TOKEN_ISSUER")) + tk.MustExec(`ALTER USER token_user REQUIRE SSL`) + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'mysql_native_password' BY '1234'`) + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + tk.MustExec(`ALTER USER token_user IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc'`) + tk.MustQuery(`show warnings`).Check(testkit.Rows()) + + // Test alter user. + createUserSQL = `CREATE USER 'test1'@'localhost' IDENTIFIED BY '123', 'test2'@'localhost' IDENTIFIED BY '123', 'test3'@'localhost' IDENTIFIED BY '123', 'test4'@'localhost' IDENTIFIED BY '123';` + tk.MustExec(createUserSQL) + alterUserSQL := `ALTER USER 'test1'@'localhost' IDENTIFIED BY '111';` + tk.MustExec(alterUserSQL) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("111"))) + alterUserSQL = `ALTER USER 'test_not_exist'@'localhost' IDENTIFIED BY '111';` + tk.MustGetErrCode(alterUserSQL, mysql.ErrCannotUser) + alterUserSQL = `ALTER USER 'test1'@'localhost' IDENTIFIED BY '222', 'test_not_exist'@'localhost' IDENTIFIED BY '111';` + tk.MustGetErrCode(alterUserSQL, mysql.ErrCannotUser) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("111"))) + alterUserSQL = `ALTER USER 'test4'@'localhost' IDENTIFIED WITH 'auth_socket';` + tk.MustExec(alterUserSQL) + result = tk.MustQuery(`SELECT plugin FROM mysql.User WHERE User="test4" and Host="localhost"`) + result.Check(testkit.Rows("auth_socket")) + + alterUserSQL = `ALTER USER IF EXISTS 'test2'@'localhost' IDENTIFIED BY '222', 'test_not_exist'@'localhost' IDENTIFIED BY '1';` + tk.MustExec(alterUserSQL) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User 'test_not_exist'@'localhost' does not exist.")) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test2" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("222"))) + alterUserSQL = `ALTER USER IF EXISTS'test_not_exist'@'localhost' IDENTIFIED BY '1', 'test3'@'localhost' IDENTIFIED BY '333';` + tk.MustExec(alterUserSQL) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User 'test_not_exist'@'localhost' does not exist.")) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test3" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("333"))) + + // Test alter user user(). + alterUserSQL = `ALTER USER USER() IDENTIFIED BY '1';` + err := tk.ExecToErr(alterUserSQL) + require.Truef(t, terror.ErrorEqual(err, errors.New("Session user is empty")), "err %v", err) + sess, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(sess) + ctx := tk.Session().(sessionctx.Context) + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "test1", Hostname: "localhost", AuthHostname: "localhost"} + tk.MustExec(alterUserSQL) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="test1" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("1"))) + dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` + tk.MustExec(dropUserSQL) + + // Test drop user if exists. + createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` + tk.MustExec(createUserSQL) + dropUserSQL = `DROP USER IF EXISTS 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost' ;` + tk.MustExec(dropUserSQL) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Note|3162|User test2@localhost does not exist.")) + + // Test negative cases without IF EXISTS. + createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` + tk.MustExec(createUserSQL) + dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` + tk.MustGetErrCode(dropUserSQL, mysql.ErrCannotUser) + dropUserSQL = `DROP USER 'test3'@'localhost';` + tk.MustExec(dropUserSQL) + dropUserSQL = `DROP USER 'test1'@'localhost';` + tk.MustExec(dropUserSQL) + // Test positive cases without IF EXISTS. + createUserSQL = `CREATE USER 'test1'@'localhost', 'test3'@'localhost';` + tk.MustExec(createUserSQL) + dropUserSQL = `DROP USER 'test1'@'localhost', 'test3'@'localhost';` + tk.MustExec(dropUserSQL) + + // Test 'identified by password' + createUserSQL = `CREATE USER 'test1'@'localhost' identified by password 'xxx';` + err = tk.ExecToErr(createUserSQL) + require.Truef(t, terror.ErrorEqual(exeerrors.ErrPasswordFormat, err), "err %v", err) + createUserSQL = `CREATE USER 'test1'@'localhost' identified by password '*3D56A309CD04FA2EEF181462E59011F075C89548';` + tk.MustExec(createUserSQL) + dropUserSQL = `DROP USER 'test1'@'localhost';` + tk.MustExec(dropUserSQL) + + // Test drop user meet error + err = tk.ExecToErr(dropUserSQL) + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrCannotUser.GenWithStackByArgs("DROP USER", "")), "err %v", err) + + createUserSQL = `CREATE USER 'test1'@'localhost'` + tk.MustExec(createUserSQL) + createUserSQL = `CREATE USER 'test2'@'localhost'` + tk.MustExec(createUserSQL) + + dropUserSQL = `DROP USER 'test1'@'localhost', 'test2'@'localhost', 'test3'@'localhost';` + err = tk.ExecToErr(dropUserSQL) + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrCannotUser.GenWithStackByArgs("DROP USER", "")), "err %v", err) + + // Close issue #17639 + dropUserSQL = `DROP USER if exists test3@'%'` + tk.MustExec(dropUserSQL) + createUserSQL = `create user test3@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';` + tk.MustExec(createUserSQL) + querySQL := `select authentication_string from mysql.user where user="test3" ;` + tk.MustQuery(querySQL).Check(testkit.Rows("*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9")) + alterUserSQL = `alter user test3@'%' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';` + tk.MustExec(alterUserSQL) + tk.MustQuery(querySQL).Check(testkit.Rows("*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9")) + + createUserSQL = `create user userA@LOCALHOST;` + tk.MustExec(createUserSQL) + querySQL = `select user,host from mysql.user where user = 'userA';` + tk.MustQuery(querySQL).Check(testkit.Rows("userA localhost")) + + createUserSQL = `create user userB@DEMO.com;` + tk.MustExec(createUserSQL) + querySQL = `select user,host from mysql.user where user = 'userB';` + tk.MustQuery(querySQL).Check(testkit.Rows("userB demo.com")) + + createUserSQL = `create user userC@localhost;` + tk.MustExec(createUserSQL) + renameUserSQL := `rename user 'userC'@'localhost' to 'userD'@'Demo.com';` + tk.MustExec(renameUserSQL) + querySQL = `select user,host from mysql.user where user = 'userD';` + tk.MustQuery(querySQL).Check(testkit.Rows("userD demo.com")) + + createUserSQL = `create user foo@localhost identified with 'foobar';` + err = tk.ExecToErr(createUserSQL) + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrPluginIsNotLoaded), "err %v", err) + + tk.MustExec(`create user joan;`) + tk.MustExec(`create user sally;`) + tk.MustExec(`create role engineering;`) + tk.MustExec(`create role consultants;`) + tk.MustExec(`create role qa;`) + tk.MustExec(`grant engineering to joan;`) + tk.MustExec(`grant engineering to sally;`) + tk.MustExec(`grant engineering, consultants to joan, sally;`) + tk.MustExec(`grant qa to consultants;`) + tk.MustExec("CREATE ROLE `engineering`@`US`;") + tk.MustExec("create role `engineering`@`INDIA`;") + tk.MustExec("grant `engineering`@`US` TO `engineering`@`INDIA`;") + + tk.MustQuery("select user,host from mysql.user where user='engineering' and host = 'india'"). + Check(testkit.Rows("engineering india")) + tk.MustQuery("select user,host from mysql.user where user='engineering' and host = 'us'"). + Check(testkit.Rows("engineering us")) + + tk.MustExec("drop role engineering@INDIA;") + tk.MustExec("drop role engineering@US;") + + tk.MustQuery("select user from mysql.user where user='engineering' and host = 'india'").Check(testkit.Rows()) + tk.MustQuery("select user from mysql.user where user='engineering' and host = 'us'").Check(testkit.Rows()) +} + +func TestSetPwd(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + createUserSQL := `CREATE USER 'testpwd'@'localhost' IDENTIFIED BY '';` + tk.MustExec(createUserSQL) + result := tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) + result.Check(testkit.Rows("")) + + // set password for + tk.MustExec(`SET PASSWORD FOR 'testpwd'@'localhost' = 'password';`) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("password"))) + + tk.MustExec(`CREATE USER 'testpwdsock'@'localhost' IDENTIFIED WITH 'auth_socket';`) + tk.MustExec(`SET PASSWORD FOR 'testpwdsock'@'localhost' = 'password';`) + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows("Note 1699 SET PASSWORD has no significance for user 'testpwdsock'@'localhost' as authentication plugin does not support it.")) + + // set password + setPwdSQL := `SET PASSWORD = 'pwd'` + // Session user is empty. + err := tk.ExecToErr(setPwdSQL) + require.Error(t, err) + sess, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(sess) + ctx := tk.Session().(sessionctx.Context) + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "testpwd1", Hostname: "localhost", AuthUsername: "testpwd1", AuthHostname: "localhost"} + // Session user doesn't exist. + err = tk.ExecToErr(setPwdSQL) + require.Truef(t, terror.ErrorEqual(err, exeerrors.ErrPasswordNoMatch), "err %v", err) + // normal + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "testpwd", Hostname: "localhost", AuthUsername: "testpwd", AuthHostname: "localhost"} + tk.MustExec(setPwdSQL) + result = tk.MustQuery(`SELECT authentication_string FROM mysql.User WHERE User="testpwd" and Host="localhost"`) + result.Check(testkit.Rows(auth.EncodePassword("pwd"))) +} + +func TestFlushPrivileges(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec(`CREATE USER 'testflush'@'localhost' IDENTIFIED BY '';`) + tk.MustExec(`UPDATE mysql.User SET Select_priv='Y' WHERE User="testflush" and Host="localhost"`) + + // Create a new session. + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + defer se.Close() + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "testflush", Hostname: "localhost"}, nil, nil, nil)) + + ctx := context.Background() + // Before flush. + _, err = se.Execute(ctx, `SELECT authentication_string FROM mysql.User WHERE User="testflush" and Host="localhost"`) + require.Error(t, err) + + tk.MustExec("FLUSH PRIVILEGES") + + // After flush. + _, err = se.Execute(ctx, `SELECT authentication_string FROM mysql.User WHERE User="testflush" and Host="localhost"`) + require.NoError(t, err) +} + +func TestFlushPrivilegesPanic(t *testing.T) { + defer view.Stop() + // Run in a separate suite because this test need to set SkipGrantTable config. + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Security.SkipGrantTable = true + }) + + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("FLUSH PRIVILEGES") +} + +func TestDropPartitionStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + // Use the testSerialSuite to fix the unstable test + tk := testkit.NewTestKit(t, store) + tk.MustExec(`create database if not exists test_drop_gstats`) + tk.MustExec("use test_drop_gstats") + tk.MustExec("drop table if exists test_drop_gstats;") + tk.MustExec(`create table test_drop_gstats ( + a int, + key(a) +) +partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20), + partition global values less than (30) +)`) + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk.MustExec("insert into test_drop_gstats values (1), (5), (11), (15), (21), (25)") + require.Nil(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) + + checkPartitionStats := func(names ...string) { + rs := tk.MustQuery("show stats_meta").Rows() + require.Equal(t, len(names), len(rs)) + for i := range names { + require.Equal(t, names[i], rs[i][2].(string)) + } + } + + tk.MustExec("analyze table test_drop_gstats") + checkPartitionStats("global", "p0", "p1", "global") + + tk.MustExec("drop stats test_drop_gstats partition p0") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1681|'DROP STATS ... PARTITION ...' is deprecated and will be removed in a future release.")) + checkPartitionStats("global", "p1", "global") + + err := tk.ExecToErr("drop stats test_drop_gstats partition abcde") + require.Error(t, err) + require.Equal(t, "can not found the specified partition name abcde in the table definition", err.Error()) + + tk.MustExec("drop stats test_drop_gstats partition global") + checkPartitionStats("global", "p1") + + tk.MustExec("drop stats test_drop_gstats global") + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1287|'DROP STATS ... GLOBAL' is deprecated and will be removed in a future release. Please use DROP STATS ... instead")) + checkPartitionStats("p1") + + tk.MustExec("analyze table test_drop_gstats") + checkPartitionStats("global", "p0", "p1", "global") + + tk.MustExec("drop stats test_drop_gstats partition p0, p1, global") + checkPartitionStats("global") + + tk.MustExec("analyze table test_drop_gstats") + checkPartitionStats("global", "p0", "p1", "global") + + tk.MustExec("drop stats test_drop_gstats") + checkPartitionStats() +} + +func TestDropStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := dom.StatsHandle() + h.Clear() + testKit.MustExec("analyze table t") + statsTbl := h.GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + testKit.MustExec("drop stats t") + require.Nil(t, h.Update(is)) + statsTbl = h.GetTableStats(tableInfo) + require.True(t, statsTbl.Pseudo) + + testKit.MustExec("analyze table t") + statsTbl = h.GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + h.SetLease(1) + testKit.MustExec("drop stats t") + require.Nil(t, h.Update(is)) + statsTbl = h.GetTableStats(tableInfo) + require.True(t, statsTbl.Pseudo) + h.SetLease(0) +} + +func TestDropStatsForMultipleTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t1 (c1 int, c2 int)") + testKit.MustExec("create table t2 (c1 int, c2 int)") + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tableInfo2 := tbl2.Meta() + + h := dom.StatsHandle() + h.Clear() + testKit.MustExec("analyze table t1, t2") + statsTbl1 := h.GetTableStats(tableInfo1) + require.False(t, statsTbl1.Pseudo) + statsTbl2 := h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + + testKit.MustExec("drop stats t1, t2") + require.Nil(t, h.Update(is)) + statsTbl1 = h.GetTableStats(tableInfo1) + require.True(t, statsTbl1.Pseudo) + statsTbl2 = h.GetTableStats(tableInfo2) + require.True(t, statsTbl2.Pseudo) + + testKit.MustExec("analyze table t1, t2") + statsTbl1 = h.GetTableStats(tableInfo1) + require.False(t, statsTbl1.Pseudo) + statsTbl2 = h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + + h.SetLease(1) + testKit.MustExec("drop stats t1, t2") + require.Nil(t, h.Update(is)) + statsTbl1 = h.GetTableStats(tableInfo1) + require.True(t, statsTbl1.Pseudo) + statsTbl2 = h.GetTableStats(tableInfo2) + require.True(t, statsTbl2.Pseudo) + h.SetLease(0) +} + +func TestCreateUserWithLDAP(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("CREATE USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_simple AS 'uid=bob,ou=People,dc=example,dc=com'") + tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=People,dc=example,dc=com authentication_ldap_simple")) + + tk.MustExec("CREATE USER 'bob2'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob2,ou=People,dc=example,dc=com'") + tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob2'").Check(testkit.Rows("localhost bob2 uid=bob2,ou=People,dc=example,dc=com authentication_ldap_sasl")) +} + +func TestAlterUserWithLDAP(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // case 1: alter from a LDAP user to LDAP user + tk.MustExec("CREATE USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_simple AS 'uid=bob,ou=People,dc=example,dc=com'") + tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=People,dc=example,dc=com authentication_ldap_simple")) + tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") + tk.MustQuery("SELECT Host, User, authentication_string, plugin FROM mysql.User WHERE User = 'bob'").Check(testkit.Rows("localhost bob uid=bob,ou=Manager,dc=example,dc=com authentication_ldap_sasl")) + + // case 2: should ignore the password history + tk.MustExec("ALTER USER 'bob'@'localhost' PASSWORD HISTORY 5\n") + tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=People,dc=example,dc=com'") + tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") + tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=People,dc=example,dc=com'") + tk.MustExec("ALTER USER 'bob'@'localhost' IDENTIFIED WITH authentication_ldap_sasl AS 'uid=bob,ou=Manager,dc=example,dc=com'") +} + +func TestIssue44098(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set global validate_password.enable = 1") + tk.MustExec("create user u1 identified with 'tidb_auth_token'") + tk.MustExec("create user u2 identified with 'auth_socket'") + tk.MustExec("create user u3 identified with 'authentication_ldap_simple'") + tk.MustExec("create user u4 identified with 'authentication_ldap_sasl'") + tk.MustGetErrCode("create user u5 identified with 'mysql_native_password'", errno.ErrNotValidPassword) + tk.MustGetErrCode("create user u5 identified with 'caching_sha2_password'", errno.ErrNotValidPassword) + tk.MustGetErrCode("create user u5 identified with 'tidb_sm3_password'", errno.ErrNotValidPassword) + tk.MustGetErrCode("create user u5 identified with 'mysql_clear_password'", errno.ErrPluginIsNotLoaded) + tk.MustGetErrCode("create user u5 identified with 'tidb_session_token'", errno.ErrPluginIsNotLoaded) +} diff --git a/pkg/executor/test/splittest/BUILD.bazel b/pkg/executor/test/splittest/BUILD.bazel new file mode 100644 index 0000000000000..7240c6591cc24 --- /dev/null +++ b/pkg/executor/test/splittest/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "splittest_test", + timeout = "moderate", + srcs = [ + "main_test.go", + "split_table_test.go", + ], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/ddl", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/store/driver/backoff", + "//pkg/store/helper", + "//pkg/table", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/util/benchdaily", + "//pkg/util/dbterror", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/splittest/main_test.go b/pkg/executor/test/splittest/main_test.go similarity index 100% rename from executor/test/splittest/main_test.go rename to pkg/executor/test/splittest/main_test.go diff --git a/executor/test/splittest/split_table_test.go b/pkg/executor/test/splittest/split_table_test.go similarity index 98% rename from executor/test/splittest/split_table_test.go rename to pkg/executor/test/splittest/split_table_test.go index 4fe38cb3bea3f..76522daf0026b 100644 --- a/executor/test/splittest/split_table_test.go +++ b/pkg/executor/test/splittest/split_table_test.go @@ -20,22 +20,22 @@ import ( "sync/atomic" "testing" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/tiflashtest/BUILD.bazel b/pkg/executor/test/tiflashtest/BUILD.bazel new file mode 100644 index 0000000000000..1b9a556d06196 --- /dev/null +++ b/pkg/executor/test/tiflashtest/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tiflashtest_test", + timeout = "moderate", + srcs = [ + "main_test.go", + "tiflash_test.go", + ], + flaky = True, + race = "on", + shard_count = 39, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/memory", + "//pkg/util/tiflashcompute", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/tiflashtest/main_test.go b/pkg/executor/test/tiflashtest/main_test.go new file mode 100644 index 0000000000000..61bfd1049122f --- /dev/null +++ b/pkg/executor/test/tiflashtest/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tiflashtest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.Cleanup(func(_ int) { + view.Stop() + }), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/tiflashtest/tiflash_test.go b/pkg/executor/test/tiflashtest/tiflash_test.go similarity index 94% rename from executor/test/tiflashtest/tiflash_test.go rename to pkg/executor/test/tiflashtest/tiflash_test.go index 0615208314125..2b0796de8f6d5 100644 --- a/executor/test/tiflashtest/tiflash_test.go +++ b/pkg/executor/test/tiflashtest/tiflash_test.go @@ -27,18 +27,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" ) @@ -451,8 +451,8 @@ func TestTiFlashPartitionTableReader(t *testing.T) { } func TestPartitionTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t, withMockTiFlash(2)) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -472,22 +472,22 @@ func TestPartitionTable(t *testing.T) { tk.MustExec("insert into t values(2,0)") tk.MustExec("insert into t values(3,0)") tk.MustExec("insert into t values(4,0)") - failpoint.Enable("github.com/pingcap/tidb/executor/checkUseMPP", `return(true)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/checkUseMPP", `return(true)`) tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") tk.MustExec("set @@session.tidb_allow_mpp=ON") tk.MustExec("set @@session.tidb_opt_enable_late_materialization=OFF") // mock executor does not support use outer table as build side for outer join, so need to // force the inner table as build side tk.MustExec("set tidb_opt_mpp_outer_join_fixed_build_side=1") - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(2)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(2)`) tk.MustQuery("select count(*) from t").Check(testkit.Rows("4")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") tk.MustExec("set @@session.tidb_partition_prune_mode='static-only'") // when we lift the restriction of partition table can not take MPP path, here should `return(true)` - failpoint.Enable("github.com/pingcap/tidb/executor/checkUseMPP", `return(true)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/checkUseMPP", `return(true)`) tk.MustQuery("select count(*) from t").Check(testkit.Rows("4")) tk.MustExec("set @@session.tidb_partition_prune_mode='dynamic-only'") - failpoint.Enable("github.com/pingcap/tidb/executor/checkUseMPP", `return(true)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/checkUseMPP", `return(true)`) tk.MustExec("create table t1(a int not null primary key, b int not null) partition by hash(a) partitions 4") tk.MustExec("alter table t1 set tiflash replica 2") @@ -502,12 +502,12 @@ func TestPartitionTable(t *testing.T) { tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") tk.MustExec("set @@session.tidb_allow_mpp=ON") // test if it is really work. - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(4)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(4)`) tk.MustQuery("select count(*) from t1 , t where t1.a = t.a").Check(testkit.Rows("4")) // test partition prune tk.MustQuery("select count(*) from t1 , t where t1.a = t.a and t1.a < 2 and t.a < 2").Check(testkit.Rows("1")) tk.MustQuery("select count(*) from t1 , t where t1.a = t.a and t1.a < -1 and t.a < 2").Check(testkit.Rows("0")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") // test multi-way join tk.MustExec("create table t2(a int not null primary key, b int not null)") tk.MustExec("alter table t2 set tiflash replica 2") @@ -520,9 +520,9 @@ func TestPartitionTable(t *testing.T) { tk.MustExec("insert into t2 values(3,0)") tk.MustExec("insert into t2 values(4,0)") // test with no partition table - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(5)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(5)`) tk.MustQuery("select count(*) from t1 , t, t2 where t1.a = t.a and t2.a = t.a").Check(testkit.Rows("4")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") tk.MustExec(`create table t3(a int not null, b int not null) PARTITION BY RANGE (b) ( PARTITION p0 VALUES LESS THAN (1), @@ -540,13 +540,13 @@ func TestPartitionTable(t *testing.T) { tk.MustExec("insert into t3 values(3,4)") tk.MustExec("insert into t3 values(4,6)") - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(4)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(4)`) tk.MustQuery("select count(*) from t, t3 where t3.a = t.a and t3.b <= 4").Check(testkit.Rows("3")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(3)`) + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(3)`) tk.MustQuery("select count(*) from t, t3 where t3.a = t.a and t3.b > 10").Check(testkit.Rows("0")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") - failpoint.Disable("github.com/pingcap/tidb/executor/checkUseMPP") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/checkUseMPP") } func TestMppEnum(t *testing.T) { @@ -626,9 +626,9 @@ func TestDispatchTaskRetry(t *testing.T) { err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) require.NoError(t, err) tk.MustExec("set @@session.tidb_enforce_mpp=ON") - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/mppDispatchTimeout", "3*return(true)")) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppDispatchTimeout", "3*return(true)")) tk.MustQuery("select count(*) from t group by b").Check(testkit.Rows("4")) - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/mppDispatchTimeout")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppDispatchTimeout")) } func TestMppVersionError(t *testing.T) { @@ -645,25 +645,25 @@ func TestMppVersionError(t *testing.T) { tk.MustExec("set @@session.tidb_enforce_mpp=ON") { item := fmt.Sprintf("return(%d)", kv.GetNewestMppVersion()+1) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/MppVersionError", item)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/MppVersionError", item)) } { err := tk.QueryToErr("select count(*) from t group by b") require.Error(t, err) } - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/MppVersionError")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/MppVersionError")) { item := fmt.Sprintf("return(%d)", kv.GetNewestMppVersion()) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/MppVersionError", item)) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/MppVersionError", item)) } { tk.MustQuery("select count(*) from t group by b").Check(testkit.Rows("4")) } - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/MppVersionError")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/MppVersionError")) } func TestCancelMppTasks(t *testing.T) { - var hang = "github.com/pingcap/tidb/store/mockstore/unistore/mppRecvHang" + var hang = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppRecvHang" store := testkit.CreateMockStore(t, withMockTiFlash(2)) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -701,9 +701,9 @@ func TestCancelMppTasks(t *testing.T) { // all goroutines exit if one goroutine hangs but another return errors func TestMppGoroutinesExitFromErrors(t *testing.T) { // mock non-root tasks return error - var mppNonRootTaskError = "github.com/pingcap/tidb/executor/internal/mpp/mppNonRootTaskError" + var mppNonRootTaskError = "github.com/pingcap/tidb/pkg/executor/internal/mpp/mppNonRootTaskError" // mock root tasks hang - var hang = "github.com/pingcap/tidb/store/mockstore/unistore/mppRecvHang" + var hang = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppRecvHang" store := testkit.CreateMockStore(t, withMockTiFlash(2)) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -780,9 +780,9 @@ func TestMppUnionAll(t *testing.T) { // test union all join union all tk.MustQuery("select count(*) from (select * from x1 union all select * from x2 union all select * from x3) x join (select * from x1 union all select * from x2 union all select * from x3) y on x.a = y.b").Check(testkit.Rows("29")) tk.MustExec("set @@session.tidb_broadcast_join_threshold_count=100000") - failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks", `return(6)`) + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks", `return(6)`) tk.MustQuery("select count(*) from (select * from x1 union all select * from x2 union all select * from x3) x join (select * from x1 union all select * from x2 union all select * from x3) y on x.a = y.b").Check(testkit.Rows("29")) - failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/checkTotalMPPTasks") + failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/checkTotalMPPTasks") tk.MustExec("drop table if exists x4") tk.MustExec("create table x4(a int not null, b int not null);") @@ -1186,10 +1186,10 @@ func TestTiflashPartitionTableScan(t *testing.T) { // MPP wg := sync.WaitGroup{} wg.Add(1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy", `return(true)`)) go func() { time.Sleep(100 * time.Millisecond) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy")) wg.Done() }() tk.MustExec("set @@session.tidb_allow_mpp=ON;") @@ -1198,10 +1198,10 @@ func TestTiflashPartitionTableScan(t *testing.T) { // BatchCop wg.Add(1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy", `return(true)`)) go func() { time.Sleep(100 * time.Millisecond) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/rpcServerBusy")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/rpcServerBusy")) wg.Done() }() tk.MustExec("set @@session.tidb_allow_mpp=OFF;") @@ -1475,9 +1475,9 @@ func TestMPPMemoryTracker(t *testing.T) { require.NoError(t, err) tk.MustExec("set tidb_enforce_mpp = on;") tk.MustQuery("select * from t").Check(testkit.Rows("1")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/testMPPOOMPanic", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/testMPPOOMPanic", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/testMPPOOMPanic")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/testMPPOOMPanic")) }() err = tk.QueryToErr("select * from t") require.NotNil(t, err) @@ -1532,7 +1532,7 @@ func TestTiFlashComputeDispatchPolicy(t *testing.T) { useASs := []bool{true, false} // Valid values. - defer failpoint.Disable("github.com/pingcap/tidb/store/copr/testWhichDispatchPolicy") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/testWhichDispatchPolicy") for _, useAS := range useASs { config.UpdateGlobal(func(conf *config.Config) { conf.UseAutoScaler = useAS @@ -1543,7 +1543,7 @@ func TestTiFlashComputeDispatchPolicy(t *testing.T) { tk1 := testkit.NewTestKit(t, store) tk1.MustExec("use test") tk1.MustQuery("select @@tiflash_compute_dispatch_policy").Check(testkit.Rows(p)) - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/testWhichDispatchPolicy", fmt.Sprintf(`return("%s")`, p))) + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/testWhichDispatchPolicy", fmt.Sprintf(`return("%s")`, p))) err = tk1.ExecToErr("select * from t;") if useAS { // Expect error, because TestAutoScaler return empty topo. @@ -1552,7 +1552,7 @@ func TestTiFlashComputeDispatchPolicy(t *testing.T) { // This error message means we use PD instead of AutoScaler. require.Contains(t, err.Error(), "tiflash_compute node is unavailable") } - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/testWhichDispatchPolicy")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/testWhichDispatchPolicy")) } } } @@ -1681,10 +1681,10 @@ func TestDisaggregatedTiFlashGeneratedColumn(t *testing.T) { func TestMppStoreCntWithErrors(t *testing.T) { // mock non-root tasks return error - var mppStoreCountPDError = "github.com/pingcap/tidb/store/copr/mppStoreCountPDError" - var mppStoreCountSetMPPCnt = "github.com/pingcap/tidb/store/copr/mppStoreCountSetMPPCnt" - var mppStoreCountSetLastUpdateTime = "github.com/pingcap/tidb/store/copr/mppStoreCountSetLastUpdateTime" - var mppStoreCountSetLastUpdateTimeP2 = "github.com/pingcap/tidb/store/copr/mppStoreCountSetLastUpdateTimeP2" + var mppStoreCountPDError = "github.com/pingcap/tidb/pkg/store/copr/mppStoreCountPDError" + var mppStoreCountSetMPPCnt = "github.com/pingcap/tidb/pkg/store/copr/mppStoreCountSetMPPCnt" + var mppStoreCountSetLastUpdateTime = "github.com/pingcap/tidb/pkg/store/copr/mppStoreCountSetLastUpdateTime" + var mppStoreCountSetLastUpdateTimeP2 = "github.com/pingcap/tidb/pkg/store/copr/mppStoreCountSetLastUpdateTimeP2" store := testkit.CreateMockStore(t, withMockTiFlash(3)) { diff --git a/pkg/executor/test/unstabletest/BUILD.bazel b/pkg/executor/test/unstabletest/BUILD.bazel new file mode 100644 index 0000000000000..06b004f5391a7 --- /dev/null +++ b/pkg/executor/test/unstabletest/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "unstabletest_test", + timeout = "short", + srcs = [ + "main_test.go", + "memory_test.go", + "unstable_test.go", + ], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/config", + "//pkg/meta/autoid", + "//pkg/testkit", + "//pkg/util", + "//pkg/util/memory", + "//pkg/util/skip", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/executor/test/unstabletest/README.md b/pkg/executor/test/unstabletest/README.md similarity index 100% rename from executor/test/unstabletest/README.md rename to pkg/executor/test/unstabletest/README.md diff --git a/pkg/executor/test/unstabletest/main_test.go b/pkg/executor/test/unstabletest/main_test.go new file mode 100644 index 0000000000000..10e6939dcd9c8 --- /dev/null +++ b/pkg/executor/test/unstabletest/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package unstabletest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.Cleanup(func(_ int) { + view.Stop() + }), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/unstabletest/memory_test.go b/pkg/executor/test/unstabletest/memory_test.go similarity index 96% rename from executor/test/unstabletest/memory_test.go rename to pkg/executor/test/unstabletest/memory_test.go index bf246445a9d7a..068c905018255 100644 --- a/executor/test/unstabletest/memory_test.go +++ b/pkg/executor/test/unstabletest/memory_test.go @@ -21,8 +21,8 @@ import ( "runtime/debug" "testing" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/skip" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/skip" "github.com/stretchr/testify/require" ) diff --git a/executor/test/unstabletest/unstable_test.go b/pkg/executor/test/unstabletest/unstable_test.go similarity index 98% rename from executor/test/unstabletest/unstable_test.go rename to pkg/executor/test/unstabletest/unstable_test.go index 50253eaece40b..7c615605e6fd3 100644 --- a/executor/test/unstabletest/unstable_test.go +++ b/pkg/executor/test/unstabletest/unstable_test.go @@ -20,10 +20,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/skip" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/skip" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/test/writetest/BUILD.bazel b/pkg/executor/test/writetest/BUILD.bazel new file mode 100644 index 0000000000000..94e4812c42396 --- /dev/null +++ b/pkg/executor/test/writetest/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "writetest_test", + timeout = "short", + srcs = [ + "main_test.go", + "write_test.go", + ], + flaky = True, + shard_count = 30, + deps = [ + "//br/pkg/lightning/mydump", + "//pkg/config", + "//pkg/executor", + "//pkg/executor/internal", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessiontxn", + "//pkg/store/mockstore", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/types", + "//pkg/util", + "//pkg/util/mock", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/executor/test/writetest/main_test.go b/pkg/executor/test/writetest/main_test.go new file mode 100644 index 0000000000000..e21407c88dbba --- /dev/null +++ b/pkg/executor/test/writetest/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package writetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/tikv/client-go/v2/tikv" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + autoid.SetStep(5000) + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowThreshold = 30000 // 30s + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + opts := []goleak.Option{ + goleak.Cleanup(func(_ int) { + view.Stop() + }), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/executor/test/writetest/write_test.go b/pkg/executor/test/writetest/write_test.go similarity index 98% rename from executor/test/writetest/write_test.go rename to pkg/executor/test/writetest/write_test.go index c885952a7210d..fcb756c4d91fc 100644 --- a/executor/test/writetest/write_test.go +++ b/pkg/executor/test/writetest/write_test.go @@ -23,22 +23,22 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/executor/internal" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/executor/internal" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) @@ -1686,8 +1686,8 @@ func TestPessimisticDeleteYourWrites(t *testing.T) { // TestWriteListPartitionTable2 test for write list partition when the partition expression is complicated and contain generated column. func TestWriteListPartitionTable2(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -1810,8 +1810,8 @@ func TestWriteListPartitionTable2(t *testing.T) { } func TestWriteListColumnsPartitionTable1(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -1979,8 +1979,8 @@ func TestListPartitionWithAutoIncrement(t *testing.T) { } func TestUpdate(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") @@ -2251,8 +2251,8 @@ func TestUpdate(t *testing.T) { } func TestListColumnsPartitionWithGlobalIndex(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") diff --git a/executor/testdata/analyze_test_data.sql b/pkg/executor/testdata/analyze_test_data.sql similarity index 100% rename from executor/testdata/analyze_test_data.sql rename to pkg/executor/testdata/analyze_test_data.sql diff --git a/executor/testdata/executor_suite_in.json b/pkg/executor/testdata/executor_suite_in.json similarity index 100% rename from executor/testdata/executor_suite_in.json rename to pkg/executor/testdata/executor_suite_in.json diff --git a/executor/testdata/executor_suite_out.json b/pkg/executor/testdata/executor_suite_out.json similarity index 100% rename from executor/testdata/executor_suite_out.json rename to pkg/executor/testdata/executor_suite_out.json diff --git a/executor/testdata/point_get_suite_in.json b/pkg/executor/testdata/point_get_suite_in.json similarity index 100% rename from executor/testdata/point_get_suite_in.json rename to pkg/executor/testdata/point_get_suite_in.json diff --git a/executor/testdata/point_get_suite_out.json b/pkg/executor/testdata/point_get_suite_out.json similarity index 100% rename from executor/testdata/point_get_suite_out.json rename to pkg/executor/testdata/point_get_suite_out.json diff --git a/executor/testdata/prepare_suite_in.json b/pkg/executor/testdata/prepare_suite_in.json similarity index 100% rename from executor/testdata/prepare_suite_in.json rename to pkg/executor/testdata/prepare_suite_in.json diff --git a/executor/testdata/prepare_suite_out.json b/pkg/executor/testdata/prepare_suite_out.json similarity index 100% rename from executor/testdata/prepare_suite_out.json rename to pkg/executor/testdata/prepare_suite_out.json diff --git a/executor/testdata/slow_query_suite_in.json b/pkg/executor/testdata/slow_query_suite_in.json similarity index 100% rename from executor/testdata/slow_query_suite_in.json rename to pkg/executor/testdata/slow_query_suite_in.json diff --git a/executor/testdata/slow_query_suite_out.json b/pkg/executor/testdata/slow_query_suite_out.json similarity index 100% rename from executor/testdata/slow_query_suite_out.json rename to pkg/executor/testdata/slow_query_suite_out.json diff --git a/executor/testdata/tiflash_v620_dt_segments.json b/pkg/executor/testdata/tiflash_v620_dt_segments.json similarity index 100% rename from executor/testdata/tiflash_v620_dt_segments.json rename to pkg/executor/testdata/tiflash_v620_dt_segments.json diff --git a/executor/testdata/tiflash_v620_dt_tables.json b/pkg/executor/testdata/tiflash_v620_dt_tables.json similarity index 100% rename from executor/testdata/tiflash_v620_dt_tables.json rename to pkg/executor/testdata/tiflash_v620_dt_tables.json diff --git a/executor/testdata/tiflash_v630_dt_segments.json b/pkg/executor/testdata/tiflash_v630_dt_segments.json similarity index 100% rename from executor/testdata/tiflash_v630_dt_segments.json rename to pkg/executor/testdata/tiflash_v630_dt_segments.json diff --git a/executor/testdata/tiflash_v640_dt_tables.json b/pkg/executor/testdata/tiflash_v640_dt_tables.json similarity index 100% rename from executor/testdata/tiflash_v640_dt_tables.json rename to pkg/executor/testdata/tiflash_v640_dt_tables.json diff --git a/executor/tikv_regions_peers_table_test.go b/pkg/executor/tikv_regions_peers_table_test.go similarity index 97% rename from executor/tikv_regions_peers_table_test.go rename to pkg/executor/tikv_regions_peers_table_test.go index a74802ef05592..4994f9b73fe69 100644 --- a/executor/tikv_regions_peers_table_test.go +++ b/pkg/executor/tikv_regions_peers_table_test.go @@ -25,9 +25,9 @@ import ( "github.com/gorilla/mux" "github.com/pingcap/fn" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/pdapi" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/pdapi" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/trace.go b/pkg/executor/trace.go new file mode 100644 index 0000000000000..3a7995a2462b2 --- /dev/null +++ b/pkg/executor/trace.go @@ -0,0 +1,412 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "archive/zip" + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + "time" + + "github.com/opentracing/basictracer-go" + "github.com/opentracing/opentracing-go" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" + "sourcegraph.com/sourcegraph/appdash" + traceImpl "sourcegraph.com/sourcegraph/appdash/opentracing" +) + +// TraceExec represents a root executor of trace query. +type TraceExec struct { + exec.BaseExecutor + // CollectedSpans collects all span during execution. Span is appended via + // callback method which passes into tracer implementation. + CollectedSpans []basictracer.RawSpan + // exhausted being true means there is no more result. + exhausted bool + // stmtNode is the real query ast tree and it is used for building real query's plan. + stmtNode ast.StmtNode + + builder *executorBuilder + format string + + // optimizerTrace indicates 'trace plan statement' + optimizerTrace bool + optimizerTraceTarget string +} + +// Next executes real query and collects span later. +func (e *TraceExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if e.exhausted { + return nil + } + se, ok := e.Ctx().(sqlexec.SQLExecutor) + if !ok { + e.exhausted = true + return nil + } + + // For audit log plugin to set the correct statement. + stmtCtx := e.Ctx().GetSessionVars().StmtCtx + defer func() { + e.Ctx().GetSessionVars().StmtCtx = stmtCtx + }() + + if e.optimizerTrace { + switch e.optimizerTraceTarget { + case core.TracePlanTargetEstimation: + return e.nextOptimizerCEPlanTrace(ctx, e.Ctx(), req) + case core.TracePlanTargetDebug: + return e.nextOptimizerDebugPlanTrace(ctx, e.Ctx(), req) + default: + return e.nextOptimizerPlanTrace(ctx, e.Ctx(), req) + } + } + + ctx = util.ContextWithTraceExecDetails(ctx) + switch e.format { + case core.TraceFormatLog: + return e.nextTraceLog(ctx, se, req) + default: + return e.nextRowJSON(ctx, se, req) + } +} + +func (e *TraceExec) nextOptimizerCEPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { + stmtCtx := se.GetSessionVars().StmtCtx + origin := stmtCtx.EnableOptimizerCETrace + stmtCtx.EnableOptimizerCETrace = true + defer func() { + stmtCtx.EnableOptimizerCETrace = origin + }() + + _, _, err := core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) + if err != nil { + return err + } + + writer := strings.Builder{} + jsonEncoder := json.NewEncoder(&writer) + // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... + jsonEncoder.SetEscapeHTML(false) + err = jsonEncoder.Encode(stmtCtx.OptimizerCETrace) + if err != nil { + return errors.AddStack(err) + } + res := []byte(writer.String()) + + req.AppendBytes(0, res) + e.exhausted = true + return nil +} + +func (e *TraceExec) nextOptimizerDebugPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { + stmtCtx := se.GetSessionVars().StmtCtx + origin := stmtCtx.EnableOptimizerDebugTrace + stmtCtx.EnableOptimizerDebugTrace = true + defer func() { + stmtCtx.EnableOptimizerDebugTrace = origin + }() + + _, _, err := core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) + if err != nil { + return err + } + + writer := strings.Builder{} + jsonEncoder := json.NewEncoder(&writer) + // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... + jsonEncoder.SetEscapeHTML(false) + err = jsonEncoder.Encode(stmtCtx.OptimizerDebugTrace) + if err != nil { + return errors.AddStack(err) + } + res := []byte(writer.String()) + + req.AppendBytes(0, res) + e.exhausted = true + return nil +} + +func (e *TraceExec) nextOptimizerPlanTrace(ctx context.Context, se sessionctx.Context, req *chunk.Chunk) error { + zf, fileName, err := generateOptimizerTraceFile() + if err != nil { + return err + } + zw := zip.NewWriter(zf) + defer func() { + err := zw.Close() + if err != nil { + logutil.BgLogger().Warn("Closing zip writer failed", zap.Error(err)) + } + err = zf.Close() + if err != nil { + logutil.BgLogger().Warn("Closing zip file failed", zap.Error(err)) + } + }() + traceZW, err := zw.Create("trace.json") + if err != nil { + return errors.AddStack(err) + } + stmtCtx := se.GetSessionVars().StmtCtx + origin := stmtCtx.EnableOptimizeTrace + stmtCtx.EnableOptimizeTrace = true + defer func() { + stmtCtx.EnableOptimizeTrace = origin + }() + _, _, err = core.OptimizeAstNode(ctx, se, e.stmtNode, se.GetInfoSchema().(infoschema.InfoSchema)) + if err != nil { + return err + } + + writer := strings.Builder{} + jsonEncoder := json.NewEncoder(&writer) + // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... + jsonEncoder.SetEscapeHTML(false) + err = jsonEncoder.Encode(se.GetSessionVars().StmtCtx.OptimizeTracer) + if err != nil { + return errors.AddStack(err) + } + res := []byte(writer.String()) + + _, err = traceZW.Write(res) + if err != nil { + return errors.AddStack(err) + } + req.AppendString(0, fileName) + e.exhausted = true + return nil +} + +func (e *TraceExec) nextTraceLog(ctx context.Context, se sqlexec.SQLExecutor, req *chunk.Chunk) error { + recorder := basictracer.NewInMemoryRecorder() + tracer := basictracer.New(recorder) + span := tracer.StartSpan("trace") + ctx = opentracing.ContextWithSpan(ctx, span) + + e.executeChild(ctx, se) + span.Finish() + + generateLogResult(recorder.GetSpans(), req) + e.exhausted = true + return nil +} + +func (e *TraceExec) nextRowJSON(ctx context.Context, se sqlexec.SQLExecutor, req *chunk.Chunk) error { + store := appdash.NewMemoryStore() + tracer := traceImpl.NewTracer(store) + span := tracer.StartSpan("trace") + ctx = opentracing.ContextWithSpan(ctx, span) + + e.executeChild(ctx, se) + span.Finish() + + traces, err := store.Traces(appdash.TracesOpts{}) + if err != nil { + return errors.Trace(err) + } + + // Row format. + if e.format != core.TraceFormatJSON { + if len(traces) < 1 { + e.exhausted = true + return nil + } + trace := traces[0] + dfsTree(trace, "", false, req) + e.exhausted = true + return nil + } + + // Json format. + data, err := json.Marshal(traces) + if err != nil { + return errors.Trace(err) + } + + // Split json data into rows to avoid the max packet size limitation. + const maxRowLen = 4096 + for len(data) > maxRowLen { + req.AppendString(0, string(data[:maxRowLen])) + data = data[maxRowLen:] + } + req.AppendString(0, string(data)) + e.exhausted = true + return nil +} + +func (e *TraceExec) executeChild(ctx context.Context, se sqlexec.SQLExecutor) { + // For audit log plugin to log the statement correctly. + // Should be logged as 'explain ...', instead of the executed SQL. + vars := e.Ctx().GetSessionVars() + origin := vars.InRestrictedSQL + vars.InRestrictedSQL = true + defer func() { + vars.InRestrictedSQL = origin + }() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnTrace) + rs, err := se.ExecuteStmt(ctx, e.stmtNode) + if err != nil { + var errCode uint16 + if te, ok := err.(*terror.Error); ok { + errCode = terror.ToSQLError(te).Code + } + logutil.Eventf(ctx, "execute with error(%d): %s", errCode, err.Error()) + } + if rs != nil { + drainRecordSet(ctx, e.Ctx(), rs) + if err = rs.Close(); err != nil { + logutil.Logger(ctx).Error("run trace close result with error", zap.Error(err)) + } + } + logutil.Eventf(ctx, "execute done, modify row: %d", e.Ctx().GetSessionVars().StmtCtx.AffectedRows()) +} + +func drainRecordSet(ctx context.Context, sctx sessionctx.Context, rs sqlexec.RecordSet) { + req := rs.NewChunk(nil) + var rowCount int + for { + err := rs.Next(ctx, req) + if err != nil || req.NumRows() == 0 { + if err != nil { + var errCode uint16 + if te, ok := err.(*terror.Error); ok { + errCode = terror.ToSQLError(te).Code + } + logutil.Eventf(ctx, "execute with error(%d): %s", errCode, err.Error()) + } else { + logutil.Eventf(ctx, "execute done, ReturnRow: %d, ModifyRow: %d", rowCount, sctx.GetSessionVars().StmtCtx.AffectedRows()) + } + return + } + rowCount += req.NumRows() + req.Reset() + } +} + +func dfsTree(t *appdash.Trace, prefix string, isLast bool, chk *chunk.Chunk) { + var newPrefix, suffix string + if prefix == "" { + newPrefix = prefix + " " + } else { + if !isLast { + suffix = "├─" + newPrefix = prefix + "│ " + } else { + suffix = "└─" + newPrefix = prefix + " " + } + } + + var start time.Time + var duration time.Duration + if e, err := t.TimespanEvent(); err == nil { + start = e.Start() + end := e.End() + duration = end.Sub(start) + } + + chk.AppendString(0, prefix+suffix+t.Span.Name()) + chk.AppendString(1, start.Format("15:04:05.000000")) + chk.AppendString(2, duration.String()) + + // Sort events by their start time + slices.SortFunc(t.Sub, func(i, j *appdash.Trace) int { + var istart, jstart time.Time + if ievent, err := i.TimespanEvent(); err == nil { + istart = ievent.Start() + } + if jevent, err := j.TimespanEvent(); err == nil { + jstart = jevent.Start() + } + return istart.Compare(jstart) + }) + + for i, sp := range t.Sub { + dfsTree(sp, newPrefix, i == (len(t.Sub))-1 /*last element of array*/, chk) + } +} + +func generateLogResult(allSpans []basictracer.RawSpan, chk *chunk.Chunk) { + for rIdx := range allSpans { + span := &allSpans[rIdx] + + chk.AppendTime(0, types.NewTime(types.FromGoTime(span.Start), mysql.TypeTimestamp, 6)) + chk.AppendString(1, "--- start span "+span.Operation+" ----") + chk.AppendString(2, "") + chk.AppendString(3, span.Operation) + + var tags string + if len(span.Tags) > 0 { + tags = fmt.Sprintf("%v", span.Tags) + } + for _, l := range span.Logs { + for _, field := range l.Fields { + if field.Key() == logutil.TraceEventKey { + chk.AppendTime(0, types.NewTime(types.FromGoTime(l.Timestamp), mysql.TypeTimestamp, 6)) + chk.AppendString(1, field.Value().(string)) + chk.AppendString(2, tags) + chk.AppendString(3, span.Operation) + } + } + } + } +} + +func generateOptimizerTraceFile() (*os.File, string, error) { + dirPath := domain.GetOptimizerTraceDirName() + // Create path + err := os.MkdirAll(dirPath, os.ModePerm) + if err != nil { + return nil, "", errors.AddStack(err) + } + // Generate key and create zip file + time := time.Now().UnixNano() + b := make([]byte, 16) + //nolint: gosec + _, err = rand.Read(b) + if err != nil { + return nil, "", errors.AddStack(err) + } + key := base64.URLEncoding.EncodeToString(b) + fileName := fmt.Sprintf("optimizer_trace_%v_%v.zip", key, time) + zf, err := os.Create(filepath.Join(dirPath, fileName)) + if err != nil { + return nil, "", errors.AddStack(err) + } + return zf, fileName, nil +} diff --git a/pkg/executor/trace_test.go b/pkg/executor/trace_test.go new file mode 100644 index 0000000000000..04f8a5db55c3b --- /dev/null +++ b/pkg/executor/trace_test.go @@ -0,0 +1,88 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestTraceExec(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + testSQL := `create table trace (id int PRIMARY KEY AUTO_INCREMENT, c1 int, c2 int, c3 int default 1);` + tk.MustExec(testSQL) + tk.MustExec("trace insert into trace (c1, c2, c3) values (1, 2, 3)") + rows := tk.MustQuery("trace select * from trace where id = 0;").Rows() + require.GreaterOrEqual(t, len(rows), 1) + + // +---------------------------+-----------------+------------+ + // | operation | snapshotTS | duration | + // +---------------------------+-----------------+------------+ + // | session.getTxnFuture | 22:08:38.247834 | 78.909µs | + // | ├─session.Execute | 22:08:38.247829 | 1.478487ms | + // | ├─session.ParseSQL | 22:08:38.248457 | 71.159µs | + // | ├─executor.Compile | 22:08:38.248578 | 45.329µs | + // | ├─session.runStmt | 22:08:38.248661 | 75.13µs | + // | ├─session.CommitTxn | 22:08:38.248699 | 13.213µs | + // | └─recordSet.Next | 22:08:38.249340 | 155.317µs | + // +---------------------------+-----------------+------------+ + rows = tk.MustQuery("trace format='row' select * from trace where id = 0;").Rows() + require.Greater(t, len(rows), 1) + require.True(t, rowsOrdered(rows)) + + rows = tk.MustQuery("trace format='row' delete from trace where id = 0").Rows() + require.Greater(t, len(rows), 1) + require.True(t, rowsOrdered(rows)) + + rows = tk.MustQuery("trace format='row' analyze table trace").Rows() + require.Greater(t, len(rows), 1) + require.True(t, rowsOrdered(rows)) + + tk.MustExec("trace format='log' insert into trace (c1, c2, c3) values (1, 2, 3)") + rows = tk.MustQuery("trace format='log' select * from trace where id = 0;").Rows() + require.GreaterOrEqual(t, len(rows), 1) +} + +func rowsOrdered(rows [][]interface{}) bool { + for idx := range rows { + if _, ok := rows[idx][1].(string); !ok { + return false + } + if idx == 0 { + continue + } + if rows[idx-1][1].(string) > rows[idx][1].(string) { + return false + } + } + return true +} + +func TestTracePlanStmt(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table tp1(id int);") + tk.MustExec("create table tp2(id int);") + tk.MustExec("set @@tidb_cost_model_version=2") + rows := tk.MustQuery("trace plan select * from tp1 t1, tp2 t2 where t1.id = t2.id").Rows() + require.Len(t, rows, 1) + require.Len(t, rows[0], 1) + require.Regexp(t, ".*zip", rows[0][0]) +} diff --git a/executor/union_scan.go b/pkg/executor/union_scan.go similarity index 93% rename from executor/union_scan.go rename to pkg/executor/union_scan.go index e6c58560ac892..83d3cb06ddb98 100644 --- a/executor/union_scan.go +++ b/pkg/executor/union_scan.go @@ -19,19 +19,19 @@ import ( "fmt" "runtime/trace" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/tracing" ) // UnionScanExec merges the rows from dirty table and the rows from distsql request. diff --git a/executor/union_scan_test.go b/pkg/executor/union_scan_test.go similarity index 99% rename from executor/union_scan_test.go rename to pkg/executor/union_scan_test.go index a4b7932b49b13..f196db8ac59a0 100644 --- a/executor/union_scan_test.go +++ b/pkg/executor/union_scan_test.go @@ -20,11 +20,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/benchdaily" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/benchdaily" "github.com/stretchr/testify/require" ) diff --git a/pkg/executor/update.go b/pkg/executor/update.go new file mode 100644 index 0000000000000..a836f720d6a4b --- /dev/null +++ b/pkg/executor/update.go @@ -0,0 +1,563 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "bytes" + "context" + "fmt" + "runtime/trace" + + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/tikv/client-go/v2/txnkv/txnsnapshot" +) + +// UpdateExec represents a new update executor. +type UpdateExec struct { + exec.BaseExecutor + + OrderedList []*expression.Assignment + + // updatedRowKeys is a map for unique (TableAlias, handle) pair. + // The value is true if the row is changed, or false otherwise + updatedRowKeys map[int]*kv.MemAwareHandleMap[bool] + tblID2table map[int64]table.Table + // mergedRowData is a map for unique (Table, handle) pair. + // The value is cached table row + mergedRowData map[int64]*kv.MemAwareHandleMap[[]types.Datum] + multiUpdateOnSameTable map[int64]bool + + matched uint64 // a counter of matched rows during update + // tblColPosInfos stores relationship between column ordinal to its table handle. + // the columns ordinals is present in ordinal range format, @see plannercore.TblColPosInfos + tblColPosInfos plannercore.TblColPosInfoSlice + assignFlag []int + evalBuffer chunk.MutRow + allAssignmentsAreConstant bool + virtualAssignmentsOffset int + drained bool + memTracker *memory.Tracker + + stats *updateRuntimeStats + + handles []kv.Handle + tableUpdatable []bool + changed []bool + matches []bool + // fkChecks contains the foreign key checkers. the map is tableID -> []*FKCheckExec + fkChecks map[int64][]*FKCheckExec + // fkCascades contains the foreign key cascade. the map is tableID -> []*FKCascadeExec + fkCascades map[int64][]*FKCascadeExec +} + +// prepare `handles`, `tableUpdatable`, `changed` to avoid re-computations. +func (e *UpdateExec) prepare(row []types.Datum) (err error) { + if e.updatedRowKeys == nil { + e.updatedRowKeys = make(map[int]*kv.MemAwareHandleMap[bool]) + } + e.handles = e.handles[:0] + e.tableUpdatable = e.tableUpdatable[:0] + e.changed = e.changed[:0] + e.matches = e.matches[:0] + for _, content := range e.tblColPosInfos { + if e.updatedRowKeys[content.Start] == nil { + e.updatedRowKeys[content.Start] = kv.NewMemAwareHandleMap[bool]() + } + handle, err := content.HandleCols.BuildHandleByDatums(row) + if err != nil { + return err + } + e.handles = append(e.handles, handle) + + updatable := false + flags := e.assignFlag[content.Start:content.End] + for _, flag := range flags { + if flag >= 0 { + updatable = true + break + } + } + if unmatchedOuterRow(content, row) { + updatable = false + } + e.tableUpdatable = append(e.tableUpdatable, updatable) + + changed, ok := e.updatedRowKeys[content.Start].Get(handle) + if ok { + e.changed = append(e.changed, changed) + e.matches = append(e.matches, false) + } else { + e.changed = append(e.changed, false) + e.matches = append(e.matches, true) + } + } + return nil +} + +func (e *UpdateExec) merge(row, newData []types.Datum, mergeGenerated bool) error { + if e.mergedRowData == nil { + e.mergedRowData = make(map[int64]*kv.MemAwareHandleMap[[]types.Datum]) + } + var mergedData []types.Datum + // merge updates from and into mergedRowData + for i, content := range e.tblColPosInfos { + if !e.multiUpdateOnSameTable[content.TblID] { + // No need to merge if not multi-updated + continue + } + if !e.tableUpdatable[i] { + // If there's nothing to update, we can just skip current row + continue + } + if e.changed[i] { + // Each matched row is updated once, even if it matches the conditions multiple times. + continue + } + handle := e.handles[i] + flags := e.assignFlag[content.Start:content.End] + + if e.mergedRowData[content.TblID] == nil { + e.mergedRowData[content.TblID] = kv.NewMemAwareHandleMap[[]types.Datum]() + } + tbl := e.tblID2table[content.TblID] + oldData := row[content.Start:content.End] + newTableData := newData[content.Start:content.End] + if v, ok := e.mergedRowData[content.TblID].Get(handle); ok { + mergedData = v + for i, flag := range flags { + if tbl.WritableCols()[i].IsGenerated() != mergeGenerated { + continue + } + mergedData[i].Copy(&oldData[i]) + if flag >= 0 { + newTableData[i].Copy(&mergedData[i]) + } else { + mergedData[i].Copy(&newTableData[i]) + } + } + } else { + mergedData = append([]types.Datum{}, newTableData...) + } + + memDelta := e.mergedRowData[content.TblID].Set(handle, mergedData) + memDelta += types.EstimatedMemUsage(mergedData, 1) + int64(handle.ExtraMemSize()) + e.memTracker.Consume(memDelta) + } + return nil +} + +func (e *UpdateExec) exec(ctx context.Context, _ *expression.Schema, row, newData []types.Datum) error { + defer trace.StartRegion(ctx, "UpdateExec").End() + bAssignFlag := make([]bool, len(e.assignFlag)) + for i, flag := range e.assignFlag { + bAssignFlag[i] = flag >= 0 + } + for i, content := range e.tblColPosInfos { + if !e.tableUpdatable[i] { + // If there's nothing to update, we can just skip current row + continue + } + if e.changed[i] { + // Each matched row is updated once, even if it matches the conditions multiple times. + continue + } + if e.matches[i] { + // Row is matched for the first time, increment `matched` counter + e.matched++ + } + tbl := e.tblID2table[content.TblID] + handle := e.handles[i] + + oldData := row[content.Start:content.End] + newTableData := newData[content.Start:content.End] + flags := bAssignFlag[content.Start:content.End] + + // Update row + fkChecks := e.fkChecks[content.TblID] + fkCascades := e.fkCascades[content.TblID] + changed, err1 := updateRecord(ctx, e.Ctx(), handle, oldData, newTableData, flags, tbl, false, e.memTracker, fkChecks, fkCascades) + if err1 == nil { + _, exist := e.updatedRowKeys[content.Start].Get(handle) + memDelta := e.updatedRowKeys[content.Start].Set(handle, changed) + if !exist { + memDelta += int64(handle.ExtraMemSize()) + } + e.memTracker.Consume(memDelta) + continue + } + + sc := e.Ctx().GetSessionVars().StmtCtx + if (kv.ErrKeyExists.Equal(err1) || table.ErrCheckConstraintViolated.Equal(err1)) && sc.DupKeyAsWarning { + sc.AppendWarning(err1) + continue + } + return err1 + } + return nil +} + +// unmatchedOuterRow checks the tableCols of a record to decide whether that record +// can not be updated. The handle is NULL only when it is the inner side of an +// outer join: the outer row can not match any inner rows, and in this scenario +// the inner handle field is filled with a NULL value. +// +// This fixes: https://github.com/pingcap/tidb/issues/7176. +func unmatchedOuterRow(tblPos plannercore.TblColPosInfo, waitUpdateRow []types.Datum) bool { + firstHandleIdx := tblPos.HandleCols.GetCol(0) + return waitUpdateRow[firstHandleIdx.Index].IsNull() +} + +// Next implements the Executor Next interface. +func (e *UpdateExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + if !e.drained { + if e.collectRuntimeStatsEnabled() { + ctx = context.WithValue(ctx, autoid.AllocatorRuntimeStatsCtxKey, e.stats.AllocatorRuntimeStats) + } + numRows, err := e.updateRows(ctx) + if err != nil { + return err + } + e.drained = true + e.Ctx().GetSessionVars().StmtCtx.AddRecordRows(uint64(numRows)) + } + return nil +} + +func (e *UpdateExec) updateRows(ctx context.Context) (int, error) { + fields := exec.RetTypes(e.Children(0)) + colsInfo := plannercore.GetUpdateColumnsInfo(e.tblID2table, e.tblColPosInfos, len(fields)) + globalRowIdx := 0 + chk := exec.TryNewCacheChunk(e.Children(0)) + if !e.allAssignmentsAreConstant { + e.evalBuffer = chunk.MutRowFromTypes(fields) + } + composeFunc := e.fastComposeNewRow + if !e.allAssignmentsAreConstant { + composeFunc = e.composeNewRow + } + memUsageOfChk := int64(0) + totalNumRows := 0 + for { + e.memTracker.Consume(-memUsageOfChk) + err := exec.Next(ctx, e.Children(0), chk) + if err != nil { + return 0, err + } + + if chk.NumRows() == 0 { + break + } + memUsageOfChk = chk.MemoryUsage() + e.memTracker.Consume(memUsageOfChk) + if e.collectRuntimeStatsEnabled() { + txn, err := e.Ctx().Txn(true) + if err == nil && txn.GetSnapshot() != nil { + txn.GetSnapshot().SetOption(kv.CollectRuntimeStats, e.stats.SnapshotRuntimeStats) + } + } + txn, err := e.Ctx().Txn(true) + if err == nil { + sc := e.Ctx().GetSessionVars().StmtCtx + txn.SetOption(kv.ResourceGroupTagger, sc.GetResourceGroupTagger()) + if sc.KvExecCounter != nil { + // Bind an interceptor for client-go to count the number of SQL executions of each TiKV. + txn.SetOption(kv.RPCInterceptor, sc.KvExecCounter.RPCInterceptor()) + } + } + for rowIdx := 0; rowIdx < chk.NumRows(); rowIdx++ { + chunkRow := chk.GetRow(rowIdx) + datumRow := chunkRow.GetDatumRow(fields) + // precomputes handles + if err := e.prepare(datumRow); err != nil { + return 0, err + } + // compose non-generated columns + newRow, err := composeFunc(globalRowIdx, datumRow, colsInfo) + if err != nil { + return 0, err + } + // merge non-generated columns + if err := e.merge(datumRow, newRow, false); err != nil { + return 0, err + } + if e.virtualAssignmentsOffset < len(e.OrderedList) { + // compose generated columns + newRow, err = e.composeGeneratedColumns(globalRowIdx, newRow, colsInfo) + if err != nil { + return 0, err + } + // merge generated columns + if err := e.merge(datumRow, newRow, true); err != nil { + return 0, err + } + } + // write to table + if err := e.exec(ctx, e.Children(0).Schema(), datumRow, newRow); err != nil { + return 0, err + } + } + totalNumRows += chk.NumRows() + chk = chunk.Renew(chk, e.MaxChunkSize()) + } + return totalNumRows, nil +} + +func (*UpdateExec) handleErr(colName model.CIStr, rowIdx int, err error) error { + if err == nil { + return nil + } + + if types.ErrDataTooLong.Equal(err) { + return resetErrDataTooLong(colName.O, rowIdx+1, err) + } + + if types.ErrOverflow.Equal(err) { + return types.ErrWarnDataOutOfRange.GenWithStackByArgs(colName.O, rowIdx+1) + } + + return err +} + +func (e *UpdateExec) fastComposeNewRow(rowIdx int, oldRow []types.Datum, cols []*table.Column) ([]types.Datum, error) { + newRowData := types.CloneRow(oldRow) + for _, assign := range e.OrderedList { + tblIdx := e.assignFlag[assign.Col.Index] + if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { + continue + } + con := assign.Expr.(*expression.Constant) + val, err := con.Eval(emptyRow) + if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { + return nil, err + } + + // info of `_tidb_rowid` column is nil. + // No need to cast `_tidb_rowid` column value. + if cols[assign.Col.Index] != nil { + val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) + if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { + return nil, err + } + } + + val.Copy(&newRowData[assign.Col.Index]) + } + return newRowData, nil +} + +func (e *UpdateExec) composeNewRow(rowIdx int, oldRow []types.Datum, cols []*table.Column) ([]types.Datum, error) { + newRowData := types.CloneRow(oldRow) + e.evalBuffer.SetDatums(newRowData...) + for _, assign := range e.OrderedList[:e.virtualAssignmentsOffset] { + tblIdx := e.assignFlag[assign.Col.Index] + if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { + continue + } + val, err := assign.Expr.Eval(e.evalBuffer.ToRow()) + if err != nil { + return nil, err + } + + // info of `_tidb_rowid` column is nil. + // No need to cast `_tidb_rowid` column value. + if cols[assign.Col.Index] != nil { + val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) + if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { + return nil, err + } + } + + val.Copy(&newRowData[assign.Col.Index]) + } + return newRowData, nil +} + +func (e *UpdateExec) composeGeneratedColumns(rowIdx int, newRowData []types.Datum, cols []*table.Column) ([]types.Datum, error) { + if e.allAssignmentsAreConstant { + return newRowData, nil + } + e.evalBuffer.SetDatums(newRowData...) + for _, assign := range e.OrderedList[e.virtualAssignmentsOffset:] { + tblIdx := e.assignFlag[assign.Col.Index] + if tblIdx >= 0 && !e.tableUpdatable[tblIdx] { + continue + } + val, err := assign.Expr.Eval(e.evalBuffer.ToRow()) + if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { + return nil, err + } + + // info of `_tidb_rowid` column is nil. + // No need to cast `_tidb_rowid` column value. + if cols[assign.Col.Index] != nil { + val, err = table.CastValue(e.Ctx(), val, cols[assign.Col.Index].ColumnInfo, false, false) + if err = e.handleErr(assign.ColName, rowIdx, err); err != nil { + return nil, err + } + } + + val.Copy(&newRowData[assign.Col.Index]) + e.evalBuffer.SetDatum(assign.Col.Index, val) + } + return newRowData, nil +} + +// Close implements the Executor Close interface. +func (e *UpdateExec) Close() error { + defer e.memTracker.ReplaceBytesUsed(0) + e.setMessage() + if e.RuntimeStats() != nil && e.stats != nil { + txn, err := e.Ctx().Txn(false) + if err == nil && txn.Valid() && txn.GetSnapshot() != nil { + txn.GetSnapshot().SetOption(kv.CollectRuntimeStats, nil) + } + defer e.Ctx().GetSessionVars().StmtCtx.RuntimeStatsColl.RegisterStats(e.ID(), e.stats) + } + return e.Children(0).Close() +} + +// Open implements the Executor Open interface. +func (e *UpdateExec) Open(ctx context.Context) error { + e.memTracker = memory.NewTracker(e.ID(), -1) + e.memTracker.AttachTo(e.Ctx().GetSessionVars().StmtCtx.MemTracker) + + return e.Children(0).Open(ctx) +} + +// setMessage sets info message(ERR_UPDATE_INFO) generated by UPDATE statement +func (e *UpdateExec) setMessage() { + stmtCtx := e.Ctx().GetSessionVars().StmtCtx + numMatched := e.matched + numChanged := stmtCtx.UpdatedRows() + numWarnings := stmtCtx.WarningCount() + msg := fmt.Sprintf(mysql.MySQLErrName[mysql.ErrUpdateInfo].Raw, numMatched, numChanged, numWarnings) + stmtCtx.SetMessage(msg) +} + +func (e *UpdateExec) collectRuntimeStatsEnabled() bool { + if e.RuntimeStats() != nil { + if e.stats == nil { + e.stats = &updateRuntimeStats{ + SnapshotRuntimeStats: &txnsnapshot.SnapshotRuntimeStats{}, + AllocatorRuntimeStats: autoid.NewAllocatorRuntimeStats(), + } + } + return true + } + return false +} + +// updateRuntimeStats is the execution stats about update statements. +type updateRuntimeStats struct { + *txnsnapshot.SnapshotRuntimeStats + *autoid.AllocatorRuntimeStats +} + +func (e *updateRuntimeStats) String() string { + if e.SnapshotRuntimeStats == nil && e.AllocatorRuntimeStats == nil { + return "" + } + buf := bytes.NewBuffer(make([]byte, 0, 16)) + if e.SnapshotRuntimeStats != nil { + stats := e.SnapshotRuntimeStats.String() + if stats != "" { + buf.WriteString(stats) + } + } + if e.AllocatorRuntimeStats != nil { + stats := e.AllocatorRuntimeStats.String() + if stats != "" { + if buf.Len() > 0 { + buf.WriteString(", ") + } + buf.WriteString(stats) + } + } + return buf.String() +} + +// Clone implements the RuntimeStats interface. +func (e *updateRuntimeStats) Clone() execdetails.RuntimeStats { + newRs := &updateRuntimeStats{} + if e.SnapshotRuntimeStats != nil { + snapshotStats := e.SnapshotRuntimeStats.Clone() + newRs.SnapshotRuntimeStats = snapshotStats + } + if e.AllocatorRuntimeStats != nil { + newRs.AllocatorRuntimeStats = e.AllocatorRuntimeStats.Clone() + } + return newRs +} + +// Merge implements the RuntimeStats interface. +func (e *updateRuntimeStats) Merge(other execdetails.RuntimeStats) { + tmp, ok := other.(*updateRuntimeStats) + if !ok { + return + } + if tmp.SnapshotRuntimeStats != nil { + if e.SnapshotRuntimeStats == nil { + snapshotStats := tmp.SnapshotRuntimeStats.Clone() + e.SnapshotRuntimeStats = snapshotStats + } else { + e.SnapshotRuntimeStats.Merge(tmp.SnapshotRuntimeStats) + } + } + if tmp.AllocatorRuntimeStats != nil { + if e.AllocatorRuntimeStats == nil { + e.AllocatorRuntimeStats = tmp.AllocatorRuntimeStats.Clone() + } + } +} + +// Tp implements the RuntimeStats interface. +func (*updateRuntimeStats) Tp() int { + return execdetails.TpUpdateRuntimeStats +} + +// GetFKChecks implements WithForeignKeyTrigger interface. +func (e *UpdateExec) GetFKChecks() []*FKCheckExec { + fkChecks := make([]*FKCheckExec, 0, len(e.fkChecks)) + for _, fkc := range e.fkChecks { + fkChecks = append(fkChecks, fkc...) + } + return fkChecks +} + +// GetFKCascades implements WithForeignKeyTrigger interface. +func (e *UpdateExec) GetFKCascades() []*FKCascadeExec { + fkCascades := make([]*FKCascadeExec, 0, len(e.fkChecks)) + for _, fkc := range e.fkCascades { + fkCascades = append(fkCascades, fkc...) + } + return fkCascades +} + +// HasFKCascades implements WithForeignKeyTrigger interface. +func (e *UpdateExec) HasFKCascades() bool { + return len(e.fkCascades) > 0 +} diff --git a/pkg/executor/update_test.go b/pkg/executor/update_test.go new file mode 100644 index 0000000000000..8a9916a81569a --- /dev/null +++ b/pkg/executor/update_test.go @@ -0,0 +1,644 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package executor_test + +import ( + "fmt" + "strconv" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestUpdateGenColInTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t(a bigint, b bigint as (a+1));`) + tk.MustExec(`begin;`) + tk.MustExec(`insert into t(a) values(1);`) + err := tk.ExecToErr(`update t set b=6 where b=2;`) + require.Equal( + t, + "[planner:3105]The value specified for generated column 'b' in table 't' is not allowed.", + err.Error(), + ) + tk.MustExec(`commit;`) + tk.MustQuery(`select * from t;`).Check( + testkit.Rows( + `1 2`, + ), + ) +} + +func TestUpdateWithAutoidSchema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`create table t1(id int primary key auto_increment, n int);`) + tk.MustExec(`create table t2(id int primary key, n float auto_increment, key I_n(n));`) + tk.MustExec(`create table t3(id int primary key, n double auto_increment, key I_n(n));`) + + tests := []struct { + exec string + query string + result [][]interface{} + }{ + { + `insert into t1 set n = 1`, + `select * from t1 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `update t1 set id = id+1`, + `select * from t1 where id = 2`, + testkit.Rows(`2 1`), + }, + { + `insert into t1 set n = 2`, + `select * from t1 where id = 3`, + testkit.Rows(`3 2`), + }, + { + `update t1 set id = id + '1.1' where id = 3`, + `select * from t1 where id = 4`, + testkit.Rows(`4 2`), + }, + { + `insert into t1 set n = 3`, + `select * from t1 where id = 5`, + testkit.Rows(`5 3`), + }, + { + `update t1 set id = id + '0.5' where id = 5`, + `select * from t1 where id = 6`, + testkit.Rows(`6 3`), + }, + { + `insert into t1 set n = 4`, + `select * from t1 where id = 7`, + testkit.Rows(`7 4`), + }, + { + `insert into t2 set id = 1`, + `select * from t2 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `update t2 set n = n+1`, + `select * from t2 where id = 1`, + testkit.Rows(`1 2`), + }, + { + `insert into t2 set id = 2`, + `select * from t2 where id = 2`, + testkit.Rows(`2 3`), + }, + { + `update t2 set n = n + '2.2'`, + `select * from t2 where id = 2`, + testkit.Rows(`2 5.2`), + }, + { + `insert into t2 set id = 3`, + `select * from t2 where id = 3`, + testkit.Rows(`3 6`), + }, + { + `update t2 set n = n + '0.5' where id = 3`, + `select * from t2 where id = 3`, + testkit.Rows(`3 6.5`), + }, + { + `insert into t2 set id = 4`, + `select * from t2 where id = 4`, + testkit.Rows(`4 7`), + }, + { + `insert into t3 set id = 1`, + `select * from t3 where id = 1`, + testkit.Rows(`1 1`), + }, + { + `update t3 set n = n+1`, + `select * from t3 where id = 1`, + testkit.Rows(`1 2`), + }, + { + `insert into t3 set id = 2`, + `select * from t3 where id = 2`, + testkit.Rows(`2 3`), + }, + { + `update t3 set n = n + '3.3'`, + `select * from t3 where id = 2`, + testkit.Rows(`2 6.3`), + }, + { + `insert into t3 set id = 3`, + `select * from t3 where id = 3`, + testkit.Rows(`3 7`), + }, + { + `update t3 set n = n + '0.5' where id = 3`, + `select * from t3 where id = 3`, + testkit.Rows(`3 7.5`), + }, + { + `insert into t3 set id = 4`, + `select * from t3 where id = 4`, + testkit.Rows(`4 8`), + }, + } + + for _, tt := range tests { + tk.MustExec(tt.exec) + tk.MustQuery(tt.query).Check(tt.result) + } +} + +func TestUpdateSchemaChange(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t(a bigint, b bigint as (a+1));`) + tk.MustExec(`begin;`) + tk.MustExec(`insert into t(a) values(1);`) + err := tk.ExecToErr(`update t set b=6 where b=2;`) + require.Equal( + t, + "[planner:3105]The value specified for generated column 'b' in table 't' is not allowed.", + err.Error(), + ) + tk.MustExec(`commit;`) + tk.MustQuery(`select * from t;`).Check( + testkit.Rows( + `1 2`, + ), + ) +} + +func TestUpdateMultiDatabaseTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop database if exists test2") + tk.MustExec("create database test2") + tk.MustExec("create table t(a int, b int generated always as (a+1) virtual)") + tk.MustExec("create table test2.t(a int, b int generated always as (a+1) virtual)") + tk.MustExec("update t, test2.t set test.t.a=1") +} + +func TestUpdateSwapColumnValues(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1 (c_str varchar(40))") + tk.MustExec("create table t2 (c_str varchar(40))") + tk.MustExec("insert into t1 values ('Alice')") + tk.MustExec("insert into t2 values ('Bob')") + tk.MustQuery("select t1.c_str, t2.c_str from t1, t2 where t1.c_str <= t2.c_str").Check(testkit.Rows("Alice Bob")) + tk.MustExec("update t1, t2 set t1.c_str = t2.c_str, t2.c_str = t1.c_str where t1.c_str <= t2.c_str") + tk.MustQuery("select t1.c_str, t2.c_str from t1, t2 where t1.c_str <= t2.c_str").Check(testkit.Rows()) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values(1, 2)") + tk.MustQuery("select * from t").Check(testkit.Rows("1 2")) + tk.MustExec("update t set a=b, b=a") + tk.MustQuery("select * from t").Check(testkit.Rows("2 1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (1,3)") + tk.MustQuery("select * from t").Check(testkit.Rows("1 3")) + tk.MustExec("update t set a=b, b=a") + tk.MustQuery("select * from t").Check(testkit.Rows("3 1")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, c int as (-a) virtual, d int as (-b) stored)") + tk.MustExec("insert into t(a, b) values (10, 11), (20, 22)") + tk.MustQuery("select * from t").Check(testkit.Rows("10 11 -10 -11", "20 22 -20 -22")) + tk.MustExec("update t set a=b, b=a") + tk.MustQuery("select * from t").Check(testkit.Rows("11 10 -11 -10", "22 20 -22 -20")) + tk.MustExec("update t set b=30, a=b") + tk.MustQuery("select * from t").Check(testkit.Rows("10 30 -10 -30", "20 30 -20 -30")) +} + +func TestMultiUpdateOnSameTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(x int, y int)") + tk.MustExec("insert into t values()") + tk.MustExec("update t t1, t t2 set t2.y=1, t1.x=2") + tk.MustQuery("select * from t").Check(testkit.Rows("2 1")) + tk.MustExec("update t t1, t t2 set t1.x=t2.y, t2.y=t1.x") + tk.MustQuery("select * from t").Check(testkit.Rows("1 2")) + + // Update generated columns + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(x int, y int, z int as (x+10) stored, w int as (y-10) virtual)") + tk.MustExec("insert into t(x, y) values(1, 2), (3, 4)") + tk.MustExec("update t t1, t t2 set t2.y=1, t1.x=2 where t1.x=1") + tk.MustQuery("select * from t").Check(testkit.Rows("2 1 12 -9", "3 1 13 -9")) + + tk.MustExec("update t t1, t t2 set t1.x=5, t2.y=t1.x where t1.x=3") + tk.MustQuery("select * from t").Check(testkit.Rows("2 3 12 -7", "5 3 15 -7")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int as (a+b) stored)") + tk.MustExec("insert into t(a, b) values (1, 2)") + tk.MustExec("update t t1, t t2 set t2.a=3") + tk.MustQuery("select * from t").Check(testkit.Rows("3 2 5")) + + tk.MustExec("update t t1, t t2 set t1.a=4, t2.b=5") + tk.MustQuery("select * from t").Check(testkit.Rows("4 5 9")) + + // Update primary keys + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int primary key)") + tk.MustExec("insert into t values (1), (2)") + tk.MustExec("update t set a=a+2") + tk.MustQuery("select * from t").Check(testkit.Rows("3", "4")) + tk.MustExec("update t m, t n set m.a = n.a+10 where m.a=n.a") + tk.MustQuery("select * from t").Check(testkit.Rows("13", "14")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int primary key, b int)") + tk.MustExec("insert into t values (1,3), (2,4)") + tk.MustGetErrMsg( + "update t m, t n set m.a = n.a+10, n.b = m.b+1 where m.a=n.a", + `[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`, + ) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, c int, primary key(a, b))") + tk.MustExec("insert into t values (1,3,5), (2,4,6)") + tk.MustExec("update t m, t n set m.a = n.a+10, m.b = n.b+10 where m.a=n.a") + tk.MustQuery("select * from t").Check(testkit.Rows("11 13 5", "12 14 6")) + tk.MustExec("update t m, t n, t q set q.c=m.a+n.b, n.c = m.a+1, m.c = n.b+1 where m.b=n.b AND m.a=q.a") + tk.MustQuery("select * from t").Check(testkit.Rows("11 13 24", "12 14 26")) + tk.MustGetErrMsg( + "update t m, t n, t q set m.a = m.a+1, n.c = n.c-1, q.c = q.a+q.b where m.b=n.b and n.b=q.b", + `[planner:1706]Primary key/partition key update is not allowed since the table is updated both as 'm' and 'n'.`, + ) +} + +func TestUpdateClusterIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(id varchar(200) primary key, v int)`) + tk.MustExec(`insert into t(id, v) values ('abc', 233)`) + tk.MustQuery(`select id, v from t where id = 'abc'`).Check(testkit.Rows("abc 233")) + tk.MustExec(`update t set id = 'dfg' where id = 'abc'`) + tk.MustQuery(`select * from t`).Check(testkit.Rows("dfg 233")) + tk.MustExec(`update t set id = 'aaa', v = 333 where id = 'dfg'`) + tk.MustQuery(`select * from t where id = 'aaa'`).Check(testkit.Rows("aaa 333")) + tk.MustExec(`update t set v = 222 where id = 'aaa'`) + tk.MustQuery(`select * from t where id = 'aaa'`).Check(testkit.Rows("aaa 222")) + tk.MustExec(`insert into t(id, v) values ('bbb', 111)`) + tk.MustGetErrCode(`update t set id = 'bbb' where id = 'aaa'`, errno.ErrDupEntry) + + tk.MustExec(`drop table if exists ut3pk`) + tk.MustExec(`create table ut3pk(id1 varchar(200), id2 varchar(200), v int, id3 int, primary key(id1, id2, id3))`) + tk.MustExec(`insert into ut3pk(id1, id2, v, id3) values ('aaa', 'bbb', 233, 111)`) + tk.MustQuery(`select id1, id2, id3, v from ut3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows("aaa bbb 111 233")) + tk.MustExec(`update ut3pk set id1 = 'abc', id2 = 'bbb2', id3 = 222, v = 555 where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`) + tk.MustQuery(`select id1, id2, id3, v from ut3pk where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`).Check(testkit.Rows("abc bbb2 222 555")) + tk.MustQuery(`select id1, id2, id3, v from ut3pk`).Check(testkit.Rows("abc bbb2 222 555")) + tk.MustExec(`update ut3pk set v = 666 where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`) + tk.MustQuery(`select id1, id2, id3, v from ut3pk`).Check(testkit.Rows("abc bbb2 222 666")) + tk.MustExec(`insert into ut3pk(id1, id2, id3, v) values ('abc', 'bbb3', 222, 777)`) + tk.MustGetErrCode( + `update ut3pk set id2 = 'bbb3' where id1 = 'abc' and id2 = 'bbb2' and id3 = 222`, + errno.ErrDupEntry, + ) + + tk.MustExec(`drop table if exists ut1pku`) + tk.MustExec(`create table ut1pku(id varchar(200) primary key, uk int, v int, unique key ukk(uk))`) + tk.MustExec(`insert into ut1pku(id, uk, v) values('a', 1, 2), ('b', 2, 3)`) + tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 1 2", "b 2 3")) + tk.MustExec(`update ut1pku set uk = 3 where id = 'a'`) + tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 3 2", "b 2 3")) + tk.MustGetErrCode(`update ut1pku set uk = 2 where id = 'a'`, errno.ErrDupEntry) + tk.MustQuery(`select * from ut1pku`).Check(testkit.Rows("a 3 2", "b 2 3")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a char(10) primary key, b char(10));") + tk.MustExec("insert into t values('a', 'b');") + tk.MustExec("update t set a='c' where t.a='a' and b='b';") + tk.MustQuery("select * from t").Check(testkit.Rows("c b")) + + tk.MustExec("drop table if exists s") + tk.MustExec("create table s (a int, b int, c int, primary key (a, b))") + tk.MustExec("insert s values (3, 3, 3), (5, 5, 5)") + tk.MustExec("update s set c = 10 where a = 3") + tk.MustQuery("select * from s").Check(testkit.Rows("3 3 10", "5 5 5")) +} + +func TestDeleteClusterIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(id varchar(200) primary key, v int)`) + tk.MustExec(`insert into t(id, v) values ('abc', 233)`) + tk.MustExec(`delete from t where id = 'abc'`) + tk.MustQuery(`select * from t`).Check(testkit.Rows()) + tk.MustQuery(`select * from t where id = 'abc'`).Check(testkit.Rows()) + + tk.MustExec(`drop table if exists it3pk`) + tk.MustExec(`create table it3pk(id1 varchar(200), id2 varchar(200), v int, id3 int, primary key(id1, id2, id3))`) + tk.MustExec(`insert into it3pk(id1, id2, v, id3) values ('aaa', 'bbb', 233, 111)`) + tk.MustExec(`delete from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`) + tk.MustQuery(`select * from it3pk`).Check(testkit.Rows()) + tk.MustQuery(`select * from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows()) + tk.MustExec(`insert into it3pk(id1, id2, v, id3) values ('aaa', 'bbb', 433, 111)`) + tk.MustQuery(`select * from it3pk where id1 = 'aaa' and id2 = 'bbb' and id3 = 111`).Check(testkit.Rows("aaa bbb 433 111")) + + tk.MustExec(`drop table if exists dt3pku`) + tk.MustExec(`create table dt3pku(id varchar(200) primary key, uk int, v int, unique key uuk(uk))`) + tk.MustExec(`insert into dt3pku(id, uk, v) values('a', 1, 2)`) + tk.MustExec(`delete from dt3pku where id = 'a'`) + tk.MustQuery(`select * from dt3pku`).Check(testkit.Rows()) + tk.MustExec(`insert into dt3pku(id, uk, v) values('a', 1, 2)`) + + tk.MustExec("drop table if exists s1") + tk.MustExec("create table s1 (a int, b int, c int, primary key (a, b))") + tk.MustExec("insert s1 values (3, 3, 3), (5, 5, 5)") + tk.MustExec("delete from s1 where a = 3") + tk.MustQuery("select * from s1").Check(testkit.Rows("5 5 5")) +} + +func TestReplaceClusterIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + + tk.MustExec(`drop table if exists rt1pk`) + tk.MustExec(`create table rt1pk(id varchar(200) primary key, v int)`) + tk.MustExec(`replace into rt1pk(id, v) values('abc', 1)`) + tk.MustQuery(`select * from rt1pk`).Check(testkit.Rows("abc 1")) + tk.MustExec(`replace into rt1pk(id, v) values('bbb', 233), ('abc', 2)`) + tk.MustQuery(`select * from rt1pk`).Check(testkit.Rows("abc 2", "bbb 233")) + + tk.MustExec(`drop table if exists rt3pk`) + tk.MustExec(`create table rt3pk(id1 timestamp, id2 time, v int, id3 year, primary key(id1, id2, id3))`) + tk.MustExec(`replace into rt3pk(id1, id2,id3, v) values('2018-01-01 11:11:11', '22:22:22', '2019', 1)`) + tk.MustQuery(`select * from rt3pk`).Check(testkit.Rows("2018-01-01 11:11:11 22:22:22 1 2019")) + tk.MustExec(`replace into rt3pk(id1, id2, id3, v) values('2018-01-01 11:11:11', '22:22:22', '2019', 2)`) + tk.MustQuery(`select * from rt3pk`).Check(testkit.Rows("2018-01-01 11:11:11 22:22:22 2 2019")) + + tk.MustExec(`drop table if exists rt1pk1u`) + tk.MustExec(`create table rt1pk1u(id varchar(200) primary key, uk int, v int, unique key uuk(uk))`) + tk.MustExec(`replace into rt1pk1u(id, uk, v) values("abc", 2, 1)`) + tk.MustQuery(`select * from rt1pk1u`).Check(testkit.Rows("abc 2 1")) + tk.MustExec(`replace into rt1pk1u(id, uk, v) values("aaa", 2, 11)`) + tk.MustQuery(`select * from rt1pk1u`).Check(testkit.Rows("aaa 2 11")) +} + +func TestPessimisticUpdatePKLazyCheck(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOn) + testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeOff) + testUpdatePKLazyCheck(t, tk, variable.ClusteredIndexDefModeIntOnly) +} + +func testUpdatePKLazyCheck(t *testing.T, tk *testkit.TestKit, clusteredIndex variable.ClusteredIndexDefMode) { + tk.Session().GetSessionVars().EnableClusteredIndex = clusteredIndex + tk.MustExec(`drop table if exists upk`) + tk.MustExec(`create table upk (a int, b int, c int, primary key (a, b))`) + tk.MustExec(`insert upk values (1, 1, 1), (2, 2, 2), (3, 3, 3)`) + tk.MustExec("begin pessimistic") + tk.MustExec("update upk set b = b + 1 where a between 1 and 2") + require.Equal(t, 2, getPresumeExistsCount(t, tk.Session())) + err := tk.ExecToErr("update upk set a = 3, b = 3 where a between 1 and 2") + require.True(t, kv.ErrKeyExists.Equal(err)) + tk.MustExec("commit") +} + +func getPresumeExistsCount(t *testing.T, se session.Session) int { + txn, err := se.Txn(false) + require.NoError(t, err) + buf := txn.GetMemBuffer() + it, err := buf.Iter(nil, nil) + require.NoError(t, err) + presumeNotExistsCnt := 0 + for it.Valid() { + flags, err1 := buf.GetFlags(it.Key()) + require.Nil(t, err1) + err = it.Next() + require.NoError(t, err) + if flags.HasPresumeKeyNotExists() { + presumeNotExistsCnt++ + } + } + return presumeNotExistsCnt +} + +func TestOutOfRangeWithUnsigned(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(ts int(10) unsigned NULL DEFAULT NULL)`) + tk.MustExec(`insert into t values(1)`) + tk.MustGetErrMsg( + "update t set ts = IF(ts < (0 - ts), 1,1) where ts>0", + "[types:1690]BIGINT UNSIGNED value is out of range in '(0 - test.t.ts)'", + ) +} + +func TestIssue21447(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1, tk2 := testkit.NewTestKit(t, store), testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("drop table if exists t1") + tk1.MustExec("create table t1(id int primary key, name varchar(40))") + tk1.MustExec("insert into t1 values(1, 'abc')") + + tk1.MustExec("begin pessimistic") + tk2.MustExec("begin pessimistic") + tk2.MustExec("update t1 set name='xyz' where id=1") + tk2.CheckExecResult(1, 0) + tk2.MustQuery("select * from t1 where id = 1").Check(testkit.Rows("1 xyz")) + tk2.MustExec("commit") + tk1.MustExec("update t1 set name='xyz' where id=1") + tk1.CheckExecResult(0, 0) + tk1.MustQuery("select * from t1 where id = 1").Check(testkit.Rows("1 abc")) + tk1.MustQuery("select * from t1 where id = 1 for update").Check(testkit.Rows("1 xyz")) + tk1.MustQuery("select * from t1 where id in (1, 2)").Check(testkit.Rows("1 abc")) + tk1.MustQuery("select * from t1 where id in (1, 2) for update").Check(testkit.Rows("1 xyz")) + tk1.MustExec("commit") +} + +func TestIssue23553(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`drop table if exists tt`) + tk.MustExec(`create table tt (m0 varchar(64), status tinyint not null)`) + tk.MustExec(`insert into tt values('1',0),('1',0),('1',0)`) + tk.MustExec(`update tt a inner join (select m0 from tt where status!=1 group by m0 having count(*)>1) b on a.m0=b.m0 set a.status=1`) +} + +func TestLockUnchangedUniqueKeys(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + + for _, shouldLock := range []bool{true, false} { + for _, tt := range []struct { + name string + create string + insert string + update string + isClusteredPK bool + }{ + { + // ref https://github.com/pingcap/tidb/issues/36438 + "Issue36438", + "create table t (i varchar(10), unique key(i))", + "insert into t values ('a')", + "update t set i = 'a'", + false, + }, + { + "ClusteredAndRowUnchanged", + "create table t (k int, v int, primary key(k) clustered, key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + true, + }, + { + "ClusteredAndRowUnchangedAndParted", + "create table t (k int, v int, primary key(k) clustered, key sk(k)) partition by hash(k) partitions 4", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + true, + }, + { + "ClusteredAndRowChanged", + "create table t (k int, v int, primary key(k) clustered, key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 11 where k = 1", + true, + }, + { + "NonClusteredAndRowUnchanged", + "create table t (k int, v int, primary key(k) nonclustered, key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + false, + }, + { + "NonClusteredAndRowUnchangedAndParted", + "create table t (k int, v int, primary key(k) nonclustered, key sk(k)) partition by hash(k) partitions 4", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + false, + }, + { + "NonClusteredAndRowChanged", + "create table t (k int, v int, primary key(k) nonclustered, key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 11 where k = 1", + false, + }, + { + "UniqueAndRowUnchanged", + "create table t (k int, v int, unique key uk(k), key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + false, + }, + { + "UniqueAndRowUnchangedAndParted", + "create table t (k int, v int, unique key uk(k), key sk(k)) partition by hash(k) partitions 4", + "insert into t values (1, 10)", + "update t force index(sk) set v = 10 where k = 1", + false, + }, + { + "UniqueAndRowChanged", + "create table t (k int, v int, unique key uk(k), key sk(k))", + "insert into t values (1, 10)", + "update t force index(sk) set v = 11 where k = 1", + false, + }, + } { + t.Run( + tt.name+"-"+strconv.FormatBool(shouldLock), func(t *testing.T) { + tk1.MustExec(fmt.Sprintf("set @@tidb_lock_unchanged_keys = %v", shouldLock)) + tk1.MustExec("drop table if exists t") + tk1.MustExec(tt.create) + tk1.MustExec(tt.insert) + tk1.MustExec("begin pessimistic") + + tk1.MustExec(tt.update) + + errCh := make(chan error, 1) + go func() { + _, err := tk2.Exec(tt.insert) + errCh <- err + }() + + select { + case <-time.After(100 * time.Millisecond): + if !shouldLock && !tt.isClusteredPK { + require.Fail(t, "insert is blocked by update") + } + tk1.MustExec("rollback") + require.Error(t, <-errCh) + case err := <-errCh: + require.Error(t, err) + if shouldLock { + require.Fail(t, "insert is not blocked by update") + } + } + }, + ) + } + } +} diff --git a/executor/utils.go b/pkg/executor/utils.go similarity index 100% rename from executor/utils.go rename to pkg/executor/utils.go diff --git a/executor/utils_test.go b/pkg/executor/utils_test.go similarity index 95% rename from executor/utils_test.go rename to pkg/executor/utils_test.go index 6891f78a66abd..12310d61c2ce1 100644 --- a/executor/utils_test.go +++ b/pkg/executor/utils_test.go @@ -18,10 +18,10 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/executor/window.go b/pkg/executor/window.go similarity index 97% rename from executor/window.go rename to pkg/executor/window.go index 2fb4cc76ded01..93ba5f919cbfe 100644 --- a/executor/window.go +++ b/pkg/executor/window.go @@ -18,15 +18,15 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/executor/aggfuncs" - "github.com/pingcap/tidb/executor/internal/exec" - "github.com/pingcap/tidb/executor/internal/vecgroupchecker" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/executor/aggfuncs" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/executor/internal/vecgroupchecker" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // WindowExec is the executor for window functions. diff --git a/executor/window_test.go b/pkg/executor/window_test.go similarity index 99% rename from executor/window_test.go rename to pkg/executor/window_test.go index 9f0d54b8a3cfa..3404acfea4d61 100644 --- a/executor/window_test.go +++ b/pkg/executor/window_test.go @@ -19,8 +19,8 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" ) func TestWindowFunctions(t *testing.T) { diff --git a/pkg/executor/write.go b/pkg/executor/write.go new file mode 100644 index 0000000000000..39187586979d5 --- /dev/null +++ b/pkg/executor/write.go @@ -0,0 +1,354 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package executor + +import ( + "context" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor/internal/exec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/tracing" +) + +var ( + _ exec.Executor = &UpdateExec{} + _ exec.Executor = &DeleteExec{} + _ exec.Executor = &InsertExec{} + _ exec.Executor = &ReplaceExec{} + _ exec.Executor = &LoadDataExec{} +) + +// updateRecord updates the row specified by the handle `h`, from `oldData` to `newData`. +// `modified` means which columns are really modified. It's used for secondary indices. +// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. +// The return values: +// 1. changed (bool) : does the update really change the row values. e.g. update set i = 1 where i = 1; +// 2. err (error) : error in the update. +func updateRecord( + ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, modified []bool, + t table.Table, + onDup bool, _ *memory.Tracker, fkChecks []*FKCheckExec, fkCascades []*FKCascadeExec, +) (bool, error) { + r, ctx := tracing.StartRegionEx(ctx, "executor.updateRecord") + defer r.End() + + sc := sctx.GetSessionVars().StmtCtx + changed, handleChanged := false, false + // onUpdateSpecified is for "UPDATE SET ts_field = old_value", the + // timestamp field is explicitly set, but not changed in fact. + onUpdateSpecified := make(map[int]bool) + + // We can iterate on public columns not writable columns, + // because all of them are sorted by their `Offset`, which + // causes all writable columns are after public columns. + + // Handle the bad null error. + for i, col := range t.Cols() { + var err error + if err = col.HandleBadNull(&newData[i], sc, 0); err != nil { + return false, err + } + } + + // Handle exchange partition + tbl := t.Meta() + if tbl.ExchangePartitionInfo != nil && tbl.GetPartitionInfo() == nil { + if err := checkRowForExchangePartition(sctx, newData, tbl); err != nil { + return false, err + } + } + + // Compare datum, then handle some flags. + for i, col := range t.Cols() { + // We should use binary collation to compare datum, otherwise the result will be incorrect. + cmp, err := newData[i].Compare(sc, &oldData[i], collate.GetBinaryCollator()) + if err != nil { + return false, err + } + if cmp != 0 { + changed = true + modified[i] = true + // Rebase auto increment id if the field is changed. + if mysql.HasAutoIncrementFlag(col.GetFlag()) { + recordID, err := getAutoRecordID(newData[i], &col.FieldType, false) + if err != nil { + return false, err + } + if err = t.Allocators(sctx).Get(autoid.AutoIncrementType).Rebase(ctx, recordID, true); err != nil { + return false, err + } + } + if col.IsPKHandleColumn(t.Meta()) { + handleChanged = true + // Rebase auto random id if the field is changed. + if err := rebaseAutoRandomValue(ctx, sctx, t, &newData[i], col); err != nil { + return false, err + } + } + if col.IsCommonHandleColumn(t.Meta()) { + handleChanged = true + } + } else { + if mysql.HasOnUpdateNowFlag(col.GetFlag()) && modified[i] { + // It's for "UPDATE t SET ts = ts" and ts is a timestamp. + onUpdateSpecified[i] = true + } + modified[i] = false + } + } + + sc.AddTouchedRows(1) + // If no changes, nothing to do, return directly. + if !changed { + // See https://dev.mysql.com/doc/refman/5.7/en/mysql-real-connect.html CLIENT_FOUND_ROWS + if sctx.GetSessionVars().ClientCapability&mysql.ClientFoundRows > 0 { + sc.AddAffectedRows(1) + } + keySet := lockRowKey + if sctx.GetSessionVars().LockUnchangedKeys { + keySet |= lockUniqueKeys + } + _, err := addUnchangedKeysForLockByRow(sctx, t, h, oldData, keySet) + return false, err + } + + // Fill values into on-update-now fields, only if they are really changed. + for i, col := range t.Cols() { + if mysql.HasOnUpdateNowFlag(col.GetFlag()) && !modified[i] && !onUpdateSpecified[i] { + v, err := expression.GetTimeValue(sctx, strings.ToUpper(ast.CurrentTimestamp), col.GetType(), col.GetDecimal(), nil) + if err != nil { + return false, err + } + newData[i] = v + modified[i] = true + // Only TIMESTAMP and DATETIME columns can be automatically updated, so it cannot be PKIsHandle. + // Ref: https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html + if col.IsPKHandleColumn(t.Meta()) { + return false, errors.Errorf("on-update-now column should never be pk-is-handle") + } + if col.IsCommonHandleColumn(t.Meta()) { + handleChanged = true + } + } + } + + // If handle changed, remove the old then add the new record, otherwise update the record. + if handleChanged { + // For `UPDATE IGNORE`/`INSERT IGNORE ON DUPLICATE KEY UPDATE` + // we use the staging buffer so that we don't need to precheck the existence of handle or unique keys by sending + // extra kv requests, and the remove action will not take effect if there are conflicts. + if updated, err := func() (bool, error) { + txn, err := sctx.Txn(true) + if err != nil { + return false, err + } + memBuffer := txn.GetMemBuffer() + sh := memBuffer.Staging() + defer memBuffer.Cleanup(sh) + + if err = t.RemoveRecord(sctx, h, oldData); err != nil { + return false, err + } + + _, err = t.AddRecord(sctx, newData, table.IsUpdate, table.WithCtx(ctx)) + if err != nil { + return false, err + } + memBuffer.Release(sh) + return true, nil + }(); err != nil { + if terr, ok := errors.Cause(err).(*terror.Error); sctx.GetSessionVars().StmtCtx.IgnoreNoPartition && ok && terr.Code() == errno.ErrNoPartitionForGivenValue { + return false, nil + } + return updated, err + } + } else { + // Update record to new value and update index. + if err := t.UpdateRecord(ctx, sctx, h, oldData, newData, modified); err != nil { + if terr, ok := errors.Cause(err).(*terror.Error); sctx.GetSessionVars().StmtCtx.IgnoreNoPartition && ok && terr.Code() == errno.ErrNoPartitionForGivenValue { + return false, nil + } + return false, err + } + if sctx.GetSessionVars().LockUnchangedKeys { + // Lock unique keys when handle unchanged + if _, err := addUnchangedKeysForLockByRow(sctx, t, h, oldData, lockUniqueKeys); err != nil { + return false, err + } + } + } + for _, fkt := range fkChecks { + err := fkt.updateRowNeedToCheck(sc, oldData, newData) + if err != nil { + return false, err + } + } + for _, fkc := range fkCascades { + err := fkc.onUpdateRow(sc, oldData, newData) + if err != nil { + return false, err + } + } + if onDup { + sc.AddAffectedRows(2) + } else { + sc.AddAffectedRows(1) + } + sc.AddUpdatedRows(1) + sc.AddCopiedRows(1) + + return true, nil +} + +const ( + lockRowKey = 1 << iota + lockUniqueKeys +) + +func addUnchangedKeysForLockByRow( + sctx sessionctx.Context, t table.Table, h kv.Handle, row []types.Datum, keySet int, +) (int, error) { + txnCtx := sctx.GetSessionVars().TxnCtx + if !txnCtx.IsPessimistic || keySet == 0 { + return 0, nil + } + count := 0 + physicalID := t.Meta().ID + if pt, ok := t.(table.PartitionedTable); ok { + p, err := pt.GetPartitionByRow(sctx, row) + if err != nil { + return 0, err + } + physicalID = p.GetPhysicalID() + } + if keySet&lockRowKey > 0 { + unchangedRowKey := tablecodec.EncodeRowKeyWithHandle(physicalID, h) + txnCtx.AddUnchangedKeyForLock(unchangedRowKey) + count++ + } + if keySet&lockUniqueKeys > 0 { + stmtCtx := sctx.GetSessionVars().StmtCtx + clustered := t.Meta().HasClusteredIndex() + for _, idx := range t.Indices() { + meta := idx.Meta() + if !meta.Unique || !meta.IsPublic() || (meta.Primary && clustered) { + continue + } + ukVals, err := idx.FetchValues(row, nil) + if err != nil { + return count, err + } + unchangedUniqueKey, _, err := tablecodec.GenIndexKey( + stmtCtx, + idx.TableMeta(), + meta, + physicalID, + ukVals, + h, + nil, + ) + if err != nil { + return count, err + } + txnCtx.AddUnchangedKeyForLock(unchangedUniqueKey) + count++ + } + } + return count, nil +} + +func rebaseAutoRandomValue( + ctx context.Context, sctx sessionctx.Context, t table.Table, newData *types.Datum, col *table.Column, +) error { + tableInfo := t.Meta() + if !tableInfo.ContainsAutoRandomBits() { + return nil + } + recordID, err := getAutoRecordID(*newData, &col.FieldType, false) + if err != nil { + return err + } + if recordID < 0 { + return nil + } + shardFmt := autoid.NewShardIDFormat(&col.FieldType, tableInfo.AutoRandomBits, tableInfo.AutoRandomRangeBits) + // Set bits except incremental_bits to zero. + recordID = recordID & shardFmt.IncrementalMask() + return t.Allocators(sctx).Get(autoid.AutoRandomType).Rebase(ctx, recordID, true) +} + +// resetErrDataTooLong reset ErrDataTooLong error msg. +// types.ErrDataTooLong is produced in types.ProduceStrWithSpecifiedTp, there is no column info in there, +// so we reset the error msg here, and wrap old err with errors.Wrap. +func resetErrDataTooLong(colName string, rowIdx int, _ error) error { + newErr := types.ErrDataTooLong.GenWithStack("Data too long for column '%v' at row %v", colName, rowIdx) + return newErr +} + +// checkRowForExchangePartition is only used for ExchangePartition by non-partitionTable during write only state. +// It check if rowData inserted or updated violate partition definition or checkConstraints of partitionTable. +func checkRowForExchangePartition(sctx sessionctx.Context, row []types.Datum, tbl *model.TableInfo) error { + is := sctx.GetDomainInfoSchema().(infoschema.InfoSchema) + pt, tableFound := is.TableByID(tbl.ExchangePartitionInfo.ExchangePartitionTableID) + if !tableFound { + return errors.Errorf("exchange partition process table by id failed") + } + p, ok := pt.(table.PartitionedTable) + if !ok { + return errors.Errorf("exchange partition process assert table partition failed") + } + err := p.CheckForExchangePartition( + sctx, + pt.Meta().Partition, + row, + tbl.ExchangePartitionInfo.ExchangePartitionDefID, + tbl.ID, + ) + if err != nil { + return err + } + if variable.EnableCheckConstraint.Load() { + type CheckConstraintTable interface { + CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error + } + cc, ok := pt.(CheckConstraintTable) + if !ok { + return errors.Errorf("exchange partition process assert check constraint failed") + } + err := cc.CheckRowConstraint(sctx, row) + if err != nil { + // TODO: make error include ExchangePartition info. + return err + } + } + return nil +} diff --git a/executor/write_concurrent_test.go b/pkg/executor/write_concurrent_test.go similarity index 98% rename from executor/write_concurrent_test.go rename to pkg/executor/write_concurrent_test.go index 19d23cb846174..fea29908f69ee 100644 --- a/executor/write_concurrent_test.go +++ b/pkg/executor/write_concurrent_test.go @@ -19,7 +19,7 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" ) func TestBatchInsertWithOnDuplicate(t *testing.T) { diff --git a/pkg/expression/BUILD.bazel b/pkg/expression/BUILD.bazel new file mode 100644 index 0000000000000..bbb5bc6e9efad --- /dev/null +++ b/pkg/expression/BUILD.bazel @@ -0,0 +1,238 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "expression", + srcs = [ + "builtin.go", + "builtin_arithmetic.go", + "builtin_arithmetic_vec.go", + "builtin_cast.go", + "builtin_cast_vec.go", + "builtin_compare.go", + "builtin_compare_vec.go", + "builtin_compare_vec_generated.go", + "builtin_control.go", + "builtin_control_vec_generated.go", + "builtin_convert_charset.go", + "builtin_encryption.go", + "builtin_encryption_vec.go", + "builtin_func_param.go", + "builtin_grouping.go", + "builtin_ilike.go", + "builtin_ilike_vec.go", + "builtin_info.go", + "builtin_info_vec.go", + "builtin_json.go", + "builtin_json_vec.go", + "builtin_like.go", + "builtin_like_vec.go", + "builtin_math.go", + "builtin_math_vec.go", + "builtin_miscellaneous.go", + "builtin_miscellaneous_vec.go", + "builtin_op.go", + "builtin_op_vec.go", + "builtin_other.go", + "builtin_other_vec.go", + "builtin_other_vec_generated.go", + "builtin_regexp.go", + "builtin_regexp_util.go", + "builtin_string.go", + "builtin_string_vec.go", + "builtin_string_vec_generated.go", + "builtin_time.go", + "builtin_time_vec.go", + "builtin_time_vec_generated.go", + "builtin_vectorized.go", + "chunk_executor.go", + "collation.go", + "column.go", + "constant.go", + "constant_fold.go", + "constant_propagation.go", + "distsql_builtin.go", + "errors.go", + "evaluator.go", + "explain.go", + "expr_to_pb.go", + "expression.go", + "extension.go", + "function_traits.go", + "grouping_sets.go", + "helper.go", + "scalar_function.go", + "schema.go", + "simple_rewriter.go", + "util.go", + "vectorized.go", + ], + importpath = "github.com/pingcap/tidb/pkg/expression", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/extension", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/planner/funcdep", + "//pkg/privilege", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/disjointset", + "//pkg/util/encrypt", + "//pkg/util/generatedexpr", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/mock", + "//pkg/util/parser", + "//pkg/util/password-validation", + "//pkg/util/plancodec", + "//pkg/util/printer", + "//pkg/util/sem", + "//pkg/util/set", + "//pkg/util/size", + "//pkg/util/sqlexec", + "//pkg/util/stringutil", + "//pkg/util/vitess", + "//pkg/util/zeropool", + "@com_github_gogo_protobuf//proto", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_pkg_errors//:errors", + "@com_github_tikv_client_go_v2//oracle", + "@org_golang_x_tools//container/intsets", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "expression_test", + timeout = "moderate", + srcs = [ + "bench_test.go", + "builtin_arithmetic_test.go", + "builtin_arithmetic_vec_test.go", + "builtin_cast_bench_test.go", + "builtin_cast_test.go", + "builtin_cast_vec_test.go", + "builtin_compare_test.go", + "builtin_compare_vec_generated_test.go", + "builtin_compare_vec_test.go", + "builtin_control_test.go", + "builtin_control_vec_generated_test.go", + "builtin_encryption_test.go", + "builtin_encryption_vec_test.go", + "builtin_grouping_test.go", + "builtin_ilike_test.go", + "builtin_info_test.go", + "builtin_info_vec_test.go", + "builtin_json_test.go", + "builtin_json_vec_test.go", + "builtin_like_test.go", + "builtin_like_vec_test.go", + "builtin_math_test.go", + "builtin_math_vec_test.go", + "builtin_miscellaneous_test.go", + "builtin_miscellaneous_vec_test.go", + "builtin_op_test.go", + "builtin_op_vec_test.go", + "builtin_other_test.go", + "builtin_other_vec_generated_test.go", + "builtin_other_vec_test.go", + "builtin_regexp_test.go", + "builtin_regexp_vec_const_test.go", + "builtin_string_test.go", + "builtin_string_vec_generated_test.go", + "builtin_string_vec_test.go", + "builtin_test.go", + "builtin_time_test.go", + "builtin_time_vec_generated_test.go", + "builtin_time_vec_test.go", + "builtin_vectorized_test.go", + "collation_test.go", + "column_test.go", + "constant_test.go", + "distsql_builtin_test.go", + "evaluator_test.go", + "expr_to_pb_test.go", + "expression_test.go", + "function_traits_test.go", + "grouping_sets_test.go", + "helper_test.go", + "main_test.go", + "scalar_function_test.go", + "schema_test.go", + "typeinfer_test.go", + "util_test.go", + ], + data = glob(["testdata/**"]), + embed = [":expression"], + flaky = True, + race = "on", + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/mathutil", + "//pkg/util/mock", + "//pkg/util/printer", + "//pkg/util/timeutil", + "@com_github_gogo_protobuf//proto", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/expression/OWNERS b/pkg/expression/OWNERS similarity index 100% rename from expression/OWNERS rename to pkg/expression/OWNERS diff --git a/pkg/expression/aggregation/BUILD.bazel b/pkg/expression/aggregation/BUILD.bazel new file mode 100644 index 0000000000000..9cb6a3734f5d7 --- /dev/null +++ b/pkg/expression/aggregation/BUILD.bazel @@ -0,0 +1,77 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "aggregation", + srcs = [ + "agg_to_pb.go", + "aggregation.go", + "avg.go", + "base_func.go", + "bit_and.go", + "bit_or.go", + "bit_xor.go", + "concat.go", + "count.go", + "descriptor.go", + "explain.go", + "first_row.go", + "max_min.go", + "sum.go", + "util.go", + "window_func.go", + ], + importpath = "github.com/pingcap/tidb/pkg/expression/aggregation", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/util", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/mathutil", + "//pkg/util/mvmap", + "//pkg/util/size", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_tipb//go-tipb", + ], +) + +go_test( + name = "aggregation_test", + timeout = "short", + srcs = [ + "agg_to_pb_test.go", + "aggregation_test.go", + "base_func_test.go", + "bench_test.go", + "main_test.go", + "util_test.go", + ], + embed = [":aggregation"], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/expression/aggregation/agg_to_pb.go b/pkg/expression/aggregation/agg_to_pb.go similarity index 96% rename from expression/aggregation/agg_to_pb.go rename to pkg/expression/aggregation/agg_to_pb.go index fa74965292e6f..213dc72d5db65 100644 --- a/expression/aggregation/agg_to_pb.go +++ b/pkg/expression/aggregation/agg_to_pb.go @@ -19,13 +19,13 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/aggregation/agg_to_pb_test.go b/pkg/expression/aggregation/agg_to_pb_test.go similarity index 94% rename from expression/aggregation/agg_to_pb_test.go rename to pkg/expression/aggregation/agg_to_pb_test.go index 8c562ed846594..9f43d87f1f995 100644 --- a/expression/aggregation/agg_to_pb_test.go +++ b/pkg/expression/aggregation/agg_to_pb_test.go @@ -19,12 +19,12 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/aggregation/aggregation.go b/pkg/expression/aggregation/aggregation.go similarity index 95% rename from expression/aggregation/aggregation.go rename to pkg/expression/aggregation/aggregation.go index 5d22c587f3c34..d26698abdb9a8 100644 --- a/expression/aggregation/aggregation.go +++ b/pkg/expression/aggregation/aggregation.go @@ -19,14 +19,14 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/aggregation/aggregation_test.go b/pkg/expression/aggregation/aggregation_test.go similarity index 98% rename from expression/aggregation/aggregation_test.go rename to pkg/expression/aggregation/aggregation_test.go index 9d32685af5a64..a9a29b1c74c38 100644 --- a/expression/aggregation/aggregation_test.go +++ b/pkg/expression/aggregation/aggregation_test.go @@ -18,14 +18,14 @@ import ( "math" "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/aggregation/avg.go b/pkg/expression/aggregation/avg.go similarity index 90% rename from expression/aggregation/avg.go rename to pkg/expression/aggregation/avg.go index 7bc839ae3fb0f..e15f0ce0f7be6 100644 --- a/expression/aggregation/avg.go +++ b/pkg/expression/aggregation/avg.go @@ -15,12 +15,12 @@ package aggregation import ( - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) type avgFunction struct { diff --git a/expression/aggregation/base_func.go b/pkg/expression/aggregation/base_func.go similarity index 97% rename from expression/aggregation/base_func.go rename to pkg/expression/aggregation/base_func.go index 989ff1588aaca..3de4f3299d976 100644 --- a/expression/aggregation/base_func.go +++ b/pkg/expression/aggregation/base_func.go @@ -21,15 +21,15 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/size" ) // baseFuncDesc describes an function signature, only used in planner. diff --git a/expression/aggregation/base_func_test.go b/pkg/expression/aggregation/base_func_test.go similarity index 90% rename from expression/aggregation/base_func_test.go rename to pkg/expression/aggregation/base_func_test.go index 0f4c125153f87..5e3f17840c7a0 100644 --- a/expression/aggregation/base_func_test.go +++ b/pkg/expression/aggregation/base_func_test.go @@ -17,11 +17,11 @@ package aggregation import ( "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/expression/aggregation/bench_test.go b/pkg/expression/aggregation/bench_test.go new file mode 100644 index 0000000000000..eb1fb72c67b9c --- /dev/null +++ b/pkg/expression/aggregation/bench_test.go @@ -0,0 +1,99 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package aggregation + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" +) + +func BenchmarkCreateContext(b *testing.B) { + col := &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + ctx := mock.NewContext() + desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, false) + if err != nil { + b.Fatal(err) + } + fun := desc.GetAggFunc(ctx) + b.StartTimer() + for i := 0; i < b.N; i++ { + fun.CreateContext(ctx.GetSessionVars().StmtCtx) + } + b.ReportAllocs() +} + +func BenchmarkResetContext(b *testing.B) { + col := &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + ctx := mock.NewContext() + desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, false) + if err != nil { + b.Fatal(err) + } + fun := desc.GetAggFunc(ctx) + evalCtx := fun.CreateContext(ctx.GetSessionVars().StmtCtx) + b.StartTimer() + for i := 0; i < b.N; i++ { + fun.ResetContext(ctx.GetSessionVars().StmtCtx, evalCtx) + } + b.ReportAllocs() +} + +func BenchmarkCreateDistinctContext(b *testing.B) { + col := &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + ctx := mock.NewContext() + desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, true) + if err != nil { + b.Fatal(err) + } + fun := desc.GetAggFunc(ctx) + b.StartTimer() + for i := 0; i < b.N; i++ { + fun.CreateContext(ctx.GetSessionVars().StmtCtx) + } + b.ReportAllocs() +} + +func BenchmarkResetDistinctContext(b *testing.B) { + col := &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + ctx := mock.NewContext() + desc, err := NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{col}, true) + if err != nil { + b.Fatal(err) + } + fun := desc.GetAggFunc(ctx) + evalCtx := fun.CreateContext(ctx.GetSessionVars().StmtCtx) + b.StartTimer() + for i := 0; i < b.N; i++ { + fun.ResetContext(ctx.GetSessionVars().StmtCtx, evalCtx) + } + b.ReportAllocs() +} diff --git a/expression/aggregation/bit_and.go b/pkg/expression/aggregation/bit_and.go similarity index 93% rename from expression/aggregation/bit_and.go rename to pkg/expression/aggregation/bit_and.go index 4ba3645e3569f..2d975f6563fcc 100644 --- a/expression/aggregation/bit_and.go +++ b/pkg/expression/aggregation/bit_and.go @@ -17,9 +17,9 @@ package aggregation import ( "math" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type bitAndFunction struct { diff --git a/expression/aggregation/bit_or.go b/pkg/expression/aggregation/bit_or.go similarity index 93% rename from expression/aggregation/bit_or.go rename to pkg/expression/aggregation/bit_or.go index fe34d3ce03bfc..409085855919c 100644 --- a/expression/aggregation/bit_or.go +++ b/pkg/expression/aggregation/bit_or.go @@ -15,9 +15,9 @@ package aggregation import ( - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type bitOrFunction struct { diff --git a/expression/aggregation/bit_xor.go b/pkg/expression/aggregation/bit_xor.go similarity index 93% rename from expression/aggregation/bit_xor.go rename to pkg/expression/aggregation/bit_xor.go index 8ca72c3afc8f3..c3c97d5bd1712 100644 --- a/expression/aggregation/bit_xor.go +++ b/pkg/expression/aggregation/bit_xor.go @@ -15,9 +15,9 @@ package aggregation import ( - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type bitXorFunction struct { diff --git a/expression/aggregation/concat.go b/pkg/expression/aggregation/concat.go similarity index 94% rename from expression/aggregation/concat.go rename to pkg/expression/aggregation/concat.go index f59a151505feb..73deacaf13570 100644 --- a/expression/aggregation/concat.go +++ b/pkg/expression/aggregation/concat.go @@ -19,11 +19,11 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) type concatFunction struct { diff --git a/expression/aggregation/count.go b/pkg/expression/aggregation/count.go similarity index 93% rename from expression/aggregation/count.go rename to pkg/expression/aggregation/count.go index f867694735b65..fd7316333de52 100644 --- a/expression/aggregation/count.go +++ b/pkg/expression/aggregation/count.go @@ -15,9 +15,9 @@ package aggregation import ( - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type countFunction struct { diff --git a/expression/aggregation/descriptor.go b/pkg/expression/aggregation/descriptor.go similarity index 97% rename from expression/aggregation/descriptor.go rename to pkg/expression/aggregation/descriptor.go index 892a97b44f6fb..2cd95a8f6f62c 100644 --- a/expression/aggregation/descriptor.go +++ b/pkg/expression/aggregation/descriptor.go @@ -22,15 +22,15 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/size" ) // AggFuncDesc describes an aggregation function signature, only used in planner. diff --git a/pkg/expression/aggregation/explain.go b/pkg/expression/aggregation/explain.go new file mode 100644 index 0000000000000..03328b18a06fa --- /dev/null +++ b/pkg/expression/aggregation/explain.go @@ -0,0 +1,67 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package aggregation + +import ( + "bytes" + "fmt" + + "github.com/pingcap/tidb/pkg/parser/ast" +) + +// ExplainAggFunc generates explain information for a aggregation function. +func ExplainAggFunc(agg *AggFuncDesc, normalized bool) string { + var buffer bytes.Buffer + fmt.Fprintf(&buffer, "%s(", agg.Name) + if agg.HasDistinct { + buffer.WriteString("distinct ") + } + for i, arg := range agg.Args { + if agg.Name == ast.AggFuncGroupConcat && i == len(agg.Args)-1 { + if len(agg.OrderByItems) > 0 { + buffer.WriteString(" order by ") + for i, item := range agg.OrderByItems { + if item.Desc { + if normalized { + fmt.Fprintf(&buffer, "%s desc", item.Expr.ExplainNormalizedInfo()) + } else { + fmt.Fprintf(&buffer, "%s desc", item.Expr.ExplainInfo()) + } + } else { + if normalized { + fmt.Fprintf(&buffer, "%s", item.Expr.ExplainNormalizedInfo()) + } else { + fmt.Fprintf(&buffer, "%s", item.Expr.ExplainInfo()) + } + } + + if i+1 < len(agg.OrderByItems) { + buffer.WriteString(", ") + } + } + } + buffer.WriteString(" separator ") + } else if i != 0 { + buffer.WriteString(", ") + } + if normalized { + buffer.WriteString(arg.ExplainNormalizedInfo()) + } else { + buffer.WriteString(arg.ExplainInfo()) + } + } + buffer.WriteString(")") + return buffer.String() +} diff --git a/expression/aggregation/first_row.go b/pkg/expression/aggregation/first_row.go similarity index 92% rename from expression/aggregation/first_row.go rename to pkg/expression/aggregation/first_row.go index e299859560a77..a30a534e3ef75 100644 --- a/expression/aggregation/first_row.go +++ b/pkg/expression/aggregation/first_row.go @@ -16,9 +16,9 @@ package aggregation import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type firstRowFunction struct { diff --git a/pkg/expression/aggregation/main_test.go b/pkg/expression/aggregation/main_test.go new file mode 100644 index 0000000000000..19582e7339882 --- /dev/null +++ b/pkg/expression/aggregation/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package aggregation + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/expression/aggregation/max_min.go b/pkg/expression/aggregation/max_min.go similarity index 90% rename from expression/aggregation/max_min.go rename to pkg/expression/aggregation/max_min.go index 0fdb319faa23c..2dd7696cdf9e7 100644 --- a/expression/aggregation/max_min.go +++ b/pkg/expression/aggregation/max_min.go @@ -15,10 +15,10 @@ package aggregation import ( - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" ) type maxMinFunction struct { diff --git a/expression/aggregation/sum.go b/pkg/expression/aggregation/sum.go similarity index 90% rename from expression/aggregation/sum.go rename to pkg/expression/aggregation/sum.go index 489c5ad2a18be..0c1dd13ae192a 100644 --- a/expression/aggregation/sum.go +++ b/pkg/expression/aggregation/sum.go @@ -15,9 +15,9 @@ package aggregation import ( - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) type sumFunction struct { diff --git a/pkg/expression/aggregation/util.go b/pkg/expression/aggregation/util.go new file mode 100644 index 0000000000000..1842d385e346d --- /dev/null +++ b/pkg/expression/aggregation/util.go @@ -0,0 +1,95 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package aggregation + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mvmap" +) + +// distinctChecker stores existing keys and checks if given data is distinct. +type distinctChecker struct { + existingKeys *mvmap.MVMap + key []byte + vals [][]byte + sc *stmtctx.StatementContext +} + +// createDistinctChecker creates a new distinct checker. +func createDistinctChecker(sc *stmtctx.StatementContext) *distinctChecker { + return &distinctChecker{ + existingKeys: mvmap.NewMVMap(), + sc: sc, + } +} + +// Check checks if values is distinct. +func (d *distinctChecker) Check(values []types.Datum) (bool, error) { + d.key = d.key[:0] + var err error + d.key, err = codec.EncodeValue(d.sc, d.key, values...) + if err != nil { + return false, err + } + d.vals = d.existingKeys.Get(d.key, d.vals[:0]) + if len(d.vals) > 0 { + return false, nil + } + d.existingKeys.Put(d.key, []byte{}) + return true, nil +} + +// calculateSum adds v to sum. +func calculateSum(sc *stmtctx.StatementContext, sum, v types.Datum) (data types.Datum, err error) { + // for avg and sum calculation + // avg and sum use decimal for integer and decimal type, use float for others + // see https://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html + + switch v.Kind() { + case types.KindNull: + case types.KindInt64, types.KindUint64: + var d *types.MyDecimal + d, err = v.ToDecimal(sc) + if err == nil { + data = types.NewDecimalDatum(d) + } + case types.KindMysqlDecimal: + v.Copy(&data) + default: + var f float64 + f, err = v.ToFloat64(sc) + if err == nil { + data = types.NewFloat64Datum(f) + } + } + + if err != nil { + return data, err + } + if data.IsNull() { + return sum, nil + } + switch sum.Kind() { + case types.KindNull: + return data, nil + case types.KindFloat64, types.KindMysqlDecimal: + return types.ComputePlus(sum, data) + default: + return data, errors.Errorf("invalid value %v for aggregate", sum.Kind()) + } +} diff --git a/pkg/expression/aggregation/util_test.go b/pkg/expression/aggregation/util_test.go new file mode 100644 index 0000000000000..699506b372971 --- /dev/null +++ b/pkg/expression/aggregation/util_test.go @@ -0,0 +1,45 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package aggregation + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestDistinct(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + dc := createDistinctChecker(sc) + testCases := []struct { + vals []interface{} + expect bool + }{ + {[]interface{}{1, 1}, true}, + {[]interface{}{1, 1}, false}, + {[]interface{}{1, 2}, true}, + {[]interface{}{1, 2}, false}, + {[]interface{}{1, nil}, true}, + {[]interface{}{1, nil}, false}, + } + for _, tc := range testCases { + d, err := dc.Check(types.MakeDatums(tc.vals...)) + require.NoError(t, err) + require.Equal(t, tc.expect, d) + } +} diff --git a/expression/aggregation/window_func.go b/pkg/expression/aggregation/window_func.go similarity index 96% rename from expression/aggregation/window_func.go rename to pkg/expression/aggregation/window_func.go index 4f82f03f62c31..1ce06b72d6d2a 100644 --- a/expression/aggregation/window_func.go +++ b/pkg/expression/aggregation/window_func.go @@ -17,11 +17,11 @@ package aggregation import ( "strings" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/expression/bench_test.go b/pkg/expression/bench_test.go new file mode 100644 index 0000000000000..3f179f9d21f7b --- /dev/null +++ b/pkg/expression/bench_test.go @@ -0,0 +1,2143 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package expression + +// This file contains benchmarks of our expression evaluation. + +import ( + "flag" + "fmt" + "math" + "math/rand" + "net" + "reflect" + "strings" + "sync" + "testing" + "time" + + "github.com/google/uuid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +type benchHelper struct { + ctx sessionctx.Context + exprs []Expression + + inputTypes []*types.FieldType + outputTypes []*types.FieldType + inputChunk *chunk.Chunk + outputChunk *chunk.Chunk +} + +func (h *benchHelper) init() { + numRows := 4 * 1024 + + h.ctx = mock.NewContext() + h.ctx.GetSessionVars().StmtCtx.SetTimeZone(time.Local) + h.ctx.GetSessionVars().InitChunkSize = 32 + h.ctx.GetSessionVars().MaxChunkSize = numRows + + h.inputTypes = make([]*types.FieldType, 0, 10) + ftb := types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeLonglong).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxIntWidth).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) + h.inputTypes = append(h.inputTypes, ftb.BuildP()) + + ftb = types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeDouble).SetFlag(mysql.BinaryFlag).SetFlen(mysql.MaxRealWidth).SetDecimal(types.UnspecifiedLength).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) + h.inputTypes = append(h.inputTypes, ftb.BuildP()) + + ftb = types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeNewDecimal).SetFlag(mysql.BinaryFlag).SetFlen(11).SetCharset(charset.CharsetBin).SetCollate(charset.CollationBin) + h.inputTypes = append(h.inputTypes, ftb.BuildP()) + + // Use 20 string columns to show the cache performance. + for i := 0; i < 20; i++ { + ftb = types.NewFieldTypeBuilder() + ftb.SetType(mysql.TypeVarString).SetDecimal(types.UnspecifiedLength).SetCharset(charset.CharsetUTF8).SetCollate(charset.CollationUTF8) + h.inputTypes = append(h.inputTypes, ftb.BuildP()) + } + + h.inputChunk = chunk.NewChunkWithCapacity(h.inputTypes, numRows) + for rowIdx := 0; rowIdx < numRows; rowIdx++ { + h.inputChunk.AppendInt64(0, 4) + h.inputChunk.AppendFloat64(1, 2.019) + h.inputChunk.AppendMyDecimal(2, types.NewDecFromFloatForTest(5.9101)) + for i := 0; i < 20; i++ { + h.inputChunk.AppendString(3+i, `abcdefughasfjsaljal1321798273528791!&(*#&@&^%&%^&!)sadfashqwer`) + } + } + + cols := make([]*Column, 0, len(h.inputTypes)) + for i := 0; i < len(h.inputTypes); i++ { + cols = append(cols, &Column{ + UniqueID: int64(i), + RetType: h.inputTypes[i], + Index: i, + }) + } + + h.exprs = make([]Expression, 0, 10) + if expr, err := NewFunction(h.ctx, ast.Substr, h.inputTypes[3], []Expression{cols[3], cols[2]}...); err != nil { + panic("create SUBSTR function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.Plus, h.inputTypes[0], []Expression{cols[1], cols[2]}...); err != nil { + panic("create PLUS function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[11], cols[8]}...); err != nil { + panic("create GT function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[19], cols[10]}...); err != nil { + panic("create GT function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[17], cols[4]}...); err != nil { + panic("create GT function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.GT, h.inputTypes[2], []Expression{cols[18], cols[5]}...); err != nil { + panic("create GT function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.LE, h.inputTypes[2], []Expression{cols[19], cols[4]}...); err != nil { + panic("create LE function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + + if expr, err := NewFunction(h.ctx, ast.EQ, h.inputTypes[2], []Expression{cols[20], cols[3]}...); err != nil { + panic("create EQ function failed.") + } else { + h.exprs = append(h.exprs, expr) + } + h.exprs = append(h.exprs, cols[2]) + h.exprs = append(h.exprs, cols[2]) + + h.outputTypes = make([]*types.FieldType, 0, len(h.exprs)) + for i := 0; i < len(h.exprs); i++ { + h.outputTypes = append(h.outputTypes, h.exprs[i].GetType()) + } + + h.outputChunk = chunk.NewChunkWithCapacity(h.outputTypes, numRows) +} + +func BenchmarkVectorizedExecute(b *testing.B) { + h := benchHelper{} + h.init() + inputIter := chunk.NewIterator4Chunk(h.inputChunk) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + h.outputChunk.Reset() + if err := VectorizedExecute(h.ctx, h.exprs, inputIter, h.outputChunk); err != nil { + panic("errors happened during \"VectorizedExecute\"") + } + } +} + +func BenchmarkScalarFunctionClone(b *testing.B) { + col := &Column{RetType: types.NewFieldType(mysql.TypeLonglong)} + con1 := NewOne() + con2 := NewZero() + add := NewFunctionInternal(mock.NewContext(), ast.Plus, types.NewFieldType(mysql.TypeLonglong), col, con1) + sub := NewFunctionInternal(mock.NewContext(), ast.Plus, types.NewFieldType(mysql.TypeLonglong), add, con2) + b.ResetTimer() + for i := 0; i < b.N; i++ { + sub.Clone() + } + b.ReportAllocs() +} + +func getRandomTime(r *rand.Rand) types.CoreTime { + return types.FromDate(r.Intn(2200), r.Intn(10)+1, r.Intn(20)+1, + r.Intn(12), r.Intn(60), r.Intn(60), r.Intn(1000000)) +} + +// dataGenerator is used to generate data for test. +type dataGenerator interface { + gen() interface{} +} + +type defaultRandGen struct { + *rand.Rand +} + +type lockedSource struct { + lk sync.Mutex + src rand.Source +} + +func (r *lockedSource) Int63() (n int64) { + r.lk.Lock() + n = r.src.Int63() + r.lk.Unlock() + return +} + +func (r *lockedSource) Seed(seed int64) { + r.lk.Lock() + r.src.Seed(seed) + r.lk.Unlock() +} + +func newDefaultRandGen() *defaultRandGen { + return &defaultRandGen{rand.New(&lockedSource{src: rand.NewSource(int64(rand.Uint64()))})} +} + +type defaultGener struct { + nullRation float64 + eType types.EvalType + randGen *defaultRandGen +} + +func newDefaultGener(nullRation float64, eType types.EvalType) *defaultGener { + return &defaultGener{ + nullRation: nullRation, + eType: eType, + randGen: newDefaultRandGen(), + } +} + +func (g *defaultGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + switch g.eType { + case types.ETInt: + if g.randGen.Float64() < 0.5 { + return -g.randGen.Int63() + } + return g.randGen.Int63() + case types.ETReal: + if g.randGen.Float64() < 0.5 { + return -g.randGen.Float64() * 1000000 + } + return g.randGen.Float64() * 1000000 + case types.ETDecimal: + d := new(types.MyDecimal) + var f float64 + if g.randGen.Float64() < 0.5 { + f = g.randGen.Float64() * 100000 + } else { + f = -g.randGen.Float64() * 100000 + } + if err := d.FromFloat64(f); err != nil { + panic(err) + } + return d + case types.ETDatetime, types.ETTimestamp: + gt := getRandomTime(g.randGen.Rand) + t := types.NewTime(gt, convertETType(g.eType), 0) + // TiDB has DST time problem, and it causes ErrWrongValue. + // We should ignore ambiguous Time. See https://timezonedb.com/time-zones/Asia/Shanghai. + for _, err := t.GoTime(time.Local); err != nil; { + gt = getRandomTime(g.randGen.Rand) + t = types.NewTime(gt, convertETType(g.eType), 0) + _, err = t.GoTime(time.Local) + } + return t + case types.ETDuration: + d := types.Duration{ + // use rand.Int32() to make it not overflow when AddDuration + Duration: time.Duration(g.randGen.Int31()), + } + return d + case types.ETJson: + j := new(types.BinaryJSON) + if err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"key":%v}`, g.randGen.Int()))); err != nil { + panic(err) + } + return *j + case types.ETString: + return randString(g.randGen.Rand) + } + return nil +} + +// charInt64Gener is used to generate int which is equal to char's ascii +type charInt64Gener struct{} + +func (g *charInt64Gener) gen() interface{} { + nanosecond := time.Now().Nanosecond() + nanosecond = nanosecond % 1024 + return int64(nanosecond) +} + +// selectStringGener select one string randomly from the candidates array +type selectStringGener struct { + candidates []string + randGen *defaultRandGen +} + +func newSelectStringGener(candidates []string) *selectStringGener { + return &selectStringGener{candidates, newDefaultRandGen()} +} + +func (g *selectStringGener) gen() interface{} { + if len(g.candidates) == 0 { + return nil + } + return g.candidates[g.randGen.Intn(len(g.candidates))] +} + +// selectRealGener select one real number randomly from the candidates array +type selectRealGener struct { + candidates []float64 + randGen *defaultRandGen +} + +func newSelectRealGener(candidates []float64) *selectRealGener { + return &selectRealGener{candidates, newDefaultRandGen()} +} + +func (g *selectRealGener) gen() interface{} { + if len(g.candidates) == 0 { + return nil + } + return g.candidates[g.randGen.Intn(len(g.candidates))] +} + +type constJSONGener struct { + jsonStr string +} + +func (g *constJSONGener) gen() interface{} { + j := new(types.BinaryJSON) + if err := j.UnmarshalJSON([]byte(g.jsonStr)); err != nil { + panic(err) + } + return *j +} + +type decimalJSONGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func newDecimalJSONGener(nullRation float64) *decimalJSONGener { + return &decimalJSONGener{nullRation, newDefaultRandGen()} +} + +func (g *decimalJSONGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + + var f float64 + if g.randGen.Float64() < 0.5 { + f = g.randGen.Float64() * 100000 + } else { + f = -g.randGen.Float64() * 100000 + } + if err := (&types.MyDecimal{}).FromFloat64(f); err != nil { + panic(err) + } + return types.CreateBinaryJSON(f) +} + +type jsonStringGener struct { + randGen *defaultRandGen +} + +func newJSONStringGener() *jsonStringGener { + return &jsonStringGener{newDefaultRandGen()} +} + +func (g *jsonStringGener) gen() interface{} { + j := new(types.BinaryJSON) + if err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"key":%v}`, g.randGen.Int()))); err != nil { + panic(err) + } + return j.String() +} + +type decimalStringGener struct { + randGen *defaultRandGen +} + +func newDecimalStringGener() *decimalStringGener { + return &decimalStringGener{newDefaultRandGen()} +} + +func (g *decimalStringGener) gen() interface{} { + tempDecimal := new(types.MyDecimal) + if err := tempDecimal.FromFloat64(g.randGen.Float64()); err != nil { + panic(err) + } + return tempDecimal.String() +} + +type realStringGener struct { + randGen *defaultRandGen +} + +func newRealStringGener() *realStringGener { + return &realStringGener{newDefaultRandGen()} +} + +func (g *realStringGener) gen() interface{} { + return fmt.Sprintf("%f", g.randGen.Float64()) +} + +type jsonTimeGener struct { + randGen *defaultRandGen +} + +func newJSONTimeGener() *jsonTimeGener { + return &jsonTimeGener{newDefaultRandGen()} +} + +func (g *jsonTimeGener) gen() interface{} { + tm := types.NewTime(getRandomTime(g.randGen.Rand), mysql.TypeDatetime, types.DefaultFsp) + return types.CreateBinaryJSON(tm) +} + +type rangeDurationGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func newRangeDurationGener(nullRation float64) *rangeDurationGener { + return &rangeDurationGener{nullRation, newDefaultRandGen()} +} + +func (g *rangeDurationGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + tm := (mathutil.Abs(g.randGen.Int63n(12))*3600 + mathutil.Abs(g.randGen.Int63n(60))*60 + mathutil.Abs(g.randGen.Int63n(60))) * 1000 + tu := (tm + mathutil.Abs(g.randGen.Int63n(1000))) * 1000 + return types.Duration{ + Duration: time.Duration(tu * 1000)} +} + +type timeFormatGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func newTimeFormatGener(nullRation float64) *timeFormatGener { + return &timeFormatGener{nullRation, newDefaultRandGen()} +} + +func (g *timeFormatGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + switch g.randGen.Uint32() % 4 { + case 0: + return "%H %i %S" + case 1: + return "%l %i %s" + case 2: + return "%p %i %s" + case 3: + return "%I %i %S %f" + case 4: + return "%T" + default: + return nil + } +} + +// rangeRealGener is used to generate float64 items in [begin, end]. +type rangeRealGener struct { + begin float64 + end float64 + + nullRation float64 + randGen *defaultRandGen +} + +func newRangeRealGener(begin, end, nullRation float64) *rangeRealGener { + return &rangeRealGener{begin, end, nullRation, newDefaultRandGen()} +} + +func (g *rangeRealGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + if g.end < g.begin { + g.begin = -100 + g.end = 100 + } + return g.randGen.Float64()*(g.end-g.begin) + g.begin +} + +// rangeDecimalGener is used to generate decimal items in [begin, end]. +type rangeDecimalGener struct { + begin float64 + end float64 + + nullRation float64 + randGen *defaultRandGen +} + +func newRangeDecimalGener(begin, end, nullRation float64) *rangeDecimalGener { + return &rangeDecimalGener{begin, end, nullRation, newDefaultRandGen()} +} + +func (g *rangeDecimalGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + if g.end < g.begin { + g.begin = -100000 + g.end = 100000 + } + d := new(types.MyDecimal) + f := g.randGen.Float64()*(g.end-g.begin) + g.begin + if err := d.FromFloat64(f); err != nil { + panic(err) + } + return d +} + +// rangeInt64Gener is used to generate int64 items in [begin, end). +type rangeInt64Gener struct { + begin int + end int + randGen *defaultRandGen +} + +func newRangeInt64Gener(begin, end int) *rangeInt64Gener { + return &rangeInt64Gener{begin, end, newDefaultRandGen()} +} + +func (rig *rangeInt64Gener) gen() interface{} { + return int64(rig.randGen.Intn(rig.end-rig.begin) + rig.begin) +} + +// numStrGener is used to generate number strings. +type numStrGener struct { + rangeInt64Gener +} + +func (g *numStrGener) gen() interface{} { + return fmt.Sprintf("%v", g.rangeInt64Gener.gen()) +} + +// ipv6StrGener is used to generate ipv6 strings. +type ipv6StrGener struct { + randGen *defaultRandGen +} + +func (g *ipv6StrGener) gen() interface{} { + var ip net.IP = make([]byte, net.IPv6len) + for i := range ip { + ip[i] = uint8(g.randGen.Intn(256)) + } + return ip.String() +} + +// ipv4StrGener is used to generate ipv4 strings. For example 111.111.111.111 +type ipv4StrGener struct { + randGen *defaultRandGen +} + +func (g *ipv4StrGener) gen() interface{} { + var ip net.IP = make([]byte, net.IPv4len) + for i := range ip { + ip[i] = uint8(g.randGen.Intn(256)) + } + return ip.String() +} + +// ipv6ByteGener is used to generate ipv6 address in 16 bytes string. +type ipv6ByteGener struct { + randGen *defaultRandGen +} + +func (g *ipv6ByteGener) gen() interface{} { + var ip = make([]byte, net.IPv6len) + for i := range ip { + ip[i] = uint8(g.randGen.Intn(256)) + } + return string(ip[:net.IPv6len]) +} + +// ipv4ByteGener is used to generate ipv4 address in 4 bytes string. +type ipv4ByteGener struct { + randGen *defaultRandGen +} + +func (g *ipv4ByteGener) gen() interface{} { + var ip = make([]byte, net.IPv4len) + for i := range ip { + ip[i] = uint8(g.randGen.Intn(256)) + } + return string(ip[:net.IPv4len]) +} + +// ipv4Compat is used to generate ipv4 compatible ipv6 strings +type ipv4CompatByteGener struct { + randGen *defaultRandGen +} + +func (g *ipv4CompatByteGener) gen() interface{} { + var ip = make([]byte, net.IPv6len) + for i := range ip { + if i < 12 { + ip[i] = 0 + } else { + ip[i] = uint8(g.randGen.Intn(256)) + } + } + return string(ip[:net.IPv6len]) +} + +// ipv4MappedByteGener is used to generate ipv4-mapped ipv6 bytes. +type ipv4MappedByteGener struct { + randGen *defaultRandGen +} + +func (g *ipv4MappedByteGener) gen() interface{} { + var ip = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 0, 0, 0} + for i := 12; i < 16; i++ { + ip[i] = uint8(g.randGen.Intn(256)) // reset the last 4 bytes + } + return string(ip[:net.IPv6len]) +} + +// uuidStrGener is used to generate uuid strings. +type uuidStrGener struct { + randGen *defaultRandGen +} + +func (g *uuidStrGener) gen() interface{} { + u, _ := uuid.NewUUID() + return u.String() +} + +// uuidBinGener is used to generate uuid binarys. +type uuidBinGener struct { + randGen *defaultRandGen +} + +func (g *uuidBinGener) gen() interface{} { + u, _ := uuid.NewUUID() + bin, _ := u.MarshalBinary() + return string(bin) +} + +// randLenStrGener is used to generate strings whose lengths are in [lenBegin, lenEnd). +type randLenStrGener struct { + lenBegin int + lenEnd int + randGen *defaultRandGen +} + +func newRandLenStrGener(lenBegin, lenEnd int) *randLenStrGener { + return &randLenStrGener{lenBegin, lenEnd, newDefaultRandGen()} +} + +func (g *randLenStrGener) gen() interface{} { + n := g.randGen.Intn(g.lenEnd-g.lenBegin) + g.lenBegin + buf := make([]byte, n) + for i := range buf { + x := g.randGen.Intn(62) + if x < 10 { + buf[i] = byte('0' + x) + } else if x-10 < 26 { + buf[i] = byte('a' + x - 10) + } else { + buf[i] = byte('A' + x - 10 - 26) + } + } + return string(buf) +} + +type randHexStrGener struct { + lenBegin int + lenEnd int + randGen *defaultRandGen +} + +func newRandHexStrGener(lenBegin, lenEnd int) *randHexStrGener { + return &randHexStrGener{lenBegin, lenEnd, newDefaultRandGen()} +} + +func (g *randHexStrGener) gen() interface{} { + n := g.randGen.Intn(g.lenEnd-g.lenBegin) + g.lenBegin + buf := make([]byte, n) + for i := range buf { + x := g.randGen.Intn(16) + if x < 10 { + buf[i] = byte('0' + x) + } else { + if x%2 == 0 { + buf[i] = byte('a' + x - 10) + } else { + buf[i] = byte('A' + x - 10) + } + } + } + return string(buf) +} + +// dateGener is used to generate a date +type dateGener struct { + randGen *defaultRandGen +} + +func (g dateGener) gen() interface{} { + year := 1970 + g.randGen.Intn(100) + month := g.randGen.Intn(10) + 1 + day := g.randGen.Intn(20) + 1 + gt := types.FromDate(year, month, day, 0, 0, 0, 0) + d := types.NewTime(gt, mysql.TypeDate, types.DefaultFsp) + return d +} + +// dateTimeGener is used to generate a dataTime +type dateTimeGener struct { + Fsp int + Year int + Month int + Day int + randGen *defaultRandGen +} + +func (g *dateTimeGener) gen() interface{} { + if g.Year == 0 { + g.Year = 1970 + g.randGen.Intn(100) + } + if g.Month == 0 { + g.Month = g.randGen.Intn(10) + 1 + } + if g.Day == 0 { + g.Day = g.randGen.Intn(20) + 1 + } + var gt types.CoreTime + if g.Fsp > 0 && g.Fsp <= 6 { + gt = types.FromDate(g.Year, g.Month, g.Day, g.randGen.Intn(12), g.randGen.Intn(60), g.randGen.Intn(60), g.randGen.Intn(1000000)) + } else { + gt = types.FromDate(g.Year, g.Month, g.Day, g.randGen.Intn(12), g.randGen.Intn(60), g.randGen.Intn(60), 0) + } + t := types.NewTime(gt, mysql.TypeDatetime, types.DefaultFsp) + return t +} + +// dateTimeStrGener is used to generate strings which are dateTime format. +// Fsp must be -1 to 9 otherwise will be ignored. -1 will generate a 0 to 9 random length fsp part, otherwise the fsp part will be of fixed length. +// Fsp more than 6 is to test robustness of fsp part parsing. +type dateTimeStrGener struct { + Fsp int + Year int + Month int + Day int + randGen *defaultRandGen +} + +func (g *dateTimeStrGener) gen() interface{} { + if g.Year == 0 { + g.Year = 1970 + g.randGen.Intn(100) + } + if g.Month == 0 { + g.Month = g.randGen.Intn(10) + 1 + } + if g.Day == 0 { + g.Day = g.randGen.Intn(20) + 1 + } + if g.Fsp == -1 { + g.Fsp = g.randGen.Intn(10) + } + hour := g.randGen.Intn(12) + minute := g.randGen.Intn(60) + second := g.randGen.Intn(60) + dataTimeStr := fmt.Sprintf("%d-%d-%d %d:%d:%d", + g.Year, g.Month, g.Day, hour, minute, second) + if g.Fsp > 0 && g.Fsp <= 9 { + microFmt := fmt.Sprintf(".%%0%dd", g.Fsp) + return dataTimeStr + fmt.Sprintf(microFmt, g.randGen.Int()%int(math.Pow10(g.Fsp))) + } + + return dataTimeStr +} + +// dateStrGener is used to generate strings which are date format +type dateStrGener struct { + Year int + Month int + Day int + NullRation float64 + randGen *defaultRandGen +} + +func (g *dateStrGener) gen() interface{} { + if g.NullRation > 1e-6 && g.randGen.Float64() < g.NullRation { + return nil + } + + if g.Year == 0 { + g.Year = 1970 + g.randGen.Intn(100) + } + if g.Month == 0 { + g.Month = g.randGen.Intn(10) + } + if g.Day == 0 { + g.Day = g.randGen.Intn(20) + } + + return fmt.Sprintf("%d-%d-%d", g.Year, g.Month, g.Day) +} + +// dateOrDatetimeStrGener is used to generate strings which are date or datetime format. +type dateOrDatetimeStrGener struct { + dateRatio float64 + dateStrGener + dateTimeStrGener +} + +func (g dateOrDatetimeStrGener) gen() interface{} { + if g.dateRatio > 1e-6 && g.dateStrGener.randGen.Float64() < g.dateRatio { + return g.dateStrGener.gen() + } + + return g.dateTimeStrGener.gen() +} + +// timeStrGener is used to generate strings which are time format +type timeStrGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func (g *timeStrGener) gen() interface{} { + if g.nullRation > 1e-6 && g.randGen.Float64() < g.nullRation { + return nil + } + hour := g.randGen.Intn(12) + minute := g.randGen.Intn(60) + second := g.randGen.Intn(60) + + return fmt.Sprintf("%d:%d:%d", hour, minute, second) +} + +// dateIntGener is used to generate int values which are date format. +type dateIntGener struct { + dateGener +} + +func (g dateIntGener) gen() interface{} { + t := g.dateGener.gen().(types.Time) + num, err := t.ToNumber().ToInt() + if err != nil { + panic(err) + } + return num +} + +// dateTimeIntGener is used to generate int values which are dateTime format. +type dateTimeIntGener struct { + dateTimeGener +} + +func (g dateTimeIntGener) gen() interface{} { + t := g.dateTimeGener.gen().(types.Time) + num, err := t.ToNumber().ToInt() + if err != nil { + panic(err) + } + return num +} + +// dateOrDatetimeIntGener is used to generate int values which are date or datetime format. +type dateOrDatetimeIntGener struct { + dateRatio float64 + dateIntGener + dateTimeIntGener +} + +func (g dateOrDatetimeIntGener) gen() interface{} { + if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { + return g.dateIntGener.gen() + } + + return g.dateTimeIntGener.gen() +} + +// dateRealGener is used to generate floating point values which are date format. +// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date. +type dateRealGener struct { + fspRatio float64 + dateGener +} + +func (g dateRealGener) gen() interface{} { + t := g.dateGener.gen().(types.Time) + num, err := t.ToNumber().ToFloat64() + if err != nil { + panic(err) + } + + if g.randGen.Float64() >= g.fspRatio { + return num + } + + num += g.randGen.Float64() + return num +} + +// dateTimeRealGener is used to generate floating point values which are dateTime format. +// `fspRatio` is used to control the ratio of values with fractional part. +type dateTimeRealGener struct { + fspRatio float64 + dateTimeGener +} + +func (g dateTimeRealGener) gen() interface{} { + t := g.dateTimeGener.gen().(types.Time) + tmp, err := t.ToNumber().ToInt() + if err != nil { + panic(err) + } + num := float64(tmp) + + if g.randGen.Float64() >= g.fspRatio { + return num + } + + // Not using `t`'s us part since it's too regular. + // Instead, generating a more arbitrary fractional part, e.g. with more than 6 digits. + // We want the parsing logic to be strong enough to deal with this arbitrary fractional number. + num += g.randGen.Float64() + return num +} + +// dateOrDatetimeRealGener is used to generate floating point values which are date or datetime format. +type dateOrDatetimeRealGener struct { + dateRatio float64 + dateRealGener + dateTimeRealGener +} + +func (g dateOrDatetimeRealGener) gen() interface{} { + if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { + return g.dateRealGener.gen() + } + + return g.dateTimeRealGener.gen() +} + +// dateDecimalGener is used to generate decimals which are date format. +// `fspRatio` is used to control the ratio of values with fractional part. I.e., 20010203.000456789 is a valid representation of a date. +type dateDecimalGener struct { + fspRatio float64 + dateGener +} + +func (g dateDecimalGener) gen() interface{} { + t := g.dateGener.gen().(types.Time) + intPart := t.ToNumber() + + if g.randGen.Float64() >= g.fspRatio { + return intPart + } + + // Generate a fractional part that is at most 9 digits. + fracDigits := g.randGen.Intn(1000000000) + fracPart := new(types.MyDecimal).FromInt(int64(fracDigits)) + if err := fracPart.Shift(-9); err != nil { + panic(err) + } + + res := new(types.MyDecimal) + err := types.DecimalAdd(intPart, fracPart, res) + if err != nil { + panic(err) + } + return res +} + +// dateTimeDecimalGener is used to generate decimals which are dateTime format. +type dateTimeDecimalGener struct { + fspRatio float64 + dateTimeGener +} + +func (g dateTimeDecimalGener) gen() interface{} { + t := g.dateTimeGener.gen().(types.Time) + num := t.ToNumber() + // Not using `num`'s fractional part so that we can: + // 1. Return early for non-fsp values. + // 2. Generate a more arbitrary fractional part if needed. + i, err := num.ToInt() + if err != nil { + panic(err) + } + intPart := new(types.MyDecimal).FromInt(i) + + if g.randGen.Float64() >= g.fspRatio { + return intPart + } + + // Generate a fractional part that is at most 9 digits. + fracDigits := g.randGen.Intn(1000000000) + fracPart := new(types.MyDecimal).FromInt(int64(fracDigits)) + if err := fracPart.Shift(-9); err != nil { + panic(err) + } + + res := new(types.MyDecimal) + err = types.DecimalAdd(intPart, fracPart, res) + if err != nil { + panic(err) + } + return res +} + +// dateOrDatetimeDecimalGener is used to generate decimals which are date or datetime format. +type dateOrDatetimeDecimalGener struct { + dateRatio float64 + dateDecimalGener + dateTimeDecimalGener +} + +func (g dateOrDatetimeDecimalGener) gen() interface{} { + if g.dateRatio > 1e-6 && g.dateGener.randGen.Float64() < g.dateRatio { + return g.dateDecimalGener.gen() + } + + return g.dateTimeDecimalGener.gen() +} + +// constStrGener always returns the given string +type constStrGener struct { + s string +} + +func (g *constStrGener) gen() interface{} { + return g.s +} + +type randDurInt struct { + randGen *defaultRandGen +} + +func newRandDurInt() *randDurInt { + return &randDurInt{newDefaultRandGen()} +} + +func (g *randDurInt) gen() interface{} { + return int64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60)) +} + +type randDurReal struct { + randGen *defaultRandGen +} + +func newRandDurReal() *randDurReal { + return &randDurReal{newDefaultRandGen()} +} + +func (g *randDurReal) gen() interface{} { + return float64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60)) +} + +type randDurDecimal struct { + randGen *defaultRandGen +} + +func newRandDurDecimal() *randDurDecimal { + return &randDurDecimal{newDefaultRandGen()} +} + +func (g *randDurDecimal) gen() interface{} { + d := new(types.MyDecimal) + return d.FromFloat64(float64(g.randGen.Intn(types.TimeMaxHour)*10000 + g.randGen.Intn(60)*100 + g.randGen.Intn(60))) +} + +// locationGener is used to generate location for the built-in function GetFormat. +type locationGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func newLocationGener(nullRation float64) *locationGener { + return &locationGener{nullRation, newDefaultRandGen()} +} + +func (g *locationGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + switch g.randGen.Uint32() % 5 { + case 0: + return usaLocation + case 1: + return jisLocation + case 2: + return isoLocation + case 3: + return eurLocation + case 4: + return internalLocation + default: + return nil + } +} + +// formatGener is used to generate a format for the built-in function GetFormat. +type formatGener struct { + nullRation float64 + randGen *defaultRandGen +} + +func newFormatGener(nullRation float64) *formatGener { + return &formatGener{nullRation, newDefaultRandGen()} +} + +func (g *formatGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + switch g.randGen.Uint32() % 4 { + case 0: + return dateFormat + case 1: + return datetimeFormat + case 2: + return timestampFormat + case 3: + return timeFormat + default: + return nil + } +} + +type nullWrappedGener struct { + nullRation float64 + inner dataGenerator + randGen *defaultRandGen +} + +func newNullWrappedGener(nullRation float64, inner dataGenerator) *nullWrappedGener { + return &nullWrappedGener{nullRation, inner, newDefaultRandGen()} +} + +func (g *nullWrappedGener) gen() interface{} { + if g.randGen.Float64() < g.nullRation { + return nil + } + return g.inner.gen() +} + +type vecExprBenchCase struct { + // retEvalType is the EvalType of the expression result. + // This field is required. + retEvalType types.EvalType + // childrenTypes is the EvalTypes of the expression children(arguments). + // This field is required. + childrenTypes []types.EvalType + // childrenFieldTypes is the field types of the expression children(arguments). + // If childrenFieldTypes is not set, it will be converted from childrenTypes. + // This field is optional. + childrenFieldTypes []*types.FieldType + // geners are used to generate data for children and geners[i] generates data for children[i]. + // If geners[i] is nil, the default dataGenerator will be used for its corresponding child. + // The geners slice can be shorter than the children slice, if it has 3 children, then + // geners[gen1, gen2] will be regarded as geners[gen1, gen2, nil]. + // This field is optional. + geners []dataGenerator + // aesModeAttr information, needed by encryption functions + aesModes string + // constants are used to generate constant data for children[i]. + constants []*Constant + // chunkSize is used to specify the chunk size of children, the maximum is 1024. + // This field is optional, 1024 by default. + chunkSize int +} + +type vecExprBenchCases map[string][]vecExprBenchCase + +func fillColumn(eType types.EvalType, chk *chunk.Chunk, colIdx int, testCase vecExprBenchCase) { + var gen dataGenerator + if len(testCase.geners) > colIdx && testCase.geners[colIdx] != nil { + gen = testCase.geners[colIdx] + } + fillColumnWithGener(eType, chk, colIdx, gen) +} + +func fillColumnWithGener(eType types.EvalType, chk *chunk.Chunk, colIdx int, gen dataGenerator) { + batchSize := chk.Capacity() + if gen == nil { + gen = newDefaultGener(0.2, eType) + } + + col := chk.Column(colIdx) + col.Reset(eType) + for i := 0; i < batchSize; i++ { + v := gen.gen() + if v == nil { + col.AppendNull() + continue + } + switch eType { + case types.ETInt: + col.AppendInt64(v.(int64)) + case types.ETReal: + col.AppendFloat64(v.(float64)) + case types.ETDecimal: + col.AppendMyDecimal(v.(*types.MyDecimal)) + case types.ETDatetime, types.ETTimestamp: + col.AppendTime(v.(types.Time)) + case types.ETDuration: + col.AppendDuration(v.(types.Duration)) + case types.ETJson: + col.AppendJSON(v.(types.BinaryJSON)) + case types.ETString: + col.AppendString(v.(string)) + } + } +} + +func randString(r *rand.Rand) string { + n := 10 + r.Intn(10) + buf := make([]byte, n) + for i := range buf { + x := r.Intn(62) + if x < 10 { + buf[i] = byte('0' + x) + } else if x-10 < 26 { + buf[i] = byte('a' + x - 10) + } else { + buf[i] = byte('A' + x - 10 - 26) + } + } + return string(buf) +} + +func eType2FieldType(eType types.EvalType) *types.FieldType { + switch eType { + case types.ETInt: + return types.NewFieldType(mysql.TypeLonglong) + case types.ETReal: + return types.NewFieldType(mysql.TypeDouble) + case types.ETDecimal: + return types.NewFieldType(mysql.TypeNewDecimal) + case types.ETDatetime, types.ETTimestamp: + return types.NewFieldType(mysql.TypeDatetime) + case types.ETDuration: + return types.NewFieldType(mysql.TypeDuration) + case types.ETJson: + return types.NewFieldType(mysql.TypeJSON) + case types.ETString: + return types.NewFieldType(mysql.TypeVarString) + default: + panic(fmt.Sprintf("EvalType=%v is not supported.", eType)) + } +} + +func genVecExprBenchCase(ctx sessionctx.Context, funcName string, testCase vecExprBenchCase) (expr Expression, fts []*types.FieldType, input *chunk.Chunk, output *chunk.Chunk) { + fts = make([]*types.FieldType, len(testCase.childrenTypes)) + for i := range fts { + if i < len(testCase.childrenFieldTypes) && testCase.childrenFieldTypes[i] != nil { + fts[i] = testCase.childrenFieldTypes[i] + } else { + fts[i] = eType2FieldType(testCase.childrenTypes[i]) + } + } + if testCase.chunkSize <= 0 || testCase.chunkSize > 1024 { + testCase.chunkSize = 1024 + } + cols := make([]Expression, len(testCase.childrenTypes)) + input = chunk.New(fts, testCase.chunkSize, testCase.chunkSize) + input.NumRows() + for i, eType := range testCase.childrenTypes { + fillColumn(eType, input, i, testCase) + if i < len(testCase.constants) && testCase.constants[i] != nil { + cols[i] = testCase.constants[i] + } else { + cols[i] = &Column{Index: i, RetType: fts[i]} + } + } + + expr, err := NewFunction(ctx, funcName, eType2FieldType(testCase.retEvalType), cols...) + if err != nil { + panic(err) + } + + output = chunk.New([]*types.FieldType{eType2FieldType(expr.GetType().EvalType())}, testCase.chunkSize, testCase.chunkSize) + return expr, fts, input, output +} + +// testVectorizedEvalOneVec is used to verify that the vectorized +// expression is evaluated correctly during projection +func testVectorizedEvalOneVec(t *testing.T, vecExprCases vecExprBenchCases) { + ctx := mock.NewContext() + for funcName, testCases := range vecExprCases { + for _, testCase := range testCases { + expr, fts, input, output := genVecExprBenchCase(ctx, funcName, testCase) + commentf := func(row int) string { + return fmt.Sprintf("func: %v, case %+v, row: %v, rowData: %v", funcName, testCase, row, input.GetRow(row).GetDatumRow(fts)) + } + output2 := output.CopyConstruct() + require.NoErrorf(t, evalOneVec(ctx, expr, input, output, 0), "func: %v, case: %+v", funcName, testCase) + it := chunk.NewIterator4Chunk(input) + require.NoErrorf(t, evalOneColumn(ctx, expr, it, output2, 0), "func: %v, case: %+v", funcName, testCase) + + c1, c2 := output.Column(0), output2.Column(0) + switch expr.GetType().EvalType() { + case types.ETInt: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetInt64(i), c2.GetInt64(i), commentf(i)) + } + } + case types.ETReal: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetFloat64(i), c2.GetFloat64(i), commentf(i)) + } + } + case types.ETDecimal: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetDecimal(i), c2.GetDecimal(i), commentf(i)) + } + } + case types.ETDatetime, types.ETTimestamp: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetTime(i), c2.GetTime(i), commentf(i)) + } + } + case types.ETDuration: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetDuration(i, 0), c2.GetDuration(i, 0), commentf(i)) + } + } + case types.ETJson: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetJSON(i), c2.GetJSON(i), commentf(i)) + } + } + case types.ETString: + for i := 0; i < input.NumRows(); i++ { + require.Equal(t, c1.IsNull(i), c2.IsNull(i), commentf(i)) + if !c1.IsNull(i) { + require.Equal(t, c1.GetString(i), c2.GetString(i), commentf(i)) + } + } + } + } + } +} + +// benchmarkVectorizedEvalOneVec is used to get the effect of +// using the vectorized expression evaluations during projection +func benchmarkVectorizedEvalOneVec(b *testing.B, vecExprCases vecExprBenchCases) { + ctx := mock.NewContext() + for funcName, testCases := range vecExprCases { + for _, testCase := range testCases { + expr, _, input, output := genVecExprBenchCase(ctx, funcName, testCase) + exprName := expr.String() + if sf, ok := expr.(*ScalarFunction); ok { + exprName = fmt.Sprintf("%v", reflect.TypeOf(sf.Function)) + tmp := strings.Split(exprName, ".") + exprName = tmp[len(tmp)-1] + } + + b.Run(exprName+"-EvalOneVec", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := evalOneVec(ctx, expr, input, output, 0); err != nil { + b.Fatal(err) + } + } + }) + b.Run(exprName+"-EvalOneCol", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + it := chunk.NewIterator4Chunk(input) + if err := evalOneColumn(ctx, expr, it, output, 0); err != nil { + b.Fatal(err) + } + } + }) + } + } +} + +func genVecBuiltinFuncBenchCase(ctx sessionctx.Context, funcName string, testCase vecExprBenchCase) (baseFunc builtinFunc, fts []*types.FieldType, input *chunk.Chunk, result *chunk.Column) { + childrenNumber := len(testCase.childrenTypes) + fts = make([]*types.FieldType, childrenNumber) + for i := range fts { + if i < len(testCase.childrenFieldTypes) && testCase.childrenFieldTypes[i] != nil { + fts[i] = testCase.childrenFieldTypes[i] + } else { + fts[i] = eType2FieldType(testCase.childrenTypes[i]) + } + } + cols := make([]Expression, childrenNumber) + if testCase.chunkSize <= 0 || testCase.chunkSize > 1024 { + testCase.chunkSize = 1024 + } + input = chunk.New(fts, testCase.chunkSize, testCase.chunkSize) + for i, eType := range testCase.childrenTypes { + fillColumn(eType, input, i, testCase) + if i < len(testCase.constants) && testCase.constants[i] != nil { + cols[i] = testCase.constants[i] + } else { + cols[i] = &Column{Index: i, RetType: fts[i]} + } + } + if len(cols) == 0 { + input.SetNumVirtualRows(testCase.chunkSize) + } + + var err error + if funcName == ast.Cast { + var fc functionClass + tp := eType2FieldType(testCase.retEvalType) + switch testCase.retEvalType { + case types.ETInt: + fc = &castAsIntFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETDecimal: + fc = &castAsDecimalFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETReal: + fc = &castAsRealFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETDatetime, types.ETTimestamp: + fc = &castAsTimeFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETDuration: + fc = &castAsDurationFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETJson: + fc = &castAsJSONFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + case types.ETString: + fc = &castAsStringFunctionClass{baseFunctionClass{ast.Cast, 1, 1}, tp} + } + baseFunc, err = fc.getFunction(ctx, cols) + } else if funcName == ast.GetVar { + var fc functionClass + tp := eType2FieldType(testCase.retEvalType) + switch testCase.retEvalType { + case types.ETInt: + fc = &getIntVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} + case types.ETDecimal: + fc = &getDecimalVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} + case types.ETReal: + fc = &getRealVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} + default: + fc = &getStringVarFunctionClass{getVarFunctionClass{baseFunctionClass{ast.GetVar, 1, 1}, tp}} + } + baseFunc, err = fc.getFunction(ctx, cols) + } else { + baseFunc, err = funcs[funcName].getFunction(ctx, cols) + } + if err != nil { + panic(err) + } + result = chunk.NewColumn(eType2FieldType(testCase.retEvalType), testCase.chunkSize) + // Mess up the output to make sure vecEvalXXX to call ResizeXXX/ReserveXXX itself. + result.AppendNull() + return baseFunc, fts, input, result +} + +// a hack way to calculate length of a chunk.Column. +func getColumnLen(col *chunk.Column, eType types.EvalType) int { + chk := chunk.New([]*types.FieldType{eType2FieldType(eType)}, 1024, 1024) + chk.SetCol(0, col) + return chk.NumRows() +} + +// removeTestOptions removes all not needed options like '-test.timeout=' from argument list +func removeTestOptions(args []string) []string { + argList := args[:0] + + // args contains '-test.timeout=' option for example + // excluding it to be able to run all tests + for _, arg := range args { + if strings.HasPrefix(arg, "builtin") || IsFunctionSupported(arg) { + argList = append(argList, arg) + } + } + return argList +} + +// testVectorizedBuiltinFunc is used to verify that the vectorized +// expression is evaluated correctly +func testVectorizedBuiltinFunc(t *testing.T, vecExprCases vecExprBenchCases) { + testFunc := make(map[string]bool) + argList := removeTestOptions(flag.Args()) + testAll := len(argList) == 0 + for _, arg := range argList { + testFunc[arg] = true + } + for funcName, testCases := range vecExprCases { + for _, testCase := range testCases { + ctx := mock.NewContext() + if testCase.aesModes == "" { + testCase.aesModes = "aes-128-ecb" + } + err := ctx.GetSessionVars().SetSystemVar(variable.BlockEncryptionMode, testCase.aesModes) + require.NoError(t, err) + if funcName == ast.CurrentUser || funcName == ast.User { + ctx.GetSessionVars().User = &auth.UserIdentity{ + Username: "tidb", + Hostname: "localhost", + CurrentUser: true, + AuthHostname: "localhost", + AuthUsername: "tidb", + } + } + if funcName == ast.GetParam { + testTime := time.Now() + ctx.GetSessionVars().PlanCacheParams.Append( + types.NewIntDatum(1), + types.NewDecimalDatum(types.NewDecFromStringForTest("20170118123950.123")), + types.NewTimeDatum(types.NewTime(types.FromGoTime(testTime), mysql.TypeTimestamp, 6)), + types.NewDurationDatum(types.ZeroDuration), + types.NewStringDatum("{}"), + types.NewBinaryLiteralDatum([]byte{1}), + types.NewBytesDatum([]byte{'b'}), + types.NewFloat32Datum(1.1), + types.NewFloat64Datum(2.1), + types.NewUintDatum(100), + types.NewMysqlBitDatum([]byte{1}), + types.NewMysqlEnumDatum(types.Enum{Name: "n", Value: 2})) + } + baseFunc, fts, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) + baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) + tmp := strings.Split(baseFuncName, ".") + baseFuncName = tmp[len(tmp)-1] + + if !testAll && (!testFunc[baseFuncName] && !testFunc[funcName]) { + continue + } + // do not forget to implement the vectorized method. + require.Truef(t, baseFunc.vectorized(), "func: %v, case: %+v", baseFuncName, testCase) + commentf := func(row int) string { + return fmt.Sprintf("func: %v, case %+v, row: %v, rowData: %v", baseFuncName, testCase, row, input.GetRow(row).GetDatumRow(fts)) + } + it := chunk.NewIterator4Chunk(input) + i := 0 + var vecWarnCnt uint16 + switch testCase.retEvalType { + case types.ETInt: + err := baseFunc.vecEvalInt(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + i64s := output.Int64s() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalInt(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, i64s[i], val, commentf(i)) + } + i++ + } + case types.ETReal: + err := baseFunc.vecEvalReal(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + f64s := output.Float64s() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalReal(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, f64s[i], val, commentf(i)) + } + i++ + } + case types.ETDecimal: + err := baseFunc.vecEvalDecimal(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + d64s := output.Decimals() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalDecimal(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, d64s[i], *val, commentf(i)) + } + i++ + } + case types.ETDatetime, types.ETTimestamp: + err := baseFunc.vecEvalTime(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + t64s := output.Times() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalTime(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, t64s[i], val, commentf(i)) + } + i++ + } + case types.ETDuration: + err := baseFunc.vecEvalDuration(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + d64s := output.GoDurations() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalDuration(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, d64s[i], val.Duration, commentf(i)) + } + i++ + } + case types.ETJson: + err := baseFunc.vecEvalJSON(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalJSON(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + cmp := types.CompareBinaryJSON(val, output.GetJSON(i)) + require.Zero(t, cmp, commentf(i)) + } + i++ + } + case types.ETString: + err := baseFunc.vecEvalString(input, output) + require.NoErrorf(t, err, "func: %v, case: %+v", baseFuncName, testCase) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + vecWarnCnt = ctx.GetSessionVars().StmtCtx.WarningCount() + for row := it.Begin(); row != it.End(); row = it.Next() { + val, isNull, err := baseFunc.evalString(row) + require.NoErrorf(t, err, commentf(i)) + require.Equal(t, output.IsNull(i), isNull, commentf(i)) + if !isNull { + require.Equal(t, output.GetString(i), val, commentf(i)) + } + i++ + } + default: + t.Fatalf("evalType=%v is not supported", testCase.retEvalType) + } + + // check warnings + totalWarns := ctx.GetSessionVars().StmtCtx.WarningCount() + require.Equal(t, totalWarns, 2*vecWarnCnt) + warns := ctx.GetSessionVars().StmtCtx.GetWarnings() + for i := 0; i < int(vecWarnCnt); i++ { + require.True(t, terror.ErrorEqual(warns[i].Err, warns[i+int(vecWarnCnt)].Err)) + } + } + } +} + +// testVectorizedBuiltinFuncForRand is used to verify that the vectorized +// expression is evaluated correctly +func testVectorizedBuiltinFuncForRand(t *testing.T, vecExprCases vecExprBenchCases) { + for funcName, testCases := range vecExprCases { + require.True(t, strings.EqualFold("rand", funcName)) + + for _, testCase := range testCases { + require.Len(t, testCase.childrenTypes, 0) + + ctx := mock.NewContext() + baseFunc, _, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) + baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) + tmp := strings.Split(baseFuncName, ".") + baseFuncName = tmp[len(tmp)-1] + // do not forget to implement the vectorized method. + require.Truef(t, baseFunc.vectorized(), "func: %v", baseFuncName) + switch testCase.retEvalType { + case types.ETReal: + err := baseFunc.vecEvalReal(input, output) + require.NoError(t, err) + // do not forget to call ResizeXXX/ReserveXXX + require.Equal(t, input.NumRows(), getColumnLen(output, testCase.retEvalType)) + // check result + res := output.Float64s() + for _, v := range res { + require.True(t, (0 <= v) && (v < 1)) + } + default: + t.Fatalf("evalType=%v is not supported", testCase.retEvalType) + } + } + } +} + +// benchmarkVectorizedBuiltinFunc is used to get the effect of +// using the vectorized expression evaluations +func benchmarkVectorizedBuiltinFunc(b *testing.B, vecExprCases vecExprBenchCases) { + ctx := mock.NewContext() + testFunc := make(map[string]bool) + argList := removeTestOptions(flag.Args()) + testAll := len(argList) == 0 + for _, arg := range argList { + testFunc[arg] = true + } + for funcName, testCases := range vecExprCases { + for _, testCase := range testCases { + if testCase.aesModes == "" { + testCase.aesModes = "aes-128-ecb" + } + err := ctx.GetSessionVars().SetSystemVar(variable.BlockEncryptionMode, testCase.aesModes) + if err != nil { + panic(err) + } + if funcName == ast.CurrentUser || funcName == ast.User { + ctx.GetSessionVars().User = &auth.UserIdentity{ + Username: "tidb", + Hostname: "localhost", + CurrentUser: true, + AuthHostname: "localhost", + AuthUsername: "tidb", + } + } + if funcName == ast.GetParam { + testTime := time.Now() + ctx.GetSessionVars().PlanCacheParams.Append( + types.NewIntDatum(1), + types.NewDecimalDatum(types.NewDecFromStringForTest("20170118123950.123")), + types.NewTimeDatum(types.NewTime(types.FromGoTime(testTime), mysql.TypeTimestamp, 6)), + types.NewDurationDatum(types.ZeroDuration), + types.NewStringDatum("{}"), + types.NewBinaryLiteralDatum([]byte{1}), + types.NewBytesDatum([]byte{'b'}), + types.NewFloat32Datum(1.1), + types.NewFloat64Datum(2.1), + types.NewUintDatum(100), + types.NewMysqlBitDatum([]byte{1}), + types.NewMysqlEnumDatum(types.Enum{Name: "n", Value: 2})) + } + baseFunc, _, input, output := genVecBuiltinFuncBenchCase(ctx, funcName, testCase) + baseFuncName := fmt.Sprintf("%v", reflect.TypeOf(baseFunc)) + tmp := strings.Split(baseFuncName, ".") + baseFuncName = tmp[len(tmp)-1] + + if !testAll && !testFunc[baseFuncName] && !testFunc[funcName] { + continue + } + + b.Run(baseFuncName+"-VecBuiltinFunc", func(b *testing.B) { + b.ResetTimer() + switch testCase.retEvalType { + case types.ETInt: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalInt(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETReal: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalReal(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETDecimal: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalDecimal(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETDatetime, types.ETTimestamp: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalTime(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETDuration: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalDuration(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETJson: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalJSON(input, output); err != nil { + b.Fatal(err) + } + } + case types.ETString: + for i := 0; i < b.N; i++ { + if err := baseFunc.vecEvalString(input, output); err != nil { + b.Fatal(err) + } + } + default: + b.Fatalf("evalType=%v is not supported", testCase.retEvalType) + } + }) + b.Run(baseFuncName+"-NonVecBuiltinFunc", func(b *testing.B) { + b.ResetTimer() + it := chunk.NewIterator4Chunk(input) + switch testCase.retEvalType { + case types.ETInt: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalInt(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendInt64(v) + } + } + } + case types.ETReal: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalReal(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendFloat64(v) + } + } + } + case types.ETDecimal: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalDecimal(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendMyDecimal(v) + } + } + } + case types.ETDatetime, types.ETTimestamp: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalTime(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendTime(v) + } + } + } + case types.ETDuration: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalDuration(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendDuration(v) + } + } + } + case types.ETJson: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalJSON(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendJSON(v) + } + } + } + case types.ETString: + for i := 0; i < b.N; i++ { + output.Reset(testCase.retEvalType) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, isNull, err := baseFunc.evalString(row) + if err != nil { + b.Fatal(err) + } + if isNull { + output.AppendNull() + } else { + output.AppendString(v) + } + } + } + default: + b.Fatalf("evalType=%v is not supported", testCase.retEvalType) + } + }) + } + } +} + +func genVecEvalBool(numCols int, colTypes, eTypes []types.EvalType) (CNFExprs, *chunk.Chunk) { + gens := make([]dataGenerator, 0, len(eTypes)) + for _, eType := range eTypes { + if eType == types.ETString { + gens = append(gens, &numStrGener{*newRangeInt64Gener(0, 10)}) + } else { + gens = append(gens, newDefaultGener(0.05, eType)) + } + } + + ts := make([]types.EvalType, 0, numCols) + gs := make([]dataGenerator, 0, numCols) + fts := make([]*types.FieldType, 0, numCols) + randGen := newDefaultRandGen() + for i := 0; i < numCols; i++ { + idx := randGen.Intn(len(eTypes)) + if colTypes != nil { + for j := range eTypes { + if colTypes[i] == eTypes[j] { + idx = j + break + } + } + } + ts = append(ts, eTypes[idx]) + gs = append(gs, gens[idx]) + fts = append(fts, eType2FieldType(eTypes[idx])) + } + + input := chunk.New(fts, 1024, 1024) + exprs := make(CNFExprs, 0, numCols) + for i := 0; i < numCols; i++ { + fillColumn(ts[i], input, i, vecExprBenchCase{geners: gs}) + exprs = append(exprs, &Column{Index: i, RetType: fts[i]}) + } + return exprs, input +} + +func generateRandomSel() []int { + randGen := newDefaultRandGen() + randGen.Seed(time.Now().UnixNano()) + var sel []int + count := 0 + // Use constant 256 to make it faster to generate randomly arranged sel slices + num := randGen.Intn(256) + 1 + existed := make([]bool, 1024) + for i := 0; i < 1024; i++ { + existed[i] = false + } + for count < num { + val := randGen.Intn(1024) + if !existed[val] { + existed[val] = true + count++ + } + } + for i := 0; i < 1024; i++ { + if existed[i] { + sel = append(sel, i) + } + } + return sel +} + +func BenchmarkVecEvalBool(b *testing.B) { + ctx := mock.NewContext() + selected := make([]bool, 0, 1024) + nulls := make([]bool, 0, 1024) + eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} + tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} + for numCols := 1; numCols <= 2; numCols++ { + typeCombination := make([]types.EvalType, numCols) + var combFunc func(nCols int) + combFunc = func(nCols int) { + if nCols == 0 { + name := "" + for _, t := range typeCombination { + for i := range eTypes { + if t == eTypes[i] { + name += tNames[t] + "/" + } + } + } + exprs, input := genVecEvalBool(numCols, typeCombination, eTypes) + b.Run("Vec-"+name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := VecEvalBool(ctx, exprs, input, selected, nulls) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run("Row-"+name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + it := chunk.NewIterator4Chunk(input) + for row := it.Begin(); row != it.End(); row = it.Next() { + _, _, err := EvalBool(ctx, exprs, row) + if err != nil { + b.Fatal(err) + } + } + } + }) + return + } + for _, eType := range eTypes { + typeCombination[nCols-1] = eType + combFunc(nCols - 1) + } + } + + combFunc(numCols) + } +} + +func BenchmarkRowBasedFilterAndVectorizedFilter(b *testing.B) { + ctx := mock.NewContext() + selected := make([]bool, 0, 1024) + nulls := make([]bool, 0, 1024) + eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} + tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} + for numCols := 1; numCols <= 2; numCols++ { + typeCombination := make([]types.EvalType, numCols) + var combFunc func(nCols int) + combFunc = func(nCols int) { + if nCols == 0 { + name := "" + for _, t := range typeCombination { + for i := range eTypes { + if t == eTypes[i] { + name += tNames[t] + "/" + } + } + } + exprs, input := genVecEvalBool(numCols, typeCombination, eTypes) + it := chunk.NewIterator4Chunk(input) + b.Run("Vec-"+name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := vectorizedFilter(ctx, exprs, it, selected, nulls) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run("Row-"+name, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := rowBasedFilter(ctx, exprs, it, selected, nulls) + if err != nil { + b.Fatal(err) + } + } + }) + return + } + for _, eType := range eTypes { + typeCombination[nCols-1] = eType + combFunc(nCols - 1) + } + } + combFunc(numCols) + } + + // Add special case to prove when some calculations are added, + // the vectorizedFilter for int types will be more faster than rowBasedFilter. + funcName := ast.Least + testCase := vecExprBenchCase{retEvalType: types.ETInt, childrenTypes: []types.EvalType{types.ETInt, types.ETInt}} + expr, _, input, _ := genVecExprBenchCase(ctx, funcName, testCase) + it := chunk.NewIterator4Chunk(input) + + b.Run("Vec-special case", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := vectorizedFilter(ctx, []Expression{expr}, it, selected, nulls) + if err != nil { + panic(err) + } + } + }) + b.Run("Row-special case", func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := rowBasedFilter(ctx, []Expression{expr}, it, selected, nulls) + if err != nil { + panic(err) + } + } + }) +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkCastIntAsIntRow, + BenchmarkCastIntAsIntVec, + BenchmarkVectorizedExecute, + BenchmarkScalarFunctionClone, + ) +} diff --git a/expression/builtin.go b/pkg/expression/builtin.go similarity index 99% rename from expression/builtin.go rename to pkg/expression/builtin.go index 81fe041cf0f60..45a5cf0b2a5ee 100644 --- a/expression/builtin.go +++ b/pkg/expression/builtin.go @@ -32,16 +32,16 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/set" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_arithmetic.go b/pkg/expression/builtin_arithmetic.go similarity index 99% rename from expression/builtin_arithmetic.go rename to pkg/expression/builtin_arithmetic.go index b21b9dc6a5914..1bff34ccd91a1 100644 --- a/expression/builtin_arithmetic.go +++ b/pkg/expression/builtin_arithmetic.go @@ -18,12 +18,12 @@ import ( "fmt" "math" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_arithmetic_test.go b/pkg/expression/builtin_arithmetic_test.go similarity index 98% rename from expression/builtin_arithmetic_test.go rename to pkg/expression/builtin_arithmetic_test.go index 0d79464ddb650..0a8c7bd0df506 100644 --- a/expression/builtin_arithmetic_test.go +++ b/pkg/expression/builtin_arithmetic_test.go @@ -19,12 +19,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_arithmetic_vec.go b/pkg/expression/builtin_arithmetic_vec.go similarity index 99% rename from expression/builtin_arithmetic_vec.go rename to pkg/expression/builtin_arithmetic_vec.go index 4197d2e977737..49c62896ecf6d 100644 --- a/expression/builtin_arithmetic_vec.go +++ b/pkg/expression/builtin_arithmetic_vec.go @@ -18,11 +18,11 @@ import ( "fmt" "math" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) //revive:disable:defer diff --git a/expression/builtin_arithmetic_vec_test.go b/pkg/expression/builtin_arithmetic_vec_test.go similarity index 98% rename from expression/builtin_arithmetic_vec_test.go rename to pkg/expression/builtin_arithmetic_vec_test.go index d3e1a7104e949..ed0b3a10bee58 100644 --- a/expression/builtin_arithmetic_vec_test.go +++ b/pkg/expression/builtin_arithmetic_vec_test.go @@ -18,11 +18,11 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_cast.go b/pkg/expression/builtin_cast.go similarity index 99% rename from expression/builtin_cast.go rename to pkg/expression/builtin_cast.go index a9a9095250f0c..4d49e0a4be94c 100644 --- a/expression/builtin_cast.go +++ b/pkg/expression/builtin_cast.go @@ -30,16 +30,16 @@ import ( gotime "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_cast_bench_test.go b/pkg/expression/builtin_cast_bench_test.go similarity index 91% rename from expression/builtin_cast_bench_test.go rename to pkg/expression/builtin_cast_bench_test.go index b5cc9279a1572..2a3c66d3203ae 100644 --- a/expression/builtin_cast_bench_test.go +++ b/pkg/expression/builtin_cast_bench_test.go @@ -18,10 +18,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" ) func genCastIntAsInt() (*builtinCastIntAsIntSig, *chunk.Chunk, *chunk.Column) { diff --git a/expression/builtin_cast_test.go b/pkg/expression/builtin_cast_test.go similarity index 99% rename from expression/builtin_cast_test.go rename to pkg/expression/builtin_cast_test.go index 143030dd3435a..f6142331f2270 100644 --- a/expression/builtin_cast_test.go +++ b/pkg/expression/builtin_cast_test.go @@ -21,12 +21,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_cast_vec.go b/pkg/expression/builtin_cast_vec.go similarity index 99% rename from expression/builtin_cast_vec.go rename to pkg/expression/builtin_cast_vec.go index 470d5ce5f3f2f..947aceecbf8da 100644 --- a/expression/builtin_cast_vec.go +++ b/pkg/expression/builtin_cast_vec.go @@ -21,9 +21,9 @@ import ( "strings" gotime "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func (b *builtinCastIntAsDurationSig) vecEvalDuration(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_cast_vec_test.go b/pkg/expression/builtin_cast_vec_test.go similarity index 98% rename from expression/builtin_cast_vec_test.go rename to pkg/expression/builtin_cast_vec_test.go index b7803c5c02012..eb5d4ae26ba30 100644 --- a/expression/builtin_cast_vec_test.go +++ b/pkg/expression/builtin_cast_vec_test.go @@ -21,11 +21,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_compare.go b/pkg/expression/builtin_compare.go similarity index 99% rename from expression/builtin_compare.go rename to pkg/expression/builtin_compare.go index 52f845bc35370..e1eb1532b8462 100644 --- a/expression/builtin_compare.go +++ b/pkg/expression/builtin_compare.go @@ -19,15 +19,15 @@ import ( "math" "strings" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-tipb" "github.com/pkg/errors" ) diff --git a/expression/builtin_compare_test.go b/pkg/expression/builtin_compare_test.go similarity index 98% rename from expression/builtin_compare_test.go rename to pkg/expression/builtin_compare_test.go index 4f6660e566984..8df462792a2a0 100644 --- a/expression/builtin_compare_test.go +++ b/pkg/expression/builtin_compare_test.go @@ -19,11 +19,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_compare_vec.go b/pkg/expression/builtin_compare_vec.go similarity index 99% rename from expression/builtin_compare_vec.go rename to pkg/expression/builtin_compare_vec.go index d3cde0c0faf4f..2d09673bfe251 100644 --- a/expression/builtin_compare_vec.go +++ b/pkg/expression/builtin_compare_vec.go @@ -17,9 +17,9 @@ package expression import ( "strings" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // vecEvalDecimal evals a builtinGreatestDecimalSig. diff --git a/expression/builtin_compare_vec_generated.go b/pkg/expression/builtin_compare_vec_generated.go similarity index 99% rename from expression/builtin_compare_vec_generated.go rename to pkg/expression/builtin_compare_vec_generated.go index 5b53f65229c31..3de52a3f4af1c 100644 --- a/expression/builtin_compare_vec_generated.go +++ b/pkg/expression/builtin_compare_vec_generated.go @@ -19,8 +19,8 @@ package expression import ( "cmp" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func (b *builtinLTRealSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_compare_vec_generated_test.go b/pkg/expression/builtin_compare_vec_generated_test.go similarity index 98% rename from expression/builtin_compare_vec_generated_test.go rename to pkg/expression/builtin_compare_vec_generated_test.go index 247111e3be448..25763c189b6a2 100644 --- a/expression/builtin_compare_vec_generated_test.go +++ b/pkg/expression/builtin_compare_vec_generated_test.go @@ -19,8 +19,8 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecGeneratedBuiltinCompareCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_compare_vec_test.go b/pkg/expression/builtin_compare_vec_test.go similarity index 98% rename from expression/builtin_compare_vec_test.go rename to pkg/expression/builtin_compare_vec_test.go index de63a9479b17d..ce44becbe2f96 100644 --- a/expression/builtin_compare_vec_test.go +++ b/pkg/expression/builtin_compare_vec_test.go @@ -17,9 +17,9 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) var vecBuiltinCompareCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_control.go b/pkg/expression/builtin_control.go similarity index 99% rename from expression/builtin_control.go rename to pkg/expression/builtin_control.go index 442f1b5f4a03d..2007bfdd416c6 100644 --- a/expression/builtin_control.go +++ b/pkg/expression/builtin_control.go @@ -15,12 +15,12 @@ package expression import ( - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_control_test.go b/pkg/expression/builtin_control_test.go similarity index 96% rename from expression/builtin_control_test.go rename to pkg/expression/builtin_control_test.go index 161bcdecebda0..329df3c1cbce8 100644 --- a/expression/builtin_control_test.go +++ b/pkg/expression/builtin_control_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_control_vec_generated.go b/pkg/expression/builtin_control_vec_generated.go similarity index 99% rename from expression/builtin_control_vec_generated.go rename to pkg/expression/builtin_control_vec_generated.go index cc69b6d07e999..0cc45f668b316 100644 --- a/expression/builtin_control_vec_generated.go +++ b/pkg/expression/builtin_control_vec_generated.go @@ -19,8 +19,8 @@ package expression import ( "time" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // NOTE: Control expressions optionally evaluate some branches depending on conditions, but vectorization executes all diff --git a/expression/builtin_control_vec_generated_test.go b/pkg/expression/builtin_control_vec_generated_test.go similarity index 99% rename from expression/builtin_control_vec_generated_test.go rename to pkg/expression/builtin_control_vec_generated_test.go index 99317242be271..002a8a60bfb9a 100644 --- a/expression/builtin_control_vec_generated_test.go +++ b/pkg/expression/builtin_control_vec_generated_test.go @@ -20,8 +20,8 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var defaultControlIntGener = &controlIntGener{zeroRation: 0.3, defaultGener: *newDefaultGener(0.3, types.ETInt)} diff --git a/expression/builtin_convert_charset.go b/pkg/expression/builtin_convert_charset.go similarity index 96% rename from expression/builtin_convert_charset.go rename to pkg/expression/builtin_convert_charset.go index abd7bfccfe584..e46f9e633c0f7 100644 --- a/expression/builtin_convert_charset.go +++ b/pkg/expression/builtin_convert_charset.go @@ -20,16 +20,16 @@ import ( "strings" "unicode" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_encryption.go b/pkg/expression/builtin_encryption.go similarity index 98% rename from expression/builtin_encryption.go rename to pkg/expression/builtin_encryption.go index 6b1de33ffec2d..e00744da23483 100644 --- a/expression/builtin_encryption.go +++ b/pkg/expression/builtin_encryption.go @@ -30,14 +30,14 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/encrypt" - pwdValidator "github.com/pingcap/tidb/util/password-validation" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/encrypt" + pwdValidator "github.com/pingcap/tidb/pkg/util/password-validation" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_encryption_test.go b/pkg/expression/builtin_encryption_test.go similarity index 98% rename from expression/builtin_encryption_test.go rename to pkg/expression/builtin_encryption_test.go index 087fb3f35e466..23aa683c6bbc6 100644 --- a/expression/builtin_encryption_test.go +++ b/pkg/expression/builtin_encryption_test.go @@ -21,16 +21,16 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_encryption_vec.go b/pkg/expression/builtin_encryption_vec.go similarity index 98% rename from expression/builtin_encryption_vec.go rename to pkg/expression/builtin_encryption_vec.go index 98fabc56b14f3..5c8e0f04f7039 100644 --- a/expression/builtin_encryption_vec.go +++ b/pkg/expression/builtin_encryption_vec.go @@ -28,12 +28,12 @@ import ( "io" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/encrypt" - "github.com/pingcap/tidb/util/zeropool" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/encrypt" + "github.com/pingcap/tidb/pkg/util/zeropool" ) //revive:disable:defer diff --git a/expression/builtin_encryption_vec_test.go b/pkg/expression/builtin_encryption_vec_test.go similarity index 98% rename from expression/builtin_encryption_vec_test.go rename to pkg/expression/builtin_encryption_vec_test.go index a3e6f2be61040..f434be423a551 100644 --- a/expression/builtin_encryption_vec_test.go +++ b/pkg/expression/builtin_encryption_vec_test.go @@ -17,8 +17,8 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecBuiltinEncryptionCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_func_param.go b/pkg/expression/builtin_func_param.go similarity index 98% rename from expression/builtin_func_param.go rename to pkg/expression/builtin_func_param.go index 1e64fb03b2978..e146a7ccf722c 100644 --- a/expression/builtin_func_param.go +++ b/pkg/expression/builtin_func_param.go @@ -15,7 +15,7 @@ package expression import ( - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/util/chunk" ) // Parameters may be const or ignored by the user, so different situations should be considered diff --git a/expression/builtin_grouping.go b/pkg/expression/builtin_grouping.go similarity index 97% rename from expression/builtin_grouping.go rename to pkg/expression/builtin_grouping.go index a08f3a713198b..4a9c75ed7ec45 100644 --- a/expression/builtin_grouping.go +++ b/pkg/expression/builtin_grouping.go @@ -19,11 +19,11 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_grouping_test.go b/pkg/expression/builtin_grouping_test.go similarity index 95% rename from expression/builtin_grouping_test.go rename to pkg/expression/builtin_grouping_test.go index 06558453077b1..75514c63561f3 100644 --- a/expression/builtin_grouping_test.go +++ b/pkg/expression/builtin_grouping_test.go @@ -18,10 +18,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_ilike.go b/pkg/expression/builtin_ilike.go similarity index 94% rename from expression/builtin_ilike.go rename to pkg/expression/builtin_ilike.go index e1eefbe99d3cc..225c6a3352412 100644 --- a/expression/builtin_ilike.go +++ b/pkg/expression/builtin_ilike.go @@ -17,11 +17,11 @@ package expression import ( "sync" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_ilike_test.go b/pkg/expression/builtin_ilike_test.go similarity index 95% rename from expression/builtin_ilike_test.go rename to pkg/expression/builtin_ilike_test.go index 3b22fd65a5378..14f119cf9ba7a 100644 --- a/expression/builtin_ilike_test.go +++ b/pkg/expression/builtin_ilike_test.go @@ -18,13 +18,13 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_ilike_vec.go b/pkg/expression/builtin_ilike_vec.go similarity index 97% rename from expression/builtin_ilike_vec.go rename to pkg/expression/builtin_ilike_vec.go index 478bfa9806868..9e310e88c1fe1 100644 --- a/expression/builtin_ilike_vec.go +++ b/pkg/expression/builtin_ilike_vec.go @@ -16,10 +16,10 @@ package expression import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // LowerAlphaASCII only lowers alpha ascii diff --git a/expression/builtin_info.go b/pkg/expression/builtin_info.go similarity index 98% rename from expression/builtin_info.go rename to pkg/expression/builtin_info.go index 6cede43e519c6..3c88cc11abf58 100644 --- a/expression/builtin_info.go +++ b/pkg/expression/builtin_info.go @@ -26,17 +26,17 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/printer" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/printer" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_info_test.go b/pkg/expression/builtin_info_test.go similarity index 96% rename from expression/builtin_info_test.go rename to pkg/expression/builtin_info_test.go index e50dba14d4416..38e9fb6be65ac 100644 --- a/expression/builtin_info_test.go +++ b/pkg/expression/builtin_info_test.go @@ -18,15 +18,15 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/printer" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/printer" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_info_vec.go b/pkg/expression/builtin_info_vec.go similarity index 97% rename from expression/builtin_info_vec.go rename to pkg/expression/builtin_info_vec.go index 8df3ca2513eeb..4d2176a252740 100644 --- a/expression/builtin_info_vec.go +++ b/pkg/expression/builtin_info_vec.go @@ -19,11 +19,11 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/printer" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/printer" ) func (b *builtinDatabaseSig) vectorized() bool { diff --git a/expression/builtin_info_vec_test.go b/pkg/expression/builtin_info_vec_test.go similarity index 95% rename from expression/builtin_info_vec_test.go rename to pkg/expression/builtin_info_vec_test.go index f66e26167f0e3..76a927604d7c2 100644 --- a/expression/builtin_info_vec_test.go +++ b/pkg/expression/builtin_info_vec_test.go @@ -19,11 +19,11 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" ) type tidbKeyGener struct { diff --git a/expression/builtin_json.go b/pkg/expression/builtin_json.go similarity index 99% rename from expression/builtin_json.go rename to pkg/expression/builtin_json.go index 1d85546497136..58b2b39f9dbe8 100644 --- a/expression/builtin_json.go +++ b/pkg/expression/builtin_json.go @@ -21,13 +21,13 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_json_test.go b/pkg/expression/builtin_json_test.go similarity index 99% rename from expression/builtin_json_test.go rename to pkg/expression/builtin_json_test.go index 23da60f380652..993323d6376f5 100644 --- a/expression/builtin_json_test.go +++ b/pkg/expression/builtin_json_test.go @@ -18,12 +18,12 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_json_vec.go b/pkg/expression/builtin_json_vec.go similarity index 99% rename from expression/builtin_json_vec.go rename to pkg/expression/builtin_json_vec.go index 0610a1f6ea3ca..f4f2aab04c929 100644 --- a/expression/builtin_json_vec.go +++ b/pkg/expression/builtin_json_vec.go @@ -21,10 +21,10 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_json_vec_test.go b/pkg/expression/builtin_json_vec_test.go similarity index 99% rename from expression/builtin_json_vec_test.go rename to pkg/expression/builtin_json_vec_test.go index be67199440f00..f47bcad66516f 100644 --- a/expression/builtin_json_vec_test.go +++ b/pkg/expression/builtin_json_vec_test.go @@ -17,8 +17,8 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecBuiltinJSONCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_like.go b/pkg/expression/builtin_like.go similarity index 94% rename from expression/builtin_like.go rename to pkg/expression/builtin_like.go index 4189e2c524b37..b25c680d2f6bb 100644 --- a/expression/builtin_like.go +++ b/pkg/expression/builtin_like.go @@ -17,10 +17,10 @@ package expression import ( "sync" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_like_test.go b/pkg/expression/builtin_like_test.go similarity index 95% rename from expression/builtin_like_test.go rename to pkg/expression/builtin_like_test.go index 49d514ade37df..61a5cf089dfd5 100644 --- a/expression/builtin_like_test.go +++ b/pkg/expression/builtin_like_test.go @@ -18,12 +18,12 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_like_vec.go b/pkg/expression/builtin_like_vec.go similarity index 97% rename from expression/builtin_like_vec.go rename to pkg/expression/builtin_like_vec.go index 6b5cd60de0b30..ff9d725563868 100644 --- a/expression/builtin_like_vec.go +++ b/pkg/expression/builtin_like_vec.go @@ -15,7 +15,7 @@ package expression import ( - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/util/chunk" ) func (b *builtinLikeSig) vectorized() bool { diff --git a/expression/builtin_like_vec_test.go b/pkg/expression/builtin_like_vec_test.go similarity index 94% rename from expression/builtin_like_vec_test.go rename to pkg/expression/builtin_like_vec_test.go index 6a9f00efc9503..69bcb93735e3e 100644 --- a/expression/builtin_like_vec_test.go +++ b/pkg/expression/builtin_like_vec_test.go @@ -17,8 +17,8 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecBuiltinLikeCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_math.go b/pkg/expression/builtin_math.go similarity index 99% rename from expression/builtin_math.go rename to pkg/expression/builtin_math.go index 35914aa21cd94..d09b1ce9967ba 100644 --- a/expression/builtin_math.go +++ b/pkg/expression/builtin_math.go @@ -25,12 +25,12 @@ import ( "strconv" "strings" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_math_test.go b/pkg/expression/builtin_math_test.go similarity index 98% rename from expression/builtin_math_test.go rename to pkg/expression/builtin_math_test.go index 71a6ebf6dc32f..9e44f6e5167e5 100644 --- a/expression/builtin_math_test.go +++ b/pkg/expression/builtin_math_test.go @@ -20,14 +20,14 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_math_vec.go b/pkg/expression/builtin_math_vec.go similarity index 99% rename from expression/builtin_math_vec.go rename to pkg/expression/builtin_math_vec.go index 772f334e416f0..44c76bb3833f5 100644 --- a/expression/builtin_math_vec.go +++ b/pkg/expression/builtin_math_vec.go @@ -20,10 +20,10 @@ import ( "math" "strconv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mathutil" ) func (b *builtinLog1ArgSig) vecEvalReal(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_math_vec_test.go b/pkg/expression/builtin_math_vec_test.go similarity index 98% rename from expression/builtin_math_vec_test.go rename to pkg/expression/builtin_math_vec_test.go index 458ac253d22ba..2aeddc4f7a307 100644 --- a/expression/builtin_math_vec_test.go +++ b/pkg/expression/builtin_math_vec_test.go @@ -17,9 +17,9 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) var vecBuiltinMathCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_miscellaneous.go b/pkg/expression/builtin_miscellaneous.go similarity index 99% rename from expression/builtin_miscellaneous.go rename to pkg/expression/builtin_miscellaneous.go index 52991696e5ca1..a208db53eaa1e 100644 --- a/expression/builtin_miscellaneous.go +++ b/pkg/expression/builtin_miscellaneous.go @@ -27,13 +27,13 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/vitess" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/vitess" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_miscellaneous_test.go b/pkg/expression/builtin_miscellaneous_test.go similarity index 98% rename from expression/builtin_miscellaneous_test.go rename to pkg/expression/builtin_miscellaneous_test.go index db6813676ab68..74f914d2c2595 100644 --- a/expression/builtin_miscellaneous_test.go +++ b/pkg/expression/builtin_miscellaneous_test.go @@ -20,12 +20,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_miscellaneous_vec.go b/pkg/expression/builtin_miscellaneous_vec.go similarity index 99% rename from expression/builtin_miscellaneous_vec.go rename to pkg/expression/builtin_miscellaneous_vec.go index aadc38fade399..9042df587f834 100644 --- a/expression/builtin_miscellaneous_vec.go +++ b/pkg/expression/builtin_miscellaneous_vec.go @@ -25,9 +25,9 @@ import ( "time" "github.com/google/uuid" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/vitess" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/vitess" ) func (b *builtinInetNtoaSig) vecEvalString(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_miscellaneous_vec_test.go b/pkg/expression/builtin_miscellaneous_vec_test.go similarity index 98% rename from expression/builtin_miscellaneous_vec_test.go rename to pkg/expression/builtin_miscellaneous_vec_test.go index 6802fe41c83c1..91da4c219c536 100644 --- a/expression/builtin_miscellaneous_vec_test.go +++ b/pkg/expression/builtin_miscellaneous_vec_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_op.go b/pkg/expression/builtin_op.go similarity index 99% rename from expression/builtin_op.go rename to pkg/expression/builtin_op.go index e15d1730c8cd9..2c44dd15994ff 100644 --- a/expression/builtin_op.go +++ b/pkg/expression/builtin_op.go @@ -20,11 +20,11 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_op_test.go b/pkg/expression/builtin_op_test.go similarity index 98% rename from expression/builtin_op_test.go rename to pkg/expression/builtin_op_test.go index b0f25fdbbca7e..e3cb53d605ca8 100644 --- a/expression/builtin_op_test.go +++ b/pkg/expression/builtin_op_test.go @@ -19,11 +19,11 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_op_vec.go b/pkg/expression/builtin_op_vec.go similarity index 99% rename from expression/builtin_op_vec.go rename to pkg/expression/builtin_op_vec.go index f2c8f7aaebf96..6c95f3ac08035 100644 --- a/expression/builtin_op_vec.go +++ b/pkg/expression/builtin_op_vec.go @@ -18,9 +18,9 @@ import ( "fmt" "math" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func (*builtinTimeIsNullSig) vectorized() bool { diff --git a/expression/builtin_op_vec_test.go b/pkg/expression/builtin_op_vec_test.go similarity index 97% rename from expression/builtin_op_vec_test.go rename to pkg/expression/builtin_op_vec_test.go index 80673d8c79342..4b80ab3fe48f1 100644 --- a/expression/builtin_op_vec_test.go +++ b/pkg/expression/builtin_op_vec_test.go @@ -18,11 +18,11 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_other.go b/pkg/expression/builtin_other.go similarity index 99% rename from expression/builtin_other.go rename to pkg/expression/builtin_other.go index 7a0f47bd0f00e..16065e20429ed 100644 --- a/expression/builtin_other.go +++ b/pkg/expression/builtin_other.go @@ -19,15 +19,15 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_other_test.go b/pkg/expression/builtin_other_test.go similarity index 97% rename from expression/builtin_other_test.go rename to pkg/expression/builtin_other_test.go index 66b201ba77e89..e6e9a9e13a184 100644 --- a/expression/builtin_other_test.go +++ b/pkg/expression/builtin_other_test.go @@ -19,13 +19,13 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_other_vec.go b/pkg/expression/builtin_other_vec.go similarity index 98% rename from expression/builtin_other_vec.go rename to pkg/expression/builtin_other_vec.go index 4517bee124df0..b7ff50aea532e 100644 --- a/expression/builtin_other_vec.go +++ b/pkg/expression/builtin_other_vec.go @@ -18,9 +18,9 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/stringutil" ) func (b *builtinValuesIntSig) vectorized() bool { diff --git a/expression/builtin_other_vec_generated.go b/pkg/expression/builtin_other_vec_generated.go similarity index 98% rename from expression/builtin_other_vec_generated.go rename to pkg/expression/builtin_other_vec_generated.go index 25f872b26e40e..80fe9c7817a85 100644 --- a/expression/builtin_other_vec_generated.go +++ b/pkg/expression/builtin_other_vec_generated.go @@ -19,10 +19,10 @@ package expression import ( "cmp" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" ) func (b *builtinInIntSig) vecEvalInt(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_other_vec_generated_test.go b/pkg/expression/builtin_other_vec_generated_test.go similarity index 98% rename from expression/builtin_other_vec_generated_test.go rename to pkg/expression/builtin_other_vec_generated_test.go index 6b4e4dbd9ed71..1a691af108030 100644 --- a/expression/builtin_other_vec_generated_test.go +++ b/pkg/expression/builtin_other_vec_generated_test.go @@ -23,9 +23,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) type inGener struct { diff --git a/expression/builtin_other_vec_test.go b/pkg/expression/builtin_other_vec_test.go similarity index 94% rename from expression/builtin_other_vec_test.go rename to pkg/expression/builtin_other_vec_test.go index 4527838c4e981..1a88cfaaef290 100644 --- a/expression/builtin_other_vec_test.go +++ b/pkg/expression/builtin_other_vec_test.go @@ -19,10 +19,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_regexp.go b/pkg/expression/builtin_regexp.go similarity index 99% rename from expression/builtin_regexp.go rename to pkg/expression/builtin_regexp.go index 322e3ea02b11b..80b062ae99c76 100644 --- a/expression/builtin_regexp.go +++ b/pkg/expression/builtin_regexp.go @@ -22,14 +22,14 @@ import ( "sync" "unicode/utf8" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/builtin_regexp_test.go b/pkg/expression/builtin_regexp_test.go similarity index 99% rename from expression/builtin_regexp_test.go rename to pkg/expression/builtin_regexp_test.go index c96ed76fab191..4b1157fcc79a5 100644 --- a/expression/builtin_regexp_test.go +++ b/pkg/expression/builtin_regexp_test.go @@ -18,13 +18,13 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_regexp_util.go b/pkg/expression/builtin_regexp_util.go similarity index 98% rename from expression/builtin_regexp_util.go rename to pkg/expression/builtin_regexp_util.go index 6eba9a84fc508..0de911d276d3d 100644 --- a/expression/builtin_regexp_util.go +++ b/pkg/expression/builtin_regexp_util.go @@ -17,7 +17,7 @@ package expression import ( "regexp" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/util/chunk" ) // memorized regexp means the constant pattern. diff --git a/expression/builtin_regexp_vec_const_test.go b/pkg/expression/builtin_regexp_vec_const_test.go similarity index 94% rename from expression/builtin_regexp_vec_const_test.go rename to pkg/expression/builtin_regexp_vec_const_test.go index 7362d148c35dd..cdf353dca6e95 100644 --- a/expression/builtin_regexp_vec_const_test.go +++ b/pkg/expression/builtin_regexp_vec_const_test.go @@ -18,11 +18,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_string.go b/pkg/expression/builtin_string.go similarity index 99% rename from expression/builtin_string.go rename to pkg/expression/builtin_string.go index 0c456b4272fcb..d371730b0620d 100644 --- a/expression/builtin_string.go +++ b/pkg/expression/builtin_string.go @@ -29,16 +29,16 @@ import ( "unicode/utf8" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/expression/builtin_string_test.go b/pkg/expression/builtin_string_test.go similarity index 99% rename from expression/builtin_string_test.go rename to pkg/expression/builtin_string_test.go index 70eb63d00d143..286f8c3385cc0 100644 --- a/expression/builtin_string_test.go +++ b/pkg/expression/builtin_string_test.go @@ -22,17 +22,17 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_string_vec.go b/pkg/expression/builtin_string_vec.go similarity index 99% rename from expression/builtin_string_vec.go rename to pkg/expression/builtin_string_vec.go index 69ac303a01b59..990b3da9c5c1d 100644 --- a/expression/builtin_string_vec.go +++ b/pkg/expression/builtin_string_vec.go @@ -24,13 +24,13 @@ import ( "strings" "unicode/utf8" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" ) //revive:disable:defer diff --git a/expression/builtin_string_vec_generated.go b/pkg/expression/builtin_string_vec_generated.go similarity index 98% rename from expression/builtin_string_vec_generated.go rename to pkg/expression/builtin_string_vec_generated.go index 8c016c04dc123..5d91ff2892e18 100644 --- a/expression/builtin_string_vec_generated.go +++ b/pkg/expression/builtin_string_vec_generated.go @@ -17,7 +17,7 @@ package expression import ( - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/util/chunk" ) // vecEvalInt evals FIELD(str,str1,str2,str3,...). diff --git a/expression/builtin_string_vec_generated_test.go b/pkg/expression/builtin_string_vec_generated_test.go similarity index 95% rename from expression/builtin_string_vec_generated_test.go rename to pkg/expression/builtin_string_vec_generated_test.go index 010f657c378d1..cbb989d970bd3 100644 --- a/expression/builtin_string_vec_generated_test.go +++ b/pkg/expression/builtin_string_vec_generated_test.go @@ -19,8 +19,8 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecGeneratedBuiltinStringCases = map[string][]vecExprBenchCase{ diff --git a/expression/builtin_string_vec_test.go b/pkg/expression/builtin_string_vec_test.go similarity index 99% rename from expression/builtin_string_vec_test.go rename to pkg/expression/builtin_string_vec_test.go index 88b786fb0fa84..32aca5e9c6624 100644 --- a/expression/builtin_string_vec_test.go +++ b/pkg/expression/builtin_string_vec_test.go @@ -18,10 +18,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) type randSpaceStrGener struct { diff --git a/expression/builtin_test.go b/pkg/expression/builtin_test.go similarity index 94% rename from expression/builtin_test.go rename to pkg/expression/builtin_test.go index 682ae03a3e849..edcfc631fc3e4 100644 --- a/expression/builtin_test.go +++ b/pkg/expression/builtin_test.go @@ -19,14 +19,14 @@ import ( "sync" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_time.go b/pkg/expression/builtin_time.go similarity index 99% rename from expression/builtin_time.go rename to pkg/expression/builtin_time.go index 4f9e4d5ac1a6d..5af1a08e1be25 100644 --- a/expression/builtin_time.go +++ b/pkg/expression/builtin_time.go @@ -29,18 +29,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/parser" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/parser" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" diff --git a/expression/builtin_time_test.go b/pkg/expression/builtin_time_test.go similarity index 99% rename from expression/builtin_time_test.go rename to pkg/expression/builtin_time_test.go index 034c9d213461c..456477b2b8961 100644 --- a/expression/builtin_time_test.go +++ b/pkg/expression/builtin_time_test.go @@ -24,17 +24,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) @@ -3155,7 +3155,7 @@ func TestTiDBBoundedStaleness(t *testing.T) { fc := funcs[ast.TiDBBoundedStaleness] for _, test := range tests { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", fmt.Sprintf("return(%v)", test.injectSafeTS))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", fmt.Sprintf("return(%v)", test.injectSafeTS))) f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(test.leftTime), types.NewDatum(test.rightTime)})) require.NoError(t, err) d, err := evalBuiltinFunc(f, chunk.Row{}) @@ -3173,7 +3173,7 @@ func TestTiDBBoundedStaleness(t *testing.T) { // Test whether it's deterministic. safeTime1 := t2.Add(-1 * time.Second) safeTS1 := oracle.GoTimeToTS(safeTime1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", fmt.Sprintf("return(%v)", safeTS1))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", fmt.Sprintf("return(%v)", safeTS1))) f, err := fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(t1Str), types.NewDatum(t2Str)})) require.NoError(t, err) d, err := evalBuiltinFunc(f, chunk.Row{}) @@ -3185,7 +3185,7 @@ func TestTiDBBoundedStaleness(t *testing.T) { // SafeTS updated. safeTime2 := t2.Add(1 * time.Second) safeTS2 := oracle.GoTimeToTS(safeTime2) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectSafeTS", fmt.Sprintf("return(%v)", safeTS2))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", fmt.Sprintf("return(%v)", safeTS2))) f, err = fc.getFunction(ctx, datumsToConstants([]types.Datum{types.NewDatum(t1Str), types.NewDatum(t2Str)})) require.NoError(t, err) d, err = evalBuiltinFunc(f, chunk.Row{}) @@ -3193,7 +3193,7 @@ func TestTiDBBoundedStaleness(t *testing.T) { // Still safeTime1 require.Equal(t, safeTime1.Format(types.TimeFormat), resultTime) resetStmtContext(ctx) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectSafeTS")) } func TestGetIntervalFromDecimal(t *testing.T) { diff --git a/expression/builtin_time_vec.go b/pkg/expression/builtin_time_vec.go similarity index 99% rename from expression/builtin_time_vec.go rename to pkg/expression/builtin_time_vec.go index 34f3fb87f14c0..0ff70bf96e7c6 100644 --- a/expression/builtin_time_vec.go +++ b/pkg/expression/builtin_time_vec.go @@ -22,11 +22,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/tikv/client-go/v2/oracle" ) diff --git a/expression/builtin_time_vec_generated.go b/pkg/expression/builtin_time_vec_generated.go similarity index 99% rename from expression/builtin_time_vec_generated.go rename to pkg/expression/builtin_time_vec_generated.go index 4e897affc66d9..a40cdb3909554 100644 --- a/expression/builtin_time_vec_generated.go +++ b/pkg/expression/builtin_time_vec_generated.go @@ -17,10 +17,10 @@ package expression import ( - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func (b *builtinAddDatetimeAndDurationSig) vecEvalTime(input *chunk.Chunk, result *chunk.Column) error { diff --git a/expression/builtin_time_vec_generated_test.go b/pkg/expression/builtin_time_vec_generated_test.go similarity index 99% rename from expression/builtin_time_vec_generated_test.go rename to pkg/expression/builtin_time_vec_generated_test.go index 12a83d21879c5..21e249e7e46ae 100644 --- a/expression/builtin_time_vec_generated_test.go +++ b/pkg/expression/builtin_time_vec_generated_test.go @@ -20,9 +20,9 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) type gener struct { diff --git a/expression/builtin_time_vec_test.go b/pkg/expression/builtin_time_vec_test.go similarity index 99% rename from expression/builtin_time_vec_test.go rename to pkg/expression/builtin_time_vec_test.go index 9ea1132066399..b7a1d2f3ca40e 100644 --- a/expression/builtin_time_vec_test.go +++ b/pkg/expression/builtin_time_vec_test.go @@ -19,11 +19,11 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/builtin_vectorized.go b/pkg/expression/builtin_vectorized.go similarity index 96% rename from expression/builtin_vectorized.go rename to pkg/expression/builtin_vectorized.go index fff63192d0bc2..5ea94d6aa41b6 100644 --- a/expression/builtin_vectorized.go +++ b/pkg/expression/builtin_vectorized.go @@ -19,9 +19,9 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // columnBufferAllocator is used to allocate and release column buffer in vectorized evaluation. diff --git a/expression/builtin_vectorized_test.go b/pkg/expression/builtin_vectorized_test.go similarity index 99% rename from expression/builtin_vectorized_test.go rename to pkg/expression/builtin_vectorized_test.go index 38b3a945ebb15..d5921a2ffc43b 100644 --- a/expression/builtin_vectorized_test.go +++ b/pkg/expression/builtin_vectorized_test.go @@ -22,10 +22,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/chunk_executor.go b/pkg/expression/chunk_executor.go similarity index 98% rename from expression/chunk_executor.go rename to pkg/expression/chunk_executor.go index 3f3a995a0b131..92f260f7e7368 100644 --- a/expression/chunk_executor.go +++ b/pkg/expression/chunk_executor.go @@ -15,11 +15,11 @@ package expression import ( - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // Vectorizable checks whether a list of expressions can employ vectorized execution. diff --git a/expression/collation.go b/pkg/expression/collation.go similarity index 98% rename from expression/collation.go rename to pkg/expression/collation.go index ccc8e248fd68c..1d771b069d5d5 100644 --- a/expression/collation.go +++ b/pkg/expression/collation.go @@ -17,15 +17,15 @@ package expression import ( goatomic "sync/atomic" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/atomic" ) diff --git a/pkg/expression/collation_test.go b/pkg/expression/collation_test.go new file mode 100644 index 0000000000000..2414e3013049f --- /dev/null +++ b/pkg/expression/collation_test.go @@ -0,0 +1,765 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func newExpression(coercibility Coercibility, repertoire Repertoire, chs, coll string) Expression { + constant := &Constant{RetType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetCharset(chs).SetCollate(coll).BuildP()} + constant.SetCoercibility(coercibility) + constant.SetRepertoire(repertoire) + return constant +} + +func TestInferCollation(t *testing.T) { + tests := []struct { + exprs []Expression + err bool + ec *ExprCollation + }{ + // same charset. + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + false, + &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + false, + &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + false, + &ExprCollation{CoercibilityNone, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + // binary charset with non-binary charset. + { + []Expression{ + newExpression(CoercibilityNumeric, UNICODE, charset.CharsetBin, charset.CollationBin), + newExpression(CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + false, + &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []Expression{ + newExpression(CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newExpression(CoercibilityNumeric, UNICODE, charset.CharsetBin, charset.CollationBin), + }, + false, + &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin), + }, + false, + &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin}, + }, + // different charset, one of them is utf8mb4 + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + false, + &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"}, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + true, + nil, + }, + // different charset, one of them is CoercibilityCoercible + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityCoercible, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin}, + }, + { + []Expression{ + newExpression(CoercibilityCoercible, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1}, + }, + // different charset, one of them is ASCII + { + []Expression{ + newExpression(CoercibilityImplicit, ASCII, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1}, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, ASCII, charset.CharsetLatin1, charset.CollationLatin1), + }, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin}, + }, + // 3 expressions. + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetBin, charset.CollationBin), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + true, + nil, + }, + { + []Expression{ + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetGBK, charset.CollationGBKBin), + newExpression(CoercibilityExplicit, UNICODE, charset.CharsetLatin1, charset.CollationLatin1), + newExpression(CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + true, + nil, + }, + } + + for i, test := range tests { + ec := inferCollation(test.exprs...) + if test.err { + require.Nil(t, ec, i) + } else { + require.Equal(t, test.ec, ec, i) + } + } +} + +func newConstString(s string, coercibility Coercibility, chs, coll string) *Constant { + repe := ASCII + for i := 0; i < len(s); i++ { + if s[i] >= 0x80 { + repe = UNICODE + } + } + constant := &Constant{RetType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetCharset(chs).SetCollate(coll).BuildP(), Value: types.NewDatum(s)} + constant.SetCoercibility(coercibility) + constant.SetRepertoire(repe) + return constant +} + +func newColString(chs, coll string) *Column { + ft := types.FieldType{} + ft.SetType(mysql.TypeString) + ft.SetCharset(chs) + ft.SetCollate(coll) + column := &Column{RetType: &ft} + column.SetCoercibility(CoercibilityImplicit) + column.SetRepertoire(UNICODE) + if chs == charset.CharsetASCII { + column.SetRepertoire(ASCII) + } + return column +} + +func newColJSON() *Column { + ft := types.FieldType{} + ft.SetType(mysql.TypeJSON) + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + column := &Column{RetType: &ft} + return column +} + +func newConstInt(coercibility Coercibility) *Constant { + ft := types.FieldType{} + ft.SetType(mysql.TypeLong) + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + constant := &Constant{RetType: &ft, Value: types.NewDatum(1)} + constant.SetCoercibility(coercibility) + constant.SetRepertoire(ASCII) + return constant +} + +func newColInt(coercibility Coercibility) *Column { + ft := types.FieldType{} + ft.SetType(mysql.TypeLong) + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + column := &Column{RetType: &ft} + column.SetCoercibility(coercibility) + column.SetRepertoire(ASCII) + return column +} + +func TestDeriveCollation(t *testing.T) { + ctx := mock.NewContext() + tests := []struct { + fcs []string + args []Expression + argTps []types.EvalType + retTp types.EvalType + + err bool + ec *ExprCollation + }{ + { + []string{ast.Left, ast.Right, ast.Repeat, ast.Substr, ast.Substring, ast.Mid}, + []Expression{ + newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + }, + []types.EvalType{types.ETString, types.ETInt}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.Trim, ast.LTrim, ast.RTrim}, + []Expression{ + newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + []types.EvalType{types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.SubstringIndex}, + []Expression{ + newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), + newConstInt(CoercibilityExplicit), + }, + []types.EvalType{types.ETString, types.ETString, types.ETInt}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.Replace, ast.Translate}, + []Expression{ + newConstString("a", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), + newConstString("ㅂ", CoercibilityExplicit, charset.CharsetBin, charset.CollationBin), + }, + []types.EvalType{types.ETString, types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.InsertFunc}, + []Expression{ + newConstString("a", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + newConstInt(CoercibilityExplicit), + newConstString("ㅂ", CoercibilityExplicit, charset.CharsetBin, charset.CollationBin), + }, + []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityExplicit, UNICODE, charset.CharsetBin, charset.CollationBin}, + }, + { + []string{ast.InsertFunc}, + []Expression{ + newConstString("a", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + newConstInt(CoercibilityExplicit), + newConstString("啊", CoercibilityImplicit, charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.InsertFunc}, + []Expression{ + newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + newConstInt(CoercibilityExplicit), + newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETInt, types.ETInt, types.ETString}, + types.ETString, + true, + nil, + }, + { + []string{ast.Lpad, ast.Rpad}, + []Expression{ + newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + newConstString("啊", CoercibilityExplicit, charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETInt, types.ETString}, + types.ETString, + true, + nil, + }, + { + []string{ast.Lpad, ast.Rpad}, + []Expression{ + newConstString("ㅂ", CoercibilityImplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstInt(CoercibilityExplicit), + newConstString("啊", CoercibilityImplicit, charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETInt, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.FindInSet, ast.Regexp}, + []Expression{ + newColString(charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETString, types.ETString}, + types.ETInt, + true, + nil, + }, + { + []string{ast.Field}, + []Expression{ + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETString, types.ETString}, + types.ETInt, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.Field}, + []Expression{ + newColInt(CoercibilityImplicit), + newColInt(CoercibilityImplicit), + }, + []types.EvalType{types.ETInt, types.ETInt}, + types.ETInt, + false, + &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, + }, + { + []string{ast.Locate, ast.Instr, ast.Position}, + []Expression{ + newColInt(CoercibilityNumeric), + newColInt(CoercibilityNumeric), + }, + []types.EvalType{types.ETInt, types.ETInt}, + types.ETInt, + false, + &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, + }, + { + []string{ast.Format, ast.SHA2}, + []Expression{ + newColInt(CoercibilityNumeric), + newColInt(CoercibilityNumeric), + }, + []types.EvalType{types.ETInt, types.ETInt}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.Space, ast.ToBase64, ast.UUID, ast.Hex, ast.MD5, ast.SHA}, + []Expression{ + newColInt(CoercibilityNumeric), + }, + []types.EvalType{types.ETInt}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.GE, ast.LE, ast.GT, ast.LT, ast.EQ, ast.NE, ast.NullEQ, ast.Strcmp}, + []Expression{ + newColString(charset.CharsetASCII, charset.CollationASCII), + newColString(charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETString}, + types.ETInt, + false, + &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetGBK, charset.CollationGBKBin}, + }, + { + []string{ast.GE, ast.LE, ast.GT, ast.LT, ast.EQ, ast.NE, ast.NullEQ, ast.Strcmp}, + []Expression{ + newColString(charset.CharsetLatin1, charset.CollationLatin1), + newColString(charset.CharsetGBK, charset.CollationGBKBin), + }, + []types.EvalType{types.ETString, types.ETString}, + types.ETInt, + true, + nil, + }, + { + []string{ast.Bin, ast.FromBase64, ast.Oct, ast.Unhex, ast.WeightString}, + []Expression{ + newColString(charset.CharsetLatin1, charset.CollationLatin1), + }, + []types.EvalType{types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ast.ASCII, ast.BitLength, ast.CharLength, ast.CharacterLength, ast.Length, ast.OctetLength, ast.Ord}, + []Expression{ + newColString(charset.CharsetLatin1, charset.CollationLatin1), + }, + []types.EvalType{types.ETString}, + types.ETInt, + false, + &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetBin, charset.CollationBin}, + }, + { + []string{ + ast.ExportSet, ast.Elt, ast.MakeSet, + }, + []Expression{ + newColInt(CoercibilityExplicit), + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETInt, types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.ExportSet, ast.Elt, ast.MakeSet, + }, + []Expression{ + newColInt(CoercibilityExplicit), + newColJSON(), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETInt, types.ETJson}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Concat, ast.ConcatWS, ast.Coalesce, ast.Greatest, ast.Least, + }, + []Expression{ + newColString(charset.CharsetGBK, charset.CollationGBKBin), + newColJSON(), + }, + []types.EvalType{types.ETString, types.ETJson}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Concat, ast.ConcatWS, ast.Coalesce, ast.Greatest, ast.Least, + }, + []Expression{ + newColJSON(), + newColString(charset.CharsetBin, charset.CharsetBin), + }, + []types.EvalType{types.ETJson, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetBin, charset.CharsetBin}, + }, + { + []string{ + ast.Concat, ast.ConcatWS, ast.Coalesce, ast.In, ast.Greatest, ast.Least, + }, + []Expression{ + newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETInt, types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Lower, ast.Lcase, ast.Reverse, ast.Upper, ast.Ucase, ast.Quote, + }, + []Expression{ + newConstString("a", CoercibilityCoercible, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + []types.EvalType{types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Lower, ast.Lcase, ast.Reverse, ast.Upper, ast.Ucase, ast.Quote, + }, + []Expression{ + newColJSON(), + }, + []types.EvalType{types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.If, + }, + []Expression{ + newColInt(CoercibilityExplicit), + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETInt, types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Ifnull, + }, + []Expression{ + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newColString(charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityImplicit, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Like, + }, + []Expression{ + newColString(charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstString("like", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + newConstString("\\", CoercibilityExplicit, charset.CharsetUTF8MB4, charset.CollationUTF8MB4), + }, + []types.EvalType{types.ETString, types.ETString, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityNumeric, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.DateFormat, ast.TimeFormat, + }, + []Expression{ + newConstString("2020-02-02", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newConstString("%Y %M %D", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETDatetime, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.DateFormat, ast.TimeFormat, + }, + []Expression{ + newConstString("2020-02-02", CoercibilityExplicit, charset.CharsetUTF8MB4, "utf8mb4_general_ci"), + newConstString("%Y %M %D", CoercibilityCoercible, charset.CharsetUTF8MB4, "utf8mb4_unicode_ci"), + }, + []types.EvalType{types.ETDatetime, types.ETString}, + types.ETString, + false, + &ExprCollation{CoercibilityCoercible, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Database, ast.User, ast.CurrentUser, ast.Version, ast.CurrentRole, ast.TiDBVersion, ast.CurrentResourceGroup, + }, + []Expression{}, + []types.EvalType{}, + types.ETString, + false, + &ExprCollation{CoercibilitySysconst, UNICODE, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + { + []string{ + ast.Cast, + }, + []Expression{ + newColInt(CoercibilityExplicit), + }, + []types.EvalType{types.ETInt}, + types.ETString, + false, + &ExprCollation{CoercibilityExplicit, ASCII, charset.CharsetUTF8MB4, charset.CollationUTF8MB4}, + }, + } + + for i, test := range tests { + for _, fc := range test.fcs { + ec, err := deriveCollation(ctx, fc, test.args, test.retTp, test.argTps...) + if test.err { + require.Error(t, err, "Number: %d, function: %s", i, fc) + require.Nil(t, ec, i) + } else { + require.Equal(t, test.ec, ec, "Number: %d, function: %s", i, fc) + } + } + } +} + +func TestCompareString(t *testing.T) { + require.Equal(t, 0, types.CompareString("a", "A", "utf8_general_ci")) + require.Equal(t, 0, types.CompareString("À", "A", "utf8_general_ci")) + require.Equal(t, 0, types.CompareString("😜", "😃", "utf8_general_ci")) + require.Equal(t, 0, types.CompareString("a ", "a ", "utf8_general_ci")) + require.Equal(t, 0, types.CompareString("ß", "s", "utf8_general_ci")) + require.NotEqual(t, 0, types.CompareString("ß", "ss", "utf8_general_ci")) + + require.Equal(t, 0, types.CompareString("a", "A", "utf8_unicode_ci")) + require.Equal(t, 0, types.CompareString("À", "A", "utf8_unicode_ci")) + require.Equal(t, 0, types.CompareString("😜", "😃", "utf8_unicode_ci")) + require.Equal(t, 0, types.CompareString("a ", "a ", "utf8_unicode_ci")) + require.NotEqual(t, 0, types.CompareString("ß", "s", "utf8_unicode_ci")) + require.Equal(t, 0, types.CompareString("ß", "ss", "utf8_unicode_ci")) + + require.Equal(t, 0, types.CompareString("a", "A", "utf8mb4_0900_ai_ci")) + require.Equal(t, 0, types.CompareString("À", "A", "utf8mb4_0900_ai_ci")) + require.NotEqual(t, 0, types.CompareString("😜", "😃", "utf8mb4_0900_ai_ci")) + require.NotEqual(t, 0, types.CompareString("a ", "a ", "utf8mb4_0900_ai_ci")) + require.NotEqual(t, 0, types.CompareString("ß", "s", "utf8mb4_0900_ai_ci")) + require.Equal(t, 0, types.CompareString("ß", "ss", "utf8mb4_0900_ai_ci")) + require.NotEqual(t, 0, types.CompareString("\U000FFFFE", "\U000FFFFF", "utf8mb4_0900_ai_ci")) + require.Equal(t, 0, types.CompareString("æ", "ae", "utf8mb4_0900_ai_ci")) + + require.NotEqual(t, 0, types.CompareString("a", "A", "binary")) + require.NotEqual(t, 0, types.CompareString("À", "A", "binary")) + require.NotEqual(t, 0, types.CompareString("😜", "😃", "binary")) + require.NotEqual(t, 0, types.CompareString("a ", "a ", "binary")) + + ctx := mock.NewContext() + ft := types.NewFieldType(mysql.TypeVarString) + col1 := &Column{ + RetType: ft, + Index: 0, + } + col2 := &Column{ + RetType: ft, + Index: 1, + } + chk := chunk.NewChunkWithCapacity([]*types.FieldType{ft, ft}, 4) + chk.Column(0).AppendString("a") + chk.Column(1).AppendString("A") + chk.Column(0).AppendString("À") + chk.Column(1).AppendString("A") + chk.Column(0).AppendString("😜") + chk.Column(1).AppendString("😃") + chk.Column(0).AppendString("a ") + chk.Column(1).AppendString("a ") + for i := 0; i < 4; i++ { + v, isNull, err := CompareStringWithCollationInfo(ctx, col1, col2, chk.GetRow(0), chk.GetRow(0), "utf8_general_ci") + require.NoError(t, err) + require.False(t, isNull) + require.Equal(t, int64(0), v) + } +} diff --git a/pkg/expression/column.go b/pkg/expression/column.go new file mode 100644 index 0000000000000..95e3bd513a228 --- /dev/null +++ b/pkg/expression/column.go @@ -0,0 +1,790 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "cmp" + "fmt" + "slices" + "strings" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/size" +) + +// CorrelatedColumn stands for a column in a correlated sub query. +type CorrelatedColumn struct { + Column + + Data *types.Datum +} + +// Clone implements Expression interface. +func (col *CorrelatedColumn) Clone() Expression { + return &CorrelatedColumn{ + Column: col.Column, + Data: col.Data, + } +} + +// VecEvalInt evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETInt, input, result) +} + +// VecEvalReal evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETReal, input, result) +} + +// VecEvalString evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETString, input, result) +} + +// VecEvalDecimal evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETDecimal, input, result) +} + +// VecEvalTime evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETTimestamp, input, result) +} + +// VecEvalDuration evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETDuration, input, result) +} + +// VecEvalJSON evaluates this expression in a vectorized manner. +func (col *CorrelatedColumn) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return genVecFromConstExpr(ctx, col, types.ETJson, input, result) +} + +// Traverse implements the TraverseDown interface. +func (col *CorrelatedColumn) Traverse(action TraverseAction) Expression { + return action.Transform(col) +} + +// Eval implements Expression interface. +func (col *CorrelatedColumn) Eval(row chunk.Row) (types.Datum, error) { + return *col.Data, nil +} + +// EvalInt returns int representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + if col.Data.IsNull() { + return 0, true, nil + } + if col.GetType().Hybrid() { + res, err := col.Data.ToInt64(ctx.GetSessionVars().StmtCtx) + return res, err != nil, err + } + return col.Data.GetInt64(), false, nil +} + +// EvalReal returns real representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { + if col.Data.IsNull() { + return 0, true, nil + } + return col.Data.GetFloat64(), false, nil +} + +// EvalString returns string representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { + if col.Data.IsNull() { + return "", true, nil + } + res, err := col.Data.ToString() + return res, err != nil, err +} + +// EvalDecimal returns decimal representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { + if col.Data.IsNull() { + return nil, true, nil + } + return col.Data.GetMysqlDecimal(), false, nil +} + +// EvalTime returns DATE/DATETIME/TIMESTAMP representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalTime(ctx sessionctx.Context, row chunk.Row) (types.Time, bool, error) { + if col.Data.IsNull() { + return types.ZeroTime, true, nil + } + return col.Data.GetMysqlTime(), false, nil +} + +// EvalDuration returns Duration representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalDuration(ctx sessionctx.Context, row chunk.Row) (types.Duration, bool, error) { + if col.Data.IsNull() { + return types.Duration{}, true, nil + } + return col.Data.GetMysqlDuration(), false, nil +} + +// EvalJSON returns JSON representation of CorrelatedColumn. +func (col *CorrelatedColumn) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { + if col.Data.IsNull() { + return types.BinaryJSON{}, true, nil + } + return col.Data.GetMysqlJSON(), false, nil +} + +// Equal implements Expression interface. +func (col *CorrelatedColumn) Equal(ctx sessionctx.Context, expr Expression) bool { + if cc, ok := expr.(*CorrelatedColumn); ok { + return col.Column.Equal(ctx, &cc.Column) + } + return false +} + +// IsCorrelated implements Expression interface. +func (col *CorrelatedColumn) IsCorrelated() bool { + return true +} + +// ConstItem implements Expression interface. +func (col *CorrelatedColumn) ConstItem(_ *stmtctx.StatementContext) bool { + return false +} + +// Decorrelate implements Expression interface. +func (col *CorrelatedColumn) Decorrelate(schema *Schema) Expression { + if !schema.Contains(&col.Column) { + return col + } + return &col.Column +} + +// ResolveIndices implements Expression interface. +func (col *CorrelatedColumn) ResolveIndices(_ *Schema) (Expression, error) { + return col, nil +} + +func (col *CorrelatedColumn) resolveIndices(_ *Schema) error { + return nil +} + +// ResolveIndicesByVirtualExpr implements Expression interface. +func (col *CorrelatedColumn) ResolveIndicesByVirtualExpr(_ *Schema) (Expression, bool) { + return col, true +} + +func (col *CorrelatedColumn) resolveIndicesByVirtualExpr(_ *Schema) bool { + return true +} + +// MemoryUsage return the memory usage of CorrelatedColumn +func (col *CorrelatedColumn) MemoryUsage() (sum int64) { + if col == nil { + return + } + + sum = col.Column.MemoryUsage() + size.SizeOfPointer + if col.Data != nil { + sum += col.Data.MemUsage() + } + return sum +} + +// RemapColumn remaps columns with provided mapping and returns new expression +func (col *CorrelatedColumn) RemapColumn(m map[int64]*Column) (Expression, error) { + mapped := m[(&col.Column).UniqueID] + if mapped == nil { + return nil, errors.Errorf("Can't remap column for %s", col) + } + return &CorrelatedColumn{ + Column: *mapped, + Data: col.Data, + }, nil +} + +// Column represents a column. +type Column struct { + RetType *types.FieldType + // ID is used to specify whether this column is ExtraHandleColumn or to access histogram. + // We'll try to remove it in the future. + ID int64 + // UniqueID is the unique id of this column. + UniqueID int64 + + // Index is used for execution, to tell the column's position in the given row. + Index int + + hashcode []byte + + // VirtualExpr is used to save expression for virtual column + VirtualExpr Expression + + OrigName string + IsHidden bool + + // IsPrefix indicates whether this column is a prefix column in index. + // + // for example: + // pk(col1, col2), index(col1(10)), key: col1(10)_col1_col2 => index's col1 will be true + // pk(col1(10), col2), index(col1), key: col1_col1(10)_col2 => pk's col1 will be true + IsPrefix bool + + // InOperand indicates whether this column is the inner operand of column equal condition converted + // from `[not] in (subq)`. + InOperand bool + + collationInfo + + CorrelatedColUniqueID int64 +} + +// Equal implements Expression interface. +func (col *Column) Equal(_ sessionctx.Context, expr Expression) bool { + if newCol, ok := expr.(*Column); ok { + return newCol.UniqueID == col.UniqueID + } + return false +} + +// EqualByExprAndID extends Equal by comparing virual expression +func (col *Column) EqualByExprAndID(_ sessionctx.Context, expr Expression) bool { + if newCol, ok := expr.(*Column); ok { + expr, isOk := col.VirtualExpr.(*ScalarFunction) + isVirExprMatched := isOk && expr.Equal(nil, newCol.VirtualExpr) && col.RetType.Equal(newCol.RetType) + return (newCol.UniqueID == col.UniqueID) || isVirExprMatched + } + return false +} + +// VecEvalInt evaluates this expression in a vectorized manner. +func (col *Column) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if col.RetType.Hybrid() { + it := chunk.NewIterator4Chunk(input) + result.ResizeInt64(0, false) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, null, err := col.EvalInt(ctx, row) + if err != nil { + return err + } + if null { + result.AppendNull() + } else { + result.AppendInt64(v) + } + } + return nil + } + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalReal evaluates this expression in a vectorized manner. +func (col *Column) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + n := input.NumRows() + src := input.Column(col.Index) + if col.GetType().GetType() == mysql.TypeFloat { + result.ResizeFloat64(n, false) + f32s := src.Float32s() + f64s := result.Float64s() + sel := input.Sel() + if sel != nil { + for i, j := range sel { + if src.IsNull(j) { + result.SetNull(i, true) + } else { + f64s[i] = float64(f32s[j]) + } + } + return nil + } + result.MergeNulls(src) + for i := range f32s { + if result.IsNull(i) { + continue + } + f64s[i] = float64(f32s[i]) + } + return nil + } + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalString evaluates this expression in a vectorized manner. +func (col *Column) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if col.RetType.Hybrid() { + it := chunk.NewIterator4Chunk(input) + result.ReserveString(input.NumRows()) + for row := it.Begin(); row != it.End(); row = it.Next() { + v, null, err := col.EvalString(ctx, row) + if err != nil { + return err + } + if null { + result.AppendNull() + } else { + result.AppendString(v) + } + } + return nil + } + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalDecimal evaluates this expression in a vectorized manner. +func (col *Column) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalTime evaluates this expression in a vectorized manner. +func (col *Column) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalDuration evaluates this expression in a vectorized manner. +func (col *Column) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +// VecEvalJSON evaluates this expression in a vectorized manner. +func (col *Column) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + input.Column(col.Index).CopyReconstruct(input.Sel(), result) + return nil +} + +const columnPrefix = "Column#" + +// String implements Stringer interface. +func (col *Column) String() string { + if col.IsHidden { + // A hidden column must be a virtual generated column, we should output its expression. + return col.VirtualExpr.String() + } + if col.OrigName != "" { + return col.OrigName + } + var builder strings.Builder + fmt.Fprintf(&builder, "%s%d", columnPrefix, col.UniqueID) + return builder.String() +} + +// MarshalJSON implements json.Marshaler interface. +func (col *Column) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", col)), nil +} + +// GetType implements Expression interface. +func (col *Column) GetType() *types.FieldType { + return col.RetType +} + +// Traverse implements the TraverseDown interface. +func (col *Column) Traverse(action TraverseAction) Expression { + return action.Transform(col) +} + +// Eval implements Expression interface. +func (col *Column) Eval(row chunk.Row) (types.Datum, error) { + return row.GetDatum(col.Index, col.RetType), nil +} + +// EvalInt returns int representation of Column. +func (col *Column) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + if col.GetType().Hybrid() { + val := row.GetDatum(col.Index, col.RetType) + if val.IsNull() { + return 0, true, nil + } + if val.Kind() == types.KindMysqlBit { + val, err := val.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) + return int64(val), err != nil, err + } + res, err := val.ToInt64(ctx.GetSessionVars().StmtCtx) + return res, err != nil, err + } + if row.IsNull(col.Index) { + return 0, true, nil + } + return row.GetInt64(col.Index), false, nil +} + +// EvalReal returns real representation of Column. +func (col *Column) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { + if row.IsNull(col.Index) { + return 0, true, nil + } + if col.GetType().GetType() == mysql.TypeFloat { + return float64(row.GetFloat32(col.Index)), false, nil + } + return row.GetFloat64(col.Index), false, nil +} + +// EvalString returns string representation of Column. +func (col *Column) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { + if row.IsNull(col.Index) { + return "", true, nil + } + + // Specially handle the ENUM/SET/BIT input value. + if col.GetType().Hybrid() { + val := row.GetDatum(col.Index, col.RetType) + res, err := val.ToString() + return res, err != nil, err + } + + val := row.GetString(col.Index) + return val, false, nil +} + +// EvalDecimal returns decimal representation of Column. +func (col *Column) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { + if row.IsNull(col.Index) { + return nil, true, nil + } + return row.GetMyDecimal(col.Index), false, nil +} + +// EvalTime returns DATE/DATETIME/TIMESTAMP representation of Column. +func (col *Column) EvalTime(ctx sessionctx.Context, row chunk.Row) (types.Time, bool, error) { + if row.IsNull(col.Index) { + return types.ZeroTime, true, nil + } + return row.GetTime(col.Index), false, nil +} + +// EvalDuration returns Duration representation of Column. +func (col *Column) EvalDuration(ctx sessionctx.Context, row chunk.Row) (types.Duration, bool, error) { + if row.IsNull(col.Index) { + return types.Duration{}, true, nil + } + duration := row.GetDuration(col.Index, col.RetType.GetDecimal()) + return duration, false, nil +} + +// EvalJSON returns JSON representation of Column. +func (col *Column) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { + if row.IsNull(col.Index) { + return types.BinaryJSON{}, true, nil + } + return row.GetJSON(col.Index), false, nil +} + +// Clone implements Expression interface. +func (col *Column) Clone() Expression { + newCol := *col + return &newCol +} + +// IsCorrelated implements Expression interface. +func (col *Column) IsCorrelated() bool { + return false +} + +// ConstItem implements Expression interface. +func (col *Column) ConstItem(_ *stmtctx.StatementContext) bool { + return false +} + +// Decorrelate implements Expression interface. +func (col *Column) Decorrelate(_ *Schema) Expression { + return col +} + +// HashCode implements Expression interface. +func (col *Column) HashCode(_ *stmtctx.StatementContext) []byte { + if len(col.hashcode) != 0 { + return col.hashcode + } + col.hashcode = make([]byte, 0, 9) + col.hashcode = append(col.hashcode, columnFlag) + col.hashcode = codec.EncodeInt(col.hashcode, col.UniqueID) + return col.hashcode +} + +// CleanHashCode will clean the hashcode you may be cached before. It's used especially in schema-cloned & reallocated-uniqueID's cases. +func (col *Column) CleanHashCode() { + col.hashcode = make([]byte, 0, 9) +} + +// ResolveIndices implements Expression interface. +func (col *Column) ResolveIndices(schema *Schema) (Expression, error) { + newCol := col.Clone() + err := newCol.resolveIndices(schema) + return newCol, err +} + +func (col *Column) resolveIndices(schema *Schema) error { + col.Index = schema.ColumnIndex(col) + if col.Index == -1 { + return errors.Errorf("Can't find column %s in schema %s", col, schema) + } + return nil +} + +// ResolveIndicesByVirtualExpr implements Expression interface. +func (col *Column) ResolveIndicesByVirtualExpr(schema *Schema) (Expression, bool) { + newCol := col.Clone() + isOk := newCol.resolveIndicesByVirtualExpr(schema) + return newCol, isOk +} + +func (col *Column) resolveIndicesByVirtualExpr(schema *Schema) bool { + for i, c := range schema.Columns { + if c.EqualByExprAndID(nil, col) { + col.Index = i + return true + } + } + return false +} + +// RemapColumn remaps columns with provided mapping and returns new expression +func (col *Column) RemapColumn(m map[int64]*Column) (Expression, error) { + mapped := m[col.UniqueID] + if mapped == nil { + return nil, errors.Errorf("Can't remap column for %s", col) + } + return mapped, nil +} + +// Vectorized returns if this expression supports vectorized evaluation. +func (col *Column) Vectorized() bool { + return true +} + +// ToInfo converts the expression.Column to model.ColumnInfo for casting values, +// beware it doesn't fill all the fields of the model.ColumnInfo. +func (col *Column) ToInfo() *model.ColumnInfo { + return &model.ColumnInfo{ + ID: col.ID, + FieldType: *col.RetType, + } +} + +// Column2Exprs will transfer column slice to expression slice. +func Column2Exprs(cols []*Column) []Expression { + result := make([]Expression, 0, len(cols)) + for _, col := range cols { + result = append(result, col) + } + return result +} + +// ColInfo2Col finds the corresponding column of the ColumnInfo in a column slice. +func ColInfo2Col(cols []*Column, col *model.ColumnInfo) *Column { + for _, c := range cols { + if c.ID == col.ID { + return c + } + } + return nil +} + +// IndexCol2Col finds the corresponding column of the IndexColumn in a column slice. +func IndexCol2Col(colInfos []*model.ColumnInfo, cols []*Column, col *model.IndexColumn) *Column { + for i, info := range colInfos { + if info.Name.L == col.Name.L { + if col.Length > 0 && info.FieldType.GetFlen() > col.Length { + c := *cols[i] + c.IsPrefix = true + return &c + } + return cols[i] + } + } + return nil +} + +// IndexInfo2PrefixCols gets the corresponding []*Column of the indexInfo's []*IndexColumn, +// together with a []int containing their lengths. +// If this index has three IndexColumn that the 1st and 3rd IndexColumn has corresponding *Column, +// the return value will be only the 1st corresponding *Column and its length. +// TODO: Use a struct to represent {*Column, int}. And merge IndexInfo2PrefixCols and IndexInfo2Cols. +func IndexInfo2PrefixCols(colInfos []*model.ColumnInfo, cols []*Column, index *model.IndexInfo) ([]*Column, []int) { + retCols := make([]*Column, 0, len(index.Columns)) + lengths := make([]int, 0, len(index.Columns)) + for _, c := range index.Columns { + col := IndexCol2Col(colInfos, cols, c) + if col == nil { + return retCols, lengths + } + retCols = append(retCols, col) + if c.Length != types.UnspecifiedLength && c.Length == col.RetType.GetFlen() { + lengths = append(lengths, types.UnspecifiedLength) + } else { + lengths = append(lengths, c.Length) + } + } + return retCols, lengths +} + +// IndexInfo2Cols gets the corresponding []*Column of the indexInfo's []*IndexColumn, +// together with a []int containing their lengths. +// If this index has three IndexColumn that the 1st and 3rd IndexColumn has corresponding *Column, +// the return value will be [col1, nil, col2]. +func IndexInfo2Cols(colInfos []*model.ColumnInfo, cols []*Column, index *model.IndexInfo) ([]*Column, []int) { + retCols := make([]*Column, 0, len(index.Columns)) + lens := make([]int, 0, len(index.Columns)) + for _, c := range index.Columns { + col := IndexCol2Col(colInfos, cols, c) + if col == nil { + retCols = append(retCols, col) + lens = append(lens, types.UnspecifiedLength) + continue + } + retCols = append(retCols, col) + if c.Length != types.UnspecifiedLength && c.Length == col.RetType.GetFlen() { + lens = append(lens, types.UnspecifiedLength) + } else { + lens = append(lens, c.Length) + } + } + return retCols, lens +} + +// FindPrefixOfIndex will find columns in index by checking the unique id. +// So it will return at once no matching column is found. +func FindPrefixOfIndex(cols []*Column, idxColIDs []int64) []*Column { + retCols := make([]*Column, 0, len(idxColIDs)) +idLoop: + for _, id := range idxColIDs { + for _, col := range cols { + if col.UniqueID == id { + retCols = append(retCols, col) + continue idLoop + } + } + // If no matching column is found, just return. + return retCols + } + return retCols +} + +// EvalVirtualColumn evals the virtual column +func (col *Column) EvalVirtualColumn(row chunk.Row) (types.Datum, error) { + return col.VirtualExpr.Eval(row) +} + +// SupportReverseEval checks whether the builtinFunc support reverse evaluation. +func (col *Column) SupportReverseEval() bool { + switch col.RetType.GetType() { + case mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, + mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: + return true + } + return false +} + +// ReverseEval evaluates the only one column value with given function result. +func (col *Column) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { + return types.ChangeReverseResultByUpperLowerBound(sc, col.RetType, res, rType) +} + +// Coercibility returns the coercibility value which is used to check collations. +func (col *Column) Coercibility() Coercibility { + if !col.HasCoercibility() { + col.SetCoercibility(deriveCoercibilityForColumn(col)) + } + return col.collationInfo.Coercibility() +} + +// Repertoire returns the repertoire value which is used to check collations. +func (col *Column) Repertoire() Repertoire { + if col.repertoire != 0 { + return col.repertoire + } + switch col.RetType.EvalType() { + case types.ETJson: + return UNICODE + case types.ETString: + if col.RetType.GetCharset() == charset.CharsetASCII { + return ASCII + } + return UNICODE + default: + return ASCII + } +} + +// SortColumns sort columns based on UniqueID. +func SortColumns(cols []*Column) []*Column { + sorted := make([]*Column, len(cols)) + copy(sorted, cols) + slices.SortFunc(sorted, func(i, j *Column) int { + return cmp.Compare(i.UniqueID, j.UniqueID) + }) + return sorted +} + +// InColumnArray check whether the col is in the cols array +func (col *Column) InColumnArray(cols []*Column) bool { + for _, c := range cols { + if col.Equal(nil, c) { + return true + } + } + return false +} + +// GcColumnExprIsTidbShard check whether the expression is tidb_shard() +func GcColumnExprIsTidbShard(virtualExpr Expression) bool { + if virtualExpr == nil { + return false + } + + f, ok := virtualExpr.(*ScalarFunction) + if !ok { + return false + } + + if f.FuncName.L != ast.TiDBShard { + return false + } + + return true +} + +const emptyColumnSize = int64(unsafe.Sizeof(Column{})) + +// MemoryUsage return the memory usage of Column +func (col *Column) MemoryUsage() (sum int64) { + if col == nil { + return + } + + sum = emptyColumnSize + int64(cap(col.hashcode)) + int64(len(col.OrigName)+len(col.charset)+len(col.collation)) + + if col.RetType != nil { + sum += col.RetType.MemoryUsage() + } + if col.VirtualExpr != nil { + sum += col.VirtualExpr.MemoryUsage() + } + return +} diff --git a/pkg/expression/column_test.go b/pkg/expression/column_test.go new file mode 100644 index 0000000000000..1f71d8407483c --- /dev/null +++ b/pkg/expression/column_test.go @@ -0,0 +1,265 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestColumn(t *testing.T) { + ctx := mock.NewContext() + col := &Column{RetType: types.NewFieldType(mysql.TypeLonglong), UniqueID: 1} + + require.True(t, col.Equal(nil, col)) + require.False(t, col.Equal(nil, &Column{})) + require.False(t, col.IsCorrelated()) + require.True(t, col.Equal(nil, col.Decorrelate(nil))) + + marshal, err := col.MarshalJSON() + require.NoError(t, err) + require.EqualValues(t, []byte{0x22, 0x43, 0x6f, 0x6c, 0x75, 0x6d, 0x6e, 0x23, 0x31, 0x22}, marshal) + + intDatum := types.NewIntDatum(1) + corCol := &CorrelatedColumn{Column: *col, Data: &intDatum} + invalidCorCol := &CorrelatedColumn{Column: Column{}} + schema := NewSchema(&Column{UniqueID: 1}) + require.True(t, corCol.Equal(nil, corCol)) + require.False(t, corCol.Equal(nil, invalidCorCol)) + require.True(t, corCol.IsCorrelated()) + require.False(t, corCol.ConstItem(nil)) + require.True(t, corCol.Decorrelate(schema).Equal(nil, col)) + require.True(t, invalidCorCol.Decorrelate(schema).Equal(nil, invalidCorCol)) + + intCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeLonglong)}, + Data: &intDatum} + intVal, isNull, err := intCorCol.EvalInt(ctx, chunk.Row{}) + require.Equal(t, int64(1), intVal) + require.False(t, isNull) + require.NoError(t, err) + + realDatum := types.NewFloat64Datum(1.2) + realCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDouble)}, + Data: &realDatum} + realVal, isNull, err := realCorCol.EvalReal(ctx, chunk.Row{}) + require.Equal(t, float64(1.2), realVal) + require.False(t, isNull) + require.NoError(t, err) + + decimalDatum := types.NewDecimalDatum(types.NewDecFromStringForTest("1.2")) + decimalCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeNewDecimal)}, + Data: &decimalDatum} + decVal, isNull, err := decimalCorCol.EvalDecimal(ctx, chunk.Row{}) + require.Zero(t, decVal.Compare(types.NewDecFromStringForTest("1.2"))) + require.False(t, isNull) + require.NoError(t, err) + + stringDatum := types.NewStringDatum("abc") + stringCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeVarchar)}, + Data: &stringDatum} + strVal, isNull, err := stringCorCol.EvalString(ctx, chunk.Row{}) + require.Equal(t, "abc", strVal) + require.False(t, isNull) + require.NoError(t, err) + + durationCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDuration)}, + Data: &durationDatum} + durationVal, isNull, err := durationCorCol.EvalDuration(ctx, chunk.Row{}) + require.Zero(t, durationVal.Compare(duration)) + require.False(t, isNull) + require.NoError(t, err) + + timeDatum := types.NewTimeDatum(tm) + timeCorCol := &CorrelatedColumn{Column: Column{RetType: types.NewFieldType(mysql.TypeDatetime)}, + Data: &timeDatum} + timeVal, isNull, err := timeCorCol.EvalTime(ctx, chunk.Row{}) + require.Zero(t, timeVal.Compare(tm)) + require.False(t, isNull) + require.NoError(t, err) +} + +func TestColumnHashCode(t *testing.T) { + col1 := &Column{ + UniqueID: 12, + } + require.EqualValues(t, []byte{0x1, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc}, col1.HashCode(nil)) + + col2 := &Column{ + UniqueID: 2, + } + require.EqualValues(t, []byte{0x1, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2}, col2.HashCode(nil)) +} + +func TestColumn2Expr(t *testing.T) { + cols := make([]*Column, 0, 5) + for i := 0; i < 5; i++ { + cols = append(cols, &Column{UniqueID: int64(i)}) + } + + exprs := Column2Exprs(cols) + for i := range exprs { + require.True(t, exprs[i].Equal(nil, cols[i])) + } +} + +func TestColInfo2Col(t *testing.T) { + col0, col1 := &Column{ID: 0}, &Column{ID: 1} + cols := []*Column{col0, col1} + colInfo := &model.ColumnInfo{ID: 0} + res := ColInfo2Col(cols, colInfo) + require.True(t, res.Equal(nil, col1)) + + colInfo.ID = 3 + res = ColInfo2Col(cols, colInfo) + require.Nil(t, res) +} + +func TestIndexInfo2Cols(t *testing.T) { + col0 := &Column{UniqueID: 0, ID: 0, RetType: types.NewFieldType(mysql.TypeLonglong)} + col1 := &Column{UniqueID: 1, ID: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} + colInfo0 := &model.ColumnInfo{ID: 0, Name: model.NewCIStr("0")} + colInfo1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("1")} + indexCol0, indexCol1 := &model.IndexColumn{Name: model.NewCIStr("0")}, &model.IndexColumn{Name: model.NewCIStr("1")} + indexInfo := &model.IndexInfo{Columns: []*model.IndexColumn{indexCol0, indexCol1}} + + cols := []*Column{col0} + colInfos := []*model.ColumnInfo{colInfo0} + resCols, lengths := IndexInfo2PrefixCols(colInfos, cols, indexInfo) + require.Len(t, resCols, 1) + require.Len(t, lengths, 1) + require.True(t, resCols[0].Equal(nil, col0)) + + cols = []*Column{col1} + colInfos = []*model.ColumnInfo{colInfo1} + resCols, lengths = IndexInfo2PrefixCols(colInfos, cols, indexInfo) + require.Len(t, resCols, 0) + require.Len(t, lengths, 0) + + cols = []*Column{col0, col1} + colInfos = []*model.ColumnInfo{colInfo0, colInfo1} + resCols, lengths = IndexInfo2PrefixCols(colInfos, cols, indexInfo) + require.Len(t, resCols, 2) + require.Len(t, lengths, 2) + require.True(t, resCols[0].Equal(nil, col0)) + require.True(t, resCols[1].Equal(nil, col1)) +} + +func TestColHybird(t *testing.T) { + ctx := mock.NewContext() + + // bit + ft := types.NewFieldType(mysql.TypeBit) + col := &Column{RetType: ft, Index: 0} + input := chunk.New([]*types.FieldType{ft}, 1024, 1024) + for i := 0; i < 1024; i++ { + num, err := types.ParseBitStr(fmt.Sprintf("0b%b", i)) + require.NoError(t, err) + input.AppendBytes(0, num) + } + result := chunk.NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + require.Nil(t, col.VecEvalInt(ctx, input, result)) + + it := chunk.NewIterator4Chunk(input) + for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { + v, _, err := col.EvalInt(ctx, row) + require.NoError(t, err) + require.Equal(t, result.GetInt64(i), v) + } + + // use a container which has the different field type with bit + result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) + require.Nil(t, col.VecEvalInt(ctx, input, result)) + for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { + v, _, err := col.EvalInt(ctx, row) + require.NoError(t, err) + require.Equal(t, result.GetInt64(i), v) + } + + // enum + ft = types.NewFieldType(mysql.TypeEnum) + col.RetType = ft + input = chunk.New([]*types.FieldType{ft}, 1024, 1024) + for i := 0; i < 1024; i++ { + input.AppendEnum(0, types.Enum{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) + } + result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) + require.Nil(t, col.VecEvalString(ctx, input, result)) + + it = chunk.NewIterator4Chunk(input) + for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { + v, _, err := col.EvalString(ctx, row) + require.NoError(t, err) + require.Equal(t, result.GetString(i), v) + } + + // set + ft = types.NewFieldType(mysql.TypeSet) + col.RetType = ft + input = chunk.New([]*types.FieldType{ft}, 1024, 1024) + for i := 0; i < 1024; i++ { + input.AppendSet(0, types.Set{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) + } + result = chunk.NewColumn(types.NewFieldType(mysql.TypeString), 1024) + require.Nil(t, col.VecEvalString(ctx, input, result)) + + it = chunk.NewIterator4Chunk(input) + for row, i := it.Begin(), 0; row != it.End(); row, i = it.Next(), i+1 { + v, _, err := col.EvalString(ctx, row) + require.NoError(t, err) + require.Equal(t, result.GetString(i), v) + } +} + +func TestInColumnArray(t *testing.T) { + // normal case, col is in column array + col0, col1 := &Column{ID: 0, UniqueID: 0}, &Column{ID: 1, UniqueID: 1} + cols := []*Column{col0, col1} + require.True(t, col0.InColumnArray(cols)) + + // abnormal case, col is not in column array + require.False(t, col0.InColumnArray([]*Column{col1})) + + // abnormal case, input is nil + require.False(t, col0.InColumnArray(nil)) +} + +func TestGcColumnExprIsTidbShard(t *testing.T) { + ctx := mock.NewContext() + + // abnormal case + // nil, not tidb_shard + require.False(t, GcColumnExprIsTidbShard(nil)) + + // `a = 1`, not tidb_shard + ft := types.NewFieldType(mysql.TypeLonglong) + col := &Column{RetType: ft, Index: 0} + d1 := types.NewDatum(1) + con := &Constant{Value: d1, RetType: ft} + expr := NewFunctionInternal(ctx, ast.EQ, ft, col, con) + require.False(t, GcColumnExprIsTidbShard(expr)) + + // normal case + // tidb_shard(a) = 1 + shardExpr := NewFunctionInternal(ctx, ast.TiDBShard, ft, col) + require.True(t, GcColumnExprIsTidbShard(shardExpr)) +} diff --git a/pkg/expression/constant.go b/pkg/expression/constant.go new file mode 100644 index 0000000000000..bb6ea63068ebe --- /dev/null +++ b/pkg/expression/constant.go @@ -0,0 +1,528 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "fmt" + "unsafe" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" +) + +// NewOne stands for a number 1. +func NewOne() *Constant { + retT := types.NewFieldType(mysql.TypeTiny) + retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion + retT.SetFlen(1) + retT.SetDecimal(0) + return &Constant{ + Value: types.NewDatum(1), + RetType: retT, + } +} + +// NewZero stands for a number 0. +func NewZero() *Constant { + retT := types.NewFieldType(mysql.TypeTiny) + retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion + retT.SetFlen(1) + retT.SetDecimal(0) + return &Constant{ + Value: types.NewDatum(0), + RetType: retT, + } +} + +// NewUInt64Const stands for constant of a given number. +func NewUInt64Const(num int) *Constant { + retT := types.NewFieldType(mysql.TypeLonglong) + retT.AddFlag(mysql.UnsignedFlag) // shrink range to avoid integral promotion + retT.SetFlen(mysql.MaxIntWidth) + retT.SetDecimal(0) + return &Constant{ + Value: types.NewDatum(num), + RetType: retT, + } +} + +// NewUInt64ConstWithFieldType stands for constant of a given number with specified fieldType. +func NewUInt64ConstWithFieldType(num uint64, fieldType *types.FieldType) *Constant { + return &Constant{ + Value: types.NewDatum(num), + RetType: fieldType, + } +} + +// NewInt64Const stands for constant of a given number. +func NewInt64Const(num int64) *Constant { + retT := types.NewFieldType(mysql.TypeLonglong) + retT.SetFlen(mysql.MaxIntWidth) + retT.SetDecimal(0) + return &Constant{ + Value: types.NewDatum(num), + RetType: retT, + } +} + +// NewNull stands for null constant. +func NewNull() *Constant { + retT := types.NewFieldType(mysql.TypeTiny) + retT.SetFlen(1) + retT.SetDecimal(0) + return &Constant{ + Value: types.NewDatum(nil), + RetType: retT, + } +} + +// NewNullWithFieldType stands for null constant with specified fieldType. +func NewNullWithFieldType(fieldType *types.FieldType) *Constant { + return &Constant{ + Value: types.NewDatum(nil), + RetType: fieldType, + } +} + +// Constant stands for a constant value. +type Constant struct { + Value types.Datum + RetType *types.FieldType + // DeferredExpr holds deferred function in PlanCache cached plan. + // it's only used to represent non-deterministic functions(see expression.DeferredFunctions) + // in PlanCache cached plan, so let them can be evaluated until cached item be used. + DeferredExpr Expression + // ParamMarker holds param index inside sessionVars.PreparedParams. + // It's only used to reference a user variable provided in the `EXECUTE` statement or `COM_EXECUTE` binary protocol. + ParamMarker *ParamMarker + hashcode []byte + + collationInfo +} + +// ParamMarker indicates param provided by COM_STMT_EXECUTE. +type ParamMarker struct { + ctx sessionctx.Context + order int +} + +// GetUserVar returns the corresponding user variable presented in the `EXECUTE` statement or `COM_EXECUTE` command. +func (d *ParamMarker) GetUserVar() types.Datum { + sessionVars := d.ctx.GetSessionVars() + return sessionVars.PlanCacheParams.GetParamValue(d.order) +} + +// String implements fmt.Stringer interface. +func (c *Constant) String() string { + if c.ParamMarker != nil { + dt := c.ParamMarker.GetUserVar() + c.Value.SetValue(dt.GetValue(), c.RetType) + } else if c.DeferredExpr != nil { + return c.DeferredExpr.String() + } + return fmt.Sprintf("%v", c.Value.GetValue()) +} + +// MarshalJSON implements json.Marshaler interface. +func (c *Constant) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", c)), nil +} + +// Clone implements Expression interface. +func (c *Constant) Clone() Expression { + con := *c + return &con +} + +// GetType implements Expression interface. +func (c *Constant) GetType() *types.FieldType { + if c.ParamMarker != nil { + // GetType() may be called in multi-threaded context, e.g, in building inner executors of IndexJoin, + // so it should avoid data race. We achieve this by returning different FieldType pointer for each call. + tp := types.NewFieldType(mysql.TypeUnspecified) + dt := c.ParamMarker.GetUserVar() + types.InferParamTypeFromDatum(&dt, tp) + return tp + } + return c.RetType +} + +// VecEvalInt evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETInt, input, result) + } + return c.DeferredExpr.VecEvalInt(ctx, input, result) +} + +// VecEvalReal evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETReal, input, result) + } + return c.DeferredExpr.VecEvalReal(ctx, input, result) +} + +// VecEvalString evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETString, input, result) + } + return c.DeferredExpr.VecEvalString(ctx, input, result) +} + +// VecEvalDecimal evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETDecimal, input, result) + } + return c.DeferredExpr.VecEvalDecimal(ctx, input, result) +} + +// VecEvalTime evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETTimestamp, input, result) + } + return c.DeferredExpr.VecEvalTime(ctx, input, result) +} + +// VecEvalDuration evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETDuration, input, result) + } + return c.DeferredExpr.VecEvalDuration(ctx, input, result) +} + +// VecEvalJSON evaluates this expression in a vectorized manner. +func (c *Constant) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + if c.DeferredExpr == nil { + return genVecFromConstExpr(ctx, c, types.ETJson, input, result) + } + return c.DeferredExpr.VecEvalJSON(ctx, input, result) +} + +func (c *Constant) getLazyDatum(row chunk.Row) (dt types.Datum, isLazy bool, err error) { + if c.ParamMarker != nil { + return c.ParamMarker.GetUserVar(), true, nil + } else if c.DeferredExpr != nil { + dt, err = c.DeferredExpr.Eval(row) + return dt, true, err + } + return types.Datum{}, false, nil +} + +// Traverse implements the TraverseDown interface. +func (c *Constant) Traverse(action TraverseAction) Expression { + return action.Transform(c) +} + +// Eval implements Expression interface. +func (c *Constant) Eval(row chunk.Row) (types.Datum, error) { + if dt, lazy, err := c.getLazyDatum(row); lazy { + if err != nil { + return c.Value, err + } + if dt.IsNull() { + c.Value.SetNull() + return c.Value, nil + } + if c.DeferredExpr != nil { + sf, sfOk := c.DeferredExpr.(*ScalarFunction) + if sfOk { + if dt.Kind() != types.KindMysqlDecimal { + val, err := dt.ConvertTo(sf.GetCtx().GetSessionVars().StmtCtx, c.RetType) + if err != nil { + return dt, err + } + return val, nil + } + if err := c.adjustDecimal(dt.GetMysqlDecimal()); err != nil { + return dt, err + } + } + } + return dt, nil + } + return c.Value, nil +} + +// EvalInt returns int representation of Constant. +func (c *Constant) EvalInt(ctx sessionctx.Context, row chunk.Row) (int64, bool, error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return 0, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return 0, true, nil + } else if dt.Kind() == types.KindBinaryLiteral { + val, err := dt.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) + return int64(val), err != nil, err + } else if c.GetType().Hybrid() || dt.Kind() == types.KindString { + res, err := dt.ToInt64(ctx.GetSessionVars().StmtCtx) + return res, false, err + } else if dt.Kind() == types.KindMysqlBit { + uintVal, err := dt.GetBinaryLiteral().ToInt(ctx.GetSessionVars().StmtCtx) + return int64(uintVal), false, err + } + return dt.GetInt64(), false, nil +} + +// EvalReal returns real representation of Constant. +func (c *Constant) EvalReal(ctx sessionctx.Context, row chunk.Row) (float64, bool, error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return 0, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return 0, true, nil + } + if c.GetType().Hybrid() || dt.Kind() == types.KindBinaryLiteral || dt.Kind() == types.KindString { + res, err := dt.ToFloat64(ctx.GetSessionVars().StmtCtx) + return res, false, err + } + return dt.GetFloat64(), false, nil +} + +// EvalString returns string representation of Constant. +func (c *Constant) EvalString(ctx sessionctx.Context, row chunk.Row) (string, bool, error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return "", false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return "", true, nil + } + res, err := dt.ToString() + return res, false, err +} + +// EvalDecimal returns decimal representation of Constant. +func (c *Constant) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (*types.MyDecimal, bool, error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return nil, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return nil, true, nil + } + res, err := dt.ToDecimal(ctx.GetSessionVars().StmtCtx) + if err != nil { + return nil, false, err + } + if err := c.adjustDecimal(res); err != nil { + return nil, false, err + } + return res, false, nil +} + +func (c *Constant) adjustDecimal(d *types.MyDecimal) error { + // Decimal Value's precision and frac may be modified during plan building. + _, frac := d.PrecisionAndFrac() + if frac < c.GetType().GetDecimal() { + return d.Round(d, c.GetType().GetDecimal(), types.ModeHalfUp) + } + return nil +} + +// EvalTime returns DATE/DATETIME/TIMESTAMP representation of Constant. +func (c *Constant) EvalTime(ctx sessionctx.Context, row chunk.Row) (val types.Time, isNull bool, err error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return types.ZeroTime, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return types.ZeroTime, true, nil + } + return dt.GetMysqlTime(), false, nil +} + +// EvalDuration returns Duration representation of Constant. +func (c *Constant) EvalDuration(ctx sessionctx.Context, row chunk.Row) (val types.Duration, isNull bool, err error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return types.Duration{}, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return types.Duration{}, true, nil + } + return dt.GetMysqlDuration(), false, nil +} + +// EvalJSON returns JSON representation of Constant. +func (c *Constant) EvalJSON(ctx sessionctx.Context, row chunk.Row) (types.BinaryJSON, bool, error) { + dt, lazy, err := c.getLazyDatum(row) + if err != nil { + return types.BinaryJSON{}, false, err + } + if !lazy { + dt = c.Value + } + if c.GetType().GetType() == mysql.TypeNull || dt.IsNull() { + return types.BinaryJSON{}, true, nil + } + return dt.GetMysqlJSON(), false, nil +} + +// Equal implements Expression interface. +func (c *Constant) Equal(ctx sessionctx.Context, b Expression) bool { + y, ok := b.(*Constant) + if !ok { + return false + } + _, err1 := y.Eval(chunk.Row{}) + _, err2 := c.Eval(chunk.Row{}) + if err1 != nil || err2 != nil { + return false + } + con, err := c.Value.Compare(ctx.GetSessionVars().StmtCtx, &y.Value, collate.GetBinaryCollator()) + if err != nil || con != 0 { + return false + } + return true +} + +// IsCorrelated implements Expression interface. +func (c *Constant) IsCorrelated() bool { + return false +} + +// ConstItem implements Expression interface. +func (c *Constant) ConstItem(sc *stmtctx.StatementContext) bool { + return !sc.UseCache || (c.DeferredExpr == nil && c.ParamMarker == nil) +} + +// Decorrelate implements Expression interface. +func (c *Constant) Decorrelate(_ *Schema) Expression { + return c +} + +// HashCode implements Expression interface. +func (c *Constant) HashCode(sc *stmtctx.StatementContext) []byte { + if len(c.hashcode) > 0 { + return c.hashcode + } + + if c.DeferredExpr != nil { + c.hashcode = c.DeferredExpr.HashCode(sc) + return c.hashcode + } + + if c.ParamMarker != nil { + c.hashcode = append(c.hashcode, parameterFlag) + c.hashcode = codec.EncodeInt(c.hashcode, int64(c.ParamMarker.order)) + return c.hashcode + } + + _, err := c.Eval(chunk.Row{}) + if err != nil { + terror.Log(err) + } + c.hashcode = append(c.hashcode, constantFlag) + c.hashcode = codec.HashCode(c.hashcode, c.Value) + return c.hashcode +} + +// ResolveIndices implements Expression interface. +func (c *Constant) ResolveIndices(_ *Schema) (Expression, error) { + return c, nil +} + +func (c *Constant) resolveIndices(_ *Schema) error { + return nil +} + +// ResolveIndicesByVirtualExpr implements Expression interface. +func (c *Constant) ResolveIndicesByVirtualExpr(_ *Schema) (Expression, bool) { + return c, true +} + +func (c *Constant) resolveIndicesByVirtualExpr(_ *Schema) bool { + return true +} + +// RemapColumn remaps columns with provided mapping and returns new expression +func (c *Constant) RemapColumn(_ map[int64]*Column) (Expression, error) { + return c, nil +} + +// Vectorized returns if this expression supports vectorized evaluation. +func (c *Constant) Vectorized() bool { + if c.DeferredExpr != nil { + return c.DeferredExpr.Vectorized() + } + return true +} + +// SupportReverseEval checks whether the builtinFunc support reverse evaluation. +func (c *Constant) SupportReverseEval() bool { + if c.DeferredExpr != nil { + return c.DeferredExpr.SupportReverseEval() + } + return true +} + +// ReverseEval evaluates the only one column value with given function result. +func (c *Constant) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { + return c.Value, nil +} + +// Coercibility returns the coercibility value which is used to check collations. +func (c *Constant) Coercibility() Coercibility { + if !c.HasCoercibility() { + c.SetCoercibility(deriveCoercibilityForConstant(c)) + } + return c.collationInfo.Coercibility() +} + +const emptyConstantSize = int64(unsafe.Sizeof(Constant{})) + +// MemoryUsage return the memory usage of Constant +func (c *Constant) MemoryUsage() (sum int64) { + if c == nil { + return + } + + sum = emptyConstantSize + c.Value.MemUsage() + int64(cap(c.hashcode)) + if c.RetType != nil { + sum += c.RetType.MemoryUsage() + } + return +} diff --git a/expression/constant_fold.go b/pkg/expression/constant_fold.go similarity index 97% rename from expression/constant_fold.go rename to pkg/expression/constant_fold.go index d2c328ace5a63..170f7100d1381 100644 --- a/expression/constant_fold.go +++ b/pkg/expression/constant_fold.go @@ -15,11 +15,11 @@ package expression import ( - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/expression/constant_propagation.go b/pkg/expression/constant_propagation.go similarity index 98% rename from expression/constant_propagation.go rename to pkg/expression/constant_propagation.go index 0fa8104f9f0e8..5bb233a3127f2 100644 --- a/expression/constant_propagation.go +++ b/pkg/expression/constant_propagation.go @@ -17,15 +17,15 @@ package expression import ( "errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/disjointset" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/disjointset" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/expression/constant_test.go b/pkg/expression/constant_test.go similarity index 98% rename from expression/constant_test.go rename to pkg/expression/constant_test.go index 0d739651a4684..e3cee448792c3 100644 --- a/expression/constant_test.go +++ b/pkg/expression/constant_test.go @@ -21,11 +21,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/expression/distsql_builtin.go b/pkg/expression/distsql_builtin.go similarity index 99% rename from expression/distsql_builtin.go rename to pkg/expression/distsql_builtin.go index 01a52a9dc5a8b..1105052d7660b 100644 --- a/expression/distsql_builtin.go +++ b/pkg/expression/distsql_builtin.go @@ -21,15 +21,15 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/distsql_builtin_test.go b/pkg/expression/distsql_builtin_test.go similarity index 99% rename from expression/distsql_builtin_test.go rename to pkg/expression/distsql_builtin_test.go index 4483602a0bcb8..ef4a2ff34567b 100644 --- a/expression/distsql_builtin_test.go +++ b/pkg/expression/distsql_builtin_test.go @@ -18,13 +18,13 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/expression/errors.go b/pkg/expression/errors.go new file mode 100644 index 0000000000000..6f0b037af984a --- /dev/null +++ b/pkg/expression/errors.go @@ -0,0 +1,117 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package expression + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + pmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// Error instances. +var ( + // All the exported errors are defined here: + ErrIncorrectParameterCount = dbterror.ClassExpression.NewStd(mysql.ErrWrongParamcountToNativeFct) + ErrDivisionByZero = dbterror.ClassExpression.NewStd(mysql.ErrDivisionByZero) + ErrRegexp = dbterror.ClassExpression.NewStd(mysql.ErrRegexp) + ErrOperandColumns = dbterror.ClassExpression.NewStd(mysql.ErrOperandColumns) + ErrCutValueGroupConcat = dbterror.ClassExpression.NewStd(mysql.ErrCutValueGroupConcat) + ErrFunctionsNoopImpl = dbterror.ClassExpression.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("function %s has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", nil)) + ErrInvalidArgumentForLogarithm = dbterror.ClassExpression.NewStd(mysql.ErrInvalidArgumentForLogarithm) + ErrIncorrectType = dbterror.ClassExpression.NewStd(mysql.ErrIncorrectType) + ErrInvalidTypeForJSON = dbterror.ClassExpression.NewStd(mysql.ErrInvalidTypeForJSON) + ErrInvalidTableSample = dbterror.ClassExpression.NewStd(mysql.ErrInvalidTableSample) + ErrInternal = dbterror.ClassOptimizer.NewStd(mysql.ErrInternal) + ErrNoDB = dbterror.ClassOptimizer.NewStd(mysql.ErrNoDB) + ErrNotSupportedYet = dbterror.ClassExpression.NewStd(mysql.ErrNotSupportedYet) + ErrInvalidJSONForFuncIndex = dbterror.ClassExpression.NewStd(mysql.ErrInvalidJSONValueForFuncIndex) + ErrDataOutOfRangeFuncIndex = dbterror.ClassExpression.NewStd(mysql.ErrDataOutOfRangeFunctionalIndex) + ErrFuncIndexDataIsTooLong = dbterror.ClassExpression.NewStd(mysql.ErrFunctionalIndexDataIsTooLong) + ErrFunctionNotExists = dbterror.ClassExpression.NewStd(mysql.ErrSpDoesNotExist) + + // All the un-exported errors are defined here: + errZlibZData = dbterror.ClassExpression.NewStd(mysql.ErrZlibZData) + errZlibZBuf = dbterror.ClassExpression.NewStd(mysql.ErrZlibZBuf) + errIncorrectArgs = dbterror.ClassExpression.NewStd(mysql.ErrWrongArguments) + errUnknownCharacterSet = dbterror.ClassExpression.NewStd(mysql.ErrUnknownCharacterSet) + errDefaultValue = dbterror.ClassExpression.NewStdErr(mysql.ErrInvalidDefault, pmysql.Message("invalid default value", nil)) + errDeprecatedSyntaxNoReplacement = dbterror.ClassExpression.NewStd(mysql.ErrWarnDeprecatedSyntaxNoReplacement) + errWarnAllowedPacketOverflowed = dbterror.ClassExpression.NewStd(mysql.ErrWarnAllowedPacketOverflowed) + errWarnOptionIgnored = dbterror.ClassExpression.NewStd(mysql.WarnOptionIgnored) + errTruncatedWrongValue = dbterror.ClassExpression.NewStd(mysql.ErrTruncatedWrongValue) + errUnknownLocale = dbterror.ClassExpression.NewStd(mysql.ErrUnknownLocale) + errNonUniq = dbterror.ClassExpression.NewStd(mysql.ErrNonUniq) + errWrongValueForType = dbterror.ClassExpression.NewStd(mysql.ErrWrongValueForType) + errUnknown = dbterror.ClassExpression.NewStd(mysql.ErrUnknown) + errSpecificAccessDenied = dbterror.ClassExpression.NewStd(mysql.ErrSpecificAccessDenied) + errUserLockDeadlock = dbterror.ClassExpression.NewStd(mysql.ErrUserLockDeadlock) + errUserLockWrongName = dbterror.ClassExpression.NewStd(mysql.ErrUserLockWrongName) + errJSONInBooleanContext = dbterror.ClassExpression.NewStd(mysql.ErrJSONInBooleanContext) + + // Sequence usage privilege check. + errSequenceAccessDenied = dbterror.ClassExpression.NewStd(mysql.ErrTableaccessDenied) + errUnsupportedJSONComparison = dbterror.ClassExpression.NewStdErr(mysql.ErrNotSupportedYet, + pmysql.Message("comparison of JSON in the LEAST and GREATEST operators", nil)) +) + +// handleInvalidTimeError reports error or warning depend on the context. +func handleInvalidTimeError(ctx sessionctx.Context, err error) error { + if err == nil || !(types.ErrWrongValue.Equal(err) || types.ErrWrongValueForType.Equal(err) || + types.ErrTruncatedWrongVal.Equal(err) || types.ErrInvalidWeekModeFormat.Equal(err) || + types.ErrDatetimeFunctionOverflow.Equal(err) || types.ErrIncorrectDatetimeValue.Equal(err)) { + return err + } + sc := ctx.GetSessionVars().StmtCtx + err = sc.HandleTruncate(err) + if ctx.GetSessionVars().StrictSQLMode && (sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt) { + return err + } + return nil +} + +// handleDivisionByZeroError reports error or warning depend on the context. +func handleDivisionByZeroError(ctx sessionctx.Context) error { + sc := ctx.GetSessionVars().StmtCtx + if sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt { + if !ctx.GetSessionVars().SQLMode.HasErrorForDivisionByZeroMode() { + return nil + } + if ctx.GetSessionVars().StrictSQLMode && !sc.DividedByZeroAsWarning { + return ErrDivisionByZero + } + } + sc.AppendWarning(ErrDivisionByZero) + return nil +} + +// handleAllowedPacketOverflowed reports error or warning depend on the context. +func handleAllowedPacketOverflowed(ctx sessionctx.Context, exprName string, maxAllowedPacketSize uint64) error { + err := errWarnAllowedPacketOverflowed.GenWithStackByArgs(exprName, maxAllowedPacketSize) + sc := ctx.GetSessionVars().StmtCtx + + // insert|update|delete ignore ... + if sc.TruncateAsWarning { + sc.AppendWarning(err) + return nil + } + + if ctx.GetSessionVars().StrictSQLMode && (sc.InInsertStmt || sc.InUpdateStmt || sc.InDeleteStmt) { + return err + } + sc.AppendWarning(err) + return nil +} diff --git a/expression/evaluator.go b/pkg/expression/evaluator.go similarity index 98% rename from expression/evaluator.go rename to pkg/expression/evaluator.go index b9af0ddda00cc..0482b4f036a3a 100644 --- a/expression/evaluator.go +++ b/pkg/expression/evaluator.go @@ -15,8 +15,8 @@ package expression import ( - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/chunk" ) type columnEvaluator struct { diff --git a/expression/evaluator_test.go b/pkg/expression/evaluator_test.go similarity index 98% rename from expression/evaluator_test.go rename to pkg/expression/evaluator_test.go index b0d1ded2d70b6..daeefbfacff8b 100644 --- a/expression/evaluator_test.go +++ b/pkg/expression/evaluator_test.go @@ -19,13 +19,13 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/pkg/expression/explain.go b/pkg/expression/explain.go new file mode 100644 index 0000000000000..523f4d89de59c --- /dev/null +++ b/pkg/expression/explain.go @@ -0,0 +1,192 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "bytes" + "fmt" + "slices" + "strings" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" +) + +// ExplainInfo implements the Expression interface. +func (expr *ScalarFunction) ExplainInfo() string { + return expr.explainInfo(false) +} + +func (expr *ScalarFunction) explainInfo(normalized bool) string { + var buffer bytes.Buffer + fmt.Fprintf(&buffer, "%s(", expr.FuncName.L) + switch expr.FuncName.L { + case ast.Cast: + for _, arg := range expr.GetArgs() { + if normalized { + buffer.WriteString(arg.ExplainNormalizedInfo()) + } else { + buffer.WriteString(arg.ExplainInfo()) + } + buffer.WriteString(", ") + buffer.WriteString(expr.RetType.String()) + } + default: + for i, arg := range expr.GetArgs() { + if normalized { + buffer.WriteString(arg.ExplainNormalizedInfo()) + } else { + buffer.WriteString(arg.ExplainInfo()) + } + if i+1 < len(expr.GetArgs()) { + buffer.WriteString(", ") + } + } + } + buffer.WriteString(")") + return buffer.String() +} + +// ExplainNormalizedInfo implements the Expression interface. +func (expr *ScalarFunction) ExplainNormalizedInfo() string { + return expr.explainInfo(true) +} + +// ExplainInfo implements the Expression interface. +func (col *Column) ExplainInfo() string { + return col.String() +} + +// ExplainNormalizedInfo implements the Expression interface. +func (col *Column) ExplainNormalizedInfo() string { + if col.OrigName != "" { + return col.OrigName + } + return "?" +} + +// ExplainInfo implements the Expression interface. +func (expr *Constant) ExplainInfo() string { + dt, err := expr.Eval(chunk.Row{}) + if err != nil { + return "not recognized const vanue" + } + return expr.format(dt) +} + +// ExplainNormalizedInfo implements the Expression interface. +func (expr *Constant) ExplainNormalizedInfo() string { + return "?" +} + +func (expr *Constant) format(dt types.Datum) string { + switch dt.Kind() { + case types.KindNull: + return "NULL" + case types.KindString, types.KindBytes, types.KindMysqlEnum, types.KindMysqlSet, + types.KindMysqlJSON, types.KindBinaryLiteral, types.KindMysqlBit: + return fmt.Sprintf("\"%v\"", dt.GetValue()) + } + return fmt.Sprintf("%v", dt.GetValue()) +} + +// ExplainExpressionList generates explain information for a list of expressions. +func ExplainExpressionList(exprs []Expression, schema *Schema) string { + builder := &strings.Builder{} + for i, expr := range exprs { + switch expr.(type) { + case *Column, *CorrelatedColumn: + builder.WriteString(expr.String()) + if expr.String() != schema.Columns[i].String() { + // simple col projected again with another uniqueID without origin name. + builder.WriteString("->") + builder.WriteString(schema.Columns[i].String()) + } + case *Constant: + v := expr.String() + length := 64 + if len(v) < length { + builder.WriteString(v) + } else { + builder.WriteString(v[:length]) + fmt.Fprintf(builder, "(len:%d)", len(v)) + } + builder.WriteString("->") + builder.WriteString(schema.Columns[i].String()) + default: + builder.WriteString(expr.String()) + builder.WriteString("->") + builder.WriteString(schema.Columns[i].String()) + } + if i+1 < len(exprs) { + builder.WriteString(", ") + } + } + return builder.String() +} + +// SortedExplainExpressionList generates explain information for a list of expressions in order. +// In some scenarios, the expr's order may not be stable when executing multiple times. +// So we add a sort to make its explain result stable. +func SortedExplainExpressionList(exprs []Expression) []byte { + return sortedExplainExpressionList(exprs, false) +} + +func sortedExplainExpressionList(exprs []Expression, normalized bool) []byte { + buffer := bytes.NewBufferString("") + exprInfos := make([]string, 0, len(exprs)) + for _, expr := range exprs { + if normalized { + exprInfos = append(exprInfos, expr.ExplainNormalizedInfo()) + } else { + exprInfos = append(exprInfos, expr.ExplainInfo()) + } + } + slices.Sort(exprInfos) + for i, info := range exprInfos { + buffer.WriteString(info) + if i+1 < len(exprInfos) { + buffer.WriteString(", ") + } + } + return buffer.Bytes() +} + +// SortedExplainNormalizedExpressionList is same like SortedExplainExpressionList, but use for generating normalized information. +func SortedExplainNormalizedExpressionList(exprs []Expression) []byte { + return sortedExplainExpressionList(exprs, true) +} + +// SortedExplainNormalizedScalarFuncList is same like SortedExplainExpressionList, but use for generating normalized information. +func SortedExplainNormalizedScalarFuncList(exprs []*ScalarFunction) []byte { + expressions := make([]Expression, len(exprs)) + for i := range exprs { + expressions[i] = exprs[i] + } + return sortedExplainExpressionList(expressions, true) +} + +// ExplainColumnList generates explain information for a list of columns. +func ExplainColumnList(cols []*Column) []byte { + buffer := bytes.NewBufferString("") + for i, col := range cols { + buffer.WriteString(col.ExplainInfo()) + if i+1 < len(cols) { + buffer.WriteString(", ") + } + } + return buffer.Bytes() +} diff --git a/expression/expr_to_pb.go b/pkg/expression/expr_to_pb.go similarity index 95% rename from expression/expr_to_pb.go rename to pkg/expression/expr_to_pb.go index 09241e33662fb..9f884f750db36 100644 --- a/expression/expr_to_pb.go +++ b/pkg/expression/expr_to_pb.go @@ -20,15 +20,15 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - ast "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + ast "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/expression/expr_to_pb_test.go b/pkg/expression/expr_to_pb_test.go similarity index 98% rename from expression/expr_to_pb_test.go rename to pkg/expression/expr_to_pb_test.go index 877508d962bc9..7764bec404281 100644 --- a/expression/expr_to_pb_test.go +++ b/pkg/expression/expr_to_pb_test.go @@ -22,13 +22,13 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) @@ -1678,9 +1678,9 @@ func TestMetadata(t *testing.T) { sc := stmtctx.NewStmtCtx() client := new(mock.Client) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/PushDownTestSwitcher", `return("all")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher", `return("all")`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/PushDownTestSwitcher")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher")) }() pc := PbConverter{client: client, sc: sc} @@ -1748,9 +1748,9 @@ func TestPushDownSwitcher(t *testing.T) { } } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/PushDownTestSwitcher", `return("all")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher", `return("all")`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/PushDownTestSwitcher")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher")) }() pbExprs, err := ExpressionsToPBList(sc, funcs, client) @@ -1761,7 +1761,7 @@ func TestPushDownSwitcher(t *testing.T) { } // All disabled - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/PushDownTestSwitcher", `return("")`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher", `return("")`)) pc := PbConverter{client: client, sc: sc} for i := range funcs { pbExpr := pc.ExprToPB(funcs[i]) @@ -1770,7 +1770,7 @@ func TestPushDownSwitcher(t *testing.T) { // Partial enabled fpexpr := fmt.Sprintf(`return("%s")`, strings.Join(enabled, ",")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/PushDownTestSwitcher", fpexpr)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/PushDownTestSwitcher", fpexpr)) for i := range funcs { pbExpr := pc.ExprToPB(funcs[i]) if !cases[i].enable { @@ -1794,9 +1794,9 @@ func TestPanicIfPbCodeUnspecified(t *testing.T) { fn.Function.setPbCode(tipb.ScalarFuncSig_Unspecified) require.Equal(t, tipb.ScalarFuncSig_Unspecified, fn.Function.PbCode()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/PanicIfPbCodeUnspecified", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/PanicIfPbCodeUnspecified", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/expression/PanicIfPbCodeUnspecified")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/PanicIfPbCodeUnspecified")) }() pc := PbConverter{client: new(mock.Client), sc: stmtctx.NewStmtCtx()} require.PanicsWithError(t, "unspecified PbCode: *expression.builtinBitAndSig", func() { pc.ExprToPB(fn) }) diff --git a/expression/expression.go b/pkg/expression/expression.go similarity index 98% rename from expression/expression.go rename to pkg/expression/expression.go index 43ebe4aebf9cc..23fa0ac2f4bbf 100644 --- a/expression/expression.go +++ b/pkg/expression/expression.go @@ -24,21 +24,21 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/generatedexpr" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/zeropool" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/generatedexpr" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/zeropool" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/pkg/expression/expression_test.go b/pkg/expression/expression_test.go new file mode 100644 index 0000000000000..3d2f2e860dfd8 --- /dev/null +++ b/pkg/expression/expression_test.go @@ -0,0 +1,292 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/stretchr/testify/require" +) + +func TestNewValuesFunc(t *testing.T) { + ctx := createContext(t) + res := NewValuesFunc(ctx, 0, types.NewFieldType(mysql.TypeLonglong)) + require.Equal(t, "values", res.FuncName.O) + require.Equal(t, mysql.TypeLonglong, res.RetType.GetType()) + _, ok := res.Function.(*builtinValuesIntSig) + require.True(t, ok) +} + +func TestEvaluateExprWithNull(t *testing.T) { + ctx := createContext(t) + tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong, 0).add("col1", mysql.TypeLonglong, 0).build() + schema := tableInfoToSchemaForTest(tblInfo) + col0 := schema.Columns[0] + col1 := schema.Columns[1] + schema.Columns = schema.Columns[:1] + innerIfNull, err := newFunctionForTest(ctx, ast.Ifnull, col1, NewOne()) + require.NoError(t, err) + outerIfNull, err := newFunctionForTest(ctx, ast.Ifnull, col0, innerIfNull) + require.NoError(t, err) + + res := EvaluateExprWithNull(ctx, schema, outerIfNull) + require.Equal(t, "ifnull(Column#1, 1)", res.String()) + schema.Columns = append(schema.Columns, col1) + // ifnull(null, ifnull(null, 1)) + res = EvaluateExprWithNull(ctx, schema, outerIfNull) + require.True(t, res.Equal(ctx, NewOne())) +} + +func TestEvaluateExprWithNullAndParameters(t *testing.T) { + ctx := createContext(t) + tblInfo := newTestTableBuilder("").add("col0", mysql.TypeLonglong, 0).build() + schema := tableInfoToSchemaForTest(tblInfo) + col0 := schema.Columns[0] + + ctx.GetSessionVars().StmtCtx.UseCache = true + + // cases for parameters + ltWithoutParam, err := newFunctionForTest(ctx, ast.LT, col0, NewOne()) + require.NoError(t, err) + res := EvaluateExprWithNull(ctx, schema, ltWithoutParam) + require.True(t, res.Equal(ctx, NewNull())) // the expression is evaluated to null + param := NewOne() + param.ParamMarker = &ParamMarker{ctx: ctx, order: 0} + ctx.GetSessionVars().PlanCacheParams.Append(types.NewIntDatum(10)) + ltWithParam, err := newFunctionForTest(ctx, ast.LT, col0, param) + require.NoError(t, err) + res = EvaluateExprWithNull(ctx, schema, ltWithParam) + _, isConst := res.(*Constant) + require.True(t, isConst) // this expression is evaluated and skip-plan cache flag is set. + require.True(t, !ctx.GetSessionVars().StmtCtx.UseCache) +} + +func TestEvaluateExprWithNullNoChangeRetType(t *testing.T) { + ctx := createContext(t) + tblInfo := newTestTableBuilder("").add("col_str", mysql.TypeString, 0).build() + schema := tableInfoToSchemaForTest(tblInfo) + + castStrAsJSON := BuildCastFunction(ctx, schema.Columns[0], types.NewFieldType(mysql.TypeJSON)) + jsonConstant := &Constant{Value: types.NewDatum("123"), RetType: types.NewFieldType(mysql.TypeJSON)} + + // initially has ParseToJSONFlag + flagInCast := castStrAsJSON.(*ScalarFunction).RetType.GetFlag() + require.True(t, mysql.HasParseToJSONFlag(flagInCast)) + + // cast's ParseToJSONFlag removed by `DisableParseJSONFlag4Expr` + eq, err := newFunctionForTest(ctx, ast.EQ, jsonConstant, castStrAsJSON) + require.NoError(t, err) + flagInCast = eq.(*ScalarFunction).GetArgs()[1].(*ScalarFunction).RetType.GetFlag() + require.False(t, mysql.HasParseToJSONFlag(flagInCast)) + + // after EvaluateExprWithNull, this flag should be still false + EvaluateExprWithNull(ctx, schema, eq) + flagInCast = eq.(*ScalarFunction).GetArgs()[1].(*ScalarFunction).RetType.GetFlag() + require.False(t, mysql.HasParseToJSONFlag(flagInCast)) +} + +func TestConstant(t *testing.T) { + ctx := createContext(t) + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + require.False(t, NewZero().IsCorrelated()) + require.True(t, NewZero().ConstItem(sc)) + require.True(t, NewZero().Decorrelate(nil).Equal(ctx, NewZero())) + require.Equal(t, []byte{0x0, 0x8, 0x0}, NewZero().HashCode(sc)) + require.False(t, NewZero().Equal(ctx, NewOne())) + res, err := NewZero().MarshalJSON() + require.NoError(t, err) + require.Equal(t, []byte{0x22, 0x30, 0x22}, res) +} + +func TestIsBinaryLiteral(t *testing.T) { + col := &Column{RetType: types.NewFieldType(mysql.TypeEnum)} + require.False(t, IsBinaryLiteral(col)) + col.RetType.SetType(mysql.TypeSet) + require.False(t, IsBinaryLiteral(col)) + col.RetType.SetType(mysql.TypeBit) + require.False(t, IsBinaryLiteral(col)) + col.RetType.SetType(mysql.TypeDuration) + require.False(t, IsBinaryLiteral(col)) + + con := &Constant{RetType: types.NewFieldType(mysql.TypeVarString), Value: types.NewBinaryLiteralDatum([]byte{byte(0), byte(1)})} + require.True(t, IsBinaryLiteral(con)) + con.Value = types.NewIntDatum(1) + require.False(t, IsBinaryLiteral(col)) +} + +func TestConstItem(t *testing.T) { + ctx := createContext(t) + sf := newFunction(ast.Rand) + require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) + sf = newFunction(ast.UUID) + require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) + sf = newFunction(ast.GetParam, NewOne()) + require.False(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) + sf = newFunction(ast.Abs, NewOne()) + require.True(t, sf.ConstItem(ctx.GetSessionVars().StmtCtx)) +} + +func TestVectorizable(t *testing.T) { + exprs := make([]Expression, 0, 4) + sf := newFunction(ast.Rand) + column := &Column{ + UniqueID: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + exprs = append(exprs, sf) + exprs = append(exprs, NewOne()) + exprs = append(exprs, NewNull()) + exprs = append(exprs, column) + require.True(t, Vectorizable(exprs)) + + column0 := &Column{ + UniqueID: 1, + RetType: types.NewFieldType(mysql.TypeString), + } + column1 := &Column{ + UniqueID: 2, + RetType: types.NewFieldType(mysql.TypeString), + } + column2 := &Column{ + UniqueID: 3, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + exprs = exprs[:0] + sf = newFunction(ast.SetVar, column0, column1) + exprs = append(exprs, sf) + require.False(t, Vectorizable(exprs)) + + exprs = exprs[:0] + sf = newFunction(ast.GetVar, column0) + exprs = append(exprs, sf) + require.False(t, Vectorizable(exprs)) + + exprs = exprs[:0] + sf = newFunction(ast.NextVal, column0) + exprs = append(exprs, sf) + sf = newFunction(ast.LastVal, column0) + exprs = append(exprs, sf) + sf = newFunction(ast.SetVal, column1, column2) + exprs = append(exprs, sf) + require.False(t, Vectorizable(exprs)) +} + +type testTableBuilder struct { + tableName string + columnNames []string + tps []byte + flags []uint +} + +func newTestTableBuilder(tableName string) *testTableBuilder { + return &testTableBuilder{tableName: tableName} +} + +func (builder *testTableBuilder) add(name string, tp byte, flag uint) *testTableBuilder { + builder.columnNames = append(builder.columnNames, name) + builder.tps = append(builder.tps, tp) + builder.flags = append(builder.flags, flag) + return builder +} + +func (builder *testTableBuilder) build() *model.TableInfo { + ti := &model.TableInfo{ + ID: 1, + Name: model.NewCIStr(builder.tableName), + State: model.StatePublic, + } + for i, colName := range builder.columnNames { + tp := builder.tps[i] + fieldType := types.NewFieldType(tp) + flen, decimal := mysql.GetDefaultFieldLengthAndDecimal(tp) + fieldType.SetFlen(flen) + fieldType.SetDecimal(decimal) + charset, collate := types.DefaultCharsetForType(tp) + fieldType.SetCharset(charset) + fieldType.SetCollate(collate) + fieldType.SetFlag(builder.flags[i]) + ti.Columns = append(ti.Columns, &model.ColumnInfo{ + ID: int64(i + 1), + Name: model.NewCIStr(colName), + Offset: i, + FieldType: *fieldType, + State: model.StatePublic, + }) + } + return ti +} + +func tableInfoToSchemaForTest(tableInfo *model.TableInfo) *Schema { + columns := tableInfo.Columns + schema := NewSchema(make([]*Column, 0, len(columns))...) + for i, col := range columns { + schema.Append(&Column{ + UniqueID: int64(i), + ID: col.ID, + RetType: &col.FieldType, + }) + } + return schema +} + +func TestEvalExpr(t *testing.T) { + ctx := createContext(t) + eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} + tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} + for i := 0; i < len(tNames); i++ { + ft := eType2FieldType(eTypes[i]) + colExpr := &Column{Index: 0, RetType: ft} + input := chunk.New([]*types.FieldType{ft}, 1024, 1024) + fillColumnWithGener(eTypes[i], input, 0, nil) + colBuf := chunk.NewColumn(ft, 1024) + colBuf2 := chunk.NewColumn(ft, 1024) + var err error + require.True(t, colExpr.Vectorized()) + ctx.GetSessionVars().EnableVectorizedExpression = false + err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf) + require.NoError(t, err) + ctx.GetSessionVars().EnableVectorizedExpression = true + err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf2) + require.NoError(t, err) + for j := 0; j < 1024; j++ { + isNull := colBuf.IsNull(j) + isNull2 := colBuf2.IsNull(j) + require.Equal(t, isNull2, isNull) + if isNull { + continue + } + require.Equal(t, string(colBuf2.GetRaw(j)), string(colBuf.GetRaw(j))) + } + } +} + +func TestExpressionMemeoryUsage(t *testing.T) { + c1 := &Column{OrigName: "Origin"} + c2 := Column{OrigName: "OriginName"} + require.Greater(t, c2.MemoryUsage(), c1.MemoryUsage()) + c1 = nil + require.Equal(t, c1.MemoryUsage(), int64(0)) + + c3 := Constant{Value: types.NewIntDatum(1)} + c4 := Constant{Value: types.NewStringDatum("11")} + require.Greater(t, c4.MemoryUsage(), c3.MemoryUsage()) +} diff --git a/pkg/expression/extension.go b/pkg/expression/extension.go new file mode 100644 index 0000000000000..4dc2d11cf0ab1 --- /dev/null +++ b/pkg/expression/extension.go @@ -0,0 +1,209 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "context" + "strings" + "sync" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sem" +) + +var extensionFuncs sync.Map + +func registerExtensionFunc(def *extension.FunctionDef) error { + if def == nil { + return errors.New("extension function def is nil") + } + + if err := def.Validate(); err != nil { + return err + } + + lowerName := strings.ToLower(def.Name) + if _, ok := funcs[lowerName]; ok { + return errors.Errorf("extension function name '%s' conflict with builtin", def.Name) + } + + class, err := newExtensionFuncClass(def) + if err != nil { + return err + } + + _, exist := extensionFuncs.LoadOrStore(lowerName, class) + if exist { + return errors.Errorf("duplicated extension function name '%s'", def.Name) + } + + return nil +} + +func removeExtensionFunc(name string) { + extensionFuncs.Delete(name) +} + +type extensionFuncClass struct { + baseFunctionClass + funcDef extension.FunctionDef + flen int +} + +func newExtensionFuncClass(def *extension.FunctionDef) (*extensionFuncClass, error) { + var flen int + switch def.EvalTp { + case types.ETString: + flen = mysql.MaxFieldVarCharLength + if def.EvalStringFunc == nil { + return nil, errors.New("eval function is nil") + } + case types.ETInt: + flen = mysql.MaxIntWidth + if def.EvalIntFunc == nil { + return nil, errors.New("eval function is nil") + } + default: + return nil, errors.Errorf("unsupported extension function ret type: '%v'", def.EvalTp) + } + + maxArgs := len(def.ArgTps) + minArgs := maxArgs - def.OptionalArgsLen + return &extensionFuncClass{ + baseFunctionClass: baseFunctionClass{def.Name, minArgs, maxArgs}, + flen: flen, + funcDef: *def, + }, nil +} + +func (c *extensionFuncClass) getFunction(ctx sessionctx.Context, args []Expression) (builtinFunc, error) { + if err := c.checkPrivileges(ctx); err != nil { + return nil, err + } + + if err := c.verifyArgs(args); err != nil { + return nil, err + } + bf, err := newBaseBuiltinFuncWithTp(ctx, c.funcName, args, c.funcDef.EvalTp, c.funcDef.ArgTps[:len(args)]...) + if err != nil { + return nil, err + } + bf.tp.SetFlen(c.flen) + sig := &extensionFuncSig{context.TODO(), bf, c.funcDef} + return sig, nil +} + +func (c *extensionFuncClass) checkPrivileges(ctx sessionctx.Context) error { + fn := c.funcDef.RequireDynamicPrivileges + if fn == nil { + return nil + } + + semEnabled := sem.IsEnabled() + privs := fn(semEnabled) + if len(privs) == 0 { + return nil + } + + manager := privilege.GetPrivilegeManager(ctx) + activeRoles := ctx.GetSessionVars().ActiveRoles + + for _, priv := range privs { + if !manager.RequestDynamicVerification(activeRoles, priv, false) { + msg := priv + if !semEnabled { + msg = "SUPER or " + msg + } + return errSpecificAccessDenied.GenWithStackByArgs(msg) + } + } + + return nil +} + +var _ extension.FunctionContext = &extensionFuncSig{} + +type extensionFuncSig struct { + context.Context + baseBuiltinFunc + extension.FunctionDef +} + +func (b *extensionFuncSig) Clone() builtinFunc { + newSig := &extensionFuncSig{} + newSig.cloneFrom(&b.baseBuiltinFunc) + newSig.FunctionDef = b.FunctionDef + return newSig +} + +func (b *extensionFuncSig) evalString(row chunk.Row) (string, bool, error) { + if b.EvalTp == types.ETString { + return b.EvalStringFunc(b, row) + } + return b.baseBuiltinFunc.evalString(row) +} + +func (b *extensionFuncSig) evalInt(row chunk.Row) (int64, bool, error) { + if b.EvalTp == types.ETInt { + return b.EvalIntFunc(b, row) + } + return b.baseBuiltinFunc.evalInt(row) +} + +func (b *extensionFuncSig) EvalArgs(row chunk.Row) ([]types.Datum, error) { + if len(b.args) == 0 { + return nil, nil + } + + result := make([]types.Datum, 0, len(b.args)) + for _, arg := range b.args { + val, err := arg.Eval(row) + if err != nil { + return nil, err + } + result = append(result, val) + } + + return result, nil +} + +func (b *extensionFuncSig) ConnectionInfo() *variable.ConnectionInfo { + return b.ctx.GetSessionVars().ConnectionInfo +} + +func (b *extensionFuncSig) User() *auth.UserIdentity { + return b.ctx.GetSessionVars().User +} + +func (b *extensionFuncSig) ActiveRoles() []*auth.RoleIdentity { + return b.ctx.GetSessionVars().ActiveRoles +} + +func (b *extensionFuncSig) CurrentDB() string { + return b.ctx.GetSessionVars().CurrentDB +} + +func init() { + extension.RegisterExtensionFunc = registerExtensionFunc + extension.RemoveExtensionFunc = removeExtensionFunc +} diff --git a/expression/function_traits.go b/pkg/expression/function_traits.go similarity index 99% rename from expression/function_traits.go rename to pkg/expression/function_traits.go index f6ddceafea8b7..566da093cbab3 100644 --- a/expression/function_traits.go +++ b/pkg/expression/function_traits.go @@ -15,8 +15,8 @@ package expression import ( - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/opcode" ) // UnCacheableFunctions stores functions which can not be cached to plan cache. diff --git a/expression/function_traits_test.go b/pkg/expression/function_traits_test.go similarity index 94% rename from expression/function_traits_test.go rename to pkg/expression/function_traits_test.go index 703739ca10e5f..18f50927d499e 100644 --- a/expression/function_traits_test.go +++ b/pkg/expression/function_traits_test.go @@ -17,7 +17,7 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/expression/generator/compare_vec.go b/pkg/expression/generator/compare_vec.go similarity index 98% rename from expression/generator/compare_vec.go rename to pkg/expression/generator/compare_vec.go index 4f433330a12a5..63687d4b99fc8 100644 --- a/expression/generator/compare_vec.go +++ b/pkg/expression/generator/compare_vec.go @@ -26,7 +26,7 @@ import ( "path/filepath" "text/template" - . "github.com/pingcap/tidb/expression/generator/helper" + . "github.com/pingcap/tidb/pkg/expression/generator/helper" ) const header = `// Copyright 2019 PingCAP, Inc. @@ -53,8 +53,8 @@ const newLine = "\n" const builtinCompareImports = `import ( "cmp" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) ` @@ -300,8 +300,8 @@ func (b *builtin{{ .compare.CompareName }}{{ .type.TypeName }}Sig) vectorized() const builtinCompareVecTestHeader = `import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecGeneratedBuiltinCompareCases = map[string][]vecExprBenchCase{ diff --git a/expression/generator/control_vec.go b/pkg/expression/generator/control_vec.go similarity index 96% rename from expression/generator/control_vec.go rename to pkg/expression/generator/control_vec.go index 454931bde2ab2..465e4e7ceb3b1 100644 --- a/expression/generator/control_vec.go +++ b/pkg/expression/generator/control_vec.go @@ -25,7 +25,7 @@ import ( "path/filepath" "text/template" - . "github.com/pingcap/tidb/expression/generator/helper" + . "github.com/pingcap/tidb/pkg/expression/generator/helper" ) const header = `// Copyright 2019 PingCAP, Inc. @@ -49,8 +49,8 @@ package expression import ( "time" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // NOTE: Control expressions optionally evaluate some branches depending on conditions, but vectorization executes all @@ -495,8 +495,8 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var defaultControlIntGener = &controlIntGener{zeroRation: 0.3, defaultGener: *newDefaultGener(0.3, types.ETInt)} @@ -561,17 +561,17 @@ func BenchmarkVectorizedBuiltin{{.Category}}FuncGenerated(b *testing.B) { `)) type typeContext struct { - // Describe the name of "github.com/pingcap/tidb/types".ET{{ .ETName }} + // Describe the name of "github.com/pingcap/tidb/pkg/types".ET{{ .ETName }} ETName string - // Describe the name of "github.com/pingcap/tidb/expression".VecExpr.VecEval{{ .TypeName }} + // Describe the name of "github.com/pingcap/tidb/pkg/expression".VecExpr.VecEval{{ .TypeName }} // If undefined, it's same as ETName. TypeName string - // Describe the name of "github.com/pingcap/tidb/util/chunk".*Column.Append{{ .TypeNameInColumn }}, + // Describe the name of "github.com/pingcap/tidb/pkg/util/chunk".*Column.Append{{ .TypeNameInColumn }}, // Resize{{ .TypeNameInColumn }}, Reserve{{ .TypeNameInColumn }}, Get{{ .TypeNameInColumn }} and // {{ .TypeNameInColumn }}s. // If undefined, it's same as TypeName. TypeNameInColumn string - // Same as "github.com/pingcap/tidb/util/chunk".getFixedLen() + // Same as "github.com/pingcap/tidb/pkg/util/chunk".getFixedLen() Fixed bool } diff --git a/pkg/expression/generator/helper/BUILD.bazel b/pkg/expression/generator/helper/BUILD.bazel new file mode 100644 index 0000000000000..f8c81a9c0ca0d --- /dev/null +++ b/pkg/expression/generator/helper/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "helper", + srcs = ["helper.go"], + importpath = "github.com/pingcap/tidb/pkg/expression/generator/helper", + visibility = ["//visibility:public"], +) diff --git a/pkg/expression/generator/helper/helper.go b/pkg/expression/generator/helper/helper.go new file mode 100644 index 0000000000000..d56b4c118edb4 --- /dev/null +++ b/pkg/expression/generator/helper/helper.go @@ -0,0 +1,49 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package helper + +// TypeContext is the template context for each "github.com/pingcap/tidb/pkg/types".EvalType . +type TypeContext struct { + // Describe the name of "github.com/pingcap/tidb/pkg/types".ET{{ .ETName }} . + ETName string + // Describe the name of "github.com/pingcap/tidb/pkg/expression".VecExpr.VecEval{{ .TypeName }} . + TypeName string + // Describe the name of "github.com/pingcap/tidb/pkg/util/chunk".*Column.Append{{ .TypeNameInColumn }}, + // Resize{{ .TypeNameInColumn }}, Reserve{{ .TypeNameInColumn }}, Get{{ .TypeNameInColumn }} and + // {{ .TypeNameInColumn }}s. + // If undefined, it's same as TypeName. + TypeNameInColumn string + // Describe the type name in golang. + TypeNameGo string + // Same as "github.com/pingcap/tidb/pkg/util/chunk".getFixedLen() . + Fixed bool +} + +var ( + // TypeInt represents the template context of types.ETInt . + TypeInt = TypeContext{ETName: "Int", TypeName: "Int", TypeNameInColumn: "Int64", TypeNameGo: "int64", Fixed: true} + // TypeReal represents the template context of types.ETReal . + TypeReal = TypeContext{ETName: "Real", TypeName: "Real", TypeNameInColumn: "Float64", TypeNameGo: "float64", Fixed: true} + // TypeDecimal represents the template context of types.ETDecimal . + TypeDecimal = TypeContext{ETName: "Decimal", TypeName: "Decimal", TypeNameInColumn: "Decimal", TypeNameGo: "types.MyDecimal", Fixed: true} + // TypeString represents the template context of types.ETString . + TypeString = TypeContext{ETName: "String", TypeName: "String", TypeNameInColumn: "String", TypeNameGo: "string", Fixed: false} + // TypeDatetime represents the template context of types.ETDatetime . + TypeDatetime = TypeContext{ETName: "Datetime", TypeName: "Time", TypeNameInColumn: "Time", TypeNameGo: "types.Time", Fixed: true} + // TypeDuration represents the template context of types.ETDuration . + TypeDuration = TypeContext{ETName: "Duration", TypeName: "Duration", TypeNameInColumn: "GoDuration", TypeNameGo: "time.Duration", Fixed: true} + // TypeJSON represents the template context of types.ETJson . + TypeJSON = TypeContext{ETName: "Json", TypeName: "JSON", TypeNameInColumn: "JSON", TypeNameGo: "json.BinaryJSON", Fixed: false} +) diff --git a/expression/generator/other_vec.go b/pkg/expression/generator/other_vec.go similarity index 97% rename from expression/generator/other_vec.go rename to pkg/expression/generator/other_vec.go index 6c7943682b56c..93a0037d70bbe 100644 --- a/expression/generator/other_vec.go +++ b/pkg/expression/generator/other_vec.go @@ -25,7 +25,7 @@ import ( "path/filepath" "text/template" - . "github.com/pingcap/tidb/expression/generator/helper" + . "github.com/pingcap/tidb/pkg/expression/generator/helper" ) const header = `// Copyright 2019 PingCAP, Inc. @@ -52,10 +52,10 @@ const newLine = "\n" const builtinOtherImports = `import ( "cmp" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" ) ` @@ -286,9 +286,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) type inGener struct { diff --git a/expression/generator/string_vec.go b/pkg/expression/generator/string_vec.go similarity index 96% rename from expression/generator/string_vec.go rename to pkg/expression/generator/string_vec.go index 6e1bf90071684..dc1f96940c883 100644 --- a/expression/generator/string_vec.go +++ b/pkg/expression/generator/string_vec.go @@ -26,7 +26,7 @@ import ( "path/filepath" "text/template" - . "github.com/pingcap/tidb/expression/generator/helper" + . "github.com/pingcap/tidb/pkg/expression/generator/helper" ) const header = `// Copyright 2021 PingCAP, Inc. @@ -51,7 +51,7 @@ package expression const newLine = "\n" const builtinStringImports = `import ( - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/util/chunk" ) ` @@ -117,8 +117,8 @@ var builtinStringVecTestTpl = template.Must(template.New("").Parse(` import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) var vecGeneratedBuiltinStringCases = map[string][]vecExprBenchCase{ diff --git a/expression/generator/time_vec.go b/pkg/expression/generator/time_vec.go similarity index 99% rename from expression/generator/time_vec.go rename to pkg/expression/generator/time_vec.go index 73067419f8cfc..39f11261056fd 100644 --- a/expression/generator/time_vec.go +++ b/pkg/expression/generator/time_vec.go @@ -25,7 +25,7 @@ import ( "path/filepath" "text/template" - . "github.com/pingcap/tidb/expression/generator/helper" + . "github.com/pingcap/tidb/pkg/expression/generator/helper" ) var addOrSubTime = template.Must(template.New("").Parse(` @@ -49,10 +49,10 @@ var addOrSubTime = template.Must(template.New("").Parse(` package expression import ( - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) {{ end }} {{ define "SetNull" }}{{if .Output.Fixed}}result.SetNull(i, true){{else}}result.AppendNull(){{end}} // fixed: {{.Output.Fixed }}{{ end }} @@ -575,9 +575,9 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) type gener struct { diff --git a/expression/grouping_sets.go b/pkg/expression/grouping_sets.go similarity index 98% rename from expression/grouping_sets.go rename to pkg/expression/grouping_sets.go index 85218d41c8789..2aedfb0f7412e 100644 --- a/expression/grouping_sets.go +++ b/pkg/expression/grouping_sets.go @@ -17,12 +17,12 @@ package expression import ( "strings" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - fd "github.com/pingcap/tidb/planner/funcdep" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + fd "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" ) diff --git a/expression/grouping_sets_test.go b/pkg/expression/grouping_sets_test.go similarity index 98% rename from expression/grouping_sets_test.go rename to pkg/expression/grouping_sets_test.go index 6fe87ca42b561..4f270cded4b97 100644 --- a/expression/grouping_sets_test.go +++ b/pkg/expression/grouping_sets_test.go @@ -17,10 +17,10 @@ package expression import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/pkg/expression/helper.go b/pkg/expression/helper.go new file mode 100644 index 0000000000000..2cb0f161bb13f --- /dev/null +++ b/pkg/expression/helper.go @@ -0,0 +1,191 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "context" + "math" + "strings" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/tikv/client-go/v2/oracle" + "go.uber.org/zap" +) + +func boolToInt64(v bool) int64 { + if v { + return 1 + } + return 0 +} + +// IsValidCurrentTimestampExpr returns true if exprNode is a valid CurrentTimestamp expression. +// Here `valid` means it is consistent with the given fieldType's decimal. +func IsValidCurrentTimestampExpr(exprNode ast.ExprNode, fieldType *types.FieldType) bool { + fn, isFuncCall := exprNode.(*ast.FuncCallExpr) + if !isFuncCall || fn.FnName.L != ast.CurrentTimestamp { + return false + } + + containsArg := len(fn.Args) > 0 + // Fsp represents fractional seconds precision. + containsFsp := fieldType != nil && fieldType.GetDecimal() > 0 + var isConsistent bool + if containsArg { + v, ok := fn.Args[0].(*driver.ValueExpr) + isConsistent = ok && fieldType != nil && v.Datum.GetInt64() == int64(fieldType.GetDecimal()) + } + + return (containsArg && isConsistent) || (!containsArg && !containsFsp) +} + +// GetTimeCurrentTimestamp is used for generating a timestamp for some special cases: cast null value to timestamp type with not null flag. +func GetTimeCurrentTimestamp(ctx sessionctx.Context, tp byte, fsp int) (d types.Datum, err error) { + var t types.Time + t, err = getTimeCurrentTimeStamp(ctx, tp, fsp) + if err != nil { + return d, err + } + d.SetMysqlTime(t) + return d, nil +} + +func getTimeCurrentTimeStamp(ctx sessionctx.Context, tp byte, fsp int) (t types.Time, err error) { + value := types.NewTime(types.ZeroCoreTime, tp, fsp) + defaultTime, err := getStmtTimestamp(ctx) + if err != nil { + return value, err + } + value.SetCoreTime(types.FromGoTime(defaultTime.Truncate(time.Duration(math.Pow10(9-fsp)) * time.Nanosecond))) + if tp == mysql.TypeTimestamp || tp == mysql.TypeDatetime || tp == mysql.TypeDate { + err = value.ConvertTimeZone(time.Local, ctx.GetSessionVars().Location()) + if err != nil { + return value, err + } + } + return value, nil +} + +// GetTimeValue gets the time value with type tp. +func GetTimeValue(ctx sessionctx.Context, v interface{}, tp byte, fsp int, explicitTz *time.Location) (d types.Datum, err error) { + var value types.Time + + sc := ctx.GetSessionVars().StmtCtx + switch x := v.(type) { + case string: + lowerX := strings.ToLower(x) + if lowerX == ast.CurrentTimestamp || lowerX == ast.CurrentDate { + if value, err = getTimeCurrentTimeStamp(ctx, tp, fsp); err != nil { + return d, err + } + } else if lowerX == types.ZeroDatetimeStr { + value, err = types.ParseTimeFromNum(sc, 0, tp, fsp) + terror.Log(err) + } else { + value, err = types.ParseTime(sc, x, tp, fsp, explicitTz) + if err != nil { + return d, err + } + } + case *driver.ValueExpr: + switch x.Kind() { + case types.KindString: + value, err = types.ParseTime(sc, x.GetString(), tp, fsp, nil) + if err != nil { + return d, err + } + case types.KindInt64: + value, err = types.ParseTimeFromNum(sc, x.GetInt64(), tp, fsp) + if err != nil { + return d, err + } + case types.KindNull: + return d, nil + default: + return d, errDefaultValue + } + case *ast.FuncCallExpr: + if x.FnName.L == ast.CurrentTimestamp || x.FnName.L == ast.CurrentDate { + d.SetString(strings.ToUpper(x.FnName.L), mysql.DefaultCollationName) + return d, nil + } + return d, errDefaultValue + case *ast.UnaryOperationExpr: + // support some expression, like `-1` + v, err := EvalAstExpr(ctx, x) + if err != nil { + return d, err + } + ft := types.NewFieldType(mysql.TypeLonglong) + xval, err := v.ConvertTo(ctx.GetSessionVars().StmtCtx, ft) + if err != nil { + return d, err + } + + value, err = types.ParseTimeFromNum(sc, xval.GetInt64(), tp, fsp) + if err != nil { + return d, err + } + default: + return d, nil + } + d.SetMysqlTime(value) + return d, nil +} + +// if timestamp session variable set, use session variable as current time, otherwise use cached time +// during one sql statement, the "current_time" should be the same +func getStmtTimestamp(ctx sessionctx.Context) (time.Time, error) { + failpoint.Inject("injectNow", func(val failpoint.Value) { + v := time.Unix(int64(val.(int)), 0) + failpoint.Return(v, nil) + }) + + if ctx != nil { + staleTSO, err := ctx.GetSessionVars().StmtCtx.GetStaleTSO() + if staleTSO != 0 && err == nil { + return oracle.GetTimeFromTS(staleTSO), nil + } else if err != nil { + logutil.BgLogger().Error("get stale tso failed", zap.Error(err)) + } + } + + now := time.Now() + + if ctx == nil { + return now, nil + } + + sessionVars := ctx.GetSessionVars() + timestampStr, err := sessionVars.GetSessionOrGlobalSystemVar(context.Background(), "timestamp") + if err != nil { + return now, err + } + + timestamp, err := types.StrToFloat(sessionVars.StmtCtx, timestampStr, false) + if err != nil { + return time.Time{}, err + } + seconds, fractionalSeconds := math.Modf(timestamp) + return time.Unix(int64(seconds), int64(fractionalSeconds*float64(time.Second))), nil +} diff --git a/pkg/expression/helper_test.go b/pkg/expression/helper_test.go new file mode 100644 index 0000000000000..eea37b00f9461 --- /dev/null +++ b/pkg/expression/helper_test.go @@ -0,0 +1,183 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "fmt" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestGetTimeValue(t *testing.T) { + ctx := mock.NewContext() + v, err := GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + + require.Equal(t, types.KindMysqlTime, v.Kind()) + timeValue := v.GetMysqlTime() + require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) + + sessionVars := ctx.GetSessionVars() + err = sessionVars.SetSystemVar("timestamp", "0") + require.NoError(t, err) + v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + + require.Equal(t, types.KindMysqlTime, v.Kind()) + timeValue = v.GetMysqlTime() + require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) + + err = sessionVars.SetSystemVar("timestamp", "0") + require.NoError(t, err) + v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + + require.Equal(t, types.KindMysqlTime, v.Kind()) + timeValue = v.GetMysqlTime() + require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) + + err = sessionVars.SetSystemVar("timestamp", "") + require.Error(t, err, "Incorrect argument type to variable 'timestamp'") + v, err = GetTimeValue(ctx, "2012-12-12 00:00:00", mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + + require.Equal(t, types.KindMysqlTime, v.Kind()) + timeValue = v.GetMysqlTime() + require.Equal(t, "2012-12-12 00:00:00", timeValue.String()) + + // trigger the stmt context cache. + err = sessionVars.SetSystemVar("timestamp", "0") + require.NoError(t, err) + + v1, err := GetTimeCurrentTimestamp(ctx, mysql.TypeTimestamp, types.MinFsp) + require.NoError(t, err) + + v2, err := GetTimeCurrentTimestamp(ctx, mysql.TypeTimestamp, types.MinFsp) + require.NoError(t, err) + + require.Equal(t, v1, v2) + + err = sessionVars.SetSystemVar("timestamp", "1234") + require.NoError(t, err) + + tbls := []struct { + Expr interface{} + Ret interface{} + }{ + {"2012-12-12 00:00:00", "2012-12-12 00:00:00"}, + {ast.CurrentTimestamp, time.Unix(1234, 0).Format(types.TimeFormat)}, + {types.ZeroDatetimeStr, "0000-00-00 00:00:00"}, + {ast.NewValueExpr("2012-12-12 00:00:00", charset.CharsetUTF8MB4, charset.CollationUTF8MB4), "2012-12-12 00:00:00"}, + {ast.NewValueExpr(int64(0), "", ""), "0000-00-00 00:00:00"}, + {ast.NewValueExpr(nil, "", ""), nil}, + {&ast.FuncCallExpr{FnName: model.NewCIStr(ast.CurrentTimestamp)}, strings.ToUpper(ast.CurrentTimestamp)}, + // {&ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(int64(0))}, "0000-00-00 00:00:00"}, + } + + for i, tbl := range tbls { + comment := fmt.Sprintf("expr: %d", i) + v, err := GetTimeValue(ctx, tbl.Expr, mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + + switch v.Kind() { + case types.KindMysqlTime: + require.EqualValues(t, tbl.Ret, v.GetMysqlTime().String(), comment) + default: + require.EqualValues(t, tbl.Ret, v.GetValue(), comment) + } + } + + errTbl := []struct { + Expr interface{} + }{ + {"2012-13-12 00:00:00"}, + {ast.NewValueExpr("2012-13-12 00:00:00", charset.CharsetUTF8MB4, charset.CollationUTF8MB4)}, + {ast.NewValueExpr(int64(1), "", "")}, + {&ast.FuncCallExpr{FnName: model.NewCIStr("xxx")}}, + // {&ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(int64(1))}}, + } + + for _, tbl := range errTbl { + _, err := GetTimeValue(ctx, tbl.Expr, mysql.TypeTimestamp, types.MinFsp, nil) + require.Error(t, err) + } +} + +func TestIsCurrentTimestampExpr(t *testing.T) { + buildTimestampFuncCallExpr := func(i int64) *ast.FuncCallExpr { + var args []ast.ExprNode + if i != 0 { + args = []ast.ExprNode{&driver.ValueExpr{Datum: types.NewIntDatum(i)}} + } + return &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: args} + } + + v := IsValidCurrentTimestampExpr(ast.NewValueExpr("abc", charset.CharsetUTF8MB4, charset.CollationUTF8MB4), nil) + require.False(t, v) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), nil) + require.True(t, v) + ft := &types.FieldType{} + ft.SetDecimal(3) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(3), ft) + require.True(t, v) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(1), ft) + require.False(t, v) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(0), ft) + require.False(t, v) + + ft1 := &types.FieldType{} + ft1.SetDecimal(0) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), ft1) + require.False(t, v) + v = IsValidCurrentTimestampExpr(buildTimestampFuncCallExpr(2), nil) + require.False(t, v) +} + +func TestCurrentTimestampTimeZone(t *testing.T) { + ctx := mock.NewContext() + sessionVars := ctx.GetSessionVars() + + err := sessionVars.SetSystemVar("timestamp", "1234") + require.NoError(t, err) + err = sessionVars.SetSystemVar("time_zone", "+00:00") + require.NoError(t, err) + v, err := GetTimeValue(ctx, ast.CurrentTimestamp, mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + require.EqualValues(t, types.NewTime( + types.FromDate(1970, 1, 1, 0, 20, 34, 0), + mysql.TypeTimestamp, types.DefaultFsp), + v.GetMysqlTime()) + + // CurrentTimestamp from "timestamp" session variable is based on UTC, so change timezone + // would get different value. + err = sessionVars.SetSystemVar("time_zone", "+08:00") + require.NoError(t, err) + v, err = GetTimeValue(ctx, ast.CurrentTimestamp, mysql.TypeTimestamp, types.MinFsp, nil) + require.NoError(t, err) + require.EqualValues(t, types.NewTime( + types.FromDate(1970, 1, 1, 8, 20, 34, 0), + mysql.TypeTimestamp, types.DefaultFsp), + v.GetMysqlTime()) +} diff --git a/pkg/expression/integration_test/BUILD.bazel b/pkg/expression/integration_test/BUILD.bazel new file mode 100644 index 0000000000000..371bab3e3de0f --- /dev/null +++ b/pkg/expression/integration_test/BUILD.bazel @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "integration_test_test", + timeout = "short", + srcs = [ + "integration_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 28, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/sem", + "//pkg/util/timeutil", + "//pkg/util/versioninfo", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/expression/integration_test/README.md b/pkg/expression/integration_test/README.md similarity index 100% rename from expression/integration_test/README.md rename to pkg/expression/integration_test/README.md diff --git a/pkg/expression/integration_test/integration_test.go b/pkg/expression/integration_test/integration_test.go new file mode 100644 index 0000000000000..fa40790a7b78d --- /dev/null +++ b/pkg/expression/integration_test/integration_test.go @@ -0,0 +1,3273 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package integration_test + +import ( + "bytes" + "context" + "encoding/binary" + "encoding/hex" + "fmt" + "hash/crc32" + "math" + "math/rand" + "sort" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/versioninfo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" +) + +func TestGetLock(t *testing.T) { + ctx := context.Background() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // Increase pessimistic txn max retry count to make test more stable. + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + newCfg.PessimisticTxn.MaxRetryCount = 2048 + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + }() + + // No timeout specified + err := tk.ExecToErr("SELECT get_lock('testlock')") + require.Error(t, err) + terr := errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(mysql.ErrWrongParamcountToNativeFct), terr.Code()) + + // 0 timeout = immediate + // Negative timeout = convert to max value + tk.MustQuery("SELECT get_lock('testlock1', 0)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT get_lock('testlock2', -10)").Check(testkit.Rows("1")) + // show warnings: + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect get_lock value: '-10'")) + tk.MustQuery("SELECT release_lock('testlock1'), release_lock('testlock2')").Check(testkit.Rows("1 1")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) + + // GetLock/ReleaseLock with NULL name or '' name + rs, _ := tk.Exec("SELECT get_lock('', 10)") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + rs, _ = tk.Exec("SELECT get_lock(NULL, 10)") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + rs, _ = tk.Exec("SELECT release_lock('')") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + rs, _ = tk.Exec("SELECT release_lock(NULL)") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + // NULL timeout is fine (= unlimited) + tk.MustQuery("SELECT get_lock('aaa', NULL)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('aaa')").Check(testkit.Rows("1")) + + // GetLock in CAPS, release lock in different case. + tk.MustQuery("SELECT get_lock('aBC', -10)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('AbC')").Check(testkit.Rows("1")) + + // Release unacquired LOCK and previously released lock + tk.MustQuery("SELECT release_lock('randombytes')").Check(testkit.Rows("0")) + tk.MustQuery("SELECT release_lock('abc')").Check(testkit.Rows("0")) + + // GetLock with integer name, 64, character name. + tk.MustQuery("SELECT get_lock(1234, 10)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT get_lock(REPEAT('a', 64), 10)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock(1234), release_lock(REPEAT('aa', 32))").Check(testkit.Rows("1 1")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) + + // 65 character name + rs, _ = tk.Exec("SELECT get_lock(REPEAT('a', 65), 10)") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + rs, _ = tk.Exec("SELECT release_lock(REPEAT('a', 65))") + _, err = session.GetRows4Test(ctx, tk.Session(), rs) + require.Error(t, err) + terr = errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(errno.ErrUserLockWrongName), terr.Code()) + + // len should be based on character length, not byte length + // accented a character = 66 bytes but only 33 chars + tk.MustQuery("SELECT get_lock(REPEAT(unhex('C3A4'), 33), 10)") + tk.MustQuery("SELECT release_lock(REPEAT(unhex('C3A4'), 33))") + + // Floating point timeout. + tk.MustQuery("SELECT get_lock('nnn', 1.2)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('nnn')").Check(testkit.Rows("1")) + + // Multiple locks acquired in one statement. + // Release all locks and one not held lock + tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) + tk.MustQuery("SELECT release_lock('a1'),release_lock('a2'),release_lock('a3'), release_lock('random'), release_lock('a4')").Check(testkit.Rows("1 1 1 0 1")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) + + // Multiple locks acquired, released all at once. + tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("4")) + tk.MustQuery("SELECT release_lock('a1')").Check(testkit.Rows("0")) // lock is free + + // Multiple locks acquired, reference count increased, released all at once. + tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a3', 1.2), get_lock('a4', 1.2)").Check(testkit.Rows("1 1 1 1")) + tk.MustQuery("SELECT get_lock('a1', 1.2), get_lock('a2', 1.2), get_lock('a5', 1.2)").Check(testkit.Rows("1 1 1")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("7")) // 7 not 5, because the it includes ref count + tk.MustQuery("SELECT release_lock('a1')").Check(testkit.Rows("0")) // lock is free + tk.MustQuery("SELECT release_lock('a5')").Check(testkit.Rows("0")) // lock is free + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) + + // Test common cases: + // Get a lock, release it immediately. + // Try to release it again (its released) + tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) + + // Get a lock, acquire it again, release it twice. + tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) + + // Test someone else has the lock with short timeout. + tk2 := testkit.NewTestKit(t, store) + tk2.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("1")) + tk.MustQuery("SELECT get_lock('mygloballock', 1)").Check(testkit.Rows("0")) // someone else has the lock + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) // never had the lock + // try again + tk.MustQuery("SELECT get_lock('mygloballock', 0)").Check(testkit.Rows("0")) // someone else has the lock + tk.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("0")) // never had the lock + // release it + tk2.MustQuery("SELECT release_lock('mygloballock')").Check(testkit.Rows("1")) // works + + // Confirm all locks are released + tk2.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) + tk.MustQuery("SELECT release_all_locks()").Check(testkit.Rows("0")) +} + +func TestInfoBuiltin(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // for last_insert_id + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int auto_increment, a int, PRIMARY KEY (id))") + tk.MustExec("insert into t(a) values(1)") + result := tk.MustQuery("select last_insert_id();") + result.Check(testkit.Rows("1")) + tk.MustExec("insert into t values(2, 1)") + result = tk.MustQuery("select last_insert_id();") + result.Check(testkit.Rows("1")) + tk.MustExec("insert into t(a) values(1)") + result = tk.MustQuery("select last_insert_id();") + result.Check(testkit.Rows("3")) + + result = tk.MustQuery("select last_insert_id(5);") + result.Check(testkit.Rows("5")) + result = tk.MustQuery("select last_insert_id();") + result.Check(testkit.Rows("5")) + + // for found_rows + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int)") + tk.MustQuery("select * from t") // Test XSelectTableExec + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("1")) // Last query is found_rows(), it returns 1 row with value 0 + tk.MustExec("insert t values (1),(2),(2)") + tk.MustQuery("select * from t") + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("3")) + tk.MustQuery("select * from t where a = 0") + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("0")) + tk.MustQuery("select * from t where a = 1") + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("1")) + tk.MustQuery("select * from t where a like '2'") // Test SelectionExec + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("2")) + tk.MustQuery("show tables like 't'") + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("1")) + tk.MustQuery("select count(*) from t") // Test ProjectionExec + result = tk.MustQuery("select found_rows()") + result.Check(testkit.Rows("1")) + + // for database + result = tk.MustQuery("select database()") + result.Check(testkit.Rows("test")) + tk.MustExec("drop database test") + result = tk.MustQuery("select database()") + result.Check(testkit.Rows("")) + tk.MustExec("create database test") + tk.MustExec("use test") + + // for current_user + sessionVars := tk.Session().GetSessionVars() + originUser := sessionVars.User + sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "127.0.%%"} + result = tk.MustQuery("select current_user()") + result.Check(testkit.Rows("root@127.0.%%")) + sessionVars.User = originUser + + // for user + sessionVars.User = &auth.UserIdentity{Username: "root", Hostname: "localhost", AuthUsername: "root", AuthHostname: "127.0.%%"} + result = tk.MustQuery("select user()") + result.Check(testkit.Rows("root@localhost")) + sessionVars.User = originUser + + // for connection_id + originConnectionID := sessionVars.ConnectionID + sessionVars.ConnectionID = uint64(1) + result = tk.MustQuery("select connection_id()") + result.Check(testkit.Rows("1")) + sessionVars.ConnectionID = originConnectionID + + // for version + result = tk.MustQuery("select version()") + result.Check(testkit.Rows(mysql.ServerVersion)) + + // for tidb_version + result = tk.MustQuery("select tidb_version()") + tidbVersionResult := "" + for _, line := range result.Rows() { + tidbVersionResult += fmt.Sprint(line) + } + lines := strings.Split(tidbVersionResult, "\n") + assert.Equal(t, true, strings.Split(lines[0], " ")[2] == mysql.TiDBReleaseVersion, "errors in 'select tidb_version()'") + assert.Equal(t, true, strings.Split(lines[1], " ")[1] == versioninfo.TiDBEdition, "errors in 'select tidb_version()'") + + // for row_count + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, PRIMARY KEY (a))") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("0")) + tk.MustExec("insert into t(a, b) values(1, 11), (2, 22), (3, 33)") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("3")) + tk.MustExec("select * from t") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("-1")) + tk.MustExec("update t set b=22 where a=1") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("1")) + tk.MustExec("update t set b=22 where a=1") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("0")) + tk.MustExec("delete from t where a=2") + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("1")) + result = tk.MustQuery("select row_count();") + result.Check(testkit.Rows("-1")) + + // for benchmark + success := testkit.Rows("0") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int)") + result = tk.MustQuery(`select benchmark(3, benchmark(2, length("abc")))`) + result.Check(success) + err := tk.ExecToErr(`select benchmark(3, length("a", "b"))`) + require.Error(t, err) + // Quoted from https://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_benchmark + // Although the expression can be a subquery, it must return a single column and at most a single row. + // For example, BENCHMARK(10, (SELECT * FROM t)) will fail if the table t has more than one column or + // more than one row. + oneColumnQuery := "select benchmark(10, (select a from t))" + twoColumnQuery := "select benchmark(10, (select * from t))" + // rows * columns: + // 0 * 1, success; + result = tk.MustQuery(oneColumnQuery) + result.Check(success) + // 0 * 2, error; + err = tk.ExecToErr(twoColumnQuery) + require.Error(t, err) + // 1 * 1, success; + tk.MustExec("insert t values (1, 2)") + result = tk.MustQuery(oneColumnQuery) + result.Check(success) + // 1 * 2, error; + err = tk.ExecToErr(twoColumnQuery) + require.Error(t, err) + // 2 * 1, error; + tk.MustExec("insert t values (3, 4)") + err = tk.ExecToErr(oneColumnQuery) + require.Error(t, err) + // 2 * 2, error. + err = tk.ExecToErr(twoColumnQuery) + require.Error(t, err) + + result = tk.MustQuery("select tidb_is_ddl_owner()") + var ret int64 + if tk.Session().IsDDLOwner() { + ret = 1 + } + result.Check(testkit.Rows(fmt.Sprintf("%v", ret))) +} + +func TestColumnInfoModified(t *testing.T) { + store := testkit.CreateMockStore(t) + + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists tab0") + testKit.MustExec("CREATE TABLE tab0(col0 INTEGER, col1 INTEGER, col2 INTEGER)") + testKit.MustExec("SELECT + - (- CASE + col0 WHEN + CAST( col0 AS SIGNED ) THEN col1 WHEN 79 THEN NULL WHEN + - col1 THEN col0 / + col0 END ) * - 16 FROM tab0") + ctx := testKit.Session() + is := domain.GetDomain(ctx).InfoSchema() + tbl, _ := is.TableByName(model.NewCIStr("test"), model.NewCIStr("tab0")) + col := table.FindCol(tbl.Cols(), "col1") + require.Equal(t, mysql.TypeLong, col.GetType()) +} + +func TestFilterExtractFromDNF(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int)") + + tests := []struct { + exprStr string + result string + }{ + { + exprStr: "a = 1 or a = 1 or a = 1", + result: "[eq(test.t.a, 1)]", + }, + { + exprStr: "a = 1 or a = 1 or (a = 1 and b = 1)", + result: "[eq(test.t.a, 1)]", + }, + { + exprStr: "(a = 1 and a = 1) or a = 1 or b = 1", + result: "[or(or(and(eq(test.t.a, 1), eq(test.t.a, 1)), eq(test.t.a, 1)), eq(test.t.b, 1))]", + }, + { + exprStr: "(a = 1 and b = 2) or (a = 1 and b = 3) or (a = 1 and b = 4)", + result: "[eq(test.t.a, 1) or(eq(test.t.b, 2), or(eq(test.t.b, 3), eq(test.t.b, 4)))]", + }, + { + exprStr: "(a = 1 and b = 1 and c = 1) or (a = 1 and b = 1) or (a = 1 and b = 1 and c > 2 and c < 3)", + result: "[eq(test.t.a, 1) eq(test.t.b, 1)]", + }, + } + + ctx := context.Background() + for _, tt := range tests { + sql := "select * from t where " + tt.exprStr + sctx := tk.Session() + sc := sctx.GetSessionVars().StmtCtx + stmts, err := session.Parse(sctx, sql) + require.NoError(t, err, "error %v, for expr %s", err, tt.exprStr) + require.Len(t, stmts, 1) + ret := &plannercore.PreprocessorReturn{} + err = plannercore.Preprocess(context.Background(), sctx, stmts[0], plannercore.WithPreprocessorReturn(ret)) + require.NoError(t, err, "error %v, for resolve name, expr %s", err, tt.exprStr) + p, _, err := plannercore.BuildLogicalPlanForTest(ctx, sctx, stmts[0], ret.InfoSchema) + require.NoError(t, err, "error %v, for build plan, expr %s", err, tt.exprStr) + selection := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) + conds := make([]expression.Expression, len(selection.Conditions)) + for i, cond := range selection.Conditions { + conds[i] = expression.PushDownNot(sctx, cond) + } + afterFunc := expression.ExtractFiltersFromDNFs(sctx, conds) + sort.Slice(afterFunc, func(i, j int) bool { + return bytes.Compare(afterFunc[i].HashCode(sc), afterFunc[j].HashCode(sc)) < 0 + }) + require.Equal(t, fmt.Sprintf("%s", afterFunc), tt.result, "wrong result for expr: %s", tt.exprStr) + } +} + +func TestTiDBDecodeKeyFunc(t *testing.T) { + store := testkit.CreateMockStore(t) + + collate.SetNewCollationEnabledForTest(false) + defer collate.SetNewCollationEnabledForTest(true) + + tk := testkit.NewTestKit(t, store) + var result *testkit.Result + + // Row Keys + result = tk.MustQuery("select tidb_decode_key( '74800000000000002B5F72800000000000A5D3' )") + result.Check(testkit.Rows(`{"_tidb_rowid":42451,"table_id":"43"}`)) + result = tk.MustQuery("select tidb_decode_key( '74800000000000ffff5f7205bff199999999999a013131000000000000f9' )") + result.Check(testkit.Rows(`{"handle":"{1.1, 11}","table_id":65535}`)) + + // Index Keys + result = tk.MustQuery("select tidb_decode_key( '74800000000000019B5F698000000000000001015257303100000000FB013736383232313130FF3900000000000000F8010000000000000000F7' )") + result.Check(testkit.Rows(`{"index_id":1,"index_vals":"RW01, 768221109, ","table_id":411}`)) + result = tk.MustQuery("select tidb_decode_key( '7480000000000000695F698000000000000001038000000000004E20' )") + result.Check(testkit.Rows(`{"index_id":1,"index_vals":"20000","table_id":105}`)) + + // Table keys + result = tk.MustQuery("select tidb_decode_key( '7480000000000000FF4700000000000000F8' )") + result.Check(testkit.Rows(`{"table_id":71}`)) + + // Test invalid record/index key. + result = tk.MustQuery("select tidb_decode_key( '7480000000000000FF2E5F728000000011FFE1A3000000000000' )") + result.Check(testkit.Rows("7480000000000000FF2E5F728000000011FFE1A3000000000000")) + warns := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Len(t, warns, 1) + require.EqualError(t, warns[0].Err, "invalid key: 7480000000000000FF2E5F728000000011FFE1A3000000000000") + + // Test in real tables. + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a varchar(255), b int, c datetime, primary key (a, b, c));") + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + getTime := func(year, month, day int, timeType byte) types.Time { + ret := types.NewTime(types.FromDate(year, month, day, 0, 0, 0, 0), timeType, types.DefaultFsp) + return ret + } + buildCommonKeyFromData := func(tableID int64, data []types.Datum) string { + k, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, data...) + require.NoError(t, err) + h, err := kv.NewCommonHandle(k) + require.NoError(t, err) + k = tablecodec.EncodeRowKeyWithHandle(tableID, h) + return hex.EncodeToString(codec.EncodeBytes(nil, k)) + } + // split table t by ('bbbb', 10, '2020-01-01'); + data := []types.Datum{types.NewStringDatum("bbbb"), types.NewIntDatum(10), types.NewTimeDatum(getTime(2020, 1, 1, mysql.TypeDatetime))} + hexKey := buildCommonKeyFromData(tbl.Meta().ID, data) + sql := fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs := fmt.Sprintf(`{"handle":{"a":"bbbb","b":"10","c":"2020-01-01 00:00:00"},"table_id":%d}`, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) + + // split table t by ('bbbb', 10, null); + data = []types.Datum{types.NewStringDatum("bbbb"), types.NewIntDatum(10), types.NewDatum(nil)} + hexKey = buildCommonKeyFromData(tbl.Meta().ID, data) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + tk.MustQuery(sql).Check(testkit.Rows(hexKey)) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a varchar(255), b int, c datetime, index idx(a, b, c));") + dom = domain.GetDomain(tk.Session()) + is = dom.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + buildIndexKeyFromData := func(tableID, indexID int64, data []types.Datum) string { + k, err := codec.EncodeKey(tk.Session().GetSessionVars().StmtCtx, nil, data...) + require.NoError(t, err) + k = tablecodec.EncodeIndexSeekKey(tableID, indexID, k) + return hex.EncodeToString(codec.EncodeBytes(nil, k)) + } + // split table t index idx by ('aaaaa', 100, '2000-01-01'); + data = []types.Datum{types.NewStringDatum("aaaaa"), types.NewIntDatum(100), types.NewTimeDatum(getTime(2000, 1, 1, mysql.TypeDatetime))} + hexKey = buildIndexKeyFromData(tbl.Meta().ID, tbl.Indices()[0].Meta().ID, data) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + result = tk.MustQuery(sql) + rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"a":"aaaaa","b":"100","c":"2000-01-01 00:00:00"},"table_id":%d}`, tbl.Meta().ID) + result.Check(testkit.Rows(rs)) + // split table t index idx by (null, null, null); + data = []types.Datum{types.NewDatum(nil), types.NewDatum(nil), types.NewDatum(nil)} + hexKey = buildIndexKeyFromData(tbl.Meta().ID, tbl.Indices()[0].Meta().ID, data) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + result = tk.MustQuery(sql) + rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"a":null,"b":null,"c":null},"table_id":%d}`, tbl.Meta().ID) + result.Check(testkit.Rows(rs)) + + // https://github.com/pingcap/tidb/issues/27434. + hexKey = "7480000000000100375F69800000000000000103800000000001D4C1023B6458" + sql = fmt.Sprintf("select tidb_decode_key('%s')", hexKey) + tk.MustQuery(sql).Check(testkit.Rows(hexKey)) + + // https://github.com/pingcap/tidb/issues/33015. + hexKey = "74800000000000012B5F72800000000000A5D3" + sql = fmt.Sprintf("select tidb_decode_key('%s')", hexKey) + tk.MustQuery(sql).Check(testkit.Rows(`{"_tidb_rowid":42451,"table_id":"299"}`)) + + // Test the table with the nonclustered index. + const rowID = 10 + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int primary key nonclustered, b int, key bk (b));") + dom = domain.GetDomain(tk.Session()) + is = dom.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + buildTableRowKey := func(tableID, rowID int64) string { + return hex.EncodeToString( + codec.EncodeBytes( + nil, + tablecodec.EncodeRowKeyWithHandle(tableID, kv.IntHandle(rowID)), + )) + } + hexKey = buildTableRowKey(tbl.Meta().ID, rowID) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs = fmt.Sprintf(`{"_tidb_rowid":%d,"table_id":"%d"}`, rowID, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) + + // Test the table with the clustered index. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int primary key clustered, b int, key bk (b));") + dom = domain.GetDomain(tk.Session()) + is = dom.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + hexKey = buildTableRowKey(tbl.Meta().ID, rowID) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs = fmt.Sprintf(`{"%s":%d,"table_id":"%d"}`, tbl.Meta().GetPkName().String(), rowID, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) + + // Test partition table. + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int primary key clustered, b int, key bk (b)) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (1), PARTITION p1 VALUES LESS THAN (2));") + dom = domain.GetDomain(tk.Session()) + is = dom.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + require.NotNil(t, tbl.Meta().Partition) + hexKey = buildTableRowKey(tbl.Meta().Partition.Definitions[0].ID, rowID) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs = fmt.Sprintf(`{"%s":%d,"partition_id":%d,"table_id":"%d"}`, tbl.Meta().GetPkName().String(), rowID, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) + + hexKey = tablecodec.EncodeTablePrefix(tbl.Meta().Partition.Definitions[0].ID).String() + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs = fmt.Sprintf(`{"partition_id":%d,"table_id":%d}`, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) + + data = []types.Datum{types.NewIntDatum(100)} + hexKey = buildIndexKeyFromData(tbl.Meta().Partition.Definitions[0].ID, tbl.Indices()[0].Meta().ID, data) + sql = fmt.Sprintf("select tidb_decode_key( '%s' )", hexKey) + rs = fmt.Sprintf(`{"index_id":1,"index_vals":{"b":"100"},"partition_id":%d,"table_id":%d}`, tbl.Meta().Partition.Definitions[0].ID, tbl.Meta().ID) + tk.MustQuery(sql).Check(testkit.Rows(rs)) +} + +func TestIssue9710(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + getSAndMS := func(str string) (int, int) { + results := strings.Split(str, ":") + SAndMS := strings.Split(results[len(results)-1], ".") + var s, ms int + s, _ = strconv.Atoi(SAndMS[0]) + if len(SAndMS) > 1 { + ms, _ = strconv.Atoi(SAndMS[1]) + } + return s, ms + } + + for { + rs := tk.MustQuery("select now(), now(6), unix_timestamp(), unix_timestamp(now())") + s, ms := getSAndMS(rs.Rows()[0][1].(string)) + if ms < 500000 { + time.Sleep(time.Second / 10) + continue + } + + s1, _ := getSAndMS(rs.Rows()[0][0].(string)) + require.Equal(t, s, s1) // now() will truncate the result instead of rounding it + + require.Equal(t, rs.Rows()[0][2], rs.Rows()[0][3]) // unix_timestamp() will truncate the result + break + } +} + +func TestShardIndexOnTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int primary key clustered, a int, b int, unique key uk_expr((tidb_shard(a)),a))") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@session.tidb_enforce_mpp = 1") + rows := tk.MustQuery("explain select max(b) from t").Rows() + for _, row := range rows { + line := fmt.Sprintf("%v", row) + if strings.Contains(line, "TableFullScan") { + require.Contains(t, line, "tiflash") + } + } + tk.MustExec("set @@session.tidb_enforce_mpp = 0") + tk.MustExec("set @@session.tidb_allow_mpp = 0") + // when we isolated the read engine as 'tiflash' and banned TiDB opening allow-mpp, no suitable plan is generated. + _, err := tk.Exec("explain select max(b) from t") + require.NotNil(t, err) + require.Equal(t, err.Error(), "[planner:1815]Internal : Can't find a proper physical plan for this query") +} + +func TestExprPushdownBlacklist(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int , b date)") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("insert into mysql.expr_pushdown_blacklist " + + "values('<', 'tikv,tiflash,tidb', 'for test'),('cast', 'tiflash', 'for test'),('date_format', 'tikv', 'for test')") + tk.MustExec("admin reload expr_pushdown_blacklist") + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@session.tidb_opt_enable_late_materialization = OFF") + + // < not pushed, cast only pushed to TiKV, date_format only pushed to TiFlash, + // > pushed to both TiKV and TiFlash + rows := tk.MustQuery("explain format = 'brief' select * from test.t where b > date'1988-01-01' and b < date'1994-01-01' " + + "and cast(a as decimal(10,2)) > 10.10 and date_format(b,'%m') = '11'").Rows() + require.Equal(t, "gt(cast(test.t.a, decimal(10,2) BINARY), 10.10), lt(test.t.b, 1994-01-01)", fmt.Sprintf("%v", rows[0][4])) + require.Equal(t, "eq(date_format(test.t.b, \"%m\"), \"11\"), gt(test.t.b, 1988-01-01)", fmt.Sprintf("%v", rows[2][4])) + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tikv'") + rows = tk.MustQuery("explain format = 'brief' select * from test.t where b > date'1988-01-01' and b < date'1994-01-01' " + + "and cast(a as decimal(10,2)) > 10.10 and date_format(b,'%m') = '11'").Rows() + require.Equal(t, "eq(date_format(test.t.b, \"%m\"), \"11\"), lt(test.t.b, 1994-01-01)", fmt.Sprintf("%v", rows[0][4])) + require.Equal(t, "gt(cast(test.t.a, decimal(10,2) BINARY), 10.10), gt(test.t.b, 1988-01-01)", fmt.Sprintf("%v", rows[2][4])) + + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name = '<' and store_type = 'tikv,tiflash,tidb' and reason = 'for test'") + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name = 'date_format' and store_type = 'tikv' and reason = 'for test'") + tk.MustExec("admin reload expr_pushdown_blacklist") +} + +func TestNotExistFunc(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + // current db is empty + tk.MustGetErrMsg("SELECT xxx(1)", "[planner:1046]No database selected") + tk.MustGetErrMsg("SELECT yyy()", "[planner:1046]No database selected") + tk.MustGetErrMsg("SELECT T.upper(1)", "[expression:1305]FUNCTION t.upper does not exist") + + // current db is not empty + tk.MustExec("use test") + tk.MustGetErrMsg("SELECT xxx(1)", "[expression:1305]FUNCTION test.xxx does not exist") + tk.MustGetErrMsg("SELECT yyy()", "[expression:1305]FUNCTION test.yyy does not exist") + tk.MustGetErrMsg("SELECT t.upper(1)", "[expression:1305]FUNCTION t.upper does not exist") + tk.MustGetErrMsg("SELECT timestampliteral(rand())", "[expression:1305]FUNCTION test.timestampliteral does not exist") +} + +func TestDecodetoChunkReuse(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table chk (a int,b varchar(20))") + for i := 0; i < 200; i++ { + if i%5 == 0 { + tk.MustExec("insert chk values (NULL,NULL)") + continue + } + tk.MustExec(fmt.Sprintf("insert chk values (%d,'%s')", i, strconv.Itoa(i))) + } + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + tk.MustExec("set tidb_init_chunk_size = 2") + tk.MustExec("set tidb_max_chunk_size = 32") + defer func() { + tk.MustExec(fmt.Sprintf("set tidb_init_chunk_size = %d", variable.DefInitChunkSize)) + tk.MustExec(fmt.Sprintf("set tidb_max_chunk_size = %d", variable.DefMaxChunkSize)) + }() + rs, err := tk.Exec("select * from chk") + require.NoError(t, err) + req := rs.NewChunk(nil) + var count int + for { + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + numRows := req.NumRows() + if numRows == 0 { + break + } + for i := 0; i < numRows; i++ { + if count%5 == 0 { + require.True(t, req.GetRow(i).IsNull(0)) + require.True(t, req.GetRow(i).IsNull(1)) + } else { + require.False(t, req.GetRow(i).IsNull(0)) + require.False(t, req.GetRow(i).IsNull(1)) + require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) + require.Equal(t, strconv.Itoa(count), req.GetRow(i).GetString(1)) + } + count++ + } + } + require.Equal(t, count, 200) + rs.Close() +} + +func TestIssue16697(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (v varchar(1024))") + tk.MustExec("insert into t values (space(1024))") + for i := 0; i < 5; i++ { + tk.MustExec("insert into t select * from t") + } + rows := tk.MustQuery("explain analyze select * from t").Rows() + for _, row := range rows { + line := fmt.Sprintf("%v", row) + if strings.Contains(line, "Projection") { + require.Contains(t, line, "KB") + require.NotContains(t, line, "MB") + require.NotContains(t, line, "GB") + } + } +} + +func TestIssue19892(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test") + tk.MustExec("CREATE TABLE dd(a date, b datetime, c timestamp)") + + // check NO_ZERO_DATE + { + tk.MustExec("SET sql_mode=''") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('0000-00-00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") + tk.MustExec("UPDATE dd SET b = '0000-00-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('0000-00-00 20:00:00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-10-01 20:00:00')") + tk.MustExec("UPDATE dd SET c = '0000-00-00 20:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + + tk.MustExec("SET sql_mode='NO_ZERO_DATE'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('0000-0-00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '0000-0-00' for column 'b' at row 1")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('2000-10-01')") + tk.MustExec("UPDATE dd SET a = '0000-00-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '0000-00-00'")) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") + tk.MustExec("UPDATE dd SET c = '0000-00-00 10:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 10:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + + tk.MustExec("SET sql_mode='NO_ZERO_DATE,STRICT_TRANS_TABLES'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('0000-00-00 20:00:00')", "[table:1292]Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1") + tk.MustExec("INSERT IGNORE INTO dd(c) VALUES ('0000-00-00 20:00:00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 20:00:00' for column 'c' at row 1")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") + tk.MustGetErrMsg("UPDATE dd SET b = '0000-00-00'", "[types:1292]Incorrect datetime value: '0000-00-00'") + tk.MustExec("UPDATE IGNORE dd SET b = '0000-00-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '0000-00-00'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") + tk.MustGetErrMsg("UPDATE dd SET c = '0000-00-00 00:00:00'", "[types:1292]Incorrect timestamp value: '0000-00-00 00:00:00'") + tk.MustExec("UPDATE IGNORE dd SET c = '0000-00-00 00:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-00-00 00:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + } + + // check NO_ZERO_IN_DATE + { + tk.MustExec("SET sql_mode=''") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('2000-01-00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00")) + tk.MustExec("INSERT INTO dd(a) values('2000-00-01')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00", "2000-00-01")) + tk.MustExec("INSERT INTO dd(a) values('0-01-02')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-01-00", "2000-00-01", "2000-01-02")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('2000-01-02')") + tk.MustExec("UPDATE dd SET b = '2000-00-02'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-00-02 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-01-02 20:00:00')") + tk.MustExec("UPDATE dd SET c = '0000-01-02 20:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-02 20:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + + tk.MustExec("SET sql_mode='NO_ZERO_IN_DATE'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('2000-01-00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '2000-01-00' for column 'a' at row 1")) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('2000-01-02')") + tk.MustExec("UPDATE dd SET a = '2000-00-02'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect date value: '2000-00-02'")) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) + tk.MustExec("UPDATE dd SET b = '2000-01-0'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + // consistent with Mysql8 + tk.MustExec("UPDATE dd SET b = '0-01-02'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-01-02 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-01-02 20:00:00')") + tk.MustExec("UPDATE dd SET c = '2000-00-02 20:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '2000-00-02 20:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + + tk.MustExec("SET sql_mode='NO_ZERO_IN_DATE,STRICT_TRANS_TABLES'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustGetErrMsg("INSERT INTO dd(b) VALUES ('2000-01-00')", "[table:1292]Incorrect datetime value: '2000-01-00' for column 'b' at row 1") + tk.MustExec("INSERT IGNORE INTO dd(b) VALUES ('2000-00-01')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-00-01' for column 'b' at row 1")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) VALUES ('2000-01-02')") + tk.MustGetErrMsg("UPDATE dd SET b = '2000-01-00'", "[types:1292]Incorrect datetime value: '2000-01-00'") + tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-0'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + tk.MustExec("UPDATE dd SET b = '0000-1-2'") + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-01-02 00:00:00")) + tk.MustGetErrMsg("UPDATE dd SET c = '0000-01-05'", "[types:1292]Incorrect timestamp value: '0000-01-05'") + tk.MustExec("UPDATE IGNORE dd SET c = '0000-01-5'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-5'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('2000-01-00 20:00:00')", "[table:1292]Incorrect timestamp value: '2000-01-00 20:00:00' for column 'c' at row 1") + tk.MustExec("INSERT INTO dd(c) VALUES ('2000-01-02')") + tk.MustGetErrMsg("UPDATE dd SET c = '2000-01-00 20:00:00'", "[types:1292]Incorrect timestamp value: '2000-01-00 20:00:00'") + tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-00'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + } + + // check !NO_ZERO_DATE + tk.MustExec("SET sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('0000-00-00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("0000-00-00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") + tk.MustExec("UPDATE dd SET b = '0000-00-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('0000-00-00 00:00:00')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") + tk.MustExec("UPDATE dd SET c = '0000-00-00 00:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustGetErrMsg("INSERT INTO dd(b) VALUES ('2000-01-00')", "[table:1292]Incorrect datetime value: '2000-01-00' for column 'b' at row 1") + tk.MustExec("INSERT IGNORE INTO dd(b) VALUES ('2000-00-01')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-00-01' for column 'b' at row 1")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) VALUES ('2000-01-02')") + tk.MustGetErrMsg("UPDATE dd SET b = '2000-01-00'", "[types:1292]Incorrect datetime value: '2000-01-00'") + tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-0'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-0'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + tk.MustExec("UPDATE dd SET b = '0000-1-2'") + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-01-02 00:00:00")) + tk.MustGetErrMsg("UPDATE dd SET c = '0000-01-05'", "[types:1292]Incorrect timestamp value: '0000-01-05'") + tk.MustExec("UPDATE IGNORE dd SET c = '0000-01-5'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '0000-01-5'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustGetErrMsg("INSERT INTO dd(c) VALUES ('2000-01-00 20:00:00')", "[table:1292]Incorrect timestamp value: '2000-01-00 20:00:00' for column 'c' at row 1") + tk.MustExec("INSERT INTO dd(c) VALUES ('2000-01-02')") + tk.MustGetErrMsg("UPDATE dd SET c = '2000-01-00 20:00:00'", "[types:1292]Incorrect timestamp value: '2000-01-00 20:00:00'") + tk.MustExec("UPDATE IGNORE dd SET b = '2000-01-00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect datetime value: '2000-01-00'")) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + + // check !NO_ZERO_IN_DATE + tk.MustExec("SET sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'") + { + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(a) values('2000-00-10')") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT a FROM dd").Check(testkit.Rows("2000-00-10")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(b) values('2000-10-01')") + tk.MustExec("UPDATE dd SET b = '2000-00-10'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows()) + tk.MustQuery("SELECT b FROM dd").Check(testkit.Rows("2000-00-10 00:00:00")) + + tk.MustExec("TRUNCATE TABLE dd") + tk.MustExec("INSERT INTO dd(c) values('2000-10-01 10:00:00')") + tk.MustGetErrMsg("UPDATE dd SET c = '2000-00-10 00:00:00'", "[types:1292]Incorrect timestamp value: '2000-00-10 00:00:00'") + tk.MustExec("UPDATE IGNORE dd SET c = '2000-01-00 00:00:00'") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Incorrect timestamp value: '2000-01-00 00:00:00'")) + tk.MustQuery("SELECT c FROM dd").Check(testkit.Rows("0000-00-00 00:00:00")) + } + tk.MustExec("drop table if exists table_20220419;") + tk.MustExec(`CREATE TABLE table_20220419 ( + id bigint(20) NOT NULL AUTO_INCREMENT, + lastLoginDate datetime NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;`) + tk.MustExec("set sql_mode='';") + tk.MustExec("insert into table_20220419 values(1,'0000-00-00 00:00:00');") + tk.MustExec("set sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';") + tk.MustGetErrMsg("insert into table_20220419(lastLoginDate) select lastLoginDate from table_20220419;", "[types:1292]Incorrect datetime value: '0000-00-00 00:00:00'") +} + +func TestIssue11333(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t(col1 decimal);") + tk.MustExec(" insert into t values(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000);") + tk.MustQuery(`select * from t;`).Check(testkit.Rows("0")) + tk.MustExec("create table t1(col1 decimal(65,30));") + tk.MustExec(" insert into t1 values(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000);") + tk.MustQuery(`select * from t1;`).Check(testkit.Rows("0.000000000000000000000000000000")) + tk.MustQuery(`select 0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000000")) + tk.MustQuery(`select 0.0000000000000000000000000000000000000000000000000000000000000000000000012;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000001")) + tk.MustQuery(`select 0.000000000000000000000000000000000000000000000000000000000000000000000001;`).Check(testkit.Rows("0.000000000000000000000000000000000000000000000000000000000000000000000001")) +} + +func TestSecurityEnhancedMode(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + sem.Enable() + defer sem.Disable() + + // When SEM is enabled these features are restricted to all users + // regardless of what privileges they have available. + tk.MustGetErrMsg("SELECT 1 INTO OUTFILE '/tmp/aaaa'", "[planner:8132]Feature 'SELECT INTO' is not supported when security enhanced mode is enabled") +} + +func TestEnumIndex(t *testing.T) { + elems := []string{"\"a\"", "\"b\"", "\"c\""} + rand.Shuffle(len(elems), func(i, j int) { + elems[i], elems[j] = elems[j], elems[i] + }) + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t,tidx") + tk.MustExec("create table t(e enum(" + strings.Join(elems, ",") + "))") + tk.MustExec("create table tidx(e enum(" + strings.Join(elems, ",") + "), index idx(e))") + + nRows := 50 + values := make([]string, 0, nRows) + for i := 0; i < nRows; i++ { + values = append(values, fmt.Sprintf("(%v)", rand.Intn(len(elems))+1)) + } + tk.MustExec(fmt.Sprintf("insert into t values %v", strings.Join(values, ", "))) + tk.MustExec(fmt.Sprintf("insert into tidx values %v", strings.Join(values, ", "))) + + ops := []string{"=", "!=", ">", ">=", "<", "<="} + testElems := []string{"\"a\"", "\"b\"", "\"c\"", "\"d\"", "\"\"", "1", "2", "3", "4", "0", "-1"} + for i := 0; i < nRows; i++ { + cond := fmt.Sprintf("e" + ops[rand.Intn(len(ops))] + testElems[rand.Intn(len(testElems))]) + result := tk.MustQuery("select * from t where " + cond).Sort().Rows() + tk.MustQuery("select * from tidx where " + cond).Sort().Check(result) + } + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(e enum('d','c','b','a'), a int, index idx(e));") + tk.MustExec("insert into t values(1,1),(2,2),(3,3),(4,4);") + tk.MustQuery("select /*+ use_index(t, idx) */ * from t where e not in ('a','d') and a = 2;").Check( + testkit.Rows("c 2")) + + // issue 24419 + tk.MustExec("use test") + tk.MustExec("drop table if exists t02") + tk.MustExec("CREATE TABLE `t02` ( `COL1` enum('^YSQT0]V@9TFN>^WB6G?NG@S8>VYOM;BSC@64=ZISGS?O[JDFBI5M]QXJYQNSKU>NGAWLXS26LMTZ2YNN`XKIUGKY0IHDWV>E[BJJCABOKH1M^CB5E@DLS7Q88PWZTEAY]1ZQMN5NX[IFIYA983K:E4N77@FINM5HVGQCUCVNF5WLOOOEORAM=_JLMVFURMUASTVDBE','NL3V:J9LM4U5KUCVR;P','M5=T5FLQEZMPZAXH]4G:TSYYYVQ7O@4S6C3N8WPFKSP;SRD6VW@94BBH8XCT','P]I52Y46F?@RMOOF6;FWDTO`7FIT]R:]ELHD[CNLDSHC7FPBYOOJXLZSBV^5C^AAF6J5BCKE4V9==@H=4C]GMZXPNM','ECIQWH>?MK=ARGI0WVJNIBZFCFVJHFIUYJ:2?2WWZBNBWTPFNQPLLBFP9R_','E<_Y9OT@SOPYR72VIJVMBWIVPF@TTBZ@8ZPBZL=LXZF`WM4V2?K>AT','PZ@PR6XN28JL`B','ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9','QLDIOY[Y:JZR@OL__I^@FBO=O_?WOOR:2BE:QJC','BI^TGJ_NEEXYKV1POHTOJQPGCPVR=TYZMGWABUQR07J8U::W4','N`ZN4P@9T[JW;FR6=FA4WP@APNPG[XQVIK4]F]2>EC>JEIOXC``;;?OHP') DEFAULT NULL, `COL2` tinyint DEFAULT NULL, `COL3` time DEFAULT NULL, KEY `U_M_COL4` (`COL1`,`COL2`), KEY `U_M_COL5` (`COL3`,`COL2`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into t02(col1, col2) values ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 39), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 51), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', 55), ('OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A', -30), ('ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9', -30);") + tk.MustQuery("select * from t02 where col1 not in (\"W1Rgd74pbJaGX47h1MPjpr0XSKJNCnwEleJ50Vbpl9EmbHJX6D6BXYKT2UAbl1uDw3ZGeYykhzG6Gld0wKdOiT4Gv5j9upHI0Q7vrXij4N9WNFJvB\", \"N`ZN4P@9T[JW;FR6=FA4WP@APNPG[XQVIK4]F]2>EC>JEIOXC``;;?OHP\") and col2 = -30;").Check( + testkit.Rows( + "OFJHCEKCQGT:MXI7P3[YO4N0DF=2XJWJ4Z9Z;HQ8TMUTZV8YLQAHWJ4BDZHR3A -30 ", + "ZOHBSCRMZPOI`IVTSEZAIDAF7DS@1TT20AP9 -30 ")) + + // issue 24576 + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(col1 enum('a','b','c'), col2 enum('a','b','c'), col3 int, index idx(col1,col2));") + tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3);") + tk.MustQuery("select /*+ use_index(t,idx) */ col3 from t where col2 between 'b' and 'b' and col1 is not null;").Check( + testkit.Rows("2")) + tk.MustQuery("select /*+ use_index(t,idx) */ col3 from t where col2 = 'b' and col1 is not null;").Check( + testkit.Rows("2")) + + // issue25099 + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(e enum(\"a\",\"b\",\"c\"), index idx(e));") + tk.MustExec("insert ignore into t values(0),(1),(2),(3);") + tk.MustQuery("select * from t where e = '';").Check( + testkit.Rows("")) + tk.MustQuery("select * from t where e != 'a';").Sort().Check( + testkit.Rows("", "b", "c")) + tk.MustExec("alter table t drop index idx;") + tk.MustQuery("select * from t where e = '';").Check( + testkit.Rows("")) + tk.MustQuery("select * from t where e != 'a';").Sort().Check( + testkit.Rows("", "b", "c")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(e enum(\"\"), index idx(e));") + tk.MustExec("insert ignore into t values(0),(1);") + tk.MustQuery("select * from t where e = '';").Check( + testkit.Rows("", "")) + tk.MustExec("alter table t drop index idx;") + tk.MustQuery("select * from t where e = '';").Check( + testkit.Rows("", "")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(e enum(\"a\",\"b\",\"c\"), index idx(e));") + tk.MustExec("insert ignore into t values(0);") + tk.MustExec("select * from t t1 join t t2 on t1.e=t2.e;") + tk.MustQuery("select /*+ inl_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( + testkit.Rows(" ")) + tk.MustQuery("select /*+ hash_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( + testkit.Rows(" ")) + tk.MustQuery("select /*+ inl_hash_join(t1,t2) */ * from t t1 join t t2 on t1.e=t2.e;").Check( + testkit.Rows(" ")) +} + +func TestBuiltinFuncJSONMergePatch_InColumn(t *testing.T) { + ctx := context.Background() + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tests := []struct { + input [2]interface{} + expected interface{} + success bool + errCode int + }{ + // RFC 7396 document: https://datatracker.ietf.org/doc/html/rfc7396 + // RFC 7396 Example Test Cases + {[2]interface{}{`{"a":"b"}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, + {[2]interface{}{`{"a":"b"}`, `{"b":"c"}`}, `{"a": "b", "b": "c"}`, true, 0}, + {[2]interface{}{`{"a":"b"}`, `{"a":null}`}, `{}`, true, 0}, + {[2]interface{}{`{"a":"b", "b":"c"}`, `{"a":null}`}, `{"b": "c"}`, true, 0}, + {[2]interface{}{`{"a":["b"]}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, + {[2]interface{}{`{"a":"c"}`, `{"a":["b"]}`}, `{"a": ["b"]}`, true, 0}, + {[2]interface{}{`{"a":{"b":"c"}}`, `{"a":{"b":"d","c":null}}`}, `{"a": {"b": "d"}}`, true, 0}, + {[2]interface{}{`{"a":[{"b":"c"}]}`, `{"a": [1]}`}, `{"a": [1]}`, true, 0}, + {[2]interface{}{`["a","b"]`, `["c","d"]`}, `["c", "d"]`, true, 0}, + {[2]interface{}{`{"a":"b"}`, `["c"]`}, `["c"]`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `null`}, `null`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `"bar"`}, `"bar"`, true, 0}, + {[2]interface{}{`{"e":null}`, `{"a":1}`}, `{"e": null, "a": 1}`, true, 0}, + {[2]interface{}{`[1,2]`, `{"a":"b","c":null}`}, `{"a": "b"}`, true, 0}, + {[2]interface{}{`{}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a": {"bb": {}}}`, true, 0}, + // RFC 7396 Example Document + {[2]interface{}{`{"title":"Goodbye!","author":{"givenName":"John","familyName":"Doe"},"tags":["example","sample"],"content":"This will be unchanged"}`, `{"title":"Hello!","phoneNumber":"+01-123-456-7890","author":{"familyName":null},"tags":["example"]}`}, `{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}`, true, 0}, + + // From mysql Example Test Cases + {[2]interface{}{nil, `{"a":1}`}, nil, true, 0}, + {[2]interface{}{`{"a":1}`, nil}, nil, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `true`}, `true`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `false`}, `false`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `123`}, `123`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `123.1`}, `123.1`, true, 0}, + {[2]interface{}{`{"a":"foo"}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[2]interface{}{"null", `{"a":1}`}, `{"a":1}`, true, 0}, + {[2]interface{}{`{"a":1}`, "null"}, `null`, true, 0}, + + // Invalid json text + {[2]interface{}{`{"a":1}`, `[1]}`}, nil, false, mysql.ErrInvalidJSONText}, + } + + tk.MustExec(`use test;`) + tk.MustExec(`drop table if exists t;`) + tk.MustExec("CREATE TABLE t ( `id` INT NOT NULL AUTO_INCREMENT, `j` json NULL, `vc` VARCHAR ( 5000 ) NULL, PRIMARY KEY ( `id` ) );") + for id, tt := range tests { + tk.MustExec("insert into t values(?,?,?)", id+1, tt.input[0], tt.input[1]) + if tt.success { + result := tk.MustQuery("select json_merge_patch(j,vc) from t where id = ?", id+1) + if tt.expected == nil { + result.Check(testkit.Rows("")) + } else { + j, e := types.ParseBinaryJSONFromString(tt.expected.(string)) + require.NoError(t, e) + result.Check(testkit.Rows(j.String())) + } + } else { + rs, _ := tk.Exec("select json_merge_patch(j,vc) from t where id = ?;", id+1) + _, err := session.GetRows4Test(ctx, tk.Session(), rs) + terr := errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(tt.errCode), terr.Code()) + } + } +} + +func TestBuiltinFuncJSONMergePatch_InExpression(t *testing.T) { + ctx := context.Background() + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + tests := []struct { + input []interface{} + expected interface{} + success bool + errCode int + }{ + // RFC 7396 document: https://datatracker.ietf.org/doc/html/rfc7396 + // RFC 7396 Example Test Cases + {[]interface{}{`{"a":"b"}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, + {[]interface{}{`{"a":"b"}`, `{"b":"c"}`}, `{"a": "b","b": "c"}`, true, 0}, + {[]interface{}{`{"a":"b"}`, `{"a":null}`}, `{}`, true, 0}, + {[]interface{}{`{"a":"b", "b":"c"}`, `{"a":null}`}, `{"b": "c"}`, true, 0}, + {[]interface{}{`{"a":["b"]}`, `{"a":"c"}`}, `{"a": "c"}`, true, 0}, + {[]interface{}{`{"a":"c"}`, `{"a":["b"]}`}, `{"a": ["b"]}`, true, 0}, + {[]interface{}{`{"a":{"b":"c"}}`, `{"a":{"b":"d","c":null}}`}, `{"a": {"b": "d"}}`, true, 0}, + {[]interface{}{`{"a":[{"b":"c"}]}`, `{"a": [1]}`}, `{"a": [1]}`, true, 0}, + {[]interface{}{`["a","b"]`, `["c","d"]`}, `["c", "d"]`, true, 0}, + {[]interface{}{`{"a":"b"}`, `["c"]`}, `["c"]`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `null`}, `null`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `"bar"`}, `"bar"`, true, 0}, + {[]interface{}{`{"e":null}`, `{"a":1}`}, `{"e": null,"a": 1}`, true, 0}, + {[]interface{}{`[1,2]`, `{"a":"b","c":null}`}, `{"a":"b"}`, true, 0}, + {[]interface{}{`{}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb": {}}}`, true, 0}, + // RFC 7396 Example Document + {[]interface{}{`{"title":"Goodbye!","author":{"givenName":"John","familyName":"Doe"},"tags":["example","sample"],"content":"This will be unchanged"}`, `{"title":"Hello!","phoneNumber":"+01-123-456-7890","author":{"familyName":null},"tags":["example"]}`}, `{"title":"Hello!","author":{"givenName":"John"},"tags":["example"],"content":"This will be unchanged","phoneNumber":"+01-123-456-7890"}`, true, 0}, + + // test cases + {[]interface{}{nil, `1`}, `1`, true, 0}, + {[]interface{}{`1`, nil}, nil, true, 0}, + {[]interface{}{nil, `null`}, `null`, true, 0}, + {[]interface{}{`null`, nil}, nil, true, 0}, + {[]interface{}{nil, `true`}, `true`, true, 0}, + {[]interface{}{`true`, nil}, nil, true, 0}, + {[]interface{}{nil, `false`}, `false`, true, 0}, + {[]interface{}{`false`, nil}, nil, true, 0}, + {[]interface{}{nil, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[]interface{}{`[1,2,3]`, nil}, nil, true, 0}, + {[]interface{}{nil, `{"a":"foo"}`}, nil, true, 0}, + {[]interface{}{`{"a":"foo"}`, nil}, nil, true, 0}, + + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"b":"123"}`, `{"c":1}`}, `{"b":"123","c":1}`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"c":1}`}, `{"c":1}`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `true`}, `true`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"d":1}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb":{}},"d":1}`, true, 0}, + {[]interface{}{`null`, `true`, `[1,2,3]`}, `[1,2,3]`, true, 0}, + + // From mysql Example Test Cases + {[]interface{}{nil, `null`, `[1,2,3]`, `{"a":1}`}, `{"a": 1}`, true, 0}, + {[]interface{}{`null`, nil, `[1,2,3]`, `{"a":1}`}, `{"a": 1}`, true, 0}, + {[]interface{}{`null`, `[1,2,3]`, nil, `{"a":1}`}, nil, true, 0}, + {[]interface{}{`null`, `[1,2,3]`, `{"a":1}`, nil}, nil, true, 0}, + + {[]interface{}{nil, `null`, `{"a":1}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[]interface{}{`null`, nil, `{"a":1}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[]interface{}{`null`, `{"a":1}`, nil, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[]interface{}{`null`, `{"a":1}`, `[1,2,3]`, nil}, nil, true, 0}, + + {[]interface{}{nil, `null`, `{"a":1}`, `true`}, `true`, true, 0}, + {[]interface{}{`null`, nil, `{"a":1}`, `true`}, `true`, true, 0}, + {[]interface{}{`null`, `{"a":1}`, nil, `true`}, `true`, true, 0}, + {[]interface{}{`null`, `{"a":1}`, `true`, nil}, nil, true, 0}, + + // non-object last item + {[]interface{}{"true", "false", "[]", "{}", "null"}, "null", true, 0}, + {[]interface{}{"false", "[]", "{}", "null", "true"}, "true", true, 0}, + {[]interface{}{"true", "[]", "{}", "null", "false"}, "false", true, 0}, + {[]interface{}{"true", "false", "{}", "null", "[]"}, "[]", true, 0}, + {[]interface{}{"true", "false", "{}", "null", "1"}, "1", true, 0}, + {[]interface{}{"true", "false", "{}", "null", "1.8"}, "1.8", true, 0}, + {[]interface{}{"true", "false", "{}", "null", `"112"`}, `"112"`, true, 0}, + + {[]interface{}{`{"a":"foo"}`, nil}, nil, true, 0}, + {[]interface{}{nil, `{"a":"foo"}`}, nil, true, 0}, + {[]interface{}{`{"a":"foo"}`, `false`}, `false`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `123`}, `123`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `123.1`}, `123.1`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `[1,2,3]`}, `[1,2,3]`, true, 0}, + {[]interface{}{`null`, `{"a":1}`}, `{"a":1}`, true, 0}, + {[]interface{}{`{"a":1}`, `null`}, `null`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"b":"123"}`, `{"c":1}`}, `{"b":"123","c":1}`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `{"c":1}`}, `{"c":1}`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"a":null}`, `true`}, `true`, true, 0}, + {[]interface{}{`{"a":"foo"}`, `{"d":1}`, `{"a":{"bb":{"ccc":null}}}`}, `{"a":{"bb":{}},"d":1}`, true, 0}, + + // Invalid json text + {[]interface{}{`{"a":1}`, `[1]}`}, nil, false, mysql.ErrInvalidJSONText}, + {[]interface{}{`{{"a":1}`, `[1]`, `null`}, nil, false, mysql.ErrInvalidJSONText}, + {[]interface{}{`{"a":1}`, `jjj`, `null`}, nil, false, mysql.ErrInvalidJSONText}, + } + + for _, tt := range tests { + marks := make([]string, len(tt.input)) + for i := 0; i < len(marks); i++ { + marks[i] = "?" + } + sql := fmt.Sprintf("select json_merge_patch(%s);", strings.Join(marks, ",")) + if tt.success { + result := tk.MustQuery(sql, tt.input...) + if tt.expected == nil { + result.Check(testkit.Rows("")) + } else { + j, e := types.ParseBinaryJSONFromString(tt.expected.(string)) + require.NoError(t, e) + result.Check(testkit.Rows(j.String())) + } + } else { + rs, _ := tk.Exec(sql, tt.input...) + _, err := session.GetRows4Test(ctx, tk.Session(), rs) + terr := errors.Cause(err).(*terror.Error) + require.Equal(t, errors.ErrCode(tt.errCode), terr.Code()) + } + } +} + +// issue https://github.com/pingcap/tidb/issues/28544 +func TestPrimaryKeyRequiredSysvar(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE t ( + name varchar(60), + age int + )`) + tk.MustExec(`DROP TABLE t`) + + tk.MustExec("set @@sql_require_primary_key=true") + + // creating table without primary key should now fail + tk.MustGetErrCode(`CREATE TABLE t ( + name varchar(60), + age int + )`, errno.ErrTableWithoutPrimaryKey) + // but with primary key should work as usual + tk.MustExec(`CREATE TABLE t ( + id bigint(20) NOT NULL PRIMARY KEY AUTO_RANDOM, + name varchar(60), + age int + )`) + tk.MustGetErrMsg(`ALTER TABLE t + DROP COLUMN id`, "[ddl:8200]Unsupported drop integer primary key") + + // test with non-clustered primary key + tk.MustExec(`CREATE TABLE t2 ( + id int(11) NOT NULL, + c1 int(11) DEFAULT NULL, + PRIMARY KEY(id) NONCLUSTERED)`) + tk.MustGetErrMsg(`ALTER TABLE t2 + DROP COLUMN id`, "[ddl:8200]can't drop column id with composite index covered or Primary Key covered now") + tk.MustGetErrCode(`ALTER TABLE t2 DROP PRIMARY KEY`, errno.ErrTableWithoutPrimaryKey) + + // this sysvar is ignored in internal sessions + tk.Session().GetSessionVars().InRestrictedSQL = true + ctx := context.Background() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) + sql := `CREATE TABLE t3 ( + id int(11) NOT NULL, + c1 int(11) DEFAULT NULL)` + stmts, err := tk.Session().Parse(ctx, sql) + require.NoError(t, err) + res, err := tk.Session().ExecuteStmt(ctx, stmts[0]) + require.NoError(t, err) + if res != nil { + require.NoError(t, res.Close()) + } +} + +func TestTimestamp(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test;`) + tk.MustExec("SET time_zone = '+00:00';") + defer tk.MustExec("SET time_zone = DEFAULT;") + timestampStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr1 = timestampStr1[1:] + timestampStr1 = timestampStr1[:len(timestampStr1)-1] + timestamp1, err := strconv.ParseFloat(timestampStr1, 64) + require.NoError(t, err) + nowStr1 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now1, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr1) + require.NoError(t, err) + tk.MustExec("set @@timestamp = 12345;") + tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustExec("set @@timestamp = default;") + time.Sleep(2 * time.Microsecond) + timestampStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr2 = timestampStr2[1:] + timestampStr2 = timestampStr2[:len(timestampStr2)-1] + timestamp2, err := strconv.ParseFloat(timestampStr2, 64) + require.NoError(t, err) + nowStr2 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now2, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr2) + require.NoError(t, err) + require.Less(t, timestamp1, timestamp2) + require.Less(t, now1.UnixNano(), now2.UnixNano()) + tk.MustExec("set @@timestamp = 12345;") + tk.MustQuery("SELECT @@timestamp;").Check(testkit.Rows("12345")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustQuery("SELECT NOW();").Check(testkit.Rows("1970-01-01 03:25:45")) + tk.MustExec("set @@timestamp = 0;") + time.Sleep(2 * time.Microsecond) + timestampStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT @@timestamp;").Rows()[0]) + timestampStr3 = timestampStr3[1:] + timestampStr3 = timestampStr3[:len(timestampStr3)-1] + timestamp3, err := strconv.ParseFloat(timestampStr3, 64) + require.NoError(t, err) + nowStr3 := fmt.Sprintf("%s", tk.MustQuery("SELECT NOW(6);").Rows()[0]) + now3, err := time.Parse("[2006-01-02 15:04:05.000000]", nowStr3) + require.NoError(t, err) + require.Less(t, timestamp2, timestamp3) + require.Less(t, now2.UnixNano(), now3.UnixNano()) +} + +func TestCastJSONTimeDuration(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(i INT, j JSON)") + + nowDate := time.Now().Format(time.DateOnly) + + // DATE/DATETIME/TIME will be automatically converted to json date/datetime/duration + tk.MustExec("insert into t values (0, DATE('1998-06-13'))") + tk.MustExec("insert into t values (1, CAST('1998-06-13 12:12:12' as DATETIME))") + tk.MustExec("insert into t values (2, DATE('1596-03-31'))") + tk.MustExec("insert into t values (3, CAST('1596-03-31 12:12:12' as DATETIME))") + tk.MustExec(`insert into t values (4, '"1596-03-31 12:12:12"')`) + tk.MustExec(`insert into t values (5, '"12:12:12"')`) + tk.MustExec("insert into t values (6, CAST('12:12:12' as TIME))") + tk.MustQuery("select i, cast(j as date), cast(j as datetime), cast(j as time), json_type(j) from t").Check(testkit.Rows( + "0 1998-06-13 1998-06-13 00:00:00 00:00:00 DATE", + "1 1998-06-13 1998-06-13 12:12:12 12:12:12 DATETIME", + "2 1596-03-31 1596-03-31 00:00:00 00:00:00 DATE", + "3 1596-03-31 1596-03-31 12:12:12 12:12:12 DATETIME", + "4 1596-03-31 1596-03-31 12:12:12 12:12:12 STRING", + "5 2012-12-12 2012-12-12 00:00:00 12:12:12 STRING", + fmt.Sprintf("6 %s %s 12:12:12 12:12:12 TIME", nowDate, nowDate), + )) +} + +func TestCompareBuiltin(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // compare as JSON + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (pk int NOT NULL PRIMARY KEY AUTO_INCREMENT, i INT, j JSON);") + tk.MustExec(`INSERT INTO t(i, j) VALUES (0, NULL)`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (1, '{"a": 2}')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (2, '[1,2]')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (3, '{"a":"b", "c":"d","ab":"abc", "bc": ["x", "y"]}')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (4, '["here", ["I", "am"], "!!!"]')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (5, '"scalar string"')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (6, 'true')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (7, 'false')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (8, 'null')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (9, '-1')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (10, CAST(CAST(1 AS UNSIGNED) AS JSON))`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (11, '32767')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (12, '32768')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (13, '-32768')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (14, '-32769')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (15, '2147483647')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (16, '2147483648')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (17, '-2147483648')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (18, '-2147483649')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (19, '18446744073709551615')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (20, '18446744073709551616')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (21, '3.14')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (22, '{}')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (23, '[]')`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (24, CAST(CAST('2015-01-15 23:24:25' AS DATETIME) AS JSON))`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (25, CAST(CAST('23:24:25' AS TIME) AS JSON))`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (26, CAST(CAST('2015-01-15' AS DATE) AS JSON))`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (27, CAST(TIMESTAMP('2015-01-15 23:24:25') AS JSON))`) + tk.MustExec(`INSERT INTO t(i, j) VALUES (28, CAST('[]' AS CHAR CHARACTER SET 'ascii'))`) + + result := tk.MustQuery(`SELECT i, + (j = '"scalar string"') AS c1, + (j = 'scalar string') AS c2, + (j = CAST('"scalar string"' AS JSON)) AS c3, + (j = CAST(CAST(j AS CHAR CHARACTER SET 'utf8mb4') AS JSON)) AS c4, + (j = CAST(NULL AS JSON)) AS c5, + (j = NULL) AS c6, + (j <=> NULL) AS c7, + (j <=> CAST(NULL AS JSON)) AS c8, + (j IN (-1, 2, 32768, 3.14)) AS c9, + (j IN (CAST('[1, 2]' AS JSON), CAST('{}' AS JSON), CAST(3.14 AS JSON))) AS c10, + (j = (SELECT j FROM t WHERE j = CAST('null' AS JSON))) AS c11, + (j = (SELECT j FROM t WHERE j IS NULL)) AS c12, + (j = (SELECT j FROM t WHERE 1<>1)) AS c13, + (j = DATE('2015-01-15')) AS c14, + (j = TIME('23:24:25')) AS c15, + (j = TIMESTAMP('2015-01-15 23:24:25')) AS c16, + (j = CURRENT_TIMESTAMP) AS c17, + (JSON_EXTRACT(j, '$.a') = 2) AS c18 + FROM t + ORDER BY i;`) + result.Check(testkit.Rows("0 1 1 ", + "1 0 0 0 1 0 0 0 0 0 0 0 0 0 1", + "2 0 0 0 1 0 0 0 1 0 0 0 0 0 ", + "3 0 0 0 1 0 0 0 0 0 0 0 0 0 0", + "4 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "5 0 1 1 1 0 0 0 0 0 0 0 0 0 ", + "6 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "7 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "8 0 0 0 1 0 0 0 0 1 0 0 0 0 ", + "9 0 0 0 1 0 0 1 0 0 0 0 0 0 ", + "10 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "11 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "12 0 0 0 1 0 0 1 0 0 0 0 0 0 ", + "13 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "14 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "15 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "16 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "17 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "18 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "19 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "20 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "21 0 0 0 1 0 0 1 1 0 0 0 0 0 ", + "22 0 0 0 1 0 0 0 1 0 0 0 0 0 ", + "23 0 0 0 1 0 0 0 0 0 0 0 0 0 ", + "24 0 0 0 0 0 0 0 0 0 0 0 1 0 ", + "25 0 0 0 0 0 0 0 0 0 0 1 0 0 ", + "26 0 0 0 0 0 0 0 0 0 1 0 0 0 ", + "27 0 0 0 0 0 0 0 0 0 0 0 1 0 ", + "28 0 0 0 1 0 0 0 0 0 0 0 0 0 ")) + + // for coalesce + result = tk.MustQuery("select coalesce(NULL), coalesce(NULL, NULL), coalesce(NULL, NULL, NULL);") + result.Check(testkit.Rows(" ")) + tk.MustQuery(`select coalesce(cast(1 as json), cast(2 as json));`).Check(testkit.Rows(`1`)) + tk.MustQuery(`select coalesce(NULL, cast(2 as json));`).Check(testkit.Rows(`2`)) + tk.MustQuery(`select coalesce(cast(1 as json), NULL);`).Check(testkit.Rows(`1`)) + tk.MustQuery(`select coalesce(NULL, NULL);`).Check(testkit.Rows(``)) + + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2(a int, b double, c datetime, d time, e char(20), f bit(10))") + tk.MustExec(`insert into t2 values(1, 1.1, "2017-08-01 12:01:01", "12:01:01", "abcdef", 0b10101)`) + + result = tk.MustQuery("select coalesce(NULL, a), coalesce(NULL, b, a), coalesce(c, NULL, a, b), coalesce(d, NULL), coalesce(d, c), coalesce(NULL, NULL, e, 1), coalesce(f), coalesce(1, a, b, c, d, e, f) from t2") + // coalesce(col_bit) is not same with MySQL, because it's a bug of MySQL(https://bugs.mysql.com/bug.php?id=103289&thanks=4) + result.Check(testkit.Rows(fmt.Sprintf("1 1.1 2017-08-01 12:01:01 12:01:01 %s 12:01:01 abcdef \x00\x15 1", time.Now().In(tk.Session().GetSessionVars().Location()).Format(time.DateOnly)))) + + // nullif + result = tk.MustQuery(`SELECT NULLIF(NULL, 1), NULLIF(1, NULL), NULLIF(1, 1), NULLIF(NULL, NULL);`) + result.Check(testkit.Rows(" 1 ")) + + result = tk.MustQuery(`SELECT NULLIF(1, 1.0), NULLIF(1, "1.0");`) + result.Check(testkit.Rows(" ")) + + result = tk.MustQuery(`SELECT NULLIF("abc", 1);`) + result.Check(testkit.Rows("abc")) + + result = tk.MustQuery(`SELECT NULLIF(1+2, 1);`) + result.Check(testkit.Rows("3")) + + result = tk.MustQuery(`SELECT NULLIF(1, 1+2);`) + result.Check(testkit.Rows("1")) + + result = tk.MustQuery(`SELECT NULLIF(2+3, 1+2);`) + result.Check(testkit.Rows("5")) + + result = tk.MustQuery(`SELECT HEX(NULLIF("abc", 1));`) + result.Check(testkit.Rows("616263")) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a date)") + result = tk.MustQuery("desc select a = a from t") + result.Check(testkit.Rows( + "Projection_3 10000.00 root eq(test.t.a, test.t.a)->Column#3", + "└─TableReader_5 10000.00 root data:TableFullScan_4", + " └─TableFullScan_4 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", + )) + + // for interval + result = tk.MustQuery(`select interval(null, 1, 2), interval(1, 2, 3), interval(2, 1, 3)`) + result.Check(testkit.Rows("-1 0 1")) + result = tk.MustQuery(`select interval(3, 1, 2), interval(0, "b", "1", "2"), interval("a", "b", "1", "2")`) + result.Check(testkit.Rows("2 1 1")) + result = tk.MustQuery(`select interval(23, 1, 23, 23, 23, 30, 44, 200), interval(23, 1.7, 15.3, 23.1, 30, 44, 200), interval(9007199254740992, 9007199254740993)`) + result.Check(testkit.Rows("4 2 0")) + result = tk.MustQuery(`select interval(cast(9223372036854775808 as unsigned), cast(9223372036854775809 as unsigned)), interval(9223372036854775807, cast(9223372036854775808 as unsigned)), interval(-9223372036854775807, cast(9223372036854775808 as unsigned))`) + result.Check(testkit.Rows("0 0 0")) + result = tk.MustQuery(`select interval(cast(9223372036854775806 as unsigned), 9223372036854775807), interval(cast(9223372036854775806 as unsigned), -9223372036854775807), interval("9007199254740991", "9007199254740992")`) + result.Check(testkit.Rows("0 1 0")) + result = tk.MustQuery(`select interval(9007199254740992, "9007199254740993"), interval("9007199254740992", 9007199254740993), interval("9007199254740992", "9007199254740993")`) + result.Check(testkit.Rows("1 1 1")) + result = tk.MustQuery(`select INTERVAL(100, NULL, NULL, NULL, NULL, NULL, 100);`) + result.Check(testkit.Rows("6")) + result = tk.MustQuery(`SELECT INTERVAL(0,(1*5)/2) + INTERVAL(5,4,3);`) + result.Check(testkit.Rows("2")) + + // for greatest + result = tk.MustQuery(`select greatest(1, 2, 3), greatest("a", "b", "c"), greatest(1.1, 1.2, 1.3), greatest("123a", 1, 2)`) + result.Check(testkit.Rows("3 c 1.3 2")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + result = tk.MustQuery(`select greatest(cast("2017-01-01" as datetime), "123", "234", cast("2018-01-01" as date)), greatest(cast("2017-01-01" as date), "123", null)`) + result.Check(testkit.Rows("234 ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect time value: '123'", "Warning|1292|Incorrect time value: '234'", "Warning|1292|Incorrect time value: '123'")) + // for least + result = tk.MustQuery(`select least(1, 2, 3), least("a", "b", "c"), least(1.1, 1.2, 1.3), least("123a", 1, 2)`) + result.Check(testkit.Rows("1 a 1.1 1")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + result = tk.MustQuery(`select least(cast("2017-01-01" as datetime), "123", "234", cast("2018-01-01" as date)), least(cast("2017-01-01" as date), "123", null)`) + result.Check(testkit.Rows("123 ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect time value: '123'", "Warning|1292|Incorrect time value: '234'", "Warning|1292|Incorrect time value: '123'")) + tk.MustQuery(`select 1 < 17666000000000000000, 1 > 17666000000000000000, 1 = 17666000000000000000`).Check(testkit.Rows("1 0 0")) + + tk.MustExec("drop table if exists t") + + // insert value at utc timezone + tk.MustExec("set time_zone = '+00:00'") + tk.MustExec("create table t(a timestamp)") + tk.MustExec("insert into t value('1991-05-06 04:59:28')") + // check daylight saving time in Asia/Shanghai + tk.MustExec("set time_zone='Asia/Shanghai'") + tk.MustQuery("select * from t").Check(testkit.Rows("1991-05-06 13:59:28")) + // insert an nonexistent time + tk.MustExec("set time_zone = 'America/Los_Angeles'") + tk.MustExecToErr("insert into t value('2011-03-13 02:00:00')") + // reset timezone to a +8 offset + tk.MustExec("set time_zone = '+08:00'") + tk.MustQuery("select * from t").Check(testkit.Rows("1991-05-06 12:59:28")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a bigint unsigned)") + tk.MustExec("insert into t value(17666000000000000000)") + tk.MustQuery("select * from t where a = 17666000000000000000").Check(testkit.Rows("17666000000000000000")) + + // test for compare row + result = tk.MustQuery(`select row(1,2,3)=row(1,2,3)`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select row(1,2,3)=row(1+3,2,3)`) + result.Check(testkit.Rows("0")) + result = tk.MustQuery(`select row(1,2,3)<>row(1,2,3)`) + result.Check(testkit.Rows("0")) + result = tk.MustQuery(`select row(1,2,3)<>row(1+3,2,3)`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select row(1+3,2,3)<>row(1+3,2,3)`) + result.Check(testkit.Rows("0")) +} + +func TestTimeBuiltin(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + originSQLMode := tk.Session().GetSessionVars().StrictSQLMode + tk.Session().GetSessionVars().StrictSQLMode = true + defer func() { + tk.Session().GetSessionVars().StrictSQLMode = originSQLMode + }() + tk.MustExec("use test") + + // for makeDate + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b double, c datetime, d time, e char(20), f bit(10))") + tk.MustExec(`insert into t values(1, 1.1, "2017-01-01 12:01:01", "12:01:01", "abcdef", 0b10101)`) + result := tk.MustQuery("select makedate(a,a), makedate(b,b), makedate(c,c), makedate(d,d), makedate(e,e), makedate(f,f), makedate(null,null), makedate(a,b) from t") + result.Check(testkit.Rows("2001-01-01 2001-01-01 2021-01-21 2001-01-01")) + + // for date + result = tk.MustQuery(`select date("2019-09-12"), date("2019-09-12 12:12:09"), date("2019-09-12 12:12:09.121212");`) + result.Check(testkit.Rows("2019-09-12 2019-09-12 2019-09-12")) + result = tk.MustQuery(`select date("0000-00-00"), date("0000-00-00 12:12:09"), date("0000-00-00 00:00:00.121212"), date("0000-00-00 00:00:00.000000");`) + result.Check(testkit.Rows(" 0000-00-00 0000-00-00 ")) + result = tk.MustQuery(`select date("aa"), date(12.1), date("");`) + result.Check(testkit.Rows(" ")) + + // for year + result = tk.MustQuery(`select year("2013-01-09"), year("2013-00-09"), year("000-01-09"), year("1-01-09"), year("20131-01-09"), year(null);`) + result.Check(testkit.Rows("2013 2013 0 2001 ")) + result = tk.MustQuery(`select year("2013-00-00"), year("2013-00-00 00:00:00"), year("0000-00-00 12:12:12"), year("2017-00-00 12:12:12");`) + result.Check(testkit.Rows("2013 2013 0 2017")) + result = tk.MustQuery(`select year("aa"), year(2013), year(2012.09), year("1-01"), year("-09");`) + result.Check(testkit.Rows(" ")) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a bigint)`) + _, err := tk.Exec(`insert into t select year("aa")`) + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err %v", err) + tk.MustExec(`set sql_mode='STRICT_TRANS_TABLES'`) // without zero date + tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) + tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) // with zero date + tk.MustExec(`insert into t select year("0000-00-00 00:00:00")`) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) + _, err = tk.Exec(`insert into t select year("0000-00-00 00:00:00");`) + require.Error(t, err) + require.True(t, types.ErrWrongValue.Equal(err), "err %v", err) + + tk.MustExec(`insert into t select 1`) + tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) + _, err = tk.Exec(`update t set a = year("aa")`) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err %v", err) + _, err = tk.Exec(`delete from t where a = year("aa")`) + // Only `code` can be used to compare because the error `class` information + // will be lost after expression push-down + require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) + + // for month + result = tk.MustQuery(`select month("2013-01-09"), month("2013-00-09"), month("000-01-09"), month("1-01-09"), month("20131-01-09"), month(null);`) + result.Check(testkit.Rows("1 0 1 1 ")) + result = tk.MustQuery(`select month("2013-00-00"), month("2013-00-00 00:00:00"), month("0000-00-00 12:12:12"), month("2017-00-00 12:12:12");`) + result.Check(testkit.Rows("0 0 0 0")) + result = tk.MustQuery(`select month("aa"), month(2013), month(2012.09), month("1-01"), month("-09");`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select month("2013-012-09"), month("2013-0000000012-09"), month("2013-30-09"), month("000-41-09");`) + result.Check(testkit.Rows("12 12 ")) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a bigint)`) + _, err = tk.Exec(`insert into t select month("aa")`) + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue), "err: %v", err) + tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) + tk.MustExec(`set sql_mode="NO_ZERO_DATE";`) + tk.MustExec(`insert into t select month("0000-00-00 00:00:00")`) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + tk.MustExec(`set sql_mode="NO_ZERO_DATE,STRICT_TRANS_TABLES";`) + _, err = tk.Exec(`insert into t select month("0000-00-00 00:00:00");`) + require.Error(t, err) + require.True(t, types.ErrWrongValue.Equal(err), "err: %v", err) + tk.MustExec(`insert into t select 1`) + tk.MustExec(`set sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION";`) + tk.MustExec(`insert into t select 1`) + _, err = tk.Exec(`update t set a = month("aa")`) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) + _, err = tk.Exec(`delete from t where a = month("aa")`) + require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) + + // for week + result = tk.MustQuery(`select week("2012-12-22"), week("2012-12-22", -2), week("2012-12-22", 0), week("2012-12-22", 1), week("2012-12-22", 2), week("2012-12-22", 200);`) + result.Check(testkit.Rows("51 51 51 51 51 51")) + result = tk.MustQuery(`select week("2008-02-20"), week("2008-02-20", 0), week("2008-02-20", 1), week("2009-02-20", 2), week("2008-02-20", 3), week("2008-02-20", 4);`) + result.Check(testkit.Rows("7 7 8 7 8 8")) + result = tk.MustQuery(`select week("2008-02-20", 5), week("2008-02-20", 6), week("2009-02-20", 7), week("2008-02-20", 8), week("2008-02-20", 9);`) + result.Check(testkit.Rows("7 8 7 7 8")) + result = tk.MustQuery(`select week("aa", 1), week(null, 2), week(11, 2), week(12.99, 2);`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select week("aa"), week(null), week(11), week(12.99);`) + result.Check(testkit.Rows(" ")) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a datetime)`) + _, err = tk.Exec(`insert into t select week("aa", 1)`) + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) + tk.MustExec(`insert into t select now()`) + _, err = tk.Exec(`update t set a = week("aa", 1)`) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) + _, err = tk.Exec(`delete from t where a = week("aa", 1)`) + require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) + + // for weekofyear + result = tk.MustQuery(`select weekofyear("2012-12-22"), weekofyear("2008-02-20"), weekofyear("aa"), weekofyear(null), weekofyear(11), weekofyear(12.99);`) + result.Check(testkit.Rows("51 8 ")) + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a bigint)`) + _, err = tk.Exec(`insert into t select weekofyear("aa")`) + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) + + tk.MustExec(`insert into t select 1`) + _, err = tk.Exec(`update t set a = weekofyear("aa")`) + require.True(t, terror.ErrorEqual(err, types.ErrWrongValue)) + _, err = tk.Exec(`delete from t where a = weekofyear("aa")`) + require.Equal(t, types.ErrWrongValue.Code(), errors.Cause(err).(*terror.Error).Code(), "err %v", err) + + // for weekday + result = tk.MustQuery(`select weekday("2012-12-20"), weekday("2012-12-21"), weekday("2012-12-22"), weekday("2012-12-23"), weekday("2012-12-24"), weekday("2012-12-25"), weekday("2012-12-26"), weekday("2012-12-27");`) + result.Check(testkit.Rows("3 4 5 6 0 1 2 3")) + result = tk.MustQuery(`select weekday("2012-12-90"), weekday("0000-00-00"), weekday("aa"), weekday(null), weekday(11), weekday(12.99);`) + result.Check(testkit.Rows(" ")) + + // for quarter + result = tk.MustQuery(`select quarter("2012-00-20"), quarter("2012-01-21"), quarter("2012-03-22"), quarter("2012-05-23"), quarter("2012-08-24"), quarter("2012-09-25"), quarter("2012-11-26"), quarter("2012-12-27");`) + result.Check(testkit.Rows("0 1 1 2 3 3 4 4")) + result = tk.MustQuery(`select quarter("2012-14-20"), quarter("aa"), quarter(null), quarter(11), quarter(12.99);`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select quarter("0000-00-00"), quarter("0000-00-00 00:00:00");`) + result.Check(testkit.Rows("0 0")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + result = tk.MustQuery(`select quarter(0), quarter(0.0), quarter(0e1), quarter(0.00);`) + result.Check(testkit.Rows("0 0 0 0")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + + // for from_days + result = tk.MustQuery(`select from_days(0), from_days(-199), from_days(1111), from_days(120), from_days(1), from_days(1111111), from_days(9999999), from_days(22222);`) + result.Check(testkit.Rows("0000-00-00 0000-00-00 0003-01-16 0000-00-00 0000-00-00 3042-02-13 0000-00-00 0060-11-03")) + result = tk.MustQuery(`select from_days("2012-14-20"), from_days("111a"), from_days("aa"), from_days(null), from_days("123asf"), from_days(12.99);`) + result.Check(testkit.Rows("0005-07-05 0000-00-00 0000-00-00 0000-00-00 0000-00-00")) + + // Fix issue #3923 + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00');") + result.Check(testkit.Rows("00:00:00")) + result = tk.MustQuery("select timediff('12:00:00', cast('2004-12-30 12:00:00' as time));") + result.Check(testkit.Rows("00:00:00")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:00' as time), '2004-12-30 12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('2004-12-30 12:00:00', cast('2004-12-30 12:00:00' as time));") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '2004-12-30 12:00:00');") + result.Check(testkit.Rows("00:00:01")) + result = tk.MustQuery("select timediff('2004-12-30 12:00:00', cast('2004-12-30 12:00:01' as datetime));") + result.Check(testkit.Rows("-00:00:01")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as time), '-34 00:00:00');") + result.Check(testkit.Rows("828:00:01")) + result = tk.MustQuery("select timediff('-34 00:00:00', cast('2004-12-30 12:00:01' as time));") + result.Check(testkit.Rows("-828:00:01")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), cast('2004-12-30 11:00:01' as datetime));") + result.Check(testkit.Rows("01:00:00")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '2004-12-30 12:00:00.1');") + result.Check(testkit.Rows("00:00:00.9")) + result = tk.MustQuery("select timediff('2004-12-30 12:00:00.1', cast('2004-12-30 12:00:01' as datetime));") + result.Check(testkit.Rows("-00:00:00.9")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as datetime), '-34 124:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('-34 124:00:00', cast('2004-12-30 12:00:01' as datetime));") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff(cast('2004-12-30 12:00:01' as time), '-34 124:00:00');") + result.Check(testkit.Rows("838:59:59")) + result = tk.MustQuery("select timediff('-34 124:00:00', cast('2004-12-30 12:00:01' as time));") + result.Check(testkit.Rows("-838:59:59")) + result = tk.MustQuery("select timediff(cast('2004-12-30' as datetime), '12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('12:00:00', cast('2004-12-30' as datetime));") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('12:00:00', '-34 12:00:00');") + result.Check(testkit.Rows("838:59:59")) + result = tk.MustQuery("select timediff('12:00:00', '34 12:00:00');") + result.Check(testkit.Rows("-816:00:00")) + result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '-34 12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('-34 12:00:00', '2014-1-2 12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('12:00:00', '2014-1-2 12:00:00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timediff('2014-1-2 12:00:00', '2014-1-1 12:00:00');") + result.Check(testkit.Rows("24:00:00")) + tk.MustQuery("select timediff(cast('10:10:10' as time), cast('10:10:11' as time))").Check(testkit.Rows("-00:00:01")) + + result = tk.MustQuery("select timestampadd(MINUTE, 1, '2003-01-02'), timestampadd(WEEK, 1, '2003-01-02 23:59:59')" + + ", timestampadd(MICROSECOND, 1, 950501);") + result.Check(testkit.Rows("2003-01-02 00:01:00 2003-01-09 23:59:59 1995-05-01 00:00:00.000001")) + result = tk.MustQuery("select timestampadd(day, 2, 950501), timestampadd(MINUTE, 37.5,'2003-01-02'), timestampadd(MINUTE, 37.49,'2003-01-02')," + + " timestampadd(YeAr, 1, '2003-01-02');") + result.Check(testkit.Rows("1995-05-03 00:00:00 2003-01-02 00:38:00 2003-01-02 00:37:00 2004-01-02 00:00:00")) + result = tk.MustQuery("select to_seconds(950501), to_seconds('2009-11-29'), to_seconds('2009-11-29 13:43:32'), to_seconds('09-11-29 13:43:32');") + result.Check(testkit.Rows("62966505600 63426672000 63426721412 63426721412")) + result = tk.MustQuery("select to_days(950501), to_days('2007-10-07'), to_days('2007-10-07 00:00:59'), to_days('0000-01-01')") + result.Check(testkit.Rows("728779 733321 733321 1")) + + result = tk.MustQuery("select last_day('2003-02-05'), last_day('2004-02-05'), last_day('2004-01-01 01:01:01'), last_day(950501);") + result.Check(testkit.Rows("2003-02-28 2004-02-29 2004-01-31 1995-05-31")) + + tk.MustExec("SET SQL_MODE='';") + result = tk.MustQuery("select last_day('0000-00-00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select to_days('0000-00-00');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select to_seconds('0000-00-00');") + result.Check(testkit.Rows("")) + + result = tk.MustQuery("select timestamp('2003-12-31'), timestamp('2003-12-31 12:00:00','12:00:00');") + result.Check(testkit.Rows("2003-12-31 00:00:00 2004-01-01 00:00:00")) + result = tk.MustQuery("select timestamp(20170118123950.123), timestamp(20170118123950.999);") + result.Check(testkit.Rows("2017-01-18 12:39:50.123 2017-01-18 12:39:50.999")) + // Issue https://github.com/pingcap/tidb/issues/20003 + result = tk.MustQuery("select timestamp(0.0001, 0.00001);") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select timestamp('2003-12-31', '01:01:01.01'), timestamp('2003-12-31 12:34', '01:01:01.01')," + + " timestamp('2008-12-31','00:00:00.0'), timestamp('2008-12-31 00:00:00.000');") + + tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 00:00:01")) + tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-01 01:00:00")) + tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-01-02")) + tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2001-02-01")) + tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as date))`).Check(testkit.Rows("2002-01-01")) + tk.MustQuery(`select timestampadd(second, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 00:00:01")) + tk.MustQuery(`select timestampadd(hour, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-01 01:00:00")) + tk.MustQuery(`select timestampadd(day, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-01-02 00:00:00")) + tk.MustQuery(`select timestampadd(month, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2001-02-01 00:00:00")) + tk.MustQuery(`select timestampadd(year, 1, cast("2001-01-01" as datetime))`).Check(testkit.Rows("2002-01-01 00:00:00")) + + result.Check(testkit.Rows("2003-12-31 01:01:01.01 2003-12-31 13:35:01.01 2008-12-31 00:00:00.0 2008-12-31 00:00:00.000")) + result = tk.MustQuery("select timestamp('2003-12-31', 1), timestamp('2003-12-31', -1);") + result.Check(testkit.Rows("2003-12-31 00:00:01 2003-12-30 23:59:59")) + result = tk.MustQuery("select timestamp('2003-12-31', '2000-12-12 01:01:01.01'), timestamp('2003-14-31','01:01:01.01');") + result.Check(testkit.Rows(" ")) + + result = tk.MustQuery("select TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01'), TIMESTAMPDIFF(yEaR,'2002-05-01', " + + "'2001-01-01'), TIMESTAMPDIFF(minute,binary('2003-02-01'),'2003-05-01 12:05:55'), TIMESTAMPDIFF(day," + + "'1995-05-02', 950501);") + result.Check(testkit.Rows("3 -1 128885 -1")) + + result = tk.MustQuery("select datediff('2007-12-31 23:59:59','2007-12-30'), datediff('2010-11-30 23:59:59', " + + "'2010-12-31'), datediff(950501,'2016-01-13'), datediff(950501.9,'2016-01-13'), datediff(binary(950501), '2016-01-13');") + result.Check(testkit.Rows("1 -31 -7562 -7562 -7562")) + result = tk.MustQuery("select datediff('0000-01-01','0001-01-01'), datediff('0001-00-01', '0001-00-01'), datediff('0001-01-00','0001-01-00'), datediff('2017-01-01','2017-01-01');") + result.Check(testkit.Rows("-365 0")) + + // for ADDTIME + result = tk.MustQuery("select addtime('01:01:11', '00:00:01.013'), addtime('01:01:11.00', '00:00:01'), addtime" + + "('2017-01-01 01:01:11.12', '00:00:01'), addtime('2017-01-01 01:01:11.12', '00:00:01.88');") + result.Check(testkit.Rows("01:01:12.013000 01:01:12 2017-01-01 01:01:12.120000 2017-01-01 01:01:13")) + result = tk.MustQuery("select addtime(cast('01:01:11' as time(4)), '00:00:01.013'), addtime(cast('01:01:11.00' " + + "as datetime(3)), '00:00:01')," + " addtime(cast('2017-01-01 01:01:11.12' as date), '00:00:01'), addtime(cast" + + "(cast('2017-01-01 01:01:11.12' as date) as datetime(2)), '00:00:01.88');") + result.Check(testkit.Rows("01:01:12.0130 2001-01-11 00:00:01.000 00:00:01 2017-01-01 00:00:01.88")) + result = tk.MustQuery("select addtime('2017-01-01 01:01:01', 5), addtime('2017-01-01 01:01:01', -5), addtime('2017-01-01 01:01:01', 0.0), addtime('2017-01-01 01:01:01', 1.34);") + result.Check(testkit.Rows("2017-01-01 01:01:06 2017-01-01 01:00:56 2017-01-01 01:01:01 2017-01-01 01:01:02.340000")) + result = tk.MustQuery("select addtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time)), addtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time(5)))") + result.Check(testkit.Rows("2001-01-11 00:00:01.000 2001-01-11 00:00:01.00000")) + result = tk.MustQuery("select addtime(cast('01:01:11.00' as date), cast('00:00:01' as time));") + result.Check(testkit.Rows("00:00:01")) + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a datetime, b timestamp, c time)") + tk.MustExec(`insert into t values("2017-01-01 12:30:31", "2017-01-01 12:30:31", "01:01:01")`) + result = tk.MustQuery("select addtime(a, b), addtime(cast(a as date), b), addtime(b,a), addtime(a,c), addtime(b," + + "c), addtime(c,a), addtime(c,b)" + + " from t;") + result.Check(testkit.Rows(" 2017-01-01 13:31:32 2017-01-01 13:31:32 ")) + result = tk.MustQuery("select addtime('01:01:11', cast('1' as time))") + result.Check(testkit.Rows("01:01:12")) + tk.MustQuery("select addtime(cast(null as char(20)), cast('1' as time))").Check(testkit.Rows("")) + require.NoError(t, tk.QueryToErr(`select addtime("01:01:11", cast('sdf' as time))`)) + tk.MustQuery(`select addtime("01:01:11", cast(null as char(20)))`).Check(testkit.Rows("")) + tk.MustQuery(`select addtime(cast(1 as time), cast(1 as time))`).Check(testkit.Rows("00:00:02")) + tk.MustQuery(`select addtime(cast(null as time), cast(1 as time))`).Check(testkit.Rows("")) + tk.MustQuery(`select addtime(cast(1 as time), cast(null as time))`).Check(testkit.Rows("")) + + // for SUBTIME + result = tk.MustQuery("select subtime('01:01:11', '00:00:01.013'), subtime('01:01:11.00', '00:00:01'), subtime" + + "('2017-01-01 01:01:11.12', '00:00:01'), subtime('2017-01-01 01:01:11.12', '00:00:01.88');") + result.Check(testkit.Rows("01:01:09.987000 01:01:10 2017-01-01 01:01:10.120000 2017-01-01 01:01:09.240000")) + result = tk.MustQuery("select subtime(cast('01:01:11' as time(4)), '00:00:01.013'), subtime(cast('01:01:11.00' " + + "as datetime(3)), '00:00:01')," + " subtime(cast('2017-01-01 01:01:11.12' as date), '00:00:01'), subtime(cast" + + "(cast('2017-01-01 01:01:11.12' as date) as datetime(2)), '00:00:01.88');") + result.Check(testkit.Rows("01:01:09.9870 2001-01-10 23:59:59.000 -00:00:01 2016-12-31 23:59:58.12")) + result = tk.MustQuery("select subtime('2017-01-01 01:01:01', 5), subtime('2017-01-01 01:01:01', -5), subtime('2017-01-01 01:01:01', 0.0), subtime('2017-01-01 01:01:01', 1.34);") + result.Check(testkit.Rows("2017-01-01 01:00:56 2017-01-01 01:01:06 2017-01-01 01:01:01 2017-01-01 01:00:59.660000")) + result = tk.MustQuery("select subtime('01:01:11', '0:0:1.013'), subtime('01:01:11.00', '0:0:1'), subtime('2017-01-01 01:01:11.12', '0:0:1'), subtime('2017-01-01 01:01:11.12', '0:0:1.120000');") + result.Check(testkit.Rows("01:01:09.987000 01:01:10 2017-01-01 01:01:10.120000 2017-01-01 01:01:10")) + result = tk.MustQuery("select subtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time)), subtime(cast('01:01:11.00' as datetime(3)), cast('00:00:01' as time(5)))") + result.Check(testkit.Rows("2001-01-10 23:59:59.000 2001-01-10 23:59:59.00000")) + result = tk.MustQuery("select subtime(cast('01:01:11.00' as date), cast('00:00:01' as time));") + result.Check(testkit.Rows("-00:00:01")) + result = tk.MustQuery("select subtime(a, b), subtime(cast(a as date), b), subtime(b,a), subtime(a,c), subtime(b," + + "c), subtime(c,a), subtime(c,b) from t;") + result.Check(testkit.Rows(" 2017-01-01 11:29:30 2017-01-01 11:29:30 ")) + tk.MustQuery("select subtime(cast('10:10:10' as time), cast('9:10:10' as time))").Check(testkit.Rows("01:00:00")) + tk.MustQuery("select subtime('10:10:10', cast('9:10:10' as time))").Check(testkit.Rows("01:00:00")) + + // SUBTIME issue #31868 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a DATETIME(6))") + tk.MustExec(`insert into t values ("1000-01-01 01:00:00.000000"), ("1000-01-01 01:00:00.000001")`) + tk.MustQuery(`SELECT SUBTIME(a, '00:00:00.000001') FROM t ORDER BY a;`).Check(testkit.Rows("1000-01-01 00:59:59.999999", "1000-01-01 01:00:00.000000")) + tk.MustQuery(`SELECT SUBTIME(a, '10:00:00.000001') FROM t ORDER BY a;`).Check(testkit.Rows("0999-12-31 14:59:59.999999", "0999-12-31 15:00:00.000000")) + + // ADDTIME & SUBTIME issue #5966 + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a datetime, b timestamp, c time, d date, e bit(1))") + tk.MustExec(`insert into t values("2017-01-01 12:30:31", "2017-01-01 12:30:31", "01:01:01", "2017-01-01", 0b1)`) + + result = tk.MustQuery("select addtime(a, e), addtime(b, e), addtime(c, e), addtime(d, e) from t") + result.Check(testkit.Rows(" ")) + result = tk.MustQuery("select addtime('2017-01-01 01:01:01', 0b1), addtime('2017-01-01', b'1'), addtime('01:01:01', 0b1011)") + result.Check(testkit.Rows(" ")) + result = tk.MustQuery("select addtime('2017-01-01', 1), addtime('2017-01-01 01:01:01', 1), addtime(cast('2017-01-01' as date), 1)") + result.Check(testkit.Rows("2017-01-01 00:00:01 2017-01-01 01:01:02 00:00:01")) + result = tk.MustQuery("select subtime(a, e), subtime(b, e), subtime(c, e), subtime(d, e) from t") + result.Check(testkit.Rows(" ")) + result = tk.MustQuery("select subtime('2017-01-01 01:01:01', 0b1), subtime('2017-01-01', b'1'), subtime('01:01:01', 0b1011)") + result.Check(testkit.Rows(" ")) + result = tk.MustQuery("select subtime('2017-01-01', 1), subtime('2017-01-01 01:01:01', 1), subtime(cast('2017-01-01' as date), 1)") + result.Check(testkit.Rows("2016-12-31 23:59:59 2017-01-01 01:01:00 -00:00:01")) + + result = tk.MustQuery("select addtime(-32073, 0), addtime(0, -32073);") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select addtime(-32073, c), addtime(c, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select addtime(a, -32073), addtime(b, -32073), addtime(d, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + + result = tk.MustQuery("select subtime(-32073, 0), subtime(0, -32073);") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select subtime(-32073, c), subtime(c, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + result = tk.MustQuery("select subtime(a, -32073), subtime(b, -32073), subtime(d, -32073) from t;") + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'", + "Warning|1292|Truncated incorrect time value: '-32073'")) + + // fixed issue #3986 + tk.MustExec("SET SQL_MODE='NO_ENGINE_SUBSTITUTION';") + tk.MustExec("SET TIME_ZONE='+03:00';") + tk.MustExec("DROP TABLE IF EXISTS t;") + tk.MustExec("CREATE TABLE t (ix TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP);") + tk.MustExec("INSERT INTO t VALUES (0), (20030101010160), (20030101016001), (20030101240101), (20030132010101), (20031301010101), (20031200000000), (20030000000000);") + result = tk.MustQuery("SELECT CAST(ix AS SIGNED) FROM t;") + result.Check(testkit.Rows("0", "0", "0", "0", "0", "0", "0", "0")) + + // test time + result = tk.MustQuery("select time('2003-12-31 01:02:03')") + result.Check(testkit.Rows("01:02:03")) + result = tk.MustQuery("select time('2003-12-31 01:02:03.000123')") + result.Check(testkit.Rows("01:02:03.000123")) + result = tk.MustQuery("select time('01:02:03.000123')") + result.Check(testkit.Rows("01:02:03.000123")) + result = tk.MustQuery("select time('01:02:03')") + result.Check(testkit.Rows("01:02:03")) + result = tk.MustQuery("select time('-838:59:59.000000')") + result.Check(testkit.Rows("-838:59:59.000000")) + result = tk.MustQuery("select time('-838:59:59.000001')") + result.Check(testkit.Rows("-838:59:59.000000")) + result = tk.MustQuery("select time('-839:59:59.000000')") + result.Check(testkit.Rows("-838:59:59.000000")) + result = tk.MustQuery("select time('840:59:59.000000')") + result.Check(testkit.Rows("838:59:59.000000")) + // FIXME: #issue 4193 + // result = tk.MustQuery("select time('840:59:60.000000')") + // result.Check(testkit.Rows("")) + // result = tk.MustQuery("select time('800:59:59.9999999')") + // result.Check(testkit.Rows("801:00:00.000000")) + // result = tk.MustQuery("select time('12003-12-10 01:02:03.000123')") + // result.Check(testkit.Rows("") + // result = tk.MustQuery("select time('')") + // result.Check(testkit.Rows("") + // result = tk.MustQuery("select time('2003-12-10-10 01:02:03.000123')") + // result.Check(testkit.Rows("00:20:03") + + // Issue 20995 + result = tk.MustQuery("select time('0.1234567')") + result.Check(testkit.Rows("00:00:00.123457")) + + // for hour + result = tk.MustQuery(`SELECT hour("12:13:14.123456"), hour("12:13:14.000010"), hour("272:59:55"), hour(020005), hour(null), hour("27aaaa2:59:55");`) + result.Check(testkit.Rows("12 12 272 2 ")) + + // for hour, issue #4340 + result = tk.MustQuery(`SELECT HOUR(20171222020005);`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`SELECT HOUR(20171222020005.1);`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`SELECT HOUR(20171222020005.1e0);`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`SELECT HOUR("20171222020005");`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`SELECT HOUR("20171222020005.1");`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`select hour(20171222);`) + result.Check(testkit.Rows("")) + result = tk.MustQuery(`select hour(8381222);`) + result.Check(testkit.Rows("838")) + result = tk.MustQuery(`select hour(10000000000);`) + result.Check(testkit.Rows("")) + result = tk.MustQuery(`select hour(10100000000);`) + result.Check(testkit.Rows("")) + result = tk.MustQuery(`select hour(10001000000);`) + result.Check(testkit.Rows("")) + result = tk.MustQuery(`select hour(10101000000);`) + result.Check(testkit.Rows("0")) + + // for minute + result = tk.MustQuery(`SELECT minute("12:13:14.123456"), minute("12:13:14.000010"), minute("272:59:55"), minute(null), minute("27aaaa2:59:55");`) + result.Check(testkit.Rows("13 13 59 ")) + + // for second + result = tk.MustQuery(`SELECT second("12:13:14.123456"), second("12:13:14.000010"), second("272:59:55"), second(null), second("27aaaa2:59:55");`) + result.Check(testkit.Rows("14 14 55 ")) + + // for microsecond + result = tk.MustQuery(`SELECT microsecond("12:00:00.123456"), microsecond("12:00:00.000010"), microsecond(null), microsecond("27aaaa2:59:55");`) + result.Check(testkit.Rows("123456 10 ")) + + // for period_add + result = tk.MustQuery(`SELECT period_add(200807, 2), period_add(200807, -2);`) + result.Check(testkit.Rows("200809 200805")) + result = tk.MustQuery(`SELECT period_add(NULL, 2), period_add(-191, NULL), period_add(NULL, NULL), period_add(12.09, -2), period_add("200207aa", "1aa");`) + result.Check(testkit.Rows(" 200010 200208")) + for _, errPeriod := range []string{ + "period_add(0, 20)", "period_add(0, 0)", "period_add(-1, 1)", "period_add(200013, 1)", "period_add(-200012, 1)", "period_add('', '')", + } { + err := tk.QueryToErr(fmt.Sprintf("SELECT %v;", errPeriod)) + require.Error(t, err, "[expression:1210]Incorrect arguments to period_add") + } + + // for period_diff + result = tk.MustQuery(`SELECT period_diff(200807, 200705), period_diff(200807, 200908);`) + result.Check(testkit.Rows("14 -13")) + result = tk.MustQuery(`SELECT period_diff(NULL, 2), period_diff(-191, NULL), period_diff(NULL, NULL), period_diff(12.09, 2), period_diff("12aa", "11aa");`) + result.Check(testkit.Rows(" 10 1")) + for _, errPeriod := range []string{ + "period_diff(-00013,1)", "period_diff(00013,1)", "period_diff(0, 0)", "period_diff(200013, 1)", "period_diff(5612, 4513)", "period_diff('', '')", + } { + err := tk.QueryToErr(fmt.Sprintf("SELECT %v;", errPeriod)) + require.Error(t, err, "[expression:1210]Incorrect arguments to period_diff") + } + + // TODO: fix `CAST(xx as duration)` and release the test below: + // result = tk.MustQuery(`SELECT hour("aaa"), hour(123456), hour(1234567);`) + // result = tk.MustQuery(`SELECT minute("aaa"), minute(123456), minute(1234567);`) + // result = tk.MustQuery(`SELECT second("aaa"), second(123456), second(1234567);`) + // result = tk.MustQuery(`SELECT microsecond("aaa"), microsecond(123456), microsecond(1234567);`) + + // for time_format + result = tk.MustQuery("SELECT TIME_FORMAT('150:02:28', '%H:%i:%s %p');") + result.Check(testkit.Rows("150:02:28 AM")) + result = tk.MustQuery("SELECT TIME_FORMAT('bad string', '%H:%i:%s %p');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("SELECT TIME_FORMAT(null, '%H:%i:%s %p');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("SELECT TIME_FORMAT(123, '%H:%i:%s %p');") + result.Check(testkit.Rows("00:01:23 AM")) + result = tk.MustQuery("SELECT TIME_FORMAT('24:00:00', '%r');") + result.Check(testkit.Rows("12:00:00 AM")) + result = tk.MustQuery("SELECT TIME_FORMAT('25:00:00', '%r');") + result.Check(testkit.Rows("01:00:00 AM")) + result = tk.MustQuery("SELECT TIME_FORMAT('24:00:00', '%l %p');") + result.Check(testkit.Rows("12 AM")) + + // for date_format + result = tk.MustQuery(`SELECT DATE_FORMAT('2017-06-15', '%W %M %e %Y %r %y');`) + result.Check(testkit.Rows("Thursday June 15 2017 12:00:00 AM 17")) + result = tk.MustQuery(`SELECT DATE_FORMAT(151113102019.12, '%W %M %e %Y %r %y');`) + result.Check(testkit.Rows("Friday November 13 2015 10:20:19 AM 15")) + result = tk.MustQuery(`SELECT DATE_FORMAT('0000-00-00', '%W %M %e %Y %r %y');`) + result.Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'")) + result = tk.MustQuery(`SELECT DATE_FORMAT('0', '%W %M %e %Y %r %y'), DATE_FORMAT('0.0', '%W %M %e %Y %r %y'), DATE_FORMAT(0, 0);`) + result.Check(testkit.Rows(" 0")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Incorrect time value: '0'", + "Warning|1292|Incorrect datetime value: '0.0'")) + result = tk.MustQuery(`SELECT DATE_FORMAT(0, '%W %M %e %Y %r %y'), DATE_FORMAT(0.0, '%W %M %e %Y %r %y');`) + result.Check(testkit.Rows(" ")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + + // for yearweek + result = tk.MustQuery(`select yearweek("2014-12-27"), yearweek("2014-29-27"), yearweek("2014-00-27"), yearweek("2014-12-27 12:38:32"), yearweek("2014-12-27 12:38:32.1111111"), yearweek("2014-12-27 12:90:32"), yearweek("2014-12-27 89:38:32.1111111");`) + result.Check(testkit.Rows("201451 201451 201451 ")) + result = tk.MustQuery(`select yearweek(12121), yearweek(1.00009), yearweek("aaaaa"), yearweek(""), yearweek(NULL);`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select yearweek("0000-00-00"), yearweek("2019-01-29", "aa"), yearweek("2011-01-01", null);`) + result.Check(testkit.Rows(" 201904 201052")) + + // for dayOfWeek, dayOfMonth, dayOfYear + result = tk.MustQuery(`select dayOfWeek(null), dayOfWeek("2017-08-12"), dayOfWeek("0000-00-00"), dayOfWeek("2017-00-00"), dayOfWeek("0000-00-00 12:12:12"), dayOfWeek("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 7 ")) + result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 224 ")) + result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 12 0 0 0 0")) + + tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") + result = tk.MustQuery(`select dayOfWeek(null), dayOfWeek("2017-08-12"), dayOfWeek("0000-00-00"), dayOfWeek("2017-00-00"), dayOfWeek("0000-00-00 12:12:12"), dayOfWeek("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 7 ")) + result = tk.MustQuery(`select dayOfYear(null), dayOfYear("2017-08-12"), dayOfYear("0000-00-00"), dayOfYear("2017-00-00"), dayOfYear("0000-00-00 12:12:12"), dayOfYear("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 224 ")) + result = tk.MustQuery(`select dayOfMonth(null), dayOfMonth("2017-08-12"), dayOfMonth("0000-00-00"), dayOfMonth("2017-00-00"), dayOfMonth("0000-00-00 12:12:12"), dayOfMonth("2017-00-00 12:12:12")`) + result.Check(testkit.Rows(" 12 0 0 0")) + + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a bigint)`) + tk.MustExec(`insert into t value(1)`) + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + + _, err = tk.Exec("insert into t value(dayOfWeek('0000-00-00'))") + require.True(t, types.ErrWrongValue.Equal(err), "%v", err) + _, err = tk.Exec(`update t set a = dayOfWeek("0000-00-00")`) + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`delete from t where a = dayOfWeek(123)`) + require.NoError(t, err) + + tk.MustExec("insert into t value(dayOfMonth('2017-00-00'))") + tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") + tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE';") + tk.MustExec("insert into t value(dayOfMonth('0000-00-00'))") + tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") + tk.MustExec(`update t set a = dayOfMonth("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE,STRICT_TRANS_TABLES';") + _, err = tk.Exec("insert into t value(dayOfMonth('0000-00-00'))") + require.True(t, types.ErrWrongValue.Equal(err)) + tk.MustExec("insert into t value(0)") + _, err = tk.Exec(`update t set a = dayOfMonth("0000-00-00")`) + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`delete from t where a = dayOfMonth(123)`) + require.NoError(t, err) + + _, err = tk.Exec("insert into t value(dayOfYear('0000-00-00'))") + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`update t set a = dayOfYear("0000-00-00")`) + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`delete from t where a = dayOfYear(123)`) + require.NoError(t, err) + + tk.MustExec("set sql_mode = ''") + + // for unix_timestamp + tk.MustExec("SET time_zone = '+00:00';") + tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00.000001');").Check(testkit.Rows("0.000000")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00.999999');").Check(testkit.Rows("0.000000")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:01.000000');").Check(testkit.Rows("1.000000")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:07.999999');").Check(testkit.Rows("2147483647.999999")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:08.000000');").Check(testkit.Rows("2147483648.000000")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-18 23:59:59.999999');").Check(testkit.Rows("32536771199.999999")) + tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-19 00:00:00.000000');").Check(testkit.Rows("0.000000")) + + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113);") + result.Check(testkit.Rows("1447372800")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(20151113);") + result.Check(testkit.Rows("1447372800")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019);") + result.Check(testkit.Rows("1447410019")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019e0);") + result.Check(testkit.Rows("1447410019.000000")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(15111310201912e-2);") + result.Check(testkit.Rows("1447410019.120000")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019.12);") + result.Check(testkit.Rows("1447410019.12")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(151113102019.1234567);") + result.Check(testkit.Rows("1447410019.123457")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(20151113102019);") + result.Check(testkit.Rows("1447410019")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 10:20:19');") + result.Check(testkit.Rows("1447410019")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 10:20:19.012');") + result.Check(testkit.Rows("1447410019.012")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00');") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1969-12-31 23:59:59');") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-13-01 00:00:00');") + // FIXME: MySQL returns 0 here. + result.Check(testkit.Rows("")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 03:14:07.999999');") + result.Check(testkit.Rows("2147483647.999999")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-18 23:59:59.999999');") + result.Check(testkit.Rows("32536771199.999999")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('3001-01-19 00:00:00');") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP(0);") + result.Check(testkit.Rows("0")) + // result = tk.MustQuery("SELECT UNIX_TIMESTAMP(-1);") + // result.Check(testkit.Rows("0")) + // result = tk.MustQuery("SELECT UNIX_TIMESTAMP(12345);") + // result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2017-01-01')") + result.Check(testkit.Rows("1483228800")) + // Test different time zone. + tk.MustExec("SET time_zone = '+08:00';") + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 00:00:00');") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('1970-01-01 08:00:00');") + result.Check(testkit.Rows("0")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2015-11-13 18:20:19.012'), UNIX_TIMESTAMP('2015-11-13 18:20:19.0123');") + result.Check(testkit.Rows("1447410019.012 1447410019.0123")) + result = tk.MustQuery("SELECT UNIX_TIMESTAMP('2038-01-19 11:14:07.999999');") + result.Check(testkit.Rows("2147483647.999999")) + + result = tk.MustQuery("SELECT TIME_FORMAT('bad string', '%H:%i:%s %p');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("SELECT TIME_FORMAT(null, '%H:%i:%s %p');") + result.Check(testkit.Rows("")) + result = tk.MustQuery("SELECT TIME_FORMAT(123, '%H:%i:%s %p');") + result.Check(testkit.Rows("00:01:23 AM")) + + // for monthname + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a varchar(10))`) + tk.MustExec(`insert into t value("abc")`) + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustExec(`update t set a = monthname("0000-00-00")`) + tk.MustExec("set sql_mode = 'NO_ZERO_DATE'") + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") + tk.MustExec(`update t set a = monthname("0000-00-00")`) + tk.MustExec("set sql_mode = ''") + tk.MustExec("insert into t value(monthname('0000-00-00'))") + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_DATE'") + _, err = tk.Exec(`update t set a = monthname("0000-00-00")`) + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`delete from t where a = monthname(123)`) + require.NoError(t, err) + result = tk.MustQuery(`select monthname("2017-12-01"), monthname("0000-00-00"), monthname("0000-01-00"), monthname("0000-01-00 00:00:00")`) + result.Check(testkit.Rows("December January January")) + tk.MustQuery("show warnings").CheckContain("Incorrect datetime value: '0000-00-00 00:00:00.000000'") + + // for dayname + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a varchar(10))`) + tk.MustExec(`insert into t value("abc")`) + tk.MustExec("set sql_mode = 'STRICT_TRANS_TABLES'") + + _, err = tk.Exec("insert into t value(dayname('0000-00-00'))") + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`update t set a = dayname("0000-00-00")`) + require.True(t, types.ErrWrongValue.Equal(err)) + _, err = tk.Exec(`delete from t where a = dayname(123)`) + require.NoError(t, err) + result = tk.MustQuery(`select dayname("2017-12-01"), dayname("0000-00-00"), dayname("0000-01-00"), dayname("0000-01-00 00:00:00")`) + result.Check(testkit.Rows("Friday ")) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00.000000'", + "Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'", + "Warning|1292|Incorrect datetime value: '0000-01-00 00:00:00.000000'")) + // for dayname implicit cast to boolean and real + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07')`) + result.Check(testkit.Rows()) + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is true`) + result.Check(testkit.Rows()) + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-07') is false`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08')`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is true`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`select 1 from dual where dayname('2016-03-08') is false`) + result.Check(testkit.Rows()) + result = tk.MustQuery(`select cast(dayname("2016-03-07") as double), cast(dayname("2016-03-08") as double)`) + result.Check(testkit.Rows("0 1")) + + // for sec_to_time + result = tk.MustQuery("select sec_to_time(NULL)") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select sec_to_time(2378), sec_to_time(3864000), sec_to_time(-3864000)") + result.Check(testkit.Rows("00:39:38 838:59:59 -838:59:59")) + result = tk.MustQuery("select sec_to_time(86401.4), sec_to_time(-86401.4), sec_to_time(864014e-1), sec_to_time(-864014e-1), sec_to_time('86401.4'), sec_to_time('-86401.4')") + result.Check(testkit.Rows("24:00:01.4 -24:00:01.4 24:00:01.400000 -24:00:01.400000 24:00:01.400000 -24:00:01.400000")) + result = tk.MustQuery("select sec_to_time(86401.54321), sec_to_time(86401.543212345)") + result.Check(testkit.Rows("24:00:01.54321 24:00:01.543212")) + result = tk.MustQuery("select sec_to_time('123.4'), sec_to_time('123.4567891'), sec_to_time('123')") + result.Check(testkit.Rows("00:02:03.400000 00:02:03.456789 00:02:03.000000")) + + // for time_to_sec + result = tk.MustQuery("select time_to_sec(NULL)") + result.Check(testkit.Rows("")) + result = tk.MustQuery("select time_to_sec('22:23:00'), time_to_sec('00:39:38'), time_to_sec('23:00'), time_to_sec('00:00'), time_to_sec('00:00:00'), time_to_sec('23:59:59')") + result.Check(testkit.Rows("80580 2378 82800 0 0 86399")) + result = tk.MustQuery("select time_to_sec('1:0'), time_to_sec('1:00'), time_to_sec('1:0:0'), time_to_sec('-02:00'), time_to_sec('-02:00:05'), time_to_sec('020005')") + result.Check(testkit.Rows("3600 3600 3600 -7200 -7205 7205")) + result = tk.MustQuery("select time_to_sec('20171222020005'), time_to_sec(020005), time_to_sec(20171222020005), time_to_sec(171222020005)") + result.Check(testkit.Rows("7205 7205 7205 7205")) + + // for str_to_date + result = tk.MustQuery("select str_to_date('01-01-2017', '%d-%m-%Y'), str_to_date('59:20:12 01-01-2017', '%s:%i:%H %d-%m-%Y'), str_to_date('59:20:12', '%s:%i:%H')") + result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) + result = tk.MustQuery("select str_to_date('aaa01-01-2017', 'aaa%d-%m-%Y'), str_to_date('59:20:12 aaa01-01-2017', '%s:%i:%H aaa%d-%m-%Y'), str_to_date('59:20:12aaa', '%s:%i:%Haaa')") + result.Check(testkit.Rows("2017-01-01 2017-01-01 12:20:59 12:20:59")) + + result = tk.MustQuery("select str_to_date('01-01-2017', '%d'), str_to_date('59', '%d-%Y')") + // TODO: MySQL returns " ". + result.Check(testkit.Rows("0000-00-01 ")) + result = tk.MustQuery("show warnings") + result.Sort().Check(testkit.RowsWithSep("|", + "Warning|1292|Incorrect datetime value: '0000-00-00 00:00:00'", + "Warning|1292|Truncated incorrect datetime value: '01-01-2017'")) + + result = tk.MustQuery("select str_to_date('2018-6-1', '%Y-%m-%d'), str_to_date('2018-6-1', '%Y-%c-%d'), str_to_date('59:20:1', '%s:%i:%k'), str_to_date('59:20:1', '%s:%i:%l')") + result.Check(testkit.Rows("2018-06-01 2018-06-01 01:20:59 01:20:59")) + + result = tk.MustQuery("select str_to_date('2020-07-04 11:22:33 PM c', '%Y-%m-%d %r')") + result.Check(testkit.Rows("2020-07-04 23:22:33")) + result = tk.MustQuery("show warnings") + result.Check(testkit.RowsWithSep("|", "Warning|1292|Truncated incorrect datetime value: '2020-07-04 11:22:33 PM c'")) + + result = tk.MustQuery("select str_to_date('11:22:33 PM', ' %r')") + result.Check(testkit.Rows("23:22:33")) + result = tk.MustQuery("show warnings") + result.Check(testkit.Rows()) + + // for maketime + tk.MustExec(`drop table if exists t`) + tk.MustExec(`create table t(a double, b float, c decimal(10,4));`) + tk.MustExec(`insert into t value(1.23, 2.34, 3.1415)`) + result = tk.MustQuery("select maketime(1,1,a), maketime(2,2,b), maketime(3,3,c) from t;") + result.Check(testkit.Rows("01:01:01.230000 02:02:02.340000 03:03:03.1415")) + result = tk.MustQuery("select maketime(12, 13, 14), maketime('12', '15', 30.1), maketime(0, 1, 59.1), maketime(0, 1, '59.1'), maketime(0, 1, 59.5)") + result.Check(testkit.Rows("12:13:14 12:15:30.1 00:01:59.1 00:01:59.100000 00:01:59.5")) + result = tk.MustQuery("select maketime(12, 15, 60), maketime(12, 15, '60'), maketime(12, 60, 0), maketime(12, 15, null)") + result.Check(testkit.Rows(" ")) + result = tk.MustQuery("select maketime('', '', ''), maketime('h', 'm', 's');") + result.Check(testkit.Rows("00:00:00.000000 00:00:00.000000")) + + // for get_format + result = tk.MustQuery(`select GET_FORMAT(DATE,'USA'), GET_FORMAT(DATE,'JIS'), GET_FORMAT(DATE,'ISO'), GET_FORMAT(DATE,'EUR'), + GET_FORMAT(DATE,'INTERNAL'), GET_FORMAT(DATETIME,'USA') , GET_FORMAT(DATETIME,'JIS'), GET_FORMAT(DATETIME,'ISO'), + GET_FORMAT(DATETIME,'EUR') , GET_FORMAT(DATETIME,'INTERNAL'), GET_FORMAT(TIME,'USA') , GET_FORMAT(TIME,'JIS'), + GET_FORMAT(TIME,'ISO'), GET_FORMAT(TIME,'EUR'), GET_FORMAT(TIME,'INTERNAL')`) + result.Check(testkit.Rows("%m.%d.%Y %Y-%m-%d %Y-%m-%d %d.%m.%Y %Y%m%d %Y-%m-%d %H.%i.%s %Y-%m-%d %H:%i:%s %Y-%m-%d %H:%i:%s %Y-%m-%d %H.%i.%s %Y%m%d%H%i%s %h:%i:%s %p %H:%i:%s %H:%i:%s %H.%i.%s %H%i%s")) + + // for convert_tz + result = tk.MustQuery(`select convert_tz("2004-01-01 12:00:00", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00.01234567", "+00:00", "+10:32");`) + result.Check(testkit.Rows("2004-01-01 22:32:00 2004-01-01 22:32:00.01 2004-01-01 22:32:00.012346")) + result = tk.MustQuery(`select convert_tz(20040101, "+00:00", "+10:32"), convert_tz(20040101.01, "+00:00", "+10:32"), convert_tz(20040101.01234567, "+00:00", "+10:32");`) + result.Check(testkit.Rows("2004-01-01 10:32:00 2004-01-01 10:32:00.00 2004-01-01 10:32:00.000000")) + result = tk.MustQuery(`select convert_tz(NULL, "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", NULL, "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", NULL);`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("a", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "a", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "a");`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "");`) + result.Check(testkit.Rows(" ")) + result = tk.MustQuery(`select convert_tz("0", "+00:00", "+10:32"), convert_tz("2004-01-01 12:00:00", "0", "+10:32"), convert_tz("2004-01-01 12:00:00", "+00:00", "0");`) + result.Check(testkit.Rows(" ")) + + // for from_unixtime + tk.MustExec(`set @@session.time_zone = "+08:00"`) + result = tk.MustQuery(`select from_unixtime(20170101), from_unixtime(20170101.9999999), from_unixtime(20170101.999), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x"), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x")`) + result.Check(testkit.Rows("1970-08-22 18:48:21 1970-08-22 18:48:22.000000 1970-08-22 18:48:21.999 1970 22nd August 06:48:21 1970 1970 22nd August 06:48:21 1970")) + tk.MustExec(`set @@session.time_zone = "+00:00"`) + result = tk.MustQuery(`select from_unixtime(20170101), from_unixtime(20170101.9999999), from_unixtime(20170101.999), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x"), from_unixtime(20170101.999, "%Y %D %M %h:%i:%s %x")`) + result.Check(testkit.Rows("1970-08-22 10:48:21 1970-08-22 10:48:22.000000 1970-08-22 10:48:21.999 1970 22nd August 10:48:21 1970 1970 22nd August 10:48:21 1970")) + tk.MustExec(`set @@session.time_zone = @@global.time_zone`) + + // for extract + result = tk.MustQuery(`select extract(day from '800:12:12'), extract(hour from '800:12:12'), extract(month from 20170101), extract(day_second from '2017-01-01 12:12:12')`) + result.Check(testkit.Rows("12 800 1 1121212")) + result = tk.MustQuery("select extract(day_microsecond from '2017-01-01 12:12:12'), extract(day_microsecond from '01 12:12:12'), extract(day_microsecond from '12:12:12'), extract(day_microsecond from '01 00:00:00.89')") + result.Check(testkit.Rows("1121212000000 361212000000 121212000000 240000890000")) + result = tk.MustQuery("select extract(day_second from '2017-01-01 12:12:12'), extract(day_second from '01 12:12:12'), extract(day_second from '12:12:12'), extract(day_second from '01 00:00:00.89')") + result.Check(testkit.Rows("1121212 361212 121212 240000")) + result = tk.MustQuery("select extract(day_minute from '2017-01-01 12:12:12'), extract(day_minute from '01 12:12:12'), extract(day_minute from '12:12:12'), extract(day_minute from '01 00:00:00.89')") + result.Check(testkit.Rows("11212 3612 1212 2400")) + result = tk.MustQuery("select extract(day_hour from '2017-01-01 12:12:12'), extract(day_hour from '01 12:12:12'), extract(day_hour from '12:12:12'), extract(day_hour from '01 00:00:00.89')") + result.Check(testkit.Rows("112 36 12 24")) + result = tk.MustQuery("select extract(day_microsecond from cast('2017-01-01 12:12:12' as datetime)), extract(day_second from cast('2017-01-01 12:12:12' as datetime)), extract(day_minute from cast('2017-01-01 12:12:12' as datetime)), extract(day_hour from cast('2017-01-01 12:12:12' as datetime))") + result.Check(testkit.Rows("1121212000000 1121212 11212 112")) + result = tk.MustQuery("select extract(day_microsecond from cast(20010101020304.050607 as decimal(20,6))), extract(day_second from cast(20010101020304.050607 as decimal(20,6))), extract(day_minute from cast(20010101020304.050607 as decimal(20,6))), extract(day_hour from cast(20010101020304.050607 as decimal(20,6))), extract(day from cast(20010101020304.050607 as decimal(20,6)))") + result.Check(testkit.Rows("1020304050607 1020304 10203 102 1")) + result = tk.MustQuery("select extract(day_microsecond from cast(1020304.050607 as decimal(20,6))), extract(day_second from cast(1020304.050607 as decimal(20,6))), extract(day_minute from cast(1020304.050607 as decimal(20,6))), extract(day_hour from cast(1020304.050607 as decimal(20,6))), extract(day from cast(1020304.050607 as decimal(20,6)))") + result.Check(testkit.Rows("1020304050607 1020304 10203 102 4")) + + // for adddate, subdate + dateArithmeticalTests := []struct { + Date string + Interval string + Unit string + AddResult string + SubResult string + }{ + {"\"2011-11-11\"", "1", "DAY", "2011-11-12", "2011-11-10"}, + {"NULL", "1", "DAY", "", ""}, + {"\"2011-11-11\"", "NULL", "DAY", "", ""}, + {"\"2011-11-11 10:10:10\"", "1000", "MICROSECOND", "2011-11-11 10:10:10.001000", "2011-11-11 10:10:09.999000"}, + {"\"2011-11-11 10:10:10\"", "\"10\"", "SECOND", "2011-11-11 10:10:20", "2011-11-11 10:10:00"}, + {"\"2011-11-11 10:10:10\"", "\"10\"", "MINUTE", "2011-11-11 10:20:10", "2011-11-11 10:00:10"}, + {"\"2011-11-11 10:10:10\"", "\"10\"", "HOUR", "2011-11-11 20:10:10", "2011-11-11 00:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"11\"", "DAY", "2011-11-22 10:10:10", "2011-10-31 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"2\"", "WEEK", "2011-11-25 10:10:10", "2011-10-28 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"2\"", "MONTH", "2012-01-11 10:10:10", "2011-09-11 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"4\"", "QUARTER", "2012-11-11 10:10:10", "2010-11-11 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"2\"", "YEAR", "2013-11-11 10:10:10", "2009-11-11 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"10.00100000\"", "SECOND_MICROSECOND", "2011-11-11 10:10:20.100000", "2011-11-11 10:09:59.900000"}, + {"\"2011-11-11 10:10:10\"", "\"10.0010000000\"", "SECOND_MICROSECOND", "2011-11-11 10:10:30", "2011-11-11 10:09:50"}, + {"\"2011-11-11 10:10:10\"", "\"10.0010000010\"", "SECOND_MICROSECOND", "2011-11-11 10:10:30.000010", "2011-11-11 10:09:49.999990"}, + {"\"2011-11-11 10:10:10\"", "\"10:10.100\"", "MINUTE_MICROSECOND", "2011-11-11 10:20:20.100000", "2011-11-11 09:59:59.900000"}, + {"\"2011-11-11 10:10:10\"", "\"10:10\"", "MINUTE_SECOND", "2011-11-11 10:20:20", "2011-11-11 10:00:00"}, + {"\"2011-11-11 10:10:10\"", "\"10:10:10.100\"", "HOUR_MICROSECOND", "2011-11-11 20:20:20.100000", "2011-11-10 23:59:59.900000"}, + {"\"2011-11-11 10:10:10\"", "\"10:10:10\"", "HOUR_SECOND", "2011-11-11 20:20:20", "2011-11-11 00:00:00"}, + {"\"2011-11-11 10:10:10\"", "\"10:10\"", "HOUR_MINUTE", "2011-11-11 20:20:10", "2011-11-11 00:00:10"}, + {"\"2011-11-11 10:10:10\"", "\"11 10:10:10.100\"", "DAY_MICROSECOND", "2011-11-22 20:20:20.100000", "2011-10-30 23:59:59.900000"}, + {"\"2011-11-11 10:10:10\"", "\"11 10:10:10\"", "DAY_SECOND", "2011-11-22 20:20:20", "2011-10-31 00:00:00"}, + {"\"2011-11-11 10:10:10\"", "\"11 10:10\"", "DAY_MINUTE", "2011-11-22 20:20:10", "2011-10-31 00:00:10"}, + {"\"2011-11-11 10:10:10\"", "\"11 10\"", "DAY_HOUR", "2011-11-22 20:10:10", "2011-10-31 00:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"11-1\"", "YEAR_MONTH", "2022-12-11 10:10:10", "2000-10-11 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"11-11\"", "YEAR_MONTH", "2023-10-11 10:10:10", "1999-12-11 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"20\"", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "19.88", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"19.88\"", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"prefix19suffix\"", "DAY", "2011-11-30 10:10:10", "2011-10-23 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"20-11\"", "DAY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"20,11\"", "daY", "2011-12-01 10:10:10", "2011-10-22 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"1000\"", "dAy", "2014-08-07 10:10:10", "2009-02-14 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "\"true\"", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10"}, + {"\"2011-11-11 10:10:10\"", "true", "Day", "2011-11-12 10:10:10", "2011-11-10 10:10:10"}, + {"\"2011-11-11\"", "1", "DAY", "2011-11-12", "2011-11-10"}, + {"\"2011-11-11\"", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"\"2011-11-11\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"\"2011-11-11\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, + {"\"2011-11-11\"", "\"10:10\"", "HOUR_MINUTE", "2011-11-11 10:10:00", "2011-11-10 13:50:00"}, + {"\"2011-11-11\"", "\"10:10:10\"", "HOUR_SECOND", "2011-11-11 10:10:10", "2011-11-10 13:49:50"}, + {"\"2011-11-11\"", "\"10:10:10.101010\"", "HOUR_MICROSECOND", "2011-11-11 10:10:10.101010", "2011-11-10 13:49:49.898990"}, + {"\"2011-11-11\"", "\"10:10\"", "MINUTE_SECOND", "2011-11-11 00:10:10", "2011-11-10 23:49:50"}, + {"\"2011-11-11\"", "\"10:10.101010\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.101010", "2011-11-10 23:49:49.898990"}, + {"\"2011-11-11\"", "\"10.101010\"", "SECOND_MICROSECOND", "2011-11-11 00:00:10.101010", "2011-11-10 23:59:49.898990"}, + {"\"2011-11-11 00:00:00\"", "1", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, + {"\"2011-11-11 00:00:00\"", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"\"2011-11-11 00:00:00\"", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"\"2011-11-11 00:00:00\"", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, + {"\"2011-11-11 00:00:00.500\"", "500000", "MICROSECOND", "2011-11-11 00:00:01", "2011-11-11 00:00:00"}, + + {"\"2011-11-11\"", "\"abc1000\"", "MICROSECOND", "2011-11-11 00:00:00", "2011-11-11 00:00:00"}, + {"\"20111111 10:10:10\"", "\"1\"", "DAY", "", ""}, + {"\"2011-11-11\"", "\"10\"", "SECOND_MICROSECOND", "2011-11-11 00:00:00.100000", "2011-11-10 23:59:59.900000"}, + {"\"2011-11-11\"", "\"10.0000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, + {"\"2011-11-11\"", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, + + {"cast(\"2011-11-11\" as datetime)", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, + {"cast(\"2011-11-11\" as datetime)", "\"1000000\"", "MICROSECOND", "2011-11-11 00:00:01.000000", "2011-11-10 23:59:59.000000"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "1", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, + + {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"1\"", "DAY", "2011-11-12 00:00:00", "2011-11-10 00:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"cast(\"2011-11-11 00:00:00\" as datetime)", "\"10\"", "SECOND", "2011-11-11 00:00:10.000000", "2011-11-10 23:59:50.000000"}, + + {"cast(\"2011-11-11\" as date)", "\"10:10:10\"", "MINUTE_MICROSECOND", "2011-11-11 00:10:10.100000", "2011-11-10 23:49:49.900000"}, + {"cast(\"2011-11-11\" as date)", "\"1000000\"", "MINUTE_MICROSECOND", "2011-11-11 00:00:01.000000", "2011-11-10 23:59:59.000000"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "1", "DAY", "2011-11-12", "2011-11-10"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "10", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "10", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "10", "SECOND", "2011-11-11 00:00:10", "2011-11-10 23:59:50"}, + + {"cast(\"2011-11-11 00:00:00\" as date)", "\"1\"", "DAY", "2011-11-12", "2011-11-10"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "HOUR", "2011-11-11 10:00:00", "2011-11-10 14:00:00"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "MINUTE", "2011-11-11 00:10:00", "2011-11-10 23:50:00"}, + {"cast(\"2011-11-11 00:00:00\" as date)", "\"10\"", "SECOND", "2011-11-11 00:00:10.000000", "2011-11-10 23:59:50.000000"}, + + // interval decimal support + {"\"2011-01-01 00:00:00\"", "10.10", "YEAR_MONTH", "2021-11-01 00:00:00", "2000-03-01 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "DAY_HOUR", "2011-01-11 10:00:00", "2010-12-21 14:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_MINUTE", "2011-01-01 10:10:00", "2010-12-31 13:50:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "DAY_MINUTE", "2011-01-01 10:10:00", "2010-12-31 13:50:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "DAY_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, + {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, + {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE_SECOND", "2011-01-01 00:10:10", "2010-12-31 23:49:50"}, + {"\"2011-01-01 00:00:00\"", "10.10", "DAY_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, + {"\"2011-01-01 00:00:00\"", "10.10", "HOUR_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, + {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, + {"\"2011-01-01 00:00:00\"", "10.10", "SECOND_MICROSECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, + {"\"2011-01-01 00:00:00\"", "10.10", "YEAR", "2021-01-01 00:00:00", "2001-01-01 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "QUARTER", "2013-07-01 00:00:00", "2008-07-01 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "MONTH", "2011-11-01 00:00:00", "2010-03-01 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "WEEK", "2011-03-12 00:00:00", "2010-10-23 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "DAY", "2011-01-11 00:00:00", "2010-12-22 00:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "HOUR", "2011-01-01 10:00:00", "2010-12-31 14:00:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "MINUTE", "2011-01-01 00:10:00", "2010-12-31 23:50:00"}, + {"\"2011-01-01 00:00:00\"", "10.10", "SECOND", "2011-01-01 00:00:10.100000", "2010-12-31 23:59:49.900000"}, + {"\"2011-01-01 00:00:00\"", "10.10", "MICROSECOND", "2011-01-01 00:00:00.000010", "2010-12-31 23:59:59.999990"}, + {"\"2011-01-01 00:00:00\"", "10.90", "MICROSECOND", "2011-01-01 00:00:00.000011", "2010-12-31 23:59:59.999989"}, + {"cast(\"2011-01-01\" as date)", "1.1", "SECOND", "2011-01-01 00:00:01.1", "2010-12-31 23:59:58.9"}, + {"cast(\"2011-01-01\" as datetime)", "1.1", "SECOND", "2011-01-01 00:00:01.1", "2010-12-31 23:59:58.9"}, + {"cast(\"2011-01-01\" as datetime(3))", "1.1", "SECOND", "2011-01-01 00:00:01.100", "2010-12-31 23:59:58.900"}, + + {"\"2009-01-01\"", "6/4", "HOUR_MINUTE", "2009-01-04 12:20:00", "2008-12-28 11:40:00"}, + {"\"2009-01-01\"", "6/0", "HOUR_MINUTE", "", ""}, + {"\"1970-01-01 12:00:00\"", "CAST(6/4 AS DECIMAL(3,1))", "HOUR_MINUTE", "1970-01-01 13:05:00", "1970-01-01 10:55:00"}, + // for issue #8077 + {"\"2012-01-02\"", "\"prefix8\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, + {"\"2012-01-02\"", "\"prefix8prefix\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, + {"\"2012-01-02\"", "\"8:00\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, + {"\"2012-01-02\"", "\"8:00:00\"", "HOUR", "2012-01-02 08:00:00", "2012-01-01 16:00:00"}, + } + for _, tc := range dateArithmeticalTests { + addDate := fmt.Sprintf("select adddate(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) + subDate := fmt.Sprintf("select subdate(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) + result = tk.MustQuery(addDate) + result.Check(testkit.Rows(tc.AddResult)) + result = tk.MustQuery(subDate) + result.Check(testkit.Rows(tc.SubResult)) + } + + // Customized check for the cases of adddate(time, ...) - it returns datetime with current date padded. + // 1. Check if the result contains space, that is, it must contain YMD part. + // 2. Check if the result's suffix matches expected, that is, the HMS part is an exact match. + checkHmsMatch := func(actual []string, expected []interface{}) bool { + return strings.Contains(actual[0], " ") && strings.HasSuffix(actual[0], expected[0].(string)) + } + + // for date_add/sub(duration, ...) + dateAddSubDurationAnyTests := []struct { + Date string + Interval string + Unit string + AddResult string + SubResult string + checkHmsOnly bool // Duration + day returns datetime with current date padded, only check HMS part for them. + }{ + {"cast('01:02:03' as time)", "'1000'", "MICROSECOND", "01:02:03.001000", "01:02:02.999000", false}, + {"cast('01:02:03' as time)", "1000", "MICROSECOND", "01:02:03.001000", "01:02:02.999000", false}, + {"cast('01:02:03' as time)", "'1'", "SECOND", "01:02:04.000000", "01:02:02.000000", false}, + {"cast('01:02:03' as time)", "1", "SECOND", "01:02:04", "01:02:02", false}, + {"cast('01:02:03' as time)", "'1.1'", "SECOND", "01:02:04.100000", "01:02:01.900000", false}, + {"cast('01:02:03' as time)", "1.1", "SECOND", "01:02:04.1", "01:02:01.9", false}, + {"cast('01:02:03' as time(3))", "1.1", "SECOND", "01:02:04.100", "01:02:01.900", false}, + {"cast('01:02:03' as time)", "cast(1.1 as decimal(10, 3))", "SECOND", "01:02:04.100", "01:02:01.900", false}, + {"cast('01:02:03' as time)", "cast('1.5' as double)", "SECOND", "01:02:04.500000", "01:02:01.500000", false}, + {"cast('01:02:03' as time)", "1", "DAY_MICROSECOND", "01:02:03.100000", "01:02:02.900000", false}, + {"cast('01:02:03' as time)", "1.1", "DAY_MICROSECOND", "01:02:04.100000", "01:02:01.900000", false}, + {"cast('01:02:03' as time)", "100", "DAY_MICROSECOND", "01:02:03.100000", "01:02:02.900000", false}, + {"cast('01:02:03' as time)", "1000000", "DAY_MICROSECOND", "01:02:04.000000", "01:02:02.000000", false}, + {"cast('01:02:03' as time)", "1", "DAY_SECOND", "01:02:04", "01:02:02", true}, + {"cast('01:02:03' as time)", "1.1", "DAY_SECOND", "01:03:04", "01:01:02", true}, + {"cast('01:02:03' as time)", "1", "DAY_MINUTE", "01:03:03", "01:01:03", true}, + {"cast('01:02:03' as time)", "1.1", "DAY_MINUTE", "02:03:03", "00:01:03", true}, + {"cast('01:02:03' as time)", "1", "DAY_HOUR", "02:02:03", "00:02:03", true}, + {"cast('01:02:03' as time)", "1.1", "DAY_HOUR", "02:02:03", "00:02:03", true}, + {"cast('01:02:03' as time)", "1", "DAY", "01:02:03", "01:02:03", true}, + {"cast('01:02:03' as time)", "1", "WEEK", "01:02:03", "01:02:03", true}, + {"cast('01:02:03' as time)", "1", "MONTH", "01:02:03", "01:02:03", true}, + {"cast('01:02:03' as time)", "1", "QUARTER", "01:02:03", "01:02:03", true}, + {"cast('01:02:03' as time)", "1", "YEAR", "01:02:03", "01:02:03", true}, + {"cast('01:02:03' as time)", "1", "YEAR_MONTH", "01:02:03", "01:02:03", true}, + } + for _, tc := range dateAddSubDurationAnyTests { + addDate := fmt.Sprintf("select date_add(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) + subDate := fmt.Sprintf("select date_sub(%s, interval %s %s);", tc.Date, tc.Interval, tc.Unit) + if tc.checkHmsOnly { + result = tk.MustQuery(addDate) + result.CheckWithFunc(testkit.Rows(tc.AddResult), checkHmsMatch) + result = tk.MustQuery(subDate) + result.CheckWithFunc(testkit.Rows(tc.SubResult), checkHmsMatch) + } else { + result = tk.MustQuery(addDate) + result.Check(testkit.Rows(tc.AddResult)) + result = tk.MustQuery(subDate) + result.Check(testkit.Rows(tc.SubResult)) + } + } + + tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast(1 as decimal))`).Check(testkit.Rows("2000-01-31 00:00:00")) + tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast(null as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select subdate(cast(null as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select subdate(cast("2000-02-01" as datetime), cast("xxx" as decimal))`).Check(testkit.Rows("2000-02-01 00:00:00")) + tk.MustQuery(`select subdate(cast("xxx" as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("1999-12-31")) + tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast("xxx" as decimal))`).Check(testkit.Rows("2000-01-01")) + tk.MustQuery(`select subdate(cast("abc" as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select subdate(cast(null as SIGNED), cast("1" as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select subdate(cast(20000101 as SIGNED), cast(null as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(1 as decimal))`).Check(testkit.Rows("2000-02-02 00:00:00")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(null as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast(null as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast("xxx" as decimal))`).Check(testkit.Rows("2000-02-01 00:00:00")) + tk.MustQuery(`select adddate(cast("xxx" as datetime), cast(1 as decimal))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("2000-02-02 00:00:00")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast(null as SIGNED))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast(null as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(cast("2000-02-01" as datetime), cast("xxx" as SIGNED))`).Check(testkit.Rows("2000-02-01 00:00:00")) + tk.MustQuery(`select adddate(cast("xxx" as datetime), cast(1 as SIGNED))`).Check(testkit.Rows("")) + tk.MustQuery(`select adddate(20100101, cast(1 as decimal))`).Check(testkit.Rows("2010-01-02")) + tk.MustQuery(`select adddate(cast('10:10:10' as time), 1)`).CheckWithFunc(testkit.Rows("10:10:10"), checkHmsMatch) + tk.MustQuery(`select adddate(cast('10:10:10' as time), cast(1 as decimal))`).CheckWithFunc(testkit.Rows("10:10:10"), checkHmsMatch) + + // for localtime, localtimestamp + result = tk.MustQuery(`select localtime() = now(), localtime = now(), localtimestamp() = now(), localtimestamp = now()`) + result.Check(testkit.Rows("1 1 1 1")) + + // for current_timestamp, current_timestamp() + result = tk.MustQuery(`select current_timestamp() = now(), current_timestamp = now()`) + result.Check(testkit.Rows("1 1")) + + // for tidb_parse_tso + tk.MustExec("SET time_zone = '+00:00';") + result = tk.MustQuery(`select tidb_parse_tso(404411537129996288)`) + result.Check(testkit.Rows("2018-11-20 09:53:04.877000")) + result = tk.MustQuery(`select tidb_parse_tso("404411537129996288")`) + result.Check(testkit.Rows("2018-11-20 09:53:04.877000")) + result = tk.MustQuery(`select tidb_parse_tso(1)`) + result.Check(testkit.Rows("1970-01-01 00:00:00.000000")) + result = tk.MustQuery(`select tidb_parse_tso(0)`) + result.Check(testkit.Rows("")) + result = tk.MustQuery(`select tidb_parse_tso(-1)`) + result.Check(testkit.Rows("")) + + // for tidb_parse_tso_logical + result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996288)`) + result.Check(testkit.Rows("0")) + result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996289)`) + result.Check(testkit.Rows("1")) + result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(404411537129996290)`) + result.Check(testkit.Rows("2")) + result = tk.MustQuery(`SELECT TIDB_PARSE_TSO_LOGICAL(-1)`) + result.Check(testkit.Rows("")) + + // for tidb_bounded_staleness + tk.MustExec("SET time_zone = '+00:00';") + tt := time.Now().UTC() + ts := oracle.GoTimeToTS(tt) + tidbBoundedStalenessTests := []struct { + sql string + injectSafeTS uint64 + expect string + }{ + { + sql: `select tidb_bounded_staleness(DATE_SUB(NOW(), INTERVAL 600 SECOND), DATE_ADD(NOW(), INTERVAL 600 SECOND))`, + injectSafeTS: ts, + expect: tt.Format(types.TimeFSPFormat[:len(types.TimeFSPFormat)-3]), + }, + { + sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 13:00:00.000")`, + injectSafeTS: func() uint64 { + tt, err := time.Parse("2006-01-02 15:04:05.000", "2021-04-27 13:30:04.877") + require.NoError(t, err) + return oracle.GoTimeToTS(tt) + }(), + expect: "2021-04-27 13:00:00.000", + }, + { + sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 13:00:00.000")`, + injectSafeTS: func() uint64 { + tt, err := time.Parse("2006-01-02 15:04:05.000", "2021-04-27 11:30:04.877") + require.NoError(t, err) + return oracle.GoTimeToTS(tt) + }(), + expect: "2021-04-27 12:00:00.000", + }, + { + sql: `select tidb_bounded_staleness("2021-04-27 12:00:00.000", "2021-04-27 11:00:00.000")`, + injectSafeTS: 0, + expect: "", + }, + // Time is too small. + { + sql: `select tidb_bounded_staleness("0020-04-27 12:00:00.000", "2021-04-27 11:00:00.000")`, + injectSafeTS: 0, + expect: "1970-01-01 00:00:00.000", + }, + // Wrong value. + { + sql: `select tidb_bounded_staleness(1, 2)`, + injectSafeTS: 0, + expect: "", + }, + { + sql: `select tidb_bounded_staleness("invalid_time_1", "invalid_time_2")`, + injectSafeTS: 0, + expect: "", + }, + } + for _, test := range tidbBoundedStalenessTests { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectSafeTS", + fmt.Sprintf("return(%v)", test.injectSafeTS))) + tk.MustQuery(test.sql).Check(testkit.Rows(test.expect)) + } + failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectSafeTS") + // test whether tidb_bounded_staleness is deterministic + result = tk.MustQuery(`select tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND)), tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND))`) + require.Len(t, result.Rows()[0], 2) + require.Equal(t, result.Rows()[0][0], result.Rows()[0][1]) + preResult := result.Rows()[0][0] + time.Sleep(time.Second) + result = tk.MustQuery(`select tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND)), tidb_bounded_staleness(NOW(), DATE_ADD(NOW(), INTERVAL 600 SECOND))`) + require.Len(t, result.Rows()[0], 2) + require.Equal(t, result.Rows()[0][0], result.Rows()[0][1]) + require.NotEqual(t, preResult, result.Rows()[0][0]) + + // fix issue 10308 + result = tk.MustQuery("select time(\"- -\");") + result.Check(testkit.Rows("00:00:00")) + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '- -'")) + result = tk.MustQuery("select time(\"---1\");") + result.Check(testkit.Rows("00:00:00")) + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '---1'")) + result = tk.MustQuery("select time(\"-- --1\");") + result.Check(testkit.Rows("00:00:00")) + tk.MustQuery("show warnings;").Check(testkit.Rows("Warning 1292 Truncated incorrect time value: '-- --1'")) + + // fix issue #15185 + result = tk.MustQuery(`select timestamp(11111.1111)`) + result.Check(testkit.Rows("2001-11-11 00:00:00.0000")) + result = tk.MustQuery(`select timestamp(cast(11111.1111 as decimal(60, 5)))`) + result.Check(testkit.Rows("2001-11-11 00:00:00.00000")) + result = tk.MustQuery(`select timestamp(1021121141105.4324)`) + result.Check(testkit.Rows("0102-11-21 14:11:05.4324")) + result = tk.MustQuery(`select timestamp(cast(1021121141105.4324 as decimal(60, 5)))`) + result.Check(testkit.Rows("0102-11-21 14:11:05.43240")) + result = tk.MustQuery(`select timestamp(21121141105.101)`) + result.Check(testkit.Rows("2002-11-21 14:11:05.101")) + result = tk.MustQuery(`select timestamp(cast(21121141105.101 as decimal(60, 5)))`) + result.Check(testkit.Rows("2002-11-21 14:11:05.10100")) + result = tk.MustQuery(`select timestamp(1121141105.799055)`) + result.Check(testkit.Rows("2000-11-21 14:11:05.799055")) + result = tk.MustQuery(`select timestamp(cast(1121141105.799055 as decimal(60, 5)))`) + result.Check(testkit.Rows("2000-11-21 14:11:05.79906")) + result = tk.MustQuery(`select timestamp(121141105.123)`) + result.Check(testkit.Rows("2000-01-21 14:11:05.123")) + result = tk.MustQuery(`select timestamp(cast(121141105.123 as decimal(60, 5)))`) + result.Check(testkit.Rows("2000-01-21 14:11:05.12300")) + result = tk.MustQuery(`select timestamp(1141105)`) + result.Check(testkit.Rows("0114-11-05 00:00:00")) + result = tk.MustQuery(`select timestamp(cast(1141105 as decimal(60, 5)))`) + result.Check(testkit.Rows("0114-11-05 00:00:00.00000")) + result = tk.MustQuery(`select timestamp(41105.11)`) + result.Check(testkit.Rows("2004-11-05 00:00:00.00")) + result = tk.MustQuery(`select timestamp(cast(41105.11 as decimal(60, 5)))`) + result.Check(testkit.Rows("2004-11-05 00:00:00.00000")) + result = tk.MustQuery(`select timestamp(1105.3)`) + result.Check(testkit.Rows("2000-11-05 00:00:00.0")) + result = tk.MustQuery(`select timestamp(cast(1105.3 as decimal(60, 5)))`) + result.Check(testkit.Rows("2000-11-05 00:00:00.00000")) + result = tk.MustQuery(`select timestamp(105)`) + result.Check(testkit.Rows("2000-01-05 00:00:00")) + result = tk.MustQuery(`select timestamp(cast(105 as decimal(60, 5)))`) + result.Check(testkit.Rows("2000-01-05 00:00:00.00000")) +} + +func TestSetVariables(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + _, err := tk.Exec("set sql_mode='adfasdfadsfdasd';") + require.Error(t, err) + _, err = tk.Exec("set @@sql_mode='adfasdfadsfdasd';") + require.Error(t, err) + _, err = tk.Exec("set @@global.sql_mode='adfasdfadsfdasd';") + require.Error(t, err) + _, err = tk.Exec("set @@session.sql_mode='adfasdfadsfdasd';") + require.Error(t, err) + + var r *testkit.Result + _, err = tk.Exec("set @@session.sql_mode=',NO_ZERO_DATE,ANSI,ANSI_QUOTES';") + require.NoError(t, err) + r = tk.MustQuery(`select @@session.sql_mode`) + r.Check(testkit.Rows("NO_ZERO_DATE,REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI")) + r = tk.MustQuery(`show variables like 'sql_mode'`) + r.Check(testkit.Rows("sql_mode NO_ZERO_DATE,REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ONLY_FULL_GROUP_BY,ANSI")) + + // for invalid SQL mode. + tk.MustExec("use test") + tk.MustExec("drop table if exists tab0") + tk.MustExec("CREATE TABLE tab0(col1 time)") + _, err = tk.Exec("set sql_mode='STRICT_TRANS_TABLES';") + require.NoError(t, err) + _, err = tk.Exec("INSERT INTO tab0 select cast('999:44:33' as time);") + require.Error(t, err) + require.Error(t, err, "[types:1292]Truncated incorrect time value: '999:44:33'") + _, err = tk.Exec("set sql_mode=' ,';") + require.Error(t, err) + _, err = tk.Exec("INSERT INTO tab0 select cast('999:44:33' as time);") + require.Error(t, err) + require.Error(t, err, "[types:1292]Truncated incorrect time value: '999:44:33'") + + // issue #5478 + _, err = tk.Exec("set session transaction read write;") + require.NoError(t, err) + _, err = tk.Exec("set global transaction read write;") + require.NoError(t, err) + r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) + r.Check(testkit.Rows("0 0 0 0")) + + _, err = tk.Exec("set session transaction read only;") + require.Error(t, err) + + _, err = tk.Exec("start transaction read only;") + require.Error(t, err) + + _, err = tk.Exec("set tidb_enable_noop_functions=1") + require.NoError(t, err) + + tk.MustExec("set session transaction read only;") + tk.MustExec("start transaction read only;") + + r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) + r.Check(testkit.Rows("1 0 1 0")) + _, err = tk.Exec("set global transaction read only;") + require.Error(t, err) + tk.MustExec("set global tidb_enable_noop_functions=1;") + tk.MustExec("set global transaction read only;") + r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) + r.Check(testkit.Rows("1 1 1 1")) + + _, err = tk.Exec("set session transaction read write;") + require.NoError(t, err) + _, err = tk.Exec("set global transaction read write;") + require.NoError(t, err) + r = tk.MustQuery(`select @@session.tx_read_only, @@global.tx_read_only, @@session.transaction_read_only, @@global.transaction_read_only;`) + r.Check(testkit.Rows("0 0 0 0")) + + // reset + tk.MustExec("set tidb_enable_noop_functions=0") + tk.MustExec("set global tidb_enable_noop_functions=1") + + _, err = tk.Exec("set @@global.max_user_connections='';") + require.Error(t, err) + require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_user_connections").Error()) + _, err = tk.Exec("set @@global.max_prepared_stmt_count='';") + require.Error(t, err) + require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_prepared_stmt_count").Error()) + + // Previously global values were cached. This is incorrect. + // See: https://github.com/pingcap/tidb/issues/24368 + tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 0")) + tk.MustExec("SET GLOBAL max_connections=1234") + tk.MustQuery("SHOW VARIABLES LIKE 'max_connections'").Check(testkit.Rows("max_connections 1234")) + // restore + tk.MustExec("SET GLOBAL max_connections=0") +} + +func TestPreparePlanCacheOnCachedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set tidb_enable_prepared_plan_cache=ON") + tk.Session() + + var err error + se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ + PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), + }) + require.NoError(t, err) + tk.SetSession(se) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + tk.MustExec("alter table t cache") + + var readFromTableCache bool + for i := 0; i < 50; i++ { + tk.MustQuery("select * from t where a = 1") + if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { + readFromTableCache = true + break + } + time.Sleep(50 * time.Millisecond) + } + require.True(t, readFromTableCache) + // already read cache after reading first time + tk.MustExec("prepare stmt from 'select * from t where a = ?';") + tk.MustExec("set @a = 1;") + tk.MustExec("execute stmt using @a;") + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("0")) + tk.MustExec("execute stmt using @a;") + readFromTableCache = tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache + require.True(t, readFromTableCache) + tk.MustQuery("select @@last_plan_from_cache;").Check(testkit.Rows("1")) +} + +func TestIssue16205(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set tidb_enable_prepared_plan_cache=ON") + tk.MustExec("use test") + tk.MustExec("prepare stmt from 'select random_bytes(3)'") + rows1 := tk.MustQuery("execute stmt").Rows() + require.Len(t, rows1, 1) + rows2 := tk.MustQuery("execute stmt").Rows() + require.Len(t, rows2, 1) + require.NotEqual(t, rows1[0][0].(string), rows2[0][0].(string)) +} + +// issues 14448, 19383, 17734 +func TestNoopFunctions(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // variable changes in the test will not affect the plan cache + tk.MustExec("DROP TABLE IF EXISTS t1") + tk.MustExec("CREATE TABLE t1 (a INT NOT NULL PRIMARY KEY)") + tk.MustExec("INSERT INTO t1 VALUES (1),(2),(3)") + + message := `.* has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions` + stmts := []string{ + "SELECT SQL_CALC_FOUND_ROWS * FROM t1 LIMIT 1", + "SELECT * FROM t1 LOCK IN SHARE MODE", + "SELECT * FROM t1 GROUP BY a DESC", + "SELECT * FROM t1 GROUP BY a ASC", + } + + for _, stmt := range stmts { + // test on + tk.MustExec("SET tidb_enable_noop_functions='ON'") + tk.MustExec(stmt) + // test warning + tk.MustExec("SET tidb_enable_noop_functions='WARN'") + tk.MustExec(stmt) + warn := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Regexp(t, message, warn[0].Err.Error()) + // test off + tk.MustExec("SET tidb_enable_noop_functions='OFF'") + _, err := tk.Exec(stmt) + require.Regexp(t, message, err.Error()) + } + + // These statements return a different error message + // to the above. Test for error, not specifically the message. + // After they execute, we need to reset the values because + // otherwise tidb_enable_noop_functions can't be changed. + + stmts = []string{ + "START TRANSACTION READ ONLY", + "SET TRANSACTION READ ONLY", + "SET tx_read_only = 1", + "SET transaction_read_only = 1", + } + + for _, stmt := range stmts { + // test off + tk.MustExec("SET tidb_enable_noop_functions='OFF'") + _, err := tk.Exec(stmt) + require.Error(t, err) + // test warning + tk.MustExec("SET tidb_enable_noop_functions='WARN'") + tk.MustExec(stmt) + warn := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Len(t, warn, 1) + // test on + tk.MustExec("SET tidb_enable_noop_functions='ON'") + tk.MustExec(stmt) + + // Reset (required for future loop iterations and future tests) + tk.MustExec("SET tx_read_only = 0") + tk.MustExec("SET transaction_read_only = 0") + } +} + +func TestCrossDCQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + tk.MustExec("create placement policy p1 leader_constraints='[+zone=sh]'") + tk.MustExec("create placement policy p2 leader_constraints='[+zone=bj]'") + tk.MustExec(`create table t1 (c int primary key, d int,e int,index idx_d(d),index idx_e(e)) +PARTITION BY RANGE (c) ( + PARTITION p0 VALUES LESS THAN (6) placement policy p1, + PARTITION p1 VALUES LESS THAN (11) placement policy p2 +);`) + defer func() { + tk.MustExec("drop table if exists t1") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + }() + + tk.MustExec(`insert into t1 (c,d,e) values (1,1,1);`) + tk.MustExec(`insert into t1 (c,d,e) values (2,3,5);`) + tk.MustExec(`insert into t1 (c,d,e) values (3,5,7);`) + + testcases := []struct { + name string + txnScope string + zone string + sql string + expectErr error + }{ + // FIXME: block by https://github.com/pingcap/tidb/issues/21872 + //{ + // name: "cross dc read to sh by holding bj, IndexReader", + // txnScope: "bj", + // sql: "select /*+ USE_INDEX(t1, idx_d) */ d from t1 where c < 5 and d < 1;", + // expectErr: fmt.Errorf(".*can not be read by.*"), + //}, + // FIXME: block by https://github.com/pingcap/tidb/issues/21847 + //{ + // name: "cross dc read to sh by holding bj, BatchPointGet", + // txnScope: "bj", + // sql: "select * from t1 where c in (1,2,3,4);", + // expectErr: fmt.Errorf(".*can not be read by.*"), + //}, + { + name: "cross dc read to sh by holding bj, PointGet", + txnScope: "local", + zone: "bj", + sql: "select * from t1 where c = 1", + expectErr: fmt.Errorf(".*can not be read by.*"), + }, + { + name: "cross dc read to sh by holding bj, IndexLookUp", + txnScope: "local", + zone: "bj", + sql: "select * from t1 use index (idx_d) where c < 5 and d < 5;", + expectErr: fmt.Errorf(".*can not be read by.*"), + }, + { + name: "cross dc read to sh by holding bj, IndexMerge", + txnScope: "local", + zone: "bj", + sql: "select /*+ USE_INDEX_MERGE(t1, idx_d, idx_e) */ * from t1 where c <5 and (d =5 or e=5);", + expectErr: fmt.Errorf(".*can not be read by.*"), + }, + { + name: "cross dc read to sh by holding bj, TableReader", + txnScope: "local", + zone: "bj", + sql: "select * from t1 where c < 6", + expectErr: fmt.Errorf(".*can not be read by.*"), + }, + { + name: "cross dc read to global by holding bj", + txnScope: "local", + zone: "bj", + sql: "select * from t1", + expectErr: fmt.Errorf(".*can not be read by.*"), + }, + { + name: "read sh dc by holding sh", + txnScope: "local", + zone: "sh", + sql: "select * from t1 where c < 6", + expectErr: nil, + }, + { + name: "read sh dc by holding global", + txnScope: "global", + zone: "", + sql: "select * from t1 where c < 6", + expectErr: nil, + }, + } + tk.MustExec("set global tidb_enable_local_txn = on;") + for _, testcase := range testcases { + t.Log(testcase.name) + require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", + fmt.Sprintf(`return("%v")`, testcase.zone))) + tk.MustExec(fmt.Sprintf("set @@txn_scope='%v'", testcase.txnScope)) + tk.Exec("begin") + res, err := tk.Exec(testcase.sql) + _, resErr := session.GetRows4Test(context.Background(), tk.Session(), res) + var checkErr error + if err != nil { + checkErr = err + } else { + checkErr = resErr + } + if testcase.expectErr != nil { + require.Error(t, checkErr) + require.Regexp(t, ".*can not be read by.*", checkErr.Error()) + } else { + require.NoError(t, checkErr) + } + if res != nil { + res.Close() + } + tk.Exec("commit") + } + require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) + tk.MustExec("set global tidb_enable_local_txn = off;") +} + +func TestTiDBRowChecksumBuiltin(t *testing.T) { + store := testkit.CreateMockStore(t) + + checksum := func(cols ...interface{}) uint32 { + buf := make([]byte, 0, 64) + for _, col := range cols { + switch x := col.(type) { + case int: + buf = binary.LittleEndian.AppendUint64(buf, uint64(x)) + case string: + buf = binary.LittleEndian.AppendUint32(buf, uint32(len(x))) + buf = append(buf, []byte(x)...) + } + } + return crc32.ChecksumIEEE(buf) + } + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set global tidb_enable_row_level_checksum = 1") + tk.MustExec("use test") + tk.MustExec("create table t (id int primary key, c int)") + + // row with 2 checksums + tk.MustExec("insert into t values (1, 10)") + tk.MustExec("alter table t change column c c varchar(10)") + checksum1 := fmt.Sprintf("%d,%d", checksum(1, 10), checksum(1, "10")) + // row with 1 checksum + tk.Session().GetSessionVars().EnableRowLevelChecksum = true + tk.MustExec("insert into t values (2, '20')") + checksum2 := fmt.Sprintf("%d", checksum(2, "20")) + // row without checksum + tk.Session().GetSessionVars().EnableRowLevelChecksum = false + tk.MustExec("insert into t values (3, '30')") + checksum3 := "" + + // fast point-get + tk.MustQuery("select tidb_row_checksum() from t where id = 1").Check(testkit.Rows(checksum1)) + tk.MustQuery("select tidb_row_checksum() from t where id = 2").Check(testkit.Rows(checksum2)) + tk.MustQuery("select tidb_row_checksum() from t where id = 3").Check(testkit.Rows(checksum3)) + // fast batch-point-get + tk.MustQuery("select tidb_row_checksum() from t where id in (1, 2, 3)").Check(testkit.Rows(checksum1, checksum2, checksum3)) + + // non-fast point-get + tk.MustGetDBError("select length(tidb_row_checksum()) from t where id = 1", expression.ErrNotSupportedYet) + tk.MustGetDBError("select c from t where id = 1 and tidb_row_checksum() is not null", expression.ErrNotSupportedYet) + // non-fast batch-point-get + tk.MustGetDBError("select length(tidb_row_checksum()) from t where id in (1, 2, 3)", expression.ErrNotSupportedYet) + tk.MustGetDBError("select c from t where id in (1, 2, 3) and tidb_row_checksum() is not null", expression.ErrNotSupportedYet) + + // other plans + tk.MustGetDBError("select tidb_row_checksum() from t", expression.ErrNotSupportedYet) + tk.MustGetDBError("select tidb_row_checksum() from t where id > 0", expression.ErrNotSupportedYet) +} diff --git a/pkg/expression/integration_test/main_test.go b/pkg/expression/integration_test/main_test.go new file mode 100644 index 0000000000000..3f9ebf8d7b0df --- /dev/null +++ b/pkg/expression/integration_test/main_test.go @@ -0,0 +1,56 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package integration_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + testmain.ShortCircuitForBench(m) + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + // Some test depends on the values of timeutil.SystemLocation() + // If we don't SetSystemTZ() here, the value would change unpredictable. + // Affected by the order whether a testsuite runs before or after integration test. + // Note, SetSystemTZ() is a sync.Once operation. + timeutil.SetSystemTZ("system") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/expression/main_test.go b/pkg/expression/main_test.go new file mode 100644 index 0000000000000..274600f443ef3 --- /dev/null +++ b/pkg/expression/main_test.go @@ -0,0 +1,66 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + testmain.ShortCircuitForBench(m) + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + // Some test depends on the values of timeutil.SystemLocation() + // If we don't SetSystemTZ() here, the value would change unpredictable. + // Affected by the order whether a testsuite runs before or after integration test. + // Note, SetSystemTZ() is a sync.Once operation. + timeutil.SetSystemTZ("system") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} + +func createContext(t *testing.T) *mock.Context { + ctx := mock.NewContext() + ctx.GetSessionVars().StmtCtx.SetTimeZone(time.Local) + sc := ctx.GetSessionVars().StmtCtx + sc.TruncateAsWarning = true + require.NoError(t, ctx.GetSessionVars().SetSystemVar("max_allowed_packet", "67108864")) + ctx.GetSessionVars().PlanColumnID.Store(0) + return ctx +} diff --git a/expression/scalar_function.go b/pkg/expression/scalar_function.go similarity index 98% rename from expression/scalar_function.go rename to pkg/expression/scalar_function.go index 48e93f97fda22..a349670be4e65 100644 --- a/expression/scalar_function.go +++ b/pkg/expression/scalar_function.go @@ -21,17 +21,17 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" ) // ScalarFunction is the function that returns a value. diff --git a/expression/scalar_function_test.go b/pkg/expression/scalar_function_test.go similarity index 94% rename from expression/scalar_function_test.go rename to pkg/expression/scalar_function_test.go index cc8cca60ec95f..6e2b874f819d9 100644 --- a/expression/scalar_function_test.go +++ b/pkg/expression/scalar_function_test.go @@ -18,12 +18,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/expression/schema.go b/pkg/expression/schema.go new file mode 100644 index 0000000000000..01c738d1cee77 --- /dev/null +++ b/pkg/expression/schema.go @@ -0,0 +1,294 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "strings" + "unsafe" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/size" +) + +// KeyInfo stores the columns of one unique key or primary key. +type KeyInfo []*Column + +// Clone copies the entire UniqueKey. +func (ki KeyInfo) Clone() KeyInfo { + result := make([]*Column, 0, len(ki)) + for _, col := range ki { + result = append(result, col.Clone().(*Column)) + } + return result +} + +// String implements fmt.Stringer interface. +func (ki KeyInfo) String() string { + ukColStrs := make([]string, 0, len(ki)) + for _, col := range ki { + ukColStrs = append(ukColStrs, col.String()) + } + return "[" + strings.Join(ukColStrs, ",") + "]" +} + +// Schema stands for the row schema and unique key information get from input. +type Schema struct { + Columns []*Column + Keys []KeyInfo + // UniqueKeys stores those unique indexes that allow null values, but Keys does not allow null values. + // since equivalence conditions can filter out null values, in this case a unique index with null values can be a Key. + UniqueKeys []KeyInfo +} + +// String implements fmt.Stringer interface. +func (s *Schema) String() string { + colStrs := make([]string, 0, len(s.Columns)) + for _, col := range s.Columns { + colStrs = append(colStrs, col.String()) + } + ukStrs := make([]string, 0, len(s.Keys)) + for _, key := range s.Keys { + ukStrs = append(ukStrs, key.String()) + } + return "Column: [" + strings.Join(colStrs, ",") + "] Unique key: [" + strings.Join(ukStrs, ",") + "]" +} + +// Clone copies the total schema. +func (s *Schema) Clone() *Schema { + cols := make([]*Column, 0, s.Len()) + keys := make([]KeyInfo, 0, len(s.Keys)) + for _, col := range s.Columns { + cols = append(cols, col.Clone().(*Column)) + } + for _, key := range s.Keys { + keys = append(keys, key.Clone()) + } + schema := NewSchema(cols...) + schema.SetUniqueKeys(keys) + return schema +} + +// ExprFromSchema checks if all columns of this expression are from the same schema. +func ExprFromSchema(expr Expression, schema *Schema) bool { + switch v := expr.(type) { + case *Column: + return schema.Contains(v) + case *ScalarFunction: + for _, arg := range v.GetArgs() { + if !ExprFromSchema(arg, schema) { + return false + } + } + return true + case *CorrelatedColumn, *Constant: + return true + } + return false +} + +// RetrieveColumn retrieves column in expression from the columns in schema. +func (s *Schema) RetrieveColumn(col *Column) *Column { + index := s.ColumnIndex(col) + if index != -1 { + return s.Columns[index] + } + return nil +} + +// IsUniqueKey checks if this column is a unique key. +func (s *Schema) IsUniqueKey(col *Column) bool { + for _, key := range s.Keys { + if len(key) == 1 && key[0].Equal(nil, col) { + return true + } + } + return false +} + +// IsUnique checks if this column is a unique key which may contain duplicate nulls . +func (s *Schema) IsUnique(col *Column) bool { + for _, key := range s.UniqueKeys { + if len(key) == 1 && key[0].Equal(nil, col) { + return true + } + } + return false +} + +// ColumnIndex finds the index for a column. +func (s *Schema) ColumnIndex(col *Column) int { + backupIdx := -1 + for i, c := range s.Columns { + if c.UniqueID == col.UniqueID { + backupIdx = i + if c.IsPrefix { + // instead of returning a prefix column + // prefer to find a full column + // only clustered index table can meet this: + // same column `c1` maybe appear in both primary key and secondary index + // so secondary index itself can have two `c1` column one for indexKey and one for handle + continue + } + return i + } + } + return backupIdx +} + +// Contains checks if the schema contains the column. +func (s *Schema) Contains(col *Column) bool { + return s.ColumnIndex(col) != -1 +} + +// Len returns the number of columns in schema. +func (s *Schema) Len() int { + return len(s.Columns) +} + +// Append append new column to the columns stored in schema. +func (s *Schema) Append(col ...*Column) { + s.Columns = append(s.Columns, col...) +} + +// SetUniqueKeys will set the value of Schema.Keys. +func (s *Schema) SetUniqueKeys(keys []KeyInfo) { + s.Keys = keys +} + +// ColumnsIndices will return a slice which contains the position of each column in schema. +// If there is one column that doesn't match, nil will be returned. +func (s *Schema) ColumnsIndices(cols []*Column) (ret []int) { + ret = make([]int, 0, len(cols)) + for _, col := range cols { + pos := s.ColumnIndex(col) + if pos == -1 { + return nil + } + ret = append(ret, pos) + } + return +} + +// ColumnsByIndices returns columns by multiple offsets. +// Callers should guarantee that all the offsets provided should be valid, which means offset should: +// 1. not smaller than 0, and +// 2. not exceed len(s.Columns) +func (s *Schema) ColumnsByIndices(offsets []int) []*Column { + cols := make([]*Column, 0, len(offsets)) + for _, offset := range offsets { + cols = append(cols, s.Columns[offset]) + } + return cols +} + +// ExtractColGroups checks if column groups are from current schema, and returns +// offsets of those satisfied column groups. +func (s *Schema) ExtractColGroups(colGroups [][]*Column) ([][]int, []int) { + if len(colGroups) == 0 { + return nil, nil + } + extracted := make([][]int, 0, len(colGroups)) + offsets := make([]int, 0, len(colGroups)) + for i, g := range colGroups { + if j := s.ColumnsIndices(g); j != nil { + extracted = append(extracted, j) + offsets = append(offsets, i) + } + } + return extracted, offsets +} + +const emptySchemaSize = int64(unsafe.Sizeof(Schema{})) + +// MemoryUsage return the memory usage of Schema +func (s *Schema) MemoryUsage() (sum int64) { + if s == nil { + return + } + + sum = emptySchemaSize + int64(cap(s.Columns))*size.SizeOfPointer + int64(cap(s.Keys)+cap(s.UniqueKeys))*size.SizeOfSlice + + for _, col := range s.Columns { + sum += col.MemoryUsage() + } + for _, cols := range s.Keys { + sum += int64(cap(cols)) * size.SizeOfPointer + for _, col := range cols { + sum += col.MemoryUsage() + } + } + for _, cols := range s.UniqueKeys { + sum += int64(cap(cols)) * size.SizeOfPointer + for _, col := range cols { + sum += col.MemoryUsage() + } + } + return +} + +// GetExtraHandleColumn gets the extra handle column. +func (s *Schema) GetExtraHandleColumn() *Column { + columnLen := len(s.Columns) + if columnLen > 0 && s.Columns[columnLen-1].ID == model.ExtraHandleID { + return s.Columns[columnLen-1] + } else if columnLen > 1 && s.Columns[columnLen-2].ID == model.ExtraHandleID { + return s.Columns[columnLen-2] + } + return nil +} + +// MergeSchema will merge two schema into one schema. We shouldn't need to consider unique keys. +// That will be processed in build_key_info.go. +func MergeSchema(lSchema, rSchema *Schema) *Schema { + if lSchema == nil && rSchema == nil { + return nil + } + if lSchema == nil { + return rSchema.Clone() + } + if rSchema == nil { + return lSchema.Clone() + } + tmpL := lSchema.Clone() + tmpR := rSchema.Clone() + ret := NewSchema(append(tmpL.Columns, tmpR.Columns...)...) + return ret +} + +// GetUsedList shows whether each column in schema is contained in usedCols. +func GetUsedList(usedCols []*Column, schema *Schema) []bool { + tmpSchema := NewSchema(usedCols...) + used := make([]bool, schema.Len()) + for i, col := range schema.Columns { + if !used[i] { + used[i] = tmpSchema.Contains(col) + + // When cols are a generated expression col, compare them in terms of virtual expr. + if expr, ok := col.VirtualExpr.(*ScalarFunction); ok && used[i] { + for j, colToCompare := range schema.Columns { + if !used[j] && j != i && (expr).Equal(nil, colToCompare.VirtualExpr) && col.RetType.Equal(colToCompare.RetType) { + used[j] = true + } + } + } + } + } + return used +} + +// NewSchema returns a schema made by its parameter. +func NewSchema(cols ...*Column) *Schema { + return &Schema{Columns: cols} +} diff --git a/expression/schema_test.go b/pkg/expression/schema_test.go similarity index 100% rename from expression/schema_test.go rename to pkg/expression/schema_test.go diff --git a/expression/simple_rewriter.go b/pkg/expression/simple_rewriter.go similarity index 95% rename from expression/simple_rewriter.go rename to pkg/expression/simple_rewriter.go index c8c586d5891be..f19c0f021278d 100644 --- a/expression/simple_rewriter.go +++ b/pkg/expression/simple_rewriter.go @@ -18,12 +18,12 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" ) // ParseSimpleExprWithTableInfo parses simple expression string to Expression. diff --git a/pkg/expression/test/multivaluedindex/BUILD.bazel b/pkg/expression/test/multivaluedindex/BUILD.bazel new file mode 100644 index 0000000000000..1c6ed8490d878 --- /dev/null +++ b/pkg/expression/test/multivaluedindex/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "multivaluedindex_test", + timeout = "short", + srcs = [ + "main_test.go", + "multi_valued_index_test.go", + ], + flaky = True, + shard_count = 6, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessiontxn", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/timeutil", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/expression/test/multivaluedindex/main_test.go b/pkg/expression/test/multivaluedindex/main_test.go new file mode 100644 index 0000000000000..8ba2902142c88 --- /dev/null +++ b/pkg/expression/test/multivaluedindex/main_test.go @@ -0,0 +1,56 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package multivaluedindex + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + testmain.ShortCircuitForBench(m) + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Experimental.AllowsExpressionIndex = true + }) + tikv.EnableFailpoints() + + // Some test depends on the values of timeutil.SystemLocation() + // If we don't SetSystemTZ() here, the value would change unpredictable. + // Affected by the order whether a testsuite runs before or after integration test. + // Note, SetSystemTZ() is a sync.Once operation. + timeutil.SetSystemTZ("system") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/expression/test/multivaluedindex/multi_valued_index_test.go b/pkg/expression/test/multivaluedindex/multi_valued_index_test.go new file mode 100644 index 0000000000000..b08a469d2b662 --- /dev/null +++ b/pkg/expression/test/multivaluedindex/multi_valued_index_test.go @@ -0,0 +1,523 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package multivaluedindex + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/stretchr/testify/require" +) + +func TestMultiValuedIndexDDL(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + + tk.MustExec("create table t(a json);") + tk.MustGetErrCode("select cast(a as signed array) from t", errno.ErrNotSupportedYet) + tk.MustGetErrCode("select json_extract(cast(a as signed array), '$[0]') from t", errno.ErrNotSupportedYet) + tk.MustGetErrCode("select * from t where cast(a as signed array)", errno.ErrNotSupportedYet) + tk.MustGetErrCode("select cast('[1,2,3]' as unsigned array);", errno.ErrNotSupportedYet) + + tk.MustExec("drop table t") + tk.MustGetErrCode("CREATE TABLE t(x INT, KEY k ((1 AND CAST(JSON_ARRAY(x) AS UNSIGNED ARRAY))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(cast(f1 as unsigned array) as unsigned array))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, primary key mvi((cast(cast(f1 as unsigned array) as unsigned array))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->>'$[*]' as unsigned array))));", errno.ErrInvalidTypeForJSON) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as year array))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as json array))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("CREATE TABLE t1 (f1 json, key mvi((cast(f1->'$[*]' as char(10) charset gbk array))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("create table t(j json, gc json as ((concat(cast(j->'$[*]' as unsigned array),\"x\"))));", errno.ErrNotSupportedYet) + tk.MustGetErrCode("create table t(j json, gc json as (cast(j->'$[*]' as unsigned array)));", errno.ErrNotSupportedYet) + tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as char array))));`, errno.ErrNotSupportedYet) + tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as binary array))));`, errno.ErrNotSupportedYet) + tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as float array))));`, errno.ErrNotSupportedYet) + tk.MustGetErrCode(`create table t1(j json, key i1((cast(j->"$" as decimal(4,2) array))));`, errno.ErrNotSupportedYet) + tk.MustGetErrCode("create view v as select cast('[1,2,3]' as unsigned array);", errno.ErrNotSupportedYet) + tk.MustExec("create table t(a json, index idx((cast(a as signed array))));") + tk.MustExec("drop table t;") + tk.MustExec("create table t(a json, index idx(((cast(a as signed array)))))") + tk.MustExec("drop table t;") + tk.MustExec(`create table t(j json, key i1((cast(j->"$" as double array))));`) + + tk.MustExec("drop table t") + tk.MustGetErrCode("create table t(a json, b int, index idx(b, (cast(a as signed array)), (cast(a as signed array))));", errno.ErrNotSupportedYet) + tk.MustExec("create table t(a json, b int);") + tk.MustGetErrCode("create index idx on t (b, (cast(a as signed array)), (cast(a as signed array)))", errno.ErrNotSupportedYet) + tk.MustGetErrCode("alter table t add index idx(b, (cast(a as signed array)), (cast(a as signed array)))", errno.ErrNotSupportedYet) + tk.MustExec("create index idx1 on t (b, (cast(a as signed array)))") + tk.MustExec("alter table t add index idx2(b, (cast(a as signed array)))") + + tk.MustExec("drop table t") + tk.MustExec("create table t(a json, b int, index idx3(b, (cast(a as signed array))));") + tk.MustExec("drop table t") + tk.MustExec("set names gbk") + tk.MustExec("create table t(a json, b int, index idx3(b, (cast(a as char(10) array))));") + + tk.MustExec("CREATE TABLE users (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, doc JSON);") + tk.MustExecToErr("CREATE TABLE t (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, doc JSON, FOREIGN KEY fk_user_id ((cast(doc->'$[*]' as signed array))) REFERENCES users(id));") +} + +func TestMultiValuedIndexDML(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE test;") + + mode := []string{`''`, `default`} + + for _, m := range mode { + tk.MustExec(fmt.Sprintf("set @@sql_mode=%s", m)) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as unsigned array))));`) + tk.MustExec(`insert into t values ('[1,2,3]');`) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrDataOutOfRangeFunctionalIndex) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as signed array))));`) + tk.MustExec(`insert into t values ('[1,2,3]');`) + tk.MustExec(`insert into t values ('[-1]');`) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as char(1) array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values ('["1"]');`) + tk.MustExec(`insert into t values ('["a"]');`) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as char(2) array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values ('["1"]');`) + tk.MustExec(`insert into t values ('["a"]');`) + tk.MustExec(`insert into t values ('["汉字"]');`) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as binary(1) array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values ('["1"]');`) + tk.MustExec(`insert into t values ('["a"]');`) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as binary(2) array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values ('["1"]');`) + tk.MustExec(`insert into t values ('["a"]');`) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrFunctionalIndexDataIsTooLong) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as date array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values (json_array(cast("2022-02-02" as date)));`) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as time array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values (json_array(cast("11:00:00" as time)));`) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as datetime array))));`) + tk.MustGetErrCode(`insert into t values ('[1,2,3]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[-1]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.2]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('[1.0]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + + tk.MustExec(`drop table if exists t;`) + tk.MustExec(`create table t(a json, index idx((cast(a as double array))));`) + tk.MustExec(`insert into t values ('[1,2,3]');`) + tk.MustExec(`insert into t values ('[-1]');`) + tk.MustGetErrCode(`insert into t values ('["1"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["a"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values ('["汉字"]');`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustExec(`insert into t values ('[1.2]');`) + tk.MustExec(`insert into t values ('[1.0]');`) + tk.MustGetErrCode(`insert into t values (json_array(cast("11:00:00" as time)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02" as date)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast("2022-02-02 11:00:00" as datetime)));`, errno.ErrInvalidJSONValueForFuncIndex) + tk.MustGetErrCode(`insert into t values (json_array(cast('{"a":1}' as json)));`, errno.ErrInvalidJSONValueForFuncIndex) + } +} + +func TestWriteMultiValuedIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as signed array))))") + tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") + tk.MustExec("insert into t1 values (2, '[1,2,3]')") + tk.MustExec("insert into t1 values (3, '[]')") + tk.MustExec("insert into t1 values (4, '[2,3,4]')") + tk.MustExec("insert into t1 values (5, null)") + tk.MustExec("insert into t1 values (6, '1')") + + t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 11) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewDatum(nil), types.NewIntDatum(5)}, + {types.NewIntDatum(1), types.NewIntDatum(1)}, + {types.NewIntDatum(1), types.NewIntDatum(2)}, + {types.NewIntDatum(1), types.NewIntDatum(6)}, + {types.NewIntDatum(2), types.NewIntDatum(1)}, + {types.NewIntDatum(2), types.NewIntDatum(2)}, + {types.NewIntDatum(2), types.NewIntDatum(4)}, + {types.NewIntDatum(3), types.NewIntDatum(1)}, + {types.NewIntDatum(3), types.NewIntDatum(2)}, + {types.NewIntDatum(3), types.NewIntDatum(4)}, + {types.NewIntDatum(4), types.NewIntDatum(4)}, + }) + } + } + tk.MustExec("delete from t1") + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 0) + } + } + + tk.MustExec("drop table t1") + tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as char(5) array))))") + tk.MustExec("insert into t1 values (1, '[\"abc\", \"abc \"]')") + tk.MustExec("insert into t1 values (2, '[\"b\"]')") + tk.MustExec("insert into t1 values (3, '[\"b \"]')") + tk.MustQuery("select pk from t1 where 'b ' member of (a)").Check(testkit.Rows("3")) + + t1, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 4) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(1)}, + {types.NewBytesDatum([]byte("abc ")), types.NewIntDatum(1)}, + {types.NewBytesDatum([]byte("b")), types.NewIntDatum(2)}, + {types.NewBytesDatum([]byte("b ")), types.NewIntDatum(3)}, + }) + } + } + + tk.MustExec("update t1 set a = json_array_append(a, '$', 'bcd') where pk = 1") + tk.MustExec("update t1 set a = '[]' where pk = 2") + tk.MustExec("update t1 set a = '[\"abc\"]' where pk = 3") + + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 4) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(1)}, + {types.NewBytesDatum([]byte("abc")), types.NewIntDatum(3)}, + {types.NewBytesDatum([]byte("abc ")), types.NewIntDatum(1)}, + {types.NewBytesDatum([]byte("bcd")), types.NewIntDatum(1)}, + }) + } + } + + tk.MustExec("delete from t1") + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 0) + } + } + + tk.MustExec("drop table t1") + tk.MustExec("create table t1(pk int primary key, a json, index idx((cast(a as unsigned array))))") + tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") + tk.MustExec("insert into t1 values (2, '[1,2,3]')") + tk.MustExec("insert into t1 values (3, '[]')") + tk.MustExec("insert into t1 values (4, '[2,3,4]')") + tk.MustExec("insert into t1 values (5, null)") + tk.MustExec("insert into t1 values (6, '1')") + + t1, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 11) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewDatum(nil), types.NewIntDatum(5)}, + {types.NewUintDatum(1), types.NewIntDatum(1)}, + {types.NewUintDatum(1), types.NewIntDatum(2)}, + {types.NewUintDatum(1), types.NewIntDatum(6)}, + {types.NewUintDatum(2), types.NewIntDatum(1)}, + {types.NewUintDatum(2), types.NewIntDatum(2)}, + {types.NewUintDatum(2), types.NewIntDatum(4)}, + {types.NewUintDatum(3), types.NewIntDatum(1)}, + {types.NewUintDatum(3), types.NewIntDatum(2)}, + {types.NewUintDatum(3), types.NewIntDatum(4)}, + {types.NewUintDatum(4), types.NewIntDatum(4)}, + }) + } + } + tk.MustExec("delete from t1") + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 0) + } + } +} + +func TestWriteMultiValuedIndexPartitionTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t1 +( + pk int primary key, + a json, + index idx ((cast(a as signed array))) +) partition by range columns (pk) (partition p0 values less than (10), partition p1 values less than (20));`) + tk.MustExec("insert into t1 values (1, '[1,2,2,3]')") + tk.MustExec("insert into t1 values (11, '[1,2,3]')") + tk.MustExec("insert into t1 values (2, '[]')") + tk.MustExec("insert into t1 values (12, '[2,3,4]')") + tk.MustExec("insert into t1 values (3, null)") + tk.MustExec("insert into t1 values (13, null)") + + t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + + expect := map[string]struct { + count int + vals [][]types.Datum + }{ + "p0": {4, [][]types.Datum{ + {types.NewDatum(nil), types.NewIntDatum(3)}, + {types.NewIntDatum(1), types.NewIntDatum(1)}, + {types.NewIntDatum(2), types.NewIntDatum(1)}, + {types.NewIntDatum(3), types.NewIntDatum(1)}, + }}, + "p1": {7, [][]types.Datum{ + {types.NewDatum(nil), types.NewIntDatum(13)}, + {types.NewIntDatum(1), types.NewIntDatum(11)}, + {types.NewIntDatum(2), types.NewIntDatum(11)}, + {types.NewIntDatum(2), types.NewIntDatum(12)}, + {types.NewIntDatum(3), types.NewIntDatum(11)}, + {types.NewIntDatum(3), types.NewIntDatum(12)}, + {types.NewIntDatum(4), types.NewIntDatum(12)}, + }}, + } + + for _, def := range t1.Meta().GetPartitionInfo().Definitions { + partition := t1.(table.PartitionedTable).GetPartition(def.ID) + for _, index := range partition.Indices() { + if index.Meta().MVIndex { + checkCount(t, partition.IndexPrefix(), index, store, expect[def.Name.L].count) + checkKey(t, partition.IndexPrefix(), index, store, expect[def.Name.L].vals) + } + } + } + + tk.MustExec("delete from t1") + for _, def := range t1.Meta().GetPartitionInfo().Definitions { + partition := t1.(table.PartitionedTable).GetPartition(def.ID) + for _, index := range partition.Indices() { + if index.Meta().MVIndex { + checkCount(t, partition.IndexPrefix(), index, store, 0) + } + } + } +} + +func TestWriteMultiValuedIndexUnique(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(pk int primary key, a json, unique index idx((cast(a as signed array))))") + tk.MustExec("insert into t1 values (1, '[1,2,2]')") + tk.MustGetErrCode("insert into t1 values (2, '[1]')", errno.ErrDupEntry) + tk.MustExec("insert into t1 values (3, '[3,3,4]')") + + t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 4) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewIntDatum(1)}, + {types.NewIntDatum(2)}, + {types.NewIntDatum(3)}, + {types.NewIntDatum(4)}, + }) + } + } +} + +func TestWriteMultiValuedIndexComposite(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(pk int primary key, a json, c int, d int, index idx(c, (cast(a as signed array)), d))") + tk.MustExec("insert into t1 values (1, '[1,2,2]', 1, 1)") + tk.MustExec("insert into t1 values (2, '[2,2,2]', 2, 2)") + tk.MustExec("insert into t1 values (3, '[3,3,4]', 3, 3)") + tk.MustExec("insert into t1 values (4, null, 4, 4)") + tk.MustExec("insert into t1 values (5, '[]', 5, 5)") + + t1, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + for _, index := range t1.Indices() { + if index.Meta().MVIndex { + checkCount(t, t1.IndexPrefix(), index, store, 6) + checkKey(t, t1.IndexPrefix(), index, store, [][]types.Datum{ + {types.NewIntDatum(1), types.NewIntDatum(1), types.NewIntDatum(1), types.NewIntDatum(1)}, + {types.NewIntDatum(1), types.NewIntDatum(2), types.NewIntDatum(1), types.NewIntDatum(1)}, + {types.NewIntDatum(2), types.NewIntDatum(2), types.NewIntDatum(2), types.NewIntDatum(2)}, + {types.NewIntDatum(3), types.NewIntDatum(3), types.NewIntDatum(3), types.NewIntDatum(3)}, + {types.NewIntDatum(3), types.NewIntDatum(4), types.NewIntDatum(3), types.NewIntDatum(3)}, + {types.NewIntDatum(4), types.NewDatum(nil), types.NewIntDatum(4), types.NewIntDatum(4)}, + }) + } + } +} + +func checkCount(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, except int) { + c := 0 + checkIndex(t, prefix, index, store, func(it kv.Iterator) { + c++ + }) + require.Equal(t, except, c) +} + +func checkKey(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, except [][]types.Datum) { + idx := 0 + checkIndex(t, prefix, index, store, func(it kv.Iterator) { + indexKey := decodeIndexKey(t, it.Key()) + require.Equal(t, except[idx], indexKey) + idx++ + }) +} + +func checkIndex(t *testing.T, prefix kv.Key, index table.Index, store kv.Storage, fn func(kv.Iterator)) { + startKey := codec.EncodeInt(prefix, index.Meta().ID) + prefix.Next() + se := testkit.NewTestKit(t, store).Session() + err := sessiontxn.NewTxn(context.Background(), se) + require.NoError(t, err) + txn, err := se.Txn(true) + require.NoError(t, err) + it, err := txn.Iter(startKey, prefix.PrefixNext()) + require.NoError(t, err) + for it.Valid() && it.Key().HasPrefix(prefix) { + fn(it) + err = it.Next() + require.NoError(t, err) + } + it.Close() + se.Close() +} + +func decodeIndexKey(t *testing.T, key kv.Key) []types.Datum { + var idLen = 8 + var prefixLen = 1 + idLen /*tableID*/ + 2 + _, _, isRecord, err := tablecodec.DecodeKeyHead(key) + require.NoError(t, err) + require.False(t, isRecord) + indexKey := key[prefixLen+idLen:] + var datumValues []types.Datum + for len(indexKey) > 0 { + remain, d, err := codec.DecodeOne(indexKey) + require.NoError(t, err) + datumValues = append(datumValues, d) + indexKey = remain + } + return datumValues +} diff --git a/expression/typeinfer_test.go b/pkg/expression/typeinfer_test.go similarity index 99% rename from expression/typeinfer_test.go rename to pkg/expression/typeinfer_test.go index c78e73f7a14d6..f2cff3c08f9fc 100644 --- a/expression/typeinfer_test.go +++ b/pkg/expression/typeinfer_test.go @@ -20,17 +20,17 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/printer" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/printer" "github.com/stretchr/testify/require" ) diff --git a/pkg/expression/util.go b/pkg/expression/util.go new file mode 100644 index 0000000000000..8a493b885631e --- /dev/null +++ b/pkg/expression/util.go @@ -0,0 +1,1796 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "bytes" + "context" + "math" + "strconv" + "strings" + "unicode" + "unicode/utf8" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" + "golang.org/x/tools/container/intsets" +) + +// cowExprRef is a copy-on-write slice ref util using in `ColumnSubstitute` +// to reduce unnecessary allocation for Expression arguments array +type cowExprRef struct { + ref []Expression + new []Expression +} + +// Set will allocate new array if changed flag true +func (c *cowExprRef) Set(i int, changed bool, val Expression) { + if c.new != nil { + c.new[i] = val + return + } + if !changed { + return + } + c.new = make([]Expression, len(c.ref)) + copy(c.new, c.ref) + c.new[i] = val +} + +// Result return the final reference +func (c *cowExprRef) Result() []Expression { + if c.new != nil { + return c.new + } + return c.ref +} + +// Filter the input expressions, append the results to result. +func Filter(result []Expression, input []Expression, filter func(Expression) bool) []Expression { + for _, e := range input { + if filter(e) { + result = append(result, e) + } + } + return result +} + +// FilterOutInPlace do the filtering out in place. +// The remained are the ones who doesn't match the filter, storing in the original slice. +// The filteredOut are the ones match the filter, storing in a new slice. +func FilterOutInPlace(input []Expression, filter func(Expression) bool) (remained, filteredOut []Expression) { + for i := len(input) - 1; i >= 0; i-- { + if filter(input[i]) { + filteredOut = append(filteredOut, input[i]) + input = append(input[:i], input[i+1:]...) + } + } + return input, filteredOut +} + +// ExtractDependentColumns extracts all dependent columns from a virtual column. +func ExtractDependentColumns(expr Expression) []*Column { + // Pre-allocate a slice to reduce allocation, 8 doesn't have special meaning. + result := make([]*Column, 0, 8) + return extractDependentColumns(result, expr) +} + +func extractDependentColumns(result []*Column, expr Expression) []*Column { + switch v := expr.(type) { + case *Column: + result = append(result, v) + if v.VirtualExpr != nil { + result = extractDependentColumns(result, v.VirtualExpr) + } + case *ScalarFunction: + for _, arg := range v.GetArgs() { + result = extractDependentColumns(result, arg) + } + } + return result +} + +// ExtractColumns extracts all columns from an expression. +func ExtractColumns(expr Expression) []*Column { + // Pre-allocate a slice to reduce allocation, 8 doesn't have special meaning. + result := make([]*Column, 0, 8) + return extractColumns(result, expr, nil) +} + +// ExtractCorColumns extracts correlated column from given expression. +func ExtractCorColumns(expr Expression) (cols []*CorrelatedColumn) { + switch v := expr.(type) { + case *CorrelatedColumn: + return []*CorrelatedColumn{v} + case *ScalarFunction: + for _, arg := range v.GetArgs() { + cols = append(cols, ExtractCorColumns(arg)...) + } + } + return +} + +// ExtractColumnsFromExpressions is a more efficient version of ExtractColumns for batch operation. +// filter can be nil, or a function to filter the result column. +// It's often observed that the pattern of the caller like this: +// +// cols := ExtractColumns(...) +// +// for _, col := range cols { +// if xxx(col) {...} +// } +// +// Provide an additional filter argument, this can be done in one step. +// To avoid allocation for cols that not need. +func ExtractColumnsFromExpressions(result []*Column, exprs []Expression, filter func(*Column) bool) []*Column { + for _, expr := range exprs { + result = extractColumns(result, expr, filter) + } + return result +} + +func extractColumns(result []*Column, expr Expression, filter func(*Column) bool) []*Column { + switch v := expr.(type) { + case *Column: + if filter == nil || filter(v) { + result = append(result, v) + } + case *ScalarFunction: + for _, arg := range v.GetArgs() { + result = extractColumns(result, arg, filter) + } + } + return result +} + +// ExtractEquivalenceColumns detects the equivalence from CNF exprs. +func ExtractEquivalenceColumns(result [][]Expression, exprs []Expression) [][]Expression { + // exprs are CNF expressions, EQ condition only make sense in the top level of every expr. + for _, expr := range exprs { + result = extractEquivalenceColumns(result, expr) + } + return result +} + +// FindUpperBound looks for column < constant or column <= constant and returns both the column +// and constant. It return nil, 0 if the expression is not of this form. +// It is used by derived Top N pattern and it is put here since it looks like +// a general purpose routine. Similar routines can be added to find lower bound as well. +func FindUpperBound(expr Expression) (*Column, int64) { + scalarFunction, scalarFunctionOk := expr.(*ScalarFunction) + if scalarFunctionOk { + args := scalarFunction.GetArgs() + if len(args) == 2 { + col, colOk := args[0].(*Column) + constant, constantOk := args[1].(*Constant) + if colOk && constantOk && (scalarFunction.FuncName.L == ast.LT || scalarFunction.FuncName.L == ast.LE) { + value, valueOk := constant.Value.GetValue().(int64) + if valueOk { + if scalarFunction.FuncName.L == ast.LT { + return col, value - 1 + } + return col, value + } + } + } + } + return nil, 0 +} + +func extractEquivalenceColumns(result [][]Expression, expr Expression) [][]Expression { + switch v := expr.(type) { + case *ScalarFunction: + // a==b, a<=>b, the latter one is evaluated to true when a,b are both null. + if v.FuncName.L == ast.EQ || v.FuncName.L == ast.NullEQ { + args := v.GetArgs() + if len(args) == 2 { + col1, ok1 := args[0].(*Column) + col2, ok2 := args[1].(*Column) + if ok1 && ok2 { + result = append(result, []Expression{col1, col2}) + } + col, ok1 := args[0].(*Column) + scl, ok2 := args[1].(*ScalarFunction) + if ok1 && ok2 { + result = append(result, []Expression{col, scl}) + } + col, ok1 = args[1].(*Column) + scl, ok2 = args[0].(*ScalarFunction) + if ok1 && ok2 { + result = append(result, []Expression{col, scl}) + } + } + return result + } + if v.FuncName.L == ast.In { + args := v.GetArgs() + // only `col in (only 1 element)`, can we build an equivalence here. + if len(args[1:]) == 1 { + col1, ok1 := args[0].(*Column) + col2, ok2 := args[1].(*Column) + if ok1 && ok2 { + result = append(result, []Expression{col1, col2}) + } + col, ok1 := args[0].(*Column) + scl, ok2 := args[1].(*ScalarFunction) + if ok1 && ok2 { + result = append(result, []Expression{col, scl}) + } + col, ok1 = args[1].(*Column) + scl, ok2 = args[0].(*ScalarFunction) + if ok1 && ok2 { + result = append(result, []Expression{col, scl}) + } + } + return result + } + // For Non-EQ function, we don't have to traverse down. + // eg: (a=b or c=d) doesn't make any definitely equivalence assertion. + } + return result +} + +// extractColumnsAndCorColumns extracts columns and correlated columns from `expr` and append them to `result`. +func extractColumnsAndCorColumns(result []*Column, expr Expression) []*Column { + switch v := expr.(type) { + case *Column: + result = append(result, v) + case *CorrelatedColumn: + result = append(result, &v.Column) + case *ScalarFunction: + for _, arg := range v.GetArgs() { + result = extractColumnsAndCorColumns(result, arg) + } + } + return result +} + +// ExtractConstantEqColumnsOrScalar detects the constant equal relationship from CNF exprs. +func ExtractConstantEqColumnsOrScalar(ctx sessionctx.Context, result []Expression, exprs []Expression) []Expression { + // exprs are CNF expressions, EQ condition only make sense in the top level of every expr. + for _, expr := range exprs { + result = extractConstantEqColumnsOrScalar(ctx, result, expr) + } + return result +} + +func extractConstantEqColumnsOrScalar(ctx sessionctx.Context, result []Expression, expr Expression) []Expression { + switch v := expr.(type) { + case *ScalarFunction: + if v.FuncName.L == ast.EQ || v.FuncName.L == ast.NullEQ { + args := v.GetArgs() + if len(args) == 2 { + col, ok1 := args[0].(*Column) + _, ok2 := args[1].(*Constant) + if ok1 && ok2 { + result = append(result, col) + } + col, ok1 = args[1].(*Column) + _, ok2 = args[0].(*Constant) + if ok1 && ok2 { + result = append(result, col) + } + // take the correlated column as constant here. + col, ok1 = args[0].(*Column) + _, ok2 = args[1].(*CorrelatedColumn) + if ok1 && ok2 { + result = append(result, col) + } + col, ok1 = args[1].(*Column) + _, ok2 = args[0].(*CorrelatedColumn) + if ok1 && ok2 { + result = append(result, col) + } + scl, ok1 := args[0].(*ScalarFunction) + _, ok2 = args[1].(*Constant) + if ok1 && ok2 { + result = append(result, scl) + } + scl, ok1 = args[1].(*ScalarFunction) + _, ok2 = args[0].(*Constant) + if ok1 && ok2 { + result = append(result, scl) + } + // take the correlated column as constant here. + scl, ok1 = args[0].(*ScalarFunction) + _, ok2 = args[1].(*CorrelatedColumn) + if ok1 && ok2 { + result = append(result, scl) + } + scl, ok1 = args[1].(*ScalarFunction) + _, ok2 = args[0].(*CorrelatedColumn) + if ok1 && ok2 { + result = append(result, scl) + } + } + return result + } + if v.FuncName.L == ast.In { + args := v.GetArgs() + allArgsIsConst := true + // only `col in (all same const)`, can col be the constant column. + // eg: a in (1, "1") does, while a in (1, '2') doesn't. + guard := args[1] + for i, v := range args[1:] { + if _, ok := v.(*Constant); !ok { + allArgsIsConst = false + break + } + if i == 0 { + continue + } + if !guard.Equal(ctx, v) { + allArgsIsConst = false + break + } + } + if allArgsIsConst { + if col, ok := args[0].(*Column); ok { + result = append(result, col) + } else if scl, ok := args[0].(*ScalarFunction); ok { + result = append(result, scl) + } + } + return result + } + // For Non-EQ function, we don't have to traverse down. + } + return result +} + +// ExtractColumnsAndCorColumnsFromExpressions extracts columns and correlated columns from expressions and append them to `result`. +func ExtractColumnsAndCorColumnsFromExpressions(result []*Column, list []Expression) []*Column { + for _, expr := range list { + result = extractColumnsAndCorColumns(result, expr) + } + return result +} + +// ExtractColumnSet extracts the different values of `UniqueId` for columns in expressions. +func ExtractColumnSet(exprs ...Expression) *intsets.Sparse { + set := &intsets.Sparse{} + for _, expr := range exprs { + extractColumnSet(expr, set) + } + return set +} + +func extractColumnSet(expr Expression, set *intsets.Sparse) { + switch v := expr.(type) { + case *Column: + set.Insert(int(v.UniqueID)) + case *ScalarFunction: + for _, arg := range v.GetArgs() { + extractColumnSet(arg, set) + } + } +} + +// SetExprColumnInOperand is used to set columns in expr as InOperand. +func SetExprColumnInOperand(expr Expression) Expression { + switch v := expr.(type) { + case *Column: + col := v.Clone().(*Column) + col.InOperand = true + return col + case *ScalarFunction: + args := v.GetArgs() + for i, arg := range args { + args[i] = SetExprColumnInOperand(arg) + } + } + return expr +} + +// ColumnSubstitute substitutes the columns in filter to expressions in select fields. +// e.g. select * from (select b as a from t) k where a < 10 => select * from (select b as a from t where b < 10) k. +func ColumnSubstitute(expr Expression, schema *Schema, newExprs []Expression) Expression { + _, _, resExpr := ColumnSubstituteImpl(expr, schema, newExprs, false) + return resExpr +} + +// ColumnSubstituteAll substitutes the columns just like ColumnSubstitute, but we don't accept partial substitution. +// Only accept: +// +// 1: substitute them all once find col in schema. +// 2: nothing in expr can be substituted. +func ColumnSubstituteAll(expr Expression, schema *Schema, newExprs []Expression) (bool, Expression) { + _, hasFail, resExpr := ColumnSubstituteImpl(expr, schema, newExprs, true) + return hasFail, resExpr +} + +// ColumnSubstituteImpl tries to substitute column expr using newExprs, +// the newFunctionInternal is only called if its child is substituted +// @return bool means whether the expr has changed. +// @return bool means whether the expr should change (has the dependency in schema, while the corresponding expr has some compatibility), but finally fallback. +// @return Expression, the original expr or the changed expr, it depends on the first @return bool. +func ColumnSubstituteImpl(expr Expression, schema *Schema, newExprs []Expression, fail1Return bool) (bool, bool, Expression) { + switch v := expr.(type) { + case *Column: + id := schema.ColumnIndex(v) + if id == -1 { + return false, false, v + } + newExpr := newExprs[id] + if v.InOperand { + newExpr = SetExprColumnInOperand(newExpr) + } + return true, false, newExpr + case *ScalarFunction: + substituted := false + hasFail := false + if v.FuncName.L == ast.Cast || v.FuncName.L == ast.Grouping { + var newArg Expression + substituted, hasFail, newArg = ColumnSubstituteImpl(v.GetArgs()[0], schema, newExprs, fail1Return) + if fail1Return && hasFail { + return substituted, hasFail, v + } + if substituted { + flag := v.RetType.GetFlag() + var e Expression + if v.FuncName.L == ast.Cast { + e = BuildCastFunction(v.GetCtx(), newArg, v.RetType) + } else { + // for grouping function recreation, use clone (meta included) instead of newFunction + e = v.Clone() + e.(*ScalarFunction).Function.getArgs()[0] = newArg + } + e.SetCoercibility(v.Coercibility()) + e.GetType().SetFlag(flag) + return true, false, e + } + return false, false, v + } + // cowExprRef is a copy-on-write util, args array allocation happens only + // when expr in args is changed + refExprArr := cowExprRef{v.GetArgs(), nil} + oldCollEt, err := CheckAndDeriveCollationFromExprs(v.GetCtx(), v.FuncName.L, v.RetType.EvalType(), v.GetArgs()...) + if err != nil { + logutil.BgLogger().Error("Unexpected error happened during ColumnSubstitution", zap.Stack("stack")) + return false, false, v + } + var tmpArgForCollCheck []Expression + if collate.NewCollationEnabled() { + tmpArgForCollCheck = make([]Expression, len(v.GetArgs())) + } + for idx, arg := range v.GetArgs() { + changed, failed, newFuncExpr := ColumnSubstituteImpl(arg, schema, newExprs, fail1Return) + if fail1Return && failed { + return changed, failed, v + } + oldChanged := changed + if collate.NewCollationEnabled() && changed { + // Make sure the collation used by the ScalarFunction isn't changed and its result collation is not weaker than the collation used by the ScalarFunction. + changed = false + copy(tmpArgForCollCheck, refExprArr.Result()) + tmpArgForCollCheck[idx] = newFuncExpr + newCollEt, err := CheckAndDeriveCollationFromExprs(v.GetCtx(), v.FuncName.L, v.RetType.EvalType(), tmpArgForCollCheck...) + if err != nil { + logutil.BgLogger().Error("Unexpected error happened during ColumnSubstitution", zap.Stack("stack")) + return false, failed, v + } + if oldCollEt.Collation == newCollEt.Collation { + if newFuncExpr.GetType().GetCollate() == arg.GetType().GetCollate() && newFuncExpr.Coercibility() == arg.Coercibility() { + // It's safe to use the new expression, otherwise some cases in projection push-down will be wrong. + changed = true + } else { + changed = checkCollationStrictness(oldCollEt.Collation, newFuncExpr.GetType().GetCollate()) + } + } + } + hasFail = hasFail || failed || oldChanged != changed + if fail1Return && oldChanged != changed { + // Only when the oldChanged is true and changed is false, we will get here. + // And this means there some dependency in this arg can be substituted with + // given expressions, while it has some collation compatibility, finally we + // fall back to use the origin args. (commonly used in projection elimination + // in which fallback usage is unacceptable) + return changed, true, v + } + refExprArr.Set(idx, changed, newFuncExpr) + if changed { + substituted = true + } + } + if substituted { + return true, hasFail, NewFunctionInternal(v.GetCtx(), v.FuncName.L, v.RetType, refExprArr.Result()...) + } + } + return false, false, expr +} + +// checkCollationStrictness check collation strictness-ship between `coll` and `newFuncColl` +// return true iff `newFuncColl` is not weaker than `coll` +func checkCollationStrictness(coll, newFuncColl string) bool { + collGroupID, ok1 := CollationStrictnessGroup[coll] + newFuncCollGroupID, ok2 := CollationStrictnessGroup[newFuncColl] + + if ok1 && ok2 { + if collGroupID == newFuncCollGroupID { + return true + } + + for _, id := range CollationStrictness[collGroupID] { + if newFuncCollGroupID == id { + return true + } + } + } + + return false +} + +// getValidPrefix gets a prefix of string which can parsed to a number with base. the minimum base is 2 and the maximum is 36. +func getValidPrefix(s string, base int64) string { + var ( + validLen int + upper rune + ) + switch { + case base >= 2 && base <= 9: + upper = rune('0' + base) + case base <= 36: + upper = rune('A' + base - 10) + default: + return "" + } +Loop: + for i := 0; i < len(s); i++ { + c := rune(s[i]) + switch { + case unicode.IsDigit(c) || unicode.IsLower(c) || unicode.IsUpper(c): + c = unicode.ToUpper(c) + if c >= upper { + break Loop + } + validLen = i + 1 + case c == '+' || c == '-': + if i != 0 { + break Loop + } + default: + break Loop + } + } + if validLen > 1 && s[0] == '+' { + return s[1:validLen] + } + return s[:validLen] +} + +// SubstituteCorCol2Constant will substitute correlated column to constant value which it contains. +// If the args of one scalar function are all constant, we will substitute it to constant. +func SubstituteCorCol2Constant(expr Expression) (Expression, error) { + switch x := expr.(type) { + case *ScalarFunction: + allConstant := true + newArgs := make([]Expression, 0, len(x.GetArgs())) + for _, arg := range x.GetArgs() { + newArg, err := SubstituteCorCol2Constant(arg) + if err != nil { + return nil, err + } + _, ok := newArg.(*Constant) + newArgs = append(newArgs, newArg) + allConstant = allConstant && ok + } + if allConstant { + val, err := x.Eval(chunk.Row{}) + if err != nil { + return nil, err + } + return &Constant{Value: val, RetType: x.GetType()}, nil + } + var ( + err error + newSf Expression + ) + if x.FuncName.L == ast.Cast { + newSf = BuildCastFunction(x.GetCtx(), newArgs[0], x.RetType) + } else if x.FuncName.L == ast.Grouping { + newSf = x.Clone() + newSf.(*ScalarFunction).GetArgs()[0] = newArgs[0] + } else { + newSf, err = NewFunction(x.GetCtx(), x.FuncName.L, x.GetType(), newArgs...) + } + return newSf, err + case *CorrelatedColumn: + return &Constant{Value: *x.Data, RetType: x.GetType()}, nil + case *Constant: + if x.DeferredExpr != nil { + newExpr := FoldConstant(x) + return &Constant{Value: newExpr.(*Constant).Value, RetType: x.GetType()}, nil + } + } + return expr, nil +} + +func locateStringWithCollation(str, substr, coll string) int64 { + collator := collate.GetCollator(coll) + strKey := collator.KeyWithoutTrimRightSpace(str) + subStrKey := collator.KeyWithoutTrimRightSpace(substr) + + index := bytes.Index(strKey, subStrKey) + if index == -1 || index == 0 { + return int64(index + 1) + } + + // todo: we can use binary search to make it faster. + count := int64(0) + for { + r, size := utf8.DecodeRuneInString(str) + count++ + index -= len(collator.KeyWithoutTrimRightSpace(string(r))) + if index <= 0 { + return count + 1 + } + str = str[size:] + } +} + +// timeZone2Duration converts timezone whose format should satisfy the regular condition +// `(^(+|-)(0?[0-9]|1[0-2]):[0-5]?\d$)|(^+13:00$)` to int for use by time.FixedZone(). +func timeZone2int(tz string) int { + sign := 1 + if strings.HasPrefix(tz, "-") { + sign = -1 + } + + i := strings.Index(tz, ":") + h, err := strconv.Atoi(tz[1:i]) + terror.Log(err) + m, err := strconv.Atoi(tz[i+1:]) + terror.Log(err) + return sign * ((h * 3600) + (m * 60)) +} + +var logicalOps = map[string]struct{}{ + ast.LT: {}, + ast.GE: {}, + ast.GT: {}, + ast.LE: {}, + ast.EQ: {}, + ast.NE: {}, + ast.UnaryNot: {}, + ast.LogicAnd: {}, + ast.LogicOr: {}, + ast.LogicXor: {}, + ast.In: {}, + ast.IsNull: {}, + ast.IsTruthWithoutNull: {}, + ast.IsFalsity: {}, + ast.Like: {}, +} + +var oppositeOp = map[string]string{ + ast.LT: ast.GE, + ast.GE: ast.LT, + ast.GT: ast.LE, + ast.LE: ast.GT, + ast.EQ: ast.NE, + ast.NE: ast.EQ, + ast.LogicOr: ast.LogicAnd, + ast.LogicAnd: ast.LogicOr, +} + +// a op b is equal to b symmetricOp a +var symmetricOp = map[opcode.Op]opcode.Op{ + opcode.LT: opcode.GT, + opcode.GE: opcode.LE, + opcode.GT: opcode.LT, + opcode.LE: opcode.GE, + opcode.EQ: opcode.EQ, + opcode.NE: opcode.NE, + opcode.NullEQ: opcode.NullEQ, +} + +func pushNotAcrossArgs(ctx sessionctx.Context, exprs []Expression, not bool) ([]Expression, bool) { + newExprs := make([]Expression, 0, len(exprs)) + flag := false + for _, expr := range exprs { + newExpr, changed := pushNotAcrossExpr(ctx, expr, not) + flag = changed || flag + newExprs = append(newExprs, newExpr) + } + return newExprs, flag +} + +// todo: consider more no precision-loss downcast cases. +func noPrecisionLossCastCompatible(cast, argCol *types.FieldType) bool { + // now only consider varchar type and integer. + if !(types.IsTypeVarchar(cast.GetType()) && types.IsTypeVarchar(argCol.GetType())) && + !(mysql.IsIntegerType(cast.GetType()) && mysql.IsIntegerType(argCol.GetType())) { + // varchar type and integer on the storage layer is quite same, while the char type has its padding suffix. + return false + } + if types.IsTypeVarchar(cast.GetType()) { + // cast varchar function only bear the flen extension. + if cast.GetFlen() < argCol.GetFlen() { + return false + } + if !collate.CompatibleCollate(cast.GetCollate(), argCol.GetCollate()) { + return false + } + } else { + // For integers, we should ignore the potential display length represented by flen, using the default flen of the type. + castFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(cast.GetType()) + originFlen, _ := mysql.GetDefaultFieldLengthAndDecimal(argCol.GetType()) + // cast integer function only bear the flen extension and signed symbol unchanged. + if castFlen < originFlen { + return false + } + if mysql.HasUnsignedFlag(cast.GetFlag()) != mysql.HasUnsignedFlag(argCol.GetFlag()) { + return false + } + } + return true +} + +func unwrapCast(sctx sessionctx.Context, parentF *ScalarFunction, castOffset int) (Expression, bool) { + _, collation := parentF.CharsetAndCollation() + cast, ok := parentF.GetArgs()[castOffset].(*ScalarFunction) + if !ok || cast.FuncName.L != ast.Cast { + return parentF, false + } + // eg: if (cast(A) EQ const) with incompatible collation, even if cast is eliminated, the condition still can not be used to build range. + if cast.RetType.EvalType() == types.ETString && !collate.CompatibleCollate(cast.RetType.GetCollate(), collation) { + return parentF, false + } + // 1-castOffset should be constant + if _, ok := parentF.GetArgs()[1-castOffset].(*Constant); !ok { + return parentF, false + } + + // the direct args of cast function should be column. + c, ok := cast.GetArgs()[0].(*Column) + if !ok { + return parentF, false + } + + // current only consider varchar and integer + if !noPrecisionLossCastCompatible(cast.RetType, c.RetType) { + return parentF, false + } + + // the column is covered by indexes, deconstructing it out. + if castOffset == 0 { + return NewFunctionInternal(sctx, parentF.FuncName.L, parentF.RetType, c, parentF.GetArgs()[1]), true + } + return NewFunctionInternal(sctx, parentF.FuncName.L, parentF.RetType, parentF.GetArgs()[0], c), true +} + +// eliminateCastFunction will detect the original arg before and the cast type after, once upon +// there is no precision loss between them, current cast wrapper can be eliminated. For string +// type, collation is also taken into consideration. (mainly used to build range or point) +func eliminateCastFunction(sctx sessionctx.Context, expr Expression) (_ Expression, changed bool) { + f, ok := expr.(*ScalarFunction) + if !ok { + return expr, false + } + _, collation := expr.CharsetAndCollation() + switch f.FuncName.L { + case ast.LogicOr: + dnfItems := FlattenDNFConditions(f) + rmCast := false + rmCastItems := make([]Expression, len(dnfItems)) + for i, dnfItem := range dnfItems { + newExpr, curDowncast := eliminateCastFunction(sctx, dnfItem) + rmCastItems[i] = newExpr + if curDowncast { + rmCast = true + } + } + if rmCast { + // compose the new DNF expression. + return ComposeDNFCondition(sctx, rmCastItems...), true + } + return expr, false + case ast.LogicAnd: + cnfItems := FlattenCNFConditions(f) + rmCast := false + rmCastItems := make([]Expression, len(cnfItems)) + for i, cnfItem := range cnfItems { + newExpr, curDowncast := eliminateCastFunction(sctx, cnfItem) + rmCastItems[i] = newExpr + if curDowncast { + rmCast = true + } + } + if rmCast { + // compose the new CNF expression. + return ComposeCNFCondition(sctx, rmCastItems...), true + } + return expr, false + case ast.EQ, ast.NullEQ, ast.LE, ast.GE, ast.LT, ast.GT: + // for case: eq(cast(test.t2.a, varchar(100), "aaaaa"), once t2.a is covered by index or pk, try deconstructing it out. + if newF, ok := unwrapCast(sctx, f, 0); ok { + return newF, true + } + // for case: eq("aaaaa", cast(test.t2.a, varchar(100)), once t2.a is covered by index or pk, try deconstructing it out. + if newF, ok := unwrapCast(sctx, f, 1); ok { + return newF, true + } + case ast.In: + // case for: cast(a as bigint) in (1,2,3), we could deconstruct column 'a out directly. + cast, ok := f.GetArgs()[0].(*ScalarFunction) + if !ok || cast.FuncName.L != ast.Cast { + return expr, false + } + // eg: if (cast(A) IN {const}) with incompatible collation, even if cast is eliminated, the condition still can not be used to build range. + if cast.RetType.EvalType() == types.ETString && !collate.CompatibleCollate(cast.RetType.GetCollate(), collation) { + return expr, false + } + for _, arg := range f.GetArgs()[1:] { + if _, ok := arg.(*Constant); !ok { + return expr, false + } + } + // the direct args of cast function should be column. + c, ok := cast.GetArgs()[0].(*Column) + if !ok { + return expr, false + } + // current only consider varchar and integer + if !noPrecisionLossCastCompatible(cast.RetType, c.RetType) { + return expr, false + } + newArgs := []Expression{c} + newArgs = append(newArgs, f.GetArgs()[1:]...) + return NewFunctionInternal(sctx, f.FuncName.L, f.RetType, newArgs...), true + } + return expr, false +} + +// pushNotAcrossExpr try to eliminate the NOT expr in expression tree. +// Input `not` indicates whether there's a `NOT` be pushed down. +// Output `changed` indicates whether the output expression differs from the +// input `expr` because of the pushed-down-not. +func pushNotAcrossExpr(ctx sessionctx.Context, expr Expression, not bool) (_ Expression, changed bool) { + if f, ok := expr.(*ScalarFunction); ok { + switch f.FuncName.L { + case ast.UnaryNot: + child, err := wrapWithIsTrue(ctx, true, f.GetArgs()[0], true) + if err != nil { + return expr, false + } + var childExpr Expression + childExpr, changed = pushNotAcrossExpr(f.GetCtx(), child, !not) + if !changed && !not { + return expr, false + } + return childExpr, true + case ast.LT, ast.GE, ast.GT, ast.LE, ast.EQ, ast.NE: + if not { + return NewFunctionInternal(f.GetCtx(), oppositeOp[f.FuncName.L], f.GetType(), f.GetArgs()...), true + } + newArgs, changed := pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), false) + if !changed { + return f, false + } + return NewFunctionInternal(f.GetCtx(), f.FuncName.L, f.GetType(), newArgs...), true + case ast.LogicAnd, ast.LogicOr: + var ( + newArgs []Expression + changed bool + ) + funcName := f.FuncName.L + if not { + newArgs, _ = pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), true) + funcName = oppositeOp[f.FuncName.L] + changed = true + } else { + newArgs, changed = pushNotAcrossArgs(f.GetCtx(), f.GetArgs(), false) + } + if !changed { + return f, false + } + return NewFunctionInternal(f.GetCtx(), funcName, f.GetType(), newArgs...), true + } + } + if not { + expr = NewFunctionInternal(ctx, ast.UnaryNot, types.NewFieldType(mysql.TypeTiny), expr) + } + return expr, not +} + +// GetExprInsideIsTruth get the expression inside the `istrue_with_null` and `istrue`. +// This is useful when handling expressions from "not" or "!", because we might wrap `istrue_with_null` or `istrue` +// when handling them. See pushNotAcrossExpr() and wrapWithIsTrue() for details. +func GetExprInsideIsTruth(expr Expression) Expression { + if f, ok := expr.(*ScalarFunction); ok { + switch f.FuncName.L { + case ast.IsTruthWithNull, ast.IsTruthWithoutNull: + return GetExprInsideIsTruth(f.GetArgs()[0]) + default: + return expr + } + } + return expr +} + +// PushDownNot pushes the `not` function down to the expression's arguments. +func PushDownNot(ctx sessionctx.Context, expr Expression) Expression { + newExpr, _ := pushNotAcrossExpr(ctx, expr, false) + return newExpr +} + +// EliminateNoPrecisionLossCast remove the redundant cast function for range build convenience. +// 1: deeper cast embedded in other complicated function will not be considered. +// 2: cast args should be one for original base column and one for constant. +// 3: some collation compatibility and precision loss will be considered when remove this cast func. +func EliminateNoPrecisionLossCast(sctx sessionctx.Context, expr Expression) Expression { + newExpr, _ := eliminateCastFunction(sctx, expr) + return newExpr +} + +// ContainOuterNot checks if there is an outer `not`. +func ContainOuterNot(expr Expression) bool { + return containOuterNot(expr, false) +} + +// containOuterNot checks if there is an outer `not`. +// Input `not` means whether there is `not` outside `expr` +// +// eg. +// +// not(0+(t.a == 1 and t.b == 2)) returns true +// not(t.a) and not(t.b) returns false +func containOuterNot(expr Expression, not bool) bool { + if f, ok := expr.(*ScalarFunction); ok { + switch f.FuncName.L { + case ast.UnaryNot: + return containOuterNot(f.GetArgs()[0], true) + case ast.IsTruthWithNull, ast.IsNull: + return containOuterNot(f.GetArgs()[0], not) + default: + if not { + return true + } + hasNot := false + for _, expr := range f.GetArgs() { + hasNot = hasNot || containOuterNot(expr, not) + if hasNot { + return hasNot + } + } + return hasNot + } + } + return false +} + +// Contains tests if `exprs` contains `e`. +func Contains(exprs []Expression, e Expression) bool { + for _, expr := range exprs { + if e == expr { + return true + } + } + return false +} + +// ExtractFiltersFromDNFs checks whether the cond is DNF. If so, it will get the extracted part and the remained part. +// The original DNF will be replaced by the remained part or just be deleted if remained part is nil. +// And the extracted part will be appended to the end of the orignal slice. +func ExtractFiltersFromDNFs(ctx sessionctx.Context, conditions []Expression) []Expression { + var allExtracted []Expression + for i := len(conditions) - 1; i >= 0; i-- { + if sf, ok := conditions[i].(*ScalarFunction); ok && sf.FuncName.L == ast.LogicOr { + extracted, remained := extractFiltersFromDNF(ctx, sf) + allExtracted = append(allExtracted, extracted...) + if remained == nil { + conditions = append(conditions[:i], conditions[i+1:]...) + } else { + conditions[i] = remained + } + } + } + return append(conditions, allExtracted...) +} + +// extractFiltersFromDNF extracts the same condition that occurs in every DNF item and remove them from dnf leaves. +func extractFiltersFromDNF(ctx sessionctx.Context, dnfFunc *ScalarFunction) ([]Expression, Expression) { + dnfItems := FlattenDNFConditions(dnfFunc) + sc := ctx.GetSessionVars().StmtCtx + codeMap := make(map[string]int) + hashcode2Expr := make(map[string]Expression) + for i, dnfItem := range dnfItems { + innerMap := make(map[string]struct{}) + cnfItems := SplitCNFItems(dnfItem) + for _, cnfItem := range cnfItems { + code := cnfItem.HashCode(sc) + if i == 0 { + codeMap[string(code)] = 1 + hashcode2Expr[string(code)] = cnfItem + } else if _, ok := codeMap[string(code)]; ok { + // We need this check because there may be the case like `select * from t, t1 where (t.a=t1.a and t.a=t1.a) or (something). + // We should make sure that the two `t.a=t1.a` contributes only once. + // TODO: do this out of this function. + if _, ok = innerMap[string(code)]; !ok { + codeMap[string(code)]++ + innerMap[string(code)] = struct{}{} + } + } + } + } + // We should make sure that this item occurs in every DNF item. + for hashcode, cnt := range codeMap { + if cnt < len(dnfItems) { + delete(hashcode2Expr, hashcode) + } + } + if len(hashcode2Expr) == 0 { + return nil, dnfFunc + } + newDNFItems := make([]Expression, 0, len(dnfItems)) + onlyNeedExtracted := false + for _, dnfItem := range dnfItems { + cnfItems := SplitCNFItems(dnfItem) + newCNFItems := make([]Expression, 0, len(cnfItems)) + for _, cnfItem := range cnfItems { + code := cnfItem.HashCode(sc) + _, ok := hashcode2Expr[string(code)] + if !ok { + newCNFItems = append(newCNFItems, cnfItem) + } + } + // If the extracted part is just one leaf of the DNF expression. Then the value of the total DNF expression is + // always the same with the value of the extracted part. + if len(newCNFItems) == 0 { + onlyNeedExtracted = true + break + } + newDNFItems = append(newDNFItems, ComposeCNFCondition(ctx, newCNFItems...)) + } + extractedExpr := make([]Expression, 0, len(hashcode2Expr)) + for _, expr := range hashcode2Expr { + extractedExpr = append(extractedExpr, expr) + } + if onlyNeedExtracted { + return extractedExpr, nil + } + return extractedExpr, ComposeDNFCondition(ctx, newDNFItems...) +} + +// DeriveRelaxedFiltersFromDNF given a DNF expression, derive a relaxed DNF expression which only contains columns +// in specified schema; the derived expression is a superset of original expression, i.e, any tuple satisfying +// the original expression must satisfy the derived expression. Return nil when the derived expression is universal set. +// A running example is: for schema of t1, `(t1.a=1 and t2.a=1) or (t1.a=2 and t2.a=2)` would be derived as +// `t1.a=1 or t1.a=2`, while `t1.a=1 or t2.a=1` would get nil. +func DeriveRelaxedFiltersFromDNF(expr Expression, schema *Schema) Expression { + sf, ok := expr.(*ScalarFunction) + if !ok || sf.FuncName.L != ast.LogicOr { + return nil + } + ctx := sf.GetCtx() + dnfItems := FlattenDNFConditions(sf) + newDNFItems := make([]Expression, 0, len(dnfItems)) + for _, dnfItem := range dnfItems { + cnfItems := SplitCNFItems(dnfItem) + newCNFItems := make([]Expression, 0, len(cnfItems)) + for _, cnfItem := range cnfItems { + if itemSF, ok := cnfItem.(*ScalarFunction); ok && itemSF.FuncName.L == ast.LogicOr { + relaxedCNFItem := DeriveRelaxedFiltersFromDNF(cnfItem, schema) + if relaxedCNFItem != nil { + newCNFItems = append(newCNFItems, relaxedCNFItem) + } + // If relaxed expression for embedded DNF is universal set, just drop this CNF item + continue + } + // This cnfItem must be simple expression now + // If it cannot be fully covered by schema, just drop this CNF item + if ExprFromSchema(cnfItem, schema) { + newCNFItems = append(newCNFItems, cnfItem) + } + } + // If this DNF item involves no column of specified schema, the relaxed expression must be universal set + if len(newCNFItems) == 0 { + return nil + } + newDNFItems = append(newDNFItems, ComposeCNFCondition(ctx, newCNFItems...)) + } + return ComposeDNFCondition(ctx, newDNFItems...) +} + +// GetRowLen gets the length if the func is row, returns 1 if not row. +func GetRowLen(e Expression) int { + if f, ok := e.(*ScalarFunction); ok && f.FuncName.L == ast.RowFunc { + return len(f.GetArgs()) + } + return 1 +} + +// CheckArgsNotMultiColumnRow checks the args are not multi-column row. +func CheckArgsNotMultiColumnRow(args ...Expression) error { + for _, arg := range args { + if GetRowLen(arg) != 1 { + return ErrOperandColumns.GenWithStackByArgs(1) + } + } + return nil +} + +// GetFuncArg gets the argument of the function at idx. +func GetFuncArg(e Expression, idx int) Expression { + if f, ok := e.(*ScalarFunction); ok { + return f.GetArgs()[idx] + } + return nil +} + +// PopRowFirstArg pops the first element and returns the rest of row. +// e.g. After this function (1, 2, 3) becomes (2, 3). +func PopRowFirstArg(ctx sessionctx.Context, e Expression) (ret Expression, err error) { + if f, ok := e.(*ScalarFunction); ok && f.FuncName.L == ast.RowFunc { + args := f.GetArgs() + if len(args) == 2 { + return args[1], nil + } + ret, err = NewFunction(ctx, ast.RowFunc, f.GetType(), args[1:]...) + return ret, err + } + return +} + +// DatumToConstant generates a Constant expression from a Datum. +func DatumToConstant(d types.Datum, tp byte, flag uint) *Constant { + t := types.NewFieldType(tp) + t.AddFlag(flag) + return &Constant{Value: d, RetType: t} +} + +// ParamMarkerExpression generate a getparam function expression. +func ParamMarkerExpression(ctx sessionctx.Context, v *driver.ParamMarkerExpr, needParam bool) (*Constant, error) { + useCache := ctx.GetSessionVars().StmtCtx.UseCache + isPointExec := ctx.GetSessionVars().StmtCtx.PointExec + tp := types.NewFieldType(mysql.TypeUnspecified) + types.InferParamTypeFromDatum(&v.Datum, tp) + value := &Constant{Value: v.Datum, RetType: tp} + if useCache || isPointExec || needParam { + value.ParamMarker = &ParamMarker{ + order: v.Order, + ctx: ctx, + } + } + return value, nil +} + +// ParamMarkerInPrepareChecker checks whether the given ast tree has paramMarker and is in prepare statement. +type ParamMarkerInPrepareChecker struct { + InPrepareStmt bool +} + +// Enter implements Visitor Interface. +func (pc *ParamMarkerInPrepareChecker) Enter(in ast.Node) (out ast.Node, skipChildren bool) { + switch v := in.(type) { + case *driver.ParamMarkerExpr: + pc.InPrepareStmt = !v.InExecute + return v, true + } + return in, false +} + +// Leave implements Visitor Interface. +func (pc *ParamMarkerInPrepareChecker) Leave(in ast.Node) (out ast.Node, ok bool) { + return in, true +} + +// DisableParseJSONFlag4Expr disables ParseToJSONFlag for `expr` except Column. +// We should not *PARSE* a string as JSON under some scenarios. ParseToJSONFlag +// is 0 for JSON column yet(as well as JSON correlated column), so we can skip +// it. Moreover, Column.RetType refers to the infoschema, if we modify it, data +// race may happen if another goroutine read from the infoschema at the same +// time. +func DisableParseJSONFlag4Expr(expr Expression) { + if _, isColumn := expr.(*Column); isColumn { + return + } + if _, isCorCol := expr.(*CorrelatedColumn); isCorCol { + return + } + expr.GetType().SetFlag(expr.GetType().GetFlag() & ^mysql.ParseToJSONFlag) +} + +// ConstructPositionExpr constructs PositionExpr with the given ParamMarkerExpr. +func ConstructPositionExpr(p *driver.ParamMarkerExpr) *ast.PositionExpr { + return &ast.PositionExpr{P: p} +} + +// PosFromPositionExpr generates a position value from PositionExpr. +func PosFromPositionExpr(ctx sessionctx.Context, v *ast.PositionExpr) (int, bool, error) { + if v.P == nil { + return v.N, false, nil + } + value, err := ParamMarkerExpression(ctx, v.P.(*driver.ParamMarkerExpr), false) + if err != nil { + return 0, true, err + } + pos, isNull, err := GetIntFromConstant(ctx, value) + if err != nil || isNull { + return 0, true, err + } + return pos, false, nil +} + +// GetStringFromConstant gets a string value from the Constant expression. +func GetStringFromConstant(ctx sessionctx.Context, value Expression) (string, bool, error) { + con, ok := value.(*Constant) + if !ok { + err := errors.Errorf("Not a Constant expression %+v", value) + return "", true, err + } + str, isNull, err := con.EvalString(ctx, chunk.Row{}) + if err != nil || isNull { + return "", true, err + } + return str, false, nil +} + +// GetIntFromConstant gets an interger value from the Constant expression. +func GetIntFromConstant(ctx sessionctx.Context, value Expression) (int, bool, error) { + str, isNull, err := GetStringFromConstant(ctx, value) + if err != nil || isNull { + return 0, true, err + } + intNum, err := strconv.Atoi(str) + if err != nil { + return 0, true, nil + } + return intNum, false, nil +} + +// BuildNotNullExpr wraps up `not(isnull())` for given expression. +func BuildNotNullExpr(ctx sessionctx.Context, expr Expression) Expression { + isNull := NewFunctionInternal(ctx, ast.IsNull, types.NewFieldType(mysql.TypeTiny), expr) + notNull := NewFunctionInternal(ctx, ast.UnaryNot, types.NewFieldType(mysql.TypeTiny), isNull) + return notNull +} + +// IsRuntimeConstExpr checks if a expr can be treated as a constant in **executor**. +func IsRuntimeConstExpr(expr Expression) bool { + switch x := expr.(type) { + case *ScalarFunction: + if _, ok := unFoldableFunctions[x.FuncName.L]; ok { + return false + } + for _, arg := range x.GetArgs() { + if !IsRuntimeConstExpr(arg) { + return false + } + } + return true + case *Column: + return false + case *Constant, *CorrelatedColumn: + return true + } + return false +} + +// CheckNonDeterministic checks whether the current expression contains a non-deterministic func. +func CheckNonDeterministic(e Expression) bool { + switch x := e.(type) { + case *Constant, *Column, *CorrelatedColumn: + return false + case *ScalarFunction: + if _, ok := unFoldableFunctions[x.FuncName.L]; ok { + return true + } + for _, arg := range x.GetArgs() { + if CheckNonDeterministic(arg) { + return true + } + } + } + return false +} + +// CheckFuncInExpr checks whether there's a given function in the expression. +func CheckFuncInExpr(e Expression, funcName string) bool { + switch x := e.(type) { + case *Constant, *Column, *CorrelatedColumn: + return false + case *ScalarFunction: + if x.FuncName.L == funcName { + return true + } + for _, arg := range x.GetArgs() { + if CheckFuncInExpr(arg, funcName) { + return true + } + } + } + return false +} + +// IsMutableEffectsExpr checks if expr contains function which is mutable or has side effects. +func IsMutableEffectsExpr(expr Expression) bool { + switch x := expr.(type) { + case *ScalarFunction: + if _, ok := mutableEffectsFunctions[x.FuncName.L]; ok { + return true + } + for _, arg := range x.GetArgs() { + if IsMutableEffectsExpr(arg) { + return true + } + } + case *Column: + case *Constant: + if x.DeferredExpr != nil { + return IsMutableEffectsExpr(x.DeferredExpr) + } + } + return false +} + +// IsInmutableExpr checks whether this expression only consists of foldable functions and inmutable constants. +// This expression can be evaluated by using `expr.Eval(chunk.Row{})` directly if it's inmutable. +func IsInmutableExpr(expr Expression) bool { + switch x := expr.(type) { + case *ScalarFunction: + if _, ok := unFoldableFunctions[x.FuncName.L]; ok { + return false + } + if _, ok := mutableEffectsFunctions[x.FuncName.L]; ok { + return false + } + for _, arg := range x.GetArgs() { + if !IsInmutableExpr(arg) { + return false + } + } + return true + case *Constant: + if x.DeferredExpr != nil || x.ParamMarker != nil { + return false + } + return true + default: + return false + } +} + +// RemoveDupExprs removes identical exprs. Not that if expr contains functions which +// are mutable or have side effects, we cannot remove it even if it has duplicates; +// if the plan is going to be cached, we cannot remove expressions containing `?` neither. +func RemoveDupExprs(ctx sessionctx.Context, exprs []Expression) []Expression { + res := make([]Expression, 0, len(exprs)) + exists := make(map[string]struct{}, len(exprs)) + sc := ctx.GetSessionVars().StmtCtx + for _, expr := range exprs { + key := string(expr.HashCode(sc)) + if _, ok := exists[key]; !ok || IsMutableEffectsExpr(expr) { + res = append(res, expr) + exists[key] = struct{}{} + } + } + return res +} + +// GetUint64FromConstant gets a uint64 from constant expression. +func GetUint64FromConstant(expr Expression) (uint64, bool, bool) { + con, ok := expr.(*Constant) + if !ok { + logutil.BgLogger().Warn("not a constant expression", zap.String("expression", expr.ExplainInfo())) + return 0, false, false + } + dt := con.Value + if con.ParamMarker != nil { + dt = con.ParamMarker.GetUserVar() + } else if con.DeferredExpr != nil { + var err error + dt, err = con.DeferredExpr.Eval(chunk.Row{}) + if err != nil { + logutil.BgLogger().Warn("eval deferred expr failed", zap.Error(err)) + return 0, false, false + } + } + switch dt.Kind() { + case types.KindNull: + return 0, true, true + case types.KindInt64: + val := dt.GetInt64() + if val < 0 { + return 0, false, false + } + return uint64(val), false, true + case types.KindUint64: + return dt.GetUint64(), false, true + } + return 0, false, false +} + +// ContainVirtualColumn checks if the expressions contain a virtual column +func ContainVirtualColumn(exprs []Expression) bool { + for _, expr := range exprs { + switch v := expr.(type) { + case *Column: + if v.VirtualExpr != nil { + return true + } + case *ScalarFunction: + if ContainVirtualColumn(v.GetArgs()) { + return true + } + } + } + return false +} + +// ContainCorrelatedColumn checks if the expressions contain a correlated column +func ContainCorrelatedColumn(exprs []Expression) bool { + for _, expr := range exprs { + switch v := expr.(type) { + case *CorrelatedColumn: + return true + case *ScalarFunction: + if ContainCorrelatedColumn(v.GetArgs()) { + return true + } + } + } + return false +} + +// MaybeOverOptimized4PlanCache used to check whether an optimization can work +// for the statement when we enable the plan cache. +// In some situations, some optimizations maybe over-optimize and cache an +// overOptimized plan. The cached plan may not get the correct result when we +// reuse the plan for other statements. +// For example, `pk>=$a and pk<=$b` can be optimized to a PointGet when +// `$a==$b`, but it will cause wrong results when `$a!=$b`. +// So we need to do the check here. The check includes the following aspects: +// 1. Whether the plan cache switch is enable. +// 2. Whether the statement can be cached. +// 3. Whether the expressions contain a lazy constant. +// TODO: Do more careful check here. +func MaybeOverOptimized4PlanCache(ctx sessionctx.Context, exprs []Expression) bool { + // If we do not enable plan cache, all the optimization can work correctly. + if !ctx.GetSessionVars().StmtCtx.UseCache { + return false + } + return containMutableConst(ctx, exprs) +} + +// containMutableConst checks if the expressions contain a lazy constant. +func containMutableConst(ctx sessionctx.Context, exprs []Expression) bool { + for _, expr := range exprs { + switch v := expr.(type) { + case *Constant: + if v.ParamMarker != nil || v.DeferredExpr != nil { + return true + } + case *ScalarFunction: + if containMutableConst(ctx, v.GetArgs()) { + return true + } + } + } + return false +} + +// RemoveMutableConst used to remove the `ParamMarker` and `DeferredExpr` in the `Constant` expr. +func RemoveMutableConst(ctx sessionctx.Context, exprs []Expression) { + for _, expr := range exprs { + switch v := expr.(type) { + case *Constant: + v.ParamMarker = nil + v.DeferredExpr = nil + case *ScalarFunction: + RemoveMutableConst(ctx, v.GetArgs()) + } + } +} + +const ( + _ = iota + kib = 1 << (10 * iota) + mib = 1 << (10 * iota) + gib = 1 << (10 * iota) + tib = 1 << (10 * iota) + pib = 1 << (10 * iota) + eib = 1 << (10 * iota) +) + +const ( + nano = 1 + micro = 1000 * nano + milli = 1000 * micro + sec = 1000 * milli + min = 60 * sec + hour = 60 * min + dayTime = 24 * hour +) + +// GetFormatBytes convert byte count to value with units. +func GetFormatBytes(bytes float64) string { + var divisor float64 + var unit string + + bytesAbs := math.Abs(bytes) + if bytesAbs >= eib { + divisor = eib + unit = "EiB" + } else if bytesAbs >= pib { + divisor = pib + unit = "PiB" + } else if bytesAbs >= tib { + divisor = tib + unit = "TiB" + } else if bytesAbs >= gib { + divisor = gib + unit = "GiB" + } else if bytesAbs >= mib { + divisor = mib + unit = "MiB" + } else if bytesAbs >= kib { + divisor = kib + unit = "KiB" + } else { + divisor = 1 + unit = "bytes" + } + + if divisor == 1 { + return strconv.FormatFloat(bytes, 'f', 0, 64) + " " + unit + } + value := bytes / divisor + if math.Abs(value) >= 100000.0 { + return strconv.FormatFloat(value, 'e', 2, 64) + " " + unit + } + return strconv.FormatFloat(value, 'f', 2, 64) + " " + unit +} + +// GetFormatNanoTime convert time in nanoseconds to value with units. +func GetFormatNanoTime(time float64) string { + var divisor float64 + var unit string + + timeAbs := math.Abs(time) + if timeAbs >= dayTime { + divisor = dayTime + unit = "d" + } else if timeAbs >= hour { + divisor = hour + unit = "h" + } else if timeAbs >= min { + divisor = min + unit = "min" + } else if timeAbs >= sec { + divisor = sec + unit = "s" + } else if timeAbs >= milli { + divisor = milli + unit = "ms" + } else if timeAbs >= micro { + divisor = micro + unit = "us" + } else { + divisor = 1 + unit = "ns" + } + + if divisor == 1 { + return strconv.FormatFloat(time, 'f', 0, 64) + " " + unit + } + value := time / divisor + if math.Abs(value) >= 100000.0 { + return strconv.FormatFloat(value, 'e', 2, 64) + " " + unit + } + return strconv.FormatFloat(value, 'f', 2, 64) + " " + unit +} + +// SQLDigestTextRetriever is used to find the normalized SQL statement text by SQL digests in statements_summary table. +// It's exported for test purposes. It's used by the `tidb_decode_sql_digests` builtin function, but also exposed to +// be used in other modules. +type SQLDigestTextRetriever struct { + // SQLDigestsMap is the place to put the digests that's requested for getting SQL text and also the place to put + // the query result. + SQLDigestsMap map[string]string + + // Replace querying for test purposes. + mockLocalData map[string]string + mockGlobalData map[string]string + // There are two ways for querying information: 1) query specified digests by WHERE IN query, or 2) query all + // information to avoid the too long WHERE IN clause. If there are more than `fetchAllLimit` digests needs to be + // queried, the second way will be chosen; otherwise, the first way will be chosen. + fetchAllLimit int +} + +// NewSQLDigestTextRetriever creates a new SQLDigestTextRetriever. +func NewSQLDigestTextRetriever() *SQLDigestTextRetriever { + return &SQLDigestTextRetriever{ + SQLDigestsMap: make(map[string]string), + fetchAllLimit: 512, + } +} + +func (r *SQLDigestTextRetriever) runMockQuery(data map[string]string, inValues []interface{}) (map[string]string, error) { + if len(inValues) == 0 { + return data, nil + } + res := make(map[string]string, len(inValues)) + for _, digest := range inValues { + if text, ok := data[digest.(string)]; ok { + res[digest.(string)] = text + } + } + return res, nil +} + +// runFetchDigestQuery runs query to the system tables to fetch the kv mapping of SQL digests and normalized SQL texts +// of the given SQL digests, if `inValues` is given, or all these mappings otherwise. If `queryGlobal` is false, it +// queries information_schema.statements_summary and information_schema.statements_summary_history; otherwise, it +// queries the cluster version of these two tables. +func (r *SQLDigestTextRetriever) runFetchDigestQuery(ctx context.Context, sctx sessionctx.Context, queryGlobal bool, inValues []interface{}) (map[string]string, error) { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) + // If mock data is set, query the mock data instead of the real statements_summary tables. + if !queryGlobal && r.mockLocalData != nil { + return r.runMockQuery(r.mockLocalData, inValues) + } else if queryGlobal && r.mockGlobalData != nil { + return r.runMockQuery(r.mockGlobalData, inValues) + } + + exec, ok := sctx.(sqlexec.RestrictedSQLExecutor) + if !ok { + return nil, errors.New("restricted sql can't be executed in this context") + } + + // Information in statements_summary will be periodically moved to statements_summary_history. Union them together + // to avoid missing information when statements_summary is just cleared. + stmt := "select digest, digest_text from information_schema.statements_summary union distinct " + + "select digest, digest_text from information_schema.statements_summary_history" + if queryGlobal { + stmt = "select digest, digest_text from information_schema.cluster_statements_summary union distinct " + + "select digest, digest_text from information_schema.cluster_statements_summary_history" + } + // Add the where clause if `inValues` is specified. + if len(inValues) > 0 { + stmt += " where digest in (" + strings.Repeat("%?,", len(inValues)-1) + "%?)" + } + + rows, _, err := exec.ExecRestrictedSQL(ctx, nil, stmt, inValues...) + if err != nil { + return nil, err + } + + res := make(map[string]string, len(rows)) + for _, row := range rows { + res[row.GetString(0)] = row.GetString(1) + } + return res, nil +} + +func (r *SQLDigestTextRetriever) updateDigestInfo(queryResult map[string]string) { + for digest, text := range r.SQLDigestsMap { + if len(text) > 0 { + // The text of this digest is already known + continue + } + sqlText, ok := queryResult[digest] + if ok { + r.SQLDigestsMap[digest] = sqlText + } + } +} + +// RetrieveLocal tries to retrieve the SQL text of the SQL digests from local information. +func (r *SQLDigestTextRetriever) RetrieveLocal(ctx context.Context, sctx sessionctx.Context) error { + if len(r.SQLDigestsMap) == 0 { + return nil + } + + var queryResult map[string]string + if len(r.SQLDigestsMap) <= r.fetchAllLimit { + inValues := make([]interface{}, 0, len(r.SQLDigestsMap)) + for key := range r.SQLDigestsMap { + inValues = append(inValues, key) + } + var err error + queryResult, err = r.runFetchDigestQuery(ctx, sctx, false, inValues) + if err != nil { + return errors.Trace(err) + } + + if len(queryResult) == len(r.SQLDigestsMap) { + r.SQLDigestsMap = queryResult + return nil + } + } else { + var err error + queryResult, err = r.runFetchDigestQuery(ctx, sctx, false, nil) + if err != nil { + return errors.Trace(err) + } + } + + r.updateDigestInfo(queryResult) + return nil +} + +// RetrieveGlobal tries to retrieve the SQL text of the SQL digests from the information of the whole cluster. +func (r *SQLDigestTextRetriever) RetrieveGlobal(ctx context.Context, sctx sessionctx.Context) error { + err := r.RetrieveLocal(ctx, sctx) + if err != nil { + return errors.Trace(err) + } + + // In some unit test environments it's unable to retrieve global info, and this function blocks it for tens of + // seconds, which wastes much time during unit test. In this case, enable this failpoint to bypass retrieving + // globally. + failpoint.Inject("sqlDigestRetrieverSkipRetrieveGlobal", func() { + failpoint.Return(nil) + }) + + var unknownDigests []interface{} + for k, v := range r.SQLDigestsMap { + if len(v) == 0 { + unknownDigests = append(unknownDigests, k) + } + } + + if len(unknownDigests) == 0 { + return nil + } + + var queryResult map[string]string + if len(r.SQLDigestsMap) <= r.fetchAllLimit { + queryResult, err = r.runFetchDigestQuery(ctx, sctx, true, unknownDigests) + if err != nil { + return errors.Trace(err) + } + } else { + queryResult, err = r.runFetchDigestQuery(ctx, sctx, true, nil) + if err != nil { + return errors.Trace(err) + } + } + + r.updateDigestInfo(queryResult) + return nil +} + +// ExprsToStringsForDisplay convert a slice of Expression to a slice of string using Expression.String(), and +// to make it better for display and debug, it also escapes the string to corresponding golang string literal, +// which means using \t, \n, \x??, \u????, ... to represent newline, control character, non-printable character, +// invalid utf-8 bytes and so on. +func ExprsToStringsForDisplay(exprs []Expression) []string { + strs := make([]string, len(exprs)) + for i, cond := range exprs { + quote := `"` + // We only need the escape functionality of strconv.Quote, the quoting is not needed, + // so we trim the \" prefix and suffix here. + strs[i] = strings.TrimSuffix( + strings.TrimPrefix( + strconv.Quote(cond.String()), + quote), + quote) + } + return strs +} diff --git a/pkg/expression/util_test.go b/pkg/expression/util_test.go new file mode 100644 index 0000000000000..a275871663dfb --- /dev/null +++ b/pkg/expression/util_test.go @@ -0,0 +1,602 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package expression + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestBaseBuiltin(t *testing.T) { + ctx := mock.NewContext() + bf, err := newBaseBuiltinFuncWithTp(ctx, "", nil, types.ETTimestamp) + require.NoError(t, err) + _, _, err = bf.evalInt(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalReal(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalString(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalDecimal(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalTime(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalDuration(chunk.Row{}) + require.Error(t, err) + _, _, err = bf.evalJSON(chunk.Row{}) + require.Error(t, err) +} + +func TestClone(t *testing.T) { + builtinFuncs := []builtinFunc{ + &builtinArithmeticPlusRealSig{}, &builtinArithmeticPlusDecimalSig{}, &builtinArithmeticPlusIntSig{}, &builtinArithmeticMinusRealSig{}, &builtinArithmeticMinusDecimalSig{}, + &builtinArithmeticMinusIntSig{}, &builtinArithmeticDivideRealSig{}, &builtinArithmeticDivideDecimalSig{}, &builtinArithmeticMultiplyRealSig{}, &builtinArithmeticMultiplyDecimalSig{}, + &builtinArithmeticMultiplyIntUnsignedSig{}, &builtinArithmeticMultiplyIntSig{}, &builtinArithmeticIntDivideIntSig{}, &builtinArithmeticIntDivideDecimalSig{}, + &builtinArithmeticModIntUnsignedUnsignedSig{}, &builtinArithmeticModIntUnsignedSignedSig{}, &builtinArithmeticModIntSignedUnsignedSig{}, &builtinArithmeticModIntSignedSignedSig{}, + &builtinArithmeticModRealSig{}, &builtinArithmeticModDecimalSig{}, &builtinCastIntAsIntSig{}, &builtinCastIntAsRealSig{}, &builtinCastIntAsStringSig{}, + &builtinCastIntAsDecimalSig{}, &builtinCastIntAsTimeSig{}, &builtinCastIntAsDurationSig{}, &builtinCastIntAsJSONSig{}, &builtinCastRealAsIntSig{}, + &builtinCastRealAsRealSig{}, &builtinCastRealAsStringSig{}, &builtinCastRealAsDecimalSig{}, &builtinCastRealAsTimeSig{}, &builtinCastRealAsDurationSig{}, + &builtinCastRealAsJSONSig{}, &builtinCastDecimalAsIntSig{}, &builtinCastDecimalAsRealSig{}, &builtinCastDecimalAsStringSig{}, &builtinCastDecimalAsDecimalSig{}, + &builtinCastDecimalAsTimeSig{}, &builtinCastDecimalAsDurationSig{}, &builtinCastDecimalAsJSONSig{}, &builtinCastStringAsIntSig{}, &builtinCastStringAsRealSig{}, + &builtinCastStringAsStringSig{}, &builtinCastStringAsDecimalSig{}, &builtinCastStringAsTimeSig{}, &builtinCastStringAsDurationSig{}, &builtinCastStringAsJSONSig{}, + &builtinCastTimeAsIntSig{}, &builtinCastTimeAsRealSig{}, &builtinCastTimeAsStringSig{}, &builtinCastTimeAsDecimalSig{}, &builtinCastTimeAsTimeSig{}, + &builtinCastTimeAsDurationSig{}, &builtinCastTimeAsJSONSig{}, &builtinCastDurationAsIntSig{}, &builtinCastDurationAsRealSig{}, &builtinCastDurationAsStringSig{}, + &builtinCastDurationAsDecimalSig{}, &builtinCastDurationAsTimeSig{}, &builtinCastDurationAsDurationSig{}, &builtinCastDurationAsJSONSig{}, &builtinCastJSONAsIntSig{}, + &builtinCastJSONAsRealSig{}, &builtinCastJSONAsStringSig{}, &builtinCastJSONAsDecimalSig{}, &builtinCastJSONAsTimeSig{}, &builtinCastJSONAsDurationSig{}, + &builtinCastJSONAsJSONSig{}, &builtinCoalesceIntSig{}, &builtinCoalesceRealSig{}, &builtinCoalesceDecimalSig{}, &builtinCoalesceStringSig{}, + &builtinCoalesceTimeSig{}, &builtinCoalesceDurationSig{}, &builtinGreatestIntSig{}, &builtinGreatestRealSig{}, &builtinGreatestDecimalSig{}, + &builtinGreatestStringSig{}, &builtinGreatestTimeSig{}, &builtinLeastIntSig{}, &builtinLeastRealSig{}, &builtinLeastDecimalSig{}, + &builtinLeastStringSig{}, &builtinLeastTimeSig{}, &builtinIntervalIntSig{}, &builtinIntervalRealSig{}, &builtinLTIntSig{}, + &builtinLTRealSig{}, &builtinLTDecimalSig{}, &builtinLTStringSig{}, &builtinLTDurationSig{}, &builtinLTTimeSig{}, + &builtinLEIntSig{}, &builtinLERealSig{}, &builtinLEDecimalSig{}, &builtinLEStringSig{}, &builtinLEDurationSig{}, + &builtinLETimeSig{}, &builtinGTIntSig{}, &builtinGTRealSig{}, &builtinGTDecimalSig{}, &builtinGTStringSig{}, + &builtinGTTimeSig{}, &builtinGTDurationSig{}, &builtinGEIntSig{}, &builtinGERealSig{}, &builtinGEDecimalSig{}, + &builtinGEStringSig{}, &builtinGETimeSig{}, &builtinGEDurationSig{}, &builtinNEIntSig{}, &builtinNERealSig{}, + &builtinNEDecimalSig{}, &builtinNEStringSig{}, &builtinNETimeSig{}, &builtinNEDurationSig{}, &builtinNullEQIntSig{}, + &builtinNullEQRealSig{}, &builtinNullEQDecimalSig{}, &builtinNullEQStringSig{}, &builtinNullEQTimeSig{}, &builtinNullEQDurationSig{}, + &builtinCaseWhenIntSig{}, &builtinCaseWhenRealSig{}, &builtinCaseWhenDecimalSig{}, &builtinCaseWhenStringSig{}, &builtinCaseWhenTimeSig{}, + &builtinCaseWhenDurationSig{}, &builtinIfNullIntSig{}, &builtinIfNullRealSig{}, &builtinIfNullDecimalSig{}, &builtinIfNullStringSig{}, + &builtinIfNullTimeSig{}, &builtinIfNullDurationSig{}, &builtinIfNullJSONSig{}, &builtinIfIntSig{}, &builtinIfRealSig{}, + &builtinIfDecimalSig{}, &builtinIfStringSig{}, &builtinIfTimeSig{}, &builtinIfDurationSig{}, &builtinIfJSONSig{}, + &builtinAesDecryptSig{}, &builtinAesDecryptIVSig{}, &builtinAesEncryptSig{}, &builtinAesEncryptIVSig{}, &builtinCompressSig{}, + &builtinMD5Sig{}, &builtinPasswordSig{}, &builtinRandomBytesSig{}, &builtinSHA1Sig{}, &builtinSHA2Sig{}, + &builtinUncompressSig{}, &builtinUncompressedLengthSig{}, &builtinDatabaseSig{}, &builtinFoundRowsSig{}, &builtinCurrentUserSig{}, + &builtinUserSig{}, &builtinConnectionIDSig{}, &builtinLastInsertIDSig{}, &builtinLastInsertIDWithIDSig{}, &builtinVersionSig{}, + &builtinTiDBVersionSig{}, &builtinRowCountSig{}, &builtinJSONTypeSig{}, &builtinJSONQuoteSig{}, &builtinJSONUnquoteSig{}, + &builtinJSONArraySig{}, &builtinJSONArrayAppendSig{}, &builtinJSONObjectSig{}, &builtinJSONExtractSig{}, &builtinJSONSetSig{}, + &builtinJSONInsertSig{}, &builtinJSONReplaceSig{}, &builtinJSONRemoveSig{}, &builtinJSONMergeSig{}, &builtinJSONContainsSig{}, + &builtinJSONStorageSizeSig{}, &builtinJSONDepthSig{}, &builtinJSONSearchSig{}, &builtinJSONKeysSig{}, &builtinJSONKeys2ArgsSig{}, &builtinJSONLengthSig{}, + &builtinLikeSig{}, &builtinIlikeSig{}, &builtinRegexpLikeFuncSig{}, &builtinRegexpSubstrFuncSig{}, &builtinRegexpInStrFuncSig{}, &builtinRegexpReplaceFuncSig{}, &builtinAbsRealSig{}, &builtinAbsIntSig{}, + &builtinAbsUIntSig{}, &builtinAbsDecSig{}, &builtinRoundRealSig{}, &builtinRoundIntSig{}, &builtinRoundDecSig{}, + &builtinRoundWithFracRealSig{}, &builtinRoundWithFracIntSig{}, &builtinRoundWithFracDecSig{}, &builtinCeilRealSig{}, &builtinCeilIntToDecSig{}, + &builtinCeilIntToIntSig{}, &builtinCeilDecToIntSig{}, &builtinCeilDecToDecSig{}, &builtinFloorRealSig{}, &builtinFloorIntToDecSig{}, + &builtinFloorIntToIntSig{}, &builtinFloorDecToIntSig{}, &builtinFloorDecToDecSig{}, &builtinLog1ArgSig{}, &builtinLog2ArgsSig{}, + &builtinLog2Sig{}, &builtinLog10Sig{}, &builtinRandSig{}, &builtinRandWithSeedFirstGenSig{}, &builtinPowSig{}, + &builtinConvSig{}, &builtinCRC32Sig{}, &builtinSignSig{}, &builtinSqrtSig{}, &builtinAcosSig{}, + &builtinAsinSig{}, &builtinAtan1ArgSig{}, &builtinAtan2ArgsSig{}, &builtinCosSig{}, &builtinCotSig{}, + &builtinDegreesSig{}, &builtinExpSig{}, &builtinPISig{}, &builtinRadiansSig{}, &builtinSinSig{}, + &builtinTanSig{}, &builtinTruncateIntSig{}, &builtinTruncateRealSig{}, &builtinTruncateDecimalSig{}, &builtinTruncateUintSig{}, + &builtinSleepSig{}, &builtinLockSig{}, &builtinReleaseLockSig{}, &builtinDecimalAnyValueSig{}, &builtinDurationAnyValueSig{}, + &builtinIntAnyValueSig{}, &builtinJSONAnyValueSig{}, &builtinRealAnyValueSig{}, &builtinStringAnyValueSig{}, &builtinTimeAnyValueSig{}, + &builtinInetAtonSig{}, &builtinInetNtoaSig{}, &builtinInet6AtonSig{}, &builtinInet6NtoaSig{}, &builtinIsIPv4Sig{}, + &builtinIsIPv4CompatSig{}, &builtinIsIPv4MappedSig{}, &builtinIsIPv6Sig{}, &builtinUUIDSig{}, &builtinNameConstIntSig{}, + &builtinNameConstRealSig{}, &builtinNameConstDecimalSig{}, &builtinNameConstTimeSig{}, &builtinNameConstDurationSig{}, &builtinNameConstStringSig{}, + &builtinNameConstJSONSig{}, &builtinLogicAndSig{}, &builtinLogicOrSig{}, &builtinLogicXorSig{}, &builtinRealIsTrueSig{}, + &builtinDecimalIsTrueSig{}, &builtinIntIsTrueSig{}, &builtinRealIsFalseSig{}, &builtinDecimalIsFalseSig{}, &builtinIntIsFalseSig{}, + &builtinUnaryMinusIntSig{}, &builtinDecimalIsNullSig{}, &builtinDurationIsNullSig{}, &builtinIntIsNullSig{}, &builtinRealIsNullSig{}, + &builtinStringIsNullSig{}, &builtinTimeIsNullSig{}, &builtinUnaryNotRealSig{}, &builtinUnaryNotDecimalSig{}, &builtinUnaryNotIntSig{}, &builtinSleepSig{}, &builtinInIntSig{}, + &builtinInStringSig{}, &builtinInDecimalSig{}, &builtinInRealSig{}, &builtinInTimeSig{}, &builtinInDurationSig{}, + &builtinInJSONSig{}, &builtinRowSig{}, &builtinSetStringVarSig{}, &builtinSetIntVarSig{}, &builtinSetRealVarSig{}, &builtinSetDecimalVarSig{}, + &builtinGetIntVarSig{}, &builtinGetRealVarSig{}, &builtinGetDecimalVarSig{}, &builtinGetStringVarSig{}, &builtinLockSig{}, + &builtinReleaseLockSig{}, &builtinValuesIntSig{}, &builtinValuesRealSig{}, &builtinValuesDecimalSig{}, &builtinValuesStringSig{}, + &builtinValuesTimeSig{}, &builtinValuesDurationSig{}, &builtinValuesJSONSig{}, &builtinBitCountSig{}, &builtinGetParamStringSig{}, + &builtinLengthSig{}, &builtinASCIISig{}, &builtinConcatSig{}, &builtinConcatWSSig{}, &builtinLeftSig{}, + &builtinLeftUTF8Sig{}, &builtinRightSig{}, &builtinRightUTF8Sig{}, &builtinRepeatSig{}, &builtinLowerSig{}, + &builtinReverseUTF8Sig{}, &builtinReverseSig{}, &builtinSpaceSig{}, &builtinUpperSig{}, &builtinStrcmpSig{}, + &builtinReplaceSig{}, &builtinConvertSig{}, &builtinSubstring2ArgsSig{}, &builtinSubstring3ArgsSig{}, &builtinSubstring2ArgsUTF8Sig{}, + &builtinSubstring3ArgsUTF8Sig{}, &builtinSubstringIndexSig{}, &builtinLocate2ArgsUTF8Sig{}, &builtinLocate3ArgsUTF8Sig{}, &builtinLocate2ArgsSig{}, + &builtinLocate3ArgsSig{}, &builtinHexStrArgSig{}, &builtinHexIntArgSig{}, &builtinUnHexSig{}, &builtinTrim1ArgSig{}, + &builtinTrim2ArgsSig{}, &builtinTrim3ArgsSig{}, &builtinLTrimSig{}, &builtinRTrimSig{}, &builtinLpadUTF8Sig{}, + &builtinLpadSig{}, &builtinRpadUTF8Sig{}, &builtinRpadSig{}, &builtinBitLengthSig{}, &builtinCharSig{}, + &builtinCharLengthUTF8Sig{}, &builtinFindInSetSig{}, &builtinMakeSetSig{}, &builtinOctIntSig{}, &builtinOctStringSig{}, + &builtinOrdSig{}, &builtinQuoteSig{}, &builtinBinSig{}, &builtinEltSig{}, &builtinExportSet3ArgSig{}, + &builtinExportSet4ArgSig{}, &builtinExportSet5ArgSig{}, &builtinFormatWithLocaleSig{}, &builtinFormatSig{}, &builtinFromBase64Sig{}, + &builtinToBase64Sig{}, &builtinInsertSig{}, &builtinInsertUTF8Sig{}, &builtinInstrUTF8Sig{}, &builtinInstrSig{}, + &builtinFieldRealSig{}, &builtinFieldIntSig{}, &builtinFieldStringSig{}, &builtinDateSig{}, &builtinDateLiteralSig{}, + &builtinDateDiffSig{}, &builtinNullTimeDiffSig{}, &builtinTimeStringTimeDiffSig{}, &builtinDurationStringTimeDiffSig{}, &builtinDurationDurationTimeDiffSig{}, + &builtinStringTimeTimeDiffSig{}, &builtinStringDurationTimeDiffSig{}, &builtinStringStringTimeDiffSig{}, &builtinTimeTimeTimeDiffSig{}, &builtinDateFormatSig{}, + &builtinHourSig{}, &builtinMinuteSig{}, &builtinSecondSig{}, &builtinMicroSecondSig{}, &builtinMonthSig{}, + &builtinMonthNameSig{}, &builtinNowWithArgSig{}, &builtinNowWithoutArgSig{}, &builtinDayNameSig{}, &builtinDayOfMonthSig{}, + &builtinDayOfWeekSig{}, &builtinDayOfYearSig{}, &builtinWeekWithModeSig{}, &builtinWeekWithoutModeSig{}, &builtinWeekDaySig{}, + &builtinWeekOfYearSig{}, &builtinYearSig{}, &builtinYearWeekWithModeSig{}, &builtinYearWeekWithoutModeSig{}, &builtinGetFormatSig{}, + &builtinSysDateWithFspSig{}, &builtinSysDateWithoutFspSig{}, &builtinCurrentDateSig{}, &builtinCurrentTime0ArgSig{}, &builtinCurrentTime1ArgSig{}, + &builtinTimeSig{}, &builtinTimeLiteralSig{}, &builtinUTCDateSig{}, &builtinUTCTimestampWithArgSig{}, &builtinUTCTimestampWithoutArgSig{}, + &builtinAddDatetimeAndDurationSig{}, &builtinAddDatetimeAndStringSig{}, &builtinAddTimeDateTimeNullSig{}, &builtinAddStringAndDurationSig{}, &builtinAddStringAndStringSig{}, + &builtinAddTimeStringNullSig{}, &builtinAddDurationAndDurationSig{}, &builtinAddDurationAndStringSig{}, &builtinAddTimeDurationNullSig{}, &builtinAddDateAndDurationSig{}, + &builtinAddDateAndStringSig{}, &builtinSubDatetimeAndDurationSig{}, &builtinSubDatetimeAndStringSig{}, &builtinSubTimeDateTimeNullSig{}, &builtinSubStringAndDurationSig{}, + &builtinSubStringAndStringSig{}, &builtinSubTimeStringNullSig{}, &builtinSubDurationAndDurationSig{}, &builtinSubDurationAndStringSig{}, &builtinSubTimeDurationNullSig{}, + &builtinSubDateAndDurationSig{}, &builtinSubDateAndStringSig{}, &builtinUnixTimestampCurrentSig{}, &builtinUnixTimestampIntSig{}, &builtinUnixTimestampDecSig{}, + &builtinConvertTzSig{}, &builtinMakeDateSig{}, &builtinMakeTimeSig{}, &builtinPeriodAddSig{}, &builtinPeriodDiffSig{}, + &builtinQuarterSig{}, &builtinSecToTimeSig{}, &builtinTimeToSecSig{}, &builtinTimestampAddSig{}, &builtinToDaysSig{}, + &builtinToSecondsSig{}, &builtinUTCTimeWithArgSig{}, &builtinUTCTimeWithoutArgSig{}, &builtinTimestamp1ArgSig{}, &builtinTimestamp2ArgsSig{}, + &builtinTimestampLiteralSig{}, &builtinLastDaySig{}, &builtinStrToDateDateSig{}, &builtinStrToDateDatetimeSig{}, &builtinStrToDateDurationSig{}, + &builtinFromUnixTime1ArgSig{}, &builtinFromUnixTime2ArgSig{}, &builtinExtractDatetimeFromStringSig{}, &builtinExtractDatetimeSig{}, &builtinExtractDurationSig{}, &builtinAddSubDateAsStringSig{}, + &builtinAddSubDateDatetimeAnySig{}, &builtinAddSubDateDurationAnySig{}, + } + for _, f := range builtinFuncs { + cf := f.Clone() + require.IsType(t, f, cf) + } +} + +func TestGetUint64FromConstant(t *testing.T) { + con := &Constant{ + Value: types.NewDatum(nil), + } + _, isNull, ok := GetUint64FromConstant(con) + require.True(t, ok) + require.True(t, isNull) + + con = &Constant{ + Value: types.NewIntDatum(-1), + } + _, _, ok = GetUint64FromConstant(con) + require.False(t, ok) + + con.Value = types.NewIntDatum(1) + num, isNull, ok := GetUint64FromConstant(con) + require.True(t, ok) + require.False(t, isNull) + require.Equal(t, uint64(1), num) + + con.Value = types.NewUintDatum(1) + num, _, _ = GetUint64FromConstant(con) + require.Equal(t, uint64(1), num) + + con.DeferredExpr = &Constant{Value: types.NewIntDatum(1)} + num, _, _ = GetUint64FromConstant(con) + require.Equal(t, uint64(1), num) + + ctx := mock.NewContext() + ctx.GetSessionVars().PlanCacheParams.Append(types.NewUintDatum(100)) + con.ParamMarker = &ParamMarker{order: 0, ctx: ctx} + num, _, _ = GetUint64FromConstant(con) + require.Equal(t, uint64(100), num) +} + +func TestSetExprColumnInOperand(t *testing.T) { + col := &Column{RetType: newIntFieldType()} + require.True(t, SetExprColumnInOperand(col).(*Column).InOperand) + + f, err := funcs[ast.Abs].getFunction(mock.NewContext(), []Expression{col}) + require.NoError(t, err) + fun := &ScalarFunction{Function: f} + SetExprColumnInOperand(fun) + require.True(t, f.getArgs()[0].(*Column).InOperand) +} + +func TestPopRowFirstArg(t *testing.T) { + c1, c2, c3 := &Column{RetType: newIntFieldType()}, &Column{RetType: newIntFieldType()}, &Column{RetType: newIntFieldType()} + f, err := funcs[ast.RowFunc].getFunction(mock.NewContext(), []Expression{c1, c2, c3}) + require.NoError(t, err) + fun := &ScalarFunction{Function: f, FuncName: model.NewCIStr(ast.RowFunc), RetType: newIntFieldType()} + fun2, err := PopRowFirstArg(mock.NewContext(), fun) + require.NoError(t, err) + require.Len(t, fun2.(*ScalarFunction).GetArgs(), 2) +} + +func TestGetStrIntFromConstant(t *testing.T) { + col := &Column{} + _, _, err := GetStringFromConstant(mock.NewContext(), col) + require.Error(t, err) + + con := &Constant{RetType: types.NewFieldType(mysql.TypeNull)} + _, isNull, err := GetStringFromConstant(mock.NewContext(), con) + require.NoError(t, err) + require.True(t, isNull) + + con = &Constant{RetType: newIntFieldType(), Value: types.NewIntDatum(1)} + ret, _, _ := GetStringFromConstant(mock.NewContext(), con) + require.Equal(t, "1", ret) + + con = &Constant{RetType: types.NewFieldType(mysql.TypeNull)} + _, isNull, _ = GetIntFromConstant(mock.NewContext(), con) + require.True(t, isNull) + + con = &Constant{RetType: newStringFieldType(), Value: types.NewStringDatum("abc")} + _, isNull, _ = GetIntFromConstant(mock.NewContext(), con) + require.True(t, isNull) + + con = &Constant{RetType: newStringFieldType(), Value: types.NewStringDatum("123")} + num, _, _ := GetIntFromConstant(mock.NewContext(), con) + require.Equal(t, 123, num) +} + +func TestSubstituteCorCol2Constant(t *testing.T) { + ctx := mock.NewContext() + corCol1 := &CorrelatedColumn{Data: &NewOne().Value} + corCol1.RetType = types.NewFieldType(mysql.TypeLonglong) + corCol2 := &CorrelatedColumn{Data: &NewOne().Value} + corCol2.RetType = types.NewFieldType(mysql.TypeLonglong) + cast := BuildCastFunction(ctx, corCol1, types.NewFieldType(mysql.TypeLonglong)) + plus := newFunction(ast.Plus, cast, corCol2) + plus2 := newFunction(ast.Plus, plus, NewOne()) + ans1 := &Constant{Value: types.NewIntDatum(3), RetType: types.NewFieldType(mysql.TypeLonglong)} + ret, err := SubstituteCorCol2Constant(plus2) + require.NoError(t, err) + require.True(t, ret.Equal(ctx, ans1)) + col1 := &Column{Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} + ret, err = SubstituteCorCol2Constant(col1) + require.NoError(t, err) + ans2 := col1 + require.True(t, ret.Equal(ctx, ans2)) + plus3 := newFunction(ast.Plus, plus2, col1) + ret, err = SubstituteCorCol2Constant(plus3) + require.NoError(t, err) + ans3 := newFunction(ast.Plus, ans1, col1) + require.True(t, ret.Equal(ctx, ans3)) +} + +func TestPushDownNot(t *testing.T) { + ctx := mock.NewContext() + col := &Column{Index: 1, RetType: types.NewFieldType(mysql.TypeLonglong)} + // !((a=1||a=1)&&a=1) + eqFunc := newFunction(ast.EQ, col, NewOne()) + orFunc := newFunction(ast.LogicOr, eqFunc, eqFunc) + andFunc := newFunction(ast.LogicAnd, orFunc, eqFunc) + notFunc := newFunction(ast.UnaryNot, andFunc) + // (a!=1&&a!=1)||a=1 + neFunc := newFunction(ast.NE, col, NewOne()) + andFunc2 := newFunction(ast.LogicAnd, neFunc, neFunc) + orFunc2 := newFunction(ast.LogicOr, andFunc2, neFunc) + notFuncCopy := notFunc.Clone() + ret := PushDownNot(ctx, notFunc) + require.True(t, ret.Equal(ctx, orFunc2)) + require.True(t, notFunc.Equal(ctx, notFuncCopy)) + + // issue 15725 + // (not not a) should be optimized to (a is true) + notFunc = newFunction(ast.UnaryNot, col) + notFunc = newFunction(ast.UnaryNot, notFunc) + ret = PushDownNot(ctx, notFunc) + require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, col))) + + // (not not (a+1)) should be optimized to (a+1 is true) + plusFunc := newFunction(ast.Plus, col, NewOne()) + notFunc = newFunction(ast.UnaryNot, plusFunc) + notFunc = newFunction(ast.UnaryNot, notFunc) + ret = PushDownNot(ctx, notFunc) + require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, plusFunc))) + // (not not not a) should be optimized to (not (a is true)) + notFunc = newFunction(ast.UnaryNot, col) + notFunc = newFunction(ast.UnaryNot, notFunc) + notFunc = newFunction(ast.UnaryNot, notFunc) + ret = PushDownNot(ctx, notFunc) + require.True(t, ret.Equal(ctx, newFunction(ast.UnaryNot, newFunction(ast.IsTruthWithNull, col)))) + // (not not not not a) should be optimized to (a is true) + notFunc = newFunction(ast.UnaryNot, col) + notFunc = newFunction(ast.UnaryNot, notFunc) + notFunc = newFunction(ast.UnaryNot, notFunc) + notFunc = newFunction(ast.UnaryNot, notFunc) + ret = PushDownNot(ctx, notFunc) + require.True(t, ret.Equal(ctx, newFunction(ast.IsTruthWithNull, col))) +} + +func TestFilter(t *testing.T) { + conditions := []Expression{ + newFunction(ast.EQ, newColumn(0), newColumn(1)), + newFunction(ast.EQ, newColumn(1), newColumn(2)), + newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), + } + result := make([]Expression, 0, 5) + result = Filter(result, conditions, isLogicOrFunction) + require.Len(t, result, 1) +} + +func TestFilterOutInPlace(t *testing.T) { + conditions := []Expression{ + newFunction(ast.EQ, newColumn(0), newColumn(1)), + newFunction(ast.EQ, newColumn(1), newColumn(2)), + newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), + } + remained, filtered := FilterOutInPlace(conditions, isLogicOrFunction) + require.Equal(t, 2, len(remained)) + require.Equal(t, "eq", remained[0].(*ScalarFunction).FuncName.L) + require.Equal(t, "eq", remained[1].(*ScalarFunction).FuncName.L) + require.Equal(t, 1, len(filtered)) + require.Equal(t, "or", filtered[0].(*ScalarFunction).FuncName.L) +} + +func TestHashGroupKey(t *testing.T) { + ctx := mock.NewContext() + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + eTypes := []types.EvalType{types.ETInt, types.ETReal, types.ETDecimal, types.ETString, types.ETTimestamp, types.ETDatetime, types.ETDuration} + tNames := []string{"int", "real", "decimal", "string", "timestamp", "datetime", "duration"} + for i := 0; i < len(tNames); i++ { + ft := eType2FieldType(eTypes[i]) + if eTypes[i] == types.ETDecimal { + ft.SetFlen(0) + } + colExpr := &Column{Index: 0, RetType: ft} + input := chunk.New([]*types.FieldType{ft}, 1024, 1024) + fillColumnWithGener(eTypes[i], input, 0, nil) + colBuf := chunk.NewColumn(ft, 1024) + bufs := make([][]byte, 1024) + for j := 0; j < 1024; j++ { + bufs[j] = bufs[j][:0] + } + var err error + err = EvalExpr(ctx, colExpr, colExpr.GetType().EvalType(), input, colBuf) + require.NoError(t, err) + bufs, err = codec.HashGroupKey(sc, 1024, colBuf, bufs, ft) + require.NoError(t, err) + + var buf []byte + for j := 0; j < input.NumRows(); j++ { + d, err := colExpr.Eval(input.GetRow(j)) + require.NoError(t, err) + buf, err = codec.EncodeValue(sc, buf[:0], d) + require.NoError(t, err) + require.Equal(t, string(bufs[j]), string(buf)) + } + } +} + +func isLogicOrFunction(e Expression) bool { + if f, ok := e.(*ScalarFunction); ok { + return f.FuncName.L == ast.LogicOr + } + return false +} + +func TestDisableParseJSONFlag4Expr(t *testing.T) { + var expr Expression + expr = &Column{RetType: newIntFieldType()} + ft := expr.GetType() + ft.AddFlag(mysql.ParseToJSONFlag) + DisableParseJSONFlag4Expr(expr) + require.True(t, mysql.HasParseToJSONFlag(ft.GetFlag())) + + expr = &CorrelatedColumn{Column: Column{RetType: newIntFieldType()}} + ft = expr.GetType() + ft.AddFlag(mysql.ParseToJSONFlag) + DisableParseJSONFlag4Expr(expr) + require.True(t, mysql.HasParseToJSONFlag(ft.GetFlag())) + expr = &ScalarFunction{RetType: newIntFieldType()} + ft = expr.GetType() + ft.AddFlag(mysql.ParseToJSONFlag) + DisableParseJSONFlag4Expr(expr) + require.False(t, mysql.HasParseToJSONFlag(ft.GetFlag())) +} + +func TestSQLDigestTextRetriever(t *testing.T) { + // Create a fake session as the argument to the retriever, though it's actually not used when mock data is set. + + r := NewSQLDigestTextRetriever() + clearResult := func() { + r.SQLDigestsMap = map[string]string{ + "digest1": "", + "digest2": "", + "digest3": "", + "digest4": "", + "digest5": "", + } + } + clearResult() + r.mockLocalData = map[string]string{ + "digest1": "text1", + "digest2": "text2", + "digest6": "text6", + } + r.mockGlobalData = map[string]string{ + "digest2": "text2", + "digest3": "text3", + "digest4": "text4", + "digest7": "text7", + } + + expectedLocalResult := map[string]string{ + "digest1": "text1", + "digest2": "text2", + "digest3": "", + "digest4": "", + "digest5": "", + } + expectedGlobalResult := map[string]string{ + "digest1": "text1", + "digest2": "text2", + "digest3": "text3", + "digest4": "text4", + "digest5": "", + } + + err := r.RetrieveLocal(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, expectedLocalResult, r.SQLDigestsMap) + clearResult() + + err = r.RetrieveGlobal(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, expectedGlobalResult, r.SQLDigestsMap) + clearResult() + + r.fetchAllLimit = 1 + err = r.RetrieveLocal(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, expectedLocalResult, r.SQLDigestsMap) + clearResult() + + err = r.RetrieveGlobal(context.Background(), nil) + require.NoError(t, err) + require.Equal(t, expectedGlobalResult, r.SQLDigestsMap) +} + +func BenchmarkExtractColumns(b *testing.B) { + conditions := []Expression{ + newFunction(ast.EQ, newColumn(0), newColumn(1)), + newFunction(ast.EQ, newColumn(1), newColumn(2)), + newFunction(ast.EQ, newColumn(2), newColumn(3)), + newFunction(ast.EQ, newColumn(3), newLonglong(1)), + newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), + } + expr := ComposeCNFCondition(mock.NewContext(), conditions...) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ExtractColumns(expr) + } + b.ReportAllocs() +} + +func BenchmarkExprFromSchema(b *testing.B) { + conditions := []Expression{ + newFunction(ast.EQ, newColumn(0), newColumn(1)), + newFunction(ast.EQ, newColumn(1), newColumn(2)), + newFunction(ast.EQ, newColumn(2), newColumn(3)), + newFunction(ast.EQ, newColumn(3), newLonglong(1)), + newFunction(ast.LogicOr, newLonglong(1), newColumn(0)), + } + expr := ComposeCNFCondition(mock.NewContext(), conditions...) + schema := &Schema{Columns: ExtractColumns(expr)} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + ExprFromSchema(expr, schema) + } + b.ReportAllocs() +} + +// MockExpr is mainly for test. +type MockExpr struct { + err error + t *types.FieldType + i interface{} +} + +func (m *MockExpr) VecEvalInt(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalReal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalString(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalDecimal(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalTime(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalDuration(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} +func (m *MockExpr) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, result *chunk.Column) error { + return nil +} + +func (m *MockExpr) String() string { return "" } +func (m *MockExpr) MarshalJSON() ([]byte, error) { return nil, nil } +func (m *MockExpr) Eval(row chunk.Row) (types.Datum, error) { return types.NewDatum(m.i), m.err } +func (m *MockExpr) EvalInt(ctx sessionctx.Context, row chunk.Row) (val int64, isNull bool, err error) { + if x, ok := m.i.(int64); ok { + return x, false, m.err + } + return 0, m.i == nil, m.err +} +func (m *MockExpr) EvalReal(ctx sessionctx.Context, row chunk.Row) (val float64, isNull bool, err error) { + if x, ok := m.i.(float64); ok { + return x, false, m.err + } + return 0, m.i == nil, m.err +} +func (m *MockExpr) EvalString(ctx sessionctx.Context, row chunk.Row) (val string, isNull bool, err error) { + if x, ok := m.i.(string); ok { + return x, false, m.err + } + return "", m.i == nil, m.err +} +func (m *MockExpr) EvalDecimal(ctx sessionctx.Context, row chunk.Row) (val *types.MyDecimal, isNull bool, err error) { + if x, ok := m.i.(*types.MyDecimal); ok { + return x, false, m.err + } + return nil, m.i == nil, m.err +} +func (m *MockExpr) EvalTime(ctx sessionctx.Context, row chunk.Row) (val types.Time, isNull bool, err error) { + if x, ok := m.i.(types.Time); ok { + return x, false, m.err + } + return types.ZeroTime, m.i == nil, m.err +} +func (m *MockExpr) EvalDuration(ctx sessionctx.Context, row chunk.Row) (val types.Duration, isNull bool, err error) { + if x, ok := m.i.(types.Duration); ok { + return x, false, m.err + } + return types.Duration{}, m.i == nil, m.err +} +func (m *MockExpr) EvalJSON(ctx sessionctx.Context, row chunk.Row) (val types.BinaryJSON, isNull bool, err error) { + if x, ok := m.i.(types.BinaryJSON); ok { + return x, false, m.err + } + return types.BinaryJSON{}, m.i == nil, m.err +} +func (m *MockExpr) ReverseEval(sc *stmtctx.StatementContext, res types.Datum, rType types.RoundingType) (val types.Datum, err error) { + return types.Datum{}, m.err +} +func (m *MockExpr) GetType() *types.FieldType { return m.t } +func (m *MockExpr) Clone() Expression { return nil } +func (m *MockExpr) Equal(ctx sessionctx.Context, e Expression) bool { return false } +func (m *MockExpr) IsCorrelated() bool { return false } +func (m *MockExpr) ConstItem(_ *stmtctx.StatementContext) bool { return false } +func (m *MockExpr) Decorrelate(schema *Schema) Expression { return m } +func (m *MockExpr) ResolveIndices(schema *Schema) (Expression, error) { return m, nil } +func (m *MockExpr) resolveIndices(schema *Schema) error { return nil } +func (m *MockExpr) ResolveIndicesByVirtualExpr(schema *Schema) (Expression, bool) { return m, true } +func (m *MockExpr) resolveIndicesByVirtualExpr(schema *Schema) bool { return true } +func (m *MockExpr) RemapColumn(_ map[int64]*Column) (Expression, error) { return m, nil } +func (m *MockExpr) ExplainInfo() string { return "" } +func (m *MockExpr) ExplainNormalizedInfo() string { return "" } +func (m *MockExpr) HashCode(sc *stmtctx.StatementContext) []byte { return nil } +func (m *MockExpr) Vectorized() bool { return false } +func (m *MockExpr) SupportReverseEval() bool { return false } +func (m *MockExpr) HasCoercibility() bool { return false } +func (m *MockExpr) Coercibility() Coercibility { return 0 } +func (m *MockExpr) SetCoercibility(Coercibility) {} +func (m *MockExpr) Repertoire() Repertoire { return UNICODE } +func (m *MockExpr) SetRepertoire(Repertoire) {} + +func (m *MockExpr) CharsetAndCollation() (string, string) { + return "", "" +} +func (m *MockExpr) SetCharsetAndCollation(chs, coll string) {} + +func (m *MockExpr) MemoryUsage() (sum int64) { + return +} +func (m *MockExpr) Traverse(action TraverseAction) Expression { + return action.Transform(m) +} diff --git a/expression/vectorized.go b/pkg/expression/vectorized.go similarity index 96% rename from expression/vectorized.go rename to pkg/expression/vectorized.go index a6e7f04a84d44..7417af68d9d78 100644 --- a/expression/vectorized.go +++ b/pkg/expression/vectorized.go @@ -16,9 +16,9 @@ package expression import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) func genVecFromConstExpr(ctx sessionctx.Context, expr Expression, targetType types.EvalType, input *chunk.Chunk, result *chunk.Column) error { diff --git a/extension/.gitignore b/pkg/extension/.gitignore similarity index 100% rename from extension/.gitignore rename to pkg/extension/.gitignore diff --git a/pkg/extension/BUILD.bazel b/pkg/extension/BUILD.bazel new file mode 100644 index 0000000000000..04ded0065fb35 --- /dev/null +++ b/pkg/extension/BUILD.bazel @@ -0,0 +1,63 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "extension", + srcs = [ + "extensions.go", + "function.go", + "manifest.go", + "registry.go", + "session.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/extension", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/chunk", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@io_etcd_go_etcd_client_v3//:client", + ], +) + +go_test( + name = "extension_test", + timeout = "short", + srcs = [ + "bootstrap_test.go", + "event_listener_test.go", + "function_test.go", + "main_test.go", + "registry_test.go", + ], + embed = [":extension"], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/expression", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/privilege/privileges", + "//pkg/server", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/sem", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/extension/_import/BUILD.bazel b/pkg/extension/_import/BUILD.bazel new file mode 100644 index 0000000000000..35a194b2e5aca --- /dev/null +++ b/pkg/extension/_import/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "_import", + srcs = ["import.go"], + importpath = "github.com/pingcap/tidb/pkg/extension/_import", + visibility = ["//visibility:public"], +) diff --git a/extension/_import/import.go b/pkg/extension/_import/import.go similarity index 100% rename from extension/_import/import.go rename to pkg/extension/_import/import.go diff --git a/pkg/extension/bootstrap_test.go b/pkg/extension/bootstrap_test.go new file mode 100644 index 0000000000000..29b036a355112 --- /dev/null +++ b/pkg/extension/bootstrap_test.go @@ -0,0 +1,48 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package extension_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestBootstrap(t *testing.T) { + defer func() { + extension.Reset() + }() + + extension.Reset() + require.NoError(t, extension.Register("test1", extension.WithBootstrapSQL("create table test.t1 (a int)"))) + require.NoError(t, extension.Register("test2", extension.WithBootstrap(func(ctx extension.BootstrapContext) error { + _, err := ctx.ExecuteSQL(ctx, "insert into test.t1 values(1)") + require.NoError(t, err) + + rows, err := ctx.ExecuteSQL(ctx, "select * from test.t1 where a=1") + require.NoError(t, err) + + require.Equal(t, 1, len(rows)) + require.Equal(t, int64(1), rows[0].GetInt64(0)) + return nil + }))) + require.NoError(t, extension.Setup()) + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("select * from test.t1").Check(testkit.Rows("1")) +} diff --git a/pkg/extension/enterprise b/pkg/extension/enterprise new file mode 160000 index 0000000000000..eac31cedd37f7 --- /dev/null +++ b/pkg/extension/enterprise @@ -0,0 +1 @@ +Subproject commit eac31cedd37f7143483f4b387c38fc2e8638b379 diff --git a/extension/event_listener_test.go b/pkg/extension/event_listener_test.go similarity index 96% rename from extension/event_listener_test.go rename to pkg/extension/event_listener_test.go index 73e9955b04ce7..802d9e8b922f0 100644 --- a/extension/event_listener_test.go +++ b/pkg/extension/event_listener_test.go @@ -22,17 +22,17 @@ import ( "testing" "time" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/extension/extensionimpl/BUILD.bazel b/pkg/extension/extensionimpl/BUILD.bazel new file mode 100644 index 0000000000000..aa1ab3a9c08c1 --- /dev/null +++ b/pkg/extension/extensionimpl/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "extensionimpl", + srcs = ["bootstrap.go"], + importpath = "github.com/pingcap/tidb/pkg/extension/extensionimpl", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/extension", + "//pkg/kv", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@io_etcd_go_etcd_client_v3//:client", + ], +) diff --git a/pkg/extension/extensionimpl/bootstrap.go b/pkg/extension/extensionimpl/bootstrap.go new file mode 100644 index 0000000000000..ff1a614892ba2 --- /dev/null +++ b/pkg/extension/extensionimpl/bootstrap.go @@ -0,0 +1,95 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package extensionimpl + +import ( + "context" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" + clientv3 "go.etcd.io/etcd/client/v3" +) + +type bootstrapContext struct { + context.Context + + sqlExecutor sqlexec.SQLExecutor + etcdCli *clientv3.Client + sessionPool extension.SessionPool +} + +func (c *bootstrapContext) ExecuteSQL(ctx context.Context, sql string) (rows []chunk.Row, err error) { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) + rs, err := c.sqlExecutor.ExecuteInternal(ctx, sql) + if err != nil { + return nil, err + } + + if rs == nil { + return nil, nil + } + + defer func() { + closeErr := rs.Close() + if err == nil { + err = closeErr + } + }() + + return sqlexec.DrainRecordSet(ctx, rs, 8) +} + +func (c *bootstrapContext) EtcdClient() *clientv3.Client { + return c.etcdCli +} + +func (c *bootstrapContext) SessionPool() extension.SessionPool { + return c.sessionPool +} + +// Bootstrap bootstraps all extensions +func Bootstrap(ctx context.Context, do *domain.Domain) error { + extensions, err := extension.GetExtensions() + if err != nil { + return err + } + + if extensions == nil { + return nil + } + + pool := do.SysSessionPool() + sctx, err := pool.Get() + if err != nil { + return err + } + defer pool.Put(sctx) + + executor, ok := sctx.(sqlexec.SQLExecutor) + if !ok { + return errors.Errorf("type '%T' cannot be casted to 'sqlexec.SQLExecutor'", sctx) + } + + return extensions.Bootstrap(&bootstrapContext{ + Context: ctx, + sessionPool: pool, + sqlExecutor: executor, + etcdCli: do.GetEtcdClient(), + }) +} diff --git a/extension/extensions.go b/pkg/extension/extensions.go similarity index 100% rename from extension/extensions.go rename to pkg/extension/extensions.go diff --git a/extension/function.go b/pkg/extension/function.go similarity index 92% rename from extension/function.go rename to pkg/extension/function.go index cb7c19de0a507..5476977134166 100644 --- a/extension/function.go +++ b/pkg/extension/function.go @@ -18,10 +18,10 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // FunctionContext is an interface to provide context to the custom function diff --git a/extension/function_test.go b/pkg/extension/function_test.go similarity index 98% rename from extension/function_test.go rename to pkg/extension/function_test.go index 06fa301543525..41c47095865d5 100644 --- a/extension/function_test.go +++ b/pkg/extension/function_test.go @@ -21,14 +21,14 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sem" "github.com/stretchr/testify/require" ) diff --git a/pkg/extension/main_test.go b/pkg/extension/main_test.go new file mode 100644 index 0000000000000..f613a3a66f26a --- /dev/null +++ b/pkg/extension/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package extension + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/extension/manifest.go b/pkg/extension/manifest.go similarity index 97% rename from extension/manifest.go rename to pkg/extension/manifest.go index 436067c11d704..dc9fd29e9c2e3 100644 --- a/extension/manifest.go +++ b/pkg/extension/manifest.go @@ -19,9 +19,9 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/extension/registry.go b/pkg/extension/registry.go similarity index 100% rename from extension/registry.go rename to pkg/extension/registry.go diff --git a/pkg/extension/registry_test.go b/pkg/extension/registry_test.go new file mode 100644 index 0000000000000..5a60f5c5afd81 --- /dev/null +++ b/pkg/extension/registry_test.go @@ -0,0 +1,349 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package extension_test + +import ( + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/stretchr/testify/require" +) + +func TestSetupExtensions(t *testing.T) { + defer func() { + extension.Reset() + }() + + extension.Reset() + require.NoError(t, extension.Setup()) + extensions, err := extension.GetExtensions() + require.NoError(t, err) + require.Equal(t, 0, len(extensions.Manifests())) + + extension.Reset() + require.NoError(t, extension.Register("test1")) + require.NoError(t, extension.Register("test2")) + require.NoError(t, extension.Setup()) + extensions, err = extension.GetExtensions() + require.NoError(t, err) + require.Equal(t, 2, len(extensions.Manifests())) + require.Equal(t, "test1", extensions.Manifests()[0].Name()) + require.Equal(t, "test2", extensions.Manifests()[1].Name()) +} + +func TestExtensionRegisterName(t *testing.T) { + defer extension.Reset() + + // test empty name + extension.Reset() + require.EqualError(t, extension.Register(""), "extension name should not be empty") + + // test dup name + extension.Reset() + require.NoError(t, extension.Register("test")) + require.EqualError(t, extension.Register("test"), "extension with name 'test' already registered") +} + +func TestRegisterExtensionWithClose(t *testing.T) { + defer extension.Reset() + + // normal register + extension.Reset() + cnt := 0 + require.NoError(t, extension.Register("test1", extension.WithClose(func() { + cnt++ + }))) + require.NoError(t, extension.Setup()) + require.Equal(t, 0, cnt) + + // reset will call close + extension.Reset() + require.Equal(t, 1, cnt) + + // reset again has no effect + extension.Reset() + require.Equal(t, 1, cnt) + + // Auto close when error + cnt = 0 + extension.Reset() + require.NoError(t, extension.Register("test1", extension.WithClose(func() { + cnt++ + }))) + require.NoError(t, extension.RegisterFactory("test2", func() ([]extension.Option, error) { + return nil, errors.New("error abc") + })) + require.EqualError(t, extension.Setup(), "error abc") + require.Equal(t, 1, cnt) +} + +func TestRegisterExtensionWithDyncPrivs(t *testing.T) { + defer extension.Reset() + + origDynPrivs := privileges.GetDynamicPrivileges() + origDynPrivs = append([]string{}, origDynPrivs...) + + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", "priv2"}))) + require.NoError(t, extension.Setup()) + privs := privileges.GetDynamicPrivileges() + require.Equal(t, origDynPrivs, privs[:len(origDynPrivs)]) + require.Equal(t, []string{"PRIV1", "PRIV2"}, privs[len(origDynPrivs):]) + + // test for empty dynamic privilege name + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", ""}))) + require.EqualError(t, extension.Setup(), "privilege name should not be empty") + require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) + + // test for duplicate name with builtin + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomDynPrivs([]string{"priv1", "ROLE_ADMIN"}))) + require.EqualError(t, extension.Setup(), "privilege is already registered") + require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) + + // test for duplicate name with other extension + extension.Reset() + require.NoError(t, extension.Register("test1", extension.WithCustomDynPrivs([]string{"priv1"}))) + require.NoError(t, extension.Register("test2", extension.WithCustomDynPrivs([]string{"priv2", "priv1"}))) + require.EqualError(t, extension.Setup(), "privilege is already registered") + require.Equal(t, origDynPrivs, privileges.GetDynamicPrivileges()) +} + +func TestRegisterExtensionWithSysVars(t *testing.T) { + defer extension.Reset() + + sysVar1 := &variable.SysVar{ + Scope: variable.ScopeGlobal | variable.ScopeSession, + Name: "var1", + Value: variable.On, + Type: variable.TypeBool, + } + + sysVar2 := &variable.SysVar{ + Scope: variable.ScopeSession, + Name: "var2", + Value: "val2", + Type: variable.TypeStr, + } + + // normal register + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1, sysVar2}))) + require.NoError(t, extension.Setup()) + require.Same(t, sysVar1, variable.GetSysVar("var1")) + require.Same(t, sysVar2, variable.GetSysVar("var2")) + + // test for empty name + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{ + {Scope: variable.ScopeGlobal, Name: "", Value: "val3"}, + }))) + require.EqualError(t, extension.Setup(), "system var name should not be empty") + require.Nil(t, variable.GetSysVar("")) + + // test for duplicate name with builtin + extension.Reset() + require.NoError(t, extension.Register("test", extension.WithCustomSysVariables([]*variable.SysVar{ + sysVar1, + {Scope: variable.ScopeGlobal, Name: variable.TiDBSnapshot, Value: "val3"}, + }))) + require.EqualError(t, extension.Setup(), "system var 'tidb_snapshot' has already registered") + require.Nil(t, variable.GetSysVar("var1")) + require.Equal(t, "", variable.GetSysVar(variable.TiDBSnapshot).Value) + require.Equal(t, variable.ScopeSession, variable.GetSysVar(variable.TiDBSnapshot).Scope) + + // test for duplicate name with other extension + extension.Reset() + require.NoError(t, extension.Register("test1", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1, sysVar2}))) + require.NoError(t, extension.Register("test2", extension.WithCustomSysVariables([]*variable.SysVar{sysVar1}))) + require.EqualError(t, extension.Setup(), "system var 'var1' has already registered") + require.Nil(t, variable.GetSysVar("var1")) + require.Nil(t, variable.GetSysVar("var2")) +} + +func TestSetVariablePrivilege(t *testing.T) { + defer extension.Reset() + + sysVar1 := &variable.SysVar{ + Scope: variable.ScopeGlobal | variable.ScopeSession, + Name: "var1", + Value: "1", + MinValue: 0, + MaxValue: 100, + Type: variable.TypeInt, + RequireDynamicPrivileges: func(isGlobal bool, sem bool) []string { + privs := []string{"priv1"} + if isGlobal { + privs = append(privs, "priv2") + } + + if sem { + privs = append(privs, "restricted_priv3") + } + + return privs + }, + } + + extension.Reset() + require.NoError(t, extension.Register( + "test", + extension.WithCustomSysVariables([]*variable.SysVar{sysVar1}), + extension.WithCustomDynPrivs([]string{"priv1", "priv2", "restricted_priv3"}), + )) + require.NoError(t, extension.Setup()) + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create user u2@localhost") + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) + + sem.Disable() + tk1.MustExec("set @@var1=7") + tk1.MustQuery("select @@var1").Check(testkit.Rows("7")) + + require.EqualError(t, tk2.ExecToErr("set @@var1=10"), "[planner:1227]Access denied; you need (at least one of) the SUPER or priv1 privilege(s) for this operation") + tk2.MustQuery("select @@var1").Check(testkit.Rows("1")) + + tk.MustExec("GRANT priv1 on *.* TO u2@localhost") + tk2.MustExec("set @@var1=8") + tk2.MustQuery("select @@var1").Check(testkit.Rows("8")) + + tk1.MustExec("set @@global.var1=17") + tk1.MustQuery("select @@global.var1").Check(testkit.Rows("17")) + + tk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN on *.* TO u2@localhost") + require.EqualError(t, tk2.ExecToErr("set @@global.var1=18"), "[planner:1227]Access denied; you need (at least one of) the SUPER or priv2 privilege(s) for this operation") + tk2.MustQuery("select @@global.var1").Check(testkit.Rows("17")) + + tk.MustExec("GRANT priv2 on *.* TO u2@localhost") + tk2.MustExec("set @@global.var1=18") + tk2.MustQuery("select @@global.var1").Check(testkit.Rows("18")) + + sem.Enable() + defer sem.Disable() + + require.EqualError(t, tk1.ExecToErr("set @@global.var1=27"), "[planner:1227]Access denied; you need (at least one of) the restricted_priv3 privilege(s) for this operation") + tk1.MustQuery("select @@global.var1").Check(testkit.Rows("18")) + + require.EqualError(t, tk2.ExecToErr("set @@global.var1=27"), "[planner:1227]Access denied; you need (at least one of) the restricted_priv3 privilege(s) for this operation") + tk2.MustQuery("select @@global.var1").Check(testkit.Rows("18")) + + tk.MustExec("GRANT restricted_priv3 on *.* TO u2@localhost") + tk2.MustExec("set @@global.var1=28") + tk2.MustQuery("select @@global.var1").Check(testkit.Rows("28")) +} + +func TestCustomAccessCheck(t *testing.T) { + defer extension.Reset() + extension.Reset() + + require.NoError(t, extension.Register( + "test", + extension.WithCustomDynPrivs([]string{"priv1", "priv2", "restricted_priv3"}), + extension.WithCustomAccessCheck(func(db, tbl, column string, priv mysql.PrivilegeType, sem bool) []string { + if db != "test" || tbl != "t1" { + return nil + } + + var privs []string + if priv == mysql.SelectPriv { + privs = append(privs, "priv1") + } else if priv == mysql.UpdatePriv { + privs = append(privs, "priv2") + if sem { + privs = append(privs, "restricted_priv3") + } + } else { + return nil + } + + return privs + }), + )) + require.NoError(t, extension.Setup()) + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create user u2@localhost") + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk1.MustExec("use test") + + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("GRANT all on test.t1 TO u2@localhost") + tk2.MustExec("use test") + + tk1.MustExec("create table t1(id int primary key, v int)") + tk1.MustExec("insert into t1 values (1, 10), (2, 20)") + + tk1.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) + tk1.MustQuery("select * from t1").Check(testkit.Rows("1 10", "2 20")) + + require.EqualError(t, tk2.ExecToErr("select * from t1 where id=1"), "[planner:1142]SELECT command denied to user 'u2'@'localhost' for table 't1'") + require.EqualError(t, tk2.ExecToErr("select * from t1"), "[planner:1142]SELECT command denied to user 'u2'@'localhost' for table 't1'") + + tk.MustExec("GRANT priv1 on *.* TO u2@localhost") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) + tk2.MustQuery("select * from t1").Check(testkit.Rows("1 10", "2 20")) + + require.EqualError(t, tk2.ExecToErr("update t1 set v=11 where id=1"), "[planner:8121]privilege check for 'Update' fail") + require.EqualError(t, tk2.ExecToErr("update t1 set v=11 where id<2"), "[planner:8121]privilege check for 'Update' fail") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 10")) + + tk.MustExec("GRANT priv2 on *.* TO u2@localhost") + tk2.MustExec("update t1 set v=11 where id=1") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 11")) + + tk2.MustExec("update t1 set v=12 where id<2") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) + + sem.Enable() + defer sem.Disable() + + require.EqualError(t, tk1.ExecToErr("update t1 set v=21 where id=1"), "[planner:8121]privilege check for 'Update' fail") + require.EqualError(t, tk1.ExecToErr("update t1 set v=21 where id<2"), "[planner:8121]privilege check for 'Update' fail") + tk1.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) + + require.EqualError(t, tk2.ExecToErr("update t1 set v=21 where id=1"), "[planner:8121]privilege check for 'Update' fail") + require.EqualError(t, tk2.ExecToErr("update t1 set v=21 where id<2"), "[planner:8121]privilege check for 'Update' fail") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 12")) + + tk.MustExec("GRANT restricted_priv3 on *.* TO u2@localhost") + tk2.MustExec("update t1 set v=31 where id=1") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 31")) + + tk2.MustExec("update t1 set v=32 where id<2") + tk2.MustQuery("select * from t1 where id=1").Check(testkit.Rows("1 32")) +} diff --git a/pkg/extension/session.go b/pkg/extension/session.go new file mode 100644 index 0000000000000..e12a43bb66169 --- /dev/null +++ b/pkg/extension/session.go @@ -0,0 +1,150 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package extension + +import ( + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" +) + +// ConnEventInfo is the connection info for the event +type ConnEventInfo struct { + *variable.ConnectionInfo + SessionAlias string + ActiveRoles []*auth.RoleIdentity + Error error +} + +// ConnEventTp is the type of the connection event +type ConnEventTp uint8 + +const ( + // ConnConnected means connection connected, but not handshake yet + ConnConnected ConnEventTp = iota + // ConnHandshakeAccepted means connection is accepted after handshake + ConnHandshakeAccepted + // ConnHandshakeRejected means connections is rejected after handshake + ConnHandshakeRejected + // ConnReset means the connection is reset + ConnReset + // ConnDisconnected means the connection is disconnected + ConnDisconnected +) + +// StmtEventTp is the type of the statement event +type StmtEventTp uint8 + +const ( + // StmtError means the stmt is failed + StmtError StmtEventTp = iota + // StmtSuccess means the stmt is successfully executed + StmtSuccess +) + +// StmtEventInfo is the information of stmt event +type StmtEventInfo interface { + // User returns the user of the session + User() *auth.UserIdentity + // ActiveRoles returns the active roles of the user + ActiveRoles() []*auth.RoleIdentity + // CurrentDB returns the current database + CurrentDB() string + // ConnectionInfo returns the connection info of the current session + ConnectionInfo() *variable.ConnectionInfo + // SessionAlias returns the session alias value set by user + SessionAlias() string + // StmtNode returns the parsed ast of the statement + // When parse error, this method will return a nil value + StmtNode() ast.StmtNode + // ExecuteStmtNode will return the `ast.ExecuteStmt` node when the current statement is EXECUTE, + // otherwise a nil value will be returned + ExecuteStmtNode() *ast.ExecuteStmt + // ExecutePreparedStmt will return the prepared stmt node for the EXECUTE statement. + // If the current statement is not EXECUTE or prepared statement is not found, a nil value will be returned + ExecutePreparedStmt() ast.StmtNode + // PreparedParams will return the params for the EXECUTE statement + PreparedParams() []types.Datum + // OriginalText will return the text of the statement. + // Notice that for the EXECUTE statement, the prepared statement text will be used as the return value + OriginalText() string + // SQLDigest will return the normalized and redact text of the `OriginalText()` + SQLDigest() (normalized string, digest *parser.Digest) + // AffectedRows will return the affected rows of the current statement + AffectedRows() uint64 + // RelatedTables will return the related tables of the current statement + RelatedTables() []stmtctx.TableEntry + // GetError will return the error when the current statement is failed + GetError() error +} + +// SessionHandler is used to listen session events +type SessionHandler struct { + OnConnectionEvent func(ConnEventTp, *ConnEventInfo) + OnStmtEvent func(StmtEventTp, StmtEventInfo) +} + +func newSessionExtensions(es *Extensions) *SessionExtensions { + connExtensions := &SessionExtensions{} + for _, m := range es.Manifests() { + if m.sessionHandlerFactory != nil { + if handler := m.sessionHandlerFactory(); handler != nil { + if fn := handler.OnConnectionEvent; fn != nil { + connExtensions.connectionEventFuncs = append(connExtensions.connectionEventFuncs, fn) + } + if fn := handler.OnStmtEvent; fn != nil { + connExtensions.stmtEventFuncs = append(connExtensions.stmtEventFuncs, fn) + } + } + } + } + return connExtensions +} + +// SessionExtensions is the extensions +type SessionExtensions struct { + connectionEventFuncs []func(ConnEventTp, *ConnEventInfo) + stmtEventFuncs []func(StmtEventTp, StmtEventInfo) +} + +// OnConnectionEvent will be called when a connection event happens +func (es *SessionExtensions) OnConnectionEvent(tp ConnEventTp, event *ConnEventInfo) { + if es == nil { + return + } + + for _, fn := range es.connectionEventFuncs { + fn(tp, event) + } +} + +// HasStmtEventListeners returns a bool that indicates if any stmt event listener exists +func (es *SessionExtensions) HasStmtEventListeners() bool { + return es != nil && len(es.stmtEventFuncs) > 0 +} + +// OnStmtEvent will be called when a stmt event happens +func (es *SessionExtensions) OnStmtEvent(tp StmtEventTp, event StmtEventInfo) { + if es == nil { + return + } + + for _, fn := range es.stmtEventFuncs { + fn(tp, event) + } +} diff --git a/extension/util.go b/pkg/extension/util.go similarity index 100% rename from extension/util.go rename to pkg/extension/util.go diff --git a/pkg/infoschema/BUILD.bazel b/pkg/infoschema/BUILD.bazel new file mode 100644 index 0000000000000..a1984931d77b8 --- /dev/null +++ b/pkg/infoschema/BUILD.bazel @@ -0,0 +1,99 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "infoschema", + srcs = [ + "builder.go", + "cache.go", + "cluster.go", + "error.go", + "infoschema.go", + "metric_table_def.go", + "metrics_schema.go", + "tables.go", + ], + importpath = "github.com/pingcap/tidb/pkg/infoschema", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl/placement", + "//pkg/ddl/resourcegroup", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/infoschema/metrics", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/privilege", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/types", + "//pkg/util", + "//pkg/util/dbterror", + "//pkg/util/deadlockhistory", + "//pkg/util/domainutil", + "//pkg/util/execdetails", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/mock", + "//pkg/util/pdapi", + "//pkg/util/sem", + "//pkg/util/set", + "//pkg/util/sqlexec", + "//pkg/util/stmtsummary", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//tikv", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "infoschema_test", + timeout = "short", + srcs = [ + "infoschema_test.go", + "main_test.go", + "metrics_schema_test.go", + ], + embed = [":infoschema"], + flaky = True, + shard_count = 8, + deps = [ + "//pkg/ddl/placement", + "//pkg/domain", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util", + "//pkg/util/set", + "@com_github_pingcap_errors//:errors", + "@com_github_prometheus_prometheus//promql", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/infoschema/builder.go b/pkg/infoschema/builder.go new file mode 100644 index 0000000000000..3b030b5b1ad25 --- /dev/null +++ b/pkg/infoschema/builder.go @@ -0,0 +1,1166 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "cmp" + "context" + "fmt" + "slices" + "strings" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/util/domainutil" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +type policyGetter struct { + is *infoSchema +} + +func (p *policyGetter) GetPolicy(policyID int64) (*model.PolicyInfo, error) { + if policy, ok := p.is.PolicyByID(policyID); ok { + return policy, nil + } + return nil, errors.Errorf("Cannot find placement policy with ID: %d", policyID) +} + +type bundleInfoBuilder struct { + deltaUpdate bool + // tables or partitions that need to update placement bundle + updateTables map[int64]interface{} + // all tables or partitions referring these policies should update placement bundle + updatePolicies map[int64]interface{} + // partitions that need to update placement bundle + updatePartitions map[int64]interface{} +} + +func (b *bundleInfoBuilder) ensureMap() { + if b.updateTables == nil { + b.updateTables = make(map[int64]interface{}) + } + if b.updatePartitions == nil { + b.updatePartitions = make(map[int64]interface{}) + } + if b.updatePolicies == nil { + b.updatePolicies = make(map[int64]interface{}) + } +} + +func (b *bundleInfoBuilder) SetDeltaUpdateBundles() { + b.deltaUpdate = true +} + +func (b *bundleInfoBuilder) deleteBundle(is *infoSchema, tblID int64) { + delete(is.ruleBundleMap, tblID) +} + +func (b *bundleInfoBuilder) markTableBundleShouldUpdate(tblID int64) { + b.ensureMap() + b.updateTables[tblID] = struct{}{} +} + +func (b *bundleInfoBuilder) markPartitionBundleShouldUpdate(partID int64) { + b.ensureMap() + b.updatePartitions[partID] = struct{}{} +} + +func (b *bundleInfoBuilder) markBundlesReferPolicyShouldUpdate(policyID int64) { + b.ensureMap() + b.updatePolicies[policyID] = struct{}{} +} + +func (b *bundleInfoBuilder) updateInfoSchemaBundles(is *infoSchema) { + if b.deltaUpdate { + b.completeUpdateTables(is) + for tblID := range b.updateTables { + b.updateTableBundles(is, tblID) + } + return + } + + // do full update bundles + is.ruleBundleMap = make(map[int64]*placement.Bundle) + for _, tbls := range is.schemaMap { + for _, tbl := range tbls.tables { + b.updateTableBundles(is, tbl.Meta().ID) + } + } +} + +func (b *bundleInfoBuilder) completeUpdateTables(is *infoSchema) { + if len(b.updatePolicies) == 0 && len(b.updatePartitions) == 0 { + return + } + + for _, tbls := range is.schemaMap { + for _, tbl := range tbls.tables { + tblInfo := tbl.Meta() + if tblInfo.PlacementPolicyRef != nil { + if _, ok := b.updatePolicies[tblInfo.PlacementPolicyRef.ID]; ok { + b.markTableBundleShouldUpdate(tblInfo.ID) + } + } + + if tblInfo.Partition != nil { + for _, par := range tblInfo.Partition.Definitions { + if _, ok := b.updatePartitions[par.ID]; ok { + b.markTableBundleShouldUpdate(tblInfo.ID) + } + } + } + } + } +} + +func (b *bundleInfoBuilder) updateTableBundles(is *infoSchema, tableID int64) { + tbl, ok := is.TableByID(tableID) + if !ok { + b.deleteBundle(is, tableID) + return + } + + getter := &policyGetter{is: is} + bundle, err := placement.NewTableBundle(getter, tbl.Meta()) + if err != nil { + logutil.BgLogger().Error("create table bundle failed", zap.Error(err)) + } else if bundle != nil { + is.ruleBundleMap[tableID] = bundle + } else { + b.deleteBundle(is, tableID) + } + + if tbl.Meta().Partition == nil { + return + } + + for _, par := range tbl.Meta().Partition.Definitions { + bundle, err = placement.NewPartitionBundle(getter, par) + if err != nil { + logutil.BgLogger().Error("create partition bundle failed", + zap.Error(err), + zap.Int64("partition id", par.ID), + ) + } else if bundle != nil { + is.ruleBundleMap[par.ID] = bundle + } else { + b.deleteBundle(is, par.ID) + } + } +} + +// Builder builds a new InfoSchema. +type Builder struct { + is *infoSchema + // dbInfos do not need to be copied everytime applying a diff, instead, + // they can be copied only once over the whole lifespan of Builder. + // This map will indicate which DB has been copied, so that they + // don't need to be copied again. + dirtyDB map[string]bool + // TODO: store is only used by autoid allocators + // detach allocators from storage, use passed transaction in the feature + store kv.Storage + + factory func() (pools.Resource, error) + bundleInfoBuilder +} + +// ApplyDiff applies SchemaDiff to the new InfoSchema. +// Return the detail updated table IDs that are produced from SchemaDiff and an error. +func (b *Builder) ApplyDiff(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + b.is.schemaMetaVersion = diff.Version + switch diff.Type { + case model.ActionCreateSchema: + return nil, b.applyCreateSchema(m, diff) + case model.ActionDropSchema: + return b.applyDropSchema(diff.SchemaID), nil + case model.ActionRecoverSchema: + return b.applyRecoverSchema(m, diff) + case model.ActionModifySchemaCharsetAndCollate: + return nil, b.applyModifySchemaCharsetAndCollate(m, diff) + case model.ActionModifySchemaDefaultPlacement: + return nil, b.applyModifySchemaDefaultPlacement(m, diff) + case model.ActionCreatePlacementPolicy: + return nil, b.applyCreatePolicy(m, diff) + case model.ActionDropPlacementPolicy: + return b.applyDropPolicy(diff.SchemaID), nil + case model.ActionAlterPlacementPolicy: + return b.applyAlterPolicy(m, diff) + case model.ActionCreateResourceGroup: + return nil, b.applyCreateOrAlterResourceGroup(m, diff) + case model.ActionAlterResourceGroup: + return nil, b.applyCreateOrAlterResourceGroup(m, diff) + case model.ActionDropResourceGroup: + return b.applyDropResourceGroup(m, diff), nil + case model.ActionTruncateTablePartition, model.ActionTruncateTable: + return b.applyTruncateTableOrPartition(m, diff) + case model.ActionDropTable, model.ActionDropTablePartition: + return b.applyDropTableOrPartition(m, diff) + case model.ActionRecoverTable: + return b.applyRecoverTable(m, diff) + case model.ActionCreateTables: + return b.applyCreateTables(m, diff) + case model.ActionReorganizePartition, model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning: + return b.applyReorganizePartition(m, diff) + case model.ActionExchangeTablePartition: + return b.applyExchangeTablePartition(m, diff) + case model.ActionFlashbackCluster: + return []int64{-1}, nil + default: + return b.applyDefaultAction(m, diff) + } +} + +func (b *Builder) applyCreateTables(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs := make([]int64, 0, len(diff.AffectedOpts)) + if diff.AffectedOpts != nil { + for _, opt := range diff.AffectedOpts { + affectedDiff := &model.SchemaDiff{ + Version: diff.Version, + Type: model.ActionCreateTable, + SchemaID: opt.SchemaID, + TableID: opt.TableID, + OldSchemaID: opt.OldSchemaID, + OldTableID: opt.OldTableID, + } + affectedIDs, err := b.ApplyDiff(m, affectedDiff) + if err != nil { + return nil, errors.Trace(err) + } + tblIDs = append(tblIDs, affectedIDs...) + } + } + return tblIDs, nil +} + +func (b *Builder) applyTruncateTableOrPartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + + if diff.Type == model.ActionTruncateTable { + b.deleteBundle(b.is, diff.OldTableID) + b.markTableBundleShouldUpdate(diff.TableID) + } + + for _, opt := range diff.AffectedOpts { + if diff.Type == model.ActionTruncateTablePartition { + // Reduce the impact on DML when executing partition DDL. eg. + // While session 1 performs the DML operation associated with partition 1, + // the TRUNCATE operation of session 2 on partition 2 does not cause the operation of session 1 to fail. + tblIDs = append(tblIDs, opt.OldTableID) + b.markPartitionBundleShouldUpdate(opt.TableID) + } + b.deleteBundle(b.is, opt.OldTableID) + } + return tblIDs, nil +} + +func (b *Builder) applyDropTableOrPartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + + b.markTableBundleShouldUpdate(diff.TableID) + for _, opt := range diff.AffectedOpts { + b.deleteBundle(b.is, opt.OldTableID) + } + return tblIDs, nil +} + +func (b *Builder) applyReorganizePartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + for _, opt := range diff.AffectedOpts { + if opt.OldTableID != 0 { + b.deleteBundle(b.is, opt.OldTableID) + } + if opt.TableID != 0 { + b.markTableBundleShouldUpdate(opt.TableID) + } + // TODO: Should we also check markPartitionBundleShouldUpdate?!? + } + return tblIDs, nil +} + +func (b *Builder) applyExchangeTablePartition(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + // It is not in StatePublic. + if diff.OldTableID == diff.TableID && diff.OldSchemaID == diff.SchemaID { + ntIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + if diff.AffectedOpts == nil || diff.AffectedOpts[0].OldSchemaID == 0 { + return ntIDs, err + } + // Reload parition tabe. + ptSchemaID := diff.AffectedOpts[0].OldSchemaID + ptID := diff.AffectedOpts[0].TableID + ptDiff := &model.SchemaDiff{ + Type: diff.Type, + Version: diff.Version, + TableID: ptID, + SchemaID: ptSchemaID, + OldTableID: ptID, + OldSchemaID: ptSchemaID, + } + ptIDs, err := b.applyTableUpdate(m, ptDiff) + if err != nil { + return nil, errors.Trace(err) + } + return append(ptIDs, ntIDs...), nil + } + ntSchemaID := diff.OldSchemaID + ntID := diff.OldTableID + ptSchemaID := diff.SchemaID + ptID := diff.TableID + partID := diff.TableID + if len(diff.AffectedOpts) > 0 { + ptID = diff.AffectedOpts[0].TableID + if diff.AffectedOpts[0].SchemaID != 0 { + ptSchemaID = diff.AffectedOpts[0].SchemaID + } + } + // The normal table needs to be updated first: + // Just update the tables separately + currDiff := &model.SchemaDiff{ + // This is only for the case since https://github.com/pingcap/tidb/pull/45877 + // Fixed now, by adding back the AffectedOpts + // to carry the partitioned Table ID. + Type: diff.Type, + Version: diff.Version, + TableID: ntID, + SchemaID: ntSchemaID, + } + if ptID != partID { + currDiff.TableID = partID + currDiff.OldTableID = ntID + currDiff.OldSchemaID = ntSchemaID + } + ntIDs, err := b.applyTableUpdate(m, currDiff) + if err != nil { + return nil, errors.Trace(err) + } + // partID is the new id for the non-partitioned table! + b.markTableBundleShouldUpdate(partID) + // Then the partitioned table, will re-read the whole table, including all partitions! + currDiff.TableID = ptID + currDiff.SchemaID = ptSchemaID + currDiff.OldTableID = ptID + currDiff.OldSchemaID = ptSchemaID + ptIDs, err := b.applyTableUpdate(m, currDiff) + if err != nil { + return nil, errors.Trace(err) + } + // ntID is the new id for the partition! + b.markPartitionBundleShouldUpdate(ntID) + err = updateAutoIDForExchangePartition(b.store, ptSchemaID, ptID, ntSchemaID, ntID) + if err != nil { + return nil, errors.Trace(err) + } + return append(ptIDs, ntIDs...), nil +} + +func (b *Builder) applyRecoverTable(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + + for _, opt := range diff.AffectedOpts { + b.markTableBundleShouldUpdate(opt.TableID) + } + return tblIDs, nil +} + +func updateAutoIDForExchangePartition(store kv.Storage, ptSchemaID, ptID, ntSchemaID, ntID int64) error { + err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + ptAutoIDs, err := t.GetAutoIDAccessors(ptSchemaID, ptID).Get() + if err != nil { + return err + } + + // non-partition table auto IDs. + ntAutoIDs, err := t.GetAutoIDAccessors(ntSchemaID, ntID).Get() + if err != nil { + return err + } + + // Set both tables to the maximum auto IDs between normal table and partitioned table. + newAutoIDs := meta.AutoIDGroup{ + RowID: mathutil.Max(ptAutoIDs.RowID, ntAutoIDs.RowID), + IncrementID: mathutil.Max(ptAutoIDs.IncrementID, ntAutoIDs.IncrementID), + RandomID: mathutil.Max(ptAutoIDs.RandomID, ntAutoIDs.RandomID), + } + err = t.GetAutoIDAccessors(ptSchemaID, ptID).Put(newAutoIDs) + if err != nil { + return err + } + err = t.GetAutoIDAccessors(ntSchemaID, ntID).Put(newAutoIDs) + if err != nil { + return err + } + return nil + }) + + return err +} + +func (b *Builder) applyDefaultAction(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + tblIDs, err := b.applyTableUpdate(m, diff) + if err != nil { + return nil, errors.Trace(err) + } + + for _, opt := range diff.AffectedOpts { + var err error + affectedDiff := &model.SchemaDiff{ + Version: diff.Version, + Type: diff.Type, + SchemaID: opt.SchemaID, + TableID: opt.TableID, + OldSchemaID: opt.OldSchemaID, + OldTableID: opt.OldTableID, + } + affectedIDs, err := b.ApplyDiff(m, affectedDiff) + if err != nil { + return nil, errors.Trace(err) + } + tblIDs = append(tblIDs, affectedIDs...) + } + + return tblIDs, nil +} + +func (b *Builder) applyTableUpdate(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + roDBInfo, ok := b.is.SchemaByID(diff.SchemaID) + if !ok { + return nil, ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", diff.SchemaID), + ) + } + dbInfo := b.getSchemaAndCopyIfNecessary(roDBInfo.Name.L) + var oldTableID, newTableID int64 + switch diff.Type { + case model.ActionCreateSequence, model.ActionRecoverTable: + newTableID = diff.TableID + case model.ActionCreateTable: + // WARN: when support create table with foreign key in https://github.com/pingcap/tidb/pull/37148, + // create table with foreign key requires a multi-step state change(none -> write-only -> public), + // when the table's state changes from write-only to public, infoSchema need to drop the old table + // which state is write-only, otherwise, infoSchema.sortedTablesBuckets will contain 2 table both + // have the same ID, but one state is write-only, another table's state is public, it's unexpected. + // + // WARN: this change will break the compatibility if execute create table with foreign key DDL when upgrading TiDB, + // since old-version TiDB doesn't know to delete the old table. + // Since the cluster-index feature also has similar problem, we chose to prevent DDL execution during the upgrade process to avoid this issue. + oldTableID = diff.OldTableID + newTableID = diff.TableID + case model.ActionDropTable, model.ActionDropView, model.ActionDropSequence: + oldTableID = diff.TableID + case model.ActionTruncateTable, model.ActionCreateView, + model.ActionExchangeTablePartition, model.ActionAlterTablePartitioning, + model.ActionRemovePartitioning: + oldTableID = diff.OldTableID + newTableID = diff.TableID + default: + oldTableID = diff.TableID + newTableID = diff.TableID + } + // handle placement rule cache + switch diff.Type { + case model.ActionCreateTable: + b.markTableBundleShouldUpdate(newTableID) + case model.ActionDropTable: + b.deleteBundle(b.is, oldTableID) + case model.ActionTruncateTable: + b.deleteBundle(b.is, oldTableID) + b.markTableBundleShouldUpdate(newTableID) + case model.ActionRecoverTable: + b.markTableBundleShouldUpdate(newTableID) + case model.ActionAlterTablePlacement: + b.markTableBundleShouldUpdate(newTableID) + } + b.copySortedTables(oldTableID, newTableID) + + tblIDs := make([]int64, 0, 2) + // We try to reuse the old allocator, so the cached auto ID can be reused. + var allocs autoid.Allocators + if tableIDIsValid(oldTableID) { + if oldTableID == newTableID && (diff.Type != model.ActionRenameTable && diff.Type != model.ActionRenameTables) && + // For repairing table in TiDB cluster, given 2 normal node and 1 repair node. + // For normal node's information schema, repaired table is existed. + // For repair node's information schema, repaired table is filtered (couldn't find it in `is`). + // So here skip to reserve the allocators when repairing table. + diff.Type != model.ActionRepairTable && + // Alter sequence will change the sequence info in the allocator, so the old allocator is not valid any more. + diff.Type != model.ActionAlterSequence { + oldAllocs, _ := b.is.AllocByID(oldTableID) + allocs = filterAllocators(diff, oldAllocs) + } + + tmpIDs := tblIDs + if (diff.Type == model.ActionRenameTable || diff.Type == model.ActionRenameTables) && diff.OldSchemaID != diff.SchemaID { + oldRoDBInfo, ok := b.is.SchemaByID(diff.OldSchemaID) + if !ok { + return nil, ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", diff.OldSchemaID), + ) + } + oldDBInfo := b.getSchemaAndCopyIfNecessary(oldRoDBInfo.Name.L) + tmpIDs = b.applyDropTable(oldDBInfo, oldTableID, tmpIDs) + } else { + tmpIDs = b.applyDropTable(dbInfo, oldTableID, tmpIDs) + } + + if oldTableID != newTableID { + // Update tblIDs only when oldTableID != newTableID because applyCreateTable() also updates tblIDs. + tblIDs = tmpIDs + } + } + if tableIDIsValid(newTableID) { + // All types except DropTableOrView. + var err error + tblIDs, err = b.applyCreateTable(m, dbInfo, newTableID, allocs, diff.Type, tblIDs) + if err != nil { + return nil, errors.Trace(err) + } + } + return tblIDs, nil +} + +func filterAllocators(diff *model.SchemaDiff, oldAllocs autoid.Allocators) autoid.Allocators { + var newAllocs autoid.Allocators + switch diff.Type { + case model.ActionRebaseAutoID, model.ActionModifyTableAutoIdCache: + // Only drop auto-increment allocator. + newAllocs = oldAllocs.Filter(func(a autoid.Allocator) bool { + tp := a.GetType() + return tp != autoid.RowIDAllocType && tp != autoid.AutoIncrementType + }) + case model.ActionRebaseAutoRandomBase: + // Only drop auto-random allocator. + newAllocs = oldAllocs.Filter(func(a autoid.Allocator) bool { + tp := a.GetType() + return tp != autoid.AutoRandomType + }) + default: + // Keep all allocators. + newAllocs = oldAllocs + } + return newAllocs +} + +func appendAffectedIDs(affected []int64, tblInfo *model.TableInfo) []int64 { + affected = append(affected, tblInfo.ID) + if pi := tblInfo.GetPartitionInfo(); pi != nil { + for _, def := range pi.Definitions { + affected = append(affected, def.ID) + } + } + return affected +} + +// copySortedTables copies sortedTables for old table and new table for later modification. +func (b *Builder) copySortedTables(oldTableID, newTableID int64) { + if tableIDIsValid(oldTableID) { + b.copySortedTablesBucket(tableBucketIdx(oldTableID)) + } + if tableIDIsValid(newTableID) && newTableID != oldTableID { + b.copySortedTablesBucket(tableBucketIdx(newTableID)) + } +} + +func (b *Builder) applyCreateOrAlterResourceGroup(m *meta.Meta, diff *model.SchemaDiff) error { + group, err := m.GetResourceGroup(diff.SchemaID) + if err != nil { + return errors.Trace(err) + } + if group == nil { + return ErrResourceGroupNotExists.GenWithStackByArgs(fmt.Sprintf("(Group ID %d)", diff.SchemaID)) + } + // TODO: need mark updated? + b.is.setResourceGroup(group) + return nil +} + +func (b *Builder) applyDropResourceGroup(m *meta.Meta, diff *model.SchemaDiff) []int64 { + group, ok := b.is.ResourceGroupByID(diff.SchemaID) + if !ok { + return nil + } + b.is.deleteResourceGroup(group.Name.L) + // TODO: return the related information. + return []int64{} +} + +func (b *Builder) applyCreatePolicy(m *meta.Meta, diff *model.SchemaDiff) error { + po, err := m.GetPolicy(diff.SchemaID) + if err != nil { + return errors.Trace(err) + } + if po == nil { + return ErrPlacementPolicyNotExists.GenWithStackByArgs( + fmt.Sprintf("(Policy ID %d)", diff.SchemaID), + ) + } + + if _, ok := b.is.PolicyByID(po.ID); ok { + // if old policy with the same id exists, it means replace, + // so the tables referring this policy's bundle should be updated + b.markBundlesReferPolicyShouldUpdate(po.ID) + } + + b.is.setPolicy(po) + return nil +} + +func (b *Builder) applyAlterPolicy(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + po, err := m.GetPolicy(diff.SchemaID) + if err != nil { + return nil, errors.Trace(err) + } + + if po == nil { + return nil, ErrPlacementPolicyNotExists.GenWithStackByArgs( + fmt.Sprintf("(Policy ID %d)", diff.SchemaID), + ) + } + + b.is.setPolicy(po) + b.markBundlesReferPolicyShouldUpdate(po.ID) + // TODO: return the policy related table ids + return []int64{}, nil +} + +func (b *Builder) applyCreateSchema(m *meta.Meta, diff *model.SchemaDiff) error { + di, err := m.GetDatabase(diff.SchemaID) + if err != nil { + return errors.Trace(err) + } + if di == nil { + // When we apply an old schema diff, the database may has been dropped already, so we need to fall back to + // full load. + return ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", diff.SchemaID), + ) + } + b.is.schemaMap[di.Name.L] = &schemaTables{dbInfo: di, tables: make(map[string]table.Table)} + return nil +} + +func (b *Builder) applyModifySchemaCharsetAndCollate(m *meta.Meta, diff *model.SchemaDiff) error { + di, err := m.GetDatabase(diff.SchemaID) + if err != nil { + return errors.Trace(err) + } + if di == nil { + // This should never happen. + return ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", diff.SchemaID), + ) + } + newDbInfo := b.getSchemaAndCopyIfNecessary(di.Name.L) + newDbInfo.Charset = di.Charset + newDbInfo.Collate = di.Collate + return nil +} + +func (b *Builder) applyModifySchemaDefaultPlacement(m *meta.Meta, diff *model.SchemaDiff) error { + di, err := m.GetDatabase(diff.SchemaID) + if err != nil { + return errors.Trace(err) + } + if di == nil { + // This should never happen. + return ErrDatabaseNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", diff.SchemaID), + ) + } + newDbInfo := b.getSchemaAndCopyIfNecessary(di.Name.L) + newDbInfo.PlacementPolicyRef = di.PlacementPolicyRef + return nil +} + +func (b *Builder) applyDropPolicy(PolicyID int64) []int64 { + po, ok := b.is.PolicyByID(PolicyID) + if !ok { + return nil + } + b.is.deletePolicy(po.Name.L) + // TODO: return the policy related table ids + return []int64{} +} + +func (b *Builder) applyDropSchema(schemaID int64) []int64 { + di, ok := b.is.SchemaByID(schemaID) + if !ok { + return nil + } + delete(b.is.schemaMap, di.Name.L) + + // Copy the sortedTables that contain the table we are going to drop. + tableIDs := make([]int64, 0, len(di.Tables)) + bucketIdxMap := make(map[int]struct{}, len(di.Tables)) + for _, tbl := range di.Tables { + bucketIdxMap[tableBucketIdx(tbl.ID)] = struct{}{} + // TODO: If the table ID doesn't exist. + tableIDs = appendAffectedIDs(tableIDs, tbl) + } + for bucketIdx := range bucketIdxMap { + b.copySortedTablesBucket(bucketIdx) + } + + di = di.Clone() + for _, id := range tableIDs { + b.deleteBundle(b.is, id) + b.applyDropTable(di, id, nil) + } + return tableIDs +} + +func (b *Builder) applyRecoverSchema(m *meta.Meta, diff *model.SchemaDiff) ([]int64, error) { + if di, ok := b.is.SchemaByID(diff.SchemaID); ok { + return nil, ErrDatabaseExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", di.ID), + ) + } + di, err := m.GetDatabase(diff.SchemaID) + if err != nil { + return nil, errors.Trace(err) + } + b.is.schemaMap[di.Name.L] = &schemaTables{ + dbInfo: di, + tables: make(map[string]table.Table, len(diff.AffectedOpts)), + } + return b.applyCreateTables(m, diff) +} + +func (b *Builder) copySortedTablesBucket(bucketIdx int) { + oldSortedTables := b.is.sortedTablesBuckets[bucketIdx] + newSortedTables := make(sortedTables, len(oldSortedTables)) + copy(newSortedTables, oldSortedTables) + b.is.sortedTablesBuckets[bucketIdx] = newSortedTables +} + +func (b *Builder) applyCreateTable(m *meta.Meta, dbInfo *model.DBInfo, tableID int64, allocs autoid.Allocators, tp model.ActionType, affected []int64) ([]int64, error) { + tblInfo, err := m.GetTable(dbInfo.ID, tableID) + if err != nil { + return nil, errors.Trace(err) + } + if tblInfo == nil { + // When we apply an old schema diff, the table may has been dropped already, so we need to fall back to + // full load. + return nil, ErrTableNotExists.GenWithStackByArgs( + fmt.Sprintf("(Schema ID %d)", dbInfo.ID), + fmt.Sprintf("(Table ID %d)", tableID), + ) + } + + switch tp { + case model.ActionDropTablePartition: + case model.ActionTruncateTablePartition: + // ReorganizePartition handle the bundles in applyReorganizePartition + case model.ActionReorganizePartition, model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning: + default: + pi := tblInfo.GetPartitionInfo() + if pi != nil { + for _, partition := range pi.Definitions { + b.markPartitionBundleShouldUpdate(partition.ID) + } + } + } + + if tp != model.ActionTruncateTablePartition { + affected = appendAffectedIDs(affected, tblInfo) + } + + // Failpoint check whether tableInfo should be added to repairInfo. + // Typically used in repair table test to load mock `bad` tableInfo into repairInfo. + failpoint.Inject("repairFetchCreateTable", func(val failpoint.Value) { + if val.(bool) { + if domainutil.RepairInfo.InRepairMode() && tp != model.ActionRepairTable && domainutil.RepairInfo.CheckAndFetchRepairedTable(dbInfo, tblInfo) { + failpoint.Return(nil, nil) + } + } + }) + + ConvertCharsetCollateToLowerCaseIfNeed(tblInfo) + ConvertOldVersionUTF8ToUTF8MB4IfNeed(tblInfo) + + if len(allocs.Allocs) == 0 { + allocs = autoid.NewAllocatorsFromTblInfo(b.store, dbInfo.ID, tblInfo) + } else { + tblVer := autoid.AllocOptionTableInfoVersion(tblInfo.Version) + switch tp { + case model.ActionRebaseAutoID, model.ActionModifyTableAutoIdCache: + idCacheOpt := autoid.CustomAutoIncCacheOption(tblInfo.AutoIdCache) + // If the allocator type might be AutoIncrementType, create both AutoIncrementType + // and RowIDAllocType allocator for it. Because auto id and row id could share the same allocator. + // Allocate auto id may route to allocate row id, if row id allocator is nil, the program panic! + for _, tp := range [2]autoid.AllocatorType{autoid.AutoIncrementType, autoid.RowIDAllocType} { + newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), tp, tblVer, idCacheOpt) + allocs = allocs.Append(newAlloc) + } + case model.ActionRebaseAutoRandomBase: + newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), autoid.AutoRandomType, tblVer) + allocs = allocs.Append(newAlloc) + case model.ActionModifyColumn: + // Change column attribute from auto_increment to auto_random. + if tblInfo.ContainsAutoRandomBits() && allocs.Get(autoid.AutoRandomType) == nil { + // Remove auto_increment allocator. + allocs = allocs.Filter(func(a autoid.Allocator) bool { + return a.GetType() != autoid.AutoIncrementType && a.GetType() != autoid.RowIDAllocType + }) + newAlloc := autoid.NewAllocator(b.store, dbInfo.ID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), autoid.AutoRandomType, tblVer) + allocs = allocs.Append(newAlloc) + } + } + } + tbl, err := b.tableFromMeta(allocs, tblInfo) + if err != nil { + return nil, errors.Trace(err) + } + + b.is.addReferredForeignKeys(dbInfo.Name, tblInfo) + + tableNames := b.is.schemaMap[dbInfo.Name.L] + tableNames.tables[tblInfo.Name.L] = tbl + bucketIdx := tableBucketIdx(tableID) + b.is.sortedTablesBuckets[bucketIdx] = append(b.is.sortedTablesBuckets[bucketIdx], tbl) + slices.SortFunc(b.is.sortedTablesBuckets[bucketIdx], func(i, j table.Table) int { + return cmp.Compare(i.Meta().ID, j.Meta().ID) + }) + + if tblInfo.TempTableType != model.TempTableNone { + b.addTemporaryTable(tableID) + } + + newTbl, ok := b.is.TableByID(tableID) + if ok { + dbInfo.Tables = append(dbInfo.Tables, newTbl.Meta()) + } + return affected, nil +} + +// ConvertCharsetCollateToLowerCaseIfNeed convert the charset / collation of table and its columns to lower case, +// if the table's version is prior to TableInfoVersion3. +func ConvertCharsetCollateToLowerCaseIfNeed(tbInfo *model.TableInfo) { + if tbInfo.Version >= model.TableInfoVersion3 { + return + } + tbInfo.Charset = strings.ToLower(tbInfo.Charset) + tbInfo.Collate = strings.ToLower(tbInfo.Collate) + for _, col := range tbInfo.Columns { + col.SetCharset(strings.ToLower(col.GetCharset())) + col.SetCollate(strings.ToLower(col.GetCollate())) + } +} + +// ConvertOldVersionUTF8ToUTF8MB4IfNeed convert old version UTF8 to UTF8MB4 if config.TreatOldVersionUTF8AsUTF8MB4 is enable. +func ConvertOldVersionUTF8ToUTF8MB4IfNeed(tbInfo *model.TableInfo) { + if tbInfo.Version >= model.TableInfoVersion2 || !config.GetGlobalConfig().TreatOldVersionUTF8AsUTF8MB4 { + return + } + if tbInfo.Charset == charset.CharsetUTF8 { + tbInfo.Charset = charset.CharsetUTF8MB4 + tbInfo.Collate = charset.CollationUTF8MB4 + } + for _, col := range tbInfo.Columns { + if col.Version < model.ColumnInfoVersion2 && col.GetCharset() == charset.CharsetUTF8 { + col.SetCharset(charset.CharsetUTF8MB4) + col.SetCollate(charset.CollationUTF8MB4) + } + } +} + +func (b *Builder) applyDropTable(dbInfo *model.DBInfo, tableID int64, affected []int64) []int64 { + bucketIdx := tableBucketIdx(tableID) + sortedTbls := b.is.sortedTablesBuckets[bucketIdx] + idx := sortedTbls.searchTable(tableID) + if idx == -1 { + return affected + } + if tableNames, ok := b.is.schemaMap[dbInfo.Name.L]; ok { + tblInfo := sortedTbls[idx].Meta() + delete(tableNames.tables, tblInfo.Name.L) + affected = appendAffectedIDs(affected, tblInfo) + } + // Remove the table in sorted table slice. + b.is.sortedTablesBuckets[bucketIdx] = append(sortedTbls[0:idx], sortedTbls[idx+1:]...) + + // Remove the table in temporaryTables + if b.is.temporaryTableIDs != nil { + delete(b.is.temporaryTableIDs, tableID) + } + + // The old DBInfo still holds a reference to old table info, we need to remove it. + for i, tblInfo := range dbInfo.Tables { + if tblInfo.ID == tableID { + if i == len(dbInfo.Tables)-1 { + dbInfo.Tables = dbInfo.Tables[:i] + } else { + dbInfo.Tables = append(dbInfo.Tables[:i], dbInfo.Tables[i+1:]...) + } + b.is.deleteReferredForeignKeys(dbInfo.Name, tblInfo) + break + } + } + return affected +} + +// Build builds and returns the built infoschema. +func (b *Builder) Build() InfoSchema { + b.updateInfoSchemaBundles(b.is) + return b.is +} + +// InitWithOldInfoSchema initializes an empty new InfoSchema by copies all the data from old InfoSchema. +func (b *Builder) InitWithOldInfoSchema(oldSchema InfoSchema) *Builder { + oldIS := oldSchema.(*infoSchema) + b.is.schemaMetaVersion = oldIS.schemaMetaVersion + b.copySchemasMap(oldIS) + b.copyBundlesMap(oldIS) + b.copyPoliciesMap(oldIS) + b.copyResourceGroupMap(oldIS) + b.copyTemporaryTableIDsMap(oldIS) + b.copyReferredForeignKeyMap(oldIS) + + copy(b.is.sortedTablesBuckets, oldIS.sortedTablesBuckets) + return b +} + +func (b *Builder) copySchemasMap(oldIS *infoSchema) { + for k, v := range oldIS.schemaMap { + b.is.schemaMap[k] = v + } +} + +func (b *Builder) copyBundlesMap(oldIS *infoSchema) { + b.is.ruleBundleMap = make(map[int64]*placement.Bundle) + for id, v := range oldIS.ruleBundleMap { + b.is.ruleBundleMap[id] = v + } +} + +func (b *Builder) copyPoliciesMap(oldIS *infoSchema) { + is := b.is + for _, v := range oldIS.AllPlacementPolicies() { + is.policyMap[v.Name.L] = v + } +} + +func (b *Builder) copyResourceGroupMap(oldIS *infoSchema) { + is := b.is + for _, v := range oldIS.AllResourceGroups() { + is.resourceGroupMap[v.Name.L] = v + } +} + +func (b *Builder) copyTemporaryTableIDsMap(oldIS *infoSchema) { + is := b.is + if len(oldIS.temporaryTableIDs) == 0 { + is.temporaryTableIDs = nil + return + } + + is.temporaryTableIDs = make(map[int64]struct{}) + for tblID := range oldIS.temporaryTableIDs { + is.temporaryTableIDs[tblID] = struct{}{} + } +} + +func (b *Builder) copyReferredForeignKeyMap(oldIS *infoSchema) { + for k, v := range oldIS.referredForeignKeyMap { + b.is.referredForeignKeyMap[k] = v + } +} + +// getSchemaAndCopyIfNecessary creates a new schemaTables instance when a table in the database has changed. +// It also does modifications on the new one because old schemaTables must be read-only. +// And it will only copy the changed database once in the lifespan of the Builder. +// NOTE: please make sure the dbName is in lowercase. +func (b *Builder) getSchemaAndCopyIfNecessary(dbName string) *model.DBInfo { + if !b.dirtyDB[dbName] { + b.dirtyDB[dbName] = true + oldSchemaTables := b.is.schemaMap[dbName] + newSchemaTables := &schemaTables{ + dbInfo: oldSchemaTables.dbInfo.Copy(), + tables: make(map[string]table.Table, len(oldSchemaTables.tables)), + } + for k, v := range oldSchemaTables.tables { + newSchemaTables.tables[k] = v + } + b.is.schemaMap[dbName] = newSchemaTables + return newSchemaTables.dbInfo + } + return b.is.schemaMap[dbName].dbInfo +} + +// InitWithDBInfos initializes an empty new InfoSchema with a slice of DBInfo, all placement rules, and schema version. +func (b *Builder) InitWithDBInfos(dbInfos []*model.DBInfo, policies []*model.PolicyInfo, resourceGroups []*model.ResourceGroupInfo, schemaVersion int64) (*Builder, error) { + info := b.is + info.schemaMetaVersion = schemaVersion + // build the policies. + for _, policy := range policies { + info.setPolicy(policy) + } + + // build the groups. + for _, group := range resourceGroups { + info.setResourceGroup(group) + } + + // Maintain foreign key reference information. + for _, di := range dbInfos { + for _, t := range di.Tables { + b.is.addReferredForeignKeys(di.Name, t) + } + } + + for _, di := range dbInfos { + err := b.createSchemaTablesForDB(di, b.tableFromMeta) + if err != nil { + return nil, errors.Trace(err) + } + } + + // Initialize virtual tables. + for _, driver := range drivers { + err := b.createSchemaTablesForDB(driver.DBInfo, driver.TableFromMeta) + if err != nil { + return nil, errors.Trace(err) + } + } + + // Sort all tables by `ID` + for _, v := range info.sortedTablesBuckets { + slices.SortFunc(v, func(a, b table.Table) int { + return cmp.Compare(a.Meta().ID, b.Meta().ID) + }) + } + return b, nil +} + +func (b *Builder) tableFromMeta(alloc autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) { + ret, err := tables.TableFromMeta(alloc, tblInfo) + if err != nil { + return nil, errors.Trace(err) + } + if t, ok := ret.(table.CachedTable); ok { + var tmp pools.Resource + tmp, err = b.factory() + if err != nil { + return nil, errors.Trace(err) + } + + err = t.Init(tmp.(sqlexec.SQLExecutor)) + if err != nil { + return nil, errors.Trace(err) + } + } + return ret, nil +} + +type tableFromMetaFunc func(alloc autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) + +func (b *Builder) createSchemaTablesForDB(di *model.DBInfo, tableFromMeta tableFromMetaFunc) error { + schTbls := &schemaTables{ + dbInfo: di, + tables: make(map[string]table.Table, len(di.Tables)), + } + b.is.schemaMap[di.Name.L] = schTbls + + for _, t := range di.Tables { + allocs := autoid.NewAllocatorsFromTblInfo(b.store, di.ID, t) + var tbl table.Table + tbl, err := tableFromMeta(allocs, t) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("Build table `%s`.`%s` schema failed", di.Name.O, t.Name.O)) + } + schTbls.tables[t.Name.L] = tbl + sortedTbls := b.is.sortedTablesBuckets[tableBucketIdx(t.ID)] + b.is.sortedTablesBuckets[tableBucketIdx(t.ID)] = append(sortedTbls, tbl) + if tblInfo := tbl.Meta(); tblInfo.TempTableType != model.TempTableNone { + b.addTemporaryTable(tblInfo.ID) + } + } + return nil +} + +func (b *Builder) addTemporaryTable(tblID int64) { + if b.is.temporaryTableIDs == nil { + b.is.temporaryTableIDs = make(map[int64]struct{}) + } + b.is.temporaryTableIDs[tblID] = struct{}{} +} + +type virtualTableDriver struct { + *model.DBInfo + TableFromMeta tableFromMetaFunc +} + +var drivers []*virtualTableDriver + +// RegisterVirtualTable register virtual tables to the builder. +func RegisterVirtualTable(dbInfo *model.DBInfo, tableFromMeta tableFromMetaFunc) { + drivers = append(drivers, &virtualTableDriver{dbInfo, tableFromMeta}) +} + +// NewBuilder creates a new Builder with a Handle. +func NewBuilder(store kv.Storage, factory func() (pools.Resource, error)) *Builder { + return &Builder{ + store: store, + is: &infoSchema{ + schemaMap: map[string]*schemaTables{}, + policyMap: map[string]*model.PolicyInfo{}, + resourceGroupMap: map[string]*model.ResourceGroupInfo{}, + ruleBundleMap: map[int64]*placement.Bundle{}, + sortedTablesBuckets: make([]sortedTables, bucketCount), + referredForeignKeyMap: make(map[SchemaAndTableName][]*model.ReferredFKInfo), + }, + dirtyDB: make(map[string]bool), + factory: factory, + } +} + +func tableBucketIdx(tableID int64) int { + return int(tableID % bucketCount) +} + +func tableIDIsValid(tableID int64) bool { + return tableID != 0 +} diff --git a/pkg/infoschema/cache.go b/pkg/infoschema/cache.go new file mode 100644 index 0000000000000..12001c77e40c7 --- /dev/null +++ b/pkg/infoschema/cache.go @@ -0,0 +1,216 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "sort" + "sync" + + infoschema_metrics "github.com/pingcap/tidb/pkg/infoschema/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// InfoCache handles information schema, including getting and setting. +// The cache behavior, however, is transparent and under automatic management. +// It only promised to cache the infoschema, if it is newer than all the cached. +type InfoCache struct { + mu sync.RWMutex + // cache is sorted by both SchemaVersion and timestamp in descending order, assume they have same order + cache []schemaAndTimestamp +} + +type schemaAndTimestamp struct { + infoschema InfoSchema + timestamp int64 +} + +// NewCache creates a new InfoCache. +func NewCache(capacity int) *InfoCache { + return &InfoCache{ + cache: make([]schemaAndTimestamp, 0, capacity), + } +} + +// ReSize re-size the cache. +func (h *InfoCache) ReSize(capacity int) { + h.mu.Lock() + defer h.mu.Unlock() + if cap(h.cache) == capacity { + return + } + oldCache := h.cache + h.cache = make([]schemaAndTimestamp, 0, capacity) + for i, v := range oldCache { + if i >= capacity { + break + } + h.cache = append(h.cache, v) + } +} + +// Size returns the size of the cache, export for test. +func (h *InfoCache) Size() int { + h.mu.Lock() + defer h.mu.Unlock() + return len(h.cache) +} + +// Reset resets the cache. +func (h *InfoCache) Reset(capacity int) { + h.mu.Lock() + defer h.mu.Unlock() + h.cache = make([]schemaAndTimestamp, 0, capacity) +} + +// GetLatest gets the newest information schema. +func (h *InfoCache) GetLatest() InfoSchema { + h.mu.RLock() + defer h.mu.RUnlock() + infoschema_metrics.GetLatestCounter.Inc() + if len(h.cache) > 0 { + infoschema_metrics.HitLatestCounter.Inc() + return h.cache[0].infoschema + } + return nil +} + +// Len returns the size of the cache +func (h *InfoCache) Len() int { + return len(h.cache) +} + +func (h *InfoCache) getSchemaByTimestampNoLock(ts uint64) (InfoSchema, bool) { + logutil.BgLogger().Debug("SCHEMA CACHE get schema", zap.Uint64("timestamp", ts)) + // search one by one instead of binary search, because the timestamp of a schema could be 0 + // this is ok because the size of h.cache is small (currently set to 16) + // moreover, the most likely hit element in the array is the first one in steady mode + // thus it may have better performance than binary search + for i, is := range h.cache { + if is.timestamp == 0 || (i > 0 && h.cache[i-1].infoschema.SchemaMetaVersion() != is.infoschema.SchemaMetaVersion()+1) { + // the schema version doesn't have a timestamp or there is a gap in the schema cache + // ignore all the schema cache equals or less than this version in search by timestamp + break + } + if ts >= uint64(is.timestamp) { + // found the largest version before the given ts + return is.infoschema, true + } + } + + logutil.BgLogger().Debug("SCHEMA CACHE no schema found") + return nil, false +} + +// GetByVersion gets the information schema based on schemaVersion. Returns nil if it is not loaded. +func (h *InfoCache) GetByVersion(version int64) InfoSchema { + h.mu.RLock() + defer h.mu.RUnlock() + return h.getByVersionNoLock(version) +} + +func (h *InfoCache) getByVersionNoLock(version int64) InfoSchema { + infoschema_metrics.GetVersionCounter.Inc() + i := sort.Search(len(h.cache), func(i int) bool { + return h.cache[i].infoschema.SchemaMetaVersion() <= version + }) + + // `GetByVersion` is allowed to load the latest schema that is less than argument `version`. + // Consider cache has values [10, 9, _, _, 6, 5, 4, 3, 2, 1], version 8 and 7 is empty because of the diff is empty. + // If we want to get version 8, we can return version 6 because v7 and v8 do not change anything, they are totally the same, + // in this case the `i` will not be 0. + // If i == 0, it means the argument version is `10`, or greater than `10`, if `version` is 10 + // `h.cache[i].SchemaMetaVersion() == version` will be true, so we can return the latest schema, return nil if not. + // The following code is equivalent to: + // ``` + // if h.GetLatest().SchemaMetaVersion() < version { + // return nil + // } + // + // if i < len(h.cache) { + // hitVersionCounter.Inc() + // return h.cache[i] + // } + // ``` + + if i < len(h.cache) && (i != 0 || h.cache[i].infoschema.SchemaMetaVersion() == version) { + infoschema_metrics.HitVersionCounter.Inc() + return h.cache[i].infoschema + } + return nil +} + +// GetBySnapshotTS gets the information schema based on snapshotTS. +// It searches the schema cache and find the schema with max schema ts that equals or smaller than given snapshot ts +// Where the schema ts is the commitTs of the txn creates the schema diff +func (h *InfoCache) GetBySnapshotTS(snapshotTS uint64) InfoSchema { + h.mu.RLock() + defer h.mu.RUnlock() + + infoschema_metrics.GetTSCounter.Inc() + if schema, ok := h.getSchemaByTimestampNoLock(snapshotTS); ok { + infoschema_metrics.HitTSCounter.Inc() + return schema + } + return nil +} + +// Insert will **TRY** to insert the infoschema into the cache. +// It only promised to cache the newest infoschema. +// It returns 'true' if it is cached, 'false' otherwise. +// schemaTs is the commitTs of the txn creates the schema diff, which indicates since when the schema version is taking effect +func (h *InfoCache) Insert(is InfoSchema, schemaTS uint64) bool { + logutil.BgLogger().Debug("INSERT SCHEMA", zap.Uint64("schema ts", schemaTS), zap.Int64("schema version", is.SchemaMetaVersion())) + h.mu.Lock() + defer h.mu.Unlock() + + version := is.SchemaMetaVersion() + + // assume this is the timestamp order as well + i := sort.Search(len(h.cache), func(i int) bool { + return h.cache[i].infoschema.SchemaMetaVersion() <= version + }) + + // cached entry + if i < len(h.cache) && h.cache[i].infoschema.SchemaMetaVersion() == version { + // update timestamp if it is not 0 and cached one is 0 + if schemaTS > 0 && h.cache[i].timestamp == 0 { + h.cache[i].timestamp = int64(schemaTS) + } + return true + } + + if len(h.cache) < cap(h.cache) { + // has free space, grown the slice + h.cache = h.cache[:len(h.cache)+1] + copy(h.cache[i+1:], h.cache[i:]) + h.cache[i] = schemaAndTimestamp{ + infoschema: is, + timestamp: int64(schemaTS), + } + } else if i < len(h.cache) { + // drop older schema + copy(h.cache[i+1:], h.cache[i:]) + h.cache[i] = schemaAndTimestamp{ + infoschema: is, + timestamp: int64(schemaTS), + } + } else { + // older than all cached schemas, refuse to cache it + return false + } + + return true +} diff --git a/pkg/infoschema/cluster.go b/pkg/infoschema/cluster.go new file mode 100644 index 0000000000000..9127882ec2b56 --- /dev/null +++ b/pkg/infoschema/cluster.go @@ -0,0 +1,160 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "net" + "strconv" + "strings" + + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sem" +) + +// Cluster table indicates that these tables need to get data from other tidb nodes, which may get from all other nodes, or may get from the ddl owner. +// Cluster table list, attention: +// 1. the table name should be upper case. +// 2. For tables that need to get data from all other TiDB nodes, clusterTableName should equal to "CLUSTER_" + memTableTableName. +const ( + // ClusterTableSlowLog is the string constant of cluster slow query memory table. + ClusterTableSlowLog = "CLUSTER_SLOW_QUERY" + ClusterTableProcesslist = "CLUSTER_PROCESSLIST" + // ClusterTableStatementsSummary is the string constant of cluster statement summary table. + ClusterTableStatementsSummary = "CLUSTER_STATEMENTS_SUMMARY" + // ClusterTableStatementsSummaryHistory is the string constant of cluster statement summary history table. + ClusterTableStatementsSummaryHistory = "CLUSTER_STATEMENTS_SUMMARY_HISTORY" + // ClusterTableStatementsSummaryEvicted is the string constant of cluster statement summary evict table. + ClusterTableStatementsSummaryEvicted = "CLUSTER_STATEMENTS_SUMMARY_EVICTED" + // ClusterTableTiDBTrx is the string constant of cluster transaction running table. + ClusterTableTiDBTrx = "CLUSTER_TIDB_TRX" + // ClusterTableDeadlocks is the string constant of cluster dead lock table. + ClusterTableDeadlocks = "CLUSTER_DEADLOCKS" + // ClusterTableDeadlocks is the string constant of cluster transaction summary table. + ClusterTableTrxSummary = "CLUSTER_TRX_SUMMARY" + // ClusterTableMemoryUsage is the memory usage status of tidb cluster. + ClusterTableMemoryUsage = "CLUSTER_MEMORY_USAGE" + // ClusterTableMemoryUsageOpsHistory is the memory control operators history of tidb cluster. + ClusterTableMemoryUsageOpsHistory = "CLUSTER_MEMORY_USAGE_OPS_HISTORY" +) + +// memTableToAllTiDBClusterTables means add memory table to cluster table that will send cop request to all TiDB nodes. +var memTableToAllTiDBClusterTables = map[string]string{ + TableSlowQuery: ClusterTableSlowLog, + TableProcesslist: ClusterTableProcesslist, + TableStatementsSummary: ClusterTableStatementsSummary, + TableStatementsSummaryHistory: ClusterTableStatementsSummaryHistory, + TableStatementsSummaryEvicted: ClusterTableStatementsSummaryEvicted, + TableTiDBTrx: ClusterTableTiDBTrx, + TableDeadlocks: ClusterTableDeadlocks, + TableTrxSummary: ClusterTableTrxSummary, + TableMemoryUsage: ClusterTableMemoryUsage, + TableMemoryUsageOpsHistory: ClusterTableMemoryUsageOpsHistory, +} + +// memTableToDDLOwnerClusterTables means add memory table to cluster table that will send cop request to DDL owner node. +var memTableToDDLOwnerClusterTables = map[string]string{ + TableTiFlashReplica: TableTiFlashReplica, +} + +// ClusterTableCopDestination means the destination that cluster tables will send cop requests to. +type ClusterTableCopDestination int + +const ( + // AllTiDB is uese by CLUSTER_* table, means that these tables will send cop request to all TiDB nodes. + AllTiDB ClusterTableCopDestination = iota + // DDLOwner is uese by tiflash_replica currently, means that this table will send cop request to DDL owner node. + DDLOwner +) + +// GetClusterTableCopDestination gets cluster table cop request destination. +func GetClusterTableCopDestination(tableName string) ClusterTableCopDestination { + if _, exist := memTableToDDLOwnerClusterTables[strings.ToUpper(tableName)]; exist { + return DDLOwner + } + return AllTiDB +} + +func init() { + var addrCol = columnInfo{name: util.ClusterTableInstanceColumnName, tp: mysql.TypeVarchar, size: 64} + for memTableName, clusterMemTableName := range memTableToAllTiDBClusterTables { + memTableCols := tableNameToColumns[memTableName] + if len(memTableCols) == 0 { + continue + } + cols := make([]columnInfo, 0, len(memTableCols)+1) + cols = append(cols, addrCol) + cols = append(cols, memTableCols...) + tableNameToColumns[clusterMemTableName] = cols + } +} + +// isClusterTableByName used to check whether the table is a cluster memory table. +func isClusterTableByName(dbName, tableName string) bool { + dbName = strings.ToUpper(dbName) + switch dbName { + case util.InformationSchemaName.O, util.PerformanceSchemaName.O: + tableName = strings.ToUpper(tableName) + for _, name := range memTableToAllTiDBClusterTables { + name = strings.ToUpper(name) + if name == tableName { + return true + } + } + for _, name := range memTableToDDLOwnerClusterTables { + name = strings.ToUpper(name) + if name == tableName { + return true + } + } + default: + } + return false +} + +// AppendHostInfoToRows appends host info to the rows. +func AppendHostInfoToRows(ctx sessionctx.Context, rows [][]types.Datum) ([][]types.Datum, error) { + addr, err := GetInstanceAddr(ctx) + if err != nil { + return nil, err + } + for i := range rows { + row := make([]types.Datum, 0, len(rows[i])+1) + row = append(row, types.NewStringDatum(addr)) + row = append(row, rows[i]...) + rows[i] = row + } + return rows, nil +} + +// GetInstanceAddr gets the instance address. +func GetInstanceAddr(ctx sessionctx.Context) (string, error) { + serverInfo, err := infosync.GetServerInfo() + if err != nil { + return "", err + } + addr := net.JoinHostPort(serverInfo.IP, strconv.FormatUint(uint64(serverInfo.StatusPort), 10)) + if sem.IsEnabled() { + checker := privilege.GetPrivilegeManager(ctx) + if checker == nil || !checker.RequestDynamicVerification(ctx.GetSessionVars().ActiveRoles, "RESTRICTED_TABLES_ADMIN", false) { + addr = serverInfo.ID + } + } + return addr, nil +} diff --git a/pkg/infoschema/error.go b/pkg/infoschema/error.go new file mode 100644 index 0000000000000..0e038e30da592 --- /dev/null +++ b/pkg/infoschema/error.go @@ -0,0 +1,109 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +var ( + // ErrDatabaseExists returns for database already exists. + ErrDatabaseExists = dbterror.ClassSchema.NewStd(mysql.ErrDBCreateExists) + // ErrDatabaseDropExists returns for dropping a non-existent database. + ErrDatabaseDropExists = dbterror.ClassSchema.NewStd(mysql.ErrDBDropExists) + // ErrAccessDenied return when the user doesn't have the permission to access the table. + ErrAccessDenied = dbterror.ClassSchema.NewStd(mysql.ErrAccessDenied) + // ErrDatabaseNotExists returns for database not exists. + ErrDatabaseNotExists = dbterror.ClassSchema.NewStd(mysql.ErrBadDB) + // ErrPlacementPolicyExists returns for placement_policy policy already exists. + ErrPlacementPolicyExists = dbterror.ClassSchema.NewStd(mysql.ErrPlacementPolicyExists) + // ErrPlacementPolicyNotExists return for placement_policy policy not exists. + ErrPlacementPolicyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrPlacementPolicyNotExists) + // ErrResourceGroupExists return for resource group already exists. + ErrResourceGroupExists = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupExists) + // ErrResourceGroupNotExists return for resource group not exists. + ErrResourceGroupNotExists = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupNotExists) + // ErrResourceGroupInvalidBackgroundTaskName return for unknown resource group background task name. + ErrResourceGroupInvalidBackgroundTaskName = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupInvalidBackgroundTaskName) + // ErrReservedSyntax for internal syntax. + ErrReservedSyntax = dbterror.ClassSchema.NewStd(mysql.ErrReservedSyntax) + // ErrTableExists returns for table already exists. + ErrTableExists = dbterror.ClassSchema.NewStd(mysql.ErrTableExists) + // ErrTableDropExists returns for dropping a non-existent table. + ErrTableDropExists = dbterror.ClassSchema.NewStd(mysql.ErrBadTable) + // ErrSequenceDropExists returns for dropping a non-exist sequence. + ErrSequenceDropExists = dbterror.ClassSchema.NewStd(mysql.ErrUnknownSequence) + // ErrColumnNotExists returns for column not exists. + ErrColumnNotExists = dbterror.ClassSchema.NewStd(mysql.ErrBadField) + // ErrColumnExists returns for column already exists. + ErrColumnExists = dbterror.ClassSchema.NewStd(mysql.ErrDupFieldName) + // ErrKeyNameDuplicate returns for index duplicate when rename index. + ErrKeyNameDuplicate = dbterror.ClassSchema.NewStd(mysql.ErrDupKeyName) + // ErrNonuniqTable returns when none unique tables errors. + ErrNonuniqTable = dbterror.ClassSchema.NewStd(mysql.ErrNonuniqTable) + // ErrMultiplePriKey returns for multiple primary keys. + ErrMultiplePriKey = dbterror.ClassSchema.NewStd(mysql.ErrMultiplePriKey) + // ErrTooManyKeyParts returns for too many key parts. + ErrTooManyKeyParts = dbterror.ClassSchema.NewStd(mysql.ErrTooManyKeyParts) + // ErrForeignKeyNotExists returns for foreign key not exists. + ErrForeignKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrCantDropFieldOrKey) + // ErrTableNotLockedForWrite returns for write tables when only hold the table read lock. + ErrTableNotLockedForWrite = dbterror.ClassSchema.NewStd(mysql.ErrTableNotLockedForWrite) + // ErrTableNotLocked returns when session has explicitly lock tables, then visit unlocked table will return this error. + ErrTableNotLocked = dbterror.ClassSchema.NewStd(mysql.ErrTableNotLocked) + // ErrTableNotExists returns for table not exists. + ErrTableNotExists = dbterror.ClassSchema.NewStd(mysql.ErrNoSuchTable) + // ErrKeyNotExists returns for index not exists. + ErrKeyNotExists = dbterror.ClassSchema.NewStd(mysql.ErrKeyDoesNotExist) + // ErrCannotAddForeign returns for foreign key exists. + ErrCannotAddForeign = dbterror.ClassSchema.NewStd(mysql.ErrCannotAddForeign) + // ErrForeignKeyOnPartitioned returns for foreign key on partition table. + ErrForeignKeyOnPartitioned = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyOnPartitioned) + // ErrForeignKeyNotMatch returns for foreign key not match. + ErrForeignKeyNotMatch = dbterror.ClassSchema.NewStd(mysql.ErrWrongFkDef) + // ErrIndexExists returns for index already exists. + ErrIndexExists = dbterror.ClassSchema.NewStd(mysql.ErrDupIndex) + // ErrUserDropExists returns for dropping a non-existent user. + ErrUserDropExists = dbterror.ClassSchema.NewStd(mysql.ErrBadUser) + // ErrUserAlreadyExists return for creating a existent user. + ErrUserAlreadyExists = dbterror.ClassSchema.NewStd(mysql.ErrUserAlreadyExists) + // ErrTableLocked returns when the table was locked by other session. + ErrTableLocked = dbterror.ClassSchema.NewStd(mysql.ErrTableLocked) + // ErrWrongObject returns when the table/view/sequence is not the expected object. + ErrWrongObject = dbterror.ClassSchema.NewStd(mysql.ErrWrongObject) + // ErrAdminCheckTable returns when the check table in temporary mode. + ErrAdminCheckTable = dbterror.ClassSchema.NewStd(mysql.ErrAdminCheckTable) + // ErrEmptyDatabase returns when the database is unexpectedly empty. + ErrEmptyDatabase = dbterror.ClassSchema.NewStd(mysql.ErrBadDB) + // ErrForbidSchemaChange returns when the schema change is illegal + ErrForbidSchemaChange = dbterror.ClassSchema.NewStd(mysql.ErrForbidSchemaChange) + // ErrTableWithoutPrimaryKey returns when there is no primary key on a table and sql_require_primary_key is set + ErrTableWithoutPrimaryKey = dbterror.ClassSchema.NewStd(mysql.ErrTableWithoutPrimaryKey) + // ErrForeignKeyCannotUseVirtualColumn returns when foreign key refer virtual generated column. + ErrForeignKeyCannotUseVirtualColumn = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyCannotUseVirtualColumn) + // ErrForeignKeyCannotOpenParent returns when foreign key refer table not exists. + ErrForeignKeyCannotOpenParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyCannotOpenParent) + // ErrForeignKeyNoColumnInParent returns when foreign key refer columns don't exist in parent table. + ErrForeignKeyNoColumnInParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyNoColumnInParent) + // ErrForeignKeyNoIndexInParent returns when foreign key refer columns don't have related index in parent table. + ErrForeignKeyNoIndexInParent = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyNoIndexInParent) + // ErrForeignKeyColumnNotNull returns when foreign key with SET NULL constrain and the related column has not null. + ErrForeignKeyColumnNotNull = dbterror.ClassSchema.NewStd(mysql.ErrForeignKeyColumnNotNull) + // ErrResourceGroupSupportDisabled returns for resource group feature is disabled + ErrResourceGroupSupportDisabled = dbterror.ClassSchema.NewStd(mysql.ErrResourceGroupSupportDisabled) + // ErrCheckConstraintDupName returns for duplicate constraint names. + ErrCheckConstraintDupName = dbterror.ClassSchema.NewStd(mysql.ErrCheckConstraintDupName) +) diff --git a/pkg/infoschema/infoschema.go b/pkg/infoschema/infoschema.go new file mode 100644 index 0000000000000..acd20e2c58264 --- /dev/null +++ b/pkg/infoschema/infoschema.go @@ -0,0 +1,777 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "cmp" + "fmt" + "slices" + "sort" + "sync" + + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/mock" +) + +// InfoSchema is the interface used to retrieve the schema information. +// It works as a in memory cache and doesn't handle any schema change. +// InfoSchema is read-only, and the returned value is a copy. +// TODO: add more methods to retrieve tables and columns. +type InfoSchema interface { + SchemaByName(schema model.CIStr) (*model.DBInfo, bool) + SchemaExists(schema model.CIStr) bool + TableByName(schema, table model.CIStr) (table.Table, error) + TableExists(schema, table model.CIStr) bool + SchemaByID(id int64) (*model.DBInfo, bool) + SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) + PolicyByName(name model.CIStr) (*model.PolicyInfo, bool) + ResourceGroupByName(name model.CIStr) (*model.ResourceGroupInfo, bool) + TableByID(id int64) (table.Table, bool) + AllocByID(id int64) (autoid.Allocators, bool) + AllSchemaNames() []string + AllSchemas() []*model.DBInfo + Clone() (result []*model.DBInfo) + SchemaTables(schema model.CIStr) []table.Table + SchemaMetaVersion() int64 + // TableIsView indicates whether the schema.table is a view. + TableIsView(schema, table model.CIStr) bool + // TableIsSequence indicates whether the schema.table is a sequence. + TableIsSequence(schema, table model.CIStr) bool + FindTableByPartitionID(partitionID int64) (table.Table, *model.DBInfo, *model.PartitionDefinition) + // PlacementBundleByPhysicalTableID is used to get a rule bundle. + PlacementBundleByPhysicalTableID(id int64) (*placement.Bundle, bool) + // AllPlacementBundles is used to get all placement bundles + AllPlacementBundles() []*placement.Bundle + // AllPlacementPolicies returns all placement policies + AllPlacementPolicies() []*model.PolicyInfo + // AllResourceGroups returns all resource groups + AllResourceGroups() []*model.ResourceGroupInfo + // HasTemporaryTable returns whether information schema has temporary table + HasTemporaryTable() bool + // GetTableReferredForeignKeys gets the table's ReferredFKInfo by lowercase schema and table name. + GetTableReferredForeignKeys(schema, table string) []*model.ReferredFKInfo +} + +type sortedTables []table.Table + +func (s sortedTables) searchTable(id int64) int { + idx := sort.Search(len(s), func(i int) bool { + return s[i].Meta().ID >= id + }) + if idx == len(s) || s[idx].Meta().ID != id { + return -1 + } + return idx +} + +type schemaTables struct { + dbInfo *model.DBInfo + tables map[string]table.Table +} + +const bucketCount = 512 + +type infoSchema struct { + // ruleBundleMap stores all placement rules + ruleBundleMap map[int64]*placement.Bundle + + // policyMap stores all placement policies. + policyMutex sync.RWMutex + policyMap map[string]*model.PolicyInfo + + // resourceGroupMap stores all resource groups. + resourceGroupMutex sync.RWMutex + resourceGroupMap map[string]*model.ResourceGroupInfo + + schemaMap map[string]*schemaTables + + // sortedTablesBuckets is a slice of sortedTables, a table's bucket index is (tableID % bucketCount). + sortedTablesBuckets []sortedTables + + // temporaryTables stores the temporary table ids + temporaryTableIDs map[int64]struct{} + + // schemaMetaVersion is the version of schema, and we should check version when change schema. + schemaMetaVersion int64 + + // referredForeignKeyMap records all table's ReferredFKInfo. + // referredSchemaAndTableName => child SchemaAndTableAndForeignKeyName => *model.ReferredFKInfo + referredForeignKeyMap map[SchemaAndTableName][]*model.ReferredFKInfo +} + +// SchemaAndTableName contains the lower-case schema name and table name. +type SchemaAndTableName struct { + schema string + table string +} + +// MockInfoSchema only serves for test. +func MockInfoSchema(tbList []*model.TableInfo) InfoSchema { + result := &infoSchema{} + result.schemaMap = make(map[string]*schemaTables) + result.policyMap = make(map[string]*model.PolicyInfo) + result.resourceGroupMap = make(map[string]*model.ResourceGroupInfo) + result.ruleBundleMap = make(map[int64]*placement.Bundle) + result.sortedTablesBuckets = make([]sortedTables, bucketCount) + dbInfo := &model.DBInfo{ID: 0, Name: model.NewCIStr("test"), Tables: tbList} + tableNames := &schemaTables{ + dbInfo: dbInfo, + tables: make(map[string]table.Table), + } + result.schemaMap["test"] = tableNames + for _, tb := range tbList { + tbl := table.MockTableFromMeta(tb) + tableNames.tables[tb.Name.L] = tbl + bucketIdx := tableBucketIdx(tb.ID) + result.sortedTablesBuckets[bucketIdx] = append(result.sortedTablesBuckets[bucketIdx], tbl) + } + for i := range result.sortedTablesBuckets { + slices.SortFunc(result.sortedTablesBuckets[i], func(i, j table.Table) int { + return cmp.Compare(i.Meta().ID, j.Meta().ID) + }) + } + return result +} + +// MockInfoSchemaWithSchemaVer only serves for test. +func MockInfoSchemaWithSchemaVer(tbList []*model.TableInfo, schemaVer int64) InfoSchema { + result := &infoSchema{} + result.schemaMap = make(map[string]*schemaTables) + result.policyMap = make(map[string]*model.PolicyInfo) + result.resourceGroupMap = make(map[string]*model.ResourceGroupInfo) + result.ruleBundleMap = make(map[int64]*placement.Bundle) + result.sortedTablesBuckets = make([]sortedTables, bucketCount) + dbInfo := &model.DBInfo{ID: 0, Name: model.NewCIStr("test"), Tables: tbList} + tableNames := &schemaTables{ + dbInfo: dbInfo, + tables: make(map[string]table.Table), + } + result.schemaMap["test"] = tableNames + for _, tb := range tbList { + tbl := table.MockTableFromMeta(tb) + tableNames.tables[tb.Name.L] = tbl + bucketIdx := tableBucketIdx(tb.ID) + result.sortedTablesBuckets[bucketIdx] = append(result.sortedTablesBuckets[bucketIdx], tbl) + } + for i := range result.sortedTablesBuckets { + slices.SortFunc(result.sortedTablesBuckets[i], func(i, j table.Table) int { + return cmp.Compare(i.Meta().ID, j.Meta().ID) + }) + } + result.schemaMetaVersion = schemaVer + return result +} + +var _ InfoSchema = (*infoSchema)(nil) + +func (is *infoSchema) SchemaByName(schema model.CIStr) (val *model.DBInfo, ok bool) { + tableNames, ok := is.schemaMap[schema.L] + if !ok { + return + } + return tableNames.dbInfo, true +} + +func (is *infoSchema) SchemaMetaVersion() int64 { + return is.schemaMetaVersion +} + +func (is *infoSchema) SchemaExists(schema model.CIStr) bool { + _, ok := is.schemaMap[schema.L] + return ok +} + +func (is *infoSchema) TableByName(schema, table model.CIStr) (t table.Table, err error) { + if tbNames, ok := is.schemaMap[schema.L]; ok { + if t, ok = tbNames.tables[table.L]; ok { + return + } + } + return nil, ErrTableNotExists.GenWithStackByArgs(schema, table) +} + +func (is *infoSchema) TableIsView(schema, table model.CIStr) bool { + if tbNames, ok := is.schemaMap[schema.L]; ok { + if t, ok := tbNames.tables[table.L]; ok { + return t.Meta().IsView() + } + } + return false +} + +func (is *infoSchema) TableIsSequence(schema, table model.CIStr) bool { + if tbNames, ok := is.schemaMap[schema.L]; ok { + if t, ok := tbNames.tables[table.L]; ok { + return t.Meta().IsSequence() + } + } + return false +} + +func (is *infoSchema) TableExists(schema, table model.CIStr) bool { + if tbNames, ok := is.schemaMap[schema.L]; ok { + if _, ok = tbNames.tables[table.L]; ok { + return true + } + } + return false +} + +func (is *infoSchema) PolicyByID(id int64) (val *model.PolicyInfo, ok bool) { + // TODO: use another hash map to avoid traveling on the policy map + for _, v := range is.policyMap { + if v.ID == id { + return v, true + } + } + return nil, false +} + +func (is *infoSchema) ResourceGroupByID(id int64) (val *model.ResourceGroupInfo, ok bool) { + is.resourceGroupMutex.RLock() + defer is.resourceGroupMutex.RUnlock() + for _, v := range is.resourceGroupMap { + if v.ID == id { + return v, true + } + } + return nil, false +} + +func (is *infoSchema) SchemaByID(id int64) (val *model.DBInfo, ok bool) { + for _, v := range is.schemaMap { + if v.dbInfo.ID == id { + return v.dbInfo, true + } + } + return nil, false +} + +func (is *infoSchema) SchemaByTable(tableInfo *model.TableInfo) (val *model.DBInfo, ok bool) { + if tableInfo == nil { + return nil, false + } + for _, v := range is.schemaMap { + if tbl, ok := v.tables[tableInfo.Name.L]; ok { + if tbl.Meta().ID == tableInfo.ID { + return v.dbInfo, true + } + } + } + return nil, false +} + +func (is *infoSchema) TableByID(id int64) (val table.Table, ok bool) { + slice := is.sortedTablesBuckets[tableBucketIdx(id)] + idx := slice.searchTable(id) + if idx == -1 { + return nil, false + } + return slice[idx], true +} + +func (is *infoSchema) AllocByID(id int64) (autoid.Allocators, bool) { + tbl, ok := is.TableByID(id) + if !ok { + return autoid.Allocators{}, false + } + return tbl.Allocators(nil), true +} + +func (is *infoSchema) AllSchemaNames() (names []string) { + for _, v := range is.schemaMap { + names = append(names, v.dbInfo.Name.O) + } + return +} + +func (is *infoSchema) AllSchemas() (schemas []*model.DBInfo) { + for _, v := range is.schemaMap { + schemas = append(schemas, v.dbInfo) + } + return +} + +func (is *infoSchema) SchemaTables(schema model.CIStr) (tables []table.Table) { + schemaTables, ok := is.schemaMap[schema.L] + if !ok { + return + } + for _, tbl := range schemaTables.tables { + tables = append(tables, tbl) + } + return +} + +// FindTableByPartitionID finds the partition-table info by the partitionID. +// FindTableByPartitionID will traverse all the tables to find the partitionID partition in which partition-table. +func (is *infoSchema) FindTableByPartitionID(partitionID int64) (table.Table, *model.DBInfo, *model.PartitionDefinition) { + for _, v := range is.schemaMap { + for _, tbl := range v.tables { + pi := tbl.Meta().GetPartitionInfo() + if pi == nil { + continue + } + for _, p := range pi.Definitions { + if p.ID == partitionID { + return tbl, v.dbInfo, &p + } + } + } + } + return nil, nil, nil +} + +// HasTemporaryTable returns whether information schema has temporary table +func (is *infoSchema) HasTemporaryTable() bool { + return len(is.temporaryTableIDs) != 0 +} + +func (is *infoSchema) Clone() (result []*model.DBInfo) { + for _, v := range is.schemaMap { + result = append(result, v.dbInfo.Clone()) + } + return +} + +// GetSequenceByName gets the sequence by name. +func GetSequenceByName(is InfoSchema, schema, sequence model.CIStr) (util.SequenceTable, error) { + tbl, err := is.TableByName(schema, sequence) + if err != nil { + return nil, err + } + if !tbl.Meta().IsSequence() { + return nil, ErrWrongObject.GenWithStackByArgs(schema, sequence, "SEQUENCE") + } + return tbl.(util.SequenceTable), nil +} + +func init() { + // Initialize the information shema database and register the driver to `drivers` + dbID := autoid.InformationSchemaDBID + infoSchemaTables := make([]*model.TableInfo, 0, len(tableNameToColumns)) + for name, cols := range tableNameToColumns { + tableInfo := buildTableMeta(name, cols) + infoSchemaTables = append(infoSchemaTables, tableInfo) + var ok bool + tableInfo.ID, ok = tableIDMap[tableInfo.Name.O] + if !ok { + panic(fmt.Sprintf("get information_schema table id failed, unknown system table `%v`", tableInfo.Name.O)) + } + for i, c := range tableInfo.Columns { + c.ID = int64(i) + 1 + } + tableInfo.MaxColumnID = int64(len(tableInfo.Columns)) + tableInfo.MaxIndexID = int64(len(tableInfo.Indices)) + } + infoSchemaDB := &model.DBInfo{ + ID: dbID, + Name: util.InformationSchemaName, + Charset: mysql.DefaultCharset, + Collate: mysql.DefaultCollationName, + Tables: infoSchemaTables, + } + RegisterVirtualTable(infoSchemaDB, createInfoSchemaTable) + util.GetSequenceByName = func(is interface{}, schema, sequence model.CIStr) (util.SequenceTable, error) { + return GetSequenceByName(is.(InfoSchema), schema, sequence) + } + mock.MockInfoschema = func(tbList []*model.TableInfo) sessionctx.InfoschemaMetaVersion { + return MockInfoSchema(tbList) + } +} + +// HasAutoIncrementColumn checks whether the table has auto_increment columns, if so, return true and the column name. +func HasAutoIncrementColumn(tbInfo *model.TableInfo) (bool, string) { + for _, col := range tbInfo.Columns { + if mysql.HasAutoIncrementFlag(col.GetFlag()) { + return true, col.Name.L + } + } + return false, "" +} + +// PolicyByName is used to find the policy. +func (is *infoSchema) PolicyByName(name model.CIStr) (*model.PolicyInfo, bool) { + is.policyMutex.RLock() + defer is.policyMutex.RUnlock() + t, r := is.policyMap[name.L] + return t, r +} + +// ResourceGroupByName is used to find the resource group. +func (is *infoSchema) ResourceGroupByName(name model.CIStr) (*model.ResourceGroupInfo, bool) { + is.resourceGroupMutex.RLock() + defer is.resourceGroupMutex.RUnlock() + t, r := is.resourceGroupMap[name.L] + return t, r +} + +// AllResourceGroups returns all resource groups. +func (is *infoSchema) AllResourceGroups() []*model.ResourceGroupInfo { + is.resourceGroupMutex.RLock() + defer is.resourceGroupMutex.RUnlock() + groups := make([]*model.ResourceGroupInfo, 0, len(is.resourceGroupMap)) + for _, group := range is.resourceGroupMap { + groups = append(groups, group) + } + return groups +} + +// AllPlacementPolicies returns all placement policies +func (is *infoSchema) AllPlacementPolicies() []*model.PolicyInfo { + is.policyMutex.RLock() + defer is.policyMutex.RUnlock() + policies := make([]*model.PolicyInfo, 0, len(is.policyMap)) + for _, policy := range is.policyMap { + policies = append(policies, policy) + } + return policies +} + +func (is *infoSchema) PlacementBundleByPhysicalTableID(id int64) (*placement.Bundle, bool) { + t, r := is.ruleBundleMap[id] + return t, r +} + +func (is *infoSchema) AllPlacementBundles() []*placement.Bundle { + bundles := make([]*placement.Bundle, 0, len(is.ruleBundleMap)) + for _, bundle := range is.ruleBundleMap { + bundles = append(bundles, bundle) + } + return bundles +} + +func (is *infoSchema) setResourceGroup(resourceGroup *model.ResourceGroupInfo) { + is.resourceGroupMutex.Lock() + defer is.resourceGroupMutex.Unlock() + is.resourceGroupMap[resourceGroup.Name.L] = resourceGroup +} + +func (is *infoSchema) deleteResourceGroup(name string) { + is.resourceGroupMutex.Lock() + defer is.resourceGroupMutex.Unlock() + delete(is.resourceGroupMap, name) +} + +func (is *infoSchema) setPolicy(policy *model.PolicyInfo) { + is.policyMutex.Lock() + defer is.policyMutex.Unlock() + is.policyMap[policy.Name.L] = policy +} + +func (is *infoSchema) deletePolicy(name string) { + is.policyMutex.Lock() + defer is.policyMutex.Unlock() + delete(is.policyMap, name) +} + +func (is *infoSchema) addReferredForeignKeys(schema model.CIStr, tbInfo *model.TableInfo) { + for _, fk := range tbInfo.ForeignKeys { + if fk.Version < model.FKVersion1 { + continue + } + refer := SchemaAndTableName{schema: fk.RefSchema.L, table: fk.RefTable.L} + referredFKList := is.referredForeignKeyMap[refer] + found := false + for _, referredFK := range referredFKList { + if referredFK.ChildSchema.L == schema.L && referredFK.ChildTable.L == tbInfo.Name.L && referredFK.ChildFKName.L == fk.Name.L { + referredFK.Cols = fk.RefCols + found = true + break + } + } + if found { + continue + } + + newReferredFKList := make([]*model.ReferredFKInfo, 0, len(referredFKList)+1) + newReferredFKList = append(newReferredFKList, referredFKList...) + newReferredFKList = append(newReferredFKList, &model.ReferredFKInfo{ + Cols: fk.RefCols, + ChildSchema: schema, + ChildTable: tbInfo.Name, + ChildFKName: fk.Name, + }) + sort.Slice(newReferredFKList, func(i, j int) bool { + if newReferredFKList[i].ChildSchema.L != newReferredFKList[j].ChildSchema.L { + return newReferredFKList[i].ChildSchema.L < newReferredFKList[j].ChildSchema.L + } + if newReferredFKList[i].ChildTable.L != newReferredFKList[j].ChildTable.L { + return newReferredFKList[i].ChildTable.L < newReferredFKList[j].ChildTable.L + } + return newReferredFKList[i].ChildFKName.L < newReferredFKList[j].ChildFKName.L + }) + is.referredForeignKeyMap[refer] = newReferredFKList + } +} + +func (is *infoSchema) deleteReferredForeignKeys(schema model.CIStr, tbInfo *model.TableInfo) { + for _, fk := range tbInfo.ForeignKeys { + if fk.Version < model.FKVersion1 { + continue + } + refer := SchemaAndTableName{schema: fk.RefSchema.L, table: fk.RefTable.L} + referredFKList := is.referredForeignKeyMap[refer] + if len(referredFKList) == 0 { + continue + } + newReferredFKList := make([]*model.ReferredFKInfo, 0, len(referredFKList)-1) + for _, referredFK := range referredFKList { + if referredFK.ChildSchema.L == schema.L && referredFK.ChildTable.L == tbInfo.Name.L && referredFK.ChildFKName.L == fk.Name.L { + continue + } + newReferredFKList = append(newReferredFKList, referredFK) + } + is.referredForeignKeyMap[refer] = newReferredFKList + } +} + +// GetTableReferredForeignKeys gets the table's ReferredFKInfo by lowercase schema and table name. +func (is *infoSchema) GetTableReferredForeignKeys(schema, table string) []*model.ReferredFKInfo { + name := SchemaAndTableName{schema: schema, table: table} + return is.referredForeignKeyMap[name] +} + +// SessionTables store local temporary tables +type SessionTables struct { + // Session tables can be accessed after the db is dropped, so there needs a way to retain the DBInfo. + // schemaTables.dbInfo will only be used when the db is dropped and it may be stale after the db is created again. + // But it's fine because we only need its name. + schemaMap map[string]*schemaTables + idx2table map[int64]table.Table +} + +// NewSessionTables creates a new NewSessionTables object +func NewSessionTables() *SessionTables { + return &SessionTables{ + schemaMap: make(map[string]*schemaTables), + idx2table: make(map[int64]table.Table), + } +} + +// TableByName get table by name +func (is *SessionTables) TableByName(schema, table model.CIStr) (table.Table, bool) { + if tbNames, ok := is.schemaMap[schema.L]; ok { + if t, ok := tbNames.tables[table.L]; ok { + return t, true + } + } + return nil, false +} + +// TableExists check if table with the name exists +func (is *SessionTables) TableExists(schema, table model.CIStr) (ok bool) { + _, ok = is.TableByName(schema, table) + return +} + +// TableByID get table by table id +func (is *SessionTables) TableByID(id int64) (tbl table.Table, ok bool) { + tbl, ok = is.idx2table[id] + return +} + +// AddTable add a table +func (is *SessionTables) AddTable(db *model.DBInfo, tbl table.Table) error { + schemaTables := is.ensureSchema(db) + + tblMeta := tbl.Meta() + if _, ok := schemaTables.tables[tblMeta.Name.L]; ok { + return ErrTableExists.GenWithStackByArgs(tblMeta.Name) + } + + if _, ok := is.idx2table[tblMeta.ID]; ok { + return ErrTableExists.GenWithStackByArgs(tblMeta.Name) + } + + schemaTables.tables[tblMeta.Name.L] = tbl + is.idx2table[tblMeta.ID] = tbl + + return nil +} + +// RemoveTable remove a table +func (is *SessionTables) RemoveTable(schema, table model.CIStr) (exist bool) { + tbls := is.schemaTables(schema) + if tbls == nil { + return false + } + + oldTable, exist := tbls.tables[table.L] + if !exist { + return false + } + + delete(tbls.tables, table.L) + delete(is.idx2table, oldTable.Meta().ID) + if len(tbls.tables) == 0 { + delete(is.schemaMap, schema.L) + } + return true +} + +// Count gets the count of the temporary tables. +func (is *SessionTables) Count() int { + return len(is.idx2table) +} + +// SchemaByTable get a table's schema name +func (is *SessionTables) SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) { + if tableInfo == nil { + return nil, false + } + + for _, v := range is.schemaMap { + if tbl, ok := v.tables[tableInfo.Name.L]; ok { + if tbl.Meta().ID == tableInfo.ID { + return v.dbInfo, true + } + } + } + + return nil, false +} + +func (is *SessionTables) ensureSchema(db *model.DBInfo) *schemaTables { + if tbls, ok := is.schemaMap[db.Name.L]; ok { + return tbls + } + + tbls := &schemaTables{dbInfo: db, tables: make(map[string]table.Table)} + is.schemaMap[db.Name.L] = tbls + return tbls +} + +func (is *SessionTables) schemaTables(schema model.CIStr) *schemaTables { + if is.schemaMap == nil { + return nil + } + + if tbls, ok := is.schemaMap[schema.L]; ok { + return tbls + } + + return nil +} + +// SessionExtendedInfoSchema implements InfoSchema +// Local temporary table has a loose relationship with database. +// So when a database is dropped, its temporary tables still exist and can be returned by TableByName/TableByID. +type SessionExtendedInfoSchema struct { + InfoSchema + LocalTemporaryTablesOnce sync.Once + LocalTemporaryTables *SessionTables + MdlTables *SessionTables +} + +// TableByName implements InfoSchema.TableByName +func (ts *SessionExtendedInfoSchema) TableByName(schema, table model.CIStr) (table.Table, error) { + if ts.LocalTemporaryTables != nil { + if tbl, ok := ts.LocalTemporaryTables.TableByName(schema, table); ok { + return tbl, nil + } + } + + if ts.MdlTables != nil { + if tbl, ok := ts.MdlTables.TableByName(schema, table); ok { + return tbl, nil + } + } + + return ts.InfoSchema.TableByName(schema, table) +} + +// TableByID implements InfoSchema.TableByID +func (ts *SessionExtendedInfoSchema) TableByID(id int64) (table.Table, bool) { + if ts.LocalTemporaryTables != nil { + if tbl, ok := ts.LocalTemporaryTables.TableByID(id); ok { + return tbl, true + } + } + + if ts.MdlTables != nil { + if tbl, ok := ts.MdlTables.TableByID(id); ok { + return tbl, true + } + } + + return ts.InfoSchema.TableByID(id) +} + +// SchemaByTable implements InfoSchema.SchemaByTable, it returns a stale DBInfo even if it's dropped. +func (ts *SessionExtendedInfoSchema) SchemaByTable(tableInfo *model.TableInfo) (*model.DBInfo, bool) { + if tableInfo == nil { + return nil, false + } + + if ts.LocalTemporaryTables != nil { + if db, ok := ts.LocalTemporaryTables.SchemaByTable(tableInfo); ok { + return db, true + } + } + + if ts.MdlTables != nil { + if tbl, ok := ts.MdlTables.SchemaByTable(tableInfo); ok { + return tbl, true + } + } + + return ts.InfoSchema.SchemaByTable(tableInfo) +} + +// UpdateTableInfo implements InfoSchema.SchemaByTable. +func (ts *SessionExtendedInfoSchema) UpdateTableInfo(db *model.DBInfo, tableInfo table.Table) error { + if ts.MdlTables == nil { + ts.MdlTables = NewSessionTables() + } + err := ts.MdlTables.AddTable(db, tableInfo) + if err != nil { + return err + } + return nil +} + +// HasTemporaryTable returns whether information schema has temporary table +func (ts *SessionExtendedInfoSchema) HasTemporaryTable() bool { + return ts.LocalTemporaryTables != nil && ts.LocalTemporaryTables.Count() > 0 || ts.InfoSchema.HasTemporaryTable() +} + +// DetachTemporaryTableInfoSchema returns a new SessionExtendedInfoSchema without temporary tables +func (ts *SessionExtendedInfoSchema) DetachTemporaryTableInfoSchema() *SessionExtendedInfoSchema { + return &SessionExtendedInfoSchema{ + InfoSchema: ts.InfoSchema, + MdlTables: ts.MdlTables, + } +} + +// FindTableByTblOrPartID looks for table.Table for the given id in the InfoSchema. +// The id can be either a table id or a partition id. +// If the id is a table id, the corresponding table.Table will be returned, and the second return value is nil. +// If the id is a partition id, the corresponding table.Table and PartitionDefinition will be returned. +// If the id is not found in the InfoSchema, nil will be returned for both return values. +func FindTableByTblOrPartID(is InfoSchema, id int64) (table.Table, *model.PartitionDefinition) { + tbl, ok := is.TableByID(id) + if ok { + return tbl, nil + } + tbl, _, partDef := is.FindTableByPartitionID(id) + return tbl, partDef +} diff --git a/pkg/infoschema/infoschema_test.go b/pkg/infoschema/infoschema_test.go new file mode 100644 index 0000000000000..aa8be2f5d61de --- /dev/null +++ b/pkg/infoschema/infoschema_test.go @@ -0,0 +1,853 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package infoschema_test + +import ( + "context" + "encoding/json" + "strings" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestBasic(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + // Make sure it calls perfschema.Init(). + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + + dbName := model.NewCIStr("Test") + tbName := model.NewCIStr("T") + colName := model.NewCIStr("A") + idxName := model.NewCIStr("idx") + noexist := model.NewCIStr("noexist") + + colID, err := genGlobalID(store) + require.NoError(t, err) + colInfo := &model.ColumnInfo{ + ID: colID, + Name: colName, + Offset: 0, + FieldType: *types.NewFieldType(mysql.TypeLonglong), + State: model.StatePublic, + } + + idxInfo := &model.IndexInfo{ + Name: idxName, + Table: tbName, + Columns: []*model.IndexColumn{ + { + Name: colName, + Offset: 0, + Length: 10, + }, + }, + Unique: true, + Primary: true, + State: model.StatePublic, + } + + tbID, err := genGlobalID(store) + require.NoError(t, err) + tblInfo := &model.TableInfo{ + ID: tbID, + Name: tbName, + Columns: []*model.ColumnInfo{colInfo}, + Indices: []*model.IndexInfo{idxInfo}, + State: model.StatePublic, + } + + dbID, err := genGlobalID(store) + require.NoError(t, err) + dbInfo := &model.DBInfo{ + ID: dbID, + Name: dbName, + Tables: []*model.TableInfo{tblInfo}, + State: model.StatePublic, + } + + dbInfos := []*model.DBInfo{dbInfo} + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + err := meta.NewMeta(txn).CreateDatabase(dbInfo) + require.NoError(t, err) + return errors.Trace(err) + }) + require.NoError(t, err) + + builder, err := infoschema.NewBuilder(dom.Store(), nil).InitWithDBInfos(dbInfos, nil, nil, 1) + require.NoError(t, err) + + txn, err := store.Begin() + require.NoError(t, err) + checkApplyCreateNonExistsSchemaDoesNotPanic(t, txn, builder) + checkApplyCreateNonExistsTableDoesNotPanic(t, txn, builder, dbID) + err = txn.Rollback() + require.NoError(t, err) + + is := builder.Build() + + schemaNames := is.AllSchemaNames() + require.Len(t, schemaNames, 4) + require.True(t, testutil.CompareUnorderedStringSlice(schemaNames, []string{util.InformationSchemaName.O, util.MetricSchemaName.O, util.PerformanceSchemaName.O, "Test"})) + + schemas := is.AllSchemas() + require.Len(t, schemas, 4) + schemas = is.Clone() + require.Len(t, schemas, 4) + + require.True(t, is.SchemaExists(dbName)) + require.False(t, is.SchemaExists(noexist)) + + schema, ok := is.SchemaByID(dbID) + require.True(t, ok) + require.NotNil(t, schema) + + schema, ok = is.SchemaByID(tbID) + require.False(t, ok) + require.Nil(t, schema) + + schema, ok = is.SchemaByName(dbName) + require.True(t, ok) + require.NotNil(t, schema) + + schema, ok = is.SchemaByName(noexist) + require.False(t, ok) + require.Nil(t, schema) + + schema, ok = is.SchemaByTable(tblInfo) + require.True(t, ok) + require.NotNil(t, schema) + + noexistTblInfo := &model.TableInfo{ID: 12345, Name: tblInfo.Name} + schema, ok = is.SchemaByTable(noexistTblInfo) + require.False(t, ok) + require.Nil(t, schema) + + require.True(t, is.TableExists(dbName, tbName)) + require.False(t, is.TableExists(dbName, noexist)) + require.False(t, is.TableIsView(dbName, tbName)) + require.False(t, is.TableIsSequence(dbName, tbName)) + + tb, ok := is.TableByID(tbID) + require.True(t, ok) + require.NotNil(t, tb) + + tb, ok = is.TableByID(dbID) + require.False(t, ok) + require.Nil(t, tb) + + alloc, ok := is.AllocByID(tbID) + require.True(t, ok) + require.NotNil(t, alloc) + + tb, err = is.TableByName(dbName, tbName) + require.NoError(t, err) + require.NotNil(t, tb) + + _, err = is.TableByName(dbName, noexist) + require.Error(t, err) + + tbs := is.SchemaTables(dbName) + require.Len(t, tbs, 1) + + tbs = is.SchemaTables(noexist) + require.Len(t, tbs, 0) + + // Make sure partitions table exists + tb, err = is.TableByName(model.NewCIStr("information_schema"), model.NewCIStr("partitions")) + require.NoError(t, err) + require.NotNil(t, tb) + + err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + err := meta.NewMeta(txn).CreateTableOrView(dbID, tblInfo) + require.NoError(t, err) + return errors.Trace(err) + }) + require.NoError(t, err) + txn, err = store.Begin() + require.NoError(t, err) + _, err = builder.ApplyDiff(meta.NewMeta(txn), &model.SchemaDiff{Type: model.ActionRenameTable, SchemaID: dbID, TableID: tbID, OldSchemaID: dbID}) + require.NoError(t, err) + err = txn.Rollback() + require.NoError(t, err) + is = builder.Build() + schema, ok = is.SchemaByID(dbID) + require.True(t, ok) + require.Equal(t, 1, len(schema.Tables)) +} + +func TestMockInfoSchema(t *testing.T) { + tblID := int64(1234) + tblName := model.NewCIStr("tbl_m") + tableInfo := &model.TableInfo{ + ID: tblID, + Name: tblName, + State: model.StatePublic, + } + colInfo := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 0, + Name: model.NewCIStr("h"), + FieldType: *types.NewFieldType(mysql.TypeLong), + ID: 1, + } + tableInfo.Columns = []*model.ColumnInfo{colInfo} + is := infoschema.MockInfoSchema([]*model.TableInfo{tableInfo}) + tbl, ok := is.TableByID(tblID) + require.True(t, ok) + require.Equal(t, tblName, tbl.Meta().Name) + require.Equal(t, colInfo, tbl.Cols()[0].ColumnInfo) +} + +func checkApplyCreateNonExistsSchemaDoesNotPanic(t *testing.T, txn kv.Transaction, builder *infoschema.Builder) { + m := meta.NewMeta(txn) + _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateSchema, SchemaID: 999}) + require.True(t, infoschema.ErrDatabaseNotExists.Equal(err)) +} + +func checkApplyCreateNonExistsTableDoesNotPanic(t *testing.T, txn kv.Transaction, builder *infoschema.Builder, dbID int64) { + m := meta.NewMeta(txn) + _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: dbID, TableID: 999}) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) +} + +// TestInfoTables makes sure that all tables of information_schema could be found in infoschema handle. +func TestInfoTables(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos(nil, nil, nil, 0) + require.NoError(t, err) + is := builder.Build() + + infoTables := []string{ + "SCHEMATA", + "TABLES", + "COLUMNS", + "STATISTICS", + "CHARACTER_SETS", + "COLLATIONS", + "FILES", + "PROFILING", + "PARTITIONS", + "KEY_COLUMN_USAGE", + "REFERENTIAL_CONSTRAINTS", + "SESSION_VARIABLES", + "PLUGINS", + "TABLE_CONSTRAINTS", + "TRIGGERS", + "USER_PRIVILEGES", + "ENGINES", + "VIEWS", + "ROUTINES", + "SCHEMA_PRIVILEGES", + "COLUMN_PRIVILEGES", + "TABLE_PRIVILEGES", + "PARAMETERS", + "EVENTS", + "GLOBAL_STATUS", + "GLOBAL_VARIABLES", + "SESSION_STATUS", + "OPTIMIZER_TRACE", + "TABLESPACES", + "COLLATION_CHARACTER_SET_APPLICABILITY", + "PROCESSLIST", + "TIDB_TRX", + "DEADLOCKS", + "PLACEMENT_POLICIES", + "TRX_SUMMARY", + "RESOURCE_GROUPS", + } + for _, tbl := range infoTables { + tb, err1 := is.TableByName(util.InformationSchemaName, model.NewCIStr(tbl)) + require.Nil(t, err1) + require.NotNil(t, tb) + } +} + +func genGlobalID(store kv.Storage) (int64, error) { + var globalID int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + var err error + globalID, err = meta.NewMeta(txn).GenGlobalID() + return errors.Trace(err) + }) + return globalID, errors.Trace(err) +} + +func TestBuildSchemaWithGlobalTemporaryTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + is := dom.InfoSchema() + require.False(t, is.HasTemporaryTable()) + db, ok := is.SchemaByName(model.NewCIStr("test")) + require.True(t, ok) + + doChange := func(changes ...func(m *meta.Meta, builder *infoschema.Builder)) infoschema.InfoSchema { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + curIs := is + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + for _, change := range changes { + builder := infoschema.NewBuilder(store, nil).InitWithOldInfoSchema(curIs) + change(m, builder) + curIs = builder.Build() + } + return nil + }) + require.NoError(t, err) + return curIs + } + + createGlobalTemporaryTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { + return func(m *meta.Meta, builder *infoschema.Builder) { + err := m.CreateTableOrView(db.ID, &model.TableInfo{ + ID: tblID, + TempTableType: model.TempTableGlobal, + State: model.StatePublic, + }) + require.NoError(t, err) + _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: db.ID, TableID: tblID}) + require.NoError(t, err) + } + } + + createNormalTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { + return func(m *meta.Meta, builder *infoschema.Builder) { + err := m.CreateTableOrView(db.ID, &model.TableInfo{ + ID: tblID, + State: model.StatePublic, + }) + require.NoError(t, err) + _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: db.ID, TableID: tblID}) + require.NoError(t, err) + } + } + + dropTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { + return func(m *meta.Meta, builder *infoschema.Builder) { + err := m.DropTableOrView(db.ID, tblID) + require.NoError(t, err) + _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionDropTable, SchemaID: db.ID, TableID: tblID}) + require.NoError(t, err) + } + } + + truncateGlobalTemporaryTableChange := func(tblID, newTblID int64) func(m *meta.Meta, builder *infoschema.Builder) { + return func(m *meta.Meta, builder *infoschema.Builder) { + err := m.DropTableOrView(db.ID, tblID) + require.NoError(t, err) + + err = m.CreateTableOrView(db.ID, &model.TableInfo{ + ID: newTblID, + TempTableType: model.TempTableGlobal, + State: model.StatePublic, + }) + require.NoError(t, err) + _, err = builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionTruncateTable, SchemaID: db.ID, OldTableID: tblID, TableID: newTblID}) + require.NoError(t, err) + } + } + + alterTableChange := func(tblID int64) func(m *meta.Meta, builder *infoschema.Builder) { + return func(m *meta.Meta, builder *infoschema.Builder) { + _, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionAddColumn, SchemaID: db.ID, TableID: tblID}) + require.NoError(t, err) + } + } + + // create table + tbID, err := genGlobalID(store) + require.NoError(t, err) + newIS := doChange( + createGlobalTemporaryTableChange(tbID), + ) + require.True(t, newIS.HasTemporaryTable()) + + // full load + newDB, ok := newIS.SchemaByName(model.NewCIStr("test")) + require.True(t, ok) + builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos([]*model.DBInfo{newDB}, newIS.AllPlacementPolicies(), newIS.AllResourceGroups(), newIS.SchemaMetaVersion()) + require.NoError(t, err) + require.True(t, builder.Build().HasTemporaryTable()) + + // create and then drop + tbID, err = genGlobalID(store) + require.NoError(t, err) + require.False(t, doChange( + createGlobalTemporaryTableChange(tbID), + dropTableChange(tbID), + ).HasTemporaryTable()) + + // create and then alter + tbID, err = genGlobalID(store) + require.NoError(t, err) + require.True(t, doChange( + createGlobalTemporaryTableChange(tbID), + alterTableChange(tbID), + ).HasTemporaryTable()) + + // create and truncate + tbID, err = genGlobalID(store) + require.NoError(t, err) + newTbID, err := genGlobalID(store) + require.NoError(t, err) + require.True(t, doChange( + createGlobalTemporaryTableChange(tbID), + truncateGlobalTemporaryTableChange(tbID, newTbID), + ).HasTemporaryTable()) + + // create two and drop one + tbID, err = genGlobalID(store) + require.NoError(t, err) + tbID2, err := genGlobalID(store) + require.NoError(t, err) + require.True(t, doChange( + createGlobalTemporaryTableChange(tbID), + createGlobalTemporaryTableChange(tbID2), + dropTableChange(tbID), + ).HasTemporaryTable()) + + // create temporary and then create normal + tbID, err = genGlobalID(store) + require.NoError(t, err) + tbID2, err = genGlobalID(store) + require.NoError(t, err) + require.True(t, doChange( + createGlobalTemporaryTableChange(tbID), + createNormalTableChange(tbID2), + ).HasTemporaryTable()) +} + +func TestBuildBundle(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + tk.MustExec("create placement policy p1 followers=1") + tk.MustExec("create placement policy p2 followers=2") + tk.MustExec(`create table t1(a int primary key) placement policy p1 partition by range(a) ( + partition p1 values less than (10) placement policy p2, + partition p2 values less than (20) + )`) + tk.MustExec("create table t2(a int)") + defer func() { + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + }() + + is := domain.GetDomain(tk.Session()).InfoSchema() + db, ok := is.SchemaByName(model.NewCIStr("test")) + require.True(t, ok) + + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + + var p1 model.PartitionDefinition + for _, par := range tbl1.Meta().Partition.Definitions { + if par.Name.L == "p1" { + p1 = par + break + } + } + require.NotNil(t, p1) + + var tb1Bundle, p1Bundle *placement.Bundle + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL) + require.NoError(t, kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) (err error) { + m := meta.NewMeta(txn) + tb1Bundle, err = placement.NewTableBundle(m, tbl1.Meta()) + require.NoError(t, err) + require.NotNil(t, tb1Bundle) + + p1Bundle, err = placement.NewPartitionBundle(m, p1) + require.NoError(t, err) + require.NotNil(t, p1Bundle) + return + })) + + assertBundle := func(checkIS infoschema.InfoSchema, id int64, expected *placement.Bundle) { + actual, ok := checkIS.PlacementBundleByPhysicalTableID(id) + if expected == nil { + require.False(t, ok) + return + } + + expectedJSON, err := json.Marshal(expected) + require.NoError(t, err) + actualJSON, err := json.Marshal(actual) + require.NoError(t, err) + require.Equal(t, string(expectedJSON), string(actualJSON)) + } + + assertBundle(is, tbl1.Meta().ID, tb1Bundle) + assertBundle(is, tbl2.Meta().ID, nil) + assertBundle(is, p1.ID, p1Bundle) + + builder, err := infoschema.NewBuilder(store, nil).InitWithDBInfos([]*model.DBInfo{db}, is.AllPlacementPolicies(), is.AllResourceGroups(), is.SchemaMetaVersion()) + require.NoError(t, err) + is2 := builder.Build() + assertBundle(is2, tbl1.Meta().ID, tb1Bundle) + assertBundle(is2, tbl2.Meta().ID, nil) + assertBundle(is2, p1.ID, p1Bundle) +} + +func TestLocalTemporaryTables(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + createNewSchemaInfo := func(schemaName string) *model.DBInfo { + schemaID, err := genGlobalID(store) + require.NoError(t, err) + return &model.DBInfo{ + ID: schemaID, + Name: model.NewCIStr(schemaName), + State: model.StatePublic, + } + } + + createNewTable := func(schemaID int64, tbName string, tempType model.TempTableType) table.Table { + colID, err := genGlobalID(store) + require.NoError(t, err) + + colInfo := &model.ColumnInfo{ + ID: colID, + Name: model.NewCIStr("col1"), + Offset: 0, + FieldType: *types.NewFieldType(mysql.TypeLonglong), + State: model.StatePublic, + } + + tbID, err := genGlobalID(store) + require.NoError(t, err) + + tblInfo := &model.TableInfo{ + ID: tbID, + Name: model.NewCIStr(tbName), + Columns: []*model.ColumnInfo{colInfo}, + Indices: []*model.IndexInfo{}, + State: model.StatePublic, + } + + allocs := autoid.NewAllocatorsFromTblInfo(store, schemaID, tblInfo) + tbl, err := table.TableFromMeta(allocs, tblInfo) + require.NoError(t, err) + + return tbl + } + + assertTableByName := func(sc *infoschema.SessionTables, schemaName, tableName string, schema *model.DBInfo, tb table.Table) { + got, ok := sc.TableByName(model.NewCIStr(schemaName), model.NewCIStr(tableName)) + if tb == nil { + require.Nil(t, schema) + require.False(t, ok) + require.Nil(t, got) + } else { + require.NotNil(t, schema) + require.True(t, ok) + require.Equal(t, tb, got) + } + } + + assertTableExists := func(sc *infoschema.SessionTables, schemaName, tableName string, exists bool) { + got := sc.TableExists(model.NewCIStr(schemaName), model.NewCIStr(tableName)) + require.Equal(t, exists, got) + } + + assertTableByID := func(sc *infoschema.SessionTables, tbID int64, schema *model.DBInfo, tb table.Table) { + got, ok := sc.TableByID(tbID) + if tb == nil { + require.Nil(t, schema) + require.False(t, ok) + require.Nil(t, got) + } else { + require.NotNil(t, schema) + require.True(t, ok) + require.Equal(t, tb, got) + } + } + + assertSchemaByTable := func(sc *infoschema.SessionTables, db *model.DBInfo, tb *model.TableInfo) { + got, ok := sc.SchemaByTable(tb) + if db == nil { + require.Nil(t, got) + require.False(t, ok) + } else { + require.NotNil(t, got) + require.Equal(t, db.Name.L, got.Name.L) + require.True(t, ok) + } + } + + sc := infoschema.NewSessionTables() + db1 := createNewSchemaInfo("db1") + tb11 := createNewTable(db1.ID, "tb1", model.TempTableLocal) + tb12 := createNewTable(db1.ID, "Tb2", model.TempTableLocal) + tb13 := createNewTable(db1.ID, "tb3", model.TempTableLocal) + + // db1b has the same name with db1 + db1b := createNewSchemaInfo("db1") + tb15 := createNewTable(db1b.ID, "tb5", model.TempTableLocal) + tb16 := createNewTable(db1b.ID, "tb6", model.TempTableLocal) + tb17 := createNewTable(db1b.ID, "tb7", model.TempTableLocal) + + db2 := createNewSchemaInfo("db2") + tb21 := createNewTable(db2.ID, "tb1", model.TempTableLocal) + tb22 := createNewTable(db2.ID, "TB2", model.TempTableLocal) + tb24 := createNewTable(db2.ID, "tb4", model.TempTableLocal) + + prepareTables := []struct { + db *model.DBInfo + tb table.Table + }{ + {db1, tb11}, {db1, tb12}, {db1, tb13}, + {db1b, tb15}, {db1b, tb16}, {db1b, tb17}, + {db2, tb21}, {db2, tb22}, {db2, tb24}, + } + + for _, p := range prepareTables { + err = sc.AddTable(p.db, p.tb) + require.NoError(t, err) + } + + // test exist tables + for _, p := range prepareTables { + dbName := p.db.Name + tbName := p.tb.Meta().Name + + assertTableByName(sc, dbName.O, tbName.O, p.db, p.tb) + assertTableByName(sc, dbName.L, tbName.L, p.db, p.tb) + assertTableByName( + sc, + strings.ToUpper(dbName.L[:1])+dbName.L[1:], + strings.ToUpper(tbName.L[:1])+tbName.L[1:], + p.db, p.tb, + ) + + assertTableExists(sc, dbName.O, tbName.O, true) + assertTableExists(sc, dbName.L, tbName.L, true) + assertTableExists( + sc, + strings.ToUpper(dbName.L[:1])+dbName.L[1:], + strings.ToUpper(tbName.L[:1])+tbName.L[1:], + true, + ) + + assertTableByID(sc, p.tb.Meta().ID, p.db, p.tb) + assertSchemaByTable(sc, p.db, p.tb.Meta()) + } + + // test add dup table + err = sc.AddTable(db1, tb11) + require.True(t, infoschema.ErrTableExists.Equal(err)) + err = sc.AddTable(db1b, tb15) + require.True(t, infoschema.ErrTableExists.Equal(err)) + err = sc.AddTable(db1b, tb11) + require.True(t, infoschema.ErrTableExists.Equal(err)) + db1c := createNewSchemaInfo("db1") + err = sc.AddTable(db1c, createNewTable(db1c.ID, "tb1", model.TempTableLocal)) + require.True(t, infoschema.ErrTableExists.Equal(err)) + err = sc.AddTable(db1b, tb11) + require.True(t, infoschema.ErrTableExists.Equal(err)) + + // failed add has no effect + assertTableByName(sc, db1.Name.L, tb11.Meta().Name.L, db1, tb11) + + // delete some tables + require.True(t, sc.RemoveTable(model.NewCIStr("db1"), model.NewCIStr("tb1"))) + require.True(t, sc.RemoveTable(model.NewCIStr("Db2"), model.NewCIStr("tB2"))) + require.False(t, sc.RemoveTable(model.NewCIStr("db1"), model.NewCIStr("tbx"))) + require.False(t, sc.RemoveTable(model.NewCIStr("dbx"), model.NewCIStr("tbx"))) + + // test non exist tables by name + for _, c := range []struct{ dbName, tbName string }{ + {"db1", "tb1"}, {"db1", "tb4"}, {"db1", "tbx"}, + {"db2", "tb2"}, {"db2", "tb3"}, {"db2", "tbx"}, + {"dbx", "tb1"}, + } { + assertTableByName(sc, c.dbName, c.tbName, nil, nil) + assertTableExists(sc, c.dbName, c.tbName, false) + } + + // test non exist tables by id + nonExistID, err := genGlobalID(store) + require.NoError(t, err) + + for _, id := range []int64{nonExistID, tb11.Meta().ID, tb22.Meta().ID} { + assertTableByID(sc, id, nil, nil) + } + + // test non exist table schemaByTable + assertSchemaByTable(sc, nil, tb11.Meta()) + assertSchemaByTable(sc, nil, tb22.Meta()) + assertSchemaByTable(sc, nil, nil) + + // test SessionExtendedInfoSchema + dbTest := createNewSchemaInfo("test") + tmpTbTestA := createNewTable(dbTest.ID, "tba", model.TempTableLocal) + normalTbTestA := createNewTable(dbTest.ID, "tba", model.TempTableNone) + normalTbTestB := createNewTable(dbTest.ID, "tbb", model.TempTableNone) + normalTbTestC := createNewTable(db1.ID, "tbc", model.TempTableNone) + + is := &infoschema.SessionExtendedInfoSchema{ + InfoSchema: infoschema.MockInfoSchema([]*model.TableInfo{normalTbTestA.Meta(), normalTbTestB.Meta()}), + LocalTemporaryTables: sc, + } + + err = sc.AddTable(dbTest, tmpTbTestA) + require.NoError(t, err) + + // test TableByName + tbl, err := is.TableByName(dbTest.Name, normalTbTestA.Meta().Name) + require.NoError(t, err) + require.Equal(t, tmpTbTestA, tbl) + tbl, err = is.TableByName(dbTest.Name, normalTbTestB.Meta().Name) + require.NoError(t, err) + require.Equal(t, normalTbTestB.Meta(), tbl.Meta()) + tbl, err = is.TableByName(db1.Name, tb11.Meta().Name) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + require.Nil(t, tbl) + tbl, err = is.TableByName(db1.Name, tb12.Meta().Name) + require.NoError(t, err) + require.Equal(t, tb12, tbl) + + // test TableByID + tbl, ok := is.TableByID(normalTbTestA.Meta().ID) + require.True(t, ok) + require.Equal(t, normalTbTestA.Meta(), tbl.Meta()) + tbl, ok = is.TableByID(normalTbTestB.Meta().ID) + require.True(t, ok) + require.Equal(t, normalTbTestB.Meta(), tbl.Meta()) + tbl, ok = is.TableByID(tmpTbTestA.Meta().ID) + require.True(t, ok) + require.Equal(t, tmpTbTestA, tbl) + tbl, ok = is.TableByID(tb12.Meta().ID) + require.True(t, ok) + require.Equal(t, tb12, tbl) + + // test SchemaByTable + info, ok := is.SchemaByTable(normalTbTestA.Meta()) + require.True(t, ok) + require.Equal(t, dbTest.Name.L, info.Name.L) + info, ok = is.SchemaByTable(normalTbTestB.Meta()) + require.True(t, ok) + require.Equal(t, dbTest.Name.L, info.Name.L) + info, ok = is.SchemaByTable(tmpTbTestA.Meta()) + require.True(t, ok) + require.Equal(t, dbTest.Name.L, info.Name.L) + // SchemaByTable also returns DBInfo when the schema is not in the infoSchema but the table is an existing tmp table. + info, ok = is.SchemaByTable(tb12.Meta()) + require.True(t, ok) + require.Equal(t, db1.Name.L, info.Name.L) + // SchemaByTable returns nil when the schema is not in the infoSchema and the table is an non-existing normal table. + info, ok = is.SchemaByTable(normalTbTestC.Meta()) + require.False(t, ok) + require.Nil(t, info) + // SchemaByTable returns nil when the schema is not in the infoSchema and the table is an non-existing tmp table. + info, ok = is.SchemaByTable(tb22.Meta()) + require.False(t, ok) + require.Nil(t, info) +} + +// TestInfoSchemaCreateTableLike tests the table's column ID and index ID for memory database. +func TestInfoSchemaCreateTableLike(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table vi like information_schema.variables_info;") + tk.MustExec("alter table vi modify min_value varchar(32);") + tk.MustExec("create table u like metrics_schema.up;") + tk.MustExec("alter table u modify job int;") + tk.MustExec("create table so like performance_schema.setup_objects;") + tk.MustExec("alter table so modify object_name int;") + + tk.MustExec("create table t1 like information_schema.variables_info;") + tk.MustExec("alter table t1 add column c varchar(32);") + is := domain.GetDomain(tk.Session()).InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tblInfo := tbl.Meta() + require.Equal(t, tblInfo.Columns[8].Name.O, "c") + require.Equal(t, tblInfo.Columns[8].ID, int64(9)) + tk.MustExec("alter table t1 add index idx(c);") + is = domain.GetDomain(tk.Session()).InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tblInfo = tbl.Meta() + require.Equal(t, tblInfo.Indices[0].Name.O, "idx") + require.Equal(t, tblInfo.Indices[0].ID, int64(1)) + + // metrics_schema + tk.MustExec("create table t2 like metrics_schema.up;") + tk.MustExec("alter table t2 add column c varchar(32);") + is = domain.GetDomain(tk.Session()).InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tblInfo = tbl.Meta() + require.Equal(t, tblInfo.Columns[4].Name.O, "c") + require.Equal(t, tblInfo.Columns[4].ID, int64(5)) + tk.MustExec("alter table t2 add index idx(c);") + is = domain.GetDomain(tk.Session()).InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tblInfo = tbl.Meta() + require.Equal(t, tblInfo.Indices[0].Name.O, "idx") + require.Equal(t, tblInfo.Indices[0].ID, int64(1)) +} diff --git a/pkg/infoschema/internal/BUILD.bazel b/pkg/infoschema/internal/BUILD.bazel new file mode 100644 index 0000000000000..8f0d97544e2bf --- /dev/null +++ b/pkg/infoschema/internal/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = ["testkit.go"], + importpath = "github.com/pingcap/tidb/pkg/infoschema/internal", + visibility = ["//pkg/infoschema:__subpackages__"], + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/infoschema/internal/testkit.go b/pkg/infoschema/internal/testkit.go similarity index 100% rename from infoschema/internal/testkit.go rename to pkg/infoschema/internal/testkit.go diff --git a/pkg/infoschema/main_test.go b/pkg/infoschema/main_test.go new file mode 100644 index 0000000000000..579aa9f7bdd61 --- /dev/null +++ b/pkg/infoschema/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/infoschema/metric_table_def.go b/pkg/infoschema/metric_table_def.go similarity index 100% rename from infoschema/metric_table_def.go rename to pkg/infoschema/metric_table_def.go diff --git a/pkg/infoschema/metrics/BUILD.bazel b/pkg/infoschema/metrics/BUILD.bazel new file mode 100644 index 0000000000000..0ef9350eefaed --- /dev/null +++ b/pkg/infoschema/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/infoschema/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/infoschema/metrics/metrics.go b/pkg/infoschema/metrics/metrics.go new file mode 100644 index 0000000000000..cb62e7014d0fc --- /dev/null +++ b/pkg/infoschema/metrics/metrics.go @@ -0,0 +1,58 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// infoschema metrics vars +var ( + GetLatestCounter prometheus.Counter + GetTSCounter prometheus.Counter + GetVersionCounter prometheus.Counter + + HitLatestCounter prometheus.Counter + HitTSCounter prometheus.Counter + HitVersionCounter prometheus.Counter + + LoadSchemaCounterSnapshot prometheus.Counter + + LoadSchemaDurationTotal prometheus.Observer + LoadSchemaDurationLoadDiff prometheus.Observer + LoadSchemaDurationLoadAll prometheus.Observer +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init infoschema metrics vars. +func InitMetricsVars() { + GetLatestCounter = metrics.InfoCacheCounters.WithLabelValues("get", "latest") + GetTSCounter = metrics.InfoCacheCounters.WithLabelValues("get", "ts") + GetVersionCounter = metrics.InfoCacheCounters.WithLabelValues("get", "version") + + HitLatestCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "latest") + HitTSCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "ts") + HitVersionCounter = metrics.InfoCacheCounters.WithLabelValues("hit", "version") + + LoadSchemaCounterSnapshot = metrics.LoadSchemaCounter.WithLabelValues("snapshot") + + LoadSchemaDurationTotal = metrics.LoadSchemaDuration.WithLabelValues("total") + LoadSchemaDurationLoadDiff = metrics.LoadSchemaDuration.WithLabelValues("load-diff") + LoadSchemaDurationLoadAll = metrics.LoadSchemaDuration.WithLabelValues("load-all") +} diff --git a/infoschema/metrics_schema.go b/pkg/infoschema/metrics_schema.go similarity index 94% rename from infoschema/metrics_schema.go rename to pkg/infoschema/metrics_schema.go index d18efa63d0eb1..70a885a639c6d 100644 --- a/infoschema/metrics_schema.go +++ b/pkg/infoschema/metrics_schema.go @@ -22,13 +22,13 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/set" ) const ( diff --git a/infoschema/metrics_schema_test.go b/pkg/infoschema/metrics_schema_test.go similarity index 97% rename from infoschema/metrics_schema_test.go rename to pkg/infoschema/metrics_schema_test.go index 5e8e4606351ba..c47f77a1b3a9b 100644 --- a/infoschema/metrics_schema_test.go +++ b/pkg/infoschema/metrics_schema_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/util/set" "github.com/prometheus/prometheus/promql" "github.com/stretchr/testify/require" ) diff --git a/pkg/infoschema/perfschema/BUILD.bazel b/pkg/infoschema/perfschema/BUILD.bazel new file mode 100644 index 0000000000000..ef3a6d0fa45a8 --- /dev/null +++ b/pkg/infoschema/perfschema/BUILD.bazel @@ -0,0 +1,57 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "perfschema", + srcs = [ + "const.go", + "init.go", + "tables.go", + ], + importpath = "github.com/pingcap/tidb/pkg/infoschema/perfschema", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/table", + "//pkg/table/tables", + "//pkg/types", + "//pkg/util", + "//pkg/util/profile", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + ], +) + +go_test( + name = "perfschema_test", + timeout = "short", + srcs = [ + "main_test.go", + "tables_test.go", + ], + data = glob(["testdata/**"]), + embed = [":perfschema"], + flaky = True, + shard_count = 5, + deps = [ + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/infoschema/perfschema/const.go b/pkg/infoschema/perfschema/const.go similarity index 100% rename from infoschema/perfschema/const.go rename to pkg/infoschema/perfschema/const.go diff --git a/infoschema/perfschema/init.go b/pkg/infoschema/perfschema/init.go similarity index 84% rename from infoschema/perfschema/init.go rename to pkg/infoschema/perfschema/init.go index 05d486dbcf1c0..386486da429e4 100644 --- a/infoschema/perfschema/init.go +++ b/pkg/infoschema/perfschema/init.go @@ -18,15 +18,15 @@ import ( "fmt" "sync" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util" ) var once sync.Once diff --git a/pkg/infoschema/perfschema/main_test.go b/pkg/infoschema/perfschema/main_test.go new file mode 100644 index 0000000000000..87f785f3390b7 --- /dev/null +++ b/pkg/infoschema/perfschema/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package perfschema + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/infoschema/perfschema/tables.go b/pkg/infoschema/perfschema/tables.go new file mode 100644 index 0000000000000..3cab9bf16847f --- /dev/null +++ b/pkg/infoschema/perfschema/tables.go @@ -0,0 +1,414 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package perfschema + +import ( + "cmp" + "context" + "fmt" + "net/http" + "slices" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/profile" +) + +const ( + tableNameGlobalStatus = "global_status" + tableNameSessionStatus = "session_status" + tableNameSetupActors = "setup_actors" + tableNameSetupObjects = "setup_objects" + tableNameSetupInstruments = "setup_instruments" + tableNameSetupConsumers = "setup_consumers" + tableNameEventsStatementsCurrent = "events_statements_current" + tableNameEventsStatementsHistory = "events_statements_history" + tableNameEventsStatementsHistoryLong = "events_statements_history_long" + tableNamePreparedStatementsInstances = "prepared_statements_instances" + tableNameEventsTransactionsCurrent = "events_transactions_current" + tableNameEventsTransactionsHistory = "events_transactions_history" + tableNameEventsTransactionsHistoryLong = "events_transactions_history_long" + tableNameEventsStagesCurrent = "events_stages_current" + tableNameEventsStagesHistory = "events_stages_history" + tableNameEventsStagesHistoryLong = "events_stages_history_long" + tableNameEventsStatementsSummaryByDigest = "events_statements_summary_by_digest" + tableNameTiDBProfileCPU = "tidb_profile_cpu" + tableNameTiDBProfileMemory = "tidb_profile_memory" + tableNameTiDBProfileMutex = "tidb_profile_mutex" + tableNameTiDBProfileAllocs = "tidb_profile_allocs" + tableNameTiDBProfileBlock = "tidb_profile_block" + tableNameTiDBProfileGoroutines = "tidb_profile_goroutines" + tableNameTiKVProfileCPU = "tikv_profile_cpu" + tableNamePDProfileCPU = "pd_profile_cpu" + tableNamePDProfileMemory = "pd_profile_memory" + tableNamePDProfileMutex = "pd_profile_mutex" + tableNamePDProfileAllocs = "pd_profile_allocs" + tableNamePDProfileBlock = "pd_profile_block" + tableNamePDProfileGoroutines = "pd_profile_goroutines" + tableNameSessionAccountConnectAttrs = "session_account_connect_attrs" + tableNameSessionConnectAttrs = "session_connect_attrs" + tableNameSessionVariables = "session_variables" +) + +var tableIDMap = map[string]int64{ + tableNameGlobalStatus: autoid.PerformanceSchemaDBID + 1, + tableNameSessionStatus: autoid.PerformanceSchemaDBID + 2, + tableNameSetupActors: autoid.PerformanceSchemaDBID + 3, + tableNameSetupObjects: autoid.PerformanceSchemaDBID + 4, + tableNameSetupInstruments: autoid.PerformanceSchemaDBID + 5, + tableNameSetupConsumers: autoid.PerformanceSchemaDBID + 6, + tableNameEventsStatementsCurrent: autoid.PerformanceSchemaDBID + 7, + tableNameEventsStatementsHistory: autoid.PerformanceSchemaDBID + 8, + tableNameEventsStatementsHistoryLong: autoid.PerformanceSchemaDBID + 9, + tableNamePreparedStatementsInstances: autoid.PerformanceSchemaDBID + 10, + tableNameEventsTransactionsCurrent: autoid.PerformanceSchemaDBID + 11, + tableNameEventsTransactionsHistory: autoid.PerformanceSchemaDBID + 12, + tableNameEventsTransactionsHistoryLong: autoid.PerformanceSchemaDBID + 13, + tableNameEventsStagesCurrent: autoid.PerformanceSchemaDBID + 14, + tableNameEventsStagesHistory: autoid.PerformanceSchemaDBID + 15, + tableNameEventsStagesHistoryLong: autoid.PerformanceSchemaDBID + 16, + tableNameEventsStatementsSummaryByDigest: autoid.PerformanceSchemaDBID + 17, + tableNameTiDBProfileCPU: autoid.PerformanceSchemaDBID + 18, + tableNameTiDBProfileMemory: autoid.PerformanceSchemaDBID + 19, + tableNameTiDBProfileMutex: autoid.PerformanceSchemaDBID + 20, + tableNameTiDBProfileAllocs: autoid.PerformanceSchemaDBID + 21, + tableNameTiDBProfileBlock: autoid.PerformanceSchemaDBID + 22, + tableNameTiDBProfileGoroutines: autoid.PerformanceSchemaDBID + 23, + tableNameTiKVProfileCPU: autoid.PerformanceSchemaDBID + 24, + tableNamePDProfileCPU: autoid.PerformanceSchemaDBID + 25, + tableNamePDProfileMemory: autoid.PerformanceSchemaDBID + 26, + tableNamePDProfileMutex: autoid.PerformanceSchemaDBID + 27, + tableNamePDProfileAllocs: autoid.PerformanceSchemaDBID + 28, + tableNamePDProfileBlock: autoid.PerformanceSchemaDBID + 29, + tableNamePDProfileGoroutines: autoid.PerformanceSchemaDBID + 30, + tableNameSessionVariables: autoid.PerformanceSchemaDBID + 31, + tableNameSessionConnectAttrs: autoid.PerformanceSchemaDBID + 32, + tableNameSessionAccountConnectAttrs: autoid.PerformanceSchemaDBID + 33, +} + +// perfSchemaTable stands for the fake table all its data is in the memory. +type perfSchemaTable struct { + infoschema.VirtualTable + meta *model.TableInfo + cols []*table.Column + tp table.Type + indices []table.Index +} + +var pluginTable = make(map[string]func(autoid.Allocators, *model.TableInfo) (table.Table, error)) + +// IsPredefinedTable judges whether this table is predefined. +func IsPredefinedTable(tableName string) bool { + _, ok := tableIDMap[strings.ToLower(tableName)] + return ok +} + +func tableFromMeta(allocs autoid.Allocators, meta *model.TableInfo) (table.Table, error) { + if f, ok := pluginTable[meta.Name.L]; ok { + ret, err := f(allocs, meta) + return ret, err + } + return createPerfSchemaTable(meta) +} + +// createPerfSchemaTable creates all perfSchemaTables +func createPerfSchemaTable(meta *model.TableInfo) (*perfSchemaTable, error) { + columns := make([]*table.Column, 0, len(meta.Columns)) + for _, colInfo := range meta.Columns { + col := table.ToColumn(colInfo) + columns = append(columns, col) + } + tp := table.VirtualTable + t := &perfSchemaTable{ + meta: meta, + cols: columns, + tp: tp, + } + if err := initTableIndices(t); err != nil { + return nil, err + } + return t, nil +} + +// Cols implements table.Table Type interface. +func (vt *perfSchemaTable) Cols() []*table.Column { + return vt.cols +} + +// VisibleCols implements table.Table VisibleCols interface. +func (vt *perfSchemaTable) VisibleCols() []*table.Column { + return vt.cols +} + +// HiddenCols implements table.Table HiddenCols interface. +func (vt *perfSchemaTable) HiddenCols() []*table.Column { + return nil +} + +// WritableCols implements table.Table Type interface. +func (vt *perfSchemaTable) WritableCols() []*table.Column { + return vt.cols +} + +// DeletableCols implements table.Table Type interface. +func (vt *perfSchemaTable) DeletableCols() []*table.Column { + return vt.cols +} + +// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. +func (vt *perfSchemaTable) FullHiddenColsAndVisibleCols() []*table.Column { + return vt.cols +} + +// GetPhysicalID implements table.Table GetID interface. +func (vt *perfSchemaTable) GetPhysicalID() int64 { + return vt.meta.ID +} + +// Meta implements table.Table Type interface. +func (vt *perfSchemaTable) Meta() *model.TableInfo { + return vt.meta +} + +// Type implements table.Table Type interface. +func (vt *perfSchemaTable) Type() table.Type { + return vt.tp +} + +// Indices implements table.Table Indices interface. +func (vt *perfSchemaTable) Indices() []table.Index { + return vt.indices +} + +// GetPartitionedTable implements table.Table GetPartitionedTable interface. +func (vt *perfSchemaTable) GetPartitionedTable() table.PartitionedTable { + return nil +} + +// initTableIndices initializes the indices of the perfSchemaTable. +func initTableIndices(t *perfSchemaTable) error { + tblInfo := t.meta + for _, idxInfo := range tblInfo.Indices { + if idxInfo.State == model.StateNone { + return table.ErrIndexStateCantNone.GenWithStackByArgs(idxInfo.Name) + } + idx := tables.NewIndex(t.meta.ID, tblInfo, idxInfo) + t.indices = append(t.indices, idx) + } + return nil +} + +func (vt *perfSchemaTable) getRows(ctx context.Context, sctx sessionctx.Context, cols []*table.Column) (fullRows [][]types.Datum, err error) { + switch vt.meta.Name.O { + case tableNameTiDBProfileCPU: + fullRows, err = (&profile.Collector{}).ProfileGraph("cpu") + case tableNameTiDBProfileMemory: + fullRows, err = (&profile.Collector{}).ProfileGraph("heap") + case tableNameTiDBProfileMutex: + fullRows, err = (&profile.Collector{}).ProfileGraph("mutex") + case tableNameTiDBProfileAllocs: + fullRows, err = (&profile.Collector{}).ProfileGraph("allocs") + case tableNameTiDBProfileBlock: + fullRows, err = (&profile.Collector{}).ProfileGraph("block") + case tableNameTiDBProfileGoroutines: + fullRows, err = (&profile.Collector{}).ProfileGraph("goroutine") + case tableNameTiKVProfileCPU: + interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) + fullRows, err = dataForRemoteProfile(sctx, "tikv", "/debug/pprof/profile?seconds="+interval, false) + case tableNamePDProfileCPU: + interval := fmt.Sprintf("%d", profile.CPUProfileInterval/time.Second) + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/profile?seconds="+interval, false) + case tableNamePDProfileMemory: + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/heap", false) + case tableNamePDProfileMutex: + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/mutex", false) + case tableNamePDProfileAllocs: + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/allocs", false) + case tableNamePDProfileBlock: + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/block", false) + case tableNamePDProfileGoroutines: + fullRows, err = dataForRemoteProfile(sctx, "pd", "/pd/api/v1/debug/pprof/goroutine?debug=2", true) + case tableNameSessionVariables: + fullRows, err = infoschema.GetDataFromSessionVariables(ctx, sctx) + case tableNameSessionConnectAttrs: + fullRows, err = infoschema.GetDataFromSessionConnectAttrs(sctx, false) + case tableNameSessionAccountConnectAttrs: + fullRows, err = infoschema.GetDataFromSessionConnectAttrs(sctx, true) + } + if err != nil { + return + } + if len(cols) == len(vt.cols) { + return + } + rows := make([][]types.Datum, len(fullRows)) + for i, fullRow := range fullRows { + row := make([]types.Datum, len(cols)) + for j, col := range cols { + row[j] = fullRow[col.Offset] + } + rows[i] = row + } + return rows, nil +} + +// IterRecords implements table.Table IterRecords interface. +func (vt *perfSchemaTable) IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error { + rows, err := vt.getRows(ctx, sctx, cols) + if err != nil { + return err + } + for i, row := range rows { + more, err := fn(kv.IntHandle(i), row, cols) + if err != nil { + return err + } + if !more { + break + } + } + return nil +} + +func dataForRemoteProfile(ctx sessionctx.Context, nodeType, uri string, isGoroutine bool) ([][]types.Datum, error) { + var ( + servers []infoschema.ServerInfo + err error + ) + switch nodeType { + case "tikv": + servers, err = infoschema.GetStoreServerInfo(ctx) + case "pd": + servers, err = infoschema.GetPDServerInfo(ctx) + default: + return nil, errors.Errorf("%s does not support profile remote component", nodeType) + } + failpoint.Inject("mockRemoteNodeStatusAddress", func(val failpoint.Value) { + // The cluster topology is injected by `failpoint` expression and + // there is no extra checks for it. (let the test fail if the expression invalid) + if s := val.(string); len(s) > 0 { + servers = servers[:0] + for _, server := range strings.Split(s, ";") { + parts := strings.Split(server, ",") + if parts[0] != nodeType { + continue + } + servers = append(servers, infoschema.ServerInfo{ + ServerType: parts[0], + Address: parts[1], + StatusAddr: parts[2], + }) + } + // erase error + err = nil + } + }) + if err != nil { + return nil, errors.Trace(err) + } + + type result struct { + addr string + rows [][]types.Datum + err error + } + + wg := sync.WaitGroup{} + ch := make(chan result, len(servers)) + for _, server := range servers { + statusAddr := server.StatusAddr + if len(statusAddr) == 0 { + ctx.GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("TiKV node %s does not contain status address", server.Address)) + continue + } + + wg.Add(1) + go func(address string) { + util.WithRecovery(func() { + defer wg.Done() + url := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), statusAddr, uri) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + ch <- result{err: errors.Trace(err)} + return + } + // Forbidden PD follower proxy + req.Header.Add("PD-Allow-follower-handle", "true") + // TiKV output svg format in default + req.Header.Add("Content-Type", "application/protobuf") + resp, err := util.InternalHTTPClient().Do(req) + if err != nil { + ch <- result{err: errors.Trace(err)} + return + } + defer func() { + terror.Log(resp.Body.Close()) + }() + if resp.StatusCode != http.StatusOK { + ch <- result{err: errors.Errorf("request %s failed: %s", url, resp.Status)} + return + } + collector := profile.Collector{} + var rows [][]types.Datum + if isGoroutine { + rows, err = collector.ParseGoroutines(resp.Body) + } else { + rows, err = collector.ProfileReaderToDatums(resp.Body) + } + if err != nil { + ch <- result{err: errors.Trace(err)} + return + } + ch <- result{addr: address, rows: rows} + }, nil) + }(statusAddr) + } + + wg.Wait() + close(ch) + + // Keep the original order to make the result more stable + var results []result //nolint: prealloc + for result := range ch { + if result.err != nil { + ctx.GetSessionVars().StmtCtx.AppendWarning(result.err) + continue + } + results = append(results, result) + } + slices.SortFunc(results, func(i, j result) int { return cmp.Compare(i.addr, j.addr) }) + var finalRows [][]types.Datum + for _, result := range results { + addr := types.NewStringDatum(result.addr) + for _, row := range result.rows { + // Insert the node address in front of rows + finalRows = append(finalRows, append([]types.Datum{addr}, row...)) + } + } + return finalRows, nil +} diff --git a/pkg/infoschema/perfschema/tables_test.go b/pkg/infoschema/perfschema/tables_test.go new file mode 100644 index 0000000000000..2a15fe9f82c41 --- /dev/null +++ b/pkg/infoschema/perfschema/tables_test.go @@ -0,0 +1,218 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package perfschema_test + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "os" + "runtime/pprof" + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/infoschema/perfschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "go.opencensus.io/stats/view" +) + +func TestPredefinedTables(t *testing.T) { + require.True(t, perfschema.IsPredefinedTable("EVENTS_statements_summary_by_digest")) + require.False(t, perfschema.IsPredefinedTable("statements")) +} + +func TestPerfSchemaTables(t *testing.T) { + store := newMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use performance_schema") + tk.MustQuery("select * from global_status where variable_name = 'Ssl_verify_mode'").Check(testkit.Rows()) + tk.MustQuery("select * from session_status where variable_name = 'Ssl_verify_mode'").Check(testkit.Rows()) + tk.MustQuery("select * from setup_actors").Check(testkit.Rows()) + tk.MustQuery("select * from events_stages_history_long").Check(testkit.Rows()) +} + +func TestSessionVariables(t *testing.T) { + store := newMockStore(t) + tk := testkit.NewTestKit(t, store) + + res := tk.MustQuery("select variable_value from performance_schema.session_variables order by variable_name limit 10;") + tk.MustQuery("select variable_value from information_schema.session_variables order by variable_name limit 10;").Check(res.Rows()) +} + +func TestTiKVProfileCPU(t *testing.T) { + store := newMockStore(t) + + router := http.NewServeMux() + mockServer := httptest.NewServer(router) + mockAddr := strings.TrimPrefix(mockServer.URL, "http://") + defer mockServer.Close() + + // mock tikv profile + copyHandler := func(filename string) http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + file, err := os.Open(filename) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + defer func() { terror.Log(file.Close()) }() + _, err = io.Copy(w, file) + terror.Log(err) + } + } + router.HandleFunc("/debug/pprof/profile", copyHandler("testdata/tikv.cpu.profile")) + + // failpoint setting + servers := []string{ + strings.Join([]string{"tikv", mockAddr, mockAddr}, ","), + strings.Join([]string{"pd", mockAddr, mockAddr}, ","), + } + fpExpr := strings.Join(servers, ";") + fpName := "github.com/pingcap/tidb/pkg/infoschema/perfschema/mockRemoteNodeStatusAddress" + require.NoError(t, failpoint.Enable(fpName, fmt.Sprintf(`return("%s")`, fpExpr))) + defer func() { require.NoError(t, failpoint.Disable(fpName)) }() + + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use performance_schema") + result := tk.MustQuery("select function, percent_abs, percent_rel from tikv_profile_cpu where depth < 3") + + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + result.Check(testkit.Rows( + "root 100% 100%", + "├─tikv::server::load_statistics::linux::ThreadLoadStatistics::record::h59facb8d680e7794 75.00% 75.00%", + "│ └─procinfo::pid::stat::stat_task::h69e1aa2c331aebb6 75.00% 100%", + "├─nom::nom::digit::h905aaaeff7d8ec8e 16.07% 16.07%", + "│ ├─ as core::iter::traits::iterator::Iterator>::next::h16936f9061bb75e4 6.25% 38.89%", + "│ ├─Unknown 3.57% 22.22%", + "│ ├─<&u8 as nom::traits::AsChar>::is_dec_digit::he9eacc3fad26ab81 2.68% 16.67%", + "│ ├─<&[u8] as nom::traits::InputIter>::iter_indices::h6192338433683bff 1.79% 11.11%", + "│ └─<&[T] as nom::traits::Slice>>::slice::h38d31f11f84aa302 1.79% 11.11%", + "├─::realloc::h5199c50710ab6f9d 1.79% 1.79%", + "│ └─rallocx 1.79% 100%", + "├─::dealloc::hea83459aa98dd2dc 1.79% 1.79%", + "│ └─sdallocx 1.79% 100%", + "├─::alloc::hc7962e02169a5c56 0.89% 0.89%", + "│ └─mallocx 0.89% 100%", + "├─engine::rocks::util::engine_metrics::flush_engine_iostall_properties::h64a7661c95aa1db7 0.89% 0.89%", + "│ └─rocksdb::rocksdb::DB::get_map_property_cf::h9722f9040411af44 0.89% 100%", + "├─core::ptr::real_drop_in_place::h8def0d99e7136f33 0.89% 0.89%", + "│ └─ as core::ops::drop::Drop>::drop::h9b59b303bffde02c 0.89% 100%", + "├─tikv_util::metrics::threads_linux::ThreadInfoStatistics::record::ha8cc290b3f46af88 0.89% 0.89%", + "│ └─procinfo::pid::stat::stat_task::h69e1aa2c331aebb6 0.89% 100%", + "├─crossbeam_utils::backoff::Backoff::snooze::h5c121ef4ce616a3c 0.89% 0.89%", + "│ └─core::iter::range::>::next::hdb23ceb766e7a91f 0.89% 100%", + "└─::next::he129c78b3deb639d 0.89% 0.89%", + " └─Unknown 0.89% 100%")) + + // We can use current processe profile to mock profile of PD because the PD has the + // same way of retrieving profile with TiDB. And the purpose of this test case is used + // to make sure all profile HTTP API have been accessed. + accessed := map[string]struct{}{} + handlerFactory := func(name string, debug ...int) func(w http.ResponseWriter, _ *http.Request) { + debugLevel := 0 + if len(debug) > 0 { + debugLevel = debug[0] + } + return func(w http.ResponseWriter, _ *http.Request) { + profile := pprof.Lookup(name) + if profile == nil { + http.Error(w, fmt.Sprintf("profile %s not found", name), http.StatusBadRequest) + return + } + if err := profile.WriteTo(w, debugLevel); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + accessed[name] = struct{}{} + } + } + + // mock PD profile + router.HandleFunc("/pd/api/v1/debug/pprof/profile", copyHandler("testdata/test.pprof")) + router.HandleFunc("/pd/api/v1/debug/pprof/heap", handlerFactory("heap")) + router.HandleFunc("/pd/api/v1/debug/pprof/mutex", handlerFactory("mutex")) + router.HandleFunc("/pd/api/v1/debug/pprof/allocs", handlerFactory("allocs")) + router.HandleFunc("/pd/api/v1/debug/pprof/block", handlerFactory("block")) + router.HandleFunc("/pd/api/v1/debug/pprof/goroutine", handlerFactory("goroutine", 2)) + + tk.MustQuery("select * from pd_profile_cpu where depth < 3") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + tk.MustQuery("select * from pd_profile_memory where depth < 3") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + tk.MustQuery("select * from pd_profile_mutex where depth < 3") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + tk.MustQuery("select * from pd_profile_allocs where depth < 3") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + tk.MustQuery("select * from pd_profile_block where depth < 3") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + tk.MustQuery("select * from pd_profile_goroutines") + warnings = tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Lenf(t, warnings, 0, "expect no warnings, but found: %+v", warnings) + + require.Lenf(t, accessed, 5, "expect all HTTP API had been accessed, but found: %v", accessed) +} + +// TestSessionConnectAttrs tests the `SESSION_CONNECT_ATTRS` table +func TestSessionConnectAttrs(t *testing.T) { + sm := &testkit.MockSessionManager{} + sm.ConAttrs = map[uint64]map[string]string{ + 123456: { + "_client_name": "libmysql", + }, + } + store := newMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.Session().SetSessionManager(sm) + tk.MustQuery("SELECT PROCESSLIST_ID,ATTR_NAME,ATTR_VALUE,ORDINAL_POSITION FROM performance_schema.SESSION_CONNECT_ATTRS").Check(testkit.Rows("123456 _client_name libmysql 0")) +} + +func newMockStore(t *testing.T) kv.Storage { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + session.DisableStats4Test() + + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + + t.Cleanup(func() { + dom.Close() + err := store.Close() + require.NoError(t, err) + view.Stop() + }) + + return store +} diff --git a/infoschema/perfschema/testdata/test.pprof b/pkg/infoschema/perfschema/testdata/test.pprof similarity index 100% rename from infoschema/perfschema/testdata/test.pprof rename to pkg/infoschema/perfschema/testdata/test.pprof diff --git a/infoschema/perfschema/testdata/tikv.cpu.profile b/pkg/infoschema/perfschema/testdata/tikv.cpu.profile similarity index 100% rename from infoschema/perfschema/testdata/tikv.cpu.profile rename to pkg/infoschema/perfschema/testdata/tikv.cpu.profile diff --git a/pkg/infoschema/tables.go b/pkg/infoschema/tables.go new file mode 100644 index 0000000000000..5c0d3cb8517e2 --- /dev/null +++ b/pkg/infoschema/tables.go @@ -0,0 +1,2488 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package infoschema + +import ( + "cmp" + "context" + "encoding/json" + "fmt" + "net" + "net/http" + "slices" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/diagnosticspb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/resourcegroup" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" +) + +const ( + // TableSchemata is the string constant of infoschema table. + TableSchemata = "SCHEMATA" + // TableTables is the string constant of infoschema table. + TableTables = "TABLES" + // TableColumns is the string constant of infoschema table + TableColumns = "COLUMNS" + tableColumnStatistics = "COLUMN_STATISTICS" + // TableStatistics is the string constant of infoschema table + TableStatistics = "STATISTICS" + // TableCharacterSets is the string constant of infoschema charactersets memory table + TableCharacterSets = "CHARACTER_SETS" + // TableCollations is the string constant of infoschema collations memory table. + TableCollations = "COLLATIONS" + tableFiles = "FILES" + // CatalogVal is the string constant of TABLE_CATALOG. + CatalogVal = "def" + // TableProfiling is the string constant of infoschema table. + TableProfiling = "PROFILING" + // TablePartitions is the string constant of infoschema table. + TablePartitions = "PARTITIONS" + // TableKeyColumn is the string constant of KEY_COLUMN_USAGE. + TableKeyColumn = "KEY_COLUMN_USAGE" + // TableReferConst is the string constant of REFERENTIAL_CONSTRAINTS. + TableReferConst = "REFERENTIAL_CONSTRAINTS" + // TableSessionVar is the string constant of SESSION_VARIABLES. + TableSessionVar = "SESSION_VARIABLES" + tablePlugins = "PLUGINS" + // TableConstraints is the string constant of TABLE_CONSTRAINTS. + TableConstraints = "TABLE_CONSTRAINTS" + tableTriggers = "TRIGGERS" + // TableUserPrivileges is the string constant of infoschema user privilege table. + TableUserPrivileges = "USER_PRIVILEGES" + tableSchemaPrivileges = "SCHEMA_PRIVILEGES" + tableTablePrivileges = "TABLE_PRIVILEGES" + tableColumnPrivileges = "COLUMN_PRIVILEGES" + // TableEngines is the string constant of infoschema table. + TableEngines = "ENGINES" + // TableViews is the string constant of infoschema table. + TableViews = "VIEWS" + tableRoutines = "ROUTINES" + tableParameters = "PARAMETERS" + tableEvents = "EVENTS" + tableGlobalStatus = "GLOBAL_STATUS" + tableGlobalVariables = "GLOBAL_VARIABLES" + tableSessionStatus = "SESSION_STATUS" + tableOptimizerTrace = "OPTIMIZER_TRACE" + tableTableSpaces = "TABLESPACES" + // TableCollationCharacterSetApplicability is the string constant of infoschema memory table. + TableCollationCharacterSetApplicability = "COLLATION_CHARACTER_SET_APPLICABILITY" + // TableProcesslist is the string constant of infoschema table. + TableProcesslist = "PROCESSLIST" + // TableTiDBIndexes is the string constant of infoschema table + TableTiDBIndexes = "TIDB_INDEXES" + // TableTiDBHotRegions is the string constant of infoschema table + TableTiDBHotRegions = "TIDB_HOT_REGIONS" + // TableTiDBHotRegionsHistory is the string constant of infoschema table + TableTiDBHotRegionsHistory = "TIDB_HOT_REGIONS_HISTORY" + // TableTiKVStoreStatus is the string constant of infoschema table + TableTiKVStoreStatus = "TIKV_STORE_STATUS" + // TableAnalyzeStatus is the string constant of Analyze Status + TableAnalyzeStatus = "ANALYZE_STATUS" + // TableTiKVRegionStatus is the string constant of infoschema table + TableTiKVRegionStatus = "TIKV_REGION_STATUS" + // TableTiKVRegionPeers is the string constant of infoschema table + TableTiKVRegionPeers = "TIKV_REGION_PEERS" + // TableTiDBServersInfo is the string constant of TiDB server information table. + TableTiDBServersInfo = "TIDB_SERVERS_INFO" + // TableSlowQuery is the string constant of slow query memory table. + TableSlowQuery = "SLOW_QUERY" + // TableClusterInfo is the string constant of cluster info memory table. + TableClusterInfo = "CLUSTER_INFO" + // TableClusterConfig is the string constant of cluster configuration memory table. + TableClusterConfig = "CLUSTER_CONFIG" + // TableClusterLog is the string constant of cluster log memory table. + TableClusterLog = "CLUSTER_LOG" + // TableClusterLoad is the string constant of cluster load memory table. + TableClusterLoad = "CLUSTER_LOAD" + // TableClusterHardware is the string constant of cluster hardware table. + TableClusterHardware = "CLUSTER_HARDWARE" + // TableClusterSystemInfo is the string constant of cluster system info table. + TableClusterSystemInfo = "CLUSTER_SYSTEMINFO" + // TableTiFlashReplica is the string constant of tiflash replica table. + TableTiFlashReplica = "TIFLASH_REPLICA" + // TableInspectionResult is the string constant of inspection result table. + TableInspectionResult = "INSPECTION_RESULT" + // TableMetricTables is a table that contains all metrics table definition. + TableMetricTables = "METRICS_TABLES" + // TableMetricSummary is a summary table that contains all metrics. + TableMetricSummary = "METRICS_SUMMARY" + // TableMetricSummaryByLabel is a metric table that contains all metrics that group by label info. + TableMetricSummaryByLabel = "METRICS_SUMMARY_BY_LABEL" + // TableInspectionSummary is the string constant of inspection summary table. + TableInspectionSummary = "INSPECTION_SUMMARY" + // TableInspectionRules is the string constant of currently implemented inspection and summary rules. + TableInspectionRules = "INSPECTION_RULES" + // TableDDLJobs is the string constant of DDL job table. + TableDDLJobs = "DDL_JOBS" + // TableSequences is the string constant of all sequences created by user. + TableSequences = "SEQUENCES" + // TableStatementsSummary is the string constant of statement summary table. + TableStatementsSummary = "STATEMENTS_SUMMARY" + // TableStatementsSummaryHistory is the string constant of statements summary history table. + TableStatementsSummaryHistory = "STATEMENTS_SUMMARY_HISTORY" + // TableStatementsSummaryEvicted is the string constant of statements summary evicted table. + TableStatementsSummaryEvicted = "STATEMENTS_SUMMARY_EVICTED" + // TableStorageStats is a table that contains all tables disk usage + TableStorageStats = "TABLE_STORAGE_STATS" + // TableTiFlashTables is the string constant of tiflash tables table. + TableTiFlashTables = "TIFLASH_TABLES" + // TableTiFlashSegments is the string constant of tiflash segments table. + TableTiFlashSegments = "TIFLASH_SEGMENTS" + // TableClientErrorsSummaryGlobal is the string constant of client errors table. + TableClientErrorsSummaryGlobal = "CLIENT_ERRORS_SUMMARY_GLOBAL" + // TableClientErrorsSummaryByUser is the string constant of client errors table. + TableClientErrorsSummaryByUser = "CLIENT_ERRORS_SUMMARY_BY_USER" + // TableClientErrorsSummaryByHost is the string constant of client errors table. + TableClientErrorsSummaryByHost = "CLIENT_ERRORS_SUMMARY_BY_HOST" + // TableTiDBTrx is current running transaction status table. + TableTiDBTrx = "TIDB_TRX" + // TableDeadlocks is the string constant of deadlock table. + TableDeadlocks = "DEADLOCKS" + // TableDataLockWaits is current lock waiting status table. + TableDataLockWaits = "DATA_LOCK_WAITS" + // TableAttributes is the string constant of attributes table. + TableAttributes = "ATTRIBUTES" + // TablePlacementPolicies is the string constant of placement policies table. + TablePlacementPolicies = "PLACEMENT_POLICIES" + // TableTrxSummary is the string constant of transaction summary table. + TableTrxSummary = "TRX_SUMMARY" + // TableVariablesInfo is the string constant of variables_info table. + TableVariablesInfo = "VARIABLES_INFO" + // TableUserAttributes is the string constant of user_attributes view. + TableUserAttributes = "USER_ATTRIBUTES" + // TableMemoryUsage is the memory usage status of tidb instance. + TableMemoryUsage = "MEMORY_USAGE" + // TableMemoryUsageOpsHistory is the memory control operators history. + TableMemoryUsageOpsHistory = "MEMORY_USAGE_OPS_HISTORY" + // TableResourceGroups is the metadata of resource groups. + TableResourceGroups = "RESOURCE_GROUPS" + // TableRunawayWatches is the query list of runaway watch. + TableRunawayWatches = "RUNAWAY_WATCHES" + // TableCheckConstraints is the list of CHECK constraints. + TableCheckConstraints = "CHECK_CONSTRAINTS" +) + +const ( + // DataLockWaitsColumnKey is the name of the KEY column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnKey = "KEY" + // DataLockWaitsColumnKeyInfo is the name of the KEY_INFO column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnKeyInfo = "KEY_INFO" + // DataLockWaitsColumnTrxID is the name of the TRX_ID column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnTrxID = "TRX_ID" + // DataLockWaitsColumnCurrentHoldingTrxID is the name of the CURRENT_HOLDING_TRX_ID column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnCurrentHoldingTrxID = "CURRENT_HOLDING_TRX_ID" + // DataLockWaitsColumnSQLDigest is the name of the SQL_DIGEST column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnSQLDigest = "SQL_DIGEST" + // DataLockWaitsColumnSQLDigestText is the name of the SQL_DIGEST_TEXT column of the DATA_LOCK_WAITS table. + DataLockWaitsColumnSQLDigestText = "SQL_DIGEST_TEXT" +) + +var tableIDMap = map[string]int64{ + TableSchemata: autoid.InformationSchemaDBID + 1, + TableTables: autoid.InformationSchemaDBID + 2, + TableColumns: autoid.InformationSchemaDBID + 3, + tableColumnStatistics: autoid.InformationSchemaDBID + 4, + TableStatistics: autoid.InformationSchemaDBID + 5, + TableCharacterSets: autoid.InformationSchemaDBID + 6, + TableCollations: autoid.InformationSchemaDBID + 7, + tableFiles: autoid.InformationSchemaDBID + 8, + CatalogVal: autoid.InformationSchemaDBID + 9, + TableProfiling: autoid.InformationSchemaDBID + 10, + TablePartitions: autoid.InformationSchemaDBID + 11, + TableKeyColumn: autoid.InformationSchemaDBID + 12, + TableReferConst: autoid.InformationSchemaDBID + 13, + TableSessionVar: autoid.InformationSchemaDBID + 14, + tablePlugins: autoid.InformationSchemaDBID + 15, + TableConstraints: autoid.InformationSchemaDBID + 16, + tableTriggers: autoid.InformationSchemaDBID + 17, + TableUserPrivileges: autoid.InformationSchemaDBID + 18, + tableSchemaPrivileges: autoid.InformationSchemaDBID + 19, + tableTablePrivileges: autoid.InformationSchemaDBID + 20, + tableColumnPrivileges: autoid.InformationSchemaDBID + 21, + TableEngines: autoid.InformationSchemaDBID + 22, + TableViews: autoid.InformationSchemaDBID + 23, + tableRoutines: autoid.InformationSchemaDBID + 24, + tableParameters: autoid.InformationSchemaDBID + 25, + tableEvents: autoid.InformationSchemaDBID + 26, + tableGlobalStatus: autoid.InformationSchemaDBID + 27, + tableGlobalVariables: autoid.InformationSchemaDBID + 28, + tableSessionStatus: autoid.InformationSchemaDBID + 29, + tableOptimizerTrace: autoid.InformationSchemaDBID + 30, + tableTableSpaces: autoid.InformationSchemaDBID + 31, + TableCollationCharacterSetApplicability: autoid.InformationSchemaDBID + 32, + TableProcesslist: autoid.InformationSchemaDBID + 33, + TableTiDBIndexes: autoid.InformationSchemaDBID + 34, + TableSlowQuery: autoid.InformationSchemaDBID + 35, + TableTiDBHotRegions: autoid.InformationSchemaDBID + 36, + TableTiKVStoreStatus: autoid.InformationSchemaDBID + 37, + TableAnalyzeStatus: autoid.InformationSchemaDBID + 38, + TableTiKVRegionStatus: autoid.InformationSchemaDBID + 39, + TableTiKVRegionPeers: autoid.InformationSchemaDBID + 40, + TableTiDBServersInfo: autoid.InformationSchemaDBID + 41, + TableClusterInfo: autoid.InformationSchemaDBID + 42, + TableClusterConfig: autoid.InformationSchemaDBID + 43, + TableClusterLoad: autoid.InformationSchemaDBID + 44, + TableTiFlashReplica: autoid.InformationSchemaDBID + 45, + ClusterTableSlowLog: autoid.InformationSchemaDBID + 46, + ClusterTableProcesslist: autoid.InformationSchemaDBID + 47, + TableClusterLog: autoid.InformationSchemaDBID + 48, + TableClusterHardware: autoid.InformationSchemaDBID + 49, + TableClusterSystemInfo: autoid.InformationSchemaDBID + 50, + TableInspectionResult: autoid.InformationSchemaDBID + 51, + TableMetricSummary: autoid.InformationSchemaDBID + 52, + TableMetricSummaryByLabel: autoid.InformationSchemaDBID + 53, + TableMetricTables: autoid.InformationSchemaDBID + 54, + TableInspectionSummary: autoid.InformationSchemaDBID + 55, + TableInspectionRules: autoid.InformationSchemaDBID + 56, + TableDDLJobs: autoid.InformationSchemaDBID + 57, + TableSequences: autoid.InformationSchemaDBID + 58, + TableStatementsSummary: autoid.InformationSchemaDBID + 59, + TableStatementsSummaryHistory: autoid.InformationSchemaDBID + 60, + ClusterTableStatementsSummary: autoid.InformationSchemaDBID + 61, + ClusterTableStatementsSummaryHistory: autoid.InformationSchemaDBID + 62, + TableStorageStats: autoid.InformationSchemaDBID + 63, + TableTiFlashTables: autoid.InformationSchemaDBID + 64, + TableTiFlashSegments: autoid.InformationSchemaDBID + 65, + // Removed, see https://github.com/pingcap/tidb/issues/28890 + //TablePlacementPolicy: autoid.InformationSchemaDBID + 66, + TableClientErrorsSummaryGlobal: autoid.InformationSchemaDBID + 67, + TableClientErrorsSummaryByUser: autoid.InformationSchemaDBID + 68, + TableClientErrorsSummaryByHost: autoid.InformationSchemaDBID + 69, + TableTiDBTrx: autoid.InformationSchemaDBID + 70, + ClusterTableTiDBTrx: autoid.InformationSchemaDBID + 71, + TableDeadlocks: autoid.InformationSchemaDBID + 72, + ClusterTableDeadlocks: autoid.InformationSchemaDBID + 73, + TableDataLockWaits: autoid.InformationSchemaDBID + 74, + TableStatementsSummaryEvicted: autoid.InformationSchemaDBID + 75, + ClusterTableStatementsSummaryEvicted: autoid.InformationSchemaDBID + 76, + TableAttributes: autoid.InformationSchemaDBID + 77, + TableTiDBHotRegionsHistory: autoid.InformationSchemaDBID + 78, + TablePlacementPolicies: autoid.InformationSchemaDBID + 79, + TableTrxSummary: autoid.InformationSchemaDBID + 80, + ClusterTableTrxSummary: autoid.InformationSchemaDBID + 81, + TableVariablesInfo: autoid.InformationSchemaDBID + 82, + TableUserAttributes: autoid.InformationSchemaDBID + 83, + TableMemoryUsage: autoid.InformationSchemaDBID + 84, + TableMemoryUsageOpsHistory: autoid.InformationSchemaDBID + 85, + ClusterTableMemoryUsage: autoid.InformationSchemaDBID + 86, + ClusterTableMemoryUsageOpsHistory: autoid.InformationSchemaDBID + 87, + TableResourceGroups: autoid.InformationSchemaDBID + 88, + TableRunawayWatches: autoid.InformationSchemaDBID + 89, + TableCheckConstraints: autoid.InformationSchemaDBID + 90, +} + +// columnInfo represents the basic column information of all kinds of INFORMATION_SCHEMA tables +type columnInfo struct { + // name of column + name string + // tp is column type + tp byte + // represent size of bytes of the column + size int + // represent decimal length of the column + decimal int + // flag represent NotNull, Unsigned, PriKey flags etc. + flag uint + // deflt is default value + deflt interface{} + // comment for the column + comment string + // enumElems represent all possible literal string values of an enum column + enumElems []string +} + +func buildColumnInfo(col columnInfo) *model.ColumnInfo { + mCharset := charset.CharsetBin + mCollation := charset.CharsetBin + if col.tp == mysql.TypeVarchar || col.tp == mysql.TypeBlob || col.tp == mysql.TypeLongBlob || col.tp == mysql.TypeEnum { + mCharset = charset.CharsetUTF8MB4 + mCollation = charset.CollationUTF8MB4 + } + fieldType := types.FieldType{} + fieldType.SetType(col.tp) + fieldType.SetCharset(mCharset) + fieldType.SetCollate(mCollation) + fieldType.SetFlen(col.size) + fieldType.SetDecimal(col.decimal) + fieldType.SetFlag(col.flag) + fieldType.SetElems(col.enumElems) + return &model.ColumnInfo{ + Name: model.NewCIStr(col.name), + FieldType: fieldType, + State: model.StatePublic, + DefaultValue: col.deflt, + Comment: col.comment, + } +} + +func buildTableMeta(tableName string, cs []columnInfo) *model.TableInfo { + cols := make([]*model.ColumnInfo, 0, len(cs)) + primaryIndices := make([]*model.IndexInfo, 0, 1) + tblInfo := &model.TableInfo{ + Name: model.NewCIStr(tableName), + State: model.StatePublic, + Charset: mysql.DefaultCharset, + Collate: mysql.DefaultCollationName, + } + for offset, c := range cs { + if tblInfo.Name.O == ClusterTableSlowLog && mysql.HasPriKeyFlag(c.flag) { + switch c.tp { + case mysql.TypeLong, mysql.TypeLonglong, + mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24: + tblInfo.PKIsHandle = true + default: + tblInfo.IsCommonHandle = true + tblInfo.CommonHandleVersion = 1 + index := &model.IndexInfo{ + Name: model.NewCIStr("primary"), + State: model.StatePublic, + Primary: true, + Unique: true, + Columns: []*model.IndexColumn{ + {Name: model.NewCIStr(c.name), Offset: offset, Length: types.UnspecifiedLength}}, + } + primaryIndices = append(primaryIndices, index) + tblInfo.Indices = primaryIndices + } + } + cols = append(cols, buildColumnInfo(c)) + } + for i, col := range cols { + col.Offset = i + } + tblInfo.Columns = cols + return tblInfo +} + +var schemataCols = []columnInfo{ + {name: "CATALOG_NAME", tp: mysql.TypeVarchar, size: 512}, + {name: "SCHEMA_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "DEFAULT_CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "DEFAULT_COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "SQL_PATH", tp: mysql.TypeVarchar, size: 512}, + {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, +} + +var tablesCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, + {name: "VERSION", tp: mysql.TypeLonglong, size: 21}, + {name: "ROW_FORMAT", tp: mysql.TypeVarchar, size: 10}, + {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, + {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, + {name: "AUTO_INCREMENT", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "UPDATE_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "CHECK_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "TABLE_COLLATION", tp: mysql.TypeVarchar, size: 32, deflt: mysql.DefaultCollationName}, + {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATE_OPTIONS", tp: mysql.TypeVarchar, size: 255}, + {name: "TABLE_COMMENT", tp: mysql.TypeVarchar, size: 2048}, + {name: "TIDB_TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "TIDB_ROW_ID_SHARDING_INFO", tp: mysql.TypeVarchar, size: 255}, + {name: "TIDB_PK_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, +} + +// See: http://dev.mysql.com/doc/refman/5.7/en/information-schema-columns-table.html +var columnsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 64}, + {name: "COLUMN_DEFAULT", tp: mysql.TypeBlob, size: 196606}, + {name: "IS_NULLABLE", tp: mysql.TypeVarchar, size: 3}, + {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "NUMERIC_PRECISION", tp: mysql.TypeLonglong, size: 21}, + {name: "NUMERIC_SCALE", tp: mysql.TypeLonglong, size: 21}, + {name: "DATETIME_PRECISION", tp: mysql.TypeLonglong, size: 21}, + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "COLUMN_TYPE", tp: mysql.TypeBlob, size: 196606}, + {name: "COLUMN_KEY", tp: mysql.TypeVarchar, size: 3}, + {name: "EXTRA", tp: mysql.TypeVarchar, size: 30}, + {name: "PRIVILEGES", tp: mysql.TypeVarchar, size: 80}, + {name: "COLUMN_COMMENT", tp: mysql.TypeVarchar, size: 1024}, + {name: "GENERATION_EXPRESSION", tp: mysql.TypeBlob, size: 589779, flag: mysql.NotNullFlag}, +} + +var columnStatisticsCols = []columnInfo{ + {name: "SCHEMA_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "HISTOGRAM", tp: mysql.TypeJSON, size: 51}, +} + +var statisticsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "NON_UNIQUE", tp: mysql.TypeVarchar, size: 1}, + {name: "INDEX_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "SEQ_IN_INDEX", tp: mysql.TypeLonglong, size: 2}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 21}, + {name: "COLLATION", tp: mysql.TypeVarchar, size: 1}, + {name: "CARDINALITY", tp: mysql.TypeLonglong, size: 21}, + {name: "SUB_PART", tp: mysql.TypeLonglong, size: 3}, + {name: "PACKED", tp: mysql.TypeVarchar, size: 10}, + {name: "NULLABLE", tp: mysql.TypeVarchar, size: 3}, + {name: "INDEX_TYPE", tp: mysql.TypeVarchar, size: 16}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 16}, + {name: "INDEX_COMMENT", tp: mysql.TypeVarchar, size: 1024}, + {name: "IS_VISIBLE", tp: mysql.TypeVarchar, size: 3}, + {name: "Expression", tp: mysql.TypeVarchar, size: 64}, +} + +var profilingCols = []columnInfo{ + {name: "QUERY_ID", tp: mysql.TypeLong, size: 20}, + {name: "SEQ", tp: mysql.TypeLong, size: 20}, + {name: "STATE", tp: mysql.TypeVarchar, size: 30}, + {name: "DURATION", tp: mysql.TypeNewDecimal, size: 9}, + {name: "CPU_USER", tp: mysql.TypeNewDecimal, size: 9}, + {name: "CPU_SYSTEM", tp: mysql.TypeNewDecimal, size: 9}, + {name: "CONTEXT_VOLUNTARY", tp: mysql.TypeLong, size: 20}, + {name: "CONTEXT_INVOLUNTARY", tp: mysql.TypeLong, size: 20}, + {name: "BLOCK_OPS_IN", tp: mysql.TypeLong, size: 20}, + {name: "BLOCK_OPS_OUT", tp: mysql.TypeLong, size: 20}, + {name: "MESSAGES_SENT", tp: mysql.TypeLong, size: 20}, + {name: "MESSAGES_RECEIVED", tp: mysql.TypeLong, size: 20}, + {name: "PAGE_FAULTS_MAJOR", tp: mysql.TypeLong, size: 20}, + {name: "PAGE_FAULTS_MINOR", tp: mysql.TypeLong, size: 20}, + {name: "SWAPS", tp: mysql.TypeLong, size: 20}, + {name: "SOURCE_FUNCTION", tp: mysql.TypeVarchar, size: 30}, + {name: "SOURCE_FILE", tp: mysql.TypeVarchar, size: 20}, + {name: "SOURCE_LINE", tp: mysql.TypeLong, size: 20}, +} + +var charsetCols = []columnInfo{ + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "DEFAULT_COLLATE_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "DESCRIPTION", tp: mysql.TypeVarchar, size: 60}, + {name: "MAXLEN", tp: mysql.TypeLonglong, size: 3}, +} + +var collationsCols = []columnInfo{ + {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32}, + {name: "ID", tp: mysql.TypeLonglong, size: 11}, + {name: "IS_DEFAULT", tp: mysql.TypeVarchar, size: 3}, + {name: "IS_COMPILED", tp: mysql.TypeVarchar, size: 3}, + {name: "SORTLEN", tp: mysql.TypeLonglong, size: 3}, +} + +var keyColumnUsageCols = []columnInfo{ + {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 10, flag: mysql.NotNullFlag}, + {name: "POSITION_IN_UNIQUE_CONSTRAINT", tp: mysql.TypeLonglong, size: 10}, + {name: "REFERENCED_TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "REFERENCED_TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "REFERENCED_COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, +} + +// See http://dev.mysql.com/doc/refman/5.7/en/information-schema-referential-constraints-table.html +var referConstCols = []columnInfo{ + {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "UNIQUE_CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "UNIQUE_CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "UNIQUE_CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "MATCH_OPTION", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "UPDATE_RULE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "DELETE_RULE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "REFERENCED_TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, +} + +// See http://dev.mysql.com/doc/refman/5.7/en/information-schema-variables-table.html +var sessionVarCols = []columnInfo{ + {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, +} + +// See https://dev.mysql.com/doc/refman/5.7/en/information-schema-plugins-table.html +var pluginsCols = []columnInfo{ + {name: "PLUGIN_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "PLUGIN_VERSION", tp: mysql.TypeVarchar, size: 20}, + {name: "PLUGIN_STATUS", tp: mysql.TypeVarchar, size: 10}, + {name: "PLUGIN_TYPE", tp: mysql.TypeVarchar, size: 80}, + {name: "PLUGIN_TYPE_VERSION", tp: mysql.TypeVarchar, size: 20}, + {name: "PLUGIN_LIBRARY", tp: mysql.TypeVarchar, size: 64}, + {name: "PLUGIN_LIBRARY_VERSION", tp: mysql.TypeVarchar, size: 20}, + {name: "PLUGIN_AUTHOR", tp: mysql.TypeVarchar, size: 64}, + {name: "PLUGIN_DESCRIPTION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "PLUGIN_LICENSE", tp: mysql.TypeVarchar, size: 80}, + {name: "LOAD_OPTION", tp: mysql.TypeVarchar, size: 64}, +} + +// See https://dev.mysql.com/doc/refman/5.7/en/information-schema-partitions-table.html +var partitionsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "SUBPARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "PARTITION_ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 21}, + {name: "SUBPARTITION_ORDINAL_POSITION", tp: mysql.TypeLonglong, size: 21}, + {name: "PARTITION_METHOD", tp: mysql.TypeVarchar, size: 18}, + {name: "SUBPARTITION_METHOD", tp: mysql.TypeVarchar, size: 12}, + {name: "PARTITION_EXPRESSION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "SUBPARTITION_EXPRESSION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "PARTITION_DESCRIPTION", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, + {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATE_TIME", tp: mysql.TypeDatetime}, + {name: "UPDATE_TIME", tp: mysql.TypeDatetime}, + {name: "CHECK_TIME", tp: mysql.TypeDatetime}, + {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, + {name: "PARTITION_COMMENT", tp: mysql.TypeVarchar, size: 80}, + {name: "NODEGROUP", tp: mysql.TypeVarchar, size: 12}, + {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_PARTITION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "TIDB_PLACEMENT_POLICY_NAME", tp: mysql.TypeVarchar, size: 64}, +} + +var tableConstraintsCols = []columnInfo{ + {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "CONSTRAINT_TYPE", tp: mysql.TypeVarchar, size: 64}, +} + +var tableTriggersCols = []columnInfo{ + {name: "TRIGGER_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "TRIGGER_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TRIGGER_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "EVENT_MANIPULATION", tp: mysql.TypeVarchar, size: 6}, + {name: "EVENT_OBJECT_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "EVENT_OBJECT_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "EVENT_OBJECT_TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "ACTION_ORDER", tp: mysql.TypeLonglong, size: 4}, + {name: "ACTION_CONDITION", tp: mysql.TypeBlob, size: -1}, + {name: "ACTION_STATEMENT", tp: mysql.TypeBlob, size: -1}, + {name: "ACTION_ORIENTATION", tp: mysql.TypeVarchar, size: 9}, + {name: "ACTION_TIMING", tp: mysql.TypeVarchar, size: 6}, + {name: "ACTION_REFERENCE_OLD_TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "ACTION_REFERENCE_NEW_TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "ACTION_REFERENCE_OLD_ROW", tp: mysql.TypeVarchar, size: 3}, + {name: "ACTION_REFERENCE_NEW_ROW", tp: mysql.TypeVarchar, size: 3}, + {name: "CREATED", tp: mysql.TypeDatetime, size: 2}, + {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192}, + {name: "DEFINER", tp: mysql.TypeVarchar, size: 77}, + {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32}, + {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32}, + {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32}, +} + +var tableUserPrivilegesCols = []columnInfo{ + {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512}, + {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3}, +} + +var tableSchemaPrivilegesCols = []columnInfo{ + {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, +} + +var tableTablePrivilegesCols = []columnInfo{ + {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, +} + +var tableColumnPrivilegesCols = []columnInfo{ + {name: "GRANTEE", tp: mysql.TypeVarchar, size: 81, flag: mysql.NotNullFlag}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "PRIVILEGE_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "IS_GRANTABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, +} + +var tableEnginesCols = []columnInfo{ + {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, + {name: "SUPPORT", tp: mysql.TypeVarchar, size: 8}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 80}, + {name: "TRANSACTIONS", tp: mysql.TypeVarchar, size: 3}, + {name: "XA", tp: mysql.TypeVarchar, size: 3}, + {name: "SAVEPOINTS", tp: mysql.TypeVarchar, size: 3}, +} + +var tableViewsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "VIEW_DEFINITION", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag}, + {name: "CHECK_OPTION", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, + {name: "IS_UPDATABLE", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, + {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, + {name: "SECURITY_TYPE", tp: mysql.TypeVarchar, size: 7, flag: mysql.NotNullFlag}, + {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, +} + +var tableRoutinesCols = []columnInfo{ + {name: "SPECIFIC_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ROUTINE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "ROUTINE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ROUTINE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ROUTINE_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, + {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeLong, size: 21}, + {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeLong, size: 21}, + {name: "NUMERIC_PRECISION", tp: mysql.TypeLonglong, size: 21}, + {name: "NUMERIC_SCALE", tp: mysql.TypeLong, size: 21}, + {name: "DATETIME_PRECISION", tp: mysql.TypeLonglong, size: 21}, + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "DTD_IDENTIFIER", tp: mysql.TypeLongBlob}, + {name: "ROUTINE_BODY", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, + {name: "ROUTINE_DEFINITION", tp: mysql.TypeLongBlob}, + {name: "EXTERNAL_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "EXTERNAL_LANGUAGE", tp: mysql.TypeVarchar, size: 64}, + {name: "PARAMETER_STYLE", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, + {name: "IS_DETERMINISTIC", tp: mysql.TypeVarchar, size: 3, flag: mysql.NotNullFlag}, + {name: "SQL_DATA_ACCESS", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "SQL_PATH", tp: mysql.TypeVarchar, size: 64}, + {name: "SECURITY_TYPE", tp: mysql.TypeVarchar, size: 7, flag: mysql.NotNullFlag}, + {name: "CREATED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, + {name: "LAST_ALTERED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, + {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192, flag: mysql.NotNullFlag}, + {name: "ROUTINE_COMMENT", tp: mysql.TypeLongBlob}, + {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, + {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, +} + +var tableParametersCols = []columnInfo{ + {name: "SPECIFIC_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "SPECIFIC_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "SPECIFIC_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ORDINAL_POSITION", tp: mysql.TypeVarchar, size: 21, flag: mysql.NotNullFlag}, + {name: "PARAMETER_MODE", tp: mysql.TypeVarchar, size: 5}, + {name: "PARAMETER_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "DATA_TYPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CHARACTER_MAXIMUM_LENGTH", tp: mysql.TypeVarchar, size: 21}, + {name: "CHARACTER_OCTET_LENGTH", tp: mysql.TypeVarchar, size: 21}, + {name: "NUMERIC_PRECISION", tp: mysql.TypeVarchar, size: 21}, + {name: "NUMERIC_SCALE", tp: mysql.TypeVarchar, size: 21}, + {name: "DATETIME_PRECISION", tp: mysql.TypeVarchar, size: 21}, + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "DTD_IDENTIFIER", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag}, + {name: "ROUTINE_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, +} + +var tableEventsCols = []columnInfo{ + {name: "EVENT_CATALOG", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "EVENT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "EVENT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "DEFINER", tp: mysql.TypeVarchar, size: 77, flag: mysql.NotNullFlag}, + {name: "TIME_ZONE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "EVENT_BODY", tp: mysql.TypeVarchar, size: 8, flag: mysql.NotNullFlag}, + {name: "EVENT_DEFINITION", tp: mysql.TypeLongBlob}, + {name: "EVENT_TYPE", tp: mysql.TypeVarchar, size: 9, flag: mysql.NotNullFlag}, + {name: "EXECUTE_AT", tp: mysql.TypeDatetime}, + {name: "INTERVAL_VALUE", tp: mysql.TypeVarchar, size: 256}, + {name: "INTERVAL_FIELD", tp: mysql.TypeVarchar, size: 18}, + {name: "SQL_MODE", tp: mysql.TypeVarchar, size: 8192, flag: mysql.NotNullFlag}, + {name: "STARTS", tp: mysql.TypeDatetime}, + {name: "ENDS", tp: mysql.TypeDatetime}, + {name: "STATUS", tp: mysql.TypeVarchar, size: 18, flag: mysql.NotNullFlag}, + {name: "ON_COMPLETION", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, + {name: "CREATED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, + {name: "LAST_ALTERED", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag, deflt: "0000-00-00 00:00:00"}, + {name: "LAST_EXECUTED", tp: mysql.TypeDatetime}, + {name: "EVENT_COMMENT", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ORIGINATOR", tp: mysql.TypeLong, size: 10, flag: mysql.NotNullFlag, deflt: 0}, + {name: "CHARACTER_SET_CLIENT", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "DATABASE_COLLATION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, +} + +var tableGlobalStatusCols = []columnInfo{ + {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, +} + +var tableGlobalVariablesCols = []columnInfo{ + {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, +} + +var tableSessionStatusCols = []columnInfo{ + {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "VARIABLE_VALUE", tp: mysql.TypeVarchar, size: 1024}, +} + +var tableOptimizerTraceCols = []columnInfo{ + {name: "QUERY", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag, deflt: ""}, + {name: "TRACE", tp: mysql.TypeLongBlob, flag: mysql.NotNullFlag, deflt: ""}, + {name: "MISSING_BYTES_BEYOND_MAX_MEM_SIZE", tp: mysql.TypeShort, size: 20, flag: mysql.NotNullFlag, deflt: 0}, + {name: "INSUFFICIENT_PRIVILEGES", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, +} + +var tableTableSpacesCols = []columnInfo{ + {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, + {name: "ENGINE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, + {name: "TABLESPACE_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "LOGFILE_GROUP_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "EXTENT_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "AUTOEXTEND_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "MAXIMUM_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "NODEGROUP_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "TABLESPACE_COMMENT", tp: mysql.TypeVarchar, size: 2048}, +} + +var tableCollationCharacterSetApplicabilityCols = []columnInfo{ + {name: "COLLATION_NAME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "CHARACTER_SET_NAME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, +} + +var tableProcesslistCols = []columnInfo{ + {name: "ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, deflt: 0}, + {name: "USER", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, deflt: ""}, + {name: "HOST", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, + {name: "DB", tp: mysql.TypeVarchar, size: 64}, + {name: "COMMAND", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, deflt: ""}, + {name: "TIME", tp: mysql.TypeLong, size: 7, flag: mysql.NotNullFlag, deflt: 0}, + {name: "STATE", tp: mysql.TypeVarchar, size: 7}, + {name: "INFO", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "DIGEST", tp: mysql.TypeVarchar, size: 64, deflt: ""}, + {name: "MEM", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, + {name: "DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, + {name: "TxnStart", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, + {name: "RESOURCE_GROUP", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag, deflt: ""}, + {name: "SESSION_ALIAS", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, deflt: ""}, +} + +var tableTiDBIndexesCols = []columnInfo{ + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "NON_UNIQUE", tp: mysql.TypeLonglong, size: 21}, + {name: "KEY_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "SEQ_IN_INDEX", tp: mysql.TypeLonglong, size: 21}, + {name: "COLUMN_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "SUB_PART", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_COMMENT", tp: mysql.TypeVarchar, size: 1024}, + {name: "Expression", tp: mysql.TypeVarchar, size: 64}, + {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "IS_VISIBLE", tp: mysql.TypeVarchar, size: 64}, + {name: "CLUSTERED", tp: mysql.TypeVarchar, size: 64}, +} + +var slowQueryCols = []columnInfo{ + {name: variable.SlowLogTimeStr, tp: mysql.TypeTimestamp, size: 26, decimal: 6, flag: mysql.PriKeyFlag | mysql.NotNullFlag | mysql.BinaryFlag}, + {name: variable.SlowLogTxnStartTSStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: variable.SlowLogUserStr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogHostStr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogConnIDStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: variable.SlowLogSessAliasStr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogExecRetryCount, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: variable.SlowLogExecRetryTime, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogQueryTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogParseTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCompileTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogRewriteTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogPreprocSubQueriesStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: variable.SlowLogPreProcSubQueryTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogOptimizeTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogWaitTSTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.PreWriteTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.WaitPrewriteBinlogTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.CommitTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.GetCommitTSTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.CommitBackoffTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.BackoffTypesStr, tp: mysql.TypeVarchar, size: 64}, + {name: execdetails.ResolveLockTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.LocalLatchWaitTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.WriteKeysStr, tp: mysql.TypeLonglong, size: 22}, + {name: execdetails.WriteSizeStr, tp: mysql.TypeLonglong, size: 22}, + {name: execdetails.PrewriteRegionStr, tp: mysql.TypeLonglong, size: 22}, + {name: execdetails.TxnRetryStr, tp: mysql.TypeLonglong, size: 22}, + {name: execdetails.CopTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.ProcessTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.WaitTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.BackoffTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.LockKeysTimeStr, tp: mysql.TypeDouble, size: 22}, + {name: execdetails.RequestCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.TotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.ProcessKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.RocksdbDeleteSkippedCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.RocksdbKeySkippedCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.RocksdbBlockCacheHitCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.RocksdbBlockReadCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: execdetails.RocksdbBlockReadByteStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.UnsignedFlag}, + {name: variable.SlowLogDBStr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogIndexNamesStr, tp: mysql.TypeVarchar, size: 100}, + {name: variable.SlowLogIsInternalStr, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogDigestStr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogStatsInfoStr, tp: mysql.TypeVarchar, size: 512}, + {name: variable.SlowLogCopProcAvg, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopProcP90, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopProcMax, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopProcAddr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogCopWaitAvg, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopWaitP90, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopWaitMax, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogCopWaitAddr, tp: mysql.TypeVarchar, size: 64}, + {name: variable.SlowLogMemMax, tp: mysql.TypeLonglong, size: 20}, + {name: variable.SlowLogDiskMax, tp: mysql.TypeLonglong, size: 20}, + {name: variable.SlowLogKVTotal, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogPDTotal, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogBackoffTotal, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogWriteSQLRespTotal, tp: mysql.TypeDouble, size: 22}, + {name: variable.SlowLogResultRows, tp: mysql.TypeLonglong, size: 22}, + {name: variable.SlowLogWarnings, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: variable.SlowLogBackoffDetail, tp: mysql.TypeVarchar, size: 4096}, + {name: variable.SlowLogPrepared, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogSucc, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogIsExplicitTxn, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogIsWriteCacheTable, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogPlanFromCache, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogPlanFromBinding, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogHasMoreResults, tp: mysql.TypeTiny, size: 1}, + {name: variable.SlowLogPlan, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: variable.SlowLogPlanDigest, tp: mysql.TypeVarchar, size: 128}, + {name: variable.SlowLogBinaryPlan, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: variable.SlowLogPrevStmt, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: variable.SlowLogQuerySQLStr, tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, +} + +// TableTiDBHotRegionsCols is TiDB hot region mem table columns. +var TableTiDBHotRegionsCols = []columnInfo{ + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "MAX_HOT_DEGREE", tp: mysql.TypeLonglong, size: 21}, + {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21}, + {name: "FLOW_BYTES", tp: mysql.TypeLonglong, size: 21}, +} + +// TableTiDBHotRegionsHistoryCols is TiDB hot region history mem table columns. +var TableTiDBHotRegionsHistoryCols = []columnInfo{ + {name: "UPDATE_TIME", tp: mysql.TypeTimestamp, size: 26, decimal: 6}, + {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "PEER_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "IS_LEARNER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "IS_LEADER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "HOT_DEGREE", tp: mysql.TypeLonglong, size: 21}, + {name: "FLOW_BYTES", tp: mysql.TypeDouble, size: 22}, + {name: "KEY_RATE", tp: mysql.TypeDouble, size: 22}, + {name: "QUERY_RATE", tp: mysql.TypeDouble, size: 22}, +} + +// GetTableTiDBHotRegionsHistoryCols is to get TableTiDBHotRegionsHistoryCols. +// It is an optimization because Go does’t support const arrays. The solution is to use initialization functions. +// It is useful in the BCE optimization. +// https://go101.org/article/bounds-check-elimination.html +func GetTableTiDBHotRegionsHistoryCols() []columnInfo { + return TableTiDBHotRegionsHistoryCols +} + +// TableTiKVStoreStatusCols is TiDB kv store status columns. +var TableTiKVStoreStatusCols = []columnInfo{ + {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "ADDRESS", tp: mysql.TypeVarchar, size: 64}, + {name: "STORE_STATE", tp: mysql.TypeLonglong, size: 21}, + {name: "STORE_STATE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "LABEL", tp: mysql.TypeJSON, size: 51}, + {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, + {name: "CAPACITY", tp: mysql.TypeVarchar, size: 64}, + {name: "AVAILABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "LEADER_COUNT", tp: mysql.TypeLonglong, size: 21}, + {name: "LEADER_WEIGHT", tp: mysql.TypeDouble, size: 22}, + {name: "LEADER_SCORE", tp: mysql.TypeDouble, size: 22}, + {name: "LEADER_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21}, + {name: "REGION_WEIGHT", tp: mysql.TypeDouble, size: 22}, + {name: "REGION_SCORE", tp: mysql.TypeDouble, size: 22}, + {name: "REGION_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "START_TS", tp: mysql.TypeDatetime}, + {name: "LAST_HEARTBEAT_TS", tp: mysql.TypeDatetime}, + {name: "UPTIME", tp: mysql.TypeVarchar, size: 64}, +} + +var tableAnalyzeStatusCols = []columnInfo{ + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "JOB_INFO", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "PROCESSED_ROWS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, + {name: "START_TIME", tp: mysql.TypeDatetime}, + {name: "END_TIME", tp: mysql.TypeDatetime}, + {name: "STATE", tp: mysql.TypeVarchar, size: 64}, + {name: "FAIL_REASON", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 512}, + {name: "PROCESS_ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, + {name: "REMAINING_SECONDS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, + {name: "PROGRESS", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "ESTIMATED_TOTAL_ROWS", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, +} + +// TableTiKVRegionStatusCols is TiKV region status mem table columns. +var TableTiKVRegionStatusCols = []columnInfo{ + {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "START_KEY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, + {name: "END_KEY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "IS_INDEX", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "INDEX_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "IS_PARTITION", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "PARTITION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "PARTITION_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "EPOCH_CONF_VER", tp: mysql.TypeLonglong, size: 21}, + {name: "EPOCH_VERSION", tp: mysql.TypeLonglong, size: 21}, + {name: "WRITTEN_BYTES", tp: mysql.TypeLonglong, size: 21}, + {name: "READ_BYTES", tp: mysql.TypeLonglong, size: 21}, + {name: "APPROXIMATE_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "APPROXIMATE_KEYS", tp: mysql.TypeLonglong, size: 21}, + {name: "REPLICATIONSTATUS_STATE", tp: mysql.TypeVarchar, size: 64}, + {name: "REPLICATIONSTATUS_STATEID", tp: mysql.TypeLonglong, size: 21}, +} + +// TableTiKVRegionPeersCols is TiKV region peers mem table columns. +var TableTiKVRegionPeersCols = []columnInfo{ + {name: "REGION_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "PEER_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "STORE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "IS_LEARNER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "IS_LEADER", tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, deflt: 0}, + {name: "STATUS", tp: mysql.TypeVarchar, size: 10, deflt: 0}, + {name: "DOWN_SECONDS", tp: mysql.TypeLonglong, size: 21, deflt: 0}, +} + +// GetTableTiKVRegionPeersCols is to get TableTiKVRegionPeersCols. +// It is an optimization because Go does’t support const arrays. The solution is to use initialization functions. +// It is useful in the BCE optimization. +// https://go101.org/article/bounds-check-elimination.html +func GetTableTiKVRegionPeersCols() []columnInfo { + return TableTiKVRegionPeersCols +} + +var tableTiDBServersInfoCols = []columnInfo{ + {name: "DDL_ID", tp: mysql.TypeVarchar, size: 64}, + {name: "IP", tp: mysql.TypeVarchar, size: 64}, + {name: "PORT", tp: mysql.TypeLonglong, size: 21}, + {name: "STATUS_PORT", tp: mysql.TypeLonglong, size: 21}, + {name: "LEASE", tp: mysql.TypeVarchar, size: 64}, + {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, + {name: "GIT_HASH", tp: mysql.TypeVarchar, size: 64}, + {name: "BINLOG_STATUS", tp: mysql.TypeVarchar, size: 64}, + {name: "LABELS", tp: mysql.TypeVarchar, size: 128}, +} + +var tableClusterConfigCols = []columnInfo{ + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "KEY", tp: mysql.TypeVarchar, size: 256}, + {name: "VALUE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, +} + +var tableClusterLogCols = []columnInfo{ + {name: "TIME", tp: mysql.TypeVarchar, size: 32}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "LEVEL", tp: mysql.TypeVarchar, size: 8}, + {name: "MESSAGE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, +} + +var tableClusterLoadCols = []columnInfo{ + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "DEVICE_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "DEVICE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "NAME", tp: mysql.TypeVarchar, size: 256}, + {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, +} + +var tableClusterHardwareCols = []columnInfo{ + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "DEVICE_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "DEVICE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "NAME", tp: mysql.TypeVarchar, size: 256}, + {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, +} + +var tableClusterSystemInfoCols = []columnInfo{ + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "SYSTEM_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "SYSTEM_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "NAME", tp: mysql.TypeVarchar, size: 256}, + {name: "VALUE", tp: mysql.TypeVarchar, size: 128}, +} + +var filesCols = []columnInfo{ + {name: "FILE_ID", tp: mysql.TypeLonglong, size: 4}, + {name: "FILE_NAME", tp: mysql.TypeVarchar, size: 4000}, + {name: "FILE_TYPE", tp: mysql.TypeVarchar, size: 20}, + {name: "TABLESPACE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "LOGFILE_GROUP_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "LOGFILE_GROUP_NUMBER", tp: mysql.TypeLonglong, size: 32}, + {name: "ENGINE", tp: mysql.TypeVarchar, size: 64}, + {name: "FULLTEXT_KEYS", tp: mysql.TypeVarchar, size: 64}, + {name: "DELETED_ROWS", tp: mysql.TypeLonglong, size: 4}, + {name: "UPDATE_COUNT", tp: mysql.TypeLonglong, size: 4}, + {name: "FREE_EXTENTS", tp: mysql.TypeLonglong, size: 4}, + {name: "TOTAL_EXTENTS", tp: mysql.TypeLonglong, size: 4}, + {name: "EXTENT_SIZE", tp: mysql.TypeLonglong, size: 4}, + {name: "INITIAL_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "MAXIMUM_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "AUTOEXTEND_SIZE", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATION_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "LAST_UPDATE_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "LAST_ACCESS_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "RECOVER_TIME", tp: mysql.TypeLonglong, size: 4}, + {name: "TRANSACTION_COUNTER", tp: mysql.TypeLonglong, size: 4}, + {name: "VERSION", tp: mysql.TypeLonglong, size: 21}, + {name: "ROW_FORMAT", tp: mysql.TypeVarchar, size: 10}, + {name: "TABLE_ROWS", tp: mysql.TypeLonglong, size: 21}, + {name: "AVG_ROW_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "MAX_DATA_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "INDEX_LENGTH", tp: mysql.TypeLonglong, size: 21}, + {name: "DATA_FREE", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "UPDATE_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "CHECK_TIME", tp: mysql.TypeDatetime, size: -1}, + {name: "CHECKSUM", tp: mysql.TypeLonglong, size: 21}, + {name: "STATUS", tp: mysql.TypeVarchar, size: 20}, + {name: "EXTRA", tp: mysql.TypeVarchar, size: 255}, +} + +var tableClusterInfoCols = []columnInfo{ + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "STATUS_ADDRESS", tp: mysql.TypeVarchar, size: 64}, + {name: "VERSION", tp: mysql.TypeVarchar, size: 64}, + {name: "GIT_HASH", tp: mysql.TypeVarchar, size: 64}, + {name: "START_TIME", tp: mysql.TypeVarchar, size: 32}, + {name: "UPTIME", tp: mysql.TypeVarchar, size: 32}, + {name: "SERVER_ID", tp: mysql.TypeLonglong, size: 21, comment: "invalid if the configuration item `enable-global-kill` is set to FALSE"}, +} + +var tableTableTiFlashReplicaCols = []columnInfo{ + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "REPLICA_COUNT", tp: mysql.TypeLonglong, size: 64}, + {name: "LOCATION_LABELS", tp: mysql.TypeVarchar, size: 64}, + {name: "AVAILABLE", tp: mysql.TypeTiny, size: 1}, + {name: "PROGRESS", tp: mysql.TypeDouble, size: 22}, +} + +var tableInspectionResultCols = []columnInfo{ + {name: "RULE", tp: mysql.TypeVarchar, size: 64}, + {name: "ITEM", tp: mysql.TypeVarchar, size: 64}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "STATUS_ADDRESS", tp: mysql.TypeVarchar, size: 64}, + {name: "VALUE", tp: mysql.TypeVarchar, size: 64}, + {name: "REFERENCE", tp: mysql.TypeVarchar, size: 64}, + {name: "SEVERITY", tp: mysql.TypeVarchar, size: 64}, + {name: "DETAILS", tp: mysql.TypeVarchar, size: 256}, +} + +var tableInspectionSummaryCols = []columnInfo{ + {name: "RULE", tp: mysql.TypeVarchar, size: 64}, + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "LABEL", tp: mysql.TypeVarchar, size: 64}, + {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, + {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableInspectionRulesCols = []columnInfo{ + {name: "NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableMetricTablesCols = []columnInfo{ + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "PROMQL", tp: mysql.TypeVarchar, size: 64}, + {name: "LABELS", tp: mysql.TypeVarchar, size: 64}, + {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableMetricSummaryCols = []columnInfo{ + {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, + {name: "SUM_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableMetricSummaryByLabelCols = []columnInfo{ + {name: "INSTANCE", tp: mysql.TypeVarchar, size: 64}, + {name: "METRICS_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "LABEL", tp: mysql.TypeVarchar, size: 64}, + {name: "QUANTILE", tp: mysql.TypeDouble, size: 22}, + {name: "SUM_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "AVG_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MIN_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "MAX_VALUE", tp: mysql.TypeDouble, size: 22, decimal: 6}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableDDLJobsCols = []columnInfo{ + {name: "JOB_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "DB_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "JOB_TYPE", tp: mysql.TypeVarchar, size: 64}, + {name: "SCHEMA_STATE", tp: mysql.TypeVarchar, size: 64}, + {name: "SCHEMA_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "ROW_COUNT", tp: mysql.TypeLonglong, size: 21}, + {name: "CREATE_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "START_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "END_TIME", tp: mysql.TypeDatetime, size: 19}, + {name: "STATE", tp: mysql.TypeVarchar, size: 64}, + {name: "QUERY", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, +} + +var tableSequencesCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "SEQUENCE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "SEQUENCE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CACHE", tp: mysql.TypeTiny, flag: mysql.NotNullFlag}, + {name: "CACHE_VALUE", tp: mysql.TypeLonglong, size: 21}, + {name: "CYCLE", tp: mysql.TypeTiny, flag: mysql.NotNullFlag}, + {name: "INCREMENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MAX_VALUE", tp: mysql.TypeLonglong, size: 21}, + {name: "MIN_VALUE", tp: mysql.TypeLonglong, size: 21}, + {name: "START", tp: mysql.TypeLonglong, size: 21}, + {name: "COMMENT", tp: mysql.TypeVarchar, size: 64}, +} + +var tableStatementsSummaryCols = []columnInfo{ + {name: stmtsummary.SummaryBeginTimeStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "Begin time of this summary"}, + {name: stmtsummary.SummaryEndTimeStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "End time of this summary"}, + {name: stmtsummary.StmtTypeStr, tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag, comment: "Statement type"}, + {name: stmtsummary.SchemaNameStr, tp: mysql.TypeVarchar, size: 64, comment: "Current schema"}, + {name: stmtsummary.DigestStr, tp: mysql.TypeVarchar, size: 64}, + {name: stmtsummary.DigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag, comment: "Normalized statement"}, + {name: stmtsummary.TableNamesStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Involved tables"}, + {name: stmtsummary.IndexNamesStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Used indices"}, + {name: stmtsummary.SampleUserStr, tp: mysql.TypeVarchar, size: 64, comment: "Sampled user who executed these statements"}, + {name: stmtsummary.ExecCountStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Count of executions"}, + {name: stmtsummary.SumErrorsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of errors"}, + {name: stmtsummary.SumWarningsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of warnings"}, + {name: stmtsummary.SumLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum latency of these statements"}, + {name: stmtsummary.MaxLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of these statements"}, + {name: stmtsummary.MinLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Min latency of these statements"}, + {name: stmtsummary.AvgLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of these statements"}, + {name: stmtsummary.AvgParseLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of parsing"}, + {name: stmtsummary.MaxParseLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of parsing"}, + {name: stmtsummary.AvgCompileLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average latency of compiling"}, + {name: stmtsummary.MaxCompileLatencyStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max latency of compiling"}, + {name: stmtsummary.SumCopTaskNumStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Total number of CopTasks"}, + {name: stmtsummary.MaxCopProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max processing time of CopTasks"}, + {name: stmtsummary.MaxCopProcessAddressStr, tp: mysql.TypeVarchar, size: 256, comment: "Address of the CopTask with max processing time"}, + {name: stmtsummary.MaxCopWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time of CopTasks"}, + {name: stmtsummary.MaxCopWaitAddressStr, tp: mysql.TypeVarchar, size: 256, comment: "Address of the CopTask with max waiting time"}, + {name: stmtsummary.AvgProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average processing time in TiKV"}, + {name: stmtsummary.MaxProcessTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max processing time in TiKV"}, + {name: stmtsummary.AvgWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time in TiKV"}, + {name: stmtsummary.MaxWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time in TiKV"}, + {name: stmtsummary.AvgBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time before retry"}, + {name: stmtsummary.MaxBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time before retry"}, + {name: stmtsummary.AvgTotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of scanned keys"}, + {name: stmtsummary.MaxTotalKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of scanned keys"}, + {name: stmtsummary.AvgProcessedKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of processed keys"}, + {name: stmtsummary.MaxProcessedKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of processed keys"}, + {name: stmtsummary.AvgRocksdbDeleteSkippedCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb delete skipped count"}, + {name: stmtsummary.MaxRocksdbDeleteSkippedCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb delete skipped count"}, + {name: stmtsummary.AvgRocksdbKeySkippedCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb key skipped count"}, + {name: stmtsummary.MaxRocksdbKeySkippedCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb key skipped count"}, + {name: stmtsummary.AvgRocksdbBlockCacheHitCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block cache hit count"}, + {name: stmtsummary.MaxRocksdbBlockCacheHitCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block cache hit count"}, + {name: stmtsummary.AvgRocksdbBlockReadCountStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block read count"}, + {name: stmtsummary.MaxRocksdbBlockReadCountStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block read count"}, + {name: stmtsummary.AvgRocksdbBlockReadByteStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rocksdb block read byte"}, + {name: stmtsummary.MaxRocksdbBlockReadByteStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of rocksdb block read byte"}, + {name: stmtsummary.AvgPrewriteTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of prewrite phase"}, + {name: stmtsummary.MaxPrewriteTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of prewrite phase"}, + {name: stmtsummary.AvgCommitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of commit phase"}, + {name: stmtsummary.MaxCommitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of commit phase"}, + {name: stmtsummary.AvgGetCommitTsTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of getting commit_ts"}, + {name: stmtsummary.MaxGetCommitTsTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time of getting commit_ts"}, + {name: stmtsummary.AvgCommitBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time before retry during commit phase"}, + {name: stmtsummary.MaxCommitBackoffTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time before retry during commit phase"}, + {name: stmtsummary.AvgResolveLockTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time for resolving locks"}, + {name: stmtsummary.MaxResolveLockTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max time for resolving locks"}, + {name: stmtsummary.AvgLocalLatchWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average waiting time of local transaction"}, + {name: stmtsummary.MaxLocalLatchWaitTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max waiting time of local transaction"}, + {name: stmtsummary.AvgWriteKeysStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average count of written keys"}, + {name: stmtsummary.MaxWriteKeysStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max count of written keys"}, + {name: stmtsummary.AvgWriteSizeStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average amount of written bytes"}, + {name: stmtsummary.MaxWriteSizeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max amount of written bytes"}, + {name: stmtsummary.AvgPrewriteRegionsStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of involved regions in prewrite phase"}, + {name: stmtsummary.MaxPrewriteRegionsStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of involved regions in prewrite phase"}, + {name: stmtsummary.AvgTxnRetryStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of transaction retries"}, + {name: stmtsummary.MaxTxnRetryStr, tp: mysql.TypeLong, size: 11, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max number of transaction retries"}, + {name: stmtsummary.SumExecRetryStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum number of execution retries in pessimistic transactions"}, + {name: stmtsummary.SumExecRetryTimeStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum time of execution retries in pessimistic transactions"}, + {name: stmtsummary.SumBackoffTimesStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Sum of retries"}, + {name: stmtsummary.BackoffTypesStr, tp: mysql.TypeVarchar, size: 1024, comment: "Types of errors and the number of retries for each type"}, + {name: stmtsummary.AvgMemStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average memory(byte) used"}, + {name: stmtsummary.MaxMemStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max memory(byte) used"}, + {name: stmtsummary.AvgDiskStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average disk space(byte) used"}, + {name: stmtsummary.MaxDiskStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Max disk space(byte) used"}, + {name: stmtsummary.AvgKvTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of TiKV used"}, + {name: stmtsummary.AvgPdTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of PD used"}, + {name: stmtsummary.AvgBackoffTotalTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of Backoff used"}, + {name: stmtsummary.AvgWriteSQLRespTimeStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average time of write sql resp used"}, + {name: stmtsummary.MaxResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Max count of sql result rows"}, + {name: stmtsummary.MinResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Min count of sql result rows"}, + {name: stmtsummary.AvgResultRowsStr, tp: mysql.TypeLonglong, size: 22, flag: mysql.NotNullFlag, comment: "Average count of sql result rows"}, + {name: stmtsummary.PreparedStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether prepared"}, + {name: stmtsummary.AvgAffectedRowsStr, tp: mysql.TypeDouble, size: 22, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Average number of rows affected"}, + {name: stmtsummary.FirstSeenStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the first time"}, + {name: stmtsummary.LastSeenStr, tp: mysql.TypeTimestamp, size: 26, flag: mysql.NotNullFlag, comment: "The time these statements are seen for the last time"}, + {name: stmtsummary.PlanInCacheStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement hit plan cache"}, + {name: stmtsummary.PlanCacheHitsStr, tp: mysql.TypeLonglong, size: 20, flag: mysql.NotNullFlag, comment: "The number of times these statements hit plan cache"}, + {name: stmtsummary.PlanInBindingStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the last statement is matched with the hints in the binding"}, + {name: stmtsummary.QuerySampleTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled original statement"}, + {name: stmtsummary.PrevSampleTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The previous statement before commit"}, + {name: stmtsummary.PlanDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "Digest of its execution plan"}, + {name: stmtsummary.PlanStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled execution plan"}, + {name: stmtsummary.BinaryPlan, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Sampled binary plan"}, + {name: stmtsummary.Charset, tp: mysql.TypeVarchar, size: 64, comment: "Sampled charset"}, + {name: stmtsummary.Collation, tp: mysql.TypeVarchar, size: 64, comment: "Sampled collation"}, + {name: stmtsummary.PlanHint, tp: mysql.TypeVarchar, size: 64, comment: "Sampled plan hint"}, +} + +var tableStorageStatsCols = []columnInfo{ + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21}, + {name: "PEER_COUNT", tp: mysql.TypeLonglong, size: 21}, + {name: "REGION_COUNT", tp: mysql.TypeLonglong, size: 21, comment: "The region count of single replica of the table"}, + {name: "EMPTY_REGION_COUNT", tp: mysql.TypeLonglong, size: 21, comment: "The region count of single replica of the table"}, + {name: "TABLE_SIZE", tp: mysql.TypeLonglong, size: 64, comment: "The disk usage(MB) of single replica of the table, if the table size is empty or less than 1MB, it would show 1MB "}, + {name: "TABLE_KEYS", tp: mysql.TypeLonglong, size: 64, comment: "The count of keys of single replica of the table"}, +} + +var tableTableTiFlashTablesCols = []columnInfo{ + {name: "DATABASE", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_DATABASE", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "IS_TOMBSTONE", tp: mysql.TypeLonglong, size: 64}, + {name: "SEGMENT_COUNT", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_RATE_ROWS", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_RATE_SEGMENTS", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_PLACED_RATE", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_CACHE_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_CACHE_RATE", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_CACHE_WASTED_RATE", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_INDEX_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "AVG_SEGMENT_ROWS", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_SEGMENT_SIZE", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_COUNT", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_DELTA_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_DELTA_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "AVG_DELTA_ROWS", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_DELTA_SIZE", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_DELTA_DELETE_RANGES", tp: mysql.TypeDouble, size: 64}, + {name: "STABLE_COUNT", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_STABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_STABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "TOTAL_STABLE_SIZE_ON_DISK", tp: mysql.TypeLonglong, size: 64}, + {name: "AVG_STABLE_ROWS", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_STABLE_SIZE", tp: mysql.TypeDouble, size: 64}, + {name: "TOTAL_PACK_COUNT_IN_DELTA", tp: mysql.TypeLonglong, size: 64}, + {name: "MAX_PACK_COUNT_IN_DELTA", tp: mysql.TypeLonglong, size: 64}, + {name: "AVG_PACK_COUNT_IN_DELTA", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_PACK_ROWS_IN_DELTA", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_PACK_SIZE_IN_DELTA", tp: mysql.TypeDouble, size: 64}, + {name: "TOTAL_PACK_COUNT_IN_STABLE", tp: mysql.TypeLonglong, size: 64}, + {name: "AVG_PACK_COUNT_IN_STABLE", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_PACK_ROWS_IN_STABLE", tp: mysql.TypeDouble, size: 64}, + {name: "AVG_PACK_SIZE_IN_STABLE", tp: mysql.TypeDouble, size: 64}, + {name: "STORAGE_STABLE_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, + {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_STABLE_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, + {name: "STORAGE_DELTA_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, + {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_DELTA_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, + {name: "STORAGE_META_NUM_SNAPSHOTS", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_META_OLDEST_SNAPSHOT_LIFETIME", tp: mysql.TypeDouble, size: 64}, + {name: "STORAGE_META_OLDEST_SNAPSHOT_THREAD_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "STORAGE_META_OLDEST_SNAPSHOT_TRACING_ID", tp: mysql.TypeVarchar, size: 128}, + {name: "BACKGROUND_TASKS_LENGTH", tp: mysql.TypeLonglong, size: 64}, + {name: "TIFLASH_INSTANCE", tp: mysql.TypeVarchar, size: 64}, +} + +var tableTableTiFlashSegmentsCols = []columnInfo{ + {name: "DATABASE", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_DATABASE", tp: mysql.TypeVarchar, size: 64}, + {name: "TIDB_TABLE", tp: mysql.TypeVarchar, size: 64}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "IS_TOMBSTONE", tp: mysql.TypeLonglong, size: 64}, + {name: "SEGMENT_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "RANGE", tp: mysql.TypeVarchar, size: 64}, + {name: "EPOCH", tp: mysql.TypeLonglong, size: 64}, + {name: "ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_RATE", tp: mysql.TypeDouble, size: 64}, + {name: "DELTA_MEMTABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_MEMTABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_MEMTABLE_COLUMN_FILES", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_MEMTABLE_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_PERSISTED_PAGE_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_PERSISTED_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_PERSISTED_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_PERSISTED_COLUMN_FILES", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_PERSISTED_DELETE_RANGES", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_CACHE_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "DELTA_INDEX_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_PAGE_ID", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES_ID_0", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES_ROWS", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES_SIZE", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES_SIZE_ON_DISK", tp: mysql.TypeLonglong, size: 64}, + {name: "STABLE_DMFILES_PACKS", tp: mysql.TypeLonglong, size: 64}, + {name: "TIFLASH_INSTANCE", tp: mysql.TypeVarchar, size: 64}, +} + +var tableClientErrorsSummaryGlobalCols = []columnInfo{ + {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, + {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, + {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, +} + +var tableClientErrorsSummaryByUserCols = []columnInfo{ + {name: "USER", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, + {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, + {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, +} + +var tableClientErrorsSummaryByHostCols = []columnInfo{ + {name: "HOST", tp: mysql.TypeVarchar, size: 255, flag: mysql.NotNullFlag}, + {name: "ERROR_NUMBER", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "ERROR_MESSAGE", tp: mysql.TypeVarchar, size: 1024, flag: mysql.NotNullFlag}, + {name: "ERROR_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "WARNING_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "FIRST_SEEN", tp: mysql.TypeTimestamp, size: 26}, + {name: "LAST_SEEN", tp: mysql.TypeTimestamp, size: 26}, +} + +var tableTiDBTrxCols = []columnInfo{ + {name: txninfo.IDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag}, + {name: txninfo.StartTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "Start time of the transaction"}, + {name: txninfo.CurrentSQLDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "Digest of the sql the transaction are currently running"}, + {name: txninfo.CurrentSQLDigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The normalized sql the transaction are currently running"}, + {name: txninfo.StateStr, tp: mysql.TypeEnum, enumElems: txninfo.TxnRunningStateStrs, comment: "Current running state of the transaction"}, + {name: txninfo.WaitingStartTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "Current lock waiting's start time"}, + {name: txninfo.MemBufferKeysStr, tp: mysql.TypeLonglong, size: 64, comment: "How many entries are in MemDB"}, + {name: txninfo.MemBufferBytesStr, tp: mysql.TypeLonglong, size: 64, comment: "MemDB used memory"}, + {name: txninfo.SessionIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag, comment: "Which session this transaction belongs to"}, + {name: txninfo.UserStr, tp: mysql.TypeVarchar, size: 16, comment: "The user who open this session"}, + {name: txninfo.DBStr, tp: mysql.TypeVarchar, size: 64, comment: "The schema this transaction works on"}, + {name: txninfo.AllSQLDigestsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the digests of SQL statements that the transaction has executed"}, + {name: txninfo.RelatedTableIDsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the table IDs that the transaction has accessed"}, + {name: txninfo.WaitingTimeStr, tp: mysql.TypeDouble, size: 22, comment: "Current lock waiting time"}, +} + +var tableDeadlocksCols = []columnInfo{ + {name: deadlockhistory.ColDeadlockIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag, comment: "The ID to distinguish different deadlock events"}, + {name: deadlockhistory.ColOccurTimeStr, tp: mysql.TypeTimestamp, decimal: 6, size: 26, comment: "The physical time when the deadlock occurs"}, + {name: deadlockhistory.ColRetryableStr, tp: mysql.TypeTiny, size: 1, flag: mysql.NotNullFlag, comment: "Whether the deadlock is retryable. Retryable deadlocks are usually not reported to the client"}, + {name: deadlockhistory.ColTryLockTrxIDStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction ID (start ts) of the transaction that's trying to acquire the lock"}, + {name: deadlockhistory.ColCurrentSQLDigestStr, tp: mysql.TypeVarchar, size: 64, comment: "The digest of the SQL that's being blocked"}, + {name: deadlockhistory.ColCurrentSQLDigestTextStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The normalized SQL that's being blocked"}, + {name: deadlockhistory.ColKeyStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "The key on which a transaction is waiting for another"}, + {name: deadlockhistory.ColKeyInfoStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Information of the key"}, + {name: deadlockhistory.ColTrxHoldingLockStr, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction ID (start ts) of the transaction that's currently holding the lock"}, +} + +var tableDataLockWaitsCols = []columnInfo{ + {name: DataLockWaitsColumnKey, tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag, comment: "The key that's being waiting on"}, + {name: DataLockWaitsColumnKeyInfo, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Information of the key"}, + {name: DataLockWaitsColumnTrxID, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "Current transaction that's waiting for the lock"}, + {name: DataLockWaitsColumnCurrentHoldingTrxID, tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag | mysql.UnsignedFlag, comment: "The transaction that's holding the lock and blocks the current transaction"}, + {name: DataLockWaitsColumnSQLDigest, tp: mysql.TypeVarchar, size: 64, comment: "Digest of the SQL that's trying to acquire the lock"}, + {name: DataLockWaitsColumnSQLDigestText, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "Digest of the SQL that's trying to acquire the lock"}, +} + +var tableStatementsSummaryEvictedCols = []columnInfo{ + {name: "BEGIN_TIME", tp: mysql.TypeTimestamp, size: 26}, + {name: "END_TIME", tp: mysql.TypeTimestamp, size: 26}, + {name: "EVICTED_COUNT", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, +} + +var tableAttributesCols = []columnInfo{ + {name: "ID", tp: mysql.TypeVarchar, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, + {name: "TYPE", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag}, + {name: "ATTRIBUTES", tp: mysql.TypeVarchar, size: types.UnspecifiedLength}, + {name: "RANGES", tp: mysql.TypeBlob, size: types.UnspecifiedLength}, +} + +var tableTrxSummaryCols = []columnInfo{ + {name: "DIGEST", tp: mysql.TypeVarchar, size: 16, flag: mysql.NotNullFlag, comment: "Digest of a transaction"}, + {name: txninfo.AllSQLDigestsStr, tp: mysql.TypeBlob, size: types.UnspecifiedLength, comment: "A list of the digests of SQL statements that the transaction has executed"}, +} + +var tablePlacementPoliciesCols = []columnInfo{ + {name: "POLICY_ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "CATALOG_NAME", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "POLICY_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, // Catalog wide policy + {name: "PRIMARY_REGION", tp: mysql.TypeVarchar, size: 1024}, + {name: "REGIONS", tp: mysql.TypeVarchar, size: 1024}, + {name: "CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, + {name: "LEADER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, + {name: "FOLLOWER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, + {name: "LEARNER_CONSTRAINTS", tp: mysql.TypeVarchar, size: 1024}, + {name: "SCHEDULE", tp: mysql.TypeVarchar, size: 20}, // EVEN or MAJORITY_IN_PRIMARY + {name: "FOLLOWERS", tp: mysql.TypeLonglong, size: 64}, + {name: "LEARNERS", tp: mysql.TypeLonglong, size: 64}, +} + +var tableVariablesInfoCols = []columnInfo{ + {name: "VARIABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "VARIABLE_SCOPE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "DEFAULT_VALUE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CURRENT_VALUE", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MIN_VALUE", tp: mysql.TypeLonglong, size: 64}, + {name: "MAX_VALUE", tp: mysql.TypeLonglong, size: 64, flag: mysql.UnsignedFlag}, + {name: "POSSIBLE_VALUES", tp: mysql.TypeVarchar, size: 256}, + {name: "IS_NOOP", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, +} + +var tableUserAttributesCols = []columnInfo{ + {name: "USER", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "HOST", tp: mysql.TypeVarchar, size: 255, flag: mysql.NotNullFlag}, + {name: "ATTRIBUTE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength}, +} + +var tableMemoryUsageCols = []columnInfo{ + {name: "MEMORY_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MEMORY_LIMIT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MEMORY_CURRENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MEMORY_MAX_USED", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "CURRENT_OPS", tp: mysql.TypeVarchar, size: 50}, + {name: "SESSION_KILL_LAST", tp: mysql.TypeDatetime}, + {name: "SESSION_KILL_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "GC_LAST", tp: mysql.TypeDatetime}, + {name: "GC_TOTAL", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "DISK_USAGE", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "QUERY_FORCE_DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, +} + +var tableMemoryUsageOpsHistoryCols = []columnInfo{ + {name: "TIME", tp: mysql.TypeDatetime, size: 64, flag: mysql.NotNullFlag}, + {name: "OPS", tp: mysql.TypeVarchar, size: 20, flag: mysql.NotNullFlag}, + {name: "MEMORY_LIMIT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MEMORY_CURRENT", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "PROCESSID", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, + {name: "MEM", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, + {name: "DISK", tp: mysql.TypeLonglong, size: 21, flag: mysql.UnsignedFlag}, + {name: "CLIENT", tp: mysql.TypeVarchar, size: 64}, + {name: "DB", tp: mysql.TypeVarchar, size: 64}, + {name: "USER", tp: mysql.TypeVarchar, size: 16}, + {name: "SQL_DIGEST", tp: mysql.TypeVarchar, size: 64}, + {name: "SQL_TEXT", tp: mysql.TypeVarchar, size: 256}, +} + +var tableResourceGroupsCols = []columnInfo{ + {name: "NAME", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag}, + {name: "RU_PER_SEC", tp: mysql.TypeVarchar, size: 21}, + {name: "PRIORITY", tp: mysql.TypeVarchar, size: 6}, + {name: "BURSTABLE", tp: mysql.TypeVarchar, size: 3}, + {name: "QUERY_LIMIT", tp: mysql.TypeVarchar, size: 256}, + {name: "BACKGROUND", tp: mysql.TypeVarchar, size: 256}, +} + +var tableRunawayWatchListCols = []columnInfo{ + {name: "ID", tp: mysql.TypeLonglong, size: 64, flag: mysql.NotNullFlag}, + {name: "RESOURCE_GROUP_NAME", tp: mysql.TypeVarchar, size: resourcegroup.MaxGroupNameLength, flag: mysql.NotNullFlag}, + {name: "START_TIME", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "END_TIME", tp: mysql.TypeVarchar, size: 32}, + {name: "WATCH", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, + {name: "WATCH_TEXT", tp: mysql.TypeBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, + {name: "SOURCE", tp: mysql.TypeVarchar, size: 128, flag: mysql.NotNullFlag}, + {name: "ACTION", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, +} + +var tableCheckConstraintsCols = []columnInfo{ + {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CHECK_CLAUSE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, +} + +// GetShardingInfo returns a nil or description string for the sharding information of given TableInfo. +// The returned description string may be: +// - "NOT_SHARDED": for tables that SHARD_ROW_ID_BITS is not specified. +// - "NOT_SHARDED(PK_IS_HANDLE)": for tables of which primary key is row id. +// - "PK_AUTO_RANDOM_BITS={bit_number}, RANGE BITS={bit_number}": for tables of which primary key is sharded row id. +// - "SHARD_BITS={bit_number}": for tables that with SHARD_ROW_ID_BITS. +// +// The returned nil indicates that sharding information is not suitable for the table(for example, when the table is a View). +// This function is exported for unit test. +func GetShardingInfo(dbInfo *model.DBInfo, tableInfo *model.TableInfo) interface{} { + if dbInfo == nil || tableInfo == nil || tableInfo.IsView() || util.IsMemOrSysDB(dbInfo.Name.L) { + return nil + } + shardingInfo := "NOT_SHARDED" + if tableInfo.PKIsHandle { + if tableInfo.ContainsAutoRandomBits() { + shardingInfo = "PK_AUTO_RANDOM_BITS=" + strconv.Itoa(int(tableInfo.AutoRandomBits)) + rangeBits := tableInfo.AutoRandomRangeBits + if rangeBits != 0 && rangeBits != autoid.AutoRandomRangeBitsDefault { + shardingInfo = fmt.Sprintf("%s, RANGE BITS=%d", shardingInfo, rangeBits) + } + } else { + shardingInfo = "NOT_SHARDED(PK_IS_HANDLE)" + } + } else if tableInfo.ShardRowIDBits > 0 { + shardingInfo = "SHARD_BITS=" + strconv.Itoa(int(tableInfo.ShardRowIDBits)) + } + return shardingInfo +} + +const ( + // PrimaryKeyType is the string constant of PRIMARY KEY. + PrimaryKeyType = "PRIMARY KEY" + // PrimaryConstraint is the string constant of PRIMARY. + PrimaryConstraint = "PRIMARY" + // UniqueKeyType is the string constant of UNIQUE. + UniqueKeyType = "UNIQUE" + // ForeignKeyType is the string constant of Foreign Key. + ForeignKeyType = "FOREIGN KEY" +) + +const ( + // TiFlashWrite is the TiFlash write node in disaggregated mode. + TiFlashWrite = "tiflash_write" +) + +// ServerInfo represents the basic server information of single cluster component +type ServerInfo struct { + ServerType string + Address string + StatusAddr string + Version string + GitHash string + StartTimestamp int64 + ServerID uint64 + EngineRole string +} + +func (s *ServerInfo) isLoopBackOrUnspecifiedAddr(addr string) bool { + tcpAddr, err := net.ResolveTCPAddr("", addr) + if err != nil { + return false + } + ip := net.ParseIP(tcpAddr.IP.String()) + return ip != nil && (ip.IsUnspecified() || ip.IsLoopback()) +} + +// ResolveLoopBackAddr exports for testing. +func (s *ServerInfo) ResolveLoopBackAddr() { + if s.isLoopBackOrUnspecifiedAddr(s.Address) && !s.isLoopBackOrUnspecifiedAddr(s.StatusAddr) { + addr, err1 := net.ResolveTCPAddr("", s.Address) + statusAddr, err2 := net.ResolveTCPAddr("", s.StatusAddr) + if err1 == nil && err2 == nil { + addr.IP = statusAddr.IP + s.Address = addr.String() + } + } else if !s.isLoopBackOrUnspecifiedAddr(s.Address) && s.isLoopBackOrUnspecifiedAddr(s.StatusAddr) { + addr, err1 := net.ResolveTCPAddr("", s.Address) + statusAddr, err2 := net.ResolveTCPAddr("", s.StatusAddr) + if err1 == nil && err2 == nil { + statusAddr.IP = addr.IP + s.StatusAddr = statusAddr.String() + } + } +} + +// GetClusterServerInfo returns all components information of cluster +func GetClusterServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { + failpoint.Inject("mockClusterInfo", func(val failpoint.Value) { + // The cluster topology is injected by `failpoint` expression and + // there is no extra checks for it. (let the test fail if the expression invalid) + if s := val.(string); len(s) > 0 { + var servers []ServerInfo + for _, server := range strings.Split(s, ";") { + parts := strings.Split(server, ",") + serverID, err := strconv.ParseUint(parts[5], 10, 64) + if err != nil { + panic("convert parts[5] to uint64 failed") + } + servers = append(servers, ServerInfo{ + ServerType: parts[0], + Address: parts[1], + StatusAddr: parts[2], + Version: parts[3], + GitHash: parts[4], + ServerID: serverID, + }) + } + failpoint.Return(servers, nil) + } + }) + + type retriever func(ctx sessionctx.Context) ([]ServerInfo, error) + //nolint: prealloc + var servers []ServerInfo + for _, r := range []retriever{GetTiDBServerInfo, GetPDServerInfo, GetStoreServerInfo} { + nodes, err := r(ctx) + if err != nil { + return nil, err + } + for i := range nodes { + nodes[i].ResolveLoopBackAddr() + } + servers = append(servers, nodes...) + } + return servers, nil +} + +// GetTiDBServerInfo returns all TiDB nodes information of cluster +func GetTiDBServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { + // Get TiDB servers info. + tidbNodes, err := infosync.GetAllServerInfo(context.Background()) + if err != nil { + return nil, errors.Trace(err) + } + var isDefaultVersion bool + if len(config.GetGlobalConfig().ServerVersion) == 0 { + isDefaultVersion = true + } + var servers = make([]ServerInfo, 0, len(tidbNodes)) + for _, node := range tidbNodes { + servers = append(servers, ServerInfo{ + ServerType: "tidb", + Address: net.JoinHostPort(node.IP, strconv.Itoa(int(node.Port))), + StatusAddr: net.JoinHostPort(node.IP, strconv.Itoa(int(node.StatusPort))), + Version: FormatTiDBVersion(node.Version, isDefaultVersion), + GitHash: node.GitHash, + StartTimestamp: node.StartTimestamp, + ServerID: node.ServerIDGetter(), + }) + } + return servers, nil +} + +// FormatTiDBVersion make TiDBVersion consistent to TiKV and PD. +// The default TiDBVersion is 5.7.25-TiDB-${TiDBReleaseVersion}. +func FormatTiDBVersion(TiDBVersion string, isDefaultVersion bool) string { + var version, nodeVersion string + + // The user hasn't set the config 'ServerVersion'. + if isDefaultVersion { + nodeVersion = TiDBVersion[strings.Index(TiDBVersion, "TiDB-")+len("TiDB-"):] + if len(nodeVersion) > 0 && nodeVersion[0] == 'v' { + nodeVersion = nodeVersion[1:] + } + nodeVersions := strings.SplitN(nodeVersion, "-", 2) + if len(nodeVersions) == 1 { + version = nodeVersions[0] + } else if len(nodeVersions) >= 2 { + version = fmt.Sprintf("%s-%s", nodeVersions[0], nodeVersions[1]) + } + } else { // The user has already set the config 'ServerVersion',it would be a complex scene, so just use the 'ServerVersion' as version. + version = TiDBVersion + } + + return version +} + +// GetPDServerInfo returns all PD nodes information of cluster +func GetPDServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { + // Get PD servers info. + store := ctx.GetStore() + etcd, ok := store.(kv.EtcdBackend) + if !ok { + return nil, errors.Errorf("%T not an etcd backend", store) + } + members, err := etcd.EtcdAddrs() + if err != nil { + return nil, errors.Trace(err) + } + // TODO: maybe we should unify the PD API request interface. + var ( + memberNum = len(members) + servers = make([]ServerInfo, 0, memberNum) + errs = make([]error, 0, memberNum) + ) + if memberNum == 0 { + return servers, nil + } + // Try on each member until one succeeds or all fail. + for _, addr := range members { + // Get PD version, git_hash + url := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), addr, pdapi.Status) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + ctx.GetSessionVars().StmtCtx.AppendWarning(err) + logutil.BgLogger().Warn("create pd server info request error", zap.String("url", url), zap.Error(err)) + errs = append(errs, err) + continue + } + req.Header.Add("PD-Allow-follower-handle", "true") + resp, err := util.InternalHTTPClient().Do(req) + if err != nil { + ctx.GetSessionVars().StmtCtx.AppendWarning(err) + logutil.BgLogger().Warn("request pd server info error", zap.String("url", url), zap.Error(err)) + errs = append(errs, err) + continue + } + var content = struct { + Version string `json:"version"` + GitHash string `json:"git_hash"` + StartTimestamp int64 `json:"start_timestamp"` + }{} + err = json.NewDecoder(resp.Body).Decode(&content) + terror.Log(resp.Body.Close()) + if err != nil { + ctx.GetSessionVars().StmtCtx.AppendWarning(err) + logutil.BgLogger().Warn("close pd server info request error", zap.String("url", url), zap.Error(err)) + errs = append(errs, err) + continue + } + if len(content.Version) > 0 && content.Version[0] == 'v' { + content.Version = content.Version[1:] + } + + servers = append(servers, ServerInfo{ + ServerType: "pd", + Address: addr, + StatusAddr: addr, + Version: content.Version, + GitHash: content.GitHash, + StartTimestamp: content.StartTimestamp, + }) + } + // Return the errors if all members' requests fail. + if len(errs) == memberNum { + errorMsg := "" + for idx, err := range errs { + errorMsg += err.Error() + if idx < memberNum-1 { + errorMsg += "; " + } + } + return nil, errors.Trace(fmt.Errorf("%s", errorMsg)) + } + return servers, nil +} + +func isTiFlashStore(store *metapb.Store) bool { + for _, label := range store.Labels { + if label.GetKey() == placement.EngineLabelKey && label.GetValue() == placement.EngineLabelTiFlash { + return true + } + } + return false +} + +func isTiFlashWriteNode(store *metapb.Store) bool { + for _, label := range store.Labels { + if label.GetKey() == placement.EngineRoleLabelKey && label.GetValue() == placement.EngineRoleLabelWrite { + return true + } + } + return false +} + +// GetStoreServerInfo returns all store nodes(TiKV or TiFlash) cluster information +func GetStoreServerInfo(ctx sessionctx.Context) ([]ServerInfo, error) { + failpoint.Inject("mockStoreServerInfo", func(val failpoint.Value) { + if s := val.(string); len(s) > 0 { + var servers []ServerInfo + for _, server := range strings.Split(s, ";") { + parts := strings.Split(server, ",") + servers = append(servers, ServerInfo{ + ServerType: parts[0], + Address: parts[1], + StatusAddr: parts[2], + Version: parts[3], + GitHash: parts[4], + StartTimestamp: 0, + }) + } + failpoint.Return(servers, nil) + } + }) + + store := ctx.GetStore() + // Get TiKV servers info. + tikvStore, ok := store.(tikv.Storage) + if !ok { + return nil, errors.Errorf("%T is not an TiKV or TiFlash store instance", store) + } + pdClient := tikvStore.GetRegionCache().PDClient() + if pdClient == nil { + return nil, errors.New("pd unavailable") + } + stores, err := pdClient.GetAllStores(context.Background()) + if err != nil { + return nil, errors.Trace(err) + } + servers := make([]ServerInfo, 0, len(stores)) + for _, store := range stores { + failpoint.Inject("mockStoreTombstone", func(val failpoint.Value) { + if val.(bool) { + store.State = metapb.StoreState_Tombstone + } + }) + + if store.GetState() == metapb.StoreState_Tombstone { + continue + } + var tp string + if isTiFlashStore(store) { + tp = kv.TiFlash.Name() + } else { + tp = tikv.GetStoreTypeByMeta(store).Name() + } + var engineRole string + if isTiFlashWriteNode(store) { + engineRole = placement.EngineRoleLabelWrite + } + servers = append(servers, ServerInfo{ + ServerType: tp, + Address: store.Address, + StatusAddr: store.StatusAddress, + Version: FormatStoreServerVersion(store.Version), + GitHash: store.GitHash, + StartTimestamp: store.StartTimestamp, + EngineRole: engineRole, + }) + } + return servers, nil +} + +// FormatStoreServerVersion format version of store servers(Tikv or TiFlash) +func FormatStoreServerVersion(version string) string { + if len(version) >= 1 && version[0] == 'v' { + version = version[1:] + } + return version +} + +// GetTiFlashStoreCount returns the count of tiflash server. +func GetTiFlashStoreCount(ctx sessionctx.Context) (cnt uint64, err error) { + failpoint.Inject("mockTiFlashStoreCount", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(uint64(10), nil) + } + }) + + stores, err := GetStoreServerInfo(ctx) + if err != nil { + return cnt, err + } + for _, store := range stores { + if store.ServerType == kv.TiFlash.Name() { + cnt++ + } + } + return cnt, nil +} + +// SysVarHiddenForSem checks if a given sysvar is hidden according to SEM and privileges. +func SysVarHiddenForSem(ctx sessionctx.Context, sysVarNameInLower string) bool { + if !sem.IsEnabled() || !sem.IsInvisibleSysVar(sysVarNameInLower) { + return false + } + checker := privilege.GetPrivilegeManager(ctx) + if checker == nil || checker.RequestDynamicVerification(ctx.GetSessionVars().ActiveRoles, "RESTRICTED_VARIABLES_ADMIN", false) { + return false + } + return true +} + +// GetDataFromSessionVariables return the [name, value] of all session variables +func GetDataFromSessionVariables(ctx context.Context, sctx sessionctx.Context) ([][]types.Datum, error) { + sessionVars := sctx.GetSessionVars() + sysVars := variable.GetSysVars() + rows := make([][]types.Datum, 0, len(sysVars)) + for _, v := range sysVars { + if SysVarHiddenForSem(sctx, v.Name) { + continue + } + var value string + value, err := sessionVars.GetSessionOrGlobalSystemVar(ctx, v.Name) + if err != nil { + return nil, err + } + row := types.MakeDatums(v.Name, value) + rows = append(rows, row) + } + return rows, nil +} + +// GetDataFromSessionConnectAttrs produces the rows for the session_connect_attrs table. +func GetDataFromSessionConnectAttrs(sctx sessionctx.Context, sameAccount bool) ([][]types.Datum, error) { + sm := sctx.GetSessionManager() + if sm == nil { + return nil, nil + } + var user *auth.UserIdentity + if sameAccount { + user = sctx.GetSessionVars().User + } + allAttrs := sm.GetConAttrs(user) + rows := make([][]types.Datum, 0, len(allAttrs)*10) // 10 Attributes per connection + for pid, attrs := range allAttrs { // Note: PID is not ordered. + // Sorts the attributes by key and gives ORDINAL_POSITION based on this. This is needed as we didn't store the + // ORDINAL_POSITION and a map doesn't have a guaranteed sort order. This is needed to keep the ORDINAL_POSITION + // stable over multiple queries. + attrnames := make([]string, 0, len(attrs)) + for attrname := range attrs { + attrnames = append(attrnames, attrname) + } + sort.Strings(attrnames) + + for ord, attrkey := range attrnames { + row := types.MakeDatums( + pid, + attrkey, + attrs[attrkey], + ord, + ) + rows = append(rows, row) + } + } + return rows, nil +} + +var tableNameToColumns = map[string][]columnInfo{ + TableSchemata: schemataCols, + TableTables: tablesCols, + TableColumns: columnsCols, + tableColumnStatistics: columnStatisticsCols, + TableStatistics: statisticsCols, + TableCharacterSets: charsetCols, + TableCollations: collationsCols, + tableFiles: filesCols, + TableProfiling: profilingCols, + TablePartitions: partitionsCols, + TableKeyColumn: keyColumnUsageCols, + TableReferConst: referConstCols, + TableSessionVar: sessionVarCols, + tablePlugins: pluginsCols, + TableConstraints: tableConstraintsCols, + tableTriggers: tableTriggersCols, + TableUserPrivileges: tableUserPrivilegesCols, + tableSchemaPrivileges: tableSchemaPrivilegesCols, + tableTablePrivileges: tableTablePrivilegesCols, + tableColumnPrivileges: tableColumnPrivilegesCols, + TableEngines: tableEnginesCols, + TableViews: tableViewsCols, + tableRoutines: tableRoutinesCols, + tableParameters: tableParametersCols, + tableEvents: tableEventsCols, + tableGlobalStatus: tableGlobalStatusCols, + tableGlobalVariables: tableGlobalVariablesCols, + tableSessionStatus: tableSessionStatusCols, + tableOptimizerTrace: tableOptimizerTraceCols, + tableTableSpaces: tableTableSpacesCols, + TableCollationCharacterSetApplicability: tableCollationCharacterSetApplicabilityCols, + TableProcesslist: tableProcesslistCols, + TableTiDBIndexes: tableTiDBIndexesCols, + TableSlowQuery: slowQueryCols, + TableTiDBHotRegions: TableTiDBHotRegionsCols, + TableTiDBHotRegionsHistory: TableTiDBHotRegionsHistoryCols, + TableTiKVStoreStatus: TableTiKVStoreStatusCols, + TableAnalyzeStatus: tableAnalyzeStatusCols, + TableTiKVRegionStatus: TableTiKVRegionStatusCols, + TableTiKVRegionPeers: TableTiKVRegionPeersCols, + TableTiDBServersInfo: tableTiDBServersInfoCols, + TableClusterInfo: tableClusterInfoCols, + TableClusterConfig: tableClusterConfigCols, + TableClusterLog: tableClusterLogCols, + TableClusterLoad: tableClusterLoadCols, + TableTiFlashReplica: tableTableTiFlashReplicaCols, + TableClusterHardware: tableClusterHardwareCols, + TableClusterSystemInfo: tableClusterSystemInfoCols, + TableInspectionResult: tableInspectionResultCols, + TableMetricSummary: tableMetricSummaryCols, + TableMetricSummaryByLabel: tableMetricSummaryByLabelCols, + TableMetricTables: tableMetricTablesCols, + TableInspectionSummary: tableInspectionSummaryCols, + TableInspectionRules: tableInspectionRulesCols, + TableDDLJobs: tableDDLJobsCols, + TableSequences: tableSequencesCols, + TableStatementsSummary: tableStatementsSummaryCols, + TableStatementsSummaryHistory: tableStatementsSummaryCols, + TableStatementsSummaryEvicted: tableStatementsSummaryEvictedCols, + TableStorageStats: tableStorageStatsCols, + TableTiFlashTables: tableTableTiFlashTablesCols, + TableTiFlashSegments: tableTableTiFlashSegmentsCols, + TableClientErrorsSummaryGlobal: tableClientErrorsSummaryGlobalCols, + TableClientErrorsSummaryByUser: tableClientErrorsSummaryByUserCols, + TableClientErrorsSummaryByHost: tableClientErrorsSummaryByHostCols, + TableTiDBTrx: tableTiDBTrxCols, + TableDeadlocks: tableDeadlocksCols, + TableDataLockWaits: tableDataLockWaitsCols, + TableAttributes: tableAttributesCols, + TablePlacementPolicies: tablePlacementPoliciesCols, + TableTrxSummary: tableTrxSummaryCols, + TableVariablesInfo: tableVariablesInfoCols, + TableUserAttributes: tableUserAttributesCols, + TableMemoryUsage: tableMemoryUsageCols, + TableMemoryUsageOpsHistory: tableMemoryUsageOpsHistoryCols, + TableResourceGroups: tableResourceGroupsCols, + TableRunawayWatches: tableRunawayWatchListCols, + TableCheckConstraints: tableCheckConstraintsCols, +} + +func createInfoSchemaTable(_ autoid.Allocators, meta *model.TableInfo) (table.Table, error) { + columns := make([]*table.Column, len(meta.Columns)) + for i, col := range meta.Columns { + columns[i] = table.ToColumn(col) + } + tp := table.VirtualTable + if isClusterTableByName(util.InformationSchemaName.O, meta.Name.O) { + tp = table.ClusterTable + } + return &infoschemaTable{meta: meta, cols: columns, tp: tp}, nil +} + +type infoschemaTable struct { + meta *model.TableInfo + cols []*table.Column + tp table.Type +} + +// IterRecords implements table.Table IterRecords interface. +func (*infoschemaTable) IterRecords(ctx context.Context, sctx sessionctx.Context, cols []*table.Column, fn table.RecordIterFunc) error { + return nil +} + +// Cols implements table.Table Cols interface. +func (it *infoschemaTable) Cols() []*table.Column { + return it.cols +} + +// VisibleCols implements table.Table VisibleCols interface. +func (it *infoschemaTable) VisibleCols() []*table.Column { + return it.cols +} + +// HiddenCols implements table.Table HiddenCols interface. +func (it *infoschemaTable) HiddenCols() []*table.Column { + return nil +} + +// WritableCols implements table.Table WritableCols interface. +func (it *infoschemaTable) WritableCols() []*table.Column { + return it.cols +} + +// DeletableCols implements table.Table WritableCols interface. +func (it *infoschemaTable) DeletableCols() []*table.Column { + return it.cols +} + +// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. +func (it *infoschemaTable) FullHiddenColsAndVisibleCols() []*table.Column { + return it.cols +} + +// Indices implements table.Table Indices interface. +func (it *infoschemaTable) Indices() []table.Index { + return nil +} + +// RecordPrefix implements table.Table RecordPrefix interface. +func (it *infoschemaTable) RecordPrefix() kv.Key { + return nil +} + +// IndexPrefix implements table.Table IndexPrefix interface. +func (it *infoschemaTable) IndexPrefix() kv.Key { + return nil +} + +// AddRecord implements table.Table AddRecord interface. +func (it *infoschemaTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return nil, table.ErrUnsupportedOp +} + +// RemoveRecord implements table.Table RemoveRecord interface. +func (it *infoschemaTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { + return table.ErrUnsupportedOp +} + +// UpdateRecord implements table.Table UpdateRecord interface. +func (it *infoschemaTable) UpdateRecord(gctx context.Context, ctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { + return table.ErrUnsupportedOp +} + +// Allocators implements table.Table Allocators interface. +func (it *infoschemaTable) Allocators(_ sessionctx.Context) autoid.Allocators { + return autoid.Allocators{} +} + +// Meta implements table.Table Meta interface. +func (it *infoschemaTable) Meta() *model.TableInfo { + return it.meta +} + +// GetPhysicalID implements table.Table GetPhysicalID interface. +func (it *infoschemaTable) GetPhysicalID() int64 { + return it.meta.ID +} + +// Type implements table.Table Type interface. +func (it *infoschemaTable) Type() table.Type { + return it.tp +} + +// GetPartitionedTable implements table.Table GetPartitionedTable interface. +func (it *infoschemaTable) GetPartitionedTable() table.PartitionedTable { + return nil +} + +// VirtualTable is a dummy table.Table implementation. +type VirtualTable struct{} + +// Cols implements table.Table Cols interface. +func (vt *VirtualTable) Cols() []*table.Column { + return nil +} + +// VisibleCols implements table.Table VisibleCols interface. +func (vt *VirtualTable) VisibleCols() []*table.Column { + return nil +} + +// HiddenCols implements table.Table HiddenCols interface. +func (vt *VirtualTable) HiddenCols() []*table.Column { + return nil +} + +// WritableCols implements table.Table WritableCols interface. +func (vt *VirtualTable) WritableCols() []*table.Column { + return nil +} + +// DeletableCols implements table.Table WritableCols interface. +func (vt *VirtualTable) DeletableCols() []*table.Column { + return nil +} + +// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. +func (vt *VirtualTable) FullHiddenColsAndVisibleCols() []*table.Column { + return nil +} + +// Indices implements table.Table Indices interface. +func (vt *VirtualTable) Indices() []table.Index { + return nil +} + +// RecordPrefix implements table.Table RecordPrefix interface. +func (vt *VirtualTable) RecordPrefix() kv.Key { + return nil +} + +// IndexPrefix implements table.Table IndexPrefix interface. +func (vt *VirtualTable) IndexPrefix() kv.Key { + return nil +} + +// AddRecord implements table.Table AddRecord interface. +func (vt *VirtualTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return nil, table.ErrUnsupportedOp +} + +// RemoveRecord implements table.Table RemoveRecord interface. +func (vt *VirtualTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { + return table.ErrUnsupportedOp +} + +// UpdateRecord implements table.Table UpdateRecord interface. +func (vt *VirtualTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { + return table.ErrUnsupportedOp +} + +// Allocators implements table.Table Allocators interface. +func (vt *VirtualTable) Allocators(_ sessionctx.Context) autoid.Allocators { + return autoid.Allocators{} +} + +// Meta implements table.Table Meta interface. +func (vt *VirtualTable) Meta() *model.TableInfo { + return nil +} + +// GetPhysicalID implements table.Table GetPhysicalID interface. +func (vt *VirtualTable) GetPhysicalID() int64 { + return 0 +} + +// Type implements table.Table Type interface. +func (vt *VirtualTable) Type() table.Type { + return table.VirtualTable +} + +// GetTiFlashServerInfo returns all TiFlash server infos +func GetTiFlashServerInfo(sctx sessionctx.Context) ([]ServerInfo, error) { + if config.GetGlobalConfig().DisaggregatedTiFlash { + return nil, table.ErrUnsupportedOp + } + serversInfo, err := GetStoreServerInfo(sctx) + if err != nil { + return nil, err + } + serversInfo = FilterClusterServerInfo(serversInfo, set.NewStringSet(kv.TiFlash.Name()), set.NewStringSet()) + return serversInfo, nil +} + +// FetchClusterServerInfoWithoutPrivilegeCheck fetches cluster server information +func FetchClusterServerInfoWithoutPrivilegeCheck(ctx context.Context, sctx sessionctx.Context, serversInfo []ServerInfo, serverInfoType diagnosticspb.ServerInfoType, recordWarningInStmtCtx bool) ([][]types.Datum, error) { + type result struct { + idx int + rows [][]types.Datum + err error + } + wg := sync.WaitGroup{} + ch := make(chan result, len(serversInfo)) + infoTp := serverInfoType + finalRows := make([][]types.Datum, 0, len(serversInfo)*10) + for i, srv := range serversInfo { + address := srv.Address + remote := address + if srv.ServerType == "tidb" { + remote = srv.StatusAddr + } + wg.Add(1) + go func(index int, remote, address, serverTP string) { + util.WithRecovery(func() { + defer wg.Done() + items, err := getServerInfoByGRPC(ctx, remote, infoTp) + if err != nil { + ch <- result{idx: index, err: err} + return + } + partRows := serverInfoItemToRows(items, serverTP, address) + ch <- result{idx: index, rows: partRows} + }, nil) + }(i, remote, address, srv.ServerType) + } + wg.Wait() + close(ch) + // Keep the original order to make the result more stable + var results []result //nolint: prealloc + for result := range ch { + if result.err != nil { + if recordWarningInStmtCtx { + sctx.GetSessionVars().StmtCtx.AppendWarning(result.err) + } else { + log.Warn(result.err.Error()) + } + continue + } + results = append(results, result) + } + slices.SortFunc(results, func(i, j result) int { return cmp.Compare(i.idx, j.idx) }) + for _, result := range results { + finalRows = append(finalRows, result.rows...) + } + return finalRows, nil +} + +func serverInfoItemToRows(items []*diagnosticspb.ServerInfoItem, tp, addr string) [][]types.Datum { + rows := make([][]types.Datum, 0, len(items)) + for _, v := range items { + for _, item := range v.Pairs { + row := types.MakeDatums( + tp, + addr, + v.Tp, + v.Name, + item.Key, + item.Value, + ) + rows = append(rows, row) + } + } + return rows +} + +func getServerInfoByGRPC(ctx context.Context, address string, tp diagnosticspb.ServerInfoType) ([]*diagnosticspb.ServerInfoItem, error) { + opt := grpc.WithTransportCredentials(insecure.NewCredentials()) + security := config.GetGlobalConfig().Security + if len(security.ClusterSSLCA) != 0 { + clusterSecurity := security.ClusterSecurity() + tlsConfig, err := clusterSecurity.ToTLSConfig() + if err != nil { + return nil, errors.Trace(err) + } + opt = grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)) + } + conn, err := grpc.Dial(address, opt) + if err != nil { + return nil, err + } + defer func() { + err := conn.Close() + if err != nil { + log.Error("close grpc connection error", zap.Error(err)) + } + }() + + cli := diagnosticspb.NewDiagnosticsClient(conn) + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + r, err := cli.ServerInfo(ctx, &diagnosticspb.ServerInfoRequest{Tp: tp}) + if err != nil { + return nil, err + } + return r.Items, nil +} + +// FilterClusterServerInfo filters serversInfo by nodeTypes and addresses +func FilterClusterServerInfo(serversInfo []ServerInfo, nodeTypes, addresses set.StringSet) []ServerInfo { + if len(nodeTypes) == 0 && len(addresses) == 0 { + return serversInfo + } + + filterServers := make([]ServerInfo, 0, len(serversInfo)) + for _, srv := range serversInfo { + // Skip some node type which has been filtered in WHERE clause + // e.g: SELECT * FROM cluster_config WHERE type='tikv' + if len(nodeTypes) > 0 && !nodeTypes.Exist(srv.ServerType) { + continue + } + // Skip some node address which has been filtered in WHERE clause + // e.g: SELECT * FROM cluster_config WHERE address='192.16.8.12:2379' + if len(addresses) > 0 && !addresses.Exist(srv.Address) { + continue + } + filterServers = append(filterServers, srv) + } + return filterServers +} diff --git a/pkg/infoschema/test/cachetest/BUILD.bazel b/pkg/infoschema/test/cachetest/BUILD.bazel new file mode 100644 index 0000000000000..8dfed58f6ce75 --- /dev/null +++ b/pkg/infoschema/test/cachetest/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "cachetest_test", + timeout = "short", + srcs = [ + "cache_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 6, + deps = [ + "//pkg/infoschema", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/infoschema/test/cachetest/cache_test.go b/pkg/infoschema/test/cachetest/cache_test.go new file mode 100644 index 0000000000000..f195394bbc475 --- /dev/null +++ b/pkg/infoschema/test/cachetest/cache_test.go @@ -0,0 +1,213 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cachetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/stretchr/testify/require" +) + +func TestNewCache(t *testing.T) { + ic := infoschema.NewCache(16) + require.NotNil(t, ic) +} + +func TestInsert(t *testing.T) { + ic := infoschema.NewCache(3) + require.NotNil(t, ic) + + is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) + ic.Insert(is2, 2) + require.Equal(t, is2, ic.GetByVersion(2)) + require.Equal(t, is2, ic.GetBySnapshotTS(2)) + require.Equal(t, is2, ic.GetBySnapshotTS(10)) + require.Nil(t, ic.GetBySnapshotTS(0)) + + // newer + is5 := infoschema.MockInfoSchemaWithSchemaVer(nil, 5) + ic.Insert(is5, 5) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is2, ic.GetByVersion(2)) + // there is a gap in schema cache, so don't use this version + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is5, ic.GetBySnapshotTS(10)) + + // older + is0 := infoschema.MockInfoSchemaWithSchemaVer(nil, 0) + ic.Insert(is0, 0) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is2, ic.GetByVersion(2)) + require.Equal(t, is0, ic.GetByVersion(0)) + + // replace 5, drop 0 + is6 := infoschema.MockInfoSchemaWithSchemaVer(nil, 6) + ic.Insert(is6, 6) + require.Equal(t, is6, ic.GetByVersion(6)) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is2, ic.GetByVersion(2)) + require.Nil(t, ic.GetByVersion(0)) + // there is a gap in schema cache, so don't use this version + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is5, ic.GetBySnapshotTS(5)) + require.Equal(t, is6, ic.GetBySnapshotTS(10)) + + // replace 2, drop 2 + is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) + ic.Insert(is3, 3) + require.Equal(t, is6, ic.GetByVersion(6)) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is3, ic.GetByVersion(3)) + require.Nil(t, ic.GetByVersion(2)) + require.Nil(t, ic.GetByVersion(0)) + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is6, ic.GetBySnapshotTS(10)) + + // insert 2, but failed silently + ic.Insert(is2, 2) + require.Equal(t, is6, ic.GetByVersion(6)) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is3, ic.GetByVersion(3)) + require.Nil(t, ic.GetByVersion(2)) + require.Nil(t, ic.GetByVersion(0)) + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is6, ic.GetBySnapshotTS(10)) + + // insert 5, but it is already in + ic.Insert(is5, 5) + require.Equal(t, is6, ic.GetByVersion(6)) + require.Equal(t, is5, ic.GetByVersion(5)) + require.Equal(t, is3, ic.GetByVersion(3)) + require.Nil(t, ic.GetByVersion(2)) + require.Nil(t, ic.GetByVersion(0)) + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is5, ic.GetBySnapshotTS(5)) + require.Equal(t, is6, ic.GetBySnapshotTS(10)) +} + +func TestGetByVersion(t *testing.T) { + ic := infoschema.NewCache(2) + require.NotNil(t, ic) + is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) + ic.Insert(is1, 1) + is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) + ic.Insert(is3, 3) + + require.Equal(t, is1, ic.GetByVersion(1)) + require.Equal(t, is3, ic.GetByVersion(3)) + require.Nilf(t, ic.GetByVersion(0), "index == 0, but not found") + require.Equal(t, int64(1), ic.GetByVersion(2).SchemaMetaVersion()) + require.Nilf(t, ic.GetByVersion(4), "index == length, but not found") +} + +func TestGetLatest(t *testing.T) { + ic := infoschema.NewCache(16) + require.NotNil(t, ic) + require.Nil(t, ic.GetLatest()) + + is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) + ic.Insert(is1, 1) + require.Equal(t, is1, ic.GetLatest()) + + // newer change the newest + is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) + ic.Insert(is2, 2) + require.Equal(t, is2, ic.GetLatest()) + + // older schema doesn't change the newest + is0 := infoschema.MockInfoSchemaWithSchemaVer(nil, 0) + ic.Insert(is0, 0) + require.Equal(t, is2, ic.GetLatest()) +} + +func TestGetByTimestamp(t *testing.T) { + ic := infoschema.NewCache(16) + require.NotNil(t, ic) + require.Nil(t, ic.GetLatest()) + require.Equal(t, 0, ic.Len()) + + is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) + ic.Insert(is1, 1) + require.Nil(t, ic.GetBySnapshotTS(0)) + require.Equal(t, is1, ic.GetBySnapshotTS(1)) + require.Equal(t, is1, ic.GetBySnapshotTS(2)) + require.Equal(t, 1, ic.Len()) + + is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) + ic.Insert(is3, 3) + require.Equal(t, is3, ic.GetLatest()) + require.Nil(t, ic.GetBySnapshotTS(0)) + // there is a gap, no schema returned for ts 2 + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is3, ic.GetBySnapshotTS(3)) + require.Equal(t, is3, ic.GetBySnapshotTS(4)) + require.Equal(t, 2, ic.Len()) + + is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) + // schema version 2 doesn't have timestamp set + // thus all schema before ver 2 cannot be searched by timestamp anymore + // because the ts of ver 2 is not accurate + ic.Insert(is2, 0) + require.Equal(t, is3, ic.GetLatest()) + require.Nil(t, ic.GetBySnapshotTS(0)) + require.Nil(t, ic.GetBySnapshotTS(1)) + require.Nil(t, ic.GetBySnapshotTS(2)) + require.Equal(t, is3, ic.GetBySnapshotTS(3)) + require.Equal(t, is3, ic.GetBySnapshotTS(4)) + require.Equal(t, 3, ic.Len()) + + // insert is2 again with correct timestamp, to correct previous wrong timestamp + ic.Insert(is2, 2) + require.Equal(t, is3, ic.GetLatest()) + require.Equal(t, is1, ic.GetBySnapshotTS(1)) + require.Equal(t, is2, ic.GetBySnapshotTS(2)) + require.Equal(t, is3, ic.GetBySnapshotTS(3)) + require.Equal(t, 3, ic.Len()) +} + +func TestReSize(t *testing.T) { + ic := infoschema.NewCache(2) + require.NotNil(t, ic) + is1 := infoschema.MockInfoSchemaWithSchemaVer(nil, 1) + ic.Insert(is1, 1) + is2 := infoschema.MockInfoSchemaWithSchemaVer(nil, 2) + ic.Insert(is2, 2) + + ic.ReSize(3) + require.Equal(t, 2, ic.Size()) + require.Equal(t, is1, ic.GetByVersion(1)) + require.Equal(t, is2, ic.GetByVersion(2)) + is3 := infoschema.MockInfoSchemaWithSchemaVer(nil, 3) + require.True(t, ic.Insert(is3, 3)) + require.Equal(t, is1, ic.GetByVersion(1)) + require.Equal(t, is2, ic.GetByVersion(2)) + require.Equal(t, is3, ic.GetByVersion(3)) + + ic.ReSize(1) + require.Equal(t, 1, ic.Size()) + require.Nil(t, ic.GetByVersion(1)) + require.Nil(t, ic.GetByVersion(2)) + require.Equal(t, is3, ic.GetByVersion(3)) + require.False(t, ic.Insert(is2, 2)) + require.Equal(t, 1, ic.Size()) + is4 := infoschema.MockInfoSchemaWithSchemaVer(nil, 4) + require.True(t, ic.Insert(is4, 4)) + require.Equal(t, 1, ic.Size()) + require.Nil(t, ic.GetByVersion(1)) + require.Nil(t, ic.GetByVersion(2)) + require.Nil(t, ic.GetByVersion(3)) + require.Equal(t, is4, ic.GetByVersion(4)) +} diff --git a/pkg/infoschema/test/cachetest/main_test.go b/pkg/infoschema/test/cachetest/main_test.go new file mode 100644 index 0000000000000..98553b931416a --- /dev/null +++ b/pkg/infoschema/test/cachetest/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cachetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/infoschema/test/clustertablestest/BUILD.bazel b/pkg/infoschema/test/clustertablestest/BUILD.bazel new file mode 100644 index 0000000000000..8d393c6d7bd1e --- /dev/null +++ b/pkg/infoschema/test/clustertablestest/BUILD.bazel @@ -0,0 +1,58 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "clustertablestest_test", + timeout = "short", + srcs = [ + "cluster_tables_test.go", + "main_test.go", + "tables_test.go", + ], + flaky = True, + shard_count = 45, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/infoschema/internal", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/server", + "//pkg/session", + "//pkg/session/txninfo", + "//pkg/sessionctx/variable", + "//pkg/store/helper", + "//pkg/store/mockstore", + "//pkg/store/mockstore/mockstorage", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/gctuner", + "//pkg/util/memory", + "//pkg/util/pdapi", + "//pkg/util/resourcegrouptag", + "//pkg/util/set", + "//pkg/util/stmtsummary", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_fn//:fn", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/infoschema/test/clustertablestest/cluster_tables_test.go b/pkg/infoschema/test/clustertablestest/cluster_tables_test.go similarity index 98% rename from infoschema/test/clustertablestest/cluster_tables_test.go rename to pkg/infoschema/test/clustertablestest/cluster_tables_test.go index f03ab8c2d85ed..00b8016a1d808 100644 --- a/infoschema/test/clustertablestest/cluster_tables_test.go +++ b/pkg/infoschema/test/clustertablestest/cluster_tables_test.go @@ -32,28 +32,28 @@ import ( "github.com/pingcap/fn" "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/infoschema/internal" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mockstorage" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/infoschema/internal" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/mockstorage" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stmtsummary" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" @@ -88,7 +88,7 @@ func TestForClusterServerInfo(t *testing.T) { } fpExpr := `return("` + strings.Join(instances, ";") + `")` - fpName := "github.com/pingcap/tidb/infoschema/mockClusterInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockClusterInfo" require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() diff --git a/pkg/infoschema/test/clustertablestest/main_test.go b/pkg/infoschema/test/clustertablestest/main_test.go new file mode 100644 index 0000000000000..2c38419f9519a --- /dev/null +++ b/pkg/infoschema/test/clustertablestest/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package clustertablestest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/infoschema/test/clustertablestest/tables_test.go b/pkg/infoschema/test/clustertablestest/tables_test.go new file mode 100644 index 0000000000000..ddcf6c69a6b88 --- /dev/null +++ b/pkg/infoschema/test/clustertablestest/tables_test.go @@ -0,0 +1,1419 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package clustertablestest + +import ( + "fmt" + "math" + "os" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/infoschema/internal" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/gctuner" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/require" +) + +func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + return tk +} + +func newTestKitWithPlanCache(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := testkit.NewTestKit(t, store) + se, err := session.CreateSession4TestWithOpt(store, &session.Opt{PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false)}) + require.NoError(t, err) + tk.SetSession(se) + tk.RefreshConnectionID() + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + return tk +} + +func TestInfoSchemaFieldValue(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists numschema, timeschema") + tk.MustExec("create table numschema(i int(2), f float(4,2), d decimal(4,3))") + tk.MustExec("create table timeschema(d date, dt datetime(3), ts timestamp(3), t time(4), y year(4))") + tk.MustExec("create table strschema(c char(3), c2 varchar(3), b blob(3), t text(3))") + tk.MustExec("create table floatschema(a float, b double(7, 3))") + + tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='numschema'"). + Check(testkit.Rows(" 2 0 ", " 4 2 ", " 4 3 ")) // FIXME: for mysql first one will be " 10 0 " + tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='timeschema'"). + Check(testkit.Rows(" ", " 3", " 3", " 4", " ")) + tk.MustQuery("select CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,DATETIME_PRECISION from information_schema.COLUMNS where table_name='strschema'"). + Check(testkit.Rows("3 12 ", "3 12 ", "255 255 ", "255 1020 ")) + tk.MustQuery("select NUMERIC_SCALE from information_schema.COLUMNS where table_name='floatschema'"). + Check(testkit.Rows("", "3")) + + // Test for auto increment ID. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c int auto_increment primary key, d int)") + tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( + testkit.Rows("1")) + tk.MustExec("insert into t(c, d) values(1, 1)") + tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( + testkit.Rows("2")) + + tk.MustQuery("show create table t").Check( + testkit.Rows("" + + "t CREATE TABLE `t` (\n" + + " `c` int(11) NOT NULL AUTO_INCREMENT,\n" + + " `d` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`c`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=30002")) + + // Test auto_increment for table without auto_increment column + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (d int)") + tk.MustQuery("select auto_increment from information_schema.tables where table_name='t'").Check( + testkit.Rows("")) + + tk.MustExec("create user xxx") + + // Test for length of enum and set + tk.MustExec("drop table if exists t") + tk.MustExec("create table t ( s set('a','bc','def','ghij') default NULL, e1 enum('a', 'ab', 'cdef'), s2 SET('1','2','3','4','1585','ONE','TWO','Y','N','THREE'))") + tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 's'").Check( + testkit.Rows("s 13")) + tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 'S'").Check( + testkit.Rows("s 13")) + tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 's2'").Check( + testkit.Rows("s2 30")) + tk.MustQuery("select column_name, character_maximum_length from information_schema.columns where table_schema=Database() and table_name = 't' and column_name = 'e1'").Check( + testkit.Rows("e1 4")) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{ + Username: "xxx", + Hostname: "127.0.0.1", + }, nil, nil, nil)) + + tk1.MustQuery("select distinct(table_schema) from information_schema.tables").Check(testkit.Rows("INFORMATION_SCHEMA")) + + // Fix issue 9836 + sm := &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 1, + User: "root", + Host: "127.0.0.1", + Command: mysql.ComQuery, + StmtCtx: tk.Session().GetSessionVars().StmtCtx, + }) + tk.Session().SetSessionManager(sm) + tk.MustQuery("SELECT user,host,command FROM information_schema.processlist;").Check(testkit.Rows("root 127.0.0.1 Query")) + + // Test for all system tables `TABLE_TYPE` is `SYSTEM VIEW`. + rows1 := tk.MustQuery("select count(*) from information_schema.tables where table_schema in ('INFORMATION_SCHEMA','PERFORMANCE_SCHEMA','METRICS_SCHEMA');").Rows() + rows2 := tk.MustQuery("select count(*) from information_schema.tables where table_schema in ('INFORMATION_SCHEMA','PERFORMANCE_SCHEMA','METRICS_SCHEMA') and table_type = 'SYSTEM VIEW';").Rows() + require.Equal(t, rows2, rows1) + // Test for system table default value + tk.MustQuery("show create table information_schema.PROCESSLIST").Check( + testkit.Rows("" + + "PROCESSLIST CREATE TABLE `PROCESSLIST` (\n" + + " `ID` bigint(21) unsigned NOT NULL DEFAULT '0',\n" + + " `USER` varchar(16) NOT NULL DEFAULT '',\n" + + " `HOST` varchar(64) NOT NULL DEFAULT '',\n" + + " `DB` varchar(64) DEFAULT NULL,\n" + + " `COMMAND` varchar(16) NOT NULL DEFAULT '',\n" + + " `TIME` int(7) NOT NULL DEFAULT '0',\n" + + " `STATE` varchar(7) DEFAULT NULL,\n" + + " `INFO` longtext DEFAULT NULL,\n" + + " `DIGEST` varchar(64) DEFAULT '',\n" + + " `MEM` bigint(21) unsigned DEFAULT NULL,\n" + + " `DISK` bigint(21) unsigned DEFAULT NULL,\n" + + " `TxnStart` varchar(64) NOT NULL DEFAULT '',\n" + + " `RESOURCE_GROUP` varchar(32) NOT NULL DEFAULT '',\n" + + " `SESSION_ALIAS` varchar(64) NOT NULL DEFAULT ''\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustQuery("show create table information_schema.cluster_log").Check( + testkit.Rows("" + + "CLUSTER_LOG CREATE TABLE `CLUSTER_LOG` (\n" + + " `TIME` varchar(32) DEFAULT NULL,\n" + + " `TYPE` varchar(64) DEFAULT NULL,\n" + + " `INSTANCE` varchar(64) DEFAULT NULL,\n" + + " `LEVEL` varchar(8) DEFAULT NULL,\n" + + " `MESSAGE` longtext DEFAULT NULL\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) +} + +func TestSomeTables(t *testing.T) { + store := testkit.CreateMockStore(t) + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk := testkit.NewTestKit(t, store) + tk.SetSession(se) + sm := &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 1, + User: "user-1", + Host: "localhost", + Port: "", + DB: "information_schema", + Command: byte(1), + Digest: "abc1", + State: 1, + Info: "do something", + StmtCtx: tk.Session().GetSessionVars().StmtCtx, + ResourceGroupName: "rg1", + SessionAlias: "alias1", + }) + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 2, + User: "user-2", + Host: "localhost", + Port: "", + DB: "test", + Command: byte(2), + Digest: "abc2", + State: 2, + Info: strings.Repeat("x", 101), + StmtCtx: tk.Session().GetSessionVars().StmtCtx, + ResourceGroupName: "rg2", + }) + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 3, + User: "user-3", + Host: "127.0.0.1", + Port: "12345", + DB: "test", + Command: byte(2), + Digest: "abc3", + State: 1, + Info: "check port", + StmtCtx: tk.Session().GetSessionVars().StmtCtx, + ResourceGroupName: "rg3", + SessionAlias: "中文alias", + }) + tk.Session().SetSessionManager(sm) + tk.MustQuery("select * from information_schema.PROCESSLIST order by ID;").Sort().Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 alias1", "in transaction", "do something"), + fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s abc2 0 0 rg2 ", "autocommit", strings.Repeat("x", 101)), + fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s abc3 0 0 rg3 中文alias", "in transaction", "check port"), + )) + tk.MustQuery("SHOW PROCESSLIST;").Sort().Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", "do something"), + fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 100)), + fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s", "in transaction", "check port"), + )) + tk.MustQuery("SHOW FULL PROCESSLIST;").Sort().Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", "do something"), + fmt.Sprintf("2 user-2 localhost test Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 101)), + fmt.Sprintf("3 user-3 127.0.0.1:12345 test Init DB 9223372036 %s %s", "in transaction", "check port"), + )) + + sm = &testkit.MockSessionManager{PS: make([]*util.ProcessInfo, 0)} + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 1, + User: "user-1", + Host: "localhost", + DB: "information_schema", + Command: byte(1), + Digest: "abc1", + State: 1, + ResourceGroupName: "rg1", + }) + sm.PS = append(sm.PS, &util.ProcessInfo{ + ID: 2, + User: "user-2", + Host: "localhost", + Command: byte(2), + Digest: "abc2", + State: 2, + Info: strings.Repeat("x", 101), + CurTxnStartTS: 410090409861578752, + ResourceGroupName: "rg2", + SessionAlias: "alias3", + }) + tk.Session().SetSessionManager(sm) + tk.Session().GetSessionVars().TimeZone = time.UTC + tk.MustQuery("select * from information_schema.PROCESSLIST order by ID;").Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 ", "in transaction", ""), + fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s abc2 0 0 07-29 03:26:05.158(410090409861578752) rg2 alias3", "autocommit", strings.Repeat("x", 101)), + )) + tk.MustQuery("SHOW PROCESSLIST;").Sort().Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", ""), + fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 100)), + )) + tk.MustQuery("SHOW FULL PROCESSLIST;").Sort().Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s", "in transaction", ""), + fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s", "autocommit", strings.Repeat("x", 101)), + )) + tk.MustQuery("select * from information_schema.PROCESSLIST where db is null;").Check( + testkit.Rows( + fmt.Sprintf("2 user-2 localhost Init DB 9223372036 %s %s abc2 0 0 07-29 03:26:05.158(410090409861578752) rg2 alias3", "autocommit", strings.Repeat("x", 101)), + )) + tk.MustQuery("select * from information_schema.PROCESSLIST where Info is null;").Check( + testkit.Rows( + fmt.Sprintf("1 user-1 localhost information_schema Quit 9223372036 %s %s abc1 0 0 rg1 ", "in transaction", ""), + )) +} + +func TestTableRowIDShardingInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("DROP DATABASE IF EXISTS `sharding_info_test_db`") + tk.MustExec("CREATE DATABASE `sharding_info_test_db`") + + assertShardingInfo := func(tableName string, expectInfo interface{}) { + querySQL := fmt.Sprintf("select tidb_row_id_sharding_info from information_schema.tables where table_schema = 'sharding_info_test_db' and table_name = '%s'", tableName) + info := tk.MustQuery(querySQL).Rows()[0][0] + if expectInfo == nil { + require.Equal(t, "", info) + } else { + require.Equal(t, expectInfo, info) + } + } + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t1` (a int)") + assertShardingInfo("t1", "NOT_SHARDED") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t2` (a int key)") + assertShardingInfo("t2", "NOT_SHARDED(PK_IS_HANDLE)") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t3` (a int) SHARD_ROW_ID_BITS=4") + assertShardingInfo("t3", "SHARD_BITS=4") + + tk.MustExec("CREATE VIEW `sharding_info_test_db`.`tv` AS select 1") + assertShardingInfo("tv", nil) + + testFunc := func(dbName string, expectInfo interface{}) { + dbInfo := model.DBInfo{Name: model.NewCIStr(dbName)} + tableInfo := model.TableInfo{} + + info := infoschema.GetShardingInfo(&dbInfo, &tableInfo) + require.Equal(t, expectInfo, info) + } + + testFunc("information_schema", nil) + testFunc("mysql", nil) + testFunc("performance_schema", nil) + testFunc("uucc", "NOT_SHARDED") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t4` (a bigint key clustered auto_random)") + assertShardingInfo("t4", "PK_AUTO_RANDOM_BITS=5") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t5` (a bigint key clustered auto_random(1))") + assertShardingInfo("t5", "PK_AUTO_RANDOM_BITS=1") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t6` (a bigint key clustered auto_random(2, 32))") + assertShardingInfo("t6", "PK_AUTO_RANDOM_BITS=2, RANGE BITS=32") + + tk.MustExec("CREATE TABLE `sharding_info_test_db`.`t7` (a bigint key clustered auto_random(5, 64))") + assertShardingInfo("t7", "PK_AUTO_RANDOM_BITS=5") + + tk.MustExec("DROP DATABASE `sharding_info_test_db`") +} + +func TestSlowQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + // Prepare slow log file. + slowLogFileName := "tidb_slow.log" + internal.PrepareSlowLogfile(t, slowLogFileName) + defer func() { require.NoError(t, os.Remove(slowLogFileName)) }() + expectedRes := [][]interface{}{ + {"2019-02-12 19:33:56.571953", + "406315658548871171", + "root", + "localhost", + "6", + "", + "57", + "0.12", + "4.895492", + "0.4", + "0.2", + "0.000000003", + "2", + "0.000000002", + "0.00000001", + "0.000000003", + "0.19", + "0.21", + "0.01", + "0", + "0.18", + "[txnLock]", + "0.03", + "0", + "15", + "480", + "1", + "8", + "0.3824278", + "0.161", + "0.101", + "0.092", + "1.71", + "1", + "100001", + "100000", + "100", + "10", + "10", + "10", + "100", + "test", + "", + "0", + "42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772", + "t1:1,t2:2", + "0.1", + "0.2", + "0.03", + "127.0.0.1:20160", + "0.05", + "0.6", + "0.8", + "0.0.0.0:20160", + "70724", + "65536", + "0", + "0", + "0", + "0", + "10", + "", + "", + "0", + "1", + "0", + "0", + "1", + "0", + "0", + "abcd", + "60e9378c746d9a2be1c791047e008967cf252eb6de9167ad3aa6098fa2d523f4", + "", + "update t set i = 2;", + "select * from t_slim;"}, + {"2021-09-08 14:39:54.506967", + "427578666238083075", + "root", + "172.16.0.0", + "40507", + "alias123", + "0", + "0", + "25.571605962", + "0.002923536", + "0.006800973", + "0.002100764", + "0", + "0", + "0", + "0.000015801", + "25.542014572", + "0", + "0.002294647", + "0.000605473", + "12.483", + "[tikvRPC regionMiss tikvRPC regionMiss regionMiss]", + "0", + "0", + "624", + "172064", + "60", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "rtdb", + "", + "0", + "124acb3a0bec903176baca5f9da00b4e7512a41c93b417923f26502edeb324cc", + "", + "0", + "0", + "0", + "", + "0", + "0", + "0", + "", + "856544", + "0", + "86.635049185", + "0.015486658", + "100.054", + "0", + "0", + "", + "", + "0", + "1", + "0", + "0", + "0", + "0", + "0", + "", + "", + "", + "", + "INSERT INTO ...;", + }, + } + + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", slowLogFileName)) + tk.MustExec("set time_zone = '+08:00';") + re := tk.MustQuery("select * from information_schema.slow_query") + re.Check(expectedRes) + + tk.MustExec("set time_zone = '+00:00';") + re = tk.MustQuery("select * from information_schema.slow_query") + expectedRes[0][0] = "2019-02-12 11:33:56.571953" + expectedRes[1][0] = "2021-09-08 06:39:54.506967" + re.Check(expectedRes) + + // Test for long query. + f, err := os.OpenFile(slowLogFileName, os.O_CREATE|os.O_WRONLY, 0644) + require.NoError(t, err) + defer func() { require.NoError(t, f.Close()) }() + _, err = f.Write([]byte(` +# Time: 2019-02-13T19:33:56.571953+08:00 +`)) + require.NoError(t, err) + sql := "select * from " + for len(sql) < 5000 { + sql += "abcdefghijklmnopqrstuvwxyz_1234567890_qwertyuiopasdfghjklzxcvbnm" + } + sql += ";" + _, err = f.Write([]byte(sql)) + require.NoError(t, err) + re = tk.MustQuery("select query from information_schema.slow_query order by time desc limit 1") + rows := re.Rows() + require.Equal(t, sql, rows[0][0]) +} + +func TestTableIfHasColumn(t *testing.T) { + columnName := variable.SlowLogHasMoreResults + store := testkit.CreateMockStore(t) + slowLogFileName := "tidb-table-has-column-slow.log" + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Log.SlowQueryFile = slowLogFileName + }) + f, err := os.OpenFile(slowLogFileName, os.O_CREATE|os.O_WRONLY, 0644) + require.NoError(t, err) + _, err = f.Write([]byte(`# Time: 2019-02-12T19:33:56.571953+08:00 +# Txn_start_ts: 406315658548871171 +# User@Host: root[root] @ localhost [127.0.0.1] +# Has_more_results: true +INSERT INTO ...; +`)) + require.NoError(t, f.Close()) + require.NoError(t, err) + defer func() { require.NoError(t, os.Remove(slowLogFileName)) }() + tk := testkit.NewTestKit(t, store) + + // check schema + tk.MustQuery(`select COUNT(*) from information_schema.columns +WHERE table_name = 'slow_query' and column_name = '` + columnName + `'`). + Check(testkit.Rows("1")) + + // check select + tk.MustQuery(`select ` + columnName + + ` from information_schema.slow_query`).Check(testkit.Rows("1")) +} + +func TestReloadDropDatabase(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database test_dbs") + tk.MustExec("use test_dbs") + tk.MustExec("create table t1 (a int)") + tk.MustExec("create table t2 (a int)") + tk.MustExec("create table t3 (a int)") + is := domain.GetDomain(tk.Session()).InfoSchema() + t2, err := is.TableByName(model.NewCIStr("test_dbs"), model.NewCIStr("t2")) + require.NoError(t, err) + tk.MustExec("drop database test_dbs") + is = domain.GetDomain(tk.Session()).InfoSchema() + _, err = is.TableByName(model.NewCIStr("test_dbs"), model.NewCIStr("t2")) + require.True(t, terror.ErrorEqual(infoschema.ErrTableNotExists, err)) + _, ok := is.TableByID(t2.Meta().ID) + require.False(t, ok) +} + +func TestSystemSchemaID(t *testing.T) { + _, dom := testkit.CreateMockStoreAndDomain(t) + + uniqueIDMap := make(map[int64]string) + checkSystemSchemaTableID(t, dom, "information_schema", autoid.InformationSchemaDBID, 1, 10000, uniqueIDMap) + checkSystemSchemaTableID(t, dom, "performance_schema", autoid.PerformanceSchemaDBID, 10000, 20000, uniqueIDMap) + checkSystemSchemaTableID(t, dom, "metrics_schema", autoid.MetricSchemaDBID, 20000, 30000, uniqueIDMap) +} + +func checkSystemSchemaTableID(t *testing.T, dom *domain.Domain, dbName string, dbID, start, end int64, uniqueIDMap map[int64]string) { + is := dom.InfoSchema() + require.NotNil(t, is) + db, ok := is.SchemaByName(model.NewCIStr(dbName)) + require.True(t, ok) + require.Equal(t, dbID, db.ID) + // Test for information_schema table id. + tables := is.SchemaTables(model.NewCIStr(dbName)) + require.Greater(t, len(tables), 0) + for _, tbl := range tables { + tid := tbl.Meta().ID + require.Greaterf(t, tid&autoid.SystemSchemaIDFlag, int64(0), "table name is %v", tbl.Meta().Name) + require.Greaterf(t, tid&^autoid.SystemSchemaIDFlag, start, "table name is %v", tbl.Meta().Name) + require.Lessf(t, tid&^autoid.SystemSchemaIDFlag, end, "table name is %v", tbl.Meta().Name) + + name, ok := uniqueIDMap[tid] + require.Falsef(t, ok, "schema id of %v is duplicate with %v, both is %v", name, tbl.Meta().Name, tid) + uniqueIDMap[tid] = tbl.Meta().Name.O + } +} + +func TestSelectHiddenColumn(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("DROP DATABASE IF EXISTS `test_hidden`;") + tk.MustExec("CREATE DATABASE `test_hidden`;") + tk.MustExec("USE test_hidden;") + tk.MustExec("CREATE TABLE hidden (a int , b int, c int);") + tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("3")) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test_hidden"), model.NewCIStr("hidden")) + require.NoError(t, err) + colInfo := tb.Meta().Columns + // Set column b to hidden + colInfo[1].Hidden = true + tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("2")) + tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden' and column_name = 'b'").Check(testkit.Rows("0")) + // Set column b to visible + colInfo[1].Hidden = false + tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden' and column_name = 'b'").Check(testkit.Rows("1")) + // Set a, b ,c to hidden + colInfo[0].Hidden = true + colInfo[1].Hidden = true + colInfo[2].Hidden = true + tk.MustQuery("select count(*) from INFORMATION_SCHEMA.COLUMNS where table_name = 'hidden'").Check(testkit.Rows("0")) +} + +func TestFormatVersion(t *testing.T) { + // Test for defaultVersions. + versions := []struct { + version string + expected string + userset bool + }{ + // default versions + {"5.7.25-TiDB-None", "None", true}, + {"5.7.25-TiDB-8.0.18", "8.0.18", true}, + {"5.7.25-TiDB-8.0.18-beta.1", "8.0.18-beta.1", true}, + {"5.7.25-TiDB-v4.0.0-beta-446-g5268094af", "4.0.0-beta-446-g5268094af", true}, + {"5.7.25-TiDB-", "", true}, + {"5.7.25-TiDB-v4.0.0-TiDB-446", "4.0.0-TiDB-446", true}, + // userset + {"8.0.18", "8.0.18", false}, + {"5.7.25-TiDB", "5.7.25-TiDB", false}, + {"8.0.18-TiDB-4.0.0-beta.1", "8.0.18-TiDB-4.0.0-beta.1", false}, + } + for _, tt := range versions { + version := infoschema.FormatTiDBVersion(tt.version, tt.userset) + require.Equal(t, tt.expected, version) + } +} + +func TestFormatStoreServerVersion(t *testing.T) { + versions := []string{"v4.0.12", "4.0.12", "v5.0.1"} + resultVersion := []string{"4.0.12", "4.0.12", "5.0.1"} + + for i, versionString := range versions { + require.Equal(t, infoschema.FormatStoreServerVersion(versionString), resultVersion[i]) + } +} + +// TestStmtSummaryTable Test statements_summary. +func TestStmtSummaryTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustQuery("select column_comment from information_schema.columns " + + "where table_name='STATEMENTS_SUMMARY' and column_name='STMT_TYPE'", + ).Check(testkit.Rows("Statement type")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) + + // Disable refreshing summary. + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + // Test INSERT + tk.MustExec("insert into t values(1, 'a')") + tk.MustExec("insert into t values(2, 'b')") + tk.MustExec("insert into t VALUES(3, 'c')") + tk.MustExec("/**/insert into t values(4, 'd')") + + sql := "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text " + + "from information_schema.statements_summary " + + "where digest_text like 'insert into `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) + + // Test point get. + tk.MustExec("drop table if exists p") + tk.MustExec("create table p(a int primary key, b int)") + for i := 1; i < 3; i++ { + tk.MustQuery("select b from p where a=1") + expectedResult := fmt.Sprintf("%d \tid \ttask\testRows\toperator info\n\tPoint_Get_1\troot\t1 \ttable:p, handle:1 %s", i, "test.p") + // Also make sure that the plan digest is not empty + sql = "select exec_count, plan, table_names from information_schema.statements_summary " + + "where digest_text like 'select `b` from `p`%' and plan_digest != ''" + tk.MustQuery(sql).Check(testkit.Rows(expectedResult)) + } + + // Point get another database. + tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'") + // Test for Encode plan cache. + p1 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() + require.Greater(t, len(p1), 0) + rows := tk.MustQuery("select tidb_decode_plan('" + p1 + "');").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, 1, len(rows[0])) + require.Regexp(t, "\n.*Point_Get.*table.tidb, index.PRIMARY.VARIABLE_NAME", rows[0][0]) + + sql = "select table_names from information_schema.statements_summary " + + "where digest_text like 'select `variable_value`%' and `schema_name`='test'" + tk.MustQuery(sql).Check(testkit.Rows("mysql.tidb")) + + // Test `create database`. + tk.MustExec("create database if not exists test") + // Test for Encode plan cache. + p2 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() + require.Equal(t, "", p2) + tk.MustQuery(`select table_names + from information_schema.statements_summary + where digest_text like 'create database%' and schema_name='test'`, + ).Check(testkit.Rows("")) + + // Test SELECT. + const failpointName = "github.com/pingcap/tidb/pkg/planner/core/mockPlanRowCount" + require.NoError(t, failpoint.Enable(failpointName, "return(100)")) + defer func() { require.NoError(t, failpoint.Disable(failpointName)) }() + tk.MustQuery("select * from t where a=2") + + // sum_cop_task_num is always 0 if tidb_enable_collect_execution_info disabled + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t100 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) + + // select ... order by + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text + from information_schema.statements_summary + order by exec_count desc limit 1`, + ).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) + + // Test different plans with same digest. + require.NoError(t, failpoint.Enable(failpointName, "return(1000)")) + tk.MustQuery("select * from t where a=3") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows( + "Select test test.t t:k 2 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t100 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) + + // Disable it again. + tk.MustExec("set global tidb_enable_stmt_summary = false") + defer tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) + + // Create a new session to test + tk = newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + // This statement shouldn't be summarized. + tk.MustQuery("select * from t where a=2") + + // The table should be cleared. + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, plan + from information_schema.statements_summary`, + ).Check(testkit.Rows()) + + tk.MustExec("SET GLOBAL tidb_enable_stmt_summary = on") + // It should work immediately. + tk.MustExec("begin") + tk.MustExec("insert into t values(1, 'a')") + tk.MustExec("commit") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text " + + "from information_schema.statements_summary " + + "where digest_text like 'insert into `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') ")) + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text + from information_schema.statements_summary + where digest_text='commit'`, + ).Check(testkit.Rows("Commit test 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a')")) + + tk.MustQuery("select * from t where a=2") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t1000 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t1000 \ttable:t, keep order:false, stats:pseudo")) + + // Disable it in global scope. + tk.MustExec("set global tidb_enable_stmt_summary = false") + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + + // Statement summary is disabled. + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, plan + from information_schema.statements_summary`, + ).Check(testkit.Rows()) + + tk.MustExec("set global tidb_enable_stmt_summary = on") + tk.MustExec("set global tidb_stmt_summary_history_size = 24") +} + +func TestStmtSummaryTablePrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t") + + // Disable refreshing summary. + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + // Create a new user to test statements summary table privilege + tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("create user 'test_user'@'localhost'") + defer tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("grant select on test.t to 'test_user'@'localhost'") + tk.MustExec("select * from t where a=1") + result := tk.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + result = tk.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.Session().Auth(&auth.UserIdentity{ + Username: "test_user", + Hostname: "localhost", + AuthUsername: "test_user", + AuthHostname: "localhost", + }, nil, nil, nil) + + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Ordinary users can not see others' records + require.Equal(t, 0, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 0, len(result.Rows())) + tk1.MustExec("select * from t where b=1") + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Ordinary users can see his own records + require.Equal(t, 1, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + + tk.MustExec("grant process on *.* to 'test_user'@'localhost'") + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Users with 'PROCESS' privileges can query all records. + require.Equal(t, 2, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 2, len(result.Rows())) +} + +func TestCapturePrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t") + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t1") + + // Disable refreshing summary. + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + // Create a new user to test statements summary table privilege + tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("create user 'test_user'@'localhost'") + defer tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") + tk.MustExec("select * from t where a=1") + tk.MustExec("select * from t where a=1") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.Session().Auth(&auth.UserIdentity{ + Username: "test_user", + Hostname: "localhost", + AuthUsername: "test_user", + AuthHostname: "localhost", + }, nil, nil, nil) + + rows = tk1.MustQuery("show global bindings").Rows() + // Ordinary users can not see others' records + require.Len(t, rows, 0) + tk1.MustExec("select * from t1 where b=1") + tk1.MustExec("select * from t1 where b=1") + tk1.MustExec("admin capture bindings") + rows = tk1.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + + tk.MustExec("grant all on *.* to 'test_user'@'localhost'") + tk1.MustExec("admin capture bindings") + rows = tk1.MustQuery("show global bindings").Rows() + require.Len(t, rows, 2) +} + +// TestStmtSummaryInternalQuery Test statements_summary_history. +func TestStmtSummaryInternalQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + originalVal := config.CheckTableBeforeDrop + config.CheckTableBeforeDrop = true + defer func() { + config.CheckTableBeforeDrop = originalVal + }() + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + + // We use the sql binding evolve to check the internal query summary. + tk.MustExec("set @@tidb_use_plan_baselines = 1") + tk.MustExec("set @@tidb_evolve_plan_baselines = 1") + tk.MustExec("create global binding for select * from t where t.a = 1 using select * from t ignore index(k) where t.a = 1") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) + // Disable refreshing summary. + tk.MustExec("set global tidb_stmt_summary_refresh_interval = 999999999") + tk.MustQuery("select @@global.tidb_stmt_summary_refresh_interval").Check(testkit.Rows("999999999")) + + // Test Internal + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + + tk.MustExec("select * from t where t.a = 1") + tk.MustQuery(`select exec_count, digest_text + from information_schema.statements_summary + where digest_text like "select original_sql , bind_sql , default_db , status%"`).Check(testkit.Rows()) + + // Enable internal query and evolve baseline. + tk.MustExec("set global tidb_stmt_summary_internal_query = 1") + defer tk.MustExec("set global tidb_stmt_summary_internal_query = false") + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + + tk.MustExec("admin flush bindings") + tk.MustExec("admin evolve bindings") + + // `exec_count` may be bigger than 1 because other cases are also running. + sql := "select digest_text " + + "from information_schema.statements_summary " + + "where digest_text like \"select `original_sql` , `bind_sql` , `default_db` , status%\"" + tk.MustQuery(sql).Check(testkit.Rows( + "select `original_sql` , `bind_sql` , `default_db` , status , `create_time` , `update_time` , charset , " + + "collation , source , `sql_digest` , `plan_digest` from `mysql` . `bind_info` where `update_time` > ? order by `update_time` , `create_time`")) + + // Test for issue #21642. + tk.MustQuery(`select tidb_version()`) + rows := tk.MustQuery("select plan from information_schema.statements_summary where digest_text like \"select `tidb_version`%\"").Rows() + require.Contains(t, rows[0][0].(string), "Projection") +} + +// TestSimpleStmtSummaryEvictedCount test stmtSummaryEvictedCount +func TestSimpleStmtSummaryEvictedCount(t *testing.T) { + store := testkit.CreateMockStore(t) + + now := time.Now().Unix() + interval := int64(1800) + beginTimeForCurInterval := now - now%interval + tk := newTestKitWithPlanCache(t, store) + tk.MustExec(fmt.Sprintf("set global tidb_stmt_summary_refresh_interval = %v", interval)) + + // no evict happens now, evicted count should be empty + tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("0")) + + // clean up side effects + defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 100") + defer tk.MustExec("set global tidb_stmt_summary_refresh_interval = 1800") + + tk.MustExec("set global tidb_enable_stmt_summary = 0") + // statements summary evicted is also disabled when set tidb_enable_stmt_summary to off + tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("0")) + tk.MustExec("set global tidb_enable_stmt_summary = 1") + // first sql + tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 1") + // second sql + tk.MustQuery("show databases;") + // query `evicted table` is also a SQL, passing it leads to the eviction of the previous SQLs. + tk.MustQuery("select * from `information_schema`.`STATEMENTS_SUMMARY_EVICTED`;"). + Check(testkit.Rows( + fmt.Sprintf("%s %s %v", + time.Unix(beginTimeForCurInterval, 0).Format(time.DateTime), + time.Unix(beginTimeForCurInterval+interval, 0).Format(time.DateTime), + int64(2)), + )) + + // test too much intervals + tk.MustExec("use test;") + tk.MustExec(fmt.Sprintf("set @@global.tidb_stmt_summary_refresh_interval=%v", interval)) + tk.MustExec("set @@global.tidb_enable_stmt_summary=0") + tk.MustExec("set @@global.tidb_enable_stmt_summary=1") + historySize := 24 + fpPath := "github.com/pingcap/tidb/pkg/util/stmtsummary/mockTimeForStatementsSummary" + for i := int64(0); i < 100; i++ { + err := failpoint.Enable(fpPath, fmt.Sprintf(`return("%v")`, time.Now().Unix()+interval*i)) + if err != nil { + panic(err.Error()) + } + tk.MustExec(fmt.Sprintf("create table if not exists th%v (p bigint key, q int);", i)) + } + err := failpoint.Disable(fpPath) + if err != nil { + panic(err.Error()) + } + tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;"). + Check(testkit.Rows(fmt.Sprintf("%v", historySize))) + + // test discrete intervals + tk.MustExec("set @@global.tidb_enable_stmt_summary=0") + tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=1;") + tk.MustExec("set @@global.tidb_enable_stmt_summary=1") + for i := int64(0); i < 3; i++ { + tk.MustExec(fmt.Sprintf("select count(*) from th%v", i)) + } + err = failpoint.Enable(fpPath, fmt.Sprintf(`return("%v")`, time.Now().Unix()+2*interval)) + if err != nil { + panic(err.Error()) + } + for i := int64(0); i < 3; i++ { + tk.MustExec(fmt.Sprintf("select count(*) from th%v", i)) + } + tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;").Check(testkit.Rows("2")) + tk.MustQuery("select BEGIN_TIME from information_schema.statements_summary_evicted;"). + Check(testkit. + Rows(time.Unix(beginTimeForCurInterval+2*interval, 0).Format(time.DateTime), + time.Unix(beginTimeForCurInterval, 0).Format(time.DateTime))) + require.NoError(t, failpoint.Disable(fpPath)) + // TODO: Add more tests. +} + +func TestStmtSummaryEvictedPointGet(t *testing.T) { + store := testkit.CreateMockStore(t) + + interval := int64(1800) + tk := newTestKitWithRoot(t, store) + tk.MustExec(fmt.Sprintf("set global tidb_stmt_summary_refresh_interval=%v;", interval)) + tk.MustExec("create database point_get;") + tk.MustExec("use point_get;") + for i := 0; i < 6; i++ { + tk.MustExec(fmt.Sprintf("create table if not exists th%v ("+ + "p bigint key,"+ + "q int);", i)) + } + + tk.MustExec("set @@global.tidb_enable_stmt_summary=0;") + tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=5;") + defer tk.MustExec("set @@global.tidb_stmt_summary_max_stmt_count=100;") + // first SQL + tk.MustExec("set @@global.tidb_enable_stmt_summary=1;") + + for i := int64(0); i < 1000; i++ { + // six SQLs + tk.MustExec(fmt.Sprintf("select p from th%v where p=2333;", i%6)) + } + tk.MustQuery("select EVICTED_COUNT from information_schema.statements_summary_evicted;"). + Check(testkit.Rows("7")) + + tk.MustExec("set @@global.tidb_enable_stmt_summary=0;") + tk.MustQuery("select count(*) from information_schema.statements_summary_evicted;"). + Check(testkit.Rows("0")) + tk.MustExec("set @@global.tidb_enable_stmt_summary=1;") +} + +func TestServerInfoResolveLoopBackAddr(t *testing.T) { + nodes := []infoschema.ServerInfo{ + {Address: "127.0.0.1:4000", StatusAddr: "192.168.130.22:10080"}, + {Address: "0.0.0.0:4000", StatusAddr: "192.168.130.22:10080"}, + {Address: "localhost:4000", StatusAddr: "192.168.130.22:10080"}, + {Address: "192.168.130.22:4000", StatusAddr: "0.0.0.0:10080"}, + {Address: "192.168.130.22:4000", StatusAddr: "127.0.0.1:10080"}, + {Address: "192.168.130.22:4000", StatusAddr: "localhost:10080"}, + } + for i := range nodes { + nodes[i].ResolveLoopBackAddr() + } + for _, n := range nodes { + require.Equal(t, "192.168.130.22:4000", n.Address) + require.Equal(t, "192.168.130.22:10080", n.StatusAddr) + } +} + +func TestInfoSchemaClientErrors(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + + tk.MustExec("FLUSH CLIENT_ERRORS_SUMMARY") + + errno.IncrementError(1365, "root", "localhost") + errno.IncrementError(1365, "infoschematest", "localhost") + errno.IncrementError(1365, "root", "localhost") + + tk.MustExec("CREATE USER 'infoschematest'@'localhost'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "infoschematest", Hostname: "localhost"}, nil, nil, nil)) + + err := tk.QueryToErr("SELECT * FROM information_schema.client_errors_summary_global") + require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation", err.Error()) + + err = tk.QueryToErr("SELECT * FROM information_schema.client_errors_summary_by_host") + require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation", err.Error()) + + tk.MustQuery("SELECT error_number, error_count, warning_count FROM information_schema.client_errors_summary_by_user ORDER BY error_number").Check(testkit.Rows("1365 1 0")) + + err = tk.ExecToErr("FLUSH CLIENT_ERRORS_SUMMARY") + require.Equal(t, "[planner:1227]Access denied; you need (at least one of) the RELOAD privilege(s) for this operation", err.Error()) +} + +func TestTiDBTrx(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + tk.MustExec("drop table if exists test_tidb_trx") + tk.MustExec("create table test_tidb_trx(i int)") + // Execute the statement once so that the statement will be collected into statements_summary and able to be found + // by digest. + tk.MustExec("update test_tidb_trx set i = i + 1") + _, digest := parser.NormalizeDigest("update test_tidb_trx set i = i + 1") + sm := &testkit.MockSessionManager{TxnInfo: make([]*txninfo.TxnInfo, 2)} + memDBTracker := memory.NewTracker(memory.LabelForMemDB, -1) + memDBTracker.Consume(19) + tk.Session().GetSessionVars().MemDBFootprint = memDBTracker + sm.TxnInfo[0] = &txninfo.TxnInfo{ + StartTS: 424768545227014155, + CurrentSQLDigest: digest.String(), + State: txninfo.TxnIdle, + EntriesCount: 1, + ConnectionID: 2, + Username: "root", + CurrentDB: "test", + } + + blockTime2 := time.Date(2021, 05, 20, 13, 18, 30, 123456000, time.Local) + sm.TxnInfo[1] = &txninfo.TxnInfo{ + StartTS: 425070846483628033, + CurrentSQLDigest: "", + AllSQLDigests: []string{"sql1", "sql2", digest.String()}, + State: txninfo.TxnLockAcquiring, + ConnectionID: 10, + Username: "user1", + CurrentDB: "db1", + } + sm.TxnInfo[1].BlockStartTime.Valid = true + sm.TxnInfo[1].BlockStartTime.Time = blockTime2 + tk.Session().SetSessionManager(sm) + + tk.MustQuery(`select ID, + START_TIME, + CURRENT_SQL_DIGEST, + CURRENT_SQL_DIGEST_TEXT, + STATE, + WAITING_START_TIME, + MEM_BUFFER_KEYS, + MEM_BUFFER_BYTES, + SESSION_ID, + USER, + DB, + ALL_SQL_DIGESTS, + RELATED_TABLE_IDS + from information_schema.TIDB_TRX`).Check(testkit.Rows( + "424768545227014155 2021-05-07 12:56:48.001000 "+digest.String()+" update `test_tidb_trx` set `i` = `i` + ? Idle 1 19 2 root test [] ", + "425070846483628033 2021-05-20 21:16:35.778000 LockWaiting 2021-05-20 13:18:30.123456 0 19 10 user1 db1 [\"sql1\",\"sql2\",\""+digest.String()+"\"] ")) + + rows := tk.MustQuery(`select WAITING_TIME from information_schema.TIDB_TRX where WAITING_TIME is not null`) + require.Len(t, rows.Rows(), 1) + + // Test the all_sql_digests column can be directly passed to the tidb_decode_sql_digests function. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/sqlDigestRetrieverSkipRetrieveGlobal", "return")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/expression/sqlDigestRetrieverSkipRetrieveGlobal")) + }() + tk.MustQuery("select tidb_decode_sql_digests(all_sql_digests) from information_schema.tidb_trx").Check(testkit.Rows( + "[]", + "[null,null,\"update `test_tidb_trx` set `i` = `i` + ?\"]")) +} + +func TestTiDBTrxSummary(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := newTestKitWithRoot(t, store) + tk.MustExec("drop table if exists test_tidb_trx") + tk.MustExec("create table test_tidb_trx(i int)") + _, beginDigest := parser.NormalizeDigest("begin") + _, digest := parser.NormalizeDigest("update test_tidb_trx set i = i + 1") + _, commitDigest := parser.NormalizeDigest("commit") + txninfo.Recorder.Clean() + txninfo.Recorder.SetMinDuration(500 * time.Millisecond) + defer txninfo.Recorder.SetMinDuration(2147483647) + txninfo.Recorder.ResizeSummaries(128) + defer txninfo.Recorder.ResizeSummaries(0) + tk.MustExec("begin") + tk.MustExec("update test_tidb_trx set i = i + 1") + time.Sleep(1 * time.Second) + tk.MustExec("update test_tidb_trx set i = i + 1") + tk.MustExec("commit") + // it is possible for TRX_SUMMARY to have other rows (due to parallel execution of tests) + for _, row := range tk.MustQuery("select * from information_schema.TRX_SUMMARY;").Rows() { + // so we just look for the row we are looking for + if row[0] == "1bb679108d0012a8" { + require.Equal(t, strings.TrimSpace(row[1].(string)), "[\""+beginDigest.String()+"\",\""+digest.String()+"\",\""+digest.String()+"\",\""+commitDigest.String()+"\"]") + return + } + } + t.Fatal("cannot find the expected row") +} + +func TestAttributes(t *testing.T) { + store := testkit.CreateMockStore(t) + + // test the failpoint for testing + fpName := "github.com/pingcap/tidb/pkg/executor/mockOutputOfAttributes" + tk := newTestKitWithRoot(t, store) + tk.MustQuery("select * from information_schema.attributes").Check(testkit.Rows()) + + require.NoError(t, failpoint.Enable(fpName, "return")) + defer func() { require.NoError(t, failpoint.Disable(fpName)) }() + + tk.MustQuery(`select * from information_schema.attributes`).Check(testkit.Rows( + `schema/test/test_label key-range "merge_option=allow" [7480000000000000ff395f720000000000fa, 7480000000000000ff3a5f720000000000fa]`, + )) +} + +func TestMemoryUsageAndOpsHistory(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/gctuner/testMemoryLimitTuner", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/gctuner/testMemoryLimitTuner")) + }() + gctuner.GlobalMemoryLimitTuner.Start() + defer func() { + time.Sleep(1 * time.Second) // Wait tuning finished. + }() + tk.MustExec("set global tidb_mem_oom_action = 'CANCEL'") + tk.MustExec("set global tidb_server_memory_limit=512<<20") + tk.MustExec("set global tidb_enable_tmp_storage_on_oom=off") + dom, err := session.GetDomain(store) + require.Nil(t, err) + go dom.ServerMemoryLimitHandle().SetSessionManager(tk.Session().GetSessionManager()).Run() + // OOM + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tk.MustExec("insert into t values(1)") + for i := 0; i < 9; i++ { + tk.MustExec("insert into t select * from t;") + } + + var tmp string + var ok bool + var beginTime = time.Now().Format(types.TimeFormat) + err = tk.QueryToErr("explain analyze select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a") + var endTime = time.Now().Format(types.TimeFormat) + require.NotNil(t, err) + // Check Memory Table + rows := tk.MustQuery("select * from INFORMATION_SCHEMA.MEMORY_USAGE").Rows() + require.Len(t, rows, 1) + row := rows[0] + require.Len(t, row, 11) + require.Equal(t, row[0], strconv.FormatUint(memory.GetMemTotalIgnoreErr(), 10)) // MEMORY_TOTAL + require.Equal(t, row[1], "536870912") // MEMORY_LIMIT + require.Greater(t, row[2], "0") // MEMORY_CURRENT + tmp, ok = row[3].(string) // MEMORY_MAX_USED + require.Equal(t, ok, true) + val, err := strconv.ParseUint(tmp, 10, 64) + require.Nil(t, err) + require.Greater(t, val, uint64(536870912)) + + tmp, ok = row[4].(string) // CURRENT_OPS + require.Equal(t, ok, true) + if tmp != "null" && tmp != "shrink" { + require.Fail(t, "CURRENT_OPS get wrong value") + } + require.GreaterOrEqual(t, row[5], beginTime) // SESSION_KILL_LAST + require.LessOrEqual(t, row[5], endTime) + require.Greater(t, row[6], "0") // SESSION_KILL_TOTAL + require.GreaterOrEqual(t, row[7], beginTime) // GC_LAST + require.LessOrEqual(t, row[7], endTime) + require.Greater(t, row[8], "0") // GC_TOTAL + require.Equal(t, row[9], "0") // DISK_USAGE + require.Equal(t, row[10], "0") // QUERY_FORCE_DISK + + rows = tk.MustQuery("select * from INFORMATION_SCHEMA.MEMORY_USAGE_OPS_HISTORY").Rows() + require.Greater(t, len(rows), 0) + row = rows[len(rows)-1] + require.Len(t, row, 12) + require.GreaterOrEqual(t, row[0], beginTime) // TIME + require.LessOrEqual(t, row[0], endTime) + require.Equal(t, row[1], "SessionKill") // OPS + require.Equal(t, row[2], "536870912") // MEMORY_LIMIT + tmp, ok = row[3].(string) // MEMORY_CURRENT + require.Equal(t, ok, true) + val, err = strconv.ParseUint(tmp, 10, 64) + require.Nil(t, err) + require.Greater(t, val, uint64(536870912)) + + require.Greater(t, row[4], "0") // PROCESSID + require.Greater(t, row[5], "0") // MEM + require.Equal(t, row[6], "0") // DISK + require.Equal(t, row[7], "") // CLIENT + require.Equal(t, row[8], "test") // DB + require.Equal(t, row[9], "") // USER + require.Equal(t, row[10], "e3237ec256015a3566757e0c2742507cd30ae04e4cac2fbc14d269eafe7b067b") // SQL_DIGEST + require.Equal(t, row[11], "explain analyze select * from t t1 join t t2 join t t3 on t1.a=t2.a and t1.a=t3.a order by t1.a") // SQL_TEXT +} + +func TestAddFieldsForBinding(t *testing.T) { + s := new(clusterTablesSuite) + s.store, s.dom = testkit.CreateMockStoreAndDomain(t) + s.rpcserver, s.listenAddr = s.setUpRPCService(t, "127.0.0.1:0", nil) + s.httpServer, s.mockAddr = s.setUpMockPDHTTPServer() + s.startTime = time.Now() + defer s.httpServer.Close() + defer s.rpcserver.Stop() + tk := s.newTestKitWithRoot(t) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, key(a))") + tk.MustExec("select /*+ ignore_index(t, a)*/ * from t where a = 1") + planDigest := "4e3159169cc63c14b139a4e7d72eae1759875c9a9581f94bb2079aae961189cb" + rows := tk.MustQuery(fmt.Sprintf("select stmt_type, prepared, sample_user, schema_name, query_sample_text, charset, collation, plan_hint, digest_text "+ + "from information_schema.cluster_statements_summary where plan_digest = '%s'", planDigest)).Rows() + + require.Equal(t, rows[0][0], "Select") + require.Equal(t, rows[0][1], "0") + require.Equal(t, rows[0][2], "root") + require.Equal(t, rows[0][3], "test") + require.Equal(t, rows[0][4], "select /*+ ignore_index(t, a)*/ * from t where a = 1") + require.Equal(t, rows[0][5], "utf8mb4") + require.Equal(t, rows[0][6], "utf8mb4_bin") + require.Equal(t, rows[0][7], "use_index(@`sel_1` `test`.`t` ), ignore_index(`t` `a`)") + require.Equal(t, rows[0][8], "select * from `t` where `a` = ?") +} diff --git a/pkg/keyspace/BUILD.bazel b/pkg/keyspace/BUILD.bazel new file mode 100644 index 0000000000000..ddfa7ec75a3c6 --- /dev/null +++ b/pkg/keyspace/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "keyspace", + srcs = ["keyspace.go"], + importpath = "github.com/pingcap/tidb/pkg/keyspace", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "keyspace_test", + timeout = "short", + srcs = ["keyspace_test.go"], + embed = [":keyspace"], + flaky = True, + deps = [ + "//pkg/config", + "@com_github_stretchr_testify//require", + ], +) diff --git a/keyspace/keyspace.go b/pkg/keyspace/keyspace.go similarity index 98% rename from keyspace/keyspace.go rename to pkg/keyspace/keyspace.go index 3b19ee789a8f5..7736b7dedffe0 100644 --- a/keyspace/keyspace.go +++ b/pkg/keyspace/keyspace.go @@ -18,7 +18,7 @@ import ( "fmt" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/keyspace/keyspace_test.go b/pkg/keyspace/keyspace_test.go similarity index 97% rename from keyspace/keyspace_test.go rename to pkg/keyspace/keyspace_test.go index dd27e8f781240..d971697db4fcc 100644 --- a/keyspace/keyspace_test.go +++ b/pkg/keyspace/keyspace_test.go @@ -17,7 +17,7 @@ package keyspace import ( "testing" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/kv/BUILD.bazel b/pkg/kv/BUILD.bazel new file mode 100644 index 0000000000000..99ba85562acdd --- /dev/null +++ b/pkg/kv/BUILD.bazel @@ -0,0 +1,100 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "kv", + srcs = [ + "cachedb.go", + "checker.go", + "error.go", + "fault_injection.go", + "iter.go", + "key.go", + "keyflags.go", + "kv.go", + "mpp.go", + "option.go", + "txn.go", + "txn_scope_var.go", + "utils.go", + "variables.go", + "version.go", + ], + importpath = "github.com/pingcap/tidb/pkg/kv", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/dbterror", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/set", + "//pkg/util/tiflash", + "//pkg/util/tiflashcompute", + "//pkg/util/trxevents", + "@com_github_coocood_freecache//:freecache", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "kv_test", + timeout = "short", + srcs = [ + "checker_test.go", + "error_test.go", + "fault_injection_test.go", + "interface_mock_test.go", + "key_test.go", + "main_test.go", + "mock_test.go", + "option_test.go", + "txn_test.go", + "utils_test.go", + "version_test.go", + ], + embed = [":kv"], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util/codec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/kv/cachedb.go b/pkg/kv/cachedb.go similarity index 100% rename from kv/cachedb.go rename to pkg/kv/cachedb.go diff --git a/kv/checker.go b/pkg/kv/checker.go similarity index 100% rename from kv/checker.go rename to pkg/kv/checker.go diff --git a/kv/checker_test.go b/pkg/kv/checker_test.go similarity index 97% rename from kv/checker_test.go rename to pkg/kv/checker_test.go index 0562d1f5868ad..cec5927a23be9 100644 --- a/kv/checker_test.go +++ b/pkg/kv/checker_test.go @@ -17,7 +17,7 @@ package kv_test import ( "testing" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/assert" ) diff --git a/pkg/kv/error.go b/pkg/kv/error.go new file mode 100644 index 0000000000000..8aa3f4b900b56 --- /dev/null +++ b/pkg/kv/error.go @@ -0,0 +1,113 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package kv + +import ( + "strings" + + mysql "github.com/pingcap/tidb/pkg/errno" + pmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// TxnRetryableMark is used to uniform the commit error messages which could retry the transaction. +// *WARNING*: changing this string will affect the backward compatibility. +const TxnRetryableMark = "[try again later]" + +var ( + // ErrNotExist is used when try to get an entry with an unexist key from KV store. + ErrNotExist = dbterror.ClassKV.NewStd(mysql.ErrNotExist) + // ErrTxnRetryable is used when KV store occurs retryable error which SQL layer can safely retry the transaction. + // When using TiKV as the storage node, the error is returned ONLY when lock not found (txnLockNotFound) in Commit, + // subject to change it in the future. + ErrTxnRetryable = dbterror.ClassKV.NewStdErr( + mysql.ErrTxnRetryable, + pmysql.Message( + mysql.MySQLErrName[mysql.ErrTxnRetryable].Raw+TxnRetryableMark, + mysql.MySQLErrName[mysql.ErrTxnRetryable].RedactArgPos, + ), + ) + // ErrCannotSetNilValue is the error when sets an empty value. + ErrCannotSetNilValue = dbterror.ClassKV.NewStd(mysql.ErrCannotSetNilValue) + // ErrInvalidTxn is the error when commits or rollbacks in an invalid transaction. + ErrInvalidTxn = dbterror.ClassKV.NewStd(mysql.ErrInvalidTxn) + // ErrTxnTooLarge is the error when transaction is too large, lock time reached the maximum value. + ErrTxnTooLarge = dbterror.ClassKV.NewStd(mysql.ErrTxnTooLarge) + // ErrEntryTooLarge is the error when a key value entry is too large. + ErrEntryTooLarge = dbterror.ClassKV.NewStd(mysql.ErrEntryTooLarge) + // ErrKeyExists returns when key is already exist. + ErrKeyExists = dbterror.ClassKV.NewStd(mysql.ErrDupEntry) + // ErrNotImplemented returns when a function is not implemented yet. + ErrNotImplemented = dbterror.ClassKV.NewStd(mysql.ErrNotImplemented) + // ErrWriteConflict is the error when the commit meets an write conflict error. + ErrWriteConflict = dbterror.ClassKV.NewStdErr( + mysql.ErrWriteConflict, + pmysql.Message( + mysql.MySQLErrName[mysql.ErrWriteConflict].Raw+" "+TxnRetryableMark, + mysql.MySQLErrName[mysql.ErrWriteConflict].RedactArgPos, + ), + ) + // ErrWriteConflictInTiDB is the error when the commit meets an write conflict error when local latch is enabled. + ErrWriteConflictInTiDB = dbterror.ClassKV.NewStdErr( + mysql.ErrWriteConflictInTiDB, + pmysql.Message( + mysql.MySQLErrName[mysql.ErrWriteConflictInTiDB].Raw+" "+TxnRetryableMark, + mysql.MySQLErrName[mysql.ErrWriteConflictInTiDB].RedactArgPos, + ), + ) + // ErrLockExpire is the error when the lock is expired. + ErrLockExpire = dbterror.ClassTiKV.NewStd(mysql.ErrLockExpire) + // ErrAssertionFailed is the error when an assertion fails. + ErrAssertionFailed = dbterror.ClassTiKV.NewStd(mysql.ErrAssertionFailed) +) + +// IsTxnRetryableError checks if the error could safely retry the transaction. +func IsTxnRetryableError(err error) bool { + if err == nil { + return false + } + + if ErrTxnRetryable.Equal(err) || ErrWriteConflict.Equal(err) || ErrWriteConflictInTiDB.Equal(err) { + return true + } + + return false +} + +// IsErrNotFound checks if err is a kind of NotFound error. +func IsErrNotFound(err error) bool { + return ErrNotExist.Equal(err) +} + +// GetDuplicateErrorHandleString is used to concat the handle columns data with '-'. +// This is consistent with MySQL. +func GetDuplicateErrorHandleString(handle Handle) string { + dt, err := handle.Data() + if err != nil { + return err.Error() + } + var sb strings.Builder + for i, d := range dt { + if i != 0 { + sb.WriteString("-") + } + s, err := d.ToString() + if err != nil { + return err.Error() + } + sb.WriteString(s) + } + return sb.String() +} diff --git a/pkg/kv/error_test.go b/pkg/kv/error_test.go new file mode 100644 index 0000000000000..08c2bde9e9c62 --- /dev/null +++ b/pkg/kv/error_test.go @@ -0,0 +1,43 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package kv + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/assert" +) + +func TestError(t *testing.T) { + kvErrs := []*terror.Error{ + ErrNotExist, + ErrTxnRetryable, + ErrCannotSetNilValue, + ErrInvalidTxn, + ErrTxnTooLarge, + ErrEntryTooLarge, + ErrNotImplemented, + ErrWriteConflict, + ErrWriteConflictInTiDB, + } + + for _, err := range kvErrs { + code := terror.ToSQLError(err).Code + assert.NotEqual(t, mysql.ErrUnknown, code) + assert.Equal(t, uint16(err.Code()), code) + } +} diff --git a/kv/fault_injection.go b/pkg/kv/fault_injection.go similarity index 100% rename from kv/fault_injection.go rename to pkg/kv/fault_injection.go diff --git a/kv/fault_injection_test.go b/pkg/kv/fault_injection_test.go similarity index 98% rename from kv/fault_injection_test.go rename to pkg/kv/fault_injection_test.go index 3412b5b717d92..863aebd7f5be4 100644 --- a/kv/fault_injection_test.go +++ b/pkg/kv/fault_injection_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" diff --git a/kv/interface_mock_test.go b/pkg/kv/interface_mock_test.go similarity index 99% rename from kv/interface_mock_test.go rename to pkg/kv/interface_mock_test.go index f6e9697614701..2c2eb48c5754e 100644 --- a/kv/interface_mock_test.go +++ b/pkg/kv/interface_mock_test.go @@ -19,7 +19,7 @@ import ( deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" ) diff --git a/kv/iter.go b/pkg/kv/iter.go similarity index 100% rename from kv/iter.go rename to pkg/kv/iter.go diff --git a/kv/key.go b/pkg/kv/key.go similarity index 99% rename from kv/key.go rename to pkg/kv/key.go index dfb20fa07f832..f1fa7b1123d6e 100644 --- a/kv/key.go +++ b/pkg/kv/key.go @@ -21,9 +21,9 @@ import ( "strconv" "strings" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/set" ) // Key represents high-level Key type. diff --git a/kv/key_test.go b/pkg/kv/key_test.go similarity index 97% rename from kv/key_test.go rename to pkg/kv/key_test.go index 528b5f7ec2f30..fff2d836a34ea 100644 --- a/kv/key_test.go +++ b/pkg/kv/key_test.go @@ -23,11 +23,11 @@ import ( "unsafe" "github.com/pingcap/kvproto/pkg/coprocessor" - . "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + . "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/kv/keyflags.go b/pkg/kv/keyflags.go similarity index 100% rename from kv/keyflags.go rename to pkg/kv/keyflags.go diff --git a/kv/kv.go b/pkg/kv/kv.go similarity index 99% rename from kv/kv.go rename to pkg/kv/kv.go index 019676882447c..1ee1661d9593c 100644 --- a/kv/kv.go +++ b/pkg/kv/kv.go @@ -25,12 +25,12 @@ import ( deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/trxevents" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/trxevents" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/tikv" diff --git a/pkg/kv/main_test.go b/pkg/kv/main_test.go new file mode 100644 index 0000000000000..943837a8a9db5 --- /dev/null +++ b/pkg/kv/main_test.go @@ -0,0 +1,35 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package kv + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/kv/mock_test.go b/pkg/kv/mock_test.go similarity index 100% rename from kv/mock_test.go rename to pkg/kv/mock_test.go diff --git a/pkg/kv/mpp.go b/pkg/kv/mpp.go new file mode 100644 index 0000000000000..ea002f669528e --- /dev/null +++ b/pkg/kv/mpp.go @@ -0,0 +1,283 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package kv + +import ( + "context" + "strconv" + "strings" + "time" + + "github.com/pingcap/kvproto/pkg/mpp" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" + "github.com/pingcap/tipb/go-tipb" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" +) + +// MppVersion indicates the mpp-version used to build mpp plan +type MppVersion int64 + +const ( + // MppVersionV0 supports TiFlash version [~, ~] + MppVersionV0 MppVersion = iota + + // MppVersionV1 supports TiFlash version [v6.6.x, ~] + MppVersionV1 + + // MppVersionV2 supports TiFlash version [v7.3, ~], support ReportMPPTaskStatus service + MppVersionV2 + // MppVersionV3 + + mppVersionMax + + newestMppVersion MppVersion = mppVersionMax - 1 + + // MppVersionUnspecified means the illegal or unspecified version, it only used in TiDB. + MppVersionUnspecified MppVersion = -1 + + // MppVersionUnspecifiedName denotes name of UNSPECIFIED mpp version + MppVersionUnspecifiedName string = "UNSPECIFIED" +) + +// ToInt64 transforms MppVersion to int64 +func (v MppVersion) ToInt64() int64 { + return int64(v) +} + +// ToMppVersion transforms string to MppVersion +func ToMppVersion(name string) (MppVersion, bool) { + name = strings.ToUpper(name) + if name == MppVersionUnspecifiedName { + return MppVersionUnspecified, true + } + v, err := strconv.ParseInt(name, 10, 64) + if err != nil { + return MppVersionUnspecified, false + } + version := MppVersion(v) + if version >= MppVersionUnspecified && version <= newestMppVersion { + return version, true + } + return MppVersionUnspecified, false +} + +// GetNewestMppVersion returns the mpp-version can be used in mpp plan +func GetNewestMppVersion() MppVersion { + return newestMppVersion +} + +// MPPTaskMeta means the meta info such as location of a mpp task. +type MPPTaskMeta interface { + // GetAddress indicates which node this task should execute on. + GetAddress() string +} + +// MPPQueryID means the global unique id of a mpp query. +type MPPQueryID struct { + QueryTs uint64 // timestamp of query execution, used for TiFlash minTSO schedule + LocalQueryID uint64 // unique mpp query id in local tidb memory. + ServerID uint64 +} + +// MPPTask means the minimum execution unit of a mpp computation job. +type MPPTask struct { + Meta MPPTaskMeta // on which store this task will execute + ID int64 // mppTaskID + StartTs uint64 + GatherID uint64 + MppQueryID MPPQueryID + TableID int64 // physical table id + MppVersion MppVersion // mpp version + + PartitionTableIDs []int64 + TiFlashStaticPrune bool +} + +// ToPB generates the pb structure. +func (t *MPPTask) ToPB() *mpp.TaskMeta { + meta := &mpp.TaskMeta{ + StartTs: t.StartTs, + GatherId: t.GatherID, + QueryTs: t.MppQueryID.QueryTs, + LocalQueryId: t.MppQueryID.LocalQueryID, + ServerId: t.MppQueryID.ServerID, + TaskId: t.ID, + MppVersion: t.MppVersion.ToInt64(), + } + if t.ID != -1 { + meta.Address = t.Meta.GetAddress() + } + return meta +} + +// MppTaskStates denotes the state of mpp tasks +type MppTaskStates uint8 + +const ( + // MppTaskReady means the task is ready + MppTaskReady MppTaskStates = iota + // MppTaskRunning means the task is running + MppTaskRunning + // MppTaskCancelled means the task is cancelled + MppTaskCancelled + // MppTaskDone means the task is done + MppTaskDone +) + +// MPPDispatchRequest stands for a dispatching task. +type MPPDispatchRequest struct { + Data []byte // data encodes the dag coprocessor request. + Meta MPPTaskMeta // mpp store is the location of tiflash store. + IsRoot bool // root task returns data to tidb directly. + Timeout uint64 // If task is assigned but doesn't receive a connect request during timeout, the task should be destroyed. + // SchemaVer is for any schema-ful storage (like tiflash) to validate schema correctness if necessary. + SchemaVar int64 + StartTs uint64 + MppQueryID MPPQueryID + GatherID uint64 + ID int64 // identify a single task + MppVersion MppVersion + CoordinatorAddress string + ReportExecutionSummary bool + State MppTaskStates + ResourceGroupName string +} + +// CancelMPPTasksParam represents parameter for MPPClient's CancelMPPTasks +type CancelMPPTasksParam struct { + StoreAddr map[string]bool + Reqs []*MPPDispatchRequest +} + +// EstablishMPPConnsParam represents parameter for MPPClient's EstablishMPPConns +type EstablishMPPConnsParam struct { + Ctx context.Context + Req *MPPDispatchRequest + TaskMeta *mpp.TaskMeta +} + +// DispatchMPPTaskParam represents parameter for MPPClient's DispatchMPPTask +type DispatchMPPTaskParam struct { + Ctx context.Context + Req *MPPDispatchRequest + EnableCollectExecutionInfo bool + Bo *tikv.Backoffer +} + +// MPPClient accepts and processes mpp requests. +type MPPClient interface { + // ConstructMPPTasks schedules task for a plan fragment. + // TODO:: This interface will be refined after we support more executors. + ConstructMPPTasks(context.Context, *MPPBuildTasksRequest, time.Duration, tiflashcompute.DispatchPolicy, tiflash.ReplicaRead, func(error)) ([]MPPTaskMeta, error) + + // DispatchMPPTask dispatch mpp task, and returns valid response when retry = false and err is nil. + DispatchMPPTask(DispatchMPPTaskParam) (resp *mpp.DispatchTaskResponse, retry bool, err error) + + // EstablishMPPConns build a mpp connection to receive data, return valid response when err is nil. + EstablishMPPConns(EstablishMPPConnsParam) (*tikvrpc.MPPStreamResponse, error) + + // CancelMPPTasks cancels mpp tasks. + CancelMPPTasks(CancelMPPTasksParam) + + // CheckVisibility checks if it is safe to read using given ts. + CheckVisibility(startTime uint64) error + + // GetMPPStoreCount returns number of TiFlash stores if there is no error, else return (0, error). + GetMPPStoreCount() (int, error) +} + +// ReportStatusRequest wraps mpp ReportStatusRequest +type ReportStatusRequest struct { + Request *mpp.ReportTaskStatusRequest +} + +// MppCoordinator describes the basic api for executing mpp physical plan. +type MppCoordinator interface { + // Execute generates and executes mpp tasks for mpp physical plan. + Execute(ctx context.Context) (Response, []KeyRange, error) + // Next returns next data + Next(ctx context.Context) (ResultSubset, error) + // ReportStatus report task execution info to coordinator + // It shouldn't change any state outside coordinator itself, since the query which generated the coordinator may not exist + ReportStatus(info ReportStatusRequest) error + // Close and release the used resources. + Close() error + // IsClosed returns whether mpp coordinator is closed or not + IsClosed() bool +} + +// MPPBuildTasksRequest request the stores allocation for a mpp plan fragment. +// However, the request doesn't contain the particular plan, because only key ranges take effect on the location assignment. +type MPPBuildTasksRequest struct { + KeyRanges []KeyRange + StartTS uint64 + + PartitionIDAndRanges []PartitionIDAndRanges +} + +// ExchangeCompressionMode means the compress method used in exchange operator +type ExchangeCompressionMode int + +const ( + // ExchangeCompressionModeNONE indicates no compression + ExchangeCompressionModeNONE ExchangeCompressionMode = iota + // ExchangeCompressionModeFast indicates fast compression/decompression speed, compression ratio is lower than HC mode + ExchangeCompressionModeFast + // ExchangeCompressionModeHC indicates high compression (HC) ratio mode + ExchangeCompressionModeHC + // ExchangeCompressionModeUnspecified indicates unspecified compress method, let TiDB choose one + ExchangeCompressionModeUnspecified + + // RecommendedExchangeCompressionMode indicates recommended compression mode + RecommendedExchangeCompressionMode ExchangeCompressionMode = ExchangeCompressionModeFast + + exchangeCompressionModeUnspecifiedName string = "UNSPECIFIED" +) + +// Name returns the name of ExchangeCompressionMode +func (t ExchangeCompressionMode) Name() string { + if t == ExchangeCompressionModeUnspecified { + return exchangeCompressionModeUnspecifiedName + } + return t.ToTipbCompressionMode().String() +} + +// ToExchangeCompressionMode returns the ExchangeCompressionMode from name +func ToExchangeCompressionMode(name string) (ExchangeCompressionMode, bool) { + name = strings.ToUpper(name) + if name == exchangeCompressionModeUnspecifiedName { + return ExchangeCompressionModeUnspecified, true + } + value, ok := tipb.CompressionMode_value[name] + if ok { + return ExchangeCompressionMode(value), true + } + return ExchangeCompressionModeNONE, false +} + +// ToTipbCompressionMode returns tipb.CompressionMode from kv.ExchangeCompressionMode +func (t ExchangeCompressionMode) ToTipbCompressionMode() tipb.CompressionMode { + switch t { + case ExchangeCompressionModeNONE: + return tipb.CompressionMode_NONE + case ExchangeCompressionModeFast: + return tipb.CompressionMode_FAST + case ExchangeCompressionModeHC: + return tipb.CompressionMode_HIGH_COMPRESSION + } + return tipb.CompressionMode_NONE +} diff --git a/kv/option.go b/pkg/kv/option.go similarity index 100% rename from kv/option.go rename to pkg/kv/option.go diff --git a/kv/option_test.go b/pkg/kv/option_test.go similarity index 100% rename from kv/option_test.go rename to pkg/kv/option_test.go diff --git a/pkg/kv/txn.go b/pkg/kv/txn.go new file mode 100644 index 0000000000000..714f7ebe37e3c --- /dev/null +++ b/pkg/kv/txn.go @@ -0,0 +1,248 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package kv + +import ( + "context" + "errors" + "fmt" + "math" + "math/rand" + "sync" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikvrpc" + "github.com/tikv/client-go/v2/tikvrpc/interceptor" + "go.uber.org/zap" +) + +const ( + // TimeToPrintLongTimeInternalTxn is the duration if the internal transaction lasts more than it, + // TiDB prints a log message. + TimeToPrintLongTimeInternalTxn = time.Minute * 5 +) + +var globalInnerTxnTsBox = innerTxnStartTsBox{ + innerTSLock: sync.Mutex{}, + innerTxnStartTsMap: make(map[uint64]struct{}, 256), +} + +type innerTxnStartTsBox struct { + innerTSLock sync.Mutex + innerTxnStartTsMap map[uint64]struct{} +} + +func (ib *innerTxnStartTsBox) storeInnerTxnTS(startTS uint64) { + ib.innerTSLock.Lock() + ib.innerTxnStartTsMap[startTS] = struct{}{} + ib.innerTSLock.Unlock() +} + +func (ib *innerTxnStartTsBox) deleteInnerTxnTS(startTS uint64) { + ib.innerTSLock.Lock() + delete(ib.innerTxnStartTsMap, startTS) + ib.innerTSLock.Unlock() +} + +// GetMinInnerTxnStartTS get the min StartTS between startTSLowerLimit and curMinStartTS in globalInnerTxnTsBox. +func GetMinInnerTxnStartTS(now time.Time, startTSLowerLimit uint64, + curMinStartTS uint64) uint64 { + return globalInnerTxnTsBox.getMinStartTS(now, startTSLowerLimit, curMinStartTS) +} + +func (ib *innerTxnStartTsBox) getMinStartTS(now time.Time, startTSLowerLimit uint64, + curMinStartTS uint64) uint64 { + minStartTS := curMinStartTS + ib.innerTSLock.Lock() + for innerTS := range ib.innerTxnStartTsMap { + PrintLongTimeInternalTxn(now, innerTS, true) + if innerTS > startTSLowerLimit && innerTS < minStartTS { + minStartTS = innerTS + } + } + ib.innerTSLock.Unlock() + return minStartTS +} + +// PrintLongTimeInternalTxn print the internal transaction information. +// runByFunction true means the transaction is run by `RunInNewTxn`, +// +// false means the transaction is run by internal session. +func PrintLongTimeInternalTxn(now time.Time, startTS uint64, runByFunction bool) { + if startTS > 0 { + innerTxnStartTime := oracle.GetTimeFromTS(startTS) + if now.Sub(innerTxnStartTime) > TimeToPrintLongTimeInternalTxn { + callerName := "internal session" + if runByFunction { + callerName = "RunInNewTxn" + } + infoHeader := fmt.Sprintf("An internal transaction running by %s lasts long time", callerName) + + logutil.BgLogger().Info(infoHeader, + zap.Duration("time", now.Sub(innerTxnStartTime)), zap.Uint64("startTS", startTS), + zap.Time("start time", innerTxnStartTime)) + } + } +} + +// RunInNewTxn will run the f in a new transaction environment, should be used by inner txn only. +func RunInNewTxn(ctx context.Context, store Storage, retryable bool, f func(ctx context.Context, txn Transaction) error) error { + var ( + err error + originalTxnTS uint64 + txn Transaction + ) + + defer func() { + globalInnerTxnTsBox.deleteInnerTxnTS(originalTxnTS) + }() + + for i := uint(0); i < maxRetryCnt; i++ { + txn, err = store.Begin() + if err != nil { + logutil.BgLogger().Error("RunInNewTxn", zap.Error(err)) + return err + } + setRequestSourceForInnerTxn(ctx, txn) + + // originalTxnTS is used to trace the original transaction when the function is retryable. + if i == 0 { + originalTxnTS = txn.StartTS() + globalInnerTxnTsBox.storeInnerTxnTS(originalTxnTS) + } + + err = f(ctx, txn) + if err != nil { + err1 := txn.Rollback() + terror.Log(err1) + if retryable && IsTxnRetryableError(err) { + logutil.BgLogger().Warn("RunInNewTxn", + zap.Uint64("retry txn", txn.StartTS()), + zap.Uint64("original txn", originalTxnTS), + zap.Error(err)) + continue + } + return err + } + + failpoint.Inject("mockCommitErrorInNewTxn", func(val failpoint.Value) { + if v := val.(string); len(v) > 0 { + switch v { + case "retry_once": + //nolint:noloopclosure + if i == 0 { + err = ErrTxnRetryable + } + case "no_retry": + failpoint.Return(errors.New("mock commit error")) + } + } + }) + + if err == nil { + err = txn.Commit(ctx) + if err == nil { + break + } + } + if retryable && IsTxnRetryableError(err) { + logutil.BgLogger().Warn("RunInNewTxn", + zap.Uint64("retry txn", txn.StartTS()), + zap.Uint64("original txn", originalTxnTS), + zap.Error(err)) + BackOff(i) + continue + } + return err + } + return err +} + +var ( + // maxRetryCnt represents maximum retry times in RunInNewTxn. + maxRetryCnt uint = 100 + // retryBackOffBase is the initial duration, in microsecond, a failed transaction stays dormancy before it retries + retryBackOffBase = 1 + // retryBackOffCap is the max amount of duration, in microsecond, a failed transaction stays dormancy before it retries + retryBackOffCap = 100 +) + +// BackOff Implements exponential backoff with full jitter. +// Returns real back off time in microsecond. +// See http://www.awsarchitectureblog.com/2015/03/backoff.html. +func BackOff(attempts uint) int { + upper := int(math.Min(float64(retryBackOffCap), float64(retryBackOffBase)*math.Pow(2.0, float64(attempts)))) + sleep := time.Duration(rand.Intn(upper)) * time.Millisecond // #nosec G404 + time.Sleep(sleep) + return int(sleep) +} + +func setRequestSourceForInnerTxn(ctx context.Context, txn Transaction) { + if source := ctx.Value(RequestSourceKey); source != nil { + requestSource := source.(RequestSource) + if requestSource.RequestSourceType != "" { + if !requestSource.RequestSourceInternal { + logutil.Logger(ctx).Warn("`RunInNewTxn` should be used by inner txn only") + } + txn.SetOption(RequestSourceInternal, requestSource.RequestSourceInternal) + txn.SetOption(RequestSourceType, requestSource.RequestSourceType) + if requestSource.ExplicitRequestSourceType != "" { + txn.SetOption(ExplicitRequestSourceType, requestSource.ExplicitRequestSourceType) + } + return + } + } + // panic in test mode in case there are requests without source in the future. + // log warnings in production mode. + if intest.InTest { + panic("unexpected no source type context, if you see this error, " + + "the `RequestSourceTypeKey` is missing in your context") + } else { + logutil.Logger(ctx).Warn("unexpected no source type context, if you see this warning, " + + "the `RequestSourceTypeKey` is missing in the context") + } +} + +// SetTxnResourceGroup update the resource group name of target txn. +func SetTxnResourceGroup(txn Transaction, name string) { + txn.SetOption(ResourceGroupName, name) + failpoint.Inject("TxnResourceGroupChecker", func(val failpoint.Value) { + expectedRgName := val.(string) + validateRNameInterceptor := func(next interceptor.RPCInterceptorFunc) interceptor.RPCInterceptorFunc { + return func(target string, req *tikvrpc.Request) (*tikvrpc.Response, error) { + var rgName *string + switch r := req.Req.(type) { + case *kvrpcpb.PrewriteRequest: + rgName = &r.Context.ResourceControlContext.ResourceGroupName + case *kvrpcpb.CommitRequest: + rgName = &r.Context.ResourceControlContext.ResourceGroupName + case *kvrpcpb.PessimisticLockRequest: + rgName = &r.Context.ResourceControlContext.ResourceGroupName + } + if rgName != nil && *rgName != expectedRgName { + panic(fmt.Sprintf("resource group name not match, expected: %s, actual: %s", expectedRgName, *rgName)) + } + return next(target, req) + } + } + txn.SetOption(RPCInterceptor, interceptor.NewRPCInterceptor("test-validate-rg-name", validateRNameInterceptor)) + }) +} diff --git a/kv/txn_scope_var.go b/pkg/kv/txn_scope_var.go similarity index 98% rename from kv/txn_scope_var.go rename to pkg/kv/txn_scope_var.go index 9e9b6f76f452a..34d0a1a2a9291 100644 --- a/kv/txn_scope_var.go +++ b/pkg/kv/txn_scope_var.go @@ -15,7 +15,7 @@ package kv import ( - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/tikv/client-go/v2/oracle" ) diff --git a/kv/txn_test.go b/pkg/kv/txn_test.go similarity index 100% rename from kv/txn_test.go rename to pkg/kv/txn_test.go diff --git a/kv/utils.go b/pkg/kv/utils.go similarity index 100% rename from kv/utils.go rename to pkg/kv/utils.go diff --git a/kv/utils_test.go b/pkg/kv/utils_test.go similarity index 100% rename from kv/utils_test.go rename to pkg/kv/utils_test.go diff --git a/kv/variables.go b/pkg/kv/variables.go similarity index 100% rename from kv/variables.go rename to pkg/kv/variables.go diff --git a/kv/version.go b/pkg/kv/version.go similarity index 100% rename from kv/version.go rename to pkg/kv/version.go diff --git a/kv/version_test.go b/pkg/kv/version_test.go similarity index 100% rename from kv/version_test.go rename to pkg/kv/version_test.go diff --git a/pkg/lock/BUILD.bazel b/pkg/lock/BUILD.bazel new file mode 100644 index 0000000000000..989c953b38695 --- /dev/null +++ b/pkg/lock/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "lock", + srcs = ["lock.go"], + importpath = "github.com/pingcap/tidb/pkg/lock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/table", + "//pkg/util", + ], +) diff --git a/lock/lock.go b/pkg/lock/lock.go similarity index 94% rename from lock/lock.go rename to pkg/lock/lock.go index c7f843e2bec39..c1064c93dbb94 100644 --- a/lock/lock.go +++ b/pkg/lock/lock.go @@ -17,12 +17,12 @@ package lock import ( "errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util" ) // Checker uses to check tables lock. diff --git a/pkg/meta/BUILD.bazel b/pkg/meta/BUILD.bazel new file mode 100644 index 0000000000000..af33205d801f8 --- /dev/null +++ b/pkg/meta/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "meta", + srcs = [ + "meta.go", + "meta_autoid.go", + ], + importpath = "github.com/pingcap/tidb/pkg/meta", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/structure", + "//pkg/util/dbterror", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + ], +) + +go_test( + name = "meta_test", + timeout = "short", + srcs = [ + "main_test.go", + "meta_test.go", + ], + embed = [":meta"], + flaky = True, + shard_count = 12, + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "//pkg/util", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/meta/autoid/BUILD.bazel b/pkg/meta/autoid/BUILD.bazel new file mode 100644 index 0000000000000..107f9f46cf768 --- /dev/null +++ b/pkg/meta/autoid/BUILD.bazel @@ -0,0 +1,70 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "autoid", + srcs = [ + "autoid.go", + "autoid_service.go", + "errors.go", + "memid.go", + ], + importpath = "github.com/pingcap/tidb/pkg/meta/autoid", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/etcd", + "//pkg/util/execdetails", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/autoid", + "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//:client", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "autoid_test", + timeout = "short", + srcs = [ + "autoid_test.go", + "bench_test.go", + "main_test.go", + "memid_test.go", + "seq_autoid_test.go", + ], + flaky = True, + shard_count = 10, + deps = [ + ":autoid", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/meta/autoid/autoid.go b/pkg/meta/autoid/autoid.go new file mode 100644 index 0000000000000..0555868a52cb7 --- /dev/null +++ b/pkg/meta/autoid/autoid.go @@ -0,0 +1,1364 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package autoid + +import ( + "bytes" + "context" + "fmt" + "math" + "strconv" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/autoid" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/tikv/client-go/v2/txnkv/txnsnapshot" + tikvutil "github.com/tikv/client-go/v2/util" + clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" +) + +// Attention: +// For reading cluster TiDB memory tables, the system schema/table should be same. +// Once the system schema/table id been allocated, it can't be changed any more. +// Change the system schema/table id may have the compatibility problem. +const ( + // SystemSchemaIDFlag is the system schema/table id flag, uses the highest bit position as system schema ID flag, it's exports for test. + SystemSchemaIDFlag = 1 << 62 + // InformationSchemaDBID is the information_schema schema id, it's exports for test. + InformationSchemaDBID int64 = SystemSchemaIDFlag | 1 + // PerformanceSchemaDBID is the performance_schema schema id, it's exports for test. + PerformanceSchemaDBID int64 = SystemSchemaIDFlag | 10000 + // MetricSchemaDBID is the metrics_schema schema id, it's exported for test. + MetricSchemaDBID int64 = SystemSchemaIDFlag | 20000 +) + +const ( + minStep = 30000 + maxStep = 2000000 + defaultConsumeTime = 10 * time.Second + minIncrement = 1 + maxIncrement = 65535 +) + +// RowIDBitLength is the bit number of a row id in TiDB. +const RowIDBitLength = 64 + +const ( + // AutoRandomShardBitsDefault is the default number of shard bits. + AutoRandomShardBitsDefault = 5 + // AutoRandomRangeBitsDefault is the default number of range bits. + AutoRandomRangeBitsDefault = 64 + // AutoRandomShardBitsMax is the max number of shard bits. + AutoRandomShardBitsMax = 15 + // AutoRandomRangeBitsMax is the max number of range bits. + AutoRandomRangeBitsMax = 64 + // AutoRandomRangeBitsMin is the min number of range bits. + AutoRandomRangeBitsMin = 32 + // AutoRandomIncBitsMin is the min number of auto random incremental bits. + AutoRandomIncBitsMin = 27 +) + +// AutoRandomShardBitsNormalize normalizes the auto random shard bits. +func AutoRandomShardBitsNormalize(shard int, colName string) (ret uint64, err error) { + if shard == types.UnspecifiedLength { + return AutoRandomShardBitsDefault, nil + } + if shard <= 0 { + return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(AutoRandomNonPositive) + } + if shard > AutoRandomShardBitsMax { + errMsg := fmt.Sprintf(AutoRandomOverflowErrMsg, AutoRandomShardBitsMax, shard, colName) + return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(errMsg) + } + return uint64(shard), nil +} + +// AutoRandomRangeBitsNormalize normalizes the auto random range bits. +func AutoRandomRangeBitsNormalize(rangeBits int) (ret uint64, err error) { + if rangeBits == types.UnspecifiedLength { + return AutoRandomRangeBitsDefault, nil + } + if rangeBits < AutoRandomRangeBitsMin || rangeBits > AutoRandomRangeBitsMax { + errMsg := fmt.Sprintf(AutoRandomInvalidRangeBits, AutoRandomRangeBitsMin, AutoRandomRangeBitsMax, rangeBits) + return 0, dbterror.ErrInvalidAutoRandom.FastGenByArgs(errMsg) + } + return uint64(rangeBits), nil +} + +// Test needs to change it, so it's a variable. +var step = int64(30000) + +// AllocatorType is the type of allocator for generating auto-id. Different type of allocators use different key-value pairs. +type AllocatorType uint8 + +const ( + // RowIDAllocType indicates the allocator is used to allocate row id. + RowIDAllocType AllocatorType = iota + // AutoIncrementType indicates the allocator is used to allocate auto increment value. + AutoIncrementType + // AutoRandomType indicates the allocator is used to allocate auto-shard id. + AutoRandomType + // SequenceType indicates the allocator is used to allocate sequence value. + SequenceType +) + +func (a AllocatorType) String() string { + switch a { + case RowIDAllocType: + return "_tidb_rowid" + case AutoIncrementType: + return "auto_increment" + case AutoRandomType: + return "auto_random" + case SequenceType: + return "sequence" + } + return "unknown" +} + +// CustomAutoIncCacheOption is one kind of AllocOption to customize the allocator step length. +type CustomAutoIncCacheOption int64 + +// ApplyOn implements the AllocOption interface. +func (step CustomAutoIncCacheOption) ApplyOn(alloc *allocator) { + if step == 0 { + return + } + alloc.step = int64(step) + alloc.customStep = true +} + +// AllocOptionTableInfoVersion is used to pass the TableInfo.Version to the allocator. +type AllocOptionTableInfoVersion uint16 + +// ApplyOn implements the AllocOption interface. +func (v AllocOptionTableInfoVersion) ApplyOn(alloc *allocator) { + alloc.tbVersion = uint16(v) +} + +// AllocOption is a interface to define allocator custom options coming in future. +type AllocOption interface { + ApplyOn(*allocator) +} + +// Allocator is an auto increment id generator. +// Just keep id unique actually. +type Allocator interface { + // Alloc allocs N consecutive autoID for table with tableID, returning (min, max] of the allocated autoID batch. + // It gets a batch of autoIDs at a time. So it does not need to access storage for each call. + // The consecutive feature is used to insert multiple rows in a statement. + // increment & offset is used to validate the start position (the allocator's base is not always the last allocated id). + // The returned range is (min, max]: + // case increment=1 & offset=1: you can derive the ids like min+1, min+2... max. + // case increment=x & offset=y: you firstly need to seek to firstID by `SeekToFirstAutoIDXXX`, then derive the IDs like firstID, firstID + increment * 2... in the caller. + Alloc(ctx context.Context, n uint64, increment, offset int64) (int64, int64, error) + + // AllocSeqCache allocs sequence batch value cached in table level(rather than in alloc), the returned range covering + // the size of sequence cache with it's increment. The returned round indicates the sequence cycle times if it is with + // cycle option. + AllocSeqCache() (min int64, max int64, round int64, err error) + + // Rebase rebases the autoID base for table with tableID and the new base value. + // If allocIDs is true, it will allocate some IDs and save to the cache. + // If allocIDs is false, it will not allocate IDs. + Rebase(ctx context.Context, newBase int64, allocIDs bool) error + + // ForceRebase set the next global auto ID to newBase. + ForceRebase(newBase int64) error + + // RebaseSeq rebases the sequence value in number axis with tableID and the new base value. + RebaseSeq(newBase int64) (int64, bool, error) + + // Base return the current base of Allocator. + Base() int64 + // End is only used for test. + End() int64 + // NextGlobalAutoID returns the next global autoID. + NextGlobalAutoID() (int64, error) + GetType() AllocatorType +} + +// Allocators represents a set of `Allocator`s. +type Allocators struct { + SepAutoInc bool + Allocs []Allocator +} + +// NewAllocators packs multiple `Allocator`s into Allocators. +func NewAllocators(sepAutoInc bool, allocators ...Allocator) Allocators { + return Allocators{ + SepAutoInc: sepAutoInc, + Allocs: allocators, + } +} + +// Append add an allocator to the allocators. +func (all Allocators) Append(a Allocator) Allocators { + return Allocators{ + SepAutoInc: all.SepAutoInc, + Allocs: append(all.Allocs, a), + } +} + +// Get returns the Allocator according to the AllocatorType. +func (all Allocators) Get(allocType AllocatorType) Allocator { + if !all.SepAutoInc { + if allocType == AutoIncrementType { + allocType = RowIDAllocType + } + } + + for _, a := range all.Allocs { + if a.GetType() == allocType { + return a + } + } + return nil +} + +// Filter filters all the allocators that match pred. +func (all Allocators) Filter(pred func(Allocator) bool) Allocators { + var ret []Allocator + for _, a := range all.Allocs { + if pred(a) { + ret = append(ret, a) + } + } + return Allocators{ + SepAutoInc: all.SepAutoInc, + Allocs: ret, + } +} + +type allocator struct { + mu sync.Mutex + base int64 + end int64 + store kv.Storage + // dbID is current database's ID. + dbID int64 + tbID int64 + tbVersion uint16 + isUnsigned bool + lastAllocTime time.Time + step int64 + customStep bool + allocType AllocatorType + sequence *model.SequenceInfo +} + +// GetStep is only used by tests +func GetStep() int64 { + return step +} + +// SetStep is only used by tests +func SetStep(s int64) { + step = s +} + +// Base implements autoid.Allocator Base interface. +func (alloc *allocator) Base() int64 { + alloc.mu.Lock() + defer alloc.mu.Unlock() + return alloc.base +} + +// End implements autoid.Allocator End interface. +func (alloc *allocator) End() int64 { + alloc.mu.Lock() + defer alloc.mu.Unlock() + return alloc.end +} + +// NextGlobalAutoID implements autoid.Allocator NextGlobalAutoID interface. +func (alloc *allocator) NextGlobalAutoID() (int64, error) { + var autoID int64 + startTime := time.Now() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + var err1 error + autoID, err1 = alloc.getIDAccessor(txn).Get() + if err1 != nil { + return errors.Trace(err1) + } + return nil + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.GlobalAutoID, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if alloc.isUnsigned { + return int64(uint64(autoID) + 1), err + } + return autoID + 1, err +} + +func (alloc *allocator) rebase4Unsigned(ctx context.Context, requiredBase uint64, allocIDs bool) error { + // Satisfied by alloc.base, nothing to do. + if requiredBase <= uint64(alloc.base) { + return nil + } + // Satisfied by alloc.end, need to update alloc.base. + if requiredBase <= uint64(alloc.end) { + alloc.base = int64(requiredBase) + return nil + } + + ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) + if allocatorStats != nil { + allocatorStats.rebaseCount++ + defer func() { + if commitDetail != nil { + allocatorStats.mergeCommitDetail(*commitDetail) + } + }() + } + var newBase, newEnd uint64 + startTime := time.Now() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + if allocatorStats != nil { + txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) + } + idAcc := alloc.getIDAccessor(txn) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + uCurrentEnd := uint64(currentEnd) + if allocIDs { + newBase = mathutil.Max(uCurrentEnd, requiredBase) + newEnd = mathutil.Min(math.MaxUint64-uint64(alloc.step), newBase) + uint64(alloc.step) + } else { + if uCurrentEnd >= requiredBase { + newBase = uCurrentEnd + newEnd = uCurrentEnd + // Required base satisfied, we don't need to update KV. + return nil + } + // If we don't want to allocate IDs, for example when creating a table with a given base value, + // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 + // will be allocated, so we need to increase the end to exactly the requiredBase. + newBase = requiredBase + newEnd = requiredBase + } + _, err1 = idAcc.Inc(int64(newEnd - uCurrentEnd)) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + alloc.base, alloc.end = int64(newBase), int64(newEnd) + return nil +} + +func (alloc *allocator) rebase4Signed(ctx context.Context, requiredBase int64, allocIDs bool) error { + // Satisfied by alloc.base, nothing to do. + if requiredBase <= alloc.base { + return nil + } + // Satisfied by alloc.end, need to update alloc.base. + if requiredBase <= alloc.end { + alloc.base = requiredBase + return nil + } + + ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) + if allocatorStats != nil { + allocatorStats.rebaseCount++ + defer func() { + if commitDetail != nil { + allocatorStats.mergeCommitDetail(*commitDetail) + } + }() + } + var newBase, newEnd int64 + startTime := time.Now() + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + if allocatorStats != nil { + txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) + } + idAcc := alloc.getIDAccessor(txn) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + if allocIDs { + newBase = mathutil.Max(currentEnd, requiredBase) + newEnd = mathutil.Min(math.MaxInt64-alloc.step, newBase) + alloc.step + } else { + if currentEnd >= requiredBase { + newBase = currentEnd + newEnd = currentEnd + // Required base satisfied, we don't need to update KV. + return nil + } + // If we don't want to allocate IDs, for example when creating a table with a given base value, + // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 + // will be allocated, so we need to increase the end to exactly the requiredBase. + newBase = requiredBase + newEnd = requiredBase + } + _, err1 = idAcc.Inc(newEnd - currentEnd) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + alloc.base, alloc.end = newBase, newEnd + return nil +} + +// rebase4Sequence won't alloc batch immediately, cause it won't cache value in allocator. +func (alloc *allocator) rebase4Sequence(requiredBase int64) (int64, bool, error) { + startTime := time.Now() + alreadySatisfied := false + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) + currentEnd, err := acc.SequenceValue().Get() + if err != nil { + return err + } + if alloc.sequence.Increment > 0 { + if currentEnd >= requiredBase { + // Required base satisfied, we don't need to update KV. + alreadySatisfied = true + return nil + } + } else { + if currentEnd <= requiredBase { + // Required base satisfied, we don't need to update KV. + alreadySatisfied = true + return nil + } + } + + // If we don't want to allocate IDs, for example when creating a table with a given base value, + // We need to make sure when other TiDB server allocates ID for the first time, requiredBase + 1 + // will be allocated, so we need to increase the end to exactly the requiredBase. + _, err = acc.SequenceValue().Inc(requiredBase - currentEnd) + return err + }) + // TODO: sequence metrics + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return 0, false, err + } + if alreadySatisfied { + return 0, true, nil + } + return requiredBase, false, err +} + +// Rebase implements autoid.Allocator Rebase interface. +// The requiredBase is the minimum base value after Rebase. +// The real base may be greater than the required base. +func (alloc *allocator) Rebase(ctx context.Context, requiredBase int64, allocIDs bool) error { + alloc.mu.Lock() + defer alloc.mu.Unlock() + if alloc.isUnsigned { + return alloc.rebase4Unsigned(ctx, uint64(requiredBase), allocIDs) + } + return alloc.rebase4Signed(ctx, requiredBase, allocIDs) +} + +// ForceRebase implements autoid.Allocator ForceRebase interface. +func (alloc *allocator) ForceRebase(requiredBase int64) error { + if requiredBase == -1 { + return ErrAutoincReadFailed.GenWithStack("Cannot force rebase the next global ID to '0'") + } + alloc.mu.Lock() + defer alloc.mu.Unlock() + startTime := time.Now() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + idAcc := alloc.getIDAccessor(txn) + currentEnd, err1 := idAcc.Get() + if err1 != nil { + return err1 + } + var step int64 + if !alloc.isUnsigned { + step = requiredBase - currentEnd + } else { + uRequiredBase, uCurrentEnd := uint64(requiredBase), uint64(currentEnd) + step = int64(uRequiredBase - uCurrentEnd) + } + _, err1 = idAcc.Inc(step) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDRebase, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return err + } + alloc.base, alloc.end = requiredBase, requiredBase + return nil +} + +// Rebase implements autoid.Allocator RebaseSeq interface. +// The return value is quite same as expression function, bool means whether it should be NULL, +// here it will be used in setval expression function (true meaning the set value has been satisfied, return NULL). +// case1:When requiredBase is satisfied with current value, it will return (0, true, nil), +// case2:When requiredBase is successfully set in, it will return (requiredBase, false, nil). +// If some error occurs in the process, return it immediately. +func (alloc *allocator) RebaseSeq(requiredBase int64) (int64, bool, error) { + alloc.mu.Lock() + defer alloc.mu.Unlock() + return alloc.rebase4Sequence(requiredBase) +} + +func (alloc *allocator) GetType() AllocatorType { + return alloc.allocType +} + +// NextStep return new auto id step according to previous step and consuming time. +func NextStep(curStep int64, consumeDur time.Duration) int64 { + failpoint.Inject("mockAutoIDCustomize", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(3) + } + }) + failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(step) + } + }) + + consumeRate := defaultConsumeTime.Seconds() / consumeDur.Seconds() + res := int64(float64(curStep) * consumeRate) + if res < minStep { + return minStep + } else if res > maxStep { + return maxStep + } + return res +} + +// MockForTest is exported for testing. +// The actual implementation is in github.com/pingcap/tidb/pkg/autoid_service because of the +// package circle depending issue. +var MockForTest func(kv.Storage) autoid.AutoIDAllocClient + +func newSinglePointAlloc(store kv.Storage, dbID, tblID int64, isUnsigned bool) *singlePointAlloc { + ebd, ok := store.(kv.EtcdBackend) + if !ok { + // newSinglePointAlloc fail because not etcd background + // This could happen in the server package unit test + return nil + } + + addrs, err := ebd.EtcdAddrs() + if err != nil { + panic(err) + } + + keyspaceID := uint32(store.GetCodec().GetKeyspaceID()) + spa := &singlePointAlloc{ + dbID: dbID, + tblID: tblID, + isUnsigned: isUnsigned, + keyspaceID: keyspaceID, + } + if len(addrs) > 0 { + etcdCli, err := clientv3.New(clientv3.Config{ + Endpoints: addrs, + AutoSyncInterval: 30 * time.Second, + TLS: ebd.TLSConfig(), + }) + etcd.SetEtcdCliByNamespace(etcdCli, keyspace.MakeKeyspaceEtcdNamespaceSlash(store.GetCodec())) + if err != nil { + logutil.BgLogger().Error("fail to connect etcd, fallback to default", zap.String("category", "autoid client"), zap.Error(err)) + return nil + } + spa.clientDiscover = clientDiscover{etcdCli: etcdCli} + } else { + spa.clientDiscover = clientDiscover{} + spa.mu.AutoIDAllocClient = MockForTest(store) + } + + // mockAutoIDChange failpoint is not implemented in this allocator, so fallback to use the default one. + failpoint.Inject("mockAutoIDChange", func(val failpoint.Value) { + if val.(bool) { + spa = nil + } + }) + return spa +} + +// NewAllocator returns a new auto increment id generator on the store. +func NewAllocator(store kv.Storage, dbID, tbID int64, isUnsigned bool, + allocType AllocatorType, opts ...AllocOption) Allocator { + alloc := &allocator{ + store: store, + dbID: dbID, + tbID: tbID, + isUnsigned: isUnsigned, + step: step, + lastAllocTime: time.Now(), + allocType: allocType, + } + for _, fn := range opts { + fn.ApplyOn(alloc) + } + + // Use the MySQL compatible AUTO_INCREMENT mode. + if alloc.customStep && alloc.step == 1 && alloc.tbVersion >= model.TableInfoVersion5 { + if allocType == AutoIncrementType { + alloc1 := newSinglePointAlloc(store, dbID, tbID, isUnsigned) + if alloc1 != nil { + return alloc1 + } + } else if allocType == RowIDAllocType { + // Now that the autoid and rowid allocator are separated, the AUTO_ID_CACHE 1 setting should not make + // the rowid allocator do not use cache. + alloc.customStep = false + alloc.step = step + } + } + + return alloc +} + +// NewSequenceAllocator returns a new sequence value generator on the store. +func NewSequenceAllocator(store kv.Storage, dbID, tbID int64, info *model.SequenceInfo) Allocator { + return &allocator{ + store: store, + dbID: dbID, + tbID: tbID, + // Sequence allocator is always signed. + isUnsigned: false, + lastAllocTime: time.Now(), + allocType: SequenceType, + sequence: info, + } +} + +// TODO: Handle allocators when changing Table ID during ALTER TABLE t PARTITION BY ... + +// NewAllocatorsFromTblInfo creates an array of allocators of different types with the information of model.TableInfo. +func NewAllocatorsFromTblInfo(store kv.Storage, schemaID int64, tblInfo *model.TableInfo) Allocators { + var allocs []Allocator + dbID := tblInfo.GetDBID(schemaID) + idCacheOpt := CustomAutoIncCacheOption(tblInfo.AutoIdCache) + tblVer := AllocOptionTableInfoVersion(tblInfo.Version) + + hasRowID := !tblInfo.PKIsHandle && !tblInfo.IsCommonHandle + hasAutoIncID := tblInfo.GetAutoIncrementColInfo() != nil + if hasRowID || hasAutoIncID { + alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), RowIDAllocType, idCacheOpt, tblVer) + allocs = append(allocs, alloc) + } + if hasAutoIncID { + alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoIncColUnsigned(), AutoIncrementType, idCacheOpt, tblVer) + allocs = append(allocs, alloc) + } + hasAutoRandID := tblInfo.ContainsAutoRandomBits() + if hasAutoRandID { + alloc := NewAllocator(store, dbID, tblInfo.ID, tblInfo.IsAutoRandomBitColUnsigned(), AutoRandomType, idCacheOpt, tblVer) + allocs = append(allocs, alloc) + } + if tblInfo.IsSequence() { + allocs = append(allocs, NewSequenceAllocator(store, dbID, tblInfo.ID, tblInfo.Sequence)) + } + return NewAllocators(tblInfo.SepAutoInc(), allocs...) +} + +// Alloc implements autoid.Allocator Alloc interface. +// For autoIncrement allocator, the increment and offset should always be positive in [1, 65535]. +// Attention: +// When increment and offset is not the default value(1), the return range (min, max] need to +// calculate the correct start position rather than simply the add 1 to min. Then you can derive +// the successive autoID by adding increment * cnt to firstID for (n-1) times. +// +// Example: +// (6, 13] is returned, increment = 4, offset = 1, n = 2. +// 6 is the last allocated value for other autoID or handle, maybe with different increment and step, +// but actually we don't care about it, all we need is to calculate the new autoID corresponding to the +// increment and offset at this time now. To simplify the rule is like (ID - offset) % increment = 0, +// so the first autoID should be 9, then add increment to it to get 13. +func (alloc *allocator) Alloc(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { + if alloc.tbID == 0 { + return 0, 0, errInvalidTableID.GenWithStackByArgs("Invalid tableID") + } + if n == 0 { + return 0, 0, nil + } + if alloc.allocType == AutoIncrementType || alloc.allocType == RowIDAllocType { + if !validIncrementAndOffset(increment, offset) { + return 0, 0, errInvalidIncrementAndOffset.GenWithStackByArgs(increment, offset) + } + } + alloc.mu.Lock() + defer alloc.mu.Unlock() + if alloc.isUnsigned { + return alloc.alloc4Unsigned(ctx, n, increment, offset) + } + return alloc.alloc4Signed(ctx, n, increment, offset) +} + +func (alloc *allocator) AllocSeqCache() (min int64, max int64, round int64, err error) { + alloc.mu.Lock() + defer alloc.mu.Unlock() + return alloc.alloc4Sequence() +} + +func validIncrementAndOffset(increment, offset int64) bool { + return (increment >= minIncrement && increment <= maxIncrement) && (offset >= minIncrement && offset <= maxIncrement) +} + +// CalcNeededBatchSize is used to calculate batch size for autoID allocation. +// It firstly seeks to the first valid position based on increment and offset, +// then plus the length remained, which could be (n-1) * increment. +func CalcNeededBatchSize(base, n, increment, offset int64, isUnsigned bool) int64 { + if increment == 1 { + return n + } + if isUnsigned { + // SeekToFirstAutoIDUnSigned seeks to the next unsigned valid position. + nr := SeekToFirstAutoIDUnSigned(uint64(base), uint64(increment), uint64(offset)) + // Calculate the total batch size needed. + nr += (uint64(n) - 1) * uint64(increment) + return int64(nr - uint64(base)) + } + nr := SeekToFirstAutoIDSigned(base, increment, offset) + // Calculate the total batch size needed. + nr += (n - 1) * increment + return nr - base +} + +// CalcSequenceBatchSize calculate the next sequence batch size. +func CalcSequenceBatchSize(base, size, increment, offset, min, max int64) (int64, error) { + // The sequence is positive growth. + if increment > 0 { + if increment == 1 { + // Sequence is already allocated to the end. + if base >= max { + return 0, ErrAutoincReadFailed + } + // The rest of sequence < cache size, return the rest. + if max-base < size { + return max - base, nil + } + // The rest of sequence is adequate. + return size, nil + } + nr, ok := SeekToFirstSequenceValue(base, increment, offset, min, max) + if !ok { + return 0, ErrAutoincReadFailed + } + // The rest of sequence < cache size, return the rest. + if max-nr < (size-1)*increment { + return max - base, nil + } + return (nr - base) + (size-1)*increment, nil + } + // The sequence is negative growth. + if increment == -1 { + if base <= min { + return 0, ErrAutoincReadFailed + } + if base-min < size { + return base - min, nil + } + return size, nil + } + nr, ok := SeekToFirstSequenceValue(base, increment, offset, min, max) + if !ok { + return 0, ErrAutoincReadFailed + } + // The rest of sequence < cache size, return the rest. + if nr-min < (size-1)*(-increment) { + return base - min, nil + } + return (base - nr) + (size-1)*(-increment), nil +} + +// SeekToFirstSequenceValue seeks to the next valid value (must be in range of [MIN, max]), +// the bool indicates whether the first value is got. +// The seeking formula is describe as below: +// +// nr := (base + increment - offset) / increment +// +// first := nr*increment + offset +// Because formula computation will overflow Int64, so we transfer it to uint64 for distance computation. +func SeekToFirstSequenceValue(base, increment, offset, min, max int64) (int64, bool) { + if increment > 0 { + // Sequence is already allocated to the end. + if base >= max { + return 0, false + } + uMax := EncodeIntToCmpUint(max) + uBase := EncodeIntToCmpUint(base) + uOffset := EncodeIntToCmpUint(offset) + uIncrement := uint64(increment) + if uMax-uBase < uIncrement { + // Enum the possible first value. + for i := uBase + 1; i <= uMax; i++ { + if (i-uOffset)%uIncrement == 0 { + return DecodeCmpUintToInt(i), true + } + } + return 0, false + } + nr := (uBase + uIncrement - uOffset) / uIncrement + nr = nr*uIncrement + uOffset + first := DecodeCmpUintToInt(nr) + return first, true + } + // Sequence is already allocated to the end. + if base <= min { + return 0, false + } + uMin := EncodeIntToCmpUint(min) + uBase := EncodeIntToCmpUint(base) + uOffset := EncodeIntToCmpUint(offset) + uIncrement := uint64(-increment) + if uBase-uMin < uIncrement { + // Enum the possible first value. + for i := uBase - 1; i >= uMin; i-- { + if (uOffset-i)%uIncrement == 0 { + return DecodeCmpUintToInt(i), true + } + } + return 0, false + } + nr := (uOffset - uBase + uIncrement) / uIncrement + nr = uOffset - nr*uIncrement + first := DecodeCmpUintToInt(nr) + return first, true +} + +// SeekToFirstAutoIDSigned seeks to the next valid signed position. +func SeekToFirstAutoIDSigned(base, increment, offset int64) int64 { + nr := (base + increment - offset) / increment + nr = nr*increment + offset + return nr +} + +// SeekToFirstAutoIDUnSigned seeks to the next valid unsigned position. +func SeekToFirstAutoIDUnSigned(base, increment, offset uint64) uint64 { + nr := (base + increment - offset) / increment + nr = nr*increment + offset + return nr +} + +func (alloc *allocator) alloc4Signed(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { + // Check offset rebase if necessary. + if offset-1 > alloc.base { + if err := alloc.rebase4Signed(ctx, offset-1, true); err != nil { + return 0, 0, err + } + } + // CalcNeededBatchSize calculates the total batch size needed. + n1 := CalcNeededBatchSize(alloc.base, int64(n), increment, offset, alloc.isUnsigned) + + // Condition alloc.base+N1 > alloc.end will overflow when alloc.base + N1 > MaxInt64. So need this. + if math.MaxInt64-alloc.base <= n1 { + return 0, 0, ErrAutoincReadFailed + } + // The local rest is not enough for allocN, skip it. + if alloc.base+n1 > alloc.end { + var newBase, newEnd int64 + startTime := time.Now() + nextStep := alloc.step + if !alloc.customStep && alloc.end > 0 { + // Although it may skip a segment here, we still think it is consumed. + consumeDur := startTime.Sub(alloc.lastAllocTime) + nextStep = NextStep(alloc.step, consumeDur) + } + + ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) + if allocatorStats != nil { + allocatorStats.allocCount++ + defer func() { + if commitDetail != nil { + allocatorStats.mergeCommitDetail(*commitDetail) + } + }() + } + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + defer tracing.StartRegion(ctx, "alloc.alloc4Signed").End() + if allocatorStats != nil { + txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) + } + + idAcc := alloc.getIDAccessor(txn) + var err1 error + newBase, err1 = idAcc.Get() + if err1 != nil { + return err1 + } + // CalcNeededBatchSize calculates the total batch size needed on global base. + n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } + tmpStep := mathutil.Min(math.MaxInt64-newBase, nextStep) + // The global rest is not enough for alloc. + if tmpStep < n1 { + return ErrAutoincReadFailed + } + newEnd, err1 = idAcc.Inc(tmpStep) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return 0, 0, err + } + // Store the step for non-customized-step allocator to calculate next dynamic step. + if !alloc.customStep { + alloc.step = nextStep + } + alloc.lastAllocTime = time.Now() + if newBase == math.MaxInt64 { + return 0, 0, ErrAutoincReadFailed + } + alloc.base, alloc.end = newBase, newEnd + } + logutil.Logger(context.TODO()).Debug("alloc N signed ID", + zap.Uint64("from ID", uint64(alloc.base)), + zap.Uint64("to ID", uint64(alloc.base+n1)), + zap.Int64("table ID", alloc.tbID), + zap.Int64("database ID", alloc.dbID)) + min = alloc.base + alloc.base += n1 + return min, alloc.base, nil +} + +func (alloc *allocator) alloc4Unsigned(ctx context.Context, n uint64, increment, offset int64) (min int64, max int64, err error) { + // Check offset rebase if necessary. + if uint64(offset-1) > uint64(alloc.base) { + if err := alloc.rebase4Unsigned(ctx, uint64(offset-1), true); err != nil { + return 0, 0, err + } + } + // CalcNeededBatchSize calculates the total batch size needed. + n1 := CalcNeededBatchSize(alloc.base, int64(n), increment, offset, alloc.isUnsigned) + + // Condition alloc.base+n1 > alloc.end will overflow when alloc.base + n1 > MaxInt64. So need this. + if math.MaxUint64-uint64(alloc.base) <= uint64(n1) { + return 0, 0, ErrAutoincReadFailed + } + // The local rest is not enough for alloc, skip it. + if uint64(alloc.base)+uint64(n1) > uint64(alloc.end) { + var newBase, newEnd int64 + startTime := time.Now() + nextStep := alloc.step + if !alloc.customStep { + // Although it may skip a segment here, we still treat it as consumed. + consumeDur := startTime.Sub(alloc.lastAllocTime) + nextStep = NextStep(alloc.step, consumeDur) + } + + ctx, allocatorStats, commitDetail := getAllocatorStatsFromCtx(ctx) + if allocatorStats != nil { + allocatorStats.allocCount++ + defer func() { + if commitDetail != nil { + allocatorStats.mergeCommitDetail(*commitDetail) + } + }() + } + + if codeRun := ctx.Value("testIssue39528"); codeRun != nil { + *(codeRun.(*bool)) = true + return 0, 0, errors.New("mock error for test") + } + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + defer tracing.StartRegion(ctx, "alloc.alloc4Unsigned").End() + if allocatorStats != nil { + txn.SetOption(kv.CollectRuntimeStats, allocatorStats.SnapshotRuntimeStats) + } + + idAcc := alloc.getIDAccessor(txn) + var err1 error + newBase, err1 = idAcc.Get() + if err1 != nil { + return err1 + } + // CalcNeededBatchSize calculates the total batch size needed on new base. + n1 = CalcNeededBatchSize(newBase, int64(n), increment, offset, alloc.isUnsigned) + // Although the step is customized by user, we still need to make sure nextStep is big enough for insert batch. + if nextStep < n1 { + nextStep = n1 + } + tmpStep := int64(mathutil.Min(math.MaxUint64-uint64(newBase), uint64(nextStep))) + // The global rest is not enough for alloc. + if tmpStep < n1 { + return ErrAutoincReadFailed + } + newEnd, err1 = idAcc.Inc(tmpStep) + return err1 + }) + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return 0, 0, err + } + // Store the step for non-customized-step allocator to calculate next dynamic step. + if !alloc.customStep { + alloc.step = nextStep + } + alloc.lastAllocTime = time.Now() + if uint64(newBase) == math.MaxUint64 { + return 0, 0, ErrAutoincReadFailed + } + alloc.base, alloc.end = newBase, newEnd + } + logutil.Logger(context.TODO()).Debug("alloc unsigned ID", + zap.Uint64(" from ID", uint64(alloc.base)), + zap.Uint64("to ID", uint64(alloc.base+n1)), + zap.Int64("table ID", alloc.tbID), + zap.Int64("database ID", alloc.dbID)) + min = alloc.base + // Use uint64 n directly. + alloc.base = int64(uint64(alloc.base) + uint64(n1)) + return min, alloc.base, nil +} + +func getAllocatorStatsFromCtx(ctx context.Context) (context.Context, *AllocatorRuntimeStats, **tikvutil.CommitDetails) { + var allocatorStats *AllocatorRuntimeStats + var commitDetail *tikvutil.CommitDetails + ctxValue := ctx.Value(AllocatorRuntimeStatsCtxKey) + if ctxValue != nil { + allocatorStats = ctxValue.(*AllocatorRuntimeStats) + ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) + } + return ctx, allocatorStats, &commitDetail +} + +// alloc4Sequence is used to alloc value for sequence, there are several aspects different from autoid logic. +// 1: sequence allocation don't need check rebase. +// 2: sequence allocation don't need auto step. +// 3: sequence allocation may have negative growth. +// 4: sequence allocation batch length can be dissatisfied. +// 5: sequence batch allocation will be consumed immediately. +func (alloc *allocator) alloc4Sequence() (min int64, max int64, round int64, err error) { + increment := alloc.sequence.Increment + offset := alloc.sequence.Start + minValue := alloc.sequence.MinValue + maxValue := alloc.sequence.MaxValue + cacheSize := alloc.sequence.CacheValue + if !alloc.sequence.Cache { + cacheSize = 1 + } + + var newBase, newEnd int64 + startTime := time.Now() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, alloc.store, true, func(ctx context.Context, txn kv.Transaction) error { + acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) + var ( + err1 error + seqStep int64 + ) + // Get the real offset if the sequence is in cycle. + // round is used to count cycle times in sequence with cycle option. + if alloc.sequence.Cycle { + // GetSequenceCycle is used to get the flag `round`, which indicates whether the sequence is already in cycle. + round, err1 = acc.SequenceCycle().Get() + if err1 != nil { + return err1 + } + if round > 0 { + if increment > 0 { + offset = alloc.sequence.MinValue + } else { + offset = alloc.sequence.MaxValue + } + } + } + + // Get the global new base. + newBase, err1 = acc.SequenceValue().Get() + if err1 != nil { + return err1 + } + + // CalcNeededBatchSize calculates the total batch size needed. + seqStep, err1 = CalcSequenceBatchSize(newBase, cacheSize, increment, offset, minValue, maxValue) + + if err1 != nil && err1 == ErrAutoincReadFailed { + if !alloc.sequence.Cycle { + return err1 + } + // Reset the sequence base and offset. + if alloc.sequence.Increment > 0 { + newBase = alloc.sequence.MinValue - 1 + offset = alloc.sequence.MinValue + } else { + newBase = alloc.sequence.MaxValue + 1 + offset = alloc.sequence.MaxValue + } + err1 = acc.SequenceValue().Put(newBase) + if err1 != nil { + return err1 + } + + // Reset sequence round state value. + round++ + // SetSequenceCycle is used to store the flag `round` which indicates whether the sequence is already in cycle. + // round > 0 means the sequence is already in cycle, so the offset should be minvalue / maxvalue rather than sequence.start. + // TiDB is a stateless node, it should know whether the sequence is already in cycle when restart. + err1 = acc.SequenceCycle().Put(round) + if err1 != nil { + return err1 + } + + // Recompute the sequence next batch size. + seqStep, err1 = CalcSequenceBatchSize(newBase, cacheSize, increment, offset, minValue, maxValue) + if err1 != nil { + return err1 + } + } + var delta int64 + if alloc.sequence.Increment > 0 { + delta = seqStep + } else { + delta = -seqStep + } + newEnd, err1 = acc.SequenceValue().Inc(delta) + return err1 + }) + + // TODO: sequence metrics + metrics.AutoIDHistogram.WithLabelValues(metrics.TableAutoIDAlloc, metrics.RetLabel(err)).Observe(time.Since(startTime).Seconds()) + if err != nil { + return 0, 0, 0, err + } + logutil.Logger(context.TODO()).Debug("alloc sequence value", + zap.Uint64(" from value", uint64(newBase)), + zap.Uint64("to value", uint64(newEnd)), + zap.Int64("table ID", alloc.tbID), + zap.Int64("database ID", alloc.dbID)) + return newBase, newEnd, round, nil +} + +func (alloc *allocator) getIDAccessor(txn kv.Transaction) meta.AutoIDAccessor { + acc := meta.NewMeta(txn).GetAutoIDAccessors(alloc.dbID, alloc.tbID) + switch alloc.allocType { + case RowIDAllocType: + return acc.RowID() + case AutoIncrementType: + return acc.IncrementID(alloc.tbVersion) + case AutoRandomType: + return acc.RandomID() + case SequenceType: + return acc.SequenceValue() + } + return nil +} + +const signMask uint64 = 0x8000000000000000 + +// EncodeIntToCmpUint make int v to comparable uint type +func EncodeIntToCmpUint(v int64) uint64 { + return uint64(v) ^ signMask +} + +// DecodeCmpUintToInt decodes the u that encoded by EncodeIntToCmpUint +func DecodeCmpUintToInt(u uint64) int64 { + return int64(u ^ signMask) +} + +// TestModifyBaseAndEndInjection exported for testing modifying the base and end. +func TestModifyBaseAndEndInjection(alloc Allocator, base, end int64) { + alloc.(*allocator).mu.Lock() + alloc.(*allocator).base = base + alloc.(*allocator).end = end + alloc.(*allocator).mu.Unlock() +} + +// ShardIDFormat is used to calculate the bit length of different segments in auto id. +// Generally, an auto id is consist of 4 segments: sign bit, reserved bits, shard bits and incremental bits. +// Take "a BIGINT AUTO_INCREMENT PRIMARY KEY" as an example, assume that the `shard_row_id_bits` = 5, +// the layout is like +// +// | [sign_bit] (1 bit) | [reserved bits] (0 bits) | [shard_bits] (5 bits) | [incremental_bits] (64-1-5=58 bits) | +// +// Please always use NewShardIDFormat() to instantiate. +type ShardIDFormat struct { + FieldType *types.FieldType + ShardBits uint64 + // Derived fields. + IncrementalBits uint64 +} + +// NewShardIDFormat create an instance of ShardIDFormat. +// RangeBits means the bit length of the sign bit + shard bits + incremental bits. +// If RangeBits is 0, it will be calculated according to field type automatically. +func NewShardIDFormat(fieldType *types.FieldType, shardBits, rangeBits uint64) ShardIDFormat { + var incrementalBits uint64 + if rangeBits == 0 { + // Zero means that the range bits is not specified. We interpret it as the length of BIGINT. + incrementalBits = RowIDBitLength - shardBits + } else { + incrementalBits = rangeBits - shardBits + } + hasSignBit := !mysql.HasUnsignedFlag(fieldType.GetFlag()) + if hasSignBit { + incrementalBits-- + } + return ShardIDFormat{ + FieldType: fieldType, + ShardBits: shardBits, + IncrementalBits: incrementalBits, + } +} + +// IncrementalBitsCapacity returns the max capacity of incremental section of the current format. +func (s *ShardIDFormat) IncrementalBitsCapacity() uint64 { + return uint64(s.IncrementalMask()) +} + +// IncrementalMask returns 00..0[11..1], where [11..1] is the incremental part of the current format. +func (s *ShardIDFormat) IncrementalMask() int64 { + return (1 << s.IncrementalBits) - 1 +} + +// Compose generates an auto ID based on the given shard and an incremental ID. +func (s *ShardIDFormat) Compose(shard int64, id int64) int64 { + return ((shard & ((1 << s.ShardBits) - 1)) << s.IncrementalBits) | id +} + +type allocatorRuntimeStatsCtxKeyType struct{} + +// AllocatorRuntimeStatsCtxKey is the context key of allocator runtime stats. +var AllocatorRuntimeStatsCtxKey = allocatorRuntimeStatsCtxKeyType{} + +// AllocatorRuntimeStats is the execution stats of auto id allocator. +type AllocatorRuntimeStats struct { + *txnsnapshot.SnapshotRuntimeStats + *execdetails.RuntimeStatsWithCommit + allocCount int + rebaseCount int +} + +// NewAllocatorRuntimeStats return a new AllocatorRuntimeStats. +func NewAllocatorRuntimeStats() *AllocatorRuntimeStats { + return &AllocatorRuntimeStats{ + SnapshotRuntimeStats: &txnsnapshot.SnapshotRuntimeStats{}, + } +} + +func (e *AllocatorRuntimeStats) mergeCommitDetail(detail *tikvutil.CommitDetails) { + if detail == nil { + return + } + if e.RuntimeStatsWithCommit == nil { + e.RuntimeStatsWithCommit = &execdetails.RuntimeStatsWithCommit{} + } + e.RuntimeStatsWithCommit.MergeCommitDetails(detail) +} + +// String implements the RuntimeStats interface. +func (e *AllocatorRuntimeStats) String() string { + if e.allocCount == 0 && e.rebaseCount == 0 { + return "" + } + var buf bytes.Buffer + buf.WriteString("auto_id_allocator: {") + initialSize := buf.Len() + if e.allocCount > 0 { + buf.WriteString("alloc_cnt: ") + buf.WriteString(strconv.FormatInt(int64(e.allocCount), 10)) + } + if e.rebaseCount > 0 { + if buf.Len() > initialSize { + buf.WriteString(", ") + } + buf.WriteString("rebase_cnt: ") + buf.WriteString(strconv.FormatInt(int64(e.rebaseCount), 10)) + } + if e.SnapshotRuntimeStats != nil { + stats := e.SnapshotRuntimeStats.String() + if stats != "" { + if buf.Len() > initialSize { + buf.WriteString(", ") + } + buf.WriteString(e.SnapshotRuntimeStats.String()) + } + } + if e.RuntimeStatsWithCommit != nil { + stats := e.RuntimeStatsWithCommit.String() + if stats != "" { + if buf.Len() > initialSize { + buf.WriteString(", ") + } + buf.WriteString(stats) + } + } + buf.WriteString("}") + return buf.String() +} + +// Clone implements the RuntimeStats interface. +func (e *AllocatorRuntimeStats) Clone() *AllocatorRuntimeStats { + newRs := &AllocatorRuntimeStats{ + allocCount: e.allocCount, + rebaseCount: e.rebaseCount, + } + if e.SnapshotRuntimeStats != nil { + snapshotStats := e.SnapshotRuntimeStats.Clone() + newRs.SnapshotRuntimeStats = snapshotStats + } + if e.RuntimeStatsWithCommit != nil { + newRs.RuntimeStatsWithCommit = e.RuntimeStatsWithCommit.Clone().(*execdetails.RuntimeStatsWithCommit) + } + return newRs +} + +// Merge implements the RuntimeStats interface. +func (e *AllocatorRuntimeStats) Merge(other *AllocatorRuntimeStats) { + if other == nil { + return + } + if other.SnapshotRuntimeStats != nil { + if e.SnapshotRuntimeStats == nil { + e.SnapshotRuntimeStats = other.SnapshotRuntimeStats.Clone() + } else { + e.SnapshotRuntimeStats.Merge(other.SnapshotRuntimeStats) + } + } + if other.RuntimeStatsWithCommit != nil { + if e.RuntimeStatsWithCommit == nil { + e.RuntimeStatsWithCommit = other.RuntimeStatsWithCommit.Clone().(*execdetails.RuntimeStatsWithCommit) + } else { + e.RuntimeStatsWithCommit.Merge(other.RuntimeStatsWithCommit) + } + } +} diff --git a/meta/autoid/autoid_service.go b/pkg/meta/autoid/autoid_service.go similarity index 97% rename from meta/autoid/autoid_service.go rename to pkg/meta/autoid/autoid_service.go index 429152947535e..982fae84091ac 100644 --- a/meta/autoid/autoid_service.go +++ b/pkg/meta/autoid/autoid_service.go @@ -22,10 +22,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/autoid" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tracing" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/pkg/meta/autoid/autoid_test.go b/pkg/meta/autoid/autoid_test.go new file mode 100644 index 0000000000000..2ae3c132e5ba6 --- /dev/null +++ b/pkg/meta/autoid/autoid_test.go @@ -0,0 +1,651 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package autoid_test + +import ( + "context" + "fmt" + "math" + "math/rand" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestSignedAutoid(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 3, Name: model.NewCIStr("t1")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 4, Name: model.NewCIStr("t2")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 5, Name: model.NewCIStr("t3")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + // Since the test here is applicable to any type of allocators, autoid.RowIDAllocType is chosen. + alloc := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + + globalAutoID, err := alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + _, id, err := alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(1), id) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), id) + globalAutoID, err = alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, autoid.GetStep()+1, globalAutoID) + + // rebase + err = alloc.Rebase(context.Background(), int64(1), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3), id) + err = alloc.Rebase(context.Background(), int64(3), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(4), id) + err = alloc.Rebase(context.Background(), int64(10), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(11), id) + err = alloc.Rebase(context.Background(), int64(3010), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3011), id) + + alloc = autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, autoid.GetStep()+1, id) + + alloc = autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(1), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), id) + + alloc = autoid.NewAllocator(store, 1, 3, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(3210), false) + require.NoError(t, err) + alloc = autoid.NewAllocator(store, 1, 3, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(3000), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3211), id) + err = alloc.Rebase(context.Background(), int64(6543), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(6544), id) + + // Test the MaxInt64 is the upper bound of `alloc` function but not `rebase`. + err = alloc.Rebase(context.Background(), int64(math.MaxInt64-1), true) + require.NoError(t, err) + _, _, err = alloc.Alloc(ctx, 1, 1, 1) + require.Error(t, err) + err = alloc.Rebase(context.Background(), int64(math.MaxInt64), true) + require.NoError(t, err) + + // alloc N for signed + alloc = autoid.NewAllocator(store, 1, 4, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + globalAutoID, err = alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + min, max, err := alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(1), max-min) + require.Equal(t, int64(1), min+1) + + min, max, err = alloc.Alloc(ctx, 2, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), max-min) + require.Equal(t, int64(2), min+1) + require.Equal(t, int64(3), max) + + min, max, err = alloc.Alloc(ctx, 100, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(100), max-min) + expected := int64(4) + for i := min + 1; i <= max; i++ { + require.Equal(t, expected, i) + expected++ + } + + err = alloc.Rebase(context.Background(), int64(1000), false) + require.NoError(t, err) + min, max, err = alloc.Alloc(ctx, 3, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3), max-min) + require.Equal(t, int64(1001), min+1) + require.Equal(t, int64(1002), min+2) + require.Equal(t, int64(1003), max) + + lastRemainOne := alloc.End() + err = alloc.Rebase(context.Background(), alloc.End()-2, false) + require.NoError(t, err) + min, max, err = alloc.Alloc(ctx, 5, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(5), max-min) + require.Greater(t, min+1, lastRemainOne) + + // Test for increment & offset for signed. + alloc = autoid.NewAllocator(store, 1, 5, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + + increment := int64(2) + offset := int64(100) + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + min, max, err = alloc.Alloc(ctx, 1, increment, offset) + require.NoError(t, err) + require.Equal(t, int64(99), min) + require.Equal(t, int64(100), max) + + min, max, err = alloc.Alloc(ctx, 2, increment, offset) + require.NoError(t, err) + require.Equal(t, int64(4), max-min) + require.Equal(t, autoid.CalcNeededBatchSize(100, 2, increment, offset, false), max-min) + require.Equal(t, int64(100), min) + require.Equal(t, int64(104), max) + + increment = int64(5) + min, max, err = alloc.Alloc(ctx, 3, increment, offset) + require.NoError(t, err) + require.Equal(t, int64(11), max-min) + require.Equal(t, autoid.CalcNeededBatchSize(104, 3, increment, offset, false), max-min) + require.Equal(t, int64(104), min) + require.Equal(t, int64(115), max) + firstID := autoid.SeekToFirstAutoIDSigned(104, increment, offset) + require.Equal(t, int64(105), firstID) + + increment = int64(15) + min, max, err = alloc.Alloc(ctx, 2, increment, offset) + require.NoError(t, err) + require.Equal(t, int64(30), max-min) + require.Equal(t, autoid.CalcNeededBatchSize(115, 2, increment, offset, false), max-min) + require.Equal(t, int64(115), min) + require.Equal(t, int64(145), max) + firstID = autoid.SeekToFirstAutoIDSigned(115, increment, offset) + require.Equal(t, int64(130), firstID) + + offset = int64(200) + min, max, err = alloc.Alloc(ctx, 2, increment, offset) + require.NoError(t, err) + require.Equal(t, int64(16), max-min) + // offset-1 > base will cause alloc rebase to offset-1. + require.Equal(t, autoid.CalcNeededBatchSize(offset-1, 2, increment, offset, false), max-min) + require.Equal(t, int64(199), min) + require.Equal(t, int64(215), max) + firstID = autoid.SeekToFirstAutoIDSigned(offset-1, increment, offset) + require.Equal(t, int64(200), firstID) +} + +func TestUnsignedAutoid(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDChange")) + }() + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 3, Name: model.NewCIStr("t1")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 4, Name: model.NewCIStr("t2")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 5, Name: model.NewCIStr("t3")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + alloc := autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + + globalAutoID, err := alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + _, id, err := alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(1), id) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), id) + globalAutoID, err = alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, autoid.GetStep()+1, globalAutoID) + + // rebase + err = alloc.Rebase(context.Background(), int64(1), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3), id) + err = alloc.Rebase(context.Background(), int64(3), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(4), id) + err = alloc.Rebase(context.Background(), int64(10), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(11), id) + err = alloc.Rebase(context.Background(), int64(3010), true) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3011), id) + + alloc = autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, autoid.GetStep()+1, id) + + alloc = autoid.NewAllocator(store, 1, 2, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(1), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), id) + + alloc = autoid.NewAllocator(store, 1, 3, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(3210), false) + require.NoError(t, err) + alloc = autoid.NewAllocator(store, 1, 3, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + err = alloc.Rebase(context.Background(), int64(3000), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(3211), id) + err = alloc.Rebase(context.Background(), int64(6543), false) + require.NoError(t, err) + _, id, err = alloc.Alloc(ctx, 1, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(6544), id) + + // Test the MaxUint64 is the upper bound of `alloc` func but not `rebase`. + var n uint64 = math.MaxUint64 - 1 + un := int64(n) + err = alloc.Rebase(context.Background(), un, true) + require.NoError(t, err) + _, _, err = alloc.Alloc(ctx, 1, 1, 1) + require.Error(t, err) + un = int64(n + 1) + err = alloc.Rebase(context.Background(), un, true) + require.NoError(t, err) + + // alloc N for unsigned + alloc = autoid.NewAllocator(store, 1, 4, true, autoid.RowIDAllocType) + require.NotNil(t, alloc) + globalAutoID, err = alloc.NextGlobalAutoID() + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + + min, max, err := alloc.Alloc(ctx, 2, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), max-min) + require.Equal(t, int64(1), min+1) + require.Equal(t, int64(2), max) + + err = alloc.Rebase(context.Background(), int64(500), true) + require.NoError(t, err) + min, max, err = alloc.Alloc(ctx, 2, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(2), max-min) + require.Equal(t, int64(501), min+1) + require.Equal(t, int64(502), max) + + lastRemainOne := alloc.End() + err = alloc.Rebase(context.Background(), alloc.End()-2, false) + require.NoError(t, err) + min, max, err = alloc.Alloc(ctx, 5, 1, 1) + require.NoError(t, err) + require.Equal(t, int64(5), max-min) + require.Greater(t, min+1, lastRemainOne) + + // Test increment & offset for unsigned. Using AutoRandomType to avoid valid range check for increment and offset. + alloc = autoid.NewAllocator(store, 1, 5, true, autoid.AutoRandomType) + require.NotNil(t, alloc) + require.NoError(t, err) + require.Equal(t, int64(1), globalAutoID) + + increment := int64(2) + n = math.MaxUint64 - 100 + offset := int64(n) + + min, max, err = alloc.Alloc(ctx, 2, increment, offset) + require.NoError(t, err) + require.Equal(t, uint64(math.MaxUint64-101), uint64(min)) + require.Equal(t, uint64(math.MaxUint64-98), uint64(max)) + + require.Equal(t, autoid.CalcNeededBatchSize(int64(uint64(offset)-1), 2, increment, offset, true), max-min) + firstID := autoid.SeekToFirstAutoIDUnSigned(uint64(min), uint64(increment), uint64(offset)) + require.Equal(t, uint64(math.MaxUint64-100), firstID) +} + +// TestConcurrentAlloc is used for the test that +// multiple allocators allocate ID with the same table ID concurrently. +func TestConcurrentAlloc(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + autoid.SetStep(100) + defer func() { + autoid.SetStep(5000) + }() + + dbID := int64(2) + tblID := int64(100) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + var mu sync.Mutex + var wg util.WaitGroupWrapper + m := map[int64]struct{}{} + count := 10 + errCh := make(chan error, count) + + allocIDs := func() { + ctx := context.Background() + alloc := autoid.NewAllocator(store, dbID, tblID, false, autoid.RowIDAllocType) + for j := 0; j < int(autoid.GetStep())+5; j++ { + _, id, err1 := alloc.Alloc(ctx, 1, 1, 1) + if err1 != nil { + errCh <- err1 + break + } + + mu.Lock() + if _, ok := m[id]; ok { + errCh <- fmt.Errorf("duplicate id:%v", id) + mu.Unlock() + break + } + m[id] = struct{}{} + mu.Unlock() + + // test Alloc N + N := rand.Uint64() % 100 + min, max, err1 := alloc.Alloc(ctx, N, 1, 1) + if err1 != nil { + errCh <- err1 + break + } + + errFlag := false + mu.Lock() + for i := min + 1; i <= max; i++ { + if _, ok := m[i]; ok { + errCh <- fmt.Errorf("duplicate id:%v", i) + errFlag = true + mu.Unlock() + break + } + m[i] = struct{}{} + } + if errFlag { + break + } + mu.Unlock() + } + } + for i := 0; i < count; i++ { + num := 1 + wg.Run(func() { + time.Sleep(time.Duration(num%10) * time.Microsecond) + allocIDs() + }) + } + wg.Wait() + + close(errCh) + err = <-errCh + require.NoError(t, err) +} + +// TestRollbackAlloc tests that when the allocation transaction commit failed, +// the local variable base and end doesn't change. +func TestRollbackAlloc(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + dbID := int64(1) + tblID := int64(2) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + injectConf := new(kv.InjectionConfig) + injectConf.SetCommitError(errors.New("injected")) + injectedStore := kv.NewInjectedStore(store, injectConf) + alloc := autoid.NewAllocator(injectedStore, 1, 2, false, autoid.RowIDAllocType) + _, _, err = alloc.Alloc(ctx, 1, 1, 1) + require.Error(t, err) + require.Equal(t, int64(0), alloc.Base()) + require.Equal(t, int64(0), alloc.End()) + + err = alloc.Rebase(context.Background(), 100, true) + require.Error(t, err) + require.Equal(t, int64(0), alloc.Base()) + require.Equal(t, int64(0), alloc.End()) +} + +// TestNextStep tests generate next auto id step. +func TestNextStep(t *testing.T) { + nextStep := autoid.NextStep(2000000, 1*time.Nanosecond) + require.Equal(t, int64(2000000), nextStep) + nextStep = autoid.NextStep(678910, 10*time.Second) + require.Equal(t, int64(678910), nextStep) + nextStep = autoid.NextStep(50000, 10*time.Minute) + require.Equal(t, int64(30000), nextStep) +} + +// Fix a computation logic bug in allocator computation. +func TestAllocComputationIssue(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDCustomize", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/meta/autoid/mockAutoIDCustomize")) + }() + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 2, Name: model.NewCIStr("t1")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + // Since the test here is applicable to any type of allocators, autoid.RowIDAllocType is chosen. + unsignedAlloc1 := autoid.NewAllocator(store, 1, 1, true, autoid.RowIDAllocType) + require.NotNil(t, unsignedAlloc1) + signedAlloc1 := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) + require.NotNil(t, signedAlloc1) + signedAlloc2 := autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) + require.NotNil(t, signedAlloc2) + + // the next valid two value must be 13 & 16, batch size = 6. + err = unsignedAlloc1.Rebase(context.Background(), 10, false) + require.NoError(t, err) + // the next valid two value must be 10 & 13, batch size = 6. + err = signedAlloc2.Rebase(context.Background(), 7, false) + require.NoError(t, err) + // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. + autoid.TestModifyBaseAndEndInjection(unsignedAlloc1, 9, 9) + // Simulate the rest cache is not enough for next batch, assuming 10 & 13, batch size = 4. + autoid.TestModifyBaseAndEndInjection(signedAlloc1, 4, 6) + + // Here will recompute the new allocator batch size base on new base = 10, which will get 6. + min, max, err := unsignedAlloc1.Alloc(ctx, 2, 3, 1) + require.NoError(t, err) + require.Equal(t, int64(10), min) + require.Equal(t, int64(16), max) + min, max, err = signedAlloc2.Alloc(ctx, 2, 3, 1) + require.NoError(t, err) + require.Equal(t, int64(7), min) + require.Equal(t, int64(13), max) +} + +func TestIssue40584(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + err := store.Close() + require.NoError(t, err) + }() + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + require.NoError(t, err) + err = m.CreateTableOrView(1, &model.TableInfo{ID: 1, Name: model.NewCIStr("t")}) + require.NoError(t, err) + return nil + }) + require.NoError(t, err) + + alloc := autoid.NewAllocator(store, 1, 1, false, autoid.RowIDAllocType) + require.NotNil(t, alloc) + + finishAlloc := make(chan bool) + finishBase := make(chan bool) + var done int32 = 0 + + // call allocator.Alloc and allocator.Base in parallel for 3 seconds to detect data race + go func() { + for { + alloc.Alloc(ctx, 1, 1, 1) + if atomic.LoadInt32(&done) > 0 { + break + } + } + finishAlloc <- true + }() + + go func() { + for { + alloc.Base() + if atomic.LoadInt32(&done) > 0 { + break + } + } + finishBase <- true + }() + + runTime := time.NewTimer(time.Second * 3) + <-runTime.C + atomic.AddInt32(&done, 1) + <-finishAlloc + <-finishBase +} diff --git a/pkg/meta/autoid/bench_test.go b/pkg/meta/autoid/bench_test.go new file mode 100644 index 0000000000000..3ea8bb3eb665f --- /dev/null +++ b/pkg/meta/autoid/bench_test.go @@ -0,0 +1,133 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package autoid_test + +import ( + "context" + "fmt" + "math" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" +) + +func BenchmarkAllocator_Alloc(b *testing.B) { + b.StopTimer() + store, err := mockstore.NewMockStore() + if err != nil { + return + } + defer func() { + err := store.Close() + if err != nil { + b.Fatal(err) + } + }() + dbID := int64(1) + tblID := int64(2) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: dbID, Name: model.NewCIStr("a")}) + if err != nil { + return err + } + err = m.CreateTableOrView(dbID, &model.TableInfo{ID: tblID, Name: model.NewCIStr("t")}) + if err != nil { + return err + } + return nil + }) + if err != nil { + return + } + alloc := autoid.NewAllocator(store, 1, 2, false, autoid.RowIDAllocType) + b.StartTimer() + for i := 0; i < b.N; i++ { + _, _, err := alloc.Alloc(ctx, 1, 1, 1) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkAllocator_SequenceAlloc(b *testing.B) { + b.StopTimer() + store, err := mockstore.NewMockStore() + if err != nil { + return + } + defer func() { + err := store.Close() + if err != nil { + b.Fatal(err) + } + }() + var seq *model.SequenceInfo + var sequenceBase int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + err = m.CreateDatabase(&model.DBInfo{ID: 1, Name: model.NewCIStr("a")}) + if err != nil { + return err + } + seq = &model.SequenceInfo{ + Start: 1, + Cycle: true, + Cache: false, + MinValue: -10, + MaxValue: math.MaxInt64, + Increment: 2, + CacheValue: 2000000, + } + seqTable := &model.TableInfo{ + ID: 1, + Name: model.NewCIStr("seq"), + Sequence: seq, + } + sequenceBase = seq.Start - 1 + err = m.CreateSequenceAndSetSeqValue(1, seqTable, sequenceBase) + return err + }) + if err != nil { + return + } + alloc := autoid.NewSequenceAllocator(store, 1, 1, seq) + b.StartTimer() + for i := 0; i < b.N; i++ { + _, _, _, err := alloc.AllocSeqCache() + if err != nil { + fmt.Println("err") + } + } +} + +func BenchmarkAllocator_Seek(b *testing.B) { + base := int64(21421948021) + offset := int64(-351354365326) + increment := int64(3) + b.StartTimer() + for i := 0; i < b.N; i++ { + _, err := autoid.CalcSequenceBatchSize(base, 3, increment, offset, math.MinInt64, math.MaxInt64) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/pkg/meta/autoid/errors.go b/pkg/meta/autoid/errors.go new file mode 100644 index 0000000000000..5fe241ef9eb5b --- /dev/null +++ b/pkg/meta/autoid/errors.go @@ -0,0 +1,74 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package autoid + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// Error instances. +var ( + errInvalidTableID = dbterror.ClassAutoid.NewStd(mysql.ErrInvalidTableID) + errInvalidIncrementAndOffset = dbterror.ClassAutoid.NewStd(mysql.ErrInvalidIncrementAndOffset) + errNotImplemented = dbterror.ClassAutoid.NewStd(mysql.ErrNotImplemented) + ErrAutoincReadFailed = dbterror.ClassAutoid.NewStd(mysql.ErrAutoincReadFailed) + ErrWrongAutoKey = dbterror.ClassAutoid.NewStd(mysql.ErrWrongAutoKey) + ErrInvalidAllocatorType = dbterror.ClassAutoid.NewStd(mysql.ErrUnknownAllocatorType) + ErrAutoRandReadFailed = dbterror.ClassAutoid.NewStd(mysql.ErrAutoRandReadFailed) +) + +const ( + // AutoRandomMustFirstColumnInPK is reported when auto_random is not the first column in primary key. + AutoRandomMustFirstColumnInPK = "column '%s' must be the first column in primary key" + // AutoRandomNoClusteredPKErrMsg indicates the primary key is not clustered. + AutoRandomNoClusteredPKErrMsg = "auto_random is only supported on the tables with clustered primary key" + // AutoRandomIncompatibleWithAutoIncErrMsg is reported when auto_random and auto_increment are specified on the same column. + AutoRandomIncompatibleWithAutoIncErrMsg = "auto_random is incompatible with auto_increment" + // AutoRandomIncompatibleWithDefaultValueErrMsg is reported when auto_random and default are specified on the same column. + AutoRandomIncompatibleWithDefaultValueErrMsg = "auto_random is incompatible with default" + // AutoRandomOverflowErrMsg is reported when auto_random is greater than max length of a MySQL data type. + AutoRandomOverflowErrMsg = "max allowed auto_random shard bits is %d, but got %d on column `%s`" + // AutoRandomModifyColTypeErrMsg is reported when a user is trying to modify the type of a column specified with auto_random. + AutoRandomModifyColTypeErrMsg = "modifying the auto_random column type is not supported" + // AutoRandomAlterErrMsg is reported when a user is trying to add/drop/modify the value of auto_random attribute. + AutoRandomAlterErrMsg = "adding/dropping/modifying auto_random is not supported" + // AutoRandomDecreaseBitErrMsg is reported when the auto_random shard bits is decreased. + AutoRandomDecreaseBitErrMsg = "decreasing auto_random shard bits is not supported" + // AutoRandomNonPositive is reported then a user specifies a non-positive value for auto_random. + AutoRandomNonPositive = "the value of auto_random should be positive" + // AutoRandomAvailableAllocTimesNote is reported when a table containing auto_random is created. + AutoRandomAvailableAllocTimesNote = "Available implicit allocation times: %d" + // AutoRandomExplicitInsertDisabledErrMsg is reported when auto_random column value is explicitly specified, but the session var 'allow_auto_random_explicit_insert' is false. + AutoRandomExplicitInsertDisabledErrMsg = "Explicit insertion on auto_random column is disabled. Try to set @@allow_auto_random_explicit_insert = true." + // AutoRandomOnNonBigIntColumn is reported when define auto random to non bigint column + AutoRandomOnNonBigIntColumn = "auto_random option must be defined on `bigint` column, but not on `%s` column" + // AutoRandomRebaseNotApplicable is reported when alter auto_random base on a non auto_random table. + AutoRandomRebaseNotApplicable = "alter auto_random_base of a non auto_random table" + // AutoRandomRebaseOverflow is reported when alter auto_random_base to a value that overflows the incremental bits. + AutoRandomRebaseOverflow = "alter auto_random_base to %d overflows the incremental bits, max allowed base is %d" + // AutoRandomAlterAddColumn is reported when adding an auto_random column. + AutoRandomAlterAddColumn = "unsupported add column '%s' constraint AUTO_RANDOM when altering '%s.%s'" + // AutoRandomAlterChangeFromAutoInc is reported when the column is changing from a non-auto_increment or a non-primary key. + AutoRandomAlterChangeFromAutoInc = "auto_random can only be converted from auto_increment clustered primary key" + // AutoRandomAllocatorNotFound is reported when auto_random ID allocator not found during changing from auto_inc to auto_random. + AutoRandomAllocatorNotFound = "auto_random ID allocator not found in table '%s.%s'" + // AutoRandomInvalidRangeBits is reported when the auto_random_range_bits is invalid. + AutoRandomInvalidRangeBits = "auto_random range bits must be between %d and %d, but got %d" + // AutoRandomIncrementalBitsTooSmall is reported when the auto_random available use space is too small. + AutoRandomIncrementalBitsTooSmall = "auto_random ID space is too small, please decrease the shard bits or increase the range bits" + // AutoRandomUnsupportedAlterRangeBits is reported when the auto_random range_bits is changed. + AutoRandomUnsupportedAlterRangeBits = "alter the range bits of auto_random column is not supported" +) diff --git a/pkg/meta/autoid/main_test.go b/pkg/meta/autoid/main_test.go new file mode 100644 index 0000000000000..4c65fd74fc3b1 --- /dev/null +++ b/pkg/meta/autoid/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package autoid_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/meta/autoid/memid.go b/pkg/meta/autoid/memid.go similarity index 99% rename from meta/autoid/memid.go rename to pkg/meta/autoid/memid.go index f3a3b50bf5275..5ba66689b8bf3 100644 --- a/meta/autoid/memid.go +++ b/pkg/meta/autoid/memid.go @@ -18,7 +18,7 @@ import ( "context" "math" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) // NewAllocatorFromTempTblInfo creates an in-memory allocator from a temporary table info. diff --git a/meta/autoid/memid_test.go b/pkg/meta/autoid/memid_test.go similarity index 93% rename from meta/autoid/memid_test.go rename to pkg/meta/autoid/memid_test.go index 1f6efa1fefd49..f0b05fdec8183 100644 --- a/meta/autoid/memid_test.go +++ b/pkg/meta/autoid/memid_test.go @@ -19,12 +19,12 @@ import ( "math" "testing" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/meta/autoid/seq_autoid_test.go b/pkg/meta/autoid/seq_autoid_test.go similarity index 96% rename from meta/autoid/seq_autoid_test.go rename to pkg/meta/autoid/seq_autoid_test.go index 761f3955cb65d..2d5f2f187818f 100644 --- a/meta/autoid/seq_autoid_test.go +++ b/pkg/meta/autoid/seq_autoid_test.go @@ -21,12 +21,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/meta/main_test.go b/pkg/meta/main_test.go new file mode 100644 index 0000000000000..ac3615334189f --- /dev/null +++ b/pkg/meta/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package meta + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/meta/meta.go b/pkg/meta/meta.go similarity index 99% rename from meta/meta.go rename to pkg/meta/meta.go index 5f58731f26fa0..b43a039647928 100644 --- a/meta/meta.go +++ b/pkg/meta/meta.go @@ -27,14 +27,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/structure" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/structure" + "github.com/pingcap/tidb/pkg/util/dbterror" ) var ( diff --git a/meta/meta_autoid.go b/pkg/meta/meta_autoid.go similarity index 99% rename from meta/meta_autoid.go rename to pkg/meta/meta_autoid.go index 5763aa268051a..96d5088e2e7cb 100644 --- a/meta/meta_autoid.go +++ b/pkg/meta/meta_autoid.go @@ -18,7 +18,7 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) var _ AutoIDAccessor = &autoIDAccessor{} diff --git a/meta/meta_test.go b/pkg/meta/meta_test.go similarity index 98% rename from meta/meta_test.go rename to pkg/meta/meta_test.go index 747d64073064c..abeaf7172fbd4 100644 --- a/meta/meta_test.go +++ b/pkg/meta/meta_test.go @@ -22,11 +22,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/metrics/BUILD.bazel b/pkg/metrics/BUILD.bazel new file mode 100644 index 0000000000000..5c515e7c9dab3 --- /dev/null +++ b/pkg/metrics/BUILD.bazel @@ -0,0 +1,66 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "metrics", + srcs = [ + "bindinfo.go", + "ddl.go", + "distsql.go", + "disttask.go", + "domain.go", + "executor.go", + "gc_worker.go", + "import.go", + "log_backup.go", + "meta.go", + "metrics.go", + "owner.go", + "resourcemanager.go", + "server.go", + "session.go", + "sli.go", + "stats.go", + "telemetry.go", + "topsql.go", + "ttl.go", + "wrapper.go", + ], + importpath = "github.com/pingcap/tidb/pkg/metrics", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/lightning/metric", + "//pkg/disttask/framework/proto", + "//pkg/parser/terror", + "//pkg/timer/metrics", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/promutil", + "@com_github_pingcap_errors//:errors", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_golang//prometheus/collectors", + "@com_github_prometheus_client_model//go", + "@com_github_tikv_client_go_v2//metrics", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "metrics_test", + timeout = "short", + srcs = [ + "main_test.go", + "metrics_internal_test.go", + "metrics_test.go", + ], + embed = [":metrics"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/parser/terror", + "//pkg/statistics/handle/cache", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/metrics/alertmanager/tidb.rules.yml b/pkg/metrics/alertmanager/tidb.rules.yml similarity index 100% rename from metrics/alertmanager/tidb.rules.yml rename to pkg/metrics/alertmanager/tidb.rules.yml diff --git a/metrics/bindinfo.go b/pkg/metrics/bindinfo.go similarity index 100% rename from metrics/bindinfo.go rename to pkg/metrics/bindinfo.go diff --git a/metrics/ddl.go b/pkg/metrics/ddl.go similarity index 100% rename from metrics/ddl.go rename to pkg/metrics/ddl.go diff --git a/metrics/distsql.go b/pkg/metrics/distsql.go similarity index 100% rename from metrics/distsql.go rename to pkg/metrics/distsql.go diff --git a/metrics/disttask.go b/pkg/metrics/disttask.go similarity index 98% rename from metrics/disttask.go rename to pkg/metrics/disttask.go index fcd57664a855b..abc91bc0e6527 100644 --- a/metrics/disttask.go +++ b/pkg/metrics/disttask.go @@ -19,7 +19,7 @@ import ( "strconv" "time" - "github.com/pingcap/tidb/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" "github.com/prometheus/client_golang/prometheus" ) diff --git a/metrics/domain.go b/pkg/metrics/domain.go similarity index 100% rename from metrics/domain.go rename to pkg/metrics/domain.go diff --git a/metrics/executor.go b/pkg/metrics/executor.go similarity index 100% rename from metrics/executor.go rename to pkg/metrics/executor.go diff --git a/metrics/gc_worker.go b/pkg/metrics/gc_worker.go similarity index 100% rename from metrics/gc_worker.go rename to pkg/metrics/gc_worker.go diff --git a/metrics/grafana/README.md b/pkg/metrics/grafana/README.md similarity index 100% rename from metrics/grafana/README.md rename to pkg/metrics/grafana/README.md diff --git a/metrics/grafana/generate_json.sh b/pkg/metrics/grafana/generate_json.sh similarity index 100% rename from metrics/grafana/generate_json.sh rename to pkg/metrics/grafana/generate_json.sh diff --git a/metrics/grafana/overview.json b/pkg/metrics/grafana/overview.json similarity index 100% rename from metrics/grafana/overview.json rename to pkg/metrics/grafana/overview.json diff --git a/metrics/grafana/performance_overview.json b/pkg/metrics/grafana/performance_overview.json similarity index 100% rename from metrics/grafana/performance_overview.json rename to pkg/metrics/grafana/performance_overview.json diff --git a/metrics/grafana/tidb.json b/pkg/metrics/grafana/tidb.json similarity index 100% rename from metrics/grafana/tidb.json rename to pkg/metrics/grafana/tidb.json diff --git a/metrics/grafana/tidb_resource_control.json b/pkg/metrics/grafana/tidb_resource_control.json similarity index 100% rename from metrics/grafana/tidb_resource_control.json rename to pkg/metrics/grafana/tidb_resource_control.json diff --git a/metrics/grafana/tidb_runtime.json b/pkg/metrics/grafana/tidb_runtime.json similarity index 100% rename from metrics/grafana/tidb_runtime.json rename to pkg/metrics/grafana/tidb_runtime.json diff --git a/metrics/grafana/tidb_summary.json b/pkg/metrics/grafana/tidb_summary.json similarity index 100% rename from metrics/grafana/tidb_summary.json rename to pkg/metrics/grafana/tidb_summary.json diff --git a/metrics/grafana/tidb_summary.jsonnet b/pkg/metrics/grafana/tidb_summary.jsonnet similarity index 100% rename from metrics/grafana/tidb_summary.jsonnet rename to pkg/metrics/grafana/tidb_summary.jsonnet diff --git a/pkg/metrics/import.go b/pkg/metrics/import.go new file mode 100644 index 0000000000000..4df09e361bb18 --- /dev/null +++ b/pkg/metrics/import.go @@ -0,0 +1,35 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/br/pkg/lightning/metric" + "github.com/pingcap/tidb/pkg/util/promutil" + "github.com/prometheus/client_golang/prometheus" +) + +const importMetricSubsystem = "import" + +// GetRegisteredImportMetrics returns the registered import metrics. +func GetRegisteredImportMetrics(factory promutil.Factory, constLabels prometheus.Labels) *metric.Common { + metrics := metric.NewCommon(factory, TiDB, importMetricSubsystem, constLabels) + metrics.RegisterTo(prometheus.DefaultRegisterer) + return metrics +} + +// UnregisterImportMetrics unregisters the registered import metrics. +func UnregisterImportMetrics(metrics *metric.Common) { + metrics.UnregisterFrom(prometheus.DefaultRegisterer) +} diff --git a/metrics/log_backup.go b/pkg/metrics/log_backup.go similarity index 100% rename from metrics/log_backup.go rename to pkg/metrics/log_backup.go diff --git a/pkg/metrics/main_test.go b/pkg/metrics/main_test.go new file mode 100644 index 0000000000000..17413b535a234 --- /dev/null +++ b/pkg/metrics/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/metrics/meta.go b/pkg/metrics/meta.go similarity index 100% rename from metrics/meta.go rename to pkg/metrics/meta.go diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go new file mode 100644 index 0000000000000..a90fc37697c3b --- /dev/null +++ b/pkg/metrics/metrics.go @@ -0,0 +1,326 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "sync" + + timermetrics "github.com/pingcap/tidb/pkg/timer/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + tikvmetrics "github.com/tikv/client-go/v2/metrics" + "go.uber.org/zap" +) + +var ( + // PanicCounter measures the count of panics. + PanicCounter *prometheus.CounterVec + + // MemoryUsage measures the usage gauge of memory. + MemoryUsage *prometheus.GaugeVec +) + +// metrics labels. +const ( + LabelSession = "session" + LabelDomain = "domain" + LabelDDLOwner = "ddl-owner" + LabelDDL = "ddl" + LabelDDLWorker = "ddl-worker" + LabelDistReorg = "dist-reorg" + LabelDDLSyncer = "ddl-syncer" + LabelGCWorker = "gcworker" + LabelAnalyze = "analyze" + LabelWorkerPool = "worker-pool" + + LabelBatchRecvLoop = "batch-recv-loop" + LabelBatchSendLoop = "batch-send-loop" + + opSucc = "ok" + opFailed = "err" + + TiDB = "tidb" + LabelScope = "scope" + ScopeGlobal = "global" + ScopeSession = "session" + Server = "server" + TiKVClient = "tikvclient" +) + +// RetLabel returns "ok" when err == nil and "err" when err != nil. +// This could be useful when you need to observe the operation result. +func RetLabel(err error) string { + if err == nil { + return opSucc + } + return opFailed +} + +func init() { + InitMetrics() +} + +// InitMetrics is used to initialize metrics. +func InitMetrics() { + InitBindInfoMetrics() + InitDDLMetrics() + InitDistSQLMetrics() + InitDomainMetrics() + InitExecutorMetrics() + InitGCWorkerMetrics() + InitLogBackupMetrics() + InitMetaMetrics() + InitOwnerMetrics() + InitResourceManagerMetrics() + InitServerMetrics() + InitSessionMetrics() + InitSliMetrics() + InitStatsMetrics() + InitTelemetryMetrics() + InitTopSQLMetrics() + InitTTLMetrics() + InitDistTaskMetrics() + timermetrics.InitTimerMetrics() + + PanicCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "panic_total", + Help: "Counter of panic.", + }, []string{LblType}) + + MemoryUsage = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "memory_usage", + Help: "Memory Usage", + }, []string{LblModule, LblType}) +} + +// RegisterMetrics registers the metrics which are ONLY used in TiDB server. +func RegisterMetrics() { + // use new go collector + prometheus.DefaultRegisterer.Unregister(prometheus.NewGoCollector()) + prometheus.MustRegister(collectors.NewGoCollector(collectors.WithGoCollections(collectors.GoRuntimeMetricsCollection | collectors.GoRuntimeMemStatsCollection))) + + prometheus.MustRegister(AutoAnalyzeCounter) + prometheus.MustRegister(AutoAnalyzeHistogram) + prometheus.MustRegister(AutoIDHistogram) + prometheus.MustRegister(BatchAddIdxHistogram) + prometheus.MustRegister(BindUsageCounter) + prometheus.MustRegister(BindTotalGauge) + prometheus.MustRegister(BindMemoryUsage) + prometheus.MustRegister(CampaignOwnerCounter) + prometheus.MustRegister(ConnGauge) + prometheus.MustRegister(DisconnectionCounter) + prometheus.MustRegister(PreparedStmtGauge) + prometheus.MustRegister(CriticalErrorCounter) + prometheus.MustRegister(DDLCounter) + prometheus.MustRegister(BackfillTotalCounter) + prometheus.MustRegister(BackfillProgressGauge) + prometheus.MustRegister(DDLWorkerHistogram) + prometheus.MustRegister(DDLJobTableDuration) + prometheus.MustRegister(DDLRunningJobCount) + prometheus.MustRegister(DeploySyncerHistogram) + prometheus.MustRegister(DistSQLPartialCountHistogram) + prometheus.MustRegister(DistSQLCoprCacheCounter) + prometheus.MustRegister(DistSQLCoprClosestReadCounter) + prometheus.MustRegister(DistSQLCoprRespBodySize) + prometheus.MustRegister(DistSQLQueryHistogram) + prometheus.MustRegister(DistSQLScanKeysHistogram) + prometheus.MustRegister(DistSQLScanKeysPartialHistogram) + prometheus.MustRegister(ExecuteErrorCounter) + prometheus.MustRegister(ExecutorCounter) + prometheus.MustRegister(GetTokenDurationHistogram) + prometheus.MustRegister(NumOfMultiQueryHistogram) + prometheus.MustRegister(HandShakeErrorCounter) + prometheus.MustRegister(HandleJobHistogram) + prometheus.MustRegister(SyncLoadCounter) + prometheus.MustRegister(SyncLoadTimeoutCounter) + prometheus.MustRegister(SyncLoadHistogram) + prometheus.MustRegister(ReadStatsHistogram) + prometheus.MustRegister(JobsGauge) + prometheus.MustRegister(LoadPrivilegeCounter) + prometheus.MustRegister(InfoCacheCounters) + prometheus.MustRegister(LoadSchemaCounter) + prometheus.MustRegister(LoadSchemaDuration) + prometheus.MustRegister(MetaHistogram) + prometheus.MustRegister(NewSessionHistogram) + prometheus.MustRegister(OwnerHandleSyncerHistogram) + prometheus.MustRegister(PanicCounter) + prometheus.MustRegister(PlanCacheCounter) + prometheus.MustRegister(PlanCacheMissCounter) + prometheus.MustRegister(PlanCacheInstanceMemoryUsage) + prometheus.MustRegister(PlanCacheInstancePlanNumCounter) + prometheus.MustRegister(PseudoEstimation) + prometheus.MustRegister(PacketIOCounter) + prometheus.MustRegister(QueryDurationHistogram) + prometheus.MustRegister(QueryTotalCounter) + prometheus.MustRegister(AffectedRowsCounter) + prometheus.MustRegister(SchemaLeaseErrorCounter) + prometheus.MustRegister(ServerEventCounter) + prometheus.MustRegister(SessionExecuteCompileDuration) + prometheus.MustRegister(SessionExecuteParseDuration) + prometheus.MustRegister(SessionExecuteRunDuration) + prometheus.MustRegister(SessionRestrictedSQLCounter) + prometheus.MustRegister(SessionRetry) + prometheus.MustRegister(SessionRetryErrorCounter) + prometheus.MustRegister(StatementPerTransaction) + prometheus.MustRegister(StatsInaccuracyRate) + prometheus.MustRegister(StmtNodeCounter) + prometheus.MustRegister(DbStmtNodeCounter) + prometheus.MustRegister(ExecPhaseDuration) + prometheus.MustRegister(OngoingTxnDurationHistogram) + prometheus.MustRegister(MppCoordinatorStats) + prometheus.MustRegister(MppCoordinatorLatency) + prometheus.MustRegister(TimeJumpBackCounter) + prometheus.MustRegister(TransactionDuration) + prometheus.MustRegister(StatementDeadlockDetectDuration) + prometheus.MustRegister(StatementPessimisticRetryCount) + prometheus.MustRegister(StatementLockKeysCount) + prometheus.MustRegister(ValidateReadTSFromPDCount) + prometheus.MustRegister(UpdateSelfVersionHistogram) + prometheus.MustRegister(WatchOwnerCounter) + prometheus.MustRegister(GCActionRegionResultCounter) + prometheus.MustRegister(GCConfigGauge) + prometheus.MustRegister(GCHistogram) + prometheus.MustRegister(GCJobFailureCounter) + prometheus.MustRegister(GCRegionTooManyLocksCounter) + prometheus.MustRegister(GCWorkerCounter) + prometheus.MustRegister(TotalQueryProcHistogram) + prometheus.MustRegister(TotalCopProcHistogram) + prometheus.MustRegister(TotalCopWaitHistogram) + prometheus.MustRegister(CopMVCCRatioHistogram) + prometheus.MustRegister(HandleSchemaValidate) + prometheus.MustRegister(MaxProcs) + prometheus.MustRegister(GOGC) + prometheus.MustRegister(ConnIdleDurationHistogram) + prometheus.MustRegister(ServerInfo) + prometheus.MustRegister(TokenGauge) + prometheus.MustRegister(ConfigStatus) + prometheus.MustRegister(TiFlashQueryTotalCounter) + prometheus.MustRegister(TiFlashFailedMPPStoreState) + prometheus.MustRegister(SmallTxnWriteDuration) + prometheus.MustRegister(TxnWriteThroughput) + prometheus.MustRegister(LoadSysVarCacheCounter) + prometheus.MustRegister(TopSQLIgnoredCounter) + prometheus.MustRegister(TopSQLReportDurationHistogram) + prometheus.MustRegister(TopSQLReportDataHistogram) + prometheus.MustRegister(PDAPIExecutionHistogram) + prometheus.MustRegister(PDAPIRequestCounter) + prometheus.MustRegister(CPUProfileCounter) + prometheus.MustRegister(ReadFromTableCacheCounter) + prometheus.MustRegister(LoadTableCacheDurationHistogram) + prometheus.MustRegister(NonTransactionalDMLCount) + prometheus.MustRegister(PessimisticDMLDurationByAttempt) + prometheus.MustRegister(ResourceGroupQueryTotalCounter) + prometheus.MustRegister(MemoryUsage) + prometheus.MustRegister(StatsCacheCounter) + prometheus.MustRegister(StatsCacheGauge) + prometheus.MustRegister(StatsHealthyGauge) + prometheus.MustRegister(TxnStatusEnteringCounter) + prometheus.MustRegister(TxnDurationHistogram) + prometheus.MustRegister(LastCheckpoint) + prometheus.MustRegister(AdvancerOwner) + prometheus.MustRegister(AdvancerTickDuration) + prometheus.MustRegister(GetCheckpointBatchSize) + prometheus.MustRegister(RegionCheckpointRequest) + prometheus.MustRegister(RegionCheckpointFailure) + prometheus.MustRegister(AutoIDReqDuration) + prometheus.MustRegister(RegionCheckpointSubscriptionEvent) + prometheus.MustRegister(RCCheckTSWriteConfilictCounter) + prometheus.MustRegister(FairLockingUsageCount) + + prometheus.MustRegister(TTLQueryDuration) + prometheus.MustRegister(TTLProcessedExpiredRowsCounter) + prometheus.MustRegister(TTLJobStatus) + prometheus.MustRegister(TTLTaskStatus) + prometheus.MustRegister(TTLPhaseTime) + prometheus.MustRegister(TTLInsertRowsCount) + prometheus.MustRegister(TTLWatermarkDelay) + prometheus.MustRegister(TTLEventCounter) + + prometheus.MustRegister(timermetrics.TimerEventCounter) + + prometheus.MustRegister(EMACPUUsageGauge) + prometheus.MustRegister(PoolConcurrencyCounter) + + prometheus.MustRegister(HistoricalStatsCounter) + prometheus.MustRegister(PlanReplayerTaskCounter) + prometheus.MustRegister(PlanReplayerRegisterTaskGauge) + + prometheus.MustRegister(DistTaskGauge) + prometheus.MustRegister(DistTaskStarttimeGauge) + prometheus.MustRegister(DistTaskSubTaskCntGauge) + prometheus.MustRegister(DistTaskSubTaskStartTimeGauge) + + tikvmetrics.InitMetrics(TiDB, TiKVClient) + tikvmetrics.RegisterMetrics() + tikvmetrics.TiKVPanicCounter = PanicCounter // reset tidb metrics for tikv metrics +} + +var mode struct { + sync.Mutex + isSimplified bool +} + +// ToggleSimplifiedMode is used to register/unregister the metrics that unused by grafana. +func ToggleSimplifiedMode(simplified bool) { + var unusedMetricsByGrafana = []prometheus.Collector{ + StatementDeadlockDetectDuration, + ValidateReadTSFromPDCount, + LoadTableCacheDurationHistogram, + TxnWriteThroughput, + SmallTxnWriteDuration, + InfoCacheCounters, + ReadFromTableCacheCounter, + TiFlashQueryTotalCounter, + TiFlashFailedMPPStoreState, + CampaignOwnerCounter, + NonTransactionalDMLCount, + MemoryUsage, + TokenGauge, + tikvmetrics.TiKVRawkvSizeHistogram, + tikvmetrics.TiKVRawkvCmdHistogram, + tikvmetrics.TiKVReadThroughput, + tikvmetrics.TiKVSmallReadDuration, + tikvmetrics.TiKVBatchWaitOverLoad, + tikvmetrics.TiKVBatchClientRecycle, + tikvmetrics.TiKVRequestRetryTimesHistogram, + tikvmetrics.TiKVStatusDuration, + } + mode.Lock() + defer mode.Unlock() + if mode.isSimplified == simplified { + return + } + mode.isSimplified = simplified + if simplified { + for _, m := range unusedMetricsByGrafana { + prometheus.Unregister(m) + } + } else { + for _, m := range unusedMetricsByGrafana { + err := prometheus.Register(m) + if err != nil { + logutil.BgLogger().Error("cannot register metrics", zap.Error(err)) + break + } + } + } +} diff --git a/metrics/metrics_internal_test.go b/pkg/metrics/metrics_internal_test.go similarity index 100% rename from metrics/metrics_internal_test.go rename to pkg/metrics/metrics_internal_test.go diff --git a/metrics/metrics_test.go b/pkg/metrics/metrics_test.go similarity index 89% rename from metrics/metrics_test.go rename to pkg/metrics/metrics_test.go index 41e9129d26767..107e2328f536f 100644 --- a/metrics/metrics_test.go +++ b/pkg/metrics/metrics_test.go @@ -18,9 +18,9 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - _ "github.com/pingcap/tidb/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + _ "github.com/pingcap/tidb/pkg/statistics/handle/cache" "github.com/stretchr/testify/require" ) diff --git a/metrics/owner.go b/pkg/metrics/owner.go similarity index 100% rename from metrics/owner.go rename to pkg/metrics/owner.go diff --git a/metrics/resourcemanager.go b/pkg/metrics/resourcemanager.go similarity index 100% rename from metrics/resourcemanager.go rename to pkg/metrics/resourcemanager.go diff --git a/pkg/metrics/server.go b/pkg/metrics/server.go new file mode 100644 index 0000000000000..27f1f0c9b8ee6 --- /dev/null +++ b/pkg/metrics/server.go @@ -0,0 +1,387 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + // ResettablePlanCacheCounterFortTest be used to support reset counter in test. + ResettablePlanCacheCounterFortTest = false +) + +// Metrics +var ( + PacketIOCounter *prometheus.CounterVec + QueryDurationHistogram *prometheus.HistogramVec + QueryTotalCounter *prometheus.CounterVec + AffectedRowsCounter *prometheus.CounterVec + ConnGauge prometheus.Gauge + DisconnectionCounter *prometheus.CounterVec + PreparedStmtGauge prometheus.Gauge + ExecuteErrorCounter *prometheus.CounterVec + CriticalErrorCounter prometheus.Counter + + EventStart = "start" + EventGracefulDown = "graceful_shutdown" + // Eventkill occurs when the server.Kill() function is called. + EventKill = "kill" + EventClose = "close" + + ServerEventCounter *prometheus.CounterVec + TimeJumpBackCounter prometheus.Counter + PlanCacheCounter *prometheus.CounterVec + PlanCacheMissCounter *prometheus.CounterVec + PlanCacheInstanceMemoryUsage *prometheus.GaugeVec + PlanCacheInstancePlanNumCounter *prometheus.GaugeVec + ReadFromTableCacheCounter prometheus.Counter + HandShakeErrorCounter prometheus.Counter + GetTokenDurationHistogram prometheus.Histogram + NumOfMultiQueryHistogram prometheus.Histogram + TotalQueryProcHistogram *prometheus.HistogramVec + TotalCopProcHistogram *prometheus.HistogramVec + TotalCopWaitHistogram *prometheus.HistogramVec + CopMVCCRatioHistogram *prometheus.HistogramVec + MaxProcs prometheus.Gauge + GOGC prometheus.Gauge + ConnIdleDurationHistogram *prometheus.HistogramVec + ServerInfo *prometheus.GaugeVec + TokenGauge prometheus.Gauge + ConfigStatus *prometheus.GaugeVec + TiFlashQueryTotalCounter *prometheus.CounterVec + TiFlashFailedMPPStoreState *prometheus.GaugeVec + PDAPIExecutionHistogram *prometheus.HistogramVec + PDAPIRequestCounter *prometheus.CounterVec + CPUProfileCounter prometheus.Counter + LoadTableCacheDurationHistogram prometheus.Histogram + RCCheckTSWriteConfilictCounter *prometheus.CounterVec +) + +// InitServerMetrics initializes server metrics. +func InitServerMetrics() { + PacketIOCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "packet_io_bytes", + Help: "Counters of packet IO bytes.", + }, []string{LblType}) + + QueryDurationHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "handle_query_duration_seconds", + Help: "Bucketed histogram of processing time (s) of handled queries.", + Buckets: prometheus.ExponentialBuckets(0.0005, 2, 29), // 0.5ms ~ 1.5days + }, []string{LblSQLType, LblDb, LblResourceGroup}) + + QueryTotalCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "query_total", + Help: "Counter of queries.", + }, []string{LblType, LblResult}) + + AffectedRowsCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "affected_rows", + Help: "Counters of server affected rows.", + }, []string{LblSQLType}) + + ConnGauge = NewGauge( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "connections", + Help: "Number of connections.", + }) + + DisconnectionCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "disconnection_total", + Help: "Counter of connections disconnected.", + }, []string{LblResult}) + + PreparedStmtGauge = NewGauge(prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "prepared_stmts", + Help: "number of prepared statements.", + }) + + ExecuteErrorCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "execute_error_total", + Help: "Counter of execute errors.", + }, []string{LblType, LblDb}) + + CriticalErrorCounter = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "critical_error_total", + Help: "Counter of critical errors.", + }) + + ServerEventCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "event_total", + Help: "Counter of tidb-server event.", + }, []string{LblType}) + + TimeJumpBackCounter = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "monitor", + Name: "time_jump_back_total", + Help: "Counter of system time jumps backward.", + }) + + PlanCacheCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "plan_cache_total", + Help: "Counter of query using plan cache.", + }, []string{LblType}) + + PlanCacheMissCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "plan_cache_miss_total", + Help: "Counter of plan cache miss.", + }, []string{LblType}) + + PlanCacheInstanceMemoryUsage = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "plan_cache_instance_memory_usage", + Help: "Total plan cache memory usage of all sessions in a instance", + }, []string{LblType}) + + PlanCacheInstancePlanNumCounter = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "plan_cache_instance_plan_num_total", + Help: "Counter of plan of all prepared plan cache in a instance", + }, []string{LblType}) + + ReadFromTableCacheCounter = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "read_from_tablecache_total", + Help: "Counter of query read from table cache.", + }, + ) + + HandShakeErrorCounter = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "handshake_error_total", + Help: "Counter of hand shake error.", + }, + ) + + GetTokenDurationHistogram = NewHistogram( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "get_token_duration_seconds", + Help: "Duration (us) for getting token, it should be small until concurrency limit is reached.", + Buckets: prometheus.ExponentialBuckets(1, 2, 30), // 1us ~ 528s + }) + + NumOfMultiQueryHistogram = NewHistogram( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "multi_query_num", + Help: "The number of queries contained in a multi-query statement.", + Buckets: prometheus.ExponentialBuckets(1, 2, 20), // 1 ~ 1048576 + }) + + TotalQueryProcHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "slow_query_process_duration_seconds", + Help: "Bucketed histogram of processing time (s) of of slow queries.", + Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days + }, []string{LblSQLType}) + + TotalCopProcHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "slow_query_cop_duration_seconds", + Help: "Bucketed histogram of all cop processing time (s) of of slow queries.", + Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days + }, []string{LblSQLType}) + + TotalCopWaitHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "slow_query_wait_duration_seconds", + Help: "Bucketed histogram of all cop waiting time (s) of of slow queries.", + Buckets: prometheus.ExponentialBuckets(0.001, 2, 28), // 1ms ~ 1.5days + }, []string{LblSQLType}) + + CopMVCCRatioHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "slow_query_cop_mvcc_ratio", + Help: "Bucketed histogram of all cop total keys / processed keys in slow queries.", + Buckets: prometheus.ExponentialBuckets(0.5, 2, 21), // 0.5 ~ 262144 + }, []string{LblSQLType}) + + MaxProcs = NewGauge( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "maxprocs", + Help: "The value of GOMAXPROCS.", + }) + + GOGC = NewGauge( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "gogc", + Help: "The value of GOGC", + }) + + ConnIdleDurationHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "conn_idle_duration_seconds", + Help: "Bucketed histogram of connection idle time (s).", + Buckets: prometheus.ExponentialBuckets(0.0005, 2, 29), // 0.5ms ~ 1.5days + }, []string{LblInTxn}) + + ServerInfo = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "info", + Help: "Indicate the tidb server info, and the value is the start timestamp (s).", + }, []string{LblVersion, LblHash}) + + TokenGauge = NewGauge( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "tokens", + Help: "The number of concurrent executing session", + }, + ) + + ConfigStatus = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "config", + Name: "status", + Help: "Status of the TiDB server configurations.", + }, []string{LblType}) + + TiFlashQueryTotalCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "tiflash_query_total", + Help: "Counter of TiFlash queries.", + }, []string{LblType, LblResult}) + + TiFlashFailedMPPStoreState = NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "tiflash_failed_store", + Help: "Statues of failed tiflash mpp store,-1 means detector heartbeat,0 means reachable,1 means abnormal.", + }, []string{LblAddress}) + + PDAPIExecutionHistogram = NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "pd_api_execution_duration_seconds", + Help: "Bucketed histogram of all pd api execution time (s)", + Buckets: prometheus.ExponentialBuckets(0.001, 2, 20), // 1ms ~ 524s + }, []string{LblType}) + + PDAPIRequestCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "pd_api_request_total", + Help: "Counter of the pd http api requests", + }, []string{LblType, LblResult}) + + CPUProfileCounter = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "cpu_profile_total", + Help: "Counter of cpu profiling", + }) + + LoadTableCacheDurationHistogram = NewHistogram( + prometheus.HistogramOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "load_table_cache_seconds", + Help: "Duration (us) for loading table cache.", + Buckets: prometheus.ExponentialBuckets(1, 2, 30), // 1us ~ 528s + }) + + RCCheckTSWriteConfilictCounter = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "server", + Name: "rc_check_ts_conflict_total", + Help: "Counter of WriteConflict caused by RCCheckTS.", + }, []string{LblType}) +} + +// ExecuteErrorToLabel converts an execute error to label. +func ExecuteErrorToLabel(err error) string { + err = errors.Cause(err) + switch x := err.(type) { + case *terror.Error: + return string(x.RFCCode()) + default: + return "unknown" + } +} diff --git a/metrics/session.go b/pkg/metrics/session.go similarity index 100% rename from metrics/session.go rename to pkg/metrics/session.go diff --git a/metrics/sli.go b/pkg/metrics/sli.go similarity index 100% rename from metrics/sli.go rename to pkg/metrics/sli.go diff --git a/metrics/stats.go b/pkg/metrics/stats.go similarity index 100% rename from metrics/stats.go rename to pkg/metrics/stats.go diff --git a/pkg/metrics/telemetry.go b/pkg/metrics/telemetry.go new file mode 100644 index 0000000000000..0cb6439053eec --- /dev/null +++ b/pkg/metrics/telemetry.go @@ -0,0 +1,586 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" +) + +// Metrics +var ( + TelemetrySQLCTECnt *prometheus.CounterVec + TelemetryMultiSchemaChangeCnt prometheus.Counter + TelemetryTablePartitionCnt prometheus.Counter + TelemetryTablePartitionListCnt prometheus.Counter + TelemetryTablePartitionRangeCnt prometheus.Counter + TelemetryTablePartitionHashCnt prometheus.Counter + TelemetryTablePartitionRangeColumnsCnt prometheus.Counter + TelemetryTablePartitionRangeColumnsGt1Cnt prometheus.Counter + TelemetryTablePartitionRangeColumnsGt2Cnt prometheus.Counter + TelemetryTablePartitionRangeColumnsGt3Cnt prometheus.Counter + TelemetryTablePartitionListColumnsCnt prometheus.Counter + TelemetryTablePartitionMaxPartitionsCnt prometheus.Counter + TelemetryAccountLockCnt *prometheus.CounterVec + TelemetryTablePartitionCreateIntervalPartitionsCnt prometheus.Counter + TelemetryTablePartitionAddIntervalPartitionsCnt prometheus.Counter + TelemetryTablePartitionDropIntervalPartitionsCnt prometheus.Counter + TelemetryExchangePartitionCnt prometheus.Counter + TelemetryAddIndexIngestCnt prometheus.Counter + TelemetryFlashbackClusterCnt prometheus.Counter + TelemetryIndexMergeUsage prometheus.Counter + TelemetryCompactPartitionCnt prometheus.Counter + TelemetryReorganizePartitionCnt prometheus.Counter + TelemetryDistReorgCnt prometheus.Counter + TelemetryStoreBatchedQueryCnt prometheus.Counter + TelemetryBatchedQueryTaskCnt prometheus.Counter + TelemetryStoreBatchedCnt prometheus.Counter + TelemetryStoreBatchedFallbackCnt prometheus.Counter +) + +// InitTelemetryMetrics initializes telemetry metrics. +func InitTelemetryMetrics() { + TelemetrySQLCTECnt = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "non_recursive_cte_usage", + Help: "Counter of usage of CTE", + }, []string{LblCTEType}) + + TelemetryMultiSchemaChangeCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "multi_schema_change_usage", + Help: "Counter of usage of multi-schema change", + }) + + TelemetryTablePartitionCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_usage", + Help: "Counter of CREATE TABLE which includes of table partitioning", + }) + + TelemetryTablePartitionListCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_list_usage", + Help: "Counter of CREATE TABLE which includes LIST partitioning", + }) + + TelemetryTablePartitionRangeCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_range_usage", + Help: "Counter of CREATE TABLE which includes RANGE partitioning", + }) + + TelemetryTablePartitionHashCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_hash_usage", + Help: "Counter of CREATE TABLE which includes HASH partitioning", + }) + + TelemetryTablePartitionRangeColumnsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_range_columns_usage", + Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning", + }) + + TelemetryTablePartitionRangeColumnsGt1Cnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_range_multi_columns_usage", + Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than one partitioning column", + }) + + TelemetryTablePartitionRangeColumnsGt2Cnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_range_multi_columns_usage", + Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than two partitioning columns", + }) + + TelemetryTablePartitionRangeColumnsGt3Cnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_range_multi_columns_usage", + Help: "Counter of CREATE TABLE which includes RANGE COLUMNS partitioning with more than three partitioning columns", + }) + + TelemetryTablePartitionListColumnsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_list_columns_usage", + Help: "Counter of CREATE TABLE which includes LIST COLUMNS partitioning", + }) + + TelemetryTablePartitionMaxPartitionsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_max_partition_usage", + Help: "Counter of partitions created by CREATE TABLE statements", + }) + + TelemetryAccountLockCnt = NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "account_lock_usage", + Help: "Counter of locked/unlocked users", + }, []string{LblAccountLock}) + + TelemetryTablePartitionCreateIntervalPartitionsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_create_interval_partition_usage", + Help: "Counter of partitions created by CREATE TABLE INTERVAL statements", + }) + + TelemetryTablePartitionAddIntervalPartitionsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_add_interval_partition_usage", + Help: "Counter of partitions added by ALTER TABLE LAST PARTITION statements", + }) + + TelemetryTablePartitionDropIntervalPartitionsCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "table_partition_drop_interval_partition_usage", + Help: "Counter of partitions added by ALTER TABLE FIRST PARTITION statements", + }) + + TelemetryExchangePartitionCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "exchange_partition_usage", + Help: "Counter of usage of exchange partition statements", + }) + + TelemetryAddIndexIngestCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "add_index_ingest_usage", + Help: "Counter of usage of add index acceleration solution", + }) + + TelemetryFlashbackClusterCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "flashback_cluster_usage", + Help: "Counter of usage of flashback cluster", + }) + + TelemetryIndexMergeUsage = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "index_merge_usage", + Help: "Counter of usage of index merge", + }) + + TelemetryCompactPartitionCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "compact_partition_usage", + Help: "Counter of compact table partition", + }) + + TelemetryReorganizePartitionCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "reorganize_partition_usage", + Help: "Counter of alter table reorganize partition", + }) + + TelemetryDistReorgCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "distributed_reorg_count", + Help: "Counter of usage of distributed reorg DDL tasks count", + }) + + TelemetryStoreBatchedQueryCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "store_batched_query", + Help: "Counter of queries which use store batched coprocessor tasks", + }) + + TelemetryBatchedQueryTaskCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "batched_query_task", + Help: "Counter of coprocessor tasks in batched queries", + }) + + TelemetryStoreBatchedCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "store_batched", + Help: "Counter of store batched coprocessor tasks", + }) + + TelemetryStoreBatchedFallbackCnt = NewCounter( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "telemetry", + Name: "store_batched_fallback", + Help: "Counter of store batched fallback coprocessor tasks", + }) +} + +// readCounter reads the value of a prometheus.Counter. +// Returns -1 when failing to read the value. +func readCounter(m prometheus.Counter) int64 { + // Actually, it's not recommended to read the value of prometheus metric types directly: + // https://github.com/prometheus/client_golang/issues/486#issuecomment-433345239 + pb := &dto.Metric{} + // It's impossible to return an error though. + if err := m.Write(pb); err != nil { + return -1 + } + return int64(pb.GetCounter().GetValue()) +} + +// CTEUsageCounter records the usages of CTE. +type CTEUsageCounter struct { + NonRecursiveCTEUsed int64 `json:"nonRecursiveCTEUsed"` + RecursiveUsed int64 `json:"recursiveUsed"` + NonCTEUsed int64 `json:"nonCTEUsed"` +} + +// Sub returns the difference of two counters. +func (c CTEUsageCounter) Sub(rhs CTEUsageCounter) CTEUsageCounter { + return CTEUsageCounter{ + NonRecursiveCTEUsed: c.NonRecursiveCTEUsed - rhs.NonRecursiveCTEUsed, + RecursiveUsed: c.RecursiveUsed - rhs.RecursiveUsed, + NonCTEUsed: c.NonCTEUsed - rhs.NonCTEUsed, + } +} + +// GetCTECounter gets the TxnCommitCounter. +func GetCTECounter() CTEUsageCounter { + return CTEUsageCounter{ + NonRecursiveCTEUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "nonRecurCTE"})), + RecursiveUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "recurCTE"})), + NonCTEUsed: readCounter(TelemetrySQLCTECnt.With(prometheus.Labels{LblCTEType: "notCTE"})), + } +} + +// AccountLockCounter records the number of lock users/roles +type AccountLockCounter struct { + LockUser int64 `json:"lockUser"` + UnlockUser int64 `json:"unlockUser"` + CreateOrAlterUser int64 `json:"createOrAlterUser"` +} + +// Sub returns the difference of two counters. +func (c AccountLockCounter) Sub(rhs AccountLockCounter) AccountLockCounter { + return AccountLockCounter{ + LockUser: c.LockUser - rhs.LockUser, + UnlockUser: c.UnlockUser - rhs.UnlockUser, + CreateOrAlterUser: c.CreateOrAlterUser - rhs.CreateOrAlterUser, + } +} + +// GetAccountLockCounter gets the AccountLockCounter +func GetAccountLockCounter() AccountLockCounter { + return AccountLockCounter{ + LockUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "lockUser"})), + UnlockUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "unlockUser"})), + CreateOrAlterUser: readCounter(TelemetryAccountLockCnt.With(prometheus.Labels{LblAccountLock: "createOrAlterUser"})), + } +} + +// MultiSchemaChangeUsageCounter records the usages of multi-schema change. +type MultiSchemaChangeUsageCounter struct { + MultiSchemaChangeUsed int64 `json:"multi_schema_change_used"` +} + +// Sub returns the difference of two counters. +func (c MultiSchemaChangeUsageCounter) Sub(rhs MultiSchemaChangeUsageCounter) MultiSchemaChangeUsageCounter { + return MultiSchemaChangeUsageCounter{ + MultiSchemaChangeUsed: c.MultiSchemaChangeUsed - rhs.MultiSchemaChangeUsed, + } +} + +// GetMultiSchemaCounter gets the TxnCommitCounter. +func GetMultiSchemaCounter() MultiSchemaChangeUsageCounter { + return MultiSchemaChangeUsageCounter{ + MultiSchemaChangeUsed: readCounter(TelemetryMultiSchemaChangeCnt), + } +} + +// TablePartitionUsageCounter records the usages of table partition. +type TablePartitionUsageCounter struct { + TablePartitionCnt int64 `json:"table_partition_cnt"` + TablePartitionListCnt int64 `json:"table_partition_list_cnt"` + TablePartitionRangeCnt int64 `json:"table_partition_range_cnt"` + TablePartitionHashCnt int64 `json:"table_partition_hash_cnt"` + TablePartitionRangeColumnsCnt int64 `json:"table_partition_range_columns_cnt"` + TablePartitionRangeColumnsGt1Cnt int64 `json:"table_partition_range_columns_gt_1_cnt"` + TablePartitionRangeColumnsGt2Cnt int64 `json:"table_partition_range_columns_gt_2_cnt"` + TablePartitionRangeColumnsGt3Cnt int64 `json:"table_partition_range_columns_gt_3_cnt"` + TablePartitionListColumnsCnt int64 `json:"table_partition_list_columns_cnt"` + TablePartitionMaxPartitionsCnt int64 `json:"table_partition_max_partitions_cnt"` + TablePartitionCreateIntervalPartitionsCnt int64 `json:"table_partition_create_interval_partitions_cnt"` + TablePartitionAddIntervalPartitionsCnt int64 `json:"table_partition_add_interval_partitions_cnt"` + TablePartitionDropIntervalPartitionsCnt int64 `json:"table_partition_drop_interval_partitions_cnt"` + TablePartitionComactCnt int64 `json:"table_TablePartitionComactCnt"` + TablePartitionReorganizePartitionCnt int64 `json:"table_reorganize_partition_cnt"` +} + +// ExchangePartitionUsageCounter records the usages of exchange partition. +type ExchangePartitionUsageCounter struct { + ExchangePartitionCnt int64 `json:"exchange_partition_cnt"` +} + +// Sub returns the difference of two counters. +func (c ExchangePartitionUsageCounter) Sub(rhs ExchangePartitionUsageCounter) ExchangePartitionUsageCounter { + return ExchangePartitionUsageCounter{ + ExchangePartitionCnt: c.ExchangePartitionCnt - rhs.ExchangePartitionCnt, + } +} + +// GetExchangePartitionCounter gets the TxnCommitCounter. +func GetExchangePartitionCounter() ExchangePartitionUsageCounter { + return ExchangePartitionUsageCounter{ + ExchangePartitionCnt: readCounter(TelemetryExchangePartitionCnt), + } +} + +// Cal returns the difference of two counters. +func (c TablePartitionUsageCounter) Cal(rhs TablePartitionUsageCounter) TablePartitionUsageCounter { + return TablePartitionUsageCounter{ + TablePartitionCnt: c.TablePartitionCnt - rhs.TablePartitionCnt, + TablePartitionListCnt: c.TablePartitionListCnt - rhs.TablePartitionListCnt, + TablePartitionRangeCnt: c.TablePartitionRangeCnt - rhs.TablePartitionRangeCnt, + TablePartitionHashCnt: c.TablePartitionHashCnt - rhs.TablePartitionHashCnt, + TablePartitionRangeColumnsCnt: c.TablePartitionRangeColumnsCnt - rhs.TablePartitionRangeColumnsCnt, + TablePartitionRangeColumnsGt1Cnt: c.TablePartitionRangeColumnsGt1Cnt - rhs.TablePartitionRangeColumnsGt1Cnt, + TablePartitionRangeColumnsGt2Cnt: c.TablePartitionRangeColumnsGt2Cnt - rhs.TablePartitionRangeColumnsGt2Cnt, + TablePartitionRangeColumnsGt3Cnt: c.TablePartitionRangeColumnsGt3Cnt - rhs.TablePartitionRangeColumnsGt3Cnt, + TablePartitionListColumnsCnt: c.TablePartitionListColumnsCnt - rhs.TablePartitionListColumnsCnt, + TablePartitionMaxPartitionsCnt: mathutil.Max(c.TablePartitionMaxPartitionsCnt-rhs.TablePartitionMaxPartitionsCnt, rhs.TablePartitionMaxPartitionsCnt), + TablePartitionCreateIntervalPartitionsCnt: c.TablePartitionCreateIntervalPartitionsCnt - rhs.TablePartitionCreateIntervalPartitionsCnt, + TablePartitionAddIntervalPartitionsCnt: c.TablePartitionAddIntervalPartitionsCnt - rhs.TablePartitionAddIntervalPartitionsCnt, + TablePartitionDropIntervalPartitionsCnt: c.TablePartitionDropIntervalPartitionsCnt - rhs.TablePartitionDropIntervalPartitionsCnt, + TablePartitionComactCnt: c.TablePartitionComactCnt - rhs.TablePartitionComactCnt, + TablePartitionReorganizePartitionCnt: c.TablePartitionReorganizePartitionCnt - rhs.TablePartitionReorganizePartitionCnt, + } +} + +// ResetTablePartitionCounter gets the TxnCommitCounter. +func ResetTablePartitionCounter(pre TablePartitionUsageCounter) TablePartitionUsageCounter { + return TablePartitionUsageCounter{ + TablePartitionCnt: readCounter(TelemetryTablePartitionCnt), + TablePartitionListCnt: readCounter(TelemetryTablePartitionListCnt), + TablePartitionRangeCnt: readCounter(TelemetryTablePartitionRangeCnt), + TablePartitionHashCnt: readCounter(TelemetryTablePartitionHashCnt), + TablePartitionRangeColumnsCnt: readCounter(TelemetryTablePartitionRangeColumnsCnt), + TablePartitionRangeColumnsGt1Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt1Cnt), + TablePartitionRangeColumnsGt2Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt2Cnt), + TablePartitionRangeColumnsGt3Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt3Cnt), + TablePartitionListColumnsCnt: readCounter(TelemetryTablePartitionListColumnsCnt), + TablePartitionMaxPartitionsCnt: mathutil.Max(readCounter(TelemetryTablePartitionMaxPartitionsCnt)-pre.TablePartitionMaxPartitionsCnt, pre.TablePartitionMaxPartitionsCnt), + TablePartitionReorganizePartitionCnt: readCounter(TelemetryReorganizePartitionCnt), + } +} + +// GetTablePartitionCounter gets the TxnCommitCounter. +func GetTablePartitionCounter() TablePartitionUsageCounter { + return TablePartitionUsageCounter{ + TablePartitionCnt: readCounter(TelemetryTablePartitionCnt), + TablePartitionListCnt: readCounter(TelemetryTablePartitionListCnt), + TablePartitionRangeCnt: readCounter(TelemetryTablePartitionRangeCnt), + TablePartitionHashCnt: readCounter(TelemetryTablePartitionHashCnt), + TablePartitionRangeColumnsCnt: readCounter(TelemetryTablePartitionRangeColumnsCnt), + TablePartitionRangeColumnsGt1Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt1Cnt), + TablePartitionRangeColumnsGt2Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt2Cnt), + TablePartitionRangeColumnsGt3Cnt: readCounter(TelemetryTablePartitionRangeColumnsGt3Cnt), + TablePartitionListColumnsCnt: readCounter(TelemetryTablePartitionListColumnsCnt), + TablePartitionMaxPartitionsCnt: readCounter(TelemetryTablePartitionMaxPartitionsCnt), + TablePartitionCreateIntervalPartitionsCnt: readCounter(TelemetryTablePartitionCreateIntervalPartitionsCnt), + TablePartitionAddIntervalPartitionsCnt: readCounter(TelemetryTablePartitionAddIntervalPartitionsCnt), + TablePartitionDropIntervalPartitionsCnt: readCounter(TelemetryTablePartitionDropIntervalPartitionsCnt), + TablePartitionComactCnt: readCounter(TelemetryCompactPartitionCnt), + TablePartitionReorganizePartitionCnt: readCounter(TelemetryReorganizePartitionCnt), + } +} + +// NonTransactionalStmtCounter records the usages of non-transactional statements. +type NonTransactionalStmtCounter struct { + DeleteCount int64 `json:"delete"` + UpdateCount int64 `json:"update"` + InsertCount int64 `json:"insert"` +} + +// Sub returns the difference of two counters. +func (n NonTransactionalStmtCounter) Sub(rhs NonTransactionalStmtCounter) NonTransactionalStmtCounter { + return NonTransactionalStmtCounter{ + DeleteCount: n.DeleteCount - rhs.DeleteCount, + UpdateCount: n.UpdateCount - rhs.UpdateCount, + InsertCount: n.InsertCount - rhs.InsertCount, + } +} + +// GetNonTransactionalStmtCounter gets the NonTransactionalStmtCounter. +func GetNonTransactionalStmtCounter() NonTransactionalStmtCounter { + return NonTransactionalStmtCounter{ + DeleteCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "delete"})), + UpdateCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "update"})), + InsertCount: readCounter(NonTransactionalDMLCount.With(prometheus.Labels{LblType: "insert"})), + } +} + +// GetSavepointStmtCounter gets the savepoint statement executed counter. +func GetSavepointStmtCounter() int64 { + return readCounter(StmtNodeCounter.With(prometheus.Labels{LblType: "Savepoint", LblDb: ""})) +} + +// GetLazyPessimisticUniqueCheckSetCounter returns the counter of setting tidb_constraint_check_in_place_pessimistic to false. +func GetLazyPessimisticUniqueCheckSetCounter() int64 { + return readCounter(LazyPessimisticUniqueCheckSetCount) +} + +// DDLUsageCounter records the usages of DDL related features. +type DDLUsageCounter struct { + AddIndexIngestUsed int64 `json:"add_index_ingest_used"` + MetadataLockUsed bool `json:"metadata_lock_used"` + FlashbackClusterUsed int64 `json:"flashback_cluster_used"` + DistReorgUsed int64 `json:"dist_reorg_used"` +} + +// Sub returns the difference of two counters. +func (a DDLUsageCounter) Sub(rhs DDLUsageCounter) DDLUsageCounter { + return DDLUsageCounter{ + AddIndexIngestUsed: a.AddIndexIngestUsed - rhs.AddIndexIngestUsed, + FlashbackClusterUsed: a.FlashbackClusterUsed - rhs.FlashbackClusterUsed, + DistReorgUsed: a.DistReorgUsed - rhs.DistReorgUsed, + } +} + +// GetDDLUsageCounter gets the add index acceleration solution counts. +func GetDDLUsageCounter() DDLUsageCounter { + return DDLUsageCounter{ + AddIndexIngestUsed: readCounter(TelemetryAddIndexIngestCnt), + FlashbackClusterUsed: readCounter(TelemetryFlashbackClusterCnt), + DistReorgUsed: readCounter(TelemetryDistReorgCnt), + } +} + +// IndexMergeUsageCounter records the usages of IndexMerge feature. +type IndexMergeUsageCounter struct { + IndexMergeUsed int64 `json:"index_merge_used"` +} + +// Sub returns the difference of two counters. +func (i IndexMergeUsageCounter) Sub(rhs IndexMergeUsageCounter) IndexMergeUsageCounter { + return IndexMergeUsageCounter{ + IndexMergeUsed: i.IndexMergeUsed - rhs.IndexMergeUsed, + } +} + +// GetIndexMergeCounter gets the IndexMerge usage counter. +func GetIndexMergeCounter() IndexMergeUsageCounter { + return IndexMergeUsageCounter{ + IndexMergeUsed: readCounter(TelemetryIndexMergeUsage), + } +} + +// StoreBatchCoprCounter records the usages of batch copr statements. +type StoreBatchCoprCounter struct { + // BatchSize is the global value of `tidb_store_batch_size` + BatchSize int `json:"batch_size"` + // BatchedQuery is the counter of queries that use this feature. + BatchedQuery int64 `json:"query"` + // BatchedQueryTask is the counter of total tasks in queries above. + BatchedQueryTask int64 `json:"tasks"` + // BatchedCount is the counter of successfully batched tasks. + BatchedCount int64 `json:"batched"` + // BatchedFallbackCount is the counter of fallback batched tasks by region miss. + BatchedFallbackCount int64 `json:"batched_fallback"` +} + +// Sub returns the difference of two counters. +func (n StoreBatchCoprCounter) Sub(rhs StoreBatchCoprCounter) StoreBatchCoprCounter { + return StoreBatchCoprCounter{ + BatchedQuery: n.BatchedQuery - rhs.BatchedQuery, + BatchedQueryTask: n.BatchedQueryTask - rhs.BatchedQueryTask, + BatchedCount: n.BatchedCount - rhs.BatchedCount, + BatchedFallbackCount: n.BatchedFallbackCount - rhs.BatchedFallbackCount, + } +} + +// GetStoreBatchCoprCounter gets the IndexMerge usage counter. +func GetStoreBatchCoprCounter() StoreBatchCoprCounter { + return StoreBatchCoprCounter{ + BatchedQuery: readCounter(TelemetryStoreBatchedQueryCnt), + BatchedQueryTask: readCounter(TelemetryBatchedQueryTaskCnt), + BatchedCount: readCounter(TelemetryStoreBatchedCnt), + BatchedFallbackCount: readCounter(TelemetryStoreBatchedFallbackCnt), + } +} + +// FairLockingUsageCounter records the usage of Fair Locking feature of pessimistic transaction. +type FairLockingUsageCounter struct { + TxnFairLockingUsed int64 `json:"txn_fair_locking_used"` + TxnFairLockingEffective int64 `json:"txn_fair_locking_effective"` +} + +// Sub returns the difference of two counters. +func (i FairLockingUsageCounter) Sub(rhs FairLockingUsageCounter) FairLockingUsageCounter { + return FairLockingUsageCounter{ + TxnFairLockingUsed: i.TxnFairLockingUsed - rhs.TxnFairLockingUsed, + TxnFairLockingEffective: i.TxnFairLockingEffective - rhs.TxnFairLockingEffective, + } +} + +// GetFairLockingUsageCounter returns the Fair Locking usage counter. +func GetFairLockingUsageCounter() FairLockingUsageCounter { + return FairLockingUsageCounter{ + TxnFairLockingUsed: readCounter(FairLockingUsageCount.WithLabelValues(LblFairLockingTxnUsed)), + TxnFairLockingEffective: readCounter(FairLockingUsageCount.WithLabelValues(LblFairLockingTxnEffective)), + } +} diff --git a/metrics/topsql.go b/pkg/metrics/topsql.go similarity index 100% rename from metrics/topsql.go rename to pkg/metrics/topsql.go diff --git a/metrics/ttl.go b/pkg/metrics/ttl.go similarity index 100% rename from metrics/ttl.go rename to pkg/metrics/ttl.go diff --git a/metrics/wrapper.go b/pkg/metrics/wrapper.go similarity index 100% rename from metrics/wrapper.go rename to pkg/metrics/wrapper.go diff --git a/pkg/owner/BUILD.bazel b/pkg/owner/BUILD.bazel new file mode 100644 index 0000000000000..5b8ef06c9da82 --- /dev/null +++ b/pkg/owner/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "owner", + srcs = [ + "manager.go", + "mock.go", + ], + importpath = "github.com/pingcap/tidb/pkg/owner", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/util", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/terror", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/timeutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@io_etcd_go_etcd_api_v3//mvccpb", + "@io_etcd_go_etcd_api_v3//v3rpc/rpctypes", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "owner_test", + timeout = "short", + srcs = [ + "fail_test.go", + "main_test.go", + "manager_test.go", + ], + embed = [":owner"], + flaky = True, + shard_count = 5, + deps = [ + "//pkg/ddl", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/owner/fail_test.go b/pkg/owner/fail_test.go new file mode 100644 index 0000000000000..b78b393db2280 --- /dev/null +++ b/pkg/owner/fail_test.go @@ -0,0 +1,112 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package owner + +import ( + "context" + "fmt" + "math" + "net" + "os" + "runtime" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + clientv3 "go.etcd.io/etcd/client/v3" + "google.golang.org/grpc" +) + +var ( + dialTimeout = 3 * time.Second + retryCnt = math.MaxInt32 +) + +func TestFailNewSession(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + + _ = os.Remove("new_session:0") + ln, err := net.Listen("unix", "new_session:0") + require.NoError(t, err) + + addr := ln.Addr() + endpoints := []string{fmt.Sprintf("%s://%s", addr.Network(), addr.String())} + require.NoError(t, err) + + srv := grpc.NewServer(grpc.ConnectionTimeout(time.Minute)) + + var stop util.WaitGroupWrapper + stop.Run(func() { + err = srv.Serve(ln) + assert.NoError(t, err) + }) + + defer func() { + srv.Stop() + stop.Wait() + }() + + func() { + cli, err := clientv3.New(clientv3.Config{ + Endpoints: endpoints, + DialTimeout: dialTimeout, + }) + require.NoError(t, err) + defer func() { + if cli != nil { + _ = cli.Close() + } + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/closeClient")) + }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/closeClient", `return(true)`)) + + // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. + // The config is hard coded, not way to control it outside. + // Call stack: + // https://github.com/etcd-io/etcd/blob/ae9734e/clientv3/concurrency/session.go#L38 + // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/client.go#L253 + // https://github.com/etcd-io/etcd/blob/ae9734ed278b7a1a7dfc82e800471ebbf9fce56f/clientv3/retry_interceptor.go#L63 + _, err = util.NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) + isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) + require.Truef(t, isContextDone, "err %v", err) + }() + + func() { + cli, err := clientv3.New(clientv3.Config{ + Endpoints: endpoints, + DialTimeout: dialTimeout, + }) + require.NoError(t, err) + defer func() { + if cli != nil { + _ = cli.Close() + } + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/closeGrpc")) + }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/closeGrpc", `return(true)`)) + + // TODO: It takes more than 2s here in etcd client, the CI takes 5s to run this test. + // The config is hard coded, not way to control it outside. + _, err = util.NewSession(context.Background(), "fail_new_session", cli, retryCnt, ManagerSessionTTL) + isContextDone := terror.ErrorEqual(grpc.ErrClientConnClosing, err) || terror.ErrorEqual(context.Canceled, err) + require.Truef(t, isContextDone, "err %v", err) + }() +} diff --git a/pkg/owner/main_test.go b/pkg/owner/main_test.go new file mode 100644 index 0000000000000..46df290b5fa71 --- /dev/null +++ b/pkg/owner/main_test.go @@ -0,0 +1,33 @@ +//Copyright 2021 PingCAP, Inc. +// +// 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. + +package owner + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/owner/manager.go b/pkg/owner/manager.go new file mode 100644 index 0000000000000..e88c1c9398df1 --- /dev/null +++ b/pkg/owner/manager.go @@ -0,0 +1,471 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package owner + +import ( + "bytes" + "context" + "fmt" + "os" + "strconv" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + util2 "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.etcd.io/etcd/api/v3/mvccpb" + "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" + clientv3 "go.etcd.io/etcd/client/v3" + "go.etcd.io/etcd/client/v3/concurrency" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +// Manager is used to campaign the owner and manage the owner information. +type Manager interface { + // ID returns the ID of the manager. + ID() string + // IsOwner returns whether the ownerManager is the owner. + IsOwner() bool + // RetireOwner make the manager to be a not owner. It's exported for testing. + RetireOwner() + // GetOwnerID gets the owner ID. + GetOwnerID(ctx context.Context) (string, error) + // SetOwnerOpValue updates the owner op value. + SetOwnerOpValue(ctx context.Context, op OpType) error + // CampaignOwner campaigns the owner. + CampaignOwner(...int) error + // ResignOwner lets the owner start a new election. + ResignOwner(ctx context.Context) error + // Cancel cancels this etcd ownerManager. + Cancel() + // RequireOwner requires the ownerManager is owner. + RequireOwner(ctx context.Context) error + // CampaignCancel cancels one etcd campaign + CampaignCancel() + + // SetBeOwnerHook sets a hook. The hook is called before becoming an owner. + SetBeOwnerHook(hook func()) +} + +const ( + keyOpDefaultTimeout = 5 * time.Second +) + +// OpType is the owner key value operation type. +type OpType byte + +// List operation of types. +const ( + OpNone OpType = 0 + OpGetUpgradingState OpType = 1 +) + +// String implements fmt.Stringer interface. +func (ot OpType) String() string { + switch ot { + case OpGetUpgradingState: + return "get upgrading state" + default: + return "none" + } +} + +// DDLOwnerChecker is used to check whether tidb is owner. +type DDLOwnerChecker interface { + // IsOwner returns whether the ownerManager is the owner. + IsOwner() bool +} + +// ownerManager represents the structure which is used for electing owner. +type ownerManager struct { + id string // id is the ID of the manager. + key string + ctx context.Context + prompt string + logPrefix string + logCtx context.Context + etcdCli *clientv3.Client + cancel context.CancelFunc + elec unsafe.Pointer + sessionLease *atomicutil.Int64 + wg sync.WaitGroup + beOwnerHook func() + campaignCancel context.CancelFunc +} + +// NewOwnerManager creates a new Manager. +func NewOwnerManager(ctx context.Context, etcdCli *clientv3.Client, prompt, id, key string) Manager { + logPrefix := fmt.Sprintf("[%s] %s ownerManager %s", prompt, key, id) + ctx, cancelFunc := context.WithCancel(ctx) + return &ownerManager{ + etcdCli: etcdCli, + id: id, + key: key, + ctx: ctx, + prompt: prompt, + cancel: cancelFunc, + logPrefix: logPrefix, + logCtx: logutil.WithKeyValue(context.Background(), "owner info", logPrefix), + sessionLease: atomicutil.NewInt64(0), + } +} + +// ID implements Manager.ID interface. +func (m *ownerManager) ID() string { + return m.id +} + +// IsOwner implements Manager.IsOwner interface. +func (m *ownerManager) IsOwner() bool { + return atomic.LoadPointer(&m.elec) != unsafe.Pointer(nil) +} + +// Cancel implements Manager.Cancel interface. +func (m *ownerManager) Cancel() { + m.cancel() + m.wg.Wait() +} + +// RequireOwner implements Manager.RequireOwner interface. +func (*ownerManager) RequireOwner(_ context.Context) error { + return nil +} + +func (m *ownerManager) SetBeOwnerHook(hook func()) { + m.beOwnerHook = hook +} + +// ManagerSessionTTL is the etcd session's TTL in seconds. It's exported for testing. +var ManagerSessionTTL = 60 + +// setManagerSessionTTL sets the ManagerSessionTTL value, it's used for testing. +func setManagerSessionTTL() error { + ttlStr := os.Getenv("tidb_manager_ttl") + if ttlStr == "" { + return nil + } + ttl, err := strconv.Atoi(ttlStr) + if err != nil { + return errors.Trace(err) + } + ManagerSessionTTL = ttl + return nil +} + +// CampaignOwner implements Manager.CampaignOwner interface. +func (m *ownerManager) CampaignOwner(withTTL ...int) error { + ttl := ManagerSessionTTL + if len(withTTL) == 1 { + ttl = withTTL[0] + } + logPrefix := fmt.Sprintf("[%s] %s", m.prompt, m.key) + logutil.BgLogger().Info("start campaign owner", zap.String("ownerInfo", logPrefix)) + session, err := util2.NewSession(m.ctx, logPrefix, m.etcdCli, util2.NewSessionDefaultRetryCnt, ttl) + if err != nil { + return errors.Trace(err) + } + m.sessionLease.Store(int64(session.Lease())) + m.wg.Add(1) + go m.campaignLoop(session) + return nil +} + +// ResignOwner lets the owner start a new election. +func (m *ownerManager) ResignOwner(ctx context.Context) error { + elec := (*concurrency.Election)(atomic.LoadPointer(&m.elec)) + if elec == nil { + return errors.Errorf("This node is not a ddl owner, can't be resigned") + } + + childCtx, cancel := context.WithTimeout(ctx, keyOpDefaultTimeout) + err := elec.Resign(childCtx) + cancel() + if err != nil { + return errors.Trace(err) + } + + logutil.Logger(m.logCtx).Warn("resign ddl owner success") + return nil +} + +func (m *ownerManager) toBeOwner(elec *concurrency.Election) { + if m.beOwnerHook != nil { + m.beOwnerHook() + } + atomic.StorePointer(&m.elec, unsafe.Pointer(elec)) +} + +// RetireOwner make the manager to be a not owner. +func (m *ownerManager) RetireOwner() { + atomic.StorePointer(&m.elec, nil) +} + +// CampaignCancel implements Manager.CampaignCancel interface. +func (m *ownerManager) CampaignCancel() { + m.campaignCancel() + m.wg.Wait() +} + +func (m *ownerManager) campaignLoop(etcdSession *concurrency.Session) { + var campaignContext context.Context + campaignContext, m.campaignCancel = context.WithCancel(m.ctx) + defer func() { + m.campaignCancel() + if r := recover(); r != nil { + logutil.BgLogger().Error("recover panic", zap.String("prompt", m.prompt), zap.Any("error", r), zap.Stack("buffer")) + metrics.PanicCounter.WithLabelValues(metrics.LabelDDLOwner).Inc() + } + m.wg.Done() + }() + + logPrefix := m.logPrefix + logCtx := m.logCtx + var err error + for { + if err != nil { + metrics.CampaignOwnerCounter.WithLabelValues(m.prompt, err.Error()).Inc() + } + + select { + case <-etcdSession.Done(): + logutil.Logger(logCtx).Info("etcd session is done, creates a new one") + leaseID := etcdSession.Lease() + etcdSession, err = util2.NewSession(campaignContext, logPrefix, m.etcdCli, util2.NewSessionRetryUnlimited, ManagerSessionTTL) + if err != nil { + logutil.Logger(logCtx).Info("break campaign loop, NewSession failed", zap.Error(err)) + m.revokeSession(logPrefix, leaseID) + return + } + m.sessionLease.Store(int64(etcdSession.Lease())) + case <-campaignContext.Done(): + failpoint.Inject("MockDelOwnerKey", func(v failpoint.Value) { + if v.(string) == "delOwnerKeyAndNotOwner" { + logutil.Logger(logCtx).Info("mock break campaign and don't clear related info") + return + } + }) + logutil.Logger(logCtx).Info("break campaign loop, context is done") + m.revokeSession(logPrefix, etcdSession.Lease()) + return + default: + } + // If the etcd server turns clocks forward,the following case may occur. + // The etcd server deletes this session's lease ID, but etcd session doesn't find it. + // In this time if we do the campaign operation, the etcd server will return ErrLeaseNotFound. + if terror.ErrorEqual(err, rpctypes.ErrLeaseNotFound) { + if etcdSession != nil { + err = etcdSession.Close() + logutil.Logger(logCtx).Info("etcd session encounters the error of lease not found, closes it", zap.Error(err)) + } + continue + } + + elec := concurrency.NewElection(etcdSession, m.key) + err = elec.Campaign(campaignContext, m.id) + if err != nil { + logutil.Logger(logCtx).Info("failed to campaign", zap.Error(err)) + continue + } + + ownerKey, err := GetOwnerKey(campaignContext, logCtx, m.etcdCli, m.key, m.id) + if err != nil { + continue + } + + m.toBeOwner(elec) + m.watchOwner(campaignContext, etcdSession, ownerKey) + m.RetireOwner() + + metrics.CampaignOwnerCounter.WithLabelValues(m.prompt, metrics.NoLongerOwner).Inc() + logutil.Logger(logCtx).Warn("is not the owner") + } +} + +func (m *ownerManager) revokeSession(_ string, leaseID clientv3.LeaseID) { + // Revoke the session lease. + // If revoke takes longer than the ttl, lease is expired anyway. + cancelCtx, cancel := context.WithTimeout(context.Background(), + time.Duration(ManagerSessionTTL)*time.Second) + _, err := m.etcdCli.Revoke(cancelCtx, leaseID) + cancel() + logutil.Logger(m.logCtx).Info("revoke session", zap.Error(err)) +} + +// GetOwnerID implements Manager.GetOwnerID interface. +func (m *ownerManager) GetOwnerID(ctx context.Context) (string, error) { + _, ownerID, _, _, err := getOwnerInfo(ctx, m.logCtx, m.etcdCli, m.key) + return string(ownerID), errors.Trace(err) +} + +func getOwnerInfo(ctx, logCtx context.Context, etcdCli *clientv3.Client, ownerPath string) (string, []byte, OpType, int64, error) { + var op OpType + var resp *clientv3.GetResponse + var err error + for i := 0; i < 3; i++ { + if util.IsContextDone(ctx) { + return "", nil, op, 0, errors.Trace(ctx.Err()) + } + + childCtx, cancel := context.WithTimeout(ctx, util.KeyOpDefaultTimeout) + resp, err = etcdCli.Get(childCtx, ownerPath, clientv3.WithFirstCreate()...) + cancel() + if err == nil { + break + } + logutil.BgLogger().Info("etcd-cli get owner info failed", zap.String("category", "ddl"), zap.String("key", ownerPath), zap.Int("retryCnt", i), zap.Error(err)) + time.Sleep(util.KeyOpRetryInterval) + } + if err != nil { + logutil.Logger(logCtx).Warn("etcd-cli get owner info failed", zap.Error(err)) + return "", nil, op, 0, errors.Trace(err) + } + if len(resp.Kvs) == 0 { + return "", nil, op, 0, concurrency.ErrElectionNoLeader + } + + var ownerID []byte + ownerID, op = splitOwnerValues(resp.Kvs[0].Value) + logutil.Logger(logCtx).Info("get owner", zap.ByteString("owner key", resp.Kvs[0].Key), + zap.ByteString("ownerID", ownerID), zap.Stringer("op", op)) + return string(resp.Kvs[0].Key), ownerID, op, resp.Kvs[0].ModRevision, nil +} + +// GetOwnerKey gets the owner key information. +func GetOwnerKey(ctx, logCtx context.Context, etcdCli *clientv3.Client, etcdKey, id string) (string, error) { + ownerKey, ownerID, _, _, err := getOwnerInfo(ctx, logCtx, etcdCli, etcdKey) + if err != nil { + return "", errors.Trace(err) + } + if string(ownerID) != id { + logutil.Logger(logCtx).Warn("is not the owner") + return "", errors.New("ownerInfoNotMatch") + } + + return ownerKey, nil +} + +func splitOwnerValues(val []byte) ([]byte, OpType) { + vals := bytes.Split(val, []byte("_")) + var op OpType + if len(vals) == 2 { + op = OpType(vals[1][0]) + } + return vals[0], op +} + +func joinOwnerValues(vals ...[]byte) []byte { + return bytes.Join(vals, []byte("_")) +} + +// SetOwnerOpValue implements Manager.SetOwnerOpValue interface. +func (m *ownerManager) SetOwnerOpValue(ctx context.Context, op OpType) error { + // owner don't change. + ownerKey, ownerID, currOp, modRevision, err := getOwnerInfo(ctx, m.logCtx, m.etcdCli, m.key) + if err != nil { + return errors.Trace(err) + } + if currOp == op { + logutil.Logger(m.logCtx).Info("set owner op is the same as the original, so do nothing.", zap.Stringer("op", op)) + return nil + } + if string(ownerID) != m.id { + return errors.New("ownerInfoNotMatch") + } + newOwnerVal := joinOwnerValues(ownerID, []byte{byte(op)}) + + failpoint.Inject("MockDelOwnerKey", func(v failpoint.Value) { + if valStr, ok := v.(string); ok { + if err := mockDelOwnerKey(valStr, ownerKey, m); err != nil { + failpoint.Return(err) + } + } + }) + + leaseOp := clientv3.WithLease(clientv3.LeaseID(m.sessionLease.Load())) + resp, err := m.etcdCli.Txn(ctx). + If(clientv3.Compare(clientv3.ModRevision(ownerKey), "=", modRevision)). + Then(clientv3.OpPut(ownerKey, string(newOwnerVal), leaseOp)). + Commit() + logutil.BgLogger().Info("set owner op value", zap.String("owner key", ownerKey), zap.ByteString("ownerID", ownerID), + zap.Stringer("old Op", currOp), zap.Stringer("op", op), zap.Bool("isSuc", resp.Succeeded), zap.Error(err)) + if !resp.Succeeded { + err = errors.New("put owner key failed, cmp is false") + } + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.PutValue+"_"+metrics.RetLabel(err)).Inc() + return errors.Trace(err) +} + +// GetOwnerOpValue gets the owner op value. +func GetOwnerOpValue(ctx context.Context, etcdCli *clientv3.Client, ownerPath, logPrefix string) (OpType, error) { + // It's using for testing. + if etcdCli == nil { + return *mockOwnerOpValue.Load(), nil + } + + logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) + _, _, op, _, err := getOwnerInfo(ctx, logCtx, etcdCli, ownerPath) + return op, errors.Trace(err) +} + +func (m *ownerManager) watchOwner(ctx context.Context, etcdSession *concurrency.Session, key string) { + logPrefix := fmt.Sprintf("[%s] ownerManager %s watch owner key %v", m.prompt, m.id, key) + logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) + logutil.BgLogger().Debug(logPrefix) + watchCh := m.etcdCli.Watch(ctx, key) + for { + select { + case resp, ok := <-watchCh: + if !ok { + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.WatcherClosed).Inc() + logutil.Logger(logCtx).Info("watcher is closed, no owner") + return + } + if resp.Canceled { + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.Cancelled).Inc() + logutil.Logger(logCtx).Info("watch canceled, no owner") + return + } + + for _, ev := range resp.Events { + if ev.Type == mvccpb.DELETE { + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.Deleted).Inc() + logutil.Logger(logCtx).Info("watch failed, owner is deleted") + return + } + } + case <-etcdSession.Done(): + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.SessionDone).Inc() + return + case <-ctx.Done(): + metrics.WatchOwnerCounter.WithLabelValues(m.prompt, metrics.CtxDone).Inc() + return + } + } +} + +func init() { + err := setManagerSessionTTL() + if err != nil { + logutil.BgLogger().Warn("set manager session TTL failed", zap.Error(err)) + } +} diff --git a/pkg/owner/manager_test.go b/pkg/owner/manager_test.go new file mode 100644 index 0000000000000..5bf2801578296 --- /dev/null +++ b/pkg/owner/manager_test.go @@ -0,0 +1,311 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package owner_test + +import ( + "context" + "fmt" + "runtime" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + . "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + clientv3 "go.etcd.io/etcd/client/v3" + "go.etcd.io/etcd/client/v3/concurrency" + "go.etcd.io/etcd/tests/v3/integration" +) + +const testLease = 5 * time.Millisecond + +type testInfo struct { + store kv.Storage + cluster *integration.ClusterV3 + client *clientv3.Client + ddl DDL +} + +func newTestInfo(t *testing.T) *testInfo { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + cluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 4}) + + cli := cluster.Client(0) + ic := infoschema.NewCache(2) + ic.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) + d := NewDDL( + context.Background(), + WithEtcdClient(cli), + WithStore(store), + WithLease(testLease), + WithInfoCache(ic), + ) + + return &testInfo{ + store: store, + cluster: cluster, + client: cli, + ddl: d, + } +} + +func (ti *testInfo) Close(t *testing.T) { + err := ti.ddl.Stop() + require.NoError(t, err) + err = ti.store.Close() + require.NoError(t, err) + ti.cluster.Terminate(t) +} + +func TestSingle(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + integration.BeforeTestExternal(t) + + tInfo := newTestInfo(t) + client, d := tInfo.client, tInfo.ddl + defer tInfo.Close(t) + require.NoError(t, d.OwnerManager().CampaignOwner()) + isOwner := checkOwner(d, true) + require.True(t, isOwner) + + // test for newSession failed + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + manager := owner.NewOwnerManager(ctx, client, "ddl", "ddl_id", DDLOwnerKey) + cancel() + + err := manager.CampaignOwner() + comment := fmt.Sprintf("campaigned result don't match, err %v", err) + require.True(t, terror.ErrorEqual(err, context.Canceled) || terror.ErrorEqual(err, context.DeadlineExceeded), comment) + + isOwner = checkOwner(d, true) + require.True(t, isOwner) + + // The test is used to exit campaign loop. + d.OwnerManager().Cancel() + isOwner = checkOwner(d, false) + require.False(t, isOwner) + + time.Sleep(200 * time.Millisecond) + + // err is ok to be not nil since we canceled the manager. + ownerID, _ := manager.GetOwnerID(ctx) + require.Equal(t, "", ownerID) + op, _ := owner.GetOwnerOpValue(ctx, client, DDLOwnerKey, "log prefix") + require.Equal(t, op, owner.OpNone) +} + +func TestSetAndGetOwnerOpValue(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + integration.BeforeTestExternal(t) + + tInfo := newTestInfo(t) + defer tInfo.Close(t) + + require.NoError(t, tInfo.ddl.OwnerManager().CampaignOwner()) + isOwner := checkOwner(tInfo.ddl, true) + require.True(t, isOwner) + + // test set/get owner info + manager := tInfo.ddl.OwnerManager() + ownerID, err := manager.GetOwnerID(context.Background()) + require.NoError(t, err) + require.Equal(t, tInfo.ddl.GetID(), ownerID) + op, err := owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpNone) + err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) + require.NoError(t, err) + op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpGetUpgradingState) + // update the same as the original value + err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) + require.NoError(t, err) + op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpGetUpgradingState) + // test del owner key when SetOwnerOpValue + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/owner/MockDelOwnerKey", `return("delOwnerKeyAndNotOwner")`)) + err = manager.SetOwnerOpValue(context.Background(), owner.OpNone) + require.Error(t, err, "put owner key failed, cmp is false") + op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") + require.NotNil(t, err) + require.Equal(t, concurrency.ErrElectionNoLeader.Error(), err.Error()) + require.Equal(t, op, owner.OpNone) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/owner/MockDelOwnerKey")) + + // Let ddl run for the owner again. + require.NoError(t, tInfo.ddl.OwnerManager().CampaignOwner()) + isOwner = checkOwner(tInfo.ddl, true) + require.True(t, isOwner) + // Mock the manager become not owner because the owner is deleted(like TTL is timeout). + // And then the manager campaigns the owner again, and become the owner. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/owner/MockDelOwnerKey", `return("onlyDelOwnerKey")`)) + err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) + require.Error(t, err, "put owner key failed, cmp is false") + isOwner = checkOwner(tInfo.ddl, true) + require.True(t, isOwner) + op, err = owner.GetOwnerOpValue(context.Background(), tInfo.client, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpNone) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/owner/MockDelOwnerKey")) +} + +// TestGetOwnerOpValueBeforeSet tests get owner opValue before set this value when the etcdClient is nil. +func TestGetOwnerOpValueBeforeSet(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/owner/MockNotSetOwnerOp", `return(true)`)) + + _, dom := testkit.CreateMockStoreAndDomain(t) + ddl := dom.DDL() + require.NoError(t, ddl.OwnerManager().CampaignOwner()) + isOwner := checkOwner(ddl, true) + require.True(t, isOwner) + + // test set/get owner info + manager := ddl.OwnerManager() + ownerID, err := manager.GetOwnerID(context.Background()) + require.NoError(t, err) + require.Equal(t, ddl.GetID(), ownerID) + op, err := owner.GetOwnerOpValue(context.Background(), nil, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpNone) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/owner/MockNotSetOwnerOp")) + err = manager.SetOwnerOpValue(context.Background(), owner.OpGetUpgradingState) + require.NoError(t, err) + op, err = owner.GetOwnerOpValue(context.Background(), nil, DDLOwnerKey, "log prefix") + require.NoError(t, err) + require.Equal(t, op, owner.OpGetUpgradingState) +} + +func TestCluster(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + integration.BeforeTestExternal(t) + + originalTTL := owner.ManagerSessionTTL + owner.ManagerSessionTTL = 3 + defer func() { + owner.ManagerSessionTTL = originalTTL + }() + + tInfo := newTestInfo(t) + store, cluster, d := tInfo.store, tInfo.cluster, tInfo.ddl + defer tInfo.Close(t) + require.NoError(t, d.OwnerManager().CampaignOwner()) + + isOwner := checkOwner(d, true) + require.True(t, isOwner) + + cli1 := cluster.Client(1) + ic2 := infoschema.NewCache(2) + ic2.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) + d1 := NewDDL( + context.Background(), + WithEtcdClient(cli1), + WithStore(store), + WithLease(testLease), + WithInfoCache(ic2), + ) + require.NoError(t, d1.OwnerManager().CampaignOwner()) + + isOwner = checkOwner(d1, false) + require.False(t, isOwner) + + // Delete the leader key, the d1 become the owner. + cliRW := cluster.Client(2) + err := deleteLeader(cliRW, DDLOwnerKey) + require.NoError(t, err) + + isOwner = checkOwner(d, false) + require.False(t, isOwner) + + d.OwnerManager().Cancel() + // d3 (not owner) stop + cli3 := cluster.Client(3) + ic3 := infoschema.NewCache(2) + ic3.Insert(infoschema.MockInfoSchemaWithSchemaVer(nil, 0), 0) + d3 := NewDDL( + context.Background(), + WithEtcdClient(cli3), + WithStore(store), + WithLease(testLease), + WithInfoCache(ic3), + ) + require.NoError(t, d3.OwnerManager().CampaignOwner()) + + isOwner = checkOwner(d3, false) + require.False(t, isOwner) + + d3.OwnerManager().Cancel() + // Cancel the owner context, there is no owner. + d1.OwnerManager().Cancel() + + logPrefix := fmt.Sprintf("[ddl] %s ownerManager %s", DDLOwnerKey, "useless id") + logCtx := logutil.WithKeyValue(context.Background(), "owner info", logPrefix) + _, err = owner.GetOwnerKey(context.Background(), logCtx, cliRW, DDLOwnerKey, "useless id") + require.Truef(t, terror.ErrorEqual(err, concurrency.ErrElectionNoLeader), "get owner info result don't match, err %v", err) + op, err := owner.GetOwnerOpValue(context.Background(), cliRW, DDLOwnerKey, logPrefix) + require.Truef(t, terror.ErrorEqual(err, concurrency.ErrElectionNoLeader), "get owner info result don't match, err %v", err) + require.Equal(t, op, owner.OpNone) +} + +func checkOwner(d DDL, fbVal bool) (isOwner bool) { + manager := d.OwnerManager() + // The longest to wait for 30 seconds to + // make sure that campaigning owners is completed. + for i := 0; i < 6000; i++ { + time.Sleep(5 * time.Millisecond) + isOwner = manager.IsOwner() + if isOwner == fbVal { + break + } + } + return +} + +func deleteLeader(cli *clientv3.Client, prefixKey string) error { + session, err := concurrency.NewSession(cli) + if err != nil { + return errors.Trace(err) + } + defer func() { + _ = session.Close() + }() + election := concurrency.NewElection(session, prefixKey) + resp, err := election.Leader(context.Background()) + if err != nil { + return errors.Trace(err) + } + _, err = cli.Delete(context.Background(), string(resp.Kvs[0].Key)) + return errors.Trace(err) +} diff --git a/pkg/owner/mock.go b/pkg/owner/mock.go new file mode 100644 index 0000000000000..20c61c9eccc41 --- /dev/null +++ b/pkg/owner/mock.go @@ -0,0 +1,221 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package owner + +import ( + "context" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" + "go.uber.org/zap" +) + +var _ Manager = &mockManager{} + +// mockManager represents the structure which is used for electing owner. +// It's used for local store and testing. +// So this worker will always be the owner. +type mockManager struct { + id string // id is the ID of manager. + storeID string + key string + ctx context.Context + wg sync.WaitGroup + cancel context.CancelFunc + beOwnerHook func() + campaignDone chan struct{} + resignDone chan struct{} +} + +var mockOwnerOpValue atomic.Pointer[OpType] + +// NewMockManager creates a new mock Manager. +func NewMockManager(ctx context.Context, id string, store kv.Storage, ownerKey string) Manager { + cancelCtx, cancelFunc := context.WithCancel(ctx) + storeID := "mock_store_id" + if store != nil { + storeID = store.UUID() + } + + // Make sure the mockOwnerOpValue is initialized before GetOwnerOpValue in bootstrap. + op := OpNone + mockOwnerOpValue.Store(&op) + return &mockManager{ + id: id, + storeID: storeID, + key: ownerKey, + ctx: cancelCtx, + cancel: cancelFunc, + campaignDone: make(chan struct{}), + resignDone: make(chan struct{}), + } +} + +// ID implements Manager.ID interface. +func (m *mockManager) ID() string { + return m.id +} + +// IsOwner implements Manager.IsOwner interface. +func (m *mockManager) IsOwner() bool { + logutil.BgLogger().Debug("owner manager checks owner", zap.String("category", "ddl"), + zap.String("ID", m.id), zap.String("ownerKey", m.key)) + return util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).IsOwner(m.id) +} + +func (m *mockManager) toBeOwner() { + ok := util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).SetOwner(m.id) + if ok { + logutil.BgLogger().Debug("owner manager gets owner", zap.String("category", "ddl"), + zap.String("ID", m.id), zap.String("ownerKey", m.key)) + if m.beOwnerHook != nil { + m.beOwnerHook() + } + } +} + +// RetireOwner implements Manager.RetireOwner interface. +func (m *mockManager) RetireOwner() { + util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).UnsetOwner(m.id) +} + +// Cancel implements Manager.Cancel interface. +func (m *mockManager) Cancel() { + m.cancel() + m.wg.Wait() + logutil.BgLogger().Info("owner manager is canceled", zap.String("category", "ddl"), + zap.String("ID", m.id), zap.String("ownerKey", m.key)) +} + +// GetOwnerID implements Manager.GetOwnerID interface. +func (m *mockManager) GetOwnerID(_ context.Context) (string, error) { + if m.IsOwner() { + return m.ID(), nil + } + return "", errors.New("no owner") +} + +func (*mockManager) SetOwnerOpValue(_ context.Context, op OpType) error { + failpoint.Inject("MockNotSetOwnerOp", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil) + } + }) + mockOwnerOpValue.Store(&op) + return nil +} + +// CampaignOwner implements Manager.CampaignOwner interface. +func (m *mockManager) CampaignOwner(_ ...int) error { + m.wg.Add(1) + go func() { + logutil.BgLogger().Debug("owner manager campaign owner", zap.String("category", "ddl"), + zap.String("ID", m.id), zap.String("ownerKey", m.key)) + defer m.wg.Done() + for { + select { + case <-m.campaignDone: + m.RetireOwner() + logutil.BgLogger().Debug("owner manager campaign done", zap.String("category", "ddl"), zap.String("ID", m.id)) + return + case <-m.ctx.Done(): + m.RetireOwner() + logutil.BgLogger().Debug("owner manager is cancelled", zap.String("category", "ddl"), zap.String("ID", m.id)) + return + case <-m.resignDone: + m.RetireOwner() + //nolint: errcheck + timeutil.Sleep(m.ctx, 1*time.Second) // Give a chance to the other owner managers to get owner. + default: + m.toBeOwner() + //nolint: errcheck + timeutil.Sleep(m.ctx, 1*time.Second) // Speed up domain.Close() + logutil.BgLogger().Debug("owner manager tick", zap.String("category", "ddl"), zap.String("ID", m.id), + zap.String("ownerKey", m.key), zap.String("currentOwner", util.MockGlobalStateEntry.OwnerKey(m.storeID, m.key).GetOwner())) + } + } + }() + return nil +} + +// ResignOwner lets the owner start a new election. +func (m *mockManager) ResignOwner(_ context.Context) error { + m.resignDone <- struct{}{} + return nil +} + +// RequireOwner implements Manager.RequireOwner interface. +func (*mockManager) RequireOwner(context.Context) error { + return nil +} + +func (m *mockManager) SetBeOwnerHook(hook func()) { + m.beOwnerHook = hook +} + +// CampaignCancel implements Manager.CampaignCancel interface +func (m *mockManager) CampaignCancel() { + m.campaignDone <- struct{}{} +} + +func mockDelOwnerKey(mockCal, ownerKey string, m *ownerManager) error { + checkIsOwner := func(m *ownerManager, checkTrue bool) error { + // 5s + for i := 0; i < 100; i++ { + if m.IsOwner() == checkTrue { + break + } + time.Sleep(50 * time.Millisecond) + } + if m.IsOwner() != checkTrue { + return errors.Errorf("expect manager state:%v", checkTrue) + } + return nil + } + + needCheckOwner := false + switch mockCal { + case "delOwnerKeyAndNotOwner": + m.CampaignCancel() + // Make sure the manager is not owner. And it will exit campaignLoop. + err := checkIsOwner(m, false) + if err != nil { + return err + } + case "onlyDelOwnerKey": + needCheckOwner = true + } + + err := util.DeleteKeyFromEtcd(ownerKey, m.etcdCli, 1, keyOpDefaultTimeout) + if err != nil { + return errors.Trace(err) + } + if needCheckOwner { + // Mock the manager become not owner because the owner is deleted(like TTL is timeout). + // And then the manager campaigns the owner again, and become the owner. + err = checkIsOwner(m, true) + if err != nil { + return err + } + } + return nil +} diff --git a/parser/.editorconfig b/pkg/parser/.editorconfig similarity index 100% rename from parser/.editorconfig rename to pkg/parser/.editorconfig diff --git a/parser/.gitignore b/pkg/parser/.gitignore similarity index 100% rename from parser/.gitignore rename to pkg/parser/.gitignore diff --git a/pkg/parser/BUILD.bazel b/pkg/parser/BUILD.bazel new file mode 100644 index 0000000000000..516fbc1dbf7cb --- /dev/null +++ b/pkg/parser/BUILD.bazel @@ -0,0 +1,65 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +exports_files([ + "go.mod", + "go.sum", +]) + +go_library( + name = "parser", + srcs = [ + "digester.go", + "hintparser.go", + "hintparserimpl.go", + "lexer.go", + "misc.go", + "parser.go", + "yy_parser.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/duration", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/tidb", + "//pkg/parser/types", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "parser_test", + timeout = "short", + srcs = [ + "bench_test.go", + "consistent_test.go", + "digester_test.go", + "hintparser_test.go", + "lexer_test.go", + "main_test.go", + "parser_test.go", + ], + data = glob(["**"]), + embed = [":parser"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/test_driver", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/parser/LICENSE b/pkg/parser/LICENSE similarity index 100% rename from parser/LICENSE rename to pkg/parser/LICENSE diff --git a/parser/Makefile b/pkg/parser/Makefile similarity index 100% rename from parser/Makefile rename to pkg/parser/Makefile diff --git a/parser/OWNERS b/pkg/parser/OWNERS similarity index 100% rename from parser/OWNERS rename to pkg/parser/OWNERS diff --git a/parser/README.md b/pkg/parser/README.md similarity index 100% rename from parser/README.md rename to pkg/parser/README.md diff --git a/parser/SECURITY.md b/pkg/parser/SECURITY.md similarity index 100% rename from parser/SECURITY.md rename to pkg/parser/SECURITY.md diff --git a/pkg/parser/ast/BUILD.bazel b/pkg/parser/ast/BUILD.bazel new file mode 100644 index 0000000000000..e07c360c2cc16 --- /dev/null +++ b/pkg/parser/ast/BUILD.bazel @@ -0,0 +1,63 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ast", + srcs = [ + "advisor.go", + "ast.go", + "base.go", + "ddl.go", + "dml.go", + "expressions.go", + "flag.go", + "functions.go", + "misc.go", + "procedure.go", + "stats.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/ast", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/tidb", + "//pkg/parser/types", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + ], +) + +go_test( + name = "ast_test", + timeout = "short", + srcs = [ + "base_test.go", + "ddl_test.go", + "dml_test.go", + "expressions_test.go", + "flag_test.go", + "format_test.go", + "functions_test.go", + "misc_test.go", + "procedure_test.go", + "util_test.go", + ], + embed = [":ast"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/parser/test_driver", + "@com_github_stretchr_testify//require", + ], +) diff --git a/parser/ast/advisor.go b/pkg/parser/ast/advisor.go similarity index 97% rename from parser/ast/advisor.go rename to pkg/parser/ast/advisor.go index 4664ff208bc5d..dba9c576ded74 100644 --- a/parser/ast/advisor.go +++ b/pkg/parser/ast/advisor.go @@ -15,7 +15,7 @@ package ast import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser/format" ) var _ StmtNode = &IndexAdviseStmt{} diff --git a/pkg/parser/ast/ast.go b/pkg/parser/ast/ast.go new file mode 100644 index 0000000000000..bea8239541e62 --- /dev/null +++ b/pkg/parser/ast/ast.go @@ -0,0 +1,258 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ast is the abstract syntax tree parsed from a SQL statement by parser. +// It can be analysed and transformed by optimizer. +package ast + +import ( + "io" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/types" +) + +// Node is the basic element of the AST. +// Interfaces embed Node should have 'Node' name suffix. +type Node interface { + // Restore returns the sql text from ast tree + Restore(ctx *format.RestoreCtx) error + // Accept accepts Visitor to visit itself. + // The returned node should replace original node. + // ok returns false to stop visiting. + // + // Implementation of this method should first call visitor.Enter, + // assign the returned node to its method receiver, if skipChildren returns true, + // children should be skipped. Otherwise, call its children in particular order that + // later elements depends on former elements. Finally, return visitor.Leave. + Accept(v Visitor) (node Node, ok bool) + // Text returns the utf8 encoding text of the element. + Text() string + // OriginalText returns the original text of the element. + OriginalText() string + // SetText sets original text to the Node. + SetText(enc charset.Encoding, text string) + // SetOriginTextPosition set the start offset of this node in the origin text. + SetOriginTextPosition(offset int) + // OriginTextPosition get the start offset of this node in the origin text. + OriginTextPosition() int +} + +// Flags indicates whether an expression contains certain types of expression. +const ( + FlagConstant uint64 = 0 + FlagHasParamMarker uint64 = 1 << iota + FlagHasFunc + FlagHasReference + FlagHasAggregateFunc + FlagHasSubquery + FlagHasVariable + FlagHasDefault + FlagPreEvaluated + FlagHasWindowFunc +) + +// ExprNode is a node that can be evaluated. +// Name of implementations should have 'Expr' suffix. +type ExprNode interface { + // Node is embedded in ExprNode. + Node + // SetType sets evaluation type to the expression. + SetType(tp *types.FieldType) + // GetType gets the evaluation type of the expression. + GetType() *types.FieldType + // SetFlag sets flag to the expression. + // Flag indicates whether the expression contains + // parameter marker, reference, aggregate function... + SetFlag(flag uint64) + // GetFlag returns the flag of the expression. + GetFlag() uint64 + + // Format formats the AST into a writer. + Format(w io.Writer) +} + +// OptBinary is used for parser. +type OptBinary struct { + IsBinary bool + Charset string +} + +// FuncNode represents function call expression node. +type FuncNode interface { + ExprNode + functionExpression() +} + +// StmtNode represents statement node. +// Name of implementations should have 'Stmt' suffix. +type StmtNode interface { + Node + statement() +} + +// DDLNode represents DDL statement node. +type DDLNode interface { + StmtNode + ddlStatement() +} + +// DMLNode represents DML statement node. +type DMLNode interface { + StmtNode + dmlStatement() +} + +// ResultField represents a result field which can be a column from a table, +// or an expression in select field. It is a generated property during +// binding process. ResultField is the key element to evaluate a ColumnNameExpr. +// After resolving process, every ColumnNameExpr will be resolved to a ResultField. +// During execution, every row retrieved from table will set the row value to +// ResultFields of that table, so ColumnNameExpr resolved to that ResultField can be +// easily evaluated. +type ResultField struct { + Column *model.ColumnInfo + ColumnAsName model.CIStr + // EmptyOrgName indicates whether this field has an empty org_name. A field has an empty org name, if it's an + // expression. It's not sure whether it's safe to use empty string in `.Column.Name`, so a new field is added to + // indicate whether it's empty. + EmptyOrgName bool + + Table *model.TableInfo + TableAsName model.CIStr + DBName model.CIStr +} + +// ResultSetNode interface has a ResultFields property, represents a Node that returns result set. +// Implementations include SelectStmt, SubqueryExpr, TableSource, TableName, Join and SetOprStmt. +type ResultSetNode interface { + Node + + resultSet() +} + +// SensitiveStmtNode overloads StmtNode and provides a SecureText method. +type SensitiveStmtNode interface { + StmtNode + // SecureText is different from Text that it hide password information. + SecureText() string +} + +// Visitor visits a Node. +type Visitor interface { + // Enter is called before children nodes are visited. + // The returned node must be the same type as the input node n. + // skipChildren returns true means children nodes should be skipped, + // this is useful when work is done in Enter and there is no need to visit children. + Enter(n Node) (node Node, skipChildren bool) + // Leave is called after children nodes have been visited. + // The returned node's type can be different from the input node if it is a ExprNode, + // Non-expression node must be the same type as the input node n. + // ok returns false to stop visiting. + Leave(n Node) (node Node, ok bool) +} + +// GetStmtLabel generates a label for a statement. +func GetStmtLabel(stmtNode StmtNode) string { + switch x := stmtNode.(type) { + case *AlterTableStmt: + return "AlterTable" + case *AnalyzeTableStmt: + return "AnalyzeTable" + case *BeginStmt: + return "Begin" + case *ChangeStmt: + return "Change" + case *CommitStmt: + return "Commit" + case *CompactTableStmt: + return "CompactTable" + case *CreateDatabaseStmt: + return "CreateDatabase" + case *CreateIndexStmt: + return "CreateIndex" + case *CreateTableStmt: + return "CreateTable" + case *CreateViewStmt: + return "CreateView" + case *CreateUserStmt: + return "CreateUser" + case *DeleteStmt: + return "Delete" + case *DropDatabaseStmt: + return "DropDatabase" + case *DropIndexStmt: + return "DropIndex" + case *DropTableStmt: + if x.IsView { + return "DropView" + } + return "DropTable" + case *ExplainStmt: + if _, ok := x.Stmt.(*ShowStmt); ok { + return "DescTable" + } + if x.Analyze { + return "ExplainAnalyzeSQL" + } + return "ExplainSQL" + case *InsertStmt: + if x.IsReplace { + return "Replace" + } + return "Insert" + case *ImportIntoStmt: + return "ImportInto" + case *LoadDataStmt: + return "LoadData" + case *RollbackStmt: + return "Rollback" + case *SelectStmt: + return "Select" + case *SetStmt, *SetPwdStmt: + return "Set" + case *ShowStmt: + return "Show" + case *TruncateTableStmt: + return "TruncateTable" + case *UpdateStmt: + return "Update" + case *GrantStmt: + return "Grant" + case *RevokeStmt: + return "Revoke" + case *DeallocateStmt: + return "Deallocate" + case *ExecuteStmt: + return "Execute" + case *PrepareStmt: + return "Prepare" + case *UseStmt: + return "Use" + case *CreateBindingStmt: + return "CreateBinding" + case *IndexAdviseStmt: + return "IndexAdvise" + case *DropBindingStmt: + return "DropBinding" + case *TraceStmt: + return "Trace" + case *ShutdownStmt: + return "Shutdown" + case *SavepointStmt: + return "Savepoint" + } + return "other" +} diff --git a/pkg/parser/ast/base.go b/pkg/parser/ast/base.go new file mode 100644 index 0000000000000..c9142510f2331 --- /dev/null +++ b/pkg/parser/ast/base.go @@ -0,0 +1,135 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "sync" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/types" +) + +// node is the struct implements Node interface except for Accept method. +// Node implementations should embed it in. +type node struct { + utf8Text string + enc charset.Encoding + once *sync.Once + + text string + offset int +} + +// SetOriginTextPosition implements Node interface. +func (n *node) SetOriginTextPosition(offset int) { + n.offset = offset +} + +// OriginTextPosition implements Node interface. +func (n *node) OriginTextPosition() int { + return n.offset +} + +// SetText implements Node interface. +func (n *node) SetText(enc charset.Encoding, text string) { + n.enc = enc + n.text = text + n.once = &sync.Once{} +} + +// Text implements Node interface. +func (n *node) Text() string { + if n.once == nil { + return n.text + } + n.once.Do(func() { + if n.enc == nil { + n.utf8Text = n.text + return + } + utf8Lit, _ := n.enc.Transform(nil, charset.HackSlice(n.text), charset.OpDecodeReplace) + n.utf8Text = charset.HackString(utf8Lit) + }) + return n.utf8Text +} + +// OriginalText implements Node interface. +func (n *node) OriginalText() string { + return n.text +} + +// stmtNode implements StmtNode interface. +// Statement implementations should embed it in. +type stmtNode struct { + node +} + +// statement implements StmtNode interface. +func (sn *stmtNode) statement() {} + +// ddlNode implements DDLNode interface. +// DDL implementations should embed it in. +type ddlNode struct { + stmtNode +} + +// ddlStatement implements DDLNode interface. +func (dn *ddlNode) ddlStatement() {} + +// dmlNode is the struct implements DMLNode interface. +// DML implementations should embed it in. +type dmlNode struct { + stmtNode +} + +// dmlStatement implements DMLNode interface. +func (dn *dmlNode) dmlStatement() {} + +// exprNode is the struct implements Expression interface. +// Expression implementations should embed it in. +type exprNode struct { + node + Type types.FieldType + flag uint64 +} + +// TexprNode is exported for parser driver. +type TexprNode = exprNode + +// SetType implements ExprNode interface. +func (en *exprNode) SetType(tp *types.FieldType) { + en.Type = *tp +} + +// GetType implements ExprNode interface. +func (en *exprNode) GetType() *types.FieldType { + return &en.Type +} + +// SetFlag implements ExprNode interface. +func (en *exprNode) SetFlag(flag uint64) { + en.flag = flag +} + +// GetFlag implements ExprNode interface. +func (en *exprNode) GetFlag() uint64 { + return en.flag +} + +type funcNode struct { + exprNode +} + +// functionExpression implements FunctionNode interface. +func (fn *funcNode) functionExpression() {} diff --git a/pkg/parser/ast/base_test.go b/pkg/parser/ast/base_test.go new file mode 100644 index 0000000000000..616d57e9f5e23 --- /dev/null +++ b/pkg/parser/ast/base_test.go @@ -0,0 +1,42 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package ast is the abstract syntax tree parsed from a SQL statement by parser. +// It can be analysed and transformed by optimizer. +package ast + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/stretchr/testify/require" +) + +func TestNodeSetText(t *testing.T) { + n := &node{} + tests := []struct { + text string + enc charset.Encoding + expectUTF8Text string + expectText string + }{ + {"你好", nil, "你好", "你好"}, + {"\xd2\xbb", charset.EncodingGBKImpl, "一", "\xd2\xbb"}, + {"\xc1\xd0", charset.EncodingGBKImpl, "列", "\xc1\xd0"}, + } + for _, tt := range tests { + n.SetText(tt.enc, tt.text) + require.Equal(t, tt.expectUTF8Text, n.Text()) + require.Equal(t, tt.expectText, n.OriginalText()) + } +} diff --git a/pkg/parser/ast/ddl.go b/pkg/parser/ast/ddl.go new file mode 100644 index 0000000000000..8d67dbc51e34e --- /dev/null +++ b/pkg/parser/ast/ddl.go @@ -0,0 +1,4754 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/parser/tidb" + "github.com/pingcap/tidb/pkg/parser/types" +) + +var ( + _ DDLNode = &AlterTableStmt{} + _ DDLNode = &AlterSequenceStmt{} + _ DDLNode = &AlterPlacementPolicyStmt{} + _ DDLNode = &AlterResourceGroupStmt{} + _ DDLNode = &CreateDatabaseStmt{} + _ DDLNode = &CreateIndexStmt{} + _ DDLNode = &CreateTableStmt{} + _ DDLNode = &CreateViewStmt{} + _ DDLNode = &CreateSequenceStmt{} + _ DDLNode = &CreatePlacementPolicyStmt{} + _ DDLNode = &CreateResourceGroupStmt{} + _ DDLNode = &DropDatabaseStmt{} + _ DDLNode = &FlashBackDatabaseStmt{} + _ DDLNode = &DropIndexStmt{} + _ DDLNode = &DropTableStmt{} + _ DDLNode = &DropSequenceStmt{} + _ DDLNode = &DropPlacementPolicyStmt{} + _ DDLNode = &DropResourceGroupStmt{} + _ DDLNode = &RenameTableStmt{} + _ DDLNode = &TruncateTableStmt{} + _ DDLNode = &RepairTableStmt{} + + _ Node = &AlterTableSpec{} + _ Node = &ColumnDef{} + _ Node = &ColumnOption{} + _ Node = &ColumnPosition{} + _ Node = &Constraint{} + _ Node = &IndexPartSpecification{} + _ Node = &ReferenceDef{} +) + +// CharsetOpt is used for parsing charset option from SQL. +type CharsetOpt struct { + Chs string + Col string +} + +// NullString represents a string that may be nil. +type NullString struct { + String string + Empty bool // Empty is true if String is empty backtick. +} + +// DatabaseOptionType is the type for database options. +type DatabaseOptionType int + +// Database option types. +const ( + DatabaseOptionNone DatabaseOptionType = iota + DatabaseOptionCharset + DatabaseOptionCollate + DatabaseOptionEncryption + DatabaseSetTiFlashReplica + DatabaseOptionPlacementPolicy = DatabaseOptionType(PlacementOptionPolicy) +) + +// DatabaseOption represents database option. +type DatabaseOption struct { + Tp DatabaseOptionType + Value string + UintValue uint64 + TiFlashReplica *TiFlashReplicaSpec +} + +// Restore implements Node interface. +func (n *DatabaseOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case DatabaseOptionCharset: + ctx.WriteKeyWord("CHARACTER SET") + ctx.WritePlain(" = ") + ctx.WritePlain(n.Value) + case DatabaseOptionCollate: + ctx.WriteKeyWord("COLLATE") + ctx.WritePlain(" = ") + ctx.WritePlain(n.Value) + case DatabaseOptionEncryption: + ctx.WriteKeyWord("ENCRYPTION") + ctx.WritePlain(" = ") + ctx.WriteString(n.Value) + case DatabaseOptionPlacementPolicy: + placementOpt := PlacementOption{ + Tp: PlacementOptionPolicy, + UintValue: n.UintValue, + StrValue: n.Value, + } + return placementOpt.Restore(ctx) + case DatabaseSetTiFlashReplica: + ctx.WriteKeyWord("SET TIFLASH REPLICA ") + ctx.WritePlainf("%d", n.TiFlashReplica.Count) + if len(n.TiFlashReplica.Labels) == 0 { + break + } + ctx.WriteKeyWord(" LOCATION LABELS ") + for i, v := range n.TiFlashReplica.Labels { + if i > 0 { + ctx.WritePlain(", ") + } + ctx.WriteString(v) + } + default: + return errors.Errorf("invalid DatabaseOptionType: %d", n.Tp) + } + return nil +} + +// CreateDatabaseStmt is a statement to create a database. +// See https://dev.mysql.com/doc/refman/5.7/en/create-database.html +type CreateDatabaseStmt struct { + ddlNode + + IfNotExists bool + Name model.CIStr + Options []*DatabaseOption +} + +// Restore implements Node interface. +func (n *CreateDatabaseStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE DATABASE ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.Name.O) + for i, option := range n.Options { + ctx.WritePlain(" ") + err := option.Restore(ctx) + if err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateDatabaseStmt DatabaseOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateDatabaseStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateDatabaseStmt) + return v.Leave(n) +} + +// AlterDatabaseStmt is a statement to change the structure of a database. +// See https://dev.mysql.com/doc/refman/5.7/en/alter-database.html +type AlterDatabaseStmt struct { + ddlNode + + Name model.CIStr + AlterDefaultDatabase bool + Options []*DatabaseOption +} + +// Restore implements Node interface. +func (n *AlterDatabaseStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.isAllPlacementOptions() { + return nil + } + // If all options placement options and RestoreTiDBSpecialComment flag is on, + // we should restore the whole node in special comment. For example, the restore result should be: + // /*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */ + // instead of + // ALTER DATABASE `db1` /*T![placement] PLACEMENT POLICY = `p1` */ + // because altering a database without any options is not a legal syntax in mysql + if n.isAllPlacementOptions() && ctx.Flags.HasTiDBSpecialCommentFlag() { + return restorePlacementStmtInSpecialComment(ctx, n) + } + + ctx.WriteKeyWord("ALTER DATABASE") + if !n.AlterDefaultDatabase { + ctx.WritePlain(" ") + ctx.WriteName(n.Name.O) + } + for i, option := range n.Options { + ctx.WritePlain(" ") + err := option.Restore(ctx) + if err != nil { + return errors.Annotatef(err, "An error occurred while splicing AlterDatabaseStmt DatabaseOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AlterDatabaseStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterDatabaseStmt) + return v.Leave(n) +} + +func (n *AlterDatabaseStmt) isAllPlacementOptions() bool { + for _, n := range n.Options { + switch n.Tp { + case DatabaseOptionPlacementPolicy: + default: + return false + } + } + return true +} + +// DropDatabaseStmt is a statement to drop a database and all tables in the database. +// See https://dev.mysql.com/doc/refman/5.7/en/drop-database.html +type DropDatabaseStmt struct { + ddlNode + + IfExists bool + Name model.CIStr +} + +// Restore implements Node interface. +func (n *DropDatabaseStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP DATABASE ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.Name.O) + return nil +} + +// Accept implements Node Accept interface. +func (n *DropDatabaseStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropDatabaseStmt) + return v.Leave(n) +} + +// FlashBackDatabaseStmt is a statement to restore a database and all tables in the database. +type FlashBackDatabaseStmt struct { + ddlNode + + DBName model.CIStr + NewName string +} + +// Restore implements Node interface. +func (n *FlashBackDatabaseStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("FLASHBACK DATABASE ") + ctx.WriteName(n.DBName.O) + if len(n.NewName) > 0 { + ctx.WriteKeyWord(" TO ") + ctx.WriteName(n.NewName) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *FlashBackDatabaseStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*FlashBackDatabaseStmt) + return v.Leave(n) +} + +// IndexPartSpecifications is used for parsing index column name or index expression from SQL. +type IndexPartSpecification struct { + node + + Column *ColumnName + Length int + // Order is parsed but should be ignored because MySQL v5.7 doesn't support it. + Desc bool + Expr ExprNode +} + +// Restore implements Node interface. +func (n *IndexPartSpecification) Restore(ctx *format.RestoreCtx) error { + if n.Expr != nil { + ctx.WritePlain("(") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications") + } + ctx.WritePlain(")") + if n.Desc { + ctx.WritePlain(" DESC") + } + return nil + } + if err := n.Column.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications") + } + if n.Length > 0 { + ctx.WritePlainf("(%d)", n.Length) + } + if n.Desc { + ctx.WritePlain(" DESC") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *IndexPartSpecification) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*IndexPartSpecification) + if n.Expr != nil { + node, ok := n.Expr.Accept(v) + if !ok { + return n, false + } + n.Expr = node.(ExprNode) + return v.Leave(n) + } + node, ok := n.Column.Accept(v) + if !ok { + return n, false + } + n.Column = node.(*ColumnName) + return v.Leave(n) +} + +// MatchType is the type for reference match type. +type MatchType int + +// match type +const ( + MatchNone MatchType = iota + MatchFull + MatchPartial + MatchSimple +) + +// ReferenceDef is used for parsing foreign key reference option from SQL. +// See http://dev.mysql.com/doc/refman/5.7/en/create-table-foreign-keys.html +type ReferenceDef struct { + node + + Table *TableName + IndexPartSpecifications []*IndexPartSpecification + OnDelete *OnDeleteOpt + OnUpdate *OnUpdateOpt + Match MatchType +} + +// Restore implements Node interface. +func (n *ReferenceDef) Restore(ctx *format.RestoreCtx) error { + if n.Table != nil { + ctx.WriteKeyWord("REFERENCES ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ReferenceDef") + } + } + + if n.IndexPartSpecifications != nil { + ctx.WritePlain("(") + for i, indexColNames := range n.IndexPartSpecifications { + if i > 0 { + ctx.WritePlain(", ") + } + if err := indexColNames.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing IndexPartSpecifications: [%v]", i) + } + } + ctx.WritePlain(")") + } + + if n.Match != MatchNone { + ctx.WriteKeyWord(" MATCH ") + switch n.Match { + case MatchFull: + ctx.WriteKeyWord("FULL") + case MatchPartial: + ctx.WriteKeyWord("PARTIAL") + case MatchSimple: + ctx.WriteKeyWord("SIMPLE") + } + } + if n.OnDelete.ReferOpt != model.ReferOptionNoOption { + ctx.WritePlain(" ") + if err := n.OnDelete.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing OnDelete") + } + } + if n.OnUpdate.ReferOpt != model.ReferOptionNoOption { + ctx.WritePlain(" ") + if err := n.OnUpdate.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing OnUpdate") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ReferenceDef) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ReferenceDef) + if n.Table != nil { + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + } + for i, val := range n.IndexPartSpecifications { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.IndexPartSpecifications[i] = node.(*IndexPartSpecification) + } + onDelete, ok := n.OnDelete.Accept(v) + if !ok { + return n, false + } + n.OnDelete = onDelete.(*OnDeleteOpt) + onUpdate, ok := n.OnUpdate.Accept(v) + if !ok { + return n, false + } + n.OnUpdate = onUpdate.(*OnUpdateOpt) + return v.Leave(n) +} + +// OnDeleteOpt is used for optional on delete clause. +type OnDeleteOpt struct { + node + ReferOpt model.ReferOptionType +} + +// Restore implements Node interface. +func (n *OnDeleteOpt) Restore(ctx *format.RestoreCtx) error { + if n.ReferOpt != model.ReferOptionNoOption { + ctx.WriteKeyWord("ON DELETE ") + ctx.WriteKeyWord(n.ReferOpt.String()) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *OnDeleteOpt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*OnDeleteOpt) + return v.Leave(n) +} + +// OnUpdateOpt is used for optional on update clause. +type OnUpdateOpt struct { + node + ReferOpt model.ReferOptionType +} + +// Restore implements Node interface. +func (n *OnUpdateOpt) Restore(ctx *format.RestoreCtx) error { + if n.ReferOpt != model.ReferOptionNoOption { + ctx.WriteKeyWord("ON UPDATE ") + ctx.WriteKeyWord(n.ReferOpt.String()) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *OnUpdateOpt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*OnUpdateOpt) + return v.Leave(n) +} + +// ColumnOptionType is the type for ColumnOption. +type ColumnOptionType int + +// ColumnOption types. +const ( + ColumnOptionNoOption ColumnOptionType = iota + ColumnOptionPrimaryKey + ColumnOptionNotNull + ColumnOptionAutoIncrement + ColumnOptionDefaultValue + ColumnOptionUniqKey + ColumnOptionNull + ColumnOptionOnUpdate // For Timestamp and Datetime only. + ColumnOptionFulltext + ColumnOptionComment + ColumnOptionGenerated + ColumnOptionReference + ColumnOptionCollate + ColumnOptionCheck + ColumnOptionColumnFormat + ColumnOptionStorage + ColumnOptionAutoRandom +) + +var ( + invalidOptionForGeneratedColumn = map[ColumnOptionType]string{ + ColumnOptionAutoIncrement: "AUTO_INCREMENT", + ColumnOptionOnUpdate: "ON UPDATE", + ColumnOptionDefaultValue: "DEFAULT", + } +) + +// ColumnOption is used for parsing column constraint info from SQL. +type ColumnOption struct { + node + + Tp ColumnOptionType + // Expr is used for ColumnOptionDefaultValue/ColumnOptionOnUpdateColumnOptionGenerated. + // For ColumnOptionDefaultValue or ColumnOptionOnUpdate, it's the target value. + // For ColumnOptionGenerated, it's the target expression. + Expr ExprNode + // Stored is only for ColumnOptionGenerated, default is false. + Stored bool + // Refer is used for foreign key. + Refer *ReferenceDef + StrValue string + AutoRandOpt AutoRandomOption + // Enforced is only for Check, default is true. + Enforced bool + // Name is only used for Check Constraint name. + ConstraintName string + PrimaryKeyTp model.PrimaryKeyType +} + +// Restore implements Node interface. +func (n *ColumnOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case ColumnOptionNoOption: + return nil + case ColumnOptionPrimaryKey: + ctx.WriteKeyWord("PRIMARY KEY") + pkTp := n.PrimaryKeyTp.String() + if len(pkTp) != 0 { + ctx.WritePlain(" ") + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDClusteredIndex, func() error { + ctx.WriteKeyWord(pkTp) + return nil + }) + } + case ColumnOptionNotNull: + ctx.WriteKeyWord("NOT NULL") + case ColumnOptionAutoIncrement: + ctx.WriteKeyWord("AUTO_INCREMENT") + case ColumnOptionDefaultValue: + ctx.WriteKeyWord("DEFAULT ") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnOption DefaultValue Expr") + } + case ColumnOptionUniqKey: + ctx.WriteKeyWord("UNIQUE KEY") + case ColumnOptionNull: + ctx.WriteKeyWord("NULL") + case ColumnOptionOnUpdate: + ctx.WriteKeyWord("ON UPDATE ") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnOption ON UPDATE Expr") + } + case ColumnOptionFulltext: + return errors.New("TiDB Parser ignore the `ColumnOptionFulltext` type now") + case ColumnOptionComment: + ctx.WriteKeyWord("COMMENT ") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnOption COMMENT Expr") + } + case ColumnOptionGenerated: + ctx.WriteKeyWord("GENERATED ALWAYS AS") + ctx.WritePlain("(") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnOption GENERATED ALWAYS Expr") + } + ctx.WritePlain(")") + if n.Stored { + ctx.WriteKeyWord(" STORED") + } else { + ctx.WriteKeyWord(" VIRTUAL") + } + case ColumnOptionReference: + if err := n.Refer.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnOption ReferenceDef") + } + case ColumnOptionCollate: + if n.StrValue == "" { + return errors.New("Empty ColumnOption COLLATE") + } + ctx.WriteKeyWord("COLLATE ") + ctx.WritePlain(n.StrValue) + case ColumnOptionCheck: + if n.ConstraintName != "" { + ctx.WriteKeyWord("CONSTRAINT ") + ctx.WriteName(n.ConstraintName) + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("CHECK") + ctx.WritePlain("(") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Trace(err) + } + ctx.WritePlain(")") + if n.Enforced { + ctx.WriteKeyWord(" ENFORCED") + } else { + ctx.WriteKeyWord(" NOT ENFORCED") + } + case ColumnOptionColumnFormat: + ctx.WriteKeyWord("COLUMN_FORMAT ") + ctx.WriteKeyWord(n.StrValue) + case ColumnOptionStorage: + ctx.WriteKeyWord("STORAGE ") + ctx.WriteKeyWord(n.StrValue) + case ColumnOptionAutoRandom: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandom, func() error { + ctx.WriteKeyWord("AUTO_RANDOM") + opt := n.AutoRandOpt + if opt.ShardBits != types.UnspecifiedLength { + if opt.RangeBits != types.UnspecifiedLength { + ctx.WritePlainf("(%d, %d)", opt.ShardBits, opt.RangeBits) + } else { + ctx.WritePlainf("(%d)", opt.ShardBits) + } + } + return nil + }) + default: + return errors.New("An error occurred while splicing ColumnOption") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ColumnOption) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ColumnOption) + if n.Expr != nil { + node, ok := n.Expr.Accept(v) + if !ok { + return n, false + } + n.Expr = node.(ExprNode) + } + return v.Leave(n) +} + +// AutoRandomOption contains the length of shard bits and range bits. +type AutoRandomOption struct { + // ShardBits is the number of bits used to store the shard. + ShardBits int + // RangeBits is the number of int primary key bits that will be used by TiDB. + RangeBits int +} + +// IndexVisibility is the option for index visibility. +type IndexVisibility int + +// IndexVisibility options. +const ( + IndexVisibilityDefault IndexVisibility = iota + IndexVisibilityVisible + IndexVisibilityInvisible +) + +// IndexOption is the index options. +// +// KEY_BLOCK_SIZE [=] value +// | index_type +// | WITH PARSER parser_name +// | COMMENT 'string' +// +// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html +type IndexOption struct { + node + + KeyBlockSize uint64 + Tp model.IndexType + Comment string + ParserName model.CIStr + Visibility IndexVisibility + PrimaryKeyTp model.PrimaryKeyType +} + +// Restore implements Node interface. +func (n *IndexOption) Restore(ctx *format.RestoreCtx) error { + hasPrevOption := false + if n.PrimaryKeyTp != model.PrimaryKeyTypeDefault { + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDClusteredIndex, func() error { + ctx.WriteKeyWord(n.PrimaryKeyTp.String()) + return nil + }) + hasPrevOption = true + } + if n.KeyBlockSize > 0 { + if hasPrevOption { + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("KEY_BLOCK_SIZE") + ctx.WritePlainf("=%d", n.KeyBlockSize) + hasPrevOption = true + } + + if n.Tp != model.IndexTypeInvalid { + if hasPrevOption { + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("USING ") + ctx.WritePlain(n.Tp.String()) + hasPrevOption = true + } + + if len(n.ParserName.O) > 0 { + if hasPrevOption { + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("WITH PARSER ") + ctx.WriteName(n.ParserName.O) + hasPrevOption = true + } + + if n.Comment != "" { + if hasPrevOption { + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("COMMENT ") + ctx.WriteString(n.Comment) + hasPrevOption = true + } + + if n.Visibility != IndexVisibilityDefault { + if hasPrevOption { + ctx.WritePlain(" ") + } + switch n.Visibility { + case IndexVisibilityVisible: + ctx.WriteKeyWord("VISIBLE") + case IndexVisibilityInvisible: + ctx.WriteKeyWord("INVISIBLE") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *IndexOption) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*IndexOption) + return v.Leave(n) +} + +// ConstraintType is the type for Constraint. +type ConstraintType int + +// ConstraintTypes +const ( + ConstraintNoConstraint ConstraintType = iota + ConstraintPrimaryKey + ConstraintKey + ConstraintIndex + ConstraintUniq + ConstraintUniqKey + ConstraintUniqIndex + ConstraintForeignKey + ConstraintFulltext + ConstraintCheck +) + +// Constraint is constraint for table definition. +type Constraint struct { + node + + // only supported by MariaDB 10.0.2+ (ADD {INDEX|KEY}, ADD FOREIGN KEY), + // see https://mariadb.com/kb/en/library/alter-table/ + IfNotExists bool + + Tp ConstraintType + Name string + + Keys []*IndexPartSpecification // Used for PRIMARY KEY, UNIQUE, ...... + + Refer *ReferenceDef // Used for foreign key. + + Option *IndexOption // Index Options + + Expr ExprNode // Used for Check + + Enforced bool // Used for Check + + InColumn bool // Used for Check + + InColumnName string // Used for Check + IsEmptyIndex bool // Used for Check +} + +// Restore implements Node interface. +func (n *Constraint) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case ConstraintNoConstraint: + return nil + case ConstraintPrimaryKey: + ctx.WriteKeyWord("PRIMARY KEY") + case ConstraintKey: + ctx.WriteKeyWord("KEY") + if n.IfNotExists { + ctx.WriteKeyWord(" IF NOT EXISTS") + } + case ConstraintIndex: + ctx.WriteKeyWord("INDEX") + if n.IfNotExists { + ctx.WriteKeyWord(" IF NOT EXISTS") + } + case ConstraintUniq: + ctx.WriteKeyWord("UNIQUE") + case ConstraintUniqKey: + ctx.WriteKeyWord("UNIQUE KEY") + case ConstraintUniqIndex: + ctx.WriteKeyWord("UNIQUE INDEX") + case ConstraintFulltext: + ctx.WriteKeyWord("FULLTEXT") + case ConstraintCheck: + if n.Name != "" { + ctx.WriteKeyWord("CONSTRAINT ") + ctx.WriteName(n.Name) + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("CHECK") + ctx.WritePlain("(") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Trace(err) + } + ctx.WritePlain(") ") + if n.Enforced { + ctx.WriteKeyWord("ENFORCED") + } else { + ctx.WriteKeyWord("NOT ENFORCED") + } + return nil + } + + if n.Tp == ConstraintForeignKey { + ctx.WriteKeyWord("CONSTRAINT ") + if n.Name != "" { + ctx.WriteName(n.Name) + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("FOREIGN KEY ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + } else if n.Name != "" || n.IsEmptyIndex { + ctx.WritePlain(" ") + ctx.WriteName(n.Name) + } + + ctx.WritePlain("(") + for i, keys := range n.Keys { + if i > 0 { + ctx.WritePlain(", ") + } + if err := keys.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing Constraint Keys: [%v]", i) + } + } + ctx.WritePlain(")") + + if n.Refer != nil { + ctx.WritePlain(" ") + if err := n.Refer.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing Constraint Refer") + } + } + + if n.Option != nil { + ctx.WritePlain(" ") + if err := n.Option.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing Constraint Option") + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *Constraint) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*Constraint) + for i, val := range n.Keys { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Keys[i] = node.(*IndexPartSpecification) + } + if n.Refer != nil { + node, ok := n.Refer.Accept(v) + if !ok { + return n, false + } + n.Refer = node.(*ReferenceDef) + } + if n.Option != nil { + node, ok := n.Option.Accept(v) + if !ok { + return n, false + } + n.Option = node.(*IndexOption) + } + if n.Expr != nil { + node, ok := n.Expr.Accept(v) + if !ok { + return n, false + } + n.Expr = node.(ExprNode) + } + return v.Leave(n) +} + +// ColumnDef is used for parsing column definition from SQL. +type ColumnDef struct { + node + + Name *ColumnName + Tp *types.FieldType + Options []*ColumnOption +} + +// Restore implements Node interface. +func (n *ColumnDef) Restore(ctx *format.RestoreCtx) error { + if err := n.Name.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnDef Name") + } + if n.Tp != nil { + ctx.WritePlain(" ") + if err := n.Tp.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing ColumnDef Type") + } + } + for i, options := range n.Options { + ctx.WritePlain(" ") + if err := options.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing ColumnDef ColumnOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ColumnDef) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ColumnDef) + node, ok := n.Name.Accept(v) + if !ok { + return n, false + } + n.Name = node.(*ColumnName) + for i, val := range n.Options { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Options[i] = node.(*ColumnOption) + } + return v.Leave(n) +} + +// Validate checks if a column definition is legal. +// For example, generated column definitions that contain such +// column options as `ON UPDATE`, `AUTO_INCREMENT`, `DEFAULT` +// are illegal. +func (n *ColumnDef) Validate() error { + generatedCol := false + var illegalOpt4gc string + for _, opt := range n.Options { + if opt.Tp == ColumnOptionGenerated { + generatedCol = true + } + msg, found := invalidOptionForGeneratedColumn[opt.Tp] + if found { + illegalOpt4gc = msg + } + } + if generatedCol && illegalOpt4gc != "" { + return ErrWrongUsage.GenWithStackByArgs(illegalOpt4gc, "generated column") + } + return nil +} + +type TemporaryKeyword int + +const ( + TemporaryNone TemporaryKeyword = iota + TemporaryGlobal + TemporaryLocal +) + +// CreateTableStmt is a statement to create a table. +// See https://dev.mysql.com/doc/refman/5.7/en/create-table.html +type CreateTableStmt struct { + ddlNode + + IfNotExists bool + TemporaryKeyword + // Meanless when TemporaryKeyword is not TemporaryGlobal. + // ON COMMIT DELETE ROWS => true + // ON COMMIT PRESERVE ROW => false + OnCommitDelete bool + Table *TableName + ReferTable *TableName + Cols []*ColumnDef + Constraints []*Constraint + Options []*TableOption + Partition *PartitionOptions + OnDuplicate OnDuplicateKeyHandlingType + Select ResultSetNode +} + +// Restore implements Node interface. +func (n *CreateTableStmt) Restore(ctx *format.RestoreCtx) error { + switch n.TemporaryKeyword { + case TemporaryNone: + ctx.WriteKeyWord("CREATE TABLE ") + case TemporaryGlobal: + ctx.WriteKeyWord("CREATE GLOBAL TEMPORARY TABLE ") + case TemporaryLocal: + ctx.WriteKeyWord("CREATE TEMPORARY TABLE ") + } + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Table") + } + + if n.ReferTable != nil { + ctx.WriteKeyWord(" LIKE ") + if err := n.ReferTable.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing CreateTableStmt ReferTable") + } + } + lenCols := len(n.Cols) + lenConstraints := len(n.Constraints) + if lenCols+lenConstraints > 0 { + ctx.WritePlain(" (") + for i, col := range n.Cols { + if i > 0 { + ctx.WritePlain(",") + } + if err := col.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt ColumnDef: [%v]", i) + } + } + for i, constraint := range n.Constraints { + if i > 0 || lenCols >= 1 { + ctx.WritePlain(",") + } + if err := constraint.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt Constraints: [%v]", i) + } + } + ctx.WritePlain(")") + } + + options := tableOptionsWithRestoreTTLFlag(ctx.Flags, n.Options) + for i, option := range options { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt TableOption: [%v]", i) + } + } + + if n.Partition != nil { + ctx.WritePlain(" ") + if err := n.Partition.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Partition") + } + } + + if n.Select != nil { + switch n.OnDuplicate { + case OnDuplicateKeyHandlingError: + ctx.WriteKeyWord(" AS ") + case OnDuplicateKeyHandlingIgnore: + ctx.WriteKeyWord(" IGNORE AS ") + case OnDuplicateKeyHandlingReplace: + ctx.WriteKeyWord(" REPLACE AS ") + } + + if err := n.Select.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Select") + } + } + + if n.TemporaryKeyword == TemporaryGlobal { + if n.OnCommitDelete { + ctx.WriteKeyWord(" ON COMMIT DELETE ROWS") + } else { + ctx.WriteKeyWord(" ON COMMIT PRESERVE ROWS") + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateTableStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + if n.ReferTable != nil { + node, ok = n.ReferTable.Accept(v) + if !ok { + return n, false + } + n.ReferTable = node.(*TableName) + } + for i, val := range n.Cols { + node, ok = val.Accept(v) + if !ok { + return n, false + } + n.Cols[i] = node.(*ColumnDef) + } + for i, val := range n.Constraints { + node, ok = val.Accept(v) + if !ok { + return n, false + } + n.Constraints[i] = node.(*Constraint) + } + if n.Select != nil { + node, ok := n.Select.Accept(v) + if !ok { + return n, false + } + n.Select = node.(ResultSetNode) + } + if n.Partition != nil { + node, ok := n.Partition.Accept(v) + if !ok { + return n, false + } + n.Partition = node.(*PartitionOptions) + } + for i, option := range n.Options { + node, ok = option.Accept(v) + if !ok { + return n, false + } + n.Options[i] = node.(*TableOption) + } + + return v.Leave(n) +} + +// DropTableStmt is a statement to drop one or more tables. +// See https://dev.mysql.com/doc/refman/5.7/en/drop-table.html +type DropTableStmt struct { + ddlNode + + IfExists bool + Tables []*TableName + IsView bool + TemporaryKeyword // make sense ONLY if/when IsView == false +} + +// Restore implements Node interface. +func (n *DropTableStmt) Restore(ctx *format.RestoreCtx) error { + if n.IsView { + ctx.WriteKeyWord("DROP VIEW ") + } else { + switch n.TemporaryKeyword { + case TemporaryNone: + ctx.WriteKeyWord("DROP TABLE ") + case TemporaryGlobal: + ctx.WriteKeyWord("DROP GLOBAL TEMPORARY TABLE ") + case TemporaryLocal: + ctx.WriteKeyWord("DROP TEMPORARY TABLE ") + } + } + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index) + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *DropTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropTableStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} + +// DropPlacementPolicyStmt is a statement to drop a Policy. +type DropPlacementPolicyStmt struct { + ddlNode + + IfExists bool + PolicyName model.CIStr +} + +// Restore implements Restore interface. +func (n *DropPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restorePlacementStmtInSpecialComment(ctx, n) + } + + ctx.WriteKeyWord("DROP PLACEMENT POLICY ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.PolicyName.O) + return nil +} + +func (n *DropPlacementPolicyStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropPlacementPolicyStmt) + return v.Leave(n) +} + +type DropResourceGroupStmt struct { + ddlNode + + IfExists bool + ResourceGroupName model.CIStr +} + +// Restore implements Restore interface. +func (n *DropResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) + } + + ctx.WriteKeyWord("DROP RESOURCE GROUP ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.ResourceGroupName.O) + return nil +} + +func (n *DropResourceGroupStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropResourceGroupStmt) + return v.Leave(n) +} + +// DropSequenceStmt is a statement to drop a Sequence. +type DropSequenceStmt struct { + ddlNode + + IfExists bool + Sequences []*TableName +} + +// Restore implements Node interface. +func (n *DropSequenceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP SEQUENCE ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + for i, sequence := range n.Sequences { + if i != 0 { + ctx.WritePlain(", ") + } + if err := sequence.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropSequenceStmt.Sequences[%d]", i) + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *DropSequenceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropSequenceStmt) + for i, val := range n.Sequences { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Sequences[i] = node.(*TableName) + } + return v.Leave(n) +} + +// RenameTableStmt is a statement to rename a table. +// See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html +type RenameTableStmt struct { + ddlNode + + TableToTables []*TableToTable +} + +// Restore implements Node interface. +func (n *RenameTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("RENAME TABLE ") + for index, table2table := range n.TableToTables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table2table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore RenameTableStmt.TableToTables") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RenameTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RenameTableStmt) + + for i, t := range n.TableToTables { + node, ok := t.Accept(v) + if !ok { + return n, false + } + n.TableToTables[i] = node.(*TableToTable) + } + + return v.Leave(n) +} + +// TableToTable represents renaming old table to new table used in RenameTableStmt. +type TableToTable struct { + node + OldTable *TableName + NewTable *TableName +} + +// Restore implements Node interface. +func (n *TableToTable) Restore(ctx *format.RestoreCtx) error { + if err := n.OldTable.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore TableToTable.OldTable") + } + ctx.WriteKeyWord(" TO ") + if err := n.NewTable.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore TableToTable.NewTable") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *TableToTable) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*TableToTable) + node, ok := n.OldTable.Accept(v) + if !ok { + return n, false + } + n.OldTable = node.(*TableName) + node, ok = n.NewTable.Accept(v) + if !ok { + return n, false + } + n.NewTable = node.(*TableName) + return v.Leave(n) +} + +// CreateViewStmt is a statement to create a View. +// See https://dev.mysql.com/doc/refman/5.7/en/create-view.html +type CreateViewStmt struct { + ddlNode + + OrReplace bool + ViewName *TableName + Cols []model.CIStr + Select StmtNode + SchemaCols []model.CIStr + Algorithm model.ViewAlgorithm + Definer *auth.UserIdentity + Security model.ViewSecurity + CheckOption model.ViewCheckOption +} + +// Restore implements Node interface. +func (n *CreateViewStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE ") + if n.OrReplace { + ctx.WriteKeyWord("OR REPLACE ") + } + ctx.WriteKeyWord("ALGORITHM") + ctx.WritePlain(" = ") + ctx.WriteKeyWord(n.Algorithm.String()) + ctx.WriteKeyWord(" DEFINER") + ctx.WritePlain(" = ") + + // todo Use n.Definer.Restore(ctx) to replace this part + if n.Definer.CurrentUser { + ctx.WriteKeyWord("current_user") + } else { + ctx.WriteName(n.Definer.Username) + if n.Definer.Hostname != "" { + ctx.WritePlain("@") + ctx.WriteName(n.Definer.Hostname) + } + } + + ctx.WriteKeyWord(" SQL SECURITY ") + ctx.WriteKeyWord(n.Security.String()) + ctx.WriteKeyWord(" VIEW ") + + if err := n.ViewName.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while create CreateViewStmt.ViewName") + } + + for i, col := range n.Cols { + if i == 0 { + ctx.WritePlain(" (") + } else { + ctx.WritePlain(",") + } + ctx.WriteName(col.O) + if i == len(n.Cols)-1 { + ctx.WritePlain(")") + } + } + + ctx.WriteKeyWord(" AS ") + + if err := n.Select.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while create CreateViewStmt.Select") + } + + if n.CheckOption != model.CheckOptionCascaded { + ctx.WriteKeyWord(" WITH ") + ctx.WriteKeyWord(n.CheckOption.String()) + ctx.WriteKeyWord(" CHECK OPTION") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateViewStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateViewStmt) + node, ok := n.ViewName.Accept(v) + if !ok { + return n, false + } + n.ViewName = node.(*TableName) + selnode, ok := n.Select.Accept(v) + if !ok { + return n, false + } + n.Select = selnode.(StmtNode) + return v.Leave(n) +} + +// CreatePlacementPolicyStmt is a statement to create a policy. +type CreatePlacementPolicyStmt struct { + ddlNode + + OrReplace bool + IfNotExists bool + PolicyName model.CIStr + PlacementOptions []*PlacementOption +} + +// Restore implements Node interface. +func (n *CreatePlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restorePlacementStmtInSpecialComment(ctx, n) + } + + ctx.WriteKeyWord("CREATE ") + if n.OrReplace { + ctx.WriteKeyWord("OR REPLACE ") + } + ctx.WriteKeyWord("PLACEMENT POLICY ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.PolicyName.O) + for i, option := range n.PlacementOptions { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreatePlacementPolicy TableOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CreatePlacementPolicyStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreatePlacementPolicyStmt) + return v.Leave(n) +} + +// CreateResourceGroupStmt is a statement to create a policy. +type CreateResourceGroupStmt struct { + ddlNode + + IfNotExists bool + ResourceGroupName model.CIStr + ResourceGroupOptionList []*ResourceGroupOption +} + +// Restore implements Node interface. +func (n *CreateResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) + } + + ctx.WriteKeyWord("CREATE ") + + ctx.WriteKeyWord("RESOURCE GROUP ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.ResourceGroupName.O) + for i, option := range n.ResourceGroupOptionList { + if i > 0 { + ctx.WritePlain(",") + } + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateResourceGroupStmt Option: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateResourceGroupStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateResourceGroupStmt) + return v.Leave(n) +} + +// CreateSequenceStmt is a statement to create a Sequence. +type CreateSequenceStmt struct { + ddlNode + + // TODO : support or replace if need : care for it will conflict on temporaryOpt. + IfNotExists bool + Name *TableName + SeqOptions []*SequenceOption + TblOptions []*TableOption +} + +// Restore implements Node interface. +func (n *CreateSequenceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE ") + ctx.WriteKeyWord("SEQUENCE ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + if err := n.Name.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while create CreateSequenceStmt.Name") + } + for i, option := range n.SeqOptions { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt SequenceOption: [%v]", i) + } + } + for i, option := range n.TblOptions { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt TableOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateSequenceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateSequenceStmt) + node, ok := n.Name.Accept(v) + if !ok { + return n, false + } + n.Name = node.(*TableName) + return v.Leave(n) +} + +// IndexLockAndAlgorithm stores the algorithm option and the lock option. +type IndexLockAndAlgorithm struct { + node + + LockTp LockType + AlgorithmTp AlgorithmType +} + +// Restore implements Node interface. +func (n *IndexLockAndAlgorithm) Restore(ctx *format.RestoreCtx) error { + hasPrevOption := false + if n.AlgorithmTp != AlgorithmTypeDefault { + ctx.WriteKeyWord("ALGORITHM") + ctx.WritePlain(" = ") + ctx.WriteKeyWord(n.AlgorithmTp.String()) + hasPrevOption = true + } + + if n.LockTp != LockTypeDefault { + if hasPrevOption { + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("LOCK") + ctx.WritePlain(" = ") + ctx.WriteKeyWord(n.LockTp.String()) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *IndexLockAndAlgorithm) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*IndexLockAndAlgorithm) + return v.Leave(n) +} + +// IndexKeyType is the type for index key. +type IndexKeyType int + +// Index key types. +const ( + IndexKeyTypeNone IndexKeyType = iota + IndexKeyTypeUnique + IndexKeyTypeSpatial + IndexKeyTypeFullText +) + +// CreateIndexStmt is a statement to create an index. +// See https://dev.mysql.com/doc/refman/5.7/en/create-index.html +type CreateIndexStmt struct { + ddlNode + + // only supported by MariaDB 10.0.2+, + // see https://mariadb.com/kb/en/library/create-index/ + IfNotExists bool + + IndexName string + Table *TableName + IndexPartSpecifications []*IndexPartSpecification + IndexOption *IndexOption + KeyType IndexKeyType + LockAlg *IndexLockAndAlgorithm +} + +// Restore implements Node interface. +func (n *CreateIndexStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE ") + switch n.KeyType { + case IndexKeyTypeUnique: + ctx.WriteKeyWord("UNIQUE ") + case IndexKeyTypeSpatial: + ctx.WriteKeyWord("SPATIAL ") + case IndexKeyTypeFullText: + ctx.WriteKeyWord("FULLTEXT ") + } + ctx.WriteKeyWord("INDEX ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.IndexName) + ctx.WriteKeyWord(" ON ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.Table") + } + + ctx.WritePlain(" (") + for i, indexColName := range n.IndexPartSpecifications { + if i != 0 { + ctx.WritePlain(", ") + } + if err := indexColName.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateIndexStmt.IndexPartSpecifications: [%v]", i) + } + } + ctx.WritePlain(")") + + if n.IndexOption.Tp != model.IndexTypeInvalid || n.IndexOption.KeyBlockSize > 0 || n.IndexOption.Comment != "" || len(n.IndexOption.ParserName.O) > 0 || n.IndexOption.Visibility != IndexVisibilityDefault { + ctx.WritePlain(" ") + if err := n.IndexOption.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.IndexOption") + } + } + + if n.LockAlg != nil { + ctx.WritePlain(" ") + if err := n.LockAlg.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg") + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateIndexStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateIndexStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + for i, val := range n.IndexPartSpecifications { + node, ok = val.Accept(v) + if !ok { + return n, false + } + n.IndexPartSpecifications[i] = node.(*IndexPartSpecification) + } + if n.IndexOption != nil { + node, ok := n.IndexOption.Accept(v) + if !ok { + return n, false + } + n.IndexOption = node.(*IndexOption) + } + if n.LockAlg != nil { + node, ok := n.LockAlg.Accept(v) + if !ok { + return n, false + } + n.LockAlg = node.(*IndexLockAndAlgorithm) + } + return v.Leave(n) +} + +// DropIndexStmt is a statement to drop the index. +// See https://dev.mysql.com/doc/refman/5.7/en/drop-index.html +type DropIndexStmt struct { + ddlNode + + IfExists bool + IndexName string + Table *TableName + LockAlg *IndexLockAndAlgorithm + IsHypo bool // whether this operation is for a hypothetical index. +} + +// Restore implements Node interface. +func (n *DropIndexStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP INDEX ") + if n.IfExists { + _ = ctx.WriteWithSpecialComments("", func() error { + ctx.WriteKeyWord("IF EXISTS ") + return nil + }) + } + ctx.WriteName(n.IndexName) + ctx.WriteKeyWord(" ON ") + + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while add index") + } + + if n.LockAlg != nil { + ctx.WritePlain(" ") + if err := n.LockAlg.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.LockAlg") + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *DropIndexStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropIndexStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + if n.LockAlg != nil { + node, ok := n.LockAlg.Accept(v) + if !ok { + return n, false + } + n.LockAlg = node.(*IndexLockAndAlgorithm) + } + return v.Leave(n) +} + +// LockTablesStmt is a statement to lock tables. +type LockTablesStmt struct { + ddlNode + + TableLocks []TableLock +} + +// TableLock contains the table name and lock type. +type TableLock struct { + Table *TableName + Type model.TableLockType +} + +// Accept implements Node Accept interface. +func (n *LockTablesStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*LockTablesStmt) + for i := range n.TableLocks { + node, ok := n.TableLocks[i].Table.Accept(v) + if !ok { + return n, false + } + n.TableLocks[i].Table = node.(*TableName) + } + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *LockTablesStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("LOCK TABLES ") + for i, tl := range n.TableLocks { + if i != 0 { + ctx.WritePlain(", ") + } + if err := tl.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while add index") + } + ctx.WriteKeyWord(" " + tl.Type.String()) + } + return nil +} + +// UnlockTablesStmt is a statement to unlock tables. +type UnlockTablesStmt struct { + ddlNode +} + +// Accept implements Node Accept interface. +func (n *UnlockTablesStmt) Accept(v Visitor) (Node, bool) { + _, _ = v.Enter(n) + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *UnlockTablesStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("UNLOCK TABLES") + return nil +} + +// CleanupTableLockStmt is a statement to cleanup table lock. +type CleanupTableLockStmt struct { + ddlNode + + Tables []*TableName +} + +// Accept implements Node Accept interface. +func (n *CleanupTableLockStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CleanupTableLockStmt) + for i := range n.Tables { + node, ok := n.Tables[i].Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *CleanupTableLockStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ADMIN CLEANUP TABLE LOCK ") + for i, v := range n.Tables { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CleanupTableLockStmt.Tables[%d]", i) + } + } + return nil +} + +// RepairTableStmt is a statement to repair tableInfo. +type RepairTableStmt struct { + ddlNode + Table *TableName + CreateStmt *CreateTableStmt +} + +// Accept implements Node Accept interface. +func (n *RepairTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RepairTableStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + node, ok = n.CreateStmt.Accept(v) + if !ok { + return n, false + } + n.CreateStmt = node.(*CreateTableStmt) + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *RepairTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ADMIN REPAIR TABLE ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.table : [%v]", n.Table) + } + ctx.WritePlain(" ") + if err := n.CreateStmt.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RepairTableStmt.createStmt : [%v]", n.CreateStmt) + } + return nil +} + +// PlacementOptionType is the type for PlacementOption +type PlacementOptionType int + +// PlacementOption types. +const ( + PlacementOptionPrimaryRegion PlacementOptionType = 0x3000 + iota + PlacementOptionRegions + PlacementOptionFollowerCount + PlacementOptionVoterCount + PlacementOptionLearnerCount + PlacementOptionSchedule + PlacementOptionConstraints + PlacementOptionLeaderConstraints + PlacementOptionLearnerConstraints + PlacementOptionFollowerConstraints + PlacementOptionVoterConstraints + PlacementOptionSurvivalPreferences + PlacementOptionPolicy +) + +// PlacementOption is used for parsing placement option. +type PlacementOption struct { + Tp PlacementOptionType + StrValue string + UintValue uint64 +} + +func (n *PlacementOption) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { + return nil + } + fn := func() error { + switch n.Tp { + case PlacementOptionPrimaryRegion: + ctx.WriteKeyWord("PRIMARY_REGION ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionRegions: + ctx.WriteKeyWord("REGIONS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionFollowerCount: + ctx.WriteKeyWord("FOLLOWERS ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case PlacementOptionVoterCount: + ctx.WriteKeyWord("VOTERS ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case PlacementOptionLearnerCount: + ctx.WriteKeyWord("LEARNERS ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case PlacementOptionSchedule: + ctx.WriteKeyWord("SCHEDULE ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionConstraints: + ctx.WriteKeyWord("CONSTRAINTS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionLeaderConstraints: + ctx.WriteKeyWord("LEADER_CONSTRAINTS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionFollowerConstraints: + ctx.WriteKeyWord("FOLLOWER_CONSTRAINTS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionVoterConstraints: + ctx.WriteKeyWord("VOTER_CONSTRAINTS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionLearnerConstraints: + ctx.WriteKeyWord("LEARNER_CONSTRAINTS ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case PlacementOptionPolicy: + ctx.WriteKeyWord("PLACEMENT POLICY ") + ctx.WritePlain("= ") + ctx.WriteName(n.StrValue) + case PlacementOptionSurvivalPreferences: + ctx.WriteKeyWord("SURVIVAL_PREFERENCES ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + default: + return errors.Errorf("invalid PlacementOption: %d", n.Tp) + } + return nil + } + // WriteSpecialComment + return ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, fn) +} + +// ResourceGroupOption is used for parsing resource group option. +type ResourceGroupOption struct { + Tp ResourceUnitType + StrValue string + UintValue uint64 + BoolValue bool + RunawayOptionList []*ResourceGroupRunawayOption + BackgroundOptions []*ResourceGroupBackgroundOption +} + +type ResourceUnitType int + +const ( + // RU mode + ResourceRURate ResourceUnitType = iota + ResourcePriority + // Raw mode + ResourceUnitCPU + ResourceUnitIOReadBandwidth + ResourceUnitIOWriteBandwidth + + // Options + ResourceBurstableOpiton + ResourceGroupRunaway + ResourceGroupBackground +) + +func (n *ResourceGroupOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case ResourceRURate: + ctx.WriteKeyWord("RU_PER_SEC ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case ResourcePriority: + ctx.WriteKeyWord("PRIORITY ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(model.PriorityValueToName(n.UintValue)) + case ResourceUnitCPU: + ctx.WriteKeyWord("CPU ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case ResourceUnitIOReadBandwidth: + ctx.WriteKeyWord("IO_READ_BANDWIDTH ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case ResourceUnitIOWriteBandwidth: + ctx.WriteKeyWord("IO_WRITE_BANDWIDTH ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case ResourceBurstableOpiton: + ctx.WriteKeyWord("BURSTABLE ") + ctx.WritePlain("= ") + ctx.WritePlain(strings.ToUpper(fmt.Sprintf("%v", n.BoolValue))) + case ResourceGroupRunaway: + ctx.WritePlain("QUERY_LIMIT ") + ctx.WritePlain("= ") + if len(n.RunawayOptionList) > 0 { + ctx.WritePlain("(") + for i, option := range n.RunawayOptionList { + if i > 0 { + ctx.WritePlain(" ") + } + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing ResourceGroupRunaway Option: [%v]", option) + } + } + ctx.WritePlain(")") + } else { + ctx.WritePlain("NULL") + } + case ResourceGroupBackground: + ctx.WritePlain("BACKGROUND ") + ctx.WritePlain("= ") + if len(n.BackgroundOptions) > 0 { + ctx.WritePlain("(") + for i, option := range n.BackgroundOptions { + if i > 0 { + ctx.WritePlain(", ") + } + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing ResourceGroup Background Option: [%v]", option) + } + } + ctx.WritePlain(")") + } else { + ctx.WritePlain("NULL") + } + default: + return errors.Errorf("invalid ResourceGroupOption: %d", n.Tp) + } + return nil +} + +type RunawayOptionType int + +const ( + RunawayRule RunawayOptionType = iota + RunawayAction + RunawayWatch +) + +// ResourceGroupRunawayOption is used for parsing resource group runaway rule option. +type ResourceGroupRunawayOption struct { + Tp RunawayOptionType + StrValue string + IntValue int32 +} + +func (n *ResourceGroupRunawayOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case RunawayRule: + ctx.WriteKeyWord("EXEC_ELAPSED ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case RunawayAction: + ctx.WriteKeyWord("ACTION ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(model.RunawayActionType(n.IntValue).String()) + case RunawayWatch: + ctx.WriteKeyWord("WATCH ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(model.RunawayWatchType(n.IntValue).String()) + ctx.WritePlain(" ") + ctx.WriteKeyWord("DURATION ") + ctx.WritePlain("= ") + if len(n.StrValue) > 0 { + ctx.WriteString(n.StrValue) + } else { + ctx.WriteKeyWord("UNLIMITED") + } + default: + return errors.Errorf("invalid ResourceGroupRunawayOption: %d", n.Tp) + } + return nil +} + +type BackgroundOptionType int + +const ( + BackgroundOptionNone BackgroundOptionType = iota + BackgroundOptionTaskNames +) + +// ResourceGroupBackgroundOption is used to config background job settings. +type ResourceGroupBackgroundOption struct { + Type BackgroundOptionType + StrValue string +} + +func (n *ResourceGroupBackgroundOption) Restore(ctx *format.RestoreCtx) error { + switch n.Type { + case BackgroundOptionTaskNames: + ctx.WriteKeyWord("TASK_TYPES ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + default: + return errors.Errorf("unknown ResourceGroupBackgroundOption: %d", n.Type) + } + + return nil +} + +type StatsOptionType int + +const ( + StatsOptionBuckets StatsOptionType = 0x5000 + iota + StatsOptionTopN + StatsOptionColsChoice + StatsOptionColList + StatsOptionSampleRate +) + +// TableOptionType is the type for TableOption +type TableOptionType int + +// TableOption types. +const ( + TableOptionNone TableOptionType = iota + TableOptionEngine + TableOptionCharset + TableOptionCollate + TableOptionAutoIdCache //nolint:revive + TableOptionAutoIncrement + TableOptionAutoRandomBase + TableOptionComment + TableOptionAvgRowLength + TableOptionCheckSum + TableOptionCompression + TableOptionConnection + TableOptionPassword + TableOptionKeyBlockSize + TableOptionMaxRows + TableOptionMinRows + TableOptionDelayKeyWrite + TableOptionRowFormat + TableOptionStatsPersistent + TableOptionStatsAutoRecalc + TableOptionShardRowID + TableOptionPreSplitRegion + TableOptionPackKeys + TableOptionTablespace + TableOptionNodegroup + TableOptionDataDirectory + TableOptionIndexDirectory + TableOptionStorageMedia + TableOptionStatsSamplePages + TableOptionSecondaryEngine + TableOptionSecondaryEngineNull + TableOptionInsertMethod + TableOptionTableCheckSum + TableOptionUnion + TableOptionEncryption + TableOptionTTL + TableOptionTTLEnable + TableOptionTTLJobInterval + TableOptionPlacementPolicy = TableOptionType(PlacementOptionPolicy) + TableOptionStatsBuckets = TableOptionType(StatsOptionBuckets) + TableOptionStatsTopN = TableOptionType(StatsOptionTopN) + TableOptionStatsColsChoice = TableOptionType(StatsOptionColsChoice) + TableOptionStatsColList = TableOptionType(StatsOptionColList) + TableOptionStatsSampleRate = TableOptionType(StatsOptionSampleRate) +) + +// RowFormat types +const ( + RowFormatDefault uint64 = iota + 1 + RowFormatDynamic + RowFormatFixed + RowFormatCompressed + RowFormatRedundant + RowFormatCompact + TokuDBRowFormatDefault + TokuDBRowFormatFast + TokuDBRowFormatSmall + TokuDBRowFormatZlib + TokuDBRowFormatQuickLZ + TokuDBRowFormatLzma + TokuDBRowFormatSnappy + TokuDBRowFormatUncompressed + TokuDBRowFormatZstd +) + +// OnDuplicateKeyHandlingType is the option that handle unique key values in 'CREATE TABLE ... SELECT' or `LOAD DATA`. +// See https://dev.mysql.com/doc/refman/5.7/en/create-table-select.html +// See https://dev.mysql.com/doc/refman/5.7/en/load-data.html +type OnDuplicateKeyHandlingType int + +// OnDuplicateKeyHandling types +const ( + OnDuplicateKeyHandlingError OnDuplicateKeyHandlingType = iota + OnDuplicateKeyHandlingIgnore + OnDuplicateKeyHandlingReplace +) + +const ( + TableOptionCharsetWithoutConvertTo uint64 = 0 + TableOptionCharsetWithConvertTo uint64 = 1 +) + +// TableOption is used for parsing table option from SQL. +type TableOption struct { + node + Tp TableOptionType + Default bool + StrValue string + UintValue uint64 + BoolValue bool + TimeUnitValue *TimeUnitExpr + Value ValueExpr + TableNames []*TableName + ColumnName *ColumnName +} + +func (n *TableOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case TableOptionEngine: + ctx.WriteKeyWord("ENGINE ") + ctx.WritePlain("= ") + if n.StrValue != "" { + ctx.WritePlain(n.StrValue) + } else { + ctx.WritePlain("''") + } + case TableOptionCharset: + if n.UintValue == TableOptionCharsetWithConvertTo { + ctx.WriteKeyWord("CONVERT TO ") + } else { + ctx.WriteKeyWord("DEFAULT ") + } + ctx.WriteKeyWord("CHARACTER SET ") + if n.UintValue == TableOptionCharsetWithoutConvertTo { + ctx.WriteKeyWord("= ") + } + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WriteKeyWord(n.StrValue) + } + case TableOptionCollate: + ctx.WriteKeyWord("DEFAULT COLLATE ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(n.StrValue) + case TableOptionAutoIncrement: + if n.BoolValue { + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDForceAutoInc, func() error { + ctx.WriteKeyWord("FORCE") + return nil + }) + ctx.WritePlain(" ") + } + ctx.WriteKeyWord("AUTO_INCREMENT ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionAutoIdCache: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoIDCache, func() error { + ctx.WriteKeyWord("AUTO_ID_CACHE ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + return nil + }) + case TableOptionAutoRandomBase: + if n.BoolValue { + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDForceAutoInc, func() error { + ctx.WriteKeyWord("FORCE") + return nil + }) + ctx.WritePlain(" ") + } + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDAutoRandomBase, func() error { + ctx.WriteKeyWord("AUTO_RANDOM_BASE ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + return nil + }) + case TableOptionComment: + ctx.WriteKeyWord("COMMENT ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionAvgRowLength: + ctx.WriteKeyWord("AVG_ROW_LENGTH ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionCheckSum: + ctx.WriteKeyWord("CHECKSUM ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionCompression: + ctx.WriteKeyWord("COMPRESSION ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionConnection: + ctx.WriteKeyWord("CONNECTION ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionPassword: + ctx.WriteKeyWord("PASSWORD ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionKeyBlockSize: + ctx.WriteKeyWord("KEY_BLOCK_SIZE ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionMaxRows: + ctx.WriteKeyWord("MAX_ROWS ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionMinRows: + ctx.WriteKeyWord("MIN_ROWS ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionDelayKeyWrite: + ctx.WriteKeyWord("DELAY_KEY_WRITE ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionRowFormat: + ctx.WriteKeyWord("ROW_FORMAT ") + ctx.WritePlain("= ") + switch n.UintValue { + case RowFormatDefault: + ctx.WriteKeyWord("DEFAULT") + case RowFormatDynamic: + ctx.WriteKeyWord("DYNAMIC") + case RowFormatFixed: + ctx.WriteKeyWord("FIXED") + case RowFormatCompressed: + ctx.WriteKeyWord("COMPRESSED") + case RowFormatRedundant: + ctx.WriteKeyWord("REDUNDANT") + case RowFormatCompact: + ctx.WriteKeyWord("COMPACT") + case TokuDBRowFormatDefault: + ctx.WriteKeyWord("TOKUDB_DEFAULT") + case TokuDBRowFormatFast: + ctx.WriteKeyWord("TOKUDB_FAST") + case TokuDBRowFormatSmall: + ctx.WriteKeyWord("TOKUDB_SMALL") + case TokuDBRowFormatZlib: + ctx.WriteKeyWord("TOKUDB_ZLIB") + case TokuDBRowFormatQuickLZ: + ctx.WriteKeyWord("TOKUDB_QUICKLZ") + case TokuDBRowFormatLzma: + ctx.WriteKeyWord("TOKUDB_LZMA") + case TokuDBRowFormatSnappy: + ctx.WriteKeyWord("TOKUDB_SNAPPY") + case TokuDBRowFormatZstd: + ctx.WriteKeyWord("TOKUDB_ZSTD") + case TokuDBRowFormatUncompressed: + ctx.WriteKeyWord("TOKUDB_UNCOMPRESSED") + default: + return errors.Errorf("invalid TableOption: TableOptionRowFormat: %d", n.UintValue) + } + case TableOptionStatsPersistent: + // TODO: not support + ctx.WriteKeyWord("STATS_PERSISTENT ") + ctx.WritePlain("= ") + ctx.WriteKeyWord("DEFAULT") + ctx.WritePlain(" /* TableOptionStatsPersistent is not supported */ ") + case TableOptionStatsAutoRecalc: + ctx.WriteKeyWord("STATS_AUTO_RECALC ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WritePlainf("%d", n.UintValue) + } + case TableOptionShardRowID: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error { + ctx.WriteKeyWord("SHARD_ROW_ID_BITS ") + ctx.WritePlainf("= %d", n.UintValue) + return nil + }) + case TableOptionPreSplitRegion: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTiDB, func() error { + ctx.WriteKeyWord("PRE_SPLIT_REGIONS ") + ctx.WritePlainf("= %d", n.UintValue) + return nil + }) + case TableOptionPackKeys: + // TODO: not support + ctx.WriteKeyWord("PACK_KEYS ") + ctx.WritePlain("= ") + ctx.WriteKeyWord("DEFAULT") + ctx.WritePlain(" /* TableOptionPackKeys is not supported */ ") + case TableOptionTablespace: + ctx.WriteKeyWord("TABLESPACE ") + ctx.WritePlain("= ") + ctx.WriteName(n.StrValue) + case TableOptionNodegroup: + ctx.WriteKeyWord("NODEGROUP ") + ctx.WritePlainf("= %d", n.UintValue) + case TableOptionDataDirectory: + ctx.WriteKeyWord("DATA DIRECTORY ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionIndexDirectory: + ctx.WriteKeyWord("INDEX DIRECTORY ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionStorageMedia: + ctx.WriteKeyWord("STORAGE ") + ctx.WriteKeyWord(n.StrValue) + case TableOptionStatsSamplePages: + ctx.WriteKeyWord("STATS_SAMPLE_PAGES ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WritePlainf("%d", n.UintValue) + } + case TableOptionSecondaryEngine: + ctx.WriteKeyWord("SECONDARY_ENGINE ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionSecondaryEngineNull: + ctx.WriteKeyWord("SECONDARY_ENGINE ") + ctx.WritePlain("= ") + ctx.WriteKeyWord("NULL") + case TableOptionInsertMethod: + ctx.WriteKeyWord("INSERT_METHOD ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(n.StrValue) + case TableOptionTableCheckSum: + ctx.WriteKeyWord("TABLE_CHECKSUM ") + ctx.WritePlain("= ") + ctx.WritePlainf("%d", n.UintValue) + case TableOptionUnion: + ctx.WriteKeyWord("UNION ") + ctx.WritePlain("= (") + for i, tableName := range n.TableNames { + if i != 0 { + ctx.WritePlain(",") + } + tableName.Restore(ctx) + } + ctx.WritePlain(")") + case TableOptionEncryption: + ctx.WriteKeyWord("ENCRYPTION ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + case TableOptionPlacementPolicy: + if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { + return nil + } + placementOpt := PlacementOption{ + Tp: PlacementOptionPolicy, + UintValue: n.UintValue, + StrValue: n.StrValue, + } + return placementOpt.Restore(ctx) + case TableOptionStatsBuckets: + ctx.WriteKeyWord("STATS_BUCKETS ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WritePlainf("%d", n.UintValue) + } + case TableOptionStatsTopN: + ctx.WriteKeyWord("STATS_TOPN ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WritePlainf("%d", n.UintValue) + } + case TableOptionStatsSampleRate: + ctx.WriteKeyWord("STATS_SAMPLE_RATE ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WritePlainf("%v", n.Value.GetValue()) + } + case TableOptionStatsColsChoice: + ctx.WriteKeyWord("STATS_COL_CHOICE ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WriteString(n.StrValue) + } + case TableOptionStatsColList: + ctx.WriteKeyWord("STATS_COL_LIST ") + ctx.WritePlain("= ") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WriteString(n.StrValue) + } + case TableOptionTTL: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { + ctx.WriteKeyWord("TTL ") + ctx.WritePlain("= ") + ctx.WriteName(n.ColumnName.Name.String()) + ctx.WritePlain(" + INTERVAL ") + err := n.Value.Restore(ctx) + ctx.WritePlain(" ") + if err != nil { + return err + } + return n.TimeUnitValue.Restore(ctx) + }) + case TableOptionTTLEnable: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { + ctx.WriteKeyWord("TTL_ENABLE ") + ctx.WritePlain("= ") + if n.BoolValue { + ctx.WriteString("ON") + } else { + ctx.WriteString("OFF") + } + return nil + }) + case TableOptionTTLJobInterval: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { + ctx.WriteKeyWord("TTL_JOB_INTERVAL ") + ctx.WritePlain("= ") + ctx.WriteString(n.StrValue) + return nil + }) + default: + return errors.Errorf("invalid TableOption: %d", n.Tp) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *TableOption) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*TableOption) + if n.Value != nil { + node, ok := n.Value.Accept(v) + if !ok { + return n, false + } + n.Value = node.(ValueExpr) + } + if n.TimeUnitValue != nil { + node, ok := n.TimeUnitValue.Accept(v) + if !ok { + return n, false + } + n.TimeUnitValue = node.(*TimeUnitExpr) + } + return v.Leave(n) +} + +// SequenceOptionType is the type for SequenceOption +type SequenceOptionType int + +// SequenceOption types. +const ( + SequenceOptionNone SequenceOptionType = iota + SequenceOptionIncrementBy + SequenceStartWith + SequenceNoMinValue + SequenceMinValue + SequenceNoMaxValue + SequenceMaxValue + SequenceNoCache + SequenceCache + SequenceNoCycle + SequenceCycle + // SequenceRestart is only used in alter sequence statement. + SequenceRestart + SequenceRestartWith +) + +// SequenceOption is used for parsing sequence option from SQL. +type SequenceOption struct { + Tp SequenceOptionType + IntValue int64 +} + +func (n *SequenceOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case SequenceOptionIncrementBy: + ctx.WriteKeyWord("INCREMENT BY ") + ctx.WritePlainf("%d", n.IntValue) + case SequenceStartWith: + ctx.WriteKeyWord("START WITH ") + ctx.WritePlainf("%d", n.IntValue) + case SequenceNoMinValue: + ctx.WriteKeyWord("NO MINVALUE") + case SequenceMinValue: + ctx.WriteKeyWord("MINVALUE ") + ctx.WritePlainf("%d", n.IntValue) + case SequenceNoMaxValue: + ctx.WriteKeyWord("NO MAXVALUE") + case SequenceMaxValue: + ctx.WriteKeyWord("MAXVALUE ") + ctx.WritePlainf("%d", n.IntValue) + case SequenceNoCache: + ctx.WriteKeyWord("NOCACHE") + case SequenceCache: + ctx.WriteKeyWord("CACHE ") + ctx.WritePlainf("%d", n.IntValue) + case SequenceNoCycle: + ctx.WriteKeyWord("NOCYCLE") + case SequenceCycle: + ctx.WriteKeyWord("CYCLE") + case SequenceRestart: + ctx.WriteKeyWord("RESTART") + case SequenceRestartWith: + ctx.WriteKeyWord("RESTART WITH ") + ctx.WritePlainf("%d", n.IntValue) + default: + return errors.Errorf("invalid SequenceOption: %d", n.Tp) + } + return nil +} + +// ColumnPositionType is the type for ColumnPosition. +type ColumnPositionType int + +// ColumnPosition Types +const ( + ColumnPositionNone ColumnPositionType = iota + ColumnPositionFirst + ColumnPositionAfter +) + +// ColumnPosition represent the position of the newly added column +type ColumnPosition struct { + node + // Tp is either ColumnPositionNone, ColumnPositionFirst or ColumnPositionAfter. + Tp ColumnPositionType + // RelativeColumn is the column the newly added column after if type is ColumnPositionAfter + RelativeColumn *ColumnName +} + +// Restore implements Node interface. +func (n *ColumnPosition) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case ColumnPositionNone: + // do nothing + case ColumnPositionFirst: + ctx.WriteKeyWord("FIRST") + case ColumnPositionAfter: + ctx.WriteKeyWord("AFTER ") + if err := n.RelativeColumn.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ColumnPosition.RelativeColumn") + } + default: + return errors.Errorf("invalid ColumnPositionType: %d", n.Tp) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ColumnPosition) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ColumnPosition) + if n.RelativeColumn != nil { + node, ok := n.RelativeColumn.Accept(v) + if !ok { + return n, false + } + n.RelativeColumn = node.(*ColumnName) + } + return v.Leave(n) +} + +// AlterTableType is the type for AlterTableSpec. +type AlterTableType int + +// AlterTable types. +const ( + AlterTableOption AlterTableType = iota + 1 + AlterTableAddColumns + AlterTableAddConstraint + AlterTableDropColumn + AlterTableDropPrimaryKey + AlterTableDropIndex + AlterTableDropForeignKey + AlterTableModifyColumn + AlterTableChangeColumn + AlterTableRenameColumn + AlterTableRenameTable + AlterTableAlterColumn + AlterTableLock + AlterTableWriteable + AlterTableAlgorithm + AlterTableRenameIndex + AlterTableForce + AlterTableAddPartitions + // A tombstone for `AlterTableAlterPartition`. It will never be used anymore. + // Just left a tombstone here to keep the enum number unchanged. + __DEPRECATED_AlterTableAlterPartition //nolint:revive + AlterTablePartitionAttributes + AlterTablePartitionOptions + AlterTableCoalescePartitions + AlterTableDropPartition + AlterTableTruncatePartition + AlterTablePartition + AlterTableEnableKeys + AlterTableDisableKeys + AlterTableRemovePartitioning + AlterTableWithValidation + AlterTableWithoutValidation + AlterTableSecondaryLoad + AlterTableSecondaryUnload + AlterTableRebuildPartition + AlterTableReorganizePartition + AlterTableCheckPartitions + AlterTableExchangePartition + AlterTableOptimizePartition + AlterTableRepairPartition + AlterTableImportPartitionTablespace + AlterTableDiscardPartitionTablespace + AlterTableAlterCheck + AlterTableDropCheck + AlterTableImportTablespace + AlterTableDiscardTablespace + AlterTableIndexInvisible + // TODO: Add more actions + AlterTableOrderByColumns + // AlterTableSetTiFlashReplica uses to set the table TiFlash replica. + AlterTableSetTiFlashReplica + // A tombstone for `AlterTablePlacement`. It will never be used anymore. + // Just left a tombstone here to keep the enum number unchanged. + __DEPRECATED_AlterTablePlacement //nolint:revive + AlterTableAddStatistics + AlterTableDropStatistics + AlterTableAttributes + AlterTableCache + AlterTableNoCache + AlterTableStatsOptions + AlterTableDropFirstPartition + AlterTableAddLastPartition + AlterTableReorganizeLastPartition + AlterTableReorganizeFirstPartition + AlterTableRemoveTTL +) + +// LockType is the type for AlterTableSpec. +// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html#alter-table-concurrency +type LockType byte + +func (n LockType) String() string { + switch n { + case LockTypeNone: + return "NONE" + case LockTypeDefault: + return "DEFAULT" + case LockTypeShared: + return "SHARED" + case LockTypeExclusive: + return "EXCLUSIVE" + } + return "" +} + +// Lock Types. +const ( + LockTypeNone LockType = iota + 1 + LockTypeDefault + LockTypeShared + LockTypeExclusive +) + +// AlgorithmType is the algorithm of the DDL operations. +// See https://dev.mysql.com/doc/refman/8.0/en/alter-table.html#alter-table-performance. +type AlgorithmType byte + +// DDL algorithms. +// For now, TiDB only supported inplace and instance algorithms. If the user specify `copy`, +// will get an error. +const ( + AlgorithmTypeDefault AlgorithmType = iota + AlgorithmTypeCopy + AlgorithmTypeInplace + AlgorithmTypeInstant +) + +func (a AlgorithmType) String() string { + switch a { + case AlgorithmTypeDefault: + return "DEFAULT" + case AlgorithmTypeCopy: + return "COPY" + case AlgorithmTypeInplace: + return "INPLACE" + case AlgorithmTypeInstant: + return "INSTANT" + default: + return "DEFAULT" + } +} + +// AlterTableSpec represents alter table specification. +type AlterTableSpec struct { + node + + // only supported by MariaDB 10.0.2+ (DROP COLUMN, CHANGE COLUMN, MODIFY COLUMN, DROP INDEX, DROP FOREIGN KEY, DROP PARTITION) + // see https://mariadb.com/kb/en/library/alter-table/ + IfExists bool + + // only supported by MariaDB 10.0.2+ (ADD COLUMN, ADD PARTITION) + // see https://mariadb.com/kb/en/library/alter-table/ + IfNotExists bool + + NoWriteToBinlog bool + OnAllPartitions bool + + Tp AlterTableType + Name string + IndexName model.CIStr + Constraint *Constraint + Options []*TableOption + OrderByList []*AlterOrderItem + NewTable *TableName + NewColumns []*ColumnDef + NewConstraints []*Constraint + OldColumnName *ColumnName + NewColumnName *ColumnName + Position *ColumnPosition + LockType LockType + Algorithm AlgorithmType + Comment string + FromKey model.CIStr + ToKey model.CIStr + Partition *PartitionOptions + PartitionNames []model.CIStr + PartDefinitions []*PartitionDefinition + WithValidation bool + Num uint64 + Visibility IndexVisibility + TiFlashReplica *TiFlashReplicaSpec + Writeable bool + Statistics *StatisticsSpec + AttributesSpec *AttributesSpec + StatsOptionsSpec *StatsOptionsSpec +} + +type TiFlashReplicaSpec struct { + Count uint64 + Labels []string + Hypo bool // hypothetical replica is used by index advisor +} + +// AlterOrderItem represents an item in order by at alter table stmt. +type AlterOrderItem struct { + node + Column *ColumnName + Desc bool +} + +// Restore implements Node interface. +func (n *AlterOrderItem) Restore(ctx *format.RestoreCtx) error { + if err := n.Column.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterOrderItem.Column") + } + if n.Desc { + ctx.WriteKeyWord(" DESC") + } + return nil +} + +func (n *AlterTableSpec) IsAllPlacementRule() bool { + switch n.Tp { + case AlterTablePartitionAttributes, AlterTablePartitionOptions, AlterTableOption, AlterTableAttributes: + for _, o := range n.Options { + if o.Tp != TableOptionPlacementPolicy { + return false + } + } + return true + default: + return false + } +} + +// Restore implements Node interface. +func (n *AlterTableSpec) Restore(ctx *format.RestoreCtx) error { + if n.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { + return nil + } + switch n.Tp { + case AlterTableSetTiFlashReplica: + ctx.WriteKeyWord("SET TIFLASH REPLICA ") + ctx.WritePlainf("%d", n.TiFlashReplica.Count) + if len(n.TiFlashReplica.Labels) == 0 { + break + } + ctx.WriteKeyWord(" LOCATION LABELS ") + for i, v := range n.TiFlashReplica.Labels { + if i > 0 { + ctx.WritePlain(", ") + } + ctx.WriteString(v) + } + case AlterTableAddStatistics: + ctx.WriteKeyWord("ADD STATS_EXTENDED ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.Statistics.StatsName) + switch n.Statistics.StatsType { + case StatsTypeCardinality: + ctx.WriteKeyWord(" CARDINALITY(") + case StatsTypeDependency: + ctx.WriteKeyWord(" DEPENDENCY(") + case StatsTypeCorrelation: + ctx.WriteKeyWord(" CORRELATION(") + } + for i, col := range n.Statistics.Columns { + if i != 0 { + ctx.WritePlain(", ") + } + if err := col.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AddStatisticsSpec.Columns: [%v]", i) + } + } + ctx.WritePlain(")") + case AlterTableDropStatistics: + ctx.WriteKeyWord("DROP STATS_EXTENDED ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.Statistics.StatsName) + case AlterTableOption: + switch { + case len(n.Options) == 2 && n.Options[0].Tp == TableOptionCharset && n.Options[1].Tp == TableOptionCollate: + if n.Options[0].UintValue == TableOptionCharsetWithConvertTo { + ctx.WriteKeyWord("CONVERT TO ") + } + ctx.WriteKeyWord("CHARACTER SET ") + if n.Options[0].Default { + ctx.WriteKeyWord("DEFAULT") + } else { + ctx.WriteKeyWord(n.Options[0].StrValue) + } + ctx.WriteKeyWord(" COLLATE ") + ctx.WriteKeyWord(n.Options[1].StrValue) + case n.Options[0].Tp == TableOptionCharset && n.Options[0].Default: + if n.Options[0].UintValue == TableOptionCharsetWithConvertTo { + ctx.WriteKeyWord("CONVERT TO ") + } + ctx.WriteKeyWord("CHARACTER SET DEFAULT") + default: + for i, opt := range n.Options { + if i != 0 { + ctx.WritePlain(" ") + } + if err := opt.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d]", i) + } + } + } + case AlterTableAddColumns: + ctx.WriteKeyWord("ADD COLUMN ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + if n.Position != nil && len(n.NewColumns) == 1 { + if err := n.NewColumns[0].Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", 0) + } + if n.Position.Tp != ColumnPositionNone { + ctx.WritePlain(" ") + } + if err := n.Position.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") + } + } else { + lenCols := len(n.NewColumns) + ctx.WritePlain("(") + for i, col := range n.NewColumns { + if i != 0 { + ctx.WritePlain(", ") + } + if err := col.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", i) + } + } + for i, constraint := range n.NewConstraints { + if i != 0 || lenCols >= 1 { + ctx.WritePlain(", ") + } + if err := constraint.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewConstraints[%d]", i) + } + } + ctx.WritePlain(")") + } + case AlterTableAddConstraint: + ctx.WriteKeyWord("ADD ") + if err := n.Constraint.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Constraint") + } + case AlterTableDropColumn: + ctx.WriteKeyWord("DROP COLUMN ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if err := n.OldColumnName.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") + } + // TODO: RestrictOrCascadeOpt not support + case AlterTableDropPrimaryKey: + ctx.WriteKeyWord("DROP PRIMARY KEY") + case AlterTableDropIndex: + ctx.WriteKeyWord("DROP INDEX ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.Name) + case AlterTableDropForeignKey: + ctx.WriteKeyWord("DROP FOREIGN KEY ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.Name) + case AlterTableModifyColumn: + ctx.WriteKeyWord("MODIFY COLUMN ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if err := n.NewColumns[0].Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") + } + if n.Position.Tp != ColumnPositionNone { + ctx.WritePlain(" ") + } + if err := n.Position.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") + } + case AlterTableChangeColumn: + ctx.WriteKeyWord("CHANGE COLUMN ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if err := n.OldColumnName.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") + } + ctx.WritePlain(" ") + if err := n.NewColumns[0].Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") + } + if n.Position.Tp != ColumnPositionNone { + ctx.WritePlain(" ") + } + if err := n.Position.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position") + } + case AlterTableRenameColumn: + ctx.WriteKeyWord("RENAME COLUMN ") + if err := n.OldColumnName.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName") + } + ctx.WriteKeyWord(" TO ") + if err := n.NewColumnName.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumnName") + } + case AlterTableRenameTable: + ctx.WriteKeyWord("RENAME AS ") + if err := n.NewTable.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewTable") + } + case AlterTableAlterColumn: + ctx.WriteKeyWord("ALTER COLUMN ") + if err := n.NewColumns[0].Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]") + } + if len(n.NewColumns[0].Options) == 1 { + ctx.WriteKeyWord("SET DEFAULT ") + expr := n.NewColumns[0].Options[0].Expr + if valueExpr, ok := expr.(ValueExpr); ok { + if err := valueExpr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr") + } + } else { + ctx.WritePlain("(") + if err := expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr") + } + ctx.WritePlain(")") + } + } else { + ctx.WriteKeyWord(" DROP DEFAULT") + } + case AlterTableLock: + ctx.WriteKeyWord("LOCK ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(n.LockType.String()) + case AlterTableWriteable: + ctx.WriteKeyWord("READ ") + if n.Writeable { + ctx.WriteKeyWord("WRITE") + } else { + ctx.WriteKeyWord("ONLY") + } + case AlterTableOrderByColumns: + ctx.WriteKeyWord("ORDER BY ") + for i, alterOrderItem := range n.OrderByList { + if i != 0 { + ctx.WritePlain(",") + } + if err := alterOrderItem.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.OrderByList[%d]", i) + } + } + case AlterTableAlgorithm: + ctx.WriteKeyWord("ALGORITHM ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(n.Algorithm.String()) + case AlterTableRenameIndex: + ctx.WriteKeyWord("RENAME INDEX ") + ctx.WriteName(n.FromKey.O) + ctx.WriteKeyWord(" TO ") + ctx.WriteName(n.ToKey.O) + case AlterTableForce: + // TODO: not support + ctx.WriteKeyWord("FORCE") + ctx.WritePlain(" /* AlterTableForce is not supported */ ") + case AlterTableAddPartitions: + ctx.WriteKeyWord("ADD PARTITION") + if n.IfNotExists { + ctx.WriteKeyWord(" IF NOT EXISTS") + } + if n.NoWriteToBinlog { + ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") + } + if n.PartDefinitions != nil { + ctx.WritePlain(" (") + for i, def := range n.PartDefinitions { + if i != 0 { + ctx.WritePlain(", ") + } + if err := def.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i) + } + } + ctx.WritePlain(")") + } else if n.Num != 0 { + ctx.WriteKeyWord(" PARTITIONS ") + ctx.WritePlainf("%d", n.Num) + } + case AlterTableDropFirstPartition: + ctx.WriteKeyWord("FIRST PARTITION LESS THAN (") + if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableDropFirstPartition Exprs") + } + ctx.WriteKeyWord(")") + if n.NoWriteToBinlog { + ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") + } + case AlterTableAddLastPartition: + ctx.WriteKeyWord("LAST PARTITION LESS THAN (") + if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableAddLastPartition Exprs") + } + ctx.WriteKeyWord(")") + if n.NoWriteToBinlog { + ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") + } + case AlterTablePartitionOptions: + restoreWithoutSpecialComment := func() error { + origFlags := ctx.Flags + defer func() { + ctx.Flags = origFlags + }() + ctx.Flags &= ^format.RestoreTiDBSpecialComment + ctx.WriteKeyWord("PARTITION ") + ctx.WriteName(n.PartitionNames[0].O) + ctx.WritePlain(" ") + + for i, opt := range n.Options { + if i != 0 { + ctx.WritePlain(" ") + } + if err := opt.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d] for PARTITION `%s`", i, n.PartitionNames[0].O) + } + } + return nil + } + + var err error + if ctx.Flags.HasTiDBSpecialCommentFlag() { + // AlterTablePartitionOptions now only supports placement options, so add put all options to special comment + err = ctx.WriteWithSpecialComments(tidb.FeatureIDPlacement, restoreWithoutSpecialComment) + } else { + err = restoreWithoutSpecialComment() + } + + if err != nil { + return err + } + case AlterTablePartitionAttributes: + ctx.WriteKeyWord("PARTITION ") + ctx.WriteName(n.PartitionNames[0].O) + ctx.WritePlain(" ") + + spec := n.AttributesSpec + if err := spec.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec") + } + case AlterTableCoalescePartitions: + ctx.WriteKeyWord("COALESCE PARTITION ") + if n.NoWriteToBinlog { + ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") + } + ctx.WritePlainf("%d", n.Num) + case AlterTableDropPartition: + ctx.WriteKeyWord("DROP PARTITION ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableTruncatePartition: + ctx.WriteKeyWord("TRUNCATE PARTITION ") + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableCheckPartitions: + ctx.WriteKeyWord("CHECK PARTITION ") + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableOptimizePartition: + ctx.WriteKeyWord("OPTIMIZE PARTITION ") + if n.NoWriteToBinlog { + ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") + } + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableRepairPartition: + ctx.WriteKeyWord("REPAIR PARTITION ") + if n.NoWriteToBinlog { + ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") + } + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableImportPartitionTablespace: + ctx.WriteKeyWord("IMPORT PARTITION ") + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + } else { + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + } + ctx.WriteKeyWord(" TABLESPACE") + case AlterTableDiscardPartitionTablespace: + ctx.WriteKeyWord("DISCARD PARTITION ") + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + } else { + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + } + ctx.WriteKeyWord(" TABLESPACE") + case AlterTablePartition: + if err := n.Partition.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Partition") + } + case AlterTableEnableKeys: + ctx.WriteKeyWord("ENABLE KEYS") + case AlterTableDisableKeys: + ctx.WriteKeyWord("DISABLE KEYS") + case AlterTableRemovePartitioning: + ctx.WriteKeyWord("REMOVE PARTITIONING") + case AlterTableWithValidation: + ctx.WriteKeyWord("WITH VALIDATION") + case AlterTableWithoutValidation: + ctx.WriteKeyWord("WITHOUT VALIDATION") + case AlterTableRebuildPartition: + ctx.WriteKeyWord("REBUILD PARTITION ") + if n.NoWriteToBinlog { + ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") + } + if n.OnAllPartitions { + ctx.WriteKeyWord("ALL") + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(name.O) + } + case AlterTableReorganizeLastPartition: + ctx.WriteKeyWord("SPLIT MAXVALUE PARTITION LESS THAN (") + if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs") + } + ctx.WriteKeyWord(")") + case AlterTableReorganizeFirstPartition: + ctx.WriteKeyWord("MERGE FIRST PARTITION LESS THAN (") + if err := n.Partition.PartitionMethod.Expr.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableReorganizeLastPartition Exprs") + } + ctx.WriteKeyWord(")") + case AlterTableReorganizePartition: + ctx.WriteKeyWord("REORGANIZE PARTITION") + if n.NoWriteToBinlog { + ctx.WriteKeyWord(" NO_WRITE_TO_BINLOG") + } + if n.OnAllPartitions { + return nil + } + for i, name := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } else { + ctx.WritePlain(" ") + } + ctx.WriteName(name.O) + } + ctx.WriteKeyWord(" INTO ") + if n.PartDefinitions != nil { + ctx.WritePlain("(") + for i, def := range n.PartDefinitions { + if i != 0 { + ctx.WritePlain(", ") + } + if err := def.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i) + } + } + ctx.WritePlain(")") + } + case AlterTableExchangePartition: + ctx.WriteKeyWord("EXCHANGE PARTITION ") + ctx.WriteName(n.PartitionNames[0].O) + ctx.WriteKeyWord(" WITH TABLE ") + n.NewTable.Restore(ctx) + if !n.WithValidation { + ctx.WriteKeyWord(" WITHOUT VALIDATION") + } + case AlterTableSecondaryLoad: + ctx.WriteKeyWord("SECONDARY_LOAD") + case AlterTableSecondaryUnload: + ctx.WriteKeyWord("SECONDARY_UNLOAD") + case AlterTableAlterCheck: + ctx.WriteKeyWord("ALTER CHECK ") + ctx.WriteName(n.Constraint.Name) + if !n.Constraint.Enforced { + ctx.WriteKeyWord(" NOT") + } + ctx.WriteKeyWord(" ENFORCED") + case AlterTableDropCheck: + ctx.WriteKeyWord("DROP CHECK ") + ctx.WriteName(n.Constraint.Name) + case AlterTableImportTablespace: + ctx.WriteKeyWord("IMPORT TABLESPACE") + case AlterTableDiscardTablespace: + ctx.WriteKeyWord("DISCARD TABLESPACE") + case AlterTableIndexInvisible: + ctx.WriteKeyWord("ALTER INDEX ") + ctx.WriteName(n.IndexName.O) + switch n.Visibility { + case IndexVisibilityVisible: + ctx.WriteKeyWord(" VISIBLE") + case IndexVisibilityInvisible: + ctx.WriteKeyWord(" INVISIBLE") + } + case AlterTableAttributes: + spec := n.AttributesSpec + if err := spec.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.AttributesSpec") + } + case AlterTableCache: + ctx.WriteKeyWord("CACHE") + case AlterTableNoCache: + ctx.WriteKeyWord("NOCACHE") + case AlterTableStatsOptions: + spec := n.StatsOptionsSpec + if err := spec.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.StatsOptionsSpec") + } + case AlterTableRemoveTTL: + _ = ctx.WriteWithSpecialComments(tidb.FeatureIDTTL, func() error { + ctx.WriteKeyWord("REMOVE TTL") + return nil + }) + default: + // TODO: not support + ctx.WritePlainf(" /* AlterTableType(%d) is not supported */ ", n.Tp) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AlterTableSpec) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterTableSpec) + if n.Constraint != nil { + node, ok := n.Constraint.Accept(v) + if !ok { + return n, false + } + n.Constraint = node.(*Constraint) + } + if n.NewTable != nil { + node, ok := n.NewTable.Accept(v) + if !ok { + return n, false + } + n.NewTable = node.(*TableName) + } + for i, col := range n.NewColumns { + node, ok := col.Accept(v) + if !ok { + return n, false + } + n.NewColumns[i] = node.(*ColumnDef) + } + for i, constraint := range n.NewConstraints { + node, ok := constraint.Accept(v) + if !ok { + return n, false + } + n.NewConstraints[i] = node.(*Constraint) + } + if n.OldColumnName != nil { + node, ok := n.OldColumnName.Accept(v) + if !ok { + return n, false + } + n.OldColumnName = node.(*ColumnName) + } + if n.Position != nil { + node, ok := n.Position.Accept(v) + if !ok { + return n, false + } + n.Position = node.(*ColumnPosition) + } + if n.Partition != nil { + node, ok := n.Partition.Accept(v) + if !ok { + return n, false + } + n.Partition = node.(*PartitionOptions) + } + for i, option := range n.Options { + node, ok := option.Accept(v) + if !ok { + return n, false + } + n.Options[i] = node.(*TableOption) + } + for _, def := range n.PartDefinitions { + if !def.acceptInPlace(v) { + return n, false + } + } + return v.Leave(n) +} + +// AlterTableStmt is a statement to change the structure of a table. +// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html +type AlterTableStmt struct { + ddlNode + + Table *TableName + Specs []*AlterTableSpec +} + +func (n *AlterTableStmt) HaveOnlyPlacementOptions() bool { + for _, n := range n.Specs { + if n.Tp != AlterTablePartitionOptions { + return false + } + if !n.IsAllPlacementRule() { + return false + } + } + return true +} + +// Restore implements Node interface. +func (n *AlterTableStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() && n.HaveOnlyPlacementOptions() { + return nil + } + ctx.WriteKeyWord("ALTER TABLE ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterTableStmt.Table") + } + specs := make([]*AlterTableSpec, 0, len(n.Specs)) + for _, spec := range n.Specs { + if spec.IsAllPlacementRule() && ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { + continue + } + if spec.Tp == AlterTableOption { + newOptions := tableOptionsWithRestoreTTLFlag(ctx.Flags, spec.Options) + if len(newOptions) == 0 { + continue + } + newSpec := *spec + newSpec.Options = newOptions + spec = &newSpec + } + specs = append(specs, spec) + } + for i, spec := range specs { + if i == 0 || spec.Tp == AlterTablePartition || spec.Tp == AlterTableRemovePartitioning || spec.Tp == AlterTableImportTablespace || spec.Tp == AlterTableDiscardTablespace { + ctx.WritePlain(" ") + } else { + ctx.WritePlain(", ") + } + if err := spec.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterTableStmt.Specs[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AlterTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterTableStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + for i, val := range n.Specs { + node, ok = val.Accept(v) + if !ok { + return n, false + } + n.Specs[i] = node.(*AlterTableSpec) + } + return v.Leave(n) +} + +// TruncateTableStmt is a statement to empty a table completely. +// See https://dev.mysql.com/doc/refman/5.7/en/truncate-table.html +type TruncateTableStmt struct { + ddlNode + + Table *TableName +} + +// Restore implements Node interface. +func (n *TruncateTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("TRUNCATE TABLE ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore TruncateTableStmt.Table") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *TruncateTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*TruncateTableStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + return v.Leave(n) +} + +var ( + ErrNoParts = terror.ClassDDL.NewStd(mysql.ErrNoParts) + ErrPartitionColumnList = terror.ClassDDL.NewStd(mysql.ErrPartitionColumnList) + ErrPartitionRequiresValues = terror.ClassDDL.NewStd(mysql.ErrPartitionRequiresValues) + ErrPartitionsMustBeDefined = terror.ClassDDL.NewStd(mysql.ErrPartitionsMustBeDefined) + ErrPartitionWrongNoPart = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoPart) + ErrPartitionWrongNoSubpart = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongNoSubpart) + ErrPartitionWrongValues = terror.ClassDDL.NewStd(mysql.ErrPartitionWrongValues) + ErrRowSinglePartitionField = terror.ClassDDL.NewStd(mysql.ErrRowSinglePartitionField) + ErrSubpartition = terror.ClassDDL.NewStd(mysql.ErrSubpartition) + ErrSystemVersioningWrongPartitions = terror.ClassDDL.NewStd(mysql.ErrSystemVersioningWrongPartitions) + ErrTooManyValues = terror.ClassDDL.NewStd(mysql.ErrTooManyValues) + ErrWrongPartitionTypeExpectedSystemTime = terror.ClassDDL.NewStd(mysql.ErrWrongPartitionTypeExpectedSystemTime) + ErrUnknownCharacterSet = terror.ClassDDL.NewStd(mysql.ErrUnknownCharacterSet) + ErrCoalescePartitionNoPartition = terror.ClassDDL.NewStd(mysql.ErrCoalescePartitionNoPartition) + ErrWrongUsage = terror.ClassDDL.NewStd(mysql.ErrWrongUsage) +) + +type SubPartitionDefinition struct { + Name model.CIStr + Options []*TableOption +} + +func (spd *SubPartitionDefinition) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SUBPARTITION ") + ctx.WriteName(spd.Name.O) + for i, opt := range spd.Options { + ctx.WritePlain(" ") + if err := opt.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore SubPartitionDefinition.Options[%d]", i) + } + } + return nil +} + +type PartitionDefinitionClause interface { + restore(ctx *format.RestoreCtx) error + acceptInPlace(v Visitor) bool + // Validate checks if the clause is consistent with the given options. + // `pt` can be 0 and `columns` can be -1 to skip checking the clause against + // the partition type or number of columns in the expression list. + Validate(pt model.PartitionType, columns int) error +} + +type PartitionDefinitionClauseNone struct{} + +func (*PartitionDefinitionClauseNone) restore(_ *format.RestoreCtx) error { + return nil +} + +func (*PartitionDefinitionClauseNone) acceptInPlace(_ Visitor) bool { + return true +} + +func (*PartitionDefinitionClauseNone) Validate(pt model.PartitionType, _ int) error { + switch pt { + case 0: + case model.PartitionTypeRange: + return ErrPartitionRequiresValues.GenWithStackByArgs("RANGE", "LESS THAN") + case model.PartitionTypeList: + return ErrPartitionRequiresValues.GenWithStackByArgs("LIST", "IN") + case model.PartitionTypeSystemTime: + return ErrSystemVersioningWrongPartitions + } + return nil +} + +type PartitionDefinitionClauseLessThan struct { + Exprs []ExprNode +} + +func (n *PartitionDefinitionClauseLessThan) restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(" VALUES LESS THAN ") + ctx.WritePlain("(") + for i, expr := range n.Exprs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := expr.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseLessThan.Exprs[%d]", i) + } + } + ctx.WritePlain(")") + return nil +} + +func (n *PartitionDefinitionClauseLessThan) acceptInPlace(v Visitor) bool { + for i, expr := range n.Exprs { + newExpr, ok := expr.Accept(v) + if !ok { + return false + } + n.Exprs[i] = newExpr.(ExprNode) + } + return true +} + +func (n *PartitionDefinitionClauseLessThan) Validate(pt model.PartitionType, columns int) error { + switch pt { + case model.PartitionTypeRange, 0: + default: + return ErrPartitionWrongValues.GenWithStackByArgs("RANGE", "LESS THAN") + } + + switch { + case columns == 0 && len(n.Exprs) != 1: + return ErrTooManyValues.GenWithStackByArgs("RANGE") + case columns > 0 && len(n.Exprs) != columns: + return ErrPartitionColumnList + } + return nil +} + +type PartitionDefinitionClauseIn struct { + Values [][]ExprNode +} + +func (n *PartitionDefinitionClauseIn) restore(ctx *format.RestoreCtx) error { + // we special-case an empty list of values to mean MariaDB's "DEFAULT" clause. + if len(n.Values) == 0 { + ctx.WriteKeyWord(" DEFAULT") + return nil + } + if len(n.Values) == 1 && len(n.Values[0]) == 1 { + if _, ok := n.Values[0][0].(*DefaultExpr); ok { + ctx.WriteKeyWord(" DEFAULT") + return nil + } + } + + ctx.WriteKeyWord(" VALUES IN ") + ctx.WritePlain("(") + for i, valList := range n.Values { + if i != 0 { + ctx.WritePlain(", ") + } + if len(valList) == 1 { + if err := valList[0].Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][0]", i) + } + } else { + ctx.WritePlain("(") + for j, val := range valList { + if j != 0 { + ctx.WritePlain(", ") + } + if err := val.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionDefinitionClauseIn.Values[%d][%d]", i, j) + } + } + ctx.WritePlain(")") + } + } + ctx.WritePlain(")") + return nil +} + +func (n *PartitionDefinitionClauseIn) acceptInPlace(v Visitor) bool { + for _, valList := range n.Values { + for j, val := range valList { + newVal, ok := val.Accept(v) + if !ok { + return false + } + valList[j] = newVal.(ExprNode) + } + } + return true +} + +func (n *PartitionDefinitionClauseIn) Validate(pt model.PartitionType, columns int) error { + switch pt { + case model.PartitionTypeList, 0: + default: + return ErrPartitionWrongValues.GenWithStackByArgs("LIST", "IN") + } + + if len(n.Values) == 0 { + return nil + } + + nextIdx := 1 + expectedColCount := len(n.Values[0]) + // OK if one of the n.Values is DefaultExpr as only value + if expectedColCount == 1 { + if _, ok := n.Values[0][0].(*DefaultExpr); ok { + // Only DEFAULT in the partition definition, OK + if len(n.Values) > 1 { + expectedColCount = len(n.Values[1]) + nextIdx++ + } + } + } + for _, val := range n.Values[nextIdx:] { + if len(val) != expectedColCount { + if _, ok := val[0].(*DefaultExpr); ok && len(val) == 1 { + continue + } + return ErrPartitionColumnList + } + } + + switch { + case columns == 0 && expectedColCount != 1: + return ErrRowSinglePartitionField + case columns > 0 && expectedColCount != columns: + if len(n.Values) == 1 && expectedColCount == 1 { + if _, ok := n.Values[0][0].(*DefaultExpr); ok { + // Only one value, which is DEFAULT, which is OK + return nil + } + } + return ErrPartitionColumnList + } + return nil +} + +type PartitionDefinitionClauseHistory struct { + Current bool +} + +func (n *PartitionDefinitionClauseHistory) restore(ctx *format.RestoreCtx) error { + if n.Current { + ctx.WriteKeyWord(" CURRENT") + } else { + ctx.WriteKeyWord(" HISTORY") + } + return nil +} + +func (*PartitionDefinitionClauseHistory) acceptInPlace(_ Visitor) bool { + return true +} + +func (*PartitionDefinitionClauseHistory) Validate(pt model.PartitionType, _ int) error { + switch pt { + case 0, model.PartitionTypeSystemTime: + default: + return ErrWrongPartitionTypeExpectedSystemTime + } + + return nil +} + +// PartitionDefinition defines a single partition. +type PartitionDefinition struct { + Name model.CIStr + Clause PartitionDefinitionClause + Options []*TableOption + Sub []*SubPartitionDefinition +} + +// Comment returns the comment option given to this definition. +// The second return value indicates if the comment option exists. +func (n *PartitionDefinition) Comment() (string, bool) { + for _, opt := range n.Options { + if opt.Tp == TableOptionComment { + return opt.StrValue, true + } + } + return "", false +} + +func (n *PartitionDefinition) acceptInPlace(v Visitor) bool { + return n.Clause.acceptInPlace(v) +} + +// Restore implements Node interface. +func (n *PartitionDefinition) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("PARTITION ") + ctx.WriteName(n.Name.O) + + if err := n.Clause.restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PartitionDefinition.Clause") + } + + for i, opt := range n.Options { + ctx.WritePlain(" ") + if err := opt.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Options[%d]", i) + } + } + + if len(n.Sub) > 0 { + ctx.WritePlain(" (") + for i, spd := range n.Sub { + if i != 0 { + ctx.WritePlain(",") + } + if err := spd.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.Sub[%d]", i) + } + } + ctx.WritePlain(")") + } + + return nil +} + +type PartitionIntervalExpr struct { + Expr ExprNode + // TimeUnitInvalid if not Time based INTERVAL! + TimeUnit TimeUnitType +} + +type PartitionInterval struct { + // To be able to get original text and replace the syntactic sugar with generated + // partition definitions + node + IntervalExpr PartitionIntervalExpr + FirstRangeEnd *ExprNode + LastRangeEnd *ExprNode + MaxValPart bool + NullPart bool +} + +// PartitionMethod describes how partitions or subpartitions are constructed. +type PartitionMethod struct { + // To be able to get original text and replace the syntactic sugar with generated + // partition definitions + node + // Tp is the type of the partition function + Tp model.PartitionType + // Linear is a modifier to the HASH and KEY type for choosing a different + // algorithm + Linear bool + // Expr is an expression used as argument of HASH, RANGE AND LIST types + Expr ExprNode + // ColumnNames is a list of column names used as argument of KEY, + // RANGE COLUMNS and LIST COLUMNS types + ColumnNames []*ColumnName + // Unit is a time unit used as argument of SYSTEM_TIME type + Unit TimeUnitType + // Limit is a row count used as argument of the SYSTEM_TIME type + Limit uint64 + + // Num is the number of (sub)partitions required by the method. + Num uint64 + + // KeyAlgorithm is the optional hash algorithm type for `PARTITION BY [LINEAR] KEY` syntax. + KeyAlgorithm *PartitionKeyAlgorithm + + Interval *PartitionInterval +} + +type PartitionKeyAlgorithm struct { + Type uint64 +} + +// Restore implements the Node interface +func (n *PartitionMethod) Restore(ctx *format.RestoreCtx) error { + if n.Linear { + ctx.WriteKeyWord("LINEAR ") + } + ctx.WriteKeyWord(n.Tp.String()) + + if n.KeyAlgorithm != nil { + ctx.WriteKeyWord(" ALGORITHM") + ctx.WritePlainf(" = %d", n.KeyAlgorithm.Type) + } + + switch { + case n.Tp == model.PartitionTypeSystemTime: + if n.Expr != nil && n.Unit != TimeUnitInvalid { + ctx.WriteKeyWord(" INTERVAL ") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr") + } + ctx.WritePlain(" ") + ctx.WriteKeyWord(n.Unit.String()) + } + if n.Limit > 0 { + ctx.WriteKeyWord(" LIMIT ") + ctx.WritePlainf("%d", n.Limit) + } + + case n.Expr != nil: + ctx.WritePlain(" (") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PartitionMethod.Expr") + } + ctx.WritePlain(")") + + default: + if n.Tp == model.PartitionTypeRange || n.Tp == model.PartitionTypeList { + ctx.WriteKeyWord(" COLUMNS") + } + ctx.WritePlain(" (") + for i, col := range n.ColumnNames { + if i > 0 { + ctx.WritePlain(",") + } + if err := col.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing PartitionMethod.ColumnName[%d]", i) + } + } + ctx.WritePlain(")") + } + + if n.Interval != nil { + ctx.WritePlain(" INTERVAL (") + n.Interval.IntervalExpr.Expr.Restore(ctx) + if n.Interval.IntervalExpr.TimeUnit != TimeUnitInvalid { + ctx.WritePlain(" ") + ctx.WriteKeyWord(n.Interval.IntervalExpr.TimeUnit.String()) + } + ctx.WritePlain(")") + if n.Interval.FirstRangeEnd != nil { + ctx.WritePlain(" FIRST PARTITION LESS THAN (") + (*n.Interval.FirstRangeEnd).Restore(ctx) + ctx.WritePlain(")") + } + if n.Interval.LastRangeEnd != nil { + ctx.WritePlain(" LAST PARTITION LESS THAN (") + (*n.Interval.LastRangeEnd).Restore(ctx) + ctx.WritePlain(")") + } + if n.Interval.NullPart { + ctx.WritePlain(" NULL PARTITION") + } + if n.Interval.MaxValPart { + ctx.WritePlain(" MAXVALUE PARTITION") + } + } + + return nil +} + +// acceptInPlace is like Node.Accept but does not allow replacing the node itself. +func (n *PartitionMethod) acceptInPlace(v Visitor) bool { + if n.Expr != nil { + expr, ok := n.Expr.Accept(v) + if !ok { + return false + } + n.Expr = expr.(ExprNode) + } + for i, colName := range n.ColumnNames { + newColName, ok := colName.Accept(v) + if !ok { + return false + } + n.ColumnNames[i] = newColName.(*ColumnName) + } + return true +} + +// PartitionOptions specifies the partition options. +type PartitionOptions struct { + PartitionMethod + Sub *PartitionMethod + Definitions []*PartitionDefinition +} + +// Validate checks if the partition is well-formed. +func (n *PartitionOptions) Validate() error { + // if both a partition list and the partition numbers are specified, their values must match + if n.Num != 0 && len(n.Definitions) != 0 && n.Num != uint64(len(n.Definitions)) { + return ErrPartitionWrongNoPart + } + // now check the subpartition count + if len(n.Definitions) > 0 { + // ensure the subpartition count for every partitions are the same + // then normalize n.Num and n.Sub.Num so equality comparison works. + n.Num = uint64(len(n.Definitions)) + + subDefCount := len(n.Definitions[0].Sub) + for _, pd := range n.Definitions[1:] { + if len(pd.Sub) != subDefCount { + return ErrPartitionWrongNoSubpart + } + } + if n.Sub != nil { + if n.Sub.Num != 0 && subDefCount != 0 && n.Sub.Num != uint64(subDefCount) { + return ErrPartitionWrongNoSubpart + } + if subDefCount != 0 { + n.Sub.Num = uint64(subDefCount) + } + } else if subDefCount != 0 { + return ErrSubpartition + } + } + + switch n.Tp { + case model.PartitionTypeHash, model.PartitionTypeKey: + if n.Num == 0 { + n.Num = 1 + } + case model.PartitionTypeRange, model.PartitionTypeList: + if n.Interval == nil && len(n.Definitions) == 0 { + return ErrPartitionsMustBeDefined.GenWithStackByArgs(n.Tp) + } + case model.PartitionTypeSystemTime: + if len(n.Definitions) < 2 { + return ErrSystemVersioningWrongPartitions + } + } + + for _, pd := range n.Definitions { + // ensure the partition definition types match the methods, + // e.g. RANGE partitions only allows VALUES LESS THAN + if err := pd.Clause.Validate(n.Tp, len(n.ColumnNames)); err != nil { + return err + } + } + + return nil +} + +func (n *PartitionOptions) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("PARTITION BY ") + if err := n.PartitionMethod.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PartitionOptions.PartitionMethod") + } + + if n.Num > 0 && len(n.Definitions) == 0 { + ctx.WriteKeyWord(" PARTITIONS ") + ctx.WritePlainf("%d", n.Num) + } + + if n.Sub != nil { + ctx.WriteKeyWord(" SUBPARTITION BY ") + if err := n.Sub.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PartitionOptions.Sub") + } + if n.Sub.Num > 0 { + ctx.WriteKeyWord(" SUBPARTITIONS ") + ctx.WritePlainf("%d", n.Sub.Num) + } + } + + if len(n.Definitions) > 0 { + ctx.WritePlain(" (") + for i, def := range n.Definitions { + if i > 0 { + ctx.WritePlain(",") + } + if err := def.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PartitionOptions.Definitions[%d]", i) + } + } + ctx.WritePlain(")") + } + + return nil +} + +func (n *PartitionOptions) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + n = newNode.(*PartitionOptions) + if !n.PartitionMethod.acceptInPlace(v) { + return n, false + } + if n.Sub != nil && !n.Sub.acceptInPlace(v) { + return n, false + } + for _, def := range n.Definitions { + if !def.acceptInPlace(v) { + return n, false + } + } + return v.Leave(n) +} + +// RecoverTableStmt is a statement to recover dropped table. +type RecoverTableStmt struct { + ddlNode + + JobID int64 + Table *TableName + JobNum int64 +} + +// Restore implements Node interface. +func (n *RecoverTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("RECOVER TABLE ") + if n.JobID != 0 { + ctx.WriteKeyWord("BY JOB ") + ctx.WritePlainf("%d", n.JobID) + } else { + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table") + } + if n.JobNum > 0 { + ctx.WritePlainf(" %d", n.JobNum) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RecoverTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + n = newNode.(*RecoverTableStmt) + if n.Table != nil { + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + } + return v.Leave(n) +} + +// FlashBackToTimestampStmt is a statement to restore the cluster to the specified timestamp +type FlashBackToTimestampStmt struct { + ddlNode + + FlashbackTS ExprNode + Tables []*TableName + DBName model.CIStr +} + +// Restore implements Node interface +func (n *FlashBackToTimestampStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("FLASHBACK ") + if len(n.Tables) != 0 { + ctx.WriteKeyWord("TABLE ") + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropTableStmt.Tables[%d]", index) + } + } + } else if n.DBName.O != "" { + ctx.WriteKeyWord("DATABASE ") + ctx.WriteName(n.DBName.O) + } else { + ctx.WriteKeyWord("CLUSTER") + } + ctx.WriteKeyWord(" TO TIMESTAMP ") + if err := n.FlashbackTS.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing FlashBackToTimestampStmt.FlashbackTS") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *FlashBackToTimestampStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*FlashBackToTimestampStmt) + if len(n.Tables) != 0 { + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + } + node, ok := n.FlashbackTS.Accept(v) + if !ok { + return n, false + } + n.FlashbackTS = node.(ExprNode) + return v.Leave(n) +} + +// FlashBackTableStmt is a statement to restore a dropped/truncate table. +type FlashBackTableStmt struct { + ddlNode + + Table *TableName + NewName string +} + +// Restore implements Node interface. +func (n *FlashBackTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("FLASHBACK TABLE ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table") + } + if len(n.NewName) > 0 { + ctx.WriteKeyWord(" TO ") + ctx.WriteName(n.NewName) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *FlashBackTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + n = newNode.(*FlashBackTableStmt) + if n.Table != nil { + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + } + return v.Leave(n) +} + +type AttributesSpec struct { + node + + Attributes string + Default bool +} + +func (n *AttributesSpec) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ATTRIBUTES") + ctx.WritePlain("=") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + return nil + } + ctx.WriteString(n.Attributes) + return nil +} + +func (n *AttributesSpec) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AttributesSpec) + return v.Leave(n) +} + +type StatsOptionsSpec struct { + node + + StatsOptions string + Default bool +} + +func (n *StatsOptionsSpec) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("STATS_OPTIONS") + ctx.WritePlain("=") + if n.Default { + ctx.WriteKeyWord("DEFAULT") + return nil + } + ctx.WriteString(n.StatsOptions) + return nil +} + +func (n *StatsOptionsSpec) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*StatsOptionsSpec) + return v.Leave(n) +} + +// AlterPlacementPolicyStmt is a statement to alter placement policy option. +type AlterPlacementPolicyStmt struct { + ddlNode + + PolicyName model.CIStr + IfExists bool + PlacementOptions []*PlacementOption +} + +func (n *AlterPlacementPolicyStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasSkipPlacementRuleForRestoreFlag() { + return nil + } + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restorePlacementStmtInSpecialComment(ctx, n) + } + + ctx.WriteKeyWord("ALTER PLACEMENT POLICY ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.PolicyName.O) + for i, option := range n.PlacementOptions { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing AlterPlacementPolicyStmt TableOption: [%v]", i) + } + } + return nil +} + +func (n *AlterPlacementPolicyStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterPlacementPolicyStmt) + return v.Leave(n) +} + +func CheckAppend(ops []*ResourceGroupOption, newOp *ResourceGroupOption) bool { + for _, op := range ops { + if op.Tp == newOp.Tp { + return false + } + } + return true +} + +func CheckRunawayAppend(ops []*ResourceGroupRunawayOption, newOp *ResourceGroupRunawayOption) bool { + for _, op := range ops { + if op.Tp == newOp.Tp { + return false + } + } + return true +} + +func CheckBackgroundAppend(ops []*ResourceGroupBackgroundOption, newOp *ResourceGroupBackgroundOption) bool { + for _, op := range ops { + if op.Type == newOp.Type { + return false + } + } + return true +} + +// AlterResourceGroupStmt is a statement to alter placement policy option. +type AlterResourceGroupStmt struct { + ddlNode + + ResourceGroupName model.CIStr + IfExists bool + ResourceGroupOptionList []*ResourceGroupOption +} + +func (n *AlterResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { + if ctx.Flags.HasTiDBSpecialCommentFlag() { + return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDResourceGroup) + } + + ctx.WriteKeyWord("ALTER RESOURCE GROUP ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + ctx.WriteName(n.ResourceGroupName.O) + for i, option := range n.ResourceGroupOptionList { + if i > 0 { + ctx.WritePlain(",") + } + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing AlterResourceGroupStmt Options: [%v]", i) + } + } + return nil +} + +func (n *AlterResourceGroupStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterResourceGroupStmt) + return v.Leave(n) +} + +// AlterSequenceStmt is a statement to alter sequence option. +type AlterSequenceStmt struct { + ddlNode + + // sequence name + Name *TableName + + IfExists bool + SeqOptions []*SequenceOption +} + +func (n *AlterSequenceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ALTER SEQUENCE ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if err := n.Name.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterSequenceStmt.Table") + } + for i, option := range n.SeqOptions { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing AlterSequenceStmt SequenceOption: [%v]", i) + } + } + return nil +} + +func (n *AlterSequenceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterSequenceStmt) + node, ok := n.Name.Accept(v) + if !ok { + return n, false + } + n.Name = node.(*TableName) + return v.Leave(n) +} + +func restorePlacementStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode) error { + return restoreStmtInSpecialComment(ctx, n, tidb.FeatureIDPlacement) +} + +func restoreStmtInSpecialComment(ctx *format.RestoreCtx, n DDLNode, feature string) error { + origFlags := ctx.Flags + defer func() { + ctx.Flags = origFlags + }() + + ctx.Flags |= format.RestoreTiDBSpecialComment + return ctx.WriteWithSpecialComments(feature, func() error { + ctx.Flags &= ^format.RestoreTiDBSpecialComment + return n.Restore(ctx) + }) +} + +func tableOptionsWithRestoreTTLFlag(flags format.RestoreFlags, options []*TableOption) []*TableOption { + if !flags.HasRestoreWithTTLEnableOff() { + return options + } + + newOptions := make([]*TableOption, 0, len(options)) + for _, opt := range options { + if opt.Tp == TableOptionTTLEnable { + continue + } + + newOptions = append(newOptions, opt) + if opt.Tp == TableOptionTTL { + newOptions = append(newOptions, &TableOption{ + Tp: TableOptionTTLEnable, + BoolValue: false, + }) + } + } + return newOptions +} diff --git a/pkg/parser/ast/ddl_test.go b/pkg/parser/ast/ddl_test.go new file mode 100644 index 0000000000000..9a67c9b796f06 --- /dev/null +++ b/pkg/parser/ast/ddl_test.go @@ -0,0 +1,920 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast_test + +import ( + "testing" + + . "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/stretchr/testify/require" +) + +func TestDDLVisitorCover(t *testing.T) { + ce := &checkExpr{} + constraint := &Constraint{Keys: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, Refer: &ReferenceDef{}, Option: &IndexOption{}} + + alterTableSpec := &AlterTableSpec{Constraint: constraint, Options: []*TableOption{{}}, NewTable: &TableName{}, NewColumns: []*ColumnDef{{Name: &ColumnName{}}}, OldColumnName: &ColumnName{}, Position: &ColumnPosition{RelativeColumn: &ColumnName{}}, AttributesSpec: &AttributesSpec{}} + + stmts := []struct { + node Node + expectedEnterCnt int + expectedLeaveCnt int + }{ + {&CreateDatabaseStmt{}, 0, 0}, + {&AlterDatabaseStmt{}, 0, 0}, + {&DropDatabaseStmt{}, 0, 0}, + {&DropIndexStmt{Table: &TableName{}}, 0, 0}, + {&DropTableStmt{Tables: []*TableName{{}, {}}}, 0, 0}, + {&RenameTableStmt{TableToTables: []*TableToTable{}}, 0, 0}, + {&TruncateTableStmt{Table: &TableName{}}, 0, 0}, + + // TODO: cover children + {&AlterTableStmt{Table: &TableName{}, Specs: []*AlterTableSpec{alterTableSpec}}, 0, 0}, + {&CreateIndexStmt{Table: &TableName{}}, 0, 0}, + {&CreateTableStmt{Table: &TableName{}, ReferTable: &TableName{}}, 0, 0}, + {&CreateViewStmt{ViewName: &TableName{}, Select: &SelectStmt{}}, 0, 0}, + {&AlterTableSpec{}, 0, 0}, + {&ColumnDef{Name: &ColumnName{}, Options: []*ColumnOption{{Expr: ce}}}, 1, 1}, + {&ColumnOption{Expr: ce}, 1, 1}, + {&ColumnPosition{RelativeColumn: &ColumnName{}}, 0, 0}, + {&Constraint{Keys: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, Refer: &ReferenceDef{}, Option: &IndexOption{}}, 0, 0}, + {&IndexPartSpecification{Column: &ColumnName{}}, 0, 0}, + {&ReferenceDef{Table: &TableName{}, IndexPartSpecifications: []*IndexPartSpecification{{Column: &ColumnName{}}, {Column: &ColumnName{}}}, OnDelete: &OnDeleteOpt{}, OnUpdate: &OnUpdateOpt{}}, 0, 0}, + {&AlterTableSpec{NewConstraints: []*Constraint{constraint, constraint}}, 0, 0}, + {&AlterTableSpec{NewConstraints: []*Constraint{constraint}, NewColumns: []*ColumnDef{{Name: &ColumnName{}}}}, 0, 0}, + } + + for _, v := range stmts { + ce.reset() + v.node.Accept(checkVisitor{}) + require.Equal(t, v.expectedEnterCnt, ce.enterCnt) + require.Equal(t, v.expectedLeaveCnt, ce.leaveCnt) + v.node.Accept(visitor1{}) + } +} + +func TestDDLIndexColNameRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"(a + 1)", "(`a`+1)"}, + {"(1 * 1 + (1 + 1))", "(1*1+(1+1))"}, + {"((1 * 1 + (1 + 1)))", "((1*1+(1+1)))"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateIndexStmt).IndexPartSpecifications[0] + } + runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (%s) USING HASH", extractNodeFunc) +} + +func TestDDLIndexExprRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"world", "`world`"}, + {"world(2)", "`world`(2)"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateIndexStmt).IndexPartSpecifications[0] + } + runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (%s) USING HASH", extractNodeFunc) +} + +func TestDDLOnDeleteRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"on delete restrict", "ON DELETE RESTRICT"}, + {"on delete CASCADE", "ON DELETE CASCADE"}, + {"on delete SET NULL", "ON DELETE SET NULL"}, + {"on delete no action", "ON DELETE NO ACTION"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Constraints[1].Refer.OnDelete + } + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s)", extractNodeFunc) + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) on update CASCADE %s)", extractNodeFunc) + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s on update CASCADE)", extractNodeFunc) +} + +func TestDDLOnUpdateRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"ON UPDATE RESTRICT", "ON UPDATE RESTRICT"}, + {"on update CASCADE", "ON UPDATE CASCADE"}, + {"on update SET NULL", "ON UPDATE SET NULL"}, + {"on update no action", "ON UPDATE NO ACTION"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Constraints[1].Refer.OnUpdate + } + runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE %s )", extractNodeFunc) + runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s ON DELETE CASCADE)", extractNodeFunc) + runNodeRestoreTest(t, testCases, "CREATE TABLE child ( id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) REFERENCES parent(id) %s )", extractNodeFunc) +} + +func TestDDLIndexOption(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"key_block_size=16", "KEY_BLOCK_SIZE=16"}, + {"USING HASH", "USING HASH"}, + {"comment 'hello'", "COMMENT 'hello'"}, + {"key_block_size=16 USING HASH", "KEY_BLOCK_SIZE=16 USING HASH"}, + {"USING HASH KEY_BLOCK_SIZE=16", "KEY_BLOCK_SIZE=16 USING HASH"}, + {"USING HASH COMMENT 'foo'", "USING HASH COMMENT 'foo'"}, + {"COMMENT 'foo'", "COMMENT 'foo'"}, + {"key_block_size = 32 using hash comment 'hello'", "KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, + {"key_block_size=32 using btree comment 'hello'", "KEY_BLOCK_SIZE=32 USING BTREE COMMENT 'hello'"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateIndexStmt).IndexOption + } + runNodeRestoreTest(t, testCases, "CREATE INDEX idx ON t (a) %s", extractNodeFunc) +} + +func TestTableToTableRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"t1 to t2", "`t1` TO `t2`"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*RenameTableStmt).TableToTables[0] + } + runNodeRestoreTest(t, testCases, "rename table %s", extractNodeFunc) +} + +func TestDDLReferenceDefRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"REFERENCES parent(id) ON DELETE CASCADE", "REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"REFERENCES parent(id,hello) ON DELETE CASCADE", "REFERENCES `parent`(`id`, `hello`) ON DELETE CASCADE"}, + {"REFERENCES parent(id,hello(12)) ON DELETE CASCADE", "REFERENCES `parent`(`id`, `hello`(12)) ON DELETE CASCADE"}, + {"REFERENCES parent(id(8),hello(12)) ON DELETE CASCADE", "REFERENCES `parent`(`id`(8), `hello`(12)) ON DELETE CASCADE"}, + {"REFERENCES parent(id)", "REFERENCES `parent`(`id`)"}, + {"REFERENCES parent((id+1))", "REFERENCES `parent`((`id`+1))"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Constraints[1].Refer + } + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, INDEX par_ind (parent_id), FOREIGN KEY (parent_id) %s)", extractNodeFunc) +} + +func TestDDLConstraintRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"INDEX par_ind (parent_id)", "INDEX `par_ind`(`parent_id`)"}, + {"INDEX par_ind (parent_id(6))", "INDEX `par_ind`(`parent_id`(6))"}, + {"INDEX expr_ind ((id + parent_id))", "INDEX `expr_ind`((`id`+`parent_id`))"}, + {"INDEX expr_ind ((lower(id)))", "INDEX `expr_ind`((LOWER(`id`)))"}, + {"key par_ind (parent_id)", "INDEX `par_ind`(`parent_id`)"}, + {"key expr_ind ((lower(id)))", "INDEX `expr_ind`((LOWER(`id`)))"}, + {"unique par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, + {"unique key par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, + {"unique index par_ind (parent_id)", "UNIQUE `par_ind`(`parent_id`)"}, + {"unique expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, + {"unique expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, + {"unique key expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, + {"unique key expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, + {"unique index expr_ind ((id + parent_id))", "UNIQUE `expr_ind`((`id`+`parent_id`))"}, + {"unique index expr_ind ((lower(id)))", "UNIQUE `expr_ind`((LOWER(`id`)))"}, + {"fulltext key full_id (parent_id)", "FULLTEXT `full_id`(`parent_id`)"}, + {"fulltext INDEX full_id (parent_id)", "FULLTEXT `full_id`(`parent_id`)"}, + {"fulltext INDEX full_id ((parent_id+1))", "FULLTEXT `full_id`((`parent_id`+1))"}, + {"PRIMARY KEY (id)", "PRIMARY KEY(`id`)"}, + {"PRIMARY KEY (id) key_block_size = 32 using hash comment 'hello'", "PRIMARY KEY(`id`) KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, + {"PRIMARY KEY ((id+1))", "PRIMARY KEY((`id`+1))"}, + {"CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent((id+1)) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`((`id`+1)) ON DELETE CASCADE"}, + {"CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent((id+1)) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`((`id`+1)) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"CONSTRAINT fk_123 FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"CONSTRAINT fk_123 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"CONSTRAINT fk_123 FOREIGN KEY ((parent_id+1),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT `fk_123` FOREIGN KEY ((`parent_id`+1), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"CONSTRAINT fk_123 FOREIGN KEY ((parent_id+1)) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT `fk_123` FOREIGN KEY ((`parent_id`+1)) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"FOREIGN KEY ((parent_id+1),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "CONSTRAINT FOREIGN KEY ((`parent_id`+1), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"FOREIGN KEY ((parent_id+1)) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "CONSTRAINT FOREIGN KEY ((`parent_id`+1)) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Constraints[0] + } + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT, parent_id INT, %s)", extractNodeFunc) + + specialCommentCases := []NodeRestoreTestCase{ + {"PRIMARY KEY (id) CLUSTERED", "PRIMARY KEY(`id`) /*T![clustered_index] CLUSTERED */"}, + {"primary key (id) NONCLUSTERED", "PRIMARY KEY(`id`) /*T![clustered_index] NONCLUSTERED */"}, + {"PRIMARY KEY (id) /*T![clustered_index] CLUSTERED */", "PRIMARY KEY(`id`) /*T![clustered_index] CLUSTERED */"}, + {"primary key (id) /*T![clustered_index] NONCLUSTERED */", "PRIMARY KEY(`id`) /*T![clustered_index] NONCLUSTERED */"}, + } + runNodeRestoreTestWithFlags(t, specialCommentCases, + "CREATE TABLE child (id INT, parent_id INT, %s)", + extractNodeFunc, format.DefaultRestoreFlags|format.RestoreTiDBSpecialComment) +} + +func TestDDLColumnOptionRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"primary key", "PRIMARY KEY"}, + {"not null", "NOT NULL"}, + {"null", "NULL"}, + {"auto_increment", "AUTO_INCREMENT"}, + {"DEFAULT 10", "DEFAULT 10"}, + {"DEFAULT '10'", "DEFAULT _UTF8MB4'10'"}, + {"DEFAULT 'hello'", "DEFAULT _UTF8MB4'hello'"}, + {"DEFAULT 1.1", "DEFAULT 1.1"}, + {"DEFAULT NULL", "DEFAULT NULL"}, + {"DEFAULT ''", "DEFAULT _UTF8MB4''"}, + {"DEFAULT TRUE", "DEFAULT TRUE"}, + {"DEFAULT FALSE", "DEFAULT FALSE"}, + {"UNIQUE KEY", "UNIQUE KEY"}, + {"on update CURRENT_TIMESTAMP", "ON UPDATE CURRENT_TIMESTAMP()"}, + {"comment 'hello'", "COMMENT 'hello'"}, + {"generated always as(id + 1)", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, + {"generated always as(id + 1) virtual", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, + {"generated always as(id + 1) stored", "GENERATED ALWAYS AS(`id`+1) STORED"}, + {"REFERENCES parent(id)", "REFERENCES `parent`(`id`)"}, + {"COLLATE utf8_bin", "COLLATE utf8_bin"}, + {"STORAGE DEFAULT", "STORAGE DEFAULT"}, + {"STORAGE DISK", "STORAGE DISK"}, + {"STORAGE MEMORY", "STORAGE MEMORY"}, + {"AUTO_RANDOM (3)", "AUTO_RANDOM(3)"}, + {"AUTO_RANDOM", "AUTO_RANDOM"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Cols[0].Options[0] + } + runNodeRestoreTest(t, testCases, "CREATE TABLE child (id INT %s)", extractNodeFunc) +} + +func TestGeneratedRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"generated always as(id + 1)", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, + {"generated always as(id + 1) virtual", "GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, + {"generated always as(id + 1) stored", "GENERATED ALWAYS AS(`id`+1) STORED"}, + {"generated always as(lower(id)) stored", "GENERATED ALWAYS AS(LOWER(`id`)) STORED"}, + {"generated always as(lower(child.id)) stored", "GENERATED ALWAYS AS(LOWER(`id`)) STORED"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Cols[0].Options[0] + } + runNodeRestoreTestWithFlagsStmtChange(t, testCases, "CREATE TABLE child (id INT %s)", extractNodeFunc, + format.DefaultRestoreFlags|format.RestoreWithoutSchemaName|format.RestoreWithoutTableName) +} + +func TestDDLColumnDefRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + // for type + {"id json", "`id` JSON"}, + {"id time(5)", "`id` TIME(5)"}, + {"id int(5) unsigned", "`id` INT(5) UNSIGNED"}, + {"id int(5) UNSIGNED ZEROFILL", "`id` INT(5) UNSIGNED ZEROFILL"}, + {"id float(12,3)", "`id` FLOAT(12,3)"}, + {"id float", "`id` FLOAT"}, + {"id double(22,3)", "`id` DOUBLE(22,3)"}, + {"id double", "`id` DOUBLE"}, + {"id tinyint(4)", "`id` TINYINT(4)"}, + {"id smallint(6)", "`id` SMALLINT(6)"}, + {"id mediumint(9)", "`id` MEDIUMINT(9)"}, + {"id integer(11)", "`id` INT(11)"}, + {"id bigint(20)", "`id` BIGINT(20)"}, + {"id DATE", "`id` DATE"}, + {"id DATETIME", "`id` DATETIME"}, + {"id DECIMAL(4,2)", "`id` DECIMAL(4,2)"}, + {"id char(1)", "`id` CHAR(1)"}, + {"id varchar(10) BINARY", "`id` VARCHAR(10) BINARY"}, + {"id binary(1)", "`id` BINARY(1)"}, + {"id timestamp(2)", "`id` TIMESTAMP(2)"}, + {"id timestamp", "`id` TIMESTAMP"}, + {"id datetime(2)", "`id` DATETIME(2)"}, + {"id date", "`id` DATE"}, + {"id year", "`id` YEAR"}, + {"id INT", "`id` INT"}, + {"id INT NULL", "`id` INT NULL"}, + {"id enum('a','b')", "`id` ENUM('a','b')"}, + {"id enum('''a''','''b''')", "`id` ENUM('''a''','''b''')"}, + {"id enum('a\\nb','a\\tb','a\\rb')", "`id` ENUM('a\nb','a\tb','a\rb')"}, + {"id enum('a','b') binary", "`id` ENUM('a','b') BINARY"}, + {"id enum(0x61, 0b01100010)", "`id` ENUM('a','b')"}, + {"id set('a','b')", "`id` SET('a','b')"}, + {"id set('''a''','''b''')", "`id` SET('''a''','''b''')"}, + {"id set('a\\nb','a'' \\r\\nb','a\\rb')", "`id` SET('a\nb','a'' \r\nb','a\rb')"}, + {`id set("a'\nb","a'b\tc")`, "`id` SET('a''\nb','a''b\tc')"}, + {"id set('a','b') binary", "`id` SET('a','b') BINARY"}, + {"id set(0x61, 0b01100010)", "`id` SET('a','b')"}, + {"id TEXT CHARACTER SET UTF8 COLLATE UTF8_UNICODE_CI", "`id` TEXT CHARACTER SET UTF8 COLLATE utf8_unicode_ci"}, + {"id text character set UTF8", "`id` TEXT CHARACTER SET UTF8"}, + {"id text charset UTF8", "`id` TEXT CHARACTER SET UTF8"}, + {"id varchar(50) collate UTF8MB4_CZECH_CI", "`id` VARCHAR(50) COLLATE utf8mb4_czech_ci"}, + {"id varchar(50) collate utf8_bin", "`id` VARCHAR(50) COLLATE utf8_bin"}, + {"id varchar(50) collate utf8_unicode_ci collate utf8mb4_bin", "`id` VARCHAR(50) COLLATE utf8_unicode_ci COLLATE utf8mb4_bin"}, + {"c1 char(10) character set LATIN1 collate latin1_german1_ci", "`c1` CHAR(10) CHARACTER SET LATIN1 COLLATE latin1_german1_ci"}, + + {"id int(11) PRIMARY KEY", "`id` INT(11) PRIMARY KEY"}, + {"id int(11) NOT NULL", "`id` INT(11) NOT NULL"}, + {"id INT(11) NULL", "`id` INT(11) NULL"}, + {"id INT(11) auto_increment", "`id` INT(11) AUTO_INCREMENT"}, + {"id INT(11) DEFAULT 10", "`id` INT(11) DEFAULT 10"}, + {"id INT(11) DEFAULT '10'", "`id` INT(11) DEFAULT _UTF8MB4'10'"}, + {"id INT(11) DEFAULT 1.1", "`id` INT(11) DEFAULT 1.1"}, + {"id INT(11) UNIQUE KEY", "`id` INT(11) UNIQUE KEY"}, + {"id INT(11) COLLATE ascii_bin", "`id` INT(11) COLLATE ascii_bin"}, + {"id INT(11) collate ascii_bin collate utf8_bin", "`id` INT(11) COLLATE ascii_bin COLLATE utf8_bin"}, + {"id INT(11) on update CURRENT_TIMESTAMP", "`id` INT(11) ON UPDATE CURRENT_TIMESTAMP()"}, + {"id INT(11) comment 'hello'", "`id` INT(11) COMMENT 'hello'"}, + {"id INT(11) generated always as(id + 1)", "`id` INT(11) GENERATED ALWAYS AS(`id`+1) VIRTUAL"}, + {"id INT(11) REFERENCES parent(id)", "`id` INT(11) REFERENCES `parent`(`id`)"}, + + {"id bit", "`id` BIT(1)"}, + {"id bit(1)", "`id` BIT(1)"}, + {"id bit(64)", "`id` BIT(64)"}, + {"id tinyint", "`id` TINYINT"}, + {"id tinyint(255)", "`id` TINYINT(255)"}, + {"id bool", "`id` TINYINT(1)"}, + {"id boolean", "`id` TINYINT(1)"}, + {"id smallint", "`id` SMALLINT"}, + {"id smallint(255)", "`id` SMALLINT(255)"}, + {"id mediumint", "`id` MEDIUMINT"}, + {"id mediumint(255)", "`id` MEDIUMINT(255)"}, + {"id int", "`id` INT"}, + {"id int(255)", "`id` INT(255)"}, + {"id integer", "`id` INT"}, + {"id integer(255)", "`id` INT(255)"}, + {"id bigint", "`id` BIGINT"}, + {"id bigint(255)", "`id` BIGINT(255)"}, + {"id decimal", "`id` DECIMAL"}, + {"id decimal(10)", "`id` DECIMAL(10)"}, + {"id decimal(10,0)", "`id` DECIMAL(10,0)"}, + {"id decimal(65)", "`id` DECIMAL(65)"}, + {"id decimal(65,30)", "`id` DECIMAL(65,30)"}, + {"id dec(10,0)", "`id` DECIMAL(10,0)"}, + {"id numeric(10,0)", "`id` DECIMAL(10,0)"}, + {"id float(0)", "`id` FLOAT"}, + {"id float(24)", "`id` FLOAT"}, + {"id float(25)", "`id` DOUBLE"}, + {"id float(53)", "`id` DOUBLE"}, + {"id float(7,0)", "`id` FLOAT(7,0)"}, + {"id float(25,0)", "`id` FLOAT(25,0)"}, + {"id double(15,0)", "`id` DOUBLE(15,0)"}, + {"id double precision(15,0)", "`id` DOUBLE(15,0)"}, + {"id real(15,0)", "`id` DOUBLE(15,0)"}, + {"id year(4)", "`id` YEAR(4)"}, + {"id time", "`id` TIME"}, + {"id char", "`id` CHAR"}, + {"id char(0)", "`id` CHAR(0)"}, + {"id char(255)", "`id` CHAR(255)"}, + {"id national char(0)", "`id` CHAR(0)"}, + {"id binary", "`id` BINARY"}, + {"id varbinary(0)", "`id` VARBINARY(0)"}, + {"id varbinary(65535)", "`id` VARBINARY(65535)"}, + {"id tinyblob", "`id` TINYBLOB"}, + {"id tinytext", "`id` TINYTEXT"}, + {"id blob", "`id` BLOB"}, + {"id blob(0)", "`id` BLOB(0)"}, + {"id blob(65535)", "`id` BLOB(65535)"}, + {"id text(0)", "`id` TEXT(0)"}, + {"id text(65535)", "`id` TEXT(65535)"}, + {"id mediumblob", "`id` MEDIUMBLOB"}, + {"id mediumtext", "`id` MEDIUMTEXT"}, + {"id longblob", "`id` LONGBLOB"}, + {"id longtext", "`id` LONGTEXT"}, + {"id json", "`id` JSON"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*CreateTableStmt).Cols[0] + } + runNodeRestoreTest(t, testCases, "CREATE TABLE t (%s)", extractNodeFunc) +} + +func TestDDLTruncateTableStmtRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"truncate t1", "TRUNCATE TABLE `t1`"}, + {"truncate table t1", "TRUNCATE TABLE `t1`"}, + {"truncate a.t1", "TRUNCATE TABLE `a`.`t1`"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*TruncateTableStmt) + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestDDLDropTableStmtRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"drop table t1", "DROP TABLE `t1`"}, + {"drop table if exists t1", "DROP TABLE IF EXISTS `t1`"}, + {"drop temporary table t1", "DROP TEMPORARY TABLE `t1`"}, + {"drop temporary table if exists t1", "DROP TEMPORARY TABLE IF EXISTS `t1`"}, + {"DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `test`", "DROP TEMPORARY TABLE IF EXISTS `test`"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*DropTableStmt) + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestColumnPositionRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"", ""}, + {"first", "FIRST"}, + {"after b", "AFTER `b`"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*AlterTableStmt).Specs[0].Position + } + runNodeRestoreTest(t, testCases, "alter table t add column a varchar(255) %s", extractNodeFunc) +} + +func TestAlterTableSpecRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"ENGINE innodb", "ENGINE = innodb"}, + {"ENGINE = innodb", "ENGINE = innodb"}, + {"ENGINE = 'innodb'", "ENGINE = innodb"}, + {"ENGINE tokudb", "ENGINE = tokudb"}, + {"ENGINE = tokudb", "ENGINE = tokudb"}, + {"ENGINE = 'tokudb'", "ENGINE = tokudb"}, + {"DEFAULT CHARACTER SET utf8", "DEFAULT CHARACTER SET = UTF8"}, + {"DEFAULT CHARACTER SET = utf8", "DEFAULT CHARACTER SET = UTF8"}, + {"DEFAULT CHARSET utf8", "DEFAULT CHARACTER SET = UTF8"}, + {"DEFAULT CHARSET = utf8", "DEFAULT CHARACTER SET = UTF8"}, + {"DEFAULT COLLATE utf8_bin", "DEFAULT COLLATE = UTF8_BIN"}, + {"DEFAULT COLLATE = utf8_bin", "DEFAULT COLLATE = UTF8_BIN"}, + {"AUTO_INCREMENT 3", "AUTO_INCREMENT = 3"}, + {"AUTO_INCREMENT = 6", "AUTO_INCREMENT = 6"}, + {"COMMENT ''", "COMMENT = ''"}, + {"COMMENT 'system role'", "COMMENT = 'system role'"}, + {"COMMENT = 'system role'", "COMMENT = 'system role'"}, + {"AVG_ROW_LENGTH 12", "AVG_ROW_LENGTH = 12"}, + {"AVG_ROW_LENGTH = 6", "AVG_ROW_LENGTH = 6"}, + {"connection 'abc'", "CONNECTION = 'abc'"}, + {"CONNECTION = 'abc'", "CONNECTION = 'abc'"}, + {"checksum 1", "CHECKSUM = 1"}, + {"checksum = 0", "CHECKSUM = 0"}, + {"PASSWORD '123456'", "PASSWORD = '123456'"}, + {"PASSWORD = ''", "PASSWORD = ''"}, + {"compression 'NONE'", "COMPRESSION = 'NONE'"}, + {"compression = 'lz4'", "COMPRESSION = 'lz4'"}, + {"key_block_size 1024", "KEY_BLOCK_SIZE = 1024"}, + {"KEY_BLOCK_SIZE = 1024", "KEY_BLOCK_SIZE = 1024"}, + {"max_rows 1000", "MAX_ROWS = 1000"}, + {"max_rows = 1000", "MAX_ROWS = 1000"}, + {"min_rows 1000", "MIN_ROWS = 1000"}, + {"MIN_ROWS = 1000", "MIN_ROWS = 1000"}, + {"DELAY_KEY_WRITE 1", "DELAY_KEY_WRITE = 1"}, + {"DELAY_KEY_WRITE = 1000", "DELAY_KEY_WRITE = 1000"}, + {"ROW_FORMAT default", "ROW_FORMAT = DEFAULT"}, + {"ROW_FORMAT = default", "ROW_FORMAT = DEFAULT"}, + {"ROW_FORMAT = fixed", "ROW_FORMAT = FIXED"}, + {"ROW_FORMAT = compressed", "ROW_FORMAT = COMPRESSED"}, + {"ROW_FORMAT = compact", "ROW_FORMAT = COMPACT"}, + {"ROW_FORMAT = redundant", "ROW_FORMAT = REDUNDANT"}, + {"ROW_FORMAT = dynamic", "ROW_FORMAT = DYNAMIC"}, + {"ROW_FORMAT tokudb_default", "ROW_FORMAT = TOKUDB_DEFAULT"}, + {"ROW_FORMAT = tokudb_default", "ROW_FORMAT = TOKUDB_DEFAULT"}, + {"ROW_FORMAT = tokudb_fast", "ROW_FORMAT = TOKUDB_FAST"}, + {"ROW_FORMAT = tokudb_small", "ROW_FORMAT = TOKUDB_SMALL"}, + {"ROW_FORMAT = tokudb_zlib", "ROW_FORMAT = TOKUDB_ZLIB"}, + {"ROW_FORMAT = tokudb_zstd", "ROW_FORMAT = TOKUDB_ZSTD"}, + {"ROW_FORMAT = tokudb_quicklz", "ROW_FORMAT = TOKUDB_QUICKLZ"}, + {"ROW_FORMAT = tokudb_lzma", "ROW_FORMAT = TOKUDB_LZMA"}, + {"ROW_FORMAT = tokudb_snappy", "ROW_FORMAT = TOKUDB_SNAPPY"}, + {"ROW_FORMAT = tokudb_uncompressed", "ROW_FORMAT = TOKUDB_UNCOMPRESSED"}, + {"shard_row_id_bits 1", "SHARD_ROW_ID_BITS = 1"}, + {"shard_row_id_bits = 1", "SHARD_ROW_ID_BITS = 1"}, + {"CONVERT TO CHARACTER SET utf8", "CONVERT TO CHARACTER SET UTF8"}, + {"CONVERT TO CHARSET utf8", "CONVERT TO CHARACTER SET UTF8"}, + {"CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin", "CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, + {"CONVERT TO CHARSET utf8 COLLATE utf8_bin", "CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, + {"ADD COLUMN (a SMALLINT UNSIGNED)", "ADD COLUMN (`a` SMALLINT UNSIGNED)"}, + {"ADD COLUMN (a SMALLINT UNSIGNED, b varchar(255))", "ADD COLUMN (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, + {"ADD COLUMN a SMALLINT UNSIGNED", "ADD COLUMN `a` SMALLINT UNSIGNED"}, + {"ADD COLUMN a SMALLINT UNSIGNED FIRST", "ADD COLUMN `a` SMALLINT UNSIGNED FIRST"}, + {"ADD COLUMN a SMALLINT UNSIGNED AFTER b", "ADD COLUMN `a` SMALLINT UNSIGNED AFTER `b`"}, + {"ADD COLUMN name mediumtext CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL", "ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL"}, + {"ADD CONSTRAINT INDEX par_ind (parent_id)", "ADD INDEX `par_ind`(`parent_id`)"}, + {"ADD CONSTRAINT INDEX par_ind (parent_id(6))", "ADD INDEX `par_ind`(`parent_id`(6))"}, + {"ADD CONSTRAINT key par_ind (parent_id)", "ADD INDEX `par_ind`(`parent_id`)"}, + {"ADD CONSTRAINT unique par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, + {"ADD CONSTRAINT unique key par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, + {"ADD CONSTRAINT unique index par_ind (parent_id)", "ADD UNIQUE `par_ind`(`parent_id`)"}, + {"ADD CONSTRAINT fulltext key full_id (parent_id)", "ADD FULLTEXT `full_id`(`parent_id`)"}, + {"ADD CONSTRAINT fulltext INDEX full_id (parent_id)", "ADD FULLTEXT `full_id`(`parent_id`)"}, + {"ADD CONSTRAINT PRIMARY KEY (id)", "ADD PRIMARY KEY(`id`)"}, + {"ADD CONSTRAINT PRIMARY KEY (id) key_block_size = 32 using hash comment 'hello'", "ADD PRIMARY KEY(`id`) KEY_BLOCK_SIZE=32 USING HASH COMMENT 'hello'"}, + {"ADD CONSTRAINT FOREIGN KEY (parent_id(2),hello(4)) REFERENCES parent(id) ON DELETE CASCADE", "ADD CONSTRAINT FOREIGN KEY (`parent_id`(2), `hello`(4)) REFERENCES `parent`(`id`) ON DELETE CASCADE"}, + {"ADD CONSTRAINT FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "ADD CONSTRAINT FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"ADD CONSTRAINT fk_123 FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE ON UPDATE RESTRICT", "ADD CONSTRAINT `fk_123` FOREIGN KEY (`parent_id`) REFERENCES `parent`(`id`) ON DELETE CASCADE ON UPDATE RESTRICT"}, + {"DROP COLUMN a", "DROP COLUMN `a`"}, + {"DROP COLUMN a RESTRICT", "DROP COLUMN `a`"}, + {"DROP COLUMN a CASCADE", "DROP COLUMN `a`"}, + {"DROP PRIMARY KEY", "DROP PRIMARY KEY"}, + {"drop index a", "DROP INDEX `a`"}, + {"drop key a", "DROP INDEX `a`"}, + {"drop FOREIGN key a", "DROP FOREIGN KEY `a`"}, + {"MODIFY column a varchar(255)", "MODIFY COLUMN `a` VARCHAR(255)"}, + {"modify COLUMN a varchar(255) FIRST", "MODIFY COLUMN `a` VARCHAR(255) FIRST"}, + {"modify COLUMN a varchar(255) AFTER b", "MODIFY COLUMN `a` VARCHAR(255) AFTER `b`"}, + {"change column a b VARCHAR(255)", "CHANGE COLUMN `a` `b` VARCHAR(255)"}, + {"change COLUMN a b varchar(255) CHARACTER SET UTF8 BINARY", "CHANGE COLUMN `a` `b` VARCHAR(255) BINARY CHARACTER SET UTF8"}, + {"CHANGE column a b varchar(255) FIRST", "CHANGE COLUMN `a` `b` VARCHAR(255) FIRST"}, + {"change COLUMN a b varchar(255) AFTER c", "CHANGE COLUMN `a` `b` VARCHAR(255) AFTER `c`"}, + {"RENAME db1.t1", "RENAME AS `db1`.`t1`"}, + {"RENAME to db1.t1", "RENAME AS `db1`.`t1`"}, + {"RENAME as t1", "RENAME AS `t1`"}, + {"ALTER a SET DEFAULT 1", "ALTER COLUMN `a` SET DEFAULT 1"}, + {"ALTER a DROP DEFAULT", "ALTER COLUMN `a` DROP DEFAULT"}, + {"ALTER COLUMN a SET DEFAULT 1", "ALTER COLUMN `a` SET DEFAULT 1"}, + {"ALTER COLUMN a DROP DEFAULT", "ALTER COLUMN `a` DROP DEFAULT"}, + {"LOCK=NONE", "LOCK = NONE"}, + {"LOCK=DEFAULT", "LOCK = DEFAULT"}, + {"LOCK=SHARED", "LOCK = SHARED"}, + {"LOCK=EXCLUSIVE", "LOCK = EXCLUSIVE"}, + {"RENAME KEY a TO b", "RENAME INDEX `a` TO `b`"}, + {"RENAME INDEX a TO b", "RENAME INDEX `a` TO `b`"}, + {"ADD PARTITION", "ADD PARTITION"}, + {"ADD PARTITION ( PARTITION P1 VALUES LESS THAN (2010))", "ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010))"}, + {"ADD PARTITION ( PARTITION P2 VALUES LESS THAN MAXVALUE)", "ADD PARTITION (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, + {"ADD PARTITION (\nPARTITION P1 VALUES LESS THAN (2010),\nPARTITION P2 VALUES LESS THAN (2015),\nPARTITION P3 VALUES LESS THAN MAXVALUE)", "ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010), PARTITION `P2` VALUES LESS THAN (2015), PARTITION `P3` VALUES LESS THAN (MAXVALUE))"}, + {"ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT 'AP_START \\' AP_END')", "ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'AP_START '' AP_END')"}, + {"ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')", "ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')"}, + {"coalesce partition 3", "COALESCE PARTITION 3"}, + {"drop partition p1", "DROP PARTITION `p1`"}, + {"TRUNCATE PARTITION p0", "TRUNCATE PARTITION `p0`"}, + {"add stats_extended s1 cardinality(a,b)", "ADD STATS_EXTENDED `s1` CARDINALITY(`a`, `b`)"}, + {"add stats_extended if not exists s1 cardinality(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` CARDINALITY(`a`, `b`)"}, + {"add stats_extended s1 correlation(a,b)", "ADD STATS_EXTENDED `s1` CORRELATION(`a`, `b`)"}, + {"add stats_extended if not exists s1 correlation(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` CORRELATION(`a`, `b`)"}, + {"add stats_extended s1 dependency(a,b)", "ADD STATS_EXTENDED `s1` DEPENDENCY(`a`, `b`)"}, + {"add stats_extended if not exists s1 dependency(a,b)", "ADD STATS_EXTENDED IF NOT EXISTS `s1` DEPENDENCY(`a`, `b`)"}, + {"drop stats_extended s1", "DROP STATS_EXTENDED `s1`"}, + {"drop stats_extended if exists s1", "DROP STATS_EXTENDED IF EXISTS `s1`"}, + {"placement policy p1", "PLACEMENT POLICY = `p1`"}, + {"placement policy p1 comment='aaa'", "PLACEMENT POLICY = `p1` COMMENT = 'aaa'"}, + {"partition p0 placement policy p1", "PARTITION `p0` PLACEMENT POLICY = `p1`"}, + } + extractNodeFunc := func(node Node) Node { + return node.(*AlterTableStmt).Specs[0] + } + runNodeRestoreTest(t, testCases, "ALTER TABLE t %s", extractNodeFunc) +} + +func TestAlterTableWithSpecialCommentRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"placement policy p1", "/*T![placement] PLACEMENT POLICY = `p1` */"}, + {"placement policy p1 comment='aaa'", "/*T![placement] PLACEMENT POLICY = `p1` */ COMMENT = 'aaa'"}, + {"partition p0 placement policy p1", "/*T![placement] PARTITION `p0` PLACEMENT POLICY = `p1` */"}, + } + + extractNodeFunc := func(node Node) Node { + return node.(*AlterTableStmt).Specs[0] + } + runNodeRestoreTestWithFlags(t, testCases, "ALTER TABLE t %s", extractNodeFunc, format.DefaultRestoreFlags|format.RestoreTiDBSpecialComment) +} + +func TestAlterTableOptionRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"ALTER TABLE t ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8", "ALTER TABLE `t` ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8"}, + {"ALTER TABLE t ROW_FORMAT = COMPRESSED, KEY_BLOCK_SIZE = 8", "ALTER TABLE `t` ROW_FORMAT = COMPRESSED, KEY_BLOCK_SIZE = 8"}, + } + extractNodeFunc := func(node Node) Node { + return node + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestAdminRepairTableRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"ADMIN REPAIR TABLE t CREATE TABLE t (a int)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` INT)"}, + {"ADMIN REPAIR TABLE t CREATE TABLE t (a char(1), b int)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` CHAR(1),`b` INT)"}, + {"ADMIN REPAIR TABLE t CREATE TABLE t (a TINYINT UNSIGNED)", "ADMIN REPAIR TABLE `t` CREATE TABLE `t` (`a` TINYINT UNSIGNED)"}, + } + extractNodeFunc := func(node Node) Node { + return node + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestSequenceRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"create sequence seq", "CREATE SEQUENCE `seq`"}, + {"create sequence if not exists seq", "CREATE SEQUENCE IF NOT EXISTS `seq`"}, + {"create sequence if not exists seq", "CREATE SEQUENCE IF NOT EXISTS `seq`"}, + {"create sequence if not exists seq increment 1", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, + {"create sequence if not exists seq increment = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, + {"create sequence if not exists seq minvalue 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, + {"create sequence if not exists seq minvalue = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, + {"create sequence if not exists seq nominvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, + {"create sequence if not exists seq no minvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, + {"create sequence if not exists seq maxvalue 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, + {"create sequence if not exists seq maxvalue = 1", "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, + {"create sequence if not exists seq nomaxvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, + {"create sequence if not exists seq no maxvalue", "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, + {"create sequence if not exists seq start 1", "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, + {"create sequence if not exists seq start with 1", "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, + {"create sequence if not exists seq cache 1", "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, + {"create sequence if not exists seq nocache", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, + {"create sequence if not exists seq no cache", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, + {"create sequence if not exists seq cycle", "CREATE SEQUENCE IF NOT EXISTS `seq` CYCLE"}, + {"create sequence if not exists seq nocycle", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, + {"create sequence if not exists seq no cycle", "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, + {"create sequence seq increment 1 minvalue 0 maxvalue 1000", "CREATE SEQUENCE `seq` INCREMENT BY 1 MINVALUE 0 MAXVALUE 1000"}, + {"create sequence seq minvalue 0 maxvalue 1000 increment 1", "CREATE SEQUENCE `seq` MINVALUE 0 MAXVALUE 1000 INCREMENT BY 1"}, + {"create sequence seq cache = 1 minvalue 0 maxvalue -1000", "CREATE SEQUENCE `seq` CACHE 1 MINVALUE 0 MAXVALUE -1000"}, + {"create sequence seq increment -1 minvalue 0 maxvalue -1000", "CREATE SEQUENCE `seq` INCREMENT BY -1 MINVALUE 0 MAXVALUE -1000"}, + {"create sequence seq nocycle nocache maxvalue 1000 cache 1", "CREATE SEQUENCE `seq` NOCYCLE NOCACHE MAXVALUE 1000 CACHE 1"}, + {"create sequence seq increment -1 no minvalue no maxvalue cache = 1", "CREATE SEQUENCE `seq` INCREMENT BY -1 NO MINVALUE NO MAXVALUE CACHE 1"}, + {"create sequence if not exists seq increment 1 minvalue 0 nomaxvalue cache 100 nocycle", "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1 MINVALUE 0 NO MAXVALUE CACHE 100 NOCYCLE"}, + + // test drop sequence + {"drop sequence seq", "DROP SEQUENCE `seq`"}, + {"drop sequence seq, seq2", "DROP SEQUENCE `seq`, `seq2`"}, + {"drop sequence if exists seq, seq2", "DROP SEQUENCE IF EXISTS `seq`, `seq2`"}, + {"drop sequence if exists seq", "DROP SEQUENCE IF EXISTS `seq`"}, + {"drop sequence sequence", "DROP SEQUENCE `sequence`"}, + } + extractNodeFunc := func(node Node) Node { + return node + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestDropIndexRestore(t *testing.T) { + sourceSQL := "drop index if exists idx on t" + cases := []struct { + flags format.RestoreFlags + expectSQL string + }{ + {format.DefaultRestoreFlags, "DROP INDEX IF EXISTS `idx` ON `t`"}, + {format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "DROP INDEX /*T! IF EXISTS */`idx` ON `t`"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestAlterDatabaseRestore(t *testing.T) { + sourceSQL1 := "alter database db1 charset='ascii'" + sourceSQL2 := "alter database db1 collate='ascii_bin'" + sourceSQL3 := "alter database db1 placement policy p1" + sourceSQL4 := "alter database db1 placement policy p1 charset='ascii'" + + cases := []struct { + sourceSQL string + flags format.RestoreFlags + expectSQL string + }{ + {sourceSQL1, format.DefaultRestoreFlags, "ALTER DATABASE `db1` CHARACTER SET = ascii"}, + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` CHARACTER SET = ascii"}, + {sourceSQL2, format.DefaultRestoreFlags, "ALTER DATABASE `db1` COLLATE = ascii_bin"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` COLLATE = ascii_bin"}, + {sourceSQL3, format.DefaultRestoreFlags, "ALTER DATABASE `db1` PLACEMENT POLICY = `p1`"}, + {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */"}, + {sourceSQL4, format.DefaultRestoreFlags, "ALTER DATABASE `db1` PLACEMENT POLICY = `p1` CHARACTER SET = ascii"}, + {sourceSQL4, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER DATABASE `db1` /*T![placement] PLACEMENT POLICY = `p1` */ CHARACTER SET = ascii"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestCreatePlacementPolicyRestore(t *testing.T) { + sourceSQL1 := "create placement policy p1 primary_region=\"r1\" regions='r1,r2' followers=1" + sourceSQL2 := "create placement policy if not exists p1 primary_region=\"r1\" regions='r1,r2' followers=1" + sourceSQL3 := "create or replace placement policy p1 followers=1" + cases := []struct { + sourceSQL string + flags format.RestoreFlags + expectSQL string + }{ + {sourceSQL1, format.DefaultRestoreFlags, "CREATE PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, + {sourceSQL2, format.DefaultRestoreFlags, "CREATE PLACEMENT POLICY IF NOT EXISTS `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE PLACEMENT POLICY IF NOT EXISTS `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, + {sourceSQL3, format.DefaultRestoreFlags, "CREATE OR REPLACE PLACEMENT POLICY `p1` FOLLOWERS = 1"}, + {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] CREATE OR REPLACE PLACEMENT POLICY `p1` FOLLOWERS = 1 */"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestAlterPlacementPolicyRestore(t *testing.T) { + sourceSQL := "alter placement policy p1 primary_region=\"r1\" regions='r1,r2' followers=1" + cases := []struct { + flags format.RestoreFlags + expectSQL string + }{ + {format.DefaultRestoreFlags, "ALTER PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1"}, + {format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] ALTER PLACEMENT POLICY `p1` PRIMARY_REGION = 'r1' REGIONS = 'r1,r2' FOLLOWERS = 1 */"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestDropPlacementPolicyRestore(t *testing.T) { + sourceSQL1 := "drop placement policy p1" + sourceSQL2 := "drop placement policy if exists p1" + cases := []struct { + sourceSQL string + flags format.RestoreFlags + expectSQL string + }{ + {sourceSQL1, format.DefaultRestoreFlags, "DROP PLACEMENT POLICY `p1`"}, + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] DROP PLACEMENT POLICY `p1` */"}, + {sourceSQL2, format.DefaultRestoreFlags, "DROP PLACEMENT POLICY IF EXISTS `p1`"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "/*T![placement] DROP PLACEMENT POLICY IF EXISTS `p1` */"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestRemovePlacementRestore(t *testing.T) { + f := format.DefaultRestoreFlags | format.SkipPlacementRuleForRestore + cases := []struct { + sourceSQL string + expectSQL string + }{ + { + "CREATE TABLE t1 (id BIGINT NOT NULL PRIMARY KEY auto_increment, b varchar(255)) PLACEMENT POLICY=placement1;", + "CREATE TABLE `t1` (`id` BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,`b` VARCHAR(255)) ", + }, + { + "CREATE TABLE `t1` (\n `a` int(11) DEFAULT NULL\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin /*T![placement] PLACEMENT POLICY=`p2` */", + "CREATE TABLE `t1` (`a` INT(11) DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = UTF8MB4 DEFAULT COLLATE = UTF8MB4_BIN ", + }, + { + "CREATE TABLE t4 (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL) PARTITION BY RANGE( YEAR(joined) ) (PARTITION p0 VALUES LESS THAN (1960) PLACEMENT POLICY=p1,PARTITION p1 VALUES LESS THAN (1970),PARTITION p2 VALUES LESS THAN (1980),PARTITION p3 VALUES LESS THAN (1990),PARTITION p4 VALUES LESS THAN MAXVALUE);", + "CREATE TABLE `t4` (`firstname` VARCHAR(25) NOT NULL,`lastname` VARCHAR(25) NOT NULL,`username` VARCHAR(16) NOT NULL,`email` VARCHAR(35),`joined` DATE NOT NULL) PARTITION BY RANGE (YEAR(`joined`)) (PARTITION `p0` VALUES LESS THAN (1960) ,PARTITION `p1` VALUES LESS THAN (1970),PARTITION `p2` VALUES LESS THAN (1980),PARTITION `p3` VALUES LESS THAN (1990),PARTITION `p4` VALUES LESS THAN (MAXVALUE))", + }, + { + "ALTER TABLE t3 PLACEMENT POLICY=DEFAULT;", + "ALTER TABLE `t3`", + }, + { + "ALTER TABLE t1 PLACEMENT POLICY=p10", + "ALTER TABLE `t1`", + }, + { + "ALTER TABLE t1 PLACEMENT POLICY=p10, add d text(50)", + "ALTER TABLE `t1` ADD COLUMN `d` TEXT(50)", + }, + { + "alter table tp PARTITION p1 placement policy p2", + "", + }, + { + "alter table t add d text(50) PARTITION p1 placement policy p2", + "ALTER TABLE `t` ADD COLUMN `d` TEXT(50)", + }, + { + "alter table tp set tiflash replica 1 PARTITION p1 placement policy p2", + "ALTER TABLE `tp` SET TIFLASH REPLICA 1", + }, + { + "ALTER DATABASE TestResetPlacementDB PLACEMENT POLICY SET DEFAULT", + "", + }, + + { + "ALTER DATABASE TestResetPlacementDB PLACEMENT POLICY p1 charset utf8mb4", + "ALTER DATABASE `TestResetPlacementDB` CHARACTER SET = utf8mb4", + }, + { + "/*T![placement] ALTER DATABASE `db1` PLACEMENT POLICY = `p1` */", + "", + }, + { + "ALTER PLACEMENT POLICY p3 PRIMARY_REGION='us-east-1' REGIONS='us-east-1,us-east-2,us-west-1';", + "", + }, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlagsStmtChange(t, testCases, "%s", extractNodeFunc, f) + } +} + +func TestFlashBackDatabaseRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"flashback database M", "FLASHBACK DATABASE `M`"}, + {"flashback schema M", "FLASHBACK DATABASE `M`"}, + {"flashback database M to n", "FLASHBACK DATABASE `M` TO `n`"}, + {"flashback schema M to N", "FLASHBACK DATABASE `M` TO `N`"}, + } + extractNodeFunc := func(node Node) Node { + return node + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestTableOptionTTLRestore(t *testing.T) { + sourceSQL1 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR" + sourceSQL2 := "alter table t ttl_enable = 'OFF'" + sourceSQL3 := "alter table t remove ttl" + cases := []struct { + sourceSQL string + flags format.RestoreFlags + expectSQL string + }{ + {sourceSQL1, format.DefaultRestoreFlags, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR"}, + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */"}, + {sourceSQL2, format.DefaultRestoreFlags, "ALTER TABLE `t` TTL_ENABLE = 'OFF'"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER TABLE `t` /*T![ttl] TTL_ENABLE = 'OFF' */"}, + {sourceSQL3, format.DefaultRestoreFlags, "ALTER TABLE `t` REMOVE TTL"}, + {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment, "ALTER TABLE `t` /*T![ttl] REMOVE TTL */"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlags(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} + +func TestTableOptionTTLRestoreWithTTLEnableOffFlag(t *testing.T) { + sourceSQL1 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR" + sourceSQL2 := "alter table t ttl_enable = 'ON'" + sourceSQL3 := "alter table t remove ttl" + sourceSQL4 := "create table t (created_at datetime) ttl = created_at + INTERVAL 1 YEAR ttl_enable = 'ON'" + sourceSQL5 := "alter table t ttl_enable = 'ON' placement policy p1" + cases := []struct { + sourceSQL string + flags format.RestoreFlags + expectSQL string + }{ + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, + {sourceSQL1, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */ /*T![ttl] TTL_ENABLE = 'OFF' */"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "ALTER TABLE `t`"}, + {sourceSQL2, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t`"}, + {sourceSQL3, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` REMOVE TTL"}, + {sourceSQL3, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` /*T![ttl] REMOVE TTL */"}, + {sourceSQL4, format.DefaultRestoreFlags | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, + {sourceSQL4, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "CREATE TABLE `t` (`created_at` DATETIME) /*T![ttl] TTL = `created_at` + INTERVAL 1 YEAR */ /*T![ttl] TTL_ENABLE = 'OFF' */"}, + {sourceSQL5, format.DefaultRestoreFlags | format.RestoreTiDBSpecialComment | format.RestoreWithTTLEnableOff, "ALTER TABLE `t` /*T![placement] PLACEMENT POLICY = `p1` */"}, + } + + extractNodeFunc := func(node Node) Node { + return node + } + + for _, ca := range cases { + testCases := []NodeRestoreTestCase{ + {ca.sourceSQL, ca.expectSQL}, + } + runNodeRestoreTestWithFlagsStmtChange(t, testCases, "%s", extractNodeFunc, ca.flags) + } +} diff --git a/parser/ast/dml.go b/pkg/parser/ast/dml.go similarity index 99% rename from parser/ast/dml.go rename to pkg/parser/ast/dml.go index 602e6960ee126..7636ebf98d9b7 100644 --- a/parser/ast/dml.go +++ b/pkg/parser/ast/dml.go @@ -17,10 +17,10 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" ) var ( diff --git a/parser/ast/dml_test.go b/pkg/parser/ast/dml_test.go similarity index 99% rename from parser/ast/dml_test.go rename to pkg/parser/ast/dml_test.go index 94860ec30130b..b8fd282c45e32 100644 --- a/parser/ast/dml_test.go +++ b/pkg/parser/ast/dml_test.go @@ -17,9 +17,9 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser" - . "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser" + . "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" "github.com/stretchr/testify/require" ) diff --git a/parser/ast/expressions.go b/pkg/parser/ast/expressions.go similarity index 99% rename from parser/ast/expressions.go rename to pkg/parser/ast/expressions.go index 7b405e125e73b..8cdeacf7c74fe 100644 --- a/parser/ast/expressions.go +++ b/pkg/parser/ast/expressions.go @@ -21,9 +21,9 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/opcode" ) var ( diff --git a/parser/ast/expressions_test.go b/pkg/parser/ast/expressions_test.go similarity index 99% rename from parser/ast/expressions_test.go rename to pkg/parser/ast/expressions_test.go index 2fa6b71618964..508fba99e759f 100644 --- a/parser/ast/expressions_test.go +++ b/pkg/parser/ast/expressions_test.go @@ -16,9 +16,9 @@ package ast_test import ( "testing" - . "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" + . "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/parser/ast/flag.go b/pkg/parser/ast/flag.go similarity index 100% rename from parser/ast/flag.go rename to pkg/parser/ast/flag.go diff --git a/parser/ast/flag_test.go b/pkg/parser/ast/flag_test.go similarity index 97% rename from parser/ast/flag_test.go rename to pkg/parser/ast/flag_test.go index e90a407f9ddcb..aa78af9fd41b1 100644 --- a/parser/ast/flag_test.go +++ b/pkg/parser/ast/flag_test.go @@ -16,8 +16,8 @@ package ast_test import ( "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/pkg/parser/ast/format_test.go b/pkg/parser/ast/format_test.go new file mode 100644 index 0000000000000..3e102df3dc073 --- /dev/null +++ b/pkg/parser/ast/format_test.go @@ -0,0 +1,98 @@ +package ast_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/stretchr/testify/require" +) + +func getDefaultCharsetAndCollate() (string, string) { + return "utf8", "utf8_bin" +} + +func TestAstFormat(t *testing.T) { + var testcases = []struct { + input string + output string + }{ + // Literals. + {`null`, `NULL`}, + {`true`, `TRUE`}, + {`350`, `350`}, + {`001e-12`, `1e-12`}, // Float. + {`345.678`, `345.678`}, + {`00.0001000`, `0.0001000`}, // Decimal. + {`null`, `NULL`}, + {`"Hello, world"`, `"Hello, world"`}, + {`'Hello, world'`, `"Hello, world"`}, + {`'Hello, "world"'`, `"Hello, \"world\""`}, + {`_utf8'你好'`, `"你好"`}, + {`x'bcde'`, "x'bcde'"}, + {`x''`, "x''"}, + {`x'0035'`, "x'0035'"}, // Shouldn't trim leading zero. + {`b'00111111'`, `b'111111'`}, + {`time'10:10:10.123'`, ast.TimeLiteral + `("10:10:10.123")`}, + {`timestamp'1999-01-01 10:0:0.123'`, ast.TimestampLiteral + `("1999-01-01 10:0:0.123")`}, + {`date '1700-01-01'`, ast.DateLiteral + `("1700-01-01")`}, + + // Expressions. + {`f between 30 and 50`, "`f` BETWEEN 30 AND 50"}, + {`f not between 30 and 50`, "`f` NOT BETWEEN 30 AND 50"}, + {`345 + " hello "`, `345 + " hello "`}, + {`"hello world" >= 'hello world'`, `"hello world" >= "hello world"`}, + {`case 3 when 1 then false else true end`, `CASE 3 WHEN 1 THEN FALSE ELSE TRUE END`}, + {`database.table.column`, "`database`.`table`.`column`"}, // ColumnNameExpr + {`3 is null`, `3 IS NULL`}, + {`3 is not null`, `3 IS NOT NULL`}, + {`3 is true`, `3 IS TRUE`}, + {`3 is not true`, `3 IS NOT TRUE`}, + {`3 is false`, `3 IS FALSE`}, + {` ( x is false )`, "(`x` IS FALSE)"}, + {`3 in ( a,b,"h",6 )`, "3 IN (`a`,`b`,\"h\",6)"}, + {`3 not in ( a,b,"h",6 )`, "3 NOT IN (`a`,`b`,\"h\",6)"}, + {`"abc" like '%b%'`, `"abc" LIKE "%b%"`}, + {`"abc" not like '%b%'`, `"abc" NOT LIKE "%b%"`}, + {`"abc" like '%b%' escape '_'`, `"abc" LIKE "%b%" ESCAPE '_'`}, + {`"abc" regexp '.*bc?'`, `"abc" REGEXP ".*bc?"`}, + {`"abc" not regexp '.*bc?'`, `"abc" NOT REGEXP ".*bc?"`}, + {`- 4`, `-4`}, + {`- ( - 4 ) `, `-(-4)`}, + {`a%b`, "`a` % `b`"}, + {`a%b+6`, "`a` % `b` + 6"}, + {`a%(b+6)`, "`a` % (`b` + 6)"}, + // Functions. + {` json_extract ( a,'$.b',"$.\"c d\"" ) `, "json_extract(`a`, \"$.b\", \"$.\\\"c d\\\"\")"}, + {` length ( a )`, "length(`a`)"}, + {`a -> '$.a'`, "json_extract(`a`, \"$.a\")"}, + {`a.b ->> '$.a'`, "json_unquote(json_extract(`a`.`b`, \"$.a\"))"}, + {`DATE_ADD('1970-01-01', interval 3 second)`, `date_add("1970-01-01", INTERVAL 3 SECOND)`}, + {`TIMESTAMPDIFF(month, '2001-01-01', '2001-02-02 12:03:05.123')`, `timestampdiff(MONTH, "2001-01-01", "2001-02-02 12:03:05.123")`}, + // Cast, Convert and Binary. + // There should not be spaces between 'cast' and '(' unless 'IGNORE_SPACE' mode is set. + // see: https://dev.mysql.com/doc/refman/5.7/en/function-resolution.html + {` cast( a as signed ) `, "CAST(`a` AS SIGNED)"}, + {` cast( a as unsigned integer) `, "CAST(`a` AS UNSIGNED)"}, + {` cast( a as char(3) binary) `, "CAST(`a` AS BINARY(3))"}, + {` cast( a as decimal ) `, "CAST(`a` AS DECIMAL(10))"}, + {` cast( a as decimal (3) ) `, "CAST(`a` AS DECIMAL(3))"}, + {` cast( a as decimal (3,3) ) `, "CAST(`a` AS DECIMAL(3, 3))"}, + {` ((case when (c0 = 0) then 0 when (c0 > 0) then (c1 / c0) end)) `, "((CASE WHEN (`c0` = 0) THEN 0 WHEN (`c0` > 0) THEN (`c1` / `c0`) END))"}, + {` convert (a, signed) `, "CONVERT(`a`, SIGNED)"}, + {` binary "hello"`, `BINARY "hello"`}, + } + for _, tt := range testcases { + expr := fmt.Sprintf("select %s", tt.input) + charset, collation := getDefaultCharsetAndCollate() + stmts, _, err := parser.New().Parse(expr, charset, collation) + node := stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr + require.NoError(t, err) + + writer := bytes.NewBufferString("") + node.Format(writer) + require.Equal(t, tt.output, writer.String()) + } +} diff --git a/parser/ast/functions.go b/pkg/parser/ast/functions.go similarity index 99% rename from parser/ast/functions.go rename to pkg/parser/ast/functions.go index 6a896ef9cdc23..db6625e14135a 100644 --- a/parser/ast/functions.go +++ b/pkg/parser/ast/functions.go @@ -20,9 +20,9 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/types" ) var ( diff --git a/parser/ast/functions_test.go b/pkg/parser/ast/functions_test.go similarity index 98% rename from parser/ast/functions_test.go rename to pkg/parser/ast/functions_test.go index 9f38e2d70958f..442958e9e494f 100644 --- a/parser/ast/functions_test.go +++ b/pkg/parser/ast/functions_test.go @@ -16,10 +16,10 @@ package ast_test import ( "testing" - "github.com/pingcap/tidb/parser" - . "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/test_driver" + "github.com/pingcap/tidb/pkg/parser" + . "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/test_driver" "github.com/stretchr/testify/require" ) diff --git a/pkg/parser/ast/misc.go b/pkg/parser/ast/misc.go new file mode 100644 index 0000000000000..db4c9fa9297fc --- /dev/null +++ b/pkg/parser/ast/misc.go @@ -0,0 +1,4090 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "bytes" + "fmt" + "net/url" + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" +) + +var ( + _ StmtNode = &AdminStmt{} + _ StmtNode = &AlterUserStmt{} + _ StmtNode = &AlterRangeStmt{} + _ StmtNode = &BeginStmt{} + _ StmtNode = &BinlogStmt{} + _ StmtNode = &CommitStmt{} + _ StmtNode = &CreateUserStmt{} + _ StmtNode = &DeallocateStmt{} + _ StmtNode = &DoStmt{} + _ StmtNode = &ExecuteStmt{} + _ StmtNode = &ExplainStmt{} + _ StmtNode = &GrantStmt{} + _ StmtNode = &PrepareStmt{} + _ StmtNode = &RollbackStmt{} + _ StmtNode = &SetPwdStmt{} + _ StmtNode = &SetRoleStmt{} + _ StmtNode = &SetDefaultRoleStmt{} + _ StmtNode = &SetStmt{} + _ StmtNode = &SetSessionStatesStmt{} + _ StmtNode = &UseStmt{} + _ StmtNode = &FlushStmt{} + _ StmtNode = &KillStmt{} + _ StmtNode = &CreateBindingStmt{} + _ StmtNode = &DropBindingStmt{} + _ StmtNode = &SetBindingStmt{} + _ StmtNode = &ShutdownStmt{} + _ StmtNode = &RestartStmt{} + _ StmtNode = &RenameUserStmt{} + _ StmtNode = &HelpStmt{} + _ StmtNode = &PlanReplayerStmt{} + _ StmtNode = &CompactTableStmt{} + _ StmtNode = &SetResourceGroupStmt{} + + _ Node = &PrivElem{} + _ Node = &VariableAssignment{} +) + +// Isolation level constants. +const ( + ReadCommitted = "READ-COMMITTED" + ReadUncommitted = "READ-UNCOMMITTED" + Serializable = "SERIALIZABLE" + RepeatableRead = "REPEATABLE-READ" + + PumpType = "PUMP" + DrainerType = "DRAINER" +) + +// Transaction mode constants. +const ( + Optimistic = "OPTIMISTIC" + Pessimistic = "PESSIMISTIC" +) + +// TypeOpt is used for parsing data type option from SQL. +type TypeOpt struct { + IsUnsigned bool + IsZerofill bool +} + +// FloatOpt is used for parsing floating-point type option from SQL. +// See http://dev.mysql.com/doc/refman/5.7/en/floating-point-types.html +type FloatOpt struct { + Flen int + Decimal int +} + +// AuthOption is used for parsing create use statement. +type AuthOption struct { + // ByAuthString set as true, if AuthString is used for authorization. Otherwise, authorization is done by HashString. + ByAuthString bool + AuthString string + ByHashString bool + HashString string + AuthPlugin string +} + +// Restore implements Node interface. +func (n *AuthOption) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("IDENTIFIED") + if n.AuthPlugin != "" { + ctx.WriteKeyWord(" WITH ") + ctx.WriteString(n.AuthPlugin) + } + if n.ByAuthString { + ctx.WriteKeyWord(" BY ") + ctx.WriteString(n.AuthString) + } else if n.ByHashString { + ctx.WriteKeyWord(" AS ") + ctx.WriteString(n.HashString) + } + return nil +} + +// TraceStmt is a statement to trace what sql actually does at background. +type TraceStmt struct { + stmtNode + + Stmt StmtNode + Format string + + TracePlan bool + TracePlanTarget string +} + +// Restore implements Node interface. +func (n *TraceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("TRACE ") + if n.TracePlan { + ctx.WriteKeyWord("PLAN ") + if n.TracePlanTarget != "" { + ctx.WriteKeyWord("TARGET") + ctx.WritePlain(" = ") + ctx.WriteString(n.TracePlanTarget) + ctx.WritePlain(" ") + } + } else if n.Format != "row" { + ctx.WriteKeyWord("FORMAT") + ctx.WritePlain(" = ") + ctx.WriteString(n.Format) + ctx.WritePlain(" ") + } + if err := n.Stmt.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore TraceStmt.Stmt") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *TraceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*TraceStmt) + node, ok := n.Stmt.Accept(v) + if !ok { + return n, false + } + n.Stmt = node.(StmtNode) + return v.Leave(n) +} + +// ExplainForStmt is a statement to provite information about how is SQL statement executeing +// in connection #ConnectionID +// See https://dev.mysql.com/doc/refman/5.7/en/explain.html +type ExplainForStmt struct { + stmtNode + + Format string + ConnectionID uint64 +} + +// Restore implements Node interface. +func (n *ExplainForStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("EXPLAIN ") + ctx.WriteKeyWord("FORMAT ") + ctx.WritePlain("= ") + ctx.WriteString(n.Format) + ctx.WritePlain(" ") + ctx.WriteKeyWord("FOR ") + ctx.WriteKeyWord("CONNECTION ") + ctx.WritePlain(strconv.FormatUint(n.ConnectionID, 10)) + return nil +} + +// Accept implements Node Accept interface. +func (n *ExplainForStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ExplainForStmt) + return v.Leave(n) +} + +// ExplainStmt is a statement to provide information about how is SQL statement executed +// or get columns information in a table. +// See https://dev.mysql.com/doc/refman/5.7/en/explain.html +type ExplainStmt struct { + stmtNode + + Stmt StmtNode + Format string + Analyze bool +} + +// Restore implements Node interface. +func (n *ExplainStmt) Restore(ctx *format.RestoreCtx) error { + if showStmt, ok := n.Stmt.(*ShowStmt); ok { + ctx.WriteKeyWord("DESC ") + if err := showStmt.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Table") + } + if showStmt.Column != nil { + ctx.WritePlain(" ") + if err := showStmt.Column.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ExplainStmt.ShowStmt.Column") + } + } + return nil + } + ctx.WriteKeyWord("EXPLAIN ") + if n.Analyze { + ctx.WriteKeyWord("ANALYZE ") + } + if !n.Analyze || strings.ToLower(n.Format) != "row" { + ctx.WriteKeyWord("FORMAT ") + ctx.WritePlain("= ") + ctx.WriteString(n.Format) + ctx.WritePlain(" ") + } + if err := n.Stmt.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ExplainStmt.Stmt") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ExplainStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ExplainStmt) + node, ok := n.Stmt.Accept(v) + if !ok { + return n, false + } + n.Stmt = node.(StmtNode) + return v.Leave(n) +} + +// PlanReplayerStmt is a statement to dump or load information for recreating plans +type PlanReplayerStmt struct { + stmtNode + + Stmt StmtNode + Analyze bool + Load bool + HistoricalStatsInfo *AsOfClause + + // Capture indicates 'plan replayer capture ' + Capture bool + // Remove indicates `plan replayer capture remove + Remove bool + + SQLDigest string + PlanDigest string + + // File is used to store 2 cases: + // 1. plan replayer load 'file'; + // 2. plan replayer dump explain 'file' + File string + + // Fields below are currently useless. + + // Where is the where clause in select statement. + Where ExprNode + // OrderBy is the ordering expression list. + OrderBy *OrderByClause + // Limit is the limit clause. + Limit *Limit +} + +// Restore implements Node interface. +func (n *PlanReplayerStmt) Restore(ctx *format.RestoreCtx) error { + if n.Load { + ctx.WriteKeyWord("PLAN REPLAYER LOAD ") + ctx.WriteString(n.File) + return nil + } + if n.Capture { + ctx.WriteKeyWord("PLAN REPLAYER CAPTURE ") + ctx.WriteString(n.SQLDigest) + ctx.WriteKeyWord(" ") + ctx.WriteString(n.PlanDigest) + return nil + } + if n.Remove { + ctx.WriteKeyWord("PLAN REPLAYER CAPTURE REMOVE ") + ctx.WriteString(n.SQLDigest) + ctx.WriteKeyWord(" ") + ctx.WriteString(n.PlanDigest) + return nil + } + + ctx.WriteKeyWord("PLAN REPLAYER DUMP ") + + if n.HistoricalStatsInfo != nil { + ctx.WriteKeyWord("WITH STATS ") + if err := n.HistoricalStatsInfo.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.HistoricalStatsInfo") + } + ctx.WriteKeyWord(" ") + } + if n.Analyze { + ctx.WriteKeyWord("EXPLAIN ANALYZE ") + } else { + ctx.WriteKeyWord("EXPLAIN ") + } + if n.Stmt == nil { + if len(n.File) > 0 { + ctx.WriteString(n.File) + return nil + } + ctx.WriteKeyWord("SLOW QUERY") + if n.Where != nil { + ctx.WriteKeyWord(" WHERE ") + if err := n.Where.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Where") + } + } + if n.OrderBy != nil { + ctx.WriteKeyWord(" ") + if err := n.OrderBy.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.OrderBy") + } + } + if n.Limit != nil { + ctx.WriteKeyWord(" ") + if err := n.Limit.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Limit") + } + } + return nil + } + if err := n.Stmt.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PlanReplayerStmt.Stmt") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *PlanReplayerStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + n = newNode.(*PlanReplayerStmt) + + if n.Load { + return v.Leave(n) + } + + if n.HistoricalStatsInfo != nil { + info, ok := n.HistoricalStatsInfo.Accept(v) + if !ok { + return n, false + } + n.HistoricalStatsInfo = info.(*AsOfClause) + } + + if n.Stmt == nil { + if n.Where != nil { + node, ok := n.Where.Accept(v) + if !ok { + return n, false + } + n.Where = node.(ExprNode) + } + + if n.OrderBy != nil { + node, ok := n.OrderBy.Accept(v) + if !ok { + return n, false + } + n.OrderBy = node.(*OrderByClause) + } + + if n.Limit != nil { + node, ok := n.Limit.Accept(v) + if !ok { + return n, false + } + n.Limit = node.(*Limit) + } + return v.Leave(n) + } + + node, ok := n.Stmt.Accept(v) + if !ok { + return n, false + } + n.Stmt = node.(StmtNode) + return v.Leave(n) +} + +type CompactReplicaKind string + +const ( + // CompactReplicaKindAll means compacting both TiKV and TiFlash replicas. + CompactReplicaKindAll = "ALL" + + // CompactReplicaKindTiFlash means compacting TiFlash replicas. + CompactReplicaKindTiFlash = "TIFLASH" + + // CompactReplicaKindTiKV means compacting TiKV replicas. + CompactReplicaKindTiKV = "TIKV" +) + +// CompactTableStmt is a statement to manually compact a table. +type CompactTableStmt struct { + stmtNode + + Table *TableName + PartitionNames []model.CIStr + ReplicaKind CompactReplicaKind +} + +// Restore implements Node interface. +func (n *CompactTableStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ALTER TABLE ") + n.Table.restoreName(ctx) + + ctx.WriteKeyWord(" COMPACT") + if len(n.PartitionNames) != 0 { + ctx.WriteKeyWord(" PARTITION ") + for i, partition := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(partition.O) + } + } + if n.ReplicaKind != CompactReplicaKindAll { + ctx.WriteKeyWord(" ") + // Note: There is only TiFlash replica available now. TiKV will be added later. + ctx.WriteKeyWord(string(n.ReplicaKind)) + ctx.WriteKeyWord(" REPLICA") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CompactTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CompactTableStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + return v.Leave(n) +} + +// PrepareStmt is a statement to prepares a SQL statement which contains placeholders, +// and it is executed with ExecuteStmt and released with DeallocateStmt. +// See https://dev.mysql.com/doc/refman/5.7/en/prepare.html +type PrepareStmt struct { + stmtNode + + Name string + SQLText string + SQLVar *VariableExpr +} + +// Restore implements Node interface. +func (n *PrepareStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("PREPARE ") + ctx.WriteName(n.Name) + ctx.WriteKeyWord(" FROM ") + if n.SQLText != "" { + ctx.WriteString(n.SQLText) + return nil + } + if n.SQLVar != nil { + if err := n.SQLVar.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore PrepareStmt.SQLVar") + } + return nil + } + return errors.New("An error occurred while restore PrepareStmt") +} + +// Accept implements Node Accept interface. +func (n *PrepareStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*PrepareStmt) + if n.SQLVar != nil { + node, ok := n.SQLVar.Accept(v) + if !ok { + return n, false + } + n.SQLVar = node.(*VariableExpr) + } + return v.Leave(n) +} + +// DeallocateStmt is a statement to release PreparedStmt. +// See https://dev.mysql.com/doc/refman/5.7/en/deallocate-prepare.html +type DeallocateStmt struct { + stmtNode + + Name string +} + +// Restore implements Node interface. +func (n *DeallocateStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DEALLOCATE PREPARE ") + ctx.WriteName(n.Name) + return nil +} + +// Accept implements Node Accept interface. +func (n *DeallocateStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DeallocateStmt) + return v.Leave(n) +} + +// Prepared represents a prepared statement. +type Prepared struct { + Stmt StmtNode + StmtType string + Params []ParamMarkerExpr + SchemaVersion int64 + CachedPlan interface{} + CachedNames interface{} +} + +// ExecuteStmt is a statement to execute PreparedStmt. +// See https://dev.mysql.com/doc/refman/5.7/en/execute.html +type ExecuteStmt struct { + stmtNode + + Name string + UsingVars []ExprNode + BinaryArgs interface{} + PrepStmt interface{} // the corresponding prepared statement + IdxInMulti int + + // FromGeneralStmt indicates whether this execute-stmt is converted from a general query. + // e.g. select * from t where a>2 --> execute 'select * from t where a>?' using 2 + FromGeneralStmt bool +} + +// Restore implements Node interface. +func (n *ExecuteStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("EXECUTE ") + ctx.WriteName(n.Name) + if len(n.UsingVars) > 0 { + ctx.WriteKeyWord(" USING ") + for i, val := range n.UsingVars { + if i != 0 { + ctx.WritePlain(",") + } + if err := val.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore ExecuteStmt.UsingVars index %d", i) + } + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *ExecuteStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ExecuteStmt) + for i, val := range n.UsingVars { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.UsingVars[i] = node.(ExprNode) + } + return v.Leave(n) +} + +// BeginStmt is a statement to start a new transaction. +// See https://dev.mysql.com/doc/refman/5.7/en/commit.html +type BeginStmt struct { + stmtNode + Mode string + CausalConsistencyOnly bool + ReadOnly bool + // AS OF is used to read the data at a specific point of time. + // Should only be used when ReadOnly is true. + AsOf *AsOfClause +} + +// Restore implements Node interface. +func (n *BeginStmt) Restore(ctx *format.RestoreCtx) error { + if n.Mode == "" { + if n.ReadOnly { + ctx.WriteKeyWord("START TRANSACTION READ ONLY") + if n.AsOf != nil { + ctx.WriteKeyWord(" ") + return n.AsOf.Restore(ctx) + } + } else if n.CausalConsistencyOnly { + ctx.WriteKeyWord("START TRANSACTION WITH CAUSAL CONSISTENCY ONLY") + } else { + ctx.WriteKeyWord("START TRANSACTION") + } + } else { + ctx.WriteKeyWord("BEGIN ") + ctx.WriteKeyWord(n.Mode) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *BeginStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + if n.AsOf != nil { + node, ok := n.AsOf.Accept(v) + if !ok { + return n, false + } + n.AsOf = node.(*AsOfClause) + } + + n = newNode.(*BeginStmt) + return v.Leave(n) +} + +// BinlogStmt is an internal-use statement. +// We just parse and ignore it. +// See http://dev.mysql.com/doc/refman/5.7/en/binlog.html +type BinlogStmt struct { + stmtNode + Str string +} + +// Restore implements Node interface. +func (n *BinlogStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("BINLOG ") + ctx.WriteString(n.Str) + return nil +} + +// Accept implements Node Accept interface. +func (n *BinlogStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*BinlogStmt) + return v.Leave(n) +} + +// CompletionType defines completion_type used in COMMIT and ROLLBACK statements +type CompletionType int8 + +const ( + // CompletionTypeDefault refers to NO_CHAIN + CompletionTypeDefault CompletionType = iota + CompletionTypeChain + CompletionTypeRelease +) + +func (n CompletionType) Restore(ctx *format.RestoreCtx) error { + switch n { + case CompletionTypeDefault: + case CompletionTypeChain: + ctx.WriteKeyWord(" AND CHAIN") + case CompletionTypeRelease: + ctx.WriteKeyWord(" RELEASE") + } + return nil +} + +// CommitStmt is a statement to commit the current transaction. +// See https://dev.mysql.com/doc/refman/5.7/en/commit.html +type CommitStmt struct { + stmtNode + // CompletionType overwrites system variable `completion_type` within transaction + CompletionType CompletionType +} + +// Restore implements Node interface. +func (n *CommitStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("COMMIT") + if err := n.CompletionType.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CommitStmt.CompletionType") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CommitStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CommitStmt) + return v.Leave(n) +} + +// RollbackStmt is a statement to roll back the current transaction. +// See https://dev.mysql.com/doc/refman/5.7/en/commit.html +type RollbackStmt struct { + stmtNode + // CompletionType overwrites system variable `completion_type` within transaction + CompletionType CompletionType + // SavepointName is the savepoint name. + SavepointName string +} + +// Restore implements Node interface. +func (n *RollbackStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ROLLBACK") + if n.SavepointName != "" { + ctx.WritePlain(" TO ") + ctx.WritePlain(n.SavepointName) + } + if err := n.CompletionType.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore RollbackStmt.CompletionType") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RollbackStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RollbackStmt) + return v.Leave(n) +} + +// UseStmt is a statement to use the DBName database as the current database. +// See https://dev.mysql.com/doc/refman/5.7/en/use.html +type UseStmt struct { + stmtNode + + DBName string +} + +// Restore implements Node interface. +func (n *UseStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("USE ") + ctx.WriteName(n.DBName) + return nil +} + +// Accept implements Node Accept interface. +func (n *UseStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*UseStmt) + return v.Leave(n) +} + +const ( + // SetNames is the const for set names stmt. + // If VariableAssignment.Name == Names, it should be set names stmt. + SetNames = "SetNAMES" + // SetCharset is the const for set charset stmt. + SetCharset = "SetCharset" +) + +// VariableAssignment is a variable assignment struct. +type VariableAssignment struct { + node + Name string + Value ExprNode + IsGlobal bool + IsSystem bool + + // ExtendValue is a way to store extended info. + // VariableAssignment should be able to store information for SetCharset/SetPWD Stmt. + // For SetCharsetStmt, Value is charset, ExtendValue is collation. + // TODO: Use SetStmt to implement set password statement. + ExtendValue ValueExpr +} + +// Restore implements Node interface. +func (n *VariableAssignment) Restore(ctx *format.RestoreCtx) error { + if n.IsSystem { + ctx.WritePlain("@@") + if n.IsGlobal { + ctx.WriteKeyWord("GLOBAL") + } else { + ctx.WriteKeyWord("SESSION") + } + ctx.WritePlain(".") + } else if n.Name != SetNames && n.Name != SetCharset { + ctx.WriteKeyWord("@") + } + if n.Name == SetNames { + ctx.WriteKeyWord("NAMES ") + } else if n.Name == SetCharset { + ctx.WriteKeyWord("CHARSET ") + } else { + ctx.WriteName(n.Name) + ctx.WritePlain("=") + } + if err := n.Value.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore VariableAssignment.Value") + } + if n.ExtendValue != nil { + ctx.WriteKeyWord(" COLLATE ") + if err := n.ExtendValue.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore VariableAssignment.ExtendValue") + } + } + return nil +} + +// Accept implements Node interface. +func (n *VariableAssignment) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*VariableAssignment) + node, ok := n.Value.Accept(v) + if !ok { + return n, false + } + n.Value = node.(ExprNode) + return v.Leave(n) +} + +// FlushStmtType is the type for FLUSH statement. +type FlushStmtType int + +// Flush statement types. +const ( + FlushNone FlushStmtType = iota + FlushTables + FlushPrivileges + FlushStatus + FlushTiDBPlugin + FlushHosts + FlushLogs + FlushClientErrorsSummary +) + +// LogType is the log type used in FLUSH statement. +type LogType int8 + +const ( + LogTypeDefault LogType = iota + LogTypeBinary + LogTypeEngine + LogTypeError + LogTypeGeneral + LogTypeSlow +) + +// FlushStmt is a statement to flush tables/privileges/optimizer costs and so on. +type FlushStmt struct { + stmtNode + + Tp FlushStmtType // Privileges/Tables/... + NoWriteToBinLog bool + LogType LogType + Tables []*TableName // For FlushTableStmt, if Tables is empty, it means flush all tables. + ReadLock bool + Plugins []string +} + +// Restore implements Node interface. +func (n *FlushStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("FLUSH ") + if n.NoWriteToBinLog { + ctx.WriteKeyWord("NO_WRITE_TO_BINLOG ") + } + switch n.Tp { + case FlushTables: + ctx.WriteKeyWord("TABLES") + for i, v := range n.Tables { + if i == 0 { + ctx.WritePlain(" ") + } else { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore FlushStmt.Tables[%d]", i) + } + } + if n.ReadLock { + ctx.WriteKeyWord(" WITH READ LOCK") + } + case FlushPrivileges: + ctx.WriteKeyWord("PRIVILEGES") + case FlushStatus: + ctx.WriteKeyWord("STATUS") + case FlushTiDBPlugin: + ctx.WriteKeyWord("TIDB PLUGINS") + for i, v := range n.Plugins { + if i == 0 { + ctx.WritePlain(" ") + } else { + ctx.WritePlain(", ") + } + ctx.WritePlain(v) + } + case FlushHosts: + ctx.WriteKeyWord("HOSTS") + case FlushLogs: + var logType string + switch n.LogType { + case LogTypeDefault: + logType = "LOGS" + case LogTypeBinary: + logType = "BINARY LOGS" + case LogTypeEngine: + logType = "ENGINE LOGS" + case LogTypeError: + logType = "ERROR LOGS" + case LogTypeGeneral: + logType = "GENERAL LOGS" + case LogTypeSlow: + logType = "SLOW LOGS" + } + ctx.WriteKeyWord(logType) + case FlushClientErrorsSummary: + ctx.WriteKeyWord("CLIENT_ERRORS_SUMMARY") + default: + return errors.New("Unsupported type of FlushStmt") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *FlushStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*FlushStmt) + return v.Leave(n) +} + +// KillStmt is a statement to kill a query or connection. +type KillStmt struct { + stmtNode + + // Query indicates whether terminate a single query on this connection or the whole connection. + // If Query is true, terminates the statement the connection is currently executing, but leaves the connection itself intact. + // If Query is false, terminates the connection associated with the given ConnectionID, after terminating any statement the connection is executing. + Query bool + ConnectionID uint64 + // TiDBExtension is used to indicate whether the user knows he is sending kill statement to the right tidb-server. + // When the SQL grammar is "KILL TIDB [CONNECTION | QUERY] connectionID", TiDBExtension will be set. + // It's a special grammar extension in TiDB. This extension exists because, when the connection is: + // client -> LVS proxy -> TiDB, and type Ctrl+C in client, the following action will be executed: + // new a connection; kill xxx; + // kill command may send to the wrong TiDB, because the exists of LVS proxy, and kill the wrong session. + // So, "KILL TIDB" grammar is introduced, and it REQUIRES DIRECT client -> TiDB TOPOLOGY. + // TODO: The standard KILL grammar will be supported once we have global connectionID. + TiDBExtension bool + + Expr ExprNode +} + +// Restore implements Node interface. +func (n *KillStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("KILL") + if n.TiDBExtension { + ctx.WriteKeyWord(" TIDB") + } + if n.Query { + ctx.WriteKeyWord(" QUERY") + } + if n.Expr != nil { + ctx.WriteKeyWord(" ") + if err := n.Expr.Restore(ctx); err != nil { + return errors.Trace(err) + } + } else { + ctx.WritePlainf(" %d", n.ConnectionID) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *KillStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*KillStmt) + return v.Leave(n) +} + +// SavepointStmt is the statement of SAVEPOINT. +type SavepointStmt struct { + stmtNode + // Name is the savepoint name. + Name string +} + +// Restore implements Node interface. +func (n *SavepointStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SAVEPOINT ") + ctx.WritePlain(n.Name) + return nil +} + +// Accept implements Node Accept interface. +func (n *SavepointStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + n = newNode.(*SavepointStmt) + return v.Leave(n) +} + +// ReleaseSavepointStmt is the statement of RELEASE SAVEPOINT. +type ReleaseSavepointStmt struct { + stmtNode + // Name is the savepoint name. + Name string +} + +// Restore implements Node interface. +func (n *ReleaseSavepointStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("RELEASE SAVEPOINT ") + ctx.WritePlain(n.Name) + return nil +} + +// Accept implements Node Accept interface. +func (n *ReleaseSavepointStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + n = newNode.(*ReleaseSavepointStmt) + return v.Leave(n) +} + +// SetStmt is the statement to set variables. +type SetStmt struct { + stmtNode + // Variables is the list of variable assignment. + Variables []*VariableAssignment +} + +// Restore implements Node interface. +func (n *SetStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET ") + for i, v := range n.Variables { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore SetStmt.Variables[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *SetStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetStmt) + for i, val := range n.Variables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Variables[i] = node.(*VariableAssignment) + } + return v.Leave(n) +} + +// SetConfigStmt is the statement to set cluster configs. +type SetConfigStmt struct { + stmtNode + + Type string // TiDB, TiKV, PD + Instance string // '127.0.0.1:3306' + Name string // the variable name + Value ExprNode +} + +func (n *SetConfigStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET CONFIG ") + if n.Type != "" { + ctx.WriteKeyWord(n.Type) + } else { + ctx.WriteString(n.Instance) + } + ctx.WritePlain(" ") + ctx.WriteKeyWord(n.Name) + ctx.WritePlain(" = ") + return n.Value.Restore(ctx) +} + +func (n *SetConfigStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetConfigStmt) + node, ok := n.Value.Accept(v) + if !ok { + return n, false + } + n.Value = node.(ExprNode) + return v.Leave(n) +} + +// SetSessionStatesStmt is a statement to restore session states. +type SetSessionStatesStmt struct { + stmtNode + + SessionStates string +} + +func (n *SetSessionStatesStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET SESSION_STATES ") + ctx.WriteString(n.SessionStates) + return nil +} + +func (n *SetSessionStatesStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetSessionStatesStmt) + return v.Leave(n) +} + +/* +// SetCharsetStmt is a statement to assign values to character and collation variables. +// See https://dev.mysql.com/doc/refman/5.7/en/set-statement.html +type SetCharsetStmt struct { + stmtNode + + Charset string + Collate string +} + +// Accept implements Node Accept interface. +func (n *SetCharsetStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetCharsetStmt) + return v.Leave(n) +} +*/ + +// SetPwdStmt is a statement to assign a password to user account. +// See https://dev.mysql.com/doc/refman/5.7/en/set-password.html +type SetPwdStmt struct { + stmtNode + + User *auth.UserIdentity + Password string +} + +// Restore implements Node interface. +func (n *SetPwdStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET PASSWORD") + if n.User != nil { + ctx.WriteKeyWord(" FOR ") + if err := n.User.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore SetPwdStmt.User") + } + } + ctx.WritePlain("=") + ctx.WriteString(n.Password) + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *SetPwdStmt) SecureText() string { + return fmt.Sprintf("set password for user %s", n.User) +} + +// Accept implements Node Accept interface. +func (n *SetPwdStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetPwdStmt) + return v.Leave(n) +} + +type ChangeStmt struct { + stmtNode + + NodeType string + State string + NodeID string +} + +// Restore implements Node interface. +func (n *ChangeStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CHANGE ") + ctx.WriteKeyWord(n.NodeType) + ctx.WriteKeyWord(" TO NODE_STATE ") + ctx.WritePlain("=") + ctx.WriteString(n.State) + ctx.WriteKeyWord(" FOR NODE_ID ") + ctx.WriteString(n.NodeID) + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *ChangeStmt) SecureText() string { + return fmt.Sprintf("change %s to node_state='%s' for node_id '%s'", strings.ToLower(n.NodeType), n.State, n.NodeID) +} + +// Accept implements Node Accept interface. +func (n *ChangeStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ChangeStmt) + return v.Leave(n) +} + +// SetRoleStmtType is the type for FLUSH statement. +type SetRoleStmtType int + +// SetRole statement types. +const ( + SetRoleDefault SetRoleStmtType = iota + SetRoleNone + SetRoleAll + SetRoleAllExcept + SetRoleRegular +) + +type SetRoleStmt struct { + stmtNode + + SetRoleOpt SetRoleStmtType + RoleList []*auth.RoleIdentity +} + +func (n *SetRoleStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET ROLE") + switch n.SetRoleOpt { + case SetRoleDefault: + ctx.WriteKeyWord(" DEFAULT") + case SetRoleNone: + ctx.WriteKeyWord(" NONE") + case SetRoleAll: + ctx.WriteKeyWord(" ALL") + case SetRoleAllExcept: + ctx.WriteKeyWord(" ALL EXCEPT") + } + for i, role := range n.RoleList { + ctx.WritePlain(" ") + err := role.Restore(ctx) + if err != nil { + return errors.Annotate(err, "An error occurred while restore SetRoleStmt.RoleList") + } + if i != len(n.RoleList)-1 { + ctx.WritePlain(",") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *SetRoleStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetRoleStmt) + return v.Leave(n) +} + +type SetDefaultRoleStmt struct { + stmtNode + + SetRoleOpt SetRoleStmtType + RoleList []*auth.RoleIdentity + UserList []*auth.UserIdentity +} + +func (n *SetDefaultRoleStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET DEFAULT ROLE") + switch n.SetRoleOpt { + case SetRoleNone: + ctx.WriteKeyWord(" NONE") + case SetRoleAll: + ctx.WriteKeyWord(" ALL") + default: + } + for i, role := range n.RoleList { + ctx.WritePlain(" ") + err := role.Restore(ctx) + if err != nil { + return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.RoleList") + } + if i != len(n.RoleList)-1 { + ctx.WritePlain(",") + } + } + ctx.WritePlain(" TO") + for i, user := range n.UserList { + ctx.WritePlain(" ") + err := user.Restore(ctx) + if err != nil { + return errors.Annotate(err, "An error occurred while restore SetDefaultRoleStmt.UserList") + } + if i != len(n.UserList)-1 { + ctx.WritePlain(",") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *SetDefaultRoleStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetDefaultRoleStmt) + return v.Leave(n) +} + +// UserSpec is used for parsing create user statement. +type UserSpec struct { + User *auth.UserIdentity + AuthOpt *AuthOption + IsRole bool +} + +// Restore implements Node interface. +func (n *UserSpec) Restore(ctx *format.RestoreCtx) error { + if err := n.User.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserSpec.User") + } + if n.AuthOpt != nil { + ctx.WritePlain(" ") + if err := n.AuthOpt.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserSpec.AuthOpt") + } + } + return nil +} + +// SecurityString formats the UserSpec without password information. +func (n *UserSpec) SecurityString() string { + withPassword := false + if opt := n.AuthOpt; opt != nil { + if len(opt.AuthString) > 0 || len(opt.HashString) > 0 { + withPassword = true + } + } + if withPassword { + return fmt.Sprintf("{%s password = ***}", n.User) + } + return n.User.String() +} + +// EncodedPassword returns the encoded password (which is the real data mysql.user). +// The boolean value indicates input's password format is legal or not. +func (n *UserSpec) EncodedPassword() (string, bool) { + if n.AuthOpt == nil { + return "", true + } + + opt := n.AuthOpt + if opt.ByAuthString { + switch opt.AuthPlugin { + case mysql.AuthCachingSha2Password, mysql.AuthTiDBSM3Password: + return auth.NewHashPassword(opt.AuthString, opt.AuthPlugin), true + case mysql.AuthSocket: + return "", true + default: + return auth.EncodePassword(opt.AuthString), true + } + } + + // store the LDAP dn directly in the password field + switch opt.AuthPlugin { + case mysql.AuthLDAPSimple, mysql.AuthLDAPSASL: + // TODO: validate the HashString to be a `dn` for LDAP + // It seems fine to not validate here, and LDAP server will give an error when the client'll try to login this user. + // The percona server implementation doesn't have a validation for this HashString. + // However, returning an error for obvious wrong format is more friendly. + return opt.HashString, true + } + + // In case we have 'IDENTIFIED WITH ' but no 'BY ' to set an empty password. + if opt.HashString == "" { + return opt.HashString, true + } + + // Not a legal password string. + switch opt.AuthPlugin { + case mysql.AuthCachingSha2Password: + if len(opt.HashString) != mysql.SHAPWDHashLen { + return "", false + } + case mysql.AuthTiDBSM3Password: + if len(opt.HashString) != mysql.SM3PWDHashLen { + return "", false + } + case "", mysql.AuthNativePassword: + if len(opt.HashString) != (mysql.PWDHashLen+1) || !strings.HasPrefix(opt.HashString, "*") { + return "", false + } + case mysql.AuthSocket: + default: + return "", false + } + return opt.HashString, true +} + +type AuthTokenOrTLSOption struct { + Type AuthTokenOrTLSOptionType + Value string +} + +func (t *AuthTokenOrTLSOption) Restore(ctx *format.RestoreCtx) error { + switch t.Type { + case TlsNone: + ctx.WriteKeyWord("NONE") + case Ssl: + ctx.WriteKeyWord("SSL") + case X509: + ctx.WriteKeyWord("X509") + case Cipher: + ctx.WriteKeyWord("CIPHER ") + ctx.WriteString(t.Value) + case Issuer: + ctx.WriteKeyWord("ISSUER ") + ctx.WriteString(t.Value) + case Subject: + ctx.WriteKeyWord("SUBJECT ") + ctx.WriteString(t.Value) + case SAN: + ctx.WriteKeyWord("SAN ") + ctx.WriteString(t.Value) + case TokenIssuer: + ctx.WriteKeyWord("TOKEN_ISSUER ") + ctx.WriteString(t.Value) + default: + return errors.Errorf("Unsupported AuthTokenOrTLSOption.Type %d", t.Type) + } + return nil +} + +type AuthTokenOrTLSOptionType int + +const ( + TlsNone AuthTokenOrTLSOptionType = iota + Ssl + X509 + Cipher + Issuer + Subject + SAN + TokenIssuer +) + +func (t AuthTokenOrTLSOptionType) String() string { + switch t { + case TlsNone: + return "NONE" + case Ssl: + return "SSL" + case X509: + return "X509" + case Cipher: + return "CIPHER" + case Issuer: + return "ISSUER" + case Subject: + return "SUBJECT" + case SAN: + return "SAN" + case TokenIssuer: + return "TOKEN_ISSUER" + default: + return "UNKNOWN" + } +} + +const ( + MaxQueriesPerHour = iota + 1 + MaxUpdatesPerHour + MaxConnectionsPerHour + MaxUserConnections +) + +type ResourceOption struct { + Type int + Count int64 +} + +func (r *ResourceOption) Restore(ctx *format.RestoreCtx) error { + switch r.Type { + case MaxQueriesPerHour: + ctx.WriteKeyWord("MAX_QUERIES_PER_HOUR ") + case MaxUpdatesPerHour: + ctx.WriteKeyWord("MAX_UPDATES_PER_HOUR ") + case MaxConnectionsPerHour: + ctx.WriteKeyWord("MAX_CONNECTIONS_PER_HOUR ") + case MaxUserConnections: + ctx.WriteKeyWord("MAX_USER_CONNECTIONS ") + default: + return errors.Errorf("Unsupported ResourceOption.Type %d", r.Type) + } + ctx.WritePlainf("%d", r.Count) + return nil +} + +const ( + PasswordExpire = iota + 1 + PasswordExpireDefault + PasswordExpireNever + PasswordExpireInterval + PasswordHistory + PasswordHistoryDefault + PasswordReuseInterval + PasswordReuseDefault + Lock + Unlock + FailedLoginAttempts + PasswordLockTime + PasswordLockTimeUnbounded + UserCommentType + UserAttributeType + + UserResourceGroupName +) + +type PasswordOrLockOption struct { + Type int + Count int64 +} + +func (p *PasswordOrLockOption) Restore(ctx *format.RestoreCtx) error { + switch p.Type { + case PasswordExpire: + ctx.WriteKeyWord("PASSWORD EXPIRE") + case PasswordExpireDefault: + ctx.WriteKeyWord("PASSWORD EXPIRE DEFAULT") + case PasswordExpireNever: + ctx.WriteKeyWord("PASSWORD EXPIRE NEVER") + case PasswordExpireInterval: + ctx.WriteKeyWord("PASSWORD EXPIRE INTERVAL") + ctx.WritePlainf(" %d", p.Count) + ctx.WriteKeyWord(" DAY") + case Lock: + ctx.WriteKeyWord("ACCOUNT LOCK") + case Unlock: + ctx.WriteKeyWord("ACCOUNT UNLOCK") + case FailedLoginAttempts: + ctx.WriteKeyWord("FAILED_LOGIN_ATTEMPTS") + ctx.WritePlainf(" %d", p.Count) + case PasswordLockTime: + ctx.WriteKeyWord("PASSWORD_LOCK_TIME") + ctx.WritePlainf(" %d", p.Count) + case PasswordLockTimeUnbounded: + ctx.WriteKeyWord("PASSWORD_LOCK_TIME UNBOUNDED") + case PasswordHistory: + ctx.WriteKeyWord("PASSWORD HISTORY") + ctx.WritePlainf(" %d", p.Count) + case PasswordHistoryDefault: + ctx.WriteKeyWord("PASSWORD HISTORY DEFAULT") + case PasswordReuseInterval: + ctx.WriteKeyWord("PASSWORD REUSE INTERVAL") + ctx.WritePlainf(" %d", p.Count) + ctx.WriteKeyWord(" DAY") + case PasswordReuseDefault: + ctx.WriteKeyWord("PASSWORD REUSE INTERVAL DEFAULT") + default: + return errors.Errorf("Unsupported PasswordOrLockOption.Type %d", p.Type) + } + return nil +} + +type CommentOrAttributeOption struct { + Type int + Value string +} + +func (c *CommentOrAttributeOption) Restore(ctx *format.RestoreCtx) error { + if c.Type == UserCommentType { + ctx.WriteKeyWord(" COMMENT ") + ctx.WriteString(c.Value) + } else if c.Type == UserAttributeType { + ctx.WriteKeyWord(" ATTRIBUTE ") + ctx.WriteString(c.Value) + } + return nil +} + +type ResourceGroupNameOption struct { + Value string +} + +func (c *ResourceGroupNameOption) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(" RESOURCE GROUP ") + ctx.WriteName(c.Value) + return nil +} + +// CreateUserStmt creates user account. +// See https://dev.mysql.com/doc/refman/8.0/en/create-user.html +type CreateUserStmt struct { + stmtNode + + IsCreateRole bool + IfNotExists bool + Specs []*UserSpec + AuthTokenOrTLSOptions []*AuthTokenOrTLSOption + ResourceOptions []*ResourceOption + PasswordOrLockOptions []*PasswordOrLockOption + CommentOrAttributeOption *CommentOrAttributeOption + ResourceGroupNameOption *ResourceGroupNameOption +} + +// Restore implements Node interface. +func (n *CreateUserStmt) Restore(ctx *format.RestoreCtx) error { + if n.IsCreateRole { + ctx.WriteKeyWord("CREATE ROLE ") + } else { + ctx.WriteKeyWord("CREATE USER ") + } + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + for i, v := range n.Specs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.Specs[%d]", i) + } + } + + if len(n.AuthTokenOrTLSOptions) != 0 { + ctx.WriteKeyWord(" REQUIRE ") + } + + for i, option := range n.AuthTokenOrTLSOptions { + if i != 0 { + ctx.WriteKeyWord(" AND ") + } + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.AuthTokenOrTLSOptions[%d]", i) + } + } + + if len(n.ResourceOptions) != 0 { + ctx.WriteKeyWord(" WITH") + } + + for i, v := range n.ResourceOptions { + ctx.WritePlain(" ") + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.ResourceOptions[%d]", i) + } + } + + for i, v := range n.PasswordOrLockOptions { + ctx.WritePlain(" ") + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.PasswordOrLockOptions[%d]", i) + } + } + + if n.CommentOrAttributeOption != nil { + if err := n.CommentOrAttributeOption.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.CommentOrAttributeOption") + } + } + + if n.ResourceGroupNameOption != nil { + if err := n.ResourceGroupNameOption.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.ResourceGroupNameOption") + } + } + + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateUserStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateUserStmt) + return v.Leave(n) +} + +// SecureText implements SensitiveStatement interface. +func (n *CreateUserStmt) SecureText() string { + var buf bytes.Buffer + buf.WriteString("create user") + for _, user := range n.Specs { + buf.WriteString(" ") + buf.WriteString(user.SecurityString()) + } + return buf.String() +} + +// AlterUserStmt modifies user account. +// See https://dev.mysql.com/doc/refman/8.0/en/alter-user.html +type AlterUserStmt struct { + stmtNode + + IfExists bool + CurrentAuth *AuthOption + Specs []*UserSpec + AuthTokenOrTLSOptions []*AuthTokenOrTLSOption + ResourceOptions []*ResourceOption + PasswordOrLockOptions []*PasswordOrLockOption + CommentOrAttributeOption *CommentOrAttributeOption + ResourceGroupNameOption *ResourceGroupNameOption +} + +// Restore implements Node interface. +func (n *AlterUserStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ALTER USER ") + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + if n.CurrentAuth != nil { + ctx.WriteKeyWord("USER") + ctx.WritePlain("() ") + if err := n.CurrentAuth.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterUserStmt.CurrentAuth") + } + } + for i, v := range n.Specs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.Specs[%d]", i) + } + } + + if len(n.AuthTokenOrTLSOptions) != 0 { + ctx.WriteKeyWord(" REQUIRE ") + } + + for i, option := range n.AuthTokenOrTLSOptions { + if i != 0 { + ctx.WriteKeyWord(" AND ") + } + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.AuthTokenOrTLSOptions[%d]", i) + } + } + + if len(n.ResourceOptions) != 0 { + ctx.WriteKeyWord(" WITH") + } + + for i, v := range n.ResourceOptions { + ctx.WritePlain(" ") + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.ResourceOptions[%d]", i) + } + } + + for i, v := range n.PasswordOrLockOptions { + ctx.WritePlain(" ") + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.PasswordOrLockOptions[%d]", i) + } + } + + if n.CommentOrAttributeOption != nil { + if err := n.CommentOrAttributeOption.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.CommentOrAttributeOption") + } + } + + if n.ResourceGroupNameOption != nil { + if err := n.ResourceGroupNameOption.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.ResourceGroupNameOption") + } + } + + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *AlterUserStmt) SecureText() string { + var buf bytes.Buffer + buf.WriteString("alter user") + for _, user := range n.Specs { + buf.WriteString(" ") + buf.WriteString(user.SecurityString()) + } + return buf.String() +} + +// Accept implements Node Accept interface. +func (n *AlterUserStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterUserStmt) + return v.Leave(n) +} + +// AlterInstanceStmt modifies instance. +// See https://dev.mysql.com/doc/refman/8.0/en/alter-instance.html +type AlterInstanceStmt struct { + stmtNode + + ReloadTLS bool + NoRollbackOnError bool +} + +// Restore implements Node interface. +func (n *AlterInstanceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ALTER INSTANCE") + if n.ReloadTLS { + ctx.WriteKeyWord(" RELOAD TLS") + } + if n.NoRollbackOnError { + ctx.WriteKeyWord(" NO ROLLBACK ON ERROR") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AlterInstanceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterInstanceStmt) + return v.Leave(n) +} + +// AlterRangeStmt modifies range configuration. +type AlterRangeStmt struct { + stmtNode + RangeName model.CIStr + PlacementOption *PlacementOption +} + +// Restore implements Node interface. +func (n *AlterRangeStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("ALTER RANGE ") + ctx.WriteName(n.RangeName.O) + ctx.WritePlain(" ") + if err := n.PlacementOption.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AlterRangeStmt.PlacementOption") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AlterRangeStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AlterRangeStmt) + return v.Leave(n) +} + +// DropUserStmt creates user account. +// See http://dev.mysql.com/doc/refman/5.7/en/drop-user.html +type DropUserStmt struct { + stmtNode + + IfExists bool + IsDropRole bool + UserList []*auth.UserIdentity +} + +// Restore implements Node interface. +func (n *DropUserStmt) Restore(ctx *format.RestoreCtx) error { + if n.IsDropRole { + ctx.WriteKeyWord("DROP ROLE ") + } else { + ctx.WriteKeyWord("DROP USER ") + } + if n.IfExists { + ctx.WriteKeyWord("IF EXISTS ") + } + for i, v := range n.UserList { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropUserStmt.UserList[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *DropUserStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropUserStmt) + return v.Leave(n) +} + +// CreateBindingStmt creates sql binding hint. +type CreateBindingStmt struct { + stmtNode + + GlobalScope bool + OriginNode StmtNode + HintedNode StmtNode + PlanDigest string +} + +func (n *CreateBindingStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE ") + if n.GlobalScope { + ctx.WriteKeyWord("GLOBAL ") + } else { + ctx.WriteKeyWord("SESSION ") + } + if n.OriginNode == nil { + ctx.WriteKeyWord("BINDING FROM HISTORY USING PLAN DIGEST ") + ctx.WriteString(n.PlanDigest) + } else { + ctx.WriteKeyWord("BINDING FOR ") + if err := n.OriginNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + ctx.WriteKeyWord(" USING ") + if err := n.HintedNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + } + return nil +} + +func (n *CreateBindingStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateBindingStmt) + if n.OriginNode != nil { + origNode, ok := n.OriginNode.Accept(v) + if !ok { + return n, false + } + n.OriginNode = origNode.(StmtNode) + hintedNode, ok := n.HintedNode.Accept(v) + if !ok { + return n, false + } + n.HintedNode = hintedNode.(StmtNode) + } + return v.Leave(n) +} + +// DropBindingStmt deletes sql binding hint. +type DropBindingStmt struct { + stmtNode + + GlobalScope bool + OriginNode StmtNode + HintedNode StmtNode + SQLDigest string +} + +func (n *DropBindingStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP ") + if n.GlobalScope { + ctx.WriteKeyWord("GLOBAL ") + } else { + ctx.WriteKeyWord("SESSION ") + } + ctx.WriteKeyWord("BINDING FOR ") + if n.OriginNode == nil { + ctx.WriteKeyWord("SQL DIGEST ") + ctx.WriteString(n.SQLDigest) + } else { + if err := n.OriginNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + if n.HintedNode != nil { + ctx.WriteKeyWord(" USING ") + if err := n.HintedNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + } + } + return nil +} + +func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropBindingStmt) + if n.OriginNode != nil { + // OriginNode is nil means we build drop binding by sql digest + origNode, ok := n.OriginNode.Accept(v) + if !ok { + return n, false + } + n.OriginNode = origNode.(StmtNode) + if n.HintedNode != nil { + hintedNode, ok := n.HintedNode.Accept(v) + if !ok { + return n, false + } + n.HintedNode = hintedNode.(StmtNode) + } + } + return v.Leave(n) +} + +// BindingStatusType defines the status type for the binding +type BindingStatusType int8 + +// Binding status types. +const ( + BindingStatusTypeEnabled BindingStatusType = iota + BindingStatusTypeDisabled +) + +// SetBindingStmt sets sql binding status. +type SetBindingStmt struct { + stmtNode + + BindingStatusType BindingStatusType + OriginNode StmtNode + HintedNode StmtNode + SQLDigest string +} + +func (n *SetBindingStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET ") + ctx.WriteKeyWord("BINDING ") + switch n.BindingStatusType { + case BindingStatusTypeEnabled: + ctx.WriteKeyWord("ENABLED ") + case BindingStatusTypeDisabled: + ctx.WriteKeyWord("DISABLED ") + } + ctx.WriteKeyWord("FOR ") + if n.OriginNode == nil { + ctx.WriteKeyWord("SQL DIGEST ") + ctx.WriteString(n.SQLDigest) + } else { + if err := n.OriginNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + if n.HintedNode != nil { + ctx.WriteKeyWord(" USING ") + if err := n.HintedNode.Restore(ctx); err != nil { + return errors.Trace(err) + } + } + } + return nil +} + +func (n *SetBindingStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetBindingStmt) + if n.OriginNode != nil { + // OriginNode is nil means we set binding stmt by sql digest + origNode, ok := n.OriginNode.Accept(v) + if !ok { + return n, false + } + n.OriginNode = origNode.(StmtNode) + if n.HintedNode != nil { + hintedNode, ok := n.HintedNode.Accept(v) + if !ok { + return n, false + } + n.HintedNode = hintedNode.(StmtNode) + } + } + return v.Leave(n) +} + +// Extended statistics types. +const ( + StatsTypeCardinality uint8 = iota + StatsTypeDependency + StatsTypeCorrelation +) + +// StatisticsSpec is the specification for ADD /DROP STATISTICS. +type StatisticsSpec struct { + StatsName string + StatsType uint8 + Columns []*ColumnName +} + +// CreateStatisticsStmt is a statement to create extended statistics. +// Examples: +// +// CREATE STATISTICS stats1 (cardinality) ON t(a, b, c); +// CREATE STATISTICS stats2 (dependency) ON t(a, b); +// CREATE STATISTICS stats3 (correlation) ON t(a, b); +type CreateStatisticsStmt struct { + stmtNode + + IfNotExists bool + StatsName string + StatsType uint8 + Table *TableName + Columns []*ColumnName +} + +// Restore implements Node interface. +func (n *CreateStatisticsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CREATE STATISTICS ") + if n.IfNotExists { + ctx.WriteKeyWord("IF NOT EXISTS ") + } + ctx.WriteName(n.StatsName) + switch n.StatsType { + case StatsTypeCardinality: + ctx.WriteKeyWord(" (cardinality) ") + case StatsTypeDependency: + ctx.WriteKeyWord(" (dependency) ") + case StatsTypeCorrelation: + ctx.WriteKeyWord(" (correlation) ") + } + ctx.WriteKeyWord("ON ") + if err := n.Table.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CreateStatisticsStmt.Table") + } + + ctx.WritePlain("(") + for i, col := range n.Columns { + if i != 0 { + ctx.WritePlain(", ") + } + if err := col.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore CreateStatisticsStmt.Columns: [%v]", i) + } + } + ctx.WritePlain(")") + return nil +} + +// Accept implements Node Accept interface. +func (n *CreateStatisticsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CreateStatisticsStmt) + node, ok := n.Table.Accept(v) + if !ok { + return n, false + } + n.Table = node.(*TableName) + for i, col := range n.Columns { + node, ok = col.Accept(v) + if !ok { + return n, false + } + n.Columns[i] = node.(*ColumnName) + } + return v.Leave(n) +} + +// DropStatisticsStmt is a statement to drop extended statistics. +// Examples: +// +// DROP STATISTICS stats1; +type DropStatisticsStmt struct { + stmtNode + + StatsName string +} + +// Restore implements Node interface. +func (n *DropStatisticsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP STATISTICS ") + ctx.WriteName(n.StatsName) + return nil +} + +// Accept implements Node Accept interface. +func (n *DropStatisticsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropStatisticsStmt) + return v.Leave(n) +} + +// DoStmt is the struct for DO statement. +type DoStmt struct { + stmtNode + + Exprs []ExprNode +} + +// Restore implements Node interface. +func (n *DoStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DO ") + for i, v := range n.Exprs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DoStmt.Exprs[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *DoStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DoStmt) + for i, val := range n.Exprs { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Exprs[i] = node.(ExprNode) + } + return v.Leave(n) +} + +// AdminStmtType is the type for admin statement. +type AdminStmtType int + +// Admin statement types. +const ( + AdminShowDDL = iota + 1 + AdminCheckTable + AdminShowDDLJobs + AdminCancelDDLJobs + AdminPauseDDLJobs + AdminResumeDDLJobs + AdminCheckIndex + AdminRecoverIndex + AdminCleanupIndex + AdminCheckIndexRange + AdminShowDDLJobQueries + AdminShowDDLJobQueriesWithRange + AdminChecksumTable + AdminShowSlow + AdminShowNextRowID + AdminReloadExprPushdownBlacklist + AdminReloadOptRuleBlacklist + AdminPluginDisable + AdminPluginEnable + AdminFlushBindings + AdminCaptureBindings + AdminEvolveBindings + AdminReloadBindings + AdminShowTelemetry + AdminResetTelemetryID + AdminReloadStatistics + AdminFlushPlanCache +) + +// HandleRange represents a range where handle value >= Begin and < End. +type HandleRange struct { + Begin int64 + End int64 +} + +type StatementScope int + +const ( + StatementScopeNone StatementScope = iota + StatementScopeSession + StatementScopeInstance + StatementScopeGlobal +) + +// ShowSlowType defines the type for SlowSlow statement. +type ShowSlowType int + +const ( + // ShowSlowTop is a ShowSlowType constant. + ShowSlowTop ShowSlowType = iota + // ShowSlowRecent is a ShowSlowType constant. + ShowSlowRecent +) + +// ShowSlowKind defines the kind for SlowSlow statement when the type is ShowSlowTop. +type ShowSlowKind int + +const ( + // ShowSlowKindDefault is a ShowSlowKind constant. + ShowSlowKindDefault ShowSlowKind = iota + // ShowSlowKindInternal is a ShowSlowKind constant. + ShowSlowKindInternal + // ShowSlowKindAll is a ShowSlowKind constant. + ShowSlowKindAll +) + +// ShowSlow is used for the following command: +// +// admin show slow top [ internal | all] N +// admin show slow recent N +type ShowSlow struct { + Tp ShowSlowType + Count uint64 + Kind ShowSlowKind +} + +// Restore implements Node interface. +func (n *ShowSlow) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case ShowSlowRecent: + ctx.WriteKeyWord("RECENT ") + case ShowSlowTop: + ctx.WriteKeyWord("TOP ") + switch n.Kind { + case ShowSlowKindDefault: + // do nothing + case ShowSlowKindInternal: + ctx.WriteKeyWord("INTERNAL ") + case ShowSlowKindAll: + ctx.WriteKeyWord("ALL ") + default: + return errors.New("Unsupported kind of ShowSlowTop") + } + default: + return errors.New("Unsupported type of ShowSlow") + } + ctx.WritePlainf("%d", n.Count) + return nil +} + +// LimitSimple is the struct for Admin statement limit option. +type LimitSimple struct { + Count uint64 + Offset uint64 +} + +// AdminStmt is the struct for Admin statement. +type AdminStmt struct { + stmtNode + + Tp AdminStmtType + Index string + Tables []*TableName + JobIDs []int64 + JobNumber int64 + + HandleRanges []HandleRange + ShowSlow *ShowSlow + Plugins []string + Where ExprNode + StatementScope StatementScope + LimitSimple LimitSimple +} + +// Restore implements Node interface. +func (n *AdminStmt) Restore(ctx *format.RestoreCtx) error { + restoreTables := func() error { + for i, v := range n.Tables { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AdminStmt.Tables[%d]", i) + } + } + return nil + } + restoreJobIDs := func() { + for i, v := range n.JobIDs { + if i != 0 { + ctx.WritePlain(", ") + } + ctx.WritePlainf("%d", v) + } + } + + ctx.WriteKeyWord("ADMIN ") + switch n.Tp { + case AdminShowDDL: + ctx.WriteKeyWord("SHOW DDL") + case AdminShowDDLJobs: + ctx.WriteKeyWord("SHOW DDL JOBS") + if n.JobNumber != 0 { + ctx.WritePlainf(" %d", n.JobNumber) + } + if n.Where != nil { + ctx.WriteKeyWord(" WHERE ") + if err := n.Where.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore ShowStmt.Where") + } + } + case AdminShowNextRowID: + ctx.WriteKeyWord("SHOW ") + if err := restoreTables(); err != nil { + return err + } + ctx.WriteKeyWord(" NEXT_ROW_ID") + case AdminCheckTable: + ctx.WriteKeyWord("CHECK TABLE ") + if err := restoreTables(); err != nil { + return err + } + case AdminCheckIndex: + ctx.WriteKeyWord("CHECK INDEX ") + if err := restoreTables(); err != nil { + return err + } + ctx.WritePlainf(" %s", n.Index) + case AdminRecoverIndex: + ctx.WriteKeyWord("RECOVER INDEX ") + if err := restoreTables(); err != nil { + return err + } + ctx.WritePlainf(" %s", n.Index) + case AdminCleanupIndex: + ctx.WriteKeyWord("CLEANUP INDEX ") + if err := restoreTables(); err != nil { + return err + } + ctx.WritePlainf(" %s", n.Index) + case AdminCheckIndexRange: + ctx.WriteKeyWord("CHECK INDEX ") + if err := restoreTables(); err != nil { + return err + } + ctx.WritePlainf(" %s", n.Index) + if n.HandleRanges != nil { + ctx.WritePlain(" ") + for i, v := range n.HandleRanges { + if i != 0 { + ctx.WritePlain(", ") + } + ctx.WritePlainf("(%d,%d)", v.Begin, v.End) + } + } + case AdminChecksumTable: + ctx.WriteKeyWord("CHECKSUM TABLE ") + if err := restoreTables(); err != nil { + return err + } + case AdminCancelDDLJobs: + ctx.WriteKeyWord("CANCEL DDL JOBS ") + restoreJobIDs() + case AdminPauseDDLJobs: + ctx.WriteKeyWord("PAUSE DDL JOBS ") + restoreJobIDs() + case AdminResumeDDLJobs: + ctx.WriteKeyWord("RESUME DDL JOBS ") + restoreJobIDs() + case AdminShowDDLJobQueries: + ctx.WriteKeyWord("SHOW DDL JOB QUERIES ") + restoreJobIDs() + case AdminShowDDLJobQueriesWithRange: + ctx.WriteKeyWord("SHOW DDL JOB QUERIES LIMIT ") + ctx.WritePlainf("%d, %d", n.LimitSimple.Offset, n.LimitSimple.Count) + case AdminShowSlow: + ctx.WriteKeyWord("SHOW SLOW ") + if err := n.ShowSlow.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore AdminStmt.ShowSlow") + } + case AdminReloadExprPushdownBlacklist: + ctx.WriteKeyWord("RELOAD EXPR_PUSHDOWN_BLACKLIST") + case AdminReloadOptRuleBlacklist: + ctx.WriteKeyWord("RELOAD OPT_RULE_BLACKLIST") + case AdminPluginEnable: + ctx.WriteKeyWord("PLUGINS ENABLE") + for i, v := range n.Plugins { + if i == 0 { + ctx.WritePlain(" ") + } else { + ctx.WritePlain(", ") + } + ctx.WritePlain(v) + } + case AdminPluginDisable: + ctx.WriteKeyWord("PLUGINS DISABLE") + for i, v := range n.Plugins { + if i == 0 { + ctx.WritePlain(" ") + } else { + ctx.WritePlain(", ") + } + ctx.WritePlain(v) + } + case AdminFlushBindings: + ctx.WriteKeyWord("FLUSH BINDINGS") + case AdminCaptureBindings: + ctx.WriteKeyWord("CAPTURE BINDINGS") + case AdminEvolveBindings: + ctx.WriteKeyWord("EVOLVE BINDINGS") + case AdminReloadBindings: + ctx.WriteKeyWord("RELOAD BINDINGS") + case AdminShowTelemetry: + ctx.WriteKeyWord("SHOW TELEMETRY") + case AdminResetTelemetryID: + ctx.WriteKeyWord("RESET TELEMETRY_ID") + case AdminReloadStatistics: + ctx.WriteKeyWord("RELOAD STATS_EXTENDED") + case AdminFlushPlanCache: + if n.StatementScope == StatementScopeSession { + ctx.WriteKeyWord("FLUSH SESSION PLAN_CACHE") + } else if n.StatementScope == StatementScopeInstance { + ctx.WriteKeyWord("FLUSH INSTANCE PLAN_CACHE") + } else if n.StatementScope == StatementScopeGlobal { + ctx.WriteKeyWord("FLUSH GLOBAL PLAN_CACHE") + } + default: + return errors.New("Unsupported AdminStmt type") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AdminStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + + n = newNode.(*AdminStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + + if n.Where != nil { + node, ok := n.Where.Accept(v) + if !ok { + return n, false + } + n.Where = node.(ExprNode) + } + + return v.Leave(n) +} + +// RoleOrPriv is a temporary structure to be further processed into auth.RoleIdentity or PrivElem +type RoleOrPriv struct { + Symbols string // hold undecided symbols + Node interface{} // hold auth.RoleIdentity or PrivElem that can be sure when parsing +} + +func (n *RoleOrPriv) ToRole() (*auth.RoleIdentity, error) { + if n.Node != nil { + if r, ok := n.Node.(*auth.RoleIdentity); ok { + return r, nil + } + return nil, errors.Errorf("can't convert to RoleIdentity, type %T", n.Node) + } + return &auth.RoleIdentity{Username: n.Symbols, Hostname: "%"}, nil +} + +func (n *RoleOrPriv) ToPriv() (*PrivElem, error) { + if n.Node != nil { + if p, ok := n.Node.(*PrivElem); ok { + return p, nil + } + return nil, errors.Errorf("can't convert to PrivElem, type %T", n.Node) + } + if len(n.Symbols) == 0 { + return nil, errors.New("symbols should not be length 0") + } + return &PrivElem{Priv: mysql.ExtendedPriv, Name: n.Symbols}, nil +} + +// PrivElem is the privilege type and optional column list. +type PrivElem struct { + node + + Priv mysql.PrivilegeType + Cols []*ColumnName + Name string +} + +// Restore implements Node interface. +func (n *PrivElem) Restore(ctx *format.RestoreCtx) error { + if n.Priv == mysql.AllPriv { + ctx.WriteKeyWord("ALL") + } else if n.Priv == mysql.ExtendedPriv { + ctx.WriteKeyWord(n.Name) + } else { + str, ok := mysql.Priv2Str[n.Priv] + if !ok { + return errors.New("Undefined privilege type") + } + ctx.WriteKeyWord(str) + } + if n.Cols != nil { + ctx.WritePlain(" (") + for i, v := range n.Cols { + if i != 0 { + ctx.WritePlain(",") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore PrivElem.Cols[%d]", i) + } + } + ctx.WritePlain(")") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *PrivElem) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*PrivElem) + for i, val := range n.Cols { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Cols[i] = node.(*ColumnName) + } + return v.Leave(n) +} + +// ObjectTypeType is the type for object type. +type ObjectTypeType int + +const ( + // ObjectTypeNone is for empty object type. + ObjectTypeNone ObjectTypeType = iota + 1 + // ObjectTypeTable means the following object is a table. + ObjectTypeTable + // ObjectTypeFunction means the following object is a stored function. + ObjectTypeFunction + // ObjectTypeProcedure means the following object is a stored procedure. + ObjectTypeProcedure +) + +// Restore implements Node interface. +func (n ObjectTypeType) Restore(ctx *format.RestoreCtx) error { + switch n { + case ObjectTypeNone: + // do nothing + case ObjectTypeTable: + ctx.WriteKeyWord("TABLE") + case ObjectTypeFunction: + ctx.WriteKeyWord("FUNCTION") + case ObjectTypeProcedure: + ctx.WriteKeyWord("PROCEDURE") + default: + return errors.New("Unsupported object type") + } + return nil +} + +// GrantLevelType is the type for grant level. +type GrantLevelType int + +const ( + // GrantLevelNone is the dummy const for default value. + GrantLevelNone GrantLevelType = iota + 1 + // GrantLevelGlobal means the privileges are administrative or apply to all databases on a given server. + GrantLevelGlobal + // GrantLevelDB means the privileges apply to all objects in a given database. + GrantLevelDB + // GrantLevelTable means the privileges apply to all columns in a given table. + GrantLevelTable +) + +// GrantLevel is used for store the privilege scope. +type GrantLevel struct { + Level GrantLevelType + DBName string + TableName string +} + +// Restore implements Node interface. +func (n *GrantLevel) Restore(ctx *format.RestoreCtx) error { + switch n.Level { + case GrantLevelDB: + if n.DBName == "" { + ctx.WritePlain("*") + } else { + ctx.WriteName(n.DBName) + ctx.WritePlain(".*") + } + case GrantLevelGlobal: + ctx.WritePlain("*.*") + case GrantLevelTable: + if n.DBName != "" { + ctx.WriteName(n.DBName) + ctx.WritePlain(".") + } + ctx.WriteName(n.TableName) + } + return nil +} + +// RevokeStmt is the struct for REVOKE statement. +type RevokeStmt struct { + stmtNode + + Privs []*PrivElem + ObjectType ObjectTypeType + Level *GrantLevel + Users []*UserSpec +} + +// Restore implements Node interface. +func (n *RevokeStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("REVOKE ") + for i, v := range n.Privs { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Privs[%d]", i) + } + } + ctx.WriteKeyWord(" ON ") + if n.ObjectType != ObjectTypeNone { + if err := n.ObjectType.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore RevokeStmt.ObjectType") + } + ctx.WritePlain(" ") + } + if err := n.Level.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore RevokeStmt.Level") + } + ctx.WriteKeyWord(" FROM ") + for i, v := range n.Users { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RevokeStmt.Users[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RevokeStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RevokeStmt) + for i, val := range n.Privs { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Privs[i] = node.(*PrivElem) + } + return v.Leave(n) +} + +// RevokeStmt is the struct for REVOKE statement. +type RevokeRoleStmt struct { + stmtNode + + Roles []*auth.RoleIdentity + Users []*auth.UserIdentity +} + +// Restore implements Node interface. +func (n *RevokeRoleStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("REVOKE ") + for i, role := range n.Roles { + if i != 0 { + ctx.WritePlain(", ") + } + if err := role.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Roles[%d]", i) + } + } + ctx.WriteKeyWord(" FROM ") + for i, v := range n.Users { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore RevokeRoleStmt.Users[%d]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RevokeRoleStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RevokeRoleStmt) + return v.Leave(n) +} + +// GrantStmt is the struct for GRANT statement. +type GrantStmt struct { + stmtNode + + Privs []*PrivElem + ObjectType ObjectTypeType + Level *GrantLevel + Users []*UserSpec + AuthTokenOrTLSOptions []*AuthTokenOrTLSOption + WithGrant bool +} + +// Restore implements Node interface. +func (n *GrantStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("GRANT ") + for i, v := range n.Privs { + if i != 0 && v.Priv != 0 { + ctx.WritePlain(", ") + } else if v.Priv == 0 { + ctx.WritePlain(" ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantStmt.Privs[%d]", i) + } + } + ctx.WriteKeyWord(" ON ") + if n.ObjectType != ObjectTypeNone { + if err := n.ObjectType.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore GrantStmt.ObjectType") + } + ctx.WritePlain(" ") + } + if err := n.Level.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore GrantStmt.Level") + } + ctx.WriteKeyWord(" TO ") + for i, v := range n.Users { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) + } + } + if n.AuthTokenOrTLSOptions != nil { + if len(n.AuthTokenOrTLSOptions) != 0 { + ctx.WriteKeyWord(" REQUIRE ") + } + for i, option := range n.AuthTokenOrTLSOptions { + if i != 0 { + ctx.WriteKeyWord(" AND ") + } + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantStmt.AuthTokenOrTLSOptions[%d]", i) + } + } + } + if n.WithGrant { + ctx.WriteKeyWord(" WITH GRANT OPTION") + } + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *GrantStmt) SecureText() string { + text := n.text + // Filter "identified by xxx" because it would expose password information. + idx := strings.Index(strings.ToLower(text), "identified") + if idx > 0 { + text = text[:idx] + } + return text +} + +// Accept implements Node Accept interface. +func (n *GrantStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*GrantStmt) + for i, val := range n.Privs { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Privs[i] = node.(*PrivElem) + } + return v.Leave(n) +} + +// GrantProxyStmt is the struct for GRANT PROXY statement. +type GrantProxyStmt struct { + stmtNode + + LocalUser *auth.UserIdentity + ExternalUsers []*auth.UserIdentity + WithGrant bool +} + +// Accept implements Node Accept interface. +func (n *GrantProxyStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*GrantProxyStmt) + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *GrantProxyStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("GRANT PROXY ON ") + if err := n.LocalUser.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantProxyStmt.LocalUser") + } + ctx.WriteKeyWord(" TO ") + for i, v := range n.ExternalUsers { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantProxyStmt.ExternalUsers[%d]", i) + } + } + if n.WithGrant { + ctx.WriteKeyWord(" WITH GRANT OPTION") + } + return nil +} + +// GrantRoleStmt is the struct for GRANT TO statement. +type GrantRoleStmt struct { + stmtNode + + Roles []*auth.RoleIdentity + Users []*auth.UserIdentity +} + +// Accept implements Node Accept interface. +func (n *GrantRoleStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*GrantRoleStmt) + return v.Leave(n) +} + +// Restore implements Node interface. +func (n *GrantRoleStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("GRANT ") + if len(n.Roles) > 0 { + for i, role := range n.Roles { + if i != 0 { + ctx.WritePlain(", ") + } + if err := role.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantRoleStmt.Roles[%d]", i) + } + } + } + ctx.WriteKeyWord(" TO ") + for i, v := range n.Users { + if i != 0 { + ctx.WritePlain(", ") + } + if err := v.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i) + } + } + return nil +} + +// SecureText implements SensitiveStatement interface. +func (n *GrantRoleStmt) SecureText() string { + text := n.text + // Filter "identified by xxx" because it would expose password information. + idx := strings.Index(strings.ToLower(text), "identified") + if idx > 0 { + text = text[:idx] + } + return text +} + +// ShutdownStmt is a statement to stop the TiDB server. +// See https://dev.mysql.com/doc/refman/5.7/en/shutdown.html +type ShutdownStmt struct { + stmtNode +} + +// Restore implements Node interface. +func (n *ShutdownStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SHUTDOWN") + return nil +} + +// Accept implements Node Accept interface. +func (n *ShutdownStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*ShutdownStmt) + return v.Leave(n) +} + +// RestartStmt is a statement to restart the TiDB server. +// See https://dev.mysql.com/doc/refman/8.0/en/restart.html +type RestartStmt struct { + stmtNode +} + +// Restore implements Node interface. +func (n *RestartStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("RESTART") + return nil +} + +// Accept implements Node Accept interface. +func (n *RestartStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RestartStmt) + return v.Leave(n) +} + +// HelpStmt is a statement for server side help +// See https://dev.mysql.com/doc/refman/8.0/en/help.html +type HelpStmt struct { + stmtNode + + Topic string +} + +// Restore implements Node interface. +func (n *HelpStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("HELP ") + ctx.WriteString(n.Topic) + return nil +} + +// Accept implements Node Accept interface. +func (n *HelpStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*HelpStmt) + return v.Leave(n) +} + +// RenameUserStmt is a statement to rename a user. +// See http://dev.mysql.com/doc/refman/5.7/en/rename-user.html +type RenameUserStmt struct { + stmtNode + + UserToUsers []*UserToUser +} + +// Restore implements Node interface. +func (n *RenameUserStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("RENAME USER ") + for index, user2user := range n.UserToUsers { + if index != 0 { + ctx.WritePlain(", ") + } + if err := user2user.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore RenameUserStmt.UserToUsers") + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *RenameUserStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*RenameUserStmt) + + for i, t := range n.UserToUsers { + node, ok := t.Accept(v) + if !ok { + return n, false + } + n.UserToUsers[i] = node.(*UserToUser) + } + return v.Leave(n) +} + +// UserToUser represents renaming old user to new user used in RenameUserStmt. +type UserToUser struct { + node + OldUser *auth.UserIdentity + NewUser *auth.UserIdentity +} + +// Restore implements Node interface. +func (n *UserToUser) Restore(ctx *format.RestoreCtx) error { + if err := n.OldUser.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserToUser.OldUser") + } + ctx.WriteKeyWord(" TO ") + if err := n.NewUser.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore UserToUser.NewUser") + } + return nil +} + +// Accept implements Node Accept interface. +func (n *UserToUser) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*UserToUser) + return v.Leave(n) +} + +type BRIEKind uint8 +type BRIEOptionType uint16 + +const ( + BRIEKindBackup BRIEKind = iota + BRIEKindCancelJob + BRIEKindStreamStart + BRIEKindStreamMetaData + BRIEKindStreamStatus + BRIEKindStreamPause + BRIEKindStreamResume + BRIEKindStreamStop + BRIEKindStreamPurge + BRIEKindRestore + BRIEKindRestorePIT + BRIEKindShowJob + BRIEKindShowQuery + BRIEKindShowBackupMeta + // common BRIE options + BRIEOptionRateLimit BRIEOptionType = iota + 1 + BRIEOptionConcurrency + BRIEOptionChecksum + BRIEOptionSendCreds + BRIEOptionCheckpoint + BRIEOptionStartTS + BRIEOptionUntilTS + // backup options + BRIEOptionBackupTimeAgo + BRIEOptionBackupTS + BRIEOptionBackupTSO + BRIEOptionLastBackupTS + BRIEOptionLastBackupTSO + BRIEOptionGCTTL + // restore options + BRIEOptionOnline + BRIEOptionFullBackupStorage + BRIEOptionRestoredTS + // import options + BRIEOptionAnalyze + BRIEOptionBackend + BRIEOptionOnDuplicate + BRIEOptionSkipSchemaFiles + BRIEOptionStrictFormat + BRIEOptionTiKVImporter + BRIEOptionResume + // CSV options + BRIEOptionCSVBackslashEscape + BRIEOptionCSVDelimiter + BRIEOptionCSVHeader + BRIEOptionCSVNotNull + BRIEOptionCSVNull + BRIEOptionCSVSeparator + BRIEOptionCSVTrimLastSeparators + + BRIECSVHeaderIsColumns = ^uint64(0) +) + +type BRIEOptionLevel uint64 + +const ( + BRIEOptionLevelOff BRIEOptionLevel = iota // equals FALSE + BRIEOptionLevelRequired // equals TRUE + BRIEOptionLevelOptional +) + +func (kind BRIEKind) String() string { + switch kind { + case BRIEKindBackup: + return "BACKUP" + case BRIEKindRestore: + return "RESTORE" + case BRIEKindStreamStart: + return "BACKUP LOGS" + case BRIEKindStreamStop: + return "STOP BACKUP LOGS" + case BRIEKindStreamPause: + return "PAUSE BACKUP LOGS" + case BRIEKindStreamResume: + return "RESUME BACKUP LOGS" + case BRIEKindStreamStatus: + return "SHOW BACKUP LOGS STATUS" + case BRIEKindStreamMetaData: + return "SHOW BACKUP LOGS METADATA" + case BRIEKindStreamPurge: + return "PURGE BACKUP LOGS" + case BRIEKindRestorePIT: + return "RESTORE POINT" + case BRIEKindShowJob: + return "SHOW BR JOB" + case BRIEKindShowQuery: + return "SHOW BR JOB QUERY" + case BRIEKindCancelJob: + return "CANCEL BR JOB" + case BRIEKindShowBackupMeta: + return "SHOW BACKUP METADATA" + default: + return "" + } +} + +func (kind BRIEOptionType) String() string { + switch kind { + case BRIEOptionRateLimit: + return "RATE_LIMIT" + case BRIEOptionConcurrency: + return "CONCURRENCY" + case BRIEOptionChecksum: + return "CHECKSUM" + case BRIEOptionSendCreds: + return "SEND_CREDENTIALS_TO_TIKV" + case BRIEOptionBackupTimeAgo, BRIEOptionBackupTS, BRIEOptionBackupTSO: + return "SNAPSHOT" + case BRIEOptionLastBackupTS, BRIEOptionLastBackupTSO: + return "LAST_BACKUP" + case BRIEOptionOnline: + return "ONLINE" + case BRIEOptionCheckpoint: + return "CHECKPOINT" + case BRIEOptionAnalyze: + return "ANALYZE" + case BRIEOptionBackend: + return "BACKEND" + case BRIEOptionOnDuplicate: + return "ON_DUPLICATE" + case BRIEOptionSkipSchemaFiles: + return "SKIP_SCHEMA_FILES" + case BRIEOptionStrictFormat: + return "STRICT_FORMAT" + case BRIEOptionTiKVImporter: + return "TIKV_IMPORTER" + case BRIEOptionResume: + return "RESUME" + case BRIEOptionCSVBackslashEscape: + return "CSV_BACKSLASH_ESCAPE" + case BRIEOptionCSVDelimiter: + return "CSV_DELIMITER" + case BRIEOptionCSVHeader: + return "CSV_HEADER" + case BRIEOptionCSVNotNull: + return "CSV_NOT_NULL" + case BRIEOptionCSVNull: + return "CSV_NULL" + case BRIEOptionCSVSeparator: + return "CSV_SEPARATOR" + case BRIEOptionCSVTrimLastSeparators: + return "CSV_TRIM_LAST_SEPARATORS" + case BRIEOptionFullBackupStorage: + return "FULL_BACKUP_STORAGE" + case BRIEOptionRestoredTS: + return "RESTORED_TS" + case BRIEOptionStartTS: + return "START_TS" + case BRIEOptionUntilTS: + return "UNTIL_TS" + case BRIEOptionGCTTL: + return "GC_TTL" + default: + return "" + } +} + +func (level BRIEOptionLevel) String() string { + switch level { + case BRIEOptionLevelOff: + return "OFF" + case BRIEOptionLevelOptional: + return "OPTIONAL" + case BRIEOptionLevelRequired: + return "REQUIRED" + default: + return "" + } +} + +type BRIEOption struct { + Tp BRIEOptionType + StrValue string + UintValue uint64 +} + +func (opt *BRIEOption) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(opt.Tp.String()) + ctx.WritePlain(" = ") + switch opt.Tp { + case BRIEOptionBackupTS, BRIEOptionLastBackupTS, BRIEOptionBackend, BRIEOptionOnDuplicate, BRIEOptionTiKVImporter, BRIEOptionCSVDelimiter, BRIEOptionCSVNull, BRIEOptionCSVSeparator, BRIEOptionFullBackupStorage, BRIEOptionRestoredTS, BRIEOptionStartTS, BRIEOptionUntilTS, BRIEOptionGCTTL: + ctx.WriteString(opt.StrValue) + case BRIEOptionBackupTimeAgo: + ctx.WritePlainf("%d ", opt.UintValue/1000) + ctx.WriteKeyWord("MICROSECOND AGO") + case BRIEOptionRateLimit: + ctx.WritePlainf("%d ", opt.UintValue/1048576) + ctx.WriteKeyWord("MB") + ctx.WritePlain("/") + ctx.WriteKeyWord("SECOND") + case BRIEOptionCSVHeader: + if opt.UintValue == BRIECSVHeaderIsColumns { + ctx.WriteKeyWord("COLUMNS") + } else { + ctx.WritePlainf("%d", opt.UintValue) + } + case BRIEOptionChecksum, BRIEOptionAnalyze: + // BACKUP/RESTORE doesn't support OPTIONAL value for now, should warn at executor + ctx.WriteKeyWord(BRIEOptionLevel(opt.UintValue).String()) + default: + ctx.WritePlainf("%d", opt.UintValue) + } + return nil +} + +// BRIEStmt is a statement for backup, restore, import and export. +type BRIEStmt struct { + stmtNode + + Kind BRIEKind + Schemas []string + Tables []*TableName + Storage string + JobID int64 + Options []*BRIEOption +} + +func (n *BRIEStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*BRIEStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} + +func (n *BRIEStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(n.Kind.String()) + + switch n.Kind { + case BRIEKindRestore, BRIEKindBackup: + switch { + case len(n.Tables) != 0: + ctx.WriteKeyWord(" TABLE ") + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore BRIEStmt.Tables[%d]", index) + } + } + case len(n.Schemas) != 0: + ctx.WriteKeyWord(" DATABASE ") + for index, schema := range n.Schemas { + if index != 0 { + ctx.WritePlain(", ") + } + ctx.WriteName(schema) + } + default: + ctx.WriteKeyWord(" DATABASE") + ctx.WritePlain(" *") + } + + if n.Kind == BRIEKindBackup { + ctx.WriteKeyWord(" TO ") + ctx.WriteString(n.Storage) + } else { + ctx.WriteKeyWord(" FROM ") + ctx.WriteString(n.Storage) + } + case BRIEKindCancelJob, BRIEKindShowJob, BRIEKindShowQuery: + ctx.WritePlainf(" %d", n.JobID) + case BRIEKindStreamStart: + ctx.WriteKeyWord(" TO ") + ctx.WriteString(n.Storage) + case BRIEKindRestorePIT, BRIEKindStreamMetaData, BRIEKindShowBackupMeta, BRIEKindStreamPurge: + ctx.WriteKeyWord(" FROM ") + ctx.WriteString(n.Storage) + } + + for _, opt := range n.Options { + ctx.WritePlain(" ") + if err := opt.Restore(ctx); err != nil { + return err + } + } + + return nil +} + +// RedactURL redacts the secret tokens in the URL. only S3 url need redaction for now. +// if the url is not a valid url, return the original string. +func RedactURL(str string) string { + // FIXME: this solution is not scalable, and duplicates some logic from BR. + u, err := url.Parse(str) + if err != nil { + return str + } + scheme := u.Scheme + failpoint.Inject("forceRedactURL", func() { + scheme = "s3" + }) + if strings.ToLower(scheme) == "s3" { + values := u.Query() + for k := range values { + // see below on why we normalize key + // https://github.com/pingcap/tidb/blob/a7c0d95f16ea2582bb569278c3f829403e6c3a7e/br/pkg/storage/parse.go#L163 + normalizedKey := strings.ToLower(strings.ReplaceAll(k, "_", "-")) + if normalizedKey == "access-key" || normalizedKey == "secret-access-key" || normalizedKey == "session-token" { + values[k] = []string{"xxxxxx"} + } + } + u.RawQuery = values.Encode() + } + return u.String() +} + +// SecureText implements SensitiveStmtNode +func (n *BRIEStmt) SecureText() string { + redactedStmt := &BRIEStmt{ + Kind: n.Kind, + Schemas: n.Schemas, + Tables: n.Tables, + Storage: RedactURL(n.Storage), + Options: n.Options, + } + + var sb strings.Builder + _ = redactedStmt.Restore(format.NewRestoreCtx(format.DefaultRestoreFlags, &sb)) + return sb.String() +} + +type LoadDataActionTp int + +const ( + LoadDataPause LoadDataActionTp = iota + LoadDataResume + LoadDataCancel + LoadDataDrop +) + +// LoadDataActionStmt represent PAUSE/RESUME/CANCEL/DROP LOAD DATA JOB statement. +type LoadDataActionStmt struct { + stmtNode + + Tp LoadDataActionTp + JobID int64 +} + +func (n *LoadDataActionStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + return v.Leave(newNode) +} + +func (n *LoadDataActionStmt) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case LoadDataPause: + ctx.WriteKeyWord("PAUSE LOAD DATA JOB ") + case LoadDataResume: + ctx.WriteKeyWord("RESUME LOAD DATA JOB ") + case LoadDataCancel: + ctx.WriteKeyWord("CANCEL LOAD DATA JOB ") + case LoadDataDrop: + ctx.WriteKeyWord("DROP LOAD DATA JOB ") + default: + return errors.Errorf("invalid load data action type: %d", n.Tp) + } + ctx.WritePlainf("%d", n.JobID) + return nil +} + +type ImportIntoActionTp string + +const ( + ImportIntoCancel ImportIntoActionTp = "cancel" +) + +// ImportIntoActionStmt represent CANCEL IMPORT INTO JOB statement. +// will support pause/resume/drop later. +type ImportIntoActionStmt struct { + stmtNode + + Tp ImportIntoActionTp + JobID int64 +} + +func (n *ImportIntoActionStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + return v.Leave(newNode) +} + +func (n *ImportIntoActionStmt) Restore(ctx *format.RestoreCtx) error { + if n.Tp != ImportIntoCancel { + return errors.Errorf("invalid IMPORT INTO action type: %s", n.Tp) + } + ctx.WriteKeyWord("CANCEL IMPORT JOB ") + ctx.WritePlainf("%d", n.JobID) + return nil +} + +// Ident is the table identifier composed of schema name and table name. +type Ident struct { + Schema model.CIStr + Name model.CIStr +} + +// String implements fmt.Stringer interface. +func (i Ident) String() string { + if i.Schema.O == "" { + return i.Name.O + } + return fmt.Sprintf("%s.%s", i.Schema, i.Name) +} + +// SelectStmtOpts wrap around select hints and switches +type SelectStmtOpts struct { + Distinct bool + SQLBigResult bool + SQLBufferResult bool + SQLCache bool + SQLSmallResult bool + CalcFoundRows bool + StraightJoin bool + Priority mysql.PriorityEnum + TableHints []*TableOptimizerHint + ExplicitAll bool +} + +// TableOptimizerHint is Table level optimizer hint +type TableOptimizerHint struct { + node + // HintName is the name or alias of the table(s) which the hint will affect. + // Table hints has no schema info + // It allows only table name or alias (if table has an alias) + HintName model.CIStr + // HintData is the payload of the hint. The actual type of this field + // is defined differently as according `HintName`. Define as following: + // + // Statement Execution Time Optimizer Hints + // See https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time + // - MAX_EXECUTION_TIME => uint64 + // - MEMORY_QUOTA => int64 + // - QUERY_TYPE => model.CIStr + // + // Time Range is used to hint the time range of inspection tables + // e.g: select /*+ time_range('','') */ * from information_schema.inspection_result. + // - TIME_RANGE => ast.HintTimeRange + // - READ_FROM_STORAGE => model.CIStr + // - USE_TOJA => bool + // - NTH_PLAN => int64 + HintData interface{} + // QBName is the default effective query block of this hint. + QBName model.CIStr + Tables []HintTable + Indexes []model.CIStr +} + +// HintTimeRange is the payload of `TIME_RANGE` hint +type HintTimeRange struct { + From string + To string +} + +// HintSetVar is the payload of `SET_VAR` hint +type HintSetVar struct { + VarName string + Value string +} + +// HintTable is table in the hint. It may have query block info. +type HintTable struct { + DBName model.CIStr + TableName model.CIStr + QBName model.CIStr + PartitionList []model.CIStr +} + +func (ht *HintTable) Restore(ctx *format.RestoreCtx) { + if ht.DBName.L != "" { + ctx.WriteName(ht.DBName.String()) + ctx.WriteKeyWord(".") + } + ctx.WriteName(ht.TableName.String()) + if ht.QBName.L != "" { + ctx.WriteKeyWord("@") + ctx.WriteName(ht.QBName.String()) + } + if len(ht.PartitionList) > 0 { + ctx.WriteKeyWord(" PARTITION") + ctx.WritePlain("(") + for i, p := range ht.PartitionList { + if i > 0 { + ctx.WritePlain(", ") + } + ctx.WriteName(p.String()) + } + ctx.WritePlain(")") + } +} + +// Restore implements Node interface. +func (n *TableOptimizerHint) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(n.HintName.String()) + ctx.WritePlain("(") + if n.QBName.L != "" { + if n.HintName.L != "qb_name" { + ctx.WriteKeyWord("@") + } + ctx.WriteName(n.QBName.String()) + } + if n.HintName.L == "qb_name" && len(n.Tables) == 0 { + ctx.WritePlain(")") + return nil + } + // Hints without args except query block. + switch n.HintName.L { + case "mpp_1phase_agg", "mpp_2phase_agg", "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "ignore_plan_cache", "limit_to_cop", "straight_join", "merge", "no_decorrelate": + ctx.WritePlain(")") + return nil + } + if n.QBName.L != "" { + ctx.WritePlain(" ") + } + // Hints with args except query block. + switch n.HintName.L { + case "max_execution_time": + ctx.WritePlainf("%d", n.HintData.(uint64)) + case "resource_group": + ctx.WriteName(n.HintData.(string)) + case "nth_plan": + ctx.WritePlainf("%d", n.HintData.(int64)) + case "tidb_hj", "tidb_smj", "tidb_inlj", "hash_join", "hash_join_build", "hash_join_probe", "merge_join", "inl_join", "broadcast_join", "shuffle_join", "inl_hash_join", "inl_merge_join", "leading": + for i, table := range n.Tables { + if i != 0 { + ctx.WritePlain(", ") + } + table.Restore(ctx) + } + case "use_index", "ignore_index", "use_index_merge", "force_index", "order_index", "no_order_index": + n.Tables[0].Restore(ctx) + ctx.WritePlain(" ") + for i, index := range n.Indexes { + if i != 0 { + ctx.WritePlain(", ") + } + ctx.WriteName(index.String()) + } + case "qb_name": + if len(n.Tables) > 0 { + ctx.WritePlain(", ") + for i, table := range n.Tables { + if i != 0 { + ctx.WritePlain(". ") + } + table.Restore(ctx) + } + } + case "use_toja", "use_cascades": + if n.HintData.(bool) { + ctx.WritePlain("TRUE") + } else { + ctx.WritePlain("FALSE") + } + case "query_type": + ctx.WriteKeyWord(n.HintData.(model.CIStr).String()) + case "memory_quota": + ctx.WritePlainf("%d MB", n.HintData.(int64)/1024/1024) + case "read_from_storage": + ctx.WriteKeyWord(n.HintData.(model.CIStr).String()) + for i, table := range n.Tables { + if i == 0 { + ctx.WritePlain("[") + } + table.Restore(ctx) + if i == len(n.Tables)-1 { + ctx.WritePlain("]") + } else { + ctx.WritePlain(", ") + } + } + case "time_range": + hintData := n.HintData.(HintTimeRange) + ctx.WriteString(hintData.From) + ctx.WritePlain(", ") + ctx.WriteString(hintData.To) + case "set_var": + hintData := n.HintData.(HintSetVar) + ctx.WritePlain(hintData.VarName) + ctx.WritePlain(" = ") + ctx.WritePlain(hintData.Value) + } + ctx.WritePlain(")") + return nil +} + +// Accept implements Node Accept interface. +func (n *TableOptimizerHint) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*TableOptimizerHint) + return v.Leave(n) +} + +// TextString represent a string, it can be a binary literal. +type TextString struct { + Value string + IsBinaryLiteral bool +} + +type BinaryLiteral interface { + ToString() string +} + +// NewDecimal creates a types.Decimal value, it's provided by parser driver. +var NewDecimal func(string) (interface{}, error) + +// NewHexLiteral creates a types.HexLiteral value, it's provided by parser driver. +var NewHexLiteral func(string) (interface{}, error) + +// NewBitLiteral creates a types.BitLiteral value, it's provided by parser driver. +var NewBitLiteral func(string) (interface{}, error) + +// SetResourceGroupStmt is a statement to set the resource group name for current session. +type SetResourceGroupStmt struct { + stmtNode + Name model.CIStr +} + +func (n *SetResourceGroupStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("SET RESOURCE GROUP ") + ctx.WriteName(n.Name.O) + return nil +} + +// Accept implements Node Accept interface. +func (n *SetResourceGroupStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*SetResourceGroupStmt) + return v.Leave(n) +} + +// CalibrateResourceType is the type for CalibrateResource statement. +type CalibrateResourceType int + +// calibrate resource [ workload < TPCC | OLTP_READ_WRITE | OLTP_READ_ONLY | OLTP_WRITE_ONLY | TPCH_10> ] +const ( + WorkloadNone CalibrateResourceType = iota + TPCC + OLTPREADWRITE + OLTPREADONLY + OLTPWRITEONLY + TPCH10 +) + +func (n CalibrateResourceType) Restore(ctx *format.RestoreCtx) error { + switch n { + case TPCC: + ctx.WriteKeyWord(" WORKLOAD TPCC") + case OLTPREADWRITE: + ctx.WriteKeyWord(" WORKLOAD OLTP_READ_WRITE") + case OLTPREADONLY: + ctx.WriteKeyWord(" WORKLOAD OLTP_READ_ONLY") + case OLTPWRITEONLY: + ctx.WriteKeyWord(" WORKLOAD OLTP_WRITE_ONLY") + case TPCH10: + ctx.WriteKeyWord(" WORKLOAD TPCH_10") + } + return nil +} + +// CalibrateResourceStmt is a statement to fetch the cluster RU capacity +type CalibrateResourceStmt struct { + stmtNode + DynamicCalibrateResourceOptionList []*DynamicCalibrateResourceOption + Tp CalibrateResourceType +} + +// Restore implements Node interface. +func (n *CalibrateResourceStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("CALIBRATE RESOURCE") + if err := n.Tp.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore CalibrateResourceStmt.CalibrateResourceType") + } + for i, option := range n.DynamicCalibrateResourceOptionList { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing DynamicCalibrateResourceOption: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *CalibrateResourceStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*CalibrateResourceStmt) + for _, val := range n.DynamicCalibrateResourceOptionList { + _, ok := val.Accept(v) + if !ok { + return n, false + } + } + return v.Leave(n) +} + +type DynamicCalibrateType int + +const ( + // specific time + CalibrateStartTime = iota + CalibrateEndTime + CalibrateDuration +) + +type DynamicCalibrateResourceOption struct { + stmtNode + Tp DynamicCalibrateType + StrValue string + Ts ExprNode + Unit TimeUnitType +} + +func (n *DynamicCalibrateResourceOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case CalibrateStartTime: + ctx.WriteKeyWord("START_TIME ") + if err := n.Ts.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing DynamicCalibrateResourceOption StartTime") + } + case CalibrateEndTime: + ctx.WriteKeyWord("END_TIME ") + if err := n.Ts.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while splicing DynamicCalibrateResourceOption EndTime") + } + case CalibrateDuration: + ctx.WriteKeyWord("DURATION ") + if len(n.StrValue) > 0 { + ctx.WriteString(n.StrValue) + } else { + ctx.WriteKeyWord("INTERVAL ") + if err := n.Ts.Restore(ctx); err != nil { + return errors.Annotate(err, "An error occurred while restore DynamicCalibrateResourceOption DURATION TS") + } + ctx.WritePlain(" ") + ctx.WriteKeyWord(n.Unit.String()) + } + default: + return errors.Errorf("invalid DynamicCalibrateResourceOption: %d", n.Tp) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *DynamicCalibrateResourceOption) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DynamicCalibrateResourceOption) + if n.Ts != nil { + node, ok := n.Ts.Accept(v) + if !ok { + return n, false + } + n.Ts = node.(ExprNode) + } + return v.Leave(n) +} + +// DropQueryWatchStmt is a statement to drop a runaway watch item. +type DropQueryWatchStmt struct { + stmtNode + IntValue int64 +} + +func (n *DropQueryWatchStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("QUERY WATCH REMOVE ") + ctx.WritePlainf("%d", n.IntValue) + return nil +} + +// Accept implements Node Accept interface. +func (n *DropQueryWatchStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + n = newNode.(*DropQueryWatchStmt) + return v.Leave(n) +} + +// AddQueryWatchStmt is a statement to add a runaway watch item. +type AddQueryWatchStmt struct { + stmtNode + QueryWatchOptionList []*QueryWatchOption +} + +func (n *AddQueryWatchStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("QUERY WATCH ADD") + for i, option := range n.QueryWatchOptionList { + ctx.WritePlain(" ") + if err := option.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing QueryWatchOptionList: [%v]", i) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AddQueryWatchStmt) Accept(v Visitor) (Node, bool) { + newNode, _ := v.Enter(n) + n = newNode.(*AddQueryWatchStmt) + for _, val := range n.QueryWatchOptionList { + _, ok := val.Accept(v) + if !ok { + return n, false + } + } + return v.Leave(n) +} + +type QueryWatchOptionType int + +const ( + QueryWatchResourceGroup QueryWatchOptionType = iota + QueryWatchAction + QueryWatchType +) + +// QueryWatchOption is used for parsing manual management of watching runaway queries option. +type QueryWatchOption struct { + stmtNode + Tp QueryWatchOptionType + StrValue model.CIStr + IntValue int32 + ExprValue ExprNode + BoolValue bool +} + +func (n *QueryWatchOption) Restore(ctx *format.RestoreCtx) error { + switch n.Tp { + case QueryWatchResourceGroup: + ctx.WriteKeyWord("RESOURCE GROUP ") + if n.ExprValue != nil { + if err := n.ExprValue.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing ExprValue: [%v]", n.ExprValue) + } + } else { + ctx.WriteName(n.StrValue.O) + } + case QueryWatchAction: + ctx.WriteKeyWord("ACTION ") + ctx.WritePlain("= ") + ctx.WriteKeyWord(model.RunawayActionType(n.IntValue).String()) + case QueryWatchType: + if n.BoolValue { + ctx.WriteKeyWord("SQL TEXT ") + ctx.WriteKeyWord(model.RunawayWatchType(n.IntValue).String()) + ctx.WriteKeyWord(" TO ") + } else { + switch n.IntValue { + case int32(model.WatchSimilar): + ctx.WriteKeyWord("SQL DIGEST ") + case int32(model.WatchPlan): + ctx.WriteKeyWord("PLAN DIGEST ") + } + } + if err := n.ExprValue.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while splicing ExprValue: [%v]", n.ExprValue) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *QueryWatchOption) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*QueryWatchOption) + if n.ExprValue != nil { + node, ok := n.ExprValue.Accept(v) + if !ok { + return n, false + } + n.ExprValue = node.(ExprNode) + } + return v.Leave(n) +} + +func CheckQueryWatchAppend(ops []*QueryWatchOption, newOp *QueryWatchOption) bool { + for _, op := range ops { + if op.Tp == newOp.Tp { + return false + } + } + return true +} diff --git a/pkg/parser/ast/misc_test.go b/pkg/parser/ast/misc_test.go new file mode 100644 index 0000000000000..e284704cefa7c --- /dev/null +++ b/pkg/parser/ast/misc_test.go @@ -0,0 +1,426 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast_test + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/stretchr/testify/require" +) + +type visitor struct{} + +func (v visitor) Enter(in ast.Node) (ast.Node, bool) { + return in, false +} + +func (v visitor) Leave(in ast.Node) (ast.Node, bool) { + return in, true +} + +type visitor1 struct { + visitor +} + +func (visitor1) Enter(in ast.Node) (ast.Node, bool) { + return in, true +} + +func TestMiscVisitorCover(t *testing.T) { + valueExpr := ast.NewValueExpr(42, mysql.DefaultCharset, mysql.DefaultCollationName) + stmts := []ast.Node{ + &ast.AdminStmt{}, + &ast.AlterUserStmt{}, + &ast.BeginStmt{}, + &ast.BinlogStmt{}, + &ast.CommitStmt{}, + &ast.CompactTableStmt{Table: &ast.TableName{}}, + &ast.CreateUserStmt{}, + &ast.DeallocateStmt{}, + &ast.DoStmt{}, + &ast.ExecuteStmt{UsingVars: []ast.ExprNode{valueExpr}}, + &ast.ExplainStmt{Stmt: &ast.ShowStmt{}}, + &ast.GrantStmt{}, + &ast.PrepareStmt{SQLVar: &ast.VariableExpr{Value: valueExpr}}, + &ast.RollbackStmt{}, + &ast.SetPwdStmt{}, + &ast.SetStmt{Variables: []*ast.VariableAssignment{ + { + Value: valueExpr, + }, + }}, + &ast.UseStmt{}, + &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{ + {}, + }, + }, + &ast.FlushStmt{}, + &ast.PrivElem{}, + &ast.VariableAssignment{Value: valueExpr}, + &ast.KillStmt{}, + &ast.DropStatsStmt{ + Tables: []*ast.TableName{ + {}, + }, + }, + &ast.ShutdownStmt{}, + } + + for _, v := range stmts { + v.Accept(visitor{}) + v.Accept(visitor1{}) + } +} + +func TestDDLVisitorCoverMisc(t *testing.T) { + sql := ` +create table t (c1 smallint unsigned, c2 int unsigned); +alter table t add column a smallint unsigned after b; +alter table t add column (a int, constraint check (a > 0)); +create index t_i on t (id); +create database test character set utf8; +drop database test; +drop index t_i on t; +drop table t; +truncate t; +create table t ( +jobAbbr char(4) not null, +constraint foreign key (jobabbr) references ffxi_jobtype (jobabbr) on delete cascade on update cascade +); +` + parse := parser.New() + stmts, _, err := parse.Parse(sql, "", "") + require.NoError(t, err) + for _, stmt := range stmts { + stmt.Accept(visitor{}) + stmt.Accept(visitor1{}) + } +} + +func TestDMLVistorCover(t *testing.T) { + sql := `delete from somelog where user = 'jcole' order by timestamp_column limit 1; +delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id; +select * from t where exists(select * from t k where t.c = k.c having sum(c) = 1); +insert into t_copy select * from t where t.x > 5; +(select /*+ TIDB_INLJ(t1) */ a from t1 where a=10 and b=1) union (select /*+ TIDB_SMJ(t2) */ a from t2 where a=11 and b=2) order by a limit 10; +update t1 set col1 = col1 + 1, col2 = col1; +show create table t; +load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'; +import into t from '/file.csv'` + + p := parser.New() + stmts, _, err := p.Parse(sql, "", "") + require.NoError(t, err) + for _, stmt := range stmts { + stmt.Accept(visitor{}) + stmt.Accept(visitor1{}) + } +} + +// test Change Pump or drainer status sql parser +func TestChangeStmt(t *testing.T) { + sql := `change pump to node_state='paused' for node_id '127.0.0.1:8249'; +change drainer to node_state='paused' for node_id '127.0.0.1:8249'; +shutdown;` + + p := parser.New() + stmts, _, err := p.Parse(sql, "", "") + require.NoError(t, err) + for _, stmt := range stmts { + stmt.Accept(visitor{}) + stmt.Accept(visitor1{}) + } +} + +func TestSensitiveStatement(t *testing.T) { + positive := []ast.StmtNode{ + &ast.SetPwdStmt{}, + &ast.CreateUserStmt{}, + &ast.AlterUserStmt{}, + &ast.GrantStmt{}, + } + for i, stmt := range positive { + _, ok := stmt.(ast.SensitiveStmtNode) + require.Truef(t, ok, "%d, %#v fail", i, stmt) + } + + negative := []ast.StmtNode{ + &ast.DropUserStmt{}, + &ast.RevokeStmt{}, + &ast.AlterTableStmt{}, + &ast.CreateDatabaseStmt{}, + &ast.CreateIndexStmt{}, + &ast.CreateTableStmt{}, + &ast.DropDatabaseStmt{}, + &ast.DropIndexStmt{}, + &ast.DropTableStmt{}, + &ast.RenameTableStmt{}, + &ast.TruncateTableStmt{}, + } + for _, stmt := range negative { + _, ok := stmt.(ast.SensitiveStmtNode) + require.False(t, ok) + } +} + +func TestUserSpec(t *testing.T) { + hashString := "*3D56A309CD04FA2EEF181462E59011F075C89548" + u := ast.UserSpec{ + User: &auth.UserIdentity{ + Username: "test", + }, + AuthOpt: &ast.AuthOption{ + ByAuthString: false, + AuthString: "xxx", + HashString: hashString, + }, + } + pwd, ok := u.EncodedPassword() + require.True(t, ok) + require.Equal(t, u.AuthOpt.HashString, pwd) + + u.AuthOpt.HashString = "not-good-password-format" + _, ok = u.EncodedPassword() + require.False(t, ok) + + u.AuthOpt.ByAuthString = true + pwd, ok = u.EncodedPassword() + require.True(t, ok) + require.Equal(t, hashString, pwd) + + u.AuthOpt.AuthString = "" + pwd, ok = u.EncodedPassword() + require.True(t, ok) + require.Equal(t, "", pwd) +} + +func TestTableOptimizerHintRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"USE_INDEX(t1 c1)", "USE_INDEX(`t1` `c1`)"}, + {"USE_INDEX(test.t1 c1)", "USE_INDEX(`test`.`t1` `c1`)"}, + {"USE_INDEX(@sel_1 t1 c1)", "USE_INDEX(@`sel_1` `t1` `c1`)"}, + {"USE_INDEX(t1@sel_1 c1)", "USE_INDEX(`t1`@`sel_1` `c1`)"}, + {"USE_INDEX(test.t1@sel_1 c1)", "USE_INDEX(`test`.`t1`@`sel_1` `c1`)"}, + {"USE_INDEX(test.t1@sel_1 partition(p0) c1)", "USE_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, + {"FORCE_INDEX(t1 c1)", "FORCE_INDEX(`t1` `c1`)"}, + {"FORCE_INDEX(test.t1 c1)", "FORCE_INDEX(`test`.`t1` `c1`)"}, + {"FORCE_INDEX(@sel_1 t1 c1)", "FORCE_INDEX(@`sel_1` `t1` `c1`)"}, + {"FORCE_INDEX(t1@sel_1 c1)", "FORCE_INDEX(`t1`@`sel_1` `c1`)"}, + {"FORCE_INDEX(test.t1@sel_1 c1)", "FORCE_INDEX(`test`.`t1`@`sel_1` `c1`)"}, + {"FORCE_INDEX(test.t1@sel_1 partition(p0) c1)", "FORCE_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, + {"IGNORE_INDEX(t1 c1)", "IGNORE_INDEX(`t1` `c1`)"}, + {"IGNORE_INDEX(@sel_1 t1 c1)", "IGNORE_INDEX(@`sel_1` `t1` `c1`)"}, + {"IGNORE_INDEX(t1@sel_1 c1)", "IGNORE_INDEX(`t1`@`sel_1` `c1`)"}, + {"IGNORE_INDEX(t1@sel_1 partition(p0, p1) c1)", "IGNORE_INDEX(`t1`@`sel_1` PARTITION(`p0`, `p1`) `c1`)"}, + {"ORDER_INDEX(t1 c1)", "ORDER_INDEX(`t1` `c1`)"}, + {"ORDER_INDEX(test.t1 c1)", "ORDER_INDEX(`test`.`t1` `c1`)"}, + {"ORDER_INDEX(@sel_1 t1 c1)", "ORDER_INDEX(@`sel_1` `t1` `c1`)"}, + {"ORDER_INDEX(t1@sel_1 c1)", "ORDER_INDEX(`t1`@`sel_1` `c1`)"}, + {"ORDER_INDEX(test.t1@sel_1 c1)", "ORDER_INDEX(`test`.`t1`@`sel_1` `c1`)"}, + {"ORDER_INDEX(test.t1@sel_1 partition(p0) c1)", "ORDER_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, + {"NO_ORDER_INDEX(t1 c1)", "NO_ORDER_INDEX(`t1` `c1`)"}, + {"NO_ORDER_INDEX(test.t1 c1)", "NO_ORDER_INDEX(`test`.`t1` `c1`)"}, + {"NO_ORDER_INDEX(@sel_1 t1 c1)", "NO_ORDER_INDEX(@`sel_1` `t1` `c1`)"}, + {"NO_ORDER_INDEX(t1@sel_1 c1)", "NO_ORDER_INDEX(`t1`@`sel_1` `c1`)"}, + {"NO_ORDER_INDEX(test.t1@sel_1 c1)", "NO_ORDER_INDEX(`test`.`t1`@`sel_1` `c1`)"}, + {"NO_ORDER_INDEX(test.t1@sel_1 partition(p0) c1)", "NO_ORDER_INDEX(`test`.`t1`@`sel_1` PARTITION(`p0`) `c1`)"}, + {"TIDB_SMJ(`t1`)", "TIDB_SMJ(`t1`)"}, + {"TIDB_SMJ(t1)", "TIDB_SMJ(`t1`)"}, + {"TIDB_SMJ(t1,t2)", "TIDB_SMJ(`t1`, `t2`)"}, + {"TIDB_SMJ(@sel1 t1,t2)", "TIDB_SMJ(@`sel1` `t1`, `t2`)"}, + {"TIDB_SMJ(t1@sel1,t2@sel2)", "TIDB_SMJ(`t1`@`sel1`, `t2`@`sel2`)"}, + {"TIDB_INLJ(t1,t2)", "TIDB_INLJ(`t1`, `t2`)"}, + {"TIDB_INLJ(@sel1 t1,t2)", "TIDB_INLJ(@`sel1` `t1`, `t2`)"}, + {"TIDB_INLJ(t1@sel1,t2@sel2)", "TIDB_INLJ(`t1`@`sel1`, `t2`@`sel2`)"}, + {"TIDB_HJ(t1,t2)", "TIDB_HJ(`t1`, `t2`)"}, + {"TIDB_HJ(@sel1 t1,t2)", "TIDB_HJ(@`sel1` `t1`, `t2`)"}, + {"TIDB_HJ(t1@sel1,t2@sel2)", "TIDB_HJ(`t1`@`sel1`, `t2`@`sel2`)"}, + {"MERGE_JOIN(t1,t2)", "MERGE_JOIN(`t1`, `t2`)"}, + {"BROADCAST_JOIN(t1,t2)", "BROADCAST_JOIN(`t1`, `t2`)"}, + {"INL_HASH_JOIN(t1,t2)", "INL_HASH_JOIN(`t1`, `t2`)"}, + {"INL_MERGE_JOIN(t1,t2)", "INL_MERGE_JOIN(`t1`, `t2`)"}, + {"INL_JOIN(t1,t2)", "INL_JOIN(`t1`, `t2`)"}, + {"HASH_JOIN(t1,t2)", "HASH_JOIN(`t1`, `t2`)"}, + {"HASH_JOIN_BUILD(t1)", "HASH_JOIN_BUILD(`t1`)"}, + {"HASH_JOIN_PROBE(t1)", "HASH_JOIN_PROBE(`t1`)"}, + {"LEADING(t1)", "LEADING(`t1`)"}, + {"LEADING(t1, c1)", "LEADING(`t1`, `c1`)"}, + {"LEADING(t1, c1, t2)", "LEADING(`t1`, `c1`, `t2`)"}, + {"LEADING(@sel1 t1, c1)", "LEADING(@`sel1` `t1`, `c1`)"}, + {"LEADING(@sel1 t1)", "LEADING(@`sel1` `t1`)"}, + {"LEADING(@sel1 t1, c1, t2)", "LEADING(@`sel1` `t1`, `c1`, `t2`)"}, + {"LEADING(t1@sel1)", "LEADING(`t1`@`sel1`)"}, + {"LEADING(t1@sel1, c1)", "LEADING(`t1`@`sel1`, `c1`)"}, + {"LEADING(t1@sel1, c1, t2)", "LEADING(`t1`@`sel1`, `c1`, `t2`)"}, + {"MAX_EXECUTION_TIME(3000)", "MAX_EXECUTION_TIME(3000)"}, + {"MAX_EXECUTION_TIME(@sel1 3000)", "MAX_EXECUTION_TIME(@`sel1` 3000)"}, + {"USE_INDEX_MERGE(t1 c1)", "USE_INDEX_MERGE(`t1` `c1`)"}, + {"USE_INDEX_MERGE(@sel1 t1 c1)", "USE_INDEX_MERGE(@`sel1` `t1` `c1`)"}, + {"USE_INDEX_MERGE(t1@sel1 c1)", "USE_INDEX_MERGE(`t1`@`sel1` `c1`)"}, + {"USE_TOJA(TRUE)", "USE_TOJA(TRUE)"}, + {"USE_TOJA(FALSE)", "USE_TOJA(FALSE)"}, + {"USE_TOJA(@sel1 TRUE)", "USE_TOJA(@`sel1` TRUE)"}, + {"USE_CASCADES(TRUE)", "USE_CASCADES(TRUE)"}, + {"USE_CASCADES(FALSE)", "USE_CASCADES(FALSE)"}, + {"USE_CASCADES(@sel1 TRUE)", "USE_CASCADES(@`sel1` TRUE)"}, + {"QUERY_TYPE(OLAP)", "QUERY_TYPE(OLAP)"}, + {"QUERY_TYPE(OLTP)", "QUERY_TYPE(OLTP)"}, + {"QUERY_TYPE(@sel1 OLTP)", "QUERY_TYPE(@`sel1` OLTP)"}, + {"NTH_PLAN(10)", "NTH_PLAN(10)"}, + {"NTH_PLAN(@sel1 30)", "NTH_PLAN(@`sel1` 30)"}, + {"MEMORY_QUOTA(1 GB)", "MEMORY_QUOTA(1024 MB)"}, + {"MEMORY_QUOTA(@sel1 1 GB)", "MEMORY_QUOTA(@`sel1` 1024 MB)"}, + {"HASH_AGG()", "HASH_AGG()"}, + {"HASH_AGG(@sel1)", "HASH_AGG(@`sel1`)"}, + {"STREAM_AGG()", "STREAM_AGG()"}, + {"STREAM_AGG(@sel1)", "STREAM_AGG(@`sel1`)"}, + {"AGG_TO_COP()", "AGG_TO_COP()"}, + {"AGG_TO_COP(@sel_1)", "AGG_TO_COP(@`sel_1`)"}, + {"LIMIT_TO_COP()", "LIMIT_TO_COP()"}, + {"MERGE()", "MERGE()"}, + {"STRAIGHT_JOIN()", "STRAIGHT_JOIN()"}, + {"NO_INDEX_MERGE()", "NO_INDEX_MERGE()"}, + {"NO_INDEX_MERGE(@sel1)", "NO_INDEX_MERGE(@`sel1`)"}, + {"READ_CONSISTENT_REPLICA()", "READ_CONSISTENT_REPLICA()"}, + {"READ_CONSISTENT_REPLICA(@sel1)", "READ_CONSISTENT_REPLICA(@`sel1`)"}, + {"QB_NAME(sel1)", "QB_NAME(`sel1`)"}, + {"READ_FROM_STORAGE(@sel TIFLASH[t1, t2])", "READ_FROM_STORAGE(@`sel` TIFLASH[`t1`, `t2`])"}, + {"READ_FROM_STORAGE(@sel TIFLASH[t1 partition(p0)])", "READ_FROM_STORAGE(@`sel` TIFLASH[`t1` PARTITION(`p0`)])"}, + {"TIME_RANGE('2020-02-02 10:10:10','2020-02-02 11:10:10')", "TIME_RANGE('2020-02-02 10:10:10', '2020-02-02 11:10:10')"}, + {"RESOURCE_GROUP(rg1)", "RESOURCE_GROUP(`rg1`)"}, + {"RESOURCE_GROUP(`default`)", "RESOURCE_GROUP(`default`)"}, + } + extractNodeFunc := func(node ast.Node) ast.Node { + return node.(*ast.SelectStmt).TableHints[0] + } + runNodeRestoreTest(t, testCases, "select /*+ %s */ * from t1 join t2", extractNodeFunc) +} + +func TestChangeStmtRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'", "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'"}, + {"CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'", "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'"}, + } + extractNodeFunc := func(node ast.Node) ast.Node { + return node.(*ast.ChangeStmt) + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestBRIESecureText(t *testing.T) { + testCases := []struct { + input string + secured string + }{ + { + input: "restore database * from 'local:///tmp/br01' snapshot = 23333", + secured: `^\QRESTORE DATABASE * FROM 'local:///tmp/br01' SNAPSHOT = 23333\E$`, + }, + { + input: "backup database * to 's3://bucket/prefix?region=us-west-2'", + secured: `^\QBACKUP DATABASE * TO 's3://bucket/prefix?region=us-west-2'\E$`, + }, + { + // we need to use regexp to match to avoid the random ordering since a map was used. + // unfortunately Go's regexp doesn't support lookahead assertion, so the test case below + // has false positives. + input: "backup database * to 's3://bucket/prefix?access-key=abcdefghi&secret-access-key=123&force-path-style=true'", + secured: `^\QBACKUP DATABASE * TO 's3://bucket/prefix?\E((access-key=xxxxxx|force-path-style=true|secret-access-key=xxxxxx)(&|'$)){3}`, + }, + { + input: "backup database * to 'gcs://bucket/prefix?access-key=irrelevant&credentials-file=/home/user/secrets.txt'", + secured: `^\QBACKUP DATABASE * TO 'gcs://bucket/prefix?\E((access-key=irrelevant|credentials-file=/home/user/secrets\.txt)(&|'$)){2}`, + }, + } + + p := parser.New() + for _, tc := range testCases { + comment := fmt.Sprintf("input = %s", tc.input) + node, err := p.ParseOneStmt(tc.input, "", "") + require.NoError(t, err, comment) + n, ok := node.(ast.SensitiveStmtNode) + require.True(t, ok, comment) + require.Regexp(t, tc.secured, n.SecureText(), comment) + } +} + +func TestCompactTableStmtRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"alter table abc compact tiflash replica", "ALTER TABLE `abc` COMPACT TIFLASH REPLICA"}, + {"alter table abc compact", "ALTER TABLE `abc` COMPACT"}, + {"alter table test.abc compact", "ALTER TABLE `test`.`abc` COMPACT"}, + } + extractNodeFunc := func(node ast.Node) ast.Node { + return node.(*ast.CompactTableStmt) + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestPlanReplayerStmtRestore(t *testing.T) { + testCases := []NodeRestoreTestCase{ + {"plan replayer dump with stats as of timestamp '2023-06-28 12:34:00' explain select * from t where a > 10", + "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP _UTF8MB4'2023-06-28 12:34:00' EXPLAIN SELECT * FROM `t` WHERE `a`>10"}, + {"plan replayer dump explain analyze select * from t where a > 10", + "PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, + {"plan replayer dump with stats as of timestamp 12345 explain analyze select * from t where a > 10", + "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP 12345 EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, + {"plan replayer dump explain analyze 'test'", + "PLAN REPLAYER DUMP EXPLAIN ANALYZE 'test'"}, + {"plan replayer dump with stats as of timestamp '12345' explain analyze 'test2'", + "PLAN REPLAYER DUMP WITH STATS AS OF TIMESTAMP _UTF8MB4'12345' EXPLAIN ANALYZE 'test2'"}, + } + extractNodeFunc := func(node ast.Node) ast.Node { + return node.(*ast.PlanReplayerStmt) + } + runNodeRestoreTest(t, testCases, "%s", extractNodeFunc) +} + +func TestRedactURL(t *testing.T) { + type args struct { + str string + } + tests := []struct { + args args + want string + }{ + {args{""}, ""}, + {args{":"}, ":"}, + {args{"~/file"}, "~/file"}, + {args{"gs://bucket/file"}, "gs://bucket/file"}, + // gs don't have access-key/secret-access-key, so it will NOT be redacted + {args{"gs://bucket/file?access-key=123"}, "gs://bucket/file?access-key=123"}, + {args{"gs://bucket/file?secret-access-key=123"}, "gs://bucket/file?secret-access-key=123"}, + {args{"s3://bucket/file"}, "s3://bucket/file"}, + {args{"s3://bucket/file?other-key=123"}, "s3://bucket/file?other-key=123"}, + {args{"s3://bucket/file?access-key=123"}, "s3://bucket/file?access-key=xxxxxx"}, + {args{"s3://bucket/file?secret-access-key=123"}, "s3://bucket/file?secret-access-key=xxxxxx"}, + // underline + {args{"s3://bucket/file?access_key=123"}, "s3://bucket/file?access_key=xxxxxx"}, + {args{"s3://bucket/file?secret_access_key=123"}, "s3://bucket/file?secret_access_key=xxxxxx"}, + } + for _, tt := range tests { + t.Run(tt.args.str, func(t *testing.T) { + got := ast.RedactURL(tt.args.str) + if got != tt.want { + t.Errorf("RedactURL() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/parser/ast/procedure.go b/pkg/parser/ast/procedure.go similarity index 99% rename from parser/ast/procedure.go rename to pkg/parser/ast/procedure.go index 755e9c43e515d..46b004bc02c3b 100644 --- a/parser/ast/procedure.go +++ b/pkg/parser/ast/procedure.go @@ -17,8 +17,8 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/types" ) var ( diff --git a/parser/ast/procedure_test.go b/pkg/parser/ast/procedure_test.go similarity index 99% rename from parser/ast/procedure_test.go rename to pkg/parser/ast/procedure_test.go index 93ec611e7743f..3fe3c8f4bcfba 100644 --- a/parser/ast/procedure_test.go +++ b/pkg/parser/ast/procedure_test.go @@ -17,8 +17,8 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/pkg/parser/ast/stats.go b/pkg/parser/ast/stats.go new file mode 100644 index 0000000000000..38135457ed5c4 --- /dev/null +++ b/pkg/parser/ast/stats.go @@ -0,0 +1,346 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" +) + +var ( + _ StmtNode = &AnalyzeTableStmt{} + _ StmtNode = &DropStatsStmt{} + _ StmtNode = &LoadStatsStmt{} +) + +// AnalyzeTableStmt is used to create table statistics. +type AnalyzeTableStmt struct { + stmtNode + + TableNames []*TableName + PartitionNames []model.CIStr + IndexNames []model.CIStr + AnalyzeOpts []AnalyzeOpt + + // IndexFlag is true when we only analyze indices for a table. + IndexFlag bool + Incremental bool + // HistogramOperation is set in "ANALYZE TABLE ... UPDATE/DROP HISTOGRAM ..." statement. + HistogramOperation HistogramOperationType + // ColumnNames indicate the columns whose statistics need to be collected. + ColumnNames []model.CIStr + ColumnChoice model.ColumnChoice +} + +// AnalyzeOptType is the type for analyze options. +type AnalyzeOptionType int + +// Analyze option types. +const ( + AnalyzeOptNumBuckets = iota + AnalyzeOptNumTopN + AnalyzeOptCMSketchDepth + AnalyzeOptCMSketchWidth + AnalyzeOptNumSamples + AnalyzeOptSampleRate +) + +// AnalyzeOptionString stores the string form of analyze options. +var AnalyzeOptionString = map[AnalyzeOptionType]string{ + AnalyzeOptNumBuckets: "BUCKETS", + AnalyzeOptNumTopN: "TOPN", + AnalyzeOptCMSketchWidth: "CMSKETCH WIDTH", + AnalyzeOptCMSketchDepth: "CMSKETCH DEPTH", + AnalyzeOptNumSamples: "SAMPLES", + AnalyzeOptSampleRate: "SAMPLERATE", +} + +// HistogramOperationType is the type for histogram operation. +type HistogramOperationType int + +// Histogram operation types. +const ( + // HistogramOperationNop shows no operation in histogram. Default value. + HistogramOperationNop HistogramOperationType = iota + HistogramOperationUpdate + HistogramOperationDrop +) + +// String implements fmt.Stringer for HistogramOperationType. +func (hot HistogramOperationType) String() string { + switch hot { + case HistogramOperationUpdate: + return "UPDATE HISTOGRAM" + case HistogramOperationDrop: + return "DROP HISTOGRAM" + } + return "" +} + +// AnalyzeOpt stores the analyze option type and value. +type AnalyzeOpt struct { + Type AnalyzeOptionType + Value ValueExpr +} + +// Restore implements Node interface. +func (n *AnalyzeTableStmt) Restore(ctx *format.RestoreCtx) error { + if n.Incremental { + ctx.WriteKeyWord("ANALYZE INCREMENTAL TABLE ") + } else { + ctx.WriteKeyWord("ANALYZE TABLE ") + } + for i, table := range n.TableNames { + if i != 0 { + ctx.WritePlain(",") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore AnalyzeTableStmt.TableNames[%d]", i) + } + } + if len(n.PartitionNames) != 0 { + ctx.WriteKeyWord(" PARTITION ") + } + for i, partition := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(partition.O) + } + if n.HistogramOperation != HistogramOperationNop { + ctx.WritePlain(" ") + ctx.WriteKeyWord(n.HistogramOperation.String()) + ctx.WritePlain(" ") + if len(n.ColumnNames) > 0 { + ctx.WriteKeyWord("ON ") + for i, columnName := range n.ColumnNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(columnName.O) + } + } + } + switch n.ColumnChoice { + case model.AllColumns: + ctx.WriteKeyWord(" ALL COLUMNS") + case model.PredicateColumns: + ctx.WriteKeyWord(" PREDICATE COLUMNS") + case model.ColumnList: + ctx.WriteKeyWord(" COLUMNS ") + for i, columnName := range n.ColumnNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(columnName.O) + } + } + if n.IndexFlag { + ctx.WriteKeyWord(" INDEX") + } + for i, index := range n.IndexNames { + if i != 0 { + ctx.WritePlain(",") + } else { + ctx.WritePlain(" ") + } + ctx.WriteName(index.O) + } + if len(n.AnalyzeOpts) != 0 { + ctx.WriteKeyWord(" WITH") + for i, opt := range n.AnalyzeOpts { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WritePlainf(" %v ", opt.Value.GetValue()) + ctx.WritePlain(AnalyzeOptionString[opt.Type]) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *AnalyzeTableStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*AnalyzeTableStmt) + for i, val := range n.TableNames { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.TableNames[i] = node.(*TableName) + } + return v.Leave(n) +} + +// DropStatsStmt is used to drop table statistics. +// if the PartitionNames is not empty, or IsGlobalStats is true, it will contain exactly one table +type DropStatsStmt struct { + stmtNode + + Tables []*TableName + PartitionNames []model.CIStr + IsGlobalStats bool +} + +// Restore implements Node interface. +func (n *DropStatsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("DROP STATS ") + + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore DropStatsStmt.Tables[%d]", index) + } + } + + if n.IsGlobalStats { + ctx.WriteKeyWord(" GLOBAL") + return nil + } + + if len(n.PartitionNames) != 0 { + ctx.WriteKeyWord(" PARTITION ") + } + for i, partition := range n.PartitionNames { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteName(partition.O) + } + return nil +} + +// Accept implements Node Accept interface. +func (n *DropStatsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*DropStatsStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} + +// LoadStatsStmt is the statement node for loading statistic. +type LoadStatsStmt struct { + stmtNode + + Path string +} + +// Restore implements Node interface. +func (n *LoadStatsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("LOAD STATS ") + ctx.WriteString(n.Path) + return nil +} + +// Accept implements Node Accept interface. +func (n *LoadStatsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*LoadStatsStmt) + return v.Leave(n) +} + +// LockStatsStmt is the statement node for lock table statistic +type LockStatsStmt struct { + stmtNode + + Tables []*TableName +} + +// Restore implements Node interface. +func (n *LockStatsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("LOCK STATS ") + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore LockStatsStmt.Tables[%d]", index) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *LockStatsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*LockStatsStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} + +// UnlockStatsStmt is the statement node for unlock table statistic +type UnlockStatsStmt struct { + stmtNode + + Tables []*TableName +} + +// Restore implements Node interface. +func (n *UnlockStatsStmt) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord("UNLOCK STATS ") + for index, table := range n.Tables { + if index != 0 { + ctx.WritePlain(", ") + } + if err := table.Restore(ctx); err != nil { + return errors.Annotatef(err, "An error occurred while restore UnlockStatsStmt.Tables[%d]", index) + } + } + return nil +} + +// Accept implements Node Accept interface. +func (n *UnlockStatsStmt) Accept(v Visitor) (Node, bool) { + newNode, skipChildren := v.Enter(n) + if skipChildren { + return v.Leave(newNode) + } + n = newNode.(*UnlockStatsStmt) + for i, val := range n.Tables { + node, ok := val.Accept(v) + if !ok { + return n, false + } + n.Tables[i] = node.(*TableName) + } + return v.Leave(n) +} diff --git a/parser/ast/util.go b/pkg/parser/ast/util.go similarity index 100% rename from parser/ast/util.go rename to pkg/parser/ast/util.go diff --git a/pkg/parser/ast/util_test.go b/pkg/parser/ast/util_test.go new file mode 100644 index 0000000000000..52bdc6c66e5ec --- /dev/null +++ b/pkg/parser/ast/util_test.go @@ -0,0 +1,221 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package ast_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + . "github.com/pingcap/tidb/pkg/parser/ast" + . "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/test_driver" + "github.com/stretchr/testify/require" +) + +func TestCacheable(t *testing.T) { + // test non-SelectStmt + var stmt Node = &DeleteStmt{} + require.False(t, IsReadOnly(stmt)) + + stmt = &InsertStmt{} + require.False(t, IsReadOnly(stmt)) + + stmt = &UpdateStmt{} + require.False(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{} + require.True(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{} + require.True(t, IsReadOnly(stmt)) + + stmt = &DoStmt{} + require.True(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{ + Stmt: &InsertStmt{}, + } + require.True(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{ + Analyze: true, + Stmt: &InsertStmt{}, + } + require.False(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{ + Stmt: &SelectStmt{}, + } + require.True(t, IsReadOnly(stmt)) + + stmt = &ExplainStmt{ + Analyze: true, + Stmt: &SelectStmt{}, + } + require.True(t, IsReadOnly(stmt)) + + stmt = &ShowStmt{} + require.True(t, IsReadOnly(stmt)) + + stmt = &ShowStmt{} + require.True(t, IsReadOnly(stmt)) +} + +func TestUnionReadOnly(t *testing.T) { + selectReadOnly := &SelectStmt{} + selectForUpdate := &SelectStmt{ + LockInfo: &SelectLockInfo{LockType: SelectLockForUpdate}, + } + selectForUpdateNoWait := &SelectStmt{ + LockInfo: &SelectLockInfo{LockType: SelectLockForUpdateNoWait}, + } + + setOprStmt := &SetOprStmt{ + SelectList: &SetOprSelectList{ + Selects: []Node{selectReadOnly, selectReadOnly}, + }, + } + require.True(t, IsReadOnly(setOprStmt)) + + setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectReadOnly, selectReadOnly} + require.True(t, IsReadOnly(setOprStmt)) + + setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdate} + require.False(t, IsReadOnly(setOprStmt)) + + setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdateNoWait} + require.False(t, IsReadOnly(setOprStmt)) + + setOprStmt.SelectList.Selects = []Node{selectForUpdate, selectForUpdateNoWait} + require.False(t, IsReadOnly(setOprStmt)) + + setOprStmt.SelectList.Selects = []Node{selectReadOnly, selectForUpdate, selectForUpdateNoWait} + require.False(t, IsReadOnly(setOprStmt)) +} + +// CleanNodeText set the text of node and all child node empty. +// For test only. +func CleanNodeText(node Node) { + var cleaner nodeTextCleaner + node.Accept(&cleaner) +} + +// nodeTextCleaner clean the text of a node and it's child node. +// For test only. +type nodeTextCleaner struct { +} + +// Enter implements Visitor interface. +func (checker *nodeTextCleaner) Enter(in Node) (out Node, skipChildren bool) { + in.SetText(nil, "") + in.SetOriginTextPosition(0) + if v, ok := in.(ValueExpr); ok && v != nil { + tpFlag := v.GetType().GetFlag() + if tpFlag&mysql.UnderScoreCharsetFlag != 0 { + // ignore underscore charset flag to let `'abc' = _utf8'abc'` pass + tpFlag ^= mysql.UnderScoreCharsetFlag + v.GetType().SetFlag(tpFlag) + } + } + + switch node := in.(type) { + case *Constraint: + if node.Option != nil { + if node.Option.KeyBlockSize == 0x0 && node.Option.Tp == 0 && node.Option.Comment == "" { + node.Option = nil + } + } + case *FuncCallExpr: + node.FnName.O = strings.ToLower(node.FnName.O) + switch node.FnName.L { + case "convert": + node.Args[1].(*test_driver.ValueExpr).Datum.SetBytes(nil) + } + case *AggregateFuncExpr: + node.F = strings.ToLower(node.F) + case *FieldList: + for _, f := range node.Fields { + f.Offset = 0 + } + case *AlterTableSpec: + for _, opt := range node.Options { + opt.StrValue = strings.ToLower(opt.StrValue) + } + case *Join: + node.ExplicitParens = false + case *ColumnDef: + node.Tp.CleanElemIsBinaryLit() + } + return in, false +} + +// Leave implements Visitor interface. +func (checker *nodeTextCleaner) Leave(in Node) (out Node, ok bool) { + return in, true +} + +type NodeRestoreTestCase struct { + sourceSQL string + expectSQL string +} + +func runNodeRestoreTest(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node) { + runNodeRestoreTestWithFlags(t, nodeTestCases, template, extractNodeFunc, DefaultRestoreFlags) +} + +func runNodeRestoreTestWithFlags(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node, flags RestoreFlags) { + p := parser.New() + p.EnableWindowFunc(true) + for _, testCase := range nodeTestCases { + sourceSQL := fmt.Sprintf(template, testCase.sourceSQL) + expectSQL := fmt.Sprintf(template, testCase.expectSQL) + stmt, err := p.ParseOneStmt(sourceSQL, "", "") + comment := fmt.Sprintf("source %#v", testCase) + require.NoError(t, err, comment) + var sb strings.Builder + err = extractNodeFunc(stmt).Restore(NewRestoreCtx(flags, &sb)) + require.NoError(t, err, comment) + restoreSql := fmt.Sprintf(template, sb.String()) + comment = fmt.Sprintf("source %#v; restore %v", testCase, restoreSql) + require.Equal(t, expectSQL, restoreSql, comment) + stmt2, err := p.ParseOneStmt(restoreSql, "", "") + require.NoError(t, err, comment) + CleanNodeText(stmt) + CleanNodeText(stmt2) + require.Equal(t, stmt, stmt2, comment) + } +} + +// runNodeRestoreTestWithFlagsStmtChange likes runNodeRestoreTestWithFlags but not check if the ASTs are same. +// Sometimes the AST are different and it's expected. +func runNodeRestoreTestWithFlagsStmtChange(t *testing.T, nodeTestCases []NodeRestoreTestCase, template string, extractNodeFunc func(node Node) Node, flags RestoreFlags) { + p := parser.New() + p.EnableWindowFunc(true) + for _, testCase := range nodeTestCases { + sourceSQL := fmt.Sprintf(template, testCase.sourceSQL) + expectSQL := fmt.Sprintf(template, testCase.expectSQL) + stmt, err := p.ParseOneStmt(sourceSQL, "", "") + comment := fmt.Sprintf("source %#v", testCase) + require.NoError(t, err, comment) + var sb strings.Builder + err = extractNodeFunc(stmt).Restore(NewRestoreCtx(flags, &sb)) + require.NoError(t, err, comment) + restoreSql := fmt.Sprintf(template, sb.String()) + comment = fmt.Sprintf("source %#v; restore %v", testCase, restoreSql) + require.Equal(t, expectSQL, restoreSql, comment) + } +} diff --git a/pkg/parser/auth/BUILD.bazel b/pkg/parser/auth/BUILD.bazel new file mode 100644 index 0000000000000..5e814c5da26fc --- /dev/null +++ b/pkg/parser/auth/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "auth", + srcs = [ + "auth.go", + "caching_sha2.go", + "mysql_native_password.go", + "tidb_sm3.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/auth", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "auth_test", + timeout = "short", + srcs = [ + "caching_sha2_test.go", + "mysql_native_password_test.go", + "tidb_sm3_test.go", + ], + embed = [":auth"], + flaky = True, + shard_count = 16, + deps = [ + "//pkg/parser/mysql", + "@com_github_stretchr_testify//require", + ], +) diff --git a/parser/auth/auth.go b/pkg/parser/auth/auth.go similarity index 98% rename from parser/auth/auth.go rename to pkg/parser/auth/auth.go index cb9abb883a33d..dfd11b34b2557 100644 --- a/parser/auth/auth.go +++ b/pkg/parser/auth/auth.go @@ -16,7 +16,7 @@ package auth import ( "fmt" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser/format" ) const ( diff --git a/parser/auth/caching_sha2.go b/pkg/parser/auth/caching_sha2.go similarity index 99% rename from parser/auth/caching_sha2.go rename to pkg/parser/auth/caching_sha2.go index d3a29cbb076f9..4a3b8a402275a 100644 --- a/parser/auth/caching_sha2.go +++ b/pkg/parser/auth/caching_sha2.go @@ -39,7 +39,7 @@ import ( "fmt" "strconv" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" ) const ( diff --git a/parser/auth/caching_sha2_test.go b/pkg/parser/auth/caching_sha2_test.go similarity index 98% rename from parser/auth/caching_sha2_test.go rename to pkg/parser/auth/caching_sha2_test.go index e3e6cccd2b167..2f4df2af7110a 100644 --- a/parser/auth/caching_sha2_test.go +++ b/pkg/parser/auth/caching_sha2_test.go @@ -17,7 +17,7 @@ import ( "encoding/hex" "testing" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/parser/auth/mysql_native_password.go b/pkg/parser/auth/mysql_native_password.go similarity index 98% rename from parser/auth/mysql_native_password.go rename to pkg/parser/auth/mysql_native_password.go index 2bfc1a8190667..e97ab4c9b794e 100644 --- a/parser/auth/mysql_native_password.go +++ b/pkg/parser/auth/mysql_native_password.go @@ -20,7 +20,7 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/terror" ) // CheckScrambledPassword check scrambled password received from client. diff --git a/parser/auth/mysql_native_password_test.go b/pkg/parser/auth/mysql_native_password_test.go similarity index 100% rename from parser/auth/mysql_native_password_test.go rename to pkg/parser/auth/mysql_native_password_test.go diff --git a/parser/auth/tidb_sm3.go b/pkg/parser/auth/tidb_sm3.go similarity index 100% rename from parser/auth/tidb_sm3.go rename to pkg/parser/auth/tidb_sm3.go diff --git a/parser/auth/tidb_sm3_test.go b/pkg/parser/auth/tidb_sm3_test.go similarity index 98% rename from parser/auth/tidb_sm3_test.go rename to pkg/parser/auth/tidb_sm3_test.go index 7d5f8593e5358..baef7e7923e70 100644 --- a/parser/auth/tidb_sm3_test.go +++ b/pkg/parser/auth/tidb_sm3_test.go @@ -17,7 +17,7 @@ import ( "encoding/hex" "testing" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/parser/bench_test.go b/pkg/parser/bench_test.go similarity index 100% rename from parser/bench_test.go rename to pkg/parser/bench_test.go diff --git a/pkg/parser/charset/BUILD.bazel b/pkg/parser/charset/BUILD.bazel new file mode 100644 index 0000000000000..56718a7644d60 --- /dev/null +++ b/pkg/parser/charset/BUILD.bazel @@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "charset", + srcs = [ + "charset.go", + "encoding.go", + "encoding_ascii.go", + "encoding_base.go", + "encoding_bin.go", + "encoding_gbk.go", + "encoding_latin1.go", + "encoding_table.go", + "encoding_utf8.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/charset", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/parser/terror", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_golang_x_text//encoding", + "@org_golang_x_text//encoding/charmap", + "@org_golang_x_text//encoding/japanese", + "@org_golang_x_text//encoding/korean", + "@org_golang_x_text//encoding/simplifiedchinese", + "@org_golang_x_text//encoding/traditionalchinese", + "@org_golang_x_text//encoding/unicode", + "@org_golang_x_text//transform", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "charset_test", + timeout = "short", + srcs = [ + "charset_test.go", + "encoding_test.go", + ], + embed = [":charset"], + flaky = True, + shard_count = 8, + deps = [ + "@com_github_stretchr_testify//require", + "@org_golang_x_text//transform", + ], +) diff --git a/pkg/parser/charset/charset.go b/pkg/parser/charset/charset.go new file mode 100644 index 0000000000000..82f5bb7b0643c --- /dev/null +++ b/pkg/parser/charset/charset.go @@ -0,0 +1,655 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package charset + +import ( + "cmp" + "slices" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "go.uber.org/zap" +) + +var ( + // ErrUnknownCollation is unknown collation. + ErrUnknownCollation = terror.ClassDDL.NewStd(mysql.ErrUnknownCollation) + // ErrCollationCharsetMismatch is collation charset mismatch. + ErrCollationCharsetMismatch = terror.ClassDDL.NewStd(mysql.ErrCollationCharsetMismatch) +) + +// Charset is a charset. +// Now we only support MySQL. +type Charset struct { + Name string + DefaultCollation string + Collations map[string]*Collation + Desc string + Maxlen int +} + +// Collation is a collation. +// Now we only support MySQL. +type Collation struct { + ID int + CharsetName string + Name string + IsDefault bool +} + +var collationsIDMap = make(map[int]*Collation) +var collationsNameMap = make(map[string]*Collation) +var supportedCollations = make([]*Collation, 0, len(supportedCollationNames)) + +// CharacterSetInfos contains all the supported charsets. +var CharacterSetInfos = map[string]*Charset{ + CharsetUTF8: {CharsetUTF8, CollationUTF8, make(map[string]*Collation), "UTF-8 Unicode", 3}, + CharsetUTF8MB4: {CharsetUTF8MB4, CollationUTF8MB4, make(map[string]*Collation), "UTF-8 Unicode", 4}, + CharsetASCII: {CharsetASCII, CollationASCII, make(map[string]*Collation), "US ASCII", 1}, + CharsetLatin1: {CharsetLatin1, CollationLatin1, make(map[string]*Collation), "Latin1", 1}, + CharsetBin: {CharsetBin, CollationBin, make(map[string]*Collation), "binary", 1}, + CharsetGBK: {CharsetGBK, CollationGBKBin, make(map[string]*Collation), "Chinese Internal Code Specification", 2}, +} + +// All the names supported collations should be in the following table. +var supportedCollationNames = map[string]struct{}{ + CollationUTF8: {}, + CollationUTF8MB4: {}, + CollationASCII: {}, + CollationLatin1: {}, + CollationBin: {}, + CollationGBKBin: {}, +} + +// TiFlashSupportedCharsets is a map which contains TiFlash supports charsets. +var TiFlashSupportedCharsets = map[string]struct{}{ + CharsetUTF8: {}, + CharsetUTF8MB4: {}, + CharsetASCII: {}, + CharsetLatin1: {}, + CharsetBin: {}, +} + +// GetSupportedCharsets gets descriptions for all charsets supported so far. +func GetSupportedCharsets() []*Charset { + charsets := make([]*Charset, 0, len(CharacterSetInfos)) + for _, ch := range CharacterSetInfos { + charsets = append(charsets, ch) + } + + // sort charset by name. + slices.SortFunc(charsets, func(i, j *Charset) int { + return cmp.Compare(i.Name, j.Name) + }) + return charsets +} + +// GetSupportedCollations gets information for all collations supported so far. +func GetSupportedCollations() []*Collation { + return supportedCollations +} + +// ValidCharsetAndCollation checks the charset and the collation validity +// and returns a boolean. +func ValidCharsetAndCollation(cs string, co string) bool { + // We will use utf8 as a default charset. + if cs == "" || cs == CharsetUTF8MB3 { + cs = CharsetUTF8 + } + chs, err := GetCharsetInfo(cs) + if err != nil { + return false + } + + if co == "" { + return true + } + co = utf8Alias(strings.ToLower(co)) + _, ok := chs.Collations[co] + return ok +} + +// GetDefaultCollationLegacy is compatible with the charset support in old version parser. +func GetDefaultCollationLegacy(charset string) (string, error) { + switch strings.ToLower(charset) { + case CharsetUTF8MB3: + return GetDefaultCollation(CharsetUTF8) + case CharsetUTF8, CharsetUTF8MB4, CharsetASCII, CharsetLatin1, CharsetBin: + return GetDefaultCollation(charset) + default: + return "", errors.Errorf("Unknown charset %s", charset) + } +} + +// GetDefaultCollation returns the default collation for charset. +func GetDefaultCollation(charset string) (string, error) { + cs, err := GetCharsetInfo(charset) + if err != nil { + return "", err + } + return cs.DefaultCollation, nil +} + +// GetDefaultCharsetAndCollate returns the default charset and collation. +func GetDefaultCharsetAndCollate() (defaultCharset string, defaultCollationName string) { + return mysql.DefaultCharset, mysql.DefaultCollationName +} + +// GetCharsetInfo returns charset and collation for cs as name. +func GetCharsetInfo(cs string) (*Charset, error) { + if strings.ToLower(cs) == CharsetUTF8MB3 { + cs = CharsetUTF8 + } + + if c, ok := CharacterSetInfos[strings.ToLower(cs)]; ok { + return c, nil + } + + if c, ok := charsets[strings.ToLower(cs)]; ok { + return c, errors.Errorf("Unsupported charset %s", cs) + } + + return nil, errors.Errorf("Unknown charset %s", cs) +} + +// GetCharsetInfoByID returns charset and collation for id as cs_number. +func GetCharsetInfoByID(coID int) (charsetStr string, collateStr string, err error) { + if coID == mysql.DefaultCollationID { + return mysql.DefaultCharset, mysql.DefaultCollationName, nil + } + if collation, ok := collationsIDMap[coID]; ok { + return collation.CharsetName, collation.Name, nil + } + + log.Warn( + "unable to get collation name from collation ID, return default charset and collation instead", + zap.Int("ID", coID), + zap.Stack("stack")) + return mysql.DefaultCharset, mysql.DefaultCollationName, errors.Errorf("Unknown collation id %d", coID) +} + +func utf8Alias(csname string) string { + switch csname { + case "utf8mb3_bin": + csname = "utf8_bin" + case "utf8mb3_unicode_ci": + csname = "utf8_unicode_ci" + case "utf8mb3_general_ci": + csname = "utf8_general_ci" + default: + } + return csname +} + +// GetCollationByName returns the collation by name. +func GetCollationByName(name string) (*Collation, error) { + csname := utf8Alias(strings.ToLower(name)) + collation, ok := collationsNameMap[csname] + if !ok { + return nil, ErrUnknownCollation.GenWithStackByArgs(name) + } + return collation, nil +} + +// GetCollationByID returns collations by given id. +func GetCollationByID(id int) (*Collation, error) { + collation, ok := collationsIDMap[id] + if !ok { + return nil, errors.Errorf("Unknown collation id %d", id) + } + + return collation, nil +} + +const ( + // CollationBin is the default collation for CharsetBin. + CollationBin = "binary" + // CollationUTF8 is the default collation for CharsetUTF8. + CollationUTF8 = "utf8_bin" + // CollationUTF8MB4 is the default collation for CharsetUTF8MB4. + CollationUTF8MB4 = "utf8mb4_bin" + // CollationASCII is the default collation for CharsetACSII. + CollationASCII = "ascii_bin" + // CollationLatin1 is the default collation for CharsetLatin1. + CollationLatin1 = "latin1_bin" + // CollationGBKBin is the default collation for CharsetGBK when new collation is disabled. + CollationGBKBin = "gbk_bin" + // CollationGBKChineseCI is the default collation for CharsetGBK when new collation is enabled. + CollationGBKChineseCI = "gbk_chinese_ci" +) + +const ( + // CharsetASCII is a subset of UTF8. + CharsetASCII = "ascii" + // CharsetBin is used for marking binary charset. + CharsetBin = "binary" + // CharsetLatin1 is a single byte charset. + CharsetLatin1 = "latin1" + // CharsetUTF8 is the default charset for string types. + CharsetUTF8 = "utf8" + // CharsetUTF8MB3 is 3 bytes utf8, a MySQL legacy encoding. "utf8" and "utf8mb3" are aliases. + CharsetUTF8MB3 = "utf8mb3" + // CharsetUTF8MB4 represents 4 bytes utf8, which works the same way as utf8 in Go. + CharsetUTF8MB4 = "utf8mb4" + //revive:disable:exported + CharsetARMSCII8 = "armscii8" + CharsetBig5 = "big5" + CharsetCP1250 = "cp1250" + CharsetCP1251 = "cp1251" + CharsetCP1256 = "cp1256" + CharsetCP1257 = "cp1257" + CharsetCP850 = "cp850" + CharsetCP852 = "cp852" + CharsetCP866 = "cp866" + CharsetCP932 = "cp932" + CharsetDEC8 = "dec8" + CharsetEUCJPMS = "eucjpms" + CharsetEUCKR = "euckr" + CharsetGB18030 = "gb18030" + CharsetGB2312 = "gb2312" + CharsetGBK = "gbk" + CharsetGEOSTD8 = "geostd8" + CharsetGreek = "greek" + CharsetHebrew = "hebrew" + CharsetHP8 = "hp8" + CharsetKEYBCS2 = "keybcs2" + CharsetKOI8R = "koi8r" + CharsetKOI8U = "koi8u" + CharsetLatin2 = "latin2" + CharsetLatin5 = "latin5" + CharsetLatin7 = "latin7" + CharsetMacCE = "macce" + CharsetMacRoman = "macroman" + CharsetSJIS = "sjis" + CharsetSWE7 = "swe7" + CharsetTIS620 = "tis620" + CharsetUCS2 = "ucs2" + CharsetUJIS = "ujis" + CharsetUTF16 = "utf16" + CharsetUTF16LE = "utf16le" + CharsetUTF32 = "utf32" + //revive:enable:exported +) + +var charsets = map[string]*Charset{ + CharsetARMSCII8: {Name: CharsetARMSCII8, Maxlen: 1, DefaultCollation: "armscii8_general_ci", Desc: "ARMSCII-8 Armenian", Collations: make(map[string]*Collation)}, + CharsetASCII: {Name: CharsetASCII, Maxlen: 1, DefaultCollation: "ascii_general_ci", Desc: "US ASCII", Collations: make(map[string]*Collation)}, + CharsetBig5: {Name: CharsetBig5, Maxlen: 2, DefaultCollation: "big5_chinese_ci", Desc: "Big5 Traditional Chinese", Collations: make(map[string]*Collation)}, + CharsetBin: {Name: CharsetBin, Maxlen: 1, DefaultCollation: "binary", Desc: "Binary pseudo charset", Collations: make(map[string]*Collation)}, + CharsetLatin1: {Name: CharsetLatin1, Maxlen: 1, DefaultCollation: "cp1250_general_ci", Desc: "Windows Central European", Collations: make(map[string]*Collation)}, + CharsetCP1250: {Name: CharsetCP1250, Maxlen: 1, DefaultCollation: "cp1251_general_ci", Desc: "Windows Cyrillic", Collations: make(map[string]*Collation)}, + CharsetCP1251: {Name: CharsetCP1251, Maxlen: 1, DefaultCollation: "cp1256_general_ci", Desc: "Windows Arabic", Collations: make(map[string]*Collation)}, + CharsetCP1256: {Name: CharsetCP1256, Maxlen: 1, DefaultCollation: "cp1257_general_ci", Desc: "Windows Baltic", Collations: make(map[string]*Collation)}, + CharsetCP1257: {Name: CharsetCP1257, Maxlen: 1, DefaultCollation: "cp850_general_ci", Desc: "DOS West European", Collations: make(map[string]*Collation)}, + CharsetCP850: {Name: CharsetCP850, Maxlen: 1, DefaultCollation: "cp852_general_ci", Desc: "DOS Central European", Collations: make(map[string]*Collation)}, + CharsetCP852: {Name: CharsetCP852, Maxlen: 1, DefaultCollation: "cp866_general_ci", Desc: "DOS Russian", Collations: make(map[string]*Collation)}, + CharsetCP866: {Name: CharsetCP866, Maxlen: 1, DefaultCollation: "cp932_japanese_ci", Desc: "SJIS for Windows Japanese", Collations: make(map[string]*Collation)}, + CharsetCP932: {Name: CharsetCP932, Maxlen: 2, DefaultCollation: "dec8_swedish_ci", Desc: "DEC West European", Collations: make(map[string]*Collation)}, + CharsetDEC8: {Name: CharsetDEC8, Maxlen: 1, DefaultCollation: "eucjpms_japanese_ci", Desc: "UJIS for Windows Japanese", Collations: make(map[string]*Collation)}, + CharsetEUCJPMS: {Name: CharsetEUCJPMS, Maxlen: 3, DefaultCollation: "euckr_korean_ci", Desc: "EUC-KR Korean", Collations: make(map[string]*Collation)}, + CharsetEUCKR: {Name: CharsetEUCKR, Maxlen: 2, DefaultCollation: "gb18030_chinese_ci", Desc: "China National Standard GB18030", Collations: make(map[string]*Collation)}, + CharsetGB18030: {Name: CharsetGB18030, Maxlen: 4, DefaultCollation: "gb2312_chinese_ci", Desc: "GB2312 Simplified Chinese", Collations: make(map[string]*Collation)}, + CharsetGB2312: {Name: CharsetGB2312, Maxlen: 2, DefaultCollation: "gbk_chinese_ci", Desc: "GBK Simplified Chinese", Collations: make(map[string]*Collation)}, + CharsetGBK: {Name: CharsetGBK, Maxlen: 2, DefaultCollation: "geostd8_general_ci", Desc: "GEOSTD8 Georgian", Collations: make(map[string]*Collation)}, + CharsetGEOSTD8: {Name: CharsetGEOSTD8, Maxlen: 1, DefaultCollation: "greek_general_ci", Desc: "ISO 8859-7 Greek", Collations: make(map[string]*Collation)}, + CharsetGreek: {Name: CharsetGreek, Maxlen: 1, DefaultCollation: "hebrew_general_ci", Desc: "ISO 8859-8 Hebrew", Collations: make(map[string]*Collation)}, + CharsetHebrew: {Name: CharsetHebrew, Maxlen: 1, DefaultCollation: "hp8_english_ci", Desc: "HP West European", Collations: make(map[string]*Collation)}, + CharsetHP8: {Name: CharsetHP8, Maxlen: 1, DefaultCollation: "keybcs2_general_ci", Desc: "DOS Kamenicky Czech-Slovak", Collations: make(map[string]*Collation)}, + CharsetKEYBCS2: {Name: CharsetKEYBCS2, Maxlen: 1, DefaultCollation: "koi8r_general_ci", Desc: "KOI8-R Relcom Russian", Collations: make(map[string]*Collation)}, + CharsetKOI8R: {Name: CharsetKOI8R, Maxlen: 1, DefaultCollation: "koi8u_general_ci", Desc: "KOI8-U Ukrainian", Collations: make(map[string]*Collation)}, + CharsetKOI8U: {Name: CharsetKOI8U, Maxlen: 1, DefaultCollation: "latin1_swedish_ci", Desc: "cp1252 West European", Collations: make(map[string]*Collation)}, + CharsetLatin2: {Name: CharsetLatin2, Maxlen: 1, DefaultCollation: "latin2_general_ci", Desc: "ISO 8859-2 Central European", Collations: make(map[string]*Collation)}, + CharsetLatin5: {Name: CharsetLatin5, Maxlen: 1, DefaultCollation: "latin5_turkish_ci", Desc: "ISO 8859-9 Turkish", Collations: make(map[string]*Collation)}, + CharsetLatin7: {Name: CharsetLatin7, Maxlen: 1, DefaultCollation: "latin7_general_ci", Desc: "ISO 8859-13 Baltic", Collations: make(map[string]*Collation)}, + CharsetMacCE: {Name: CharsetMacCE, Maxlen: 1, DefaultCollation: "macce_general_ci", Desc: "Mac Central European", Collations: make(map[string]*Collation)}, + CharsetMacRoman: {Name: CharsetMacRoman, Maxlen: 1, DefaultCollation: "macroman_general_ci", Desc: "Mac West European", Collations: make(map[string]*Collation)}, + CharsetSJIS: {Name: CharsetSJIS, Maxlen: 2, DefaultCollation: "sjis_japanese_ci", Desc: "Shift-JIS Japanese", Collations: make(map[string]*Collation)}, + CharsetSWE7: {Name: CharsetSWE7, Maxlen: 1, DefaultCollation: "swe7_swedish_ci", Desc: "7bit Swedish", Collations: make(map[string]*Collation)}, + CharsetTIS620: {Name: CharsetTIS620, Maxlen: 1, DefaultCollation: "tis620_thai_ci", Desc: "TIS620 Thai", Collations: make(map[string]*Collation)}, + CharsetUCS2: {Name: CharsetUCS2, Maxlen: 2, DefaultCollation: "ucs2_general_ci", Desc: "UCS-2 Unicode", Collations: make(map[string]*Collation)}, + CharsetUJIS: {Name: CharsetUJIS, Maxlen: 3, DefaultCollation: "ujis_japanese_ci", Desc: "EUC-JP Japanese", Collations: make(map[string]*Collation)}, + CharsetUTF16: {Name: CharsetUTF16, Maxlen: 4, DefaultCollation: "utf16_general_ci", Desc: "UTF-16 Unicode", Collations: make(map[string]*Collation)}, + CharsetUTF16LE: {Name: CharsetUTF16LE, Maxlen: 4, DefaultCollation: "utf16le_general_ci", Desc: "UTF-16LE Unicode", Collations: make(map[string]*Collation)}, + CharsetUTF32: {Name: CharsetUTF32, Maxlen: 4, DefaultCollation: "utf32_general_ci", Desc: "UTF-32 Unicode", Collations: make(map[string]*Collation)}, + CharsetUTF8: {Name: CharsetUTF8, Maxlen: 3, DefaultCollation: "utf8_general_ci", Desc: "UTF-8 Unicode", Collations: make(map[string]*Collation)}, + CharsetUTF8MB4: {Name: CharsetUTF8MB4, Maxlen: 4, DefaultCollation: "utf8mb4_0900_ai_ci", Desc: "UTF-8 Unicode", Collations: make(map[string]*Collation)}, +} + +var collations = []*Collation{ + {1, "big5", "big5_chinese_ci", true}, + {2, "latin2", "latin2_czech_cs", false}, + {3, "dec8", "dec8_swedish_ci", true}, + {4, "cp850", "cp850_general_ci", true}, + {5, "latin1", "latin1_german1_ci", false}, + {6, "hp8", "hp8_english_ci", true}, + {7, "koi8r", "koi8r_general_ci", true}, + {8, "latin1", "latin1_swedish_ci", false}, + {9, "latin2", "latin2_general_ci", true}, + {10, "swe7", "swe7_swedish_ci", true}, + {11, "ascii", "ascii_general_ci", false}, + {12, "ujis", "ujis_japanese_ci", true}, + {13, "sjis", "sjis_japanese_ci", true}, + {14, "cp1251", "cp1251_bulgarian_ci", false}, + {15, "latin1", "latin1_danish_ci", false}, + {16, "hebrew", "hebrew_general_ci", true}, + {18, "tis620", "tis620_thai_ci", true}, + {19, "euckr", "euckr_korean_ci", true}, + {20, "latin7", "latin7_estonian_cs", false}, + {21, "latin2", "latin2_hungarian_ci", false}, + {22, "koi8u", "koi8u_general_ci", true}, + {23, "cp1251", "cp1251_ukrainian_ci", false}, + {24, "gb2312", "gb2312_chinese_ci", true}, + {25, "greek", "greek_general_ci", true}, + {26, "cp1250", "cp1250_general_ci", true}, + {27, "latin2", "latin2_croatian_ci", false}, + {28, "gbk", "gbk_chinese_ci", false}, + {29, "cp1257", "cp1257_lithuanian_ci", false}, + {30, "latin5", "latin5_turkish_ci", true}, + {31, "latin1", "latin1_german2_ci", false}, + {32, "armscii8", "armscii8_general_ci", true}, + {33, "utf8", "utf8_general_ci", false}, + {34, "cp1250", "cp1250_czech_cs", false}, + {35, "ucs2", "ucs2_general_ci", true}, + {36, "cp866", "cp866_general_ci", true}, + {37, "keybcs2", "keybcs2_general_ci", true}, + {38, "macce", "macce_general_ci", true}, + {39, "macroman", "macroman_general_ci", true}, + {40, "cp852", "cp852_general_ci", true}, + {41, "latin7", "latin7_general_ci", true}, + {42, "latin7", "latin7_general_cs", false}, + {43, "macce", "macce_bin", false}, + {44, "cp1250", "cp1250_croatian_ci", false}, + {45, "utf8mb4", "utf8mb4_general_ci", false}, + {46, "utf8mb4", "utf8mb4_bin", true}, + {47, "latin1", "latin1_bin", true}, + {48, "latin1", "latin1_general_ci", false}, + {49, "latin1", "latin1_general_cs", false}, + {50, "cp1251", "cp1251_bin", false}, + {51, "cp1251", "cp1251_general_ci", true}, + {52, "cp1251", "cp1251_general_cs", false}, + {53, "macroman", "macroman_bin", false}, + {54, "utf16", "utf16_general_ci", true}, + {55, "utf16", "utf16_bin", false}, + {56, "utf16le", "utf16le_general_ci", true}, + {57, "cp1256", "cp1256_general_ci", true}, + {58, "cp1257", "cp1257_bin", false}, + {59, "cp1257", "cp1257_general_ci", true}, + {60, "utf32", "utf32_general_ci", true}, + {61, "utf32", "utf32_bin", false}, + {62, "utf16le", "utf16le_bin", false}, + {63, "binary", "binary", true}, + {64, "armscii8", "armscii8_bin", false}, + {65, "ascii", "ascii_bin", true}, + {66, "cp1250", "cp1250_bin", false}, + {67, "cp1256", "cp1256_bin", false}, + {68, "cp866", "cp866_bin", false}, + {69, "dec8", "dec8_bin", false}, + {70, "greek", "greek_bin", false}, + {71, "hebrew", "hebrew_bin", false}, + {72, "hp8", "hp8_bin", false}, + {73, "keybcs2", "keybcs2_bin", false}, + {74, "koi8r", "koi8r_bin", false}, + {75, "koi8u", "koi8u_bin", false}, + {76, "utf8", "utf8_tolower_ci", false}, + {77, "latin2", "latin2_bin", false}, + {78, "latin5", "latin5_bin", false}, + {79, "latin7", "latin7_bin", false}, + {80, "cp850", "cp850_bin", false}, + {81, "cp852", "cp852_bin", false}, + {82, "swe7", "swe7_bin", false}, + {83, "utf8", "utf8_bin", true}, + {84, "big5", "big5_bin", false}, + {85, "euckr", "euckr_bin", false}, + {86, "gb2312", "gb2312_bin", false}, + {87, "gbk", "gbk_bin", true}, + {88, "sjis", "sjis_bin", false}, + {89, "tis620", "tis620_bin", false}, + {90, "ucs2", "ucs2_bin", false}, + {91, "ujis", "ujis_bin", false}, + {92, "geostd8", "geostd8_general_ci", true}, + {93, "geostd8", "geostd8_bin", false}, + {94, "latin1", "latin1_spanish_ci", false}, + {95, "cp932", "cp932_japanese_ci", true}, + {96, "cp932", "cp932_bin", false}, + {97, "eucjpms", "eucjpms_japanese_ci", true}, + {98, "eucjpms", "eucjpms_bin", false}, + {99, "cp1250", "cp1250_polish_ci", false}, + {101, "utf16", "utf16_unicode_ci", false}, + {102, "utf16", "utf16_icelandic_ci", false}, + {103, "utf16", "utf16_latvian_ci", false}, + {104, "utf16", "utf16_romanian_ci", false}, + {105, "utf16", "utf16_slovenian_ci", false}, + {106, "utf16", "utf16_polish_ci", false}, + {107, "utf16", "utf16_estonian_ci", false}, + {108, "utf16", "utf16_spanish_ci", false}, + {109, "utf16", "utf16_swedish_ci", false}, + {110, "utf16", "utf16_turkish_ci", false}, + {111, "utf16", "utf16_czech_ci", false}, + {112, "utf16", "utf16_danish_ci", false}, + {113, "utf16", "utf16_lithuanian_ci", false}, + {114, "utf16", "utf16_slovak_ci", false}, + {115, "utf16", "utf16_spanish2_ci", false}, + {116, "utf16", "utf16_roman_ci", false}, + {117, "utf16", "utf16_persian_ci", false}, + {118, "utf16", "utf16_esperanto_ci", false}, + {119, "utf16", "utf16_hungarian_ci", false}, + {120, "utf16", "utf16_sinhala_ci", false}, + {121, "utf16", "utf16_german2_ci", false}, + {122, "utf16", "utf16_croatian_ci", false}, + {123, "utf16", "utf16_unicode_520_ci", false}, + {124, "utf16", "utf16_vietnamese_ci", false}, + {128, "ucs2", "ucs2_unicode_ci", false}, + {129, "ucs2", "ucs2_icelandic_ci", false}, + {130, "ucs2", "ucs2_latvian_ci", false}, + {131, "ucs2", "ucs2_romanian_ci", false}, + {132, "ucs2", "ucs2_slovenian_ci", false}, + {133, "ucs2", "ucs2_polish_ci", false}, + {134, "ucs2", "ucs2_estonian_ci", false}, + {135, "ucs2", "ucs2_spanish_ci", false}, + {136, "ucs2", "ucs2_swedish_ci", false}, + {137, "ucs2", "ucs2_turkish_ci", false}, + {138, "ucs2", "ucs2_czech_ci", false}, + {139, "ucs2", "ucs2_danish_ci", false}, + {140, "ucs2", "ucs2_lithuanian_ci", false}, + {141, "ucs2", "ucs2_slovak_ci", false}, + {142, "ucs2", "ucs2_spanish2_ci", false}, + {143, "ucs2", "ucs2_roman_ci", false}, + {144, "ucs2", "ucs2_persian_ci", false}, + {145, "ucs2", "ucs2_esperanto_ci", false}, + {146, "ucs2", "ucs2_hungarian_ci", false}, + {147, "ucs2", "ucs2_sinhala_ci", false}, + {148, "ucs2", "ucs2_german2_ci", false}, + {149, "ucs2", "ucs2_croatian_ci", false}, + {150, "ucs2", "ucs2_unicode_520_ci", false}, + {151, "ucs2", "ucs2_vietnamese_ci", false}, + {159, "ucs2", "ucs2_general_mysql500_ci", false}, + {160, "utf32", "utf32_unicode_ci", false}, + {161, "utf32", "utf32_icelandic_ci", false}, + {162, "utf32", "utf32_latvian_ci", false}, + {163, "utf32", "utf32_romanian_ci", false}, + {164, "utf32", "utf32_slovenian_ci", false}, + {165, "utf32", "utf32_polish_ci", false}, + {166, "utf32", "utf32_estonian_ci", false}, + {167, "utf32", "utf32_spanish_ci", false}, + {168, "utf32", "utf32_swedish_ci", false}, + {169, "utf32", "utf32_turkish_ci", false}, + {170, "utf32", "utf32_czech_ci", false}, + {171, "utf32", "utf32_danish_ci", false}, + {172, "utf32", "utf32_lithuanian_ci", false}, + {173, "utf32", "utf32_slovak_ci", false}, + {174, "utf32", "utf32_spanish2_ci", false}, + {175, "utf32", "utf32_roman_ci", false}, + {176, "utf32", "utf32_persian_ci", false}, + {177, "utf32", "utf32_esperanto_ci", false}, + {178, "utf32", "utf32_hungarian_ci", false}, + {179, "utf32", "utf32_sinhala_ci", false}, + {180, "utf32", "utf32_german2_ci", false}, + {181, "utf32", "utf32_croatian_ci", false}, + {182, "utf32", "utf32_unicode_520_ci", false}, + {183, "utf32", "utf32_vietnamese_ci", false}, + {192, "utf8", "utf8_unicode_ci", false}, + {193, "utf8", "utf8_icelandic_ci", false}, + {194, "utf8", "utf8_latvian_ci", false}, + {195, "utf8", "utf8_romanian_ci", false}, + {196, "utf8", "utf8_slovenian_ci", false}, + {197, "utf8", "utf8_polish_ci", false}, + {198, "utf8", "utf8_estonian_ci", false}, + {199, "utf8", "utf8_spanish_ci", false}, + {200, "utf8", "utf8_swedish_ci", false}, + {201, "utf8", "utf8_turkish_ci", false}, + {202, "utf8", "utf8_czech_ci", false}, + {203, "utf8", "utf8_danish_ci", false}, + {204, "utf8", "utf8_lithuanian_ci", false}, + {205, "utf8", "utf8_slovak_ci", false}, + {206, "utf8", "utf8_spanish2_ci", false}, + {207, "utf8", "utf8_roman_ci", false}, + {208, "utf8", "utf8_persian_ci", false}, + {209, "utf8", "utf8_esperanto_ci", false}, + {210, "utf8", "utf8_hungarian_ci", false}, + {211, "utf8", "utf8_sinhala_ci", false}, + {212, "utf8", "utf8_german2_ci", false}, + {213, "utf8", "utf8_croatian_ci", false}, + {214, "utf8", "utf8_unicode_520_ci", false}, + {215, "utf8", "utf8_vietnamese_ci", false}, + {223, "utf8", "utf8_general_mysql500_ci", false}, + {224, "utf8mb4", "utf8mb4_unicode_ci", false}, + {225, "utf8mb4", "utf8mb4_icelandic_ci", false}, + {226, "utf8mb4", "utf8mb4_latvian_ci", false}, + {227, "utf8mb4", "utf8mb4_romanian_ci", false}, + {228, "utf8mb4", "utf8mb4_slovenian_ci", false}, + {229, "utf8mb4", "utf8mb4_polish_ci", false}, + {230, "utf8mb4", "utf8mb4_estonian_ci", false}, + {231, "utf8mb4", "utf8mb4_spanish_ci", false}, + {232, "utf8mb4", "utf8mb4_swedish_ci", false}, + {233, "utf8mb4", "utf8mb4_turkish_ci", false}, + {234, "utf8mb4", "utf8mb4_czech_ci", false}, + {235, "utf8mb4", "utf8mb4_danish_ci", false}, + {236, "utf8mb4", "utf8mb4_lithuanian_ci", false}, + {237, "utf8mb4", "utf8mb4_slovak_ci", false}, + {238, "utf8mb4", "utf8mb4_spanish2_ci", false}, + {239, "utf8mb4", "utf8mb4_roman_ci", false}, + {240, "utf8mb4", "utf8mb4_persian_ci", false}, + {241, "utf8mb4", "utf8mb4_esperanto_ci", false}, + {242, "utf8mb4", "utf8mb4_hungarian_ci", false}, + {243, "utf8mb4", "utf8mb4_sinhala_ci", false}, + {244, "utf8mb4", "utf8mb4_german2_ci", false}, + {245, "utf8mb4", "utf8mb4_croatian_ci", false}, + {246, "utf8mb4", "utf8mb4_unicode_520_ci", false}, + {247, "utf8mb4", "utf8mb4_vietnamese_ci", false}, + {248, "gb18030", "gb18030_chinese_ci", false}, + {249, "gb18030", "gb18030_bin", true}, + {250, "gb18030", "gb18030_unicode_520_ci", false}, + {255, "utf8mb4", "utf8mb4_0900_ai_ci", false}, + {256, "utf8mb4", "utf8mb4_de_pb_0900_ai_ci", false}, + {257, "utf8mb4", "utf8mb4_is_0900_ai_ci", false}, + {258, "utf8mb4", "utf8mb4_lv_0900_ai_ci", false}, + {259, "utf8mb4", "utf8mb4_ro_0900_ai_ci", false}, + {260, "utf8mb4", "utf8mb4_sl_0900_ai_ci", false}, + {261, "utf8mb4", "utf8mb4_pl_0900_ai_ci", false}, + {262, "utf8mb4", "utf8mb4_et_0900_ai_ci", false}, + {263, "utf8mb4", "utf8mb4_es_0900_ai_ci", false}, + {264, "utf8mb4", "utf8mb4_sv_0900_ai_ci", false}, + {265, "utf8mb4", "utf8mb4_tr_0900_ai_ci", false}, + {266, "utf8mb4", "utf8mb4_cs_0900_ai_ci", false}, + {267, "utf8mb4", "utf8mb4_da_0900_ai_ci", false}, + {268, "utf8mb4", "utf8mb4_lt_0900_ai_ci", false}, + {269, "utf8mb4", "utf8mb4_sk_0900_ai_ci", false}, + {270, "utf8mb4", "utf8mb4_es_trad_0900_ai_ci", false}, + {271, "utf8mb4", "utf8mb4_la_0900_ai_ci", false}, + {273, "utf8mb4", "utf8mb4_eo_0900_ai_ci", false}, + {274, "utf8mb4", "utf8mb4_hu_0900_ai_ci", false}, + {275, "utf8mb4", "utf8mb4_hr_0900_ai_ci", false}, + {277, "utf8mb4", "utf8mb4_vi_0900_ai_ci", false}, + {278, "utf8mb4", "utf8mb4_0900_as_cs", false}, + {279, "utf8mb4", "utf8mb4_de_pb_0900_as_cs", false}, + {280, "utf8mb4", "utf8mb4_is_0900_as_cs", false}, + {281, "utf8mb4", "utf8mb4_lv_0900_as_cs", false}, + {282, "utf8mb4", "utf8mb4_ro_0900_as_cs", false}, + {283, "utf8mb4", "utf8mb4_sl_0900_as_cs", false}, + {284, "utf8mb4", "utf8mb4_pl_0900_as_cs", false}, + {285, "utf8mb4", "utf8mb4_et_0900_as_cs", false}, + {286, "utf8mb4", "utf8mb4_es_0900_as_cs", false}, + {287, "utf8mb4", "utf8mb4_sv_0900_as_cs", false}, + {288, "utf8mb4", "utf8mb4_tr_0900_as_cs", false}, + {289, "utf8mb4", "utf8mb4_cs_0900_as_cs", false}, + {290, "utf8mb4", "utf8mb4_da_0900_as_cs", false}, + {291, "utf8mb4", "utf8mb4_lt_0900_as_cs", false}, + {292, "utf8mb4", "utf8mb4_sk_0900_as_cs", false}, + {293, "utf8mb4", "utf8mb4_es_trad_0900_as_cs", false}, + {294, "utf8mb4", "utf8mb4_la_0900_as_cs", false}, + {296, "utf8mb4", "utf8mb4_eo_0900_as_cs", false}, + {297, "utf8mb4", "utf8mb4_hu_0900_as_cs", false}, + {298, "utf8mb4", "utf8mb4_hr_0900_as_cs", false}, + {300, "utf8mb4", "utf8mb4_vi_0900_as_cs", false}, + {303, "utf8mb4", "utf8mb4_ja_0900_as_cs", false}, + {304, "utf8mb4", "utf8mb4_ja_0900_as_cs_ks", false}, + {305, "utf8mb4", "utf8mb4_0900_as_ci", false}, + {306, "utf8mb4", "utf8mb4_ru_0900_ai_ci", false}, + {307, "utf8mb4", "utf8mb4_ru_0900_as_cs", false}, + {308, "utf8mb4", "utf8mb4_zh_0900_as_cs", false}, + {309, "utf8mb4", "utf8mb4_0900_bin", false}, + {2048, "utf8mb4", "utf8mb4_zh_pinyin_tidb_as_cs", false}, +} + +// AddCharset adds a new charset. +// Use only when adding a custom charset to the parser. +func AddCharset(c *Charset) { + CharacterSetInfos[c.Name] = c +} + +// RemoveCharset remove a charset. +// Use only when remove a custom charset to the parser. +func RemoveCharset(c string) { + delete(CharacterSetInfos, c) + for i := range supportedCollations { + if supportedCollations[i].Name == c { + supportedCollations = append(supportedCollations[:i], supportedCollations[i+1:]...) + } + } +} + +// AddCollation adds a new collation. +// Use only when adding a custom collation to the parser. +func AddCollation(c *Collation) { + collationsIDMap[c.ID] = c + collationsNameMap[c.Name] = c + + if _, ok := supportedCollationNames[c.Name]; ok { + AddSupportedCollation(c) + } + + if charset, ok := CharacterSetInfos[c.CharsetName]; ok { + charset.Collations[c.Name] = c + } + + if charset, ok := charsets[c.CharsetName]; ok { + charset.Collations[c.Name] = c + } +} + +// AddSupportedCollation adds a new collation into supportedCollations. +// Use only when adding a custom collation to the parser. +func AddSupportedCollation(c *Collation) { + supportedCollations = append(supportedCollations, c) +} + +// init method always puts to the end of file. +func init() { + for _, c := range collations { + AddCollation(c) + } +} diff --git a/parser/charset/charset_test.go b/pkg/parser/charset/charset_test.go similarity index 100% rename from parser/charset/charset_test.go rename to pkg/parser/charset/charset_test.go diff --git a/parser/charset/encoding.go b/pkg/parser/charset/encoding.go similarity index 100% rename from parser/charset/encoding.go rename to pkg/parser/charset/encoding.go diff --git a/parser/charset/encoding_ascii.go b/pkg/parser/charset/encoding_ascii.go similarity index 100% rename from parser/charset/encoding_ascii.go rename to pkg/parser/charset/encoding_ascii.go diff --git a/parser/charset/encoding_base.go b/pkg/parser/charset/encoding_base.go similarity index 97% rename from parser/charset/encoding_base.go rename to pkg/parser/charset/encoding_base.go index 529734fb8a0cf..730aff23ccd61 100644 --- a/parser/charset/encoding_base.go +++ b/pkg/parser/charset/encoding_base.go @@ -20,8 +20,8 @@ import ( "strings" "unsafe" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" "golang.org/x/text/encoding" "golang.org/x/text/transform" ) diff --git a/parser/charset/encoding_bin.go b/pkg/parser/charset/encoding_bin.go similarity index 100% rename from parser/charset/encoding_bin.go rename to pkg/parser/charset/encoding_bin.go diff --git a/parser/charset/encoding_gbk.go b/pkg/parser/charset/encoding_gbk.go similarity index 100% rename from parser/charset/encoding_gbk.go rename to pkg/parser/charset/encoding_gbk.go diff --git a/parser/charset/encoding_latin1.go b/pkg/parser/charset/encoding_latin1.go similarity index 100% rename from parser/charset/encoding_latin1.go rename to pkg/parser/charset/encoding_latin1.go diff --git a/parser/charset/encoding_table.go b/pkg/parser/charset/encoding_table.go similarity index 100% rename from parser/charset/encoding_table.go rename to pkg/parser/charset/encoding_table.go diff --git a/parser/charset/encoding_test.go b/pkg/parser/charset/encoding_test.go similarity index 99% rename from parser/charset/encoding_test.go rename to pkg/parser/charset/encoding_test.go index 8322d2f611f06..ebe85c87b3412 100644 --- a/parser/charset/encoding_test.go +++ b/pkg/parser/charset/encoding_test.go @@ -18,7 +18,7 @@ import ( "testing" "unicode/utf8" - "github.com/pingcap/tidb/parser/charset" + "github.com/pingcap/tidb/pkg/parser/charset" "github.com/stretchr/testify/require" "golang.org/x/text/transform" ) diff --git a/parser/charset/encoding_utf8.go b/pkg/parser/charset/encoding_utf8.go similarity index 100% rename from parser/charset/encoding_utf8.go rename to pkg/parser/charset/encoding_utf8.go diff --git a/parser/consistent_test.go b/pkg/parser/consistent_test.go similarity index 100% rename from parser/consistent_test.go rename to pkg/parser/consistent_test.go diff --git a/parser/digester.go b/pkg/parser/digester.go similarity index 99% rename from parser/digester.go rename to pkg/parser/digester.go index da39a02137f42..8c5fad0e47a01 100644 --- a/parser/digester.go +++ b/pkg/parser/digester.go @@ -23,7 +23,7 @@ import ( "sync" "unsafe" - "github.com/pingcap/tidb/parser/charset" + "github.com/pingcap/tidb/pkg/parser/charset" ) // Digest stores the fixed length hash value. diff --git a/parser/digester_test.go b/pkg/parser/digester_test.go similarity index 99% rename from parser/digester_test.go rename to pkg/parser/digester_test.go index 2e17481943e42..58fa5723c3314 100644 --- a/parser/digester_test.go +++ b/pkg/parser/digester_test.go @@ -19,7 +19,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser" + "github.com/pingcap/tidb/pkg/parser" "github.com/stretchr/testify/require" ) diff --git a/parser/docs/quickstart.md b/pkg/parser/docs/quickstart.md similarity index 100% rename from parser/docs/quickstart.md rename to pkg/parser/docs/quickstart.md diff --git a/pkg/parser/duration/BUILD.bazel b/pkg/parser/duration/BUILD.bazel new file mode 100644 index 0000000000000..e25543983dbe7 --- /dev/null +++ b/pkg/parser/duration/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "duration", + srcs = ["duration.go"], + importpath = "github.com/pingcap/tidb/pkg/parser/duration", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "duration_test", + timeout = "short", + srcs = ["duration_test.go"], + embed = [":duration"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/parser/duration/duration.go b/pkg/parser/duration/duration.go similarity index 100% rename from parser/duration/duration.go rename to pkg/parser/duration/duration.go diff --git a/parser/duration/duration_test.go b/pkg/parser/duration/duration_test.go similarity index 100% rename from parser/duration/duration_test.go rename to pkg/parser/duration/duration_test.go diff --git a/pkg/parser/format/BUILD.bazel b/pkg/parser/format/BUILD.bazel new file mode 100644 index 0000000000000..66842a8740fe7 --- /dev/null +++ b/pkg/parser/format/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "format", + srcs = ["format.go"], + importpath = "github.com/pingcap/tidb/pkg/parser/format", + visibility = ["//visibility:public"], +) + +go_test( + name = "format_test", + timeout = "short", + srcs = ["format_test.go"], + embed = [":format"], + flaky = True, + shard_count = 3, + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) diff --git a/parser/format/format.go b/pkg/parser/format/format.go similarity index 100% rename from parser/format/format.go rename to pkg/parser/format/format.go diff --git a/parser/format/format_test.go b/pkg/parser/format/format_test.go similarity index 100% rename from parser/format/format_test.go rename to pkg/parser/format/format_test.go diff --git a/parser/go.mod b/pkg/parser/go.mod similarity index 96% rename from parser/go.mod rename to pkg/parser/go.mod index d642b0ee7578e..8bc3326719c0a 100644 --- a/parser/go.mod +++ b/pkg/parser/go.mod @@ -1,4 +1,4 @@ -module github.com/pingcap/tidb/parser +module github.com/pingcap/tidb/pkg/parser require ( github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 diff --git a/parser/go.sum b/pkg/parser/go.sum similarity index 100% rename from parser/go.sum rename to pkg/parser/go.sum diff --git a/pkg/parser/goyacc/BUILD.bazel b/pkg/parser/goyacc/BUILD.bazel new file mode 100644 index 0000000000000..8fc378a5e2cce --- /dev/null +++ b/pkg/parser/goyacc/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "goyacc_lib", + srcs = [ + "format_yacc.go", + "main.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/goyacc", + visibility = ["//visibility:private"], + deps = [ + "//pkg/parser/format", + "@com_github_cznic_mathutil//:mathutil", + "@com_github_cznic_sortutil//:sortutil", + "@com_github_cznic_strutil//:strutil", + "@com_github_pingcap_errors//:errors", + "@org_modernc_parser//yacc", + "@org_modernc_y//:y", + ], +) + +go_binary( + name = "goyacc", + embed = [":goyacc_lib"], + visibility = ["//visibility:public"], +) diff --git a/parser/goyacc/format_yacc.go b/pkg/parser/goyacc/format_yacc.go similarity index 99% rename from parser/goyacc/format_yacc.go rename to pkg/parser/goyacc/format_yacc.go index d52eacdc78609..a79cd874b5aaa 100644 --- a/parser/goyacc/format_yacc.go +++ b/pkg/parser/goyacc/format_yacc.go @@ -24,7 +24,7 @@ import ( "github.com/cznic/strutil" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser/format" parser "modernc.org/parser/yacc" ) diff --git a/parser/goyacc/main.go b/pkg/parser/goyacc/main.go similarity index 100% rename from parser/goyacc/main.go rename to pkg/parser/goyacc/main.go diff --git a/parser/hintparser.go b/pkg/parser/hintparser.go similarity index 99% rename from parser/hintparser.go rename to pkg/parser/hintparser.go index 54bf4db01e5b0..b634df616ca17 100644 --- a/parser/hintparser.go +++ b/pkg/parser/hintparser.go @@ -21,8 +21,8 @@ import ( "math" "strconv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" ) type yyhintSymType struct { diff --git a/parser/hintparser.y b/pkg/parser/hintparser.y similarity index 99% rename from parser/hintparser.y rename to pkg/parser/hintparser.y index 1235152abea0e..73476be61b279 100644 --- a/parser/hintparser.y +++ b/pkg/parser/hintparser.y @@ -18,8 +18,8 @@ import ( "math" "strconv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" ) %} diff --git a/parser/hintparser_test.go b/pkg/parser/hintparser_test.go similarity index 98% rename from parser/hintparser_test.go rename to pkg/parser/hintparser_test.go index 696cebd6c7be6..862f81ff6e49f 100644 --- a/parser/hintparser_test.go +++ b/pkg/parser/hintparser_test.go @@ -16,10 +16,10 @@ package parser_test import ( "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/parser/hintparserimpl.go b/pkg/parser/hintparserimpl.go similarity index 97% rename from parser/hintparserimpl.go rename to pkg/parser/hintparserimpl.go index 88a0dec64db8b..534acf855f132 100644 --- a/parser/hintparserimpl.go +++ b/pkg/parser/hintparserimpl.go @@ -17,9 +17,9 @@ import ( "strconv" "strings" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" ) //revive:disable:exported diff --git a/parser/lexer.go b/pkg/parser/lexer.go similarity index 99% rename from parser/lexer.go rename to pkg/parser/lexer.go index dcfae1d1f5f54..66d47a1b3df20 100644 --- a/parser/lexer.go +++ b/pkg/parser/lexer.go @@ -20,9 +20,9 @@ import ( "strings" "unicode" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - tidbfeature "github.com/pingcap/tidb/parser/tidb" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + tidbfeature "github.com/pingcap/tidb/pkg/parser/tidb" ) var _ = yyLexer(&Scanner{}) diff --git a/parser/lexer_test.go b/pkg/parser/lexer_test.go similarity index 99% rename from parser/lexer_test.go rename to pkg/parser/lexer_test.go index 3328bc315237c..05ecfeb77d452 100644 --- a/parser/lexer_test.go +++ b/pkg/parser/lexer_test.go @@ -18,7 +18,7 @@ import ( "testing" "unicode" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" requires "github.com/stretchr/testify/require" ) diff --git a/parser/main_test.go b/pkg/parser/main_test.go similarity index 100% rename from parser/main_test.go rename to pkg/parser/main_test.go diff --git a/parser/misc.go b/pkg/parser/misc.go similarity index 100% rename from parser/misc.go rename to pkg/parser/misc.go diff --git a/pkg/parser/model/BUILD.bazel b/pkg/parser/model/BUILD.bazel new file mode 100644 index 0000000000000..e1c9fab9fea24 --- /dev/null +++ b/pkg/parser/model/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "model", + srcs = [ + "ddl.go", + "flags.go", + "model.go", + "reorg.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/model", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/duration", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "model_test", + timeout = "short", + srcs = [ + "ddl_test.go", + "model_test.go", + ], + embed = [":model"], + flaky = True, + shard_count = 20, + deps = [ + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/parser/model/ddl.go b/pkg/parser/model/ddl.go new file mode 100644 index 0000000000000..ec12b06bde7ae --- /dev/null +++ b/pkg/parser/model/ddl.go @@ -0,0 +1,1000 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" +) + +// ActionType is the type for DDL action. +type ActionType byte + +// List DDL actions. +const ( + ActionNone ActionType = 0 + ActionCreateSchema ActionType = 1 + ActionDropSchema ActionType = 2 + ActionCreateTable ActionType = 3 + ActionDropTable ActionType = 4 + ActionAddColumn ActionType = 5 + ActionDropColumn ActionType = 6 + ActionAddIndex ActionType = 7 + ActionDropIndex ActionType = 8 + ActionAddForeignKey ActionType = 9 + ActionDropForeignKey ActionType = 10 + ActionTruncateTable ActionType = 11 + ActionModifyColumn ActionType = 12 + ActionRebaseAutoID ActionType = 13 + ActionRenameTable ActionType = 14 + ActionSetDefaultValue ActionType = 15 + ActionShardRowID ActionType = 16 + ActionModifyTableComment ActionType = 17 + ActionRenameIndex ActionType = 18 + ActionAddTablePartition ActionType = 19 + ActionDropTablePartition ActionType = 20 + ActionCreateView ActionType = 21 + ActionModifyTableCharsetAndCollate ActionType = 22 + ActionTruncateTablePartition ActionType = 23 + ActionDropView ActionType = 24 + ActionRecoverTable ActionType = 25 + ActionModifySchemaCharsetAndCollate ActionType = 26 + ActionLockTable ActionType = 27 + ActionUnlockTable ActionType = 28 + ActionRepairTable ActionType = 29 + ActionSetTiFlashReplica ActionType = 30 + ActionUpdateTiFlashReplicaStatus ActionType = 31 + ActionAddPrimaryKey ActionType = 32 + ActionDropPrimaryKey ActionType = 33 + ActionCreateSequence ActionType = 34 + ActionAlterSequence ActionType = 35 + ActionDropSequence ActionType = 36 + ActionAddColumns ActionType = 37 // Deprecated, we use ActionMultiSchemaChange instead. + ActionDropColumns ActionType = 38 // Deprecated, we use ActionMultiSchemaChange instead. + ActionModifyTableAutoIdCache ActionType = 39 //nolint:revive + ActionRebaseAutoRandomBase ActionType = 40 + ActionAlterIndexVisibility ActionType = 41 + ActionExchangeTablePartition ActionType = 42 + ActionAddCheckConstraint ActionType = 43 + ActionDropCheckConstraint ActionType = 44 + ActionAlterCheckConstraint ActionType = 45 + + // `ActionAlterTableAlterPartition` is removed and will never be used. + // Just left a tombstone here for compatibility. + __DEPRECATED_ActionAlterTableAlterPartition ActionType = 46 //nolint:revive + + ActionRenameTables ActionType = 47 + ActionDropIndexes ActionType = 48 // Deprecated, we use ActionMultiSchemaChange instead. + ActionAlterTableAttributes ActionType = 49 + ActionAlterTablePartitionAttributes ActionType = 50 + ActionCreatePlacementPolicy ActionType = 51 + ActionAlterPlacementPolicy ActionType = 52 + ActionDropPlacementPolicy ActionType = 53 + ActionAlterTablePartitionPlacement ActionType = 54 + ActionModifySchemaDefaultPlacement ActionType = 55 + ActionAlterTablePlacement ActionType = 56 + ActionAlterCacheTable ActionType = 57 + ActionAlterTableStatsOptions ActionType = 58 + ActionAlterNoCacheTable ActionType = 59 + ActionCreateTables ActionType = 60 + ActionMultiSchemaChange ActionType = 61 + ActionFlashbackCluster ActionType = 62 + ActionRecoverSchema ActionType = 63 + ActionReorganizePartition ActionType = 64 + ActionAlterTTLInfo ActionType = 65 + ActionAlterTTLRemove ActionType = 67 + ActionCreateResourceGroup ActionType = 68 + ActionAlterResourceGroup ActionType = 69 + ActionDropResourceGroup ActionType = 70 + ActionAlterTablePartitioning ActionType = 71 + ActionRemovePartitioning ActionType = 72 +) + +var actionMap = map[ActionType]string{ + ActionCreateSchema: "create schema", + ActionDropSchema: "drop schema", + ActionCreateTable: "create table", + ActionCreateTables: "create tables", + ActionDropTable: "drop table", + ActionAddColumn: "add column", + ActionDropColumn: "drop column", + ActionAddIndex: "add index", + ActionDropIndex: "drop index", + ActionAddForeignKey: "add foreign key", + ActionDropForeignKey: "drop foreign key", + ActionTruncateTable: "truncate table", + ActionModifyColumn: "modify column", + ActionRebaseAutoID: "rebase auto_increment ID", + ActionRenameTable: "rename table", + ActionRenameTables: "rename tables", + ActionSetDefaultValue: "set default value", + ActionShardRowID: "shard row ID", + ActionModifyTableComment: "modify table comment", + ActionRenameIndex: "rename index", + ActionAddTablePartition: "add partition", + ActionDropTablePartition: "drop partition", + ActionCreateView: "create view", + ActionModifyTableCharsetAndCollate: "modify table charset and collate", + ActionTruncateTablePartition: "truncate partition", + ActionDropView: "drop view", + ActionRecoverTable: "recover table", + ActionModifySchemaCharsetAndCollate: "modify schema charset and collate", + ActionLockTable: "lock table", + ActionUnlockTable: "unlock table", + ActionRepairTable: "repair table", + ActionSetTiFlashReplica: "set tiflash replica", + ActionUpdateTiFlashReplicaStatus: "update tiflash replica status", + ActionAddPrimaryKey: "add primary key", + ActionDropPrimaryKey: "drop primary key", + ActionCreateSequence: "create sequence", + ActionAlterSequence: "alter sequence", + ActionDropSequence: "drop sequence", + ActionModifyTableAutoIdCache: "modify auto id cache", + ActionRebaseAutoRandomBase: "rebase auto_random ID", + ActionAlterIndexVisibility: "alter index visibility", + ActionExchangeTablePartition: "exchange partition", + ActionAddCheckConstraint: "add check constraint", + ActionDropCheckConstraint: "drop check constraint", + ActionAlterCheckConstraint: "alter check constraint", + ActionAlterTableAttributes: "alter table attributes", + ActionAlterTablePartitionPlacement: "alter table partition placement", + ActionAlterTablePartitionAttributes: "alter table partition attributes", + ActionCreatePlacementPolicy: "create placement policy", + ActionAlterPlacementPolicy: "alter placement policy", + ActionDropPlacementPolicy: "drop placement policy", + ActionModifySchemaDefaultPlacement: "modify schema default placement", + ActionAlterTablePlacement: "alter table placement", + ActionAlterCacheTable: "alter table cache", + ActionAlterNoCacheTable: "alter table nocache", + ActionAlterTableStatsOptions: "alter table statistics options", + ActionMultiSchemaChange: "alter table multi-schema change", + ActionFlashbackCluster: "flashback cluster", + ActionRecoverSchema: "flashback schema", + ActionReorganizePartition: "alter table reorganize partition", + ActionAlterTTLInfo: "alter table ttl", + ActionAlterTTLRemove: "alter table no_ttl", + ActionCreateResourceGroup: "create resource group", + ActionAlterResourceGroup: "alter resource group", + ActionDropResourceGroup: "drop resource group", + ActionAlterTablePartitioning: "alter table partition by", + ActionRemovePartitioning: "alter table remove partitioning", + + // `ActionAlterTableAlterPartition` is removed and will never be used. + // Just left a tombstone here for compatibility. + __DEPRECATED_ActionAlterTableAlterPartition: "alter partition", +} + +// String return current ddl action in string +func (action ActionType) String() string { + if v, ok := actionMap[action]; ok { + return v + } + return "none" +} + +// HistoryInfo is used for binlog. +type HistoryInfo struct { + SchemaVersion int64 + DBInfo *DBInfo + TableInfo *TableInfo + FinishedTS uint64 + + // MultipleTableInfos is like TableInfo but only for operations updating multiple tables. + MultipleTableInfos []*TableInfo +} + +// AddDBInfo adds schema version and schema information that are used for binlog. +// dbInfo is added in the following operations: create database, drop database. +func (h *HistoryInfo) AddDBInfo(schemaVer int64, dbInfo *DBInfo) { + h.SchemaVersion = schemaVer + h.DBInfo = dbInfo +} + +// AddTableInfo adds schema version and table information that are used for binlog. +// tblInfo is added except for the following operations: create database, drop database. +func (h *HistoryInfo) AddTableInfo(schemaVer int64, tblInfo *TableInfo) { + h.SchemaVersion = schemaVer + h.TableInfo = tblInfo +} + +// SetTableInfos is like AddTableInfo, but will add multiple table infos to the binlog. +func (h *HistoryInfo) SetTableInfos(schemaVer int64, tblInfos []*TableInfo) { + h.SchemaVersion = schemaVer + h.MultipleTableInfos = make([]*TableInfo, len(tblInfos)) + copy(h.MultipleTableInfos, tblInfos) +} + +// Clean cleans history information. +func (h *HistoryInfo) Clean() { + h.SchemaVersion = 0 + h.DBInfo = nil + h.TableInfo = nil + h.MultipleTableInfos = nil +} + +// TimeZoneLocation represents a single time zone. +type TimeZoneLocation struct { + Name string `json:"name"` + Offset int `json:"offset"` // seconds east of UTC + location *time.Location +} + +// GetLocation gets the timezone location. +func (tz *TimeZoneLocation) GetLocation() (*time.Location, error) { + if tz.location != nil { + return tz.location, nil + } + + var err error + if tz.Offset == 0 { + tz.location, err = time.LoadLocation(tz.Name) + } else { + tz.location = time.FixedZone(tz.Name, tz.Offset) + } + return tz.location, err +} + +// MultiSchemaInfo keeps some information for multi schema change. +type MultiSchemaInfo struct { + SubJobs []*SubJob `json:"sub_jobs"` + Revertible bool `json:"revertible"` + Seq int32 `json:"seq"` + + // SkipVersion is used to control whether generating a new schema version for a sub-job. + SkipVersion bool `json:"-"` + + AddColumns []CIStr `json:"-"` + DropColumns []CIStr `json:"-"` + ModifyColumns []CIStr `json:"-"` + AddIndexes []CIStr `json:"-"` + DropIndexes []CIStr `json:"-"` + AlterIndexes []CIStr `json:"-"` + + AddForeignKeys []AddForeignKeyInfo `json:"-"` + + RelativeColumns []CIStr `json:"-"` + PositionColumns []CIStr `json:"-"` +} + +// AddForeignKeyInfo contains foreign key information. +type AddForeignKeyInfo struct { + Name CIStr + Cols []CIStr +} + +// NewMultiSchemaInfo new a MultiSchemaInfo. +func NewMultiSchemaInfo() *MultiSchemaInfo { + return &MultiSchemaInfo{ + SubJobs: nil, + Revertible: true, + } +} + +// SubJob is a representation of one DDL schema change. A Job may contain zero(when multi-schema change is not applicable) or more SubJobs. +type SubJob struct { + Type ActionType `json:"type"` + Args []interface{} `json:"-"` + RawArgs json.RawMessage `json:"raw_args"` + SchemaState SchemaState `json:"schema_state"` + SnapshotVer uint64 `json:"snapshot_ver"` + RealStartTS uint64 `json:"real_start_ts"` + Revertible bool `json:"revertible"` + State JobState `json:"state"` + RowCount int64 `json:"row_count"` + Warning *terror.Error `json:"warning"` + CtxVars []interface{} `json:"-"` + SchemaVer int64 `json:"schema_version"` + ReorgTp ReorgType `json:"reorg_tp"` +} + +// IsNormal returns true if the sub-job is normally running. +func (sub *SubJob) IsNormal() bool { + switch sub.State { + case JobStateCancelling, JobStateCancelled, + JobStateRollingback, JobStateRollbackDone: + return false + default: + return true + } +} + +// IsFinished returns true if the job is done. +func (sub *SubJob) IsFinished() bool { + return sub.State == JobStateDone || + sub.State == JobStateRollbackDone || + sub.State == JobStateCancelled +} + +// ToProxyJob converts a sub-job to a proxy job. +func (sub *SubJob) ToProxyJob(parentJob *Job, seq int) Job { + return Job{ + ID: parentJob.ID, + Type: sub.Type, + SchemaID: parentJob.SchemaID, + TableID: parentJob.TableID, + SchemaName: parentJob.SchemaName, + State: sub.State, + Warning: sub.Warning, + Error: nil, + ErrorCount: 0, + RowCount: sub.RowCount, + Mu: sync.Mutex{}, + CtxVars: sub.CtxVars, + Args: sub.Args, + RawArgs: sub.RawArgs, + SchemaState: sub.SchemaState, + SnapshotVer: sub.SnapshotVer, + RealStartTS: sub.RealStartTS, + StartTS: parentJob.StartTS, + DependencyID: parentJob.DependencyID, + Query: parentJob.Query, + BinlogInfo: parentJob.BinlogInfo, + Version: parentJob.Version, + ReorgMeta: parentJob.ReorgMeta, + MultiSchemaInfo: &MultiSchemaInfo{Revertible: sub.Revertible, Seq: int32(seq)}, + Priority: parentJob.Priority, + SeqNum: parentJob.SeqNum, + Charset: parentJob.Charset, + Collate: parentJob.Collate, + AdminOperator: parentJob.AdminOperator, + TraceInfo: parentJob.TraceInfo, + } +} + +// FromProxyJob converts a proxy job to a sub-job. +func (sub *SubJob) FromProxyJob(proxyJob *Job, ver int64) { + sub.Revertible = proxyJob.MultiSchemaInfo.Revertible + sub.SchemaState = proxyJob.SchemaState + sub.SnapshotVer = proxyJob.SnapshotVer + sub.RealStartTS = proxyJob.RealStartTS + sub.Args = proxyJob.Args + sub.State = proxyJob.State + sub.Warning = proxyJob.Warning + sub.RowCount = proxyJob.RowCount + sub.SchemaVer = ver + sub.ReorgTp = proxyJob.ReorgMeta.ReorgTp +} + +// JobMeta is meta info of Job. +type JobMeta struct { + SchemaID int64 `json:"schema_id"` + TableID int64 `json:"table_id"` + // Type is the DDL job's type. + Type ActionType `json:"job_type"` + // Query is the DDL job's SQL string. + Query string `json:"query"` + // Priority is only used to set the operation priority of adding indices. + Priority int `json:"priority"` +} + +// Job is for a DDL operation. +type Job struct { + ID int64 `json:"id"` + Type ActionType `json:"type"` + SchemaID int64 `json:"schema_id"` + TableID int64 `json:"table_id"` + SchemaName string `json:"schema_name"` + TableName string `json:"table_name"` + State JobState `json:"state"` + Warning *terror.Error `json:"warning"` + Error *terror.Error `json:"err"` + // ErrorCount will be increased, every time we meet an error when running job. + ErrorCount int64 `json:"err_count"` + // RowCount means the number of rows that are processed. + RowCount int64 `json:"row_count"` + Mu sync.Mutex `json:"-"` + // CtxVars are variables attached to the job. It is for internal usage. + // E.g. passing arguments between functions by one single *Job pointer. + CtxVars []interface{} `json:"-"` + Args []interface{} `json:"-"` + // RawArgs : We must use json raw message to delay parsing special args. + RawArgs json.RawMessage `json:"raw_args"` + SchemaState SchemaState `json:"schema_state"` + // SnapshotVer means snapshot version for this job. + SnapshotVer uint64 `json:"snapshot_ver"` + // RealStartTS uses timestamp allocated by TSO. + // Now it's the TS when we actually start the job. + RealStartTS uint64 `json:"real_start_ts"` + // StartTS uses timestamp allocated by TSO. + // Now it's the TS when we put the job to TiKV queue. + StartTS uint64 `json:"start_ts"` + // DependencyID is the job's ID that the current job depends on. + DependencyID int64 `json:"dependency_id"` + // Query string of the ddl job. + Query string `json:"query"` + BinlogInfo *HistoryInfo `json:"binlog"` + + // Version indicates the DDL job version. For old jobs, it will be 0. + Version int64 `json:"version"` + + // ReorgMeta is meta info of ddl reorganization. + ReorgMeta *DDLReorgMeta `json:"reorg_meta"` + + // MultiSchemaInfo keeps some warning now for multi schema change. + MultiSchemaInfo *MultiSchemaInfo `json:"multi_schema_info"` + + // Priority is only used to set the operation priority of adding indices. + Priority int `json:"priority"` + + // SeqNum is the total order in all DDLs, it's used to identify the order of DDL. + SeqNum uint64 `json:"seq_num"` + + // Charset is the charset when the DDL Job is created. + Charset string `json:"charset"` + // Collate is the collation the DDL Job is created. + Collate string `json:"collate"` + + // AdminOperator indicates where the Admin command comes, by the TiDB + // itself (AdminCommandBySystem) or by user (AdminCommandByEndUser). + AdminOperator AdminCommandOperator `json:"admin_operator"` + + // TraceInfo indicates the information for SQL tracing + TraceInfo *TraceInfo `json:"trace_info"` +} + +// FinishTableJob is called when a job is finished. +// It updates the job's state information and adds tblInfo to the binlog. +func (job *Job) FinishTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfo *TableInfo) { + job.State = jobState + job.SchemaState = schemaState + job.BinlogInfo.AddTableInfo(ver, tblInfo) +} + +// FinishMultipleTableJob is called when a job is finished. +// It updates the job's state information and adds tblInfos to the binlog. +func (job *Job) FinishMultipleTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfos []*TableInfo) { + job.State = jobState + job.SchemaState = schemaState + job.BinlogInfo.SchemaVersion = ver + job.BinlogInfo.MultipleTableInfos = tblInfos + job.BinlogInfo.TableInfo = tblInfos[len(tblInfos)-1] +} + +// FinishDBJob is called when a job is finished. +// It updates the job's state information and adds dbInfo the binlog. +func (job *Job) FinishDBJob(jobState JobState, schemaState SchemaState, ver int64, dbInfo *DBInfo) { + job.State = jobState + job.SchemaState = schemaState + job.BinlogInfo.AddDBInfo(ver, dbInfo) +} + +// MarkNonRevertible mark the current job to be non-revertible. +// It means the job cannot be cancelled or rollbacked. +func (job *Job) MarkNonRevertible() { + if job.MultiSchemaInfo != nil { + job.MultiSchemaInfo.Revertible = false + } +} + +// Clone returns a copy of the job. +func (job *Job) Clone() *Job { + encode, err := job.Encode(true) + if err != nil { + return nil + } + var clone Job + err = clone.Decode(encode) + if err != nil { + return nil + } + if len(job.Args) > 0 { + clone.Args = make([]interface{}, len(job.Args)) + copy(clone.Args, job.Args) + } + if job.MultiSchemaInfo != nil { + for i, sub := range job.MultiSchemaInfo.SubJobs { + clone.MultiSchemaInfo.SubJobs[i].Args = make([]interface{}, len(sub.Args)) + copy(clone.MultiSchemaInfo.SubJobs[i].Args, sub.Args) + } + } + return &clone +} + +// TSConvert2Time converts timestamp to time. +func TSConvert2Time(ts uint64) time.Time { + t := int64(ts >> 18) // 18 is for the logical time. + return time.UnixMilli(t) +} + +// SetRowCount sets the number of rows. Make sure it can pass `make race`. +func (job *Job) SetRowCount(count int64) { + job.Mu.Lock() + defer job.Mu.Unlock() + + job.RowCount = count +} + +// GetRowCount gets the number of rows. Make sure it can pass `make race`. +func (job *Job) GetRowCount() int64 { + job.Mu.Lock() + defer job.Mu.Unlock() + + return job.RowCount +} + +// SetWarnings sets the warnings of rows handled. +func (job *Job) SetWarnings(warnings map[errors.ErrorID]*terror.Error, warningsCount map[errors.ErrorID]int64) { + job.Mu.Lock() + job.ReorgMeta.Warnings = warnings + job.ReorgMeta.WarningsCount = warningsCount + job.Mu.Unlock() +} + +// GetWarnings gets the warnings of the rows handled. +func (job *Job) GetWarnings() (map[errors.ErrorID]*terror.Error, map[errors.ErrorID]int64) { + job.Mu.Lock() + w, wc := job.ReorgMeta.Warnings, job.ReorgMeta.WarningsCount + job.Mu.Unlock() + return w, wc +} + +// Encode encodes job with json format. +// updateRawArgs is used to determine whether to update the raw args. +func (job *Job) Encode(updateRawArgs bool) ([]byte, error) { + var err error + if updateRawArgs { + job.RawArgs, err = json.Marshal(job.Args) + if err != nil { + return nil, errors.Trace(err) + } + if job.MultiSchemaInfo != nil { + for _, sub := range job.MultiSchemaInfo.SubJobs { + // Only update the args of executing sub-jobs. + if sub.Args == nil { + continue + } + sub.RawArgs, err = json.Marshal(sub.Args) + if err != nil { + return nil, errors.Trace(err) + } + } + } + } + + var b []byte + job.Mu.Lock() + defer job.Mu.Unlock() + b, err = json.Marshal(job) + + return b, errors.Trace(err) +} + +// Decode decodes job from the json buffer, we must use DecodeArgs later to +// decode special args for this job. +func (job *Job) Decode(b []byte) error { + err := json.Unmarshal(b, job) + return errors.Trace(err) +} + +// DecodeArgs decodes job args. +func (job *Job) DecodeArgs(args ...interface{}) error { + var rawArgs []json.RawMessage + if err := json.Unmarshal(job.RawArgs, &rawArgs); err != nil { + return errors.Trace(err) + } + + sz := len(rawArgs) + if sz > len(args) { + sz = len(args) + } + + for i := 0; i < sz; i++ { + if err := json.Unmarshal(rawArgs[i], args[i]); err != nil { + return errors.Trace(err) + } + } + job.Args = args[:sz] + return nil +} + +// String implements fmt.Stringer interface. +func (job *Job) String() string { + rowCount := job.GetRowCount() + ret := fmt.Sprintf("ID:%d, Type:%s, State:%s, SchemaState:%s, SchemaID:%d, TableID:%d, RowCount:%d, ArgLen:%d, start time: %v, Err:%v, ErrCount:%d, SnapshotVersion:%v", + job.ID, job.Type, job.State, job.SchemaState, job.SchemaID, job.TableID, rowCount, len(job.Args), TSConvert2Time(job.StartTS), job.Error, job.ErrorCount, job.SnapshotVer) + if job.ReorgMeta != nil { + warnings, _ := job.GetWarnings() + ret += fmt.Sprintf(", UniqueWarnings:%d", len(warnings)) + } + if job.Type != ActionMultiSchemaChange && job.MultiSchemaInfo != nil { + ret += fmt.Sprintf(", Multi-Schema Change:true, Revertible:%v", job.MultiSchemaInfo.Revertible) + } + return ret +} + +func (job *Job) hasDependentSchema(other *Job) (bool, error) { + if other.Type == ActionDropSchema || other.Type == ActionCreateSchema { + if other.SchemaID == job.SchemaID { + return true, nil + } + if job.Type == ActionRenameTable { + var oldSchemaID int64 + if err := job.DecodeArgs(&oldSchemaID); err != nil { + return false, errors.Trace(err) + } + if other.SchemaID == oldSchemaID { + return true, nil + } + } + if job.Type == ActionExchangeTablePartition { + var ( + defID int64 + ptSchemaID int64 + ptID int64 + partName string + withValidation bool + ) + if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { + return false, errors.Trace(err) + } + if other.SchemaID == ptSchemaID { + return true, nil + } + } + } + return false, nil +} + +func (job *Job) hasDependentTableForExchangePartition(other *Job) (bool, error) { + if job.Type == ActionExchangeTablePartition { + var ( + defID int64 + ptSchemaID int64 + ptID int64 + partName string + withValidation bool + ) + + if err := job.DecodeArgs(&defID, &ptSchemaID, &ptID, &partName, &withValidation); err != nil { + return false, errors.Trace(err) + } + if ptID == other.TableID || defID == other.TableID { + return true, nil + } + + if other.Type == ActionExchangeTablePartition { + var ( + otherDefID int64 + otherPtSchemaID int64 + otherPtID int64 + otherPartName string + otherWithValidation bool + ) + if err := other.DecodeArgs(&otherDefID, &otherPtSchemaID, &otherPtID, &otherPartName, &otherWithValidation); err != nil { + return false, errors.Trace(err) + } + if job.TableID == other.TableID || job.TableID == otherPtID || job.TableID == otherDefID { + return true, nil + } + if ptID == other.TableID || ptID == otherPtID || ptID == otherDefID { + return true, nil + } + if defID == other.TableID || defID == otherPtID || defID == otherDefID { + return true, nil + } + } + } + return false, nil +} + +// IsDependentOn returns whether the job depends on "other". +// How to check the job depends on "other"? +// 1. The two jobs handle the same database when one of the two jobs is an ActionDropSchema or ActionCreateSchema type. +// 2. Or the two jobs handle the same table. +// 3. Or other job is flashback cluster. +func (job *Job) IsDependentOn(other *Job) (bool, error) { + if other.Type == ActionFlashbackCluster { + return true, nil + } + + isDependent, err := job.hasDependentSchema(other) + if err != nil || isDependent { + return isDependent, errors.Trace(err) + } + isDependent, err = other.hasDependentSchema(job) + if err != nil || isDependent { + return isDependent, errors.Trace(err) + } + + // TODO: If a job is ActionRenameTable, we need to check table name. + if other.TableID == job.TableID { + return true, nil + } + isDependent, err = job.hasDependentTableForExchangePartition(other) + if err != nil || isDependent { + return isDependent, errors.Trace(err) + } + isDependent, err = other.hasDependentTableForExchangePartition(job) + if err != nil || isDependent { + return isDependent, errors.Trace(err) + } + return false, nil +} + +// IsFinished returns whether job is finished or not. +// If the job state is Done or Cancelled, it is finished. +func (job *Job) IsFinished() bool { + return job.State == JobStateDone || job.State == JobStateRollbackDone || job.State == JobStateCancelled +} + +// IsCancelled returns whether the job is cancelled or not. +func (job *Job) IsCancelled() bool { + return job.State == JobStateCancelled +} + +// IsRollbackDone returns whether the job is rolled back or not. +func (job *Job) IsRollbackDone() bool { + return job.State == JobStateRollbackDone +} + +// IsRollingback returns whether the job is rolling back or not. +func (job *Job) IsRollingback() bool { + return job.State == JobStateRollingback +} + +// IsCancelling returns whether the job is cancelling or not. +func (job *Job) IsCancelling() bool { + return job.State == JobStateCancelling +} + +// IsPaused returns whether the job is paused. +func (job *Job) IsPaused() bool { + return job.State == JobStatePaused +} + +// IsPausedBySystem returns whether the job is paused by system. +func (job *Job) IsPausedBySystem() bool { + return job.IsPaused() && job.AdminOperator == AdminCommandBySystem +} + +// IsPausing indicates whether the job is pausing. +func (job *Job) IsPausing() bool { + return job.State == JobStatePausing +} + +// IsPausable checks whether we can pause the job. +func (job *Job) IsPausable() bool { + return job.NotStarted() || (job.IsRunning() && job.IsRollbackable()) +} + +// IsResumable checks whether the job can be rollback. +func (job *Job) IsResumable() bool { + return job.IsPaused() +} + +// IsSynced returns whether the DDL modification is synced among all TiDB servers. +func (job *Job) IsSynced() bool { + return job.State == JobStateSynced +} + +// IsDone returns whether job is done. +func (job *Job) IsDone() bool { + return job.State == JobStateDone +} + +// IsRunning returns whether job is still running or not. +func (job *Job) IsRunning() bool { + return job.State == JobStateRunning +} + +// IsQueueing returns whether job is queuing or not. +func (job *Job) IsQueueing() bool { + return job.State == JobStateQueueing +} + +// NotStarted returns true if the job is never run by a worker. +func (job *Job) NotStarted() bool { + return job.State == JobStateNone || job.State == JobStateQueueing +} + +// MayNeedReorg indicates that this job may need to reorganize the data. +func (job *Job) MayNeedReorg() bool { + switch job.Type { + case ActionAddIndex, ActionAddPrimaryKey, ActionReorganizePartition, + ActionRemovePartitioning, ActionAlterTablePartitioning: + return true + case ActionModifyColumn: + if len(job.CtxVars) > 0 { + needReorg, ok := job.CtxVars[0].(bool) + return ok && needReorg + } + return false + case ActionMultiSchemaChange: + for _, sub := range job.MultiSchemaInfo.SubJobs { + proxyJob := Job{Type: sub.Type, CtxVars: sub.CtxVars} + if proxyJob.MayNeedReorg() { + return true + } + } + return false + default: + return false + } +} + +// IsRollbackable checks whether the job can be rollback. +func (job *Job) IsRollbackable() bool { + switch job.Type { + case ActionDropIndex, ActionDropPrimaryKey: + // We can't cancel if index current state is in StateDeleteOnly or StateDeleteReorganization or StateWriteOnly, otherwise there will be an inconsistent issue between record and index. + // In WriteOnly state, we can rollback for normal index but can't rollback for expression index(need to drop hidden column). Since we can't + // know the type of index here, we consider all indices except primary index as non-rollbackable. + // TODO: distinguish normal index and expression index so that we can rollback `DropIndex` for normal index in WriteOnly state. + // TODO: make DropPrimaryKey rollbackable in WriteOnly, it need to deal with some tests. + if job.SchemaState == StateDeleteOnly || + job.SchemaState == StateDeleteReorganization || + job.SchemaState == StateWriteOnly { + return false + } + case ActionAddTablePartition: + return job.SchemaState == StateNone || job.SchemaState == StateReplicaOnly + case ActionDropColumn, ActionDropSchema, ActionDropTable, ActionDropSequence, + ActionDropForeignKey, ActionDropTablePartition, ActionTruncateTablePartition: + return job.SchemaState == StatePublic + case ActionRebaseAutoID, ActionShardRowID, + ActionTruncateTable, ActionAddForeignKey, ActionRenameTable, ActionRenameTables, + ActionModifyTableCharsetAndCollate, + ActionModifySchemaCharsetAndCollate, ActionRepairTable, + ActionModifyTableAutoIdCache, ActionModifySchemaDefaultPlacement, ActionDropCheckConstraint: + return job.SchemaState == StateNone + case ActionMultiSchemaChange: + return job.MultiSchemaInfo.Revertible + case ActionFlashbackCluster: + if job.SchemaState == StateWriteReorganization || + job.SchemaState == StateWriteOnly { + return false + } + } + return true +} + +// JobState is for job state. +type JobState int32 + +// List job states. +const ( + JobStateNone JobState = 0 + JobStateRunning JobState = 1 + // When DDL encountered an unrecoverable error at reorganization state, + // some keys has been added already, we need to remove them. + // JobStateRollingback is the state to do the rolling back job. + JobStateRollingback JobState = 2 + JobStateRollbackDone JobState = 3 + JobStateDone JobState = 4 + JobStateCancelled JobState = 5 + // JobStateSynced is used to mark the information about the completion of this job + // has been synchronized to all servers. + JobStateSynced JobState = 6 + // JobStateCancelling is used to mark the DDL job is cancelled by the client, but the DDL work hasn't handle it. + JobStateCancelling JobState = 7 + // JobStateQueueing means the job has not yet been started. + JobStateQueueing JobState = 8 + + JobStatePaused JobState = 9 + JobStatePausing JobState = 10 +) + +// String implements fmt.Stringer interface. +func (s JobState) String() string { + switch s { + case JobStateRunning: + return "running" + case JobStateRollingback: + return "rollingback" + case JobStateRollbackDone: + return "rollback done" + case JobStateDone: + return "done" + case JobStateCancelled: + return "cancelled" + case JobStateCancelling: + return "cancelling" + case JobStateSynced: + return "synced" + case JobStateQueueing: + return "queueing" + case JobStatePaused: + return "paused" + case JobStatePausing: + return "pausing" + default: + return "none" + } +} + +// StrToJobState converts string to JobState. +func StrToJobState(s string) JobState { + switch s { + case "running": + return JobStateRunning + case "rollingback": + return JobStateRollingback + case "rollback done": + return JobStateRollbackDone + case "done": + return JobStateDone + case "cancelled": + return JobStateCancelled + case "cancelling": + return JobStateCancelling + case "synced": + return JobStateSynced + case "queueing": + return JobStateQueueing + case "paused": + return JobStatePaused + case "pausing": + return JobStatePausing + default: + return JobStateNone + } +} + +// AdminCommandOperator indicates where the Cancel/Pause/Resume command on DDL +// jobs comes from. +type AdminCommandOperator int + +const ( + // AdminCommandByNotKnown indicates that unknow calling of the + // Cancel/Pause/Resume on DDL job. + AdminCommandByNotKnown AdminCommandOperator = iota + // AdminCommandByEndUser indicates that the Cancel/Pause/Resume command on + // DDL job is issued by the end user. + AdminCommandByEndUser + // AdminCommandBySystem indicates that the Cancel/Pause/Resume command on + // DDL job is issued by TiDB itself, such as Upgrade(bootstrap). + AdminCommandBySystem +) + +func (a *AdminCommandOperator) String() string { + switch *a { + case AdminCommandByEndUser: + return "EndUser" + case AdminCommandBySystem: + return "System" + default: + return "None" + } +} + +// SchemaDiff contains the schema modification at a particular schema version. +// It is used to reduce schema reload cost. +type SchemaDiff struct { + Version int64 `json:"version"` + Type ActionType `json:"type"` + SchemaID int64 `json:"schema_id"` + TableID int64 `json:"table_id"` + + // OldTableID is the table ID before truncate, only used by truncate table DDL. + OldTableID int64 `json:"old_table_id"` + // OldSchemaID is the schema ID before rename table, only used by rename table DDL. + OldSchemaID int64 `json:"old_schema_id"` + // RegenerateSchemaMap means whether to rebuild the schema map when applying to the schema diff. + RegenerateSchemaMap bool `json:"regenerate_schema_map"` + + AffectedOpts []*AffectedOption `json:"affected_options"` +} + +// AffectedOption is used when a ddl affects multi tables. +type AffectedOption struct { + SchemaID int64 `json:"schema_id"` + TableID int64 `json:"table_id"` + OldTableID int64 `json:"old_table_id"` + OldSchemaID int64 `json:"old_schema_id"` +} diff --git a/pkg/parser/model/ddl_test.go b/pkg/parser/model/ddl_test.go new file mode 100644 index 0000000000000..66550b407c578 --- /dev/null +++ b/pkg/parser/model/ddl_test.go @@ -0,0 +1,106 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package model_test + +import ( + "testing" + "unsafe" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/require" +) + +func TestJobClone(t *testing.T) { + job := &model.Job{ + ID: 100, + Type: model.ActionCreateTable, + SchemaID: 101, + TableID: 102, + SchemaName: "test", + TableName: "t", + State: model.JobStateDone, + MultiSchemaInfo: nil, + } + clone := job.Clone() + require.Equal(t, job.ID, clone.ID) + require.Equal(t, job.Type, clone.Type) + require.Equal(t, job.SchemaID, clone.SchemaID) + require.Equal(t, job.TableID, clone.TableID) + require.Equal(t, job.SchemaName, clone.SchemaName) + require.Equal(t, job.TableName, clone.TableName) + require.Equal(t, job.State, clone.State) + require.Equal(t, job.MultiSchemaInfo, clone.MultiSchemaInfo) +} + +func TestJobSize(t *testing.T) { + msg := `Please make sure that the following methods work as expected: +- SubJob.FromProxyJob() +- SubJob.ToProxyJob() +` + job := model.Job{} + require.Equal(t, 336, int(unsafe.Sizeof(job)), msg) +} + +func TestBackfillMetaCodec(t *testing.T) { + jm := &model.JobMeta{ + SchemaID: 1, + TableID: 2, + Query: "alter table t add index idx(a)", + Priority: 1, + } + bm := &model.BackfillMeta{ + EndInclude: true, + Error: terror.ErrResultUndetermined, + JobMeta: jm, + } + bmBytes, err := bm.Encode() + require.NoError(t, err) + bmRet := &model.BackfillMeta{} + bmRet.Decode(bmBytes) + require.Equal(t, bm, bmRet) +} + +func TestMayNeedReorg(t *testing.T) { + //TODO(bb7133): add more test cases for different ActionType. + reorgJobTypes := []model.ActionType{ + model.ActionReorganizePartition, + model.ActionRemovePartitioning, + model.ActionAlterTablePartitioning, + model.ActionAddIndex, + model.ActionAddPrimaryKey, + } + generalJobTypes := []model.ActionType{ + model.ActionCreateTable, + model.ActionDropTable, + } + job := &model.Job{ + ID: 100, + Type: model.ActionCreateTable, + SchemaID: 101, + TableID: 102, + SchemaName: "test", + TableName: "t", + State: model.JobStateDone, + MultiSchemaInfo: nil, + } + for _, jobType := range reorgJobTypes { + job.Type = jobType + require.True(t, job.MayNeedReorg()) + } + for _, jobType := range generalJobTypes { + job.Type = jobType + require.False(t, job.MayNeedReorg()) + } +} diff --git a/parser/model/flags.go b/pkg/parser/model/flags.go similarity index 100% rename from parser/model/flags.go rename to pkg/parser/model/flags.go diff --git a/parser/model/model.go b/pkg/parser/model/model.go similarity index 99% rename from parser/model/model.go rename to pkg/parser/model/model.go index 12e619e56fcf7..48ff5665a51ef 100644 --- a/parser/model/model.go +++ b/pkg/parser/model/model.go @@ -24,11 +24,11 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/duration" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/duration" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" ) // SchemaState is the state for schema elements. diff --git a/parser/model/model_test.go b/pkg/parser/model/model_test.go similarity index 99% rename from parser/model/model_test.go rename to pkg/parser/model/model_test.go index dccdaab1fd95f..e5cc71034fb25 100644 --- a/parser/model/model_test.go +++ b/pkg/parser/model/model_test.go @@ -19,9 +19,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/parser/model/reorg.go b/pkg/parser/model/reorg.go new file mode 100644 index 0000000000000..07f702b9b916e --- /dev/null +++ b/pkg/parser/model/reorg.go @@ -0,0 +1,139 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package model + +import ( + "encoding/json" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" +) + +// DDLReorgMeta is meta info of DDL reorganization. +type DDLReorgMeta struct { + SQLMode mysql.SQLMode `json:"sql_mode"` + Warnings map[errors.ErrorID]*terror.Error `json:"warnings"` + WarningsCount map[errors.ErrorID]int64 `json:"warnings_count"` + Location *TimeZoneLocation `json:"location"` + ReorgTp ReorgType `json:"reorg_tp"` + IsDistReorg bool `json:"is_dist_reorg"` + ResourceGroupName string `json:"resource_group_name"` +} + +// ReorgType indicates which process is used for the data reorganization. +type ReorgType int8 + +const ( + // ReorgTypeNone means the backfill task is not started yet. + ReorgTypeNone ReorgType = iota + // ReorgTypeTxn means the index records are backfill with transactions. + // All the index KVs are written through the transaction interface. + // This is the original backfill implementation. + ReorgTypeTxn + // ReorgTypeLitMerge means the index records are backfill with lightning. + // The index KVs are encoded to SST files and imported to the storage directly. + // The incremental index KVs written by DML are redirected to a temporary index. + // After the backfill is finished, the temporary index records are merged back to the original index. + ReorgTypeLitMerge + // ReorgTypeTxnMerge means backfill with transactions and merge incremental changes. + // The backfill index KVs are written through the transaction interface. + // The incremental index KVs written by DML are redirected to a temporary index. + // After the backfill is finished, the temporary index records are merged back to the original index. + ReorgTypeTxnMerge +) + +// NeedMergeProcess means the incremental changes need to be merged. +func (tp ReorgType) NeedMergeProcess() bool { + return tp == ReorgTypeLitMerge || tp == ReorgTypeTxnMerge +} + +// String implements fmt.Stringer interface. +func (tp ReorgType) String() string { + switch tp { + case ReorgTypeTxn: + return "txn" + case ReorgTypeLitMerge: + return "ingest" + case ReorgTypeTxnMerge: + return "txn-merge" + } + return "" +} + +// BackfillState is the state used by the backfill-merge process. +type BackfillState byte + +const ( + // BackfillStateInapplicable means the backfill-merge process is not used. + BackfillStateInapplicable BackfillState = iota + // BackfillStateRunning is the state that the backfill process is running. + // In this state, the index's write and delete operations are redirected to a temporary index. + BackfillStateRunning + // BackfillStateReadyToMerge is the state that the temporary index's records are ready to be merged back + // to the origin index. + // In this state, the index's write and delete operations are copied to a temporary index. + // This state is used to make sure that all the TiDB instances are aware of the copy + // during the merge(BackfillStateMerging). + BackfillStateReadyToMerge + // BackfillStateMerging is the state that the temp index is merging back to the origin index. + // In this state, the index's write and delete operations are copied to a temporary index. + BackfillStateMerging +) + +// String implements fmt.Stringer interface. +func (s BackfillState) String() string { + switch s { + case BackfillStateRunning: + return "backfill state running" + case BackfillStateReadyToMerge: + return "backfill state ready to merge" + case BackfillStateMerging: + return "backfill state merging" + case BackfillStateInapplicable: + return "backfill state inapplicable" + default: + return "backfill state unknown" + } +} + +// BackfillMeta is meta info of the backfill job. +type BackfillMeta struct { + IsUnique bool `json:"is_unique"` + EndInclude bool `json:"end_include"` + Error *terror.Error `json:"err"` + + SQLMode mysql.SQLMode `json:"sql_mode"` + Warnings map[errors.ErrorID]*terror.Error `json:"warnings"` + WarningsCount map[errors.ErrorID]int64 `json:"warnings_count"` + Location *TimeZoneLocation `json:"location"` + ReorgTp ReorgType `json:"reorg_tp"` + RowCount int64 `json:"row_count"` + StartKey []byte `json:"start_key"` + EndKey []byte `json:"end_key"` + CurrKey []byte `json:"curr_key"` + *JobMeta `json:"job_meta"` +} + +// Encode encodes BackfillMeta with json format. +func (bm *BackfillMeta) Encode() ([]byte, error) { + b, err := json.Marshal(bm) + return b, errors.Trace(err) +} + +// Decode decodes BackfillMeta from the json buffer. +func (bm *BackfillMeta) Decode(b []byte) error { + err := json.Unmarshal(b, bm) + return errors.Trace(err) +} diff --git a/pkg/parser/mysql/BUILD.bazel b/pkg/parser/mysql/BUILD.bazel new file mode 100644 index 0000000000000..5f721edc44c3a --- /dev/null +++ b/pkg/parser/mysql/BUILD.bazel @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mysql", + srcs = [ + "charset.go", + "const.go", + "errcode.go", + "errname.go", + "error.go", + "locale_format.go", + "privs.go", + "state.go", + "type.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/mysql", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/format", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "mysql_test", + timeout = "short", + srcs = [ + "const_test.go", + "error_test.go", + "privs_test.go", + "type_test.go", + ], + embed = [":mysql"], + flaky = True, + shard_count = 8, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/parser/mysql/charset.go b/pkg/parser/mysql/charset.go similarity index 100% rename from parser/mysql/charset.go rename to pkg/parser/mysql/charset.go diff --git a/parser/mysql/const.go b/pkg/parser/mysql/const.go similarity index 99% rename from parser/mysql/const.go rename to pkg/parser/mysql/const.go index fdd6ec84fc171..012b71a0c80f9 100644 --- a/parser/mysql/const.go +++ b/pkg/parser/mysql/const.go @@ -18,7 +18,7 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser/format" ) func newInvalidModeErr(s string) error { diff --git a/parser/mysql/const_test.go b/pkg/parser/mysql/const_test.go similarity index 100% rename from parser/mysql/const_test.go rename to pkg/parser/mysql/const_test.go diff --git a/parser/mysql/errcode.go b/pkg/parser/mysql/errcode.go similarity index 99% rename from parser/mysql/errcode.go rename to pkg/parser/mysql/errcode.go index faad932cd57df..3a217f93a93ec 100644 --- a/parser/mysql/errcode.go +++ b/pkg/parser/mysql/errcode.go @@ -970,5 +970,5 @@ const ( ErrWarnOptimizerHintWrongPos = 8066 // Stop adding error code here! - // They are moved to github.com/pingcap/tidb/errno + // They are moved to github.com/pingcap/tidb/pkg/errno ) diff --git a/parser/mysql/errname.go b/pkg/parser/mysql/errname.go similarity index 100% rename from parser/mysql/errname.go rename to pkg/parser/mysql/errname.go diff --git a/parser/mysql/error.go b/pkg/parser/mysql/error.go similarity index 100% rename from parser/mysql/error.go rename to pkg/parser/mysql/error.go diff --git a/parser/mysql/error_test.go b/pkg/parser/mysql/error_test.go similarity index 100% rename from parser/mysql/error_test.go rename to pkg/parser/mysql/error_test.go diff --git a/parser/mysql/locale_format.go b/pkg/parser/mysql/locale_format.go similarity index 100% rename from parser/mysql/locale_format.go rename to pkg/parser/mysql/locale_format.go diff --git a/parser/mysql/privs.go b/pkg/parser/mysql/privs.go similarity index 100% rename from parser/mysql/privs.go rename to pkg/parser/mysql/privs.go diff --git a/parser/mysql/privs_test.go b/pkg/parser/mysql/privs_test.go similarity index 100% rename from parser/mysql/privs_test.go rename to pkg/parser/mysql/privs_test.go diff --git a/parser/mysql/state.go b/pkg/parser/mysql/state.go similarity index 100% rename from parser/mysql/state.go rename to pkg/parser/mysql/state.go diff --git a/parser/mysql/type.go b/pkg/parser/mysql/type.go similarity index 100% rename from parser/mysql/type.go rename to pkg/parser/mysql/type.go diff --git a/parser/mysql/type_test.go b/pkg/parser/mysql/type_test.go similarity index 100% rename from parser/mysql/type_test.go rename to pkg/parser/mysql/type_test.go diff --git a/parser/mysql/util.go b/pkg/parser/mysql/util.go similarity index 100% rename from parser/mysql/util.go rename to pkg/parser/mysql/util.go diff --git a/pkg/parser/opcode/BUILD.bazel b/pkg/parser/opcode/BUILD.bazel new file mode 100644 index 0000000000000..81a468ae1c57a --- /dev/null +++ b/pkg/parser/opcode/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "opcode", + srcs = ["opcode.go"], + importpath = "github.com/pingcap/tidb/pkg/parser/opcode", + visibility = ["//visibility:public"], + deps = ["//pkg/parser/format"], +) + +go_test( + name = "opcode_test", + timeout = "short", + srcs = ["opcode_test.go"], + embed = [":opcode"], + flaky = True, +) diff --git a/parser/opcode/opcode.go b/pkg/parser/opcode/opcode.go similarity index 98% rename from parser/opcode/opcode.go rename to pkg/parser/opcode/opcode.go index b855f4bd5ac4b..17dd568e710e5 100644 --- a/parser/opcode/opcode.go +++ b/pkg/parser/opcode/opcode.go @@ -16,7 +16,7 @@ package opcode import ( "io" - "github.com/pingcap/tidb/parser/format" + "github.com/pingcap/tidb/pkg/parser/format" ) // Op is opcode type. diff --git a/parser/opcode/opcode_test.go b/pkg/parser/opcode/opcode_test.go similarity index 100% rename from parser/opcode/opcode_test.go rename to pkg/parser/opcode/opcode_test.go diff --git a/pkg/parser/parser.go b/pkg/parser/parser.go new file mode 100644 index 0000000000000..d7d0314ca8517 --- /dev/null +++ b/pkg/parser/parser.go @@ -0,0 +1,24733 @@ +// Code generated by goyacc DO NOT EDIT. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Initial yacc source generated by ebnf2y[1] +// at 2013-10-04 23:10:47.861401015 +0200 CEST +// +// $ ebnf2y -o ql.y -oe ql.ebnf -start StatementList -pkg ql -p _ +// +// [1]: http://github.com/cznic/ebnf2y + +package parser + +import __yyfmt__ "fmt" + +import ( + "strings" + "time" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/duration" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/types" +) + +type yySymType struct { + yys int + offset int // offset + item interface{} + ident string + expr ast.ExprNode + statement ast.StmtNode +} + +type yyXError struct { + state, xsym int +} + +const ( + yyDefault = 58193 + yyEOFCode = 57344 + account = 57595 + action = 57596 + add = 57362 + addDate = 57961 + admin = 58077 + advise = 57597 + after = 57598 + against = 57599 + ago = 57600 + algorithm = 57601 + all = 57363 + alter = 57364 + always = 57602 + analyze = 57365 + and = 57366 + andand = 57357 + andnot = 58153 + any = 57603 + approxCountDistinct = 57962 + approxPercentile = 57963 + array = 57367 + as = 57368 + asc = 57369 + ascii = 57604 + asof = 57347 + assignmentEq = 58154 + attribute = 57605 + attributes = 57606 + autoIdCache = 57611 + autoIncrement = 57612 + autoRandom = 57613 + autoRandomBase = 57614 + avg = 57615 + avgRowLength = 57616 + backend = 57617 + background = 58075 + backup = 57618 + backups = 57619 + batch = 58078 + begin = 57620 + bernoulli = 57621 + between = 57370 + bigIntType = 57371 + binaryType = 57372 + binding = 57622 + bindingCache = 57623 + bindings = 57624 + binlog = 57625 + bitAnd = 57964 + bitLit = 58152 + bitOr = 57965 + bitType = 57626 + bitXor = 57966 + blobType = 57373 + block = 57627 + boolType = 57629 + booleanType = 57628 + both = 57374 + bound = 57967 + br = 57968 + briefType = 57969 + btree = 57630 + buckets = 58079 + builtinApproxCountDistinct = 58126 + builtinApproxPercentile = 58127 + builtinBitAnd = 58121 + builtinBitOr = 58122 + builtinBitXor = 58123 + builtinCast = 58124 + builtinCount = 58125 + builtinCurDate = 58128 + builtinCurTime = 58129 + builtinDateAdd = 58130 + builtinDateSub = 58131 + builtinExtract = 58132 + builtinGroupConcat = 58133 + builtinMax = 58134 + builtinMin = 58135 + builtinNow = 58136 + builtinPosition = 58137 + builtinStddevPop = 58141 + builtinStddevSamp = 58142 + builtinSubstring = 58138 + builtinSum = 58139 + builtinSysDate = 58140 + builtinTranslate = 58143 + builtinTrim = 58144 + builtinUser = 58145 + builtinVarPop = 58146 + builtinVarSamp = 58147 + builtins = 58080 + burstable = 57970 + by = 57375 + byteType = 57631 + cache = 57632 + calibrate = 57633 + call = 57376 + cancel = 58081 + capture = 57634 + cardinality = 58082 + cascade = 57377 + cascaded = 57635 + caseKwd = 57378 + cast = 57971 + causal = 57636 + chain = 57637 + change = 57379 + charType = 57381 + character = 57380 + charsetKwd = 57638 + check = 57382 + checkpoint = 57639 + checksum = 57640 + cipher = 57641 + cleanup = 57642 + client = 57643 + clientErrorsSummary = 57644 + close = 57670 + cluster = 57671 + clustered = 57672 + cmSketch = 58083 + coalesce = 57645 + collate = 57383 + collation = 57646 + column = 57384 + columnFormat = 57647 + columnStatsUsage = 58084 + columns = 57648 + comment = 57650 + commit = 57651 + committed = 57652 + compact = 57653 + compressed = 57654 + compression = 57655 + concurrency = 57656 + config = 57649 + connection = 57657 + consistency = 57658 + consistent = 57659 + constraint = 57385 + constraints = 57973 + context = 57660 + continueKwd = 57386 + convert = 57387 + cooldown = 58071 + copyKwd = 57972 + correlation = 58085 + cpu = 57661 + create = 57388 + createTableSelect = 58177 + cross = 57389 + csvBackslashEscape = 57662 + csvDelimiter = 57663 + csvHeader = 57664 + csvNotNull = 57665 + csvNull = 57666 + csvSeparator = 57667 + csvTrimLastSeparators = 57668 + cumeDist = 57390 + curDate = 57975 + curTime = 57974 + current = 57669 + currentDate = 57391 + currentRole = 57395 + currentTime = 57392 + currentTs = 57393 + currentUser = 57394 + cursor = 57396 + cycle = 57673 + data = 57674 + database = 57397 + databases = 57398 + dateAdd = 57976 + dateSub = 57977 + dateType = 57676 + datetimeType = 57675 + day = 57677 + dayHour = 57399 + dayMicrosecond = 57400 + dayMinute = 57401 + daySecond = 57402 + ddl = 58086 + deallocate = 57678 + decLit = 58149 + decimalType = 57403 + declare = 57679 + defaultKwd = 57404 + defined = 57978 + definer = 57680 + delayKeyWrite = 57681 + delayed = 57405 + deleteKwd = 57406 + denseRank = 57407 + dependency = 58087 + depth = 58088 + desc = 57408 + describe = 57409 + digest = 57682 + directory = 57683 + disable = 57684 + disabled = 57685 + discard = 57686 + disk = 57687 + distinct = 57410 + distinctRow = 57411 + div = 57412 + do = 57688 + dotType = 57979 + doubleAtIdentifier = 57354 + doubleType = 57413 + drainer = 58089 + drop = 57414 + dry = 58090 + dryRun = 58070 + dual = 57415 + dump = 57980 + duplicate = 57689 + dynamic = 57690 + elseIfKwd = 57416 + elseKwd = 57417 + empty = 58167 + enable = 57691 + enabled = 57692 + enclosed = 57418 + encryption = 57693 + end = 57694 + endTime = 57982 + enforced = 57695 + engine = 57696 + engines = 57697 + enum = 57698 + eq = 58155 + yyErrCode = 57345 + errorKwd = 57699 + escape = 57700 + escaped = 57419 + event = 57701 + events = 57702 + evolve = 57703 + exact = 57983 + except = 57423 + exchange = 57704 + exclusive = 57705 + execElapsed = 58069 + execute = 57706 + exists = 57420 + exit = 57421 + expansion = 57707 + expire = 57708 + explain = 57422 + exprPushdownBlacklist = 57984 + extended = 57709 + extract = 57985 + failedLoginAttempts = 57959 + falseKwd = 57424 + faultsSym = 57710 + fetch = 57425 + fields = 57711 + file = 57712 + first = 57713 + firstValue = 57426 + fixed = 57714 + flashback = 57986 + float4Type = 57428 + float8Type = 57429 + floatLit = 58148 + floatType = 57427 + flush = 57715 + follower = 57987 + followerConstraints = 57988 + followers = 57989 + following = 57717 + forKwd = 57430 + force = 57431 + foreign = 57432 + format = 57718 + found = 57716 + from = 57433 + full = 57719 + fullBackupStorage = 57990 + fulltext = 57434 + function = 57720 + gcTTL = 57992 + ge = 58156 + general = 57721 + generated = 57435 + getFormat = 57991 + global = 57722 + grant = 57436 + grants = 57723 + group = 57437 + groupConcat = 57993 + groups = 57438 + handler = 57724 + hash = 57725 + having = 57439 + help = 57726 + hexLit = 58151 + high = 58064 + highPriority = 57440 + higherThanComma = 58192 + higherThanParenthese = 58186 + hintComment = 57356 + histogram = 57727 + histogramsInFlight = 58110 + history = 57728 + hosts = 57729 + hour = 57730 + hourMicrosecond = 57441 + hourMinute = 57442 + hourSecond = 57443 + hypo = 57865 + identSQLErrors = 57732 + identified = 57731 + identifier = 57346 + ifKwd = 57444 + ignore = 57445 + ilike = 57476 + importKwd = 57733 + imports = 57734 + in = 57446 + increment = 57735 + incremental = 57736 + index = 57447 + indexes = 57737 + infile = 57448 + inner = 57449 + inout = 57450 + inplace = 57995 + insert = 57457 + insertMethod = 57738 + insertValues = 58175 + instance = 57739 + instant = 57996 + int1Type = 57459 + int2Type = 57460 + int3Type = 57461 + int4Type = 57462 + int8Type = 57463 + intLit = 58150 + intType = 57458 + integerType = 57451 + internal = 57997 + intersect = 57452 + interval = 57453 + into = 57454 + invalid = 57355 + invisible = 57740 + invoker = 57741 + io = 57742 + ioReadBandwidth = 58067 + ioWriteBandwidth = 58068 + ipc = 57743 + is = 57456 + isolation = 57744 + issuer = 57745 + iterate = 57464 + job = 58092 + jobs = 58091 + join = 57465 + jsonArrayagg = 57998 + jsonObjectAgg = 57999 + jsonType = 57746 + jss = 58158 + juss = 58159 + key = 57466 + keyBlockSize = 57747 + keys = 57467 + kill = 57468 + labels = 57748 + lag = 57469 + language = 57749 + last = 57750 + lastBackup = 57751 + lastValue = 57470 + lastval = 57752 + le = 58157 + lead = 57471 + leader = 58000 + leaderConstraints = 58001 + leading = 57472 + learner = 58002 + learnerConstraints = 58003 + learners = 58004 + leave = 57473 + left = 57474 + less = 57753 + level = 57754 + like = 57475 + limit = 57477 + linear = 57479 + lines = 57478 + list = 57755 + load = 57480 + local = 57756 + localTime = 57481 + localTs = 57482 + location = 57758 + lock = 57483 + locked = 57757 + logs = 57759 + long = 57579 + longblobType = 57484 + longtextType = 57485 + low = 58066 + lowPriority = 57486 + lowerThanCharsetKwd = 58178 + lowerThanComma = 58191 + lowerThanCreateTableSelect = 58176 + lowerThanEq = 58188 + lowerThanFunction = 58183 + lowerThanInsertValues = 58174 + lowerThanKey = 58179 + lowerThanLocal = 58180 + lowerThanNot = 58190 + lowerThanOn = 58187 + lowerThanParenthese = 58185 + lowerThanRemove = 58181 + lowerThanSelectOpt = 58168 + lowerThanSelectStmt = 58173 + lowerThanSetKeyword = 58172 + lowerThanStringLitToken = 58171 + lowerThanValueKeyword = 58169 + lowerThanWith = 58170 + lowerThenOrder = 58182 + lsh = 58160 + master = 57760 + match = 57487 + max = 58006 + maxConnectionsPerHour = 57763 + maxQueriesPerHour = 57764 + maxRows = 57765 + maxUpdatesPerHour = 57766 + maxUserConnections = 57767 + maxValue = 57488 + max_idxnum = 57761 + max_minutes = 57762 + mb = 57768 + medium = 58065 + mediumIntType = 57490 + mediumblobType = 57489 + mediumtextType = 57491 + member = 57769 + memberof = 57349 + memory = 57770 + merge = 57771 + metadata = 58007 + microsecond = 57772 + middleIntType = 57492 + min = 58005 + minRows = 57773 + minValue = 57775 + minute = 57774 + minuteMicrosecond = 57493 + minuteSecond = 57494 + mod = 57495 + mode = 57776 + modify = 57777 + month = 57778 + names = 57779 + national = 57780 + natural = 57594 + ncharType = 57781 + neg = 58189 + neq = 58161 + neqSynonym = 58162 + never = 57782 + next = 57783 + next_row_id = 57994 + nextval = 57784 + no = 57785 + noWriteToBinLog = 57497 + nocache = 57786 + nocycle = 57787 + nodeID = 58093 + nodeState = 58094 + nodegroup = 57788 + nomaxvalue = 57789 + nominvalue = 57790 + nonclustered = 57791 + none = 57792 + not = 57496 + not2 = 58166 + now = 58008 + nowait = 57793 + nthValue = 57498 + ntile = 57499 + null = 57500 + nulleq = 58163 + nulls = 57795 + numericType = 57501 + nvarcharType = 57794 + odbcDateType = 57359 + odbcTimeType = 57360 + odbcTimestampType = 57361 + of = 57502 + off = 57796 + offset = 57797 + oltpReadOnly = 57798 + oltpReadWrite = 57799 + oltpWriteOnly = 57800 + on = 57503 + onDuplicate = 57802 + online = 57803 + only = 57804 + open = 57805 + optRuleBlacklist = 58009 + optimistic = 58095 + optimize = 57504 + option = 57505 + optional = 57806 + optionally = 57506 + optionallyEnclosedBy = 57350 + or = 57507 + order = 57508 + out = 57509 + outer = 57510 + outfile = 57455 + over = 57511 + packKeys = 57807 + pageSym = 57808 + paramMarker = 58164 + parser = 57809 + partial = 57810 + partition = 57512 + partitioning = 57811 + partitions = 57812 + password = 57813 + passwordLockTime = 57960 + pause = 57814 + per_db = 57816 + per_table = 57817 + percent = 57815 + percentRank = 57513 + pessimistic = 58096 + pipes = 57358 + pipesAsOr = 57818 + placement = 58010 + plan = 58011 + planCache = 58012 + plugins = 57819 + point = 57820 + policy = 57821 + position = 58013 + preSplitRegions = 57822 + preceding = 57823 + precisionType = 57514 + predicate = 58014 + prepare = 57824 + preserve = 57825 + primary = 57515 + primaryRegion = 58015 + priority = 58063 + privileges = 57826 + procedure = 57516 + process = 57827 + processlist = 57828 + profile = 57829 + profiles = 57830 + proxy = 57831 + pump = 58097 + purge = 57832 + quarter = 57833 + queries = 57834 + query = 57835 + queryLimit = 58074 + quick = 57836 + rangeKwd = 57517 + rank = 57518 + rateLimit = 57837 + read = 57519 + realType = 57520 + rebuild = 57838 + recent = 58016 + recover = 57839 + recursive = 57521 + redundant = 57840 + references = 57522 + regexpKwd = 57523 + region = 58120 + regions = 58119 + release = 57524 + reload = 57841 + remove = 57842 + rename = 57525 + reorganize = 57843 + repair = 57844 + repeat = 57526 + repeatable = 57845 + replace = 57527 + replayer = 58017 + replica = 57846 + replicas = 57847 + replication = 57848 + require = 57528 + required = 57849 + reset = 58118 + resource = 57850 + respect = 57851 + restart = 57852 + restore = 57853 + restoredTS = 58018 + restores = 57854 + restrict = 57529 + resume = 57855 + reuse = 57856 + reverse = 57857 + revoke = 57530 + right = 57531 + rlike = 57532 + role = 57858 + rollback = 57859 + rollup = 57860 + routine = 57861 + row = 57533 + rowCount = 57862 + rowFormat = 57863 + rowNumber = 57535 + rows = 57534 + rsh = 58165 + rtree = 57864 + ruRate = 58062 + run = 58098 + running = 58019 + s3 = 58020 + sampleRate = 58100 + samples = 58099 + san = 57866 + savepoint = 57867 + schedule = 58021 + second = 57868 + secondMicrosecond = 57536 + secondaryEngine = 57869 + secondaryLoad = 57870 + secondaryUnload = 57871 + security = 57872 + selectKwd = 57537 + sendCredentialsToTiKV = 57873 + separator = 57874 + sequence = 57875 + serial = 57876 + serializable = 57877 + session = 57878 + sessionStates = 58101 + set = 57538 + setval = 57879 + shardRowIDBits = 57880 + share = 57881 + shared = 57882 + show = 57539 + shutdown = 57883 + signed = 57884 + similar = 58073 + simple = 57885 + singleAtIdentifier = 57353 + skip = 57886 + skipSchemaFiles = 57887 + slave = 57888 + slow = 57889 + smallIntType = 57540 + snapshot = 57890 + some = 57891 + source = 57892 + spatial = 57541 + split = 58116 + sql = 57542 + sqlBigResult = 57543 + sqlBufferResult = 57893 + sqlCache = 57894 + sqlCalcFoundRows = 57544 + sqlNoCache = 57895 + sqlSmallResult = 57545 + sqlTsiDay = 57896 + sqlTsiHour = 57897 + sqlTsiMinute = 57898 + sqlTsiMonth = 57899 + sqlTsiQuarter = 57900 + sqlTsiSecond = 57901 + sqlTsiWeek = 57902 + sqlTsiYear = 57903 + sqlexception = 57546 + sqlstate = 57547 + sqlwarning = 57548 + ssl = 57549 + staleness = 58022 + start = 57904 + startTS = 58024 + startTime = 58023 + starting = 57550 + statistics = 58102 + stats = 58103 + statsAutoRecalc = 57905 + statsBuckets = 58106 + statsColChoice = 57609 + statsColList = 57610 + statsExtended = 57551 + statsHealthy = 58107 + statsHistograms = 58105 + statsLocked = 58109 + statsMeta = 58104 + statsOptions = 57607 + statsPersistent = 57906 + statsSamplePages = 57907 + statsSampleRate = 57608 + statsTopN = 58108 + status = 57908 + std = 58025 + stddev = 58026 + stddevPop = 58027 + stddevSamp = 58028 + stop = 58029 + storage = 57909 + stored = 57556 + straightJoin = 57552 + strict = 58030 + strictFormat = 57910 + stringLit = 57352 + strong = 58031 + subDate = 58032 + subject = 57911 + subpartition = 57912 + subpartitions = 57913 + substring = 58034 + sum = 58033 + super = 57914 + survivalPreferences = 58035 + swaps = 57915 + switchesSym = 57916 + system = 57917 + systemTime = 57918 + tableChecksum = 57919 + tableKwd = 57554 + tableRefPriority = 58184 + tableSample = 57555 + tables = 57920 + tablespace = 57921 + target = 58036 + taskTypes = 58037 + telemetry = 58111 + telemetryID = 58112 + temporary = 57922 + temptable = 57923 + terminated = 57557 + textType = 57924 + than = 57925 + then = 57558 + tiFlash = 58114 + tidb = 58113 + tidbCurrentTSO = 57553 + tidbJson = 58038 + tikvImporter = 57926 + timeDuration = 57981 + timeType = 57928 + timestampAdd = 58039 + timestampDiff = 58040 + timestampType = 57927 + tinyIntType = 57560 + tinyblobType = 57559 + tinytextType = 57561 + tls = 58041 + to = 57562 + toTimestamp = 57348 + tokenIssuer = 57929 + tokudbDefault = 58042 + tokudbFast = 58043 + tokudbLzma = 58044 + tokudbQuickLZ = 58045 + tokudbSmall = 58047 + tokudbSnappy = 58046 + tokudbUncompressed = 58048 + tokudbZlib = 58049 + tokudbZstd = 58050 + top = 58051 + topn = 58115 + tp = 57930 + tpcc = 57931 + tpch10 = 57801 + trace = 57932 + traditional = 57933 + trailing = 57563 + transaction = 57934 + trigger = 57564 + triggers = 57935 + trim = 58052 + trueCardCost = 58058 + trueKwd = 57565 + truncate = 57936 + ttl = 57937 + ttlEnable = 57938 + ttlJobInterval = 57939 + unbounded = 57940 + uncommitted = 57941 + undefined = 57942 + underscoreCS = 57351 + unicodeSym = 57943 + union = 57567 + unique = 57566 + unknown = 57944 + unlimited = 58076 + unlock = 57568 + unsigned = 57569 + until = 57570 + untilTS = 58053 + update = 57571 + usage = 57572 + use = 57573 + user = 57945 + using = 57574 + utcDate = 57575 + utcTime = 57577 + utcTimestamp = 57576 + validation = 57946 + value = 57947 + values = 57578 + varPop = 58055 + varSamp = 58056 + varbinaryType = 57582 + varcharType = 57580 + varcharacter = 57581 + variables = 57948 + variance = 58054 + varying = 57583 + verboseType = 58057 + view = 57949 + virtual = 57584 + visible = 57950 + voter = 58059 + voterConstraints = 58060 + voters = 58061 + wait = 57958 + warnings = 57951 + watch = 58072 + week = 57952 + weightString = 57953 + when = 57585 + where = 57586 + while = 57587 + width = 58117 + window = 57589 + with = 57590 + without = 57954 + workload = 57955 + write = 57588 + x509 = 57956 + xor = 57591 + yearMonth = 57592 + yearType = 57957 + zerofill = 57593 + + yyMaxDepth = 200 + yyTabOfs = -2854 +) + +var ( + yyXLAT = map[int]int{ + 59: 0, // ';' (2503x) + 57344: 1, // $end (2490x) + 57842: 2, // remove (1990x) + 58116: 3, // split (1990x) + 57771: 4, // merge (1989x) + 57843: 5, // reorganize (1988x) + 57650: 6, // comment (1981x) + 57909: 7, // storage (1893x) + 57612: 8, // autoIncrement (1882x) + 44: 9, // ',' (1856x) + 57713: 10, // first (1781x) + 57598: 11, // after (1775x) + 57876: 12, // serial (1771x) + 57613: 13, // autoRandom (1770x) + 57647: 14, // columnFormat (1770x) + 57813: 15, // password (1742x) + 57638: 16, // charsetKwd (1734x) + 57640: 17, // checksum (1724x) + 58010: 18, // placement (1721x) + 57747: 19, // keyBlockSize (1705x) + 57921: 20, // tablespace (1701x) + 57693: 21, // encryption (1699x) + 57674: 22, // data (1697x) + 57696: 23, // engine (1696x) + 57738: 24, // insertMethod (1692x) + 57765: 25, // maxRows (1692x) + 57773: 26, // minRows (1692x) + 57788: 27, // nodegroup (1692x) + 57657: 28, // connection (1684x) + 57614: 29, // autoRandomBase (1681x) + 58106: 30, // statsBuckets (1679x) + 58108: 31, // statsTopN (1679x) + 57937: 32, // ttl (1679x) + 57611: 33, // autoIdCache (1678x) + 57616: 34, // avgRowLength (1678x) + 57655: 35, // compression (1678x) + 57681: 36, // delayKeyWrite (1678x) + 57807: 37, // packKeys (1678x) + 57822: 38, // preSplitRegions (1678x) + 57863: 39, // rowFormat (1678x) + 57869: 40, // secondaryEngine (1678x) + 57880: 41, // shardRowIDBits (1678x) + 57905: 42, // statsAutoRecalc (1678x) + 57609: 43, // statsColChoice (1678x) + 57610: 44, // statsColList (1678x) + 57906: 45, // statsPersistent (1678x) + 57907: 46, // statsSamplePages (1678x) + 57608: 47, // statsSampleRate (1678x) + 57919: 48, // tableChecksum (1678x) + 57938: 49, // ttlEnable (1678x) + 57939: 50, // ttlJobInterval (1678x) + 57850: 51, // resource (1656x) + 57605: 52, // attribute (1629x) + 57595: 53, // account (1627x) + 57959: 54, // failedLoginAttempts (1627x) + 57960: 55, // passwordLockTime (1627x) + 57346: 56, // identifier (1626x) + 41: 57, // ')' (1622x) + 57855: 58, // resume (1614x) + 57884: 59, // signed (1614x) + 57890: 60, // snapshot (1612x) + 57617: 61, // backend (1611x) + 57639: 62, // checkpoint (1611x) + 57656: 63, // concurrency (1611x) + 57662: 64, // csvBackslashEscape (1611x) + 57663: 65, // csvDelimiter (1611x) + 57664: 66, // csvHeader (1611x) + 57665: 67, // csvNotNull (1611x) + 57666: 68, // csvNull (1611x) + 57667: 69, // csvSeparator (1611x) + 57668: 70, // csvTrimLastSeparators (1611x) + 57990: 71, // fullBackupStorage (1611x) + 57992: 72, // gcTTL (1611x) + 57751: 73, // lastBackup (1611x) + 57802: 74, // onDuplicate (1611x) + 57803: 75, // online (1611x) + 57837: 76, // rateLimit (1611x) + 58018: 77, // restoredTS (1611x) + 57873: 78, // sendCredentialsToTiKV (1611x) + 57887: 79, // skipSchemaFiles (1611x) + 58024: 80, // startTS (1611x) + 57910: 81, // strictFormat (1611x) + 57926: 82, // tikvImporter (1611x) + 58053: 83, // untilTS (1611x) + 57620: 84, // begin (1605x) + 57651: 85, // commit (1605x) + 57785: 86, // no (1605x) + 57859: 87, // rollback (1605x) + 57904: 88, // start (1603x) + 57936: 89, // truncate (1602x) + 57632: 90, // cache (1600x) + 57786: 91, // nocache (1599x) + 57805: 92, // open (1599x) + 57596: 93, // action (1598x) + 57670: 94, // close (1598x) + 57673: 95, // cycle (1598x) + 57775: 96, // minValue (1598x) + 57694: 97, // end (1597x) + 57735: 98, // increment (1597x) + 57787: 99, // nocycle (1597x) + 57789: 100, // nomaxvalue (1597x) + 57790: 101, // nominvalue (1597x) + 57601: 102, // algorithm (1595x) + 57852: 103, // restart (1595x) + 57930: 104, // tp (1595x) + 57672: 105, // clustered (1594x) + 57740: 106, // invisible (1594x) + 57791: 107, // nonclustered (1594x) + 58119: 108, // regions (1594x) + 57950: 109, // visible (1594x) + 58075: 110, // background (1592x) + 57970: 111, // burstable (1592x) + 58063: 112, // priority (1592x) + 58074: 113, // queryLimit (1592x) + 58062: 114, // ruRate (1592x) + 57912: 115, // subpartition (1590x) + 57812: 116, // partitions (1589x) + 58011: 117, // plan (1589x) + 57957: 118, // yearType (1589x) + 57973: 119, // constraints (1587x) + 57988: 120, // followerConstraints (1587x) + 57989: 121, // followers (1587x) + 58001: 122, // leaderConstraints (1587x) + 58003: 123, // learnerConstraints (1587x) + 58004: 124, // learners (1587x) + 58015: 125, // primaryRegion (1587x) + 58021: 126, // schedule (1587x) + 57903: 127, // sqlTsiYear (1587x) + 58035: 128, // survivalPreferences (1587x) + 58060: 129, // voterConstraints (1587x) + 58061: 130, // voters (1587x) + 57648: 131, // columns (1585x) + 57949: 132, // view (1585x) + 57677: 133, // day (1584x) + 58072: 134, // watch (1583x) + 57978: 135, // defined (1582x) + 58069: 136, // execElapsed (1582x) + 57868: 137, // second (1582x) + 57730: 138, // hour (1581x) + 57772: 139, // microsecond (1581x) + 57774: 140, // minute (1581x) + 57778: 141, // month (1581x) + 57833: 142, // quarter (1581x) + 57896: 143, // sqlTsiDay (1581x) + 57897: 144, // sqlTsiHour (1581x) + 57898: 145, // sqlTsiMinute (1581x) + 57899: 146, // sqlTsiMonth (1581x) + 57900: 147, // sqlTsiQuarter (1581x) + 57901: 148, // sqlTsiSecond (1581x) + 57902: 149, // sqlTsiWeek (1581x) + 57952: 150, // week (1581x) + 57604: 151, // ascii (1580x) + 57631: 152, // byteType (1580x) + 57943: 153, // unicodeSym (1580x) + 57711: 154, // fields (1579x) + 57759: 155, // logs (1578x) + 57908: 156, // status (1578x) + 57920: 157, // tables (1578x) + 57981: 158, // timeDuration (1578x) + 57835: 159, // query (1576x) + 57874: 160, // separator (1576x) + 57641: 161, // cipher (1575x) + 57745: 162, // issuer (1575x) + 57763: 163, // maxConnectionsPerHour (1575x) + 57764: 164, // maxQueriesPerHour (1575x) + 57766: 165, // maxUpdatesPerHour (1575x) + 57767: 166, // maxUserConnections (1575x) + 57823: 167, // preceding (1575x) + 57866: 168, // san (1575x) + 57911: 169, // subject (1575x) + 57929: 170, // tokenIssuer (1575x) + 57982: 171, // endTime (1574x) + 57746: 172, // jsonType (1574x) + 57756: 173, // local (1574x) + 58023: 174, // startTime (1574x) + 57675: 175, // datetimeType (1573x) + 57676: 176, // dateType (1573x) + 57714: 177, // fixed (1573x) + 58092: 178, // job (1573x) + 57928: 179, // timeType (1573x) + 57624: 180, // bindings (1572x) + 57680: 181, // definer (1572x) + 57725: 182, // hash (1572x) + 57731: 183, // identified (1572x) + 57851: 184, // respect (1572x) + 57927: 185, // timestampType (1572x) + 57947: 186, // value (1572x) + 57618: 187, // backup (1571x) + 57628: 188, // booleanType (1571x) + 57669: 189, // current (1571x) + 57695: 190, // enforced (1571x) + 57717: 191, // following (1571x) + 57753: 192, // less (1571x) + 57793: 193, // nowait (1571x) + 57804: 194, // only (1571x) + 57867: 195, // savepoint (1571x) + 57886: 196, // skip (1571x) + 58037: 197, // taskTypes (1571x) + 57924: 198, // textType (1571x) + 57925: 199, // than (1571x) + 58114: 200, // tiFlash (1571x) + 57940: 201, // unbounded (1571x) + 57622: 202, // binding (1570x) + 57626: 203, // bitType (1570x) + 57629: 204, // boolType (1570x) + 57698: 205, // enum (1570x) + 57722: 206, // global (1570x) + 57865: 207, // hypo (1570x) + 57733: 208, // importKwd (1570x) + 57780: 209, // national (1570x) + 57781: 210, // ncharType (1570x) + 57994: 211, // next_row_id (1570x) + 57794: 212, // nvarcharType (1570x) + 57797: 213, // offset (1570x) + 57821: 214, // policy (1570x) + 58014: 215, // predicate (1570x) + 57922: 216, // temporary (1570x) + 57945: 217, // user (1570x) + 57682: 218, // digest (1569x) + 58091: 219, // jobs (1569x) + 57758: 220, // location (1569x) + 58012: 221, // planCache (1569x) + 57824: 222, // prepare (1569x) + 57846: 223, // replica (1569x) + 57858: 224, // role (1569x) + 58103: 225, // stats (1569x) + 57944: 226, // unknown (1569x) + 57958: 227, // wait (1569x) + 57630: 228, // btree (1568x) + 58071: 229, // cooldown (1568x) + 57679: 230, // declare (1568x) + 58070: 231, // dryRun (1568x) + 57718: 232, // format (1568x) + 57744: 233, // isolation (1568x) + 57750: 234, // last (1568x) + 57761: 235, // max_idxnum (1568x) + 57770: 236, // memory (1568x) + 57796: 237, // off (1568x) + 57806: 238, // optional (1568x) + 57816: 239, // per_db (1568x) + 57826: 240, // privileges (1568x) + 57849: 241, // required (1568x) + 57864: 242, // rtree (1568x) + 58100: 243, // sampleRate (1568x) + 57875: 244, // sequence (1568x) + 57878: 245, // session (1568x) + 57889: 246, // slow (1568x) + 57946: 247, // validation (1568x) + 57948: 248, // variables (1568x) + 57606: 249, // attributes (1567x) + 58081: 250, // cancel (1567x) + 57653: 251, // compact (1567x) + 58086: 252, // ddl (1567x) + 57684: 253, // disable (1567x) + 57688: 254, // do (1567x) + 57690: 255, // dynamic (1567x) + 57691: 256, // enable (1567x) + 57699: 257, // errorKwd (1567x) + 57983: 258, // exact (1567x) + 57715: 259, // flush (1567x) + 57719: 260, // full (1567x) + 57724: 261, // handler (1567x) + 57728: 262, // history (1567x) + 57768: 263, // mb (1567x) + 57776: 264, // mode (1567x) + 57783: 265, // next (1567x) + 57814: 266, // pause (1567x) + 57819: 267, // plugins (1567x) + 57828: 268, // processlist (1567x) + 57839: 269, // recover (1567x) + 57844: 270, // repair (1567x) + 57845: 271, // repeatable (1567x) + 58073: 272, // similar (1567x) + 58102: 273, // statistics (1567x) + 57913: 274, // subpartitions (1567x) + 58113: 275, // tidb (1567x) + 57954: 276, // without (1567x) + 58077: 277, // admin (1566x) + 58078: 278, // batch (1566x) + 57625: 279, // binlog (1566x) + 57627: 280, // block (1566x) + 57968: 281, // br (1566x) + 57969: 282, // briefType (1566x) + 58079: 283, // buckets (1566x) + 57633: 284, // calibrate (1566x) + 57634: 285, // capture (1566x) + 58082: 286, // cardinality (1566x) + 57637: 287, // chain (1566x) + 57644: 288, // clientErrorsSummary (1566x) + 58083: 289, // cmSketch (1566x) + 57645: 290, // coalesce (1566x) + 57654: 291, // compressed (1566x) + 57660: 292, // context (1566x) + 57972: 293, // copyKwd (1566x) + 58085: 294, // correlation (1566x) + 57661: 295, // cpu (1566x) + 57678: 296, // deallocate (1566x) + 58087: 297, // dependency (1566x) + 57683: 298, // directory (1566x) + 57686: 299, // discard (1566x) + 57687: 300, // disk (1566x) + 57979: 301, // dotType (1566x) + 58089: 302, // drainer (1566x) + 58090: 303, // dry (1566x) + 57689: 304, // duplicate (1566x) + 57704: 305, // exchange (1566x) + 57706: 306, // execute (1566x) + 57707: 307, // expansion (1566x) + 57986: 308, // flashback (1566x) + 57721: 309, // general (1566x) + 57726: 310, // help (1566x) + 58064: 311, // high (1566x) + 57727: 312, // histogram (1566x) + 57729: 313, // hosts (1566x) + 57732: 314, // identSQLErrors (1566x) + 57995: 315, // inplace (1566x) + 57739: 316, // instance (1566x) + 57996: 317, // instant (1566x) + 57743: 318, // ipc (1566x) + 57748: 319, // labels (1566x) + 57757: 320, // locked (1566x) + 58066: 321, // low (1566x) + 58065: 322, // medium (1566x) + 58007: 323, // metadata (1566x) + 57777: 324, // modify (1566x) + 58093: 325, // nodeID (1566x) + 58094: 326, // nodeState (1566x) + 57795: 327, // nulls (1566x) + 57808: 328, // pageSym (1566x) + 58097: 329, // pump (1566x) + 57832: 330, // purge (1566x) + 57838: 331, // rebuild (1566x) + 57840: 332, // redundant (1566x) + 57841: 333, // reload (1566x) + 57853: 334, // restore (1566x) + 57861: 335, // routine (1566x) + 58020: 336, // s3 (1566x) + 58099: 337, // samples (1566x) + 57870: 338, // secondaryLoad (1566x) + 57871: 339, // secondaryUnload (1566x) + 57881: 340, // share (1566x) + 57883: 341, // shutdown (1566x) + 57892: 342, // source (1566x) + 57607: 343, // statsOptions (1566x) + 58029: 344, // stop (1566x) + 57915: 345, // swaps (1566x) + 58038: 346, // tidbJson (1566x) + 58042: 347, // tokudbDefault (1566x) + 58043: 348, // tokudbFast (1566x) + 58044: 349, // tokudbLzma (1566x) + 58045: 350, // tokudbQuickLZ (1566x) + 58047: 351, // tokudbSmall (1566x) + 58046: 352, // tokudbSnappy (1566x) + 58048: 353, // tokudbUncompressed (1566x) + 58049: 354, // tokudbZlib (1566x) + 58050: 355, // tokudbZstd (1566x) + 58115: 356, // topn (1566x) + 57932: 357, // trace (1566x) + 57933: 358, // traditional (1566x) + 58058: 359, // trueCardCost (1566x) + 58076: 360, // unlimited (1566x) + 58057: 361, // verboseType (1566x) + 57951: 362, // warnings (1566x) + 57597: 363, // advise (1565x) + 57599: 364, // against (1565x) + 57600: 365, // ago (1565x) + 57602: 366, // always (1565x) + 57619: 367, // backups (1565x) + 57621: 368, // bernoulli (1565x) + 57623: 369, // bindingCache (1565x) + 58080: 370, // builtins (1565x) + 57635: 371, // cascaded (1565x) + 57636: 372, // causal (1565x) + 57642: 373, // cleanup (1565x) + 57643: 374, // client (1565x) + 57671: 375, // cluster (1565x) + 57646: 376, // collation (1565x) + 58084: 377, // columnStatsUsage (1565x) + 57652: 378, // committed (1565x) + 57649: 379, // config (1565x) + 57658: 380, // consistency (1565x) + 57659: 381, // consistent (1565x) + 58088: 382, // depth (1565x) + 57685: 383, // disabled (1565x) + 57980: 384, // dump (1565x) + 57692: 385, // enabled (1565x) + 57697: 386, // engines (1565x) + 57702: 387, // events (1565x) + 57703: 388, // evolve (1565x) + 57708: 389, // expire (1565x) + 57984: 390, // exprPushdownBlacklist (1565x) + 57709: 391, // extended (1565x) + 57710: 392, // faultsSym (1565x) + 57716: 393, // found (1565x) + 57720: 394, // function (1565x) + 57723: 395, // grants (1565x) + 58110: 396, // histogramsInFlight (1565x) + 57736: 397, // incremental (1565x) + 57737: 398, // indexes (1565x) + 57997: 399, // internal (1565x) + 57741: 400, // invoker (1565x) + 57742: 401, // io (1565x) + 57749: 402, // language (1565x) + 57754: 403, // level (1565x) + 57755: 404, // list (1565x) + 57760: 405, // master (1565x) + 57762: 406, // max_minutes (1565x) + 57782: 407, // never (1565x) + 57784: 408, // nextval (1565x) + 57792: 409, // none (1565x) + 57798: 410, // oltpReadOnly (1565x) + 57799: 411, // oltpReadWrite (1565x) + 57800: 412, // oltpWriteOnly (1565x) + 58095: 413, // optimistic (1565x) + 58009: 414, // optRuleBlacklist (1565x) + 57809: 415, // parser (1565x) + 57810: 416, // partial (1565x) + 57811: 417, // partitioning (1565x) + 57817: 418, // per_table (1565x) + 57815: 419, // percent (1565x) + 58096: 420, // pessimistic (1565x) + 57820: 421, // point (1565x) + 57825: 422, // preserve (1565x) + 57829: 423, // profile (1565x) + 57830: 424, // profiles (1565x) + 57834: 425, // queries (1565x) + 58016: 426, // recent (1565x) + 58120: 427, // region (1565x) + 58017: 428, // replayer (1565x) + 58118: 429, // reset (1565x) + 57854: 430, // restores (1565x) + 57856: 431, // reuse (1565x) + 57860: 432, // rollup (1565x) + 58098: 433, // run (1565x) + 57872: 434, // security (1565x) + 57877: 435, // serializable (1565x) + 58101: 436, // sessionStates (1565x) + 57885: 437, // simple (1565x) + 57888: 438, // slave (1565x) + 58107: 439, // statsHealthy (1565x) + 58105: 440, // statsHistograms (1565x) + 58109: 441, // statsLocked (1565x) + 58104: 442, // statsMeta (1565x) + 57916: 443, // switchesSym (1565x) + 57917: 444, // system (1565x) + 57918: 445, // systemTime (1565x) + 58036: 446, // target (1565x) + 58112: 447, // telemetryID (1565x) + 57923: 448, // temptable (1565x) + 58041: 449, // tls (1565x) + 58051: 450, // top (1565x) + 57931: 451, // tpcc (1565x) + 57801: 452, // tpch10 (1565x) + 57934: 453, // transaction (1565x) + 57935: 454, // triggers (1565x) + 57941: 455, // uncommitted (1565x) + 57942: 456, // undefined (1565x) + 58117: 457, // width (1565x) + 57955: 458, // workload (1565x) + 57956: 459, // x509 (1565x) + 57961: 460, // addDate (1564x) + 57603: 461, // any (1564x) + 57962: 462, // approxCountDistinct (1564x) + 57963: 463, // approxPercentile (1564x) + 57615: 464, // avg (1564x) + 57964: 465, // bitAnd (1564x) + 57965: 466, // bitOr (1564x) + 57966: 467, // bitXor (1564x) + 57967: 468, // bound (1564x) + 57971: 469, // cast (1564x) + 57975: 470, // curDate (1564x) + 57974: 471, // curTime (1564x) + 57976: 472, // dateAdd (1564x) + 57977: 473, // dateSub (1564x) + 57700: 474, // escape (1564x) + 57701: 475, // event (1564x) + 57705: 476, // exclusive (1564x) + 57985: 477, // extract (1564x) + 57712: 478, // file (1564x) + 57987: 479, // follower (1564x) + 57991: 480, // getFormat (1564x) + 57993: 481, // groupConcat (1564x) + 57734: 482, // imports (1564x) + 58067: 483, // ioReadBandwidth (1564x) + 58068: 484, // ioWriteBandwidth (1564x) + 57998: 485, // jsonArrayagg (1564x) + 57999: 486, // jsonObjectAgg (1564x) + 57752: 487, // lastval (1564x) + 58000: 488, // leader (1564x) + 58002: 489, // learner (1564x) + 58006: 490, // max (1564x) + 57769: 491, // member (1564x) + 58005: 492, // min (1564x) + 57779: 493, // names (1564x) + 58008: 494, // now (1564x) + 58013: 495, // position (1564x) + 57827: 496, // process (1564x) + 57831: 497, // proxy (1564x) + 57836: 498, // quick (1564x) + 57847: 499, // replicas (1564x) + 57848: 500, // replication (1564x) + 57857: 501, // reverse (1564x) + 57862: 502, // rowCount (1564x) + 58019: 503, // running (1564x) + 57879: 504, // setval (1564x) + 57882: 505, // shared (1564x) + 57891: 506, // some (1564x) + 57893: 507, // sqlBufferResult (1564x) + 57894: 508, // sqlCache (1564x) + 57895: 509, // sqlNoCache (1564x) + 58022: 510, // staleness (1564x) + 58025: 511, // std (1564x) + 58026: 512, // stddev (1564x) + 58027: 513, // stddevPop (1564x) + 58028: 514, // stddevSamp (1564x) + 58030: 515, // strict (1564x) + 58031: 516, // strong (1564x) + 58032: 517, // subDate (1564x) + 58034: 518, // substring (1564x) + 58033: 519, // sum (1564x) + 57914: 520, // super (1564x) + 58111: 521, // telemetry (1564x) + 58039: 522, // timestampAdd (1564x) + 58040: 523, // timestampDiff (1564x) + 58052: 524, // trim (1564x) + 58054: 525, // variance (1564x) + 58055: 526, // varPop (1564x) + 58056: 527, // varSamp (1564x) + 58059: 528, // voter (1564x) + 57953: 529, // weightString (1564x) + 57503: 530, // on (1474x) + 40: 531, // '(' (1469x) + 57590: 532, // with (1340x) + 57352: 533, // stringLit (1327x) + 58166: 534, // not2 (1278x) + 57404: 535, // defaultKwd (1230x) + 57496: 536, // not (1209x) + 57368: 537, // as (1176x) + 57383: 538, // collate (1144x) + 57567: 539, // union (1134x) + 57474: 540, // left (1131x) + 57531: 541, // right (1131x) + 57574: 542, // using (1120x) + 43: 543, // '+' (1107x) + 45: 544, // '-' (1105x) + 57495: 545, // mod (1085x) + 57512: 546, // partition (1062x) + 57578: 547, // values (1041x) + 57500: 548, // null (1039x) + 57445: 549, // ignore (1029x) + 57423: 550, // except (1023x) + 57452: 551, // intersect (1022x) + 57527: 552, // replace (1016x) + 57381: 553, // charType (1012x) + 57425: 554, // fetch (1005x) + 58155: 555, // eq (996x) + 57477: 556, // limit (996x) + 57538: 557, // set (996x) + 57430: 558, // forKwd (994x) + 57454: 559, // into (988x) + 58150: 560, // intLit (987x) + 57433: 561, // from (986x) + 57483: 562, // lock (981x) + 57586: 563, // where (973x) + 57508: 564, // order (968x) + 57431: 565, // force (963x) + 57366: 566, // and (960x) + 57507: 567, // or (936x) + 57357: 568, // andand (935x) + 57818: 569, // pipesAsOr (935x) + 57591: 570, // xor (935x) + 57437: 571, // group (906x) + 57439: 572, // having (901x) + 57552: 573, // straightJoin (893x) + 57589: 574, // window (887x) + 57573: 575, // use (885x) + 57465: 576, // join (881x) + 57408: 577, // desc (876x) + 57444: 578, // ifKwd (873x) + 57475: 579, // like (871x) + 57594: 580, // natural (871x) + 57389: 581, // cross (870x) + 57422: 582, // explain (870x) + 57449: 583, // inner (870x) + 42: 584, // '*' (868x) + 125: 585, // '}' (867x) + 57372: 586, // binaryType (864x) + 57457: 587, // insert (861x) + 57534: 588, // rows (855x) + 57585: 589, // when (849x) + 57417: 590, // elseKwd (845x) + 57517: 591, // rangeKwd (845x) + 57555: 592, // tableSample (845x) + 57438: 593, // groups (843x) + 57399: 594, // dayHour (842x) + 57400: 595, // dayMicrosecond (842x) + 57401: 596, // dayMinute (842x) + 57402: 597, // daySecond (842x) + 57441: 598, // hourMicrosecond (842x) + 57442: 599, // hourMinute (842x) + 57443: 600, // hourSecond (842x) + 57493: 601, // minuteMicrosecond (842x) + 57494: 602, // minuteSecond (842x) + 57536: 603, // secondMicrosecond (842x) + 57592: 604, // yearMonth (842x) + 57369: 605, // asc (840x) + 57446: 606, // in (834x) + 57558: 607, // then (834x) + 57554: 608, // tableKwd (827x) + 47: 609, // '/' (826x) + 37: 610, // '%' (825x) + 38: 611, // '&' (825x) + 94: 612, // '^' (825x) + 124: 613, // '|' (825x) + 57378: 614, // caseKwd (825x) + 57412: 615, // div (825x) + 58160: 616, // lsh (825x) + 57526: 617, // repeat (825x) + 58165: 618, // rsh (825x) + 60: 619, // '<' (824x) + 62: 620, // '>' (824x) + 58156: 621, // ge (824x) + 57456: 622, // is (824x) + 58157: 623, // le (824x) + 58161: 624, // neq (824x) + 58162: 625, // neqSynonym (824x) + 58163: 626, // nulleq (824x) + 57370: 627, // between (819x) + 57353: 628, // singleAtIdentifier (818x) + 57424: 629, // falseKwd (814x) + 57565: 630, // trueKwd (814x) + 57394: 631, // currentUser (813x) + 57476: 632, // ilike (811x) + 57523: 633, // regexpKwd (811x) + 57532: 634, // rlike (811x) + 57349: 635, // memberof (808x) + 58149: 636, // decLit (806x) + 58148: 637, // floatLit (806x) + 58151: 638, // hexLit (806x) + 57533: 639, // row (805x) + 58152: 640, // bitLit (804x) + 57453: 641, // interval (804x) + 58164: 642, // paramMarker (803x) + 123: 643, // '{' (801x) + 57397: 644, // database (797x) + 57420: 645, // exists (796x) + 57387: 646, // convert (793x) + 57351: 647, // underscoreCS (793x) + 58128: 648, // builtinCurDate (792x) + 58136: 649, // builtinNow (792x) + 57391: 650, // currentDate (792x) + 57393: 651, // currentTs (792x) + 57354: 652, // doubleAtIdentifier (792x) + 57481: 653, // localTime (792x) + 57482: 654, // localTs (792x) + 58125: 655, // builtinCount (790x) + 33: 656, // '!' (789x) + 126: 657, // '~' (789x) + 58126: 658, // builtinApproxCountDistinct (789x) + 58127: 659, // builtinApproxPercentile (789x) + 58121: 660, // builtinBitAnd (789x) + 58122: 661, // builtinBitOr (789x) + 58123: 662, // builtinBitXor (789x) + 58124: 663, // builtinCast (789x) + 58129: 664, // builtinCurTime (789x) + 58130: 665, // builtinDateAdd (789x) + 58131: 666, // builtinDateSub (789x) + 58132: 667, // builtinExtract (789x) + 58133: 668, // builtinGroupConcat (789x) + 58134: 669, // builtinMax (789x) + 58135: 670, // builtinMin (789x) + 58137: 671, // builtinPosition (789x) + 58141: 672, // builtinStddevPop (789x) + 58142: 673, // builtinStddevSamp (789x) + 58138: 674, // builtinSubstring (789x) + 58139: 675, // builtinSum (789x) + 58140: 676, // builtinSysDate (789x) + 58143: 677, // builtinTranslate (789x) + 58144: 678, // builtinTrim (789x) + 58145: 679, // builtinUser (789x) + 58146: 680, // builtinVarPop (789x) + 58147: 681, // builtinVarSamp (789x) + 57390: 682, // cumeDist (789x) + 57395: 683, // currentRole (789x) + 57392: 684, // currentTime (789x) + 57407: 685, // denseRank (789x) + 57426: 686, // firstValue (789x) + 57469: 687, // lag (789x) + 57470: 688, // lastValue (789x) + 57471: 689, // lead (789x) + 57498: 690, // nthValue (789x) + 57499: 691, // ntile (789x) + 57513: 692, // percentRank (789x) + 57518: 693, // rank (789x) + 57535: 694, // rowNumber (789x) + 57537: 695, // selectKwd (789x) + 57542: 696, // sql (789x) + 57553: 697, // tidbCurrentTSO (789x) + 57575: 698, // utcDate (789x) + 57577: 699, // utcTime (789x) + 57576: 700, // utcTimestamp (789x) + 57466: 701, // key (783x) + 57382: 702, // check (773x) + 57358: 703, // pipes (773x) + 57515: 704, // primary (773x) + 57566: 705, // unique (766x) + 57385: 706, // constraint (763x) + 57522: 707, // references (761x) + 57435: 708, // generated (757x) + 57380: 709, // character (753x) + 57447: 710, // index (737x) + 57487: 711, // match (723x) + 57562: 712, // to (632x) + 57365: 713, // analyze (626x) + 57571: 714, // update (621x) + 57363: 715, // all (610x) + 46: 716, // '.' (609x) + 58154: 717, // assignmentEq (575x) + 58158: 718, // jss (574x) + 58159: 719, // juss (574x) + 57488: 720, // maxValue (574x) + 57367: 721, // array (571x) + 57478: 722, // lines (567x) + 57375: 723, // by (559x) + 57364: 724, // alter (557x) + 57528: 725, // require (554x) + 64: 726, // '@' (549x) + 57414: 727, // drop (543x) + 57377: 728, // cascade (542x) + 57519: 729, // read (542x) + 57529: 730, // restrict (542x) + 57347: 731, // asof (541x) + 57581: 732, // varcharacter (541x) + 57580: 733, // varcharType (541x) + 57403: 734, // decimalType (540x) + 57413: 735, // doubleType (540x) + 57427: 736, // floatType (540x) + 57451: 737, // integerType (540x) + 57458: 738, // intType (540x) + 57520: 739, // realType (540x) + 57582: 740, // varbinaryType (539x) + 57371: 741, // bigIntType (538x) + 57373: 742, // blobType (538x) + 57388: 743, // create (538x) + 57428: 744, // float4Type (538x) + 57429: 745, // float8Type (538x) + 57432: 746, // foreign (538x) + 57434: 747, // fulltext (538x) + 57459: 748, // int1Type (538x) + 57460: 749, // int2Type (538x) + 57461: 750, // int3Type (538x) + 57462: 751, // int4Type (538x) + 57463: 752, // int8Type (538x) + 57579: 753, // long (538x) + 57484: 754, // longblobType (538x) + 57485: 755, // longtextType (538x) + 57489: 756, // mediumblobType (538x) + 57490: 757, // mediumIntType (538x) + 57491: 758, // mediumtextType (538x) + 57492: 759, // middleIntType (538x) + 57501: 760, // numericType (538x) + 57540: 761, // smallIntType (538x) + 57559: 762, // tinyblobType (538x) + 57560: 763, // tinyIntType (538x) + 57561: 764, // tinytextType (538x) + 57348: 765, // toTimestamp (537x) + 57379: 766, // change (535x) + 57525: 767, // rename (535x) + 57588: 768, // write (535x) + 57362: 769, // add (534x) + 58439: 770, // Identifier (534x) + 58520: 771, // NotKeywordToken (534x) + 58798: 772, // TiDBKeyword (534x) + 58808: 773, // UnReservedKeyword (534x) + 57504: 774, // optimize (533x) + 58763: 775, // SubSelect (259x) + 58818: 776, // UserVariable (200x) + 58491: 777, // Literal (198x) + 58734: 778, // SimpleIdent (198x) + 58753: 779, // StringLiteral (198x) + 58517: 780, // NextValueForSequence (195x) + 58416: 781, // FunctionCallGeneric (194x) + 58417: 782, // FunctionCallKeyword (194x) + 58418: 783, // FunctionCallNonKeyword (194x) + 58419: 784, // FunctionNameConflict (194x) + 58420: 785, // FunctionNameDateArith (194x) + 58421: 786, // FunctionNameDateArithMultiForms (194x) + 58422: 787, // FunctionNameDatetimePrecision (194x) + 58423: 788, // FunctionNameOptionalBraces (194x) + 58424: 789, // FunctionNameSequence (194x) + 58733: 790, // SimpleExpr (194x) + 58764: 791, // SumExpr (194x) + 58766: 792, // SystemVariable (194x) + 58829: 793, // Variable (194x) + 58853: 794, // WindowFuncCall (194x) + 58247: 795, // BitExpr (176x) + 58595: 796, // PredicateExpr (144x) + 58250: 797, // BoolPri (141x) + 58379: 798, // Expression (141x) + 58515: 799, // NUM (122x) + 58869: 800, // logAnd (107x) + 58870: 801, // logOr (107x) + 58370: 802, // EqOpt (98x) + 57406: 803, // deleteKwd (86x) + 58776: 804, // TableName (81x) + 58754: 805, // StringName (56x) + 58688: 806, // SelectStmt (52x) + 58689: 807, // SelectStmtBasic (52x) + 58691: 808, // SelectStmtFromDualTable (52x) + 58692: 809, // SelectStmtFromTable (52x) + 58709: 810, // SetOprClause (52x) + 58710: 811, // SetOprClauseList (51x) + 58713: 812, // SetOprStmtWithLimitOrderBy (51x) + 58714: 813, // SetOprStmtWoutLimitOrderBy (51x) + 57569: 814, // unsigned (50x) + 58859: 815, // WithClause (49x) + 58482: 816, // LengthNum (48x) + 58701: 817, // SelectStmtWithClause (48x) + 58712: 818, // SetOprStmt (48x) + 57593: 819, // zerofill (48x) + 57511: 820, // over (45x) + 58276: 821, // ColumnName (41x) + 58812: 822, // UpdateStmtNoWith (41x) + 58336: 823, // DeleteWithoutUsingStmt (40x) + 58467: 824, // InsertIntoStmt (38x) + 58470: 825, // Int64Num (38x) + 58652: 826, // ReplaceIntoStmt (38x) + 58811: 827, // UpdateStmt (38x) + 57409: 828, // describe (36x) + 57410: 829, // distinct (36x) + 57411: 830, // distinctRow (36x) + 57587: 831, // while (36x) + 58858: 832, // WindowingClause (35x) + 58335: 833, // DeleteWithUsingStmt (34x) + 57464: 834, // iterate (34x) + 57473: 835, // leave (34x) + 57405: 836, // delayed (33x) + 57440: 837, // highPriority (33x) + 57486: 838, // lowPriority (33x) + 58334: 839, // DeleteFromStmt (32x) + 57356: 840, // hintComment (27x) + 58390: 841, // FieldLen (25x) + 58565: 842, // OrderBy (25x) + 58695: 843, // SelectStmtLimit (25x) + 58559: 844, // OptWindowingClause (24x) + 58220: 845, // AnalyzeTableStmt (23x) + 58290: 846, // CommitStmt (23x) + 58679: 847, // RollbackStmt (23x) + 58717: 848, // SetStmt (23x) + 57543: 849, // sqlBigResult (23x) + 57544: 850, // sqlCalcFoundRows (23x) + 57545: 851, // sqlSmallResult (23x) + 57557: 852, // terminated (21x) + 58265: 853, // CharsetKw (20x) + 58440: 854, // IfExists (20x) + 58820: 855, // Username (20x) + 57418: 856, // enclosed (19x) + 58375: 857, // ExplainStmt (19x) + 58376: 858, // ExplainSym (19x) + 58577: 859, // PartitionNameList (19x) + 58806: 860, // TruncateTableStmt (19x) + 58813: 861, // UseStmt (19x) + 57419: 862, // escaped (18x) + 58380: 863, // ExpressionList (18x) + 57350: 864, // optionallyEnclosedBy (18x) + 58589: 865, // PlacementPolicyOption (18x) + 58606: 866, // ProcedureBlockContent (18x) + 58635: 867, // ProcedureUnlabelLoopStmt (18x) + 58608: 868, // ProcedureCaseStmt (17x) + 58609: 869, // ProcedureCloseCur (17x) + 58615: 870, // ProcedureFetchInto (17x) + 58621: 871, // ProcedureIfstmt (17x) + 58622: 872, // ProcedureIterate (17x) + 58623: 873, // ProcedureLabeledBlock (17x) + 58637: 874, // ProcedurelabeledLoopStmt (17x) + 58624: 875, // ProcedureLeave (17x) + 58625: 876, // ProcedureOpenCur (17x) + 58628: 877, // ProcedureProcStmt (17x) + 58631: 878, // ProcedureSearchedCase (17x) + 58632: 879, // ProcedureSimpleCase (17x) + 58633: 880, // ProcedureStatementStmt (17x) + 58636: 881, // ProcedureUnlabeledBlock (17x) + 58634: 882, // ProcedureUnlabelLoopBlock (17x) + 58441: 883, // IfNotExists (16x) + 58777: 884, // TableNameList (16x) + 58341: 885, // DistinctKwd (15x) + 58800: 886, // TimestampUnit (15x) + 58342: 887, // DistinctOpt (14x) + 58543: 888, // OptFieldLen (14x) + 58843: 889, // WhereClause (14x) + 58844: 890, // WhereClauseOptional (14x) + 58329: 891, // DefaultKwdOpt (13x) + 58371: 892, // EqOrAssignmentEq (13x) + 58378: 893, // ExprOrDefault (13x) + 57480: 894, // load (13x) + 58476: 895, // JoinTable (12x) + 58538: 896, // OptBinary (12x) + 57524: 897, // release (12x) + 58676: 898, // RolenameComposed (12x) + 58773: 899, // TableFactor (12x) + 58786: 900, // TableRef (12x) + 58799: 901, // TimeUnit (12x) + 58219: 902, // AnalyzeOptionListOpt (11x) + 58411: 903, // FromOrIn (11x) + 58215: 904, // AlterTableStmt (10x) + 58266: 905, // CharsetName (10x) + 58277: 906, // ColumnNameList (10x) + 58319: 907, // DBName (10x) + 57497: 908, // noWriteToBinLog (10x) + 58566: 909, // OrderByOptional (10x) + 58568: 910, // PartDefOption (10x) + 58732: 911, // SignedNum (10x) + 58253: 912, // BuggyDefaultFalseDistinctOpt (9x) + 58328: 913, // DefaultFalseDistinctOpt (9x) + 58477: 914, // JoinType (9x) + 58521: 915, // NotSym (9x) + 58528: 916, // NumLiteral (9x) + 58675: 917, // Rolename (9x) + 58670: 918, // RoleNameString (9x) + 58317: 919, // CrossOpt (8x) + 58377: 920, // ExplainableStmt (8x) + 58381: 921, // ExpressionListOpt (8x) + 58461: 922, // IndexPartSpecification (8x) + 58478: 923, // KeyOrIndex (8x) + 58518: 924, // NoWriteToBinLogAliasOpt (8x) + 58696: 925, // SelectStmtLimitOpt (8x) + 58832: 926, // VariableName (8x) + 58200: 927, // AllOrPartitionNameList (7x) + 58300: 928, // ConstraintKeywordOpt (7x) + 58324: 929, // DatabaseSym (7x) + 58396: 930, // FieldsOrColumns (7x) + 58408: 931, // ForceOpt (7x) + 58462: 932, // IndexPartSpecificationList (7x) + 57468: 933, // kill (7x) + 58599: 934, // Priority (7x) + 58629: 935, // ProcedureProcStmt1s (7x) + 58658: 936, // ResourceGroupName (7x) + 58680: 937, // RowFormat (7x) + 58683: 938, // RowValue (7x) + 58707: 939, // SetExpr (7x) + 58719: 940, // ShowDatabaseNameOpt (7x) + 58783: 941, // TableOption (7x) + 57583: 942, // varying (7x) + 58242: 943, // BeginTransactionStmt (6x) + 58244: 944, // BindableStmt (6x) + 58234: 945, // BRIEBooleanOptionName (6x) + 58235: 946, // BRIEIntegerOptionName (6x) + 58236: 947, // BRIEKeywordOptionName (6x) + 58237: 948, // BRIEOption (6x) + 58238: 949, // BRIEOptions (6x) + 58240: 950, // BRIEStringOptionName (6x) + 58264: 951, // Char (6x) + 57384: 952, // column (6x) + 58271: 953, // ColumnDef (6x) + 58321: 954, // DatabaseOption (6x) + 58372: 955, // EscapedTableRef (6x) + 58394: 956, // FieldTerminator (6x) + 57436: 957, // grant (6x) + 58443: 958, // IgnoreOptional (6x) + 58453: 959, // IndexInvisible (6x) + 58458: 960, // IndexNameList (6x) + 58464: 961, // IndexType (6x) + 58498: 962, // LoadDataStmt (6x) + 58578: 963, // PartitionNameListOpt (6x) + 57516: 964, // procedure (6x) + 58647: 965, // ReleaseSavepointStmt (6x) + 58677: 966, // RolenameList (6x) + 58684: 967, // SavepointStmt (6x) + 57539: 968, // show (6x) + 58781: 969, // TableOptimizerHints (6x) + 58821: 970, // UsernameList (6x) + 58860: 971, // WithClustered (6x) + 58198: 972, // AlgorithmClause (5x) + 58255: 973, // ByItem (5x) + 58270: 974, // CollationName (5x) + 58274: 975, // ColumnKeywordOpt (5x) + 58337: 976, // DirectPlacementOption (5x) + 58339: 977, // DirectResourceGroupOption (5x) + 58392: 978, // FieldOpt (5x) + 58393: 979, // FieldOpts (5x) + 58437: 980, // IdentList (5x) + 58456: 981, // IndexName (5x) + 58459: 982, // IndexOption (5x) + 58460: 983, // IndexOptionList (5x) + 57448: 984, // infile (5x) + 58487: 985, // LimitOption (5x) + 58502: 986, // LockClause (5x) + 58540: 987, // OptCharsetWithOptBinary (5x) + 58550: 988, // OptNullTreatment (5x) + 58593: 989, // PolicyName (5x) + 58600: 990, // PriorityOpt (5x) + 58687: 991, // SelectLockOpt (5x) + 58694: 992, // SelectStmtIntoOption (5x) + 58787: 993, // TableRefs (5x) + 58814: 994, // UserSpec (5x) + 58223: 995, // AsOfClause (4x) + 58226: 996, // Assignment (4x) + 58232: 997, // AuthString (4x) + 58251: 998, // Boolean (4x) + 58254: 999, // BuiltinFunction (4x) + 58256: 1000, // ByList (4x) + 58294: 1001, // ConfigItemName (4x) + 58298: 1002, // Constraint (4x) + 58404: 1003, // FloatOpt (4x) + 58465: 1004, // IndexTypeName (4x) + 58527: 1005, // NumList (4x) + 57505: 1006, // option (4x) + 57506: 1007, // optionally (4x) + 58556: 1008, // OptWild (4x) + 57510: 1009, // outer (4x) + 58594: 1010, // Precision (4x) + 58643: 1011, // ReferDef (4x) + 58666: 1012, // RestrictOrCascadeOpt (4x) + 58682: 1013, // RowStmt (4x) + 58702: 1014, // SequenceOption (4x) + 57551: 1015, // statsExtended (4x) + 58768: 1016, // TableAsName (4x) + 58769: 1017, // TableAsNameOpt (4x) + 58780: 1018, // TableNameOptWild (4x) + 58782: 1019, // TableOptimizerHintsOpt (4x) + 58784: 1020, // TableOptionList (4x) + 58795: 1021, // TextString (4x) + 58802: 1022, // TraceableStmt (4x) + 58803: 1023, // TransactionChar (4x) + 58815: 1024, // UserSpecList (4x) + 58828: 1025, // Varchar (4x) + 58854: 1026, // WindowName (4x) + 58227: 1027, // AssignmentList (3x) + 58229: 1028, // AttributesOpt (3x) + 58248: 1029, // BitValueType (3x) + 58249: 1030, // BlobType (3x) + 58252: 1031, // BooleanType (3x) + 58283: 1032, // ColumnOption (3x) + 58286: 1033, // ColumnPosition (3x) + 58291: 1034, // CommonTableExpr (3x) + 58313: 1035, // CreateTableStmt (3x) + 58318: 1036, // CurdateSym (3x) + 58322: 1037, // DatabaseOptionList (3x) + 58325: 1038, // DateAndTimeType (3x) + 58332: 1039, // DefaultTrueDistinctOpt (3x) + 58338: 1040, // DirectResourceGroupBackgroundOption (3x) + 58340: 1041, // DirectResourceGroupRunawayOption (3x) + 58362: 1042, // DynamicCalibrateResourceOption (3x) + 57416: 1043, // elseIfKwd (3x) + 58367: 1044, // EnforcedOrNot (3x) + 58383: 1045, // ExtendedPriv (3x) + 58399: 1046, // FixedPointType (3x) + 58405: 1047, // FloatingPointType (3x) + 58425: 1048, // GeneratedAlways (3x) + 58427: 1049, // GlobalScope (3x) + 58431: 1050, // GroupByClause (3x) + 58448: 1051, // IndexHint (3x) + 58452: 1052, // IndexHintType (3x) + 58457: 1053, // IndexNameAndTypeOpt (3x) + 58471: 1054, // IntegerType (3x) + 57467: 1055, // keys (3x) + 58489: 1056, // Lines (3x) + 58501: 1057, // LocationLabelList (3x) + 58514: 1058, // NChar (3x) + 58522: 1059, // NowSym (3x) + 58523: 1060, // NowSymFunc (3x) + 58524: 1061, // NowSymOptionFraction (3x) + 58529: 1062, // NumericType (3x) + 58516: 1063, // NVarchar (3x) + 58551: 1064, // OptOrder (3x) + 58555: 1065, // OptTemporary (3x) + 58569: 1066, // PartDefOptionList (3x) + 58571: 1067, // PartitionDefinition (3x) + 58582: 1068, // PasswordOrLockOption (3x) + 58592: 1069, // PluginNameList (3x) + 58598: 1070, // PrimaryOpt (3x) + 58601: 1071, // PrivElem (3x) + 58603: 1072, // PrivType (3x) + 58638: 1073, // QueryWatchOption (3x) + 58640: 1074, // QueryWatchTextOption (3x) + 58653: 1075, // RequireClause (3x) + 58654: 1076, // RequireClauseOpt (3x) + 58656: 1077, // RequireListElement (3x) + 58678: 1078, // RolenameWithoutIdent (3x) + 58671: 1079, // RoleOrPrivElem (3x) + 58693: 1080, // SelectStmtGroup (3x) + 58711: 1081, // SetOprOpt (3x) + 58731: 1082, // SignedLiteral (3x) + 58756: 1083, // StringType (3x) + 58767: 1084, // TableAliasRefList (3x) + 58770: 1085, // TableElement (3x) + 58797: 1086, // TextType (3x) + 58804: 1087, // TransactionChars (3x) + 57564: 1088, // trigger (3x) + 58807: 1089, // Type (3x) + 57568: 1090, // unlock (3x) + 57570: 1091, // until (3x) + 57572: 1092, // usage (3x) + 58825: 1093, // ValuesList (3x) + 58827: 1094, // ValuesStmtList (3x) + 58823: 1095, // ValueSym (3x) + 58830: 1096, // VariableAssignment (3x) + 58851: 1097, // WindowFrameStart (3x) + 58868: 1098, // Year (3x) + 58194: 1099, // AddQueryWatchStmt (2x) + 58196: 1100, // AdminStmt (2x) + 58199: 1101, // AllColumnsOrPredicateColumnsOpt (2x) + 58201: 1102, // AlterDatabaseStmt (2x) + 58202: 1103, // AlterInstanceStmt (2x) + 58203: 1104, // AlterOrderItem (2x) + 58205: 1105, // AlterPolicyStmt (2x) + 58206: 1106, // AlterRangeStmt (2x) + 58207: 1107, // AlterResourceGroupStmt (2x) + 58208: 1108, // AlterSequenceOption (2x) + 58210: 1109, // AlterSequenceStmt (2x) + 58211: 1110, // AlterTableSpec (2x) + 58216: 1111, // AlterUserStmt (2x) + 58217: 1112, // AnalyzeOption (2x) + 58246: 1113, // BinlogStmt (2x) + 58239: 1114, // BRIEStmt (2x) + 58241: 1115, // BRIETables (2x) + 58258: 1116, // CalibrateResourceStmt (2x) + 57376: 1117, // call (2x) + 58260: 1118, // CallStmt (2x) + 58261: 1119, // CancelImportStmt (2x) + 58262: 1120, // CastType (2x) + 58263: 1121, // ChangeStmt (2x) + 58269: 1122, // CheckConstraintKeyword (2x) + 58278: 1123, // ColumnNameListOpt (2x) + 58281: 1124, // ColumnNameOrUserVariable (2x) + 58280: 1125, // ColumnNameOrUserVarListOptWithBrackets (2x) + 58284: 1126, // ColumnOptionList (2x) + 58285: 1127, // ColumnOptionListOpt (2x) + 58289: 1128, // CommentOrAttributeOption (2x) + 58293: 1129, // CompletionTypeWithinTransaction (2x) + 58295: 1130, // ConnectionOption (2x) + 58297: 1131, // ConnectionOptions (2x) + 58301: 1132, // CreateBindingStmt (2x) + 58302: 1133, // CreateDatabaseStmt (2x) + 58303: 1134, // CreateIndexStmt (2x) + 58304: 1135, // CreatePolicyStmt (2x) + 58305: 1136, // CreateProcedureStmt (2x) + 58306: 1137, // CreateResourceGroupStmt (2x) + 58307: 1138, // CreateRoleStmt (2x) + 58309: 1139, // CreateSequenceStmt (2x) + 58310: 1140, // CreateStatisticsStmt (2x) + 58311: 1141, // CreateTableOptionListOpt (2x) + 58314: 1142, // CreateUserStmt (2x) + 58316: 1143, // CreateViewStmt (2x) + 57398: 1144, // databases (2x) + 58326: 1145, // DeallocateStmt (2x) + 58327: 1146, // DeallocateSym (2x) + 58330: 1147, // DefaultOrExpression (2x) + 58343: 1148, // DoStmt (2x) + 58344: 1149, // DropBindingStmt (2x) + 58345: 1150, // DropDatabaseStmt (2x) + 58346: 1151, // DropIndexStmt (2x) + 58347: 1152, // DropLoadDataStmt (2x) + 58348: 1153, // DropPolicyStmt (2x) + 58349: 1154, // DropProcedureStmt (2x) + 58350: 1155, // DropQueryWatchStmt (2x) + 58351: 1156, // DropResourceGroupStmt (2x) + 58352: 1157, // DropRoleStmt (2x) + 58353: 1158, // DropSequenceStmt (2x) + 58354: 1159, // DropStatisticsStmt (2x) + 58355: 1160, // DropStatsStmt (2x) + 58356: 1161, // DropTableStmt (2x) + 58357: 1162, // DropUserStmt (2x) + 58358: 1163, // DropViewStmt (2x) + 58360: 1164, // DuplicateOpt (2x) + 58363: 1165, // ElseCaseOpt (2x) + 58365: 1166, // EmptyStmt (2x) + 58366: 1167, // EncryptionOpt (2x) + 58368: 1168, // EnforcedOrNotOpt (2x) + 58373: 1169, // ExecuteStmt (2x) + 58374: 1170, // ExplainFormatType (2x) + 58385: 1171, // Field (2x) + 58388: 1172, // FieldItem (2x) + 58395: 1173, // Fields (2x) + 58400: 1174, // FlashbackDatabaseStmt (2x) + 58401: 1175, // FlashbackTableStmt (2x) + 58402: 1176, // FlashbackToNewName (2x) + 58403: 1177, // FlashbackToTimestampStmt (2x) + 58407: 1178, // FlushStmt (2x) + 58409: 1179, // FormatOpt (2x) + 58414: 1180, // FuncDatetimePrecList (2x) + 58415: 1181, // FuncDatetimePrecListOpt (2x) + 58428: 1182, // GrantProxyStmt (2x) + 58429: 1183, // GrantRoleStmt (2x) + 58430: 1184, // GrantStmt (2x) + 58432: 1185, // HandleRange (2x) + 58434: 1186, // HashString (2x) + 58435: 1187, // HavingClause (2x) + 58436: 1188, // HelpStmt (2x) + 58445: 1189, // ImportIntoStmt (2x) + 58447: 1190, // IndexAdviseStmt (2x) + 58449: 1191, // IndexHintList (2x) + 58450: 1192, // IndexHintListOpt (2x) + 58455: 1193, // IndexLockAndAlgorithmOpt (2x) + 57450: 1194, // inout (2x) + 58468: 1195, // InsertValues (2x) + 58473: 1196, // IntoOpt (2x) + 58479: 1197, // KeyOrIndexOpt (2x) + 58480: 1198, // KillOrKillTiDB (2x) + 58481: 1199, // KillStmt (2x) + 58483: 1200, // LikeOrIlikeEscapeOpt (2x) + 58486: 1201, // LimitClause (2x) + 57479: 1202, // linear (2x) + 58488: 1203, // LinearOpt (2x) + 58492: 1204, // LoadDataOption (2x) + 58494: 1205, // LoadDataOptionListOpt (2x) + 58495: 1206, // LoadDataSetItem (2x) + 58497: 1207, // LoadDataSetSpecOpt (2x) + 58499: 1208, // LoadStatsStmt (2x) + 58500: 1209, // LocalOpt (2x) + 58503: 1210, // LockStatsStmt (2x) + 58504: 1211, // LockTablesStmt (2x) + 58512: 1212, // MaxValueOrExpression (2x) + 58519: 1213, // NonTransactionalDMLStmt (2x) + 58525: 1214, // NowSymOptionFractionParentheses (2x) + 58530: 1215, // ObjectType (2x) + 57502: 1216, // of (2x) + 58531: 1217, // OfTablesOpt (2x) + 58532: 1218, // OnCommitOpt (2x) + 58533: 1219, // OnDelete (2x) + 58536: 1220, // OnUpdate (2x) + 58541: 1221, // OptCollate (2x) + 58545: 1222, // OptFull (2x) + 58547: 1223, // OptInteger (2x) + 58561: 1224, // OptionalBraces (2x) + 58560: 1225, // OptionLevel (2x) + 58549: 1226, // OptLeadLagInfo (2x) + 58548: 1227, // OptLLDefault (2x) + 57509: 1228, // out (2x) + 58567: 1229, // OuterOpt (2x) + 58572: 1230, // PartitionDefinitionList (2x) + 58573: 1231, // PartitionDefinitionListOpt (2x) + 58574: 1232, // PartitionIntervalOpt (2x) + 58580: 1233, // PartitionOpt (2x) + 58581: 1234, // PasswordOpt (2x) + 58583: 1235, // PasswordOrLockOptionList (2x) + 58584: 1236, // PasswordOrLockOptions (2x) + 58585: 1237, // PauseLoadDataStmt (2x) + 58588: 1238, // PlacementOptionList (2x) + 58591: 1239, // PlanReplayerStmt (2x) + 58597: 1240, // PreparedStmt (2x) + 58602: 1241, // PrivLevel (2x) + 58604: 1242, // ProcedurceCond (2x) + 58605: 1243, // ProcedurceLabelOpt (2x) + 58611: 1244, // ProcedureDecl (2x) + 58618: 1245, // ProcedureHcond (2x) + 58620: 1246, // ProcedureIf (2x) + 58641: 1247, // QuickOptional (2x) + 58642: 1248, // RecoverTableStmt (2x) + 58644: 1249, // ReferOpt (2x) + 58646: 1250, // RegexpSym (2x) + 58648: 1251, // RenameTableStmt (2x) + 58649: 1252, // RenameUserStmt (2x) + 58651: 1253, // RepeatableOpt (2x) + 58659: 1254, // ResourceGroupNameOption (2x) + 58660: 1255, // ResourceGroupOptionList (2x) + 58662: 1256, // ResourceGroupRunawayActionOption (2x) + 58664: 1257, // ResourceGroupRunawayWatchOption (2x) + 58665: 1258, // RestartStmt (2x) + 58667: 1259, // ResumeLoadDataStmt (2x) + 57530: 1260, // revoke (2x) + 58668: 1261, // RevokeRoleStmt (2x) + 58669: 1262, // RevokeStmt (2x) + 58672: 1263, // RoleOrPrivElemList (2x) + 58673: 1264, // RoleSpec (2x) + 58685: 1265, // SearchWhenThen (2x) + 58697: 1266, // SelectStmtOpt (2x) + 58700: 1267, // SelectStmtSQLCache (2x) + 58704: 1268, // SetBindingStmt (2x) + 58705: 1269, // SetDefaultRoleOpt (2x) + 58706: 1270, // SetDefaultRoleStmt (2x) + 58716: 1271, // SetRoleStmt (2x) + 58724: 1272, // ShowProfileType (2x) + 58727: 1273, // ShowStmt (2x) + 58728: 1274, // ShowTableAliasOpt (2x) + 58730: 1275, // ShutdownStmt (2x) + 58735: 1276, // SimpleWhenThen (2x) + 58740: 1277, // SplitOption (2x) + 58741: 1278, // SplitRegionStmt (2x) + 58737: 1279, // SpOptInout (2x) + 58738: 1280, // SpPdparam (2x) + 57546: 1281, // sqlexception (2x) + 57547: 1282, // sqlstate (2x) + 57548: 1283, // sqlwarning (2x) + 58745: 1284, // Statement (2x) + 58748: 1285, // StatsOptionsOpt (2x) + 58749: 1286, // StatsPersistentVal (2x) + 58750: 1287, // StatsType (2x) + 58757: 1288, // SubPartDefinition (2x) + 58760: 1289, // SubPartitionMethod (2x) + 58765: 1290, // Symbol (2x) + 58771: 1291, // TableElementList (2x) + 58774: 1292, // TableLock (2x) + 58778: 1293, // TableNameListOpt (2x) + 58785: 1294, // TableOrTables (2x) + 58794: 1295, // TablesTerminalSym (2x) + 58792: 1296, // TableToTable (2x) + 58796: 1297, // TextStringList (2x) + 58801: 1298, // TraceStmt (2x) + 58809: 1299, // UnlockStatsStmt (2x) + 58810: 1300, // UnlockTablesStmt (2x) + 58816: 1301, // UserToUser (2x) + 58831: 1302, // VariableAssignmentList (2x) + 58841: 1303, // WhenClause (2x) + 58846: 1304, // WindowDefinition (2x) + 58849: 1305, // WindowFrameBound (2x) + 58856: 1306, // WindowSpec (2x) + 58861: 1307, // WithGrantOptionOpt (2x) + 58862: 1308, // WithList (2x) + 58867: 1309, // Writeable (2x) + 58: 1310, // ':' (1x) + 58195: 1311, // AdminShowSlow (1x) + 58197: 1312, // AdminStmtLimitOpt (1x) + 58204: 1313, // AlterOrderList (1x) + 58209: 1314, // AlterSequenceOptionList (1x) + 58212: 1315, // AlterTableSpecList (1x) + 58213: 1316, // AlterTableSpecListOpt (1x) + 58214: 1317, // AlterTableSpecSingleOpt (1x) + 58218: 1318, // AnalyzeOptionList (1x) + 58221: 1319, // AnyOrAll (1x) + 58222: 1320, // ArrayKwdOpt (1x) + 58224: 1321, // AsOfClauseOpt (1x) + 58225: 1322, // AsOpt (1x) + 58230: 1323, // AuthOption (1x) + 58231: 1324, // AuthPlugin (1x) + 58233: 1325, // AutoRandomOpt (1x) + 58243: 1326, // BetweenOrNotOp (1x) + 58245: 1327, // BindingStatusType (1x) + 57374: 1328, // both (1x) + 58257: 1329, // CalibrateOption (1x) + 58259: 1330, // CalibrateResourceWorkloadOption (1x) + 58267: 1331, // CharsetNameOrDefault (1x) + 58268: 1332, // CharsetOpt (1x) + 58273: 1333, // ColumnFormat (1x) + 58275: 1334, // ColumnList (1x) + 58282: 1335, // ColumnNameOrUserVariableList (1x) + 58279: 1336, // ColumnNameOrUserVarListOpt (1x) + 58287: 1337, // ColumnSetValueList (1x) + 58292: 1338, // CompareOp (1x) + 58296: 1339, // ConnectionOptionList (1x) + 58299: 1340, // ConstraintElem (1x) + 57386: 1341, // continueKwd (1x) + 58308: 1342, // CreateSequenceOptionListOpt (1x) + 58312: 1343, // CreateTableSelectOpt (1x) + 58315: 1344, // CreateViewSelectOpt (1x) + 57396: 1345, // cursor (1x) + 58323: 1346, // DatabaseOptionListOpt (1x) + 58320: 1347, // DBNameList (1x) + 58331: 1348, // DefaultOrExpressionList (1x) + 58333: 1349, // DefaultValueExpr (1x) + 58359: 1350, // DryRunOptions (1x) + 57415: 1351, // dual (1x) + 58361: 1352, // DynamicCalibrateOptionList (1x) + 58364: 1353, // ElseOpt (1x) + 58369: 1354, // EnforcedOrNotOrNotNullOpt (1x) + 57421: 1355, // exit (1x) + 58382: 1356, // ExpressionOpt (1x) + 58384: 1357, // FetchFirstOpt (1x) + 58386: 1358, // FieldAsName (1x) + 58387: 1359, // FieldAsNameOpt (1x) + 58389: 1360, // FieldItemList (1x) + 58391: 1361, // FieldList (1x) + 58397: 1362, // FirstAndLastPartOpt (1x) + 58398: 1363, // FirstOrNext (1x) + 58406: 1364, // FlushOption (1x) + 58410: 1365, // FromDual (1x) + 58412: 1366, // FulltextSearchModifierOpt (1x) + 58413: 1367, // FuncDatetimePrec (1x) + 58426: 1368, // GetFormatSelector (1x) + 58433: 1369, // HandleRangeList (1x) + 58438: 1370, // IdentListWithParenOpt (1x) + 58442: 1371, // IgnoreLines (1x) + 58444: 1372, // IlikeOrNotOp (1x) + 58451: 1373, // IndexHintScope (1x) + 58454: 1374, // IndexKeyTypeOpt (1x) + 58463: 1375, // IndexPartSpecificationListOpt (1x) + 58466: 1376, // IndexTypeOpt (1x) + 58446: 1377, // InOrNotOp (1x) + 58469: 1378, // InstanceOption (1x) + 58472: 1379, // IntervalExpr (1x) + 58475: 1380, // IsolationLevel (1x) + 58474: 1381, // IsOrNotOp (1x) + 57472: 1382, // leading (1x) + 58484: 1383, // LikeOrNotOp (1x) + 58485: 1384, // LikeTableWithOrWithoutParen (1x) + 58490: 1385, // LinesTerminated (1x) + 58493: 1386, // LoadDataOptionList (1x) + 58496: 1387, // LoadDataSetList (1x) + 58505: 1388, // LockType (1x) + 58506: 1389, // LogTypeOpt (1x) + 58507: 1390, // Match (1x) + 58508: 1391, // MatchOpt (1x) + 58509: 1392, // MaxIndexNumOpt (1x) + 58510: 1393, // MaxMinutesOpt (1x) + 58511: 1394, // MaxValPartOpt (1x) + 58513: 1395, // MaxValueOrExpressionList (1x) + 58526: 1396, // NullPartOpt (1x) + 58534: 1397, // OnDeleteUpdateOpt (1x) + 58535: 1398, // OnDuplicateKeyUpdate (1x) + 58537: 1399, // OptBinMod (1x) + 58539: 1400, // OptCharset (1x) + 58542: 1401, // OptExistingWindowName (1x) + 58544: 1402, // OptFromFirstLast (1x) + 58546: 1403, // OptGConcatSeparator (1x) + 58562: 1404, // OptionalShardColumn (1x) + 58552: 1405, // OptPartitionClause (1x) + 58553: 1406, // OptSpPdparams (1x) + 58554: 1407, // OptTable (1x) + 58871: 1408, // optValue (1x) + 58557: 1409, // OptWindowFrameClause (1x) + 58558: 1410, // OptWindowOrderByClause (1x) + 58564: 1411, // Order (1x) + 58563: 1412, // OrReplace (1x) + 57455: 1413, // outfile (1x) + 58570: 1414, // PartDefValuesOpt (1x) + 58575: 1415, // PartitionKeyAlgorithmOpt (1x) + 58576: 1416, // PartitionMethod (1x) + 58579: 1417, // PartitionNumOpt (1x) + 58586: 1418, // PerDB (1x) + 58587: 1419, // PerTable (1x) + 58590: 1420, // PlanReplayerDumpOpt (1x) + 57514: 1421, // precisionType (1x) + 58596: 1422, // PrepareSQL (1x) + 58872: 1423, // procedurceElseIfs (1x) + 58607: 1424, // ProcedureCall (1x) + 58610: 1425, // ProcedureCursorSelectStmt (1x) + 58612: 1426, // ProcedureDeclIdents (1x) + 58613: 1427, // ProcedureDecls (1x) + 58614: 1428, // ProcedureDeclsOpt (1x) + 58616: 1429, // ProcedureFetchList (1x) + 58617: 1430, // ProcedureHandlerType (1x) + 58619: 1431, // ProcedureHcondList (1x) + 58626: 1432, // ProcedureOptDefault (1x) + 58627: 1433, // ProcedureOptFetchNo (1x) + 58630: 1434, // ProcedureProcStmts (1x) + 58639: 1435, // QueryWatchOptionList (1x) + 57521: 1436, // recursive (1x) + 58645: 1437, // RegexpOrNotOp (1x) + 58650: 1438, // ReorganizePartitionRuleOpt (1x) + 58655: 1439, // RequireList (1x) + 58657: 1440, // ResourceGroupBackgroundOptionList (1x) + 58661: 1441, // ResourceGroupPriorityOption (1x) + 58663: 1442, // ResourceGroupRunawayOptionList (1x) + 58674: 1443, // RoleSpecList (1x) + 58681: 1444, // RowOrRows (1x) + 58686: 1445, // SearchedWhenThenList (1x) + 58690: 1446, // SelectStmtFieldList (1x) + 58698: 1447, // SelectStmtOpts (1x) + 58699: 1448, // SelectStmtOptsList (1x) + 58703: 1449, // SequenceOptionList (1x) + 58708: 1450, // SetOpr (1x) + 58715: 1451, // SetRoleOpt (1x) + 58718: 1452, // ShardableStmt (1x) + 58720: 1453, // ShowIndexKwd (1x) + 58721: 1454, // ShowLikeOrWhereOpt (1x) + 58722: 1455, // ShowPlacementTarget (1x) + 58723: 1456, // ShowProfileArgsOpt (1x) + 58725: 1457, // ShowProfileTypes (1x) + 58726: 1458, // ShowProfileTypesOpt (1x) + 58729: 1459, // ShowTargetFilterable (1x) + 58736: 1460, // SimpleWhenThenList (1x) + 57541: 1461, // spatial (1x) + 58742: 1462, // SplitSyntaxOption (1x) + 58739: 1463, // SpPdparams (1x) + 57549: 1464, // ssl (1x) + 58743: 1465, // Start (1x) + 58744: 1466, // Starting (1x) + 57550: 1467, // starting (1x) + 58746: 1468, // StatementList (1x) + 58747: 1469, // StatementScope (1x) + 58751: 1470, // StorageMedia (1x) + 57556: 1471, // stored (1x) + 58752: 1472, // StringList (1x) + 58755: 1473, // StringNameOrBRIEOptionKeyword (1x) + 58758: 1474, // SubPartDefinitionList (1x) + 58759: 1475, // SubPartDefinitionListOpt (1x) + 58761: 1476, // SubPartitionNumOpt (1x) + 58762: 1477, // SubPartitionOpt (1x) + 58772: 1478, // TableElementListOpt (1x) + 58775: 1479, // TableLockList (1x) + 58788: 1480, // TableRefsClause (1x) + 58789: 1481, // TableSampleMethodOpt (1x) + 58790: 1482, // TableSampleOpt (1x) + 58791: 1483, // TableSampleUnitOpt (1x) + 58793: 1484, // TableToTableList (1x) + 57563: 1485, // trailing (1x) + 58805: 1486, // TrimDirection (1x) + 58817: 1487, // UserToUserList (1x) + 58819: 1488, // UserVariableList (1x) + 58822: 1489, // UsingRoles (1x) + 58824: 1490, // Values (1x) + 58826: 1491, // ValuesOpt (1x) + 58833: 1492, // ViewAlgorithm (1x) + 58834: 1493, // ViewCheckOption (1x) + 58835: 1494, // ViewDefiner (1x) + 58836: 1495, // ViewFieldList (1x) + 58837: 1496, // ViewName (1x) + 58838: 1497, // ViewSQLSecurity (1x) + 57584: 1498, // virtual (1x) + 58839: 1499, // VirtualOrStored (1x) + 58840: 1500, // WatchDurationOption (1x) + 58842: 1501, // WhenClauseList (1x) + 58845: 1502, // WindowClauseOptional (1x) + 58847: 1503, // WindowDefinitionList (1x) + 58848: 1504, // WindowFrameBetween (1x) + 58850: 1505, // WindowFrameExtent (1x) + 58852: 1506, // WindowFrameUnits (1x) + 58855: 1507, // WindowNameOrSpec (1x) + 58857: 1508, // WindowSpecDetails (1x) + 58863: 1509, // WithReadLockOpt (1x) + 58864: 1510, // WithRollupClause (1x) + 58865: 1511, // WithValidation (1x) + 58866: 1512, // WithValidationOpt (1x) + 58193: 1513, // $default (0x) + 58153: 1514, // andnot (0x) + 58228: 1515, // AssignmentListOpt (0x) + 58272: 1516, // ColumnDefList (0x) + 58288: 1517, // CommaOpt (0x) + 58177: 1518, // createTableSelect (0x) + 58167: 1519, // empty (0x) + 57345: 1520, // error (0x) + 58192: 1521, // higherThanComma (0x) + 58186: 1522, // higherThanParenthese (0x) + 58175: 1523, // insertValues (0x) + 57355: 1524, // invalid (0x) + 58178: 1525, // lowerThanCharsetKwd (0x) + 58191: 1526, // lowerThanComma (0x) + 58176: 1527, // lowerThanCreateTableSelect (0x) + 58188: 1528, // lowerThanEq (0x) + 58183: 1529, // lowerThanFunction (0x) + 58174: 1530, // lowerThanInsertValues (0x) + 58179: 1531, // lowerThanKey (0x) + 58180: 1532, // lowerThanLocal (0x) + 58190: 1533, // lowerThanNot (0x) + 58187: 1534, // lowerThanOn (0x) + 58185: 1535, // lowerThanParenthese (0x) + 58181: 1536, // lowerThanRemove (0x) + 58168: 1537, // lowerThanSelectOpt (0x) + 58173: 1538, // lowerThanSelectStmt (0x) + 58172: 1539, // lowerThanSetKeyword (0x) + 58171: 1540, // lowerThanStringLitToken (0x) + 58169: 1541, // lowerThanValueKeyword (0x) + 58170: 1542, // lowerThanWith (0x) + 58182: 1543, // lowerThenOrder (0x) + 58189: 1544, // neg (0x) + 57359: 1545, // odbcDateType (0x) + 57361: 1546, // odbcTimestampType (0x) + 57360: 1547, // odbcTimeType (0x) + 58779: 1548, // TableNameListOpt2 (0x) + 58184: 1549, // tableRefPriority (0x) + } + + yySymNames = []string{ + "';'", + "$end", + "remove", + "split", + "merge", + "reorganize", + "comment", + "storage", + "autoIncrement", + "','", + "first", + "after", + "serial", + "autoRandom", + "columnFormat", + "password", + "charsetKwd", + "checksum", + "placement", + "keyBlockSize", + "tablespace", + "encryption", + "data", + "engine", + "insertMethod", + "maxRows", + "minRows", + "nodegroup", + "connection", + "autoRandomBase", + "statsBuckets", + "statsTopN", + "ttl", + "autoIdCache", + "avgRowLength", + "compression", + "delayKeyWrite", + "packKeys", + "preSplitRegions", + "rowFormat", + "secondaryEngine", + "shardRowIDBits", + "statsAutoRecalc", + "statsColChoice", + "statsColList", + "statsPersistent", + "statsSamplePages", + "statsSampleRate", + "tableChecksum", + "ttlEnable", + "ttlJobInterval", + "resource", + "attribute", + "account", + "failedLoginAttempts", + "passwordLockTime", + "identifier", + "')'", + "resume", + "signed", + "snapshot", + "backend", + "checkpoint", + "concurrency", + "csvBackslashEscape", + "csvDelimiter", + "csvHeader", + "csvNotNull", + "csvNull", + "csvSeparator", + "csvTrimLastSeparators", + "fullBackupStorage", + "gcTTL", + "lastBackup", + "onDuplicate", + "online", + "rateLimit", + "restoredTS", + "sendCredentialsToTiKV", + "skipSchemaFiles", + "startTS", + "strictFormat", + "tikvImporter", + "untilTS", + "begin", + "commit", + "no", + "rollback", + "start", + "truncate", + "cache", + "nocache", + "open", + "action", + "close", + "cycle", + "minValue", + "end", + "increment", + "nocycle", + "nomaxvalue", + "nominvalue", + "algorithm", + "restart", + "tp", + "clustered", + "invisible", + "nonclustered", + "regions", + "visible", + "background", + "burstable", + "priority", + "queryLimit", + "ruRate", + "subpartition", + "partitions", + "plan", + "yearType", + "constraints", + "followerConstraints", + "followers", + "leaderConstraints", + "learnerConstraints", + "learners", + "primaryRegion", + "schedule", + "sqlTsiYear", + "survivalPreferences", + "voterConstraints", + "voters", + "columns", + "view", + "day", + "watch", + "defined", + "execElapsed", + "second", + "hour", + "microsecond", + "minute", + "month", + "quarter", + "sqlTsiDay", + "sqlTsiHour", + "sqlTsiMinute", + "sqlTsiMonth", + "sqlTsiQuarter", + "sqlTsiSecond", + "sqlTsiWeek", + "week", + "ascii", + "byteType", + "unicodeSym", + "fields", + "logs", + "status", + "tables", + "timeDuration", + "query", + "separator", + "cipher", + "issuer", + "maxConnectionsPerHour", + "maxQueriesPerHour", + "maxUpdatesPerHour", + "maxUserConnections", + "preceding", + "san", + "subject", + "tokenIssuer", + "endTime", + "jsonType", + "local", + "startTime", + "datetimeType", + "dateType", + "fixed", + "job", + "timeType", + "bindings", + "definer", + "hash", + "identified", + "respect", + "timestampType", + "value", + "backup", + "booleanType", + "current", + "enforced", + "following", + "less", + "nowait", + "only", + "savepoint", + "skip", + "taskTypes", + "textType", + "than", + "tiFlash", + "unbounded", + "binding", + "bitType", + "boolType", + "enum", + "global", + "hypo", + "importKwd", + "national", + "ncharType", + "next_row_id", + "nvarcharType", + "offset", + "policy", + "predicate", + "temporary", + "user", + "digest", + "jobs", + "location", + "planCache", + "prepare", + "replica", + "role", + "stats", + "unknown", + "wait", + "btree", + "cooldown", + "declare", + "dryRun", + "format", + "isolation", + "last", + "max_idxnum", + "memory", + "off", + "optional", + "per_db", + "privileges", + "required", + "rtree", + "sampleRate", + "sequence", + "session", + "slow", + "validation", + "variables", + "attributes", + "cancel", + "compact", + "ddl", + "disable", + "do", + "dynamic", + "enable", + "errorKwd", + "exact", + "flush", + "full", + "handler", + "history", + "mb", + "mode", + "next", + "pause", + "plugins", + "processlist", + "recover", + "repair", + "repeatable", + "similar", + "statistics", + "subpartitions", + "tidb", + "without", + "admin", + "batch", + "binlog", + "block", + "br", + "briefType", + "buckets", + "calibrate", + "capture", + "cardinality", + "chain", + "clientErrorsSummary", + "cmSketch", + "coalesce", + "compressed", + "context", + "copyKwd", + "correlation", + "cpu", + "deallocate", + "dependency", + "directory", + "discard", + "disk", + "dotType", + "drainer", + "dry", + "duplicate", + "exchange", + "execute", + "expansion", + "flashback", + "general", + "help", + "high", + "histogram", + "hosts", + "identSQLErrors", + "inplace", + "instance", + "instant", + "ipc", + "labels", + "locked", + "low", + "medium", + "metadata", + "modify", + "nodeID", + "nodeState", + "nulls", + "pageSym", + "pump", + "purge", + "rebuild", + "redundant", + "reload", + "restore", + "routine", + "s3", + "samples", + "secondaryLoad", + "secondaryUnload", + "share", + "shutdown", + "source", + "statsOptions", + "stop", + "swaps", + "tidbJson", + "tokudbDefault", + "tokudbFast", + "tokudbLzma", + "tokudbQuickLZ", + "tokudbSmall", + "tokudbSnappy", + "tokudbUncompressed", + "tokudbZlib", + "tokudbZstd", + "topn", + "trace", + "traditional", + "trueCardCost", + "unlimited", + "verboseType", + "warnings", + "advise", + "against", + "ago", + "always", + "backups", + "bernoulli", + "bindingCache", + "builtins", + "cascaded", + "causal", + "cleanup", + "client", + "cluster", + "collation", + "columnStatsUsage", + "committed", + "config", + "consistency", + "consistent", + "depth", + "disabled", + "dump", + "enabled", + "engines", + "events", + "evolve", + "expire", + "exprPushdownBlacklist", + "extended", + "faultsSym", + "found", + "function", + "grants", + "histogramsInFlight", + "incremental", + "indexes", + "internal", + "invoker", + "io", + "language", + "level", + "list", + "master", + "max_minutes", + "never", + "nextval", + "none", + "oltpReadOnly", + "oltpReadWrite", + "oltpWriteOnly", + "optimistic", + "optRuleBlacklist", + "parser", + "partial", + "partitioning", + "per_table", + "percent", + "pessimistic", + "point", + "preserve", + "profile", + "profiles", + "queries", + "recent", + "region", + "replayer", + "reset", + "restores", + "reuse", + "rollup", + "run", + "security", + "serializable", + "sessionStates", + "simple", + "slave", + "statsHealthy", + "statsHistograms", + "statsLocked", + "statsMeta", + "switchesSym", + "system", + "systemTime", + "target", + "telemetryID", + "temptable", + "tls", + "top", + "tpcc", + "tpch10", + "transaction", + "triggers", + "uncommitted", + "undefined", + "width", + "workload", + "x509", + "addDate", + "any", + "approxCountDistinct", + "approxPercentile", + "avg", + "bitAnd", + "bitOr", + "bitXor", + "bound", + "cast", + "curDate", + "curTime", + "dateAdd", + "dateSub", + "escape", + "event", + "exclusive", + "extract", + "file", + "follower", + "getFormat", + "groupConcat", + "imports", + "ioReadBandwidth", + "ioWriteBandwidth", + "jsonArrayagg", + "jsonObjectAgg", + "lastval", + "leader", + "learner", + "max", + "member", + "min", + "names", + "now", + "position", + "process", + "proxy", + "quick", + "replicas", + "replication", + "reverse", + "rowCount", + "running", + "setval", + "shared", + "some", + "sqlBufferResult", + "sqlCache", + "sqlNoCache", + "staleness", + "std", + "stddev", + "stddevPop", + "stddevSamp", + "strict", + "strong", + "subDate", + "substring", + "sum", + "super", + "telemetry", + "timestampAdd", + "timestampDiff", + "trim", + "variance", + "varPop", + "varSamp", + "voter", + "weightString", + "on", + "'('", + "with", + "stringLit", + "not2", + "defaultKwd", + "not", + "as", + "collate", + "union", + "left", + "right", + "using", + "'+'", + "'-'", + "mod", + "partition", + "values", + "null", + "ignore", + "except", + "intersect", + "replace", + "charType", + "fetch", + "eq", + "limit", + "set", + "forKwd", + "into", + "intLit", + "from", + "lock", + "where", + "order", + "force", + "and", + "or", + "andand", + "pipesAsOr", + "xor", + "group", + "having", + "straightJoin", + "window", + "use", + "join", + "desc", + "ifKwd", + "like", + "natural", + "cross", + "explain", + "inner", + "'*'", + "'}'", + "binaryType", + "insert", + "rows", + "when", + "elseKwd", + "rangeKwd", + "tableSample", + "groups", + "dayHour", + "dayMicrosecond", + "dayMinute", + "daySecond", + "hourMicrosecond", + "hourMinute", + "hourSecond", + "minuteMicrosecond", + "minuteSecond", + "secondMicrosecond", + "yearMonth", + "asc", + "in", + "then", + "tableKwd", + "'/'", + "'%'", + "'&'", + "'^'", + "'|'", + "caseKwd", + "div", + "lsh", + "repeat", + "rsh", + "'<'", + "'>'", + "ge", + "is", + "le", + "neq", + "neqSynonym", + "nulleq", + "between", + "singleAtIdentifier", + "falseKwd", + "trueKwd", + "currentUser", + "ilike", + "regexpKwd", + "rlike", + "memberof", + "decLit", + "floatLit", + "hexLit", + "row", + "bitLit", + "interval", + "paramMarker", + "'{'", + "database", + "exists", + "convert", + "underscoreCS", + "builtinCurDate", + "builtinNow", + "currentDate", + "currentTs", + "doubleAtIdentifier", + "localTime", + "localTs", + "builtinCount", + "'!'", + "'~'", + "builtinApproxCountDistinct", + "builtinApproxPercentile", + "builtinBitAnd", + "builtinBitOr", + "builtinBitXor", + "builtinCast", + "builtinCurTime", + "builtinDateAdd", + "builtinDateSub", + "builtinExtract", + "builtinGroupConcat", + "builtinMax", + "builtinMin", + "builtinPosition", + "builtinStddevPop", + "builtinStddevSamp", + "builtinSubstring", + "builtinSum", + "builtinSysDate", + "builtinTranslate", + "builtinTrim", + "builtinUser", + "builtinVarPop", + "builtinVarSamp", + "cumeDist", + "currentRole", + "currentTime", + "denseRank", + "firstValue", + "lag", + "lastValue", + "lead", + "nthValue", + "ntile", + "percentRank", + "rank", + "rowNumber", + "selectKwd", + "sql", + "tidbCurrentTSO", + "utcDate", + "utcTime", + "utcTimestamp", + "key", + "check", + "pipes", + "primary", + "unique", + "constraint", + "references", + "generated", + "character", + "index", + "match", + "to", + "analyze", + "update", + "all", + "'.'", + "assignmentEq", + "jss", + "juss", + "maxValue", + "array", + "lines", + "by", + "alter", + "require", + "'@'", + "drop", + "cascade", + "read", + "restrict", + "asof", + "varcharacter", + "varcharType", + "decimalType", + "doubleType", + "floatType", + "integerType", + "intType", + "realType", + "varbinaryType", + "bigIntType", + "blobType", + "create", + "float4Type", + "float8Type", + "foreign", + "fulltext", + "int1Type", + "int2Type", + "int3Type", + "int4Type", + "int8Type", + "long", + "longblobType", + "longtextType", + "mediumblobType", + "mediumIntType", + "mediumtextType", + "middleIntType", + "numericType", + "smallIntType", + "tinyblobType", + "tinyIntType", + "tinytextType", + "toTimestamp", + "change", + "rename", + "write", + "add", + "Identifier", + "NotKeywordToken", + "TiDBKeyword", + "UnReservedKeyword", + "optimize", + "SubSelect", + "UserVariable", + "Literal", + "SimpleIdent", + "StringLiteral", + "NextValueForSequence", + "FunctionCallGeneric", + "FunctionCallKeyword", + "FunctionCallNonKeyword", + "FunctionNameConflict", + "FunctionNameDateArith", + "FunctionNameDateArithMultiForms", + "FunctionNameDatetimePrecision", + "FunctionNameOptionalBraces", + "FunctionNameSequence", + "SimpleExpr", + "SumExpr", + "SystemVariable", + "Variable", + "WindowFuncCall", + "BitExpr", + "PredicateExpr", + "BoolPri", + "Expression", + "NUM", + "logAnd", + "logOr", + "EqOpt", + "deleteKwd", + "TableName", + "StringName", + "SelectStmt", + "SelectStmtBasic", + "SelectStmtFromDualTable", + "SelectStmtFromTable", + "SetOprClause", + "SetOprClauseList", + "SetOprStmtWithLimitOrderBy", + "SetOprStmtWoutLimitOrderBy", + "unsigned", + "WithClause", + "LengthNum", + "SelectStmtWithClause", + "SetOprStmt", + "zerofill", + "over", + "ColumnName", + "UpdateStmtNoWith", + "DeleteWithoutUsingStmt", + "InsertIntoStmt", + "Int64Num", + "ReplaceIntoStmt", + "UpdateStmt", + "describe", + "distinct", + "distinctRow", + "while", + "WindowingClause", + "DeleteWithUsingStmt", + "iterate", + "leave", + "delayed", + "highPriority", + "lowPriority", + "DeleteFromStmt", + "hintComment", + "FieldLen", + "OrderBy", + "SelectStmtLimit", + "OptWindowingClause", + "AnalyzeTableStmt", + "CommitStmt", + "RollbackStmt", + "SetStmt", + "sqlBigResult", + "sqlCalcFoundRows", + "sqlSmallResult", + "terminated", + "CharsetKw", + "IfExists", + "Username", + "enclosed", + "ExplainStmt", + "ExplainSym", + "PartitionNameList", + "TruncateTableStmt", + "UseStmt", + "escaped", + "ExpressionList", + "optionallyEnclosedBy", + "PlacementPolicyOption", + "ProcedureBlockContent", + "ProcedureUnlabelLoopStmt", + "ProcedureCaseStmt", + "ProcedureCloseCur", + "ProcedureFetchInto", + "ProcedureIfstmt", + "ProcedureIterate", + "ProcedureLabeledBlock", + "ProcedurelabeledLoopStmt", + "ProcedureLeave", + "ProcedureOpenCur", + "ProcedureProcStmt", + "ProcedureSearchedCase", + "ProcedureSimpleCase", + "ProcedureStatementStmt", + "ProcedureUnlabeledBlock", + "ProcedureUnlabelLoopBlock", + "IfNotExists", + "TableNameList", + "DistinctKwd", + "TimestampUnit", + "DistinctOpt", + "OptFieldLen", + "WhereClause", + "WhereClauseOptional", + "DefaultKwdOpt", + "EqOrAssignmentEq", + "ExprOrDefault", + "load", + "JoinTable", + "OptBinary", + "release", + "RolenameComposed", + "TableFactor", + "TableRef", + "TimeUnit", + "AnalyzeOptionListOpt", + "FromOrIn", + "AlterTableStmt", + "CharsetName", + "ColumnNameList", + "DBName", + "noWriteToBinLog", + "OrderByOptional", + "PartDefOption", + "SignedNum", + "BuggyDefaultFalseDistinctOpt", + "DefaultFalseDistinctOpt", + "JoinType", + "NotSym", + "NumLiteral", + "Rolename", + "RoleNameString", + "CrossOpt", + "ExplainableStmt", + "ExpressionListOpt", + "IndexPartSpecification", + "KeyOrIndex", + "NoWriteToBinLogAliasOpt", + "SelectStmtLimitOpt", + "VariableName", + "AllOrPartitionNameList", + "ConstraintKeywordOpt", + "DatabaseSym", + "FieldsOrColumns", + "ForceOpt", + "IndexPartSpecificationList", + "kill", + "Priority", + "ProcedureProcStmt1s", + "ResourceGroupName", + "RowFormat", + "RowValue", + "SetExpr", + "ShowDatabaseNameOpt", + "TableOption", + "varying", + "BeginTransactionStmt", + "BindableStmt", + "BRIEBooleanOptionName", + "BRIEIntegerOptionName", + "BRIEKeywordOptionName", + "BRIEOption", + "BRIEOptions", + "BRIEStringOptionName", + "Char", + "column", + "ColumnDef", + "DatabaseOption", + "EscapedTableRef", + "FieldTerminator", + "grant", + "IgnoreOptional", + "IndexInvisible", + "IndexNameList", + "IndexType", + "LoadDataStmt", + "PartitionNameListOpt", + "procedure", + "ReleaseSavepointStmt", + "RolenameList", + "SavepointStmt", + "show", + "TableOptimizerHints", + "UsernameList", + "WithClustered", + "AlgorithmClause", + "ByItem", + "CollationName", + "ColumnKeywordOpt", + "DirectPlacementOption", + "DirectResourceGroupOption", + "FieldOpt", + "FieldOpts", + "IdentList", + "IndexName", + "IndexOption", + "IndexOptionList", + "infile", + "LimitOption", + "LockClause", + "OptCharsetWithOptBinary", + "OptNullTreatment", + "PolicyName", + "PriorityOpt", + "SelectLockOpt", + "SelectStmtIntoOption", + "TableRefs", + "UserSpec", + "AsOfClause", + "Assignment", + "AuthString", + "Boolean", + "BuiltinFunction", + "ByList", + "ConfigItemName", + "Constraint", + "FloatOpt", + "IndexTypeName", + "NumList", + "option", + "optionally", + "OptWild", + "outer", + "Precision", + "ReferDef", + "RestrictOrCascadeOpt", + "RowStmt", + "SequenceOption", + "statsExtended", + "TableAsName", + "TableAsNameOpt", + "TableNameOptWild", + "TableOptimizerHintsOpt", + "TableOptionList", + "TextString", + "TraceableStmt", + "TransactionChar", + "UserSpecList", + "Varchar", + "WindowName", + "AssignmentList", + "AttributesOpt", + "BitValueType", + "BlobType", + "BooleanType", + "ColumnOption", + "ColumnPosition", + "CommonTableExpr", + "CreateTableStmt", + "CurdateSym", + "DatabaseOptionList", + "DateAndTimeType", + "DefaultTrueDistinctOpt", + "DirectResourceGroupBackgroundOption", + "DirectResourceGroupRunawayOption", + "DynamicCalibrateResourceOption", + "elseIfKwd", + "EnforcedOrNot", + "ExtendedPriv", + "FixedPointType", + "FloatingPointType", + "GeneratedAlways", + "GlobalScope", + "GroupByClause", + "IndexHint", + "IndexHintType", + "IndexNameAndTypeOpt", + "IntegerType", + "keys", + "Lines", + "LocationLabelList", + "NChar", + "NowSym", + "NowSymFunc", + "NowSymOptionFraction", + "NumericType", + "NVarchar", + "OptOrder", + "OptTemporary", + "PartDefOptionList", + "PartitionDefinition", + "PasswordOrLockOption", + "PluginNameList", + "PrimaryOpt", + "PrivElem", + "PrivType", + "QueryWatchOption", + "QueryWatchTextOption", + "RequireClause", + "RequireClauseOpt", + "RequireListElement", + "RolenameWithoutIdent", + "RoleOrPrivElem", + "SelectStmtGroup", + "SetOprOpt", + "SignedLiteral", + "StringType", + "TableAliasRefList", + "TableElement", + "TextType", + "TransactionChars", + "trigger", + "Type", + "unlock", + "until", + "usage", + "ValuesList", + "ValuesStmtList", + "ValueSym", + "VariableAssignment", + "WindowFrameStart", + "Year", + "AddQueryWatchStmt", + "AdminStmt", + "AllColumnsOrPredicateColumnsOpt", + "AlterDatabaseStmt", + "AlterInstanceStmt", + "AlterOrderItem", + "AlterPolicyStmt", + "AlterRangeStmt", + "AlterResourceGroupStmt", + "AlterSequenceOption", + "AlterSequenceStmt", + "AlterTableSpec", + "AlterUserStmt", + "AnalyzeOption", + "BinlogStmt", + "BRIEStmt", + "BRIETables", + "CalibrateResourceStmt", + "call", + "CallStmt", + "CancelImportStmt", + "CastType", + "ChangeStmt", + "CheckConstraintKeyword", + "ColumnNameListOpt", + "ColumnNameOrUserVariable", + "ColumnNameOrUserVarListOptWithBrackets", + "ColumnOptionList", + "ColumnOptionListOpt", + "CommentOrAttributeOption", + "CompletionTypeWithinTransaction", + "ConnectionOption", + "ConnectionOptions", + "CreateBindingStmt", + "CreateDatabaseStmt", + "CreateIndexStmt", + "CreatePolicyStmt", + "CreateProcedureStmt", + "CreateResourceGroupStmt", + "CreateRoleStmt", + "CreateSequenceStmt", + "CreateStatisticsStmt", + "CreateTableOptionListOpt", + "CreateUserStmt", + "CreateViewStmt", + "databases", + "DeallocateStmt", + "DeallocateSym", + "DefaultOrExpression", + "DoStmt", + "DropBindingStmt", + "DropDatabaseStmt", + "DropIndexStmt", + "DropLoadDataStmt", + "DropPolicyStmt", + "DropProcedureStmt", + "DropQueryWatchStmt", + "DropResourceGroupStmt", + "DropRoleStmt", + "DropSequenceStmt", + "DropStatisticsStmt", + "DropStatsStmt", + "DropTableStmt", + "DropUserStmt", + "DropViewStmt", + "DuplicateOpt", + "ElseCaseOpt", + "EmptyStmt", + "EncryptionOpt", + "EnforcedOrNotOpt", + "ExecuteStmt", + "ExplainFormatType", + "Field", + "FieldItem", + "Fields", + "FlashbackDatabaseStmt", + "FlashbackTableStmt", + "FlashbackToNewName", + "FlashbackToTimestampStmt", + "FlushStmt", + "FormatOpt", + "FuncDatetimePrecList", + "FuncDatetimePrecListOpt", + "GrantProxyStmt", + "GrantRoleStmt", + "GrantStmt", + "HandleRange", + "HashString", + "HavingClause", + "HelpStmt", + "ImportIntoStmt", + "IndexAdviseStmt", + "IndexHintList", + "IndexHintListOpt", + "IndexLockAndAlgorithmOpt", + "inout", + "InsertValues", + "IntoOpt", + "KeyOrIndexOpt", + "KillOrKillTiDB", + "KillStmt", + "LikeOrIlikeEscapeOpt", + "LimitClause", + "linear", + "LinearOpt", + "LoadDataOption", + "LoadDataOptionListOpt", + "LoadDataSetItem", + "LoadDataSetSpecOpt", + "LoadStatsStmt", + "LocalOpt", + "LockStatsStmt", + "LockTablesStmt", + "MaxValueOrExpression", + "NonTransactionalDMLStmt", + "NowSymOptionFractionParentheses", + "ObjectType", + "of", + "OfTablesOpt", + "OnCommitOpt", + "OnDelete", + "OnUpdate", + "OptCollate", + "OptFull", + "OptInteger", + "OptionalBraces", + "OptionLevel", + "OptLeadLagInfo", + "OptLLDefault", + "out", + "OuterOpt", + "PartitionDefinitionList", + "PartitionDefinitionListOpt", + "PartitionIntervalOpt", + "PartitionOpt", + "PasswordOpt", + "PasswordOrLockOptionList", + "PasswordOrLockOptions", + "PauseLoadDataStmt", + "PlacementOptionList", + "PlanReplayerStmt", + "PreparedStmt", + "PrivLevel", + "ProcedurceCond", + "ProcedurceLabelOpt", + "ProcedureDecl", + "ProcedureHcond", + "ProcedureIf", + "QuickOptional", + "RecoverTableStmt", + "ReferOpt", + "RegexpSym", + "RenameTableStmt", + "RenameUserStmt", + "RepeatableOpt", + "ResourceGroupNameOption", + "ResourceGroupOptionList", + "ResourceGroupRunawayActionOption", + "ResourceGroupRunawayWatchOption", + "RestartStmt", + "ResumeLoadDataStmt", + "revoke", + "RevokeRoleStmt", + "RevokeStmt", + "RoleOrPrivElemList", + "RoleSpec", + "SearchWhenThen", + "SelectStmtOpt", + "SelectStmtSQLCache", + "SetBindingStmt", + "SetDefaultRoleOpt", + "SetDefaultRoleStmt", + "SetRoleStmt", + "ShowProfileType", + "ShowStmt", + "ShowTableAliasOpt", + "ShutdownStmt", + "SimpleWhenThen", + "SplitOption", + "SplitRegionStmt", + "SpOptInout", + "SpPdparam", + "sqlexception", + "sqlstate", + "sqlwarning", + "Statement", + "StatsOptionsOpt", + "StatsPersistentVal", + "StatsType", + "SubPartDefinition", + "SubPartitionMethod", + "Symbol", + "TableElementList", + "TableLock", + "TableNameListOpt", + "TableOrTables", + "TablesTerminalSym", + "TableToTable", + "TextStringList", + "TraceStmt", + "UnlockStatsStmt", + "UnlockTablesStmt", + "UserToUser", + "VariableAssignmentList", + "WhenClause", + "WindowDefinition", + "WindowFrameBound", + "WindowSpec", + "WithGrantOptionOpt", + "WithList", + "Writeable", + "':'", + "AdminShowSlow", + "AdminStmtLimitOpt", + "AlterOrderList", + "AlterSequenceOptionList", + "AlterTableSpecList", + "AlterTableSpecListOpt", + "AlterTableSpecSingleOpt", + "AnalyzeOptionList", + "AnyOrAll", + "ArrayKwdOpt", + "AsOfClauseOpt", + "AsOpt", + "AuthOption", + "AuthPlugin", + "AutoRandomOpt", + "BetweenOrNotOp", + "BindingStatusType", + "both", + "CalibrateOption", + "CalibrateResourceWorkloadOption", + "CharsetNameOrDefault", + "CharsetOpt", + "ColumnFormat", + "ColumnList", + "ColumnNameOrUserVariableList", + "ColumnNameOrUserVarListOpt", + "ColumnSetValueList", + "CompareOp", + "ConnectionOptionList", + "ConstraintElem", + "continueKwd", + "CreateSequenceOptionListOpt", + "CreateTableSelectOpt", + "CreateViewSelectOpt", + "cursor", + "DatabaseOptionListOpt", + "DBNameList", + "DefaultOrExpressionList", + "DefaultValueExpr", + "DryRunOptions", + "dual", + "DynamicCalibrateOptionList", + "ElseOpt", + "EnforcedOrNotOrNotNullOpt", + "exit", + "ExpressionOpt", + "FetchFirstOpt", + "FieldAsName", + "FieldAsNameOpt", + "FieldItemList", + "FieldList", + "FirstAndLastPartOpt", + "FirstOrNext", + "FlushOption", + "FromDual", + "FulltextSearchModifierOpt", + "FuncDatetimePrec", + "GetFormatSelector", + "HandleRangeList", + "IdentListWithParenOpt", + "IgnoreLines", + "IlikeOrNotOp", + "IndexHintScope", + "IndexKeyTypeOpt", + "IndexPartSpecificationListOpt", + "IndexTypeOpt", + "InOrNotOp", + "InstanceOption", + "IntervalExpr", + "IsolationLevel", + "IsOrNotOp", + "leading", + "LikeOrNotOp", + "LikeTableWithOrWithoutParen", + "LinesTerminated", + "LoadDataOptionList", + "LoadDataSetList", + "LockType", + "LogTypeOpt", + "Match", + "MatchOpt", + "MaxIndexNumOpt", + "MaxMinutesOpt", + "MaxValPartOpt", + "MaxValueOrExpressionList", + "NullPartOpt", + "OnDeleteUpdateOpt", + "OnDuplicateKeyUpdate", + "OptBinMod", + "OptCharset", + "OptExistingWindowName", + "OptFromFirstLast", + "OptGConcatSeparator", + "OptionalShardColumn", + "OptPartitionClause", + "OptSpPdparams", + "OptTable", + "optValue", + "OptWindowFrameClause", + "OptWindowOrderByClause", + "Order", + "OrReplace", + "outfile", + "PartDefValuesOpt", + "PartitionKeyAlgorithmOpt", + "PartitionMethod", + "PartitionNumOpt", + "PerDB", + "PerTable", + "PlanReplayerDumpOpt", + "precisionType", + "PrepareSQL", + "procedurceElseIfs", + "ProcedureCall", + "ProcedureCursorSelectStmt", + "ProcedureDeclIdents", + "ProcedureDecls", + "ProcedureDeclsOpt", + "ProcedureFetchList", + "ProcedureHandlerType", + "ProcedureHcondList", + "ProcedureOptDefault", + "ProcedureOptFetchNo", + "ProcedureProcStmts", + "QueryWatchOptionList", + "recursive", + "RegexpOrNotOp", + "ReorganizePartitionRuleOpt", + "RequireList", + "ResourceGroupBackgroundOptionList", + "ResourceGroupPriorityOption", + "ResourceGroupRunawayOptionList", + "RoleSpecList", + "RowOrRows", + "SearchedWhenThenList", + "SelectStmtFieldList", + "SelectStmtOpts", + "SelectStmtOptsList", + "SequenceOptionList", + "SetOpr", + "SetRoleOpt", + "ShardableStmt", + "ShowIndexKwd", + "ShowLikeOrWhereOpt", + "ShowPlacementTarget", + "ShowProfileArgsOpt", + "ShowProfileTypes", + "ShowProfileTypesOpt", + "ShowTargetFilterable", + "SimpleWhenThenList", + "spatial", + "SplitSyntaxOption", + "SpPdparams", + "ssl", + "Start", + "Starting", + "starting", + "StatementList", + "StatementScope", + "StorageMedia", + "stored", + "StringList", + "StringNameOrBRIEOptionKeyword", + "SubPartDefinitionList", + "SubPartDefinitionListOpt", + "SubPartitionNumOpt", + "SubPartitionOpt", + "TableElementListOpt", + "TableLockList", + "TableRefsClause", + "TableSampleMethodOpt", + "TableSampleOpt", + "TableSampleUnitOpt", + "TableToTableList", + "trailing", + "TrimDirection", + "UserToUserList", + "UserVariableList", + "UsingRoles", + "Values", + "ValuesOpt", + "ViewAlgorithm", + "ViewCheckOption", + "ViewDefiner", + "ViewFieldList", + "ViewName", + "ViewSQLSecurity", + "virtual", + "VirtualOrStored", + "WatchDurationOption", + "WhenClauseList", + "WindowClauseOptional", + "WindowDefinitionList", + "WindowFrameBetween", + "WindowFrameExtent", + "WindowFrameUnits", + "WindowNameOrSpec", + "WindowSpecDetails", + "WithReadLockOpt", + "WithRollupClause", + "WithValidation", + "WithValidationOpt", + "$default", + "andnot", + "AssignmentListOpt", + "ColumnDefList", + "CommaOpt", + "createTableSelect", + "empty", + "error", + "higherThanComma", + "higherThanParenthese", + "insertValues", + "invalid", + "lowerThanCharsetKwd", + "lowerThanComma", + "lowerThanCreateTableSelect", + "lowerThanEq", + "lowerThanFunction", + "lowerThanInsertValues", + "lowerThanKey", + "lowerThanLocal", + "lowerThanNot", + "lowerThanOn", + "lowerThanParenthese", + "lowerThanRemove", + "lowerThanSelectOpt", + "lowerThanSelectStmt", + "lowerThanSetKeyword", + "lowerThanStringLitToken", + "lowerThanValueKeyword", + "lowerThanWith", + "lowerThenOrder", + "neg", + "odbcDateType", + "odbcTimestampType", + "odbcTimeType", + "TableNameListOpt2", + "tableRefPriority", + } + + yyReductions = []struct{ xsym, components int }{ + {0, 1}, + {1465, 1}, + {904, 6}, + {904, 8}, + {904, 10}, + {904, 5}, + {904, 7}, + {904, 7}, + {904, 9}, + {1255, 1}, + {1255, 2}, + {1255, 3}, + {1441, 1}, + {1441, 1}, + {1441, 1}, + {1442, 1}, + {1442, 2}, + {1442, 3}, + {1257, 1}, + {1257, 1}, + {1257, 1}, + {1256, 1}, + {1256, 1}, + {1256, 1}, + {1041, 3}, + {1041, 3}, + {1041, 4}, + {1500, 0}, + {1500, 3}, + {1500, 3}, + {977, 3}, + {977, 3}, + {977, 1}, + {977, 3}, + {977, 5}, + {977, 4}, + {977, 3}, + {977, 5}, + {977, 4}, + {977, 3}, + {1440, 1}, + {1440, 2}, + {1440, 3}, + {1040, 3}, + {1238, 1}, + {1238, 2}, + {1238, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {976, 3}, + {865, 4}, + {865, 4}, + {865, 4}, + {865, 4}, + {1028, 3}, + {1028, 3}, + {1285, 3}, + {1285, 3}, + {1317, 1}, + {1317, 2}, + {1317, 4}, + {1317, 8}, + {1317, 8}, + {1317, 3}, + {1317, 3}, + {1317, 2}, + {1057, 0}, + {1057, 3}, + {1110, 1}, + {1110, 5}, + {1110, 6}, + {1110, 5}, + {1110, 5}, + {1110, 5}, + {1110, 6}, + {1110, 2}, + {1110, 5}, + {1110, 6}, + {1110, 8}, + {1110, 8}, + {1110, 1}, + {1110, 1}, + {1110, 3}, + {1110, 4}, + {1110, 5}, + {1110, 3}, + {1110, 4}, + {1110, 8}, + {1110, 4}, + {1110, 7}, + {1110, 3}, + {1110, 4}, + {1110, 4}, + {1110, 4}, + {1110, 4}, + {1110, 2}, + {1110, 2}, + {1110, 4}, + {1110, 4}, + {1110, 5}, + {1110, 3}, + {1110, 2}, + {1110, 2}, + {1110, 5}, + {1110, 6}, + {1110, 6}, + {1110, 8}, + {1110, 5}, + {1110, 5}, + {1110, 3}, + {1110, 3}, + {1110, 3}, + {1110, 5}, + {1110, 1}, + {1110, 1}, + {1110, 1}, + {1110, 1}, + {1110, 2}, + {1110, 2}, + {1110, 1}, + {1110, 1}, + {1110, 4}, + {1110, 3}, + {1110, 4}, + {1110, 1}, + {1110, 1}, + {1438, 0}, + {1438, 5}, + {927, 1}, + {927, 1}, + {1512, 0}, + {1512, 1}, + {1511, 2}, + {1511, 2}, + {971, 1}, + {971, 1}, + {972, 3}, + {972, 3}, + {972, 3}, + {972, 3}, + {972, 3}, + {986, 3}, + {986, 3}, + {1309, 2}, + {1309, 2}, + {923, 1}, + {923, 1}, + {1197, 0}, + {1197, 1}, + {975, 0}, + {975, 1}, + {1033, 0}, + {1033, 1}, + {1033, 2}, + {1316, 0}, + {1316, 1}, + {1315, 1}, + {1315, 3}, + {859, 1}, + {859, 3}, + {928, 0}, + {928, 1}, + {928, 2}, + {1290, 1}, + {1251, 3}, + {1484, 1}, + {1484, 3}, + {1296, 3}, + {1252, 3}, + {1487, 1}, + {1487, 3}, + {1301, 3}, + {1248, 5}, + {1248, 3}, + {1248, 4}, + {1177, 4}, + {1177, 5}, + {1177, 5}, + {1175, 4}, + {1176, 0}, + {1176, 2}, + {1174, 4}, + {1278, 6}, + {1278, 8}, + {1277, 6}, + {1277, 2}, + {1462, 0}, + {1462, 2}, + {1462, 1}, + {1462, 3}, + {845, 5}, + {845, 6}, + {845, 7}, + {845, 7}, + {845, 8}, + {845, 9}, + {845, 8}, + {845, 7}, + {845, 6}, + {845, 8}, + {1101, 0}, + {1101, 2}, + {1101, 2}, + {902, 0}, + {902, 2}, + {1318, 1}, + {1318, 3}, + {1112, 2}, + {1112, 2}, + {1112, 3}, + {1112, 3}, + {1112, 2}, + {1112, 2}, + {996, 3}, + {1027, 1}, + {1027, 3}, + {1515, 0}, + {1515, 1}, + {943, 1}, + {943, 2}, + {943, 2}, + {943, 2}, + {943, 4}, + {943, 5}, + {943, 6}, + {943, 4}, + {943, 5}, + {1113, 2}, + {1516, 1}, + {1516, 3}, + {953, 3}, + {953, 3}, + {821, 1}, + {821, 3}, + {821, 5}, + {906, 1}, + {906, 3}, + {1123, 0}, + {1123, 1}, + {1370, 0}, + {1370, 3}, + {980, 1}, + {980, 3}, + {1336, 0}, + {1336, 1}, + {1335, 1}, + {1335, 3}, + {1124, 1}, + {1124, 1}, + {1125, 0}, + {1125, 3}, + {846, 1}, + {846, 2}, + {1070, 0}, + {1070, 1}, + {915, 1}, + {915, 1}, + {1044, 1}, + {1044, 2}, + {1168, 0}, + {1168, 1}, + {1354, 2}, + {1354, 1}, + {1032, 2}, + {1032, 1}, + {1032, 1}, + {1032, 2}, + {1032, 3}, + {1032, 1}, + {1032, 2}, + {1032, 2}, + {1032, 3}, + {1032, 3}, + {1032, 2}, + {1032, 6}, + {1032, 6}, + {1032, 1}, + {1032, 2}, + {1032, 2}, + {1032, 2}, + {1032, 2}, + {1325, 0}, + {1325, 3}, + {1325, 5}, + {1470, 1}, + {1470, 1}, + {1470, 1}, + {1333, 1}, + {1333, 1}, + {1333, 1}, + {1048, 0}, + {1048, 2}, + {1499, 0}, + {1499, 1}, + {1499, 1}, + {1126, 1}, + {1126, 2}, + {1127, 0}, + {1127, 1}, + {1340, 7}, + {1340, 7}, + {1340, 7}, + {1340, 7}, + {1340, 8}, + {1340, 5}, + {1390, 2}, + {1390, 2}, + {1390, 2}, + {1391, 0}, + {1391, 1}, + {1011, 5}, + {1219, 3}, + {1220, 3}, + {1397, 0}, + {1397, 1}, + {1397, 1}, + {1397, 2}, + {1397, 2}, + {1249, 1}, + {1249, 1}, + {1249, 2}, + {1249, 2}, + {1249, 2}, + {1349, 1}, + {1349, 1}, + {1349, 1}, + {1349, 1}, + {999, 3}, + {999, 3}, + {999, 4}, + {1214, 3}, + {1214, 1}, + {1061, 1}, + {1061, 3}, + {1061, 4}, + {1061, 3}, + {1061, 1}, + {780, 4}, + {780, 4}, + {1060, 1}, + {1060, 1}, + {1060, 1}, + {1060, 1}, + {1059, 1}, + {1059, 1}, + {1059, 1}, + {1036, 1}, + {1036, 1}, + {1082, 1}, + {1082, 2}, + {1082, 2}, + {916, 1}, + {916, 1}, + {916, 1}, + {1287, 1}, + {1287, 1}, + {1287, 1}, + {1327, 1}, + {1327, 1}, + {1140, 12}, + {1159, 3}, + {1134, 13}, + {1375, 0}, + {1375, 3}, + {932, 1}, + {932, 3}, + {922, 3}, + {922, 4}, + {1193, 0}, + {1193, 1}, + {1193, 1}, + {1193, 2}, + {1193, 2}, + {1374, 0}, + {1374, 1}, + {1374, 1}, + {1374, 1}, + {1102, 4}, + {1102, 3}, + {1133, 5}, + {907, 1}, + {989, 1}, + {936, 1}, + {936, 1}, + {954, 4}, + {954, 4}, + {954, 4}, + {954, 2}, + {954, 1}, + {954, 5}, + {1346, 0}, + {1346, 1}, + {1037, 1}, + {1037, 2}, + {1035, 12}, + {1035, 7}, + {1218, 0}, + {1218, 4}, + {1218, 4}, + {891, 0}, + {891, 1}, + {1233, 0}, + {1233, 6}, + {1289, 6}, + {1289, 5}, + {1415, 0}, + {1415, 3}, + {1416, 1}, + {1416, 5}, + {1416, 6}, + {1416, 4}, + {1416, 5}, + {1416, 4}, + {1416, 3}, + {1416, 1}, + {1232, 0}, + {1232, 7}, + {1379, 1}, + {1379, 2}, + {1396, 0}, + {1396, 2}, + {1394, 0}, + {1394, 2}, + {1362, 0}, + {1362, 14}, + {1203, 0}, + {1203, 1}, + {1477, 0}, + {1477, 4}, + {1476, 0}, + {1476, 2}, + {1417, 0}, + {1417, 2}, + {1231, 0}, + {1231, 3}, + {1230, 1}, + {1230, 3}, + {1067, 5}, + {1475, 0}, + {1475, 3}, + {1474, 1}, + {1474, 3}, + {1288, 3}, + {1066, 0}, + {1066, 2}, + {910, 3}, + {910, 3}, + {910, 4}, + {910, 3}, + {910, 4}, + {910, 4}, + {910, 3}, + {910, 3}, + {910, 3}, + {910, 3}, + {910, 1}, + {1414, 0}, + {1414, 4}, + {1414, 6}, + {1414, 1}, + {1414, 5}, + {1414, 1}, + {1414, 1}, + {1164, 0}, + {1164, 1}, + {1164, 1}, + {1322, 0}, + {1322, 1}, + {1343, 0}, + {1343, 1}, + {1343, 1}, + {1343, 1}, + {1343, 1}, + {1344, 1}, + {1344, 1}, + {1344, 1}, + {1344, 1}, + {1384, 2}, + {1384, 4}, + {1143, 11}, + {1412, 0}, + {1412, 2}, + {1492, 0}, + {1492, 3}, + {1492, 3}, + {1492, 3}, + {1494, 0}, + {1494, 3}, + {1497, 0}, + {1497, 3}, + {1497, 3}, + {1496, 1}, + {1495, 0}, + {1495, 3}, + {1334, 1}, + {1334, 3}, + {1493, 0}, + {1493, 4}, + {1493, 4}, + {1148, 2}, + {823, 13}, + {823, 9}, + {833, 10}, + {839, 1}, + {839, 1}, + {839, 2}, + {839, 2}, + {929, 1}, + {1150, 4}, + {1151, 7}, + {1151, 7}, + {1161, 6}, + {1065, 0}, + {1065, 1}, + {1065, 2}, + {1163, 4}, + {1163, 6}, + {1162, 3}, + {1162, 5}, + {1157, 3}, + {1157, 5}, + {1160, 3}, + {1160, 5}, + {1160, 4}, + {1012, 0}, + {1012, 1}, + {1012, 1}, + {1294, 1}, + {1294, 1}, + {802, 0}, + {802, 1}, + {1166, 0}, + {1298, 2}, + {1298, 5}, + {1298, 3}, + {1298, 6}, + {858, 1}, + {858, 1}, + {858, 1}, + {857, 2}, + {857, 3}, + {857, 2}, + {857, 4}, + {857, 7}, + {857, 5}, + {857, 7}, + {857, 5}, + {857, 3}, + {857, 6}, + {857, 6}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {1170, 1}, + {967, 2}, + {965, 3}, + {1114, 5}, + {1114, 5}, + {1114, 3}, + {1114, 4}, + {1114, 3}, + {1114, 6}, + {1114, 4}, + {1114, 6}, + {1114, 4}, + {1114, 5}, + {1114, 4}, + {1114, 5}, + {1114, 5}, + {1114, 5}, + {1115, 2}, + {1115, 2}, + {1115, 2}, + {1347, 1}, + {1347, 3}, + {949, 0}, + {949, 2}, + {946, 1}, + {946, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {945, 1}, + {950, 1}, + {950, 1}, + {950, 1}, + {950, 1}, + {947, 1}, + {947, 1}, + {947, 2}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 5}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 6}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {948, 3}, + {816, 1}, + {825, 1}, + {799, 1}, + {998, 1}, + {998, 1}, + {998, 1}, + {1225, 1}, + {1225, 1}, + {1225, 1}, + {1237, 5}, + {1259, 5}, + {1119, 4}, + {1152, 5}, + {798, 3}, + {798, 3}, + {798, 3}, + {798, 3}, + {798, 2}, + {798, 9}, + {798, 3}, + {798, 3}, + {798, 3}, + {798, 1}, + {1147, 1}, + {1147, 1}, + {1212, 1}, + {1212, 1}, + {1366, 0}, + {1366, 4}, + {1366, 7}, + {1366, 3}, + {1366, 3}, + {801, 1}, + {801, 1}, + {800, 1}, + {800, 1}, + {863, 1}, + {863, 3}, + {1395, 1}, + {1395, 3}, + {1348, 1}, + {1348, 3}, + {921, 0}, + {921, 1}, + {1181, 0}, + {1181, 1}, + {1180, 1}, + {797, 3}, + {797, 3}, + {797, 4}, + {797, 5}, + {797, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1338, 1}, + {1326, 1}, + {1326, 2}, + {1381, 1}, + {1381, 2}, + {1377, 1}, + {1377, 2}, + {1383, 1}, + {1383, 2}, + {1372, 1}, + {1372, 2}, + {1437, 1}, + {1437, 2}, + {1319, 1}, + {1319, 1}, + {1319, 1}, + {796, 5}, + {796, 3}, + {796, 5}, + {796, 4}, + {796, 4}, + {796, 3}, + {796, 5}, + {796, 1}, + {1250, 1}, + {1250, 1}, + {1200, 0}, + {1200, 2}, + {1171, 1}, + {1171, 3}, + {1171, 5}, + {1171, 2}, + {1359, 0}, + {1359, 1}, + {1358, 1}, + {1358, 2}, + {1358, 1}, + {1358, 2}, + {1361, 1}, + {1361, 3}, + {1510, 0}, + {1510, 2}, + {1050, 4}, + {1187, 0}, + {1187, 2}, + {1321, 0}, + {1321, 1}, + {995, 3}, + {854, 0}, + {854, 2}, + {883, 0}, + {883, 3}, + {958, 0}, + {958, 1}, + {981, 0}, + {981, 1}, + {983, 0}, + {983, 2}, + {982, 3}, + {982, 1}, + {982, 3}, + {982, 2}, + {982, 1}, + {982, 1}, + {1053, 1}, + {1053, 3}, + {1053, 3}, + {1376, 0}, + {1376, 1}, + {961, 2}, + {961, 2}, + {1004, 1}, + {1004, 1}, + {1004, 1}, + {1004, 1}, + {959, 1}, + {959, 1}, + {770, 1}, + {770, 1}, + {770, 1}, + {770, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {773, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {772, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {771, 1}, + {1118, 2}, + {1424, 1}, + {1424, 3}, + {1424, 4}, + {1424, 6}, + {824, 9}, + {1196, 0}, + {1196, 1}, + {1195, 5}, + {1195, 4}, + {1195, 4}, + {1195, 4}, + {1195, 4}, + {1195, 2}, + {1195, 1}, + {1195, 1}, + {1195, 1}, + {1195, 1}, + {1195, 2}, + {1095, 1}, + {1095, 1}, + {1093, 1}, + {1093, 3}, + {938, 3}, + {1491, 0}, + {1491, 1}, + {1490, 3}, + {1490, 1}, + {893, 1}, + {893, 1}, + {1337, 3}, + {1337, 5}, + {1398, 0}, + {1398, 5}, + {826, 6}, + {777, 1}, + {777, 1}, + {777, 1}, + {777, 1}, + {777, 1}, + {777, 1}, + {777, 1}, + {777, 2}, + {777, 1}, + {777, 1}, + {777, 2}, + {777, 2}, + {779, 1}, + {779, 2}, + {1313, 1}, + {1313, 3}, + {1104, 2}, + {842, 3}, + {1000, 1}, + {1000, 3}, + {973, 1}, + {973, 2}, + {1411, 1}, + {1411, 1}, + {1064, 0}, + {1064, 1}, + {1064, 1}, + {909, 0}, + {909, 1}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 5}, + {795, 5}, + {795, 5}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 3}, + {795, 1}, + {778, 1}, + {778, 3}, + {778, 5}, + {790, 1}, + {790, 1}, + {790, 1}, + {790, 1}, + {790, 3}, + {790, 1}, + {790, 1}, + {790, 1}, + {790, 1}, + {790, 1}, + {790, 2}, + {790, 2}, + {790, 2}, + {790, 2}, + {790, 3}, + {790, 2}, + {790, 1}, + {790, 3}, + {790, 5}, + {790, 6}, + {790, 2}, + {790, 4}, + {790, 2}, + {790, 7}, + {790, 5}, + {790, 6}, + {790, 6}, + {790, 4}, + {790, 4}, + {790, 3}, + {790, 3}, + {1320, 0}, + {1320, 1}, + {885, 1}, + {885, 1}, + {887, 1}, + {887, 1}, + {913, 0}, + {913, 1}, + {1039, 0}, + {1039, 1}, + {912, 1}, + {912, 2}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {784, 1}, + {1224, 0}, + {1224, 2}, + {788, 1}, + {788, 1}, + {788, 1}, + {788, 1}, + {788, 1}, + {787, 1}, + {787, 1}, + {787, 1}, + {787, 1}, + {787, 1}, + {787, 1}, + {782, 4}, + {782, 4}, + {782, 2}, + {782, 3}, + {782, 2}, + {782, 4}, + {782, 6}, + {782, 2}, + {782, 2}, + {782, 2}, + {782, 4}, + {782, 6}, + {782, 4}, + {783, 4}, + {783, 4}, + {783, 6}, + {783, 8}, + {783, 8}, + {783, 6}, + {783, 6}, + {783, 6}, + {783, 6}, + {783, 6}, + {783, 8}, + {783, 8}, + {783, 8}, + {783, 8}, + {783, 4}, + {783, 6}, + {783, 6}, + {783, 7}, + {783, 4}, + {783, 7}, + {783, 7}, + {783, 1}, + {783, 8}, + {1368, 1}, + {1368, 1}, + {1368, 1}, + {1368, 1}, + {785, 1}, + {785, 1}, + {786, 1}, + {786, 1}, + {1486, 1}, + {1486, 1}, + {1486, 1}, + {789, 4}, + {789, 6}, + {789, 1}, + {791, 6}, + {791, 4}, + {791, 4}, + {791, 5}, + {791, 6}, + {791, 5}, + {791, 6}, + {791, 5}, + {791, 6}, + {791, 5}, + {791, 6}, + {791, 5}, + {791, 5}, + {791, 8}, + {791, 6}, + {791, 6}, + {791, 6}, + {791, 6}, + {791, 6}, + {791, 6}, + {791, 6}, + {791, 5}, + {791, 6}, + {791, 7}, + {791, 8}, + {791, 8}, + {791, 9}, + {1403, 0}, + {1403, 2}, + {781, 4}, + {781, 6}, + {1367, 0}, + {1367, 2}, + {1367, 3}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {901, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {886, 1}, + {1356, 0}, + {1356, 1}, + {1501, 1}, + {1501, 2}, + {1303, 4}, + {1353, 0}, + {1353, 2}, + {1120, 2}, + {1120, 3}, + {1120, 1}, + {1120, 1}, + {1120, 2}, + {1120, 2}, + {1120, 2}, + {1120, 2}, + {1120, 2}, + {1120, 1}, + {1120, 1}, + {1120, 2}, + {1120, 1}, + {934, 1}, + {934, 1}, + {934, 1}, + {990, 0}, + {990, 1}, + {804, 1}, + {804, 3}, + {884, 1}, + {884, 3}, + {1018, 2}, + {1018, 4}, + {1084, 1}, + {1084, 3}, + {1008, 0}, + {1008, 2}, + {1247, 0}, + {1247, 1}, + {1240, 4}, + {1422, 1}, + {1422, 1}, + {1169, 2}, + {1169, 4}, + {1488, 1}, + {1488, 3}, + {1145, 3}, + {1146, 1}, + {1146, 1}, + {847, 1}, + {847, 2}, + {847, 3}, + {847, 4}, + {1129, 4}, + {1129, 4}, + {1129, 5}, + {1129, 2}, + {1129, 3}, + {1129, 1}, + {1129, 2}, + {1275, 1}, + {1258, 1}, + {1188, 2}, + {807, 4}, + {808, 3}, + {809, 7}, + {1482, 0}, + {1482, 7}, + {1482, 5}, + {1481, 0}, + {1481, 1}, + {1481, 1}, + {1481, 1}, + {1483, 0}, + {1483, 1}, + {1483, 1}, + {1253, 0}, + {1253, 4}, + {806, 7}, + {806, 6}, + {806, 5}, + {806, 6}, + {806, 6}, + {817, 2}, + {817, 2}, + {815, 2}, + {815, 3}, + {1308, 3}, + {1308, 1}, + {1034, 4}, + {1365, 2}, + {1502, 0}, + {1502, 2}, + {1503, 1}, + {1503, 3}, + {1304, 3}, + {1026, 1}, + {1306, 3}, + {1508, 4}, + {1401, 0}, + {1401, 1}, + {1405, 0}, + {1405, 3}, + {1410, 0}, + {1410, 3}, + {1409, 0}, + {1409, 2}, + {1506, 1}, + {1506, 1}, + {1506, 1}, + {1505, 1}, + {1505, 1}, + {1097, 2}, + {1097, 2}, + {1097, 2}, + {1097, 4}, + {1097, 2}, + {1504, 4}, + {1305, 1}, + {1305, 2}, + {1305, 2}, + {1305, 2}, + {1305, 4}, + {844, 0}, + {844, 1}, + {832, 2}, + {1507, 1}, + {1507, 1}, + {794, 4}, + {794, 4}, + {794, 4}, + {794, 4}, + {794, 4}, + {794, 5}, + {794, 7}, + {794, 7}, + {794, 6}, + {794, 6}, + {794, 9}, + {1226, 0}, + {1226, 3}, + {1226, 3}, + {1227, 0}, + {1227, 2}, + {988, 0}, + {988, 2}, + {988, 2}, + {1402, 0}, + {1402, 2}, + {1402, 2}, + {1480, 1}, + {993, 1}, + {993, 3}, + {955, 1}, + {955, 4}, + {900, 1}, + {900, 1}, + {899, 6}, + {899, 2}, + {899, 3}, + {963, 0}, + {963, 4}, + {1017, 0}, + {1017, 1}, + {1016, 1}, + {1016, 2}, + {1052, 2}, + {1052, 2}, + {1052, 2}, + {1373, 0}, + {1373, 2}, + {1373, 3}, + {1373, 3}, + {1051, 5}, + {960, 0}, + {960, 1}, + {960, 3}, + {960, 1}, + {960, 3}, + {1191, 1}, + {1191, 2}, + {1192, 0}, + {1192, 1}, + {895, 3}, + {895, 5}, + {895, 7}, + {895, 7}, + {895, 9}, + {895, 4}, + {895, 6}, + {895, 3}, + {895, 5}, + {914, 1}, + {914, 1}, + {1229, 0}, + {1229, 1}, + {919, 1}, + {919, 2}, + {919, 2}, + {1201, 0}, + {1201, 2}, + {985, 1}, + {985, 1}, + {1444, 1}, + {1444, 1}, + {1363, 1}, + {1363, 1}, + {1357, 0}, + {1357, 1}, + {843, 2}, + {843, 4}, + {843, 4}, + {843, 5}, + {925, 0}, + {925, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1266, 1}, + {1447, 0}, + {1447, 1}, + {1448, 2}, + {1448, 1}, + {969, 1}, + {1019, 0}, + {1019, 1}, + {1267, 1}, + {1267, 1}, + {1446, 1}, + {1080, 0}, + {1080, 1}, + {992, 0}, + {992, 5}, + {775, 3}, + {775, 3}, + {775, 3}, + {775, 3}, + {991, 0}, + {991, 3}, + {991, 3}, + {991, 4}, + {991, 5}, + {991, 4}, + {991, 5}, + {991, 5}, + {991, 4}, + {1217, 0}, + {1217, 2}, + {818, 1}, + {818, 1}, + {818, 2}, + {818, 2}, + {813, 3}, + {813, 3}, + {812, 4}, + {812, 4}, + {812, 5}, + {812, 2}, + {812, 2}, + {812, 3}, + {811, 1}, + {811, 3}, + {810, 1}, + {810, 1}, + {1450, 2}, + {1450, 2}, + {1450, 2}, + {1081, 1}, + {1121, 9}, + {1121, 9}, + {848, 2}, + {848, 4}, + {848, 6}, + {848, 4}, + {848, 4}, + {848, 3}, + {848, 6}, + {848, 6}, + {848, 3}, + {848, 4}, + {1271, 3}, + {1270, 6}, + {1269, 1}, + {1269, 1}, + {1269, 1}, + {1451, 3}, + {1451, 1}, + {1451, 1}, + {1087, 1}, + {1087, 3}, + {1023, 3}, + {1023, 2}, + {1023, 2}, + {1023, 3}, + {1380, 2}, + {1380, 2}, + {1380, 2}, + {1380, 1}, + {939, 1}, + {939, 1}, + {939, 1}, + {892, 1}, + {892, 1}, + {926, 1}, + {926, 3}, + {1001, 1}, + {1001, 3}, + {1001, 3}, + {1096, 3}, + {1096, 4}, + {1096, 4}, + {1096, 4}, + {1096, 3}, + {1096, 3}, + {1096, 2}, + {1096, 4}, + {1096, 4}, + {1096, 2}, + {1096, 2}, + {1331, 1}, + {1331, 1}, + {905, 1}, + {905, 1}, + {974, 1}, + {974, 1}, + {1302, 1}, + {1302, 3}, + {793, 1}, + {793, 1}, + {792, 1}, + {776, 1}, + {855, 1}, + {855, 3}, + {855, 2}, + {855, 2}, + {970, 1}, + {970, 3}, + {1234, 1}, + {1234, 4}, + {997, 1}, + {918, 1}, + {918, 1}, + {898, 3}, + {898, 2}, + {1078, 1}, + {1078, 1}, + {917, 1}, + {917, 1}, + {966, 1}, + {966, 3}, + {1312, 2}, + {1312, 4}, + {1312, 4}, + {1100, 3}, + {1100, 5}, + {1100, 6}, + {1100, 4}, + {1100, 4}, + {1100, 5}, + {1100, 5}, + {1100, 5}, + {1100, 6}, + {1100, 4}, + {1100, 5}, + {1100, 5}, + {1100, 5}, + {1100, 6}, + {1100, 6}, + {1100, 4}, + {1100, 3}, + {1100, 3}, + {1100, 4}, + {1100, 4}, + {1100, 5}, + {1100, 5}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 3}, + {1100, 4}, + {1311, 2}, + {1311, 2}, + {1311, 3}, + {1311, 3}, + {1369, 1}, + {1369, 3}, + {1185, 5}, + {1005, 1}, + {1005, 3}, + {1273, 3}, + {1273, 4}, + {1273, 4}, + {1273, 5}, + {1273, 4}, + {1273, 5}, + {1273, 5}, + {1273, 4}, + {1273, 6}, + {1273, 4}, + {1273, 8}, + {1273, 2}, + {1273, 5}, + {1273, 3}, + {1273, 3}, + {1273, 2}, + {1273, 5}, + {1273, 2}, + {1273, 2}, + {1273, 4}, + {1273, 4}, + {1273, 4}, + {1455, 2}, + {1455, 2}, + {1455, 4}, + {1458, 0}, + {1458, 1}, + {1457, 1}, + {1457, 3}, + {1272, 1}, + {1272, 1}, + {1272, 2}, + {1272, 2}, + {1272, 2}, + {1272, 1}, + {1272, 1}, + {1272, 1}, + {1272, 1}, + {1456, 0}, + {1456, 3}, + {1489, 0}, + {1489, 2}, + {1453, 1}, + {1453, 1}, + {1453, 1}, + {903, 1}, + {903, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 3}, + {1459, 3}, + {1459, 3}, + {1459, 3}, + {1459, 5}, + {1459, 4}, + {1459, 5}, + {1459, 5}, + {1459, 1}, + {1459, 5}, + {1459, 1}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 1}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 2}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 2}, + {1459, 1}, + {1459, 1}, + {1459, 1}, + {1459, 2}, + {1459, 2}, + {1454, 0}, + {1454, 2}, + {1454, 2}, + {1049, 0}, + {1049, 1}, + {1049, 1}, + {1469, 0}, + {1469, 1}, + {1469, 1}, + {1469, 1}, + {1222, 0}, + {1222, 1}, + {940, 0}, + {940, 2}, + {1274, 2}, + {1178, 3}, + {1069, 1}, + {1069, 3}, + {1364, 1}, + {1364, 1}, + {1364, 3}, + {1364, 1}, + {1364, 2}, + {1364, 3}, + {1364, 1}, + {1389, 0}, + {1389, 1}, + {1389, 1}, + {1389, 1}, + {1389, 1}, + {1389, 1}, + {924, 0}, + {924, 1}, + {924, 1}, + {1293, 0}, + {1293, 1}, + {1548, 0}, + {1548, 2}, + {1509, 0}, + {1509, 3}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1284, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {1022, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {920, 1}, + {1468, 1}, + {1468, 3}, + {1002, 2}, + {1122, 1}, + {1122, 1}, + {1085, 1}, + {1085, 1}, + {1291, 1}, + {1291, 3}, + {1478, 0}, + {1478, 3}, + {941, 1}, + {941, 4}, + {941, 4}, + {941, 4}, + {941, 3}, + {941, 4}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 1}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 3}, + {941, 2}, + {941, 2}, + {941, 3}, + {941, 3}, + {941, 5}, + {941, 3}, + {941, 7}, + {941, 3}, + {941, 3}, + {931, 0}, + {931, 1}, + {1286, 1}, + {1286, 1}, + {1141, 0}, + {1141, 1}, + {1020, 1}, + {1020, 2}, + {1020, 3}, + {1407, 0}, + {1407, 1}, + {860, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {937, 3}, + {1089, 1}, + {1089, 1}, + {1089, 1}, + {1062, 3}, + {1062, 2}, + {1062, 3}, + {1062, 3}, + {1062, 2}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1054, 1}, + {1031, 1}, + {1031, 1}, + {1223, 0}, + {1223, 1}, + {1223, 1}, + {1046, 1}, + {1046, 1}, + {1046, 1}, + {1047, 1}, + {1047, 1}, + {1047, 1}, + {1047, 2}, + {1047, 1}, + {1047, 1}, + {1029, 1}, + {1083, 3}, + {1083, 2}, + {1083, 3}, + {1083, 2}, + {1083, 3}, + {1083, 3}, + {1083, 2}, + {1083, 2}, + {1083, 1}, + {1083, 2}, + {1083, 5}, + {1083, 5}, + {1083, 1}, + {1083, 3}, + {1083, 2}, + {951, 1}, + {951, 1}, + {1058, 1}, + {1058, 2}, + {1058, 2}, + {1025, 2}, + {1025, 2}, + {1025, 1}, + {1025, 1}, + {1063, 2}, + {1063, 2}, + {1063, 1}, + {1063, 2}, + {1063, 2}, + {1063, 3}, + {1063, 3}, + {1063, 2}, + {1098, 1}, + {1098, 1}, + {1030, 1}, + {1030, 2}, + {1030, 1}, + {1030, 1}, + {1030, 2}, + {1086, 1}, + {1086, 2}, + {1086, 1}, + {1086, 1}, + {987, 1}, + {987, 1}, + {987, 1}, + {987, 1}, + {1038, 1}, + {1038, 2}, + {1038, 2}, + {1038, 2}, + {1038, 3}, + {841, 3}, + {888, 0}, + {888, 1}, + {978, 1}, + {978, 1}, + {978, 1}, + {979, 0}, + {979, 2}, + {1003, 0}, + {1003, 1}, + {1003, 1}, + {1010, 5}, + {1399, 0}, + {1399, 1}, + {896, 0}, + {896, 2}, + {896, 3}, + {1400, 0}, + {1400, 2}, + {853, 2}, + {853, 1}, + {853, 2}, + {1221, 0}, + {1221, 2}, + {1472, 1}, + {1472, 3}, + {1021, 1}, + {1021, 1}, + {1021, 1}, + {1297, 1}, + {1297, 3}, + {805, 1}, + {805, 1}, + {1473, 1}, + {1473, 1}, + {1473, 1}, + {827, 1}, + {827, 2}, + {822, 10}, + {822, 8}, + {861, 2}, + {889, 2}, + {890, 0}, + {890, 1}, + {1517, 0}, + {1517, 1}, + {1142, 9}, + {1138, 4}, + {1111, 9}, + {1111, 9}, + {1103, 3}, + {1106, 4}, + {1378, 2}, + {1378, 6}, + {994, 2}, + {1024, 1}, + {1024, 3}, + {1131, 0}, + {1131, 2}, + {1339, 1}, + {1339, 2}, + {1130, 2}, + {1130, 2}, + {1130, 2}, + {1130, 2}, + {1076, 0}, + {1076, 1}, + {1075, 2}, + {1075, 2}, + {1075, 2}, + {1075, 2}, + {1439, 1}, + {1439, 3}, + {1439, 2}, + {1077, 2}, + {1077, 2}, + {1077, 2}, + {1077, 2}, + {1077, 2}, + {1128, 0}, + {1128, 2}, + {1128, 2}, + {1254, 0}, + {1254, 3}, + {1236, 0}, + {1236, 1}, + {1235, 1}, + {1235, 2}, + {1068, 2}, + {1068, 2}, + {1068, 3}, + {1068, 3}, + {1068, 4}, + {1068, 5}, + {1068, 2}, + {1068, 5}, + {1068, 3}, + {1068, 3}, + {1068, 2}, + {1068, 2}, + {1068, 2}, + {1323, 0}, + {1323, 3}, + {1323, 3}, + {1323, 5}, + {1323, 5}, + {1323, 4}, + {1324, 1}, + {1186, 1}, + {1186, 1}, + {1264, 1}, + {1443, 1}, + {1443, 3}, + {944, 1}, + {944, 1}, + {944, 1}, + {944, 1}, + {944, 1}, + {944, 1}, + {944, 1}, + {944, 1}, + {1132, 7}, + {1132, 9}, + {1149, 5}, + {1149, 7}, + {1149, 7}, + {1268, 5}, + {1268, 7}, + {1268, 7}, + {1184, 9}, + {1182, 7}, + {1183, 4}, + {1307, 0}, + {1307, 3}, + {1307, 3}, + {1307, 3}, + {1307, 3}, + {1307, 3}, + {1045, 1}, + {1045, 2}, + {1079, 1}, + {1079, 1}, + {1079, 1}, + {1079, 3}, + {1079, 3}, + {1263, 1}, + {1263, 3}, + {1071, 1}, + {1071, 4}, + {1072, 1}, + {1072, 2}, + {1072, 1}, + {1072, 1}, + {1072, 2}, + {1072, 2}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 2}, + {1072, 1}, + {1072, 2}, + {1072, 1}, + {1072, 2}, + {1072, 2}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 1}, + {1072, 3}, + {1072, 2}, + {1072, 2}, + {1072, 2}, + {1072, 2}, + {1072, 2}, + {1072, 2}, + {1072, 2}, + {1072, 1}, + {1072, 1}, + {1215, 0}, + {1215, 1}, + {1215, 1}, + {1215, 1}, + {1241, 1}, + {1241, 3}, + {1241, 3}, + {1241, 3}, + {1241, 1}, + {1262, 7}, + {1261, 4}, + {962, 17}, + {1179, 0}, + {1179, 2}, + {1371, 0}, + {1371, 3}, + {1332, 0}, + {1332, 3}, + {1209, 0}, + {1209, 1}, + {1173, 0}, + {1173, 2}, + {930, 1}, + {930, 1}, + {1360, 2}, + {1360, 1}, + {1172, 3}, + {1172, 2}, + {1172, 3}, + {1172, 3}, + {1172, 4}, + {1172, 6}, + {956, 1}, + {956, 1}, + {956, 1}, + {1056, 0}, + {1056, 3}, + {1466, 0}, + {1466, 3}, + {1385, 0}, + {1385, 3}, + {1207, 0}, + {1207, 2}, + {1387, 3}, + {1387, 1}, + {1206, 3}, + {1205, 0}, + {1205, 2}, + {1386, 1}, + {1386, 3}, + {1204, 1}, + {1204, 3}, + {1189, 9}, + {1300, 2}, + {1211, 3}, + {1295, 1}, + {1295, 1}, + {1292, 2}, + {1388, 1}, + {1388, 2}, + {1388, 1}, + {1388, 2}, + {1479, 1}, + {1479, 3}, + {1213, 6}, + {1452, 1}, + {1452, 1}, + {1452, 1}, + {1452, 1}, + {1350, 0}, + {1350, 2}, + {1350, 3}, + {1404, 0}, + {1404, 2}, + {1199, 2}, + {1199, 3}, + {1199, 3}, + {1199, 2}, + {1198, 1}, + {1198, 2}, + {1208, 3}, + {1210, 3}, + {1210, 5}, + {1210, 7}, + {1299, 3}, + {1299, 5}, + {1299, 7}, + {1153, 5}, + {1137, 6}, + {1107, 6}, + {1156, 5}, + {1135, 7}, + {1105, 6}, + {1139, 6}, + {1342, 0}, + {1342, 1}, + {1449, 1}, + {1449, 2}, + {1014, 3}, + {1014, 3}, + {1014, 3}, + {1014, 3}, + {1014, 3}, + {1014, 1}, + {1014, 2}, + {1014, 3}, + {1014, 1}, + {1014, 2}, + {1014, 3}, + {1014, 1}, + {1014, 2}, + {1014, 1}, + {1014, 1}, + {1014, 2}, + {911, 1}, + {911, 2}, + {911, 2}, + {1158, 4}, + {1109, 5}, + {1314, 1}, + {1314, 2}, + {1108, 1}, + {1108, 1}, + {1108, 3}, + {1108, 3}, + {1190, 8}, + {1393, 0}, + {1393, 2}, + {1392, 0}, + {1392, 3}, + {1419, 0}, + {1419, 2}, + {1418, 0}, + {1418, 2}, + {1167, 1}, + {1094, 1}, + {1094, 3}, + {1013, 2}, + {1239, 6}, + {1239, 7}, + {1239, 10}, + {1239, 11}, + {1239, 6}, + {1239, 7}, + {1239, 4}, + {1239, 5}, + {1239, 6}, + {1420, 0}, + {1420, 3}, + {1406, 0}, + {1406, 1}, + {1463, 3}, + {1463, 1}, + {1280, 3}, + {1279, 0}, + {1279, 1}, + {1279, 1}, + {1279, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {880, 1}, + {1425, 1}, + {1425, 1}, + {1425, 1}, + {1425, 1}, + {881, 1}, + {1426, 1}, + {1426, 3}, + {1432, 0}, + {1432, 2}, + {1244, 4}, + {1244, 5}, + {1244, 6}, + {1430, 1}, + {1430, 1}, + {1431, 1}, + {1431, 3}, + {1245, 1}, + {1245, 1}, + {1245, 2}, + {1245, 1}, + {1242, 1}, + {1242, 3}, + {1408, 0}, + {1408, 1}, + {876, 2}, + {870, 5}, + {869, 2}, + {1433, 0}, + {1433, 2}, + {1433, 1}, + {1429, 1}, + {1429, 3}, + {1428, 0}, + {1428, 1}, + {1427, 2}, + {1427, 3}, + {1434, 0}, + {1434, 3}, + {935, 2}, + {935, 3}, + {866, 4}, + {871, 4}, + {1246, 4}, + {1423, 0}, + {1423, 2}, + {1423, 2}, + {868, 1}, + {868, 1}, + {1460, 1}, + {1460, 2}, + {1445, 1}, + {1445, 2}, + {1276, 4}, + {1265, 4}, + {1165, 0}, + {1165, 2}, + {879, 6}, + {878, 5}, + {882, 1}, + {867, 6}, + {867, 6}, + {873, 4}, + {1243, 0}, + {1243, 1}, + {874, 4}, + {872, 2}, + {875, 2}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {877, 1}, + {1136, 8}, + {1154, 4}, + {1116, 3}, + {1329, 0}, + {1329, 1}, + {1329, 1}, + {1352, 1}, + {1352, 2}, + {1352, 3}, + {1042, 3}, + {1042, 3}, + {1042, 3}, + {1042, 5}, + {1330, 2}, + {1330, 2}, + {1330, 2}, + {1330, 2}, + {1330, 2}, + {1099, 4}, + {1435, 1}, + {1435, 2}, + {1435, 3}, + {1073, 3}, + {1073, 3}, + {1073, 3}, + {1073, 1}, + {1074, 3}, + {1074, 3}, + {1074, 5}, + {1155, 4}, + } + + yyXErrors = map[yyXError]string{} + + yyParseTab = [4917][]uint16{ + // 0 + {2314, 2314, 3: 2861, 58: 2884, 84: 2863, 2866, 87: 2896, 2864, 3017, 103: 2898, 117: 3031, 159: 3033, 187: 2881, 195: 2879, 208: 3024, 222: 2892, 250: 2887, 254: 2869, 259: 2917, 266: 2883, 269: 2859, 277: 2916, 3027, 2865, 284: 3032, 296: 2895, 306: 2893, 308: 2860, 310: 2899, 330: 2885, 334: 2888, 341: 2897, 344: 2882, 357: 2874, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 2915, 562: 3026, 575: 3020, 577: 2877, 582: 2875, 587: 2890, 608: 2904, 695: 2900, 710: 3030, 713: 2862, 3019, 724: 2857, 727: 2868, 743: 2867, 766: 2914, 2858, 775: 2911, 803: 2870, 806: 2913, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 2995, 2994, 822: 3018, 2871, 2976, 826: 2988, 3004, 2876, 833: 2872, 839: 2934, 845: 2928, 2932, 2985, 2996, 857: 2936, 2878, 860: 3003, 3005, 894: 3023, 897: 2880, 904: 2921, 933: 3029, 943: 2929, 957: 3021, 962: 2979, 965: 2990, 967: 2993, 2886, 1035: 2941, 1090: 3025, 1099: 2949, 2919, 1102: 2920, 2923, 1105: 2926, 2924, 2927, 1109: 2925, 1111: 2922, 1113: 2930, 2931, 1116: 2937, 2889, 2974, 3014, 1121: 2938, 1132: 2945, 2939, 2940, 2946, 2947, 2948, 2944, 2950, 2951, 1142: 2943, 2942, 1145: 2933, 2894, 1148: 2952, 2966, 2953, 2954, 3015, 2957, 2956, 2962, 2961, 2963, 2958, 2964, 2965, 2955, 2960, 2959, 1166: 2918, 1169: 2935, 1174: 2970, 2968, 1177: 2969, 2967, 1182: 2972, 2973, 2971, 1188: 3010, 2975, 2977, 1198: 3028, 2978, 1208: 2980, 1210: 2981, 3007, 1213: 3011, 1237: 3012, 1239: 2983, 2984, 1248: 2989, 1251: 2986, 2987, 1258: 3009, 3013, 3022, 2992, 2991, 1268: 2997, 1270: 2999, 2998, 1273: 3001, 1275: 3008, 1278: 3000, 1284: 3016, 1298: 3002, 2982, 3006, 1465: 2855, 1468: 2856}, + {1: 2854}, + {7769, 2853}, + {18: 7722, 51: 7721, 217: 7718, 244: 7723, 316: 7719, 549: 4675, 591: 7720, 608: 2115, 644: 6649, 929: 7717, 958: 4674}, + {217: 7702, 608: 7701}, + // 5 + {608: 7695}, + {375: 7679, 608: 7680, 644: 6649, 929: 7681}, + {427: 7660, 546: 7661, 608: 2659, 1462: 7659}, + {397: 7615, 608: 7614}, + {2627, 2627, 413: 7613, 420: 7612}, + // 10 + {453: 7601}, + {533: 7600}, + {2594, 2594, 86: 6564, 566: 6562, 897: 6563, 1129: 7599}, + {18: 2365, 51: 7129, 102: 2365, 132: 2365, 181: 2365, 202: 790, 206: 7046, 216: 6150, 7126, 224: 7127, 244: 7130, 6806, 273: 7118, 567: 7125, 608: 2333, 644: 6649, 696: 2365, 705: 7120, 710: 2472, 747: 7122, 929: 7123, 964: 7131, 1049: 7128, 1065: 6149, 1374: 7119, 1412: 7124, 1461: 7121}, + {18: 7053, 51: 7054, 132: 7047, 157: 2333, 202: 790, 206: 7046, 7044, 216: 6150, 7048, 222: 1233, 224: 7049, 7050, 244: 7055, 6806, 273: 7041, 608: 2333, 644: 6649, 710: 7043, 894: 7051, 929: 7042, 964: 7056, 1049: 7052, 1065: 7045}, + // 15 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 7040}, + {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 561: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 7028}, + {2342, 2342}, + {2341, 2341}, + {531: 2907, 547: 2905, 608: 2904, 695: 2900, 714: 3019, 775: 3854, 803: 2870, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856, 822: 5705, 5703, 833: 5704}, + // 20 + {84: 2863, 2866, 87: 2896, 2864, 117: 7001, 195: 2879, 232: 7000, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 6999}, + {2: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 10: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 58: 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309, 531: 2309, 2309, 547: 2309, 552: 2309, 558: 2309, 587: 2309, 608: 2309, 695: 2309, 713: 2309, 2309, 724: 2309, 803: 2309}, + {2: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 10: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 58: 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 2308, 531: 2308, 2308, 547: 2308, 552: 2308, 558: 2308, 587: 2308, 608: 2308, 695: 2308, 713: 2308, 2308, 724: 2308, 803: 2308}, + {2: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 10: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 58: 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 2307, 531: 2307, 2307, 547: 2307, 552: 2307, 558: 2307, 587: 2307, 608: 2307, 695: 2307, 713: 2307, 2307, 724: 2307, 803: 2307}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 6968, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2907, 2906, 547: 2905, 552: 2891, 558: 6967, 587: 2890, 608: 2904, 695: 2900, 713: 6969, 3019, 724: 4646, 770: 3927, 3051, 3052, 3050, 775: 4647, 803: 2870, 6965, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6966}, + // 25 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6964, 3051, 3052, 3050}, + {195: 6962}, + {155: 6955, 608: 6653, 644: 6649, 929: 6652, 1115: 6954}, + {187: 6952}, + {187: 6945, 894: 6946}, + // 30 + {187: 6939, 894: 6940}, + {187: 6934}, + {16: 4418, 18: 6767, 30: 6797, 6796, 92: 6776, 131: 783, 154: 783, 156: 790, 783, 180: 790, 187: 6755, 206: 6805, 208: 6768, 240: 6765, 245: 6806, 248: 790, 260: 6807, 267: 6791, 783, 281: 6756, 302: 6788, 314: 6781, 329: 6787, 362: 6780, 367: 6803, 369: 6785, 6766, 376: 6783, 6801, 379: 6774, 386: 6772, 6790, 391: 6778, 394: 6789, 6760, 6800, 398: 6770, 405: 6761, 423: 6764, 6763, 430: 6804, 436: 6792, 439: 6798, 6795, 6799, 6794, 454: 6784, 553: 4419, 608: 6759, 655: 6779, 709: 4417, 6769, 713: 6802, 743: 6758, 853: 6775, 964: 6786, 1015: 6793, 1049: 6782, 1055: 6771, 1144: 6773, 1222: 6762, 1453: 6777, 1459: 6757}, + {208: 6750, 281: 6749}, + {421: 6651, 608: 6653, 644: 6649, 929: 6652, 1115: 6650}, + // 35 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6638, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6640, 3051, 3052, 3050, 1424: 6639}, + {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 559: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 6625}, + {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 559: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6590}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6585, 3051, 3052, 3050}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6579, 3051, 3052, 3050}, + // 40 + {222: 6577}, + {222: 1234}, + {1232, 1232, 86: 6564, 566: 6562, 712: 6561, 897: 6563, 1129: 6560}, + {1221, 1221}, + {1220, 1220}, + // 45 + {533: 6559}, + {2: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 10: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 58: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 6529, 6535, 6536, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 531: 1057, 533: 1057, 1057, 1057, 1057, 540: 1057, 1057, 543: 1057, 1057, 1057, 547: 1057, 1057, 552: 1057, 1057, 560: 1057, 573: 6532, 578: 1057, 584: 1057, 586: 1057, 1057, 614: 1057, 617: 1057, 628: 1057, 1057, 1057, 1057, 636: 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 1057, 697: 1057, 1057, 1057, 1057, 711: 1057, 715: 4174, 829: 4172, 4173, 836: 5961, 5960, 5959, 840: 5956, 849: 6528, 6531, 6527, 885: 6447, 887: 6525, 934: 6526, 969: 6524, 1266: 6534, 6530, 1447: 6523, 6533}, + {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 561: 6498, 424, 4661, 424, 571: 424, 889: 4662, 6499, 1365: 6497}, + {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1050: 6487, 1080: 6486}, + {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6481}, + // 50 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6476}, + {639: 3892, 1013: 3891, 1094: 3890}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6462, 1308: 6460, 1436: 6461}, + {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 6459, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848}, + {1028, 1028, 57: 1028, 530: 1028, 532: 1028, 542: 1028}, + // 55 + {1027, 1027, 57: 1027, 530: 1027, 532: 1027, 542: 1027}, + {539: 6444, 550: 6445, 6446, 1450: 6443}, + {674, 674, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {539: 1016, 550: 1016, 1016}, + {676, 676, 539: 1014, 550: 1014, 1014}, + // 60 + {302: 6428, 329: 6427}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 6265, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 6266, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 6270, 3069, 3070, 3102, 6262, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 6267, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6268, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 6261, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 6271, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6269, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 6273, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6279, 1302: 6272}, + {17: 6125, 58: 6128, 250: 6126, 259: 6132, 266: 6127, 6130, 269: 6123, 6131, 285: 6133, 333: 6129, 373: 6124, 388: 6134, 429: 6135, 702: 6122, 968: 6121}, + {23: 762, 155: 762, 762, 762, 173: 5251, 240: 762, 246: 762, 257: 762, 275: 762, 288: 762, 309: 762, 313: 762, 586: 762, 608: 762, 908: 5250, 924: 6094}, + {753, 753}, + // 65 + {752, 752}, + {751, 751}, + {750, 750}, + {749, 749}, + {748, 748}, + // 70 + {747, 747}, + {746, 746}, + {745, 745}, + {744, 744}, + {743, 743}, + // 75 + {742, 742}, + {741, 741}, + {740, 740}, + {739, 739}, + {738, 738}, + // 80 + {737, 737}, + {736, 736}, + {735, 735}, + {734, 734}, + {733, 733}, + // 85 + {732, 732}, + {731, 731}, + {730, 730}, + {729, 729}, + {728, 728}, + // 90 + {727, 727}, + {726, 726}, + {725, 725}, + {724, 724}, + {723, 723}, + // 95 + {722, 722}, + {721, 721}, + {720, 720}, + {719, 719}, + {718, 718}, + // 100 + {717, 717}, + {716, 716}, + {715, 715}, + {714, 714}, + {713, 713}, + // 105 + {712, 712}, + {711, 711}, + {710, 710}, + {709, 709}, + {708, 708}, + // 110 + {707, 707}, + {706, 706}, + {705, 705}, + {704, 704}, + {703, 703}, + // 115 + {702, 702}, + {701, 701}, + {700, 700}, + {699, 699}, + {698, 698}, + // 120 + {697, 697}, + {696, 696}, + {695, 695}, + {694, 694}, + {693, 693}, + // 125 + {692, 692}, + {691, 691}, + {690, 690}, + {689, 689}, + {688, 688}, + // 130 + {687, 687}, + {686, 686}, + {685, 685}, + {684, 684}, + {683, 683}, + // 135 + {682, 682}, + {681, 681}, + {680, 680}, + {679, 679}, + {678, 678}, + // 140 + {677, 677}, + {675, 675}, + {673, 673}, + {672, 672}, + {671, 671}, + // 145 + {670, 670}, + {669, 669}, + {668, 668}, + {667, 667}, + {666, 666}, + // 150 + {665, 665}, + {664, 664}, + {663, 663}, + {662, 662}, + {661, 661}, + // 155 + {660, 660}, + {659, 659}, + {658, 658}, + {657, 657}, + {656, 656}, + // 160 + {655, 655}, + {654, 654}, + {628, 628}, + {2: 571, 571, 571, 571, 571, 571, 571, 10: 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 58: 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 571, 608: 6091, 1407: 6092}, + {430, 430, 542: 430}, + // 165 + {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 531: 1052, 549: 1052, 643: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 5958}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 5955}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 5907, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5815, 1088: 5821, 1092: 5830, 1263: 5908}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 3134, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5815, 1088: 5821, 1092: 5830, 1263: 5816}, + {22: 5771, 225: 5772}, + // 170 + {559: 5736}, + {157: 5707, 225: 5728, 608: 5708, 1295: 5727}, + {157: 5707, 225: 5709, 608: 5708, 1295: 5706}, + {530: 5689, 556: 210, 1404: 5688}, + {28: 5683, 56: 5210, 159: 5684, 531: 5681, 560: 3037, 799: 5682, 999: 5685}, + // 175 + {28: 204, 56: 204, 159: 204, 275: 5680, 531: 204, 560: 204}, + {363: 5663}, + {428: 4628}, + {51: 4602}, + {134: 3034}, + // 180 + {2: 3036, 769: 3035}, + {51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 3040, 3043, 1435: 3039}, + {560: 3037, 799: 3038}, + {2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 15: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 60: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 94: 2224, 2224, 2224, 98: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 118: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 133: 2224, 137: 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 2224, 163: 2224, 2224, 2224, 2224, 213: 2224, 220: 2224, 235: 2224, 239: 2224, 263: 2224, 303: 2224, 530: 2224, 2224, 2224, 535: 2224, 537: 2224, 2224, 2224, 542: 2224, 546: 2224, 2224, 549: 2224, 2224, 2224, 2224, 2224, 2224, 556: 2224, 2224, 2224, 2224, 562: 2224, 2224, 565: 2224, 575: 2224, 577: 2224, 2224, 582: 2224, 587: 2224, 2224, 608: 2224, 614: 2224, 617: 2224, 639: 2224, 695: 2224, 709: 2224, 2224, 713: 2224, 2224, 720: 2224, 722: 2224, 803: 2224, 828: 2224, 831: 2224, 834: 2224, 2224}, + {1, 1}, + // 185 + {12, 12, 9: 4600, 51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 4599, 3043}, + {11, 11, 9: 11, 51: 11, 93: 11, 117: 11, 696: 11}, + {571: 4594}, + {229: 2316, 231: 2316, 555: 4588, 802: 4589, 933: 2316}, + {5, 5, 9: 5, 51: 5, 93: 5, 117: 5, 696: 5}, + // 190 + {198: 4580, 218: 4579}, + {218: 3046}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3592, 3597, 3679, 3596, 3593}, + {1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 4576, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 532: 1786, 1786, 1786, 536: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 549: 1786, 1786, 1786, 554: 1786, 1786, 1786, 1786, 1786, 1786, 561: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 579: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 588: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 609: 1786, 1786, 1786, 1786, 1786, 615: 1786, 1786, 618: 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 632: 1786, 1786, 1786, 1786, 696: 1786, 703: 1786, 716: 1786, 718: 1786, 1786}, + {1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 4573, 1785, 1785, 1785, 536: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 549: 1785, 1785, 1785, 554: 1785, 1785, 1785, 1785, 1785, 1785, 561: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 579: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 588: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 609: 1785, 1785, 1785, 1785, 1785, 615: 1785, 1785, 618: 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 632: 1785, 1785, 1785, 1785, 696: 1785, 703: 1785, 716: 1785, 718: 1785, 1785}, + // 195 + {2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 4570, 2090, 2090, 2090, 536: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 549: 2090, 2090, 2090, 554: 2090, 2090, 2090, 2090, 2090, 2090, 561: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 579: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 588: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 609: 2090, 2090, 2090, 2090, 2090, 615: 2090, 2090, 618: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 632: 2090, 2090, 2090, 2090, 696: 2090, 703: 2090, 716: 2090, 718: 2090, 2090}, + {2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 2089, 774: 2089}, + {2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 2088, 774: 2088}, + {2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087, 774: 2087}, + {2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 2086, 774: 2086}, + // 200 + {2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 2085, 774: 2085}, + {2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 1435, 2084, 2084, 2084, 536: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 549: 2084, 2084, 2084, 554: 2084, 2084, 2084, 2084, 2084, 2084, 561: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 579: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 588: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 609: 2084, 2084, 2084, 2084, 2084, 615: 2084, 2084, 618: 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 632: 2084, 2084, 2084, 2084, 696: 2084, 703: 2084, 716: 2084, 718: 2084, 2084}, + {2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 2083, 774: 2083}, + {2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 2082, 774: 2082}, + {2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 2081, 774: 2081}, + // 205 + {2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 2080, 774: 2080}, + {2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 2079, 774: 2079}, + {2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 2078, 774: 2078}, + {2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 2077, 774: 2077}, + {2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 2076, 774: 2076}, + // 210 + {2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 2075, 774: 2075}, + {2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074, 774: 2074}, + {2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 2073, 774: 2073}, + {2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 4565, 2072, 2072, 2072, 536: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 549: 2072, 2072, 2072, 554: 2072, 2072, 2072, 2072, 2072, 2072, 561: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 579: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 588: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 609: 2072, 2072, 2072, 2072, 2072, 615: 2072, 2072, 618: 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 632: 2072, 2072, 2072, 2072, 696: 2072, 703: 2072, 716: 2072, 718: 2072, 2072}, + {2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 2071, 774: 2071}, + // 215 + {2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 2070, 774: 2070}, + {2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 2069, 774: 2069}, + {2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 2068, 774: 2068}, + {2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 2067, 774: 2067}, + {2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 2066, 774: 2066}, + // 220 + {2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 2065, 774: 2065}, + {2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 2064, 774: 2064}, + {2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 2063, 774: 2063}, + {2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 2062, 774: 2062}, + {2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 2061, 774: 2061}, + // 225 + {2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 1434, 2060, 2060, 2060, 536: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 549: 2060, 2060, 2060, 554: 2060, 2060, 2060, 2060, 2060, 2060, 561: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 579: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 588: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 609: 2060, 2060, 2060, 2060, 2060, 615: 2060, 2060, 618: 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 632: 2060, 2060, 2060, 2060, 696: 2060, 703: 2060, 716: 2060, 718: 2060, 2060}, + {2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 2059, 774: 2059}, + {2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 2058, 774: 2058}, + {2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 2057, 774: 2057}, + {2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 2056, 774: 2056}, + // 230 + {2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 2055, 774: 2055}, + {2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 2054, 774: 2054}, + {2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 2053, 774: 2053}, + {2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 2052, 774: 2052}, + {2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 2051, 774: 2051}, + // 235 + {2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 2050, 774: 2050}, + {2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 1431, 2049, 4564, 2049, 536: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 549: 2049, 2049, 2049, 554: 2049, 2049, 2049, 2049, 2049, 2049, 561: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 579: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 588: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 609: 2049, 2049, 2049, 2049, 2049, 615: 2049, 2049, 618: 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 632: 2049, 2049, 2049, 2049, 696: 2049, 703: 2049, 716: 2049, 718: 2049, 2049}, + {2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 774: 2048}, + {2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 1429, 2047, 2047, 2047, 536: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 549: 2047, 2047, 2047, 554: 2047, 2047, 2047, 2047, 2047, 2047, 561: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 579: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 588: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 609: 2047, 2047, 2047, 2047, 2047, 615: 2047, 2047, 618: 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 632: 2047, 2047, 2047, 2047, 696: 2047, 703: 2047, 716: 2047, 718: 2047, 2047}, + {2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 2046, 774: 2046}, + // 240 + {2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 2045, 774: 2045}, + {2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 2044, 774: 2044}, + {2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043, 774: 2043}, + {2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 2042, 774: 2042}, + {2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 2041, 774: 2041}, + // 245 + {2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 2040, 774: 2040}, + {2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 774: 2039}, + {2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 774: 2038}, + {2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 2037, 774: 2037}, + {2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 774: 2036}, + // 250 + {2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 774: 2035}, + {2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 2034, 774: 2034}, + {2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 774: 2033}, + {2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 774: 2032}, + {2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 2031, 774: 2031}, + // 255 + {2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 774: 2030}, + {2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 774: 2029}, + {2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 2028, 774: 2028}, + {2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 774: 2027}, + {2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 774: 2026}, + // 260 + {2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 774: 2025}, + {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 1425, 2024, 2024, 2024, 536: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 549: 2024, 2024, 2024, 554: 2024, 2024, 2024, 2024, 2024, 2024, 561: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 579: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 588: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 609: 2024, 2024, 2024, 2024, 2024, 615: 2024, 2024, 618: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 632: 2024, 2024, 2024, 2024, 696: 2024, 703: 2024, 716: 2024, 718: 2024, 2024}, + {2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 774: 2023}, + {2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 2022, 774: 2022}, + {2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 2021, 774: 2021}, + // 265 + {2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 774: 2020}, + {2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 774: 2019}, + {2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 1428, 2018, 2018, 2018, 536: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 549: 2018, 2018, 2018, 554: 2018, 2018, 2018, 2018, 2018, 2018, 561: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 579: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 588: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 609: 2018, 2018, 2018, 2018, 2018, 615: 2018, 2018, 618: 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 632: 2018, 2018, 2018, 2018, 696: 2018, 703: 2018, 716: 2018, 718: 2018, 2018}, + {2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 774: 2017}, + {2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 2016, 774: 2016}, + // 270 + {2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 774: 2015}, + {2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 2014, 774: 2014}, + {2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 774: 2013}, + {2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 2012, 774: 2012}, + {2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 774: 2011}, + // 275 + {2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 2010, 774: 2010}, + {2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 774: 2009}, + {2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 4561, 2008, 2008, 2008, 536: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 549: 2008, 2008, 2008, 554: 2008, 2008, 2008, 2008, 2008, 2008, 561: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 579: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 588: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 609: 2008, 2008, 2008, 2008, 2008, 615: 2008, 2008, 618: 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 632: 2008, 2008, 2008, 2008, 696: 2008, 703: 2008, 716: 2008, 718: 2008, 2008}, + {2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 774: 2007}, + {2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 774: 2006}, + // 280 + {2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 2005, 774: 2005}, + {2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 2004, 774: 2004}, + {2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 2003, 774: 2003}, + {2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 2002, 774: 2002}, + {2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 2001, 774: 2001}, + // 285 + {2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 774: 2000}, + {1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 1999, 774: 1999}, + {1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 1998, 774: 1998}, + {1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 1997, 774: 1997}, + {1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 1996, 774: 1996}, + // 290 + {1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 1995, 774: 1995}, + {1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 1994, 774: 1994}, + {1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 1993, 774: 1993}, + {1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 1992, 774: 1992}, + {1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 1991, 774: 1991}, + // 295 + {1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990, 774: 1990}, + {1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 1989, 774: 1989}, + {1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 1988, 774: 1988}, + {1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1419, 1987, 1987, 1987, 536: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 549: 1987, 1987, 1987, 554: 1987, 1987, 1987, 1987, 1987, 1987, 561: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 579: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 588: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 609: 1987, 1987, 1987, 1987, 1987, 615: 1987, 1987, 618: 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 632: 1987, 1987, 1987, 1987, 696: 1987, 703: 1987, 716: 1987, 718: 1987, 1987}, + {1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 1986, 774: 1986}, + // 300 + {1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985, 774: 1985}, + {1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 1984, 774: 1984}, + {1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 1983, 774: 1983}, + {1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 1982, 774: 1982}, + {1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 1981, 774: 1981}, + // 305 + {1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1411, 1980, 4560, 1980, 536: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 549: 1980, 1980, 1980, 554: 1980, 1980, 1980, 1980, 1980, 1980, 561: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 579: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 588: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 609: 1980, 1980, 1980, 1980, 1980, 615: 1980, 1980, 618: 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 632: 1980, 1980, 1980, 1980, 696: 1980, 703: 1980, 716: 1980, 718: 1980, 1980}, + {1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1410, 1979, 4559, 1979, 536: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 549: 1979, 1979, 1979, 554: 1979, 1979, 1979, 1979, 1979, 1979, 561: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 579: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 588: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 609: 1979, 1979, 1979, 1979, 1979, 615: 1979, 1979, 618: 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 632: 1979, 1979, 1979, 1979, 696: 1979, 703: 1979, 716: 1979, 718: 1979, 1979}, + {1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 1978, 774: 1978}, + {1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977, 774: 1977}, + {1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1409, 1976, 1976, 1976, 536: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 549: 1976, 1976, 1976, 554: 1976, 1976, 1976, 1976, 1976, 1976, 561: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 579: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 588: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 609: 1976, 1976, 1976, 1976, 1976, 615: 1976, 1976, 618: 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 632: 1976, 1976, 1976, 1976, 696: 1976, 703: 1976, 716: 1976, 718: 1976, 1976}, + // 310 + {1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 1975, 774: 1975}, + {1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 1974, 774: 1974}, + {1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973, 774: 1973}, + {1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 1972, 774: 1972}, + {1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1406, 1971, 1971, 1971, 536: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 549: 1971, 1971, 1971, 554: 1971, 1971, 1971, 1971, 1971, 1971, 561: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 579: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 588: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 609: 1971, 1971, 1971, 1971, 1971, 615: 1971, 1971, 618: 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 632: 1971, 1971, 1971, 1971, 696: 1971, 703: 1971, 716: 1971, 718: 1971, 1971}, + // 315 + {1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 1970, 774: 1970}, + {1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1407, 1969, 1969, 1969, 536: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 549: 1969, 1969, 1969, 554: 1969, 1969, 1969, 1969, 1969, 1969, 561: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 579: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 588: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 609: 1969, 1969, 1969, 1969, 1969, 615: 1969, 1969, 618: 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 632: 1969, 1969, 1969, 1969, 696: 1969, 703: 1969, 716: 1969, 718: 1969, 1969}, + {1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 4549, 1968, 1968, 1968, 536: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 549: 1968, 1968, 1968, 554: 1968, 1968, 1968, 1968, 1968, 1968, 561: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 579: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 588: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 609: 1968, 1968, 1968, 1968, 1968, 615: 1968, 1968, 618: 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 632: 1968, 1968, 1968, 1968, 696: 1968, 703: 1968, 716: 1968, 718: 1968, 1968}, + {1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 774: 1967}, + {1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 774: 1966}, + // 320 + {1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1408, 1965, 1965, 1965, 536: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 549: 1965, 1965, 1965, 554: 1965, 1965, 1965, 1965, 1965, 1965, 561: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 579: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 588: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 609: 1965, 1965, 1965, 1965, 1965, 615: 1965, 1965, 618: 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 632: 1965, 1965, 1965, 1965, 696: 1965, 703: 1965, 716: 1965, 718: 1965, 1965}, + {1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 1964, 774: 1964}, + {1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1432, 1963, 1963, 1963, 536: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 549: 1963, 1963, 1963, 554: 1963, 1963, 1963, 1963, 1963, 1963, 561: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 579: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 588: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 609: 1963, 1963, 1963, 1963, 1963, 615: 1963, 1963, 618: 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 632: 1963, 1963, 1963, 1963, 696: 1963, 703: 1963, 716: 1963, 718: 1963, 1963}, + {1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 774: 1962}, + {1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 1961, 774: 1961}, + // 325 + {1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 1960, 774: 1960}, + {1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 1959, 774: 1959}, + {1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 1958, 774: 1958}, + {1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 1957, 774: 1957}, + {1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 1956, 774: 1956}, + // 330 + {1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 1955, 774: 1955}, + {1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 1954, 774: 1954}, + {1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 1953, 774: 1953}, + {1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 1952, 774: 1952}, + {1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 1951, 774: 1951}, + // 335 + {1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1418, 1950, 1950, 1950, 536: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 549: 1950, 1950, 1950, 554: 1950, 1950, 1950, 1950, 1950, 1950, 561: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 579: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 588: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 609: 1950, 1950, 1950, 1950, 1950, 615: 1950, 1950, 618: 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 632: 1950, 1950, 1950, 1950, 696: 1950, 703: 1950, 716: 1950, 718: 1950, 1950}, + {1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949, 774: 1949}, + {1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 1948, 774: 1948}, + {1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 1947, 774: 1947}, + {1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 1946, 774: 1946}, + // 340 + {1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945, 774: 1945}, + {1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 774: 1944}, + {1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 1943, 774: 1943}, + {1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 774: 1942}, + {1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 1941, 774: 1941}, + // 345 + {1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 1940, 774: 1940}, + {1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 1939, 774: 1939}, + {1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 1938, 774: 1938}, + {1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 1937, 774: 1937}, + {1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936, 774: 1936}, + // 350 + {1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 1935, 774: 1935}, + {1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 1934, 774: 1934}, + {1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 1933, 774: 1933}, + {1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932, 774: 1932}, + {1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 1931, 774: 1931}, + // 355 + {1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 1930, 774: 1930}, + {1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 1929, 774: 1929}, + {1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 1928, 774: 1928}, + {1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1415, 1927, 1927, 1927, 536: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 549: 1927, 1927, 1927, 554: 1927, 1927, 1927, 1927, 1927, 1927, 561: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 579: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 588: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 609: 1927, 1927, 1927, 1927, 1927, 615: 1927, 1927, 618: 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 632: 1927, 1927, 1927, 1927, 696: 1927, 703: 1927, 716: 1927, 718: 1927, 1927}, + {1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 1926, 774: 1926}, + // 360 + {1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 1925, 774: 1925}, + {1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924, 774: 1924}, + {1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 1923, 774: 1923}, + {1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 1922, 774: 1922}, + {1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, 774: 1921}, + // 365 + {1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 774: 1920}, + {1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 1919, 774: 1919}, + {1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 1918, 774: 1918}, + {1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 1917, 774: 1917}, + {1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 1916, 774: 1916}, + // 370 + {1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 1915, 774: 1915}, + {1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 1914, 774: 1914}, + {1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 1913, 774: 1913}, + {1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912, 774: 1912}, + {1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 1911, 774: 1911}, + // 375 + {1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1413, 1910, 1910, 1910, 536: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 549: 1910, 1910, 1910, 554: 1910, 1910, 1910, 1910, 1910, 1910, 561: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 579: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 588: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 609: 1910, 1910, 1910, 1910, 1910, 615: 1910, 1910, 618: 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 632: 1910, 1910, 1910, 1910, 696: 1910, 703: 1910, 716: 1910, 718: 1910, 1910}, + {1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1433, 1909, 1909, 1909, 536: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 549: 1909, 1909, 1909, 554: 1909, 1909, 1909, 1909, 1909, 1909, 561: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 579: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 588: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 609: 1909, 1909, 1909, 1909, 1909, 615: 1909, 1909, 618: 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 632: 1909, 1909, 1909, 1909, 696: 1909, 703: 1909, 716: 1909, 718: 1909, 1909}, + {1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1421, 1908, 1908, 1908, 536: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 549: 1908, 1908, 1908, 554: 1908, 1908, 1908, 1908, 1908, 1908, 561: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 579: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 588: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 609: 1908, 1908, 1908, 1908, 1908, 615: 1908, 1908, 618: 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 632: 1908, 1908, 1908, 1908, 696: 1908, 703: 1908, 716: 1908, 718: 1908, 1908}, + {1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 1907, 774: 1907}, + {1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906, 774: 1906}, + // 380 + {1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 1905, 774: 1905}, + {1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1423, 1904, 1904, 1904, 536: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 549: 1904, 1904, 1904, 554: 1904, 1904, 1904, 1904, 1904, 1904, 561: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 579: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 588: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 609: 1904, 1904, 1904, 1904, 1904, 615: 1904, 1904, 618: 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 632: 1904, 1904, 1904, 1904, 696: 1904, 703: 1904, 716: 1904, 718: 1904, 1904}, + {1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1422, 1903, 1903, 1903, 536: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 549: 1903, 1903, 1903, 554: 1903, 1903, 1903, 1903, 1903, 1903, 561: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 579: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 588: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 609: 1903, 1903, 1903, 1903, 1903, 615: 1903, 1903, 618: 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 632: 1903, 1903, 1903, 1903, 696: 1903, 703: 1903, 716: 1903, 718: 1903, 1903}, + {1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902, 774: 1902}, + {1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 1901, 774: 1901}, + // 385 + {1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 1900, 774: 1900}, + {1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 1899, 774: 1899}, + {1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 1898, 774: 1898}, + {1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1412, 1897, 1897, 1897, 536: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 549: 1897, 1897, 1897, 554: 1897, 1897, 1897, 1897, 1897, 1897, 561: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 579: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 588: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 609: 1897, 1897, 1897, 1897, 1897, 615: 1897, 1897, 618: 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 632: 1897, 1897, 1897, 1897, 696: 1897, 703: 1897, 716: 1897, 718: 1897, 1897}, + {1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 1896, 774: 1896}, + // 390 + {1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 1895, 774: 1895}, + {1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 1894, 774: 1894}, + {1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 1893, 774: 1893}, + {1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 1892, 774: 1892}, + {1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 1891, 774: 1891}, + // 395 + {1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 1890, 774: 1890}, + {1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 1889, 774: 1889}, + {1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 1888, 774: 1888}, + {1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887, 774: 1887}, + {1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 1886, 774: 1886}, + // 400 + {1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 1885, 774: 1885}, + {1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 1884, 774: 1884}, + {1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883, 774: 1883}, + {1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 1882, 774: 1882}, + {1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 1881, 774: 1881}, + // 405 + {1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 1880, 774: 1880}, + {1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 1879, 774: 1879}, + {1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 1878, 774: 1878}, + {1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 1877, 774: 1877}, + {1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876, 774: 1876}, + // 410 + {1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 774: 1875}, + {1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 1874, 774: 1874}, + {1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 1873, 774: 1873}, + {1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 1872, 774: 1872}, + {1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 1871, 774: 1871}, + // 415 + {1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 1870, 774: 1870}, + {1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 1869, 774: 1869}, + {1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 1868, 774: 1868}, + {1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867, 774: 1867}, + {1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 1866, 774: 1866}, + // 420 + {1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 1865, 774: 1865}, + {1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 1864, 774: 1864}, + {1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 1863, 774: 1863}, + {1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 1862, 774: 1862}, + {1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 1861, 774: 1861}, + // 425 + {1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 1860, 774: 1860}, + {1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 1859, 774: 1859}, + {1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 1858, 774: 1858}, + {1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 1857, 774: 1857}, + {1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 1856, 774: 1856}, + // 430 + {1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 1855, 774: 1855}, + {1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 1854, 774: 1854}, + {1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853, 774: 1853}, + {1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 1852, 774: 1852}, + {1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 1851, 774: 1851}, + // 435 + {1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 1850, 774: 1850}, + {1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 1849, 774: 1849}, + {1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 1848, 774: 1848}, + {1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 1847, 774: 1847}, + {1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846, 774: 1846}, + // 440 + {1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 1845, 774: 1845}, + {1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 1844, 774: 1844}, + {1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 1843, 774: 1843}, + {1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 774: 1842}, + {1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 1841, 774: 1841}, + // 445 + {1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 1840, 774: 1840}, + {1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 1839, 774: 1839}, + {1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 1838, 774: 1838}, + {1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 774: 1837}, + {1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 1836, 774: 1836}, + // 450 + {1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 1835, 774: 1835}, + {1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 1834, 774: 1834}, + {1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 1833, 774: 1833}, + {1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832, 774: 1832}, + {1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 1831, 774: 1831}, + // 455 + {1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 1830, 774: 1830}, + {1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 1829, 774: 1829}, + {1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 774: 1828}, + {1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 1827, 774: 1827}, + {1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 1826, 774: 1826}, + // 460 + {1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 1825, 774: 1825}, + {1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 1824, 774: 1824}, + {1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 1823, 774: 1823}, + {1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 1822, 774: 1822}, + {1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 1821, 774: 1821}, + // 465 + {1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 1820, 774: 1820}, + {1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 1819, 774: 1819}, + {1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 1818, 774: 1818}, + {1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 1817, 774: 1817}, + {1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 1816, 774: 1816}, + // 470 + {1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 1815, 774: 1815}, + {1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 1814, 774: 1814}, + {1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 1813, 774: 1813}, + {1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 1812, 774: 1812}, + {1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 1811, 774: 1811}, + // 475 + {1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 1810, 774: 1810}, + {1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 1809, 774: 1809}, + {1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 1808, 774: 1808}, + {1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 1807, 774: 1807}, + {1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 1806, 774: 1806}, + // 480 + {1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 1805, 774: 1805}, + {1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 1804, 774: 1804}, + {1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 1803, 774: 1803}, + {1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 1802, 774: 1802}, + {1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 1801, 774: 1801}, + // 485 + {1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 774: 1800}, + {1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 1799, 774: 1799}, + {1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 1798, 774: 1798}, + {1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 1797, 774: 1797}, + {1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 1796, 774: 1796}, + // 490 + {1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 1795, 774: 1795}, + {1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 1794, 774: 1794}, + {1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 1793, 774: 1793}, + {1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 1792, 774: 1792}, + {1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 1791, 774: 1791}, + // 495 + {1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 1790, 774: 1790}, + {1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 1789, 774: 1789}, + {1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 1788, 774: 1788}, + {1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 1787, 774: 1787}, + {1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 4546, 1784, 1784, 1784, 536: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 549: 1784, 1784, 1784, 554: 1784, 1784, 1784, 1784, 1784, 1784, 561: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 579: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 588: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 609: 1784, 1784, 1784, 1784, 1784, 615: 1784, 1784, 618: 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 632: 1784, 1784, 1784, 1784, 696: 1784, 703: 1784, 716: 1784, 718: 1784, 1784}, + // 500 + {1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 4535, 1783, 1783, 1783, 536: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 549: 1783, 1783, 1783, 554: 1783, 1783, 1783, 1783, 1783, 1783, 561: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 579: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 588: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 609: 1783, 1783, 1783, 1783, 1783, 615: 1783, 1783, 618: 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 632: 1783, 1783, 1783, 1783, 696: 1783, 703: 1783, 716: 1783, 718: 1783, 1783}, + {1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 1782, 774: 1782}, + {1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 1781, 774: 1781}, + {1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 1780, 774: 1780}, + {1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 1779, 774: 1779}, + // 505 + {1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778, 774: 1778}, + {1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 1777, 774: 1777}, + {1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 1776, 774: 1776}, + {1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 1775, 774: 1775}, + {1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 1774, 774: 1774}, + // 510 + {1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 1773, 774: 1773}, + {1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 1772, 774: 1772}, + {1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 1771, 774: 1771}, + {1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770, 774: 1770}, + {1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 1769, 774: 1769}, + // 515 + {1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 1768, 774: 1768}, + {1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 1767, 774: 1767}, + {1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766, 774: 1766}, + {1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 1765, 774: 1765}, + {1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 1764, 774: 1764}, + // 520 + {1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 1763, 774: 1763}, + {1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 1762, 774: 1762}, + {1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 1761, 774: 1761}, + {1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 1760, 774: 1760}, + {1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 1759, 774: 1759}, + // 525 + {1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 1758, 774: 1758}, + {1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 1757, 774: 1757}, + {1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 1756, 774: 1756}, + {1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 1755, 774: 1755}, + {1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 1754, 774: 1754}, + // 530 + {1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 1753, 774: 1753}, + {1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 1752, 774: 1752}, + {1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 1751, 774: 1751}, + {1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, 774: 1750}, + {1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749, 774: 1749}, + // 535 + {1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 1748, 774: 1748}, + {1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 1747, 774: 1747}, + {1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 1746, 774: 1746}, + {1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745, 774: 1745}, + {1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 1744, 774: 1744}, + // 540 + {1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 1743, 774: 1743}, + {1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 1742, 774: 1742}, + {1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741, 774: 1741}, + {1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 1740, 774: 1740}, + {1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 1739, 774: 1739}, + // 545 + {1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 1738, 774: 1738}, + {1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 1737, 774: 1737}, + {1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 1736, 774: 1736}, + {1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 1735, 774: 1735}, + {1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 1734, 774: 1734}, + // 550 + {1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 1733, 774: 1733}, + {1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 1732, 774: 1732}, + {1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 1731, 774: 1731}, + {1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 1730, 774: 1730}, + {1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 1729, 774: 1729}, + // 555 + {1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 774: 1728}, + {1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727, 774: 1727}, + {1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 1726, 774: 1726}, + {1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 1725, 774: 1725}, + {1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 1724, 774: 1724}, + // 560 + {1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 1723, 774: 1723}, + {1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 1722, 774: 1722}, + {1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721, 774: 1721}, + {1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 1720, 774: 1720}, + {1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 1719, 774: 1719}, + // 565 + {1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 1718, 774: 1718}, + {1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 1717, 774: 1717}, + {1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 1716, 774: 1716}, + {1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 1715, 774: 1715}, + {1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 1714, 774: 1714}, + // 570 + {1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 1713, 774: 1713}, + {1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 1712, 774: 1712}, + {1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 1711, 774: 1711}, + {1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 1710, 774: 1710}, + {1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 1709, 774: 1709}, + // 575 + {1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 1708, 774: 1708}, + {1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, 774: 1707}, + {1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 1706, 774: 1706}, + {1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 1705, 774: 1705}, + {1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 1704, 774: 1704}, + // 580 + {1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 1703, 774: 1703}, + {1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 1702, 774: 1702}, + {1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 1701, 774: 1701}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 774: 1700}, + {1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 1699, 774: 1699}, + // 585 + {1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 1698, 774: 1698}, + {1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 1697, 774: 1697}, + {1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696, 774: 1696}, + {1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 1695, 774: 1695}, + {1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 1694, 774: 1694}, + // 590 + {1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 1693, 774: 1693}, + {1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 1692, 774: 1692}, + {1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 1691, 774: 1691}, + {1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 1690, 774: 1690}, + {1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689, 774: 1689}, + // 595 + {1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 1688, 774: 1688}, + {1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 1687, 774: 1687}, + {1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 1686, 774: 1686}, + {1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685, 774: 1685}, + {1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 1684, 774: 1684}, + // 600 + {1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 1683, 774: 1683}, + {1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682, 774: 1682}, + {1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 1681, 774: 1681}, + {1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 1680, 774: 1680}, + {1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 1679, 774: 1679}, + // 605 + {1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 1678, 774: 1678}, + {1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1350, 1677, 1677, 1677, 536: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 549: 1677, 1677, 1677, 554: 1677, 1677, 1677, 1677, 1677, 1677, 561: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 579: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 588: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 609: 1677, 1677, 1677, 1677, 1677, 615: 1677, 1677, 618: 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 632: 1677, 1677, 1677, 1677, 696: 1677, 703: 1677, 716: 1677, 718: 1677, 1677}, + {1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 1676, 774: 1676}, + {1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675, 774: 1675}, + {1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 1674, 774: 1674}, + // 610 + {1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 1673, 774: 1673}, + {1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 1672, 774: 1672}, + {1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671, 774: 1671}, + {1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 1670, 774: 1670}, + {1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 1669, 774: 1669}, + // 615 + {1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 1668, 774: 1668}, + {1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 1667, 774: 1667}, + {1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 1666, 774: 1666}, + {1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 1665, 774: 1665}, + {1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 1664, 774: 1664}, + // 620 + {1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 1663, 774: 1663}, + {1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662, 774: 1662}, + {1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 1661, 774: 1661}, + {1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 1660, 774: 1660}, + {1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 1659, 774: 1659}, + // 625 + {1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 4526, 1658, 1658, 1658, 536: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 549: 1658, 1658, 1658, 554: 1658, 1658, 1658, 1658, 1658, 1658, 561: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 579: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 588: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 609: 1658, 1658, 1658, 1658, 1658, 615: 1658, 1658, 618: 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 632: 1658, 1658, 1658, 1658, 696: 1658, 703: 1658, 716: 1658, 718: 1658, 1658}, + {1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 1657, 774: 1657}, + {1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 1656, 774: 1656}, + {1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 1655, 774: 1655}, + {1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 1654, 774: 1654}, + // 630 + {1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653, 774: 1653}, + {1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 1652, 774: 1652}, + {1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 1651, 774: 1651}, + {1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 1650, 774: 1650}, + {1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 1649, 774: 1649}, + // 635 + {1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648, 774: 1648}, + {1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 1647, 774: 1647}, + {1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 1646, 774: 1646}, + {1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 1645, 774: 1645}, + {1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 1644, 774: 1644}, + // 640 + {1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 1643, 774: 1643}, + {1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 1642, 774: 1642}, + {1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 1641, 774: 1641}, + {1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1349, 1640, 1640, 1640, 536: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 549: 1640, 1640, 1640, 554: 1640, 1640, 1640, 1640, 1640, 1640, 561: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 579: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 588: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 609: 1640, 1640, 1640, 1640, 1640, 615: 1640, 1640, 618: 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 632: 1640, 1640, 1640, 1640, 696: 1640, 703: 1640, 716: 1640, 718: 1640, 1640}, + {1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 1639, 774: 1639}, + // 645 + {1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 1638, 774: 1638}, + {1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 1637, 774: 1637}, + {1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 1636, 774: 1636}, + {1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635, 774: 1635}, + {1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 1634, 774: 1634}, + // 650 + {1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 1633, 774: 1633}, + {1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 1632, 774: 1632}, + {1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 1631, 774: 1631}, + {1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 1630, 774: 1630}, + {1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 1629, 774: 1629}, + // 655 + {1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628, 774: 1628}, + {1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 4519, 1627, 1627, 1627, 536: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 549: 1627, 1627, 1627, 554: 1627, 1627, 1627, 1627, 1627, 1627, 561: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 579: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 588: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 609: 1627, 1627, 1627, 1627, 1627, 615: 1627, 1627, 618: 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 632: 1627, 1627, 1627, 1627, 696: 1627, 703: 1627, 716: 1627, 718: 1627, 1627}, + {1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 4512, 1626, 1626, 1626, 536: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 549: 1626, 1626, 1626, 554: 1626, 1626, 1626, 1626, 1626, 1626, 561: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 579: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 588: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 609: 1626, 1626, 1626, 1626, 1626, 615: 1626, 1626, 618: 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 632: 1626, 1626, 1626, 1626, 696: 1626, 703: 1626, 716: 1626, 718: 1626, 1626}, + {1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 1625, 774: 1625}, + {1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624, 774: 1624}, + // 660 + {1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 1623, 774: 1623}, + {1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622, 774: 1622}, + {1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 1621, 774: 1621}, + {1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 1620, 774: 1620}, + {1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 1619, 774: 1619}, + // 665 + {1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 1618, 774: 1618}, + {1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 1617, 774: 1617}, + {1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 1616, 774: 1616}, + {1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 774: 1615}, + {1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614, 774: 1614}, + // 670 + {1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 1613, 774: 1613}, + {1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 1612, 774: 1612}, + {1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 1611, 774: 1611}, + {1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 1610, 774: 1610}, + {1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 1609, 774: 1609}, + // 675 + {1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 1608, 774: 1608}, + {1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 1607, 774: 1607}, + {1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 4492, 1606, 1606, 1606, 536: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 549: 1606, 1606, 1606, 554: 1606, 1606, 1606, 1606, 1606, 1606, 561: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 579: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 588: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 609: 1606, 1606, 1606, 1606, 1606, 615: 1606, 1606, 618: 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 632: 1606, 1606, 1606, 1606, 696: 1606, 703: 1606, 716: 1606, 718: 1606, 1606}, + {1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 4484, 1605, 1605, 1605, 536: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 549: 1605, 1605, 1605, 554: 1605, 1605, 1605, 1605, 1605, 1605, 561: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 579: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 588: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 609: 1605, 1605, 1605, 1605, 1605, 615: 1605, 1605, 618: 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 632: 1605, 1605, 1605, 1605, 696: 1605, 703: 1605, 716: 1605, 718: 1605, 1605}, + {1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 1604, 774: 1604}, + // 680 + {1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 1603, 774: 1603}, + {1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 1602, 774: 1602}, + {1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 1601, 774: 1601}, + {1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 774: 1600}, + {1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 1599, 774: 1599}, + // 685 + {1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 1598, 774: 1598}, + {1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 1597, 774: 1597}, + {1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 1596, 774: 1596}, + {1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595, 774: 1595}, + {1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 1594, 774: 1594}, + // 690 + {1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 1593, 774: 1593}, + {1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 1592, 774: 1592}, + {1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591, 774: 1591}, + {1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 1590, 774: 1590}, + {1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 1589, 774: 1589}, + // 695 + {1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 1588, 774: 1588}, + {1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 1587, 774: 1587}, + {1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 1586, 774: 1586}, + {1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 1585, 774: 1585}, + {1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584, 774: 1584}, + // 700 + {1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 1583, 774: 1583}, + {1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 1582, 774: 1582}, + {1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 1581, 774: 1581}, + {1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 1580, 774: 1580}, + {1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, 774: 1579}, + // 705 + {1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 1578, 774: 1578}, + {1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 1577, 774: 1577}, + {1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 1576, 774: 1576}, + {1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 1575, 774: 1575}, + {1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 1574, 774: 1574}, + // 710 + {1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 1573, 774: 1573}, + {1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 1572, 774: 1572}, + {1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 1571, 774: 1571}, + {1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 1570, 774: 1570}, + {1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 1569, 774: 1569}, + // 715 + {1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 1568, 774: 1568}, + {1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 1567, 774: 1567}, + {1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 1566, 774: 1566}, + {1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565, 774: 1565}, + {1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 1564, 774: 1564}, + // 720 + {1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 1563, 774: 1563}, + {1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 1562, 774: 1562}, + {1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 532: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 548: 1526, 1526, 1526, 1526, 554: 1526, 1526, 1526, 1526, 1526, 1526, 561: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 579: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 588: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 609: 1526, 1526, 1526, 1526, 1526, 615: 1526, 1526, 618: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526, 632: 1526, 1526, 1526, 1526, 696: 1526, 701: 1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526}, + {1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 532: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 548: 1525, 1525, 1525, 1525, 554: 1525, 1525, 1525, 1525, 1525, 1525, 561: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 579: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 588: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 609: 1525, 1525, 1525, 1525, 1525, 615: 1525, 1525, 618: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525, 632: 1525, 1525, 1525, 1525, 696: 1525, 701: 1525, 1525, 1525, 1525, 1525, 1525, 1525, 1525}, + {1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 532: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 548: 1524, 1524, 1524, 1524, 554: 1524, 1524, 1524, 1524, 1524, 1524, 561: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 579: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 588: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 609: 1524, 1524, 1524, 1524, 1524, 615: 1524, 1524, 618: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524, 632: 1524, 1524, 1524, 1524, 696: 1524, 701: 1524, 1524, 1524, 1524, 1524, 1524, 1524, 1524}, + // 725 + {1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 532: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 548: 1523, 1523, 1523, 1523, 554: 1523, 1523, 1523, 1523, 1523, 1523, 561: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 579: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 588: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 609: 1523, 1523, 1523, 1523, 1523, 615: 1523, 1523, 618: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523, 632: 1523, 1523, 1523, 1523, 696: 1523, 701: 1523, 1523, 1523, 1523, 1523, 1523, 1523, 1523}, + {1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 532: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 548: 1522, 1522, 1522, 1522, 554: 1522, 1522, 1522, 1522, 1522, 1522, 561: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 579: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 588: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 609: 1522, 1522, 1522, 1522, 1522, 615: 1522, 1522, 618: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522, 632: 1522, 1522, 1522, 1522, 696: 1522, 701: 1522, 1522, 1522, 1522, 1522, 1522, 1522, 1522}, + {1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 532: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 548: 1521, 1521, 1521, 1521, 554: 1521, 1521, 1521, 1521, 1521, 1521, 561: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 579: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 588: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 609: 1521, 1521, 1521, 1521, 1521, 615: 1521, 1521, 618: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521, 632: 1521, 1521, 1521, 1521, 696: 1521, 701: 1521, 1521, 1521, 1521, 1521, 1521, 1521, 1521}, + {1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 532: 1520, 4483, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 548: 1520, 1520, 1520, 1520, 554: 1520, 1520, 1520, 1520, 1520, 1520, 561: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 579: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 588: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 609: 1520, 1520, 1520, 1520, 1520, 615: 1520, 1520, 618: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520, 632: 1520, 1520, 1520, 1520, 696: 1520, 701: 1520, 1520, 1520, 1520, 1520, 1520, 1520, 1520}, + {533: 4480, 638: 4481, 640: 4482}, + // 730 + {1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 532: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 548: 1518, 1518, 1518, 1518, 554: 1518, 1518, 1518, 1518, 1518, 1518, 561: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 579: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 588: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 609: 1518, 1518, 1518, 1518, 1518, 615: 1518, 1518, 618: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518, 632: 1518, 1518, 1518, 1518, 696: 1518, 701: 1518, 1518, 1518, 1518, 1518, 1518, 1518, 1518}, + {1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 532: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 548: 1517, 1517, 1517, 1517, 554: 1517, 1517, 1517, 1517, 1517, 1517, 561: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 579: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 588: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 609: 1517, 1517, 1517, 1517, 1517, 615: 1517, 1517, 618: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517, 632: 1517, 1517, 1517, 1517, 696: 1517, 701: 1517, 1517, 1517, 1517, 1517, 1517, 1517, 1517}, + {1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 532: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 548: 1514, 1514, 1514, 1514, 554: 1514, 1514, 1514, 1514, 1514, 1514, 561: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 579: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 588: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 609: 1514, 1514, 1514, 1514, 1514, 615: 1514, 1514, 618: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514, 632: 1514, 1514, 1514, 1514, 696: 1514, 701: 1514, 1514, 1514, 1514, 1514, 1514, 1514, 1514}, + {1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 532: 1481, 1481, 1481, 536: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 549: 1481, 1481, 1481, 554: 1481, 1481, 1481, 1481, 1481, 1481, 561: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 579: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 588: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 609: 1481, 1481, 1481, 1481, 1481, 615: 1481, 1481, 618: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 632: 1481, 1481, 1481, 1481, 696: 1481, 703: 1481, 716: 4475, 718: 1481, 1481}, + {1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 532: 1478, 1478, 1478, 536: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 549: 1478, 1478, 1478, 554: 1478, 1478, 1478, 1478, 1478, 1478, 561: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 579: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 588: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 609: 1478, 1478, 1478, 1478, 1478, 615: 1478, 1478, 618: 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 1478, 632: 1478, 1478, 1478, 1478, 696: 1478, 703: 1478, 718: 4471, 4472}, + // 735 + {1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 532: 1477, 1477, 1477, 536: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 549: 1477, 1477, 1477, 554: 1477, 1477, 1477, 1477, 1477, 1477, 561: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 579: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 588: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 609: 1477, 1477, 1477, 1477, 1477, 615: 1477, 1477, 618: 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 1477, 632: 1477, 1477, 1477, 1477, 696: 1477, 703: 1477}, + {1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 532: 1476, 1476, 1476, 536: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 549: 1476, 1476, 1476, 554: 1476, 1476, 1476, 1476, 1476, 1476, 561: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 579: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 588: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 609: 1476, 1476, 1476, 1476, 1476, 615: 1476, 1476, 618: 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 1476, 632: 1476, 1476, 1476, 1476, 696: 1476, 703: 1476}, + {1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 532: 1475, 1475, 1475, 536: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 549: 1475, 1475, 1475, 554: 1475, 1475, 1475, 1475, 1475, 1475, 561: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 579: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 588: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 609: 1475, 1475, 1475, 1475, 1475, 615: 1475, 1475, 618: 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475, 632: 1475, 1475, 1475, 1475, 696: 1475, 703: 1475}, + {3, 3, 9: 3, 51: 3, 93: 3, 117: 3, 538: 3693, 696: 3, 703: 3694}, + {1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 532: 1473, 1473, 1473, 536: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 549: 1473, 1473, 1473, 554: 1473, 1473, 1473, 1473, 1473, 1473, 561: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 579: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 588: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 609: 1473, 1473, 1473, 1473, 1473, 615: 1473, 1473, 618: 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 1473, 632: 1473, 1473, 1473, 1473, 696: 1473, 703: 1473}, + // 740 + {1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 532: 1472, 1472, 1472, 536: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 549: 1472, 1472, 1472, 554: 1472, 1472, 1472, 1472, 1472, 1472, 561: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 579: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 588: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 609: 1472, 1472, 1472, 1472, 1472, 615: 1472, 1472, 618: 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 1472, 632: 1472, 1472, 1472, 1472, 696: 1472, 703: 1472}, + {1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 532: 1471, 1471, 1471, 536: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 549: 1471, 1471, 1471, 554: 1471, 1471, 1471, 1471, 1471, 1471, 561: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 579: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 588: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 609: 1471, 1471, 1471, 1471, 1471, 615: 1471, 1471, 618: 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 1471, 632: 1471, 1471, 1471, 1471, 696: 1471, 703: 1471}, + {1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 532: 1470, 1470, 1470, 536: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 549: 1470, 1470, 1470, 554: 1470, 1470, 1470, 1470, 1470, 1470, 561: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 579: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 588: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 609: 1470, 1470, 1470, 1470, 1470, 615: 1470, 1470, 618: 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 1470, 632: 1470, 1470, 1470, 1470, 696: 1470, 703: 1470}, + {1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 532: 1469, 1469, 1469, 536: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 549: 1469, 1469, 1469, 554: 1469, 1469, 1469, 1469, 1469, 1469, 561: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 579: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 588: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 609: 1469, 1469, 1469, 1469, 1469, 615: 1469, 1469, 618: 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 1469, 632: 1469, 1469, 1469, 1469, 696: 1469, 703: 1469}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4470, 3597, 3679, 3596, 3593}, + // 745 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4469, 3597, 3679, 3596, 3593}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4468, 3597, 3679, 3596, 3593}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4467, 3597, 3679, 3596, 3593}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4466, 3597, 3679, 3596, 3593}, + {1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 532: 1462, 1462, 1462, 536: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 549: 1462, 1462, 1462, 554: 1462, 1462, 1462, 1462, 1462, 1462, 561: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 579: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 588: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 609: 1462, 1462, 1462, 1462, 1462, 615: 1462, 1462, 618: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 632: 1462, 1462, 1462, 1462, 696: 1462, 703: 1462}, + // 750 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 2906, 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3845, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 608: 2904, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 2900, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3844, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4460, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 863: 4461}, + {531: 4455}, + {531: 2907, 775: 4454}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4451, 3051, 3052, 3050}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4450, 3597, 3679, 3596, 3593}, + // 755 + {531: 4443}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 589: 1279, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4430, 1356: 4431}, + {531: 4372}, + {531: 3900}, + {531: 3889}, + // 760 + {531: 1430}, + {531: 1427}, + {531: 1426}, + {531: 1424}, + {531: 1420}, + // 765 + {531: 1417}, + {531: 1416}, + {531: 1414}, + {1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 536: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 549: 1403, 1403, 1403, 554: 1403, 1403, 1403, 1403, 1403, 1403, 561: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 579: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 588: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 609: 1403, 1403, 1403, 1403, 1403, 615: 1403, 1403, 618: 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 1403, 632: 1403, 1403, 1403, 1403, 696: 1403, 703: 1403}, + {1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 536: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 549: 1402, 1402, 1402, 554: 1402, 1402, 1402, 1402, 1402, 1402, 561: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 579: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 588: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 609: 1402, 1402, 1402, 1402, 1402, 615: 1402, 1402, 618: 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 1402, 632: 1402, 1402, 1402, 1402, 696: 1402, 703: 1402}, + // 770 + {1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 536: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 549: 1401, 1401, 1401, 554: 1401, 1401, 1401, 1401, 1401, 1401, 561: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 579: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 588: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 609: 1401, 1401, 1401, 1401, 1401, 615: 1401, 1401, 618: 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 1401, 632: 1401, 1401, 1401, 1401, 696: 1401, 703: 1401}, + {1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 536: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 549: 1400, 1400, 1400, 554: 1400, 1400, 1400, 1400, 1400, 1400, 561: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 579: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 588: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 609: 1400, 1400, 1400, 1400, 1400, 615: 1400, 1400, 618: 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 1400, 632: 1400, 1400, 1400, 1400, 696: 1400, 703: 1400}, + {1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 536: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 549: 1399, 1399, 1399, 554: 1399, 1399, 1399, 1399, 1399, 1399, 561: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 579: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 588: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 609: 1399, 1399, 1399, 1399, 1399, 615: 1399, 1399, 618: 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 1399, 632: 1399, 1399, 1399, 1399, 696: 1399, 703: 1399}, + {1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 536: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 549: 1398, 1398, 1398, 554: 1398, 1398, 1398, 1398, 1398, 1398, 561: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 579: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 588: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 609: 1398, 1398, 1398, 1398, 1398, 615: 1398, 1398, 618: 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 1398, 632: 1398, 1398, 1398, 1398, 696: 1398, 703: 1398}, + {1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 536: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 549: 1397, 1397, 1397, 554: 1397, 1397, 1397, 1397, 1397, 1397, 561: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 579: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 588: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 609: 1397, 1397, 1397, 1397, 1397, 615: 1397, 1397, 618: 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 1397, 632: 1397, 1397, 1397, 1397, 696: 1397, 703: 1397}, + // 775 + {1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 536: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 549: 1396, 1396, 1396, 554: 1396, 1396, 1396, 1396, 1396, 1396, 561: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 579: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 588: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 609: 1396, 1396, 1396, 1396, 1396, 615: 1396, 1396, 618: 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 1396, 632: 1396, 1396, 1396, 1396, 696: 1396, 703: 1396}, + {1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 536: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 549: 1395, 1395, 1395, 554: 1395, 1395, 1395, 1395, 1395, 1395, 561: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 579: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 588: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 609: 1395, 1395, 1395, 1395, 1395, 615: 1395, 1395, 618: 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 1395, 632: 1395, 1395, 1395, 1395, 696: 1395, 703: 1395}, + {1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 536: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 549: 1394, 1394, 1394, 554: 1394, 1394, 1394, 1394, 1394, 1394, 561: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 579: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 588: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 609: 1394, 1394, 1394, 1394, 1394, 615: 1394, 1394, 618: 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 1394, 632: 1394, 1394, 1394, 1394, 696: 1394, 703: 1394}, + {1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 536: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 549: 1393, 1393, 1393, 554: 1393, 1393, 1393, 1393, 1393, 1393, 561: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 579: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 588: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 609: 1393, 1393, 1393, 1393, 1393, 615: 1393, 1393, 618: 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 1393, 632: 1393, 1393, 1393, 1393, 696: 1393, 703: 1393}, + {531: 4369}, + // 780 + {531: 4366}, + {1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 4363, 1405, 1405, 1405, 536: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 549: 1405, 1405, 1405, 554: 1405, 1405, 1405, 1405, 1405, 1405, 561: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 579: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 588: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 609: 1405, 1405, 1405, 1405, 1405, 615: 1405, 1405, 618: 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 1405, 632: 1405, 1405, 1405, 1405, 696: 1405, 703: 1405, 1224: 4364}, + {531: 4361}, + {1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 4357, 1311, 1311, 1311, 536: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 549: 1311, 1311, 1311, 554: 1311, 1311, 1311, 1311, 1311, 1311, 561: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 579: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 588: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 609: 1311, 1311, 1311, 1311, 1311, 615: 1311, 1311, 618: 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 1311, 632: 1311, 1311, 1311, 1311, 696: 1311, 703: 1311, 1367: 4356}, + {531: 4348}, + // 785 + {531: 4344}, + {531: 4339}, + {531: 4336}, + {531: 4331}, + {531: 4322}, + // 790 + {531: 4315}, + {531: 4310}, + {531: 4305}, + {531: 4291}, + {531: 4274}, + // 795 + {1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 532: 1358, 1358, 1358, 536: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 549: 1358, 1358, 1358, 554: 1358, 1358, 1358, 1358, 1358, 1358, 561: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 579: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 588: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 609: 1358, 1358, 1358, 1358, 1358, 615: 1358, 1358, 618: 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 1358, 632: 1358, 1358, 1358, 1358, 696: 1358, 703: 1358}, + {531: 4267}, + {531: 1352}, + {531: 1351}, + {1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 532: 1343, 1343, 1343, 536: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 549: 1343, 1343, 1343, 554: 1343, 1343, 1343, 1343, 1343, 1343, 561: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 579: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 588: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 609: 1343, 1343, 1343, 1343, 1343, 615: 1343, 1343, 618: 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 1343, 632: 1343, 1343, 1343, 1343, 696: 1343, 703: 1343}, + // 800 + {531: 4264}, + {531: 4261}, + {531: 4253}, + {531: 4245}, + {531: 4237}, + // 805 + {531: 4223}, + {531: 4214}, + {531: 4209}, + {531: 4204}, + {531: 4199}, + // 810 + {531: 4194}, + {531: 4189}, + {531: 4184}, + {531: 4171}, + {531: 4168}, + // 815 + {531: 4165}, + {531: 4162}, + {531: 4159}, + {531: 4156}, + {531: 4152}, + // 820 + {531: 4146}, + {531: 4133}, + {531: 4128}, + {531: 4123}, + {531: 3683}, + // 825 + {949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 532: 949, 949, 949, 536: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 549: 949, 949, 949, 554: 949, 949, 949, 949, 949, 949, 561: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 579: 949, 949, 949, 949, 949, 949, 949, 588: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 609: 949, 949, 949, 949, 949, 615: 949, 949, 618: 949, 949, 949, 949, 949, 949, 949, 949, 949, 949, 632: 949, 949, 949, 949, 696: 949, 703: 949}, + {948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 532: 948, 948, 948, 536: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 549: 948, 948, 948, 554: 948, 948, 948, 948, 948, 948, 561: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 579: 948, 948, 948, 948, 948, 948, 948, 588: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 609: 948, 948, 948, 948, 948, 615: 948, 948, 618: 948, 948, 948, 948, 948, 948, 948, 948, 948, 948, 632: 948, 948, 948, 948, 696: 948, 703: 948}, + {947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 532: 947, 947, 947, 536: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 549: 947, 947, 947, 554: 947, 947, 947, 947, 947, 947, 561: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 579: 947, 947, 947, 947, 947, 947, 947, 588: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 609: 947, 947, 947, 947, 947, 615: 947, 947, 618: 947, 947, 947, 947, 947, 947, 947, 947, 947, 947, 632: 947, 947, 947, 947, 696: 947, 703: 947}, + {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 696: 946, 703: 946}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3685}, + // 830 + {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 703: 946, 717: 4121}, + {9: 4052, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4051}, + {531: 4023}, + {2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 532: 2204, 2204, 537: 2204, 539: 2204, 2204, 2204, 2204, 549: 2204, 2204, 2204, 554: 2204, 4006, 2204, 2204, 2204, 2204, 561: 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 580: 2204, 2204, 2204, 2204, 585: 2204, 588: 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 2204, 607: 2204, 619: 4003, 4001, 4000, 4008, 4002, 4004, 4005, 4007, 1338: 3999, 1381: 3998}, + // 835 + {2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 532: 2175, 2175, 537: 2175, 539: 2175, 2175, 2175, 2175, 549: 2175, 2175, 2175, 554: 2175, 2175, 2175, 2175, 2175, 2175, 561: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 580: 2175, 2175, 2175, 2175, 585: 2175, 588: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175, 607: 2175, 619: 2175, 2175, 2175, 2175, 2175, 2175, 2175, 2175}, + {2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 532: 2144, 2144, 3812, 536: 3811, 2144, 539: 2144, 2144, 2144, 2144, 3782, 3783, 3788, 549: 2144, 2144, 2144, 554: 2144, 2144, 2144, 2144, 2144, 2144, 561: 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 579: 3816, 2144, 2144, 2144, 2144, 3784, 2144, 588: 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 3815, 2144, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144, 3813, 632: 3817, 3825, 3826, 3824, 915: 3814, 1250: 3818, 1326: 3820, 1372: 3822, 1377: 3819, 1383: 3821, 1437: 3823}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3738}, + {1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 532: 1482, 1482, 1482, 536: 1482, 1482, 3693, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 549: 1482, 1482, 1482, 554: 1482, 1482, 1482, 1482, 1482, 1482, 561: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 579: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 588: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 609: 1482, 1482, 1482, 1482, 1482, 615: 1482, 1482, 618: 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482, 632: 1482, 1482, 1482, 1482, 703: 3694}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 3733}, + // 840 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3695, 3597, 3679, 3596, 3593}, + {1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 532: 1464, 1464, 1464, 536: 1464, 1464, 3693, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 549: 1464, 1464, 1464, 554: 1464, 1464, 1464, 1464, 1464, 1464, 561: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 579: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 588: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 609: 1464, 1464, 1464, 1464, 1464, 615: 1464, 1464, 618: 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 1464, 632: 1464, 1464, 1464, 1464, 696: 1464, 703: 1464}, + {2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 774: 2090}, + {2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 2084, 774: 2084}, + {2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 2072, 774: 2072}, + // 845 + {2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 2060, 774: 2060}, + {2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 2049, 774: 2049}, + {2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 774: 2047}, + {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 774: 2024}, + {2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 2018, 774: 2018}, + // 850 + {2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 2008, 774: 2008}, + {1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 1987, 774: 1987}, + {1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 1980, 774: 1980}, + {1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 1979, 774: 1979}, + {1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 1976, 774: 1976}, + // 855 + {1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 1971, 774: 1971}, + {1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 1969, 774: 1969}, + {1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 774: 1968}, + {1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 1965, 774: 1965}, + {1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 1963, 774: 1963}, + // 860 + {1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 1950, 774: 1950}, + {1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 1927, 774: 1927}, + {1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 1910, 774: 1910}, + {1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 1909, 774: 1909}, + {1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908, 774: 1908}, + // 865 + {1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 1904, 774: 1904}, + {1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 1903, 774: 1903}, + {1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 1897, 774: 1897}, + {1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 1786, 774: 1786}, + {1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 1785, 774: 1785}, + // 870 + {1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 1784, 774: 1784}, + {1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783, 774: 1783}, + {1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 1677, 774: 1677}, + {1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 1658, 774: 1658}, + {1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 1640, 774: 1640}, + // 875 + {1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 1627, 774: 1627}, + {1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 1626, 774: 1626}, + {1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 1606, 774: 1606}, + {1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 1605, 774: 1605}, + {1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 532: 1474, 1474, 1474, 536: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 549: 1474, 1474, 1474, 554: 1474, 1474, 1474, 1474, 1474, 1474, 561: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 579: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 588: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 609: 1474, 1474, 1474, 1474, 1474, 615: 1474, 1474, 618: 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 1474, 632: 1474, 1474, 1474, 1474, 696: 1474, 703: 1474}, + // 880 + {953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 561: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 579: 953, 953, 953, 953, 953, 953, 953, 588: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 615: 953, 953, 618: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953, 632: 953, 953, 953, 953, 695: 953, 953, 701: 953, 953, 953, 953, 953, 953, 953, 953, 953, 953}, + {952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 561: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 579: 952, 952, 952, 952, 952, 952, 952, 588: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 615: 952, 952, 618: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952, 632: 952, 952, 952, 952, 695: 952, 952, 701: 952, 952, 952, 952, 952, 952, 952, 952, 952, 952}, + {435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 561: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 579: 435, 435, 435, 435, 435, 435, 435, 435, 588: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 615: 435, 435, 618: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 632: 435, 435, 435, 435, 695: 435, 435, 701: 435, 435, 435, 435, 435, 435, 435, 435, 435, 435, 712: 435, 435, 717: 435, 721: 435, 435, 435, 725: 435, 435}, + {434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 561: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 579: 434, 434, 434, 434, 434, 434, 434, 434, 588: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 615: 434, 434, 618: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 632: 434, 434, 434, 434, 695: 434, 434, 701: 434, 434, 434, 434, 434, 434, 434, 434, 434, 434, 712: 434, 434, 717: 434, 721: 434, 434, 434, 725: 434, 434}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3746}, + // 885 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3810}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3809}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3808}, + {2: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 10: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 58: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 531: 2194, 533: 2194, 2194, 2194, 2194, 540: 2194, 2194, 543: 2194, 2194, 2194, 547: 2194, 2194, 552: 2194, 2194, 560: 2194, 578: 2194, 586: 2194, 2194, 614: 2194, 617: 2194, 628: 2194, 2194, 2194, 2194, 636: 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 2194, 697: 2194, 2194, 2194, 2194, 711: 2194}, + {2: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 10: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 58: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 531: 2193, 533: 2193, 2193, 2193, 2193, 540: 2193, 2193, 543: 2193, 2193, 2193, 547: 2193, 2193, 552: 2193, 2193, 560: 2193, 578: 2193, 586: 2193, 2193, 614: 2193, 617: 2193, 628: 2193, 2193, 2193, 2193, 636: 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 2193, 697: 2193, 2193, 2193, 2193, 711: 2193}, + // 890 + {2: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 10: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 58: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 531: 2192, 533: 2192, 2192, 2192, 2192, 540: 2192, 2192, 543: 2192, 2192, 2192, 547: 2192, 2192, 552: 2192, 2192, 560: 2192, 578: 2192, 586: 2192, 2192, 614: 2192, 617: 2192, 628: 2192, 2192, 2192, 2192, 636: 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 2192, 697: 2192, 2192, 2192, 2192, 711: 2192}, + {2: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 10: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 58: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 531: 2191, 533: 2191, 2191, 2191, 2191, 540: 2191, 2191, 543: 2191, 2191, 2191, 547: 2191, 2191, 552: 2191, 2191, 560: 2191, 578: 2191, 586: 2191, 2191, 614: 2191, 617: 2191, 628: 2191, 2191, 2191, 2191, 636: 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 2191, 697: 2191, 2191, 2191, 2191, 711: 2191}, + {543: 3776}, + {1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 549: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 561: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 579: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 588: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 615: 1308, 1308, 618: 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 1308, 632: 1308, 1308, 1308, 1308, 695: 1308, 709: 1308, 1308}, + {1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 549: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 561: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 579: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 588: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 615: 1307, 1307, 618: 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 1307, 632: 1307, 1307, 1307, 1307, 695: 1307, 709: 1307, 1307}, + // 895 + {1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 549: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 561: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 579: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 588: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 615: 1306, 1306, 618: 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 1306, 632: 1306, 1306, 1306, 1306, 695: 1306, 709: 1306, 1306}, + {1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 549: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 561: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 579: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 588: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 615: 1305, 1305, 618: 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 1305, 632: 1305, 1305, 1305, 1305, 695: 1305, 709: 1305, 1305}, + {1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 549: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 561: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 579: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 588: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 615: 1304, 1304, 618: 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 1304, 632: 1304, 1304, 1304, 1304, 695: 1304, 709: 1304, 1304}, + {1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 549: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 561: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 579: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 588: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 615: 1303, 1303, 618: 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 1303, 632: 1303, 1303, 1303, 1303, 695: 1303, 709: 1303, 1303}, + {1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 549: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 561: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 579: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 588: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 615: 1302, 1302, 618: 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 1302, 632: 1302, 1302, 1302, 1302, 695: 1302, 709: 1302, 1302}, + // 900 + {1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 549: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 561: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 579: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 588: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 615: 1301, 1301, 618: 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 1301, 632: 1301, 1301, 1301, 1301, 695: 1301, 709: 1301, 1301}, + {1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 549: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 561: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 579: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 588: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 615: 1300, 1300, 618: 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 1300, 632: 1300, 1300, 1300, 1300, 695: 1300, 709: 1300, 1300}, + {1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 549: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 561: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 579: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 588: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 615: 1299, 1299, 618: 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 1299, 632: 1299, 1299, 1299, 1299, 695: 1299, 709: 1299, 1299}, + {1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 549: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 561: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 579: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 588: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 615: 1298, 1298, 618: 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 1298, 632: 1298, 1298, 1298, 1298, 695: 1298, 709: 1298, 1298}, + {1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 549: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 561: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 579: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 588: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 615: 1297, 1297, 618: 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 1297, 632: 1297, 1297, 1297, 1297, 695: 1297, 709: 1297, 1297}, + // 905 + {1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 549: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 561: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 579: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 588: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 615: 1296, 1296, 618: 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 1296, 632: 1296, 1296, 1296, 1296, 695: 1296, 709: 1296, 1296}, + {1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 549: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 561: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 579: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 588: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 615: 1295, 1295, 618: 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 1295, 632: 1295, 1295, 1295, 1295, 695: 1295, 709: 1295, 1295}, + {1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 549: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 561: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 579: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 588: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 615: 1294, 1294, 618: 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 1294, 632: 1294, 1294, 1294, 1294, 695: 1294, 709: 1294, 1294}, + {1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 549: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 561: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 579: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 588: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 615: 1293, 1293, 618: 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 1293, 632: 1293, 1293, 1293, 1293, 695: 1293, 709: 1293, 1293}, + {1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 549: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 561: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 579: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 588: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 615: 1292, 1292, 618: 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 1292, 632: 1292, 1292, 1292, 1292, 695: 1292, 709: 1292, 1292}, + // 910 + {1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 549: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 561: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 579: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 588: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 615: 1291, 1291, 618: 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 1291, 632: 1291, 1291, 1291, 1291, 695: 1291, 709: 1291, 1291}, + {1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 549: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 561: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 579: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 588: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 615: 1290, 1290, 618: 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290, 632: 1290, 1290, 1290, 1290, 695: 1290, 709: 1290, 1290}, + {1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 549: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 561: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 579: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 588: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 615: 1289, 1289, 618: 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 1289, 632: 1289, 1289, 1289, 1289, 695: 1289, 709: 1289, 1289}, + {1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 549: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 561: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 579: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 588: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 615: 1288, 1288, 618: 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 1288, 632: 1288, 1288, 1288, 1288, 695: 1288, 709: 1288, 1288}, + {1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 549: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 561: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 579: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 588: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 615: 1287, 1287, 618: 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 1287, 632: 1287, 1287, 1287, 1287, 695: 1287, 709: 1287, 1287}, + // 915 + {1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 549: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 561: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 579: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 588: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 615: 1286, 1286, 618: 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 1286, 632: 1286, 1286, 1286, 1286, 695: 1286, 709: 1286, 1286}, + {1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 549: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 561: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 579: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 588: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 615: 1285, 1285, 618: 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 1285, 632: 1285, 1285, 1285, 1285, 695: 1285, 709: 1285, 1285}, + {1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 549: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 561: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 579: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 588: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 615: 1284, 1284, 618: 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 1284, 632: 1284, 1284, 1284, 1284, 695: 1284, 709: 1284, 1284}, + {1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 549: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 561: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 579: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 588: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 615: 1283, 1283, 618: 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 1283, 632: 1283, 1283, 1283, 1283, 695: 1283, 709: 1283, 1283}, + {1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 549: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 561: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 579: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 588: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 615: 1282, 1282, 618: 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 1282, 632: 1282, 1282, 1282, 1282, 695: 1282, 709: 1282, 1282}, + // 920 + {1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 549: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 561: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 579: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 588: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 615: 1281, 1281, 618: 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, 632: 1281, 1281, 1281, 1281, 695: 1281, 709: 1281, 1281}, + {1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 549: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 561: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 579: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 588: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 615: 1280, 1280, 618: 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 632: 1280, 1280, 1280, 1280, 695: 1280, 709: 1280, 1280}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3777}, + {1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 532: 1489, 1489, 1489, 536: 1489, 1489, 539: 1489, 1489, 1489, 1489, 1489, 1489, 3788, 549: 1489, 1489, 1489, 554: 1489, 1489, 1489, 1489, 1489, 1489, 561: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 579: 1489, 1489, 1489, 1489, 1489, 3784, 1489, 588: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 609: 3785, 3786, 1489, 3789, 1489, 615: 3787, 1489, 618: 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 1489, 632: 1489, 1489, 1489, 1489}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3807}, + // 925 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3806}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3805}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3804}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3801, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3800}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3797, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3796}, + // 930 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3795}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3794}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3793}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3792}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3791}, + // 935 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3790}, + {1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 532: 1483, 1483, 1483, 536: 1483, 1483, 539: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 549: 1483, 1483, 1483, 554: 1483, 1483, 1483, 1483, 1483, 1483, 561: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 579: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 588: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 609: 1483, 1483, 1483, 1483, 1483, 615: 1483, 1483, 618: 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 1483, 632: 1483, 1483, 1483, 1483}, + {1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 532: 1484, 1484, 1484, 536: 1484, 1484, 539: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 549: 1484, 1484, 1484, 554: 1484, 1484, 1484, 1484, 1484, 1484, 561: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 579: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 588: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 609: 1484, 1484, 1484, 3789, 1484, 615: 1484, 1484, 618: 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 1484, 632: 1484, 1484, 1484, 1484}, + {1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 532: 1485, 1485, 1485, 536: 1485, 1485, 539: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 549: 1485, 1485, 1485, 554: 1485, 1485, 1485, 1485, 1485, 1485, 561: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 579: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 588: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 609: 1485, 1485, 1485, 3789, 1485, 615: 1485, 1485, 618: 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 1485, 632: 1485, 1485, 1485, 1485}, + {1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 532: 1486, 1486, 1486, 536: 1486, 1486, 539: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 549: 1486, 1486, 1486, 554: 1486, 1486, 1486, 1486, 1486, 1486, 561: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 579: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 588: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 609: 1486, 1486, 1486, 3789, 1486, 615: 1486, 1486, 618: 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486, 632: 1486, 1486, 1486, 1486}, + // 940 + {1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 532: 1487, 1487, 1487, 536: 1487, 1487, 539: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 549: 1487, 1487, 1487, 554: 1487, 1487, 1487, 1487, 1487, 1487, 561: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 579: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 588: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 609: 1487, 1487, 1487, 3789, 1487, 615: 1487, 1487, 618: 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 1487, 632: 1487, 1487, 1487, 1487}, + {1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 532: 1488, 1488, 1488, 536: 1488, 1488, 539: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 549: 1488, 1488, 1488, 554: 1488, 1488, 1488, 1488, 1488, 1488, 561: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 579: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 588: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 609: 1488, 1488, 1488, 3789, 1488, 615: 1488, 1488, 618: 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 1488, 632: 1488, 1488, 1488, 1488}, + {1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 532: 1492, 1492, 1492, 536: 1492, 1492, 539: 1492, 1492, 1492, 1492, 1492, 1492, 3788, 549: 1492, 1492, 1492, 554: 1492, 1492, 1492, 1492, 1492, 1492, 561: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 579: 1492, 1492, 1492, 1492, 1492, 3784, 1492, 588: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 609: 3785, 3786, 1492, 3789, 1492, 615: 3787, 1492, 618: 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 1492, 632: 1492, 1492, 1492, 1492}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3798}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3799}, + // 945 + {1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 532: 1490, 1490, 1490, 536: 1490, 1490, 539: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 549: 1490, 1490, 1490, 554: 1490, 1490, 1490, 1490, 1490, 1490, 561: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 579: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 588: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 609: 1490, 1490, 1490, 1490, 1490, 615: 1490, 1490, 618: 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 1490, 632: 1490, 1490, 1490, 1490}, + {1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 532: 1493, 1493, 1493, 536: 1493, 1493, 539: 1493, 1493, 1493, 1493, 1493, 1493, 3788, 549: 1493, 1493, 1493, 554: 1493, 1493, 1493, 1493, 1493, 1493, 561: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 579: 1493, 1493, 1493, 1493, 1493, 3784, 1493, 588: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 609: 3785, 3786, 1493, 3789, 1493, 615: 3787, 1493, 618: 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 1493, 632: 1493, 1493, 1493, 1493}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3802}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 3803}, + {1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 532: 1491, 1491, 1491, 536: 1491, 1491, 539: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 549: 1491, 1491, 1491, 554: 1491, 1491, 1491, 1491, 1491, 1491, 561: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 579: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 588: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 609: 1491, 1491, 1491, 1491, 1491, 615: 1491, 1491, 618: 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 1491, 632: 1491, 1491, 1491, 1491}, + // 950 + {1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 532: 1494, 1494, 1494, 536: 1494, 1494, 539: 1494, 1494, 1494, 1494, 3782, 3783, 3788, 549: 1494, 1494, 1494, 554: 1494, 1494, 1494, 1494, 1494, 1494, 561: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 579: 1494, 1494, 1494, 1494, 1494, 3784, 1494, 588: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 609: 3785, 3786, 1494, 3789, 1494, 615: 3787, 1494, 618: 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 1494, 632: 1494, 1494, 1494, 1494}, + {1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 532: 1495, 1495, 1495, 536: 1495, 1495, 539: 1495, 1495, 1495, 1495, 3782, 3783, 3788, 549: 1495, 1495, 1495, 554: 1495, 1495, 1495, 1495, 1495, 1495, 561: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 579: 1495, 1495, 1495, 1495, 1495, 3784, 1495, 588: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 609: 3785, 3786, 1495, 3789, 1495, 615: 3787, 1495, 618: 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 1495, 632: 1495, 1495, 1495, 1495}, + {1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 532: 1496, 1496, 1496, 536: 1496, 1496, 539: 1496, 1496, 1496, 1496, 3782, 3783, 3788, 549: 1496, 1496, 1496, 554: 1496, 1496, 1496, 1496, 1496, 1496, 561: 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 579: 1496, 1496, 1496, 1496, 1496, 3784, 1496, 588: 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 609: 3785, 3786, 1496, 3789, 1496, 615: 3787, 3780, 618: 3781, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 1496, 632: 1496, 1496, 1496, 1496}, + {1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 532: 1497, 1497, 1497, 536: 1497, 1497, 539: 1497, 1497, 1497, 1497, 3782, 3783, 3788, 549: 1497, 1497, 1497, 554: 1497, 1497, 1497, 1497, 1497, 1497, 561: 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 579: 1497, 1497, 1497, 1497, 1497, 3784, 1497, 588: 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 609: 3785, 3786, 3779, 3789, 1497, 615: 3787, 3780, 618: 3781, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 1497, 632: 1497, 1497, 1497, 1497}, + {2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 532: 2210, 2210, 537: 2210, 539: 2210, 2210, 2210, 2210, 549: 2210, 2210, 2210, 554: 2210, 556: 2210, 2210, 2210, 2210, 561: 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 580: 2210, 2210, 2210, 2210, 585: 2210, 588: 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210, 607: 2210, 800: 3741, 3739}, + // 955 + {2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 532: 2211, 2211, 537: 2211, 539: 2211, 2211, 2211, 2211, 549: 2211, 2211, 2211, 554: 2211, 556: 2211, 2211, 2211, 2211, 561: 2211, 2211, 2211, 2211, 2211, 3745, 2211, 3744, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 580: 2211, 2211, 2211, 2211, 585: 2211, 588: 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 2211, 607: 2211, 800: 3741, 3739}, + {2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 532: 2212, 2212, 537: 2212, 539: 2212, 2212, 2212, 2212, 549: 2212, 2212, 2212, 554: 2212, 556: 2212, 2212, 2212, 2212, 561: 2212, 2212, 2212, 2212, 2212, 3745, 2212, 3744, 2212, 3740, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 580: 2212, 2212, 2212, 2212, 585: 2212, 588: 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 2212, 607: 2212, 800: 3741, 3739}, + {190: 2590, 226: 2590, 548: 2590, 579: 2590, 606: 2590, 627: 2590, 629: 2590, 2590, 632: 2590, 2590, 2590, 645: 2590}, + {190: 2589, 226: 2589, 548: 2589, 579: 2589, 606: 2589, 627: 2589, 629: 2589, 2589, 632: 2589, 2589, 2589, 645: 2589}, + {2: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 10: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 58: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 531: 2166, 533: 2166, 2166, 2166, 540: 2166, 2166, 543: 2166, 2166, 2166, 547: 2166, 2166, 552: 2166, 2166, 560: 2166, 578: 2166, 586: 2166, 2166, 614: 2166, 617: 2166, 628: 2166, 2166, 2166, 2166, 636: 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 2166, 697: 2166, 2166, 2166, 2166}, + // 960 + {579: 3995, 606: 3994, 627: 3993, 632: 3996, 3825, 3826, 1250: 3997}, + {531: 2162}, + {2: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 10: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 58: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 531: 2160, 533: 2160, 2160, 2160, 540: 2160, 2160, 543: 2160, 2160, 2160, 547: 2160, 2160, 552: 2160, 2160, 560: 2160, 578: 2160, 586: 2160, 2160, 614: 2160, 617: 2160, 628: 2160, 2160, 2160, 2160, 636: 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 697: 2160, 2160, 2160, 2160}, + {2: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 10: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 58: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 531: 2158, 533: 2158, 2158, 2158, 540: 2158, 2158, 543: 2158, 2158, 2158, 547: 2158, 2158, 552: 2158, 2158, 560: 2158, 578: 2158, 586: 2158, 2158, 614: 2158, 617: 2158, 628: 2158, 2158, 2158, 2158, 636: 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158, 697: 2158, 2158, 2158, 2158}, + {2: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 10: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 58: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 531: 2156, 533: 2156, 2156, 2156, 540: 2156, 2156, 543: 2156, 2156, 2156, 547: 2156, 2156, 552: 2156, 2156, 560: 2156, 578: 2156, 586: 2156, 2156, 614: 2156, 617: 2156, 628: 2156, 2156, 2156, 2156, 636: 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 2156, 697: 2156, 2156, 2156, 2156}, + // 965 + {531: 3840, 775: 3841}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3837}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3835, 3597, 3679, 3596, 3593}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3831, 3597, 3679, 3596, 3593}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3830, 3597, 3679, 3596, 3593}, + // 970 + {531: 3827}, + {2: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 10: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 58: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 531: 2143, 533: 2143, 2143, 2143, 540: 2143, 2143, 543: 2143, 2143, 2143, 547: 2143, 2143, 552: 2143, 2143, 560: 2143, 578: 2143, 586: 2143, 2143, 614: 2143, 617: 2143, 628: 2143, 2143, 2143, 2143, 636: 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 2143, 697: 2143, 2143, 2143, 2143}, + {2: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 10: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 58: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 531: 2142, 533: 2142, 2142, 2142, 540: 2142, 2142, 543: 2142, 2142, 2142, 547: 2142, 2142, 552: 2142, 2142, 560: 2142, 578: 2142, 586: 2142, 2142, 614: 2142, 617: 2142, 628: 2142, 2142, 2142, 2142, 636: 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 697: 2142, 2142, 2142, 2142}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3828, 3597, 3679, 3596, 3593}, + {57: 3829, 538: 3693, 703: 3694}, + // 975 + {2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 532: 2145, 2145, 537: 2145, 539: 2145, 2145, 2145, 2145, 549: 2145, 2145, 2145, 554: 2145, 2145, 2145, 2145, 2145, 2145, 561: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 580: 2145, 2145, 2145, 2145, 585: 2145, 588: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145, 607: 2145, 619: 2145, 2145, 2145, 2145, 2145, 2145, 2145, 2145}, + {2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 532: 2146, 2146, 537: 2146, 3693, 2146, 2146, 2146, 2146, 549: 2146, 2146, 2146, 554: 2146, 2146, 2146, 2146, 2146, 2146, 561: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 580: 2146, 2146, 2146, 2146, 585: 2146, 588: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 607: 2146, 619: 2146, 2146, 2146, 2146, 2146, 2146, 2146, 2146, 703: 3694}, + {2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 3833, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 532: 2141, 2141, 537: 2141, 3693, 2141, 2141, 2141, 2141, 549: 2141, 2141, 2141, 554: 2141, 2141, 2141, 2141, 2141, 2141, 561: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 580: 2141, 2141, 2141, 2141, 585: 2141, 588: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 607: 2141, 619: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 703: 3694, 1200: 3832}, + {2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 532: 2147, 2147, 537: 2147, 539: 2147, 2147, 2147, 2147, 549: 2147, 2147, 2147, 554: 2147, 2147, 2147, 2147, 2147, 2147, 561: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 580: 2147, 2147, 2147, 2147, 585: 2147, 588: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147, 607: 2147, 619: 2147, 2147, 2147, 2147, 2147, 2147, 2147, 2147}, + {533: 3834}, + // 980 + {2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 532: 2140, 2140, 537: 2140, 539: 2140, 2140, 2140, 2140, 549: 2140, 2140, 2140, 554: 2140, 2140, 2140, 2140, 2140, 2140, 561: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 580: 2140, 2140, 2140, 2140, 585: 2140, 588: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140, 607: 2140, 619: 2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140}, + {2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 3833, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 532: 2141, 2141, 537: 2141, 3693, 2141, 2141, 2141, 2141, 549: 2141, 2141, 2141, 554: 2141, 2141, 2141, 2141, 2141, 2141, 561: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 580: 2141, 2141, 2141, 2141, 585: 2141, 588: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 607: 2141, 619: 2141, 2141, 2141, 2141, 2141, 2141, 2141, 2141, 703: 3694, 1200: 3836}, + {2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 532: 2148, 2148, 537: 2148, 539: 2148, 2148, 2148, 2148, 549: 2148, 2148, 2148, 554: 2148, 2148, 2148, 2148, 2148, 2148, 561: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 580: 2148, 2148, 2148, 2148, 585: 2148, 588: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148, 607: 2148, 619: 2148, 2148, 2148, 2148, 2148, 2148, 2148, 2148}, + {543: 3782, 3783, 3788, 566: 3838, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3839}, + // 985 + {2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 532: 2149, 2149, 537: 2149, 539: 2149, 2149, 2149, 2149, 549: 2149, 2149, 2149, 554: 2149, 2149, 2149, 2149, 2149, 2149, 561: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 580: 2149, 2149, 2149, 2149, 585: 2149, 588: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149, 607: 2149, 619: 2149, 2149, 2149, 2149, 2149, 2149, 2149, 2149}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 2906, 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3845, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 608: 2904, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 2900, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3844, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 863: 3843}, + {2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 532: 2150, 2150, 537: 2150, 539: 2150, 2150, 2150, 2150, 549: 2150, 2150, 2150, 554: 2150, 2150, 2150, 2150, 2150, 2150, 561: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 580: 2150, 2150, 2150, 2150, 585: 2150, 588: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150, 607: 2150, 619: 2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150}, + {2190, 2190, 9: 2190, 57: 2190, 160: 2190, 542: 2190, 564: 2190, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {9: 3990, 57: 3991}, + // 990 + {9: 1462, 57: 1462, 534: 1462, 536: 1462, 538: 1462, 1013, 543: 1462, 1462, 1462, 550: 1013, 1013, 554: 3859, 1462, 3858, 564: 3857, 566: 1462, 1462, 1462, 1462, 1462, 579: 1462, 584: 1462, 606: 1462, 609: 1462, 1462, 1462, 1462, 1462, 615: 1462, 1462, 618: 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462, 632: 1462, 1462, 1462, 1462, 703: 1462, 842: 3860, 3861}, + {531: 3889, 639: 3892, 1013: 3891, 1094: 3890}, + {531: 2907, 547: 2905, 608: 2904, 695: 2900, 775: 3854, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856}, + {57: 3852, 539: 1014, 550: 1014, 1014}, + {57: 3851}, + // 995 + {57: 3850}, + {1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 536: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 547: 1041, 549: 1041, 1041, 1041, 554: 1041, 1041, 1041, 1041, 1041, 1041, 561: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 579: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 588: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 615: 1041, 1041, 618: 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041, 632: 1041, 1041, 1041, 1041, 695: 1041, 1041, 703: 1041, 714: 1041, 803: 1041}, + {1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 536: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 547: 1042, 549: 1042, 1042, 1042, 554: 1042, 1042, 1042, 1042, 1042, 1042, 561: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 579: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 588: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 615: 1042, 1042, 618: 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 1042, 632: 1042, 1042, 1042, 1042, 695: 1042, 1042, 703: 1042, 714: 1042, 803: 1042}, + {1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 536: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 547: 1043, 549: 1043, 1043, 1043, 554: 1043, 1043, 1043, 1043, 1043, 1043, 561: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 579: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 588: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 615: 1043, 1043, 618: 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 1043, 632: 1043, 1043, 1043, 1043, 695: 1043, 1043, 703: 1043, 714: 1043, 803: 1043}, + {1198, 1198, 57: 1198, 530: 1198, 532: 1198, 539: 1014, 542: 1198, 550: 1014, 1014}, + // 1000 + {1197, 1197, 57: 1197, 530: 1197, 532: 1197, 539: 1013, 542: 1197, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {1026, 1026, 57: 1026, 530: 1026, 532: 1026, 542: 1026}, + {1025, 1025, 57: 1025, 530: 1025, 532: 1025, 542: 1025}, + {723: 3880}, + {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3875}, + // 1005 + {10: 3863, 265: 3864, 1363: 3865}, + {1019, 1019, 57: 1019, 530: 1019, 532: 1019, 542: 1019, 554: 3859, 556: 3858, 843: 3862}, + {1018, 1018, 57: 1018, 530: 1018, 532: 1018, 542: 1018}, + {1017, 1017, 57: 1017, 530: 1017, 532: 1017, 542: 1017}, + {560: 1076, 588: 1076, 639: 1076, 642: 1076}, + // 1010 + {560: 1075, 588: 1075, 639: 1075, 642: 1075}, + {560: 3037, 588: 1074, 639: 1074, 642: 3868, 799: 3866, 816: 3867, 985: 3869, 1357: 3870}, + {2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 15: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 57: 2226, 2226, 60: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 102: 2226, 104: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 118: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 133: 2226, 137: 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 2226, 213: 2226, 220: 2226, 263: 2226, 530: 2226, 2226, 2226, 535: 2226, 537: 2226, 2226, 2226, 542: 2226, 546: 2226, 2226, 549: 2226, 2226, 2226, 2226, 2226, 557: 2226, 2226, 2226, 562: 2226, 565: 2226, 588: 2226, 608: 2226, 639: 2226, 695: 2226, 709: 2226, 2226, 713: 2226}, + {1080, 1080, 9: 1080, 57: 1080, 213: 1080, 530: 1080, 532: 1080, 539: 1080, 542: 1080, 550: 1080, 1080, 558: 1080, 1080, 562: 1080, 588: 1080, 639: 1080}, + {1079, 1079, 9: 1079, 57: 1079, 213: 1079, 530: 1079, 532: 1079, 539: 1079, 542: 1079, 550: 1079, 1079, 558: 1079, 1079, 562: 1079, 588: 1079, 639: 1079}, + // 1015 + {588: 1073, 639: 1073}, + {588: 3872, 639: 3871, 1444: 3873}, + {194: 1078}, + {194: 1077}, + {194: 3874}, + // 1020 + {1069, 1069, 57: 1069, 530: 1069, 532: 1069, 539: 1069, 542: 1069, 550: 1069, 1069, 558: 1069, 1069, 562: 1069}, + {1072, 1072, 9: 3876, 57: 1072, 213: 3877, 530: 1072, 532: 1072, 539: 1072, 542: 1072, 550: 1072, 1072, 558: 1072, 1072, 562: 1072}, + {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3879}, + {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 3878}, + {1070, 1070, 57: 1070, 530: 1070, 532: 1070, 539: 1070, 542: 1070, 550: 1070, 1070, 558: 1070, 1070, 562: 1070}, + // 1025 + {1071, 1071, 57: 1071, 530: 1071, 532: 1071, 539: 1071, 542: 1071, 550: 1071, 1071, 558: 1071, 1071, 562: 1071}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 3882}, + {1506, 1506, 9: 1506, 57: 1506, 160: 1506, 530: 1506, 532: 1506, 539: 1506, 542: 1506, 550: 1506, 1506, 554: 1506, 556: 1506, 558: 1506, 1506, 562: 1506, 564: 1506, 566: 3745, 3743, 3744, 3742, 3740, 572: 1506, 574: 1506, 577: 3888, 588: 1506, 591: 1506, 593: 1506, 605: 3887, 800: 3741, 3739, 1411: 3886}, + {1509, 1509, 9: 3884, 57: 1509, 160: 1509, 530: 1509, 532: 1509, 539: 1509, 542: 1509, 550: 1509, 1509, 554: 1509, 556: 1509, 558: 1509, 1509, 562: 1509}, + {1508, 1508, 9: 1508, 57: 1508, 160: 1508, 530: 1508, 532: 1508, 539: 1508, 542: 1508, 550: 1508, 1508, 554: 1508, 556: 1508, 558: 1508, 1508, 562: 1508, 564: 1508, 572: 1508, 574: 1508, 588: 1508, 591: 1508, 593: 1508}, + // 1030 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3885}, + {1507, 1507, 9: 1507, 57: 1507, 160: 1507, 530: 1507, 532: 1507, 539: 1507, 542: 1507, 550: 1507, 1507, 554: 1507, 556: 1507, 558: 1507, 1507, 562: 1507, 564: 1507, 572: 1507, 574: 1507, 588: 1507, 591: 1507, 593: 1507}, + {1505, 1505, 9: 1505, 57: 1505, 160: 1505, 530: 1505, 532: 1505, 539: 1505, 542: 1505, 550: 1505, 1505, 554: 1505, 556: 1505, 558: 1505, 1505, 562: 1505, 564: 1505, 572: 1505, 574: 1505, 588: 1505, 591: 1505, 593: 1505}, + {1504, 1504, 9: 1504, 57: 1504, 160: 1504, 530: 1504, 532: 1504, 539: 1504, 542: 1504, 550: 1504, 1504, 554: 1504, 556: 1504, 558: 1504, 1504, 562: 1504, 564: 1504, 572: 1504, 574: 1504, 588: 1504, 591: 1504, 593: 1504}, + {1503, 1503, 9: 1503, 57: 1503, 160: 1503, 530: 1503, 532: 1503, 539: 1503, 542: 1503, 550: 1503, 1503, 554: 1503, 556: 1503, 558: 1503, 1503, 562: 1503, 564: 1503, 572: 1503, 574: 1503, 588: 1503, 591: 1503, 593: 1503}, + // 1035 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 3987}, + {1499, 1499, 9: 3913, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 3912}, + {147, 147, 9: 147, 57: 147, 530: 147, 532: 147, 539: 147, 542: 147, 550: 147, 147, 554: 147, 556: 147, 558: 147, 147, 562: 147, 564: 147}, + {531: 3893, 938: 3894}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 1537, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 3898, 1490: 3897, 3896}, + // 1040 + {145, 145, 9: 145, 57: 145, 530: 145, 532: 145, 539: 145, 542: 145, 550: 145, 145, 554: 145, 556: 145, 558: 145, 145, 562: 145, 564: 145}, + {1533, 1533, 9: 1533, 57: 1533, 530: 1533, 532: 1533, 542: 1533, 556: 1533, 561: 1533, 563: 1533, 1533, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {57: 3910}, + {9: 3908, 57: 1536}, + {9: 1534, 57: 1534}, + // 1045 + {1532, 1532, 9: 1532, 57: 1532, 530: 1532, 3900, 1532, 542: 1532, 556: 1532, 561: 1532, 563: 1532, 1532}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 3902}, + {57: 1481, 555: 1481, 716: 3904}, + {57: 3903}, + {1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 532: 1451, 1451, 1451, 536: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 549: 1451, 1451, 1451, 554: 1451, 1451, 1451, 1451, 1451, 1451, 561: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 579: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 588: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 609: 1451, 1451, 1451, 1451, 1451, 615: 1451, 1451, 618: 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 1451, 632: 1451, 1451, 1451, 1451, 696: 1451, 703: 1451}, + // 1050 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3905, 3051, 3052, 3050}, + {57: 1480, 555: 1480, 716: 3906}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3907, 3051, 3052, 3050}, + {1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 532: 1479, 1479, 1479, 536: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 549: 1479, 1479, 1479, 554: 1479, 1479, 1479, 1479, 1479, 1479, 561: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 579: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 588: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 609: 1479, 1479, 1479, 1479, 1479, 615: 1479, 1479, 618: 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 1479, 632: 1479, 1479, 1479, 1479, 696: 1479, 703: 1479, 718: 1479, 1479}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 3909}, + // 1055 + {9: 1535, 57: 1535}, + {1538, 1538, 9: 1538, 57: 1538, 108: 1538, 530: 1538, 532: 1538, 539: 1538, 542: 1538, 550: 1538, 1538, 554: 1538, 556: 1538, 558: 1538, 1538, 562: 1538, 564: 1538, 566: 1538}, + {1498, 1498, 57: 1498, 160: 1498, 530: 1498, 532: 1498, 539: 1498, 542: 1498, 550: 1498, 1498, 554: 1498, 556: 1498, 558: 1498, 1498, 562: 1498}, + {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 3915}, + {639: 3892, 1013: 3914}, + // 1060 + {146, 146, 9: 146, 57: 146, 530: 146, 532: 146, 539: 146, 542: 146, 550: 146, 146, 554: 146, 556: 146, 558: 146, 146, 562: 146, 564: 146}, + {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 3917}, + {1067, 1067, 57: 1067, 530: 1067, 532: 1067, 539: 1067, 542: 1067, 550: 1067, 1067, 558: 1067, 1067, 562: 1067}, + {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 3943}, + {340: 3924, 714: 3923}, + // 1065 + {606: 3920}, + {340: 3921}, + {264: 3922}, + {1031, 1031, 57: 1031, 530: 1031, 532: 1031, 539: 1031, 542: 1031, 550: 1031, 1031, 559: 1031}, + {1030, 1030, 57: 1030, 193: 1030, 196: 1030, 227: 1030, 530: 1030, 532: 1030, 539: 1030, 542: 1030, 550: 1030, 1030, 559: 1030, 1216: 3926, 3937}, + // 1070 + {1030, 1030, 57: 1030, 193: 1030, 196: 1030, 530: 1030, 532: 1030, 539: 1030, 542: 1030, 550: 1030, 1030, 559: 1030, 1216: 3926, 3925}, + {1037, 1037, 57: 1037, 193: 3934, 196: 3935, 530: 1037, 532: 1037, 539: 1037, 542: 1037, 550: 1037, 1037, 559: 1037}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 3929}, + {1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 579: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 588: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 615: 1254, 1254, 618: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 632: 1254, 1254, 1254, 1254, 646: 1254, 695: 1254, 1254, 701: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 3932, 720: 1254, 722: 1254, 1254, 1254, 727: 1254, 1254, 1254, 1254, 1254, 743: 1254, 765: 1254, 1254, 1254, 1254, 1254, 774: 1254}, + {1252, 1252, 9: 1252, 57: 1252, 193: 1252, 196: 1252, 227: 1252, 530: 1252, 532: 1252, 539: 1252, 542: 1252, 550: 1252, 1252, 559: 1252, 561: 1252, 712: 1252, 728: 1252, 730: 1252}, + // 1075 + {1029, 1029, 9: 3930, 57: 1029, 193: 1029, 196: 1029, 227: 1029, 530: 1029, 532: 1029, 539: 1029, 542: 1029, 550: 1029, 1029, 559: 1029}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3931}, + {1251, 1251, 9: 1251, 57: 1251, 193: 1251, 196: 1251, 215: 1251, 227: 1251, 530: 1251, 532: 1251, 539: 1251, 542: 1251, 550: 1251, 1251, 559: 1251, 561: 1251, 712: 1251, 715: 1251, 728: 1251, 730: 1251, 765: 1251}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3933, 3051, 3052, 3050}, + {1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 579: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 588: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 615: 1253, 1253, 618: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 632: 1253, 1253, 1253, 1253, 646: 1253, 695: 1253, 1253, 701: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 720: 1253, 722: 1253, 1253, 1253, 727: 1253, 1253, 1253, 1253, 1253, 743: 1253, 765: 1253, 1253, 1253, 1253, 1253, 774: 1253}, + // 1080 + {1034, 1034, 57: 1034, 530: 1034, 532: 1034, 539: 1034, 542: 1034, 550: 1034, 1034, 559: 1034}, + {320: 3936}, + {1032, 1032, 57: 1032, 530: 1032, 532: 1032, 539: 1032, 542: 1032, 550: 1032, 1032, 559: 1032}, + {1038, 1038, 57: 1038, 193: 3938, 196: 3940, 227: 3939, 530: 1038, 532: 1038, 539: 1038, 542: 1038, 550: 1038, 1038, 559: 1038}, + {1036, 1036, 57: 1036, 530: 1036, 532: 1036, 539: 1036, 542: 1036, 550: 1036, 1036, 559: 1036}, + // 1085 + {560: 3037, 799: 3942}, + {320: 3941}, + {1033, 1033, 57: 1033, 530: 1033, 532: 1033, 539: 1033, 542: 1033, 550: 1033, 1033, 559: 1033}, + {1035, 1035, 57: 1035, 530: 1035, 532: 1035, 539: 1035, 542: 1035, 550: 1035, 1035, 559: 1035}, + {1199, 1199, 57: 1199, 530: 1199, 532: 1199, 539: 1199, 542: 1199, 550: 1199, 1199}, + // 1090 + {1413: 3945}, + {533: 3946}, + {262, 262, 57: 262, 131: 3950, 154: 3949, 530: 262, 532: 262, 539: 262, 542: 262, 550: 262, 262, 722: 262, 930: 3948, 1173: 3947}, + {247, 247, 57: 247, 530: 247, 532: 247, 539: 247, 542: 247, 550: 247, 247, 722: 3978, 1056: 3977}, + {135: 3957, 852: 3953, 856: 3955, 862: 3956, 864: 3954, 1172: 3952, 1360: 3951}, + // 1095 + {260, 260, 17: 260, 58: 260, 60: 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 135: 260, 530: 260, 260, 561: 260, 606: 260, 713: 260, 852: 260, 856: 260, 862: 260, 864: 260}, + {259, 259, 17: 259, 58: 259, 60: 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 135: 259, 530: 259, 259, 561: 259, 606: 259, 713: 259, 852: 259, 856: 259, 862: 259, 864: 259}, + {261, 261, 57: 261, 135: 3957, 530: 261, 261, 261, 539: 261, 542: 261, 549: 261, 261, 261, 557: 261, 722: 261, 852: 3953, 856: 3955, 862: 3956, 864: 3954, 1172: 3976}, + {257, 257, 57: 257, 135: 257, 530: 257, 257, 257, 539: 257, 542: 257, 549: 257, 257, 257, 557: 257, 722: 257, 852: 257, 856: 257, 862: 257, 864: 257}, + {723: 3974}, + // 1100 + {533: 3968, 638: 3969, 640: 3970, 956: 3973}, + {723: 3971}, + {723: 3966}, + {548: 3958}, + {723: 3959}, + // 1105 + {533: 3960, 638: 3961, 640: 3962, 1021: 3963}, + {440, 440, 9: 440, 57: 440, 135: 440, 530: 440, 440, 440, 539: 440, 542: 440, 549: 440, 440, 440, 557: 440, 722: 440, 852: 440, 856: 440, 862: 440, 864: 440, 1007: 440}, + {439, 439, 9: 439, 57: 439, 135: 439, 530: 439, 439, 439, 539: 439, 542: 439, 549: 439, 439, 439, 557: 439, 722: 439, 852: 439, 856: 439, 862: 439, 864: 439, 1007: 439}, + {438, 438, 9: 438, 57: 438, 135: 438, 530: 438, 438, 438, 539: 438, 542: 438, 549: 438, 438, 438, 557: 438, 722: 438, 852: 438, 856: 438, 862: 438, 864: 438, 1007: 438}, + {252, 252, 57: 252, 135: 252, 530: 252, 252, 252, 539: 252, 542: 252, 549: 252, 252, 252, 557: 252, 722: 252, 852: 252, 856: 252, 862: 252, 864: 252, 1007: 3964}, + // 1110 + {856: 3965}, + {251, 251, 57: 251, 135: 251, 530: 251, 251, 251, 539: 251, 542: 251, 549: 251, 251, 251, 557: 251, 722: 251, 852: 251, 856: 251, 862: 251, 864: 251}, + {533: 3968, 638: 3969, 640: 3970, 956: 3967}, + {253, 253, 57: 253, 135: 253, 530: 253, 253, 253, 539: 253, 542: 253, 549: 253, 253, 253, 557: 253, 722: 253, 852: 253, 856: 253, 862: 253, 864: 253}, + {250, 250, 57: 250, 135: 250, 530: 250, 250, 250, 539: 250, 542: 250, 549: 250, 250, 250, 557: 250, 722: 250, 852: 250, 856: 250, 862: 250, 864: 250}, + // 1115 + {249, 249, 57: 249, 135: 249, 530: 249, 249, 249, 539: 249, 542: 249, 549: 249, 249, 249, 557: 249, 722: 249, 852: 249, 856: 249, 862: 249, 864: 249}, + {248, 248, 57: 248, 135: 248, 530: 248, 248, 248, 539: 248, 542: 248, 549: 248, 248, 248, 557: 248, 722: 248, 852: 248, 856: 248, 862: 248, 864: 248}, + {533: 3968, 638: 3969, 640: 3970, 956: 3972}, + {254, 254, 57: 254, 135: 254, 530: 254, 254, 254, 539: 254, 542: 254, 549: 254, 254, 254, 557: 254, 722: 254, 852: 254, 856: 254, 862: 254, 864: 254}, + {255, 255, 57: 255, 135: 255, 530: 255, 255, 255, 539: 255, 542: 255, 549: 255, 255, 255, 557: 255, 722: 255, 852: 255, 856: 255, 862: 255, 864: 255}, + // 1120 + {533: 3968, 638: 3969, 640: 3970, 956: 3975}, + {256, 256, 57: 256, 135: 256, 530: 256, 256, 256, 539: 256, 542: 256, 549: 256, 256, 256, 557: 256, 722: 256, 852: 256, 856: 256, 862: 256, 864: 256}, + {258, 258, 57: 258, 135: 258, 530: 258, 258, 258, 539: 258, 542: 258, 549: 258, 258, 258, 557: 258, 722: 258, 852: 258, 856: 258, 862: 258, 864: 258}, + {1044, 1044, 57: 1044, 530: 1044, 532: 1044, 539: 1044, 542: 1044, 550: 1044, 1044}, + {245, 245, 57: 245, 530: 245, 245, 245, 539: 245, 542: 245, 549: 245, 245, 245, 557: 245, 852: 245, 1466: 3979, 3980}, + // 1125 + {243, 243, 57: 243, 530: 243, 243, 243, 539: 243, 542: 243, 549: 243, 243, 243, 557: 243, 852: 3984, 1385: 3983}, + {723: 3981}, + {533: 3968, 638: 3969, 640: 3970, 956: 3982}, + {244, 244, 57: 244, 530: 244, 244, 244, 539: 244, 542: 244, 549: 244, 244, 244, 557: 244, 852: 244}, + {246, 246, 57: 246, 530: 246, 246, 246, 539: 246, 542: 246, 549: 246, 246, 246, 557: 246}, + // 1130 + {723: 3985}, + {533: 3968, 638: 3969, 640: 3970, 956: 3986}, + {242, 242, 57: 242, 530: 242, 242, 242, 539: 242, 542: 242, 549: 242, 242, 242, 557: 242}, + {57: 3988}, + {1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 532: 1450, 1450, 1450, 536: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 549: 1450, 1450, 1450, 554: 1450, 1450, 1450, 1450, 1450, 1450, 561: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 579: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 588: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 609: 1450, 1450, 1450, 1450, 1450, 615: 1450, 1450, 618: 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 1450, 632: 1450, 1450, 1450, 1450, 696: 1450, 703: 1450}, + // 1135 + {1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 536: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 547: 1040, 549: 1040, 1040, 1040, 554: 1040, 1040, 1040, 1040, 1040, 1040, 561: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 579: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 588: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 615: 1040, 1040, 618: 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 1040, 632: 1040, 1040, 1040, 1040, 695: 1040, 1040, 703: 1040, 714: 1040, 803: 1040}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3992}, + {2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 532: 2151, 2151, 537: 2151, 539: 2151, 2151, 2151, 2151, 549: 2151, 2151, 2151, 554: 2151, 2151, 2151, 2151, 2151, 2151, 561: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 580: 2151, 2151, 2151, 2151, 585: 2151, 588: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151, 607: 2151, 619: 2151, 2151, 2151, 2151, 2151, 2151, 2151, 2151}, + {2189, 2189, 9: 2189, 57: 2189, 160: 2189, 542: 2189, 564: 2189, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 10: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 58: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 531: 2165, 533: 2165, 2165, 2165, 540: 2165, 2165, 543: 2165, 2165, 2165, 547: 2165, 2165, 552: 2165, 2165, 560: 2165, 578: 2165, 586: 2165, 2165, 614: 2165, 617: 2165, 628: 2165, 2165, 2165, 2165, 636: 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 2165, 697: 2165, 2165, 2165, 2165}, + // 1140 + {531: 2161}, + {2: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 10: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 58: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 531: 2159, 533: 2159, 2159, 2159, 540: 2159, 2159, 543: 2159, 2159, 2159, 547: 2159, 2159, 552: 2159, 2159, 560: 2159, 578: 2159, 586: 2159, 2159, 614: 2159, 617: 2159, 628: 2159, 2159, 2159, 2159, 636: 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 2159, 697: 2159, 2159, 2159, 2159}, + {2: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 10: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 58: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 531: 2157, 533: 2157, 2157, 2157, 540: 2157, 2157, 543: 2157, 2157, 2157, 547: 2157, 2157, 552: 2157, 2157, 560: 2157, 578: 2157, 586: 2157, 2157, 614: 2157, 617: 2157, 628: 2157, 2157, 2157, 2157, 636: 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 2157, 697: 2157, 2157, 2157, 2157}, + {2: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 10: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 58: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 531: 2155, 533: 2155, 2155, 2155, 540: 2155, 2155, 543: 2155, 2155, 2155, 547: 2155, 2155, 552: 2155, 2155, 560: 2155, 578: 2155, 586: 2155, 2155, 614: 2155, 617: 2155, 628: 2155, 2155, 2155, 2155, 636: 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 2155, 697: 2155, 2155, 2155, 2155}, + {226: 4021, 548: 4022, 629: 4020, 4019}, + // 1145 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 4013, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 4014, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 4012, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 715: 4015, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 4010, 1319: 4011}, + {2: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 10: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 58: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 531: 2174, 533: 2174, 2174, 2174, 540: 2174, 2174, 543: 2174, 2174, 2174, 547: 2174, 2174, 552: 2174, 2174, 560: 2174, 578: 2174, 586: 2174, 2174, 614: 2174, 617: 2174, 628: 2174, 2174, 2174, 2174, 636: 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 2174, 697: 2174, 2174, 2174, 2174, 715: 2174}, + {2: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 10: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 58: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 531: 2173, 533: 2173, 2173, 2173, 540: 2173, 2173, 543: 2173, 2173, 2173, 547: 2173, 2173, 552: 2173, 2173, 560: 2173, 578: 2173, 586: 2173, 2173, 614: 2173, 617: 2173, 628: 2173, 2173, 2173, 2173, 636: 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 2173, 697: 2173, 2173, 2173, 2173, 715: 2173}, + {2: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 10: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 58: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 531: 2172, 533: 2172, 2172, 2172, 540: 2172, 2172, 543: 2172, 2172, 2172, 547: 2172, 2172, 552: 2172, 2172, 560: 2172, 578: 2172, 586: 2172, 2172, 614: 2172, 617: 2172, 628: 2172, 2172, 2172, 2172, 636: 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 2172, 697: 2172, 2172, 2172, 2172, 715: 2172}, + {2: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 10: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 58: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 531: 2171, 533: 2171, 2171, 2171, 540: 2171, 2171, 543: 2171, 2171, 2171, 547: 2171, 2171, 552: 2171, 2171, 560: 2171, 578: 2171, 586: 2171, 2171, 614: 2171, 617: 2171, 628: 2171, 2171, 2171, 2171, 636: 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171, 697: 2171, 2171, 2171, 2171, 715: 2171}, + // 1150 + {2: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 10: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 58: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 531: 2170, 533: 2170, 2170, 2170, 540: 2170, 2170, 543: 2170, 2170, 2170, 547: 2170, 2170, 552: 2170, 2170, 560: 2170, 578: 2170, 586: 2170, 2170, 614: 2170, 617: 2170, 628: 2170, 2170, 2170, 2170, 636: 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 2170, 697: 2170, 2170, 2170, 2170, 715: 2170}, + {2: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 10: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 58: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 531: 2169, 533: 2169, 2169, 2169, 540: 2169, 2169, 543: 2169, 2169, 2169, 547: 2169, 2169, 552: 2169, 2169, 560: 2169, 578: 2169, 586: 2169, 2169, 614: 2169, 617: 2169, 628: 2169, 2169, 2169, 2169, 636: 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 2169, 697: 2169, 2169, 2169, 2169, 715: 2169}, + {2: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 10: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 58: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 531: 2168, 533: 2168, 2168, 2168, 540: 2168, 2168, 543: 2168, 2168, 2168, 547: 2168, 2168, 552: 2168, 2168, 560: 2168, 578: 2168, 586: 2168, 2168, 614: 2168, 617: 2168, 628: 2168, 2168, 2168, 2168, 636: 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 2168, 697: 2168, 2168, 2168, 2168, 715: 2168}, + {2: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 10: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 58: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 531: 2167, 533: 2167, 2167, 2167, 540: 2167, 2167, 543: 2167, 2167, 2167, 547: 2167, 2167, 552: 2167, 2167, 560: 2167, 578: 2167, 586: 2167, 2167, 614: 2167, 617: 2167, 628: 2167, 2167, 2167, 2167, 636: 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 2167, 697: 2167, 2167, 2167, 2167, 715: 2167}, + {226: 2164, 534: 3812, 536: 3811, 548: 2164, 629: 2164, 2164, 915: 4009}, + // 1155 + {226: 2163, 548: 2163, 629: 2163, 2163}, + {2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 532: 2178, 2178, 537: 2178, 539: 2178, 2178, 2178, 2178, 549: 2178, 2178, 2178, 554: 2178, 2178, 2178, 2178, 2178, 2178, 561: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 580: 2178, 2178, 2178, 2178, 585: 2178, 588: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178, 607: 2178, 619: 2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178}, + {531: 2907, 775: 4018}, + {946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 532: 946, 946, 946, 536: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 549: 946, 946, 946, 554: 946, 946, 946, 946, 946, 946, 561: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 579: 946, 946, 946, 946, 946, 946, 946, 588: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 609: 946, 946, 946, 946, 946, 615: 946, 946, 618: 946, 946, 946, 946, 946, 946, 946, 946, 946, 946, 632: 946, 946, 946, 946, 703: 946, 717: 4016}, + {1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 2154, 1967, 1967, 1967, 536: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 549: 1967, 1967, 1967, 554: 1967, 1967, 1967, 1967, 1967, 1967, 561: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 579: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 588: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 609: 1967, 1967, 1967, 1967, 1967, 615: 1967, 1967, 618: 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 1967, 632: 1967, 1967, 1967, 1967, 703: 1967, 716: 1967, 718: 1967, 1967}, + // 1160 + {1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 2153, 1966, 1966, 1966, 536: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 549: 1966, 1966, 1966, 554: 1966, 1966, 1966, 1966, 1966, 1966, 561: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 579: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 588: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 609: 1966, 1966, 1966, 1966, 1966, 615: 1966, 1966, 618: 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 1966, 632: 1966, 1966, 1966, 1966, 703: 1966, 716: 1966, 718: 1966, 1966}, + {531: 2152}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 4017}, + {2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 532: 2176, 2176, 537: 2176, 539: 2176, 2176, 2176, 2176, 549: 2176, 2176, 2176, 554: 2176, 2176, 2176, 2176, 2176, 2176, 561: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 580: 2176, 2176, 2176, 2176, 585: 2176, 588: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176, 607: 2176, 619: 2176, 2176, 2176, 2176, 2176, 2176, 2176, 2176}, + {2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 532: 2177, 2177, 537: 2177, 539: 2177, 2177, 2177, 2177, 549: 2177, 2177, 2177, 554: 2177, 2177, 2177, 2177, 2177, 2177, 561: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 580: 2177, 2177, 2177, 2177, 585: 2177, 588: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177, 607: 2177, 619: 2177, 2177, 2177, 2177, 2177, 2177, 2177, 2177}, + // 1165 + {2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 532: 2207, 2207, 537: 2207, 539: 2207, 2207, 2207, 2207, 549: 2207, 2207, 2207, 554: 2207, 556: 2207, 2207, 2207, 2207, 561: 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 580: 2207, 2207, 2207, 2207, 585: 2207, 588: 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 607: 2207}, + {2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 532: 2206, 2206, 537: 2206, 539: 2206, 2206, 2206, 2206, 549: 2206, 2206, 2206, 554: 2206, 556: 2206, 2206, 2206, 2206, 561: 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 580: 2206, 2206, 2206, 2206, 585: 2206, 588: 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 2206, 607: 2206}, + {2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 532: 2205, 2205, 537: 2205, 539: 2205, 2205, 2205, 2205, 549: 2205, 2205, 2205, 554: 2205, 556: 2205, 2205, 2205, 2205, 561: 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 580: 2205, 2205, 2205, 2205, 585: 2205, 588: 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 2205, 607: 2205}, + {2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 532: 2179, 2179, 537: 2179, 539: 2179, 2179, 2179, 2179, 549: 2179, 2179, 2179, 554: 2179, 2179, 2179, 2179, 2179, 2179, 561: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 580: 2179, 2179, 2179, 2179, 585: 2179, 588: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179, 607: 2179, 619: 2179, 2179, 2179, 2179, 2179, 2179, 2179, 2179}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 4026}, + // 1170 + {2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 531: 2613, 546: 2613, 553: 2613, 555: 2613, 2613, 2613, 577: 2613, 586: 2613, 605: 2613, 709: 2613, 716: 4047, 2613, 727: 2613, 2613, 730: 2613, 732: 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 744: 2613, 2613, 748: 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613, 2613}, + {9: 2610, 57: 2610}, + {9: 4027, 57: 4028}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4046}, + {364: 4029}, + // 1175 + {531: 4030}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4031}, + {57: 2199, 532: 4034, 543: 3782, 3783, 3788, 584: 3784, 606: 4033, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 1366: 4032}, + {57: 4045}, + {188: 4038, 580: 4037}, + // 1180 + {159: 4035}, + {307: 4036}, + {57: 2195}, + {402: 4040}, + {264: 4039}, + // 1185 + {57: 2196}, + {264: 4041}, + {57: 2198, 532: 4042}, + {159: 4043}, + {307: 4044}, + // 1190 + {57: 2197}, + {2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 532: 2208, 2208, 537: 2208, 539: 2208, 2208, 2208, 2208, 549: 2208, 2208, 2208, 554: 2208, 556: 2208, 2208, 2208, 2208, 561: 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 580: 2208, 2208, 2208, 2208, 585: 2208, 588: 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 2208, 607: 2208}, + {9: 2609, 57: 2609}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4048, 3051, 3052, 3050}, + {2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 531: 2612, 546: 2612, 553: 2612, 555: 2612, 2612, 2612, 577: 2612, 586: 2612, 605: 2612, 709: 2612, 716: 4049, 2612, 727: 2612, 2612, 730: 2612, 732: 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 744: 2612, 2612, 748: 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612, 2612}, + // 1195 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4050, 3051, 3052, 3050}, + {2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 531: 2611, 546: 2611, 553: 2611, 555: 2611, 2611, 2611, 577: 2611, 586: 2611, 605: 2611, 709: 2611, 717: 2611, 727: 2611, 2611, 730: 2611, 732: 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 744: 2611, 2611, 748: 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611, 2611}, + {2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 532: 2209, 2209, 537: 2209, 539: 2209, 2209, 2209, 2209, 549: 2209, 2209, 2209, 554: 2209, 556: 2209, 2209, 2209, 2209, 561: 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 580: 2209, 2209, 2209, 2209, 585: 2209, 588: 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 2209, 607: 2209, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4053, 3597, 3679, 3596, 3593}, + {57: 4054, 538: 3693, 703: 3694}, + // 1200 + {184: 1134, 549: 1134, 561: 4056, 820: 1134, 1402: 4055}, + {184: 4060, 549: 4061, 820: 1137, 988: 4059}, + {10: 4057, 234: 4058}, + {184: 1133, 549: 1133, 820: 1133}, + {184: 1132, 549: 1132, 820: 1132}, + // 1205 + {820: 4064, 832: 4065}, + {327: 4063}, + {327: 4062}, + {820: 1135}, + {820: 1136}, + // 1210 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 4067, 770: 4066, 3051, 3052, 3050, 1026: 4069, 1306: 4070, 1507: 4068}, + {1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 532: 1143, 1143, 1143, 536: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 549: 1143, 1143, 1143, 554: 1143, 1143, 1143, 1143, 1143, 1143, 561: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 579: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 588: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 609: 1143, 1143, 1143, 1143, 1143, 615: 1143, 1143, 618: 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 1143, 632: 1143, 1143, 1143, 1143, 696: 1143, 703: 1143}, + {1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 532: 1185, 1185, 1185, 536: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 549: 1185, 1185, 1185, 554: 1185, 1185, 1185, 1185, 1185, 1185, 561: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 579: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 588: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 609: 1185, 1185, 1185, 1185, 1185, 615: 1185, 1185, 618: 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 1185, 632: 1185, 1185, 1185, 1185, 696: 1185, 703: 1185}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1182, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 546: 1182, 564: 1182, 588: 1182, 591: 1182, 593: 1182, 770: 4066, 3051, 3052, 3050, 1026: 4073, 1401: 4072, 1508: 4071}, + {1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 532: 1156, 1156, 1156, 536: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 549: 1156, 1156, 1156, 554: 1156, 1156, 1156, 1156, 1156, 1156, 561: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 579: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 588: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 609: 1156, 1156, 1156, 1156, 1156, 615: 1156, 1156, 618: 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 1156, 632: 1156, 1156, 1156, 1156, 696: 1156, 703: 1156}, + // 1215 + {1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 532: 1155, 1155, 1155, 536: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 549: 1155, 1155, 1155, 554: 1155, 1155, 1155, 1155, 1155, 1155, 561: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 579: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 588: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 609: 1155, 1155, 1155, 1155, 1155, 615: 1155, 1155, 618: 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 1155, 632: 1155, 1155, 1155, 1155, 696: 1155, 703: 1155}, + {1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 532: 1154, 1154, 1154, 536: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 549: 1154, 1154, 1154, 554: 1154, 1154, 1154, 1154, 1154, 1154, 561: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 579: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 588: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 609: 1154, 1154, 1154, 1154, 1154, 615: 1154, 1154, 618: 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 1154, 632: 1154, 1154, 1154, 1154, 696: 1154, 703: 1154}, + {57: 4120}, + {57: 1180, 546: 4075, 564: 1180, 588: 1180, 591: 1180, 593: 1180, 1405: 4074}, + {57: 1181, 546: 1181, 564: 1181, 588: 1181, 591: 1181, 593: 1181}, + // 1220 + {57: 1178, 564: 4079, 588: 1178, 591: 1178, 593: 1178, 1410: 4078}, + {723: 4076}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 4077}, + {9: 3884, 57: 1179, 564: 1179, 588: 1179, 591: 1179, 593: 1179}, + {57: 1176, 588: 4084, 591: 4085, 593: 4086, 1409: 4082, 1506: 4083}, + // 1225 + {723: 4080}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 4081}, + {9: 3884, 57: 1177, 588: 1177, 591: 1177, 593: 1177}, + {57: 1183}, + {189: 4097, 201: 4093, 560: 4087, 627: 4098, 636: 4089, 4088, 641: 4096, 4095, 916: 4094, 1097: 4091, 1504: 4092, 4090}, + // 1230 + {189: 1174, 201: 1174, 560: 1174, 627: 1174, 636: 1174, 1174, 641: 1174, 1174}, + {189: 1173, 201: 1173, 560: 1173, 627: 1173, 636: 1173, 1173, 641: 1173, 1173}, + {189: 1172, 201: 1172, 560: 1172, 627: 1172, 636: 1172, 1172, 641: 1172, 1172}, + {2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 2494, 57: 2494, 167: 2494, 191: 2494, 530: 2494, 2494, 2494, 534: 2494, 2494, 2494, 2494, 2494, 2494, 546: 2494, 2494, 2494, 2494, 552: 2494, 2494, 565: 2494, 608: 2494, 695: 2494, 701: 2494, 2494, 704: 2494, 2494, 2494, 2494, 2494, 2494, 2494}, + {2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 2493, 57: 2493, 167: 2493, 191: 2493, 243: 2493, 530: 2493, 2493, 2493, 534: 2493, 2493, 2493, 2493, 2493, 2493, 546: 2493, 2493, 2493, 2493, 552: 2493, 2493, 565: 2493, 608: 2493, 695: 2493, 701: 2493, 2493, 704: 2493, 2493, 2493, 2493, 2493, 2493, 2493}, + // 1235 + {2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 2492, 57: 2492, 167: 2492, 191: 2492, 243: 2492, 530: 2492, 2492, 2492, 534: 2492, 2492, 2492, 2492, 2492, 2492, 546: 2492, 2492, 2492, 2492, 552: 2492, 2492, 565: 2492, 608: 2492, 695: 2492, 701: 2492, 2492, 704: 2492, 2492, 2492, 2492, 2492, 2492, 2492}, + {57: 1175}, + {57: 1171}, + {57: 1170}, + {167: 4115}, + // 1240 + {167: 4113}, + {167: 4111}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4118}, + {639: 4117}, + {189: 4097, 201: 4099, 560: 4087, 636: 4089, 4088, 641: 4102, 4101, 916: 4100, 1097: 4104, 1305: 4103}, + // 1245 + {167: 4115, 191: 4116}, + {167: 4113, 191: 4114}, + {167: 4111, 191: 4112}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4107}, + {566: 4105}, + // 1250 + {57: 1163, 566: 1163}, + {189: 4097, 201: 4099, 560: 4087, 636: 4089, 4088, 641: 4102, 4101, 916: 4100, 1097: 4104, 1305: 4106}, + {57: 1164}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4108}, + {167: 4109, 191: 4110}, + // 1255 + {57: 1166, 566: 1166}, + {57: 1159, 566: 1159}, + {57: 1167, 566: 1167}, + {57: 1160, 566: 1160}, + {57: 1168, 566: 1168}, + // 1260 + {57: 1161, 566: 1161}, + {57: 1169, 566: 1169}, + {57: 1162, 566: 1162}, + {57: 1165, 566: 1165}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4119}, + // 1265 + {167: 4109}, + {1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 532: 1184, 1184, 1184, 536: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 549: 1184, 1184, 1184, 554: 1184, 1184, 1184, 1184, 1184, 1184, 561: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 579: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 588: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 609: 1184, 1184, 1184, 1184, 1184, 615: 1184, 1184, 618: 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 1184, 632: 1184, 1184, 1184, 1184, 696: 1184, 703: 1184}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4122}, + {2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 532: 2213, 2213, 537: 2213, 539: 2213, 2213, 2213, 2213, 549: 2213, 2213, 2213, 554: 2213, 556: 2213, 2213, 2213, 2213, 561: 2213, 2213, 2213, 2213, 2213, 3745, 3743, 3744, 3742, 3740, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 580: 2213, 2213, 2213, 2213, 585: 2213, 588: 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 2213, 607: 2213, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4124}, + // 1270 + {57: 4125, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {184: 4060, 549: 4061, 820: 1137, 988: 4126}, + {820: 4064, 832: 4127}, + {1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 532: 1144, 1144, 1144, 536: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 549: 1144, 1144, 1144, 554: 1144, 1144, 1144, 1144, 1144, 1144, 561: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 579: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 588: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 609: 1144, 1144, 1144, 1144, 1144, 615: 1144, 1144, 618: 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 1144, 632: 1144, 1144, 1144, 1144, 696: 1144, 703: 1144}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4129}, + // 1275 + {57: 4130, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {184: 4060, 549: 4061, 820: 1137, 988: 4131}, + {820: 4064, 832: 4132}, + {1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 532: 1145, 1145, 1145, 536: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 549: 1145, 1145, 1145, 554: 1145, 1145, 1145, 1145, 1145, 1145, 561: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 579: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 588: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 609: 1145, 1145, 1145, 1145, 1145, 615: 1145, 1145, 618: 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 1145, 632: 1145, 1145, 1145, 1145, 696: 1145, 703: 1145}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4134}, + // 1280 + {9: 4136, 57: 1142, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739, 1226: 4135}, + {57: 4143}, + {560: 4087, 636: 4089, 4088, 642: 4138, 916: 4137}, + {9: 4140, 57: 1139, 1227: 4142}, + {9: 4140, 57: 1139, 1227: 4139}, + // 1285 + {57: 1140}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4141}, + {57: 1138, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {57: 1141}, + {184: 4060, 549: 4061, 820: 1137, 988: 4144}, + // 1290 + {820: 4064, 832: 4145}, + {1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 532: 1146, 1146, 1146, 536: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 549: 1146, 1146, 1146, 554: 1146, 1146, 1146, 1146, 1146, 1146, 561: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 579: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 588: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 609: 1146, 1146, 1146, 1146, 1146, 615: 1146, 1146, 618: 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146, 632: 1146, 1146, 1146, 1146, 696: 1146, 703: 1146}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4147}, + {9: 4136, 57: 1142, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739, 1226: 4148}, + {57: 4149}, + // 1295 + {184: 4060, 549: 4061, 820: 1137, 988: 4150}, + {820: 4064, 832: 4151}, + {1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 532: 1147, 1147, 1147, 536: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 549: 1147, 1147, 1147, 554: 1147, 1147, 1147, 1147, 1147, 1147, 561: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 579: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 588: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 609: 1147, 1147, 1147, 1147, 1147, 615: 1147, 1147, 618: 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 1147, 632: 1147, 1147, 1147, 1147, 696: 1147, 703: 1147}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4153, 3597, 3679, 3596, 3593}, + {57: 4154, 538: 3693, 703: 3694}, + // 1300 + {820: 4064, 832: 4155}, + {1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 532: 1148, 1148, 1148, 536: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 549: 1148, 1148, 1148, 554: 1148, 1148, 1148, 1148, 1148, 1148, 561: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 579: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 588: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 609: 1148, 1148, 1148, 1148, 1148, 615: 1148, 1148, 618: 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 1148, 632: 1148, 1148, 1148, 1148, 696: 1148, 703: 1148}, + {57: 4157}, + {820: 4064, 832: 4158}, + {1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 532: 1149, 1149, 1149, 536: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 549: 1149, 1149, 1149, 554: 1149, 1149, 1149, 1149, 1149, 1149, 561: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 579: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 588: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 609: 1149, 1149, 1149, 1149, 1149, 615: 1149, 1149, 618: 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 1149, 632: 1149, 1149, 1149, 1149, 696: 1149, 703: 1149}, + // 1305 + {57: 4160}, + {820: 4064, 832: 4161}, + {1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 532: 1150, 1150, 1150, 536: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 549: 1150, 1150, 1150, 554: 1150, 1150, 1150, 1150, 1150, 1150, 561: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 579: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 588: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 609: 1150, 1150, 1150, 1150, 1150, 615: 1150, 1150, 618: 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150, 632: 1150, 1150, 1150, 1150, 696: 1150, 703: 1150}, + {57: 4163}, + {820: 4064, 832: 4164}, + // 1310 + {1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 532: 1151, 1151, 1151, 536: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 549: 1151, 1151, 1151, 554: 1151, 1151, 1151, 1151, 1151, 1151, 561: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 579: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 588: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 609: 1151, 1151, 1151, 1151, 1151, 615: 1151, 1151, 618: 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 1151, 632: 1151, 1151, 1151, 1151, 696: 1151, 703: 1151}, + {57: 4166}, + {820: 4064, 832: 4167}, + {1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 532: 1152, 1152, 1152, 536: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 549: 1152, 1152, 1152, 554: 1152, 1152, 1152, 1152, 1152, 1152, 561: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 579: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 588: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 609: 1152, 1152, 1152, 1152, 1152, 615: 1152, 1152, 618: 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 632: 1152, 1152, 1152, 1152, 696: 1152, 703: 1152}, + {57: 4169}, + // 1315 + {820: 4064, 832: 4170}, + {1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 532: 1153, 1153, 1153, 536: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 549: 1153, 1153, 1153, 554: 1153, 1153, 1153, 1153, 1153, 1153, 561: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 579: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 588: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 609: 1153, 1153, 1153, 1153, 1153, 615: 1153, 1153, 618: 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 1153, 632: 1153, 1153, 1153, 1153, 696: 1153, 703: 1153}, + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4178, 4177}, + {2: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 10: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 58: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 531: 1445, 533: 1445, 1445, 1445, 1445, 540: 1445, 1445, 543: 1445, 1445, 1445, 547: 1445, 1445, 552: 1445, 1445, 560: 1445, 573: 1445, 578: 1445, 584: 1445, 586: 1445, 1445, 608: 1445, 614: 1445, 617: 1445, 628: 1445, 1445, 1445, 1445, 636: 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 1445, 697: 1445, 1445, 1445, 1445, 711: 1445, 715: 1445, 829: 1445, 1445, 836: 1445, 1445, 1445, 840: 1445, 849: 1445, 1445, 1445}, + {2: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 10: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 58: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 531: 1444, 533: 1444, 1444, 1444, 1444, 540: 1444, 1444, 543: 1444, 1444, 1444, 547: 1444, 1444, 552: 1444, 1444, 560: 1444, 573: 1444, 578: 1444, 584: 1444, 586: 1444, 1444, 608: 1444, 614: 1444, 617: 1444, 628: 1444, 1444, 1444, 1444, 636: 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 1444, 697: 1444, 1444, 1444, 1444, 711: 1444, 715: 1444, 829: 1444, 1444, 836: 1444, 1444, 1444, 840: 1444, 849: 1444, 1444, 1444}, + // 1320 + {2: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 10: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 58: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 531: 1443, 533: 1443, 1443, 1443, 1443, 540: 1443, 1443, 543: 1443, 1443, 1443, 547: 1443, 1443, 552: 1443, 1443, 560: 1443, 573: 1443, 578: 1443, 584: 1443, 586: 1443, 1443, 608: 1443, 614: 1443, 617: 1443, 628: 1443, 1443, 1443, 1443, 636: 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 1443, 697: 1443, 1443, 1443, 1443, 711: 1443, 715: 1443, 829: 1443, 1443, 836: 1443, 1443, 1443, 840: 1443, 849: 1443, 1443, 1443}, + {2: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 10: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 58: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 531: 1442, 533: 1442, 1442, 1442, 1442, 540: 1442, 1442, 543: 1442, 1442, 1442, 547: 1442, 1442, 552: 1442, 1442, 560: 1442, 578: 1442, 586: 1442, 1442, 614: 1442, 617: 1442, 628: 1442, 1442, 1442, 1442, 636: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 697: 1442, 1442, 1442, 1442, 711: 1442, 715: 4183}, + {2: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 10: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 58: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 531: 1440, 533: 1440, 1440, 1440, 1440, 540: 1440, 1440, 543: 1440, 1440, 1440, 547: 1440, 1440, 552: 1440, 1440, 560: 1440, 578: 1440, 586: 1440, 1440, 614: 1440, 617: 1440, 628: 1440, 1440, 1440, 1440, 636: 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 697: 1440, 1440, 1440, 1440, 711: 1440}, + {2: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 10: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 58: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 531: 1437, 533: 1437, 1437, 1437, 1437, 540: 1437, 1437, 543: 1437, 1437, 1437, 547: 1437, 1437, 552: 1437, 1437, 560: 1437, 578: 1437, 586: 1437, 1437, 614: 1437, 617: 1437, 628: 1437, 1437, 1437, 1437, 636: 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 1437, 697: 1437, 1437, 1437, 1437, 711: 1437}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4179}, + // 1325 + {57: 4180, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4181}, + {1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 532: 1322, 1322, 1322, 536: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 549: 1322, 1322, 1322, 554: 1322, 1322, 1322, 1322, 1322, 1322, 561: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 579: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 588: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 609: 1322, 1322, 1322, 1322, 1322, 615: 1322, 1322, 618: 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322, 632: 1322, 1322, 1322, 1322, 696: 1322, 703: 1322}, + {1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 532: 1157, 1157, 1157, 536: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 549: 1157, 1157, 1157, 554: 1157, 1157, 1157, 1157, 1157, 1157, 561: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 579: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 588: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 609: 1157, 1157, 1157, 1157, 1157, 615: 1157, 1157, 618: 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 1157, 632: 1157, 1157, 1157, 1157, 696: 1157, 703: 1157}, + {2: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 10: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 58: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 531: 1436, 533: 1436, 1436, 1436, 1436, 540: 1436, 1436, 543: 1436, 1436, 1436, 547: 1436, 1436, 552: 1436, 1436, 560: 1436, 578: 1436, 586: 1436, 1436, 614: 1436, 617: 1436, 628: 1436, 1436, 1436, 1436, 636: 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 1436, 697: 1436, 1436, 1436, 1436, 711: 1436}, + // 1330 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4185, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4186}, + {57: 4187, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4188}, + {1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 532: 1323, 1323, 1323, 536: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 549: 1323, 1323, 1323, 554: 1323, 1323, 1323, 1323, 1323, 1323, 561: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 579: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 588: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 609: 1323, 1323, 1323, 1323, 1323, 615: 1323, 1323, 618: 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 1323, 632: 1323, 1323, 1323, 1323, 696: 1323, 703: 1323}, + // 1335 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4190, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4191}, + {57: 4192, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4193}, + {1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 532: 1324, 1324, 1324, 536: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 549: 1324, 1324, 1324, 554: 1324, 1324, 1324, 1324, 1324, 1324, 561: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 579: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 588: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 609: 1324, 1324, 1324, 1324, 1324, 615: 1324, 1324, 618: 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 1324, 632: 1324, 1324, 1324, 1324, 696: 1324, 703: 1324}, + // 1340 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4195, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4196}, + {57: 4197, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4198}, + {1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 532: 1325, 1325, 1325, 536: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 549: 1325, 1325, 1325, 554: 1325, 1325, 1325, 1325, 1325, 1325, 561: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 579: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 588: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 609: 1325, 1325, 1325, 1325, 1325, 615: 1325, 1325, 618: 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 1325, 632: 1325, 1325, 1325, 1325, 696: 1325, 703: 1325}, + // 1345 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4200, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4201}, + {57: 4202, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4203}, + {1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 532: 1326, 1326, 1326, 536: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 549: 1326, 1326, 1326, 554: 1326, 1326, 1326, 1326, 1326, 1326, 561: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 579: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 588: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 609: 1326, 1326, 1326, 1326, 1326, 615: 1326, 1326, 618: 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326, 632: 1326, 1326, 1326, 1326, 696: 1326, 703: 1326}, + // 1350 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4205, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4206}, + {57: 4207, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4208}, + {1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 532: 1327, 1327, 1327, 536: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 549: 1327, 1327, 1327, 554: 1327, 1327, 1327, 1327, 1327, 1327, 561: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 579: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 588: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 609: 1327, 1327, 1327, 1327, 1327, 615: 1327, 1327, 618: 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 1327, 632: 1327, 1327, 1327, 1327, 696: 1327, 703: 1327}, + // 1355 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4210, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4211}, + {57: 4212, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4213}, + {1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 532: 1328, 1328, 1328, 536: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 549: 1328, 1328, 1328, 554: 1328, 1328, 1328, 1328, 1328, 1328, 561: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 579: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 588: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 609: 1328, 1328, 1328, 1328, 1328, 615: 1328, 1328, 618: 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 1328, 632: 1328, 1328, 1328, 1328, 696: 1328, 703: 1328}, + // 1360 + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4215, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4216}, + {9: 3990, 57: 1499, 160: 1499, 564: 3857, 842: 3911, 909: 4217}, + {57: 1315, 160: 4219, 1403: 4218}, + {57: 4221}, + // 1365 + {533: 4220}, + {57: 1314}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4222}, + {1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 532: 1329, 1329, 1329, 536: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 549: 1329, 1329, 1329, 554: 1329, 1329, 1329, 1329, 1329, 1329, 561: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 579: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 588: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 609: 1329, 1329, 1329, 1329, 1329, 615: 1329, 1329, 618: 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 1329, 632: 1329, 1329, 1329, 1329, 696: 1329, 703: 1329}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 4227, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4226, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4224, 829: 4172, 4173, 885: 4225}, + // 1370 + {57: 4235, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4233}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4230}, + {57: 4228}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4229}, + // 1375 + {1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 532: 1330, 1330, 1330, 536: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 549: 1330, 1330, 1330, 554: 1330, 1330, 1330, 1330, 1330, 1330, 561: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 579: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 588: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 609: 1330, 1330, 1330, 1330, 1330, 615: 1330, 1330, 618: 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 1330, 632: 1330, 1330, 1330, 1330, 696: 1330, 703: 1330}, + {57: 4231, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4232}, + {1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 532: 1332, 1332, 1332, 536: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 549: 1332, 1332, 1332, 554: 1332, 1332, 1332, 1332, 1332, 1332, 561: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 579: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 588: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 609: 1332, 1332, 1332, 1332, 1332, 615: 1332, 1332, 618: 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 632: 1332, 1332, 1332, 1332, 696: 1332, 703: 1332}, + {9: 3990, 57: 4234}, + // 1380 + {1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 532: 1333, 1333, 1333, 536: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 549: 1333, 1333, 1333, 554: 1333, 1333, 1333, 1333, 1333, 1333, 561: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 579: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 588: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 609: 1333, 1333, 1333, 1333, 1333, 615: 1333, 1333, 618: 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 1333, 632: 1333, 1333, 1333, 1333, 696: 1333, 703: 1333}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4236}, + {1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 532: 1331, 1331, 1331, 536: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 549: 1331, 1331, 1331, 554: 1331, 1331, 1331, 1331, 1331, 1331, 561: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 579: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 588: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 609: 1331, 1331, 1331, 1331, 1331, 615: 1331, 1331, 618: 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 1331, 632: 1331, 1331, 1331, 1331, 696: 1331, 703: 1331}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4239, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4238}, + {57: 4243, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1385 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4240}, + {57: 4241, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4242}, + {1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 532: 1334, 1334, 1334, 536: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 549: 1334, 1334, 1334, 554: 1334, 1334, 1334, 1334, 1334, 1334, 561: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 579: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 588: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 609: 1334, 1334, 1334, 1334, 1334, 615: 1334, 1334, 618: 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 1334, 632: 1334, 1334, 1334, 1334, 696: 1334, 703: 1334}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4244}, + // 1390 + {1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 532: 1335, 1335, 1335, 536: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 549: 1335, 1335, 1335, 554: 1335, 1335, 1335, 1335, 1335, 1335, 561: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 579: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 588: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 609: 1335, 1335, 1335, 1335, 1335, 615: 1335, 1335, 618: 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 1335, 632: 1335, 1335, 1335, 1335, 696: 1335, 703: 1335}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4247, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4246}, + {57: 4251, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4248}, + {57: 4249, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1395 + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4250}, + {1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 532: 1336, 1336, 1336, 536: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 549: 1336, 1336, 1336, 554: 1336, 1336, 1336, 1336, 1336, 1336, 561: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 579: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 588: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 609: 1336, 1336, 1336, 1336, 1336, 615: 1336, 1336, 618: 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 1336, 632: 1336, 1336, 1336, 1336, 696: 1336, 703: 1336}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4252}, + {1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 532: 1337, 1337, 1337, 536: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 549: 1337, 1337, 1337, 554: 1337, 1337, 1337, 1337, 1337, 1337, 561: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 579: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 588: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 609: 1337, 1337, 1337, 1337, 1337, 615: 1337, 1337, 618: 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 1337, 632: 1337, 1337, 1337, 1337, 696: 1337, 703: 1337}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4255, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4254}, + // 1400 + {57: 4259, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4256}, + {57: 4257, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4258}, + {1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 532: 1338, 1338, 1338, 536: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 549: 1338, 1338, 1338, 554: 1338, 1338, 1338, 1338, 1338, 1338, 561: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 579: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 588: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 609: 1338, 1338, 1338, 1338, 1338, 615: 1338, 1338, 618: 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 1338, 632: 1338, 1338, 1338, 1338, 696: 1338, 703: 1338}, + // 1405 + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4260}, + {1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 532: 1339, 1339, 1339, 536: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 549: 1339, 1339, 1339, 554: 1339, 1339, 1339, 1339, 1339, 1339, 561: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 579: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 588: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 609: 1339, 1339, 1339, 1339, 1339, 615: 1339, 1339, 618: 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 1339, 632: 1339, 1339, 1339, 1339, 696: 1339, 703: 1339}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4262}, + {9: 3990, 57: 4263}, + {1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 532: 1340, 1340, 1340, 536: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 549: 1340, 1340, 1340, 554: 1340, 1340, 1340, 1340, 1340, 1340, 561: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 579: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 588: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 609: 1340, 1340, 1340, 1340, 1340, 615: 1340, 1340, 618: 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 1340, 632: 1340, 1340, 1340, 1340, 696: 1340, 703: 1340}, + // 1410 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4265}, + {9: 3990, 57: 4266}, + {1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 532: 1341, 1341, 1341, 536: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 549: 1341, 1341, 1341, 554: 1341, 1341, 1341, 1341, 1341, 1341, 561: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 579: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 588: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 609: 1341, 1341, 1341, 1341, 1341, 615: 1341, 1341, 618: 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 632: 1341, 1341, 1341, 1341, 696: 1341, 703: 1341}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4268}, + {9: 4269, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1415 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4270}, + {9: 4271, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4272}, + {57: 4273, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 532: 1357, 1357, 1357, 536: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 549: 1357, 1357, 1357, 554: 1357, 1357, 1357, 1357, 1357, 1357, 561: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 579: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 588: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 609: 1357, 1357, 1357, 1357, 1357, 615: 1357, 1357, 618: 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 1357, 632: 1357, 1357, 1357, 1357, 696: 1357, 703: 1357}, + // 1420 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4275, 1328: 4277, 1382: 4278, 1485: 4279, 4276}, + {57: 4287, 561: 4288, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 4281, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4280}, + {2: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 10: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 58: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 531: 1348, 533: 1348, 1348, 1348, 1348, 540: 1348, 1348, 543: 1348, 1348, 1348, 547: 1348, 1348, 552: 1348, 1348, 560: 1348, 1348, 578: 1348, 586: 1348, 1348, 614: 1348, 617: 1348, 628: 1348, 1348, 1348, 1348, 636: 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 1348, 697: 1348, 1348, 1348, 1348, 711: 1348}, + {2: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 10: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 58: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 531: 1347, 533: 1347, 1347, 1347, 1347, 540: 1347, 1347, 543: 1347, 1347, 1347, 547: 1347, 1347, 552: 1347, 1347, 560: 1347, 1347, 578: 1347, 586: 1347, 1347, 614: 1347, 617: 1347, 628: 1347, 1347, 1347, 1347, 636: 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347, 697: 1347, 1347, 1347, 1347, 711: 1347}, + // 1425 + {2: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 10: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 58: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 531: 1346, 533: 1346, 1346, 1346, 1346, 540: 1346, 1346, 543: 1346, 1346, 1346, 547: 1346, 1346, 552: 1346, 1346, 560: 1346, 1346, 578: 1346, 586: 1346, 1346, 614: 1346, 617: 1346, 628: 1346, 1346, 1346, 1346, 636: 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 1346, 697: 1346, 1346, 1346, 1346, 711: 1346}, + {561: 4284, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4282}, + {57: 4283, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 532: 1363, 1363, 1363, 536: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 549: 1363, 1363, 1363, 554: 1363, 1363, 1363, 1363, 1363, 1363, 561: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 579: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 588: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 609: 1363, 1363, 1363, 1363, 1363, 615: 1363, 1363, 618: 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363, 632: 1363, 1363, 1363, 1363, 696: 1363, 703: 1363}, + // 1430 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4285}, + {57: 4286, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 532: 1362, 1362, 1362, 536: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 549: 1362, 1362, 1362, 554: 1362, 1362, 1362, 1362, 1362, 1362, 561: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 579: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 588: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 609: 1362, 1362, 1362, 1362, 1362, 615: 1362, 1362, 618: 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 1362, 632: 1362, 1362, 1362, 1362, 696: 1362, 703: 1362}, + {1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 532: 1365, 1365, 1365, 536: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 549: 1365, 1365, 1365, 554: 1365, 1365, 1365, 1365, 1365, 1365, 561: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 579: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 588: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 609: 1365, 1365, 1365, 1365, 1365, 615: 1365, 1365, 618: 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 1365, 632: 1365, 1365, 1365, 1365, 696: 1365, 703: 1365}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4289}, + // 1435 + {57: 4290, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 532: 1364, 1364, 1364, 536: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 549: 1364, 1364, 1364, 554: 1364, 1364, 1364, 1364, 1364, 1364, 561: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 579: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 588: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 609: 1364, 1364, 1364, 1364, 1364, 615: 1364, 1364, 618: 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 1364, 632: 1364, 1364, 1364, 1364, 696: 1364, 703: 1364}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4292}, + {9: 4293, 561: 4294, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4300}, + // 1440 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4295}, + {57: 4296, 558: 4297, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 532: 1370, 1370, 1370, 536: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 549: 1370, 1370, 1370, 554: 1370, 1370, 1370, 1370, 1370, 1370, 561: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 579: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 588: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 609: 1370, 1370, 1370, 1370, 1370, 615: 1370, 1370, 618: 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 1370, 632: 1370, 1370, 1370, 1370, 696: 1370, 703: 1370}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4298}, + {57: 4299, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1445 + {1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 532: 1368, 1368, 1368, 536: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 549: 1368, 1368, 1368, 554: 1368, 1368, 1368, 1368, 1368, 1368, 561: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 579: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 588: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 609: 1368, 1368, 1368, 1368, 1368, 615: 1368, 1368, 618: 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 1368, 632: 1368, 1368, 1368, 1368, 696: 1368, 703: 1368}, + {9: 4302, 57: 4301, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 532: 1371, 1371, 1371, 536: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 549: 1371, 1371, 1371, 554: 1371, 1371, 1371, 1371, 1371, 1371, 561: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 579: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 588: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 609: 1371, 1371, 1371, 1371, 1371, 615: 1371, 1371, 618: 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371, 632: 1371, 1371, 1371, 1371, 696: 1371, 703: 1371}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4303}, + {57: 4304, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1450 + {1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 532: 1369, 1369, 1369, 536: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 549: 1369, 1369, 1369, 554: 1369, 1369, 1369, 1369, 1369, 1369, 561: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 579: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 588: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 609: 1369, 1369, 1369, 1369, 1369, 615: 1369, 1369, 618: 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 1369, 632: 1369, 1369, 1369, 1369, 696: 1369, 703: 1369}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4306}, + {543: 3782, 3783, 3788, 584: 3784, 606: 4307, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4308}, + {57: 4309, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1455 + {1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 532: 1372, 1372, 1372, 536: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 549: 1372, 1372, 1372, 554: 1372, 1372, 1372, 1372, 1372, 1372, 561: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 579: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 588: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 609: 1372, 1372, 1372, 1372, 1372, 615: 1372, 1372, 618: 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 1372, 632: 1372, 1372, 1372, 1372, 696: 1372, 703: 1372}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 886: 3747, 901: 4311}, + {561: 4312}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4313}, + {57: 4314, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1460 + {1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 532: 1374, 1374, 1374, 536: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 549: 1374, 1374, 1374, 554: 1374, 1374, 1374, 1374, 1374, 1374, 561: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 579: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 588: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 609: 1374, 1374, 1374, 1374, 1374, 615: 1374, 1374, 618: 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 1374, 632: 1374, 1374, 1374, 1374, 696: 1374, 703: 1374}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4316}, + {9: 4317, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {641: 4318}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4319}, + // 1465 + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4320}, + {57: 4321}, + {1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 532: 1375, 1375, 1375, 536: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 549: 1375, 1375, 1375, 554: 1375, 1375, 1375, 1375, 1375, 1375, 561: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 579: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 588: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 609: 1375, 1375, 1375, 1375, 1375, 615: 1375, 1375, 618: 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375, 632: 1375, 1375, 1375, 1375, 696: 1375, 703: 1375}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4323}, + {9: 4324, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1470 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 4326, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4325}, + {57: 4330, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 1426, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4327}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4328}, + {57: 4329, 543: 3776}, + // 1475 + {1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 532: 1376, 1376, 1376, 536: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 549: 1376, 1376, 1376, 554: 1376, 1376, 1376, 1376, 1376, 1376, 561: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 579: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 588: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 609: 1376, 1376, 1376, 1376, 1376, 615: 1376, 1376, 618: 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 1376, 632: 1376, 1376, 1376, 1376, 696: 1376, 703: 1376}, + {1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 532: 1377, 1377, 1377, 536: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 549: 1377, 1377, 1377, 554: 1377, 1377, 1377, 1377, 1377, 1377, 561: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 579: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 588: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 609: 1377, 1377, 1377, 1377, 1377, 615: 1377, 1377, 618: 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 1377, 632: 1377, 1377, 1377, 1377, 696: 1377, 703: 1377}, + {57: 2182, 560: 4333, 1180: 4332, 4334}, + {57: 2181}, + {57: 2180}, + // 1480 + {57: 4335}, + {1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 532: 1378, 1378, 1378, 536: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 549: 1378, 1378, 1378, 554: 1378, 1378, 1378, 1378, 1378, 1378, 561: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 579: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 588: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 609: 1378, 1378, 1378, 1378, 1378, 615: 1378, 1378, 618: 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 1378, 632: 1378, 1378, 1378, 1378, 696: 1378, 703: 1378}, + {57: 2182, 560: 4333, 1180: 4332, 4337}, + {57: 4338}, + {1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 532: 1379, 1379, 1379, 536: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 549: 1379, 1379, 1379, 554: 1379, 1379, 1379, 1379, 1379, 1379, 561: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 579: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 588: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 609: 1379, 1379, 1379, 1379, 1379, 615: 1379, 1379, 618: 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 1379, 632: 1379, 1379, 1379, 1379, 696: 1379, 703: 1379}, + // 1485 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4340}, + {9: 4341, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 4342}, + {57: 4343, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 532: 1381, 1381, 1381, 536: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 549: 1381, 1381, 1381, 554: 1381, 1381, 1381, 1381, 1381, 1381, 561: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 579: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 588: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 609: 1381, 1381, 1381, 1381, 1381, 615: 1381, 1381, 618: 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381, 632: 1381, 1381, 1381, 1381, 696: 1381, 703: 1381}, + // 1490 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4346}, + {9: 3990, 57: 2183}, + {57: 4347}, + {1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 532: 1382, 1382, 1382, 536: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 549: 1382, 1382, 1382, 554: 1382, 1382, 1382, 1382, 1382, 1382, 561: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 579: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 588: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 609: 1382, 1382, 1382, 1382, 1382, 615: 1382, 1382, 618: 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 1382, 632: 1382, 1382, 1382, 1382, 696: 1382, 703: 1382}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4349}, + // 1495 + {9: 3990, 57: 4350, 542: 4351}, + {1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 532: 1387, 1387, 1387, 536: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 549: 1387, 1387, 1387, 554: 1387, 1387, 1387, 1387, 1387, 1387, 561: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 579: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 588: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 609: 1387, 1387, 1387, 1387, 1387, 615: 1387, 1387, 618: 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 1387, 632: 1387, 1387, 1387, 1387, 696: 1387, 703: 1387}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4352}, + {57: 4355}, + {955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 955, 57: 955, 131: 955, 154: 955, 530: 955, 955, 955, 534: 955, 955, 955, 955, 955, 955, 546: 955, 955, 955, 955, 552: 955, 955, 557: 955, 565: 955, 586: 955, 608: 955, 695: 955, 701: 955, 955, 704: 955, 955, 955, 955, 955, 955, 955, 721: 955, 955}, + // 1500 + {954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 954, 57: 954, 131: 954, 154: 954, 530: 954, 954, 954, 534: 954, 954, 954, 954, 954, 954, 546: 954, 954, 954, 954, 552: 954, 954, 557: 954, 565: 954, 586: 954, 608: 954, 695: 954, 701: 954, 954, 704: 954, 954, 954, 954, 954, 954, 954, 721: 954, 954}, + {1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 532: 1386, 1386, 1386, 536: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 549: 1386, 1386, 1386, 554: 1386, 1386, 1386, 1386, 1386, 1386, 561: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 579: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 588: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 609: 1386, 1386, 1386, 1386, 1386, 615: 1386, 1386, 618: 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 1386, 632: 1386, 1386, 1386, 1386, 696: 1386, 703: 1386}, + {1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 532: 1388, 1388, 1388, 536: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 549: 1388, 1388, 1388, 554: 1388, 1388, 1388, 1388, 1388, 1388, 561: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 579: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 588: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 609: 1388, 1388, 1388, 1388, 1388, 615: 1388, 1388, 618: 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388, 632: 1388, 1388, 1388, 1388, 696: 1388, 703: 1388}, + {57: 4358, 560: 4359}, + {1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 532: 1310, 1310, 1310, 536: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 549: 1310, 1310, 1310, 554: 1310, 1310, 1310, 1310, 1310, 1310, 561: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 579: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 588: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 609: 1310, 1310, 1310, 1310, 1310, 615: 1310, 1310, 618: 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 1310, 632: 1310, 1310, 1310, 1310, 696: 1310, 703: 1310}, + // 1505 + {57: 4360}, + {1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 532: 1309, 1309, 1309, 536: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 549: 1309, 1309, 1309, 554: 1309, 1309, 1309, 1309, 1309, 1309, 561: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 579: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 588: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 609: 1309, 1309, 1309, 1309, 1309, 615: 1309, 1309, 618: 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 1309, 632: 1309, 1309, 1309, 1309, 696: 1309, 703: 1309}, + {57: 4362}, + {1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 532: 1389, 1389, 1389, 536: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 549: 1389, 1389, 1389, 554: 1389, 1389, 1389, 1389, 1389, 1389, 561: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 579: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 588: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 609: 1389, 1389, 1389, 1389, 1389, 615: 1389, 1389, 618: 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 1389, 632: 1389, 1389, 1389, 1389, 696: 1389, 703: 1389}, + {57: 4365}, + // 1510 + {1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 532: 1390, 1390, 1390, 536: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 549: 1390, 1390, 1390, 554: 1390, 1390, 1390, 1390, 1390, 1390, 561: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 579: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 588: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 609: 1390, 1390, 1390, 1390, 1390, 615: 1390, 1390, 618: 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 1390, 632: 1390, 1390, 1390, 1390, 696: 1390, 703: 1390}, + {1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 532: 1404, 1404, 1404, 536: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 549: 1404, 1404, 1404, 554: 1404, 1404, 1404, 1404, 1404, 1404, 561: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 579: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 588: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 609: 1404, 1404, 1404, 1404, 1404, 615: 1404, 1404, 618: 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 1404, 632: 1404, 1404, 1404, 1404, 696: 1404, 703: 1404, 712: 1404, 717: 1404, 725: 1404}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4367}, + {57: 4368}, + {1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 532: 1391, 1391, 1391, 536: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 549: 1391, 1391, 1391, 554: 1391, 1391, 1391, 1391, 1391, 1391, 561: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 579: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 588: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 609: 1391, 1391, 1391, 1391, 1391, 615: 1391, 1391, 618: 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 1391, 632: 1391, 1391, 1391, 1391, 696: 1391, 703: 1391}, + // 1515 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4370}, + {57: 4371}, + {1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 532: 1392, 1392, 1392, 536: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 549: 1392, 1392, 1392, 554: 1392, 1392, 1392, 1392, 1392, 1392, 561: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 579: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 588: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 609: 1392, 1392, 1392, 1392, 1392, 615: 1392, 1392, 618: 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 1392, 632: 1392, 1392, 1392, 1392, 696: 1392, 703: 1392}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4373}, + {9: 4374, 542: 4375, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1520 + {59: 4386, 118: 4382, 172: 4388, 175: 4383, 4381, 179: 4385, 553: 4393, 586: 4379, 709: 4392, 734: 4384, 4389, 4390, 739: 4391, 814: 4387, 951: 4380, 1120: 4378}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4376}, + {57: 4377}, + {1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 532: 1452, 1452, 1452, 536: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 549: 1452, 1452, 1452, 554: 1452, 1452, 1452, 1452, 1452, 1452, 561: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 579: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 588: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 609: 1452, 1452, 1452, 1452, 1452, 615: 1452, 1452, 618: 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 1452, 632: 1452, 1452, 1452, 1452, 696: 1452, 703: 1452}, + {57: 4429}, + // 1525 + {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4428}, + {16: 465, 57: 465, 531: 4408, 553: 465, 586: 465, 709: 465, 721: 465, 841: 4409, 888: 4413}, + {57: 1270, 721: 1270}, + {57: 1269, 721: 1269}, + {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4412}, + // 1530 + {57: 458, 531: 4395, 721: 458, 841: 4396, 1003: 4411, 1010: 4397}, + {57: 465, 531: 4408, 721: 465, 841: 4409, 888: 4407}, + {57: 531, 721: 531, 737: 4404, 4405, 1223: 4406}, + {57: 531, 721: 531, 737: 4404, 4405, 1223: 4403}, + {57: 1263, 721: 1263}, + // 1535 + {57: 1262, 721: 1262}, + {57: 458, 531: 4395, 721: 458, 841: 4396, 1003: 4394, 1010: 4397}, + {57: 1260, 721: 1260}, + {16: 503, 57: 503, 531: 503, 553: 503, 586: 503, 709: 503, 721: 503}, + {16: 502, 57: 502, 531: 502, 553: 502, 586: 502, 709: 502, 721: 502}, + // 1540 + {57: 1261, 721: 1261}, + {560: 3037, 799: 3866, 816: 4398}, + {457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 457, 57: 457, 59: 457, 530: 457, 534: 457, 457, 457, 457, 457, 546: 457, 548: 457, 701: 457, 457, 704: 457, 457, 457, 457, 457, 721: 457, 814: 457, 819: 457}, + {456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 456, 57: 456, 59: 456, 530: 456, 534: 456, 456, 456, 456, 456, 546: 456, 548: 456, 701: 456, 456, 704: 456, 456, 456, 456, 456, 721: 456, 814: 456, 819: 456}, + {9: 4400, 57: 4399}, + // 1545 + {466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 466, 16: 466, 57: 466, 59: 466, 151: 466, 466, 466, 530: 466, 534: 466, 466, 466, 466, 466, 546: 466, 548: 466, 553: 466, 577: 466, 586: 466, 605: 466, 701: 466, 466, 704: 466, 466, 466, 466, 466, 466, 721: 466, 814: 466, 819: 466}, + {560: 3037, 799: 3866, 816: 4401}, + {57: 4402}, + {455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 455, 57: 455, 59: 455, 530: 455, 534: 455, 455, 455, 455, 455, 546: 455, 548: 455, 701: 455, 455, 704: 455, 455, 455, 455, 455, 721: 455, 814: 455, 819: 455}, + {57: 1264, 721: 1264}, + // 1550 + {57: 530, 721: 530}, + {57: 529, 721: 529}, + {57: 1265, 721: 1265}, + {57: 1266, 721: 1266}, + {560: 3037, 799: 3866, 816: 4410}, + // 1555 + {464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464, 16: 464, 57: 464, 59: 464, 151: 464, 464, 464, 530: 464, 534: 464, 464, 464, 464, 464, 546: 464, 548: 464, 553: 464, 577: 464, 586: 464, 605: 464, 701: 464, 464, 704: 464, 464, 464, 464, 464, 464, 721: 464, 814: 464, 819: 464}, + {57: 4399}, + {57: 1267, 721: 1267}, + {57: 1268, 721: 1268}, + {16: 4418, 57: 452, 553: 4419, 586: 4415, 709: 4417, 721: 452, 853: 4416, 896: 4414}, + // 1560 + {57: 1271, 721: 1271}, + {449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 449, 16: 4418, 57: 449, 530: 449, 534: 449, 449, 449, 449, 449, 546: 449, 548: 449, 553: 4419, 701: 449, 449, 704: 449, 449, 449, 449, 449, 4417, 721: 449, 853: 4426, 1400: 4425}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4422}, + {557: 4421}, + {446, 446, 446, 446, 446, 446, 446, 446, 446, 10: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 58: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 533: 446, 535: 446, 555: 446, 563: 446, 579: 446, 586: 446}, + // 1565 + {557: 4420}, + {445, 445, 445, 445, 445, 445, 445, 445, 445, 10: 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 58: 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, 533: 445, 535: 445, 555: 445, 563: 445, 579: 445, 586: 445}, + {447, 447, 447, 447, 447, 447, 447, 447, 447, 10: 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 58: 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 447, 533: 447, 535: 447, 555: 447, 563: 447, 579: 447, 586: 447}, + {454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 454, 57: 454, 530: 454, 534: 454, 454, 454, 454, 454, 546: 454, 548: 454, 586: 4423, 701: 454, 454, 704: 454, 454, 454, 454, 454, 721: 454, 1399: 4424}, + {453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 453, 57: 453, 530: 453, 534: 453, 453, 453, 453, 453, 546: 453, 548: 453, 701: 453, 453, 704: 453, 453, 453, 453, 453, 721: 453}, + // 1570 + {450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 450, 57: 450, 530: 450, 534: 450, 450, 450, 450, 450, 546: 450, 548: 450, 701: 450, 450, 704: 450, 450, 450, 450, 450, 721: 450}, + {451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 451, 57: 451, 530: 451, 534: 451, 451, 451, 451, 451, 546: 451, 548: 451, 701: 451, 451, 704: 451, 451, 451, 451, 451, 721: 451}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4427}, + {448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 448, 57: 448, 530: 448, 534: 448, 448, 448, 448, 448, 546: 448, 548: 448, 701: 448, 448, 704: 448, 448, 448, 448, 448, 721: 448}, + {57: 1272, 721: 1272}, + // 1575 + {1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 532: 1453, 1453, 1453, 536: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 549: 1453, 1453, 1453, 554: 1453, 1453, 1453, 1453, 1453, 1453, 561: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 579: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 588: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 609: 1453, 1453, 1453, 1453, 1453, 615: 1453, 1453, 618: 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 1453, 632: 1453, 1453, 1453, 1453, 696: 1453, 703: 1453}, + {566: 3745, 3743, 3744, 3742, 3740, 589: 1278, 800: 3741, 3739}, + {589: 4434, 1303: 4433, 1501: 4432}, + {97: 1274, 589: 4434, 4440, 1303: 4439, 1353: 4438}, + {97: 1277, 589: 1277, 1277}, + // 1580 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4435}, + {566: 3745, 3743, 3744, 3742, 3740, 607: 4436, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4437}, + {97: 1275, 566: 3745, 3743, 3744, 3742, 3740, 589: 1275, 1275, 800: 3741, 3739}, + {97: 4442}, + // 1585 + {97: 1276, 589: 1276, 1276}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4441}, + {97: 1273, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 532: 1454, 1454, 1454, 536: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 549: 1454, 1454, 1454, 554: 1454, 1454, 1454, 1454, 1454, 1454, 561: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 579: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 588: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 609: 1454, 1454, 1454, 1454, 1454, 615: 1454, 1454, 618: 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 1454, 632: 1454, 1454, 1454, 1454, 696: 1454, 703: 1454}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4444}, + // 1590 + {537: 4445, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {59: 4386, 118: 4382, 172: 4388, 175: 4383, 4381, 179: 4385, 553: 4393, 586: 4379, 709: 4392, 734: 4384, 4389, 4390, 739: 4391, 814: 4387, 951: 4380, 1120: 4446}, + {57: 1447, 721: 4448, 1320: 4447}, + {57: 4449}, + {57: 1446}, + // 1595 + {1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 532: 1455, 1455, 1455, 536: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 549: 1455, 1455, 1455, 554: 1455, 1455, 1455, 1455, 1455, 1455, 561: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 579: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 588: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 609: 1455, 1455, 1455, 1455, 1455, 615: 1455, 1455, 618: 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 1455, 632: 1455, 1455, 1455, 1455, 696: 1455, 703: 1455}, + {1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 532: 1456, 1456, 1456, 536: 1456, 1456, 3693, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 549: 1456, 1456, 1456, 554: 1456, 1456, 1456, 1456, 1456, 1456, 561: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 579: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 588: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 609: 1456, 1456, 1456, 1456, 1456, 615: 1456, 1456, 618: 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 1456, 632: 1456, 1456, 1456, 1456, 696: 1456, 703: 1456}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4452}, + {566: 3745, 3743, 3744, 3742, 3740, 585: 4453, 800: 3741, 3739}, + {1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 532: 1457, 1457, 1457, 536: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 549: 1457, 1457, 1457, 554: 1457, 1457, 1457, 1457, 1457, 1457, 561: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 579: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 588: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 609: 1457, 1457, 1457, 1457, 1457, 615: 1457, 1457, 618: 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 1457, 632: 1457, 1457, 1457, 1457, 696: 1457, 703: 1457}, + // 1600 + {1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 532: 1458, 1458, 1458, 536: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 549: 1458, 1458, 1458, 554: 1458, 1458, 1458, 1458, 1458, 1458, 561: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 579: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 588: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 609: 1458, 1458, 1458, 1458, 1458, 615: 1458, 1458, 618: 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 632: 1458, 1458, 1458, 1458, 696: 1458, 703: 1458}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4456}, + {9: 4457}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4458}, + {9: 2189, 57: 4459, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1605 + {1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 532: 1459, 1459, 1459, 536: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 549: 1459, 1459, 1459, 554: 1459, 1459, 1459, 1459, 1459, 1459, 561: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 579: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 588: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 609: 1459, 1459, 1459, 1459, 1459, 615: 1459, 1459, 618: 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 1459, 632: 1459, 1459, 1459, 1459, 696: 1459, 703: 1459}, + {9: 2190, 57: 4465, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {9: 4462}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4463}, + {9: 2189, 57: 4464, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1610 + {1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 532: 1460, 1460, 1460, 536: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 549: 1460, 1460, 1460, 554: 1460, 1460, 1460, 1460, 1460, 1460, 561: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 579: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 588: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 609: 1460, 1460, 1460, 1460, 1460, 615: 1460, 1460, 618: 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 1460, 632: 1460, 1460, 1460, 1460, 696: 1460, 703: 1460}, + {1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 532: 1461, 1461, 1461, 536: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 549: 1461, 1461, 1461, 554: 1461, 1461, 1461, 1461, 1461, 1461, 561: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 579: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 588: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 609: 1461, 1461, 1461, 1461, 1461, 615: 1461, 1461, 618: 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 1461, 632: 1461, 1461, 1461, 1461, 696: 1461, 703: 1461}, + {1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 532: 1463, 1463, 1463, 536: 1463, 1463, 3693, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 549: 1463, 1463, 1463, 554: 1463, 1463, 1463, 1463, 1463, 1463, 561: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 579: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 588: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 609: 1463, 1463, 1463, 1463, 1463, 615: 1463, 1463, 618: 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 1463, 632: 1463, 1463, 1463, 1463, 696: 1463, 703: 1463}, + {1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 532: 1465, 1465, 1465, 536: 1465, 1465, 3693, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 549: 1465, 1465, 1465, 554: 1465, 1465, 1465, 1465, 1465, 1465, 561: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 579: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 588: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 609: 1465, 1465, 1465, 1465, 1465, 615: 1465, 1465, 618: 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 1465, 632: 1465, 1465, 1465, 1465, 696: 1465, 703: 1465}, + {1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 532: 1466, 1466, 1466, 536: 1466, 1466, 3693, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 549: 1466, 1466, 1466, 554: 1466, 1466, 1466, 1466, 1466, 1466, 561: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 579: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 588: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 609: 1466, 1466, 1466, 1466, 1466, 615: 1466, 1466, 618: 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 1466, 632: 1466, 1466, 1466, 1466, 696: 1466, 703: 1466}, + // 1615 + {1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 532: 1467, 1467, 1467, 536: 1467, 1467, 3693, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 549: 1467, 1467, 1467, 554: 1467, 1467, 1467, 1467, 1467, 1467, 561: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 579: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 588: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 609: 1467, 1467, 1467, 1467, 1467, 615: 1467, 1467, 618: 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 1467, 632: 1467, 1467, 1467, 1467, 696: 1467, 703: 1467}, + {1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 532: 1468, 1468, 1468, 536: 1468, 1468, 3693, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 549: 1468, 1468, 1468, 554: 1468, 1468, 1468, 1468, 1468, 1468, 561: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 579: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 588: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 609: 1468, 1468, 1468, 1468, 1468, 615: 1468, 1468, 618: 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 1468, 632: 1468, 1468, 1468, 1468, 696: 1468, 703: 1468}, + {533: 4474}, + {533: 4473}, + {1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 532: 1448, 1448, 1448, 536: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 549: 1448, 1448, 1448, 554: 1448, 1448, 1448, 1448, 1448, 1448, 561: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 579: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 588: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 609: 1448, 1448, 1448, 1448, 1448, 615: 1448, 1448, 618: 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 1448, 632: 1448, 1448, 1448, 1448, 696: 1448, 703: 1448}, + // 1620 + {1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 532: 1449, 1449, 1449, 536: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 549: 1449, 1449, 1449, 554: 1449, 1449, 1449, 1449, 1449, 1449, 561: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 579: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 588: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 609: 1449, 1449, 1449, 1449, 1449, 615: 1449, 1449, 618: 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 1449, 632: 1449, 1449, 1449, 1449, 696: 1449, 703: 1449}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4476, 3051, 3052, 3050}, + {1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 4477, 1480, 1480, 1480, 536: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 549: 1480, 1480, 1480, 554: 1480, 1480, 1480, 1480, 1480, 1480, 561: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 579: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 588: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 609: 1480, 1480, 1480, 1480, 1480, 615: 1480, 1480, 618: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 632: 1480, 1480, 1480, 1480, 696: 1480, 703: 1480, 716: 3906, 718: 1480, 1480}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4478}, + {57: 4479}, + // 1625 + {1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 532: 1312, 1312, 1312, 536: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 549: 1312, 1312, 1312, 554: 1312, 1312, 1312, 1312, 1312, 1312, 561: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 579: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 588: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 609: 1312, 1312, 1312, 1312, 1312, 615: 1312, 1312, 618: 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 1312, 632: 1312, 1312, 1312, 1312, 696: 1312, 703: 1312}, + {1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 532: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 548: 1519, 1519, 1519, 1519, 554: 1519, 1519, 1519, 1519, 1519, 1519, 561: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 579: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 588: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 609: 1519, 1519, 1519, 1519, 1519, 615: 1519, 1519, 618: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519, 632: 1519, 1519, 1519, 1519, 696: 1519, 701: 1519, 1519, 1519, 1519, 1519, 1519, 1519, 1519}, + {1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 532: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 548: 1516, 1516, 1516, 1516, 554: 1516, 1516, 1516, 1516, 1516, 1516, 561: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 579: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 588: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 609: 1516, 1516, 1516, 1516, 1516, 615: 1516, 1516, 618: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516, 632: 1516, 1516, 1516, 1516, 696: 1516, 701: 1516, 1516, 1516, 1516, 1516, 1516, 1516, 1516}, + {1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 532: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 548: 1515, 1515, 1515, 1515, 554: 1515, 1515, 1515, 1515, 1515, 1515, 561: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 579: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 588: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 609: 1515, 1515, 1515, 1515, 1515, 615: 1515, 1515, 618: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515, 632: 1515, 1515, 1515, 1515, 696: 1515, 701: 1515, 1515, 1515, 1515, 1515, 1515, 1515, 1515}, + {1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 532: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 548: 1513, 1513, 1513, 1513, 554: 1513, 1513, 1513, 1513, 1513, 1513, 561: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 579: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 588: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 609: 1513, 1513, 1513, 1513, 1513, 615: 1513, 1513, 618: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513, 632: 1513, 1513, 1513, 1513, 696: 1513, 701: 1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513}, + // 1630 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4486, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4485}, + {57: 4490, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4487}, + {57: 4488, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4489}, + // 1635 + {1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 532: 1320, 1320, 1320, 536: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 549: 1320, 1320, 1320, 554: 1320, 1320, 1320, 1320, 1320, 1320, 561: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 579: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 588: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 609: 1320, 1320, 1320, 1320, 1320, 615: 1320, 1320, 618: 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 1320, 632: 1320, 1320, 1320, 1320, 696: 1320, 703: 1320}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4491}, + {1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 532: 1321, 1321, 1321, 536: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 549: 1321, 1321, 1321, 554: 1321, 1321, 1321, 1321, 1321, 1321, 561: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 579: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 588: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 609: 1321, 1321, 1321, 1321, 1321, 615: 1321, 1321, 618: 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 1321, 632: 1321, 1321, 1321, 1321, 696: 1321, 703: 1321}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4494, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4493}, + {9: 4504, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1640 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4495}, + {9: 4496, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4498, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4497}, + {57: 4502, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4499}, + // 1645 + {57: 4500, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4501}, + {1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 532: 1316, 1316, 1316, 536: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 549: 1316, 1316, 1316, 554: 1316, 1316, 1316, 1316, 1316, 1316, 561: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 579: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 588: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 609: 1316, 1316, 1316, 1316, 1316, 615: 1316, 1316, 618: 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 1316, 632: 1316, 1316, 1316, 1316, 696: 1316, 703: 1316}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4503}, + {1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 532: 1318, 1318, 1318, 536: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 549: 1318, 1318, 1318, 554: 1318, 1318, 1318, 1318, 1318, 1318, 561: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 579: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 588: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 609: 1318, 1318, 1318, 1318, 1318, 615: 1318, 1318, 618: 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 1318, 632: 1318, 1318, 1318, 1318, 696: 1318, 703: 1318}, + // 1650 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 715: 4506, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4505}, + {57: 4510, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4507}, + {57: 4508, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4509}, + // 1655 + {1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 532: 1317, 1317, 1317, 536: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 549: 1317, 1317, 1317, 554: 1317, 1317, 1317, 1317, 1317, 1317, 561: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 579: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 588: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 609: 1317, 1317, 1317, 1317, 1317, 615: 1317, 1317, 618: 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 1317, 632: 1317, 1317, 1317, 1317, 696: 1317, 703: 1317}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4511}, + {1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 532: 1319, 1319, 1319, 536: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 549: 1319, 1319, 1319, 554: 1319, 1319, 1319, 1319, 1319, 1319, 561: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 579: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 588: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 609: 1319, 1319, 1319, 1319, 1319, 615: 1319, 1319, 618: 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 1319, 632: 1319, 1319, 1319, 1319, 696: 1319, 703: 1319}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 886: 4513}, + {9: 4514}, + // 1660 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4515}, + {9: 4516, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4517}, + {57: 4518, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 532: 1366, 1366, 1366, 536: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 549: 1366, 1366, 1366, 554: 1366, 1366, 1366, 1366, 1366, 1366, 561: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 579: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 588: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 609: 1366, 1366, 1366, 1366, 1366, 615: 1366, 1366, 618: 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 1366, 632: 1366, 1366, 1366, 1366, 696: 1366, 703: 1366}, + // 1665 + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 886: 4520}, + {9: 4521}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4522}, + {9: 4523, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4524}, + // 1670 + {57: 4525, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 532: 1367, 1367, 1367, 536: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 549: 1367, 1367, 1367, 554: 1367, 1367, 1367, 1367, 1367, 1367, 561: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 579: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 588: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 609: 1367, 1367, 1367, 1367, 1367, 615: 1367, 1367, 618: 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 1367, 632: 1367, 1367, 1367, 1367, 696: 1367, 703: 1367}, + {175: 4529, 4528, 179: 4530, 185: 4531, 1368: 4527}, + {9: 4532}, + {9: 1356}, + // 1675 + {9: 1355}, + {9: 1354}, + {9: 1353}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4533}, + {57: 4534, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 1680 + {1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 532: 1373, 1373, 1373, 536: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 549: 1373, 1373, 1373, 554: 1373, 1373, 1373, 1373, 1373, 1373, 561: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 579: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 588: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 609: 1373, 1373, 1373, 1373, 1373, 615: 1373, 1373, 618: 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 1373, 632: 1373, 1373, 1373, 1373, 696: 1373, 703: 1373}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4536}, + {9: 4537}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 4539}, + {2225, 2225, 6: 2225, 2225, 2225, 2225, 15: 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225, 57: 2225, 86: 2225, 88: 2225, 90: 2225, 2225, 95: 2225, 2225, 98: 2225, 2225, 2225, 2225, 103: 2225, 133: 2225, 163: 2225, 2225, 2225, 2225, 535: 2225, 538: 2225, 2225, 553: 2225, 2225, 556: 2225, 563: 2225, 565: 2225, 709: 2225, 2225, 720: 2225}, + // 1685 + {57: 4545}, + {168, 168, 6: 168, 168, 168, 15: 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 57: 168, 86: 168, 88: 168, 90: 168, 168, 95: 168, 168, 98: 168, 168, 168, 168, 103: 168, 535: 168, 538: 168, 168, 553: 168, 565: 168, 709: 168, 168, 720: 168}, + {560: 3037, 799: 4538, 825: 4544}, + {560: 3037, 799: 4543}, + {166, 166, 6: 166, 166, 166, 15: 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 166, 57: 166, 86: 166, 88: 166, 90: 166, 166, 95: 166, 166, 98: 166, 166, 166, 166, 103: 166, 535: 166, 538: 166, 166, 553: 166, 565: 166, 709: 166, 166, 720: 166}, + // 1690 + {167, 167, 6: 167, 167, 167, 15: 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 167, 57: 167, 86: 167, 88: 167, 90: 167, 167, 95: 167, 167, 98: 167, 167, 167, 167, 103: 167, 535: 167, 538: 167, 167, 553: 167, 565: 167, 709: 167, 167, 720: 167}, + {1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 532: 1344, 1344, 1344, 536: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 549: 1344, 1344, 1344, 554: 1344, 1344, 1344, 1344, 1344, 1344, 561: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 579: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 588: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 609: 1344, 1344, 1344, 1344, 1344, 615: 1344, 1344, 618: 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 1344, 632: 1344, 1344, 1344, 1344, 696: 1344, 703: 1344}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4547}, + {57: 4548}, + {1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 532: 1345, 1345, 1345, 536: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 549: 1345, 1345, 1345, 554: 1345, 1345, 1345, 1345, 1345, 1345, 561: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 579: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 588: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 609: 1345, 1345, 1345, 1345, 1345, 615: 1345, 1345, 618: 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345, 632: 1345, 1345, 1345, 1345, 696: 1345, 703: 1345}, + // 1695 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4550}, + {57: 4551, 537: 4552, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 532: 1361, 1361, 1361, 536: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 549: 1361, 1361, 1361, 554: 1361, 1361, 1361, 1361, 1361, 1361, 561: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 579: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 588: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 609: 1361, 1361, 1361, 1361, 1361, 615: 1361, 1361, 618: 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 1361, 632: 1361, 1361, 1361, 1361, 696: 1361, 703: 1361}, + {553: 4393, 586: 4554, 709: 4392, 951: 4553}, + {531: 4408, 841: 4557}, + // 1700 + {531: 4408, 841: 4555}, + {57: 4556}, + {1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 532: 1359, 1359, 1359, 536: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 549: 1359, 1359, 1359, 554: 1359, 1359, 1359, 1359, 1359, 1359, 561: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 579: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 588: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 609: 1359, 1359, 1359, 1359, 1359, 615: 1359, 1359, 618: 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 1359, 632: 1359, 1359, 1359, 1359, 696: 1359, 703: 1359}, + {57: 4558}, + {1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 532: 1360, 1360, 1360, 536: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 549: 1360, 1360, 1360, 554: 1360, 1360, 1360, 1360, 1360, 1360, 561: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 579: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 588: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 609: 1360, 1360, 1360, 1360, 1360, 615: 1360, 1360, 618: 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 1360, 632: 1360, 1360, 1360, 1360, 696: 1360, 703: 1360}, + // 1705 + {1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 532: 1383, 1383, 1383, 536: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 549: 1383, 1383, 1383, 554: 1383, 1383, 1383, 1383, 1383, 1383, 561: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 579: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 588: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 609: 1383, 1383, 1383, 1383, 1383, 615: 1383, 1383, 618: 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 1383, 632: 1383, 1383, 1383, 1383, 696: 1383, 703: 1383}, + {1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 532: 1384, 1384, 1384, 536: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 549: 1384, 1384, 1384, 554: 1384, 1384, 1384, 1384, 1384, 1384, 561: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 579: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 588: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 609: 1384, 1384, 1384, 1384, 1384, 615: 1384, 1384, 618: 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384, 632: 1384, 1384, 1384, 1384, 696: 1384, 703: 1384}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4562}, + {57: 4563}, + {1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 532: 1380, 1380, 1380, 536: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 549: 1380, 1380, 1380, 554: 1380, 1380, 1380, 1380, 1380, 1380, 561: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 579: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 588: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 609: 1380, 1380, 1380, 1380, 1380, 615: 1380, 1380, 618: 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 1380, 632: 1380, 1380, 1380, 1380, 696: 1380, 703: 1380}, + // 1710 + {1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 532: 1385, 1385, 1385, 536: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 549: 1385, 1385, 1385, 554: 1385, 1385, 1385, 1385, 1385, 1385, 561: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 579: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 588: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 609: 1385, 1385, 1385, 1385, 1385, 615: 1385, 1385, 618: 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 1385, 632: 1385, 1385, 1385, 1385, 696: 1385, 703: 1385}, + {2: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 10: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 58: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 531: 1441, 533: 1441, 1441, 1441, 1441, 540: 1441, 1441, 543: 1441, 1441, 1441, 547: 1441, 1441, 552: 1441, 1441, 560: 1441, 578: 1441, 586: 1441, 1441, 614: 1441, 617: 1441, 628: 1441, 1441, 1441, 1441, 636: 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 1441, 697: 1441, 1441, 1441, 1441, 711: 1441, 715: 4174, 829: 4172, 4173, 885: 4175, 887: 4176, 912: 4566, 4177}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4567}, + {57: 4568, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 532: 1158, 1158, 1158, 536: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 549: 1158, 1158, 1158, 554: 1158, 1158, 1158, 1158, 1158, 1158, 561: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 579: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 588: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 609: 1158, 1158, 1158, 1158, 1158, 615: 1158, 1158, 618: 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 1158, 632: 1158, 1158, 1158, 1158, 696: 1158, 703: 1158, 820: 4064, 832: 4182, 844: 4569}, + // 1715 + {1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 532: 1342, 1342, 1342, 536: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 549: 1342, 1342, 1342, 554: 1342, 1342, 1342, 1342, 1342, 1342, 561: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 579: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 588: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 609: 1342, 1342, 1342, 1342, 1342, 615: 1342, 1342, 618: 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 1342, 632: 1342, 1342, 1342, 1342, 696: 1342, 703: 1342}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 4571}, + {57: 4572}, + {1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 532: 1313, 1313, 1313, 536: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 549: 1313, 1313, 1313, 554: 1313, 1313, 1313, 1313, 1313, 1313, 561: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 579: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 588: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 609: 1313, 1313, 1313, 1313, 1313, 615: 1313, 1313, 618: 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 1313, 632: 1313, 1313, 1313, 1313, 696: 1313, 703: 1313}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4574}, + // 1720 + {57: 4575}, + {2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 532: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 548: 2507, 2507, 2507, 2507, 554: 2507, 2507, 2507, 2507, 2507, 2507, 561: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 579: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 588: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 609: 2507, 2507, 2507, 2507, 2507, 615: 2507, 2507, 618: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507, 632: 2507, 2507, 2507, 2507, 696: 2507, 701: 2507, 2507, 2507, 2507, 2507, 2507, 2507, 2507}, + {558: 4577}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4578}, + {2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 532: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 548: 2508, 2508, 2508, 2508, 554: 2508, 2508, 2508, 2508, 2508, 2508, 561: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 579: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 588: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 609: 2508, 2508, 2508, 2508, 2508, 615: 2508, 2508, 618: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508, 632: 2508, 2508, 2508, 2508, 696: 2508, 701: 2508, 2508, 2508, 2508, 2508, 2508, 2508, 2508}, + // 1725 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4587, 3597, 3679, 3596, 3593}, + {117: 4583, 258: 4581, 272: 4582, 1257: 4584}, + {9: 2836, 57: 2836, 93: 2836, 134: 2836, 136: 2836, 158: 2836, 712: 2836}, + {9: 2835, 57: 2835, 93: 2835, 134: 2835, 136: 2835, 158: 2835, 712: 2835}, + {9: 2834, 57: 2834, 93: 2834, 134: 2834, 136: 2834, 158: 2834, 712: 2834}, + // 1730 + {712: 4585}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4586, 3597, 3679, 3596, 3593}, + {2, 2, 9: 2, 51: 2, 93: 2, 117: 2, 538: 3693, 696: 2, 703: 3694}, + {4, 4, 9: 4, 51: 4, 93: 4, 117: 4, 538: 3693, 696: 4, 703: 3694}, + {2: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 10: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 58: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 531: 2315, 533: 2315, 2315, 2315, 2315, 540: 2315, 2315, 543: 2315, 2315, 2315, 547: 2315, 2315, 2315, 552: 2315, 2315, 560: 2315, 578: 2315, 586: 2315, 2315, 614: 2315, 617: 2315, 628: 2315, 2315, 2315, 2315, 636: 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 2315, 697: 2315, 2315, 2315, 2315, 711: 2315, 933: 2315}, + // 1735 + {229: 4591, 231: 4590, 933: 4592, 1256: 4593}, + {2833, 2833, 9: 2833, 51: 2833, 57: 2833, 93: 2833, 117: 2833, 134: 2833, 136: 2833, 696: 2833}, + {2832, 2832, 9: 2832, 51: 2832, 57: 2832, 93: 2832, 117: 2832, 134: 2832, 136: 2832, 696: 2832}, + {2831, 2831, 9: 2831, 51: 2831, 57: 2831, 93: 2831, 117: 2831, 134: 2831, 136: 2831, 696: 2831}, + {6, 6, 9: 6, 51: 6, 93: 6, 117: 6, 696: 6}, + // 1740 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 628: 3682, 770: 4595, 3051, 3052, 3050, 776: 4598, 936: 4597}, + {2463, 2463, 9: 2463, 51: 2463, 93: 2463, 110: 2463, 2463, 2463, 2463, 2463, 117: 2463, 696: 2463}, + {2462, 2462, 9: 2462, 51: 2462, 93: 2462, 110: 2462, 2462, 2462, 2462, 2462, 117: 2462, 696: 2462}, + {8, 8, 9: 8, 51: 8, 93: 8, 117: 8, 696: 8}, + {7, 7, 9: 7, 51: 7, 93: 7, 117: 7, 696: 7}, + // 1745 + {10, 10, 9: 10, 51: 10, 93: 10, 117: 10, 696: 10}, + {51: 3041, 93: 3042, 117: 3045, 696: 3044, 1073: 4601, 3043}, + {9, 9, 9: 9, 51: 9, 93: 9, 117: 9, 696: 9}, + {27, 27, 158: 4609, 171: 4608, 174: 4607, 458: 4610, 1042: 4606, 1329: 4603, 4605, 1352: 4604}, + {28, 28}, + // 1750 + {26, 26, 9: 4626, 158: 4609, 171: 4608, 174: 4607, 1042: 4625}, + {25, 25}, + {24, 24, 9: 24, 158: 24, 171: 24, 174: 24}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 531: 2316, 533: 2316, 2316, 2316, 2316, 540: 2316, 2316, 543: 2316, 2316, 2316, 547: 2316, 2316, 552: 2316, 2316, 555: 4588, 560: 2316, 578: 2316, 586: 2316, 2316, 614: 2316, 617: 2316, 628: 2316, 2316, 2316, 2316, 636: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 697: 2316, 2316, 2316, 2316, 711: 2316, 802: 4623}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 531: 2316, 533: 2316, 2316, 2316, 2316, 540: 2316, 2316, 543: 2316, 2316, 2316, 547: 2316, 2316, 552: 2316, 2316, 555: 4588, 560: 2316, 578: 2316, 586: 2316, 2316, 614: 2316, 617: 2316, 628: 2316, 2316, 2316, 2316, 636: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 697: 2316, 2316, 2316, 2316, 711: 2316, 802: 4621}, + // 1755 + {533: 2316, 555: 4588, 641: 2316, 802: 4616}, + {410: 4613, 4612, 4614, 451: 4611, 4615}, + {17, 17}, + {16, 16}, + {15, 15}, + // 1760 + {14, 14}, + {13, 13}, + {533: 4617, 641: 4618}, + {19, 19, 9: 19, 158: 19, 171: 19, 174: 19}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4619}, + // 1765 + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 4620}, + {18, 18, 9: 18, 158: 18, 171: 18, 174: 18}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4622}, + {20, 20, 9: 20, 158: 20, 171: 20, 174: 20, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4624}, + // 1770 + {21, 21, 9: 21, 158: 21, 171: 21, 174: 21, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {23, 23, 9: 23, 158: 23, 171: 23, 174: 23}, + {158: 4609, 171: 4608, 174: 4607, 1042: 4627}, + {22, 22, 9: 22, 158: 22, 171: 22, 174: 22}, + {285: 4631, 384: 4629, 894: 4630}, + // 1775 + {532: 4639, 582: 135, 1420: 4638}, + {533: 4637}, + {2: 4633, 533: 4632}, + {533: 4636}, + {533: 4634}, + // 1780 + {533: 4635}, + {136, 136}, + {137, 137}, + {138, 138}, + {582: 4645}, + // 1785 + {225: 4640}, + {731: 4641, 995: 4642}, + {185: 4643}, + {582: 134}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4644}, + // 1790 + {2120, 2120, 9: 2120, 57: 2120, 530: 2120, 532: 2120, 539: 2120, 2120, 2120, 2120, 549: 2120, 2120, 2120, 554: 2120, 556: 2120, 2120, 2120, 2120, 562: 2120, 2120, 2120, 2120, 3745, 3743, 3744, 3742, 3740, 2120, 2120, 2120, 2120, 2120, 2120, 580: 2120, 2120, 2120, 2120, 585: 2120, 592: 2120, 800: 3741, 3739}, + {246: 4658, 531: 2907, 2906, 4659, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 713: 4657, 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 4656}, + {549: 4675, 608: 2115, 958: 4674}, + {630, 630, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {632, 632, 539: 1014, 550: 1014, 1014}, + // 1795 + {637, 637}, + {636, 636}, + {635, 635}, + {634, 634}, + {633, 633}, + // 1800 + {631, 631}, + {629, 629}, + {144, 144}, + {246: 4668, 531: 2907, 2906, 4669, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 4667}, + {159: 4660}, + // 1805 + {140, 140}, + {424, 424, 554: 424, 556: 424, 563: 4661, 424, 889: 4662, 4663}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4666}, + {423, 423, 57: 423, 530: 423, 532: 423, 539: 423, 542: 423, 550: 423, 423, 554: 423, 556: 423, 558: 423, 423, 562: 423, 564: 423, 571: 423, 423, 574: 423}, + {1499, 1499, 554: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 4664}, + // 1810 + {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 4665}, + {142, 142}, + {425, 425, 57: 425, 530: 425, 532: 425, 539: 425, 542: 425, 550: 425, 425, 554: 425, 556: 425, 558: 425, 425, 562: 425, 564: 425, 566: 3745, 3743, 3744, 3742, 3740, 425, 425, 574: 425, 800: 3741, 3739}, + {143, 143}, + {159: 4670}, + // 1815 + {139, 139}, + {424, 424, 554: 424, 556: 424, 563: 4661, 424, 889: 4662, 4671}, + {1499, 1499, 554: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 4672}, + {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 4673}, + {141, 141}, + // 1820 + {608: 4676}, + {2: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 10: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 58: 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 2114, 531: 2114, 559: 2114, 561: 2114, 608: 2114, 643: 2114}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4677}, + {2691, 2691, 2691, 2691, 2691, 2691, 4725, 4727, 580, 10: 4694, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 89: 4696, 4717, 4718, 102: 4719, 208: 4699, 234: 4688, 249: 4682, 251: 4680, 253: 4703, 256: 4704, 270: 4698, 276: 4714, 290: 4692, 299: 4700, 305: 4695, 324: 4705, 331: 4701, 338: 4715, 4716, 343: 4683, 532: 4713, 535: 4724, 538: 2446, 4761, 546: 2691, 553: 2446, 557: 4685, 562: 4720, 564: 4702, 4712, 646: 4686, 702: 4691, 709: 2446, 4730, 713: 4679, 724: 4707, 727: 4693, 729: 4721, 766: 4706, 4708, 769: 4687, 774: 4697, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 972: 4711, 986: 4709, 1020: 4684, 1028: 4689, 1110: 4723, 1285: 4690, 1309: 4710, 1315: 4722, 4678}, + {2444, 2444, 5551, 5553, 5554, 5552, 546: 5555, 1233: 5550, 1317: 5549}, + // 1825 + {546: 5523}, + {2849, 2849, 200: 5517, 546: 5518}, + {214: 5509}, + {533: 2316, 535: 2316, 555: 4588, 802: 5506}, + {533: 2316, 535: 2316, 555: 4588, 802: 5503}, + // 1830 + {2777, 2777, 2777, 2777, 2777, 2777, 4725, 4727, 580, 2777, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 546: 2777, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5500}, + {200: 5484, 207: 5485}, + {712: 5476}, + {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 531: 2696, 546: 5329, 578: 2696, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 928: 5331, 952: 4920, 975: 5327, 1002: 5328, 1015: 5330}, + {546: 5320}, + // 1835 + {2765, 2765, 2765, 2765, 2765, 2765, 9: 2765, 546: 2765}, + {2764, 2764, 2764, 2764, 2764, 2764, 9: 2764, 546: 2764}, + {546: 5318}, + {546: 5315}, + {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 546: 5294, 578: 2696, 701: 4906, 4921, 704: 5293, 706: 4922, 710: 4907, 746: 5297, 923: 5296, 952: 4920, 975: 5292, 1015: 5295, 1122: 5298}, + // 1840 + {546: 5285}, + {546: 5274}, + {546: 5272}, + {546: 5269}, + {546: 5266}, + // 1845 + {20: 5263, 546: 5262}, + {20: 5259, 546: 5258}, + {546: 5248}, + {723: 5241}, + {1055: 5240}, + // 1850 + {1055: 5239}, + {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 578: 2696, 952: 4920, 975: 5235}, + {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 578: 2696, 952: 4920, 975: 4946}, + {2: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 10: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 58: 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 2696, 702: 4921, 706: 4922, 710: 4919, 952: 4920, 975: 4917, 1122: 4918}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 537: 4904, 555: 4588, 701: 4906, 710: 4907, 712: 4902, 802: 4903, 923: 4905, 952: 4901}, + // 1855 + {2732, 2732, 2732, 2732, 2732, 2732, 9: 2732, 546: 2732}, + {2731, 2731, 2731, 2731, 2731, 2731, 9: 2731, 546: 2731}, + {2730, 2730, 2730, 2730, 2730, 2730, 9: 2730, 546: 2730}, + {2729, 2729, 2729, 2729, 2729, 2729, 8: 579, 2729, 29: 579, 546: 2729}, + {247: 4900}, + // 1860 + {247: 4899}, + {2726, 2726, 2726, 2726, 2726, 2726, 9: 2726, 546: 2726}, + {2725, 2725, 2725, 2725, 2725, 2725, 9: 2725, 546: 2725}, + {2721, 2721, 2721, 2721, 2721, 2721, 9: 2721, 546: 2721}, + {2720, 2720, 2720, 2720, 2720, 2720, 9: 2720, 546: 2720}, + // 1865 + {56: 2316, 293: 2316, 315: 2316, 317: 2316, 535: 2316, 555: 4588, 802: 4893}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 535: 2316, 555: 4588, 802: 4890}, + {194: 4889, 768: 4888}, + {2690, 2690, 2690, 2690, 2690, 2690, 9: 4886, 546: 2690}, + {2689, 2689, 2689, 2689, 2689, 2689, 9: 2689, 546: 2689}, + // 1870 + {16: 2445, 18: 2445, 21: 2445, 538: 2445, 553: 2445, 709: 2445}, + {533: 2316, 555: 4588, 802: 4884}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4882}, + {23: 4877, 236: 4878, 300: 4879}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4875}, + // 1875 + {298: 4872}, + {298: 4869}, + {555: 4588, 560: 2316, 802: 4867}, + {555: 4588, 560: 2316, 802: 4865}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 555: 4588, 802: 4863}, + // 1880 + {555: 4588, 560: 2316, 802: 4861}, + {2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 15: 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, 57: 2390, 530: 2390, 2390, 2390, 535: 2390, 537: 2390, 2390, 2390, 546: 2390, 2390, 549: 2390, 552: 2390, 2390, 565: 2390, 608: 2390, 695: 2390, 709: 2390, 2390}, + {617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 15: 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 617, 530: 617, 617, 617, 535: 617, 537: 617, 617, 617, 546: 617, 617, 549: 617, 552: 617, 617, 565: 617, 608: 617, 695: 617, 709: 617, 617}, + {16: 4418, 538: 4856, 553: 4419, 709: 4417, 853: 4855}, + {8: 4849, 29: 4850}, + // 1885 + {555: 4588, 560: 2316, 802: 4847}, + {555: 4588, 560: 2316, 802: 4845}, + {533: 2316, 555: 4588, 802: 4843}, + {555: 4588, 560: 2316, 802: 4841}, + {555: 4588, 560: 2316, 802: 4839}, + // 1890 + {533: 2316, 555: 4588, 802: 4837}, + {533: 2316, 555: 4588, 802: 4835}, + {555: 4588, 560: 2316, 802: 4833}, + {555: 4588, 560: 2316, 802: 4831}, + {603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 15: 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 603, 530: 603, 603, 603, 535: 603, 537: 603, 603, 603, 546: 603, 603, 549: 603, 552: 603, 603, 565: 603, 608: 603, 695: 603, 709: 603, 603}, + // 1895 + {535: 2316, 555: 4588, 560: 2316, 802: 4829}, + {535: 2316, 555: 4588, 560: 2316, 802: 4826}, + {535: 2316, 555: 4588, 560: 2316, 802: 4823}, + {555: 4588, 560: 2316, 802: 4821}, + {555: 4588, 560: 2316, 802: 4819}, + // 1900 + {555: 4588, 560: 2316, 636: 2316, 2316, 802: 4817}, + {533: 2316, 555: 4588, 802: 4815}, + {533: 2316, 555: 4588, 802: 4813}, + {555: 4588, 560: 2316, 802: 4811}, + {555: 4588, 560: 2316, 802: 4809}, + // 1905 + {535: 2316, 555: 4588, 560: 2316, 802: 4805}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 548: 2316, 555: 4588, 802: 4802}, + {531: 2316, 555: 4588, 802: 4797}, + {533: 2316, 555: 4588, 802: 4794}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 555: 4588, 802: 4788}, + // 1910 + {533: 2316, 555: 4588, 802: 4786}, + {533: 2316, 555: 4588, 802: 4784}, + {574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 15: 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 530: 574, 574, 574, 535: 574, 537: 574, 574, 574, 546: 574, 574, 549: 574, 552: 574, 574, 565: 574, 608: 574, 695: 574, 709: 574, 574}, + {177: 2316, 251: 2316, 255: 2316, 291: 2316, 332: 2316, 347: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 535: 2316, 555: 4588, 802: 4768}, + {177: 4771, 251: 4774, 255: 4770, 291: 4772, 332: 4773, 347: 4775, 4776, 4781, 4780, 4777, 4782, 4783, 4778, 4779, 535: 4769}, + // 1915 + {568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 15: 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 568, 530: 568, 568, 568, 535: 568, 537: 568, 568, 568, 546: 568, 568, 549: 568, 552: 568, 568, 565: 568, 608: 568, 695: 568, 709: 568, 568}, + {567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 15: 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 567, 530: 567, 567, 567, 535: 567, 537: 567, 567, 567, 546: 567, 567, 549: 567, 552: 567, 567, 565: 567, 608: 567, 695: 567, 709: 567, 567}, + {566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 15: 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 566, 530: 566, 566, 566, 535: 566, 537: 566, 566, 566, 546: 566, 566, 549: 566, 552: 566, 566, 565: 566, 608: 566, 695: 566, 709: 566, 566}, + {565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 15: 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 565, 530: 565, 565, 565, 535: 565, 537: 565, 565, 565, 546: 565, 565, 549: 565, 552: 565, 565, 565: 565, 608: 565, 695: 565, 709: 565, 565}, + {564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 15: 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 564, 530: 564, 564, 564, 535: 564, 537: 564, 564, 564, 546: 564, 564, 549: 564, 552: 564, 564, 565: 564, 608: 564, 695: 564, 709: 564, 564}, + // 1920 + {563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 15: 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 563, 530: 563, 563, 563, 535: 563, 537: 563, 563, 563, 546: 563, 563, 549: 563, 552: 563, 563, 565: 563, 608: 563, 695: 563, 709: 563, 563}, + {562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 15: 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 562, 530: 562, 562, 562, 535: 562, 537: 562, 562, 562, 546: 562, 562, 549: 562, 552: 562, 562, 565: 562, 608: 562, 695: 562, 709: 562, 562}, + {561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 15: 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 561, 530: 561, 561, 561, 535: 561, 537: 561, 561, 561, 546: 561, 561, 549: 561, 552: 561, 561, 565: 561, 608: 561, 695: 561, 709: 561, 561}, + {560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 15: 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 560, 530: 560, 560, 560, 535: 560, 537: 560, 560, 560, 546: 560, 560, 549: 560, 552: 560, 560, 565: 560, 608: 560, 695: 560, 709: 560, 560}, + {559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 15: 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 559, 530: 559, 559, 559, 535: 559, 537: 559, 559, 559, 546: 559, 559, 549: 559, 552: 559, 559, 565: 559, 608: 559, 695: 559, 709: 559, 559}, + // 1925 + {558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 15: 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 558, 530: 558, 558, 558, 535: 558, 537: 558, 558, 558, 546: 558, 558, 549: 558, 552: 558, 558, 565: 558, 608: 558, 695: 558, 709: 558, 558}, + {557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 15: 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 557, 530: 557, 557, 557, 535: 557, 537: 557, 557, 557, 546: 557, 557, 549: 557, 552: 557, 557, 565: 557, 608: 557, 695: 557, 709: 557, 557}, + {556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 15: 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 530: 556, 556, 556, 535: 556, 537: 556, 556, 556, 546: 556, 556, 549: 556, 552: 556, 556, 565: 556, 608: 556, 695: 556, 709: 556, 556}, + {555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 15: 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 555, 530: 555, 555, 555, 535: 555, 537: 555, 555, 555, 546: 555, 555, 549: 555, 552: 555, 555, 565: 555, 608: 555, 695: 555, 709: 555, 555}, + {554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 15: 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 530: 554, 554, 554, 535: 554, 537: 554, 554, 554, 546: 554, 554, 549: 554, 552: 554, 554, 565: 554, 608: 554, 695: 554, 709: 554, 554}, + // 1930 + {533: 4785}, + {581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 15: 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 581, 530: 581, 581, 581, 535: 581, 537: 581, 581, 581, 546: 581, 581, 549: 581, 552: 581, 581, 565: 581, 608: 581, 695: 581, 709: 581, 581}, + {533: 4787}, + {582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 15: 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 582, 530: 582, 582, 582, 535: 582, 537: 582, 582, 582, 546: 582, 582, 549: 582, 552: 582, 582, 565: 582, 608: 582, 695: 582, 709: 582, 582}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4789, 3051, 3052, 3050}, + // 1935 + {543: 4790}, + {641: 4791}, + {533: 3586, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4792, 779: 3582}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 886: 3747, 901: 4793}, + {583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 15: 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 583, 530: 583, 583, 583, 535: 583, 537: 583, 583, 583, 546: 583, 583, 549: 583, 552: 583, 583, 565: 583, 608: 583, 695: 583, 709: 583, 583}, + // 1940 + {533: 4796, 1167: 4795}, + {584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 15: 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 584, 530: 584, 584, 584, 535: 584, 537: 584, 584, 584, 546: 584, 584, 549: 584, 552: 584, 584, 565: 584, 608: 584, 695: 584, 709: 584, 584}, + {148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 15: 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 148, 530: 148, 148, 148, 535: 148, 537: 148, 148, 148, 546: 148, 148, 549: 148, 552: 148, 148, 557: 148, 565: 148, 608: 148, 695: 148, 709: 148, 148}, + {531: 4798}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 759, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 4799, 1293: 4800}, + // 1945 + {758, 758, 9: 3930, 57: 758, 532: 758}, + {57: 4801}, + {585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 15: 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 585, 530: 585, 585, 585, 535: 585, 537: 585, 585, 585, 546: 585, 585, 549: 585, 552: 585, 585, 565: 585, 608: 585, 695: 585, 709: 585, 585}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 548: 4803, 770: 3737, 3051, 3052, 3050, 805: 4804}, + {587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 15: 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 587, 530: 587, 587, 587, 535: 587, 537: 587, 587, 587, 546: 587, 587, 549: 587, 552: 587, 587, 565: 587, 608: 587, 695: 587, 709: 587, 587}, + // 1950 + {586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 15: 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 586, 530: 586, 586, 586, 535: 586, 537: 586, 586, 586, 546: 586, 586, 549: 586, 552: 586, 586, 565: 586, 608: 586, 695: 586, 709: 586, 586}, + {535: 4807, 560: 3037, 799: 3866, 816: 4808, 1286: 4806}, + {590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 15: 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 590, 530: 590, 590, 590, 535: 590, 537: 590, 590, 590, 546: 590, 590, 549: 590, 552: 590, 590, 565: 590, 608: 590, 695: 590, 709: 590, 590}, + {578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 15: 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 578, 530: 578, 578, 578, 535: 578, 537: 578, 578, 578, 546: 578, 578, 549: 578, 552: 578, 578, 565: 578, 608: 578, 695: 578, 709: 578, 578}, + {577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 15: 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 577, 530: 577, 577, 577, 535: 577, 537: 577, 577, 577, 546: 577, 577, 549: 577, 552: 577, 577, 565: 577, 608: 577, 695: 577, 709: 577, 577}, + // 1955 + {560: 3037, 799: 3866, 816: 4810}, + {591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 15: 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 591, 530: 591, 591, 591, 535: 591, 537: 591, 591, 591, 546: 591, 591, 549: 591, 552: 591, 591, 565: 591, 608: 591, 695: 591, 709: 591, 591}, + {560: 3037, 799: 3866, 816: 4812}, + {592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 15: 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 592, 530: 592, 592, 592, 535: 592, 537: 592, 592, 592, 546: 592, 592, 549: 592, 552: 592, 592, 565: 592, 608: 592, 695: 592, 709: 592, 592}, + {533: 4814}, + // 1960 + {593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 15: 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 593, 530: 593, 593, 593, 535: 593, 537: 593, 593, 593, 546: 593, 593, 549: 593, 552: 593, 593, 565: 593, 608: 593, 695: 593, 709: 593, 593}, + {533: 4816}, + {594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 15: 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 594, 530: 594, 594, 594, 535: 594, 537: 594, 594, 594, 546: 594, 594, 549: 594, 552: 594, 594, 565: 594, 608: 594, 695: 594, 709: 594, 594}, + {560: 4087, 636: 4089, 4088, 916: 4818}, + {595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 15: 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 595, 530: 595, 595, 595, 535: 595, 537: 595, 595, 595, 546: 595, 595, 549: 595, 552: 595, 595, 565: 595, 608: 595, 695: 595, 709: 595, 595}, + // 1965 + {560: 3037, 799: 3866, 816: 4820}, + {596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 15: 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 596, 530: 596, 596, 596, 535: 596, 537: 596, 596, 596, 546: 596, 596, 549: 596, 552: 596, 596, 565: 596, 608: 596, 695: 596, 709: 596, 596}, + {560: 3037, 799: 3866, 816: 4822}, + {597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 15: 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 597, 530: 597, 597, 597, 535: 597, 537: 597, 597, 597, 546: 597, 597, 549: 597, 552: 597, 597, 565: 597, 608: 597, 695: 597, 709: 597, 597}, + {535: 4825, 560: 3037, 799: 3866, 816: 4824}, + // 1970 + {599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 15: 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 599, 530: 599, 599, 599, 535: 599, 537: 599, 599, 599, 546: 599, 599, 549: 599, 552: 599, 599, 565: 599, 608: 599, 695: 599, 709: 599, 599}, + {598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 15: 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 598, 530: 598, 598, 598, 535: 598, 537: 598, 598, 598, 546: 598, 598, 549: 598, 552: 598, 598, 565: 598, 608: 598, 695: 598, 709: 598, 598}, + {535: 4828, 560: 3037, 799: 3866, 816: 4827}, + {601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 15: 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 601, 530: 601, 601, 601, 535: 601, 537: 601, 601, 601, 546: 601, 601, 549: 601, 552: 601, 601, 565: 601, 608: 601, 695: 601, 709: 601, 601}, + {600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 15: 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 530: 600, 600, 600, 535: 600, 537: 600, 600, 600, 546: 600, 600, 549: 600, 552: 600, 600, 565: 600, 608: 600, 695: 600, 709: 600, 600}, + // 1975 + {535: 4807, 560: 3037, 799: 3866, 816: 4808, 1286: 4830}, + {602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 15: 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 602, 530: 602, 602, 602, 535: 602, 537: 602, 602, 602, 546: 602, 602, 549: 602, 552: 602, 602, 565: 602, 608: 602, 695: 602, 709: 602, 602}, + {560: 3037, 799: 3866, 816: 4832}, + {604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 15: 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 604, 530: 604, 604, 604, 535: 604, 537: 604, 604, 604, 546: 604, 604, 549: 604, 552: 604, 604, 565: 604, 608: 604, 695: 604, 709: 604, 604}, + {560: 3037, 799: 3866, 816: 4834}, + // 1980 + {605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 15: 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 605, 530: 605, 605, 605, 535: 605, 537: 605, 605, 605, 546: 605, 605, 549: 605, 552: 605, 605, 565: 605, 608: 605, 695: 605, 709: 605, 605}, + {533: 4836}, + {606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 15: 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 606, 530: 606, 606, 606, 535: 606, 537: 606, 606, 606, 546: 606, 606, 549: 606, 552: 606, 606, 565: 606, 608: 606, 695: 606, 709: 606, 606}, + {533: 4838}, + {607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 15: 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 607, 530: 607, 607, 607, 535: 607, 537: 607, 607, 607, 546: 607, 607, 549: 607, 552: 607, 607, 565: 607, 608: 607, 695: 607, 709: 607, 607}, + // 1985 + {560: 3037, 799: 3866, 816: 4840}, + {608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 15: 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 530: 608, 608, 608, 535: 608, 537: 608, 608, 608, 546: 608, 608, 549: 608, 552: 608, 608, 565: 608, 608: 608, 695: 608, 709: 608, 608}, + {560: 3037, 799: 3866, 816: 4842}, + {609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 15: 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 609, 530: 609, 609, 609, 535: 609, 537: 609, 609, 609, 546: 609, 609, 549: 609, 552: 609, 609, 565: 609, 608: 609, 695: 609, 709: 609, 609}, + {533: 4844}, + // 1990 + {610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 15: 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 610, 530: 610, 610, 610, 535: 610, 537: 610, 610, 610, 546: 610, 610, 549: 610, 552: 610, 610, 565: 610, 608: 610, 695: 610, 709: 610, 610}, + {560: 3037, 799: 3866, 816: 4846}, + {611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 15: 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 611, 530: 611, 611, 611, 535: 611, 537: 611, 611, 611, 546: 611, 611, 549: 611, 552: 611, 611, 565: 611, 608: 611, 695: 611, 709: 611, 611}, + {560: 3037, 799: 3866, 816: 4848}, + {613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 15: 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 530: 613, 613, 613, 535: 613, 537: 613, 613, 613, 546: 613, 613, 549: 613, 552: 613, 613, 565: 613, 608: 613, 695: 613, 709: 613, 613}, + // 1995 + {555: 4588, 560: 2316, 802: 4853}, + {555: 4588, 560: 2316, 802: 4851}, + {560: 3037, 799: 3866, 816: 4852}, + {612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 15: 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 612, 530: 612, 612, 612, 535: 612, 537: 612, 612, 612, 546: 612, 612, 549: 612, 552: 612, 612, 565: 612, 608: 612, 695: 612, 709: 612, 612}, + {560: 3037, 799: 3866, 816: 4854}, + // 2000 + {614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 15: 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 614, 530: 614, 614, 614, 535: 614, 537: 614, 614, 614, 546: 614, 614, 549: 614, 552: 614, 614, 565: 614, 608: 614, 695: 614, 709: 614, 614}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 4859}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 4857}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 4858}, + {615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 15: 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 530: 615, 615, 615, 535: 615, 537: 615, 615, 615, 546: 615, 615, 549: 615, 552: 615, 615, 565: 615, 608: 615, 695: 615, 709: 615, 615}, + // 2005 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 4860}, + {616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 15: 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 616, 530: 616, 616, 616, 535: 616, 537: 616, 616, 616, 546: 616, 616, 549: 616, 552: 616, 616, 565: 616, 608: 616, 695: 616, 709: 616, 616}, + {560: 3037, 799: 3866, 816: 4862}, + {2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 15: 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 2391, 57: 2391, 530: 2391, 2391, 2391, 535: 2391, 537: 2391, 2391, 2391, 546: 2391, 2391, 549: 2391, 552: 2391, 2391, 565: 2391, 608: 2391, 695: 2391, 709: 2391, 2391}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4864, 3051, 3052, 3050}, + // 2010 + {2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 15: 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 2392, 57: 2392, 530: 2392, 2392, 2392, 535: 2392, 537: 2392, 2392, 2392, 546: 2392, 2392, 549: 2392, 552: 2392, 2392, 565: 2392, 608: 2392, 695: 2392, 709: 2392, 2392}, + {560: 3037, 799: 3866, 816: 4866}, + {2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 15: 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 2393, 57: 2393, 530: 2393, 2393, 2393, 535: 2393, 537: 2393, 2393, 2393, 546: 2393, 2393, 549: 2393, 552: 2393, 2393, 565: 2393, 608: 2393, 695: 2393, 709: 2393, 2393}, + {560: 3037, 799: 3866, 816: 4868}, + {2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 15: 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 2394, 57: 2394, 530: 2394, 2394, 2394, 535: 2394, 537: 2394, 2394, 2394, 546: 2394, 2394, 549: 2394, 552: 2394, 2394, 565: 2394, 608: 2394, 695: 2394, 709: 2394, 2394}, + // 2015 + {533: 2316, 555: 4588, 802: 4870}, + {533: 4871}, + {2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 15: 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 2395, 57: 2395, 530: 2395, 2395, 2395, 535: 2395, 537: 2395, 2395, 2395, 546: 2395, 2395, 549: 2395, 552: 2395, 2395, 565: 2395, 608: 2395, 695: 2395, 709: 2395, 2395}, + {533: 2316, 555: 4588, 802: 4873}, + {533: 4874}, + // 2020 + {2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 15: 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 2396, 57: 2396, 530: 2396, 2396, 2396, 535: 2396, 537: 2396, 2396, 2396, 546: 2396, 2396, 549: 2396, 552: 2396, 2396, 565: 2396, 608: 2396, 695: 2396, 709: 2396, 2396}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4876}, + {2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 15: 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 2397, 57: 2397, 530: 2397, 2397, 2397, 535: 2397, 537: 2397, 2397, 2397, 546: 2397, 2397, 549: 2397, 552: 2397, 2397, 565: 2397, 608: 2397, 695: 2397, 709: 2397, 2397}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 802: 4880}, + {589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 15: 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 589, 530: 589, 589, 589, 535: 589, 537: 589, 589, 589, 546: 589, 589, 549: 589, 552: 589, 589, 565: 589, 608: 589, 695: 589, 709: 589, 589}, + // 2025 + {588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 15: 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 588, 530: 588, 588, 588, 535: 588, 537: 588, 588, 588, 546: 588, 588, 549: 588, 552: 588, 588, 565: 588, 608: 588, 695: 588, 709: 588, 588}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4881}, + {2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 15: 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 2398, 57: 2398, 530: 2398, 2398, 2398, 535: 2398, 537: 2398, 2398, 2398, 546: 2398, 2398, 549: 2398, 552: 2398, 2398, 565: 2398, 608: 2398, 695: 2398, 709: 2398, 2398}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 4883}, + {2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 15: 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 2399, 57: 2399, 530: 2399, 2399, 2399, 535: 2399, 537: 2399, 2399, 2399, 546: 2399, 2399, 549: 2399, 552: 2399, 2399, 565: 2399, 608: 2399, 695: 2399, 709: 2399, 2399}, + // 2030 + {533: 4885}, + {2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 15: 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 57: 2400, 530: 2400, 2400, 2400, 535: 2400, 537: 2400, 2400, 2400, 546: 2400, 2400, 549: 2400, 552: 2400, 2400, 565: 2400, 608: 2400, 695: 2400, 709: 2400, 2400}, + {6: 4725, 4727, 580, 10: 4694, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 89: 4696, 4717, 4718, 102: 4719, 208: 4699, 234: 4688, 249: 4682, 253: 4703, 256: 4704, 270: 4698, 276: 4714, 290: 4692, 299: 4700, 305: 4695, 324: 4705, 331: 4701, 338: 4715, 4716, 343: 4683, 532: 4713, 535: 4724, 538: 2446, 4761, 553: 2446, 557: 4685, 562: 4720, 564: 4702, 4712, 646: 4686, 702: 4691, 709: 2446, 4730, 724: 4707, 727: 4693, 729: 4721, 766: 4706, 4708, 769: 4687, 774: 4697, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 972: 4711, 986: 4709, 1020: 4684, 1028: 4689, 1110: 4887, 1285: 4690, 1309: 4710}, + {2688, 2688, 2688, 2688, 2688, 2688, 9: 2688, 546: 2688}, + {2702, 2702, 2702, 2702, 2702, 2702, 9: 2702, 546: 2702}, + // 2035 + {2701, 2701, 2701, 2701, 2701, 2701, 9: 2701, 546: 2701}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4891, 770: 4892, 3051, 3052, 3050}, + {2704, 2704, 2704, 2704, 2704, 2704, 9: 2704, 102: 2704, 546: 2704}, + {2703, 2703, 2703, 2703, 2703, 2703, 9: 2703, 102: 2703, 546: 2703}, + {56: 4898, 293: 4895, 315: 4896, 317: 4897, 535: 4894}, + // 2040 + {2709, 2709, 2709, 2709, 2709, 2709, 9: 2709, 546: 2709, 562: 2709}, + {2708, 2708, 2708, 2708, 2708, 2708, 9: 2708, 546: 2708, 562: 2708}, + {2707, 2707, 2707, 2707, 2707, 2707, 9: 2707, 546: 2707, 562: 2707}, + {2706, 2706, 2706, 2706, 2706, 2706, 9: 2706, 546: 2706, 562: 2706}, + {2705, 2705, 2705, 2705, 2705, 2705, 9: 2705, 546: 2705, 562: 2705}, + // 2045 + {2727, 2727, 2727, 2727, 2727, 2727, 9: 2727, 546: 2727}, + {2728, 2728, 2728, 2728, 2728, 2728, 9: 2728, 546: 2728}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4914, 3051, 3052, 3050}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4913}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4912}, + // 2050 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 4911}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4908, 3051, 3052, 3050}, + {2: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 10: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 58: 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 531: 2700, 542: 2700, 558: 2700, 578: 2700}, + {2: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 10: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 58: 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 2699, 531: 2699, 542: 2699, 558: 2699, 578: 2699}, + {712: 4909}, + // 2055 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4910, 3051, 3052, 3050}, + {2733, 2733, 2733, 2733, 2733, 2733, 9: 2733, 546: 2733}, + {2734, 2734, 2734, 2734, 2734, 2734, 9: 2734, 546: 2734}, + {2735, 2735, 2735, 2735, 2735, 2735, 9: 2735, 546: 2735}, + {2736, 2736, 2736, 2736, 2736, 2736, 9: 2736, 546: 2736}, + // 2060 + {712: 4915}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4916, 3051, 3052, 3050}, + {2737, 2737, 2737, 2737, 2737, 2737, 9: 2737, 546: 2737}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4932}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4927, 3051, 3052, 3050}, + // 2065 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4923, 3051, 3052, 3050}, + {2: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 10: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 58: 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 2695, 531: 2695, 578: 2695}, + {2: 625, 625, 625, 625, 625, 625, 625, 10: 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 58: 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625, 625}, + {2: 624, 624, 624, 624, 624, 624, 624, 10: 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 58: 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624, 624}, + {106: 4926, 109: 4925, 959: 4924}, + // 2070 + {2722, 2722, 2722, 2722, 2722, 2722, 9: 2722, 546: 2722}, + {2092, 2092, 2092, 2092, 2092, 2092, 2092, 9: 2092, 19: 2092, 57: 2092, 102: 2092, 104: 2092, 2092, 2092, 2092, 109: 2092, 532: 2092, 542: 2092, 546: 2092, 562: 2092}, + {2091, 2091, 2091, 2091, 2091, 2091, 2091, 9: 2091, 19: 2091, 57: 2091, 102: 2091, 104: 2091, 2091, 2091, 2091, 109: 2091, 532: 2091, 542: 2091, 546: 2091, 562: 2091}, + {190: 4929, 534: 3812, 536: 3811, 915: 4930, 1044: 4928}, + {2724, 2724, 2724, 2724, 2724, 2724, 9: 2724, 546: 2724}, + // 2075 + {2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 2588, 57: 2588, 530: 2588, 534: 2588, 2588, 2588, 2588, 2588, 546: 2588, 548: 2588, 701: 2588, 2588, 704: 2588, 2588, 2588, 2588, 2588}, + {190: 4931}, + {2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 2587, 57: 2587, 530: 2587, 534: 2587, 2587, 2587, 2587, 2587, 546: 2587, 548: 2587, 701: 2587, 2587, 704: 2587, 2587, 2587, 2587, 2587}, + {557: 4933, 727: 4934}, + {535: 4936}, + // 2080 + {535: 4935}, + {2738, 2738, 2738, 2738, 2738, 2738, 9: 2738, 546: 2738}, + {531: 4938, 533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4939, 779: 3582, 1082: 4937}, + {2740, 2740, 2740, 2740, 2740, 2740, 9: 2740, 546: 2740}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 4944}, + // 2085 + {2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 2497, 57: 2497, 530: 2497, 534: 2497, 2497, 2497, 2497, 2497, 546: 2497, 548: 2497, 701: 2497, 2497, 704: 2497, 2497, 2497, 2497, 2497}, + {560: 4087, 636: 4089, 4088, 916: 4943}, + {560: 4087, 636: 4089, 4088, 916: 4942}, + {2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 2495, 57: 2495, 530: 2495, 534: 2495, 2495, 2495, 2495, 2495, 546: 2495, 548: 2495, 701: 2495, 2495, 704: 2495, 2495, 2495, 2495, 2495}, + {2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 2496, 57: 2496, 530: 2496, 534: 2496, 2496, 2496, 2496, 2496, 546: 2496, 548: 2496, 701: 2496, 2496, 704: 2496, 2496, 2496, 2496, 2496}, + // 2090 + {57: 4945, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2739, 2739, 2739, 2739, 2739, 2739, 9: 2739, 546: 2739}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 4947}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4950}, + {645: 4949}, + // 2095 + {2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 58: 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 2118, 533: 2118, 535: 2118, 546: 2118, 631: 2118}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 4951}, + {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5231}, + {12: 4954, 118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 4953, 1098: 5019}, + {2548, 2548, 2548, 2548, 2548, 2548, 5096, 5102, 5090, 2548, 2548, 2548, 5094, 5103, 5101, 57: 2548, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2548, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5105, 1048: 5098, 1070: 5091, 1126: 5106, 5230}, + // 2100 + {2548, 2548, 2548, 2548, 2548, 2548, 5096, 5102, 5090, 2548, 2548, 2548, 5094, 5103, 5101, 57: 2548, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2548, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5105, 1048: 5098, 1070: 5091, 1126: 5106, 5086}, + {553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 553, 57: 553, 530: 553, 534: 553, 553, 553, 553, 553, 546: 553, 548: 553, 701: 553, 553, 704: 553, 553, 553, 553, 553}, + {552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 552, 57: 552, 530: 552, 534: 552, 552, 552, 552, 552, 546: 552, 548: 552, 701: 552, 552, 704: 552, 552, 552, 552, 552}, + {551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 551, 57: 551, 530: 551, 534: 551, 551, 551, 551, 551, 546: 551, 548: 551, 701: 551, 551, 704: 551, 551, 551, 551, 551}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 59: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 814: 465, 819: 465, 841: 4409, 888: 5083}, + // 2105 + {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5082}, + {458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 57: 458, 59: 458, 530: 458, 4395, 534: 458, 458, 458, 458, 458, 546: 458, 548: 458, 701: 458, 458, 704: 458, 458, 458, 458, 458, 814: 458, 819: 458, 841: 4396, 1003: 5080, 1010: 4397}, + {458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 458, 57: 458, 59: 458, 530: 458, 4395, 534: 458, 458, 458, 458, 458, 546: 458, 548: 458, 701: 458, 458, 704: 458, 458, 458, 458, 458, 814: 458, 819: 458, 841: 4396, 1003: 5078, 1010: 4397}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5077}, + {545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 545, 57: 545, 59: 545, 530: 545, 545, 534: 545, 545, 545, 545, 545, 546: 545, 548: 545, 701: 545, 545, 704: 545, 545, 545, 545, 545, 814: 545, 819: 545}, + // 2110 + {544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 57: 544, 59: 544, 530: 544, 544, 534: 544, 544, 544, 544, 544, 546: 544, 548: 544, 701: 544, 544, 704: 544, 544, 544, 544, 544, 814: 544, 819: 544}, + {543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 543, 57: 543, 59: 543, 530: 543, 543, 534: 543, 543, 543, 543, 543, 546: 543, 548: 543, 701: 543, 543, 704: 543, 543, 543, 543, 543, 814: 543, 819: 543}, + {542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 542, 57: 542, 59: 542, 530: 542, 542, 534: 542, 542, 542, 542, 542, 546: 542, 548: 542, 701: 542, 542, 704: 542, 542, 542, 542, 542, 814: 542, 819: 542}, + {541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 541, 57: 541, 59: 541, 530: 541, 541, 534: 541, 541, 541, 541, 541, 546: 541, 548: 541, 701: 541, 541, 704: 541, 541, 541, 541, 541, 814: 541, 819: 541}, + {540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 540, 57: 540, 59: 540, 530: 540, 540, 534: 540, 540, 540, 540, 540, 546: 540, 548: 540, 701: 540, 540, 704: 540, 540, 540, 540, 540, 814: 540, 819: 540}, + // 2115 + {539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 539, 57: 539, 59: 539, 530: 539, 539, 534: 539, 539, 539, 539, 539, 546: 539, 548: 539, 701: 539, 539, 704: 539, 539, 539, 539, 539, 814: 539, 819: 539}, + {538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 538, 57: 538, 59: 538, 530: 538, 538, 534: 538, 538, 538, 538, 538, 546: 538, 548: 538, 701: 538, 538, 704: 538, 538, 538, 538, 538, 814: 538, 819: 538}, + {537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 537, 57: 537, 59: 537, 530: 537, 537, 534: 537, 537, 537, 537, 537, 546: 537, 548: 537, 701: 537, 537, 704: 537, 537, 537, 537, 537, 814: 537, 819: 537}, + {536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 536, 57: 536, 59: 536, 530: 536, 536, 534: 536, 536, 536, 536, 536, 546: 536, 548: 536, 701: 536, 536, 704: 536, 536, 536, 536, 536, 814: 536, 819: 536}, + {535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 535, 57: 535, 59: 535, 530: 535, 535, 534: 535, 535, 535, 535, 535, 546: 535, 548: 535, 701: 535, 535, 704: 535, 535, 535, 535, 535, 814: 535, 819: 535}, + // 2120 + {534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 57: 534, 59: 534, 530: 534, 534, 534: 534, 534, 534, 534, 534, 546: 534, 548: 534, 701: 534, 534, 704: 534, 534, 534, 534, 534, 814: 534, 819: 534}, + {533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 533, 57: 533, 59: 533, 530: 533, 534: 533, 533, 533, 533, 533, 546: 533, 548: 533, 701: 533, 533, 704: 533, 533, 533, 533, 533, 814: 533, 819: 533}, + {532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 57: 532, 59: 532, 530: 532, 534: 532, 532, 532, 532, 532, 546: 532, 548: 532, 701: 532, 532, 704: 532, 532, 532, 532, 532, 814: 532, 819: 532}, + {528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 528, 57: 528, 59: 528, 530: 528, 528, 534: 528, 528, 528, 528, 528, 546: 528, 548: 528, 701: 528, 528, 704: 528, 528, 528, 528, 528, 814: 528, 819: 528}, + {527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 527, 57: 527, 59: 527, 530: 527, 527, 534: 527, 527, 527, 527, 527, 546: 527, 548: 527, 701: 527, 527, 704: 527, 527, 527, 527, 527, 814: 527, 819: 527}, + // 2125 + {526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 526, 57: 526, 59: 526, 530: 526, 526, 534: 526, 526, 526, 526, 526, 546: 526, 548: 526, 701: 526, 526, 704: 526, 526, 526, 526, 526, 814: 526, 819: 526}, + {525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 525, 57: 525, 59: 525, 530: 525, 525, 534: 525, 525, 525, 525, 525, 546: 525, 548: 525, 701: 525, 525, 704: 525, 525, 525, 525, 525, 814: 525, 819: 525}, + {524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 524, 57: 524, 59: 524, 530: 524, 524, 534: 524, 524, 524, 524, 524, 546: 524, 548: 524, 701: 524, 524, 704: 524, 524, 524, 524, 524, 814: 524, 819: 524}, + {523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, 57: 523, 59: 523, 530: 523, 523, 534: 523, 523, 523, 523, 523, 546: 523, 548: 523, 701: 523, 523, 704: 523, 523, 523, 523, 523, 814: 523, 819: 523, 1421: 5076}, + {521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 521, 57: 521, 59: 521, 530: 521, 521, 534: 521, 521, 521, 521, 521, 546: 521, 548: 521, 701: 521, 521, 704: 521, 521, 521, 521, 521, 814: 521, 819: 521}, + // 2130 + {520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 520, 57: 520, 59: 520, 530: 520, 520, 534: 520, 520, 520, 520, 520, 546: 520, 548: 520, 701: 520, 520, 704: 520, 520, 520, 520, 520, 814: 520, 819: 520}, + {519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 519, 57: 519, 530: 519, 519, 534: 519, 519, 519, 519, 519, 546: 519, 548: 519, 701: 519, 519, 704: 519, 519, 519, 519, 519}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 4408, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 841: 5073, 853: 4416, 896: 5074}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 4408, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 841: 5070, 853: 4416, 896: 5071}, + {531: 4408, 841: 5068}, + // 2135 + {531: 4408, 841: 5066}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5065}, + {531: 4408, 841: 5064}, + {510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 510, 57: 510, 530: 510, 534: 510, 510, 510, 510, 510, 546: 510, 548: 510, 701: 510, 510, 704: 510, 510, 510, 510, 510}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5063}, + // 2140 + {531: 5059}, + {531: 5052}, + {506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 506, 57: 506, 530: 506, 534: 506, 506, 506, 506, 506, 546: 506, 548: 506, 701: 506, 506, 704: 506, 506, 506, 506, 506}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 5045, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 5044, 732: 5003, 5002, 740: 5046, 853: 4416, 896: 5047, 987: 5043, 1025: 5042}, + {503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 16: 503, 57: 503, 530: 503, 503, 534: 503, 503, 503, 503, 503, 546: 503, 548: 503, 553: 503, 586: 503, 701: 503, 503, 704: 503, 503, 503, 503, 503, 503, 942: 5041}, + // 2145 + {502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 16: 502, 57: 502, 530: 502, 502, 534: 502, 502, 502, 502, 502, 546: 502, 548: 502, 553: 502, 586: 502, 701: 502, 502, 704: 502, 502, 502, 502, 502, 502, 942: 5040}, + {501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 16: 501, 57: 501, 530: 501, 501, 534: 501, 501, 501, 501, 501, 546: 501, 548: 501, 553: 501, 586: 501, 701: 501, 501, 704: 501, 501, 501, 501, 501, 501, 732: 5038, 5037, 942: 5039}, + {553: 5032, 709: 5031, 732: 5034, 5033}, + {496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 496, 16: 496, 57: 496, 151: 496, 496, 496, 530: 496, 496, 534: 496, 496, 496, 496, 496, 546: 496, 548: 496, 553: 496, 586: 496, 701: 496, 496, 704: 496, 496, 496, 496, 496, 496}, + {495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 495, 16: 495, 57: 495, 151: 495, 495, 495, 530: 495, 495, 534: 495, 495, 495, 495, 495, 546: 495, 548: 495, 553: 495, 586: 495, 701: 495, 495, 704: 495, 495, 495, 495, 495, 495}, + // 2150 + {531: 492}, + {486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 486, 57: 486, 59: 486, 530: 486, 486, 534: 486, 486, 486, 486, 486, 546: 486, 548: 486, 701: 486, 486, 704: 486, 486, 486, 486, 486, 814: 486, 819: 486}, + {485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 485, 57: 485, 59: 485, 530: 485, 485, 534: 485, 485, 485, 485, 485, 546: 485, 548: 485, 701: 485, 485, 704: 485, 485, 485, 485, 485, 814: 485, 819: 485}, + {484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 484, 57: 484, 530: 484, 534: 484, 484, 484, 484, 484, 546: 484, 548: 484, 701: 484, 484, 704: 484, 484, 484, 484, 484}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5030}, + // 2155 + {482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 482, 57: 482, 530: 482, 534: 482, 482, 482, 482, 482, 546: 482, 548: 482, 701: 482, 482, 704: 482, 482, 482, 482, 482}, + {481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 481, 57: 481, 530: 481, 534: 481, 481, 481, 481, 481, 546: 481, 548: 481, 701: 481, 481, 704: 481, 481, 481, 481, 481}, + {479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 479, 16: 479, 57: 479, 151: 479, 479, 479, 530: 479, 534: 479, 479, 479, 479, 479, 546: 479, 548: 479, 553: 479, 586: 479, 701: 479, 479, 704: 479, 479, 479, 479, 479, 479}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 16: 465, 57: 465, 151: 465, 465, 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 553: 465, 586: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 465, 841: 4409, 888: 5029}, + {477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 477, 16: 477, 57: 477, 151: 477, 477, 477, 530: 477, 534: 477, 477, 477, 477, 477, 546: 477, 548: 477, 553: 477, 586: 477, 701: 477, 477, 704: 477, 477, 477, 477, 477, 477}, + // 2160 + {476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 476, 16: 476, 57: 476, 151: 476, 476, 476, 530: 476, 534: 476, 476, 476, 476, 476, 546: 476, 548: 476, 553: 476, 586: 476, 701: 476, 476, 704: 476, 476, 476, 476, 476, 476}, + {471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 471, 57: 471, 530: 471, 534: 471, 471, 471, 471, 471, 546: 471, 548: 471, 701: 471, 471, 704: 471, 471, 471, 471, 471}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5028}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5027}, + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 841: 4409, 888: 5026}, + // 2165 + {465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 465, 57: 465, 59: 465, 530: 465, 4408, 534: 465, 465, 465, 465, 465, 546: 465, 548: 465, 701: 465, 465, 704: 465, 465, 465, 465, 465, 814: 465, 819: 465, 841: 4409, 888: 5020}, + {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5021}, + {467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 467, 57: 467, 59: 5023, 530: 467, 534: 467, 467, 467, 467, 467, 546: 467, 548: 467, 701: 467, 467, 704: 467, 467, 467, 467, 467, 814: 5022, 819: 5024, 978: 5025}, + {463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 463, 57: 463, 59: 463, 530: 463, 534: 463, 463, 463, 463, 463, 546: 463, 548: 463, 701: 463, 463, 704: 463, 463, 463, 463, 463, 814: 463, 819: 463}, + {462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 462, 57: 462, 59: 462, 530: 462, 534: 462, 462, 462, 462, 462, 546: 462, 548: 462, 701: 462, 462, 704: 462, 462, 462, 462, 462, 814: 462, 819: 462}, + // 2170 + {461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 461, 57: 461, 59: 461, 530: 461, 534: 461, 461, 461, 461, 461, 546: 461, 548: 461, 701: 461, 461, 704: 461, 461, 461, 461, 461, 814: 461, 819: 461}, + {459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 459, 57: 459, 59: 459, 530: 459, 534: 459, 459, 459, 459, 459, 546: 459, 548: 459, 701: 459, 459, 704: 459, 459, 459, 459, 459, 814: 459, 819: 459}, + {468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 468, 57: 468, 530: 468, 534: 468, 468, 468, 468, 468, 546: 468, 548: 468, 701: 468, 468, 704: 468, 468, 468, 468, 468}, + {469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 469, 57: 469, 530: 469, 534: 469, 469, 469, 469, 469, 546: 469, 548: 469, 701: 469, 469, 704: 469, 469, 469, 469, 469}, + {470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 470, 57: 470, 530: 470, 534: 470, 470, 470, 470, 470, 546: 470, 548: 470, 701: 470, 470, 704: 470, 470, 470, 470, 470}, + // 2175 + {478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 478, 16: 478, 57: 478, 151: 478, 478, 478, 530: 478, 534: 478, 478, 478, 478, 478, 546: 478, 548: 478, 553: 478, 586: 478, 701: 478, 478, 704: 478, 478, 478, 478, 478, 478}, + {483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 483, 57: 483, 530: 483, 534: 483, 483, 483, 483, 483, 546: 483, 548: 483, 701: 483, 483, 704: 483, 483, 483, 483, 483}, + {500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 16: 500, 57: 500, 530: 500, 500, 534: 500, 500, 500, 500, 500, 546: 500, 548: 500, 553: 500, 586: 500, 701: 500, 500, 704: 500, 500, 500, 500, 500, 500, 942: 5036}, + {499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 16: 499, 57: 499, 530: 499, 499, 534: 499, 499, 499, 499, 499, 546: 499, 548: 499, 553: 499, 586: 499, 701: 499, 499, 704: 499, 499, 499, 499, 499, 499, 942: 5035}, + {531: 494}, + // 2180 + {531: 493}, + {531: 488}, + {531: 489}, + {531: 491}, + {531: 490}, + // 2185 + {531: 487}, + {497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 16: 497, 57: 497, 151: 497, 497, 497, 530: 497, 497, 534: 497, 497, 497, 497, 497, 546: 497, 548: 497, 553: 497, 586: 497, 701: 497, 497, 704: 497, 497, 497, 497, 497, 497}, + {498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 16: 498, 57: 498, 151: 498, 498, 498, 530: 498, 498, 534: 498, 498, 498, 498, 498, 546: 498, 548: 498, 553: 498, 586: 498, 701: 498, 498, 704: 498, 498, 498, 498, 498, 498}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5051}, + {504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 57: 504, 530: 504, 534: 504, 504, 504, 504, 504, 546: 504, 548: 504, 701: 504, 504, 704: 504, 504, 504, 504, 504}, + // 2190 + {557: 4421, 942: 5041}, + {557: 4420, 942: 5040}, + {480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 57: 480, 530: 480, 534: 480, 480, 480, 480, 480, 546: 480, 548: 480, 701: 480, 480, 704: 480, 480, 480, 480, 480}, + {475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 475, 57: 475, 530: 475, 534: 475, 475, 475, 475, 475, 546: 475, 548: 475, 701: 475, 475, 704: 475, 475, 475, 475, 475}, + {474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 474, 57: 474, 530: 474, 534: 474, 474, 474, 474, 474, 546: 474, 548: 474, 701: 474, 474, 704: 474, 474, 474, 474, 474}, + // 2195 + {473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 473, 57: 473, 530: 473, 534: 473, 473, 473, 473, 473, 546: 473, 548: 473, 701: 473, 473, 704: 473, 473, 473, 473, 473}, + {472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 472, 57: 472, 530: 472, 534: 472, 472, 472, 472, 472, 546: 472, 548: 472, 701: 472, 472, 704: 472, 472, 472, 472, 472}, + {505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 505, 57: 505, 530: 505, 534: 505, 505, 505, 505, 505, 546: 505, 548: 505, 701: 505, 505, 704: 505, 505, 505, 505, 505}, + {533: 3960, 638: 3961, 640: 3962, 1021: 5054, 1297: 5053}, + {9: 5056, 57: 5055}, + // 2200 + {9: 437, 57: 437}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5058}, + {533: 3960, 638: 3961, 640: 3962, 1021: 5057}, + {9: 436, 57: 436}, + {507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 507, 57: 507, 530: 507, 534: 507, 507, 507, 507, 507, 546: 507, 548: 507, 701: 507, 507, 704: 507, 507, 507, 507, 507}, + // 2205 + {533: 3960, 638: 3961, 640: 3962, 1021: 5054, 1297: 5060}, + {9: 5056, 57: 5061}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 151: 5048, 5050, 5049, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5047, 987: 5062}, + {508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 508, 57: 508, 530: 508, 534: 508, 508, 508, 508, 508, 546: 508, 548: 508, 701: 508, 508, 704: 508, 508, 508, 508, 508}, + {509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 509, 57: 509, 530: 509, 534: 509, 509, 509, 509, 509, 546: 509, 548: 509, 701: 509, 509, 704: 509, 509, 509, 509, 509}, + // 2210 + {511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 511, 57: 511, 530: 511, 534: 511, 511, 511, 511, 511, 546: 511, 548: 511, 701: 511, 511, 704: 511, 511, 511, 511, 511}, + {512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 57: 512, 530: 512, 534: 512, 512, 512, 512, 512, 546: 512, 548: 512, 701: 512, 512, 704: 512, 512, 512, 512, 512}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5067}, + {513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 513, 57: 513, 530: 513, 534: 513, 513, 513, 513, 513, 546: 513, 548: 513, 701: 513, 513, 704: 513, 513, 513, 513, 513}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5069}, + // 2215 + {514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 514, 57: 514, 530: 514, 534: 514, 514, 514, 514, 514, 546: 514, 548: 514, 701: 514, 514, 704: 514, 514, 514, 514, 514}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5072}, + {515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 515, 57: 515, 530: 515, 534: 515, 515, 515, 515, 515, 546: 515, 548: 515, 701: 515, 515, 704: 515, 515, 515, 515, 515}, + {516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 516, 57: 516, 530: 516, 534: 516, 516, 516, 516, 516, 546: 516, 548: 516, 701: 516, 516, 704: 516, 516, 516, 516, 516}, + {452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 452, 16: 4418, 57: 452, 530: 452, 534: 452, 452, 452, 452, 452, 546: 452, 548: 452, 553: 4419, 586: 4415, 701: 452, 452, 704: 452, 452, 452, 452, 452, 4417, 853: 4416, 896: 5075}, + // 2220 + {517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, 57: 517, 530: 517, 534: 517, 517, 517, 517, 517, 546: 517, 548: 517, 701: 517, 517, 704: 517, 517, 517, 517, 517}, + {518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 518, 57: 518, 530: 518, 534: 518, 518, 518, 518, 518, 546: 518, 548: 518, 701: 518, 518, 704: 518, 518, 518, 518, 518}, + {522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 522, 57: 522, 59: 522, 530: 522, 522, 534: 522, 522, 522, 522, 522, 546: 522, 548: 522, 701: 522, 522, 704: 522, 522, 522, 522, 522, 814: 522, 819: 522}, + {546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 546, 57: 546, 530: 546, 534: 546, 546, 546, 546, 546, 546: 546, 548: 546, 701: 546, 546, 704: 546, 546, 546, 546, 546}, + {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5079}, + // 2225 + {547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 547, 57: 547, 59: 5023, 530: 547, 534: 547, 547, 547, 547, 547, 546: 547, 548: 547, 701: 547, 547, 704: 547, 547, 547, 547, 547, 814: 5022, 819: 5024, 978: 5025}, + {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5081}, + {548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 548, 57: 548, 59: 5023, 530: 548, 534: 548, 548, 548, 548, 548, 546: 548, 548: 548, 701: 548, 548, 704: 548, 548, 548, 548, 548, 814: 5022, 819: 5024, 978: 5025}, + {549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 549, 57: 549, 59: 5023, 530: 549, 534: 549, 549, 549, 549, 549, 546: 549, 548: 549, 701: 549, 549, 704: 549, 549, 549, 549, 549, 814: 5022, 819: 5024, 978: 5025}, + {460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 460, 57: 460, 59: 460, 530: 460, 534: 460, 460, 460, 460, 460, 546: 460, 548: 460, 701: 460, 460, 704: 460, 460, 460, 460, 460, 814: 460, 819: 460, 979: 5084}, + // 2230 + {550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 550, 57: 550, 59: 5023, 530: 550, 534: 550, 550, 550, 550, 550, 546: 550, 548: 550, 701: 550, 550, 704: 550, 550, 550, 550, 550, 814: 5022, 819: 5024, 978: 5025}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2684, 2684, 704: 2684, 2684, 710: 2684, 746: 2684, 2684, 770: 5229, 3051, 3052, 3050, 1290: 5228}, + {2614, 2614, 2614, 2614, 2614, 2614, 9: 2614, 2614, 2614, 57: 2614, 546: 2614}, + {701: 2591}, + {548: 5227}, + // 2235 + {2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 2581, 57: 2581, 530: 2581, 534: 2581, 2581, 2581, 2581, 2581, 546: 2581, 548: 2581, 701: 2581, 2581, 704: 2581, 2581, 2581, 2581, 2581}, + {2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580, 57: 2580, 530: 2580, 534: 2580, 2580, 2580, 2580, 2580, 546: 2580, 548: 2580, 701: 2580, 2580, 704: 2580, 2580, 2580, 2580, 2580}, + {701: 5223}, + {2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 2577, 57: 2577, 530: 2577, 534: 2577, 2577, 2577, 2577, 2577, 546: 2577, 548: 2577, 701: 5222, 2577, 704: 2577, 2577, 2577, 2577, 2577}, + {56: 5210, 265: 5212, 408: 5213, 531: 5209, 533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 5195, 5194, 5190, 5191, 653: 5192, 5193, 777: 4939, 779: 3582, 5207, 999: 5208, 1036: 5189, 1059: 5187, 5188, 5211, 1082: 5206, 1214: 5205, 1349: 5204}, + // 2240 + {535: 5202}, + {714: 5185}, + {533: 5184}, + {702: 5175}, + {537: 5168}, + // 2245 + {2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 2569, 57: 2569, 530: 2569, 534: 2569, 2569, 2569, 2569, 2569, 546: 2569, 548: 2569, 701: 2569, 2569, 704: 2569, 2569, 2569, 2569, 2569}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 5167}, + {177: 5165, 255: 5166, 535: 5164, 1333: 5163}, + {236: 5162, 300: 5161, 535: 5160, 1470: 5159}, + {2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 2564, 57: 2564, 530: 2564, 5153, 534: 2564, 2564, 2564, 2564, 2564, 546: 2564, 548: 2564, 701: 2564, 2564, 704: 2564, 2564, 2564, 2564, 2564, 1325: 5152}, + // 2250 + {366: 5151}, + {2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 2550, 57: 2550, 530: 2550, 534: 2550, 2550, 2550, 2550, 2550, 546: 2550, 548: 2550, 701: 2550, 2550, 704: 2550, 2550, 2550, 2550, 2550}, + {2547, 2547, 2547, 2547, 2547, 2547, 5096, 5102, 5090, 2547, 2547, 2547, 5094, 5103, 5101, 57: 2547, 530: 5095, 534: 3812, 5093, 3811, 2555, 5100, 546: 2547, 548: 5089, 701: 2592, 2685, 704: 5087, 5092, 5085, 5107, 5104, 915: 5088, 928: 5097, 1011: 5099, 1032: 5150, 1048: 5098, 1070: 5091}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5108}, + {2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 2483, 57: 2483, 530: 2483, 5110, 534: 2483, 2483, 2483, 2483, 2483, 546: 2483, 548: 2483, 701: 2483, 2483, 704: 2483, 2483, 2483, 2483, 2483, 711: 2483, 1375: 5109}, + // 2255 + {2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 2537, 57: 2537, 530: 2537, 534: 2537, 2537, 2537, 2537, 2537, 546: 2537, 548: 2537, 701: 2537, 2537, 704: 2537, 2537, 2537, 2537, 2537, 711: 5125, 1390: 5126, 5127}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5111}, + {9: 5123, 57: 5122}, + {9: 2481, 57: 2481}, + {9: 465, 57: 465, 531: 4408, 577: 465, 605: 465, 841: 4409, 888: 5120}, + // 2260 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5115}, + {57: 5116, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {9: 1502, 57: 1502, 577: 5119, 605: 5118, 1064: 5117}, + {9: 2478, 57: 2478}, + {1501, 1501, 1501, 1501, 1501, 1501, 9: 1501, 57: 1501, 546: 1501}, + // 2265 + {1500, 1500, 1500, 1500, 1500, 1500, 9: 1500, 57: 1500, 546: 1500}, + {9: 1502, 57: 1502, 577: 5119, 605: 5118, 1064: 5121}, + {9: 2479, 57: 2479}, + {2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 2482, 57: 2482, 530: 2482, 534: 2482, 2482, 2482, 2482, 2482, 546: 2482, 548: 2482, 701: 2482, 2482, 704: 2482, 2482, 2482, 2482, 2482, 711: 2482}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5124}, + // 2270 + {9: 2480, 57: 2480}, + {260: 5147, 416: 5148, 437: 5149}, + {2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 2536, 57: 2536, 530: 2536, 534: 2536, 2536, 2536, 2536, 2536, 546: 2536, 548: 2536, 701: 2536, 2536, 704: 2536, 2536, 2536, 2536, 2536}, + {2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 2532, 57: 2532, 530: 5129, 534: 2532, 2532, 2532, 2532, 2532, 546: 2532, 548: 2532, 701: 2532, 2532, 704: 2532, 2532, 2532, 2532, 2532, 1219: 5130, 5131, 1397: 5128}, + {2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535, 57: 2535, 530: 2535, 534: 2535, 2535, 2535, 2535, 2535, 546: 2535, 548: 2535, 701: 2535, 2535, 704: 2535, 2535, 2535, 2535, 2535}, + // 2275 + {714: 5145, 803: 5134}, + {2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531, 57: 2531, 530: 5143, 534: 2531, 2531, 2531, 2531, 2531, 546: 2531, 548: 2531, 701: 2531, 2531, 704: 2531, 2531, 2531, 2531, 2531, 1220: 5144}, + {2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 2530, 57: 2530, 530: 5132, 534: 2530, 2530, 2530, 2530, 2530, 546: 2530, 548: 2530, 701: 2530, 2530, 704: 2530, 2530, 2530, 2530, 2530, 1219: 5133}, + {803: 5134}, + {2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 2528, 57: 2528, 530: 2528, 534: 2528, 2528, 2528, 2528, 2528, 546: 2528, 548: 2528, 701: 2528, 2528, 704: 2528, 2528, 2528, 2528, 2528}, + // 2280 + {86: 5139, 557: 5138, 728: 5137, 730: 5136, 1249: 5135}, + {2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 2534, 57: 2534, 530: 2534, 534: 2534, 2534, 2534, 2534, 2534, 546: 2534, 548: 2534, 701: 2534, 2534, 704: 2534, 2534, 2534, 2534, 2534}, + {2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 2527, 57: 2527, 530: 2527, 534: 2527, 2527, 2527, 2527, 2527, 546: 2527, 548: 2527, 701: 2527, 2527, 704: 2527, 2527, 2527, 2527, 2527}, + {2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 2526, 57: 2526, 530: 2526, 534: 2526, 2526, 2526, 2526, 2526, 546: 2526, 548: 2526, 701: 2526, 2526, 704: 2526, 2526, 2526, 2526, 2526}, + {535: 5142, 548: 5141}, + // 2285 + {93: 5140}, + {2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 2524, 57: 2524, 530: 2524, 534: 2524, 2524, 2524, 2524, 2524, 546: 2524, 548: 2524, 701: 2524, 2524, 704: 2524, 2524, 2524, 2524, 2524}, + {2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525, 57: 2525, 530: 2525, 534: 2525, 2525, 2525, 2525, 2525, 546: 2525, 548: 2525, 701: 2525, 2525, 704: 2525, 2525, 2525, 2525, 2525}, + {2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 2523, 57: 2523, 530: 2523, 534: 2523, 2523, 2523, 2523, 2523, 546: 2523, 548: 2523, 701: 2523, 2523, 704: 2523, 2523, 2523, 2523, 2523}, + {714: 5145}, + // 2290 + {2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529, 57: 2529, 530: 2529, 534: 2529, 2529, 2529, 2529, 2529, 546: 2529, 548: 2529, 701: 2529, 2529, 704: 2529, 2529, 2529, 2529, 2529}, + {86: 5139, 557: 5138, 728: 5137, 730: 5136, 1249: 5146}, + {2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 2533, 57: 2533, 530: 2533, 534: 2533, 2533, 2533, 2533, 2533, 546: 2533, 548: 2533, 701: 2533, 2533, 704: 2533, 2533, 2533, 2533, 2533}, + {2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 2540, 57: 2540, 530: 2540, 534: 2540, 2540, 2540, 2540, 2540, 546: 2540, 548: 2540, 701: 2540, 2540, 704: 2540, 2540, 2540, 2540, 2540}, + {2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 2539, 57: 2539, 530: 2539, 534: 2539, 2539, 2539, 2539, 2539, 546: 2539, 548: 2539, 701: 2539, 2539, 704: 2539, 2539, 2539, 2539, 2539}, + // 2295 + {2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 2538, 57: 2538, 530: 2538, 534: 2538, 2538, 2538, 2538, 2538, 546: 2538, 548: 2538, 701: 2538, 2538, 704: 2538, 2538, 2538, 2538, 2538}, + {2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 2549, 57: 2549, 530: 2549, 534: 2549, 2549, 2549, 2549, 2549, 546: 2549, 548: 2549, 701: 2549, 2549, 704: 2549, 2549, 2549, 2549, 2549}, + {537: 2554}, + {2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 2565, 57: 2565, 530: 2565, 534: 2565, 2565, 2565, 2565, 2565, 546: 2565, 548: 2565, 701: 2565, 2565, 704: 2565, 2565, 2565, 2565, 2565}, + {560: 3037, 799: 3866, 816: 5154}, + // 2300 + {9: 5156, 57: 5155}, + {2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 2563, 57: 2563, 530: 2563, 534: 2563, 2563, 2563, 2563, 2563, 546: 2563, 548: 2563, 701: 2563, 2563, 704: 2563, 2563, 2563, 2563, 2563}, + {560: 3037, 799: 3866, 816: 5157}, + {57: 5158}, + {2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 2562, 57: 2562, 530: 2562, 534: 2562, 2562, 2562, 2562, 2562, 546: 2562, 548: 2562, 701: 2562, 2562, 704: 2562, 2562, 2562, 2562, 2562}, + // 2305 + {2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 2566, 57: 2566, 530: 2566, 534: 2566, 2566, 2566, 2566, 2566, 546: 2566, 548: 2566, 701: 2566, 2566, 704: 2566, 2566, 2566, 2566, 2566}, + {2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 2561, 57: 2561, 530: 2561, 534: 2561, 2561, 2561, 2561, 2561, 546: 2561, 548: 2561, 701: 2561, 2561, 704: 2561, 2561, 2561, 2561, 2561}, + {2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 57: 2560, 530: 2560, 534: 2560, 2560, 2560, 2560, 2560, 546: 2560, 548: 2560, 701: 2560, 2560, 704: 2560, 2560, 2560, 2560, 2560}, + {2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 2559, 57: 2559, 530: 2559, 534: 2559, 2559, 2559, 2559, 2559, 546: 2559, 548: 2559, 701: 2559, 2559, 704: 2559, 2559, 2559, 2559, 2559}, + {2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 2567, 57: 2567, 530: 2567, 534: 2567, 2567, 2567, 2567, 2567, 546: 2567, 548: 2567, 701: 2567, 2567, 704: 2567, 2567, 2567, 2567, 2567}, + // 2310 + {2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 2558, 57: 2558, 530: 2558, 534: 2558, 2558, 2558, 2558, 2558, 546: 2558, 548: 2558, 701: 2558, 2558, 704: 2558, 2558, 2558, 2558, 2558}, + {2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557, 57: 2557, 530: 2557, 534: 2557, 2557, 2557, 2557, 2557, 546: 2557, 548: 2557, 701: 2557, 2557, 704: 2557, 2557, 2557, 2557, 2557}, + {2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 2556, 57: 2556, 530: 2556, 534: 2556, 2556, 2556, 2556, 2556, 546: 2556, 548: 2556, 701: 2556, 2556, 704: 2556, 2556, 2556, 2556, 2556}, + {2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 2568, 57: 2568, 530: 2568, 534: 2568, 2568, 2568, 2568, 2568, 546: 2568, 548: 2568, 701: 2568, 2568, 704: 2568, 2568, 2568, 2568, 2568}, + {531: 5169}, + // 2315 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5170}, + {57: 5171, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 2553, 57: 2553, 530: 2553, 534: 2553, 2553, 2553, 2553, 2553, 546: 2553, 548: 2553, 701: 2553, 2553, 704: 2553, 2553, 2553, 2553, 2553, 1471: 5174, 1498: 5173, 5172}, + {2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 2570, 57: 2570, 530: 2570, 534: 2570, 2570, 2570, 2570, 2570, 546: 2570, 548: 2570, 701: 2570, 2570, 704: 2570, 2570, 2570, 2570, 2570}, + {2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 2552, 57: 2552, 530: 2552, 534: 2552, 2552, 2552, 2552, 2552, 546: 2552, 548: 2552, 701: 2552, 2552, 704: 2552, 2552, 2552, 2552, 2552}, + // 2320 + {2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 2551, 57: 2551, 530: 2551, 534: 2551, 2551, 2551, 2551, 2551, 546: 2551, 548: 2551, 701: 2551, 2551, 704: 2551, 2551, 2551, 2551, 2551}, + {531: 5176}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5177}, + {57: 5178, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586, 57: 2586, 190: 4929, 530: 2586, 534: 3812, 2586, 3811, 2586, 2586, 546: 2586, 548: 2586, 701: 2586, 2586, 704: 2586, 2586, 2586, 2586, 2586, 915: 5179, 1044: 5180, 1168: 5181, 1354: 5182}, + // 2325 + {190: 4931, 548: 5183}, + {2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 2585, 57: 2585, 530: 2585, 534: 2585, 2585, 2585, 2585, 2585, 546: 2585, 548: 2585, 701: 2585, 2585, 704: 2585, 2585, 2585, 2585, 2585}, + {2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 2583, 57: 2583, 530: 2583, 534: 2583, 2583, 2583, 2583, 2583, 546: 2583, 548: 2583, 701: 2583, 2583, 704: 2583, 2583, 2583, 2583, 2583}, + {2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 2571, 57: 2571, 530: 2571, 534: 2571, 2571, 2571, 2571, 2571, 546: 2571, 548: 2571, 701: 2571, 2571, 704: 2571, 2571, 2571, 2571, 2571}, + {2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 2584, 57: 2584, 530: 2584, 534: 2584, 2584, 2584, 2584, 2584, 546: 2584, 548: 2584, 701: 2584, 2584, 704: 2584, 2584, 2584, 2584, 2584}, + // 2330 + {2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 2572, 57: 2572, 530: 2572, 534: 2572, 2572, 2572, 2572, 2572, 546: 2572, 548: 2572, 701: 2572, 2572, 704: 2572, 2572, 2572, 2572, 2572}, + {648: 5195, 5194, 5190, 5191, 653: 5192, 5193, 1036: 5189, 1059: 5187, 5188, 5186}, + {2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 2573, 57: 2573, 530: 2573, 534: 2573, 2573, 2573, 2573, 2573, 546: 2573, 548: 2573, 701: 2573, 2573, 704: 2573, 2573, 2573, 2573, 2573}, + {2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 2513, 57: 2513, 530: 2513, 534: 2513, 2513, 2513, 2513, 2513, 546: 2513, 548: 2513, 701: 2513, 2513, 704: 2513, 2513, 2513, 2513, 2513}, + {531: 5198}, + // 2335 + {531: 5196}, + {2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 2509, 57: 2509, 530: 2509, 2498, 534: 2509, 2509, 2509, 2509, 2509, 546: 2509, 548: 2509, 701: 2509, 2509, 704: 2509, 2509, 2509, 2509, 2509}, + {2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 2502, 57: 2502, 530: 2502, 2506, 534: 2502, 2502, 2502, 2502, 2502, 546: 2502, 548: 2502, 701: 2502, 2502, 704: 2502, 2502, 2502, 2502, 2502}, + {2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 2501, 57: 2501, 530: 2501, 2505, 534: 2501, 2501, 2501, 2501, 2501, 546: 2501, 548: 2501, 701: 2501, 2501, 704: 2501, 2501, 2501, 2501, 2501}, + {2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 57: 2500, 530: 2500, 2504, 534: 2500, 2500, 2500, 2500, 2500, 546: 2500, 548: 2500, 701: 2500, 2500, 704: 2500, 2500, 2500, 2500, 2500}, + // 2340 + {531: 2503}, + {531: 2499}, + {57: 5197}, + {2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 2510, 57: 2510, 530: 2510, 534: 2510, 2510, 2510, 2510, 2510, 546: 2510, 548: 2510, 701: 2510, 2510, 704: 2510, 2510, 2510, 2510, 2510}, + {57: 5199, 560: 3037, 799: 5200}, + // 2345 + {2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512, 57: 2512, 530: 2512, 534: 2512, 2512, 2512, 2512, 2512, 546: 2512, 548: 2512, 701: 2512, 2512, 704: 2512, 2512, 2512, 2512, 2512}, + {57: 5201}, + {2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 2511, 57: 2511, 530: 2511, 534: 2511, 2511, 2511, 2511, 2511, 546: 2511, 548: 2511, 701: 2511, 2511, 704: 2511, 2511, 2511, 2511, 2511}, + {186: 5203}, + {2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 2574, 57: 2574, 530: 2574, 534: 2574, 2574, 2574, 2574, 2574, 546: 2574, 548: 2574, 701: 2574, 2574, 704: 2574, 2574, 2574, 2574, 2574}, + // 2350 + {2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 2575, 57: 2575, 530: 2575, 534: 2575, 2575, 2575, 2575, 2575, 546: 2575, 548: 2575, 701: 2575, 2575, 704: 2575, 2575, 2575, 2575, 2575}, + {2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 2522, 57: 2522, 530: 2522, 534: 2522, 2522, 2522, 2522, 2522, 546: 2522, 548: 2522, 701: 2522, 2522, 704: 2522, 2522, 2522, 2522, 2522}, + {2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 2521, 57: 2521, 530: 2521, 534: 2521, 2521, 2521, 2521, 2521, 546: 2521, 548: 2521, 701: 2521, 2521, 704: 2521, 2521, 2521, 2521, 2521}, + {2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 2520, 57: 2520, 530: 2520, 534: 2520, 2520, 2520, 2520, 2520, 546: 2520, 548: 2520, 701: 2520, 2520, 704: 2520, 2520, 2520, 2520, 2520}, + {2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 2519, 57: 2519, 530: 2519, 534: 2519, 2519, 2519, 2519, 2519, 546: 2519, 548: 2519, 701: 2519, 2519, 704: 2519, 2519, 2519, 2519, 2519}, + // 2355 + {56: 5210, 531: 5209, 648: 5195, 5194, 5190, 5191, 653: 5192, 5193, 999: 5218, 1036: 5189, 1059: 5187, 5188, 5211, 1214: 5219}, + {531: 5214}, + {2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 2514, 57: 2514, 530: 2514, 534: 2514, 2514, 2514, 2514, 2514, 546: 2514, 548: 2514, 701: 2514, 2514, 704: 2514, 2514, 2514, 2514, 2514}, + {186: 4576}, + {531: 4573}, + // 2360 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 5215, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 5216}, + {2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 2517, 57: 2517, 530: 2517, 534: 2517, 2517, 2517, 2517, 2517, 546: 2517, 548: 2517, 701: 2517, 2517, 704: 2517, 2517, 2517, 2517, 2517}, + {9: 3990, 57: 5217}, + {2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 2516, 57: 2516, 530: 2516, 534: 2516, 2516, 2516, 2516, 2516, 546: 2516, 548: 2516, 701: 2516, 2516, 704: 2516, 2516, 2516, 2516, 2516}, + {57: 5221}, + // 2365 + {57: 5220}, + {2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 2515, 57: 2515, 530: 2515, 534: 2515, 2515, 2515, 2515, 2515, 546: 2515, 548: 2515, 701: 2515, 2515, 704: 2515, 2515, 2515, 2515, 2515}, + {2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 2518, 57: 2518, 530: 2518, 534: 2518, 2518, 2518, 2518, 2518, 546: 2518, 548: 2518, 701: 2518, 2518, 704: 2518, 2518, 2518, 2518, 2518}, + {2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 2576, 57: 2576, 530: 2576, 534: 2576, 2576, 2576, 2576, 2576, 546: 2576, 548: 2576, 701: 2576, 2576, 704: 2576, 2576, 2576, 2576, 2576}, + {2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 2579, 57: 2579, 105: 5224, 107: 5225, 530: 2579, 534: 2579, 2579, 2579, 2579, 2579, 546: 2579, 548: 2579, 701: 2579, 2579, 704: 2579, 2579, 2579, 2579, 2579, 971: 5226}, + // 2370 + {2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 2711, 19: 2711, 57: 2711, 102: 2711, 104: 2711, 2711, 2711, 2711, 109: 2711, 530: 2711, 532: 2711, 534: 2711, 2711, 2711, 2711, 2711, 542: 2711, 546: 2711, 548: 2711, 562: 2711, 701: 2711, 2711, 704: 2711, 2711, 2711, 2711, 2711}, + {2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 2710, 19: 2710, 57: 2710, 102: 2710, 104: 2710, 2710, 2710, 2710, 109: 2710, 530: 2710, 532: 2710, 534: 2710, 2710, 2710, 2710, 2710, 542: 2710, 546: 2710, 548: 2710, 562: 2710, 701: 2710, 2710, 704: 2710, 2710, 2710, 2710, 2710}, + {2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578, 57: 2578, 530: 2578, 534: 2578, 2578, 2578, 2578, 2578, 546: 2578, 548: 2578, 701: 2578, 2578, 704: 2578, 2578, 2578, 2578, 2578}, + {2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 2582, 57: 2582, 530: 2582, 534: 2582, 2582, 2582, 2582, 2582, 546: 2582, 548: 2582, 701: 2582, 2582, 704: 2582, 2582, 2582, 2582, 2582}, + {701: 2683, 2683, 704: 2683, 2683, 710: 2683, 746: 2683, 2683}, + // 2375 + {2682, 2682, 2682, 2682, 2682, 2682, 9: 2682, 546: 2682, 701: 2682, 2682, 704: 2682, 2682, 710: 2682, 746: 2682, 2682}, + {2615, 2615, 2615, 2615, 2615, 2615, 9: 2615, 2615, 2615, 57: 2615, 546: 2615}, + {2741, 2741, 2741, 2741, 2741, 2741, 9: 2741, 546: 2741}, + {2693, 2693, 2693, 2693, 2693, 2693, 9: 2693, 546: 2693}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5234}, + // 2380 + {2692, 2692, 2692, 2692, 2692, 2692, 9: 2692, 546: 2692}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5236}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 5237}, + {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5238}, + {2742, 2742, 2742, 2742, 2742, 2742, 9: 2742, 546: 2742}, + // 2385 + {2743, 2743, 2743, 2743, 2743, 2743, 9: 2743, 546: 2743}, + {2744, 2744, 2744, 2744, 2744, 2744, 9: 2744, 546: 2744}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5244, 1104: 5243, 1313: 5242}, + {2745, 2745, 2745, 2745, 2745, 2745, 9: 5246, 546: 2745}, + {1512, 1512, 1512, 1512, 1512, 1512, 9: 1512, 546: 1512}, + // 2390 + {1502, 1502, 1502, 1502, 1502, 1502, 9: 1502, 546: 1502, 577: 5119, 605: 5118, 1064: 5245}, + {1510, 1510, 1510, 1510, 1510, 1510, 9: 1510, 546: 1510}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5244, 1104: 5247}, + {1511, 1511, 1511, 1511, 1511, 1511, 9: 1511, 546: 1511}, + {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5249}, + // 2395 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5252}, + {761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 58: 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 761, 531: 761, 546: 761, 560: 761, 586: 761, 608: 761, 715: 761}, + {760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 58: 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 760, 531: 760, 546: 760, 560: 760, 586: 760, 608: 760, 715: 760}, + {2748, 2748, 2748, 2748, 2748, 2748, 9: 2748, 546: 2748}, + {2717, 2717, 2717, 2717, 2717, 2717, 9: 2717, 20: 2717, 546: 2717}, + // 2400 + {2716, 2716, 2716, 2716, 2716, 2716, 9: 5256, 20: 2716, 546: 2716}, + {2687, 2687, 2687, 2687, 2687, 2687, 9: 2687, 20: 2687, 57: 2687, 131: 2687, 200: 2687, 215: 2687, 532: 2687, 546: 2687, 559: 2687, 710: 2687, 715: 2687}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5257, 3051, 3052, 3050}, + {2686, 2686, 2686, 2686, 2686, 2686, 9: 2686, 20: 2686, 57: 2686, 131: 2686, 200: 2686, 215: 2686, 532: 2686, 546: 2686, 559: 2686, 710: 2686, 715: 2686}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5260}, + // 2405 + {2749, 2749, 2749, 2749, 2749, 2749, 9: 2749, 546: 2749}, + {20: 5261}, + {2751, 2751, 2751, 2751, 2751, 2751, 9: 2751, 546: 2751}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5264}, + {2750, 2750, 2750, 2750, 2750, 2750, 9: 2750, 546: 2750}, + // 2410 + {20: 5265}, + {2752, 2752, 2752, 2752, 2752, 2752, 9: 2752, 546: 2752}, + {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5267}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5268}, + {2753, 2753, 2753, 2753, 2753, 2753, 9: 2753, 546: 2753}, + // 2415 + {2: 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 715: 762, 908: 5250, 924: 5270}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5271}, + {2754, 2754, 2754, 2754, 2754, 2754, 9: 2754, 546: 2754}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5273}, + {2755, 2755, 2755, 2755, 2755, 2755, 9: 2755, 546: 2755}, + // 2420 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5275, 3051, 3052, 3050}, + {532: 5276}, + {608: 5277}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5278}, + {2715, 2715, 2715, 2715, 2715, 2715, 9: 2715, 276: 5282, 532: 5281, 546: 2715, 1511: 5280, 5279}, + // 2425 + {2756, 2756, 2756, 2756, 2756, 2756, 9: 2756, 546: 2756}, + {2714, 2714, 2714, 2714, 2714, 2714, 9: 2714, 546: 2714}, + {247: 5284}, + {247: 5283}, + {2712, 2712, 2712, 2712, 2712, 2712, 9: 2712, 546: 2712}, + // 2430 + {2713, 2713, 2713, 2713, 2713, 2713, 9: 2713, 546: 2713}, + {192: 5286}, + {199: 5287}, + {531: 5288}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5289}, + // 2435 + {57: 5290, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2119, 2119, 2119, 2119, 2119, 2119, 9: 2119, 546: 2119, 578: 4948, 854: 5291}, + {2758, 2758, 2758, 2758, 2758, 2758, 9: 2758, 546: 2758}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5310}, + {701: 5309}, + // 2440 + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5307}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5305}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5303}, + {701: 5300}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5299, 3051, 3052, 3050}, + // 2445 + {2723, 2723, 2723, 2723, 2723, 2723, 9: 2723, 546: 2723}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 5301}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5229, 3051, 3052, 3050, 1290: 5302}, + {2746, 2746, 2746, 2746, 2746, 2746, 9: 2746, 546: 2746}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5304, 3051, 3052, 3050}, + // 2450 + {2747, 2747, 2747, 2747, 2747, 2747, 9: 2747, 546: 2747}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5306, 3051, 3052, 3050}, + {2757, 2757, 2757, 2757, 2757, 2757, 9: 2757, 546: 2757}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5308}, + {2759, 2759, 2759, 2759, 2759, 2759, 9: 5256, 546: 2759}, + // 2455 + {2760, 2760, 2760, 2760, 2760, 2760, 9: 2760, 546: 2760}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5311}, + {2321, 2321, 2321, 2321, 2321, 2321, 9: 2321, 546: 2321, 728: 5314, 730: 5313, 1012: 5312}, + {2761, 2761, 2761, 2761, 2761, 2761, 9: 2761, 546: 2761}, + {2320, 2320, 2320, 2320, 2320, 2320, 9: 2320, 546: 2320}, + // 2460 + {2319, 2319, 2319, 2319, 2319, 2319, 9: 2319, 546: 2319}, + {173: 5251, 560: 762, 908: 5250, 924: 5316}, + {560: 3037, 799: 5317}, + {2762, 2762, 2762, 2762, 2762, 2762, 9: 2762, 546: 2762}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 715: 5253, 770: 5255, 3051, 3052, 3050, 859: 5254, 927: 5319}, + // 2465 + {2763, 2763, 2763, 2763, 2763, 2763, 9: 2763, 546: 2763}, + {192: 5321}, + {199: 5322}, + {531: 5323}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5324}, + // 2470 + {57: 5325, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {762, 762, 762, 762, 762, 762, 9: 762, 173: 5251, 546: 762, 908: 5250, 924: 5326}, + {2767, 2767, 2767, 2767, 2767, 2767, 9: 2767, 546: 2767}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 578: 5345, 883: 5465}, + {2770, 2770, 2770, 2770, 2770, 2770, 9: 2770, 546: 2770}, + // 2475 + {2117, 2117, 2117, 2117, 2117, 2117, 9: 2117, 116: 2117, 173: 2117, 531: 2117, 546: 2117, 578: 5345, 883: 5414, 908: 2117}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 5405}, + {701: 4906, 5337, 704: 5332, 5335, 710: 4907, 746: 5336, 5333, 923: 5334, 1340: 5338}, + {701: 5399}, + {2: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 10: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 58: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 531: 2698, 701: 4906, 710: 4907, 923: 5354, 1197: 5393}, + // 2480 + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 542: 2117, 578: 5345, 883: 5387}, + {2: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 10: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 58: 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 2698, 531: 2698, 542: 2698, 701: 4906, 710: 4907, 923: 5354, 1197: 5355}, + {701: 5343}, + {531: 5339}, + {626, 626, 626, 626, 626, 626, 9: 626, 57: 626, 546: 626}, + // 2485 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5340}, + {57: 5341, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2586, 2586, 2586, 2586, 2586, 2586, 9: 2586, 57: 2586, 190: 4929, 534: 3812, 536: 3811, 546: 2586, 915: 4930, 1044: 5180, 1168: 5342}, + {2541, 2541, 2541, 2541, 2541, 2541, 9: 2541, 57: 2541, 546: 2541}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 531: 2117, 578: 5345, 883: 5344}, + // 2490 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 770: 5349, 3051, 3052, 3050, 981: 5348}, + {534: 3812, 536: 3811, 915: 5346}, + {645: 5347}, + {2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 58: 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 2116, 531: 2116, 533: 2116, 535: 2116, 542: 2116, 546: 2116, 631: 2116, 908: 2116}, + {531: 5350}, + // 2495 + {531: 2112}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5351}, + {9: 5123, 57: 5352}, + {707: 5107, 1011: 5353}, + {2542, 2542, 2542, 2542, 2542, 2542, 9: 2542, 57: 2542, 546: 2542}, + // 2500 + {2: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 10: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 58: 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 2697, 531: 2697, 542: 2697}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5356}, + {531: 5367}, + {104: 5365, 531: 2112, 542: 2112}, + {531: 2103, 542: 5359}, + // 2505 + {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5360}, + {531: 2102}, + {2096, 2096, 2096, 2096, 2096, 2096, 2096, 9: 2096, 19: 2096, 57: 2096, 102: 2096, 104: 2096, 2096, 2096, 2096, 109: 2096, 530: 2096, 2096, 2096, 542: 2096, 546: 2096, 562: 2096}, + {2095, 2095, 2095, 2095, 2095, 2095, 2095, 9: 2095, 19: 2095, 57: 2095, 102: 2095, 104: 2095, 2095, 2095, 2095, 109: 2095, 530: 2095, 2095, 2095, 542: 2095, 546: 2095, 562: 2095}, + {2094, 2094, 2094, 2094, 2094, 2094, 2094, 9: 2094, 19: 2094, 57: 2094, 102: 2094, 104: 2094, 2094, 2094, 2094, 109: 2094, 530: 2094, 2094, 2094, 542: 2094, 546: 2094, 562: 2094}, + // 2510 + {2093, 2093, 2093, 2093, 2093, 2093, 2093, 9: 2093, 19: 2093, 57: 2093, 102: 2093, 104: 2093, 2093, 2093, 2093, 109: 2093, 530: 2093, 2093, 2093, 542: 2093, 546: 2093, 562: 2093}, + {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5366}, + {531: 2101}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5368}, + {9: 5123, 57: 5369}, + // 2515 + {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5370}, + {2543, 2543, 2543, 2543, 2543, 2543, 5375, 9: 2543, 19: 5372, 57: 2543, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2543, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, + {2110, 2110, 2110, 2110, 2110, 2110, 2110, 9: 2110, 19: 2110, 57: 2110, 102: 2110, 104: 2110, 2110, 2110, 2110, 109: 2110, 532: 2110, 542: 2110, 546: 2110, 562: 2110}, + {555: 4588, 560: 2316, 802: 5385}, + {2108, 2108, 2108, 2108, 2108, 2108, 2108, 9: 2108, 19: 2108, 57: 2108, 102: 2108, 104: 2108, 2108, 2108, 2108, 109: 2108, 532: 2108, 542: 2108, 546: 2108, 562: 2108}, + // 2520 + {415: 5383}, + {533: 5382}, + {2105, 2105, 2105, 2105, 2105, 2105, 2105, 9: 2105, 19: 2105, 57: 2105, 102: 2105, 104: 2105, 2105, 2105, 2105, 109: 2105, 532: 2105, 542: 2105, 546: 2105, 562: 2105}, + {2104, 2104, 2104, 2104, 2104, 2104, 2104, 9: 2104, 19: 2104, 57: 2104, 102: 2104, 104: 2104, 2104, 2104, 2104, 109: 2104, 532: 2104, 542: 2104, 546: 2104, 562: 2104}, + {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5381}, + // 2525 + {182: 5362, 207: 5364, 228: 5361, 242: 5363, 1004: 5380}, + {2097, 2097, 2097, 2097, 2097, 2097, 2097, 9: 2097, 19: 2097, 57: 2097, 102: 2097, 104: 2097, 2097, 2097, 2097, 109: 2097, 530: 2097, 532: 2097, 542: 2097, 546: 2097, 562: 2097}, + {2098, 2098, 2098, 2098, 2098, 2098, 2098, 9: 2098, 19: 2098, 57: 2098, 102: 2098, 104: 2098, 2098, 2098, 2098, 109: 2098, 530: 2098, 532: 2098, 542: 2098, 546: 2098, 562: 2098}, + {2106, 2106, 2106, 2106, 2106, 2106, 2106, 9: 2106, 19: 2106, 57: 2106, 102: 2106, 104: 2106, 2106, 2106, 2106, 109: 2106, 532: 2106, 542: 2106, 546: 2106, 562: 2106}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5384, 3051, 3052, 3050}, + // 2530 + {2107, 2107, 2107, 2107, 2107, 2107, 2107, 9: 2107, 19: 2107, 57: 2107, 102: 2107, 104: 2107, 2107, 2107, 2107, 109: 2107, 532: 2107, 542: 2107, 546: 2107, 562: 2107}, + {560: 3037, 799: 3866, 816: 5386}, + {2109, 2109, 2109, 2109, 2109, 2109, 2109, 9: 2109, 19: 2109, 57: 2109, 102: 2109, 104: 2109, 2109, 2109, 2109, 109: 2109, 532: 2109, 542: 2109, 546: 2109, 562: 2109}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5388}, + {531: 5389}, + // 2535 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5390}, + {9: 5123, 57: 5391}, + {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5392}, + {2544, 2544, 2544, 2544, 2544, 2544, 5375, 9: 2544, 19: 5372, 57: 2544, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2544, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 770: 5349, 3051, 3052, 3050, 981: 5394}, + // 2540 + {531: 5395}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5396}, + {9: 5123, 57: 5397}, + {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5398}, + {2545, 2545, 2545, 2545, 2545, 2545, 5375, 9: 2545, 19: 5372, 57: 2545, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2545, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, + // 2545 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2113, 542: 2113, 770: 5357, 3051, 3052, 3050, 981: 5358, 1053: 5400}, + {531: 5401}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 5402}, + {9: 5123, 57: 5403}, + {2111, 2111, 2111, 2111, 2111, 2111, 2111, 9: 2111, 19: 2111, 57: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 546: 2111, 983: 5404}, + // 2550 + {2546, 2546, 2546, 2546, 2546, 2546, 5375, 9: 2546, 19: 5372, 57: 2546, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 546: 2546, 959: 5376, 961: 5373, 971: 5377, 982: 5371}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5406, 3051, 3052, 3050}, + {286: 5408, 294: 5410, 297: 5409, 1287: 5407}, + {531: 5411}, + {57: 2491, 531: 2491}, + // 2555 + {57: 2490, 531: 2490}, + {57: 2489, 531: 2489}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5412}, + {9: 4027, 57: 5413}, + {2766, 2766, 2766, 2766, 2766, 2766, 9: 2766, 546: 2766}, + // 2560 + {762, 762, 762, 762, 762, 762, 9: 762, 116: 762, 173: 5251, 531: 762, 546: 762, 908: 5250, 924: 5415}, + {2412, 2412, 2412, 2412, 2412, 2412, 9: 2412, 116: 5417, 531: 5418, 546: 2412, 1231: 5416}, + {2769, 2769, 2769, 2769, 2769, 2769, 9: 2769, 546: 2769}, + {560: 3037, 799: 5464}, + {546: 5421, 1067: 5420, 1230: 5419}, + // 2565 + {9: 5462, 57: 5461}, + {9: 2410, 57: 2410}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5422, 3051, 3052, 3050}, + {6: 2389, 2389, 9: 2389, 18: 2389, 20: 2389, 22: 2389, 2389, 2389, 2389, 2389, 2389, 57: 2389, 189: 5427, 262: 5426, 531: 2389, 535: 5425, 547: 5424, 710: 2389, 1414: 5423}, + {6: 2402, 2402, 9: 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 57: 2402, 531: 2402, 710: 2402, 1066: 5448}, + // 2570 + {192: 5428, 606: 5429}, + {6: 2386, 2386, 9: 2386, 18: 2386, 20: 2386, 22: 2386, 2386, 2386, 2386, 2386, 2386, 57: 2386, 531: 2386, 710: 2386}, + {6: 2384, 2384, 9: 2384, 18: 2384, 20: 2384, 22: 2384, 2384, 2384, 2384, 2384, 2384, 57: 2384, 531: 2384, 710: 2384}, + {6: 2383, 2383, 9: 2383, 18: 2383, 20: 2383, 22: 2383, 2383, 2383, 2383, 2383, 2383, 57: 2383, 531: 2383, 710: 2383}, + {199: 5438}, + // 2575 + {531: 5430}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 5432, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5433, 1147: 5434, 1348: 5431}, + {9: 5436, 57: 5435}, + {9: 2203, 57: 2203, 531: 3900}, + {9: 2202, 57: 2202, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + // 2580 + {9: 2186, 57: 2186}, + {6: 2385, 2385, 9: 2385, 18: 2385, 20: 2385, 22: 2385, 2385, 2385, 2385, 2385, 2385, 57: 2385, 531: 2385, 710: 2385}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 5432, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5433, 1147: 5437}, + {9: 2185, 57: 2185}, + {531: 5440, 720: 5439}, + // 2585 + {6: 2388, 2388, 9: 2388, 18: 2388, 20: 2388, 22: 2388, 2388, 2388, 2388, 2388, 2388, 57: 2388, 531: 2388, 710: 2388}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 720: 5442, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5443, 1212: 5444, 1395: 5441}, + {9: 5446, 57: 5445}, + {9: 2201, 57: 2201}, + {9: 2200, 57: 2200, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + // 2590 + {9: 2188, 57: 2188}, + {6: 2387, 2387, 9: 2387, 18: 2387, 20: 2387, 22: 2387, 2387, 2387, 2387, 2387, 2387, 57: 2387, 531: 2387, 710: 2387}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 720: 5442, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5443, 1212: 5447}, + {9: 2187, 57: 2187}, + {6: 4725, 5452, 9: 2407, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 57: 2407, 531: 5450, 710: 4730, 865: 4735, 910: 5451, 1475: 5449}, + // 2595 + {9: 2408, 57: 2408}, + {115: 5455, 1288: 5454, 1474: 5453}, + {2401, 2401, 6: 2401, 2401, 9: 2401, 18: 2401, 20: 2401, 22: 2401, 2401, 2401, 2401, 2401, 2401, 57: 2401, 531: 2401, 710: 2401}, + {23: 4877}, + {9: 5459, 57: 5458}, + // 2600 + {9: 2405, 57: 2405}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5456, 3051, 3052, 3050}, + {6: 2402, 2402, 9: 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 57: 2402, 710: 2402, 1066: 5457}, + {6: 4725, 5452, 9: 2403, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 57: 2403, 710: 4730, 865: 4735, 910: 5451}, + {9: 2406, 57: 2406}, + // 2605 + {115: 5455, 1288: 5460}, + {9: 2404, 57: 2404}, + {2411, 2411, 2411, 2411, 2411, 2411, 9: 2411, 530: 2411, 2411, 2411, 537: 2411, 546: 2411, 2411, 549: 2411, 552: 2411, 608: 2411, 695: 2411}, + {546: 5421, 1067: 5463}, + {9: 2409, 57: 2409}, + // 2610 + {2768, 2768, 2768, 2768, 2768, 2768, 9: 2768, 546: 2768}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5467, 770: 4024, 3051, 3052, 3050, 821: 4952, 953: 5466}, + {2694, 2694, 2694, 2694, 2694, 2694, 9: 2694, 5232, 5233, 546: 2694, 1033: 5475}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5471, 1291: 5468}, + {9: 5473, 57: 5472}, + // 2615 + {9: 623, 57: 623}, + {9: 622, 57: 622}, + {9: 621, 57: 621}, + {2771, 2771, 2771, 2771, 2771, 2771, 9: 2771, 546: 2771}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5474}, + // 2620 + {9: 620, 57: 620}, + {2772, 2772, 2772, 2772, 2772, 2772, 9: 2772, 546: 2772}, + {16: 4418, 553: 4419, 709: 4417, 853: 5477}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 5479, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 5478}, + {444, 444, 444, 444, 444, 444, 9: 444, 538: 5481, 546: 444, 1221: 5483}, + // 2625 + {444, 444, 444, 444, 444, 444, 9: 444, 538: 5481, 546: 444, 1221: 5480}, + {2773, 2773, 2773, 2773, 2773, 2773, 9: 2773, 546: 2773}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 5482}, + {443, 443, 443, 443, 443, 443, 9: 443, 546: 443}, + {2774, 2774, 2774, 2774, 2774, 2774, 9: 2774, 546: 2774}, + // 2630 + {223: 5496}, + {200: 5486}, + {223: 5487}, + {560: 3037, 799: 3866, 816: 5488}, + {2779, 2779, 2779, 2779, 2779, 2779, 9: 2779, 220: 5489, 546: 2779, 1057: 5490}, + // 2635 + {319: 5491}, + {2775, 2775, 2775, 2775, 2775, 2775, 9: 2775, 546: 2775}, + {533: 5493, 1472: 5492}, + {2778, 2778, 2778, 2778, 2778, 2778, 9: 5494, 16: 2778, 18: 2778, 21: 2778, 535: 2778, 538: 2778, 546: 2778, 553: 2778, 557: 2778, 709: 2778}, + {442, 442, 442, 442, 442, 442, 9: 442, 16: 442, 18: 442, 21: 442, 535: 442, 538: 442, 546: 442, 553: 442, 557: 442, 709: 442}, + // 2640 + {533: 5495}, + {441, 441, 441, 441, 441, 441, 9: 441, 16: 441, 18: 441, 21: 441, 535: 441, 538: 441, 546: 441, 553: 441, 557: 441, 709: 441}, + {560: 3037, 799: 3866, 816: 5497}, + {2779, 2779, 2779, 2779, 2779, 2779, 9: 2779, 220: 5489, 546: 2779, 1057: 5498}, + {2776, 2776, 2776, 2776, 2776, 2776, 9: 2776, 546: 2776}, + // 2645 + {8: 579, 29: 579}, + {573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 15: 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 573, 530: 573, 573, 573, 535: 573, 537: 573, 573, 573, 546: 573, 573, 549: 573, 552: 573, 573, 565: 573, 608: 573, 695: 573, 709: 573, 573}, + {6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5502}, + {572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 15: 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 572, 530: 572, 572, 572, 535: 572, 537: 572, 572, 572, 546: 572, 572, 549: 572, 552: 572, 572, 565: 572, 608: 572, 695: 572, 709: 572, 572}, + {533: 5505, 535: 5504}, + // 2650 + {2789, 2789, 2789, 2789, 2789, 2789, 9: 2789, 546: 2789}, + {2788, 2788, 2788, 2788, 2788, 2788, 9: 2788, 546: 2788}, + {533: 5508, 535: 5507}, + {2791, 2791, 2791, 2791, 2791, 2791, 9: 2791, 546: 2791}, + {2790, 2790, 2790, 2790, 2790, 2790, 9: 2790, 546: 2790}, + // 2655 + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 535: 2316, 555: 4588, 557: 5511, 802: 5510}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5513, 535: 5515, 770: 5516, 3051, 3052, 3050, 989: 5514}, + {535: 5512}, + {2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 15: 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 2792, 57: 2792, 530: 2792, 2792, 2792, 535: 2792, 537: 2792, 2792, 2792, 546: 2792, 2792, 549: 2792, 552: 2792, 2792, 557: 2792, 565: 2792, 608: 2792, 695: 2792, 709: 2792, 2792}, + {2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 15: 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 2795, 57: 2795, 530: 2795, 2795, 2795, 535: 2795, 537: 2795, 2795, 2795, 546: 2795, 2795, 549: 2795, 552: 2795, 2795, 557: 2795, 565: 2795, 608: 2795, 695: 2795, 709: 2795, 2795}, + // 2660 + {2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 15: 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 2794, 57: 2794, 530: 2794, 2794, 2794, 535: 2794, 537: 2794, 2794, 2794, 546: 2794, 2794, 549: 2794, 552: 2794, 2794, 557: 2794, 565: 2794, 608: 2794, 695: 2794, 709: 2794, 2794}, + {2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 15: 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 2793, 57: 2793, 530: 2793, 2793, 2793, 535: 2793, 537: 2793, 2793, 2793, 546: 2793, 2793, 549: 2793, 552: 2793, 2793, 557: 2793, 565: 2793, 608: 2793, 695: 2793, 709: 2793, 2793}, + {2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 15: 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 57: 2464, 108: 2464, 119: 2464, 2464, 2464, 2464, 2464, 2464, 2464, 2464, 128: 2464, 2464, 2464, 530: 2464, 2464, 2464, 535: 2464, 537: 2464, 2464, 2464, 546: 2464, 2464, 549: 2464, 552: 2464, 2464, 557: 2464, 565: 2464, 608: 2464, 695: 2464, 709: 2464, 2464}, + {223: 5522}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5519}, + // 2665 + {2847, 2847, 9: 5256, 200: 5520}, + {223: 5521}, + {2846, 2846}, + {2848, 2848}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5524}, + // 2670 + {2642, 2642, 9: 5256, 532: 5527, 710: 5526, 902: 5525}, + {2851, 2851}, + {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 5542}, + {560: 5532, 636: 4089, 4088, 799: 5530, 916: 5531, 1112: 5529, 1318: 5528}, + {2641, 2641, 9: 5540}, + // 2675 + {2640, 2640, 9: 2640}, + {283: 5534, 289: 5536, 337: 5537, 356: 5535}, + {243: 5533}, + {243: 2494, 283: 2224, 289: 2224, 337: 2224, 356: 2224}, + {2633, 2633, 9: 2633}, + // 2680 + {2638, 2638, 9: 2638}, + {2637, 2637, 9: 2637}, + {382: 5538, 457: 5539}, + {2634, 2634, 9: 2634}, + {2636, 2636, 9: 2636}, + // 2685 + {2635, 2635, 9: 2635}, + {560: 5532, 636: 4089, 4088, 799: 5530, 916: 5531, 1112: 5541}, + {2639, 2639, 9: 2639}, + {2642, 2642, 9: 5546, 532: 5527, 902: 5545}, + {1106, 1106, 9: 1106, 57: 1106, 532: 1106}, + // 2690 + {1104, 1104, 9: 1104, 57: 1104, 532: 1104}, + {2850, 2850}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 704: 5548, 770: 5547, 3051, 3052, 3050}, + {1105, 1105, 9: 1105, 57: 1105, 532: 1105}, + {1103, 1103, 9: 1103, 57: 1103, 532: 1103}, + // 2695 + {2852, 2852}, + {2787, 2787}, + {32: 5662, 417: 5661}, + {546: 5653}, + {720: 5646}, + // 2700 + {10: 5639}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 723: 5557, 770: 5556, 3051, 3052, 3050}, + {2402, 2402, 6: 2402, 2402, 18: 2402, 20: 2402, 22: 2402, 2402, 2402, 2402, 2402, 2402, 249: 4682, 710: 2402, 1028: 5637, 1066: 5638}, + {182: 2420, 404: 5562, 445: 5563, 591: 5561, 701: 2420, 1202: 5564, 5559, 1289: 5560, 1416: 5558}, + {2414, 2414, 115: 2414, 5627, 530: 2414, 2414, 2414, 537: 2414, 547: 2414, 549: 2414, 552: 2414, 608: 2414, 695: 2414, 1417: 5626}, + // 2705 + {182: 5614, 701: 5613}, + {2438, 2438, 115: 2438, 2438, 530: 2438, 2438, 2438, 537: 2438, 547: 2438, 549: 2438, 552: 2438, 608: 2438, 695: 2438}, + {131: 3950, 154: 3949, 531: 5577, 930: 5578}, + {131: 3950, 154: 3949, 531: 5570, 930: 5571}, + {2431, 2431, 115: 2431, 2431, 530: 2431, 2431, 2431, 537: 2431, 547: 2431, 549: 2431, 552: 2431, 556: 5566, 608: 2431, 641: 5565, 695: 2431}, + // 2710 + {182: 2419, 701: 2419}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5568}, + {560: 3037, 799: 3866, 816: 5567}, + {2432, 2432, 115: 2432, 2432, 530: 2432, 2432, 2432, 537: 2432, 547: 2432, 549: 2432, 552: 2432, 608: 2432, 695: 2432}, + {118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 566: 3745, 3743, 3744, 3742, 3740, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 800: 3741, 3739, 886: 3747, 901: 5569}, + // 2715 + {2433, 2433, 115: 2433, 2433, 530: 2433, 2433, 2433, 537: 2433, 547: 2433, 549: 2433, 552: 2433, 608: 2433, 695: 2433}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5575}, + {531: 5572}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5573}, + {9: 4027, 57: 5574}, + // 2720 + {2434, 2434, 115: 2434, 2434, 530: 2434, 2434, 2434, 537: 2434, 547: 2434, 549: 2434, 552: 2434, 608: 2434, 695: 2434}, + {57: 5576, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2435, 2435, 115: 2435, 2435, 530: 2435, 2435, 2435, 537: 2435, 547: 2435, 549: 2435, 552: 2435, 608: 2435, 695: 2435}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5610}, + {531: 5579}, + // 2725 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5580}, + {9: 4027, 57: 5581}, + {2430, 2430, 115: 2430, 2430, 530: 2430, 2430, 2430, 537: 2430, 547: 2430, 549: 2430, 552: 2430, 608: 2430, 641: 5583, 695: 2430, 1232: 5582}, + {2436, 2436, 115: 2436, 2436, 530: 2436, 2436, 2436, 537: 2436, 547: 2436, 549: 2436, 552: 2436, 608: 2436, 695: 2436}, + {531: 5584}, + // 2730 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5586, 1379: 5585}, + {57: 5588}, + {57: 2428, 118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 543: 3782, 3783, 3788, 584: 3784, 594: 3757, 3754, 3756, 3755, 3751, 3753, 3752, 3749, 3750, 3748, 3758, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781, 886: 3747, 901: 5587}, + {57: 2427}, + {2422, 2422, 10: 5590, 115: 2422, 2422, 530: 2422, 2422, 2422, 537: 2422, 547: 2422, 2422, 2422, 552: 2422, 608: 2422, 695: 2422, 720: 2422, 1362: 5589}, + // 2735 + {2426, 2426, 115: 2426, 2426, 530: 2426, 2426, 2426, 537: 2426, 547: 2426, 5605, 2426, 552: 2426, 608: 2426, 695: 2426, 720: 2426, 1396: 5604}, + {546: 5591}, + {192: 5592}, + {199: 5593}, + {531: 5594}, + // 2740 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5595}, + {57: 5596, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {234: 5597}, + {546: 5598}, + {192: 5599}, + // 2745 + {199: 5600}, + {531: 5601}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5602}, + {57: 5603, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2421, 2421, 115: 2421, 2421, 530: 2421, 2421, 2421, 537: 2421, 547: 2421, 2421, 2421, 552: 2421, 608: 2421, 695: 2421, 720: 2421}, + // 2750 + {2424, 2424, 115: 2424, 2424, 530: 2424, 2424, 2424, 537: 2424, 547: 2424, 549: 2424, 552: 2424, 608: 2424, 695: 2424, 720: 5608, 1394: 5607}, + {546: 5606}, + {2425, 2425, 115: 2425, 2425, 530: 2425, 2425, 2425, 537: 2425, 547: 2425, 549: 2425, 552: 2425, 608: 2425, 695: 2425, 720: 2425}, + {2429, 2429, 115: 2429, 2429, 530: 2429, 2429, 2429, 537: 2429, 547: 2429, 549: 2429, 552: 2429, 608: 2429, 695: 2429}, + {546: 5609}, + // 2755 + {2423, 2423, 115: 2423, 2423, 530: 2423, 2423, 2423, 537: 2423, 547: 2423, 549: 2423, 552: 2423, 608: 2423, 695: 2423}, + {57: 5611, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2430, 2430, 115: 2430, 2430, 530: 2430, 2430, 2430, 537: 2430, 547: 2430, 549: 2430, 552: 2430, 608: 2430, 641: 5583, 695: 2430, 1232: 5612}, + {2437, 2437, 115: 2437, 2437, 530: 2437, 2437, 2437, 537: 2437, 547: 2437, 549: 2437, 552: 2437, 608: 2437, 695: 2437}, + {102: 5619, 531: 2440, 1415: 5618}, + // 2760 + {531: 5615}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5616}, + {57: 5617, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2441, 2441, 115: 2441, 2441, 274: 2441, 530: 2441, 2441, 2441, 537: 2441, 547: 2441, 549: 2441, 552: 2441, 608: 2441, 695: 2441}, + {531: 5622}, + // 2765 + {555: 5620}, + {560: 3037, 799: 5621}, + {531: 2439}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2608, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5623, 1123: 5624}, + {9: 4027, 57: 2607}, + // 2770 + {57: 5625}, + {2442, 2442, 115: 2442, 2442, 274: 2442, 530: 2442, 2442, 2442, 537: 2442, 547: 2442, 549: 2442, 552: 2442, 608: 2442, 695: 2442}, + {2418, 2418, 115: 5630, 530: 2418, 2418, 2418, 537: 2418, 547: 2418, 549: 2418, 552: 2418, 608: 2418, 695: 2418, 1477: 5629}, + {560: 3037, 799: 3866, 816: 5628}, + {2413, 2413, 115: 2413, 530: 2413, 2413, 2413, 537: 2413, 547: 2413, 549: 2413, 552: 2413, 608: 2413, 695: 2413}, + // 2775 + {2412, 2412, 530: 2412, 5418, 2412, 537: 2412, 547: 2412, 549: 2412, 552: 2412, 608: 2412, 695: 2412, 1231: 5636}, + {723: 5631}, + {182: 2420, 701: 2420, 1202: 5564, 5559, 1289: 5632}, + {2416, 2416, 274: 5634, 530: 2416, 2416, 2416, 537: 2416, 547: 2416, 549: 2416, 552: 2416, 608: 2416, 695: 2416, 1476: 5633}, + {2417, 2417, 530: 2417, 2417, 2417, 537: 2417, 547: 2417, 549: 2417, 552: 2417, 608: 2417, 695: 2417}, + // 2780 + {560: 3037, 799: 3866, 816: 5635}, + {2415, 2415, 530: 2415, 2415, 2415, 537: 2415, 547: 2415, 549: 2415, 552: 2415, 608: 2415, 695: 2415}, + {2443, 2443, 530: 2443, 2443, 2443, 537: 2443, 547: 2443, 549: 2443, 552: 2443, 608: 2443, 695: 2443}, + {2782, 2782}, + {2781, 2781, 6: 4725, 5452, 18: 4681, 20: 4733, 22: 4729, 4726, 4728, 4731, 4732, 4734, 710: 4730, 865: 4735, 910: 5451}, + // 2785 + {546: 5640}, + {192: 5641}, + {199: 5642}, + {531: 5643}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5644}, + // 2790 + {57: 5645, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2783, 2783}, + {546: 5647}, + {192: 5648}, + {199: 5649}, + // 2795 + {531: 5650}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 5651}, + {57: 5652, 543: 3782, 3783, 3788, 584: 3784, 609: 3785, 3786, 3779, 3789, 3778, 615: 3787, 3780, 618: 3781}, + {2784, 2784}, + {762, 762, 762, 762, 762, 762, 762, 762, 762, 10: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 58: 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 5251, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 762, 908: 5250, 924: 5654}, + // 2800 + {2719, 2719, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5656, 1438: 5655}, + {2785, 2785}, + {9: 5256, 559: 5657}, + {531: 5658}, + {546: 5421, 1067: 5420, 1230: 5659}, + // 2805 + {9: 5462, 57: 5660}, + {2718, 2718}, + {2786, 2786}, + {2780, 2780}, + {173: 5664, 984: 264, 1209: 5665}, + // 2810 + {984: 263}, + {984: 5666}, + {533: 5667}, + {156, 156, 235: 156, 406: 5669, 722: 156, 1393: 5668}, + {154, 154, 235: 5672, 722: 154, 1392: 5671}, + // 2815 + {560: 3037, 799: 5670}, + {155, 155, 235: 155, 722: 155}, + {247, 247, 722: 3978, 1056: 5679}, + {152, 152, 239: 152, 418: 5674, 722: 152, 1419: 5673}, + {150, 150, 239: 5677, 722: 150, 1418: 5676}, + // 2820 + {560: 3037, 799: 5675}, + {151, 151, 239: 151, 722: 151}, + {153, 153, 722: 153}, + {560: 3037, 799: 5678}, + {149, 149, 722: 149}, + // 2825 + {157, 157}, + {28: 203, 56: 203, 159: 203, 531: 203, 560: 203}, + {56: 5210, 531: 5681, 999: 5218}, + {208, 208}, + {560: 3037, 799: 5687}, + // 2830 + {560: 3037, 799: 5686}, + {205, 205}, + {206, 206}, + {207, 207}, + {556: 5691}, + // 2835 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 5690}, + {556: 209}, + {560: 3037, 799: 5692}, + {303: 5694, 532: 213, 552: 213, 587: 213, 714: 213, 803: 213, 1350: 5693}, + {532: 2906, 552: 2891, 587: 2890, 714: 3019, 803: 2870, 815: 5697, 822: 3018, 2871, 5701, 826: 5702, 5700, 833: 2872, 839: 5699, 1452: 5698}, + // 2840 + {433: 5695}, + {159: 5696, 532: 212, 552: 212, 587: 212, 714: 212, 803: 212}, + {532: 211, 552: 211, 587: 211, 714: 211, 803: 211}, + {714: 3019, 803: 2870, 822: 5705, 5703, 833: 5704}, + {218, 218}, + // 2845 + {217, 217}, + {216, 216}, + {215, 215}, + {214, 214}, + {2340, 2340}, + // 2850 + {2339, 2339}, + {429, 429, 542: 429}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5718, 1292: 5719, 1479: 5717}, + {227, 227, 227, 227, 227, 227, 227, 227, 227, 10: 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 58: 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227, 227}, + {226, 226, 226, 226, 226, 226, 226, 226, 226, 10: 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 58: 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226, 226}, + // 2855 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5710, 884: 5711}, + {1252, 1252, 9: 1252, 546: 5712}, + {201, 201, 9: 3930}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5714, 770: 5255, 3051, 3052, 3050, 859: 5713}, + {200, 200, 9: 5256}, + // 2860 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5715}, + {9: 5256, 57: 5716}, + {199, 199}, + {228, 228, 9: 5725}, + {729: 5721, 768: 5722, 1388: 5720}, + // 2865 + {220, 220, 9: 220}, + {225, 225, 9: 225}, + {224, 224, 9: 224, 173: 5724}, + {222, 222, 9: 222, 173: 5723}, + {221, 221, 9: 221}, + // 2870 + {223, 223, 9: 223}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5718, 1292: 5726}, + {219, 219, 9: 219}, + {229, 229}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5729, 884: 5730}, + // 2875 + {1252, 1252, 9: 1252, 546: 5731}, + {198, 198, 9: 3930}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5733, 770: 5255, 3051, 3052, 3050, 859: 5732}, + {197, 197, 9: 5256}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 5734}, + // 2880 + {9: 5256, 57: 5735}, + {196, 196}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5737}, + {531: 5738, 557: 2596, 561: 2596, 1125: 5739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2602, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 628: 3682, 770: 4024, 3051, 3052, 3050, 776: 5766, 821: 5765, 1124: 5764, 1335: 5763, 5767}, + // 2885 + {557: 5740, 561: 241, 1207: 5741}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 5758, 1206: 5757, 1387: 5756}, + {561: 5742}, + {533: 5743}, + {270, 270, 232: 5744, 532: 270, 1179: 5745}, + // 2890 + {533: 5755}, + {236, 236, 532: 5746, 1205: 5747}, + {56: 5750, 1204: 5749, 1386: 5748}, + {230, 230}, + {235, 235, 9: 5753}, + // 2895 + {234, 234, 9: 234}, + {232, 232, 9: 232, 555: 5751}, + {533: 3586, 543: 4940, 4941, 548: 3577, 560: 3581, 629: 3576, 3578, 636: 3580, 3579, 3584, 640: 3585, 647: 3583, 777: 4939, 779: 3582, 1082: 5752}, + {231, 231, 9: 231}, + {56: 5750, 1204: 5754}, + // 2900 + {233, 233, 9: 233}, + {269, 269, 532: 269, 549: 269, 552: 269, 559: 269}, + {240, 240, 9: 5761, 532: 240, 561: 240}, + {238, 238, 9: 238, 532: 238, 561: 238}, + {555: 5759}, + // 2905 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 5760}, + {237, 237, 9: 237, 532: 237, 561: 237}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3901, 3051, 3052, 3050, 778: 5758, 1206: 5762}, + {239, 239, 9: 239, 532: 239, 561: 239}, + {9: 5769, 57: 2601}, + // 2910 + {9: 2600, 57: 2600}, + {9: 2598, 57: 2598}, + {9: 2597, 57: 2597}, + {57: 5768}, + {2595, 2595, 532: 2595, 557: 2595, 561: 2595}, + // 2915 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 628: 3682, 770: 4024, 3051, 3052, 3050, 776: 5766, 821: 5765, 1124: 5770}, + {9: 2599, 57: 2599}, + {173: 5664, 984: 264, 1209: 5774}, + {533: 5773}, + {202, 202}, + // 2920 + {984: 5775}, + {533: 5776}, + {232: 5744, 549: 270, 552: 270, 559: 270, 1179: 5777}, + {549: 5778, 552: 5779, 559: 2382, 1164: 5780}, + {2381, 2381, 530: 2381, 2381, 2381, 537: 2381, 547: 2381, 559: 2381, 608: 2381, 695: 2381}, + // 2925 + {2380, 2380, 530: 2380, 2380, 2380, 537: 2380, 547: 2380, 559: 2380, 608: 2380, 695: 2380}, + {559: 5781}, + {608: 5782}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 5783}, + {266, 266, 131: 266, 154: 266, 531: 266, 266, 549: 266, 557: 266, 709: 5785, 722: 266, 1332: 5784}, + // 2930 + {262, 262, 131: 3950, 154: 3949, 531: 262, 262, 549: 262, 557: 262, 722: 262, 930: 3948, 1173: 5788}, + {557: 5786}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 5787}, + {265, 265, 131: 265, 154: 265, 531: 265, 265, 549: 265, 557: 265, 722: 265}, + {247, 247, 531: 247, 247, 549: 247, 557: 247, 722: 3978, 1056: 5789}, + // 2935 + {268, 268, 531: 268, 268, 549: 5791, 557: 268, 1371: 5790}, + {2596, 2596, 531: 5738, 2596, 557: 2596, 1125: 5794}, + {560: 3037, 799: 5792}, + {722: 5793}, + {267, 267, 531: 267, 267, 557: 267}, + // 2940 + {241, 241, 532: 241, 557: 5740, 1207: 5795}, + {236, 236, 532: 5746, 1205: 5796}, + {271, 271}, + {9: 328, 56: 328, 530: 328, 561: 328, 628: 2090, 712: 328, 726: 2090}, + {9: 293, 530: 293, 293, 561: 293, 628: 2058, 712: 293, 726: 2058}, + // 2945 + {9: 307, 530: 307, 307, 561: 307, 628: 2032, 712: 307, 726: 2032}, + {9: 294, 530: 294, 294, 561: 294, 628: 2029, 712: 294, 726: 2029}, + {9: 283, 530: 283, 283, 561: 283, 628: 1992, 712: 283, 726: 1992}, + {9: 303, 530: 303, 303, 561: 303, 628: 1914, 712: 303, 726: 1914}, + {9: 308, 530: 308, 308, 561: 308, 628: 1907, 712: 308, 726: 1907}, + // 2950 + {374: 5906, 438: 5905, 628: 1888, 726: 1888}, + {9: 295, 530: 295, 295, 561: 295, 628: 1885, 712: 295, 726: 1885}, + {9: 284, 530: 284, 284, 561: 284, 628: 1882, 712: 284, 726: 1882}, + {628: 5903, 726: 5902}, + {9: 932, 530: 932, 561: 932, 628: 435, 712: 932, 726: 435}, + // 2955 + {9: 931, 530: 931, 561: 931, 712: 931}, + {9: 324, 56: 5901, 530: 324, 561: 324, 712: 324}, + {9: 326, 530: 326, 561: 326, 712: 326}, + {9: 325, 530: 325, 561: 325, 712: 325}, + {561: 5899}, + // 2960 + {9: 304, 530: 304, 304, 559: 5897, 561: 304, 712: 304}, + {9: 321, 530: 321, 561: 321, 712: 321}, + {9: 5849, 530: 5850, 561: 5851}, + {9: 319, 530: 319, 5846, 561: 319, 712: 319}, + {9: 317, 240: 5845, 530: 317, 317, 561: 317, 712: 317}, + // 2965 + {9: 315, 335: 5844, 530: 315, 315, 561: 315, 712: 315}, + {9: 314, 20: 5838, 132: 5840, 216: 5839, 5837, 224: 5841, 335: 5842, 530: 314, 314, 561: 314, 712: 314}, + {9: 311, 530: 311, 311, 561: 311, 712: 311}, + {9: 310, 530: 310, 310, 561: 310, 712: 310}, + {9: 309, 224: 5836, 530: 309, 309, 561: 309, 712: 309}, + // 2970 + {9: 306, 530: 306, 306, 561: 306, 712: 306}, + {9: 305, 530: 305, 305, 561: 305, 712: 305}, + {132: 5835, 1144: 5834}, + {9: 301, 530: 301, 301, 561: 301, 712: 301}, + {1006: 5833}, + // 2975 + {9: 299, 530: 299, 299, 561: 299, 712: 299}, + {9: 296, 530: 296, 296, 561: 296, 712: 296}, + {157: 5832}, + {9: 291, 530: 291, 291, 561: 291, 712: 291}, + {9: 300, 530: 300, 300, 561: 300, 712: 300}, + // 2980 + {9: 302, 530: 302, 302, 561: 302, 712: 302}, + {9: 289, 530: 289, 289, 561: 289, 712: 289}, + {9: 287, 530: 287, 287, 561: 287, 712: 287}, + {9: 313, 530: 313, 313, 561: 313, 712: 313}, + {9: 312, 530: 312, 312, 561: 312, 712: 312}, + // 2985 + {157: 5843}, + {9: 290, 530: 290, 290, 561: 290, 712: 290}, + {9: 288, 530: 288, 288, 561: 288, 712: 288}, + {9: 286, 530: 286, 286, 561: 286, 712: 286}, + {9: 292, 530: 292, 292, 561: 292, 712: 292}, + // 2990 + {9: 285, 530: 285, 285, 561: 285, 712: 285}, + {9: 316, 530: 316, 316, 561: 316, 712: 316}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 5847}, + {9: 4027, 57: 5848}, + {9: 318, 530: 318, 561: 318, 712: 318}, + // 2995 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 5797, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 5799, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 5805, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 5801, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 5798, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 5806, 3226, 3477, 5800, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 5803, 3134, 3135, 3379, 5804, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 5802, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 5808, 562: 5831, 587: 5825, 695: 5814, 707: 5829, 710: 5824, 714: 5827, 5818, 724: 5819, 727: 5823, 743: 5820, 770: 3737, 3051, 3052, 3050, 803: 5822, 805: 5807, 894: 5813, 898: 5809, 957: 5828, 968: 5826, 1045: 5810, 1071: 5811, 5817, 1078: 5812, 5896, 1088: 5821, 1092: 5830}, + {2: 282, 282, 282, 282, 282, 282, 282, 10: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 58: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 5863, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 584: 282, 608: 5862, 964: 5864, 1215: 5865}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5855}, + {945, 945, 6: 945, 9: 945, 15: 945, 51: 945, 945, 945, 945, 945, 132: 945, 183: 945, 532: 945, 542: 945, 555: 945, 628: 5860, 696: 945, 712: 945, 717: 945, 725: 945, 5859}, + {1405, 1405, 6: 1405, 9: 1405, 15: 1405, 51: 1405, 1405, 1405, 1405, 1405, 132: 1405, 183: 1405, 531: 4363, 1405, 542: 1405, 555: 1405, 696: 1405, 712: 1405, 717: 1405, 725: 1405, 1224: 5858}, + // 3000 + {941, 941, 9: 941, 532: 941}, + {272, 272, 9: 5856}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5857}, + {940, 940, 9: 940, 532: 940}, + {942, 942, 6: 942, 9: 942, 15: 942, 51: 942, 942, 942, 942, 942, 132: 942, 183: 942, 532: 942, 542: 942, 555: 942, 696: 942, 712: 942, 717: 942, 725: 942}, + // 3005 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5861}, + {943, 943, 6: 943, 9: 943, 15: 943, 51: 943, 943, 943, 943, 943, 132: 943, 183: 943, 532: 943, 542: 943, 555: 943, 696: 943, 712: 943, 717: 943, 725: 943}, + {944, 944, 6: 944, 9: 944, 15: 944, 51: 944, 944, 944, 944, 944, 132: 944, 183: 944, 532: 944, 542: 944, 555: 944, 696: 944, 712: 944, 717: 944, 725: 944}, + {2: 281, 281, 281, 281, 281, 281, 281, 10: 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 58: 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 584: 281}, + {2: 280, 280, 280, 280, 280, 280, 280, 10: 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 58: 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 584: 280}, + // 3010 + {2: 279, 279, 279, 279, 279, 279, 279, 10: 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 58: 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 584: 279}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5866, 770: 5867, 3051, 3052, 3050, 1241: 5868}, + {561: 278, 712: 278, 716: 5894}, + {561: 274, 712: 274, 716: 5891}, + {561: 5869}, + // 3015 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 5872}, + {365, 365, 6: 365, 9: 365, 15: 365, 51: 365, 365, 365, 365, 365, 183: 5876, 532: 365, 725: 365, 1323: 5875}, + {411, 411, 6: 411, 9: 411, 15: 411, 51: 411, 411, 411, 411, 411, 532: 411, 725: 411}, + {273, 273, 9: 5873}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5874}, + // 3020 + {410, 410, 6: 410, 9: 410, 15: 410, 51: 410, 410, 410, 410, 410, 532: 410, 725: 410}, + {412, 412, 6: 412, 9: 412, 15: 412, 51: 412, 412, 412, 412, 412, 532: 412, 725: 412}, + {532: 5878, 723: 5877}, + {15: 5889, 533: 5886, 997: 5888}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5880, 1324: 5879}, + // 3025 + {363, 363, 6: 363, 9: 363, 15: 363, 51: 363, 363, 363, 363, 363, 532: 363, 537: 5882, 723: 5881, 725: 363}, + {359, 359, 6: 359, 9: 359, 15: 359, 51: 359, 359, 359, 359, 359, 532: 359, 537: 359, 723: 359, 725: 359}, + {533: 5886, 997: 5887}, + {533: 5884, 638: 5885, 1186: 5883}, + {361, 361, 6: 361, 9: 361, 15: 361, 51: 361, 361, 361, 361, 361, 532: 361, 725: 361}, + // 3030 + {358, 358, 6: 358, 9: 358, 15: 358, 51: 358, 358, 358, 358, 358, 532: 358, 725: 358}, + {357, 357, 6: 357, 9: 357, 15: 357, 51: 357, 357, 357, 357, 357, 532: 357, 725: 357}, + {937, 937, 6: 937, 9: 937, 15: 937, 51: 937, 937, 937, 937, 937, 57: 937, 532: 937, 725: 937}, + {362, 362, 6: 362, 9: 362, 15: 362, 51: 362, 362, 362, 362, 362, 532: 362, 725: 362}, + {364, 364, 6: 364, 9: 364, 15: 364, 51: 364, 364, 364, 364, 364, 532: 364, 725: 364}, + // 3035 + {533: 5884, 638: 5885, 1186: 5890}, + {360, 360, 6: 360, 9: 360, 15: 360, 51: 360, 360, 360, 360, 360, 532: 360, 725: 360}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5892, 770: 5893, 3051, 3052, 3050}, + {561: 276, 712: 276}, + {561: 275, 712: 275}, + // 3040 + {584: 5895}, + {561: 277, 712: 277}, + {9: 320, 530: 320, 561: 320, 712: 320}, + {336: 5898}, + {9: 322, 530: 322, 561: 322, 712: 322}, + // 3045 + {336: 5900}, + {9: 323, 530: 323, 561: 323, 712: 323}, + {9: 327, 56: 327, 530: 327, 561: 327, 712: 327}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 770: 3737, 3051, 3052, 3050, 805: 5904}, + {933, 933, 9: 933, 530: 933, 561: 933, 712: 933}, + // 3050 + {934, 934, 9: 934, 530: 934, 561: 934, 712: 934}, + {9: 298, 530: 298, 298, 561: 298, 712: 298}, + {9: 297, 530: 297, 297, 561: 297, 712: 297}, + {530: 5949, 628: 2005, 726: 2005}, + {9: 5849, 530: 5909, 712: 5910}, + // 3055 + {2: 282, 282, 282, 282, 282, 282, 282, 10: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 58: 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 5863, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 584: 282, 608: 5862, 964: 5864, 1215: 5912}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5911}, + {335, 335, 9: 5856}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 5866, 770: 5867, 3051, 3052, 3050, 1241: 5913}, + {712: 5914}, + // 3060 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 5915}, + {401, 401, 9: 5873, 532: 401, 725: 5917, 1075: 5916, 5918}, + {400, 400, 6: 400, 15: 400, 51: 400, 400, 400, 400, 400, 532: 400}, + {161: 5938, 5936, 168: 5939, 5937, 5940, 409: 5931, 459: 5933, 1077: 5935, 1439: 5934, 1464: 5932}, + {334, 334, 532: 5920, 1307: 5919}, + // 3065 + {337, 337}, + {163: 5924, 5922, 5923, 5925, 957: 5921}, + {1006: 5930}, + {560: 3037, 799: 5929}, + {560: 3037, 799: 5928}, + // 3070 + {560: 3037, 799: 5927}, + {560: 3037, 799: 5926}, + {329, 329}, + {330, 330}, + {331, 331}, + // 3075 + {332, 332}, + {333, 333}, + {399, 399, 6: 399, 15: 399, 51: 399, 399, 399, 399, 399, 532: 399}, + {398, 398, 6: 398, 15: 398, 51: 398, 398, 398, 398, 398, 532: 398}, + {397, 397, 6: 397, 15: 397, 51: 397, 397, 397, 397, 397, 532: 397}, + // 3080 + {396, 396, 6: 396, 15: 396, 51: 396, 396, 396, 396, 396, 161: 5938, 5936, 168: 5939, 5937, 5940, 532: 396, 566: 5946, 1077: 5947}, + {395, 395, 6: 395, 15: 395, 51: 395, 395, 395, 395, 395, 161: 395, 395, 168: 395, 395, 395, 532: 395, 566: 395}, + {533: 5945}, + {533: 5944}, + {533: 5943}, + // 3085 + {533: 5942}, + {533: 5941}, + {388, 388, 6: 388, 15: 388, 51: 388, 388, 388, 388, 388, 161: 388, 388, 168: 388, 388, 388, 532: 388, 566: 388}, + {389, 389, 6: 389, 15: 389, 51: 389, 389, 389, 389, 389, 161: 389, 389, 168: 389, 389, 389, 532: 389, 566: 389}, + {390, 390, 6: 390, 15: 390, 51: 390, 390, 390, 390, 390, 161: 390, 390, 168: 390, 390, 390, 532: 390, 566: 390}, + // 3090 + {391, 391, 6: 391, 15: 391, 51: 391, 391, 391, 391, 391, 161: 391, 391, 168: 391, 391, 391, 532: 391, 566: 391}, + {392, 392, 6: 392, 15: 392, 51: 392, 392, 392, 392, 392, 161: 392, 392, 168: 392, 392, 392, 532: 392, 566: 392}, + {161: 5938, 5936, 168: 5939, 5937, 5940, 1077: 5948}, + {393, 393, 6: 393, 15: 393, 51: 393, 393, 393, 393, 393, 161: 393, 393, 168: 393, 393, 393, 532: 393, 566: 393}, + {394, 394, 6: 394, 15: 394, 51: 394, 394, 394, 394, 394, 161: 394, 394, 168: 394, 394, 394, 532: 394, 566: 394}, + // 3095 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5950}, + {712: 5951}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 5952}, + {334, 334, 9: 5856, 532: 5920, 1307: 5953}, + {336, 336}, + // 3100 + {2465, 2465, 9: 2465, 16: 2465, 18: 2465, 21: 2465, 535: 2465, 538: 2465, 553: 2465, 557: 2465, 561: 2465, 563: 2465, 579: 2465, 709: 2465, 712: 2465, 765: 2465}, + {426, 426}, + {2: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 10: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 58: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 531: 1053, 533: 1053, 1053, 1053, 1053, 540: 1053, 1053, 543: 1053, 1053, 1053, 547: 1053, 1053, 1053, 552: 1053, 1053, 559: 1053, 1053, 1053, 573: 1053, 578: 1053, 584: 1053, 586: 1053, 1053, 614: 1053, 617: 1053, 628: 1053, 1053, 1053, 1053, 636: 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 1053, 697: 1053, 1053, 1053, 1053, 711: 1053, 715: 1053, 829: 1053, 1053, 836: 1053, 1053, 1053, 840: 1053, 849: 1053, 1053, 1053}, + {2: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 10: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 58: 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 1051, 531: 1051, 549: 1051, 559: 1051, 561: 1051, 643: 1051, 836: 1051, 1051, 1051}, + {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 531: 1256, 549: 1256, 643: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 5963}, + // 3105 + {2: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 10: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 58: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 531: 1259, 533: 1259, 1259, 1259, 1259, 540: 1259, 1259, 543: 1259, 1259, 1259, 547: 1259, 1259, 1259, 552: 1259, 1259, 559: 1259, 1259, 1259, 573: 1259, 578: 1259, 584: 1259, 586: 1259, 1259, 614: 1259, 617: 1259, 628: 1259, 1259, 1259, 1259, 636: 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 1259, 697: 1259, 1259, 1259, 1259, 711: 1259, 715: 1259, 829: 1259, 1259, 836: 1259, 1259, 1259, 840: 1259, 849: 1259, 1259, 1259}, + {2: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 10: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 58: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 531: 1258, 533: 1258, 1258, 1258, 1258, 540: 1258, 1258, 543: 1258, 1258, 1258, 547: 1258, 1258, 1258, 552: 1258, 1258, 559: 1258, 1258, 1258, 573: 1258, 578: 1258, 584: 1258, 586: 1258, 1258, 614: 1258, 617: 1258, 628: 1258, 1258, 1258, 1258, 636: 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 1258, 697: 1258, 1258, 1258, 1258, 711: 1258, 715: 1258, 829: 1258, 1258, 836: 1258, 1258, 1258, 840: 1258, 849: 1258, 1258, 1258}, + {2: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 10: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 58: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 531: 1257, 533: 1257, 1257, 1257, 1257, 540: 1257, 1257, 543: 1257, 1257, 1257, 547: 1257, 1257, 1257, 552: 1257, 1257, 559: 1257, 1257, 1257, 573: 1257, 578: 1257, 584: 1257, 586: 1257, 1257, 614: 1257, 617: 1257, 628: 1257, 1257, 1257, 1257, 636: 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 1257, 697: 1257, 1257, 1257, 1257, 711: 1257, 715: 1257, 829: 1257, 1257, 836: 1257, 1257, 1257, 840: 1257, 849: 1257, 1257, 1257}, + {2: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 10: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 58: 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 1255, 531: 1255, 549: 1255, 559: 1255, 561: 1255, 643: 1255}, + {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 531: 2115, 549: 4675, 643: 2115, 958: 5964}, + // 3110 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5967, 955: 5965, 993: 5966}, + {1130, 1130, 9: 1130, 57: 1130, 530: 1130, 532: 1130, 539: 1130, 542: 1130, 550: 1130, 1130, 554: 1130, 556: 1130, 1130, 1130, 1130, 562: 1130, 1130, 1130, 571: 1130, 1130, 574: 1130}, + {9: 6019, 557: 6088}, + {9: 1128, 540: 5986, 5987, 557: 6073, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6070, 3051, 3052, 3050}, + // 3115 + {1126, 1126, 9: 1126, 57: 1126, 530: 1126, 532: 1126, 539: 1126, 1126, 1126, 1126, 550: 1126, 1126, 554: 1126, 556: 1126, 1126, 1126, 1126, 562: 1126, 1126, 1126, 571: 1126, 1126, 1126, 1126, 576: 1126, 580: 1126, 1126, 583: 1126, 585: 1126}, + {1125, 1125, 9: 1125, 57: 1125, 530: 1125, 532: 1125, 539: 1125, 1125, 1125, 1125, 550: 1125, 1125, 554: 1125, 556: 1125, 1125, 1125, 1125, 562: 1125, 1125, 1125, 571: 1125, 1125, 1125, 1125, 576: 1125, 580: 1125, 1125, 583: 1125, 585: 1125}, + {1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 532: 1121, 537: 1121, 539: 1121, 1121, 1121, 1121, 546: 6023, 549: 1121, 1121, 1121, 554: 1121, 556: 1121, 1121, 1121, 1121, 562: 1121, 1121, 1121, 1121, 571: 1121, 1121, 1121, 1121, 1121, 1121, 580: 1121, 1121, 583: 1121, 585: 1121, 592: 1121, 731: 1121, 963: 6022}, + {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1119, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 1119, 532: 1119, 537: 5980, 539: 1119, 1119, 1119, 1119, 550: 1119, 1119, 554: 1119, 556: 1119, 1119, 1119, 1119, 562: 1119, 1119, 1119, 571: 1119, 1119, 1119, 1119, 576: 1119, 580: 1119, 1119, 583: 1119, 585: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 5977}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 2906, 547: 2905, 608: 2904, 643: 5968, 695: 2900, 770: 3927, 3051, 3052, 3050, 775: 5976, 804: 5971, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 5974}, + // 3120 + {9: 6019, 57: 6020}, + {1128, 1128, 9: 1128, 57: 1128, 530: 1128, 532: 1128, 539: 1128, 5986, 5987, 1128, 550: 1128, 1128, 554: 1128, 556: 1128, 1128, 1128, 1128, 562: 1128, 1128, 1128, 571: 1128, 1128, 5985, 1128, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 3989, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 537: 5980, 539: 1013, 1119, 1119, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 573: 1119, 576: 1119, 580: 1119, 1119, 583: 1119, 770: 5979, 3051, 3052, 3050, 842: 3860, 3861, 1016: 5978, 5977}, + {1123, 1123, 9: 1123, 57: 1123, 530: 1123, 532: 1123, 539: 1123, 1123, 1123, 1123, 550: 1123, 1123, 554: 1123, 556: 1123, 1123, 1123, 1123, 562: 1123, 1123, 1123, 571: 1123, 1123, 1123, 1123, 576: 1123, 580: 1123, 1123, 583: 1123, 585: 1123}, + {1118, 1118, 9: 1118, 57: 1118, 530: 1118, 532: 1118, 539: 1118, 1118, 1118, 1118, 549: 1118, 1118, 1118, 554: 1118, 556: 1118, 1118, 1118, 1118, 562: 1118, 1118, 1118, 1118, 571: 1118, 1118, 1118, 1118, 1118, 1118, 580: 1118, 1118, 583: 1118, 585: 1118, 592: 1118, 731: 1118}, + // 3125 + {1117, 1117, 9: 1117, 57: 1117, 530: 1117, 532: 1117, 539: 1117, 1117, 1117, 1117, 549: 1117, 1117, 1117, 554: 1117, 556: 1117, 1117, 1117, 1117, 562: 1117, 1117, 1117, 1117, 571: 1117, 1117, 1117, 1117, 1117, 1117, 580: 1117, 1117, 583: 1117, 585: 1117, 592: 1117, 731: 1117}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5981, 3051, 3052, 3050}, + {1116, 1116, 9: 1116, 57: 1116, 530: 1116, 532: 1116, 539: 1116, 1116, 1116, 1116, 549: 1116, 1116, 1116, 554: 1116, 556: 1116, 1116, 1116, 1116, 562: 1116, 1116, 1116, 1116, 571: 1116, 1116, 1116, 1116, 1116, 1116, 580: 1116, 1116, 583: 1116, 585: 1116, 592: 1116, 731: 1116}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6012}, + {576: 1087, 1009: 5999, 1229: 6003}, + // 3130 + {540: 5986, 5987, 576: 5996, 914: 5997}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5993}, + {576: 1089, 1009: 1089}, + {576: 1088, 1009: 1088}, + {2: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 10: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 58: 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 1085, 531: 1085}, + // 3135 + {576: 5992}, + {576: 5991}, + {2: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 10: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 58: 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 1083, 531: 1083}, + {2: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 10: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 58: 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 1084, 531: 1084}, + {1091, 1091, 9: 1091, 57: 1091, 530: 5994, 532: 1091, 539: 1091, 1091, 1091, 1091, 550: 1091, 1091, 554: 1091, 556: 1091, 1091, 1091, 1091, 562: 1091, 1091, 1091, 571: 1091, 1091, 1091, 1091, 576: 1091, 580: 1091, 1091, 583: 1091, 585: 1091, 914: 5983, 919: 5982}, + // 3140 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 5995}, + {1090, 1090, 9: 1090, 57: 1090, 530: 1090, 532: 1090, 539: 1090, 1090, 1090, 1090, 550: 1090, 1090, 554: 1090, 556: 1090, 1090, 1090, 1090, 562: 1090, 1090, 1090, 566: 3745, 3743, 3744, 3742, 3740, 1090, 1090, 1090, 1090, 576: 1090, 580: 1090, 1090, 583: 1090, 585: 1090, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6002}, + {576: 1087, 1009: 5999, 1229: 5998}, + {576: 6000}, + // 3145 + {576: 1086}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6001}, + {1092, 1092, 9: 1092, 57: 1092, 530: 1092, 532: 1092, 539: 1092, 1092, 1092, 1092, 550: 1092, 1092, 554: 1092, 556: 1092, 1092, 1092, 1092, 562: 1092, 1092, 1092, 571: 1092, 1092, 1092, 1092, 576: 1092, 580: 1092, 1092, 583: 1092, 585: 1092, 914: 5983, 919: 5982}, + {1093, 1093, 9: 1093, 57: 1093, 530: 1093, 532: 1093, 539: 1093, 1093, 1093, 1093, 550: 1093, 1093, 554: 1093, 556: 1093, 1093, 1093, 1093, 562: 1093, 1093, 1093, 571: 1093, 1093, 1093, 1093, 576: 1093, 580: 1093, 1093, 583: 1093, 585: 1093, 914: 5983, 919: 5982}, + {576: 6004}, + // 3150 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6005}, + {530: 6006, 540: 5986, 5987, 6007, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 914: 5983, 919: 5982}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6011}, + {531: 6008}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 6009}, + // 3155 + {9: 4027, 57: 6010}, + {1094, 1094, 9: 1094, 57: 1094, 530: 1094, 532: 1094, 539: 1094, 1094, 1094, 1094, 550: 1094, 1094, 554: 1094, 556: 1094, 1094, 1094, 1094, 562: 1094, 1094, 1094, 571: 1094, 1094, 1094, 1094, 576: 1094, 580: 1094, 1094, 583: 1094, 585: 1094}, + {1095, 1095, 9: 1095, 57: 1095, 530: 1095, 532: 1095, 539: 1095, 1095, 1095, 1095, 550: 1095, 1095, 554: 1095, 556: 1095, 1095, 1095, 1095, 562: 1095, 1095, 1095, 566: 3745, 3743, 3744, 3742, 3740, 1095, 1095, 1095, 1095, 576: 1095, 580: 1095, 1095, 583: 1095, 585: 1095, 800: 3741, 3739}, + {1098, 1098, 9: 1098, 57: 1098, 530: 6013, 532: 1098, 539: 1098, 5986, 5987, 6014, 550: 1098, 1098, 554: 1098, 556: 1098, 1098, 1098, 1098, 562: 1098, 1098, 1098, 571: 1098, 1098, 5985, 1098, 576: 5988, 580: 5984, 5989, 583: 5990, 585: 1098, 914: 5983, 919: 5982}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6018}, + // 3160 + {531: 6015}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 6016}, + {9: 4027, 57: 6017}, + {1096, 1096, 9: 1096, 57: 1096, 530: 1096, 532: 1096, 539: 1096, 1096, 1096, 1096, 550: 1096, 1096, 554: 1096, 556: 1096, 1096, 1096, 1096, 562: 1096, 1096, 1096, 571: 1096, 1096, 1096, 1096, 576: 1096, 580: 1096, 1096, 583: 1096, 585: 1096}, + {1097, 1097, 9: 1097, 57: 1097, 530: 1097, 532: 1097, 539: 1097, 1097, 1097, 1097, 550: 1097, 1097, 554: 1097, 556: 1097, 1097, 1097, 1097, 562: 1097, 1097, 1097, 566: 3745, 3743, 3744, 3742, 3740, 1097, 1097, 1097, 1097, 576: 1097, 580: 1097, 1097, 583: 1097, 585: 1097, 800: 3741, 3739}, + // 3165 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 6021}, + {1122, 1122, 9: 1122, 57: 1122, 530: 1122, 532: 1122, 539: 1122, 1122, 1122, 1122, 550: 1122, 1122, 554: 1122, 556: 1122, 1122, 1122, 1122, 562: 1122, 1122, 1122, 571: 1122, 1122, 1122, 1122, 576: 1122, 580: 1122, 1122, 583: 1122, 585: 1122}, + {1129, 1129, 9: 1129, 57: 1129, 530: 1129, 532: 1129, 539: 1129, 542: 1129, 550: 1129, 1129, 554: 1129, 556: 1129, 1129, 1129, 1129, 562: 1129, 1129, 1129, 571: 1129, 1129, 574: 1129}, + {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1119, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1119, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 1119, 532: 1119, 537: 5980, 539: 1119, 1119, 1119, 1119, 549: 1119, 1119, 1119, 554: 1119, 556: 1119, 1119, 1119, 1119, 562: 1119, 1119, 1119, 1119, 571: 1119, 1119, 1119, 1119, 1119, 1119, 580: 1119, 1119, 583: 1119, 585: 1119, 592: 1119, 731: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 6027}, + {531: 6024}, + // 3170 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 6025}, + {9: 5256, 57: 6026}, + {1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 1120, 537: 1120, 539: 1120, 1120, 1120, 1120, 547: 1120, 549: 1120, 1120, 1120, 554: 1120, 556: 1120, 1120, 1120, 1120, 562: 1120, 1120, 1120, 1120, 571: 1120, 1120, 1120, 1120, 1120, 1120, 580: 1120, 1120, 583: 1120, 585: 1120, 592: 1120, 608: 1120, 627: 1120, 695: 1120, 710: 1120, 723: 1120, 731: 1120}, + {2122, 2122, 9: 2122, 57: 2122, 530: 2122, 532: 2122, 539: 2122, 2122, 2122, 2122, 549: 2122, 2122, 2122, 554: 2122, 556: 2122, 2122, 2122, 2122, 562: 2122, 2122, 2122, 2122, 571: 2122, 2122, 2122, 2122, 2122, 2122, 580: 2122, 2122, 583: 2122, 585: 2122, 592: 2122, 731: 4641, 995: 6028, 1321: 6029}, + {2121, 2121, 9: 2121, 57: 2121, 530: 2121, 532: 2121, 539: 2121, 2121, 2121, 2121, 549: 2121, 2121, 2121, 554: 2121, 556: 2121, 2121, 2121, 2121, 562: 2121, 2121, 2121, 2121, 571: 2121, 2121, 2121, 2121, 2121, 2121, 580: 2121, 2121, 583: 2121, 585: 2121, 592: 2121}, + // 3175 + {1100, 1100, 9: 1100, 57: 1100, 530: 1100, 532: 1100, 539: 1100, 1100, 1100, 1100, 549: 6032, 1100, 1100, 554: 1100, 556: 1100, 1100, 1100, 1100, 562: 1100, 1100, 1100, 6033, 571: 1100, 1100, 1100, 1100, 6031, 1100, 580: 1100, 1100, 583: 1100, 585: 1100, 592: 1100, 1051: 6035, 6034, 1191: 6036, 6030}, + {1215, 1215, 9: 1215, 57: 1215, 530: 1215, 532: 1215, 539: 1215, 1215, 1215, 1215, 550: 1215, 1215, 554: 1215, 556: 1215, 1215, 1215, 1215, 562: 1215, 1215, 1215, 571: 1215, 1215, 1215, 1215, 576: 1215, 580: 1215, 1215, 583: 1215, 585: 1215, 592: 6051, 1482: 6052}, + {701: 4906, 710: 4907, 923: 6050}, + {701: 4906, 710: 4907, 923: 6049}, + {701: 4906, 710: 4907, 923: 6048}, + // 3180 + {531: 1112, 558: 6038, 1373: 6039}, + {1102, 1102, 9: 1102, 57: 1102, 530: 1102, 532: 1102, 539: 1102, 1102, 1102, 1102, 549: 1102, 1102, 1102, 554: 1102, 556: 1102, 1102, 1102, 1102, 562: 1102, 1102, 1102, 1102, 571: 1102, 1102, 1102, 1102, 1102, 1102, 580: 1102, 1102, 583: 1102, 585: 1102, 592: 1102}, + {1099, 1099, 9: 1099, 57: 1099, 530: 1099, 532: 1099, 539: 1099, 1099, 1099, 1099, 549: 6032, 1099, 1099, 554: 1099, 556: 1099, 1099, 1099, 1099, 562: 1099, 1099, 1099, 6033, 571: 1099, 1099, 1099, 1099, 6031, 1099, 580: 1099, 1099, 583: 1099, 585: 1099, 592: 1099, 1051: 6037, 6034}, + {1101, 1101, 9: 1101, 57: 1101, 530: 1101, 532: 1101, 539: 1101, 1101, 1101, 1101, 549: 1101, 1101, 1101, 554: 1101, 556: 1101, 1101, 1101, 1101, 562: 1101, 1101, 1101, 1101, 571: 1101, 1101, 1101, 1101, 1101, 1101, 580: 1101, 1101, 583: 1101, 585: 1101, 592: 1101}, + {564: 6044, 571: 6045, 576: 6043}, + // 3185 + {531: 6040}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 1107, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 6041}, + {9: 5546, 57: 6042}, + {1108, 1108, 9: 1108, 57: 1108, 530: 1108, 532: 1108, 539: 1108, 1108, 1108, 1108, 549: 1108, 1108, 1108, 554: 1108, 556: 1108, 1108, 1108, 1108, 562: 1108, 1108, 1108, 1108, 571: 1108, 1108, 1108, 1108, 1108, 1108, 580: 1108, 1108, 583: 1108, 585: 1108, 592: 1108}, + {531: 1111}, + // 3190 + {723: 6047}, + {723: 6046}, + {531: 1109}, + {531: 1110}, + {531: 1113, 558: 1113}, + // 3195 + {531: 1114, 558: 1114}, + {531: 1115, 558: 1115}, + {108: 6056, 368: 6055, 444: 6054, 531: 1212, 1481: 6053}, + {1124, 1124, 9: 1124, 57: 1124, 530: 1124, 532: 1124, 539: 1124, 1124, 1124, 1124, 550: 1124, 1124, 554: 1124, 556: 1124, 1124, 1124, 1124, 562: 1124, 1124, 1124, 571: 1124, 1124, 1124, 1124, 576: 1124, 580: 1124, 1124, 583: 1124, 585: 1124}, + {531: 6057}, + // 3200 + {531: 1211}, + {531: 1210}, + {531: 1209}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 6059, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6058}, + {57: 1208, 419: 6067, 566: 3745, 3743, 3744, 3742, 3740, 588: 6066, 800: 3741, 3739, 1483: 6065}, + // 3205 + {1205, 1205, 9: 1205, 57: 1205, 271: 6061, 530: 1205, 532: 1205, 539: 1205, 1205, 1205, 1205, 550: 1205, 1205, 554: 1205, 556: 1205, 1205, 1205, 1205, 562: 1205, 1205, 1205, 571: 1205, 1205, 1205, 1205, 576: 1205, 580: 1205, 1205, 583: 1205, 585: 1205, 1253: 6060}, + {1213, 1213, 9: 1213, 57: 1213, 530: 1213, 532: 1213, 539: 1213, 1213, 1213, 1213, 550: 1213, 1213, 554: 1213, 556: 1213, 1213, 1213, 1213, 562: 1213, 1213, 1213, 571: 1213, 1213, 1213, 1213, 576: 1213, 580: 1213, 1213, 583: 1213, 585: 1213}, + {531: 6062}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6063}, + {57: 6064, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 3210 + {1204, 1204, 9: 1204, 57: 1204, 530: 1204, 532: 1204, 539: 1204, 1204, 1204, 1204, 550: 1204, 1204, 554: 1204, 556: 1204, 1204, 1204, 1204, 562: 1204, 1204, 1204, 571: 1204, 1204, 1204, 1204, 576: 1204, 580: 1204, 1204, 583: 1204, 585: 1204}, + {57: 6068}, + {57: 1207}, + {57: 1206}, + {1205, 1205, 9: 1205, 57: 1205, 271: 6061, 530: 1205, 532: 1205, 539: 1205, 1205, 1205, 1205, 550: 1205, 1205, 554: 1205, 556: 1205, 1205, 1205, 1205, 562: 1205, 1205, 1205, 571: 1205, 1205, 1205, 1205, 576: 1205, 580: 1205, 1205, 583: 1205, 585: 1205, 1253: 6069}, + // 3215 + {1214, 1214, 9: 1214, 57: 1214, 530: 1214, 532: 1214, 539: 1214, 1214, 1214, 1214, 550: 1214, 1214, 554: 1214, 556: 1214, 1214, 1214, 1214, 562: 1214, 1214, 1214, 571: 1214, 1214, 1214, 1214, 576: 1214, 580: 1214, 1214, 583: 1214, 585: 1214}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 6071}, + {540: 5986, 5987, 573: 5985, 576: 5988, 580: 5984, 5989, 583: 5990, 585: 6072, 914: 5983, 919: 5982}, + {1127, 1127, 9: 1127, 57: 1127, 530: 1127, 532: 1127, 539: 1127, 542: 1127, 550: 1127, 1127, 554: 1127, 556: 1127, 1127, 1127, 1127, 562: 1127, 1127, 1127, 571: 1127, 1127, 574: 1127}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6076}, + // 3220 + {555: 6085, 717: 6086, 892: 6084}, + {2631, 2631, 9: 2631, 542: 2631, 556: 2631, 563: 2631, 2631}, + {424, 424, 9: 6077, 542: 424, 556: 424, 563: 4661, 424, 889: 4662, 6078}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6083}, + {1499, 1499, 542: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 6079}, + // 3225 + {1082, 1082, 542: 1082, 556: 6080, 1201: 6081}, + {560: 3037, 642: 3868, 799: 3866, 816: 3867, 985: 6082}, + {428, 428, 542: 428}, + {1081, 1081, 542: 1081}, + {2630, 2630, 9: 2630, 542: 2630, 556: 2630, 563: 2630, 2630}, + // 3230 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6087}, + {2: 975, 975, 975, 975, 975, 975, 975, 10: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 58: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 533: 975, 975, 975, 975, 540: 975, 975, 543: 975, 975, 975, 547: 975, 975, 552: 975, 975, 560: 975, 578: 975, 586: 975, 975, 614: 975, 617: 975, 628: 975, 975, 975, 975, 636: 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 975, 697: 975, 975, 975, 975, 711: 975}, + {2: 974, 974, 974, 974, 974, 974, 974, 10: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 58: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 533: 974, 974, 974, 974, 540: 974, 974, 543: 974, 974, 974, 547: 974, 974, 552: 974, 974, 560: 974, 578: 974, 586: 974, 974, 614: 974, 617: 974, 628: 974, 974, 974, 974, 636: 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 974, 697: 974, 974, 974, 974, 711: 974}, + {2632, 2632, 9: 2632, 542: 2632, 556: 2632, 563: 2632, 2632}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6089}, + // 3235 + {424, 424, 9: 6077, 542: 424, 563: 4661, 889: 4662, 6090}, + {427, 427, 542: 427}, + {2: 570, 570, 570, 570, 570, 570, 570, 10: 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 58: 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570, 570}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6093}, + {569, 569}, + // 3240 + {23: 6106, 155: 768, 6099, 6096, 240: 6098, 246: 6109, 257: 6107, 275: 6100, 288: 6104, 309: 6108, 313: 6101, 586: 6105, 608: 6095, 1294: 6103, 1364: 6097, 1389: 6102}, + {2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 10: 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 58: 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 2318, 532: 2318, 578: 2318}, + {2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 10: 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 58: 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 2317, 532: 2317, 578: 2317}, + {778, 778}, + {775, 775}, + // 3245 + {774, 774}, + {267: 6116}, + {772, 772}, + {155: 6115}, + {759, 759, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 759, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 4799, 1293: 6110}, + // 3250 + {769, 769}, + {155: 767}, + {155: 766}, + {155: 765}, + {155: 764}, + // 3255 + {155: 763}, + {755, 755, 532: 6112, 1509: 6111}, + {770, 770}, + {729: 6113}, + {562: 6114}, + // 3260 + {754, 754}, + {771, 771}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6118}, + {777, 777, 9: 777}, + {773, 773, 9: 6119}, + // 3265 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6120, 3051, 3052, 3050}, + {776, 776, 9: 776}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 6232, 3305, 3203, 3057, 3420, 3084, 6233, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 6234, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6235}, + {608: 6218, 710: 6219}, + {710: 6215}, + // 3270 + {608: 6210, 710: 6209}, + {608: 6207}, + {252: 6204}, + {252: 6201}, + {252: 6195}, + // 3275 + {180: 6192, 273: 6194, 390: 6190, 414: 6191, 1015: 6193}, + {253: 6187, 256: 6186}, + {608: 6145}, + {180: 6139, 206: 6141, 221: 787, 245: 6143, 316: 6142, 1469: 6140}, + {180: 6138}, + // 3280 + {180: 6137}, + {447: 6136}, + {894, 894}, + {899, 899}, + {900, 900}, + // 3285 + {901, 901}, + {221: 6144}, + {221: 786}, + {221: 785}, + {221: 784}, + // 3290 + {893, 893}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6146}, + {743: 6147, 1035: 6148}, + {206: 6151, 216: 6150, 608: 2333, 1065: 6149}, + {902, 902}, + // 3295 + {608: 6153}, + {157: 2332, 608: 2332}, + {216: 6152}, + {157: 2331, 608: 2331}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 6154}, + // 3300 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6155}, + {619, 619, 6: 619, 619, 619, 15: 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 619, 530: 619, 6159, 619, 535: 619, 537: 619, 619, 619, 546: 619, 619, 549: 619, 552: 619, 619, 565: 619, 579: 6158, 608: 619, 695: 619, 709: 619, 619, 1384: 6157, 1478: 6156}, + {576, 576, 6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 530: 576, 576, 576, 535: 4724, 537: 576, 2446, 4761, 546: 576, 576, 549: 576, 552: 576, 2446, 565: 5499, 608: 576, 695: 576, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 1020: 6174, 1141: 6173}, + {2449, 2449, 530: 6167, 1218: 6166}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6165}, + // 3305 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 579: 6160, 701: 2685, 2685, 704: 2685, 2685, 5085, 710: 2685, 746: 2685, 2685, 770: 4024, 3051, 3052, 3050, 821: 4952, 928: 5331, 953: 5469, 1002: 5470, 1085: 5471, 1291: 6161}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6163}, + {9: 5473, 57: 6162}, + {618, 618, 6: 618, 618, 618, 15: 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 530: 618, 618, 618, 535: 618, 537: 618, 618, 618, 546: 618, 618, 549: 618, 552: 618, 618, 565: 618, 608: 618, 695: 618, 709: 618, 618}, + {57: 6164}, + // 3310 + {2367, 2367, 530: 2367}, + {2368, 2368, 530: 2368}, + {2450, 2450}, + {85: 6168}, + {422: 6170, 803: 6169}, + // 3315 + {588: 6172}, + {588: 6171}, + {2447, 2447}, + {2448, 2448}, + {2444, 2444, 530: 2444, 2444, 2444, 537: 2444, 546: 6176, 2444, 549: 2444, 552: 2444, 608: 2444, 695: 2444, 1233: 6175}, + // 3320 + {575, 575, 6: 4725, 4727, 580, 5501, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 530: 575, 575, 575, 535: 4724, 537: 575, 2446, 4761, 546: 575, 575, 549: 575, 552: 575, 2446, 565: 5499, 608: 575, 695: 575, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 5500}, + {2382, 2382, 530: 2382, 2382, 2382, 537: 2382, 547: 2382, 549: 5778, 552: 5779, 608: 2382, 695: 2382, 1164: 6177}, + {723: 5557}, + {2379, 2379, 530: 2379, 2379, 2379, 537: 6179, 547: 2379, 608: 2379, 695: 2379, 1322: 6178}, + {2377, 2377, 530: 2377, 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 6184, 806: 6182, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6183, 6181, 1343: 6180}, + // 3325 + {2378, 2378, 530: 2378, 2378, 2378, 547: 2378, 608: 2378, 695: 2378}, + {2449, 2449, 530: 6167, 1218: 6185}, + {2376, 2376, 530: 2376}, + {2375, 2375, 530: 2375, 539: 1014, 550: 1014, 1014}, + {2374, 2374, 530: 2374}, + // 3330 + {2373, 2373, 530: 2373, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {2451, 2451}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6189}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6117, 3051, 3052, 3050, 1069: 6188}, + {904, 904, 9: 6119}, + // 3335 + {905, 905, 9: 6119}, + {907, 907}, + {906, 906}, + {898, 898}, + {897, 897}, + // 3340 + {896, 896}, + {219: 6196}, + {560: 3037, 799: 4538, 825: 6198, 1005: 6197}, + {911, 911, 9: 6199}, + {885, 885, 9: 885}, + // 3345 + {560: 3037, 799: 4538, 825: 6200}, + {884, 884, 9: 884}, + {219: 6202}, + {560: 3037, 799: 4538, 825: 6198, 1005: 6203}, + {912, 912, 9: 6199}, + // 3350 + {219: 6205}, + {560: 3037, 799: 4538, 825: 6198, 1005: 6206}, + {913, 913, 9: 6199}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6208}, + {914, 914, 9: 3930}, + // 3355 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6213}, + {562: 6211}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6212}, + {903, 903, 9: 3930}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6214, 3051, 3052, 3050}, + // 3360 + {916, 916}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6216}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6217, 3051, 3052, 3050}, + {917, 917}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6231}, + // 3365 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6220}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6221, 3051, 3052, 3050}, + {918, 918, 531: 6224, 1185: 6223, 1369: 6222}, + {915, 915, 9: 6229}, + {888, 888, 9: 888}, + // 3370 + {560: 3037, 799: 4538, 825: 6225}, + {9: 6226}, + {560: 3037, 799: 4538, 825: 6227}, + {57: 6228}, + {886, 886, 9: 886}, + // 3375 + {531: 6224, 1185: 6230}, + {887, 887, 9: 887}, + {919, 919, 9: 3930}, + {211: 1893, 426: 6252, 450: 6253, 716: 1893, 1311: 6251}, + {923, 923, 178: 6238, 211: 1712, 219: 6237, 716: 1712}, + // 3380 + {895, 895, 211: 1691, 716: 1691}, + {211: 6236}, + {920, 920}, + {424, 424, 560: 3037, 563: 4661, 799: 4538, 825: 6249, 889: 4662, 6248}, + {425: 6239}, + // 3385 + {556: 6240, 560: 3037, 799: 4538, 825: 6198, 1005: 6241, 1312: 6242}, + {560: 3037, 799: 3866, 816: 6243}, + {910, 910, 9: 6199}, + {909, 909}, + {926, 926, 9: 6244, 213: 6245}, + // 3390 + {560: 3037, 799: 3866, 816: 6247}, + {560: 3037, 799: 3866, 816: 6246}, + {924, 924}, + {925, 925}, + {922, 922}, + // 3395 + {424, 424, 563: 4661, 889: 4662, 6250}, + {921, 921}, + {908, 908}, + {560: 3037, 799: 6259}, + {399: 6255, 560: 3037, 715: 6256, 799: 6254}, + // 3400 + {891, 891}, + {560: 3037, 799: 6258}, + {560: 3037, 799: 6257}, + {889, 889}, + {890, 890}, + // 3405 + {892, 892}, + {2: 446, 446, 446, 446, 446, 446, 446, 10: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 58: 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 446, 533: 446, 535: 446, 555: 2060, 586: 446, 716: 2060, 2060}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6415, 555: 2058, 716: 2058, 2058, 770: 6414, 3051, 3052, 3050}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6412, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2021, 716: 2021, 2021, 770: 6274, 3051, 3052, 3050, 926: 6315}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2015, 716: 2015, 2015, 770: 6274, 3051, 3052, 3050, 926: 6409}, + // 3410 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6405, 555: 2013, 586: 4354, 716: 2013, 2013, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 6404}, + {555: 6085, 558: 6394, 716: 2008, 2008, 892: 6393}, + {555: 2000, 571: 6391, 716: 2000, 2000}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 6296, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 535: 6388, 555: 1998, 715: 6386, 1998, 1998, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6299, 1269: 6387, 1451: 6385}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6383, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 1995, 716: 1995, 1995, 770: 6274, 3051, 3052, 3050, 926: 6312}, + // 3415 + {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6366}, + {383: 6320, 385: 6319, 555: 1921, 716: 1921, 1921, 1327: 6321}, + {533: 6318, 555: 1701, 716: 1701, 1701}, + {1006, 1006, 9: 6308}, + {224: 6294}, + // 3420 + {555: 973, 716: 6292, 973}, + {555: 6085, 717: 6086, 892: 6290}, + {555: 6085, 717: 6086, 892: 6285}, + {555: 6085, 717: 6086, 892: 6283}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6282, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 6281, 1331: 6280}, + // 3425 + {951, 951, 9: 951}, + {958, 958, 9: 958}, + {957, 957, 9: 957}, + {956, 956, 9: 956}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6284}, + // 3430 + {963, 963, 9: 963, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6289}, + {977, 977, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 977, 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 4450, 3597, 3679, 3596, 3593}, + {978, 978, 9: 978}, + {976, 976, 9: 976}, + // 3435 + {964, 964, 9: 964}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6291}, + {968, 968, 9: 968}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6293, 3051, 3052, 3050}, + {555: 972, 717: 972}, + // 3440 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 6296, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 715: 6298, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6299, 1269: 6297}, + {935, 935, 9: 935, 628: 2090, 712: 935, 726: 2090}, + {994, 994, 628: 1916, 712: 994, 726: 1916}, + {712: 6306}, + {712: 993}, + // 3445 + {992, 992, 9: 6304, 712: 992}, + {936, 936, 9: 936, 628: 435, 712: 936, 726: 435}, + {930, 930, 9: 930, 712: 930}, + {929, 929, 9: 929, 712: 929}, + {928, 928, 9: 928, 712: 928}, + // 3450 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6305, 6301}, + {927, 927, 9: 927, 712: 927}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 6307}, + {995, 995, 9: 5856}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 6309, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6310, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6311}, + // 3455 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 2021, 716: 2021, 2021, 770: 6274, 3051, 3052, 3050, 926: 6315}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 555: 1995, 716: 1995, 1995, 770: 6274, 3051, 3052, 3050, 926: 6312}, + {950, 950, 9: 950}, + {555: 6085, 717: 6086, 892: 6313}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6314}, + // 3460 + {966, 966, 9: 966}, + {555: 6085, 717: 6086, 892: 6316}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6317}, + {967, 967, 9: 967}, + {998, 998}, + // 3465 + {558: 2488}, + {558: 2487}, + {558: 6322}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 6334, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 6333}, + {2: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 10: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 58: 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 1052, 549: 1052, 561: 1052, 836: 1052, 1052, 1052, 840: 5956, 969: 5957, 1019: 6339}, + // 3470 + {531: 2907, 547: 2905, 608: 2904, 695: 2900, 714: 3019, 775: 3854, 806: 3853, 2901, 2902, 2903, 2912, 2910, 3855, 3856, 822: 5705}, + {350, 350, 539: 1013, 542: 350, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {352, 352, 539: 1014, 542: 352, 550: 1014, 1014}, + {353, 353, 542: 353}, + {351, 351, 542: 351}, + // 3475 + {349, 349, 542: 349}, + {348, 348, 542: 348}, + {347, 347, 542: 347}, + {346, 346, 542: 346}, + {340, 340, 542: 6337}, + // 3480 + {218: 6335}, + {533: 6336}, + {338, 338}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 6338}, + {339, 339}, + // 3485 + {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 561: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6340}, + {2: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 10: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 58: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 6342, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 549: 1244, 561: 1244, 1247: 6341}, + {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 561: 2115, 958: 6343}, + {2: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 10: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 58: 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 1243, 549: 1243, 561: 1243}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 561: 6344, 770: 6346, 3051, 3052, 3050, 1018: 6347, 1084: 6345}, + // 3490 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6359}, + {9: 6355, 561: 6354}, + {9: 1246, 542: 1246, 561: 1246, 716: 6349, 1008: 6348}, + {9: 1248, 542: 1248, 561: 1248}, + {9: 1250, 542: 1250, 561: 1250}, + // 3495 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6351, 770: 6350, 3051, 3052, 3050}, + {9: 1246, 542: 1246, 561: 1246, 716: 6353, 1008: 6352}, + {9: 1245, 542: 1245, 561: 1245}, + {9: 1249, 542: 1249, 561: 1249}, + {584: 6351}, + // 3500 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 6357}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6346, 3051, 3052, 3050, 1018: 6356}, + {9: 1247, 542: 1247, 561: 1247}, + {424, 424, 9: 6019, 542: 424, 563: 4661, 889: 4662, 6358}, + {2344, 2344, 542: 2344}, + // 3505 + {1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 10: 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 58: 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 1121, 537: 1121, 542: 1121, 546: 6023, 549: 1121, 556: 1121, 563: 1121, 1121, 1121, 575: 1121, 963: 6360}, + {1119, 1119, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 537: 5980, 542: 1119, 549: 1119, 556: 1119, 563: 1119, 1119, 1119, 575: 1119, 770: 5979, 3051, 3052, 3050, 1016: 5978, 6361}, + {1100, 1100, 542: 1100, 549: 6032, 556: 1100, 563: 1100, 1100, 6033, 575: 6031, 1051: 6035, 6034, 1191: 6036, 6362}, + {424, 424, 542: 424, 556: 424, 563: 4661, 424, 889: 4662, 6363}, + {1499, 1499, 542: 1499, 556: 1499, 564: 3857, 842: 3911, 909: 6364}, + // 3510 + {1082, 1082, 542: 1082, 556: 6080, 1201: 6365}, + {2345, 2345, 542: 2345}, + {1001, 1001, 9: 6381}, + {988, 988, 9: 988}, + {403: 6373}, + // 3515 + {194: 6371, 768: 6370}, + {985, 985, 9: 985}, + {984, 984, 9: 984, 731: 4641, 995: 6372}, + {983, 983, 9: 983}, + {271: 6375, 435: 6377, 729: 6376, 1380: 6374}, + // 3520 + {986, 986, 9: 986}, + {729: 6380}, + {378: 6378, 455: 6379}, + {979, 979, 9: 979}, + {981, 981, 9: 981}, + // 3525 + {980, 980, 9: 980}, + {982, 982, 9: 982}, + {233: 6368, 729: 6369, 1023: 6382}, + {987, 987, 9: 987}, + {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6384}, + // 3530 + {1002, 1002, 9: 6381}, + {996, 996}, + {993, 993, 550: 6389}, + {990, 990}, + {989, 989}, + // 3535 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6390}, + {991, 991, 9: 6304}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 6392}, + {997, 997}, + {15: 6399, 533: 6398, 1234: 6403}, + // 3540 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6395}, + {555: 6085, 717: 6086, 892: 6396}, + {15: 6399, 533: 6398, 1234: 6397}, + {1004, 1004}, + {939, 939}, + // 3545 + {531: 6400}, + {533: 5886, 997: 6401}, + {57: 6402}, + {938, 938}, + {1005, 1005}, + // 3550 + {962, 962, 9: 962, 538: 6406}, + {959, 959, 9: 959}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 535: 6407, 770: 3737, 3051, 3052, 3050, 805: 6408}, + {961, 961, 9: 961}, + {960, 960, 9: 960}, + // 3555 + {555: 6085, 717: 6086, 892: 6410}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6411}, + {965, 965, 9: 965}, + {233: 6368, 555: 1977, 716: 1977, 1977, 729: 6369, 1023: 6367, 1087: 6413}, + {1003, 1003, 9: 6381}, + // 3560 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6424}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6416}, + {555: 6085, 717: 6086, 892: 6422}, + {544: 6419, 555: 971, 716: 6418, 971}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6421}, + // 3565 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6417, 3051, 3052, 3050, 1001: 6420}, + {555: 969, 717: 969}, + {555: 970, 717: 970}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6423}, + {999, 999}, + // 3570 + {555: 6085, 717: 6086, 892: 6425}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 6287, 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 6286, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6288, 939: 6426}, + {1000, 1000}, + {712: 6436}, + {712: 6429}, + // 3575 + {326: 6430}, + {555: 6431}, + {533: 6432}, + {558: 6433}, + {325: 6434}, + // 3580 + {533: 6435}, + {1007, 1007}, + {326: 6437}, + {555: 6438}, + {533: 6439}, + // 3585 + {558: 6440}, + {325: 6441}, + {533: 6442}, + {1008, 1008}, + {531: 2907, 547: 2905, 608: 2904, 695: 2900, 775: 6454, 806: 6453, 2901, 2902, 2903, 6455}, + // 3590 + {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6452}, + {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6451}, + {531: 1439, 547: 1439, 608: 1439, 695: 1439, 715: 4174, 829: 4172, 4173, 885: 6447, 887: 6448, 1039: 6450, 1081: 6449}, + {2: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 10: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 58: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 531: 1442, 533: 1442, 1442, 1442, 1442, 540: 1442, 1442, 543: 1442, 1442, 1442, 547: 1442, 1442, 552: 1442, 1442, 560: 1442, 573: 1442, 578: 1442, 584: 1442, 586: 1442, 1442, 608: 1442, 614: 1442, 617: 1442, 628: 1442, 1442, 1442, 1442, 636: 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 1442, 697: 1442, 1442, 1442, 1442, 711: 1442, 715: 1442, 829: 1442, 1442, 836: 1442, 1442, 1442, 840: 1442, 849: 1442, 1442, 1442}, + {531: 1438, 547: 1438, 608: 1438, 695: 1438}, + // 3595 + {531: 1010, 547: 1010, 608: 1010, 695: 1010}, + {531: 1009, 547: 1009, 608: 1009, 695: 1009}, + {531: 1011, 547: 1011, 608: 1011, 695: 1011}, + {531: 1012, 547: 1012, 608: 1012, 695: 1012}, + {1024, 1024, 57: 1024, 530: 1024, 532: 1024, 539: 1014, 542: 1024, 550: 1014, 1014}, + // 3600 + {1023, 1023, 57: 1023, 530: 1023, 532: 1023, 539: 1013, 542: 1023, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 6456, 6457}, + {539: 1015, 550: 1015, 1015}, + {1022, 1022, 57: 1022, 530: 1022, 532: 1022, 542: 1022, 554: 3859, 556: 3858, 843: 6458}, + {1021, 1021, 57: 1021, 530: 1021, 532: 1021, 542: 1021}, + {1020, 1020, 57: 1020, 530: 1020, 532: 1020, 542: 1020}, + // 3605 + {57: 3989, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {9: 6474, 531: 1196, 547: 1196, 608: 1196, 695: 1196, 714: 1196, 803: 1196}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6462, 1308: 6473}, + {9: 1193, 531: 1193, 547: 1193, 608: 1193, 695: 1193, 714: 1193, 803: 1193}, + {531: 6464, 537: 2606, 1370: 6465}, + // 3610 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 6468}, + {537: 6466}, + {531: 2907, 775: 6467}, + {9: 1192, 531: 1192, 547: 1192, 608: 1192, 695: 1192, 714: 1192, 803: 1192}, + {9: 6471, 57: 6470}, + // 3615 + {2604, 2604, 9: 2604, 57: 2604, 532: 2604}, + {537: 2605}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6472, 3051, 3052, 3050}, + {2603, 2603, 9: 2603, 57: 2603, 532: 2603}, + {9: 6474, 531: 1195, 547: 1195, 608: 1195, 695: 1195, 714: 1195, 803: 1195}, + // 3620 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6463, 3051, 3052, 3050, 1034: 6475}, + {9: 1194, 531: 1194, 547: 1194, 608: 1194, 695: 1194, 714: 1194, 803: 1194}, + {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6477}, + {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6478}, + {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6479}, + // 3625 + {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6480}, + {1200, 1200, 57: 1200, 530: 1200, 532: 1200, 539: 1200, 542: 1200, 550: 1200, 1200}, + {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6482}, + {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6483}, + {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6484}, + // 3630 + {1201, 1201, 57: 1201, 530: 1201, 532: 1201, 539: 1201, 542: 1201, 550: 1201, 1201}, + {723: 6492}, + {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6488}, + {1046, 1046, 57: 1046, 530: 1046, 532: 1046, 539: 1046, 542: 1046, 550: 1046, 1046, 554: 1046, 556: 1046, 558: 1046, 1046, 562: 1046, 564: 1046, 572: 1046, 574: 1046}, + {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6489}, + // 3635 + {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6490}, + {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6491}, + {1202, 1202, 57: 1202, 530: 1202, 532: 1202, 539: 1202, 542: 1202, 550: 1202, 1202}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3881, 973: 3883, 1000: 6493}, + {2127, 2127, 9: 3884, 57: 2127, 530: 2127, 532: 6494, 539: 2127, 542: 2127, 550: 2127, 2127, 554: 2127, 556: 2127, 558: 2127, 2127, 562: 2127, 564: 2127, 572: 2127, 574: 2127, 1510: 6495}, + // 3640 + {432: 6496}, + {2125, 2125, 57: 2125, 530: 2125, 532: 2125, 539: 2125, 542: 2125, 550: 2125, 2125, 554: 2125, 556: 2125, 558: 2125, 2125, 562: 2125, 564: 2125, 572: 2125, 574: 2125}, + {2126, 2126, 57: 2126, 530: 2126, 532: 2126, 539: 2126, 542: 2126, 550: 2126, 2126, 554: 2126, 556: 2126, 558: 2126, 2126, 562: 2126, 564: 2126, 572: 2126, 574: 2126}, + {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 562: 424, 4661, 424, 571: 424, 889: 4662, 6522}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 6507, 1351: 6506, 1480: 6505}, + // 3645 + {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1050: 6487, 1080: 6500}, + {1499, 1499, 57: 1499, 530: 1499, 532: 1499, 539: 1499, 542: 1499, 550: 1499, 1499, 554: 1499, 556: 1499, 558: 1499, 1499, 562: 1499, 564: 3857, 842: 3911, 909: 6501}, + {1068, 1068, 57: 1068, 530: 1068, 532: 1068, 539: 1068, 542: 1068, 550: 1068, 1068, 554: 3859, 556: 3858, 558: 1068, 1068, 562: 1068, 843: 3916, 925: 6502}, + {1039, 1039, 57: 1039, 530: 1039, 532: 1039, 539: 1039, 542: 1039, 550: 1039, 1039, 558: 3918, 1039, 562: 3919, 991: 6503}, + {1045, 1045, 57: 1045, 530: 1045, 532: 1045, 539: 1045, 542: 1045, 550: 1045, 1045, 559: 3944, 992: 6504}, + // 3650 + {1203, 1203, 57: 1203, 530: 1203, 532: 1203, 539: 1203, 542: 1203, 550: 1203, 1203}, + {424, 424, 57: 424, 530: 424, 532: 424, 539: 424, 542: 424, 550: 424, 424, 554: 424, 556: 424, 558: 424, 424, 562: 424, 4661, 424, 571: 424, 424, 574: 424, 889: 4662, 6508}, + {1191, 1191, 57: 1191, 530: 1191, 532: 1191, 539: 1191, 542: 1191, 550: 1191, 1191, 554: 1191, 556: 1191, 558: 1191, 1191, 562: 1191, 1191, 1191, 571: 1191}, + {1131, 1131, 9: 6019, 57: 1131, 530: 1131, 532: 1131, 539: 1131, 542: 1131, 550: 1131, 1131, 554: 1131, 556: 1131, 558: 1131, 1131, 562: 1131, 1131, 1131, 571: 1131, 1131, 574: 1131}, + {1047, 1047, 57: 1047, 530: 1047, 532: 1047, 539: 1047, 542: 1047, 550: 1047, 1047, 554: 1047, 556: 1047, 558: 1047, 1047, 562: 1047, 564: 1047, 571: 6485, 1047, 574: 1047, 1050: 6487, 1080: 6509}, + // 3655 + {2124, 2124, 57: 2124, 530: 2124, 532: 2124, 539: 2124, 542: 2124, 550: 2124, 2124, 554: 2124, 556: 2124, 558: 2124, 2124, 562: 2124, 564: 2124, 572: 6510, 574: 2124, 1187: 6511}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6521}, + {1190, 1190, 57: 1190, 530: 1190, 532: 1190, 539: 1190, 542: 1190, 550: 1190, 1190, 554: 1190, 556: 1190, 558: 1190, 1190, 562: 1190, 564: 1190, 574: 6513, 1502: 6512}, + {1216, 1216, 57: 1216, 530: 1216, 532: 1216, 539: 1216, 542: 1216, 550: 1216, 1216, 554: 1216, 556: 1216, 558: 1216, 1216, 562: 1216, 564: 1216}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4066, 3051, 3052, 3050, 1026: 6516, 1304: 6515, 1503: 6514}, + // 3660 + {1189, 1189, 9: 6519, 57: 1189, 530: 1189, 532: 1189, 539: 1189, 542: 1189, 550: 1189, 1189, 554: 1189, 556: 1189, 558: 1189, 1189, 562: 1189, 564: 1189}, + {1188, 1188, 9: 1188, 57: 1188, 530: 1188, 532: 1188, 539: 1188, 542: 1188, 550: 1188, 1188, 554: 1188, 556: 1188, 558: 1188, 1188, 562: 1188, 564: 1188}, + {537: 6517}, + {531: 4067, 1306: 6518}, + {1186, 1186, 9: 1186, 57: 1186, 530: 1186, 532: 1186, 539: 1186, 542: 1186, 550: 1186, 1186, 554: 1186, 556: 1186, 558: 1186, 1186, 562: 1186, 564: 1186}, + // 3665 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4066, 3051, 3052, 3050, 1026: 6516, 1304: 6520}, + {1187, 1187, 9: 1187, 57: 1187, 530: 1187, 532: 1187, 539: 1187, 542: 1187, 550: 1187, 1187, 554: 1187, 556: 1187, 558: 1187, 1187, 562: 1187, 564: 1187}, + {2123, 2123, 57: 2123, 530: 2123, 532: 2123, 539: 2123, 542: 2123, 550: 2123, 2123, 554: 2123, 556: 2123, 558: 2123, 2123, 561: 2123, 2123, 2123, 2123, 566: 3745, 3743, 3744, 3742, 3740, 2123, 574: 2123, 800: 3741, 3739}, + {1217, 1217, 57: 1217, 530: 1217, 532: 1217, 539: 1217, 542: 1217, 550: 1217, 1217, 554: 1217, 556: 1217, 558: 1217, 1217, 562: 1217, 564: 1217, 571: 1217}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 6539, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 6540, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6538, 1171: 6541, 1361: 6542, 1446: 6543}, + // 3670 + {2: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 10: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 58: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 531: 1066, 533: 1066, 1066, 1066, 1066, 540: 1066, 1066, 543: 1066, 1066, 1066, 547: 1066, 1066, 552: 1066, 1066, 560: 1066, 573: 1066, 578: 1066, 584: 1066, 586: 1066, 1066, 614: 1066, 617: 1066, 628: 1066, 1066, 1066, 1066, 636: 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 1066, 697: 1066, 1066, 1066, 1066, 711: 1066, 715: 1066, 829: 1066, 1066, 836: 1066, 1066, 1066, 840: 1066, 849: 1066, 1066, 1066}, + {2: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 10: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 58: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 531: 1065, 533: 1065, 1065, 1065, 1065, 540: 1065, 1065, 543: 1065, 1065, 1065, 547: 1065, 1065, 552: 1065, 1065, 560: 1065, 573: 1065, 578: 1065, 584: 1065, 586: 1065, 1065, 614: 1065, 617: 1065, 628: 1065, 1065, 1065, 1065, 636: 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 1065, 697: 1065, 1065, 1065, 1065, 711: 1065, 715: 1065, 829: 1065, 1065, 836: 1065, 1065, 1065, 840: 1065, 849: 1065, 1065, 1065}, + {2: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 10: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 58: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 531: 1064, 533: 1064, 1064, 1064, 1064, 540: 1064, 1064, 543: 1064, 1064, 1064, 547: 1064, 1064, 552: 1064, 1064, 560: 1064, 573: 1064, 578: 1064, 584: 1064, 586: 1064, 1064, 614: 1064, 617: 1064, 628: 1064, 1064, 1064, 1064, 636: 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 1064, 697: 1064, 1064, 1064, 1064, 711: 1064, 715: 1064, 829: 1064, 1064, 836: 1064, 1064, 1064, 840: 1064, 849: 1064, 1064, 1064}, + {2: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 10: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 58: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 531: 1063, 533: 1063, 1063, 1063, 1063, 540: 1063, 1063, 543: 1063, 1063, 1063, 547: 1063, 1063, 552: 1063, 1063, 560: 1063, 573: 1063, 578: 1063, 584: 1063, 586: 1063, 1063, 614: 1063, 617: 1063, 628: 1063, 1063, 1063, 1063, 636: 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 1063, 697: 1063, 1063, 1063, 1063, 711: 1063, 715: 1063, 829: 1063, 1063, 836: 1063, 1063, 1063, 840: 1063, 849: 1063, 1063, 1063}, + {2: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 10: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 58: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 531: 1062, 533: 1062, 1062, 1062, 1062, 540: 1062, 1062, 543: 1062, 1062, 1062, 547: 1062, 1062, 552: 1062, 1062, 560: 1062, 573: 1062, 578: 1062, 584: 1062, 586: 1062, 1062, 614: 1062, 617: 1062, 628: 1062, 1062, 1062, 1062, 636: 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 1062, 697: 1062, 1062, 1062, 1062, 711: 1062, 715: 1062, 829: 1062, 1062, 836: 1062, 1062, 1062, 840: 1062, 849: 1062, 1062, 1062}, + // 3675 + {2: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 10: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 58: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 531: 1061, 533: 1061, 1061, 1061, 1061, 540: 1061, 1061, 543: 1061, 1061, 1061, 547: 1061, 1061, 552: 1061, 1061, 560: 1061, 573: 1061, 578: 1061, 584: 1061, 586: 1061, 1061, 614: 1061, 617: 1061, 628: 1061, 1061, 1061, 1061, 636: 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061, 697: 1061, 1061, 1061, 1061, 711: 1061, 715: 1061, 829: 1061, 1061, 836: 1061, 1061, 1061, 840: 1061, 849: 1061, 1061, 1061}, + {2: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 10: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 58: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 531: 1060, 533: 1060, 1060, 1060, 1060, 540: 1060, 1060, 543: 1060, 1060, 1060, 547: 1060, 1060, 552: 1060, 1060, 560: 1060, 573: 1060, 578: 1060, 584: 1060, 586: 1060, 1060, 614: 1060, 617: 1060, 628: 1060, 1060, 1060, 1060, 636: 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 1060, 697: 1060, 1060, 1060, 1060, 711: 1060, 715: 1060, 829: 1060, 1060, 836: 1060, 1060, 1060, 840: 1060, 849: 1060, 1060, 1060}, + {2: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 10: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 58: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 531: 1059, 533: 1059, 1059, 1059, 1059, 540: 1059, 1059, 543: 1059, 1059, 1059, 547: 1059, 1059, 552: 1059, 1059, 560: 1059, 573: 1059, 578: 1059, 584: 1059, 586: 1059, 1059, 614: 1059, 617: 1059, 628: 1059, 1059, 1059, 1059, 636: 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 1059, 697: 1059, 1059, 1059, 1059, 711: 1059, 715: 1059, 829: 1059, 1059, 836: 1059, 1059, 1059, 840: 1059, 849: 1059, 1059, 1059}, + {2: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 10: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 58: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 531: 1058, 533: 1058, 1058, 1058, 1058, 540: 1058, 1058, 543: 1058, 1058, 1058, 547: 1058, 1058, 552: 1058, 1058, 560: 1058, 573: 1058, 578: 1058, 584: 1058, 586: 1058, 1058, 614: 1058, 617: 1058, 628: 1058, 1058, 1058, 1058, 636: 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 1058, 697: 1058, 1058, 1058, 1058, 711: 1058, 715: 1058, 829: 1058, 1058, 836: 1058, 1058, 1058, 840: 1058, 849: 1058, 1058, 1058}, + {2: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 10: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 58: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 6529, 6535, 6536, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 531: 1056, 533: 1056, 1056, 1056, 1056, 540: 1056, 1056, 543: 1056, 1056, 1056, 547: 1056, 1056, 552: 1056, 1056, 560: 1056, 573: 6532, 578: 1056, 584: 1056, 586: 1056, 1056, 614: 1056, 617: 1056, 628: 1056, 1056, 1056, 1056, 636: 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 1056, 697: 1056, 1056, 1056, 1056, 711: 1056, 715: 4174, 829: 4172, 4173, 836: 5961, 5960, 5959, 840: 5956, 849: 6528, 6531, 6527, 885: 6447, 887: 6525, 934: 6526, 969: 6524, 1266: 6537, 6530}, + // 3680 + {2: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 10: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 58: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 531: 1054, 533: 1054, 1054, 1054, 1054, 540: 1054, 1054, 543: 1054, 1054, 1054, 547: 1054, 1054, 552: 1054, 1054, 560: 1054, 573: 1054, 578: 1054, 584: 1054, 586: 1054, 1054, 614: 1054, 617: 1054, 628: 1054, 1054, 1054, 1054, 636: 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054, 697: 1054, 1054, 1054, 1054, 711: 1054, 715: 1054, 829: 1054, 1054, 836: 1054, 1054, 1054, 840: 1054, 849: 1054, 1054, 1054}, + {2: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 10: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 58: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 531: 1050, 533: 1050, 1050, 1050, 1050, 540: 1050, 1050, 543: 1050, 1050, 1050, 547: 1050, 1050, 552: 1050, 1050, 560: 1050, 573: 1050, 578: 1050, 584: 1050, 586: 1050, 1050, 614: 1050, 617: 1050, 628: 1050, 1050, 1050, 1050, 636: 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050, 697: 1050, 1050, 1050, 1050, 711: 1050, 715: 1050, 829: 1050, 1050, 836: 1050, 1050, 1050, 840: 1050, 849: 1050, 1050, 1050}, + {2: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 10: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 58: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 531: 1049, 533: 1049, 1049, 1049, 1049, 540: 1049, 1049, 543: 1049, 1049, 1049, 547: 1049, 1049, 552: 1049, 1049, 560: 1049, 573: 1049, 578: 1049, 584: 1049, 586: 1049, 1049, 614: 1049, 617: 1049, 628: 1049, 1049, 1049, 1049, 636: 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 1049, 697: 1049, 1049, 1049, 1049, 711: 1049, 715: 1049, 829: 1049, 1049, 836: 1049, 1049, 1049, 840: 1049, 849: 1049, 1049, 1049}, + {2: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 10: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 58: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 531: 1055, 533: 1055, 1055, 1055, 1055, 540: 1055, 1055, 543: 1055, 1055, 1055, 547: 1055, 1055, 552: 1055, 1055, 560: 1055, 573: 1055, 578: 1055, 584: 1055, 586: 1055, 1055, 614: 1055, 617: 1055, 628: 1055, 1055, 1055, 1055, 636: 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 1055, 697: 1055, 1055, 1055, 1055, 711: 1055, 715: 1055, 829: 1055, 1055, 836: 1055, 1055, 1055, 840: 1055, 849: 1055, 1055, 1055}, + {2135, 2135, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 2135, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2135, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 2135, 532: 2135, 6556, 537: 6555, 539: 2135, 542: 2135, 550: 2135, 2135, 554: 2135, 556: 2135, 558: 2135, 2135, 561: 2135, 2135, 2135, 2135, 566: 3745, 3743, 3744, 3742, 3740, 2135, 2135, 770: 6554, 3051, 3052, 3050, 800: 3741, 3739, 1358: 6553, 6552}, + // 3685 + {2139, 2139, 9: 2139, 57: 2139, 530: 2139, 532: 2139, 539: 2139, 542: 2139, 550: 2139, 2139, 554: 2139, 556: 2139, 558: 2139, 2139, 561: 2139, 2139, 2139, 2139, 571: 2139, 2139}, + {1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 532: 1481, 1481, 1481, 536: 1481, 1481, 1481, 1481, 542: 1481, 1481, 1481, 1481, 550: 1481, 1481, 554: 1481, 1481, 1481, 558: 1481, 1481, 561: 1481, 1481, 1481, 1481, 566: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 579: 1481, 584: 1481, 606: 1481, 609: 1481, 1481, 1481, 1481, 1481, 615: 1481, 1481, 618: 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 632: 1481, 1481, 1481, 1481, 703: 1481, 716: 6547, 718: 1481, 1481}, + {2129, 2129, 9: 2129, 57: 2129, 530: 2129, 532: 2129, 539: 2129, 542: 2129, 550: 2129, 2129, 554: 2129, 556: 2129, 558: 2129, 2129, 561: 2129, 2129, 2129, 2129, 571: 2129, 2129}, + {1048, 1048, 9: 6545, 57: 1048, 530: 1048, 532: 1048, 539: 1048, 542: 1048, 550: 1048, 1048, 554: 1048, 556: 1048, 558: 1048, 1048, 561: 1048, 1048, 1048, 1048, 571: 1048, 1048}, + {2124, 2124, 57: 2124, 530: 2124, 532: 2124, 539: 2124, 542: 2124, 550: 2124, 2124, 554: 2124, 556: 2124, 558: 2124, 2124, 561: 2124, 2124, 2124, 2124, 571: 2124, 6510, 1187: 6544}, + // 3690 + {1218, 1218, 57: 1218, 530: 1218, 532: 1218, 539: 1218, 542: 1218, 550: 1218, 1218, 554: 1218, 556: 1218, 558: 1218, 1218, 561: 1218, 1218, 1218, 1218, 571: 1218}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 584: 6539, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 6540, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6538, 1171: 6546}, + {2128, 2128, 9: 2128, 57: 2128, 530: 2128, 532: 2128, 539: 2128, 542: 2128, 550: 2128, 2128, 554: 2128, 556: 2128, 558: 2128, 2128, 561: 2128, 2128, 2128, 2128, 571: 2128, 2128}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6548, 770: 6549, 3051, 3052, 3050}, + {2138, 2138, 9: 2138, 57: 2138, 530: 2138, 532: 2138, 539: 2138, 542: 2138, 550: 2138, 2138, 554: 2138, 556: 2138, 558: 2138, 2138, 561: 2138, 2138, 2138, 2138, 571: 2138, 2138}, + // 3695 + {1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 4477, 1480, 1480, 1480, 536: 1480, 1480, 1480, 1480, 542: 1480, 1480, 1480, 1480, 550: 1480, 1480, 554: 1480, 1480, 1480, 558: 1480, 1480, 561: 1480, 1480, 1480, 1480, 566: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 579: 1480, 584: 1480, 606: 1480, 609: 1480, 1480, 1480, 1480, 1480, 615: 1480, 1480, 618: 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 1480, 632: 1480, 1480, 1480, 1480, 703: 1480, 716: 6550, 718: 1480, 1480}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6551, 770: 3907, 3051, 3052, 3050}, + {2137, 2137, 9: 2137, 57: 2137, 530: 2137, 532: 2137, 539: 2137, 542: 2137, 550: 2137, 2137, 554: 2137, 556: 2137, 558: 2137, 2137, 561: 2137, 2137, 2137, 2137, 571: 2137, 2137}, + {2136, 2136, 9: 2136, 57: 2136, 530: 2136, 532: 2136, 539: 2136, 542: 2136, 550: 2136, 2136, 554: 2136, 556: 2136, 558: 2136, 2136, 561: 2136, 2136, 2136, 2136, 571: 2136, 2136}, + {2134, 2134, 9: 2134, 57: 2134, 530: 2134, 532: 2134, 539: 2134, 542: 2134, 550: 2134, 2134, 554: 2134, 556: 2134, 558: 2134, 2134, 561: 2134, 2134, 2134, 2134, 571: 2134, 2134}, + // 3700 + {2133, 2133, 9: 2133, 57: 2133, 530: 2133, 532: 2133, 539: 2133, 542: 2133, 550: 2133, 2133, 554: 2133, 556: 2133, 558: 2133, 2133, 561: 2133, 2133, 2133, 2133, 571: 2133, 2133}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6558, 770: 6557, 3051, 3052, 3050}, + {2131, 2131, 9: 2131, 57: 2131, 530: 2131, 532: 2131, 539: 2131, 542: 2131, 550: 2131, 2131, 554: 2131, 556: 2131, 558: 2131, 2131, 561: 2131, 2131, 2131, 2131, 571: 2131, 2131}, + {2132, 2132, 9: 2132, 57: 2132, 530: 2132, 532: 2132, 539: 2132, 542: 2132, 550: 2132, 2132, 554: 2132, 556: 2132, 558: 2132, 2132, 561: 2132, 2132, 2132, 2132, 571: 2132, 2132}, + {2130, 2130, 9: 2130, 57: 2130, 530: 2130, 532: 2130, 539: 2130, 542: 2130, 550: 2130, 2130, 554: 2130, 556: 2130, 558: 2130, 2130, 561: 2130, 2130, 2130, 2130, 571: 2130, 2130}, + // 3705 + {1219, 1219}, + {1231, 1231}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 6574, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6575, 3051, 3052, 3050}, + {86: 6567, 287: 6566}, + {1223, 1223}, + // 3710 + {897: 6565}, + {1222, 1222}, + {1225, 1225, 86: 6572}, + {287: 6568}, + {1224, 1224, 86: 6570, 897: 6569}, + // 3715 + {1227, 1227}, + {897: 6571}, + {1226, 1226}, + {897: 6573}, + {1228, 1228}, + // 3720 + {1898, 1898, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6576, 3051, 3052, 3050}, + {1230, 1230}, + {1229, 1229}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6578, 3051, 3052, 3050}, + {1235, 1235}, + // 3725 + {1239, 1239, 542: 6580}, + {628: 3682, 776: 6582, 1488: 6581}, + {1238, 1238, 9: 6583}, + {1237, 1237, 9: 1237}, + {628: 3682, 776: 6584}, + // 3730 + {1236, 1236, 9: 1236}, + {561: 6586}, + {533: 6588, 628: 3682, 776: 6589, 1422: 6587}, + {1242, 1242}, + {1241, 1241}, + // 3735 + {1240, 1240}, + {2: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 10: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 58: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 559: 6591, 1196: 6592}, + {2: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 10: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 58: 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554, 1554}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6593}, + {186: 1121, 531: 1121, 1121, 546: 6023, 1121, 557: 1121, 608: 1121, 695: 1121, 963: 6594}, + // 3740 + {186: 6602, 531: 6595, 2906, 547: 6603, 557: 6601, 608: 2904, 695: 2900, 775: 6600, 806: 6598, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6599, 6597, 1095: 6596, 1195: 6604}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 2608, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 770: 4024, 3051, 3052, 3050, 775: 6459, 806: 3847, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 3849, 3848, 821: 4025, 906: 5623, 1123: 6617}, + {531: 3893, 938: 6614, 1093: 6613}, + {1547, 1547, 530: 1547, 542: 1547}, + {1546, 1546, 530: 1546, 539: 1014, 542: 1546, 550: 1014, 1014}, + // 3745 + {1545, 1545, 530: 1545, 542: 1545}, + {1544, 1544, 530: 1544, 539: 1013, 542: 1544, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6606, 1337: 6605}, + {531: 1542}, + {531: 1541, 639: 3892, 1013: 3891, 1094: 3890}, + // 3750 + {1527, 1527, 542: 1527}, + {1543, 1543, 9: 6609, 530: 1543, 542: 1543}, + {555: 6085, 717: 6086, 892: 6607}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6608}, + {1531, 1531, 9: 1531, 530: 1531, 542: 1531}, + // 3755 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6610}, + {555: 6085, 717: 6086, 892: 6611}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3899, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3895, 893: 6612}, + {1530, 1530, 9: 1530, 530: 1530, 542: 1530}, + {1548, 1548, 9: 6615, 530: 1548, 542: 1548}, + // 3760 + {1540, 1540, 9: 1540, 530: 1540, 542: 1540}, + {531: 3893, 938: 6616}, + {1539, 1539, 9: 1539, 530: 1539, 542: 1539}, + {57: 6618}, + {186: 6602, 531: 2907, 2906, 547: 6603, 608: 2904, 695: 2900, 775: 6623, 806: 6621, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6622, 6620, 1095: 6619}, + // 3765 + {531: 3893, 938: 6614, 1093: 6624}, + {1552, 1552, 530: 1552, 542: 1552}, + {1551, 1551, 530: 1551, 539: 1014, 542: 1551, 550: 1014, 1014}, + {1550, 1550, 530: 1550, 542: 1550}, + {1549, 1549, 530: 1549, 539: 1013, 542: 1549, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + // 3770 + {1553, 1553, 9: 6615, 530: 1553, 542: 1553}, + {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 559: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 6626}, + {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 559: 2115, 958: 6627}, + {2: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 10: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 58: 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 1555, 559: 6591, 1196: 6628}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6629}, + // 3775 + {186: 1121, 531: 1121, 1121, 546: 6023, 1121, 557: 1121, 608: 1121, 695: 1121, 963: 6630}, + {186: 6602, 531: 6595, 2906, 547: 6603, 557: 6601, 608: 2904, 695: 2900, 775: 6600, 806: 6598, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 6599, 6597, 1095: 6596, 1195: 6631}, + {1529, 1529, 530: 6633, 542: 1529, 1398: 6632}, + {1556, 1556, 542: 1556}, + {304: 6634}, + // 3780 + {701: 6635}, + {714: 6636}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6074, 996: 6075, 1027: 6637}, + {1528, 1528, 9: 6077, 542: 1528}, + {1560, 1560, 531: 6646, 716: 2090}, + // 3785 + {1561, 1561}, + {716: 6641}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6642, 3051, 3052, 3050}, + {1559, 1559, 531: 6643}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 6644}, + // 3790 + {57: 6645}, + {1557, 1557}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 2184, 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 3842, 863: 4345, 921: 6647}, + {57: 6648}, + {1558, 1558}, + // 3795 + {2: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 10: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 58: 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 2338, 535: 2338, 538: 2338, 553: 2338, 557: 2338, 578: 2338, 584: 2338, 709: 2338}, + {561: 6746}, + {561: 6660}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6655, 770: 5954, 3051, 3052, 3050, 907: 6657, 1347: 6656}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 6654}, + // 3800 + {9: 3930, 561: 2269, 712: 2269}, + {561: 2271, 712: 2271}, + {9: 6658, 561: 2270, 712: 2270}, + {9: 2268, 561: 2268, 712: 2268}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6659}, + // 3805 + {9: 2267, 561: 2267, 712: 2267}, + {533: 6661}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6662}, + {2272, 2272, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + {2265, 2265, 17: 2265, 58: 2265, 60: 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 2265, 530: 2265, 713: 2265}, + // 3810 + {555: 2264, 560: 2264}, + {555: 2263, 560: 2263}, + {555: 2262, 560: 2262, 629: 2262, 2262}, + {555: 2261, 560: 2261, 629: 2261, 2261}, + {555: 2260, 560: 2260, 629: 2260, 2260}, + // 3815 + {555: 2259, 560: 2259, 629: 2259, 2259}, + {555: 2258, 560: 2258, 629: 2258, 2258}, + {555: 2257, 560: 2257, 629: 2257, 2257}, + {555: 2256, 560: 2256, 629: 2256, 2256}, + {555: 2255, 560: 2255, 629: 2255, 2255}, + // 3820 + {533: 2254, 555: 2254}, + {533: 2253, 555: 2253}, + {533: 2252, 555: 2252}, + {533: 2251, 555: 2251}, + {2: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 10: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 58: 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 533: 2250, 549: 2250, 552: 2250, 555: 2250}, + // 3825 + {2: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 10: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 58: 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 2249, 533: 2249, 549: 2249, 552: 2249, 555: 2249}, + {304: 6745}, + {555: 4588, 560: 2316, 802: 6743}, + {555: 4588, 560: 2316, 629: 2316, 2316, 802: 6741}, + {533: 2316, 555: 4588, 802: 6739}, + // 3830 + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 549: 2316, 552: 2316, 555: 4588, 802: 6734}, + {533: 2316, 555: 4588, 560: 2316, 802: 6729}, + {533: 2316, 555: 4588, 560: 2316, 802: 6726}, + {555: 4588, 560: 2316, 802: 6721}, + {131: 2316, 154: 2316, 555: 4588, 560: 2316, 802: 6718}, + // 3835 + {237: 2316, 2316, 241: 2316, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 6715}, + {237: 2316, 2316, 241: 2316, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 6706}, + {533: 2316, 555: 4588, 802: 6704}, + {533: 2316, 555: 4588, 802: 6702}, + {533: 2316, 555: 4588, 802: 6700}, + // 3840 + {533: 2316, 555: 4588, 802: 6698}, + {533: 2316, 555: 4588, 802: 6696}, + {533: 6697}, + {2227, 2227, 17: 2227, 58: 2227, 60: 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 2227, 530: 2227, 713: 2227}, + {533: 6699}, + // 3845 + {2228, 2228, 17: 2228, 58: 2228, 60: 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 2228, 530: 2228, 713: 2228}, + {533: 6701}, + {2229, 2229, 17: 2229, 58: 2229, 60: 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 2229, 530: 2229, 713: 2229}, + {533: 6703}, + {2230, 2230, 17: 2230, 58: 2230, 60: 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 2230, 530: 2230, 713: 2230}, + // 3850 + {533: 6705}, + {2231, 2231, 17: 2231, 58: 2231, 60: 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231, 530: 2231, 713: 2231}, + {237: 6712, 6713, 241: 6714, 560: 3037, 629: 6710, 6711, 799: 6709, 998: 6707, 1225: 6708}, + {2233, 2233, 17: 2233, 58: 2233, 60: 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 2233, 530: 2233, 713: 2233}, + {2232, 2232, 17: 2232, 58: 2232, 60: 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 2232, 530: 2232, 713: 2232}, + // 3855 + {2223, 2223, 9: 2223, 17: 2223, 58: 2223, 60: 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 2223, 110: 2223, 2223, 2223, 2223, 2223, 530: 2223, 713: 2223}, + {2222, 2222, 9: 2222, 17: 2222, 58: 2222, 60: 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 2222, 110: 2222, 2222, 2222, 2222, 2222, 530: 2222, 713: 2222}, + {2221, 2221, 9: 2221, 17: 2221, 58: 2221, 60: 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 2221, 110: 2221, 2221, 2221, 2221, 2221, 530: 2221, 713: 2221}, + {2220, 2220, 17: 2220, 58: 2220, 60: 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 2220, 530: 2220, 713: 2220}, + {2219, 2219, 17: 2219, 58: 2219, 60: 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 2219, 530: 2219, 713: 2219}, + // 3860 + {2218, 2218, 17: 2218, 58: 2218, 60: 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 2218, 530: 2218, 713: 2218}, + {237: 6712, 6713, 241: 6714, 560: 3037, 629: 6710, 6711, 799: 6709, 998: 6716, 1225: 6717}, + {2235, 2235, 17: 2235, 58: 2235, 60: 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 2235, 530: 2235, 713: 2235}, + {2234, 2234, 17: 2234, 58: 2234, 60: 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 2234, 530: 2234, 713: 2234}, + {131: 3950, 154: 3949, 560: 3037, 799: 3866, 816: 6720, 930: 6719}, + // 3865 + {2237, 2237, 17: 2237, 58: 2237, 60: 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 2237, 530: 2237, 713: 2237}, + {2236, 2236, 17: 2236, 58: 2236, 60: 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 2236, 530: 2236, 713: 2236}, + {560: 3037, 799: 3866, 816: 6722}, + {263: 6723}, + {609: 6724}, + // 3870 + {137: 6725}, + {2238, 2238, 17: 2238, 58: 2238, 60: 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238, 530: 2238, 713: 2238}, + {533: 6727, 560: 3037, 799: 3866, 816: 6728}, + {2240, 2240, 17: 2240, 58: 2240, 60: 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 2240, 530: 2240, 713: 2240}, + {2239, 2239, 17: 2239, 58: 2239, 60: 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 2239, 530: 2239, 713: 2239}, + // 3875 + {533: 6731, 560: 3037, 799: 3866, 816: 6730}, + {2241, 2241, 17: 2241, 58: 2241, 60: 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 2241, 118: 3767, 127: 3775, 133: 3763, 137: 3760, 3762, 3759, 3761, 3765, 3766, 3771, 3770, 3769, 3773, 3774, 3768, 3772, 3764, 530: 2241, 713: 2241, 886: 6732}, + {2242, 2242, 17: 2242, 58: 2242, 60: 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 2242, 530: 2242, 713: 2242}, + {365: 6733}, + {2243, 2243, 17: 2243, 58: 2243, 60: 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243, 530: 2243, 713: 2243}, + // 3880 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 549: 6737, 552: 6738, 770: 3737, 3051, 3052, 3050, 805: 6736, 1473: 6735}, + {2244, 2244, 17: 2244, 58: 2244, 60: 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 2244, 530: 2244, 713: 2244}, + {433, 433, 17: 433, 58: 433, 60: 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 433, 530: 433, 713: 433}, + {432, 432, 17: 432, 58: 432, 60: 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 530: 432, 713: 432}, + {431, 431, 17: 431, 58: 431, 60: 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 431, 530: 431, 713: 431}, + // 3885 + {533: 6740}, + {2245, 2245, 17: 2245, 58: 2245, 60: 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 2245, 530: 2245, 713: 2245}, + {560: 3037, 629: 6710, 6711, 799: 6709, 998: 6742}, + {2246, 2246, 17: 2246, 58: 2246, 60: 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 2246, 530: 2246, 713: 2246}, + {560: 3037, 799: 3866, 816: 6744}, + // 3890 + {2247, 2247, 17: 2247, 58: 2247, 60: 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247, 530: 2247, 713: 2247}, + {2: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 10: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 58: 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 2248, 533: 2248, 549: 2248, 552: 2248, 555: 2248}, + {533: 6747}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6748}, + {2273, 2273, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + // 3895 + {178: 6753}, + {178: 6751}, + {560: 3037, 799: 4538, 825: 6752}, + {2215, 2215}, + {560: 3037, 799: 4538, 825: 6754}, + // 3900 + {2275, 2275}, + {155: 6926, 323: 6927}, + {178: 6922}, + {793, 793, 563: 6919, 579: 6918, 1454: 6917}, + {18: 6902, 51: 6903, 132: 6899, 217: 6904, 244: 6901, 608: 6898, 644: 6900, 964: 6905}, + // 3905 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 6887, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6888}, + {872, 872, 558: 6882}, + {156: 6881}, + {131: 3950, 154: 3949, 157: 6876, 268: 6875, 930: 6877}, + {868, 868}, + // 3910 + {858, 858, 236: 6857, 280: 6858, 292: 6859, 295: 6856, 318: 6861, 328: 6860, 342: 6863, 345: 6862, 554: 858, 556: 858, 558: 858, 715: 6864, 1272: 6855, 1457: 6854, 6853}, + {866, 866}, + {865, 865}, + {796, 796, 319: 6845, 558: 6844, 563: 796, 579: 796}, + {178: 6841, 219: 6842}, + // 3915 + {561: 841, 606: 841}, + {561: 840, 606: 840}, + {561: 839, 606: 839}, + {836, 836, 563: 836, 579: 836}, + {835, 835, 563: 835, 579: 835}, + // 3920 + {834, 834, 563: 834, 579: 834}, + {833, 833, 563: 833, 579: 833}, + {157: 6839}, + {561: 6809, 606: 6810, 903: 6834}, + {131: 783, 154: 783, 260: 6807, 1222: 6828}, + // 3925 + {531: 6823}, + {824, 824, 563: 824, 579: 824}, + {822, 822, 563: 822, 579: 822}, + {156: 6821, 180: 6822, 248: 6820}, + {818, 818, 563: 818, 579: 818}, + // 3930 + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6819}, + {156: 6818}, + {156: 6817}, + {156: 6816}, + {156: 6815}, + // 3935 + {156: 6814}, + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6811}, + {810, 810, 563: 810, 579: 810}, + {809, 809, 563: 809, 579: 809}, + {808, 808, 563: 808, 579: 808}, + // 3940 + {807, 807, 563: 807, 579: 807}, + {806, 806, 563: 806, 579: 806}, + {805, 805, 563: 805, 579: 805}, + {804, 804, 563: 804, 579: 804}, + {803, 803, 563: 803, 579: 803}, + // 3945 + {802, 802, 563: 802, 579: 802}, + {801, 801, 563: 801, 579: 801}, + {800, 800, 563: 800, 579: 800}, + {156: 6808}, + {798, 798, 563: 798, 579: 798}, + // 3950 + {797, 797, 563: 797, 579: 797}, + {156: 789, 180: 789, 248: 789}, + {156: 788, 180: 788, 202: 788, 248: 788}, + {131: 782, 154: 782, 157: 782, 268: 782}, + {799, 799, 563: 799, 579: 799}, + // 3955 + {2: 838, 838, 838, 838, 838, 838, 838, 10: 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 58: 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838, 838}, + {2: 837, 837, 837, 837, 837, 837, 837, 10: 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 58: 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837, 837}, + {811, 811, 563: 811, 579: 811}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6813}, + {780, 780, 563: 780, 579: 780}, + // 3960 + {812, 812, 563: 812, 579: 812}, + {813, 813, 563: 813, 579: 813}, + {814, 814, 563: 814, 579: 814}, + {815, 815, 563: 815, 579: 815}, + {816, 816, 563: 816, 579: 816}, + // 3965 + {817, 817, 563: 817, 579: 817}, + {821, 821, 563: 821, 579: 821}, + {820, 820, 563: 820, 579: 820}, + {819, 819, 563: 819, 579: 819}, + {584: 6824}, + // 3970 + {57: 6825}, + {314: 6827, 362: 6826}, + {825, 825, 563: 825, 579: 825}, + {823, 823, 563: 823, 579: 823}, + {131: 3950, 154: 3949, 930: 6829}, + // 3975 + {561: 6809, 606: 6810, 903: 6831, 1274: 6830}, + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6833}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6832}, + {779, 779, 561: 779, 563: 779, 579: 779, 606: 779}, + {826, 826, 563: 826, 579: 826}, + // 3980 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6835, 3051, 3052, 3050, 804: 6836}, + {1254, 1254, 561: 6809, 563: 1254, 579: 1254, 606: 6810, 716: 3932, 903: 6837}, + {829, 829, 563: 829, 579: 829}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6838, 3051, 3052, 3050}, + {828, 828, 563: 828, 579: 828}, + // 3985 + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6840}, + {831, 831, 563: 831, 579: 831}, + {560: 3037, 799: 4538, 825: 6843}, + {794, 794, 563: 794, 579: 794}, + {863, 863}, + // 3990 + {608: 6848, 644: 6649, 929: 6847, 1455: 6846}, + {795, 795, 563: 795, 579: 795}, + {864, 864}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6852}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6849}, + // 3995 + {860, 860, 546: 6850}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6851, 3051, 3052, 3050}, + {859, 859}, + {861, 861}, + {845, 845, 554: 845, 556: 845, 558: 6871, 1456: 6870}, + // 4000 + {857, 857, 9: 6868, 554: 857, 556: 857, 558: 857}, + {856, 856, 9: 856, 554: 856, 556: 856, 558: 856}, + {854, 854, 9: 854, 554: 854, 556: 854, 558: 854}, + {853, 853, 9: 853, 554: 853, 556: 853, 558: 853}, + {401: 6867}, + // 4005 + {443: 6866}, + {392: 6865}, + {849, 849, 9: 849, 554: 849, 556: 849, 558: 849}, + {848, 848, 9: 848, 554: 848, 556: 848, 558: 848}, + {847, 847, 9: 847, 554: 847, 556: 847, 558: 847}, + // 4010 + {846, 846, 9: 846, 554: 846, 556: 846, 558: 846}, + {850, 850, 9: 850, 554: 850, 556: 850, 558: 850}, + {851, 851, 9: 851, 554: 851, 556: 851, 558: 851}, + {852, 852, 9: 852, 554: 852, 556: 852, 558: 852}, + {236: 6857, 280: 6858, 292: 6859, 295: 6856, 318: 6861, 328: 6860, 342: 6863, 345: 6862, 715: 6864, 1272: 6869}, + // 4015 + {855, 855, 9: 855, 554: 855, 556: 855, 558: 855}, + {1068, 1068, 554: 3859, 556: 3858, 843: 3916, 925: 6874}, + {159: 6872}, + {560: 3037, 799: 4538, 825: 6873}, + {844, 844, 554: 844, 556: 844}, + // 4020 + {867, 867}, + {869, 869}, + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6880}, + {561: 6809, 606: 6810, 903: 6831, 1274: 6878}, + {781, 781, 561: 6809, 563: 781, 579: 781, 606: 6810, 903: 6812, 940: 6879}, + // 4025 + {827, 827, 563: 827, 579: 827}, + {832, 832, 563: 832, 579: 832}, + {870, 870}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6883}, + {843, 843, 542: 6885, 1489: 6884}, + // 4030 + {871, 871}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 6886}, + {842, 842, 9: 6304}, + {781, 781, 108: 1989, 211: 1989, 546: 1989, 561: 6809, 563: 781, 579: 781, 606: 6810, 710: 1989, 716: 1989, 903: 6812, 940: 6897}, + {108: 1121, 211: 6890, 546: 6023, 710: 1121, 963: 6889}, + // 4035 + {108: 6891, 710: 6892}, + {874, 874}, + {424, 424, 563: 4661, 889: 4662, 6896}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6893, 3051, 3052, 3050}, + {108: 6894}, + // 4040 + {424, 424, 563: 4661, 889: 4662, 6895}, + {873, 873}, + {875, 875}, + {830, 830, 563: 830, 579: 830}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6916}, + // 4045 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6915}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 6913}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6912}, + {214: 6910}, + {571: 6908}, + // 4050 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 6907}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 6906}, + {862, 862}, + {876, 876}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 6909}, + // 4055 + {877, 877}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 6911}, + {878, 878}, + {879, 879}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 6914}, + // 4060 + {880, 880}, + {881, 881}, + {882, 882}, + {883, 883}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3682, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3616, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 6921, 3597, 3679, 3596, 3593}, + // 4065 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 6920}, + {791, 791, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {792, 792, 538: 3693, 703: 3694}, + {159: 6924, 560: 3037, 799: 4538, 825: 6923}, + {2277, 2277}, + // 4070 + {560: 3037, 799: 4538, 825: 6925}, + {2276, 2276}, + {156: 6930, 323: 6931}, + {561: 6928}, + {533: 6929}, + // 4075 + {2274, 2274}, + {2279, 2279}, + {561: 6932}, + {533: 6933}, + {2278, 2278}, + // 4080 + {155: 6935}, + {561: 6936}, + {533: 6937}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6938}, + {2280, 2280, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + // 4085 + {155: 6944}, + {22: 6941}, + {178: 6942}, + {560: 3037, 799: 4538, 825: 6943}, + {2216, 2216}, + // 4090 + {2281, 2281}, + {155: 6950}, + {22: 6947}, + {178: 6948}, + {560: 3037, 799: 4538, 825: 6949}, + // 4095 + {2217, 2217}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6951}, + {2282, 2282, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + {155: 6953}, + {2283, 2283}, + // 4100 + {712: 6959}, + {712: 6956}, + {533: 6957}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6958}, + {2284, 2284, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + // 4105 + {533: 6960}, + {2266, 2266, 17: 2266, 58: 2266, 60: 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 2266, 530: 2266, 713: 2266, 949: 6961}, + {2285, 2285, 17: 6689, 58: 6665, 60: 6685, 6678, 6668, 6664, 6672, 6676, 6688, 6671, 6677, 6675, 6673, 6691, 6695, 6686, 6679, 6667, 6687, 6692, 6666, 6669, 6693, 6670, 6674, 6694, 530: 6680, 713: 6690, 945: 6682, 6681, 6684, 6663, 950: 6683}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6963, 3051, 3052, 3050}, + {2286, 2286}, + // 4110 + {2287, 2287}, + {2306, 2306, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 6998}, + {2304, 2304}, + {28: 6996}, + {2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 10: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 58: 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 555: 6985, 716: 2024}, + // 4115 + {232: 6971, 531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6970}, + {2298, 2298}, + {555: 6972}, + {172: 6976, 282: 6979, 301: 6978, 346: 6982, 358: 6975, 6981, 361: 6980, 533: 6974, 639: 6977, 1170: 6973}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6984}, + // 4120 + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6983}, + {531: 2295, 2295, 547: 2295, 552: 2295, 558: 2295, 587: 2295, 608: 2295, 695: 2295, 714: 2295, 724: 2295, 803: 2295}, + {531: 2294, 2294, 547: 2294, 552: 2294, 558: 2294, 587: 2294, 608: 2294, 695: 2294, 714: 2294, 724: 2294, 803: 2294}, + {531: 2293, 2293, 547: 2293, 552: 2293, 558: 2293, 587: 2293, 608: 2293, 695: 2293, 714: 2293, 724: 2293, 803: 2293}, + {531: 2292, 2292, 547: 2292, 552: 2292, 558: 2292, 587: 2292, 608: 2292, 695: 2292, 714: 2292, 724: 2292, 803: 2292}, + // 4125 + {531: 2291, 2291, 547: 2291, 552: 2291, 558: 2291, 587: 2291, 608: 2291, 695: 2291, 714: 2291, 724: 2291, 803: 2291}, + {531: 2290, 2290, 547: 2290, 552: 2290, 558: 2290, 587: 2290, 608: 2290, 695: 2290, 714: 2290, 724: 2290, 803: 2290}, + {531: 2289, 2289, 547: 2289, 552: 2289, 558: 2289, 587: 2289, 608: 2289, 695: 2289, 714: 2289, 724: 2289, 803: 2289}, + {531: 2288, 2288, 547: 2288, 552: 2288, 558: 2288, 587: 2288, 608: 2288, 695: 2288, 714: 2288, 724: 2288, 803: 2288}, + {2296, 2296}, + // 4130 + {2297, 2297}, + {172: 6976, 282: 6979, 301: 6978, 346: 6982, 358: 6975, 6981, 361: 6980, 533: 6986, 639: 6977, 1170: 6987}, + {531: 2907, 2906, 547: 2905, 552: 2891, 558: 6992, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6993}, + {531: 2907, 2906, 547: 2905, 552: 2891, 558: 6988, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 724: 4646, 775: 4647, 803: 2870, 806: 4648, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 4654, 4653, 822: 3018, 2871, 4651, 826: 4652, 4650, 833: 2872, 839: 4649, 904: 4655, 920: 6989}, + {28: 6990}, + // 4135 + {2299, 2299}, + {560: 3037, 799: 6991}, + {2300, 2300}, + {28: 6994}, + {2301, 2301}, + // 4140 + {560: 3037, 799: 6995}, + {2302, 2302}, + {560: 3037, 799: 6997}, + {2303, 2303}, + {2305, 2305}, + // 4145 + {2313, 2313}, + {555: 7025}, + {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 446: 7021, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7020}, + {646, 646, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {648, 648, 539: 1014, 550: 1014, 1014}, + // 4150 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 6265, 6260, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 6266, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 6263, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 6262, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 6268, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 6261, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 6271, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 6269, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 6264, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 553: 4419, 628: 6277, 652: 6276, 709: 4417, 770: 6274, 3051, 3052, 3050, 853: 6278, 926: 6275, 1096: 6279, 1302: 6272}, + {653, 653}, + {652, 652}, + {651, 651}, + {650, 650}, + // 4155 + {649, 649}, + {647, 647}, + {645, 645}, + {644, 644}, + {643, 643}, + // 4160 + {642, 642}, + {641, 641}, + {640, 640}, + {639, 639}, + {638, 638}, + // 4165 + {22: 5771}, + {2311, 2311}, + {555: 7022}, + {533: 7023}, + {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7024}, + // 4170 + {2310, 2310}, + {533: 7026}, + {84: 2863, 2866, 87: 2896, 2864, 195: 2879, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 7004, 587: 2890, 608: 2904, 695: 2900, 713: 2862, 3019, 775: 7002, 803: 2870, 806: 7003, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7010, 7009, 822: 3018, 2871, 7007, 826: 7008, 7006, 833: 2872, 839: 7005, 845: 7018, 7013, 7016, 7017, 894: 7019, 897: 2880, 943: 7012, 962: 7011, 965: 7015, 967: 7014, 1022: 7027}, + {2312, 2312}, + {2: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 10: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 58: 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256, 549: 1256, 561: 1256, 836: 5961, 5960, 5959, 934: 5962, 990: 7029}, + // 4175 + {2: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 10: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 58: 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 6342, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 1244, 549: 1244, 561: 1244, 1247: 7030}, + {2: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 10: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 58: 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 2115, 549: 4675, 561: 2115, 958: 7031}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 561: 7032, 770: 6346, 3051, 3052, 3050, 1018: 6347, 1084: 6345}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7034, 3051, 3052, 3050, 804: 6359, 1018: 6347, 1084: 7033}, + {9: 6355, 542: 7037}, + // 4180 + {1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1246, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 58: 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 537: 1254, 542: 1246, 546: 1254, 549: 1254, 556: 1254, 563: 1254, 1254, 1254, 575: 1254, 716: 7035, 1008: 6348}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 584: 6351, 770: 7036, 3051, 3052, 3050}, + {1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1246, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 58: 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 1253, 537: 1253, 542: 1246, 546: 1253, 549: 1253, 556: 1253, 563: 1253, 1253, 1253, 575: 1253, 716: 6353, 1008: 6352}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5973, 643: 5968, 770: 3927, 3051, 3052, 3050, 775: 5972, 804: 5971, 895: 5970, 899: 5969, 5975, 955: 5965, 993: 7038}, + {424, 424, 9: 6019, 563: 4661, 889: 4662, 7039}, + // 4185 + {2343, 2343}, + {2346, 2346, 9: 3990}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7117, 3051, 3052, 3050}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7115}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7106}, + // 4190 + {710: 7101}, + {157: 6096, 608: 6095, 1294: 7097}, + {202: 789, 216: 6152}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 578: 7092, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7091}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 578: 7088, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 7087}, + // 4195 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 578: 7084, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 7083}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7079, 884: 7078}, + {22: 7075}, + {202: 7067}, + {214: 7064}, + // 4200 + {571: 7061}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7059}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7057}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7058}, + {29, 29}, + // 4205 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7060}, + {165, 165, 9: 3930}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 535: 2119, 578: 4948, 854: 7062}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7063}, + {192, 192}, + // 4210 + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7065}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7066}, + {195, 195}, + {558: 7068}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 7070, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7069}, + // 4215 + {343, 343, 542: 7073}, + {218: 7071}, + {533: 7072}, + {341, 341}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7074}, + // 4220 + {342, 342}, + {178: 7076}, + {560: 3037, 799: 4538, 825: 7077}, + {2214, 2214}, + {2324, 2324, 9: 3930}, + // 4225 + {1252, 1252, 9: 1252, 206: 7081, 546: 7080}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7082}, + {2322, 2322}, + {2323, 2323, 9: 5256}, + {2326, 2326, 9: 6304}, + // 4230 + {645: 7085}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 6303, 6301, 966: 7086}, + {2325, 2325, 9: 6304}, + {2328, 2328, 9: 5856}, + {645: 7089}, + // 4235 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5854, 970: 7090}, + {2327, 2327, 9: 5856}, + {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7096}, + {645: 7093}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7094}, + // 4240 + {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7095}, + {2329, 2329}, + {2330, 2330}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7098}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 3928, 884: 7099}, + // 4245 + {2321, 2321, 9: 3930, 728: 5314, 730: 5313, 1012: 7100}, + {2334, 2334}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7102}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7103, 3051, 3052, 3050}, + {530: 7104}, + // 4250 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7105}, + {2335, 2335}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7107, 3051, 3052, 3050}, + {530: 7108}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7109}, + // 4255 + {2477, 2477, 102: 4719, 562: 4720, 972: 7111, 986: 7110, 1193: 7112}, + {2476, 2476, 102: 4719, 972: 7114}, + {2475, 2475, 562: 4720, 986: 7113}, + {2336, 2336}, + {2473, 2473}, + // 4260 + {2474, 2474}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7116}, + {2337, 2337}, + {2485, 2485}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7589}, + // 4265 + {710: 7577}, + {710: 2471}, + {710: 2470}, + {710: 2469}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7554}, + // 4270 + {18: 7472, 102: 7471, 132: 2363, 181: 2363, 696: 2363, 1492: 7470}, + {552: 7469}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 533: 2117, 578: 5345, 631: 2117, 883: 7417}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 533: 2117, 578: 5345, 883: 7411}, + {202: 7400}, + // 4275 + {571: 7341}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7305}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7132}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7133}, + {531: 7134}, + // 4280 + {2: 128, 128, 128, 128, 128, 128, 128, 10: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 133, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 606: 7138, 1194: 7140, 1228: 7139, 1279: 7137, 7136, 1406: 7141, 1463: 7135}, + {9: 7303, 57: 132}, + {9: 130, 57: 130}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7301, 3051, 3052, 3050}, + {2: 127, 127, 127, 127, 127, 127, 127, 10: 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 58: 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}, + // 4285 + {2: 126, 126, 126, 126, 126, 126, 126, 10: 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 58: 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126}, + {2: 125, 125, 125, 125, 125, 125, 125, 10: 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 58: 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125}, + {57: 7142}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7185, 7165, 7164, 7173, 7174, 7177}, + {122, 122, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + // 4290 + {124, 124, 539: 1014, 550: 1014, 1014}, + {123, 123}, + {121, 121}, + {120, 120}, + {119, 119}, + // 4295 + {118, 118}, + {117, 117}, + {116, 116}, + {115, 115}, + {114, 114}, + // 4300 + {113, 113}, + {112, 112}, + {111, 111}, + {110, 110}, + {105, 105}, + // 4305 + {56: 7300}, + {56: 82, 265: 7291, 561: 7292, 1433: 7290}, + {56: 7289}, + {56: 77, 84: 77, 77, 87: 77, 89: 77, 92: 77, 94: 77, 97: 77, 230: 7242, 531: 77, 77, 547: 77, 552: 77, 554: 77, 557: 77, 575: 77, 577: 77, 77, 582: 77, 587: 77, 608: 77, 614: 77, 617: 77, 695: 77, 713: 77, 77, 803: 77, 828: 77, 831: 77, 834: 77, 77, 1244: 7244, 1427: 7243, 7245}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7231, 1246: 7232}, + // 4310 + {63, 63}, + {62, 62}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 589: 7211, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7208, 1265: 7209, 1445: 7210}, + {51, 51}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7203}, + // 4315 + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7195}, + {1310: 7188}, + {56: 7187}, + {56: 7186}, + {42, 42}, + // 4320 + {41, 41}, + {40, 40}, + {39, 39}, + {38, 38}, + {37, 37}, + // 4325 + {36, 36}, + {35, 35}, + {34, 34}, + {33, 33}, + {32, 32}, + // 4330 + {31, 31}, + {30, 30}, + {43, 43}, + {44, 44}, + {84: 7162, 617: 7169, 831: 7168, 866: 7189, 7190}, + // 4335 + {47, 47, 56: 7191, 1243: 7193}, + {47, 47, 56: 7191, 1243: 7192}, + {46, 46}, + {45, 45}, + {48, 48}, + // 4340 + {7202}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177, 1091: 7197}, + {7201}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7198}, + {97: 7199, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 4345 + {617: 7200}, + {49, 49, 56: 49}, + {56: 70, 84: 70, 70, 87: 70, 89: 70, 92: 70, 94: 70, 97: 70, 531: 70, 70, 547: 70, 552: 70, 554: 70, 557: 70, 575: 70, 577: 70, 70, 582: 70, 587: 70, 589: 70, 70, 608: 70, 614: 70, 617: 70, 695: 70, 713: 70, 70, 803: 70, 828: 70, 831: 70, 834: 70, 70, 1043: 70, 1091: 70}, + {56: 71, 84: 71, 71, 87: 71, 89: 71, 92: 71, 94: 71, 97: 71, 531: 71, 71, 547: 71, 552: 71, 554: 71, 557: 71, 575: 71, 577: 71, 71, 582: 71, 587: 71, 589: 71, 71, 608: 71, 614: 71, 617: 71, 695: 71, 713: 71, 71, 803: 71, 828: 71, 831: 71, 834: 71, 71, 1043: 71, 1091: 71}, + {254: 7204, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + // 4350 + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7205}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 7206, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, + {831: 7207}, + {50, 50, 56: 50}, + {566: 3745, 3743, 3744, 3742, 3740, 589: 7223, 800: 3741, 3739, 1276: 7221, 1460: 7222}, + // 4355 + {97: 59, 589: 59, 59}, + {97: 55, 589: 7211, 7216, 1165: 7217, 1265: 7215}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7212}, + {566: 3745, 3743, 3744, 3742, 3740, 607: 7213, 800: 3741, 3739}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7214}, + // 4360 + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 56, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 589: 56, 56, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, + {97: 58, 589: 58, 58}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7220}, + {97: 7218}, + {614: 7219}, + // 4365 + {52, 52}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 54, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, + {97: 61, 589: 61, 61}, + {97: 55, 589: 7223, 7216, 1165: 7228, 1276: 7227}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7224}, + // 4370 + {566: 3745, 3743, 3744, 3742, 3740, 607: 7225, 800: 3741, 3739}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7226}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 57, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 589: 57, 57, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, + {97: 60, 589: 60, 60}, + {97: 7229}, + // 4375 + {614: 7230}, + {53, 53}, + {566: 3745, 3743, 3744, 3742, 3740, 607: 7235, 800: 3741, 3739}, + {97: 7233}, + {578: 7234}, + // 4380 + {68, 68}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7236}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 66, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 590: 7239, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177, 1043: 7238, 1423: 7237}, + {97: 67}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7231, 1246: 7241}, + // 4385 + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7194, 7165, 7164, 7173, 7174, 7177, 935: 7240}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 64, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7196, 7165, 7164, 7173, 7174, 7177}, + {97: 65}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 7253, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7254, 3051, 3052, 3050, 1341: 7257, 1355: 7258, 1426: 7255, 1430: 7256}, + {56: 76, 84: 76, 76, 87: 76, 89: 76, 92: 76, 94: 76, 97: 76, 230: 7242, 531: 76, 76, 547: 76, 552: 76, 554: 76, 557: 76, 575: 76, 577: 76, 76, 582: 76, 587: 76, 608: 76, 614: 76, 617: 76, 695: 76, 713: 76, 76, 803: 76, 828: 76, 831: 76, 834: 76, 76, 1244: 7251}, + // 4390 + {7250}, + {56: 73, 84: 73, 73, 87: 73, 89: 73, 92: 73, 94: 73, 97: 73, 531: 73, 73, 547: 73, 552: 73, 554: 73, 557: 73, 575: 73, 577: 73, 73, 582: 73, 587: 73, 608: 73, 614: 73, 617: 73, 695: 73, 713: 73, 73, 803: 73, 828: 73, 831: 73, 834: 73, 73, 1434: 7246}, + {56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 97: 7248, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7247, 7165, 7164, 7173, 7174, 7177}, + {7249}, + {69, 69, 56: 69}, + // 4395 + {56: 72, 84: 72, 72, 87: 72, 89: 72, 92: 72, 94: 72, 97: 72, 531: 72, 72, 547: 72, 552: 72, 554: 72, 557: 72, 575: 72, 577: 72, 72, 582: 72, 587: 72, 608: 72, 614: 72, 617: 72, 695: 72, 713: 72, 72, 803: 72, 828: 72, 831: 72, 834: 72, 72}, + {56: 75, 84: 75, 75, 87: 75, 89: 75, 92: 75, 94: 75, 97: 75, 230: 75, 531: 75, 75, 547: 75, 552: 75, 554: 75, 557: 75, 575: 75, 577: 75, 75, 582: 75, 587: 75, 608: 75, 614: 75, 617: 75, 695: 75, 713: 75, 75, 803: 75, 828: 75, 831: 75, 834: 75, 75}, + {7252}, + {56: 74, 84: 74, 74, 87: 74, 89: 74, 92: 74, 94: 74, 97: 74, 230: 74, 531: 74, 74, 547: 74, 552: 74, 554: 74, 557: 74, 575: 74, 577: 74, 74, 582: 74, 587: 74, 608: 74, 614: 74, 617: 74, 695: 74, 713: 74, 74, 803: 74, 828: 74, 831: 74, 834: 74, 74}, + {9: 2090, 118: 2090, 127: 2090, 172: 2090, 175: 2090, 2090, 2090, 179: 2090, 185: 2090, 188: 2090, 198: 2090, 203: 2090, 2090, 2090, 209: 2090, 2090, 212: 2090, 553: 2090, 557: 2090, 586: 2090, 709: 2090, 732: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 744: 2090, 2090, 748: 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 2090, 1345: 7282}, + // 4400 + {9: 104, 118: 104, 127: 104, 172: 104, 175: 104, 104, 104, 179: 104, 185: 104, 188: 104, 198: 104, 203: 104, 104, 104, 209: 104, 104, 212: 104, 553: 104, 557: 104, 586: 104, 709: 104, 732: 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 744: 104, 104, 748: 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104}, + {9: 7276, 118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 7277, 1098: 5019}, + {261: 7259}, + {261: 97}, + {261: 96}, + // 4405 + {558: 7260}, + {536: 7265, 560: 3037, 799: 7267, 1242: 7263, 1245: 7262, 1281: 7266, 7268, 7264, 1431: 7261}, + {9: 7274, 56: 7170, 84: 7162, 2866, 87: 2896, 89: 3017, 92: 7159, 94: 7161, 531: 2907, 2906, 547: 2905, 552: 2891, 554: 7160, 557: 7004, 575: 3020, 577: 2877, 7163, 582: 2875, 587: 2890, 608: 2904, 614: 7166, 617: 7169, 695: 2900, 713: 2862, 3019, 775: 7143, 803: 2870, 806: 7144, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 7145, 7154, 822: 3018, 2871, 7149, 826: 7150, 7147, 2876, 831: 7168, 833: 2872, 7171, 7172, 839: 7155, 845: 7156, 7151, 7152, 7146, 857: 7153, 2878, 860: 7157, 7148, 866: 7158, 7167, 7176, 7179, 7180, 7175, 7183, 7181, 7182, 7184, 7178, 7273, 7165, 7164, 7173, 7174, 7177}, + {9: 95, 56: 95, 84: 95, 95, 87: 95, 89: 95, 92: 95, 94: 95, 531: 95, 95, 547: 95, 552: 95, 554: 95, 557: 95, 575: 95, 577: 95, 95, 582: 95, 587: 95, 608: 95, 614: 95, 617: 95, 695: 95, 713: 95, 95, 803: 95, 828: 95, 831: 95, 834: 95, 95}, + {9: 93, 56: 93, 84: 93, 93, 87: 93, 89: 93, 92: 93, 94: 93, 531: 93, 93, 547: 93, 552: 93, 554: 93, 557: 93, 575: 93, 577: 93, 93, 582: 93, 587: 93, 608: 93, 614: 93, 617: 93, 695: 93, 713: 93, 93, 803: 93, 828: 93, 831: 93, 834: 93, 93}, + // 4410 + {9: 92, 56: 92, 84: 92, 92, 87: 92, 89: 92, 92: 92, 94: 92, 531: 92, 92, 547: 92, 552: 92, 554: 92, 557: 92, 575: 92, 577: 92, 92, 582: 92, 587: 92, 608: 92, 614: 92, 617: 92, 695: 92, 713: 92, 92, 803: 92, 828: 92, 831: 92, 834: 92, 92}, + {393: 7272}, + {9: 90, 56: 90, 84: 90, 90, 87: 90, 89: 90, 92: 90, 94: 90, 531: 90, 90, 547: 90, 552: 90, 554: 90, 557: 90, 575: 90, 577: 90, 90, 582: 90, 587: 90, 608: 90, 614: 90, 617: 90, 695: 90, 713: 90, 90, 803: 90, 828: 90, 831: 90, 834: 90, 90}, + {9: 89, 56: 89, 84: 89, 89, 87: 89, 89: 89, 92: 89, 94: 89, 531: 89, 89, 547: 89, 552: 89, 554: 89, 557: 89, 575: 89, 577: 89, 89, 582: 89, 587: 89, 608: 89, 614: 89, 617: 89, 695: 89, 713: 89, 89, 803: 89, 828: 89, 831: 89, 834: 89, 89}, + {186: 7270, 533: 87, 1408: 7269}, + // 4415 + {533: 7271}, + {533: 86}, + {9: 88, 56: 88, 84: 88, 88, 87: 88, 89: 88, 92: 88, 94: 88, 531: 88, 88, 547: 88, 552: 88, 554: 88, 557: 88, 575: 88, 577: 88, 88, 582: 88, 587: 88, 608: 88, 614: 88, 617: 88, 695: 88, 713: 88, 88, 803: 88, 828: 88, 831: 88, 834: 88, 88}, + {9: 91, 56: 91, 84: 91, 91, 87: 91, 89: 91, 92: 91, 94: 91, 531: 91, 91, 547: 91, 552: 91, 554: 91, 557: 91, 575: 91, 577: 91, 91, 582: 91, 587: 91, 608: 91, 614: 91, 617: 91, 695: 91, 713: 91, 91, 803: 91, 828: 91, 831: 91, 834: 91, 91}, + {98}, + // 4420 + {536: 7265, 560: 3037, 799: 7267, 1242: 7263, 1245: 7275, 1281: 7266, 7268, 7264}, + {9: 94, 56: 94, 84: 94, 94, 87: 94, 89: 94, 92: 94, 94: 94, 531: 94, 94, 547: 94, 552: 94, 554: 94, 557: 94, 575: 94, 577: 94, 94, 582: 94, 587: 94, 608: 94, 614: 94, 617: 94, 695: 94, 713: 94, 94, 803: 94, 828: 94, 831: 94, 834: 94, 94}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7281, 3051, 3052, 3050}, + {102, 535: 7278, 1432: 7279}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3131, 3079, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3049, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3163, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3168, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3092, 3570, 3473, 3567, 3242, 3121, 3235, 3236, 3231, 3189, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3170, 3055, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3090, 3112, 3430, 3159, 3219, 3259, 3119, 3175, 3196, 3160, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3174, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3115, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3047, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3230, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3176, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3048, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3152, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3460, 3172, 3461, 3462, 3067, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3479, 3480, 3313, 3552, 3553, 3532, 3531, 3353, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3212, 3229, 3489, 3354, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3497, 3498, 3499, 3225, 3446, 3510, 3511, 3522, 3506, 3507, 3508, 3541, 3171, 531: 3604, 533: 3586, 3602, 3612, 3686, 540: 3617, 3621, 543: 3601, 3600, 3640, 547: 3613, 3577, 552: 3620, 3638, 560: 3581, 578: 3615, 586: 3608, 3639, 614: 3610, 617: 3619, 628: 3684, 3576, 3578, 3622, 636: 3580, 3579, 3584, 3605, 3585, 3691, 3595, 3607, 3614, 3606, 3611, 3583, 3636, 3618, 3623, 3628, 3681, 3629, 3630, 3659, 3598, 3599, 3654, 3655, 3656, 3657, 3658, 3609, 3641, 3651, 3652, 3645, 3660, 3661, 3662, 3646, 3664, 3665, 3647, 3663, 3642, 3650, 3648, 3634, 3666, 3667, 3671, 3624, 3627, 3670, 3676, 3675, 3677, 3674, 3678, 3673, 3672, 3669, 3668, 697: 3626, 3625, 3631, 3632, 711: 3687, 770: 3587, 3051, 3052, 3050, 775: 3603, 3680, 3594, 3588, 3582, 3653, 3591, 3589, 3590, 3633, 3644, 3643, 3637, 3635, 3649, 3692, 3597, 3679, 3596, 3593, 3690, 3689, 3688, 7280}, + // 4425 + {100}, + {101, 566: 3745, 3743, 3744, 3742, 3740, 800: 3741, 3739}, + {9: 103, 118: 103, 127: 103, 172: 103, 175: 103, 103, 103, 179: 103, 185: 103, 188: 103, 198: 103, 203: 103, 103, 103, 209: 103, 103, 212: 103, 553: 103, 557: 103, 586: 103, 709: 103, 732: 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 744: 103, 103, 748: 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103, 103}, + {558: 7283}, + {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 7284, 806: 7285, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 7286, 7287, 1425: 7288}, + // 4430 + {107, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {109, 539: 1014, 550: 1014, 1014}, + {108}, + {106}, + {99}, + // 4435 + {83, 83}, + {56: 7294}, + {561: 7293}, + {56: 80}, + {56: 81}, + // 4440 + {559: 7295}, + {56: 7297, 1429: 7296}, + {84, 84, 9: 7298}, + {79, 79, 9: 79}, + {56: 7299}, + // 4445 + {78, 78, 9: 78}, + {85, 85}, + {118: 5005, 127: 5006, 172: 4996, 175: 5016, 5015, 4979, 179: 5018, 185: 5017, 188: 4976, 198: 5012, 203: 4985, 4975, 4994, 209: 5001, 5000, 212: 5004, 553: 4999, 557: 4995, 586: 4990, 709: 4998, 732: 5003, 5002, 4977, 4982, 4980, 4973, 4967, 4981, 4991, 4974, 5008, 744: 4983, 4984, 748: 4968, 4969, 4970, 4971, 4972, 4997, 5010, 5014, 5009, 4965, 5013, 4966, 4978, 4964, 5007, 4963, 5011, 951: 4986, 1025: 4988, 1029: 4962, 4992, 4959, 1038: 4957, 1046: 4960, 4961, 1054: 4958, 1058: 4987, 1062: 4955, 4989, 1083: 4956, 1086: 4993, 1089: 7302, 1098: 5019}, + {9: 129, 57: 129}, + {2: 128, 128, 128, 128, 128, 128, 128, 10: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 58: 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 606: 7138, 1194: 7140, 1228: 7139, 1279: 7137, 7304}, + // 4450 + {9: 131, 57: 131}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7306}, + {188, 188, 6: 188, 188, 188, 15: 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 188, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 535: 188, 538: 188, 188, 553: 188, 565: 188, 709: 188, 188, 720: 7315, 1014: 7309, 1342: 7307, 1449: 7308}, + {576, 576, 6: 4725, 4727, 580, 15: 4744, 2446, 4742, 4681, 4746, 4733, 4762, 4729, 4726, 4728, 4731, 4732, 4734, 4741, 580, 4752, 4753, 4763, 4739, 4740, 4745, 4747, 4759, 4758, 4767, 4760, 4757, 4750, 4755, 4756, 4749, 4751, 4754, 4743, 4764, 4765, 535: 4724, 538: 2446, 4761, 553: 2446, 565: 5499, 709: 2446, 4730, 865: 4735, 891: 4737, 910: 4736, 931: 4738, 937: 4748, 941: 4766, 1020: 6174, 1141: 7340}, + {187, 187, 6: 187, 187, 187, 15: 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 535: 187, 538: 187, 187, 553: 187, 565: 187, 709: 187, 187, 720: 7315, 1014: 7339}, + // 4455 + {186, 186, 6: 186, 186, 186, 15: 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 186, 86: 186, 88: 186, 90: 186, 186, 95: 186, 186, 98: 186, 186, 186, 186, 535: 186, 538: 186, 186, 553: 186, 565: 186, 709: 186, 186, 720: 186}, + {543: 2316, 2316, 555: 4588, 560: 2316, 723: 7336, 802: 7335}, + {532: 7332, 543: 2316, 2316, 555: 4588, 560: 2316, 802: 7331}, + {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7329}, + {179, 179, 6: 179, 179, 179, 15: 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 179, 86: 179, 88: 179, 90: 179, 179, 95: 179, 179, 98: 179, 179, 179, 179, 103: 179, 535: 179, 538: 179, 179, 553: 179, 565: 179, 709: 179, 179, 720: 179}, + // 4460 + {90: 7327, 95: 7328, 7325, 720: 7326}, + {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7323}, + {176, 176, 6: 176, 176, 176, 15: 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 86: 176, 88: 176, 90: 176, 176, 95: 176, 176, 98: 176, 176, 176, 176, 103: 176, 535: 176, 538: 176, 176, 553: 176, 565: 176, 709: 176, 176, 720: 176}, + {543: 2316, 2316, 555: 4588, 560: 2316, 802: 7321}, + {173, 173, 6: 173, 173, 173, 15: 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 86: 173, 88: 173, 90: 173, 173, 95: 173, 173, 98: 173, 173, 173, 173, 103: 173, 535: 173, 538: 173, 173, 553: 173, 565: 173, 709: 173, 173, 720: 173}, + // 4465 + {171, 171, 6: 171, 171, 171, 15: 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 171, 86: 171, 88: 171, 90: 171, 171, 95: 171, 171, 98: 171, 171, 171, 171, 103: 171, 535: 171, 538: 171, 171, 553: 171, 565: 171, 709: 171, 171, 720: 171}, + {170, 170, 6: 170, 170, 170, 15: 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 170, 86: 170, 88: 170, 90: 170, 170, 95: 170, 170, 98: 170, 170, 170, 170, 103: 170, 535: 170, 538: 170, 170, 553: 170, 565: 170, 709: 170, 170, 720: 170}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7322}, + {174, 174, 6: 174, 174, 174, 15: 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, 86: 174, 88: 174, 90: 174, 174, 95: 174, 174, 98: 174, 174, 174, 174, 103: 174, 535: 174, 538: 174, 174, 553: 174, 565: 174, 709: 174, 174, 720: 174}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7324}, + // 4470 + {177, 177, 6: 177, 177, 177, 15: 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 177, 86: 177, 88: 177, 90: 177, 177, 95: 177, 177, 98: 177, 177, 177, 177, 103: 177, 535: 177, 538: 177, 177, 553: 177, 565: 177, 709: 177, 177, 720: 177}, + {178, 178, 6: 178, 178, 178, 15: 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 86: 178, 88: 178, 90: 178, 178, 95: 178, 178, 98: 178, 178, 178, 178, 103: 178, 535: 178, 538: 178, 178, 553: 178, 565: 178, 709: 178, 178, 720: 178}, + {175, 175, 6: 175, 175, 175, 15: 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 175, 86: 175, 88: 175, 90: 175, 175, 95: 175, 175, 98: 175, 175, 175, 175, 103: 175, 535: 175, 538: 175, 175, 553: 175, 565: 175, 709: 175, 175, 720: 175}, + {172, 172, 6: 172, 172, 172, 15: 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 172, 86: 172, 88: 172, 90: 172, 172, 95: 172, 172, 98: 172, 172, 172, 172, 103: 172, 535: 172, 538: 172, 172, 553: 172, 565: 172, 709: 172, 172, 720: 172}, + {169, 169, 6: 169, 169, 169, 15: 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 86: 169, 88: 169, 90: 169, 169, 95: 169, 169, 98: 169, 169, 169, 169, 103: 169, 535: 169, 538: 169, 169, 553: 169, 565: 169, 709: 169, 169, 720: 169}, + // 4475 + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7330}, + {180, 180, 6: 180, 180, 180, 15: 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 86: 180, 88: 180, 90: 180, 180, 95: 180, 180, 98: 180, 180, 180, 180, 103: 180, 535: 180, 538: 180, 180, 553: 180, 565: 180, 709: 180, 180, 720: 180}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7334}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7333}, + {181, 181, 6: 181, 181, 181, 15: 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 181, 86: 181, 88: 181, 90: 181, 181, 95: 181, 181, 98: 181, 181, 181, 181, 103: 181, 535: 181, 538: 181, 181, 553: 181, 565: 181, 709: 181, 181, 720: 181}, + // 4480 + {182, 182, 6: 182, 182, 182, 15: 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 182, 86: 182, 88: 182, 90: 182, 182, 95: 182, 182, 98: 182, 182, 182, 182, 103: 182, 535: 182, 538: 182, 182, 553: 182, 565: 182, 709: 182, 182, 720: 182}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7338}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7337}, + {183, 183, 6: 183, 183, 183, 15: 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 183, 86: 183, 88: 183, 90: 183, 183, 95: 183, 183, 98: 183, 183, 183, 183, 103: 183, 535: 183, 538: 183, 183, 553: 183, 565: 183, 709: 183, 183, 720: 183}, + {184, 184, 6: 184, 184, 184, 15: 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 86: 184, 88: 184, 90: 184, 184, 95: 184, 184, 98: 184, 184, 184, 184, 103: 184, 535: 184, 538: 184, 184, 553: 184, 565: 184, 709: 184, 184, 720: 184}, + // 4485 + {185, 185, 6: 185, 185, 185, 15: 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 185, 86: 185, 88: 185, 90: 185, 185, 95: 185, 185, 98: 185, 185, 185, 185, 535: 185, 538: 185, 185, 553: 185, 565: 185, 709: 185, 185, 720: 185}, + {189, 189}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 535: 2117, 578: 5345, 883: 7342}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7343}, + {110: 7350, 7348, 7347, 7349, 7346, 977: 7344, 1255: 7345}, + // 4490 + {2845, 2845, 9: 2845, 110: 2845, 2845, 2845, 2845, 2845}, + {194, 194, 9: 7398, 110: 7350, 7348, 7347, 7349, 7346, 977: 7397}, + {555: 4588, 560: 2316, 802: 7395}, + {311: 2316, 321: 2316, 2316, 555: 4588, 802: 7390}, + {2822, 2822, 9: 2822, 110: 2822, 2822, 2822, 2822, 2822, 555: 4588, 560: 2316, 629: 2316, 2316, 802: 7388}, + // 4495 + {531: 2316, 548: 2316, 555: 4588, 802: 7364}, + {531: 2316, 548: 2316, 555: 4588, 802: 7351}, + {531: 7352, 548: 7353}, + {57: 7355, 197: 7357, 1040: 7356, 1440: 7354}, + {2815, 2815, 9: 2815, 110: 2815, 2815, 2815, 2815, 2815}, + // 4500 + {9: 7362, 57: 7360, 197: 7357, 1040: 7361}, + {2816, 2816, 9: 2816, 110: 2816, 2816, 2816, 2816, 2816}, + {9: 2814, 57: 2814, 197: 2814}, + {533: 2316, 555: 4588, 802: 7358}, + {533: 7359}, + // 4505 + {9: 2811, 57: 2811, 197: 2811}, + {2817, 2817, 9: 2817, 110: 2817, 2817, 2817, 2817, 2817}, + {9: 2813, 57: 2813, 197: 2813}, + {197: 7357, 1040: 7363}, + {9: 2812, 57: 2812, 197: 2812}, + // 4510 + {531: 7365, 548: 7366}, + {57: 7372, 93: 7370, 134: 7371, 136: 7369, 1041: 7367, 1442: 7368}, + {2818, 2818, 9: 2818, 110: 2818, 2818, 2818, 2818, 2818}, + {9: 2839, 57: 2839, 93: 2839, 134: 2839, 136: 2839}, + {9: 7385, 57: 7386, 93: 7370, 134: 7371, 136: 7369, 1041: 7384}, + // 4515 + {533: 2316, 555: 4588, 802: 7382}, + {229: 2316, 231: 2316, 555: 4588, 802: 7380, 933: 2316}, + {117: 2316, 258: 2316, 272: 2316, 555: 4588, 802: 7373}, + {2819, 2819, 9: 2819, 110: 2819, 2819, 2819, 2819, 2819}, + {117: 4583, 258: 4581, 272: 4582, 1257: 7374}, + // 4520 + {9: 2827, 57: 2827, 93: 2827, 134: 2827, 136: 2827, 158: 7376, 1500: 7375}, + {9: 2828, 57: 2828, 93: 2828, 134: 2828, 136: 2828}, + {360: 2316, 533: 2316, 555: 4588, 802: 7377}, + {360: 7379, 533: 7378}, + {9: 2826, 57: 2826, 93: 2826, 134: 2826, 136: 2826}, + // 4525 + {9: 2825, 57: 2825, 93: 2825, 134: 2825, 136: 2825}, + {229: 4591, 231: 4590, 933: 4592, 1256: 7381}, + {9: 2829, 57: 2829, 93: 2829, 134: 2829, 136: 2829}, + {533: 7383}, + {9: 2830, 57: 2830, 93: 2830, 134: 2830, 136: 2830}, + // 4530 + {9: 2838, 57: 2838, 93: 2838, 134: 2838, 136: 2838}, + {93: 7370, 134: 7371, 136: 7369, 1041: 7387}, + {2820, 2820, 9: 2820, 110: 2820, 2820, 2820, 2820, 2820}, + {9: 2837, 57: 2837, 93: 2837, 134: 2837, 136: 2837}, + {560: 3037, 629: 6710, 6711, 799: 6709, 998: 7389}, + // 4535 + {2821, 2821, 9: 2821, 110: 2821, 2821, 2821, 2821, 2821}, + {311: 7393, 321: 7391, 7392, 1441: 7394}, + {2842, 2842, 9: 2842, 110: 2842, 2842, 2842, 2842, 2842}, + {2841, 2841, 9: 2841, 110: 2841, 2841, 2841, 2841, 2841}, + {2840, 2840, 9: 2840, 110: 2840, 2840, 2840, 2840, 2840}, + // 4540 + {2823, 2823, 9: 2823, 110: 2823, 2823, 2823, 2823, 2823}, + {560: 3037, 799: 3866, 816: 7396}, + {2824, 2824, 9: 2824, 110: 2824, 2824, 2824, 2824, 2824}, + {2844, 2844, 9: 2844, 110: 2844, 2844, 2844, 2844, 2844}, + {110: 7350, 7348, 7347, 7349, 7346, 977: 7399}, + // 4545 + {2843, 2843, 9: 2843, 110: 2843, 2843, 2843, 2843, 2843}, + {558: 7401, 561: 7402}, + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7408}, + {262: 7403}, + {542: 7404}, + // 4550 + {117: 7405}, + {218: 7406}, + {533: 7407}, + {344, 344}, + {542: 7409}, + // 4555 + {531: 2907, 2906, 547: 2905, 552: 2891, 587: 2890, 608: 2904, 695: 2900, 714: 3019, 775: 6325, 803: 6323, 806: 6326, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 6324, 817: 6328, 6327, 822: 3018, 6330, 6331, 826: 6332, 6329, 944: 7410}, + {345, 345}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 7413, 6301, 1264: 7414, 1443: 7412}, + {419, 419, 9: 7415}, + {356, 356, 9: 356}, + // 4560 + {355, 355, 9: 355}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 6295, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 6300, 770: 3737, 3051, 3052, 3050, 805: 5807, 898: 6302, 917: 7413, 6301, 1264: 7416}, + {354, 354, 9: 354}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 7418}, + {401, 401, 6: 401, 9: 5873, 15: 401, 51: 401, 401, 401, 401, 401, 532: 401, 725: 5917, 1075: 5916, 7419}, + // 4565 + {409, 409, 6: 409, 15: 409, 51: 409, 409, 409, 409, 409, 532: 7421, 1131: 7420}, + {382, 382, 6: 382, 15: 7437, 51: 382, 382, 7436, 7438, 7439, 1068: 7435, 1235: 7434, 7433}, + {163: 7426, 7424, 7425, 7427, 1130: 7423, 1339: 7422}, + {408, 408, 6: 408, 15: 408, 51: 408, 408, 408, 408, 408, 163: 7426, 7424, 7425, 7427, 1130: 7432}, + {407, 407, 6: 407, 15: 407, 51: 407, 407, 407, 407, 407, 163: 407, 407, 407, 407}, + // 4570 + {560: 3037, 799: 4538, 825: 7431}, + {560: 3037, 799: 4538, 825: 7430}, + {560: 3037, 799: 4538, 825: 7429}, + {560: 3037, 799: 4538, 825: 7428}, + {402, 402, 6: 402, 15: 402, 51: 402, 402, 402, 402, 402, 163: 402, 402, 402, 402}, + // 4575 + {403, 403, 6: 403, 15: 403, 51: 403, 403, 403, 403, 403, 163: 403, 403, 403, 403}, + {404, 404, 6: 404, 15: 404, 51: 404, 404, 404, 404, 404, 163: 404, 404, 404, 404}, + {405, 405, 6: 405, 15: 405, 51: 405, 405, 405, 405, 405, 163: 405, 405, 405, 405}, + {406, 406, 6: 406, 15: 406, 51: 406, 406, 406, 406, 406, 163: 406, 406, 406, 406}, + {387, 387, 6: 7461, 51: 387, 7462, 1128: 7460}, + // 4580 + {381, 381, 6: 381, 15: 7437, 51: 381, 381, 7436, 7438, 7439, 1068: 7459}, + {380, 380, 6: 380, 15: 380, 51: 380, 380, 380, 380, 380}, + {562: 7458, 1090: 7457}, + {262: 7443, 389: 7445, 431: 7444}, + {560: 3037, 799: 4538, 825: 7442}, + // 4585 + {201: 7441, 560: 3037, 799: 4538, 825: 7440}, + {367, 367, 6: 367, 15: 367, 51: 367, 367, 367, 367, 367}, + {366, 366, 6: 366, 15: 366, 51: 366, 366, 366, 366, 366}, + {368, 368, 6: 368, 15: 368, 51: 368, 368, 368, 368, 368}, + {535: 7455, 560: 3037, 799: 7456}, + // 4590 + {641: 7451}, + {372, 372, 6: 372, 15: 372, 51: 372, 372, 372, 372, 372, 407: 7447, 535: 7448, 641: 7446}, + {560: 3037, 799: 4538, 825: 7449}, + {370, 370, 6: 370, 15: 370, 51: 370, 370, 370, 370, 370}, + {369, 369, 6: 369, 15: 369, 51: 369, 369, 369, 369, 369}, + // 4595 + {133: 7450}, + {371, 371, 6: 371, 15: 371, 51: 371, 371, 371, 371, 371}, + {535: 7452, 560: 3037, 799: 7453}, + {374, 374, 6: 374, 15: 374, 51: 374, 374, 374, 374, 374}, + {133: 7454}, + // 4600 + {373, 373, 6: 373, 15: 373, 51: 373, 373, 373, 373, 373}, + {376, 376, 6: 376, 15: 376, 51: 376, 376, 376, 376, 376}, + {375, 375, 6: 375, 15: 375, 51: 375, 375, 375, 375, 375}, + {378, 378, 6: 378, 15: 378, 51: 378, 378, 378, 378, 378}, + {377, 377, 6: 377, 15: 377, 51: 377, 377, 377, 377, 377}, + // 4605 + {379, 379, 6: 379, 15: 379, 51: 379, 379, 379, 379, 379}, + {384, 384, 51: 7466, 1254: 7465}, + {533: 7464}, + {533: 7463}, + {385, 385, 51: 385}, + // 4610 + {386, 386, 51: 386}, + {420, 420}, + {571: 7467}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7468}, + {383, 383}, + // 4615 + {18: 2364, 102: 2364, 132: 2364, 181: 2364, 696: 2364}, + {132: 2359, 181: 7522, 696: 2359, 1494: 7521}, + {555: 7517}, + {214: 7473}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7474}, + // 4620 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7475}, + {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7476, 1238: 7477}, + {2810, 2810, 9: 2810, 108: 2810, 119: 2810, 2810, 2810, 2810, 2810, 2810, 2810, 2810, 128: 2810, 2810, 2810}, + {191, 191, 9: 7515, 108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7514}, + {533: 2316, 555: 4588, 802: 7512}, + // 4625 + {533: 2316, 555: 4588, 802: 7510}, + {555: 4588, 560: 2316, 802: 7508}, + {555: 4588, 560: 2316, 802: 7506}, + {555: 4588, 560: 2316, 802: 7504}, + {533: 2316, 555: 4588, 802: 7502}, + // 4630 + {533: 2316, 555: 4588, 802: 7500}, + {533: 2316, 555: 4588, 802: 7498}, + {533: 2316, 555: 4588, 802: 7496}, + {533: 2316, 555: 4588, 802: 7494}, + {533: 2316, 555: 4588, 802: 7492}, + // 4635 + {533: 2316, 555: 4588, 802: 7490}, + {533: 7491}, + {2796, 2796, 9: 2796, 108: 2796, 119: 2796, 2796, 2796, 2796, 2796, 2796, 2796, 2796, 128: 2796, 2796, 2796}, + {533: 7493}, + {2797, 2797, 9: 2797, 108: 2797, 119: 2797, 2797, 2797, 2797, 2797, 2797, 2797, 2797, 128: 2797, 2797, 2797}, + // 4640 + {533: 7495}, + {2798, 2798, 9: 2798, 108: 2798, 119: 2798, 2798, 2798, 2798, 2798, 2798, 2798, 2798, 128: 2798, 2798, 2798}, + {533: 7497}, + {2799, 2799, 9: 2799, 108: 2799, 119: 2799, 2799, 2799, 2799, 2799, 2799, 2799, 2799, 128: 2799, 2799, 2799}, + {533: 7499}, + // 4645 + {2800, 2800, 9: 2800, 108: 2800, 119: 2800, 2800, 2800, 2800, 2800, 2800, 2800, 2800, 128: 2800, 2800, 2800}, + {533: 7501}, + {2801, 2801, 9: 2801, 108: 2801, 119: 2801, 2801, 2801, 2801, 2801, 2801, 2801, 2801, 128: 2801, 2801, 2801}, + {533: 7503}, + {2802, 2802, 9: 2802, 108: 2802, 119: 2802, 2802, 2802, 2802, 2802, 2802, 2802, 2802, 128: 2802, 2802, 2802}, + // 4650 + {560: 3037, 799: 3866, 816: 7505}, + {2803, 2803, 9: 2803, 108: 2803, 119: 2803, 2803, 2803, 2803, 2803, 2803, 2803, 2803, 128: 2803, 2803, 2803}, + {560: 3037, 799: 3866, 816: 7507}, + {2804, 2804, 9: 2804, 108: 2804, 119: 2804, 2804, 2804, 2804, 2804, 2804, 2804, 2804, 128: 2804, 2804, 2804}, + {560: 3037, 799: 3866, 816: 7509}, + // 4655 + {2805, 2805, 9: 2805, 108: 2805, 119: 2805, 2805, 2805, 2805, 2805, 2805, 2805, 2805, 128: 2805, 2805, 2805}, + {533: 7511}, + {2806, 2806, 9: 2806, 108: 2806, 119: 2806, 2806, 2806, 2806, 2806, 2806, 2806, 2806, 128: 2806, 2806, 2806}, + {533: 7513}, + {2807, 2807, 9: 2807, 108: 2807, 119: 2807, 2807, 2807, 2807, 2807, 2807, 2807, 2807, 128: 2807, 2807, 2807}, + // 4660 + {2809, 2809, 9: 2809, 108: 2809, 119: 2809, 2809, 2809, 2809, 2809, 2809, 2809, 2809, 128: 2809, 2809, 2809}, + {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7516}, + {2808, 2808, 9: 2808, 108: 2808, 119: 2808, 2808, 2808, 2808, 2808, 2808, 2808, 2808, 128: 2808, 2808, 2808}, + {4: 7519, 448: 7520, 456: 7518}, + {132: 2362, 181: 2362, 696: 2362}, + // 4665 + {132: 2361, 181: 2361, 696: 2361}, + {132: 2360, 181: 2360, 696: 2360}, + {132: 2357, 696: 7526, 1497: 7525}, + {555: 7523}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7524}, + // 4670 + {132: 2358, 696: 2358}, + {132: 7530}, + {434: 7527}, + {181: 7528, 400: 7529}, + {132: 2356}, + // 4675 + {132: 2355}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7532, 1496: 7531}, + {531: 7534, 537: 2353, 1495: 7533}, + {531: 2354, 537: 2354}, + {537: 7540}, + // 4680 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7536, 3051, 3052, 3050, 1334: 7535}, + {9: 7538, 57: 7537}, + {9: 2351, 57: 2351}, + {537: 2352}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7539, 3051, 3052, 3050}, + // 4685 + {9: 2350, 57: 2350}, + {531: 2907, 2906, 547: 2905, 608: 2904, 695: 2900, 775: 7544, 806: 7542, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 3846, 817: 7543, 7541, 1344: 7545}, + {2372, 2372, 532: 2372}, + {2371, 2371, 532: 2371, 539: 1014, 550: 1014, 1014}, + {2370, 2370, 532: 2370}, + // 4690 + {2369, 2369, 532: 2369, 539: 1013, 550: 1013, 1013, 554: 3859, 556: 3858, 564: 3857, 842: 3860, 3861}, + {2349, 2349, 532: 7547, 1493: 7546}, + {2366, 2366}, + {173: 7549, 371: 7548}, + {702: 7552}, + // 4695 + {702: 7550}, + {1006: 7551}, + {2347, 2347}, + {1006: 7553}, + {2348, 2348}, + // 4700 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7555}, + {2455, 2455, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7561, 1037: 7560, 1346: 7556}, + {2466, 2466}, + {16: 4418, 18: 4681, 21: 7569, 538: 7568, 553: 4419, 709: 4417, 853: 7567, 865: 7570}, + {2457, 2457, 16: 2457, 18: 2457, 21: 2457, 535: 2457, 538: 2457, 553: 2457, 557: 2457, 709: 2457}, + // 4705 + {200: 7563}, + {2454, 2454, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, + {2453, 2453, 16: 2453, 18: 2453, 21: 2453, 535: 2453, 538: 2453, 553: 2453, 557: 2453, 709: 2453}, + {2452, 2452, 16: 2452, 18: 2452, 21: 2452, 535: 2452, 538: 2452, 553: 2452, 557: 2452, 709: 2452}, + {223: 7564}, + // 4710 + {560: 3037, 799: 3866, 816: 7565}, + {2779, 2779, 16: 2779, 18: 2779, 21: 2779, 220: 5489, 535: 2779, 538: 2779, 553: 2779, 557: 2779, 709: 2779, 1057: 7566}, + {2456, 2456, 16: 2456, 18: 2456, 21: 2456, 535: 2456, 538: 2456, 553: 2456, 557: 2456, 709: 2456}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 7575}, + {2: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 10: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 58: 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 2316, 533: 2316, 555: 4588, 586: 2316, 802: 7573}, + // 4715 + {533: 2316, 555: 4588, 802: 7571}, + {2458, 2458, 16: 2458, 18: 2458, 21: 2458, 535: 2458, 538: 2458, 553: 2458, 557: 2458, 709: 2458}, + {533: 4796, 1167: 7572}, + {2459, 2459, 16: 2459, 18: 2459, 21: 2459, 535: 2459, 538: 2459, 553: 2459, 557: 2459, 709: 2459}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 3735, 770: 3737, 3051, 3052, 3050, 805: 3734, 974: 7574}, + // 4720 + {2460, 2460, 16: 2460, 18: 2460, 21: 2460, 535: 2460, 538: 2460, 553: 2460, 557: 2460, 709: 2460}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 586: 4354, 770: 3737, 3051, 3052, 3050, 805: 4353, 905: 7576}, + {2461, 2461, 16: 2461, 18: 2461, 21: 2461, 535: 2461, 538: 2461, 553: 2461, 557: 2461, 709: 2461}, + {2: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 10: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 58: 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 2117, 578: 5345, 883: 7578}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7579, 3051, 3052, 3050}, + // 4725 + {104: 5379, 530: 2100, 542: 5378, 961: 7581, 1376: 7580}, + {530: 7582}, + {530: 2099}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7583}, + {531: 7584}, + // 4730 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 531: 5114, 770: 4024, 3051, 3052, 3050, 821: 5113, 922: 5112, 932: 7585}, + {9: 5123, 57: 7586}, + {2111, 2111, 6: 2111, 19: 2111, 102: 2111, 104: 2111, 2111, 2111, 2111, 109: 2111, 532: 2111, 542: 2111, 562: 2111, 983: 7587}, + {2477, 2477, 6: 5375, 19: 5372, 102: 4719, 104: 5379, 5224, 4926, 5225, 109: 4925, 532: 5374, 542: 5378, 562: 4720, 959: 5376, 961: 5373, 971: 5377, 7111, 982: 5371, 986: 7110, 1193: 7588}, + {2484, 2484}, + // 4735 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7590, 3051, 3052, 3050}, + {531: 7591}, + {286: 5408, 294: 5410, 297: 5409, 1287: 7592}, + {57: 7593}, + {530: 7594}, + // 4740 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7595}, + {531: 7596}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 4024, 3051, 3052, 3050, 821: 4025, 906: 7597}, + {9: 4027, 57: 7598}, + {2486, 2486}, + // 4745 + {2593, 2593}, + {2618, 2618}, + {2624, 2624, 532: 7603, 729: 7602}, + {194: 7610, 768: 7609}, + {372: 7605, 381: 7604}, + // 4750 + {60: 7608}, + {380: 7606}, + {194: 7607}, + {2621, 2621}, + {2622, 2622}, + // 4755 + {2623, 2623}, + {2620, 2620, 731: 4641, 995: 7611}, + {2619, 2619}, + {2626, 2626}, + {2625, 2625}, + // 4760 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7627, 884: 7626}, + {608: 7616}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7617}, + {546: 7619, 710: 7618}, + {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7624}, + // 4765 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7620}, + {9: 5256, 710: 7621}, + {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7622}, + {2642, 2642, 9: 5546, 532: 5527, 902: 7623}, + {2650, 2650}, + // 4770 + {2642, 2642, 9: 5546, 532: 5527, 902: 7625}, + {2653, 2653}, + {2645, 2645, 9: 3930, 215: 7647, 532: 2645, 715: 7646, 1101: 7657}, + {1252, 1252, 9: 1252, 131: 7632, 215: 1252, 532: 1252, 546: 7629, 710: 7628, 714: 7630, 1252, 727: 7631}, + {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7655}, + // 4775 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5255, 3051, 3052, 3050, 859: 7642}, + {312: 7638}, + {312: 7635}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7633}, + {2642, 2642, 9: 6471, 532: 5527, 902: 7634}, + // 4780 + {2647, 2647}, + {530: 7636}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7637}, + {2648, 2648, 9: 6471}, + {530: 7639}, + // 4785 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7640}, + {2642, 2642, 9: 6471, 532: 5527, 902: 7641}, + {2649, 2649}, + {2645, 2645, 9: 5256, 131: 7645, 215: 7647, 532: 2645, 710: 7644, 715: 7646, 1101: 7643}, + {2642, 2642, 532: 5527, 902: 7654}, + // 4790 + {1107, 1107, 3297, 3451, 3261, 3138, 3177, 3299, 3064, 1107, 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 532: 1107, 704: 5544, 770: 5543, 3051, 3052, 3050, 960: 7652}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 6469, 3051, 3052, 3050, 980: 7650}, + {131: 7649}, + {131: 7648}, + {2643, 2643, 532: 2643}, + // 4795 + {2644, 2644, 532: 2644}, + {2642, 2642, 9: 6471, 532: 5527, 902: 7651}, + {2646, 2646}, + {2642, 2642, 9: 5546, 532: 5527, 902: 7653}, + {2651, 2651}, + // 4800 + {2652, 2652}, + {2642, 2642, 9: 5546, 532: 5527, 902: 7656}, + {2654, 2654}, + {2642, 2642, 532: 5527, 902: 7658}, + {2655, 2655}, + // 4805 + {608: 7664}, + {558: 7662}, + {608: 2657}, + {546: 7663, 608: 2658}, + {608: 2656}, + // 4810 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7665}, + {546: 6023, 627: 1121, 710: 1121, 723: 1121, 963: 7666}, + {627: 7669, 710: 7668, 723: 7670, 1277: 7667}, + {2663, 2663}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7677, 3051, 3052, 3050}, + // 4815 + {531: 3893, 938: 7672}, + {531: 3893, 938: 6614, 1093: 7671}, + {2660, 2660, 9: 6615}, + {566: 7673}, + {531: 3893, 938: 7674}, + // 4820 + {108: 7675}, + {560: 3037, 799: 4538, 825: 7676}, + {2661, 2661}, + {627: 7669, 723: 7670, 1277: 7678}, + {2662, 2662}, + // 4825 + {765: 7693}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7689, 884: 7688}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5954, 3051, 3052, 3050, 907: 7682}, + {2666, 2666, 712: 7684, 765: 7683, 1176: 7685}, + {533: 7687}, + // 4830 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7686, 3051, 3052, 3050}, + {2664, 2664}, + {2665, 2665}, + {2668, 2668}, + {9: 3930, 765: 7691}, + // 4835 + {2666, 2666, 9: 1252, 712: 7684, 765: 1252, 1176: 7690}, + {2667, 2667}, + {533: 7692}, + {2669, 2669}, + {533: 7694}, + // 4840 + {2670, 2670}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 723: 7696, 770: 3927, 3051, 3052, 3050, 804: 7697}, + {178: 7699}, + {2672, 2672, 560: 3037, 799: 4538, 825: 7698}, + {2671, 2671}, + // 4845 + {560: 3037, 799: 4538, 825: 7700}, + {2673, 2673}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7712, 1296: 7711, 1484: 7710}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7705, 1301: 7704, 1487: 7703}, + {2677, 2677, 9: 7708}, + // 4850 + {2676, 2676, 9: 2676}, + {712: 7706}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7707}, + {2674, 2674, 9: 2674}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 7705, 1301: 7709}, + // 4855 + {2675, 2675, 9: 2675}, + {2681, 2681, 9: 7715}, + {2680, 2680, 9: 2680}, + {712: 7713}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7714}, + // 4860 + {2678, 2678, 9: 2678}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7712, 1296: 7716}, + {2679, 2679, 9: 2679}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 2446, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 770: 5954, 3051, 3052, 3050, 865: 7558, 891: 7557, 907: 7766, 954: 7561, 1037: 7767}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 533: 2119, 578: 4948, 631: 2119, 854: 7752}, + // 4865 + {333: 7746, 1378: 7745}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 7743, 3051, 3052, 3050}, + {571: 7739}, + {214: 7735}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7724}, + // 4870 + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 3927, 3051, 3052, 3050, 804: 7725}, + {86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 103: 7729, 720: 7315, 1014: 7728, 1108: 7727, 1314: 7726}, + {164, 164, 86: 7314, 88: 7311, 90: 7317, 7318, 95: 7319, 7312, 98: 7310, 7320, 7316, 7313, 103: 7729, 720: 7315, 1014: 7728, 1108: 7734}, + {163, 163, 86: 163, 88: 163, 90: 163, 163, 95: 163, 163, 98: 163, 163, 163, 163, 103: 163, 720: 163}, + {161, 161, 86: 161, 88: 161, 90: 161, 161, 95: 161, 161, 98: 161, 161, 161, 161, 103: 161, 720: 161}, + // 4875 + {160, 160, 86: 160, 88: 160, 90: 160, 160, 95: 160, 160, 98: 160, 160, 160, 160, 103: 160, 532: 7731, 543: 2316, 2316, 555: 4588, 560: 2316, 720: 160, 802: 7730}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7733}, + {543: 4541, 4542, 560: 3037, 799: 4538, 825: 4540, 911: 7732}, + {158, 158, 86: 158, 88: 158, 90: 158, 158, 95: 158, 158, 98: 158, 158, 158, 158, 103: 158, 720: 158}, + {159, 159, 86: 159, 88: 159, 90: 159, 159, 95: 159, 159, 98: 159, 159, 159, 159, 103: 159, 720: 159}, + // 4880 + {162, 162, 86: 162, 88: 162, 90: 162, 162, 95: 162, 162, 98: 162, 162, 162, 162, 103: 162, 720: 162}, + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 578: 4948, 854: 7736}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 770: 5516, 3051, 3052, 3050, 989: 7737}, + {108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7476, 1238: 7738}, + {190, 190, 9: 7515, 108: 7479, 119: 7484, 7486, 7480, 7485, 7488, 7482, 7478, 7483, 128: 7489, 7487, 7481, 976: 7514}, + // 4885 + {2: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 10: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 58: 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 2119, 535: 2119, 578: 4948, 854: 7740}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 3712, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 535: 4596, 770: 4595, 3051, 3052, 3050, 936: 7741}, + {110: 7350, 7348, 7347, 7349, 7346, 977: 7344, 1255: 7742}, + {193, 193, 9: 7398, 110: 7350, 7348, 7347, 7349, 7346, 977: 7397}, + {18: 4681, 865: 7744}, + // 4890 + {415, 415}, + {416, 416}, + {449: 7747}, + {414, 414, 86: 7748}, + {87: 7749}, + // 4895 + {530: 7750}, + {257: 7751}, + {413, 413}, + {2: 3297, 3451, 3261, 3138, 3177, 3299, 3064, 10: 3111, 3065, 3200, 3317, 3310, 3704, 3699, 3180, 3490, 3182, 3156, 3097, 3089, 3100, 3122, 3184, 3185, 3293, 3179, 3318, 3442, 3441, 3399, 3063, 3178, 3181, 3192, 3129, 3133, 3188, 3302, 3146, 3228, 3061, 3062, 3227, 3301, 3060, 3315, 3400, 3401, 3139, 3056, 3273, 3402, 3403, 3696, 58: 3387, 3145, 3148, 3369, 3366, 3358, 3370, 3373, 3374, 3371, 3375, 3376, 3372, 3566, 3561, 3365, 3377, 3360, 3361, 3565, 3364, 3367, 3563, 3368, 3378, 3564, 3068, 3083, 3214, 3142, 3149, 3708, 3345, 3344, 3151, 3053, 3077, 3346, 3341, 3098, 3340, 3347, 3342, 3343, 3258, 3140, 3330, 3395, 3328, 3396, 3455, 3329, 3573, 3559, 3555, 3572, 3554, 3154, 3222, 3491, 3709, 3543, 3548, 3535, 3547, 3549, 3538, 3544, 3545, 3327, 3546, 3550, 3542, 3080, 3217, 3701, 3570, 3473, 3567, 3721, 3703, 3719, 3720, 3718, 3714, 3319, 3320, 3321, 3322, 3323, 3324, 3326, 3710, 3697, 3073, 3316, 3109, 3336, 3150, 3155, 3476, 3239, 3243, 3267, 3269, 3247, 3248, 3249, 3250, 3238, 3082, 3268, 3398, 3478, 3194, 3124, 3500, 3091, 3700, 3112, 3430, 3706, 3219, 3259, 3119, 3175, 3196, 3707, 3166, 3356, 3071, 3088, 3099, 3114, 3123, 3331, 3199, 3241, 3392, 3574, 3157, 3158, 3449, 3164, 3218, 3069, 3070, 3102, 3118, 3308, 3312, 3186, 3187, 3523, 3127, 3128, 3380, 3494, 3255, 7753, 3404, 3429, 3334, 3492, 3132, 3333, 3141, 3438, 3165, 3381, 3072, 3569, 3406, 3568, 3702, 3193, 3125, 3350, 3277, 3388, 3389, 3352, 3213, 3390, 3307, 3435, 3348, 3144, 3246, 3305, 3203, 3057, 3420, 3084, 3425, 3208, 3094, 3096, 3210, 3103, 3527, 3113, 3116, 3407, 3291, 3359, 3169, 3722, 3386, 3237, 3206, 3266, 3311, 3195, 3571, 3437, 3153, 3448, 3306, 3416, 3417, 3215, 3278, 3560, 3466, 3418, 3409, 3074, 3421, 3078, 3382, 3422, 3717, 3085, 3280, 3468, 3424, 3275, 3093, 3426, 3289, 3314, 3300, 3474, 3428, 3458, 3095, 3309, 3107, 3339, 3530, 3117, 3120, 3556, 3290, 3337, 3104, 3481, 3332, 3482, 3284, 3335, 3393, 3558, 3557, 3562, 3220, 3431, 3432, 3224, 3282, 3433, 3391, 3136, 3137, 3254, 3362, 3256, 3495, 3434, 3303, 3304, 3244, 3147, 3286, 3059, 3505, 3285, 3551, 3512, 3513, 3514, 3515, 3517, 3516, 3518, 3519, 3520, 3450, 3161, 3287, 3540, 3575, 3539, 3167, 3054, 3338, 3355, 3066, 3357, 3383, 3058, 3419, 3265, 3075, 3076, 3252, 3394, 3713, 3423, 3197, 3081, 3086, 3087, 3427, 3209, 3475, 3211, 3101, 3221, 3106, 3272, 3524, 3108, 3283, 3408, 3216, 3190, 3445, 3274, 3205, 3483, 3260, 3279, 3325, 3202, 3292, 3183, 3349, 3271, 3723, 3223, 3413, 3412, 3414, 3452, 3525, 3130, 3295, 3298, 3351, 3385, 3453, 3705, 3397, 3233, 3234, 3240, 3487, 3456, 3488, 3457, 3363, 3405, 3143, 3459, 3264, 3201, 3436, 3296, 3253, 3443, 3440, 3444, 3439, 3281, 3384, 3294, 3509, 3447, 3262, 3533, 3521, 3411, 3415, 3162, 3191, 3198, 3263, 3454, 3410, 3270, 3726, 3172, 3461, 3462, 3698, 3463, 3464, 3465, 3526, 3467, 3470, 3469, 3471, 3472, 3105, 3257, 3226, 3477, 3110, 3534, 3727, 3480, 3313, 3552, 3553, 3732, 3731, 3724, 3536, 3537, 3485, 3276, 3484, 3126, 3486, 3493, 3232, 3134, 3135, 3379, 3251, 3715, 3716, 3489, 3725, 3245, 3173, 3288, 3204, 3207, 3528, 3501, 3502, 3503, 3504, 3496, 3529, 3728, 3498, 3499, 3225, 3446, 3729, 3730, 3522, 3506, 3507, 3508, 3541, 3711, 533: 3736, 631: 5853, 770: 3737, 3051, 3052, 3050, 805: 5852, 855: 5870, 994: 5871, 1024: 7754}, + {1965, 1965, 6: 1965, 9: 1965, 15: 1965, 51: 1965, 1965, 1965, 1965, 1965, 183: 1965, 531: 7760, 1965, 628: 1965, 725: 1965, 1965}, + // 4900 + {401, 401, 6: 401, 9: 5873, 15: 401, 51: 401, 401, 401, 401, 401, 532: 401, 725: 5917, 1075: 5916, 7755}, + {409, 409, 6: 409, 15: 409, 51: 409, 409, 409, 409, 409, 532: 7421, 1131: 7756}, + {382, 382, 6: 382, 15: 7437, 51: 382, 382, 7436, 7438, 7439, 1068: 7435, 1235: 7434, 7757}, + {387, 387, 6: 7461, 51: 387, 7462, 1128: 7758}, + {384, 384, 51: 7466, 1254: 7759}, + // 4905 + {418, 418}, + {57: 7761}, + {183: 7762}, + {723: 7763}, + {533: 5886, 997: 7764}, + // 4910 + {417, 417}, + {16: 1647, 18: 1647, 21: 1647, 214: 5509, 535: 1647, 538: 1647, 553: 1647, 557: 1647, 709: 1647}, + {16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7561, 1037: 7768}, + {2467, 2467, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, + {2468, 2468, 16: 2446, 18: 2446, 21: 2446, 535: 4724, 538: 2446, 553: 2446, 557: 7559, 709: 2446, 865: 7558, 891: 7557, 954: 7562}, + // 4915 + {2314, 2314, 3: 2861, 58: 2884, 84: 2863, 2866, 87: 2896, 2864, 3017, 103: 2898, 117: 3031, 159: 3033, 187: 2881, 195: 2879, 208: 3024, 222: 2892, 250: 2887, 254: 2869, 259: 2917, 266: 2883, 269: 2859, 277: 2916, 3027, 2865, 284: 3032, 296: 2895, 306: 2893, 308: 2860, 310: 2899, 330: 2885, 334: 2888, 341: 2897, 344: 2882, 357: 2874, 531: 2907, 2906, 547: 2905, 552: 2891, 557: 2915, 562: 3026, 575: 3020, 577: 2877, 582: 2875, 587: 2890, 608: 2904, 695: 2900, 710: 3030, 713: 2862, 3019, 724: 2857, 727: 2868, 743: 2867, 766: 2914, 2858, 775: 2911, 803: 2870, 806: 2913, 2901, 2902, 2903, 2912, 2910, 2909, 2908, 815: 2873, 817: 2995, 2994, 822: 3018, 2871, 2976, 826: 2988, 3004, 2876, 833: 2872, 839: 2934, 845: 2928, 2932, 2985, 2996, 857: 2936, 2878, 860: 3003, 3005, 894: 3023, 897: 2880, 904: 2921, 933: 3029, 943: 2929, 957: 3021, 962: 2979, 965: 2990, 967: 2993, 2886, 1035: 2941, 1090: 3025, 1099: 2949, 2919, 1102: 2920, 2923, 1105: 2926, 2924, 2927, 1109: 2925, 1111: 2922, 1113: 2930, 2931, 1116: 2937, 2889, 2974, 3014, 1121: 2938, 1132: 2945, 2939, 2940, 2946, 2947, 2948, 2944, 2950, 2951, 1142: 2943, 2942, 1145: 2933, 2894, 1148: 2952, 2966, 2953, 2954, 3015, 2957, 2956, 2962, 2961, 2963, 2958, 2964, 2965, 2955, 2960, 2959, 1166: 2918, 1169: 2935, 1174: 2970, 2968, 1177: 2969, 2967, 1182: 2972, 2973, 2971, 1188: 3010, 2975, 2977, 1198: 3028, 2978, 1208: 2980, 1210: 2981, 3007, 1213: 3011, 1237: 3012, 1239: 2983, 2984, 1248: 2989, 1251: 2986, 2987, 1258: 3009, 3013, 3022, 2992, 2991, 1268: 2997, 1270: 2999, 2998, 1273: 3001, 1275: 3008, 1278: 3000, 1284: 7770, 1298: 3002, 2982, 3006}, + {627, 627}, + } +) + +var yyDebug = 0 + +type yyLexer interface { + Lex(lval *yySymType) int + Errorf(format string, a ...interface{}) error + AppendError(err error) + AppendWarn(err error) + Errors() (warns []error, errs []error) +} + +type yyLexerEx interface { + yyLexer + Reduced(rule, state int, lval *yySymType) bool +} + +func yySymName(c int) (s string) { + x, ok := yyXLAT[c] + if ok { + return yySymNames[x] + } + + return __yyfmt__.Sprintf("%d", c) +} + +func yylex1(yylex yyLexer, lval *yySymType) (n int) { + n = yylex.Lex(lval) + if n <= 0 { + n = yyEOFCode + } + if yyDebug >= 3 { + __yyfmt__.Printf("\nlex %s(%#x %d), lval: %+v\n", yySymName(n), n, n, lval) + } + return n +} + +func yyParse(yylex yyLexer, parser *Parser) int { + const yyError = 1520 + + yyEx, _ := yylex.(yyLexerEx) + var yyn int + parser.yylval = yySymType{} + yyS := parser.cache + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + yyerrok := func() { + if yyDebug >= 2 { + __yyfmt__.Printf("yyerrok()\n") + } + Errflag = 0 + } + _ = yyerrok + yystate := 0 + yychar := -1 + var yyxchar int + var yyshift int + yyp := -1 + goto yystack + +ret0: + return 0 + +ret1: + return 1 + +yystack: + /* put a state and value onto the stack */ + yyp++ + if yyp+1 >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + parser.cache = yyS + } + parser.yyVAL = &yyS[yyp+1] + yyS[yyp].yys = yystate + +yynewstate: + if yychar < 0 { + yychar = yylex1(yylex, &parser.yylval) + var ok bool + if yyxchar, ok = yyXLAT[yychar]; !ok { + yyxchar = len(yySymNames) // > tab width + } + } + if yyDebug >= 4 { + var a []int + for _, v := range yyS[:yyp+1] { + a = append(a, v.yys) + } + __yyfmt__.Printf("state stack %v\n", a) + } + row := yyParseTab[yystate] + yyn = 0 + if yyxchar < len(row) { + if yyn = int(row[yyxchar]); yyn != 0 { + yyn += yyTabOfs + } + } + switch { + case yyn > 0: // shift + yychar = -1 + *parser.yyVAL = parser.yylval + yystate = yyn + yyshift = yyn + if yyDebug >= 2 { + __yyfmt__.Printf("shift, and goto state %d\n", yystate) + } + if Errflag > 0 { + Errflag-- + } + goto yystack + case yyn < 0: // reduce + case yystate == 1: // accept + if yyDebug >= 2 { + __yyfmt__.Println("accept") + } + goto ret0 + } + + if yyn == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + if yyDebug >= 1 { + __yyfmt__.Printf("no action for %s in state %d\n", yySymName(yychar), yystate) + } + msg, ok := yyXErrors[yyXError{yystate, yyxchar}] + if !ok { + msg, ok = yyXErrors[yyXError{yystate, -1}] + } + if !ok && yyshift != 0 { + msg, ok = yyXErrors[yyXError{yyshift, yyxchar}] + } + if !ok { + msg, ok = yyXErrors[yyXError{yyshift, -1}] + } + if !ok || msg == "" { + msg = "syntax error" + } + // ignore goyacc error message + yylex.AppendError(yylex.Errorf("")) + Nerrs++ + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for yyp >= 0 { + row := yyParseTab[yyS[yyp].yys] + if yyError < len(row) { + yyn = int(row[yyError]) + yyTabOfs + if yyn > 0 { // hit + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery found error shift in state %d\n", yyS[yyp].yys) + } + yystate = yyn /* simulate a shift of "error" */ + goto yystack + } + } + + /* the current p has no shift on "error", pop stack */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) + } + yyp-- + } + /* there is no state on the stack with an error shift ... abort */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery failed\n") + } + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if yyDebug >= 2 { + __yyfmt__.Printf("error recovery discards %s\n", yySymName(yychar)) + } + if yychar == yyEOFCode { + goto ret1 + } + + yychar = -1 + goto yynewstate /* try again in the same state */ + } + } + + r := -yyn + x0 := yyReductions[r] + x, n := x0.xsym, x0.components + yypt := yyp + _ = yypt // guard against "declared and not used" + + yyp -= n + if yyp+1 >= len(yyS) { + nyys := make([]yySymType, len(yyS)*2) + copy(nyys, yyS) + yyS = nyys + parser.cache = yyS + } + parser.yyVAL = &yyS[yyp+1] + + /* consult goto table to find next state */ + exState := yystate + yystate = int(yyParseTab[yyS[yyp].yys][x]) + yyTabOfs + /* reduction by production r */ + if yyDebug >= 2 { + __yyfmt__.Printf("reduce using rule %v (%s), and goto state %d\n", r, yySymNames[x], yystate) + } + + switch r { + case 2: + { + specs := yyS[yypt-1].item.([]*ast.AlterTableSpec) + if yyS[yypt-0].item != nil { + specs = append(specs, yyS[yypt-0].item.(*ast.AlterTableSpec)) + } + parser.yyVAL.statement = &ast.AlterTableStmt{ + Table: yyS[yypt-2].item.(*ast.TableName), + Specs: specs, + } + } + case 3: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, PartitionNames: yyS[yypt-1].item.([]model.CIStr), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 4: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-6].item.(*ast.TableName)}, + PartitionNames: yyS[yypt-3].item.([]model.CIStr), + IndexNames: yyS[yypt-1].item.([]model.CIStr), + IndexFlag: true, + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), + } + } + case 5: + { + parser.yyVAL.statement = &ast.CompactTableStmt{ + Table: yyS[yypt-1].item.(*ast.TableName), + ReplicaKind: ast.CompactReplicaKindAll, + } + } + case 6: + { + parser.yyVAL.statement = &ast.CompactTableStmt{ + Table: yyS[yypt-3].item.(*ast.TableName), + ReplicaKind: ast.CompactReplicaKindTiFlash, + } + } + case 7: + { + parser.yyVAL.statement = &ast.CompactTableStmt{ + Table: yyS[yypt-3].item.(*ast.TableName), + PartitionNames: yyS[yypt-0].item.([]model.CIStr), + ReplicaKind: ast.CompactReplicaKindAll, + } + } + case 8: + { + parser.yyVAL.statement = &ast.CompactTableStmt{ + Table: yyS[yypt-5].item.(*ast.TableName), + PartitionNames: yyS[yypt-2].item.([]model.CIStr), + ReplicaKind: ast.CompactReplicaKindTiFlash, + } + } + case 9: + { + parser.yyVAL.item = []*ast.ResourceGroupOption{yyS[yypt-0].item.(*ast.ResourceGroupOption)} + } + case 10: + { + if !ast.CheckAppend(yyS[yypt-1].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) + } + case 11: + { + if !ast.CheckAppend(yyS[yypt-2].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupOption), yyS[yypt-0].item.(*ast.ResourceGroupOption)) + } + case 12: + { + parser.yyVAL.item = uint64(1) + } + case 13: + { + parser.yyVAL.item = uint64(8) + } + case 14: + { + parser.yyVAL.item = uint64(16) + } + case 15: + { + parser.yyVAL.item = []*ast.ResourceGroupRunawayOption{yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)} + } + case 16: + { + if !ast.CheckRunawayAppend(yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) { + yylex.AppendError(yylex.Errorf("Dupliated runaway options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) + } + case 17: + { + if !ast.CheckRunawayAppend(yyS[yypt-2].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) { + yylex.AppendError(yylex.Errorf("Dupliated runaway options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupRunawayOption), yyS[yypt-0].item.(*ast.ResourceGroupRunawayOption)) + } + case 18: + { + parser.yyVAL.item = int32(model.WatchExact) + } + case 19: + { + parser.yyVAL.item = int32(model.WatchSimilar) + } + case 20: + { + parser.yyVAL.item = int32(model.WatchPlan) + } + case 21: + { + parser.yyVAL.item = int32(model.RunawayActionDryRun) + } + case 22: + { + parser.yyVAL.item = int32(model.RunawayActionCooldown) + } + case 23: + { + parser.yyVAL.item = int32(model.RunawayActionKill) + } + case 24: + { + _, err := time.ParseDuration(yyS[yypt-0].ident) + if err != nil { + yylex.AppendError(yylex.Errorf("The EXEC_ELAPSED option is not a valid duration: %s", err.Error())) + return 1 + } + parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayRule, StrValue: yyS[yypt-0].ident} + } + case 25: + { + parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayAction, IntValue: yyS[yypt-0].item.(int32)} + } + case 26: + { + dur := strings.ToLower(yyS[yypt-0].item.(string)) + if dur == "unlimited" { + dur = "" + } + if len(dur) > 0 { + _, err := time.ParseDuration(dur) + if err != nil { + yylex.AppendError(yylex.Errorf("The WATCH DURATION option is not a valid duration: %s", err.Error())) + return 1 + } + } + parser.yyVAL.item = &ast.ResourceGroupRunawayOption{Tp: ast.RunawayWatch, StrValue: dur, IntValue: yyS[yypt-1].item.(int32)} + } + case 27: + { + parser.yyVAL.item = "" + } + case 28: + { + parser.yyVAL.item = yyS[yypt-0].ident + } + case 29: + { + parser.yyVAL.item = "" + } + case 30: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceRURate, UintValue: yyS[yypt-0].item.(uint64)} + } + case 31: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourcePriority, UintValue: yyS[yypt-0].item.(uint64)} + } + case 32: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceBurstableOpiton, BoolValue: true} + } + case 33: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceBurstableOpiton, BoolValue: yyS[yypt-0].item.(bool)} + } + case 34: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: yyS[yypt-1].item.([]*ast.ResourceGroupRunawayOption)} + } + case 35: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: nil} + } + case 36: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupRunaway, RunawayOptionList: nil} + } + case 37: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption)} + } + case 38: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: nil} + } + case 39: + { + parser.yyVAL.item = &ast.ResourceGroupOption{Tp: ast.ResourceGroupBackground, BackgroundOptions: nil} + } + case 40: + { + parser.yyVAL.item = []*ast.ResourceGroupBackgroundOption{yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)} + } + case 41: + { + if !ast.CheckBackgroundAppend(yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) { + yylex.AppendError(yylex.Errorf("Dupliated background options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) + } + case 42: + { + if !ast.CheckBackgroundAppend(yyS[yypt-2].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) { + yylex.AppendError(yylex.Errorf("Dupliated background options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ResourceGroupBackgroundOption), yyS[yypt-0].item.(*ast.ResourceGroupBackgroundOption)) + } + case 43: + { + parser.yyVAL.item = &ast.ResourceGroupBackgroundOption{Type: ast.BackgroundOptionTaskNames, StrValue: yyS[yypt-0].ident} + } + case 44: + { + parser.yyVAL.item = []*ast.PlacementOption{yyS[yypt-0].item.(*ast.PlacementOption)} + } + case 45: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.PlacementOption), yyS[yypt-0].item.(*ast.PlacementOption)) + } + case 46: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.PlacementOption), yyS[yypt-0].item.(*ast.PlacementOption)) + } + case 47: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPrimaryRegion, StrValue: yyS[yypt-0].ident} + } + case 48: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionRegions, StrValue: yyS[yypt-0].ident} + } + case 49: + { + cnt := yyS[yypt-0].item.(uint64) + if cnt == 0 { + yylex.AppendError(yylex.Errorf("FOLLOWERS must be positive")) + return 1 + } + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionFollowerCount, UintValue: cnt} + } + case 50: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionVoterCount, UintValue: yyS[yypt-0].item.(uint64)} + } + case 51: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLearnerCount, UintValue: yyS[yypt-0].item.(uint64)} + } + case 52: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionSchedule, StrValue: yyS[yypt-0].ident} + } + case 53: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionConstraints, StrValue: yyS[yypt-0].ident} + } + case 54: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLeaderConstraints, StrValue: yyS[yypt-0].ident} + } + case 55: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionFollowerConstraints, StrValue: yyS[yypt-0].ident} + } + case 56: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionVoterConstraints, StrValue: yyS[yypt-0].ident} + } + case 57: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionLearnerConstraints, StrValue: yyS[yypt-0].ident} + } + case 58: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionSurvivalPreferences, StrValue: yyS[yypt-0].ident} + } + case 59: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} + } + case 60: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} + } + case 61: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} + } + case 62: + { + parser.yyVAL.item = &ast.PlacementOption{Tp: ast.PlacementOptionPolicy, StrValue: yyS[yypt-0].ident} + } + case 63: + { + parser.yyVAL.item = &ast.AttributesSpec{Default: true} + } + case 64: + { + parser.yyVAL.item = &ast.AttributesSpec{Default: false, Attributes: yyS[yypt-0].ident} + } + case 65: + { + parser.yyVAL.item = &ast.StatsOptionsSpec{Default: true} + } + case 66: + { + parser.yyVAL.item = &ast.StatsOptionsSpec{Default: false, StatsOptions: yyS[yypt-0].ident} + } + case 67: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTablePartition, + Partition: yyS[yypt-0].item.(*ast.PartitionOptions), + } + } else { + parser.yyVAL.item = nil + } + } + case 68: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRemovePartitioning, + } + } + case 69: + { + ret := yyS[yypt-0].item.(*ast.AlterTableSpec) + ret.NoWriteToBinlog = yyS[yypt-1].item.(bool) + parser.yyVAL.item = ret + } + case 70: + { + partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-1].expr} + startOffset := parser.yyVAL.offset + endOffset := parser.yylval.offset + partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + partitionMethod.SetOriginTextPosition(startOffset) + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableReorganizeLastPartition, + Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, + } + } + case 71: + { + partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-1].expr} + startOffset := parser.yyVAL.offset + endOffset := parser.yylval.offset + partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + // Needed for replacing syntactic sugar with generated partitioning definition string + partitionMethod.SetOriginTextPosition(startOffset) + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableReorganizeFirstPartition, + Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, + } + } + case 72: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTablePartitionAttributes, + PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-1].ident)}, + AttributesSpec: yyS[yypt-0].item.(*ast.AttributesSpec), + } + } + case 73: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTablePartitionOptions, + PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-1].ident)}, + Options: yyS[yypt-0].item.([]*ast.TableOption), + } + } + case 74: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRemoveTTL, + } + } + case 75: + { + parser.yyVAL.item = []string{} + } + case 76: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 77: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableOption, + Options: yyS[yypt-0].item.([]*ast.TableOption), + } + } + case 78: + { + tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ + Count: yyS[yypt-1].item.(uint64), + Labels: yyS[yypt-0].item.([]string), + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableSetTiFlashReplica, + TiFlashReplica: tiflashReplicaSpec, + } + } + case 79: + { + tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ + Count: yyS[yypt-1].item.(uint64), + Labels: yyS[yypt-0].item.([]string), + Hypo: true, + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableSetTiFlashReplica, + TiFlashReplica: tiflashReplicaSpec, + } + } + case 80: + { + op := &ast.AlterTableSpec{ + Tp: ast.AlterTableOption, + Options: []*ast.TableOption{{Tp: ast.TableOptionCharset, StrValue: yyS[yypt-1].ident, + UintValue: ast.TableOptionCharsetWithConvertTo}}, + } + if yyS[yypt-0].ident != "" { + op.Options = append(op.Options, &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident}) + } + parser.yyVAL.item = op + } + case 81: + { + op := &ast.AlterTableSpec{ + Tp: ast.AlterTableOption, + Options: []*ast.TableOption{{Tp: ast.TableOptionCharset, Default: true, + UintValue: ast.TableOptionCharsetWithConvertTo}}, + } + if yyS[yypt-0].ident != "" { + op.Options = append(op.Options, &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident}) + } + parser.yyVAL.item = op + } + case 82: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfNotExists: yyS[yypt-2].item.(bool), + Tp: ast.AlterTableAddColumns, + NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, + Position: yyS[yypt-0].item.(*ast.ColumnPosition), + } + } + case 83: + { + tes := yyS[yypt-1].item.([]interface{}) + var columnDefs []*ast.ColumnDef + var constraints []*ast.Constraint + for _, te := range tes { + switch te := te.(type) { + case *ast.ColumnDef: + columnDefs = append(columnDefs, te) + case *ast.Constraint: + constraints = append(constraints, te) + } + } + parser.yyVAL.item = &ast.AlterTableSpec{ + IfNotExists: yyS[yypt-3].item.(bool), + Tp: ast.AlterTableAddColumns, + NewColumns: columnDefs, + NewConstraints: constraints, + } + } + case 84: + { + constraint := yyS[yypt-0].item.(*ast.Constraint) + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAddConstraint, + Constraint: constraint, + } + } + case 85: + { + var defs []*ast.PartitionDefinition + if yyS[yypt-0].item != nil { + defs = yyS[yypt-0].item.([]*ast.PartitionDefinition) + } + noWriteToBinlog := yyS[yypt-1].item.(bool) + if noWriteToBinlog { + yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) + parser.lastErrorAsWarn() + } + parser.yyVAL.item = &ast.AlterTableSpec{ + IfNotExists: yyS[yypt-2].item.(bool), + NoWriteToBinlog: noWriteToBinlog, + Tp: ast.AlterTableAddPartitions, + PartDefinitions: defs, + } + } + case 86: + { + noWriteToBinlog := yyS[yypt-2].item.(bool) + if noWriteToBinlog { + yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) + parser.lastErrorAsWarn() + } + parser.yyVAL.item = &ast.AlterTableSpec{ + IfNotExists: yyS[yypt-3].item.(bool), + NoWriteToBinlog: noWriteToBinlog, + Tp: ast.AlterTableAddPartitions, + Num: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 87: + { + noWriteToBinlog := yyS[yypt-0].item.(bool) + if noWriteToBinlog { + yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) + parser.lastErrorAsWarn() + } + partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-2].expr} + startOffset := parser.yyVAL.offset + endOffset := parser.yylval.offset + partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + // Needed for replacing syntactic sugar with generated partitioning definition string + partitionMethod.SetOriginTextPosition(startOffset) + parser.yyVAL.item = &ast.AlterTableSpec{ + NoWriteToBinlog: noWriteToBinlog, + Tp: ast.AlterTableAddLastPartition, + Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, + } + } + case 88: + { + statsSpec := &ast.StatisticsSpec{ + StatsName: yyS[yypt-4].ident, + StatsType: yyS[yypt-3].item.(uint8), + Columns: yyS[yypt-1].item.([]*ast.ColumnName), + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAddStatistics, + IfNotExists: yyS[yypt-5].item.(bool), + Statistics: statsSpec, + } + } + case 89: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAttributes, + AttributesSpec: yyS[yypt-0].item.(*ast.AttributesSpec), + } + } + case 90: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableStatsOptions, + StatsOptionsSpec: yyS[yypt-0].item.(*ast.StatsOptionsSpec), + } + } + case 91: + { + yylex.AppendError(yylex.Errorf("The CHECK PARTITIONING clause is parsed but not implement yet.")) + parser.lastErrorAsWarn() + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableCheckPartitions, + } + if yyS[yypt-0].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + } + parser.yyVAL.item = ret + } + case 92: + { + noWriteToBinlog := yyS[yypt-1].item.(bool) + if noWriteToBinlog { + yylex.AppendError(yylex.Errorf("The NO_WRITE_TO_BINLOG option is parsed but ignored for now.")) + parser.lastErrorAsWarn() + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableCoalescePartitions, + NoWriteToBinlog: noWriteToBinlog, + Num: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 93: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-2].item.(bool), + Tp: ast.AlterTableDropColumn, + OldColumnName: yyS[yypt-1].item.(*ast.ColumnName), + } + } + case 94: + { + parser.yyVAL.item = &ast.AlterTableSpec{Tp: ast.AlterTableDropPrimaryKey} + } + case 95: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-1].item.(bool), + Tp: ast.AlterTableDropPartition, + PartitionNames: yyS[yypt-0].item.([]model.CIStr), + } + } + case 96: + { + partitionMethod := ast.PartitionMethod{Expr: yyS[yypt-2].expr} + startOffset := parser.yyVAL.offset + endOffset := parser.yylval.offset + partitionMethod.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + // Needed for replacing syntactic sugar with generated partitioning definition string + partitionMethod.SetOriginTextPosition(startOffset) + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-0].item.(bool), + Tp: ast.AlterTableDropFirstPartition, + Partition: &ast.PartitionOptions{PartitionMethod: partitionMethod}, + } + } + case 97: + { + statsSpec := &ast.StatisticsSpec{ + StatsName: yyS[yypt-0].ident, + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableDropStatistics, + IfExists: yyS[yypt-1].item.(bool), + Statistics: statsSpec, + } + } + case 98: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableExchangePartition, + PartitionNames: []model.CIStr{model.NewCIStr(yyS[yypt-4].ident)}, + NewTable: yyS[yypt-1].item.(*ast.TableName), + WithValidation: yyS[yypt-0].item.(bool), + } + } + case 99: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableTruncatePartition, + } + if yyS[yypt-0].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + } + parser.yyVAL.item = ret + } + case 100: + { + ret := &ast.AlterTableSpec{ + NoWriteToBinlog: yyS[yypt-1].item.(bool), + Tp: ast.AlterTableOptimizePartition, + } + if yyS[yypt-0].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + } + parser.yyVAL.item = ret + } + case 101: + { + ret := &ast.AlterTableSpec{ + NoWriteToBinlog: yyS[yypt-1].item.(bool), + Tp: ast.AlterTableRepairPartition, + } + if yyS[yypt-0].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + } + parser.yyVAL.item = ret + } + case 102: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableImportPartitionTablespace, + } + if yyS[yypt-1].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-1].item.([]model.CIStr) + } + parser.yyVAL.item = ret + yylex.AppendError(yylex.Errorf("The IMPORT PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 103: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableDiscardPartitionTablespace, + } + if yyS[yypt-1].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-1].item.([]model.CIStr) + } + parser.yyVAL.item = ret + yylex.AppendError(yylex.Errorf("The DISCARD PARTITION TABLESPACE clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 104: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableImportTablespace, + } + parser.yyVAL.item = ret + yylex.AppendError(yylex.Errorf("The IMPORT TABLESPACE clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 105: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableDiscardTablespace, + } + parser.yyVAL.item = ret + yylex.AppendError(yylex.Errorf("The DISCARD TABLESPACE clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 106: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableRebuildPartition, + NoWriteToBinlog: yyS[yypt-1].item.(bool), + } + if yyS[yypt-0].item == nil { + ret.OnAllPartitions = true + } else { + ret.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + } + parser.yyVAL.item = ret + } + case 107: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-1].item.(bool), + Tp: ast.AlterTableDropIndex, + Name: yyS[yypt-0].ident, + } + } + case 108: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-1].item.(bool), + Tp: ast.AlterTableDropForeignKey, + Name: yyS[yypt-0].ident, + } + } + case 109: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableOrderByColumns, + OrderByList: yyS[yypt-0].item.([]*ast.AlterOrderItem), + } + } + case 110: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableDisableKeys, + } + } + case 111: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableEnableKeys, + } + } + case 112: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-2].item.(bool), + Tp: ast.AlterTableModifyColumn, + NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, + Position: yyS[yypt-0].item.(*ast.ColumnPosition), + } + } + case 113: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + IfExists: yyS[yypt-3].item.(bool), + Tp: ast.AlterTableChangeColumn, + OldColumnName: yyS[yypt-2].item.(*ast.ColumnName), + NewColumns: []*ast.ColumnDef{yyS[yypt-1].item.(*ast.ColumnDef)}, + Position: yyS[yypt-0].item.(*ast.ColumnPosition), + } + } + case 114: + { + option := &ast.ColumnOption{Expr: yyS[yypt-0].expr} + colDef := &ast.ColumnDef{ + Name: yyS[yypt-3].item.(*ast.ColumnName), + Options: []*ast.ColumnOption{option}, + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAlterColumn, + NewColumns: []*ast.ColumnDef{colDef}, + } + } + case 115: + { + option := &ast.ColumnOption{Expr: yyS[yypt-1].expr} + colDef := &ast.ColumnDef{ + Name: yyS[yypt-5].item.(*ast.ColumnName), + Options: []*ast.ColumnOption{option}, + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAlterColumn, + NewColumns: []*ast.ColumnDef{colDef}, + } + } + case 116: + { + colDef := &ast.ColumnDef{ + Name: yyS[yypt-2].item.(*ast.ColumnName), + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAlterColumn, + NewColumns: []*ast.ColumnDef{colDef}, + } + } + case 117: + { + oldColName := &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-2].ident)} + newColName := &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-0].ident)} + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRenameColumn, + OldColumnName: oldColName, + NewColumnName: newColName, + } + } + case 118: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRenameTable, + NewTable: yyS[yypt-0].item.(*ast.TableName), + } + } + case 119: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRenameTable, + NewTable: yyS[yypt-0].item.(*ast.TableName), + } + } + case 120: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRenameTable, + NewTable: yyS[yypt-0].item.(*ast.TableName), + } + } + case 121: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableRenameIndex, + FromKey: model.NewCIStr(yyS[yypt-2].ident), + ToKey: model.NewCIStr(yyS[yypt-0].ident), + } + } + case 122: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableLock, + LockType: yyS[yypt-0].item.(ast.LockType), + } + } + case 123: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableWriteable, + Writeable: yyS[yypt-0].item.(bool), + } + } + case 124: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAlgorithm, + Algorithm: yyS[yypt-0].item.(ast.AlgorithmType), + } + } + case 125: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableForce, + } + } + case 126: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableWithValidation, + } + } + case 127: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableWithoutValidation, + } + } + case 128: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableSecondaryLoad, + } + yylex.AppendError(yylex.Errorf("The SECONDARY_LOAD clause is parsed but not implement yet.")) + parser.lastErrorAsWarn() + } + case 129: + { + // Parse it and ignore it. Just for compatibility. + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableSecondaryUnload, + } + yylex.AppendError(yylex.Errorf("The SECONDARY_UNLOAD VALIDATION clause is parsed but not implement yet.")) + parser.lastErrorAsWarn() + } + case 130: + { + c := &ast.Constraint{ + Name: yyS[yypt-1].ident, + Enforced: yyS[yypt-0].item.(bool), + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableAlterCheck, + Constraint: c, + } + } + case 131: + { + // Parse it and ignore it. Just for compatibility. + c := &ast.Constraint{ + Name: yyS[yypt-0].ident, + } + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableDropCheck, + Constraint: c, + } + } + case 132: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableIndexInvisible, + IndexName: model.NewCIStr(yyS[yypt-1].ident), + Visibility: yyS[yypt-0].item.(ast.IndexVisibility), + } + } + case 133: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableCache, + } + } + case 134: + { + parser.yyVAL.item = &ast.AlterTableSpec{ + Tp: ast.AlterTableNoCache, + } + } + case 135: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableReorganizePartition, + OnAllPartitions: true, + } + parser.yyVAL.item = ret + } + case 136: + { + ret := &ast.AlterTableSpec{ + Tp: ast.AlterTableReorganizePartition, + PartitionNames: yyS[yypt-4].item.([]model.CIStr), + PartDefinitions: yyS[yypt-1].item.([]*ast.PartitionDefinition), + } + parser.yyVAL.item = ret + } + case 137: + { + parser.yyVAL.item = nil + } + case 139: + { + parser.yyVAL.item = true + } + case 141: + { + parser.yyVAL.item = true + } + case 142: + { + parser.yyVAL.item = false + } + case 143: + { + parser.yyVAL.item = model.PrimaryKeyTypeClustered + } + case 144: + { + parser.yyVAL.item = model.PrimaryKeyTypeNonClustered + } + case 145: + { + parser.yyVAL.item = ast.AlgorithmTypeDefault + } + case 146: + { + parser.yyVAL.item = ast.AlgorithmTypeCopy + } + case 147: + { + parser.yyVAL.item = ast.AlgorithmTypeInplace + } + case 148: + { + parser.yyVAL.item = ast.AlgorithmTypeInstant + } + case 149: + { + yylex.AppendError(ErrUnknownAlterAlgorithm.GenWithStackByArgs(yyS[yypt-2].ident)) + return 1 + } + case 150: + { + parser.yyVAL.item = ast.LockTypeDefault + } + case 151: + { + id := strings.ToUpper(yyS[yypt-0].ident) + + if id == "NONE" { + parser.yyVAL.item = ast.LockTypeNone + } else if id == "SHARED" { + parser.yyVAL.item = ast.LockTypeShared + } else if id == "EXCLUSIVE" { + parser.yyVAL.item = ast.LockTypeExclusive + } else { + yylex.AppendError(ErrUnknownAlterLock.GenWithStackByArgs(yyS[yypt-0].ident)) + return 1 + } + } + case 152: + { + parser.yyVAL.item = true + } + case 153: + { + parser.yyVAL.item = false + } + case 160: + { + parser.yyVAL.item = &ast.ColumnPosition{Tp: ast.ColumnPositionNone} + } + case 161: + { + parser.yyVAL.item = &ast.ColumnPosition{Tp: ast.ColumnPositionFirst} + } + case 162: + { + parser.yyVAL.item = &ast.ColumnPosition{ + Tp: ast.ColumnPositionAfter, + RelativeColumn: yyS[yypt-0].item.(*ast.ColumnName), + } + } + case 163: + { + parser.yyVAL.item = make([]*ast.AlterTableSpec, 0, 1) + } + case 165: + { + parser.yyVAL.item = []*ast.AlterTableSpec{yyS[yypt-0].item.(*ast.AlterTableSpec)} + } + case 166: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.AlterTableSpec), yyS[yypt-0].item.(*ast.AlterTableSpec)) + } + case 167: + { + parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} + } + case 168: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) + } + case 169: + { + parser.yyVAL.item = nil + } + case 170: + { + parser.yyVAL.item = nil + } + case 171: + { + parser.yyVAL.item = yyS[yypt-0].ident + } + case 173: + { + parser.yyVAL.statement = &ast.RenameTableStmt{ + TableToTables: yyS[yypt-0].item.([]*ast.TableToTable), + } + } + case 174: + { + parser.yyVAL.item = []*ast.TableToTable{yyS[yypt-0].item.(*ast.TableToTable)} + } + case 175: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableToTable), yyS[yypt-0].item.(*ast.TableToTable)) + } + case 176: + { + parser.yyVAL.item = &ast.TableToTable{ + OldTable: yyS[yypt-2].item.(*ast.TableName), + NewTable: yyS[yypt-0].item.(*ast.TableName), + } + } + case 177: + { + parser.yyVAL.statement = &ast.RenameUserStmt{ + UserToUsers: yyS[yypt-0].item.([]*ast.UserToUser), + } + } + case 178: + { + parser.yyVAL.item = []*ast.UserToUser{yyS[yypt-0].item.(*ast.UserToUser)} + } + case 179: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserToUser), yyS[yypt-0].item.(*ast.UserToUser)) + } + case 180: + { + parser.yyVAL.item = &ast.UserToUser{ + OldUser: yyS[yypt-2].item.(*auth.UserIdentity), + NewUser: yyS[yypt-0].item.(*auth.UserIdentity), + } + } + case 181: + { + parser.yyVAL.statement = &ast.RecoverTableStmt{ + JobID: yyS[yypt-0].item.(int64), + } + } + case 182: + { + parser.yyVAL.statement = &ast.RecoverTableStmt{ + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 183: + { + parser.yyVAL.statement = &ast.RecoverTableStmt{ + Table: yyS[yypt-1].item.(*ast.TableName), + JobNum: yyS[yypt-0].item.(int64), + } + } + case 184: + { + parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ + FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), + } + } + case 185: + { + parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ + Tables: yyS[yypt-2].item.([]*ast.TableName), + FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), + } + } + case 186: + { + parser.yyVAL.statement = &ast.FlashBackToTimestampStmt{ + DBName: model.NewCIStr(yyS[yypt-2].ident), + FlashbackTS: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), + } + } + case 187: + { + parser.yyVAL.statement = &ast.FlashBackTableStmt{ + Table: yyS[yypt-1].item.(*ast.TableName), + NewName: yyS[yypt-0].ident, + } + } + case 188: + { + parser.yyVAL.ident = "" + } + case 189: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 190: + { + parser.yyVAL.statement = &ast.FlashBackDatabaseStmt{ + DBName: model.NewCIStr(yyS[yypt-1].ident), + NewName: yyS[yypt-0].ident, + } + } + case 191: + { + parser.yyVAL.statement = &ast.SplitRegionStmt{ + SplitSyntaxOpt: yyS[yypt-4].item.(*ast.SplitSyntaxOption), + Table: yyS[yypt-2].item.(*ast.TableName), + PartitionNames: yyS[yypt-1].item.([]model.CIStr), + SplitOpt: yyS[yypt-0].item.(*ast.SplitOption), + } + } + case 192: + { + parser.yyVAL.statement = &ast.SplitRegionStmt{ + SplitSyntaxOpt: yyS[yypt-6].item.(*ast.SplitSyntaxOption), + Table: yyS[yypt-4].item.(*ast.TableName), + PartitionNames: yyS[yypt-3].item.([]model.CIStr), + IndexName: model.NewCIStr(yyS[yypt-1].ident), + SplitOpt: yyS[yypt-0].item.(*ast.SplitOption), + } + } + case 193: + { + parser.yyVAL.item = &ast.SplitOption{ + Lower: yyS[yypt-4].item.([]ast.ExprNode), + Upper: yyS[yypt-2].item.([]ast.ExprNode), + Num: yyS[yypt-0].item.(int64), + } + } + case 194: + { + parser.yyVAL.item = &ast.SplitOption{ + ValueLists: yyS[yypt-0].item.([][]ast.ExprNode), + } + } + case 195: + { + parser.yyVAL.item = &ast.SplitSyntaxOption{} + } + case 196: + { + parser.yyVAL.item = &ast.SplitSyntaxOption{ + HasRegionFor: true, + } + } + case 197: + { + parser.yyVAL.item = &ast.SplitSyntaxOption{ + HasPartition: true, + } + } + case 198: + { + parser.yyVAL.item = &ast.SplitSyntaxOption{ + HasRegionFor: true, + HasPartition: true, + } + } + case 199: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: yyS[yypt-2].item.([]*ast.TableName), ColumnChoice: yyS[yypt-1].item.(model.ColumnChoice), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 200: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, IndexNames: yyS[yypt-1].item.([]model.CIStr), IndexFlag: true, AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 201: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, IndexNames: yyS[yypt-1].item.([]model.CIStr), IndexFlag: true, Incremental: true, AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 202: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, PartitionNames: yyS[yypt-2].item.([]model.CIStr), ColumnChoice: yyS[yypt-1].item.(model.ColumnChoice), AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 203: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, + PartitionNames: yyS[yypt-3].item.([]model.CIStr), + IndexNames: yyS[yypt-1].item.([]model.CIStr), + IndexFlag: true, + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), + } + } + case 204: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, + PartitionNames: yyS[yypt-3].item.([]model.CIStr), + IndexNames: yyS[yypt-1].item.([]model.CIStr), + IndexFlag: true, + Incremental: true, + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), + } + } + case 205: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, + ColumnNames: yyS[yypt-1].item.([]model.CIStr), + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt), + HistogramOperation: ast.HistogramOperationUpdate, + } + } + case 206: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-4].item.(*ast.TableName)}, + ColumnNames: yyS[yypt-0].item.([]model.CIStr), + HistogramOperation: ast.HistogramOperationDrop, + } + } + case 207: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-3].item.(*ast.TableName)}, + ColumnNames: yyS[yypt-1].item.([]model.CIStr), + ColumnChoice: model.ColumnList, + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 208: + { + parser.yyVAL.statement = &ast.AnalyzeTableStmt{ + TableNames: []*ast.TableName{yyS[yypt-5].item.(*ast.TableName)}, + PartitionNames: yyS[yypt-3].item.([]model.CIStr), + ColumnNames: yyS[yypt-1].item.([]model.CIStr), + ColumnChoice: model.ColumnList, + AnalyzeOpts: yyS[yypt-0].item.([]ast.AnalyzeOpt)} + } + case 209: + { + parser.yyVAL.item = model.DefaultChoice + } + case 210: + { + parser.yyVAL.item = model.AllColumns + } + case 211: + { + parser.yyVAL.item = model.PredicateColumns + } + case 212: + { + parser.yyVAL.item = []ast.AnalyzeOpt{} + } + case 213: + { + parser.yyVAL.item = yyS[yypt-0].item.([]ast.AnalyzeOpt) + } + case 214: + { + parser.yyVAL.item = []ast.AnalyzeOpt{yyS[yypt-0].item.(ast.AnalyzeOpt)} + } + case 215: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.AnalyzeOpt), yyS[yypt-0].item.(ast.AnalyzeOpt)) + } + case 216: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumBuckets, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} + } + case 217: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumTopN, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} + } + case 218: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchDepth, Value: ast.NewValueExpr(yyS[yypt-2].item, "", "")} + } + case 219: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptCMSketchWidth, Value: ast.NewValueExpr(yyS[yypt-2].item, "", "")} + } + case 220: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptNumSamples, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} + } + case 221: + { + parser.yyVAL.item = ast.AnalyzeOpt{Type: ast.AnalyzeOptSampleRate, Value: ast.NewValueExpr(yyS[yypt-1].item, "", "")} + } + case 222: + { + parser.yyVAL.item = &ast.Assignment{Column: yyS[yypt-2].item.(*ast.ColumnName), Expr: yyS[yypt-0].expr} + } + case 223: + { + parser.yyVAL.item = []*ast.Assignment{yyS[yypt-0].item.(*ast.Assignment)} + } + case 224: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.Assignment), yyS[yypt-0].item.(*ast.Assignment)) + } + case 225: + { + parser.yyVAL.item = []*ast.Assignment{} + } + case 227: + { + parser.yyVAL.statement = &ast.BeginStmt{} + } + case 228: + { + parser.yyVAL.statement = &ast.BeginStmt{ + Mode: ast.Pessimistic, + } + } + case 229: + { + parser.yyVAL.statement = &ast.BeginStmt{ + Mode: ast.Optimistic, + } + } + case 230: + { + parser.yyVAL.statement = &ast.BeginStmt{} + } + case 231: + { + parser.yyVAL.statement = &ast.BeginStmt{} + } + case 232: + { + parser.yyVAL.statement = &ast.BeginStmt{} + } + case 233: + { + parser.yyVAL.statement = &ast.BeginStmt{ + CausalConsistencyOnly: true, + } + } + case 234: + { + parser.yyVAL.statement = &ast.BeginStmt{ + ReadOnly: true, + } + } + case 235: + { + parser.yyVAL.statement = &ast.BeginStmt{ + ReadOnly: true, + AsOf: yyS[yypt-0].item.(*ast.AsOfClause), + } + } + case 236: + { + parser.yyVAL.statement = &ast.BinlogStmt{Str: yyS[yypt-0].ident} + } + case 237: + { + parser.yyVAL.item = []*ast.ColumnDef{yyS[yypt-0].item.(*ast.ColumnDef)} + } + case 238: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnDef), yyS[yypt-0].item.(*ast.ColumnDef)) + } + case 239: + { + colDef := &ast.ColumnDef{Name: yyS[yypt-2].item.(*ast.ColumnName), Tp: yyS[yypt-1].item.(*types.FieldType), Options: yyS[yypt-0].item.([]*ast.ColumnOption)} + if err := colDef.Validate(); err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.item = colDef + } + case 240: + { + // TODO: check flen 0 + tp := types.NewFieldType(mysql.TypeLonglong) + options := []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} + options = append(options, yyS[yypt-0].item.([]*ast.ColumnOption)...) + tp.AddFlag(mysql.UnsignedFlag) + colDef := &ast.ColumnDef{Name: yyS[yypt-2].item.(*ast.ColumnName), Tp: tp, Options: options} + if err := colDef.Validate(); err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.item = colDef + } + case 241: + { + parser.yyVAL.item = &ast.ColumnName{Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 242: + { + parser.yyVAL.item = &ast.ColumnName{Table: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 243: + { + parser.yyVAL.item = &ast.ColumnName{Schema: model.NewCIStr(yyS[yypt-4].ident), Table: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 244: + { + parser.yyVAL.item = []*ast.ColumnName{yyS[yypt-0].item.(*ast.ColumnName)} + } + case 245: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnName), yyS[yypt-0].item.(*ast.ColumnName)) + } + case 246: + { + parser.yyVAL.item = []*ast.ColumnName{} + } + case 248: + { + parser.yyVAL.item = []model.CIStr{} + } + case 249: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 250: + { + parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} + } + case 251: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) + } + case 252: + { + parser.yyVAL.item = []*ast.ColumnNameOrUserVar{} + } + case 254: + { + parser.yyVAL.item = []*ast.ColumnNameOrUserVar{yyS[yypt-0].item.(*ast.ColumnNameOrUserVar)} + } + case 255: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ColumnNameOrUserVar), yyS[yypt-0].item.(*ast.ColumnNameOrUserVar)) + } + case 256: + { + parser.yyVAL.item = &ast.ColumnNameOrUserVar{ColumnName: yyS[yypt-0].item.(*ast.ColumnName)} + } + case 257: + { + parser.yyVAL.item = &ast.ColumnNameOrUserVar{UserVar: yyS[yypt-0].expr.(*ast.VariableExpr)} + } + case 258: + { + parser.yyVAL.item = []*ast.ColumnNameOrUserVar{} + } + case 259: + { + parser.yyVAL.item = yyS[yypt-1].item.([]*ast.ColumnNameOrUserVar) + } + case 260: + { + parser.yyVAL.statement = &ast.CommitStmt{} + } + case 261: + { + parser.yyVAL.statement = &ast.CommitStmt{CompletionType: yyS[yypt-0].item.(ast.CompletionType)} + } + case 265: + { + parser.yyVAL.ident = "NOT" + } + case 266: + { + parser.yyVAL.item = true + } + case 267: + { + parser.yyVAL.item = false + } + case 268: + { + parser.yyVAL.item = true + } + case 270: + { + parser.yyVAL.item = 0 + } + case 271: + { + if yyS[yypt-0].item.(bool) { + parser.yyVAL.item = 1 + } else { + parser.yyVAL.item = 2 + } + } + case 272: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionNotNull} + } + case 273: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionNull} + } + case 274: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionAutoIncrement} + } + case 275: + { + // KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY + // can also be specified as just KEY when given in a column definition. + // See http://dev.mysql.com/doc/refman/5.7/en/create-table.html + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey} + } + case 276: + { + // KEY is normally a synonym for INDEX. The key attribute PRIMARY KEY + // can also be specified as just KEY when given in a column definition. + // See http://dev.mysql.com/doc/refman/5.7/en/create-table.html + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionPrimaryKey, PrimaryKeyTp: yyS[yypt-0].item.(model.PrimaryKeyType)} + } + case 277: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} + } + case 278: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionUniqKey} + } + case 279: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionDefaultValue, Expr: yyS[yypt-0].expr} + } + case 280: + { + parser.yyVAL.item = []*ast.ColumnOption{{Tp: ast.ColumnOptionNotNull}, {Tp: ast.ColumnOptionAutoIncrement}, {Tp: ast.ColumnOptionUniqKey}} + } + case 281: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionOnUpdate, Expr: yyS[yypt-0].expr} + } + case 282: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionComment, Expr: ast.NewValueExpr(yyS[yypt-0].ident, "", "")} + } + case 283: + { + // See https://dev.mysql.com/doc/refman/5.7/en/create-table.html + // The CHECK clause is parsed but ignored by all storage engines. + // See the branch named `EnforcedOrNotOrNotNullOpt`. + + optionCheck := &ast.ColumnOption{ + Tp: ast.ColumnOptionCheck, + Expr: yyS[yypt-2].expr, + Enforced: true, + } + // Keep the column type check constraint name. + if yyS[yypt-5].item != nil { + optionCheck.ConstraintName = yyS[yypt-5].item.(string) + } + switch yyS[yypt-0].item.(int) { + case 0: + parser.yyVAL.item = []*ast.ColumnOption{optionCheck, {Tp: ast.ColumnOptionNotNull}} + case 1: + optionCheck.Enforced = true + parser.yyVAL.item = optionCheck + case 2: + optionCheck.Enforced = false + parser.yyVAL.item = optionCheck + default: + } + } + case 284: + { + startOffset := parser.startOffset(&yyS[yypt-2]) + endOffset := parser.endOffset(&yyS[yypt-1]) + expr := yyS[yypt-2].expr + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + + parser.yyVAL.item = &ast.ColumnOption{ + Tp: ast.ColumnOptionGenerated, + Expr: expr, + Stored: yyS[yypt-0].item.(bool), + } + } + case 285: + { + parser.yyVAL.item = &ast.ColumnOption{ + Tp: ast.ColumnOptionReference, + Refer: yyS[yypt-0].item.(*ast.ReferenceDef), + } + } + case 286: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionCollate, StrValue: yyS[yypt-0].ident} + } + case 287: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionColumnFormat, StrValue: yyS[yypt-0].ident} + } + case 288: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionStorage, StrValue: yyS[yypt-0].ident} + yylex.AppendError(yylex.Errorf("The STORAGE clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 289: + { + parser.yyVAL.item = &ast.ColumnOption{Tp: ast.ColumnOptionAutoRandom, AutoRandOpt: yyS[yypt-0].item.(ast.AutoRandomOption)} + } + case 290: + { + parser.yyVAL.item = ast.AutoRandomOption{ShardBits: types.UnspecifiedLength, RangeBits: types.UnspecifiedLength} + } + case 291: + { + parser.yyVAL.item = ast.AutoRandomOption{ShardBits: int(yyS[yypt-1].item.(uint64)), RangeBits: types.UnspecifiedLength} + } + case 292: + { + parser.yyVAL.item = ast.AutoRandomOption{ShardBits: int(yyS[yypt-3].item.(uint64)), RangeBits: int(yyS[yypt-1].item.(uint64))} + } + case 296: + { + parser.yyVAL.ident = "DEFAULT" + } + case 297: + { + parser.yyVAL.ident = "FIXED" + } + case 298: + { + parser.yyVAL.ident = "DYNAMIC" + } + case 301: + { + parser.yyVAL.item = false + } + case 302: + { + parser.yyVAL.item = false + } + case 303: + { + parser.yyVAL.item = true + } + case 304: + { + if columnOption, ok := yyS[yypt-0].item.(*ast.ColumnOption); ok { + parser.yyVAL.item = []*ast.ColumnOption{columnOption} + } else { + parser.yyVAL.item = yyS[yypt-0].item + } + } + case 305: + { + if columnOption, ok := yyS[yypt-0].item.(*ast.ColumnOption); ok { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ColumnOption), columnOption) + } else { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.ColumnOption), yyS[yypt-0].item.([]*ast.ColumnOption)...) + } + } + case 306: + { + parser.yyVAL.item = []*ast.ColumnOption{} + } + case 308: + { + c := &ast.Constraint{ + Tp: ast.ConstraintPrimaryKey, + Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, + IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, + } + if yyS[yypt-0].item != nil { + c.Option = yyS[yypt-0].item.(*ast.IndexOption) + } + if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { + if c.Option == nil { + c.Option = &ast.IndexOption{} + } + c.Option.Tp = indexType.(model.IndexType) + } + parser.yyVAL.item = c + } + case 309: + { + c := &ast.Constraint{ + Tp: ast.ConstraintFulltext, + Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + Name: yyS[yypt-4].item.(*ast.NullString).String, + IsEmptyIndex: yyS[yypt-4].item.(*ast.NullString).Empty, + } + if yyS[yypt-0].item != nil { + c.Option = yyS[yypt-0].item.(*ast.IndexOption) + } + parser.yyVAL.item = c + } + case 310: + { + c := &ast.Constraint{ + IfNotExists: yyS[yypt-5].item.(bool), + Tp: ast.ConstraintIndex, + Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, + IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, + } + if yyS[yypt-0].item != nil { + c.Option = yyS[yypt-0].item.(*ast.IndexOption) + } + if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { + if c.Option == nil { + c.Option = &ast.IndexOption{} + } + c.Option.Tp = indexType.(model.IndexType) + } + parser.yyVAL.item = c + } + case 311: + { + c := &ast.Constraint{ + Tp: ast.ConstraintUniq, + Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + Name: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).String, + IsEmptyIndex: yyS[yypt-4].item.([]interface{})[0].(*ast.NullString).Empty, + } + if yyS[yypt-0].item != nil { + c.Option = yyS[yypt-0].item.(*ast.IndexOption) + } + + if indexType := yyS[yypt-4].item.([]interface{})[1]; indexType != nil { + if c.Option == nil { + c.Option = &ast.IndexOption{} + } + c.Option.Tp = indexType.(model.IndexType) + } + parser.yyVAL.item = c + } + case 312: + { + parser.yyVAL.item = &ast.Constraint{ + IfNotExists: yyS[yypt-5].item.(bool), + Tp: ast.ConstraintForeignKey, + Keys: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + Name: yyS[yypt-4].item.(*ast.NullString).String, + Refer: yyS[yypt-0].item.(*ast.ReferenceDef), + IsEmptyIndex: yyS[yypt-4].item.(*ast.NullString).Empty, + } + } + case 313: + { + parser.yyVAL.item = &ast.Constraint{ + Tp: ast.ConstraintCheck, + Expr: yyS[yypt-2].expr.(ast.ExprNode), + Enforced: yyS[yypt-0].item.(bool), + } + } + case 314: + { + parser.yyVAL.item = ast.MatchFull + } + case 315: + { + parser.yyVAL.item = ast.MatchPartial + } + case 316: + { + parser.yyVAL.item = ast.MatchSimple + } + case 317: + { + parser.yyVAL.item = ast.MatchNone + } + case 318: + { + parser.yyVAL.item = yyS[yypt-0].item + yylex.AppendError(yylex.Errorf("The MATCH clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 319: + { + onDeleteUpdate := yyS[yypt-0].item.([2]interface{}) + parser.yyVAL.item = &ast.ReferenceDef{ + Table: yyS[yypt-3].item.(*ast.TableName), + IndexPartSpecifications: yyS[yypt-2].item.([]*ast.IndexPartSpecification), + OnDelete: onDeleteUpdate[0].(*ast.OnDeleteOpt), + OnUpdate: onDeleteUpdate[1].(*ast.OnUpdateOpt), + Match: yyS[yypt-1].item.(ast.MatchType), + } + } + case 320: + { + parser.yyVAL.item = &ast.OnDeleteOpt{ReferOpt: yyS[yypt-0].item.(model.ReferOptionType)} + } + case 321: + { + parser.yyVAL.item = &ast.OnUpdateOpt{ReferOpt: yyS[yypt-0].item.(model.ReferOptionType)} + } + case 322: + { + parser.yyVAL.item = [2]interface{}{&ast.OnDeleteOpt{}, &ast.OnUpdateOpt{}} + } + case 323: + { + parser.yyVAL.item = [2]interface{}{yyS[yypt-0].item, &ast.OnUpdateOpt{}} + } + case 324: + { + parser.yyVAL.item = [2]interface{}{&ast.OnDeleteOpt{}, yyS[yypt-0].item} + } + case 325: + { + parser.yyVAL.item = [2]interface{}{yyS[yypt-1].item, yyS[yypt-0].item} + } + case 326: + { + parser.yyVAL.item = [2]interface{}{yyS[yypt-0].item, yyS[yypt-1].item} + } + case 327: + { + parser.yyVAL.item = model.ReferOptionRestrict + } + case 328: + { + parser.yyVAL.item = model.ReferOptionCascade + } + case 329: + { + parser.yyVAL.item = model.ReferOptionSetNull + } + case 330: + { + parser.yyVAL.item = model.ReferOptionNoAction + } + case 331: + { + parser.yyVAL.item = model.ReferOptionSetDefault + yylex.AppendError(yylex.Errorf("The SET DEFAULT clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 336: + { + parser.yyVAL.expr = yyS[yypt-1].expr.(*ast.FuncCallExpr) + } + case 337: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-2].ident), + } + } + case 338: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 339: + { + parser.yyVAL.expr = yyS[yypt-1].expr.(*ast.FuncCallExpr) + } + case 341: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} + } + case 342: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP")} + } + case 343: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_TIMESTAMP"), Args: []ast.ExprNode{ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}} + } + case 344: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_DATE")} + } + case 345: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr("CURRENT_DATE")} + } + case 346: + { + objNameExpr := &ast.TableNameExpr{ + Name: yyS[yypt-0].item.(*ast.TableName), + } + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.NextVal), + Args: []ast.ExprNode{objNameExpr}, + } + } + case 347: + { + objNameExpr := &ast.TableNameExpr{ + Name: yyS[yypt-1].item.(*ast.TableName), + } + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.NextVal), + Args: []ast.ExprNode{objNameExpr}, + } + } + case 357: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) + } + case 358: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Plus, V: ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation)} + } + case 359: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Minus, V: ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation)} + } + case 363: + { + parser.yyVAL.item = ast.StatsTypeCardinality + } + case 364: + { + parser.yyVAL.item = ast.StatsTypeDependency + } + case 365: + { + parser.yyVAL.item = ast.StatsTypeCorrelation + } + case 366: + { + parser.yyVAL.item = ast.BindingStatusTypeEnabled + } + case 367: + { + parser.yyVAL.item = ast.BindingStatusTypeDisabled + } + case 368: + { + parser.yyVAL.statement = &ast.CreateStatisticsStmt{ + IfNotExists: yyS[yypt-9].item.(bool), + StatsName: yyS[yypt-8].ident, + StatsType: yyS[yypt-6].item.(uint8), + Table: yyS[yypt-3].item.(*ast.TableName), + Columns: yyS[yypt-1].item.([]*ast.ColumnName), + } + } + case 369: + { + parser.yyVAL.statement = &ast.DropStatisticsStmt{StatsName: yyS[yypt-0].ident} + } + case 370: + { + var indexOption *ast.IndexOption + if yyS[yypt-1].item != nil { + indexOption = yyS[yypt-1].item.(*ast.IndexOption) + if indexOption.Tp == model.IndexTypeInvalid { + if yyS[yypt-7].item != nil { + indexOption.Tp = yyS[yypt-7].item.(model.IndexType) + } + } + } else { + indexOption = &ast.IndexOption{} + if yyS[yypt-7].item != nil { + indexOption.Tp = yyS[yypt-7].item.(model.IndexType) + } + } + var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm + if yyS[yypt-0].item != nil { + indexLockAndAlgorithm = yyS[yypt-0].item.(*ast.IndexLockAndAlgorithm) + if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { + indexLockAndAlgorithm = nil + } + } + parser.yyVAL.statement = &ast.CreateIndexStmt{ + IfNotExists: yyS[yypt-9].item.(bool), + IndexName: yyS[yypt-8].ident, + Table: yyS[yypt-5].item.(*ast.TableName), + IndexPartSpecifications: yyS[yypt-3].item.([]*ast.IndexPartSpecification), + IndexOption: indexOption, + KeyType: yyS[yypt-11].item.(ast.IndexKeyType), + LockAlg: indexLockAndAlgorithm, + } + } + case 371: + { + parser.yyVAL.item = ([]*ast.IndexPartSpecification)(nil) + } + case 372: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 373: + { + parser.yyVAL.item = []*ast.IndexPartSpecification{yyS[yypt-0].item.(*ast.IndexPartSpecification)} + } + case 374: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.IndexPartSpecification), yyS[yypt-0].item.(*ast.IndexPartSpecification)) + } + case 375: + { + parser.yyVAL.item = &ast.IndexPartSpecification{Column: yyS[yypt-2].item.(*ast.ColumnName), Length: yyS[yypt-1].item.(int), Desc: yyS[yypt-0].item.(bool)} + } + case 376: + { + parser.yyVAL.item = &ast.IndexPartSpecification{Expr: yyS[yypt-2].expr, Desc: yyS[yypt-0].item.(bool)} + } + case 377: + { + parser.yyVAL.item = nil + } + case 378: + { + parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ + LockTp: yyS[yypt-0].item.(ast.LockType), + AlgorithmTp: ast.AlgorithmTypeDefault, + } + } + case 379: + { + parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ + LockTp: ast.LockTypeDefault, + AlgorithmTp: yyS[yypt-0].item.(ast.AlgorithmType), + } + } + case 380: + { + parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ + LockTp: yyS[yypt-1].item.(ast.LockType), + AlgorithmTp: yyS[yypt-0].item.(ast.AlgorithmType), + } + } + case 381: + { + parser.yyVAL.item = &ast.IndexLockAndAlgorithm{ + LockTp: yyS[yypt-0].item.(ast.LockType), + AlgorithmTp: yyS[yypt-1].item.(ast.AlgorithmType), + } + } + case 382: + { + parser.yyVAL.item = ast.IndexKeyTypeNone + } + case 383: + { + parser.yyVAL.item = ast.IndexKeyTypeUnique + } + case 384: + { + parser.yyVAL.item = ast.IndexKeyTypeSpatial + } + case 385: + { + parser.yyVAL.item = ast.IndexKeyTypeFullText + } + case 386: + { + parser.yyVAL.statement = &ast.AlterDatabaseStmt{ + Name: model.NewCIStr(yyS[yypt-1].ident), + AlterDefaultDatabase: false, + Options: yyS[yypt-0].item.([]*ast.DatabaseOption), + } + } + case 387: + { + parser.yyVAL.statement = &ast.AlterDatabaseStmt{ + Name: model.NewCIStr(""), + AlterDefaultDatabase: true, + Options: yyS[yypt-0].item.([]*ast.DatabaseOption), + } + } + case 388: + { + parser.yyVAL.statement = &ast.CreateDatabaseStmt{ + IfNotExists: yyS[yypt-2].item.(bool), + Name: model.NewCIStr(yyS[yypt-1].ident), + Options: yyS[yypt-0].item.([]*ast.DatabaseOption), + } + } + case 393: + { + parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionCharset, Value: yyS[yypt-0].ident} + } + case 394: + { + parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionCollate, Value: yyS[yypt-0].ident} + } + case 395: + { + parser.yyVAL.item = &ast.DatabaseOption{Tp: ast.DatabaseOptionEncryption, Value: yyS[yypt-0].ident} + } + case 396: + { + placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) + parser.yyVAL.item = &ast.DatabaseOption{ + // offset trick, enums are identical but of different type + Tp: ast.DatabaseOptionType(placementOptions.Tp), + Value: placementOptions.StrValue, + UintValue: placementOptions.UintValue, + } + } + case 397: + { + placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) + parser.yyVAL.item = &ast.DatabaseOption{ + // offset trick, enums are identical but of different type + Tp: ast.DatabaseOptionType(placementOptions.Tp), + Value: placementOptions.StrValue, + UintValue: placementOptions.UintValue, + } + } + case 398: + { + tiflashReplicaSpec := &ast.TiFlashReplicaSpec{ + Count: yyS[yypt-1].item.(uint64), + Labels: yyS[yypt-0].item.([]string), + } + parser.yyVAL.item = &ast.DatabaseOption{ + Tp: ast.DatabaseSetTiFlashReplica, + TiFlashReplica: tiflashReplicaSpec, + } + } + case 399: + { + parser.yyVAL.item = []*ast.DatabaseOption{} + } + case 401: + { + parser.yyVAL.item = []*ast.DatabaseOption{yyS[yypt-0].item.(*ast.DatabaseOption)} + } + case 402: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.DatabaseOption), yyS[yypt-0].item.(*ast.DatabaseOption)) + } + case 403: + { + stmt := yyS[yypt-6].item.(*ast.CreateTableStmt) + stmt.Table = yyS[yypt-7].item.(*ast.TableName) + stmt.IfNotExists = yyS[yypt-8].item.(bool) + stmt.TemporaryKeyword = yyS[yypt-10].item.(ast.TemporaryKeyword) + stmt.Options = yyS[yypt-5].item.([]*ast.TableOption) + if yyS[yypt-4].item != nil { + stmt.Partition = yyS[yypt-4].item.(*ast.PartitionOptions) + } + stmt.OnDuplicate = yyS[yypt-3].item.(ast.OnDuplicateKeyHandlingType) + stmt.Select = yyS[yypt-1].item.(*ast.CreateTableStmt).Select + if (yyS[yypt-0].item != nil && stmt.TemporaryKeyword != ast.TemporaryGlobal) || (stmt.TemporaryKeyword == ast.TemporaryGlobal && yyS[yypt-0].item == nil) { + yylex.AppendError(yylex.Errorf("GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together")) + } else { + if stmt.TemporaryKeyword == ast.TemporaryGlobal { + stmt.OnCommitDelete = yyS[yypt-0].item.(bool) + } + } + parser.yyVAL.statement = stmt + } + case 404: + { + tmp := &ast.CreateTableStmt{ + Table: yyS[yypt-2].item.(*ast.TableName), + ReferTable: yyS[yypt-1].item.(*ast.TableName), + IfNotExists: yyS[yypt-3].item.(bool), + TemporaryKeyword: yyS[yypt-5].item.(ast.TemporaryKeyword), + } + if (yyS[yypt-0].item != nil && tmp.TemporaryKeyword != ast.TemporaryGlobal) || (tmp.TemporaryKeyword == ast.TemporaryGlobal && yyS[yypt-0].item == nil) { + yylex.AppendError(yylex.Errorf("GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together")) + } else { + if tmp.TemporaryKeyword == ast.TemporaryGlobal { + tmp.OnCommitDelete = yyS[yypt-0].item.(bool) + } + } + parser.yyVAL.statement = tmp + } + case 405: + { + parser.yyVAL.item = nil + } + case 406: + { + parser.yyVAL.item = true + } + case 407: + { + parser.yyVAL.item = false + } + case 410: + { + parser.yyVAL.item = nil + } + case 411: + { + method := yyS[yypt-3].item.(*ast.PartitionMethod) + method.Num = yyS[yypt-2].item.(uint64) + sub, _ := yyS[yypt-1].item.(*ast.PartitionMethod) + defs, _ := yyS[yypt-0].item.([]*ast.PartitionDefinition) + opt := &ast.PartitionOptions{ + PartitionMethod: *method, + Sub: sub, + Definitions: defs, + } + if err := opt.Validate(); err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.item = opt + } + case 412: + { + keyAlgorithm, _ := yyS[yypt-3].item.(*ast.PartitionKeyAlgorithm) + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeKey, + Linear: len(yyS[yypt-5].ident) != 0, + ColumnNames: yyS[yypt-1].item.([]*ast.ColumnName), + KeyAlgorithm: keyAlgorithm, + } + } + case 413: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeHash, + Linear: len(yyS[yypt-4].ident) != 0, + Expr: yyS[yypt-1].expr.(ast.ExprNode), + } + } + case 414: + { + parser.yyVAL.item = nil + } + case 415: + { + tp := getUint64FromNUM(yyS[yypt-0].item) + if tp != 1 && tp != 2 { + yylex.AppendError(ErrSyntax) + return 1 + } + parser.yyVAL.item = &ast.PartitionKeyAlgorithm{ + Type: tp, + } + } + case 417: + { + partitionInterval, _ := yyS[yypt-0].item.(*ast.PartitionInterval) + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeRange, + Expr: yyS[yypt-2].expr.(ast.ExprNode), + Interval: partitionInterval, + } + } + case 418: + { + partitionInterval, _ := yyS[yypt-0].item.(*ast.PartitionInterval) + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeRange, + ColumnNames: yyS[yypt-2].item.([]*ast.ColumnName), + Interval: partitionInterval, + } + } + case 419: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeList, + Expr: yyS[yypt-1].expr.(ast.ExprNode), + } + } + case 420: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeList, + ColumnNames: yyS[yypt-1].item.([]*ast.ColumnName), + } + } + case 421: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeSystemTime, + Expr: yyS[yypt-1].expr.(ast.ExprNode), + Unit: yyS[yypt-0].item.(ast.TimeUnitType), + } + } + case 422: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeSystemTime, + Limit: yyS[yypt-0].item.(uint64), + } + } + case 423: + { + parser.yyVAL.item = &ast.PartitionMethod{ + Tp: model.PartitionTypeSystemTime, + } + } + case 424: + { + parser.yyVAL.item = nil + } + case 425: + { + partitionInterval := &ast.PartitionInterval{ + IntervalExpr: yyS[yypt-4].item.(ast.PartitionIntervalExpr), + FirstRangeEnd: yyS[yypt-2].item.(ast.PartitionInterval).FirstRangeEnd, + LastRangeEnd: yyS[yypt-2].item.(ast.PartitionInterval).LastRangeEnd, + NullPart: yyS[yypt-1].item.(bool), + MaxValPart: yyS[yypt-0].item.(bool), + } + startOffset := parser.yyVAL.offset + endOffset := parser.yylval.offset + partitionInterval.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + // Needed for replacing syntactic sugar with generated partitioning definition string + partitionInterval.SetOriginTextPosition(startOffset) + parser.yyVAL.item = partitionInterval + } + case 426: + { + parser.yyVAL.item = ast.PartitionIntervalExpr{Expr: yyS[yypt-0].expr, TimeUnit: ast.TimeUnitInvalid} + } + case 427: + { + parser.yyVAL.item = ast.PartitionIntervalExpr{Expr: yyS[yypt-1].expr, TimeUnit: yyS[yypt-0].item.(ast.TimeUnitType)} + } + case 428: + { + parser.yyVAL.item = false + } + case 429: + { + parser.yyVAL.item = true + } + case 430: + { + parser.yyVAL.item = false + } + case 431: + { + parser.yyVAL.item = true + } + case 432: + { + parser.yyVAL.item = ast.PartitionInterval{} // First/LastRangeEnd defaults to nil + } + case 433: + { + first := yyS[yypt-8].expr.(ast.ExprNode) + last := yyS[yypt-1].expr.(ast.ExprNode) + parser.yyVAL.item = ast.PartitionInterval{ + FirstRangeEnd: &first, + LastRangeEnd: &last, + } + } + case 434: + { + parser.yyVAL.ident = "" + } + case 436: + { + parser.yyVAL.item = nil + } + case 437: + { + method := yyS[yypt-1].item.(*ast.PartitionMethod) + method.Num = yyS[yypt-0].item.(uint64) + parser.yyVAL.item = method + } + case 438: + { + parser.yyVAL.item = uint64(0) + } + case 439: + { + res := yyS[yypt-0].item.(uint64) + if res == 0 { + yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("subpartitions")) + return 1 + } + parser.yyVAL.item = res + } + case 440: + { + parser.yyVAL.item = uint64(0) + } + case 441: + { + res := yyS[yypt-0].item.(uint64) + if res == 0 { + yylex.AppendError(ast.ErrNoParts.GenWithStackByArgs("partitions")) + return 1 + } + parser.yyVAL.item = res + } + case 442: + { + parser.yyVAL.item = nil + } + case 443: + { + parser.yyVAL.item = yyS[yypt-1].item.([]*ast.PartitionDefinition) + } + case 444: + { + parser.yyVAL.item = []*ast.PartitionDefinition{yyS[yypt-0].item.(*ast.PartitionDefinition)} + } + case 445: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.PartitionDefinition), yyS[yypt-0].item.(*ast.PartitionDefinition)) + } + case 446: + { + parser.yyVAL.item = &ast.PartitionDefinition{ + Name: model.NewCIStr(yyS[yypt-3].ident), + Clause: yyS[yypt-2].item.(ast.PartitionDefinitionClause), + Options: yyS[yypt-1].item.([]*ast.TableOption), + Sub: yyS[yypt-0].item.([]*ast.SubPartitionDefinition), + } + } + case 447: + { + parser.yyVAL.item = make([]*ast.SubPartitionDefinition, 0) + } + case 448: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 449: + { + parser.yyVAL.item = []*ast.SubPartitionDefinition{yyS[yypt-0].item.(*ast.SubPartitionDefinition)} + } + case 450: + { + list := yyS[yypt-2].item.([]*ast.SubPartitionDefinition) + parser.yyVAL.item = append(list, yyS[yypt-0].item.(*ast.SubPartitionDefinition)) + } + case 451: + { + parser.yyVAL.item = &ast.SubPartitionDefinition{ + Name: model.NewCIStr(yyS[yypt-1].ident), + Options: yyS[yypt-0].item.([]*ast.TableOption), + } + } + case 452: + { + parser.yyVAL.item = make([]*ast.TableOption, 0) + } + case 453: + { + list := yyS[yypt-1].item.([]*ast.TableOption) + parser.yyVAL.item = append(list, yyS[yypt-0].item.(*ast.TableOption)) + } + case 454: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionComment, StrValue: yyS[yypt-0].ident} + } + case 455: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: yyS[yypt-0].ident} + } + case 456: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionEngine, StrValue: yyS[yypt-0].ident} + } + case 457: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionInsertMethod, StrValue: yyS[yypt-0].ident} + } + case 458: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionDataDirectory, StrValue: yyS[yypt-0].ident} + } + case 459: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionIndexDirectory, StrValue: yyS[yypt-0].ident} + } + case 460: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionMaxRows, UintValue: yyS[yypt-0].item.(uint64)} + } + case 461: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionMinRows, UintValue: yyS[yypt-0].item.(uint64)} + } + case 462: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionTablespace, StrValue: yyS[yypt-0].ident} + } + case 463: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionNodegroup, UintValue: yyS[yypt-0].item.(uint64)} + } + case 464: + { + placementOptions := yyS[yypt-0].item.(*ast.PlacementOption) + parser.yyVAL.item = &ast.TableOption{ + // offset trick, enums are identical but of different type + Tp: ast.TableOptionType(placementOptions.Tp), + StrValue: placementOptions.StrValue, + UintValue: placementOptions.UintValue, + } + } + case 465: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseNone{} + } + case 466: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseLessThan{ + Exprs: []ast.ExprNode{&ast.MaxValueExpr{}}, + } + } + case 467: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseLessThan{ + Exprs: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 468: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseIn{ + Values: [][]ast.ExprNode{{&ast.DefaultExpr{}}}, + } + } + case 469: + { + exprs := yyS[yypt-1].item.([]ast.ExprNode) + values := make([][]ast.ExprNode, 0, len(exprs)) + for _, expr := range exprs { + if row, ok := expr.(*ast.RowExpr); ok { + values = append(values, row.Values) + } else { + values = append(values, []ast.ExprNode{expr}) + } + } + parser.yyVAL.item = &ast.PartitionDefinitionClauseIn{Values: values} + } + case 470: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseHistory{Current: false} + } + case 471: + { + parser.yyVAL.item = &ast.PartitionDefinitionClauseHistory{Current: true} + } + case 472: + { + parser.yyVAL.item = ast.OnDuplicateKeyHandlingError + } + case 473: + { + parser.yyVAL.item = ast.OnDuplicateKeyHandlingIgnore + } + case 474: + { + parser.yyVAL.item = ast.OnDuplicateKeyHandlingReplace + } + case 477: + { + parser.yyVAL.item = &ast.CreateTableStmt{} + } + case 478: + { + parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 479: + { + parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 480: + { + parser.yyVAL.item = &ast.CreateTableStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 481: + { + var sel ast.ResultSetNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.item = &ast.CreateTableStmt{Select: sel} + } + case 485: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 486: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 487: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 488: + { + startOffset := parser.startOffset(&yyS[yypt-1]) + selStmt := yyS[yypt-1].statement.(ast.StmtNode) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + x := &ast.CreateViewStmt{ + OrReplace: yyS[yypt-9].item.(bool), + ViewName: yyS[yypt-4].item.(*ast.TableName), + Select: selStmt, + Algorithm: yyS[yypt-8].item.(model.ViewAlgorithm), + Definer: yyS[yypt-7].item.(*auth.UserIdentity), + Security: yyS[yypt-6].item.(model.ViewSecurity), + } + if yyS[yypt-3].item != nil { + x.Cols = yyS[yypt-3].item.([]model.CIStr) + } + if yyS[yypt-0].item != nil { + x.CheckOption = yyS[yypt-0].item.(model.ViewCheckOption) + endOffset := parser.startOffset(&yyS[yypt]) + selStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) + } else { + x.CheckOption = model.CheckOptionCascaded + } + parser.yyVAL.statement = x + } + case 489: + { + parser.yyVAL.item = false + } + case 490: + { + parser.yyVAL.item = true + } + case 491: + { + parser.yyVAL.item = model.AlgorithmUndefined + } + case 492: + { + parser.yyVAL.item = model.AlgorithmUndefined + } + case 493: + { + parser.yyVAL.item = model.AlgorithmMerge + } + case 494: + { + parser.yyVAL.item = model.AlgorithmTemptable + } + case 495: + { + parser.yyVAL.item = &auth.UserIdentity{CurrentUser: true} + } + case 496: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 497: + { + parser.yyVAL.item = model.SecurityDefiner + } + case 498: + { + parser.yyVAL.item = model.SecurityDefiner + } + case 499: + { + parser.yyVAL.item = model.SecurityInvoker + } + case 501: + { + parser.yyVAL.item = nil + } + case 502: + { + parser.yyVAL.item = yyS[yypt-1].item.([]model.CIStr) + } + case 503: + { + parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} + } + case 504: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) + } + case 505: + { + parser.yyVAL.item = nil + } + case 506: + { + parser.yyVAL.item = model.CheckOptionCascaded + } + case 507: + { + parser.yyVAL.item = model.CheckOptionLocal + } + case 508: + { + parser.yyVAL.statement = &ast.DoStmt{ + Exprs: yyS[yypt-0].item.([]ast.ExprNode), + } + } + case 509: + { + // Single Table + tn := yyS[yypt-6].item.(*ast.TableName) + tn.IndexHints = yyS[yypt-3].item.([]*ast.IndexHint) + tn.PartitionNames = yyS[yypt-5].item.([]model.CIStr) + join := &ast.Join{Left: &ast.TableSource{Source: tn, AsName: yyS[yypt-4].item.(model.CIStr)}, Right: nil} + x := &ast.DeleteStmt{ + TableRefs: &ast.TableRefsClause{TableRefs: join}, + Priority: yyS[yypt-10].item.(mysql.PriorityEnum), + Quick: yyS[yypt-9].item.(bool), + IgnoreErr: yyS[yypt-8].item.(bool), + } + if yyS[yypt-11].item != nil { + x.TableHints = yyS[yypt-11].item.([]*ast.TableOptimizerHint) + } + if yyS[yypt-2].item != nil { + x.Where = yyS[yypt-2].item.(ast.ExprNode) + } + if yyS[yypt-1].item != nil { + x.Order = yyS[yypt-1].item.(*ast.OrderByClause) + } + if yyS[yypt-0].item != nil { + x.Limit = yyS[yypt-0].item.(*ast.Limit) + } + + parser.yyVAL.statement = x + } + case 510: + { + // Multiple Table + x := &ast.DeleteStmt{ + Priority: yyS[yypt-6].item.(mysql.PriorityEnum), + Quick: yyS[yypt-5].item.(bool), + IgnoreErr: yyS[yypt-4].item.(bool), + IsMultiTable: true, + BeforeFrom: true, + Tables: &ast.DeleteTableList{Tables: yyS[yypt-3].item.([]*ast.TableName)}, + TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-1].item.(*ast.Join)}, + } + if yyS[yypt-7].item != nil { + x.TableHints = yyS[yypt-7].item.([]*ast.TableOptimizerHint) + } + if yyS[yypt-0].item != nil { + x.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = x + } + case 511: + { + // Multiple Table + x := &ast.DeleteStmt{ + Priority: yyS[yypt-7].item.(mysql.PriorityEnum), + Quick: yyS[yypt-6].item.(bool), + IgnoreErr: yyS[yypt-5].item.(bool), + IsMultiTable: true, + Tables: &ast.DeleteTableList{Tables: yyS[yypt-3].item.([]*ast.TableName)}, + TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-1].item.(*ast.Join)}, + } + if yyS[yypt-8].item != nil { + x.TableHints = yyS[yypt-8].item.([]*ast.TableOptimizerHint) + } + if yyS[yypt-0].item != nil { + x.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = x + } + case 514: + { + d := yyS[yypt-0].statement.(*ast.DeleteStmt) + d.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = d + } + case 515: + { + d := yyS[yypt-0].statement.(*ast.DeleteStmt) + d.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = d + } + case 517: + { + parser.yyVAL.statement = &ast.DropDatabaseStmt{IfExists: yyS[yypt-1].item.(bool), Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 518: + { + var indexLockAndAlgorithm *ast.IndexLockAndAlgorithm + if yyS[yypt-0].item != nil { + indexLockAndAlgorithm = yyS[yypt-0].item.(*ast.IndexLockAndAlgorithm) + if indexLockAndAlgorithm.LockTp == ast.LockTypeDefault && indexLockAndAlgorithm.AlgorithmTp == ast.AlgorithmTypeDefault { + indexLockAndAlgorithm = nil + } + } + parser.yyVAL.statement = &ast.DropIndexStmt{IfExists: yyS[yypt-4].item.(bool), IndexName: yyS[yypt-3].ident, Table: yyS[yypt-1].item.(*ast.TableName), LockAlg: indexLockAndAlgorithm} + } + case 519: + { + parser.yyVAL.statement = &ast.DropIndexStmt{IfExists: yyS[yypt-3].item.(bool), IndexName: yyS[yypt-2].ident, Table: yyS[yypt-0].item.(*ast.TableName), IsHypo: true} + } + case 520: + { + parser.yyVAL.statement = &ast.DropTableStmt{IfExists: yyS[yypt-2].item.(bool), Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: false, TemporaryKeyword: yyS[yypt-4].item.(ast.TemporaryKeyword)} + } + case 521: + { + parser.yyVAL.item = ast.TemporaryNone + } + case 522: + { + parser.yyVAL.item = ast.TemporaryLocal + } + case 523: + { + parser.yyVAL.item = ast.TemporaryGlobal + } + case 524: + { + parser.yyVAL.statement = &ast.DropTableStmt{Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: true} + } + case 525: + { + parser.yyVAL.statement = &ast.DropTableStmt{IfExists: true, Tables: yyS[yypt-1].item.([]*ast.TableName), IsView: true} + } + case 526: + { + parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: false, IfExists: false, UserList: yyS[yypt-0].item.([]*auth.UserIdentity)} + } + case 527: + { + parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: false, IfExists: true, UserList: yyS[yypt-0].item.([]*auth.UserIdentity)} + } + case 528: + { + tmp := make([]*auth.UserIdentity, 0, 10) + roleList := yyS[yypt-0].item.([]*auth.RoleIdentity) + for _, r := range roleList { + tmp = append(tmp, &auth.UserIdentity{Username: r.Username, Hostname: r.Hostname}) + } + parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: true, IfExists: false, UserList: tmp} + } + case 529: + { + tmp := make([]*auth.UserIdentity, 0, 10) + roleList := yyS[yypt-0].item.([]*auth.RoleIdentity) + for _, r := range roleList { + tmp = append(tmp, &auth.UserIdentity{Username: r.Username, Hostname: r.Hostname}) + } + parser.yyVAL.statement = &ast.DropUserStmt{IsDropRole: true, IfExists: true, UserList: tmp} + } + case 530: + { + parser.yyVAL.statement = &ast.DropStatsStmt{Tables: yyS[yypt-0].item.([]*ast.TableName)} + } + case 531: + { + yylex.AppendError(ErrWarnDeprecatedSyntaxNoReplacement.FastGenByArgs("'DROP STATS ... PARTITION ...'", "")) + parser.lastErrorAsWarn() + parser.yyVAL.statement = &ast.DropStatsStmt{ + Tables: []*ast.TableName{yyS[yypt-2].item.(*ast.TableName)}, + PartitionNames: yyS[yypt-0].item.([]model.CIStr), + } + } + case 532: + { + yylex.AppendError(ErrWarnDeprecatedSyntax.FastGenByArgs("DROP STATS ... GLOBAL", "DROP STATS ...")) + parser.lastErrorAsWarn() + parser.yyVAL.statement = &ast.DropStatsStmt{ + Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, + IsGlobalStats: true, + } + } + case 540: + { + parser.yyVAL.statement = nil + } + case 541: + { + parser.yyVAL.statement = &ast.TraceStmt{ + Stmt: yyS[yypt-0].statement, + Format: "row", + TracePlan: false, + } + startOffset := parser.startOffset(&yyS[yypt]) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) + } + case 542: + { + parser.yyVAL.statement = &ast.TraceStmt{ + Stmt: yyS[yypt-0].statement, + Format: yyS[yypt-1].ident, + TracePlan: false, + } + startOffset := parser.startOffset(&yyS[yypt]) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) + } + case 543: + { + parser.yyVAL.statement = &ast.TraceStmt{ + Stmt: yyS[yypt-0].statement, + TracePlan: true, + } + startOffset := parser.startOffset(&yyS[yypt]) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) + } + case 544: + { + parser.yyVAL.statement = &ast.TraceStmt{ + Stmt: yyS[yypt-0].statement, + TracePlan: true, + TracePlanTarget: yyS[yypt-1].ident, + } + startOffset := parser.startOffset(&yyS[yypt]) + yyS[yypt-0].statement.SetText(parser.lexer.client, string(parser.src[startOffset:])) + } + case 548: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: &ast.ShowStmt{ + Tp: ast.ShowColumns, + Table: yyS[yypt-0].item.(*ast.TableName), + }, + } + } + case 549: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: &ast.ShowStmt{ + Tp: ast.ShowColumns, + Table: yyS[yypt-1].item.(*ast.TableName), + Column: yyS[yypt-0].item.(*ast.ColumnName), + }, + } + } + case 550: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: "row", + } + } + case 551: + { + parser.yyVAL.statement = &ast.ExplainForStmt{ + Format: "row", + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 552: + { + parser.yyVAL.statement = &ast.ExplainForStmt{ + Format: yyS[yypt-3].ident, + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 553: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: yyS[yypt-1].ident, + } + } + case 554: + { + parser.yyVAL.statement = &ast.ExplainForStmt{ + Format: yyS[yypt-3].ident, + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 555: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: yyS[yypt-1].ident, + } + } + case 556: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: "row", + Analyze: true, + } + } + case 557: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: yyS[yypt-1].ident, + Analyze: true, + } + } + case 558: + { + parser.yyVAL.statement = &ast.ExplainStmt{ + Stmt: yyS[yypt-0].statement, + Format: yyS[yypt-1].ident, + Analyze: true, + } + } + case 567: + { + parser.yyVAL.statement = &ast.SavepointStmt{Name: yyS[yypt-0].ident} + } + case 568: + { + parser.yyVAL.statement = &ast.ReleaseSavepointStmt{Name: yyS[yypt-0].ident} + } + case 569: + { + stmt := yyS[yypt-3].item.(*ast.BRIEStmt) + stmt.Kind = ast.BRIEKindBackup + stmt.Storage = yyS[yypt-1].ident + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 570: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamStart + stmt.Storage = yyS[yypt-1].ident + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 571: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamStop + parser.yyVAL.statement = stmt + } + case 572: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamPause + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 573: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamResume + parser.yyVAL.statement = stmt + } + case 574: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamPurge + stmt.Storage = yyS[yypt-1].ident + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 575: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamStatus + parser.yyVAL.statement = stmt + } + case 576: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindStreamMetaData + stmt.Storage = yyS[yypt-0].ident + parser.yyVAL.statement = stmt + } + case 577: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindShowJob + stmt.JobID = yyS[yypt-0].item.(int64) + parser.yyVAL.statement = stmt + } + case 578: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindShowQuery + stmt.JobID = yyS[yypt-0].item.(int64) + parser.yyVAL.statement = stmt + } + case 579: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindCancelJob + stmt.JobID = yyS[yypt-0].item.(int64) + parser.yyVAL.statement = stmt + } + case 580: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindShowBackupMeta + stmt.Storage = yyS[yypt-0].ident + parser.yyVAL.statement = stmt + } + case 581: + { + stmt := yyS[yypt-3].item.(*ast.BRIEStmt) + stmt.Kind = ast.BRIEKindRestore + stmt.Storage = yyS[yypt-1].ident + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 582: + { + stmt := &ast.BRIEStmt{} + stmt.Kind = ast.BRIEKindRestorePIT + stmt.Storage = yyS[yypt-1].ident + stmt.Options = yyS[yypt-0].item.([]*ast.BRIEOption) + parser.yyVAL.statement = stmt + } + case 583: + { + parser.yyVAL.item = &ast.BRIEStmt{} + } + case 584: + { + parser.yyVAL.item = &ast.BRIEStmt{Schemas: yyS[yypt-0].item.([]string)} + } + case 585: + { + parser.yyVAL.item = &ast.BRIEStmt{Tables: yyS[yypt-0].item.([]*ast.TableName)} + } + case 586: + { + parser.yyVAL.item = []string{yyS[yypt-0].ident} + } + case 587: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) + } + case 588: + { + parser.yyVAL.item = []*ast.BRIEOption{} + } + case 589: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.BRIEOption), yyS[yypt-0].item.(*ast.BRIEOption)) + } + case 590: + { + parser.yyVAL.item = ast.BRIEOptionConcurrency + } + case 591: + { + parser.yyVAL.item = ast.BRIEOptionResume + } + case 592: + { + parser.yyVAL.item = ast.BRIEOptionSendCreds + } + case 593: + { + parser.yyVAL.item = ast.BRIEOptionOnline + } + case 594: + { + parser.yyVAL.item = ast.BRIEOptionCheckpoint + } + case 595: + { + parser.yyVAL.item = ast.BRIEOptionSkipSchemaFiles + } + case 596: + { + parser.yyVAL.item = ast.BRIEOptionStrictFormat + } + case 597: + { + parser.yyVAL.item = ast.BRIEOptionCSVNotNull + } + case 598: + { + parser.yyVAL.item = ast.BRIEOptionCSVBackslashEscape + } + case 599: + { + parser.yyVAL.item = ast.BRIEOptionCSVTrimLastSeparators + } + case 600: + { + parser.yyVAL.item = ast.BRIEOptionTiKVImporter + } + case 601: + { + parser.yyVAL.item = ast.BRIEOptionCSVSeparator + } + case 602: + { + parser.yyVAL.item = ast.BRIEOptionCSVDelimiter + } + case 603: + { + parser.yyVAL.item = ast.BRIEOptionCSVNull + } + case 604: + { + parser.yyVAL.item = ast.BRIEOptionBackend + } + case 605: + { + parser.yyVAL.item = ast.BRIEOptionOnDuplicate + } + case 606: + { + parser.yyVAL.item = ast.BRIEOptionOnDuplicate + } + case 607: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: yyS[yypt-2].item.(ast.BRIEOptionType), + UintValue: yyS[yypt-0].item.(uint64), + } + } + case 608: + { + value := uint64(0) + if yyS[yypt-0].item.(bool) { + value = 1 + } + parser.yyVAL.item = &ast.BRIEOption{ + Tp: yyS[yypt-2].item.(ast.BRIEOptionType), + UintValue: value, + } + } + case 609: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: yyS[yypt-2].item.(ast.BRIEOptionType), + StrValue: yyS[yypt-0].ident, + } + } + case 610: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: yyS[yypt-2].item.(ast.BRIEOptionType), + StrValue: strings.ToLower(yyS[yypt-0].ident), + } + } + case 611: + { + unit, err := yyS[yypt-1].item.(ast.TimeUnitType).Duration() + if err != nil { + yylex.AppendError(err) + return 1 + } + // TODO: check overflow? + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionBackupTimeAgo, + UintValue: yyS[yypt-2].item.(uint64) * uint64(unit), + } + } + case 612: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionBackupTS, + StrValue: yyS[yypt-0].ident, + } + } + case 613: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionBackupTSO, + UintValue: yyS[yypt-0].item.(uint64), + } + } + case 614: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionLastBackupTS, + StrValue: yyS[yypt-0].ident, + } + } + case 615: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionLastBackupTSO, + UintValue: yyS[yypt-0].item.(uint64), + } + } + case 616: + { + // TODO: check overflow? + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionRateLimit, + UintValue: yyS[yypt-3].item.(uint64) * 1048576, + } + } + case 617: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionCSVHeader, + UintValue: ast.BRIECSVHeaderIsColumns, + } + } + case 618: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionCSVHeader, + UintValue: yyS[yypt-0].item.(uint64), + } + } + case 619: + { + value := uint64(0) + if yyS[yypt-0].item.(bool) { + value = 1 + } + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionChecksum, + UintValue: value, + } + } + case 620: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionChecksum, + UintValue: uint64(yyS[yypt-0].item.(ast.BRIEOptionLevel)), + } + } + case 621: + { + value := uint64(0) + if yyS[yypt-0].item.(bool) { + value = 1 + } + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionAnalyze, + UintValue: value, + } + } + case 622: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionAnalyze, + UintValue: uint64(yyS[yypt-0].item.(ast.BRIEOptionLevel)), + } + } + case 623: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionFullBackupStorage, + StrValue: yyS[yypt-0].ident, + } + } + case 624: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionRestoredTS, + StrValue: yyS[yypt-0].ident, + } + } + case 625: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionStartTS, + StrValue: yyS[yypt-0].ident, + } + } + case 626: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionUntilTS, + StrValue: yyS[yypt-0].ident, + } + } + case 627: + { + parser.yyVAL.item = &ast.BRIEOption{ + Tp: ast.BRIEOptionGCTTL, + StrValue: yyS[yypt-0].ident, + } + } + case 628: + { + parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) + } + case 629: + { + v, rangeErrMsg := getInt64FromNUM(yyS[yypt-0].item) + if len(rangeErrMsg) != 0 { + yylex.AppendError(yylex.Errorf(rangeErrMsg)) + return 1 + } + parser.yyVAL.item = v + } + case 631: + { + parser.yyVAL.item = yyS[yypt-0].item.(int64) != 0 + } + case 632: + { + parser.yyVAL.item = false + } + case 633: + { + parser.yyVAL.item = true + } + case 634: + { + parser.yyVAL.item = ast.BRIEOptionLevelOff + } + case 635: + { + parser.yyVAL.item = ast.BRIEOptionLevelOptional + } + case 636: + { + parser.yyVAL.item = ast.BRIEOptionLevelRequired + } + case 637: + { + parser.yyVAL.statement = &ast.LoadDataActionStmt{ + Tp: ast.LoadDataPause, + JobID: yyS[yypt-0].item.(int64), + } + } + case 638: + { + parser.yyVAL.statement = &ast.LoadDataActionStmt{ + Tp: ast.LoadDataResume, + JobID: yyS[yypt-0].item.(int64), + } + } + case 639: + { + parser.yyVAL.statement = &ast.ImportIntoActionStmt{ + Tp: ast.ImportIntoCancel, + JobID: yyS[yypt-0].item.(int64), + } + } + case 640: + { + parser.yyVAL.statement = &ast.LoadDataActionStmt{ + Tp: ast.LoadDataDrop, + JobID: yyS[yypt-0].item.(int64), + } + } + case 641: + { + v := yyS[yypt-2].ident + v = strings.TrimPrefix(v, "@") + parser.yyVAL.expr = &ast.VariableExpr{ + Name: v, + IsGlobal: false, + IsSystem: false, + Value: yyS[yypt-0].expr, + } + } + case 642: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicOr, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 643: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicXor, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 644: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LogicAnd, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 645: + { + expr, ok := yyS[yypt-0].expr.(*ast.ExistsSubqueryExpr) + if ok { + expr.Not = !expr.Not + parser.yyVAL.expr = yyS[yypt-0].expr + } else { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not, V: yyS[yypt-0].expr} + } + } + case 646: + { + parser.yyVAL.expr = &ast.MatchAgainst{ + ColumnNames: yyS[yypt-6].item.([]*ast.ColumnName), + Against: yyS[yypt-2].expr, + Modifier: ast.FulltextSearchModifier(yyS[yypt-1].item.(int)), + } + } + case 647: + { + parser.yyVAL.expr = &ast.IsTruthExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), True: int64(1)} + } + case 648: + { + parser.yyVAL.expr = &ast.IsTruthExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), True: int64(0)} + } + case 649: + { + /* https://dev.mysql.com/doc/refman/5.7/en/comparison-operators.html#operator_is */ + parser.yyVAL.expr = &ast.IsNullExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool)} + } + case 651: + { + parser.yyVAL.expr = &ast.DefaultExpr{} + } + case 653: + { + parser.yyVAL.expr = &ast.MaxValueExpr{} + } + case 655: + { + parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode + } + case 656: + { + parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode + } + case 657: + { + parser.yyVAL.item = ast.FulltextSearchModifierNaturalLanguageMode | ast.FulltextSearchModifierWithQueryExpansion + } + case 658: + { + parser.yyVAL.item = ast.FulltextSearchModifierBooleanMode + } + case 659: + { + parser.yyVAL.item = ast.FulltextSearchModifierWithQueryExpansion + } + case 664: + { + parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} + } + case 665: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) + } + case 666: + { + parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} + } + case 667: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) + } + case 668: + { + parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} + } + case 669: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) + } + case 670: + { + parser.yyVAL.item = []ast.ExprNode{} + } + case 672: + { + parser.yyVAL.item = []ast.ExprNode{} + } + case 674: + { + expr := ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + parser.yyVAL.item = []ast.ExprNode{expr} + } + case 675: + { + parser.yyVAL.expr = &ast.IsNullExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool)} + } + case 676: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: yyS[yypt-1].item.(opcode.Op), L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 677: + { + sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) + sq.MultiRows = true + parser.yyVAL.expr = &ast.CompareSubqueryExpr{Op: yyS[yypt-2].item.(opcode.Op), L: yyS[yypt-3].expr, R: sq, All: yyS[yypt-1].item.(bool)} + } + case 678: + { + v := yyS[yypt-2].ident + v = strings.TrimPrefix(v, "@") + variable := &ast.VariableExpr{ + Name: v, + IsGlobal: false, + IsSystem: false, + Value: yyS[yypt-0].expr, + } + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: yyS[yypt-3].item.(opcode.Op), L: yyS[yypt-4].expr, R: variable} + } + case 680: + { + parser.yyVAL.item = opcode.GE + } + case 681: + { + parser.yyVAL.item = opcode.GT + } + case 682: + { + parser.yyVAL.item = opcode.LE + } + case 683: + { + parser.yyVAL.item = opcode.LT + } + case 684: + { + parser.yyVAL.item = opcode.NE + } + case 685: + { + parser.yyVAL.item = opcode.NE + } + case 686: + { + parser.yyVAL.item = opcode.EQ + } + case 687: + { + parser.yyVAL.item = opcode.NullEQ + } + case 688: + { + parser.yyVAL.item = true + } + case 689: + { + parser.yyVAL.item = false + } + case 690: + { + parser.yyVAL.item = true + } + case 691: + { + parser.yyVAL.item = false + } + case 692: + { + parser.yyVAL.item = true + } + case 693: + { + parser.yyVAL.item = false + } + case 694: + { + parser.yyVAL.item = true + } + case 695: + { + parser.yyVAL.item = false + } + case 696: + { + parser.yyVAL.item = true + } + case 697: + { + parser.yyVAL.item = false + } + case 698: + { + parser.yyVAL.item = true + } + case 699: + { + parser.yyVAL.item = false + } + case 700: + { + parser.yyVAL.item = false + } + case 701: + { + parser.yyVAL.item = false + } + case 702: + { + parser.yyVAL.item = true + } + case 703: + { + parser.yyVAL.expr = &ast.PatternInExpr{Expr: yyS[yypt-4].expr, Not: !yyS[yypt-3].item.(bool), List: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 704: + { + sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) + sq.MultiRows = true + parser.yyVAL.expr = &ast.PatternInExpr{Expr: yyS[yypt-2].expr, Not: !yyS[yypt-1].item.(bool), Sel: sq} + } + case 705: + { + parser.yyVAL.expr = &ast.BetweenExpr{ + Expr: yyS[yypt-4].expr, + Left: yyS[yypt-2].expr, + Right: yyS[yypt-0].expr, + Not: !yyS[yypt-3].item.(bool), + } + } + case 706: + { + escape := yyS[yypt-0].ident + if len(escape) > 1 { + yylex.AppendError(ErrWrongArguments.GenWithStackByArgs("ESCAPE")) + return 1 + } else if len(escape) == 0 { + escape = "\\" + } + parser.yyVAL.expr = &ast.PatternLikeOrIlikeExpr{ + Expr: yyS[yypt-3].expr, + Pattern: yyS[yypt-1].expr, + Not: !yyS[yypt-2].item.(bool), + Escape: escape[0], + IsLike: true, + } + } + case 707: + { + escape := yyS[yypt-0].ident + if len(escape) > 1 { + yylex.AppendError(ErrWrongArguments.GenWithStackByArgs("ESCAPE")) + return 1 + } else if len(escape) == 0 { + escape = "\\" + } + parser.yyVAL.expr = &ast.PatternLikeOrIlikeExpr{ + Expr: yyS[yypt-3].expr, + Pattern: yyS[yypt-1].expr, + Not: !yyS[yypt-2].item.(bool), + Escape: escape[0], + IsLike: false, + } + } + case 708: + { + parser.yyVAL.expr = &ast.PatternRegexpExpr{Expr: yyS[yypt-2].expr, Pattern: yyS[yypt-0].expr, Not: !yyS[yypt-1].item.(bool)} + } + case 709: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONMemberOf), Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-1].expr}} + } + case 713: + { + parser.yyVAL.ident = "\\" + } + case 714: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 715: + { + parser.yyVAL.item = &ast.SelectField{WildCard: &ast.WildCardField{}} + } + case 716: + { + wildCard := &ast.WildCardField{Table: model.NewCIStr(yyS[yypt-2].ident)} + parser.yyVAL.item = &ast.SelectField{WildCard: wildCard} + } + case 717: + { + wildCard := &ast.WildCardField{Schema: model.NewCIStr(yyS[yypt-4].ident), Table: model.NewCIStr(yyS[yypt-2].ident)} + parser.yyVAL.item = &ast.SelectField{WildCard: wildCard} + } + case 718: + { + expr := yyS[yypt-1].expr + asName := yyS[yypt-0].ident + parser.yyVAL.item = &ast.SelectField{Expr: expr, AsName: model.NewCIStr(asName)} + } + case 719: + { + parser.yyVAL.ident = "" + } + case 722: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 724: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 725: + { + field := yyS[yypt-0].item.(*ast.SelectField) + field.Offset = parser.startOffset(&yyS[yypt]) + if field.Expr != nil { + endOffset := parser.yylval.offset + field.SetText(parser.lexer.client, strings.TrimSpace(parser.src[field.Offset:endOffset])) + } + parser.yyVAL.item = []*ast.SelectField{field} + } + case 726: + { + fl := yyS[yypt-2].item.([]*ast.SelectField) + field := yyS[yypt-0].item.(*ast.SelectField) + field.Offset = parser.startOffset(&yyS[yypt]) + if field.Expr != nil { + endOffset := parser.yylval.offset + field.SetText(parser.lexer.client, strings.TrimSpace(parser.src[field.Offset:endOffset])) + } + parser.yyVAL.item = append(fl, field) + } + case 727: + { + parser.yyVAL.item = false + } + case 728: + { + parser.yyVAL.item = true + } + case 729: + { + parser.yyVAL.item = &ast.GroupByClause{Items: yyS[yypt-1].item.([]*ast.ByItem), Rollup: yyS[yypt-0].item.(bool)} + } + case 730: + { + parser.yyVAL.item = nil + } + case 731: + { + parser.yyVAL.item = &ast.HavingClause{Expr: yyS[yypt-0].expr} + } + case 732: + { + parser.yyVAL.item = nil + } + case 734: + { + parser.yyVAL.item = &ast.AsOfClause{ + TsExpr: yyS[yypt-0].expr.(ast.ExprNode), + } + } + case 735: + { + parser.yyVAL.item = false + } + case 736: + { + parser.yyVAL.item = true + } + case 737: + { + parser.yyVAL.item = false + } + case 738: + { + parser.yyVAL.item = true + } + case 739: + { + parser.yyVAL.item = false + } + case 740: + { + parser.yyVAL.item = true + } + case 741: + { + parser.yyVAL.item = &ast.NullString{ + String: "", + Empty: false, + } + } + case 742: + { + parser.yyVAL.item = &ast.NullString{ + String: yyS[yypt-0].ident, + Empty: len(yyS[yypt-0].ident) == 0, + } + } + case 743: + { + parser.yyVAL.item = nil + } + case 744: + { + // Merge the options + if yyS[yypt-1].item == nil { + parser.yyVAL.item = yyS[yypt-0].item + } else { + opt1 := yyS[yypt-1].item.(*ast.IndexOption) + opt2 := yyS[yypt-0].item.(*ast.IndexOption) + if len(opt2.Comment) > 0 { + opt1.Comment = opt2.Comment + } else if opt2.Tp != 0 { + opt1.Tp = opt2.Tp + } else if opt2.KeyBlockSize > 0 { + opt1.KeyBlockSize = opt2.KeyBlockSize + } else if len(opt2.ParserName.O) > 0 { + opt1.ParserName = opt2.ParserName + } else if opt2.Visibility != ast.IndexVisibilityDefault { + opt1.Visibility = opt2.Visibility + } else if opt2.PrimaryKeyTp != model.PrimaryKeyTypeDefault { + opt1.PrimaryKeyTp = opt2.PrimaryKeyTp + } + parser.yyVAL.item = opt1 + } + } + case 745: + { + parser.yyVAL.item = &ast.IndexOption{ + KeyBlockSize: yyS[yypt-0].item.(uint64), + } + } + case 746: + { + parser.yyVAL.item = &ast.IndexOption{ + Tp: yyS[yypt-0].item.(model.IndexType), + } + } + case 747: + { + parser.yyVAL.item = &ast.IndexOption{ + ParserName: model.NewCIStr(yyS[yypt-0].ident), + } + yylex.AppendError(yylex.Errorf("The WITH PARASER clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 748: + { + parser.yyVAL.item = &ast.IndexOption{ + Comment: yyS[yypt-0].ident, + } + } + case 749: + { + parser.yyVAL.item = &ast.IndexOption{ + Visibility: yyS[yypt-0].item.(ast.IndexVisibility), + } + } + case 750: + { + parser.yyVAL.item = &ast.IndexOption{ + PrimaryKeyTp: yyS[yypt-0].item.(model.PrimaryKeyType), + } + } + case 751: + { + parser.yyVAL.item = []interface{}{yyS[yypt-0].item, nil} + } + case 752: + { + parser.yyVAL.item = []interface{}{yyS[yypt-2].item, yyS[yypt-0].item} + } + case 753: + { + parser.yyVAL.item = []interface{}{&ast.NullString{String: yyS[yypt-2].ident, Empty: len(yyS[yypt-2].ident) == 0}, yyS[yypt-0].item} + } + case 754: + { + parser.yyVAL.item = nil + } + case 756: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 757: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 758: + { + parser.yyVAL.item = model.IndexTypeBtree + } + case 759: + { + parser.yyVAL.item = model.IndexTypeHash + } + case 760: + { + parser.yyVAL.item = model.IndexTypeRtree + } + case 761: + { + parser.yyVAL.item = model.IndexTypeHypo + } + case 762: + { + parser.yyVAL.item = ast.IndexVisibilityVisible + } + case 763: + { + parser.yyVAL.item = ast.IndexVisibilityInvisible + } + case 1293: + { + parser.yyVAL.statement = &ast.CallStmt{ + Procedure: yyS[yypt-0].expr.(*ast.FuncCallExpr), + } + } + case 1294: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + Tp: ast.FuncCallExprTypeGeneric, + FnName: model.NewCIStr(yyS[yypt-0].ident), + Args: []ast.ExprNode{}, + } + } + case 1295: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + Tp: ast.FuncCallExprTypeGeneric, + Schema: model.NewCIStr(yyS[yypt-2].ident), + FnName: model.NewCIStr(yyS[yypt-0].ident), + Args: []ast.ExprNode{}, + } + } + case 1296: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + Tp: ast.FuncCallExprTypeGeneric, + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 1297: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + Tp: ast.FuncCallExprTypeGeneric, + Schema: model.NewCIStr(yyS[yypt-5].ident), + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 1298: + { + x := yyS[yypt-1].item.(*ast.InsertStmt) + x.Priority = yyS[yypt-6].item.(mysql.PriorityEnum) + x.IgnoreErr = yyS[yypt-5].item.(bool) + // Wraps many layers here so that it can be processed the same way as select statement. + ts := &ast.TableSource{Source: yyS[yypt-3].item.(*ast.TableName)} + x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} + if yyS[yypt-0].item != nil { + x.OnDuplicate = yyS[yypt-0].item.([]*ast.Assignment) + } + if yyS[yypt-7].item != nil { + x.TableHints = yyS[yypt-7].item.([]*ast.TableOptimizerHint) + } + x.PartitionNames = yyS[yypt-2].item.([]model.CIStr) + parser.yyVAL.statement = x + } + case 1301: + { + parser.yyVAL.item = &ast.InsertStmt{ + Columns: yyS[yypt-3].item.([]*ast.ColumnName), + Lists: yyS[yypt-0].item.([][]ast.ExprNode), + } + } + case 1302: + { + parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1303: + { + parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1304: + { + parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1305: + { + var sel ast.ResultSetNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.item = &ast.InsertStmt{Columns: yyS[yypt-2].item.([]*ast.ColumnName), Select: sel} + } + case 1306: + { + parser.yyVAL.item = &ast.InsertStmt{Lists: yyS[yypt-0].item.([][]ast.ExprNode)} + } + case 1307: + { + parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1308: + { + parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1309: + { + parser.yyVAL.item = &ast.InsertStmt{Select: yyS[yypt-0].statement.(ast.ResultSetNode)} + } + case 1310: + { + var sel ast.ResultSetNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.item = &ast.InsertStmt{Select: sel} + } + case 1311: + { + parser.yyVAL.item = yyS[yypt-0].item.(*ast.InsertStmt) + } + case 1314: + { + parser.yyVAL.item = [][]ast.ExprNode{yyS[yypt-0].item.([]ast.ExprNode)} + } + case 1315: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([][]ast.ExprNode), yyS[yypt-0].item.([]ast.ExprNode)) + } + case 1316: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 1317: + { + parser.yyVAL.item = []ast.ExprNode{} + } + case 1319: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) + } + case 1320: + { + parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} + } + case 1322: + { + parser.yyVAL.expr = &ast.DefaultExpr{} + } + case 1323: + { + parser.yyVAL.item = &ast.InsertStmt{ + Columns: []*ast.ColumnName{yyS[yypt-2].item.(*ast.ColumnName)}, + Lists: [][]ast.ExprNode{{yyS[yypt-0].expr.(ast.ExprNode)}}, + Setlist: true, + } + } + case 1324: + { + ins := yyS[yypt-4].item.(*ast.InsertStmt) + ins.Columns = append(ins.Columns, yyS[yypt-2].item.(*ast.ColumnName)) + ins.Lists[0] = append(ins.Lists[0], yyS[yypt-0].expr.(ast.ExprNode)) + parser.yyVAL.item = ins + } + case 1325: + { + parser.yyVAL.item = nil + } + case 1326: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 1327: + { + x := yyS[yypt-0].item.(*ast.InsertStmt) + x.IsReplace = true + x.Priority = yyS[yypt-4].item.(mysql.PriorityEnum) + ts := &ast.TableSource{Source: yyS[yypt-2].item.(*ast.TableName)} + x.Table = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} + x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) + parser.yyVAL.statement = x + } + case 1328: + { + parser.yyVAL.expr = ast.NewValueExpr(false, parser.charset, parser.collation) + } + case 1329: + { + parser.yyVAL.expr = ast.NewValueExpr(nil, parser.charset, parser.collation) + } + case 1330: + { + parser.yyVAL.expr = ast.NewValueExpr(true, parser.charset, parser.collation) + } + case 1331: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1332: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1333: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1335: + { + // See https://dev.mysql.com/doc/refman/5.7/en/charset-literal.html + co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) + if err != nil { + yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) + return 1 + } + expr := ast.NewValueExpr(yyS[yypt-0].ident, yyS[yypt-1].ident, co) + tp := expr.GetType() + tp.SetCharset(yyS[yypt-1].ident) + tp.SetCollate(co) + tp.AddFlag(mysql.UnderScoreCharsetFlag) + if tp.GetCollate() == charset.CollationBin { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.expr = expr + } + case 1336: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1337: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1338: + { + co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) + if err != nil { + yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) + return 1 + } + expr := ast.NewValueExpr(yyS[yypt-0].item, yyS[yypt-1].ident, co) + tp := expr.GetType() + tp.SetCharset(yyS[yypt-1].ident) + tp.SetCollate(co) + tp.AddFlag(mysql.UnderScoreCharsetFlag) + if tp.GetCollate() == charset.CollationBin { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.expr = expr + } + case 1339: + { + co, err := charset.GetDefaultCollationLegacy(yyS[yypt-1].ident) + if err != nil { + yylex.AppendError(ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", yyS[yypt-1].ident)) + return 1 + } + expr := ast.NewValueExpr(yyS[yypt-0].item, yyS[yypt-1].ident, co) + tp := expr.GetType() + tp.SetCharset(yyS[yypt-1].ident) + tp.SetCollate(co) + tp.AddFlag(mysql.UnderScoreCharsetFlag) + if tp.GetCollate() == charset.CollationBin { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.expr = expr + } + case 1340: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) + parser.yyVAL.expr = expr + } + case 1341: + { + valExpr := yyS[yypt-1].expr.(ast.ValueExpr) + strLit := valExpr.GetString() + expr := ast.NewValueExpr(strLit+yyS[yypt-0].ident, parser.charset, parser.collation) + // Fix #4239, use first string literal as projection name. + if valExpr.GetProjectionOffset() >= 0 { + expr.SetProjectionOffset(valExpr.GetProjectionOffset()) + } else { + expr.SetProjectionOffset(len(strLit)) + } + parser.yyVAL.expr = expr + } + case 1342: + { + parser.yyVAL.item = []*ast.AlterOrderItem{yyS[yypt-0].item.(*ast.AlterOrderItem)} + } + case 1343: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.AlterOrderItem), yyS[yypt-0].item.(*ast.AlterOrderItem)) + } + case 1344: + { + parser.yyVAL.item = &ast.AlterOrderItem{Column: yyS[yypt-1].item.(*ast.ColumnName), Desc: yyS[yypt-0].item.(bool)} + } + case 1345: + { + parser.yyVAL.item = &ast.OrderByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} + } + case 1346: + { + parser.yyVAL.item = []*ast.ByItem{yyS[yypt-0].item.(*ast.ByItem)} + } + case 1347: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.ByItem), yyS[yypt-0].item.(*ast.ByItem)) + } + case 1348: + { + expr := yyS[yypt-0].expr + valueExpr, ok := expr.(ast.ValueExpr) + if ok { + position, isPosition := valueExpr.GetValue().(int64) + if isPosition { + expr = &ast.PositionExpr{N: int(position)} + } + } + parser.yyVAL.item = &ast.ByItem{Expr: expr, NullOrder: true} + } + case 1349: + { + expr := yyS[yypt-1].expr + valueExpr, ok := expr.(ast.ValueExpr) + if ok { + position, isPosition := valueExpr.GetValue().(int64) + if isPosition { + expr = &ast.PositionExpr{N: int(position)} + } + } + parser.yyVAL.item = &ast.ByItem{Expr: expr, Desc: yyS[yypt-0].item.(bool)} + } + case 1350: + { + parser.yyVAL.item = false + } + case 1351: + { + parser.yyVAL.item = true + } + case 1352: + { + parser.yyVAL.item = false // ASC by default + } + case 1353: + { + parser.yyVAL.item = false + } + case 1354: + { + parser.yyVAL.item = true + } + case 1355: + { + parser.yyVAL.item = nil + } + case 1357: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Or, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1358: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.And, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1359: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.LeftShift, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1360: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.RightShift, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1361: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Plus, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1362: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Minus, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1363: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr("DATE_ADD"), + Args: []ast.ExprNode{ + yyS[yypt-4].expr, + yyS[yypt-1].expr, + &ast.TimeUnitExpr{Unit: yyS[yypt-0].item.(ast.TimeUnitType)}, + }, + } + } + case 1364: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr("DATE_SUB"), + Args: []ast.ExprNode{ + yyS[yypt-4].expr, + yyS[yypt-1].expr, + &ast.TimeUnitExpr{Unit: yyS[yypt-0].item.(ast.TimeUnitType)}, + }, + } + } + case 1365: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr("DATE_ADD"), + Args: []ast.ExprNode{ + yyS[yypt-0].expr, + yyS[yypt-3].expr, + &ast.TimeUnitExpr{Unit: yyS[yypt-2].item.(ast.TimeUnitType)}, + }, + } + } + case 1366: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mul, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1367: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Div, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1368: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1369: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.IntDiv, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1370: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1371: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Xor, L: yyS[yypt-2].expr, R: yyS[yypt-0].expr} + } + case 1373: + { + parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ + Name: model.NewCIStr(yyS[yypt-0].ident), + }} + } + case 1374: + { + parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ + Table: model.NewCIStr(yyS[yypt-2].ident), + Name: model.NewCIStr(yyS[yypt-0].ident), + }} + } + case 1375: + { + parser.yyVAL.expr = &ast.ColumnNameExpr{Name: &ast.ColumnName{ + Schema: model.NewCIStr(yyS[yypt-4].ident), + Table: model.NewCIStr(yyS[yypt-2].ident), + Name: model.NewCIStr(yyS[yypt-0].ident), + }} + } + case 1380: + { + parser.yyVAL.expr = &ast.SetCollationExpr{Expr: yyS[yypt-2].expr, Collate: yyS[yypt-0].ident} + } + case 1383: + { + parser.yyVAL.expr = ast.NewParamMarkerExpr(yyS[yypt].offset) + } + case 1386: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not2, V: yyS[yypt-0].expr} + } + case 1387: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.BitNeg, V: yyS[yypt-0].expr} + } + case 1388: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Minus, V: yyS[yypt-0].expr} + } + case 1389: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Plus, V: yyS[yypt-0].expr} + } + case 1390: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.Concat), Args: []ast.ExprNode{yyS[yypt-2].expr, yyS[yypt-0].expr}} + } + case 1391: + { + parser.yyVAL.expr = &ast.UnaryOperationExpr{Op: opcode.Not2, V: yyS[yypt-0].expr} + } + case 1393: + { + startOffset := parser.startOffset(&yyS[yypt-1]) + endOffset := parser.endOffset(&yyS[yypt]) + expr := yyS[yypt-1].expr + expr.SetText(parser.lexer.client, parser.src[startOffset:endOffset]) + parser.yyVAL.expr = &ast.ParenthesesExpr{Expr: expr} + } + case 1394: + { + values := append(yyS[yypt-3].item.([]ast.ExprNode), yyS[yypt-1].expr) + parser.yyVAL.expr = &ast.RowExpr{Values: values} + } + case 1395: + { + values := append(yyS[yypt-3].item.([]ast.ExprNode), yyS[yypt-1].expr) + parser.yyVAL.expr = &ast.RowExpr{Values: values} + } + case 1396: + { + sq := yyS[yypt-0].expr.(*ast.SubqueryExpr) + sq.Exists = true + parser.yyVAL.expr = &ast.ExistsSubqueryExpr{Sel: sq} + } + case 1397: + { + /* + * ODBC escape syntax. + * See https://dev.mysql.com/doc/refman/5.7/en/expressions.html + */ + tp := yyS[yypt-1].expr.GetType() + switch yyS[yypt-2].ident { + case "d": + tp.SetCharset("") + tp.SetCollate("") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.DateLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} + case "t": + tp.SetCharset("") + tp.SetCollate("") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimeLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} + case "ts": + tp.SetCharset("") + tp.SetCollate("") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimestampLiteral), Args: []ast.ExprNode{yyS[yypt-1].expr}} + default: + parser.yyVAL.expr = yyS[yypt-1].expr + } + } + case 1398: + { + // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#operator_binary + tp := types.NewFieldType(mysql.TypeString) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CharsetBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.expr = &ast.FuncCastExpr{ + Expr: yyS[yypt-0].expr, + Tp: tp, + FunctionType: ast.CastBinaryOperator, + } + } + case 1399: + { + /* See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_cast */ + tp := yyS[yypt-2].item.(*types.FieldType) + defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) + if tp.GetFlen() == types.UnspecifiedLength { + tp.SetFlen(defaultFlen) + } + if tp.GetDecimal() == types.UnspecifiedLength { + tp.SetDecimal(defaultDecimal) + } + isArray := yyS[yypt-1].item.(bool) + tp.SetArray(isArray) + explicitCharset := parser.explicitCharset + if isArray && !explicitCharset && tp.GetCharset() != charset.CharsetBin { + tp.SetCharset(charset.CharsetUTF8MB4) + tp.SetCollate(charset.CollationUTF8MB4) + } + parser.explicitCharset = false + parser.yyVAL.expr = &ast.FuncCastExpr{ + Expr: yyS[yypt-4].expr, + Tp: tp, + FunctionType: ast.CastFunction, + ExplicitCharSet: explicitCharset, + } + } + case 1400: + { + x := &ast.CaseExpr{WhenClauses: yyS[yypt-2].item.([]*ast.WhenClause)} + if yyS[yypt-3].expr != nil { + x.Value = yyS[yypt-3].expr + } + if yyS[yypt-1].item != nil { + x.ElseClause = yyS[yypt-1].item.(ast.ExprNode) + } + parser.yyVAL.expr = x + } + case 1401: + { + // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert + tp := yyS[yypt-1].item.(*types.FieldType) + defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) + if tp.GetFlen() == types.UnspecifiedLength { + tp.SetFlen(defaultFlen) + } + if tp.GetDecimal() == types.UnspecifiedLength { + tp.SetDecimal(defaultDecimal) + } + explicitCharset := parser.explicitCharset + parser.explicitCharset = false + parser.yyVAL.expr = &ast.FuncCastExpr{ + Expr: yyS[yypt-3].expr, + Tp: tp, + FunctionType: ast.CastConvertFunction, + ExplicitCharSet: explicitCharset, + } + } + case 1402: + { + // See https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert + charset1 := ast.NewValueExpr(yyS[yypt-1].ident, "", "") + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{yyS[yypt-3].expr, charset1}, + } + } + case 1403: + { + parser.yyVAL.expr = &ast.DefaultExpr{Name: yyS[yypt-1].expr.(*ast.ColumnNameExpr).Name} + } + case 1404: + { + parser.yyVAL.expr = &ast.ValuesExpr{Column: yyS[yypt-1].expr.(*ast.ColumnNameExpr)} + } + case 1405: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{yyS[yypt-2].expr, expr}} + } + case 1406: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) + extract := &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONExtract), Args: []ast.ExprNode{yyS[yypt-2].expr, expr}} + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.JSONUnquote), Args: []ast.ExprNode{extract}} + } + case 1407: + { + parser.yyVAL.item = false + } + case 1408: + { + parser.yyVAL.item = true + } + case 1411: + { + parser.yyVAL.item = false + } + case 1412: + { + parser.yyVAL.item = true + } + case 1413: + { + parser.yyVAL.item = false + } + case 1415: + { + parser.yyVAL.item = true + } + case 1418: + { + parser.yyVAL.item = true + } + case 1462: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1463: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1464: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-1].ident)} + } + case 1465: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-2].ident)} + } + case 1466: + { + args := []ast.ExprNode{} + if yyS[yypt-0].item != nil { + args = append(args, yyS[yypt-0].item.(ast.ExprNode)) + } + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-1].ident), Args: args} + } + case 1467: + { + nilVal := ast.NewValueExpr(nil, parser.charset, parser.collation) + args := yyS[yypt-1].item.([]ast.ExprNode) + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.CharFunc), + Args: append(args, nilVal), + } + } + case 1468: + { + charset1 := ast.NewValueExpr(yyS[yypt-1].ident, "", "") + args := yyS[yypt-3].item.([]ast.ExprNode) + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.CharFunc), + Args: append(args, charset1), + } + } + case 1469: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.DateLiteral), Args: []ast.ExprNode{expr}} + } + case 1470: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimeLiteral), Args: []ast.ExprNode{expr}} + } + case 1471: + { + expr := ast.NewValueExpr(yyS[yypt-0].ident, "", "") + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.TimestampLiteral), Args: []ast.ExprNode{expr}} + } + case 1472: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.InsertFunc), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1473: + { + parser.yyVAL.expr = &ast.BinaryOperationExpr{Op: opcode.Mod, L: yyS[yypt-3].expr, R: yyS[yypt-1].expr} + } + case 1474: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(ast.PasswordFunc), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1475: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1476: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-3].ident), Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1477: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{ + yyS[yypt-3].expr, + yyS[yypt-1].expr, + &ast.TimeUnitExpr{Unit: ast.TimeUnitDay}, + }, + } + } + case 1478: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{ + yyS[yypt-5].expr, + yyS[yypt-2].expr, + &ast.TimeUnitExpr{Unit: yyS[yypt-1].item.(ast.TimeUnitType)}, + }, + } + } + case 1479: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{ + yyS[yypt-5].expr, + yyS[yypt-2].expr, + &ast.TimeUnitExpr{Unit: yyS[yypt-1].item.(ast.TimeUnitType)}, + }, + } + } + case 1480: + { + timeUnit := &ast.TimeUnitExpr{Unit: yyS[yypt-3].item.(ast.TimeUnitType)} + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{timeUnit, yyS[yypt-1].expr}, + } + } + case 1481: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{ + &ast.GetFormatSelectorExpr{Selector: yyS[yypt-3].item.(ast.GetFormatSelectorType)}, + yyS[yypt-1].expr, + }, + } + } + case 1482: + { + parser.yyVAL.expr = &ast.FuncCallExpr{FnName: model.NewCIStr(yyS[yypt-5].ident), Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}} + } + case 1483: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1484: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1485: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1486: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1487: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: yyS[yypt-5].item.(ast.TimeUnitType)}, yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1488: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{&ast.TimeUnitExpr{Unit: yyS[yypt-5].item.(ast.TimeUnitType)}, yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1489: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: []ast.ExprNode{yyS[yypt-1].expr}, + } + } + case 1490: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{yyS[yypt-1].expr, yyS[yypt-3].expr}, + } + } + case 1491: + { + spaceVal := ast.NewValueExpr(" ", parser.charset, parser.collation) + direction := &ast.TrimDirectionExpr{Direction: yyS[yypt-3].item.(ast.TrimDirectionType)} + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-5].ident), + Args: []ast.ExprNode{yyS[yypt-1].expr, spaceVal, direction}, + } + } + case 1492: + { + direction := &ast.TrimDirectionExpr{Direction: yyS[yypt-4].item.(ast.TrimDirectionType)} + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-6].ident), + Args: []ast.ExprNode{yyS[yypt-1].expr, yyS[yypt-3].expr, direction}, + } + } + case 1493: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: []ast.ExprNode{yyS[yypt-1].expr}, + } + } + case 1494: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-6].ident), + Args: []ast.ExprNode{yyS[yypt-4].expr, ast.NewValueExpr("CHAR", parser.charset, parser.collation), ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}, + } + } + case 1495: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-6].ident), + Args: []ast.ExprNode{yyS[yypt-4].expr, ast.NewValueExpr("BINARY", parser.charset, parser.collation), ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)}, + } + } + case 1497: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-7].ident), + Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-3].expr, yyS[yypt-1].expr}, + } + } + case 1498: + { + parser.yyVAL.item = ast.GetFormatSelectorDate + } + case 1499: + { + parser.yyVAL.item = ast.GetFormatSelectorDatetime + } + case 1500: + { + parser.yyVAL.item = ast.GetFormatSelectorTime + } + case 1501: + { + parser.yyVAL.item = ast.GetFormatSelectorDatetime + } + case 1506: + { + parser.yyVAL.item = ast.TrimBoth + } + case 1507: + { + parser.yyVAL.item = ast.TrimLeading + } + case 1508: + { + parser.yyVAL.item = ast.TrimTrailing + } + case 1509: + { + objNameExpr := &ast.TableNameExpr{ + Name: yyS[yypt-1].item.(*ast.TableName), + } + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.LastVal), + Args: []ast.ExprNode{objNameExpr}, + } + } + case 1510: + { + objNameExpr := &ast.TableNameExpr{ + Name: yyS[yypt-3].item.(*ast.TableName), + } + valueExpr := ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation) + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(ast.SetVal), + Args: []ast.ExprNode{objNameExpr, valueExpr}, + } + } + case 1512: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1513: + { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-3].ident, Args: yyS[yypt-1].item.([]ast.ExprNode), Distinct: false} + } + case 1514: + { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-3].ident, Args: yyS[yypt-1].item.([]ast.ExprNode)} + } + case 1515: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1516: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1517: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1518: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1519: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1520: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1521: + { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: yyS[yypt-1].item.([]ast.ExprNode), Distinct: true} + } + case 1522: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1523: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1524: + { + args := []ast.ExprNode{ast.NewValueExpr(1, parser.charset, parser.collation)} + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: args, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: args} + } + } + case 1525: + { + args := yyS[yypt-4].item.([]ast.ExprNode) + args = append(args, yyS[yypt-2].item.(ast.ExprNode)) + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: args, Distinct: yyS[yypt-5].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + agg := &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: args, Distinct: yyS[yypt-5].item.(bool)} + if yyS[yypt-3].item != nil { + agg.Order = yyS[yypt-3].item.(*ast.OrderByClause) + } + parser.yyVAL.expr = agg + } + } + case 1526: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1527: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1528: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1529: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: ast.AggFuncStddevPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: ast.AggFuncStddevPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1530: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1531: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: ast.AggFuncVarPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool), Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: ast.AggFuncVarPop, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + } + case 1532: + { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Distinct: yyS[yypt-3].item.(bool)} + } + case 1533: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1534: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}} + } + } + case 1535: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-6].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}} + } + } + case 1536: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-4].expr, yyS[yypt-2].expr}} + } + } + case 1537: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-7].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}} + } + } + case 1538: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}, Spec: *(yyS[yypt-0].item.(*ast.WindowSpec))} + } else { + parser.yyVAL.expr = &ast.AggregateFuncExpr{F: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-5].expr, yyS[yypt-2].expr}} + } + } + case 1539: + { + parser.yyVAL.item = ast.NewValueExpr(",", "", "") + } + case 1540: + { + parser.yyVAL.item = ast.NewValueExpr(yyS[yypt-0].ident, "", "") + } + case 1541: + { + parser.yyVAL.expr = &ast.FuncCallExpr{ + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 1542: + { + var tp ast.FuncCallExprType + if isInTokenMap(yyS[yypt-3].ident) { + tp = ast.FuncCallExprTypeKeyword + } else { + tp = ast.FuncCallExprTypeGeneric + } + parser.yyVAL.expr = &ast.FuncCallExpr{ + Tp: tp, + Schema: model.NewCIStr(yyS[yypt-5].ident), + FnName: model.NewCIStr(yyS[yypt-3].ident), + Args: yyS[yypt-1].item.([]ast.ExprNode), + } + } + case 1543: + { + parser.yyVAL.item = nil + } + case 1544: + { + parser.yyVAL.item = nil + } + case 1545: + { + expr := ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation) + parser.yyVAL.item = expr + } + case 1547: + { + parser.yyVAL.item = ast.TimeUnitSecondMicrosecond + } + case 1548: + { + parser.yyVAL.item = ast.TimeUnitMinuteMicrosecond + } + case 1549: + { + parser.yyVAL.item = ast.TimeUnitMinuteSecond + } + case 1550: + { + parser.yyVAL.item = ast.TimeUnitHourMicrosecond + } + case 1551: + { + parser.yyVAL.item = ast.TimeUnitHourSecond + } + case 1552: + { + parser.yyVAL.item = ast.TimeUnitHourMinute + } + case 1553: + { + parser.yyVAL.item = ast.TimeUnitDayMicrosecond + } + case 1554: + { + parser.yyVAL.item = ast.TimeUnitDaySecond + } + case 1555: + { + parser.yyVAL.item = ast.TimeUnitDayMinute + } + case 1556: + { + parser.yyVAL.item = ast.TimeUnitDayHour + } + case 1557: + { + parser.yyVAL.item = ast.TimeUnitYearMonth + } + case 1558: + { + parser.yyVAL.item = ast.TimeUnitMicrosecond + } + case 1559: + { + parser.yyVAL.item = ast.TimeUnitSecond + } + case 1560: + { + parser.yyVAL.item = ast.TimeUnitMinute + } + case 1561: + { + parser.yyVAL.item = ast.TimeUnitHour + } + case 1562: + { + parser.yyVAL.item = ast.TimeUnitDay + } + case 1563: + { + parser.yyVAL.item = ast.TimeUnitWeek + } + case 1564: + { + parser.yyVAL.item = ast.TimeUnitMonth + } + case 1565: + { + parser.yyVAL.item = ast.TimeUnitQuarter + } + case 1566: + { + parser.yyVAL.item = ast.TimeUnitYear + } + case 1567: + { + parser.yyVAL.item = ast.TimeUnitSecond + } + case 1568: + { + parser.yyVAL.item = ast.TimeUnitMinute + } + case 1569: + { + parser.yyVAL.item = ast.TimeUnitHour + } + case 1570: + { + parser.yyVAL.item = ast.TimeUnitDay + } + case 1571: + { + parser.yyVAL.item = ast.TimeUnitWeek + } + case 1572: + { + parser.yyVAL.item = ast.TimeUnitMonth + } + case 1573: + { + parser.yyVAL.item = ast.TimeUnitQuarter + } + case 1574: + { + parser.yyVAL.item = ast.TimeUnitYear + } + case 1575: + { + parser.yyVAL.expr = nil + } + case 1577: + { + parser.yyVAL.item = []*ast.WhenClause{yyS[yypt-0].item.(*ast.WhenClause)} + } + case 1578: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.WhenClause), yyS[yypt-0].item.(*ast.WhenClause)) + } + case 1579: + { + parser.yyVAL.item = &ast.WhenClause{ + Expr: yyS[yypt-2].expr, + Result: yyS[yypt-0].expr, + } + } + case 1580: + { + parser.yyVAL.item = nil + } + case 1581: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 1582: + { + tp := types.NewFieldType(mysql.TypeVarString) + tp.SetFlen(yyS[yypt-0].item.(int)) // TODO: Flen should be the flen of expression + if tp.GetFlen() != types.UnspecifiedLength { + tp.SetType(mysql.TypeString) + } + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1583: + { + tp := types.NewFieldType(mysql.TypeVarString) + tp.SetFlen(yyS[yypt-1].item.(int)) // TODO: Flen should be the flen of expression + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + } else if tp.GetCharset() != "" { + co, err := charset.GetDefaultCollation(tp.GetCharset()) + if err != nil { + yylex.AppendError(yylex.Errorf("Get collation error for charset: %s", tp.GetCharset())) + return 1 + } + tp.SetCollate(co) + parser.explicitCharset = true + } else { + tp.SetCharset(parser.charset) + tp.SetCollate(parser.collation) + } + parser.yyVAL.item = tp + } + case 1584: + { + tp := types.NewFieldType(mysql.TypeDate) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1585: + { + tp := types.NewFieldType(mysql.TypeYear) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1586: + { + tp := types.NewFieldType(mysql.TypeDatetime) + flen, _ := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDatetime) + tp.SetFlen(flen) + tp.SetDecimal(yyS[yypt-0].item.(int)) + if tp.GetDecimal() > 0 { + tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) + } + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1587: + { + fopt := yyS[yypt-0].item.(*ast.FloatOpt) + tp := types.NewFieldType(mysql.TypeNewDecimal) + tp.SetFlen(fopt.Flen) + tp.SetDecimal(fopt.Decimal) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1588: + { + tp := types.NewFieldType(mysql.TypeDuration) + flen, _ := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDuration) + tp.SetFlen(flen) + tp.SetDecimal(yyS[yypt-0].item.(int)) + if tp.GetDecimal() > 0 { + tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) + } + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1589: + { + tp := types.NewFieldType(mysql.TypeLonglong) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 1590: + { + tp := types.NewFieldType(mysql.TypeLonglong) + tp.AddFlag(mysql.UnsignedFlag | mysql.BinaryFlag) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + parser.yyVAL.item = tp + } + case 1591: + { + tp := types.NewFieldType(mysql.TypeJSON) + tp.AddFlag(mysql.BinaryFlag | mysql.ParseToJSONFlag) + tp.SetCharset(mysql.DefaultCharset) + tp.SetCollate(mysql.DefaultCollationName) + parser.yyVAL.item = tp + } + case 1592: + { + tp := types.NewFieldType(mysql.TypeDouble) + flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(mysql.TypeDouble) + tp.SetFlen(flen) + tp.SetDecimal(decimal) + tp.AddFlag(mysql.BinaryFlag) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + parser.yyVAL.item = tp + } + case 1593: + { + tp := types.NewFieldType(mysql.TypeFloat) + fopt := yyS[yypt-0].item.(*ast.FloatOpt) + if fopt.Flen >= 54 { + yylex.AppendError(ErrTooBigPrecision.GenWithStackByArgs(fopt.Flen, "CAST", 53)) + } else if fopt.Flen >= 25 { + tp = types.NewFieldType(mysql.TypeDouble) + } + flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) + tp.SetFlen(flen) + tp.SetDecimal(decimal) + tp.AddFlag(mysql.BinaryFlag) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + parser.yyVAL.item = tp + } + case 1594: + { + var tp *types.FieldType + if parser.lexer.GetSQLMode().HasRealAsFloatMode() { + tp = types.NewFieldType(mysql.TypeFloat) + } else { + tp = types.NewFieldType(mysql.TypeDouble) + } + flen, decimal := mysql.GetDefaultFieldLengthAndDecimalForCast(tp.GetType()) + tp.SetFlen(flen) + tp.SetDecimal(decimal) + tp.AddFlag(mysql.BinaryFlag) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + parser.yyVAL.item = tp + } + case 1595: + { + parser.yyVAL.item = mysql.LowPriority + } + case 1596: + { + parser.yyVAL.item = mysql.HighPriority + } + case 1597: + { + parser.yyVAL.item = mysql.DelayedPriority + } + case 1598: + { + parser.yyVAL.item = mysql.NoPriority + } + case 1600: + { + parser.yyVAL.item = &ast.TableName{Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 1601: + { + parser.yyVAL.item = &ast.TableName{Schema: model.NewCIStr(yyS[yypt-2].ident), Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 1602: + { + tbl := []*ast.TableName{yyS[yypt-0].item.(*ast.TableName)} + parser.yyVAL.item = tbl + } + case 1603: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableName), yyS[yypt-0].item.(*ast.TableName)) + } + case 1604: + { + parser.yyVAL.item = &ast.TableName{Name: model.NewCIStr(yyS[yypt-1].ident)} + } + case 1605: + { + parser.yyVAL.item = &ast.TableName{Schema: model.NewCIStr(yyS[yypt-3].ident), Name: model.NewCIStr(yyS[yypt-1].ident)} + } + case 1606: + { + tbl := []*ast.TableName{yyS[yypt-0].item.(*ast.TableName)} + parser.yyVAL.item = tbl + } + case 1607: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TableName), yyS[yypt-0].item.(*ast.TableName)) + } + case 1610: + { + parser.yyVAL.item = false + } + case 1611: + { + parser.yyVAL.item = true + } + case 1612: + { + var sqlText string + var sqlVar *ast.VariableExpr + switch x := yyS[yypt-0].item.(type) { + case string: + sqlText = x + case *ast.VariableExpr: + sqlVar = x + } + parser.yyVAL.statement = &ast.PrepareStmt{ + Name: yyS[yypt-2].ident, + SQLText: sqlText, + SQLVar: sqlVar, + } + } + case 1613: + { + parser.yyVAL.item = yyS[yypt-0].ident + } + case 1614: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 1615: + { + parser.yyVAL.statement = &ast.ExecuteStmt{Name: yyS[yypt-0].ident} + } + case 1616: + { + parser.yyVAL.statement = &ast.ExecuteStmt{ + Name: yyS[yypt-2].ident, + UsingVars: yyS[yypt-0].item.([]ast.ExprNode), + } + } + case 1617: + { + parser.yyVAL.item = []ast.ExprNode{yyS[yypt-0].expr} + } + case 1618: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.ExprNode), yyS[yypt-0].expr) + } + case 1619: + { + parser.yyVAL.statement = &ast.DeallocateStmt{Name: yyS[yypt-0].ident} + } + case 1622: + { + parser.yyVAL.statement = &ast.RollbackStmt{} + } + case 1623: + { + parser.yyVAL.statement = &ast.RollbackStmt{CompletionType: yyS[yypt-0].item.(ast.CompletionType)} + } + case 1624: + { + parser.yyVAL.statement = &ast.RollbackStmt{SavepointName: yyS[yypt-0].ident} + } + case 1625: + { + parser.yyVAL.statement = &ast.RollbackStmt{SavepointName: yyS[yypt-0].ident} + } + case 1626: + { + parser.yyVAL.item = ast.CompletionTypeChain + } + case 1627: + { + parser.yyVAL.item = ast.CompletionTypeRelease + } + case 1628: + { + parser.yyVAL.item = ast.CompletionTypeDefault + } + case 1629: + { + parser.yyVAL.item = ast.CompletionTypeChain + } + case 1630: + { + parser.yyVAL.item = ast.CompletionTypeDefault + } + case 1631: + { + parser.yyVAL.item = ast.CompletionTypeRelease + } + case 1632: + { + parser.yyVAL.item = ast.CompletionTypeDefault + } + case 1633: + { + parser.yyVAL.statement = &ast.ShutdownStmt{} + } + case 1634: + { + parser.yyVAL.statement = &ast.RestartStmt{} + } + case 1635: + { + parser.yyVAL.statement = &ast.HelpStmt{Topic: yyS[yypt-0].ident} + } + case 1636: + { + st := &ast.SelectStmt{ + SelectStmtOpts: yyS[yypt-2].item.(*ast.SelectStmtOpts), + Distinct: yyS[yypt-2].item.(*ast.SelectStmtOpts).Distinct, + Fields: yyS[yypt-1].item.(*ast.FieldList), + Kind: ast.SelectStmtKindSelect, + } + if st.SelectStmtOpts.TableHints != nil { + st.TableHints = st.SelectStmtOpts.TableHints + } + if yyS[yypt-0].item != nil { + st.Having = yyS[yypt-0].item.(*ast.HavingClause) + } + parser.yyVAL.item = st + } + case 1637: + { + st := yyS[yypt-2].item.(*ast.SelectStmt) + lastField := st.Fields.Fields[len(st.Fields.Fields)-1] + if lastField.Expr != nil && lastField.AsName.O == "" { + lastEnd := yyS[yypt-1].offset - 1 + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) + } + if yyS[yypt-0].item != nil { + st.Where = yyS[yypt-0].item.(ast.ExprNode) + } + } + case 1638: + { + st := yyS[yypt-6].item.(*ast.SelectStmt) + st.From = yyS[yypt-4].item.(*ast.TableRefsClause) + lastField := st.Fields.Fields[len(st.Fields.Fields)-1] + if lastField.Expr != nil && lastField.AsName.O == "" { + lastEnd := parser.endOffset(&yyS[yypt-5]) + lastField.SetText(parser.lexer.client, parser.src[lastField.Offset:lastEnd]) + } + if yyS[yypt-3].item != nil { + st.Where = yyS[yypt-3].item.(ast.ExprNode) + } + if yyS[yypt-2].item != nil { + st.GroupBy = yyS[yypt-2].item.(*ast.GroupByClause) + } + if yyS[yypt-1].item != nil { + st.Having = yyS[yypt-1].item.(*ast.HavingClause) + } + if yyS[yypt-0].item != nil { + st.WindowSpecs = (yyS[yypt-0].item.([]ast.WindowSpec)) + } + parser.yyVAL.item = st + } + case 1639: + { + parser.yyVAL.item = nil + } + case 1640: + { + var repSeed ast.ExprNode + if yyS[yypt-0].expr != nil { + repSeed = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) + } + parser.yyVAL.item = &ast.TableSample{ + SampleMethod: yyS[yypt-5].item.(ast.SampleMethodType), + Expr: ast.NewValueExpr(yyS[yypt-3].expr, parser.charset, parser.collation), + SampleClauseUnit: yyS[yypt-2].item.(ast.SampleClauseUnitType), + RepeatableSeed: repSeed, + } + } + case 1641: + { + var repSeed ast.ExprNode + if yyS[yypt-0].expr != nil { + repSeed = ast.NewValueExpr(yyS[yypt-0].expr, parser.charset, parser.collation) + } + parser.yyVAL.item = &ast.TableSample{ + SampleMethod: yyS[yypt-3].item.(ast.SampleMethodType), + RepeatableSeed: repSeed, + } + } + case 1642: + { + parser.yyVAL.item = ast.SampleMethodTypeNone + } + case 1643: + { + parser.yyVAL.item = ast.SampleMethodTypeSystem + } + case 1644: + { + parser.yyVAL.item = ast.SampleMethodTypeBernoulli + } + case 1645: + { + parser.yyVAL.item = ast.SampleMethodTypeTiDBRegion + } + case 1646: + { + parser.yyVAL.item = ast.SampleClauseUnitTypeDefault + } + case 1647: + { + parser.yyVAL.item = ast.SampleClauseUnitTypeRow + } + case 1648: + { + parser.yyVAL.item = ast.SampleClauseUnitTypePercent + } + case 1649: + { + parser.yyVAL.expr = nil + } + case 1650: + { + parser.yyVAL.expr = yyS[yypt-1].expr + } + case 1651: + { + st := yyS[yypt-6].item.(*ast.SelectStmt) + if yyS[yypt-1].item != nil { + st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) + } + if yyS[yypt-5].item != nil { + st.Where = yyS[yypt-5].item.(ast.ExprNode) + } + if yyS[yypt-4].item != nil { + st.GroupBy = yyS[yypt-4].item.(*ast.GroupByClause) + } + if yyS[yypt-3].item != nil { + st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) + } + if yyS[yypt-2].item != nil { + st.Limit = yyS[yypt-2].item.(*ast.Limit) + } + if yyS[yypt-0].item != nil { + st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) + } + parser.yyVAL.statement = st + } + case 1652: + { + st := yyS[yypt-5].item.(*ast.SelectStmt) + if yyS[yypt-4].item != nil { + st.GroupBy = yyS[yypt-4].item.(*ast.GroupByClause) + } + if yyS[yypt-3].item != nil { + st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) + } + if yyS[yypt-2].item != nil { + st.Limit = yyS[yypt-2].item.(*ast.Limit) + } + if yyS[yypt-1].item != nil { + st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) + } + if yyS[yypt-0].item != nil { + st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) + } + parser.yyVAL.statement = st + } + case 1653: + { + st := yyS[yypt-4].item.(*ast.SelectStmt) + if yyS[yypt-1].item != nil { + st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) + } + if yyS[yypt-3].item != nil { + st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) + } + if yyS[yypt-2].item != nil { + st.Limit = yyS[yypt-2].item.(*ast.Limit) + } + if yyS[yypt-0].item != nil { + st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) + } + parser.yyVAL.statement = st + } + case 1654: + { + st := &ast.SelectStmt{ + Kind: ast.SelectStmtKindTable, + Fields: &ast.FieldList{Fields: []*ast.SelectField{{WildCard: &ast.WildCardField{}}}}, + } + ts := &ast.TableSource{Source: yyS[yypt-4].item.(*ast.TableName)} + st.From = &ast.TableRefsClause{TableRefs: &ast.Join{Left: ts}} + if yyS[yypt-3].item != nil { + st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) + } + if yyS[yypt-2].item != nil { + st.Limit = yyS[yypt-2].item.(*ast.Limit) + } + if yyS[yypt-1].item != nil { + st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) + } + if yyS[yypt-0].item != nil { + st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) + } + parser.yyVAL.statement = st + } + case 1655: + { + st := &ast.SelectStmt{ + Kind: ast.SelectStmtKindValues, + Fields: &ast.FieldList{Fields: []*ast.SelectField{{WildCard: &ast.WildCardField{}}}}, + Lists: yyS[yypt-4].item.([]*ast.RowExpr), + } + if yyS[yypt-3].item != nil { + st.OrderBy = yyS[yypt-3].item.(*ast.OrderByClause) + } + if yyS[yypt-2].item != nil { + st.Limit = yyS[yypt-2].item.(*ast.Limit) + } + if yyS[yypt-1].item != nil { + st.LockInfo = yyS[yypt-1].item.(*ast.SelectLockInfo) + } + if yyS[yypt-0].item != nil { + st.SelectIntoOpt = yyS[yypt-0].item.(*ast.SelectIntoOption) + } + parser.yyVAL.statement = st + } + case 1656: + { + sel := yyS[yypt-0].statement.(*ast.SelectStmt) + sel.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = sel + } + case 1657: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + x.WithBeforeBraces = true + x.With = yyS[yypt-1].item.(*ast.WithClause) + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + x.With = yyS[yypt-1].item.(*ast.WithClause) + sel = x + } + parser.yyVAL.statement = sel + } + case 1658: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 1659: + { + ws := yyS[yypt-0].item.(*ast.WithClause) + ws.IsRecursive = true + for _, cte := range ws.CTEs { + cte.IsRecursive = true + } + parser.yyVAL.item = ws + } + case 1660: + { + ws := yyS[yypt-2].item.(*ast.WithClause) + ws.CTEs = append(ws.CTEs, yyS[yypt-0].item.(*ast.CommonTableExpression)) + parser.yyVAL.item = ws + } + case 1661: + { + ws := &ast.WithClause{} + ws.CTEs = make([]*ast.CommonTableExpression, 0, 4) + ws.CTEs = append(ws.CTEs, yyS[yypt-0].item.(*ast.CommonTableExpression)) + parser.yyVAL.item = ws + } + case 1662: + { + cte := &ast.CommonTableExpression{} + cte.Name = model.NewCIStr(yyS[yypt-3].ident) + cte.ColNameList = yyS[yypt-2].item.([]model.CIStr) + cte.Query = yyS[yypt-0].expr.(*ast.SubqueryExpr) + parser.yyVAL.item = cte + } + case 1664: + { + parser.yyVAL.item = nil + } + case 1665: + { + parser.yyVAL.item = yyS[yypt-0].item.([]ast.WindowSpec) + } + case 1666: + { + parser.yyVAL.item = []ast.WindowSpec{yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1667: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.WindowSpec), yyS[yypt-0].item.(ast.WindowSpec)) + } + case 1668: + { + var spec = yyS[yypt-0].item.(ast.WindowSpec) + spec.Name = yyS[yypt-2].item.(model.CIStr) + parser.yyVAL.item = spec + } + case 1669: + { + parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) + } + case 1670: + { + parser.yyVAL.item = yyS[yypt-1].item.(ast.WindowSpec) + } + case 1671: + { + spec := ast.WindowSpec{Ref: yyS[yypt-3].item.(model.CIStr)} + if yyS[yypt-2].item != nil { + spec.PartitionBy = yyS[yypt-2].item.(*ast.PartitionByClause) + } + if yyS[yypt-1].item != nil { + spec.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) + } + if yyS[yypt-0].item != nil { + spec.Frame = yyS[yypt-0].item.(*ast.FrameClause) + } + parser.yyVAL.item = spec + } + case 1672: + { + parser.yyVAL.item = model.CIStr{} + } + case 1674: + { + parser.yyVAL.item = nil + } + case 1675: + { + parser.yyVAL.item = &ast.PartitionByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} + } + case 1676: + { + parser.yyVAL.item = nil + } + case 1677: + { + parser.yyVAL.item = &ast.OrderByClause{Items: yyS[yypt-0].item.([]*ast.ByItem)} + } + case 1678: + { + parser.yyVAL.item = nil + } + case 1679: + { + parser.yyVAL.item = &ast.FrameClause{ + Type: yyS[yypt-1].item.(ast.FrameType), + Extent: yyS[yypt-0].item.(ast.FrameExtent), + } + } + case 1680: + { + parser.yyVAL.item = ast.FrameType(ast.Rows) + } + case 1681: + { + parser.yyVAL.item = ast.FrameType(ast.Ranges) + } + case 1682: + { + parser.yyVAL.item = ast.FrameType(ast.Groups) + } + case 1683: + { + parser.yyVAL.item = ast.FrameExtent{ + Start: yyS[yypt-0].item.(ast.FrameBound), + End: ast.FrameBound{Type: ast.CurrentRow}, + } + } + case 1685: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, UnBounded: true} + } + case 1686: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} + } + case 1687: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset)} + } + case 1688: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Preceding, Expr: yyS[yypt-2].expr, Unit: yyS[yypt-1].item.(ast.TimeUnitType)} + } + case 1689: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.CurrentRow} + } + case 1690: + { + parser.yyVAL.item = ast.FrameExtent{Start: yyS[yypt-2].item.(ast.FrameBound), End: yyS[yypt-0].item.(ast.FrameBound)} + } + case 1692: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Following, UnBounded: true} + } + case 1693: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} + } + case 1694: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: ast.NewParamMarkerExpr(yyS[yypt].offset)} + } + case 1695: + { + parser.yyVAL.item = ast.FrameBound{Type: ast.Following, Expr: yyS[yypt-2].expr, Unit: yyS[yypt-1].item.(ast.TimeUnitType)} + } + case 1696: + { + parser.yyVAL.item = nil + } + case 1697: + { + spec := yyS[yypt-0].item.(ast.WindowSpec) + parser.yyVAL.item = &spec + } + case 1698: + { + parser.yyVAL.item = yyS[yypt-0].item.(ast.WindowSpec) + } + case 1699: + { + parser.yyVAL.item = ast.WindowSpec{Name: yyS[yypt-0].item.(model.CIStr), OnlyAlias: true} + } + case 1701: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1702: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1703: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1704: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1705: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-3].ident, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1706: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-4].ident, Args: []ast.ExprNode{yyS[yypt-2].expr}, Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1707: + { + args := []ast.ExprNode{yyS[yypt-4].expr} + if yyS[yypt-3].item != nil { + args = append(args, yyS[yypt-3].item.([]ast.ExprNode)...) + } + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: args, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1708: + { + args := []ast.ExprNode{yyS[yypt-4].expr} + if yyS[yypt-3].item != nil { + args = append(args, yyS[yypt-3].item.([]ast.ExprNode)...) + } + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-6].ident, Args: args, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1709: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-3].expr}, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1710: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-5].ident, Args: []ast.ExprNode{yyS[yypt-3].expr}, IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1711: + { + parser.yyVAL.expr = &ast.WindowFuncExpr{Name: yyS[yypt-8].ident, Args: []ast.ExprNode{yyS[yypt-6].expr, yyS[yypt-4].expr}, FromLast: yyS[yypt-2].item.(bool), IgnoreNull: yyS[yypt-1].item.(bool), Spec: yyS[yypt-0].item.(ast.WindowSpec)} + } + case 1712: + { + parser.yyVAL.item = nil + } + case 1713: + { + args := []ast.ExprNode{ast.NewValueExpr(yyS[yypt-1].item, parser.charset, parser.collation)} + if yyS[yypt-0].item != nil { + args = append(args, yyS[yypt-0].item.(ast.ExprNode)) + } + parser.yyVAL.item = args + } + case 1714: + { + args := []ast.ExprNode{ast.NewParamMarkerExpr(yyS[yypt-1].offset)} + if yyS[yypt-0].item != nil { + args = append(args, yyS[yypt-0].item.(ast.ExprNode)) + } + parser.yyVAL.item = args + } + case 1715: + { + parser.yyVAL.item = nil + } + case 1716: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 1717: + { + parser.yyVAL.item = false + } + case 1718: + { + parser.yyVAL.item = false + } + case 1719: + { + parser.yyVAL.item = true + } + case 1720: + { + parser.yyVAL.item = false + } + case 1721: + { + parser.yyVAL.item = false + } + case 1722: + { + parser.yyVAL.item = true + } + case 1723: + { + parser.yyVAL.item = &ast.TableRefsClause{TableRefs: yyS[yypt-0].item.(*ast.Join)} + } + case 1724: + { + if j, ok := yyS[yypt-0].item.(*ast.Join); ok { + // if $1 is Join, use it directly + parser.yyVAL.item = j + } else { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-0].item.(ast.ResultSetNode), Right: nil} + } + } + case 1725: + { + /* from a, b is default cross join */ + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-2].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), Tp: ast.CrossJoin} + } + case 1727: + { + /* + * ODBC escape syntax for outer join is { OJ join_table } + * Use an Identifier for OJ + */ + parser.yyVAL.item = yyS[yypt-1].item + } + case 1730: + { + tn := yyS[yypt-5].item.(*ast.TableName) + tn.PartitionNames = yyS[yypt-4].item.([]model.CIStr) + tn.IndexHints = yyS[yypt-1].item.([]*ast.IndexHint) + if yyS[yypt-0].item != nil { + tn.TableSample = yyS[yypt-0].item.(*ast.TableSample) + } + if yyS[yypt-2].item != nil { + tn.AsOf = yyS[yypt-2].item.(*ast.AsOfClause) + } + parser.yyVAL.item = &ast.TableSource{Source: tn, AsName: yyS[yypt-3].item.(model.CIStr)} + } + case 1731: + { + resultNode := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query + parser.yyVAL.item = &ast.TableSource{Source: resultNode, AsName: yyS[yypt-0].item.(model.CIStr)} + } + case 1732: + { + j := yyS[yypt-1].item.(*ast.Join) + j.ExplicitParens = true + parser.yyVAL.item = yyS[yypt-1].item + } + case 1733: + { + parser.yyVAL.item = []model.CIStr{} + } + case 1734: + { + parser.yyVAL.item = yyS[yypt-1].item + } + case 1735: + { + parser.yyVAL.item = model.CIStr{} + } + case 1737: + { + parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) + } + case 1738: + { + parser.yyVAL.item = model.NewCIStr(yyS[yypt-0].ident) + } + case 1739: + { + parser.yyVAL.item = ast.HintUse + } + case 1740: + { + parser.yyVAL.item = ast.HintIgnore + } + case 1741: + { + parser.yyVAL.item = ast.HintForce + } + case 1742: + { + parser.yyVAL.item = ast.HintForScan + } + case 1743: + { + parser.yyVAL.item = ast.HintForJoin + } + case 1744: + { + parser.yyVAL.item = ast.HintForOrderBy + } + case 1745: + { + parser.yyVAL.item = ast.HintForGroupBy + } + case 1746: + { + parser.yyVAL.item = &ast.IndexHint{ + IndexNames: yyS[yypt-1].item.([]model.CIStr), + HintType: yyS[yypt-4].item.(ast.IndexHintType), + HintScope: yyS[yypt-3].item.(ast.IndexHintScope), + } + } + case 1747: + { + var nameList []model.CIStr + parser.yyVAL.item = nameList + } + case 1748: + { + parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} + } + case 1749: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) + } + case 1750: + { + parser.yyVAL.item = []model.CIStr{model.NewCIStr(yyS[yypt-0].ident)} + } + case 1751: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]model.CIStr), model.NewCIStr(yyS[yypt-0].ident)) + } + case 1752: + { + parser.yyVAL.item = []*ast.IndexHint{yyS[yypt-0].item.(*ast.IndexHint)} + } + case 1753: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.IndexHint), yyS[yypt-0].item.(*ast.IndexHint)) + } + case 1754: + { + parser.yyVAL.item = []*ast.IndexHint{} + } + case 1756: + { + parser.yyVAL.item = ast.NewCrossJoin(yyS[yypt-2].item.(ast.ResultSetNode), yyS[yypt-0].item.(ast.ResultSetNode)) + } + case 1757: + { + on := &ast.OnCondition{Expr: yyS[yypt-0].expr} + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-4].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), Tp: ast.CrossJoin, On: on} + } + case 1758: + { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-6].item.(ast.ResultSetNode), Right: yyS[yypt-4].item.(ast.ResultSetNode), Tp: ast.CrossJoin, Using: yyS[yypt-1].item.([]*ast.ColumnName)} + } + case 1759: + { + on := &ast.OnCondition{Expr: yyS[yypt-0].expr} + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-6].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), Tp: yyS[yypt-5].item.(ast.JoinType), On: on} + } + case 1760: + { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-8].item.(ast.ResultSetNode), Right: yyS[yypt-4].item.(ast.ResultSetNode), Tp: yyS[yypt-7].item.(ast.JoinType), Using: yyS[yypt-1].item.([]*ast.ColumnName)} + } + case 1761: + { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-3].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), NaturalJoin: true} + } + case 1762: + { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-5].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), Tp: yyS[yypt-3].item.(ast.JoinType), NaturalJoin: true} + } + case 1763: + { + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-2].item.(ast.ResultSetNode), Right: yyS[yypt-0].item.(ast.ResultSetNode), StraightJoin: true} + } + case 1764: + { + on := &ast.OnCondition{Expr: yyS[yypt-0].expr} + parser.yyVAL.item = &ast.Join{Left: yyS[yypt-4].item.(ast.ResultSetNode), Right: yyS[yypt-2].item.(ast.ResultSetNode), StraightJoin: true, On: on} + } + case 1765: + { + parser.yyVAL.item = ast.LeftJoin + } + case 1766: + { + parser.yyVAL.item = ast.RightJoin + } + case 1772: + { + parser.yyVAL.item = nil + } + case 1773: + { + parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-0].item.(ast.ValueExpr)} + } + case 1774: + { + parser.yyVAL.item = ast.NewValueExpr(yyS[yypt-0].item, parser.charset, parser.collation) + } + case 1775: + { + parser.yyVAL.item = ast.NewParamMarkerExpr(yyS[yypt].offset) + } + case 1780: + { + parser.yyVAL.item = ast.NewValueExpr(uint64(1), parser.charset, parser.collation) + } + case 1782: + { + parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-0].item.(ast.ExprNode)} + } + case 1783: + { + parser.yyVAL.item = &ast.Limit{Offset: yyS[yypt-2].item.(ast.ExprNode), Count: yyS[yypt-0].item.(ast.ExprNode)} + } + case 1784: + { + parser.yyVAL.item = &ast.Limit{Offset: yyS[yypt-0].item.(ast.ExprNode), Count: yyS[yypt-2].item.(ast.ExprNode)} + } + case 1785: + { + parser.yyVAL.item = &ast.Limit{Count: yyS[yypt-2].item.(ast.ExprNode)} + } + case 1786: + { + parser.yyVAL.item = nil + } + case 1788: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.TableHints = yyS[yypt-0].item.([]*ast.TableOptimizerHint) + parser.yyVAL.item = opt + } + case 1789: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + if yyS[yypt-0].item.(bool) { + opt.Distinct = true + } else { + opt.Distinct = false + opt.ExplicitAll = true + } + parser.yyVAL.item = opt + } + case 1790: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.Priority = yyS[yypt-0].item.(mysql.PriorityEnum) + parser.yyVAL.item = opt + } + case 1791: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.SQLSmallResult = true + parser.yyVAL.item = opt + } + case 1792: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.SQLBigResult = true + parser.yyVAL.item = opt + } + case 1793: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.SQLBufferResult = true + parser.yyVAL.item = opt + } + case 1794: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = yyS[yypt-0].item.(bool) + parser.yyVAL.item = opt + } + case 1795: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.CalcFoundRows = true + parser.yyVAL.item = opt + } + case 1796: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + opt.StraightJoin = true + parser.yyVAL.item = opt + } + case 1797: + { + opt := &ast.SelectStmtOpts{} + opt.SQLCache = true + parser.yyVAL.item = opt + } + case 1799: + { + opts := yyS[yypt-1].item.(*ast.SelectStmtOpts) + opt := yyS[yypt-0].item.(*ast.SelectStmtOpts) + + // Merge options. + // Always use the first hint. + if opt.TableHints != nil && opts.TableHints == nil { + opts.TableHints = opt.TableHints + } + if opt.Distinct { + opts.Distinct = true + } + if opt.Priority != mysql.NoPriority { + opts.Priority = opt.Priority + } + if opt.SQLSmallResult { + opts.SQLSmallResult = true + } + if opt.SQLBigResult { + opts.SQLBigResult = true + } + if opt.SQLBufferResult { + opts.SQLBufferResult = true + } + if !opt.SQLCache { + opts.SQLCache = false + } + if opt.CalcFoundRows { + opts.CalcFoundRows = true + } + if opt.StraightJoin { + opts.StraightJoin = true + } + if opt.ExplicitAll { + opts.ExplicitAll = true + } + + if opts.Distinct && opts.ExplicitAll { + yylex.AppendError(ErrWrongUsage.GenWithStackByArgs("ALL", "DISTINCT")) + return 1 + } + + parser.yyVAL.item = opts + } + case 1801: + { + hints, warns := parser.parseHint(yyS[yypt-0].ident) + for _, w := range warns { + yylex.AppendError(w) + parser.lastErrorAsWarn() + } + parser.yyVAL.item = hints + } + case 1802: + { + parser.yyVAL.item = nil + } + case 1804: + { + parser.yyVAL.item = true + } + case 1805: + { + parser.yyVAL.item = false + } + case 1806: + { + parser.yyVAL.item = &ast.FieldList{Fields: yyS[yypt-0].item.([]*ast.SelectField)} + } + case 1807: + { + parser.yyVAL.item = nil + } + case 1809: + { + parser.yyVAL.item = nil + } + case 1810: + { + x := &ast.SelectIntoOption{ + Tp: ast.SelectIntoOutfile, + FileName: yyS[yypt-2].ident, + } + if yyS[yypt-1].item != nil { + x.FieldsInfo = yyS[yypt-1].item.(*ast.FieldsClause) + } + if yyS[yypt-0].item != nil { + x.LinesInfo = yyS[yypt-0].item.(*ast.LinesClause) + } + + parser.yyVAL.item = x + } + case 1811: + { + rs := yyS[yypt-1].statement.(*ast.SelectStmt) + endOffset := parser.endOffset(&yyS[yypt]) + parser.setLastSelectFieldText(rs, endOffset) + src := parser.src + // See the implementation of yyParse function + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + } + case 1812: + { + rs := yyS[yypt-1].statement.(*ast.SetOprStmt) + src := parser.src + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + } + case 1813: + { + switch rs := yyS[yypt-1].statement.(type) { + case *ast.SelectStmt: + endOffset := parser.endOffset(&yyS[yypt]) + parser.setLastSelectFieldText(rs, endOffset) + src := parser.src + // See the implementation of yyParse function + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + case *ast.SetOprStmt: + src := parser.src + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + } + } + case 1814: + { + subQuery := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query + isRecursive := true + // remove redundant brackets like '((select 1))' + for isRecursive { + if _, isRecursive = subQuery.(*ast.SubqueryExpr); isRecursive { + subQuery = subQuery.(*ast.SubqueryExpr).Query + } + } + switch rs := subQuery.(type) { + case *ast.SelectStmt: + endOffset := parser.endOffset(&yyS[yypt]) + parser.setLastSelectFieldText(rs, endOffset) + src := parser.src + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + case *ast.SetOprStmt: + src := parser.src + rs.SetText(parser.lexer.client, src[yyS[yypt-1].offset:yyS[yypt].offset]) + parser.yyVAL.expr = &ast.SubqueryExpr{Query: rs} + } + } + case 1815: + { + parser.yyVAL.item = nil + } + case 1816: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForUpdate, + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 1817: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForShare, + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 1818: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForUpdateNoWait, + Tables: yyS[yypt-1].item.([]*ast.TableName), + } + } + case 1819: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForUpdateWaitN, + WaitSec: getUint64FromNUM(yyS[yypt-0].item), + Tables: yyS[yypt-2].item.([]*ast.TableName), + } + } + case 1820: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForShareNoWait, + Tables: yyS[yypt-1].item.([]*ast.TableName), + } + } + case 1821: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForUpdateSkipLocked, + Tables: yyS[yypt-2].item.([]*ast.TableName), + } + } + case 1822: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForShareSkipLocked, + Tables: yyS[yypt-2].item.([]*ast.TableName), + } + } + case 1823: + { + parser.yyVAL.item = &ast.SelectLockInfo{ + LockType: ast.SelectLockForShare, + Tables: []*ast.TableName{}, + } + } + case 1824: + { + parser.yyVAL.item = []*ast.TableName{} + } + case 1825: + { + parser.yyVAL.item = yyS[yypt-0].item.([]*ast.TableName) + } + case 1828: + { + setOpr := yyS[yypt-0].statement.(*ast.SetOprStmt) + setOpr.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = setOpr + } + case 1829: + { + setOpr := yyS[yypt-0].statement.(*ast.SetOprStmt) + setOpr.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = setOpr + } + case 1830: + { + setOprList1 := yyS[yypt-2].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-1]) + parser.setLastSelectFieldText(sel, endOffset) + } + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: yyS[yypt-2].item.([]ast.Node)}} + st := yyS[yypt-0].statement.(*ast.SelectStmt) + setOpr.Limit = st.Limit + setOpr.OrderBy = st.OrderBy + st.Limit = nil + st.OrderBy = nil + st.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) + setOpr.SelectList.Selects = append(setOpr.SelectList.Selects, st) + parser.yyVAL.statement = setOpr + } + case 1831: + { + setOprList1 := yyS[yypt-2].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-1]) + parser.setLastSelectFieldText(sel, endOffset) + } + var setOprList2 []ast.Node + var with2 *ast.WithClause + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList2 = []ast.Node{x} + with2 = x.With + case *ast.SetOprStmt: + setOprList2 = x.SelectList.Selects + with2 = x.With + } + nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} + nextSetOprList.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) + setOprList := append(setOprList1, nextSetOprList) + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} + parser.yyVAL.statement = setOpr + } + case 1832: + { + setOprList1 := yyS[yypt-3].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-2]) + parser.setLastSelectFieldText(sel, endOffset) + } + var setOprList2 []ast.Node + var with2 *ast.WithClause + switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList2 = []ast.Node{x} + with2 = x.With + case *ast.SetOprStmt: + setOprList2 = x.SelectList.Selects + with2 = x.With + } + nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} + nextSetOprList.AfterSetOperator = yyS[yypt-2].item.(*ast.SetOprType) + setOprList := append(setOprList1, nextSetOprList) + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} + setOpr.OrderBy = yyS[yypt-0].item.(*ast.OrderByClause) + parser.yyVAL.statement = setOpr + } + case 1833: + { + setOprList1 := yyS[yypt-3].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-2]) + parser.setLastSelectFieldText(sel, endOffset) + } + var setOprList2 []ast.Node + var with2 *ast.WithClause + switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList2 = []ast.Node{x} + with2 = x.With + case *ast.SetOprStmt: + setOprList2 = x.SelectList.Selects + with2 = x.With + } + nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} + nextSetOprList.AfterSetOperator = yyS[yypt-2].item.(*ast.SetOprType) + setOprList := append(setOprList1, nextSetOprList) + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} + setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) + parser.yyVAL.statement = setOpr + } + case 1834: + { + setOprList1 := yyS[yypt-4].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-3]) + parser.setLastSelectFieldText(sel, endOffset) + } + var setOprList2 []ast.Node + var with2 *ast.WithClause + switch x := yyS[yypt-2].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList2 = []ast.Node{x} + with2 = x.With + case *ast.SetOprStmt: + setOprList2 = x.SelectList.Selects + with2 = x.With + } + nextSetOprList := &ast.SetOprSelectList{Selects: setOprList2, With: with2} + nextSetOprList.AfterSetOperator = yyS[yypt-3].item.(*ast.SetOprType) + setOprList := append(setOprList1, nextSetOprList) + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}} + setOpr.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) + setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) + parser.yyVAL.statement = setOpr + } + case 1835: + { + var setOprList []ast.Node + var with *ast.WithClause + switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList = []ast.Node{x} + with = x.With + case *ast.SetOprStmt: + setOprList = x.SelectList.Selects + with = x.With + } + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} + setOpr.OrderBy = yyS[yypt-0].item.(*ast.OrderByClause) + parser.yyVAL.statement = setOpr + } + case 1836: + { + var setOprList []ast.Node + var with *ast.WithClause + switch x := yyS[yypt-1].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList = []ast.Node{x} + with = x.With + case *ast.SetOprStmt: + setOprList = x.SelectList.Selects + with = x.With + } + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} + setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) + parser.yyVAL.statement = setOpr + } + case 1837: + { + var setOprList []ast.Node + var with *ast.WithClause + switch x := yyS[yypt-2].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList = []ast.Node{x} + with = x.With + case *ast.SetOprStmt: + setOprList = x.SelectList.Selects + with = x.With + } + setOpr := &ast.SetOprStmt{SelectList: &ast.SetOprSelectList{Selects: setOprList}, With: with} + setOpr.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) + setOpr.Limit = yyS[yypt-0].item.(*ast.Limit) + parser.yyVAL.statement = setOpr + } + case 1839: + { + setOprList1 := yyS[yypt-2].item.([]ast.Node) + setOprList2 := yyS[yypt-0].item.([]ast.Node) + if sel, isSelect := setOprList1[len(setOprList1)-1].(*ast.SelectStmt); isSelect && !sel.IsInBraces { + endOffset := parser.endOffset(&yyS[yypt-1]) + parser.setLastSelectFieldText(sel, endOffset) + } + switch x := setOprList2[0].(type) { + case *ast.SelectStmt: + x.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) + case *ast.SetOprSelectList: + x.AfterSetOperator = yyS[yypt-1].item.(*ast.SetOprType) + } + parser.yyVAL.item = append(setOprList1, setOprList2...) + } + case 1840: + { + parser.yyVAL.item = []ast.Node{yyS[yypt-0].statement.(*ast.SelectStmt)} + } + case 1841: + { + var setOprList []ast.Node + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + setOprList = []ast.Node{&ast.SetOprSelectList{Selects: []ast.Node{x}}} + case *ast.SetOprStmt: + setOprList = []ast.Node{&ast.SetOprSelectList{Selects: x.SelectList.Selects, With: x.With}} + } + parser.yyVAL.item = setOprList + } + case 1842: + { + var tp ast.SetOprType + tp = ast.Union + if yyS[yypt-0].item == false { + tp = ast.UnionAll + } + parser.yyVAL.item = &tp + } + case 1843: + { + var tp ast.SetOprType + tp = ast.Except + if yyS[yypt-0].item == false { + tp = ast.ExceptAll + } + parser.yyVAL.item = &tp + } + case 1844: + { + var tp ast.SetOprType + tp = ast.Intersect + if yyS[yypt-0].item == false { + tp = ast.IntersectAll + } + parser.yyVAL.item = &tp + } + case 1846: + { + parser.yyVAL.statement = &ast.ChangeStmt{ + NodeType: ast.PumpType, + State: yyS[yypt-3].ident, + NodeID: yyS[yypt-0].ident, + } + } + case 1847: + { + parser.yyVAL.statement = &ast.ChangeStmt{ + NodeType: ast.DrainerType, + State: yyS[yypt-3].ident, + NodeID: yyS[yypt-0].ident, + } + } + case 1848: + { + parser.yyVAL.statement = &ast.SetStmt{Variables: yyS[yypt-0].item.([]*ast.VariableAssignment)} + } + case 1849: + { + parser.yyVAL.statement = &ast.SetPwdStmt{Password: yyS[yypt-0].ident} + } + case 1850: + { + parser.yyVAL.statement = &ast.SetPwdStmt{User: yyS[yypt-2].item.(*auth.UserIdentity), Password: yyS[yypt-0].ident} + } + case 1851: + { + vars := yyS[yypt-0].item.([]*ast.VariableAssignment) + for _, v := range vars { + v.IsGlobal = true + } + parser.yyVAL.statement = &ast.SetStmt{Variables: vars} + } + case 1852: + { + parser.yyVAL.statement = &ast.SetStmt{Variables: yyS[yypt-0].item.([]*ast.VariableAssignment)} + } + case 1853: + { + assigns := yyS[yypt-0].item.([]*ast.VariableAssignment) + for i := 0; i < len(assigns); i++ { + if assigns[i].Name == "tx_isolation" { + // A special session variable that make setting tx_isolation take effect one time. + assigns[i].Name = "tx_isolation_one_shot" + } + } + parser.yyVAL.statement = &ast.SetStmt{Variables: assigns} + } + case 1854: + { + parser.yyVAL.statement = &ast.SetConfigStmt{Type: strings.ToLower(yyS[yypt-3].ident), Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr} + } + case 1855: + { + parser.yyVAL.statement = &ast.SetConfigStmt{Instance: yyS[yypt-3].ident, Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr} + } + case 1856: + { + parser.yyVAL.statement = &ast.SetSessionStatesStmt{SessionStates: yyS[yypt-0].ident} + } + case 1857: + { + parser.yyVAL.statement = &ast.SetResourceGroupStmt{Name: model.NewCIStr(yyS[yypt-0].ident)} + } + case 1858: + { + parser.yyVAL.statement = yyS[yypt-0].item.(*ast.SetRoleStmt) + } + case 1859: + { + tmp := yyS[yypt-2].item.(*ast.SetRoleStmt) + parser.yyVAL.statement = &ast.SetDefaultRoleStmt{ + SetRoleOpt: tmp.SetRoleOpt, + RoleList: tmp.RoleList, + UserList: yyS[yypt-0].item.([]*auth.UserIdentity), + } + } + case 1860: + { + parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleNone, RoleList: nil} + } + case 1861: + { + parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleAll, RoleList: nil} + } + case 1862: + { + parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleRegular, RoleList: yyS[yypt-0].item.([]*auth.RoleIdentity)} + } + case 1863: + { + parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleAllExcept, RoleList: yyS[yypt-0].item.([]*auth.RoleIdentity)} + } + case 1865: + { + parser.yyVAL.item = &ast.SetRoleStmt{SetRoleOpt: ast.SetRoleDefault, RoleList: nil} + } + case 1866: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.item = yyS[yypt-0].item + } else { + parser.yyVAL.item = []*ast.VariableAssignment{} + } + } + case 1867: + { + if yyS[yypt-0].item != nil { + varAssigns := yyS[yypt-0].item.([]*ast.VariableAssignment) + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.VariableAssignment), varAssigns...) + } else { + parser.yyVAL.item = yyS[yypt-2].item + } + } + case 1868: + { + varAssigns := []*ast.VariableAssignment{} + expr := ast.NewValueExpr(yyS[yypt-0].ident, parser.charset, parser.collation) + varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_isolation", Value: expr, IsSystem: true}) + parser.yyVAL.item = varAssigns + } + case 1869: + { + varAssigns := []*ast.VariableAssignment{} + expr := ast.NewValueExpr("0", parser.charset, parser.collation) + varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_only", Value: expr, IsSystem: true}) + parser.yyVAL.item = varAssigns + } + case 1870: + { + varAssigns := []*ast.VariableAssignment{} + expr := ast.NewValueExpr("1", parser.charset, parser.collation) + varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_only", Value: expr, IsSystem: true}) + parser.yyVAL.item = varAssigns + } + case 1871: + { + varAssigns := []*ast.VariableAssignment{} + asof := yyS[yypt-0].item.(*ast.AsOfClause) + if asof != nil { + varAssigns = append(varAssigns, &ast.VariableAssignment{Name: "tx_read_ts", Value: asof.TsExpr, IsSystem: true}) + } + parser.yyVAL.item = varAssigns + } + case 1872: + { + parser.yyVAL.ident = ast.RepeatableRead + } + case 1873: + { + parser.yyVAL.ident = ast.ReadCommitted + } + case 1874: + { + parser.yyVAL.ident = ast.ReadUncommitted + } + case 1875: + { + parser.yyVAL.ident = ast.Serializable + } + case 1876: + { + parser.yyVAL.expr = ast.NewValueExpr("ON", parser.charset, parser.collation) + } + case 1877: + { + parser.yyVAL.expr = ast.NewValueExpr("BINARY", parser.charset, parser.collation) + } + case 1882: + { + parser.yyVAL.ident = yyS[yypt-2].ident + "." + yyS[yypt-0].ident + } + case 1884: + { + parser.yyVAL.ident = yyS[yypt-2].ident + "." + yyS[yypt-0].ident + } + case 1885: + { + parser.yyVAL.ident = yyS[yypt-2].ident + "-" + yyS[yypt-0].ident + } + case 1886: + { + parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} + } + case 1887: + { + parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsGlobal: true, IsSystem: true} + } + case 1888: + { + parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} + } + case 1889: + { + parser.yyVAL.item = &ast.VariableAssignment{Name: yyS[yypt-2].ident, Value: yyS[yypt-0].expr, IsSystem: true} + } + case 1890: + { + v := strings.ToLower(yyS[yypt-2].ident) + var isGlobal bool + if strings.HasPrefix(v, "@@global.") { + isGlobal = true + v = strings.TrimPrefix(v, "@@global.") + } else if strings.HasPrefix(v, "@@session.") { + v = strings.TrimPrefix(v, "@@session.") + } else if strings.HasPrefix(v, "@@local.") { + v = strings.TrimPrefix(v, "@@local.") + } else if strings.HasPrefix(v, "@@") { + v = strings.TrimPrefix(v, "@@") + } + parser.yyVAL.item = &ast.VariableAssignment{Name: v, Value: yyS[yypt-0].expr, IsGlobal: isGlobal, IsSystem: true} + } + case 1891: + { + v := yyS[yypt-2].ident + v = strings.TrimPrefix(v, "@") + parser.yyVAL.item = &ast.VariableAssignment{Name: v, Value: yyS[yypt-0].expr} + } + case 1892: + { + parser.yyVAL.item = &ast.VariableAssignment{ + Name: ast.SetNames, + Value: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), + } + } + case 1893: + { + parser.yyVAL.item = &ast.VariableAssignment{ + Name: ast.SetNames, + Value: ast.NewValueExpr(yyS[yypt-2].ident, "", ""), + } + } + case 1894: + { + parser.yyVAL.item = &ast.VariableAssignment{ + Name: ast.SetNames, + Value: ast.NewValueExpr(yyS[yypt-2].ident, "", ""), + ExtendValue: ast.NewValueExpr(yyS[yypt-0].ident, "", ""), + } + } + case 1895: + { + v := &ast.DefaultExpr{} + parser.yyVAL.item = &ast.VariableAssignment{Name: ast.SetNames, Value: v} + } + case 1896: + { + parser.yyVAL.item = &ast.VariableAssignment{Name: ast.SetCharset, Value: yyS[yypt-0].expr} + } + case 1897: + { + parser.yyVAL.expr = ast.NewValueExpr(yyS[yypt-0].ident, "", "") + } + case 1898: + { + parser.yyVAL.expr = &ast.DefaultExpr{} + } + case 1899: + { + // Validate input charset name to keep the same behavior as parser of MySQL. + cs, err := charset.GetCharsetInfo(yyS[yypt-0].ident) + if err != nil { + yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs(yyS[yypt-0].ident)) + return 1 + } + // Use charset name returned from charset.GetCharsetInfo(), + // to keep lower case of input for generated column restore. + parser.yyVAL.ident = cs.Name + } + case 1900: + { + parser.yyVAL.ident = charset.CharsetBin + } + case 1901: + { + info, err := charset.GetCollationByName(yyS[yypt-0].ident) + if err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.ident = info.Name + } + case 1902: + { + parser.yyVAL.ident = charset.CollationBin + } + case 1903: + { + parser.yyVAL.item = []*ast.VariableAssignment{yyS[yypt-0].item.(*ast.VariableAssignment)} + } + case 1904: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.VariableAssignment), yyS[yypt-0].item.(*ast.VariableAssignment)) + } + case 1907: + { + v := strings.ToLower(yyS[yypt-0].ident) + var isGlobal bool + explicitScope := true + if strings.HasPrefix(v, "@@global.") { + isGlobal = true + v = strings.TrimPrefix(v, "@@global.") + } else if strings.HasPrefix(v, "@@session.") { + v = strings.TrimPrefix(v, "@@session.") + } else if strings.HasPrefix(v, "@@local.") { + v = strings.TrimPrefix(v, "@@local.") + } else if strings.HasPrefix(v, "@@") { + v, explicitScope = strings.TrimPrefix(v, "@@"), false + } + parser.yyVAL.expr = &ast.VariableExpr{Name: v, IsGlobal: isGlobal, IsSystem: true, ExplicitScope: explicitScope} + } + case 1908: + { + v := yyS[yypt-0].ident + v = strings.TrimPrefix(v, "@") + parser.yyVAL.expr = &ast.VariableExpr{Name: v, IsGlobal: false, IsSystem: false} + } + case 1909: + { + parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} + } + case 1910: + { + parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-2].ident, Hostname: strings.ToLower(yyS[yypt-0].ident)} + } + case 1911: + { + parser.yyVAL.item = &auth.UserIdentity{Username: yyS[yypt-1].ident, Hostname: strings.ToLower(strings.TrimPrefix(yyS[yypt-0].ident, "@"))} + } + case 1912: + { + parser.yyVAL.item = &auth.UserIdentity{CurrentUser: true} + } + case 1913: + { + parser.yyVAL.item = []*auth.UserIdentity{yyS[yypt-0].item.(*auth.UserIdentity)} + } + case 1914: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*auth.UserIdentity), yyS[yypt-0].item.(*auth.UserIdentity)) + } + case 1916: + { + parser.yyVAL.ident = yyS[yypt-1].ident + } + case 1920: + { + parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-2].ident, Hostname: strings.ToLower(yyS[yypt-0].ident)} + } + case 1921: + { + parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-1].ident, Hostname: strings.ToLower(strings.TrimPrefix(yyS[yypt-0].ident, "@"))} + } + case 1922: + { + parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} + } + case 1923: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 1924: + { + parser.yyVAL.item = &auth.RoleIdentity{Username: yyS[yypt-0].ident, Hostname: "%"} + } + case 1925: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 1926: + { + parser.yyVAL.item = []*auth.RoleIdentity{yyS[yypt-0].item.(*auth.RoleIdentity)} + } + case 1927: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*auth.RoleIdentity), yyS[yypt-0].item.(*auth.RoleIdentity)) + } + case 1928: + { + parser.yyVAL.item = &ast.LimitSimple{Offset: 0, Count: yyS[yypt-0].item.(uint64)} + } + case 1929: + { + parser.yyVAL.item = &ast.LimitSimple{Offset: yyS[yypt-2].item.(uint64), Count: yyS[yypt-0].item.(uint64)} + } + case 1930: + { + parser.yyVAL.item = &ast.LimitSimple{Offset: yyS[yypt-0].item.(uint64), Count: yyS[yypt-2].item.(uint64)} + } + case 1931: + { + parser.yyVAL.statement = &ast.AdminStmt{Tp: ast.AdminShowDDL} + } + case 1932: + { + stmt := &ast.AdminStmt{Tp: ast.AdminShowDDLJobs} + if yyS[yypt-0].item != nil { + stmt.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = stmt + } + case 1933: + { + stmt := &ast.AdminStmt{ + Tp: ast.AdminShowDDLJobs, + JobNumber: yyS[yypt-1].item.(int64), + } + if yyS[yypt-0].item != nil { + stmt.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = stmt + } + case 1934: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminShowNextRowID, + Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, + } + } + case 1935: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCheckTable, + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 1936: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCheckIndex, + Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, + Index: string(yyS[yypt-0].ident), + } + } + case 1937: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminRecoverIndex, + Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, + Index: string(yyS[yypt-0].ident), + } + } + case 1938: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCleanupIndex, + Tables: []*ast.TableName{yyS[yypt-1].item.(*ast.TableName)}, + Index: string(yyS[yypt-0].ident), + } + } + case 1939: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCheckIndexRange, + Tables: []*ast.TableName{yyS[yypt-2].item.(*ast.TableName)}, + Index: string(yyS[yypt-1].ident), + HandleRanges: yyS[yypt-0].item.([]ast.HandleRange), + } + } + case 1940: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminChecksumTable, + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 1941: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCancelDDLJobs, + JobIDs: yyS[yypt-0].item.([]int64), + } + } + case 1942: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminPauseDDLJobs, + JobIDs: yyS[yypt-0].item.([]int64), + } + } + case 1943: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminResumeDDLJobs, + JobIDs: yyS[yypt-0].item.([]int64), + } + } + case 1944: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminShowDDLJobQueries, + JobIDs: yyS[yypt-0].item.([]int64), + } + } + case 1945: + { + ret := &ast.AdminStmt{ + Tp: ast.AdminShowDDLJobQueriesWithRange, + } + ret.LimitSimple.Count = yyS[yypt-0].item.(*ast.LimitSimple).Count + ret.LimitSimple.Offset = yyS[yypt-0].item.(*ast.LimitSimple).Offset + parser.yyVAL.statement = ret + } + case 1946: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminShowSlow, + ShowSlow: yyS[yypt-0].item.(*ast.ShowSlow), + } + } + case 1947: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminReloadExprPushdownBlacklist, + } + } + case 1948: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminReloadOptRuleBlacklist, + } + } + case 1949: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminPluginEnable, + Plugins: yyS[yypt-0].item.([]string), + } + } + case 1950: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminPluginDisable, + Plugins: yyS[yypt-0].item.([]string), + } + } + case 1951: + { + parser.yyVAL.statement = &ast.CleanupTableLockStmt{ + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 1952: + { + parser.yyVAL.statement = &ast.RepairTableStmt{ + Table: yyS[yypt-1].item.(*ast.TableName), + CreateStmt: yyS[yypt-0].statement.(*ast.CreateTableStmt), + } + } + case 1953: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminFlushBindings, + } + } + case 1954: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminCaptureBindings, + } + } + case 1955: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminEvolveBindings, + } + } + case 1956: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminReloadBindings, + } + } + case 1957: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminReloadStatistics, + } + } + case 1958: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminReloadStatistics, + } + } + case 1959: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminShowTelemetry, + } + } + case 1960: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminResetTelemetryID, + } + } + case 1961: + { + parser.yyVAL.statement = &ast.AdminStmt{ + Tp: ast.AdminFlushPlanCache, + StatementScope: yyS[yypt-1].item.(ast.StatementScope), + } + } + case 1962: + { + parser.yyVAL.item = &ast.ShowSlow{ + Tp: ast.ShowSlowRecent, + Count: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 1963: + { + parser.yyVAL.item = &ast.ShowSlow{ + Tp: ast.ShowSlowTop, + Kind: ast.ShowSlowKindDefault, + Count: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 1964: + { + parser.yyVAL.item = &ast.ShowSlow{ + Tp: ast.ShowSlowTop, + Kind: ast.ShowSlowKindInternal, + Count: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 1965: + { + parser.yyVAL.item = &ast.ShowSlow{ + Tp: ast.ShowSlowTop, + Kind: ast.ShowSlowKindAll, + Count: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 1966: + { + parser.yyVAL.item = []ast.HandleRange{yyS[yypt-0].item.(ast.HandleRange)} + } + case 1967: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.HandleRange), yyS[yypt-0].item.(ast.HandleRange)) + } + case 1968: + { + parser.yyVAL.item = ast.HandleRange{Begin: yyS[yypt-3].item.(int64), End: yyS[yypt-1].item.(int64)} + } + case 1969: + { + parser.yyVAL.item = []int64{yyS[yypt-0].item.(int64)} + } + case 1970: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]int64), yyS[yypt-0].item.(int64)) + } + case 1971: + { + stmt := yyS[yypt-1].item.(*ast.ShowStmt) + if yyS[yypt-0].item != nil { + if x, ok := yyS[yypt-0].item.(*ast.PatternLikeOrIlikeExpr); ok && x.Expr == nil { + stmt.Pattern = x + } else { + stmt.Where = yyS[yypt-0].item.(ast.ExprNode) + } + } + parser.yyVAL.statement = stmt + } + case 1972: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateTable, + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 1973: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateView, + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 1974: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateDatabase, + IfNotExists: yyS[yypt-1].item.(bool), + DBName: yyS[yypt-0].ident, + } + } + case 1975: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateSequence, + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 1976: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreatePlacementPolicy, + DBName: yyS[yypt-0].ident, + } + } + case 1977: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateResourceGroup, + ResourceGroupName: yyS[yypt-0].ident, + } + } + case 1978: + { + // See https://dev.mysql.com/doc/refman/5.7/en/show-create-user.html + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateUser, + User: yyS[yypt-0].item.(*auth.UserIdentity), + } + } + case 1979: + { + stmt := &ast.ShowStmt{ + Tp: ast.ShowRegions, + Table: yyS[yypt-3].item.(*ast.TableName), + } + stmt.Table.PartitionNames = yyS[yypt-2].item.([]model.CIStr) + if yyS[yypt-0].item != nil { + stmt.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = stmt + } + case 1980: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowTableNextRowId, + Table: yyS[yypt-1].item.(*ast.TableName), + } + } + case 1981: + { + stmt := &ast.ShowStmt{ + Tp: ast.ShowRegions, + Table: yyS[yypt-5].item.(*ast.TableName), + IndexName: model.NewCIStr(yyS[yypt-2].ident), + } + stmt.Table.PartitionNames = yyS[yypt-4].item.([]model.CIStr) + if yyS[yypt-0].item != nil { + stmt.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = stmt + } + case 1982: + { + // See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html + parser.yyVAL.statement = &ast.ShowStmt{Tp: ast.ShowGrants} + } + case 1983: + { + // See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html + if yyS[yypt-0].item != nil { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowGrants, + User: yyS[yypt-1].item.(*auth.UserIdentity), + Roles: yyS[yypt-0].item.([]*auth.RoleIdentity), + } + } else { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowGrants, + User: yyS[yypt-1].item.(*auth.UserIdentity), + Roles: nil, + } + } + } + case 1984: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowMasterStatus, + } + } + case 1985: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowProcessList, + Full: yyS[yypt-1].item.(bool), + } + } + case 1986: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowProfiles, + } + } + case 1987: + { + v := &ast.ShowStmt{ + Tp: ast.ShowProfile, + } + if yyS[yypt-2].item != nil { + v.ShowProfileTypes = yyS[yypt-2].item.([]int) + } + if yyS[yypt-1].item != nil { + v.ShowProfileArgs = yyS[yypt-1].item.(*int64) + } + if yyS[yypt-0].item != nil { + v.ShowProfileLimit = yyS[yypt-0].item.(*ast.Limit) + } + parser.yyVAL.statement = v + } + case 1988: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowPrivileges, + } + } + case 1989: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowBuiltins, + } + } + case 1990: + { + parser.yyVAL.statement = yyS[yypt-0].item.(*ast.ShowStmt) + } + case 1991: + { + v := yyS[yypt-0].item.(int64) + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowImportJobs, + ImportJobID: &v, + } + } + case 1992: + { + parser.yyVAL.statement = &ast.ShowStmt{ + Tp: ast.ShowCreateProcedure, + Procedure: yyS[yypt-0].item.(*ast.TableName), + } + } + case 1993: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowPlacementForDatabase, + DBName: yyS[yypt-0].ident, + } + } + case 1994: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowPlacementForTable, + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 1995: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowPlacementForPartition, + Table: yyS[yypt-2].item.(*ast.TableName), + Partition: model.NewCIStr(yyS[yypt-0].ident), + } + } + case 1996: + { + parser.yyVAL.item = nil + } + case 1998: + { + parser.yyVAL.item = []int{yyS[yypt-0].item.(int)} + } + case 1999: + { + l := yyS[yypt-2].item.([]int) + l = append(l, yyS[yypt-0].item.(int)) + parser.yyVAL.item = l + } + case 2000: + { + parser.yyVAL.item = ast.ProfileTypeCPU + } + case 2001: + { + parser.yyVAL.item = ast.ProfileTypeMemory + } + case 2002: + { + parser.yyVAL.item = ast.ProfileTypeBlockIo + } + case 2003: + { + parser.yyVAL.item = ast.ProfileTypeContextSwitch + } + case 2004: + { + parser.yyVAL.item = ast.ProfileTypePageFaults + } + case 2005: + { + parser.yyVAL.item = ast.ProfileTypeIpc + } + case 2006: + { + parser.yyVAL.item = ast.ProfileTypeSwaps + } + case 2007: + { + parser.yyVAL.item = ast.ProfileTypeSource + } + case 2008: + { + parser.yyVAL.item = ast.ProfileTypeAll + } + case 2009: + { + parser.yyVAL.item = nil + } + case 2010: + { + v := yyS[yypt-0].item.(int64) + parser.yyVAL.item = &v + } + case 2011: + { + parser.yyVAL.item = nil + } + case 2012: + { + parser.yyVAL.item = yyS[yypt-0].item.([]*auth.RoleIdentity) + } + case 2018: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowEngines} + } + case 2019: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowDatabases} + } + case 2020: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowConfig} + } + case 2021: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowCharset} + } + case 2022: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowTables, + DBName: yyS[yypt-0].ident, + Full: yyS[yypt-2].item.(bool), + } + } + case 2023: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowOpenTables, + DBName: yyS[yypt-0].ident, + } + } + case 2024: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowTableStatus, + DBName: yyS[yypt-0].ident, + } + } + case 2025: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowIndex, + Table: yyS[yypt-0].item.(*ast.TableName), + } + } + case 2026: + { + show := &ast.ShowStmt{ + Tp: ast.ShowIndex, + Table: &ast.TableName{Name: model.NewCIStr(yyS[yypt-2].ident), Schema: model.NewCIStr(yyS[yypt-0].ident)}, + } + parser.yyVAL.item = show + } + case 2027: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowColumns, + Table: yyS[yypt-1].item.(*ast.TableName), + DBName: yyS[yypt-0].ident, + Full: yyS[yypt-3].item.(bool), + } + } + case 2028: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowColumns, + Table: yyS[yypt-1].item.(*ast.TableName), + DBName: yyS[yypt-0].ident, + Full: yyS[yypt-3].item.(bool), + Extended: true, + } + } + case 2029: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowWarnings, CountWarningsOrErrors: true} + } + case 2030: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowWarnings} + } + case 2031: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowErrors, CountWarningsOrErrors: true} + } + case 2032: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowErrors} + } + case 2033: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowVariables, + GlobalScope: yyS[yypt-1].item.(bool), + } + } + case 2034: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowStatus, + GlobalScope: yyS[yypt-1].item.(bool), + } + } + case 2035: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowBindings, + GlobalScope: yyS[yypt-1].item.(bool), + } + } + case 2036: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowCollation, + } + } + case 2037: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowTriggers, + DBName: yyS[yypt-0].ident, + } + } + case 2038: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowBindingCacheStatus, + } + } + case 2039: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowProcedureStatus, + } + } + case 2040: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowPumpStatus, + } + } + case 2041: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowDrainerStatus, + } + } + case 2042: + { + // This statement is similar to SHOW PROCEDURE STATUS but for stored functions. + // See http://dev.mysql.com/doc/refman/5.7/en/show-function-status.html + // We do not support neither stored functions nor stored procedures. + // So we reuse show procedure status process logic. + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowFunctionStatus, + } + } + case 2043: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowEvents, + DBName: yyS[yypt-0].ident, + } + } + case 2044: + { + parser.yyVAL.item = &ast.ShowStmt{ + Tp: ast.ShowPlugins, + } + } + case 2045: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowSessionStates} + } + case 2046: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsExtended} + } + case 2047: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsMeta, Table: &ast.TableName{Name: model.NewCIStr("STATS_META"), Schema: model.NewCIStr(mysql.SystemDB)}} + } + case 2048: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsHistograms, Table: &ast.TableName{Name: model.NewCIStr("STATS_HISTOGRAMS"), Schema: model.NewCIStr(mysql.SystemDB)}} + } + case 2049: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsTopN} + } + case 2050: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsBuckets, Table: &ast.TableName{Name: model.NewCIStr("STATS_BUCKETS"), Schema: model.NewCIStr(mysql.SystemDB)}} + } + case 2051: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsHealthy} + } + case 2052: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowStatsLocked, Table: &ast.TableName{Name: model.NewCIStr("STATS_TABLE_LOCKED"), Schema: model.NewCIStr(mysql.SystemDB)}} + } + case 2053: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowHistogramsInFlight} + } + case 2054: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowColumnStatsUsage} + } + case 2055: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowAnalyzeStatus} + } + case 2056: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowBackups} + } + case 2057: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowRestores} + } + case 2058: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowPlacement} + } + case 2059: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowPlacementLabels} + } + case 2060: + { + parser.yyVAL.item = &ast.ShowStmt{Tp: ast.ShowImportJobs} + } + case 2061: + { + parser.yyVAL.item = nil + } + case 2062: + { + parser.yyVAL.item = &ast.PatternLikeOrIlikeExpr{ + Pattern: yyS[yypt-0].expr, + Escape: '\\', + IsLike: true, + } + } + case 2063: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 2064: + { + parser.yyVAL.item = false + } + case 2065: + { + parser.yyVAL.item = true + } + case 2066: + { + parser.yyVAL.item = false + } + case 2067: + { + parser.yyVAL.item = ast.StatementScopeSession + } + case 2068: + { + parser.yyVAL.item = ast.StatementScopeGlobal + } + case 2069: + { + parser.yyVAL.item = ast.StatementScopeInstance + } + case 2070: + { + parser.yyVAL.item = ast.StatementScopeSession + } + case 2071: + { + parser.yyVAL.item = false + } + case 2072: + { + parser.yyVAL.item = true + } + case 2073: + { + parser.yyVAL.ident = "" + } + case 2074: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 2075: + { + parser.yyVAL.item = yyS[yypt-0].item.(*ast.TableName) + } + case 2076: + { + tmp := yyS[yypt-0].item.(*ast.FlushStmt) + tmp.NoWriteToBinLog = yyS[yypt-1].item.(bool) + parser.yyVAL.statement = tmp + } + case 2077: + { + parser.yyVAL.item = []string{yyS[yypt-0].ident} + } + case 2078: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) + } + case 2079: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushPrivileges, + } + } + case 2080: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushStatus, + } + } + case 2081: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushTiDBPlugin, + Plugins: yyS[yypt-0].item.([]string), + } + } + case 2082: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushHosts, + } + } + case 2083: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushLogs, + LogType: yyS[yypt-1].item.(ast.LogType), + } + } + case 2084: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushTables, + Tables: yyS[yypt-1].item.([]*ast.TableName), + ReadLock: yyS[yypt-0].item.(bool), + } + } + case 2085: + { + parser.yyVAL.item = &ast.FlushStmt{ + Tp: ast.FlushClientErrorsSummary, + } + } + case 2086: + { + parser.yyVAL.item = ast.LogTypeDefault + } + case 2087: + { + parser.yyVAL.item = ast.LogTypeBinary + } + case 2088: + { + parser.yyVAL.item = ast.LogTypeEngine + } + case 2089: + { + parser.yyVAL.item = ast.LogTypeError + } + case 2090: + { + parser.yyVAL.item = ast.LogTypeGeneral + } + case 2091: + { + parser.yyVAL.item = ast.LogTypeSlow + } + case 2092: + { + parser.yyVAL.item = false + } + case 2093: + { + parser.yyVAL.item = true + } + case 2094: + { + parser.yyVAL.item = true + } + case 2095: + { + parser.yyVAL.item = []*ast.TableName{} + } + case 2097: + { + parser.yyVAL.item = []*ast.TableName{} + } + case 2098: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2099: + { + parser.yyVAL.item = false + } + case 2100: + { + parser.yyVAL.item = true + } + case 2180: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2208: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2224: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2226: + { + if yyS[yypt-0].statement != nil { + s := yyS[yypt-0].statement + if lexer, ok := yylex.(stmtTexter); ok { + s.SetText(parser.lexer.client, lexer.stmtText()) + } + parser.result = append(parser.result, s) + } + } + case 2227: + { + if yyS[yypt-0].statement != nil { + s := yyS[yypt-0].statement + if lexer, ok := yylex.(stmtTexter); ok { + s.SetText(parser.lexer.client, lexer.stmtText()) + } + parser.result = append(parser.result, s) + } + } + case 2228: + { + cst := yyS[yypt-0].item.(*ast.Constraint) + if yyS[yypt-1].item != nil { + cst.Name = yyS[yypt-1].item.(string) + cst.IsEmptyIndex = len(cst.Name) == 0 + } + parser.yyVAL.item = cst + } + case 2233: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.item = []interface{}{yyS[yypt-0].item.(interface{})} + } else { + parser.yyVAL.item = []interface{}{} + } + } + case 2234: + { + if yyS[yypt-0].item != nil { + parser.yyVAL.item = append(yyS[yypt-2].item.([]interface{}), yyS[yypt-0].item) + } else { + parser.yyVAL.item = yyS[yypt-2].item + } + } + case 2235: + { + var columnDefs []*ast.ColumnDef + var constraints []*ast.Constraint + parser.yyVAL.item = &ast.CreateTableStmt{ + Cols: columnDefs, + Constraints: constraints, + } + } + case 2236: + { + tes := yyS[yypt-1].item.([]interface{}) + var columnDefs []*ast.ColumnDef + var constraints []*ast.Constraint + for _, te := range tes { + switch te := te.(type) { + case *ast.ColumnDef: + columnDefs = append(columnDefs, te) + case *ast.Constraint: + constraints = append(constraints, te) + } + } + parser.yyVAL.item = &ast.CreateTableStmt{ + Cols: columnDefs, + Constraints: constraints, + } + } + case 2238: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCharset, StrValue: yyS[yypt-0].ident, + UintValue: ast.TableOptionCharsetWithoutConvertTo} + } + case 2239: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCollate, StrValue: yyS[yypt-0].ident, + UintValue: ast.TableOptionCharsetWithoutConvertTo} + } + case 2240: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoIncrement, UintValue: yyS[yypt-0].item.(uint64), BoolValue: yyS[yypt-3].item.(bool)} + } + case 2241: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoIdCache, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2242: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAutoRandomBase, UintValue: yyS[yypt-0].item.(uint64), BoolValue: yyS[yypt-3].item.(bool)} + } + case 2243: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionAvgRowLength, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2244: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionConnection, StrValue: yyS[yypt-0].ident} + } + case 2245: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCheckSum, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2246: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionTableCheckSum, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2247: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionPassword, StrValue: yyS[yypt-0].ident} + } + case 2248: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionCompression, StrValue: yyS[yypt-0].ident} + } + case 2249: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionKeyBlockSize, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2250: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionDelayKeyWrite, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2251: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionRowFormat, UintValue: yyS[yypt-0].item.(uint64)} + } + case 2252: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsPersistent} + } + case 2253: + { + n := yyS[yypt-0].item.(uint64) + if n != 0 && n != 1 { + yylex.AppendError(yylex.Errorf("The value of STATS_AUTO_RECALC must be one of [0|1|DEFAULT].")) + return 1 + } + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsAutoRecalc, UintValue: n} + yylex.AppendError(yylex.Errorf("The STATS_AUTO_RECALC is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 2254: + { + parser.yyVAL.item = &ast.TableOption{Tp: ast.TableOptionStatsAutoRecalc, Default: true} + yylex.AppendError(yylex.Errorf("The STATS_AUTO_RECALC is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + } + case 2255: + { + // Parse it but will ignore it. + // In MySQL, STATS_SAMPLE_PAGES=N(Where 0 mysql.MaxFloatPrecisionLength { + tp.SetType(mysql.TypeDouble) + } + tp.SetFlen(types.UnspecifiedLength) + } + tp.SetDecimal(fopt.Decimal) + for _, o := range yyS[yypt-0].item.([]*ast.TypeOpt) { + if o.IsUnsigned { + tp.AddFlag(mysql.UnsignedFlag) + } + if o.IsZerofill { + tp.AddFlag(mysql.ZerofillFlag) + } + } + parser.yyVAL.item = tp + } + case 2308: + { + tp := types.NewFieldType(yyS[yypt-1].item.(byte)) + tp.SetFlen(yyS[yypt-0].item.(int)) + if tp.GetFlen() == types.UnspecifiedLength { + tp.SetFlen(1) + } + parser.yyVAL.item = tp + } + case 2309: + { + parser.yyVAL.item = mysql.TypeTiny + } + case 2310: + { + parser.yyVAL.item = mysql.TypeShort + } + case 2311: + { + parser.yyVAL.item = mysql.TypeInt24 + } + case 2312: + { + parser.yyVAL.item = mysql.TypeInt24 + } + case 2313: + { + parser.yyVAL.item = mysql.TypeLong + } + case 2314: + { + parser.yyVAL.item = mysql.TypeTiny + } + case 2315: + { + parser.yyVAL.item = mysql.TypeShort + } + case 2316: + { + parser.yyVAL.item = mysql.TypeInt24 + } + case 2317: + { + parser.yyVAL.item = mysql.TypeLong + } + case 2318: + { + parser.yyVAL.item = mysql.TypeLonglong + } + case 2319: + { + parser.yyVAL.item = mysql.TypeLong + } + case 2320: + { + parser.yyVAL.item = mysql.TypeLonglong + } + case 2321: + { + parser.yyVAL.item = mysql.TypeTiny + } + case 2322: + { + parser.yyVAL.item = mysql.TypeTiny + } + case 2326: + { + parser.yyVAL.item = mysql.TypeNewDecimal + } + case 2327: + { + parser.yyVAL.item = mysql.TypeNewDecimal + } + case 2328: + { + parser.yyVAL.item = mysql.TypeNewDecimal + } + case 2329: + { + parser.yyVAL.item = mysql.TypeFloat + } + case 2330: + { + if parser.lexer.GetSQLMode().HasRealAsFloatMode() { + parser.yyVAL.item = mysql.TypeFloat + } else { + parser.yyVAL.item = mysql.TypeDouble + } + } + case 2331: + { + parser.yyVAL.item = mysql.TypeDouble + } + case 2332: + { + parser.yyVAL.item = mysql.TypeDouble + } + case 2333: + { + parser.yyVAL.item = mysql.TypeFloat + } + case 2334: + { + parser.yyVAL.item = mysql.TypeDouble + } + case 2335: + { + parser.yyVAL.item = mysql.TypeBit + } + case 2336: + { + tp := types.NewFieldType(mysql.TypeString) + tp.SetFlen(yyS[yypt-1].item.(int)) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2337: + { + tp := types.NewFieldType(mysql.TypeString) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2338: + { + tp := types.NewFieldType(mysql.TypeString) + tp.SetFlen(yyS[yypt-1].item.(int)) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2339: + { + tp := types.NewFieldType(mysql.TypeString) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2340: + { + tp := types.NewFieldType(mysql.TypeVarchar) + tp.SetFlen(yyS[yypt-1].item.(int)) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2341: + { + tp := types.NewFieldType(mysql.TypeVarchar) + tp.SetFlen(yyS[yypt-1].item.(int)) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2342: + { + tp := types.NewFieldType(mysql.TypeString) + tp.SetFlen(yyS[yypt-0].item.(int)) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CharsetBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 2343: + { + tp := types.NewFieldType(mysql.TypeVarchar) + tp.SetFlen(yyS[yypt-0].item.(int)) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CharsetBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 2344: + { + tp := yyS[yypt-0].item.(*types.FieldType) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CharsetBin) + tp.AddFlag(mysql.BinaryFlag) + parser.yyVAL.item = tp + } + case 2345: + { + tp := yyS[yypt-1].item.(*types.FieldType) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { + tp.AddFlag(mysql.BinaryFlag) + tp.SetCollate(charset.CollationBin) + } + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2346: + { + tp := types.NewFieldType(mysql.TypeEnum) + elems := yyS[yypt-2].item.([]*ast.TextString) + opt := yyS[yypt-0].item.(*ast.OptBinary) + tp.SetElems(make([]string, len(elems))) + fieldLen := -1 // enum_flen = max(ele_flen) + for i, e := range elems { + trimmed := strings.TrimRight(e.Value, " ") + tp.SetElemWithIsBinaryLit(i, trimmed, e.IsBinaryLiteral) + if len(trimmed) > fieldLen { + fieldLen = len(trimmed) + } + } + tp.SetFlen(fieldLen) + tp.SetCharset(opt.Charset) + if opt.IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2347: + { + tp := types.NewFieldType(mysql.TypeSet) + elems := yyS[yypt-2].item.([]*ast.TextString) + opt := yyS[yypt-0].item.(*ast.OptBinary) + tp.SetElems(make([]string, len(elems))) + fieldLen := len(elems) - 1 // set_flen = sum(ele_flen) + number_of_ele - 1 + for i, e := range elems { + trimmed := strings.TrimRight(e.Value, " ") + tp.SetElemWithIsBinaryLit(i, trimmed, e.IsBinaryLiteral) + fieldLen += len(trimmed) + } + tp.SetFlen(fieldLen) + tp.SetCharset(opt.Charset) + if opt.IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2348: + { + tp := types.NewFieldType(mysql.TypeJSON) + tp.SetDecimal(0) + tp.SetCharset(charset.CharsetBin) + tp.SetCollate(charset.CollationBin) + parser.yyVAL.item = tp + } + case 2349: + { + tp := types.NewFieldType(mysql.TypeMediumBlob) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { + tp.AddFlag(mysql.BinaryFlag) + tp.SetCollate(charset.CollationBin) + } + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2350: + { + tp := types.NewFieldType(mysql.TypeMediumBlob) + tp.SetCharset(yyS[yypt-0].item.(*ast.OptBinary).Charset) + if yyS[yypt-0].item.(*ast.OptBinary).Charset == charset.CharsetBin { + tp.AddFlag(mysql.BinaryFlag) + tp.SetCollate(charset.CollationBin) + } + if yyS[yypt-0].item.(*ast.OptBinary).IsBinary { + tp.AddFlag(mysql.BinaryFlag) + } + parser.yyVAL.item = tp + } + case 2370: + { + tp := types.NewFieldType(mysql.TypeTinyBlob) + parser.yyVAL.item = tp + } + case 2371: + { + tp := types.NewFieldType(mysql.TypeBlob) + tp.SetFlen(yyS[yypt-0].item.(int)) + parser.yyVAL.item = tp + } + case 2372: + { + tp := types.NewFieldType(mysql.TypeMediumBlob) + parser.yyVAL.item = tp + } + case 2373: + { + tp := types.NewFieldType(mysql.TypeLongBlob) + parser.yyVAL.item = tp + } + case 2374: + { + tp := types.NewFieldType(mysql.TypeMediumBlob) + parser.yyVAL.item = tp + } + case 2375: + { + tp := types.NewFieldType(mysql.TypeTinyBlob) + parser.yyVAL.item = tp + } + case 2376: + { + tp := types.NewFieldType(mysql.TypeBlob) + tp.SetFlen(yyS[yypt-0].item.(int)) + parser.yyVAL.item = tp + } + case 2377: + { + tp := types.NewFieldType(mysql.TypeMediumBlob) + parser.yyVAL.item = tp + } + case 2378: + { + tp := types.NewFieldType(mysql.TypeLongBlob) + parser.yyVAL.item = tp + } + case 2380: + { + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: false, + Charset: charset.CharsetLatin1, + } + } + case 2381: + { + cs, err := charset.GetCharsetInfo("ucs2") + if err != nil { + yylex.AppendError(ErrUnknownCharacterSet.GenWithStackByArgs("ucs2")) + return 1 + } + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: false, + Charset: cs.Name, + } + } + case 2382: + { + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: false, + Charset: charset.CharsetBin, + } + } + case 2383: + { + tp := types.NewFieldType(mysql.TypeDate) + parser.yyVAL.item = tp + } + case 2384: + { + tp := types.NewFieldType(mysql.TypeDatetime) + tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) + tp.SetDecimal(yyS[yypt-0].item.(int)) + if tp.GetDecimal() > 0 { + tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) + } + parser.yyVAL.item = tp + } + case 2385: + { + tp := types.NewFieldType(mysql.TypeTimestamp) + tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) + tp.SetDecimal(yyS[yypt-0].item.(int)) + if tp.GetDecimal() > 0 { + tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) + } + parser.yyVAL.item = tp + } + case 2386: + { + tp := types.NewFieldType(mysql.TypeDuration) + tp.SetFlen(mysql.MaxDurationWidthNoFsp) + tp.SetDecimal(yyS[yypt-0].item.(int)) + if tp.GetDecimal() > 0 { + tp.SetFlen(tp.GetFlen() + 1 + tp.GetDecimal()) + } + parser.yyVAL.item = tp + } + case 2387: + { + tp := types.NewFieldType(mysql.TypeYear) + tp.SetFlen(yyS[yypt-1].item.(int)) + if tp.GetFlen() != types.UnspecifiedLength && tp.GetFlen() != 4 { + yylex.AppendError(ErrInvalidYearColumnLength.GenWithStackByArgs()) + return -1 + } + parser.yyVAL.item = tp + } + case 2388: + { + parser.yyVAL.item = int(yyS[yypt-1].item.(uint64)) + } + case 2389: + { + parser.yyVAL.item = types.UnspecifiedLength + } + case 2391: + { + parser.yyVAL.item = &ast.TypeOpt{IsUnsigned: true} + } + case 2392: + { + parser.yyVAL.item = &ast.TypeOpt{IsUnsigned: false} + } + case 2393: + { + parser.yyVAL.item = &ast.TypeOpt{IsZerofill: true, IsUnsigned: true} + } + case 2394: + { + parser.yyVAL.item = []*ast.TypeOpt{} + } + case 2395: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.TypeOpt), yyS[yypt-0].item.(*ast.TypeOpt)) + } + case 2396: + { + parser.yyVAL.item = &ast.FloatOpt{Flen: types.UnspecifiedLength, Decimal: types.UnspecifiedLength} + } + case 2397: + { + parser.yyVAL.item = &ast.FloatOpt{Flen: yyS[yypt-0].item.(int), Decimal: types.UnspecifiedLength} + } + case 2399: + { + parser.yyVAL.item = &ast.FloatOpt{Flen: int(yyS[yypt-3].item.(uint64)), Decimal: int(yyS[yypt-1].item.(uint64))} + } + case 2400: + { + parser.yyVAL.item = false + } + case 2401: + { + parser.yyVAL.item = true + } + case 2402: + { + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: false, + Charset: "", + } + } + case 2403: + { + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: true, + Charset: yyS[yypt-0].ident, + } + } + case 2404: + { + parser.yyVAL.item = &ast.OptBinary{ + IsBinary: yyS[yypt-0].item.(bool), + Charset: yyS[yypt-1].ident, + } + } + case 2405: + { + parser.yyVAL.ident = "" + } + case 2406: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 2410: + { + parser.yyVAL.ident = "" + } + case 2411: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 2412: + { + parser.yyVAL.item = []string{yyS[yypt-0].ident} + } + case 2413: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]string), yyS[yypt-0].ident) + } + case 2414: + { + parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].ident} + } + case 2415: + { + parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].item.(ast.BinaryLiteral).ToString(), IsBinaryLiteral: true} + } + case 2416: + { + parser.yyVAL.item = &ast.TextString{Value: yyS[yypt-0].item.(ast.BinaryLiteral).ToString(), IsBinaryLiteral: true} + } + case 2417: + { + parser.yyVAL.item = []*ast.TextString{yyS[yypt-0].item.(*ast.TextString)} + } + case 2418: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.TextString), yyS[yypt-0].item.(*ast.TextString)) + } + case 2425: + { + u := yyS[yypt-0].statement.(*ast.UpdateStmt) + u.With = yyS[yypt-1].item.(*ast.WithClause) + parser.yyVAL.statement = u + } + case 2426: + { + var refs *ast.Join + if x, ok := yyS[yypt-5].item.(*ast.Join); ok { + refs = x + } else { + refs = &ast.Join{Left: yyS[yypt-5].item.(ast.ResultSetNode)} + } + st := &ast.UpdateStmt{ + Priority: yyS[yypt-7].item.(mysql.PriorityEnum), + TableRefs: &ast.TableRefsClause{TableRefs: refs}, + List: yyS[yypt-3].item.([]*ast.Assignment), + IgnoreErr: yyS[yypt-6].item.(bool), + } + if yyS[yypt-8].item != nil { + st.TableHints = yyS[yypt-8].item.([]*ast.TableOptimizerHint) + } + if yyS[yypt-2].item != nil { + st.Where = yyS[yypt-2].item.(ast.ExprNode) + } + if yyS[yypt-1].item != nil { + st.Order = yyS[yypt-1].item.(*ast.OrderByClause) + } + if yyS[yypt-0].item != nil { + st.Limit = yyS[yypt-0].item.(*ast.Limit) + } + parser.yyVAL.statement = st + } + case 2427: + { + st := &ast.UpdateStmt{ + Priority: yyS[yypt-5].item.(mysql.PriorityEnum), + TableRefs: &ast.TableRefsClause{TableRefs: yyS[yypt-3].item.(*ast.Join)}, + List: yyS[yypt-1].item.([]*ast.Assignment), + IgnoreErr: yyS[yypt-4].item.(bool), + } + if yyS[yypt-6].item != nil { + st.TableHints = yyS[yypt-6].item.([]*ast.TableOptimizerHint) + } + if yyS[yypt-0].item != nil { + st.Where = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.statement = st + } + case 2428: + { + parser.yyVAL.statement = &ast.UseStmt{DBName: yyS[yypt-0].ident} + } + case 2429: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 2430: + { + parser.yyVAL.item = nil + } + case 2434: + { + // See https://dev.mysql.com/doc/refman/8.0/en/create-user.html + ret := &ast.CreateUserStmt{ + IsCreateRole: false, + IfNotExists: yyS[yypt-6].item.(bool), + Specs: yyS[yypt-5].item.([]*ast.UserSpec), + AuthTokenOrTLSOptions: yyS[yypt-4].item.([]*ast.AuthTokenOrTLSOption), + ResourceOptions: yyS[yypt-3].item.([]*ast.ResourceOption), + PasswordOrLockOptions: yyS[yypt-2].item.([]*ast.PasswordOrLockOption), + } + if yyS[yypt-1].item != nil { + ret.CommentOrAttributeOption = yyS[yypt-1].item.(*ast.CommentOrAttributeOption) + } + if yyS[yypt-0].item != nil { + ret.ResourceGroupNameOption = yyS[yypt-0].item.(*ast.ResourceGroupNameOption) + } + parser.yyVAL.statement = ret + } + case 2435: + { + // See https://dev.mysql.com/doc/refman/8.0/en/create-role.html + parser.yyVAL.statement = &ast.CreateUserStmt{ + IsCreateRole: true, + IfNotExists: yyS[yypt-1].item.(bool), + Specs: yyS[yypt-0].item.([]*ast.UserSpec), + } + } + case 2436: + { + ret := &ast.AlterUserStmt{ + IfExists: yyS[yypt-6].item.(bool), + Specs: yyS[yypt-5].item.([]*ast.UserSpec), + AuthTokenOrTLSOptions: yyS[yypt-4].item.([]*ast.AuthTokenOrTLSOption), + ResourceOptions: yyS[yypt-3].item.([]*ast.ResourceOption), + PasswordOrLockOptions: yyS[yypt-2].item.([]*ast.PasswordOrLockOption), + } + if yyS[yypt-1].item != nil { + ret.CommentOrAttributeOption = yyS[yypt-1].item.(*ast.CommentOrAttributeOption) + } + if yyS[yypt-0].item != nil { + ret.ResourceGroupNameOption = yyS[yypt-0].item.(*ast.ResourceGroupNameOption) + } + parser.yyVAL.statement = ret + } + case 2437: + { + auth := &ast.AuthOption{ + AuthString: yyS[yypt-0].ident, + ByAuthString: true, + } + parser.yyVAL.statement = &ast.AlterUserStmt{ + IfExists: yyS[yypt-6].item.(bool), + CurrentAuth: auth, + } + } + case 2438: + { + parser.yyVAL.statement = yyS[yypt-0].item.(*ast.AlterInstanceStmt) + } + case 2439: + { + option := yyS[yypt-0].item.(*ast.PlacementOption) + parser.yyVAL.statement = &ast.AlterRangeStmt{RangeName: model.NewCIStr(yyS[yypt-1].ident), PlacementOption: option} + } + case 2440: + { + parser.yyVAL.item = &ast.AlterInstanceStmt{ + ReloadTLS: true, + } + } + case 2441: + { + parser.yyVAL.item = &ast.AlterInstanceStmt{ + ReloadTLS: true, + NoRollbackOnError: true, + } + } + case 2442: + { + userSpec := &ast.UserSpec{ + User: yyS[yypt-1].item.(*auth.UserIdentity), + } + if yyS[yypt-0].item != nil { + userSpec.AuthOpt = yyS[yypt-0].item.(*ast.AuthOption) + } + parser.yyVAL.item = userSpec + } + case 2443: + { + parser.yyVAL.item = []*ast.UserSpec{yyS[yypt-0].item.(*ast.UserSpec)} + } + case 2444: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserSpec), yyS[yypt-0].item.(*ast.UserSpec)) + } + case 2445: + { + l := []*ast.ResourceOption{} + parser.yyVAL.item = l + } + case 2446: + { + parser.yyVAL.item = yyS[yypt-0].item + yylex.AppendError(yylex.Errorf("TiDB does not support WITH ConnectionOptions now, they would be parsed but ignored.")) + parser.lastErrorAsWarn() + } + case 2447: + { + parser.yyVAL.item = []*ast.ResourceOption{yyS[yypt-0].item.(*ast.ResourceOption)} + } + case 2448: + { + l := yyS[yypt-1].item.([]*ast.ResourceOption) + l = append(l, yyS[yypt-0].item.(*ast.ResourceOption)) + parser.yyVAL.item = l + } + case 2449: + { + parser.yyVAL.item = &ast.ResourceOption{ + Type: ast.MaxQueriesPerHour, + Count: yyS[yypt-0].item.(int64), + } + } + case 2450: + { + parser.yyVAL.item = &ast.ResourceOption{ + Type: ast.MaxUpdatesPerHour, + Count: yyS[yypt-0].item.(int64), + } + } + case 2451: + { + parser.yyVAL.item = &ast.ResourceOption{ + Type: ast.MaxConnectionsPerHour, + Count: yyS[yypt-0].item.(int64), + } + } + case 2452: + { + parser.yyVAL.item = &ast.ResourceOption{ + Type: ast.MaxUserConnections, + Count: yyS[yypt-0].item.(int64), + } + } + case 2453: + { + parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{} + } + case 2455: + { + t := &ast.AuthTokenOrTLSOption{ + Type: ast.TlsNone, + } + parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} + } + case 2456: + { + t := &ast.AuthTokenOrTLSOption{ + Type: ast.Ssl, + } + parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} + } + case 2457: + { + t := &ast.AuthTokenOrTLSOption{ + Type: ast.X509, + } + parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{t} + } + case 2458: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2459: + { + parser.yyVAL.item = []*ast.AuthTokenOrTLSOption{yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)} + } + case 2460: + { + l := yyS[yypt-2].item.([]*ast.AuthTokenOrTLSOption) + l = append(l, yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)) + parser.yyVAL.item = l + } + case 2461: + { + l := yyS[yypt-1].item.([]*ast.AuthTokenOrTLSOption) + l = append(l, yyS[yypt-0].item.(*ast.AuthTokenOrTLSOption)) + parser.yyVAL.item = l + } + case 2462: + { + parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ + Type: ast.Issuer, + Value: yyS[yypt-0].ident, + } + } + case 2463: + { + parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ + Type: ast.Subject, + Value: yyS[yypt-0].ident, + } + } + case 2464: + { + parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ + Type: ast.Cipher, + Value: yyS[yypt-0].ident, + } + } + case 2465: + { + parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ + Type: ast.SAN, + Value: yyS[yypt-0].ident, + } + } + case 2466: + { + parser.yyVAL.item = &ast.AuthTokenOrTLSOption{ + Type: ast.TokenIssuer, + Value: yyS[yypt-0].ident, + } + } + case 2467: + { + parser.yyVAL.item = nil + } + case 2468: + { + parser.yyVAL.item = &ast.CommentOrAttributeOption{Type: ast.UserCommentType, Value: yyS[yypt-0].ident} + } + case 2469: + { + parser.yyVAL.item = &ast.CommentOrAttributeOption{Type: ast.UserAttributeType, Value: yyS[yypt-0].ident} + } + case 2470: + { + parser.yyVAL.item = nil + } + case 2471: + { + parser.yyVAL.item = &ast.ResourceGroupNameOption{Value: yyS[yypt-0].ident} + } + case 2472: + { + parser.yyVAL.item = []*ast.PasswordOrLockOption{} + } + case 2473: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2474: + { + parser.yyVAL.item = []*ast.PasswordOrLockOption{yyS[yypt-0].item.(*ast.PasswordOrLockOption)} + } + case 2475: + { + l := yyS[yypt-1].item.([]*ast.PasswordOrLockOption) + l = append(l, yyS[yypt-0].item.(*ast.PasswordOrLockOption)) + parser.yyVAL.item = l + } + case 2476: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.Unlock, + } + } + case 2477: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.Lock, + } + } + case 2478: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordHistoryDefault, + } + } + case 2479: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordHistory, + Count: yyS[yypt-0].item.(int64), + } + } + case 2480: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordReuseDefault, + } + } + case 2481: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordReuseInterval, + Count: yyS[yypt-1].item.(int64), + } + } + case 2482: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordExpire, + } + } + case 2483: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordExpireInterval, + Count: yyS[yypt-1].item.(int64), + } + } + case 2484: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordExpireNever, + } + } + case 2485: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordExpireDefault, + } + } + case 2486: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.FailedLoginAttempts, + Count: yyS[yypt-0].item.(int64), + } + } + case 2487: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordLockTime, + Count: yyS[yypt-0].item.(int64), + } + } + case 2488: + { + parser.yyVAL.item = &ast.PasswordOrLockOption{ + Type: ast.PasswordLockTimeUnbounded, + } + } + case 2489: + { + parser.yyVAL.item = nil + } + case 2490: + { + parser.yyVAL.item = &ast.AuthOption{ + AuthString: yyS[yypt-0].ident, + ByAuthString: true, + } + } + case 2491: + { + parser.yyVAL.item = &ast.AuthOption{ + AuthPlugin: yyS[yypt-0].ident, + } + } + case 2492: + { + parser.yyVAL.item = &ast.AuthOption{ + AuthPlugin: yyS[yypt-2].ident, + AuthString: yyS[yypt-0].ident, + ByAuthString: true, + } + } + case 2493: + { + parser.yyVAL.item = &ast.AuthOption{ + AuthPlugin: yyS[yypt-2].ident, + HashString: yyS[yypt-0].ident, + ByHashString: true, + } + } + case 2494: + { + parser.yyVAL.item = &ast.AuthOption{ + AuthPlugin: mysql.AuthNativePassword, + HashString: yyS[yypt-0].ident, + ByHashString: true, + } + } + case 2497: + { + parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() + } + case 2498: + { + role := yyS[yypt-0].item.(*auth.RoleIdentity) + roleSpec := &ast.UserSpec{ + User: &auth.UserIdentity{ + Username: role.Username, + Hostname: role.Hostname, + }, + IsRole: true, + } + parser.yyVAL.item = roleSpec + } + case 2499: + { + parser.yyVAL.item = []*ast.UserSpec{yyS[yypt-0].item.(*ast.UserSpec)} + } + case 2500: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.UserSpec), yyS[yypt-0].item.(*ast.UserSpec)) + } + case 2504: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2509: + { + startOffset := parser.startOffset(&yyS[yypt-2]) + endOffset := parser.startOffset(&yyS[yypt-1]) + originStmt := yyS[yypt-2].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) + + startOffset = parser.startOffset(&yyS[yypt]) + hintedStmt := yyS[yypt-0].statement + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + x := &ast.CreateBindingStmt{ + OriginNode: originStmt, + HintedNode: hintedStmt, + GlobalScope: yyS[yypt-5].item.(bool), + } + + parser.yyVAL.statement = x + } + case 2510: + { + x := &ast.CreateBindingStmt{ + GlobalScope: yyS[yypt-7].item.(bool), + PlanDigest: yyS[yypt-0].ident, + } + + parser.yyVAL.statement = x + } + case 2511: + { + startOffset := parser.startOffset(&yyS[yypt]) + originStmt := yyS[yypt-0].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + x := &ast.DropBindingStmt{ + OriginNode: originStmt, + GlobalScope: yyS[yypt-3].item.(bool), + } + + parser.yyVAL.statement = x + } + case 2512: + { + startOffset := parser.startOffset(&yyS[yypt-2]) + endOffset := parser.startOffset(&yyS[yypt-1]) + originStmt := yyS[yypt-2].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) + + startOffset = parser.startOffset(&yyS[yypt]) + hintedStmt := yyS[yypt-0].statement + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + x := &ast.DropBindingStmt{ + OriginNode: originStmt, + HintedNode: hintedStmt, + GlobalScope: yyS[yypt-5].item.(bool), + } + + parser.yyVAL.statement = x + } + case 2513: + { + x := &ast.DropBindingStmt{ + GlobalScope: yyS[yypt-5].item.(bool), + SQLDigest: yyS[yypt-0].ident, + } + + parser.yyVAL.statement = x + } + case 2514: + { + startOffset := parser.startOffset(&yyS[yypt]) + originStmt := yyS[yypt-0].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + x := &ast.SetBindingStmt{ + BindingStatusType: yyS[yypt-2].item.(ast.BindingStatusType), + OriginNode: originStmt, + } + + parser.yyVAL.statement = x + } + case 2515: + { + startOffset := parser.startOffset(&yyS[yypt-2]) + endOffset := parser.startOffset(&yyS[yypt-1]) + originStmt := yyS[yypt-2].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:endOffset])) + + startOffset = parser.startOffset(&yyS[yypt]) + hintedStmt := yyS[yypt-0].statement + hintedStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + x := &ast.SetBindingStmt{ + BindingStatusType: yyS[yypt-4].item.(ast.BindingStatusType), + OriginNode: originStmt, + HintedNode: hintedStmt, + } + + parser.yyVAL.statement = x + } + case 2516: + { + x := &ast.SetBindingStmt{ + BindingStatusType: yyS[yypt-4].item.(ast.BindingStatusType), + SQLDigest: yyS[yypt-0].ident, + } + + parser.yyVAL.statement = x + } + case 2517: + { + p, err := convertToPriv(yyS[yypt-7].item.([]*ast.RoleOrPriv)) + if err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.statement = &ast.GrantStmt{ + Privs: p, + ObjectType: yyS[yypt-5].item.(ast.ObjectTypeType), + Level: yyS[yypt-4].item.(*ast.GrantLevel), + Users: yyS[yypt-2].item.([]*ast.UserSpec), + AuthTokenOrTLSOptions: yyS[yypt-1].item.([]*ast.AuthTokenOrTLSOption), + WithGrant: yyS[yypt-0].item.(bool), + } + } + case 2518: + { + parser.yyVAL.statement = &ast.GrantProxyStmt{ + LocalUser: yyS[yypt-3].item.(*auth.UserIdentity), + ExternalUsers: yyS[yypt-1].item.([]*auth.UserIdentity), + WithGrant: yyS[yypt-0].item.(bool), + } + } + case 2519: + { + r, err := convertToRole(yyS[yypt-2].item.([]*ast.RoleOrPriv)) + if err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.statement = &ast.GrantRoleStmt{ + Roles: r, + Users: yyS[yypt-0].item.([]*auth.UserIdentity), + } + } + case 2520: + { + parser.yyVAL.item = false + } + case 2521: + { + parser.yyVAL.item = true + } + case 2522: + { + parser.yyVAL.item = false + } + case 2523: + { + parser.yyVAL.item = false + } + case 2524: + { + parser.yyVAL.item = false + } + case 2525: + { + parser.yyVAL.item = false + } + case 2526: + { + parser.yyVAL.item = []string{yyS[yypt-0].ident} + } + case 2527: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]string), yyS[yypt-0].ident) + } + case 2528: + { + parser.yyVAL.item = &ast.RoleOrPriv{ + Node: yyS[yypt-0].item, + } + } + case 2529: + { + parser.yyVAL.item = &ast.RoleOrPriv{ + Node: yyS[yypt-0].item, + } + } + case 2530: + { + parser.yyVAL.item = &ast.RoleOrPriv{ + Symbols: strings.Join(yyS[yypt-0].item.([]string), " "), + } + } + case 2531: + { + parser.yyVAL.item = &ast.RoleOrPriv{ + Symbols: "LOAD FROM S3", + } + } + case 2532: + { + parser.yyVAL.item = &ast.RoleOrPriv{ + Symbols: "SELECT INTO S3", + } + } + case 2533: + { + parser.yyVAL.item = []*ast.RoleOrPriv{yyS[yypt-0].item.(*ast.RoleOrPriv)} + } + case 2534: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.RoleOrPriv), yyS[yypt-0].item.(*ast.RoleOrPriv)) + } + case 2535: + { + parser.yyVAL.item = &ast.PrivElem{ + Priv: yyS[yypt-0].item.(mysql.PrivilegeType), + } + } + case 2536: + { + parser.yyVAL.item = &ast.PrivElem{ + Priv: yyS[yypt-3].item.(mysql.PrivilegeType), + Cols: yyS[yypt-1].item.([]*ast.ColumnName), + } + } + case 2537: + { + parser.yyVAL.item = mysql.AllPriv + } + case 2538: + { + parser.yyVAL.item = mysql.AllPriv + } + case 2539: + { + parser.yyVAL.item = mysql.AlterPriv + } + case 2540: + { + parser.yyVAL.item = mysql.CreatePriv + } + case 2541: + { + parser.yyVAL.item = mysql.CreateUserPriv + } + case 2542: + { + parser.yyVAL.item = mysql.CreateTablespacePriv + } + case 2543: + { + parser.yyVAL.item = mysql.TriggerPriv + } + case 2544: + { + parser.yyVAL.item = mysql.DeletePriv + } + case 2545: + { + parser.yyVAL.item = mysql.DropPriv + } + case 2546: + { + parser.yyVAL.item = mysql.ProcessPriv + } + case 2547: + { + parser.yyVAL.item = mysql.ExecutePriv + } + case 2548: + { + parser.yyVAL.item = mysql.IndexPriv + } + case 2549: + { + parser.yyVAL.item = mysql.InsertPriv + } + case 2550: + { + parser.yyVAL.item = mysql.SelectPriv + } + case 2551: + { + parser.yyVAL.item = mysql.SuperPriv + } + case 2552: + { + parser.yyVAL.item = mysql.ShowDBPriv + } + case 2553: + { + parser.yyVAL.item = mysql.UpdatePriv + } + case 2554: + { + parser.yyVAL.item = mysql.GrantPriv + } + case 2555: + { + parser.yyVAL.item = mysql.ReferencesPriv + } + case 2556: + { + parser.yyVAL.item = mysql.ReplicationSlavePriv + } + case 2557: + { + parser.yyVAL.item = mysql.ReplicationClientPriv + } + case 2558: + { + parser.yyVAL.item = mysql.UsagePriv + } + case 2559: + { + parser.yyVAL.item = mysql.ReloadPriv + } + case 2560: + { + parser.yyVAL.item = mysql.FilePriv + } + case 2561: + { + parser.yyVAL.item = mysql.ConfigPriv + } + case 2562: + { + parser.yyVAL.item = mysql.CreateTMPTablePriv + } + case 2563: + { + parser.yyVAL.item = mysql.LockTablesPriv + } + case 2564: + { + parser.yyVAL.item = mysql.CreateViewPriv + } + case 2565: + { + parser.yyVAL.item = mysql.ShowViewPriv + } + case 2566: + { + parser.yyVAL.item = mysql.CreateRolePriv + } + case 2567: + { + parser.yyVAL.item = mysql.DropRolePriv + } + case 2568: + { + parser.yyVAL.item = mysql.CreateRoutinePriv + } + case 2569: + { + parser.yyVAL.item = mysql.AlterRoutinePriv + } + case 2570: + { + parser.yyVAL.item = mysql.EventPriv + } + case 2571: + { + parser.yyVAL.item = mysql.ShutdownPriv + } + case 2572: + { + parser.yyVAL.item = ast.ObjectTypeNone + } + case 2573: + { + parser.yyVAL.item = ast.ObjectTypeTable + } + case 2574: + { + parser.yyVAL.item = ast.ObjectTypeFunction + } + case 2575: + { + parser.yyVAL.item = ast.ObjectTypeProcedure + } + case 2576: + { + parser.yyVAL.item = &ast.GrantLevel{ + Level: ast.GrantLevelDB, + } + } + case 2577: + { + parser.yyVAL.item = &ast.GrantLevel{ + Level: ast.GrantLevelGlobal, + } + } + case 2578: + { + parser.yyVAL.item = &ast.GrantLevel{ + Level: ast.GrantLevelDB, + DBName: yyS[yypt-2].ident, + } + } + case 2579: + { + parser.yyVAL.item = &ast.GrantLevel{ + Level: ast.GrantLevelTable, + DBName: yyS[yypt-2].ident, + TableName: yyS[yypt-0].ident, + } + } + case 2580: + { + parser.yyVAL.item = &ast.GrantLevel{ + Level: ast.GrantLevelTable, + TableName: yyS[yypt-0].ident, + } + } + case 2581: + { + p, err := convertToPriv(yyS[yypt-5].item.([]*ast.RoleOrPriv)) + if err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.statement = &ast.RevokeStmt{ + Privs: p, + ObjectType: yyS[yypt-3].item.(ast.ObjectTypeType), + Level: yyS[yypt-2].item.(*ast.GrantLevel), + Users: yyS[yypt-0].item.([]*ast.UserSpec), + } + } + case 2582: + { + // MySQL has special syntax for REVOKE ALL [PRIVILEGES], GRANT OPTION + // which uses the RevokeRoleStmt syntax but is of type RevokeStmt. + // It is documented at https://dev.mysql.com/doc/refman/5.7/en/revoke.html + // as the "second syntax" for REVOKE. It is only valid if *both* + // ALL PRIVILEGES + GRANT OPTION are specified in that order. + if isRevokeAllGrant(yyS[yypt-2].item.([]*ast.RoleOrPriv)) { + var users []*ast.UserSpec + for _, u := range yyS[yypt-0].item.([]*auth.UserIdentity) { + users = append(users, &ast.UserSpec{ + User: u, + }) + } + parser.yyVAL.statement = &ast.RevokeStmt{ + Privs: []*ast.PrivElem{{Priv: mysql.AllPriv}, {Priv: mysql.GrantPriv}}, + ObjectType: ast.ObjectTypeNone, + Level: &ast.GrantLevel{Level: ast.GrantLevelGlobal}, + Users: users, + } + } else { + r, err := convertToRole(yyS[yypt-2].item.([]*ast.RoleOrPriv)) + if err != nil { + yylex.AppendError(err) + return 1 + } + parser.yyVAL.statement = &ast.RevokeRoleStmt{ + Roles: r, + Users: yyS[yypt-0].item.([]*auth.UserIdentity), + } + } + } + case 2583: + { + x := &ast.LoadDataStmt{ + FileLocRef: ast.FileLocServerOrRemote, + Path: yyS[yypt-12].ident, + Format: yyS[yypt-11].item.(*string), + OnDuplicate: yyS[yypt-10].item.(ast.OnDuplicateKeyHandlingType), + Table: yyS[yypt-7].item.(*ast.TableName), + Charset: yyS[yypt-6].item.(*string), + FieldsInfo: yyS[yypt-5].item.(*ast.FieldsClause), + LinesInfo: yyS[yypt-4].item.(*ast.LinesClause), + IgnoreLines: yyS[yypt-3].item.(*uint64), + ColumnsAndUserVars: yyS[yypt-2].item.([]*ast.ColumnNameOrUserVar), + ColumnAssignments: yyS[yypt-1].item.([]*ast.Assignment), + Options: yyS[yypt-0].item.([]*ast.LoadDataOpt), + } + if yyS[yypt-14].item != nil { + x.FileLocRef = ast.FileLocClient + // See https://dev.mysql.com/doc/refman/5.7/en/load-data.html#load-data-duplicate-key-handling + // If you do not specify IGNORE or REPLACE modifier , then we set default behavior to IGNORE when LOCAL modifier is specified + if x.OnDuplicate == ast.OnDuplicateKeyHandlingError { + x.OnDuplicate = ast.OnDuplicateKeyHandlingIgnore + } + } + columns := []*ast.ColumnName{} + for _, v := range x.ColumnsAndUserVars { + if v.ColumnName != nil { + columns = append(columns, v.ColumnName) + } + } + x.Columns = columns + + parser.yyVAL.statement = x + } + case 2584: + { + parser.yyVAL.item = (*string)(nil) + } + case 2585: + { + str := yyS[yypt-0].ident + parser.yyVAL.item = &str + } + case 2586: + { + parser.yyVAL.item = (*uint64)(nil) + } + case 2587: + { + v := getUint64FromNUM(yyS[yypt-1].item) + parser.yyVAL.item = &v + } + case 2588: + { + parser.yyVAL.item = (*string)(nil) + } + case 2589: + { + v := yyS[yypt-0].ident + parser.yyVAL.item = &v + } + case 2590: + { + parser.yyVAL.item = nil + } + case 2591: + { + parser.yyVAL.item = yyS[yypt-0].ident + } + case 2592: + { + parser.yyVAL.item = (*ast.FieldsClause)(nil) + } + case 2593: + { + fieldsClause := &ast.FieldsClause{} + fieldItems := yyS[yypt-0].item.([]*ast.FieldItem) + for _, item := range fieldItems { + switch item.Type { + case ast.Terminated: + fieldsClause.Terminated = &item.Value + case ast.Enclosed: + fieldsClause.Enclosed = &item.Value + fieldsClause.OptEnclosed = item.OptEnclosed + case ast.Escaped: + fieldsClause.Escaped = &item.Value + case ast.DefinedNullBy: + fieldsClause.DefinedNullBy = &item.Value + fieldsClause.NullValueOptEnclosed = item.OptEnclosed + } + } + parser.yyVAL.item = fieldsClause + } + case 2596: + { + fieldItems := yyS[yypt-1].item.([]*ast.FieldItem) + parser.yyVAL.item = append(fieldItems, yyS[yypt-0].item.(*ast.FieldItem)) + } + case 2597: + { + fieldItems := make([]*ast.FieldItem, 1, 1) + fieldItems[0] = yyS[yypt-0].item.(*ast.FieldItem) + parser.yyVAL.item = fieldItems + } + case 2598: + { + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.Terminated, + Value: yyS[yypt-0].ident, + } + } + case 2599: + { + str := yyS[yypt-0].ident + if str != "\\" && len(str) > 1 { + yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) + return 1 + } + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.Enclosed, + Value: str, + OptEnclosed: true, + } + } + case 2600: + { + str := yyS[yypt-0].ident + if str != "\\" && len(str) > 1 { + yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) + return 1 + } + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.Enclosed, + Value: str, + } + } + case 2601: + { + str := yyS[yypt-0].ident + if str != "\\" && len(str) > 1 { + yylex.AppendError(ErrWrongFieldTerminators.GenWithStackByArgs()) + return 1 + } + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.Escaped, + Value: str, + } + } + case 2602: + { + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.DefinedNullBy, + Value: yyS[yypt-0].item.(*ast.TextString).Value, + } + } + case 2603: + { + parser.yyVAL.item = &ast.FieldItem{ + Type: ast.DefinedNullBy, + Value: yyS[yypt-2].item.(*ast.TextString).Value, + OptEnclosed: true, + } + } + case 2605: + { + parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() + } + case 2606: + { + parser.yyVAL.ident = yyS[yypt-0].item.(ast.BinaryLiteral).ToString() + } + case 2607: + { + parser.yyVAL.item = (*ast.LinesClause)(nil) + } + case 2608: + { + parser.yyVAL.item = &ast.LinesClause{Starting: yyS[yypt-1].item.(*string), Terminated: yyS[yypt-0].item.(*string)} + } + case 2609: + { + parser.yyVAL.item = (*string)(nil) + } + case 2610: + { + s := yyS[yypt-0].ident + parser.yyVAL.item = &s + } + case 2611: + { + parser.yyVAL.item = (*string)(nil) + } + case 2612: + { + s := yyS[yypt-0].ident + parser.yyVAL.item = &s + } + case 2613: + { + parser.yyVAL.item = ([]*ast.Assignment)(nil) + } + case 2614: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2615: + { + l := yyS[yypt-2].item.([]*ast.Assignment) + parser.yyVAL.item = append(l, yyS[yypt-0].item.(*ast.Assignment)) + } + case 2616: + { + parser.yyVAL.item = []*ast.Assignment{yyS[yypt-0].item.(*ast.Assignment)} + } + case 2617: + { + parser.yyVAL.item = &ast.Assignment{ + Column: yyS[yypt-2].expr.(*ast.ColumnNameExpr).Name, + Expr: yyS[yypt-0].expr, + } + } + case 2618: + { + parser.yyVAL.item = []*ast.LoadDataOpt{} + } + case 2619: + { + parser.yyVAL.item = yyS[yypt-0].item.([]*ast.LoadDataOpt) + } + case 2620: + { + parser.yyVAL.item = []*ast.LoadDataOpt{yyS[yypt-0].item.(*ast.LoadDataOpt)} + } + case 2621: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.LoadDataOpt), yyS[yypt-0].item.(*ast.LoadDataOpt)) + } + case 2622: + { + parser.yyVAL.item = &ast.LoadDataOpt{Name: strings.ToLower(yyS[yypt-0].ident)} + } + case 2623: + { + parser.yyVAL.item = &ast.LoadDataOpt{Name: strings.ToLower(yyS[yypt-2].ident), Value: yyS[yypt-0].expr.(ast.ExprNode)} + } + case 2624: + { + parser.yyVAL.statement = &ast.ImportIntoStmt{ + Table: yyS[yypt-6].item.(*ast.TableName), + ColumnsAndUserVars: yyS[yypt-5].item.([]*ast.ColumnNameOrUserVar), + ColumnAssignments: yyS[yypt-4].item.([]*ast.Assignment), + Path: yyS[yypt-2].ident, + Format: yyS[yypt-1].item.(*string), + Options: yyS[yypt-0].item.([]*ast.LoadDataOpt), + } + } + case 2625: + { + parser.yyVAL.statement = &ast.UnlockTablesStmt{} + } + case 2626: + { + parser.yyVAL.statement = &ast.LockTablesStmt{ + TableLocks: yyS[yypt-0].item.([]ast.TableLock), + } + } + case 2629: + { + parser.yyVAL.item = ast.TableLock{ + Table: yyS[yypt-1].item.(*ast.TableName), + Type: yyS[yypt-0].item.(model.TableLockType), + } + } + case 2630: + { + parser.yyVAL.item = model.TableLockRead + } + case 2631: + { + parser.yyVAL.item = model.TableLockReadLocal + } + case 2632: + { + parser.yyVAL.item = model.TableLockWrite + } + case 2633: + { + parser.yyVAL.item = model.TableLockWriteLocal + } + case 2634: + { + parser.yyVAL.item = []ast.TableLock{yyS[yypt-0].item.(ast.TableLock)} + } + case 2635: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]ast.TableLock), yyS[yypt-0].item.(ast.TableLock)) + } + case 2636: + { + parser.yyVAL.statement = &ast.NonTransactionalDMLStmt{ + DryRun: yyS[yypt-1].item.(int), + ShardColumn: yyS[yypt-4].item.(*ast.ColumnName), + Limit: getUint64FromNUM(yyS[yypt-2].item), + DMLStmt: yyS[yypt-0].statement.(ast.ShardableDMLStmt), + } + } + case 2641: + { + parser.yyVAL.item = ast.NoDryRun + } + case 2642: + { + parser.yyVAL.item = ast.DryRunSplitDml + } + case 2643: + { + parser.yyVAL.item = ast.DryRunQuery + } + case 2644: + { + parser.yyVAL.item = (*ast.ColumnName)(nil) + } + case 2645: + { + parser.yyVAL.item = yyS[yypt-0].item.(*ast.ColumnName) + } + case 2646: + { + parser.yyVAL.statement = &ast.KillStmt{ + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + TiDBExtension: yyS[yypt-1].item.(bool), + } + } + case 2647: + { + parser.yyVAL.statement = &ast.KillStmt{ + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + TiDBExtension: yyS[yypt-2].item.(bool), + } + } + case 2648: + { + parser.yyVAL.statement = &ast.KillStmt{ + ConnectionID: getUint64FromNUM(yyS[yypt-0].item), + Query: true, + TiDBExtension: yyS[yypt-2].item.(bool), + } + } + case 2649: + { + parser.yyVAL.statement = &ast.KillStmt{ + TiDBExtension: yyS[yypt-1].item.(bool), + Expr: yyS[yypt-0].expr, + } + } + case 2650: + { + parser.yyVAL.item = false + } + case 2651: + { + parser.yyVAL.item = true + } + case 2652: + { + parser.yyVAL.statement = &ast.LoadStatsStmt{ + Path: yyS[yypt-0].ident, + } + } + case 2653: + { + parser.yyVAL.statement = &ast.LockStatsStmt{ + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 2654: + { + x := yyS[yypt-2].item.(*ast.TableName) + x.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + parser.yyVAL.statement = &ast.LockStatsStmt{ + Tables: []*ast.TableName{x}, + } + } + case 2655: + { + x := yyS[yypt-4].item.(*ast.TableName) + x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) + parser.yyVAL.statement = &ast.LockStatsStmt{ + Tables: []*ast.TableName{x}, + } + } + case 2656: + { + parser.yyVAL.statement = &ast.UnlockStatsStmt{ + Tables: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 2657: + { + x := yyS[yypt-2].item.(*ast.TableName) + x.PartitionNames = yyS[yypt-0].item.([]model.CIStr) + parser.yyVAL.statement = &ast.UnlockStatsStmt{ + Tables: []*ast.TableName{x}, + } + } + case 2658: + { + x := yyS[yypt-4].item.(*ast.TableName) + x.PartitionNames = yyS[yypt-1].item.([]model.CIStr) + parser.yyVAL.statement = &ast.UnlockStatsStmt{ + Tables: []*ast.TableName{x}, + } + } + case 2659: + { + parser.yyVAL.statement = &ast.DropPlacementPolicyStmt{ + IfExists: yyS[yypt-1].item.(bool), + PolicyName: model.NewCIStr(yyS[yypt-0].ident), + } + } + case 2660: + { + parser.yyVAL.statement = &ast.CreateResourceGroupStmt{ + IfNotExists: yyS[yypt-2].item.(bool), + ResourceGroupName: model.NewCIStr(yyS[yypt-1].ident), + ResourceGroupOptionList: yyS[yypt-0].item.([]*ast.ResourceGroupOption), + } + } + case 2661: + { + parser.yyVAL.statement = &ast.AlterResourceGroupStmt{ + IfExists: yyS[yypt-2].item.(bool), + ResourceGroupName: model.NewCIStr(yyS[yypt-1].ident), + ResourceGroupOptionList: yyS[yypt-0].item.([]*ast.ResourceGroupOption), + } + } + case 2662: + { + parser.yyVAL.statement = &ast.DropResourceGroupStmt{ + IfExists: yyS[yypt-1].item.(bool), + ResourceGroupName: model.NewCIStr(yyS[yypt-0].ident), + } + } + case 2663: + { + parser.yyVAL.statement = &ast.CreatePlacementPolicyStmt{ + OrReplace: yyS[yypt-5].item.(bool), + IfNotExists: yyS[yypt-2].item.(bool), + PolicyName: model.NewCIStr(yyS[yypt-1].ident), + PlacementOptions: yyS[yypt-0].item.([]*ast.PlacementOption), + } + } + case 2664: + { + parser.yyVAL.statement = &ast.AlterPlacementPolicyStmt{ + IfExists: yyS[yypt-2].item.(bool), + PolicyName: model.NewCIStr(yyS[yypt-1].ident), + PlacementOptions: yyS[yypt-0].item.([]*ast.PlacementOption), + } + } + case 2665: + { + parser.yyVAL.statement = &ast.CreateSequenceStmt{ + IfNotExists: yyS[yypt-3].item.(bool), + Name: yyS[yypt-2].item.(*ast.TableName), + SeqOptions: yyS[yypt-1].item.([]*ast.SequenceOption), + TblOptions: yyS[yypt-0].item.([]*ast.TableOption), + } + } + case 2666: + { + parser.yyVAL.item = []*ast.SequenceOption{} + } + case 2668: + { + parser.yyVAL.item = []*ast.SequenceOption{yyS[yypt-0].item.(*ast.SequenceOption)} + } + case 2669: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.SequenceOption), yyS[yypt-0].item.(*ast.SequenceOption)) + } + case 2670: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceOptionIncrementBy, IntValue: yyS[yypt-0].item.(int64)} + } + case 2671: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceOptionIncrementBy, IntValue: yyS[yypt-0].item.(int64)} + } + case 2672: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceStartWith, IntValue: yyS[yypt-0].item.(int64)} + } + case 2673: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceStartWith, IntValue: yyS[yypt-0].item.(int64)} + } + case 2674: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceMinValue, IntValue: yyS[yypt-0].item.(int64)} + } + case 2675: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMinValue} + } + case 2676: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMinValue} + } + case 2677: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceMaxValue, IntValue: yyS[yypt-0].item.(int64)} + } + case 2678: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMaxValue} + } + case 2679: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoMaxValue} + } + case 2680: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceCache, IntValue: yyS[yypt-0].item.(int64)} + } + case 2681: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCache} + } + case 2682: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCache} + } + case 2683: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceCycle} + } + case 2684: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCycle} + } + case 2685: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceNoCycle} + } + case 2687: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2688: + { + unsigned_num := getUint64FromNUM(yyS[yypt-0].item) + if unsigned_num > 9223372036854775808 { + yylex.AppendError(yylex.Errorf("the Signed Value should be at the range of [-9223372036854775808, 9223372036854775807].")) + return 1 + } else if unsigned_num == 9223372036854775808 { + signed_one := int64(1) + parser.yyVAL.item = signed_one << 63 + } else { + parser.yyVAL.item = -int64(unsigned_num) + } + } + case 2689: + { + parser.yyVAL.statement = &ast.DropSequenceStmt{ + IfExists: yyS[yypt-1].item.(bool), + Sequences: yyS[yypt-0].item.([]*ast.TableName), + } + } + case 2690: + { + parser.yyVAL.statement = &ast.AlterSequenceStmt{ + IfExists: yyS[yypt-2].item.(bool), + Name: yyS[yypt-1].item.(*ast.TableName), + SeqOptions: yyS[yypt-0].item.([]*ast.SequenceOption), + } + } + case 2691: + { + parser.yyVAL.item = []*ast.SequenceOption{yyS[yypt-0].item.(*ast.SequenceOption)} + } + case 2692: + { + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.SequenceOption), yyS[yypt-0].item.(*ast.SequenceOption)) + } + case 2694: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestart} + } + case 2695: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestartWith, IntValue: yyS[yypt-0].item.(int64)} + } + case 2696: + { + parser.yyVAL.item = &ast.SequenceOption{Tp: ast.SequenceRestartWith, IntValue: yyS[yypt-0].item.(int64)} + } + case 2697: + { + x := &ast.IndexAdviseStmt{ + Path: yyS[yypt-3].ident, + MaxMinutes: yyS[yypt-2].item.(uint64), + } + if yyS[yypt-5].item != nil { + x.IsLocal = true + } + if yyS[yypt-1].item != nil { + x.MaxIndexNum = yyS[yypt-1].item.(*ast.MaxIndexNumClause) + } + if yyS[yypt-0].item != nil { + x.LinesInfo = yyS[yypt-0].item.(*ast.LinesClause) + } + parser.yyVAL.statement = x + } + case 2698: + { + parser.yyVAL.item = uint64(ast.UnspecifiedSize) + } + case 2699: + { + parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) + } + case 2700: + { + parser.yyVAL.item = nil + } + case 2701: + { + parser.yyVAL.item = &ast.MaxIndexNumClause{ + PerTable: yyS[yypt-1].item.(uint64), + PerDB: yyS[yypt-0].item.(uint64), + } + } + case 2702: + { + parser.yyVAL.item = uint64(ast.UnspecifiedSize) + } + case 2703: + { + parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) + } + case 2704: + { + parser.yyVAL.item = uint64(ast.UnspecifiedSize) + } + case 2705: + { + parser.yyVAL.item = getUint64FromNUM(yyS[yypt-0].item) + } + case 2706: + { + // Parse it but will ignore it + switch yyS[yypt-0].ident { + case "Y", "y": + yylex.AppendError(yylex.Errorf("The ENCRYPTION clause is parsed but ignored by all storage engines.")) + parser.lastErrorAsWarn() + case "N", "n": + break + default: + yylex.AppendError(ErrWrongValue.GenWithStackByArgs("argument (should be Y or N)", yyS[yypt-0].ident)) + return 1 + } + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 2707: + { + parser.yyVAL.item = append([]*ast.RowExpr{}, yyS[yypt-0].item.(*ast.RowExpr)) + } + case 2708: + { + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.RowExpr), yyS[yypt-0].item.(*ast.RowExpr)) + } + case 2709: + { + parser.yyVAL.item = &ast.RowExpr{Values: yyS[yypt-0].item.([]ast.ExprNode)} + } + case 2710: + { + x := &ast.PlanReplayerStmt{ + Stmt: yyS[yypt-0].statement, + Analyze: false, + Load: false, + File: "", + Where: nil, + OrderBy: nil, + Limit: nil, + } + if yyS[yypt-2].item != nil { + x.HistoricalStatsInfo = yyS[yypt-2].item.(*ast.AsOfClause) + } + startOffset := parser.startOffset(&yyS[yypt]) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + parser.yyVAL.statement = x + } + case 2711: + { + x := &ast.PlanReplayerStmt{ + Stmt: yyS[yypt-0].statement, + Analyze: true, + Load: false, + File: "", + Where: nil, + OrderBy: nil, + Limit: nil, + } + if yyS[yypt-3].item != nil { + x.HistoricalStatsInfo = yyS[yypt-3].item.(*ast.AsOfClause) + } + startOffset := parser.startOffset(&yyS[yypt]) + x.Stmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:])) + + parser.yyVAL.statement = x + } + case 2712: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: false, + Load: false, + File: "", + } + if yyS[yypt-6].item != nil { + x.HistoricalStatsInfo = yyS[yypt-6].item.(*ast.AsOfClause) + } + if yyS[yypt-2].item != nil { + x.Where = yyS[yypt-2].item.(ast.ExprNode) + } + if yyS[yypt-1].item != nil { + x.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) + } + if yyS[yypt-0].item != nil { + x.Limit = yyS[yypt-0].item.(*ast.Limit) + } + + parser.yyVAL.statement = x + } + case 2713: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: true, + Load: false, + File: "", + } + if yyS[yypt-7].item != nil { + x.HistoricalStatsInfo = yyS[yypt-7].item.(*ast.AsOfClause) + } + if yyS[yypt-2].item != nil { + x.Where = yyS[yypt-2].item.(ast.ExprNode) + } + if yyS[yypt-1].item != nil { + x.OrderBy = yyS[yypt-1].item.(*ast.OrderByClause) + } + if yyS[yypt-0].item != nil { + x.Limit = yyS[yypt-0].item.(*ast.Limit) + } + + parser.yyVAL.statement = x + } + case 2714: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: false, + Load: false, + File: yyS[yypt-0].ident, + } + if yyS[yypt-2].item != nil { + x.HistoricalStatsInfo = yyS[yypt-2].item.(*ast.AsOfClause) + } + parser.yyVAL.statement = x + } + case 2715: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: true, + Load: false, + File: yyS[yypt-0].ident, + } + if yyS[yypt-3].item != nil { + x.HistoricalStatsInfo = yyS[yypt-3].item.(*ast.AsOfClause) + } + parser.yyVAL.statement = x + } + case 2716: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: false, + Load: true, + File: yyS[yypt-0].ident, + Where: nil, + OrderBy: nil, + Limit: nil, + } + + parser.yyVAL.statement = x + } + case 2717: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: false, + Capture: true, + SQLDigest: yyS[yypt-1].ident, + PlanDigest: yyS[yypt-0].ident, + Where: nil, + OrderBy: nil, + Limit: nil, + } + + parser.yyVAL.statement = x + } + case 2718: + { + x := &ast.PlanReplayerStmt{ + Stmt: nil, + Analyze: false, + Remove: true, + SQLDigest: yyS[yypt-1].ident, + PlanDigest: yyS[yypt-0].ident, + Where: nil, + OrderBy: nil, + Limit: nil, + } + + parser.yyVAL.statement = x + } + case 2719: + { + parser.yyVAL.item = nil + } + case 2720: + { + parser.yyVAL.item = yyS[yypt-0].item.(*ast.AsOfClause) + } + case 2721: + { + parser.yyVAL.item = []*ast.StoreParameter{} + } + case 2722: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2723: + { + l := yyS[yypt-2].item.([]*ast.StoreParameter) + l = append(l, yyS[yypt-0].item.(*ast.StoreParameter)) + parser.yyVAL.item = l + } + case 2724: + { + parser.yyVAL.item = []*ast.StoreParameter{yyS[yypt-0].item.(*ast.StoreParameter)} + } + case 2725: + { + x := &ast.StoreParameter{ + Paramstatus: yyS[yypt-2].item.(int), + ParamType: yyS[yypt-0].item.(*types.FieldType), + ParamName: yyS[yypt-1].ident, + } + parser.yyVAL.item = x + } + case 2726: + { + parser.yyVAL.item = ast.MODE_IN + } + case 2727: + { + parser.yyVAL.item = ast.MODE_IN + } + case 2728: + { + parser.yyVAL.item = ast.MODE_OUT + } + case 2729: + { + parser.yyVAL.item = ast.MODE_INOUT + } + case 2732: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2747: + { + var sel ast.StmtNode + switch x := yyS[yypt-0].expr.(*ast.SubqueryExpr).Query.(type) { + case *ast.SelectStmt: + x.IsInBraces = true + sel = x + case *ast.SetOprStmt: + x.IsInBraces = true + sel = x + } + parser.yyVAL.statement = sel + } + case 2749: + { + parser.yyVAL.statement = yyS[yypt-0].statement + } + case 2750: + { + parser.yyVAL.item = []string{strings.ToLower(yyS[yypt-0].ident)} + } + case 2751: + { + l := yyS[yypt-2].item.([]string) + l = append(l, strings.ToLower(yyS[yypt-0].ident)) + parser.yyVAL.item = l + } + case 2752: + { + parser.yyVAL.item = nil + } + case 2753: + { + parser.yyVAL.item = yyS[yypt-0].expr + } + case 2754: + { + x := &ast.ProcedureDecl{ + DeclNames: yyS[yypt-2].item.([]string), + DeclType: yyS[yypt-1].item.(*types.FieldType), + } + if yyS[yypt-0].item != nil { + x.DeclDefault = yyS[yypt-0].item.(ast.ExprNode) + } + parser.yyVAL.item = x + } + case 2755: + { + name := strings.ToLower(yyS[yypt-3].ident) + parser.yyVAL.item = &ast.ProcedureCursor{ + CurName: name, + Selectstring: yyS[yypt-0].statement.(ast.StmtNode), + } + } + case 2756: + { + parser.yyVAL.item = &ast.ProcedureErrorControl{ + ControlHandle: yyS[yypt-4].item.(int), + ErrorCon: yyS[yypt-1].item.([]ast.ErrNode), + Operate: yyS[yypt-0].statement.(ast.StmtNode), + } + } + case 2757: + { + parser.yyVAL.item = ast.PROCEDUR_CONTINUE + } + case 2758: + { + parser.yyVAL.item = ast.PROCEDUR_EXIT + } + case 2759: + { + parser.yyVAL.item = []ast.ErrNode{yyS[yypt-0].statement.(ast.ErrNode)} + } + case 2760: + { + l := yyS[yypt-2].item.([]ast.ErrNode) + l = append(l, yyS[yypt-0].statement.(ast.ErrNode)) + parser.yyVAL.item = l + } + case 2761: + { + parser.yyVAL.statement = yyS[yypt-0].statement.(ast.ErrNode) + } + case 2762: + { + parser.yyVAL.statement = &ast.ProcedureErrorCon{ + ErrorCon: ast.PROCEDUR_SQLWARNING, + } + } + case 2763: + { + parser.yyVAL.statement = &ast.ProcedureErrorCon{ + ErrorCon: ast.PROCEDUR_NOT_FOUND, + } + } + case 2764: + { + parser.yyVAL.statement = &ast.ProcedureErrorCon{ + ErrorCon: ast.PROCEDUR_SQLEXCEPTION, + } + } + case 2765: + { + parser.yyVAL.statement = &ast.ProcedureErrorVal{ + ErrorNum: getUint64FromNUM(yyS[yypt-0].item), + } + } + case 2766: + { + parser.yyVAL.statement = &ast.ProcedureErrorState{ + CodeStatus: yyS[yypt-0].ident, + } + } + case 2769: + { + name := strings.ToLower(yyS[yypt-0].ident) + parser.yyVAL.statement = &ast.ProcedureOpenCur{ + CurName: name, + } + } + case 2770: + { + name := strings.ToLower(yyS[yypt-2].ident) + parser.yyVAL.statement = &ast.ProcedureFetchInto{ + CurName: name, + Variables: yyS[yypt-0].item.([]string), + } + } + case 2771: + { + name := strings.ToLower(yyS[yypt-0].ident) + parser.yyVAL.statement = &ast.ProcedureCloseCur{ + CurName: name, + } + } + case 2775: + { + parser.yyVAL.item = []string{strings.ToLower(yyS[yypt-0].ident)} + } + case 2776: + { + l := yyS[yypt-2].item.([]string) + l = append(l, strings.ToLower(yyS[yypt-0].ident)) + parser.yyVAL.item = l + } + case 2777: + { + parser.yyVAL.item = []ast.DeclNode{} + } + case 2778: + { + parser.yyVAL.item = yyS[yypt-0].item + } + case 2779: + { + parser.yyVAL.item = []ast.DeclNode{yyS[yypt-1].item.(ast.DeclNode)} + } + case 2780: + { + l := yyS[yypt-2].item.([]ast.DeclNode) + l = append(l, yyS[yypt-1].item.(ast.DeclNode)) + parser.yyVAL.item = l + } + case 2781: + { + parser.yyVAL.item = []ast.StmtNode{} + } + case 2782: + { + l := yyS[yypt-2].item.([]ast.StmtNode) + l = append(l, yyS[yypt-1].statement.(ast.StmtNode)) + parser.yyVAL.item = l + } + case 2783: + { + parser.yyVAL.item = []ast.StmtNode{yyS[yypt-1].statement.(ast.StmtNode)} + } + case 2784: + { + l := yyS[yypt-2].item.([]ast.StmtNode) + l = append(l, yyS[yypt-1].statement.(ast.StmtNode)) + parser.yyVAL.item = l + } + case 2785: + { + x := &ast.ProcedureBlock{ + ProcedureVars: yyS[yypt-2].item.([]ast.DeclNode), + ProcedureProcStmts: yyS[yypt-1].item.([]ast.StmtNode), + } + parser.yyVAL.statement = x + } + case 2786: + { + parser.yyVAL.statement = &ast.ProcedureIfInfo{ + IfBody: yyS[yypt-2].statement.(*ast.ProcedureIfBlock), + } + } + case 2787: + { + ifBlock := &ast.ProcedureIfBlock{ + IfExpr: yyS[yypt-3].expr.(ast.ExprNode), + ProcedureIfStmts: yyS[yypt-1].item.([]ast.StmtNode), + } + if yyS[yypt-0].statement != nil { + ifBlock.ProcedureElseStmt = yyS[yypt-0].statement.(ast.StmtNode) + } + parser.yyVAL.statement = ifBlock + } + case 2788: + { + parser.yyVAL.statement = nil + } + case 2789: + { + parser.yyVAL.statement = &ast.ProcedureElseIfBlock{ + ProcedureIfStmt: yyS[yypt-0].statement.(*ast.ProcedureIfBlock), + } + } + case 2790: + { + parser.yyVAL.statement = &ast.ProcedureElseBlock{ + ProcedureIfStmts: yyS[yypt-0].item.([]ast.StmtNode), + } + } + case 2791: + { + parser.yyVAL.statement = yyS[yypt-0].statement + } + case 2792: + { + parser.yyVAL.statement = yyS[yypt-0].statement + } + case 2793: + { + parser.yyVAL.item = []*ast.SimpleWhenThenStmt{yyS[yypt-0].statement.(*ast.SimpleWhenThenStmt)} + } + case 2794: + { + l := yyS[yypt-1].item.([]*ast.SimpleWhenThenStmt) + l = append(l, yyS[yypt-0].statement.(*ast.SimpleWhenThenStmt)) + parser.yyVAL.item = l + } + case 2795: + { + parser.yyVAL.item = []*ast.SearchWhenThenStmt{yyS[yypt-0].statement.(*ast.SearchWhenThenStmt)} + } + case 2796: + { + l := yyS[yypt-1].item.([]*ast.SearchWhenThenStmt) + l = append(l, yyS[yypt-0].statement.(*ast.SearchWhenThenStmt)) + parser.yyVAL.item = l + } + case 2797: + { + parser.yyVAL.statement = &ast.SimpleWhenThenStmt{ + Expr: yyS[yypt-2].expr.(ast.ExprNode), + ProcedureStmts: yyS[yypt-0].item.([]ast.StmtNode), + } + } + case 2798: + { + parser.yyVAL.statement = &ast.SearchWhenThenStmt{ + Expr: yyS[yypt-2].expr.(ast.ExprNode), + ProcedureStmts: yyS[yypt-0].item.([]ast.StmtNode), + } + } + case 2799: + { + parser.yyVAL.item = nil + } + case 2800: + { + parser.yyVAL.item = yyS[yypt-0].item.([]ast.StmtNode) + } + case 2801: + { + caseStmt := &ast.SimpleCaseStmt{ + Condition: yyS[yypt-4].expr.(ast.ExprNode), + WhenCases: yyS[yypt-3].item.([]*ast.SimpleWhenThenStmt), + } + if yyS[yypt-2].item != nil { + caseStmt.ElseCases = yyS[yypt-2].item.([]ast.StmtNode) + } + parser.yyVAL.statement = caseStmt + } + case 2802: + { + caseStmt := &ast.SearchCaseStmt{ + WhenCases: yyS[yypt-3].item.([]*ast.SearchWhenThenStmt), + } + if yyS[yypt-2].item != nil { + caseStmt.ElseCases = yyS[yypt-2].item.([]ast.StmtNode) + } + parser.yyVAL.statement = caseStmt + } + case 2803: + { + parser.yyVAL.statement = yyS[yypt-0].statement + } + case 2804: + { + parser.yyVAL.statement = &ast.ProcedureWhileStmt{ + Condition: yyS[yypt-4].expr.(ast.ExprNode), + Body: yyS[yypt-2].item.([]ast.StmtNode), + } + } + case 2805: + { + parser.yyVAL.statement = &ast.ProcedureRepeatStmt{ + Body: yyS[yypt-4].item.([]ast.StmtNode), + Condition: yyS[yypt-2].expr.(ast.ExprNode), + } + } + case 2806: + { + labelBlock := &ast.ProcedureLabelBlock{ + LabelName: yyS[yypt-3].ident, + Block: yyS[yypt-1].statement.(*ast.ProcedureBlock), + } + if yyS[yypt-0].ident != "" && (yyS[yypt-3].ident != yyS[yypt-0].ident) { + labelBlock.LabelError = true + labelBlock.LabelEnd = yyS[yypt-0].ident + } + parser.yyVAL.statement = labelBlock + } + case 2807: + { + parser.yyVAL.ident = "" + } + case 2808: + { + parser.yyVAL.ident = yyS[yypt-0].ident + } + case 2809: + { + labelLoop := &ast.ProcedureLabelLoop{ + LabelName: yyS[yypt-3].ident, + Block: yyS[yypt-1].statement.(ast.StmtNode), + } + if yyS[yypt-0].ident != "" && (yyS[yypt-3].ident != yyS[yypt-0].ident) { + labelLoop.LabelError = true + labelLoop.LabelEnd = yyS[yypt-0].ident + } + parser.yyVAL.statement = labelLoop + } + case 2810: + { + parser.yyVAL.statement = &ast.ProcedureJump{ + Name: yyS[yypt-0].ident, + IsLeave: false, + } + } + case 2811: + { + parser.yyVAL.statement = &ast.ProcedureJump{ + Name: yyS[yypt-0].ident, + IsLeave: true, + } + } + case 2824: + { + x := &ast.ProcedureInfo{ + IfNotExists: yyS[yypt-5].item.(bool), + ProcedureName: yyS[yypt-4].item.(*ast.TableName), + ProcedureParam: yyS[yypt-2].item.([]*ast.StoreParameter), + ProcedureBody: yyS[yypt-0].statement, + } + startOffset := parser.startOffset(&yyS[yypt]) + originStmt := yyS[yypt-0].statement + originStmt.SetText(parser.lexer.client, strings.TrimSpace(parser.src[startOffset:parser.yylval.offset])) + startOffset = parser.startOffset(&yyS[yypt-3]) + if parser.src[startOffset] == '(' { + startOffset++ + } + endOffset := parser.startOffset(&yyS[yypt-1]) + x.ProcedureParamStr = strings.TrimSpace(parser.src[startOffset:endOffset]) + parser.yyVAL.statement = x + } + case 2825: + { + parser.yyVAL.statement = &ast.DropProcedureStmt{ + IfExists: yyS[yypt-1].item.(bool), + ProcedureName: yyS[yypt-0].item.(*ast.TableName), + } + } + case 2826: + { + parser.yyVAL.statement = yyS[yypt-0].item.(*ast.CalibrateResourceStmt) + } + case 2827: + { + parser.yyVAL.item = &ast.CalibrateResourceStmt{} + } + case 2828: + { + parser.yyVAL.item = &ast.CalibrateResourceStmt{ + DynamicCalibrateResourceOptionList: yyS[yypt-0].item.([]*ast.DynamicCalibrateResourceOption), + } + } + case 2829: + { + parser.yyVAL.item = &ast.CalibrateResourceStmt{ + Tp: yyS[yypt-0].item.(ast.CalibrateResourceType), + } + } + case 2830: + { + parser.yyVAL.item = []*ast.DynamicCalibrateResourceOption{yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)} + } + case 2831: + { + if yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)[0].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp || + (len(yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)) > 1 && yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption)[1].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.DynamicCalibrateResourceOption), yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)) + } + case 2832: + { + if yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)[0].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp || + (len(yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)) > 1 && yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption)[1].Tp == yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption).Tp) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.DynamicCalibrateResourceOption), yyS[yypt-0].item.(*ast.DynamicCalibrateResourceOption)) + } + case 2833: + { + parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateStartTime, Ts: yyS[yypt-0].expr.(ast.ExprNode)} + } + case 2834: + { + parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateEndTime, Ts: yyS[yypt-0].expr.(ast.ExprNode)} + } + case 2835: + { + _, err := duration.ParseDuration(yyS[yypt-0].ident) + if err != nil { + yylex.AppendError(yylex.Errorf("The DURATION option is not a valid duration: %s", err.Error())) + return 1 + } + parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateDuration, StrValue: yyS[yypt-0].ident} + } + case 2836: + { + parser.yyVAL.item = &ast.DynamicCalibrateResourceOption{Tp: ast.CalibrateDuration, Ts: yyS[yypt-1].expr.(ast.ExprNode), Unit: yyS[yypt-0].item.(ast.TimeUnitType)} + } + case 2837: + { + parser.yyVAL.item = ast.TPCC + } + case 2838: + { + parser.yyVAL.item = ast.OLTPREADWRITE + } + case 2839: + { + parser.yyVAL.item = ast.OLTPREADONLY + } + case 2840: + { + parser.yyVAL.item = ast.OLTPWRITEONLY + } + case 2841: + { + parser.yyVAL.item = ast.TPCH10 + } + case 2842: + { + parser.yyVAL.statement = &ast.AddQueryWatchStmt{ + QueryWatchOptionList: yyS[yypt-0].item.([]*ast.QueryWatchOption), + } + } + case 2843: + { + parser.yyVAL.item = []*ast.QueryWatchOption{yyS[yypt-0].item.(*ast.QueryWatchOption)} + } + case 2844: + { + if !ast.CheckQueryWatchAppend(yyS[yypt-1].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-1].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) + } + case 2845: + { + if !ast.CheckQueryWatchAppend(yyS[yypt-2].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) { + yylex.AppendError(yylex.Errorf("Dupliated options specified")) + return 1 + } + parser.yyVAL.item = append(yyS[yypt-2].item.([]*ast.QueryWatchOption), yyS[yypt-0].item.(*ast.QueryWatchOption)) + } + case 2846: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchResourceGroup, StrValue: model.NewCIStr(yyS[yypt-0].ident)} + } + case 2847: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchResourceGroup, ExprValue: yyS[yypt-0].expr} + } + case 2848: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchAction, IntValue: yyS[yypt-0].item.(int32)} + } + case 2849: + { + parser.yyVAL.item = yyS[yypt-0].item.(*ast.QueryWatchOption) + } + case 2850: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: int32(model.WatchSimilar), ExprValue: yyS[yypt-0].expr} + } + case 2851: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: int32(model.WatchPlan), ExprValue: yyS[yypt-0].expr} + } + case 2852: + { + parser.yyVAL.item = &ast.QueryWatchOption{Tp: ast.QueryWatchType, IntValue: yyS[yypt-2].item.(int32), ExprValue: yyS[yypt-0].expr, BoolValue: true} + } + case 2853: + { + parser.yyVAL.statement = &ast.DropQueryWatchStmt{ + IntValue: yyS[yypt-0].item.(int64), + } + } + + } + + if !parser.lexer.skipPositionRecording { + yySetOffset(parser.yyVAL, parser.yyVAL.offset) + } + + if yyEx != nil && yyEx.Reduced(r, exState, parser.yyVAL) { + return -1 + } + goto yystack /* stack new state and value */ +} diff --git a/parser/parser.y b/pkg/parser/parser.y similarity index 99% rename from parser/parser.y rename to pkg/parser/parser.y index fce14ee23af8d..f7dd2e859e98f 100644 --- a/parser/parser.y +++ b/pkg/parser/parser.y @@ -29,14 +29,14 @@ import ( "strings" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/parser/duration" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/parser/duration" ) %} diff --git a/pkg/parser/parser_test.go b/pkg/parser/parser_test.go new file mode 100644 index 0000000000000..f9b8fb8681096 --- /dev/null +++ b/pkg/parser/parser_test.go @@ -0,0 +1,7472 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser_test + +import ( + "bytes" + "fmt" + "runtime" + "strings" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + . "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/parser/test_driver" + "github.com/stretchr/testify/require" +) + +func TestSimple(t *testing.T) { + p := parser.New() + + reservedKws := []string{ + "add", "all", "alter", "analyze", "and", "as", "asc", "between", "bigint", + "binary", "blob", "both", "by", "call", "cascade", "case", "change", "character", "check", "collate", + "column", "constraint", "convert", "create", "cross", "current_date", "current_time", + "current_timestamp", "current_user", "database", "databases", "day_hour", "day_microsecond", + "day_minute", "day_second", "decimal", "default", "delete", "desc", "describe", + "distinct", "distinctRow", "div", "double", "drop", "dual", "else", "enclosed", "escaped", + "exists", "explain", "false", "float", "fetch", "for", "force", "foreign", "from", + "fulltext", "grant", "group", "having", "hour_microsecond", "hour_minute", + "hour_second", "if", "ignore", "in", "index", "infile", "inner", "insert", "int", "into", "integer", + "interval", "is", "join", "key", "keys", "kill", "leading", "left", "like", "ilike", "limit", "lines", "load", + "localtime", "localtimestamp", "lock", "longblob", "longtext", "mediumblob", "maxvalue", "mediumint", "mediumtext", + "minute_microsecond", "minute_second", "mod", "not", "no_write_to_binlog", "null", "numeric", + "on", "option", "optionally", "or", "order", "outer", "partition", "precision", "primary", "procedure", "range", "read", "real", "recursive", + "references", "regexp", "rename", "repeat", "replace", "revoke", "restrict", "right", "rlike", + "schema", "schemas", "second_microsecond", "select", "set", "show", "smallint", + "starting", "table", "terminated", "then", "tinyblob", "tinyint", "tinytext", "to", + "trailing", "true", "union", "unique", "unlock", "unsigned", + "update", "use", "using", "utc_date", "values", "varbinary", "varchar", + "when", "where", "write", "xor", "year_month", "zerofill", + "generated", "virtual", "stored", "usage", + "delayed", "high_priority", "low_priority", + "cumeDist", "denseRank", "firstValue", "lag", "lastValue", "lead", "nthValue", "ntile", + "over", "percentRank", "rank", "row", "rows", "rowNumber", "window", "linear", + "match", "until", "placement", "tablesample", "failedLoginAttempts", "passwordLockTime", + // TODO: support the following keywords + // "with", + } + for _, kw := range reservedKws { + src := fmt.Sprintf("SELECT * FROM db.%s;", kw) + _, err := p.ParseOneStmt(src, "", "") + + require.NoErrorf(t, err, "source %s", src) + + src = fmt.Sprintf("SELECT * FROM %s.desc", kw) + _, err = p.ParseOneStmt(src, "", "") + require.NoErrorf(t, err, "source %s", src) + + src = fmt.Sprintf("SELECT t.%s FROM t", kw) + _, err = p.ParseOneStmt(src, "", "") + require.NoErrorf(t, err, "source %s", src) + } + + // Testcase for unreserved keywords + unreservedKws := []string{ + "auto_increment", "after", "begin", "bit", "bool", "boolean", "charset", "columns", "commit", + "date", "datediff", "datetime", "deallocate", "do", "from_days", "end", "engine", "engines", "execute", "extended", "first", "file", "full", + "local", "names", "offset", "password", "prepare", "quick", "rollback", "savepoint", "session", "signed", + "start", "global", "tables", "tablespace", "target", "text", "time", "timestamp", "tidb", "transaction", "truncate", "unknown", + "value", "warnings", "year", "now", "substr", "subpartition", "subpartitions", "substring", "mode", "any", "some", "user", "identified", + "collation", "comment", "avg_row_length", "checksum", "compression", "connection", "key_block_size", + "max_rows", "min_rows", "national", "quarter", "escape", "grants", "status", "fields", "triggers", "language", + "delay_key_write", "isolation", "partitions", "repeatable", "committed", "uncommitted", "only", "serializable", "level", + "curtime", "variables", "dayname", "version", "btree", "hash", "row_format", "dynamic", "fixed", "compressed", + "compact", "redundant", "1 sql_no_cache", "1 sql_cache", "action", "round", + "enable", "disable", "reverse", "space", "privileges", "get_lock", "release_lock", "sleep", "no", "greatest", "least", + "binlog", "hex", "unhex", "function", "indexes", "from_unixtime", "processlist", "events", "less", "than", "timediff", + "ln", "log", "log2", "log10", "timestampdiff", "pi", "proxy", "quote", "none", "super", "shared", "exclusive", + "always", "stats", "stats_meta", "stats_histogram", "stats_buckets", "stats_healthy", "tidb_version", "replication", "slave", "client", + "max_connections_per_hour", "max_queries_per_hour", "max_updates_per_hour", "max_user_connections", "event", "reload", "routine", "temporary", + "following", "preceding", "unbounded", "respect", "nulls", "current", "last", "against", "expansion", + "chain", "error", "general", "nvarchar", "pack_keys", "p", "shard_row_id_bits", "pre_split_regions", + "constraints", "role", "replicas", "policy", "s3", "strict", "running", "stop", "preserve", "placement", "attributes", "attribute", "resource", + "burstable", "calibrate", "rollup", + } + for _, kw := range unreservedKws { + src := fmt.Sprintf("SELECT %s FROM tbl;", kw) + _, err := p.ParseOneStmt(src, "", "") + require.NoErrorf(t, err, "source %s", src) + } + + // Testcase for prepared statement + src := "SELECT id+?, id+? from t;" + _, err := p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // Testcase for -- Comment and unary -- operator + src = "CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED); -- foo\nSelect --1 from foo;" + stmts, _, err := p.Parse(src, "", "") + require.NoError(t, err) + require.Len(t, stmts, 2) + + // Testcase for /*! xx */ + // See http://dev.mysql.com/doc/refman/5.7/en/comments.html + // Fix: https://github.com/pingcap/tidb/issues/971 + src = "/*!40101 SET character_set_client = utf8 */;" + stmts, _, err = p.Parse(src, "", "") + require.NoError(t, err) + require.Len(t, stmts, 1) + stmt := stmts[0] + _, ok := stmt.(*ast.SetStmt) + require.True(t, ok) + + // for issue #2017 + src = "insert into blobtable (a) values ('/*! truncated */');" + stmt, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + is, ok := stmt.(*ast.InsertStmt) + require.True(t, ok) + require.Len(t, is.Lists, 1) + require.Len(t, is.Lists[0], 1) + require.Equal(t, "/*! truncated */", is.Lists[0][0].(ast.ValueExpr).GetDatumString()) + + // Testcase for CONVERT(expr,type) + src = "SELECT CONVERT('111', SIGNED);" + st, err := p.ParseOneStmt(src, "", "") + require.NoError(t, err) + ss, ok := st.(*ast.SelectStmt) + require.True(t, ok) + require.Len(t, ss.Fields.Fields, 1) + cv, ok := ss.Fields.Fields[0].Expr.(*ast.FuncCastExpr) + require.True(t, ok) + require.Equal(t, ast.CastConvertFunction, cv.FunctionType) + + // for query start with comment + srcs := []string{ + "/* some comments */ SELECT CONVERT('111', SIGNED) ;", + "/* some comments */ /*comment*/ SELECT CONVERT('111', SIGNED) ;", + "SELECT /*comment*/ CONVERT('111', SIGNED) ;", + "SELECT CONVERT('111', /*comment*/ SIGNED) ;", + "SELECT CONVERT('111', SIGNED) /*comment*/;", + } + for _, src := range srcs { + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + _, ok = st.(*ast.SelectStmt) + require.True(t, ok) + } + + // for issue #961 + src = "create table t (c int key);" + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + cs, ok := st.(*ast.CreateTableStmt) + require.True(t, ok) + require.Len(t, cs.Cols, 1) + require.Len(t, cs.Cols[0].Options, 1) + require.Equal(t, ast.ColumnOptionPrimaryKey, cs.Cols[0].Options[0].Tp) + + // for issue #4497 + src = "create table t1(a NVARCHAR(100));" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // for issue 2803 + src = "use quote;" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // issue #4354 + src = "select b'';" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + src = "select B'';" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // src = "select 0b'';" + // _, err = p.ParseOneStmt(src, "", "") + // require.Error(t, err) + + // for #4909, support numericType `signed` filedOpt. + src = "CREATE TABLE t(_sms smallint signed, _smu smallint unsigned);" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // for #7371, support NATIONAL CHARACTER + // reference link: https://dev.mysql.com/doc/refman/5.7/en/charset-national.html + src = "CREATE TABLE t(c1 NATIONAL CHARACTER(10));" + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + src = `CREATE TABLE t(a tinyint signed, + b smallint signed, + c mediumint signed, + d int signed, + e int1 signed, + f int2 signed, + g int3 signed, + h int4 signed, + i int8 signed, + j integer signed, + k bigint signed, + l bool signed, + m boolean signed + );` + + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + ct, ok := st.(*ast.CreateTableStmt) + require.True(t, ok) + for _, col := range ct.Cols { + require.Equal(t, uint(0), col.Tp.GetFlag()&mysql.UnsignedFlag) + } + + // for issue #4006 + src = `insert into tb(v) (select v from tb);` + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // for issue #34642 + src = `SELECT a as c having c = a;` + _, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + + // for issue #9823 + src = "SELECT 9223372036854775807;" + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + sel, ok := st.(*ast.SelectStmt) + require.True(t, ok) + expr := sel.Fields.Fields[0] + vExpr := expr.Expr.(*test_driver.ValueExpr) + require.Equal(t, test_driver.KindInt64, vExpr.Kind()) + src = "SELECT 9223372036854775808;" + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + sel, ok = st.(*ast.SelectStmt) + require.True(t, ok) + expr = sel.Fields.Fields[0] + vExpr = expr.Expr.(*test_driver.ValueExpr) + require.Equal(t, test_driver.KindUint64, vExpr.Kind()) + + src = `select 99e+r10 from t1;` + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + sel, ok = st.(*ast.SelectStmt) + require.True(t, ok) + bExpr, ok := sel.Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) + require.True(t, ok) + require.Equal(t, opcode.Plus, bExpr.Op) + require.Equal(t, "99e", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) + require.Equal(t, "r10", bExpr.R.(*ast.ColumnNameExpr).Name.Name.O) + + src = `select t./*123*/*,@c3:=0 from t order by t.c1;` + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + sel, ok = st.(*ast.SelectStmt) + require.True(t, ok) + require.Equal(t, "t", sel.Fields.Fields[0].WildCard.Table.O) + varExpr, ok := sel.Fields.Fields[1].Expr.(*ast.VariableExpr) + require.True(t, ok) + require.Equal(t, "c3", varExpr.Name) + + src = `select t.1e from test.t;` + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + sel, ok = st.(*ast.SelectStmt) + require.True(t, ok) + colExpr, ok := sel.Fields.Fields[0].Expr.(*ast.ColumnNameExpr) + require.True(t, ok) + require.Equal(t, "t", colExpr.Name.Table.O) + require.Equal(t, "1e", colExpr.Name.Name.O) + tName := sel.From.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) + require.Equal(t, "test", tName.Schema.O) + require.Equal(t, "t", tName.Name.O) + + src = "select t. `a` > 10 from t;" + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + bExpr, ok = st.(*ast.SelectStmt).Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) + require.True(t, ok) + require.Equal(t, opcode.GT, bExpr.Op) + require.Equal(t, "a", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) + require.Equal(t, "t", bExpr.L.(*ast.ColumnNameExpr).Name.Table.O) + require.Equal(t, int64(10), bExpr.R.(ast.ValueExpr).GetValue().(int64)) + + p.SetSQLMode(mysql.ModeANSIQuotes) + src = `select t."dot"=10 from t;` + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + bExpr, ok = st.(*ast.SelectStmt).Fields.Fields[0].Expr.(*ast.BinaryOperationExpr) + require.True(t, ok) + require.Equal(t, opcode.EQ, bExpr.Op) + require.Equal(t, "dot", bExpr.L.(*ast.ColumnNameExpr).Name.Name.O) + require.Equal(t, "t", bExpr.L.(*ast.ColumnNameExpr).Name.Table.O) + require.Equal(t, int64(10), bExpr.R.(ast.ValueExpr).GetValue().(int64)) +} + +func TestSpecialComments(t *testing.T) { + p := parser.New() + + // 1. Make sure /*! ... */ respects the same SQL mode. + _, err := p.ParseOneStmt(`SELECT /*! '\' */;`, "", "") + require.Error(t, err) + + p.SetSQLMode(mysql.ModeNoBackslashEscapes) + st, err := p.ParseOneStmt(`SELECT /*! '\' */;`, "", "") + require.NoError(t, err) + require.IsType(t, &ast.SelectStmt{}, st) + + // 2. Make sure multiple statements inside /*! ... */ will not crash + // (this is issue #330) + stmts, _, err := p.Parse("/*! SET x = 1; SELECT 2 */", "", "") + require.NoError(t, err) + require.Len(t, stmts, 2) + require.IsType(t, &ast.SetStmt{}, stmts[0]) + require.Equal(t, "/*! SET x = 1;", stmts[0].Text()) + require.IsType(t, &ast.SelectStmt{}, stmts[1]) + require.Equal(t, " SELECT 2 */", stmts[1].Text()) + // ^ not sure if correct approach; having multiple statements in MySQL is a syntax error. + + // 3. Make sure invalid text won't cause infinite loop + // (this is issue #336) + st, err = p.ParseOneStmt("SELECT /*+ 😅 */ SLEEP(1);", "", "") + require.NoError(t, err) + sel, ok := st.(*ast.SelectStmt) + require.True(t, ok) + require.Len(t, sel.TableHints, 0) +} + +type testCase struct { + src string + ok bool + restore string +} + +type testErrMsgCase struct { + src string + err error +} + +func RunTest(t *testing.T, table []testCase, enableWindowFunc bool) { + p := parser.New() + p.EnableWindowFunc(enableWindowFunc) + for _, tbl := range table { + _, _, err := p.Parse(tbl.src, "", "") + if !tbl.ok { + require.Errorf(t, err, "source %v", tbl.src, errors.Trace(err)) + continue + } + require.NoErrorf(t, err, "source %v", tbl.src, errors.Trace(err)) + // restore correctness test + if tbl.ok { + RunRestoreTest(t, tbl.src, tbl.restore, enableWindowFunc) + } + } +} + +func RunRestoreTest(t *testing.T, sourceSQLs, expectSQLs string, enableWindowFunc bool) { + var sb strings.Builder + p := parser.New() + p.EnableWindowFunc(enableWindowFunc) + comment := fmt.Sprintf("source %v", sourceSQLs) + stmts, _, err := p.Parse(sourceSQLs, "", "") + require.NoErrorf(t, err, "source %v", sourceSQLs) + restoreSQLs := "" + for _, stmt := range stmts { + sb.Reset() + err = stmt.Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) + require.NoError(t, err, comment) + restoreSQL := sb.String() + comment = fmt.Sprintf("source %v; restore %v", sourceSQLs, restoreSQL) + restoreStmt, err := p.ParseOneStmt(restoreSQL, "", "") + require.NoError(t, err, comment) + CleanNodeText(stmt) + CleanNodeText(restoreStmt) + require.Equal(t, stmt, restoreStmt, comment) + if restoreSQLs != "" { + restoreSQLs += "; " + } + restoreSQLs += restoreSQL + } + require.Equalf(t, expectSQLs, restoreSQLs, "restore %v; expect %v", restoreSQLs, expectSQLs) +} + +func RunTestInRealAsFloatMode(t *testing.T, table []testCase, enableWindowFunc bool) { + p := parser.New() + p.EnableWindowFunc(enableWindowFunc) + p.SetSQLMode(mysql.ModeRealAsFloat) + for _, tbl := range table { + _, _, err := p.Parse(tbl.src, "", "") + comment := fmt.Sprintf("source %v", tbl.src) + if !tbl.ok { + require.Error(t, err, comment) + continue + } + require.NoError(t, err, comment) + // restore correctness test + if tbl.ok { + RunRestoreTestInRealAsFloatMode(t, tbl.src, tbl.restore, enableWindowFunc) + } + } +} + +func RunRestoreTestInRealAsFloatMode(t *testing.T, sourceSQLs, expectSQLs string, enableWindowFunc bool) { + var sb strings.Builder + p := parser.New() + p.EnableWindowFunc(enableWindowFunc) + p.SetSQLMode(mysql.ModeRealAsFloat) + comment := fmt.Sprintf("source %v", sourceSQLs) + stmts, _, err := p.Parse(sourceSQLs, "", "") + require.NoError(t, err, comment) + restoreSQLs := "" + for _, stmt := range stmts { + sb.Reset() + err = stmt.Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) + require.NoError(t, err, comment) + restoreSQL := sb.String() + comment = fmt.Sprintf("source %v; restore %v", sourceSQLs, restoreSQL) + restoreStmt, err := p.ParseOneStmt(restoreSQL, "", "") + require.NoError(t, err, comment) + CleanNodeText(stmt) + CleanNodeText(restoreStmt) + require.Equal(t, stmt, restoreStmt, comment) + if restoreSQLs != "" { + restoreSQLs += "; " + } + restoreSQLs += restoreSQL + } + require.Equal(t, expectSQLs, restoreSQLs, "restore %v; expect %v", restoreSQLs, expectSQLs) +} + +func RunErrMsgTest(t *testing.T, table []testErrMsgCase) { + p := parser.New() + for _, tbl := range table { + _, _, err := p.Parse(tbl.src, "", "") + comment := fmt.Sprintf("source %v", tbl.src) + if tbl.err != nil { + require.True(t, terror.ErrorEqual(err, tbl.err), comment) + } else { + require.NoError(t, err, comment) + } + } +} + +func TestDMLStmt(t *testing.T) { + table := []testCase{ + {"", true, ""}, + {";", true, ""}, + {"INSERT INTO foo VALUES (1234)", true, "INSERT INTO `foo` VALUES (1234)"}, + {"INSERT INTO foo VALUES (1234, 5678)", true, "INSERT INTO `foo` VALUES (1234,5678)"}, + {"INSERT INTO t1 (SELECT * FROM t2)", true, "INSERT INTO `t1` (SELECT * FROM `t2`)"}, + {"INSERT INTO t partition (p0) values(1234)", true, "INSERT INTO `t` PARTITION(`p0`) VALUES (1234)"}, + {"REPLACE INTO t partition (p0) values(1234)", true, "REPLACE INTO `t` PARTITION(`p0`) VALUES (1234)"}, + {"INSERT INTO t partition (p0, p1, p2) values(1234)", true, "INSERT INTO `t` PARTITION(`p0`, `p1`, `p2`) VALUES (1234)"}, + {"REPLACE INTO t partition (p0, p1, p2) values(1234)", true, "REPLACE INTO `t` PARTITION(`p0`, `p1`, `p2`) VALUES (1234)"}, + // 15 + {"INSERT INTO foo VALUES (1 || 2)", true, "INSERT INTO `foo` VALUES (1 OR 2)"}, + {"INSERT INTO foo VALUES (1 | 2)", true, "INSERT INTO `foo` VALUES (1|2)"}, + {"INSERT INTO foo VALUES (false || true)", true, "INSERT INTO `foo` VALUES (FALSE OR TRUE)"}, + {"INSERT INTO foo VALUES (bar(5678))", true, "INSERT INTO `foo` VALUES (BAR(5678))"}, + // 20 + {"INSERT INTO foo VALUES ()", true, "INSERT INTO `foo` VALUES ()"}, + {"SELECT * FROM t", true, "SELECT * FROM `t`"}, + {"SELECT * FROM t AS u", true, "SELECT * FROM `t` AS `u`"}, + // 25 + {"SELECT * FROM t, v", true, "SELECT * FROM (`t`) JOIN `v`"}, + {"SELECT * FROM t AS u, v", true, "SELECT * FROM (`t` AS `u`) JOIN `v`"}, + {"SELECT * FROM t, v AS w", true, "SELECT * FROM (`t`) JOIN `v` AS `w`"}, + {"SELECT * FROM t AS u, v AS w", true, "SELECT * FROM (`t` AS `u`) JOIN `v` AS `w`"}, + {"SELECT * FROM foo, bar, foo", true, "SELECT * FROM ((`foo`) JOIN `bar`) JOIN `foo`"}, + // 30 + {"SELECT DISTINCTS * FROM t", false, ""}, + {"SELECT DISTINCT * FROM t", true, "SELECT DISTINCT * FROM `t`"}, + {"SELECT DISTINCTROW * FROM t", true, "SELECT DISTINCT * FROM `t`"}, + {"SELECT ALL * FROM t", true, "SELECT ALL * FROM `t`"}, + {"SELECT DISTINCT ALL * FROM t", false, ""}, + {"SELECT DISTINCTROW ALL * FROM t", false, ""}, + {"INSERT INTO foo (a) VALUES (42)", true, "INSERT INTO `foo` (`a`) VALUES (42)"}, + {"INSERT INTO foo (a,) VALUES (42,)", false, ""}, + // 35 + {"INSERT INTO foo (a,b) VALUES (42,314)", true, "INSERT INTO `foo` (`a`,`b`) VALUES (42,314)"}, + {"INSERT INTO foo (a,b,) VALUES (42,314)", false, ""}, + {"INSERT INTO foo (a,b,) VALUES (42,314,)", false, ""}, + {"INSERT INTO foo () VALUES ()", true, "INSERT INTO `foo` () VALUES ()"}, + {"INSERT INTO foo VALUE ()", true, "INSERT INTO `foo` VALUES ()"}, + + // for issue 2402 + {"INSERT INTO tt VALUES (01000001783);", true, "INSERT INTO `tt` VALUES (1000001783)"}, + {"INSERT INTO tt VALUES (default);", true, "INSERT INTO `tt` VALUES (DEFAULT)"}, + + {"REPLACE INTO foo VALUES (1 || 2)", true, "REPLACE INTO `foo` VALUES (1 OR 2)"}, + {"REPLACE INTO foo VALUES (1 | 2)", true, "REPLACE INTO `foo` VALUES (1|2)"}, + {"REPLACE INTO foo VALUES (false || true)", true, "REPLACE INTO `foo` VALUES (FALSE OR TRUE)"}, + {"REPLACE INTO foo VALUES (bar(5678))", true, "REPLACE INTO `foo` VALUES (BAR(5678))"}, + {"REPLACE INTO foo VALUES ()", true, "REPLACE INTO `foo` VALUES ()"}, + {"REPLACE INTO foo (a,b) VALUES (42,314)", true, "REPLACE INTO `foo` (`a`,`b`) VALUES (42,314)"}, + {"REPLACE INTO foo (a,b,) VALUES (42,314)", false, ""}, + {"REPLACE INTO foo (a,b,) VALUES (42,314,)", false, ""}, + {"REPLACE INTO foo () VALUES ()", true, "REPLACE INTO `foo` () VALUES ()"}, + {"REPLACE INTO foo VALUE ()", true, "REPLACE INTO `foo` VALUES ()"}, + // 40 + {`SELECT stuff.id + FROM stuff + WHERE stuff.value >= ALL (SELECT stuff.value + FROM stuff)`, true, "SELECT `stuff`.`id` FROM `stuff` WHERE `stuff`.`value`>=ALL (SELECT `stuff`.`value` FROM `stuff`)"}, + {"BEGIN", true, "START TRANSACTION"}, + {"START TRANSACTION", true, "START TRANSACTION"}, + // 45 + {"COMMIT", true, "COMMIT"}, + {"COMMIT AND NO CHAIN", true, "COMMIT"}, + {"COMMIT NO RELEASE", true, "COMMIT"}, + {"COMMIT AND NO CHAIN NO RELEASE", true, "COMMIT"}, + {"COMMIT AND NO CHAIN RELEASE", true, "COMMIT RELEASE"}, + {"COMMIT AND CHAIN NO RELEASE", true, "COMMIT AND CHAIN"}, + {"COMMIT AND CHAIN RELEASE", false, ""}, + {"ROLLBACK", true, "ROLLBACK"}, + {"ROLLBACK AND NO CHAIN", true, "ROLLBACK"}, + {"ROLLBACK NO RELEASE", true, "ROLLBACK"}, + {"ROLLBACK AND NO CHAIN NO RELEASE", true, "ROLLBACK"}, + {"ROLLBACK AND NO CHAIN RELEASE", true, "ROLLBACK RELEASE"}, + {"ROLLBACK AND CHAIN NO RELEASE", true, "ROLLBACK AND CHAIN"}, + {"ROLLBACK AND CHAIN RELEASE", false, ""}, + {`BEGIN; + INSERT INTO foo VALUES (42, 3.14); + INSERT INTO foo VALUES (-1, 2.78); + COMMIT;`, true, "START TRANSACTION; INSERT INTO `foo` VALUES (42,3.14); INSERT INTO `foo` VALUES (-1,2.78); COMMIT"}, + {`BEGIN; + INSERT INTO tmp SELECT * from bar; + SELECT * from tmp; + ROLLBACK;`, true, "START TRANSACTION; INSERT INTO `tmp` SELECT * FROM `bar`; SELECT * FROM `tmp`; ROLLBACK"}, + {"SAVEPOINT x", true, "SAVEPOINT x"}, + {"RELEASE SAVEPOINT x", true, "RELEASE SAVEPOINT x"}, + {"ROLLBACK TO x", true, "ROLLBACK TO x"}, + {"ROLLBACK TO X", true, "ROLLBACK TO X"}, + {"ROLLBACK TO SAVEPOINT x", true, "ROLLBACK TO x"}, + + // table statement + {"TABLE t", true, "TABLE `t`"}, + {"(TABLE t)", true, "(TABLE `t`)"}, + {"TABLE t1, t2", false, ""}, + {"TABLE t ORDER BY b", true, "TABLE `t` ORDER BY `b`"}, + {"TABLE t LIMIT 3", true, "TABLE `t` LIMIT 3"}, + {"TABLE t ORDER BY b LIMIT 3", true, "TABLE `t` ORDER BY `b` LIMIT 3"}, + {"TABLE t ORDER BY b LIMIT 3 OFFSET 2", true, "TABLE `t` ORDER BY `b` LIMIT 2,3"}, + {"TABLE t ORDER BY b LIMIT 2,3", true, "TABLE `t` ORDER BY `b` LIMIT 2,3"}, + {"INSERT INTO ta TABLE tb", true, "INSERT INTO `ta` TABLE `tb`"}, + {"INSERT INTO t.a TABLE t.b", true, "INSERT INTO `t`.`a` TABLE `t`.`b`"}, + {"REPLACE INTO ta TABLE tb", true, "REPLACE INTO `ta` TABLE `tb`"}, + {"REPLACE INTO t.a TABLE t.b", true, "REPLACE INTO `t`.`a` TABLE `t`.`b`"}, + {"TABLE t1 INTO OUTFILE 'a.txt'", true, "TABLE `t1` INTO OUTFILE 'a.txt'"}, + {"TABLE t ORDER BY a INTO OUTFILE '/tmp/abc'", true, "TABLE `t` ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, + {"CREATE TABLE t.a TABLE t.b", true, "CREATE TABLE `t`.`a` AS TABLE `t`.`b`"}, + {"CREATE TABLE ta TABLE tb", true, "CREATE TABLE `ta` AS TABLE `tb`"}, + {"CREATE TABLE ta (x INT) TABLE tb", true, "CREATE TABLE `ta` (`x` INT) AS TABLE `tb`"}, + {"CREATE VIEW v AS TABLE t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS TABLE `t`"}, + {"CREATE VIEW v AS (TABLE t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (TABLE `t`)"}, + {"SELECT * FROM t1 WHERE a IN (TABLE t2)", true, "SELECT * FROM `t1` WHERE `a` IN (TABLE `t2`)"}, + + // values statement + {"VALUES ROW(1)", true, "VALUES ROW(1)"}, + {"VALUES ROW()", true, "VALUES ROW()"}, + {"VALUES ROW(1, default)", true, "VALUES ROW(1,DEFAULT)"}, + {"VALUES ROW(1), ROW(2,3)", true, "VALUES ROW(1), ROW(2,3)"}, + {"VALUES (1,2)", false, ""}, + {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8)", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8)"}, + {"VALUES ROW(1,s,3.1), ROW(5,y,9.9)", true, "VALUES ROW(1,`s`,3.1), ROW(5,`y`,9.9)"}, + {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) LIMIT 3", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) LIMIT 3"}, + {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY a", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY `a`"}, + {"VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY a LIMIT 2", true, "VALUES ROW(1,-2,3), ROW(5,7,9), ROW(4,6,8) ORDER BY `a` LIMIT 2"}, + {"VALUES ROW(1,-2,3), ROW(5,7,9) INTO OUTFILE 'a.txt'", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTO OUTFILE 'a.txt'"}, + {"VALUES ROW(1,-2,3), ROW(5,7,9) ORDER BY a INTO OUTFILE '/tmp/abc'", true, "VALUES ROW(1,-2,3), ROW(5,7,9) ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, + {"CREATE TABLE ta VALUES ROW(1)", true, "CREATE TABLE `ta` AS VALUES ROW(1)"}, + {"CREATE TABLE ta AS VALUES ROW(1)", true, "CREATE TABLE `ta` AS VALUES ROW(1)"}, + {"CREATE VIEW a AS VALUES ROW(1)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `a` AS VALUES ROW(1)"}, + + // qualified select + {"SELECT a.b.c FROM t", true, "SELECT `a`.`b`.`c` FROM `t`"}, + {"SELECT a.b.*.c FROM t", false, ""}, + {"SELECT a.b.* FROM t", true, "SELECT `a`.`b`.* FROM `t`"}, + {"SELECT a FROM t", true, "SELECT `a` FROM `t`"}, + {"SELECT a.b.c.d FROM t", false, ""}, + + // do statement + {"DO 1", true, "DO 1"}, + {"DO 1, sleep(1)", true, "DO 1, SLEEP(1)"}, + {"DO 1 from t", false, ""}, + + // load data + {"load data local infile '/tmp/t.csv' into table t1 fields terminated by ',' optionally enclosed by '\"' ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' IGNORE 1 LINES"}, + {"load data infile '/tmp/t.csv' into table t", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t`"}, + {"load data infile '/tmp/t.csv' into table t character set utf8", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` CHARACTER SET utf8"}, + {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, + {"load data infile '/tmp/t.csv' into table t columns terminated by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, + {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, + {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, + {"load data infile '/tmp/t.csv' into table t lines starting by 'ab'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` LINES STARTING BY 'ab'"}, + {"load data infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy'"}, + {"load data infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy'"}, + {"load data infile '/tmp/t.csv' into table t terminated by 'xy' fields terminated by 'ab'", false, ""}, + {"load data local infile '/tmp/t.csv' into table t", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t`"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, + {"load data local infile '/tmp/t.csv' into table t columns terminated by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab'"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, + {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' enclosed by 'b' escaped by '*'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*'"}, + {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab'"}, + {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy'"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy'"}, + {"load data local infile '/tmp/t.csv' into table t terminated by 'xy' fields terminated by 'ab'", false, ""}, + {"load data infile '/tmp/t.csv' into table t (a,b)", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t columns terminated by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' enclosed by 'b' escaped by '*' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t character set utf8 fields terminated by 'ab' lines terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` CHARACTER SET utf8 FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' lines terminated by 'xy' (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' LINES TERMINATED BY 'xy' (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t (a,b) fields terminated by 'ab'", false, ""}, + {"load data local infile '/tmp/t.csv' into table t ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` IGNORE 1 LINES"}, + {"load data local infile '/tmp/t.csv' into table t ignore -1 lines", false, ""}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' (a,b) ignore 1 lines", false, ""}, + {"load data local infile '/tmp/t.csv' into table t lines starting by 'ab' terminated by 'xy' ignore 1 lines", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` LINES STARTING BY 'ab' TERMINATED BY 'xy' IGNORE 1 LINES"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by '*' ignore 1 lines (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '*' IGNORE 1 LINES (`a`,`b`)"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' escaped by ''", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY ''"}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk'"}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B' enclosed by X'0D';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk' ENCLOSED BY '\r'"}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by X'6B6B' enclosed by X'0D0D';", false, ""}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk'"}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011' enclosed by B'1101';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` FIELDS TERMINATED BY 'kk' ENCLOSED BY '\r'"}, + {"load data local infile '~/1.csv' into table `t_ascii` fields terminated by B'110101101101011' enclosed by B'110100001101';", false, ""}, + {"load data local infile '~/1.csv' into table `t_ascii` lines starting by B'110101101101011' terminated by B'110101101101011';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` LINES STARTING BY 'kk' TERMINATED BY 'kk'"}, + {"load data local infile '~/1.csv' into table `t_ascii` lines starting by X'6B6B' terminated by X'6B6B';", true, "LOAD DATA LOCAL INFILE '~/1.csv' IGNORE INTO TABLE `t_ascii` LINES STARTING BY 'kk' TERMINATED BY 'kk'"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' enclosed by 'b' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b'"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' escaped by '' enclosed by 'b'", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY ''"}, + {"load data local infile '/tmp/t.csv' into table t fields terminated by 'ab' escaped by '' enclosed by 'b' SET b = CAST(CONV(MID(@var1, 3, LENGTH(@var1)-3), 2, 10) AS UNSIGNED)", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t` FIELDS TERMINATED BY 'ab' ENCLOSED BY 'b' ESCAPED BY '' SET `b`=CAST(CONV(MID(@`var1`, 3, LENGTH(@`var1`)-3), 2, 10) AS UNSIGNED)"}, + + {"load data infile '/tmp/t.csv' into table t fields enclosed by ''", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ENCLOSED BY ''"}, + {"load data infile '/tmp/t.csv' into table t fields enclosed by 'a'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ENCLOSED BY 'a'"}, + {"load data infile '/tmp/t.csv' into table t fields enclosed by 'aa'", false, ""}, + {"load data infile '/tmp/t.csv' into table t fields escaped by ''", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ESCAPED BY ''"}, + {"load data infile '/tmp/t.csv' into table t fields escaped by 'a'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS ESCAPED BY 'a'"}, + {"load data infile '/tmp/t.csv' into table t fields escaped by 'aa'", false, ""}, + + {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @dummy, column2, @dummy, column3)", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`dummy`,`column2`,@`dummy`,`column3`)"}, + {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, + {"LOAD DATA INFILE 'file.txt' INTO TABLE t1 (column1, @var1, @var2) SET column2 = @var1/100, column3 = DEFAULT, column4=CURRENT_TIMESTAMP, column5=@var2+1", true, "LOAD DATA INFILE 'file.txt' INTO TABLE `t1` (`column1`,@`var1`,@`var2`) SET `column2`=@`var1`/100, `column3`=DEFAULT, `column4`=CURRENT_TIMESTAMP(), `column5`=@`var2`+1"}, + + {"LOAD DATA INFILE '/tmp/t.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, + {"LOAD DATA LOCAL INFILE '/tmp/t.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, + {"LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' IGNORE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, + {"LOAD DATA LOCAL INFILE '/tmp/t.csv' REPLACE INTO TABLE t1 FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';", true, "LOAD DATA LOCAL INFILE '/tmp/t.csv' REPLACE INTO TABLE `t1` FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'"}, + + {"load data infile 's3://bucket-name/t.csv' into table t", true, "LOAD DATA INFILE 's3://bucket-name/t.csv' INTO TABLE `t`"}, + {"load data infile '/tmp/t.csv' into table t fields defined null by 'nil'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY 'nil'"}, + {"load data infile '/tmp/t.csv' into table t fields defined null by X'00'", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY '\x00'"}, + {"load data infile '/tmp/t.csv' into table t fields defined null by 'NULL' optionally enclosed ignore 1 lines", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` FIELDS DEFINED NULL BY 'NULL' OPTIONALLY ENCLOSED IGNORE 1 LINES"}, + {"load data infile '/tmp/t.csv' format 'delimited data' into table t (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE '/tmp/t.csv' FORMAT 'delimited data' INTO TABLE `t` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, + {"load data local infile '/tmp/t.sql' format 'sql file' replace into table t (a,b)", true, "LOAD DATA LOCAL INFILE '/tmp/t.sql' FORMAT 'sql file' REPLACE INTO TABLE `t` (`a`,`b`)"}, + {"load data infile '/tmp/t.parquet' format 'parquet' into table t (column1, @var1) SET column2 = @var1/100", true, "LOAD DATA INFILE '/tmp/t.parquet' FORMAT 'parquet' INTO TABLE `t` (`column1`,@`var1`) SET `column2`=@`var1`/100"}, + + {"load data infile '/tmp/t.csv' into table t with detached", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH detached"}, + // we must add "`" to table name, since the offset of restored sql might be different with the source + {"load data infile '/tmp/t.csv' into table `t` with threads=10", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH threads=10"}, + {"load data infile '/tmp/t.csv' into table `t` with threads=10, detached", true, "LOAD DATA INFILE '/tmp/t.csv' INTO TABLE `t` WITH threads=10, detached"}, + + // IMPORT INTO + {"import into t from '/file.csv'", true, "IMPORT INTO `t` FROM '/file.csv'"}, + {"import into t (a,b) from '/file.csv'", true, "IMPORT INTO `t` (`a`,`b`) FROM '/file.csv'"}, + {"import into t (a,@1) from '/file.csv'", true, "IMPORT INTO `t` (`a`,@`1`) FROM '/file.csv'"}, + {"import into t (a,@1) set b=@1+100 from '/file.csv'", true, "IMPORT INTO `t` (`a`,@`1`) SET `b`=@`1`+100 FROM '/file.csv'"}, + {"import into t from '/file.csv' format 'sql file'", true, "IMPORT INTO `t` FROM '/file.csv' FORMAT 'sql file'"}, + {"import into t from '/file.csv' with detached", true, "IMPORT INTO `t` FROM '/file.csv' WITH detached"}, + {"import into `t` from '/file.csv' with thread=1", true, "IMPORT INTO `t` FROM '/file.csv' WITH thread=1"}, + {"import into `t` from '/file.csv' with detached, thread=1", true, "IMPORT INTO `t` FROM '/file.csv' WITH detached, thread=1"}, + + // select for update/share + {"select * from t for update", true, "SELECT * FROM `t` FOR UPDATE"}, + {"select * from t for share", true, "SELECT * FROM `t` FOR SHARE"}, + {"select * from t for update nowait", true, "SELECT * FROM `t` FOR UPDATE NOWAIT"}, + {"select * from t for update wait 5", true, "SELECT * FROM `t` FOR UPDATE WAIT 5"}, + {"select * from t limit 1 for update wait 11", true, "SELECT * FROM `t` LIMIT 1 FOR UPDATE WAIT 11"}, + {"select * from t for share nowait", true, "SELECT * FROM `t` FOR SHARE NOWAIT"}, + {"select * from t for update skip locked", true, "SELECT * FROM `t` FOR UPDATE SKIP LOCKED"}, + {"select * from t for share skip locked", true, "SELECT * FROM `t` FOR SHARE SKIP LOCKED"}, + {"select * from t lock in share mode", true, "SELECT * FROM `t` FOR SHARE"}, + {"select * from t lock in share mode nowait", false, ""}, + {"select * from t lock in share mode skip locked", false, ""}, + + {"select * from t for update of t", true, "SELECT * FROM `t` FOR UPDATE OF `t`"}, + {"select * from t for share of t", true, "SELECT * FROM `t` FOR SHARE OF `t`"}, + {"select * from t for update of t nowait", true, "SELECT * FROM `t` FOR UPDATE OF `t` NOWAIT"}, + {"select * from t for update of t wait 5", true, "SELECT * FROM `t` FOR UPDATE OF `t` WAIT 5"}, + {"select * from t limit 1 for update of t wait 11", true, "SELECT * FROM `t` LIMIT 1 FOR UPDATE OF `t` WAIT 11"}, + {"select * from t for share of t nowait", true, "SELECT * FROM `t` FOR SHARE OF `t` NOWAIT"}, + {"select * from t for update of t skip locked", true, "SELECT * FROM `t` FOR UPDATE OF `t` SKIP LOCKED"}, + {"select * from t for share of t skip locked", true, "SELECT * FROM `t` FOR SHARE OF `t` SKIP LOCKED"}, + + // select into outfile + {"select a, b from t into outfile '/tmp/result.txt'", true, "SELECT `a`,`b` FROM `t` INTO OUTFILE '/tmp/result.txt'"}, + {"select a from t order by a into outfile '/tmp/abc'", true, "SELECT `a` FROM `t` ORDER BY `a` INTO OUTFILE '/tmp/abc'"}, + {"select 1 into outfile '/tmp/1.csv'", true, "SELECT 1 INTO OUTFILE '/tmp/1.csv'"}, + {"select 1 for update into outfile '/tmp/1.csv'", true, "SELECT 1 FOR UPDATE INTO OUTFILE '/tmp/1.csv'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ','", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ','"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' lines terminated BY '\n'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' LINES TERMINATED BY '\n'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"' lines terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES TERMINATED BY '\r'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"' lines terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' optionally enclosed BY '\"' lines starting by 'xy' terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"' LINES STARTING BY 'xy' TERMINATED BY '\r'"}, + {"select a,b,a+b from t into outfile '/tmp/result.txt' fields terminated BY ',' enclosed BY '\"' lines starting by 'xy' terminated BY '\r'", true, "SELECT `a`,`b`,`a`+`b` FROM `t` INTO OUTFILE '/tmp/result.txt' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES STARTING BY 'xy' TERMINATED BY '\r'"}, + + // from join + {"SELECT * from t1, t2, t3", true, "SELECT * FROM ((`t1`) JOIN `t2`) JOIN `t3`"}, + {"select * from t1 join t2 left join t3 on t2.id = t3.id", true, "SELECT * FROM (`t1` JOIN `t2`) LEFT JOIN `t3` ON `t2`.`id`=`t3`.`id`"}, + {"select * from t1 right join t2 on t1.id = t2.id left join t3 on t3.id = t2.id", true, "SELECT * FROM (`t1` RIGHT JOIN `t2` ON `t1`.`id`=`t2`.`id`) LEFT JOIN `t3` ON `t3`.`id`=`t2`.`id`"}, + {"select * from t1 right join t2 on t1.id = t2.id left join t3", false, ""}, + {"select * from t1 join t2 left join t3 using (id)", true, "SELECT * FROM (`t1` JOIN `t2`) LEFT JOIN `t3` USING (`id`)"}, + {"select * from t1 right join t2 using (id) left join t3 using (id)", true, "SELECT * FROM (`t1` RIGHT JOIN `t2` USING (`id`)) LEFT JOIN `t3` USING (`id`)"}, + {"select * from t1 right join t2 using (id) left join t3", false, ""}, + {"select * from t1 natural join t2", true, "SELECT * FROM `t1` NATURAL JOIN `t2`"}, + {"select * from t1 natural right join t2", true, "SELECT * FROM `t1` NATURAL RIGHT JOIN `t2`"}, + {"select * from t1 natural left outer join t2", true, "SELECT * FROM `t1` NATURAL LEFT JOIN `t2`"}, + {"select * from t1 natural inner join t2", false, ""}, + {"select * from t1 natural cross join t2", false, ""}, + {"select * from t3 join t1 join t2 on t1.a=t2.a on t3.b=t2.b", true, "SELECT * FROM `t3` JOIN (`t1` JOIN `t2` ON `t1`.`a`=`t2`.`a`) ON `t3`.`b`=`t2`.`b`"}, + + // for straight_join + {"select * from t1 straight_join t2 on t1.id = t2.id", true, "SELECT * FROM `t1` STRAIGHT_JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, + {"select straight_join * from t1 join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, + {"select straight_join * from t1 left join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` LEFT JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, + {"select straight_join * from t1 right join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` RIGHT JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, + {"select straight_join * from t1 straight_join t2 on t1.id = t2.id", true, "SELECT STRAIGHT_JOIN * FROM `t1` STRAIGHT_JOIN `t2` ON `t1`.`id`=`t2`.`id`"}, + + // delete statement + // single table syntax + {"DELETE from t1", true, "DELETE FROM `t1`"}, + {"DELETE from t1.*", false, ""}, + {"DELETE LOW_priORITY from t1", true, "DELETE LOW_PRIORITY FROM `t1`"}, + {"DELETE quick from t1", true, "DELETE QUICK FROM `t1`"}, + {"DELETE ignore from t1", true, "DELETE IGNORE FROM `t1`"}, + {"DELETE low_priority quick ignore from t1", true, "DELETE LOW_PRIORITY QUICK IGNORE FROM `t1`"}, + {"DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a", true, "DELETE FROM `t1` WHERE `t1`.`a`>0 ORDER BY `t1`.`a`"}, + {"delete from t1 where a=26", true, "DELETE FROM `t1` WHERE `a`=26"}, + {"DELETE from t1 where a=1 limit 1", true, "DELETE FROM `t1` WHERE `a`=1 LIMIT 1"}, + {"DELETE FROM t1 WHERE t1.a > 0 ORDER BY t1.a LIMIT 1", true, "DELETE FROM `t1` WHERE `t1`.`a`>0 ORDER BY `t1`.`a` LIMIT 1"}, + {"DELETE FROM x.y z WHERE z.a > 0", true, "DELETE FROM `x`.`y` AS `z` WHERE `z`.`a`>0"}, + {"DELETE FROM t1 AS w WHERE a > 0", true, "DELETE FROM `t1` AS `w` WHERE `a`>0"}, + {"DELETE from t1 partition (p0,p1)", true, "DELETE FROM `t1` PARTITION(`p0`, `p1`)"}, + + // multi table syntax: before from + {"delete low_priority t1, t2 from t1, t2", true, "DELETE LOW_PRIORITY `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete quick t1, t2 from t1, t2", true, "DELETE QUICK `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete ignore t1, t2 from t1, t2", true, "DELETE IGNORE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete ignore t1, t2 from t1 partition (p0,p1), t2", true, "DELETE IGNORE `t1`,`t2` FROM (`t1` PARTITION(`p0`, `p1`)) JOIN `t2`"}, + {"delete low_priority quick ignore t1, t2 from t1, t2 where t1.a > 5", true, "DELETE LOW_PRIORITY QUICK IGNORE `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`a`>5"}, + {"delete t1, t2 from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete t1, t2 from t1, t2 where t1.a = 1 and t2.b <> 1", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`a`=1 AND `t2`.`b`!=1"}, + {"delete t1 from t1, t2", true, "DELETE `t1` FROM (`t1`) JOIN `t2`"}, + {"delete t2 from t1, t2", true, "DELETE `t2` FROM (`t1`) JOIN `t2`"}, + {"delete t1 from t1", true, "DELETE `t1` FROM `t1`"}, + {"delete t1,t2,t3 from t1, t2, t3", true, "DELETE `t1`,`t2`,`t3` FROM ((`t1`) JOIN `t2`) JOIN `t3`"}, + {"delete t1,t2,t3 from t1, t2, t3 where t3.c < 5 and t1.a = 3", true, "DELETE `t1`,`t2`,`t3` FROM ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t3`.`c`<5 AND `t1`.`a`=3"}, + {"delete t1 from t1, t1 as t2 where t1.b = t2.b and t1.a > t2.a", true, "DELETE `t1` FROM (`t1`) JOIN `t1` AS `t2` WHERE `t1`.`b`=`t2`.`b` AND `t1`.`a`>`t2`.`a`"}, + {"delete t1.*,t2 from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete t.t1.*,t2 from t1, t2", true, "DELETE `t`.`t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete t1.*, t2.* from t1, t2", true, "DELETE `t1`,`t2` FROM (`t1`) JOIN `t2`"}, + {"delete t11.*, t12.* from t11, t12 where t11.a = t12.a and t11.b <> 1", true, "DELETE `t11`,`t12` FROM (`t11`) JOIN `t12` WHERE `t11`.`a`=`t12`.`a` AND `t11`.`b`!=1"}, + + // multi table syntax: with using + {"DELETE quick FROM t1,t2 USING t1,t2", true, "DELETE QUICK FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, + {"DELETE low_priority ignore FROM t1,t2 USING t1,t2", true, "DELETE LOW_PRIORITY IGNORE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, + {"DELETE low_priority quick ignore FROM t1,t2 USING t1,t2", true, "DELETE LOW_PRIORITY QUICK IGNORE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, + {"DELETE FROM t1 USING t1 WHERE post='1'", true, "DELETE FROM `t1` USING `t1` WHERE `post`=_UTF8MB4'1'"}, + {"DELETE FROM t1,t2 USING t1,t2", true, "DELETE FROM `t1`,`t2` USING (`t1`) JOIN `t2`"}, + {"DELETE FROM t1,t2,t3 USING t1,t2,t3 where t3.a = 1", true, "DELETE FROM `t1`,`t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t3`.`a`=1"}, + {"DELETE FROM t2,t3 USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, + {"DELETE FROM t2.*,t3.* USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, + {"DELETE FROM t1,t2.*,t3.* USING t1,t2,t3 where t1.a = 1", true, "DELETE FROM `t1`,`t2`,`t3` USING ((`t1`) JOIN `t2`) JOIN `t3` WHERE `t1`.`a`=1"}, + + // for delete statement + {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true, "DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, + {"DELETE FROM t1, t2 USING t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id;", true, "DELETE FROM `t1`,`t2` USING (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, + // for optimizer hint in delete statement + {"DELETE /*+ TiDB_INLJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id;", true, "DELETE /*+ TIDB_INLJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, + {"DELETE /*+ TiDB_HJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id", true, "DELETE /*+ TIDB_HJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, + {"DELETE /*+ TiDB_SMJ(t1, t2) */ t1, t2 from t1, t2 where t1.id=t2.id", true, "DELETE /*+ TIDB_SMJ(`t1`, `t2`)*/ `t1`,`t2` FROM (`t1`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, + // for "USE INDEX" in delete statement + {"DELETE FROM t1 USE INDEX(idx_a) WHERE t1.id=1;", true, "DELETE FROM `t1` USE INDEX (`idx_a`) WHERE `t1`.`id`=1"}, + {"DELETE t1, t2 FROM t1 USE INDEX(idx_a) JOIN t2 WHERE t1.id=t2.id;", true, "DELETE `t1`,`t2` FROM `t1` USE INDEX (`idx_a`) JOIN `t2` WHERE `t1`.`id`=`t2`.`id`"}, + {"DELETE t1, t2 FROM t1 USE INDEX(idx_a) JOIN t2 USE INDEX(idx_a) WHERE t1.id=t2.id;", true, "DELETE `t1`,`t2` FROM `t1` USE INDEX (`idx_a`) JOIN `t2` USE INDEX (`idx_a`) WHERE `t1`.`id`=`t2`.`id`"}, + + // for fail case + {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id limit 10;", false, ""}, + {"DELETE t1, t2 FROM t1 INNER JOIN t2 INNER JOIN t3 WHERE t1.id=t2.id AND t2.id=t3.id order by t1.id;", false, ""}, + + // for admin + {"admin show ddl;", true, "ADMIN SHOW DDL"}, + {"admin show ddl jobs;", true, "ADMIN SHOW DDL JOBS"}, + {"admin show ddl jobs where id > 0;", true, "ADMIN SHOW DDL JOBS WHERE `id`>0"}, + {"admin show ddl jobs 20 where id=0;", true, "ADMIN SHOW DDL JOBS 20 WHERE `id`=0"}, + {"admin show ddl jobs -1;", false, ""}, + {"admin show ddl job queries 1", true, "ADMIN SHOW DDL JOB QUERIES 1"}, + {"admin show ddl job queries 1, 2, 3, 4", true, "ADMIN SHOW DDL JOB QUERIES 1, 2, 3, 4"}, + {"admin show ddl job queries limit 5", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 0, 5"}, + {"admin show ddl job queries limit 5, 10", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 5, 10"}, + {"admin show ddl job queries limit 3 offset 2", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 2, 3"}, + {"admin show ddl job queries limit 22 offset 0", true, "ADMIN SHOW DDL JOB QUERIES LIMIT 0, 22"}, + {"admin show t1 next_row_id", true, "ADMIN SHOW `t1` NEXT_ROW_ID"}, + {"admin check table t1, t2;", true, "ADMIN CHECK TABLE `t1`, `t2`"}, + {"admin check index tableName idxName;", true, "ADMIN CHECK INDEX `tableName` idxName"}, + {"admin check index tableName idxName (1, 2), (4, 5);", true, "ADMIN CHECK INDEX `tableName` idxName (1,2), (4,5)"}, + {"admin checksum table t1, t2;", true, "ADMIN CHECKSUM TABLE `t1`, `t2`"}, + {"admin cancel ddl jobs 1", true, "ADMIN CANCEL DDL JOBS 1"}, + {"admin cancel ddl jobs 1, 2", true, "ADMIN CANCEL DDL JOBS 1, 2"}, + {"admin pause ddl jobs 1, 3", true, "ADMIN PAUSE DDL JOBS 1, 3"}, + {"admin pause ddl jobs 5", true, "ADMIN PAUSE DDL JOBS 5"}, + {"admin pause ddl jobs", false, "ADMIN PAUSE DDL JOBS"}, + {"admin pause ddl jobs str_not_num", false, "ADMIN PAUSE DDL JOBS str_not_num"}, + {"admin resume ddl jobs 1, 2", true, "ADMIN RESUME DDL JOBS 1, 2"}, + {"admin resume ddl jobs 3", true, "ADMIN RESUME DDL JOBS 3"}, + {"admin resume ddl jobs", false, "ADMIN RESUME DDL JOBS"}, + {"admin resume ddl jobs str_not_num", false, "ADMIN RESUME DDL JOBS str_not_num"}, + {"admin recover index t1 idx_a", true, "ADMIN RECOVER INDEX `t1` idx_a"}, + {"admin cleanup index t1 idx_a", true, "ADMIN CLEANUP INDEX `t1` idx_a"}, + {"admin show slow top 3", true, "ADMIN SHOW SLOW TOP 3"}, + {"admin show slow top internal 7", true, "ADMIN SHOW SLOW TOP INTERNAL 7"}, + {"admin show slow top all 9", true, "ADMIN SHOW SLOW TOP ALL 9"}, + {"admin show slow recent 11", true, "ADMIN SHOW SLOW RECENT 11"}, + {"admin reload expr_pushdown_blacklist", true, "ADMIN RELOAD EXPR_PUSHDOWN_BLACKLIST"}, + {"admin plugins disable audit, whitelist", true, "ADMIN PLUGINS DISABLE audit, whitelist"}, + {"admin plugins enable audit, whitelist", true, "ADMIN PLUGINS ENABLE audit, whitelist"}, + {"admin flush bindings", true, "ADMIN FLUSH BINDINGS"}, + {"admin capture bindings", true, "ADMIN CAPTURE BINDINGS"}, + {"admin evolve bindings", true, "ADMIN EVOLVE BINDINGS"}, + {"admin reload bindings", true, "ADMIN RELOAD BINDINGS"}, + {"admin show telemetry", true, "ADMIN SHOW TELEMETRY"}, + {"admin reset telemetry_id", true, "ADMIN RESET TELEMETRY_ID"}, + // This case would be removed once TiDB PR to remove ADMIN RELOAD STATISTICS is merged. + {"admin reload statistics", true, "ADMIN RELOAD STATS_EXTENDED"}, + {"admin reload stats_extended", true, "ADMIN RELOAD STATS_EXTENDED"}, + // Test for 'admin flush plan_cache' + {"admin flush instance plan_cache", true, "ADMIN FLUSH INSTANCE PLAN_CACHE"}, + {"admin flush session plan_cache", true, "ADMIN FLUSH SESSION PLAN_CACHE"}, + // We do not support the global level. We will check it in the later. + {"admin flush global plan_cache", true, "ADMIN FLUSH GLOBAL PLAN_CACHE"}, + + // for on duplicate key update + {"INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);", true, "INSERT INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, + {"INSERT INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c:=VALUES(a)+VALUES(b);", true, "INSERT INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, + {"INSERT IGNORE INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);", true, "INSERT IGNORE INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, + {"INSERT IGNORE INTO t (a,b,c) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE c:=VALUES(a)+VALUES(b);", true, "INSERT IGNORE INTO `t` (`a`,`b`,`c`) VALUES (1,2,3),(4,5,6) ON DUPLICATE KEY UPDATE `c`=VALUES(`a`)+VALUES(`b`)"}, + + // for insert ... set + {"INSERT INTO t SET a=1,b=2", true, "INSERT INTO `t` SET `a`=1,`b`=2"}, + {"INSERT INTO t (a) SET a=1", false, ""}, + + // for update statement + {"UPDATE LOW_PRIORITY IGNORE t SET id = id + 1 ORDER BY id DESC;", true, "UPDATE LOW_PRIORITY IGNORE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, + {"UPDATE t SET id = id + 1 ORDER BY id DESC;", true, "UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, + {"UPDATE t SET id = id + 1 ORDER BY id DESC limit 3 ;", true, "UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC LIMIT 3"}, + {"UPDATE t SET id = id + 1, name = 'jojo';", true, "UPDATE `t` SET `id`=`id`+1, `name`=_UTF8MB4'jojo'"}, + {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id;", true, "UPDATE (`items`) JOIN `month` SET `items`.`price`=`month`.`price` WHERE `items`.`id`=`month`.`id`"}, + {"UPDATE user T0 LEFT OUTER JOIN user_profile T1 ON T1.id = T0.profile_id SET T0.profile_id = 1 WHERE T0.profile_id IN (1);", true, "UPDATE `user` AS `T0` LEFT JOIN `user_profile` AS `T1` ON `T1`.`id`=`T0`.`profile_id` SET `T0`.`profile_id`=1 WHERE `T0`.`profile_id` IN (1)"}, + {"UPDATE t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, + // for optimizer hint in update statement + {"UPDATE /*+ TiDB_INLJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_INLJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, + {"UPDATE /*+ TiDB_SMJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_SMJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, + {"UPDATE /*+ TiDB_HJ(t1, t2) */ t1, t2 set t1.profile_id = 1, t2.profile_id = 1 where ta.a=t.ba", true, "UPDATE /*+ TIDB_HJ(`t1`, `t2`)*/ (`t1`) JOIN `t2` SET `t1`.`profile_id`=1, `t2`.`profile_id`=1 WHERE `ta`.`a`=`t`.`ba`"}, + // fail case for update statement + {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id LIMIT 10;", false, ""}, + {"UPDATE items,month SET items.price=month.price WHERE items.id=month.id order by month.id;", false, ""}, + // for "USE INDEX" in delete statement + {"UPDATE t1 USE INDEX(idx_a) SET t1.price=3.25 WHERE t1.id=1;", true, "UPDATE `t1` USE INDEX (`idx_a`) SET `t1`.`price`=3.25 WHERE `t1`.`id`=1"}, + {"UPDATE t1 USE INDEX(idx_a) JOIN t2 SET t1.price=t2.price WHERE t1.id=t2.id;", true, "UPDATE `t1` USE INDEX (`idx_a`) JOIN `t2` SET `t1`.`price`=`t2`.`price` WHERE `t1`.`id`=`t2`.`id`"}, + {"UPDATE t1 USE INDEX(idx_a) JOIN t2 USE INDEX(idx_a) SET t1.price=t2.price WHERE t1.id=t2.id;", true, "UPDATE `t1` USE INDEX (`idx_a`) JOIN `t2` USE INDEX (`idx_a`) SET `t1`.`price`=`t2`.`price` WHERE `t1`.`id`=`t2`.`id`"}, + + // for select with where clause + {"SELECT * FROM t WHERE 1 = 1", true, "SELECT * FROM `t` WHERE 1=1"}, + + // for select with FETCH FIRST syntax + {"SELECT * FROM t FETCH FIRST 5 ROW ONLY", true, "SELECT * FROM `t` LIMIT 5"}, + {"SELECT * FROM t FETCH NEXT 5 ROW ONLY", true, "SELECT * FROM `t` LIMIT 5"}, + {"SELECT * FROM t FETCH FIRST 5 ROWS ONLY", true, "SELECT * FROM `t` LIMIT 5"}, + {"SELECT * FROM t FETCH NEXT 5 ROWS ONLY", true, "SELECT * FROM `t` LIMIT 5"}, + {"SELECT * FROM t FETCH FIRST ROW ONLY", true, "SELECT * FROM `t` LIMIT 1"}, + {"SELECT * FROM t FETCH NEXT ROW ONLY", true, "SELECT * FROM `t` LIMIT 1"}, + + // for dual + {"select 1 from dual", true, "SELECT 1"}, + {"select 1 from dual limit 1", true, "SELECT 1 LIMIT 1"}, + {"select 1 where exists (select 2)", true, "SELECT 1 FROM DUAL WHERE EXISTS (SELECT 2)"}, + {"select 1 from dual where not exists (select 2)", true, "SELECT 1 FROM DUAL WHERE NOT EXISTS (SELECT 2)"}, + {"select 1 as a from dual order by a", true, "SELECT 1 AS `a` ORDER BY `a`"}, + {"select 1 as a from dual where 1 < any (select 2) order by a", true, "SELECT 1 AS `a` FROM DUAL WHERE 1 now() - interval 10 hour", true, "SHOW BACKUPS WHERE `start_time`>DATE_SUB(NOW(), INTERVAL 10 HOUR)"}, + {"show backup", false, ""}, + {"show restore", false, ""}, + + // for load stats + {"load stats '/tmp/stats.json'", true, "LOAD STATS '/tmp/stats.json'"}, + // for lock stats + {"lock stats test.t", true, "LOCK STATS `test`.`t`"}, + {"lock stats t, t2", true, "LOCK STATS `t`, `t2`"}, + {"lock stats t partition (p0, p1)", true, "LOCK STATS `t` PARTITION(`p0`, `p1`)"}, + // for unlock stats + {"unlock stats test.t", true, "UNLOCK STATS `test`.`t`"}, + {"unlock stats t, t2", true, "UNLOCK STATS `t`, `t2`"}, + {"unlock stats t partition (p0, p1)", true, "UNLOCK STATS `t` PARTITION(`p0`, `p1`)"}, + // set + // user defined + {"SET @ = 1", true, "SET @``=1"}, + {"SET @' ' = 1", true, "SET @` `=1"}, + {"SET @! = 1", false, ""}, + {"SET @1 = 1", true, "SET @`1`=1"}, + {"SET @a = 1", true, "SET @`a`=1"}, + {"SET @b := 1", true, "SET @`b`=1"}, + {"SET @.c = 1", true, "SET @`.c`=1"}, + {"SET @_d = 1", true, "SET @`_d`=1"}, + {"SET @_e._$. = 1", true, "SET @`_e._$.`=1"}, + {"SET @~f = 1", false, ""}, + {"SET @`g,` = 1", true, "SET @`g,`=1"}, + {"SET", false, ""}, + {"SET @a = 1, @b := 2", true, "SET @`a`=1, @`b`=2"}, + // session system variables + {"SET SESSION autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@session.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@SESSION.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@GLOBAL.GTID_PURGED = '123'", true, "SET @@GLOBAL.`gtid_purged`=_UTF8MB4'123'"}, + {"SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN", true, "SET @`MYSQLDUMP_TEMP_LOG_BIN`=@@SESSION.`sql_log_bin`"}, + {"SET LOCAL autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@local.autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET autocommit = 1", true, "SET @@SESSION.`autocommit`=1"}, + // global system variables + {"SET GLOBAL autocommit = 1", true, "SET @@GLOBAL.`autocommit`=1"}, + {"SET @@global.autocommit = 1", true, "SET @@GLOBAL.`autocommit`=1"}, + // set through mysql extension assignment syntax + {"SET autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@session.autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @MYSQLDUMP_TEMP_LOG_BIN := @@SESSION.SQL_LOG_BIN", true, "SET @`MYSQLDUMP_TEMP_LOG_BIN`=@@SESSION.`sql_log_bin`"}, + {"SET LOCAL autocommit := 1", true, "SET @@SESSION.`autocommit`=1"}, + {"SET @@global.autocommit := default", true, "SET @@GLOBAL.`autocommit`=DEFAULT"}, + // set default value + {"SET @@global.autocommit = default", true, "SET @@GLOBAL.`autocommit`=DEFAULT"}, + {"SET @@session.autocommit = default", true, "SET @@SESSION.`autocommit`=DEFAULT"}, + // set binary value + {"SET @@character_set_results = binary", true, "SET @@SESSION.`character_set_results`=_UTF8MB4'BINARY'"}, + // SET CHARACTER SET + {"SET CHARACTER SET utf8mb4;", true, "SET CHARSET 'utf8mb4'"}, + {"SET CHARACTER SET 'utf8mb4';", true, "SET CHARSET 'utf8mb4'"}, + // set password + {"SET PASSWORD = 'password';", true, "SET PASSWORD='password'"}, + {"SET PASSWORD FOR 'root'@'localhost' = 'password';", true, "SET PASSWORD FOR `root`@`localhost`='password'"}, + // SET TRANSACTION Syntax + {"SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'REPEATABLE-READ'"}, + {"SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@GLOBAL.`tx_isolation`=_UTF8MB4'REPEATABLE-READ'"}, + {"SET SESSION TRANSACTION READ WRITE", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'0'"}, + {"SET SESSION TRANSACTION READ ONLY", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'1'"}, + {"SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'READ-COMMITTED'"}, + {"SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'READ-UNCOMMITTED'"}, + {"SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE", true, "SET @@SESSION.`tx_isolation`=_UTF8MB4'SERIALIZABLE'"}, + {"SET TRANSACTION ISOLATION LEVEL REPEATABLE READ", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'REPEATABLE-READ'"}, + {"SET TRANSACTION READ WRITE", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'0'"}, + {"SET TRANSACTION READ ONLY", true, "SET @@SESSION.`tx_read_only`=_UTF8MB4'1'"}, + {"SET TRANSACTION ISOLATION LEVEL READ COMMITTED", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'READ-COMMITTED'"}, + {"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'READ-UNCOMMITTED'"}, + {"SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true, "SET @@SESSION.`tx_isolation_one_shot`=_UTF8MB4'SERIALIZABLE'"}, + // for set names + {"set names utf8", true, "SET NAMES 'utf8'"}, + {"set names utf8 collate utf8_unicode_ci", true, "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'"}, + {"set names binary", true, "SET NAMES 'binary'"}, + + // for set character set | name default + {"set names default", true, "SET NAMES DEFAULT"}, + {"set character set default", true, "SET CHARSET DEFAULT"}, + {"set charset default", true, "SET CHARSET DEFAULT"}, + {"set char set default", true, "SET CHARSET DEFAULT"}, + + {"set role `role1`", true, "SET ROLE `role1`@`%`"}, + {"SET ROLE DEFAULT", true, "SET ROLE DEFAULT"}, + {"SET ROLE ALL", true, "SET ROLE ALL"}, + {"SET ROLE ALL EXCEPT `role1`, `role2`", true, "SET ROLE ALL EXCEPT `role1`@`%`, `role2`@`%`"}, + {"SET DEFAULT ROLE administrator, developer TO `joe`@`10.0.0.1`", true, "SET DEFAULT ROLE `administrator`@`%`, `developer`@`%` TO `joe`@`10.0.0.1`"}, + // for set names and set vars + {"set names utf8, @@session.sql_mode=1;", true, "SET NAMES 'utf8', @@SESSION.`sql_mode`=1"}, + {"set @@session.sql_mode=1, names utf8, charset utf8;", true, "SET @@SESSION.`sql_mode`=1, NAMES 'utf8', CHARSET 'utf8'"}, + + // for set/show config + {"set config TIKV LOG.LEVEL='info'", true, "SET CONFIG TIKV LOG.LEVEL = _UTF8MB4'info'"}, + {"set config PD LOG.LEVEL='info'", true, "SET CONFIG PD LOG.LEVEL = _UTF8MB4'info'"}, + {"set config TIDB LOG.LEVEL='info'", true, "SET CONFIG TIDB LOG.LEVEL = _UTF8MB4'info'"}, + {"set config '127.0.0.1:3306' LOG.LEVEL='info'", true, "SET CONFIG '127.0.0.1:3306' LOG.LEVEL = _UTF8MB4'info'"}, + {"set config '127.0.0.1:3306' AUTO-COMPACTION-MODE=TRUE", true, "SET CONFIG '127.0.0.1:3306' AUTO-COMPACTION-MODE = TRUE"}, + {"set config '127.0.0.1:3306' LABEL-PROPERTY.REJECT-LEADER.KEY='zone'", true, "SET CONFIG '127.0.0.1:3306' LABEL-PROPERTY.REJECT-LEADER.KEY = _UTF8MB4'zone'"}, + {"show config", true, "SHOW CONFIG"}, + {"show config where type='tidb'", true, "SHOW CONFIG WHERE `type`=_UTF8MB4'tidb'"}, + {"show config where instance='127.0.0.1:3306'", true, "SHOW CONFIG WHERE `instance`=_UTF8MB4'127.0.0.1:3306'"}, + {"create table CONFIG (a int)", true, "CREATE TABLE `CONFIG` (`a` INT)"}, // check that `CONFIG` is unreserved keyword + + // for FLUSH statement + {"flush no_write_to_binlog tables tbl1 with read lock", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1` WITH READ LOCK"}, + {"flush table", true, "FLUSH TABLES"}, + {"flush tables", true, "FLUSH TABLES"}, + {"flush tables tbl1", true, "FLUSH TABLES `tbl1`"}, + {"flush no_write_to_binlog tables tbl1", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1`"}, + {"flush local tables tbl1", true, "FLUSH NO_WRITE_TO_BINLOG TABLES `tbl1`"}, + {"flush table with read lock", true, "FLUSH TABLES WITH READ LOCK"}, + {"flush tables tbl1, tbl2, tbl3", true, "FLUSH TABLES `tbl1`, `tbl2`, `tbl3`"}, + {"flush tables tbl1, tbl2, tbl3 with read lock", true, "FLUSH TABLES `tbl1`, `tbl2`, `tbl3` WITH READ LOCK"}, + {"flush privileges", true, "FLUSH PRIVILEGES"}, + {"flush status", true, "FLUSH STATUS"}, + {"flush tidb plugins plugin1", true, "FLUSH TIDB PLUGINS plugin1"}, + {"flush tidb plugins plugin1, plugin2", true, "FLUSH TIDB PLUGINS plugin1, plugin2"}, + {"flush hosts", true, "FLUSH HOSTS"}, + {"flush logs", true, "FLUSH LOGS"}, + {"flush binary logs", true, "FLUSH BINARY LOGS"}, + {"flush engine logs", true, "FLUSH ENGINE LOGS"}, + {"flush error logs", true, "FLUSH ERROR LOGS"}, + {"flush general logs", true, "FLUSH GENERAL LOGS"}, + {"flush slow logs", true, "FLUSH SLOW LOGS"}, + {"flush client_errors_summary", true, "FLUSH CLIENT_ERRORS_SUMMARY"}, + + // for change statement + {"change pump to node_state ='paused' for node_id '127.0.0.1:8250'", true, "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:8250'"}, + {"change drainer to node_state ='paused' for node_id '127.0.0.1:8249'", true, "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:8249'"}, + + // for call statement + {"call ", false, ""}, + {"call test", true, "CALL `test`()"}, + {"call test()", true, "CALL `test`()"}, + {"call test(1, 'test', true)", true, "CALL `test`(1, _UTF8MB4'test', TRUE)"}, + {"call x.y;", true, "CALL `x`.`y`()"}, + {"call x.y();", true, "CALL `x`.`y`()"}, + {"call x.y('p', 'q', 'r');", true, "CALL `x`.`y`(_UTF8MB4'p', _UTF8MB4'q', _UTF8MB4'r')"}, + {"call `x`.`y`;", true, "CALL `x`.`y`()"}, + {"call `x`.`y`();", true, "CALL `x`.`y`()"}, + {"call `x`.`y`('p', 'q', 'r');", true, "CALL `x`.`y`(_UTF8MB4'p', _UTF8MB4'q', _UTF8MB4'r')"}, + } + RunTest(t, table, false) +} + +func TestSetVariable(t *testing.T) { + table := []struct { + Input string + Name string + IsGlobal bool + IsSystem bool + }{ + + // Set system variable xx.xx, although xx.xx isn't a system variable, the parser should accept it. + {"set xx.xx = 666", "xx.xx", false, true}, + // Set session system variable xx.xx + {"set session xx.xx = 666", "xx.xx", false, true}, + {"set local xx.xx = 666", "xx.xx", false, true}, + {"set global xx.xx = 666", "xx.xx", true, true}, + + {"set @@xx.xx = 666", "xx.xx", false, true}, + {"set @@session.xx.xx = 666", "xx.xx", false, true}, + {"set @@local.xx.xx = 666", "xx.xx", false, true}, + {"set @@global.xx.xx = 666", "xx.xx", true, true}, + + // Set user defined variable xx.xx + {"set @xx.xx = 666", "xx.xx", false, false}, + } + + p := parser.New() + for _, tbl := range table { + stmt, err := p.ParseOneStmt(tbl.Input, "", "") + require.NoError(t, err) + + setStmt, ok := stmt.(*ast.SetStmt) + require.True(t, ok) + require.Len(t, setStmt.Variables, 1) + + v := setStmt.Variables[0] + require.Equal(t, tbl.Name, v.Name) + require.Equal(t, tbl.IsGlobal, v.IsGlobal) + require.Equal(t, tbl.IsSystem, v.IsSystem) + } + + _, err := p.ParseOneStmt("set xx.xx.xx = 666", "", "") + require.Error(t, err) +} + +func TestFlushTable(t *testing.T) { + p := parser.New() + stmt, _, err := p.Parse("flush local tables tbl1,tbl2 with read lock", "", "") + require.NoError(t, err) + flushTable := stmt[0].(*ast.FlushStmt) + require.Equal(t, ast.FlushTables, flushTable.Tp) + require.Equal(t, "tbl1", flushTable.Tables[0].Name.L) + require.Equal(t, "tbl2", flushTable.Tables[1].Name.L) + require.True(t, flushTable.NoWriteToBinLog) + require.True(t, flushTable.ReadLock) +} + +func TestFlushPrivileges(t *testing.T) { + p := parser.New() + stmt, _, err := p.Parse("flush privileges", "", "") + require.NoError(t, err) + flushPrivilege := stmt[0].(*ast.FlushStmt) + require.Equal(t, ast.FlushPrivileges, flushPrivilege.Tp) +} + +func TestExpression(t *testing.T) { + table := []testCase{ + // sign expression + {"SELECT ++1", true, "SELECT ++1"}, + {"SELECT -*1", false, "SELECT -*1"}, + {"SELECT -+1", true, "SELECT -+1"}, + {"SELECT -1", true, "SELECT -1"}, + {"SELECT --1", true, "SELECT --1"}, + + // for string literal + {`select '''a''', """a"""`, true, "SELECT _UTF8MB4'''a''',_UTF8MB4'\"a\"'"}, + {`select ''a''`, false, ""}, + {`select ""a""`, false, ""}, + {`select '''a''';`, true, "SELECT _UTF8MB4'''a'''"}, + {`select '\'a\'';`, true, "SELECT _UTF8MB4'''a'''"}, + {`select "\"a\"";`, true, "SELECT _UTF8MB4'\"a\"'"}, + {`select """a""";`, true, "SELECT _UTF8MB4'\"a\"'"}, + {`select _utf8"string";`, true, "SELECT _UTF8'string'"}, + {`select _binary"string";`, true, "SELECT _BINARY'string'"}, + {"select N'string'", true, "SELECT _UTF8'string'"}, + {"select n'string'", true, "SELECT _UTF8'string'"}, + {"select _utf8 0xD0B1;", true, "SELECT _UTF8 x'd0b1'"}, + {"select _utf8 X'D0B1';", true, "SELECT _UTF8 x'd0b1'"}, + {"select _utf8 0b1101000010110001;", true, "SELECT _UTF8 b'1101000010110001'"}, + {"select _utf8 B'1101000010110001';", true, "SELECT _UTF8 b'1101000010110001'"}, + // for comparison + {"select 1 <=> 0, 1 <=> null, 1 = null", true, "SELECT 1<=>0,1<=>NULL,1=NULL"}, + // for date literal + {"select date'1989-09-10'", true, "SELECT DATE '1989-09-10'"}, + {"select date 19890910", false, ""}, + // for time literal + {"select time '00:00:00.111'", true, "SELECT TIME '00:00:00.111'"}, + {"select time 19890910", false, ""}, + // for timestamp literal + {"select timestamp '1989-09-10 11:11:11'", true, "SELECT TIMESTAMP '1989-09-10 11:11:11'"}, + {"select timestamp 19890910", false, ""}, + + // The ODBC syntax for time/date/timestamp literal. + // See: https://dev.mysql.com/doc/refman/5.7/en/date-and-time-literals.html + {"select {ts '1989-09-10 11:11:11'}", true, "SELECT TIMESTAMP '1989-09-10 11:11:11'"}, + {"select {d '1989-09-10'}", true, "SELECT DATE '1989-09-10'"}, + {"select {t '00:00:00.111'}", true, "SELECT TIME '00:00:00.111'"}, + {"select * from t where a > {ts '1989-09-10 11:11:11'}", true, "SELECT * FROM `t` WHERE `a`>TIMESTAMP '1989-09-10 11:11:11'"}, + {"select * from t where a > {ts {abc '1989-09-10 11:11:11'}}", true, "SELECT * FROM `t` WHERE `a`>TIMESTAMP '1989-09-10 11:11:11'"}, + // If the identifier is not in (t, d, ts), we just ignore it and consider the following expression as the value. + // See: https://dev.mysql.com/doc/refman/5.7/en/expressions.html + {"select {ts123 '1989-09-10 11:11:11'}", true, "SELECT _UTF8MB4'1989-09-10 11:11:11'"}, + {"select {ts123 123}", true, "SELECT 123"}, + {"select {ts123 1 xor 1}", true, "SELECT 1 XOR 1"}, + {"select * from t where a > {ts123 '1989-09-10 11:11:11'}", true, "SELECT * FROM `t` WHERE `a`>_UTF8MB4'1989-09-10 11:11:11'"}, + {"select .t.a from t", false, ""}, + } + + RunTest(t, table, false) +} + +func TestBuiltin(t *testing.T) { + table := []testCase{ + // for builtin functions + {"SELECT POW(1, 2)", true, "SELECT POW(1, 2)"}, + {"SELECT POW(1, 2, 1)", true, "SELECT POW(1, 2, 1)"}, // illegal number of arguments shall pass too + {"SELECT POW(1, 0.5)", true, "SELECT POW(1, 0.5)"}, + {"SELECT POW(1, -1)", true, "SELECT POW(1, -1)"}, + {"SELECT POW(-1, 1)", true, "SELECT POW(-1, 1)"}, + {"SELECT RAND();", true, "SELECT RAND()"}, + {"SELECT RAND(1);", true, "SELECT RAND(1)"}, + {"SELECT MOD(10, 2);", true, "SELECT 10%2"}, + {"SELECT ROUND(-1.23);", true, "SELECT ROUND(-1.23)"}, + {"SELECT ROUND(1.23, 1);", true, "SELECT ROUND(1.23, 1)"}, + {"SELECT ROUND(1.23, 1, 1);", true, "SELECT ROUND(1.23, 1, 1)"}, + {"SELECT CEIL(-1.23);", true, "SELECT CEIL(-1.23)"}, + {"SELECT CEILING(1.23);", true, "SELECT CEILING(1.23)"}, + {"SELECT FLOOR(-1.23);", true, "SELECT FLOOR(-1.23)"}, + {"SELECT LN(1);", true, "SELECT LN(1)"}, + {"SELECT LN(1, 2);", true, "SELECT LN(1, 2)"}, + {"SELECT LOG(-2);", true, "SELECT LOG(-2)"}, + {"SELECT LOG(2, 65536);", true, "SELECT LOG(2, 65536)"}, + {"SELECT LOG(2, 65536, 1);", true, "SELECT LOG(2, 65536, 1)"}, + {"SELECT LOG2(2);", true, "SELECT LOG2(2)"}, + {"SELECT LOG2(2, 2);", true, "SELECT LOG2(2, 2)"}, + {"SELECT LOG10(10);", true, "SELECT LOG10(10)"}, + {"SELECT LOG10(10, 1);", true, "SELECT LOG10(10, 1)"}, + {"SELECT ABS(10, 1);", true, "SELECT ABS(10, 1)"}, + {"SELECT ABS(10);", true, "SELECT ABS(10)"}, + {"SELECT ABS();", true, "SELECT ABS()"}, + {"SELECT CONV(10+'10'+'10'+X'0a',10,10);", true, "SELECT CONV(10+_UTF8MB4'10'+_UTF8MB4'10'+x'0a', 10, 10)"}, + {"SELECT CONV();", true, "SELECT CONV()"}, + {"SELECT CRC32('MySQL');", true, "SELECT CRC32(_UTF8MB4'MySQL')"}, + {"SELECT CRC32();", true, "SELECT CRC32()"}, + {"SELECT SIGN();", true, "SELECT SIGN()"}, + {"SELECT SIGN(0);", true, "SELECT SIGN(0)"}, + {"SELECT SQRT(0);", true, "SELECT SQRT(0)"}, + {"SELECT SQRT();", true, "SELECT SQRT()"}, + {"SELECT ACOS();", true, "SELECT ACOS()"}, + {"SELECT ACOS(1);", true, "SELECT ACOS(1)"}, + {"SELECT ACOS(1, 2);", true, "SELECT ACOS(1, 2)"}, + {"SELECT ASIN();", true, "SELECT ASIN()"}, + {"SELECT ASIN(1);", true, "SELECT ASIN(1)"}, + {"SELECT ASIN(1, 2);", true, "SELECT ASIN(1, 2)"}, + {"SELECT ATAN(0), ATAN(1), ATAN(1, 2);", true, "SELECT ATAN(0),ATAN(1),ATAN(1, 2)"}, + {"SELECT ATAN2(), ATAN2(1,2);", true, "SELECT ATAN2(),ATAN2(1, 2)"}, + {"SELECT COS(0);", true, "SELECT COS(0)"}, + {"SELECT COS(1);", true, "SELECT COS(1)"}, + {"SELECT COS(1, 2);", true, "SELECT COS(1, 2)"}, + {"SELECT COT();", true, "SELECT COT()"}, + {"SELECT COT(1);", true, "SELECT COT(1)"}, + {"SELECT COT(1, 2);", true, "SELECT COT(1, 2)"}, + {"SELECT DEGREES();", true, "SELECT DEGREES()"}, + {"SELECT DEGREES(0);", true, "SELECT DEGREES(0)"}, + {"SELECT EXP();", true, "SELECT EXP()"}, + {"SELECT EXP(1);", true, "SELECT EXP(1)"}, + {"SELECT PI();", true, "SELECT PI()"}, + {"SELECT PI(1);", true, "SELECT PI(1)"}, + {"SELECT RADIANS();", true, "SELECT RADIANS()"}, + {"SELECT RADIANS(1);", true, "SELECT RADIANS(1)"}, + {"SELECT SIN();", true, "SELECT SIN()"}, + {"SELECT SIN(1);", true, "SELECT SIN(1)"}, + {"SELECT TAN(1);", true, "SELECT TAN(1)"}, + {"SELECT TAN();", true, "SELECT TAN()"}, + {"SELECT TRUNCATE(1.223,1);", true, "SELECT TRUNCATE(1.223, 1)"}, + {"SELECT TRUNCATE();", true, "SELECT TRUNCATE()"}, + + {"SELECT SUBSTR('Quadratically',5);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5)"}, + {"SELECT SUBSTR('Quadratically',5, 3);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5, 3)"}, + {"SELECT SUBSTR('Quadratically' FROM 5);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5)"}, + {"SELECT SUBSTR('Quadratically' FROM 5 FOR 3);", true, "SELECT SUBSTR(_UTF8MB4'Quadratically', 5, 3)"}, + + {"SELECT SUBSTRING('Quadratically',5);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5)"}, + {"SELECT SUBSTRING('Quadratically',5, 3);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5, 3)"}, + {"SELECT SUBSTRING('Quadratically' FROM 5);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5)"}, + {"SELECT SUBSTRING('Quadratically' FROM 5 FOR 3);", true, "SELECT SUBSTRING(_UTF8MB4'Quadratically', 5, 3)"}, + + {"SELECT CONVERT('111', SIGNED);", true, "SELECT CONVERT(_UTF8MB4'111', SIGNED)"}, + + {"SELECT LEAST(), LEAST(1, 2, 3);", true, "SELECT LEAST(),LEAST(1, 2, 3)"}, + + {"SELECT INTERVAL(1, 0, 1, 2)", true, "SELECT INTERVAL(1, 0, 1, 2)"}, + {"SELECT (INTERVAL(1, 0, 1, 2)+5)*7+INTERVAL(1, 0, 1, 2)/2", true, "SELECT (INTERVAL(1, 0, 1, 2)+5)*7+INTERVAL(1, 0, 1, 2)/2"}, + {"SELECT INTERVAL(0, (1*5)/2)+INTERVAL(5, 4, 3)", true, "SELECT INTERVAL(0, (1*5)/2)+INTERVAL(5, 4, 3)"}, + {"SELECT DATE_ADD('2008-01-02', INTERVAL INTERVAL(1, 0, 1) DAY);", true, "SELECT DATE_ADD(_UTF8MB4'2008-01-02', INTERVAL INTERVAL(1, 0, 1) DAY)"}, + + // information functions + {"SELECT DATABASE();", true, "SELECT DATABASE()"}, + {"SELECT SCHEMA();", true, "SELECT SCHEMA()"}, + {"SELECT USER();", true, "SELECT USER()"}, + {"SELECT USER(1);", true, "SELECT USER(1)"}, + {"SELECT CURRENT_USER();", true, "SELECT CURRENT_USER()"}, + {"SELECT CURRENT_ROLE();", true, "SELECT CURRENT_ROLE()"}, + {"SELECT CURRENT_USER;", true, "SELECT CURRENT_USER()"}, + {"SELECT CONNECTION_ID();", true, "SELECT CONNECTION_ID()"}, + {"SELECT VERSION();", true, "SELECT VERSION()"}, + {"SELECT CURRENT_RESOURCE_GROUP();", true, "SELECT CURRENT_RESOURCE_GROUP()"}, + {"SELECT BENCHMARK(1000000, AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));", true, "SELECT BENCHMARK(1000000, AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3')))"}, + {"SELECT BENCHMARK(AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3')));", true, "SELECT BENCHMARK(AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3')))"}, + {"SELECT CHARSET('abc');", true, "SELECT CHARSET(_UTF8MB4'abc')"}, + {"SELECT COERCIBILITY('abc');", true, "SELECT COERCIBILITY(_UTF8MB4'abc')"}, + {"SELECT COERCIBILITY('abc', 'a');", true, "SELECT COERCIBILITY(_UTF8MB4'abc', _UTF8MB4'a')"}, + {"SELECT COLLATION('abc');", true, "SELECT COLLATION(_UTF8MB4'abc')"}, + {"SELECT ROW_COUNT();", true, "SELECT ROW_COUNT()"}, + {"SELECT SESSION_USER();", true, "SELECT SESSION_USER()"}, + {"SELECT SYSTEM_USER();", true, "SELECT SYSTEM_USER()"}, + {"SELECT FORMAT_BYTES(512);", true, "SELECT FORMAT_BYTES(512)"}, + {"SELECT FORMAT_NANO_TIME(3501);", true, "SELECT FORMAT_NANO_TIME(3501)"}, + + {"SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2);", true, "SELECT SUBSTRING_INDEX(_UTF8MB4'www.mysql.com', _UTF8MB4'.', 2)"}, + {"SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2);", true, "SELECT SUBSTRING_INDEX(_UTF8MB4'www.mysql.com', _UTF8MB4'.', -2)"}, + + {`SELECT ASCII(), ASCII(""), ASCII("A"), ASCII(1);`, true, "SELECT ASCII(),ASCII(_UTF8MB4''),ASCII(_UTF8MB4'A'),ASCII(1)"}, + + {`SELECT LOWER("A"), UPPER("a")`, true, "SELECT LOWER(_UTF8MB4'A'),UPPER(_UTF8MB4'a')"}, + {`SELECT LCASE("A"), UCASE("a")`, true, "SELECT LCASE(_UTF8MB4'A'),UCASE(_UTF8MB4'a')"}, + + {`SELECT REPLACE('www.mysql.com', 'w', 'Ww')`, true, "SELECT REPLACE(_UTF8MB4'www.mysql.com', _UTF8MB4'w', _UTF8MB4'Ww')"}, + + {`SELECT LOCATE('bar', 'foobarbar');`, true, "SELECT LOCATE(_UTF8MB4'bar', _UTF8MB4'foobarbar')"}, + {`SELECT LOCATE('bar', 'foobarbar', 5);`, true, "SELECT LOCATE(_UTF8MB4'bar', _UTF8MB4'foobarbar', 5)"}, + + {`SELECT tidb_version();`, true, "SELECT TIDB_VERSION()"}, + {`SELECT tidb_is_ddl_owner();`, true, "SELECT TIDB_IS_DDL_OWNER()"}, + {`SELECT tidb_decode_plan();`, true, "SELECT TIDB_DECODE_PLAN()"}, + {`SELECT tidb_decode_key('abc');`, true, "SELECT TIDB_DECODE_KEY(_UTF8MB4'abc')"}, + {`SELECT tidb_decode_base64_key('abc');`, true, "SELECT TIDB_DECODE_BASE64_KEY(_UTF8MB4'abc')"}, + {`SELECT tidb_decode_sql_digests('[]');`, true, "SELECT TIDB_DECODE_SQL_DIGESTS(_UTF8MB4'[]')"}, + {`SELECT get_mvcc_info('hex', '0xabc');`, true, "SELECT GET_MVCC_INFO(_UTF8MB4'hex', _UTF8MB4'0xabc')"}, + + // for time fsp + {"CREATE TABLE t( c1 TIME(2), c2 DATETIME(2), c3 TIMESTAMP(2) );", true, "CREATE TABLE `t` (`c1` TIME(2),`c2` DATETIME(2),`c3` TIMESTAMP(2))"}, + + // for row + {"select row(1)", false, ""}, + {"select row(1, 1,)", false, ""}, + {"select (1, 1,)", false, ""}, + {"select row(1, 1) > row(1, 1), row(1, 1, 1) > row(1, 1, 1)", true, "SELECT ROW(1,1)>ROW(1,1),ROW(1,1,1)>ROW(1,1,1)"}, + {"Select (1, 1) > (1, 1)", true, "SELECT ROW(1,1)>ROW(1,1)"}, + {"create table t (`row` int)", true, "CREATE TABLE `t` (`row` INT)"}, + {"create table t (row int)", false, ""}, + + // for cast with charset + {"SELECT *, CAST(data AS CHAR CHARACTER SET utf8) FROM t;", true, "SELECT *,CAST(`data` AS CHAR CHARSET UTF8) FROM `t`"}, + {"SELECT CAST(data AS CHARACTER);", true, "SELECT CAST(`data` AS CHAR)"}, + {"SELECT CAST(data AS CHARACTER(10) CHARACTER SET utf8);", true, "SELECT CAST(`data` AS CHAR(10) CHARSET UTF8)"}, + {"SELECT CAST(data AS BINARY)", true, "SELECT CAST(`data` AS BINARY)"}, + + // for cast as JSON + {"SELECT *, CAST(data AS JSON) FROM t;", true, "SELECT *,CAST(`data` AS JSON) FROM `t`"}, + + // for cast as signed int, fix issue #3691. + {"select cast(1 as signed int);", true, "SELECT CAST(1 AS SIGNED)"}, + + // for cast as double + {"select cast(1 as double);", true, "SELECT CAST(1 AS DOUBLE)"}, + + // for cast as float + {"select cast(1 as float);", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(0));", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(24));", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(25));", true, "SELECT CAST(1 AS DOUBLE)"}, + {"select cast(1 as float(53));", true, "SELECT CAST(1 AS DOUBLE)"}, + {"select cast(1 as float(54));", false, ""}, + + {"select cast(1 as real);", true, "SELECT CAST(1 AS DOUBLE)"}, + {"select cast('2000' as year);", true, "SELECT CAST(_UTF8MB4'2000' AS YEAR)"}, + {"select cast(time '2000' as year);", true, "SELECT CAST(TIME '2000' AS YEAR)"}, + + {"select cast(b as signed array);", true, "SELECT CAST(`b` AS SIGNED ARRAY)"}, + {"select cast(b as char(10) array);", true, "SELECT CAST(`b` AS CHAR(10) ARRAY)"}, + + // for last_insert_id + {"SELECT last_insert_id();", true, "SELECT LAST_INSERT_ID()"}, + {"SELECT last_insert_id(1);", true, "SELECT LAST_INSERT_ID(1)"}, + + // for binary operator + {"SELECT binary 'a';", true, "SELECT BINARY _UTF8MB4'a'"}, + + // for bit_count + {`SELECT BIT_COUNT(1);`, true, "SELECT BIT_COUNT(1)"}, + + // select time + {"select current_timestamp", true, "SELECT CURRENT_TIMESTAMP()"}, + {"select current_timestamp()", true, "SELECT CURRENT_TIMESTAMP()"}, + {"select current_timestamp(6)", true, "SELECT CURRENT_TIMESTAMP(6)"}, + {"select current_timestamp(null)", false, ""}, + {"select current_timestamp(-1)", false, ""}, + {"select current_timestamp(1.0)", false, ""}, + {"select current_timestamp('2')", false, ""}, + {"select now()", true, "SELECT NOW()"}, + {"select now(6)", true, "SELECT NOW(6)"}, + {"select sysdate(), sysdate(6)", true, "SELECT SYSDATE(),SYSDATE(6)"}, + {"SELECT time('01:02:03');", true, "SELECT TIME(_UTF8MB4'01:02:03')"}, + {"SELECT time('01:02:03.1')", true, "SELECT TIME(_UTF8MB4'01:02:03.1')"}, + {"SELECT time('20.1')", true, "SELECT TIME(_UTF8MB4'20.1')"}, + {"SELECT TIMEDIFF('2000:01:01 00:00:00', '2000:01:01 00:00:00.000001');", true, "SELECT TIMEDIFF(_UTF8MB4'2000:01:01 00:00:00', _UTF8MB4'2000:01:01 00:00:00.000001')"}, + {"SELECT TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01');", true, "SELECT TIMESTAMPDIFF(MONTH, _UTF8MB4'2003-02-01', _UTF8MB4'2003-05-01')"}, + {"SELECT TIMESTAMPDIFF(YEAR,'2002-05-01','2001-01-01');", true, "SELECT TIMESTAMPDIFF(YEAR, _UTF8MB4'2002-05-01', _UTF8MB4'2001-01-01')"}, + {"SELECT TIMESTAMPDIFF(MINUTE,'2003-02-01','2003-05-01 12:05:55');", true, "SELECT TIMESTAMPDIFF(MINUTE, _UTF8MB4'2003-02-01', _UTF8MB4'2003-05-01 12:05:55')"}, + + // select current_time + {"select current_time", true, "SELECT CURRENT_TIME()"}, + {"select current_time()", true, "SELECT CURRENT_TIME()"}, + {"select current_time(6)", true, "SELECT CURRENT_TIME(6)"}, + {"select current_time(-1)", false, ""}, + {"select current_time(1.0)", false, ""}, + {"select current_time('1')", false, ""}, + {"select current_time(null)", false, ""}, + {"select curtime()", true, "SELECT CURTIME()"}, + {"select curtime(6)", true, "SELECT CURTIME(6)"}, + {"select curtime(-1)", false, ""}, + {"select curtime(1.0)", false, ""}, + {"select curtime('1')", false, ""}, + {"select curtime(null)", false, ""}, + + // select utc_timestamp + {"select utc_timestamp", true, "SELECT UTC_TIMESTAMP()"}, + {"select utc_timestamp()", true, "SELECT UTC_TIMESTAMP()"}, + {"select utc_timestamp(6)", true, "SELECT UTC_TIMESTAMP(6)"}, + {"select utc_timestamp(-1)", false, ""}, + {"select utc_timestamp(1.0)", false, ""}, + {"select utc_timestamp('1')", false, ""}, + {"select utc_timestamp(null)", false, ""}, + + // select utc_time + {"select utc_time", true, "SELECT UTC_TIME()"}, + {"select utc_time()", true, "SELECT UTC_TIME()"}, + {"select utc_time(6)", true, "SELECT UTC_TIME(6)"}, + {"select utc_time(-1)", false, ""}, + {"select utc_time(1.0)", false, ""}, + {"select utc_time('1')", false, ""}, + {"select utc_time(null)", false, ""}, + + // for microsecond, second, minute, hour + {"SELECT MICROSECOND('2009-12-31 23:59:59.000010');", true, "SELECT MICROSECOND(_UTF8MB4'2009-12-31 23:59:59.000010')"}, + {"SELECT SECOND('10:05:03');", true, "SELECT SECOND(_UTF8MB4'10:05:03')"}, + {"SELECT MINUTE('2008-02-03 10:05:03');", true, "SELECT MINUTE(_UTF8MB4'2008-02-03 10:05:03')"}, + {"SELECT HOUR(), HOUR('10:05:03');", true, "SELECT HOUR(),HOUR(_UTF8MB4'10:05:03')"}, + + // for date, day, weekday + {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE()", true, "SELECT CURRENT_DATE(),CURRENT_DATE(),CURDATE()"}, + {"SELECT CURRENT_DATE, CURRENT_DATE(), CURDATE(1)", false, ""}, + {"SELECT DATEDIFF('2003-12-31', '2003-12-30');", true, "SELECT DATEDIFF(_UTF8MB4'2003-12-31', _UTF8MB4'2003-12-30')"}, + {"SELECT DATE('2003-12-31 01:02:03');", true, "SELECT DATE(_UTF8MB4'2003-12-31 01:02:03')"}, + {"SELECT DATE();", true, "SELECT DATE()"}, + {"SELECT DATE('2003-12-31 01:02:03', '');", true, "SELECT DATE(_UTF8MB4'2003-12-31 01:02:03', _UTF8MB4'')"}, + {`SELECT DATE_FORMAT('2003-12-31 01:02:03', '%W %M %Y');`, true, "SELECT DATE_FORMAT(_UTF8MB4'2003-12-31 01:02:03', _UTF8MB4'%W %M %Y')"}, + {"SELECT DAY('2007-02-03');", true, "SELECT DAY(_UTF8MB4'2007-02-03')"}, + {"SELECT DAYOFMONTH('2007-02-03');", true, "SELECT DAYOFMONTH(_UTF8MB4'2007-02-03')"}, + {"SELECT DAYOFWEEK('2007-02-03');", true, "SELECT DAYOFWEEK(_UTF8MB4'2007-02-03')"}, + {"SELECT DAYOFYEAR('2007-02-03');", true, "SELECT DAYOFYEAR(_UTF8MB4'2007-02-03')"}, + {"SELECT DAYNAME('2007-02-03');", true, "SELECT DAYNAME(_UTF8MB4'2007-02-03')"}, + {"SELECT FROM_DAYS(1423);", true, "SELECT FROM_DAYS(1423)"}, + {"SELECT WEEKDAY('2007-02-03');", true, "SELECT WEEKDAY(_UTF8MB4'2007-02-03')"}, + + // for utc_date + {"SELECT UTC_DATE, UTC_DATE();", true, "SELECT UTC_DATE(),UTC_DATE()"}, + {"SELECT UTC_DATE(), UTC_DATE()+0", true, "SELECT UTC_DATE(),UTC_DATE()+0"}, + + // for week, month, year + {"SELECT WEEK();", true, "SELECT WEEK()"}, + {"SELECT WEEK('2007-02-03');", true, "SELECT WEEK(_UTF8MB4'2007-02-03')"}, + {"SELECT WEEK('2007-02-03', 0);", true, "SELECT WEEK(_UTF8MB4'2007-02-03', 0)"}, + {"SELECT WEEKOFYEAR('2007-02-03');", true, "SELECT WEEKOFYEAR(_UTF8MB4'2007-02-03')"}, + {"SELECT MONTH('2007-02-03');", true, "SELECT MONTH(_UTF8MB4'2007-02-03')"}, + {"SELECT MONTHNAME('2007-02-03');", true, "SELECT MONTHNAME(_UTF8MB4'2007-02-03')"}, + {"SELECT YEAR('2007-02-03');", true, "SELECT YEAR(_UTF8MB4'2007-02-03')"}, + {"SELECT YEARWEEK('2007-02-03');", true, "SELECT YEARWEEK(_UTF8MB4'2007-02-03')"}, + {"SELECT YEARWEEK('2007-02-03', 0);", true, "SELECT YEARWEEK(_UTF8MB4'2007-02-03', 0)"}, + + // for ADDTIME, SUBTIME + {"SELECT ADDTIME('01:00:00.999999', '02:00:00.999998');", true, "SELECT ADDTIME(_UTF8MB4'01:00:00.999999', _UTF8MB4'02:00:00.999998')"}, + {"SELECT ADDTIME('02:00:00.999998');", true, "SELECT ADDTIME(_UTF8MB4'02:00:00.999998')"}, + {"SELECT ADDTIME();", true, "SELECT ADDTIME()"}, + {"SELECT SUBTIME('01:00:00.999999', '02:00:00.999998');", true, "SELECT SUBTIME(_UTF8MB4'01:00:00.999999', _UTF8MB4'02:00:00.999998')"}, + + // for CONVERT_TZ + {"SELECT CONVERT_TZ();", true, "SELECT CONVERT_TZ()"}, + {"SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00');", true, "SELECT CONVERT_TZ(_UTF8MB4'2004-01-01 12:00:00', _UTF8MB4'+00:00', _UTF8MB4'+10:00')"}, + {"SELECT CONVERT_TZ('2004-01-01 12:00:00','+00:00','+10:00', '+10:00');", true, "SELECT CONVERT_TZ(_UTF8MB4'2004-01-01 12:00:00', _UTF8MB4'+00:00', _UTF8MB4'+10:00', _UTF8MB4'+10:00')"}, + + // for GET_FORMAT + {"SELECT GET_FORMAT(DATE, 'USA');", true, "SELECT GET_FORMAT(DATE, _UTF8MB4'USA')"}, + {"SELECT GET_FORMAT(DATETIME, 'USA');", true, "SELECT GET_FORMAT(DATETIME, _UTF8MB4'USA')"}, + {"SELECT GET_FORMAT(TIME, 'USA');", true, "SELECT GET_FORMAT(TIME, _UTF8MB4'USA')"}, + {"SELECT GET_FORMAT(TIMESTAMP, 'USA');", true, "SELECT GET_FORMAT(DATETIME, _UTF8MB4'USA')"}, + + // for LOCALTIME, LOCALTIMESTAMP + {"SELECT LOCALTIME(), LOCALTIME(1)", true, "SELECT LOCALTIME(),LOCALTIME(1)"}, + {"SELECT LOCALTIMESTAMP(), LOCALTIMESTAMP(2)", true, "SELECT LOCALTIMESTAMP(),LOCALTIMESTAMP(2)"}, + + // for MAKEDATE, MAKETIME + {"SELECT MAKEDATE(2011,31);", true, "SELECT MAKEDATE(2011, 31)"}, + {"SELECT MAKETIME(12,15,30);", true, "SELECT MAKETIME(12, 15, 30)"}, + {"SELECT MAKEDATE();", true, "SELECT MAKEDATE()"}, + {"SELECT MAKETIME();", true, "SELECT MAKETIME()"}, + + // for PERIOD_ADD, PERIOD_DIFF + {"SELECT PERIOD_ADD(200801,2)", true, "SELECT PERIOD_ADD(200801, 2)"}, + {"SELECT PERIOD_DIFF(200802,200703)", true, "SELECT PERIOD_DIFF(200802, 200703)"}, + + // for QUARTER + {"SELECT QUARTER('2008-04-01');", true, "SELECT QUARTER(_UTF8MB4'2008-04-01')"}, + + // for SEC_TO_TIME + {"SELECT SEC_TO_TIME(2378)", true, "SELECT SEC_TO_TIME(2378)"}, + + // for TIME_FORMAT + {`SELECT TIME_FORMAT('100:00:00', '%H %k %h %I %l')`, true, "SELECT TIME_FORMAT(_UTF8MB4'100:00:00', _UTF8MB4'%H %k %h %I %l')"}, + + // for TIME_TO_SEC + {"SELECT TIME_TO_SEC('22:23:00')", true, "SELECT TIME_TO_SEC(_UTF8MB4'22:23:00')"}, + + // for TIMESTAMPADD + {"SELECT TIMESTAMPADD(WEEK,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(WEEK, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_SECOND,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(SECOND, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_MINUTE,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MINUTE, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_HOUR,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(HOUR, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_DAY,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(DAY, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_WEEK,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(WEEK, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_MONTH,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MONTH, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_QUARTER,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(QUARTER, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_YEAR,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(YEAR, 1, _UTF8MB4'2003-01-02')"}, + {"SELECT TIMESTAMPADD(SQL_TSI_MICROSECOND,1,'2003-01-02');", false, ""}, + {"SELECT TIMESTAMPADD(MICROSECOND,1,'2003-01-02');", true, "SELECT TIMESTAMPADD(MICROSECOND, 1, _UTF8MB4'2003-01-02')"}, + + // for TO_DAYS, TO_SECONDS + {"SELECT TO_DAYS('2007-10-07')", true, "SELECT TO_DAYS(_UTF8MB4'2007-10-07')"}, + {"SELECT TO_SECONDS('2009-11-29')", true, "SELECT TO_SECONDS(_UTF8MB4'2009-11-29')"}, + + // for LAST_DAY + {"SELECT LAST_DAY('2003-02-05');", true, "SELECT LAST_DAY(_UTF8MB4'2003-02-05')"}, + + // for UTC_TIME + {"SELECT UTC_TIME(), UTC_TIME(1)", true, "SELECT UTC_TIME(),UTC_TIME(1)"}, + + // for time extract + {`select extract(microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(hour from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(day from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(week from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(WEEK FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(month from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MONTH FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(quarter from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(QUARTER FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(year from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(YEAR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(second_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(SECOND_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(minute_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(minute_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(MINUTE_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(hour_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(hour_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(hour_minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(HOUR_MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(day_microsecond from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_MICROSECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(day_second from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_SECOND FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(day_minute from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_MINUTE FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(day_hour from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(DAY_HOUR FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + {`select extract(year_month from "2011-11-11 10:10:10.123456")`, true, "SELECT EXTRACT(YEAR_MONTH FROM _UTF8MB4'2011-11-11 10:10:10.123456')"}, + + // for from_unixtime + {`select from_unixtime(1447430881)`, true, "SELECT FROM_UNIXTIME(1447430881)"}, + {`select from_unixtime(1447430881.123456)`, true, "SELECT FROM_UNIXTIME(1447430881.123456)"}, + {`select from_unixtime(1447430881.1234567)`, true, "SELECT FROM_UNIXTIME(1447430881.1234567)"}, + {`select from_unixtime(1447430881.9999999)`, true, "SELECT FROM_UNIXTIME(1447430881.9999999)"}, + {`select from_unixtime(1447430881, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, + {`select from_unixtime(1447430881.123456, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881.123456, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, + {`select from_unixtime(1447430881.1234567, "%Y %D %M %h:%i:%s %x")`, true, "SELECT FROM_UNIXTIME(1447430881.1234567, _UTF8MB4'%Y %D %M %h:%i:%s %x')"}, + + // for issue 224 + {`SELECT CAST('test collated returns' AS CHAR CHARACTER SET utf8) COLLATE utf8_bin;`, true, "SELECT CAST(_UTF8MB4'test collated returns' AS CHAR CHARSET UTF8) COLLATE utf8_bin"}, + + // for string functions + // trim + {`SELECT TRIM(' bar ');`, true, "SELECT TRIM(_UTF8MB4' bar ')"}, + {`SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');`, true, "SELECT TRIM(LEADING _UTF8MB4'x' FROM _UTF8MB4'xxxbarxxx')"}, + {`SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');`, true, "SELECT TRIM(BOTH _UTF8MB4'x' FROM _UTF8MB4'xxxbarxxx')"}, + {`SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');`, true, "SELECT TRIM(TRAILING _UTF8MB4'xyz' FROM _UTF8MB4'barxxyz')"}, + {`SELECT LTRIM(' foo ');`, true, "SELECT LTRIM(_UTF8MB4' foo ')"}, + {`SELECT RTRIM(' bar ');`, true, "SELECT RTRIM(_UTF8MB4' bar ')"}, + + {`SELECT RPAD('hi', 6, 'c');`, true, "SELECT RPAD(_UTF8MB4'hi', 6, _UTF8MB4'c')"}, + {`SELECT BIT_LENGTH('hi');`, true, "SELECT BIT_LENGTH(_UTF8MB4'hi')"}, + {`SELECT CHAR(65);`, true, "SELECT CHAR_FUNC(65, NULL)"}, + {`SELECT CHAR_LENGTH('abc');`, true, "SELECT CHAR_LENGTH(_UTF8MB4'abc')"}, + {`SELECT CHARACTER_LENGTH('abc');`, true, "SELECT CHARACTER_LENGTH(_UTF8MB4'abc')"}, + {`SELECT FIELD('ej', 'Hej', 'ej', 'Heja', 'hej', 'foo');`, true, "SELECT FIELD(_UTF8MB4'ej', _UTF8MB4'Hej', _UTF8MB4'ej', _UTF8MB4'Heja', _UTF8MB4'hej', _UTF8MB4'foo')"}, + {`SELECT FIND_IN_SET('foo', 'foo,bar')`, true, "SELECT FIND_IN_SET(_UTF8MB4'foo', _UTF8MB4'foo,bar')"}, + {`SELECT FIND_IN_SET('foo')`, true, "SELECT FIND_IN_SET(_UTF8MB4'foo')"}, // illegal number of argument still pass + {`SELECT MAKE_SET(1,'a'), MAKE_SET(1,'a','b','c')`, true, "SELECT MAKE_SET(1, _UTF8MB4'a'),MAKE_SET(1, _UTF8MB4'a', _UTF8MB4'b', _UTF8MB4'c')"}, + {`SELECT MID('Sakila', -5, 3)`, true, "SELECT MID(_UTF8MB4'Sakila', -5, 3)"}, + {`SELECT OCT(12)`, true, "SELECT OCT(12)"}, + {`SELECT OCTET_LENGTH('text')`, true, "SELECT OCTET_LENGTH(_UTF8MB4'text')"}, + {`SELECT ORD('2')`, true, "SELECT ORD(_UTF8MB4'2')"}, + {`SELECT POSITION('bar' IN 'foobarbar')`, true, "SELECT POSITION(_UTF8MB4'bar' IN _UTF8MB4'foobarbar')"}, + {`SELECT QUOTE('Don\'t!')`, true, "SELECT QUOTE(_UTF8MB4'Don''t!')"}, + {`SELECT BIN(12)`, true, "SELECT BIN(12)"}, + {`SELECT ELT(1, 'ej', 'Heja', 'hej', 'foo')`, true, "SELECT ELT(1, _UTF8MB4'ej', _UTF8MB4'Heja', _UTF8MB4'hej', _UTF8MB4'foo')"}, + {`SELECT EXPORT_SET(5,'Y','N'), EXPORT_SET(5,'Y','N',','), EXPORT_SET(5,'Y','N',',',4)`, true, "SELECT EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N'),EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N', _UTF8MB4','),EXPORT_SET(5, _UTF8MB4'Y', _UTF8MB4'N', _UTF8MB4',', 4)"}, + {`SELECT FORMAT(), FORMAT(12332.2,2,'de_DE'), FORMAT(12332.123456, 4)`, true, "SELECT FORMAT(),FORMAT(12332.2, 2, _UTF8MB4'de_DE'),FORMAT(12332.123456, 4)"}, + {`SELECT FROM_BASE64('abc')`, true, "SELECT FROM_BASE64(_UTF8MB4'abc')"}, + {`SELECT TO_BASE64('abc')`, true, "SELECT TO_BASE64(_UTF8MB4'abc')"}, + {`SELECT INSERT(), INSERT('Quadratic', 3, 4, 'What'), INSTR('foobarbar', 'bar')`, true, "SELECT INSERT_FUNC(),INSERT_FUNC(_UTF8MB4'Quadratic', 3, 4, _UTF8MB4'What'),INSTR(_UTF8MB4'foobarbar', _UTF8MB4'bar')"}, + {`SELECT LOAD_FILE('/tmp/picture')`, true, "SELECT LOAD_FILE(_UTF8MB4'/tmp/picture')"}, + {`SELECT LPAD('hi',4,'??')`, true, "SELECT LPAD(_UTF8MB4'hi', 4, _UTF8MB4'??')"}, + {`SELECT LEFT("foobar", 3)`, true, "SELECT LEFT(_UTF8MB4'foobar', 3)"}, + {`SELECT RIGHT("foobar", 3)`, true, "SELECT RIGHT(_UTF8MB4'foobar', 3)"}, + + // repeat + {`SELECT REPEAT("a", 10);`, true, "SELECT REPEAT(_UTF8MB4'a', 10)"}, + + // for miscellaneous functions + {`SELECT SLEEP(10);`, true, "SELECT SLEEP(10)"}, + {`SELECT ANY_VALUE(@arg);`, true, "SELECT ANY_VALUE(@`arg`)"}, + {`SELECT INET_ATON('10.0.5.9');`, true, "SELECT INET_ATON(_UTF8MB4'10.0.5.9')"}, + {`SELECT INET_NTOA(167773449);`, true, "SELECT INET_NTOA(167773449)"}, + {`SELECT INET6_ATON('fdfe::5a55:caff:fefa:9089');`, true, "SELECT INET6_ATON(_UTF8MB4'fdfe::5a55:caff:fefa:9089')"}, + {`SELECT INET6_NTOA(INET_NTOA(167773449));`, true, "SELECT INET6_NTOA(INET_NTOA(167773449))"}, + {`SELECT IS_FREE_LOCK(@str);`, true, "SELECT IS_FREE_LOCK(@`str`)"}, + {`SELECT IS_IPV4('10.0.5.9');`, true, "SELECT IS_IPV4(_UTF8MB4'10.0.5.9')"}, + {`SELECT IS_IPV4_COMPAT(INET6_ATON('::10.0.5.9'));`, true, "SELECT IS_IPV4_COMPAT(INET6_ATON(_UTF8MB4'::10.0.5.9'))"}, + {`SELECT IS_IPV4_MAPPED(INET6_ATON('::10.0.5.9'));`, true, "SELECT IS_IPV4_MAPPED(INET6_ATON(_UTF8MB4'::10.0.5.9'))"}, + {`SELECT IS_IPV6('10.0.5.9');`, true, "SELECT IS_IPV6(_UTF8MB4'10.0.5.9')"}, + {`SELECT IS_USED_LOCK(@str);`, true, "SELECT IS_USED_LOCK(@`str`)"}, + {`SELECT MASTER_POS_WAIT(@log_name, @log_pos), MASTER_POS_WAIT(@log_name, @log_pos, @timeout), MASTER_POS_WAIT(@log_name, @log_pos, @timeout, @channel_name);`, true, "SELECT MASTER_POS_WAIT(@`log_name`, @`log_pos`),MASTER_POS_WAIT(@`log_name`, @`log_pos`, @`timeout`),MASTER_POS_WAIT(@`log_name`, @`log_pos`, @`timeout`, @`channel_name`)"}, + {`SELECT NAME_CONST('myname', 14);`, true, "SELECT NAME_CONST(_UTF8MB4'myname', 14)"}, + {`SELECT RELEASE_ALL_LOCKS();`, true, "SELECT RELEASE_ALL_LOCKS()"}, + {`SELECT UUID();`, true, "SELECT UUID()"}, + {`SELECT UUID_SHORT()`, true, "SELECT UUID_SHORT()"}, + {`SELECT UUID_TO_BIN('6ccd780c-baba-1026-9564-5b8c656024db')`, true, "SELECT UUID_TO_BIN(_UTF8MB4'6ccd780c-baba-1026-9564-5b8c656024db')"}, + {`SELECT UUID_TO_BIN('6ccd780c-baba-1026-9564-5b8c656024db', 1)`, true, "SELECT UUID_TO_BIN(_UTF8MB4'6ccd780c-baba-1026-9564-5b8c656024db', 1)"}, + {`SELECT BIN_TO_UUID(0x6ccd780cbaba102695645b8c656024db)`, true, "SELECT BIN_TO_UUID(x'6ccd780cbaba102695645b8c656024db')"}, + {`SELECT BIN_TO_UUID(0x6ccd780cbaba102695645b8c656024db, 1)`, true, "SELECT BIN_TO_UUID(x'6ccd780cbaba102695645b8c656024db', 1)"}, + // test illegal arguments + {`SELECT SLEEP();`, true, "SELECT SLEEP()"}, + {`SELECT ANY_VALUE();`, true, "SELECT ANY_VALUE()"}, + {`SELECT INET_ATON();`, true, "SELECT INET_ATON()"}, + {`SELECT INET_NTOA();`, true, "SELECT INET_NTOA()"}, + {`SELECT INET6_ATON();`, true, "SELECT INET6_ATON()"}, + {`SELECT INET6_NTOA(INET_NTOA());`, true, "SELECT INET6_NTOA(INET_NTOA())"}, + {`SELECT IS_FREE_LOCK();`, true, "SELECT IS_FREE_LOCK()"}, + {`SELECT IS_IPV4();`, true, "SELECT IS_IPV4()"}, + {`SELECT IS_IPV4_COMPAT(INET6_ATON());`, true, "SELECT IS_IPV4_COMPAT(INET6_ATON())"}, + {`SELECT IS_IPV4_MAPPED(INET6_ATON());`, true, "SELECT IS_IPV4_MAPPED(INET6_ATON())"}, + {`SELECT IS_IPV6()`, true, "SELECT IS_IPV6()"}, + {`SELECT IS_USED_LOCK();`, true, "SELECT IS_USED_LOCK()"}, + {`SELECT MASTER_POS_WAIT();`, true, "SELECT MASTER_POS_WAIT()"}, + {`SELECT NAME_CONST();`, true, "SELECT NAME_CONST()"}, + {`SELECT RELEASE_ALL_LOCKS(1);`, true, "SELECT RELEASE_ALL_LOCKS(1)"}, + {`SELECT UUID(1);`, true, "SELECT UUID(1)"}, + {`SELECT UUID_SHORT(1)`, true, "SELECT UUID_SHORT(1)"}, + // interval + {`select "2011-11-11 10:10:10.123456" + interval 10 second`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select "2011-11-11 10:10:10.123456" - interval 10 second`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select interval 10 second + "2011-11-11 10:10:10.123456"`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + // for date_add + {`select date_add("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, + {`select date_add("2011-11-11 10:10:10.123456", 10)`, false, ""}, + {`select date_add("2011-11-11 10:10:10.123456", 0.10)`, false, ""}, + {`select date_add("2011-11-11 10:10:10.123456", "11,11")`, false, ""}, + + {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_microsecond)`, false, ""}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_second)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_minute)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_hour)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 10 sql_tsi_day)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_week)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_month)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_quarter)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, + {`select date_add("2011-11-11 10:10:10.123456", interval 1 sql_tsi_year)`, true, "SELECT DATE_ADD(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, + + // for strcmp + {`select strcmp('abc', 'def')`, true, "SELECT STRCMP(_UTF8MB4'abc', _UTF8MB4'def')"}, + + // for adddate + {`select adddate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, + {`select adddate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, + {`select adddate("2011-11-11 10:10:10.123456", 10)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select adddate("2011-11-11 10:10:10.123456", 0.10)`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 0.10 DAY)"}, + {`select adddate("2011-11-11 10:10:10.123456", "11,11")`, true, "SELECT ADDDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11,11' DAY)"}, + + // for date_sub + {`select date_sub("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, + {`select date_sub("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT DATE_SUB(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, + {`select date_sub("2011-11-11 10:10:10.123456", 10)`, false, ""}, + {`select date_sub("2011-11-11 10:10:10.123456", 0.10)`, false, ""}, + {`select date_sub("2011-11-11 10:10:10.123456", "11,11")`, false, ""}, + + // for subdate + {`select subdate("2011-11-11 10:10:10.123456", interval 10 microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MICROSECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 SECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 MINUTE)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 hour)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 HOUR)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10 day)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 week)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 WEEK)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 month)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 MONTH)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 quarter)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 QUARTER)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 1 year)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 1 YEAR)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10.10" second_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10.10' SECOND_MICROSECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10.10" minute_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10.10' MINUTE_MICROSECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" minute_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' MINUTE_SECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10.10" hour_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10.10' HOUR_MICROSECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10:10" hour_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10:10' HOUR_SECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "10:10" hour_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'10:10' HOUR_MINUTE)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval 10.10 hour_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10.10 HOUR_MINUTE)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10.10" day_microsecond)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10.10' DAY_MICROSECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10:10" day_second)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10:10' DAY_SECOND)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10:10" day_minute)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10:10' DAY_MINUTE)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11 10" day_hour)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11 10' DAY_HOUR)"}, + {`select subdate("2011-11-11 10:10:10.123456", interval "11-11" year_month)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11-11' YEAR_MONTH)"}, + {`select subdate("2011-11-11 10:10:10.123456", 10)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 10 DAY)"}, + {`select subdate("2011-11-11 10:10:10.123456", 0.10)`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL 0.10 DAY)"}, + {`select subdate("2011-11-11 10:10:10.123456", "11,11")`, true, "SELECT SUBDATE(_UTF8MB4'2011-11-11 10:10:10.123456', INTERVAL _UTF8MB4'11,11' DAY)"}, + + // for unix_timestamp + {`select unix_timestamp()`, true, "SELECT UNIX_TIMESTAMP()"}, + {`select unix_timestamp('2015-11-13 10:20:19.012')`, true, "SELECT UNIX_TIMESTAMP(_UTF8MB4'2015-11-13 10:20:19.012')"}, + + // for misc functions + {`SELECT GET_LOCK('lock1',10);`, true, "SELECT GET_LOCK(_UTF8MB4'lock1', 10)"}, + {`SELECT RELEASE_LOCK('lock1');`, true, "SELECT RELEASE_LOCK(_UTF8MB4'lock1')"}, + + // for aggregate functions + {`select avg(), avg(c1,c2) from t;`, false, "SELECT AVG(),AVG(`c1`, `c2`) FROM `t`"}, + {`select avg(distinct c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, + {`select avg(distinctrow c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, + {`select avg(distinct all c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, + {`select avg(distinctrow all c1) from t;`, true, "SELECT AVG(DISTINCT `c1`) FROM `t`"}, + {`select avg(c2) from t;`, true, "SELECT AVG(`c2`) FROM `t`"}, + {`select bit_and(c1) from t;`, true, "SELECT BIT_AND(`c1`) FROM `t`"}, + {`select bit_and(all c1) from t;`, true, "SELECT BIT_AND(`c1`) FROM `t`"}, + {`select bit_and(distinct c1) from t;`, false, ""}, + {`select bit_and(distinctrow c1) from t;`, false, ""}, + {`select bit_and(distinctrow all c1) from t;`, false, ""}, + {`select bit_and(distinct all c1) from t;`, false, ""}, + {`select bit_and(), bit_and(distinct c1) from t;`, false, ""}, + {`select bit_and(), bit_and(distinctrow c1) from t;`, false, ""}, + {`select bit_and(), bit_and(all c1) from t;`, false, ""}, + {`select bit_or(c1) from t;`, true, "SELECT BIT_OR(`c1`) FROM `t`"}, + {`select bit_or(all c1) from t;`, true, "SELECT BIT_OR(`c1`) FROM `t`"}, + {`select bit_or(distinct c1) from t;`, false, ""}, + {`select bit_or(distinctrow c1) from t;`, false, ""}, + {`select bit_or(distinctrow all c1) from t;`, false, ""}, + {`select bit_or(distinct all c1) from t;`, false, ""}, + {`select bit_or(), bit_or(distinct c1) from t;`, false, ""}, + {`select bit_or(), bit_or(distinctrow c1) from t;`, false, ""}, + {`select bit_or(), bit_or(all c1) from t;`, false, ""}, + {`select bit_xor(c1) from t;`, true, "SELECT BIT_XOR(`c1`) FROM `t`"}, + {`select bit_xor(all c1) from t;`, true, "SELECT BIT_XOR(`c1`) FROM `t`"}, + {`select bit_xor(distinct c1) from t;`, false, ""}, + {`select bit_xor(distinctrow c1) from t;`, false, ""}, + {`select bit_xor(distinctrow all c1) from t;`, false, ""}, + {`select bit_xor(), bit_xor(distinct c1) from t;`, false, ""}, + {`select bit_xor(), bit_xor(distinctrow c1) from t;`, false, ""}, + {`select bit_xor(), bit_xor(all c1) from t;`, false, ""}, + {`select max(c1,c2) from t;`, false, ""}, + {`select max(distinct c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, + {`select max(distinctrow c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, + {`select max(distinct all c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, + {`select max(distinctrow all c1) from t;`, true, "SELECT MAX(DISTINCT `c1`) FROM `t`"}, + {`select max(c2) from t;`, true, "SELECT MAX(`c2`) FROM `t`"}, + {`select min(c1,c2) from t;`, false, ""}, + {`select min(distinct c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, + {`select min(distinctrow c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, + {`select min(distinct all c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, + {`select min(distinctrow all c1) from t;`, true, "SELECT MIN(DISTINCT `c1`) FROM `t`"}, + {`select min(c2) from t;`, true, "SELECT MIN(`c2`) FROM `t`"}, + {`select sum(c1,c2) from t;`, false, ""}, + {`select sum(distinct c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, + {`select sum(distinctrow c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, + {`select sum(distinct all c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, + {`select sum(distinctrow all c1) from t;`, true, "SELECT SUM(DISTINCT `c1`) FROM `t`"}, + {`select sum(c2) from t;`, true, "SELECT SUM(`c2`) FROM `t`"}, + {`select count(c1) from t;`, true, "SELECT COUNT(`c1`) FROM `t`"}, + {`select count(distinct *) from t;`, false, ""}, + {`select count(distinctrow *) from t;`, false, ""}, + {`select count(*) from t;`, true, "SELECT COUNT(1) FROM `t`"}, + {`select count(distinct c1, c2) from t;`, true, "SELECT COUNT(DISTINCT `c1`, `c2`) FROM `t`"}, + {`select count(distinctrow c1, c2) from t;`, true, "SELECT COUNT(DISTINCT `c1`, `c2`) FROM `t`"}, + {`select count(c1, c2) from t;`, false, ""}, + {`select count(all c1) from t;`, true, "SELECT COUNT(`c1`) FROM `t`"}, + {`select count(distinct all c1) from t;`, false, ""}, + {`select count(distinctrow all c1) from t;`, false, ""}, + {`select approx_count_distinct(c1) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`) FROM `t`"}, + {`select approx_count_distinct(c1, c2) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`, `c2`) FROM `t`"}, + {`select approx_count_distinct(c1, 123) from t;`, true, "SELECT APPROX_COUNT_DISTINCT(`c1`, 123) FROM `t`"}, + {`select approx_percentile(c1) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`) FROM `t`"}, + {`select approx_percentile(c1, c2) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`, `c2`) FROM `t`"}, + {`select approx_percentile(c1, 123) from t;`, true, "SELECT APPROX_PERCENTILE(`c1`, 123) FROM `t`"}, + {`select group_concat(c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(`c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, + {`select group_concat(c2,c1 SEPARATOR ';') from t group by c1;`, true, "SELECT GROUP_CONCAT(`c2`, `c1` SEPARATOR ';') FROM `t` GROUP BY `c1`"}, + {`select group_concat(distinct c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(DISTINCT `c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, + {`select group_concat(distinctrow c2,c1) from t group by c1;`, true, "SELECT GROUP_CONCAT(DISTINCT `c2`, `c1` SEPARATOR ',') FROM `t` GROUP BY `c1`"}, + {`SELECT student_name, GROUP_CONCAT(DISTINCT test_score ORDER BY test_score DESC SEPARATOR ' ') FROM student GROUP BY student_name;`, true, "SELECT `student_name`,GROUP_CONCAT(DISTINCT `test_score` ORDER BY `test_score` DESC SEPARATOR ' ') FROM `student` GROUP BY `student_name`"}, + {`select std(c1), std(all c1), std(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, + {`select std(c1, c2) from t`, false, ""}, + {`select stddev(c1), stddev(all c1), stddev(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, + {`select stddev(c1, c2) from t`, false, ""}, + {`select stddev_pop(c1), stddev_pop(all c1), stddev_pop(distinct c1) from t`, true, "SELECT STDDEV_POP(`c1`),STDDEV_POP(`c1`),STDDEV_POP(DISTINCT `c1`) FROM `t`"}, + {`select stddev_pop(c1, c2) from t`, false, ""}, + {`select stddev_samp(c1), stddev_samp(all c1), stddev_samp(distinct c1) from t`, true, "SELECT STDDEV_SAMP(`c1`),STDDEV_SAMP(`c1`),STDDEV_SAMP(DISTINCT `c1`) FROM `t`"}, + {`select stddev_samp(c1, c2) from t`, false, ""}, + {`select variance(c1), variance(all c1), variance(distinct c1) from t`, true, "SELECT VAR_POP(`c1`),VAR_POP(`c1`),VAR_POP(DISTINCT `c1`) FROM `t`"}, + {`select variance(c1, c2) from t`, false, ""}, + {`select var_pop(c1), var_pop(all c1), var_pop(distinct c1) from t`, true, "SELECT VAR_POP(`c1`),VAR_POP(`c1`),VAR_POP(DISTINCT `c1`) FROM `t`"}, + {`select var_pop(c1, c2) from t`, false, ""}, + {`select var_samp(c1), var_samp(all c1), var_samp(distinct c1) from t`, true, "SELECT VAR_SAMP(`c1`),VAR_SAMP(`c1`),VAR_SAMP(DISTINCT `c1`) FROM `t`"}, + {`select var_samp(c1, c2) from t`, false, ""}, + {`select json_arrayagg(c2) from t group by c1`, true, "SELECT JSON_ARRAYAGG(`c2`) FROM `t` GROUP BY `c1`"}, + {`select json_arrayagg(c1, c2) from t group by c1`, false, ""}, + {`select json_arrayagg(distinct c2) from t group by c1`, false, "SELECT JSON_ARRAYAGG(DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_arrayagg(all c2) from t group by c1`, true, "SELECT JSON_ARRAYAGG(`c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(c1, c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(c1, c2, c3) from t group by c1`, false, ""}, + {`select json_objectagg(distinct c1, c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(DISTINCT `c1`, `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(c1, distinct c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(`c1`, DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(distinct c1, distinct c2) from t group by c1`, false, "SELECT JSON_OBJECTAGG(DISTINCT `c1`, DISTINCT `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(all c1, c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(c1, all c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, + {`select json_objectagg(all c1, all c2) from t group by c1`, true, "SELECT JSON_OBJECTAGG(`c1`, `c2`) FROM `t` GROUP BY `c1`"}, + + // for encryption and compression functions + {`select AES_ENCRYPT('text',UNHEX('F3229A0B371ED2D9441B830D21A390C3'))`, true, "SELECT AES_ENCRYPT(_UTF8MB4'text', UNHEX(_UTF8MB4'F3229A0B371ED2D9441B830D21A390C3'))"}, + {`select AES_DECRYPT(@crypt_str,@key_str)`, true, "SELECT AES_DECRYPT(@`crypt_str`, @`key_str`)"}, + {`select AES_DECRYPT(@crypt_str,@key_str,@init_vector);`, true, "SELECT AES_DECRYPT(@`crypt_str`, @`key_str`, @`init_vector`)"}, + {`SELECT COMPRESS('');`, true, "SELECT COMPRESS(_UTF8MB4'')"}, + {`SELECT DECODE(@crypt_str, @pass_str);`, true, "SELECT DECODE(@`crypt_str`, @`pass_str`)"}, + {`SELECT DES_DECRYPT(@crypt_str), DES_DECRYPT(@crypt_str, @key_str);`, true, "SELECT DES_DECRYPT(@`crypt_str`),DES_DECRYPT(@`crypt_str`, @`key_str`)"}, + {`SELECT DES_ENCRYPT(@str), DES_ENCRYPT(@key_num);`, true, "SELECT DES_ENCRYPT(@`str`),DES_ENCRYPT(@`key_num`)"}, + {`SELECT ENCODE('cleartext', CONCAT('my_random_salt','my_secret_password'));`, true, "SELECT ENCODE(_UTF8MB4'cleartext', CONCAT(_UTF8MB4'my_random_salt', _UTF8MB4'my_secret_password'))"}, + {`SELECT ENCRYPT('hello'), ENCRYPT('hello', @salt);`, true, "SELECT ENCRYPT(_UTF8MB4'hello'),ENCRYPT(_UTF8MB4'hello', @`salt`)"}, + {`SELECT MD5('testing');`, true, "SELECT MD5(_UTF8MB4'testing')"}, + {`SELECT OLD_PASSWORD(@str);`, true, "SELECT OLD_PASSWORD(@`str`)"}, + {`SELECT PASSWORD(@str);`, true, "SELECT PASSWORD_FUNC(@`str`)"}, + {`SELECT RANDOM_BYTES(@len);`, true, "SELECT RANDOM_BYTES(@`len`)"}, + {`SELECT SHA1('abc');`, true, "SELECT SHA1(_UTF8MB4'abc')"}, + {`SELECT SHA('abc');`, true, "SELECT SHA(_UTF8MB4'abc')"}, + {`SELECT SHA2('abc', 224);`, true, "SELECT SHA2(_UTF8MB4'abc', 224)"}, + {`SELECT SM3('abc');`, true, "SELECT SM3(_UTF8MB4'abc')"}, + {`SELECT UNCOMPRESS('any string');`, true, "SELECT UNCOMPRESS(_UTF8MB4'any string')"}, + {`SELECT UNCOMPRESSED_LENGTH(@compressed_string);`, true, "SELECT UNCOMPRESSED_LENGTH(@`compressed_string`)"}, + {`SELECT VALIDATE_PASSWORD_STRENGTH(@str);`, true, "SELECT VALIDATE_PASSWORD_STRENGTH(@`str`)"}, + + // For JSON functions. + {`SELECT JSON_EXTRACT();`, true, "SELECT JSON_EXTRACT()"}, + {`SELECT JSON_UNQUOTE();`, true, "SELECT JSON_UNQUOTE()"}, + {`SELECT JSON_TYPE('[123]');`, true, "SELECT JSON_TYPE(_UTF8MB4'[123]')"}, + {`SELECT JSON_TYPE();`, true, "SELECT JSON_TYPE()"}, + + // For two json grammar sugar. + {`SELECT a->'$.a' FROM t`, true, "SELECT JSON_EXTRACT(`a`, _UTF8MB4'$.a') FROM `t`"}, + {`SELECT a->>'$.a' FROM t`, true, "SELECT JSON_UNQUOTE(JSON_EXTRACT(`a`, _UTF8MB4'$.a')) FROM `t`"}, + {`SELECT '{}'->'$.a' FROM t`, false, ""}, + {`SELECT '{}'->>'$.a' FROM t`, false, ""}, + {`SELECT a->3 FROM t`, false, ""}, + {`SELECT a->>3 FROM t`, false, ""}, + + {`SELECT 1 member of (a)`, true, "SELECT 1 MEMBER OF (`a`)"}, + {`SELECT 1 member of a`, false, ""}, + {`SELECT 1 member a`, false, ""}, + {`SELECT 1 not member of a`, false, ""}, + {`SELECT 1 member of (1+1)`, false, ""}, + {`SELECT concat('a') member of (cast(1 as char(1)))`, true, "SELECT CONCAT(_UTF8MB4'a') MEMBER OF (CAST(1 AS CHAR(1)))"}, + + // Test that quoted identifier can be a function name. + {"SELECT `uuid`()", true, "SELECT UUID()"}, + + // Test sequence function. + {"select nextval(seq)", true, "SELECT NEXTVAL(`seq`)"}, + {"select lastval(seq)", true, "SELECT LASTVAL(`seq`)"}, + {"select setval(seq, 100)", true, "SELECT SETVAL(`seq`, 100)"}, + {"select next value for seq", true, "SELECT NEXTVAL(`seq`)"}, + {"select next value for sequence", true, "SELECT NEXTVAL(`sequence`)"}, + {"select NeXt vAluE for seQuEncE2", true, "SELECT NEXTVAL(`seQuEncE2`)"}, + + // Test regexp functions + {"select regexp_like('aBc', 'abc', 'im');", true, "SELECT REGEXP_LIKE(_UTF8MB4'aBc', _UTF8MB4'abc', _UTF8MB4'im')"}, + {"select regexp_substr('aBc', 'abc', 1, 1, 'im');", true, "SELECT REGEXP_SUBSTR(_UTF8MB4'aBc', _UTF8MB4'abc', 1, 1, _UTF8MB4'im')"}, + {"select regexp_instr('aBc', 'abc', 1, 1, 0, 'im');", true, "SELECT REGEXP_INSTR(_UTF8MB4'aBc', _UTF8MB4'abc', 1, 1, 0, _UTF8MB4'im')"}, + {"select regexp_replace('aBc', 'abc', 'def', 1, 1, 'i');", true, "SELECT REGEXP_REPLACE(_UTF8MB4'aBc', _UTF8MB4'abc', _UTF8MB4'def', 1, 1, _UTF8MB4'i')"}, + + // Test ilike functions + {"select 'aBc' ilike 'abc';", true, "SELECT _UTF8MB4'aBc' ILIKE _UTF8MB4'abc'"}, + } + RunTest(t, table, false) + + // Test in REAL_AS_FLOAT SQL mode. + table2 := []testCase{ + // for cast as float + {"select cast(1 as float);", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(0));", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(24));", true, "SELECT CAST(1 AS FLOAT)"}, + {"select cast(1 as float(25));", true, "SELECT CAST(1 AS DOUBLE)"}, + {"select cast(1 as float(53));", true, "SELECT CAST(1 AS DOUBLE)"}, + {"select cast(1 as float(54));", false, ""}, + + // for cast as real + {"select cast(1 as real);", true, "SELECT CAST(1 AS FLOAT)"}, + } + RunTestInRealAsFloatMode(t, table2, false) +} + +func TestIdentifier(t *testing.T) { + table := []testCase{ + // for quote identifier + {"select `a`, `a.b`, `a b` from t", true, "SELECT `a`,`a.b`,`a b` FROM `t`"}, + // for unquoted identifier + {"create table MergeContextTest$Simple (value integer not null, primary key (value))", true, "CREATE TABLE `MergeContextTest$Simple` (`value` INT NOT NULL,PRIMARY KEY(`value`))"}, + // for as + {"select 1 as a, 1 as `a`, 1 as \"a\", 1 as 'a'", true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`,1 AS `a`"}, + {`select 1 as a, 1 as "a", 1 as 'a'`, true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`"}, + {`select 1 a, 1 "a", 1 'a'`, true, "SELECT 1 AS `a`,1 AS `a`,1 AS `a`"}, + {`select * from t as "a"`, false, ""}, + {`select * from t a`, true, "SELECT * FROM `t` AS `a`"}, + // reserved keyword can't be used as identifier directly, but A.B pattern is an exception + {`select * from ROW`, false, ""}, + {`select COUNT from DESC`, false, ""}, + {`select COUNT from SELECT.DESC`, true, "SELECT `COUNT` FROM `SELECT`.`DESC`"}, + {"use `select`", true, "USE `select`"}, + {"use `sel``ect`", true, "USE `sel``ect`"}, //nolint: misspell + {"use select", false, "USE `select`"}, + {`select * from t as a`, true, "SELECT * FROM `t` AS `a`"}, + {"select 1 full, 1 row, 1 abs", false, ""}, + {"select 1 full, 1 `row`, 1 abs", true, "SELECT 1 AS `full`,1 AS `row`,1 AS `abs`"}, + {"select * from t full, t1 row, t2 abs", false, ""}, + {"select * from t full, t1 `row`, t2 abs", true, "SELECT * FROM ((`t` AS `full`) JOIN `t1` AS `row`) JOIN `t2` AS `abs`"}, + // for issue 1878, identifiers may begin with digit. + {"create database 123test", true, "CREATE DATABASE `123test`"}, + {"create database 123", false, "CREATE DATABASE `123`"}, + {"create database `123`", true, "CREATE DATABASE `123`"}, + {"create database `12``3`", true, "CREATE DATABASE `12``3`"}, + {"create table `123` (123a1 int)", true, "CREATE TABLE `123` (`123a1` INT)"}, + {"create table 123 (123a1 int)", false, ""}, + {fmt.Sprintf("select * from t%cble", 0), false, ""}, + // for issue 3954, should NOT be recognized as identifiers. + {`select .78+123`, true, "SELECT 0.78+123"}, + {`select .78+.21`, true, "SELECT 0.78+0.21"}, + {`select .78-123`, true, "SELECT 0.78-123"}, + {`select .78-.21`, true, "SELECT 0.78-0.21"}, + {`select .78--123`, true, "SELECT 0.78--123"}, + {`select .78*123`, true, "SELECT 0.78*123"}, + {`select .78*.21`, true, "SELECT 0.78*0.21"}, + {`select .78/123`, true, "SELECT 0.78/123"}, + {`select .78/.21`, true, "SELECT 0.78/0.21"}, + {`select .78,123`, true, "SELECT 0.78,123"}, + {`select .78,.21`, true, "SELECT 0.78,0.21"}, + {`select .78 , 123`, true, "SELECT 0.78,123"}, + {`select .78.123`, false, ""}, + {`select .78#123`, true, "SELECT 0.78"}, + {`insert float_test values(.67, 'string');`, true, "INSERT INTO `float_test` VALUES (0.67,_UTF8MB4'string')"}, + {`select .78'123'`, true, "SELECT 0.78 AS `123`"}, + {"select .78`123`", true, "SELECT 0.78 AS `123`"}, + {`select .78"123"`, true, "SELECT 0.78 AS `123`"}, + {"select 111 as \xd6\xf7", true, "SELECT 111 AS `??`"}, + } + RunTest(t, table, false) +} + +func TestBuiltinFuncAsIdentifier(t *testing.T) { + whitespaceFuncs := []struct { + funcName string + args string + }{ + {"BIT_AND", "`c1`"}, + {"BIT_OR", "`c1`"}, + {"BIT_XOR", "`c1`"}, + {"CAST", "1 AS FLOAT"}, + {"COUNT", "1"}, + {"CURDATE", ""}, + {"CURTIME", ""}, + {"DATE_ADD", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, + {"DATE_SUB", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, + {"EXTRACT", "SECOND FROM _UTF8MB4'2011-11-11 10:10:10'"}, + {"GROUP_CONCAT", "`c2`, `c1` SEPARATOR ','"}, + {"MAX", "`c1`"}, + {"MID", "_UTF8MB4'Sakila', -5, 3"}, + {"MIN", "`c1`"}, + {"NOW", ""}, + {"POSITION", "_UTF8MB4'bar' IN _UTF8MB4'foobarbar'"}, + {"STDDEV_POP", "`c1`"}, + {"STDDEV_SAMP", "`c1`"}, + {"SUBSTR", "_UTF8MB4'Quadratically', 5"}, + {"SUBSTRING", "_UTF8MB4'Quadratically', 5"}, + {"SUM", "`c1`"}, + {"SYSDATE", ""}, + {"TRIM", "_UTF8MB4' foo '"}, + {"VAR_POP", "`c1`"}, + {"VAR_SAMP", "`c1`"}, + } + + testcases := make([]testCase, 0, 3*len(whitespaceFuncs)) + runTests := func(ignoreSpace bool) { + p := parser.New() + if ignoreSpace { + p.SetSQLMode(mysql.ModeIgnoreSpace) + } + for _, c := range testcases { + _, _, err := p.Parse(c.src, "", "") + if !c.ok { + require.Errorf(t, err, "source %v", c.src) + continue + } + require.NoErrorf(t, err, "source %v", c.src) + if c.ok && !ignoreSpace { + RunRestoreTest(t, c.src, c.restore, false) + } + } + } + + for _, function := range whitespaceFuncs { + // `x` is recognized as a function name for `x()`. + testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) + + // In MySQL, `select x ()` is recognized as a stored function. + // In TiDB, most of these functions are recognized as identifiers while some are builtin functions (such as COUNT, CURDATE) + // because the later ones are not added to the token map. We'd better not to modify it since it breaks compatibility. + // For example, `select CURDATE ()` reports an error in MySQL but it works well for TiDB. + + // `x` is recognized as an identifier for `x ()`. + testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) + + // `x` is recognized as a function name for `x()`. + testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), false, ""}) + } + runTests(false) + + testcases = make([]testCase, 0, 4*len(whitespaceFuncs)) + for _, function := range whitespaceFuncs { + testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) + testcases = append(testcases, testCase{fmt.Sprintf("select %s (%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) + testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), false, ""}) + testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), false, ""}) + } + runTests(true) + + normalFuncs := []struct { + funcName string + args string + }{ + {"ADDDATE", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, + {"SESSION_USER", ""}, + {"SUBDATE", "_UTF8MB4'2011-11-11 10:10:10', INTERVAL 10 SECOND"}, + {"SYSTEM_USER", ""}, + } + + testcases = make([]testCase, 0, 4*len(normalFuncs)) + for _, function := range normalFuncs { + // `x` is recognized as a function name for `select x()`. + testcases = append(testcases, testCase{fmt.Sprintf("select %s(%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) + + // `x` is recognized as a function name for `select x ()`. + testcases = append(testcases, testCase{fmt.Sprintf("select %s (%s)", function.funcName, function.args), true, fmt.Sprintf("SELECT %s(%s)", function.funcName, function.args)}) + + // `x` is recognized as an identifier for `create table x ()`. + testcases = append(testcases, testCase{fmt.Sprintf("create table %s (a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) + + // `x` is recognized as an identifier for `create table x()`. + testcases = append(testcases, testCase{fmt.Sprintf("create table %s(a int)", function.funcName), true, fmt.Sprintf("CREATE TABLE `%s` (`a` INT)", function.funcName)}) + } + runTests(false) + runTests(true) +} + +func TestDDL(t *testing.T) { + table := []testCase{ + {"CREATE", false, ""}, + {"CREATE TABLE", false, ""}, + {"CREATE TABLE foo (", false, ""}, + {"CREATE TABLE foo ()", false, ""}, + {"CREATE TABLE foo ();", false, ""}, + {"CREATE TABLE foo.* (a varchar(50), b int);", false, ""}, + {"CREATE TABLE foo (a varchar(50), b int);", true, "CREATE TABLE `foo` (`a` VARCHAR(50),`b` INT)"}, + {"CREATE TABLE foo (a TINYINT UNSIGNED);", true, "CREATE TABLE `foo` (`a` TINYINT UNSIGNED)"}, + {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED)", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, + {"CREATE TABLE foo (a bigint unsigned, b bool);", true, "CREATE TABLE `foo` (`a` BIGINT UNSIGNED,`b` TINYINT(1))"}, + {"CREATE TABLE foo (a TINYINT, b SMALLINT) CREATE TABLE bar (x INT, y int64)", false, ""}, + {"CREATE TABLE foo (a int, b float); CREATE TABLE bar (x double, y float)", true, "CREATE TABLE `foo` (`a` INT,`b` FLOAT); CREATE TABLE `bar` (`x` DOUBLE,`y` FLOAT)"}, + {"CREATE TABLE foo (a bytes)", false, ""}, + {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED)", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, + {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) -- foo", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, + {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) // foo", false, ""}, + {"CREATE TABLE foo (a SMALLINT UNSIGNED, b INT UNSIGNED) /* foo */", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, + {"CREATE TABLE foo /* foo */ (a SMALLINT UNSIGNED, b INT UNSIGNED) /* foo */", true, "CREATE TABLE `foo` (`a` SMALLINT UNSIGNED,`b` INT UNSIGNED)"}, + {"CREATE TABLE foo (name CHAR(50) BINARY);", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY)"}, + {"CREATE TABLE foo (name CHAR(50) COLLATE utf8_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE utf8_bin)"}, + {"CREATE TABLE foo (id varchar(50) collate utf8_bin);", true, "CREATE TABLE `foo` (`id` VARCHAR(50) COLLATE utf8_bin)"}, + {"CREATE TABLE foo (name CHAR(50) CHARACTER SET UTF8)", true, "CREATE TABLE `foo` (`name` CHAR(50) CHARACTER SET UTF8)"}, + {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 BINARY)", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY CHARACTER SET UTF8)"}, + {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 BINARY CHARACTER set utf8)", false, ""}, + {"CREATE TABLE foo (name CHAR(50) BINARY CHARACTER SET utf8 COLLATE utf8_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) BINARY CHARACTER SET UTF8 COLLATE utf8_bin)"}, + {"CREATE TABLE foo (name CHAR(50) CHARACTER SET utf8 COLLATE utf8_bin COLLATE ascii_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) CHARACTER SET UTF8 COLLATE utf8_bin COLLATE ascii_bin)"}, + {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin COLLATE latin1_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin COLLATE latin1_bin)"}, + {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin)", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin)"}, + {"CREATE TABLE foo (a.b, b);", false, ""}, + {"CREATE TABLE foo (a, b.c);", false, ""}, + {"CREATE TABLE (name CHAR(50) BINARY)", false, ""}, + {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin, INDEX (name ASC))", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin,INDEX(`name`))"}, + {"CREATE TABLE foo (name CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin, INDEX (name DESC))", true, "CREATE TABLE `foo` (`name` CHAR(50) COLLATE ascii_bin PRIMARY KEY COLLATE latin1_bin,INDEX(`name` DESC))"}, + // test enable or disable cached table + {"ALTER TABLE tmp CACHE", true, "ALTER TABLE `tmp` CACHE"}, + {"ALTER TABLE tmp NOCACHE", true, "ALTER TABLE `tmp` NOCACHE"}, + // for create temporary table + {"CREATE TEMPORARY TABLE t (a varchar(50), b int);", true, "CREATE TEMPORARY TABLE `t` (`a` VARCHAR(50),`b` INT)"}, + {"CREATE TEMPORARY TABLE t LIKE t1", true, "CREATE TEMPORARY TABLE `t` LIKE `t1`"}, + {"DROP TEMPORARY TABLE t", true, "DROP TEMPORARY TABLE `t`"}, + {"create global temporary table t (a int, b varchar(255)) on commit delete rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) ON COMMIT DELETE ROWS"}, + {"create temporary table t (a int, b varchar(255))", true, "CREATE TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255))"}, + {"create global temporary table t (a int, b varchar(255))", false, ""}, // missing on commit delete rows + {"create temporary table t (a int, b varchar(255)) on commit delete rows", false, ""}, + {"create table t (a int, b varchar(255)) on commit delete rows", false, ""}, + {"create global temporary table t (a int, b varchar(255)) partition by hash(a) partitions 10 on commit delete rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) PARTITION BY HASH (`a`) PARTITIONS 10 ON COMMIT DELETE ROWS"}, + {"create global temporary table t (a int, b varchar(255)) on commit preserve rows", true, "CREATE GLOBAL TEMPORARY TABLE `t` (`a` INT,`b` VARCHAR(255)) ON COMMIT PRESERVE ROWS"}, + {"drop global temporary table t", true, "DROP GLOBAL TEMPORARY TABLE `t`"}, + {"drop temporary table t", true, "DROP TEMPORARY TABLE `t`"}, + // test use key word as column name + {"CREATE TABLE foo (pump varchar(50), b int);", true, "CREATE TABLE `foo` (`pump` VARCHAR(50),`b` INT)"}, + {"CREATE TABLE foo (drainer varchar(50), b int);", true, "CREATE TABLE `foo` (`drainer` VARCHAR(50),`b` INT)"}, + {"CREATE TABLE foo (node_id varchar(50), b int);", true, "CREATE TABLE `foo` (`node_id` VARCHAR(50),`b` INT)"}, + {"CREATE TABLE foo (node_state varchar(50), b int);", true, "CREATE TABLE `foo` (`node_state` VARCHAR(50),`b` INT)"}, + // for table option + {"create table t (c int) avg_row_length = 3", true, "CREATE TABLE `t` (`c` INT) AVG_ROW_LENGTH = 3"}, + {"create table t (c int) avg_row_length 3", true, "CREATE TABLE `t` (`c` INT) AVG_ROW_LENGTH = 3"}, + {"create table t (c int) checksum = 0", true, "CREATE TABLE `t` (`c` INT) CHECKSUM = 0"}, + {"create table t (c int) checksum 1", true, "CREATE TABLE `t` (`c` INT) CHECKSUM = 1"}, + {"create table t (c int) table_checksum = 0", true, "CREATE TABLE `t` (`c` INT) TABLE_CHECKSUM = 0"}, + {"create table t (c int) table_checksum 1", true, "CREATE TABLE `t` (`c` INT) TABLE_CHECKSUM = 1"}, + {"create table t (c int) compression = 'NONE'", true, "CREATE TABLE `t` (`c` INT) COMPRESSION = 'NONE'"}, + {"create table t (c int) compression 'lz4'", true, "CREATE TABLE `t` (`c` INT) COMPRESSION = 'lz4'"}, + {"create table t (c int) connection = 'abc'", true, "CREATE TABLE `t` (`c` INT) CONNECTION = 'abc'"}, + {"create table t (c int) connection 'abc'", true, "CREATE TABLE `t` (`c` INT) CONNECTION = 'abc'"}, + {"create table t (c int) key_block_size = 1024", true, "CREATE TABLE `t` (`c` INT) KEY_BLOCK_SIZE = 1024"}, + {"create table t (c int) key_block_size 1024", true, "CREATE TABLE `t` (`c` INT) KEY_BLOCK_SIZE = 1024"}, + {"create table t (c int) max_rows = 1000", true, "CREATE TABLE `t` (`c` INT) MAX_ROWS = 1000"}, + {"create table t (c int) max_rows 1000", true, "CREATE TABLE `t` (`c` INT) MAX_ROWS = 1000"}, + {"create table t (c int) min_rows = 1000", true, "CREATE TABLE `t` (`c` INT) MIN_ROWS = 1000"}, + {"create table t (c int) min_rows 1000", true, "CREATE TABLE `t` (`c` INT) MIN_ROWS = 1000"}, + {"create table t (c int) password = 'abc'", true, "CREATE TABLE `t` (`c` INT) PASSWORD = 'abc'"}, + {"create table t (c int) password 'abc'", true, "CREATE TABLE `t` (`c` INT) PASSWORD = 'abc'"}, + {"create table t (c int) DELAY_KEY_WRITE=1", true, "CREATE TABLE `t` (`c` INT) DELAY_KEY_WRITE = 1"}, + {"create table t (c int) DELAY_KEY_WRITE 1", true, "CREATE TABLE `t` (`c` INT) DELAY_KEY_WRITE = 1"}, + {"create table t (c int) ROW_FORMAT = default", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DEFAULT"}, + {"create table t (c int) ROW_FORMAT default", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DEFAULT"}, + {"create table t (c int) ROW_FORMAT = fixed", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = FIXED"}, + {"create table t (c int) ROW_FORMAT = compressed", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = COMPRESSED"}, + {"create table t (c int) ROW_FORMAT = compact", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = COMPACT"}, + {"create table t (c int) ROW_FORMAT = redundant", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = REDUNDANT"}, + {"create table t (c int) ROW_FORMAT = dynamic", true, "CREATE TABLE `t` (`c` INT) ROW_FORMAT = DYNAMIC"}, + {"create table t (c int) STATS_PERSISTENT = default", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, + {"create table t (c int) STATS_PERSISTENT = 0", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, + {"create table t (c int) STATS_PERSISTENT = 1", true, "CREATE TABLE `t` (`c` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, + {"create table t (c int) STATS_SAMPLE_PAGES 0", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 0"}, + {"create table t (c int) STATS_SAMPLE_PAGES 10", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 10"}, + {"create table t (c int) STATS_SAMPLE_PAGES = 10", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = 10"}, + {"create table t (c int) STATS_SAMPLE_PAGES = default", true, "CREATE TABLE `t` (`c` INT) STATS_SAMPLE_PAGES = DEFAULT"}, + {"create table t (c int) PACK_KEYS = 1", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, + {"create table t (c int) PACK_KEYS = 0", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, + {"create table t (c int) PACK_KEYS = DEFAULT", true, "CREATE TABLE `t` (`c` INT) PACK_KEYS = DEFAULT /* TableOptionPackKeys is not supported */ "}, + {"create table t (c int) STORAGE DISK", true, "CREATE TABLE `t` (`c` INT) STORAGE DISK"}, + {"create table t (c int) STORAGE MEMORY", true, "CREATE TABLE `t` (`c` INT) STORAGE MEMORY"}, + {"create table t (c int) SECONDARY_ENGINE null", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = NULL"}, + {"create table t (c int) SECONDARY_ENGINE = innodb", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = 'innodb'"}, + {"create table t (c int) SECONDARY_ENGINE 'null'", true, "CREATE TABLE `t` (`c` INT) SECONDARY_ENGINE = 'null'"}, + {`create table testTableCompression (c VARCHAR(15000)) compression="ZLIB";`, true, "CREATE TABLE `testTableCompression` (`c` VARCHAR(15000)) COMPRESSION = 'ZLIB'"}, + {`create table t1 (c1 int) compression="zlib";`, true, "CREATE TABLE `t1` (`c1` INT) COMPRESSION = 'zlib'"}, + {`create table t1 (c1 int) collate=binary;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT COLLATE = BINARY"}, + {`create table t1 (c1 int) collate=utf8mb4_0900_as_cs;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT COLLATE = UTF8MB4_0900_AS_CS"}, + {`create table t1 (c1 int) default charset=binary collate=binary;`, true, "CREATE TABLE `t1` (`c1` INT) DEFAULT CHARACTER SET = BINARY DEFAULT COLLATE = BINARY"}, + + // for table option `UNION` + {"ALTER TABLE t_n UNION ( ), KEY_BLOCK_SIZE = 1", true, "ALTER TABLE `t_n` UNION = (), KEY_BLOCK_SIZE = 1"}, + {"ALTER TABLE d_n.t_n UNION ( t_n ) REMOVE PARTITIONING", true, "ALTER TABLE `d_n`.`t_n` UNION = (`t_n`) REMOVE PARTITIONING"}, + {"ALTER TABLE d_n.t_n LOCK DEFAULT , UNION = ( t_n , d_n.t_n ) REMOVE PARTITIONING", true, "ALTER TABLE `d_n`.`t_n` LOCK = DEFAULT, UNION = (`t_n`,`d_n`.`t_n`) REMOVE PARTITIONING"}, + {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , MAX_ROWS 10, UNION ( d_n.t_n ) , ROW_FORMAT REDUNDANT, STATS_PERSISTENT = DEFAULT", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, MAX_ROWS = 10, UNION = (`d_n`.`t_n`), ROW_FORMAT = REDUNDANT, STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ "}, + + // partition option + {"create table t (b int) partition by range columns (b) (partition p0 values less than (not 3), partition p2 values less than (20));", false, ""}, + {"create table t (b int) partition by range columns (b) (partition p0 values less than (1 or 3), partition p2 values less than (20));", false, ""}, + {"create table t (b int) partition by range columns (b) (partition p0 values less than (3 is null), partition p2 values less than (20));", false, ""}, + {"create table t (b int) partition by range (b is null) (partition p0 values less than (10));", false, ""}, + {"create table t (b int) partition by list (not b) (partition p0 values in (10, 20));", false, ""}, + {"create table t (b int) partition by hash ( not b );", false, ""}, + {"create table t (b int) partition by range columns (b) (partition p0 values less than (3 in (3, 4, 5)), partition p2 values less than (20));", false, ""}, + {"CREATE TABLE t (id int) ENGINE = INNDB PARTITION BY RANGE (id) (PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN (20));", true, "CREATE TABLE `t` (`id` INT) ENGINE = INNDB PARTITION BY RANGE (`id`) (PARTITION `p0` VALUES LESS THAN (10),PARTITION `p1` VALUES LESS THAN (20))"}, + {"create table t (c int) PARTITION BY HASH (c) PARTITIONS 32;", true, "CREATE TABLE `t` (`c` INT) PARTITION BY HASH (`c`) PARTITIONS 32"}, + {"create table t (c int) PARTITION BY HASH (Year(VDate)) (PARTITION p1980 VALUES LESS THAN (1980) ENGINE = MyISAM, PARTITION p1990 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION pothers VALUES LESS THAN MAXVALUE ENGINE = MyISAM)", false, ""}, + {"create table t (c int) PARTITION BY RANGE (Year(VDate)) (PARTITION p1980 VALUES LESS THAN (1980) ENGINE = MyISAM, PARTITION p1990 VALUES LESS THAN (1990) ENGINE = MyISAM, PARTITION pothers VALUES LESS THAN MAXVALUE ENGINE = MyISAM)", true, "CREATE TABLE `t` (`c` INT) PARTITION BY RANGE (YEAR(`VDate`)) (PARTITION `p1980` VALUES LESS THAN (1980) ENGINE = MyISAM,PARTITION `p1990` VALUES LESS THAN (1990) ENGINE = MyISAM,PARTITION `pothers` VALUES LESS THAN (MAXVALUE) ENGINE = MyISAM)"}, + {"create table t (c int, `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '') PARTITION BY RANGE (UNIX_TIMESTAMP(create_time)) (PARTITION p201610 VALUES LESS THAN(1477929600), PARTITION p201611 VALUES LESS THAN(1480521600),PARTITION p201612 VALUES LESS THAN(1483200000),PARTITION p201701 VALUES LESS THAN(1485878400),PARTITION p201702 VALUES LESS THAN(1488297600),PARTITION p201703 VALUES LESS THAN(1490976000))", true, "CREATE TABLE `t` (`c` INT,`create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP() COMMENT '') PARTITION BY RANGE (UNIX_TIMESTAMP(`create_time`)) (PARTITION `p201610` VALUES LESS THAN (1477929600),PARTITION `p201611` VALUES LESS THAN (1480521600),PARTITION `p201612` VALUES LESS THAN (1483200000),PARTITION `p201701` VALUES LESS THAN (1485878400),PARTITION `p201702` VALUES LESS THAN (1488297600),PARTITION `p201703` VALUES LESS THAN (1490976000))"}, + {"CREATE TABLE `md_product_shop` (`shopCode` varchar(4) DEFAULT NULL COMMENT '地点') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 /*!50100 PARTITION BY KEY (shopCode) PARTITIONS 19 */;", true, "CREATE TABLE `md_product_shop` (`shopCode` VARCHAR(4) DEFAULT NULL COMMENT '地点') ENGINE = InnoDB DEFAULT CHARACTER SET = UTF8MB4 PARTITION BY KEY (`shopCode`) PARTITIONS 19"}, + {"CREATE TABLE `payinfo1` (`id` bigint(20) NOT NULL AUTO_INCREMENT, `oderTime` datetime NOT NULL) ENGINE=InnoDB AUTO_INCREMENT=641533032 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8 /*!50500 PARTITION BY RANGE COLUMNS(oderTime) (PARTITION P2011 VALUES LESS THAN ('2012-01-01 00:00:00') ENGINE = InnoDB, PARTITION P1201 VALUES LESS THAN ('2012-02-01 00:00:00') ENGINE = InnoDB, PARTITION PMAX VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB)*/;", true, "CREATE TABLE `payinfo1` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`oderTime` DATETIME NOT NULL) ENGINE = InnoDB AUTO_INCREMENT = 641533032 DEFAULT CHARACTER SET = UTF8 ROW_FORMAT = COMPRESSED KEY_BLOCK_SIZE = 8 PARTITION BY RANGE COLUMNS (`oderTime`) (PARTITION `P2011` VALUES LESS THAN (_UTF8MB4'2012-01-01 00:00:00') ENGINE = InnoDB,PARTITION `P1201` VALUES LESS THAN (_UTF8MB4'2012-02-01 00:00:00') ENGINE = InnoDB,PARTITION `PMAX` VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB)"}, + {`CREATE TABLE app_channel_daily_report (id bigint(20) NOT NULL AUTO_INCREMENT, app_version varchar(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'default', gmt_create datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (id)) ENGINE=InnoDB AUTO_INCREMENT=33703438 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci +/*!50100 PARTITION BY RANGE (month(gmt_create)-1) +(PARTITION part0 VALUES LESS THAN (1) COMMENT = '1月份' ENGINE = InnoDB, + PARTITION part1 VALUES LESS THAN (2) COMMENT = '2月份' ENGINE = InnoDB, + PARTITION part2 VALUES LESS THAN (3) COMMENT = '3月份' ENGINE = InnoDB, + PARTITION part3 VALUES LESS THAN (4) COMMENT = '4月份' ENGINE = InnoDB, + PARTITION part4 VALUES LESS THAN (5) COMMENT = '5月份' ENGINE = InnoDB, + PARTITION part5 VALUES LESS THAN (6) COMMENT = '6月份' ENGINE = InnoDB, + PARTITION part6 VALUES LESS THAN (7) COMMENT = '7月份' ENGINE = InnoDB, + PARTITION part7 VALUES LESS THAN (8) COMMENT = '8月份' ENGINE = InnoDB, + PARTITION part8 VALUES LESS THAN (9) COMMENT = '9月份' ENGINE = InnoDB, + PARTITION part9 VALUES LESS THAN (10) COMMENT = '10月份' ENGINE = InnoDB, + PARTITION part10 VALUES LESS THAN (11) COMMENT = '11月份' ENGINE = InnoDB, + PARTITION part11 VALUES LESS THAN (12) COMMENT = '12月份' ENGINE = InnoDB) */ ;`, true, "CREATE TABLE `app_channel_daily_report` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`app_version` VARCHAR(32) COLLATE utf8_unicode_ci NOT NULL DEFAULT _UTF8MB4'default',`gmt_create` DATETIME NOT NULL COMMENT '创建时间',PRIMARY KEY(`id`)) ENGINE = InnoDB AUTO_INCREMENT = 33703438 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_UNICODE_CI PARTITION BY RANGE (MONTH(`gmt_create`)-1) (PARTITION `part0` VALUES LESS THAN (1) COMMENT = '1月份' ENGINE = InnoDB,PARTITION `part1` VALUES LESS THAN (2) COMMENT = '2月份' ENGINE = InnoDB,PARTITION `part2` VALUES LESS THAN (3) COMMENT = '3月份' ENGINE = InnoDB,PARTITION `part3` VALUES LESS THAN (4) COMMENT = '4月份' ENGINE = InnoDB,PARTITION `part4` VALUES LESS THAN (5) COMMENT = '5月份' ENGINE = InnoDB,PARTITION `part5` VALUES LESS THAN (6) COMMENT = '6月份' ENGINE = InnoDB,PARTITION `part6` VALUES LESS THAN (7) COMMENT = '7月份' ENGINE = InnoDB,PARTITION `part7` VALUES LESS THAN (8) COMMENT = '8月份' ENGINE = InnoDB,PARTITION `part8` VALUES LESS THAN (9) COMMENT = '9月份' ENGINE = InnoDB,PARTITION `part9` VALUES LESS THAN (10) COMMENT = '10月份' ENGINE = InnoDB,PARTITION `part10` VALUES LESS THAN (11) COMMENT = '11月份' ENGINE = InnoDB,PARTITION `part11` VALUES LESS THAN (12) COMMENT = '12月份' ENGINE = InnoDB)"}, + + // for placement option + // 1. create table + {`create table t (c int) primary_region="us";`, false, ""}, + {`create table t (c int) regions="us,3";`, false, ""}, + {`create table t (c int) followers="us,3";`, false, ""}, + {`create table t (c int) followers=3;`, false, ""}, + {`create table t (c int) followers=0;`, false, ""}, + {`create table t (c int) voters=3;`, false, ""}, + {`create table t (c int) learners="us,3";`, false, ""}, + {`create table t (c int) learners=3;`, false, ""}, + {`create table t (c int) schedule="even";`, false, ""}, + {`create table t (c int) constraints="ww";`, false, ""}, + {`create table t (c int) leader_constraints="ww";`, false, ""}, + {`create table t (c int) follower_constraints="ww";`, false, ""}, + {`create table t (c int) voter_constraints="ww";`, false, ""}, + {`create table t (c int) learner_constraints="ww";`, false, ""}, + {`create table t (c int) survival_preference="ww";`, false, ""}, + {`create table t (c int) /*T![placement] primary_region="us" */;`, false, ""}, + {`create table t (c int) /*T![placement] regions="us,3" */;`, false, ""}, + {`create table t (c int) /*T![placement] followers="us,3 */";`, false, ""}, + {`create table t (c int) /*T![placement] followers=3 */;`, false, ""}, + {`create table t (c int) /*T![placement] followers=0 */;`, false, ""}, + {`create table t (c int) /*T![placement] voters="us,3" */;`, false, ""}, + {`create table t (c int) /*T![placement] primary_region="us" regions="us,3" */;`, false, ""}, + {`create table t (c int) placement policy="ww";`, true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `ww`"}, + {"create table t (c int) /*T![placement] placement policy=`x` */;", true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `x`"}, + {`create table t (c int) /*T![placement] placement policy="y" */;`, true, "CREATE TABLE `t` (`c` INT) PLACEMENT POLICY = `y`"}, + // 2. alter table + {`alter table t primary_region="us";`, false, ""}, + {`alter table t regions="us,3";`, false, ""}, + {`alter table t followers=3;`, false, ""}, + {`alter table t followers=0;`, false, ""}, + {`alter table t voters=3;`, false, ""}, + {`alter table t learners=3;`, false, ""}, + {`alter table t schedule="even";`, false, ""}, + {`alter table t constraints="ww";`, false, ""}, + {`alter table t leader_constraints="ww";`, false, ""}, + {`alter table t follower_constraints="ww";`, false, ""}, + {`alter table t voter_constraints="ww";`, false, ""}, + {`alter table t learner_constraints="ww";`, false, ""}, + {`alter table t /*T![placement] primary_region="us" */;`, false, ""}, + {`alter table t placement policy="ww";`, true, "ALTER TABLE `t` PLACEMENT POLICY = `ww`"}, + {`alter table t /*T![placement] placement policy="ww" */;`, true, "ALTER TABLE `t` PLACEMENT POLICY = `ww`"}, + {`alter table t compact;`, true, "ALTER TABLE `t` COMPACT"}, + {`alter table t compact tiflash replica;`, true, "ALTER TABLE `t` COMPACT TIFLASH REPLICA"}, + {`alter table t compact partition p1,p2;`, true, "ALTER TABLE `t` COMPACT PARTITION `p1`,`p2`"}, + {`alter table t compact partition p1,p2 tiflash replica;`, true, "ALTER TABLE `t` COMPACT PARTITION `p1`,`p2` TIFLASH REPLICA"}, + // 3. create db + {`create database t primary_region="us";`, false, ""}, + {`create database t regions="us,3";`, false, ""}, + {`create database t followers=3;`, false, ""}, + {`create database t followers=0;`, false, ""}, + {`create database t voters=3;`, false, ""}, + {`create database t learners=3;`, false, ""}, + {`create database t schedule="even";`, false, ""}, + {`create database t constraints="ww";`, false, ""}, + {`create database t leader_constraints="ww";`, false, ""}, + {`create database t follower_constraints="ww";`, false, ""}, + {`create database t voter_constraints="ww";`, false, ""}, + {`create database t learner_constraints="ww";`, false, ""}, + {`create database t /*T![placement] primary_region="us" */;`, false, ""}, + {`create database t placement policy="ww";`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, + {`create database t default placement policy="ww";`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, + {`create database t /*T![placement] placement policy="ww" */;`, true, "CREATE DATABASE `t` PLACEMENT POLICY = `ww`"}, + // 4. alter db + {`alter database t primary_region="us";`, false, ""}, + {`alter database t regions="us,3";`, false, ""}, + {`alter database t followers=3;`, false, ""}, + {`alter database t followers=0;`, false, ""}, + {`alter database t voters=3;`, false, ""}, + {`alter database t learners=3;`, false, ""}, + {`alter database t schedule="even";`, false, ""}, + {`alter database t constraints="ww";`, false, ""}, + {`alter database t leader_constraints="ww";`, false, ""}, + {`alter database t follower_constraints="ww";`, false, ""}, + {`alter database t voter_constraints="ww";`, false, ""}, + {`alter database t learner_constraints="ww";`, false, ""}, + {`alter database t /*T![placement] primary_region="us" */;`, false, ""}, + {`alter database t placement policy="ww";`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `ww`"}, + {`alter database t default placement policy="ww";`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `ww`"}, + {`alter database t PLACEMENT POLICY='DEFAULT';`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + {`alter database t PLACEMENT POLICY=DEFAULT;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + {`alter database t PLACEMENT POLICY = DEFAULT;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + {`alter database t PLACEMENT POLICY SET DEFAULT`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + {"alter database t PLACEMENT POLICY=`DEFAULT`;", true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + {`alter database t /*T![placement] PLACEMENT POLICY='DEFAULT' */;`, true, "ALTER DATABASE `t` PLACEMENT POLICY = `DEFAULT`"}, + // 5. create partition + {`create table m (c int) partition by range (c) (partition p1 values less than (200) primary_region="us");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) regions="us,3");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) followers=3);`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) voters=3);`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) learners=3);`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) schedule="even");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) constraints="ww");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) leader_constraints="ww");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) follower_constraints="ww");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) voter_constraints="ww");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) learner_constraints="ww");`, false, ""}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) placement policy="ww");`, true, "CREATE TABLE `m` (`c` INT) PARTITION BY RANGE (`c`) (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, + {`create table m (c int) partition by range (c) (partition p1 values less than (200) /*T![placement] placement policy="ww" */);`, true, "CREATE TABLE `m` (`c` INT) PARTITION BY RANGE (`c`) (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, + // 6. alter partition + {`alter table m partition t primary_region="us";`, false, ""}, + {`alter table m partition t regions="us,3";`, false, ""}, + {`alter table m partition t followers=3;`, false, ""}, + {`alter table m partition t primary_region="us" followers=3;`, false, ""}, + {`alter table m partition t voters=3;`, false, ""}, + {`alter table m partition t learners=3;`, false, ""}, + {`alter table m partition t schedule="even";`, false, ""}, + {`alter table m partition t constraints="ww";`, false, ""}, + {`alter table m partition t leader_constraints="ww";`, false, ""}, + {`alter table m partition t follower_constraints="ww";`, false, ""}, + {`alter table m partition t voter_constraints="ww";`, false, ""}, + {`alter table m partition t learner_constraints="ww";`, false, ""}, + {`alter table m partition t placement policy="ww";`, true, "ALTER TABLE `m` PARTITION `t` PLACEMENT POLICY = `ww`"}, + {`alter table m partition t /*T![placement] placement policy="ww" */;`, true, "ALTER TABLE `m` PARTITION `t` PLACEMENT POLICY = `ww`"}, + // 7. add partition + {`alter table m add partition (partition p1 values less than (200) primary_region="us");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) regions="us,3");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) followers=3);`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) voters=3);`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) learners=3);`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) schedule="even");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) constraints="ww");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) leader_constraints="ww");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) follower_constraints="ww");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) voter_constraints="ww");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) learner_constraints="ww");`, false, ""}, + {`alter table m add partition (partition p1 values less than (200) placement policy="ww");`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, + {`alter table m add partition (partition p1 values less than (200) /*T![placement] placement policy="ww" */);`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200) PLACEMENT POLICY = `ww`)"}, + {`alter table m add column a int, add partition (partition p1 values less than (200))`, true, "ALTER TABLE `m` ADD COLUMN `a` INT, ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200))"}, + // TODO: Do not allow this order! + {`alter table m add partition (partition p1 values less than (200)), add column a int`, true, "ALTER TABLE `m` ADD PARTITION (PARTITION `p1` VALUES LESS THAN (200)), ADD COLUMN `a` INT"}, + // for check clause + {"create table t (c1 bool, c2 bool, check (c1 in (0, 1)) not enforced, check (c2 in (0, 1)))", true, "CREATE TABLE `t` (`c1` TINYINT(1),`c2` TINYINT(1),CHECK(`c1` IN (0,1)) NOT ENFORCED,CHECK(`c2` IN (0,1)) ENFORCED)"}, + {"CREATE TABLE Customer (SD integer CHECK (SD > 0), First_Name varchar(30));", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) ENFORCED,`First_Name` VARCHAR(30))"}, + {"CREATE TABLE Customer (SD integer CHECK (SD > 0) not enforced, SS varchar(30) check(ss='test') enforced);", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) NOT ENFORCED,`SS` VARCHAR(30) CHECK(`ss`=_UTF8MB4'test') ENFORCED)"}, + {"CREATE TABLE Customer (SD integer CHECK (SD > 0) not null, First_Name varchar(30) comment 'string' not null);", true, "CREATE TABLE `Customer` (`SD` INT CHECK(`SD`>0) ENFORCED NOT NULL,`First_Name` VARCHAR(30) COMMENT 'string' NOT NULL)"}, + {"CREATE TABLE Customer (SD integer comment 'string' CHECK (SD > 0) not null);", true, "CREATE TABLE `Customer` (`SD` INT COMMENT 'string' CHECK(`SD`>0) ENFORCED NOT NULL)"}, + {"CREATE TABLE Customer (SD integer comment 'string' not enforced, First_Name varchar(30));", false, ""}, + {"CREATE TABLE Customer (SD integer not enforced, First_Name varchar(30));", false, ""}, + + {"create database xxx", true, "CREATE DATABASE `xxx`"}, + {"create database if exists xxx", false, ""}, + {"create database if not exists xxx", true, "CREATE DATABASE IF NOT EXISTS `xxx`"}, + + // for create database with encryption + {"create database xxx encryption = 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, + {"create database xxx encryption 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, + {"create database xxx default encryption = 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, + {"create database xxx default encryption 'N'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'N'"}, + {"create database xxx encryption = 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, + {"create database xxx encryption 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, + {"create database xxx default encryption = 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, + {"create database xxx default encryption 'Y'", true, "CREATE DATABASE `xxx` ENCRYPTION = 'Y'"}, + {"create database xxx encryption = N", false, ""}, + + {"create schema xxx", true, "CREATE DATABASE `xxx`"}, + {"create schema if exists xxx", false, ""}, + {"create schema if not exists xxx", true, "CREATE DATABASE IF NOT EXISTS `xxx`"}, + // for drop database/schema/table/view/stats + {"drop database xxx", true, "DROP DATABASE `xxx`"}, + {"drop database if exists xxx", true, "DROP DATABASE IF EXISTS `xxx`"}, + {"drop database if not exists xxx", false, ""}, + {"drop schema xxx", true, "DROP DATABASE `xxx`"}, + {"drop schema if exists xxx", true, "DROP DATABASE IF EXISTS `xxx`"}, + {"drop schema if not exists xxx", false, ""}, + {"drop table", false, ""}, + {"drop table if exists t'xyz", false, ""}, + {"drop table if exists t'", false, ""}, + {"drop table if exists t`", false, ""}, + {`drop table if exists t'`, false, ""}, + {`drop table if exists t"`, false, ""}, + {"drop table xxx", true, "DROP TABLE `xxx`"}, + {"drop table xxx, yyy", true, "DROP TABLE `xxx`, `yyy`"}, + {"drop tables xxx", true, "DROP TABLE `xxx`"}, + {"drop tables xxx, yyy", true, "DROP TABLE `xxx`, `yyy`"}, + {"drop table if exists xxx", true, "DROP TABLE IF EXISTS `xxx`"}, + {"drop table if exists xxx, yyy", true, "DROP TABLE IF EXISTS `xxx`, `yyy`"}, + {"drop table if not exists xxx", false, ""}, + {"drop table xxx restrict", true, "DROP TABLE `xxx`"}, + {"drop table xxx, yyy cascade", true, "DROP TABLE `xxx`, `yyy`"}, + {"drop table if exists xxx restrict", true, "DROP TABLE IF EXISTS `xxx`"}, + {"drop view", false, "DROP VIEW"}, + {"drop view xxx", true, "DROP VIEW `xxx`"}, + {"drop view xxx, yyy", true, "DROP VIEW `xxx`, `yyy`"}, + {"drop view if exists xxx", true, "DROP VIEW IF EXISTS `xxx`"}, + {"drop view if exists xxx, yyy", true, "DROP VIEW IF EXISTS `xxx`, `yyy`"}, + {"drop stats t", true, "DROP STATS `t`"}, + {"drop stats t1, t2, t3", true, "DROP STATS `t1`, `t2`, `t3`"}, + {"drop stats t global", true, "DROP STATS `t` GLOBAL"}, + {"drop stats t partition p0", true, "DROP STATS `t` PARTITION `p0`"}, + {"drop stats t partition p0, p1, p2", true, "DROP STATS `t` PARTITION `p0`,`p1`,`p2`"}, + // for issue 974 + {`CREATE TABLE address ( + id bigint(20) NOT NULL AUTO_INCREMENT, + create_at datetime NOT NULL, + deleted tinyint(1) NOT NULL, + update_at datetime NOT NULL, + version bigint(20) DEFAULT NULL, + address varchar(128) NOT NULL, + address_detail varchar(128) NOT NULL, + cellphone varchar(16) NOT NULL, + latitude double NOT NULL, + longitude double NOT NULL, + name varchar(16) NOT NULL, + sex tinyint(1) NOT NULL, + user_id bigint(20) NOT NULL, + PRIMARY KEY (id), + CONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id), + INDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment '' + ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET UTF8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`),INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, + // for issue 975 + {`CREATE TABLE test_data ( + id bigint(20) NOT NULL AUTO_INCREMENT, + create_at datetime NOT NULL, + deleted tinyint(1) NOT NULL, + update_at datetime NOT NULL, + version bigint(20) DEFAULT NULL, + address varchar(255) NOT NULL, + amount decimal(19,2) DEFAULT NULL, + charge_id varchar(32) DEFAULT NULL, + paid_amount decimal(19,2) DEFAULT NULL, + transaction_no varchar(64) DEFAULT NULL, + wx_mp_app_id varchar(32) DEFAULT NULL, + contacts varchar(50) DEFAULT NULL, + deliver_fee decimal(19,2) DEFAULT NULL, + deliver_info varchar(255) DEFAULT NULL, + deliver_time varchar(255) DEFAULT NULL, + description varchar(255) DEFAULT NULL, + invoice varchar(255) DEFAULT NULL, + order_from int(11) DEFAULT NULL, + order_state int(11) NOT NULL, + packing_fee decimal(19,2) DEFAULT NULL, + payment_time datetime DEFAULT NULL, + payment_type int(11) DEFAULT NULL, + phone varchar(50) NOT NULL, + store_employee_id bigint(20) DEFAULT NULL, + store_id bigint(20) NOT NULL, + user_id bigint(20) NOT NULL, + payment_mode int(11) NOT NULL, + current_latitude double NOT NULL, + current_longitude double NOT NULL, + address_latitude double NOT NULL, + address_longitude double NOT NULL, + PRIMARY KEY (id), + CONSTRAINT food_order_ibfk_1 FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id), + CONSTRAINT food_order_ibfk_2 FOREIGN KEY (store_id) REFERENCES waimaiqa.store (id), + CONSTRAINT food_order_ibfk_3 FOREIGN KEY (store_employee_id) REFERENCES waimaiqa.store_employee (id), + UNIQUE FK_UNIQUE_charge_id USING BTREE (charge_id) comment '', + INDEX FK_eqst2x1xisn3o0wbrlahnnqq8 USING BTREE (store_employee_id) comment '', + INDEX FK_8jcmec4kb03f4dod0uqwm54o9 USING BTREE (store_id) comment '', + INDEX FK_a3t0m9apja9jmrn60uab30pqd USING BTREE (user_id) comment '' + ) ENGINE=InnoDB AUTO_INCREMENT=95 DEFAULT CHARACTER SET utf8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `test_data` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(255) NOT NULL,`amount` DECIMAL(19,2) DEFAULT NULL,`charge_id` VARCHAR(32) DEFAULT NULL,`paid_amount` DECIMAL(19,2) DEFAULT NULL,`transaction_no` VARCHAR(64) DEFAULT NULL,`wx_mp_app_id` VARCHAR(32) DEFAULT NULL,`contacts` VARCHAR(50) DEFAULT NULL,`deliver_fee` DECIMAL(19,2) DEFAULT NULL,`deliver_info` VARCHAR(255) DEFAULT NULL,`deliver_time` VARCHAR(255) DEFAULT NULL,`description` VARCHAR(255) DEFAULT NULL,`invoice` VARCHAR(255) DEFAULT NULL,`order_from` INT(11) DEFAULT NULL,`order_state` INT(11) NOT NULL,`packing_fee` DECIMAL(19,2) DEFAULT NULL,`payment_time` DATETIME DEFAULT NULL,`payment_type` INT(11) DEFAULT NULL,`phone` VARCHAR(50) NOT NULL,`store_employee_id` BIGINT(20) DEFAULT NULL,`store_id` BIGINT(20) NOT NULL,`user_id` BIGINT(20) NOT NULL,`payment_mode` INT(11) NOT NULL,`current_latitude` DOUBLE NOT NULL,`current_longitude` DOUBLE NOT NULL,`address_latitude` DOUBLE NOT NULL,`address_longitude` DOUBLE NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `food_order_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`),CONSTRAINT `food_order_ibfk_2` FOREIGN KEY (`store_id`) REFERENCES `waimaiqa`.`store`(`id`),CONSTRAINT `food_order_ibfk_3` FOREIGN KEY (`store_employee_id`) REFERENCES `waimaiqa`.`store_employee`(`id`),UNIQUE `FK_UNIQUE_charge_id`(`charge_id`) USING BTREE,INDEX `FK_eqst2x1xisn3o0wbrlahnnqq8`(`store_employee_id`) USING BTREE,INDEX `FK_8jcmec4kb03f4dod0uqwm54o9`(`store_id`) USING BTREE,INDEX `FK_a3t0m9apja9jmrn60uab30pqd`(`user_id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 95 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, + {`create table t (c int KEY);`, true, "CREATE TABLE `t` (`c` INT PRIMARY KEY)"}, + {`CREATE TABLE address ( + id bigint(20) NOT NULL AUTO_INCREMENT, + create_at datetime NOT NULL, + deleted tinyint(1) NOT NULL, + update_at datetime NOT NULL, + version bigint(20) DEFAULT NULL, + address varchar(128) NOT NULL, + address_detail varchar(128) NOT NULL, + cellphone varchar(16) NOT NULL, + latitude double NOT NULL, + longitude double NOT NULL, + name varchar(16) NOT NULL, + sex tinyint(1) NOT NULL, + user_id bigint(20) NOT NULL, + PRIMARY KEY (id), + CONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id) ON DELETE CASCADE ON UPDATE NO ACTION, + INDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment '' + ) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET utf8 COLLATE UTF8_GENERAL_CI ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;`, true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION,INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, + {"CREATE TABLE address (\r\nid bigint(20) NOT NULL AUTO_INCREMENT,\r\ncreate_at datetime NOT NULL,\r\ndeleted tinyint(1) NOT NULL,\r\nupdate_at datetime NOT NULL,\r\nversion bigint(20) DEFAULT NULL,\r\naddress varchar(128) NOT NULL,\r\naddress_detail varchar(128) NOT NULL,\r\ncellphone varchar(16) NOT NULL,\r\nlatitude double NOT NULL,\r\nlongitude double NOT NULL,\r\nname varchar(16) NOT NULL,\r\nsex tinyint(1) NOT NULL,\r\nuser_id bigint(20) NOT NULL,\r\nPRIMARY KEY (id),\r\nCONSTRAINT FK_7rod8a71yep5vxasb0ms3osbg FOREIGN KEY (user_id) REFERENCES waimaiqa.user (id) ON DELETE CASCADE ON UPDATE NO ACTION,\r\nINDEX FK_7rod8a71yep5vxasb0ms3osbg (user_id) comment ''\r\n) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ROW_FORMAT=COMPACT COMMENT='' CHECKSUM=0 DELAY_KEY_WRITE=0;", true, "CREATE TABLE `address` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`create_at` DATETIME NOT NULL,`deleted` TINYINT(1) NOT NULL,`update_at` DATETIME NOT NULL,`version` BIGINT(20) DEFAULT NULL,`address` VARCHAR(128) NOT NULL,`address_detail` VARCHAR(128) NOT NULL,`cellphone` VARCHAR(16) NOT NULL,`latitude` DOUBLE NOT NULL,`longitude` DOUBLE NOT NULL,`name` VARCHAR(16) NOT NULL,`sex` TINYINT(1) NOT NULL,`user_id` BIGINT(20) NOT NULL,PRIMARY KEY(`id`),CONSTRAINT `FK_7rod8a71yep5vxasb0ms3osbg` FOREIGN KEY (`user_id`) REFERENCES `waimaiqa`.`user`(`id`) ON DELETE CASCADE ON UPDATE NO ACTION,INDEX `FK_7rod8a71yep5vxasb0ms3osbg`(`user_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 30 DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI ROW_FORMAT = COMPACT COMMENT = '' CHECKSUM = 0 DELAY_KEY_WRITE = 0"}, + // for issue 1802 + {`CREATE TABLE t1 ( + accout_id int(11) DEFAULT '0', + summoner_id int(11) DEFAULT '0', + union_name varbinary(52) NOT NULL, + union_id int(11) DEFAULT '0', + PRIMARY KEY (union_name)) ENGINE=MyISAM DEFAULT CHARSET=binary;`, true, "CREATE TABLE `t1` (`accout_id` INT(11) DEFAULT _UTF8MB4'0',`summoner_id` INT(11) DEFAULT _UTF8MB4'0',`union_name` VARBINARY(52) NOT NULL,`union_id` INT(11) DEFAULT _UTF8MB4'0',PRIMARY KEY(`union_name`)) ENGINE = MyISAM DEFAULT CHARACTER SET = BINARY"}, + // for issue pingcap/parser#310 + {`CREATE TABLE t (a DECIMAL(20,0), b DECIMAL(30), c FLOAT(25,0))`, true, "CREATE TABLE `t` (`a` DECIMAL(20,0),`b` DECIMAL(30),`c` FLOAT(25,0))"}, + // Create table with multiple index options. + {`create table t (c int, index ci (c) USING BTREE COMMENT "123");`, true, "CREATE TABLE `t` (`c` INT,INDEX `ci`(`c`) USING BTREE COMMENT '123')"}, + // for default value + {"CREATE TABLE sbtest (id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, k integer UNSIGNED DEFAULT '0' NOT NULL, c char(120) DEFAULT '' NOT NULL, pad char(60) DEFAULT '' NOT NULL, PRIMARY KEY (id) )", true, "CREATE TABLE `sbtest` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,`k` INT UNSIGNED DEFAULT _UTF8MB4'0' NOT NULL,`c` CHAR(120) DEFAULT _UTF8MB4'' NOT NULL,`pad` CHAR(60) DEFAULT _UTF8MB4'' NOT NULL,PRIMARY KEY(`id`))"}, + {"create table test (create_date TIMESTAMP NOT NULL COMMENT '创建日期 create date' DEFAULT now());", true, "CREATE TABLE `test` (`create_date` TIMESTAMP NOT NULL COMMENT '创建日期 create date' DEFAULT CURRENT_TIMESTAMP())"}, + {"create table ts (t int, v timestamp(3) default CURRENT_TIMESTAMP(3));", true, "CREATE TABLE `ts` (`t` INT,`v` TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3))"}, // TODO: The number yacc in parentheses has not been implemented yet. + // Create table with primary key name. + {"create table if not exists `t` (`id` int not null auto_increment comment '消息ID', primary key `pk_id` (`id`) );", true, "CREATE TABLE IF NOT EXISTS `t` (`id` INT NOT NULL AUTO_INCREMENT COMMENT '消息ID',PRIMARY KEY `pk_id`(`id`))"}, + // Create table with like. + {"create table a like b", true, "CREATE TABLE `a` LIKE `b`"}, + {"create table a (id int REFERENCES a (id) ON delete NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE NO ACTION)"}, + {"create table a (id int REFERENCES a (id) ON update set default )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON UPDATE SET DEFAULT)"}, + {"create table a (id int REFERENCES a (id) ON delete set null on update CASCADE)", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE SET NULL ON UPDATE CASCADE)"}, + {"create table a (id int REFERENCES a (id) ON update set default on delete RESTRICT)", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON DELETE RESTRICT ON UPDATE SET DEFAULT)"}, + {"create table a (id int REFERENCES a (id) MATCH FULL ON delete NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH FULL ON DELETE NO ACTION)"}, + {"create table a (id int REFERENCES a (id) MATCH PARTIAL ON update NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH PARTIAL ON UPDATE NO ACTION)"}, + {"create table a (id int REFERENCES a (id) MATCH SIMPLE ON update NO ACTION )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) MATCH SIMPLE ON UPDATE NO ACTION)"}, + {"create table a (id int REFERENCES a (id) ON update set default )", true, "CREATE TABLE `a` (`id` INT REFERENCES `a`(`id`) ON UPDATE SET DEFAULT)"}, + {"create table a (id int REFERENCES a (id) ON update set default on update CURRENT_TIMESTAMP)", false, ""}, + {"create table a (id int REFERENCES a (id) ON delete set default on update CURRENT_TIMESTAMP)", false, ""}, + {"create table a (like b)", true, "CREATE TABLE `a` LIKE `b`"}, + {"create table if not exists a like b", true, "CREATE TABLE IF NOT EXISTS `a` LIKE `b`"}, + {"create table if not exists a (like b)", true, "CREATE TABLE IF NOT EXISTS `a` LIKE `b`"}, + {"create table if not exists a like (b)", false, ""}, + {"create table a (t int) like b", false, ""}, + {"create table a (t int) like (b)", false, ""}, + // Create table with select statement + {"create table a select * from b", true, "CREATE TABLE `a` AS SELECT * FROM `b`"}, + {"create table a as select * from b", true, "CREATE TABLE `a` AS SELECT * FROM `b`"}, + {"create table a (m int, n datetime) as select * from b", true, "CREATE TABLE `a` (`m` INT,`n` DATETIME) AS SELECT * FROM `b`"}, + {"create table a (unique(n)) as select n from b", true, "CREATE TABLE `a` (UNIQUE(`n`)) AS SELECT `n` FROM `b`"}, + {"create table a ignore as select n from b", true, "CREATE TABLE `a` IGNORE AS SELECT `n` FROM `b`"}, + {"create table a replace as select n from b", true, "CREATE TABLE `a` REPLACE AS SELECT `n` FROM `b`"}, + {"create table a (m int) replace as (select n as m from b union select n+1 as m from c group by 1 limit 2)", true, "CREATE TABLE `a` (`m` INT) REPLACE AS (SELECT `n` AS `m` FROM `b` UNION SELECT `n`+1 AS `m` FROM `c` GROUP BY 1 LIMIT 2)"}, + + // Create table with no option is valid for parser + {"create table a", true, "CREATE TABLE `a`"}, + + {"create table t (a timestamp default now)", false, ""}, + {"create table t (a timestamp default now())", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP())"}, + {"create table t (a timestamp default (((now()))))", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP())"}, + {"create table t (a timestamp default now() on update now)", false, ""}, + {"create table t (a timestamp default now() on update now())", true, "CREATE TABLE `t` (`a` TIMESTAMP DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP())"}, + {"create table t (a timestamp default now() on update (now()))", false, ""}, + {"CREATE TABLE t (c TEXT) default CHARACTER SET utf8, default COLLATE utf8_general_ci;", true, "CREATE TABLE `t` (`c` TEXT) DEFAULT CHARACTER SET = UTF8 DEFAULT COLLATE = UTF8_GENERAL_CI"}, + {"CREATE TABLE t (c TEXT) shard_row_id_bits = 1;", true, "CREATE TABLE `t` (`c` TEXT) SHARD_ROW_ID_BITS = 1"}, + {"CREATE TABLE t (c TEXT) shard_row_id_bits = 1, PRE_SPLIT_REGIONS = 1;", true, "CREATE TABLE `t` (`c` TEXT) SHARD_ROW_ID_BITS = 1 PRE_SPLIT_REGIONS = 1"}, + // Create table with ON UPDATE CURRENT_TIMESTAMP(6), specify fraction part. + {"CREATE TABLE IF NOT EXISTS `general_log` (`event_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),`user_host` mediumtext NOT NULL,`thread_id` bigint(20) unsigned NOT NULL,`server_id` int(10) unsigned NOT NULL,`command_type` varchar(64) NOT NULL,`argument` mediumblob NOT NULL) ENGINE=CSV DEFAULT CHARSET=utf8 COMMENT='General log'", true, "CREATE TABLE IF NOT EXISTS `general_log` (`event_time` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),`user_host` MEDIUMTEXT NOT NULL,`thread_id` BIGINT(20) UNSIGNED NOT NULL,`server_id` INT(10) UNSIGNED NOT NULL,`command_type` VARCHAR(64) NOT NULL,`argument` MEDIUMBLOB NOT NULL) ENGINE = CSV DEFAULT CHARACTER SET = UTF8 COMMENT = 'General log'"}, // TODO: The number yacc in parentheses has not been implemented yet. + // For reference_definition in column_definition. + {"CREATE TABLE followers ( f1 int NOT NULL REFERENCES user_profiles (uid) );", true, "CREATE TABLE `followers` (`f1` INT NOT NULL REFERENCES `user_profiles`(`uid`))"}, + + // For column default expression + {"create table t (a int default rand())", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, + {"create table t (a int default rand(1))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, + {"create table t (a int default (rand()))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, + {"create table t (a int default (rand(1)))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, + {"create table t (a int default (((rand()))))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND())"}, + {"create table t (a int default (((rand(1)))))", true, "CREATE TABLE `t` (`a` INT DEFAULT RAND(1))"}, + {"create table t (d date default current_date())", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, + {"create table t (d date default current_date)", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, + {"create table t (d date default (current_date()))", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, + {"create table t (d date default (curdate()))", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, + {"create table t (d date default curdate())", true, "CREATE TABLE `t` (`d` DATE DEFAULT CURRENT_DATE())"}, + + // For table option `ENCRYPTION` + {"create table t (a int) encryption = 'n';", true, "CREATE TABLE `t` (`a` INT) ENCRYPTION = 'n'"}, + {"create table t (a int) encryption 'n';", true, "CREATE TABLE `t` (`a` INT) ENCRYPTION = 'n'"}, + {"alter table t encryption = 'y';", true, "ALTER TABLE `t` ENCRYPTION = 'y'"}, + {"alter table t encryption 'y';", true, "ALTER TABLE `t` ENCRYPTION = 'y'"}, + + // for alter database/schema/table + {"ALTER DATABASE t CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, + {"ALTER DATABASE CHARACTER SET = 'utf8'", true, "ALTER DATABASE CHARACTER SET = utf8"}, + {"ALTER DATABASE t DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, + {"ALTER SCHEMA t DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, + {"ALTER SCHEMA DEFAULT CHARACTER SET = 'utf8'", true, "ALTER DATABASE CHARACTER SET = utf8"}, + {"ALTER SCHEMA t DEFAULT CHARSET = 'UTF8'", true, "ALTER DATABASE `t` CHARACTER SET = utf8"}, + + {"ALTER DATABASE t COLLATE = binary", true, "ALTER DATABASE `t` COLLATE = binary"}, + {"ALTER DATABASE t CHARSET=binary COLLATE = binary", true, "ALTER DATABASE `t` CHARACTER SET = binary COLLATE = binary"}, + + {"ALTER DATABASE t COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, + {"ALTER DATABASE COLLATE = 'utf8_bin'", true, "ALTER DATABASE COLLATE = utf8_bin"}, + {"ALTER DATABASE t DEFAULT COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, + {"ALTER SCHEMA t DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE `t` COLLATE = utf8_bin"}, + {"ALTER SCHEMA DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE COLLATE = utf8_bin"}, + {"ALTER SCHEMA `` DEFAULT COLLATE = 'UTF8_BiN'", true, "ALTER DATABASE `` COLLATE = utf8_bin"}, + + {"ALTER DATABASE t CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", true, "ALTER DATABASE `t` CHARACTER SET = utf8mb4 COLLATE = utf8_bin"}, + { + "ALTER DATABASE t DEFAULT CHARSET = 'utf8mb4' DEFAULT COLLATE = 'utf8mb4_general_ci' CHARACTER SET = 'utf8' COLLATE = 'utf8mb4_bin'", + true, + "ALTER DATABASE `t` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci CHARACTER SET = utf8 COLLATE = utf8mb4_bin", + }, + {"ALTER DATABASE DEFAULT CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", true, "ALTER DATABASE CHARACTER SET = utf8mb4 COLLATE = utf8_bin"}, + { + "ALTER DATABASE DEFAULT CHARSET = 'utf8mb4' DEFAULT COLLATE = 'utf8mb4_general_ci' CHARACTER SET = 'utf8' COLLATE = 'utf8mb4_bin'", + true, + "ALTER DATABASE CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci CHARACTER SET = utf8 COLLATE = utf8mb4_bin", + }, + + {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED)", true, "ALTER TABLE `t` ADD COLUMN (`a` SMALLINT UNSIGNED)"}, + {"ALTER TABLE t.* ADD COLUMN (a SMALLINT UNSIGNED)", false, ""}, + {"ALTER TABLE t ADD COLUMN IF NOT EXISTS (a SMALLINT UNSIGNED)", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS (`a` SMALLINT UNSIGNED)"}, + {"ALTER TABLE ADD COLUMN (a SMALLINT UNSIGNED)", false, ""}, + {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED, b varchar(255))", true, "ALTER TABLE `t` ADD COLUMN (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, + {"ALTER TABLE t ADD COLUMN IF NOT EXISTS (a SMALLINT UNSIGNED, b varchar(255))", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS (`a` SMALLINT UNSIGNED, `b` VARCHAR(255))"}, + {"ALTER TABLE t ADD COLUMN (a SMALLINT UNSIGNED FIRST)", false, ""}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED FIRST"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED AFTER b", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED AFTER `b`"}, + {"ALTER TABLE t ADD COLUMN IF NOT EXISTS a SMALLINT UNSIGNED AFTER b", true, "ALTER TABLE `t` ADD COLUMN IF NOT EXISTS `a` SMALLINT UNSIGNED AFTER `b`"}, + {"ALTER TABLE employees ADD PARTITION", true, "ALTER TABLE `employees` ADD PARTITION"}, + {"ALTER TABLE employees ADD PARTITION ( PARTITION P1 VALUES LESS THAN (2010))", true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010))"}, + {"ALTER TABLE employees ADD PARTITION ( PARTITION P2 VALUES LESS THAN MAXVALUE)", true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, + {"ALTER TABLE employees ADD PARTITION IF NOT EXISTS ( PARTITION P2 VALUES LESS THAN MAXVALUE)", true, "ALTER TABLE `employees` ADD PARTITION IF NOT EXISTS (PARTITION `P2` VALUES LESS THAN (MAXVALUE))"}, + {"ALTER TABLE employees ADD PARTITION IF NOT EXISTS PARTITIONS 5", true, "ALTER TABLE `employees` ADD PARTITION IF NOT EXISTS PARTITIONS 5"}, + {`ALTER TABLE employees ADD PARTITION ( + PARTITION P1 VALUES LESS THAN (2010), + PARTITION P2 VALUES LESS THAN (2015), + PARTITION P3 VALUES LESS THAN MAXVALUE)`, true, "ALTER TABLE `employees` ADD PARTITION (PARTITION `P1` VALUES LESS THAN (2010), PARTITION `P2` VALUES LESS THAN (2015), PARTITION `P3` VALUES LESS THAN (MAXVALUE))"}, + {"alter table t add partition (partition x values in ((3, 4), (5, 6)))", true, "ALTER TABLE `t` ADD PARTITION (PARTITION `x` VALUES IN ((3, 4), (5, 6)))"}, + {"ALTER TABLE employees ADD PARTITION NO_WRITE_TO_BINLOG", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG"}, + {"ALTER TABLE employees ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10"}, + // LOCAL is alias to NO_WRITE_TO_BINLOG + {"ALTER TABLE employees ADD PARTITION LOCAL", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG"}, + {"ALTER TABLE employees ADD PARTITION LOCAL PARTITIONS 10", true, "ALTER TABLE `employees` ADD PARTITION NO_WRITE_TO_BINLOG PARTITIONS 10"}, + + // For rebuild table partition statement. + {"ALTER TABLE t_n REBUILD PARTITION ALL", true, "ALTER TABLE `t_n` REBUILD PARTITION ALL"}, + {"ALTER TABLE d_n.t_n REBUILD PARTITION LOCAL ALL", true, "ALTER TABLE `d_n`.`t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG ALL"}, + {"ALTER TABLE t_n REBUILD PARTITION LOCAL ident", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `ident`"}, + {"ALTER TABLE t_n REBUILD PARTITION NO_WRITE_TO_BINLOG ident , ident", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `ident`,`ident`"}, + // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), + // and the remains should re recognized as identifier, used as partition name here. + {"ALTER TABLE t_n REBUILD PARTITION LOCAL", false, ""}, + {"ALTER TABLE t_n REBUILD PARTITION LOCAL local", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `local`"}, + {"ALTER TABLE t_n REBUILD PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` REBUILD PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, + + // For drop table partition statement. + {"alter table t drop partition p1;", true, "ALTER TABLE `t` DROP PARTITION `p1`"}, + {"alter table t drop partition p2;", true, "ALTER TABLE `t` DROP PARTITION `p2`"}, + {"alter table t drop partition if exists p2;", true, "ALTER TABLE `t` DROP PARTITION IF EXISTS `p2`"}, + {"alter table t drop partition p1, p2;", true, "ALTER TABLE `t` DROP PARTITION `p1`,`p2`"}, + {"alter table t drop partition if exists p1, p2;", true, "ALTER TABLE `t` DROP PARTITION IF EXISTS `p1`,`p2`"}, + // For check table partition statement + {"alter table t check partition all;", true, "ALTER TABLE `t` CHECK PARTITION ALL"}, + {"alter table t check partition p;", true, "ALTER TABLE `t` CHECK PARTITION `p`"}, + {"alter table t check partition p1, p2;", true, "ALTER TABLE `t` CHECK PARTITION `p1`,`p2`"}, + {"alter table employees add partition partitions 1;", true, "ALTER TABLE `employees` ADD PARTITION PARTITIONS 1"}, + {"alter table employees add partition partitions 2;", true, "ALTER TABLE `employees` ADD PARTITION PARTITIONS 2"}, + {"alter table clients coalesce partition 3;", true, "ALTER TABLE `clients` COALESCE PARTITION 3"}, + {"alter table clients coalesce partition 4;", true, "ALTER TABLE `clients` COALESCE PARTITION 4"}, + {"alter table clients coalesce partition no_write_to_binlog 4;", true, "ALTER TABLE `clients` COALESCE PARTITION NO_WRITE_TO_BINLOG 4"}, + {"alter table clients coalesce partition local 4;", true, "ALTER TABLE `clients` COALESCE PARTITION NO_WRITE_TO_BINLOG 4"}, + {"ALTER TABLE t DISABLE KEYS", true, "ALTER TABLE `t` DISABLE KEYS"}, + {"ALTER TABLE t ENABLE KEYS", true, "ALTER TABLE `t` ENABLE KEYS"}, + {"ALTER TABLE t MODIFY COLUMN a varchar(255)", true, "ALTER TABLE `t` MODIFY COLUMN `a` VARCHAR(255)"}, + {"ALTER TABLE t MODIFY COLUMN IF EXISTS a varchar(255)", true, "ALTER TABLE `t` MODIFY COLUMN IF EXISTS `a` VARCHAR(255)"}, + {"ALTER TABLE t CHANGE COLUMN a b varchar(255)", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255)"}, + {"ALTER TABLE t CHANGE COLUMN IF EXISTS a b varchar(255)", true, "ALTER TABLE `t` CHANGE COLUMN IF EXISTS `a` `b` VARCHAR(255)"}, + {"ALTER TABLE t CHANGE COLUMN a b varchar(255) CHARACTER SET UTF8 BINARY", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255) BINARY CHARACTER SET UTF8"}, + {"ALTER TABLE t CHANGE COLUMN a b varchar(255) FIRST", true, "ALTER TABLE `t` CHANGE COLUMN `a` `b` VARCHAR(255) FIRST"}, + + // For alter table rename statement. + {"ALTER TABLE db.t RENAME to db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, + {"ALTER TABLE db.t RENAME db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, + {"ALTER TABLE db.t RENAME = db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, + {"ALTER TABLE db.t RENAME as db1.t1", true, "ALTER TABLE `db`.`t` RENAME AS `db1`.`t1`"}, + {"ALTER TABLE t RENAME to t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, + {"ALTER TABLE t RENAME t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, + {"ALTER TABLE t RENAME = t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, + {"ALTER TABLE t RENAME as t1", true, "ALTER TABLE `t` RENAME AS `t1`"}, + + // For #499, alter table order by + {"ALTER TABLE t_n ORDER BY ident", true, "ALTER TABLE `t_n` ORDER BY `ident`"}, + {"ALTER TABLE t_n ORDER BY ident ASC", true, "ALTER TABLE `t_n` ORDER BY `ident`"}, + {"ALTER TABLE t_n ORDER BY ident DESC", true, "ALTER TABLE `t_n` ORDER BY `ident` DESC"}, + {"ALTER TABLE t_n ORDER BY ident1, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, + {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, + {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`"}, + {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2` DESC"}, + {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2`"}, + {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2`"}, + {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2` DESC"}, + {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, + {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, + {"ALTER TABLE t_n ORDER BY ident1, ident2, ident3 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3` DESC"}, + {"ALTER TABLE t_n ORDER BY ident1 ASC, ident2 ASC, ident3 ASC", true, "ALTER TABLE `t_n` ORDER BY `ident1`,`ident2`,`ident3`"}, + {"ALTER TABLE t_n ORDER BY ident1 DESC, ident2 DESC, ident3 DESC", true, "ALTER TABLE `t_n` ORDER BY `ident1` DESC,`ident2` DESC,`ident3` DESC"}, + + // For alter table rename column statement. + {"ALTER TABLE t RENAME COLUMN a TO b", true, "ALTER TABLE `t` RENAME COLUMN `a` TO `b`"}, + {"ALTER TABLE t RENAME COLUMN t.a TO t.b", false, ""}, + {"ALTER TABLE t RENAME COLUMN a TO t.b", false, ""}, + {"ALTER TABLE t RENAME COLUMN t.a TO b", false, ""}, + + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT 1", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, + {"ALTER TABLE t ALTER a SET DEFAULT 1", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT CURRENT_TIMESTAMP", false, ""}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT NOW()", false, ""}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT 1+1", false, ""}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (CURRENT_TIMESTAMP())", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (CURRENT_TIMESTAMP())"}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (NOW())", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (NOW())"}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (1+1)", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT (1+1)"}, + {"ALTER TABLE t ALTER COLUMN a SET DEFAULT (1)", true, "ALTER TABLE `t` ALTER COLUMN `a` SET DEFAULT 1"}, + {"ALTER TABLE t ALTER COLUMN a DROP DEFAULT", true, "ALTER TABLE `t` ALTER COLUMN `a` DROP DEFAULT"}, + {"ALTER TABLE t ALTER a DROP DEFAULT", true, "ALTER TABLE `t` ALTER COLUMN `a` DROP DEFAULT"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=none", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=default", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=shared", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock=exclusive", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock none", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock default", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock shared", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, lock exclusive", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=NONE", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = NONE"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=DEFAULT", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = DEFAULT"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=SHARED", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = SHARED"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, LOCK=EXCLUSIVE", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, LOCK = EXCLUSIVE"}, + {"ALTER TABLE t ADD FULLTEXT KEY `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, + {"ALTER TABLE t ADD FULLTEXT `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, + {"ALTER TABLE t ADD FULLTEXT INDEX `FullText` (`name` ASC)", true, "ALTER TABLE `t` ADD FULLTEXT `FullText`(`name`)"}, + {"ALTER TABLE t ADD INDEX (a) USING BTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING BTREE COMMENT 'a'"}, + {"ALTER TABLE t ADD INDEX IF NOT EXISTS (a) USING BTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX IF NOT EXISTS(`a`) USING BTREE COMMENT 'a'"}, + {"ALTER TABLE t ADD INDEX (a) USING RTREE COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING RTREE COMMENT 'a'"}, + {"ALTER TABLE t ADD KEY (a) USING HASH COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX(`a`) USING HASH COMMENT 'a'"}, + {"ALTER TABLE t ADD KEY IF NOT EXISTS (a) USING HASH COMMENT 'a'", true, "ALTER TABLE `t` ADD INDEX IF NOT EXISTS(`a`) USING HASH COMMENT 'a'"}, + {"ALTER TABLE t ADD PRIMARY KEY ident USING RTREE ( a DESC , b )", true, "ALTER TABLE `t` ADD PRIMARY KEY `ident`(`a` DESC, `b`) USING RTREE"}, + {"ALTER TABLE t ADD KEY USING RTREE ( a ) ", true, "ALTER TABLE `t` ADD INDEX(`a`) USING RTREE"}, + {"ALTER TABLE t ADD KEY USING RTREE ( ident ASC , ident ( 123 ) )", true, "ALTER TABLE `t` ADD INDEX(`ident`, `ident`(123)) USING RTREE"}, + {"ALTER TABLE t ADD PRIMARY KEY (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`) COMMENT 'a'"}, + {"ALTER TABLE t ADD UNIQUE (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, + {"ALTER TABLE t ADD UNIQUE KEY (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, + {"ALTER TABLE t ADD UNIQUE INDEX (a) COMMENT 'a'", true, "ALTER TABLE `t` ADD UNIQUE(`a`) COMMENT 'a'"}, + {"ALTER TABLE t ADD CONSTRAINT fk_t2_id FOREIGN KEY (t2_id) REFERENCES t(id)", true, "ALTER TABLE `t` ADD CONSTRAINT `fk_t2_id` FOREIGN KEY (`t2_id`) REFERENCES `t`(`id`)"}, + {"ALTER TABLE t ADD CONSTRAINT fk_t2_id FOREIGN KEY IF NOT EXISTS (t2_id) REFERENCES t(id)", true, "ALTER TABLE `t` ADD CONSTRAINT `fk_t2_id` FOREIGN KEY IF NOT EXISTS (`t2_id`) REFERENCES `t`(`id`)"}, + {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1) NOT ENFORCED, ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) NOT ENFORCED, ADD UNIQUE(`a`)"}, + {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1) ENFORCED, ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) ENFORCED, ADD UNIQUE(`a`)"}, + {"ALTER TABLE t ADD CONSTRAINT c_1 CHECK (1+1), ADD UNIQUE (a)", true, "ALTER TABLE `t` ADD CONSTRAINT `c_1` CHECK(1+1) ENFORCED, ADD UNIQUE(`a`)"}, + {"ALTER TABLE t ENGINE ''", true, "ALTER TABLE `t` ENGINE = ''"}, + {"ALTER TABLE t ENGINE = ''", true, "ALTER TABLE `t` ENGINE = ''"}, + {"ALTER TABLE t ENGINE = 'innodb'", true, "ALTER TABLE `t` ENGINE = innodb"}, + {"ALTER TABLE t ENGINE = innodb", true, "ALTER TABLE `t` ENGINE = innodb"}, + {"ALTER TABLE `db`.`t` ENGINE = ``", true, "ALTER TABLE `db`.`t` ENGINE = ''"}, + {"ALTER TABLE t INSERT_METHOD = FIRST", true, "ALTER TABLE `t` INSERT_METHOD = FIRST"}, + {"ALTER TABLE t INSERT_METHOD LAST", true, "ALTER TABLE `t` INSERT_METHOD = LAST"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT UNSIGNED, ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT UNSIGNED, ADD COLUMN `a` SMALLINT"}, + {"ALTER TABLE t ADD COLUMN a SMALLINT, ENGINE = '', default COLLATE = UTF8_GENERAL_CI", true, "ALTER TABLE `t` ADD COLUMN `a` SMALLINT, ENGINE = '', DEFAULT COLLATE = UTF8_GENERAL_CI"}, + {"ALTER TABLE t ENGINE = '', COMMENT='', default COLLATE = UTF8_GENERAL_CI", true, "ALTER TABLE `t` ENGINE = '', COMMENT = '', DEFAULT COLLATE = UTF8_GENERAL_CI"}, + {"ALTER TABLE t ENGINE = '', ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` ENGINE = '', ADD COLUMN `a` SMALLINT"}, + {"ALTER TABLE t default COLLATE = UTF8_GENERAL_CI, ENGINE = '', ADD COLUMN a SMALLINT", true, "ALTER TABLE `t` DEFAULT COLLATE = UTF8_GENERAL_CI, ENGINE = '', ADD COLUMN `a` SMALLINT"}, + {"ALTER TABLE t shard_row_id_bits = 1", true, "ALTER TABLE `t` SHARD_ROW_ID_BITS = 1"}, + {"ALTER TABLE t AUTO_INCREMENT 3", true, "ALTER TABLE `t` AUTO_INCREMENT = 3"}, + {"ALTER TABLE t AUTO_INCREMENT = 3", true, "ALTER TABLE `t` AUTO_INCREMENT = 3"}, + {"ALTER TABLE t FORCE AUTO_INCREMENT 3", true, "ALTER TABLE `t` FORCE AUTO_INCREMENT = 3"}, + {"ALTER TABLE t FORCE AUTO_INCREMENT = 3", true, "ALTER TABLE `t` FORCE AUTO_INCREMENT = 3"}, + {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = DEFAULT;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = DEFAULT"}, + {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = INPLACE;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = INPLACE"}, + {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` mediumtext CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL , ALGORITHM = COPY;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = COPY"}, + {"ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE UTF8MB4_UNICODE_CI NOT NULL, ALGORITHM = INSTANT;", true, "ALTER TABLE `hello-world@dev`.`User` ADD COLUMN `name` MEDIUMTEXT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci NOT NULL, ALGORITHM = INSTANT"}, + {"ALTER TABLE t CONVERT TO CHARACTER SET UTF8;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8"}, + {"ALTER TABLE t CONVERT TO CHARSET UTF8;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8"}, + {"ALTER TABLE t CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, + {"ALTER TABLE t CONVERT TO CHARSET UTF8 COLLATE UTF8_BIN;", true, "ALTER TABLE `t` CONVERT TO CHARACTER SET UTF8 COLLATE UTF8_BIN"}, + + // alter table convert to character set default, issue #498 + {"alter table d_n.t_n convert to character set default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, + {"alter table d_n.t_n convert to charset default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, + {"alter table d_n.t_n convert to char set default", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT"}, + {"alter table d_n.t_n convert to character set default collate utf8mb4_0900_ai_ci", true, "ALTER TABLE `d_n`.`t_n` CONVERT TO CHARACTER SET DEFAULT COLLATE UTF8MB4_0900_AI_CI"}, + + {"ALTER TABLE t FORCE", true, "ALTER TABLE `t` FORCE /* AlterTableForce is not supported */ "}, + {"ALTER TABLE t DROP INDEX;", false, "ALTER TABLE `t` DROP INDEX"}, + {"ALTER TABLE t DROP INDEX a", true, "ALTER TABLE `t` DROP INDEX `a`"}, + {"ALTER TABLE t DROP INDEX IF EXISTS a", true, "ALTER TABLE `t` DROP INDEX IF EXISTS `a`"}, + + // For alter table alter index statement + {"ALTER TABLE t ALTER INDEX a INVISIBLE", true, "ALTER TABLE `t` ALTER INDEX `a` INVISIBLE"}, + {"ALTER TABLE t ALTER INDEX a VISIBLE", true, "ALTER TABLE `t` ALTER INDEX `a` VISIBLE"}, + + {"ALTER TABLE t DROP FOREIGN KEY a", true, "ALTER TABLE `t` DROP FOREIGN KEY `a`"}, + {"ALTER TABLE t DROP FOREIGN KEY IF EXISTS a", true, "ALTER TABLE `t` DROP FOREIGN KEY IF EXISTS `a`"}, + {"ALTER TABLE t DROP COLUMN a CASCADE", true, "ALTER TABLE `t` DROP COLUMN `a`"}, + {"ALTER TABLE t DROP COLUMN IF EXISTS a CASCADE", true, "ALTER TABLE `t` DROP COLUMN IF EXISTS `a`"}, + {`ALTER TABLE testTableCompression COMPRESSION="LZ4";`, true, "ALTER TABLE `testTableCompression` COMPRESSION = 'LZ4'"}, + {`ALTER TABLE t1 COMPRESSION="zlib";`, true, "ALTER TABLE `t1` COMPRESSION = 'zlib'"}, + {"ALTER TABLE t1", true, "ALTER TABLE `t1`"}, + {"ALTER TABLE t1 ,", false, ""}, + + // For #6405 + {"ALTER TABLE t RENAME KEY a TO b;", true, "ALTER TABLE `t` RENAME INDEX `a` TO `b`"}, + {"ALTER TABLE t RENAME INDEX a TO b;", true, "ALTER TABLE `t` RENAME INDEX `a` TO `b`"}, + + // For #497, support `ALTER TABLE ALTER CHECK` and `ALTER TABLE DROP CHECK` syntax + {"ALTER TABLE d_n.t_n DROP CHECK ident;", true, "ALTER TABLE `d_n`.`t_n` DROP CHECK `ident`"}, + {"ALTER TABLE t_n LOCK = DEFAULT , DROP CHECK ident;", true, "ALTER TABLE `t_n` LOCK = DEFAULT, DROP CHECK `ident`"}, + {"ALTER TABLE t_n ALTER CHECK ident ENFORCED;", true, "ALTER TABLE `t_n` ALTER CHECK `ident` ENFORCED"}, + {"ALTER TABLE t_n ALTER CHECK ident NOT ENFORCED;", true, "ALTER TABLE `t_n` ALTER CHECK `ident` NOT ENFORCED"}, + {"ALTER TABLE t_n DROP CONSTRAINT ident", true, "ALTER TABLE `t_n` DROP CHECK `ident`"}, + {"ALTER TABLE t_n DROP CHECK ident", true, "ALTER TABLE `t_n` DROP CHECK `ident`"}, + {"ALTER TABLE t_n ALTER CONSTRAINT ident", false, ""}, + {"ALTER TABLE t_n ALTER CONSTRAINT ident enforced", true, "ALTER TABLE `t_n` ALTER CHECK `ident` ENFORCED"}, + {"ALTER TABLE t_n ALTER CHECK ident not enforced", true, "ALTER TABLE `t_n` ALTER CHECK `ident` NOT ENFORCED"}, + + {"alter table t analyze partition a", true, "ANALYZE TABLE `t` PARTITION `a`"}, + {"alter table t analyze partition a with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` WITH 4 BUCKETS"}, + {"alter table t analyze partition a index b", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b`"}, + {"alter table t analyze partition a index b with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b` WITH 4 BUCKETS"}, + + {"alter table t partition by hash(a)", true, "ALTER TABLE `t` PARTITION BY HASH (`a`) PARTITIONS 1"}, + {"alter table t add column a int partition by hash(a)", true, "ALTER TABLE `t` ADD COLUMN `a` INT PARTITION BY HASH (`a`) PARTITIONS 1"}, + {"alter table t partition by range(a)", false, ""}, + {"alter table t partition by range(a) (partition x values less than (75))", true, "ALTER TABLE `t` PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (75))"}, + {"alter table t add column a int, partition by range(a) (partition x values less than (75))", false, ""}, + {"alter table t comment 'cmt' partition by hash(a)", true, "ALTER TABLE `t` COMMENT = 'cmt' PARTITION BY HASH (`a`) PARTITIONS 1"}, + {"alter table t enable keys, comment = 'cmt' partition by hash(a)", true, "ALTER TABLE `t` ENABLE KEYS, COMMENT = 'cmt' PARTITION BY HASH (`a`) PARTITIONS 1"}, + {"alter table t enable keys, comment = 'cmt', partition by hash(a)", false, ""}, + {"alter table t partition by hash(a) enable keys", false, ""}, + {"alter table t partition by hash(a), enable keys", false, ""}, + + // Test keyword `FIELDS` + {"alter table t partition by range FIELDS(a) (partition x values less than maxvalue)", true, "ALTER TABLE `t` PARTITION BY RANGE COLUMNS (`a`) (PARTITION `x` VALUES LESS THAN (MAXVALUE))"}, + {"alter table t partition by list FIELDS(a) (PARTITION p0 VALUES IN (5, 10, 15))", true, "ALTER TABLE `t` PARTITION BY LIST COLUMNS (`a`) (PARTITION `p0` VALUES IN (5, 10, 15))"}, + {"alter table t partition by range FIELDS(a,b,c) (partition p1 values less than (1,1,1));", true, "ALTER TABLE `t` PARTITION BY RANGE COLUMNS (`a`,`b`,`c`) (PARTITION `p1` VALUES LESS THAN (1, 1, 1))"}, + {"alter table t partition by list FIELDS(a,b,c) (PARTITION p0 VALUES IN ((5, 10, 15)))", true, "ALTER TABLE `t` PARTITION BY LIST COLUMNS (`a`,`b`,`c`) (PARTITION `p0` VALUES IN ((5, 10, 15)))"}, + + {"alter table t with validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITH VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, + {"alter table t without validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITHOUT VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, + {"alter table t without validation, with validation, add column b int as (a + 1)", true, "ALTER TABLE `t` WITHOUT VALIDATION, WITH VALIDATION, ADD COLUMN `b` INT GENERATED ALWAYS AS(`a`+1) VIRTUAL"}, + {"alter table t with validation, modify column b int as (a + 2) ", true, "ALTER TABLE `t` WITH VALIDATION, MODIFY COLUMN `b` INT GENERATED ALWAYS AS(`a`+2) VIRTUAL"}, + {"alter table t with validation, change column b c int as (a + 2)", true, "ALTER TABLE `t` WITH VALIDATION, CHANGE COLUMN `b` `c` INT GENERATED ALWAYS AS(`a`+2) VIRTUAL"}, + + {"ALTER TABLE d_n.t_n ADD PARTITION NO_WRITE_TO_BINLOG", true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION NO_WRITE_TO_BINLOG"}, + {"ALTER TABLE d_n.t_n ADD PARTITION LOCAL", true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION NO_WRITE_TO_BINLOG"}, + + {"alter table t with validation, exchange partition p with table nt without validation;", true, "ALTER TABLE `t` WITH VALIDATION, EXCHANGE PARTITION `p` WITH TABLE `nt` WITHOUT VALIDATION"}, + {"alter table t exchange partition p with table nt with validation;", true, "ALTER TABLE `t` EXCHANGE PARTITION `p` WITH TABLE `nt`"}, + + // For reorganize partition statement + {"alter table t reorganize partition;", true, "ALTER TABLE `t` REORGANIZE PARTITION"}, + {"alter table t reorganize partition local;", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG"}, + {"alter table t reorganize partition no_write_to_binlog;", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG"}, + {"ALTER TABLE members REORGANIZE PARTITION n0 INTO (PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION `n0` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, + {"ALTER TABLE members REORGANIZE PARTITION LOCAL n0 INTO (PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION NO_WRITE_TO_BINLOG `n0` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, + {"ALTER TABLE members REORGANIZE PARTITION p1,p2,p3 INTO ( PARTITION s0 VALUES LESS THAN (1960), PARTITION s1 VALUES LESS THAN (1970));", true, "ALTER TABLE `members` REORGANIZE PARTITION `p1`,`p2`,`p3` INTO (PARTITION `s0` VALUES LESS THAN (1960), PARTITION `s1` VALUES LESS THAN (1970))"}, + {"alter table t reorganize partition remove partition;", false, ""}, + {"alter table t reorganize partition no_write_to_binlog remove into (partition p0 VALUES LESS THAN (1991));", true, "ALTER TABLE `t` REORGANIZE PARTITION NO_WRITE_TO_BINLOG `remove` INTO (PARTITION `p0` VALUES LESS THAN (1991))"}, + + // alter attributes + {"ALTER TABLE t ATTRIBUTES='str'", true, "ALTER TABLE `t` ATTRIBUTES='str'"}, + {"ALTER TABLE t ATTRIBUTES='str1,str2'", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t ATTRIBUTES=\"str1,str2\"", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t ATTRIBUTES 'str1,str2'", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t ATTRIBUTES \"str1,str2\"", true, "ALTER TABLE `t` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t ATTRIBUTES=DEFAULT", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t ATTRIBUTES=default", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t ATTRIBUTES=DeFaUlT", true, "ALTER TABLE `t` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t ATTRIBUTES", false, ""}, + {"ALTER TABLE t PARTITION p ATTRIBUTES='str'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str'"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES='str1,str2'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES=\"str1,str2\"", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES 'str1,str2'", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES \"str1,str2\"", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES='str1,str2'"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES=DEFAULT", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES=default", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES=DeFaUlT", true, "ALTER TABLE `t` PARTITION `p` ATTRIBUTES=DEFAULT"}, + {"ALTER TABLE t PARTITION p ATTRIBUTES", false, ""}, + // For https://github.com/pingcap/tidb/issues/26778 + {"CREATE TABLE t1 (attributes int);", true, "CREATE TABLE `t1` (`attributes` INT)"}, + + // For create index statement + {"CREATE INDEX idx ON t (a)", true, "CREATE INDEX `idx` ON `t` (`a`)"}, + {"CREATE INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, + {"CREATE UNIQUE INDEX idx ON t (a)", true, "CREATE UNIQUE INDEX `idx` ON `t` (`a`)"}, + {"CREATE UNIQUE INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE UNIQUE INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, + {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE BTREE", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, + {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE HASH", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING HASH"}, + {"CREATE UNIQUE INDEX ident ON d_n.t_n ( ident , ident ASC ) TYPE RTREE", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING RTREE"}, + {"CREATE UNIQUE INDEX ident TYPE BTREE ON d_n.t_n ( ident , ident ASC )", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, + {"CREATE UNIQUE INDEX ident USING BTREE ON d_n.t_n ( ident , ident ASC )", true, "CREATE UNIQUE INDEX `ident` ON `d_n`.`t_n` (`ident`, `ident`) USING BTREE"}, + {"CREATE SPATIAL INDEX idx ON t (a)", true, "CREATE SPATIAL INDEX `idx` ON `t` (`a`)"}, + {"CREATE SPATIAL INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE SPATIAL INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, + {"CREATE FULLTEXT INDEX idx ON t (a)", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`)"}, + {"CREATE FULLTEXT INDEX IF NOT EXISTS idx ON t (a)", true, "CREATE FULLTEXT INDEX IF NOT EXISTS `idx` ON `t` (`a`)"}, + {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident`"}, + {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident comment 'string'", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, + {"CREATE FULLTEXT INDEX idx ON t (a) comment 'string' with parser ident", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, + {"CREATE FULLTEXT INDEX idx ON t (a) WITH PARSER ident comment 'string' lock default", true, "CREATE FULLTEXT INDEX `idx` ON `t` (`a`) WITH PARSER `ident` COMMENT 'string'"}, + {"CREATE INDEX idx ON t (a) USING HASH", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH"}, + {"CREATE INDEX idx ON t (a) COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) COMMENT 'foo'"}, + {"CREATE INDEX idx ON t (a) USING HASH COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH COMMENT 'foo'"}, + {"CREATE INDEX idx ON t (a) LOCK=NONE", true, "CREATE INDEX `idx` ON `t` (`a`) LOCK = NONE"}, + {"CREATE INDEX idx USING BTREE ON t (a) USING HASH COMMENT 'foo'", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH COMMENT 'foo'"}, + {"CREATE INDEX idx USING BTREE ON t (a)", true, "CREATE INDEX `idx` ON `t` (`a`) USING BTREE"}, + {"CREATE INDEX idx ON t ( a ) VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) VISIBLE"}, + {"CREATE INDEX idx ON t ( a ) INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) INVISIBLE"}, + {"CREATE INDEX idx ON t ( a ) INVISIBLE VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) VISIBLE"}, + {"CREATE INDEX idx ON t ( a ) VISIBLE INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) INVISIBLE"}, + {"CREATE INDEX idx ON t ( a ) USING HASH VISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH VISIBLE"}, + {"CREATE INDEX idx ON t ( a ) USING HASH INVISIBLE", true, "CREATE INDEX `idx` ON `t` (`a`) USING HASH INVISIBLE"}, + + // For create index with algorithm + {"CREATE INDEX idx ON t ( a ) ALGORITHM = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM = INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM = COPY", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = COPY"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM COPY", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = COPY"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM = DEFAULT LOCK = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, + {"CREATE INDEX idx ON t ( a ) LOCK = DEFAULT ALGORITHM = DEFAULT", true, "CREATE INDEX `idx` ON `t` (`a`)"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM = INPLACE LOCK = EXCLUSIVE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, + {"CREATE INDEX idx ON t ( a ) LOCK = EXCLUSIVE ALGORITHM = INPLACE", true, "CREATE INDEX `idx` ON `t` (`a`) ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM = ident", false, ""}, + {"CREATE INDEX idx ON t ( a ) ALGORITHM ident", false, ""}, + + // For dorp index statement + {"drop index a on t", true, "DROP INDEX `a` ON `t`"}, + {"drop index a on db.t", true, "DROP INDEX `a` ON `db`.`t`"}, + {"drop index a on db.`tb-ttb`", true, "DROP INDEX `a` ON `db`.`tb-ttb`"}, + {"drop index if exists a on t", true, "DROP INDEX IF EXISTS `a` ON `t`"}, + {"drop index if exists a on db.t", true, "DROP INDEX IF EXISTS `a` ON `db`.`t`"}, + {"drop index if exists a on db.`tb-ttb`", true, "DROP INDEX IF EXISTS `a` ON `db`.`tb-ttb`"}, + {"drop index idx on t algorithm = default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t algorithm default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t algorithm = inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE"}, + {"drop index idx on t algorithm inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE"}, + {"drop index idx on t lock = default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t lock default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t lock = shared", true, "DROP INDEX `idx` ON `t` LOCK = SHARED"}, + {"drop index idx on t lock shared", true, "DROP INDEX `idx` ON `t` LOCK = SHARED"}, + {"drop index idx on t algorithm = default lock = default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t lock = default algorithm = default", true, "DROP INDEX `idx` ON `t`"}, + {"drop index idx on t algorithm = inplace lock = exclusive", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, + {"drop index idx on t lock = exclusive algorithm = inplace", true, "DROP INDEX `idx` ON `t` ALGORITHM = INPLACE LOCK = EXCLUSIVE"}, + {"drop index idx on t algorithm = algorithm_type", false, ""}, + {"drop index idx on t algorithm algorithm_type", false, ""}, + {"drop index idx on t lock = lock_type", false, ""}, + {"drop index idx on t lock lock_type", false, ""}, + + // for rename table statement + {"RENAME TABLE t TO t1", true, "RENAME TABLE `t` TO `t1`"}, + {"RENAME TABLE t t1", false, "RENAME TABLE `t` TO `t1`"}, + {"RENAME TABLE d.t TO d1.t1", true, "RENAME TABLE `d`.`t` TO `d1`.`t1`"}, + {"RENAME TABLE t1 TO t2, t3 TO t4", true, "RENAME TABLE `t1` TO `t2`, `t3` TO `t4`"}, + + // for truncate statement + {"TRUNCATE TABLE t1", true, "TRUNCATE TABLE `t1`"}, + {"TRUNCATE t1", true, "TRUNCATE TABLE `t1`"}, + + // for empty alert table index + {"ALTER TABLE t ADD INDEX () ", false, ""}, + {"ALTER TABLE t ADD UNIQUE ()", false, ""}, + {"ALTER TABLE t ADD UNIQUE INDEX ()", false, ""}, + {"ALTER TABLE t ADD UNIQUE KEY ()", false, ""}, + + // for keyword `SECONDARY_LOAD`, `SECONDARY_UNLOAD` + {"ALTER TABLE d_n.t_n SECONDARY_LOAD", true, "ALTER TABLE `d_n`.`t_n` SECONDARY_LOAD"}, + {"ALTER TABLE d_n.t_n SECONDARY_UNLOAD", true, "ALTER TABLE `d_n`.`t_n` SECONDARY_UNLOAD"}, + {"ALTER TABLE t_n LOCK = DEFAULT , SECONDARY_LOAD", true, "ALTER TABLE `t_n` LOCK = DEFAULT, SECONDARY_LOAD"}, + {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , SECONDARY_LOAD", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, SECONDARY_LOAD"}, + {"ALTER TABLE d_n.t_n ALGORITHM = DEFAULT , SECONDARY_UNLOAD", true, "ALTER TABLE `d_n`.`t_n` ALGORITHM = DEFAULT, SECONDARY_UNLOAD"}, + + // for issue 4538 + {"create table a (process double)", true, "CREATE TABLE `a` (`process` DOUBLE)"}, + + // for issue 4740 + {"create table t (a int1, b int2, c int3, d int4, e int8)", true, "CREATE TABLE `t` (`a` TINYINT,`b` SMALLINT,`c` MEDIUMINT,`d` INT,`e` BIGINT)"}, + + // for issue 5918 + {"create table t (lv long varchar null)", true, "CREATE TABLE `t` (`lv` MEDIUMTEXT NULL)"}, + + // special table name + {"CREATE TABLE cdp_test.`test2-1` (id int(11) DEFAULT NULL,key(id));", true, "CREATE TABLE `cdp_test`.`test2-1` (`id` INT(11) DEFAULT NULL,INDEX(`id`))"}, + {"CREATE TABLE miantiao (`扁豆焖面` INT(11));", true, "CREATE TABLE `miantiao` (`扁豆焖面` INT(11))"}, + + // for create table select + {"CREATE TABLE bar (m INT) SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) AS SELECT `n` FROM `foo`"}, + {"CREATE TABLE bar (m INT) IGNORE SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) IGNORE AS SELECT `n` FROM `foo`"}, + {"CREATE TABLE bar (m INT) REPLACE SELECT n FROM foo;", true, "CREATE TABLE `bar` (`m` INT) REPLACE AS SELECT `n` FROM `foo`"}, + + // for generated column definition + {"create table t (a timestamp, b timestamp as (a) not null on update current_timestamp);", false, ""}, + {"create table t (a bigint, b bigint as (a) primary key auto_increment);", false, ""}, + {"create table t (a bigint, b bigint as (a) not null default 10);", false, ""}, + {"create table t (a bigint, b bigint as (a+1) not null);", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL)"}, + {"create table t (a bigint, b bigint as (a+1) not null);", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL)"}, + {"create table t (a bigint, b bigint as (a+1) not null comment 'ttt');", true, "CREATE TABLE `t` (`a` BIGINT,`b` BIGINT GENERATED ALWAYS AS(`a`+1) VIRTUAL NOT NULL COMMENT 'ttt')"}, + {"create table t(a int, index idx((cast(a as binary(1)))));", true, "CREATE TABLE `t` (`a` INT,INDEX `idx`((CAST(`a` AS BINARY(1)))))"}, + {"alter table t add column (f timestamp as (a+1) default '2019-01-01 11:11:11');", false, ""}, + {"alter table t modify column f int as (a+1) default 55;", false, ""}, + + // for column format + {"create table t (a int column_format fixed)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT FIXED)"}, + {"create table t (a int column_format default)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT DEFAULT)"}, + {"create table t (a int column_format dynamic)", true, "CREATE TABLE `t` (`a` INT COLUMN_FORMAT DYNAMIC)"}, + {"alter table t modify column a bigint column_format default", true, "ALTER TABLE `t` MODIFY COLUMN `a` BIGINT COLUMN_FORMAT DEFAULT"}, + + // for recover table + {"recover table by job 11", true, "RECOVER TABLE BY JOB 11"}, + {"recover table by job 11,12,13", false, ""}, + {"recover table by job", false, ""}, + {"recover table t1", true, "RECOVER TABLE `t1`"}, + {"recover table t1,t2", false, ""}, + {"recover table ", false, ""}, + {"recover table t1 100", true, "RECOVER TABLE `t1` 100"}, + {"recover table t1 abc", false, ""}, + + // for flashback table. + {"flashback table t", true, "FLASHBACK TABLE `t`"}, + {"flashback table t TO t1", true, "FLASHBACK TABLE `t` TO `t1`"}, + {"flashback table t TO timestamp", true, "FLASHBACK TABLE `t` TO `timestamp`"}, + + // for flashback database. + {"flashback database db1", true, "FLASHBACK DATABASE `db1`"}, + {"flashback schema db1", true, "FLASHBACK DATABASE `db1`"}, + {"flashback database db1 to db2", true, "FLASHBACK DATABASE `db1` TO `db2`"}, + {"flashback schema db1 to db2", true, "FLASHBACK DATABASE `db1` TO `db2`"}, + + // for flashback to timestamp + {"flashback cluster to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK CLUSTER TO TIMESTAMP '2021-05-26 16:45:26'"}, + {"flashback table t to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK TABLE `t` TO TIMESTAMP '2021-05-26 16:45:26'"}, + {"flashback table t,t1 to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK TABLE `t`, `t1` TO TIMESTAMP '2021-05-26 16:45:26'"}, + {"flashback database test to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK DATABASE `test` TO TIMESTAMP '2021-05-26 16:45:26'"}, + {"flashback schema test to timestamp '2021-05-26 16:45:26'", true, "FLASHBACK DATABASE `test` TO TIMESTAMP '2021-05-26 16:45:26'"}, + {"flashback cluster to timestamp TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", false, ""}, + {"flashback cluster to timestamp DATE_SUB(NOW(), INTERVAL 3 SECOND)", false, ""}, + {"flashback table to timestamp '2021-05-26 16:45:26'", false, ""}, + {"flashback database to timestamp '2021-05-26 16:45:26'", false, ""}, + + // for remove partitioning + {"alter table t remove partitioning", true, "ALTER TABLE `t` REMOVE PARTITIONING"}, + {"alter table db.ident remove partitioning", true, "ALTER TABLE `db`.`ident` REMOVE PARTITIONING"}, + {"alter table t lock = default remove partitioning", true, "ALTER TABLE `t` LOCK = DEFAULT REMOVE PARTITIONING"}, + {"alter table t add column a int remove partitioning", true, "ALTER TABLE `t` ADD COLUMN `a` INT REMOVE PARTITIONING"}, + {"alter table t add column a int, add index (c) remove partitioning", true, "ALTER TABLE `t` ADD COLUMN `a` INT, ADD INDEX(`c`) REMOVE PARTITIONING"}, + {"alter table t add column a int, remove partitioning", false, ""}, + {"alter table t add column a int, add index (c), remove partitioning", false, ""}, + {"alter table t remove partitioning add column a int", false, ""}, + {"alter table t remove partitioning, add column a int", false, ""}, + + // for references without IndexColNameList + {"alter table t add column a double (4,2) zerofill references b match full on update set null first", true, "ALTER TABLE `t` ADD COLUMN `a` DOUBLE(4,2) UNSIGNED ZEROFILL REFERENCES `b` MATCH FULL ON UPDATE SET NULL FIRST"}, + {"alter table d_n.t_n add constraint foreign key ident (ident(1)) references d_n.t_n match full on delete set null", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`(1)) REFERENCES `d_n`.`t_n` MATCH FULL ON DELETE SET NULL"}, + {"alter table t_n add constraint ident foreign key (ident,ident(1)) references t_n match full on update set null on delete restrict", true, "ALTER TABLE `t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`, `ident`(1)) REFERENCES `t_n` MATCH FULL ON DELETE RESTRICT ON UPDATE SET NULL"}, + {"alter table d_n.t_n add foreign key ident (ident, ident(1) asc) references t_n match partial on delete cascade remove partitioning", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT `ident` FOREIGN KEY (`ident`, `ident`(1)) REFERENCES `t_n` MATCH PARTIAL ON DELETE CASCADE REMOVE PARTITIONING"}, + {"alter table d_n.t_n add constraint foreign key (ident asc) references d_n.t_n match simple on update cascade on delete cascade", true, "ALTER TABLE `d_n`.`t_n` ADD CONSTRAINT FOREIGN KEY (`ident`) REFERENCES `d_n`.`t_n` MATCH SIMPLE ON DELETE CASCADE ON UPDATE CASCADE"}, + + // for character vary syntax + {"create table t (a character varying(1));", true, "CREATE TABLE `t` (`a` VARCHAR(1))"}, + {"create table t (a character varying(255));", true, "CREATE TABLE `t` (`a` VARCHAR(255))"}, + {"create table t (a char varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a varcharacter(1));", true, "CREATE TABLE `t` (`a` VARCHAR(1))"}, + {"create table t (a varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a varcharacter(1), b varcharacter(255));", true, "CREATE TABLE `t` (`a` VARCHAR(1),`b` VARCHAR(255))"}, + {"create table t (a char);", true, "CREATE TABLE `t` (`a` CHAR)"}, + {"create table t (a character);", true, "CREATE TABLE `t` (`a` CHAR)"}, + {"create table t (a character varying(50), b int);", true, "CREATE TABLE `t` (`a` VARCHAR(50),`b` INT)"}, + {"create table t (a character, b int);", true, "CREATE TABLE `t` (`a` CHAR,`b` INT)"}, + {"create table t (a national character varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a national char varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a national char);", true, "CREATE TABLE `t` (`a` CHAR)"}, + {"create table t (a national character);", true, "CREATE TABLE `t` (`a` CHAR)"}, + {"create table t (a nchar);", true, "CREATE TABLE `t` (`a` CHAR)"}, + {"create table t (a nchar varchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a nchar varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a national varchar);", false, ""}, + {"create table t (a national varchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a national varcharacter(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a nchar varying(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table t (a nvarchar(50));", true, "CREATE TABLE `t` (`a` VARCHAR(50))"}, + {"create table nchar (a int);", true, "CREATE TABLE `nchar` (`a` INT)"}, + {"create table nchar (a int, b nchar);", true, "CREATE TABLE `nchar` (`a` INT,`b` CHAR)"}, + {"create table nchar (a int, b nchar(50));", true, "CREATE TABLE `nchar` (`a` INT,`b` CHAR(50))"}, + {"alter table t_n storage disk , modify ident national varcharacter(12) column_format fixed first;", true, "ALTER TABLE `t_n` STORAGE DISK, MODIFY COLUMN `ident` VARCHAR(12) COLUMN_FORMAT FIXED FIRST"}, + + // Test keyword `SERIAL` + {"create table t (a serial);", true, "CREATE TABLE `t` (`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, + {"create table t (a serial null);", true, "CREATE TABLE `t` (`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY NULL)"}, + {"create table t (b int, a serial);", true, "CREATE TABLE `t` (`b` INT,`a` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, + {"create table t (a int serial default value);", true, "CREATE TABLE `t` (`a` INT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, + {"create table t (a int serial default value null);", true, "CREATE TABLE `t` (`a` INT NOT NULL AUTO_INCREMENT UNIQUE KEY NULL)"}, + {"create table t (a bigint serial default value);", true, "CREATE TABLE `t` (`a` BIGINT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, + {"create table t (a smallint serial default value);", true, "CREATE TABLE `t` (`a` SMALLINT NOT NULL AUTO_INCREMENT UNIQUE KEY)"}, + + // for LONG syntax + {"create table t (a long);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, + {"create table t (a long varchar);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, + {"create table t (a long varcharacter);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, + {"create table t (a long char varying);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, + {"create table t (a long character varying);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT)"}, + {"create table t (a mediumtext, b long varchar, c long, d long varcharacter, e long char varying, f long character varying, g long);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT,`b` MEDIUMTEXT,`c` MEDIUMTEXT,`d` MEDIUMTEXT,`e` MEDIUMTEXT,`f` MEDIUMTEXT,`g` MEDIUMTEXT)"}, + {"create table t (a long varbinary);", true, "CREATE TABLE `t` (`a` MEDIUMBLOB)"}, + {"create table t (a long char varying, b long varbinary);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT,`b` MEDIUMBLOB)"}, + {"create table t (a long char set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, + {"create table t (a long char varying char set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, + {"create table t (a long character set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, + {"create table t (a long character varying character set utf8);", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8)"}, + {"alter table d_n.t_n modify column ident long after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, + {"alter table d_n.t_n modify column ident long char varying after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, + {"alter table d_n.t_n modify column ident long character varying after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, + {"alter table d_n.t_n modify column ident long varchar after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, + {"alter table d_n.t_n modify column ident long varcharacter after ident remove partitioning", true, "ALTER TABLE `d_n`.`t_n` MODIFY COLUMN `ident` MEDIUMTEXT AFTER `ident` REMOVE PARTITIONING"}, + {"alter table t_n change column ident ident long char varying binary charset utf8 first , tablespace ident", true, "ALTER TABLE `t_n` CHANGE COLUMN `ident` `ident` MEDIUMTEXT BINARY CHARACTER SET UTF8 FIRST, TABLESPACE = `ident`"}, + {"alter table t_n change column ident ident long character varying binary charset utf8 first , tablespace ident", true, "ALTER TABLE `t_n` CHANGE COLUMN `ident` `ident` MEDIUMTEXT BINARY CHARACTER SET UTF8 FIRST, TABLESPACE = `ident`"}, + + // for STATS_AUTO_RECALC syntax + {"create table t (a int) stats_auto_recalc 2;", false, ""}, + {"create table t (a int) stats_auto_recalc = 10;", false, ""}, + {"create table t (a int) stats_auto_recalc 0;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 0"}, + {"create table t (a int) stats_auto_recalc default;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = DEFAULT"}, + {"create table t (a int) stats_auto_recalc = 0;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 0"}, + {"create table t (a int) stats_auto_recalc = 1;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1"}, + {"create table t (a int) stats_auto_recalc=default;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = DEFAULT"}, + {"create table t (a int) stats_persistent = 1, stats_auto_recalc = 1;", true, "CREATE TABLE `t` (`a` INT) STATS_PERSISTENT = DEFAULT /* TableOptionStatsPersistent is not supported */ STATS_AUTO_RECALC = 1"}, + {"create table t (a int) stats_auto_recalc = 1, stats_sample_pages = 25;", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1 STATS_SAMPLE_PAGES = 25"}, + {"alter table t modify a bigint, ENGINE=InnoDB, stats_auto_recalc = 0", true, "ALTER TABLE `t` MODIFY COLUMN `a` BIGINT, ENGINE = InnoDB, STATS_AUTO_RECALC = 0"}, + {"create table stats_auto_recalc (a int);", true, "CREATE TABLE `stats_auto_recalc` (`a` INT)"}, + {"create table stats_auto_recalc (a int) stats_auto_recalc=1;", true, "CREATE TABLE `stats_auto_recalc` (`a` INT) STATS_AUTO_RECALC = 1"}, + + // for TYPE/USING syntax + {"create table t (a int, primary key type type btree (a));", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY `type`(`a`) USING BTREE)"}, + {"create table t (a int, primary key type btree (a));", false, ""}, + {"create table t (a int, primary key using btree (a));", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, + {"create table t (a int, primary key (a) type btree);", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, + {"create table t (a int, primary key (a) using btree);", true, "CREATE TABLE `t` (`a` INT,PRIMARY KEY(`a`) USING BTREE)"}, + {"create table t (a int, unique index type type btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE `type`(`a`) USING BTREE)"}, + {"create table t (a int, unique index type using btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE `type`(`a`) USING BTREE)"}, + {"create table t (a int, unique index type btree (a));", false, ""}, + {"create table t (a int, unique index using btree (a));", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, + {"create table t (a int, unique index (a) using btree);", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, + {"create table t (a int, unique key (a) using btree);", true, "CREATE TABLE `t` (`a` INT,UNIQUE(`a`) USING BTREE)"}, + {"create table t (a int, index type type btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX `type`(`a`) USING BTREE)"}, + {"create table t (a int, index type btree (a));", false, ""}, + {"create table t (a int, index type using btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX `type`(`a`) USING BTREE)"}, + {"create table t (a int, index using btree (a));", true, "CREATE TABLE `t` (`a` INT,INDEX(`a`) USING BTREE)"}, + + // for issue 500 + {`ALTER TABLE d_n.t_n WITHOUT VALIDATION , ADD PARTITION ( PARTITION ident VALUES LESS THAN ( MAXVALUE ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITHOUT VALIDATION, ADD PARTITION (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string MAX_ROWS = 12)"}, + {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION ident VALUES LESS THAN MAXVALUE STORAGE ENGINE = text_string, PARTITION ident VALUES LESS THAN ( MAXVALUE ) (SUBPARTITION text_string MIN_ROWS 11))`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string, PARTITION `ident` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `text_string` MIN_ROWS = 11))"}, + // for test VALUE IN + {`ALTER TABLE d_n.t_n WITHOUT VALIDATION , ADD PARTITION ( PARTITION ident VALUES IN ( DEFAULT ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITHOUT VALIDATION, ADD PARTITION (PARTITION `ident` DEFAULT ENGINE = text_string MAX_ROWS = 12)"}, + {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG ( PARTITION ident VALUES IN ( DEFAULT ) STORAGE ENGINE text_string MAX_ROWS 12 )`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` DEFAULT ENGINE = text_string MAX_ROWS = 12)"}, + {`ALTER TABLE d_n.t_n ADD PARTITION ( PARTITION ident VALUES IN ( DEFAULT ), partition ptext values in ('default') )`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` DEFAULT, PARTITION `ptext` VALUES IN (_UTF8MB4'default'))"}, + {`ALTER TABLE d_n.t_n WITH VALIDATION , ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION ident VALUES LESS THAN MAXVALUE STORAGE ENGINE = text_string, PARTITION ident VALUES IN ( DEFAULT ) (SUBPARTITION text_string MIN_ROWS 11))`, true, "ALTER TABLE `d_n`.`t_n` WITH VALIDATION, ADD PARTITION NO_WRITE_TO_BINLOG (PARTITION `ident` VALUES LESS THAN (MAXVALUE) ENGINE = text_string, PARTITION `ident` DEFAULT (SUBPARTITION `text_string` MIN_ROWS = 11))"}, + {`ALTER TABLE d_n.t_n ADD PARTITION (PARTITION ident VALUES IN ( DEFAULT ))`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` DEFAULT)"}, + {`ALTER TABLE d_n.t_n ADD PARTITION (PARTITION ident VALUES IN (1, default ))`, true, "ALTER TABLE `d_n`.`t_n` ADD PARTITION (PARTITION `ident` VALUES IN (1, DEFAULT))"}, + // for issue 501 + {"ALTER TABLE t IMPORT TABLESPACE;", true, "ALTER TABLE `t` IMPORT TABLESPACE"}, + {"ALTER TABLE t DISCARD TABLESPACE;", true, "ALTER TABLE `t` DISCARD TABLESPACE"}, + {"ALTER TABLE db.t IMPORT TABLESPACE;", true, "ALTER TABLE `db`.`t` IMPORT TABLESPACE"}, + {"ALTER TABLE db.t DISCARD TABLESPACE;", true, "ALTER TABLE `db`.`t` DISCARD TABLESPACE"}, + + // for CONSTRAINT syntax, see issue 413 + {"ALTER TABLE t ADD ( CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, + {"ALTER TABLE t ADD ( CONSTRAINT CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, + {"ALTER TABLE t ADD COLUMN ( CONSTRAINT ident CHECK ( 1>2 ) NOT ENFORCED )", true, "ALTER TABLE `t` ADD COLUMN (CONSTRAINT `ident` CHECK(1>2) NOT ENFORCED)"}, + {"alter table t add column (b int, constraint c unique key (b))", true, "ALTER TABLE `t` ADD COLUMN (`b` INT, UNIQUE `c`(`b`))"}, + {"ALTER TABLE t ADD COLUMN ( CONSTRAINT CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED)"}, + {"ALTER TABLE t ADD COLUMN ( CONSTRAINT CHECK ( true ) ENFORCED , CHECK ( true ) )", true, "ALTER TABLE `t` ADD COLUMN (CHECK(TRUE) ENFORCED, CHECK(TRUE) ENFORCED)"}, + {"ALTER TABLE t ADD COLUMN (a1 int, CONSTRAINT b1 CHECK (a1>0))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, CONSTRAINT `b1` CHECK(`a1`>0) ENFORCED)"}, + {"ALTER TABLE t ADD COLUMN (a1 int, a2 int, CONSTRAINT b1 CHECK (a1>0), CONSTRAINT b2 CHECK (a2<10))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, CONSTRAINT `b1` CHECK(`a1`>0) ENFORCED, CONSTRAINT `b2` CHECK(`a2`<10) ENFORCED)"}, + {"ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY (`a1`))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY(`a1`))"}, + {"ALTER TABLE t ADD (a1 int, CONSTRAINT PRIMARY KEY (a1))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, PRIMARY KEY(`a1`))"}, + {"ALTER TABLE t ADD (a1 int, a2 int, PRIMARY KEY (a1), UNIQUE (a2))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, PRIMARY KEY(`a1`), UNIQUE(`a2`))"}, + {"ALTER TABLE t ADD (a1 int, a2 int, PRIMARY KEY (a1), CONSTRAINT b2 UNIQUE (a2))", true, "ALTER TABLE `t` ADD COLUMN (`a1` INT, `a2` INT, PRIMARY KEY(`a1`), UNIQUE `b2`(`a2`))"}, + {"ALTER TABLE ident ADD ( CONSTRAINT FOREIGN KEY ident ( EXECUTE ( 123 ) ) REFERENCES t ( a ) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL )", true, "ALTER TABLE `ident` ADD COLUMN (CONSTRAINT `ident` FOREIGN KEY (`EXECUTE`(123)) REFERENCES `t`(`a`) MATCH SIMPLE ON DELETE CASCADE ON UPDATE SET NULL)"}, + // for CONSTRAINT cont'd, the following tests are for another aspect of the incompatibility + {"ALTER TABLE t ADD COLUMN a DATE CHECK ( a > 0 ) FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` DATE CHECK(`a`>0) ENFORCED FIRST"}, + {"ALTER TABLE t ADD a1 int CONSTRAINT ident CHECK ( a1 > 1 ) REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE;", true, "ALTER TABLE `t` ADD COLUMN `a1` INT CONSTRAINT `ident` CHECK(`a1`>1) ENFORCED REFERENCES `b` ON DELETE CASCADE ON UPDATE CASCADE"}, + {"ALTER TABLE t ADD COLUMN a DATE CONSTRAINT CHECK ( a > 0 ) FIRST", true, "ALTER TABLE `t` ADD COLUMN `a` DATE CHECK(`a`>0) ENFORCED FIRST"}, + {"ALTER TABLE t ADD a TINYBLOB CONSTRAINT ident CHECK ( 1>2 ) REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE", true, "ALTER TABLE `t` ADD COLUMN `a` TINYBLOB CONSTRAINT `ident` CHECK(1>2) ENFORCED REFERENCES `b` ON DELETE CASCADE ON UPDATE CASCADE"}, + {"ALTER TABLE t ADD a2 int CONSTRAINT ident CHECK (a2 > 1) ENFORCED", true, "ALTER TABLE `t` ADD COLUMN `a2` INT CONSTRAINT `ident` CHECK(`a2`>1) ENFORCED"}, + {"ALTER TABLE t ADD a2 int CONSTRAINT ident CHECK (a2 > 1) NOT ENFORCED", true, "ALTER TABLE `t` ADD COLUMN `a2` INT CONSTRAINT `ident` CHECK(`a2`>1) NOT ENFORCED"}, + {"ALTER TABLE t ADD a2 int CONSTRAINT ident primary key REFERENCES b ON DELETE CASCADE ON UPDATE CASCADE;", false, ""}, + {"ALTER TABLE t ADD a2 int CONSTRAINT ident primary key (a2))", false, ""}, + {"ALTER TABLE t ADD a2 int CONSTRAINT ident unique key (a2))", false, ""}, + + {"ALTER TABLE t SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER TABLE `t` SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, + {"ALTER TABLE t SET TIFLASH REPLICA 0", true, "ALTER TABLE `t` SET TIFLASH REPLICA 0"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 0", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 0"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2 LOCATION LABELS 'a','b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 SET TIFLASH REPLICA 2"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 1 LOCATION LABELS 'a','b' SET TIFLASH REPLICA 2", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 LOCATION LABELS 'a', 'b' SET TIFLASH REPLICA 2"}, + {"ALTER DATABASE t SET TIFLASH REPLICA 1 LOCATION LABELS 'a','b' SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'", true, "ALTER DATABASE `t` SET TIFLASH REPLICA 1 LOCATION LABELS 'a', 'b' SET TIFLASH REPLICA 2 LOCATION LABELS 'a', 'b'"}, + + // for issue 537 + {"CREATE TABLE IF NOT EXISTS table_ident (a SQL_TSI_YEAR(4), b SQL_TSI_YEAR);", true, "CREATE TABLE IF NOT EXISTS `table_ident` (`a` YEAR(4),`b` YEAR)"}, + {`CREATE TABLE IF NOT EXISTS table_ident (ident1 BOOL COMMENT "text_string" unique, ident2 SQL_TSI_YEAR(4) ZEROFILL);`, true, "CREATE TABLE IF NOT EXISTS `table_ident` (`ident1` TINYINT(1) COMMENT 'text_string' UNIQUE KEY,`ident2` YEAR(4))"}, + {"create table t (y sql_tsi_year(4), y1 sql_tsi_year)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, + {"create table t (y sql_tsi_year(4) unsigned zerofill zerofill, y1 sql_tsi_year signed unsigned zerofill)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, + + // for issue 549 + {"insert into t set a = default", true, "INSERT INTO `t` SET `a`=DEFAULT"}, + {"insert into t set a := default", true, "INSERT INTO `t` SET `a`=DEFAULT"}, + {"replace t set a = default", true, "REPLACE INTO `t` SET `a`=DEFAULT"}, + {"update t set a = default", true, "UPDATE `t` SET `a`=DEFAULT"}, + {"insert into t set a = default on duplicate key update a = default", true, "INSERT INTO `t` SET `a`=DEFAULT ON DUPLICATE KEY UPDATE `a`=DEFAULT"}, + + // for issue 529 + {"create table t (a text byte ascii)", false, ""}, + {"create table t (a text byte charset latin1)", false, ""}, + {"create table t (a longtext ascii)", true, "CREATE TABLE `t` (`a` LONGTEXT CHARACTER SET LATIN1)"}, + {"create table t (a mediumtext ascii)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET LATIN1)"}, + {"create table t (a tinytext ascii)", true, "CREATE TABLE `t` (`a` TINYTEXT CHARACTER SET LATIN1)"}, + {"create table t (a text byte)", true, "CREATE TABLE `t` (`a` BLOB)"}, + {"create table t (a long byte, b text ascii)", true, "CREATE TABLE `t` (`a` MEDIUMBLOB,`b` TEXT CHARACTER SET LATIN1)"}, + {"create table t (a text ascii, b mediumtext ascii, c int)", true, "CREATE TABLE `t` (`a` TEXT CHARACTER SET LATIN1,`b` MEDIUMTEXT CHARACTER SET LATIN1,`c` INT)"}, + {"create table t (a int, b text ascii, c mediumtext ascii)", true, "CREATE TABLE `t` (`a` INT,`b` TEXT CHARACTER SET LATIN1,`c` MEDIUMTEXT CHARACTER SET LATIN1)"}, + {"create table t (a long ascii, b long ascii)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET LATIN1,`b` MEDIUMTEXT CHARACTER SET LATIN1)"}, + {"create table t (a long character set utf8mb4, b long charset utf8mb4, c long char set utf8mb4)", true, "CREATE TABLE `t` (`a` MEDIUMTEXT CHARACTER SET UTF8MB4,`b` MEDIUMTEXT CHARACTER SET UTF8MB4,`c` MEDIUMTEXT CHARACTER SET UTF8MB4)"}, + + {"create table t (a int STORAGE MEMORY, b varchar(255) STORAGE MEMORY)", true, "CREATE TABLE `t` (`a` INT STORAGE MEMORY,`b` VARCHAR(255) STORAGE MEMORY)"}, + {"create table t (a int storage DISK, b varchar(255) STORAGE DEFAULT)", true, "CREATE TABLE `t` (`a` INT STORAGE DISK,`b` VARCHAR(255) STORAGE DEFAULT)"}, + {"create table t (a int STORAGE DEFAULT, b varchar(255) STORAGE DISK)", true, "CREATE TABLE `t` (`a` INT STORAGE DEFAULT,`b` VARCHAR(255) STORAGE DISK)"}, + + // for issue 555 + {"create table t (a fixed(6, 3), b fixed key)", true, "CREATE TABLE `t` (`a` DECIMAL(6,3),`b` DECIMAL PRIMARY KEY)"}, + {"create table t (a numeric, b fixed(6))", true, "CREATE TABLE `t` (`a` DECIMAL,`b` DECIMAL(6))"}, + {"create table t (a fixed(65, 30) zerofill, b numeric, c fixed(65) unsigned zerofill)", true, "CREATE TABLE `t` (`a` DECIMAL(65,30) UNSIGNED ZEROFILL,`b` DECIMAL,`c` DECIMAL(65) UNSIGNED ZEROFILL)"}, + + // create table with expression index + {"create table a(a int, key(lower(a)));", false, ""}, + {"create table a(a int, key(a+1));", false, ""}, + {"create table a(a int, key(a, a+1));", false, ""}, + {"create table a(a int, b int, key((a+1), (b+1)));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1), (`b`+1)))"}, + {"create table a(a int, b int, key(a, (b+1)));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX(`a`, (`b`+1)))"}, + {"create table a(a int, b int, key((a+1), b));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1), `b`))"}, + {"create table a(a int, b int, key((a + 1) desc));", true, "CREATE TABLE `a` (`a` INT,`b` INT,INDEX((`a`+1) DESC))"}, + + // for create sequence + {"create sequence sequence", true, "CREATE SEQUENCE `sequence`"}, + {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, + {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, + {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, + {"create sequence seq", true, "CREATE SEQUENCE `seq`"}, + {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, + {"create sequence if not exists seq", true, "CREATE SEQUENCE IF NOT EXISTS `seq`"}, + {"create sequence if not exists seq increment", false, ""}, + {"create sequence if not exists seq increment 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, + {"create sequence if not exists seq increment = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, + {"create sequence if not exists seq increment by 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1"}, + {"create sequence if not exists seq minvalue", false, ""}, + {"create sequence if not exists seq minvalue 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, + {"create sequence if not exists seq minvalue = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MINVALUE 1"}, + {"create sequence if not exists seq no", false, ""}, + {"create sequence if not exists seq nominvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, + {"create sequence if not exists seq no minvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MINVALUE"}, + {"create sequence if not exists seq maxvalue", false, ""}, + {"create sequence if not exists seq maxvalue 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, + {"create sequence if not exists seq maxvalue = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` MAXVALUE 1"}, + {"create sequence if not exists seq no", false, ""}, + {"create sequence if not exists seq nomaxvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, + {"create sequence if not exists seq no maxvalue", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NO MAXVALUE"}, + {"create sequence if not exists seq start", false, ""}, + {"create sequence if not exists seq start with", false, ""}, + {"create sequence if not exists seq start =", false, ""}, + {"create sequence if not exists seq start with", false, ""}, + {"create sequence if not exists seq start 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, + {"create sequence if not exists seq start = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, + {"create sequence if not exists seq start with 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` START WITH 1"}, + {"create sequence if not exists seq cache", false, ""}, + {"create sequence if not exists seq cache 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, + {"create sequence if not exists seq cache = 1", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1"}, + {"create sequence if not exists seq nocache", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, + {"create sequence if not exists seq no cache", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCACHE"}, + {"create sequence if not exists seq cycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CYCLE"}, + {"create sequence if not exists seq nocycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, + {"create sequence if not exists seq no cycle", true, "CREATE SEQUENCE IF NOT EXISTS `seq` NOCYCLE"}, + {"create sequence seq increment 1 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 1 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, + {"create sequence seq increment 1 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 1 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, + // TODO : support or replace if need : care for it will conflict on temporary. + {"create sequence seq increment 10 start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `seq` INCREMENT BY 10 START WITH 0 MINVALUE 0 MAXVALUE 1000"}, + {"create sequence if not exists seq cache 1 increment 1 start with -1 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE IF NOT EXISTS `seq` CACHE 1 INCREMENT BY 1 START WITH -1 MINVALUE 0 MAXVALUE 1000"}, + {"create sequence sEq start with 0 minvalue 0 maxvalue 1000", true, "CREATE SEQUENCE `sEq` START WITH 0 MINVALUE 0 MAXVALUE 1000"}, + {"create sequence if not exists seq increment 1 start with 0 minvalue -2 maxvalue 1000", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1 START WITH 0 MINVALUE -2 MAXVALUE 1000"}, + {"create sequence seq increment -1 start with -1 minvalue -1 maxvalue -1000 cache = 10 nocycle", true, "CREATE SEQUENCE `seq` INCREMENT BY -1 START WITH -1 MINVALUE -1 MAXVALUE -1000 CACHE 10 NOCYCLE"}, + + // test sequence is not a reserved keyword + {"create table sequence (a int)", true, "CREATE TABLE `sequence` (`a` INT)"}, + {"create table t (sequence int)", true, "CREATE TABLE `t` (`sequence` INT)"}, + + // test drop sequence + {"drop sequence", false, ""}, + {"drop sequence seq", true, "DROP SEQUENCE `seq`"}, + {"drop sequence if exists seq", true, "DROP SEQUENCE IF EXISTS `seq`"}, + {"drop sequence seq", true, "DROP SEQUENCE `seq`"}, + {"drop sequence if exists seq", true, "DROP SEQUENCE IF EXISTS `seq`"}, + {"drop sequence if exists seq, seq2, seq3", true, "DROP SEQUENCE IF EXISTS `seq`, `seq2`, `seq3`"}, + {"drop sequence seq seq2", false, ""}, + {"drop sequence seq, seq2", true, "DROP SEQUENCE `seq`, `seq2`"}, + + // for auto_random + {"create table t (a bigint auto_random(3) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(3) PRIMARY KEY,`b` VARCHAR(255))"}, + {"create table t (a bigint auto_random primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM PRIMARY KEY,`b` VARCHAR(255))"}, + {"create table t (a bigint primary key auto_random(4), b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(4),`b` VARCHAR(255))"}, + {"create table t (a bigint primary key auto_random(3) primary key unique, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(3) PRIMARY KEY UNIQUE KEY,`b` VARCHAR(255))"}, + {"create table t (a bigint auto_random(5, 53) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(5, 53) PRIMARY KEY,`b` VARCHAR(255))"}, + {"create table t (a bigint auto_random(15, 32) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(15, 32) PRIMARY KEY,`b` VARCHAR(255))"}, + + // for auto_id_cache + {"create table t (a int) auto_id_cache=1", true, "CREATE TABLE `t` (`a` INT) AUTO_ID_CACHE = 1"}, + {"create table t (a int auto_increment key) auto_id_cache 10", true, "CREATE TABLE `t` (`a` INT AUTO_INCREMENT PRIMARY KEY) AUTO_ID_CACHE = 10"}, + {"create table t (a bigint, b varchar(255)) auto_id_cache 50", true, "CREATE TABLE `t` (`a` BIGINT,`b` VARCHAR(255)) AUTO_ID_CACHE = 50"}, + + // for auto_random_id + {"create table t (a bigint auto_random(3) primary key) auto_random_base = 10", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(3) PRIMARY KEY) AUTO_RANDOM_BASE = 10"}, + {"create table t (a bigint primary key auto_random(4), b varchar(100)) auto_random_base 200", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(4),`b` VARCHAR(100)) AUTO_RANDOM_BASE = 200"}, + {"alter table t auto_random_base = 50", true, "ALTER TABLE `t` AUTO_RANDOM_BASE = 50"}, + {"alter table t auto_increment 30, auto_random_base 40", true, "ALTER TABLE `t` AUTO_INCREMENT = 30, AUTO_RANDOM_BASE = 40"}, + {"alter table t force auto_random_base = 50", true, "ALTER TABLE `t` FORCE AUTO_RANDOM_BASE = 50"}, + {"alter table t auto_increment 30, force auto_random_base 40", true, "ALTER TABLE `t` AUTO_INCREMENT = 30, FORCE AUTO_RANDOM_BASE = 40"}, + + // for alter sequence + {"alter sequence seq", false, ""}, + {"alter sequence seq comment=\"haha\"", false, ""}, + {"alter sequence seq start = 1", true, "ALTER SEQUENCE `seq` START WITH 1"}, + {"alter sequence seq start with 1 increment by 1", true, "ALTER SEQUENCE `seq` START WITH 1 INCREMENT BY 1"}, + {"alter sequence seq start with 1 increment by 2 minvalue 0 maxvalue 100", true, "ALTER SEQUENCE `seq` START WITH 1 INCREMENT BY 2 MINVALUE 0 MAXVALUE 100"}, + {"alter sequence seq increment -1 start with -1 minvalue -1 maxvalue -1000 cache = 10 nocycle", true, "ALTER SEQUENCE `seq` INCREMENT BY -1 START WITH -1 MINVALUE -1 MAXVALUE -1000 CACHE 10 NOCYCLE"}, + {"alter sequence if exists seq2 increment = 2", true, "ALTER SEQUENCE IF EXISTS `seq2` INCREMENT BY 2"}, + {"alter sequence seq restart", true, "ALTER SEQUENCE `seq` RESTART"}, + {"alter sequence seq start with 3 restart with 5", true, "ALTER SEQUENCE `seq` START WITH 3 RESTART WITH 5"}, + {"alter sequence seq restart = 5", true, "ALTER SEQUENCE `seq` RESTART WITH 5"}, + {"create sequence seq restart = 5", false, ""}, + + // for issue 18149 + {"create table t (a int, index ``(a))", true, "CREATE TABLE `t` (`a` INT,INDEX ``(`a`))"}, + + // for clustered index + {"create table t (a int, b varchar(255), primary key(b, a) clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) CLUSTERED)"}, + {"create table t (a int, b varchar(255), primary key(b, a) nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED)"}, + {"create table t (a int primary key nonclustered, b varchar(255))", true, "CREATE TABLE `t` (`a` INT PRIMARY KEY NONCLUSTERED,`b` VARCHAR(255))"}, + {"create table t (a int, b varchar(255) primary key clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) PRIMARY KEY CLUSTERED)"}, + {"create table t (a int, b varchar(255) default 'a' primary key clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) DEFAULT _UTF8MB4'a' PRIMARY KEY CLUSTERED)"}, + {"create table t (a int, b varchar(255) primary key nonclustered, primary key(b, a) nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255) PRIMARY KEY NONCLUSTERED,PRIMARY KEY(`b`, `a`) NONCLUSTERED)"}, + {"create table t (a int, b varchar(255), primary key(b, a) using RTREE nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED USING RTREE)"}, + {"create table t (a int, b varchar(255), primary key(b, a) using RTREE clustered nonclustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) NONCLUSTERED USING RTREE)"}, + {"create table t (a int, b varchar(255), primary key(b, a) using RTREE nonclustered clustered)", true, "CREATE TABLE `t` (`a` INT,`b` VARCHAR(255),PRIMARY KEY(`b`, `a`) CLUSTERED USING RTREE)"}, + {"create table t (a int, b varchar(255) clustered primary key)", false, ""}, + {"create table t (a int, b varchar(255) primary key nonclustered clustered)", false, ""}, + {"alter table t add primary key (`a`, `b`) clustered", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`, `b`) CLUSTERED"}, + {"alter table t add primary key (`a`, `b`) nonclustered", true, "ALTER TABLE `t` ADD PRIMARY KEY(`a`, `b`) NONCLUSTERED"}, + + // for drop placement policy + {"drop placement policy x", true, "DROP PLACEMENT POLICY `x`"}, + {"drop placement policy x, y", false, ""}, + {"drop placement policy if exists x", true, "DROP PLACEMENT POLICY IF EXISTS `x`"}, + {"drop placement policy if exists x, y", false, ""}, + // for show create placement policy + {"show create placement policy x", true, "SHOW CREATE PLACEMENT POLICY `x`"}, + {"show create placement policy if exists x", false, ""}, + {"show create placement policy x, y", false, ""}, + {"show create placement policy `placement`", true, "SHOW CREATE PLACEMENT POLICY `placement`"}, + // for create placement policy + {"create placement policy x primary_region='us'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'us'"}, + {"create placement policy x region='us, 3'", false, ""}, + {"create placement policy x followers=3", true, "CREATE PLACEMENT POLICY `x` FOLLOWERS = 3"}, + {"create placement policy x followers=0", false, ""}, + {"create placement policy x voters=3", true, "CREATE PLACEMENT POLICY `x` VOTERS = 3"}, + {"create placement policy x learners=3", true, "CREATE PLACEMENT POLICY `x` LEARNERS = 3"}, + {"create placement policy x schedule='even'", true, "CREATE PLACEMENT POLICY `x` SCHEDULE = 'even'"}, + {"create placement policy x constraints='ww'", true, "CREATE PLACEMENT POLICY `x` CONSTRAINTS = 'ww'"}, + {"create placement policy x leader_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` LEADER_CONSTRAINTS = 'ww'"}, + {"create placement policy x follower_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` FOLLOWER_CONSTRAINTS = 'ww'"}, + {"create placement policy x voter_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` VOTER_CONSTRAINTS = 'ww'"}, + {"create placement policy x learner_constraints='ww'", true, "CREATE PLACEMENT POLICY `x` LEARNER_CONSTRAINTS = 'ww'"}, + {"create placement policy x primary_region='cn' regions='us' schedule='even'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' REGIONS = 'us' SCHEDULE = 'even'"}, + {"create placement policy x primary_region='cn', leader_constraints='ww', leader_constraints='yy'", true, "CREATE PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' LEADER_CONSTRAINTS = 'ww' LEADER_CONSTRAINTS = 'yy'"}, + {"create placement policy if not exists x regions = 'us', follower_constraints='yy'", true, "CREATE PLACEMENT POLICY IF NOT EXISTS `x` REGIONS = 'us' FOLLOWER_CONSTRAINTS = 'yy'"}, + {"create or replace placement policy x regions='us'", true, "CREATE OR REPLACE PLACEMENT POLICY `x` REGIONS = 'us'"}, + {"create placement policy x placement policy y", false, ""}, + + {"alter placement policy x primary_region='us'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'us'"}, + {"alter placement policy x region='us, 3'", false, ""}, + {"alter placement policy x followers=3", true, "ALTER PLACEMENT POLICY `x` FOLLOWERS = 3"}, + {"alter placement policy x voters=3", true, "ALTER PLACEMENT POLICY `x` VOTERS = 3"}, + {"alter placement policy x learners=3", true, "ALTER PLACEMENT POLICY `x` LEARNERS = 3"}, + {"alter placement policy x schedule='even'", true, "ALTER PLACEMENT POLICY `x` SCHEDULE = 'even'"}, + {"alter placement policy x constraints='ww'", true, "ALTER PLACEMENT POLICY `x` CONSTRAINTS = 'ww'"}, + {"alter placement policy x leader_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` LEADER_CONSTRAINTS = 'ww'"}, + {"alter placement policy x follower_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` FOLLOWER_CONSTRAINTS = 'ww'"}, + {"alter placement policy x voter_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` VOTER_CONSTRAINTS = 'ww'"}, + {"alter placement policy x learner_constraints='ww'", true, "ALTER PLACEMENT POLICY `x` LEARNER_CONSTRAINTS = 'ww'"}, + {"alter placement policy x primary_region='cn' regions='us' schedule='even'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' REGIONS = 'us' SCHEDULE = 'even'"}, + {"alter placement policy x primary_region='cn', leader_constraints='ww', leader_constraints='yy'", true, "ALTER PLACEMENT POLICY `x` PRIMARY_REGION = 'cn' LEADER_CONSTRAINTS = 'ww' LEADER_CONSTRAINTS = 'yy'"}, + {"alter placement policy if exists x regions = 'us', follower_constraints='yy'", true, "ALTER PLACEMENT POLICY IF EXISTS `x` REGIONS = 'us' FOLLOWER_CONSTRAINTS = 'yy'"}, + {"alter placement policy x placement policy y", false, ""}, + + // for create resource group + {"create resource group x cpu ='8c'", false, ""}, + {"create resource group x region ='us, 3'", false, ""}, + {"create resource group x cpu='8c', io_read_bandwidth='2GB/s', io_write_bandwidth='200MB/s'", false, ""}, + {"create resource group x ru_per_sec=2000", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 2000"}, + {"create resource group x ru_per_sec=200000", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 200000"}, + {"create resource group x followers=0", false, ""}, + {"create resource group x ru_per_sec=1000, burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BURSTABLE = TRUE"}, + {"create resource group x burstable, ru_per_sec=2000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 2000"}, + {"create resource group x ru_per_sec=3000 burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 3000, BURSTABLE = TRUE"}, + {"create resource group x burstable ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, + {"create resource group x burstable=false ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = FALSE, RU_PER_SEC = 4000"}, + {"create resource group x burstable = true ru_per_sec=4000", true, "CREATE RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, + {"create resource group x ru_per_sec=20, priority=LOW, burstable", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 20, PRIORITY = LOW, BURSTABLE = TRUE"}, + {"create resource group default ru_per_sec=20, priority=LOW, burstable", true, "CREATE RESOURCE GROUP `default` RU_PER_SEC = 20, PRIORITY = LOW, BURSTABLE = TRUE"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION DRYRUN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = DRYRUN)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10m' ACTION COOLDOWN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10m' ACTION = COOLDOWN)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(ACTION KILL EXEC_ELAPSED='10m')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (ACTION = KILL EXEC_ELAPSED = '10m')"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' WATCH=SIMILAR DURATION '10m' ACTION COOLDOWN)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' WATCH = SIMILAR DURATION = '10m' ACTION = COOLDOWN)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED \"10s\" ACTION COOLDOWN WATCH EXACT DURATION='10m')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m')"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '9s' ACTION COOLDOWN WATCH EXACT)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '9s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '8s' ACTION COOLDOWN WATCH EXACT DURATION = UNLIMITED)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '8s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '7s' ACTION COOLDOWN WATCH EXACT DURATION UNLIMITED)", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '7s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED '7s' ACTION COOLDOWN WATCH EXACT DURATION 'UNLIMITED')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '7s' ACTION = COOLDOWN WATCH = EXACT DURATION = UNLIMITED)"}, + {"create resource group x ru_per_sec=1000 background = (task_types='')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BACKGROUND = (TASK_TYPES = '')"}, + {"create resource group x ru_per_sec=1000 background (task_types='br,lightning')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, BACKGROUND = (TASK_TYPES = 'br,lightning')"}, + {`create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED "10s" ACTION COOLDOWN WATCH EXACT DURATION='10m') background (task_types 'br,lightning')`, true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m'), BACKGROUND = (TASK_TYPES = 'br,lightning')"}, + {`create resource group x ru_per_sec=1000 QUERY_LIMIT (EXEC_ELAPSED "10s" ACTION COOLDOWN WATCH PLAN DURATION='10m') background (task_types 'br,lightning')`, true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = PLAN DURATION = '10m'), BACKGROUND = (TASK_TYPES = 'br,lightning')"}, + // This case is expected in parser test but not in actual ddl job. + {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s')", true, "CREATE RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s')"}, + {"create resource group x ru_per_sec=1000 QUERY=(EXEC_ELAPSED '10s')", false, ""}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT=EXEC_ELAPSED '10s'", false, ""}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s'", false, ""}, + {"create resource group x ru_per_sec=1000 LIMIT=(EXEC_ELAPSED '10s')", false, ""}, + {"create resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN ACTION KILL)", false, ""}, + + {"alter resource group x cpu ='8c'", false, ""}, + {"alter resource group x region ='us, 3'", false, ""}, + {"alter resource group default priority = high", true, "ALTER RESOURCE GROUP `default` PRIORITY = HIGH"}, + {"alter resource group x cpu='8c', io_read_bandwidth='2GB/s', io_write_bandwidth='200MB/s'", false, ""}, + {"alter resource group x ru_per_sec=1000", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000"}, + {"alter resource group x ru_per_sec=2000, BURSTABLE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 2000, BURSTABLE = TRUE"}, + {"alter resource group x BURSTABLE, ru_per_sec=3000", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 3000"}, + {"alter resource group x BURSTABLE ru_per_sec=4000", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE, RU_PER_SEC = 4000"}, + // This case is expected in parser test but not in actual ddl job. + // Todo: support patch setting(not cover all). + {"alter resource group x BURSTABLE", true, "ALTER RESOURCE GROUP `x` BURSTABLE = TRUE"}, + {"alter resource group x ru_per_sec=200000 BURSTABLE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 200000, BURSTABLE = TRUE"}, + {"alter resource group x followers=0", false, ""}, + {"alter resource group x ru_per_sec=20 priority=MID BURSTABLE", false, ""}, + {"alter resource group x ru_per_sec=20 priority=HIGH BURSTABLE=FALSE", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 20, PRIORITY = HIGH, BURSTABLE = FALSE"}, + + {"alter resource group x QUERY_LIMIT=NULL", true, "ALTER RESOURCE GROUP `x` QUERY_LIMIT = NULL"}, + {"alter resource group x QUERY_LIMIT=()", true, "ALTER RESOURCE GROUP `x` QUERY_LIMIT = NULL"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION DRYRUN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = DRYRUN)"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=()", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = NULL"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10m' ACTION COOLDOWN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10m' ACTION = COOLDOWN)"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=( ACTION KILL EXEC_ELAPSED '10m')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (ACTION = KILL EXEC_ELAPSED = '10m')"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' WATCH SIMILAR DURATION '10m' ACTION COOLDOWN)", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' WATCH = SIMILAR DURATION = '10m' ACTION = COOLDOWN)"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT=(EXEC_ELAPSED '10s' ACTION COOLDOWN WATCH EXACT DURATION '10m')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s' ACTION = COOLDOWN WATCH = EXACT DURATION = '10m')"}, + // This case is expected in parser test but not in actual ddl job. + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s')", true, "ALTER RESOURCE GROUP `x` RU_PER_SEC = 1000, QUERY_LIMIT = (EXEC_ELAPSED = '10s')"}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT EXEC_ELAPSED '10s'", false, ""}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN ACTION KILL)", false, ""}, + {"alter resource group x ru_per_sec=1000 QUERY_LIMIT = (EXEC_ELAPSED '10s' ACTION DRYRUN WATCH SIMILAR DURATION '10m' ACTION COOLDOWN)", false, ""}, + {"alter resource group x background=()", true, "ALTER RESOURCE GROUP `x` BACKGROUND = NULL"}, + {"alter resource group x background NULL", true, "ALTER RESOURCE GROUP `x` BACKGROUND = NULL"}, + {"alter resource group default priority=low background = ( task_types \"ttl\" )", true, "ALTER RESOURCE GROUP `default` PRIORITY = LOW, BACKGROUND = (TASK_TYPES = 'ttl')"}, + {"alter resource group default burstable background ( task_types = 'a,b,c' )", true, "ALTER RESOURCE GROUP `default` BURSTABLE = TRUE, BACKGROUND = (TASK_TYPES = 'a,b,c')"}, + + {"drop resource group x;", true, "DROP RESOURCE GROUP `x`"}, + {"drop resource group DEFAULT;", true, "DROP RESOURCE GROUP `DEFAULT`"}, + {"drop resource group if exists x;", true, "DROP RESOURCE GROUP IF EXISTS `x`"}, + {"drop resource group x,y", false, ""}, + {"drop resource group if exists x,y", false, ""}, + + {"set resource group x;", true, "SET RESOURCE GROUP `x`"}, + {"set resource group ``;", true, "SET RESOURCE GROUP ``"}, + {"set resource group `default`;", true, "SET RESOURCE GROUP `default`"}, + {"set resource group default;", true, "SET RESOURCE GROUP `default`"}, + {"set resource group x y", false, ""}, + + {"CREATE ROLE `RESOURCE`", true, "CREATE ROLE `RESOURCE`@`%`"}, + {"CREATE ROLE RESOURCE", false, ""}, + + // for table stats options + // 1. create table with options + {"CREATE TABLE t (a int) STATS_BUCKETS=1", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1"}, + {"CREATE TABLE t (a int) STATS_BUCKETS='abc'", false, ""}, + {"CREATE TABLE t (a int) STATS_BUCKETS=", false, ""}, + {"CREATE TABLE t (a int) STATS_TOPN=1", true, "CREATE TABLE `t` (`a` INT) STATS_TOPN = 1"}, + {"CREATE TABLE t (a int) STATS_TOPN='abc'", false, ""}, + {"CREATE TABLE t (a int) STATS_AUTO_RECALC=1", true, "CREATE TABLE `t` (`a` INT) STATS_AUTO_RECALC = 1"}, + {"CREATE TABLE t (a int) STATS_AUTO_RECALC='abc'", false, ""}, + {"CREATE TABLE t(a int) STATS_SAMPLE_RATE=0.1", true, "CREATE TABLE `t` (`a` INT) STATS_SAMPLE_RATE = 0.1"}, + {"CREATE TABLE t (a int) STATS_SAMPLE_RATE='abc'", false, ""}, + {"CREATE TABLE t (a int) STATS_COL_CHOICE='all'", true, "CREATE TABLE `t` (`a` INT) STATS_COL_CHOICE = 'all'"}, + {"CREATE TABLE t (a int) STATS_COL_CHOICE='list'", true, "CREATE TABLE `t` (`a` INT) STATS_COL_CHOICE = 'list'"}, + {"CREATE TABLE t (a int) STATS_COL_CHOICE=1", false, ""}, + {"CREATE TABLE t (a int, b int) STATS_COL_LIST='a,b'", true, "CREATE TABLE `t` (`a` INT,`b` INT) STATS_COL_LIST = 'a,b'"}, + {"CREATE TABLE t (a int, b int) STATS_COL_LIST=1", false, ""}, + {"CREATE TABLE t (a int) STATS_BUCKETS=1,STATS_TOPN=1", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1 STATS_TOPN = 1"}, + // 2. create partition table with options + {"CREATE TABLE t (a int) STATS_BUCKETS=1,STATS_TOPN=1 PARTITION BY RANGE (a) (PARTITION p1 VALUES LESS THAN (200))", true, "CREATE TABLE `t` (`a` INT) STATS_BUCKETS = 1 STATS_TOPN = 1 PARTITION BY RANGE (`a`) (PARTITION `p1` VALUES LESS THAN (200))"}, + // 3. alter table with options + {"ALTER TABLE t STATS_OPTIONS='str'", true, "ALTER TABLE `t` STATS_OPTIONS='str'"}, + {"ALTER TABLE t STATS_OPTIONS='str1,str2'", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, + {"ALTER TABLE t STATS_OPTIONS=\"str1,str2\"", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, + {"ALTER TABLE t STATS_OPTIONS 'str1,str2'", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, + {"ALTER TABLE t STATS_OPTIONS \"str1,str2\"", true, "ALTER TABLE `t` STATS_OPTIONS='str1,str2'"}, + {"ALTER TABLE t STATS_OPTIONS=DEFAULT", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, + {"ALTER TABLE t STATS_OPTIONS=default", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, + {"ALTER TABLE t STATS_OPTIONS=DeFaUlT", true, "ALTER TABLE `t` STATS_OPTIONS=DEFAULT"}, + {"ALTER TABLE t STATS_OPTIONS", false, ""}, + + // Restore INSERT_METHOD table option + {"CREATE TABLE t (a int) INSERT_METHOD=FIRST", true, "CREATE TABLE `t` (`a` INT) INSERT_METHOD = FIRST"}, + } + RunTest(t, table, false) +} + +func TestHintError(t *testing.T) { + p := parser.New() + stmt, warns, err := p.Parse("select /*+ tidb_unknown(T1,t2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + require.Len(t, warns, 1) + require.Regexp(t, `Optimizer hint syntax error at line 1 column 23 near "tidb_unknown\(T1,t2\) \*/" $`, warns[0].Error()) + require.Len(t, stmt[0].(*ast.SelectStmt).TableHints, 0) + stmt, warns, err = p.Parse("select /*+ TIDB_INLJ(t1, T2) tidb_unknow(T1,t2, 1) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.Len(t, stmt[0].(*ast.SelectStmt).TableHints, 0) + require.NoError(t, err) + require.Len(t, warns, 1) + require.Regexp(t, `Optimizer hint syntax error at line 1 column 40 near "tidb_unknow\(T1,t2, 1\) \*/" $`, warns[0].Error()) + _, _, err = p.Parse("select c1, c2 from /*+ tidb_unknow(T1,t2) */ t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) // Hints are ignored after the "FROM" keyword! + _, _, err = p.Parse("select1 /*+ TIDB_INLJ(t1, T2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.EqualError(t, err, "line 1 column 7 near \"select1 /*+ TIDB_INLJ(t1, T2) */ c1, c2 from t1, t2 where t1.c1 = t2.c1\" ") + _, _, err = p.Parse("select /*+ TIDB_INLJ(t1, T2) */ c1, c2 fromt t1, t2 where t1.c1 = t2.c1", "", "") + require.EqualError(t, err, "line 1 column 47 near \"t1, t2 where t1.c1 = t2.c1\" ") + _, _, err = p.Parse("SELECT 1 FROM DUAL WHERE 1 IN (SELECT /*+ DEBUG_HINT3 */ 1)", "", "") + require.NoError(t, err) + stmt, _, err = p.Parse("insert into t select /*+ memory_quota(1 MB) */ * from t;", "", "") + require.NoError(t, err) + require.Len(t, stmt[0].(*ast.InsertStmt).TableHints, 0) + require.Len(t, stmt[0].(*ast.InsertStmt).Select.(*ast.SelectStmt).TableHints, 1) + stmt, _, err = p.Parse("insert /*+ memory_quota(1 MB) */ into t select * from t;", "", "") + require.NoError(t, err) + require.Len(t, stmt[0].(*ast.InsertStmt).TableHints, 1) + + _, warns, err = p.Parse("SELECT id FROM tbl WHERE id = 0 FOR UPDATE /*+ xyz */", "", "") + require.NoError(t, err) + require.Len(t, warns, 1) + require.Regexp(t, `near '/\*\+' at line 1$`, warns[0].Error()) + + _, warns, err = p.Parse("create global binding for select /*+ max_execution_time(1) */ 1 using select /*+ max_execution_time(1) */ 1;\n", "", "") + require.NoError(t, err) + require.Len(t, warns, 0) +} + +func TestErrorMsg(t *testing.T) { + p := parser.New() + _, _, err := p.Parse("select1 1", "", "") + require.EqualError(t, err, "line 1 column 7 near \"select1 1\" ") + _, _, err = p.Parse("select 1 from1 dual", "", "") + require.EqualError(t, err, "line 1 column 19 near \"dual\" ") + _, _, err = p.Parse("select * from t1 join t2 from t1.a = t2.a;", "", "") + require.EqualError(t, err, "line 1 column 29 near \"from t1.a = t2.a;\" ") + _, _, err = p.Parse("select * from t1 join t2 one t1.a = t2.a;", "", "") + require.EqualError(t, err, "line 1 column 31 near \"t1.a = t2.a;\" ") + _, _, err = p.Parse("select * from t1 join t2 on t1.a >>> t2.a;", "", "") + require.EqualError(t, err, "line 1 column 36 near \"> t2.a;\" ") + + _, _, err = p.Parse("create table t(f_year year(5))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;", "", "") + require.EqualError(t, err, "[parser:1818]Supports only YEAR or YEAR(4) column") + + _, _, err = p.Parse("select ifnull(a,0) & ifnull(a,0) like '55' ESCAPE '\\\\a' from t;", "", "") + require.EqualError(t, err, "[parser:1210]Incorrect arguments to ESCAPE") + + _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Enclosed by '\\\\b';", "", "") + require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") + + _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Escaped by '\\\\b';", "", "") + require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") + + _, _, err = p.Parse("load data infile 'aaa' into table aaa FIELDS Enclosed by '\\\\b' Escaped by '\\\\b' ;", "", "") + require.EqualError(t, err, "[parser:1083]Field separator argument is not what is expected; check the manual") + + _, _, err = p.Parse("ALTER DATABASE `` CHARACTER SET = ''", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: ''") + + _, _, err = p.Parse("ALTER DATABASE t CHARACTER SET = ''", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: ''") + + _, _, err = p.Parse("ALTER SCHEMA t CHARACTER SET = 'SOME_INVALID_CHARSET'", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: 'SOME_INVALID_CHARSET'") + + _, _, err = p.Parse("ALTER DATABASE t COLLATE = ''", "", "") + require.EqualError(t, err, "[ddl:1273]Unknown collation: ''") + + _, _, err = p.Parse("ALTER SCHEMA t COLLATE = 'SOME_INVALID_COLLATION'", "", "") + require.EqualError(t, err, "[ddl:1273]Unknown collation: 'SOME_INVALID_COLLATION'") + + _, _, err = p.Parse("ALTER DATABASE CHARSET = 'utf8mb4' COLLATE = 'utf8_bin'", "", "") + require.EqualError(t, err, "line 1 column 24 near \"= 'utf8mb4' COLLATE = 'utf8_bin'\" ") + + _, _, err = p.Parse("ALTER DATABASE t ENCRYPTION = ''", "", "") + require.EqualError(t, err, "[parser:1525]Incorrect argument (should be Y or N) value: ''") + + _, _, err = p.Parse("ALTER DATABASE", "", "") + require.EqualError(t, err, "line 1 column 14 near \"\" ") + + _, _, err = p.Parse("ALTER SCHEMA `ANY_DB_NAME`", "", "") + require.EqualError(t, err, "line 1 column 26 near \"\" ") + + _, _, err = p.Parse("alter table t partition by range FIELDS(a)", "", "") + require.EqualError(t, err, "[ddl:1492]For RANGE partitions each partition must be defined") + + _, _, err = p.Parse("alter table t partition by list FIELDS(a)", "", "") + require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") + + _, _, err = p.Parse("alter table t partition by list FIELDS(a)", "", "") + require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") + + _, _, err = p.Parse("alter table t partition by list FIELDS(a,b,c)", "", "") + require.EqualError(t, err, "[ddl:1492]For LIST partitions each partition must be defined") + + _, _, err = p.Parse("alter table t lock = first", "", "") + require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'first'") + + _, _, err = p.Parse("alter table t lock = start", "", "") + require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'start'") + + _, _, err = p.Parse("alter table t lock = commit", "", "") + require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'commit'") + + _, _, err = p.Parse("alter table t lock = binlog", "", "") + require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'binlog'") + + _, _, err = p.Parse("alter table t lock = randomStr123", "", "") + require.EqualError(t, err, "[parser:1801]Unknown LOCK type 'randomStr123'") + + _, _, err = p.Parse("create table t (a longtext unicode)", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") + + _, _, err = p.Parse("create table t (a long byte, b text unicode)", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") + + _, _, err = p.Parse("create table t (a long ascii, b long unicode)", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") + + _, _, err = p.Parse("create table t (a text unicode, b mediumtext ascii, c int)", "", "") + require.EqualError(t, err, "[parser:1115]Unknown character set: 'ucs2'") + + _, _, err = p.Parse("select 1 collate some_unknown_collation", "", "") + require.EqualError(t, err, "[ddl:1273]Unknown collation: 'some_unknown_collation'") +} + +func TestOptimizerHints(t *testing.T) { + p := parser.New() + // Test USE_INDEX + stmt, _, err := p.Parse("select /*+ USE_INDEX(T1,T2), use_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt := stmt[0].(*ast.SelectStmt) + + hints := selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "use_index", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "t2", hints[0].Indexes[0].L) + + require.Equal(t, "use_index", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "t4", hints[1].Indexes[0].L) + + // Test FORCE_INDEX + stmt, _, err = p.Parse("select /*+ FORCE_INDEX(T1,T2), force_index(t3,t4) RESOURCE_GROUP(rg1)*/ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 3) + require.Equal(t, "force_index", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "t2", hints[0].Indexes[0].L) + + require.Equal(t, "force_index", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "t4", hints[1].Indexes[0].L) + + require.Equal(t, "resource_group", hints[2].HintName.L) + require.Equal(t, hints[2].HintData, "rg1") + + // Test IGNORE_INDEX + stmt, _, err = p.Parse("select /*+ IGNORE_INDEX(T1,T2), ignore_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "ignore_index", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "t2", hints[0].Indexes[0].L) + + require.Equal(t, "ignore_index", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "t4", hints[1].Indexes[0].L) + + // Test ORDER_INDEX + stmt, _, err = p.Parse("select /*+ ORDER_INDEX(T1,T2), order_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "order_index", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "t2", hints[0].Indexes[0].L) + + require.Equal(t, "order_index", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "t4", hints[1].Indexes[0].L) + + // Test NO_ORDER_INDEX + stmt, _, err = p.Parse("select /*+ NO_ORDER_INDEX(T1,T2), no_order_index(t3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_order_index", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "t2", hints[0].Indexes[0].L) + + require.Equal(t, "no_order_index", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "t4", hints[1].Indexes[0].L) + + // Test TIDB_SMJ + stmt, _, err = p.Parse("select /*+ TIDB_SMJ(T1,t2), tidb_smj(T3,t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "tidb_smj", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "tidb_smj", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test MERGE_JOIN + stmt, _, err = p.Parse("select /*+ MERGE_JOIN(t1, T2), merge_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "merge_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "merge_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // TEST BROADCAST_JOIN + stmt, _, err = p.Parse("select /*+ BROADCAST_JOIN(t1, T2), broadcast_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "broadcast_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "broadcast_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test TIDB_INLJ + stmt, _, err = p.Parse("select /*+ TIDB_INLJ(t1, T2), tidb_inlj(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "tidb_inlj", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "tidb_inlj", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test INL_JOIN + stmt, _, err = p.Parse("select /*+ INL_JOIN(t1, T2), inl_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "inl_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "inl_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test INL_HASH_JOIN + stmt, _, err = p.Parse("select /*+ INL_HASH_JOIN(t1, T2), inl_hash_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "inl_hash_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "inl_hash_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test INL_MERGE_JOIN + stmt, _, err = p.Parse("select /*+ INL_MERGE_JOIN(t1, T2), inl_merge_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "inl_merge_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "inl_merge_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test TIDB_HJ + stmt, _, err = p.Parse("select /*+ TIDB_HJ(t1, T2), tidb_hj(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "tidb_hj", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "tidb_hj", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test HASH_JOIN + stmt, _, err = p.Parse("select /*+ HASH_JOIN(t1, T2), hash_join(t3, t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "hash_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "hash_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + // Test HASH_JOIN_BUILD and HASH_JOIN_PROBE + stmt, _, err = p.Parse("select /*+ hash_join_build(t1), hash_join_probe(t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "hash_join_build", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + + require.Equal(t, "hash_join_probe", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t4", hints[1].Tables[0].TableName.L) + + // Test HASH_JOIN with SWAP_JOIN_INPUTS/NO_SWAP_JOIN_INPUTS + // t1 for build, t4 for probe + stmt, _, err = p.Parse("select /*+ HASH_JOIN(t1, T2), hash_join(t3, t4), SWAP_JOIN_INPUTS(t1), NO_SWAP_JOIN_INPUTS(t4) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 4) + require.Equal(t, "hash_join", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + + require.Equal(t, "hash_join", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + require.Equal(t, "t4", hints[1].Tables[1].TableName.L) + + require.Equal(t, "swap_join_inputs", hints[2].HintName.L) + require.Len(t, hints[2].Tables, 1) + require.Equal(t, "t1", hints[2].Tables[0].TableName.L) + + require.Equal(t, "no_swap_join_inputs", hints[3].HintName.L) + require.Len(t, hints[3].Tables, 1) + require.Equal(t, "t4", hints[3].Tables[0].TableName.L) + + // Test MAX_EXECUTION_TIME + queries := []string{ + "SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 where t1.c1 = t2.c1", + "SELECT /*+ MAX_EXECUTION_TIME(1000) */ 1", + "SELECT /*+ MAX_EXECUTION_TIME(1000) */ SLEEP(20)", + "SELECT /*+ MAX_EXECUTION_TIME(1000) */ 1 FROM DUAL", + } + for i, query := range queries { + stmt, _, err = p.Parse(query, "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + hints = selectStmt.TableHints + require.Len(t, hints, 1) + require.Equal(t, "max_execution_time", hints[0].HintName.L, "case", i) + require.Equal(t, uint64(1000), hints[0].HintData.(uint64)) + } + + // Test NTH_PLAN + queries = []string{ + "SELECT /*+ NTH_PLAN(10) */ * FROM t1 INNER JOIN t2 where t1.c1 = t2.c1", + "SELECT /*+ NTH_PLAN(10) */ 1", + "SELECT /*+ NTH_PLAN(10) */ SLEEP(20)", + "SELECT /*+ NTH_PLAN(10) */ 1 FROM DUAL", + } + for i, query := range queries { + stmt, _, err = p.Parse(query, "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + hints = selectStmt.TableHints + require.Len(t, hints, 1) + require.Equal(t, "nth_plan", hints[0].HintName.L, "case", i) + require.Equal(t, int64(10), hints[0].HintData.(int64)) + } + + // Test USE_INDEX_MERGE + stmt, _, err = p.Parse("select /*+ USE_INDEX_MERGE(t1, c1), use_index_merge(t2, c1), use_index_merge(t3, c1, primary, c2) */ c1, c2 from t1, t2, t3 where t1.c1 = t2.c1 and t3.c2 = t1.c2", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 3) + require.Equal(t, "use_index_merge", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Len(t, hints[0].Indexes, 1) + require.Equal(t, "c1", hints[0].Indexes[0].L) + + require.Equal(t, "use_index_merge", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t2", hints[1].Tables[0].TableName.L) + require.Len(t, hints[1].Indexes, 1) + require.Equal(t, "c1", hints[1].Indexes[0].L) + + require.Equal(t, "use_index_merge", hints[2].HintName.L) + require.Len(t, hints[2].Tables, 1) + require.Equal(t, "t3", hints[2].Tables[0].TableName.L) + require.Len(t, hints[2].Indexes, 3) + require.Equal(t, "c1", hints[2].Indexes[0].L) + require.Equal(t, "primary", hints[2].Indexes[1].L) + require.Equal(t, "c2", hints[2].Indexes[2].L) + + // Test READ_FROM_STORAGE + stmt, _, err = p.Parse("select /*+ READ_FROM_STORAGE(tiflash[t1, t2], tikv[t3]) */ c1, c2 from t1, t2, t1 t3 where t1.c1 = t2.c1 and t2.c1 = t3.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "read_from_storage", hints[0].HintName.L) + require.Equal(t, "tiflash", hints[0].HintData.(model.CIStr).L) + require.Len(t, hints[0].Tables, 2) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + require.Equal(t, "t2", hints[0].Tables[1].TableName.L) + require.Equal(t, "read_from_storage", hints[1].HintName.L) + require.Equal(t, "tikv", hints[1].HintData.(model.CIStr).L) + require.Len(t, hints[1].Tables, 1) + require.Equal(t, "t3", hints[1].Tables[0].TableName.L) + + // Test USE_TOJA + stmt, _, err = p.Parse("select /*+ USE_TOJA(true), use_toja(false) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "use_toja", hints[0].HintName.L) + require.True(t, hints[0].HintData.(bool)) + + require.Equal(t, "use_toja", hints[1].HintName.L) + require.False(t, hints[1].HintData.(bool)) + + // Test IGNORE_PLAN_CACHE + stmt, _, err = p.Parse("select /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) + require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) + + stmt, _, err = p.Parse("delete /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ from t where a = 1", "", "") + require.NoError(t, err) + deleteStmt := stmt[0].(*ast.DeleteStmt) + hints = deleteStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) + require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) + + stmt, _, err = p.Parse("update /*+ IGNORE_PLAN_CACHE(), ignore_plan_cache() */ t set a = 1 where a = 10", "", "") + require.NoError(t, err) + updateStmt := stmt[0].(*ast.UpdateStmt) + hints = updateStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "ignore_plan_cache", hints[0].HintName.L) + require.Equal(t, "ignore_plan_cache", hints[1].HintName.L) + + // Test USE_CASCADES + stmt, _, err = p.Parse("select /*+ USE_CASCADES(true), use_cascades(false) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "use_cascades", hints[0].HintName.L) + require.True(t, hints[0].HintData.(bool)) + + require.Equal(t, "use_cascades", hints[1].HintName.L) + require.False(t, hints[1].HintData.(bool)) + + // Test USE_PLAN_CACHE + stmt, _, err = p.Parse("select /*+ USE_PLAN_CACHE(), use_plan_cache() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "use_plan_cache", hints[0].HintName.L) + require.Equal(t, "use_plan_cache", hints[1].HintName.L) + + // Test QUERY_TYPE + stmt, _, err = p.Parse("select /*+ QUERY_TYPE(OLAP), query_type(OLTP) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "query_type", hints[0].HintName.L) + require.Equal(t, "olap", hints[0].HintData.(model.CIStr).L) + require.Equal(t, "query_type", hints[1].HintName.L) + require.Equal(t, "oltp", hints[1].HintData.(model.CIStr).L) + + // Test MEMORY_QUOTA + stmt, _, err = p.Parse("select /*+ MEMORY_QUOTA(1 MB), memory_quota(1 GB) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "memory_quota", hints[0].HintName.L) + require.Equal(t, int64(1024*1024), hints[0].HintData.(int64)) + require.Equal(t, "memory_quota", hints[1].HintName.L) + require.Equal(t, int64(1024*1024*1024), hints[1].HintData.(int64)) + + _, _, err = p.Parse("select /*+ MEMORY_QUOTA(18446744073709551612 MB), memory_quota(8689934592 GB) */ 1", "", "") + require.NoError(t, err) + + // Test HASH_AGG + stmt, _, err = p.Parse("select /*+ HASH_AGG(), hash_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "hash_agg", hints[0].HintName.L) + require.Equal(t, "hash_agg", hints[1].HintName.L) + + // Test MPPAgg + stmt, _, err = p.Parse("select /*+ MPP_1PHASE_AGG(), mpp_1phase_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "mpp_1phase_agg", hints[0].HintName.L) + require.Equal(t, "mpp_1phase_agg", hints[1].HintName.L) + + stmt, _, err = p.Parse("select /*+ MPP_2PHASE_AGG(), mpp_2phase_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "mpp_2phase_agg", hints[0].HintName.L) + require.Equal(t, "mpp_2phase_agg", hints[1].HintName.L) + + // Test ShuffleJoin + stmt, _, err = p.Parse("select /*+ SHUFFLE_JOIN(t1, t2), shuffle_join(t1, t2) */ * from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "shuffle_join", hints[0].HintName.L) + require.Equal(t, "shuffle_join", hints[1].HintName.L) + + // Test STREAM_AGG + stmt, _, err = p.Parse("select /*+ STREAM_AGG(), stream_agg() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "stream_agg", hints[0].HintName.L) + require.Equal(t, "stream_agg", hints[1].HintName.L) + + // Test AGG_TO_COP + stmt, _, err = p.Parse("select /*+ AGG_TO_COP(), agg_to_cop() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "agg_to_cop", hints[0].HintName.L) + require.Equal(t, "agg_to_cop", hints[1].HintName.L) + + // Test NO_INDEX_MERGE + stmt, _, err = p.Parse("select /*+ NO_INDEX_MERGE(), no_index_merge() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_index_merge", hints[0].HintName.L) + require.Equal(t, "no_index_merge", hints[1].HintName.L) + + // Test READ_CONSISTENT_REPLICA + stmt, _, err = p.Parse("select /*+ READ_CONSISTENT_REPLICA(), read_consistent_replica() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "read_consistent_replica", hints[0].HintName.L) + require.Equal(t, "read_consistent_replica", hints[1].HintName.L) + + // Test LIMIT_TO_COP + stmt, _, err = p.Parse("select /*+ LIMIT_TO_COP(), limit_to_cop() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "limit_to_cop", hints[0].HintName.L) + require.Equal(t, "limit_to_cop", hints[1].HintName.L) + + // Test CTE MERGE + stmt, _, err = p.Parse("with cte(x) as (select * from t1) select /*+ MERGE(), merge() */ * from cte;", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "merge", hints[0].HintName.L) + require.Equal(t, "merge", hints[1].HintName.L) + + // Test STRAIGHT_JOIN + stmt, _, err = p.Parse("select /*+ STRAIGHT_JOIN(), straight_join() */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "straight_join", hints[0].HintName.L) + require.Equal(t, "straight_join", hints[1].HintName.L) + + // Test LEADING + stmt, _, err = p.Parse("select /*+ LEADING(T1), LEADING(t2, t3), LEADING(T4, t5, t6) */ c1, c2 from t1, t2 where t1.c1 = t2.c1", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 3) + require.Equal(t, "leading", hints[0].HintName.L) + require.Len(t, hints[0].Tables, 1) + require.Equal(t, "t1", hints[0].Tables[0].TableName.L) + + require.Equal(t, "leading", hints[1].HintName.L) + require.Len(t, hints[1].Tables, 2) + require.Equal(t, "t2", hints[1].Tables[0].TableName.L) + require.Equal(t, "t3", hints[1].Tables[1].TableName.L) + + require.Equal(t, "leading", hints[2].HintName.L) + require.Len(t, hints[2].Tables, 3) + require.Equal(t, "t4", hints[2].Tables[0].TableName.L) + require.Equal(t, "t5", hints[2].Tables[1].TableName.L) + require.Equal(t, "t6", hints[2].Tables[2].TableName.L) + + // Test NO_HASH_JOIN + stmt, _, err = p.Parse("select /*+ NO_HASH_JOIN(t1, t2), NO_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_hash_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + require.Equal(t, hints[0].Tables[1].TableName.L, "t2") + + require.Equal(t, "no_hash_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test NO_MERGE_JOIN + stmt, _, err = p.Parse("select /*+ NO_MERGE_JOIN(t1), NO_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_merge_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "no_merge_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test INDEX_JOIN + stmt, _, err = p.Parse("select /*+ INDEX_JOIN(t1), INDEX_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "index_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "index_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test NO_INDEX_JOIN + stmt, _, err = p.Parse("select /*+ NO_INDEX_JOIN(t1), NO_INDEX_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_index_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "no_index_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test INDEX_HASH_JOIN + stmt, _, err = p.Parse("select /*+ INDEX_HASH_JOIN(t1), INDEX_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "index_hash_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "index_hash_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test NO_INDEX_HASH_JOIN + stmt, _, err = p.Parse("select /*+ NO_INDEX_HASH_JOIN(t1), NO_INDEX_HASH_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_index_hash_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "no_index_hash_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test INDEX_MERGE_JOIN + stmt, _, err = p.Parse("select /*+ INDEX_MERGE_JOIN(t1), INDEX_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "index_merge_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "index_merge_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") + + // Test NO_INDEX_MERGE_JOIN + stmt, _, err = p.Parse("select /*+ NO_INDEX_MERGE_JOIN(t1), NO_INDEX_MERGE_JOIN(t3) */ * from t1, t2, t3", "", "") + require.NoError(t, err) + selectStmt = stmt[0].(*ast.SelectStmt) + + hints = selectStmt.TableHints + require.Len(t, hints, 2) + require.Equal(t, "no_index_merge_join", hints[0].HintName.L) + require.Equal(t, hints[0].Tables[0].TableName.L, "t1") + + require.Equal(t, "no_index_merge_join", hints[1].HintName.L) + require.Equal(t, hints[1].Tables[0].TableName.L, "t3") +} + +func TestType(t *testing.T) { + table := []testCase{ + // for time fsp + {"CREATE TABLE t( c1 TIME(2), c2 DATETIME(2), c3 TIMESTAMP(2) );", true, "CREATE TABLE `t` (`c1` TIME(2),`c2` DATETIME(2),`c3` TIMESTAMP(2))"}, + + // for hexadecimal + {"select x'0a', X'11', 0x11", true, "SELECT x'0a',x'11',x'11'"}, + {"select x'13181C76734725455A'", true, "SELECT x'13181c76734725455a'"}, + {"select x'0xaa'", false, ""}, + {"select 0X11", false, ""}, + {"select 0x4920616D2061206C6F6E672068657820737472696E67", true, "SELECT x'4920616d2061206c6f6e672068657820737472696e67'"}, + + // for bit + {"select 0b01, 0b0, b'11', B'11'", true, "SELECT b'1',b'0',b'11',b'11'"}, + // 0B01 and 0b21 are identifiers, the following two statement could parse. + // {"select 0B01", false, ""}, + // {"select 0b21", false, ""}, + + // for enum and set type + {"create table t (c1 enum('a', 'b'), c2 set('a', 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, + {"create table t (c1 enum('a ', 'b\t'), c2 set('a ', 'b\t'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b\t'),`c2` SET('a','b\t'))"}, + {"create table t (c1 enum('a', 'b') binary, c2 set('a', 'b') binary)", true, "CREATE TABLE `t` (`c1` ENUM('a','b') BINARY,`c2` SET('a','b') BINARY)"}, + {"create table t (c1 enum(0x61, 'b'), c2 set(0x61, 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, + {"create table t (c1 enum(0b01100001, 'b'), c2 set(0b01100001, 'b'))", true, "CREATE TABLE `t` (`c1` ENUM('a','b'),`c2` SET('a','b'))"}, + {"create table t (c1 enum)", false, ""}, + {"create table t (c1 set)", false, ""}, + + // for blob and text field length + {"create table t (c1 blob(1024), c2 text(1024))", true, "CREATE TABLE `t` (`c1` BLOB(1024),`c2` TEXT(1024))"}, + + // for year + {"create table t (y year(4), y1 year)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, + {"create table t (y year(4) unsigned zerofill zerofill, y1 year signed unsigned zerofill)", true, "CREATE TABLE `t` (`y` YEAR(4),`y1` YEAR)"}, + + // for national + {"create table t (c1 national char(2), c2 national varchar(2))", true, "CREATE TABLE `t` (`c1` CHAR(2),`c2` VARCHAR(2))"}, + + // for json type + {`create table t (a JSON);`, true, "CREATE TABLE `t` (`a` JSON)"}, + } + RunTest(t, table, false) +} + +func TestPrivilege(t *testing.T) { + table := []testCase{ + // for create user + {`CREATE USER 'ttt' REQUIRE X509;`, true, "CREATE USER `ttt`@`%` REQUIRE X509"}, + {`CREATE USER 'ttt' REQUIRE SSL;`, true, "CREATE USER `ttt`@`%` REQUIRE SSL"}, + {`CREATE USER 'ttt' REQUIRE NONE;`, true, "CREATE USER `ttt`@`%` REQUIRE NONE"}, + {`CREATE USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';`, true, "CREATE USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, + {`CREATE USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' CIPHER 'EDH-RSA-DES-CBC3-SHA' SUBJECT '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com';`, true, "CREATE USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA' AND SUBJECT '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com'"}, + {`CREATE USER 'ttt' REQUIRE SAN 'DNS:mysql-user, URI:spiffe://example.org/myservice'`, true, "CREATE USER `ttt`@`%` REQUIRE SAN 'DNS:mysql-user, URI:spiffe://example.org/myservice'"}, + {`CREATE USER 'ttt' WITH MAX_QUERIES_PER_HOUR 2;`, true, "CREATE USER `ttt`@`%` WITH MAX_QUERIES_PER_HOUR 2"}, + {`CREATE USER 'ttt'@'localhost' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;`, true, "CREATE USER `ttt`@`localhost` REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, + {`CREATE USER 'u1'@'%' IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK ;`, true, "CREATE USER `u1`@`%` IDENTIFIED WITH 'mysql_native_password' AS '' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, + {`CREATE USER 'test'`, true, "CREATE USER `test`@`%`"}, + {`CREATE USER test`, true, "CREATE USER `test`@`%`"}, + {"CREATE USER `test`", true, "CREATE USER `test`@`%`"}, + {"CREATE USER test-user", false, ""}, + {"CREATE USER test.user", false, ""}, + {"CREATE USER 'test-user'", true, "CREATE USER `test-user`@`%`"}, + {"CREATE USER `test-user`", true, "CREATE USER `test-user`@`%`"}, + {"CREATE USER test.user", false, ""}, + {"CREATE USER 'test.user'", true, "CREATE USER `test.user`@`%`"}, + {"CREATE USER `test.user`", true, "CREATE USER `test.user`@`%`"}, + {"CREATE USER uesr1@LOCALhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@localhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER uesr1@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@localhost", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER uesr1@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER 'uesr1'@`localhost`", true, "CREATE USER `uesr1`@`localhost`"}, + {"CREATE USER `uesr1`@'localhost'", true, "CREATE USER `uesr1`@`localhost`"}, + {"create user 'test@localhost' password expire;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE"}, + {"create user 'test@localhost' password expire never;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE NEVER"}, + {"create user 'test@localhost' password expire default;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE DEFAULT"}, + {"create user 'test@localhost' password expire interval 3 day;", true, "CREATE USER `test@localhost`@`%` PASSWORD EXPIRE INTERVAL 3 DAY"}, + {"create user 'test@localhost' identified by 'password' failed_login_attempts 3 password_lock_time 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 3"}, + {"create user 'test@localhost' identified by 'password' failed_login_attempts 3 password_lock_time unbounded;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME UNBOUNDED"}, + {"create user 'test@localhost' identified by 'password' failed_login_attempts 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' FAILED_LOGIN_ATTEMPTS 3"}, + {"create user 'test@localhost' identified by 'password' password_lock_time 3;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' PASSWORD_LOCK_TIME 3"}, + {"create user 'test@localhost' identified by 'password' password_lock_time unbounded;", true, "CREATE USER `test@localhost`@`%` IDENTIFIED BY 'password' PASSWORD_LOCK_TIME UNBOUNDED"}, + {"CREATE USER 'sha_test'@'localhost' IDENTIFIED WITH 'caching_sha2_password' BY 'sha_test'", true, "CREATE USER `sha_test`@`localhost` IDENTIFIED WITH 'caching_sha2_password' BY 'sha_test'"}, + {"CREATE USER 'sha_test3'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS 0x24412430303524255B03496C662C1055127B3B654A2F04207D01485276703644704B76303247474564416A516662346C5868646D32764C6B514F43585A473779565947514F34", true, "CREATE USER `sha_test3`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'"}, + {"CREATE USER 'sha_test4'@'localhost' IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'", true, "CREATE USER `sha_test4`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$%[\x03Ilf,\x10U\x12{;eJ/\x04 }\x01HRvp6DpKv02GGEdAjQfb4lXhdm2vLkQOCXZG7yVYGQO4'"}, + {"CREATE USER `user@pingcap.com`@'localhost' IDENTIFIED WITH 'tidb_auth_token' REQUIRE token_issuer 'issuer-abc' ATTRIBUTE '{\"email\": \"user@pingcap.com\"}'", true, "CREATE USER `user@pingcap.com`@`localhost` IDENTIFIED WITH 'tidb_auth_token' REQUIRE TOKEN_ISSUER 'issuer-abc' ATTRIBUTE '{\"email\": \"user@pingcap.com\"}'"}, + {"CREATE USER 'nopwd_native'@'localhost' IDENTIFIED WITH 'mysql_native_password'", true, "CREATE USER `nopwd_native`@`localhost` IDENTIFIED WITH 'mysql_native_password'"}, + {"CREATE USER 'nopwd_sha'@'localhost' IDENTIFIED WITH 'caching_sha2_password'", true, "CREATE USER `nopwd_sha`@`localhost` IDENTIFIED WITH 'caching_sha2_password'"}, + {"CREATE ROLE `test-role`, `role1`@'localhost'", true, "CREATE ROLE `test-role`@`%`, `role1`@`localhost`"}, + {"CREATE ROLE `test-role`", true, "CREATE ROLE `test-role`@`%`"}, + {"CREATE ROLE role1", true, "CREATE ROLE `role1`@`%`"}, + {"CREATE ROLE `role1`@'localhost'", true, "CREATE ROLE `role1`@`localhost`"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password'"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password by 'new-password'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password' BY 'new-password'"}, + {"create user 'bug19354014user'@'%' identified WITH mysql_native_password as 'hashstring'", true, "CREATE USER `bug19354014user`@`%` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, + {`CREATE USER IF NOT EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER IF NOT EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, + {`CREATE USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "CREATE USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, + {`CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY 'hashstring' RESOURCE GROUP rg1`, true, "CREATE USER `root`@`127.0.0.1` IDENTIFIED BY 'hashstring' RESOURCE GROUP `rg1`"}, + {`ALTER USER IF EXISTS 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password'"}, + {`ALTER USER 'root'@'localhost' RESOURCE GROUP rg2`, true, "ALTER USER `root`@`localhost` RESOURCE GROUP `rg2`"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, + {`ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-password', 'root'@'127.0.0.1' IDENTIFIED BY PASSWORD 'hashstring'`, true, "ALTER USER `root`@`localhost` IDENTIFIED BY 'new-password', `root`@`127.0.0.1` IDENTIFIED WITH 'mysql_native_password' AS 'hashstring'"}, + {`ALTER USER USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER USER() IDENTIFIED BY 'new-password'"}, + {`ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'`, true, "ALTER USER IF EXISTS USER() IDENTIFIED BY 'new-password'"}, + {"alter user 'test@localhost' password expire;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE"}, + {"alter user 'test@localhost' password expire never;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE NEVER"}, + {"alter user 'test@localhost' password expire default;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE DEFAULT"}, + {"alter user 'test@localhost' password expire interval 3 day;", true, "ALTER USER `test@localhost`@`%` PASSWORD EXPIRE INTERVAL 3 DAY"}, + {"ALTER USER 'ttt' REQUIRE X509;", true, "ALTER USER `ttt`@`%` REQUIRE X509"}, + {"ALTER USER 'ttt' REQUIRE SSL;", true, "ALTER USER `ttt`@`%` REQUIRE SSL"}, + {"ALTER USER 'ttt' REQUIRE NONE;", true, "ALTER USER `ttt`@`%` REQUIRE NONE"}, + {"ALTER USER 'ttt' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';", true, "ALTER USER `ttt`@`%` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, + {"ALTER USER 'ttt' WITH MAX_QUERIES_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_QUERIES_PER_HOUR 2"}, + {"ALTER USER 'ttt' WITH MAX_UPDATES_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_UPDATES_PER_HOUR 2"}, + {"ALTER USER 'ttt' WITH MAX_CONNECTIONS_PER_HOUR 2;", true, "ALTER USER `ttt`@`%` WITH MAX_CONNECTIONS_PER_HOUR 2"}, + {"ALTER USER 'ttt' WITH MAX_USER_CONNECTIONS 2;", true, "ALTER USER `ttt`@`%` WITH MAX_USER_CONNECTIONS 2"}, + {"ALTER USER 'ttt'@'localhost' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;", true, "ALTER USER `ttt`@`localhost` REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1 MAX_UPDATES_PER_HOUR 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK"}, + {`DROP USER 'root'@'localhost', 'root1'@'localhost'`, true, "DROP USER `root`@`localhost`, `root1`@`localhost`"}, + {`DROP USER IF EXISTS 'root'@'localhost'`, true, "DROP USER IF EXISTS `root`@`localhost`"}, + {`RENAME USER 'root'@'localhost' TO 'root'@'%'`, true, "RENAME USER `root`@`localhost` TO `root`@`%`"}, + {`RENAME USER 'fred' TO 'barry'`, true, "RENAME USER `fred`@`%` TO `barry`@`%`"}, + {`RENAME USER u1 to u2, u3 to u4`, true, "RENAME USER `u1`@`%` TO `u2`@`%`, `u3`@`%` TO `u4`@`%`"}, + {`DROP ROLE 'role'@'localhost', 'role1'@'localhost'`, true, "DROP ROLE `role`@`localhost`, `role1`@`localhost`"}, + {`DROP ROLE 'administrator', 'developer';`, true, "DROP ROLE `administrator`@`%`, `developer`@`%`"}, + {`DROP ROLE IF EXISTS 'role'@'localhost'`, true, "DROP ROLE IF EXISTS `role`@`localhost`"}, + + // for grant statement + {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE X509;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE X509"}, + {"GRANT ALL ON db1.* TO 'jeffrey'@'LOCALhost' REQUIRE SSL;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE SSL"}, + {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE NONE;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE NONE"}, + {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA';", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` REQUIRE ISSUER '/C=SE/ST=Stockholm/L=Stockholm/O=MySQL/CN=CA/emailAddress=ca@example.com' AND CIPHER 'EDH-RSA-DES-CBC3-SHA'"}, + {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost';", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost`"}, + {"GRANT ALL ON TABLE db1.* TO 'jeffrey'@'localhost';", true, "GRANT ALL ON TABLE `db1`.* TO `jeffrey`@`localhost`"}, + {"GRANT ALL ON db1.* TO 'jeffrey'@'localhost' WITH GRANT OPTION;", true, "GRANT ALL ON `db1`.* TO `jeffrey`@`localhost` WITH GRANT OPTION"}, + {"GRANT SELECT ON db2.invoice TO 'jeffrey'@'localhost';", true, "GRANT SELECT ON `db2`.`invoice` TO `jeffrey`@`localhost`"}, + {"GRANT ALL ON *.* TO 'someuser'@'somehost';", true, "GRANT ALL ON *.* TO `someuser`@`somehost`"}, + {"GRANT ALL ON *.* TO 'SOMEuser'@'SOMEhost';", true, "GRANT ALL ON *.* TO `SOMEuser`@`somehost`"}, + {"GRANT SELECT, INSERT ON *.* TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON *.* TO `someuser`@`somehost`"}, + {"GRANT ALL ON mydb.* TO 'someuser'@'somehost';", true, "GRANT ALL ON `mydb`.* TO `someuser`@`somehost`"}, + {"GRANT SELECT, INSERT ON mydb.* TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON `mydb`.* TO `someuser`@`somehost`"}, + {"GRANT ALL ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT ALL ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, + {"GRANT SELECT, INSERT ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT SELECT, INSERT ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, + {"GRANT SELECT (col1), INSERT (col1,col2) ON mydb.mytbl TO 'someuser'@'somehost';", true, "GRANT SELECT (`col1`), INSERT (`col1`,`col2`) ON `mydb`.`mytbl` TO `someuser`@`somehost`"}, + {"grant all privileges on zabbix.* to 'zabbix'@'localhost' identified by 'password';", true, "GRANT ALL ON `zabbix`.* TO `zabbix`@`localhost` IDENTIFIED BY 'password'"}, + {"GRANT SELECT ON test.* to 'test'", true, "GRANT SELECT ON `test`.* TO `test`@`%`"}, // For issue 2654. + {"grant PROCESS,usage, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'xxxxxxxxxx'@'%' identified by password 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'", true, "GRANT PROCESS, USAGE, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO `xxxxxxxxxx`@`%` IDENTIFIED WITH 'mysql_native_password' AS 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'"}, + {"/* rds internal mark */ GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, RELOAD, PROCESS, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER on *.* to 'root2'@'%' identified by password '*sdsadsdsadssadsadsadsadsada' with grant option", true, "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, RELOAD, PROCESS, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER ON *.* TO `root2`@`%` IDENTIFIED WITH 'mysql_native_password' AS '*sdsadsdsadssadsadsadsadsada' WITH GRANT OPTION"}, + {"GRANT 'role1', 'role2' TO 'user1'@'LOCalhost', 'user2'@'LOcalhost';", true, "GRANT `role1`@`%`, `role2`@`%` TO `user1`@`localhost`, `user2`@`localhost`"}, + {"GRANT 'u1' TO 'u1';", true, "GRANT `u1`@`%` TO `u1`@`%`"}, + {"GRANT 'app_read'@'%','app_write'@'%' TO 'rw_user1'@'localhost'", true, "GRANT `app_read`@`%`, `app_write`@`%` TO `rw_user1`@`localhost`"}, + {"GRANT 'app_developer' TO 'dev1'@'localhost';", true, "GRANT `app_developer`@`%` TO `dev1`@`localhost`"}, + {"GRANT SHUTDOWN ON *.* TO 'dev1'@'localhost';", true, "GRANT SHUTDOWN ON *.* TO `dev1`@`localhost`"}, + {"GRANT CONFIG ON *.* TO 'dev1'@'localhost';", true, "GRANT CONFIG ON *.* TO `dev1`@`localhost`"}, + {"GRANT CREATE ON *.* TO 'dev1'@'localhost';", true, "GRANT CREATE ON *.* TO `dev1`@`localhost`"}, + {"GRANT CREATE TABLESPACE ON *.* TO 'dev1'@'localhost';", true, "GRANT CREATE TABLESPACE ON *.* TO `dev1`@`localhost`"}, + {"GRANT EXECUTE ON FUNCTION db1.anomaly_score TO 'user1'@'domain-or-ip-address1'", true, "GRANT EXECUTE ON FUNCTION `db1`.`anomaly_score` TO `user1`@`domain-or-ip-address1`"}, + {"GRANT EXECUTE ON PROCEDURE mydb.myproc TO 'someuser'@'somehost'", true, "GRANT EXECUTE ON PROCEDURE `mydb`.`myproc` TO `someuser`@`somehost`"}, + {"GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN ON *.* TO 'root'@'localhost'", true, "GRANT APPLICATION_PASSWORD_ADMIN, AUDIT_ADMIN ON *.* TO `root`@`localhost`"}, + {"GRANT LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'localhost'", true, "GRANT LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO `root`@`localhost`"}, + {"GRANT PROXY ON 'localuser'@'localhost' TO 'externaluser'@'somehost'", true, "GRANT PROXY ON `localuser`@`localhost` TO `externaluser`@`somehost`"}, + {"GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION", true, "GRANT PROXY ON ``@`` TO `root`@`localhost` WITH GRANT OPTION"}, + {"GRANT PROXY ON 'proxied_user' TO 'proxy_user1', 'proxy_user2'", true, "GRANT PROXY ON `proxied_user`@`%` TO `proxy_user1`@`%`, `proxy_user2`@`%`"}, + {"grant grant option on *.* to u1", true, "GRANT GRANT OPTION ON *.* TO `u1`@`%`"}, // not typical syntax, but supported + + // for revoke statement + {"REVOKE ALL ON db1.* FROM 'jeffrey'@'LOCalhost';", true, "REVOKE ALL ON `db1`.* FROM `jeffrey`@`localhost`"}, + {"REVOKE SELECT ON db2.invoice FROM 'jeffrey'@'localhost';", true, "REVOKE SELECT ON `db2`.`invoice` FROM `jeffrey`@`localhost`"}, + {"REVOKE ALL ON *.* FROM 'someuser'@'somehost';", true, "REVOKE ALL ON *.* FROM `someuser`@`somehost`"}, + {"REVOKE SELECT, INSERT ON *.* FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON *.* FROM `someuser`@`somehost`"}, + {"REVOKE ALL ON mydb.* FROM 'someuser'@'somehost';", true, "REVOKE ALL ON `mydb`.* FROM `someuser`@`somehost`"}, + {"REVOKE SELECT, INSERT ON mydb.* FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON `mydb`.* FROM `someuser`@`somehost`"}, + {"REVOKE ALL ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE ALL ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, + {"REVOKE SELECT, INSERT ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE SELECT, INSERT ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, + {"REVOKE SELECT (col1), INSERT (col1,col2) ON mydb.mytbl FROM 'someuser'@'somehost';", true, "REVOKE SELECT (`col1`), INSERT (`col1`,`col2`) ON `mydb`.`mytbl` FROM `someuser`@`somehost`"}, + {"REVOKE all privileges on zabbix.* FROM 'zabbix'@'localhost' identified by 'password';", true, "REVOKE ALL ON `zabbix`.* FROM `zabbix`@`localhost` IDENTIFIED BY 'password'"}, + {"REVOKE 'role1', 'role2' FROM 'user1'@'localhost', 'user2'@'localhost';", true, "REVOKE `role1`@`%`, `role2`@`%` FROM `user1`@`localhost`, `user2`@`localhost`"}, + {"REVOKE SHUTDOWN ON *.* FROM 'dev1'@'localhost';", true, "REVOKE SHUTDOWN ON *.* FROM `dev1`@`localhost`"}, + {"REVOKE CONFIG ON *.* FROM 'dev1'@'localhost';", true, "REVOKE CONFIG ON *.* FROM `dev1`@`localhost`"}, + {"REVOKE EXECUTE ON FUNCTION db.func FROM 'user'@'localhost'", true, "REVOKE EXECUTE ON FUNCTION `db`.`func` FROM `user`@`localhost`"}, + {"REVOKE EXECUTE ON PROCEDURE db.func FROM 'user'@'localhost'", true, "REVOKE EXECUTE ON PROCEDURE `db`.`func` FROM `user`@`localhost`"}, + {"REVOKE APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN ON *.* FROM 'root'@'localhost'", true, "REVOKE APPLICATION_PASSWORD_ADMIN, AUDIT_ADMIN ON *.* FROM `root`@`localhost`"}, + {"revoke all privileges, grant option from u1", true, "REVOKE ALL, GRANT OPTION ON *.* FROM `u1`@`%`"}, // special case syntax + {"revoke all privileges, grant option from u1, u2, u3", true, "REVOKE ALL, GRANT OPTION ON *.* FROM `u1`@`%`, `u2`@`%`, `u3`@`%`"}, // special case syntax + } + RunTest(t, table, false) +} + +func TestComment(t *testing.T) { + table := []testCase{ + {"create table t (c int comment 'comment')", true, "CREATE TABLE `t` (`c` INT COMMENT 'comment')"}, + {"create table t (c int) comment = 'comment'", true, "CREATE TABLE `t` (`c` INT) COMMENT = 'comment'"}, + {"create table t (c int) comment 'comment'", true, "CREATE TABLE `t` (`c` INT) COMMENT = 'comment'"}, + {"create table t (c int) comment comment", false, ""}, + {"create table t (comment text)", true, "CREATE TABLE `t` (`comment` TEXT)"}, + {"START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */", true, "START TRANSACTION"}, + // for comment in query + {"/*comment*/ /*comment*/ select c /* this is a comment */ from t;", true, "SELECT `c` FROM `t`"}, + // for unclosed comment + {"delete from t where a = 7 or 1=1/*' and b = 'p'", false, ""}, + + {"create table t (ssl int)", false, ""}, + {"create table t (require int)", false, ""}, + {"create table t (account int)", true, "CREATE TABLE `t` (`account` INT)"}, + {"create table t (expire int)", true, "CREATE TABLE `t` (`expire` INT)"}, + {"create table t (cipher int)", true, "CREATE TABLE `t` (`cipher` INT)"}, + {"create table t (issuer int)", true, "CREATE TABLE `t` (`issuer` INT)"}, + {"create table t (never int)", true, "CREATE TABLE `t` (`never` INT)"}, + {"create table t (subject int)", true, "CREATE TABLE `t` (`subject` INT)"}, + {"create table t (x509 int)", true, "CREATE TABLE `t` (`x509` INT)"}, + + // COMMENT/ATTRIBUTE in CREATE/ALTER USER + {"create user commentUser COMMENT '123456' '{\"name\": \"Tom\", \"age\", 19}", false, ""}, + {"alter user commentUser COMMENT '123456' '{\"name\": \"Tom\", \"age\", 19}", false, ""}, + {"create user commentUser COMMENT '123456'", true, "CREATE USER `commentUser`@`%` COMMENT '123456'"}, + {"alter user commentUser COMMENT '123456'", true, "ALTER USER `commentUser`@`%` COMMENT '123456'"}, + {"create user commentUser ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'", true, "CREATE USER `commentUser`@`%` ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'"}, + {"alter user commentUser ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'", true, "ALTER USER `commentUser`@`%` ATTRIBUTE '{\"name\": \"Tom\", \"age\", 19}'"}, + } + RunTest(t, table, false) +} + +func TestParserErrMsg(t *testing.T) { + commentMsgCases := []testErrMsgCase{ + {"delete from t where a = 7 or 1=1/*' and b = 'p'", errors.New("near '/*' and b = 'p'' at line 1")}, + {"delete from t where a = 7 or\n 1=1/*' and b = 'p'", errors.New("near '/*' and b = 'p'' at line 2")}, + {"select 1/*", errors.New("near '/*' at line 1")}, + {"select 1/* comment */", nil}, + } + funcCallMsgCases := []testErrMsgCase{ + {"select a.b()", nil}, + {"SELECT foo.bar('baz');", nil}, + } + RunErrMsgTest(t, commentMsgCases) + RunErrMsgTest(t, funcCallMsgCases) +} + +type subqueryChecker struct { + text string + t *testing.T +} + +// Enter implements ast.Visitor interface. +func (sc *subqueryChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { + if expr, ok := inNode.(*ast.SubqueryExpr); ok { + require.Equal(sc.t, sc.text, expr.Query.Text()) + return inNode, true + } + return inNode, false +} + +// Leave implements ast.Visitor interface. +func (sc *subqueryChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { + return inNode, true +} + +func TestSubquery(t *testing.T) { + table := []testCase{ + // for compare subquery + {"SELECT 1 > (select 1)", true, "SELECT 1>(SELECT 1)"}, + {"SELECT 1 > ANY (select 1)", true, "SELECT 1>ANY (SELECT 1)"}, + {"SELECT 1 > ALL (select 1)", true, "SELECT 1>ALL (SELECT 1)"}, + {"SELECT 1 > SOME (select 1)", true, "SELECT 1>ANY (SELECT 1)"}, + + // for exists subquery + {"SELECT EXISTS select 1", false, ""}, + {"SELECT EXISTS (select 1)", true, "SELECT EXISTS (SELECT 1)"}, + {"SELECT + EXISTS (select 1)", true, "SELECT +EXISTS (SELECT 1)"}, + {"SELECT - EXISTS (select 1)", true, "SELECT -EXISTS (SELECT 1)"}, + {"SELECT NOT EXISTS (select 1)", true, "SELECT NOT EXISTS (SELECT 1)"}, + {"SELECT + NOT EXISTS (select 1)", false, ""}, + {"SELECT - NOT EXISTS (select 1)", false, ""}, + {"SELECT * FROM t where t.a in (select a from t limit 1, 10)", true, "SELECT * FROM `t` WHERE `t`.`a` IN (SELECT `a` FROM `t` LIMIT 1,10)"}, + {"SELECT * FROM t where t.a in ((select a from t limit 1, 10))", true, "SELECT * FROM `t` WHERE `t`.`a` IN ((SELECT `a` FROM `t` LIMIT 1,10))"}, + {"SELECT * FROM t where t.a in ((select a from t limit 1, 10), 1)", true, "SELECT * FROM `t` WHERE `t`.`a` IN ((SELECT `a` FROM `t` LIMIT 1,10),1)"}, + {"select * from ((select a from t) t1 join t t2) join t3", true, "SELECT * FROM ((SELECT `a` FROM `t`) AS `t1` JOIN `t` AS `t2`) JOIN `t3`"}, + {"SELECT t1.a AS a FROM ((SELECT a FROM t) AS t1)", true, "SELECT `t1`.`a` AS `a` FROM (SELECT `a` FROM `t`) AS `t1`"}, + {"select count(*) from (select a, b from x1 union all select a, b from x3 union all (select x1.a, x3.b from (select * from x3 union all select * from x2) x3 left join x1 on x3.a = x1.b))", true, "SELECT COUNT(1) FROM (SELECT `a`,`b` FROM `x1` UNION ALL SELECT `a`,`b` FROM `x3` UNION ALL (SELECT `x1`.`a`,`x3`.`b` FROM (SELECT * FROM `x3` UNION ALL SELECT * FROM `x2`) AS `x3` LEFT JOIN `x1` ON `x3`.`a`=`x1`.`b`))"}, + {"(SELECT 1 a,3 b) UNION (SELECT 2,1) ORDER BY (SELECT 2)", true, "(SELECT 1 AS `a`,3 AS `b`) UNION (SELECT 2,1) ORDER BY (SELECT 2)"}, + {"((select * from t1)) union (select * from t1)", true, "(SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)"}, + {"(((select * from t1))) union (select * from t1)", true, "(SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)"}, + {"select * from (((select * from t1)) union (select * from t1) union (select * from t1)) a", true, "SELECT * FROM ((SELECT * FROM `t1`) UNION (SELECT * FROM `t1`) UNION (SELECT * FROM `t1`)) AS `a`"}, + {"SELECT COUNT(*) FROM plan_executions WHERE (EXISTS((SELECT * FROM triggers WHERE plan_executions.trigger_id=triggers.id AND triggers.type='CRON')))", true, "SELECT COUNT(1) FROM `plan_executions` WHERE (EXISTS (SELECT * FROM `triggers` WHERE `plan_executions`.`trigger_id`=`triggers`.`id` AND `triggers`.`type`=_UTF8MB4'CRON'))"}, + {"select exists((select 1));", true, "SELECT EXISTS (SELECT 1)"}, + {"select * from ((SELECT 1 a,3 b) UNION (SELECT 2,1) ORDER BY (SELECT 2)) t order by a,b", true, "SELECT * FROM ((SELECT 1 AS `a`,3 AS `b`) UNION (SELECT 2,1) ORDER BY (SELECT 2)) AS `t` ORDER BY `a`,`b`"}, + {"select (select * from t1 where a != t.a union all (select * from t2 where a != t.a) order by a limit 1) from t1 t", true, "SELECT (SELECT * FROM `t1` WHERE `a`!=`t`.`a` UNION ALL (SELECT * FROM `t2` WHERE `a`!=`t`.`a`) ORDER BY `a` LIMIT 1) FROM `t1` AS `t`"}, + {"(WITH v0 AS (SELECT TRUE) (SELECT 'abc' EXCEPT (SELECT TRUE)))", true, "WITH `v0` AS (SELECT TRUE) (SELECT _UTF8MB4'abc' EXCEPT (SELECT TRUE))"}, + } + RunTest(t, table, false) + + tests := []struct { + input string + text string + }{ + {"SELECT 1 > (select 1)", "select 1"}, + {"SELECT 1 > (select 1 union select 2)", "select 1 union select 2"}, + } + p := parser.New() + for _, tbl := range tests { + stmt, err := p.ParseOneStmt(tbl.input, "", "") + require.NoError(t, err) + stmt.Accept(&subqueryChecker{ + text: tbl.text, + t: t, + }) + } +} + +func TestSetOperator(t *testing.T) { + table := []testCase{ + // union and union all + {"select c1 from t1 union select c2 from t2", true, "SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, + {"select c1 from t1 union (select c2 from t2)", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`)"}, + {"select c1 from t1 union (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, + {"select c1 from t1 union select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2` ORDER BY `c2`"}, + {"select c1 from t1 union (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1"}, + {"select c1 from t1 union (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"select c1 from t1 union (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) union distinct select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2`"}, + {"(select c1 from t1) union distinctrow select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2`"}, + {"(select c1 from t1) union all select c2 from t2", true, "(SELECT `c1` FROM `t1`) UNION ALL SELECT `c2` FROM `t2`"}, + {"(select c1 from t1) union distinct all select c2 from t2", false, ""}, + {"(select c1 from t1) union distinctrow all select c2 from t2", false, ""}, + {"(select c1 from t1) union (select c2 from t2) order by c1 union select c3 from t3", false, ""}, + {"(select c1 from t1) union (select c2 from t2) limit 1 union select c3 from t3", false, ""}, + {"(select c1 from t1) union select c2 from t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"select (select 1 union select 1) as a", true, "SELECT (SELECT 1 UNION SELECT 1) AS `a`"}, + {"select * from (select 1 union select 2) as a", true, "SELECT * FROM (SELECT 1 UNION SELECT 2) AS `a`"}, + {"insert into t select c1 from t1 union select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, + {"insert into t (c) select c1 from t1 union select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` UNION SELECT `c2` FROM `t2`"}, + {"select 2 as a from dual union select 1 as b from dual order by a", true, "SELECT 2 AS `a` UNION SELECT 1 AS `b` ORDER BY `a`"}, + {"table t1 union table t2", true, "TABLE `t1` UNION TABLE `t2`"}, + {"table t1 union (table t2)", true, "TABLE `t1` UNION (TABLE `t2`)"}, + {"table t1 union select * from t2", true, "TABLE `t1` UNION SELECT * FROM `t2`"}, + {"select * from t1 union table t2", true, "SELECT * FROM `t1` UNION TABLE `t2`"}, + {"table t1 union (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` UNION (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"select c1 from t1 union (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` UNION (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) union table t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION TABLE `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"(table t1) union select c2 from t2 union (table t3) order by c1 limit 1", true, "(TABLE `t1`) UNION SELECT `c2` FROM `t2` UNION (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, + {"values row(1,-2,3), row(5,7,9) union values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"values row(1,-2,3), row(5,7,9) union (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION (VALUES ROW(1,-2,3), ROW(5,7,9))"}, + {"values row(1,-2,3), row(5,7,9) union select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION SELECT * FROM `t`"}, + {"values row(1,-2,3), row(5,7,9) union table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) UNION TABLE `t`"}, + {"select * from t union values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"table t union values row(1,-2,3), row(5,7,9)", true, "TABLE `t` UNION VALUES ROW(1,-2,3), ROW(5,7,9)"}, + // except + {"select c1 from t1 except select c2 from t2", true, "SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, + {"select c1 from t1 except (select c2 from t2)", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`)"}, + {"select c1 from t1 except (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, + {"select c1 from t1 except select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2` ORDER BY `c2`"}, + {"select c1 from t1 except (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) LIMIT 1"}, + {"select c1 from t1 except (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"select c1 from t1 except (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) except (select c2 from t2) order by c1 except select c3 from t3", false, ""}, + {"(select c1 from t1) except (select c2 from t2) limit 1 except select c3 from t3", false, ""}, + {"(select c1 from t1) except select c2 from t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"select (select 1 except select 1) as a", true, "SELECT (SELECT 1 EXCEPT SELECT 1) AS `a`"}, + {"select * from (select 1 except select 2) as a", true, "SELECT * FROM (SELECT 1 EXCEPT SELECT 2) AS `a`"}, + {"insert into t select c1 from t1 except select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, + {"insert into t (c) select c1 from t1 except select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` EXCEPT SELECT `c2` FROM `t2`"}, + {"select 2 as a from dual except select 1 as b from dual order by a", true, "SELECT 2 AS `a` EXCEPT SELECT 1 AS `b` ORDER BY `a`"}, + {"table t1 except table t2", true, "TABLE `t1` EXCEPT TABLE `t2`"}, + {"table t1 except (table t2)", true, "TABLE `t1` EXCEPT (TABLE `t2`)"}, + {"table t1 except select * from t2", true, "TABLE `t1` EXCEPT SELECT * FROM `t2`"}, + {"select * from t1 except table t2", true, "SELECT * FROM `t1` EXCEPT TABLE `t2`"}, + {"table t1 except (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` EXCEPT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"select c1 from t1 except (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` EXCEPT (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) except table t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT TABLE `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"(table t1) except select c2 from t2 except (table t3) order by c1 limit 1", true, "(TABLE `t1`) EXCEPT SELECT `c2` FROM `t2` EXCEPT (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, + {"values row(1,-2,3), row(5,7,9) except values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"values row(1,-2,3), row(5,7,9) except (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT (VALUES ROW(1,-2,3), ROW(5,7,9))"}, + {"values row(1,-2,3), row(5,7,9) except select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT SELECT * FROM `t`"}, + {"values row(1,-2,3), row(5,7,9) except table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) EXCEPT TABLE `t`"}, + {"select * from t except values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"table t except values row(1,-2,3), row(5,7,9)", true, "TABLE `t` EXCEPT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + // intersect + {"select c1 from t1 intersect select c2 from t2", true, "SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, + {"select c1 from t1 intersect (select c2 from t2)", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`)"}, + {"select c1 from t1 intersect (select c2 from t2) order by c1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1`"}, + {"select c1 from t1 intersect select c2 from t2 order by c2", true, "SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2` ORDER BY `c2`"}, + {"select c1 from t1 intersect (select c2 from t2) limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) LIMIT 1"}, + {"select c1 from t1 intersect (select c2 from t2) limit 1, 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"select c1 from t1 intersect (select c2 from t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) intersect (select c2 from t2) order by c1 intersect select c3 from t3", false, ""}, + {"(select c1 from t1) intersect (select c2 from t2) limit 1 intersect select c3 from t3", false, ""}, + {"(select c1 from t1) intersect select c2 from t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT SELECT `c2` FROM `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"select (select 1 intersect select 1) as a", true, "SELECT (SELECT 1 INTERSECT SELECT 1) AS `a`"}, + {"select * from (select 1 intersect select 2) as a", true, "SELECT * FROM (SELECT 1 INTERSECT SELECT 2) AS `a`"}, + {"insert into t select c1 from t1 intersect select c2 from t2", true, "INSERT INTO `t` SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, + {"insert into t (c) select c1 from t1 intersect select c2 from t2", true, "INSERT INTO `t` (`c`) SELECT `c1` FROM `t1` INTERSECT SELECT `c2` FROM `t2`"}, + {"select 2 as a from dual intersect select 1 as b from dual order by a", true, "SELECT 2 AS `a` INTERSECT SELECT 1 AS `b` ORDER BY `a`"}, + {"table t1 intersect table t2", true, "TABLE `t1` INTERSECT TABLE `t2`"}, + {"table t1 intersect (table t2)", true, "TABLE `t1` INTERSECT (TABLE `t2`)"}, + {"table t1 intersect select * from t2", true, "TABLE `t1` INTERSECT SELECT * FROM `t2`"}, + {"select * from t1 intersect table t2", true, "SELECT * FROM `t1` INTERSECT TABLE `t2`"}, + {"table t1 intersect (select c2 from t2) order by c1 limit 1", true, "TABLE `t1` INTERSECT (SELECT `c2` FROM `t2`) ORDER BY `c1` LIMIT 1"}, + {"select c1 from t1 intersect (table t2) order by c1 limit 1", true, "SELECT `c1` FROM `t1` INTERSECT (TABLE `t2`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) intersect table t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT TABLE `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"(table t1) intersect select c2 from t2 intersect (table t3) order by c1 limit 1", true, "(TABLE `t1`) INTERSECT SELECT `c2` FROM `t2` INTERSECT (TABLE `t3`) ORDER BY `c1` LIMIT 1"}, + {"values row(1,-2,3), row(5,7,9) intersect values row(1,-2,3), row(5,7,9)", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"values row(1,-2,3), row(5,7,9) intersect (values row(1,-2,3), row(5,7,9))", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT (VALUES ROW(1,-2,3), ROW(5,7,9))"}, + {"values row(1,-2,3), row(5,7,9) intersect select * from t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT SELECT * FROM `t`"}, + {"values row(1,-2,3), row(5,7,9) intersect table t", true, "VALUES ROW(1,-2,3), ROW(5,7,9) INTERSECT TABLE `t`"}, + {"select * from t intersect values row(1,-2,3), row(5,7,9)", true, "SELECT * FROM `t` INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + {"table t intersect values row(1,-2,3), row(5,7,9)", true, "TABLE `t` INTERSECT VALUES ROW(1,-2,3), ROW(5,7,9)"}, + // mixture of union, except and intersect + {"(select c1 from t1) intersect select c2 from t2 union (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) union all select c2 from t2 except (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION ALL SELECT `c2` FROM `t2` EXCEPT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) except select c2 from t2 intersect (select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2` INTERSECT (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"select 1 union distinct select 1 except select 1 intersect select 1", true, "SELECT 1 UNION SELECT 1 EXCEPT SELECT 1 INTERSECT SELECT 1"}, + // mixture of union, except and intersect with parentheses + {"(select c1 from t1) intersect all (select c2 from t2 union (select c3 from t3)) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) INTERSECT ALL (SELECT `c2` FROM `t2` UNION (SELECT `c3` FROM `t3`)) ORDER BY `c1` LIMIT 1"}, + {"(select c1 from t1) union all (select c2 from t2 except select c3 from t3) order by c1 limit 1", true, "(SELECT `c1` FROM `t1`) UNION ALL (SELECT `c2` FROM `t2` EXCEPT SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"((select c1 from t1) except select c2 from t2) intersect all (select c3 from t3) order by c1 limit 1", true, "((SELECT `c1` FROM `t1`) EXCEPT SELECT `c2` FROM `t2`) INTERSECT ALL (SELECT `c3` FROM `t3`) ORDER BY `c1` LIMIT 1"}, + {"select 1 union distinct (select 1 except all select 1 intersect select 1)", true, "SELECT 1 UNION (SELECT 1 EXCEPT ALL SELECT 1 INTERSECT SELECT 1)"}, + } + RunTest(t, table, false) +} + +func checkOrderBy(t *testing.T, s ast.Node, hasOrderBy []bool, i int) int { + switch x := s.(type) { + case *ast.SelectStmt: + require.Equal(t, hasOrderBy[i], x.OrderBy != nil) + return i + 1 + case *ast.SetOprSelectList: + for _, sel := range x.Selects { + i = checkOrderBy(t, sel, hasOrderBy, i) + } + return i + } + return i +} + +func TestUnionOrderBy(t *testing.T) { + p := parser.New() + p.EnableWindowFunc(false) + + tests := []struct { + src string + hasOrderBy []bool + }{ + {"select 2 as a from dual union select 1 as b from dual order by a", []bool{false, false, true}}, + {"select 2 as a from dual union (select 1 as b from dual order by a)", []bool{false, true, false}}, + {"(select 2 as a from dual order by a) union select 1 as b from dual order by a", []bool{true, false, true}}, + {"select 1 a, 2 b from dual order by a", []bool{true}}, + {"select 1 a, 2 b from dual", []bool{false}}, + } + + for _, tbl := range tests { + stmt, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err) + us, ok := stmt[0].(*ast.SetOprStmt) + if ok { + var i int + for _, s := range us.SelectList.Selects { + i = checkOrderBy(t, s, tbl.hasOrderBy, i) + } + require.Equal(t, tbl.hasOrderBy[i], us.OrderBy != nil) + } + ss, ok := stmt[0].(*ast.SelectStmt) + if ok { + require.Equal(t, tbl.hasOrderBy[0], ss.OrderBy != nil) + } + } +} + +func TestLikeEscape(t *testing.T) { + table := []testCase{ + // for like escape + {`select "abc_" like "abc\\_" escape ''`, true, "SELECT _UTF8MB4'abc_' LIKE _UTF8MB4'abc\\_'"}, + {`select "abc_" like "abc\\_" escape '\\'`, true, "SELECT _UTF8MB4'abc_' LIKE _UTF8MB4'abc\\_'"}, + {`select "abc_" like "abc\\_" escape '||'`, false, ""}, + {`select "abc" like "escape" escape '+'`, true, "SELECT _UTF8MB4'abc' LIKE _UTF8MB4'escape' ESCAPE '+'"}, + {"select '''_' like '''_' escape ''''", true, "SELECT _UTF8MB4'''_' LIKE _UTF8MB4'''_' ESCAPE ''''"}, + } + + RunTest(t, table, false) +} + +func TestLockUnlockTables(t *testing.T) { + table := []testCase{ + {`UNLOCK TABLES;`, true, "UNLOCK TABLES"}, + {`LOCK TABLES t1 READ;`, true, "LOCK TABLES `t1` READ"}, + {`LOCK TABLES t1 READ LOCAL;`, true, "LOCK TABLES `t1` READ LOCAL"}, + {`show table status like 't'`, true, "SHOW TABLE STATUS LIKE _UTF8MB4't'"}, + {`LOCK TABLES t2 WRITE`, true, "LOCK TABLES `t2` WRITE"}, + {`LOCK TABLES t2 WRITE LOCAL;`, true, "LOCK TABLES `t2` WRITE LOCAL"}, + {`LOCK TABLES t1 WRITE, t2 READ;`, true, "LOCK TABLES `t1` WRITE, `t2` READ"}, + {`LOCK TABLES t1 WRITE LOCAL, t2 READ LOCAL;`, true, "LOCK TABLES `t1` WRITE LOCAL, `t2` READ LOCAL"}, + + // for unlock table and lock table + {`UNLOCK TABLE;`, true, "UNLOCK TABLES"}, + {`LOCK TABLE t1 READ;`, true, "LOCK TABLES `t1` READ"}, + {`LOCK TABLE t1 READ LOCAL;`, true, "LOCK TABLES `t1` READ LOCAL"}, + {`show table status like 't'`, true, "SHOW TABLE STATUS LIKE _UTF8MB4't'"}, + {`LOCK TABLE t2 WRITE`, true, "LOCK TABLES `t2` WRITE"}, + {`LOCK TABLE t2 WRITE LOCAL;`, true, "LOCK TABLES `t2` WRITE LOCAL"}, + {`LOCK TABLE t1 WRITE, t2 READ;`, true, "LOCK TABLES `t1` WRITE, `t2` READ"}, + + // for cleanup table lock. + {"ADMIN CLEANUP TABLE LOCK", false, ""}, + {"ADMIN CLEANUP TABLE LOCK t", true, "ADMIN CLEANUP TABLE LOCK `t`"}, + {"ADMIN CLEANUP TABLE LOCK t1,t2", true, "ADMIN CLEANUP TABLE LOCK `t1`, `t2`"}, + + // For alter table read only/write. + {"ALTER TABLE t READ ONLY", true, "ALTER TABLE `t` READ ONLY"}, + {"ALTER TABLE t READ WRITE", true, "ALTER TABLE `t` READ WRITE"}, + } + + RunTest(t, table, false) +} + +func TestWithRollup(t *testing.T) { + table := []testCase{ + {`select * from t group by a, b rollup`, false, ""}, + {`select * from t group by a, b with rollup`, true, "SELECT * FROM `t` GROUP BY `a`,`b` WITH ROLLUP"}, + // should be ERROR 1241 (21000): Operand should contain 1 column(s) in runtime. + {`select * from t group by (a, b) with rollup`, true, "SELECT * FROM `t` GROUP BY ROW(`a`,`b`) WITH ROLLUP"}, + {`select * from t group by (a+b) with rollup`, true, "SELECT * FROM `t` GROUP BY (`a`+`b`) WITH ROLLUP"}, + } + RunTest(t, table, false) +} + +func TestIndexHint(t *testing.T) { + table := []testCase{ + {`select * from t use index (primary)`, true, "SELECT * FROM `t` USE INDEX (`primary`)"}, + {"select * from t use index (`primary`)", true, "SELECT * FROM `t` USE INDEX (`primary`)"}, + {`select * from t use index ();`, true, "SELECT * FROM `t` USE INDEX ()"}, + {`select * from t use index (idx);`, true, "SELECT * FROM `t` USE INDEX (`idx`)"}, + {`select * from t use index (idx1, idx2);`, true, "SELECT * FROM `t` USE INDEX (`idx1`, `idx2`)"}, + {`select * from t ignore key (idx1)`, true, "SELECT * FROM `t` IGNORE INDEX (`idx1`)"}, + {`select * from t force index for join (idx1)`, true, "SELECT * FROM `t` FORCE INDEX FOR JOIN (`idx1`)"}, + {`select * from t use index for order by (idx1)`, true, "SELECT * FROM `t` USE INDEX FOR ORDER BY (`idx1`)"}, + {`select * from t force index for group by (idx1)`, true, "SELECT * FROM `t` FORCE INDEX FOR GROUP BY (`idx1`)"}, + {`select * from t use index for group by (idx1) use index for order by (idx2), t2`, true, "SELECT * FROM (`t` USE INDEX FOR GROUP BY (`idx1`) USE INDEX FOR ORDER BY (`idx2`)) JOIN `t2`"}, + } + + RunTest(t, table, false) +} + +func TestPriority(t *testing.T) { + table := []testCase{ + {`select high_priority * from t`, true, "SELECT HIGH_PRIORITY * FROM `t`"}, + {`select low_priority * from t`, true, "SELECT LOW_PRIORITY * FROM `t`"}, + {`select delayed * from t`, true, "SELECT DELAYED * FROM `t`"}, + {`insert high_priority into t values (1)`, true, "INSERT HIGH_PRIORITY INTO `t` VALUES (1)"}, + {`insert LOW_PRIORITY into t values (1)`, true, "INSERT LOW_PRIORITY INTO `t` VALUES (1)"}, + {`insert delayed into t values (1)`, true, "INSERT DELAYED INTO `t` VALUES (1)"}, + {`update low_priority t set a = 2`, true, "UPDATE LOW_PRIORITY `t` SET `a`=2"}, + {`update high_priority t set a = 2`, true, "UPDATE HIGH_PRIORITY `t` SET `a`=2"}, + {`update delayed t set a = 2`, true, "UPDATE DELAYED `t` SET `a`=2"}, + {`delete low_priority from t where a = 2`, true, "DELETE LOW_PRIORITY FROM `t` WHERE `a`=2"}, + {`delete high_priority from t where a = 2`, true, "DELETE HIGH_PRIORITY FROM `t` WHERE `a`=2"}, + {`delete delayed from t where a = 2`, true, "DELETE DELAYED FROM `t` WHERE `a`=2"}, + {`replace high_priority into t values (1)`, true, "REPLACE HIGH_PRIORITY INTO `t` VALUES (1)"}, + {`replace LOW_PRIORITY into t values (1)`, true, "REPLACE LOW_PRIORITY INTO `t` VALUES (1)"}, + {`replace delayed into t values (1)`, true, "REPLACE DELAYED INTO `t` VALUES (1)"}, + } + RunTest(t, table, false) + + p := parser.New() + stmt, _, err := p.Parse("select HIGH_PRIORITY * from t", "", "") + require.NoError(t, err) + sel := stmt[0].(*ast.SelectStmt) + require.Equal(t, mysql.HighPriority, sel.SelectStmtOpts.Priority) +} + +func TestSQLResult(t *testing.T) { + table := []testCase{ + {`select SQL_BIG_RESULT c1 from t group by c1`, true, "SELECT SQL_BIG_RESULT `c1` FROM `t` GROUP BY `c1`"}, + {`select SQL_SMALL_RESULT c1 from t group by c1`, true, "SELECT SQL_SMALL_RESULT `c1` FROM `t` GROUP BY `c1`"}, + {`select SQL_BUFFER_RESULT * from t`, true, "SELECT SQL_BUFFER_RESULT * FROM `t`"}, + {`select sql_small_result sql_big_result sql_buffer_result 1`, true, "SELECT SQL_SMALL_RESULT SQL_BIG_RESULT SQL_BUFFER_RESULT 1"}, + {`select STRAIGHT_JOIN SQL_SMALL_RESULT * from t`, true, "SELECT SQL_SMALL_RESULT STRAIGHT_JOIN * FROM `t`"}, + {`select SQL_CALC_FOUND_ROWS DISTINCT * from t`, true, "SELECT SQL_CALC_FOUND_ROWS DISTINCT * FROM `t`"}, + } + + RunTest(t, table, false) +} + +func TestSQLNoCache(t *testing.T) { + table := []testCase{ + {`select SQL_NO_CACHE * from t`, false, ""}, + {`select SQL_CACHE * from t`, true, "SELECT * FROM `t`"}, + {`select * from t`, true, "SELECT * FROM `t`"}, + } + + p := parser.New() + for _, tbl := range table { + stmt, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err) + + sel := stmt[0].(*ast.SelectStmt) + require.Equal(t, tbl.ok, sel.SelectStmtOpts.SQLCache) + } +} + +func TestEscape(t *testing.T) { + table := []testCase{ + {`select """;`, false, ""}, + {`select """";`, true, "SELECT _UTF8MB4'\"'"}, + {`select "汉字";`, true, "SELECT _UTF8MB4'汉字'"}, + {`select 'abc"def';`, true, "SELECT _UTF8MB4'abc\"def'"}, + {`select 'a\r\n';`, true, "SELECT _UTF8MB4'a\r\n'"}, + {`select "\a\r\n"`, true, "SELECT _UTF8MB4'a\r\n'"}, + {`select "\xFF"`, true, "SELECT _UTF8MB4'xFF'"}, + } + RunTest(t, table, false) +} + +func TestExplain(t *testing.T) { + table := []testCase{ + {"explain select c1 from t1", true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1`"}, + {"explain delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id;", true, "EXPLAIN FORMAT = 'row' DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, + {"explain insert into t values (1), (2), (3)", true, "EXPLAIN FORMAT = 'row' INSERT INTO `t` VALUES (1),(2),(3)"}, + {"explain replace into foo values (1 || 2)", true, "EXPLAIN FORMAT = 'row' REPLACE INTO `foo` VALUES (1 OR 2)"}, + {"explain update t set id = id + 1 order by id desc;", true, "EXPLAIN FORMAT = 'row' UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, + {"explain select c1 from t1 union (select c2 from t2) limit 1, 1", true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {`explain format = "row" select c1 from t1 union (select c2 from t2) limit 1, 1`, true, "EXPLAIN FORMAT = 'row' SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"explain format = 'brief' select * from t", true, "EXPLAIN FORMAT = 'brief' SELECT * FROM `t`"}, + {"DESC SCHE.TABL", true, "DESC `SCHE`.`TABL`"}, + {"DESC SCHE.TABL COLUM", true, "DESC `SCHE`.`TABL` `COLUM`"}, + {"DESCRIBE SCHE.TABL COLUM", true, "DESC `SCHE`.`TABL` `COLUM`"}, + {"EXPLAIN ANALYZE SELECT 1", true, "EXPLAIN ANALYZE SELECT 1"}, + {"EXPLAIN ANALYZE format=VERBOSE SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'VERBOSE' SELECT 1"}, + {"EXPLAIN ANALYZE format=TRUE_CARD_COST SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'TRUE_CARD_COST' SELECT 1"}, + {"EXPLAIN ANALYZE format='VERBOSE' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'VERBOSE' SELECT 1"}, + {"EXPLAIN ANALYZE format='TRUE_CARD_COST' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'TRUE_CARD_COST' SELECT 1"}, + {"EXPLAIN FORMAT = 'dot' SELECT 1", true, "EXPLAIN FORMAT = 'dot' SELECT 1"}, + {"EXPLAIN FORMAT = DOT SELECT 1", true, "EXPLAIN FORMAT = 'DOT' SELECT 1"}, + {"EXPLAIN FORMAT = 'row' SELECT 1", true, "EXPLAIN FORMAT = 'row' SELECT 1"}, + {"EXPLAIN FORMAT = 'ROW' SELECT 1", true, "EXPLAIN FORMAT = 'ROW' SELECT 1"}, + {"EXPLAIN FORMAT = 'BRIEF' SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, + {"EXPLAIN FORMAT = BRIEF SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, + {"EXPLAIN FORMAT = 'verbose' SELECT 1", true, "EXPLAIN FORMAT = 'verbose' SELECT 1"}, + {"EXPLAIN FORMAT = 'VERBOSE' SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, + {"EXPLAIN FORMAT = VERBOSE SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, + {"EXPLAIN SELECT 1", true, "EXPLAIN FORMAT = 'row' SELECT 1"}, + {"EXPLAIN FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 1"}, + {"EXPLAIN FOR connection 42", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 42"}, + {"EXPLAIN FORMAT = 'dot' FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'dot' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = DOT FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'DOT' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = 'row' FOR connection 1", true, "EXPLAIN FORMAT = 'row' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = ROW FOR connection 1", true, "EXPLAIN FORMAT = 'ROW' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = TRADITIONAL FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'TRADITIONAL' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = TRADITIONAL SELECT 1", true, "EXPLAIN FORMAT = 'TRADITIONAL' SELECT 1"}, + {"EXPLAIN FORMAT = BRIEF SELECT 1", true, "EXPLAIN FORMAT = 'BRIEF' SELECT 1"}, + {"EXPLAIN FORMAT = 'brief' SELECT 1", true, "EXPLAIN FORMAT = 'brief' SELECT 1"}, + {"EXPLAIN FORMAT = DOT SELECT 1", true, "EXPLAIN FORMAT = 'DOT' SELECT 1"}, + {"EXPLAIN FORMAT = 'dot' SELECT 1", true, "EXPLAIN FORMAT = 'dot' SELECT 1"}, + {"EXPLAIN FORMAT = VERBOSE SELECT 1", true, "EXPLAIN FORMAT = 'VERBOSE' SELECT 1"}, + {"EXPLAIN FORMAT = 'verbose' SELECT 1", true, "EXPLAIN FORMAT = 'verbose' SELECT 1"}, + {"EXPLAIN FORMAT = JSON FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'JSON' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = JSON SELECT 1", true, "EXPLAIN FORMAT = 'JSON' SELECT 1"}, + {"EXPLAIN FORMAT = 'hint' SELECT 1", true, "EXPLAIN FORMAT = 'hint' SELECT 1"}, + {"EXPLAIN ANALYZE FORMAT = 'verbose' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'verbose' SELECT 1"}, + {"EXPLAIN ANALYZE FORMAT = 'binary' SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'binary' SELECT 1"}, + {"EXPLAIN ALTER TABLE t1 ADD INDEX (a)", true, "EXPLAIN FORMAT = 'row' ALTER TABLE `t1` ADD INDEX(`a`)"}, + {"EXPLAIN ALTER TABLE t1 ADD a varchar(255)", true, "EXPLAIN FORMAT = 'row' ALTER TABLE `t1` ADD COLUMN `a` VARCHAR(255)"}, + {"EXPLAIN FORMAT = TIDB_JSON FOR CONNECTION 1", true, "EXPLAIN FORMAT = 'TIDB_JSON' FOR CONNECTION 1"}, + {"EXPLAIN FORMAT = tidb_json SELECT 1", true, "EXPLAIN FORMAT = 'tidb_json' SELECT 1"}, + {"EXPLAIN ANALYZE FORMAT = tidb_json SELECT 1", true, "EXPLAIN ANALYZE FORMAT = 'tidb_json' SELECT 1"}, + } + RunTest(t, table, false) +} + +func TestPrepare(t *testing.T) { + table := []testCase{ + {"PREPARE pname FROM 'SELECT ?'", true, "PREPARE `pname` FROM 'SELECT ?'"}, + {"PREPARE pname FROM @test", true, "PREPARE `pname` FROM @`test`"}, + {"PREPARE `` FROM @test", true, "PREPARE `` FROM @`test`"}, + } + RunTest(t, table, false) +} + +func TestDeallocate(t *testing.T) { + table := []testCase{ + {"DEALLOCATE PREPARE test", true, "DEALLOCATE PREPARE `test`"}, + {"DEALLOCATE PREPARE ``", true, "DEALLOCATE PREPARE ``"}, + } + RunTest(t, table, false) +} + +func TestExecute(t *testing.T) { + table := []testCase{ + {"EXECUTE test", true, "EXECUTE `test`"}, + {"EXECUTE test USING @var1,@var2", true, "EXECUTE `test` USING @`var1`,@`var2`"}, + {"EXECUTE `` USING @var1,@var2", true, "EXECUTE `` USING @`var1`,@`var2`"}, + } + RunTest(t, table, false) +} + +func TestTrace(t *testing.T) { + table := []testCase{ + {"trace begin", true, "TRACE START TRANSACTION"}, + {"trace commit", true, "TRACE COMMIT"}, + {"trace rollback", true, "TRACE ROLLBACK"}, + {"trace set a = 1", true, "TRACE SET @@SESSION.`a`=1"}, + {"trace select c1 from t1", true, "TRACE SELECT `c1` FROM `t1`"}, + {"trace delete t1, t2 from t1 inner join t2 inner join t3 where t1.id=t2.id and t2.id=t3.id;", true, "TRACE DELETE `t1`,`t2` FROM (`t1` JOIN `t2`) JOIN `t3` WHERE `t1`.`id`=`t2`.`id` AND `t2`.`id`=`t3`.`id`"}, + {"trace insert into t values (1), (2), (3)", true, "TRACE INSERT INTO `t` VALUES (1),(2),(3)"}, + {"trace replace into foo values (1 || 2)", true, "TRACE REPLACE INTO `foo` VALUES (1 OR 2)"}, + {"trace update t set id = id + 1 order by id desc;", true, "TRACE UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, + {"trace select c1 from t1 union (select c2 from t2) limit 1, 1", true, "TRACE SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"trace format = 'row' select c1 from t1 union (select c2 from t2) limit 1, 1", true, "TRACE SELECT `c1` FROM `t1` UNION (SELECT `c2` FROM `t2`) LIMIT 1,1"}, + {"trace format = 'json' update t set id = id + 1 order by id desc;", true, "TRACE FORMAT = 'json' UPDATE `t` SET `id`=`id`+1 ORDER BY `id` DESC"}, + {"trace plan select c1 from t1", true, "TRACE PLAN SELECT `c1` FROM `t1`"}, + {"trace plan target = 'estimation' select c1 from t1", true, "TRACE PLAN TARGET = 'estimation' SELECT `c1` FROM `t1`"}, + {"trace plan target = 'arandomstring' select c1 from t1", true, "TRACE PLAN TARGET = 'arandomstring' SELECT `c1` FROM `t1`"}, + } + RunTest(t, table, false) +} + +func TestBinding(t *testing.T) { + table := []testCase{ + {"create global binding for select * from t using select * from t use index(a)", true, "CREATE GLOBAL BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"create session binding for select * from t using select * from t use index(a)", true, "CREATE SESSION BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"drop global binding for select * from t", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t`"}, + {"drop session binding for select * from t", true, "DROP SESSION BINDING FOR SELECT * FROM `t`"}, + {"drop global binding for select * from t using select * from t use index(a)", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"drop session binding for select * from t using select * from t use index(a)", true, "DROP SESSION BINDING FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"show global bindings", true, "SHOW GLOBAL BINDINGS"}, + {"show session bindings", true, "SHOW SESSION BINDINGS"}, + {"set binding enabled for select * from t", true, "SET BINDING ENABLED FOR SELECT * FROM `t`"}, + {"set binding enabled for select * from t using select * from t use index(a)", true, "SET BINDING ENABLED FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"set binding disabled for select * from t", true, "SET BINDING DISABLED FOR SELECT * FROM `t`"}, + {"set binding disabled for select * from t using select * from t use index(a)", true, "SET BINDING DISABLED FOR SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`)"}, + {"create global binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "CREATE GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, + {"create session binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "CREATE SESSION BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, + {"drop global binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, + {"drop session binding for select * from t union all select * from t using select * from t use index(a) union all select * from t use index(a)", true, "DROP SESSION BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t` USING SELECT * FROM `t` USE INDEX (`a`) UNION ALL SELECT * FROM `t` USE INDEX (`a`)"}, + {"drop global binding for select * from t union all select * from t", true, "DROP GLOBAL BINDING FOR SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create session binding for select 1 union select 2 intersect select 3 using select 1 union select 2 intersect select 3", true, "CREATE SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3 USING SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, + {"drop session binding for select 1 union select 2 intersect select 3 using select 1 union select 2 intersect select 3", true, "DROP SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3 USING SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, + {"drop session binding for select 1 union select 2 intersect select 3", true, "DROP SESSION BINDING FOR SELECT 1 UNION SELECT 2 INTERSECT SELECT 3"}, + // Update cases. + {"CREATE GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "CREATE GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, + {"CREATE SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "CREATE SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, + {"drop global binding for update t set a = 1 where b = 1", true, "DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1"}, + {"drop session binding for update t set a = 1 where b = 1", true, "DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1"}, + {"DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "DROP GLOBAL BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, + {"DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1", true, "DROP SESSION BINDING FOR UPDATE `t` SET `a`=1 WHERE `b`=1 USING UPDATE /*+ USE_INDEX(`t` `b`)*/ `t` SET `a`=1 WHERE `b`=1"}, + // Multi-table Update. + {"CREATE GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "CREATE GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + {"CREATE SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "CREATE SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + {"DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + {"DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + {"DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP GLOBAL BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + {"DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`", true, "DROP SESSION BINDING FOR UPDATE `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b` USING UPDATE /*+ INL_JOIN(`t1`)*/ `t1` JOIN `t2` SET `t1`.`a`=1 WHERE `t1`.`b`=`t2`.`b`"}, + // Delete cases. + {"CREATE GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "CREATE GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, + {"CREATE SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "CREATE SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, + {"drop global binding for delete from t where a = 1", true, "DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1"}, + {"drop session binding for delete from t where a = 1", true, "DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1"}, + {"DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "DROP GLOBAL BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, + {"DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1", true, "DROP SESSION BINDING FOR DELETE FROM `t` WHERE `a`=1 USING DELETE /*+ USE_INDEX(`t` `a`)*/ FROM `t` WHERE `a`=1"}, + // Multi-table Delete. + {"CREATE GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "CREATE GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + {"CREATE SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "CREATE SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + {"drop global binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.a = 1", true, "DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + {"drop session binding for delete t1, t2 from t1 inner join t2 on t1.b = t2.b where t1.a = 1", true, "DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + {"DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "DROP GLOBAL BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + {"DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1", true, "DROP SESSION BINDING FOR DELETE `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1 USING DELETE /*+ HASH_JOIN(`t1`, `t2`)*/ `t1`,`t2` FROM `t1` JOIN `t2` ON `t1`.`b`=`t2`.`b` WHERE `t1`.`a`=1"}, + // Insert cases. + {"CREATE GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"CREATE SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"drop global binding for insert into t1 select * from t2 where t1.a=1", true, "DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, + {"drop session binding for insert into t1 select * from t2 where t1.a=1", true, "DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, + {"DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP GLOBAL BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP SESSION BINDING FOR INSERT INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING INSERT INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + // Replace cases. + {"CREATE GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"CREATE SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "CREATE SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"drop global binding for replace into t1 select * from t2 where t1.a=1", true, "DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, + {"drop session binding for replace into t1 select * from t2 where t1.a=1", true, "DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t1`.`a`=1"}, + {"DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP GLOBAL BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1", true, "DROP SESSION BINDING FOR REPLACE INTO `t1` SELECT * FROM `t2` WHERE `t2`.`a`=1 USING REPLACE INTO `t1` SELECT /*+ USE_INDEX(`t2` `a`)*/ * FROM `t2` WHERE `t2`.`a`=1"}, + {"DROP SESSION BINDING FOR SQL DIGEST 'a'", true, "DROP SESSION BINDING FOR SQL DIGEST 'a'"}, + {"drop global binding for sql digest 's'", true, "DROP GLOBAL BINDING FOR SQL DIGEST 's'"}, + {"create session binding from history using plan digest 'sss'", true, "CREATE SESSION BINDING FROM HISTORY USING PLAN DIGEST 'sss'"}, + {"CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST 'sss'", true, "CREATE GLOBAL BINDING FROM HISTORY USING PLAN DIGEST 'sss'"}, + {"set binding enabled for sql digest '1'", true, "SET BINDING ENABLED FOR SQL DIGEST '1'"}, + {"set binding disabled for sql digest '1'", true, "SET BINDING DISABLED FOR SQL DIGEST '1'"}, + } + RunTest(t, table, false) + + p := parser.New() + sms, _, err := p.Parse("create global binding for select * from t using select * from t use index(a)", "", "") + require.NoError(t, err) + v, ok := sms[0].(*ast.CreateBindingStmt) + require.True(t, ok) + require.Equal(t, "select * from t", v.OriginNode.Text()) + require.Equal(t, "select * from t use index(a)", v.HintedNode.Text()) + require.True(t, v.GlobalScope) +} + +func TestView(t *testing.T) { + table := []testCase{ + {"create view v as select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = undefined view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = temptable view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = current_user view v as select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t`"}, + + // create view with `(` select statement `)` + {"create view v as (select * from t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = undefined view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = temptable view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security definer view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t) with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`) WITH LOCAL CHECK OPTION"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t) with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = current_user view v as (select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t`)"}, + + // create view with union statement + {"create view v as select * from t union select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = undefined view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = temptable view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = current_user view v as select * from t union select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION SELECT * FROM `t`"}, + + // create view with union all statement + {"create view v as select * from t union all select * from t", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = undefined view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = temptable view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security definer view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t` WITH LOCAL CHECK OPTION"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as select * from t union all select * from t with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + {"create or replace algorithm = merge definer = current_user view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + + // create view with `(` union statement `)` + {"create view v as (select * from t union all select * from t)", true, "CREATE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = undefined view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = temptable view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = TEMPTABLE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security definer view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY DEFINER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t)", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t) with local check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`) WITH LOCAL CHECK OPTION"}, + {"create or replace algorithm = merge definer = 'root' sql security invoker view v(a,b) as (select * from t union all select * from t) with cascaded check option", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = `root`@`%` SQL SECURITY INVOKER VIEW `v` (`a`,`b`) AS (SELECT * FROM `t` UNION ALL SELECT * FROM `t`)"}, + {"create or replace algorithm = merge definer = current_user view v as select * from t union all select * from t", true, "CREATE OR REPLACE ALGORITHM = MERGE DEFINER = CURRENT_USER SQL SECURITY DEFINER VIEW `v` AS SELECT * FROM `t` UNION ALL SELECT * FROM `t`"}, + } + RunTest(t, table, false) + + // Test case for the text of the select statement in create view statement. + p := parser.New() + sms, _, err := p.Parse("create view v as select * from t", "", "") + require.NoError(t, err) + v, ok := sms[0].(*ast.CreateViewStmt) + require.True(t, ok) + require.Equal(t, model.AlgorithmUndefined, v.Algorithm) + require.Equal(t, "select * from t", v.Select.Text()) + require.Equal(t, model.SecurityDefiner, v.Security) + require.Equal(t, model.CheckOptionCascaded, v.CheckOption) + + src := `CREATE OR REPLACE ALGORITHM = UNDEFINED DEFINER = root@localhost + SQL SECURITY DEFINER + VIEW V(a,b,c) AS select c,d,e from t + WITH CASCADED CHECK OPTION;` + + var st ast.StmtNode + st, err = p.ParseOneStmt(src, "", "") + require.NoError(t, err) + v, ok = st.(*ast.CreateViewStmt) + require.True(t, ok) + require.True(t, v.OrReplace) + require.Equal(t, model.AlgorithmUndefined, v.Algorithm) + require.Equal(t, "root", v.Definer.Username) + require.Equal(t, "localhost", v.Definer.Hostname) + require.Equal(t, model.NewCIStr("a"), v.Cols[0]) + require.Equal(t, model.NewCIStr("b"), v.Cols[1]) + require.Equal(t, model.NewCIStr("c"), v.Cols[2]) + require.Equal(t, "select c,d,e from t", v.Select.Text()) + require.Equal(t, model.SecurityDefiner, v.Security) + require.Equal(t, model.CheckOptionCascaded, v.CheckOption) +} + +func TestTimestampDiffUnit(t *testing.T) { + // Test case for timestampdiff unit. + // TimeUnit should be unified to upper case. + p := parser.New() + stmt, _, err := p.Parse("SELECT TIMESTAMPDIFF(MONTH,'2003-02-01','2003-05-01'), TIMESTAMPDIFF(month,'2003-02-01','2003-05-01');", "", "") + require.NoError(t, err) + ss := stmt[0].(*ast.SelectStmt) + fields := ss.Fields.Fields + require.Len(t, fields, 2) + expr := fields[0].Expr + f, ok := expr.(*ast.FuncCallExpr) + require.True(t, ok) + require.Equal(t, ast.TimeUnitMonth, f.Args[0].(*ast.TimeUnitExpr).Unit) + + expr = fields[1].Expr + f, ok = expr.(*ast.FuncCallExpr) + require.True(t, ok) + require.Equal(t, ast.TimeUnitMonth, f.Args[0].(*ast.TimeUnitExpr).Unit) + + // Test Illegal TimeUnit for TimestampDiff + table := []testCase{ + {"SELECT TIMESTAMPDIFF(SECOND_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(MINUTE_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(MINUTE_SECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(HOUR_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(HOUR_SECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(HOUR_MINUTE,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(DAY_MICROSECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(DAY_SECOND,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(DAY_MINUTE,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(DAY_HOUR,'2003-02-01','2003-05-01')", false, ""}, + {"SELECT TIMESTAMPDIFF(YEAR_MONTH,'2003-02-01','2003-05-01')", false, ""}, + } + RunTest(t, table, false) +} + +func TestFuncCallExprOffset(t *testing.T) { + // Test case for offset field on func call expr. + p := parser.New() + stmt, _, err := p.Parse("SELECT s.a(), b();", "", "") + require.NoError(t, err) + ss := stmt[0].(*ast.SelectStmt) + fields := ss.Fields.Fields + require.Len(t, fields, 2) + + { + // s.a() + expr := fields[0].Expr + f, ok := expr.(*ast.FuncCallExpr) + require.True(t, ok) + require.Equal(t, 7, f.OriginTextPosition()) + } + + { + // b() + expr := fields[1].Expr + f, ok := expr.(*ast.FuncCallExpr) + require.True(t, ok) + require.Equal(t, 14, f.OriginTextPosition()) + } +} + +func TestSessionManage(t *testing.T) { + table := []testCase{ + // Kill statement. + // See https://dev.mysql.com/doc/refman/5.7/en/kill.html + {"kill 23123", true, "KILL 23123"}, + {"kill CONNECTION_ID()", true, "KILL CONNECTION_ID()"}, + {"kill connection 23123", true, "KILL 23123"}, + {"kill query 23123", true, "KILL QUERY 23123"}, + {"kill tidb 23123", true, "KILL TIDB 23123"}, + {"kill tidb connection 23123", true, "KILL TIDB 23123"}, + {"kill tidb query 23123", true, "KILL TIDB QUERY 23123"}, + {"show processlist", true, "SHOW PROCESSLIST"}, + {"show full processlist", true, "SHOW FULL PROCESSLIST"}, + {"shutdown", true, "SHUTDOWN"}, + {"restart", true, "RESTART"}, + } + RunTest(t, table, false) +} + +func TestParseShowOpenTables(t *testing.T) { + table := []testCase{ + {"SHOW OPEN TABLES", true, "SHOW OPEN TABLES"}, + {"SHOW OPEN TABLES IN test", true, "SHOW OPEN TABLES IN `test`"}, + {"SHOW OPEN TABLES FROM test", true, "SHOW OPEN TABLES IN `test`"}, + } + RunTest(t, table, false) +} + +func TestSQLModeANSIQuotes(t *testing.T) { + p := parser.New() + p.SetSQLMode(mysql.ModeANSIQuotes) + tests := []string{ + `CREATE TABLE "table" ("id" int)`, + `select * from t "tt"`, + } + for _, test := range tests { + _, _, err := p.Parse(test, "", "") + require.NoError(t, err) + } +} + +func TestDDLStatements(t *testing.T) { + p := parser.New() + // Tests that whatever the charset it is define, we always assign utf8 charset and utf8_bin collate. + createTableStr := `CREATE TABLE t ( + a varchar(64) binary, + b char(10) charset utf8 collate utf8_general_ci, + c text charset latin1) ENGINE=innoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin` + stmts, _, err := p.Parse(createTableStr, "", "") + require.NoError(t, err) + stmt := stmts[0].(*ast.CreateTableStmt) + require.True(t, mysql.HasBinaryFlag(stmt.Cols[0].Tp.GetFlag())) + for _, colDef := range stmt.Cols[1:] { + require.False(t, mysql.HasBinaryFlag(colDef.Tp.GetFlag())) + } + for _, tblOpt := range stmt.Options { + switch tblOpt.Tp { + case ast.TableOptionCharset: + require.Equal(t, "utf8", tblOpt.StrValue) + case ast.TableOptionCollate: + require.Equal(t, "utf8_bin", tblOpt.StrValue) + } + } + createTableStr = `CREATE TABLE t ( + a varbinary(64), + b binary(10), + c blob)` + stmts, _, err = p.Parse(createTableStr, "", "") + require.NoError(t, err) + stmt = stmts[0].(*ast.CreateTableStmt) + for _, colDef := range stmt.Cols { + require.Equal(t, charset.CharsetBin, colDef.Tp.GetCharset()) + require.Equal(t, charset.CollationBin, colDef.Tp.GetCollate()) + require.True(t, mysql.HasBinaryFlag(colDef.Tp.GetFlag())) + } + // Test set collate for all column types + createTableStr = `CREATE TABLE t ( + c_int int collate utf8_bin, + c_real real collate utf8_bin, + c_float float collate utf8_bin, + c_bool bool collate utf8_bin, + c_char char collate utf8_bin, + c_binary binary collate utf8_bin, + c_varchar varchar(2) collate utf8_bin, + c_year year collate utf8_bin, + c_date date collate utf8_bin, + c_time time collate utf8_bin, + c_datetime datetime collate utf8_bin, + c_timestamp timestamp collate utf8_bin, + c_tinyblob tinyblob collate utf8_bin, + c_blob blob collate utf8_bin, + c_mediumblob mediumblob collate utf8_bin, + c_longblob longblob collate utf8_bin, + c_bit bit collate utf8_bin, + c_long_varchar long varchar collate utf8_bin, + c_tinytext tinytext collate utf8_bin, + c_text text collate utf8_bin, + c_mediumtext mediumtext collate utf8_bin, + c_longtext longtext collate utf8_bin, + c_decimal decimal collate utf8_bin, + c_numeric numeric collate utf8_bin, + c_enum enum('1') collate utf8_bin, + c_set set('1') collate utf8_bin, + c_json json collate utf8_bin)` + _, _, err = p.Parse(createTableStr, "", "") + require.NoError(t, err) + + createTableStr = `CREATE TABLE t (c_double double(10))` + _, _, err = p.Parse(createTableStr, "", "") + require.EqualError(t, err, "[parser:1149]You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use") + p.SetStrictDoubleTypeCheck(false) + _, _, err = p.Parse(createTableStr, "", "") + require.NoError(t, err) + p.SetStrictDoubleTypeCheck(true) + + createTableStr = `CREATE TABLE t (c_double double(10, 2))` + _, _, err = p.Parse(createTableStr, "", "") + require.NoError(t, err) + + createTableStr = `create global temporary table t010(local_01 int, local_03 varchar(20))` + _, _, err = p.Parse(createTableStr, "", "") + require.EqualError(t, err, "line 1 column 70 near \"\"GLOBAL TEMPORARY and ON COMMIT DELETE ROWS must appear together ") + + createTableStr = `create global temporary table t010(local_01 int, local_03 varchar(20)) on commit preserve rows` + _, _, err = p.Parse(createTableStr, "", "") + require.NoError(t, err) +} + +func TestAnalyze(t *testing.T) { + table := []testCase{ + {"analyze table t1", true, "ANALYZE TABLE `t1`"}, + {"analyze table t1.*", false, ""}, + {"analyze table t,t1", true, "ANALYZE TABLE `t`,`t1`"}, + {"analyze table t1 index", true, "ANALYZE TABLE `t1` INDEX"}, + {"analyze table t1 index a", true, "ANALYZE TABLE `t1` INDEX `a`"}, + {"analyze table t1 index a,b", true, "ANALYZE TABLE `t1` INDEX `a`,`b`"}, + {"analyze table t with 4 buckets", true, "ANALYZE TABLE `t` WITH 4 BUCKETS"}, + {"analyze table t with 4 topn", true, "ANALYZE TABLE `t` WITH 4 TOPN"}, + {"analyze table t with 4 cmsketch width", true, "ANALYZE TABLE `t` WITH 4 CMSKETCH WIDTH"}, + {"analyze table t with 4 cmsketch depth", true, "ANALYZE TABLE `t` WITH 4 CMSKETCH DEPTH"}, + {"analyze table t with 4 samples", true, "ANALYZE TABLE `t` WITH 4 SAMPLES"}, + {"analyze table t with 4 buckets, 4 topn, 4 cmsketch width, 4 cmsketch depth, 4 samples", true, "ANALYZE TABLE `t` WITH 4 BUCKETS, 4 TOPN, 4 CMSKETCH WIDTH, 4 CMSKETCH DEPTH, 4 SAMPLES"}, + {"analyze table t index a with 4 buckets", true, "ANALYZE TABLE `t` INDEX `a` WITH 4 BUCKETS"}, + {"analyze table t partition a", true, "ANALYZE TABLE `t` PARTITION `a`"}, + {"analyze table t partition a with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` WITH 4 BUCKETS"}, + {"analyze table t partition a index b", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b`"}, + {"analyze table t partition a index b with 4 buckets", true, "ANALYZE TABLE `t` PARTITION `a` INDEX `b` WITH 4 BUCKETS"}, + {"analyze incremental table t index", true, "ANALYZE INCREMENTAL TABLE `t` INDEX"}, + {"analyze incremental table t index idx", true, "ANALYZE INCREMENTAL TABLE `t` INDEX `idx`"}, + {"analyze table t update histogram on b with 1024 buckets", true, "ANALYZE TABLE `t` UPDATE HISTOGRAM ON `b` WITH 1024 BUCKETS"}, + {"analyze table t drop histogram on b", true, "ANALYZE TABLE `t` DROP HISTOGRAM ON `b`"}, + {"analyze table t update histogram on c1, c2;", true, "ANALYZE TABLE `t` UPDATE HISTOGRAM ON `c1`,`c2`"}, + {"analyze table t drop histogram on c1, c2;", true, "ANALYZE TABLE `t` DROP HISTOGRAM ON `c1`,`c2`"}, + {"analyze table t update histogram on t.c1, t.c2", false, ""}, + {"analyze table t drop histogram on t.c1, t.c2", false, ""}, + {"analyze table t1,t2 all columns", true, "ANALYZE TABLE `t1`,`t2` ALL COLUMNS"}, + {"analyze table t partition a all columns", true, "ANALYZE TABLE `t` PARTITION `a` ALL COLUMNS"}, + {"analyze table t1,t2 all columns with 4 topn", true, "ANALYZE TABLE `t1`,`t2` ALL COLUMNS WITH 4 TOPN"}, + {"analyze table t partition a all columns with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` ALL COLUMNS WITH 1024 BUCKETS"}, + {"analyze table t1,t2 predicate columns", true, "ANALYZE TABLE `t1`,`t2` PREDICATE COLUMNS"}, + {"analyze table t partition a predicate columns", true, "ANALYZE TABLE `t` PARTITION `a` PREDICATE COLUMNS"}, + {"analyze table t1,t2 predicate columns with 4 topn", true, "ANALYZE TABLE `t1`,`t2` PREDICATE COLUMNS WITH 4 TOPN"}, + {"analyze table t partition a predicate columns with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` PREDICATE COLUMNS WITH 1024 BUCKETS"}, + {"analyze table t columns c1,c2", true, "ANALYZE TABLE `t` COLUMNS `c1`,`c2`"}, + {"analyze table t partition a columns c1,c2", true, "ANALYZE TABLE `t` PARTITION `a` COLUMNS `c1`,`c2`"}, + {"analyze table t columns t.c1,t.c2", false, ""}, + {"analyze table t partition a columns t.c1,t.c2", false, ""}, + {"analyze table t columns c1,c2 with 4 topn", true, "ANALYZE TABLE `t` COLUMNS `c1`,`c2` WITH 4 TOPN"}, + {"analyze table t partition a columns c1,c2 with 1024 buckets", true, "ANALYZE TABLE `t` PARTITION `a` COLUMNS `c1`,`c2` WITH 1024 BUCKETS"}, + {"analyze table t index a columns c", false, ""}, + {"analyze table t index a all columns", false, ""}, + {"analyze table t index a predicate columns", false, ""}, + {"analyze table t with 10 samplerate", true, "ANALYZE TABLE `t` WITH 10 SAMPLERATE"}, + {"analyze table t with 0.1 samplerate", true, "ANALYZE TABLE `t` WITH 0.1 SAMPLERATE"}, + } + RunTest(t, table, false) +} + +func TestTableSample(t *testing.T) { + table := []testCase{ + // positive test cases + {"select * from tbl tablesample system (50);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (50)"}, + {"select * from tbl tablesample system (50 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (50 PERCENT)"}, + {"select * from tbl tablesample system (49.9 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (49.9 PERCENT)"}, + {"select * from tbl tablesample system (120 rows);", true, "SELECT * FROM `tbl` TABLESAMPLE SYSTEM (120 ROWS)"}, + {"select * from tbl tablesample bernoulli (50);", true, "SELECT * FROM `tbl` TABLESAMPLE BERNOULLI (50)"}, + {"select * from tbl tablesample (50);", true, "SELECT * FROM `tbl` TABLESAMPLE (50)"}, + {"select * from tbl tablesample (50) repeatable (123456789);", true, "SELECT * FROM `tbl` TABLESAMPLE (50) REPEATABLE(123456789)"}, + {"select * from tbl as a tablesample (50);", true, "SELECT * FROM `tbl` AS `a` TABLESAMPLE (50)"}, + {"select * from tbl `tablesample` tablesample (50);", true, "SELECT * FROM `tbl` AS `tablesample` TABLESAMPLE (50)"}, + {"select * from tbl tablesample (50) where id > 20;", true, "SELECT * FROM `tbl` TABLESAMPLE (50) WHERE `id`>20"}, + {"select * from tbl partition (p0) tablesample (50);", true, "SELECT * FROM `tbl` PARTITION(`p0`) TABLESAMPLE (50)"}, + {"select * from tbl tablesample (0 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE (0 PERCENT)"}, + {"select * from tbl tablesample (100 percent);", true, "SELECT * FROM `tbl` TABLESAMPLE (100 PERCENT)"}, + {"select * from tbl tablesample (0 rows);", true, "SELECT * FROM `tbl` TABLESAMPLE (0 ROWS)"}, + {"select * from tbl tablesample ('34');", true, "SELECT * FROM `tbl` TABLESAMPLE (_UTF8MB4'34')"}, + {"select * from tbl1 tablesample (10), tbl2 tablesample (20);", true, "SELECT * FROM (`tbl1` TABLESAMPLE (10)) JOIN `tbl2` TABLESAMPLE (20)"}, + {"select * from tbl1 a tablesample (10) join tbl2 b tablesample (20) on a.id <> b.id;", true, "SELECT * FROM `tbl1` AS `a` TABLESAMPLE (10) JOIN `tbl2` AS `b` TABLESAMPLE (20) ON `a`.`id`!=`b`.`id`"}, + {"select * from demo tablesample bernoulli(50) limit 1 into outfile '/tmp/sample.csv';", true, "SELECT * FROM `demo` TABLESAMPLE BERNOULLI (50) LIMIT 1 INTO OUTFILE '/tmp/sample.csv'"}, + {"select * from demo tablesample bernoulli(50) order by a, b into outfile '/tmp/sample.csv';", true, "SELECT * FROM `demo` TABLESAMPLE BERNOULLI (50) ORDER BY `a`,`b` INTO OUTFILE '/tmp/sample.csv'"}, + + // negative test cases + {"select * from tbl tablesample system(50) a;", false, ""}, + {"select * from tbl tablesample (50) partition (p0);", false, ""}, + {"select * from tbl where id > 20 tablesample system(50);", false, ""}, + {"select * from (select * from tbl) a tablesample system(50);", false, ""}, + {"select * from tbl tablesample system(50) tablesample system(50);", false, ""}, + {"select * from tbl tablesample system(50, 50);", false, ""}, + {"select * from tbl tablesample dhfksdlfljcoew(50);", false, ""}, + {"select * from tbl tablesample system;", false, ""}, + {"select * from tbl tablesample system (33) repeatable;", false, ""}, + {"select 1 from dual tablesample system (50);", false, ""}, + } + RunTest(t, table, false) + p := parser.New() + cases := []string{ + "select * from tbl tablesample (33.3 + 44.4);", + "select * from tbl tablesample (33.3 + 44.4 percent);", + "select * from tbl tablesample (33 + 44 rows);", + "select * from tbl tablesample (33 + 44 rows) repeatable (55 + 66);", + "select * from tbl tablesample (200);", + "select * from tbl tablesample (-10);", + "select * from tbl tablesample (null);", + "select * from tbl tablesample (33.3 rows);", + "select * from tbl tablesample (-4 rows);", + "select * from tbl tablesample (50) repeatable ('ssss');", + "delete from tbl using tbl2 tablesample(10 rows) repeatable (111) where tbl.id = tbl2.id", + "update tbl tablesample regions() set id = '1'", + } + for _, sql := range cases { + _, err := p.ParseOneStmt(sql, "", "") + require.NoErrorf(t, err, "source %v", sql) + } +} + +func TestGeneratedColumn(t *testing.T) { + tests := []struct { + input string + ok bool + expr string + }{ + {"create table t (c int, d int generated always as (c + 1) virtual)", true, "c + 1"}, + {"create table t (c int, d int as ( c + 1 ) virtual)", true, "c + 1"}, + {"create table t (c int, d int as (1 + 1) stored)", true, "1 + 1"}, + } + p := parser.New() + for _, tbl := range tests { + stmtNodes, _, err := p.Parse(tbl.input, "", "") + if tbl.ok { + require.NoError(t, err) + stmtNode := stmtNodes[0] + for _, col := range stmtNode.(*ast.CreateTableStmt).Cols { + for _, opt := range col.Options { + if opt.Tp == ast.ColumnOptionGenerated { + require.Equal(t, tbl.expr, opt.Expr.Text()) + } + } + } + } else { + require.Error(t, err) + } + } + + _, _, err := p.Parse("create table t1 (a int, b int as (a + 1) default 10);", "", "") + require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of DEFAULT and generated column") + _, _, err = p.Parse("create table t1 (a int, b int as (a + 1) on update now());", "", "") + require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of ON UPDATE and generated column") + _, _, err = p.Parse("create table t1 (a int, b int as (a + 1) auto_increment);", "", "") + require.Equal(t, err.Error(), "[ddl:1221]Incorrect usage of AUTO_INCREMENT and generated column") +} + +func TestSetTransaction(t *testing.T) { + // Set transaction is equivalent to setting the global or session value of tx_isolation. + // For example: + // SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED + // SET SESSION tx_isolation='READ-COMMITTED' + tests := []struct { + input string + isGlobal bool + value string + }{ + { + "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", + false, "READ-COMMITTED", + }, + { + "SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ", + true, "REPEATABLE-READ", + }, + } + p := parser.New() + for _, tbl := range tests { + stmt1, err := p.ParseOneStmt(tbl.input, "", "") + require.NoError(t, err) + setStmt := stmt1.(*ast.SetStmt) + vars := setStmt.Variables[0] + require.Equal(t, "tx_isolation", vars.Name) + require.Equal(t, tbl.isGlobal, vars.IsGlobal) + require.Equal(t, true, vars.IsSystem) + require.Equal(t, tbl.value, vars.Value.(ast.ValueExpr).GetValue()) + } +} + +func TestSideEffect(t *testing.T) { + // This test cover a bug that parse an error SQL doesn't leave the parser in a + // clean state, cause the following SQL parse fail. + p := parser.New() + _, err := p.ParseOneStmt("create table t /*!50100 'abc', 'abc' */;", "", "") + require.Error(t, err) + + _, err = p.ParseOneStmt("show tables;", "", "") + require.NoError(t, err) +} + +func TestTablePartition(t *testing.T) { + table := []testCase{ + {"ALTER TABLE t1 TRUNCATE PARTITION p0", true, "ALTER TABLE `t1` TRUNCATE PARTITION `p0`"}, + {"ALTER TABLE t1 TRUNCATE PARTITION p0, p1", true, "ALTER TABLE `t1` TRUNCATE PARTITION `p0`,`p1`"}, + {"ALTER TABLE t1 TRUNCATE PARTITION ALL", true, "ALTER TABLE `t1` TRUNCATE PARTITION ALL"}, + {"ALTER TABLE t1 TRUNCATE PARTITION ALL, p0", false, ""}, + {"ALTER TABLE t1 TRUNCATE PARTITION p0, ALL", false, ""}, + + {"ALTER TABLE t1 OPTIMIZE PARTITION p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION `p0`"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`"}, + // LOCAL is alias to NO_WRITE_TO_BINLOG + {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL p0", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION `p0`,`p1`"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL p0, p1", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION ALL"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION LOCAL ALL", true, "ALTER TABLE `t1` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG ALL"}, + {"ALTER TABLE t1 OPTIMIZE PARTITION ALL, p0", false, ""}, + {"ALTER TABLE t1 OPTIMIZE PARTITION p0, ALL", false, ""}, + // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), + // and the remains should re recognized as identifier, used as partition name here. + {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL", false, ""}, + {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL local", true, "ALTER TABLE `t_n` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `local`"}, + {"ALTER TABLE t_n OPTIMIZE PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` OPTIMIZE PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, + + {"ALTER TABLE t1 REPAIR PARTITION p0", true, "ALTER TABLE `t1` REPAIR PARTITION `p0`"}, + {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG p0", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`"}, + // LOCAL is alias to NO_WRITE_TO_BINLOG + {"ALTER TABLE t1 REPAIR PARTITION LOCAL p0", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`"}, + {"ALTER TABLE t1 REPAIR PARTITION p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION `p0`,`p1`"}, + {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, + {"ALTER TABLE t1 REPAIR PARTITION LOCAL p0, p1", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG `p0`,`p1`"}, + {"ALTER TABLE t1 REPAIR PARTITION ALL", true, "ALTER TABLE `t1` REPAIR PARTITION ALL"}, + {"ALTER TABLE t1 REPAIR PARTITION NO_WRITE_TO_BINLOG ALL", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG ALL"}, + {"ALTER TABLE t1 REPAIR PARTITION LOCAL ALL", true, "ALTER TABLE `t1` REPAIR PARTITION NO_WRITE_TO_BINLOG ALL"}, + {"ALTER TABLE t1 REPAIR PARTITION ALL, p0", false, ""}, + {"ALTER TABLE t1 REPAIR PARTITION p0, ALL", false, ""}, + // The first `LOCAL` should be recognized as unreserved keyword `LOCAL` (alias to `NO_WRITE_TO_BINLOG`), + // and the remains should re recognized as identifier, used as partition name here. + {"ALTER TABLE t_n REPAIR PARTITION LOCAL", false, ""}, + {"ALTER TABLE t_n REPAIR PARTITION LOCAL local", true, "ALTER TABLE `t_n` REPAIR PARTITION NO_WRITE_TO_BINLOG `local`"}, + {"ALTER TABLE t_n REPAIR PARTITION LOCAL local, local", true, "ALTER TABLE `t_n` REPAIR PARTITION NO_WRITE_TO_BINLOG `local`,`local`"}, + + {"ALTER TABLE t1 IMPORT PARTITION p0 TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION `p0` TABLESPACE"}, + {"ALTER TABLE t1 IMPORT PARTITION p0, p1 TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION `p0`,`p1` TABLESPACE"}, + {"ALTER TABLE t1 IMPORT PARTITION ALL TABLESPACE", true, "ALTER TABLE `t1` IMPORT PARTITION ALL TABLESPACE"}, + {"ALTER TABLE t1 IMPORT PARTITION ALL, p0 TABLESPACE", false, ""}, + {"ALTER TABLE t1 IMPORT PARTITION p0, ALL TABLESPACE", false, ""}, + + {"ALTER TABLE t1 DISCARD PARTITION p0 TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION `p0` TABLESPACE"}, + {"ALTER TABLE t1 DISCARD PARTITION p0, p1 TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION `p0`,`p1` TABLESPACE"}, + {"ALTER TABLE t1 DISCARD PARTITION ALL TABLESPACE", true, "ALTER TABLE `t1` DISCARD PARTITION ALL TABLESPACE"}, + {"ALTER TABLE t1 DISCARD PARTITION ALL, p0 TABLESPACE", false, ""}, + {"ALTER TABLE t1 DISCARD PARTITION p0, ALL TABLESPACE", false, ""}, + + {"ALTER TABLE t1 ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT 'APSTART \\' APEND')", true, "ALTER TABLE `t1` ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'APSTART '' APEND')"}, + {"ALTER TABLE t1 ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')", true, "ALTER TABLE `t1` ADD PARTITION (PARTITION `p5` VALUES LESS THAN (2010) COMMENT = 'xxx')"}, + {`CREATE TABLE t1 (a int not null,b int not null,c int not null,primary key(a,b)) + partition by range (a) + partitions 3 + (partition x1 values less than (5), + partition x2 values less than (10), + partition x3 values less than maxvalue);`, true, "CREATE TABLE `t1` (`a` INT NOT NULL,`b` INT NOT NULL,`c` INT NOT NULL,PRIMARY KEY(`a`, `b`)) PARTITION BY RANGE (`a`) (PARTITION `x1` VALUES LESS THAN (5),PARTITION `x2` VALUES LESS THAN (10),PARTITION `x3` VALUES LESS THAN (MAXVALUE))"}, + {"CREATE TABLE t1 (a int not null) partition by range (a) (partition x1 values less than (5) tablespace ts1)", true, "CREATE TABLE `t1` (`a` INT NOT NULL) PARTITION BY RANGE (`a`) (PARTITION `x1` VALUES LESS THAN (5) TABLESPACE = `ts1`)"}, + {`create table t (a int) partition by range (a) + (PARTITION p0 VALUES LESS THAN (63340531200) ENGINE = MyISAM, + PARTITION p1 VALUES LESS THAN (63342604800) ENGINE MyISAM)`, true, "CREATE TABLE `t` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) ENGINE = MyISAM,PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, + {`create table t (a int) partition by range (a) + (PARTITION p0 VALUES LESS THAN (63340531200) ENGINE = MyISAM COMMENT 'xxx', + PARTITION p1 VALUES LESS THAN (63342604800) ENGINE = MyISAM)`, true, "CREATE TABLE `t` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) ENGINE = MyISAM COMMENT = 'xxx',PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, + {`create table t1 (a int) partition by range (a) + (PARTITION p0 VALUES LESS THAN (63340531200) COMMENT 'xxx' ENGINE = MyISAM , + PARTITION p1 VALUES LESS THAN (63342604800) ENGINE = MyISAM)`, true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `p0` VALUES LESS THAN (63340531200) COMMENT = 'xxx' ENGINE = MyISAM,PARTITION `p1` VALUES LESS THAN (63342604800) ENGINE = MyISAM)"}, + {`create table t (id int) + partition by range (id) + subpartition by key (id) subpartitions 2 + (partition p0 values less than (42))`, true, "CREATE TABLE `t` (`id` INT) PARTITION BY RANGE (`id`) SUBPARTITION BY KEY (`id`) SUBPARTITIONS 2 (PARTITION `p0` VALUES LESS THAN (42))"}, + {`create table t (id int) + partition by range (id) + subpartition by hash (id) + (partition p0 values less than (42))`, true, "CREATE TABLE `t` (`id` INT) PARTITION BY RANGE (`id`) SUBPARTITION BY HASH (`id`) (PARTITION `p0` VALUES LESS THAN (42))"}, + {`create table t1 (a varchar(5), b int signed, c varchar(10), d datetime) + partition by range columns(b,c) + subpartition by hash(to_seconds(d)) + ( partition p0 values less than (2, 'b'), + partition p1 values less than (4, 'd'), + partition p2 values less than (10, 'za'));`, true, + "CREATE TABLE `t1` (`a` VARCHAR(5),`b` INT,`c` VARCHAR(10),`d` DATETIME) PARTITION BY RANGE COLUMNS (`b`,`c`) SUBPARTITION BY HASH (TO_SECONDS(`d`)) (PARTITION `p0` VALUES LESS THAN (2, _UTF8MB4'b'),PARTITION `p1` VALUES LESS THAN (4, _UTF8MB4'd'),PARTITION `p2` VALUES LESS THAN (10, _UTF8MB4'za'))"}, + {`CREATE TABLE t1 (a INT, b TIMESTAMP DEFAULT '0000-00-00 00:00:00') +ENGINE=INNODB PARTITION BY LINEAR HASH (a) PARTITIONS 1;`, true, "CREATE TABLE `t1` (`a` INT,`b` TIMESTAMP DEFAULT _UTF8MB4'0000-00-00 00:00:00') ENGINE = INNODB PARTITION BY LINEAR HASH (`a`) PARTITIONS 1"}, + + // empty clause is valid only for HASH/KEY partitions + {"create table t1 (a int) partition by hash (a) (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x`,PARTITION `y`)"}, + {"create table t1 (a int) partition by key (a) (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY KEY (`a`) (PARTITION `x`,PARTITION `y`)"}, + {"create table t1 (a int) partition by range (a) (partition x, partition y)", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x, partition y)", false, ""}, + {"create table t1 (a int) partition by system_time (partition x, partition y)", false, ""}, + // VALUES LESS THAN clause is valid only for RANGE partitions + {"create table t1 (a int) partition by hash (a) (partition x values less than (10))", false, ""}, + {"create table t1 (a int) partition by key (a) (partition x values less than (10))", false, ""}, + {"create table t1 (a int) partition by range (a) (partition x values less than (maxvalue))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (MAXVALUE))"}, + {"create table t1 (a int) partition by range (a) (partition x values less than (default))", false, ""}, + {"create table t (a varchar(100), b int) partition by list columns (a) (partition p1 values in ('a','b','DEFAULT'), partition pDef values in (default))", true, "CREATE TABLE `t` (`a` VARCHAR(100),`b` INT) PARTITION BY LIST COLUMNS (`a`) (PARTITION `p1` VALUES IN (_UTF8MB4'a', _UTF8MB4'b', _UTF8MB4'DEFAULT'),PARTITION `pDef` DEFAULT)"}, + {"create table t1 (a int) partition by range (a) (partition x values less than (10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY RANGE (`a`) (PARTITION `x` VALUES LESS THAN (10))"}, + {"create table t1 (a int) partition by list (a) (partition x values less than (10))", false, ""}, + {"create table t1 (a int) partition by system_time (partition x values less than (10))", false, ""}, + // VALUES IN clause is valid only for LIST partitions + {"create table t1 (a int) partition by hash (a) (partition x values in (10))", false, ""}, + {"create table t1 (a int) partition by key (a) (partition x values in (10))", false, ""}, + {"create table t1 (a int) partition by range (a) (partition x values in (10))", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x values in (10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` VALUES IN (10))"}, + {"create table t1 (a int) partition by list (a) (partition x values in (default))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` DEFAULT)"}, + {"create table t1 (a int) partition by list (a) (partition x values in (maxvalue))", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x values in (default, 10))", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` VALUES IN (DEFAULT, 10))"}, + {"create table t1 (a int) partition by system_time (partition x values in (10))", false, ""}, + // HISTORY/CURRENT clauses are valid only for SYSTEM_TIME partitions + {"create table t1 (a int) partition by hash (a) (partition x history, partition y current)", false, ""}, + {"create table t1 (a int) partition by key (a) (partition x history, partition y current)", false, ""}, + {"create table t1 (a int) partition by range (a) (partition x history, partition y current)", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x history, partition y current)", false, ""}, + {"create table t1 (a int) partition by system_time (partition x history, partition y current)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME (PARTITION `x` HISTORY,PARTITION `y` CURRENT)"}, + + // LIST, RANGE and SYSTEM_TIME partitions all required definitions + {"create table t1 (a int) partition by hash (a)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) PARTITIONS 1"}, + {"create table t1 (a int) partition by key (a)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY KEY (`a`) PARTITIONS 1"}, + {"create table t1 (a int) partition by range (a)", false, ""}, + {"create table t1 (a int) partition by list (a)", false, ""}, + {"create table t1 (a int) partition by system_time", false, ""}, + // SYSTEM_TIME required 2 or more partitions + {"create table t1 (a int) partition by system_time (partition x history)", false, ""}, + {"create table t1 (a int) partition by system_time (partition x current)", false, ""}, + + // number of columns and number of values in VALUES clauses must match + {"create table t1 (a int, b int) partition by range (a) (partition x values less than (10, 20))", false, ""}, + {"create table t (id int) partition by range columns (id) (partition p0 values less than (1, 2))", false, ""}, + {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than (10, 20))", true, "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE COLUMNS (`a`,`b`) (PARTITION `x` VALUES LESS THAN (10, 20))"}, + {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than (10))", false, ""}, + {"create table t1 (a int, b int) partition by range columns (a, b) (partition x values less than maxvalue)", false, ""}, + {"create table t1 (a int, b int) partition by list (a) (partition x values in ((10, 20)))", false, ""}, + {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20)))", true, "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY LIST COLUMNS (`a`,`b`) (PARTITION `x` VALUES IN ((10, 20)))"}, + {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in (10, 20))", false, ""}, + {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in (10, (20, 30)))", false, ""}, + {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20), 30))", false, ""}, + {"create table t1 (a int, b int) partition by list columns (a, b) (partition x values in ((10, 20), (30, 40, 50)))", false, ""}, + + // there must be at least one column/partition/value inside (...) + {"create table t1 (a int) partition by hash (a) ()", false, ""}, + {"create table t1 (a int primary key) partition by key ()", true, "CREATE TABLE `t1` (`a` INT PRIMARY KEY) PARTITION BY KEY () PARTITIONS 1"}, + {"create table t1 (a int) partition by range columns () (partition x values less than maxvalue)", false, ""}, + {"create table t1 (a int) partition by list columns () (partition x default)", false, ""}, + {"create table t1 (a int) partition by range (a) (partition x values less than ())", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x values in ())", false, ""}, + {"create table t1 (a int) partition by list (a) (partition x default)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY LIST (`a`) (PARTITION `x` DEFAULT)"}, + + // only hash and key subpartitions are allowed + {"create table t1 (a int, b int) partition by range (a) subpartition by range (b) (partition x values less than maxvalue)", false, ""}, + + // number of partitions/subpartitions must be matching + {"create table t1 (a int) partition by hash (a) partitions 2 (partition x)", false, ""}, + {"create table t1 (a int) partition by hash (a) partitions 2 (partition x, partition y)", true, "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x`,PARTITION `y`)"}, + {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 2 (partition x values less than maxvalue (subpartition y))", false, ""}, + { + "create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 2 (partition x values less than maxvalue (subpartition y, subpartition z))", true, + "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 2 (PARTITION `x` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `y`,SUBPARTITION `z`))", + }, + { + "create table t1 (a int, b int) partition by range (a) subpartition by hash (b) (partition x values less than (10) (subpartition y,subpartition z),partition a values less than (20) (subpartition b,subpartition c))", true, + "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 2 (PARTITION `x` VALUES LESS THAN (10) (SUBPARTITION `y`,SUBPARTITION `z`),PARTITION `a` VALUES LESS THAN (20) (SUBPARTITION `b`,SUBPARTITION `c`))", + }, + {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) (partition x values less than (10) (subpartition y),partition a values less than (20) (subpartition b,subpartition c))", false, ""}, + {"create table t1 (a int, b int) partition by range (a) (partition x values less than (10) (subpartition y))", false, ""}, + {"create table t1 (a int) partition by hash (a) partitions 0", false, ""}, + {"create table t1 (a int, b int) partition by range (a) subpartition by hash (b) subpartitions 0 (partition x values less than (10))", false, ""}, + + // other partition tests + {"create table t1 (a int) partition by system_time interval 7 day limit 50000 (partition x history, partition y current)", false, ""}, + { + "create table t1 (a int) partition by system_time interval 7 day (partition x history, partition y current)", true, + "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME INTERVAL 7 DAY (PARTITION `x` HISTORY,PARTITION `y` CURRENT)", + }, + { + "create table t1 (a int) partition by system_time limit 50000 (partition x history, partition y current)", true, + "CREATE TABLE `t1` (`a` INT) PARTITION BY SYSTEM_TIME LIMIT 50000 (PARTITION `x` HISTORY,PARTITION `y` CURRENT)", + }, + { + "create table t1 (a int) partition by hash(a) (partition x engine InnoDB comment 'xxxx' data directory '/var/data' index directory '/var/index' max_rows 70000 min_rows 50 tablespace `innodb_file_per_table` nodegroup 255)", true, + "CREATE TABLE `t1` (`a` INT) PARTITION BY HASH (`a`) (PARTITION `x` ENGINE = InnoDB COMMENT = 'xxxx' DATA DIRECTORY = '/var/data' INDEX DIRECTORY = '/var/index' MAX_ROWS = 70000 MIN_ROWS = 50 TABLESPACE = `innodb_file_per_table` NODEGROUP = 255)", + }, + { + "create table t1 (a int, b int) partition by range(a) subpartition by hash(b) (partition x values less than maxvalue (subpartition y engine InnoDB comment 'xxxx' data directory '/var/data' index directory '/var/index' max_rows 70000 min_rows 50 tablespace `innodb_file_per_table` nodegroup 255))", true, + "CREATE TABLE `t1` (`a` INT,`b` INT) PARTITION BY RANGE (`a`) SUBPARTITION BY HASH (`b`) SUBPARTITIONS 1 (PARTITION `x` VALUES LESS THAN (MAXVALUE) (SUBPARTITION `y` ENGINE = InnoDB COMMENT = 'xxxx' DATA DIRECTORY = '/var/data' INDEX DIRECTORY = '/var/index' MAX_ROWS = 70000 MIN_ROWS = 50 TABLESPACE = `innodb_file_per_table` NODEGROUP = 255))", + }, + } + RunTest(t, table, false) + + // Check comment content. + p := parser.New() + stmt, err := p.ParseOneStmt("create table t (id int) partition by range (id) (partition p0 values less than (10) comment 'check')", "", "") + require.NoError(t, err) + createTable := stmt.(*ast.CreateTableStmt) + comment, ok := createTable.Partition.Definitions[0].Comment() + require.True(t, ok) + require.Equal(t, "check", comment) +} + +func TestTablePartitionNameList(t *testing.T) { + table := []testCase{ + {`select * from t partition (p0,p1)`, true, ""}, + } + + p := parser.New() + for _, tbl := range table { + stmt, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err) + + sel := stmt[0].(*ast.SelectStmt) + source, ok := sel.From.TableRefs.Left.(*ast.TableSource) + require.True(t, ok) + tableName, ok := source.Source.(*ast.TableName) + require.True(t, ok) + require.Len(t, tableName.PartitionNames, 2) + require.Equal(t, model.CIStr{O: "p0", L: "p0"}, tableName.PartitionNames[0]) + require.Equal(t, model.CIStr{O: "p1", L: "p1"}, tableName.PartitionNames[1]) + } +} + +func TestNotExistsSubquery(t *testing.T) { + table := []testCase{ + {`select * from t1 where not exists (select * from t2 where t1.a = t2.a)`, true, ""}, + } + + p := parser.New() + for _, tbl := range table { + stmt, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err) + + sel := stmt[0].(*ast.SelectStmt) + exists, ok := sel.Where.(*ast.ExistsSubqueryExpr) + require.True(t, ok) + require.Equal(t, tbl.ok, exists.Not) + } +} + +func TestWindowFunctionIdentifier(t *testing.T) { + //nolint: prealloc + var table []testCase + for key := range parser.WindowFuncTokenMapForTest { + table = append(table, testCase{fmt.Sprintf("select 1 %s", key), false, fmt.Sprintf("SELECT 1 AS `%s`", key)}) + } + RunTest(t, table, true) + + for i := range table { + table[i].ok = true + } + RunTest(t, table, false) +} + +func TestWindowFunctions(t *testing.T) { + table := []testCase{ + // For window function descriptions. + // See https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html + {`SELECT CUME_DIST() OVER w FROM t;`, true, "SELECT CUME_DIST() OVER `w` FROM `t`"}, + {`SELECT DENSE_RANK() OVER (w) FROM t;`, true, "SELECT DENSE_RANK() OVER (`w`) FROM `t`"}, + {`SELECT FIRST_VALUE(val) OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) OVER `w` FROM `t`"}, + {`SELECT FIRST_VALUE(val) RESPECT NULLS OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) OVER `w` FROM `t`"}, + {`SELECT FIRST_VALUE(val) IGNORE NULLS OVER w FROM t;`, true, "SELECT FIRST_VALUE(`val`) IGNORE NULLS OVER `w` FROM `t`"}, + {`SELECT LAG(val) OVER (w) FROM t;`, true, "SELECT LAG(`val`) OVER (`w`) FROM `t`"}, + {`SELECT LAG(val, 1) OVER (w) FROM t;`, true, "SELECT LAG(`val`, 1) OVER (`w`) FROM `t`"}, + {`SELECT LAG(val, 1, def) OVER (w) FROM t;`, true, "SELECT LAG(`val`, 1, `def`) OVER (`w`) FROM `t`"}, + {`SELECT LAST_VALUE(val) OVER (w) FROM t;`, true, "SELECT LAST_VALUE(`val`) OVER (`w`) FROM `t`"}, + {`SELECT LEAD(val) OVER w FROM t;`, true, "SELECT LEAD(`val`) OVER `w` FROM `t`"}, + {`SELECT LEAD(val, 1) OVER w FROM t;`, true, "SELECT LEAD(`val`, 1) OVER `w` FROM `t`"}, + {`SELECT LEAD(val, 1, def) OVER w FROM t;`, true, "SELECT LEAD(`val`, 1, `def`) OVER `w` FROM `t`"}, + {`SELECT NTH_VALUE(val, 233) OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) OVER `w` FROM `t`"}, + {`SELECT NTH_VALUE(val, 233) FROM FIRST OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) OVER `w` FROM `t`"}, + {`SELECT NTH_VALUE(val, 233) FROM LAST OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) FROM LAST OVER `w` FROM `t`"}, + {`SELECT NTH_VALUE(val, 233) FROM LAST IGNORE NULLS OVER w FROM t;`, true, "SELECT NTH_VALUE(`val`, 233) FROM LAST IGNORE NULLS OVER `w` FROM `t`"}, + {`SELECT NTH_VALUE(val) OVER w FROM t;`, false, ""}, + {`SELECT NTILE(233) OVER (w) FROM t;`, true, "SELECT NTILE(233) OVER (`w`) FROM `t`"}, + {`SELECT PERCENT_RANK() OVER (w) FROM t;`, true, "SELECT PERCENT_RANK() OVER (`w`) FROM `t`"}, + {`SELECT RANK() OVER (w) FROM t;`, true, "SELECT RANK() OVER (`w`) FROM `t`"}, + {`SELECT ROW_NUMBER() OVER (w) FROM t;`, true, "SELECT ROW_NUMBER() OVER (`w`) FROM `t`"}, + {`SELECT n, LAG(n, 1, 0) OVER (w), LEAD(n, 1, 0) OVER w, n + LAG(n, 1, 0) OVER (w) FROM fib;`, true, "SELECT `n`,LAG(`n`, 1, 0) OVER (`w`),LEAD(`n`, 1, 0) OVER `w`,`n`+LAG(`n`, 1, 0) OVER (`w`) FROM `fib`"}, + + // For window function concepts and syntax. + // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html + {`SELECT SUM(profit) OVER(PARTITION BY country) AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER (PARTITION BY `country`) AS `country_profit` FROM `sales`"}, + {`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT AVG(profit) OVER() AS country_profit FROM sales;`, true, "SELECT AVG(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT BIT_XOR(profit) OVER() AS country_profit FROM sales;`, true, "SELECT BIT_XOR(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT COUNT(profit) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT COUNT(ALL profit) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT COUNT(*) OVER() AS country_profit FROM sales;`, true, "SELECT COUNT(1) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT MAX(profit) OVER() AS country_profit FROM sales;`, true, "SELECT MAX(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT MIN(profit) OVER() AS country_profit FROM sales;`, true, "SELECT MIN(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT SUM(profit) OVER() AS country_profit FROM sales;`, true, "SELECT SUM(`profit`) OVER () AS `country_profit` FROM `sales`"}, + {`SELECT ROW_NUMBER() OVER(PARTITION BY country) AS row_num1 FROM sales;`, true, "SELECT ROW_NUMBER() OVER (PARTITION BY `country`) AS `row_num1` FROM `sales`"}, + {`SELECT ROW_NUMBER() OVER(PARTITION BY country, d ORDER BY year, product) AS row_num2 FROM sales;`, true, "SELECT ROW_NUMBER() OVER (PARTITION BY `country`, `d` ORDER BY `year`,`product`) AS `row_num2` FROM `sales`"}, + + // For window function frame specification. + // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-frames.html + {`SELECT SUM(val) OVER (PARTITION BY subject ORDER BY time ROWS UNBOUNDED PRECEDING) FROM t;`, true, "SELECT SUM(`val`) OVER (PARTITION BY `subject` ORDER BY `time` ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM `t`"}, + {`SELECT AVG(val) OVER (PARTITION BY subject ORDER BY time ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (PARTITION BY `subject` ORDER BY `time` ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM `t`"}, + {`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) FROM `t`"}, + {`SELECT AVG(val) OVER (ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (ROWS BETWEEN 1 PRECEDING AND UNBOUNDED FOLLOWING) FROM `t`"}, + {`SELECT AVG(val) OVER (RANGE BETWEEN INTERVAL 5 DAY PRECEDING AND INTERVAL '2:30' MINUTE_SECOND FOLLOWING) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN INTERVAL 5 DAY PRECEDING AND INTERVAL _UTF8MB4'2:30' MINUTE_SECOND FOLLOWING) FROM `t`"}, + {`SELECT AVG(val) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM `t`"}, + {`SELECT AVG(val) OVER (RANGE CURRENT ROW) FROM t;`, true, "SELECT AVG(`val`) OVER (RANGE BETWEEN CURRENT ROW AND CURRENT ROW) FROM `t`"}, + + // For named windows. + // See https://dev.mysql.com/doc/refman/8.0/en/window-functions-named-windows.html + {`SELECT RANK() OVER (w) FROM t WINDOW w AS (ORDER BY val);`, true, "SELECT RANK() OVER (`w`) FROM `t` WINDOW `w` AS (ORDER BY `val`)"}, + {`SELECT RANK() OVER w FROM t WINDOW w AS ();`, true, "SELECT RANK() OVER `w` FROM `t` WINDOW `w` AS ()"}, + {`SELECT FIRST_VALUE(year) OVER (w ORDER BY year) AS first FROM sales WINDOW w AS (PARTITION BY country);`, true, "SELECT FIRST_VALUE(`year`) OVER (`w` ORDER BY `year`) AS `first` FROM `sales` WINDOW `w` AS (PARTITION BY `country`)"}, + {`SELECT RANK() OVER (w1) FROM t WINDOW w1 AS (w2), w2 AS (), w3 AS (w1);`, true, "SELECT RANK() OVER (`w1`) FROM `t` WINDOW `w1` AS (`w2`),`w2` AS (),`w3` AS (`w1`)"}, + {`SELECT RANK() OVER w1 FROM t WINDOW w1 AS (w2), w2 AS (w3), w3 AS (w1);`, true, "SELECT RANK() OVER `w1` FROM `t` WINDOW `w1` AS (`w2`),`w2` AS (`w3`),`w3` AS (`w1`)"}, + + // For TSO functions + {`select tidb_parse_tso(1)`, true, "SELECT TIDB_PARSE_TSO(1)"}, + {`select tidb_parse_tso_logical(1)`, true, "SELECT TIDB_PARSE_TSO_LOGICAL(1)"}, + {`select tidb_bounded_staleness('2015-09-21 00:07:01', NOW())`, true, "SELECT TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, + {`select tidb_bounded_staleness(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())`, true, "SELECT TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, + {`select tidb_bounded_staleness('2015-09-21 00:07:01', '2021-04-27 11:26:13')`, true, "SELECT TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, + + {`select from_unixtime(404411537129996288)`, true, "SELECT FROM_UNIXTIME(404411537129996288)"}, + {`select from_unixtime(404411537129996288.22)`, true, "SELECT FROM_UNIXTIME(404411537129996288.22)"}, + } + RunTest(t, table, true) +} + +type windowFrameBoundChecker struct { + fb *ast.FrameBound + exprRc int + unit ast.TimeUnitType + t *testing.T +} + +// Enter implements ast.Visitor interface. +func (wfc *windowFrameBoundChecker) Enter(inNode ast.Node) (outNode ast.Node, skipChildren bool) { + if _, ok := inNode.(*ast.FrameBound); ok { + wfc.fb = inNode.(*ast.FrameBound) + if wfc.fb.Unit != ast.TimeUnitInvalid { + _, ok := wfc.fb.Expr.(ast.ValueExpr) + require.False(wfc.t, ok) + } + } + return inNode, false +} + +// Leave implements ast.Visitor interface. +func (wfc *windowFrameBoundChecker) Leave(inNode ast.Node) (node ast.Node, ok bool) { + if _, ok := inNode.(*ast.FrameBound); ok { + wfc.fb = nil + } + if wfc.fb != nil { + if inNode == wfc.fb.Expr { + wfc.exprRc++ + } + wfc.unit = wfc.fb.Unit + } + return inNode, true +} + +// For issue #51 +// See https://github.com/pingcap/parser/pull/51 for details +func TestVisitFrameBound(t *testing.T) { + p := parser.New() + p.EnableWindowFunc(true) + table := []struct { + s string + exprRc int + unit ast.TimeUnitType + }{ + {`SELECT AVG(val) OVER (RANGE INTERVAL 1+3 MINUTE_SECOND PRECEDING) FROM t;`, 1, ast.TimeUnitMinuteSecond}, + {`SELECT AVG(val) OVER (RANGE 5 PRECEDING) FROM t;`, 1, ast.TimeUnitInvalid}, + {`SELECT AVG(val) OVER () FROM t;`, 0, ast.TimeUnitInvalid}, + } + for _, tbl := range table { + stmt, err := p.ParseOneStmt(tbl.s, "", "") + require.NoError(t, err) + checker := windowFrameBoundChecker{t: t} + stmt.Accept(&checker) + require.Equal(t, tbl.exprRc, checker.exprRc) + require.Equal(t, tbl.unit, checker.unit) + } +} + +func TestFieldText(t *testing.T) { + p := parser.New() + stmts, _, err := p.Parse("select a from t", "", "") + require.NoError(t, err) + tmp := stmts[0].(*ast.SelectStmt) + require.Equal(t, "a", tmp.Fields.Fields[0].Text()) + + sqls := []string{ + "trace select a from t", + "trace format = 'row' select a from t", + "trace format = 'json' select a from t", + } + for _, sql := range sqls { + stmts, _, err = p.Parse(sql, "", "") + require.NoError(t, err) + traceStmt := stmts[0].(*ast.TraceStmt) + require.Equal(t, sql, traceStmt.Text()) + require.Equal(t, "select a from t", traceStmt.Stmt.Text()) + } +} + +// See https://github.com/pingcap/parser/issue/94 +func TestQuotedSystemVariables(t *testing.T) { + p := parser.New() + + st, err := p.ParseOneStmt( + "select @@Sql_Mode, @@`SQL_MODE`, @@session.`sql_mode`, @@global.`s ql``mode`, @@session.'sql\\nmode', @@local.\"sql\\\"mode\";", + "", + "", + ) + require.NoError(t, err) + ss := st.(*ast.SelectStmt) + expected := []*ast.VariableExpr{ + { + Name: "sql_mode", + IsGlobal: false, + IsSystem: true, + ExplicitScope: false, + }, + { + Name: "sql_mode", + IsGlobal: false, + IsSystem: true, + ExplicitScope: false, + }, + { + Name: "sql_mode", + IsGlobal: false, + IsSystem: true, + ExplicitScope: true, + }, + { + Name: "s ql`mode", + IsGlobal: true, + IsSystem: true, + ExplicitScope: true, + }, + { + Name: "sql\nmode", + IsGlobal: false, + IsSystem: true, + ExplicitScope: true, + }, + { + Name: `sql"mode`, + IsGlobal: false, + IsSystem: true, + ExplicitScope: true, + }, + } + + require.Len(t, ss.Fields.Fields, len(expected)) + for i, field := range ss.Fields.Fields { + ve := field.Expr.(*ast.VariableExpr) + comment := fmt.Sprintf("field %d, ve = %v", i, ve) + require.Equal(t, expected[i].Name, ve.Name, comment) + require.Equal(t, expected[i].IsGlobal, ve.IsGlobal, comment) + require.Equal(t, expected[i].IsSystem, ve.IsSystem, comment) + require.Equal(t, expected[i].ExplicitScope, ve.ExplicitScope, comment) + } +} + +// See https://github.com/pingcap/parser/issue/95 +func TestQuotedVariableColumnName(t *testing.T) { + p := parser.New() + + st, err := p.ParseOneStmt( + "select @abc, @`abc`, @'aBc', @\"AbC\", @6, @`6`, @'6', @\"6\", @@sql_mode, @@`sql_mode`, @;", + "", + "", + ) + require.NoError(t, err) + ss := st.(*ast.SelectStmt) + expected := []string{ + "@abc", + "@`abc`", + "@'aBc'", + `@"AbC"`, + "@6", + "@`6`", + "@'6'", + `@"6"`, + "@@sql_mode", + "@@`sql_mode`", + "@", + } + + require.Len(t, ss.Fields.Fields, len(expected)) + for i, field := range ss.Fields.Fields { + require.Equal(t, expected[i], field.Text()) + } +} + +func TestCharset(t *testing.T) { + p := parser.New() + + st, err := p.ParseOneStmt("ALTER SCHEMA GLOBAL DEFAULT CHAR SET utf8mb4", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.AlterDatabaseStmt)) + st, err = p.ParseOneStmt("ALTER DATABASE CHAR SET = utf8mb4", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.AlterDatabaseStmt)) + st, err = p.ParseOneStmt("ALTER DATABASE DEFAULT CHAR SET = utf8mb4", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.AlterDatabaseStmt)) +} + +func TestUnderscoreCharset(t *testing.T) { + p := parser.New() + tests := []struct { + cs string + parseFail bool + unSupport bool + }{ + {"utf8", false, false}, + {"gbk", false, true}, + {"ujis", false, true}, + {"gbk1", true, true}, + {"ujisx", true, true}, + } + for _, tt := range tests { + sql := fmt.Sprintf("select hex(_%s '3F')", tt.cs) + _, err := p.ParseOneStmt(sql, "", "") + if tt.parseFail { + require.EqualError(t, err, fmt.Sprintf("line 1 column %d near \"'3F')\" ", len(tt.cs)+17)) + } else if tt.unSupport { + require.EqualError(t, err, ast.ErrUnknownCharacterSet.GenWithStack("Unsupported character introducer: '%-.64s'", tt.cs).Error()) + } else { + require.NoError(t, err) + } + } +} + +func TestFulltextSearch(t *testing.T) { + p := parser.New() + + st, err := p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search')", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.SelectStmt)) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH() AGAINST('search')", "", "") + require.Error(t, err) + require.Nil(t, st) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST()", "", "") + require.Error(t, err) + require.Nil(t, st) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search' IN)", "", "") + require.Error(t, err) + require.Nil(t, st) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(content) AGAINST('search' IN BOOLEAN MODE WITH QUERY EXPANSION)", "", "") + require.Error(t, err) + require.Nil(t, st) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' IN NATURAL LANGUAGE MODE)", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.SelectStmt)) + writer := bytes.NewBufferString("") + st.(*ast.SelectStmt).Where.Format(writer) + require.Equal(t, "MATCH(title,content) AGAINST(\"search\")", writer.String()) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' IN BOOLEAN MODE)", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.SelectStmt)) + writer.Reset() + st.(*ast.SelectStmt).Where.Format(writer) + require.Equal(t, "MATCH(title,content) AGAINST(\"search\" IN BOOLEAN MODE)", writer.String()) + + st, err = p.ParseOneStmt("SELECT * FROM fulltext_test WHERE MATCH(title,content) AGAINST('search' WITH QUERY EXPANSION)", "", "") + require.NoError(t, err) + require.NotNil(t, st.(*ast.SelectStmt)) + writer.Reset() + st.(*ast.SelectStmt).Where.Format(writer) + require.Equal(t, "MATCH(title,content) AGAINST(\"search\" WITH QUERY EXPANSION)", writer.String()) +} + +func TestStartTransaction(t *testing.T) { + cases := []testCase{ + {"START TRANSACTION READ WRITE", true, "START TRANSACTION"}, + {"START TRANSACTION WITH CONSISTENT SNAPSHOT", true, "START TRANSACTION"}, + {"START TRANSACTION WITH CAUSAL CONSISTENCY ONLY", true, "START TRANSACTION WITH CAUSAL CONSISTENCY ONLY"}, + {"START TRANSACTION READ ONLY", true, "START TRANSACTION READ ONLY"}, + {"START TRANSACTION READ ONLY AS OF", false, ""}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP", false, ""}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP '2015-09-21 00:07:01'", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP _UTF8MB4'2015-09-21 00:07:01'"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', '2021-04-27 11:26:13')", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, + } + + RunTest(t, cases, false) +} + +func TestSignedInt64OutOfRange(t *testing.T) { + p := parser.New() + cases := []string{ + "recover table by job 18446744073709551612", + "recover table t 18446744073709551612", + "admin check index t idx (0, 18446744073709551612)", + "create user abc@def with max_queries_per_hour 18446744073709551612", + } + + for _, s := range cases { + _, err := p.ParseOneStmt(s, "", "") + require.Error(t, err) + require.Contains(t, err.Error(), "out of range") + } +} + +// CleanNodeText set the text of node and all child node empty. +// For test only. +func CleanNodeText(node ast.Node) { + var cleaner nodeTextCleaner + node.Accept(&cleaner) +} + +// nodeTextCleaner clean the text of a node and it's child node. +// For test only. +type nodeTextCleaner struct { +} + +func cleanPartition(n ast.Node) { + if p, ok := n.(*ast.PartitionOptions); ok && p != nil { + var tmpCleaner nodeTextCleaner + if p.Interval != nil { + p.Interval.SetText(nil, "") + p.Interval.SetOriginTextPosition(0) + p.Interval.IntervalExpr.Expr.Accept(&tmpCleaner) + if p.Interval.FirstRangeEnd != nil { + (*p.Interval.FirstRangeEnd).Accept(&tmpCleaner) + } + if p.Interval.LastRangeEnd != nil { + (*p.Interval.LastRangeEnd).Accept(&tmpCleaner) + } + } + } +} + +// Enter implements Visitor interface. +func (checker *nodeTextCleaner) Enter(in ast.Node) (out ast.Node, skipChildren bool) { + in.SetText(nil, "") + in.SetOriginTextPosition(0) + if v, ok := in.(ast.ValueExpr); ok && v != nil { + tpFlag := v.GetType().GetFlag() + if tpFlag&mysql.UnderScoreCharsetFlag != 0 { + // ignore underscore charset flag to let `'abc' = _utf8'abc'` pass + tpFlag ^= mysql.UnderScoreCharsetFlag + v.GetType().SetFlag(tpFlag) + } + } + + switch node := in.(type) { + case *ast.CreateTableStmt: + for _, opt := range node.Options { + switch opt.Tp { + case ast.TableOptionCharset: + opt.StrValue = strings.ToUpper(opt.StrValue) + case ast.TableOptionCollate: + opt.StrValue = strings.ToUpper(opt.StrValue) + } + } + for _, col := range node.Cols { + col.Tp.SetCharset(strings.ToUpper(col.Tp.GetCharset())) + col.Tp.SetCollate(strings.ToUpper(col.Tp.GetCollate())) + + for i, option := range col.Options { + if option.Tp == 0 && option.Expr == nil && !option.Stored && option.Refer == nil { + col.Options = append(col.Options[:i], col.Options[i+1:]...) + } + } + } + case *ast.DeleteStmt: + for _, tableHint := range node.TableHints { + tableHint.HintName.O = "" + } + case *ast.UpdateStmt: + for _, tableHint := range node.TableHints { + tableHint.HintName.O = "" + } + case *ast.Constraint: + if node.Option != nil { + if node.Option.KeyBlockSize == 0x0 && node.Option.Tp == 0 && node.Option.Comment == "" { + node.Option = nil + } + } + case *ast.FuncCallExpr: + node.FnName.O = strings.ToLower(node.FnName.O) + node.SetOriginTextPosition(0) + case *ast.AggregateFuncExpr: + node.F = strings.ToLower(node.F) + case *ast.SelectField: + node.Offset = 0 + case *test_driver.ValueExpr: + if node.Kind() == test_driver.KindMysqlDecimal { + _ = node.GetMysqlDecimal().FromString(node.GetMysqlDecimal().ToString()) + } + case *ast.GrantStmt: + var privs []*ast.PrivElem + for _, v := range node.Privs { + if v.Priv != 0 { + privs = append(privs, v) + } + } + node.Privs = privs + case *ast.AlterTableStmt: + var specs []*ast.AlterTableSpec + for _, v := range node.Specs { + if v.Tp != 0 && !(v.Tp == ast.AlterTableOption && len(v.Options) == 0) { + specs = append(specs, v) + } + } + node.Specs = specs + case *ast.Join: + node.ExplicitParens = false + case *ast.ColumnDef: + node.Tp.CleanElemIsBinaryLit() + case *ast.PartitionOptions: + cleanPartition(node) + } + return in, false +} + +// Leave implements Visitor interface. +func (checker *nodeTextCleaner) Leave(in ast.Node) (out ast.Node, ok bool) { + return in, true +} + +// For index advisor +func TestIndexAdviseStmt(t *testing.T) { + table := []testCase{ + {"INDEX ADVISE INFILE '/tmp/t.sql'", true, "INDEX ADVISE INFILE '/tmp/t.sql'"}, + {"INDEX ADVISE LOCAL INFILE '/tmp/t.sql'", true, "INDEX ADVISE LOCAL INFILE '/tmp/t.sql'"}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1", false, ""}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_IDXNUM PER_DB 4 PER_TABLE 8", false, ""}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 4", false, ""}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_DB 4", false, ""}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4", false, ""}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 LINES STARTING BY 'ab' TERMINATED BY '\n'", false, ""}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 LINES STARTING BY 'ab' TERMINATED BY 'cd'", false, ""}, + + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 4 LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_DB 4 LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 3 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", true, "INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES 0 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'"}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY '\n'", false, ""}, + {"INDEX ADVISE INFILE '/tmp/t.sql' MAX_MINUTES -1 MAX_IDXNUM PER_TABLE 8 PER_DB 4 LINES STARTING BY 'ab' TERMINATED BY 'cd'", false, ""}, + } + + RunTest(t, table, false) +} + +// For BRIE +func TestBRIE(t *testing.T) { + table := []testCase{ + {"BACKUP DATABASE a TO 'local:///tmp/archive01/'", true, "BACKUP DATABASE `a` TO 'local:///tmp/archive01/'"}, + {"BACKUP SCHEMA a TO 'local:///tmp/archive01/'", true, "BACKUP DATABASE `a` TO 'local:///tmp/archive01/'"}, + {"BACKUP DATABASE a,b,c TO 'noop://'", true, "BACKUP DATABASE `a`, `b`, `c` TO 'noop://'"}, + {"BACKUP DATABASE a.b TO 'noop://'", false, ""}, + {"BACKUP DATABASE * TO 'noop://'", true, "BACKUP DATABASE * TO 'noop://'"}, + {"BACKUP DATABASE *, a TO 'noop://'", false, ""}, + {"BACKUP DATABASE a, * TO 'noop://'", false, ""}, + {"BACKUP DATABASE TO 'noop://'", false, ""}, + {"BACKUP TABLE a TO 'noop://'", true, "BACKUP TABLE `a` TO 'noop://'"}, + {"BACKUP TABLE a.b TO 'noop://'", true, "BACKUP TABLE `a`.`b` TO 'noop://'"}, + {"BACKUP TABLE a.b,c.d,e TO 'noop://'", true, "BACKUP TABLE `a`.`b`, `c`.`d`, `e` TO 'noop://'"}, + {"BACKUP TABLE a.* TO 'noop://'", false, ""}, + {"BACKUP TABLE * TO 'noop://'", false, ""}, + {"BACKUP TABLE TO 'noop://'", false, ""}, + {"RESTORE DATABASE * FROM 's3://bucket/path/'", true, "RESTORE DATABASE * FROM 's3://bucket/path/'"}, + + {"BACKUP DATABASE * TO 'noop://' LAST_BACKUP = '2020-02-02 14:14:14'", true, "BACKUP DATABASE * TO 'noop://' LAST_BACKUP = '2020-02-02 14:14:14'"}, + {"BACKUP DATABASE * TO 'noop://' LAST_BACKUP = 1234567890", true, "BACKUP DATABASE * TO 'noop://' LAST_BACKUP = 1234567890"}, + + {"backup database * to 'noop://' rate_limit 500 MB/second snapshot 5 minute ago", true, "BACKUP DATABASE * TO 'noop://' RATE_LIMIT = 500 MB/SECOND SNAPSHOT = 300000000 MICROSECOND AGO"}, + {"backup database * to 'noop://' snapshot = '2020-03-18 18:13:54'", true, "BACKUP DATABASE * TO 'noop://' SNAPSHOT = '2020-03-18 18:13:54'"}, + {"backup database * to 'noop://' snapshot = 1234567890", true, "BACKUP DATABASE * TO 'noop://' SNAPSHOT = 1234567890"}, + {"restore table g from 'noop://' concurrency 40 checksum 0 online 1", true, "RESTORE TABLE `g` FROM 'noop://' CONCURRENCY = 40 CHECKSUM = OFF ONLINE = 1"}, + { + "backup table x to 's3://bucket/path/?endpoint=https://test-cluster-s3.local&access-key=aaaaaaaaa&secret-access-key=bbbbbbbb&force-path-style=1'", + true, + "BACKUP TABLE `x` TO 's3://bucket/path/?endpoint=https://test-cluster-s3.local&access-key=aaaaaaaaa&secret-access-key=bbbbbbbb&force-path-style=1'", + }, + { + "backup database * to 's3://bucket/path/?provider=alibaba®ion=us-west-9&storage-class=glacier&sse=AES256&acl=authenticated-read&use-accelerate-endpoint=1' send_credentials_to_tikv = 1", + true, + "BACKUP DATABASE * TO 's3://bucket/path/?provider=alibaba®ion=us-west-9&storage-class=glacier&sse=AES256&acl=authenticated-read&use-accelerate-endpoint=1' SEND_CREDENTIALS_TO_TIKV = 1", + }, + { + "restore database * from 'gcs://bucket/path/?endpoint=https://test-cluster.gcs.local&storage-class=coldline&predefined-acl=OWNER&credentials-file=/data/private/creds.json'", + true, + "RESTORE DATABASE * FROM 'gcs://bucket/path/?endpoint=https://test-cluster.gcs.local&storage-class=coldline&predefined-acl=OWNER&credentials-file=/data/private/creds.json'", + }, + {"restore table g from 'noop://' checksum off", true, "RESTORE TABLE `g` FROM 'noop://' CHECKSUM = OFF"}, + {"restore table g from 'noop://' checksum optional", true, "RESTORE TABLE `g` FROM 'noop://' CHECKSUM = OPTIONAL"}, + {"backup logs to 'noop://'", true, "BACKUP LOGS TO 'noop://'"}, + {"backup logs to 'noop://' start_ts='20220304'", true, "BACKUP LOGS TO 'noop://' START_TS = '20220304'"}, + {"pause backup logs", true, "PAUSE BACKUP LOGS"}, + {"pause backup logs gc_ttl='20220304'", true, "PAUSE BACKUP LOGS GC_TTL = '20220304'"}, + {"resume backup logs", true, "RESUME BACKUP LOGS"}, + {"show backup logs status", true, "SHOW BACKUP LOGS STATUS"}, + {"show backup logs metadata from 'noop://'", true, "SHOW BACKUP LOGS METADATA FROM 'noop://'"}, + {"show br job 1234", true, "SHOW BR JOB 1234"}, + {"show br job query 1234", true, "SHOW BR JOB QUERY 1234"}, + {"cancel br job 1234", true, "CANCEL BR JOB 1234"}, + {"purge backup logs from 'noop://'", true, "PURGE BACKUP LOGS FROM 'noop://'"}, + {"purge backup logs from 'noop://' until_ts='2012122304'", true, "PURGE BACKUP LOGS FROM 'noop://' UNTIL_TS = '2012122304'"}, + {"restore point from 'noop://log_backup'", true, "RESTORE POINT FROM 'noop://log_backup'"}, + {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log'"}, + {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log' restored_ts='20230123'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log' RESTORED_TS = '20230123'"}, + {"restore point from 'noop://log_backup' full_backup_storage='noop://full_log' start_ts='20230101' restored_ts='20230123'", true, "RESTORE POINT FROM 'noop://log_backup' FULL_BACKUP_STORAGE = 'noop://full_log' START_TS = '20230101' RESTORED_TS = '20230123'"}, + } + + RunTest(t, table, false) +} + +func TestStatisticsOps(t *testing.T) { + table := []testCase{ + {"create statistics stats1 (cardinality) on t(a,b,c)", true, "CREATE STATISTICS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, + {"create statistics stats2 (dependency) on t(a,b)", true, "CREATE STATISTICS `stats2` (DEPENDENCY) ON `t`(`a`, `b`)"}, + {"create statistics stats3 (correlation) on t(a,b)", true, "CREATE STATISTICS `stats3` (CORRELATION) ON `t`(`a`, `b`)"}, + {"create statistics stats3 on t(a,b)", false, ""}, + {"create statistics if not exists stats1 (cardinality) on t(a,b,c)", true, "CREATE STATISTICS IF NOT EXISTS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, + {"create statistics if not exists stats2 (dependency) on t(a,b)", true, "CREATE STATISTICS IF NOT EXISTS `stats2` (DEPENDENCY) ON `t`(`a`, `b`)"}, + {"create statistics if not exists stats3 (correlation) on t(a,b)", true, "CREATE STATISTICS IF NOT EXISTS `stats3` (CORRELATION) ON `t`(`a`, `b`)"}, + {"create statistics if not exists stats3 on t(a,b)", false, ""}, + {"create statistics stats1(cardinality) on t(a,b,c)", true, "CREATE STATISTICS `stats1` (CARDINALITY) ON `t`(`a`, `b`, `c`)"}, + {"drop statistics stats1", true, "DROP STATISTICS `stats1`"}, + } + RunTest(t, table, false) + + p := parser.New() + sms, _, err := p.Parse("create statistics if not exists stats1 (cardinality) on t(a,b,c)", "", "") + require.NoError(t, err) + v, ok := sms[0].(*ast.CreateStatisticsStmt) + require.True(t, ok) + require.True(t, v.IfNotExists) + require.Equal(t, "stats1", v.StatsName) + require.Equal(t, ast.StatsTypeCardinality, v.StatsType) + require.Equal(t, model.CIStr{O: "t", L: "t"}, v.Table.Name) + require.Len(t, v.Columns, 3) + require.Equal(t, model.CIStr{O: "a", L: "a"}, v.Columns[0].Name) + require.Equal(t, model.CIStr{O: "b", L: "b"}, v.Columns[1].Name) + require.Equal(t, model.CIStr{O: "c", L: "c"}, v.Columns[2].Name) +} + +func TestHighNotPrecedenceMode(t *testing.T) { + p := parser.New() + var sb strings.Builder + + sms, _, err := p.Parse("SELECT NOT 1 BETWEEN -5 AND 5", "", "") + require.NoError(t, err) + v, ok := sms[0].(*ast.SelectStmt) + require.True(t, ok) + v1, ok := v.Fields.Fields[0].Expr.(*ast.UnaryOperationExpr) + require.True(t, ok) + require.Equal(t, opcode.Not, v1.Op) + err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) + require.NoError(t, err) + restoreSQL := sb.String() + require.Equal(t, "SELECT NOT 1 BETWEEN -5 AND 5", restoreSQL) + sb.Reset() + + sms, _, err = p.Parse("SELECT !1 BETWEEN -5 AND 5", "", "") + require.NoError(t, err) + v, ok = sms[0].(*ast.SelectStmt) + require.True(t, ok) + _, ok = v.Fields.Fields[0].Expr.(*ast.BetweenExpr) + require.True(t, ok) + err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) + require.NoError(t, err) + restoreSQL = sb.String() + require.Equal(t, "SELECT !1 BETWEEN -5 AND 5", restoreSQL) + sb.Reset() + + p = parser.New() + p.SetSQLMode(mysql.ModeHighNotPrecedence) + sms, _, err = p.Parse("SELECT NOT 1 BETWEEN -5 AND 5", "", "") + require.NoError(t, err) + v, ok = sms[0].(*ast.SelectStmt) + require.True(t, ok) + _, ok = v.Fields.Fields[0].Expr.(*ast.BetweenExpr) + require.True(t, ok) + err = sms[0].Restore(NewRestoreCtx(DefaultRestoreFlags, &sb)) + require.NoError(t, err) + restoreSQL = sb.String() + require.Equal(t, "SELECT !1 BETWEEN -5 AND 5", restoreSQL) +} + +// For CTE +func TestCTE(t *testing.T) { + table := []testCase{ + {"WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` (col1, col2) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` AS (SELECT 1,2), cte2 as (select 3) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2), `cte2` AS (SELECT 3) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH RECURSIVE cte (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5)SELECT * FROM cte;", true, "WITH RECURSIVE `cte` (`n`) AS (SELECT 1 UNION ALL SELECT `n`+1 FROM `cte` WHERE `n`<5) SELECT * FROM `cte`"}, + {"with cte(a) as (select 1) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) UPDATE (`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a`=`cte`.`a`"}, + {"with cte(a) as (select 1) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) DELETE `t` FROM (`t`) JOIN `cte` WHERE `t`.`a`=`cte`.`a`"}, + {"WITH cte1 AS (SELECT 1) SELECT * FROM (WITH cte2 AS (SELECT 2) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT 1) SELECT * FROM (WITH `cte2` AS (SELECT 2) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, + {"WITH cte AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, + {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `t`) TABLE `cte`"}, + {"with cte as (select 1) select 1 union with cte as (select 1) select * from cte;", false, ""}, + {"with cte as (select 1) (select 1);", true, "WITH `cte` AS (SELECT 1) (SELECT 1)"}, + {"with cte as (select 1) (select 1 union select 1)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT 1)"}, + {"select * from (with cte as (select 1) select 1 union select 2) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT 1) SELECT 1 UNION SELECT 2) AS `qn`"}, + {"select * from t where 1 > (with cte as (select 2) select * from cte)", true, "SELECT * FROM `t` WHERE 1>(WITH `cte` AS (SELECT 2) SELECT * FROM `cte`)"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte` UNION SELECT `n`+2 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) (select n+1 from cte)) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) (SELECT `n`+1 FROM `cte`)) UNION SELECT 1"}, + } + + RunTest(t, table, false) +} + +// For CTE Merge +func TestCTEMerge(t *testing.T) { + table := []testCase{ + {"WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` (col1, col2) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT 1,2 UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` AS (SELECT 1,2), cte2 as (select 3) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT 1,2), `cte2` AS (SELECT 3) SELECT `col1`,`col2` FROM `cte`"}, + {"with cte(a) as (select 1) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) UPDATE (`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a`=`cte`.`a`"}, + {"with cte(a) as (select 1) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT 1) DELETE `t` FROM (`t`) JOIN `cte` WHERE `t`.`a`=`cte`.`a`"}, + {"WITH cte1 AS (SELECT 1) SELECT * FROM (WITH cte2 AS (SELECT 2) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT 1) SELECT * FROM (WITH `cte2` AS (SELECT 2) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, + {"WITH cte AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT 1) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, + {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `t`) TABLE `cte`"}, + {"with cte as (select 1) select 1 union with cte as (select 1) select * from cte;", false, ""}, + {"with cte as (select 1) (select 1);", true, "WITH `cte` AS (SELECT 1) (SELECT 1)"}, + {"with cte as (select 1) (select 1 union select 1)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT 1)"}, + {"select * from (with cte as (select 1) select 1 union select 2) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT 1) SELECT 1 UNION SELECT 2) AS `qn`"}, + {"select * from t where 1 > (with cte as (select 2) select * from cte)", true, "SELECT * FROM `t` WHERE 1>(WITH `cte` AS (SELECT 2) SELECT * FROM `cte`)"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte` UNION SELECT `n`+2 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) select n+1 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) SELECT `n`+1 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select 1 ) (select n+1 from cte)) union select 1", true, "(WITH `cte` (`n`) AS (SELECT 1) (SELECT `n`+1 FROM `cte`)) UNION SELECT 1"}, + } + + RunTest(t, table, false) +} + +func TestAsOfClause(t *testing.T) { + table := []testCase{ + {"SELECT * FROM `t` AS /* comment */ a;", true, "SELECT * FROM `t` AS `a`"}, + {"SELECT * FROM `t` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW());", true, "SELECT * FROM `t` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, + {"select * from `t` as of timestamp '2021-04-15 00:00:00'", true, "SELECT * FROM `t` AS OF TIMESTAMP _UTF8MB4'2021-04-15 00:00:00'"}, + {"SELECT * FROM (`a` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())) JOIN `b` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW());", true, "SELECT * FROM (`a` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())) JOIN `b` AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, + {"INSERT INTO `employees` (SELECT * FROM `employees` AS OF TIMESTAMP (DATE_SUB(NOW(), INTERVAL _UTF8MB4'60' MINUTE)) NOT IN (SELECT * FROM `employees`))", true, "INSERT INTO `employees` (SELECT * FROM `employees` AS OF TIMESTAMP (DATE_SUB(NOW(), INTERVAL _UTF8MB4'60' MINUTE)) NOT IN (SELECT * FROM `employees`))"}, + {"SET TRANSACTION READ ONLY as of timestamp '2021-04-21 00:42:12'", true, "SET @@SESSION.`tx_read_ts`=_UTF8MB4'2021-04-21 00:42:12'"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP '2015-09-21 00:07:01'", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP _UTF8MB4'2015-09-21 00:07:01'"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', NOW())"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(DATE_SUB(NOW(), INTERVAL 3 SECOND), NOW())"}, + {"START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', '2021-04-27 11:26:13')", true, "START TRANSACTION READ ONLY AS OF TIMESTAMP TIDB_BOUNDED_STALENESS(_UTF8MB4'2015-09-21 00:07:01', _UTF8MB4'2021-04-27 11:26:13')"}, + } + RunTest(t, table, false) +} + +// For `PARTITION BY [LINEAR] KEY ALGORITHM` syntax +func TestPartitionKeyAlgorithm(t *testing.T) { + table := []testCase{ + {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 1 (c1,c2) PARTITIONS 4", true, "CREATE TABLE `t` (`c1` INT,`c2` INT) PARTITION BY LINEAR KEY ALGORITHM = 1 (`c1`,`c2`) PARTITIONS 4"}, + {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = -1 (c1,c2) PARTITIONS 4", false, ""}, + {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 0 (c1,c2) PARTITIONS 4", false, ""}, + {"CREATE TABLE t (c1 integer ,c2 integer) PARTITION BY LINEAR KEY ALGORITHM = 3 (c1,c2) PARTITIONS 4", false, ""}, + } + + RunTest(t, table, false) +} + +// server side help syntax +func TestHelp(t *testing.T) { + table := []testCase{ + {"HELP 'select'", true, "HELP 'select'"}, + } + + RunTest(t, table, false) +} + +func TestWithoutCharsetFlags(t *testing.T) { + type testCaseWithFlag struct { + src string + ok bool + restore string + flag RestoreFlags + } + + flag := RestoreStringSingleQuotes | RestoreSpacesAroundBinaryOperation | RestoreBracketAroundBinaryOperation | RestoreNameBackQuotes + cases := []testCaseWithFlag{ + {"select 'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, + {"select _utf8'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, + {"select _utf8mb4'a'", true, "SELECT 'a'", flag | RestoreStringWithoutCharset}, + {"select _utf8 X'D0B1'", true, "SELECT x'd0b1'", flag | RestoreStringWithoutCharset}, + + {"select _utf8mb4'a'", true, "SELECT 'a'", flag | RestoreStringWithoutDefaultCharset}, + {"select _utf8'a'", true, "SELECT _utf8'a'", flag | RestoreStringWithoutDefaultCharset}, + {"select _utf8'a'", true, "SELECT _utf8'a'", flag | RestoreStringWithoutDefaultCharset}, + {"select _utf8 X'D0B1'", true, "SELECT _utf8 x'd0b1'", flag | RestoreStringWithoutDefaultCharset}, + } + + p := parser.New() + p.EnableWindowFunc(false) + for _, tbl := range cases { + stmts, _, err := p.Parse(tbl.src, "", "") + if !tbl.ok { + require.Error(t, err) + continue + } + require.NoError(t, err) + // restore correctness test + var sb strings.Builder + restoreSQLs := "" + for _, stmt := range stmts { + sb.Reset() + ctx := NewRestoreCtx(tbl.flag, &sb) + ctx.DefaultDB = "test" + err = stmt.Restore(ctx) + require.NoError(t, err) + restoreSQL := sb.String() + if restoreSQLs != "" { + restoreSQLs += "; " + } + restoreSQLs += restoreSQL + } + require.Equal(t, tbl.restore, restoreSQLs) + } +} + +func TestRestoreBinOpWithBrackets(t *testing.T) { + cases := []testCase{ + {"select mod(a+b, 4)+1", true, "SELECT (((`a` + `b`) % 4) + 1)"}, + {"select mod( year(a) - abs(weekday(a) + dayofweek(a)), 4) + 1", true, "SELECT (((year(`a`) - abs((weekday(`a`) + dayofweek(`a`)))) % 4) + 1)"}, + } + + p := parser.New() + p.EnableWindowFunc(false) + for _, tbl := range cases { + _, _, err := p.Parse(tbl.src, "", "") + comment := fmt.Sprintf("source %v", tbl.src) + if !tbl.ok { + require.Error(t, err, comment) + continue + } + require.NoError(t, err, comment) + // restore correctness test + if tbl.ok { + var sb strings.Builder + comment := fmt.Sprintf("source %v", tbl.src) + stmts, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err, comment) + restoreSQLs := "" + for _, stmt := range stmts { + sb.Reset() + ctx := NewRestoreCtx(RestoreStringSingleQuotes|RestoreSpacesAroundBinaryOperation|RestoreBracketAroundBinaryOperation|RestoreStringWithoutCharset|RestoreNameBackQuotes, &sb) + ctx.DefaultDB = "test" + err = stmt.Restore(ctx) + require.NoError(t, err, comment) + restoreSQL := sb.String() + comment = fmt.Sprintf("source %v; restore %v", tbl.src, restoreSQL) + if restoreSQLs != "" { + restoreSQLs += "; " + } + restoreSQLs += restoreSQL + } + comment = fmt.Sprintf("restore %v; expect %v", restoreSQLs, tbl.restore) + require.Equal(t, tbl.restore, restoreSQLs, comment) + } + } +} + +// For CTE bindings. +func TestCTEBindings(t *testing.T) { + table := []testCase{ + {"WITH `cte` AS (SELECT * from t) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` (col1, col2) AS (SELECT * from t UNION ALL SELECT 3,4) SELECT col1, col2 FROM cte;", true, "WITH `cte` (`col1`, `col2`) AS (SELECT * FROM `test`.`t` UNION ALL SELECT 3,4) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH `cte` AS (SELECT * from t), cte2 as (select * from cte) SELECT `col1`,`col2` FROM `cte`", true, "WITH `cte` AS (SELECT * FROM `test`.`t`), `cte2` AS (SELECT * FROM `cte`) SELECT `col1`,`col2` FROM `cte`"}, + {"WITH RECURSIVE cte (n) AS ( SELECT * from t UNION ALL SELECT n + 1 FROM cte WHERE n < 5)SELECT * FROM cte;", true, "WITH RECURSIVE `cte` (`n`) AS (SELECT * FROM `test`.`t` UNION ALL SELECT `n` + 1 FROM `cte` WHERE `n` < 5) SELECT * FROM `cte`"}, + {"with cte(a) as (select * from t) update t, cte set t.a=1 where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT * FROM `test`.`t`) UPDATE (`test`.`t`) JOIN `cte` SET `t`.`a`=1 WHERE `t`.`a` = `cte`.`a`"}, + {"with cte(a) as (select * from t) delete t from t, cte where t.a=cte.a;", true, "WITH `cte` (`a`) AS (SELECT * FROM `test`.`t`) DELETE `test`.`t` FROM (`test`.`t`) JOIN `cte` WHERE `t`.`a` = `cte`.`a`"}, + {"WITH cte1 AS (SELECT * from t) SELECT * FROM (WITH cte2 AS (SELECT * from cte1) SELECT * FROM cte2 JOIN cte1) AS dt;", true, "WITH `cte1` AS (SELECT * FROM `test`.`t`) SELECT * FROM (WITH `cte2` AS (SELECT * FROM `cte1`) SELECT * FROM `cte2` JOIN `cte1`) AS `dt`"}, + {"WITH cte AS (SELECT * from t) SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM cte;", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT /*+ MAX_EXECUTION_TIME(1000)*/ * FROM `cte`"}, + {"with cte as (table t) table cte;", true, "WITH `cte` AS (TABLE `test`.`t`) TABLE `cte`"}, + {"with cte as (select * from t) select 1 union with cte as (select * from t) select * from cte;", false, ""}, + {"with cte as (select * from t) (select * from t);", true, "WITH `cte` AS (SELECT * FROM `test`.`t`) (SELECT * FROM `test`.`t`)"}, + {"with cte as (select 1) (select 1 union select * from t)", true, "WITH `cte` AS (SELECT 1) (SELECT 1 UNION SELECT * FROM `test`.`t`)"}, + {"select * from (with cte as (select * from t) select 1 union select * from t) qn", true, "SELECT * FROM (WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT 1 UNION SELECT * FROM `test`.`t`) AS `qn`"}, + {"select * from t where 1 > (with cte as (select * from t) select * from cte)", true, "SELECT * FROM `test`.`t` WHERE 1 > (WITH `cte` AS (SELECT * FROM `test`.`t`) SELECT * FROM `cte`)"}, + {"( with cte(n) as ( select * from t ) select n+1 from cte union select n+2 from cte) union select 1", true, "(WITH `cte` (`n`) AS (SELECT * FROM `test`.`t`) SELECT `n` + 1 FROM `cte` UNION SELECT `n` + 2 FROM `cte`) UNION SELECT 1"}, + {"( with cte(n) as ( select * from t ) select n+1 from cte) union select * from t", true, "(WITH `cte` (`n`) AS (SELECT * FROM `test`.`t`) SELECT `n` + 1 FROM `cte`) UNION SELECT * FROM `test`.`t`"}, + {"with cte as (select * from t union select * from cte) select * from cte", true, "WITH `cte` AS (SELECT * FROM `test`.`t` UNION SELECT * FROM `test`.`cte`) SELECT * FROM `cte`"}, + } + + p := parser.New() + p.EnableWindowFunc(false) + for _, tbl := range table { + _, _, err := p.Parse(tbl.src, "", "") + comment := fmt.Sprintf("source %v", tbl.src) + if !tbl.ok { + require.Error(t, err, comment) + continue + } + require.NoError(t, err, comment) + // restore correctness test + if tbl.ok { + var sb strings.Builder + comment := fmt.Sprintf("source %v", tbl.src) + stmts, _, err := p.Parse(tbl.src, "", "") + require.NoError(t, err, comment) + restoreSQLs := "" + for _, stmt := range stmts { + sb.Reset() + ctx := NewRestoreCtx(RestoreStringSingleQuotes|RestoreSpacesAroundBinaryOperation|RestoreStringWithoutCharset|RestoreNameBackQuotes, &sb) + ctx.DefaultDB = "test" + err = stmt.Restore(ctx) + require.NoError(t, err, comment) + restoreSQL := sb.String() + comment = fmt.Sprintf("source %v; restore %v", tbl.src, restoreSQL) + if restoreSQLs != "" { + restoreSQLs += "; " + } + restoreSQLs += restoreSQL + } + comment = fmt.Sprintf("restore %v; expect %v", restoreSQLs, tbl.restore) + require.Equal(t, tbl.restore, restoreSQLs, comment) + } + } +} + +func TestPlanReplayer(t *testing.T) { + table := []testCase{ + {"PLAN REPLAYER DUMP EXPLAIN SELECT a FROM t", true, "PLAN REPLAYER DUMP EXPLAIN SELECT `a` FROM `t`"}, + {"PLAN REPLAYER DUMP EXPLAIN SELECT * FROM t WHERE a > 10", true, "PLAN REPLAYER DUMP EXPLAIN SELECT * FROM `t` WHERE `a`>10"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM t WHERE a > 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT * FROM `t` WHERE `a`>10"}, + {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE a > 10 and t < 1 ORDER BY t LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE `a`>10 AND `t`<1 ORDER BY `t` LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE a > 10 and t < 1 ORDER BY t LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE `a`>10 AND `t`<1 ORDER BY `t` LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE a > 10 and t < 1 LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY WHERE `a`>10 AND `t`<1 LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE a > 10 and t < 1 LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY WHERE `a`>10 AND `t`<1 LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY LIMIT 10", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY LIMIT 10"}, + {"PLAN REPLAYER DUMP EXPLAIN SLOW QUERY", true, "PLAN REPLAYER DUMP EXPLAIN SLOW QUERY"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE SLOW QUERY"}, + {"PLAN REPLAYER LOAD '/tmp/sdfaalskdjf.zip'", true, "PLAN REPLAYER LOAD '/tmp/sdfaalskdjf.zip'"}, + {"PLAN REPLAYER DUMP EXPLAIN 'sql.txt'", true, "PLAN REPLAYER DUMP EXPLAIN 'sql.txt'"}, + {"PLAN REPLAYER DUMP EXPLAIN ANALYZE 'sql.txt'", true, "PLAN REPLAYER DUMP EXPLAIN ANALYZE 'sql.txt'"}, + {"PLAN REPLAYER CAPTURE '123' '123'", true, "PLAN REPLAYER CAPTURE '123' '123'"}, + {"PLAN REPLAYER CAPTURE REMOVE '123' '123'", true, "PLAN REPLAYER CAPTURE REMOVE '123' '123'"}, + } + RunTest(t, table, false) + + p := parser.New() + sms, _, err := p.Parse("PLAN REPLAYER DUMP EXPLAIN SELECT a FROM t", "", "") + require.NoError(t, err) + v, ok := sms[0].(*ast.PlanReplayerStmt) + require.True(t, ok) + require.Equal(t, "SELECT a FROM t", v.Stmt.Text()) + require.False(t, v.Analyze) + + sms, _, err = p.Parse("PLAN REPLAYER DUMP EXPLAIN ANALYZE SELECT a FROM t", "", "") + require.NoError(t, err) + v, ok = sms[0].(*ast.PlanReplayerStmt) + require.True(t, ok) + require.Equal(t, "SELECT a FROM t", v.Stmt.Text()) + require.True(t, v.Analyze) +} + +func TestGBKEncoding(t *testing.T) { + p := parser.New() + gbkEncoding, _ := charset.Lookup("gbk") + encoder := gbkEncoding.NewEncoder() + sql, err := encoder.String("create table 测试表 (测试列 varchar(255) default 'GBK测试用例');") + require.NoError(t, err) + + stmt, _, err := p.ParseSQL(sql) + require.NoError(t, err) + checker := &gbkEncodingChecker{} + _, _ = stmt[0].Accept(checker) + require.NotEqual(t, "测试表", checker.tblName) + require.NotEqual(t, "测试列", checker.colName) + + gbkOpt := parser.CharsetClient("gbk") + stmt, _, err = p.ParseSQL(sql, gbkOpt) + require.NoError(t, err) + _, _ = stmt[0].Accept(checker) + require.Equal(t, "测试表", checker.tblName) + require.Equal(t, "测试列", checker.colName) + require.Equal(t, "GBK测试用例", checker.expr) + + _, _, err = p.ParseSQL("select _gbk '\xc6\x5c' from dual;") + require.Error(t, err) + + for _, test := range []struct { + sql string + err bool + }{ + {"select '\xc6\x5c' from `\xab\x60`;", false}, + {`prepare p1 from "insert into t values ('中文');";`, false}, + {"select '啊';", false}, + {"create table t1(s set('a一','b二','c三'));", false}, + {"insert into t3 values('一a');", false}, + {"select '\xa5\x5c'", false}, + {"select '''\xa5\x5c'", false}, + {"select ```\xa5\x5c`", false}, + {"select '\x65\x5c'", true}, + } { + _, _, err = p.ParseSQL(test.sql, gbkOpt) + if test.err { + require.Error(t, err, test.sql) + } else { + require.NoError(t, err, test.sql) + } + } +} + +type gbkEncodingChecker struct { + tblName string + colName string + expr string +} + +func (g *gbkEncodingChecker) Enter(n ast.Node) (node ast.Node, skipChildren bool) { + if tn, ok := n.(*ast.TableName); ok { + g.tblName = tn.Name.O + return n, false + } + if cn, ok := n.(*ast.ColumnName); ok { + g.colName = cn.Name.O + return n, false + } + if c, ok := n.(*ast.ColumnOption); ok { + if ve, ok := c.Expr.(ast.ValueExpr); ok { + g.expr = ve.GetString() + return n, false + } + } + return n, false +} + +func (g *gbkEncodingChecker) Leave(n ast.Node) (node ast.Node, ok bool) { + return n, true +} + +func TestInsertStatementMemoryAllocation(t *testing.T) { + sql := "insert t values (1)" + strings.Repeat(",(1)", 1000) + var oldStats, newStats runtime.MemStats + runtime.ReadMemStats(&oldStats) + _, err := parser.New().ParseOneStmt(sql, "", "") + require.NoError(t, err) + runtime.ReadMemStats(&newStats) + require.Less(t, int(newStats.TotalAlloc-oldStats.TotalAlloc), 1024*500) +} + +func TestCharsetIntroducer(t *testing.T) { + p := parser.New() + defer charset.RemoveCharset("gbk") + // `_gbk` is treated as a character set. + _, _, err := p.Parse("select _gbk 'a';", "", "") + require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") + _, _, err = p.Parse("select _gbk 0x1234;", "", "") + require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") + _, _, err = p.Parse("select _gbk 0b101001;", "", "") + require.EqualError(t, err, "[ddl:1115]Unsupported character introducer: 'gbk'") +} + +func TestNonTransactionalDML(t *testing.T) { + cases := []testCase{ + // deletes + {"batch on c limit 10 delete from t where c = 10", true, + "BATCH ON `c` LIMIT 10 DELETE FROM `t` WHERE `c`=10"}, + {"batch on c limit 10 dry run delete from t where c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN DELETE FROM `t` WHERE `c`=10"}, + {"batch on c limit 10 dry run query delete from t where c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN QUERY DELETE FROM `t` WHERE `c`=10"}, + {"batch limit 10 delete from t where c = 10", true, + "BATCH LIMIT 10 DELETE FROM `t` WHERE `c`=10"}, + {"batch limit 10 dry run delete from t where c = 10", true, + "BATCH LIMIT 10 DRY RUN DELETE FROM `t` WHERE `c`=10"}, + {"batch limit 10 dry run query delete from t where c = 10", true, + "BATCH LIMIT 10 DRY RUN QUERY DELETE FROM `t` WHERE `c`=10"}, + // updates + {"batch on c limit 10 update t set c = 10", true, + "BATCH ON `c` LIMIT 10 UPDATE `t` SET `c`=10"}, + {"batch on c limit 10 dry run update t set c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN UPDATE `t` SET `c`=10"}, + {"batch on c limit 10 dry run query update t set c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN QUERY UPDATE `t` SET `c`=10"}, + {"batch limit 10 update t set c = 10", true, + "BATCH LIMIT 10 UPDATE `t` SET `c`=10"}, + {"batch limit 10 dry run update t set c = 10", true, + "BATCH LIMIT 10 DRY RUN UPDATE `t` SET `c`=10"}, + {"batch limit 10 dry run query update t set c = 10", true, + "BATCH LIMIT 10 DRY RUN QUERY UPDATE `t` SET `c`=10"}, + // inserts + {"batch on c limit 10 insert into t1 select * from t2 where c = 10", true, + "BATCH ON `c` LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + {"batch on c limit 10 dry run insert into t1 select * from t2 where c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + {"batch on c limit 10 dry run query insert into t1 select * from t2 where c = 10", true, + "BATCH ON `c` LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + {"batch limit 10 insert into t1 select * from t2 where c = 10", true, + "BATCH LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + {"batch limit 10 dry run insert into t1 select * from t2 where c = 10", true, + "BATCH LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + {"batch limit 10 dry run query insert into t1 select * from t2 where c = 10", true, + "BATCH LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10"}, + // inserts on duplicate key update + {"batch on c limit 10 insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH ON `c` LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + {"batch on c limit 10 dry run insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH ON `c` LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + {"batch on c limit 10 dry run query insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH ON `c` LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + {"batch limit 10 insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH LIMIT 10 INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + {"batch limit 10 dry run insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH LIMIT 10 DRY RUN INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + {"batch limit 10 dry run query insert into t1 select * from t2 where c = 10 on duplicate key update t1.val = t2.val", true, + "BATCH LIMIT 10 DRY RUN QUERY INSERT INTO `t1` SELECT * FROM `t2` WHERE `c`=10 ON DUPLICATE KEY UPDATE `t1`.`val`=`t2`.`val`"}, + } + + RunTest(t, cases, false) +} + +func TestIntervalPartition(t *testing.T) { + table := []testCase{ + {"CREATE TABLE t (c1 integer,c2 integer) PARTITION BY RANGE (c1) INTERVAL (1000)", true, "CREATE TABLE `t` (`c1` INT,`c2` INT) PARTITION BY RANGE (`c1`) INTERVAL (1000)"}, + {"CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE (c2) INTERVAL (1 Month)", true, "CREATE TABLE `t` (`c1` INT,`c2` DATE) PARTITION BY RANGE (`c2`) INTERVAL (1 MONTH)"}, + {"CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE (c1) (partition p1 values less than (22))", true, "CREATE TABLE `t` (`c1` INT,`c2` DATE) PARTITION BY RANGE (`c1`) (PARTITION `p1` VALUES LESS THAN (22))"}, + {`CREATE TABLE t (c1 int, c2 date) PARTITION BY RANGE COLUMNS (c2) INTERVAL (1 year) first partition less than ("2022-02-01")`, false, ""}, + {`CREATE TABLE t (c1 int, c2 datetime) PARTITION BY RANGE COLUMNS (c2) INTERVAL (1 day) first partition less than ("2022-01-02") last partition less than ("2022-06-01") NULL PARTITION MAXVALUE PARTITION`, true, "CREATE TABLE `t` (`c1` INT,`c2` DATETIME) PARTITION BY RANGE COLUMNS (`c2`) INTERVAL (1 DAY) FIRST PARTITION LESS THAN (_UTF8MB4'2022-01-02') LAST PARTITION LESS THAN (_UTF8MB4'2022-06-01') NULL PARTITION MAXVALUE PARTITION"}, + {`ALTER TABLE t LAST PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` LAST PARTITION LESS THAN (1000)"}, + {`ALTER TABLE t REORGANIZE MAX PARTITION INTO NEW LAST PARTITION LESS THAN (1000)`, false, ""}, + {`ALTER TABLE t REORGANIZE MAX PARTITION INTO LAST PARTITION LESS THAN (1000)`, false, ""}, + {`ALTER TABLE t REORGANIZE MAXVALUE PARTITION INTO NEW LAST PARTITION LESS THAN (1000)`, false, ""}, + {`ALTER TABLE t REORGANIZE MAXVALUE PARTITION INTO LAST PARTITION LESS THAN (1000)`, false, ""}, + {`ALTER TABLE t split MAXVALUE PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` SPLIT MAXVALUE PARTITION LESS THAN (1000)"}, + {`ALTER TABLE t merge first PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` MERGE FIRST PARTITION LESS THAN (1000)"}, + {`ALTER TABLE t first PARTITION LESS THAN (1000)`, true, "ALTER TABLE `t` FIRST PARTITION LESS THAN (1000)"}, + } + + RunTest(t, table, false) +} + +func TestTTLTableOption(t *testing.T) { + table := []testCase{ + // create table with various temporal interval + {"create table t (created_at datetime) TTL = created_at + INTERVAL 3.1415 YEAR", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 3.1415 YEAR"}, + {"create table t (created_at datetime) TTL = created_at + INTERVAL '1 1:1:1' DAY_SECOND", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL _UTF8MB4'1 1:1:1' DAY_SECOND"}, + {"create table t (created_at datetime) TTL = created_at + INTERVAL 1 YEAR", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR"}, + {"create table t (created_at datetime) TTL = created_at + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, + {"create table t (created_at datetime) TTL created_at + INTERVAL 1 YEAR TTL_ENABLE 'OFF'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF'"}, + {"create table t (created_at datetime) TTL created_at + INTERVAL 1 YEAR TTL_ENABLE 'OFF' TTL_JOB_INTERVAL='8h'", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'OFF' TTL_JOB_INTERVAL = '8h'"}, + {"create table t (created_at datetime) /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON'*/", true, "CREATE TABLE `t` (`created_at` DATETIME) TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON'"}, + + // alter table with various temporal interval + {"alter table t TTL = created_at + INTERVAL 1 MONTH", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH"}, + {"alter table t TTL_ENABLE = 'ON'", true, "ALTER TABLE `t` TTL_ENABLE = 'ON'"}, + {"alter table t TTL_ENABLE = 'OFF'", true, "ALTER TABLE `t` TTL_ENABLE = 'OFF'"}, + {"alter table t TTL = created_at + INTERVAL 1 MONTH TTL_ENABLE 'OFF'", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH TTL_ENABLE = 'OFF'"}, + {"alter table t TTL = created_at + INTERVAL 1 MONTH TTL_ENABLE 'OFF' TTL_JOB_INTERVAL '1h'", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 MONTH TTL_ENABLE = 'OFF' TTL_JOB_INTERVAL = '1h'"}, + {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON'"}, + {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON' TTL_JOB_INTERVAL='8h'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON' TTL_JOB_INTERVAL = '8h'"}, + {"alter table t /*T![ttl] ttl=created_at + INTERVAL 1 YEAR ttl_enable='ON' TTL_JOB_INTERVAL='8.645124531235h'*/", true, "ALTER TABLE `t` TTL = `created_at` + INTERVAL 1 YEAR TTL_ENABLE = 'ON' TTL_JOB_INTERVAL = '8.645124531235h'"}, + + // alter table to remove ttl settings + {"alter table t remove ttl", true, "ALTER TABLE `t` REMOVE TTL"}, + + // validate invalid TTL_ENABLE settings + {"create table t (created_at datetime) TTL_ENABLE = 'test_case'", false, ""}, + {"create table t (created_at datetime) /*T![ttl] TTL_ENABLE = 'test_case' */", false, ""}, + {"alter table t /*T![ttl] TTL_ENABLE = 'test_case' */", false, ""}, + + // validate invalid TTL_JOB_INTERVAL settings + {"create table t (created_at datetime) TTL_JOB_INTERVAL = '@monthly'", false, ""}, + {"create table t (created_at datetime) TTL_JOB_INTERVAL = '10hourxx'", false, ""}, + {"create table t (created_at datetime) TTL_JOB_INTERVAL = '10.10.255h'", false, ""}, + } + + RunTest(t, table, false) +} + +func TestIssue45898(t *testing.T) { + p := parser.New() + p.ParseSQL("a.") + stmts, _, err := p.ParseSQL("select count(1) from t") + require.NoError(t, err) + var sb strings.Builder + restoreCtx := NewRestoreCtx(DefaultRestoreFlags, &sb) + sb.Reset() + stmts[0].Restore(restoreCtx) + require.Equal(t, "SELECT COUNT(1) FROM `t`", sb.String()) +} + +func TestMultiStmt(t *testing.T) { + p := parser.New() + stmts, _, err := p.Parse("SELECT 'foo'; SELECT 'foo;bar','baz'; select 'foo' , 'bar' , 'baz' ;select 1", "", "") + require.NoError(t, err) + require.Equal(t, len(stmts), 4) + stmt1 := stmts[0].(*ast.SelectStmt) + stmt2 := stmts[1].(*ast.SelectStmt) + stmt3 := stmts[2].(*ast.SelectStmt) + stmt4 := stmts[3].(*ast.SelectStmt) + require.Equal(t, "'foo'", stmt1.Fields.Fields[0].Text()) + require.Equal(t, "'foo;bar'", stmt2.Fields.Fields[0].Text()) + require.Equal(t, "'baz'", stmt2.Fields.Fields[1].Text()) + require.Equal(t, "'foo'", stmt3.Fields.Fields[0].Text()) + require.Equal(t, "'bar'", stmt3.Fields.Fields[1].Text()) + require.Equal(t, "'baz'", stmt3.Fields.Fields[2].Text()) + require.Equal(t, "1", stmt4.Fields.Fields[0].Text()) +} + +// https://dev.mysql.com/doc/refman/8.1/en/other-vendor-data-types.html +func TestCompatTypes(t *testing.T) { + table := []testCase{ + {`CREATE TABLE t(id INT PRIMARY KEY, c1 BOOL)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT(1))"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 BOOLEAN)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT(1))"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 CHARACTER VARYING(0))`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` VARCHAR(0))"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 FIXED)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DECIMAL)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 FLOAT4)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` FLOAT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 FLOAT8)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DOUBLE)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT1)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` TINYINT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT2)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` SMALLINT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT3)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMINT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT4)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` INT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 INT8)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` BIGINT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG VARBINARY)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMBLOB)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG VARCHAR)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMTEXT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 LONG)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMTEXT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 MIDDLEINT)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` MEDIUMINT)"}, + {`CREATE TABLE t(id INT PRIMARY KEY, c1 NUMERIC)`, true, "CREATE TABLE `t` (`id` INT PRIMARY KEY,`c1` DECIMAL)"}, + } + + RunTest(t, table, false) +} diff --git a/parser/reserved_words_test.go b/pkg/parser/reserved_words_test.go similarity index 98% rename from parser/reserved_words_test.go rename to pkg/parser/reserved_words_test.go index 6896167741284..6e409145ba428 100644 --- a/parser/reserved_words_test.go +++ b/pkg/parser/reserved_words_test.go @@ -33,7 +33,7 @@ import ( "testing" _ "github.com/go-sql-driver/mysql" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser/ast" requires "github.com/stretchr/testify/require" ) diff --git a/pkg/parser/terror/BUILD.bazel b/pkg/parser/terror/BUILD.bazel new file mode 100644 index 0000000000000..c51ac9bc2b5d9 --- /dev/null +++ b/pkg/parser/terror/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "terror", + srcs = ["terror.go"], + importpath = "github.com/pingcap/tidb/pkg/parser/terror", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "terror_test", + timeout = "short", + srcs = ["terror_test.go"], + embed = [":terror"], + flaky = True, + shard_count = 6, + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/parser/terror/terror.go b/pkg/parser/terror/terror.go new file mode 100644 index 0000000000000..58be892236bab --- /dev/null +++ b/pkg/parser/terror/terror.go @@ -0,0 +1,337 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package terror + +import ( + "fmt" + "runtime/debug" + "strconv" + "strings" + "sync" + "sync/atomic" + + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/parser/mysql" + "go.uber.org/zap" +) + +// ErrCode represents a specific error type in a error class. +// Same error code can be used in different error classes. +type ErrCode int + +const ( + // Executor error codes. + + // CodeUnknown is for errors of unknown reason. + CodeUnknown ErrCode = -1 + // CodeExecResultIsEmpty indicates execution result is empty. + CodeExecResultIsEmpty ErrCode = 3 + + // Expression error codes. + + // CodeMissConnectionID indicates connection id is missing. + CodeMissConnectionID ErrCode = 1 + + // Special error codes. + + // CodeResultUndetermined indicates the sql execution result is undetermined. + CodeResultUndetermined ErrCode = 2 +) + +// ErrClass represents a class of errors. +type ErrClass int + +// Error implements error interface. +type Error = errors.Error + +// Error classes. +var ( + ClassAutoid = RegisterErrorClass(1, "autoid") + ClassDDL = RegisterErrorClass(2, "ddl") + ClassDomain = RegisterErrorClass(3, "domain") + ClassEvaluator = RegisterErrorClass(4, "evaluator") + ClassExecutor = RegisterErrorClass(5, "executor") + ClassExpression = RegisterErrorClass(6, "expression") + ClassAdmin = RegisterErrorClass(7, "admin") + ClassKV = RegisterErrorClass(8, "kv") + ClassMeta = RegisterErrorClass(9, "meta") + ClassOptimizer = RegisterErrorClass(10, "planner") + ClassParser = RegisterErrorClass(11, "parser") + ClassPerfSchema = RegisterErrorClass(12, "perfschema") + ClassPrivilege = RegisterErrorClass(13, "privilege") + ClassSchema = RegisterErrorClass(14, "schema") + ClassServer = RegisterErrorClass(15, "server") + ClassStructure = RegisterErrorClass(16, "structure") + ClassVariable = RegisterErrorClass(17, "variable") + ClassXEval = RegisterErrorClass(18, "xeval") + ClassTable = RegisterErrorClass(19, "table") + ClassTypes = RegisterErrorClass(20, "types") + ClassGlobal = RegisterErrorClass(21, "global") + ClassMockTikv = RegisterErrorClass(22, "mocktikv") + ClassJSON = RegisterErrorClass(23, "json") + ClassTiKV = RegisterErrorClass(24, "tikv") + ClassSession = RegisterErrorClass(25, "session") + ClassPlugin = RegisterErrorClass(26, "plugin") + ClassUtil = RegisterErrorClass(27, "util") + // Add more as needed. +) + +var errClass2Desc = make(map[ErrClass]string) +var rfcCode2errClass = newCode2ErrClassMap() + +type code2ErrClassMap struct { + data sync.Map +} + +func newCode2ErrClassMap() *code2ErrClassMap { + return &code2ErrClassMap{ + data: sync.Map{}, + } +} + +func (m *code2ErrClassMap) Get(key string) (ErrClass, bool) { + ret, have := m.data.Load(key) + if !have { + return ErrClass(-1), false + } + return ret.(ErrClass), true +} + +func (m *code2ErrClassMap) Put(key string, err ErrClass) { + m.data.Store(key, err) +} + +var registerFinish uint32 + +// RegisterFinish makes the register of new error panic. +// The use pattern should be register all the errors during initialization, and then call RegisterFinish. +func RegisterFinish() { + atomic.StoreUint32(®isterFinish, 1) +} + +func frozen() bool { + return atomic.LoadUint32(®isterFinish) != 0 +} + +// RegisterErrorClass registers new error class for terror. +func RegisterErrorClass(classCode int, desc string) ErrClass { + errClass := ErrClass(classCode) + if _, exists := errClass2Desc[errClass]; exists { + panic(fmt.Sprintf("duplicate register ClassCode %d - %s", classCode, desc)) + } + errClass2Desc[errClass] = desc + return errClass +} + +// String implements fmt.Stringer interface. +func (ec ErrClass) String() string { + if s, exists := errClass2Desc[ec]; exists { + return s + } + return strconv.Itoa(int(ec)) +} + +// EqualClass returns true if err is *Error with the same class. +func (ec ErrClass) EqualClass(err error) bool { + e := errors.Cause(err) + if e == nil { + return false + } + if te, ok := e.(*Error); ok { + rfcCode := te.RFCCode() + if index := strings.Index(string(rfcCode), ":"); index > 0 { + if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has { + return class == ec + } + } + } + return false +} + +// NotEqualClass returns true if err is not *Error with the same class. +func (ec ErrClass) NotEqualClass(err error) bool { + return !ec.EqualClass(err) +} + +func (ec ErrClass) initError(code ErrCode) string { + if frozen() { + debug.PrintStack() + panic("register error after initialized is prohibited") + } + clsMap, ok := ErrClassToMySQLCodes[ec] + if !ok { + clsMap = make(map[ErrCode]struct{}) + ErrClassToMySQLCodes[ec] = clsMap + } + clsMap[code] = struct{}{} + class := errClass2Desc[ec] + rfcCode := fmt.Sprintf("%s:%d", class, code) + rfcCode2errClass.Put(class, ec) + return rfcCode +} + +// New defines an *Error with an error code and an error message. +// Usually used to create base *Error. +// Attention: +// this method is not goroutine-safe and +// usually be used in global variable initializer +// +// Deprecated: use NewStd or NewStdErr instead. +func (ec ErrClass) New(code ErrCode, message string) *Error { + rfcCode := ec.initError(code) + err := errors.Normalize(message, errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode)) + return err +} + +// NewStdErr defines an *Error with an error code, an error +// message and workaround to create standard error. +func (ec ErrClass) NewStdErr(code ErrCode, message *mysql.ErrMessage) *Error { + rfcCode := ec.initError(code) + err := errors.Normalize( + message.Raw, errors.RedactArgs(message.RedactArgPos), + errors.MySQLErrorCode(int(code)), errors.RFCCodeText(rfcCode), + ) + return err +} + +// NewStd calls New using the standard message for the error code +// Attention: +// this method is not goroutine-safe and +// usually be used in global variable initializer +func (ec ErrClass) NewStd(code ErrCode) *Error { + return ec.NewStdErr(code, mysql.MySQLErrName[uint16(code)]) +} + +// Synthesize synthesizes an *Error in the air +// it didn't register error into ErrClassToMySQLCodes +// so it's goroutine-safe +// and often be used to create Error came from other systems like TiKV. +func (ec ErrClass) Synthesize(code ErrCode, message string) *Error { + return errors.Normalize( + message, errors.MySQLErrorCode(int(code)), + errors.RFCCodeText(fmt.Sprintf("%s:%d", errClass2Desc[ec], code)), + ) +} + +// ToSQLError convert Error to mysql.SQLError. +func ToSQLError(e *Error) *mysql.SQLError { + code := getMySQLErrorCode(e) + return mysql.NewErrf(code, "%s", nil, e.GetMsg()) +} + +var defaultMySQLErrorCode uint16 + +func getMySQLErrorCode(e *Error) uint16 { + rfcCode := e.RFCCode() + var class ErrClass + if index := strings.Index(string(rfcCode), ":"); index > 0 { + ec, has := rfcCode2errClass.Get(string(rfcCode)[:index]) + if !has { + log.Warn("Unknown error class", zap.String("class", string(rfcCode)[:index])) + return defaultMySQLErrorCode + } + class = ec + } + codeMap, ok := ErrClassToMySQLCodes[class] + if !ok { + log.Warn("Unknown error class", zap.Int("class", int(class))) + return defaultMySQLErrorCode + } + _, ok = codeMap[ErrCode(e.Code())] + if !ok { + log.Debug("Unknown error code", zap.Int("class", int(class)), zap.Int("code", int(e.Code()))) + return defaultMySQLErrorCode + } + return uint16(e.Code()) +} + +var ( + // ErrClassToMySQLCodes is the map of ErrClass to code-set. + ErrClassToMySQLCodes = make(map[ErrClass]map[ErrCode]struct{}) + // ErrCritical is the critical error class. + ErrCritical = ClassGlobal.NewStdErr(CodeExecResultIsEmpty, mysql.Message("critical error %v", nil)) + // ErrResultUndetermined is the error when execution result is unknown. + ErrResultUndetermined = ClassGlobal.NewStdErr( + CodeResultUndetermined, + mysql.Message("execution result undetermined", nil), + ) +) + +func init() { + defaultMySQLErrorCode = mysql.ErrUnknown +} + +// ErrorEqual returns a boolean indicating whether err1 is equal to err2. +func ErrorEqual(err1, err2 error) bool { + e1 := errors.Cause(err1) + e2 := errors.Cause(err2) + + if e1 == e2 { + return true + } + + if e1 == nil || e2 == nil { + return e1 == e2 + } + + te1, ok1 := e1.(*Error) + te2, ok2 := e2.(*Error) + if ok1 && ok2 { + return te1.RFCCode() == te2.RFCCode() + } + + return e1.Error() == e2.Error() +} + +// ErrorNotEqual returns a boolean indicating whether err1 isn't equal to err2. +func ErrorNotEqual(err1, err2 error) bool { + return !ErrorEqual(err1, err2) +} + +// MustNil cleans up and fatals if err is not nil. +func MustNil(err error, closeFuns ...func()) { + if err != nil { + for _, f := range closeFuns { + f() + } + log.Fatal("unexpected error", zap.Error(err), zap.Stack("stack")) + } +} + +// Call executes a function and checks the returned err. +func Call(fn func() error) { + err := fn() + if err != nil { + log.Error("function call errored", zap.Error(err), zap.Stack("stack")) + } +} + +// Log logs the error if it is not nil. +func Log(err error) { + if err != nil { + log.Error("encountered error", zap.Error(err), zap.Stack("stack")) + } +} + +// GetErrClass returns the error class of the error. +func GetErrClass(e *Error) ErrClass { + rfcCode := e.RFCCode() + if index := strings.Index(string(rfcCode), ":"); index > 0 { + if class, has := rfcCode2errClass.Get(string(rfcCode)[:index]); has { + return class + } + } + return ErrClass(-1) +} diff --git a/parser/terror/terror_test.go b/pkg/parser/terror/terror_test.go similarity index 100% rename from parser/terror/terror_test.go rename to pkg/parser/terror/terror_test.go diff --git a/parser/test.sh b/pkg/parser/test.sh similarity index 100% rename from parser/test.sh rename to pkg/parser/test.sh diff --git a/pkg/parser/test_driver/BUILD.bazel b/pkg/parser/test_driver/BUILD.bazel new file mode 100644 index 0000000000000..fd5afd6d7ecf9 --- /dev/null +++ b/pkg/parser/test_driver/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "test_driver", + srcs = [ + "test_driver.go", + "test_driver_datum.go", + "test_driver_helper.go", + "test_driver_mydecimal.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/test_driver", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/parser/types", + "@com_github_pingcap_errors//:errors", + ], +) diff --git a/parser/test_driver/test_driver.go b/pkg/parser/test_driver/test_driver.go similarity index 97% rename from parser/test_driver/test_driver.go rename to pkg/parser/test_driver/test_driver.go index 00fb002864abf..1df47c9c095b0 100644 --- a/parser/test_driver/test_driver.go +++ b/pkg/parser/test_driver/test_driver.go @@ -21,10 +21,10 @@ import ( "io" "strconv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" ) func init() { diff --git a/parser/test_driver/test_driver_datum.go b/pkg/parser/test_driver/test_driver_datum.go similarity index 98% rename from parser/test_driver/test_driver_datum.go rename to pkg/parser/test_driver/test_driver_datum.go index db7d58af5bd30..16e8a27567bfe 100644 --- a/parser/test_driver/test_driver_datum.go +++ b/pkg/parser/test_driver/test_driver_datum.go @@ -25,9 +25,9 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" ) // Kind constants. diff --git a/parser/test_driver/test_driver_helper.go b/pkg/parser/test_driver/test_driver_helper.go similarity index 100% rename from parser/test_driver/test_driver_helper.go rename to pkg/parser/test_driver/test_driver_helper.go diff --git a/parser/test_driver/test_driver_mydecimal.go b/pkg/parser/test_driver/test_driver_mydecimal.go similarity index 100% rename from parser/test_driver/test_driver_mydecimal.go rename to pkg/parser/test_driver/test_driver_mydecimal.go diff --git a/pkg/parser/tidb/BUILD.bazel b/pkg/parser/tidb/BUILD.bazel new file mode 100644 index 0000000000000..114b4b609589d --- /dev/null +++ b/pkg/parser/tidb/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tidb", + srcs = ["features.go"], + importpath = "github.com/pingcap/tidb/pkg/parser/tidb", + visibility = ["//visibility:public"], +) diff --git a/parser/tidb/features.go b/pkg/parser/tidb/features.go similarity index 100% rename from parser/tidb/features.go rename to pkg/parser/tidb/features.go diff --git a/pkg/parser/types/BUILD.bazel b/pkg/parser/types/BUILD.bazel new file mode 100644 index 0000000000000..4e82ed0da8594 --- /dev/null +++ b/pkg/parser/types/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "types", + srcs = [ + "etc.go", + "eval_type.go", + "field_type.go", + ], + importpath = "github.com/pingcap/tidb/pkg/parser/types", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "@com_github_cznic_mathutil//:mathutil", + ], +) + +go_test( + name = "types_test", + timeout = "short", + srcs = [ + "etc_test.go", + "field_type_test.go", + ], + embed = [":types"], + flaky = True, + shard_count = 6, + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/parser/test_driver", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/parser/types/etc.go b/pkg/parser/types/etc.go new file mode 100644 index 0000000000000..ee776b0c37ae0 --- /dev/null +++ b/pkg/parser/types/etc.go @@ -0,0 +1,164 @@ +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "strings" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" +) + +// IsTypeBlob returns a boolean indicating whether the tp is a blob type. +func IsTypeBlob(tp byte) bool { + switch tp { + case mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob: + return true + default: + return false + } +} + +// IsTypeChar returns a boolean indicating +// whether the tp is the char type like a string type or a varchar type. +func IsTypeChar(tp byte) bool { + return tp == mysql.TypeString || tp == mysql.TypeVarchar +} + +var type2Str = map[byte]string{ + mysql.TypeBit: "bit", + mysql.TypeBlob: "text", + mysql.TypeDate: "date", + mysql.TypeDatetime: "datetime", + mysql.TypeUnspecified: "unspecified", + mysql.TypeNewDecimal: "decimal", + mysql.TypeDouble: "double", + mysql.TypeEnum: "enum", + mysql.TypeFloat: "float", + mysql.TypeGeometry: "geometry", + mysql.TypeInt24: "mediumint", + mysql.TypeJSON: "json", + mysql.TypeLong: "int", + mysql.TypeLonglong: "bigint", + mysql.TypeLongBlob: "longtext", + mysql.TypeMediumBlob: "mediumtext", + mysql.TypeNull: "null", + mysql.TypeSet: "set", + mysql.TypeShort: "smallint", + mysql.TypeString: "char", + mysql.TypeDuration: "time", + mysql.TypeTimestamp: "timestamp", + mysql.TypeTiny: "tinyint", + mysql.TypeTinyBlob: "tinytext", + mysql.TypeVarchar: "varchar", + mysql.TypeVarString: "var_string", + mysql.TypeYear: "year", +} + +var str2Type = map[string]byte{ + "bit": mysql.TypeBit, + "text": mysql.TypeBlob, + "date": mysql.TypeDate, + "datetime": mysql.TypeDatetime, + "unspecified": mysql.TypeUnspecified, + "decimal": mysql.TypeNewDecimal, + "double": mysql.TypeDouble, + "enum": mysql.TypeEnum, + "float": mysql.TypeFloat, + "geometry": mysql.TypeGeometry, + "mediumint": mysql.TypeInt24, + "json": mysql.TypeJSON, + "int": mysql.TypeLong, + "bigint": mysql.TypeLonglong, + "longtext": mysql.TypeLongBlob, + "mediumtext": mysql.TypeMediumBlob, + "null": mysql.TypeNull, + "set": mysql.TypeSet, + "smallint": mysql.TypeShort, + "char": mysql.TypeString, + "time": mysql.TypeDuration, + "timestamp": mysql.TypeTimestamp, + "tinyint": mysql.TypeTiny, + "tinytext": mysql.TypeTinyBlob, + "varchar": mysql.TypeVarchar, + "var_string": mysql.TypeVarString, + "year": mysql.TypeYear, +} + +// TypeStr converts tp to a string. +func TypeStr(tp byte) (r string) { + return type2Str[tp] +} + +// TypeToStr converts a field to a string. +// It is used for converting Text to Blob, +// or converting Char to Binary. +// Args: +// +// tp: type enum +// cs: charset +func TypeToStr(tp byte, cs string) (r string) { + ts := type2Str[tp] + if cs != "binary" { + return ts + } + if IsTypeBlob(tp) { + ts = strings.Replace(ts, "text", "blob", 1) + } else if IsTypeChar(tp) { + ts = strings.Replace(ts, "char", "binary", 1) + } else if tp == mysql.TypeNull { + ts = "binary" + } + return ts +} + +// StrToType convert a string to type enum. +// Args: +// +// ts: type string +func StrToType(ts string) (tp byte) { + ts = strings.Replace(ts, "blob", "text", 1) + ts = strings.Replace(ts, "binary", "char", 1) + if tp, ok := str2Type[ts]; ok { + return tp + } + + return mysql.TypeUnspecified +} + +var ( + dig2bytes = [10]int{0, 1, 1, 2, 2, 3, 3, 4, 4, 4} +) + +// constant values. +const ( + digitsPerWord = 9 // A word holds 9 digits. + wordSize = 4 // A word is 4 bytes int32. +) + +var ( + // ErrInvalidDefault is returned when meet a invalid default value. + ErrInvalidDefault = terror.ClassTypes.NewStd(mysql.ErrInvalidDefault) + // ErrDataOutOfRange is returned when meet a value out of range. + ErrDataOutOfRange = terror.ClassTypes.NewStd(mysql.ErrDataOutOfRange) + // ErrTruncatedWrongValue is returned when meet a value bigger than + // 99999999999999999999999999999999999999999999999999999999999999999 during parsing. + ErrTruncatedWrongValue = terror.ClassTypes.NewStd(mysql.ErrTruncatedWrongValue) + // ErrIllegalValueForType is returned when strconv.ParseFloat meet strconv.ErrRange during parsing. + ErrIllegalValueForType = terror.ClassTypes.NewStd(mysql.ErrIllegalValueForType) +) diff --git a/pkg/parser/types/etc_test.go b/pkg/parser/types/etc_test.go new file mode 100644 index 0000000000000..0c67551990d63 --- /dev/null +++ b/pkg/parser/types/etc_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/stretchr/testify/require" +) + +func TestStrToType(t *testing.T) { + for tp, str := range type2Str { + a := StrToType(str) + require.Equal(t, tp, a) + } + + tp := StrToType("blob") + require.Equal(t, tp, mysql.TypeBlob) + + tp = StrToType("binary") + require.Equal(t, tp, mysql.TypeString) +} diff --git a/parser/types/eval_type.go b/pkg/parser/types/eval_type.go similarity index 100% rename from parser/types/eval_type.go rename to pkg/parser/types/eval_type.go diff --git a/pkg/parser/types/field_type.go b/pkg/parser/types/field_type.go new file mode 100644 index 0000000000000..2e80bbf3c7d2b --- /dev/null +++ b/pkg/parser/types/field_type.go @@ -0,0 +1,696 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "fmt" + "io" + "strings" + "unsafe" + + "github.com/cznic/mathutil" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" +) + +// UnspecifiedLength is unspecified length. +const ( + UnspecifiedLength = -1 +) + +// TiDBStrictIntegerDisplayWidth represent whether return warnings when integerType with (length) was parsed. +// The default is `false`, it will be parsed as warning, and the result in show-create-table will ignore the +// display length when it set to `true`. This is for compatibility with MySQL 8.0 in which integer max display +// length is deprecated, referring this issue #6688 for more details. +var ( + TiDBStrictIntegerDisplayWidth bool +) + +// FieldType records field type information. +type FieldType struct { + // tp is type of the field + tp byte + // flag represent NotNull, Unsigned, PriKey flags etc. + flag uint + // flen represent size of bytes of the field + flen int + // decimal represent decimal length of the field + decimal int + // charset represent character set + charset string + // collate represent collate rules of the charset + collate string + // elems is the element list for enum and set type. + elems []string + elemsIsBinaryLit []bool + array bool + // Please keep in mind that jsonFieldType should be updated if you add a new field here. +} + +// NewFieldType returns a FieldType, +// with a type and other information about field type. +func NewFieldType(tp byte) *FieldType { + return &FieldType{ + tp: tp, + flen: UnspecifiedLength, + decimal: UnspecifiedLength, + } +} + +// IsDecimalValid checks whether the decimal is valid. +func (ft *FieldType) IsDecimalValid() bool { + if ft.GetType() == mysql.TypeNewDecimal && (ft.decimal < 0 || ft.decimal > mysql.MaxDecimalScale || ft.flen <= 0 || ft.flen > mysql.MaxDecimalWidth || ft.flen < ft.decimal) { + return false + } + return true +} + +// IsVarLengthType Determine whether the column type is a variable-length type +func (ft *FieldType) IsVarLengthType() bool { + switch ft.GetType() { + case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeJSON, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + return true + default: + return false + } +} + +// GetType returns the type of the FieldType. +func (ft *FieldType) GetType() byte { + if ft.array { + return mysql.TypeJSON + } + return ft.tp +} + +// GetFlag returns the flag of the FieldType. +func (ft *FieldType) GetFlag() uint { + return ft.flag +} + +// GetFlen returns the length of the field. +func (ft *FieldType) GetFlen() int { + return ft.flen +} + +// GetDecimal returns the decimal of the FieldType. +func (ft *FieldType) GetDecimal() int { + return ft.decimal +} + +// GetCharset returns the field's charset +func (ft *FieldType) GetCharset() string { + return ft.charset +} + +// GetCollate returns the collation of the field. +func (ft *FieldType) GetCollate() string { + return ft.collate +} + +// GetElems returns the elements of the FieldType. +func (ft *FieldType) GetElems() []string { + return ft.elems +} + +// SetType sets the type of the FieldType. +func (ft *FieldType) SetType(tp byte) { + ft.tp = tp + ft.array = false +} + +// SetFlag sets the flag of the FieldType. +func (ft *FieldType) SetFlag(flag uint) { + ft.flag = flag +} + +// AddFlag adds a flag to the FieldType. +func (ft *FieldType) AddFlag(flag uint) { + ft.flag |= flag +} + +// AndFlag and the flag of the FieldType. +func (ft *FieldType) AndFlag(flag uint) { + ft.flag &= flag +} + +// ToggleFlag toggle the flag of the FieldType. +func (ft *FieldType) ToggleFlag(flag uint) { + ft.flag ^= flag +} + +// DelFlag delete the flag of the FieldType. +func (ft *FieldType) DelFlag(flag uint) { + ft.flag &= ^flag +} + +// SetFlen sets the length of the field. +func (ft *FieldType) SetFlen(flen int) { + ft.flen = flen +} + +// SetFlenUnderLimit sets the length of the field to the value of the argument +func (ft *FieldType) SetFlenUnderLimit(flen int) { + if ft.GetType() == mysql.TypeNewDecimal { + ft.flen = mathutil.Min(flen, mysql.MaxDecimalWidth) + } else { + ft.flen = flen + } +} + +// SetDecimal sets the decimal of the FieldType. +func (ft *FieldType) SetDecimal(decimal int) { + ft.decimal = decimal +} + +// SetDecimalUnderLimit sets the decimal of the field to the value of the argument +func (ft *FieldType) SetDecimalUnderLimit(decimal int) { + if ft.GetType() == mysql.TypeNewDecimal { + ft.decimal = mathutil.Min(decimal, mysql.MaxDecimalScale) + } else { + ft.decimal = decimal + } +} + +// UpdateFlenAndDecimalUnderLimit updates the length and decimal to the value of the argument +func (ft *FieldType) UpdateFlenAndDecimalUnderLimit(old *FieldType, deltaDecimal int, deltaFlen int) { + if ft.GetType() != mysql.TypeNewDecimal { + return + } + if old.decimal < 0 { + deltaFlen += mysql.MaxDecimalScale + ft.decimal = mysql.MaxDecimalScale + } else { + ft.SetDecimal(old.decimal + deltaDecimal) + } + if old.flen < 0 { + ft.flen = mysql.MaxDecimalWidth + } else { + ft.SetFlenUnderLimit(old.flen + deltaFlen) + } +} + +// SetCharset sets the charset of the FieldType. +func (ft *FieldType) SetCharset(charset string) { + ft.charset = charset +} + +// SetCollate sets the collation of the FieldType. +func (ft *FieldType) SetCollate(collate string) { + ft.collate = collate +} + +// SetElems sets the elements of the FieldType. +func (ft *FieldType) SetElems(elems []string) { + ft.elems = elems +} + +// SetElem sets the element of the FieldType. +func (ft *FieldType) SetElem(idx int, element string) { + ft.elems[idx] = element +} + +// SetArray sets the array field of the FieldType. +func (ft *FieldType) SetArray(array bool) { + ft.array = array +} + +// IsArray return true if the filed type is array. +func (ft *FieldType) IsArray() bool { + return ft.array +} + +// ArrayType return the type of the array. +func (ft *FieldType) ArrayType() *FieldType { + if !ft.array { + return ft + } + clone := ft.Clone() + clone.SetArray(false) + return clone +} + +// SetElemWithIsBinaryLit sets the element of the FieldType. +func (ft *FieldType) SetElemWithIsBinaryLit(idx int, element string, isBinaryLit bool) { + ft.elems[idx] = element + if isBinaryLit { + // Create the binary literal flags lazily. + if ft.elemsIsBinaryLit == nil { + ft.elemsIsBinaryLit = make([]bool, len(ft.elems)) + } + ft.elemsIsBinaryLit[idx] = true + } +} + +// GetElem returns the element of the FieldType. +func (ft *FieldType) GetElem(idx int) string { + return ft.elems[idx] +} + +// GetElemIsBinaryLit returns the binary literal flag of the element at index idx. +func (ft *FieldType) GetElemIsBinaryLit(idx int) bool { + if len(ft.elemsIsBinaryLit) == 0 { + return false + } + return ft.elemsIsBinaryLit[idx] +} + +// CleanElemIsBinaryLit cleans the binary literal flag of the element at index idx. +func (ft *FieldType) CleanElemIsBinaryLit() { + if ft != nil && ft.elemsIsBinaryLit != nil { + ft.elemsIsBinaryLit = nil + } +} + +// Clone returns a copy of itself. +func (ft *FieldType) Clone() *FieldType { + ret := *ft + return &ret +} + +// Equal checks whether two FieldType objects are equal. +func (ft *FieldType) Equal(other *FieldType) bool { + // We do not need to compare whole `ft.flag == other.flag` when wrapping cast upon an Expression. + // but need compare unsigned_flag of ft.flag. + // When tp is float or double with decimal unspecified, do not check whether flen is equal, + // because flen for them is useless. + // The decimal field can be ignored if the type is int or string. + tpEqual := (ft.GetType() == other.GetType()) || (ft.GetType() == mysql.TypeVarchar && other.GetType() == mysql.TypeVarString) || (ft.GetType() == mysql.TypeVarString && other.GetType() == mysql.TypeVarchar) + flenEqual := ft.flen == other.flen || (ft.EvalType() == ETReal && ft.decimal == UnspecifiedLength) + ignoreDecimal := ft.EvalType() == ETInt || ft.EvalType() == ETString + partialEqual := tpEqual && + (ignoreDecimal || ft.decimal == other.decimal) && + ft.charset == other.charset && + ft.collate == other.collate && + flenEqual && + mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag) + if !partialEqual || len(ft.elems) != len(other.elems) { + return false + } + for i := range ft.elems { + if ft.elems[i] != other.elems[i] { + return false + } + } + return true +} + +// PartialEqual checks whether two FieldType objects are equal. +// If unsafe is true and the objects is string type, PartialEqual will ignore flen. +// See https://github.com/pingcap/tidb/issues/35490#issuecomment-1211658886 for more detail. +func (ft *FieldType) PartialEqual(other *FieldType, unsafe bool) bool { + if !unsafe || ft.EvalType() != ETString || other.EvalType() != ETString { + return ft.Equal(other) + } + + partialEqual := ft.charset == other.charset && ft.collate == other.collate && mysql.HasUnsignedFlag(ft.flag) == mysql.HasUnsignedFlag(other.flag) + if !partialEqual || len(ft.elems) != len(other.elems) { + return false + } + for i := range ft.elems { + if ft.elems[i] != other.elems[i] { + return false + } + } + return true +} + +// EvalType gets the type in evaluation. +func (ft *FieldType) EvalType() EvalType { + switch ft.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, + mysql.TypeBit, mysql.TypeYear: + return ETInt + case mysql.TypeFloat, mysql.TypeDouble: + return ETReal + case mysql.TypeNewDecimal: + return ETDecimal + case mysql.TypeDate, mysql.TypeDatetime: + return ETDatetime + case mysql.TypeTimestamp: + return ETTimestamp + case mysql.TypeDuration: + return ETDuration + case mysql.TypeJSON: + return ETJson + case mysql.TypeEnum, mysql.TypeSet: + if ft.flag&mysql.EnumSetAsIntFlag > 0 { + return ETInt + } + } + return ETString +} + +// Hybrid checks whether a type is a hybrid type, which can represent different types of value in specific context. +func (ft *FieldType) Hybrid() bool { + return ft.GetType() == mysql.TypeEnum || ft.GetType() == mysql.TypeBit || ft.GetType() == mysql.TypeSet +} + +// Init initializes the FieldType data. +func (ft *FieldType) Init(tp byte) { + ft.tp = tp + ft.flen = UnspecifiedLength + ft.decimal = UnspecifiedLength +} + +// CompactStr only considers tp/CharsetBin/flen/Deimal. +// This is used for showing column type in infoschema. +func (ft *FieldType) CompactStr() string { + ts := TypeToStr(ft.GetType(), ft.charset) + suffix := "" + + defaultFlen, defaultDecimal := mysql.GetDefaultFieldLengthAndDecimal(ft.GetType()) + isDecimalNotDefault := ft.decimal != defaultDecimal && ft.decimal != 0 && ft.decimal != UnspecifiedLength + + // displayFlen and displayDecimal are flen and decimal values with `-1` substituted with default value. + displayFlen, displayDecimal := ft.flen, ft.decimal + if displayFlen == UnspecifiedLength { + displayFlen = defaultFlen + } + if displayDecimal == UnspecifiedLength { + displayDecimal = defaultDecimal + } + + switch ft.GetType() { + case mysql.TypeEnum, mysql.TypeSet: + // Format is ENUM ('e1', 'e2') or SET ('e1', 'e2') + es := make([]string, 0, len(ft.elems)) + for _, e := range ft.elems { + e = format.OutputFormat(e) + es = append(es, e) + } + suffix = fmt.Sprintf("('%s')", strings.Join(es, "','")) + case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: + if isDecimalNotDefault { + suffix = fmt.Sprintf("(%d)", displayDecimal) + } + case mysql.TypeDouble, mysql.TypeFloat: + // 1. flen Not Default, decimal Not Default -> Valid + // 2. flen Not Default, decimal Default (-1) -> Invalid + // 3. flen Default, decimal Not Default -> Valid + // 4. flen Default, decimal Default -> Valid (hide) + if isDecimalNotDefault { + suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) + } + case mysql.TypeNewDecimal: + suffix = fmt.Sprintf("(%d,%d)", displayFlen, displayDecimal) + case mysql.TypeBit, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString: + suffix = fmt.Sprintf("(%d)", displayFlen) + case mysql.TypeTiny: + // With display length deprecation active tinyint(1) still has + // a display length to indicate this might have been a BOOL. + // Connectors expect this. + // + // See also: + // https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-19.html + if !TiDBStrictIntegerDisplayWidth || (mysql.HasZerofillFlag(ft.flag) || displayFlen == 1) { + suffix = fmt.Sprintf("(%d)", displayFlen) + } + case mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + // Referring this issue #6688, the integer max display length is deprecated in MySQL 8.0. + // Since the length doesn't take any effect in TiDB storage or showing result, we remove it here. + if !TiDBStrictIntegerDisplayWidth || mysql.HasZerofillFlag(ft.flag) { + suffix = fmt.Sprintf("(%d)", displayFlen) + } + case mysql.TypeYear: + suffix = fmt.Sprintf("(%d)", ft.flen) + case mysql.TypeNull: + suffix = "(0)" + } + return ts + suffix +} + +// InfoSchemaStr joins the CompactStr with unsigned flag and +// returns a string. +func (ft *FieldType) InfoSchemaStr() string { + suffix := "" + if mysql.HasUnsignedFlag(ft.flag) && + ft.GetType() != mysql.TypeBit && + ft.GetType() != mysql.TypeYear { + suffix = " unsigned" + } + return ft.CompactStr() + suffix +} + +// String joins the information of FieldType and returns a string. +// Note: when flen or decimal is unspecified, this function will use the default value instead of -1. +func (ft *FieldType) String() string { + strs := []string{ft.CompactStr()} + if mysql.HasUnsignedFlag(ft.flag) { + strs = append(strs, "UNSIGNED") + } + if mysql.HasZerofillFlag(ft.flag) { + strs = append(strs, "ZEROFILL") + } + if mysql.HasBinaryFlag(ft.flag) && ft.GetType() != mysql.TypeString { + strs = append(strs, "BINARY") + } + + if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) { + if ft.charset != "" && ft.charset != charset.CharsetBin { + strs = append(strs, fmt.Sprintf("CHARACTER SET %s", ft.charset)) + } + if ft.collate != "" && ft.collate != charset.CharsetBin { + strs = append(strs, fmt.Sprintf("COLLATE %s", ft.collate)) + } + } + + return strings.Join(strs, " ") +} + +// Restore implements Node interface. +func (ft *FieldType) Restore(ctx *format.RestoreCtx) error { + ctx.WriteKeyWord(TypeToStr(ft.GetType(), ft.charset)) + + precision := UnspecifiedLength + scale := UnspecifiedLength + + switch ft.GetType() { + case mysql.TypeEnum, mysql.TypeSet: + ctx.WritePlain("(") + for i, e := range ft.elems { + if i != 0 { + ctx.WritePlain(",") + } + ctx.WriteString(e) + } + ctx.WritePlain(")") + case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration: + precision = ft.decimal + case mysql.TypeUnspecified, mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: + precision = ft.flen + scale = ft.decimal + default: + precision = ft.flen + } + + if precision != UnspecifiedLength { + ctx.WritePlainf("(%d", precision) + if scale != UnspecifiedLength { + ctx.WritePlainf(",%d", scale) + } + ctx.WritePlain(")") + } + + if mysql.HasUnsignedFlag(ft.flag) { + ctx.WriteKeyWord(" UNSIGNED") + } + if mysql.HasZerofillFlag(ft.flag) { + ctx.WriteKeyWord(" ZEROFILL") + } + if mysql.HasBinaryFlag(ft.flag) && ft.charset != charset.CharsetBin { + ctx.WriteKeyWord(" BINARY") + } + + if IsTypeChar(ft.GetType()) || IsTypeBlob(ft.GetType()) { + if ft.charset != "" && ft.charset != charset.CharsetBin { + ctx.WriteKeyWord(" CHARACTER SET " + ft.charset) + } + if ft.collate != "" && ft.collate != charset.CharsetBin { + ctx.WriteKeyWord(" COLLATE ") + ctx.WritePlain(ft.collate) + } + } + + return nil +} + +// RestoreAsCastType is used for write AST back to string. +func (ft *FieldType) RestoreAsCastType(ctx *format.RestoreCtx, explicitCharset bool) { + switch ft.tp { + case mysql.TypeVarString, mysql.TypeString: + skipWriteBinary := false + if ft.charset == charset.CharsetBin && ft.collate == charset.CollationBin { + ctx.WriteKeyWord("BINARY") + skipWriteBinary = true + } else { + ctx.WriteKeyWord("CHAR") + } + if ft.flen != UnspecifiedLength { + ctx.WritePlainf("(%d)", ft.flen) + } + if !explicitCharset { + break + } + if !skipWriteBinary && ft.flag&mysql.BinaryFlag != 0 { + ctx.WriteKeyWord(" BINARY") + } + if ft.charset != charset.CharsetBin && ft.charset != mysql.DefaultCharset { + ctx.WriteKeyWord(" CHARSET ") + ctx.WriteKeyWord(ft.charset) + } + case mysql.TypeDate: + ctx.WriteKeyWord("DATE") + case mysql.TypeDatetime: + ctx.WriteKeyWord("DATETIME") + if ft.decimal > 0 { + ctx.WritePlainf("(%d)", ft.decimal) + } + case mysql.TypeNewDecimal: + ctx.WriteKeyWord("DECIMAL") + if ft.flen > 0 && ft.decimal > 0 { + ctx.WritePlainf("(%d, %d)", ft.flen, ft.decimal) + } else if ft.flen > 0 { + ctx.WritePlainf("(%d)", ft.flen) + } + case mysql.TypeDuration: + ctx.WriteKeyWord("TIME") + if ft.decimal > 0 { + ctx.WritePlainf("(%d)", ft.decimal) + } + case mysql.TypeLonglong: + if ft.flag&mysql.UnsignedFlag != 0 { + ctx.WriteKeyWord("UNSIGNED") + } else { + ctx.WriteKeyWord("SIGNED") + } + case mysql.TypeJSON: + ctx.WriteKeyWord("JSON") + case mysql.TypeDouble: + ctx.WriteKeyWord("DOUBLE") + case mysql.TypeFloat: + ctx.WriteKeyWord("FLOAT") + case mysql.TypeYear: + ctx.WriteKeyWord("YEAR") + } + if ft.array { + ctx.WritePlain(" ") + ctx.WriteKeyWord("ARRAY") + } +} + +// FormatAsCastType is used for write AST back to string. +func (ft *FieldType) FormatAsCastType(w io.Writer, explicitCharset bool) { + var sb strings.Builder + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) + ft.RestoreAsCastType(restoreCtx, explicitCharset) + fmt.Fprint(w, sb.String()) +} + +// VarStorageLen indicates this column is a variable length column. +const VarStorageLen = -1 + +// StorageLength is the length of stored value for the type. +func (ft *FieldType) StorageLength() int { + switch ft.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, + mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeFloat, mysql.TypeYear, mysql.TypeDuration, + mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp, mysql.TypeEnum, mysql.TypeSet, + mysql.TypeBit: + // This may not be the accurate length, because we may encode them as varint. + return 8 + case mysql.TypeNewDecimal: + precision, frac := ft.flen-ft.decimal, ft.decimal + return precision/digitsPerWord*wordSize + dig2bytes[precision%digitsPerWord] + frac/digitsPerWord*wordSize + dig2bytes[frac%digitsPerWord] + default: + return VarStorageLen + } +} + +// HasCharset indicates if a COLUMN has an associated charset. Returning false here prevents some information +// statements(like `SHOW CREATE TABLE`) from attaching a CHARACTER SET clause to the column. +func HasCharset(ft *FieldType) bool { + switch ft.GetType() { + case mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString, mysql.TypeBlob, + mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + return !mysql.HasBinaryFlag(ft.flag) + case mysql.TypeEnum, mysql.TypeSet: + return true + } + return false +} + +// for json +type jsonFieldType struct { + Tp byte + Flag uint + Flen int + Decimal int + Charset string + Collate string + Elems []string + ElemsIsBinaryLit []bool + Array bool +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (ft *FieldType) UnmarshalJSON(data []byte) error { + var r jsonFieldType + err := json.Unmarshal(data, &r) + if err == nil { + ft.tp = r.Tp + ft.flag = r.Flag + ft.flen = r.Flen + ft.decimal = r.Decimal + ft.charset = r.Charset + ft.collate = r.Collate + ft.elems = r.Elems + ft.elemsIsBinaryLit = r.ElemsIsBinaryLit + ft.array = r.Array + } + return err +} + +// MarshalJSON marshals the FieldType to JSON. +func (ft *FieldType) MarshalJSON() ([]byte, error) { + var r jsonFieldType + r.Tp = ft.tp + r.Flag = ft.flag + r.Flen = ft.flen + r.Decimal = ft.decimal + r.Charset = ft.charset + r.Collate = ft.collate + r.Elems = ft.elems + r.ElemsIsBinaryLit = ft.elemsIsBinaryLit + r.Array = ft.array + return json.Marshal(r) +} + +const emptyFieldTypeSize = int64(unsafe.Sizeof(FieldType{})) + +// MemoryUsage return the memory usage of FieldType +func (ft *FieldType) MemoryUsage() (sum int64) { + if ft == nil { + return + } + sum = emptyFieldTypeSize + int64(len(ft.charset)+len(ft.collate)) + int64(cap(ft.elems))*int64(unsafe.Sizeof(*new(string))) + + int64(cap(ft.elemsIsBinaryLit))*int64(unsafe.Sizeof(*new(bool))) + + for _, s := range ft.elems { + sum += int64(len(s)) + } + return +} diff --git a/pkg/parser/types/field_type_test.go b/pkg/parser/types/field_type_test.go new file mode 100644 index 0000000000000..a35a097ff1bde --- /dev/null +++ b/pkg/parser/types/field_type_test.go @@ -0,0 +1,331 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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, +// See the License for the specific language governing permissions and +// limitations under the License. + +package types_test + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + // import parser_driver + _ "github.com/pingcap/tidb/pkg/parser/test_driver" + . "github.com/pingcap/tidb/pkg/parser/types" + "github.com/stretchr/testify/require" +) + +func TestFieldType(t *testing.T) { + ft := NewFieldType(mysql.TypeDuration) + require.Equal(t, UnspecifiedLength, ft.GetFlen()) + require.Equal(t, UnspecifiedLength, ft.GetDecimal()) + ft.SetDecimal(5) + require.Equal(t, "time(5)", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeLong) + ft.SetFlen(5) + ft.SetFlag(mysql.UnsignedFlag | mysql.ZerofillFlag) + require.Equal(t, "int(5) UNSIGNED ZEROFILL", ft.String()) + require.Equal(t, "int(5) unsigned", ft.InfoSchemaStr()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(12) // Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "float(12,3)", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(12) // Default + ft.SetDecimal(-1) // Default + require.Equal(t, "float", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(5) // Not Default + ft.SetDecimal(-1) // Default + require.Equal(t, "float", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(7) // Not Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "float(7,3)", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(22) // Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "double(22,3)", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(22) // Default + ft.SetDecimal(-1) // Default + require.Equal(t, "double", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(5) // Not Default + ft.SetDecimal(-1) // Default + require.Equal(t, "double", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(7) // Not Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "double(7,3)", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeBlob) + ft.SetFlen(10) + ft.SetCharset("UTF8") + ft.SetCollate("UTF8_UNICODE_GI") + require.Equal(t, "text CHARACTER SET UTF8 COLLATE UTF8_UNICODE_GI", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeVarchar) + ft.SetFlen(10) + ft.AddFlag(mysql.BinaryFlag) + require.Equal(t, "varchar(10) BINARY", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeString) + ft.SetCharset(charset.CharsetBin) + ft.AddFlag(mysql.BinaryFlag) + require.Equal(t, "binary(1)", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a", "b"}) + require.Equal(t, "enum('a','b')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"'a'", "'b'"}) + require.Equal(t, "enum('''a''','''b''')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a\nb", "a\tb", "a\rb"}) + require.Equal(t, "enum('a\\nb','a\tb','a\\rb')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) + require.Equal(t, "enum('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a", "b"}) + require.Equal(t, "set('a','b')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"'a'", "'b'"}) + require.Equal(t, "set('''a''','''b''')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) + require.Equal(t, "set('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a'\nb", "a'b\tc"}) + require.Equal(t, "set('a''\\nb','a''b c')", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeTimestamp) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "timestamp(2)", ft.String()) + require.False(t, HasCharset(ft)) + ft = NewFieldType(mysql.TypeTimestamp) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "timestamp", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeDatetime) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "datetime(2)", ft.String()) + require.False(t, HasCharset(ft)) + ft = NewFieldType(mysql.TypeDatetime) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "datetime", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeDate) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "date", ft.String()) + require.False(t, HasCharset(ft)) + ft = NewFieldType(mysql.TypeDate) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "date", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeYear) + ft.SetFlen(4) + ft.SetDecimal(0) + require.Equal(t, "year(4)", ft.String()) + require.False(t, HasCharset(ft)) + ft = NewFieldType(mysql.TypeYear) + ft.SetFlen(2) + ft.SetDecimal(2) + require.Equal(t, "year(2)", ft.String()) + require.False(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeVarchar) + ft.SetFlen(0) + ft.SetDecimal(0) + require.Equal(t, "varchar(0)", ft.String()) + require.True(t, HasCharset(ft)) + + ft = NewFieldType(mysql.TypeString) + ft.SetFlen(0) + ft.SetDecimal(0) + require.Equal(t, "char(0)", ft.String()) + require.True(t, HasCharset(ft)) +} + +func TestHasCharsetFromStmt(t *testing.T) { + template := "CREATE TABLE t(a %s)" + + types := []struct { + strType string + hasCharset bool + }{ + {"int", false}, + {"real", false}, + {"float", false}, + {"bit", false}, + {"bool", false}, + {"char(1)", true}, + {"national char(1)", true}, + {"binary", false}, + {"varchar(1)", true}, + {"national varchar(1)", true}, + {"varbinary(1)", false}, + {"year", false}, + {"date", false}, + {"time", false}, + {"datetime", false}, + {"timestamp", false}, + {"blob", false}, + {"tinyblob", false}, + {"mediumblob", false}, + {"longblob", false}, + {"bit", false}, + {"text", true}, + {"tinytext", true}, + {"mediumtext", true}, + {"longtext", true}, + {"json", false}, + {"enum('1')", true}, + {"set('1')", true}, + } + + p := parser.New() + for _, typ := range types { + sql := fmt.Sprintf(template, typ.strType) + stmt, err := p.ParseOneStmt(sql, "", "") + require.NoError(t, err) + + col := stmt.(*ast.CreateTableStmt).Cols[0] + require.Equal(t, typ.hasCharset, HasCharset(col.Tp)) + } +} + +func TestEnumSetFlen(t *testing.T) { + p := parser.New() + cases := []struct { + sql string + ex int + }{ + {"enum('a')", 1}, + {"enum('a', 'b')", 1}, + {"enum('a', 'bb')", 2}, + {"enum('a', 'b', 'c')", 1}, + {"enum('a', 'bb', 'c')", 2}, + {"enum('a', 'bb', 'c')", 2}, + {"enum('')", 0}, + {"enum('a', '')", 1}, + {"set('a')", 1}, + {"set('a', 'b')", 3}, + {"set('a', 'bb')", 4}, + {"set('a', 'b', 'c')", 5}, + {"set('a', 'bb', 'c')", 6}, + {"set('')", 0}, + {"set('a', '')", 2}, + } + + for _, ca := range cases { + stmt, err := p.ParseOneStmt(fmt.Sprintf("create table t (e %v)", ca.sql), "", "") + require.NoError(t, err) + col := stmt.(*ast.CreateTableStmt).Cols[0] + require.Equal(t, ca.ex, col.Tp.GetFlen()) + } +} + +func TestFieldTypeEqual(t *testing.T) { + // tp not equal + ft1 := NewFieldType(mysql.TypeDouble) + ft2 := NewFieldType(mysql.TypeFloat) + require.Equal(t, false, ft1.Equal(ft2)) + + // decimal not equal + ft2 = NewFieldType(mysql.TypeDouble) + ft2.SetDecimal(5) + require.Equal(t, false, ft1.Equal(ft2)) + + // flen not equal and decimal not -1 + ft1.SetDecimal(5) + ft1.SetFlen(22) + require.Equal(t, false, ft1.Equal(ft2)) + + // flen equal + ft2.SetFlen(22) + require.Equal(t, true, ft1.Equal(ft2)) + + // decimal is -1 + ft1.SetDecimal(-1) + ft2.SetDecimal(-1) + ft1.SetFlen(23) + require.Equal(t, true, ft1.Equal(ft2)) +} + +func TestCompactStr(t *testing.T) { + cases := []struct { + t byte // Field Type + flen int // Field Length + flags uint // Field Flags, e.g. ZEROFILL + e1 string // Expected string with TiDBStrictIntegerDisplayWidth disabled + e2 string // Expected string with TiDBStrictIntegerDisplayWidth enabled + }{ + // TINYINT(1) is considered a bool by connectors, this should always display + // the display length. + {mysql.TypeTiny, 1, 0, `tinyint(1)`, `tinyint(1)`}, + {mysql.TypeTiny, 2, 0, `tinyint(2)`, `tinyint`}, + + // If the ZEROFILL flag is set the display length should not be hidden. + {mysql.TypeLong, 10, 0, `int(10)`, `int`}, + {mysql.TypeLong, 10, mysql.ZerofillFlag, `int(10)`, `int(10)`}, + } + for _, cc := range cases { + ft := NewFieldType(cc.t) + ft.SetFlen(cc.flen) + ft.SetFlag(cc.flags) + + TiDBStrictIntegerDisplayWidth = false + require.Equal(t, cc.e1, ft.CompactStr()) + + TiDBStrictIntegerDisplayWidth = true + require.Equal(t, cc.e2, ft.CompactStr()) + } +} diff --git a/parser/yy_parser.go b/pkg/parser/yy_parser.go similarity index 98% rename from parser/yy_parser.go rename to pkg/parser/yy_parser.go index a7c47ea1feaf4..eb60bb4972962 100644 --- a/parser/yy_parser.go +++ b/pkg/parser/yy_parser.go @@ -21,12 +21,12 @@ import ( "unicode" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/parser/types" ) var ( diff --git a/pkg/planner/BUILD.bazel b/pkg/planner/BUILD.bazel new file mode 100644 index 0000000000000..8c23060065bc3 --- /dev/null +++ b/pkg/planner/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "planner", + srcs = ["optimize.go"], + importpath = "github.com/pingcap/tidb/pkg/planner", + visibility = ["//visibility:public"], + deps = [ + "//pkg/bindinfo", + "//pkg/domain", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/planner/cascades", + "//pkg/planner/core", + "//pkg/planner/util/debugtrace", + "//pkg/privilege", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/types", + "//pkg/util/hint", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/parser", + "//pkg/util/topsql", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/planner/cardinality/BUILD.bazel b/pkg/planner/cardinality/BUILD.bazel new file mode 100644 index 0000000000000..911368cf9135a --- /dev/null +++ b/pkg/planner/cardinality/BUILD.bazel @@ -0,0 +1,93 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cardinality", + srcs = [ + "cross_estimation.go", + "join.go", + "ndv.go", + "pseudo.go", + "row_count_column.go", + "row_count_index.go", + "row_size.go", + "selectivity.go", + "trace.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/cardinality", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/property", + "//pkg/planner/util", + "//pkg/planner/util/debugtrace", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/ranger", + "//pkg/util/set", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_golang_x_exp//maps", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "cardinality_test", + timeout = "short", + srcs = [ + "main_test.go", + "row_count_test.go", + "row_size_test.go", + "selectivity_test.go", + "trace_test.go", + ], + data = glob(["testdata/**"]), + embed = [":cardinality"], + flaky = True, + shard_count = 25, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/executor", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/ranger", + "//pkg/util/tracing", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/cardinality/cross_estimation.go b/pkg/planner/cardinality/cross_estimation.go similarity index 97% rename from planner/cardinality/cross_estimation.go rename to pkg/planner/cardinality/cross_estimation.go index 510c86aba46fe..f25bad34acb56 100644 --- a/planner/cardinality/cross_estimation.go +++ b/pkg/planner/cardinality/cross_estimation.go @@ -17,14 +17,14 @@ package cardinality import ( "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/set" ) // SelectionFactor is the factor which is used to estimate the row count of selection. diff --git a/pkg/planner/cardinality/join.go b/pkg/planner/cardinality/join.go new file mode 100644 index 0000000000000..40dc24db8caf7 --- /dev/null +++ b/pkg/planner/cardinality/join.go @@ -0,0 +1,51 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cardinality + +import ( + "math" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// EstimateFullJoinRowCount estimates the row count of a full join. +func EstimateFullJoinRowCount(sctx sessionctx.Context, + isCartesian bool, + leftProfile, rightProfile *property.StatsInfo, + leftJoinKeys, rightJoinKeys []*expression.Column, + leftSchema, rightSchema *expression.Schema, + leftNAJoinKeys, rightNAJoinKeys []*expression.Column) float64 { + if isCartesian { + return leftProfile.RowCount * rightProfile.RowCount + } + var leftKeyNDV, rightKeyNDV float64 + var leftColCnt, rightColCnt int + if len(leftJoinKeys) > 0 || len(rightJoinKeys) > 0 { + leftKeyNDV, leftColCnt = EstimateColsNDVWithMatchedLen(leftJoinKeys, leftSchema, leftProfile) + rightKeyNDV, rightColCnt = EstimateColsNDVWithMatchedLen(rightJoinKeys, rightSchema, rightProfile) + } else { + leftKeyNDV, leftColCnt = EstimateColsNDVWithMatchedLen(leftNAJoinKeys, leftSchema, leftProfile) + rightKeyNDV, rightColCnt = EstimateColsNDVWithMatchedLen(rightNAJoinKeys, rightSchema, rightProfile) + } + count := leftProfile.RowCount * rightProfile.RowCount / max(leftKeyNDV, rightKeyNDV) + if sctx.GetSessionVars().TiDBOptJoinReorderThreshold <= 0 { + return count + } + // If we enable the DP choice, we multiple the 0.9 for each remained join key supposing that 0.9 is the correlation factor between them. + // This estimation logic is referred to Presto. + return count * math.Pow(0.9, float64(len(leftJoinKeys)-max(leftColCnt, rightColCnt))) +} diff --git a/pkg/planner/cardinality/main_test.go b/pkg/planner/cardinality/main_test.go new file mode 100644 index 0000000000000..4831dcefa4953 --- /dev/null +++ b/pkg/planner/cardinality/main_test.go @@ -0,0 +1,65 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cardinality + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper, 3) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + if !flag.Parsed() { + flag.Parse() + } + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + testDataMap.LoadTestSuiteData("testdata", "cardinality_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetCardinalitySuiteData() testdata.TestData { + return testDataMap["cardinality_suite"] +} + +// MockStatsNode is only used for test. +func MockStatsNode(id int64, m int64, num int) *StatsNode { + return &StatsNode{ID: id, mask: m, numCols: num} +} diff --git a/planner/cardinality/ndv.go b/pkg/planner/cardinality/ndv.go similarity index 95% rename from planner/cardinality/ndv.go rename to pkg/planner/cardinality/ndv.go index c78110276b5c6..2d9b4aa9d10e6 100644 --- a/planner/cardinality/ndv.go +++ b/pkg/planner/cardinality/ndv.go @@ -17,10 +17,10 @@ package cardinality import ( "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/planner/cardinality/pseudo.go b/pkg/planner/cardinality/pseudo.go similarity index 95% rename from planner/cardinality/pseudo.go rename to pkg/planner/cardinality/pseudo.go index 600274904b580..bd0d62a4b7d7b 100644 --- a/planner/cardinality/pseudo.go +++ b/pkg/planner/cardinality/pseudo.go @@ -18,13 +18,13 @@ import ( "math" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" ) const ( diff --git a/planner/cardinality/row_count_column.go b/pkg/planner/cardinality/row_count_column.go similarity index 97% rename from planner/cardinality/row_count_column.go rename to pkg/planner/cardinality/row_count_column.go index 32fbab9d922f8..f9f075dc818f6 100644 --- a/planner/cardinality/row_count_column.go +++ b/pkg/planner/cardinality/row_count_column.go @@ -16,14 +16,14 @@ package cardinality import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/ranger" ) func init() { diff --git a/planner/cardinality/row_count_index.go b/pkg/planner/cardinality/row_count_index.go similarity index 97% rename from planner/cardinality/row_count_index.go rename to pkg/planner/cardinality/row_count_index.go index e841636846e89..06ff7c9627cf3 100644 --- a/planner/cardinality/row_count_index.go +++ b/pkg/planner/cardinality/row_count_index.go @@ -22,17 +22,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/ranger" ) // GetRowCountByIndexRanges estimates the row count by a slice of Range. diff --git a/planner/cardinality/row_count_test.go b/pkg/planner/cardinality/row_count_test.go similarity index 89% rename from planner/cardinality/row_count_test.go rename to pkg/planner/cardinality/row_count_test.go index 6cfeef7818d39..de3ed97815b28 100644 --- a/planner/cardinality/row_count_test.go +++ b/pkg/planner/cardinality/row_count_test.go @@ -17,11 +17,11 @@ package cardinality import ( "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/planner/cardinality/row_size.go b/pkg/planner/cardinality/row_size.go similarity index 95% rename from planner/cardinality/row_size.go rename to pkg/planner/cardinality/row_size.go index 6417e251f3343..be5bfdf01b7a2 100644 --- a/planner/cardinality/row_size.go +++ b/pkg/planner/cardinality/row_size.go @@ -17,13 +17,13 @@ package cardinality import ( "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/chunk" ) const pseudoColSize = 8.0 diff --git a/planner/cardinality/row_size_test.go b/pkg/planner/cardinality/row_size_test.go similarity index 96% rename from planner/cardinality/row_size_test.go rename to pkg/planner/cardinality/row_size_test.go index 976fefe6da911..41d9404676c89 100644 --- a/planner/cardinality/row_size_test.go +++ b/pkg/planner/cardinality/row_size_test.go @@ -19,10 +19,10 @@ import ( "testing" "unsafe" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/planner/cardinality/selectivity.go b/pkg/planner/cardinality/selectivity.go similarity index 98% rename from planner/cardinality/selectivity.go rename to pkg/planner/cardinality/selectivity.go index 49da670b9b5d0..d2d5d8ff8041a 100644 --- a/planner/cardinality/selectivity.go +++ b/pkg/planner/cardinality/selectivity.go @@ -21,18 +21,18 @@ import ( "slices" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - planutil "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + planutil "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" "go.uber.org/zap" "golang.org/x/exp/maps" ) diff --git a/planner/cardinality/selectivity_test.go b/pkg/planner/cardinality/selectivity_test.go similarity index 97% rename from planner/cardinality/selectivity_test.go rename to pkg/planner/cardinality/selectivity_test.go index 34b2a7c34613d..2706e1d89e6da 100644 --- a/planner/cardinality/selectivity_test.go +++ b/pkg/planner/cardinality/selectivity_test.go @@ -26,24 +26,24 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/stretchr/testify/require" ) @@ -543,11 +543,11 @@ func TestIndexEstimationCrossValidate(t *testing.T) { tk.MustExec("create table t(a int, b int, key(a,b))") tk.MustExec("insert into t values(1, 1), (1, 2), (1, 3), (2, 2)") tk.MustExec("analyze table t") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/statistics/table/mockQueryBytesMaxUint64", `return(100000)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/statistics/table/mockQueryBytesMaxUint64", `return(100000)`)) tk.MustQuery("explain select * from t where a = 1 and b = 2").Check(testkit.Rows( "IndexReader_6 1.00 root index:IndexRangeScan_5", "└─IndexRangeScan_5 1.00 cop[tikv] table:t, index:a(a, b) range:[1 2,1 2], keep order:false")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/statistics/table/mockQueryBytesMaxUint64")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/statistics/table/mockQueryBytesMaxUint64")) // Test issue 22466 tk.MustExec("drop table if exists t2") diff --git a/planner/cardinality/testdata/cardinality_suite_in.json b/pkg/planner/cardinality/testdata/cardinality_suite_in.json similarity index 100% rename from planner/cardinality/testdata/cardinality_suite_in.json rename to pkg/planner/cardinality/testdata/cardinality_suite_in.json diff --git a/planner/cardinality/testdata/cardinality_suite_out.json b/pkg/planner/cardinality/testdata/cardinality_suite_out.json similarity index 91% rename from planner/cardinality/testdata/cardinality_suite_out.json rename to pkg/planner/cardinality/testdata/cardinality_suite_out.json index 0ab8f7aa2466b..7360396d69701 100644 --- a/planner/cardinality/testdata/cardinality_suite_out.json +++ b/pkg/planner/cardinality/testdata/cardinality_suite_out.json @@ -1336,7 +1336,7 @@ { "ResultForV1": [ { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "eq(test.t.a, 100)", @@ -1344,7 +1344,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -1352,7 +1352,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1366,7 +1366,7 @@ "TopN total count": 1095 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1376,13 +1376,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAABk", "Value": "KindInt64 100" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 13 }, @@ -1413,7 +1413,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 2, "Ranges": [ @@ -1421,7 +1421,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1435,7 +1435,7 @@ "TopN total count": 1098 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1445,13 +1445,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAFe", "Value": "KindInt64 350" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -1462,7 +1462,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 3, 3, @@ -1502,7 +1502,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -1510,7 +1510,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 3080 @@ -1522,7 +1522,7 @@ "TopN total count": 0 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV1": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV1": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1532,11 +1532,11 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.getEqualCondSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getEqualCondSelectivity": [ { - "github.com/pingcap/tidb/planner/cardinality.crossValidationSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.crossValidationSelectivity": [ { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1545,7 +1545,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1555,13 +1555,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAABk", "Value": "KindInt64 100" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 13 }, @@ -1586,7 +1586,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1595,7 +1595,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1605,13 +1605,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAFe", "Value": "KindInt64 350" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -1622,7 +1622,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 3, 3, @@ -1664,9 +1664,9 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).QueryBytes": [ + "github.com/pingcap/tidb/pkg/statistics.(*Index).QueryBytes": [ { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 3, 3, @@ -1715,7 +1715,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 2, "Ranges": [ @@ -1723,7 +1723,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 3080 @@ -1735,7 +1735,7 @@ "TopN total count": 0 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV1": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV1": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1745,11 +1745,11 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.getEqualCondSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getEqualCondSelectivity": [ { - "github.com/pingcap/tidb/planner/cardinality.crossValidationSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.crossValidationSelectivity": [ { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1758,7 +1758,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1768,13 +1768,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAFe", "Value": "KindInt64 350" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -1785,7 +1785,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 3, 3, @@ -1827,9 +1827,9 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).QueryBytes": [ + "github.com/pingcap/tidb/pkg/statistics.(*Index).QueryBytes": [ { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 2, 2, @@ -1893,7 +1893,7 @@ ], "ResultForV2": [ { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "eq(test.t.a, 100)", @@ -1901,7 +1901,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -1909,7 +1909,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1923,7 +1923,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -1933,13 +1933,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAABk", "Value": "KindInt64 100" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 11 }, @@ -1970,7 +1970,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 2, "Ranges": [ @@ -1978,7 +1978,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -1992,7 +1992,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2002,13 +2002,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAFe", "Value": "KindInt64 350" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2019,7 +2019,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).EqualRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).EqualRowCount": [ { "Locate value in buckets": { "BucketIdx": 105, @@ -2065,7 +2065,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -2073,7 +2073,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 2980 @@ -2085,7 +2085,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV2": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV2": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2095,12 +2095,12 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnIndex": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnIndex": [ { "Encoded Value": "A4AAAAAAAABkA4AAAAAAAAFe" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2111,7 +2111,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).EqualRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).EqualRowCount": [ { "Locate value in buckets": { "BucketIdx": 136, @@ -2156,7 +2156,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 2, "Ranges": [ @@ -2164,7 +2164,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 2980 @@ -2176,7 +2176,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV2": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV2": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2186,12 +2186,12 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnIndex": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnIndex": [ { "Encoded Value": "A4AAAAAAAAFe" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2202,7 +2202,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).EqualRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).EqualRowCount": [ { "Locate value in buckets": { "BucketIdx": 105, @@ -2264,7 +2264,7 @@ { "ResultForV1": [ { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "lt(test.t.a, -1500)", @@ -2273,7 +2273,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -2281,7 +2281,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -2295,7 +2295,7 @@ "TopN total count": 1095 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2305,7 +2305,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -2336,7 +2336,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -2367,7 +2367,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).OutOfRangeRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).OutOfRangeRowCount": [ { "lDatum": "KindMinNotNull ", "modifyCount": 0, @@ -2408,7 +2408,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 2, "Ranges": [ @@ -2416,7 +2416,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -2430,7 +2430,7 @@ "TopN total count": 1098 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2440,13 +2440,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 401" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2457,7 +2457,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 2, @@ -2489,13 +2489,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 402" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2506,7 +2506,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 3, @@ -2546,7 +2546,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -2554,7 +2554,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 3080 @@ -2566,7 +2566,7 @@ "TopN total count": 0 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV1": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV1": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2576,7 +2576,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV2": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV2": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2586,7 +2586,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -2617,7 +2617,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -2648,7 +2648,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).OutOfRangeRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).OutOfRangeRowCount": [ { "lDatum": "KindBytes \\x01", "modifyCount": 0, @@ -2697,7 +2697,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 2, "Ranges": [ @@ -2705,7 +2705,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 3080 @@ -2717,7 +2717,7 @@ "TopN total count": 0 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV1": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV1": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2727,11 +2727,11 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.getEqualCondSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getEqualCondSelectivity": [ { - "github.com/pingcap/tidb/planner/cardinality.crossValidationSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.crossValidationSelectivity": [ { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -2740,7 +2740,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2750,13 +2750,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 400" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 7 }, @@ -2779,13 +2779,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 401" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2796,7 +2796,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 2, @@ -2828,13 +2828,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 402" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2845,7 +2845,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 3, @@ -2877,13 +2877,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 403" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -2894,7 +2894,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 2, 2, @@ -2936,9 +2936,9 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).QueryBytes": [ + "github.com/pingcap/tidb/pkg/statistics.(*Index).QueryBytes": [ { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 2, @@ -2973,11 +2973,11 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.getEqualCondSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getEqualCondSelectivity": [ { - "github.com/pingcap/tidb/planner/cardinality.crossValidationSelectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.crossValidationSelectivity": [ { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -2986,7 +2986,7 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -2996,13 +2996,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 400" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 7 }, @@ -3025,13 +3025,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 401" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -3042,7 +3042,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 2, @@ -3074,13 +3074,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 402" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -3091,7 +3091,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 1, 3, @@ -3123,13 +3123,13 @@ } }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 403" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": -1 }, @@ -3140,7 +3140,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 2, 2, @@ -3182,9 +3182,9 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).QueryBytes": [ + "github.com/pingcap/tidb/pkg/statistics.(*Index).QueryBytes": [ { - "github.com/pingcap/tidb/statistics.(*CMSketch).queryHashValue": { + "github.com/pingcap/tidb/pkg/statistics.(*CMSketch).queryHashValue": { "Origin Values": [ 2, 3, @@ -3255,7 +3255,7 @@ ], "ResultForV2": [ { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "lt(test.t.a, -1500)", @@ -3264,7 +3264,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -3272,7 +3272,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -3286,7 +3286,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -3296,7 +3296,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -3327,7 +3327,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -3358,12 +3358,12 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*TopN).BetweenCount": { + "github.com/pingcap/tidb/pkg/statistics.(*TopN).BetweenCount": { "Result": 0 } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).OutOfRangeRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).OutOfRangeRowCount": [ { "lDatum": "KindMinNotNull ", "modifyCount": 100, @@ -3404,7 +3404,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 2, "Ranges": [ @@ -3412,7 +3412,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": true, "InValidForCollPseudo": false, "IsInvalid": false, @@ -3426,7 +3426,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.GetColumnRowCount": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetColumnRowCount": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -3436,7 +3436,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 111, @@ -3467,7 +3467,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 111, @@ -3498,7 +3498,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*TopN).BetweenCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).BetweenCount": [ { "Related TopN Range": { "Count": [ @@ -3516,13 +3516,13 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.equalRowCountOnColumn": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.equalRowCountOnColumn": [ { "Encoded": "A4AAAAAAAAGQ", "Value": "KindInt64 400" }, { - "github.com/pingcap/tidb/statistics.(*TopN).QueryTopN": [ + "github.com/pingcap/tidb/pkg/statistics.(*TopN).QueryTopN": [ { "FindTopN idx": 9 }, @@ -3553,7 +3553,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -3561,7 +3561,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 2980 @@ -3573,7 +3573,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV2": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV2": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -3583,7 +3583,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -3614,7 +3614,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 0, @@ -3645,12 +3645,12 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*TopN).BetweenCount": { + "github.com/pingcap/tidb/pkg/statistics.(*TopN).BetweenCount": { "Result": 0 } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).OutOfRangeRowCount": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).OutOfRangeRowCount": [ { "lDatum": "KindBytes \\x01", "modifyCount": 100, @@ -3682,7 +3682,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 2, "Ranges": [ @@ -3690,7 +3690,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": false, "IsInvalid": false, "TotalCount": 2980 @@ -3702,7 +3702,7 @@ "TopN total count": 1000 }, { - "github.com/pingcap/tidb/planner/cardinality.getIndexRowCountForStatsV2": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.getIndexRowCountForStatsV2": [ { "Start estimate range": { "CurrentRowCount": 0, @@ -3712,7 +3712,7 @@ } }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 111, @@ -3743,7 +3743,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Histogram).LessRowCountWithBktIdx": [ + "github.com/pingcap/tidb/pkg/statistics.(*Histogram).LessRowCountWithBktIdx": [ { "Locate value in buckets": { "BucketIdx": 111, @@ -3774,7 +3774,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*TopN).BetweenCount": { + "github.com/pingcap/tidb/pkg/statistics.(*TopN).BetweenCount": { "Result": 0 } }, diff --git a/pkg/planner/cardinality/trace.go b/pkg/planner/cardinality/trace.go new file mode 100644 index 0000000000000..346703a57f981 --- /dev/null +++ b/pkg/planner/cardinality/trace.go @@ -0,0 +1,306 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cardinality + +import ( + "bytes" + "encoding/json" + "errors" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/tracing" + "go.uber.org/zap" +) + +// ceTraceExpr appends an expression and related information into CE trace +func ceTraceExpr(sctx sessionctx.Context, tableID int64, tp string, expr expression.Expression, rowCount float64) { + exprStr, err := exprToString(expr) + if err != nil { + logutil.BgLogger().Debug("Failed to trace CE of an expression", zap.String("category", "OptimizerTrace"), + zap.Any("expression", expr)) + return + } + rec := tracing.CETraceRecord{ + TableID: tableID, + Type: tp, + Expr: exprStr, + RowCount: uint64(rowCount), + } + sc := sctx.GetSessionVars().StmtCtx + sc.OptimizerCETrace = append(sc.OptimizerCETrace, &rec) +} + +// exprToString prints an Expression into a string which can appear in a SQL. +// +// It might be too tricky because it makes use of TiDB allowing using internal function name in SQL. +// For example, you can write `eq`(a, 1), which is the same as a = 1. +// We should have implemented this by first implementing a method to turn an expression to an AST +// +// then call astNode.Restore(), like the Constant case here. But for convenience, we use this trick for now. +// +// It may be more appropriate to put this in expression package. But currently we only use it for CE trace, +// +// and it may not be general enough to handle all possible expressions. So we put it here for now. +func exprToString(e expression.Expression) (string, error) { + switch expr := e.(type) { + case *expression.ScalarFunction: + var buffer bytes.Buffer + buffer.WriteString("`" + expr.FuncName.L + "`(") + switch expr.FuncName.L { + case ast.Cast: + for _, arg := range expr.GetArgs() { + argStr, err := exprToString(arg) + if err != nil { + return "", err + } + buffer.WriteString(argStr) + buffer.WriteString(", ") + buffer.WriteString(expr.RetType.String()) + } + default: + for i, arg := range expr.GetArgs() { + argStr, err := exprToString(arg) + if err != nil { + return "", err + } + buffer.WriteString(argStr) + if i+1 != len(expr.GetArgs()) { + buffer.WriteString(", ") + } + } + } + buffer.WriteString(")") + return buffer.String(), nil + case *expression.Column: + return expr.String(), nil + case *expression.CorrelatedColumn: + return "", errors.New("tracing for correlated columns not supported now") + case *expression.Constant: + value, err := expr.Eval(chunk.Row{}) + if err != nil { + return "", err + } + valueExpr := driver.ValueExpr{Datum: value} + var buffer bytes.Buffer + restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buffer) + err = valueExpr.Restore(restoreCtx) + if err != nil { + return "", err + } + return buffer.String(), nil + } + return "", errors.New("unexpected type of Expression") +} + +/* + Below is debug trace for GetRowCountByXXX(). +*/ + +type getRowCountInput struct { + Ranges []string + ID int64 +} + +func debugTraceGetRowCountInput( + s sessionctx.Context, + id int64, + ranges ranger.Ranges, +) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + newCtx := &getRowCountInput{ + ID: id, + Ranges: make([]string, len(ranges)), + } + for i, r := range ranges { + newCtx.Ranges[i] = r.String() + } + root.AppendStepToCurrentContext(newCtx) +} + +// GetTblInfoForUsedStatsByPhysicalID get table name, partition name and TableInfo that will be used to record used stats. +var GetTblInfoForUsedStatsByPhysicalID func(sctx sessionctx.Context, id int64) (fullName string, tblInfo *model.TableInfo) + +// recordUsedItemStatsStatus only records un-FullLoad item load status during user query +func recordUsedItemStatsStatus(sctx sessionctx.Context, stats interface{}, tableID, id int64) { + // Sometimes we try to use stats on _tidb_rowid (id == -1), which must be empty, we ignore this case here. + if id <= 0 { + return + } + var isIndex, missing bool + var loadStatus *statistics.StatsLoadedStatus + switch x := stats.(type) { + case *statistics.Column: + isIndex = false + if x == nil { + missing = true + } else { + loadStatus = &x.StatsLoadedStatus + } + case *statistics.Index: + isIndex = true + if x == nil { + missing = true + } else { + loadStatus = &x.StatsLoadedStatus + } + } + + // no need to record + if !missing && loadStatus.IsFullLoad() { + return + } + + // need to record + statsRecord := sctx.GetSessionVars().StmtCtx.GetUsedStatsInfo(true) + if statsRecord[tableID] == nil { + name, tblInfo := GetTblInfoForUsedStatsByPhysicalID(sctx, tableID) + statsRecord[tableID] = &stmtctx.UsedStatsInfoForTable{ + Name: name, + TblInfo: tblInfo, + } + } + recordForTbl := statsRecord[tableID] + + var recordForColOrIdx map[int64]string + if isIndex { + if recordForTbl.IndexStatsLoadStatus == nil { + recordForTbl.IndexStatsLoadStatus = make(map[int64]string, 1) + } + recordForColOrIdx = recordForTbl.IndexStatsLoadStatus + } else { + if recordForTbl.ColumnStatsLoadStatus == nil { + recordForTbl.ColumnStatsLoadStatus = make(map[int64]string, 1) + } + recordForColOrIdx = recordForTbl.ColumnStatsLoadStatus + } + + if missing { + recordForColOrIdx[id] = "missing" + return + } + recordForColOrIdx[id] = loadStatus.StatusToString() +} + +// ceTraceRange appends a list of ranges and related information into CE trace +func ceTraceRange(sctx sessionctx.Context, tableID int64, colNames []string, ranges []*ranger.Range, tp string, rowCount uint64) { + sc := sctx.GetSessionVars().StmtCtx + allPoint := true + for _, ran := range ranges { + if !ran.IsPointNullable(sctx) { + allPoint = false + break + } + } + if allPoint { + tp = tp + "-Point" + } else { + tp = tp + "-Range" + } + expr, err := ranger.RangesToString(sc, ranges, colNames) + if err != nil { + logutil.BgLogger().Debug("Failed to trace CE of ranges", zap.String("category", "OptimizerTrace"), zap.Error(err)) + } + // We don't need to record meaningless expressions. + if expr == "" || expr == "true" || expr == "false" { + return + } + ceRecord := tracing.CETraceRecord{ + TableID: tableID, + Type: tp, + Expr: expr, + RowCount: rowCount, + } + sc.OptimizerCETrace = append(sc.OptimizerCETrace, &ceRecord) +} + +/* + Below is debug trace for the estimation for each single range inside GetRowCountByXXX(). +*/ + +type startEstimateRangeInfo struct { + Range string + LowValueEncoded []byte + HighValueEncoded []byte + CurrentRowCount float64 +} + +func debugTraceStartEstimateRange( + s sessionctx.Context, + r *ranger.Range, + lowBytes, highBytes []byte, + currentCount float64, +) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &startEstimateRangeInfo{ + CurrentRowCount: currentCount, + Range: r.String(), + LowValueEncoded: lowBytes, + HighValueEncoded: highBytes, + } + root.AppendStepWithNameToCurrentContext(traceInfo, "Start estimate range") +} + +type debugTraceAddRowCountType int8 + +const ( + debugTraceUnknownTypeAddRowCount debugTraceAddRowCountType = iota + debugTraceImpossible + debugTraceUniquePoint + debugTracePoint + debugTraceRange + debugTraceVer1SmallRange +) + +var addRowCountTypeToString = map[debugTraceAddRowCountType]string{ + debugTraceUnknownTypeAddRowCount: "Unknown", + debugTraceImpossible: "Impossible", + debugTraceUniquePoint: "Unique point", + debugTracePoint: "Point", + debugTraceRange: "Range", + debugTraceVer1SmallRange: "Small range in ver1 stats", +} + +func (d debugTraceAddRowCountType) MarshalJSON() ([]byte, error) { + return json.Marshal(addRowCountTypeToString[d]) +} + +type endEstimateRangeInfo struct { + RowCount float64 + Type debugTraceAddRowCountType +} + +func debugTraceEndEstimateRange( + s sessionctx.Context, + count float64, + addType debugTraceAddRowCountType, +) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &endEstimateRangeInfo{ + RowCount: count, + Type: addType, + } + root.AppendStepWithNameToCurrentContext(traceInfo, "End estimate range") +} diff --git a/pkg/planner/cardinality/trace_test.go b/pkg/planner/cardinality/trace_test.go new file mode 100644 index 0000000000000..07b8cbbe8149c --- /dev/null +++ b/pkg/planner/cardinality/trace_test.go @@ -0,0 +1,262 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cardinality_test + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/stretchr/testify/require" +) + +func TestTraceCE(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, d varchar(10), index idx(a, b))") + tk.MustExec(`insert into t values(1, 1, "aaa"), + (1, 1, "bbb"), + (1, 2, "ccc"), + (1, 2, "ddd"), + (2, 2, "aaa"), + (2, 3, "bbb")`) + tk.MustExec("analyze table t") + var ( + in []string + out []struct { + Expr string + Trace []*tracing.CETraceRecord + } + ) + traceSuiteData := cardinality.GetCardinalitySuiteData() + traceSuiteData.LoadTestCases(t, &in, &out) + + // Load needed statistics. + for _, tt := range in { + sql := "explain select * from t where " + tt + tk.MustExec(sql) + } + statsHandle := dom.StatsHandle() + err := statsHandle.LoadNeededHistograms() + require.NoError(t, err) + + sctx := tk.Session().(sessionctx.Context) + is := sctx.GetInfoSchema().(infoschema.InfoSchema) + p := parser.New() + for i, expr := range in { + stmtCtx := sctx.GetSessionVars().StmtCtx + sql := "explain select * from t where " + expr + stmtCtx.EnableOptimizerCETrace = true + stmtCtx.OptimizerCETrace = nil + stmt, err := p.ParseOneStmt(sql, "", "") + require.NoError(t, err) + _, _, err = plannercore.OptimizeAstNode(context.Background(), sctx, stmt, is) + require.NoError(t, err) + + traceResult := sctx.GetSessionVars().StmtCtx.OptimizerCETrace + // Ignore the TableID field because this field is unexported when marshalling to JSON. + for _, rec := range traceResult { + rec.TableID = 0 + } + + testdata.OnRecord(func() { + out[i].Expr = expr + out[i].Trace = traceResult + }) + // Assert using the result in the stmtCtx + require.ElementsMatch(t, traceResult, out[i].Trace) + + sql = "trace plan target='estimation' select * from t where " + expr + result := tk.MustQuery(sql) + require.Len(t, result.Rows(), 1) + resultStr := result.Rows()[0][0].(string) + var resultJSON []*tracing.CETraceRecord + err = json.Unmarshal([]byte(resultStr), &resultJSON) + require.NoError(t, err) + // Assert using the result of trace plan SQL + require.ElementsMatch(t, resultJSON, out[i].Trace) + } +} + +func TestTraceDebugSelectivity(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + statsHandle := dom.StatsHandle() + + // Make the result of v1 analyze result stable + // 1. make sure all rows are always collect as samples + originalSampleSize := executor.MaxRegionSampleSize + executor.MaxRegionSampleSize = 10000 + defer func() { + executor.MaxRegionSampleSize = originalSampleSize + }() + // 2. make the order of samples for building TopN stable + // (the earlier TopN entry will modify the CMSketch, therefore influence later TopN entry's row count, + // see (*SampleCollector).ExtractTopN() for details) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/statistics/StabilizeV1AnalyzeTopN", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/statistics/StabilizeV1AnalyzeTopN")) + }() + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index iab(a, b), index ib(b))") + require.NoError(t, statsHandle.HandleDDLEvent(<-statsHandle.DDLEventCh())) + + // Prepare the data. + + // For column a, from -1000 to 999, each value appears 1 time, + // but if it's dividable by 100, make this value appear 50 times. + // For column b, it's always a+500. + start := -1000 + for i := 0; i < 2000; i += 50 { + sql := "insert into t values " + // 50 rows as a batch + values := make([]string, 0, 50) + for j := 0; j < 50; j++ { + values = append(values, fmt.Sprintf("(%d,%d)", start+i+j, start+i+j+500)) + } + sql = sql + strings.Join(values, ",") + tk.MustExec(sql) + + if i%100 == 0 { + sql := "insert into t values " + topNValue := fmt.Sprintf("(%d,%d) ,", start+i, start+i+500) + sql = sql + strings.Repeat(topNValue, 49) + sql = sql[0 : len(sql)-1] + tk.MustExec(sql) + } + } + require.Nil(t, statsHandle.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t with 1 samplerate, 20 topn") + require.Nil(t, statsHandle.Update(dom.InfoSchema())) + // Add 100 modify count + sql := "insert into t values " + topNValue := fmt.Sprintf("(%d,%d) ,", 5000, 5000) + sql = sql + strings.Repeat(topNValue, 100) + sql = sql[0 : len(sql)-1] + tk.MustExec(sql) + require.Nil(t, statsHandle.DumpStatsDeltaToKV(true)) + require.Nil(t, statsHandle.Update(dom.InfoSchema())) + + var ( + in []string + out []struct { + ResultForV1 interface{} + ResultForV2 interface{} + } + ) + traceSuiteData := cardinality.GetCardinalitySuiteData() + traceSuiteData.LoadTestCases(t, &in, &out) + + // Trigger loading needed statistics. + for _, tt := range in { + sql := "explain " + tt + tk.MustExec(sql) + } + err := statsHandle.LoadNeededHistograms() + require.NoError(t, err) + + sctx := tk.Session().(sessionctx.Context) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tb.Meta() + statsTbl := statsHandle.GetTableStats(tblInfo) + stmtCtx := sctx.GetSessionVars().StmtCtx + stmtCtx.EnableOptimizerDebugTrace = true + + // Collect common information for the following tests. + p := parser.New() + dsSchemaCols := make([][]*expression.Column, 0, len(in)) + selConditions := make([][]expression.Expression, 0, len(in)) + tblInfos := make([]*model.TableInfo, 0, len(in)) + for _, sql := range in { + stmt, err := p.ParseOneStmt(sql, "", "") + require.NoError(t, err) + ret := &plannercore.PreprocessorReturn{} + err = plannercore.Preprocess(context.Background(), sctx, stmt, plannercore.WithPreprocessorReturn(ret)) + require.NoError(t, err) + p, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), sctx, stmt, ret.InfoSchema) + require.NoError(t, err) + + sel := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) + ds := sel.Children()[0].(*plannercore.DataSource) + + dsSchemaCols = append(dsSchemaCols, ds.Schema().Columns) + selConditions = append(selConditions, sel.Conditions) + tblInfos = append(tblInfos, ds.TableInfo()) + } + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + encoder.SetEscapeHTML(false) + + // Test using ver2 stats. + for i, sql := range in { + stmtCtx.OptimizerDebugTrace = nil + histColl := statsTbl.GenerateHistCollFromColumnInfo(tblInfos[i], dsSchemaCols[i]) + _, _, err = cardinality.Selectivity(sctx, histColl, selConditions[i], nil) + require.NoError(t, err, sql, "For ver2") + traceInfo := stmtCtx.OptimizerDebugTrace + buf.Reset() + require.NoError(t, encoder.Encode(traceInfo), sql, "For ver2") + var res interface{} + require.NoError(t, json.Unmarshal(buf.Bytes(), &res), sql, "For ver2") + testdata.OnRecord(func() { + out[i].ResultForV2 = res + }) + require.Equal(t, out[i].ResultForV2, res, sql, "For ver2") + } + + tk.MustExec("set tidb_analyze_version = 1") + tk.MustExec("analyze table t with 20 topn") + require.Nil(t, statsHandle.Update(dom.InfoSchema())) + statsTbl = statsHandle.GetTableStats(tblInfo) + + // Test using ver1 stats. + stmtCtx = sctx.GetSessionVars().StmtCtx + stmtCtx.EnableOptimizerDebugTrace = true + for i, sql := range in { + stmtCtx.OptimizerDebugTrace = nil + histColl := statsTbl.GenerateHistCollFromColumnInfo(tblInfos[i], dsSchemaCols[i]) + _, _, err = cardinality.Selectivity(sctx, histColl, selConditions[i], nil) + require.NoError(t, err, sql, "For ver1") + traceInfo := stmtCtx.OptimizerDebugTrace + buf.Reset() + require.NoError(t, encoder.Encode(traceInfo), sql, "For ver1") + var res interface{} + require.NoError(t, json.Unmarshal(buf.Bytes(), &res), sql, "For ver1") + testdata.OnRecord(func() { + out[i].ResultForV1 = res + }) + require.Equal(t, out[i].ResultForV1, res, sql, "For ver1") + } +} diff --git a/pkg/planner/cascades/BUILD.bazel b/pkg/planner/cascades/BUILD.bazel new file mode 100644 index 0000000000000..683ca0a738bea --- /dev/null +++ b/pkg/planner/cascades/BUILD.bazel @@ -0,0 +1,63 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cascades", + srcs = [ + "enforcer_rules.go", + "implementation_rules.go", + "optimize.go", + "stringer.go", + "transformation_rules.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/cascades", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/planner/implementation", + "//pkg/planner/memo", + "//pkg/planner/property", + "//pkg/planner/util", + "//pkg/sessionctx", + "//pkg/types", + "//pkg/util/ranger", + "//pkg/util/set", + ], +) + +go_test( + name = "cascades_test", + timeout = "short", + srcs = [ + "enforcer_rules_test.go", + "integration_test.go", + "main_test.go", + "optimize_test.go", + "stringer_test.go", + "transformation_rules_test.go", + ], + data = glob(["testdata/**"]), + embed = [":cascades"], + flaky = True, + shard_count = 26, + deps = [ + "//pkg/domain", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/planner/memo", + "//pkg/planner/property", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/cascades/enforcer_rules.go b/pkg/planner/cascades/enforcer_rules.go similarity index 92% rename from planner/cascades/enforcer_rules.go rename to pkg/planner/cascades/enforcer_rules.go index def263c5fcbc4..250a8b33aa535 100644 --- a/planner/cascades/enforcer_rules.go +++ b/pkg/planner/cascades/enforcer_rules.go @@ -17,11 +17,11 @@ package cascades import ( "math" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/implementation" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/implementation" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" ) // Enforcer defines the interface for enforcer rules. diff --git a/planner/cascades/enforcer_rules_test.go b/pkg/planner/cascades/enforcer_rules_test.go similarity index 92% rename from planner/cascades/enforcer_rules_test.go rename to pkg/planner/cascades/enforcer_rules_test.go index a71a7cbd9c680..a2d40b6141ca4 100644 --- a/planner/cascades/enforcer_rules_test.go +++ b/pkg/planner/cascades/enforcer_rules_test.go @@ -17,9 +17,9 @@ package cascades import ( "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/property" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/property" "github.com/stretchr/testify/require" ) diff --git a/planner/cascades/implementation_rules.go b/pkg/planner/cascades/implementation_rules.go similarity index 99% rename from planner/cascades/implementation_rules.go rename to pkg/planner/cascades/implementation_rules.go index dbf5d1c716782..e24602218b063 100644 --- a/planner/cascades/implementation_rules.go +++ b/pkg/planner/cascades/implementation_rules.go @@ -17,11 +17,11 @@ package cascades import ( "math" - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - impl "github.com/pingcap/tidb/planner/implementation" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/property" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + impl "github.com/pingcap/tidb/pkg/planner/implementation" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/property" ) // ImplementationRule defines the interface for implementation rules. diff --git a/pkg/planner/cascades/integration_test.go b/pkg/planner/cascades/integration_test.go new file mode 100644 index 0000000000000..193793a6155df --- /dev/null +++ b/pkg/planner/cascades/integration_test.go @@ -0,0 +1,59 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package cascades_test + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/planner/cascades" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" +) + +func TestCascadePlannerHashedPartTable(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists pt1") + tk.MustExec("create table pt1(a bigint, b bigint) partition by hash(a) partitions 4") + tk.MustExec(`insert into pt1 values(1,10)`) + tk.MustExec(`insert into pt1 values(2,20)`) + tk.MustExec(`insert into pt1 values(3,30)`) + tk.MustExec(`insert into pt1 values(4,40)`) + tk.MustExec(`insert into pt1 values(5,50)`) + + tk.MustExec("set @@tidb_enable_cascades_planner = 1") + var input []string + var output []struct { + SQL string + Plan []string + Result []string + } + integrationSuiteData := cascades.GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, sql := range input { + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + sql).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(sql).Rows()) + }) + tk.MustQuery("explain " + sql).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(sql).Check(testkit.Rows(output[i].Result...)) + } +} diff --git a/pkg/planner/cascades/main_test.go b/pkg/planner/cascades/main_test.go new file mode 100644 index 0000000000000..dc2dfb4d56a24 --- /dev/null +++ b/pkg/planner/cascades/main_test.go @@ -0,0 +1,64 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cascades + +import ( + "flag" + "fmt" + "os" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper, 3) +var stringerSuiteData testdata.TestData +var transformationRulesSuiteData testdata.TestData + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + + testDataMap.LoadTestSuiteData("testdata", "stringer_suite") + testDataMap.LoadTestSuiteData("testdata", "transformation_rules_suite") + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + stringerSuiteData = testDataMap["stringer_suite"] + transformationRulesSuiteData = testDataMap["transformation_rules_suite"] + + if exitCode := m.Run(); exitCode != 0 { + os.Exit(exitCode) + } + + testDataMap.GenerateOutputIfNeeded() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + if err := goleak.Find(opts...); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "goleak: Errors on successful test run: %v\n", err) + os.Exit(1) + } +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} diff --git a/pkg/planner/cascades/optimize.go b/pkg/planner/cascades/optimize.go new file mode 100644 index 0000000000000..d16ab3a7c7113 --- /dev/null +++ b/pkg/planner/cascades/optimize.go @@ -0,0 +1,383 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package cascades + +import ( + "container/list" + "math" + + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// DefaultOptimizer is the optimizer which contains all of the default +// transformation and implementation rules. +var DefaultOptimizer = NewOptimizer() + +// Optimizer is the struct for cascades optimizer. +type Optimizer struct { + transformationRuleBatches []TransformationRuleBatch + implementationRuleMap map[memo.Operand][]ImplementationRule +} + +// NewOptimizer returns a cascades optimizer with default transformation +// rules and implementation rules. +func NewOptimizer() *Optimizer { + return &Optimizer{ + transformationRuleBatches: DefaultRuleBatches, + implementationRuleMap: defaultImplementationMap, + } +} + +// ResetTransformationRules resets the transformationRuleBatches of the optimizer, and returns the optimizer. +func (opt *Optimizer) ResetTransformationRules(ruleBatches ...TransformationRuleBatch) *Optimizer { + opt.transformationRuleBatches = ruleBatches + return opt +} + +// ResetImplementationRules resets the implementationRuleMap of the optimizer, and returns the optimizer. +func (opt *Optimizer) ResetImplementationRules(rules map[memo.Operand][]ImplementationRule) *Optimizer { + opt.implementationRuleMap = rules + return opt +} + +// GetImplementationRules gets all the candidate implementation rules of the optimizer +// for the logical plan node. +func (opt *Optimizer) GetImplementationRules(node plannercore.LogicalPlan) []ImplementationRule { + return opt.implementationRuleMap[memo.GetOperand(node)] +} + +// FindBestPlan is the optimization entrance of the cascades planner. The +// optimization is composed of 3 phases: preprocessing, exploration and implementation. +// +// ------------------------------------------------------------------------------ +// Phase 1: Preprocessing +// ------------------------------------------------------------------------------ +// +// The target of this phase is to preprocess the plan tree by some heuristic +// rules which should always be beneficial, for example Column Pruning. +// +// ------------------------------------------------------------------------------ +// Phase 2: Exploration +// ------------------------------------------------------------------------------ +// +// The target of this phase is to explore all the logically equivalent +// expressions by exploring all the equivalent group expressions of each group. +// +// At the very beginning, there is only one group expression in a Group. After +// applying some transformation rules on certain expressions of the Group, all +// the equivalent expressions are found and stored in the Group. This procedure +// can be regarded as searching for a weak connected component in a directed +// graph, where nodes are expressions and directed edges are the transformation +// rules. +// +// ------------------------------------------------------------------------------ +// Phase 3: Implementation +// ------------------------------------------------------------------------------ +// +// The target of this phase is to search the best physical plan for a Group +// which satisfies a certain required physical property. +// +// In this phase, we need to enumerate all the applicable implementation rules +// for each expression in each group under the required physical property. A +// memo structure is used for a group to reduce the repeated search on the same +// required physical property. +func (opt *Optimizer) FindBestPlan(sctx sessionctx.Context, logical plannercore.LogicalPlan) (p plannercore.PhysicalPlan, cost float64, err error) { + logical, err = opt.onPhasePreprocessing(sctx, logical) + if err != nil { + return nil, 0, err + } + rootGroup := memo.Convert2Group(logical) + err = opt.onPhaseExploration(sctx, rootGroup) + if err != nil { + return nil, 0, err + } + p, cost, err = opt.onPhaseImplementation(sctx, rootGroup) + if err != nil { + return nil, 0, err + } + err = p.ResolveIndices() + return p, cost, err +} + +func (*Optimizer) onPhasePreprocessing(_ sessionctx.Context, plan plannercore.LogicalPlan) (plannercore.LogicalPlan, error) { + err := plan.PruneColumns(plan.Schema().Columns, nil) + if err != nil { + return nil, err + } + return plan, nil +} + +func (opt *Optimizer) onPhaseExploration(_ sessionctx.Context, g *memo.Group) error { + for round, ruleBatch := range opt.transformationRuleBatches { + for !g.Explored(round) { + err := opt.exploreGroup(g, round, ruleBatch) + if err != nil { + return err + } + } + } + return nil +} + +func (opt *Optimizer) exploreGroup(g *memo.Group, round int, ruleBatch TransformationRuleBatch) error { + if g.Explored(round) { + return nil + } + g.SetExplored(round) + + for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { + curExpr := elem.Value.(*memo.GroupExpr) + if curExpr.Explored(round) { + continue + } + curExpr.SetExplored(round) + + // Explore child groups firstly. + for _, childGroup := range curExpr.Children { + for !childGroup.Explored(round) { + if err := opt.exploreGroup(childGroup, round, ruleBatch); err != nil { + return err + } + } + } + + eraseCur, err := opt.findMoreEquiv(g, elem, round, ruleBatch) + if err != nil { + return err + } + if eraseCur { + g.Delete(curExpr) + } + } + return nil +} + +// findMoreEquiv finds and applies the matched transformation rules. +func (*Optimizer) findMoreEquiv(g *memo.Group, elem *list.Element, round int, ruleBatch TransformationRuleBatch) (eraseCur bool, err error) { + expr := elem.Value.(*memo.GroupExpr) + operand := memo.GetOperand(expr.ExprNode) + for _, rule := range ruleBatch[operand] { + pattern := rule.GetPattern() + if !pattern.Operand.Match(operand) { + continue + } + // Create a binding of the current Group expression and the pattern of + // the transformation rule to enumerate all the possible expressions. + iter := memo.NewExprIterFromGroupElem(elem, pattern) + for ; iter != nil && iter.Matched(); iter.Next() { + if !rule.Match(iter) { + continue + } + + newExprs, eraseOld, eraseAll, err := rule.OnTransform(iter) + if err != nil { + return false, err + } + + if eraseAll { + g.DeleteAll() + for _, e := range newExprs { + g.Insert(e) + } + // If we delete all of the other GroupExprs, we can break the search. + g.SetExplored(round) + return false, nil + } + + eraseCur = eraseCur || eraseOld + for _, e := range newExprs { + if !g.Insert(e) { + continue + } + // If the new Group expression is successfully inserted into the + // current Group, mark the Group as unexplored to enable the exploration + // on the new Group expressions. + g.SetUnexplored(round) + } + } + } + return eraseCur, nil +} + +// fillGroupStats computes Stats property for each Group recursively. +func (opt *Optimizer) fillGroupStats(g *memo.Group) (err error) { + if g.Prop.Stats != nil { + return nil + } + // All GroupExpr in a Group should share same LogicalProperty, so just use + // first one to compute Stats property. + elem := g.Equivalents.Front() + expr := elem.Value.(*memo.GroupExpr) + childStats := make([]*property.StatsInfo, len(expr.Children)) + childSchema := make([]*expression.Schema, len(expr.Children)) + for i, childGroup := range expr.Children { + err = opt.fillGroupStats(childGroup) + if err != nil { + return err + } + childStats[i] = childGroup.Prop.Stats + childSchema[i] = childGroup.Prop.Schema + } + planNode := expr.ExprNode + g.Prop.Stats, err = planNode.DeriveStats(childStats, g.Prop.Schema, childSchema, nil) + return err +} + +// onPhaseImplementation starts implementation physical operators from given root Group. +func (opt *Optimizer) onPhaseImplementation(_ sessionctx.Context, g *memo.Group) (plannercore.PhysicalPlan, float64, error) { + prop := &property.PhysicalProperty{ + ExpectedCnt: math.MaxFloat64, + } + preparePossibleProperties(g, make(map[*memo.Group][][]*expression.Column)) + // TODO replace MaxFloat64 costLimit by variable from sctx, or other sources. + impl, err := opt.implGroup(g, prop, math.MaxFloat64) + if err != nil { + return nil, 0, err + } + if impl == nil { + return nil, 0, plannercore.ErrInternal.GenWithStackByArgs("Can't find a proper physical plan for this query") + } + return impl.GetPlan(), impl.GetCost(), nil +} + +// implGroup finds the best Implementation which satisfies the required +// physical property for a Group. The best Implementation should have the +// lowest cost among all the applicable Implementations. +// +// g: the Group to be implemented. +// reqPhysProp: the required physical property. +// costLimit: the maximum cost of all the Implementations. +func (opt *Optimizer) implGroup(g *memo.Group, reqPhysProp *property.PhysicalProperty, costLimit float64) (memo.Implementation, error) { + groupImpl := g.GetImpl(reqPhysProp) + if groupImpl != nil { + if groupImpl.GetCost() <= costLimit { + return groupImpl, nil + } + return nil, nil + } + // Handle implementation rules for each equivalent GroupExpr. + var childImpls []memo.Implementation + err := opt.fillGroupStats(g) + if err != nil { + return nil, err + } + outCount := math.Min(g.Prop.Stats.RowCount, reqPhysProp.ExpectedCnt) + for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { + curExpr := elem.Value.(*memo.GroupExpr) + impls, err := opt.implGroupExpr(curExpr, reqPhysProp) + if err != nil { + return nil, err + } + for _, impl := range impls { + childImpls = childImpls[:0] + for i, childGroup := range curExpr.Children { + childImpl, err := opt.implGroup(childGroup, impl.GetPlan().GetChildReqProps(i), impl.GetCostLimit(costLimit, childImpls...)) + if err != nil { + return nil, err + } + if childImpl == nil { + impl.SetCost(math.MaxFloat64) + break + } + childImpls = append(childImpls, childImpl) + } + if impl.GetCost() == math.MaxFloat64 { + continue + } + implCost := impl.CalcCost(outCount, childImpls...) + if implCost > costLimit { + continue + } + if groupImpl == nil || groupImpl.GetCost() > implCost { + groupImpl = impl.AttachChildren(childImpls...) + costLimit = implCost + } + } + } + // Handle enforcer rules for required physical property. + for _, rule := range GetEnforcerRules(g, reqPhysProp) { + newReqPhysProp := rule.NewProperty(reqPhysProp) + enforceCost := rule.GetEnforceCost(g) + childImpl, err := opt.implGroup(g, newReqPhysProp, costLimit-enforceCost) + if err != nil { + return nil, err + } + if childImpl == nil { + continue + } + impl := rule.OnEnforce(reqPhysProp, childImpl) + implCost := enforceCost + childImpl.GetCost() + impl.SetCost(implCost) + if groupImpl == nil || groupImpl.GetCost() > implCost { + groupImpl = impl + costLimit = implCost + } + } + if groupImpl == nil || groupImpl.GetCost() == math.MaxFloat64 { + return nil, nil + } + g.InsertImpl(reqPhysProp, groupImpl) + return groupImpl, nil +} + +func (opt *Optimizer) implGroupExpr(cur *memo.GroupExpr, reqPhysProp *property.PhysicalProperty) (impls []memo.Implementation, err error) { + for _, rule := range opt.GetImplementationRules(cur.ExprNode) { + if !rule.Match(cur, reqPhysProp) { + continue + } + curImpls, err := rule.OnImplement(cur, reqPhysProp) + if err != nil { + return nil, err + } + impls = append(impls, curImpls...) + } + return impls, nil +} + +// preparePossibleProperties recursively calls LogicalPlan PreparePossibleProperties +// interface. It will fulfill the the possible properties fields of LogicalAggregation +// and LogicalJoin. +func preparePossibleProperties(g *memo.Group, propertyMap map[*memo.Group][][]*expression.Column) [][]*expression.Column { + if prop, ok := propertyMap[g]; ok { + return prop + } + groupPropertyMap := make(map[string][]*expression.Column) + for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { + expr := elem.Value.(*memo.GroupExpr) + childrenProperties := make([][][]*expression.Column, len(expr.Children)) + for i, child := range expr.Children { + childrenProperties[i] = preparePossibleProperties(child, propertyMap) + } + exprProperties := expr.ExprNode.PreparePossibleProperties(expr.Schema(), childrenProperties...) + for _, newPropCols := range exprProperties { + // Check if the prop has already been in `groupPropertyMap`. + newProp := property.PhysicalProperty{SortItems: property.SortItemsFromCols(newPropCols, true)} + key := newProp.HashCode() + if _, ok := groupPropertyMap[string(key)]; !ok { + groupPropertyMap[string(key)] = newPropCols + } + } + } + resultProps := make([][]*expression.Column, 0, len(groupPropertyMap)) + for _, prop := range groupPropertyMap { + resultProps = append(resultProps, prop) + } + propertyMap[g] = resultProps + return resultProps +} diff --git a/pkg/planner/cascades/optimize_test.go b/pkg/planner/cascades/optimize_test.go new file mode 100644 index 0000000000000..940cc2542727e --- /dev/null +++ b/pkg/planner/cascades/optimize_test.go @@ -0,0 +1,237 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package cascades + +import ( + "context" + "math" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/stretchr/testify/require" +) + +func TestImplGroupZeroCost(t *testing.T) { + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + + stmt, err := p.ParseOneStmt("select t1.a, t2.a from t as t1 left join t as t2 on t1.a = t2.a where t1.a < 1.0", "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + rootGroup := memo.Convert2Group(logic) + prop := &property.PhysicalProperty{ + ExpectedCnt: math.MaxFloat64, + } + impl, err := NewOptimizer().implGroup(rootGroup, prop, 0.0) + require.NoError(t, err) + require.Nil(t, impl) +} + +func TestInitGroupSchema(t *testing.T) { + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + + stmt, err := p.ParseOneStmt("select a from t", "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + g := memo.Convert2Group(logic) + require.NotNil(t, g) + require.NotNil(t, g.Prop) + require.Equal(t, 1, g.Prop.Schema.Len()) + require.Nil(t, g.Prop.Stats) +} + +func TestFillGroupStats(t *testing.T) { + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + + stmt, err := p.ParseOneStmt("select * from t t1 join t t2 on t1.a = t2.a", "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + rootGroup := memo.Convert2Group(logic) + err = NewOptimizer().fillGroupStats(rootGroup) + require.NoError(t, err) + require.NotNil(t, rootGroup.Prop.Stats) +} + +func TestPreparePossibleProperties(t *testing.T) { + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + optimizer := NewOptimizer() + + optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ + memo.OperandDataSource: { + NewRuleEnumeratePaths(), + }, + }) + defer func() { + optimizer.ResetTransformationRules(DefaultRuleBatches...) + }() + + stmt, err := p.ParseOneStmt("select f, sum(a) from t group by f", "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + logic, err = optimizer.onPhasePreprocessing(ctx, logic) + require.NoError(t, err) + + // collect the target columns: f, a + ds, ok := logic.Children()[0].Children()[0].(*plannercore.DataSource) + require.True(t, ok) + + var columnF, columnA *expression.Column + for i, col := range ds.Columns { + if col.Name.L == "f" { + columnF = ds.Schema().Columns[i] + } else if col.Name.L == "a" { + columnA = ds.Schema().Columns[i] + } + } + require.NotNil(t, columnF) + require.NotNil(t, columnA) + + agg, ok := logic.Children()[0].(*plannercore.LogicalAggregation) + require.True(t, ok) + + group := memo.Convert2Group(agg) + require.NoError(t, optimizer.onPhaseExploration(ctx, group)) + + // The memo looks like this: + // Group#0 Schema:[Column#13,test.t.f] + // Aggregation_2 input:[Group#1], group by:test.t.f, funcs:sum(test.t.a), firstrow(test.t.f) + // Group#1 Schema:[test.t.a,test.t.f] + // TiKVSingleGather_5 input:[Group#2], table:t + // TiKVSingleGather_9 input:[Group#3], table:t, index:f_g + // TiKVSingleGather_7 input:[Group#4], table:t, index:f + // Group#2 Schema:[test.t.a,test.t.f] + // TableScan_4 table:t, pk col:test.t.a + // Group#3 Schema:[test.t.a,test.t.f] + // IndexScan_8 table:t, index:f, g + // Group#4 Schema:[test.t.a,test.t.f] + // IndexScan_6 table:t, index:f + propMap := make(map[*memo.Group][][]*expression.Column) + aggProp := preparePossibleProperties(group, propMap) + // We only have one prop for Group0 : f + require.Len(t, aggProp, 1) + require.True(t, aggProp[0][0].Equal(nil, columnF)) + + gatherGroup := group.Equivalents.Front().Value.(*memo.GroupExpr).Children[0] + gatherProp, ok := propMap[gatherGroup] + require.True(t, ok) + // We have 2 props for Group1: [f], [a] + require.Len(t, gatherProp, 2) + for _, prop := range gatherProp { + require.Len(t, prop, 1) + require.True(t, prop[0].Equal(nil, columnA) || prop[0].Equal(nil, columnF)) + } +} + +// fakeTransformation is used for TestAppliedRuleSet. +type fakeTransformation struct { + baseRule + appliedTimes int +} + +// OnTransform implements Transformation interface. +func (rule *fakeTransformation) OnTransform(old *memo.ExprIter) (newExprs []*memo.GroupExpr, eraseOld bool, eraseAll bool, err error) { + rule.appliedTimes++ + old.GetExpr().AddAppliedRule(rule) + return []*memo.GroupExpr{old.GetExpr()}, true, false, nil +} + +func TestAppliedRuleSet(t *testing.T) { + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + optimizer := NewOptimizer() + + rule := fakeTransformation{} + rule.pattern = memo.NewPattern(memo.OperandProjection, memo.EngineAll) + optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ + memo.OperandProjection: { + &rule, + }, + }) + defer func() { + optimizer.ResetTransformationRules(DefaultRuleBatches...) + }() + + stmt, err := p.ParseOneStmt("select 1", "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + group := memo.Convert2Group(logic) + require.NoError(t, optimizer.onPhaseExploration(ctx, group)) + require.Equal(t, 1, rule.appliedTimes) +} diff --git a/pkg/planner/cascades/stringer.go b/pkg/planner/cascades/stringer.go new file mode 100644 index 0000000000000..de1f12897a7b7 --- /dev/null +++ b/pkg/planner/cascades/stringer.go @@ -0,0 +1,119 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package cascades + +import ( + "bytes" + "fmt" + "strings" + + "github.com/pingcap/tidb/pkg/planner/memo" +) + +// ToString stringifies a Group Tree. +func ToString(g *memo.Group) []string { + idMap := make(map[*memo.Group]int) + idMap[g] = 0 + return toString(g, idMap, map[*memo.Group]struct{}{}, []string{}) +} + +// toString recursively stringifies a Group Tree using a preorder traversal method. +func toString(g *memo.Group, idMap map[*memo.Group]int, visited map[*memo.Group]struct{}, strs []string) []string { + if _, exists := visited[g]; exists { + return strs + } + visited[g] = struct{}{} + // Add new Groups to idMap. + for item := g.Equivalents.Front(); item != nil; item = item.Next() { + expr := item.Value.(*memo.GroupExpr) + for _, childGroup := range expr.Children { + if _, exists := idMap[childGroup]; !exists { + idMap[childGroup] = len(idMap) + } + } + } + // Visit self first. + strs = append(strs, groupToString(g, idMap)...) + // Visit children then. + for item := g.Equivalents.Front(); item != nil; item = item.Next() { + expr := item.Value.(*memo.GroupExpr) + for _, childGroup := range expr.Children { + strs = toString(childGroup, idMap, visited, strs) + } + } + return strs +} + +// groupToString only stringifies a single Group. +// Format: +// Group#1 Column: [Column#1,Column#2,Column#13] Unique key: [] +// +// Selection_4 input:[Group#2], eq(Column#13, Column#2), gt(Column#1, 10) +// Projection_15 input:Group#3 Column#1, Column#2 +func groupToString(g *memo.Group, idMap map[*memo.Group]int) []string { + schema := g.Prop.Schema + colStrs := make([]string, 0, len(schema.Columns)) + for _, col := range schema.Columns { + colStrs = append(colStrs, col.String()) + } + + groupLine := bytes.NewBufferString("") + fmt.Fprintf(groupLine, "Group#%d Schema:[%s]", idMap[g], strings.Join(colStrs, ",")) + + if len(g.Prop.Schema.Keys) > 0 { + ukStrs := make([]string, 0, len(schema.Keys)) + for _, key := range schema.Keys { + ukColStrs := make([]string, 0, len(key)) + for _, col := range key { + ukColStrs = append(ukColStrs, col.String()) + } + ukStrs = append(ukStrs, strings.Join(ukColStrs, ",")) + } + fmt.Fprintf(groupLine, ", UniqueKey:[%s]", strings.Join(ukStrs, ",")) + } + + result := make([]string, 0, g.Equivalents.Len()+1) + result = append(result, groupLine.String()) + for item := g.Equivalents.Front(); item != nil; item = item.Next() { + expr := item.Value.(*memo.GroupExpr) + result = append(result, " "+groupExprToString(expr, idMap)) + } + return result +} + +// groupExprToString stringifies a groupExpr(or a LogicalPlan). +// Format: +// Selection_13 input:Group#2 gt(Column#1, Column#4) +func groupExprToString(expr *memo.GroupExpr, idMap map[*memo.Group]int) string { + buffer := bytes.NewBufferString(expr.ExprNode.ExplainID().String()) + if len(expr.Children) == 0 { + fmt.Fprintf(buffer, " %s", expr.ExprNode.ExplainInfo()) + } else { + fmt.Fprintf(buffer, " %s", getChildrenGroupID(expr, idMap)) + explainInfo := expr.ExprNode.ExplainInfo() + if len(explainInfo) != 0 { + fmt.Fprintf(buffer, ", %s", explainInfo) + } + } + return buffer.String() +} + +func getChildrenGroupID(expr *memo.GroupExpr, idMap map[*memo.Group]int) string { + children := make([]string, 0, len(expr.Children)) + for _, child := range expr.Children { + children = append(children, fmt.Sprintf("Group#%d", idMap[child])) + } + return "input:[" + strings.Join(children, ",") + "]" +} diff --git a/pkg/planner/cascades/stringer_test.go b/pkg/planner/cascades/stringer_test.go new file mode 100644 index 0000000000000..1356984d54e92 --- /dev/null +++ b/pkg/planner/cascades/stringer_test.go @@ -0,0 +1,83 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package cascades + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func TestGroupStringer(t *testing.T) { + optimizer := NewOptimizer() + optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ + memo.OperandSelection: { + NewRulePushSelDownTiKVSingleGather(), + NewRulePushSelDownTableScan(), + }, + memo.OperandDataSource: { + NewRuleEnumeratePaths(), + }, + }) + defer func() { + optimizer.ResetTransformationRules(DefaultRuleBatches...) + }() + + var input []string + var output []struct { + SQL string + Result []string + } + stringerSuiteData.LoadTestCases(t, &input, &output) + + p := parser.New() + ctx := plannercore.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) + domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) + for i, sql := range input { + stmt, err := p.ParseOneStmt(sql, "", "") + require.NoError(t, err) + + plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) + require.NoError(t, err) + + logic, ok := plan.(plannercore.LogicalPlan) + require.True(t, ok) + + logic, err = optimizer.onPhasePreprocessing(ctx, logic) + require.NoError(t, err) + + group := memo.Convert2Group(logic) + require.NoError(t, optimizer.onPhaseExploration(ctx, group)) + + group.BuildKeyInfo() + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Result = ToString(group) + }) + require.Equalf(t, output[i].Result, ToString(group), "case:%v, sql:%s", i, sql) + } +} diff --git a/planner/cascades/testdata/integration_suite_in.json b/pkg/planner/cascades/testdata/integration_suite_in.json similarity index 100% rename from planner/cascades/testdata/integration_suite_in.json rename to pkg/planner/cascades/testdata/integration_suite_in.json diff --git a/planner/cascades/testdata/integration_suite_out.json b/pkg/planner/cascades/testdata/integration_suite_out.json similarity index 100% rename from planner/cascades/testdata/integration_suite_out.json rename to pkg/planner/cascades/testdata/integration_suite_out.json diff --git a/planner/cascades/testdata/stringer_suite_in.json b/pkg/planner/cascades/testdata/stringer_suite_in.json similarity index 100% rename from planner/cascades/testdata/stringer_suite_in.json rename to pkg/planner/cascades/testdata/stringer_suite_in.json diff --git a/planner/cascades/testdata/stringer_suite_out.json b/pkg/planner/cascades/testdata/stringer_suite_out.json similarity index 100% rename from planner/cascades/testdata/stringer_suite_out.json rename to pkg/planner/cascades/testdata/stringer_suite_out.json diff --git a/planner/cascades/testdata/transformation_rules_suite_in.json b/pkg/planner/cascades/testdata/transformation_rules_suite_in.json similarity index 100% rename from planner/cascades/testdata/transformation_rules_suite_in.json rename to pkg/planner/cascades/testdata/transformation_rules_suite_in.json diff --git a/planner/cascades/testdata/transformation_rules_suite_out.json b/pkg/planner/cascades/testdata/transformation_rules_suite_out.json similarity index 100% rename from planner/cascades/testdata/transformation_rules_suite_out.json rename to pkg/planner/cascades/testdata/transformation_rules_suite_out.json diff --git a/planner/cascades/transformation_rules.go b/pkg/planner/cascades/transformation_rules.go similarity index 99% rename from planner/cascades/transformation_rules.go rename to pkg/planner/cascades/transformation_rules.go index 3dd85fbaa5120..80c2b7c0a5c18 100644 --- a/planner/cascades/transformation_rules.go +++ b/pkg/planner/cascades/transformation_rules.go @@ -17,18 +17,18 @@ package cascades import ( "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/set" ) // Transformation defines the interface for the transformation rules. diff --git a/planner/cascades/transformation_rules_test.go b/pkg/planner/cascades/transformation_rules_test.go similarity index 97% rename from planner/cascades/transformation_rules_test.go rename to pkg/planner/cascades/transformation_rules_test.go index fe47adbbee379..a6669629ab25d 100644 --- a/planner/cascades/transformation_rules_test.go +++ b/pkg/planner/cascades/transformation_rules_test.go @@ -18,13 +18,13 @@ import ( "context" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/BUILD.bazel b/pkg/planner/core/BUILD.bazel new file mode 100644 index 0000000000000..a37bc98e2cc3e --- /dev/null +++ b/pkg/planner/core/BUILD.bazel @@ -0,0 +1,289 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "core", + srcs = [ + "access_object.go", + "collect_column_stats_usage.go", + "common_plans.go", + "debugtrace.go", + "encode.go", + "errors.go", + "exhaust_physical_plans.go", + "explain.go", + "expression_rewriter.go", + "find_best_task.go", + "flat_plan.go", + "foreign_key.go", + "fragment.go", + "handle_cols.go", + "hashcode.go", + "hints.go", + "indexmerge_path.go", + "initialize.go", + "logical_plan_builder.go", + "logical_plans.go", + "memtable_predicate_extractor.go", + "mock.go", + "optimizer.go", + "partition_prune.go", + "pb_to_plan.go", + "physical_plans.go", + "plan.go", + "plan_cache.go", + "plan_cache_lru.go", + "plan_cache_param.go", + "plan_cache_utils.go", + "plan_cacheable_checker.go", + "plan_cost_detail.go", + "plan_cost_ver1.go", + "plan_cost_ver2.go", + "plan_stats.go", + "plan_to_pb.go", + "planbuilder.go", + "point_get_plan.go", + "preprocess.go", + "property_cols_prune.go", + "resolve_indices.go", + "rule_aggregation_elimination.go", + "rule_aggregation_push_down.go", + "rule_aggregation_skew_rewrite.go", + "rule_build_key_info.go", + "rule_column_pruning.go", + "rule_constant_propagation.go", + "rule_decorrelate.go", + "rule_derive_topn_from_window.go", + "rule_eliminate_projection.go", + "rule_generate_column_substitute.go", + "rule_inject_extra_projection.go", + "rule_join_elimination.go", + "rule_join_reorder.go", + "rule_join_reorder_dp.go", + "rule_join_reorder_greedy.go", + "rule_max_min_eliminate.go", + "rule_partition_processor.go", + "rule_predicate_push_down.go", + "rule_predicate_simplification.go", + "rule_push_down_sequence.go", + "rule_resolve_grouping_expand.go", + "rule_result_reorder.go", + "rule_semi_join_rewrite.go", + "rule_topn_push_down.go", + "runtime_filter.go", + "runtime_filter_generator.go", + "scalar_subq_expression.go", + "show_predicate_extractor.go", + "stats.go", + "stringer.go", + "task.go", + "telemetry.go", + "tiflash_selection_late_materialization.go", + "trace.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/core", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/storage", + "//pkg/bindinfo", + "//pkg/config", + "//pkg/distsql", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/lock", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/planner/cardinality", + "//pkg/planner/core/internal", + "//pkg/planner/core/internal/base", + "//pkg/planner/core/metrics", + "//pkg/planner/funcdep", + "//pkg/planner/property", + "//pkg/planner/util", + "//pkg/planner/util/debugtrace", + "//pkg/planner/util/fixcontrol", + "//pkg/privilege", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/staleread", + "//pkg/statistics", + "//pkg/table", + "//pkg/table/tables", + "//pkg/table/temptable", + "//pkg/tablecodec", + "//pkg/telemetry", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/disjointset", + "//pkg/util/domainutil", + "//pkg/util/execdetails", + "//pkg/util/filter", + "//pkg/util/hack", + "//pkg/util/hint", + "//pkg/util/intest", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/paging", + "//pkg/util/parser", + "//pkg/util/plancache", + "//pkg/util/plancodec", + "//pkg/util/ranger", + "//pkg/util/rowcodec", + "//pkg/util/sem", + "//pkg/util/set", + "//pkg/util/size", + "//pkg/util/sqlexec", + "//pkg/util/stmtsummary", + "//pkg/util/stringutil", + "//pkg/util/syncutil", + "//pkg/util/texttree", + "//pkg/util/tiflashcompute", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "core_test", + timeout = "short", + srcs = [ + "binary_plan_test.go", + "cbo_test.go", + "collect_column_stats_usage_test.go", + "common_plans_test.go", + "enforce_mpp_test.go", + "errors_test.go", + "exhaust_physical_plans_test.go", + "expression_rewriter_test.go", + "expression_test.go", + "find_best_task_test.go", + "fragment_test.go", + "indexmerge_intersection_test.go", + "indexmerge_path_test.go", + "indexmerge_test.go", + "integration_partition_test.go", + "integration_test.go", + "logical_plan_trace_test.go", + "logical_plans_test.go", + "main_test.go", + "memtable_predicate_extractor_test.go", + "optimizer_test.go", + "partition_pruning_test.go", + "physical_plan_test.go", + "physical_plan_trace_test.go", + "plan_cache_lru_test.go", + "plan_cache_param_test.go", + "plan_cache_test.go", + "plan_cacheable_checker_test.go", + "plan_cost_detail_test.go", + "plan_cost_ver1_test.go", + "plan_cost_ver2_test.go", + "plan_replayer_capture_test.go", + "plan_test.go", + "plan_to_pb_test.go", + "planbuilder_test.go", + "point_get_plan_test.go", + "preprocess_test.go", + "rule_generate_column_substitute_test.go", + "rule_join_reorder_dp_test.go", + "rule_join_reorder_test.go", + "runtime_filter_generator_test.go", + "stringer_test.go", + ], + data = glob(["testdata/**"]), + embed = [":core"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner", + "//pkg/planner/core/internal", + "//pkg/planner/property", + "//pkg/planner/util", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/statistics", + "//pkg/table", + "//pkg/testkit", + "//pkg/testkit/ddlhelper", + "//pkg/testkit/external", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/benchdaily", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/hint", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/plancache", + "//pkg/util/plancodec", + "//pkg/util/ranger", + "//pkg/util/set", + "//pkg/util/stmtsummary", + "//pkg/util/tracing", + "@com_github_golang_snappy//:snappy", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/access_object.go b/pkg/planner/core/access_object.go similarity index 98% rename from planner/core/access_object.go rename to pkg/planner/core/access_object.go index 5c36226d7bcb2..ea59145cbc956 100644 --- a/planner/core/access_object.go +++ b/pkg/planner/core/access_object.go @@ -19,10 +19,10 @@ import ( "strconv" "strings" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/planner/core/binary_plan_test.go b/pkg/planner/core/binary_plan_test.go new file mode 100644 index 0000000000000..55959a7d0f647 --- /dev/null +++ b/pkg/planner/core/binary_plan_test.go @@ -0,0 +1,428 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "encoding/base64" + "fmt" + "io" + "os" + "strings" + "testing" + + "github.com/golang/snappy" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + "github.com/pingcap/tipb/go-tipb" + "github.com/stretchr/testify/require" +) + +func TestBinaryPlanSwitch(t *testing.T) { + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + + origin := tk.MustQuery("SELECT @@global.tidb_generate_binary_plan") + originStr := origin.Rows()[0][0].(string) + defer func() { + tk.MustExec("set @@global.tidb_generate_binary_plan = '" + originStr + "'") + }() + + tk.MustExec("use test") + // 1. assert binary plan is generated if the variable is turned on + tk.MustExec("set global tidb_generate_binary_plan = 1") + tk.MustQuery("select sleep(1)") + + result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query like "%select sleep(1)%" and query not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s := result[0] + b, err := base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary := &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + + `where QUERY_SAMPLE_TEXT like "%select sleep(1)%" and QUERY_SAMPLE_TEXT not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s = result[0] + b, err = base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary = &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + + // 2. assert binary plan is not generated if the variable is turned off + tk.MustExec("set global tidb_generate_binary_plan = 0") + tk.MustQuery("select 1 > sleep(1)") + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query like "%select 1 > sleep(1)%" and query not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s = result[0] + require.Empty(t, s) + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + + `where QUERY_SAMPLE_TEXT like "%select 1 > sleep(1)%" and QUERY_SAMPLE_TEXT not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s = result[0] + require.Empty(t, s) +} + +// TestTooLongBinaryPlan asserts that if the binary plan is larger than 1024*1024 bytes, it should be output to slow query but not to stmt summary. +func TestTooLongBinaryPlan(t *testing.T) { + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + + origin := tk.MustQuery("SELECT @@global.tidb_enable_stmt_summary") + originStr := origin.Rows()[0][0].(string) + defer func() { + tk.MustExec("set @@global.tidb_enable_stmt_summary = '" + originStr + "'") + }() + + // Trigger clear the stmt summary in memory to prevent this case from being affected by other cases. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + tk.MustExec("use test") + tk.MustExec("drop table if exists th") + tk.MustExec("set @@session.tidb_enable_table_partition = 1") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") + tk.MustQuery("select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i") + + result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query like "%th t1 join th t2 join th t3%" and query not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s := result[0] + require.Greater(t, len(s), stmtsummary.MaxEncodedPlanSizeInBytes) + b, err := base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary := &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + require.False(t, binary.DiscardedDueToTooLong) + require.True(t, binary.WithRuntimeStats) + require.NotNil(t, binary.Main) + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + + `where QUERY_SAMPLE_TEXT like "%th t1 join th t2 join th t3%" and QUERY_SAMPLE_TEXT not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s = result[0] + b, err = base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary = &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + require.True(t, binary.DiscardedDueToTooLong) + require.Nil(t, binary.Main) + require.Nil(t, binary.Ctes) +} + +// TestLongBinaryPlan asserts that if the binary plan is smaller than 1024*1024 bytes, it should be output to both slow query and stmt summary. +func TestLongBinaryPlan(t *testing.T) { + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + + tk.MustExec("use test") + + tk.MustExec("drop table if exists th") + tk.MustExec("set @@session.tidb_enable_table_partition = 1") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 1000;") + tk.MustQuery("select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i") + + result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query like "%th t1 join th t2 join th t3%" and query not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s1 := result[0] + // The binary plan in this test case is expected to be smaller than MaxEncodedPlanSizeInBytes. + // If the size of the binary plan changed and this case failed in the future, you can adjust the partition numbers in the CREATE TABLE statement above. + require.Less(t, len(s1), stmtsummary.MaxEncodedPlanSizeInBytes) + b, err := base64.StdEncoding.DecodeString(s1) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary := &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + require.False(t, binary.DiscardedDueToTooLong) + require.True(t, binary.WithRuntimeStats) + require.NotNil(t, binary.Main) + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + + `where QUERY_SAMPLE_TEXT like "%th t1 join th t2 join th t3%" and QUERY_SAMPLE_TEXT not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s2 := result[0] + require.Equal(t, s1, s2) +} + +func TestBinaryPlanOfPreparedStmt(t *testing.T) { + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int);") + tk.MustExec("insert into t value(30,30);") + tk.MustExec(`prepare stmt from "select sleep(1), b from t where a > ?"`) + tk.MustExec("set @a = 20") + tk.MustQuery("execute stmt using @a") + + result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query like "%select sleep%" and query not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s1 := result[0] + b, err := base64.StdEncoding.DecodeString(s1) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary := &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + require.False(t, binary.DiscardedDueToTooLong) + require.True(t, binary.WithRuntimeStats) + require.NotNil(t, binary.Main) + + result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + + `where QUERY_SAMPLE_TEXT like "%select sleep%" and QUERY_SAMPLE_TEXT not like "%like%" ` + + "limit 1;").Rows()) + require.Len(t, result, 1) + s2 := result[0] + require.Equal(t, s1, s2) +} + +// TestDecodeBinaryPlan asserts that the result of EXPLAIN ANALYZE FORMAT = 'verbose' is the same as tidb_decode_binary_plan(). +func TestDecodeBinaryPlan(t *testing.T) { + // Prepare the slow log + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + tk.MustExec("set tidb_slow_log_threshold=0;") + defer func() { + tk.MustExec("set tidb_slow_log_threshold=300;") + }() + tk.MustExec(`create table tp (a int, b int) partition by range(a) ( + partition p0 values less than (100), + partition p1 values less than (200), + partition p2 values less than (300), + partition p3 values less than maxvalue + )`) + tk.MustExec("insert into tp value(1,1), (10,10), (123,234), (213, 234);") + tk.MustExec("create table t(a int, b int, c int, index ia(a));") + tk.MustExec("insert into t value(1,1,1), (10,10,10), (123,234,345), (-213, -234, -234);") + cases := []string{ + "explain analyze format = 'verbose' select * from t", + "explain analyze format = 'verbose' select * from t where a > 10", + "explain analyze format = 'verbose' select /*+ inl_join(t1) */ * from t t1 join t t2 where t1.a = t2.a", + "explain analyze format = 'verbose' WITH RECURSIVE cte(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", + "set @@tidb_partition_prune_mode='static'", + "explain analyze format = 'verbose' select * from tp", + "explain analyze format = 'verbose' select * from tp t1 join tp t2 on t1.b > t2.b", + "explain analyze format = 'verbose' select * from tp where a > 400", + "explain analyze format = 'verbose' select * from tp where a < 30", + "explain analyze format = 'verbose' select * from tp where a > 0", + "set @@tidb_partition_prune_mode='dynamic'", + "explain analyze format = 'verbose' select * from tp", + "explain analyze format = 'verbose' select * from tp t1 join tp t2 on t1.b > t2.b", + "explain analyze format = 'verbose' select * from tp where a > 400", + "explain analyze format = 'verbose' select * from tp where a < 30", + "explain analyze format = 'verbose' select * from tp where a > 0", + } + + for _, c := range cases { + if len(c) < 7 || c[:7] != "explain" { + tk.MustExec(c) + continue + } + comment := fmt.Sprintf("sql:%s", c) + + var res1, res2 []string + + explainResult := tk.MustQuery(c).Rows() + for _, row := range explainResult { + for _, val := range row { + str := val.(string) + str = strings.TrimSpace(str) + if len(str) > 0 { + res1 = append(res1, str) + } + } + } + + slowLogResult := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query = "` + c + `;" ` + + "order by time desc limit 1").Rows()) + require.Lenf(t, slowLogResult, 1, comment) + decoded := testdata.ConvertRowsToStrings(tk.MustQuery(`select tidb_decode_binary_plan('` + slowLogResult[0] + `')`).Rows())[0] + decodedRows := strings.Split(decoded, "\n") + // remove the first newline and the title row + decodedRows = decodedRows[2:] + for _, decodedRow := range decodedRows { + vals := strings.Split(decodedRow, "|") + for _, val := range vals { + val = strings.TrimSpace(val) + if len(val) > 0 { + res2 = append(res2, val) + } + } + } + + require.Equalf(t, res1, res2, comment) + } +} + +func TestInvalidDecodeBinaryPlan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + str1 := "some random bytes" + str2 := base64.StdEncoding.EncodeToString([]byte(str1)) + str3 := base64.StdEncoding.EncodeToString(snappy.Encode(nil, []byte(str1))) + + tk.MustQuery(`select tidb_decode_binary_plan('` + str1 + `')`).Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 illegal base64 data at input byte 4")) + tk.MustQuery(`select tidb_decode_binary_plan('` + str2 + `')`).Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 snappy: corrupt input")) + tk.MustQuery(`select tidb_decode_binary_plan('` + str3 + `')`).Check(testkit.Rows("")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 proto: illegal wireType 7")) +} + +func TestUnnecessaryBinaryPlanInSlowLog(t *testing.T) { + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + + origin := tk.MustQuery("SELECT @@global.tidb_slow_log_threshold") + originStr := origin.Rows()[0][0].(string) + defer func() { + tk.MustExec("set @@global.tidb_slow_log_threshold = '" + originStr + "'") + }() + + tk.MustExec("use test") + tk.MustExec("drop table if exists th") + tk.MustExec("set global tidb_slow_log_threshold = 1;") + tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 100;") + slowLogBytes, err := io.ReadAll(f) + require.NoError(t, err) + require.NotContains(t, string(slowLogBytes), `tidb_decode_binary_plan('')`) +} diff --git a/pkg/planner/core/casetest/BUILD.bazel b/pkg/planner/core/casetest/BUILD.bazel new file mode 100644 index 0000000000000..1f88dc86084d3 --- /dev/null +++ b/pkg/planner/core/casetest/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "casetest_test", + timeout = "moderate", + srcs = [ + "integration_test.go", + "main_test.go", + "plan_test.go", + "stats_test.go", + "tiflash_selection_late_materialization_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 19, + deps = [ + "//pkg/domain", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/planner/property", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/hint", + "//pkg/util/plancodec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/binaryplan/BUILD.bazel b/pkg/planner/core/casetest/binaryplan/BUILD.bazel new file mode 100644 index 0000000000000..dfbd7dc0aa262 --- /dev/null +++ b/pkg/planner/core/casetest/binaryplan/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "binaryplan_test", + timeout = "short", + srcs = [ + "binary_plan_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + deps = [ + "//pkg/config", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/logutil", + "@com_github_golang_snappy//:snappy", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/binaryplan/binary_plan_test.go b/pkg/planner/core/casetest/binaryplan/binary_plan_test.go new file mode 100644 index 0000000000000..6d8ec897a8f22 --- /dev/null +++ b/pkg/planner/core/casetest/binaryplan/binary_plan_test.go @@ -0,0 +1,141 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package binaryplan + +import ( + "encoding/base64" + "fmt" + "os" + "regexp" + "testing" + + "github.com/golang/snappy" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tipb/go-tipb" + "github.com/stretchr/testify/require" +) + +func simplifyAndCheckBinaryOperator(t *testing.T, pb *tipb.ExplainOperator, withRuntimeStats bool) { + if withRuntimeStats { + if pb.TaskType == tipb.TaskType_root { + require.NotEmpty(t, pb.RootBasicExecInfo) + } else if pb.TaskType != tipb.TaskType_unknown { + require.NotEmpty(t, pb.CopExecInfo) + } + } + pb.RootBasicExecInfo = "" + pb.RootGroupExecInfo = nil + pb.CopExecInfo = "" + match, err := regexp.MatchString("((Table|Index).*Scan)|CTEFullScan|Point_Get", pb.Name) + if err == nil && match { + require.NotNil(t, pb.AccessObjects) + } + // AccessObject field is an interface and json.Unmarshall can't handle it, so we don't check it against the json output. + pb.AccessObjects = nil + // MemoryBytes and DiskBytes are not stable sometimes. + pb.MemoryBytes = 0 + pb.DiskBytes = 0 + if len(pb.Children) > 0 { + for _, op := range pb.Children { + if op != nil { + simplifyAndCheckBinaryOperator(t, op, withRuntimeStats) + } + } + } +} + +func simplifyAndCheckBinaryPlan(t *testing.T, pb *tipb.ExplainData) { + if pb.Main != nil { + simplifyAndCheckBinaryOperator(t, pb.Main, pb.WithRuntimeStats) + } + for _, cte := range pb.Ctes { + if cte != nil { + simplifyAndCheckBinaryOperator(t, cte, pb.WithRuntimeStats) + } + } +} +func TestBinaryPlanInExplainAndSlowLog(t *testing.T) { + // Prepare the slow log + originCfg := config.GetGlobalConfig() + newCfg := *originCfg + f, err := os.CreateTemp("", "tidb-slow-*.log") + require.NoError(t, err) + newCfg.Log.SlowQueryFile = f.Name() + config.StoreGlobalConfig(&newCfg) + defer func() { + config.StoreGlobalConfig(originCfg) + require.NoError(t, f.Close()) + require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) + }() + require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + // If we don't set this, it will be false sometimes and the cost in the result will be different. + tk.MustExec("set @@tidb_enable_chunk_rpc=true") + tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) + tk.MustExec("set tidb_slow_log_threshold=0;") + defer func() { + tk.MustExec("set tidb_slow_log_threshold=300;") + }() + + var input []string + var output []struct { + SQL string + BinaryPlan *tipb.ExplainData + } + planSuiteData := GetBinaryPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + if len(test) < 7 || test[:7] != "explain" { + tk.MustExec(test) + testdata.OnRecord(func() { + output[i].SQL = test + output[i].BinaryPlan = nil + }) + continue + } + result := testdata.ConvertRowsToStrings(tk.MustQuery(test).Rows()) + require.Equal(t, len(result), 1, comment) + s := result[0] + + // assert that the binary plan in the slow log is the same as the result in the EXPLAIN statement + slowLogResult := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + + `where query = "` + test + `;" ` + + "order by time desc limit 1").Rows()) + require.Lenf(t, slowLogResult, 1, comment) + require.Equal(t, s, slowLogResult[0], comment) + + b, err := base64.StdEncoding.DecodeString(s) + require.NoError(t, err) + b, err = snappy.Decode(nil, b) + require.NoError(t, err) + binary := &tipb.ExplainData{} + err = binary.Unmarshal(b) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = test + output[i].BinaryPlan = binary + }) + simplifyAndCheckBinaryPlan(t, binary) + require.Equal(t, output[i].BinaryPlan, binary) + } +} diff --git a/pkg/planner/core/casetest/binaryplan/main_test.go b/pkg/planner/core/casetest/binaryplan/main_test.go new file mode 100644 index 0000000000000..526f50cbe0d99 --- /dev/null +++ b/pkg/planner/core/casetest/binaryplan/main_test.go @@ -0,0 +1,58 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package binaryplan + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "binary_plan_suite") + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetBinaryPlanSuiteData() testdata.TestData { + return testDataMap["binary_plan_suite"] +} diff --git a/planner/core/casetest/binaryplan/testdata/binary_plan_suite_in.json b/pkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_in.json similarity index 100% rename from planner/core/casetest/binaryplan/testdata/binary_plan_suite_in.json rename to pkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_in.json diff --git a/planner/core/casetest/binaryplan/testdata/binary_plan_suite_out.json b/pkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_out.json similarity index 100% rename from planner/core/casetest/binaryplan/testdata/binary_plan_suite_out.json rename to pkg/planner/core/casetest/binaryplan/testdata/binary_plan_suite_out.json diff --git a/pkg/planner/core/casetest/cbotest/BUILD.bazel b/pkg/planner/core/casetest/cbotest/BUILD.bazel new file mode 100644 index 0000000000000..65af4d8f3e5d7 --- /dev/null +++ b/pkg/planner/core/casetest/cbotest/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "cbotest_test", + timeout = "short", + srcs = [ + "cbo_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + race = "on", + shard_count = 17, + deps = [ + "//pkg/domain", + "//pkg/executor", + "//pkg/parser/model", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/storage", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/cbotest/cbo_test.go b/pkg/planner/core/casetest/cbotest/cbo_test.go new file mode 100644 index 0000000000000..1edce60cba240 --- /dev/null +++ b/pkg/planner/core/casetest/cbotest/cbo_test.go @@ -0,0 +1,633 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cbotest + +import ( + "context" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func loadTableStats(fileName string, dom *domain.Domain) error { + statsPath := filepath.Join("testdata", fileName) + bytes, err := os.ReadFile(statsPath) + if err != nil { + return err + } + statsTbl := &storage.JSONTable{} + err = json.Unmarshal(bytes, statsTbl) + if err != nil { + return err + } + statsHandle := dom.StatsHandle() + err = statsHandle.LoadStatsFromJSON(context.Background(), dom.InfoSchema(), statsTbl, 0) + if err != nil { + return err + } + return nil +} + +// TestCBOWithoutAnalyze tests the plan with stats that only have count info. +func TestCBOWithoutAnalyze(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t1 (a int)") + testKit.MustExec("create table t2 (a int)") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + testKit.MustExec("insert into t1 values (1), (2), (3), (4), (5), (6)") + testKit.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(dom.InfoSchema())) + var input []string + var output []struct { + SQL string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, sql := range input { + plan := testKit.MustQuery(sql) + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + }) + plan.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestStraightJoin(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + h := dom.StatsHandle() + for _, tblName := range []string{"t1", "t2", "t3", "t4"} { + testKit.MustExec(fmt.Sprintf("create table %s (a int)", tblName)) + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + } + var input []string + var output [][]string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) + }) + testKit.MustQuery(tt).Check(testkit.Rows(output[i]...)) + } +} + +func TestTableDual(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + testKit := testkit.NewTestKit(t, store) + testKit.MustExec(`use test`) + h := dom.StatsHandle() + testKit.MustExec(`create table t(a int)`) + testKit.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(dom.InfoSchema())) + var input []string + var output []struct { + SQL string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, sql := range input { + plan := testKit.MustQuery(sql) + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + }) + plan.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestEstimation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + statistics.RatioOfPseudoEstimate.Store(10.0) + defer statistics.RatioOfPseudoEstimate.Store(0.7) + testKit.MustExec("use test") + testKit.MustExec("set tidb_cost_model_version=2") + testKit.MustExec("create table t (a int)") + testKit.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)") + testKit.MustExec("insert into t select * from t") + testKit.MustExec("insert into t select * from t") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec("analyze table t") + for i := 1; i <= 8; i++ { + testKit.MustExec("delete from t where a = ?", i) + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(dom.InfoSchema())) + var input []string + var output []struct { + SQL string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, sql := range input { + plan := testKit.MustQuery(sql) + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + }) + plan.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIndexRead(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("set tidb_cost_model_version=2") + testKit.MustExec("set @@session.tidb_executor_concurrency = 4;") + testKit.MustExec("set @@session.tidb_hash_join_concurrency = 5;") + testKit.MustExec("set @@session.tidb_distsql_scan_concurrency = 15;") + + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t, t1") + testKit.MustExec("create table t (a int primary key, b int, c varchar(200), d datetime DEFAULT CURRENT_TIMESTAMP, e int, ts timestamp DEFAULT CURRENT_TIMESTAMP)") + testKit.MustExec("create index b on t (b)") + testKit.MustExec("create index d on t (d)") + testKit.MustExec("create index e on t (e)") + testKit.MustExec("create index b_c on t (b,c)") + testKit.MustExec("create index ts on t (ts)") + testKit.MustExec("create table t1 (a int, b int, index idx(a), index idxx(b))") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + testKit.MustExec("set @@tidb_enable_chunk_rpc = on") + + // This stats is generated by following format: + // fill (a, b, c, e) as (i*100+j, i, i+j, i*100+j), i and j is dependent and range of this two are [0, 99]. + require.NoError(t, loadTableStats("analyzesSuiteTestIndexReadT.json", dom)) + for i := 1; i < 16; i++ { + testKit.MustExec(fmt.Sprintf("insert into t1 values(%v, %v)", i, i)) + } + testKit.MustExec("analyze table t1") + ctx := testKit.Session() + var input, output []string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + + for i, tt := range input { + stmts, err := session.Parse(ctx, tt) + require.NoError(t, err) + require.Len(t, stmts, 1) + stmt := stmts[0] + ret := &core.PreprocessorReturn{} + err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) + require.NoError(t, err) + planString := core.ToString(p) + testdata.OnRecord(func() { + output[i] = planString + }) + require.Equalf(t, output[i], planString, "case: %v", tt) + } +} + +func TestEmptyTable(t *testing.T) { + store := testkit.CreateMockStore(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set tidb_cost_model_version=2") + testKit.MustExec("drop table if exists t, t1") + testKit.MustExec("create table t (c1 int)") + testKit.MustExec("create table t1 (c1 int)") + testKit.MustExec("analyze table t, t1") + var input, output []string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + ctx := testKit.Session() + stmts, err := session.Parse(ctx, tt) + require.NoError(t, err) + require.Len(t, stmts, 1) + stmt := stmts[0] + ret := &core.PreprocessorReturn{} + err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) + require.NoError(t, err) + planString := core.ToString(p) + testdata.OnRecord(func() { + output[i] = planString + }) + require.Equalf(t, output[i], planString, "case: %v", tt) + } +} + +func TestAnalyze(t *testing.T) { + store := testkit.CreateMockStore(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t, t1, t2, t3") + testKit.MustExec("create table t (a int, b int)") + testKit.MustExec("create index a on t (a)") + testKit.MustExec("create index b on t (b)") + testKit.MustExec("insert into t (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") + testKit.MustExec("analyze table t") + + testKit.MustExec("create table t1 (a int, b int)") + testKit.MustExec("create index a on t1 (a)") + testKit.MustExec("create index b on t1 (b)") + testKit.MustExec("insert into t1 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") + + testKit.MustExec("create table t2 (a int, b int)") + testKit.MustExec("create index a on t2 (a)") + testKit.MustExec("create index b on t2 (b)") + testKit.MustExec("insert into t2 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") + testKit.MustExec("analyze table t2 index a") + + testKit.MustExec("create table t3 (a int, b int)") + testKit.MustExec("create index a on t3 (a)") + + testKit.MustExec("set @@tidb_partition_prune_mode = 'static';") + testKit.MustExec("create table t4 (a int, b int) partition by range (a) (partition p1 values less than (2), partition p2 values less than (3))") + testKit.MustExec("create index a on t4 (a)") + testKit.MustExec("create index b on t4 (b)") + testKit.MustExec("insert into t4 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") + testKit.MustExec("analyze table t4") + + testKit.MustExec("create view v as select * from t") + _, err := testKit.Exec("analyze table v") + require.EqualError(t, err, "analyze view v is not supported now") + testKit.MustExec("drop view v") + + testKit.MustExec("create sequence seq") + _, err = testKit.Exec("analyze table seq") + require.EqualError(t, err, "analyze sequence seq is not supported now") + testKit.MustExec("drop sequence seq") + + var input, output []string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + + for i, tt := range input { + ctx := testKit.Session() + stmts, err := session.Parse(ctx, tt) + require.NoError(t, err) + require.Len(t, stmts, 1) + stmt := stmts[0] + err = executor.ResetContextOfStmt(ctx, stmt) + require.NoError(t, err) + ret := &core.PreprocessorReturn{} + err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) + require.NoError(t, err) + planString := core.ToString(p) + testdata.OnRecord(func() { + output[i] = planString + }) + require.Equalf(t, output[i], planString, "case: %v", tt) + } +} + +func TestOutdatedAnalyze(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (a int, b int, index idx(a))") + for i := 0; i < 10; i++ { + testKit.MustExec(fmt.Sprintf("insert into t values (%d,%d)", i, i)) + } + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec("analyze table t") + testKit.MustExec("insert into t select * from t") + testKit.MustExec("insert into t select * from t") + testKit.MustExec("insert into t select * from t") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(dom.InfoSchema())) + var input []struct { + SQL string + EnablePseudoForOutdatedStats bool + RatioOfPseudoEstimate float64 + } + var output []struct { + SQL string + EnablePseudoForOutdatedStats bool + RatioOfPseudoEstimate float64 + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testKit.Session().GetSessionVars().SetEnablePseudoForOutdatedStats(tt.EnablePseudoForOutdatedStats) + statistics.RatioOfPseudoEstimate.Store(tt.RatioOfPseudoEstimate) + plan := testKit.MustQuery(tt.SQL) + testdata.OnRecord(func() { + output[i].SQL = tt.SQL + output[i].EnablePseudoForOutdatedStats = tt.EnablePseudoForOutdatedStats + output[i].RatioOfPseudoEstimate = tt.RatioOfPseudoEstimate + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + }) + plan.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestNullCount(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t (a int, b int, index idx(a))") + testKit.MustExec("insert into t values (null, null), (null, null)") + testKit.MustExec("analyze table t") + var input []string + var output [][]string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i := 0; i < 2; i++ { + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(input[i]).Rows()) + }) + testKit.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) + } + h := dom.StatsHandle() + h.Clear() + require.NoError(t, h.Update(dom.InfoSchema())) + for i := 2; i < 4; i++ { + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(input[i]).Rows()) + }) + testKit.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) + } +} + +func TestCorrelatedEstimation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by + tk.MustExec("create table t(a int, b int, c int, index idx(c,b,a))") + tk.MustExec("insert into t values(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5), (6,6,6), (7,7,7), (8,8,8), (9,9,9),(10,10,10)") + tk.MustExec("analyze table t") + var ( + input []string + output [][]string + ) + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + rs := tk.MustQuery(tt) + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(rs.Rows()) + }) + rs.Check(testkit.Rows(output[i]...)) + } +} + +func TestInconsistentEstimation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int, index ab(a,b), index ac(a,c))") + tk.MustExec("insert into t values (1,1,1), (1000,1000,1000)") + for i := 0; i < 10; i++ { + tk.MustExec("insert into t values (5,5,5), (10,10,10)") + } + tk.MustExec("set @@tidb_analyze_version=1") + tk.MustExec("analyze table t with 2 buckets") + // Force using the histogram to estimate. + tk.MustExec("update mysql.stats_histograms set stats_ver = 0") + dom.StatsHandle().Clear() + require.NoError(t, dom.StatsHandle().Update(dom.InfoSchema())) + var input []string + var output []struct { + SQL string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, sql := range input { + plan := tk.MustQuery(sql) + testdata.OnRecord(func() { + output[i].SQL = sql + output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) + }) + plan.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIssue9562(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + var input [][]string + var output []struct { + SQL []string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, ts := range input { + for j, tt := range ts { + if j != len(ts)-1 { + tk.MustExec(tt) + } + testdata.OnRecord(func() { + output[i].SQL = ts + if j == len(ts)-1 { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + } + }) + if j == len(ts)-1 { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + } + } +} + +func TestLimitCrossEstimation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("set @@session.tidb_executor_concurrency = 4;") + tk.MustExec("set @@session.tidb_hash_join_concurrency = 5;") + tk.MustExec("set @@session.tidb_distsql_scan_concurrency = 15;") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b int not null, c int not null default 0, index idx_bc(b, c))") + var input [][]string + var output []struct { + SQL []string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, ts := range input { + for j, tt := range ts { + if j != len(ts)-1 { + tk.MustExec(tt) + } + testdata.OnRecord(func() { + output[i].SQL = ts + if j == len(ts)-1 { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + } + }) + if j == len(ts)-1 { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + } + } +} + +func TestLowSelIndexGreedySearch(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set tidb_cost_model_version=2") + testKit.MustExec(`set tidb_opt_limit_push_down_threshold=0`) + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t (a varchar(32) default null, b varchar(10) default null, c varchar(12) default null, d varchar(32) default null, e bigint(10) default null, key idx1 (d,a), key idx2 (a,c), key idx3 (c,b), key idx4 (e))") + require.NoError(t, loadTableStats("analyzeSuiteTestLowSelIndexGreedySearchT.json", dom)) + var input []string + var output []struct { + SQL string + Plan []string + } + // The test purposes are: + // - index `idx2` runs much faster than `idx4` experimentally; + // - estimated row count of IndexLookUp should be 0; + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) + }) + testKit.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestTiFlashCostModel(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t (a int, b int, c int, primary key(a))") + tk.MustExec("insert into t values(1,1,1), (2,2,2), (3,3,3)") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + var input, output [][]string + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, ts := range input { + for j, tt := range ts { + if j != len(ts)-1 { + tk.MustExec(tt) + } + testdata.OnRecord(func() { + if j == len(ts)-1 { + output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + } + }) + if j == len(ts)-1 { + tk.MustQuery(tt).Check(testkit.Rows(output[i]...)) + } + } + } +} + +func TestIndexEqualUnknown(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t, t1") + testKit.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly + testKit.MustExec("CREATE TABLE t(a bigint(20) NOT NULL, b bigint(20) NOT NULL, c bigint(20) NOT NULL, PRIMARY KEY (a,c,b), KEY (b))") + require.NoError(t, loadTableStats("analyzeSuiteTestIndexEqualUnknownT.json", dom)) + var input []string + var output []struct { + SQL string + Plan []string + } + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) + }) + testKit.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestLimitIndexEstimation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, key idx_a(a), key idx_b(b))") + tk.MustExec("set session tidb_enable_extended_stats = on") + // Values in column a are from 1 to 1000000, values in column b are from 1000000 to 1, + // these 2 columns are strictly correlated in reverse order. + require.NoError(t, loadTableStats("analyzeSuiteTestLimitIndexEstimationT.json", dom)) + var input []string + var output []struct { + SQL string + Plan []string + } + + analyzeSuiteData := GetAnalyzeSuiteData() + analyzeSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} diff --git a/pkg/planner/core/casetest/cbotest/main_test.go b/pkg/planner/core/casetest/cbotest/main_test.go new file mode 100644 index 0000000000000..5e3871dc7da52 --- /dev/null +++ b/pkg/planner/core/casetest/cbotest/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cbotest + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "analyze_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetAnalyzeSuiteData() testdata.TestData { + return testDataMap["analyze_suite"] +} diff --git a/planner/core/casetest/cbotest/testdata/analyzeSuiteTestIndexEqualUnknownT.json b/pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestIndexEqualUnknownT.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyzeSuiteTestIndexEqualUnknownT.json rename to pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestIndexEqualUnknownT.json diff --git a/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLimitIndexEstimationT.json b/pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLimitIndexEstimationT.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyzeSuiteTestLimitIndexEstimationT.json rename to pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLimitIndexEstimationT.json diff --git a/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLowSelIndexGreedySearchT.json b/pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLowSelIndexGreedySearchT.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyzeSuiteTestLowSelIndexGreedySearchT.json rename to pkg/planner/core/casetest/cbotest/testdata/analyzeSuiteTestLowSelIndexGreedySearchT.json diff --git a/planner/core/casetest/cbotest/testdata/analyze_suite_in.json b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_in.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyze_suite_in.json rename to pkg/planner/core/casetest/cbotest/testdata/analyze_suite_in.json diff --git a/planner/core/casetest/cbotest/testdata/analyze_suite_out.json b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyze_suite_out.json rename to pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json diff --git a/planner/core/casetest/cbotest/testdata/analyzesSuiteTestIndexReadT.json b/pkg/planner/core/casetest/cbotest/testdata/analyzesSuiteTestIndexReadT.json similarity index 100% rename from planner/core/casetest/cbotest/testdata/analyzesSuiteTestIndexReadT.json rename to pkg/planner/core/casetest/cbotest/testdata/analyzesSuiteTestIndexReadT.json diff --git a/pkg/planner/core/casetest/dag/BUILD.bazel b/pkg/planner/core/casetest/dag/BUILD.bazel new file mode 100644 index 0000000000000..bcd13ebcde11d --- /dev/null +++ b/pkg/planner/core/casetest/dag/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "dag_test", + timeout = "short", + srcs = [ + "dag_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 10, + deps = [ + "//pkg/domain", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessiontxn", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/hint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/casetest/dag/dag_test.go b/pkg/planner/core/casetest/dag/dag_test.go similarity index 95% rename from planner/core/casetest/dag/dag_test.go rename to pkg/planner/core/casetest/dag/dag_test.go index b246d9301cb19..3b6ed17cd2e39 100644 --- a/planner/core/casetest/dag/dag_test.go +++ b/pkg/planner/core/casetest/dag/dag_test.go @@ -19,18 +19,18 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/dag/main_test.go b/pkg/planner/core/casetest/dag/main_test.go new file mode 100644 index 0000000000000..899d68a67b0eb --- /dev/null +++ b/pkg/planner/core/casetest/dag/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package dag + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetPlanSuiteData() testdata.TestData { + return testDataMap["plan_suite"] +} diff --git a/planner/core/casetest/dag/testdata/plan_suite_in.json b/pkg/planner/core/casetest/dag/testdata/plan_suite_in.json similarity index 100% rename from planner/core/casetest/dag/testdata/plan_suite_in.json rename to pkg/planner/core/casetest/dag/testdata/plan_suite_in.json diff --git a/planner/core/casetest/dag/testdata/plan_suite_out.json b/pkg/planner/core/casetest/dag/testdata/plan_suite_out.json similarity index 100% rename from planner/core/casetest/dag/testdata/plan_suite_out.json rename to pkg/planner/core/casetest/dag/testdata/plan_suite_out.json diff --git a/pkg/planner/core/casetest/enforcempp/BUILD.bazel b/pkg/planner/core/casetest/enforcempp/BUILD.bazel new file mode 100644 index 0000000000000..a2cd098e8ef40 --- /dev/null +++ b/pkg/planner/core/casetest/enforcempp/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "enforcempp_test", + timeout = "short", + srcs = [ + "enforce_mpp_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 12, + deps = [ + "//pkg/domain", + "//pkg/parser/model", + "//pkg/planner/core/internal", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/collate", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/enforcempp/enforce_mpp_test.go b/pkg/planner/core/casetest/enforcempp/enforce_mpp_test.go new file mode 100644 index 0000000000000..f6017576d0296 --- /dev/null +++ b/pkg/planner/core/casetest/enforcempp/enforce_mpp_test.go @@ -0,0 +1,727 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package enforcempp + +import ( + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/stretchr/testify/require" +) + +func TestEnforceMPP(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test query + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("create index idx on t(a)") + tk.MustExec("CREATE TABLE `s` (\n `a` int(11) DEFAULT NULL,\n `b` int(11) DEFAULT NULL,\n `c` int(11) DEFAULT NULL,\n `d` int(11) DEFAULT NULL,\n UNIQUE KEY `a` (`a`),\n KEY `ii` (`a`,`b`)\n)") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + if tblInfo.Name.L == "s" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + filterWarnings := func(originalWarnings []stmtctx.SQLWarn) []stmtctx.SQLWarn { + warnings := make([]stmtctx.SQLWarn, 0, 4) + for _, warning := range originalWarnings { + // filter out warning about skyline pruning + if !strings.Contains(warning.Err.Error(), "remain after pruning paths for") { + warnings = append(warnings, warning) + } + } + return warnings + } + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(filterWarnings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + }) + require.Eventually(t, + func() bool { + res := tk.MustQuery(tt) + return res.Equal(testkit.Rows(output[i].Plan...)) + }, 1*time.Second, 100*time.Millisecond) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(filterWarnings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()))) + } +} + +// general cases. +func TestEnforceMPPWarning1(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test query + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int as (a+1), c enum('xx', 'yy'), d bit(1))") + tk.MustExec("create index idx on t(a)") + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") { + tk.MustExec(tt) + continue + } + if strings.HasPrefix(tt, "cmd: create-replica") { + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: false, + } + } + } + continue + } + if strings.HasPrefix(tt, "cmd: enable-replica") { + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// partition table. +func TestEnforceMPPWarning2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test query + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (a int, b char(20)) PARTITION BY HASH(a)") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// new collation. +func TestEnforceMPPWarning3(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test query + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t (a int, b char(20))") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + if strings.HasPrefix(tt, "cmd: enable-new-collation") { + collate.SetNewCollationEnabledForTest(true) + continue + } + if strings.HasPrefix(tt, "cmd: disable-new-collation") { + collate.SetNewCollationEnabledForTest(false) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } + collate.SetNewCollationEnabledForTest(true) +} + +// Test enforce mpp warning for joins +func TestEnforceMPPWarning4(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE t(a int primary key)") + tk.MustExec("drop table if exists s") + tk.MustExec("CREATE TABLE s(a int primary key)") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" || tblInfo.Name.L == "s" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// Test agg push down for MPP mode +func TestMPP2PhaseAggPushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists c") + tk.MustExec("drop table if exists o") + tk.MustExec("create table c(c_id bigint)") + tk.MustExec("create table o(o_id bigint, c_id bigint not null)") + + tk.MustExec("create table t (a int, b int)") + tk.MustExec("insert into t values (1, 1);") + tk.MustExec("insert into t values (1, 1);") + tk.MustExec("insert into t values (1, 1);") + tk.MustExec("insert into t values (1, 1);") + tk.MustExec("insert into t values (1, 1);") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "c" || tblInfo.Name.L == "o" || tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// Test skewed group distinct aggregate rewrite for MPP mode +func TestMPPSkewedGroupDistinctRewrite(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b bigint not null, c bigint, d date, e varchar(20))") + // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// Test 3 stage aggregation for single count distinct +func TestMPPSingleDistinct3Stage(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b bigint not null, c bigint, d date, e varchar(20) collate utf8mb4_general_ci)") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// todo: some post optimization after resolveIndices will inject another projection below agg, which change the column name used in higher operator, +// +// since it doesn't change the schema out (index ref is still the right), so by now it's fine. SEE case: EXPLAIN select count(distinct a), count(distinct b), sum(c) from t. +func TestMPPMultiDistinct3Stage(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test;") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, d int);") + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + tk.MustExec("set @@session.tidb_opt_enable_three_stage_multi_distinct_agg=1") + defer tk.MustExec("set @@session.tidb_opt_enable_three_stage_multi_distinct_agg=0") + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\";") + tk.MustExec("set @@session.tidb_enforce_mpp=1") + tk.MustExec("set @@session.tidb_allow_mpp=ON;") + // todo: current mock regionCache won't scale the regions among tiFlash nodes. The under layer still collect data from only one of the nodes. + tk.MustExec("split table t BETWEEN (0) AND (5000) REGIONS 5;") + tk.MustExec("insert into t values(1000, 1000, 1000, 1)") + tk.MustExec("insert into t values(1000, 1000, 1000, 1)") + tk.MustExec("insert into t values(2000, 2000, 2000, 1)") + tk.MustExec("insert into t values(2000, 2000, 2000, 1)") + tk.MustExec("insert into t values(3000, 3000, 3000, 1)") + tk.MustExec("insert into t values(3000, 3000, 3000, 1)") + tk.MustExec("insert into t values(4000, 4000, 4000, 1)") + tk.MustExec("insert into t values(4000, 4000, 4000, 1)") + tk.MustExec("insert into t values(5000, 5000, 5000, 1)") + tk.MustExec("insert into t values(5000, 5000, 5000, 1)") + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +// Test null-aware semi join push down for MPP mode +func TestMPPNullAwareSemiJoinPushDown(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists s") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("create table s(a int, b int, c int)") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustExec("alter table s set tiflash replica 1") + + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tb = external.GetTableByName(t, tk, "test", "s") + err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPSharedCTEScan(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + + // test table + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists s") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("create table s(a int, b int, c int)") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustExec("alter table s set tiflash replica 1") + + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tb = external.GetTableByName(t, tk, "test", "s") + err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + + tk.MustExec("set @@tidb_enforce_mpp='on'") + tk.MustExec("set @@tidb_opt_enable_mpp_shared_cte_execution='on'") + + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestRollupMPP(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("drop table if exists s") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("create table s(a int, b int, c int)") + tk.MustExec("CREATE TABLE `sales` (`year` int(11) DEFAULT NULL, `country` varchar(20) DEFAULT NULL, `product` varchar(32) DEFAULT NULL, `profit` int(11) DEFAULT NULL)") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustExec("alter table s set tiflash replica 1") + tk.MustExec("alter table sales set tiflash replica 1") + + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tb = external.GetTableByName(t, tk, "test", "s") + err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tb = external.GetTableByName(t, tk, "test", "sales") + err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + // error test + err = tk.ExecToErr("explain format = 'brief' SELECT country, product, SUM(profit) AS profit FROM sales GROUP BY country, country, product with rollup order by grouping(year);") + require.Equal(t, err.Error(), "[planner:3602]Argument #0 of GROUPING function is not in GROUP BY") + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + + tk.MustExec("set @@tidb_enforce_mpp='on'") + tk.Session().GetSessionVars().TiFlashFineGrainedShuffleStreamCount = -1 + + enforceMPPSuiteData := GetEnforceMPPSuiteData() + enforceMPPSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} diff --git a/pkg/planner/core/casetest/enforcempp/main_test.go b/pkg/planner/core/casetest/enforcempp/main_test.go new file mode 100644 index 0000000000000..9f7ba67aee4bd --- /dev/null +++ b/pkg/planner/core/casetest/enforcempp/main_test.go @@ -0,0 +1,53 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package enforcempp + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "enforce_mpp_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetEnforceMPPSuiteData() testdata.TestData { + return testDataMap["enforce_mpp_suite"] +} diff --git a/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_in.json b/pkg/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_in.json similarity index 100% rename from planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_in.json rename to pkg/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_in.json diff --git a/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_out.json b/pkg/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_out.json similarity index 100% rename from planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_out.json rename to pkg/planner/core/casetest/enforcempp/testdata/enforce_mpp_suite_out.json diff --git a/pkg/planner/core/casetest/flatplan/BUILD.bazel b/pkg/planner/core/casetest/flatplan/BUILD.bazel new file mode 100644 index 0000000000000..f246f4fe15e46 --- /dev/null +++ b/pkg/planner/core/casetest/flatplan/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "flatplan_test", + timeout = "short", + srcs = [ + "flat_plan_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + deps = [ + "//pkg/config", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/casetest/flatplan/flat_plan_test.go b/pkg/planner/core/casetest/flatplan/flat_plan_test.go similarity index 89% rename from planner/core/casetest/flatplan/flat_plan_test.go rename to pkg/planner/core/casetest/flatplan/flat_plan_test.go index d130fb2b9b575..ae4461ea6b553 100644 --- a/planner/core/casetest/flatplan/flat_plan_test.go +++ b/pkg/planner/core/casetest/flatplan/flat_plan_test.go @@ -19,14 +19,14 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/flatplan/main_test.go b/pkg/planner/core/casetest/flatplan/main_test.go new file mode 100644 index 0000000000000..7295be82c07b1 --- /dev/null +++ b/pkg/planner/core/casetest/flatplan/main_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package flatplan + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "flat_plan_suite") + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetFlatPlanSuiteData() testdata.TestData { + return testDataMap["flat_plan_suite"] +} diff --git a/planner/core/casetest/flatplan/testdata/flat_plan_suite_in.json b/pkg/planner/core/casetest/flatplan/testdata/flat_plan_suite_in.json similarity index 100% rename from planner/core/casetest/flatplan/testdata/flat_plan_suite_in.json rename to pkg/planner/core/casetest/flatplan/testdata/flat_plan_suite_in.json diff --git a/planner/core/casetest/flatplan/testdata/flat_plan_suite_out.json b/pkg/planner/core/casetest/flatplan/testdata/flat_plan_suite_out.json similarity index 100% rename from planner/core/casetest/flatplan/testdata/flat_plan_suite_out.json rename to pkg/planner/core/casetest/flatplan/testdata/flat_plan_suite_out.json diff --git a/pkg/planner/core/casetest/hint/BUILD.bazel b/pkg/planner/core/casetest/hint/BUILD.bazel new file mode 100644 index 0000000000000..9db40cf4873e4 --- /dev/null +++ b/pkg/planner/core/casetest/hint/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "hint_test", + timeout = "short", + srcs = [ + "hint_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 6, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/parser/model", + "//pkg/planner/core/internal", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/casetest/hint/hint_test.go b/pkg/planner/core/casetest/hint/hint_test.go similarity index 97% rename from planner/core/casetest/hint/hint_test.go rename to pkg/planner/core/casetest/hint/hint_test.go index 82aa6c8147462..4f13d1edce434 100644 --- a/planner/core/casetest/hint/hint_test.go +++ b/pkg/planner/core/casetest/hint/hint_test.go @@ -17,12 +17,12 @@ package hint import ( "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/hint/main_test.go b/pkg/planner/core/casetest/hint/main_test.go new file mode 100644 index 0000000000000..07482a3e3efe8 --- /dev/null +++ b/pkg/planner/core/casetest/hint/main_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package hint + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} diff --git a/planner/core/casetest/hint/testdata/integration_suite_in.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_in.json similarity index 100% rename from planner/core/casetest/hint/testdata/integration_suite_in.json rename to pkg/planner/core/casetest/hint/testdata/integration_suite_in.json diff --git a/planner/core/casetest/hint/testdata/integration_suite_out.json b/pkg/planner/core/casetest/hint/testdata/integration_suite_out.json similarity index 100% rename from planner/core/casetest/hint/testdata/integration_suite_out.json rename to pkg/planner/core/casetest/hint/testdata/integration_suite_out.json diff --git a/pkg/planner/core/casetest/index/BUILD.bazel b/pkg/planner/core/casetest/index/BUILD.bazel new file mode 100644 index 0000000000000..aac14243db6d6 --- /dev/null +++ b/pkg/planner/core/casetest/index/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "index_test", + timeout = "short", + srcs = [ + "index_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 12, + deps = [ + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/index/index_test.go b/pkg/planner/core/casetest/index/index_test.go new file mode 100644 index 0000000000000..b8841b121260c --- /dev/null +++ b/pkg/planner/core/casetest/index/index_test.go @@ -0,0 +1,419 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package index + +import ( + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestIndexJoinUniqueCompositeIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly + tk.MustExec("create table t1(a int not null, c int not null)") + tk.MustExec("create table t2(a int not null, b int not null, c int not null, primary key(a,b))") + tk.MustExec("insert into t1 values(1,1)") + tk.MustExec("insert into t2 values(1,1,1),(1,2,1)") + tk.MustExec("analyze table t1,t2") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIndexMerge(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, unique index(a), unique index(b), primary key(c))") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +// for issue #14822 and #38258 +func TestIndexJoinTableRange(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int, primary key (a), key idx_t1_b (b))") + tk.MustExec("create table t2(a int, b int, primary key (a), key idx_t1_b (b))") + tk.MustExec("create table t3(a int, b int, c int)") + tk.MustExec("create table t4(a int, b int, c int, primary key (a, b) clustered)") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIndexJoinInnerIndexNDV(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int not null, b int not null, c int not null)") + tk.MustExec("create table t2(a int not null, b int not null, c int not null, index idx1(a,b), index idx2(c))") + tk.MustExec("insert into t1 values(1,1,1),(1,1,1),(1,1,1)") + tk.MustExec("insert into t2 values(1,1,1),(1,1,2),(1,1,3)") + tk.MustExec("analyze table t1, t2") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIndexMergeSerial(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, unique key(a), unique key(b))") + tk.MustExec("insert into t value (1, 5), (2, 4), (3, 3), (4, 2), (5, 1)") + tk.MustExec("insert into t value (6, 0), (7, -1), (8, -2), (9, -3), (10, -4)") + tk.MustExec("analyze table t") + + var input []string + var output []struct { + SQL string + Plan []string + Warnings []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warnings = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warnings...)) + } +} + +func TestIndexJoinOnClusteredIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c))") + tk.MustExec(`insert into t values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13)`) + tk.MustExec("analyze table t") + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery("explain format = 'brief'" + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...)) + } +} + +func TestIndexMergeWithCorrelatedColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1(c1 int, c2 int, c3 int, primary key(c1), key(c2));") + tk.MustExec("insert into t1 values(1, 1, 1);") + tk.MustExec("insert into t1 values(2, 2, 2);") + tk.MustExec("create table t2(c1 int, c2 int, c3 int);") + tk.MustExec("insert into t2 values(1, 1, 1);") + tk.MustExec("insert into t2 values(2, 2, 2);") + + tk.MustExec("drop table if exists tt1, tt2;") + tk.MustExec("create table tt1 (c_int int, c_str varchar(40), c_datetime datetime, c_decimal decimal(12, 6), primary key(c_int), key(c_int), key(c_str), unique key(c_decimal), key(c_datetime));") + tk.MustExec("create table tt2 like tt1 ;") + tk.MustExec(`insert into tt1 (c_int, c_str, c_datetime, c_decimal) values (6, 'sharp payne', '2020-06-07 10:40:39', 6.117000) , + (7, 'objective kare', '2020-02-05 18:47:26', 1.053000) , + (8, 'thirsty pasteur', '2020-01-02 13:06:56', 2.506000) , + (9, 'blissful wilbur', '2020-06-04 11:34:04', 9.144000) , + (10, 'reverent mclean', '2020-02-12 07:36:26', 7.751000) ;`) + tk.MustExec(`insert into tt2 (c_int, c_str, c_datetime, c_decimal) values (6, 'beautiful joliot', '2020-01-16 01:44:37', 5.627000) , + (7, 'hopeful blackburn', '2020-05-23 21:44:20', 7.890000) , + (8, 'ecstatic davinci', '2020-02-01 12:27:17', 5.648000) , + (9, 'hopeful lewin', '2020-05-05 05:58:25', 7.288000) , + (10, 'sharp jennings', '2020-01-28 04:35:03', 9.758000) ;`) + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format=brief " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery("explain format=brief " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...)) + } +} + +func TestIndexJoinRangeFallback(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int, c varchar(10), d varchar(10), index idx_a_b_c_d(a, b, c(2), d(2)))") + tk.MustExec("create table t2(e int, f int, g varchar(10), h varchar(10))") + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + setStmt := strings.HasPrefix(tt, "set") + testdata.OnRecord(func() { + output[i].SQL = tt + if !setStmt { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + } + }) + if setStmt { + tk.MustExec(tt) + } else { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } + } +} + +func TestNullConditionForPrefixIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE t1 ( + id char(1) DEFAULT NULL, + c1 varchar(255) DEFAULT NULL, + c2 text DEFAULT NULL, + KEY idx1 (c1), + KEY idx2 (c1,c2(5)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`) + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t2(a int, b varchar(10), index idx(b(5)))") + tk.MustExec("create table t3(a int, b varchar(10), c int, primary key (a, b(5)) clustered)") + tk.MustExec("set tidb_opt_prefix_index_single_scan = 1") + tk.MustExec("insert into t1 values ('a', '0xfff', '111111'), ('b', '0xfff', '22 '), ('c', '0xfff', ''), ('d', '0xfff', null)") + tk.MustExec("insert into t2 values (1, 'aaaaaa'), (2, 'bb '), (3, ''), (4, null)") + tk.MustExec("insert into t3 values (1, 'aaaaaa', 2), (1, 'bb ', 3), (1, '', 4)") + + var input []string + var output []struct { + SQL string + Plan []string + Result []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + tt).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain format='brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) + } + + // test plan cache + tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0") + tk.MustExec("prepare stmt from 'select count(1) from t1 where c1 = ? and c2 is not null'") + tk.MustExec("set @a = '0xfff'") + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( + "StreamAgg_17 1.00 root funcs:count(Column#7)->Column#5", + "└─IndexReader_18 1.00 root index:StreamAgg_9", + " └─StreamAgg_9 1.00 cop[tikv] funcs:count(1)->Column#7", + " └─IndexRangeScan_16 99.90 cop[tikv] table:t1, index:idx2(c1, c2) range:[\"0xfff\" -inf,\"0xfff\" +inf], keep order:false, stats:pseudo")) +} + +func TestHeuristicIndexSelection(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int, c int, d int, e int, f int, g int, primary key (a), unique key c_d_e (c, d, e), unique key f (f), unique key f_g (f, g), key g (g))") + tk.MustExec("create table t2(a int, b int, c int, d int, unique index idx_a (a), unique index idx_b_c (b, c), unique index idx_b_c_a_d (b, c, a, d))") + tk.MustExec("create table t3(a bigint, b varchar(255), c bigint, primary key(a, b) clustered)") + tk.MustExec("create table t4(a bigint, b varchar(255), c bigint, primary key(a, b) nonclustered)") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + var input []string + var output []struct { + SQL string + Plan []string + Warnings []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'verbose' " + tt).Rows()) + output[i].Warnings = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'verbose' " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warnings...)) + } +} + +func TestLimitIndexLookUpKeepOrder(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int, c int, d int, index idx(a,b,c));") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestAccessPathOnClusterIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c))") + tk.MustExec(`insert into t1 values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13)`) + tk.MustExec("analyze table t1") + + var input []string + var output []struct { + SQL string + Plan []string + Res []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + tt).Rows()) + output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) + }) + tk.MustQuery("explain format='brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) + } +} diff --git a/pkg/planner/core/casetest/index/main_test.go b/pkg/planner/core/casetest/index/main_test.go new file mode 100644 index 0000000000000..b7f7c1d55f621 --- /dev/null +++ b/pkg/planner/core/casetest/index/main_test.go @@ -0,0 +1,54 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package index + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} diff --git a/planner/core/casetest/index/testdata/integration_suite_in.json b/pkg/planner/core/casetest/index/testdata/integration_suite_in.json similarity index 100% rename from planner/core/casetest/index/testdata/integration_suite_in.json rename to pkg/planner/core/casetest/index/testdata/integration_suite_in.json diff --git a/planner/core/casetest/index/testdata/integration_suite_out.json b/pkg/planner/core/casetest/index/testdata/integration_suite_out.json similarity index 100% rename from planner/core/casetest/index/testdata/integration_suite_out.json rename to pkg/planner/core/casetest/index/testdata/integration_suite_out.json diff --git a/pkg/planner/core/casetest/integration_test.go b/pkg/planner/core/casetest/integration_test.go new file mode 100644 index 0000000000000..d5aacba6c1bbc --- /dev/null +++ b/pkg/planner/core/casetest/integration_test.go @@ -0,0 +1,441 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package casetest + +import ( + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func TestVerboseExplain(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`set tidb_opt_limit_push_down_threshold=0`) + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int, b int)") + tk.MustExec("create table t3(a int, b int, index c(b))") + tk.MustExec("insert into t1 values(1,2)") + tk.MustExec("insert into t1 values(3,4)") + tk.MustExec("insert into t1 values(5,6)") + tk.MustExec("insert into t2 values(1,2)") + tk.MustExec("insert into t2 values(3,4)") + tk.MustExec("insert into t2 values(5,6)") + tk.MustExec("insert into t3 values(1,2)") + tk.MustExec("insert into t3 values(3,4)") + tk.MustExec("insert into t3 values(5,6)") + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustExec("analyze table t3") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t1" || tblInfo.Name.L == "t2" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIsolationReadTiFlashNotChoosePointGet(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, primary key (a))") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") + var input []string + var output []struct { + SQL string + Result []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) + } +} + +func TestIsolationReadDoNotFilterSystemDB(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set @@tidb_isolation_read_engines = \"tiflash\"") + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestPartitionPruningForInExpr(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int(11) not null, b int) partition by range (a) (partition p0 values less than (4), partition p1 values less than(10), partition p2 values less than maxvalue);") + tk.MustExec("insert into t values (1, 1),(10, 10),(11, 11)") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestPartitionExplain(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + + tk.MustExec("set @@tidb_enable_index_merge = 1;") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) + }) + tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestMergeContinuousSelections(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists ts") + tk.MustExec("create table ts (col_char_64 char(64), col_varchar_64_not_null varchar(64) not null, col_varchar_key varchar(1), id int primary key, col_varchar_64 varchar(64),col_char_64_not_null char(64) not null);") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "ts" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec(" set @@tidb_allow_mpp=1;") + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIssue31240(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t31240(a int, b int);") + tk.MustExec("set @@tidb_allow_mpp = 0") + tk.MustExec("set tidb_cost_model_version=2") + // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31240", L: "t31240"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + tk.MustExec("drop table if exists t31240") +} + +func TestIssue32632(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("CREATE TABLE `partsupp` (" + + " `PS_PARTKEY` bigint(20) NOT NULL," + + "`PS_SUPPKEY` bigint(20) NOT NULL," + + "`PS_AVAILQTY` bigint(20) NOT NULL," + + "`PS_SUPPLYCOST` decimal(15,2) NOT NULL," + + "`PS_COMMENT` varchar(199) NOT NULL," + + "PRIMARY KEY (`PS_PARTKEY`,`PS_SUPPKEY`) /*T![clustered_index] NONCLUSTERED */)") + tk.MustExec("CREATE TABLE `supplier` (" + + "`S_SUPPKEY` bigint(20) NOT NULL," + + "`S_NAME` char(25) NOT NULL," + + "`S_ADDRESS` varchar(40) NOT NULL," + + "`S_NATIONKEY` bigint(20) NOT NULL," + + "`S_PHONE` char(15) NOT NULL," + + "`S_ACCTBAL` decimal(15,2) NOT NULL," + + "`S_COMMENT` varchar(101) NOT NULL," + + "PRIMARY KEY (`S_SUPPKEY`) /*T![clustered_index] CLUSTERED */)") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("set @@tidb_enforce_mpp = 1") + + tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "partsupp", L: "partsupp"}) + require.NoError(t, err) + tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "supplier", L: "supplier"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + statsTbl1 := h.GetTableStats(tbl1.Meta()) + statsTbl1.RealtimeCount = 800000 + statsTbl2 := h.GetTableStats(tbl2.Meta()) + statsTbl2.RealtimeCount = 10000 + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + tk.MustExec("drop table if exists partsupp") + tk.MustExec("drop table if exists supplier") +} + +func TestTiFlashPartitionTableScan(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@tidb_enforce_mpp = on") + tk.MustExec("set @@tidb_allow_batch_cop = 2") + tk.MustExec("drop table if exists rp_t;") + tk.MustExec("drop table if exists hp_t;") + tk.MustExec("create table rp_t(a int) partition by RANGE (a) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN (21));") + tk.MustExec("create table hp_t(a int) partition by hash(a) partitions 4;") + tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "rp_t", L: "rp_t"}) + require.NoError(t, err) + tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "hp_t", L: "hp_t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + tk.MustExec("drop table rp_t;") + tk.MustExec("drop table hp_t;") +} + +func TestTiFlashFineGrainedShuffle(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@tidb_enforce_mpp = on") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int, c2 int)") + + tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + var input []string + var output []struct { + SQL string + Plan []string + } + integrationSuiteData := GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestFixControl45132(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec(`create table t (a int, b int, key(a))`) + values := make([]string, 0, 101) + for i := 0; i < 100; i++ { + values = append(values, "(1, 1)") + } + values = append(values, "(2, 2)") // count(1) : count(2) == 100 : 1 + tk.MustExec(`insert into t values ` + strings.Join(values, ",")) + for i := 0; i < 7; i++ { + tk.MustExec(`insert into t select * from t`) + } + tk.MustExec(`analyze table t`) + // the cost model prefers to use TableScan instead of IndexLookup to avoid double requests. + tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) + + tk.MustExec(`set @@tidb_opt_fix_control = "45132:99"`) + tk.MustIndexLookup(`select * from t where a=2`) // index lookup + + tk.MustExec(`set @@tidb_opt_fix_control = "45132:500"`) + tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) + + tk.MustExec(`set @@tidb_opt_fix_control = "45132:0"`) + tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) +} + +func TestIssue41957(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`use test`) + tk.MustExec("CREATE TABLE `github_events` (\n `id` bigint(20) NOT NULL DEFAULT '0',\n `type` varchar(29) NOT NULL DEFAULT 'Event',\n `created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `repo_id` bigint(20) NOT NULL DEFAULT '0',\n `repo_name` varchar(140) NOT NULL DEFAULT '',\n `actor_id` bigint(20) NOT NULL DEFAULT '0',\n `actor_login` varchar(40) NOT NULL DEFAULT '',\n `language` varchar(26) NOT NULL DEFAULT '',\n `additions` bigint(20) NOT NULL DEFAULT '0',\n `deletions` bigint(20) NOT NULL DEFAULT '0',\n `action` varchar(11) NOT NULL DEFAULT '',\n `number` int(11) NOT NULL DEFAULT '0',\n `commit_id` varchar(40) NOT NULL DEFAULT '',\n `comment_id` bigint(20) NOT NULL DEFAULT '0',\n `org_login` varchar(40) NOT NULL DEFAULT '',\n `org_id` bigint(20) NOT NULL DEFAULT '0',\n `state` varchar(6) NOT NULL DEFAULT '',\n `closed_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `comments` int(11) NOT NULL DEFAULT '0',\n `pr_merged_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `pr_merged` tinyint(1) NOT NULL DEFAULT '0',\n `pr_changed_files` int(11) NOT NULL DEFAULT '0',\n `pr_review_comments` int(11) NOT NULL DEFAULT '0',\n `pr_or_issue_id` bigint(20) NOT NULL DEFAULT '0',\n `event_day` date NOT NULL,\n `event_month` date NOT NULL,\n `event_year` int(11) NOT NULL,\n `push_size` int(11) NOT NULL DEFAULT '0',\n `push_distinct_size` int(11) NOT NULL DEFAULT '0',\n `creator_user_login` varchar(40) NOT NULL DEFAULT '',\n `creator_user_id` bigint(20) NOT NULL DEFAULT '0',\n `pr_or_issue_created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n KEY `index_github_events_on_id` (`id`),\n KEY `index_github_events_on_created_at` (`created_at`),\n KEY `index_github_events_on_repo_id_type_action_month_actor_login` (`repo_id`,`type`,`action`,`event_month`,`actor_login`),\n KEY `index_ge_on_repo_id_type_action_pr_merged_created_at_add_del` (`repo_id`,`type`,`action`,`pr_merged`,`created_at`,`additions`,`deletions`),\n KEY `index_ge_on_creator_id_type_action_merged_created_at_add_del` (`creator_user_id`,`type`,`action`,`pr_merged`,`created_at`,`additions`,`deletions`),\n KEY `index_ge_on_actor_id_type_action_created_at_repo_id_commits` (`actor_id`,`type`,`action`,`created_at`,`repo_id`,`push_distinct_size`),\n KEY `index_ge_on_repo_id_type_action_created_at_number_pdsize_psize` (`repo_id`,`type`,`action`,`created_at`,`number`,`push_distinct_size`,`push_size`),\n KEY `index_ge_on_repo_id_type_action_created_at_actor_login` (`repo_id`,`type`,`action`,`created_at`,`actor_login`),\n KEY `index_ge_on_repo_name_type` (`repo_name`,`type`),\n KEY `index_ge_on_actor_login_type` (`actor_login`,`type`),\n KEY `index_ge_on_org_login_type` (`org_login`,`type`),\n KEY `index_ge_on_language` (`language`),\n KEY `index_ge_on_org_id_type` (`org_id`,`type`),\n KEY `index_ge_on_actor_login_lower` ((lower(`actor_login`))),\n KEY `index_ge_on_repo_name_lower` ((lower(`repo_name`))),\n KEY `index_ge_on_language_lower` ((lower(`language`))),\n KEY `index_ge_on_type_action` (`type`,`action`) /*!80000 INVISIBLE */,\n KEY `index_ge_on_repo_id_type_created_at` (`repo_id`,`type`,`created_at`),\n KEY `index_ge_on_repo_id_created_at` (`repo_id`,`created_at`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\nPARTITION BY LIST COLUMNS(`type`)\n(PARTITION `push_event` VALUES IN ('PushEvent'),\n PARTITION `create_event` VALUES IN ('CreateEvent'),\n PARTITION `pull_request_event` VALUES IN ('PullRequestEvent'),\n PARTITION `watch_event` VALUES IN ('WatchEvent'),\n PARTITION `issue_comment_event` VALUES IN ('IssueCommentEvent'),\n PARTITION `issues_event` VALUES IN ('IssuesEvent'),\n PARTITION `delete_event` VALUES IN ('DeleteEvent'),\n PARTITION `fork_event` VALUES IN ('ForkEvent'),\n PARTITION `pull_request_review_comment_event` VALUES IN ('PullRequestReviewCommentEvent'),\n PARTITION `pull_request_review_event` VALUES IN ('PullRequestReviewEvent'),\n PARTITION `gollum_event` VALUES IN ('GollumEvent'),\n PARTITION `release_event` VALUES IN ('ReleaseEvent'),\n PARTITION `member_event` VALUES IN ('MemberEvent'),\n PARTITION `commit_comment_event` VALUES IN ('CommitCommentEvent'),\n PARTITION `public_event` VALUES IN ('PublicEvent'),\n PARTITION `gist_event` VALUES IN ('GistEvent'),\n PARTITION `follow_event` VALUES IN ('FollowEvent'),\n PARTITION `event` VALUES IN ('Event'),\n PARTITION `download_event` VALUES IN ('DownloadEvent'),\n PARTITION `team_add_event` VALUES IN ('TeamAddEvent'),\n PARTITION `fork_apply_event` VALUES IN ('ForkApplyEvent'))\n") + tk.MustQuery("SELECT\n repo_id, GROUP_CONCAT(\n DISTINCT actor_login\n ORDER BY cnt DESC\n SEPARATOR ','\n ) AS actor_logins\nFROM (\n SELECT\n ge.repo_id AS repo_id,\n ge.actor_login AS actor_login,\n COUNT(*) AS cnt\n FROM github_events ge\n WHERE\n type = 'PullRequestEvent' AND action = 'opened'\n AND (ge.created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY) AND ge.created_at <= NOW())\n GROUP BY ge.repo_id, ge.actor_login\n ORDER BY cnt DESC\n) sub\nGROUP BY repo_id").Check(testkit.Rows()) +} diff --git a/pkg/planner/core/casetest/main_test.go b/pkg/planner/core/casetest/main_test.go new file mode 100644 index 0000000000000..173c428d9ff96 --- /dev/null +++ b/pkg/planner/core/casetest/main_test.go @@ -0,0 +1,69 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package casetest + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_normalized_suite") + testDataMap.LoadTestSuiteData("testdata", "stats_suite") + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + testDataMap.LoadTestSuiteData("testdata", "json_plan_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetPlanNormalizedSuiteData() testdata.TestData { + return testDataMap["plan_normalized_suite"] +} + +func GetStatsSuiteData() testdata.TestData { + return testDataMap["stats_suite"] +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} + +func GetJSONPlanSuiteData() testdata.TestData { + return testDataMap["json_plan_suite"] +} diff --git a/pkg/planner/core/casetest/mpp/BUILD.bazel b/pkg/planner/core/casetest/mpp/BUILD.bazel new file mode 100644 index 0000000000000..5a32c6bebf92e --- /dev/null +++ b/pkg/planner/core/casetest/mpp/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "mpp_test", + timeout = "short", + srcs = [ + "main_test.go", + "mpp_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 20, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/mpp/main_test.go b/pkg/planner/core/casetest/mpp/main_test.go new file mode 100644 index 0000000000000..a488d8aa28f56 --- /dev/null +++ b/pkg/planner/core/casetest/mpp/main_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package mpp + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Performance.EnableStatsCacheMemQuota = true + }) + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} diff --git a/planner/core/casetest/mpp/mpp_test.go b/pkg/planner/core/casetest/mpp/mpp_test.go similarity index 98% rename from planner/core/casetest/mpp/mpp_test.go rename to pkg/planner/core/casetest/mpp/mpp_test.go index 8660cb79b6ff7..dd674f7a26d32 100644 --- a/planner/core/casetest/mpp/mpp_test.go +++ b/pkg/planner/core/casetest/mpp/mpp_test.go @@ -19,10 +19,10 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) @@ -670,11 +670,11 @@ func TestMppFineGrainedJoinAndAgg(t *testing.T) { "tiflash,127.0.0.1:3933,127.0.0.1:7777,,", "tikv,127.0.0.1:11080,127.0.0.1:10080,,", } - fpName := "github.com/pingcap/tidb/infoschema/mockStoreServerInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockStoreServerInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - fpName2 := "github.com/pingcap/tidb/planner/core/mockTiFlashStreamCountUsingMinLogicalCores" + fpName2 := "github.com/pingcap/tidb/pkg/planner/core/mockTiFlashStreamCountUsingMinLogicalCores" require.NoError(t, failpoint.Enable(fpName2, `return("8")`)) defer func() { require.NoError(t, failpoint.Disable(fpName2)) }() diff --git a/planner/core/casetest/mpp/testdata/integration_suite_in.json b/pkg/planner/core/casetest/mpp/testdata/integration_suite_in.json similarity index 100% rename from planner/core/casetest/mpp/testdata/integration_suite_in.json rename to pkg/planner/core/casetest/mpp/testdata/integration_suite_in.json diff --git a/planner/core/casetest/mpp/testdata/integration_suite_out.json b/pkg/planner/core/casetest/mpp/testdata/integration_suite_out.json similarity index 100% rename from planner/core/casetest/mpp/testdata/integration_suite_out.json rename to pkg/planner/core/casetest/mpp/testdata/integration_suite_out.json diff --git a/pkg/planner/core/casetest/partition/BUILD.bazel b/pkg/planner/core/casetest/partition/BUILD.bazel new file mode 100644 index 0000000000000..bb7ecd6f0beda --- /dev/null +++ b/pkg/planner/core/casetest/partition/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "partition_test", + timeout = "short", + srcs = [ + "integration_partition_test.go", + "main_test.go", + "partition_pruner_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 6, + deps = [ + "//pkg/config", + "//pkg/planner/core/internal", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/partition/integration_partition_test.go b/pkg/planner/core/casetest/partition/integration_partition_test.go new file mode 100644 index 0000000000000..811ea9a4e6ca1 --- /dev/null +++ b/pkg/planner/core/casetest/partition/integration_partition_test.go @@ -0,0 +1,248 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package partition + +import ( + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func TestListPartitionPruning(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database list_partition_pruning") + tk.MustExec("use list_partition_pruning") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + tk.MustExec(`create table tlist (a int) partition by list (a) ( + partition p0 values in (0, 1, 2), + partition p1 values in (3, 4, 5), + partition p2 values in (6, 7, 8), + partition p3 values in (9, 10, 11))`) + tk.MustExec(`create table tcollist (a int) partition by list columns(a) ( + partition p0 values in (0, 1, 2), + partition p1 values in (3, 4, 5), + partition p2 values in (6, 7, 8), + partition p3 values in (9, 10, 11))`) + tk.MustExec(`analyze table tlist`) + tk.MustExec(`analyze table tcollist`) + + var input []string + var output []struct { + SQL string + DynamicPlan []string + StaticPlan []string + } + integrationPartitionSuiteData := getIntegrationPartitionSuiteData() + integrationPartitionSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery(tt).Check(testkit.Rows(output[i].DynamicPlan...)) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustQuery(tt).Check(testkit.Rows(output[i].StaticPlan...)) + } +} + +func TestPartitionTableExplain(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t (a int primary key, b int, key (b)) partition by hash(a) (partition P0, partition p1, partition P2)`) + tk.MustExec(`create table t2 (a int, b int)`) + tk.MustExec(`insert into t values (1,1),(2,2),(3,3)`) + tk.MustExec(`insert into t2 values (1,1),(2,2),(3,3)`) + tk.MustExec(`analyze table t, t2`) + + var input []string + var output []struct { + SQL string + DynamicPlan []string + StaticPlan []string + } + integrationPartitionSuiteData := getIntegrationPartitionSuiteData() + integrationPartitionSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery(tt).Check(testkit.Rows(output[i].DynamicPlan...)) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustQuery(tt).Check(testkit.Rows(output[i].StaticPlan...)) + } +} + +func TestBatchPointGetTablePartition(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table thash1(a int, b int, primary key(a,b) nonclustered) partition by hash(b) partitions 2") + tk.MustExec("insert into thash1 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table trange1(a int, b int, primary key(a,b) nonclustered) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)") + tk.MustExec("insert into trange1 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table tlist1(a int, b int, primary key(a,b) nonclustered) partition by list(b) (partition p0 values in (0, 1), partition p1 values in (2, 3))") + tk.MustExec("insert into tlist1 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table thash2(a int, b int, primary key(a,b)) partition by hash(b) partitions 2") + tk.MustExec("insert into thash2 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table trange2(a int, b int, primary key(a,b)) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)") + tk.MustExec("insert into trange2 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table tlist2(a int, b int, primary key(a,b)) partition by list(b) (partition p0 values in (0, 1), partition p1 values in (2, 3))") + tk.MustExec("insert into tlist2 values(1,1),(1,2),(2,1),(2,2)") + + tk.MustExec("create table thash3(a int, b int, primary key(a)) partition by hash(a) partitions 2") + tk.MustExec("insert into thash3 values(1,0),(2,0),(3,0),(4,0)") + + tk.MustExec("create table trange3(a int, b int, primary key(a)) partition by range(a) (partition p0 values less than (3), partition p1 values less than maxvalue)") + tk.MustExec("insert into trange3 values(1,0),(2,0),(3,0),(4,0)") + + tk.MustExec("create table tlist3(a int, b int, primary key(a)) partition by list(a) (partition p0 values in (0, 1, 2), partition p1 values in (3, 4, 5))") + tk.MustExec("insert into tlist3 values(1,0),(2,0),(3,0),(4,0)") + + tk.MustExec("create table issue45889(a int) partition by list(a) (partition p0 values in (0, 1), partition p1 values in (2, 3))") + tk.MustExec("insert into issue45889 values (0),(0),(1),(1),(2),(2),(3),(3)") + + var input []string + var output []struct { + SQL string + DynamicPlan []string + StaticPlan []string + Result []string + } + + integrationPartitionSuiteData := getIntegrationPartitionSuiteData() + integrationPartitionSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + dynamicQuery := tk.MustQuery(tt) + if !strings.Contains(tt, "order by") { + dynamicQuery = dynamicQuery.Sort() + } + dynamicRes := testdata.ConvertRowsToStrings(dynamicQuery.Rows()) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + staticQuery := tk.MustQuery(tt) + if !strings.Contains(tt, "order by") { + staticQuery = staticQuery.Sort() + } + staticRes := testdata.ConvertRowsToStrings(staticQuery.Rows()) + require.Equal(t, dynamicRes, staticRes) + output[i].Result = staticRes + }) + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].DynamicPlan...)) + if strings.Contains(tt, "order by") { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) + } else { + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) + } + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].StaticPlan...)) + if strings.Contains(tt, "order by") { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) + } else { + tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) + } + } +} + +func TestBatchPointGetPartitionForAccessObject(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int, UNIQUE KEY (b)) PARTITION BY HASH(b) PARTITIONS 4") + tk.MustExec("insert into t1 values(1, 1), (2, 2), (3, 3), (4, 4)") + + tk.MustExec("CREATE TABLE t2 (id int primary key, name_id int) PARTITION BY LIST(id) (" + + "partition p0 values IN (1, 2), " + + "partition p1 values IN (3, 4), " + + "partition p3 values IN (5))") + tk.MustExec("insert into t2 values(1, 1), (2, 2), (3, 3), (4, 4)") + + tk.MustExec("CREATE TABLE t3 (id int primary key, name_id int) PARTITION BY LIST COLUMNS(id) (" + + "partition p0 values IN (1, 2), " + + "partition p1 values IN (3, 4), " + + "partition p3 values IN (5))") + tk.MustExec("insert into t3 values(1, 1), (2, 2), (3, 3), (4, 4)") + + tk.MustExec("CREATE TABLE t4 (id int, name_id int, unique key(id, name_id)) PARTITION BY LIST COLUMNS(id, name_id) (" + + "partition p0 values IN ((1, 1),(2, 2)), " + + "partition p1 values IN ((3, 3),(4, 4)), " + + "partition p3 values IN ((5, 5)))") + tk.MustExec("insert into t4 values(1, 1), (2, 2), (3, 3), (4, 4)") + + tk.MustExec("CREATE TABLE t5 (id int, name varchar(10), unique key(id, name)) PARTITION BY LIST COLUMNS(id, name) (" + + "partition p0 values IN ((1,'a'),(2,'b')), " + + "partition p1 values IN ((3,'c'),(4,'d')), " + + "partition p3 values IN ((5,'e')))") + tk.MustExec("insert into t5 values(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')") + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + + var input []string + var output []struct { + SQL string + Plan []string + } + + integrationPartitionSuiteData := getIntegrationPartitionSuiteData() + integrationPartitionSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + }) + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } +} diff --git a/pkg/planner/core/casetest/partition/main_test.go b/pkg/planner/core/casetest/partition/main_test.go new file mode 100644 index 0000000000000..025108ac7f246 --- /dev/null +++ b/pkg/planner/core/casetest/partition/main_test.go @@ -0,0 +1,62 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package partition + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "integration_partition_suite") + testDataMap.LoadTestSuiteData("testdata", "partition_pruner") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + conf.Performance.EnableStatsCacheMemQuota = true + }) + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func getIntegrationPartitionSuiteData() testdata.TestData { + return testDataMap["integration_partition_suite"] +} + +func getPartitionPrunerData() testdata.TestData { + return testDataMap["partition_pruner"] +} diff --git a/planner/core/casetest/partition/partition_pruner_test.go b/pkg/planner/core/casetest/partition/partition_pruner_test.go similarity index 94% rename from planner/core/casetest/partition/partition_pruner_test.go rename to pkg/planner/core/casetest/partition/partition_pruner_test.go index 52961bcff1f4d..8aa63eb3bff2e 100644 --- a/planner/core/casetest/partition/partition_pruner_test.go +++ b/pkg/planner/core/casetest/partition/partition_pruner_test.go @@ -22,16 +22,16 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) func TestHashPartitionPruner(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("create database test_partition") @@ -120,8 +120,8 @@ func checkPrunePartitionInfo(c *testing.T, query string, infos1 string, plan []s } func TestListColumnsPartitionPruner(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("set tidb_cost_model_version=2") diff --git a/planner/core/casetest/partition/testdata/integration_partition_suite_in.json b/pkg/planner/core/casetest/partition/testdata/integration_partition_suite_in.json similarity index 100% rename from planner/core/casetest/partition/testdata/integration_partition_suite_in.json rename to pkg/planner/core/casetest/partition/testdata/integration_partition_suite_in.json diff --git a/planner/core/casetest/partition/testdata/integration_partition_suite_out.json b/pkg/planner/core/casetest/partition/testdata/integration_partition_suite_out.json similarity index 100% rename from planner/core/casetest/partition/testdata/integration_partition_suite_out.json rename to pkg/planner/core/casetest/partition/testdata/integration_partition_suite_out.json diff --git a/planner/core/casetest/partition/testdata/partition_pruner_in.json b/pkg/planner/core/casetest/partition/testdata/partition_pruner_in.json similarity index 100% rename from planner/core/casetest/partition/testdata/partition_pruner_in.json rename to pkg/planner/core/casetest/partition/testdata/partition_pruner_in.json diff --git a/planner/core/casetest/partition/testdata/partition_pruner_out.json b/pkg/planner/core/casetest/partition/testdata/partition_pruner_out.json similarity index 100% rename from planner/core/casetest/partition/testdata/partition_pruner_out.json rename to pkg/planner/core/casetest/partition/testdata/partition_pruner_out.json diff --git a/pkg/planner/core/casetest/physicalplantest/BUILD.bazel b/pkg/planner/core/casetest/physicalplantest/BUILD.bazel new file mode 100644 index 0000000000000..63eb84c1bafb4 --- /dev/null +++ b/pkg/planner/core/casetest/physicalplantest/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "physicalplantest_test", + timeout = "short", + srcs = [ + "main_test.go", + "physical_plan_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + race = "on", + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/planner/core/internal", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/hint", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/physicalplantest/main_test.go b/pkg/planner/core/casetest/physicalplantest/main_test.go new file mode 100644 index 0000000000000..7d60f7f3329f0 --- /dev/null +++ b/pkg/planner/core/casetest/physicalplantest/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package physicalplantest + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetPlanSuiteData() testdata.TestData { + return testDataMap["plan_suite"] +} diff --git a/pkg/planner/core/casetest/physicalplantest/physical_plan_test.go b/pkg/planner/core/casetest/physicalplantest/physical_plan_test.go new file mode 100644 index 0000000000000..0d481614c6e25 --- /dev/null +++ b/pkg/planner/core/casetest/physicalplantest/physical_plan_test.go @@ -0,0 +1,2243 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package physicalplantest + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/stretchr/testify/require" +) + +func assertSameHints(t *testing.T, expected, actual []*ast.TableOptimizerHint) { + expectedStr := make([]string, 0, len(expected)) + actualStr := make([]string, 0, len(actual)) + for _, h := range expected { + expectedStr = append(expectedStr, hint.RestoreTableOptimizerHint(h)) + } + for _, h := range actual { + actualStr = append(actualStr, hint.RestoreTableOptimizerHint(h)) + } + require.ElementsMatch(t, expectedStr, actualStr) +} + +func doTestPushdownDistinct(t *testing.T, vars, input []string, output []struct { + SQL string + Plan []string + Result []string +}) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, index(c))") + tk.MustExec("insert into t values (1, 1, 1), (1, 1, 3), (1, 2, 3), (2, 1, 3), (1, 2, NULL);") + + tk.MustExec("drop table if exists pt") + tk.MustExec(`CREATE TABLE pt (a int, b int) PARTITION BY RANGE (a) ( + PARTITION p0 VALUES LESS THAN (2), + PARTITION p1 VALUES LESS THAN (100) + );`) + + tk.MustExec("drop table if exists tc;") + tk.MustExec("CREATE TABLE `tc`(`timestamp` timestamp NULL DEFAULT NULL, KEY `idx_timestamp` (`timestamp`)) PARTITION BY RANGE ( UNIX_TIMESTAMP(`timestamp`) ) (PARTITION `p2020072312` VALUES LESS THAN (1595480400),PARTITION `p2020072313` VALUES LESS THAN (1595484000));") + + tk.MustExec("drop table if exists ta") + tk.MustExec("create table ta(a int);") + tk.MustExec("insert into ta values(1), (1);") + tk.MustExec("drop table if exists tb") + tk.MustExec("create table tb(a int);") + tk.MustExec("insert into tb values(1), (1);") + + tk.MustExec("set session sql_mode=''") + tk.MustExec(fmt.Sprintf("set session %s=1", variable.TiDBHashAggPartialConcurrency)) + tk.MustExec(fmt.Sprintf("set session %s=1", variable.TiDBHashAggFinalConcurrency)) + + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + + for _, v := range vars { + tk.MustExec(v) + } + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestRefine(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + + var input []string + var output []struct { + SQL string + Best string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, tt := range input { + comment := fmt.Sprintf("input: %s", tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + sc := tk.Session().GetSessionVars().StmtCtx + sc.IgnoreTruncate.Store(false) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err, comment) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Best = core.ToString(p) + }) + require.Equal(t, output[i].Best, core.ToString(p), comment) + } +} + +func TestAggEliminator(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("set tidb_opt_limit_push_down_threshold=0") + tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by + var input []string + var output []struct { + SQL string + Best string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, tt := range input { + comment := fmt.Sprintf("input: %s", tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + sc := tk.Session().GetSessionVars().StmtCtx + sc.IgnoreTruncate.Store(false) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Best = core.ToString(p) + }) + require.Equal(t, output[i].Best, core.ToString(p), fmt.Sprintf("input: %s", tt)) + } +} + +func TestINMJHint(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int primary key, b int not null)") + tk.MustExec("create table t2(a int primary key, b int not null)") + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestEliminateMaxOneRow(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1;") + tk.MustExec("drop table if exists t2;") + tk.MustExec("drop table if exists t3;") + tk.MustExec("create table t1(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL, UNIQUE KEY idx_a (a))") + tk.MustExec("create table t2(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL)") + tk.MustExec("create table t3(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL, c int(11) DEFAULT NULL, UNIQUE KEY idx_abc (a, b, c))") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) + } +} + +func TestIndexJoinUnionScan(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int primary key, b int, index idx(a))") + tk.MustExec("create table tt (a int primary key) partition by range (a) (partition p0 values less than (100), partition p1 values less than (200))") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + + var input [][]string + var output []struct { + SQL []string + Plan []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + tk.MustExec("begin") + for j, tt := range ts { + if j != len(ts)-1 { + tk.MustExec(tt) + } + testdata.OnRecord(func() { + output[i].SQL = ts + if j == len(ts)-1 { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + } + }) + if j == len(ts)-1 { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + } + tk.MustExec("rollback") + } +} + +func TestMergeJoinUnionScan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (c_int int, c_str varchar(40), primary key (c_int))") + tk.MustExec("create table t2 (c_int int, c_str varchar(40), primary key (c_int))") + tk.MustExec("insert into t1 (`c_int`, `c_str`) values (11, 'keen williamson'), (10, 'gracious hermann')") + tk.MustExec("insert into t2 (`c_int`, `c_str`) values (10, 'gracious hermann')") + + var input [][]string + var output []struct { + SQL []string + Plan []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + tk.MustExec("begin") + for j, tt := range ts { + if j != len(ts)-1 { + tk.MustExec(tt) + } + testdata.OnRecord(func() { + output[i].SQL = ts + if j == len(ts)-1 { + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + } + }) + if j == len(ts)-1 { + tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) + } + } + tk.MustExec("rollback") + } +} + +func TestSemiJoinToInner(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + + var input []string + var output []struct { + SQL string + Best string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, tt := range input { + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Best = core.ToString(p) + }) + require.Equal(t, output[i].Best, core.ToString(p)) + } +} + +func TestUnmatchedTableInHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + var input []string + var output []struct { + SQL string + Warning string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, test := range input { + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err) + _, _, err = planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + testdata.OnRecord(func() { + output[i].SQL = test + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + }) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + } +} + +func TestIssue37520(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int primary key, b int);") + tk.MustExec("create table t2(a int, b int, index ia(a));") + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPHints(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustExec("set @@session.tidb_allow_mpp=ON") + tk.MustExec("create definer='root'@'localhost' view v as select a, sum(b) from t group by a, c;") + tk.MustExec("create definer='root'@'localhost' view v1 as select t1.a from t t1, t t2 where t1.a=t2.a;") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPHintsScope(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tk.MustExec("select /*+ MPP_1PHASE_AGG() */ a, sum(b) from t group by a, c") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The agg can not push down to the MPP side, the MPP_1PHASE_AGG() hint is invalid")) + tk.MustExec("select /*+ MPP_2PHASE_AGG() */ a, sum(b) from t group by a, c") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The agg can not push down to the MPP side, the MPP_2PHASE_AGG() hint is invalid")) + tk.MustExec("select /*+ shuffle_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The join can not push down to the MPP side, the shuffle_join() hint is invalid")) + tk.MustExec("select /*+ broadcast_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The join can not push down to the MPP side, the broadcast_join() hint is invalid")) + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPBCJModel(t *testing.T) { + /* + if there are 3 mpp stores, planner won't choose broadcast join enven if `tidb_prefer_broadcast_join_by_exchange_data_size` is ON + broadcast exchange size: + Build: 2 * sizeof(Data) + Probe: 0 + exchange size: Build = 2 * sizeof(Data) + hash exchange size: + Build: sizeof(Data) * 2 / 3 + Probe: sizeof(Data) * 2 / 3 + exchange size: Build + Probe = 4/3 * sizeof(Data) + */ + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) + { + cnt, err := store.GetMPPClient().GetMPPStoreCount() + require.Equal(t, cnt, 3) + require.Nil(t, err) + } + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPPreferBCJ(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int)") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (b int)") + + tk.MustExec("insert into t1 values (1);") + tk.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8);") + + { + tk.MustExec("alter table t1 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t1") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + { + tk.MustExec("alter table t2 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t2") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + { + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } + } +} + +func TestMPPBCJModelOneTiFlash(t *testing.T) { + /* + if there are 1 mpp stores, planner should choose broadcast join if `tidb_prefer_broadcast_join_by_exchange_data_size` is ON + broadcast exchange size: + Build: 0 * sizeof(Data) + Probe: 0 + exchange size: Build = 0 * sizeof(Data) + hash exchange size: + Build: sizeof(Data) * 0 / 1 + Probe: sizeof(Data) * 0 / 1 + exchange size: Build + Probe = 0 * sizeof(Data) + */ + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(1)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + { + cnt, err := store.GetMPPClient().GetMPPStoreCount() + require.Equal(t, cnt, 1) + require.Nil(t, err) + } + { + tk.MustExecToErr("set @@session.tidb_prefer_broadcast_join_by_exchange_data_size=-1") + tk.MustExecToErr("set @@session.tidb_prefer_broadcast_join_by_exchange_data_size=2") + } + { + // no BCJ if `tidb_prefer_broadcast_join_by_exchange_data_size` is OFF + tk.MustExec("set @@session.tidb_broadcast_join_threshold_size=0") + tk.MustExec("set @@session.tidb_broadcast_join_threshold_count=0") + } + + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } +} + +func TestMPPRightSemiJoin(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int)") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (b int)") + + tk.MustExec("insert into t1 values (1);") + tk.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8);") + + { + tk.MustExec("alter table t1 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t1") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + { + tk.MustExec("alter table t2 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t2") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + { + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } + } +} + +func TestMPPRightOuterJoin(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int, c int)") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (b int, d int)") + + tk.MustExec("insert into t1 values (1, 10), (2, 20), (3, 30), (4, 40), (5, 50);") + tk.MustExec("insert into t2 values (1, 12), (2, 18), (7, 66);") + + { + tk.MustExec("alter table t1 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t1") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + { + tk.MustExec("alter table t2 set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t2") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + } + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + { + var input []string + var output []struct { + SQL string + Plan []string + Warn []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + }) + if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { + tk.MustExec(tt) + continue + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) + output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) + }) + res := tk.MustQuery(tt) + res.Check(testkit.Rows(output[i].Plan...)) + require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) + } + } +} + +func TestHintScope(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) + + var input []string + var output []struct { + SQL string + Best string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(context.Background(), tk.Session(), stmt, is) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Best = core.ToString(p) + }) + require.Equal(t, output[i].Best, core.ToString(p)) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + require.Len(t, warnings, 0, comment) + } +} + +func TestJoinHints(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + + var input []string + var output []struct { + SQL string + Best string + Warning string + Hints string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Best = core.ToString(p) + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) + }) + require.Equal(t, output[i].Best, core.ToString(p)) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + hints := core.GenHintsFromPhysicalPlan(p) + + // test the new genHints code + flat := core.FlattenPhysicalPlan(p, false) + newHints := core.GenHintsFromFlatPlan(flat) + assertSameHints(t, hints, newHints) + + require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) + } +} + +func TestAggregationHints(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + sessionVars := tk.Session().GetSessionVars() + sessionVars.SetHashAggFinalConcurrency(1) + sessionVars.SetHashAggPartialConcurrency(1) + + var input []struct { + SQL string + AggPushDown bool + } + var output []struct { + SQL string + Best string + Warning string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, test := range input { + comment := fmt.Sprintf("case: %v sql: %v", i, test) + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + tk.Session().GetSessionVars().AllowAggPushDown = test.AggPushDown + + stmt, err := p.ParseOneStmt(test.SQL, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + + testdata.OnRecord(func() { + output[i].SQL = test.SQL + output[i].Best = core.ToString(p) + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + }) + require.Equal(t, output[i].Best, core.ToString(p), comment) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + } +} + +func TestSemiJoinRewriteHints(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t(a int, b int, c int)") + + sessionVars := tk.Session().GetSessionVars() + sessionVars.SetHashAggFinalConcurrency(1) + sessionVars.SetHashAggPartialConcurrency(1) + + var input []string + var output []struct { + SQL string + Plan []string + Warning string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, test := range input { + comment := fmt.Sprintf("case: %v sql: %v", i, test) + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + _, _, err = planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief'" + test).Rows()) + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + }) + tk.MustQuery("explain format = 'brief'" + test).Check(testkit.Rows(output[i].Plan...)) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + } +} + +func TestAggToCopHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists ta") + tk.MustExec("create table ta(a int, b int, index(a))") + + var ( + input []string + output []struct { + SQL string + Best string + Warning string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + ctx := context.Background() + is := domain.GetDomain(tk.Session()).InfoSchema() + p := parser.New() + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + testdata.OnRecord(func() { + output[i].SQL = test + }) + require.Equal(t, output[i].SQL, test, comment) + + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err, comment) + planString := core.ToString(p) + testdata.OnRecord(func() { + output[i].Best = planString + }) + require.Equal(t, output[i].Best, planString, comment) + + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + testdata.OnRecord(func() { + if len(warnings) > 0 { + output[i].Warning = warnings[0].Err.Error() + } + }) + if output[i].Warning == "" { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) + require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) + require.Equal(t, output[i].Warning, warnings[0].Err.Error()) + } + } +} + +func TestLimitToCopHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists tn") + tk.MustExec("create table tn(a int, b int, c int, d int, key (a, b, c, d))") + tk.MustExec(`set tidb_opt_limit_push_down_threshold=0`) + + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + + comment := fmt.Sprintf("case:%v sql:%s", i, ts) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + testdata.OnRecord(func() { + if len(warnings) > 0 { + output[i].Warning = make([]string, len(warnings)) + for j, warning := range warnings { + output[i].Warning[j] = warning.Err.Error() + } + } + }) + if len(output[i].Warning) == 0 { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, len(output[i].Warning), comment) + for j, warning := range warnings { + require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) + require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) + } + } + } +} + +func TestCTEMergeHint(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists tc") + tk.MustExec("drop table if exists te") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop table if exists t2") + tk.MustExec("drop table if exists t3") + tk.MustExec("drop table if exists t4") + tk.MustExec("drop view if exists v") + tk.MustExec("create table tc(a int)") + tk.MustExec("create table te(c int)") + tk.MustExec("create table t1(a int)") + tk.MustExec("create table t2(b int)") + tk.MustExec("create table t3(c int)") + tk.MustExec("create table t4(d int)") + tk.MustExec("insert into tc values (1), (5), (10), (15), (20), (30), (50);") + tk.MustExec("insert into te values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t1 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t2 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t3 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("insert into t4 values (1), (5), (10), (25), (40), (60), (100);") + tk.MustExec("analyze table tc;") + tk.MustExec("analyze table te;") + tk.MustExec("analyze table t1;") + tk.MustExec("analyze table t2;") + tk.MustExec("analyze table t3;") + tk.MustExec("analyze table t4;") + tk.MustExec("create definer='root'@'localhost' view v as select * from tc") + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + + comment := fmt.Sprintf("case:%v sql:%s", i, ts) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + testdata.OnRecord(func() { + if len(warnings) > 0 { + output[i].Warning = make([]string, len(warnings)) + for j, warning := range warnings { + output[i].Warning[j] = warning.Err.Error() + } + } + }) + if len(output[i].Warning) == 0 { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, len(output[i].Warning), comment) + for j, warning := range warnings { + require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) + require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) + } + } + } +} + +func TestForceInlineCTE(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`a` int(11));") + tk.MustExec("insert into t values (1), (5), (10), (15), (20), (30), (50);") + + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + }) + if strings.HasPrefix(ts, "set") { + tk.MustExec(ts) + continue + } + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + + comment := fmt.Sprintf("case:%v sql:%s", i, ts) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + testdata.OnRecord(func() { + if len(warnings) > 0 { + output[i].Warning = make([]string, len(warnings)) + for j, warning := range warnings { + output[i].Warning[j] = warning.Err.Error() + } + } + }) + if len(output[i].Warning) == 0 { + require.Len(t, warnings, 0) + } else { + require.Len(t, warnings, len(output[i].Warning), comment) + for j, warning := range warnings { + require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) + require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) + } + } + } +} + +func TestSingleConsumerCTE(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE `t` (`a` int(11));") + tk.MustExec("insert into t values (1), (5), (10), (15), (20), (30), (50);") + + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + }) + if strings.HasPrefix(ts, "set") { + tk.MustExec(ts) + continue + } + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestPushdownDistinctEnableAggPushDownDisable(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + vars := []string{ + fmt.Sprintf("set @@session.%s = 1", variable.TiDBOptDistinctAggPushDown), + "set session tidb_opt_agg_push_down = 0", + "set tidb_cost_model_version=2", + } + doTestPushdownDistinct(t, vars, input, output) +} + +func TestGroupConcatOrderby(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists test;") + tk.MustExec("create table test(id int, name int)") + tk.MustExec("insert into test values(1, 10);") + tk.MustExec("insert into test values(1, 20);") + tk.MustExec("insert into test values(1, 30);") + tk.MustExec("insert into test values(2, 20);") + tk.MustExec("insert into test values(3, 200);") + tk.MustExec("insert into test values(3, 500);") + + tk.MustExec("drop table if exists ptest;") + tk.MustExec("CREATE TABLE ptest (id int,name int) PARTITION BY RANGE ( id ) " + + "(PARTITION `p0` VALUES LESS THAN (2), PARTITION `p1` VALUES LESS THAN (11))") + tk.MustExec("insert into ptest select * from test;") + tk.MustExec(fmt.Sprintf("set session tidb_opt_distinct_agg_push_down = %v", 1)) + tk.MustExec(fmt.Sprintf("set session tidb_opt_agg_push_down = %v", 1)) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) + } +} + +func TestIndexHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + var input []string + var output []struct { + SQL string + Best string + HasWarn bool + Hints string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Best = core.ToString(p) + output[i].HasWarn = len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) > 0 + output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) + }) + require.Equal(t, output[i].Best, core.ToString(p), comment) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + if output[i].HasWarn { + require.Len(t, warnings, 1, comment) + } else { + require.Len(t, warnings, 0, comment) + } + hints := core.GenHintsFromPhysicalPlan(p) + + // test the new genHints code + flat := core.FlattenPhysicalPlan(p, false) + newHints := core.GenHintsFromFlatPlan(flat) + assertSameHints(t, hints, newHints) + + require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) + } +} + +func TestIndexMergeHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + + var input []string + var output []struct { + SQL string + Best string + HasWarn bool + Hints string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.Background() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, test := range input { + comment := fmt.Sprintf("case:%v sql:%s", i, test) + tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) + stmt, err := p.ParseOneStmt(test, "", "") + require.NoError(t, err, comment) + sctx := tk.Session() + err = executor.ResetContextOfStmt(sctx, stmt) + require.NoError(t, err) + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = test + output[i].Best = core.ToString(p) + output[i].HasWarn = len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) > 0 + output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) + }) + require.Equal(t, output[i].Best, core.ToString(p), comment) + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + if output[i].HasWarn { + require.Len(t, warnings, 1, comment) + } else { + require.Len(t, warnings, 0, comment) + } + hints := core.GenHintsFromPhysicalPlan(p) + + // test the new genHints code + flat := core.FlattenPhysicalPlan(p, false) + newHints := core.GenHintsFromFlatPlan(flat) + assertSameHints(t, hints, newHints) + + require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) + } +} + +func TestQueryBlockHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + + var input []string + var output []struct { + SQL string + Plan string + Hints string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + ctx := context.TODO() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, tt := range input { + comment := fmt.Sprintf("case:%v sql: %s", i, tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err, comment) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = core.ToString(p) + output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) + }) + require.Equal(t, output[i].Plan, core.ToString(p), comment) + hints := core.GenHintsFromPhysicalPlan(p) + + // test the new genHints code + flat := core.FlattenPhysicalPlan(p, false) + newHints := core.GenHintsFromFlatPlan(flat) + assertSameHints(t, hints, newHints) + + require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) + } +} + +func TestInlineProjection(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`drop table if exists test.t1, test.t2;`) + tk.MustExec(`create table test.t1(a bigint, b bigint, index idx_a(a), index idx_b(b));`) + tk.MustExec(`create table test.t2(a bigint, b bigint, index idx_a(a), index idx_b(b));`) + + var input []string + var output []struct { + SQL string + Plan string + Hints string + } + is := domain.GetDomain(tk.Session()).InfoSchema() + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + ctx := context.Background() + p := parser.New() + + for i, tt := range input { + comment := fmt.Sprintf("case:%v sql: %s", i, tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err, comment) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = core.ToString(p) + output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) + }) + require.Equal(t, output[i].Plan, core.ToString(p), comment) + hints := core.GenHintsFromPhysicalPlan(p) + + // test the new genHints code + flat := core.FlattenPhysicalPlan(p, false) + newHints := core.GenHintsFromFlatPlan(flat) + assertSameHints(t, hints, newHints) + + require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) + } +} + +func TestIndexJoinHint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`drop table if exists test.t1, test.t2, test.t;`) + tk.MustExec(`create table test.t1(a bigint, b bigint, index idx_a(a), index idx_b(b));`) + tk.MustExec(`create table test.t2(a bigint, b bigint, index idx_a(a), index idx_b(b));`) + tk.MustExec("CREATE TABLE `t` ( `a` bigint(20) NOT NULL, `b` tinyint(1) DEFAULT NULL, `c` datetime DEFAULT NULL, `d` int(10) unsigned DEFAULT NULL, `e` varchar(20) DEFAULT NULL, `f` double DEFAULT NULL, `g` decimal(30,5) DEFAULT NULL, `h` float DEFAULT NULL, `i` date DEFAULT NULL, `j` timestamp NULL DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), KEY `c` (`c`,`d`,`e`), KEY `f` (`f`), KEY `g` (`g`,`h`), KEY `g_2` (`g`), UNIQUE KEY `g_3` (`g`), KEY `i` (`i`) );") + + var input []string + var output []struct { + SQL string + Plan string + } + + is := domain.GetDomain(tk.Session()).InfoSchema() + p := parser.New() + ctx := context.Background() + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + comment := fmt.Sprintf("case:%v sql: %s", i, tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err, comment) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = core.ToString(p) + }) + require.Equal(t, output[i].Plan, core.ToString(p), comment) + } +} + +func TestNominalSort(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + var input []string + var output []struct { + SQL string + Plan []string + Result []string + } + tk.MustExec("create table t (a int, b int, index idx_a(a), index idx_b(b))") + tk.MustExec("insert into t values(1, 1)") + tk.MustExec("insert into t values(1, 2)") + tk.MustExec("insert into t values(2, 4)") + tk.MustExec("insert into t values(3, 5)") + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) + } +} + +func TestHintFromDiffDatabase(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`drop table if exists test.t1`) + tk.MustExec(`create table test.t1(a bigint, index idx_a(a));`) + tk.MustExec(`create table test.t2(a bigint, index idx_a(a));`) + tk.MustExec("drop database if exists test2") + tk.MustExec("create database test2") + tk.MustExec("use test2") + + var input []string + var output []struct { + SQL string + Plan string + } + is := domain.GetDomain(tk.Session()).InfoSchema() + p := parser.New() + ctx := context.Background() + + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + comment := fmt.Sprintf("case:%v sql: %s", i, tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) + require.NoError(t, err, comment) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = core.ToString(p) + }) + require.Equal(t, output[i].Plan, core.ToString(p), comment) + } +} + +func TestNthPlanHintWithExplain(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`drop table if exists test.tt`) + tk.MustExec(`create table test.tt (a int,b int, index(a), index(b));`) + tk.MustExec("insert into tt values (1, 1), (2, 2), (3, 4)") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + + var input []string + var output []struct { + SQL string + Plan []string + } + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } + + // This assertion makes sure a query with or without nth_plan() hint output exactly the same plan(including plan ID). + // The query below is the same as queries in the testdata except for nth_plan() hint. + // Currently, its output is the same as the second test case in the testdata, which is `output[1]`. If this doesn't + // hold in the future, you may need to modify this. + tk.MustQuery("explain format = 'brief' select * from test.tt where a=1 and b=1").Check(testkit.Rows(output[1].Plan...)) +} + +func TestEnumIndex(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(e enum('c','b','a',''), index idx(e))") + tk.MustExec("insert ignore into t values(0),(1),(2),(3),(4);") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestIssue27233(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `PK_S_MULTI_31` (\n `COL1` tinyint(45) NOT NULL,\n `COL2` tinyint(45) NOT NULL,\n PRIMARY KEY (`COL1`,`COL2`) /*T![clustered_index] NONCLUSTERED */\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("insert into PK_S_MULTI_31 values(122,100),(124,-22),(124,34),(127,103);") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestSelectionPartialPushDown(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int as (a+1) virtual)") + tk.MustExec("create table t2(a int, b int as (a+1) virtual, c int, key idx_a(a))") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIssue28316(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestSkewDistinctAgg(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t` (`a` int(11), `b` int(11), `c` int(11), `d` date)") + tk.MustExec("insert into t (a,b,c,d) value(1,4,5,'2019-06-01')") + tk.MustExec("insert into t (a,b,c,d) value(2,null,1,'2019-07-01')") + tk.MustExec("insert into t (a,b,c,d) value(3,4,5,'2019-08-01')") + tk.MustExec("insert into t (a,b,c,d) value(3,6,2,'2019-09-01')") + tk.MustExec("insert into t (a,b,c,d) value(10,4,null,'2020-06-01')") + tk.MustExec("insert into t (a,b,c,d) value(20,null,1,'2020-07-01')") + tk.MustExec("insert into t (a,b,c,d) value(30,4,5,'2020-08-01')") + tk.MustExec("insert into t (a,b,c,d) value(30,6,5,'2020-09-01')") + tk.MustQuery("select date_format(d,'%Y') as df, sum(a), count(b), count(distinct c) " + + "from t group by date_format(d,'%Y') order by df;").Check( + testkit.Rows("2019 9 3 3", "2020 90 3 2")) + tk.MustExec("set @@tidb_opt_skew_distinct_agg=1") + tk.MustQuery("select date_format(d,'%Y') as df, sum(a), count(b), count(distinct c) " + + "from t group by date_format(d,'%Y') order by df;").Check( + testkit.Rows("2019 9 3 3", "2020 90 3 2")) + tk.MustQuery("select count(distinct b), sum(c) from t group by a order by 1,2;").Check( + testkit.Rows("0 1", "0 1", "1 ", "1 5", "2 7", "2 10")) + tk.MustQuery("select count(distinct b) from t group by date_format(d,'%Y') order by 1;").Check( + testkit.Rows("2", "2")) + tk.MustQuery("select count(a), count(distinct b), max(b) from t group by date_format(d,'%Y') order by 1,2,3;").Check( + testkit.Rows("4 2 6", "4 2 6")) + tk.MustQuery("select count(a), count(distinct b), max(b) from t group by date_format(d,'%Y'),c order by 1,2,3;").Check( + testkit.Rows("1 0 ", "1 0 ", "1 1 4", "1 1 6", "2 1 4", "2 2 6")) + tk.MustQuery("select avg(distinct b), count(a), sum(b) from t group by date_format(d,'%Y'),c order by 1,2,3;").Check( + testkit.Rows(" 1 ", " 1 ", "4.0000 1 4", "4.0000 2 8", "5.0000 2 10", "6.0000 1 6")) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestHJBuildAndProbeHint(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1(a int primary key, b int not null)") + tk.MustExec("create table t2(a int primary key, b int not null)") + tk.MustExec("create table t3(a int primary key, b int not null)") + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + tk.MustExec("insert into t3 values(1,1),(2,1)") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} + +func TestHJBuildAndProbeHint4StaticPartitionTable(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + tk.MustExec("insert into t3 values(1,1),(2,1)") + tk.MustExec(`set @@tidb_partition_prune_mode="static"`) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestHJBuildAndProbeHint4DynamicPartitionTable(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + tk.MustExec("insert into t3 values(1,1),(2,1)") + tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + } +} + +func TestHJBuildAndProbeHint4TiFlash(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1(a int primary key, b int not null)") + tk.MustExec("create table t2(a int primary key, b int not null)") + tk.MustExec("create table t3(a int primary key, b int not null)") + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + tk.MustExec("insert into t3 values(1,1),(2,1)") + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tableName := tblInfo.Name.L + if tableName == "t1" || tableName == "t2" || tableName == "t3" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestMPPSinglePartitionType(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists employee") + tk.MustExec("create table employee(empid int, deptid int, salary decimal(10,2))") + tk.MustExec("set tidb_enforce_mpp=0") + + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "employee" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + }) + if strings.HasPrefix(ts, "set") { + tk.MustExec(ts) + continue + } + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) + }) + tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestNoDecorrelateHint(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Result []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int primary key, b int)") + tk.MustExec("create table t3(a int, b int)") + tk.MustExec("insert into t1 values(1,1),(2,2)") + tk.MustExec("insert into t2 values(1,1),(2,1)") + tk.MustExec("insert into t3 values(1,1),(2,1)") + + tk.MustExec("create table ta(id int, code int, name varchar(20), index idx_ta_id(id),index idx_ta_name(name), index idx_ta_code(code))") + tk.MustExec("create table tb(id int, code int, name varchar(20), index idx_tb_id(id),index idx_tb_name(name))") + tk.MustExec("create table tc(id int, code int, name varchar(20), index idx_tc_id(id),index idx_tc_name(name))") + tk.MustExec("create table td(id int, code int, name varchar(20), index idx_tc_id(id),index idx_tc_name(name))") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} + +func TestCountStarForTikv(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("create table t (a int(11) not null, b varchar(10) not null, c date not null, d char(1) not null, e bigint not null, f datetime not null, g bool not null, h bool )") + tk.MustExec("create table t_pick_row_id (a char(20) not null)") + + // tikv + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestCountStarForTiFlash(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("create table t (a int(11) not null, b varchar(10) not null, c date not null, d char(1) not null, e bigint not null, f datetime not null, g bool not null, h bool )") + tk.MustExec("create table t_pick_row_id (a char(20) not null)") + + // tiflash + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tableName := tblInfo.Name.L + if tableName == "t" || tableName == "t_pick_row_id" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestHashAggPushdownToTiFlashCompute(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists tbl_15;") + tk.MustExec(`create table tbl_15 (col_89 text (473) collate utf8mb4_bin , + col_90 timestamp default '1976-04-03' , + col_91 tinyint unsigned not null , + col_92 tinyint , + col_93 double not null , + col_94 datetime not null default '1970-06-08' , + col_95 datetime default '2028-02-13' , + col_96 int unsigned not null default 2532480521 , + col_97 char (168) default '') partition by hash (col_91) partitions 4;`) + + tk.MustExec("drop table if exists tbl_16;") + tk.MustExec(`create table tbl_16 (col_98 text (246) not null , + col_99 decimal (30 ,19) , + col_100 mediumint unsigned , + col_101 text (410) collate utf8mb4_bin , + col_102 date not null , + col_103 timestamp not null default '2003-08-27' , + col_104 text (391) not null , + col_105 date default '2010-10-24' , + col_106 text (9) not null,primary key (col_100, col_98(5), col_103), + unique key idx_23 (col_100, col_106 (3), col_101 (3))) partition by hash (col_100) partitions 2;`) + + config.UpdateGlobal(func(conf *config.Config) { + conf.DisaggregatedTiFlash = true + }) + defer config.UpdateGlobal(func(conf *config.Config) { + conf.DisaggregatedTiFlash = false + }) + + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tableName := tblInfo.Name.L + if tableName == "tbl_15" || tableName == "tbl_16" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_partition_prune_mode = 'static';") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + } +} + +func TestIndexMergeOrderPushDown(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("create table t (a int, b int, c int, index idx(a, c), index idx2(b, c))") + tk.MustExec("create table tcommon (a int, b int, c int, primary key(a, c), index idx2(b, c))") + tk.MustExec("create table thash(a int, b int, c int, index idx_ac(a, c), index idx_bc(b, c)) PARTITION BY HASH (`a`) PARTITIONS 4") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} + +func TestIndexMergeSinkLimit(t *testing.T) { + var ( + input []string + output []struct { + SQL string + Plan []string + Warning []string + } + ) + planSuiteData := GetPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec(" CREATE TABLE `t2` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL, KEY `a` (`a`), KEY `b` (`b`)) ") + tk.MustExec("insert into t2 values(1,2,1),(2,1,1),(3,3,1)") + tk.MustExec("create table t(a int, j json, index kj((cast(j as signed array))))") + tk.MustExec("insert into t values(1, '[1,2,3]')") + + for i, ts := range input { + testdata.OnRecord(func() { + output[i].SQL = ts + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery(ts).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} diff --git a/planner/core/casetest/physicalplantest/testdata/plan_suite_in.json b/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_in.json similarity index 100% rename from planner/core/casetest/physicalplantest/testdata/plan_suite_in.json rename to pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_in.json diff --git a/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json b/pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json similarity index 100% rename from planner/core/casetest/physicalplantest/testdata/plan_suite_out.json rename to pkg/planner/core/casetest/physicalplantest/testdata/plan_suite_out.json diff --git a/pkg/planner/core/casetest/plan_test.go b/pkg/planner/core/casetest/plan_test.go new file mode 100644 index 0000000000000..9b2c386a8641d --- /dev/null +++ b/pkg/planner/core/casetest/plan_test.go @@ -0,0 +1,234 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package casetest + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/stretchr/testify/require" +) + +func getPlanRows(planStr string) []string { + planStr = strings.Replace(planStr, "\t", " ", -1) + return strings.Split(planStr, "\n") +} + +func compareStringSlice(t *testing.T, ss1, ss2 []string) { + require.Equal(t, len(ss1), len(ss2)) + for i, s := range ss1 { + require.Equal(t, len(s), len(ss2[i])) + } +} + +func TestPreferRangeScan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect this ut: tidb_opt_prefer_range_scan + tk.MustExec("drop table if exists test;") + tk.MustExec("create table test(`id` int(10) NOT NULL AUTO_INCREMENT,`name` varchar(50) NOT NULL DEFAULT 'tidb',`age` int(11) NOT NULL,`addr` varchar(50) DEFAULT 'The ocean of stars',PRIMARY KEY (`id`),KEY `idx_age` (`age`))") + tk.MustExec("insert into test(age) values(5);") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") + tk.MustExec("analyze table test;") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + var input []string + var output []struct { + SQL string + Plan []string + } + planNormalizedSuiteData := GetPlanNormalizedSuiteData() + planNormalizedSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + if i == 0 { + tk.MustExec("set session tidb_opt_prefer_range_scan=0") + } else if i == 1 { + tk.MustExec("set session tidb_opt_prefer_range_scan=1") + } + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(tt) + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(core.Plan) + require.True(t, ok) + normalized, digest := core.NormalizePlan(p) + + // test the new normalization code + flat := core.FlattenPhysicalPlan(p, false) + newNormalized, newDigest := core.NormalizeFlatPlan(flat) + require.Equal(t, normalized, newNormalized) + require.Equal(t, digest, newDigest) + + normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) + normalizedPlanRows := getPlanRows(normalizedPlan) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = normalizedPlanRows + }) + compareStringSlice(t, normalizedPlanRows, output[i].Plan) + } +} + +func TestNormalizedPlan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode='static';") + tk.MustExec("drop table if exists t1,t2,t3,t4") + tk.MustExec("create table t1 (a int key,b int,c int, index (b));") + tk.MustExec("create table t2 (a int key,b int,c int, index (b));") + tk.MustExec("create table t3 (a int key,b int) partition by hash(a) partitions 2;") + tk.MustExec("create table t4 (a int, b int, index(a)) partition by range(a) (partition p0 values less than (10),partition p1 values less than MAXVALUE);") + tk.MustExec("set @@global.tidb_enable_foreign_key=1") + tk.MustExec("set @@foreign_key_checks=1") + tk.MustExec("create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));") + tk.MustExec("create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), " + + "foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE CASCADE, " + + "foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, " + + "foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);") + tk.MustExec("insert into t5 values (1,1,1), (2,2,2)") + var input []string + var output []struct { + SQL string + Plan []string + } + planNormalizedSuiteData := GetPlanNormalizedSuiteData() + planNormalizedSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(tt) + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(core.Plan) + require.True(t, ok) + normalized, digest := core.NormalizePlan(p) + + // test the new normalization code + flat := core.FlattenPhysicalPlan(p, false) + newNormalized, newDigest := core.NormalizeFlatPlan(flat) + require.Equal(t, normalized, newNormalized) + require.Equal(t, digest, newDigest) + // Test for GenHintsFromFlatPlan won't panic. + core.GenHintsFromFlatPlan(flat) + + normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) + normalizedPlanRows := getPlanRows(normalizedPlan) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = normalizedPlanRows + }) + compareStringSlice(t, normalizedPlanRows, output[i].Plan) + } +} + +func TestNormalizedPlanForDiffStore(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1 (a int, b int, c int, primary key(a))") + tk.MustExec("insert into t1 values(1,1,1), (2,2,2), (3,3,3)") + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + var input []string + var output []struct { + Digest string + Plan []string + } + planNormalizedSuiteData := GetPlanNormalizedSuiteData() + planNormalizedSuiteData.LoadTestCases(t, &input, &output) + lastDigest := "" + for i, tt := range input { + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(tt) + info := tk.Session().ShowProcess() + require.NotNil(t, info) + ep, ok := info.Plan.(*core.Explain) + require.True(t, ok) + normalized, digest := core.NormalizePlan(ep.TargetPlan) + + // test the new normalization code + flat := core.FlattenPhysicalPlan(ep.TargetPlan, false) + newNormalized, newPlanDigest := core.NormalizeFlatPlan(flat) + require.Equal(t, digest, newPlanDigest) + require.Equal(t, normalized, newNormalized) + + normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) + normalizedPlanRows := getPlanRows(normalizedPlan) + require.NoError(t, err) + testdata.OnRecord(func() { + output[i].Digest = digest.String() + output[i].Plan = normalizedPlanRows + }) + compareStringSlice(t, normalizedPlanRows, output[i].Plan) + require.NotEqual(t, digest.String(), lastDigest) + lastDigest = digest.String() + } +} + +func TestJSONPlanInExplain(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(id int, key(id))") + tk.MustExec("create table t2(id int, key(id))") + + var input []string + var output []struct { + SQL string + JSONPlan []*core.ExplainInfoForEncode + } + planSuiteData := GetJSONPlanSuiteData() + planSuiteData.LoadTestCases(t, &input, &output) + + for i, test := range input { + resJSON := tk.MustQuery(test).Rows() + var res []*core.ExplainInfoForEncode + require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), &res)) + for j, expect := range output[i].JSONPlan { + require.Equal(t, expect.ID, res[j].ID) + require.Equal(t, expect.EstRows, res[j].EstRows) + require.Equal(t, expect.ActRows, res[j].ActRows) + require.Equal(t, expect.TaskType, res[j].TaskType) + require.Equal(t, expect.AccessObject, res[j].AccessObject) + require.Equal(t, expect.OperatorInfo, res[j].OperatorInfo) + } + } +} diff --git a/pkg/planner/core/casetest/planstats/BUILD.bazel b/pkg/planner/core/casetest/planstats/BUILD.bazel new file mode 100644 index 0000000000000..094d33c59f3ac --- /dev/null +++ b/pkg/planner/core/casetest/planstats/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "planstats_test", + timeout = "short", + srcs = [ + "main_test.go", + "plan_stats_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 4, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/executor", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/statistics/handle", + "//pkg/table", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/planstats/main_test.go b/pkg/planner/core/casetest/planstats/main_test.go new file mode 100644 index 0000000000000..07659467e02b1 --- /dev/null +++ b/pkg/planner/core/casetest/planstats/main_test.go @@ -0,0 +1,54 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package planstats_test + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_stats_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetPlanStatsData() testdata.TestData { + return testDataMap["plan_stats_suite"] +} diff --git a/planner/core/casetest/planstats/plan_stats_test.go b/pkg/planner/core/casetest/planstats/plan_stats_test.go similarity index 96% rename from planner/core/casetest/planstats/plan_stats_test.go rename to pkg/planner/core/casetest/planstats/plan_stats_test.go index a19539fe3ccbf..f29b42d2bfbf4 100644 --- a/planner/core/casetest/planstats/plan_stats_test.go +++ b/pkg/planner/core/casetest/planstats/plan_stats_test.go @@ -22,20 +22,20 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/planner/core/casetest/planstats/testdata/plan_stats_suite_in.json b/pkg/planner/core/casetest/planstats/testdata/plan_stats_suite_in.json similarity index 100% rename from planner/core/casetest/planstats/testdata/plan_stats_suite_in.json rename to pkg/planner/core/casetest/planstats/testdata/plan_stats_suite_in.json diff --git a/planner/core/casetest/planstats/testdata/plan_stats_suite_out.json b/pkg/planner/core/casetest/planstats/testdata/plan_stats_suite_out.json similarity index 100% rename from planner/core/casetest/planstats/testdata/plan_stats_suite_out.json rename to pkg/planner/core/casetest/planstats/testdata/plan_stats_suite_out.json diff --git a/pkg/planner/core/casetest/pushdown/BUILD.bazel b/pkg/planner/core/casetest/pushdown/BUILD.bazel new file mode 100644 index 0000000000000..55145a587d7f8 --- /dev/null +++ b/pkg/planner/core/casetest/pushdown/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "pushdown_test", + timeout = "short", + srcs = [ + "main_test.go", + "push_down_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 6, + deps = [ + "//pkg/domain", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/pushdown/main_test.go b/pkg/planner/core/casetest/pushdown/main_test.go new file mode 100644 index 0000000000000..5f822937fd2e3 --- /dev/null +++ b/pkg/planner/core/casetest/pushdown/main_test.go @@ -0,0 +1,54 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package pushdown + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} diff --git a/planner/core/casetest/pushdown/push_down_test.go b/pkg/planner/core/casetest/pushdown/push_down_test.go similarity index 98% rename from planner/core/casetest/pushdown/push_down_test.go rename to pkg/planner/core/casetest/pushdown/push_down_test.go index 99a66dc81eb44..1f10c7129d47d 100644 --- a/planner/core/casetest/pushdown/push_down_test.go +++ b/pkg/planner/core/casetest/pushdown/push_down_test.go @@ -17,10 +17,10 @@ package pushdown import ( "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/planner/core/casetest/pushdown/testdata/integration_suite_in.json b/pkg/planner/core/casetest/pushdown/testdata/integration_suite_in.json similarity index 100% rename from planner/core/casetest/pushdown/testdata/integration_suite_in.json rename to pkg/planner/core/casetest/pushdown/testdata/integration_suite_in.json diff --git a/planner/core/casetest/pushdown/testdata/integration_suite_out.json b/pkg/planner/core/casetest/pushdown/testdata/integration_suite_out.json similarity index 100% rename from planner/core/casetest/pushdown/testdata/integration_suite_out.json rename to pkg/planner/core/casetest/pushdown/testdata/integration_suite_out.json diff --git a/pkg/planner/core/casetest/rule/BUILD.bazel b/pkg/planner/core/casetest/rule/BUILD.bazel new file mode 100644 index 0000000000000..eeef80bad415c --- /dev/null +++ b/pkg/planner/core/casetest/rule/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "rule_test", + timeout = "short", + srcs = [ + "main_test.go", + "rule_derive_topn_from_window_test.go", + "rule_inject_extra_projection_test.go", + "rule_join_reorder_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 5, + deps = [ + "//pkg/domain", + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core/internal", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/mock", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/rule/main_test.go b/pkg/planner/core/casetest/rule/main_test.go new file mode 100644 index 0000000000000..51a39a986dcbd --- /dev/null +++ b/pkg/planner/core/casetest/rule/main_test.go @@ -0,0 +1,57 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package rule + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "derive_topn_from_window") + testDataMap.LoadTestSuiteData("testdata", "join_reorder_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetDerivedTopNSuiteData() testdata.TestData { + return testDataMap["derive_topn_from_window"] +} + +func GetJoinReorderSuiteData() testdata.TestData { + return testDataMap["join_reorder_suite"] +} diff --git a/planner/core/casetest/rule/rule_derive_topn_from_window_test.go b/pkg/planner/core/casetest/rule/rule_derive_topn_from_window_test.go similarity index 90% rename from planner/core/casetest/rule/rule_derive_topn_from_window_test.go rename to pkg/planner/core/casetest/rule/rule_derive_topn_from_window_test.go index d5c5a1ec63069..b00a915790ab1 100644 --- a/planner/core/casetest/rule/rule_derive_topn_from_window_test.go +++ b/pkg/planner/core/casetest/rule/rule_derive_topn_from_window_test.go @@ -17,10 +17,10 @@ package rule import ( "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" ) type Input []string diff --git a/planner/core/casetest/rule/rule_inject_extra_projection_test.go b/pkg/planner/core/casetest/rule/rule_inject_extra_projection_test.go similarity index 87% rename from planner/core/casetest/rule/rule_inject_extra_projection_test.go rename to pkg/planner/core/casetest/rule/rule_inject_extra_projection_test.go index 69a8d634c6413..660a63f2ef2fc 100644 --- a/planner/core/casetest/rule/rule_inject_extra_projection_test.go +++ b/pkg/planner/core/casetest/rule/rule_inject_extra_projection_test.go @@ -17,13 +17,13 @@ package rule import ( "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/rule/rule_join_reorder_test.go b/pkg/planner/core/casetest/rule/rule_join_reorder_test.go new file mode 100644 index 0000000000000..ed1848df2be90 --- /dev/null +++ b/pkg/planner/core/casetest/rule/rule_join_reorder_test.go @@ -0,0 +1,114 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package rule + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func runJoinReorderTestData(t *testing.T, tk *testkit.TestKit, name string) { + var input []string + var output []struct { + SQL string + Plan []string + Warning []string + } + joinReorderSuiteData := GetJoinReorderSuiteData() + joinReorderSuiteData.LoadTestCasesByName(name, t, &input, &output) + require.Equal(t, len(input), len(output)) + for i := range input { + testdata.OnRecord(func() { + output[i].SQL = input[i] + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + input[i]).Rows()) + output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) + }) + tk.MustQuery("explain format = 'brief' " + input[i]).Check(testkit.Rows(output[i].Plan...)) + tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) + } +} + +// test the global/session variable tidb_opt_enable_hash_join being set to no +func TestOptEnableHashJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_opt_enable_hash_join=off") + tk.MustExec("create table t1(a int, b int, key(a));") + tk.MustExec("create table t2(a int, b int, key(a));") + tk.MustExec("create table t3(a int, b int, key(a));") + tk.MustExec("create table t4(a int, b int, key(a));") + runJoinReorderTestData(t, tk, "TestOptEnableHashJoin") +} + +func TestJoinOrderHint4TiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec("create table t(a int, b int, key(a));") + tk.MustExec("create table t1(a int, b int, key(a));") + tk.MustExec("create table t2(a int, b int, key(a));") + tk.MustExec("create table t3(a int, b int, key(a));") + tk.MustExec("create table t4(a int, b int, key(a));") + tk.MustExec("create table t5(a int, b int, key(a));") + tk.MustExec("create table t6(a int, b int, key(a));") + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + tableName := tblInfo.Name.L + if tableName == "t" || tableName == "t1" || tableName == "t2" || tableName == "t3" || tableName == "t4" || tableName == "t5" || tableName == "t6" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + runJoinReorderTestData(t, tk, "TestJoinOrderHint4TiFlash") +} + +func TestJoinOrderHint4DynamicPartitionTable(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) + tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) + tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) + tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) + tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) + + tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) + tk.MustExec("set @@tidb_enable_outer_join_reorder=true") + runJoinReorderTestData(t, tk, "TestJoinOrderHint4DynamicPartitionTable") +} diff --git a/planner/core/casetest/rule/testdata/derive_topn_from_window_in.json b/pkg/planner/core/casetest/rule/testdata/derive_topn_from_window_in.json similarity index 100% rename from planner/core/casetest/rule/testdata/derive_topn_from_window_in.json rename to pkg/planner/core/casetest/rule/testdata/derive_topn_from_window_in.json diff --git a/planner/core/casetest/rule/testdata/derive_topn_from_window_out.json b/pkg/planner/core/casetest/rule/testdata/derive_topn_from_window_out.json similarity index 100% rename from planner/core/casetest/rule/testdata/derive_topn_from_window_out.json rename to pkg/planner/core/casetest/rule/testdata/derive_topn_from_window_out.json diff --git a/planner/core/casetest/rule/testdata/join_reorder_suite_in.json b/pkg/planner/core/casetest/rule/testdata/join_reorder_suite_in.json similarity index 100% rename from planner/core/casetest/rule/testdata/join_reorder_suite_in.json rename to pkg/planner/core/casetest/rule/testdata/join_reorder_suite_in.json diff --git a/planner/core/casetest/rule/testdata/join_reorder_suite_out.json b/pkg/planner/core/casetest/rule/testdata/join_reorder_suite_out.json similarity index 100% rename from planner/core/casetest/rule/testdata/join_reorder_suite_out.json rename to pkg/planner/core/casetest/rule/testdata/join_reorder_suite_out.json diff --git a/pkg/planner/core/casetest/scalarsubquery/BUILD.bazel b/pkg/planner/core/casetest/scalarsubquery/BUILD.bazel new file mode 100644 index 0000000000000..81c7867e8d498 --- /dev/null +++ b/pkg/planner/core/casetest/scalarsubquery/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "scalarsubquery_test", + timeout = "short", + srcs = [ + "cases_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + deps = [ + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/casetest/scalarsubquery/cases_test.go b/pkg/planner/core/casetest/scalarsubquery/cases_test.go similarity index 96% rename from planner/core/casetest/scalarsubquery/cases_test.go rename to pkg/planner/core/casetest/scalarsubquery/cases_test.go index 5fd6a3fceeb79..1593a5d4411dc 100644 --- a/planner/core/casetest/scalarsubquery/cases_test.go +++ b/pkg/planner/core/casetest/scalarsubquery/cases_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/scalarsubquery/main_test.go b/pkg/planner/core/casetest/scalarsubquery/main_test.go new file mode 100644 index 0000000000000..04fd9d9e86f51 --- /dev/null +++ b/pkg/planner/core/casetest/scalarsubquery/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package scalarsubquery + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetPlanSuiteData() testdata.TestData { + return testDataMap["plan_suite"] +} diff --git a/planner/core/casetest/scalarsubquery/testdata/plan_suite_in.json b/pkg/planner/core/casetest/scalarsubquery/testdata/plan_suite_in.json similarity index 100% rename from planner/core/casetest/scalarsubquery/testdata/plan_suite_in.json rename to pkg/planner/core/casetest/scalarsubquery/testdata/plan_suite_in.json diff --git a/planner/core/casetest/scalarsubquery/testdata/plan_suite_out.json b/pkg/planner/core/casetest/scalarsubquery/testdata/plan_suite_out.json similarity index 100% rename from planner/core/casetest/scalarsubquery/testdata/plan_suite_out.json rename to pkg/planner/core/casetest/scalarsubquery/testdata/plan_suite_out.json diff --git a/pkg/planner/core/casetest/stats_test.go b/pkg/planner/core/casetest/stats_test.go new file mode 100644 index 0000000000000..cc7f4da935e11 --- /dev/null +++ b/pkg/planner/core/casetest/stats_test.go @@ -0,0 +1,153 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package casetest + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/stretchr/testify/require" +) + +func TestGroupNDVs(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int not null, b int not null, key(a,b))") + tk.MustExec("insert into t1 values(1,1),(1,2),(2,1),(2,2),(1,1)") + tk.MustExec("create table t2(a int not null, b int not null, key(a,b))") + tk.MustExec("insert into t2 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3),(1,1)") + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + + ctx := context.Background() + p := parser.New() + var input []string + var output []struct { + SQL string + AggInput string + JoinInput string + } + statsSuiteData := GetStatsSuiteData() + statsSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + comment := fmt.Sprintf("case:%v sql: %s", i, tt) + stmt, err := p.ParseOneStmt(tt, "", "") + require.NoError(t, err, comment) + ret := &core.PreprocessorReturn{} + err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(ret)) + require.NoError(t, err) + tk.Session().GetSessionVars().PlanColumnID.Store(0) + builder, _ := core.NewPlanBuilder().Init(tk.Session(), ret.InfoSchema, &hint.BlockHintProcessor{}) + p, err := builder.Build(ctx, stmt) + require.NoError(t, err, comment) + p, err = core.LogicalOptimizeTest(ctx, builder.GetOptFlag(), p.(core.LogicalPlan)) + require.NoError(t, err, comment) + lp := p.(core.LogicalPlan) + _, err = core.RecursiveDeriveStats4Test(lp) + require.NoError(t, err, comment) + var agg *core.LogicalAggregation + var join *core.LogicalJoin + stack := make([]core.LogicalPlan, 0, 2) + traversed := false + for !traversed { + switch v := lp.(type) { + case *core.LogicalAggregation: + agg = v + lp = lp.Children()[0] + case *core.LogicalJoin: + join = v + lp = v.Children()[0] + stack = append(stack, v.Children()[1]) + case *core.LogicalApply: + lp = lp.Children()[0] + stack = append(stack, v.Children()[1]) + case *core.LogicalUnionAll: + lp = lp.Children()[0] + for i := 1; i < len(v.Children()); i++ { + stack = append(stack, v.Children()[i]) + } + case *core.DataSource: + if len(stack) == 0 { + traversed = true + } else { + lp = stack[0] + stack = stack[1:] + } + default: + lp = lp.Children()[0] + } + } + aggInput := "" + joinInput := "" + if agg != nil { + s := core.GetStats4Test(agg.Children()[0]) + aggInput = property.ToString(s.GroupNDVs) + } + if join != nil { + l := core.GetStats4Test(join.Children()[0]) + r := core.GetStats4Test(join.Children()[1]) + joinInput = property.ToString(l.GroupNDVs) + ";" + property.ToString(r.GroupNDVs) + } + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].AggInput = aggInput + output[i].JoinInput = joinInput + }) + require.Equal(t, output[i].AggInput, aggInput, comment) + require.Equal(t, output[i].JoinInput, joinInput, comment) + } +} + +func TestNDVGroupCols(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int not null, b int not null, key(a,b))") + tk.MustExec("insert into t1 values(1,1),(1,2),(2,1),(2,2)") + tk.MustExec("create table t2(a int not null, b int not null, key(a,b))") + tk.MustExec("insert into t2 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)") + tk.MustExec("analyze table t1") + tk.MustExec("analyze table t2") + + // Default RPC encoding may cause statistics explain result differ and then the test unstable. + tk.MustExec("set @@tidb_enable_chunk_rpc = on") + + var input []string + var output []struct { + SQL string + Plan []string + } + statsSuiteData := GetStatsSuiteData() + statsSuiteData.LoadTestCases(t, &input, &output) + for i, tt := range input { + testdata.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) + }) + // The test point is the row count estimation for aggregations and joins. + tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) + } +} diff --git a/planner/core/casetest/testdata/integration_suite_in.json b/pkg/planner/core/casetest/testdata/integration_suite_in.json similarity index 100% rename from planner/core/casetest/testdata/integration_suite_in.json rename to pkg/planner/core/casetest/testdata/integration_suite_in.json diff --git a/planner/core/casetest/testdata/integration_suite_out.json b/pkg/planner/core/casetest/testdata/integration_suite_out.json similarity index 100% rename from planner/core/casetest/testdata/integration_suite_out.json rename to pkg/planner/core/casetest/testdata/integration_suite_out.json diff --git a/planner/core/casetest/testdata/json_plan_suite_in.json b/pkg/planner/core/casetest/testdata/json_plan_suite_in.json similarity index 100% rename from planner/core/casetest/testdata/json_plan_suite_in.json rename to pkg/planner/core/casetest/testdata/json_plan_suite_in.json diff --git a/planner/core/casetest/testdata/json_plan_suite_out.json b/pkg/planner/core/casetest/testdata/json_plan_suite_out.json similarity index 100% rename from planner/core/casetest/testdata/json_plan_suite_out.json rename to pkg/planner/core/casetest/testdata/json_plan_suite_out.json diff --git a/planner/core/casetest/testdata/plan_normalized_suite_in.json b/pkg/planner/core/casetest/testdata/plan_normalized_suite_in.json similarity index 100% rename from planner/core/casetest/testdata/plan_normalized_suite_in.json rename to pkg/planner/core/casetest/testdata/plan_normalized_suite_in.json diff --git a/planner/core/casetest/testdata/plan_normalized_suite_out.json b/pkg/planner/core/casetest/testdata/plan_normalized_suite_out.json similarity index 100% rename from planner/core/casetest/testdata/plan_normalized_suite_out.json rename to pkg/planner/core/casetest/testdata/plan_normalized_suite_out.json diff --git a/planner/core/casetest/testdata/stats_suite_in.json b/pkg/planner/core/casetest/testdata/stats_suite_in.json similarity index 100% rename from planner/core/casetest/testdata/stats_suite_in.json rename to pkg/planner/core/casetest/testdata/stats_suite_in.json diff --git a/planner/core/casetest/testdata/stats_suite_out.json b/pkg/planner/core/casetest/testdata/stats_suite_out.json similarity index 100% rename from planner/core/casetest/testdata/stats_suite_out.json rename to pkg/planner/core/casetest/testdata/stats_suite_out.json diff --git a/planner/core/casetest/tiflash_selection_late_materialization_test.go b/pkg/planner/core/casetest/tiflash_selection_late_materialization_test.go similarity index 92% rename from planner/core/casetest/tiflash_selection_late_materialization_test.go rename to pkg/planner/core/casetest/tiflash_selection_late_materialization_test.go index 58a7160de1d4b..9c789f71559e3 100644 --- a/planner/core/casetest/tiflash_selection_late_materialization_test.go +++ b/pkg/planner/core/casetest/tiflash_selection_late_materialization_test.go @@ -17,11 +17,11 @@ package casetest import ( "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/plancodec" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/casetest/windows/BUILD.bazel b/pkg/planner/core/casetest/windows/BUILD.bazel new file mode 100644 index 0000000000000..bdac1460f976b --- /dev/null +++ b/pkg/planner/core/casetest/windows/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "windows_test", + timeout = "short", + srcs = [ + "main_test.go", + "window_push_down_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + shard_count = 4, + deps = [ + "//pkg/domain", + "//pkg/planner/core/internal", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/casetest/windows/main_test.go b/pkg/planner/core/casetest/windows/main_test.go new file mode 100644 index 0000000000000..ca2f32e4b7f6f --- /dev/null +++ b/pkg/planner/core/casetest/windows/main_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package windows + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "window_push_down_suite") + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func getWindowPushDownSuiteData() testdata.TestData { + return testDataMap["window_push_down_suite"] +} diff --git a/planner/core/casetest/windows/testdata/window_push_down_suite_in.json b/pkg/planner/core/casetest/windows/testdata/window_push_down_suite_in.json similarity index 100% rename from planner/core/casetest/windows/testdata/window_push_down_suite_in.json rename to pkg/planner/core/casetest/windows/testdata/window_push_down_suite_in.json diff --git a/planner/core/casetest/windows/testdata/window_push_down_suite_out.json b/pkg/planner/core/casetest/windows/testdata/window_push_down_suite_out.json similarity index 100% rename from planner/core/casetest/windows/testdata/window_push_down_suite_out.json rename to pkg/planner/core/casetest/windows/testdata/window_push_down_suite_out.json diff --git a/planner/core/casetest/windows/window_push_down_test.go b/pkg/planner/core/casetest/windows/window_push_down_test.go similarity index 92% rename from planner/core/casetest/windows/window_push_down_test.go rename to pkg/planner/core/casetest/windows/window_push_down_test.go index 05f0a4d0a1652..633318cf0682b 100644 --- a/planner/core/casetest/windows/window_push_down_test.go +++ b/pkg/planner/core/casetest/windows/window_push_down_test.go @@ -19,10 +19,10 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) @@ -119,9 +119,9 @@ func TestIssue34765(t *testing.T) { internal.SetTiFlashReplica(t, dom, "test", "t1") internal.SetTiFlashReplica(t, dom, "test", "t2") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/core/CheckMPPWindowSchemaLength", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/CheckMPPWindowSchemaLength", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/core/CheckMPPWindowSchemaLength")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/CheckMPPWindowSchemaLength")) }() tk.MustExec("explain select count(*) from (select row_number() over (partition by c1 order by c2) num from (select * from t1 left join t2 on t1.c4 = t2.b2) tem2 ) tx where num = 1;") } diff --git a/pkg/planner/core/cbo_test.go b/pkg/planner/core/cbo_test.go new file mode 100644 index 0000000000000..3ede953e2d1d7 --- /dev/null +++ b/pkg/planner/core/cbo_test.go @@ -0,0 +1,235 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "context" + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestExplainCostTrace(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + tk.MustExec("insert into t values (1)") + + tk.MustExec("set tidb_cost_model_version=2") + tk.MustQuery("explain format='cost_trace' select * from t").Check(testkit.Rows( + `TableReader_5 10000.00 177906.67 ((scan(10000*logrowsize(32)*tikv_scan_factor(40.7))) + (net(10000*rowsize(16)*tidb_kv_net_factor(3.96))))/15.00 root data:TableFullScan_4`, + `└─TableFullScan_4 10000.00 2035000.00 scan(10000*logrowsize(32)*tikv_scan_factor(40.7)) cop[tikv] table:t keep order:false, stats:pseudo`)) + tk.MustQuery("explain analyze format='cost_trace' select * from t").CheckAt([]int{0, 1, 2, 3, 4}, [][]interface{}{ + {"TableReader_5", "10000.00", "177906.67", "((scan(10000*logrowsize(32)*tikv_scan_factor(40.7))) + (net(10000*rowsize(16)*tidb_kv_net_factor(3.96))))/15.00", "1"}, + {"└─TableFullScan_4", "10000.00", "2035000.00", "scan(10000*logrowsize(32)*tikv_scan_factor(40.7))", "1"}, + }) + + tk.MustExec("set tidb_cost_model_version=1") + tk.MustQuery("explain format='cost_trace' select * from t").Check(testkit.Rows( + // cost trace on model ver1 is not supported + `TableReader_5 10000.00 34418.00 N/A root data:TableFullScan_4`, + `└─TableFullScan_4 10000.00 435000.00 N/A cop[tikv] table:t keep order:false, stats:pseudo`, + )) + tk.MustQuery("explain analyze format='cost_trace' select * from t").CheckAt([]int{0, 1, 2, 3, 4}, [][]interface{}{ + {"TableReader_5", "10000.00", "34418.00", "N/A", "1"}, + {"└─TableFullScan_4", "10000.00", "435000.00", "N/A", "1"}, + }) +} + +func TestExplainAnalyze(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by + tk.MustExec("create table t1(a int, b int, c int, key idx(a, b))") + tk.MustExec("create table t2(a int, b int)") + tk.MustExec("insert into t1 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)") + tk.MustExec("insert into t2 values (2, 22), (3, 33), (5, 55), (233, 2), (333, 3), (3434, 5)") + tk.MustExec("analyze table t1, t2") + rs := tk.MustQuery("explain analyze select t1.a, t1.b, sum(t1.c) from t1 join t2 on t1.a = t2.b where t1.a > 1") + require.Len(t, rs.Rows(), 10) + for _, row := range rs.Rows() { + require.Len(t, row, 9) + execInfo := row[5].(string) + require.Contains(t, execInfo, "time") + require.Contains(t, execInfo, "loops") + if strings.Contains(row[0].(string), "Reader") || strings.Contains(row[0].(string), "IndexLookUp") { + require.Contains(t, execInfo, "cop_task") + } + } +} + +func constructInsertSQL(i, n int) string { + sql := "insert into t (a,b,c,e)values " + for j := 0; j < n; j++ { + sql += fmt.Sprintf("(%d, %d, '%d', %d)", i*n+j, i, i+j, i*n+j) + if j != n-1 { + sql += ", " + } + } + return sql +} + +func BenchmarkOptimize(b *testing.B) { + store := testkit.CreateMockStore(b) + + testKit := testkit.NewTestKit(b, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t (a int primary key, b int, c varchar(200), d datetime DEFAULT CURRENT_TIMESTAMP, e int, ts timestamp DEFAULT CURRENT_TIMESTAMP)") + testKit.MustExec("create index b on t (b)") + testKit.MustExec("create index d on t (d)") + testKit.MustExec("create index e on t (e)") + testKit.MustExec("create index b_c on t (b,c)") + testKit.MustExec("create index ts on t (ts)") + for i := 0; i < 100; i++ { + testKit.MustExec(constructInsertSQL(i, 100)) + } + testKit.MustExec("analyze table t") + tests := []struct { + sql string + best string + }{ + { + sql: "select count(*) from t group by e", + best: "IndexReader(Index(t.e)[[NULL,+inf]])->StreamAgg", + }, + { + sql: "select count(*) from t where e <= 10 group by e", + best: "IndexReader(Index(t.e)[[-inf,10]])->StreamAgg", + }, + { + sql: "select count(*) from t where e <= 50", + best: "IndexReader(Index(t.e)[[-inf,50]]->HashAgg)->HashAgg", + }, + { + sql: "select count(*) from t where c > '1' group by b", + best: "IndexReader(Index(t.b_c)[[NULL,+inf]]->Sel([gt(test.t.c, 1)]))->StreamAgg", + }, + { + sql: "select count(*) from t where e = 1 group by b", + best: "IndexLookUp(Index(t.e)[[1,1]], Table(t)->HashAgg)->HashAgg", + }, + { + sql: "select count(*) from t where e > 1 group by b", + best: "TableReader(Table(t)->Sel([gt(test.t.e, 1)])->HashAgg)->HashAgg", + }, + { + sql: "select count(e) from t where t.b <= 20", + best: "IndexLookUp(Index(t.b)[[-inf,20]], Table(t)->HashAgg)->HashAgg", + }, + { + sql: "select count(e) from t where t.b <= 30", + best: "IndexLookUp(Index(t.b)[[-inf,30]], Table(t)->HashAgg)->HashAgg", + }, + { + sql: "select count(e) from t where t.b <= 40", + best: "IndexLookUp(Index(t.b)[[-inf,40]], Table(t)->HashAgg)->HashAgg", + }, + { + sql: "select count(e) from t where t.b <= 50", + best: "TableReader(Table(t)->Sel([le(test.t.b, 50)])->HashAgg)->HashAgg", + }, + { + sql: "select * from t where t.b <= 40", + best: "IndexLookUp(Index(t.b)[[-inf,40]], Table(t))", + }, + { + sql: "select * from t where t.b <= 50", + best: "TableReader(Table(t)->Sel([le(test.t.b, 50)]))", + }, + // test panic + { + sql: "select * from t where 1 and t.b <= 50", + best: "TableReader(Table(t)->Sel([le(test.t.b, 50)]))", + }, + { + sql: "select * from t where t.b <= 100 order by t.a limit 1", + best: "TableReader(Table(t)->Sel([le(test.t.b, 100)])->Limit)->Limit", + }, + { + sql: "select * from t where t.b <= 1 order by t.a limit 10", + best: "IndexLookUp(Index(t.b)[[-inf,1]]->TopN([test.t.a],0,10), Table(t))->TopN([test.t.a],0,10)", + }, + { + sql: "select * from t use index(b) where b = 1 order by a", + best: "IndexLookUp(Index(t.b)[[1,1]], Table(t))->Sort", + }, + // test datetime + { + sql: "select * from t where d < cast('1991-09-05' as datetime)", + best: "IndexLookUp(Index(t.d)[[-inf,1991-09-05 00:00:00)], Table(t))", + }, + // test timestamp + { + sql: "select * from t where ts < '1991-09-05'", + best: "IndexLookUp(Index(t.ts)[[-inf,1991-09-05 00:00:00)], Table(t))", + }, + } + for _, tt := range tests { + ctx := testKit.Session() + stmts, err := session.Parse(ctx, tt.sql) + require.NoError(b, err) + require.Len(b, stmts, 1) + stmt := stmts[0] + ret := &core.PreprocessorReturn{} + err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) + require.NoError(b, err) + + b.Run(tt.sql, func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) + require.NoError(b, err) + } + b.ReportAllocs() + }) + } +} + +func TestIssue9805(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec(` + create table t1 ( + id bigint primary key, + a bigint not null, + b varchar(100) not null, + c varchar(10) not null, + d bigint as (a % 30) not null, + key (d, b, c) + ) + `) + tk.MustExec(` + create table t2 ( + id varchar(50) primary key, + a varchar(100) unique, + b datetime, + c varchar(45), + d int not null unique auto_increment + ) + `) + // Test when both tables are empty, EXPLAIN ANALYZE for IndexLookUp would not panic. + tk.MustQuery("explain analyze select /*+ TIDB_INLJ(t2) */ t1.id, t2.a from t1 join t2 on t1.a = t2.d where t1.b = 't2' and t1.d = 4") +} diff --git a/planner/core/collect_column_stats_usage.go b/pkg/planner/core/collect_column_stats_usage.go similarity index 99% rename from planner/core/collect_column_stats_usage.go rename to pkg/planner/core/collect_column_stats_usage.go index 5cb184136d875..8bb4ea472b054 100644 --- a/planner/core/collect_column_stats_usage.go +++ b/pkg/planner/core/collect_column_stats_usage.go @@ -15,8 +15,8 @@ package core import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" ) const ( diff --git a/planner/core/collect_column_stats_usage_test.go b/pkg/planner/core/collect_column_stats_usage_test.go similarity index 97% rename from planner/core/collect_column_stats_usage_test.go rename to pkg/planner/core/collect_column_stats_usage_test.go index 82844ce4def83..a9a3668fa79ad 100644 --- a/planner/core/collect_column_stats_usage_test.go +++ b/pkg/planner/core/collect_column_stats_usage_test.go @@ -21,9 +21,9 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) @@ -274,8 +274,8 @@ func TestCollectPredicateColumns(t *testing.T) { } func TestCollectHistNeededColumns(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") tests := []struct { pruneMode string sql string diff --git a/planner/core/common_plans.go b/pkg/planner/core/common_plans.go similarity index 98% rename from planner/core/common_plans.go rename to pkg/planner/core/common_plans.go index 92194796c0cc2..648b5c373b2d7 100644 --- a/planner/core/common_plans.go +++ b/pkg/planner/core/common_plans.go @@ -22,24 +22,24 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/texttree" "github.com/pingcap/tipb/go-tipb" ) diff --git a/planner/core/common_plans_test.go b/pkg/planner/core/common_plans_test.go similarity index 97% rename from planner/core/common_plans_test.go rename to pkg/planner/core/common_plans_test.go index 309adfd4aef5a..cec90eef77648 100644 --- a/planner/core/common_plans_test.go +++ b/pkg/planner/core/common_plans_test.go @@ -17,8 +17,8 @@ package core import ( "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/debugtrace.go b/pkg/planner/core/debugtrace.go new file mode 100644 index 0000000000000..249d811204ea5 --- /dev/null +++ b/pkg/planner/core/debugtrace.go @@ -0,0 +1,259 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package core + +import ( + "strconv" + "strings" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/util/hint" +) + +/* + Below is debug trace for the received command from the client. + It records the input to the optimizer at the very beginning of query optimization. +*/ + +type receivedCmdInfo struct { + Command string + ExecutedASTText string + ExecuteStmtInfo *executeInfo +} + +type executeInfo struct { + PreparedSQL string + BinaryParamsInfo []binaryParamInfo + UseCursor bool +} + +type binaryParamInfo struct { + Type string + Value string +} + +func (info *binaryParamInfo) MarshalJSON() ([]byte, error) { + type binaryParamInfoForMarshal binaryParamInfo + infoForMarshal := new(binaryParamInfoForMarshal) + quote := `"` + // We only need the escape functionality of strconv.Quote, the quoting is not needed, + // so we trim the \" prefix and suffix here. + infoForMarshal.Type = strings.TrimSuffix( + strings.TrimPrefix( + strconv.Quote(info.Type), + quote), + quote) + infoForMarshal.Value = strings.TrimSuffix( + strings.TrimPrefix( + strconv.Quote(info.Value), + quote), + quote) + return debugtrace.EncodeJSONCommon(infoForMarshal) +} + +// DebugTraceReceivedCommand records the received command from the client to the debug trace. +func DebugTraceReceivedCommand(s sessionctx.Context, cmd byte, stmtNode ast.StmtNode) { + sessionVars := s.GetSessionVars() + trace := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := new(receivedCmdInfo) + trace.AppendStepWithNameToCurrentContext(traceInfo, "Received Command") + traceInfo.Command = mysql.Command2Str[cmd] + traceInfo.ExecutedASTText = stmtNode.Text() + + // Collect information for execute stmt, and record it in executeInfo. + var binaryParams []expression.Expression + var planCacheStmt *PlanCacheStmt + if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { + if execStmt.PrepStmt != nil { + planCacheStmt, _ = execStmt.PrepStmt.(*PlanCacheStmt) + } + if execStmt.BinaryArgs != nil { + binaryParams, _ = execStmt.BinaryArgs.([]expression.Expression) + } + } + useCursor := mysql.HasCursorExistsFlag(sessionVars.Status) + // If none of them needs record, we don't need a executeInfo. + if binaryParams == nil && planCacheStmt == nil && !useCursor { + return + } + execInfo := &executeInfo{} + traceInfo.ExecuteStmtInfo = execInfo + execInfo.UseCursor = useCursor + if planCacheStmt != nil { + execInfo.PreparedSQL = planCacheStmt.StmtText + } + if len(binaryParams) > 0 { + execInfo.BinaryParamsInfo = make([]binaryParamInfo, len(binaryParams)) + for i, param := range binaryParams { + execInfo.BinaryParamsInfo[i].Type = param.GetType().String() + execInfo.BinaryParamsInfo[i].Value = param.String() + } + } +} + +/* + Below is debug trace for the hint that matches the current query. +*/ + +type bindingHint struct { + Hint *hint.HintsSet + trying bool +} + +func (b *bindingHint) MarshalJSON() ([]byte, error) { + tmp := make(map[string]string, 1) + hintStr, err := b.Hint.Restore() + if err != nil { + return debugtrace.EncodeJSONCommon(err) + } + if b.trying { + tmp["Trying Hint"] = hintStr + } else { + tmp["Best Hint"] = hintStr + } + return debugtrace.EncodeJSONCommon(tmp) +} + +// DebugTraceTryBinding records the hint that might be chosen to the debug trace. +func DebugTraceTryBinding(s sessionctx.Context, binding *hint.HintsSet) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &bindingHint{ + Hint: binding, + trying: true, + } + root.AppendStepToCurrentContext(traceInfo) +} + +// DebugTraceBestBinding records the chosen hint to the debug trace. +func DebugTraceBestBinding(s sessionctx.Context, binding *hint.HintsSet) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &bindingHint{ + Hint: binding, + trying: false, + } + root.AppendStepToCurrentContext(traceInfo) +} + +/* + Below is debug trace for getStatsTable(). + Part of the logic for collecting information is in statistics/debug_trace.go. +*/ + +type getStatsTblInfo struct { + TableName string + TblInfoID int64 + InputPhysicalID int64 + HandleIsNil bool + UsePartitionStats bool + CountIsZero bool + Uninitialized bool + Outdated bool + StatsTblInfo *statistics.StatsTblTraceInfo +} + +func debugTraceGetStatsTbl( + s sessionctx.Context, + tblInfo *model.TableInfo, + pid int64, + handleIsNil, + usePartitionStats, + countIsZero, + uninitialized, + outdated bool, + statsTbl *statistics.Table, +) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &getStatsTblInfo{ + TableName: tblInfo.Name.O, + TblInfoID: tblInfo.ID, + InputPhysicalID: pid, + HandleIsNil: handleIsNil, + UsePartitionStats: usePartitionStats, + CountIsZero: countIsZero, + Uninitialized: uninitialized, + Outdated: outdated, + StatsTblInfo: statistics.TraceStatsTbl(statsTbl), + } + failpoint.Inject("DebugTraceStableStatsTbl", func(val failpoint.Value) { + if val.(bool) { + stabilizeGetStatsTblInfo(traceInfo) + } + }) + root.AppendStepToCurrentContext(traceInfo) +} + +// Only for test. +func stabilizeGetStatsTblInfo(info *getStatsTblInfo) { + info.TblInfoID = 100 + info.InputPhysicalID = 100 + tbl := info.StatsTblInfo + if tbl == nil { + return + } + tbl.PhysicalID = 100 + tbl.Version = 440930000000000000 + for _, col := range tbl.Columns { + col.LastUpdateVersion = 440930000000000000 + } + for _, idx := range tbl.Indexes { + idx.LastUpdateVersion = 440930000000000000 + } +} + +/* + Below is debug trace for AccessPath. +*/ + +type accessPathForDebugTrace struct { + IndexName string `json:",omitempty"` + AccessConditions []string + IndexFilters []string + TableFilters []string + PartialPaths []accessPathForDebugTrace `json:",omitempty"` + CountAfterAccess float64 + CountAfterIndex float64 +} + +func convertAccessPathForDebugTrace(path *util.AccessPath, out *accessPathForDebugTrace) { + if path.Index != nil { + out.IndexName = path.Index.Name.O + } + out.AccessConditions = expression.ExprsToStringsForDisplay(path.AccessConds) + out.IndexFilters = expression.ExprsToStringsForDisplay(path.IndexFilters) + out.TableFilters = expression.ExprsToStringsForDisplay(path.TableFilters) + out.CountAfterAccess = path.CountAfterAccess + out.CountAfterIndex = path.CountAfterIndex + out.PartialPaths = make([]accessPathForDebugTrace, len(path.PartialIndexPaths)) + for i, partialPath := range path.PartialIndexPaths { + convertAccessPathForDebugTrace(partialPath, &out.PartialPaths[i]) + } +} + +func debugTraceAccessPaths(s sessionctx.Context, paths []*util.AccessPath) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := make([]accessPathForDebugTrace, len(paths)) + for i, partialPath := range paths { + convertAccessPathForDebugTrace(partialPath, &traceInfo[i]) + } + root.AppendStepWithNameToCurrentContext(traceInfo, "Access paths") +} diff --git a/planner/core/encode.go b/pkg/planner/core/encode.go similarity index 98% rename from planner/core/encode.go rename to pkg/planner/core/encode.go index 9e118ea22bfc2..dc7ed1fc52a1a 100644 --- a/planner/core/encode.go +++ b/pkg/planner/core/encode.go @@ -22,9 +22,9 @@ import ( "sync" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/plancodec" ) // EncodeFlatPlan encodes a FlatPhysicalPlan with compression. diff --git a/pkg/planner/core/enforce_mpp_test.go b/pkg/planner/core/enforce_mpp_test.go new file mode 100644 index 0000000000000..f161f7b7bd7b0 --- /dev/null +++ b/pkg/planner/core/enforce_mpp_test.go @@ -0,0 +1,62 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestRowSizeInMPP(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a varchar(10), b varchar(20), c varchar(256))") + tk.MustExec("insert into t values (space(10), space(20), space(256))") + tk.MustExec("analyze table t") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec(`set @@tidb_opt_tiflash_concurrency_factor=1`) + tk.MustExec(`set @@tidb_allow_mpp=1`) + var costs [3]float64 + for i, col := range []string{"a", "b", "c"} { + rs := tk.MustQuery(fmt.Sprintf(`explain format='verbose' select /*+ read_from_storage(tiflash[t]) */ %v from t`, col)).Rows() + cost, err := strconv.ParseFloat(rs[0][2].(string), 64) + require.NoError(t, err) + costs[i] = cost + } + require.True(t, costs[0] < costs[1] && costs[1] < costs[2]) // rowSize can affect the final cost +} diff --git a/pkg/planner/core/errors.go b/pkg/planner/core/errors.go new file mode 100644 index 0000000000000..ba00a626e7a7c --- /dev/null +++ b/pkg/planner/core/errors.go @@ -0,0 +1,122 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package core + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// error definitions. +var ( + ErrUnsupportedType = dbterror.ClassOptimizer.NewStd(mysql.ErrUnsupportedType) + ErrAnalyzeMissIndex = dbterror.ClassOptimizer.NewStd(mysql.ErrAnalyzeMissIndex) + ErrAnalyzeMissColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrAnalyzeMissColumn) + ErrWrongParamCount = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongParamCount) + ErrSchemaChanged = dbterror.ClassOptimizer.NewStd(mysql.ErrSchemaChanged) + ErrTablenameNotAllowedHere = dbterror.ClassOptimizer.NewStd(mysql.ErrTablenameNotAllowedHere) + ErrNotSupportedYet = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedYet) + ErrWrongUsage = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongUsage) + ErrUnknown = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknown) + ErrUnknownTable = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknownTable) + ErrNoSuchTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNoSuchTable) + ErrViewRecursive = dbterror.ClassOptimizer.NewStd(mysql.ErrViewRecursive) + ErrWrongArguments = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongArguments) + ErrWrongNumberOfColumnsInSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongNumberOfColumnsInSelect) + ErrBadGeneratedColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrBadGeneratedColumn) + ErrFieldNotInGroupBy = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldNotInGroupBy) + ErrAggregateOrderNonAggQuery = dbterror.ClassOptimizer.NewStd(mysql.ErrAggregateOrderNonAggQuery) + ErrFieldInOrderNotSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldInOrderNotSelect) + ErrAggregateInOrderNotSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrAggregateInOrderNotSelect) + ErrBadTable = dbterror.ClassOptimizer.NewStd(mysql.ErrBadTable) + ErrKeyDoesNotExist = dbterror.ClassOptimizer.NewStd(mysql.ErrKeyDoesNotExist) + ErrOperandColumns = dbterror.ClassOptimizer.NewStd(mysql.ErrOperandColumns) + ErrInvalidGroupFuncUse = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidGroupFuncUse) + ErrIllegalReference = dbterror.ClassOptimizer.NewStd(mysql.ErrIllegalReference) + ErrNoDB = dbterror.ClassOptimizer.NewStd(mysql.ErrNoDB) + ErrUnknownExplainFormat = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknownExplainFormat) + ErrWrongGroupField = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongGroupField) + ErrDupFieldName = dbterror.ClassOptimizer.NewStd(mysql.ErrDupFieldName) + ErrNonUpdatableTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonUpdatableTable) + ErrMultiUpdateKeyConflict = dbterror.ClassOptimizer.NewStd(mysql.ErrMultiUpdateKeyConflict) + ErrInternal = dbterror.ClassOptimizer.NewStd(mysql.ErrInternal) + ErrNonUniqTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonuniqTable) + ErrWindowInvalidWindowFuncUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowInvalidWindowFuncUse) + ErrWindowInvalidWindowFuncAliasUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowInvalidWindowFuncAliasUse) + ErrWindowNoSuchWindow = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoSuchWindow) + ErrWindowCircularityInWindowGraph = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowCircularityInWindowGraph) + ErrWindowNoChildPartitioning = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoChildPartitioning) + ErrWindowNoInherentFrame = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoInherentFrame) + ErrWindowNoRedefineOrderBy = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoRedefineOrderBy) + ErrWindowDuplicateName = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowDuplicateName) + ErrPartitionClauseOnNonpartitioned = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionClauseOnNonpartitioned) + ErrWindowFrameStartIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameStartIllegal) + ErrWindowFrameEndIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameEndIllegal) + ErrWindowFrameIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameIllegal) + ErrWindowRangeFrameOrderType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameOrderType) + ErrWindowRangeFrameTemporalType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameTemporalType) + ErrWindowRangeFrameNumericType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameNumericType) + ErrWindowRangeBoundNotConstant = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeBoundNotConstant) + ErrWindowRowsIntervalUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRowsIntervalUse) + ErrWindowFunctionIgnoresFrame = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFunctionIgnoresFrame) + ErrInvalidNumberOfArgs = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidNumberOfArgs) + ErrFieldInGroupingNotGroupBy = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldInGroupingNotGroupBy) + ErrUnsupportedOnGeneratedColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrUnsupportedOnGeneratedColumn) + ErrPrivilegeCheckFail = dbterror.ClassOptimizer.NewStd(mysql.ErrPrivilegeCheckFail) + ErrInvalidWildCard = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidWildCard) + ErrMixOfGroupFuncAndFields = dbterror.ClassOptimizer.NewStd(mysql.ErrMixOfGroupFuncAndFieldsIncompatible) + errTooBigPrecision = dbterror.ClassExpression.NewStd(mysql.ErrTooBigPrecision) + ErrDBaccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrDBaccessDenied) + ErrTableaccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrTableaccessDenied) + ErrSpecificAccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrSpecificAccessDenied) + ErrViewNoExplain = dbterror.ClassOptimizer.NewStd(mysql.ErrViewNoExplain) + ErrWrongValueCountOnRow = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongValueCountOnRow) + ErrViewInvalid = dbterror.ClassOptimizer.NewStd(mysql.ErrViewInvalid) + ErrNoSuchThread = dbterror.ClassOptimizer.NewStd(mysql.ErrNoSuchThread) + ErrUnknownColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrBadField) + ErrCartesianProductUnsupported = dbterror.ClassOptimizer.NewStd(mysql.ErrCartesianProductUnsupported) + ErrStmtNotFound = dbterror.ClassOptimizer.NewStd(mysql.ErrPreparedStmtNotFound) + ErrAmbiguous = dbterror.ClassOptimizer.NewStd(mysql.ErrNonUniq) + ErrUnresolvedHintName = dbterror.ClassOptimizer.NewStd(mysql.ErrUnresolvedHintName) + ErrNotHintUpdatable = dbterror.ClassOptimizer.NewStd(mysql.ErrNotHintUpdatable) + ErrWarnConflictingHint = dbterror.ClassOptimizer.NewStd(mysql.ErrWarnConflictingHint) + ErrCTERecursiveRequiresUnion = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveRequiresUnion) + ErrCTERecursiveRequiresNonRecursiveFirst = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveRequiresNonRecursiveFirst) + ErrCTERecursiveForbidsAggregation = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveForbidsAggregation) + ErrCTERecursiveForbiddenJoinOrder = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveForbiddenJoinOrder) + ErrInvalidRequiresSingleReference = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidRequiresSingleReference) + ErrSQLInReadOnlyMode = dbterror.ClassOptimizer.NewStd(mysql.ErrReadOnlyMode) + // Since we cannot know if user logged in with a password, use message of ErrAccessDeniedNoPassword instead + ErrAccessDenied = dbterror.ClassOptimizer.NewStdErr(mysql.ErrAccessDenied, mysql.MySQLErrName[mysql.ErrAccessDeniedNoPassword]) + ErrBadNull = dbterror.ClassOptimizer.NewStd(mysql.ErrBadNull) + ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) + ErrAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) + ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable) + ErrOptOnCacheTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnCacheTable) + ErrDropTableOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrDropTableOnTemporaryTable) + // ErrPartitionNoTemporary returns when partition at temporary mode + ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary) + ErrViewSelectTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrViewSelectTmptable) + ErrSubqueryMoreThan1Row = dbterror.ClassOptimizer.NewStd(mysql.ErrSubqueryNo1Row) + ErrKeyPart0 = dbterror.ClassOptimizer.NewStd(mysql.ErrKeyPart0) + ErrGettingNoopVariable = dbterror.ClassOptimizer.NewStd(mysql.ErrGettingNoopVariable) + + ErrPrepareMulti = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareMulti) + ErrUnsupportedPs = dbterror.ClassExecutor.NewStd(mysql.ErrUnsupportedPs) + ErrPsManyParam = dbterror.ClassExecutor.NewStd(mysql.ErrPsManyParam) + ErrPrepareDDL = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareDDL) + ErrRowIsReferenced2 = dbterror.ClassOptimizer.NewStd(mysql.ErrRowIsReferenced2) + ErrNoReferencedRow2 = dbterror.ClassOptimizer.NewStd(mysql.ErrNoReferencedRow2) +) diff --git a/pkg/planner/core/errors_test.go b/pkg/planner/core/errors_test.go new file mode 100644 index 0000000000000..5323a49bee1d1 --- /dev/null +++ b/pkg/planner/core/errors_test.go @@ -0,0 +1,91 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package core + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/require" +) + +func TestError(t *testing.T) { + kvErrs := []*terror.Error{ + ErrUnsupportedType, + ErrAnalyzeMissIndex, + ErrAnalyzeMissColumn, + ErrWrongParamCount, + ErrSchemaChanged, + ErrTablenameNotAllowedHere, + ErrNotSupportedYet, + ErrWrongUsage, + ErrUnknownTable, + ErrWrongArguments, + ErrWrongNumberOfColumnsInSelect, + ErrBadGeneratedColumn, + ErrFieldNotInGroupBy, + ErrBadTable, + ErrKeyDoesNotExist, + ErrOperandColumns, + ErrInvalidGroupFuncUse, + ErrIllegalReference, + ErrNoDB, + ErrUnknownExplainFormat, + ErrWrongGroupField, + ErrDupFieldName, + ErrNonUpdatableTable, + ErrInternal, + ErrNonUniqTable, + ErrWindowInvalidWindowFuncUse, + ErrWindowInvalidWindowFuncAliasUse, + ErrWindowNoSuchWindow, + ErrWindowCircularityInWindowGraph, + ErrWindowNoChildPartitioning, + ErrWindowNoInherentFrame, + ErrWindowNoRedefineOrderBy, + ErrWindowDuplicateName, + ErrPartitionClauseOnNonpartitioned, + ErrWindowFrameStartIllegal, + ErrWindowFrameEndIllegal, + ErrWindowFrameIllegal, + ErrWindowRangeFrameOrderType, + ErrWindowRangeFrameTemporalType, + ErrWindowRangeFrameNumericType, + ErrWindowRangeBoundNotConstant, + ErrWindowRowsIntervalUse, + ErrWindowFunctionIgnoresFrame, + ErrUnsupportedOnGeneratedColumn, + ErrPrivilegeCheckFail, + ErrInvalidWildCard, + ErrMixOfGroupFuncAndFields, + ErrDBaccessDenied, + ErrTableaccessDenied, + ErrSpecificAccessDenied, + ErrViewNoExplain, + ErrWrongValueCountOnRow, + ErrViewInvalid, + ErrNoSuchThread, + ErrUnknownColumn, + ErrCartesianProductUnsupported, + ErrStmtNotFound, + ErrAmbiguous, + ErrKeyPart0, + } + for _, err := range kvErrs { + code := terror.ToSQLError(err).Code + require.Truef(t, code != mysql.ErrUnknown && code == uint16(err.Code()), "err: %v", err) + } +} diff --git a/planner/core/exhaust_physical_plans.go b/pkg/planner/core/exhaust_physical_plans.go similarity index 99% rename from planner/core/exhaust_physical_plans.go rename to pkg/planner/core/exhaust_physical_plans.go index d690c8539a891..718fd03864549 100644 --- a/planner/core/exhaust_physical_plans.go +++ b/pkg/planner/core/exhaust_physical_plans.go @@ -24,27 +24,27 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" "go.uber.org/atomic" "go.uber.org/zap" diff --git a/planner/core/exhaust_physical_plans_test.go b/pkg/planner/core/exhaust_physical_plans_test.go similarity index 97% rename from planner/core/exhaust_physical_plans_test.go rename to pkg/planner/core/exhaust_physical_plans_test.go index 06a5ea12a56fe..f610b5c0baa8e 100644 --- a/planner/core/exhaust_physical_plans_test.go +++ b/pkg/planner/core/exhaust_physical_plans_test.go @@ -18,18 +18,18 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/explain.go b/pkg/planner/core/explain.go new file mode 100644 index 0000000000000..1c3166b1cb71c --- /dev/null +++ b/pkg/planner/core/explain.go @@ -0,0 +1,1112 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core + +import ( + "bytes" + "fmt" + "strconv" + "strings" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +// ExplainInfo implements Plan interface. +func (p *PhysicalLock) ExplainInfo() string { + var str strings.Builder + str.WriteString(p.Lock.LockType.String()) + str.WriteString(" ") + str.WriteString(strconv.FormatUint(p.Lock.WaitSec, 10)) + return str.String() +} + +// ExplainID overrides the ExplainID in order to match different range. +func (p *PhysicalIndexScan) ExplainID() fmt.Stringer { + return stringutil.MemoizeStr(func() string { + if p.SCtx() != nil && p.SCtx().GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { + return p.TP() + } + return p.TP() + "_" + strconv.Itoa(p.ID()) + }) +} + +// TP overrides the TP in order to match different range. +func (p *PhysicalIndexScan) TP() string { + if p.isFullScan() { + return plancodec.TypeIndexFullScan + } + return plancodec.TypeIndexRangeScan +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexScan) ExplainInfo() string { + return p.AccessObject().String() + ", " + p.OperatorInfo(false) +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalIndexScan) ExplainNormalizedInfo() string { + return p.AccessObject().NormalizedString() + ", " + p.OperatorInfo(true) +} + +// OperatorInfo implements dataAccesser interface. +func (p *PhysicalIndexScan) OperatorInfo(normalized bool) string { + var buffer strings.Builder + if len(p.rangeInfo) > 0 { + if !normalized { + buffer.WriteString("range: decided by ") + buffer.WriteString(p.rangeInfo) + buffer.WriteString(", ") + } + } else if p.haveCorCol() { + if normalized { + buffer.WriteString("range: decided by ") + buffer.Write(expression.SortedExplainNormalizedExpressionList(p.AccessCondition)) + buffer.WriteString(", ") + } else { + buffer.WriteString("range: decided by [") + for i, expr := range p.AccessCondition { + if i != 0 { + buffer.WriteString(" ") + } + buffer.WriteString(expr.String()) + } + buffer.WriteString("], ") + } + } else if len(p.Ranges) > 0 { + if normalized { + buffer.WriteString("range:[?,?], ") + } else if !p.isFullScan() { + buffer.WriteString("range:") + for _, idxRange := range p.Ranges { + buffer.WriteString(idxRange.String()) + buffer.WriteString(", ") + } + } + } + buffer.WriteString("keep order:") + buffer.WriteString(strconv.FormatBool(p.KeepOrder)) + if p.Desc { + buffer.WriteString(", desc") + } + if !normalized { + if p.usedStatsInfo != nil { + str := p.usedStatsInfo.FormatForExplain() + if len(str) > 0 { + buffer.WriteString(", ") + buffer.WriteString(str) + } + } else if p.StatsInfo().StatsVersion == statistics.PseudoVersion { + // This branch is not needed in fact, we add this to prevent test result changes under planner/cascades/ + buffer.WriteString(", stats:pseudo") + } + } + return buffer.String() +} + +func (p *PhysicalIndexScan) haveCorCol() bool { + for _, cond := range p.AccessCondition { + if len(expression.ExtractCorColumns(cond)) > 0 { + return true + } + } + return false +} + +func (p *PhysicalIndexScan) isFullScan() bool { + if len(p.rangeInfo) > 0 || p.haveCorCol() { + return false + } + for _, ran := range p.Ranges { + if !ran.IsFullRange(false) { + return false + } + } + return true +} + +// ExplainID overrides the ExplainID in order to match different range. +func (p *PhysicalTableScan) ExplainID() fmt.Stringer { + return stringutil.MemoizeStr(func() string { + if p.SCtx() != nil && p.SCtx().GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { + return p.TP() + } + return p.TP() + "_" + strconv.Itoa(p.ID()) + }) +} + +// TP overrides the TP in order to match different range. +func (p *PhysicalTableScan) TP() string { + if p.isChildOfIndexLookUp { + return plancodec.TypeTableRowIDScan + } else if p.isFullScan() { + return plancodec.TypeTableFullScan + } + return plancodec.TypeTableRangeScan +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalTableScan) ExplainInfo() string { + return p.AccessObject().String() + ", " + p.OperatorInfo(false) +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalTableScan) ExplainNormalizedInfo() string { + return p.AccessObject().NormalizedString() + ", " + p.OperatorInfo(true) +} + +// OperatorInfo implements dataAccesser interface. +func (p *PhysicalTableScan) OperatorInfo(normalized bool) string { + var buffer strings.Builder + if len(p.rangeInfo) > 0 { + // TODO: deal with normalized case + buffer.WriteString("range: decided by ") + buffer.WriteString(p.rangeInfo) + buffer.WriteString(", ") + } else if p.haveCorCol() { + if normalized { + buffer.WriteString("range: decided by ") + buffer.Write(expression.SortedExplainNormalizedExpressionList(p.AccessCondition)) + buffer.WriteString(", ") + } else { + buffer.WriteString("range: decided by [") + for i, AccessCondition := range p.AccessCondition { + if i != 0 { + buffer.WriteString(" ") + } + buffer.WriteString(AccessCondition.String()) + } + buffer.WriteString("], ") + } + } else if len(p.Ranges) > 0 { + if normalized { + buffer.WriteString("range:[?,?], ") + } else if !p.isFullScan() { + buffer.WriteString("range:") + for _, idxRange := range p.Ranges { + buffer.WriteString(idxRange.String()) + buffer.WriteString(", ") + } + } + } + if p.SCtx().GetSessionVars().EnableLateMaterialization && len(p.filterCondition) > 0 && p.StoreType == kv.TiFlash { + buffer.WriteString("pushed down filter:") + if len(p.lateMaterializationFilterCondition) > 0 { + if normalized { + buffer.Write(expression.SortedExplainNormalizedExpressionList(p.lateMaterializationFilterCondition)) + } else { + buffer.Write(expression.SortedExplainExpressionList(p.lateMaterializationFilterCondition)) + } + } else { + buffer.WriteString("empty") + } + buffer.WriteString(", ") + } + buffer.WriteString("keep order:") + buffer.WriteString(strconv.FormatBool(p.KeepOrder)) + if p.Desc { + buffer.WriteString(", desc") + } + if !normalized { + if p.usedStatsInfo != nil { + str := p.usedStatsInfo.FormatForExplain() + if len(str) > 0 { + buffer.WriteString(", ") + buffer.WriteString(str) + } + } else if p.StatsInfo().StatsVersion == statistics.PseudoVersion { + // This branch is not needed in fact, we add this to prevent test result changes under planner/cascades/ + buffer.WriteString(", stats:pseudo") + } + } + if p.StoreType == kv.TiFlash && p.Table.GetPartitionInfo() != nil && p.IsMPPOrBatchCop && p.SCtx().GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { + buffer.WriteString(", PartitionTableScan:true") + } + if len(p.runtimeFilterList) > 0 { + buffer.WriteString(", runtime filter:") + for i, runtimeFilter := range p.runtimeFilterList { + if i != 0 { + buffer.WriteString(", ") + } + buffer.WriteString(runtimeFilter.ExplainInfo(false)) + } + } + return buffer.String() +} + +func (p *PhysicalTableScan) haveCorCol() bool { + for _, cond := range p.AccessCondition { + if len(expression.ExtractCorColumns(cond)) > 0 { + return true + } + } + return false +} + +func (p *PhysicalTableScan) isFullScan() bool { + if len(p.rangeInfo) > 0 || p.haveCorCol() { + return false + } + var unsignedIntHandle bool + if p.Table.PKIsHandle { + if pkColInfo := p.Table.GetPkColInfo(); pkColInfo != nil { + unsignedIntHandle = mysql.HasUnsignedFlag(pkColInfo.GetFlag()) + } + } + for _, ran := range p.Ranges { + if !ran.IsFullRange(unsignedIntHandle) { + return false + } + } + return true +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalTableReader) ExplainInfo() string { + tablePlanInfo := "data:" + p.tablePlan.ExplainID().String() + + if p.ReadReqType == MPP { + return fmt.Sprintf("MppVersion: %d, %s", p.SCtx().GetSessionVars().ChooseMppVersion(), tablePlanInfo) + } + + return tablePlanInfo +} + +// ExplainNormalizedInfo implements Plan interface. +func (*PhysicalTableReader) ExplainNormalizedInfo() string { + return "" +} + +// OperatorInfo return other operator information to be explained. +func (p *PhysicalTableReader) OperatorInfo(_ bool) string { + return "data:" + p.tablePlan.ExplainID().String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexReader) ExplainInfo() string { + return "index:" + p.indexPlan.ExplainID().String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalIndexReader) ExplainNormalizedInfo() string { + return "index:" + p.indexPlan.TP() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexLookUpReader) ExplainInfo() string { + var str strings.Builder + // The children can be inferred by the relation symbol. + if p.PushedLimit != nil { + str.WriteString("limit embedded(offset:") + str.WriteString(strconv.FormatUint(p.PushedLimit.Offset, 10)) + str.WriteString(", count:") + str.WriteString(strconv.FormatUint(p.PushedLimit.Count, 10)) + str.WriteString(")") + } + return str.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexMergeReader) ExplainInfo() string { + var str strings.Builder + if p.IsIntersectionType { + str.WriteString("type: intersection") + } else { + str.WriteString("type: union") + } + if p.PushedLimit != nil { + str.WriteString(", limit embedded(offset:") + str.WriteString(strconv.FormatUint(p.PushedLimit.Offset, 10)) + str.WriteString(", count:") + str.WriteString(strconv.FormatUint(p.PushedLimit.Count, 10)) + str.WriteString(")") + } + return str.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalUnionScan) ExplainInfo() string { + return string(expression.SortedExplainExpressionList(p.Conditions)) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalSelection) ExplainInfo() string { + exprStr := string(expression.SortedExplainExpressionList(p.Conditions)) + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + exprStr += fmt.Sprintf(", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return exprStr +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalSelection) ExplainNormalizedInfo() string { + return string(expression.SortedExplainNormalizedExpressionList(p.Conditions)) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalProjection) ExplainInfo() string { + exprStr := expression.ExplainExpressionList(p.Exprs, p.schema) + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + exprStr += fmt.Sprintf(", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return exprStr +} + +func (p *PhysicalExpand) explainInfoV2() string { + sb := strings.Builder{} + for i, oneL := range p.LevelExprs { + if i == 0 { + sb.WriteString("level-projection:") + sb.WriteString("[") + sb.WriteString(expression.ExplainExpressionList(oneL, p.schema)) + sb.WriteString("]") + } else { + sb.WriteString(",[") + sb.WriteString(expression.ExplainExpressionList(oneL, p.schema)) + sb.WriteString("]") + } + } + sb.WriteString("; schema: [") + colStrs := make([]string, 0, len(p.schema.Columns)) + for _, col := range p.schema.Columns { + colStrs = append(colStrs, col.String()) + } + sb.WriteString(strings.Join(colStrs, ",")) + sb.WriteString("]") + return sb.String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalProjection) ExplainNormalizedInfo() string { + return string(expression.SortedExplainNormalizedExpressionList(p.Exprs)) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalTableDual) ExplainInfo() string { + var str strings.Builder + str.WriteString("rows:") + str.WriteString(strconv.Itoa(p.RowCount)) + return str.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalSort) ExplainInfo() string { + buffer := bytes.NewBufferString("") + buffer = explainByItems(buffer, p.ByItems) + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalLimit) ExplainInfo() string { + buffer := bytes.NewBufferString("") + if len(p.GetPartitionBy()) > 0 { + buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) + fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) + } else { + fmt.Fprintf(buffer, "offset:%v, count:%v", p.Offset, p.Count) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalExpand) ExplainInfo() string { + if len(p.LevelExprs) > 0 { + return p.explainInfoV2() + } + var str strings.Builder + str.WriteString("group set num:") + str.WriteString(strconv.FormatInt(int64(len(p.GroupingSets)), 10)) + str.WriteString(", groupingID:") + str.WriteString(p.GroupingIDCol.String()) + str.WriteString(", ") + str.WriteString(p.GroupingSets.String()) + return str.String() +} + +// ExplainInfo implements Plan interface. +func (p *basePhysicalAgg) ExplainInfo() string { + return p.explainInfo(false) +} + +func (p *basePhysicalAgg) explainInfo(normalized bool) string { + sortedExplainExpressionList := expression.SortedExplainExpressionList + if normalized { + sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList + } + + builder := &strings.Builder{} + if len(p.GroupByItems) > 0 { + builder.WriteString("group by:") + builder.Write(sortedExplainExpressionList(p.GroupByItems)) + builder.WriteString(", ") + } + for i := 0; i < len(p.AggFuncs); i++ { + builder.WriteString("funcs:") + var colName string + if normalized { + colName = p.schema.Columns[i].ExplainNormalizedInfo() + } else { + colName = p.schema.Columns[i].ExplainInfo() + } + builder.WriteString(aggregation.ExplainAggFunc(p.AggFuncs[i], normalized)) + builder.WriteString("->") + builder.WriteString(colName) + if i+1 < len(p.AggFuncs) { + builder.WriteString(", ") + } + } + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + fmt.Fprintf(builder, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return builder.String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *basePhysicalAgg) ExplainNormalizedInfo() string { + return p.explainInfo(true) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexJoin) ExplainInfo() string { + return p.explainInfo(false, false) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalIndexMergeJoin) ExplainInfo() string { + return p.explainInfo(false, true) +} + +func (p *PhysicalIndexJoin) explainInfo(normalized bool, isIndexMergeJoin bool) string { + sortedExplainExpressionList := expression.SortedExplainExpressionList + if normalized { + sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList + } + + buffer := bytes.NewBufferString(p.JoinType.String()) + buffer.WriteString(", inner:") + if normalized { + buffer.WriteString(p.Children()[p.InnerChildIdx].TP()) + } else { + buffer.WriteString(p.Children()[p.InnerChildIdx].ExplainID().String()) + } + if len(p.OuterJoinKeys) > 0 { + buffer.WriteString(", outer key:") + buffer.Write(expression.ExplainColumnList(p.OuterJoinKeys)) + } + if len(p.InnerJoinKeys) > 0 { + buffer.WriteString(", inner key:") + buffer.Write(expression.ExplainColumnList(p.InnerJoinKeys)) + } + + if len(p.OuterHashKeys) > 0 && !isIndexMergeJoin { + exprs := make([]expression.Expression, 0, len(p.OuterHashKeys)) + for i := range p.OuterHashKeys { + expr, err := expression.NewFunctionBase(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeLonglong), p.OuterHashKeys[i], p.InnerHashKeys[i]) + if err != nil { + logutil.BgLogger().Warn("fail to NewFunctionBase", zap.Error(err)) + } + exprs = append(exprs, expr) + } + buffer.WriteString(", equal cond:") + buffer.Write(sortedExplainExpressionList(exprs)) + } + if len(p.LeftConditions) > 0 { + buffer.WriteString(", left cond:") + buffer.Write(sortedExplainExpressionList(p.LeftConditions)) + } + if len(p.RightConditions) > 0 { + buffer.WriteString(", right cond:") + buffer.Write(sortedExplainExpressionList(p.RightConditions)) + } + if len(p.OtherConditions) > 0 { + buffer.WriteString(", other cond:") + buffer.Write(sortedExplainExpressionList(p.OtherConditions)) + } + return buffer.String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalIndexJoin) ExplainNormalizedInfo() string { + return p.explainInfo(true, false) +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalIndexMergeJoin) ExplainNormalizedInfo() string { + return p.explainInfo(true, true) +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalHashJoin) ExplainInfo() string { + return p.explainInfo(false) +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalHashJoin) ExplainNormalizedInfo() string { + return p.explainInfo(true) +} + +func (p *PhysicalHashJoin) explainInfo(normalized bool) string { + sortedExplainExpressionList := expression.SortedExplainExpressionList + if normalized { + sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList + } + + buffer := new(strings.Builder) + + if len(p.EqualConditions) == 0 { + if len(p.NAEqualConditions) == 0 { + buffer.WriteString("CARTESIAN ") + } else { + buffer.WriteString("Null-aware ") + } + } + + buffer.WriteString(p.JoinType.String()) + + if len(p.EqualConditions) > 0 { + if normalized { + buffer.WriteString(", equal:") + buffer.Write(expression.SortedExplainNormalizedScalarFuncList(p.EqualConditions)) + } else { + buffer.WriteString(", equal:[") + for i, EqualConditions := range p.EqualConditions { + if i != 0 { + buffer.WriteString(" ") + } + buffer.WriteString(EqualConditions.String()) + } + buffer.WriteString("]") + } + } + if len(p.NAEqualConditions) > 0 { + if normalized { + buffer.WriteString(", equal:") + buffer.Write(expression.SortedExplainNormalizedScalarFuncList(p.NAEqualConditions)) + } else { + buffer.WriteString(", equal:[") + for i, NAEqualCondition := range p.NAEqualConditions { + if i != 0 { + buffer.WriteString(" ") + } + buffer.WriteString(NAEqualCondition.String()) + } + buffer.WriteString("]") + } + } + if len(p.LeftConditions) > 0 { + if normalized { + buffer.WriteString(", left cond:") + buffer.Write(expression.SortedExplainNormalizedExpressionList(p.LeftConditions)) + } else { + buffer.WriteString(", left cond:[") + for i, LeftConditions := range p.LeftConditions { + if i != 0 { + buffer.WriteString(" ") + } + buffer.WriteString(LeftConditions.String()) + } + buffer.WriteString("]") + } + } + if len(p.RightConditions) > 0 { + buffer.WriteString(", right cond:") + buffer.Write(sortedExplainExpressionList(p.RightConditions)) + } + if len(p.OtherConditions) > 0 { + buffer.WriteString(", other cond:") + buffer.Write(sortedExplainExpressionList(p.OtherConditions)) + } + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + + // for runtime filter + if len(p.runtimeFilterList) > 0 { + buffer.WriteString(", runtime filter:") + for i, runtimeFilter := range p.runtimeFilterList { + if i != 0 { + buffer.WriteString(", ") + } + buffer.WriteString(runtimeFilter.ExplainInfo(true)) + } + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalMergeJoin) ExplainInfo() string { + return p.explainInfo(false) +} + +func (p *PhysicalMergeJoin) explainInfo(normalized bool) string { + sortedExplainExpressionList := expression.SortedExplainExpressionList + if normalized { + sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList + } + + buffer := bytes.NewBufferString(p.JoinType.String()) + if len(p.LeftJoinKeys) > 0 { + fmt.Fprintf(buffer, ", left key:%s", + expression.ExplainColumnList(p.LeftJoinKeys)) + } + if len(p.RightJoinKeys) > 0 { + fmt.Fprintf(buffer, ", right key:%s", + expression.ExplainColumnList(p.RightJoinKeys)) + } + if len(p.LeftConditions) > 0 { + if normalized { + fmt.Fprintf(buffer, ", left cond:%s", expression.SortedExplainNormalizedExpressionList(p.LeftConditions)) + } else { + fmt.Fprintf(buffer, ", left cond:%s", p.LeftConditions) + } + } + if len(p.RightConditions) > 0 { + fmt.Fprintf(buffer, ", right cond:%s", + sortedExplainExpressionList(p.RightConditions)) + } + if len(p.OtherConditions) > 0 { + fmt.Fprintf(buffer, ", other cond:%s", + sortedExplainExpressionList(p.OtherConditions)) + } + return buffer.String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalMergeJoin) ExplainNormalizedInfo() string { + return p.explainInfo(true) +} + +// explainPartitionBy: produce text for p.PartitionBy. Common for window functions and TopN. +func explainPartitionBy(buffer *bytes.Buffer, partitionBy []property.SortItem, normalized bool) *bytes.Buffer { + if len(partitionBy) > 0 { + buffer.WriteString("partition by ") + for i, item := range partitionBy { + if normalized { + fmt.Fprintf(buffer, "%s", item.Col.ExplainNormalizedInfo()) + } else { + fmt.Fprintf(buffer, "%s", item.Col.ExplainInfo()) + } + if i+1 < len(partitionBy) { + buffer.WriteString(", ") + } + } + } + return buffer +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalTopN) ExplainInfo() string { + buffer := bytes.NewBufferString("") + if len(p.GetPartitionBy()) > 0 { + buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) + buffer.WriteString(" ") + } + if len(p.ByItems) > 0 { + // Add order by text to separate partition by. Otherwise, do not add order by to + // avoid breaking existing TopN tests. + if len(p.GetPartitionBy()) > 0 { + buffer.WriteString("order by ") + } + buffer = explainByItems(buffer, p.ByItems) + } + fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) + return buffer.String() +} + +// ExplainNormalizedInfo implements Plan interface. +func (p *PhysicalTopN) ExplainNormalizedInfo() string { + buffer := bytes.NewBufferString("") + if len(p.GetPartitionBy()) > 0 { + buffer = explainPartitionBy(buffer, p.GetPartitionBy(), true) + buffer.WriteString(" ") + } + if len(p.ByItems) > 0 { + // Add order by text to separate partition by. Otherwise, do not add order by to + // avoid breaking existing TopN tests. + if len(p.GetPartitionBy()) > 0 { + buffer.WriteString("order by ") + } + buffer = explainNormalizedByItems(buffer, p.ByItems) + } + return buffer.String() +} + +func (*PhysicalWindow) formatFrameBound(buffer *bytes.Buffer, bound *FrameBound) { + if bound.Type == ast.CurrentRow { + buffer.WriteString("current row") + return + } + if bound.UnBounded { + buffer.WriteString("unbounded") + } else if len(bound.CalcFuncs) > 0 { + sf := bound.CalcFuncs[0].(*expression.ScalarFunction) + switch sf.FuncName.L { + case ast.DateAdd, ast.DateSub: + // For `interval '2:30' minute_second`. + fmt.Fprintf(buffer, "interval %s %s", sf.GetArgs()[1].ExplainInfo(), sf.GetArgs()[2].ExplainInfo()) + case ast.Plus, ast.Minus: + // For `1 preceding` of range frame. + fmt.Fprintf(buffer, "%s", sf.GetArgs()[1].ExplainInfo()) + } + } else { + fmt.Fprintf(buffer, "%d", bound.Num) + } + if bound.Type == ast.Preceding { + buffer.WriteString(" preceding") + } else { + buffer.WriteString(" following") + } +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalWindow) ExplainInfo() string { + buffer := bytes.NewBufferString("") + formatWindowFuncDescs(buffer, p.WindowFuncDescs, p.schema) + buffer.WriteString(" over(") + isFirst := true + buffer = explainPartitionBy(buffer, p.PartitionBy, false) + if len(p.PartitionBy) > 0 { + isFirst = false + } + if len(p.OrderBy) > 0 { + if !isFirst { + buffer.WriteString(" ") + } + buffer.WriteString("order by ") + for i, item := range p.OrderBy { + if item.Desc { + fmt.Fprintf(buffer, "%s desc", item.Col.ExplainInfo()) + } else { + fmt.Fprintf(buffer, "%s", item.Col.ExplainInfo()) + } + + if i+1 < len(p.OrderBy) { + buffer.WriteString(", ") + } + } + isFirst = false + } + if p.Frame != nil { + if !isFirst { + buffer.WriteString(" ") + } + if p.Frame.Type == ast.Rows { + buffer.WriteString("rows") + } else { + buffer.WriteString("range") + } + buffer.WriteString(" between ") + p.formatFrameBound(buffer, p.Frame.Start) + buffer.WriteString(" and ") + p.formatFrameBound(buffer, p.Frame.End) + } + buffer.WriteString(")") + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalShuffle) ExplainInfo() string { + explainIds := make([]fmt.Stringer, len(p.DataSources)) + for i := range p.DataSources { + explainIds[i] = p.DataSources[i].ExplainID() + } + + buffer := bytes.NewBufferString("") + fmt.Fprintf(buffer, "execution info: concurrency:%v, data sources:%v", p.Concurrency, explainIds) + return buffer.String() +} + +func formatWindowFuncDescs(buffer *bytes.Buffer, descs []*aggregation.WindowFuncDesc, schema *expression.Schema) *bytes.Buffer { + winFuncStartIdx := len(schema.Columns) - len(descs) + for i, desc := range descs { + if i != 0 { + buffer.WriteString(", ") + } + fmt.Fprintf(buffer, "%v->%v", desc, schema.Columns[winFuncStartIdx+i]) + } + return buffer +} + +// ExplainInfo implements Plan interface. +func (p *LogicalJoin) ExplainInfo() string { + buffer := bytes.NewBufferString(p.JoinType.String()) + if len(p.EqualConditions) > 0 { + fmt.Fprintf(buffer, ", equal:%v", p.EqualConditions) + } + if len(p.LeftConditions) > 0 { + fmt.Fprintf(buffer, ", left cond:%s", + expression.SortedExplainExpressionList(p.LeftConditions)) + } + if len(p.RightConditions) > 0 { + fmt.Fprintf(buffer, ", right cond:%s", + expression.SortedExplainExpressionList(p.RightConditions)) + } + if len(p.OtherConditions) > 0 { + fmt.Fprintf(buffer, ", other cond:%s", + expression.SortedExplainExpressionList(p.OtherConditions)) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalAggregation) ExplainInfo() string { + buffer := bytes.NewBufferString("") + if len(p.GroupByItems) > 0 { + fmt.Fprintf(buffer, "group by:%s, ", + expression.SortedExplainExpressionList(p.GroupByItems)) + } + if len(p.AggFuncs) > 0 { + buffer.WriteString("funcs:") + for i, agg := range p.AggFuncs { + buffer.WriteString(aggregation.ExplainAggFunc(agg, false)) + if i+1 < len(p.AggFuncs) { + buffer.WriteString(", ") + } + } + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalProjection) ExplainInfo() string { + return expression.ExplainExpressionList(p.Exprs, p.schema) +} + +// ExplainInfo implements Plan interface. +func (p *LogicalSelection) ExplainInfo() string { + return string(expression.SortedExplainExpressionList(p.Conditions)) +} + +// ExplainInfo implements Plan interface. +func (p *LogicalApply) ExplainInfo() string { + return p.LogicalJoin.ExplainInfo() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalTableDual) ExplainInfo() string { + var str strings.Builder + str.WriteString("rowcount:") + str.WriteString(strconv.Itoa(p.RowCount)) + return str.String() +} + +// ExplainInfo implements Plan interface. +func (ds *DataSource) ExplainInfo() string { + buffer := bytes.NewBufferString("") + tblName := ds.tableInfo.Name.O + if ds.TableAsName != nil && ds.TableAsName.O != "" { + tblName = ds.TableAsName.O + } + fmt.Fprintf(buffer, "table:%s", tblName) + if ds.isPartition { + if pi := ds.tableInfo.GetPartitionInfo(); pi != nil { + partitionName := pi.GetNameByID(ds.physicalTableID) + fmt.Fprintf(buffer, ", partition:%s", partitionName) + } + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalExchangeSender) ExplainInfo() string { + buffer := bytes.NewBufferString("ExchangeType: ") + switch p.ExchangeType { + case tipb.ExchangeType_PassThrough: + fmt.Fprintf(buffer, "PassThrough") + case tipb.ExchangeType_Broadcast: + fmt.Fprintf(buffer, "Broadcast") + case tipb.ExchangeType_Hash: + fmt.Fprintf(buffer, "HashPartition") + } + if p.CompressionMode != kv.ExchangeCompressionModeNONE { + fmt.Fprintf(buffer, ", Compression: %s", p.CompressionMode.Name()) + } + if p.ExchangeType == tipb.ExchangeType_Hash { + fmt.Fprintf(buffer, ", Hash Cols: %s", property.ExplainColumnList(p.HashCols)) + } + if len(p.Tasks) > 0 { + fmt.Fprintf(buffer, ", tasks: [") + for idx, task := range p.Tasks { + if idx != 0 { + fmt.Fprintf(buffer, ", ") + } + fmt.Fprintf(buffer, "%v", task.ID) + } + fmt.Fprintf(buffer, "]") + } + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *PhysicalExchangeReceiver) ExplainInfo() (res string) { + if p.TiFlashFineGrainedShuffleStreamCount > 0 { + res = fmt.Sprintf("stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) + } + return res +} + +// ExplainInfo implements Plan interface. +func (p *LogicalUnionScan) ExplainInfo() string { + buffer := bytes.NewBufferString("") + fmt.Fprintf(buffer, "conds:%s", + expression.SortedExplainExpressionList(p.conditions)) + fmt.Fprintf(buffer, ", handle:%s", p.handleCols) + return buffer.String() +} + +func explainByItems(buffer *bytes.Buffer, byItems []*util.ByItems) *bytes.Buffer { + for i, item := range byItems { + if item.Desc { + fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainInfo()) + } else { + fmt.Fprintf(buffer, "%s", item.Expr.ExplainInfo()) + } + + if i+1 < len(byItems) { + buffer.WriteString(", ") + } + } + return buffer +} + +func explainNormalizedByItems(buffer *bytes.Buffer, byItems []*util.ByItems) *bytes.Buffer { + for i, item := range byItems { + if item.Desc { + fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainNormalizedInfo()) + } else { + fmt.Fprintf(buffer, "%s", item.Expr.ExplainNormalizedInfo()) + } + + if i+1 < len(byItems) { + buffer.WriteString(", ") + } + } + return buffer +} + +// ExplainInfo implements Plan interface. +func (p *LogicalSort) ExplainInfo() string { + buffer := bytes.NewBufferString("") + return explainByItems(buffer, p.ByItems).String() +} + +// ExplainInfo implements Plan interface. +func (lt *LogicalTopN) ExplainInfo() string { + buffer := bytes.NewBufferString("") + buffer = explainPartitionBy(buffer, lt.GetPartitionBy(), false) + if len(lt.GetPartitionBy()) > 0 && len(lt.ByItems) > 0 { + buffer.WriteString("order by ") + } + buffer = explainByItems(buffer, lt.ByItems) + fmt.Fprintf(buffer, ", offset:%v, count:%v", lt.Offset, lt.Count) + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalLimit) ExplainInfo() string { + buffer := bytes.NewBufferString("") + if len(p.GetPartitionBy()) > 0 { + buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) + fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) + } else { + fmt.Fprintf(buffer, "offset:%v, count:%v", p.Offset, p.Count) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalTableScan) ExplainInfo() string { + buffer := bytes.NewBufferString(p.Source.ExplainInfo()) + if p.Source.handleCols != nil { + fmt.Fprintf(buffer, ", pk col:%s", p.Source.handleCols) + } + if len(p.AccessConds) > 0 { + fmt.Fprintf(buffer, ", cond:%v", p.AccessConds) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *LogicalIndexScan) ExplainInfo() string { + buffer := bytes.NewBufferString(p.Source.ExplainInfo()) + index := p.Index + if len(index.Columns) > 0 { + buffer.WriteString(", index:") + for i, idxCol := range index.Columns { + if tblCol := p.Source.tableInfo.Columns[idxCol.Offset]; tblCol.Hidden { + buffer.WriteString(tblCol.GeneratedExprString) + } else { + buffer.WriteString(idxCol.Name.O) + } + if i+1 < len(index.Columns) { + buffer.WriteString(", ") + } + } + } + if len(p.AccessConds) > 0 { + fmt.Fprintf(buffer, ", cond:%v", p.AccessConds) + } + return buffer.String() +} + +// ExplainInfo implements Plan interface. +func (p *TiKVSingleGather) ExplainInfo() string { + buffer := bytes.NewBufferString(p.Source.ExplainInfo()) + if p.IsIndexGather { + buffer.WriteString(", index:" + p.Index.Name.String()) + } + return buffer.String() +} + +// MetricTableTimeFormat is the time format for metric table explain and format. +const MetricTableTimeFormat = "2006-01-02 15:04:05.999" + +// ExplainInfo implements Plan interface. +func (p *PhysicalMemTable) ExplainInfo() string { + accessObject, operatorInfo := p.AccessObject().String(), p.OperatorInfo(false) + if len(operatorInfo) == 0 { + return accessObject + } + return accessObject + ", " + operatorInfo +} + +// OperatorInfo implements dataAccesser interface. +func (p *PhysicalMemTable) OperatorInfo(_ bool) string { + if p.Extractor != nil { + return p.Extractor.explainInfo(p) + } + return "" +} diff --git a/planner/core/expression_rewriter.go b/pkg/planner/core/expression_rewriter.go similarity index 98% rename from planner/core/expression_rewriter.go rename to pkg/planner/core/expression_rewriter.go index 21e0906088f93..8d43879693180 100644 --- a/planner/core/expression_rewriter.go +++ b/pkg/planner/core/expression_rewriter.go @@ -23,28 +23,28 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // EvalSubqueryFirstRow evaluates incorrelated subqueries once, and get first row. diff --git a/planner/core/expression_rewriter_test.go b/pkg/planner/core/expression_rewriter_test.go similarity index 99% rename from planner/core/expression_rewriter_test.go rename to pkg/planner/core/expression_rewriter_test.go index 0849ab6af3ba4..503a6538c3b77 100644 --- a/planner/core/expression_rewriter_test.go +++ b/pkg/planner/core/expression_rewriter_test.go @@ -17,10 +17,10 @@ package core_test import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/expression_test.go b/pkg/planner/core/expression_test.go new file mode 100644 index 0000000000000..cf4e2765a4889 --- /dev/null +++ b/pkg/planner/core/expression_test.go @@ -0,0 +1,334 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package core + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func parseExpr(t *testing.T, expr string) ast.ExprNode { + p := parser.New() + st, err := p.ParseOneStmt("select "+expr, "", "") + require.NoError(t, err) + stmt := st.(*ast.SelectStmt) + return stmt.Fields.Fields[0].Expr +} + +type testCase struct { + exprStr string + resultStr string +} + +func runTests(t *testing.T, tests []testCase) { + ctx := MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + for _, tt := range tests { + expr := parseExpr(t, tt.exprStr) + val, err := evalAstExpr(ctx, expr) + require.NoError(t, err) + valStr := fmt.Sprintf("%v", val.GetValue()) + require.Equalf(t, tt.resultStr, valStr, "for %s", tt.exprStr) + } +} + +func TestBetween(t *testing.T) { + tests := []testCase{ + {exprStr: "1 between 2 and 3", resultStr: "0"}, + {exprStr: "1 not between 2 and 3", resultStr: "1"}, + {exprStr: "'2001-04-10 12:34:56' between cast('2001-01-01 01:01:01' as datetime) and '01-05-01'", resultStr: "1"}, + {exprStr: "20010410123456 between cast('2001-01-01 01:01:01' as datetime) and 010501", resultStr: "0"}, + {exprStr: "20010410123456 between cast('2001-01-01 01:01:01' as datetime) and 20010501123456", resultStr: "1"}, + } + runTests(t, tests) +} + +func TestCaseWhen(t *testing.T) { + tests := []testCase{ + { + exprStr: "case 1 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "str1", + }, + { + exprStr: "case 2 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "str2", + }, + { + exprStr: "case 3 when 1 then 'str1' when 2 then 'str2' end", + resultStr: "", + }, + { + exprStr: "case 4 when 1 then 'str1' when 2 then 'str2' else 'str3' end", + resultStr: "str3", + }, + } + runTests(t, tests) + + // When expression value changed, result set back to null. + valExpr := ast.NewValueExpr(1, "", "") + whenClause := &ast.WhenClause{Expr: ast.NewValueExpr(1, "", ""), Result: ast.NewValueExpr(1, "", "")} + caseExpr := &ast.CaseExpr{ + Value: valExpr, + WhenClauses: []*ast.WhenClause{whenClause}, + } + ctx := MockContext() + defer func() { + do := domain.GetDomain(ctx) + do.StatsHandle().Close() + }() + v, err := evalAstExpr(ctx, caseExpr) + require.NoError(t, err) + require.Equal(t, types.NewDatum(int64(1)), v) + valExpr.SetValue(4) + v, err = evalAstExpr(ctx, caseExpr) + require.NoError(t, err) + require.Equal(t, types.KindNull, v.Kind()) +} + +func TestCast(t *testing.T) { + f := types.NewFieldType(mysql.TypeLonglong) + + expr := &ast.FuncCastExpr{ + Expr: ast.NewValueExpr(1, "", ""), + Tp: f, + } + + ctx := MockContext() + defer func() { + do := domain.GetDomain(ctx) + do.StatsHandle().Close() + }() + ast.SetFlag(expr) + v, err := evalAstExpr(ctx, expr) + require.NoError(t, err) + require.Equal(t, types.NewDatum(int64(1)), v) + + f.AddFlag(mysql.UnsignedFlag) + v, err = evalAstExpr(ctx, expr) + require.NoError(t, err) + require.Equal(t, types.NewDatum(uint64(1)), v) + + f.SetType(mysql.TypeString) + f.SetCharset(charset.CharsetBin) + f.SetFlen(-1) + f.SetDecimal(-1) + v, err = evalAstExpr(ctx, expr) + require.NoError(t, err) + testutil.DatumEqual(t, types.NewDatum([]byte("1")), v) + + f.SetType(mysql.TypeString) + f.SetCharset(charset.CharsetUTF8) + f.SetFlen(-1) + f.SetDecimal(-1) + v, err = evalAstExpr(ctx, expr) + require.NoError(t, err) + testutil.DatumEqual(t, types.NewDatum([]byte("1")), v) + + expr.Expr = ast.NewValueExpr(nil, "", "") + v, err = evalAstExpr(ctx, expr) + require.NoError(t, err) + require.Equal(t, types.KindNull, v.Kind()) +} + +func TestPatternIn(t *testing.T) { + tests := []testCase{ + { + exprStr: "1 not in (1, 2, 3)", + resultStr: "0", + }, + { + exprStr: "1 in (1, 2, 3)", + resultStr: "1", + }, + { + exprStr: "1 in (2, 3)", + resultStr: "0", + }, + { + exprStr: "NULL in (2, 3)", + resultStr: "", + }, + { + exprStr: "NULL not in (2, 3)", + resultStr: "", + }, + { + exprStr: "NULL in (NULL, 3)", + resultStr: "", + }, + { + exprStr: "1 in (1, NULL)", + resultStr: "1", + }, + { + exprStr: "1 in (NULL, 1)", + resultStr: "1", + }, + { + exprStr: "2 in (1, NULL)", + resultStr: "", + }, + { + exprStr: "(-(23)++46/51*+51) in (+23)", + resultStr: "0", + }, + } + runTests(t, tests) +} + +func TestIsNull(t *testing.T) { + tests := []testCase{ + { + exprStr: "1 IS NULL", + resultStr: "0", + }, + { + exprStr: "1 IS NOT NULL", + resultStr: "1", + }, + { + exprStr: "NULL IS NULL", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT NULL", + resultStr: "0", + }, + } + runTests(t, tests) +} + +func TestCompareRow(t *testing.T) { + tests := []testCase{ + { + exprStr: "row(1,2,3)=row(1,2,3)", + resultStr: "1", + }, + { + exprStr: "row(1,2,3)=row(1+3,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)<>row(1,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)<>row(1+3,2,3)", + resultStr: "1", + }, + { + exprStr: "row(1+3,2,3)<>row(1+3,2,3)", + resultStr: "0", + }, + { + exprStr: "row(1,2,3)", + }, + { + exprStr: "row(1,2,3)=row(0,NULL,3)", + resultStr: "1", + }, + { + exprStr: "row(1,2,3)<=row(2,NULL,3)", + resultStr: "1", + }, + } + runTests(t, tests) +} + +func TestIsTruth(t *testing.T) { + tests := []testCase{ + { + exprStr: "1 IS TRUE", + resultStr: "1", + }, + { + exprStr: "2 IS TRUE", + resultStr: "1", + }, + { + exprStr: "0 IS TRUE", + resultStr: "0", + }, + { + exprStr: "NULL IS TRUE", + resultStr: "0", + }, + { + exprStr: "1 IS FALSE", + resultStr: "0", + }, + { + exprStr: "2 IS FALSE", + resultStr: "0", + }, + { + exprStr: "0 IS FALSE", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "1 IS NOT TRUE", + resultStr: "0", + }, + { + exprStr: "2 IS NOT TRUE", + resultStr: "0", + }, + { + exprStr: "0 IS NOT TRUE", + resultStr: "1", + }, + { + exprStr: "NULL IS NOT TRUE", + resultStr: "1", + }, + { + exprStr: "1 IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "2 IS NOT FALSE", + resultStr: "1", + }, + { + exprStr: "0 IS NOT FALSE", + resultStr: "0", + }, + { + exprStr: "NULL IS NOT FALSE", + resultStr: "1", + }, + } + runTests(t, tests) +} diff --git a/planner/core/find_best_task.go b/pkg/planner/core/find_best_task.go similarity index 99% rename from planner/core/find_best_task.go rename to pkg/planner/core/find_best_task.go index 1c986d982aebf..e5867af395138 100644 --- a/planner/core/find_best_task.go +++ b/pkg/planner/core/find_best_task.go @@ -22,26 +22,26 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/tracing" "go.uber.org/zap" ) diff --git a/planner/core/find_best_task_test.go b/pkg/planner/core/find_best_task_test.go similarity index 98% rename from planner/core/find_best_task_test.go rename to pkg/planner/core/find_best_task_test.go index 9b7caa3baef5a..713d61b455931 100644 --- a/planner/core/find_best_task_test.go +++ b/pkg/planner/core/find_best_task_test.go @@ -18,10 +18,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" "github.com/stretchr/testify/require" ) diff --git a/planner/core/flat_plan.go b/pkg/planner/core/flat_plan.go similarity index 99% rename from planner/core/flat_plan.go rename to pkg/planner/core/flat_plan.go index c35857df679da..747de0a4e0f11 100644 --- a/planner/core/flat_plan.go +++ b/pkg/planner/core/flat_plan.go @@ -18,9 +18,9 @@ import ( "fmt" "sort" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/texttree" "go.uber.org/zap" ) diff --git a/pkg/planner/core/foreign_key.go b/pkg/planner/core/foreign_key.go new file mode 100644 index 0000000000000..8ba8f14ca6a01 --- /dev/null +++ b/pkg/planner/core/foreign_key.go @@ -0,0 +1,499 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package core + +import ( + "fmt" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" +) + +// FKCheck indicates the foreign key constraint checker. +type FKCheck struct { + basePhysicalPlan + FK *model.FKInfo + ReferredFK *model.ReferredFKInfo + Tbl table.Table + Idx table.Index + Cols []model.CIStr + + IdxIsPrimaryKey bool + IdxIsExclusive bool + + CheckExist bool + FailedErr error +} + +// FKCascade indicates the foreign key constraint cascade behaviour. +type FKCascade struct { + basePhysicalPlan + Tp FKCascadeType + ReferredFK *model.ReferredFKInfo + ChildTable table.Table + FK *model.FKInfo + FKCols []*model.ColumnInfo + FKIdx *model.IndexInfo + // CascadePlans contains the child cascade plan. + // CascadePlans will be filled during execution, so only `explain analyze` statement result contains the cascade plan, + // `explain` statement result doesn't contain the cascade plan. + CascadePlans []Plan +} + +// FKCascadeType indicates in which (delete/update) statements. +type FKCascadeType int8 + +const ( + // FKCascadeOnDelete indicates in delete statement. + FKCascadeOnDelete FKCascadeType = 1 + // FKCascadeOnUpdate indicates in update statement. + FKCascadeOnUpdate FKCascadeType = 2 + + emptyFkCheckSize = int64(unsafe.Sizeof(FKCheck{})) + emptyFkCascadeSize = int64(unsafe.Sizeof(FKCascade{})) +) + +// AccessObject implements dataAccesser interface. +func (f *FKCheck) AccessObject() AccessObject { + if f.Idx == nil { + return OtherAccessObject(fmt.Sprintf("table:%s", f.Tbl.Meta().Name)) + } + return OtherAccessObject(fmt.Sprintf("table:%s, index:%s", f.Tbl.Meta().Name, f.Idx.Meta().Name)) +} + +// OperatorInfo implements dataAccesser interface. +func (f *FKCheck) OperatorInfo(bool) string { + if f.FK != nil { + return fmt.Sprintf("foreign_key:%s, check_exist", f.FK.Name) + } + if f.ReferredFK != nil { + return fmt.Sprintf("foreign_key:%s, check_not_exist", f.ReferredFK.ChildFKName) + } + return "" +} + +// ExplainInfo implement Plan interface. +func (f *FKCheck) ExplainInfo() string { + return f.AccessObject().String() + ", " + f.OperatorInfo(false) +} + +// MemoryUsage return the memory usage of FKCheck +func (f *FKCheck) MemoryUsage() (sum int64) { + if f == nil { + return + } + + sum = emptyFkCheckSize + for _, cis := range f.Cols { + sum += cis.MemoryUsage() + } + return +} + +// AccessObject implements dataAccesser interface. +func (f *FKCascade) AccessObject() AccessObject { + if f.FKIdx == nil { + return OtherAccessObject(fmt.Sprintf("table:%s", f.ChildTable.Meta().Name)) + } + return OtherAccessObject(fmt.Sprintf("table:%s, index:%s", f.ChildTable.Meta().Name, f.FKIdx.Name)) +} + +// OperatorInfo implements dataAccesser interface. +func (f *FKCascade) OperatorInfo(bool) string { + switch f.Tp { + case FKCascadeOnDelete: + return fmt.Sprintf("foreign_key:%s, on_delete:%s", f.FK.Name, model.ReferOptionType(f.FK.OnDelete).String()) + case FKCascadeOnUpdate: + return fmt.Sprintf("foreign_key:%s, on_update:%s", f.FK.Name, model.ReferOptionType(f.FK.OnUpdate).String()) + } + return "" +} + +// ExplainInfo implement Plan interface. +func (f *FKCascade) ExplainInfo() string { + return f.AccessObject().String() + ", " + f.OperatorInfo(false) +} + +// MemoryUsage return the memory usage of FKCascade +func (f *FKCascade) MemoryUsage() (sum int64) { + if f == nil { + return + } + sum = emptyFkCascadeSize + return +} + +func (p *Insert) buildOnInsertFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string) error { + if !ctx.GetSessionVars().ForeignKeyChecks { + return nil + } + tblInfo := p.Table.Meta() + fkChecks := make([]*FKCheck, 0, len(tblInfo.ForeignKeys)) + fkCascades := make([]*FKCascade, 0, len(tblInfo.ForeignKeys)) + updateCols := p.buildOnDuplicateUpdateColumns() + if len(updateCols) > 0 { + referredFKChecks, referredFKCascades, err := buildOnUpdateReferredFKTriggers(ctx, is, dbName, tblInfo, updateCols) + if err != nil { + return err + } + if len(referredFKChecks) > 0 { + fkChecks = append(fkChecks, referredFKChecks...) + } + if len(referredFKCascades) > 0 { + fkCascades = append(fkCascades, referredFKCascades...) + } + } else if p.IsReplace { + referredFKChecks, referredFKCascades, err := p.buildOnReplaceReferredFKTriggers(ctx, is, dbName, tblInfo) + if err != nil { + return err + } + if len(referredFKChecks) > 0 { + fkChecks = append(fkChecks, referredFKChecks...) + } + if len(referredFKCascades) > 0 { + fkCascades = append(fkCascades, referredFKCascades...) + } + } + for _, fk := range tblInfo.ForeignKeys { + if fk.Version < 1 { + continue + } + failedErr := ErrNoReferencedRow2.FastGenByArgs(fk.String(dbName, tblInfo.Name.L)) + fkCheck, err := buildFKCheckOnModifyChildTable(ctx, is, fk, failedErr) + if err != nil { + return err + } + if fkCheck != nil { + fkChecks = append(fkChecks, fkCheck) + } + } + p.FKChecks = fkChecks + p.FKCascades = fkCascades + return nil +} + +func (p *Insert) buildOnDuplicateUpdateColumns() map[string]struct{} { + m := make(map[string]struct{}) + for _, assign := range p.OnDuplicate { + m[assign.ColName.L] = struct{}{} + } + return m +} + +func (*Insert) buildOnReplaceReferredFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo) ([]*FKCheck, []*FKCascade, error) { + referredFKs := is.GetTableReferredForeignKeys(dbName, tblInfo.Name.L) + fkChecks := make([]*FKCheck, 0, len(referredFKs)) + fkCascades := make([]*FKCascade, 0, len(referredFKs)) + for _, referredFK := range referredFKs { + fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnDelete) + if err != nil { + return nil, nil, err + } + if fkCheck != nil { + fkChecks = append(fkChecks, fkCheck) + } + if fkCascade != nil { + fkCascades = append(fkCascades, fkCascade) + } + } + return fkChecks, fkCascades, nil +} + +func (updt *Update) buildOnUpdateFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, tblID2table map[int64]table.Table) error { + if !ctx.GetSessionVars().ForeignKeyChecks { + return nil + } + tblID2UpdateColumns := updt.buildTbl2UpdateColumns() + fkChecks := make(map[int64][]*FKCheck) + fkCascades := make(map[int64][]*FKCascade) + for tid, tbl := range tblID2table { + tblInfo := tbl.Meta() + dbInfo, exist := is.SchemaByTable(tblInfo) + if !exist { + // Normally, it should never happen. Just check here to avoid panic here. + return infoschema.ErrDatabaseNotExists + } + updateCols := tblID2UpdateColumns[tid] + if len(updateCols) == 0 { + continue + } + referredFKChecks, referredFKCascades, err := buildOnUpdateReferredFKTriggers(ctx, is, dbInfo.Name.L, tblInfo, updateCols) + if err != nil { + return err + } + if len(referredFKChecks) > 0 { + fkChecks[tid] = append(fkChecks[tid], referredFKChecks...) + } + if len(referredFKCascades) > 0 { + fkCascades[tid] = append(fkCascades[tid], referredFKCascades...) + } + childFKChecks, err := buildOnUpdateChildFKChecks(ctx, is, dbInfo.Name.L, tblInfo, updateCols) + if err != nil { + return err + } + if len(childFKChecks) > 0 { + fkChecks[tid] = append(fkChecks[tid], childFKChecks...) + } + } + updt.FKChecks = fkChecks + updt.FKCascades = fkCascades + return nil +} + +func (del *Delete) buildOnDeleteFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, tblID2table map[int64]table.Table) error { + if !ctx.GetSessionVars().ForeignKeyChecks { + return nil + } + fkChecks := make(map[int64][]*FKCheck) + fkCascades := make(map[int64][]*FKCascade) + for tid, tbl := range tblID2table { + tblInfo := tbl.Meta() + dbInfo, exist := is.SchemaByTable(tblInfo) + if !exist { + return infoschema.ErrDatabaseNotExists + } + referredFKs := is.GetTableReferredForeignKeys(dbInfo.Name.L, tblInfo.Name.L) + for _, referredFK := range referredFKs { + fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnDelete) + if err != nil { + return err + } + if fkCheck != nil { + fkChecks[tid] = append(fkChecks[tid], fkCheck) + } + if fkCascade != nil { + fkCascades[tid] = append(fkCascades[tid], fkCascade) + } + } + } + del.FKChecks = fkChecks + del.FKCascades = fkCascades + return nil +} + +func buildOnUpdateReferredFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo, updateCols map[string]struct{}) ([]*FKCheck, []*FKCascade, error) { + referredFKs := is.GetTableReferredForeignKeys(dbName, tblInfo.Name.L) + fkChecks := make([]*FKCheck, 0, len(referredFKs)) + fkCascades := make([]*FKCascade, 0, len(referredFKs)) + for _, referredFK := range referredFKs { + if !isMapContainAnyCols(updateCols, referredFK.Cols...) { + continue + } + fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnUpdate) + if err != nil { + return nil, nil, err + } + if fkCheck != nil { + fkChecks = append(fkChecks, fkCheck) + } + if fkCascade != nil { + fkCascades = append(fkCascades, fkCascade) + } + } + return fkChecks, fkCascades, nil +} + +func buildOnUpdateChildFKChecks(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo, updateCols map[string]struct{}) ([]*FKCheck, error) { + fkChecks := make([]*FKCheck, 0, len(tblInfo.ForeignKeys)) + for _, fk := range tblInfo.ForeignKeys { + if fk.Version < 1 { + continue + } + if !isMapContainAnyCols(updateCols, fk.Cols...) { + continue + } + failedErr := ErrNoReferencedRow2.FastGenByArgs(fk.String(dbName, tblInfo.Name.L)) + fkCheck, err := buildFKCheckOnModifyChildTable(ctx, is, fk, failedErr) + if err != nil { + return nil, err + } + if fkCheck != nil { + fkChecks = append(fkChecks, fkCheck) + } + } + return fkChecks, nil +} + +func (updt *Update) buildTbl2UpdateColumns() map[int64]map[string]struct{} { + colsInfo := GetUpdateColumnsInfo(updt.tblID2Table, updt.TblColPosInfos, len(updt.SelectPlan.Schema().Columns)) + tblID2UpdateColumns := make(map[int64]map[string]struct{}) + for _, assign := range updt.OrderedList { + col := colsInfo[assign.Col.Index] + for _, content := range updt.TblColPosInfos { + if assign.Col.Index >= content.Start && assign.Col.Index < content.End { + if _, ok := tblID2UpdateColumns[content.TblID]; !ok { + tblID2UpdateColumns[content.TblID] = make(map[string]struct{}) + } + tblID2UpdateColumns[content.TblID][col.Name.L] = struct{}{} + break + } + } + } + for tid, tbl := range updt.tblID2Table { + updateCols := tblID2UpdateColumns[tid] + if len(updateCols) == 0 { + continue + } + for _, col := range tbl.WritableCols() { + if !col.IsGenerated() || !col.GeneratedStored { + continue + } + for depCol := range col.Dependences { + if _, ok := updateCols[depCol]; ok { + tblID2UpdateColumns[tid][col.Name.L] = struct{}{} + } + } + } + } + return tblID2UpdateColumns +} + +func buildOnDeleteOrUpdateFKTrigger(ctx sessionctx.Context, is infoschema.InfoSchema, referredFK *model.ReferredFKInfo, tp FKCascadeType) (*FKCheck, *FKCascade, error) { + childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) + if err != nil { + return nil, nil, nil + } + fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) + if fk == nil || fk.Version < 1 { + return nil, nil, nil + } + var fkReferOption model.ReferOptionType + if fk.State != model.StatePublic { + fkReferOption = model.ReferOptionRestrict + } else { + switch tp { + case FKCascadeOnDelete: + fkReferOption = model.ReferOptionType(fk.OnDelete) + case FKCascadeOnUpdate: + fkReferOption = model.ReferOptionType(fk.OnUpdate) + } + } + switch fkReferOption { + case model.ReferOptionCascade, model.ReferOptionSetNull: + fkCascade, err := buildFKCascade(ctx, tp, referredFK, childTable, fk) + return nil, fkCascade, err + default: + fkCheck, err := buildFKCheckForReferredFK(ctx, childTable, fk, referredFK) + return fkCheck, nil, err + } +} + +func isMapContainAnyCols(colsMap map[string]struct{}, cols ...model.CIStr) bool { + for _, col := range cols { + _, exist := colsMap[col.L] + if exist { + return true + } + } + return false +} + +func buildFKCheckOnModifyChildTable(ctx sessionctx.Context, is infoschema.InfoSchema, fk *model.FKInfo, failedErr error) (*FKCheck, error) { + referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) + if err != nil { + return nil, nil + } + fkCheck, err := buildFKCheck(ctx, referTable, fk.RefCols, failedErr) + if err != nil { + return nil, err + } + fkCheck.CheckExist = true + fkCheck.FK = fk + return fkCheck, nil +} + +func buildFKCheckForReferredFK(ctx sessionctx.Context, childTable table.Table, fk *model.FKInfo, referredFK *model.ReferredFKInfo) (*FKCheck, error) { + failedErr := ErrRowIsReferenced2.GenWithStackByArgs(fk.String(referredFK.ChildSchema.L, referredFK.ChildTable.L)) + fkCheck, err := buildFKCheck(ctx, childTable, fk.Cols, failedErr) + if err != nil { + return nil, err + } + fkCheck.CheckExist = false + fkCheck.ReferredFK = referredFK + return fkCheck, nil +} + +func buildFKCheck(ctx sessionctx.Context, tbl table.Table, cols []model.CIStr, failedErr error) (*FKCheck, error) { + tblInfo := tbl.Meta() + if tblInfo.PKIsHandle && len(cols) == 1 { + refColInfo := model.FindColumnInfo(tblInfo.Columns, cols[0].L) + if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { + return FKCheck{ + Tbl: tbl, + IdxIsPrimaryKey: true, + IdxIsExclusive: true, + FailedErr: failedErr, + }.Init(ctx), nil + } + } + + referTbIdxInfo := model.FindIndexByColumns(tblInfo, tblInfo.Indices, cols...) + if referTbIdxInfo == nil { + return nil, failedErr + } + var tblIdx table.Index + for _, idx := range tbl.Indices() { + if idx.Meta().ID == referTbIdxInfo.ID { + tblIdx = idx + } + } + if tblIdx == nil { + return nil, failedErr + } + + return FKCheck{ + Tbl: tbl, + Idx: tblIdx, + IdxIsExclusive: len(cols) == len(referTbIdxInfo.Columns), + IdxIsPrimaryKey: referTbIdxInfo.Primary && tblInfo.IsCommonHandle, + FailedErr: failedErr, + }.Init(ctx), nil +} + +func buildFKCascade(ctx sessionctx.Context, tp FKCascadeType, referredFK *model.ReferredFKInfo, childTable table.Table, fk *model.FKInfo) (*FKCascade, error) { + cols := make([]*model.ColumnInfo, len(fk.Cols)) + childTableColumns := childTable.Meta().Columns + for i, c := range fk.Cols { + col := model.FindColumnInfo(childTableColumns, c.L) + if col == nil { + return nil, errors.Errorf("foreign key column %s is not found in table %s", c.L, childTable.Meta().Name) + } + cols[i] = col + } + fkCascade := FKCascade{ + Tp: tp, + ReferredFK: referredFK, + ChildTable: childTable, + FK: fk, + FKCols: cols, + }.Init(ctx) + if childTable.Meta().PKIsHandle && len(cols) == 1 { + refColInfo := model.FindColumnInfo(childTableColumns, cols[0].Name.L) + if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { + return fkCascade, nil + } + } + indexForFK := model.FindIndexByColumns(childTable.Meta(), childTable.Meta().Indices, fk.Cols...) + if indexForFK == nil { + return nil, errors.Errorf("Missing index for '%s' foreign key columns in the table '%s'", fk.Name, childTable.Meta().Name) + } + fkCascade.FKIdx = indexForFK + return fkCascade, nil +} diff --git a/planner/core/fragment.go b/pkg/planner/core/fragment.go similarity index 97% rename from planner/core/fragment.go rename to pkg/planner/core/fragment.go index b361f13afe123..c74598eba0c0f 100644 --- a/planner/core/fragment.go +++ b/pkg/planner/core/fragment.go @@ -23,20 +23,20 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/distsql" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/distsql" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/planner/core/fragment_test.go b/pkg/planner/core/fragment_test.go similarity index 100% rename from planner/core/fragment_test.go rename to pkg/planner/core/fragment_test.go diff --git a/planner/core/handle_cols.go b/pkg/planner/core/handle_cols.go similarity index 95% rename from planner/core/handle_cols.go rename to pkg/planner/core/handle_cols.go index 4848f47697463..c745e3d1fb4c3 100644 --- a/planner/core/handle_cols.go +++ b/pkg/planner/core/handle_cols.go @@ -18,17 +18,17 @@ import ( "strings" "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/size" ) // HandleCols is the interface that holds handle columns. diff --git a/planner/core/hashcode.go b/pkg/planner/core/hashcode.go similarity index 98% rename from planner/core/hashcode.go rename to pkg/planner/core/hashcode.go index fe8980fa82405..c7854fe5cb7d1 100644 --- a/planner/core/hashcode.go +++ b/pkg/planner/core/hashcode.go @@ -19,7 +19,7 @@ import ( "encoding/binary" "slices" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/util/plancodec" ) func encodeIntAsUint32(result []byte, value int) []byte { diff --git a/planner/core/hints.go b/pkg/planner/core/hints.go similarity index 97% rename from planner/core/hints.go rename to pkg/planner/core/hints.go index 15aa171e4fdc6..b73045caa1de6 100644 --- a/planner/core/hints.go +++ b/pkg/planner/core/hints.go @@ -15,11 +15,11 @@ package core import ( - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - utilhint "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + utilhint "github.com/pingcap/tidb/pkg/util/hint" ) // GenHintsFromFlatPlan generates hints from a FlatPhysicalPlan. diff --git a/planner/core/indexmerge_intersection_test.go b/pkg/planner/core/indexmerge_intersection_test.go similarity index 98% rename from planner/core/indexmerge_intersection_test.go rename to pkg/planner/core/indexmerge_intersection_test.go index 6a674112db984..209cb4af00401 100644 --- a/planner/core/indexmerge_intersection_test.go +++ b/pkg/planner/core/indexmerge_intersection_test.go @@ -18,9 +18,9 @@ import ( "regexp" "testing" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" ) diff --git a/planner/core/indexmerge_path.go b/pkg/planner/core/indexmerge_path.go similarity index 98% rename from planner/core/indexmerge_path.go rename to pkg/planner/core/indexmerge_path.go index 60ea8e83a74ee..814c480c1b61f 100644 --- a/planner/core/indexmerge_path.go +++ b/pkg/planner/core/indexmerge_path.go @@ -19,20 +19,20 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" "go.uber.org/zap" ) diff --git a/planner/core/indexmerge_path_test.go b/pkg/planner/core/indexmerge_path_test.go similarity index 99% rename from planner/core/indexmerge_path_test.go rename to pkg/planner/core/indexmerge_path_test.go index ea16652369d8e..49acb9ee0b787 100644 --- a/planner/core/indexmerge_path_test.go +++ b/pkg/planner/core/indexmerge_path_test.go @@ -20,7 +20,7 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" ) func TestMVIndexRandom(t *testing.T) { diff --git a/planner/core/indexmerge_test.go b/pkg/planner/core/indexmerge_test.go similarity index 91% rename from planner/core/indexmerge_test.go rename to pkg/planner/core/indexmerge_test.go index 58e739e096161..f0af902ff9607 100644 --- a/planner/core/indexmerge_test.go +++ b/pkg/planner/core/indexmerge_test.go @@ -18,13 +18,13 @@ import ( "context" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) diff --git a/planner/core/initialize.go b/pkg/planner/core/initialize.go similarity index 98% rename from planner/core/initialize.go rename to pkg/planner/core/initialize.go index ebcc1be1ef2fd..f48eb8296c59b 100644 --- a/planner/core/initialize.go +++ b/pkg/planner/core/initialize.go @@ -15,15 +15,15 @@ package core import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/planner/core/internal/base" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/size" ) // Init initializes LogicalAggregation. diff --git a/pkg/planner/core/integration_partition_test.go b/pkg/planner/core/integration_partition_test.go new file mode 100644 index 0000000000000..ed2d15bdad374 --- /dev/null +++ b/pkg/planner/core/integration_partition_test.go @@ -0,0 +1,315 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "bytes" + "fmt" + "math/rand" + "strconv" + "testing" + + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/stretchr/testify/require" +) + +func TestListPartitionOrderLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database list_partition_order_limit") + tk.MustExec("use list_partition_order_limit") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tnormal (a int, b int)`) + + vals := "" + for i := 0; i < 50; i++ { + if vals != "" { + vals += ", " + } + vals += fmt.Sprintf("(%v, %v)", i*2+rand.Intn(2), i*2+rand.Intn(2)) + } + tk.MustExec(`insert into tlist values ` + vals) + tk.MustExec(`insert into tcollist values ` + vals) + tk.MustExec(`insert into tnormal values ` + vals) + + for _, orderCol := range []string{"a", "b"} { + for _, limitNum := range []string{"1", "5", "20", "100"} { + randCond := fmt.Sprintf("where %v > %v", []string{"a", "b"}[rand.Intn(2)], rand.Intn(100)) + rs := tk.MustQuery(fmt.Sprintf(`select * from tnormal %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + rsDynamic := tk.MustQuery(fmt.Sprintf(`select * from tlist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + rsStatic := tk.MustQuery(fmt.Sprintf(`select * from tlist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + rsColDynamic := tk.MustQuery(fmt.Sprintf(`select * from tcollist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + rsColStatic := tk.MustQuery(fmt.Sprintf(`select * from tcollist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() + + rs.Check(rsDynamic.Rows()) + rs.Check(rsStatic.Rows()) + rs.Check(rsColDynamic.Rows()) + rs.Check(rsColStatic.Rows()) + } + } +} + +func TestListPartitionAgg(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database list_partition_agg") + tk.MustExec("use list_partition_agg") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tnormal (a int, b int)`) + + vals := "" + for i := 0; i < 50; i++ { + if vals != "" { + vals += ", " + } + vals += fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100)) + } + tk.MustExec(`insert into tlist values ` + vals) + tk.MustExec(`insert into tcollist values ` + vals) + tk.MustExec(`insert into tnormal values ` + vals) + + for _, aggFunc := range []string{"min", "max", "sum", "count"} { + c1, c2 := "a", "b" + for i := 0; i < 2; i++ { + rs := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tnormal group by %v`, c1, aggFunc, c2, c1)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + rsDynamic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tlist group by %v`, c1, aggFunc, c2, c1)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + rsStatic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tlist group by %v`, c1, aggFunc, c2, c1)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + rsColDynamic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tcollist group by %v`, c1, aggFunc, c2, c1)).Sort() + + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + rsColStatic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tcollist group by %v`, c1, aggFunc, c2, c1)).Sort() + + rs.Check(rsDynamic.Rows()) + rs.Check(rsStatic.Rows()) + rs.Check(rsColDynamic.Rows()) + rs.Check(rsColStatic.Rows()) + } + } +} + +func TestListPartitionPrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.SetSession(se) + tk.MustExec("create database list_partition_pri") + tk.MustExec("use list_partition_pri") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + tk.MustExec(`create table tlist (a int) partition by list (a) (partition p0 values in (0), partition p1 values in (1))`) + + tk.MustExec(`create user 'priv_test'@'%'`) + tk.MustExec(`grant select on list_partition_pri.tlist to 'priv_test'`) + + tk1 := testkit.NewTestKit(t, store) + se, err = session.CreateSession4Test(store) + require.NoError(t, err) + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "priv_test", Hostname: "%"}, nil, nil, nil)) + tk1.SetSession(se) + tk1.MustExec(`use list_partition_pri`) + err = tk1.ExecToErr(`alter table tlist truncate partition p0`) + require.Error(t, err) + require.Contains(t, err.Error(), "denied") + err = tk1.ExecToErr(`alter table tlist drop partition p0`) + require.Error(t, err) + require.Contains(t, err.Error(), "denied") + err = tk1.ExecToErr(`alter table tlist add partition (partition p2 values in (2))`) + require.Error(t, err) + require.Contains(t, err.Error(), "denied") + err = tk1.ExecToErr(`insert into tlist values (1)`) + require.Error(t, err) + require.Contains(t, err.Error(), "denied") +} + +func TestListPartitionView(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database list_partition_view") + tk.MustExec("use list_partition_view") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + + tk.MustExec(`create table tlist (a int, b int) partition by list (a) ( + partition p0 values in (0, 1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8, 9), + partition p2 values in (10, 11, 12, 13, 14))`) + tk.MustExec(`create definer='root'@'localhost' view vlist as select a*2 as a2, a+b as ab from tlist`) + tk.MustExec(`create table tnormal (a int, b int)`) + tk.MustExec(`create definer='root'@'localhost' view vnormal as select a*2 as a2, a+b as ab from tnormal`) + for i := 0; i < 10; i++ { + a, b := rand.Intn(15), rand.Intn(100) + tk.MustExec(fmt.Sprintf(`insert into tlist values (%v, %v)`, a, b)) + tk.MustExec(fmt.Sprintf(`insert into tnormal values (%v, %v)`, a, b)) + } + + r1 := tk.MustQuery(`select * from vlist`).Sort() + r2 := tk.MustQuery(`select * from vnormal`).Sort() + r1.Check(r2.Rows()) + + tk.MustExec(`create table tcollist (a int, b int) partition by list columns (a) ( + partition p0 values in (0, 1, 2, 3, 4), + partition p1 values in (5, 6, 7, 8, 9), + partition p2 values in (10, 11, 12, 13, 14))`) + tk.MustExec(`create definer='root'@'localhost' view vcollist as select a*2 as a2, a+b as ab from tcollist`) + tk.MustExec(`truncate tnormal`) + for i := 0; i < 10; i++ { + a, b := rand.Intn(15), rand.Intn(100) + tk.MustExec(fmt.Sprintf(`insert into tcollist values (%v, %v)`, a, b)) + tk.MustExec(fmt.Sprintf(`insert into tnormal values (%v, %v)`, a, b)) + } + + r1 = tk.MustQuery(`select * from vcollist`).Sort() + r2 = tk.MustQuery(`select * from vnormal`).Sort() + r1.Check(r2.Rows()) +} + +func TestListPartitionRandomTransaction(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database list_partition_random_tran") + tk.MustExec("use list_partition_random_tran") + tk.MustExec("drop table if exists tlist") + tk.MustExec(`set tidb_enable_list_partition = 1`) + + tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + + ` partition p0 values in ` + genListPartition(0, 20) + + `, partition p1 values in ` + genListPartition(20, 40) + + `, partition p2 values in ` + genListPartition(40, 60) + + `, partition p3 values in ` + genListPartition(60, 80) + + `, partition p4 values in ` + genListPartition(80, 100) + `)`) + tk.MustExec(`create table tnormal (a int, b int)`) + + inTrans := false + for i := 0; i < 50; i++ { + switch rand.Intn(4) { + case 0: // begin + if inTrans { + continue + } + tk.MustExec(`begin`) + inTrans = true + case 1: // sel + cond := fmt.Sprintf("where a>=%v and a<=%v", rand.Intn(50), 50+rand.Intn(50)) + rnormal := tk.MustQuery(`select * from tnormal ` + cond).Sort().Rows() + tk.MustQuery(`select * from tlist ` + cond).Sort().Check(rnormal) + tk.MustQuery(`select * from tcollist ` + cond).Sort().Check(rnormal) + case 2: // insert + values := fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100)) + tk.MustExec(`insert into tnormal values ` + values) + tk.MustExec(`insert into tlist values ` + values) + tk.MustExec(`insert into tcollist values ` + values) + case 3: // commit or rollback + if !inTrans { + continue + } + tk.MustExec([]string{"commit", "rollback"}[rand.Intn(2)]) + inTrans = false + } + } +} + +func genListPartition(begin, end int) string { + buf := &bytes.Buffer{} + buf.WriteString("(") + for i := begin; i < end-1; i++ { + buf.WriteString(fmt.Sprintf("%v, ", i)) + } + buf.WriteString(fmt.Sprintf("%v)", end-1)) + return buf.String() +} + +func BenchmarkPartitionRangeColumns(b *testing.B) { + store := testkit.CreateMockStore(b) + + tk := testkit.NewTestKit(b, store) + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("create schema rcb") + tk.MustExec("use rcb") + tk.MustExec(`create table t (` + + `c1 int primary key clustered,` + + `c2 varchar(255))` + + ` partition by range columns (c1)` + + ` interval (10000) first partition less than (10000) last partition less than (5120000)`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + val := strconv.FormatInt(int64(rand.Intn(5120000)), 10) + tk.MustExec("select * from t where c1 = " + val) + //tk.MustExec("insert ignore into t values (" + val + ",'" + val + "')") + } + b.StopTimer() +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkPartitionRangeColumns, + ) +} diff --git a/pkg/planner/core/integration_test.go b/pkg/planner/core/integration_test.go new file mode 100644 index 0000000000000..1a6688649ecea --- /dev/null +++ b/pkg/planner/core/integration_test.go @@ -0,0 +1,2570 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "bytes" + "fmt" + "regexp" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestNoneAccessPathsFoundByIsolationRead(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key)") + + tk.MustExec("select * from t") + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") + + // Don't filter mysql.SystemDB by isolation read. + tk.MustQuery("explain format = 'brief' select * from mysql.stats_meta").Check(testkit.Rows( + "TableReader 10000.00 root data:TableFullScan", + "└─TableFullScan 10000.00 cop[tikv] table:stats_meta keep order:false, stats:pseudo")) + + _, err := tk.Exec("select * from t") + require.EqualError(t, err, "[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tikv'. Please check tiflash replica.") + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash, tikv'") + tk.MustExec("select * from t") + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.IsolationRead.Engines = []string{"tiflash"} + }) + // Change instance config doesn't affect isolation read. + tk.MustExec("select * from t") +} + +func TestAggPushDownEngine(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b varchar(20))") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") + + tk.MustQuery("explain format = 'brief' select approx_count_distinct(a) from t").Check(testkit.Rows( + "StreamAgg 1.00 root funcs:approx_count_distinct(Column#5)->Column#3", + "└─TableReader 1.00 root data:StreamAgg", + " └─StreamAgg 1.00 batchCop[tiflash] funcs:approx_count_distinct(test.t.a)->Column#5", + " └─TableFullScan 10000.00 batchCop[tiflash] table:t keep order:false, stats:pseudo")) + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tikv'") + + tk.MustQuery("explain format = 'brief' select approx_count_distinct(a) from t").Check(testkit.Rows( + "HashAgg 1.00 root funcs:approx_count_distinct(test.t.a)->Column#3", + "└─TableReader 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) +} + +func TestIssue15110(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists crm_rd_150m") + tk.MustExec(`CREATE TABLE crm_rd_150m ( + product varchar(256) DEFAULT NULL, + uks varchar(16) DEFAULT NULL, + brand varchar(256) DEFAULT NULL, + cin varchar(16) DEFAULT NULL, + created_date timestamp NULL DEFAULT NULL, + quantity int(11) DEFAULT NULL, + amount decimal(11,0) DEFAULT NULL, + pl_date timestamp NULL DEFAULT NULL, + customer_first_date timestamp NULL DEFAULT NULL, + recent_date timestamp NULL DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;`) + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "crm_rd_150m" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("explain format = 'brief' SELECT count(*) FROM crm_rd_150m dataset_48 WHERE (CASE WHEN (month(dataset_48.customer_first_date)) <= 30 THEN '新客' ELSE NULL END) IS NOT NULL;") +} + +func TestKeepOrderHintWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int, b int, index idx_a(a));") + + // create binding for order_index hint + tk.MustExec("select * from t1 where a<10 order by a limit 1;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select * from t1 where a<10 order by a limit 1 using select /*+ order_index(t1, idx_a) */ * from t1 where a<10 order by a limit 1;") + tk.MustExec("select * from t1 where a<10 order by a limit 1;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from `test` . `t1` where `a` < ? order by `a` limit ?") + require.Equal(t, res[0][1], "SELECT /*+ order_index(`t1` `idx_a`)*/ * FROM `test`.`t1` WHERE `a` < 10 ORDER BY `a` LIMIT 1") + + tk.MustExec("drop global binding for select * from t1 where a<10 order by a limit 1;") + tk.MustExec("select * from t1 where a<10 order by a limit 1;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) + + // create binding for no_order_index hint + tk.MustExec("create global binding for select * from t1 where a<10 order by a limit 1 using select /*+ no_order_index(t1, idx_a) */ * from t1 where a<10 order by a limit 1;") + tk.MustExec("select * from t1 where a<10 order by a limit 1;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from `test` . `t1` where `a` < ? order by `a` limit ?") + require.Equal(t, res[0][1], "SELECT /*+ no_order_index(`t1` `idx_a`)*/ * FROM `test`.`t1` WHERE `a` < 10 ORDER BY `a` LIMIT 1") + + tk.MustExec("drop global binding for select * from t1 where a<10 order by a limit 1;") + tk.MustExec("select * from t1 where a<10 order by a limit 1;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) +} + +func TestViewHintWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop view if exists v, v1") + tk.MustExec("drop table if exists t, t1, t2, t3") + tk.MustExec("create table t(a int, b int);") + tk.MustExec("create table t1(a int, b int);") + tk.MustExec("create table t2(a int, b int);") + tk.MustExec("create table t3(a int, b int)") + tk.MustExec("create definer='root'@'localhost' view v as select t.a, t.b from t join (select count(*) as a from t1 join t2 join t3 where t1.b=t2.b and t2.a = t3.a group by t2.a) tt on t.a = tt.a;") + tk.MustExec("create definer='root'@'localhost' view v1 as select t.a, t.b from t join (select count(*) as a from t1 join v on t1.b=v.b group by v.a) tt on t.a = tt.a;") + tk.MustExec("create definer='root'@'localhost' view v2 as select t.a, t.b from t join (select count(*) as a from t1 join v1 on t1.b=v1.b group by v1.a) tt on t.a = tt.a;") + + tk.MustExec("select * from v2") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select * from v2 using select /*+ qb_name(qb_v_2, v2.v1@sel_2 .v@sel_2 .@sel_2), merge_join(t1@qb_v_2), stream_agg(@qb_v_2), qb_name(qb_v_1, v2. v1@sel_2 .v@sel_2 .@sel_1), merge_join(t@qb_v_1) */ * from v2;") + tk.MustExec("select * from v2") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from `test` . `v2`") + require.Equal(t, res[0][1], "SELECT /*+ qb_name(`qb_v_2` , `v2`. `v1`@`sel_2`. `v`@`sel_2`. ``@`sel_2`) merge_join(`t1`@`qb_v_2`) stream_agg(@`qb_v_2`) qb_name(`qb_v_1` , `v2`. `v1`@`sel_2`. `v`@`sel_2`. ``@`sel_1`) merge_join(`t`@`qb_v_1`)*/ * FROM `test`.`v2`") + + tk.MustExec("drop global binding for select * from v2") + tk.MustExec("select * from v2") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) +} + +func TestPartitionPruningWithDateType(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a datetime) partition by range columns (a) (partition p1 values less than ('20000101'), partition p2 values less than ('2000-10-01'));") + tk.MustExec("insert into t values ('20000201'), ('19000101');") + + // cannot get the statistical information immediately + // tk.MustQuery(`SELECT PARTITION_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't';`).Check(testkit.Rows("p1 1", "p2 1")) + str := tk.MustQuery(`desc select * from t where a < '2000-01-01';`).Rows()[0][3].(string) + require.True(t, strings.Contains(str, "partition:p1")) +} + +func TestPartitionPruningForEQ(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a datetime, b int) partition by range(weekday(a)) (partition p0 values less than(10), partition p1 values less than (100))") + + is := tk.Session().GetInfoSchema().(infoschema.InfoSchema) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + pt := tbl.(table.PartitionedTable) + query, err := expression.ParseSimpleExprWithTableInfo(tk.Session(), "a = '2020-01-01 00:00:00'", tbl.Meta()) + require.NoError(t, err) + dbName := model.NewCIStr(tk.Session().GetSessionVars().CurrentDB) + columns, names, err := expression.ColumnInfos2ColumnsAndNames(tk.Session(), dbName, tbl.Meta().Name, tbl.Meta().Cols(), tbl.Meta()) + require.NoError(t, err) + // Even the partition is not monotonous, EQ condition should be prune! + // select * from t where a = '2020-01-01 00:00:00' + res, err := core.PartitionPruning(tk.Session(), pt, []expression.Expression{query}, nil, columns, names) + require.NoError(t, err) + require.Len(t, res, 1) + require.Equal(t, 0, res[0]) +} + +func TestErrNoDB(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create user test") + _, err := tk.Exec("grant select on test1111 to test@'%'") + require.Equal(t, core.ErrNoDB, errors.Cause(err)) + _, err = tk.Exec("grant select on * to test@'%'") + require.Equal(t, core.ErrNoDB, errors.Cause(err)) + _, err = tk.Exec("revoke select on * from test@'%'") + require.Equal(t, core.ErrNoDB, errors.Cause(err)) + tk.MustExec("use test") + tk.MustExec("create table test1111 (id int)") + tk.MustExec("grant select on test1111 to test@'%'") +} + +func TestNotReadOnlySQLOnTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b varchar(20))") + tk.MustExec(`set @@tidb_isolation_read_engines = "tiflash"`) + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + err := tk.ExecToErr("select * from t for update") + require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) + + err = tk.ExecToErr("insert into t select * from t") + require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) + + tk.MustExec("prepare stmt_insert from 'insert into t select * from t where t.a = ?'") + tk.MustExec("set @a=1") + err = tk.ExecToErr("execute stmt_insert using @a") + require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) +} + +func TestTimeToSecPushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a time(4))") + tk.MustExec("insert into t values('700:10:10.123456')") + tk.MustExec("insert into t values('20:20:20')") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "10000.00", "root", " MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "10000.00", "mpp[tiflash]", " ExchangeType: PassThrough"}, + {" └─Projection_4", "10000.00", "mpp[tiflash]", " time_to_sec(test.t.a)->Column#3"}, + {" └─TableFullScan_8", "10000.00", "mpp[tiflash]", "table:t", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select time_to_sec(a) from t;").Check(rows) +} + +func TestRightShiftPushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(2147483647, 32)") + tk.MustExec("insert into t values(12, 2)") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "rightshift(test.t.a, test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select a >> b from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestBitColumnPushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=1") + tk.MustExec("create table t1(a bit(8), b int)") + tk.MustExec("create table t2(a bit(8), b int)") + tk.MustExec("insert into t1 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)") + tk.MustExec("insert into t2 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)") + sql := "select b from t1 where t1.b > (select min(t2.b) from t2 where t2.a < t1.a)" + tk.MustQuery(sql).Sort().Check(testkit.Rows("2", "2", "3", "3", "4", "4")) + rows := [][]interface{}{ + {"Projection_15", "root", "test.t1.b"}, + {"└─Apply_17", "root", "CARTESIAN inner join, other cond:gt(test.t1.b, Column#7)"}, + {" ├─TableReader_20(Build)", "root", "data:Selection_19"}, + {" │ └─Selection_19", "cop[tikv]", "not(isnull(test.t1.b))"}, + {" │ └─TableFullScan_18", "cop[tikv]", "keep order:false, stats:pseudo"}, + {" └─Selection_21(Probe)", "root", "not(isnull(Column#7))"}, + {" └─StreamAgg_23", "root", "funcs:min(test.t2.b)->Column#7"}, + {" └─TopN_24", "root", "test.t2.b, offset:0, count:1"}, + {" └─TableReader_32", "root", "data:TopN_31"}, + {" └─TopN_31", "cop[tikv]", "test.t2.b, offset:0, count:1"}, + {" └─Selection_30", "cop[tikv]", "lt(test.t2.a, test.t1.a), not(isnull(test.t2.b))"}, + {" └─TableFullScan_29", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + tk.MustExec("insert t1 values ('A', 1);") + sql = "select a from t1 where ascii(a)=65" + tk.MustQuery(sql).Check(testkit.Rows("A")) + rows = [][]interface{}{ + {"TableReader_7", "root", "data:Selection_6"}, + {"└─Selection_6", "cop[tikv]", "eq(ascii(cast(test.t1.a, var_string(1))), 65)"}, + {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = `eq(concat(cast(test.t1.a, var_string(1)), "A"), "AA")` + sql = "select a from t1 where concat(a, 'A')='AA'" + tk.MustQuery(sql).Check(testkit.Rows("A")) + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = `eq(cast(test.t1.a, binary(1)), "A")` + sql = "select a from t1 where binary a='A'" + tk.MustQuery(sql).Check(testkit.Rows("A")) + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = `eq(cast(test.t1.a, var_string(1)), "A")` + sql = "select a from t1 where cast(a as char)='A'" + tk.MustQuery(sql).Check(testkit.Rows("A")) + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + tk.MustExec("insert into mysql.expr_pushdown_blacklist values('bit', 'tikv','');") + tk.MustExec("admin reload expr_pushdown_blacklist;") + rows = [][]interface{}{ + {"Selection_5", "root", `eq(cast(test.t1.a, var_string(1)), "A")`}, + {"└─TableReader_7", "root", "data:TableFullScan_6"}, + {" └─TableFullScan_6", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + sql = "select a from t1 where cast(a as char)='A'" + tk.MustQuery(sql).Check(testkit.Rows("A")) + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name='bit'") + tk.MustExec("admin reload expr_pushdown_blacklist;") + sql = "select a from t1 where ascii(a)=65" + tk.MustQuery(sql).Check(testkit.Rows("A")) + rows = [][]interface{}{ + {"TableReader_7", "root", "data:Selection_6"}, + {"└─Selection_6", "cop[tikv]", "eq(ascii(cast(test.t1.a, var_string(1))), 65)"}, + {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) + + // test collation + tk.MustExec("update mysql.tidb set VARIABLE_VALUE='True' where VARIABLE_NAME='new_collation_enabled'") + tk.MustQuery("SELECT VARIABLE_VALUE FROM mysql.tidb WHERE VARIABLE_NAME='new_collation_enabled';").Check( + testkit.Rows("True")) + tk.MustExec("create table t3 (a bit(8));") + tk.MustExec("insert into t3 values (65)") + tk.MustExec("SET NAMES utf8mb4 COLLATE utf8mb4_bin") + tk.MustQuery("select a from t3 where cast(a as char) = 'a'").Check(testkit.Rows()) + tk.MustExec("SET NAMES utf8mb4 COLLATE utf8mb4_general_ci") + tk.MustQuery("select a from t3 where cast(a as char) = 'a'").Check(testkit.Rows("A")) +} + +func TestSysdatePushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id int signed, id2 int unsigned, c varchar(11), d datetime, b double, e bit(10))") + tk.MustExec("insert into t(id, id2, c, d) values (-1, 1, 'abc', '2021-12-12')") + rows := [][]interface{}{ + {"TableReader_7", "root", "data:Selection_6"}, + {"└─Selection_6", "cop[tikv]", "gt(test.t.d, sysdate())"}, + {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). + CheckAt([]int{0, 3, 6}, rows) + // assert sysdate isn't now after set global tidb_sysdate_is_now in the same session + tk.MustExec("set global tidb_sysdate_is_now='1'") + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). + CheckAt([]int{0, 3, 6}, rows) + + // assert sysdate is now after set global tidb_sysdate_is_now in the new session + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + now := time.Now() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) + rows[1][2] = fmt.Sprintf("gt(test.t.d, %v)", now.Format(time.DateTime)) + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). + CheckAt([]int{0, 3, 6}, rows) + failpoint.Disable("github.com/pingcap/tidb/pkg/expression/injectNow") + + // assert sysdate isn't now after set session tidb_sysdate_is_now false in the same session + tk.MustExec("set tidb_sysdate_is_now='0'") + rows[1][2] = "gt(test.t.d, sysdate())" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). + CheckAt([]int{0, 3, 6}, rows) +} + +func TestTimeScalarFunctionPushDownResult(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(col1 datetime, col2 datetime, y int(8), m int(8), d int(8)) CHARSET=utf8 COLLATE=utf8_general_ci;") + tk.MustExec("insert into t values ('2022-03-24 01:02:03.040506', '9999-12-31 23:59:59', 9999, 12, 31);") + testcases := []struct { + sql string + function string + }{ + { + sql: "select col1, hour(col1) from t where hour(col1)=hour('2022-03-24 01:02:03.040506');", + function: "hour", + }, + { + sql: "select col1, month(col1) from t where month(col1)=month('2022-03-24 01:02:03.040506');", + function: "month", + }, + { + sql: "select col1, minute(col1) from t where minute(col1)=minute('2022-03-24 01:02:03.040506');", + function: "minute", + }, + { + function: "second", + sql: "select col1, second(col1) from t where second(col1)=second('2022-03-24 01:02:03.040506');", + }, + { + function: "microsecond", + sql: "select col1, microsecond(col1) from t where microsecond(col1)=microsecond('2022-03-24 01:02:03.040506');", + }, + { + function: "dayName", + sql: "select col1, dayName(col1) from t where dayName(col1)=dayName('2022-03-24 01:02:03.040506');", + }, + { + function: "dayOfMonth", + sql: "select col1, dayOfMonth(col1) from t where dayOfMonth(col1)=dayOfMonth('2022-03-24 01:02:03.040506');", + }, + { + function: "dayOfWeek", + sql: "select col1, dayOfWeek(col1) from t where dayOfWeek(col1)=dayOfWeek('2022-03-24 01:02:03.040506');", + }, + { + function: "dayOfYear", + sql: "select col1, dayOfYear(col1) from t where dayOfYear(col1)=dayOfYear('2022-03-24 01:02:03.040506');", + }, + { + function: "Date", + sql: "select col1, Date(col1) from t where Date(col1)=Date('2022-03-24 01:02:03.040506');", + }, + { + function: "Week", + sql: "select col1, Week(col1) from t where Week(col1)=Week('2022-03-24 01:02:03.040506');", + }, + { + function: "time_to_sec", + sql: "select col1, time_to_sec (col1) from t where time_to_sec(col1)=time_to_sec('2022-03-24 01:02:03.040506');", + }, + { + function: "DateDiff", + sql: "select col1, DateDiff(col1, col2) from t where DateDiff(col1, col2)=DateDiff('2022-03-24 01:02:03.040506', '9999-12-31 23:59:59');", + }, + { + function: "MonthName", + sql: "select col1, MonthName(col1) from t where MonthName(col1)=MonthName('2022-03-24 01:02:03.040506');", + }, + { + function: "MakeDate", + sql: "select col1, MakeDate(9999, 31) from t where MakeDate(y, d)=MakeDate(9999, 31);", + }, + { + function: "MakeTime", + sql: "select col1, MakeTime(12, 12, 31) from t where MakeTime(m, m, d)=MakeTime(12, 12, 31);", + }, + } + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") + tk.MustExec("admin reload expr_pushdown_blacklist;") + for _, testcase := range testcases { + r1 := tk.MustQuery(testcase.sql).Rows() + tk.MustExec(fmt.Sprintf("insert into mysql.expr_pushdown_blacklist(name) values('%s');", testcase.function)) + tk.MustExec("admin reload expr_pushdown_blacklist;") + r2 := tk.MustQuery(testcase.sql).Rows() + require.EqualValues(t, r2, r1, testcase.sql) + } + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") + tk.MustExec("admin reload expr_pushdown_blacklist;") +} + +func TestNumberFunctionPushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int signed, b int unsigned,c double)") + tk.MustExec("insert into t values (-1,61,4.4)") + testcases := []struct { + sql string + function string + }{ + { + sql: "select a, mod(a,2) from t where mod(-1,2)=mod(a,2);", + function: "mod", + }, + { + sql: "select b, mod(b,2) from t where mod(61,2)=mod(b,2);", + function: "mod", + }, + { + sql: "select b,unhex(b) from t where unhex(61) = unhex(b)", + function: "unhex", + }, + { + sql: "select b, oct(b) from t where oct(61) = oct(b)", + function: "oct", + }, + { + sql: "select c, sin(c) from t where sin(4.4) = sin(c)", + function: "sin", + }, + { + sql: "select c, asin(c) from t where asin(4.4) = asin(c)", + function: "asin", + }, + { + sql: "select c, cos(c) from t where cos(4.4) = cos(c)", + function: "cos", + }, + { + sql: "select c, acos(c) from t where acos(4.4) = acos(c)", + function: "acos", + }, + { + sql: "select b,atan(b) from t where atan(61)=atan(b)", + function: "atan", + }, + { + sql: "select b, atan2(b, c) from t where atan2(61,4.4)=atan2(b,c)", + function: "atan2", + }, + { + sql: "select b,cot(b) from t where cot(61)=cot(b)", + function: "cot", + }, + { + sql: "select c from t where pi() < c", + function: "pi", + }, + } + for _, testcase := range testcases { + tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") + tk.MustExec("admin reload expr_pushdown_blacklist;") + r1 := tk.MustQuery(testcase.sql).Rows() + tk.MustExec(fmt.Sprintf("insert into mysql.expr_pushdown_blacklist(name) values('%s');", testcase.function)) + tk.MustExec("admin reload expr_pushdown_blacklist;") + r2 := tk.MustQuery(testcase.sql).Rows() + require.EqualValues(t, r2, r1, testcase.sql) + } +} + +func TestScalarFunctionPushDown(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id int signed, id2 int unsigned, c varchar(11), d datetime, b double, e bit(10))") + tk.MustExec("insert into t(id, id2, c, d) values (-1, 1, '{\"a\":1}', '2021-12-12')") + rows := [][]interface{}{ + {"TableReader_7", "root", "data:Selection_6"}, + {"└─Selection_6", "cop[tikv]", "right(test.t.c, 1)"}, + {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where right(c,1);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "mod(test.t.id, test.t.id)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id, id);"). + CheckAt([]int{0, 3, 6}, rows) + rows[1][2] = "mod(test.t.id, test.t.id2)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id, id2);"). + CheckAt([]int{0, 3, 6}, rows) + rows[1][2] = "mod(test.t.id2, test.t.id)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id2, id);"). + CheckAt([]int{0, 3, 6}, rows) + rows[1][2] = "mod(test.t.id2, test.t.id2)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id2, id2);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "sin(cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where sin(id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "asin(cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where asin(id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "cos(cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where cos(id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "acos(cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where acos(id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "atan(cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where atan(id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "atan2(cast(test.t.id, double BINARY), cast(test.t.id, double BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where atan2(id,id);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "hour(cast(test.t.d, time))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where hour(d);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "hour(cast(test.t.d, time))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where hour(d);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "minute(cast(test.t.d, time))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where minute(d);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "second(cast(test.t.d, time))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where second(d);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "month(test.t.d)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where month(d);"). + CheckAt([]int{0, 3, 6}, rows) + + //rows[1][2] = "dayname(test.t.d)" + //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where dayname(d);"). + // CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "dayofmonth(test.t.d)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where dayofmonth(d);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "from_days(test.t.id)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where from_days(id);"). + CheckAt([]int{0, 3, 6}, rows) + + //rows[1][2] = "last_day(test.t.d)" + //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where last_day(d);"). + // CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "gt(4, test.t.id)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where pi() > id;"). + CheckAt([]int{0, 3, 6}, rows) + + //rows[1][2] = "truncate(test.t.id, 0)" + //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where truncate(id,0)"). + // CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "round(test.t.b)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where round(b)"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "date(test.t.d)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where date(d)"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "week(test.t.d)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where week(d)"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "datediff(test.t.d, test.t.d)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where datediff(d,d)"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "gt(test.t.d, sysdate())" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "ascii(cast(test.t.e, var_string(2)))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where ascii(e);"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "eq(json_valid(test.t.c), 1)" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where json_valid(c)=1;"). + CheckAt([]int{0, 3, 6}, rows) + + rows[1][2] = "json_contains(cast(test.t.c, json BINARY), cast(\"1\", json BINARY))" + tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where json_contains(c, '1');"). + CheckAt([]int{0, 3, 6}, rows) +} + +func TestReverseUTF8PushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a varchar(256))") + tk.MustExec("insert into t values('pingcap')") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "reverse(test.t.a)->Column#3"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + + tk.MustQuery("explain select reverse(a) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestReversePushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a binary(32))") + tk.MustExec("insert into t values('pingcap')") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "reverse(test.t.a)->Column#3"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + + tk.MustQuery("explain select reverse(a) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestSpacePushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int)") + tk.MustExec("insert into t values(5)") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "space(test.t.a)->Column#3"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + + tk.MustQuery("explain select space(a) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestExplainAnalyzePointGet(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b varchar(20))") + tk.MustExec("insert into t values (1,1)") + + res := tk.MustQuery("explain analyze select * from t where a=1;") + checkExplain := func(rpc string) { + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + _, _ = fmt.Fprintf(resBuff, "%s\n", row) + } + explain := resBuff.String() + require.Containsf(t, explain, rpc+":{num_rpc:", "%s", explain) + require.Containsf(t, explain, "total_time:", "%s", explain) + } + checkExplain("Get") + res = tk.MustQuery("explain analyze select * from t where a in (1,2,3);") + checkExplain("BatchGet") +} + +func TestExplainAnalyzeDML(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(" create table t (a int, b int, unique index (a));") + tk.MustExec("insert into t values (1,1)") + + res := tk.MustQuery("explain analyze select * from t where a=1;") + checkExplain := func(rpc string) { + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + _, _ = fmt.Fprintf(resBuff, "%s\n", row) + } + explain := resBuff.String() + require.Containsf(t, explain, rpc+":{num_rpc:", "%s", explain) + require.Containsf(t, explain, "total_time:", "%s", explain) + } + checkExplain("Get") + res = tk.MustQuery("explain analyze insert ignore into t values (1,1),(2,2),(3,3),(4,4);") + checkExplain("BatchGet") +} + +func TestExplainAnalyzeDML2(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + cases := []struct { + prepare string + sql string + planRegexp string + }{ + // Test for alloc auto ID. + { + sql: "insert into t () values ()", + planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", + }, + // Test for rebase ID. + { + sql: "insert into t (a) values (99000000000)", + planRegexp: ".*prepare.*total.*, auto_id_allocator.*rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", + }, + // Test for alloc auto ID and rebase ID. + { + sql: "insert into t (a) values (null), (99000000000)", + planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", + }, + // Test for insert ignore. + { + sql: "insert ignore into t values (null,1), (2, 2), (99000000000, 3), (100000000000, 4)", + planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 2, Get.*num_rpc.*total_time.*commit_txn.*count: 3, prewrite.*get_commit_ts.*commit.*write_keys.*, check_insert.*", + }, + // Test for insert on duplicate. + { + sql: "insert into t values (null,null), (1,1),(2,2) on duplicate key update a = a + 100000000000", + planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*count: 2, prewrite.*get_commit_ts.*commit.*write_keys.*, check_insert.*", + }, + // Test for replace with alloc ID. + { + sql: "replace into t () values ()", + planRegexp: ".*auto_id_allocator.*alloc_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", + }, + // Test for replace with alloc ID and rebase ID. + { + sql: "replace into t (a) values (null), (99000000000)", + planRegexp: ".*auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", + }, + // Test for update with rebase ID. + { + prepare: "insert into t values (1,1),(2,2)", + sql: "update t set a=a*100000000000", + planRegexp: ".*auto_id_allocator.*rebase_cnt: 2, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", + }, + } + + for _, ca := range cases { + for i := 0; i < 3; i++ { + tk.MustExec("drop table if exists t") + switch i { + case 0: + tk.MustExec("create table t (a bigint auto_increment, b int, primary key (a));") + case 1: + tk.MustExec("create table t (a bigint unsigned auto_increment, b int, primary key (a));") + case 2: + if strings.Contains(ca.sql, "on duplicate key") { + continue + } + tk.MustExec("create table t (a bigint primary key auto_random(5), b int);") + tk.MustExec("set @@allow_auto_random_explicit_insert=1;") + default: + panic("should never happen") + } + if ca.prepare != "" { + tk.MustExec(ca.prepare) + } + res := tk.MustQuery("explain analyze " + ca.sql) + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + _, _ = fmt.Fprintf(resBuff, "%s\t", row) + } + explain := resBuff.String() + require.Regexpf(t, ca.planRegexp, explain, "idx: %v,sql: %v", i, ca.sql) + } + } + + // Test for table without auto id. + for _, ca := range cases { + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a bigint, b int);") + tk.MustExec("insert into t () values ()") + if ca.prepare != "" { + tk.MustExec(ca.prepare) + } + res := tk.MustQuery("explain analyze " + ca.sql) + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + _, _ = fmt.Fprintf(resBuff, "%s\t", row) + } + explain := resBuff.String() + require.NotContainsf(t, explain, "auto_id_allocator", "sql: %v, explain: %v", ca.sql, explain) + } +} + +func TestIssue20139(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int, c int) partition by range (id) (partition p0 values less than (4), partition p1 values less than (7))") + tk.MustExec("insert into t values(3, 3), (5, 5)") + plan := tk.MustQuery("explain format = 'brief' select * from t where c = 1 and id = c") + plan.Check(testkit.Rows( + "TableReader 0.01 root partition:p0 data:Selection", + "└─Selection 0.01 cop[tikv] eq(test.t.c, 1), eq(test.t.id, 1)", + " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", + )) + tk.MustExec("drop table t") +} + +// Test for issue https://github.com/pingcap/tidb/issues/21607. +func TestConditionColPruneInPhysicalUnionScan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int, b int);") + tk.MustExec("begin;") + tk.MustExec("insert into t values (1, 2);") + tk.MustQuery("select count(*) from t where b = 1 and b in (3);"). + Check(testkit.Rows("0")) + + tk.MustExec("drop table t;") + tk.MustExec("create table t (a int, b int as (a + 1), c int as (b + 1));") + tk.MustExec("begin;") + tk.MustExec("insert into t (a) values (1);") + tk.MustQuery("select count(*) from t where b = 1 and b in (3);"). + Check(testkit.Rows("0")) + tk.MustQuery("select count(*) from t where c = 1 and c in (3);"). + Check(testkit.Rows("0")) +} + +func TestCreateViewIsolationRead(t *testing.T) { + store := testkit.CreateMockStore(t) + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk := testkit.NewTestKit(t, store) + tk.SetSession(se) + + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int, b int);") + tk.MustExec("set session tidb_isolation_read_engines='tiflash,tidb';") + // No error for CreateView. + tk.MustExec("create view v0 (a, avg_b) as select a, avg(b) from t group by a;") + tk.MustGetErrMsg("select * from v0;", "[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash,tidb', valid values can be 'tikv'.") + tk.MustExec("set session tidb_isolation_read_engines='tikv,tiflash,tidb';") + tk.MustQuery("select * from v0;").Check(testkit.Rows()) +} + +func TestConflictReadFromStorage(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(`create table t ( + a int, b int, c varchar(20), + primary key(a), key(b), key(c) + ) partition by range columns(a) ( + partition p0 values less than(6), + partition p1 values less than(11), + partition p2 values less than(16));`) + tk.MustExec(`insert into t values (1,1,"1"), (2,2,"2"), (8,8,"8"), (11,11,"11"), (15,15,"15")`) + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + tk.MustQuery(`explain select /*+ read_from_storage(tikv[t partition(p0)], tiflash[t partition(p1, p2)]) */ * from t`) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Storage hints are conflict, you can only specify one storage type of table test.t")) + tk.MustQuery(`explain select /*+ read_from_storage(tikv[t], tiflash[t]) */ * from t`) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Storage hints are conflict, you can only specify one storage type of table test.t")) +} + +func TestSelectIgnoreTemporaryTableInView(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + tk.MustExec("create table t1 (a int, b int)") + tk.MustExec("create table t2 (c int, d int)") + tk.MustExec("create view v1 as select * from t1 order by a limit 5") + tk.MustExec("create view v2 as select * from ((select * from t1) union (select * from t2)) as tt order by a, b limit 5") + tk.MustExec("create view v3 as select * from v1 order by a limit 5") + tk.MustExec("create view v4 as select * from t1, t2 where t1.a = t2.c order by a, b limit 5") + tk.MustExec("create view v5 as select * from (select * from t1) as t1 order by a limit 5") + + tk.MustExec("insert into t1 values (1, 2), (3, 4)") + tk.MustExec("insert into t2 values (3, 5), (6, 7)") + + tk.MustExec("create temporary table t1 (a int, b int)") + tk.MustExec("create temporary table t2 (c int, d int)") + tk.MustQuery("select * from t1").Check(testkit.Rows()) + tk.MustQuery("select * from t2").Check(testkit.Rows()) + + tk.MustQuery("select * from v1").Check(testkit.Rows("1 2", "3 4")) + tk.MustQuery("select * from v2").Check(testkit.Rows("1 2", "3 4", "3 5", "6 7")) + tk.MustQuery("select * from v3").Check(testkit.Rows("1 2", "3 4")) + tk.MustQuery("select * from v4").Check(testkit.Rows("3 4 3 5")) + tk.MustQuery("select * from v5").Check(testkit.Rows("1 2", "3 4")) +} + +func TestIssue29503(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Status.RecordQPSbyDB = true + }) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t(a int);") + require.NoError(t, tk.ExecToErr("create binding for select 1 using select 1;")) + require.NoError(t, tk.ExecToErr("create binding for select a from t using select a from t;")) + res := tk.MustQuery("show session bindings;") + require.Len(t, res.Rows(), 2) +} + +func verifyTimestampOutOfRange(tk *testkit.TestKit) { + tk.MustQuery(`select * from t28424 where t != "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t < "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t <= "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t >= "2038-1-19 3:14:08"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t > "2038-1-19 3:14:08"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t != "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t < "1970-1-1 0:0:0"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t <= "1970-1-1 0:0:0"`).Check(testkit.Rows()) + tk.MustQuery(`select * from t28424 where t >= "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) + tk.MustQuery(`select * from t28424 where t > "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) +} + +func TestIssue27949(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t27949") + tk.MustExec("create table t27949 (a int, b int, key(b))") + tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("IndexLookUp 10.00 root ", + "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t27949, index:b(b) range:[1,1], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) 10.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) + tk.MustExec("create global binding for select * from t27949 where b=1 using select * from t27949 ignore index(b) where b=1") + tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("TableReader 10.00 root data:Selection", + "└─Selection 10.00 cop[tikv] eq(test.t27949.b, 1)", + " └─TableFullScan 10000.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) + tk.MustExec("set @@sql_select_limit=100") + tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("Limit 10.00 root offset:0, count:100", + "└─TableReader 10.00 root data:Limit", + " └─Limit 10.00 cop[tikv] offset:0, count:100", + " └─Selection 10.00 cop[tikv] eq(test.t27949.b, 1)", + " └─TableFullScan 10000.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, index idx_a(a));") + tk.MustExec("create binding for select * from t using select * from t use index(idx_a);") + tk.MustExec("select * from t;") + tk.MustQuery("select @@last_plan_from_binding;").Check(testkit.Rows("1")) + tk.MustExec("prepare stmt from 'select * from t';") + tk.MustExec("execute stmt;") + tk.MustQuery("select @@last_plan_from_binding;").Check(testkit.Rows("1")) +} + +func TestIssue30804(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int, b int)") + // minimal reproduction of https://github.com/pingcap/tidb/issues/30804 + tk.MustExec("select avg(0) over w from t1 window w as (order by (select 1))") + // named window cannot be used in subquery + err := tk.ExecToErr("select avg(0) over w from t1 where b > (select sum(t2.a) over w from t2) window w as (partition by t1.b)") + require.True(t, core.ErrWindowNoSuchWindow.Equal(err)) + tk.MustExec("select avg(0) over w1 from t1 where b > (select sum(t2.a) over w2 from t2 window w2 as (partition by t2.b)) window w1 as (partition by t1.b)") +} + +func TestIssue31202(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t31202(a int primary key, b int);") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31202", L: "t31202"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + tk.MustQuery("explain format = 'brief' select * from t31202;").Check(testkit.Rows( + "TableReader 10000.00 root MppVersion: 2, data:ExchangeSender", + "└─ExchangeSender 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TableFullScan 10000.00 mpp[tiflash] table:t31202 keep order:false, stats:pseudo")) + + tk.MustQuery("explain format = 'brief' select * from t31202 use index (primary);").Check(testkit.Rows( + "TableReader 10000.00 root data:TableFullScan", + "└─TableFullScan 10000.00 cop[tikv] table:t31202 keep order:false, stats:pseudo")) + tk.MustExec("drop table if exists t31202") +} + +func TestNaturalJoinUpdateSameTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create database natural_join_update") + defer tk.MustExec("drop database natural_join_update") + tk.MustExec("use natural_join_update") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustExec("update t1 as a natural join t1 b SET a.a = 2, b.b = 3") + tk.MustQuery("select * from t1").Sort().Check(testkit.Rows("2 3", "2 3")) + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (a int primary key, b int)") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (a int, b int) partition by hash (a) partitions 3") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (A int, b int) partition by hash (b) partitions 3") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) + _, err := tk.Exec(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`) + require.Error(t, err) + require.Regexp(t, ".planner:1706.Primary key/partition key update is not allowed since the table is updated both as 'a' and 'B'.", err.Error()) + tk.MustExec("drop table t1") + tk.MustExec("create table t1 (A int, b int) partition by RANGE COLUMNS (b) (partition `pNeg` values less than (0),partition `pPos` values less than MAXVALUE)") + tk.MustExec("insert into t1 values (1,1),(2,2)") + tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) + tk.MustExec("drop table t1") +} + +func TestAggPushToCopForCachedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec(`create table t32157( + process_code varchar(8) NOT NULL, + ctrl_class varchar(2) NOT NULL, + ctrl_type varchar(1) NOT NULL, + oper_no varchar(12) DEFAULT NULL, + modify_date datetime DEFAULT NULL, + d_c_flag varchar(2) NOT NULL, + PRIMARY KEY (process_code,ctrl_class,d_c_flag) NONCLUSTERED);`) + tk.MustExec("insert into t32157 values ('GDEP0071', '05', '1', '10000', '2016-06-29 00:00:00', 'C')") + tk.MustExec("insert into t32157 values ('GDEP0071', '05', '0', '0000', '2016-06-01 00:00:00', 'D')") + tk.MustExec("alter table t32157 cache") + + tk.MustQuery("explain format = 'brief' select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows( + "StreamAgg 1.00 root funcs:count(1)->Column#8]\n" + + "[└─UnionScan 10.00 root eq(test.t32157.process_code, \"GDEP0071\")]\n" + + "[ └─TableReader 10.00 root data:Selection]\n" + + "[ └─Selection 10.00 cop[tikv] eq(test.t32157.process_code, \"GDEP0071\")]\n" + + "[ └─TableFullScan 10000.00 cop[tikv] table:t32157 keep order:false, stats:pseudo")) + + require.Eventually(t, func() bool { + tk.MustQuery("select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows("2")) + return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache + }, 10*time.Second, 500*time.Millisecond) + + tk.MustExec("drop table if exists t31202") +} + +func TestTiFlashFineGrainedShuffleWithMaxTiFlashThreads(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@tidb_enforce_mpp = on") + tk.MustExec("drop table if exists t1;") + tk.MustExec("create table t1(c1 int, c2 int)") + tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + sql := "explain select row_number() over w1 from t1 window w1 as (partition by c1);" + + getStreamCountFromExplain := func(rows [][]interface{}) (res []uint64) { + re := regexp.MustCompile("stream_count: ([0-9]+)") + for _, row := range rows { + buf := bytes.NewBufferString("") + _, _ = fmt.Fprintf(buf, "%s\n", row) + if matched := re.FindStringSubmatch(buf.String()); matched != nil { + require.Equal(t, len(matched), 2) + c, err := strconv.ParseUint(matched[1], 10, 64) + require.NoError(t, err) + res = append(res, c) + } + } + return res + } + + // tiflash_fine_grained_shuffle_stream_count should be same with tidb_max_tiflash_threads. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") + tk.MustExec("set @@tidb_max_tiflash_threads = 10") + rows := tk.MustQuery(sql).Rows() + streamCount := getStreamCountFromExplain(rows) + // require.Equal(t, len(streamCount), 1) + require.Equal(t, uint64(10), streamCount[0]) + + // tiflash_fine_grained_shuffle_stream_count should be default value when tidb_max_tiflash_threads is -1. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") + tk.MustExec("set @@tidb_max_tiflash_threads = -1") + rows = tk.MustQuery(sql).Rows() + streamCount = getStreamCountFromExplain(rows) + // require.Equal(t, len(streamCount), 1) + require.Equal(t, uint64(variable.DefStreamCountWhenMaxThreadsNotSet), streamCount[0]) + + // tiflash_fine_grained_shuffle_stream_count should be default value when tidb_max_tiflash_threads is 0. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") + tk.MustExec("set @@tidb_max_tiflash_threads = 0") + rows = tk.MustQuery(sql).Rows() + streamCount = getStreamCountFromExplain(rows) + // require.Equal(t, len(streamCount), 1) + require.Equal(t, uint64(variable.DefStreamCountWhenMaxThreadsNotSet), streamCount[0]) + + // Disabled when tiflash_fine_grained_shuffle_stream_count is -1. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = -1") + tk.MustExec("set @@tidb_max_tiflash_threads = 10") + rows = tk.MustQuery(sql).Rows() + streamCount = getStreamCountFromExplain(rows) + require.Equal(t, len(streamCount), 0) + + // Test when tiflash_fine_grained_shuffle_stream_count is greater than 0. + tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 16") + tk.MustExec("set @@tidb_max_tiflash_threads = 10") + rows = tk.MustQuery(sql).Rows() + streamCount = getStreamCountFromExplain(rows) + // require.Equal(t, len(streamCount), 1) + require.Equal(t, uint64(16), streamCount[0]) +} + +func TestIssue33175(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table t (id bigint(45) unsigned not null, c varchar(20), primary key(id));") + tk.MustExec("insert into t values (9734095886065816707, 'a'), (10353107668348738101, 'b'), (0, 'c');") + tk.MustExec("begin") + tk.MustExec("insert into t values (33, 'd');") + tk.MustQuery("select max(id) from t;").Check(testkit.Rows("10353107668348738101")) + tk.MustExec("rollback") + + tk.MustExec("alter table t cache") + for { + tk.MustQuery("select max(id) from t;").Check(testkit.Rows("10353107668348738101")) + if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { + break + } + } + + // // With subquery, like the original issue case. + for { + tk.MustQuery("select * from t where id > (select max(id) from t where t.id > 0);").Check(testkit.Rows()) + if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { + break + } + } + + // Test order by desc / asc. + tk.MustQuery("select id from t order by id desc;").Check(testkit.Rows( + "10353107668348738101", + "9734095886065816707", + "0")) + + tk.MustQuery("select id from t order by id asc;").Check(testkit.Rows( + "0", + "9734095886065816707", + "10353107668348738101")) + + tk.MustExec("alter table t nocache") + tk.MustExec("drop table t") + + // Cover more code that use union scan + // TableReader/IndexReader/IndexLookup + for idx, q := range []string{ + "create temporary table t (id bigint unsigned, c int default null, index(id))", + "create temporary table t (id bigint unsigned primary key)", + } { + tk.MustExec(q) + tk.MustExec("insert into t(id) values (1), (3), (9734095886065816707), (9734095886065816708)") + tk.MustQuery("select min(id) from t").Check(testkit.Rows("1")) + tk.MustQuery("select max(id) from t").Check(testkit.Rows("9734095886065816708")) + tk.MustQuery("select id from t order by id asc").Check(testkit.Rows( + "1", "3", "9734095886065816707", "9734095886065816708")) + tk.MustQuery("select id from t order by id desc").Check(testkit.Rows( + "9734095886065816708", "9734095886065816707", "3", "1")) + if idx == 0 { + tk.MustQuery("select * from t order by id asc").Check(testkit.Rows( + "1 ", + "3 ", + "9734095886065816707 ", + "9734095886065816708 ")) + tk.MustQuery("select * from t order by id desc").Check(testkit.Rows( + "9734095886065816708 ", + "9734095886065816707 ", + "3 ", + "1 ")) + } + tk.MustExec("drop table t") + } + + // More and more test + tk.MustExec("create global temporary table `tmp1` (id bigint unsigned primary key) on commit delete rows;") + tk.MustExec("begin") + tk.MustExec("insert into tmp1 values (0),(1),(2),(65536),(9734095886065816707),(9734095886065816708);") + tk.MustQuery("select * from tmp1 where id <= 65534 or (id > 65535 and id < 9734095886065816700) or id >= 9734095886065816707 order by id desc;").Check(testkit.Rows( + "9734095886065816708", "9734095886065816707", "65536", "2", "1", "0")) + + tk.MustQuery("select * from tmp1 where id <= 65534 or (id > 65535 and id < 9734095886065816700) or id >= 9734095886065816707 order by id asc;").Check(testkit.Rows( + "0", "1", "2", "65536", "9734095886065816707", "9734095886065816708")) + + tk.MustExec("create global temporary table `tmp2` (id bigint primary key) on commit delete rows;") + tk.MustExec("begin") + tk.MustExec("insert into tmp2 values(-2),(-1),(0),(1),(2);") + tk.MustQuery("select * from tmp2 where id <= -1 or id > 0 order by id desc;").Check(testkit.Rows("2", "1", "-1", "-2")) + tk.MustQuery("select * from tmp2 where id <= -1 or id > 0 order by id asc;").Check(testkit.Rows("-2", "-1", "1", "2")) +} + +func TestIssue35083(t *testing.T) { + defer func() { + variable.SetSysVar(variable.TiDBOptProjectionPushDown, variable.BoolToOnOff(config.GetGlobalConfig().Performance.ProjectionPushDown)) + }() + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.ProjectionPushDown = true + }) + variable.SetSysVar(variable.TiDBOptProjectionPushDown, variable.BoolToOnOff(config.GetGlobalConfig().Performance.ProjectionPushDown)) + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a varchar(100), b int)") + tk.MustQuery("select @@tidb_opt_projection_push_down").Check(testkit.Rows("1")) + tk.MustQuery("explain format = 'brief' select cast(a as datetime) from t1").Check(testkit.Rows( + "TableReader 10000.00 root data:Projection", + "└─Projection 10000.00 cop[tikv] cast(test.t1.a, datetime BINARY)->Column#4", + " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo")) +} + +func TestRepeatPushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(2147483647, 2)") + tk.MustExec("insert into t values(12, 2)") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "repeat(cast(test.t.a, var_string(20)), test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select repeat(a,b) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestIssue36194(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + // create virtual tiflash replica. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + tk.MustQuery("explain format = 'brief' select /*+ read_from_storage(tiflash[t]) */ * from t where a + 1 > 20 limit 100;;").Check(testkit.Rows( + "Limit 100.00 root offset:0, count:100", + "└─TableReader 100.00 root MppVersion: 2, data:ExchangeSender", + " └─ExchangeSender 100.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Limit 100.00 mpp[tiflash] offset:0, count:100", + " └─Selection 100.00 mpp[tiflash] gt(plus(test.t.a, 1), 20)", + " └─TableFullScan 125.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo")) +} + +func TestGetFormatPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(location varchar(10));") + tk.MustExec("insert into t values('USA'), ('JIS'), ('ISO'), ('EUR'), ('INTERNAL')") + tk.MustExec("set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + tk.MustQuery("explain format = 'brief' select GET_FORMAT(DATE, location) from t;").Check(testkit.Rows( + "TableReader 10000.00 root MppVersion: 2, data:ExchangeSender", + "└─ExchangeSender 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Projection 10000.00 mpp[tiflash] get_format(DATE, test.t.location)->Column#3", + " └─TableFullScan 10000.00 mpp[tiflash] table:t keep order:false, stats:pseudo")) +} + +func TestAggWithJsonPushDownToTiFlash(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a json);") + tk.MustExec("insert into t values(null);") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // Create virtual tiflash replica info. + dom := domain.GetDomain(tk.Session()) + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"HashAgg_6", "root", "funcs:avg(Column#4)->Column#3"}, + {"└─Projection_19", "root", "cast(test.t.a, double BINARY)->Column#4"}, + {" └─TableReader_12", "root", "data:TableFullScan_11"}, + {" └─TableFullScan_11", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select avg(a) from t;").CheckAt([]int{0, 2, 4}, rows) + + rows = [][]interface{}{ + {"HashAgg_6", "root", "funcs:sum(Column#4)->Column#3"}, + {"└─Projection_19", "root", "cast(test.t.a, double BINARY)->Column#4"}, + {" └─TableReader_12", "root", "data:TableFullScan_11"}, + {" └─TableFullScan_11", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select sum(a) from t;").CheckAt([]int{0, 2, 4}, rows) + + rows = [][]interface{}{ + {"HashAgg_6", "root", "funcs:group_concat(Column#4 separator \",\")->Column#3"}, + {"└─Projection_13", "root", "cast(test.t.a, var_string(4294967295))->Column#4"}, + {" └─TableReader_10", "root", "data:TableFullScan_9"}, + {" └─TableFullScan_9", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select /*+ hash_agg() */ group_concat(a) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestLeftShiftPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(2147483647, 32)") + tk.MustExec("insert into t values(12, 2)") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "leftshift(test.t.a, test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select a << b from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestIssue36609(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") + tk.MustExec("create table t2(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") + tk.MustExec("create table t3(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") + tk.MustExec("create table t4(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") + tk.MustExec("create table t5(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") + tk.MustQuery("select * from t3 straight_join t4 on t3.a = t4.b straight_join t2 on t3.d = t2.c straight_join t1 on t1.a = t2.b straight_join t5 on t4.c = t5.d where t2.b < 100 and t4.a = 10;") + tk.MustQuery("select * from information_schema.statements_summary;") +} + +func TestHexIntOrStrPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10));") + tk.MustExec("insert into t values(1, 'tiflash');") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "hex(test.t.a)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select hex(a) from t;").CheckAt([]int{0, 2, 4}, rows) + + rows = [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "hex(test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select hex(b) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestBinPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int);") + tk.MustExec("insert into t values(1);") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "bin(test.t.a)->Column#3"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select bin(a) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestEltPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(20))") + tk.MustExec("insert into t values(2147483647, '32')") + tk.MustExec("insert into t values(12, 'abc')") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "elt(test.t.a, test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select elt(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestRegexpInstrPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists test.t;") + tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), pos int, occur int, ret_op int, match_type varchar(30));") + tk.MustExec("insert into test.t values ('123', '12.', 1, 1, 0, ''), ('aBb', 'bb', 1, 1, 0, 'i'), ('ab\nabc', '^abc$', 1, 1, 0, 'm');") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "regexp_instr(test.t.expr, test.t.pattern, 1, 1, 0, test.t.match_type)->Column#8"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select regexp_instr(expr, pattern, 1, 1, 0, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestRegexpSubstrPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists test.t;") + tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), pos int, occur int, match_type varchar(30));") + tk.MustExec("insert into test.t values ('123', '12.', 1, 1, ''), ('aBb', 'bb', 1, 1, 'i'), ('ab\nabc', '^abc$', 1, 1, 'm');") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "regexp_substr(test.t.expr, test.t.pattern, 1, 1, test.t.match_type)->Column#7"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select regexp_substr(expr, pattern, 1, 1, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestRegexpReplacePushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists test.t;") + tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), repl varchar(30), pos int, occur int, match_type varchar(30));") + tk.MustExec("insert into test.t values ('123', '12.', '233', 1, 1, ''), ('aBb', 'bb', 'bc', 1, 1, 'i'), ('ab\nabc', '^abc$', 'd', 1, 1, 'm');") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "regexp_replace(test.t.expr, test.t.pattern, test.t.repl, 1, 1, test.t.match_type)->Column#8"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select regexp_replace(expr, pattern, repl, 1, 1, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestCastTimeAsDurationToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a date, b datetime(4))") + tk.MustExec("insert into t values('2021-10-26', '2021-10-26')") + tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11')") + tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.111111')") + tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.123456')") + tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.999999')") + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "cast(test.t.a, time BINARY)->Column#4, cast(test.t.b, time BINARY)->Column#5"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select cast(a as time), cast(b as time) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestUnhexPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(20));") + tk.MustExec("insert into t values(6162, '7469666C617368');") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "unhex(cast(test.t.a, var_string(20)))->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select unhex(a) from t;").CheckAt([]int{0, 2, 4}, rows) + + rows = [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "unhex(test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select unhex(b) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestLeastGretestStringPushDownToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a varchar(20), b varchar(20))") + tk.MustExec("insert into t values('123', '234')") + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "least(test.t.a, test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select least(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) + + rows = [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "greatest(test.t.a, test.t.b)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select greatest(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestTiFlashReadForWriteStmt(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1, 2)") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2(a int)") + tk.MustExec("set @@tidb_allow_mpp=1") + + // Default should be 1 + tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) + // Set ON + tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = ON") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) + // Set OFF + tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = OFF") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 tidb_enable_tiflash_read_for_write_stmt is always turned on. This variable has been deprecated and will be removed in the future releases")) + tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) + + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t2", L: "t2"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + checkRes := func(r [][]interface{}, pos int, expected string) { + check := false + for i := range r { + if r[i][pos] == expected { + check = true + break + } + } + require.Equal(t, check, true) + } + + check := func(query string) { + // If sql mode is strict, read does not push down to tiflash + tk.MustExec("set @@sql_mode = 'strict_trans_tables'") + tk.MustExec("set @@tidb_enforce_mpp=0") + rs := tk.MustQuery(query).Rows() + checkRes(rs, 2, "cop[tikv]") + tk.MustQuery("show warnings").Check(testkit.Rows()) + + // If sql mode is strict and tidb_enforce_mpp is on, read does not push down to tiflash + // and should return a warning. + tk.MustExec("set @@tidb_enforce_mpp=1") + rs = tk.MustQuery(query).Rows() + checkRes(rs, 2, "cop[tikv]") + rs = tk.MustQuery("show warnings").Rows() + checkRes(rs, 2, "MPP mode may be blocked because the query is not readonly and sql mode is strict.") + + // If sql mode is not strict, read should push down to tiflash + tk.MustExec("set @@sql_mode = ''") + rs = tk.MustQuery(query).Rows() + checkRes(rs, 2, "mpp[tiflash]") + tk.MustQuery("show warnings").Check(testkit.Rows()) + } + + // Insert into ... select + check("explain insert into t2 select a+b from t") + check("explain insert into t2 select t.a from t2 join t on t2.a = t.a") + + // Replace into ... select + check("explain replace into t2 select a+b from t") + + // CTE + check("explain update t set a=a+1 where b in (select a from t2 where t.a > t2.a)") +} + +func TestPointGetWithSelectLock(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, primary key(a, b));") + tk.MustExec("create table t1(c int unique, d int);") + tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) + require.NoError(t, err) + // Set the hacked TiFlash replica for explain tests. + tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} + + sqls := []string{ + "explain select a, b from t where (a = 1 and b = 2) or (a =2 and b = 1) for update;", + "explain select a, b from t where a = 1 and b = 2 for update;", + "explain select c, d from t1 where c = 1 for update;", + "explain select c, d from t1 where c = 1 and d = 1 for update;", + "explain select c, d from t1 where (c = 1 or c = 2 )and d = 1 for update;", + "explain select c, d from t1 where c in (1,2,3,4) for update;", + } + tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = on;") + tk.MustExec("set @@sql_mode='';") + tk.MustExec("set @@tidb_isolation_read_engines='tidb,tiflash';") + tk.MustExec("begin;") + // assert point get / batch point get can't work with tiflash in interaction txn + for _, sql := range sqls { + err = tk.ExecToErr(sql) + require.Error(t, err) + } + // assert point get / batch point get can work with tikv in interaction txn + tk.MustExec("set @@tidb_isolation_read_engines='tidb,tikv,tiflash';") + for _, sql := range sqls { + tk.MustQuery(sql) + } + tk.MustExec("commit") + // assert point get / batch point get can work with tiflash in auto commit + tk.MustExec("set @@tidb_isolation_read_engines='tidb,tiflash';") + for _, sql := range sqls { + tk.MustQuery(sql) + } +} + +func TestPlanCacheForIndexRangeFallback(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec(`set @@tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0") // In this way `explain for connection id` doesn't display execution info. + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a varchar(10), b varchar(10), c varchar(10), index idx_a_b(a, b))") + tk.MustExec("set @@tidb_opt_range_max_size=1330") // 1330 is the memory usage of ["aa","aa"], ["bb","bb"], ["cc","cc"], ["dd","dd"], ["ee","ee"]. + rows := tk.MustQuery("explain format='brief' select * from t where a in ('aa', 'bb', 'cc', 'dd', 'ee')").Rows() + require.True(t, strings.Contains(rows[1][0].(string), "IndexRangeScan")) + require.True(t, strings.Contains(rows[1][4].(string), "range:[\"aa\",\"aa\"], [\"bb\",\"bb\"], [\"cc\",\"cc\"], [\"dd\",\"dd\"], [\"ee\",\"ee\"]")) + rows = tk.MustQuery("explain format='brief' select * from t where a in ('aaaaaaaaaa', 'bbbbbbbbbb', 'cccccccccc', 'dddddddddd', 'eeeeeeeeee')").Rows() + // 1330 is not enough for ["aaaaaaaaaa","aaaaaaaaaa"], ["bbbbbbbbbb","bbbbbbbbbb"], ["cccccccccc","cccccccccc"], ["dddddddddd","dddddddddd"], ["eeeeeeeeee","eeeeeeeeee"]. + // So it falls back to table full scan. + require.True(t, strings.Contains(rows[2][0].(string), "TableFullScan")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1330 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) + + // Test rebuilding ranges for the cached plan doesn't have memory limit. + tk.MustExec("prepare stmt1 from 'select * from t where a in (?, ?, ?, ?, ?)'") + tk.MustExec("set @a='aa', @b='bb', @c='cc', @d='dd', @e='ee'") + tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") + tk.MustQuery("show warnings").Check(testkit.Rows()) // Range fallback doesn't happen and the plan can be put into cache. + tk.MustExec("set @a='aaaaaaaaaa', @b='bbbbbbbbbb', @c='cccccccccc', @d='dddddddddd', @e='eeeeeeeeee'") + tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + // We don't limit range mem usage when rebuilding ranges for the cached plan. + // So ["aaaaaaaaaa","aaaaaaaaaa"], ["bbbbbbbbbb","bbbbbbbbbb"], ["cccccccccc","cccccccccc"], ["dddddddddd","dddddddddd"], ["eeeeeeeeee","eeeeeeeeee"] can still be built even if its mem usage exceeds 1330. + require.True(t, strings.Contains(rows[1][0].(string), "IndexRangeScan")) + require.True(t, strings.Contains(rows[1][4].(string), "range:[\"aaaaaaaaaa\",\"aaaaaaaaaa\"], [\"bbbbbbbbbb\",\"bbbbbbbbbb\"], [\"cccccccccc\",\"cccccccccc\"], [\"dddddddddd\",\"dddddddddd\"], [\"eeeeeeeeee\",\"eeeeeeeeee\"]")) + + // Test the plan with range fallback would not be put into cache. + tk.MustExec("prepare stmt2 from 'select * from t where a in (?, ?, ?, ?, ?) and b in (?, ?, ?, ?, ?)'") + tk.MustExec("set @a='aa', @b='bb', @c='cc', @d='dd', @e='ee', @f='ff', @g='gg', @h='hh', @i='ii', @j='jj'") + tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e, @f, @g, @h, @i, @j") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1330 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", + "Warning 1105 skip prepared plan-cache: in-list is too long")) + tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e, @f, @g, @h, @i, @j") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) +} + +func TestCorColRangeWithRangeMaxSize(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2, t3") + tk.MustExec("create table t1(a int)") + tk.MustExec("create table t2 (a int, b int, c int, index idx_a_b(a, b))") + tk.MustExec("create table t3(a int primary key)") + tk.MustExec("insert into t1 values (2), (4), (6)") + tk.MustExec("insert into t2 (a, b) values (1, 2), (3, 2), (5, 2)") + tk.MustExec("insert into t3 values (2), (4)") + tk.MustExec("insert into mysql.opt_rule_blacklist value(\"decorrelate\")") + tk.MustExec("admin reload opt_rule_blacklist") + defer func() { + tk.MustExec("delete from mysql.opt_rule_blacklist where name = \"decorrelate\"") + tk.MustExec("admin reload opt_rule_blacklist") + }() + + // Correlated column in index range. + tk.MustExec("set @@tidb_opt_range_max_size=1000") + rows := tk.MustQuery("explain format='brief' select * from t1 where exists (select * from t2 where t2.a in (1, 3, 5) and b >= 2 and t2.b = t1.a)").Rows() + // 1000 is not enough for [1 2,1 +inf], [3 2,3 +inf], [5 2,5 +inf]. So b >= 2 is not used to build ranges. + require.True(t, strings.Contains(rows[4][0].(string), "Selection")) + require.True(t, strings.Contains(rows[4][4].(string), "ge(test.t2.b, 2)")) + // 1000 is not enough for [1 ?,1 ?], [3 ?,3 ?], [5 ?,5 ?] but we don't restrict range mem usage when appending col = cor_col + // conditions to access conditions in SplitCorColAccessCondFromFilters. + require.True(t, strings.Contains(rows[5][0].(string), "IndexRangeScan")) + require.True(t, strings.Contains(rows[5][4].(string), "range: decided by [in(test.t2.a, 1, 3, 5) eq(test.t2.b, test.t1.a)]")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1000 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) + // We need to rebuild index ranges each time the value of correlated column test.t1.a changes. We don't restrict range + // mem usage when rebuilding index ranges, otherwise range fallback would happen when rebuilding index ranges, causing + // to wrong query results. + tk.MustQuery("select * from t1 where exists (select * from t2 where t2.a in (1, 3, 5) and b >= 2 and t2.b = t1.a)").Check(testkit.Rows("2")) + + // Correlated column in table range. + tk.MustExec("set @@tidb_opt_range_max_size=1") + rows = tk.MustQuery("explain format='brief' select * from t1 where exists (select * from t3 where t3.a = t1.a)").Rows() + // 1 is not enough for [?,?] but we don't restrict range mem usage when adding col = cor_col to access conditions. + require.True(t, strings.Contains(rows[4][0].(string), "TableRangeScan")) + require.True(t, strings.Contains(rows[4][4].(string), "range: decided by [eq(test.t3.a, test.t1.a)]")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + // We need to rebuild table ranges each time the value of correlated column test.t1.a changes. We don't restrict range + // mem usage when rebuilding table ranges, otherwise range fallback would happen when rebuilding table ranges, causing + // to wrong query results. + tk.MustQuery("select * from t1 where exists (select * from t3 where t3.a = t1.a)").Check(testkit.Rows("2", "4")) +} + +// TestExplainAnalyzeDMLCommit covers the issue #37373. +func TestExplainAnalyzeDMLCommit(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 int key, c2 int);") + tk.MustExec("insert into t values (1, 1)") + + err := failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockSleepBeforeTxnCommit", "return(500)") + require.NoError(t, err) + defer func() { + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockSleepBeforeTxnCommit") + }() + // The commit is paused by the failpoint, after the fix the explain statement + // execution should proceed after the commit finishes. + _, err = tk.Exec("explain analyze delete from t;") + require.NoError(t, err) + tk.MustQuery("select * from t").Check(testkit.Rows()) +} + +func TestPlanCacheForIndexJoinRangeFallback(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec(`set @@tidb_enable_prepared_plan_cache=1`) + tk.MustExec("set @@tidb_enable_collect_execution_info=0") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2") + tk.MustExec("create table t1(a int, b varchar(10), c varchar(10), index idx_a_b(a, b))") + tk.MustExec("create table t2(d int)") + tk.MustExec("set @@tidb_opt_range_max_size=1275") + // 1275 is enough for [? a,? a], [? b,? b], [? c,? c] but is not enough for [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc]. + rows := tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('a', 'b', 'c')").Rows() + require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, a, b, c)]")) + tk.MustQuery("show warnings").Check(testkit.Rows()) + rows = tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('aaaaaa', 'bbbbbb', 'cccccc');").Rows() + require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d)]")) + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) + + tk.MustExec("prepare stmt1 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?)'") + tk.MustExec("set @a='a', @b='b', @c='c'") + tk.MustExec("execute stmt1 using @a, @b, @c") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec("set @a='aaaaaa', @b='bbbbbb', @c='cccccc'") + tk.MustExec("execute stmt1 using @a, @b, @c") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + tk.MustExec("execute stmt1 using @a, @b, @c") + tkProcess := tk.Session().ShowProcess() + ps := []*util.ProcessInfo{tkProcess} + tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) + rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() + // We don't limit range mem usage when rebuilding index join ranges for the cached plan. So [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc] can be built. + require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, aaaaaa, bbbbbb, cccccc)]")) + + // Test the plan with range fallback would not be put into cache. + tk.MustExec("prepare stmt2 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?, ?, ?)'") + tk.MustExec("set @a='a', @b='b', @c='c', @d='d', @e='e'") + tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", + "Warning 1105 skip prepared plan-cache: in-list is too long")) + tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) +} + +// https://github.com/pingcap/tidb/issues/38295. +// WARN: this test cannot be migrated to tests/integrationtest, because the error message of +// `SELECT t0.c1, t0.c2 FROM t0 GROUP BY MOD(t0.c0, DEFAULT(t0.c2));` is unstable. +func TestIssue38295(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t0(c0 BLOB(298) , c1 BLOB(182) , c2 NUMERIC);") + tk.MustExec("CREATE VIEW v0(c0) AS SELECT t0.c1 FROM t0;") + tk.MustExec("INSERT INTO t0 VALUES (-1, 'a', '2046549365');") + tk.MustExec("CREATE INDEX i0 ON t0(c2);") + tk.MustGetErrCode("SELECT t0.c1, t0.c2 FROM t0 GROUP BY MOD(t0.c0, DEFAULT(t0.c2));", errno.ErrFieldNotInGroupBy) + tk.MustExec("UPDATE t0 SET c2=1413;") +} + +// https://github.com/pingcap/tidb/issues/41273 +func TestIssue41273(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE t ( + a set('nwbk','r5','1ad3u','van','ir1z','y','9m','f1','z','e6yd','wfev') NOT NULL DEFAULT 'ir1z,f1,e6yd', + b enum('soo2','4s4j','qi9om','8ue','i71o','qon','3','3feh','6o1i','5yebx','d') NOT NULL DEFAULT '8ue', + c varchar(66) DEFAULT '13mdezixgcn', + PRIMARY KEY (a,b) /*T![clustered_index] CLUSTERED */, + UNIQUE KEY ib(b), + KEY ia(a) + )ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin;`) + tk.MustExec("INSERT INTO t VALUES('ir1z,f1,e6yd','i71o','13mdezixgcn'),('ir1z,f1,e6yd','d','13mdezixgcn'),('nwbk','8ue','13mdezixgcn');") + expectedRes := []string{"ir1z,f1,e6yd d 13mdezixgcn", "ir1z,f1,e6yd i71o 13mdezixgcn", "nwbk 8ue 13mdezixgcn"} + tk.MustQuery("select * from t where a between 'e6yd' and 'z' or b <> '8ue';").Sort().Check(testkit.Rows(expectedRes...)) + tk.MustQuery("select /*+ use_index_merge(t) */ * from t where a between 'e6yd' and 'z' or b <> '8ue';").Sort().Check(testkit.Rows(expectedRes...)) + // For now tidb doesn't support push set type to TiKV, and column a is a set type, so we shouldn't generate a IndexMerge path. + require.False(t, tk.HasPlanForLastExecution("IndexMerge")) +} + +func TestIsIPv4ToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(v4 varchar(100), v6 varchar(100))") + tk.MustExec("insert into t values('123.123.123.123', 'F746:C349:48E3:22F2:81E0:0EA8:E7B6:8286')") + tk.MustExec("insert into t values('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000')") + tk.MustExec("insert into t values('127.0.0.1', '2001:0:2851:b9f0:6d:2326:9036:f37a')") + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "is_ipv4(test.t.v4)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select is_ipv4(v4) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestIsIPv6ToTiFlash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(v4 varchar(100), v6 varchar(100))") + tk.MustExec("insert into t values('123.123.123.123', 'F746:C349:48E3:22F2:81E0:0EA8:E7B6:8286')") + tk.MustExec("insert into t values('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000')") + tk.MustExec("insert into t values('127.0.0.1', '2001:0:2851:b9f0:6d:2326:9036:f37a')") + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := [][]interface{}{ + {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, + {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, + {" └─Projection_4", "mpp[tiflash]", "is_ipv6(test.t.v6)->Column#4"}, + {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select is_ipv6(v6) from t;").CheckAt([]int{0, 2, 4}, rows) +} + +// https://github.com/pingcap/tidb/issues/41355 +// The "virtual generated column" push down is not supported now. +// This test covers: TopN, Projection, Selection. +func TestVirtualExprPushDown(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (c1 int DEFAULT 0, c2 int GENERATED ALWAYS AS (abs(c1)) VIRTUAL);") + tk.MustExec("insert into t(c1) values(1), (-1), (2), (-2), (99), (-99);") + tk.MustExec("set @@tidb_isolation_read_engines = 'tikv'") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + // TopN to tikv. + rows := [][]interface{}{ + {"TopN_7", "root", "test.t.c2, offset:0, count:2"}, + {"└─TableReader_13", "root", "data:TableFullScan_12"}, + {" └─TableFullScan_12", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select * from t order by c2 limit 2;").CheckAt([]int{0, 2, 4}, rows) + + // Projection to tikv. + rows = [][]interface{}{ + {"Projection_3", "root", "plus(test.t.c1, test.t.c2)->Column#4"}, + {"└─TableReader_5", "root", "data:TableFullScan_4"}, + {" └─TableFullScan_4", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustExec("set session tidb_opt_projection_push_down='ON';") + tk.MustQuery("explain select c1 + c2 from t;").CheckAt([]int{0, 2, 4}, rows) + tk.MustExec("set session tidb_opt_projection_push_down='OFF';") + + // Selection to tikv. + rows = [][]interface{}{ + {"Selection_7", "root", "gt(test.t.c2, 1)"}, + {"└─TableReader_6", "root", "data:TableFullScan_5"}, + {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select * from t where c2 > 1;").CheckAt([]int{0, 2, 4}, rows) + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + // TopN to tiflash. + rows = [][]interface{}{ + {"TopN_7", "root", "test.t.c2, offset:0, count:2"}, + {"└─TableReader_15", "root", "data:TableFullScan_14"}, + {" └─TableFullScan_14", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select * from t order by c2 limit 2;").CheckAt([]int{0, 2, 4}, rows) + + // Projection to tiflash. + rows = [][]interface{}{ + {"Projection_3", "root", "plus(test.t.c1, test.t.c2)->Column#4"}, + {"└─TableReader_6", "root", "data:TableFullScan_5"}, + {" └─TableFullScan_5", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustExec("set session tidb_opt_projection_push_down='ON';") + tk.MustQuery("explain select c1 + c2 from t;").CheckAt([]int{0, 2, 4}, rows) + tk.MustExec("set session tidb_opt_projection_push_down='OFF';") + + // Selection to tiflash. + rows = [][]interface{}{ + {"Selection_8", "root", "gt(test.t.c2, 1)"}, + {"└─TableReader_7", "root", "data:TableFullScan_6"}, + {" └─TableFullScan_6", "cop[tiflash]", "keep order:false, stats:pseudo"}, + } + tk.MustQuery("explain select * from t where c2 > 1;").CheckAt([]int{0, 2, 4}, rows) +} + +func TestWindowRangeFramePushDownTiflash(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("drop table if exists test.first_range;") + tk.MustExec("create table test.first_range(p int not null, o int not null, v int not null, o_datetime datetime not null, o_time time not null);") + tk.MustExec("insert into test.first_range (p, o, v, o_datetime, o_time) values (0, 0, 0, '2023-9-20 11:17:10', '11:17:10');") + + tk.MustExec("drop table if exists test.first_range_d64;") + tk.MustExec("create table test.first_range_d64(p int not null, o decimal(17,1) not null, v int not null);") + tk.MustExec("insert into test.first_range_d64 (p, o, v) values (0, 0.1, 0), (1, 1.0, 1), (1, 2.1, 2), (1, 4.1, 4), (1, 8.1, 8), (2, 0.0, 0), (2, 3.1, 3), (2, 10.0, 10), (2, 13.1, 13), (2, 15.1, 15), (3, 1.1, 1), (3, 2.9, 3), (3, 5.1, 5), (3, 9.1, 9), (3, 15.0, 15), (3, 20.1, 20), (3, 31.1, 31);") + + tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") + + // Create virtual tiflash replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "first_range" || tblInfo.Name.L == "first_range_d64" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + tk.MustExec(`set @@tidb_max_tiflash_threads=20`) + + tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 3 preceding and 0 following) as a from test.first_range;").Check(testkit.Rows( + "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", + "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o range between 3 preceding and 0 following), stream_count: 20", + " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o, stream_count: 20", + " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", + " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", + " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) + + tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 3 preceding and 2.9E0 following) as a from test.first_range;").Check(testkit.Rows( + "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", + "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o range between 3 preceding and 2.9 following), stream_count: 20", + " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o, stream_count: 20", + " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", + " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", + " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) + + tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 2.3 preceding and 0 following) as a from test.first_range_d64;").Check(testkit.Rows( + "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", + "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range_d64.v)->Column#6 over(partition by test.first_range_d64.p order by test.first_range_d64.o range between 2.3 preceding and 0 following), stream_count: 20", + " └─Sort_13 10000.00 mpp[tiflash] test.first_range_d64.p, test.first_range_d64.o, stream_count: 20", + " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", + " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range_d64.p, collate: binary], stream_count: 20", + " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range_d64 keep order:false, stats:pseudo")) + + tk.MustQuery("explain select *, first_value(v) over (partition by p order by o_datetime range between interval 1 day preceding and interval 1 day following) as a from test.first_range;").Check(testkit.Rows( + "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", + "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o_datetime range between interval 1 \"DAY\" preceding and interval 1 \"DAY\" following), stream_count: 20", + " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o_datetime, stream_count: 20", + " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", + " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", + " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) + + tk.MustQuery("explain select *, first_value(v) over (partition by p order by o_time range between interval 1 day preceding and interval 1 day following) as a from test.first_range;").Check(testkit.Rows( + "Shuffle_13 10000.00 root execution info: concurrency:5, data sources:[TableReader_11]", + "└─Window_8 10000.00 root first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o_time range between interval 1 \"DAY\" preceding and interval 1 \"DAY\" following)", + " └─Sort_12 10000.00 root test.first_range.p, test.first_range.o_time", + " └─TableReader_11 10000.00 root MppVersion: 2, data:ExchangeSender_10", + " └─ExchangeSender_10 10000.00 mpp[tiflash] ExchangeType: PassThrough", + " └─TableFullScan_9 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) +} + +func TestIssue46298(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists test.first_range;") + tk.MustExec("create table test.first_range(p int not null, o tinyint not null, v int not null);") + tk.MustExec("insert into test.first_range (p, o, v) values (0, 0, 0), (1, 1, 1), (1, 2, 2), (1, 4, 4), (1, 8, 8), (2, 0, 0), (2, 3, 3), (2, 10, 10), (2, 13, 13), (2, 15, 15), (3, 1, 1), (3, 3, 3), (3, 5, 5), (3, 9, 9), (3, 15, 15), (3, 20, 20), (3, 31, 31);") + tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") + tk.MustExec(`set @@tidb_enable_pipelined_window_function=0`) + tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") +} + +// https://github.com/pingcap/tidb/issues/41458 +func TestIssue41458(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("use test") + tk.MustExec(`create table t (a int, b int, c int, index ia(a));`) + tk.MustExec("select * from t t1 join t t2 on t1.b = t2.b join t t3 on t2.b=t3.b join t t4 on t3.b=t4.b where t3.a=1 and t2.a=2;") + rawRows := tk.MustQuery("select plan from information_schema.statements_summary where SCHEMA_NAME = 'test' and STMT_TYPE = 'Select';").Sort().Rows() + plan := rawRows[0][0].(string) + rows := strings.Split(plan, "\n") + rows = rows[1:] + expectedRes := []string{ + "Projection", + "└─HashJoin", + " ├─HashJoin", + " │ ├─HashJoin", + " │ │ ├─IndexLookUp", + " │ │ │ ├─IndexRangeScan", + " │ │ │ └─Selection", + " │ │ │ └─TableRowIDScan", + " │ │ └─IndexLookUp", + " │ │ ├─IndexRangeScan", + " │ │ └─Selection", + " │ │ └─TableRowIDScan", + " │ └─TableReader", + " │ └─Selection", + " │ └─TableFullScan", + " └─TableReader", + " └─Selection", + " └─TableFullScan", + } + for i, row := range rows { + fields := strings.Split(row, "\t") + fields = strings.Split(fields[1], "_") + op := fields[0] + require.Equalf(t, expectedRes[i], op, fmt.Sprintf("Mismatch at index %d.", i)) + } +} diff --git a/pkg/planner/core/internal/BUILD.bazel b/pkg/planner/core/internal/BUILD.bazel new file mode 100644 index 0000000000000..359447cea4297 --- /dev/null +++ b/pkg/planner/core/internal/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = [ + "testkit.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/core/internal", + visibility = ["//pkg/planner/core:__subpackages__"], + deps = [ + "//pkg/domain", + "//pkg/expression/aggregation", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + ], +) diff --git a/pkg/planner/core/internal/base/BUILD.bazel b/pkg/planner/core/internal/base/BUILD.bazel new file mode 100644 index 0000000000000..131e657804dd9 --- /dev/null +++ b/pkg/planner/core/internal/base/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "base", + srcs = ["plan.go"], + importpath = "github.com/pingcap/tidb/pkg/planner/core/internal/base", + visibility = ["//pkg/planner/core:__subpackages__"], + deps = [ + "//pkg/expression", + "//pkg/planner/property", + "//pkg/sessionctx", + "//pkg/types", + "//pkg/util/stringutil", + "//pkg/util/tracing", + ], +) diff --git a/pkg/planner/core/internal/base/plan.go b/pkg/planner/core/internal/base/plan.go new file mode 100644 index 0000000000000..7463093c8f2e3 --- /dev/null +++ b/pkg/planner/core/internal/base/plan.go @@ -0,0 +1,138 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package base + +import ( + "fmt" + "strconv" + "unsafe" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tracing" +) + +// Plan Should be used as embedded struct in Plan implementations. +type Plan struct { + ctx sessionctx.Context + stats *property.StatsInfo + tp string + id int + blockOffset int +} + +// NewBasePlan creates a new base plan. +func NewBasePlan(ctx sessionctx.Context, tp string, offset int) Plan { + id := ctx.GetSessionVars().PlanID.Add(1) + return Plan{ + tp: tp, + id: int(id), + ctx: ctx, + blockOffset: offset, + } +} + +// SCtx is to get the sessionctx from the plan. +func (p *Plan) SCtx() sessionctx.Context { + return p.ctx +} + +// SetSCtx is to set the sessionctx for the plan. +func (p *Plan) SetSCtx(ctx sessionctx.Context) { + p.ctx = ctx +} + +// OutputNames returns the outputting names of each column. +func (*Plan) OutputNames() types.NameSlice { + return nil +} + +// SetOutputNames sets the outputting name by the given slice. +func (*Plan) SetOutputNames(_ types.NameSlice) {} + +// ReplaceExprColumns implements Plan interface. +func (*Plan) ReplaceExprColumns(_ map[string]*expression.Column) {} + +// ID is to get the id. +func (p *Plan) ID() int { + return p.id +} + +// SetID is to set id. +func (p *Plan) SetID(id int) { + p.id = id +} + +// StatsInfo is to get the stats info. +func (p *Plan) StatsInfo() *property.StatsInfo { + return p.stats +} + +// ExplainInfo is to get the explain information. +func (*Plan) ExplainInfo() string { + return "N/A" +} + +// ExplainID is to get the explain ID. +func (p *Plan) ExplainID() fmt.Stringer { + return stringutil.MemoizeStr(func() string { + if p.ctx != nil && p.ctx.GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { + return p.tp + } + return p.tp + "_" + strconv.Itoa(p.id) + }) +} + +// TP is to get the tp. +func (p *Plan) TP() string { + return p.tp +} + +// SetTP is to set the tp. +func (p *Plan) SetTP(tp string) { + p.tp = tp +} + +// SelectBlockOffset is to get the select block offset. +func (p *Plan) SelectBlockOffset() int { + return p.blockOffset +} + +// SetStats sets the stats +func (p *Plan) SetStats(s *property.StatsInfo) { + p.stats = s +} + +// PlanSize is the size of BasePlan. +const PlanSize = int64(unsafe.Sizeof(Plan{})) + +// MemoryUsage return the memory usage of BasePlan +func (p *Plan) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = PlanSize + int64(len(p.tp)) + return sum +} + +// BuildPlanTrace is to build the plan trace. +func (p *Plan) BuildPlanTrace() *tracing.PlanTrace { + planTrace := &tracing.PlanTrace{ID: p.ID(), TP: p.TP()} + return planTrace +} diff --git a/pkg/planner/core/internal/testkit.go b/pkg/planner/core/internal/testkit.go new file mode 100644 index 0000000000000..d1b58331ddceb --- /dev/null +++ b/pkg/planner/core/internal/testkit.go @@ -0,0 +1,78 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "fmt" + "strings" + "testing" + + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" +) + +// SetTiFlashReplica is to set TiFlash replica +func SetTiFlashReplica(t *testing.T, dom *domain.Domain, dbName, tableName string) { + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr(dbName)) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == tableName { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } +} + +// WithMockTiFlash sets the mockStore to have N TiFlash stores (naming as tiflash0, tiflash1, ...). +func WithMockTiFlash(nodes int) mockstore.MockTiKVStoreOption { + return mockstore.WithMultipleOptions( + mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockCluster := c.(*unistore.Cluster) + _, _, region1 := mockstore.BootstrapWithSingleStore(c) + tiflashIdx := 0 + for tiflashIdx < nodes { + store2 := c.AllocID() + peer2 := c.AllocID() + addr2 := fmt.Sprintf("tiflash%d", tiflashIdx) + mockCluster.AddStore(store2, addr2, &metapb.StoreLabel{Key: "engine", Value: "tiflash"}) + mockCluster.AddPeer(region1, store2, peer2) + tiflashIdx++ + } + }), + mockstore.WithStoreType(mockstore.EmbedUnistore), + ) +} + +// GetFieldValue is to get field value. +func GetFieldValue(prefix, row string) string { + if idx := strings.Index(row, prefix); idx > 0 { + start := idx + len(prefix) + end := strings.Index(row[start:], " ") + if end > 0 { + value := row[start : start+end] + value = strings.Trim(value, ",") + return value + } + } + return "" +} diff --git a/pkg/planner/core/internal/util.go b/pkg/planner/core/internal/util.go new file mode 100644 index 0000000000000..849a921fe2318 --- /dev/null +++ b/pkg/planner/core/internal/util.go @@ -0,0 +1,31 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// WrapCastForAggFuncs wraps the args of an aggregate function with a cast function. +// If the mode is FinalMode or Partial2Mode, we do not need to wrap cast upon the args, +// since the types of the args are already the expected. +func WrapCastForAggFuncs(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc) { + for i := range aggFuncs { + if aggFuncs[i].Mode != aggregation.FinalMode && aggFuncs[i].Mode != aggregation.Partial2Mode { + aggFuncs[i].WrapCastForAggArgs(sctx) + } + } +} diff --git a/pkg/planner/core/issuetest/BUILD.bazel b/pkg/planner/core/issuetest/BUILD.bazel new file mode 100644 index 0000000000000..a08f63c0df355 --- /dev/null +++ b/pkg/planner/core/issuetest/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "issuetest_test", + timeout = "short", + srcs = [ + "main_test.go", + "planner_issue_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + race = "on", + deps = [ + "//pkg/parser", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/issuetest/main_test.go b/pkg/planner/core/issuetest/main_test.go new file mode 100644 index 0000000000000..fba537637990e --- /dev/null +++ b/pkg/planner/core/issuetest/main_test.go @@ -0,0 +1,41 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package issuetest + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/planner/core/issuetest/planner_issue_test.go b/pkg/planner/core/issuetest/planner_issue_test.go similarity index 91% rename from planner/core/issuetest/planner_issue_test.go rename to pkg/planner/core/issuetest/planner_issue_test.go index f3c499fac947a..be9e5ef29d971 100644 --- a/planner/core/issuetest/planner_issue_test.go +++ b/pkg/planner/core/issuetest/planner_issue_test.go @@ -18,10 +18,10 @@ import ( "context" "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go similarity index 99% rename from planner/core/logical_plan_builder.go rename to pkg/planner/core/logical_plan_builder.go index 55117483768fd..38f0d604d2a29 100644 --- a/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -27,45 +27,45 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - core_metrics "github.com/pingcap/tidb/planner/core/metrics" - fd "github.com/pingcap/tidb/planner/funcdep" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - util2 "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + core_metrics "github.com/pingcap/tidb/pkg/planner/core/metrics" + fd "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + util2 "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" ) diff --git a/planner/core/logical_plan_trace_test.go b/pkg/planner/core/logical_plan_trace_test.go similarity index 99% rename from planner/core/logical_plan_trace_test.go rename to pkg/planner/core/logical_plan_trace_test.go index da14575e93256..c46015bcbca89 100644 --- a/planner/core/logical_plan_trace_test.go +++ b/pkg/planner/core/logical_plan_trace_test.go @@ -19,9 +19,9 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) diff --git a/planner/core/logical_plans.go b/pkg/planner/core/logical_plans.go similarity index 99% rename from planner/core/logical_plans.go rename to pkg/planner/core/logical_plans.go index 8557319bad370..c2665ba2065db 100644 --- a/planner/core/logical_plans.go +++ b/pkg/planner/core/logical_plans.go @@ -20,25 +20,25 @@ import ( "math" "unsafe" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - fd "github.com/pingcap/tidb/planner/funcdep" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + fd "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/planner/core/logical_plans_test.go b/pkg/planner/core/logical_plans_test.go similarity index 99% rename from planner/core/logical_plans_test.go rename to pkg/planner/core/logical_plans_test.go index 32e0f0d370328..82283419f6012 100644 --- a/planner/core/logical_plans_test.go +++ b/pkg/planner/core/logical_plans_test.go @@ -21,22 +21,22 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/main_test.go b/pkg/planner/core/main_test.go new file mode 100644 index 0000000000000..c716e04c93336 --- /dev/null +++ b/pkg/planner/core/main_test.go @@ -0,0 +1,64 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package core + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) +var planSuiteUnexportedData testdata.TestData +var indexMergeSuiteData testdata.TestData + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + testDataMap.LoadTestSuiteData("testdata", "plan_suite_unexported") + testDataMap.LoadTestSuiteData("testdata", "index_merge_suite") + testDataMap.LoadTestSuiteData("testdata", "runtime_filter_generator_suite") + + indexMergeSuiteData = testDataMap["index_merge_suite"] + planSuiteUnexportedData = testDataMap["plan_suite_unexported"] + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIndexMergeSuiteData() testdata.TestData { + return testDataMap["index_merge_suite"] +} + +func GetRuntimeFilterGeneratorData() testdata.TestData { + return testDataMap["runtime_filter_generator_suite"] +} diff --git a/planner/core/memtable_predicate_extractor.go b/pkg/planner/core/memtable_predicate_extractor.go similarity index 98% rename from planner/core/memtable_predicate_extractor.go rename to pkg/planner/core/memtable_predicate_extractor.go index 470f652400d28..cd8d412ddd3a2 100644 --- a/planner/core/memtable_predicate_extractor.go +++ b/pkg/planner/core/memtable_predicate_extractor.go @@ -25,21 +25,21 @@ import ( "time" "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/planner/core/memtable_predicate_extractor_test.go b/pkg/planner/core/memtable_predicate_extractor_test.go similarity index 99% rename from planner/core/memtable_predicate_extractor_test.go rename to pkg/planner/core/memtable_predicate_extractor_test.go index 9e7d3963cfdc4..ea21f18572270 100644 --- a/planner/core/memtable_predicate_extractor_test.go +++ b/pkg/planner/core/memtable_predicate_extractor_test.go @@ -24,18 +24,18 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/metrics/BUILD.bazel b/pkg/planner/core/metrics/BUILD.bazel new file mode 100644 index 0000000000000..be5fe2c98ba75 --- /dev/null +++ b/pkg/planner/core/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/planner/core/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/planner/core/metrics/metrics.go b/pkg/planner/core/metrics/metrics.go new file mode 100644 index 0000000000000..505b297550dd3 --- /dev/null +++ b/pkg/planner/core/metrics/metrics.go @@ -0,0 +1,82 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// planner core metrics vars +var ( + PseudoEstimationNotAvailable prometheus.Counter + PseudoEstimationOutdate prometheus.Counter + preparedPlanCacheHitCounter prometheus.Counter + nonPreparedPlanCacheHitCounter prometheus.Counter + preparedPlanCacheMissCounter prometheus.Counter + nonPreparedPlanCacheMissCounter prometheus.Counter + nonPreparedPlanCacheUnsupportedCounter prometheus.Counter + sessionPlanCacheInstancePlanNumCounter prometheus.Gauge + sessionPlanCacheInstanceMemoryUsage prometheus.Gauge +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init planner core metrics vars. +func InitMetricsVars() { + PseudoEstimationNotAvailable = metrics.PseudoEstimation.WithLabelValues("nodata") + PseudoEstimationOutdate = metrics.PseudoEstimation.WithLabelValues("outdate") + // plan cache metrics + preparedPlanCacheHitCounter = metrics.PlanCacheCounter.WithLabelValues("prepared") + nonPreparedPlanCacheHitCounter = metrics.PlanCacheCounter.WithLabelValues("non-prepared") + preparedPlanCacheMissCounter = metrics.PlanCacheMissCounter.WithLabelValues("prepared") + nonPreparedPlanCacheMissCounter = metrics.PlanCacheMissCounter.WithLabelValues("non-prepared") + nonPreparedPlanCacheUnsupportedCounter = metrics.PlanCacheMissCounter.WithLabelValues("non-prepared-unsupported") + sessionPlanCacheInstancePlanNumCounter = metrics.PlanCacheInstancePlanNumCounter.WithLabelValues(" session-plan-cache") + sessionPlanCacheInstanceMemoryUsage = metrics.PlanCacheInstanceMemoryUsage.WithLabelValues(" session-plan-cache") +} + +// GetPlanCacheHitCounter get different plan cache hit counter +func GetPlanCacheHitCounter(isNonPrepared bool) prometheus.Counter { + if isNonPrepared { + return nonPreparedPlanCacheHitCounter + } + return preparedPlanCacheHitCounter +} + +// GetPlanCacheMissCounter get different plan cache miss counter +func GetPlanCacheMissCounter(isNonPrepared bool) prometheus.Counter { + if isNonPrepared { + return nonPreparedPlanCacheMissCounter + } + return preparedPlanCacheMissCounter +} + +// GetNonPrepPlanCacheUnsupportedCounter get non-prepared plan cache unsupported counter. +func GetNonPrepPlanCacheUnsupportedCounter() prometheus.Counter { + return nonPreparedPlanCacheUnsupportedCounter +} + +// GetPlanCacheInstanceNumCounter get different plan counter of plan cache +func GetPlanCacheInstanceNumCounter() prometheus.Gauge { + return sessionPlanCacheInstancePlanNumCounter +} + +// GetPlanCacheInstanceMemoryUsage get different plan memory usage counter of plan cache +func GetPlanCacheInstanceMemoryUsage() prometheus.Gauge { + return sessionPlanCacheInstanceMemoryUsage +} diff --git a/pkg/planner/core/mock.go b/pkg/planner/core/mock.go new file mode 100644 index 0000000000000..334d4b39bdffd --- /dev/null +++ b/pkg/planner/core/mock.go @@ -0,0 +1,611 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package core + +import ( + "fmt" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" +) + +func newLongType() types.FieldType { + return *(types.NewFieldType(mysql.TypeLong)) +} + +func newStringType() types.FieldType { + ft := types.NewFieldType(mysql.TypeVarchar) + charset, collate := types.DefaultCharsetForType(mysql.TypeVarchar) + ft.SetCharset(charset) + ft.SetCollate(collate) + return *ft +} + +func newDateType() types.FieldType { + ft := types.NewFieldType(mysql.TypeDate) + return *ft +} + +// MockSignedTable is only used for plan related tests. +func MockSignedTable() *model.TableInfo { + // column: a, b, c, d, e, c_str, d_str, e_str, f, g, h, i_date + // PK: a + // indices: c_d_e, e, f, g, f_g, c_d_e_str, e_d_c_str_prefix + indices := []*model.IndexInfo{ + { + Name: model.NewCIStr("c_d_e"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("c"), + Length: types.UnspecifiedLength, + Offset: 2, + }, + { + Name: model.NewCIStr("d"), + Length: types.UnspecifiedLength, + Offset: 3, + }, + { + Name: model.NewCIStr("e"), + Length: types.UnspecifiedLength, + Offset: 4, + }, + }, + State: model.StatePublic, + Unique: true, + }, + { + Name: model.NewCIStr("x"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("e"), + Length: types.UnspecifiedLength, + Offset: 4, + }, + }, + State: model.StateWriteOnly, + Unique: true, + }, + { + Name: model.NewCIStr("f"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("f"), + Length: types.UnspecifiedLength, + Offset: 8, + }, + }, + State: model.StatePublic, + Unique: true, + }, + { + Name: model.NewCIStr("g"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("g"), + Length: types.UnspecifiedLength, + Offset: 9, + }, + }, + State: model.StatePublic, + }, + { + Name: model.NewCIStr("f_g"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("f"), + Length: types.UnspecifiedLength, + Offset: 8, + }, + { + Name: model.NewCIStr("g"), + Length: types.UnspecifiedLength, + Offset: 9, + }, + }, + State: model.StatePublic, + Unique: true, + }, + { + Name: model.NewCIStr("c_d_e_str"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("c_str"), + Length: types.UnspecifiedLength, + Offset: 5, + }, + { + Name: model.NewCIStr("d_str"), + Length: types.UnspecifiedLength, + Offset: 6, + }, + { + Name: model.NewCIStr("e_str"), + Length: types.UnspecifiedLength, + Offset: 7, + }, + }, + State: model.StatePublic, + }, + { + Name: model.NewCIStr("e_d_c_str_prefix"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("e_str"), + Length: types.UnspecifiedLength, + Offset: 7, + }, + { + Name: model.NewCIStr("d_str"), + Length: types.UnspecifiedLength, + Offset: 6, + }, + { + Name: model.NewCIStr("c_str"), + Length: 10, + Offset: 5, + }, + }, + State: model.StatePublic, + }, + } + pkColumn := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 0, + Name: model.NewCIStr("a"), + FieldType: newLongType(), + ID: 1, + } + col0 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 1, + Name: model.NewCIStr("b"), + FieldType: newLongType(), + ID: 2, + } + col1 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 2, + Name: model.NewCIStr("c"), + FieldType: newLongType(), + ID: 3, + } + col2 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 3, + Name: model.NewCIStr("d"), + FieldType: newLongType(), + ID: 4, + } + col3 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 4, + Name: model.NewCIStr("e"), + FieldType: newLongType(), + ID: 5, + } + colStr1 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 5, + Name: model.NewCIStr("c_str"), + FieldType: newStringType(), + ID: 6, + } + colStr2 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 6, + Name: model.NewCIStr("d_str"), + FieldType: newStringType(), + ID: 7, + } + colStr3 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 7, + Name: model.NewCIStr("e_str"), + FieldType: newStringType(), + ID: 8, + } + col4 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 8, + Name: model.NewCIStr("f"), + FieldType: newLongType(), + ID: 9, + } + col5 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 9, + Name: model.NewCIStr("g"), + FieldType: newLongType(), + ID: 10, + } + col6 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 10, + Name: model.NewCIStr("h"), + FieldType: newLongType(), + ID: 11, + } + col7 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 11, + Name: model.NewCIStr("i_date"), + FieldType: newDateType(), + ID: 12, + } + pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag) + // Column 'b', 'c', 'd', 'f', 'g' is not null. + col0.SetFlag(mysql.NotNullFlag) + col1.SetFlag(mysql.NotNullFlag) + col2.SetFlag(mysql.NotNullFlag) + col4.SetFlag(mysql.NotNullFlag) + col5.SetFlag(mysql.NotNullFlag) + col6.SetFlag(mysql.NoDefaultValueFlag) + table := &model.TableInfo{ + Columns: []*model.ColumnInfo{pkColumn, col0, col1, col2, col3, colStr1, colStr2, colStr3, col4, col5, col6, col7}, + Indices: indices, + Name: model.NewCIStr("t"), + PKIsHandle: true, + } + return table +} + +// MockUnsignedTable is only used for plan related tests. +func MockUnsignedTable() *model.TableInfo { + // column: a, b, c + // PK: a + // indeices: b, b_c + indices := []*model.IndexInfo{ + { + Name: model.NewCIStr("b"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("b"), + Length: types.UnspecifiedLength, + Offset: 1, + }, + }, + State: model.StatePublic, + Unique: true, + }, + { + Name: model.NewCIStr("b_c"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("b"), + Length: types.UnspecifiedLength, + Offset: 1, + }, + { + Name: model.NewCIStr("c"), + Length: types.UnspecifiedLength, + Offset: 2, + }, + }, + State: model.StatePublic, + }, + } + pkColumn := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 0, + Name: model.NewCIStr("a"), + FieldType: newLongType(), + ID: 1, + } + col0 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 1, + Name: model.NewCIStr("b"), + FieldType: newLongType(), + ID: 2, + } + col1 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 2, + Name: model.NewCIStr("c"), + FieldType: newLongType(), + ID: 3, + } + pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag) + // Column 'b' is not null. + col0.SetFlag(mysql.NotNullFlag) + col1.SetFlag(mysql.UnsignedFlag) + table := &model.TableInfo{ + Columns: []*model.ColumnInfo{pkColumn, col0, col1}, + Indices: indices, + Name: model.NewCIStr("t2"), + PKIsHandle: true, + } + return table +} + +// MockNoPKTable is only used for plan related tests. +func MockNoPKTable() *model.TableInfo { + // column: a, b + col0 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 1, + Name: model.NewCIStr("a"), + FieldType: newLongType(), + ID: 2, + } + col1 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 2, + Name: model.NewCIStr("b"), + FieldType: newLongType(), + ID: 3, + } + // Column 'a', 'b' is not null. + col0.SetFlag(mysql.NotNullFlag) + col1.SetFlag(mysql.UnsignedFlag) + table := &model.TableInfo{ + Columns: []*model.ColumnInfo{col0, col1}, + Name: model.NewCIStr("t3"), + PKIsHandle: true, + } + return table +} + +// MockView is only used for plan related tests. +func MockView() *model.TableInfo { + selectStmt := "select b,c,d from t" + col0 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 0, + Name: model.NewCIStr("b"), + ID: 1, + } + col1 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 1, + Name: model.NewCIStr("c"), + ID: 2, + } + col2 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 2, + Name: model.NewCIStr("d"), + ID: 3, + } + view := &model.ViewInfo{SelectStmt: selectStmt, Security: model.SecurityDefiner, Definer: &auth.UserIdentity{Username: "root", Hostname: ""}, Cols: []model.CIStr{col0.Name, col1.Name, col2.Name}} + table := &model.TableInfo{ + Name: model.NewCIStr("v"), + Columns: []*model.ColumnInfo{col0, col1, col2}, + View: view, + } + return table +} + +// MockContext is only used for plan related tests. +func MockContext() sessionctx.Context { + ctx := mock.NewContext() + ctx.Store = &mock.Store{ + Client: &mock.Client{}, + } + initStatsCtx := mock.NewContext() + initStatsCtx.Store = &mock.Store{ + Client: &mock.Client{}, + } + ctx.GetSessionVars().CurrentDB = "test" + do := domain.NewMockDomain() + if err := do.CreateStatsHandle(ctx, initStatsCtx); err != nil { + panic(fmt.Sprintf("create mock context panic: %+v", err)) + } + domain.BindDomain(ctx, do) + return ctx +} + +// MockPartitionInfoSchema mocks an info schema for partition table. +func MockPartitionInfoSchema(definitions []model.PartitionDefinition) infoschema.InfoSchema { + tableInfo := MockSignedTable() + cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) + cols = append(cols, tableInfo.Columns...) + last := tableInfo.Columns[len(tableInfo.Columns)-1] + cols = append(cols, &model.ColumnInfo{ + State: model.StatePublic, + Offset: last.Offset + 1, + Name: model.NewCIStr("ptn"), + FieldType: newLongType(), + ID: last.ID + 1, + }) + partition := &model.PartitionInfo{ + Type: model.PartitionTypeRange, + Expr: "ptn", + Enable: true, + Definitions: definitions, + } + tableInfo.Columns = cols + tableInfo.Partition = partition + is := infoschema.MockInfoSchema([]*model.TableInfo{tableInfo}) + return is +} + +// MockRangePartitionTable mocks a range partition table for test +func MockRangePartitionTable() *model.TableInfo { + definitions := []model.PartitionDefinition{ + { + ID: 41, + Name: model.NewCIStr("p1"), + LessThan: []string{"16"}, + }, + { + ID: 42, + Name: model.NewCIStr("p2"), + LessThan: []string{"32"}, + }, + } + tableInfo := MockSignedTable() + tableInfo.Name = model.NewCIStr("pt1") + cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) + cols = append(cols, tableInfo.Columns...) + last := tableInfo.Columns[len(tableInfo.Columns)-1] + cols = append(cols, &model.ColumnInfo{ + State: model.StatePublic, + Offset: last.Offset + 1, + Name: model.NewCIStr("ptn"), + FieldType: newLongType(), + ID: last.ID + 1, + }) + partition := &model.PartitionInfo{ + Type: model.PartitionTypeRange, + Expr: "ptn", + Enable: true, + Definitions: definitions, + } + tableInfo.Columns = cols + tableInfo.Partition = partition + return tableInfo +} + +// MockHashPartitionTable mocks a hash partition table for test +func MockHashPartitionTable() *model.TableInfo { + definitions := []model.PartitionDefinition{ + { + ID: 51, + Name: model.NewCIStr("p1"), + }, + { + ID: 52, + Name: model.NewCIStr("p2"), + }, + } + tableInfo := MockSignedTable() + tableInfo.Name = model.NewCIStr("pt2") + cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) + cols = append(cols, tableInfo.Columns...) + last := tableInfo.Columns[len(tableInfo.Columns)-1] + cols = append(cols, &model.ColumnInfo{ + State: model.StatePublic, + Offset: last.Offset + 1, + Name: model.NewCIStr("ptn"), + FieldType: newLongType(), + ID: last.ID + 1, + }) + partition := &model.PartitionInfo{ + Type: model.PartitionTypeHash, + Expr: "ptn", + Enable: true, + Definitions: definitions, + Num: 2, + } + tableInfo.Columns = cols + tableInfo.Partition = partition + return tableInfo +} + +// MockListPartitionTable mocks a list partition table for test +func MockListPartitionTable() *model.TableInfo { + definitions := []model.PartitionDefinition{ + { + ID: 61, + Name: model.NewCIStr("p1"), + InValues: [][]string{ + { + "1", + }, + }, + }, + { + ID: 62, + Name: model.NewCIStr("p2"), + InValues: [][]string{ + { + "2", + }, + }, + }, + } + tableInfo := MockSignedTable() + tableInfo.Name = model.NewCIStr("pt3") + cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) + cols = append(cols, tableInfo.Columns...) + last := tableInfo.Columns[len(tableInfo.Columns)-1] + cols = append(cols, &model.ColumnInfo{ + State: model.StatePublic, + Offset: last.Offset + 1, + Name: model.NewCIStr("ptn"), + FieldType: newLongType(), + ID: last.ID + 1, + }) + partition := &model.PartitionInfo{ + Type: model.PartitionTypeList, + Expr: "ptn", + Enable: true, + Definitions: definitions, + Num: 2, + } + tableInfo.Columns = cols + tableInfo.Partition = partition + return tableInfo +} + +// MockStateNoneColumnTable is only used for plan related tests. +func MockStateNoneColumnTable() *model.TableInfo { + // column: a, b + // PK: a + // indeices: b + indices := []*model.IndexInfo{ + { + Name: model.NewCIStr("b"), + Columns: []*model.IndexColumn{ + { + Name: model.NewCIStr("b"), + Length: types.UnspecifiedLength, + Offset: 1, + }, + }, + State: model.StatePublic, + Unique: true, + }, + } + pkColumn := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 0, + Name: model.NewCIStr("a"), + FieldType: newLongType(), + ID: 1, + } + col0 := &model.ColumnInfo{ + State: model.StatePublic, + Offset: 1, + Name: model.NewCIStr("b"), + FieldType: newLongType(), + ID: 2, + } + col1 := &model.ColumnInfo{ + State: model.StateNone, + Offset: 2, + Name: model.NewCIStr("c"), + FieldType: newLongType(), + ID: 3, + } + pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag) + col0.SetFlag(mysql.NotNullFlag) + col1.SetFlag(mysql.UnsignedFlag) + table := &model.TableInfo{ + Columns: []*model.ColumnInfo{pkColumn, col0, col1}, + Indices: indices, + Name: model.NewCIStr("T_StateNoneColumn"), + PKIsHandle: true, + } + return table +} diff --git a/planner/core/optimizer.go b/pkg/planner/core/optimizer.go similarity index 98% rename from planner/core/optimizer.go rename to pkg/planner/core/optimizer.go index fa7c2396ecb77..bd552f83706a7 100644 --- a/planner/core/optimizer.go +++ b/pkg/planner/core/optimizer.go @@ -27,28 +27,28 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/diagnosticspb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/lock" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - utilhint "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/lock" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + utilhint "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/pingcap/tipb/go-tipb" "go.uber.org/atomic" "go.uber.org/zap" diff --git a/planner/core/optimizer_test.go b/pkg/planner/core/optimizer_test.go similarity index 97% rename from planner/core/optimizer_test.go rename to pkg/planner/core/optimizer_test.go index 90da2e886bce8..5e876d29caecc 100644 --- a/planner/core/optimizer_test.go +++ b/pkg/planner/core/optimizer_test.go @@ -20,12 +20,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/types" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) @@ -299,11 +299,11 @@ func TestHandleFineGrainedShuffle(t *testing.T) { "tiflash,127.0.0.1:3933,127.0.0.1:7777,,", "tikv,127.0.0.1:11080,127.0.0.1:10080,,", } - fpName := "github.com/pingcap/tidb/infoschema/mockStoreServerInfo" + fpName := "github.com/pingcap/tidb/pkg/infoschema/mockStoreServerInfo" fpExpr := `return("` + strings.Join(instances, ";") + `")` require.NoError(t, failpoint.Enable(fpName, fpExpr)) defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - fpName2 := "github.com/pingcap/tidb/planner/core/mockTiFlashStreamCountUsingMinLogicalCores" + fpName2 := "github.com/pingcap/tidb/pkg/planner/core/mockTiFlashStreamCountUsingMinLogicalCores" require.NoError(t, failpoint.Enable(fpName2, `return("8")`)) sctx.GetSessionVars().TiFlashFineGrainedShuffleStreamCount = 0 diff --git a/planner/core/partition_prune.go b/pkg/planner/core/partition_prune.go similarity index 89% rename from planner/core/partition_prune.go rename to pkg/planner/core/partition_prune.go index 6989f49dc1400..29defea381f68 100644 --- a/planner/core/partition_prune.go +++ b/pkg/planner/core/partition_prune.go @@ -15,11 +15,11 @@ package core import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" ) // PartitionPruning finds all used partitions according to query conditions, it will diff --git a/planner/core/partition_pruning_test.go b/pkg/planner/core/partition_pruning_test.go similarity index 98% rename from planner/core/partition_pruning_test.go rename to pkg/planner/core/partition_pruning_test.go index f11c4209d6543..e65499dc32182 100644 --- a/planner/core/partition_pruning_test.go +++ b/pkg/planner/core/partition_pruning_test.go @@ -20,14 +20,14 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit/ddlhelper" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit/ddlhelper" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/planner/core/pb_to_plan.go b/pkg/planner/core/pb_to_plan.go similarity index 96% rename from planner/core/pb_to_plan.go rename to pkg/planner/core/pb_to_plan.go index 9b00624374fd2..767c7e609501a 100644 --- a/planner/core/pb_to_plan.go +++ b/pkg/planner/core/pb_to_plan.go @@ -19,15 +19,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/planner/core/physical_plan_test.go b/pkg/planner/core/physical_plan_test.go new file mode 100644 index 0000000000000..7f502ed09acae --- /dev/null +++ b/pkg/planner/core/physical_plan_test.go @@ -0,0 +1,471 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "context" + "fmt" + "math" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/stretchr/testify/require" +) + +func TestAnalyzeBuildSucc(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tests := []struct { + sql string + succ bool + statsVer int + }{ + { + sql: "analyze table t with 0.1 samplerate", + succ: true, + statsVer: 2, + }, + { + sql: "analyze table t with 0.1 samplerate", + succ: false, + statsVer: 1, + }, + { + sql: "analyze table t with 10 samplerate", + succ: false, + statsVer: 2, + }, + { + sql: "analyze table t with 0.1 samplerate, 100000 samples", + succ: false, + statsVer: 2, + }, + { + sql: "analyze table t with 0.1 samplerate, 100000 samples", + succ: false, + statsVer: 1, + }, + } + + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, tt := range tests { + comment := fmt.Sprintf("The %v-th test failed", i) + tk.MustExec(fmt.Sprintf("set @@tidb_analyze_version=%v", tt.statsVer)) + + stmt, err := p.ParseOneStmt(tt.sql, "", "") + if tt.succ { + require.NoError(t, err, comment) + } else if err != nil { + continue + } + err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) + require.NoError(t, err) + _, _, err = planner.Optimize(context.Background(), tk.Session(), stmt, is) + if tt.succ { + require.NoError(t, err, comment) + } else { + require.Error(t, err, comment) + } + } +} + +func TestAnalyzeSetRate(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + tests := []struct { + sql string + rate float64 + }{ + { + sql: "analyze table t", + rate: -1, + }, + { + sql: "analyze table t with 0.1 samplerate", + rate: 0.1, + }, + { + sql: "analyze table t with 10000 samples", + rate: -1, + }, + } + + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + for i, tt := range tests { + comment := fmt.Sprintf("The %v-th test failed", i) + stmt, err := p.ParseOneStmt(tt.sql, "", "") + require.NoError(t, err, comment) + + err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) + require.NoError(t, err, comment) + p, _, err := planner.Optimize(context.Background(), tk.Session(), stmt, is) + require.NoError(t, err, comment) + ana := p.(*core.Analyze) + require.Equal(t, tt.rate, math.Float64frombits(ana.Opts[ast.AnalyzeOptSampleRate])) + } +} + +type overrideStore struct{ kv.Storage } + +func (store overrideStore) GetClient() kv.Client { + cli := store.Storage.GetClient() + return overrideClient{cli} +} + +type overrideClient struct{ kv.Client } + +func (cli overrideClient) IsRequestTypeSupported(_, _ int64) bool { + return false +} + +func TestRequestTypeSupportedOff(t *testing.T) { + store := testkit.CreateMockStore(t) + se, err := session.CreateSession4Test(overrideStore{store}) + require.NoError(t, err) + _, err = se.Execute(context.Background(), "use test") + require.NoError(t, err) + + sql := "select * from t where a in (1, 10, 20)" + expect := "TableReader(Table(t))->Sel([in(test.t.a, 1, 10, 20)])" + + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + stmt, err := parser.New().ParseOneStmt(sql, "", "") + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), se, stmt, is) + require.NoError(t, err) + require.Equal(t, expect, core.ToString(p), fmt.Sprintf("sql: %s", sql)) +} + +func TestDoSubQuery(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tests := []struct { + sql string + best string + }{ + { + sql: "do 1 in (select a from t)", + best: "LeftHashJoin{Dual->PointGet(Handle(t.a)1)}->Projection", + }, + } + + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for _, tt := range tests { + comment := fmt.Sprintf("for %s", tt.sql) + stmt, err := p.ParseOneStmt(tt.sql, "", "") + require.NoError(t, err, comment) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err) + require.Equal(t, tt.best, core.ToString(p), comment) + } +} + +func TestIndexLookupCartesianJoin(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + stmt, err := parser.New().ParseOneStmt("select /*+ TIDB_INLJ(t1, t2) */ * from t t1 join t t2", "", "") + require.NoError(t, err) + + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err) + require.Equal(t, "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}", core.ToString(p)) + + warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() + lastWarn := warnings[len(warnings)-1] + err = core.ErrInternal.GenWithStack("TIDB_INLJ hint is inapplicable without column equal ON condition") + require.True(t, terror.ErrorEqual(err, lastWarn.Err)) +} + +func TestMPPHintsWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int, b int, c int)") + tk.MustExec("alter table t set tiflash replica 1") + tk.MustExec("set @@session.tidb_allow_mpp=ON") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tk.MustExec("explain select a, sum(b) from t group by a, c") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select a, sum(b) from t group by a, c using select /*+ read_from_storage(tiflash[t]), MPP_1PHASE_AGG() */ a, sum(b) from t group by a, c;") + tk.MustExec("explain select a, sum(b) from t group by a, c") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select `a` , sum ( `b` ) from `test` . `t` group by `a` , `c`") + require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t`]) MPP_1PHASE_AGG()*/ `a`,sum(`b`) FROM `test`.`t` GROUP BY `a`,`c`") + tk.MustExec("create global binding for select a, sum(b) from t group by a, c using select /*+ read_from_storage(tiflash[t]), MPP_2PHASE_AGG() */ a, sum(b) from t group by a, c;") + tk.MustExec("explain select a, sum(b) from t group by a, c") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select `a` , sum ( `b` ) from `test` . `t` group by `a` , `c`") + require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t`]) MPP_2PHASE_AGG()*/ `a`,sum(`b`) FROM `test`.`t` GROUP BY `a`,`c`") + tk.MustExec("drop global binding for select a, sum(b) from t group by a, c;") + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) + + tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select * from t t1, t t2 where t1.a=t2.a using select /*+ read_from_storage(tiflash[t1, t2]), shuffle_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") + tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` ) join `test` . `t` as `t2` where `t1` . `a` = `t2` . `a`") + require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t1`, `t2`]) shuffle_join(`t1`, `t2`)*/ * FROM (`test`.`t` AS `t1`) JOIN `test`.`t` AS `t2` WHERE `t1`.`a` = `t2`.`a`") + tk.MustExec("create global binding for select * from t t1, t t2 where t1.a=t2.a using select /*+ read_from_storage(tiflash[t1, t2]), broadcast_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a;") + tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` ) join `test` . `t` as `t2` where `t1` . `a` = `t2` . `a`") + require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t1`, `t2`]) broadcast_join(`t1`, `t2`)*/ * FROM (`test`.`t` AS `t1`) JOIN `test`.`t` AS `t2` WHERE `t1`.`a` = `t2`.`a`") + tk.MustExec("drop global binding for select * from t t1, t t2 where t1.a=t2.a;") + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) +} + +func TestJoinHintCompatibilityWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec("create global binding for select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b using select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` join `test` . `t` as `t2` ) join `test` . `t` as `t3` where `t1` . `a` = `t2` . `a` and `t2` . `b` = `t3` . `b`") + require.Equal(t, res[0][1], "SELECT /*+ leading(`t2`) hash_join(`t2`)*/ * FROM (`test`.`t` AS `t1` JOIN `test`.`t` AS `t2`) JOIN `test`.`t` AS `t3` WHERE `t1`.`a` = `t2`.`a` AND `t2`.`b` = `t3`.`b`") + tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec("drop global binding for select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) +} + +func TestJoinHintCompatibilityWithVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") + tb := external.GetTableByName(t, tk, "test", "t") + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tk.MustExec("set @@session.tidb_opt_advanced_join_hint=0") + tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") + res := tk.MustQuery("show warnings").Rows() + require.Equal(t, len(res) > 0, true) +} + +func TestHintAlias(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tests := []struct { + sql1 string + sql2 string + }{ + { + sql1: "select /*+ TIDB_SMJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_INLJ(t3) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + sql2: "select /*+ MERGE_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ INL_JOIN(t3) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + }, + { + sql1: "select /*+ TIDB_HJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_SMJ(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + sql2: "select /*+ HASH_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ MERGE_JOIN(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + }, + { + sql1: "select /*+ TIDB_INLJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_HJ(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + sql2: "select /*+ INL_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ HASH_JOIN(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", + }, + } + ctx := context.TODO() + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for i, tt := range tests { + comment := fmt.Sprintf("case:%v sql1:%s sql2:%s", i, tt.sql1, tt.sql2) + stmt1, err := p.ParseOneStmt(tt.sql1, "", "") + require.NoError(t, err, comment) + stmt2, err := p.ParseOneStmt(tt.sql2, "", "") + require.NoError(t, err, comment) + + p1, _, err := planner.Optimize(ctx, tk.Session(), stmt1, is) + require.NoError(t, err) + p2, _, err := planner.Optimize(ctx, tk.Session(), stmt2, is) + require.NoError(t, err) + + require.Equal(t, core.ToString(p2), core.ToString(p1)) + } +} + +func TestDAGPlanBuilderSplitAvg(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tests := []struct { + sql string + plan string + }{ + { + sql: "select avg(a),avg(b),avg(c) from t", + plan: "TableReader(Table(t)->HashAgg)->HashAgg", + }, + { + sql: "select /*+ HASH_AGG() */ avg(a),avg(b),avg(c) from t", + plan: "TableReader(Table(t)->HashAgg)->HashAgg", + }, + } + + p := parser.New() + is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) + + for _, tt := range tests { + comment := fmt.Sprintf("for %s", tt.sql) + stmt, err := p.ParseOneStmt(tt.sql, "", "") + require.NoError(t, err, comment) + + err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) + require.NoError(t, err) + p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) + require.NoError(t, err, comment) + + require.Equal(t, tt.plan, core.ToString(p), comment) + root, ok := p.(core.PhysicalPlan) + if !ok { + continue + } + testDAGPlanBuilderSplitAvg(t, root) + } +} + +func testDAGPlanBuilderSplitAvg(t *testing.T, root core.PhysicalPlan) { + if p, ok := root.(*core.PhysicalTableReader); ok { + if p.TablePlans != nil { + baseAgg := p.TablePlans[len(p.TablePlans)-1] + if agg, ok := baseAgg.(*core.PhysicalHashAgg); ok { + for i, aggfunc := range agg.AggFuncs { + require.Equal(t, aggfunc.RetTp, agg.Schema().Columns[i].RetType) + } + } + if agg, ok := baseAgg.(*core.PhysicalStreamAgg); ok { + for i, aggfunc := range agg.AggFuncs { + require.Equal(t, aggfunc.RetTp, agg.Schema().Columns[i].RetType) + } + } + } + } + + childs := root.Children() + if childs == nil { + return + } + for _, son := range childs { + testDAGPlanBuilderSplitAvg(t, son) + } +} + +func TestHJBuildAndProbeHintWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec("create table t(a int, b int, key(a));") + tk.MustExec("create table t1(a int, b int, key(a));") + tk.MustExec("create table t2(a int, b int, key(a));") + tk.MustExec("create table t3(a int, b int, key(a));") + + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ hash_join_build(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ hash_join_build(t1)*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") + + tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ hash_join_probe(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ hash_join_probe(t1)*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") + + tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) +} + +func TestPhysicalPlanMemoryTrace(t *testing.T) { + // PhysicalSort + ls := core.PhysicalSort{} + size := ls.MemoryUsage() + ls.ByItems = append(ls.ByItems, &util.ByItems{}) + require.Greater(t, ls.MemoryUsage(), size) + + // PhysicalProperty + pp := property.PhysicalProperty{} + size = pp.MemoryUsage() + pp.MPPPartitionCols = append(pp.MPPPartitionCols, &property.MPPPartitionColumn{}) + require.Greater(t, pp.MemoryUsage(), size) +} diff --git a/planner/core/physical_plan_trace_test.go b/pkg/planner/core/physical_plan_trace_test.go similarity index 96% rename from planner/core/physical_plan_trace_test.go rename to pkg/planner/core/physical_plan_trace_test.go index 583720af678f5..55f8ad8c2c589 100644 --- a/planner/core/physical_plan_trace_test.go +++ b/pkg/planner/core/physical_plan_trace_test.go @@ -19,13 +19,13 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/stretchr/testify/require" ) diff --git a/planner/core/physical_plans.go b/pkg/planner/core/physical_plans.go similarity index 98% rename from planner/core/physical_plans.go rename to pkg/planner/core/physical_plans.go index 1e43c6809b3c5..c40ce39b04524 100644 --- a/planner/core/physical_plans.go +++ b/pkg/planner/core/physical_plans.go @@ -21,26 +21,26 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/planner/core/plan.go b/pkg/planner/core/plan.go new file mode 100644 index 0000000000000..b16364cbc555a --- /dev/null +++ b/pkg/planner/core/plan.go @@ -0,0 +1,855 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package core + +import ( + "fmt" + "math" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + fd "github.com/pingcap/tidb/pkg/planner/funcdep" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tipb/go-tipb" +) + +// Plan is the description of an execution flow. +// It is created from ast.Node first, then optimized by the optimizer, +// finally used by the executor to create a Cursor which executes the statement. +type Plan interface { + // Get the schema. + Schema() *expression.Schema + + // Get the ID. + ID() int + + // TP get the plan type. + TP() string + + // Get the ID in explain statement + ExplainID() fmt.Stringer + + // ExplainInfo returns operator information to be explained. + ExplainInfo() string + + // ReplaceExprColumns replace all the column reference in the plan's expression node. + ReplaceExprColumns(replace map[string]*expression.Column) + + SCtx() sessionctx.Context + + // StatsInfo will return the property.StatsInfo for this plan. + StatsInfo() *property.StatsInfo + + // OutputNames returns the outputting names of each column. + OutputNames() types.NameSlice + + // SetOutputNames sets the outputting name by the given slice. + SetOutputNames(names types.NameSlice) + + SelectBlockOffset() int + + BuildPlanTrace() *tracing.PlanTrace +} + +func enforceProperty(p *property.PhysicalProperty, tsk task, ctx sessionctx.Context) task { + if p.TaskTp == property.MppTaskType { + mpp, ok := tsk.(*mppTask) + if !ok || mpp.invalid() { + return invalidTask + } + if !p.IsSortItemAllForPartition() { + ctx.GetSessionVars().RaiseWarningWhenMPPEnforced("MPP mode may be blocked because operator `Sort` is not supported now.") + return invalidTask + } + tsk = mpp.enforceExchanger(p) + } + if p.IsSortItemEmpty() || tsk.plan() == nil { + return tsk + } + if p.TaskTp != property.MppTaskType { + tsk = tsk.convertToRootTask(ctx) + } + sortReqProp := &property.PhysicalProperty{TaskTp: property.RootTaskType, SortItems: p.SortItems, ExpectedCnt: math.MaxFloat64} + sort := PhysicalSort{ + ByItems: make([]*util.ByItems, 0, len(p.SortItems)), + IsPartialSort: p.IsSortItemAllForPartition(), + }.Init(ctx, tsk.plan().StatsInfo(), tsk.plan().SelectBlockOffset(), sortReqProp) + for _, col := range p.SortItems { + sort.ByItems = append(sort.ByItems, &util.ByItems{Expr: col.Col, Desc: col.Desc}) + } + return sort.attach2Task(tsk) +} + +// optimizeByShuffle insert `PhysicalShuffle` to optimize performance by running in a parallel manner. +func optimizeByShuffle(tsk task, ctx sessionctx.Context) task { + if tsk.plan() == nil { + return tsk + } + + switch p := tsk.plan().(type) { + case *PhysicalWindow: + if shuffle := optimizeByShuffle4Window(p, ctx); shuffle != nil { + return shuffle.attach2Task(tsk) + } + case *PhysicalMergeJoin: + if shuffle := optimizeByShuffle4MergeJoin(p, ctx); shuffle != nil { + return shuffle.attach2Task(tsk) + } + case *PhysicalStreamAgg: + if shuffle := optimizeByShuffle4StreamAgg(p, ctx); shuffle != nil { + return shuffle.attach2Task(tsk) + } + } + return tsk +} + +func optimizeByShuffle4Window(pp *PhysicalWindow, ctx sessionctx.Context) *PhysicalShuffle { + concurrency := ctx.GetSessionVars().WindowConcurrency() + if concurrency <= 1 { + return nil + } + + sort, ok := pp.Children()[0].(*PhysicalSort) + if !ok { + // Multi-thread executing on SORTED data source is not effective enough by current implementation. + // TODO: Implement a better one. + return nil + } + tail, dataSource := sort, sort.Children()[0] + + partitionBy := make([]*expression.Column, 0, len(pp.PartitionBy)) + for _, item := range pp.PartitionBy { + partitionBy = append(partitionBy, item.Col) + } + ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(partitionBy, dataSource.Schema(), dataSource.StatsInfo()) + if ndv <= 1 { + return nil + } + concurrency = mathutil.Min(concurrency, int(ndv)) + + byItems := make([]expression.Expression, 0, len(pp.PartitionBy)) + for _, item := range pp.PartitionBy { + byItems = append(byItems, item.Col) + } + reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} + shuffle := PhysicalShuffle{ + Concurrency: concurrency, + Tails: []PhysicalPlan{tail}, + DataSources: []PhysicalPlan{dataSource}, + SplitterType: PartitionHashSplitterType, + ByItemArrays: [][]expression.Expression{byItems}, + }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) + return shuffle +} + +func optimizeByShuffle4StreamAgg(pp *PhysicalStreamAgg, ctx sessionctx.Context) *PhysicalShuffle { + concurrency := ctx.GetSessionVars().StreamAggConcurrency() + if concurrency <= 1 { + return nil + } + + sort, ok := pp.Children()[0].(*PhysicalSort) + if !ok { + // Multi-thread executing on SORTED data source is not effective enough by current implementation. + // TODO: Implement a better one. + return nil + } + tail, dataSource := sort, sort.Children()[0] + + partitionBy := make([]*expression.Column, 0, len(pp.GroupByItems)) + for _, item := range pp.GroupByItems { + if col, ok := item.(*expression.Column); ok { + partitionBy = append(partitionBy, col) + } + } + ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(partitionBy, dataSource.Schema(), dataSource.StatsInfo()) + if ndv <= 1 { + return nil + } + concurrency = mathutil.Min(concurrency, int(ndv)) + + reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} + shuffle := PhysicalShuffle{ + Concurrency: concurrency, + Tails: []PhysicalPlan{tail}, + DataSources: []PhysicalPlan{dataSource}, + SplitterType: PartitionHashSplitterType, + ByItemArrays: [][]expression.Expression{util.CloneExprs(pp.GroupByItems)}, + }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) + return shuffle +} + +func optimizeByShuffle4MergeJoin(pp *PhysicalMergeJoin, ctx sessionctx.Context) *PhysicalShuffle { + concurrency := ctx.GetSessionVars().MergeJoinConcurrency() + if concurrency <= 1 { + return nil + } + + children := pp.Children() + dataSources := make([]PhysicalPlan, len(children)) + tails := make([]PhysicalPlan, len(children)) + + for i := range children { + sort, ok := children[i].(*PhysicalSort) + if !ok { + // Multi-thread executing on SORTED data source is not effective enough by current implementation. + // TODO: Implement a better one. + return nil + } + tails[i], dataSources[i] = sort, sort.Children()[0] + } + + leftByItemArray := make([]expression.Expression, 0, len(pp.LeftJoinKeys)) + for _, col := range pp.LeftJoinKeys { + leftByItemArray = append(leftByItemArray, col.Clone()) + } + rightByItemArray := make([]expression.Expression, 0, len(pp.RightJoinKeys)) + for _, col := range pp.RightJoinKeys { + rightByItemArray = append(rightByItemArray, col.Clone()) + } + reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} + shuffle := PhysicalShuffle{ + Concurrency: concurrency, + Tails: tails, + DataSources: dataSources, + SplitterType: PartitionHashSplitterType, + ByItemArrays: [][]expression.Expression{leftByItemArray, rightByItemArray}, + }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) + return shuffle +} + +// LogicalPlan is a tree of logical operators. +// We can do a lot of logical optimizations to it, like predicate pushdown and column pruning. +type LogicalPlan interface { + Plan + + // HashCode encodes a LogicalPlan to fast compare whether a LogicalPlan equals to another. + // We use a strict encode method here which ensures there is no conflict. + HashCode() []byte + + // PredicatePushDown pushes down the predicates in the where/on/having clauses as deeply as possible. + // It will accept a predicate that is an expression slice, and return the expressions that can't be pushed. + // Because it might change the root if the having clause exists, we need to return a plan that represents a new root. + PredicatePushDown([]expression.Expression, *logicalOptimizeOp) ([]expression.Expression, LogicalPlan) + + // PruneColumns prunes the unused columns. + PruneColumns([]*expression.Column, *logicalOptimizeOp) error + + // findBestTask converts the logical plan to the physical plan. It's a new interface. + // It is called recursively from the parent to the children to create the result physical plan. + // Some logical plans will convert the children to the physical plans in different ways, and return the one + // With the lowest cost and how many plans are found in this function. + // planCounter is a counter for planner to force a plan. + // If planCounter > 0, the clock_th plan generated in this function will be returned. + // If planCounter = 0, the plan generated in this function will not be considered. + // If planCounter = -1, then we will not force plan. + findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, op *physicalOptimizeOp) (task, int64, error) + + // BuildKeyInfo will collect the information of unique keys into schema. + // Because this method is also used in cascades planner, we cannot use + // things like `p.schema` or `p.children` inside it. We should use the `selfSchema` + // and `childSchema` instead. + BuildKeyInfo(selfSchema *expression.Schema, childSchema []*expression.Schema) + + // pushDownTopN will push down the topN or limit operator during logical optimization. + pushDownTopN(topN *LogicalTopN, opt *logicalOptimizeOp) LogicalPlan + + // deriveTopN derives an implicit TopN from a filter on row_number window function.. + deriveTopN(opt *logicalOptimizeOp) LogicalPlan + + // predicateSimplification consolidates different predcicates on a column and its equivalence classes. + predicateSimplification(opt *logicalOptimizeOp) LogicalPlan + + // constantPropagation generate new constant predicate according to column equivalence relation + constantPropagation(parentPlan LogicalPlan, currentChildIdx int, opt *logicalOptimizeOp) (newRoot LogicalPlan) + + // pullUpConstantPredicates recursive find constant predicate, used for the constant propagation rule + pullUpConstantPredicates() []expression.Expression + + // recursiveDeriveStats derives statistic info between plans. + recursiveDeriveStats(colGroups [][]*expression.Column) (*property.StatsInfo, error) + + // DeriveStats derives statistic info for current plan node given child stats. + // We need selfSchema, childSchema here because it makes this method can be used in + // cascades planner, where LogicalPlan might not record its children or schema. + DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) + + // ExtractColGroups extracts column groups from child operator whose DNVs are required by the current operator. + // For example, if current operator is LogicalAggregation of `Group By a, b`, we indicate the child operators to maintain + // and propagate the NDV info of column group (a, b), to improve the row count estimation of current LogicalAggregation. + // The parameter colGroups are column groups required by upper operators, besides from the column groups derived from + // current operator, we should pass down parent colGroups to child operator as many as possible. + ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column + + // PreparePossibleProperties is only used for join and aggregation. Like group by a,b,c, all permutation of (a,b,c) is + // valid, but the ordered indices in leaf plan is limited. So we can get all possible order properties by a pre-walking. + PreparePossibleProperties(schema *expression.Schema, childrenProperties ...[][]*expression.Column) [][]*expression.Column + + // exhaustPhysicalPlans generates all possible plans that can match the required property. + // It will return: + // 1. All possible plans that can match the required property. + // 2. Whether the SQL hint can work. Return true if there is no hint. + exhaustPhysicalPlans(*property.PhysicalProperty) (physicalPlans []PhysicalPlan, hintCanWork bool, err error) + + // ExtractCorrelatedCols extracts correlated columns inside the LogicalPlan. + ExtractCorrelatedCols() []*expression.CorrelatedColumn + + // MaxOneRow means whether this operator only returns max one row. + MaxOneRow() bool + + // Get all the children. + Children() []LogicalPlan + + // SetChildren sets the children for the plan. + SetChildren(...LogicalPlan) + + // SetChild sets the ith child for the plan. + SetChild(i int, child LogicalPlan) + + // rollBackTaskMap roll back all taskMap's logs after TimeStamp TS. + rollBackTaskMap(TS uint64) + + // canPushToCop check if we might push this plan to a specific store. + canPushToCop(store kv.StoreType) bool + + // ExtractFD derive the FDSet from the tree bottom up. + ExtractFD() *fd.FDSet +} + +// PhysicalPlan is a tree of the physical operators. +type PhysicalPlan interface { + Plan + + // getPlanCostVer1 calculates the cost of the plan if it has not been calculated yet and returns the cost on model ver1. + getPlanCostVer1(taskType property.TaskType, option *PlanCostOption) (float64, error) + + // getPlanCostVer2 calculates the cost of the plan if it has not been calculated yet and returns the cost on model ver2. + getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) + + // attach2Task makes the current physical plan as the father of task's physicalPlan and updates the cost of + // current task. If the child's task is cop task, some operator may close this task and return a new rootTask. + attach2Task(...task) task + + // ToPB converts physical plan to tipb executor. + ToPB(ctx sessionctx.Context, storeType kv.StoreType) (*tipb.Executor, error) + + // GetChildReqProps gets the required property by child index. + GetChildReqProps(idx int) *property.PhysicalProperty + + // StatsCount returns the count of property.StatsInfo for this plan. + StatsCount() float64 + + // ExtractCorrelatedCols extracts correlated columns inside the PhysicalPlan. + ExtractCorrelatedCols() []*expression.CorrelatedColumn + + // Children get all the children. + Children() []PhysicalPlan + + // SetChildren sets the children for the plan. + SetChildren(...PhysicalPlan) + + // SetChild sets the ith child for the plan. + SetChild(i int, child PhysicalPlan) + + // ResolveIndices resolves the indices for columns. After doing this, the columns can evaluate the rows by their indices. + ResolveIndices() error + + // StatsInfo returns the StatsInfo of the plan. + StatsInfo() *property.StatsInfo + + // SetStats sets basePlan.stats inside the basePhysicalPlan. + SetStats(s *property.StatsInfo) + + // ExplainNormalizedInfo returns operator normalized information for generating digest. + ExplainNormalizedInfo() string + + // Clone clones this physical plan. + Clone() (PhysicalPlan, error) + + // appendChildCandidate append child physicalPlan into tracer in order to track each child physicalPlan which can't + // be tracked during findBestTask or enumeratePhysicalPlans4Task + appendChildCandidate(op *physicalOptimizeOp) + + // MemoryUsage return the memory usage of PhysicalPlan + MemoryUsage() int64 + + // Below three methods help to handle the inconsistency between row count in the StatsInfo and the recorded + // actual row count. + // For the children in the inner side (probe side) of Index Join and Apply, the row count in the StatsInfo + // means the estimated row count for a single "probe", but the recorded actual row count is the total row + // count for all "probes". + // To handle this inconsistency without breaking anything else, we added a field `probeParents` of + // type `[]PhysicalPlan` into all PhysicalPlan to record all operators that are (indirect or direct) parents + // of this PhysicalPlan and will cause this inconsistency. + // Using this information, we can convert the row count between the "single probe" row count and "all probes" + // row count freely. + + // setProbeParents sets the above stated `probeParents` field. + setProbeParents([]PhysicalPlan) + // getEstRowCountForDisplay uses the "single probe" row count in StatsInfo and the probeParents to calculate + // the "all probe" row count. + // All places that display the row count for a PhysicalPlan are expected to use this method. + getEstRowCountForDisplay() float64 + // getEstRowCountForDisplay uses the runtime stats and the probeParents to calculate the actual "probe" count. + getActualProbeCnt(*execdetails.RuntimeStatsColl) int64 +} + +// NewDefaultPlanCostOption returns PlanCostOption +func NewDefaultPlanCostOption() *PlanCostOption { + return &PlanCostOption{} +} + +// PlanCostOption indicates option during GetPlanCost +type PlanCostOption struct { + CostFlag uint64 + tracer *physicalOptimizeOp +} + +// WithCostFlag set costflag +func (op *PlanCostOption) WithCostFlag(flag uint64) *PlanCostOption { + if op == nil { + return nil + } + op.CostFlag = flag + return op +} + +// WithOptimizeTracer set tracer +func (op *PlanCostOption) WithOptimizeTracer(v *physicalOptimizeOp) *PlanCostOption { + if op == nil { + return nil + } + op.tracer = v + if v != nil && v.tracer != nil { + op.CostFlag |= CostFlagTrace + } + return op +} + +type baseLogicalPlan struct { + base.Plan + + taskMap map[string]task + // taskMapBak forms a backlog stack of taskMap, used to roll back the taskMap. + taskMapBak []string + // taskMapBakTS stores the timestamps of logs. + taskMapBakTS []uint64 + self LogicalPlan + maxOneRow bool + children []LogicalPlan + // fdSet is a set of functional dependencies(FDs) which powers many optimizations, + // including eliminating unnecessary DISTINCT operators, simplifying ORDER BY columns, + // removing Max1Row operators, and mapping semi-joins to inner-joins. + // for now, it's hard to maintain in individual operator, build it from bottom up when using. + fdSet *fd.FDSet +} + +// ExtractFD return the children[0]'s fdSet if there are no adding/removing fd in this logic plan. +func (p *baseLogicalPlan) ExtractFD() *fd.FDSet { + if p.fdSet != nil { + return p.fdSet + } + fds := &fd.FDSet{HashCodeToUniqueID: make(map[string]int)} + for _, ch := range p.children { + fds.AddFrom(ch.ExtractFD()) + } + return fds +} + +func (p *baseLogicalPlan) MaxOneRow() bool { + return p.maxOneRow +} + +// ExplainInfo implements Plan interface. +func (*baseLogicalPlan) ExplainInfo() string { + return "" +} + +func getEstimatedProbeCntFromProbeParents(probeParents []PhysicalPlan) float64 { + res := float64(1) + for _, pp := range probeParents { + switch pp.(type) { + case *PhysicalApply, *PhysicalIndexHashJoin, *PhysicalIndexMergeJoin, *PhysicalIndexJoin: + if join, ok := pp.(interface{ getInnerChildIdx() int }); ok { + outer := pp.Children()[1-join.getInnerChildIdx()] + res *= outer.StatsInfo().RowCount + } + } + } + return res +} + +func getActualProbeCntFromProbeParents(pps []PhysicalPlan, statsColl *execdetails.RuntimeStatsColl) int64 { + res := int64(1) + for _, pp := range pps { + switch pp.(type) { + case *PhysicalApply, *PhysicalIndexHashJoin, *PhysicalIndexMergeJoin, *PhysicalIndexJoin: + if join, ok := pp.(interface{ getInnerChildIdx() int }); ok { + outerChildID := pp.Children()[1-join.getInnerChildIdx()].ID() + actRows := int64(1) + if statsColl.ExistsRootStats(outerChildID) { + actRows = statsColl.GetRootStats(outerChildID).GetActRows() + } + if statsColl.ExistsCopStats(outerChildID) { + actRows = statsColl.GetCopStats(outerChildID).GetActRows() + } + // TODO: For PhysicalApply, we need to consider cache hit ratio in JoinRuntimeStats and use actRows/(1-ratio) here. + res *= actRows + } + } + } + return res +} + +type basePhysicalPlan struct { + base.Plan + + childrenReqProps []*property.PhysicalProperty + self PhysicalPlan + children []PhysicalPlan + + // used by the new cost interface + planCostInit bool + planCost float64 + planCostVer2 costVer2 + + // probeParents records the IndexJoins and Applys with this operator in their inner children. + // Please see comments in PhysicalPlan for details. + probeParents []PhysicalPlan + + // Only for MPP. If TiFlashFineGrainedShuffleStreamCount > 0: + // 1. For ExchangeSender, means its output will be partitioned by hash key. + // 2. For ExchangeReceiver/Window/Sort, means its input is already partitioned. + TiFlashFineGrainedShuffleStreamCount uint64 +} + +func (p *basePhysicalPlan) cloneWithSelf(newSelf PhysicalPlan) (*basePhysicalPlan, error) { + base := &basePhysicalPlan{ + Plan: p.Plan, + self: newSelf, + TiFlashFineGrainedShuffleStreamCount: p.TiFlashFineGrainedShuffleStreamCount, + probeParents: p.probeParents, + } + for _, child := range p.children { + cloned, err := child.Clone() + if err != nil { + return nil, err + } + base.children = append(base.children, cloned) + } + for _, prop := range p.childrenReqProps { + if prop == nil { + continue + } + base.childrenReqProps = append(base.childrenReqProps, prop.CloneEssentialFields()) + } + return base, nil +} + +// Clone implements PhysicalPlan interface. +func (p *basePhysicalPlan) Clone() (PhysicalPlan, error) { + return nil, errors.Errorf("%T doesn't support cloning", p.self) +} + +// ExplainInfo implements Plan interface. +func (*basePhysicalPlan) ExplainInfo() string { + return "" +} + +// ExplainNormalizedInfo implements PhysicalPlan interface. +func (*basePhysicalPlan) ExplainNormalizedInfo() string { + return "" +} + +func (p *basePhysicalPlan) GetChildReqProps(idx int) *property.PhysicalProperty { + return p.childrenReqProps[idx] +} + +// ExtractCorrelatedCols implements PhysicalPlan interface. +func (*basePhysicalPlan) ExtractCorrelatedCols() []*expression.CorrelatedColumn { + return nil +} + +// MemoryUsage return the memory usage of basePhysicalPlan +func (p *basePhysicalPlan) MemoryUsage() (sum int64) { + if p == nil { + return + } + + sum = p.Plan.MemoryUsage() + size.SizeOfSlice + int64(cap(p.childrenReqProps))*size.SizeOfPointer + + size.SizeOfSlice + int64(cap(p.children)+1)*size.SizeOfInterface + size.SizeOfFloat64 + + size.SizeOfUint64 + size.SizeOfBool + + for _, prop := range p.childrenReqProps { + sum += prop.MemoryUsage() + } + for _, plan := range p.children { + sum += plan.MemoryUsage() + } + return +} + +func (p *basePhysicalPlan) getEstRowCountForDisplay() float64 { + if p == nil { + return 0 + } + return p.StatsInfo().RowCount * getEstimatedProbeCntFromProbeParents(p.probeParents) +} + +func (p *basePhysicalPlan) getActualProbeCnt(statsColl *execdetails.RuntimeStatsColl) int64 { + if p == nil { + return 1 + } + return getActualProbeCntFromProbeParents(p.probeParents, statsColl) +} + +func (p *basePhysicalPlan) setProbeParents(probeParents []PhysicalPlan) { + p.probeParents = probeParents +} + +// GetLogicalTS4TaskMap get the logical TimeStamp now to help rollback the TaskMap changes after that. +func (p *baseLogicalPlan) GetLogicalTS4TaskMap() uint64 { + p.SCtx().GetSessionVars().StmtCtx.TaskMapBakTS++ + return p.SCtx().GetSessionVars().StmtCtx.TaskMapBakTS +} + +func (p *baseLogicalPlan) rollBackTaskMap(ts uint64) { + if !p.SCtx().GetSessionVars().StmtCtx.StmtHints.TaskMapNeedBackUp() { + return + } + if len(p.taskMapBak) > 0 { + // Rollback all the logs with TimeStamp TS. + n := len(p.taskMapBak) + for i := 0; i < n; i++ { + cur := p.taskMapBak[i] + if p.taskMapBakTS[i] < ts { + continue + } + + // Remove the i_th log. + p.taskMapBak = append(p.taskMapBak[:i], p.taskMapBak[i+1:]...) + p.taskMapBakTS = append(p.taskMapBakTS[:i], p.taskMapBakTS[i+1:]...) + i-- + n-- + + // Roll back taskMap. + p.taskMap[cur] = nil + } + } + for _, child := range p.children { + child.rollBackTaskMap(ts) + } +} + +func (p *baseLogicalPlan) getTask(prop *property.PhysicalProperty) task { + key := prop.HashCode() + return p.taskMap[string(key)] +} + +func (p *baseLogicalPlan) storeTask(prop *property.PhysicalProperty, task task) { + key := prop.HashCode() + if p.SCtx().GetSessionVars().StmtCtx.StmtHints.TaskMapNeedBackUp() { + // Empty string for useless change. + ts := p.GetLogicalTS4TaskMap() + p.taskMapBakTS = append(p.taskMapBakTS, ts) + p.taskMapBak = append(p.taskMapBak, string(key)) + } + p.taskMap[string(key)] = task +} + +// HasMaxOneRow returns if the LogicalPlan will output at most one row. +func HasMaxOneRow(p LogicalPlan, childMaxOneRow []bool) bool { + if len(childMaxOneRow) == 0 { + // The reason why we use this check is that, this function + // is used both in planner/core and planner/cascades. + // In cascades planner, LogicalPlan may have no `children`. + return false + } + switch x := p.(type) { + case *LogicalLock, *LogicalLimit, *LogicalSort, *LogicalSelection, + *LogicalApply, *LogicalProjection, *LogicalWindow, *LogicalAggregation: + return childMaxOneRow[0] + case *LogicalMaxOneRow: + return true + case *LogicalJoin: + switch x.JoinType { + case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin: + return childMaxOneRow[0] + default: + return childMaxOneRow[0] && childMaxOneRow[1] + } + } + return false +} + +// BuildKeyInfo implements LogicalPlan BuildKeyInfo interface. +func (p *baseLogicalPlan) BuildKeyInfo(_ *expression.Schema, _ []*expression.Schema) { + childMaxOneRow := make([]bool, len(p.children)) + for i := range p.children { + childMaxOneRow[i] = p.children[i].MaxOneRow() + } + p.maxOneRow = HasMaxOneRow(p.self, childMaxOneRow) +} + +// BuildKeyInfo implements LogicalPlan BuildKeyInfo interface. +func (p *logicalSchemaProducer) BuildKeyInfo(selfSchema *expression.Schema, childSchema []*expression.Schema) { + selfSchema.Keys = nil + p.baseLogicalPlan.BuildKeyInfo(selfSchema, childSchema) + + // default implementation for plans has only one child: proprgate child keys + // multi-children plans are likely to have particular implementation. + if len(childSchema) == 1 { + for _, key := range childSchema[0].Keys { + indices := selfSchema.ColumnsIndices(key) + if indices == nil { + continue + } + newKey := make([]*expression.Column, 0, len(key)) + for _, i := range indices { + newKey = append(newKey, selfSchema.Columns[i]) + } + selfSchema.Keys = append(selfSchema.Keys, newKey) + } + } +} + +func newBaseLogicalPlan(ctx sessionctx.Context, tp string, self LogicalPlan, offset int) baseLogicalPlan { + return baseLogicalPlan{ + taskMap: make(map[string]task), + taskMapBak: make([]string, 0, 10), + taskMapBakTS: make([]uint64, 0, 10), + Plan: base.NewBasePlan(ctx, tp, offset), + self: self, + } +} + +func newBasePhysicalPlan(ctx sessionctx.Context, tp string, self PhysicalPlan, offset int) basePhysicalPlan { + return basePhysicalPlan{ + Plan: base.NewBasePlan(ctx, tp, offset), + self: self, + } +} + +func (*baseLogicalPlan) ExtractCorrelatedCols() []*expression.CorrelatedColumn { + return nil +} + +// PruneColumns implements LogicalPlan interface. +func (p *baseLogicalPlan) PruneColumns(parentUsedCols []*expression.Column, opt *logicalOptimizeOp) error { + if len(p.children) == 0 { + return nil + } + return p.children[0].PruneColumns(parentUsedCols, opt) +} + +// Schema implements Plan Schema interface. +func (p *baseLogicalPlan) Schema() *expression.Schema { + return p.children[0].Schema() +} + +func (p *baseLogicalPlan) OutputNames() types.NameSlice { + return p.children[0].OutputNames() +} + +func (p *baseLogicalPlan) SetOutputNames(names types.NameSlice) { + p.children[0].SetOutputNames(names) +} + +// Schema implements Plan Schema interface. +func (p *basePhysicalPlan) Schema() *expression.Schema { + return p.children[0].Schema() +} + +// Children implements LogicalPlan Children interface. +func (p *baseLogicalPlan) Children() []LogicalPlan { + return p.children +} + +// Children implements PhysicalPlan Children interface. +func (p *basePhysicalPlan) Children() []PhysicalPlan { + return p.children +} + +// SetChildren implements LogicalPlan SetChildren interface. +func (p *baseLogicalPlan) SetChildren(children ...LogicalPlan) { + p.children = children +} + +// SetChildren implements PhysicalPlan SetChildren interface. +func (p *basePhysicalPlan) SetChildren(children ...PhysicalPlan) { + p.children = children +} + +// SetChild implements LogicalPlan SetChild interface. +func (p *baseLogicalPlan) SetChild(i int, child LogicalPlan) { + p.children[i] = child +} + +// SetChild implements PhysicalPlan SetChild interface. +func (p *basePhysicalPlan) SetChild(i int, child PhysicalPlan) { + p.children[i] = child +} + +// BuildPlanTrace implements Plan +func (p *basePhysicalPlan) BuildPlanTrace() *tracing.PlanTrace { + tp := "" + info := "" + if p.self != nil { + tp = p.self.TP() + info = p.self.ExplainInfo() + } + + planTrace := &tracing.PlanTrace{ID: p.ID(), TP: tp, ExplainInfo: info} + for _, child := range p.Children() { + planTrace.Children = append(planTrace.Children, child.BuildPlanTrace()) + } + return planTrace +} + +// BuildPlanTrace implements Plan +func (p *baseLogicalPlan) BuildPlanTrace() *tracing.PlanTrace { + planTrace := &tracing.PlanTrace{ID: p.ID(), TP: p.TP(), ExplainInfo: p.self.ExplainInfo()} + for _, child := range p.Children() { + planTrace.Children = append(planTrace.Children, child.BuildPlanTrace()) + } + return planTrace +} + +func (p *basePhysicalPlan) appendChildCandidate(op *physicalOptimizeOp) { + if len(p.Children()) < 1 { + return + } + childrenID := make([]int, 0) + for _, child := range p.Children() { + childCandidate := &tracing.CandidatePlanTrace{ + PlanTrace: &tracing.PlanTrace{TP: child.TP(), ID: child.ID(), + ExplainInfo: child.ExplainInfo()}, + } + op.tracer.AppendCandidate(childCandidate) + child.appendChildCandidate(op) + childrenID = append(childrenID, child.ID()) + } + op.tracer.Candidates[p.ID()].PlanTrace.AppendChildrenID(childrenID...) +} diff --git a/planner/core/plan_cache.go b/pkg/planner/core/plan_cache.go similarity index 96% rename from planner/core/plan_cache.go rename to pkg/planner/core/plan_cache.go index 94d85216fd8a1..9cad0384c919b 100644 --- a/planner/core/plan_cache.go +++ b/pkg/planner/core/plan_cache.go @@ -18,29 +18,29 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - core_metrics "github.com/pingcap/tidb/planner/core/metrics" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/kvcache" - utilpc "github.com/pingcap/tidb/util/plancache" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + core_metrics "github.com/pingcap/tidb/pkg/planner/core/metrics" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/kvcache" + utilpc "github.com/pingcap/tidb/pkg/util/plancache" + "github.com/pingcap/tidb/pkg/util/ranger" ) // PlanCacheKeyTestIssue43667 is only for test. diff --git a/planner/core/plan_cache_lru.go b/pkg/planner/core/plan_cache_lru.go similarity index 96% rename from planner/core/plan_cache_lru.go rename to pkg/planner/core/plan_cache_lru.go index 42cdc034eb2cf..ce74307738e29 100644 --- a/planner/core/plan_cache_lru.go +++ b/pkg/planner/core/plan_cache_lru.go @@ -17,14 +17,14 @@ import ( "container/list" "github.com/pingcap/errors" - core_metrics "github.com/pingcap/tidb/planner/core/metrics" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - utilpc "github.com/pingcap/tidb/util/plancache" - "github.com/pingcap/tidb/util/syncutil" + core_metrics "github.com/pingcap/tidb/pkg/planner/core/metrics" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + utilpc "github.com/pingcap/tidb/pkg/util/plancache" + "github.com/pingcap/tidb/pkg/util/syncutil" ) // planCacheEntry wraps Key and Value. It's the value of list.Element. diff --git a/planner/core/plan_cache_lru_test.go b/pkg/planner/core/plan_cache_lru_test.go similarity index 98% rename from planner/core/plan_cache_lru_test.go rename to pkg/planner/core/plan_cache_lru_test.go index 5b3e0a5309e3c..0efe8a49e5ca9 100644 --- a/planner/core/plan_cache_lru_test.go +++ b/pkg/planner/core/plan_cache_lru_test.go @@ -19,12 +19,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" - utilpc "github.com/pingcap/tidb/util/plancache" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" + utilpc "github.com/pingcap/tidb/pkg/util/plancache" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cache_param.go b/pkg/planner/core/plan_cache_param.go similarity index 95% rename from planner/core/plan_cache_param.go rename to pkg/planner/core/plan_cache_param.go index 63d15054bf78a..fcd2d37c1b58f 100644 --- a/planner/core/plan_cache_param.go +++ b/pkg/planner/core/plan_cache_param.go @@ -19,13 +19,13 @@ import ( "errors" "sync" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" ) var ( diff --git a/planner/core/plan_cache_param_test.go b/pkg/planner/core/plan_cache_param_test.go similarity index 98% rename from planner/core/plan_cache_param_test.go rename to pkg/planner/core/plan_cache_param_test.go index a70f2210823dd..bd38b3fc0bfc0 100644 --- a/planner/core/plan_cache_param_test.go +++ b/pkg/planner/core/plan_cache_param_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cache_test.go b/pkg/planner/core/plan_cache_test.go similarity index 99% rename from planner/core/plan_cache_test.go rename to pkg/planner/core/plan_cache_test.go index 2f9910c5c3c4c..3a70630733020 100644 --- a/planner/core/plan_cache_test.go +++ b/pkg/planner/core/plan_cache_test.go @@ -24,19 +24,19 @@ import ( "testing" "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cache_utils.go b/pkg/planner/core/plan_cache_utils.go similarity index 96% rename from planner/core/plan_cache_utils.go rename to pkg/planner/core/plan_cache_utils.go index c9352e0e5dd50..b3c5bbcded942 100644 --- a/planner/core/plan_cache_utils.go +++ b/pkg/planner/core/plan_cache_utils.go @@ -24,27 +24,27 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/kvcache" - utilpc "github.com/pingcap/tidb/util/plancache" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/kvcache" + utilpc "github.com/pingcap/tidb/pkg/util/plancache" + "github.com/pingcap/tidb/pkg/util/size" atomic2 "go.uber.org/atomic" ) diff --git a/planner/core/plan_cacheable_checker.go b/pkg/planner/core/plan_cacheable_checker.go similarity index 97% rename from planner/core/plan_cacheable_checker.go rename to pkg/planner/core/plan_cacheable_checker.go index 610350c89868a..717f16d97d50e 100644 --- a/planner/core/plan_cacheable_checker.go +++ b/pkg/planner/core/plan_cacheable_checker.go @@ -22,20 +22,20 @@ import ( "strings" "sync" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - core_metrics "github.com/pingcap/tidb/planner/core/metrics" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/filter" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + core_metrics "github.com/pingcap/tidb/pkg/planner/core/metrics" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/filter" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/planner/core/plan_cacheable_checker_test.go b/pkg/planner/core/plan_cacheable_checker_test.go similarity index 97% rename from planner/core/plan_cacheable_checker_test.go rename to pkg/planner/core/plan_cacheable_checker_test.go index 7d7c6f690fd45..533b8d1fc8334 100644 --- a/planner/core/plan_cacheable_checker_test.go +++ b/pkg/planner/core/plan_cacheable_checker_test.go @@ -20,17 +20,17 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cost_detail.go b/pkg/planner/core/plan_cost_detail.go similarity index 99% rename from planner/core/plan_cost_detail.go rename to pkg/planner/core/plan_cost_detail.go index 08c701d7ad3a3..8ba5c98ac0b61 100644 --- a/planner/core/plan_cost_detail.go +++ b/pkg/planner/core/plan_cost_detail.go @@ -17,8 +17,8 @@ package core import ( "fmt" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/tracing" ) const ( diff --git a/planner/core/plan_cost_detail_test.go b/pkg/planner/core/plan_cost_detail_test.go similarity index 92% rename from planner/core/plan_cost_detail_test.go rename to pkg/planner/core/plan_cost_detail_test.go index 62463ae70cfbc..c65e5dc9aa20d 100644 --- a/planner/core/plan_cost_detail_test.go +++ b/pkg/planner/core/plan_cost_detail_test.go @@ -18,14 +18,14 @@ import ( "context" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cost_ver1.go b/pkg/planner/core/plan_cost_ver1.go similarity index 99% rename from planner/core/plan_cost_ver1.go rename to pkg/planner/core/plan_cost_ver1.go index 6fc6e28547f4b..a0c7fdf80da26 100644 --- a/planner/core/plan_cost_ver1.go +++ b/pkg/planner/core/plan_cost_ver1.go @@ -18,13 +18,13 @@ import ( "math" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/util/paging" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/util/paging" ) const ( diff --git a/planner/core/plan_cost_ver1_test.go b/pkg/planner/core/plan_cost_ver1_test.go similarity index 97% rename from planner/core/plan_cost_ver1_test.go rename to pkg/planner/core/plan_cost_ver1_test.go index 9966f742615e1..923e79e7e0687 100644 --- a/planner/core/plan_cost_ver1_test.go +++ b/pkg/planner/core/plan_cost_ver1_test.go @@ -19,9 +19,9 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_cost_ver2.go b/pkg/planner/core/plan_cost_ver2.go similarity index 99% rename from planner/core/plan_cost_ver2.go rename to pkg/planner/core/plan_cost_ver2.go index 1294bb0e8c362..fd44e4aed344d 100644 --- a/planner/core/plan_cost_ver2.go +++ b/pkg/planner/core/plan_cost_ver2.go @@ -19,15 +19,15 @@ import ( "math" "strconv" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/paging" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/paging" "github.com/pingcap/tipb/go-tipb" ) diff --git a/planner/core/plan_cost_ver2_test.go b/pkg/planner/core/plan_cost_ver2_test.go similarity index 96% rename from planner/core/plan_cost_ver2_test.go rename to pkg/planner/core/plan_cost_ver2_test.go index ac8d2cc4c3191..a9e2c798dac7d 100644 --- a/planner/core/plan_cost_ver2_test.go +++ b/pkg/planner/core/plan_cost_ver2_test.go @@ -23,12 +23,12 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_replayer_capture_test.go b/pkg/planner/core/plan_replayer_capture_test.go similarity index 89% rename from planner/core/plan_replayer_capture_test.go rename to pkg/planner/core/plan_replayer_capture_test.go index b89b1e634a677..9d857fd9eb178 100644 --- a/planner/core/plan_replayer_capture_test.go +++ b/pkg/planner/core/plan_replayer_capture_test.go @@ -18,13 +18,13 @@ import ( "context" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) diff --git a/planner/core/plan_stats.go b/pkg/planner/core/plan_stats.go similarity index 96% rename from planner/core/plan_stats.go rename to pkg/planner/core/plan_stats.go index 7b71b67dd2510..bf363d0a6bb97 100644 --- a/planner/core/plan_stats.go +++ b/pkg/planner/core/plan_stats.go @@ -18,16 +18,16 @@ import ( "context" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/pkg/planner/core/plan_test.go b/pkg/planner/core/plan_test.go new file mode 100644 index 0000000000000..9949574eb87db --- /dev/null +++ b/pkg/planner/core/plan_test.go @@ -0,0 +1,763 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "bytes" + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/stretchr/testify/require" +) + +func TestEncodeDecodePlan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1,t2") + tk.MustExec("create table t1 (a int key,b int,c int, index (b));") + tk.MustExec("create table tp (a int ,b int,c int) partition by hash(b) partitions 5;") + tk.MustExec("set tidb_enable_collect_execution_info=1;") + tk.MustExec("set tidb_partition_prune_mode='static';") + + tk.Session().GetSessionVars().PlanID.Store(0) + getPlanTree := func() (str1, str2 string) { + info := tk.Session().ShowProcess() + require.NotNil(t, info) + p, ok := info.Plan.(core.Plan) + require.True(t, ok) + encodeStr := core.EncodePlan(p) + planTree, err := plancodec.DecodePlan(encodeStr) + require.NoError(t, err) + + // test the new encoding method + flat := core.FlattenPhysicalPlan(p, true) + newEncodeStr := core.EncodeFlatPlan(flat) + newPlanTree, err := plancodec.DecodePlan(newEncodeStr) + require.NoError(t, err) + + return planTree, newPlanTree + } + tk.MustExec("select max(a) from t1 where a>0;") + planTree, newplanTree := getPlanTree() + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("prepare stmt from \"select max(a) from t1 where a > ?\";") + tk.MustExec("set @a = 1;") + tk.MustExec("execute stmt using @a;") + planTree, newplanTree = getPlanTree() + require.Empty(t, planTree) + require.Empty(t, newplanTree) + + tk.MustExec("insert into t1 values (1,1,1), (2,2,2);") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Insert") + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "Insert") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("update t1 set b = 3 where c = 1;") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Update") + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "Update") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("delete from t1 where b = 3;") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Delete") + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "Delete") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("with cte(a) as (select 1) select * from cte") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Projection_7") + require.Contains(t, planTree, "1->Column#3") + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "Projection_7") + require.Contains(t, newplanTree, "1->Column#3") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("with cte(a) as (select 2) select * from cte") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Projection_7") + require.Contains(t, planTree, "2->Column#3") + require.Contains(t, planTree, "time") + require.Contains(t, planTree, "loops") + require.Contains(t, newplanTree, "Projection_7") + require.Contains(t, newplanTree, "2->Column#3") + require.Contains(t, newplanTree, "time") + require.Contains(t, newplanTree, "loops") + + tk.MustExec("select * from tp") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "PartitionUnion") + require.Contains(t, newplanTree, "PartitionUnion") + + tk.MustExec("select row_number() over (partition by c) from t1;") + planTree, newplanTree = getPlanTree() + require.Contains(t, planTree, "Shuffle") + require.Contains(t, planTree, "ShuffleReceiver") + require.Contains(t, newplanTree, "Shuffle") + require.Contains(t, newplanTree, "ShuffleReceiver") +} + +func TestNormalizedDigest(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1,t2,t3,t4, bmsql_order_line, bmsql_district,bmsql_stock") + tk.MustExec("create table t1 (a int key,b int,c int, index (b));") + tk.MustExec("create table t2 (a int key,b int,c int, index (b));") + tk.MustExec("create table t3 (a int, b int, index(a)) partition by range(a) (partition p0 values less than (10),partition p1 values less than MAXVALUE);") + tk.MustExec("create table t4 (a int key,b int) partition by hash(a) partitions 2;") + tk.MustExec(`CREATE TABLE bmsql_order_line ( + ol_w_id int(11) NOT NULL, + ol_d_id int(11) NOT NULL, + ol_o_id int(11) NOT NULL, + ol_number int(11) NOT NULL, + ol_i_id int(11) NOT NULL, + ol_delivery_d timestamp NULL DEFAULT NULL, + ol_amount decimal(6,2) DEFAULT NULL, + ol_supply_w_id int(11) DEFAULT NULL, + ol_quantity int(11) DEFAULT NULL, + ol_dist_info char(24) DEFAULT NULL, + PRIMARY KEY ( ol_w_id , ol_d_id , ol_o_id , ol_number ) NONCLUSTERED + );`) + tk.MustExec(`CREATE TABLE bmsql_district ( + d_w_id int(11) NOT NULL, + d_id int(11) NOT NULL, + d_ytd decimal(12,2) DEFAULT NULL, + d_tax decimal(4,4) DEFAULT NULL, + d_next_o_id int(11) DEFAULT NULL, + d_name varchar(10) DEFAULT NULL, + d_street_1 varchar(20) DEFAULT NULL, + d_street_2 varchar(20) DEFAULT NULL, + d_city varchar(20) DEFAULT NULL, + d_state char(2) DEFAULT NULL, + d_zip char(9) DEFAULT NULL, + PRIMARY KEY ( d_w_id , d_id ) NONCLUSTERED + );`) + tk.MustExec(`CREATE TABLE bmsql_stock ( + s_w_id int(11) NOT NULL, + s_i_id int(11) NOT NULL, + s_quantity int(11) DEFAULT NULL, + s_ytd int(11) DEFAULT NULL, + s_order_cnt int(11) DEFAULT NULL, + s_remote_cnt int(11) DEFAULT NULL, + s_data varchar(50) DEFAULT NULL, + s_dist_01 char(24) DEFAULT NULL, + s_dist_02 char(24) DEFAULT NULL, + s_dist_03 char(24) DEFAULT NULL, + s_dist_04 char(24) DEFAULT NULL, + s_dist_05 char(24) DEFAULT NULL, + s_dist_06 char(24) DEFAULT NULL, + s_dist_07 char(24) DEFAULT NULL, + s_dist_08 char(24) DEFAULT NULL, + s_dist_09 char(24) DEFAULT NULL, + s_dist_10 char(24) DEFAULT NULL, + PRIMARY KEY ( s_w_id , s_i_id ) NONCLUSTERED + );`) + + err := failpoint.Enable("github.com/pingcap/tidb/pkg/planner/mockRandomPlanID", "return(true)") + require.NoError(t, err) + defer func() { + err = failpoint.Disable("github.com/pingcap/tidb/pkg/planner/mockRandomPlanID") + require.NoError(t, err) + }() + + normalizedDigestCases := []struct { + sql1 string + sql2 string + isSame bool + }{ + { + sql1: "select * from t1;", + sql2: "select * from t2;", + isSame: false, + }, + { // test for tableReader and tableScan. + sql1: "select * from t1 where a<1", + sql2: "select * from t1 where a<2", + isSame: true, + }, + { + sql1: "select * from t1 where a<1", + sql2: "select * from t1 where a=2", + isSame: false, + }, + { // test for point get. + sql1: "select * from t1 where a=3", + sql2: "select * from t1 where a=2", + isSame: true, + }, + { // test for indexLookUp. + sql1: "select * from t1 use index(b) where b=3", + sql2: "select * from t1 use index(b) where b=1", + isSame: true, + }, + { // test for indexReader. + sql1: "select a+1,b+2 from t1 use index(b) where b=3", + sql2: "select a+2,b+3 from t1 use index(b) where b=2", + isSame: true, + }, + { // test for merge join. + sql1: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", + sql2: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>2;", + isSame: true, + }, + { // test for indexLookUpJoin. + sql1: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", + sql2: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", + isSame: true, + }, + { // test for hashJoin. + sql1: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", + sql2: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", + isSame: true, + }, + { // test for diff join. + sql1: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", + sql2: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", + isSame: false, + }, + { // test for diff join. + sql1: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", + sql2: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", + isSame: false, + }, + { // test for apply. + sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", + sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >0)", + isSame: true, + }, + { // test for apply. + sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", + sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null)", + isSame: false, + }, + { // test for topN. + sql1: "SELECT * from t1 where a!=1 order by c limit 1", + sql2: "SELECT * from t1 where a!=2 order by c limit 2", + isSame: true, + }, + { // test for union + sql1: "select count(1) as num,a from t1 where a=1 group by a union select count(1) as num,a from t1 where a=3 group by a;", + sql2: "select count(1) as num,a from t1 where a=2 group by a union select count(1) as num,a from t1 where a=4 group by a;", + isSame: true, + }, + { // test for tablescan partition + sql1: "select * from t3 where a=5", + sql2: "select * from t3 where a=15", + isSame: true, + }, + { // test for point get partition + sql1: "select * from t4 where a=4", + sql2: "select * from t4 where a=30", + isSame: true, + }, + { + sql1: `SELECT COUNT(*) AS low_stock + FROM + ( + SELECT * + FROM bmsql_stock + WHERE s_w_id = 1 + AND s_quantity < 2 + AND s_i_id IN ( SELECT /*+ TIDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 20 AND ol_o_id < d_next_o_id WHERE d_w_id = 1 AND d_id = 2 ) + ) AS L;`, + sql2: `SELECT COUNT(*) AS low_stock + FROM + ( + SELECT * + FROM bmsql_stock + WHERE s_w_id = 5 + AND s_quantity < 6 + AND s_i_id IN ( SELECT /*+ TIDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 70 AND ol_o_id < d_next_o_id WHERE d_w_id = 5 AND d_id = 6 ) + ) AS L;`, + isSame: true, + }, + } + for _, testCase := range normalizedDigestCases { + testNormalizeDigest(tk, t, testCase.sql1, testCase.sql2, testCase.isSame) + } +} + +func testNormalizeDigest(tk *testkit.TestKit, t *testing.T, sql1, sql2 string, isSame bool) { + tk.MustQuery(sql1) + info := tk.Session().ShowProcess() + require.NotNil(t, info) + physicalPlan, ok := info.Plan.(core.PhysicalPlan) + require.True(t, ok) + normalized1, digest1 := core.NormalizePlan(physicalPlan) + + // test the new normalization code + flat := core.FlattenPhysicalPlan(physicalPlan, false) + newNormalized, newPlanDigest := core.NormalizeFlatPlan(flat) + require.Equal(t, digest1, newPlanDigest) + require.Equal(t, normalized1, newNormalized) + + tk.MustQuery(sql2) + info = tk.Session().ShowProcess() + require.NotNil(t, info) + physicalPlan, ok = info.Plan.(core.PhysicalPlan) + require.True(t, ok) + normalized2, digest2 := core.NormalizePlan(physicalPlan) + + // test the new normalization code + flat = core.FlattenPhysicalPlan(physicalPlan, false) + newNormalized, newPlanDigest = core.NormalizeFlatPlan(flat) + require.Equal(t, digest2, newPlanDigest) + require.Equal(t, normalized2, newNormalized) + + comment := fmt.Sprintf("sql1: %v, sql2: %v\n%v !=\n%v\n", sql1, sql2, normalized1, normalized2) + if isSame { + require.Equal(t, normalized1, normalized2, comment) + require.Equal(t, digest1.String(), digest2.String(), comment) + } else { + require.NotEqual(t, normalized1, normalized2, comment) + require.NotEqual(t, digest1.String(), digest2.String(), comment) + } +} + +func TestExplainFormatHintRecoverableForTiFlashReplica(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + // Create virtual `tiflash` replica info. + is := dom.InfoSchema() + db, exists := is.SchemaByName(model.NewCIStr("test")) + require.True(t, exists) + for _, tblInfo := range db.Tables { + if tblInfo.Name.L == "t" { + tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ + Count: 1, + Available: true, + } + } + } + + rows := tk.MustQuery("explain select * from t").Rows() + require.Equal(t, rows[len(rows)-1][2], "mpp[tiflash]") + + rows = tk.MustQuery("explain format='hint' select * from t").Rows() + require.Equal(t, rows[0][0], "read_from_storage(@`sel_1` tiflash[`test`.`t`])") + + hints := tk.MustQuery("explain format='hint' select * from t;").Rows()[0][0] + rows = tk.MustQuery(fmt.Sprintf("explain select /*+ %s */ * from t", hints)).Rows() + require.Equal(t, rows[len(rows)-1][2], "mpp[tiflash]") +} + +func BenchmarkDecodePlan(b *testing.B) { + store := testkit.CreateMockStore(b) + tk := testkit.NewTestKit(b, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a varchar(10) key,b int);") + tk.MustExec("set @@tidb_slow_log_threshold=200000") + + // generate SQL + buf := bytes.NewBuffer(make([]byte, 0, 1024*1024*4)) + for i := 0; i < 50000; i++ { + if i > 0 { + buf.WriteString(" union ") + } + buf.WriteString(fmt.Sprintf("select count(1) as num,a from t where a='%v' group by a", i)) + } + query := buf.String() + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(query) + info := tk.Session().ShowProcess() + require.NotNil(b, info) + p, ok := info.Plan.(core.PhysicalPlan) + require.True(b, ok) + // TODO: optimize the encode plan performance when encode plan with runtimeStats + tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil + encodedPlanStr := core.EncodePlan(p) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := plancodec.DecodePlan(encodedPlanStr) + require.NoError(b, err) + } +} + +func BenchmarkEncodePlan(b *testing.B) { + store := testkit.CreateMockStore(b) + tk := testkit.NewTestKit(b, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists th") + tk.MustExec("set @@session.tidb_enable_table_partition = 1") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") + tk.MustExec("set @@tidb_slow_log_threshold=200000") + + query := "select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i" + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(query) + info := tk.Session().ShowProcess() + require.NotNil(b, info) + p, ok := info.Plan.(core.PhysicalPlan) + require.True(b, ok) + tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil + b.ResetTimer() + for i := 0; i < b.N; i++ { + core.EncodePlan(p) + } +} + +func BenchmarkEncodeFlatPlan(b *testing.B) { + store := testkit.CreateMockStore(b) + tk := testkit.NewTestKit(b, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists th") + tk.MustExec("set @@session.tidb_enable_table_partition = 1") + tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) + tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") + tk.MustExec("set @@tidb_slow_log_threshold=200000") + + query := "select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i" + tk.Session().GetSessionVars().PlanID.Store(0) + tk.MustExec(query) + info := tk.Session().ShowProcess() + require.NotNil(b, info) + p, ok := info.Plan.(core.PhysicalPlan) + require.True(b, ok) + tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil + b.ResetTimer() + for i := 0; i < b.N; i++ { + flat := core.FlattenPhysicalPlan(p, false) + core.EncodeFlatPlan(flat) + } +} + +func TestIssue35090(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists p, t;") + tk.MustExec("create table p (id int, c int, key i_id(id), key i_c(c));") + tk.MustExec("create table t (id int);") + tk.MustExec("insert into p values (3,3), (4,4), (6,6), (9,9);") + tk.MustExec("insert into t values (4), (9);") + tk.MustExec("select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id;") + rows := [][]interface{}{ + {"IndexJoin"}, + {"├─TableReader(Build)"}, + {"│ └─Selection"}, + {"│ └─TableFullScan"}, + {"└─IndexLookUp(Probe)"}, + {" ├─Selection(Build)"}, + {" │ └─IndexRangeScan"}, + {" └─TableRowIDScan(Probe)"}, + } + tk.MustQuery("explain analyze format='brief' select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id;").CheckAt([]int{0}, rows) +} + +func TestCopPaging(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("drop table if exists t") + tk.MustExec("set session tidb_enable_paging = 1") + tk.MustExec("create table t(id int, c1 int, c2 int, primary key (id), key i(c1))") + defer tk.MustExec("drop table t") + for i := 0; i < 1024; i++ { + tk.MustExec("insert into t values(?, ?, ?)", i, i, i) + } + tk.MustExec("analyze table t") + + // limit 960 should go paging + for i := 0; i < 10; i++ { + tk.MustQuery("explain format='brief' select * from t force index(i) where id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 960").Check(testkit.Rows( + "Limit 4.00 root offset:0, count:960", + "└─IndexLookUp 4.00 root ", + " ├─Selection(Build) 1024.00 cop[tikv] le(test.t.id, 1024)", + " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", + " └─Selection(Probe) 4.00 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", + " └─TableRowIDScan 1024.00 cop[tikv] table:t keep order:false")) + } + + // selection between limit and indexlookup, limit 960 should also go paging + for i := 0; i < 10; i++ { + tk.MustQuery("explain format='brief' select * from t force index(i) where mod(id, 2) > 0 and id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 960").Check(testkit.Rows( + "Limit 3.20 root offset:0, count:960", + "└─IndexLookUp 3.20 root ", + " ├─Selection(Build) 819.20 cop[tikv] gt(mod(test.t.id, 2), 0), le(test.t.id, 1024)", + " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", + " └─Selection(Probe) 3.20 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", + " └─TableRowIDScan 819.20 cop[tikv] table:t keep order:false")) + } + + // limit 961 exceeds the threshold, it should not go paging + for i := 0; i < 10; i++ { + tk.MustQuery("explain format='brief' select * from t force index(i) where id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 961").Check(testkit.Rows( + "Limit 4.00 root offset:0, count:961", + "└─IndexLookUp 4.00 root ", + " ├─Selection(Build) 1024.00 cop[tikv] le(test.t.id, 1024)", + " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", + " └─Selection(Probe) 4.00 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", + " └─TableRowIDScan 1024.00 cop[tikv] table:t keep order:false")) + } + + // selection between limit and indexlookup, limit 961 should not go paging too + for i := 0; i < 10; i++ { + tk.MustQuery("explain format='brief' select * from t force index(i) where mod(id, 2) > 0 and id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 961").Check(testkit.Rows( + "Limit 3.20 root offset:0, count:961", + "└─IndexLookUp 3.20 root ", + " ├─Selection(Build) 819.20 cop[tikv] gt(mod(test.t.id, 2), 0), le(test.t.id, 1024)", + " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", + " └─Selection(Probe) 3.20 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", + " └─TableRowIDScan 819.20 cop[tikv] table:t keep order:false")) + } +} + +func TestBuildFinalModeAggregation(t *testing.T) { + aggSchemaBuilder := func(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc) *expression.Schema { + schema := expression.NewSchema(make([]*expression.Column, 0, len(aggFuncs))...) + for _, agg := range aggFuncs { + newCol := &expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: agg.RetTp, + } + schema.Append(newCol) + } + return schema + } + isFinalAggMode := func(mode aggregation.AggFunctionMode) bool { + return mode == aggregation.FinalMode || mode == aggregation.CompleteMode + } + checkResult := func(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc, groubyItems []expression.Expression) { + for partialIsCop := 0; partialIsCop < 2; partialIsCop++ { + for isMPPTask := 0; isMPPTask < 2; isMPPTask++ { + partial, final, _ := core.BuildFinalModeAggregation(sctx, &core.AggInfo{ + AggFuncs: aggFuncs, + GroupByItems: groubyItems, + Schema: aggSchemaBuilder(sctx, aggFuncs), + }, partialIsCop == 0, isMPPTask == 0) + if partial != nil { + for _, aggFunc := range partial.AggFuncs { + if partialIsCop == 0 { + require.True(t, !isFinalAggMode(aggFunc.Mode)) + } else { + require.True(t, isFinalAggMode(aggFunc.Mode)) + } + } + } + if final != nil { + for _, aggFunc := range final.AggFuncs { + require.True(t, isFinalAggMode(aggFunc.Mode)) + } + } + } + } + } + + ctx := core.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + aggCol := &expression.Column{ + Index: 0, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + gbyCol := &expression.Column{ + Index: 1, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + orderCol := &expression.Column{ + Index: 2, + RetType: types.NewFieldType(mysql.TypeLonglong), + } + + emptyGroupByItems := make([]expression.Expression, 0, 1) + groupByItems := make([]expression.Expression, 0, 1) + groupByItems = append(groupByItems, gbyCol) + + orderByItems := make([]*util.ByItems, 0, 1) + orderByItems = append(orderByItems, &util.ByItems{ + Expr: orderCol, + Desc: true, + }) + + aggFuncs := make([]*aggregation.AggFuncDesc, 0, 5) + desc, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncMax, []expression.Expression{aggCol}, false) + require.NoError(t, err) + aggFuncs = append(aggFuncs, desc) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncFirstRow, []expression.Expression{aggCol}, false) + require.NoError(t, err) + aggFuncs = append(aggFuncs, desc) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncCount, []expression.Expression{aggCol}, false) + require.NoError(t, err) + aggFuncs = append(aggFuncs, desc) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncSum, []expression.Expression{aggCol}, false) + require.NoError(t, err) + aggFuncs = append(aggFuncs, desc) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{aggCol}, false) + require.NoError(t, err) + aggFuncs = append(aggFuncs, desc) + + aggFuncsWithDistinct := make([]*aggregation.AggFuncDesc, 0, 2) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{aggCol}, true) + require.NoError(t, err) + aggFuncsWithDistinct = append(aggFuncsWithDistinct, desc) + desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncCount, []expression.Expression{aggCol}, true) + require.NoError(t, err) + aggFuncsWithDistinct = append(aggFuncsWithDistinct, desc) + + groupConcatAggFuncs := make([]*aggregation.AggFuncDesc, 0, 4) + groupConcatWithoutDistinctWithoutOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, false) + require.NoError(t, err) + groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithoutDistinctWithoutOrderBy) + groupConcatWithoutDistinctWithOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, false) + require.NoError(t, err) + groupConcatWithoutDistinctWithOrderBy.OrderByItems = orderByItems + groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithoutDistinctWithOrderBy) + groupConcatWithDistinctWithoutOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, true) + require.NoError(t, err) + groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithDistinctWithoutOrderBy) + groupConcatWithDistinctWithOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, true) + require.NoError(t, err) + groupConcatWithDistinctWithOrderBy.OrderByItems = orderByItems + groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithDistinctWithOrderBy) + + // case 1 agg without distinct + checkResult(ctx, aggFuncs, emptyGroupByItems) + checkResult(ctx, aggFuncs, groupByItems) + + // case 2 agg with distinct + checkResult(ctx, aggFuncsWithDistinct, emptyGroupByItems) + checkResult(ctx, aggFuncsWithDistinct, groupByItems) + + // case 3 mixed with distinct and without distinct + mixedAggFuncs := make([]*aggregation.AggFuncDesc, 0, 10) + mixedAggFuncs = append(mixedAggFuncs, aggFuncs...) + mixedAggFuncs = append(mixedAggFuncs, aggFuncsWithDistinct...) + checkResult(ctx, mixedAggFuncs, emptyGroupByItems) + checkResult(ctx, mixedAggFuncs, groupByItems) + + // case 4 group concat + for _, groupConcatAggFunc := range groupConcatAggFuncs { + checkResult(ctx, []*aggregation.AggFuncDesc{groupConcatAggFunc}, emptyGroupByItems) + checkResult(ctx, []*aggregation.AggFuncDesc{groupConcatAggFunc}, groupByItems) + } + checkResult(ctx, groupConcatAggFuncs, emptyGroupByItems) + checkResult(ctx, groupConcatAggFuncs, groupByItems) + + // case 5 mixed group concat and other agg funcs + for _, groupConcatAggFunc := range groupConcatAggFuncs { + funcs := make([]*aggregation.AggFuncDesc, 0, 10) + funcs = append(funcs, groupConcatAggFunc) + funcs = append(funcs, aggFuncs...) + checkResult(ctx, funcs, emptyGroupByItems) + checkResult(ctx, funcs, groupByItems) + funcs = append(funcs, aggFuncsWithDistinct...) + checkResult(ctx, funcs, emptyGroupByItems) + checkResult(ctx, funcs, groupByItems) + } + mixedAggFuncs = append(mixedAggFuncs, groupConcatAggFuncs...) + checkResult(ctx, mixedAggFuncs, emptyGroupByItems) + checkResult(ctx, mixedAggFuncs, groupByItems) +} + +func TestIssue40857(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("CREATE TABLE t (c1 mediumint(9) DEFAULT '-4747160',c2 year(4) NOT NULL DEFAULT '2075',c3 double DEFAULT '1.1559030660251948',c4 enum('wbv4','eli','d8ym','m3gsx','lz7td','o','d1k7l','y1x','xcxq','bj','n7') DEFAULT 'xcxq',c5 int(11) DEFAULT '255080866',c6 tinyint(1) DEFAULT '1',PRIMARY KEY (c2),KEY `c4d86d54-091c-4307-957b-b164c9652b7f` (c6,c4) );") + tk.MustExec("insert into t values (-4747160, 2075, 722.5719203870632, 'xcxq', 1576824797, 1);") + tk.MustExec("select /*+ stream_agg() */ bit_or(t.c5) as r0 from t where t.c3 in (select c6 from t where not(t.c6 <> 1) and not(t.c3 in(9263.749352636818))) group by t.c1;") + require.Empty(t, tk.Session().LastMessage()) +} + +func TestCloneFineGrainedShuffleStreamCount(t *testing.T) { + window := &core.PhysicalWindow{} + newPlan, err := window.Clone() + require.NoError(t, err) + newWindow, ok := newPlan.(*core.PhysicalWindow) + require.Equal(t, ok, true) + require.Equal(t, window.TiFlashFineGrainedShuffleStreamCount, newWindow.TiFlashFineGrainedShuffleStreamCount) + + window.TiFlashFineGrainedShuffleStreamCount = 8 + newPlan, err = window.Clone() + require.NoError(t, err) + newWindow, ok = newPlan.(*core.PhysicalWindow) + require.Equal(t, ok, true) + require.Equal(t, window.TiFlashFineGrainedShuffleStreamCount, newWindow.TiFlashFineGrainedShuffleStreamCount) + + sort := &core.PhysicalSort{} + newPlan, err = sort.Clone() + require.NoError(t, err) + newSort, ok := newPlan.(*core.PhysicalSort) + require.Equal(t, ok, true) + require.Equal(t, sort.TiFlashFineGrainedShuffleStreamCount, newSort.TiFlashFineGrainedShuffleStreamCount) + + sort.TiFlashFineGrainedShuffleStreamCount = 8 + newPlan, err = sort.Clone() + require.NoError(t, err) + newSort, ok = newPlan.(*core.PhysicalSort) + require.Equal(t, ok, true) + require.Equal(t, sort.TiFlashFineGrainedShuffleStreamCount, newSort.TiFlashFineGrainedShuffleStreamCount) +} + +func TestIssue40535(t *testing.T) { + store := testkit.CreateMockStore(t) + var cfg kv.InjectionConfig + tk := testkit.NewTestKit(t, kv.NewInjectedStore(store, &cfg)) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t1; drop table if exists t2;") + tk.MustExec("CREATE TABLE `t1`(`c1` bigint(20) NOT NULL DEFAULT '-2312745469307452950', `c2` datetime DEFAULT '5316-02-03 06:54:49', `c3` tinyblob DEFAULT NULL, PRIMARY KEY (`c1`) /*T![clustered_index] CLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;") + tk.MustExec("CREATE TABLE `t2`(`c1` set('kn8pu','7et','vekx6','v3','liwrh','q14','1met','nnd5i','5o0','8cz','l') DEFAULT '7et,vekx6,liwrh,q14,1met', `c2` float DEFAULT '1.683167', KEY `k1` (`c2`,`c1`), KEY `k2` (`c2`)) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_chinese_ci;") + tk.MustExec("(select /*+ agg_to_cop()*/ locate(t1.c3, t1.c3) as r0, t1.c3 as r1 from t1 where not( IsNull(t1.c1)) order by r0,r1) union all (select concat_ws(',', t2.c2, t2.c1) as r0, t2.c1 as r1 from t2 order by r0, r1) order by 1 limit 273;") + require.Empty(t, tk.Session().LastMessage()) +} + +func TestExplainValuesStatement(t *testing.T) { + store, _ := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + tk.MustMatchErrMsg("EXPLAIN FORMAT = TRADITIONAL ((VALUES ROW ()) ORDER BY 1)", ".*Unknown table ''.*") +} diff --git a/planner/core/plan_to_pb.go b/pkg/planner/core/plan_to_pb.go similarity index 98% rename from planner/core/plan_to_pb.go rename to pkg/planner/core/plan_to_pb.go index 9b4b2f1e8a9da..d6a425f390791 100644 --- a/planner/core/plan_to_pb.go +++ b/pkg/planner/core/plan_to_pb.go @@ -16,15 +16,15 @@ package core import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" ) diff --git a/planner/core/plan_to_pb_test.go b/pkg/planner/core/plan_to_pb_test.go similarity index 93% rename from planner/core/plan_to_pb_test.go rename to pkg/planner/core/plan_to_pb_test.go index 62b13e4fece59..c6548d6c8049e 100644 --- a/planner/core/plan_to_pb_test.go +++ b/pkg/planner/core/plan_to_pb_test.go @@ -17,11 +17,11 @@ package core import ( "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/planner/core/planbuilder.go b/pkg/planner/core/planbuilder.go similarity index 99% rename from planner/core/planbuilder.go rename to pkg/planner/core/planbuilder.go index 0b6518047e965..d4ec806e2a6a2 100644 --- a/planner/core/planbuilder.go +++ b/pkg/planner/core/planbuilder.go @@ -26,48 +26,48 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - util2 "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + util2 "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stmtsummary" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" ) diff --git a/planner/core/planbuilder_test.go b/pkg/planner/core/planbuilder_test.go similarity index 97% rename from planner/core/planbuilder_test.go rename to pkg/planner/core/planbuilder_test.go index cf1a50da18ea1..81ee1e7bacbe9 100644 --- a/planner/core/planbuilder_test.go +++ b/pkg/planner/core/planbuilder_test.go @@ -23,20 +23,20 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hint" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/planner/core/point_get_plan.go b/pkg/planner/core/point_get_plan.go similarity index 97% rename from planner/core/point_get_plan.go rename to pkg/planner/core/point_get_plan.go index 5a013ae6e7edc..29bc6464c2a9a 100644 --- a/planner/core/point_get_plan.go +++ b/pkg/planner/core/point_get_plan.go @@ -22,37 +22,37 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - ptypes "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/planner/core/internal/base" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + ptypes "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/pingcap/tipb/go-tipb" tikvstore "github.com/tikv/client-go/v2/kv" "go.uber.org/zap" diff --git a/planner/core/point_get_plan_test.go b/pkg/planner/core/point_get_plan_test.go similarity index 97% rename from planner/core/point_get_plan_test.go rename to pkg/planner/core/point_get_plan_test.go index 051c3510bcad8..2d4a5222e5d80 100644 --- a/planner/core/point_get_plan_test.go +++ b/pkg/planner/core/point_get_plan_test.go @@ -21,13 +21,13 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" ) diff --git a/planner/core/preprocess.go b/pkg/planner/core/preprocess.go similarity index 98% rename from planner/core/preprocess.go rename to pkg/planner/core/preprocess.go index ff26794fa6059..0ba0cd8f0be50 100644 --- a/planner/core/preprocess.go +++ b/pkg/planner/core/preprocess.go @@ -21,33 +21,33 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/domainutil" - "github.com/pingcap/tidb/util/logutil" - utilparser "github.com/pingcap/tidb/util/parser" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/domainutil" + "github.com/pingcap/tidb/pkg/util/logutil" + utilparser "github.com/pingcap/tidb/pkg/util/parser" "go.uber.org/zap" ) diff --git a/planner/core/preprocess_test.go b/pkg/planner/core/preprocess_test.go similarity index 98% rename from planner/core/preprocess_test.go rename to pkg/planner/core/preprocess_test.go index f1ad88e9e7850..8944d627cdd60 100644 --- a/planner/core/preprocess_test.go +++ b/pkg/planner/core/preprocess_test.go @@ -20,19 +20,19 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" "github.com/stretchr/testify/require" ) diff --git a/planner/core/property_cols_prune.go b/pkg/planner/core/property_cols_prune.go similarity index 98% rename from planner/core/property_cols_prune.go rename to pkg/planner/core/property_cols_prune.go index 975496105c9db..efd66970e8432 100644 --- a/planner/core/property_cols_prune.go +++ b/pkg/planner/core/property_cols_prune.go @@ -15,8 +15,8 @@ package core import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/util" ) // preparePossibleProperties traverses the plan tree by a post-order method, diff --git a/planner/core/resolve_indices.go b/pkg/planner/core/resolve_indices.go similarity index 99% rename from planner/core/resolve_indices.go rename to pkg/planner/core/resolve_indices.go index 82888c3b376a9..e43405fb1f6db 100644 --- a/planner/core/resolve_indices.go +++ b/pkg/planner/core/resolve_indices.go @@ -16,9 +16,9 @@ package core import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/util/disjointset" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/util/disjointset" ) // ResolveIndicesItself resolve indices for PhysicalPlan itself diff --git a/planner/core/rule_aggregation_elimination.go b/pkg/planner/core/rule_aggregation_elimination.go similarity index 97% rename from planner/core/rule_aggregation_elimination.go rename to pkg/planner/core/rule_aggregation_elimination.go index 62b9b4ceeb59d..50f3d3a01621c 100644 --- a/planner/core/rule_aggregation_elimination.go +++ b/pkg/planner/core/rule_aggregation_elimination.go @@ -19,12 +19,12 @@ import ( "fmt" "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" ) type aggregationEliminator struct { diff --git a/planner/core/rule_aggregation_push_down.go b/pkg/planner/core/rule_aggregation_push_down.go similarity index 98% rename from planner/core/rule_aggregation_push_down.go rename to pkg/planner/core/rule_aggregation_push_down.go index beaf3377cba12..a1296d41f1dfd 100644 --- a/planner/core/rule_aggregation_push_down.go +++ b/pkg/planner/core/rule_aggregation_push_down.go @@ -19,13 +19,13 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" ) type aggregationPushDownSolver struct { diff --git a/planner/core/rule_aggregation_skew_rewrite.go b/pkg/planner/core/rule_aggregation_skew_rewrite.go similarity index 98% rename from planner/core/rule_aggregation_skew_rewrite.go rename to pkg/planner/core/rule_aggregation_skew_rewrite.go index 8652d37c43da2..d82e0d0dc4d47 100644 --- a/planner/core/rule_aggregation_skew_rewrite.go +++ b/pkg/planner/core/rule_aggregation_skew_rewrite.go @@ -18,10 +18,10 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - fd "github.com/pingcap/tidb/planner/funcdep" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + fd "github.com/pingcap/tidb/pkg/planner/funcdep" ) type skewDistinctAggRewriter struct { diff --git a/planner/core/rule_build_key_info.go b/pkg/planner/core/rule_build_key_info.go similarity index 98% rename from planner/core/rule_build_key_info.go rename to pkg/planner/core/rule_build_key_info.go index 43eac5bd59a59..819e9101aa4dc 100644 --- a/planner/core/rule_build_key_info.go +++ b/pkg/planner/core/rule_build_key_info.go @@ -17,10 +17,10 @@ package core import ( "context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" ) type buildKeySolver struct{} diff --git a/planner/core/rule_column_pruning.go b/pkg/planner/core/rule_column_pruning.go similarity index 98% rename from planner/core/rule_column_pruning.go rename to pkg/planner/core/rule_column_pruning.go index 8c51cd677eeac..ae7be6306f01c 100644 --- a/planner/core/rule_column_pruning.go +++ b/pkg/planner/core/rule_column_pruning.go @@ -19,13 +19,13 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" ) type columnPruner struct { diff --git a/planner/core/rule_constant_propagation.go b/pkg/planner/core/rule_constant_propagation.go similarity index 99% rename from planner/core/rule_constant_propagation.go rename to pkg/planner/core/rule_constant_propagation.go index 25bb3b14ec637..0e2d26e5d7379 100644 --- a/planner/core/rule_constant_propagation.go +++ b/pkg/planner/core/rule_constant_propagation.go @@ -17,8 +17,8 @@ package core import ( "context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" ) // constantPropagationSolver can support constant propagated cross-query block. diff --git a/planner/core/rule_decorrelate.go b/pkg/planner/core/rule_decorrelate.go similarity index 98% rename from planner/core/rule_decorrelate.go rename to pkg/planner/core/rule_decorrelate.go index 824cbaa759528..56bb103894776 100644 --- a/planner/core/rule_decorrelate.go +++ b/pkg/planner/core/rule_decorrelate.go @@ -20,12 +20,12 @@ import ( "fmt" "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/plancodec" ) // canPullUpAgg checks if an apply can pull an aggregation up. diff --git a/planner/core/rule_derive_topn_from_window.go b/pkg/planner/core/rule_derive_topn_from_window.go similarity index 96% rename from planner/core/rule_derive_topn_from_window.go rename to pkg/planner/core/rule_derive_topn_from_window.go index 5b6b8ee179ee4..2063cd7a0c7b4 100644 --- a/planner/core/rule_derive_topn_from_window.go +++ b/pkg/planner/core/rule_derive_topn_from_window.go @@ -18,10 +18,10 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/util" ) // deriveTopNFromWindow pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase. diff --git a/planner/core/rule_eliminate_projection.go b/pkg/planner/core/rule_eliminate_projection.go similarity index 99% rename from planner/core/rule_eliminate_projection.go rename to pkg/planner/core/rule_eliminate_projection.go index 27a23d576a675..65405cdb87360 100644 --- a/planner/core/rule_eliminate_projection.go +++ b/pkg/planner/core/rule_eliminate_projection.go @@ -20,9 +20,9 @@ import ( "fmt" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" ) // canProjectionBeEliminatedLoose checks whether a projection can be eliminated, diff --git a/planner/core/rule_generate_column_substitute.go b/pkg/planner/core/rule_generate_column_substitute.go similarity index 98% rename from planner/core/rule_generate_column_substitute.go rename to pkg/planner/core/rule_generate_column_substitute.go index 476a81e871104..117551f5c5db3 100644 --- a/planner/core/rule_generate_column_substitute.go +++ b/pkg/planner/core/rule_generate_column_substitute.go @@ -18,9 +18,9 @@ import ( "bytes" "context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" ) type gcSubstituter struct { diff --git a/planner/core/rule_generate_column_substitute_test.go b/pkg/planner/core/rule_generate_column_substitute_test.go similarity index 96% rename from planner/core/rule_generate_column_substitute_test.go rename to pkg/planner/core/rule_generate_column_substitute_test.go index ece8c6915f9a2..385c8fb463f8f 100644 --- a/planner/core/rule_generate_column_substitute_test.go +++ b/pkg/planner/core/rule_generate_column_substitute_test.go @@ -19,10 +19,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -34,7 +34,7 @@ import ( // Entering interactive mode (type "help" for commands, "o" for options) // (pprof) list BenchmarkSubstituteExpression // Total: 1.40GB -// ROUTINE ======================== github.com/pingcap/tidb/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/planner/core/rule_generate_column_substitute_test.go +// ROUTINE ======================== github.com/pingcap/tidb/pkg/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/pkg/planner/core/rule_generate_column_substitute_test.go // // 0 173.44MB (flat, cum) 12.12% of Total // . . 29:func BenchmarkSubstituteExpression(b *testing.B) { @@ -124,7 +124,7 @@ import ( // Entering interactive mode (type "help" for commands, "o" for options) // (pprof) list BenchmarkSubstituteExpression // Total: 1.41GB -// ROUTINE ======================== github.com/pingcap/tidb/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/planner/core/rule_generate_column_substitute_test.go +// ROUTINE ======================== github.com/pingcap/tidb/pkg/planner/core_test.BenchmarkSubstituteExpression in /home/arenatlx/go/src/github.com/pingcap/tidb/pkg/planner/core/rule_generate_column_substitute_test.go // // 0 172.22MB (flat, cum) 11.90% of Total // . . 29:func BenchmarkSubstituteExpression(b *testing.B) { diff --git a/planner/core/rule_inject_extra_projection.go b/pkg/planner/core/rule_inject_extra_projection.go similarity index 97% rename from planner/core/rule_inject_extra_projection.go rename to pkg/planner/core/rule_inject_extra_projection.go index 3cc26a271e492..5e93699b0e6e7 100644 --- a/planner/core/rule_inject_extra_projection.go +++ b/pkg/planner/core/rule_inject_extra_projection.go @@ -16,12 +16,12 @@ package core import ( "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core/internal" + "github.com/pingcap/tidb/pkg/planner/util" ) // InjectExtraProjection is used to extract the expressions of specific diff --git a/planner/core/rule_join_elimination.go b/pkg/planner/core/rule_join_elimination.go similarity index 98% rename from planner/core/rule_join_elimination.go rename to pkg/planner/core/rule_join_elimination.go index 45a1cb278f1c5..c5330a1f6b4d6 100644 --- a/planner/core/rule_join_elimination.go +++ b/pkg/planner/core/rule_join_elimination.go @@ -19,9 +19,9 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/util/set" ) type outerJoinEliminator struct { diff --git a/planner/core/rule_join_reorder.go b/pkg/planner/core/rule_join_reorder.go similarity index 99% rename from planner/core/rule_join_reorder.go rename to pkg/planner/core/rule_join_reorder.go index dc1e949fe95f2..17e598b9d473c 100644 --- a/planner/core/rule_join_reorder.go +++ b/pkg/planner/core/rule_join_reorder.go @@ -20,11 +20,11 @@ import ( "fmt" "slices" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/tracing" ) // extractJoinGroup extracts all the join nodes connected with continuous diff --git a/planner/core/rule_join_reorder_dp.go b/pkg/planner/core/rule_join_reorder_dp.go similarity index 99% rename from planner/core/rule_join_reorder_dp.go rename to pkg/planner/core/rule_join_reorder_dp.go index 9e87ced883fe3..2d0cb21b50851 100644 --- a/planner/core/rule_join_reorder_dp.go +++ b/pkg/planner/core/rule_join_reorder_dp.go @@ -17,8 +17,8 @@ package core import ( "math/bits" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" ) type joinReorderDPSolver struct { diff --git a/planner/core/rule_join_reorder_dp_test.go b/pkg/planner/core/rule_join_reorder_dp_test.go similarity index 96% rename from planner/core/rule_join_reorder_dp_test.go rename to pkg/planner/core/rule_join_reorder_dp_test.go index 926ad80194c9c..ca48f11908ec5 100644 --- a/planner/core/rule_join_reorder_dp_test.go +++ b/pkg/planner/core/rule_join_reorder_dp_test.go @@ -18,14 +18,14 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/planner/core/rule_join_reorder_greedy.go b/pkg/planner/core/rule_join_reorder_greedy.go similarity index 99% rename from planner/core/rule_join_reorder_greedy.go rename to pkg/planner/core/rule_join_reorder_greedy.go index 4b648a9254960..2cc01d48dd55e 100644 --- a/planner/core/rule_join_reorder_greedy.go +++ b/pkg/planner/core/rule_join_reorder_greedy.go @@ -18,7 +18,7 @@ import ( "math" "sort" - "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/pkg/expression" ) type joinReorderGreedySolver struct { diff --git a/pkg/planner/core/rule_join_reorder_test.go b/pkg/planner/core/rule_join_reorder_test.go new file mode 100644 index 0000000000000..3298c0d87dbbd --- /dev/null +++ b/pkg/planner/core/rule_join_reorder_test.go @@ -0,0 +1,70 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestJoinOrderHintWithBinding(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t, t1, t2, t3;") + tk.MustExec("create table t(a int, b int, key(a));") + tk.MustExec("create table t1(a int, b int, key(a));") + tk.MustExec("create table t2(a int, b int, key(a));") + tk.MustExec("create table t3(a int, b int, key(a));") + + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res := tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ straight_join()*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") + + tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) + + tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ leading(t3) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`") + + tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") + + // test for outer join + tk.MustExec("select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, len(res), 0) + + tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b using select /*+ leading(t2) */ * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") + tk.MustExec("select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") + tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) + res = tk.MustQuery("show global bindings").Rows() + require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) left join `test` . `t3` on `t2` . `b` = `t3` . `b`") + + tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") +} diff --git a/planner/core/rule_max_min_eliminate.go b/pkg/planner/core/rule_max_min_eliminate.go similarity index 97% rename from planner/core/rule_max_min_eliminate.go rename to pkg/planner/core/rule_max_min_eliminate.go index fd3a098aea3f3..03cc81a60067d 100644 --- a/planner/core/rule_max_min_eliminate.go +++ b/pkg/planner/core/rule_max_min_eliminate.go @@ -19,14 +19,14 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" ) // maxMinEliminator tries to eliminate max/min aggregate function. diff --git a/planner/core/rule_partition_processor.go b/pkg/planner/core/rule_partition_processor.go similarity index 99% rename from planner/core/rule_partition_processor.go rename to pkg/planner/core/rule_partition_processor.go index 3a2da51f912ef..ccf16b62920eb 100644 --- a/planner/core/rule_partition_processor.go +++ b/pkg/planner/core/rule_partition_processor.go @@ -25,20 +25,20 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/set" ) // FullRange represent used all partitions. diff --git a/planner/core/rule_predicate_push_down.go b/pkg/planner/core/rule_predicate_push_down.go similarity index 99% rename from planner/core/rule_predicate_push_down.go rename to pkg/planner/core/rule_predicate_push_down.go index 8ef2f5ff325cd..dd2f82cd6ac09 100644 --- a/planner/core/rule_predicate_push_down.go +++ b/pkg/planner/core/rule_predicate_push_down.go @@ -19,15 +19,15 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/ranger" "go.uber.org/zap" ) diff --git a/planner/core/rule_predicate_simplification.go b/pkg/planner/core/rule_predicate_simplification.go similarity index 97% rename from planner/core/rule_predicate_simplification.go rename to pkg/planner/core/rule_predicate_simplification.go index 98672bc97575d..00f65638423d2 100644 --- a/planner/core/rule_predicate_simplification.go +++ b/pkg/planner/core/rule_predicate_simplification.go @@ -19,9 +19,9 @@ import ( "errors" "slices" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" ) // predicateSimplification consolidates different predcicates on a column and its equivalence classes. Initial out is for diff --git a/planner/core/rule_push_down_sequence.go b/pkg/planner/core/rule_push_down_sequence.go similarity index 100% rename from planner/core/rule_push_down_sequence.go rename to pkg/planner/core/rule_push_down_sequence.go diff --git a/planner/core/rule_resolve_grouping_expand.go b/pkg/planner/core/rule_resolve_grouping_expand.go similarity index 100% rename from planner/core/rule_resolve_grouping_expand.go rename to pkg/planner/core/rule_resolve_grouping_expand.go diff --git a/planner/core/rule_result_reorder.go b/pkg/planner/core/rule_result_reorder.go similarity index 97% rename from planner/core/rule_result_reorder.go rename to pkg/planner/core/rule_result_reorder.go index 0b207ad0be594..96e8137389b0e 100644 --- a/planner/core/rule_result_reorder.go +++ b/pkg/planner/core/rule_result_reorder.go @@ -17,8 +17,8 @@ package core import ( "context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/util" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/util" ) /* diff --git a/planner/core/rule_semi_join_rewrite.go b/pkg/planner/core/rule_semi_join_rewrite.go similarity index 97% rename from planner/core/rule_semi_join_rewrite.go rename to pkg/planner/core/rule_semi_join_rewrite.go index db2737cd30706..2082082d30d92 100644 --- a/planner/core/rule_semi_join_rewrite.go +++ b/pkg/planner/core/rule_semi_join_rewrite.go @@ -17,9 +17,9 @@ package core import ( "context" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/ast" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/ast" ) type semiJoinRewriter struct { diff --git a/planner/core/rule_topn_push_down.go b/pkg/planner/core/rule_topn_push_down.go similarity index 98% rename from planner/core/rule_topn_push_down.go rename to pkg/planner/core/rule_topn_push_down.go index d9b94ec124e50..0bf451035b9f2 100644 --- a/planner/core/rule_topn_push_down.go +++ b/pkg/planner/core/rule_topn_push_down.go @@ -19,9 +19,9 @@ import ( "context" "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // pushDownTopNOptimizer pushes down the topN or limit. In the future we will remove the limit from `requiredProperty` in CBO phase. diff --git a/planner/core/runtime_filter.go b/pkg/planner/core/runtime_filter.go similarity index 97% rename from planner/core/runtime_filter.go rename to pkg/planner/core/runtime_filter.go index 802a12ea77dd5..4f9339db7a96b 100644 --- a/planner/core/runtime_filter.go +++ b/pkg/planner/core/runtime_filter.go @@ -18,12 +18,12 @@ import ( "fmt" "strings" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/planner/core/runtime_filter_generator.go b/pkg/planner/core/runtime_filter_generator.go similarity index 96% rename from planner/core/runtime_filter_generator.go rename to pkg/planner/core/runtime_filter_generator.go index 38f1e591508a0..891689457a101 100644 --- a/planner/core/runtime_filter_generator.go +++ b/pkg/planner/core/runtime_filter_generator.go @@ -15,13 +15,13 @@ package core import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/planner/core/runtime_filter_generator_test.go b/pkg/planner/core/runtime_filter_generator_test.go similarity index 82% rename from planner/core/runtime_filter_generator_test.go rename to pkg/planner/core/runtime_filter_generator_test.go index 61585e4fe6ba2..6543177d607f6 100644 --- a/planner/core/runtime_filter_generator_test.go +++ b/pkg/planner/core/runtime_filter_generator_test.go @@ -19,13 +19,13 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" ) @@ -75,9 +75,9 @@ func TestRuntimeFilterGenerator(t *testing.T) { planSuiteData.LoadTestCases(t, &input, &output) tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") tk.MustExec("set tidb_runtime_filter_mode=LOCAL;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/core/mockPreferredBuildIndex", fmt.Sprintf(`return(%d)`, 0))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/mockPreferredBuildIndex", fmt.Sprintf(`return(%d)`, 0))) defer func() { - failpoint.Disable("github.com/pingcap/tidb/planner/core/mockPreferredBuildIndex") + failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/mockPreferredBuildIndex") }() for i, ts := range input { testdata.OnRecord(func() { diff --git a/planner/core/scalar_subq_expression.go b/pkg/planner/core/scalar_subq_expression.go similarity index 96% rename from planner/core/scalar_subq_expression.go rename to pkg/planner/core/scalar_subq_expression.go index 922309077422b..d4c3d852828b0 100644 --- a/planner/core/scalar_subq_expression.go +++ b/pkg/planner/core/scalar_subq_expression.go @@ -20,14 +20,14 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/planner/core/internal/base" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" ) // ScalarSubqueryEvalCtx store the plan for the subquery, used by ScalarSubQueryExpr. diff --git a/planner/core/show_predicate_extractor.go b/pkg/planner/core/show_predicate_extractor.go similarity index 94% rename from planner/core/show_predicate_extractor.go rename to pkg/planner/core/show_predicate_extractor.go index 566baa686a799..f2695d525a1ae 100644 --- a/planner/core/show_predicate_extractor.go +++ b/pkg/planner/core/show_predicate_extractor.go @@ -19,11 +19,11 @@ import ( "fmt" "strings" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/stringutil" ) const ( diff --git a/pkg/planner/core/stats.go b/pkg/planner/core/stats.go new file mode 100644 index 0000000000000..371e5b94aafcc --- /dev/null +++ b/pkg/planner/core/stats.go @@ -0,0 +1,1042 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core + +import ( + "context" + "fmt" + "math" + "slices" + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/ranger" + "go.uber.org/zap" +) + +func (p *basePhysicalPlan) StatsCount() float64 { + return p.StatsInfo().RowCount +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalTableDual) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + profile := &property.StatsInfo{ + RowCount: float64(p.RowCount), + ColNDVs: make(map[int64]float64, selfSchema.Len()), + } + for _, col := range selfSchema.Columns { + profile.ColNDVs[col.UniqueID] = float64(p.RowCount) + } + p.SetStats(profile) + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalMemTable) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + statsTable := statistics.PseudoTable(p.TableInfo, false) + stats := &property.StatsInfo{ + RowCount: float64(statsTable.RealtimeCount), + ColNDVs: make(map[int64]float64, len(p.TableInfo.Columns)), + HistColl: statsTable.GenerateHistCollFromColumnInfo(p.TableInfo, p.schema.Columns), + StatsVersion: statistics.PseudoVersion, + } + for _, col := range selfSchema.Columns { + stats.ColNDVs[col.UniqueID] = float64(statsTable.RealtimeCount) + } + p.SetStats(stats) + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalShow) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + // A fake count, just to avoid panic now. + p.SetStats(getFakeStats(selfSchema)) + return p.StatsInfo(), nil +} + +func getFakeStats(schema *expression.Schema) *property.StatsInfo { + profile := &property.StatsInfo{ + RowCount: 1, + ColNDVs: make(map[int64]float64, schema.Len()), + } + for _, col := range schema.Columns { + profile.ColNDVs[col.UniqueID] = 1 + } + return profile +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalShowDDLJobs) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + // A fake count, just to avoid panic now. + p.SetStats(getFakeStats(selfSchema)) + return p.StatsInfo(), nil +} + +// RecursiveDeriveStats4Test is a exporter just for test. +func RecursiveDeriveStats4Test(p LogicalPlan) (*property.StatsInfo, error) { + return p.recursiveDeriveStats(nil) +} + +// GetStats4Test is a exporter just for test. +func GetStats4Test(p LogicalPlan) *property.StatsInfo { + return p.StatsInfo() +} + +func (p *baseLogicalPlan) recursiveDeriveStats(colGroups [][]*expression.Column) (*property.StatsInfo, error) { + childStats := make([]*property.StatsInfo, len(p.children)) + childSchema := make([]*expression.Schema, len(p.children)) + cumColGroups := p.self.ExtractColGroups(colGroups) + for i, child := range p.children { + childProfile, err := child.recursiveDeriveStats(cumColGroups) + if err != nil { + return nil, err + } + childStats[i] = childProfile + childSchema[i] = child.Schema() + } + return p.self.DeriveStats(childStats, p.self.Schema(), childSchema, colGroups) +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (*baseLogicalPlan) ExtractColGroups(_ [][]*expression.Column) [][]*expression.Column { + return nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *baseLogicalPlan) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if len(childStats) == 1 { + p.SetStats(childStats[0]) + return p.StatsInfo(), nil + } + if len(childStats) > 1 { + err := ErrInternal.GenWithStack("LogicalPlans with more than one child should implement their own DeriveStats().") + return nil, err + } + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + profile := &property.StatsInfo{ + RowCount: float64(1), + ColNDVs: make(map[int64]float64, selfSchema.Len()), + } + for _, col := range selfSchema.Columns { + profile.ColNDVs[col.UniqueID] = 1 + } + p.SetStats(profile) + return profile, nil +} + +func (ds *DataSource) getGroupNDVs(colGroups [][]*expression.Column) []property.GroupNDV { + if colGroups == nil { + return nil + } + tbl := ds.tableStats.HistColl + ndvs := make([]property.GroupNDV, 0, len(colGroups)) + for idxID, idx := range tbl.Indices { + colsLen := len(tbl.Idx2ColumnIDs[idxID]) + // tbl.Idx2ColumnIDs may only contain the prefix of index columns. + // But it may exceeds the total index since the index would contain the handle column if it's not a unique index. + // We append the handle at fillIndexPath. + if colsLen < len(idx.Info.Columns) { + continue + } else if colsLen > len(idx.Info.Columns) { + colsLen-- + } + idxCols := make([]int64, colsLen) + copy(idxCols, tbl.Idx2ColumnIDs[idxID]) + slices.Sort(idxCols) + for _, g := range colGroups { + // We only want those exact matches. + if len(g) != colsLen { + continue + } + match := true + for i, col := range g { + // Both slices are sorted according to UniqueID. + if col.UniqueID != idxCols[i] { + match = false + break + } + } + if match { + ndv := property.GroupNDV{ + Cols: idxCols, + NDV: float64(idx.NDV), + } + ndvs = append(ndvs, ndv) + break + } + } + } + return ndvs +} + +func init() { + // To handle cycle import, we have to define this function here. + cardinality.GetTblInfoForUsedStatsByPhysicalID = getTblInfoForUsedStatsByPhysicalID +} + +// getTblInfoForUsedStatsByPhysicalID get table name, partition name and TableInfo that will be used to record used stats. +func getTblInfoForUsedStatsByPhysicalID(sctx sessionctx.Context, id int64) (fullName string, tblInfo *model.TableInfo) { + fullName = "tableID " + strconv.FormatInt(id, 10) + + is := domain.GetDomain(sctx).InfoSchema() + var tbl table.Table + var partDef *model.PartitionDefinition + + tbl, partDef = infoschema.FindTableByTblOrPartID(is, id) + if tbl == nil || tbl.Meta() == nil { + return + } + tblInfo = tbl.Meta() + fullName = tblInfo.Name.O + if partDef != nil { + fullName += " " + partDef.Name.O + } else if pi := tblInfo.GetPartitionInfo(); pi != nil && len(pi.Definitions) > 0 { + fullName += " global" + } + return +} + +func (ds *DataSource) initStats(colGroups [][]*expression.Column) { + if ds.tableStats != nil { + // Reload GroupNDVs since colGroups may have changed. + ds.tableStats.GroupNDVs = ds.getGroupNDVs(colGroups) + return + } + if ds.statisticTable == nil { + ds.statisticTable = getStatsTable(ds.SCtx(), ds.tableInfo, ds.physicalTableID) + } + tableStats := &property.StatsInfo{ + RowCount: float64(ds.statisticTable.RealtimeCount), + ColNDVs: make(map[int64]float64, ds.schema.Len()), + HistColl: ds.statisticTable.GenerateHistCollFromColumnInfo(ds.tableInfo, ds.TblCols), + StatsVersion: ds.statisticTable.Version, + } + if ds.statisticTable.Pseudo { + tableStats.StatsVersion = statistics.PseudoVersion + } + + statsRecord := ds.SCtx().GetSessionVars().StmtCtx.GetUsedStatsInfo(true) + name, tblInfo := getTblInfoForUsedStatsByPhysicalID(ds.SCtx(), ds.physicalTableID) + statsRecord[ds.physicalTableID] = &stmtctx.UsedStatsInfoForTable{ + Name: name, + TblInfo: tblInfo, + Version: tableStats.StatsVersion, + RealtimeCount: tableStats.HistColl.RealtimeCount, + ModifyCount: tableStats.HistColl.ModifyCount, + } + + for _, col := range ds.schema.Columns { + tableStats.ColNDVs[col.UniqueID] = cardinality.EstimateColumnNDV(ds.statisticTable, col.ID) + } + ds.tableStats = tableStats + ds.tableStats.GroupNDVs = ds.getGroupNDVs(colGroups) + ds.TblColHists = ds.statisticTable.ID2UniqueID(ds.TblCols) +} + +func (ds *DataSource) deriveStatsByFilter(conds expression.CNFExprs, filledPaths []*util.AccessPath) *property.StatsInfo { + if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(ds.SCtx()) + defer debugtrace.LeaveContextCommon(ds.SCtx()) + } + selectivity, _, err := cardinality.Selectivity(ds.SCtx(), ds.tableStats.HistColl, conds, filledPaths) + if err != nil { + logutil.BgLogger().Debug("something wrong happened, use the default selectivity", zap.Error(err)) + selectivity = SelectionFactor + } + // TODO: remove NewHistCollBySelectivity later on. + // if ds.SCtx().GetSessionVars().OptimizerSelectivityLevel >= 1 { + // Only '0' is suggested, see https://docs.pingcap.com/zh/tidb/stable/system-variables#tidb_optimizer_selectivity_level. + // stats.HistColl = stats.HistColl.NewHistCollBySelectivity(ds.SCtx(), nodes) + // } + return ds.tableStats.Scale(selectivity) +} + +// We bind logic of derivePathStats and tryHeuristics together. When some path matches the heuristic rule, we don't need +// to derive stats of subsequent paths. In this way we can save unnecessary computation of derivePathStats. +func (ds *DataSource) derivePathStatsAndTryHeuristics() error { + if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(ds.SCtx()) + defer debugtrace.LeaveContextCommon(ds.SCtx()) + } + uniqueIdxsWithDoubleScan := make([]*util.AccessPath, 0, len(ds.possibleAccessPaths)) + singleScanIdxs := make([]*util.AccessPath, 0, len(ds.possibleAccessPaths)) + var ( + selected, uniqueBest, refinedBest *util.AccessPath + isRefinedPath bool + ) + for _, path := range ds.possibleAccessPaths { + if path.IsTablePath() { + err := ds.deriveTablePathStats(path, ds.pushedDownConds, false) + if err != nil { + return err + } + path.IsSingleScan = true + } else { + ds.deriveIndexPathStats(path, ds.pushedDownConds, false) + path.IsSingleScan = ds.isSingleScan(path.FullIdxCols, path.FullIdxColLens) + } + // Try some heuristic rules to select access path. + if len(path.Ranges) == 0 { + selected = path + break + } + if path.OnlyPointRange(ds.SCtx()) { + if path.IsTablePath() || path.Index.Unique { + if path.IsSingleScan { + selected = path + break + } + uniqueIdxsWithDoubleScan = append(uniqueIdxsWithDoubleScan, path) + } + } else if path.IsSingleScan { + singleScanIdxs = append(singleScanIdxs, path) + } + } + if selected == nil && len(uniqueIdxsWithDoubleScan) > 0 { + uniqueIdxAccessCols := make([]util.Col2Len, 0, len(uniqueIdxsWithDoubleScan)) + for _, uniqueIdx := range uniqueIdxsWithDoubleScan { + uniqueIdxAccessCols = append(uniqueIdxAccessCols, uniqueIdx.GetCol2LenFromAccessConds()) + // Find the unique index with the minimal number of ranges as `uniqueBest`. + if uniqueBest == nil || len(uniqueIdx.Ranges) < len(uniqueBest.Ranges) { + uniqueBest = uniqueIdx + } + } + // `uniqueBest` may not always be the best. + // ``` + // create table t(a int, b int, c int, unique index idx_b(b), index idx_b_c(b, c)); + // select b, c from t where b = 5 and c > 10; + // ``` + // In the case, `uniqueBest` is `idx_b`. However, `idx_b_c` is better than `idx_b`. + // Hence, for each index in `singleScanIdxs`, we check whether it is better than some index in `uniqueIdxsWithDoubleScan`. + // If yes, the index is a refined one. We find the refined index with the minimal number of ranges as `refineBest`. + for _, singleScanIdx := range singleScanIdxs { + col2Len := singleScanIdx.GetCol2LenFromAccessConds() + for _, uniqueIdxCol2Len := range uniqueIdxAccessCols { + accessResult, comparable1 := util.CompareCol2Len(col2Len, uniqueIdxCol2Len) + if comparable1 && accessResult == 1 { + if refinedBest == nil || len(singleScanIdx.Ranges) < len(refinedBest.Ranges) { + refinedBest = singleScanIdx + } + } + } + } + // `refineBest` may not always be better than `uniqueBest`. + // ``` + // create table t(a int, b int, c int, d int, unique index idx_a(a), unique index idx_b_c(b, c), unique index idx_b_c_a_d(b, c, a, d)); + // select a, b, c from t where a = 1 and b = 2 and c in (1, 2, 3, 4, 5); + // ``` + // In the case, `refinedBest` is `idx_b_c_a_d` and `uniqueBest` is `a`. `idx_b_c_a_d` needs to access five points while `idx_a` + // only needs one point access and one table access. + // Hence we should compare `len(refinedBest.Ranges)` and `2*len(uniqueBest.Ranges)` to select the better one. + if refinedBest != nil && (uniqueBest == nil || len(refinedBest.Ranges) < 2*len(uniqueBest.Ranges)) { + selected = refinedBest + isRefinedPath = true + } else { + selected = uniqueBest + } + } + // heuristic rule pruning other path should consider hint prefer. + // If no hints and some path matches a heuristic rule, just remove other possible paths. + if selected != nil { + // if user wanna tiFlash read, while current heuristic choose a TiKV path. so we shouldn't prune other paths. + keep := ds.preferStoreType&preferTiFlash != 0 && selected.StoreType != kv.TiFlash + if keep { + return nil + } + ds.possibleAccessPaths[0] = selected + ds.possibleAccessPaths = ds.possibleAccessPaths[:1] + var tableName string + if ds.TableAsName.O == "" { + tableName = ds.tableInfo.Name.O + } else { + tableName = ds.TableAsName.O + } + var sb strings.Builder + if selected.IsTablePath() { + // TODO: primary key / handle / real name? + fmt.Fprintf(&sb, "handle of %s is selected since the path only has point ranges", tableName) + } else { + if selected.Index.Unique { + sb.WriteString("unique ") + } + sb.WriteString(fmt.Sprintf("index %s of %s is selected since the path", selected.Index.Name.O, tableName)) + if isRefinedPath { + sb.WriteString(" only fetches limited number of rows") + } else { + sb.WriteString(" only has point ranges") + } + if selected.IsSingleScan { + sb.WriteString(" with single scan") + } else { + sb.WriteString(" with double scan") + } + } + if ds.SCtx().GetSessionVars().StmtCtx.InVerboseExplain { + ds.SCtx().GetSessionVars().StmtCtx.AppendNote(errors.New(sb.String())) + } else { + ds.SCtx().GetSessionVars().StmtCtx.AppendExtraNote(errors.New(sb.String())) + } + } + return nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (ds *DataSource) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + if ds.StatsInfo() != nil && len(colGroups) == 0 { + return ds.StatsInfo(), nil + } + ds.initStats(colGroups) + if ds.StatsInfo() != nil { + // Just reload the GroupNDVs. + selectivity := ds.StatsInfo().RowCount / ds.tableStats.RowCount + ds.SetStats(ds.tableStats.Scale(selectivity)) + return ds.StatsInfo(), nil + } + if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(ds.SCtx()) + defer debugtrace.LeaveContextCommon(ds.SCtx()) + } + // two preprocess here. + // 1: PushDownNot here can convert query 'not (a != 1)' to 'a = 1'. + // 2: EliminateNoPrecisionCast here can convert query 'cast(c as bigint) = 1' to 'c = 1' to leverage access range. + for i, expr := range ds.pushedDownConds { + ds.pushedDownConds[i] = expression.PushDownNot(ds.SCtx(), expr) + ds.pushedDownConds[i] = expression.EliminateNoPrecisionLossCast(ds.SCtx(), ds.pushedDownConds[i]) + } + for _, path := range ds.possibleAccessPaths { + if path.IsTablePath() { + continue + } + err := ds.fillIndexPath(path, ds.pushedDownConds) + if err != nil { + return nil, err + } + } + // TODO: Can we move ds.deriveStatsByFilter after pruning by heuristics? In this way some computation can be avoided + // when ds.possibleAccessPaths are pruned. + ds.SetStats(ds.deriveStatsByFilter(ds.pushedDownConds, ds.possibleAccessPaths)) + err := ds.derivePathStatsAndTryHeuristics() + if err != nil { + return nil, err + } + + if err := ds.generateIndexMergePath(); err != nil { + return nil, err + } + + if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugTraceAccessPaths(ds.SCtx(), ds.possibleAccessPaths) + } + ds.accessPathMinSelectivity = getMinSelectivityFromPaths(ds.possibleAccessPaths, float64(ds.TblColHists.RealtimeCount)) + + return ds.StatsInfo(), nil +} + +func getMinSelectivityFromPaths(paths []*util.AccessPath, totalRowCount float64) float64 { + minSelectivity := 1.0 + if totalRowCount <= 0 { + return minSelectivity + } + for _, path := range paths { + // For table path and index merge path, AccessPath.CountAfterIndex is not set and meaningless, + // but we still consider their AccessPath.CountAfterAccess. + if path.IsTablePath() || path.PartialIndexPaths != nil { + minSelectivity = mathutil.Min(minSelectivity, path.CountAfterAccess/totalRowCount) + continue + } + minSelectivity = mathutil.Min(minSelectivity, path.CountAfterIndex/totalRowCount) + } + return minSelectivity +} + +// DeriveStats implements LogicalPlan DeriveStats interface. +func (ts *LogicalTableScan) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (_ *property.StatsInfo, err error) { + ts.Source.initStats(nil) + // PushDownNot here can convert query 'not (a != 1)' to 'a = 1'. + for i, expr := range ts.AccessConds { + // TODO The expressions may be shared by TableScan and several IndexScans, there would be redundant + // `PushDownNot` function call in multiple `DeriveStats` then. + ts.AccessConds[i] = expression.PushDownNot(ts.SCtx(), expr) + } + ts.SetStats(ts.Source.deriveStatsByFilter(ts.AccessConds, nil)) + // ts.Handle could be nil if PK is Handle, and PK column has been pruned. + // TODO: support clustered index. + if ts.HandleCols != nil { + // TODO: restrict mem usage of table ranges. + ts.Ranges, _, _, err = ranger.BuildTableRange(ts.AccessConds, ts.SCtx(), ts.HandleCols.GetCol(0).RetType, 0) + } else { + isUnsigned := false + if ts.Source.tableInfo.PKIsHandle { + if pkColInfo := ts.Source.tableInfo.GetPkColInfo(); pkColInfo != nil { + isUnsigned = mysql.HasUnsignedFlag(pkColInfo.GetFlag()) + } + } + ts.Ranges = ranger.FullIntRange(isUnsigned) + } + if err != nil { + return nil, err + } + return ts.StatsInfo(), nil +} + +// DeriveStats implements LogicalPlan DeriveStats interface. +func (is *LogicalIndexScan) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + is.Source.initStats(nil) + for i, expr := range is.AccessConds { + is.AccessConds[i] = expression.PushDownNot(is.SCtx(), expr) + } + is.SetStats(is.Source.deriveStatsByFilter(is.AccessConds, nil)) + if len(is.AccessConds) == 0 { + is.Ranges = ranger.FullRange() + } + is.IdxCols, is.IdxColLens = expression.IndexInfo2PrefixCols(is.Columns, selfSchema.Columns, is.Index) + is.FullIdxCols, is.FullIdxColLens = expression.IndexInfo2Cols(is.Columns, selfSchema.Columns, is.Index) + if !is.Index.Unique && !is.Index.Primary && len(is.Index.Columns) == len(is.IdxCols) { + handleCol := is.getPKIsHandleCol(selfSchema) + if handleCol != nil && !mysql.HasUnsignedFlag(handleCol.RetType.GetFlag()) { + is.IdxCols = append(is.IdxCols, handleCol) + is.IdxColLens = append(is.IdxColLens, types.UnspecifiedLength) + } + } + return is.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalSelection) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + p.SetStats(childStats[0].Scale(SelectionFactor)) + p.StatsInfo().GroupNDVs = nil + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalUnionAll) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + p.SetStats(&property.StatsInfo{ + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + for _, childProfile := range childStats { + p.StatsInfo().RowCount += childProfile.RowCount + for _, col := range selfSchema.Columns { + p.StatsInfo().ColNDVs[col.UniqueID] += childProfile.ColNDVs[col.UniqueID] + } + } + return p.StatsInfo(), nil +} + +func deriveLimitStats(childProfile *property.StatsInfo, limitCount float64) *property.StatsInfo { + stats := &property.StatsInfo{ + RowCount: math.Min(limitCount, childProfile.RowCount), + ColNDVs: make(map[int64]float64, len(childProfile.ColNDVs)), + } + for id, c := range childProfile.ColNDVs { + stats.ColNDVs[id] = math.Min(c, stats.RowCount) + } + return stats +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalLimit) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + p.SetStats(deriveLimitStats(childStats[0], float64(p.Count))) + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (lt *LogicalTopN) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if lt.StatsInfo() != nil { + return lt.StatsInfo(), nil + } + lt.SetStats(deriveLimitStats(childStats[0], float64(lt.Count))) + return lt.StatsInfo(), nil +} + +func (p *LogicalProjection) getGroupNDVs(colGroups [][]*expression.Column, childProfile *property.StatsInfo, selfSchema *expression.Schema) []property.GroupNDV { + if len(colGroups) == 0 || len(childProfile.GroupNDVs) == 0 { + return nil + } + exprCol2ProjCol := make(map[int64]int64) + for i, expr := range p.Exprs { + exprCol, ok := expr.(*expression.Column) + if !ok { + continue + } + exprCol2ProjCol[exprCol.UniqueID] = selfSchema.Columns[i].UniqueID + } + ndvs := make([]property.GroupNDV, 0, len(childProfile.GroupNDVs)) + for _, childGroupNDV := range childProfile.GroupNDVs { + projCols := make([]int64, len(childGroupNDV.Cols)) + for i, col := range childGroupNDV.Cols { + projCol, ok := exprCol2ProjCol[col] + if !ok { + projCols = nil + break + } + projCols[i] = projCol + } + if projCols == nil { + continue + } + slices.Sort(projCols) + groupNDV := property.GroupNDV{ + Cols: projCols, + NDV: childGroupNDV.NDV, + } + ndvs = append(ndvs, groupNDV) + } + return ndvs +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalProjection) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + childProfile := childStats[0] + if p.StatsInfo() != nil { + // Reload GroupNDVs since colGroups may have changed. + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childProfile, selfSchema) + return p.StatsInfo(), nil + } + p.SetStats(&property.StatsInfo{ + RowCount: childProfile.RowCount, + ColNDVs: make(map[int64]float64, len(p.Exprs)), + }) + for i, expr := range p.Exprs { + cols := expression.ExtractColumns(expr) + p.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID], _ = cardinality.EstimateColsNDVWithMatchedLen(cols, childSchema[0], childProfile) + } + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childProfile, selfSchema) + return p.StatsInfo(), nil +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (p *LogicalProjection) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { + if len(colGroups) == 0 { + return nil + } + extColGroups, _ := p.Schema().ExtractColGroups(colGroups) + if len(extColGroups) == 0 { + return nil + } + extracted := make([][]*expression.Column, 0, len(extColGroups)) + for _, cols := range extColGroups { + exprs := make([]*expression.Column, len(cols)) + allCols := true + for i, offset := range cols { + col, ok := p.Exprs[offset].(*expression.Column) + // TODO: for functional dependent projections like `col1 + 1` -> `col2`, we can maintain GroupNDVs actually. + if !ok { + allCols = false + break + } + exprs[i] = col + } + if allCols { + extracted = append(extracted, expression.SortColumns(exprs)) + } + } + return extracted +} + +func (*LogicalAggregation) getGroupNDVs(colGroups [][]*expression.Column, childProfile *property.StatsInfo, gbyCols []*expression.Column) []property.GroupNDV { + if len(colGroups) == 0 { + return nil + } + // Check if the child profile provides GroupNDV for the GROUP BY columns. + // Note that gbyCols may not be the exact GROUP BY columns, e.g, GROUP BY a+b, + // but we have no other approaches for the NDV estimation of these cases + // except for using the independent assumption, unless we can use stats of expression index. + groupNDV := childProfile.GetGroupNDV4Cols(gbyCols) + if groupNDV == nil { + return nil + } + return []property.GroupNDV{*groupNDV} +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (la *LogicalAggregation) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + childProfile := childStats[0] + gbyCols := make([]*expression.Column, 0, len(la.GroupByItems)) + for _, gbyExpr := range la.GroupByItems { + cols := expression.ExtractColumns(gbyExpr) + gbyCols = append(gbyCols, cols...) + } + if la.StatsInfo() != nil { + // Reload GroupNDVs since colGroups may have changed. + la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childProfile, gbyCols) + return la.StatsInfo(), nil + } + ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(gbyCols, childSchema[0], childProfile) + la.SetStats(&property.StatsInfo{ + RowCount: ndv, + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + // We cannot estimate the ColNDVs for every output, so we use a conservative strategy. + for _, col := range selfSchema.Columns { + la.StatsInfo().ColNDVs[col.UniqueID] = ndv + } + la.inputCount = childProfile.RowCount + la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childProfile, gbyCols) + return la.StatsInfo(), nil +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (la *LogicalAggregation) ExtractColGroups(_ [][]*expression.Column) [][]*expression.Column { + // Parent colGroups would be dicarded, because aggregation would make NDV of colGroups + // which does not match GroupByItems invalid. + // Note that gbyCols may not be the exact GROUP BY columns, e.g, GROUP BY a+b, + // but we have no other approaches for the NDV estimation of these cases + // except for using the independent assumption, unless we can use stats of expression index. + gbyCols := make([]*expression.Column, 0, len(la.GroupByItems)) + for _, gbyExpr := range la.GroupByItems { + cols := expression.ExtractColumns(gbyExpr) + gbyCols = append(gbyCols, cols...) + } + if len(gbyCols) > 1 { + return [][]*expression.Column{expression.SortColumns(gbyCols)} + } + return nil +} + +func (p *LogicalJoin) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { + outerIdx := int(-1) + if p.JoinType == LeftOuterJoin || p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { + outerIdx = 0 + } else if p.JoinType == RightOuterJoin { + outerIdx = 1 + } + if outerIdx >= 0 && len(colGroups) > 0 { + return childStats[outerIdx].GroupNDVs + } + return nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +// If the type of join is SemiJoin, the selectivity of it will be same as selection's. +// If the type of join is LeftOuterSemiJoin, it will not add or remove any row. The last column is a boolean value, whose NDV should be two. +// If the type of join is inner/outer join, the output of join(s, t) should be N(s) * N(t) / (V(s.key) * V(t.key)) * Min(s.key, t.key). +// N(s) stands for the number of rows in relation s. V(s.key) means the NDV of join key in s. +// This is a quite simple strategy: We assume every bucket of relation which will participate join has the same number of rows, and apply cross join for +// every matched bucket. +func (p *LogicalJoin) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + // Reload GroupNDVs since colGroups may have changed. + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) + return p.StatsInfo(), nil + } + leftProfile, rightProfile := childStats[0], childStats[1] + leftJoinKeys, rightJoinKeys, _, _ := p.GetJoinKeys() + p.equalCondOutCnt = cardinality.EstimateFullJoinRowCount(p.SCtx(), + 0 == len(p.EqualConditions), + leftProfile, rightProfile, + leftJoinKeys, rightJoinKeys, + childSchema[0], childSchema[1], + nil, nil) + if p.JoinType == SemiJoin || p.JoinType == AntiSemiJoin { + p.SetStats(&property.StatsInfo{ + RowCount: leftProfile.RowCount * SelectionFactor, + ColNDVs: make(map[int64]float64, len(leftProfile.ColNDVs)), + }) + for id, c := range leftProfile.ColNDVs { + p.StatsInfo().ColNDVs[id] = c * SelectionFactor + } + return p.StatsInfo(), nil + } + if p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { + p.SetStats(&property.StatsInfo{ + RowCount: leftProfile.RowCount, + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + for id, c := range leftProfile.ColNDVs { + p.StatsInfo().ColNDVs[id] = c + } + p.StatsInfo().ColNDVs[selfSchema.Columns[selfSchema.Len()-1].UniqueID] = 2.0 + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) + return p.StatsInfo(), nil + } + count := p.equalCondOutCnt + if p.JoinType == LeftOuterJoin { + count = math.Max(count, leftProfile.RowCount) + } else if p.JoinType == RightOuterJoin { + count = math.Max(count, rightProfile.RowCount) + } + colNDVs := make(map[int64]float64, selfSchema.Len()) + for id, c := range leftProfile.ColNDVs { + colNDVs[id] = math.Min(c, count) + } + for id, c := range rightProfile.ColNDVs { + colNDVs[id] = math.Min(c, count) + } + p.SetStats(&property.StatsInfo{ + RowCount: count, + ColNDVs: colNDVs, + }) + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) + return p.StatsInfo(), nil +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (p *LogicalJoin) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { + leftJoinKeys, rightJoinKeys, _, _ := p.GetJoinKeys() + extracted := make([][]*expression.Column, 0, 2+len(colGroups)) + if len(leftJoinKeys) > 1 && (p.JoinType == InnerJoin || p.JoinType == LeftOuterJoin || p.JoinType == RightOuterJoin) { + extracted = append(extracted, expression.SortColumns(leftJoinKeys), expression.SortColumns(rightJoinKeys)) + } + var outerSchema *expression.Schema + if p.JoinType == LeftOuterJoin || p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { + outerSchema = p.Children()[0].Schema() + } else if p.JoinType == RightOuterJoin { + outerSchema = p.Children()[1].Schema() + } + if len(colGroups) == 0 || outerSchema == nil { + return extracted + } + _, offsets := outerSchema.ExtractColGroups(colGroups) + if len(offsets) == 0 { + return extracted + } + for _, offset := range offsets { + extracted = append(extracted, colGroups[offset]) + } + return extracted +} + +func (la *LogicalApply) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { + if len(colGroups) > 0 && (la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin || la.JoinType == LeftOuterJoin) { + return childStats[0].GroupNDVs + } + return nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (la *LogicalApply) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + if la.StatsInfo() != nil { + // Reload GroupNDVs since colGroups may have changed. + la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childStats) + return la.StatsInfo(), nil + } + leftProfile := childStats[0] + la.SetStats(&property.StatsInfo{ + RowCount: leftProfile.RowCount, + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + for id, c := range leftProfile.ColNDVs { + la.StatsInfo().ColNDVs[id] = c + } + if la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin { + la.StatsInfo().ColNDVs[selfSchema.Columns[selfSchema.Len()-1].UniqueID] = 2.0 + } else { + for i := childSchema[0].Len(); i < selfSchema.Len(); i++ { + la.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID] = leftProfile.RowCount + } + } + la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childStats) + return la.StatsInfo(), nil +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (la *LogicalApply) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { + var outerSchema *expression.Schema + // Apply doesn't have RightOuterJoin. + if la.JoinType == LeftOuterJoin || la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin { + outerSchema = la.Children()[0].Schema() + } + if len(colGroups) == 0 || outerSchema == nil { + return nil + } + _, offsets := outerSchema.ExtractColGroups(colGroups) + if len(offsets) == 0 { + return nil + } + extracted := make([][]*expression.Column, len(offsets)) + for i, offset := range offsets { + extracted[i] = colGroups[offset] + } + return extracted +} + +// Exists and MaxOneRow produce at most one row, so we set the RowCount of stats one. +func getSingletonStats(schema *expression.Schema) *property.StatsInfo { + ret := &property.StatsInfo{ + RowCount: 1.0, + ColNDVs: make(map[int64]float64, schema.Len()), + } + for _, col := range schema.Columns { + ret.ColNDVs[col.UniqueID] = 1 + } + return ret +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalMaxOneRow) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + p.SetStats(getSingletonStats(selfSchema)) + return p.StatsInfo(), nil +} + +func (*LogicalWindow) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { + if len(colGroups) > 0 { + return childStats[0].GroupNDVs + } + return nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalWindow) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + // Reload GroupNDVs since colGroups may have changed. + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) + return p.StatsInfo(), nil + } + childProfile := childStats[0] + p.SetStats(&property.StatsInfo{ + RowCount: childProfile.RowCount, + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + childLen := selfSchema.Len() - len(p.WindowFuncDescs) + for i := 0; i < childLen; i++ { + id := selfSchema.Columns[i].UniqueID + p.StatsInfo().ColNDVs[id] = childProfile.ColNDVs[id] + } + for i := childLen; i < selfSchema.Len(); i++ { + p.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID] = childProfile.RowCount + } + p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) + return p.StatsInfo(), nil +} + +// ExtractColGroups implements LogicalPlan ExtractColGroups interface. +func (p *LogicalWindow) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { + if len(colGroups) == 0 { + return nil + } + childSchema := p.Children()[0].Schema() + _, offsets := childSchema.ExtractColGroups(colGroups) + if len(offsets) == 0 { + return nil + } + extracted := make([][]*expression.Column, len(offsets)) + for i, offset := range offsets { + extracted[i] = colGroups[offset] + } + return extracted +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalCTE) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + + var err error + if p.cte.seedPartPhysicalPlan == nil { + // Build push-downed predicates. + if len(p.cte.pushDownPredicates) > 0 { + newCond := expression.ComposeDNFCondition(p.SCtx(), p.cte.pushDownPredicates...) + newSel := LogicalSelection{Conditions: []expression.Expression{newCond}}.Init(p.SCtx(), p.cte.seedPartLogicalPlan.SelectBlockOffset()) + newSel.SetChildren(p.cte.seedPartLogicalPlan) + p.cte.seedPartLogicalPlan = newSel + p.cte.optFlag |= flagPredicatePushDown + } + p.cte.seedPartLogicalPlan, p.cte.seedPartPhysicalPlan, _, err = DoOptimizeAndLogicAsRet(context.TODO(), p.SCtx(), p.cte.optFlag, p.cte.seedPartLogicalPlan) + if err != nil { + return nil, err + } + } + if p.onlyUsedAsStorage { + p.SetChildren(p.cte.seedPartLogicalPlan) + } + resStat := p.cte.seedPartPhysicalPlan.StatsInfo() + // Changing the pointer so that seedStat in LogicalCTETable can get the new stat. + *p.seedStat = *resStat + p.SetStats(&property.StatsInfo{ + RowCount: resStat.RowCount, + ColNDVs: make(map[int64]float64, selfSchema.Len()), + }) + for i, col := range selfSchema.Columns { + p.StatsInfo().ColNDVs[col.UniqueID] += resStat.ColNDVs[p.cte.seedPartLogicalPlan.Schema().Columns[i].UniqueID] + } + if p.cte.recursivePartLogicalPlan != nil { + if p.cte.recursivePartPhysicalPlan == nil { + p.cte.recursivePartPhysicalPlan, _, err = DoOptimize(context.TODO(), p.SCtx(), p.cte.optFlag, p.cte.recursivePartLogicalPlan) + if err != nil { + return nil, err + } + } + recurStat := p.cte.recursivePartLogicalPlan.StatsInfo() + for i, col := range selfSchema.Columns { + p.StatsInfo().ColNDVs[col.UniqueID] += recurStat.ColNDVs[p.cte.recursivePartLogicalPlan.Schema().Columns[i].UniqueID] + } + if p.cte.IsDistinct { + p.StatsInfo().RowCount, _ = cardinality.EstimateColsNDVWithMatchedLen(p.schema.Columns, p.schema, p.StatsInfo()) + } else { + p.StatsInfo().RowCount += recurStat.RowCount + } + } + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalCTETable) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + if p.StatsInfo() != nil { + return p.StatsInfo(), nil + } + p.SetStats(p.seedStat) + return p.StatsInfo(), nil +} + +// DeriveStats implement LogicalPlan DeriveStats interface. +func (p *LogicalSequence) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { + p.SetStats(childStats[len(childStats)-1]) + return p.StatsInfo(), nil +} diff --git a/pkg/planner/core/stringer.go b/pkg/planner/core/stringer.go new file mode 100644 index 0000000000000..df6b14528fbef --- /dev/null +++ b/pkg/planner/core/stringer.go @@ -0,0 +1,378 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package core + +import ( + "bytes" + "fmt" + "strings" + + "github.com/pingcap/tidb/pkg/util/plancodec" +) + +// ToString explains a Plan, returns description string. +func ToString(p Plan) string { + strs, _ := toString(p, []string{}, []int{}) + return strings.Join(strs, "->") +} + +// FDToString explains fd transfer over a Plan, returns description string. +func FDToString(p LogicalPlan) string { + strs, _ := fdToString(p, []string{}, []int{}) + for i, j := 0, len(strs)-1; i < j; i, j = i+1, j-1 { + strs[i], strs[j] = strs[j], strs[i] + } + return strings.Join(strs, " >>> ") +} + +func needIncludeChildrenString(plan Plan) bool { + switch x := plan.(type) { + case *LogicalUnionAll, *PhysicalUnionAll, *LogicalPartitionUnionAll: + // after https://github.com/pingcap/tidb/pull/25218, the union may contain less than 2 children, + // but we still wants to include its child plan's information when calling `toString` on union. + return true + case LogicalPlan: + return len(x.Children()) > 1 + case PhysicalPlan: + return len(x.Children()) > 1 + default: + return false + } +} + +func fdToString(in LogicalPlan, strs []string, idxs []int) ([]string, []int) { + switch x := in.(type) { + case *LogicalProjection: + strs = append(strs, "{"+x.fdSet.String()+"}") + for _, child := range x.Children() { + strs, idxs = fdToString(child, strs, idxs) + } + case *LogicalAggregation: + strs = append(strs, "{"+x.fdSet.String()+"}") + for _, child := range x.Children() { + strs, idxs = fdToString(child, strs, idxs) + } + case *DataSource: + strs = append(strs, "{"+x.fdSet.String()+"}") + case *LogicalApply: + strs = append(strs, "{"+x.fdSet.String()+"}") + case *LogicalJoin: + strs = append(strs, "{"+x.fdSet.String()+"}") + default: + } + return strs, idxs +} + +func toString(in Plan, strs []string, idxs []int) ([]string, []int) { + switch x := in.(type) { + case LogicalPlan: + if needIncludeChildrenString(in) { + idxs = append(idxs, len(strs)) + } + + for _, c := range x.Children() { + strs, idxs = toString(c, strs, idxs) + } + case *PhysicalExchangeReceiver: // do nothing + case PhysicalPlan: + if needIncludeChildrenString(in) { + idxs = append(idxs, len(strs)) + } + + for _, c := range x.Children() { + strs, idxs = toString(c, strs, idxs) + } + } + + var str string + switch x := in.(type) { + case *CheckTable: + str = "CheckTable" + case *PhysicalIndexScan: + str = fmt.Sprintf("Index(%s.%s)%v", x.Table.Name.L, x.Index.Name.L, x.Ranges) + case *PhysicalTableScan: + str = fmt.Sprintf("Table(%s)", x.Table.Name.L) + case *PhysicalHashJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + if x.InnerChildIdx == 0 { + str = "RightHashJoin{" + strings.Join(children, "->") + "}" + } else { + str = "LeftHashJoin{" + strings.Join(children, "->") + "}" + } + for _, eq := range x.EqualConditions { + l := eq.GetArgs()[0].String() + r := eq.GetArgs()[1].String() + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *PhysicalMergeJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + id := "MergeJoin" + switch x.JoinType { + case SemiJoin: + id = "MergeSemiJoin" + case AntiSemiJoin: + id = "MergeAntiSemiJoin" + case LeftOuterSemiJoin: + id = "MergeLeftOuterSemiJoin" + case AntiLeftOuterSemiJoin: + id = "MergeAntiLeftOuterSemiJoin" + case LeftOuterJoin: + id = "MergeLeftOuterJoin" + case RightOuterJoin: + id = "MergeRightOuterJoin" + case InnerJoin: + id = "MergeInnerJoin" + } + str = id + "{" + strings.Join(children, "->") + "}" + for i := range x.LeftJoinKeys { + l := x.LeftJoinKeys[i].String() + r := x.RightJoinKeys[i].String() + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *LogicalApply, *PhysicalApply: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + str = "Apply{" + strings.Join(children, "->") + "}" + case *LogicalMaxOneRow, *PhysicalMaxOneRow: + str = "MaxOneRow" + case *LogicalLimit, *PhysicalLimit: + str = "Limit" + case *PhysicalLock, *LogicalLock: + str = "Lock" + case *ShowDDL: + str = "ShowDDL" + case *LogicalShow: + str = "Show" + if pl := in.(*LogicalShow); pl.Extractor != nil { + str = str + "(" + pl.Extractor.explainInfo() + ")" + } + case *PhysicalShow: + str = "Show" + if pl := in.(*PhysicalShow); pl.Extractor != nil { + str = str + "(" + pl.Extractor.explainInfo() + ")" + } + case *LogicalShowDDLJobs, *PhysicalShowDDLJobs: + str = "ShowDDLJobs" + case *LogicalSort, *PhysicalSort: + str = "Sort" + case *LogicalJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + str = "Join{" + strings.Join(children, "->") + "}" + idxs = idxs[:last] + for _, eq := range x.EqualConditions { + l := eq.GetArgs()[0].String() + r := eq.GetArgs()[1].String() + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *LogicalUnionAll, *PhysicalUnionAll, *LogicalPartitionUnionAll: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + name := "UnionAll" + if x.TP() == plancodec.TypePartitionUnion { + name = "PartitionUnionAll" + } + str = name + "{" + strings.Join(children, "->") + "}" + idxs = idxs[:last] + case *LogicalSequence: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + name := "Sequence" + str = name + "{" + strings.Join(children, ",") + "}" + idxs = idxs[:last] + case *DataSource: + if x.isPartition { + str = fmt.Sprintf("Partition(%d)", x.physicalTableID) + } else { + if x.TableAsName != nil && x.TableAsName.L != "" { + str = fmt.Sprintf("DataScan(%s)", x.TableAsName) + } else { + str = fmt.Sprintf("DataScan(%s)", x.tableInfo.Name) + } + } + case *LogicalSelection: + str = fmt.Sprintf("Sel(%s)", x.Conditions) + case *PhysicalSelection: + str = fmt.Sprintf("Sel(%s)", x.Conditions) + case *LogicalProjection, *PhysicalProjection: + str = "Projection" + case *LogicalTopN: + str = fmt.Sprintf("TopN(%v,%d,%d)", x.ByItems, x.Offset, x.Count) + case *PhysicalTopN: + str = fmt.Sprintf("TopN(%v,%d,%d)", x.ByItems, x.Offset, x.Count) + case *LogicalTableDual, *PhysicalTableDual: + str = "Dual" + case *PhysicalHashAgg: + str = "HashAgg" + case *PhysicalStreamAgg: + str = "StreamAgg" + case *LogicalAggregation: + str = "Aggr(" + for i, aggFunc := range x.AggFuncs { + str += aggFunc.String() + if i != len(x.AggFuncs)-1 { + str += "," + } + } + str += ")" + case *PhysicalTableReader: + str = fmt.Sprintf("TableReader(%s)", ToString(x.tablePlan)) + case *PhysicalIndexReader: + str = fmt.Sprintf("IndexReader(%s)", ToString(x.indexPlan)) + case *PhysicalIndexLookUpReader: + str = fmt.Sprintf("IndexLookUp(%s, %s)", ToString(x.indexPlan), ToString(x.tablePlan)) + case *PhysicalIndexMergeReader: + str = "IndexMergeReader(PartialPlans->[" + for i, paritalPlan := range x.partialPlans { + if i > 0 { + str += ", " + } + str += ToString(paritalPlan) + } + str += "], TablePlan->" + ToString(x.tablePlan) + ")" + case *PhysicalUnionScan: + str = fmt.Sprintf("UnionScan(%s)", x.Conditions) + case *PhysicalIndexJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + str = "IndexJoin{" + strings.Join(children, "->") + "}" + for i := range x.OuterJoinKeys { + l := x.OuterJoinKeys[i] + r := x.InnerJoinKeys[i] + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *PhysicalIndexMergeJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + str = "IndexMergeJoin{" + strings.Join(children, "->") + "}" + for i := range x.OuterJoinKeys { + l := x.OuterJoinKeys[i] + r := x.InnerJoinKeys[i] + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *PhysicalIndexHashJoin: + last := len(idxs) - 1 + idx := idxs[last] + children := strs[idx:] + strs = strs[:idx] + idxs = idxs[:last] + str = "IndexHashJoin{" + strings.Join(children, "->") + "}" + for i := range x.OuterJoinKeys { + l := x.OuterJoinKeys[i] + r := x.InnerJoinKeys[i] + str += fmt.Sprintf("(%s,%s)", l, r) + } + case *Analyze: + str = "Analyze{" + var children []string + for _, idx := range x.IdxTasks { + children = append(children, fmt.Sprintf("Index(%s)", idx.IndexInfo.Name.O)) + } + for _, col := range x.ColTasks { + var colNames []string + if col.HandleCols != nil { + colNames = append(colNames, col.HandleCols.String()) + } + for _, c := range col.ColsInfo { + colNames = append(colNames, c.Name.O) + } + children = append(children, fmt.Sprintf("Table(%s)", strings.Join(colNames, ", "))) + } + str = str + strings.Join(children, ",") + "}" + case *Update: + str = fmt.Sprintf("%s->Update", ToString(x.SelectPlan)) + case *Delete: + str = fmt.Sprintf("%s->Delete", ToString(x.SelectPlan)) + case *Insert: + str = "Insert" + if x.SelectPlan != nil { + str = fmt.Sprintf("%s->Insert", ToString(x.SelectPlan)) + } + case *LogicalWindow: + buffer := bytes.NewBufferString("") + formatWindowFuncDescs(buffer, x.WindowFuncDescs, x.schema) + str = fmt.Sprintf("Window(%s)", buffer.String()) + case *PhysicalWindow: + str = fmt.Sprintf("Window(%s)", x.ExplainInfo()) + case *PhysicalShuffle: + str = fmt.Sprintf("Partition(%s)", x.ExplainInfo()) + case *PhysicalShuffleReceiverStub: + str = fmt.Sprintf("PartitionReceiverStub(%s)", x.ExplainInfo()) + case *PointGetPlan: + str = "PointGet(" + if x.IndexInfo != nil { + str += fmt.Sprintf("Index(%s.%s)%v)", x.TblInfo.Name.L, x.IndexInfo.Name.L, x.IndexValues) + } else { + str += fmt.Sprintf("Handle(%s.%s)%v)", x.TblInfo.Name.L, x.TblInfo.GetPkName().L, x.Handle) + } + case *BatchPointGetPlan: + str = "BatchPointGet(" + if x.IndexInfo != nil { + str += fmt.Sprintf("Index(%s.%s)%v)", x.TblInfo.Name.L, x.IndexInfo.Name.L, x.IndexValues) + } else { + str += fmt.Sprintf("Handle(%s.%s)%v)", x.TblInfo.Name.L, x.TblInfo.GetPkName().L, x.Handles) + } + case *PhysicalExchangeReceiver: + str = "Recv(" + for _, task := range x.Tasks { + str += fmt.Sprintf("%d, ", task.ID) + } + str += ")" + case *PhysicalExchangeSender: + str = "Send(" + for _, task := range x.TargetTasks { + str += fmt.Sprintf("%d, ", task.ID) + } + for _, tasks := range x.TargetCTEReaderTasks { + str += "(" + for _, task := range tasks { + str += fmt.Sprintf("%d, ", task.ID) + } + str += ")" + } + str += ")" + case *PhysicalCTE: + str = "CTEReader(" + str += fmt.Sprintf("%v", x.CTE.IDForStorage) + str += ")" + default: + str = fmt.Sprintf("%T", in) + } + strs = append(strs, str) + return strs, idxs +} diff --git a/pkg/planner/core/stringer_test.go b/pkg/planner/core/stringer_test.go new file mode 100644 index 0000000000000..09e5b8dcebb6b --- /dev/null +++ b/pkg/planner/core/stringer_test.go @@ -0,0 +1,127 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package core_test + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" + "github.com/stretchr/testify/require" +) + +func TestPlanStringer(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int, index idx(a))") + tests := []struct { + sql string + plan string + }{ + { + sql: "show columns from t like 'a'", + plan: "Show(field:[a])", + }, + { + sql: "show columns from t like 'a%'", + plan: "Show(field_pattern:[a%])", + }, + { + sql: "show columns from t where field = 'a'", + plan: "Show->Sel([eq(Column#13, a)])->Projection", + }, + { + sql: "desc t", + plan: "Show", + }, + { + sql: "desc t a", + plan: "Show(field:[a])", + }, + { + sql: "show tables in test like 't'", + plan: "Show(table:[t])", + }, + { + sql: "show tables in test like 'T'", + plan: "Show(table:[t])", + }, + { + sql: "show tables in test like 't%'", + plan: "Show(table_pattern:[t%])", + }, + { + sql: "show tables in test like '%T%'", + plan: "Show(table_pattern:[%t%])", + }, + { + sql: "show databases like 't'", + plan: "Show(database:[t])", + }, + { + sql: "show databases like 'T'", + plan: "Show(database:[t])", + }, + { + sql: "show databases like 't%'", + plan: "Show(database_pattern:[t%])", + }, + { + sql: "show databases like '%T%'", + plan: "Show(database_pattern:[%t%])", + }, + { + sql: "show table status in test like 'T%'", + plan: "Show(table_pattern:[t%])", + }, + { + sql: "show table status in test like '%T%'", + plan: "Show(table_pattern:[%t%])", + }, + { + sql: "show collation like 't'", + plan: "Show(collation:[t])", + }, + { + sql: "show collation like 'T'", + plan: "Show(collation:[t])", + }, + { + sql: "show collation like 't%'", + plan: "Show(collation_pattern:[t%])", + }, + { + sql: "show collation like '%T%'", + plan: "Show(collation_pattern:[%t%])", + }, + } + parser := parser.New() + for _, tt := range tests { + stmt, err := parser.ParseOneStmt(tt.sql, "", "") + require.NoError(t, err, "for %s", tt.sql) + ret := &core.PreprocessorReturn{} + builder, _ := core.NewPlanBuilder().Init(tk.Session(), ret.InfoSchema, &hint.BlockHintProcessor{}) + p, err := builder.Build(context.TODO(), stmt) + require.NoError(t, err, "for %s", tt.sql) + p, err = core.LogicalOptimize(context.TODO(), builder.GetOptFlag(), p.(core.LogicalPlan)) + require.NoError(t, err, "for %s", tt.sql) + require.Equal(t, tt.plan, core.ToString(p)) + } +} diff --git a/pkg/planner/core/task.go b/pkg/planner/core/task.go new file mode 100644 index 0000000000000..72537f6be8783 --- /dev/null +++ b/pkg/planner/core/task.go @@ -0,0 +1,2747 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core + +import ( + "math" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + "github.com/pingcap/tidb/pkg/planner/property" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +var ( + _ task = &copTask{} + _ task = &rootTask{} + _ task = &mppTask{} +) + +// task is a new version of `PhysicalPlanInfo`. It stores cost information for a task. +// A task may be CopTask, RootTask, MPPTaskMeta or a ParallelTask. +type task interface { + count() float64 + copy() task + plan() PhysicalPlan + invalid() bool + convertToRootTask(ctx sessionctx.Context) *rootTask + MemoryUsage() int64 +} + +// copTask is a task that runs in a distributed kv store. +// TODO: In future, we should split copTask to indexTask and tableTask. +type copTask struct { + indexPlan PhysicalPlan + tablePlan PhysicalPlan + // indexPlanFinished means we have finished index plan. + indexPlanFinished bool + // keepOrder indicates if the plan scans data by order. + keepOrder bool + // needExtraProj means an extra prune is needed because + // in double read / index merge cases, they may output one more column for handle(row id). + needExtraProj bool + // originSchema is the target schema to be projected to when needExtraProj is true. + originSchema *expression.Schema + + extraHandleCol *expression.Column + commonHandleCols []*expression.Column + // tblColHists stores the original stats of DataSource, it is used to get + // average row width when computing network cost. + tblColHists *statistics.HistColl + // tblCols stores the original columns of DataSource before being pruned, it + // is used to compute average row width when computing scan cost. + tblCols []*expression.Column + + idxMergePartPlans []PhysicalPlan + idxMergeIsIntersection bool + idxMergeAccessMVIndex bool + + // rootTaskConds stores select conditions containing virtual columns. + // These conditions can't push to TiKV, so we have to add a selection for rootTask + rootTaskConds []expression.Expression + + // For table partition. + partitionInfo PartitionInfo + + // expectCnt is the expected row count of upper task, 0 for unlimited. + // It's used for deciding whether using paging distsql. + expectCnt uint64 +} + +func (t *copTask) invalid() bool { + return t.tablePlan == nil && t.indexPlan == nil +} + +func (t *rootTask) invalid() bool { + return t.p == nil +} + +func (t *copTask) count() float64 { + if t.indexPlanFinished { + return t.tablePlan.StatsInfo().RowCount + } + return t.indexPlan.StatsInfo().RowCount +} + +func (t *copTask) copy() task { + nt := *t + return &nt +} + +func (t *copTask) plan() PhysicalPlan { + if t.indexPlanFinished { + return t.tablePlan + } + return t.indexPlan +} + +func attachPlan2Task(p PhysicalPlan, t task) task { + switch v := t.(type) { + case *copTask: + if v.indexPlanFinished { + p.SetChildren(v.tablePlan) + v.tablePlan = p + } else { + p.SetChildren(v.indexPlan) + v.indexPlan = p + } + case *rootTask: + p.SetChildren(v.p) + v.p = p + case *mppTask: + p.SetChildren(v.p) + v.p = p + } + return t +} + +// finishIndexPlan means we no longer add plan to index plan, and compute the network cost for it. +func (t *copTask) finishIndexPlan() { + if t.indexPlanFinished { + return + } + t.indexPlanFinished = true + // index merge case is specially handled for now. + // We need a elegant way to solve the stats of index merge in this case. + if t.tablePlan != nil && t.indexPlan != nil { + ts := t.tablePlan.(*PhysicalTableScan) + originStats := ts.StatsInfo() + ts.SetStats(t.indexPlan.StatsInfo()) + if originStats != nil { + // keep the original stats version + ts.StatsInfo().StatsVersion = originStats.StatsVersion + } + } +} + +func (t *copTask) getStoreType() kv.StoreType { + if t.tablePlan == nil { + return kv.TiKV + } + tp := t.tablePlan + for len(tp.Children()) > 0 { + if len(tp.Children()) > 1 { + return kv.TiFlash + } + tp = tp.Children()[0] + } + if ts, ok := tp.(*PhysicalTableScan); ok { + return ts.StoreType + } + return kv.TiKV +} + +// MemoryUsage return the memory usage of copTask +func (t *copTask) MemoryUsage() (sum int64) { + if t == nil { + return + } + + sum = size.SizeOfInterface*(2+int64(cap(t.idxMergePartPlans)+cap(t.rootTaskConds))) + size.SizeOfBool*3 + size.SizeOfUint64 + + size.SizeOfPointer*(3+int64(cap(t.commonHandleCols)+cap(t.tblCols))) + size.SizeOfSlice*4 + t.partitionInfo.MemoryUsage() + if t.indexPlan != nil { + sum += t.indexPlan.MemoryUsage() + } + if t.tablePlan != nil { + sum += t.tablePlan.MemoryUsage() + } + if t.originSchema != nil { + sum += t.originSchema.MemoryUsage() + } + if t.extraHandleCol != nil { + sum += t.extraHandleCol.MemoryUsage() + } + + for _, col := range t.commonHandleCols { + sum += col.MemoryUsage() + } + for _, col := range t.tblCols { + sum += col.MemoryUsage() + } + for _, p := range t.idxMergePartPlans { + sum += p.MemoryUsage() + } + for _, expr := range t.rootTaskConds { + sum += expr.MemoryUsage() + } + return +} + +func (p *basePhysicalPlan) attach2Task(tasks ...task) task { + t := tasks[0].convertToRootTask(p.SCtx()) + return attachPlan2Task(p.self, t) +} + +func (p *PhysicalUnionScan) attach2Task(tasks ...task) task { + // We need to pull the projection under unionScan upon unionScan. + // Since the projection only prunes columns, it's ok the put it upon unionScan. + if sel, ok := tasks[0].plan().(*PhysicalSelection); ok { + if pj, ok := sel.children[0].(*PhysicalProjection); ok { + // Convert unionScan->selection->projection to projection->unionScan->selection. + sel.SetChildren(pj.children...) + p.SetChildren(sel) + p.SetStats(tasks[0].plan().StatsInfo()) + rt, _ := tasks[0].(*rootTask) + rt.p = p + pj.SetChildren(p) + return pj.attach2Task(tasks...) + } + } + if pj, ok := tasks[0].plan().(*PhysicalProjection); ok { + // Convert unionScan->projection to projection->unionScan, because unionScan can't handle projection as its children. + p.SetChildren(pj.children...) + p.SetStats(tasks[0].plan().StatsInfo()) + rt, _ := tasks[0].(*rootTask) + rt.p = pj.children[0] + pj.SetChildren(p) + return pj.attach2Task(p.basePhysicalPlan.attach2Task(tasks...)) + } + p.SetStats(tasks[0].plan().StatsInfo()) + return p.basePhysicalPlan.attach2Task(tasks...) +} + +func (p *PhysicalApply) attach2Task(tasks ...task) task { + lTask := tasks[0].convertToRootTask(p.SCtx()) + rTask := tasks[1].convertToRootTask(p.SCtx()) + p.SetChildren(lTask.plan(), rTask.plan()) + p.schema = BuildPhysicalJoinSchema(p.JoinType, p) + t := &rootTask{ + p: p, + } + return t +} + +func (p *PhysicalIndexMergeJoin) attach2Task(tasks ...task) task { + innerTask := p.innerTask + outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) + if p.InnerChildIdx == 1 { + p.SetChildren(outerTask.plan(), innerTask.plan()) + } else { + p.SetChildren(innerTask.plan(), outerTask.plan()) + } + t := &rootTask{ + p: p, + } + return t +} + +func (p *PhysicalIndexHashJoin) attach2Task(tasks ...task) task { + innerTask := p.innerTask + outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) + if p.InnerChildIdx == 1 { + p.SetChildren(outerTask.plan(), innerTask.plan()) + } else { + p.SetChildren(innerTask.plan(), outerTask.plan()) + } + t := &rootTask{ + p: p, + } + return t +} + +func (p *PhysicalIndexJoin) attach2Task(tasks ...task) task { + innerTask := p.innerTask + outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) + if p.InnerChildIdx == 1 { + p.SetChildren(outerTask.plan(), innerTask.plan()) + } else { + p.SetChildren(innerTask.plan(), outerTask.plan()) + } + t := &rootTask{ + p: p, + } + return t +} + +// RowSize for cost model ver2 is simplified, always use this function to calculate row size. +func getAvgRowSize(stats *property.StatsInfo, cols []*expression.Column) (size float64) { + if stats.HistColl != nil { + size = cardinality.GetAvgRowSizeListInDisk(stats.HistColl, cols) + } else { + // Estimate using just the type info. + for _, col := range cols { + size += float64(chunk.EstimateTypeWidth(col.GetType())) + } + } + return +} + +func (p *PhysicalHashJoin) attach2Task(tasks ...task) task { + if p.storeTp == kv.TiFlash { + return p.attach2TaskForTiFlash(tasks...) + } + lTask := tasks[0].convertToRootTask(p.SCtx()) + rTask := tasks[1].convertToRootTask(p.SCtx()) + p.SetChildren(lTask.plan(), rTask.plan()) + task := &rootTask{ + p: p, + } + return task +} + +// TiDB only require that the types fall into the same catalog but TiFlash require the type to be exactly the same, so +// need to check if the conversion is a must +func needConvert(tp *types.FieldType, rtp *types.FieldType) bool { + // all the string type are mapped to the same type in TiFlash, so + // do not need convert for string types + if types.IsString(tp.GetType()) && types.IsString(rtp.GetType()) { + return false + } + if tp.GetType() != rtp.GetType() { + return true + } + if tp.GetType() != mysql.TypeNewDecimal { + return false + } + if tp.GetDecimal() != rtp.GetDecimal() { + return true + } + // for decimal type, TiFlash have 4 different impl based on the required precision + if tp.GetFlen() >= 0 && tp.GetFlen() <= 9 && rtp.GetFlen() >= 0 && rtp.GetFlen() <= 9 { + return false + } + if tp.GetFlen() > 9 && tp.GetFlen() <= 18 && rtp.GetFlen() > 9 && rtp.GetFlen() <= 18 { + return false + } + if tp.GetFlen() > 18 && tp.GetFlen() <= 38 && rtp.GetFlen() > 18 && rtp.GetFlen() <= 38 { + return false + } + if tp.GetFlen() > 38 && tp.GetFlen() <= 65 && rtp.GetFlen() > 38 && rtp.GetFlen() <= 65 { + return false + } + return true +} + +func negotiateCommonType(lType, rType *types.FieldType) (*types.FieldType, bool, bool) { + commonType := types.AggFieldType([]*types.FieldType{lType, rType}) + if commonType.GetType() == mysql.TypeNewDecimal { + lExtend := 0 + rExtend := 0 + cDec := rType.GetDecimal() + if lType.GetDecimal() < rType.GetDecimal() { + lExtend = rType.GetDecimal() - lType.GetDecimal() + } else if lType.GetDecimal() > rType.GetDecimal() { + rExtend = lType.GetDecimal() - rType.GetDecimal() + cDec = lType.GetDecimal() + } + lLen, rLen := lType.GetFlen()+lExtend, rType.GetFlen()+rExtend + cLen := mathutil.Max(lLen, rLen) + commonType.SetDecimalUnderLimit(cDec) + commonType.SetFlenUnderLimit(cLen) + } else if needConvert(lType, commonType) || needConvert(rType, commonType) { + if mysql.IsIntegerType(commonType.GetType()) { + // If the target type is int, both TiFlash and Mysql only support cast to Int64 + // so we need to promote the type to Int64 + commonType.SetType(mysql.TypeLonglong) + commonType.SetFlen(mysql.MaxIntWidth) + } + } + return commonType, needConvert(lType, commonType), needConvert(rType, commonType) +} + +func getProj(ctx sessionctx.Context, p PhysicalPlan) *PhysicalProjection { + proj := PhysicalProjection{ + Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)), + }.Init(ctx, p.StatsInfo(), p.SelectBlockOffset()) + for _, col := range p.Schema().Columns { + proj.Exprs = append(proj.Exprs, col) + } + proj.SetSchema(p.Schema().Clone()) + proj.SetChildren(p) + return proj +} + +func appendExpr(p *PhysicalProjection, expr expression.Expression) *expression.Column { + p.Exprs = append(p.Exprs, expr) + + col := &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: expr.GetType(), + } + col.SetCoercibility(expr.Coercibility()) + p.schema.Append(col) + return col +} + +// TiFlash join require that partition key has exactly the same type, while TiDB only guarantee the partition key is the same catalog, +// so if the partition key type is not exactly the same, we need add a projection below the join or exchanger if exists. +func (p *PhysicalHashJoin) convertPartitionKeysIfNeed(lTask, rTask *mppTask) (*mppTask, *mppTask) { + lp := lTask.p + if _, ok := lp.(*PhysicalExchangeReceiver); ok { + lp = lp.Children()[0].Children()[0] + } + rp := rTask.p + if _, ok := rp.(*PhysicalExchangeReceiver); ok { + rp = rp.Children()[0].Children()[0] + } + // to mark if any partition key needs to convert + lMask := make([]bool, len(lTask.hashCols)) + rMask := make([]bool, len(rTask.hashCols)) + cTypes := make([]*types.FieldType, len(lTask.hashCols)) + lChanged := false + rChanged := false + for i := range lTask.hashCols { + lKey := lTask.hashCols[i] + rKey := rTask.hashCols[i] + cType, lConvert, rConvert := negotiateCommonType(lKey.Col.RetType, rKey.Col.RetType) + if lConvert { + lMask[i] = true + cTypes[i] = cType + lChanged = true + } + if rConvert { + rMask[i] = true + cTypes[i] = cType + rChanged = true + } + } + if !lChanged && !rChanged { + return lTask, rTask + } + var lProj, rProj *PhysicalProjection + if lChanged { + lProj = getProj(p.SCtx(), lp) + lp = lProj + } + if rChanged { + rProj = getProj(p.SCtx(), rp) + rp = rProj + } + + lPartKeys := make([]*property.MPPPartitionColumn, 0, len(rTask.hashCols)) + rPartKeys := make([]*property.MPPPartitionColumn, 0, len(lTask.hashCols)) + for i := range lTask.hashCols { + lKey := lTask.hashCols[i] + rKey := rTask.hashCols[i] + if lMask[i] { + cType := cTypes[i].Clone() + cType.SetFlag(lKey.Col.RetType.GetFlag()) + lCast := expression.BuildCastFunction(p.SCtx(), lKey.Col, cType) + lKey = &property.MPPPartitionColumn{Col: appendExpr(lProj, lCast), CollateID: lKey.CollateID} + } + if rMask[i] { + cType := cTypes[i].Clone() + cType.SetFlag(rKey.Col.RetType.GetFlag()) + rCast := expression.BuildCastFunction(p.SCtx(), rKey.Col, cType) + rKey = &property.MPPPartitionColumn{Col: appendExpr(rProj, rCast), CollateID: rKey.CollateID} + } + lPartKeys = append(lPartKeys, lKey) + rPartKeys = append(rPartKeys, rKey) + } + // if left or right child changes, we need to add enforcer. + if lChanged { + nlTask := lTask.copy().(*mppTask) + nlTask.p = lProj + nlTask = nlTask.enforceExchanger(&property.PhysicalProperty{ + TaskTp: property.MppTaskType, + MPPPartitionTp: property.HashType, + MPPPartitionCols: lPartKeys, + }) + lTask = nlTask + } + if rChanged { + nrTask := rTask.copy().(*mppTask) + nrTask.p = rProj + nrTask = nrTask.enforceExchanger(&property.PhysicalProperty{ + TaskTp: property.MppTaskType, + MPPPartitionTp: property.HashType, + MPPPartitionCols: rPartKeys, + }) + rTask = nrTask + } + return lTask, rTask +} + +func (p *PhysicalHashJoin) attach2TaskForMpp(tasks ...task) task { + lTask, lok := tasks[0].(*mppTask) + rTask, rok := tasks[1].(*mppTask) + if !lok || !rok { + return invalidTask + } + if p.mppShuffleJoin { + // protection check is case of some bugs + if len(lTask.hashCols) != len(rTask.hashCols) || len(lTask.hashCols) == 0 { + return invalidTask + } + lTask, rTask = p.convertPartitionKeysIfNeed(lTask, rTask) + } + p.SetChildren(lTask.plan(), rTask.plan()) + p.schema = BuildPhysicalJoinSchema(p.JoinType, p) + + // outer task is the task that will pass its MPPPartitionType to the join result + // for broadcast inner join, it should be the non-broadcast side, since broadcast side is always the build side, so + // just use the probe side is ok. + // for hash inner join, both side is ok, by default, we use the probe side + // for outer join, it should always be the outer side of the join + // for semi join, it should be the left side(the same as left out join) + outerTaskIndex := 1 - p.InnerChildIdx + if p.JoinType != InnerJoin { + if p.JoinType == RightOuterJoin { + outerTaskIndex = 1 + } else { + outerTaskIndex = 0 + } + } + // can not use the task from tasks because it maybe updated. + outerTask := lTask + if outerTaskIndex == 1 { + outerTask = rTask + } + task := &mppTask{ + p: p, + partTp: outerTask.partTp, + hashCols: outerTask.hashCols, + } + return task +} + +func (p *PhysicalHashJoin) attach2TaskForTiFlash(tasks ...task) task { + lTask, lok := tasks[0].(*copTask) + rTask, rok := tasks[1].(*copTask) + if !lok || !rok { + return p.attach2TaskForMpp(tasks...) + } + p.SetChildren(lTask.plan(), rTask.plan()) + p.schema = BuildPhysicalJoinSchema(p.JoinType, p) + if !lTask.indexPlanFinished { + lTask.finishIndexPlan() + } + if !rTask.indexPlanFinished { + rTask.finishIndexPlan() + } + + task := &copTask{ + tblColHists: rTask.tblColHists, + indexPlanFinished: true, + tablePlan: p, + } + return task +} + +func (p *PhysicalMergeJoin) attach2Task(tasks ...task) task { + lTask := tasks[0].convertToRootTask(p.SCtx()) + rTask := tasks[1].convertToRootTask(p.SCtx()) + p.SetChildren(lTask.plan(), rTask.plan()) + t := &rootTask{ + p: p, + } + return t +} + +func buildIndexLookUpTask(ctx sessionctx.Context, t *copTask) *rootTask { + newTask := &rootTask{} + p := PhysicalIndexLookUpReader{ + tablePlan: t.tablePlan, + indexPlan: t.indexPlan, + ExtraHandleCol: t.extraHandleCol, + CommonHandleCols: t.commonHandleCols, + expectedCnt: t.expectCnt, + keepOrder: t.keepOrder, + }.Init(ctx, t.tablePlan.SelectBlockOffset()) + p.PartitionInfo = t.partitionInfo + setTableScanToTableRowIDScan(p.tablePlan) + p.SetStats(t.tablePlan.StatsInfo()) + // Do not inject the extra Projection even if t.needExtraProj is set, or the schema between the phase-1 agg and + // the final agg would be broken. Please reference comments for the similar logic in + // (*copTask).convertToRootTaskImpl() for the PhysicalTableReader case. + // We need to refactor these logics. + aggPushedDown := false + switch p.tablePlan.(type) { + case *PhysicalHashAgg, *PhysicalStreamAgg: + aggPushedDown = true + } + + if t.needExtraProj && !aggPushedDown { + schema := t.originSchema + proj := PhysicalProjection{Exprs: expression.Column2Exprs(schema.Columns)}.Init(ctx, p.StatsInfo(), t.tablePlan.SelectBlockOffset(), nil) + proj.SetSchema(schema) + proj.SetChildren(p) + newTask.p = proj + } else { + newTask.p = p + } + return newTask +} + +func extractRows(p PhysicalPlan) float64 { + f := float64(0) + for _, c := range p.Children() { + if len(c.Children()) != 0 { + f += extractRows(c) + } else { + f += c.StatsInfo().RowCount + } + } + return f +} + +// calcPagingCost calculates the cost for paging processing which may increase the seekCnt and reduce scanned rows. +func calcPagingCost(ctx sessionctx.Context, indexPlan PhysicalPlan, expectCnt uint64) float64 { + sessVars := ctx.GetSessionVars() + indexRows := indexPlan.StatsCount() + sourceRows := extractRows(indexPlan) + // with paging, the scanned rows is always less than or equal to source rows. + if uint64(sourceRows) < expectCnt { + expectCnt = uint64(sourceRows) + } + seekCnt := paging.CalculateSeekCnt(expectCnt) + indexSelectivity := float64(1) + if sourceRows > indexRows { + indexSelectivity = indexRows / sourceRows + } + pagingCst := seekCnt*sessVars.GetSeekFactor(nil) + float64(expectCnt)*sessVars.GetCPUFactor() + pagingCst *= indexSelectivity + + // we want the diff between idxCst and pagingCst here, + // however, the idxCst does not contain seekFactor, so a seekFactor needs to be removed + return math.Max(pagingCst-sessVars.GetSeekFactor(nil), 0) +} + +func (t *rootTask) convertToRootTask(_ sessionctx.Context) *rootTask { + return t.copy().(*rootTask) +} + +func (t *copTask) convertToRootTask(ctx sessionctx.Context) *rootTask { + // copy one to avoid changing itself. + return t.copy().(*copTask).convertToRootTaskImpl(ctx) +} + +func (t *copTask) convertToRootTaskImpl(ctx sessionctx.Context) *rootTask { + // copTasks are run in parallel, to make the estimated cost closer to execution time, we amortize + // the cost to cop iterator workers. According to `CopClient::Send`, the concurrency + // is Min(DistSQLScanConcurrency, numRegionsInvolvedInScan), since we cannot infer + // the number of regions involved, we simply use DistSQLScanConcurrency. + t.finishIndexPlan() + // Network cost of transferring rows of table scan to TiDB. + if t.tablePlan != nil { + tp := t.tablePlan + for len(tp.Children()) > 0 { + if len(tp.Children()) == 1 { + tp = tp.Children()[0] + } else { + join := tp.(*PhysicalHashJoin) + tp = join.children[1-join.InnerChildIdx] + } + } + ts := tp.(*PhysicalTableScan) + prevColumnLen := len(ts.Columns) + prevSchema := ts.schema.Clone() + ts.Columns = ExpandVirtualColumn(ts.Columns, ts.schema, ts.Table.Columns) + if !t.needExtraProj && len(ts.Columns) > prevColumnLen { + // Add an projection to make sure not to output extract columns. + t.needExtraProj = true + t.originSchema = prevSchema + } + } + newTask := &rootTask{} + if t.idxMergePartPlans != nil { + p := PhysicalIndexMergeReader{ + partialPlans: t.idxMergePartPlans, + tablePlan: t.tablePlan, + IsIntersectionType: t.idxMergeIsIntersection, + AccessMVIndex: t.idxMergeAccessMVIndex, + KeepOrder: t.keepOrder, + }.Init(ctx, t.idxMergePartPlans[0].SelectBlockOffset()) + p.PartitionInfo = t.partitionInfo + setTableScanToTableRowIDScan(p.tablePlan) + newTask.p = p + t.handleRootTaskConds(ctx, newTask) + if t.needExtraProj { + schema := t.originSchema + proj := PhysicalProjection{Exprs: expression.Column2Exprs(schema.Columns)}.Init(ctx, p.StatsInfo(), t.idxMergePartPlans[0].SelectBlockOffset(), nil) + proj.SetSchema(schema) + proj.SetChildren(p) + newTask.p = proj + } + return newTask + } + if t.indexPlan != nil && t.tablePlan != nil { + newTask = buildIndexLookUpTask(ctx, t) + } else if t.indexPlan != nil { + p := PhysicalIndexReader{indexPlan: t.indexPlan}.Init(ctx, t.indexPlan.SelectBlockOffset()) + p.PartitionInfo = t.partitionInfo + p.SetStats(t.indexPlan.StatsInfo()) + newTask.p = p + } else { + tp := t.tablePlan + for len(tp.Children()) > 0 { + if len(tp.Children()) == 1 { + tp = tp.Children()[0] + } else { + join := tp.(*PhysicalHashJoin) + tp = join.children[1-join.InnerChildIdx] + } + } + ts := tp.(*PhysicalTableScan) + p := PhysicalTableReader{ + tablePlan: t.tablePlan, + StoreType: ts.StoreType, + IsCommonHandle: ts.Table.IsCommonHandle, + }.Init(ctx, t.tablePlan.SelectBlockOffset()) + p.PartitionInfo = t.partitionInfo + p.SetStats(t.tablePlan.StatsInfo()) + + // If agg was pushed down in attach2Task(), the partial agg was placed on the top of tablePlan, the final agg was + // placed above the PhysicalTableReader, and the schema should have been set correctly for them, the schema of + // partial agg contains the columns needed by the final agg. + // If we add the projection here, the projection will be between the final agg and the partial agg, then the + // schema will be broken, the final agg will fail to find needed columns in ResolveIndices(). + // Besides, the agg would only be pushed down if it doesn't contain virtual columns, so virtual column should not be affected. + aggPushedDown := false + switch p.tablePlan.(type) { + case *PhysicalHashAgg, *PhysicalStreamAgg: + aggPushedDown = true + } + + if t.needExtraProj && !aggPushedDown { + proj := PhysicalProjection{Exprs: expression.Column2Exprs(t.originSchema.Columns)}.Init(ts.SCtx(), ts.StatsInfo(), ts.SelectBlockOffset(), nil) + proj.SetSchema(t.originSchema) + proj.SetChildren(p) + newTask.p = proj + } else { + newTask.p = p + } + } + + t.handleRootTaskConds(ctx, newTask) + return newTask +} + +func (t *copTask) handleRootTaskConds(ctx sessionctx.Context, newTask *rootTask) { + if len(t.rootTaskConds) > 0 { + selectivity, _, err := cardinality.Selectivity(ctx, t.tblColHists, t.rootTaskConds, nil) + if err != nil { + logutil.BgLogger().Debug("calculate selectivity failed, use selection factor", zap.Error(err)) + selectivity = SelectionFactor + } + sel := PhysicalSelection{Conditions: t.rootTaskConds}.Init(ctx, newTask.p.StatsInfo().Scale(selectivity), newTask.p.SelectBlockOffset()) + sel.fromDataSource = true + sel.SetChildren(newTask.p) + newTask.p = sel + } +} + +// setTableScanToTableRowIDScan is to update the isChildOfIndexLookUp attribute of PhysicalTableScan child +func setTableScanToTableRowIDScan(p PhysicalPlan) { + if ts, ok := p.(*PhysicalTableScan); ok { + ts.SetIsChildOfIndexLookUp(true) + } else { + for _, child := range p.Children() { + setTableScanToTableRowIDScan(child) + } + } +} + +// rootTask is the final sink node of a plan graph. It should be a single goroutine on tidb. +type rootTask struct { + p PhysicalPlan + isEmpty bool // isEmpty indicates if this task contains a dual table and returns empty data. + // TODO: The flag 'isEmpty' is only checked by Projection and UnionAll. We should support more cases in the future. +} + +func (t *rootTask) copy() task { + return &rootTask{ + p: t.p, + } +} + +func (t *rootTask) count() float64 { + return t.p.StatsInfo().RowCount +} + +func (t *rootTask) plan() PhysicalPlan { + return t.p +} + +// MemoryUsage return the memory usage of rootTask +func (t *rootTask) MemoryUsage() (sum int64) { + if t == nil { + return + } + + sum = size.SizeOfInterface + size.SizeOfBool + if t.p != nil { + sum += t.p.MemoryUsage() + } + return sum +} + +// attach2Task attach limit to different cases. +// For Normal Index Lookup +// 1: attach the limit to table side or index side of normal index lookup cop task. (normal case, old code, no more +// explanation here) +// +// For Index Merge: +// 2: attach the limit to **table** side for index merge intersection case, cause intersection will invalidate the +// fetched limit+offset rows from each partial index plan, you can not decide how many you want in advance for partial +// index path, actually. After we sink limit to table side, we still need an upper root limit to control the real limit +// count admission. +// +// 3: attach the limit to **index** side for index merge union case, because each index plan will output the fetched +// limit+offset (* N path) rows, you still need an embedded pushedLimit inside index merge reader to cut it down. +// +// 4: attach the limit to the TOP of root index merge operator if there is some root condition exists for index merge +// intersection/union case. +func (p *PhysicalLimit) attach2Task(tasks ...task) task { + t := tasks[0].copy() + newPartitionBy := make([]property.SortItem, 0, len(p.GetPartitionBy())) + for _, expr := range p.GetPartitionBy() { + newPartitionBy = append(newPartitionBy, expr.Clone()) + } + + sunk := false + if cop, ok := t.(*copTask); ok { + suspendLimitAboveTablePlan := func() { + newCount := p.Offset + p.Count + childProfile := cop.tablePlan.StatsInfo() + // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. + stats := deriveLimitStats(childProfile, float64(newCount)) + pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) + pushedDownLimit.SetChildren(cop.tablePlan) + cop.tablePlan = pushedDownLimit + // Don't use clone() so that Limit and its children share the same schema. Otherwise, the virtual generated column may not be resolved right. + pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) + t = cop.convertToRootTask(p.SCtx()) + } + if len(cop.idxMergePartPlans) == 0 { + // For double read which requires order being kept, the limit cannot be pushed down to the table side, + // because handles would be reordered before being sent to table scan. + if (!cop.keepOrder || !cop.indexPlanFinished || cop.indexPlan == nil) && len(cop.rootTaskConds) == 0 { + // When limit is pushed down, we should remove its offset. + newCount := p.Offset + p.Count + childProfile := cop.plan().StatsInfo() + // Strictly speaking, for the row count of stats, we should multiply newCount with "regionNum", + // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. + stats := deriveLimitStats(childProfile, float64(newCount)) + pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) + cop = attachPlan2Task(pushedDownLimit, cop).(*copTask) + // Don't use clone() so that Limit and its children share the same schema. Otherwise the virtual generated column may not be resolved right. + pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) + } + t = cop.convertToRootTask(p.SCtx()) + sunk = p.sinkIntoIndexLookUp(t) + } else if !cop.idxMergeIsIntersection { + // We only support push part of the order prop down to index merge build case. + if len(cop.rootTaskConds) == 0 { + if cop.indexPlanFinished { + // when the index plan is finished, sink the limit to the index merge table side. + suspendLimitAboveTablePlan() + } else { + // cop.indexPlanFinished = false indicates the table side is a pure table-scan, sink the limit to the index merge index side. + newCount := p.Offset + p.Count + limitChildren := make([]PhysicalPlan, 0, len(cop.idxMergePartPlans)) + for _, partialScan := range cop.idxMergePartPlans { + childProfile := partialScan.StatsInfo() + stats := deriveLimitStats(childProfile, float64(newCount)) + pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) + pushedDownLimit.SetChildren(partialScan) + pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) + limitChildren = append(limitChildren, pushedDownLimit) + } + cop.idxMergePartPlans = limitChildren + t = cop.convertToRootTask(p.SCtx()) + sunk = p.sinkIntoIndexMerge(t) + } + } else { + // when there are some root conditions, just sink the limit upon the index merge reader. + t = cop.convertToRootTask(p.SCtx()) + sunk = p.sinkIntoIndexMerge(t) + } + } else if cop.idxMergeIsIntersection { + // In the index merge with intersection case, only the limit can be pushed down to the index merge table side. + // Note Difference: + // IndexMerge.PushedLimit is applied before table scan fetching, limiting the indexPartialPlan rows returned (it maybe ordered if orderBy items not empty) + // TableProbeSide sink limit is applied on the top of table plan, which will quickly shut down the both fetch-back and read-back process. + if len(cop.rootTaskConds) == 0 { + if cop.indexPlanFinished { + // indicates the table side is not a pure table-scan, so we could only append the limit upon the table plan. + suspendLimitAboveTablePlan() + } else { + t = cop.convertToRootTask(p.SCtx()) + sunk = p.sinkIntoIndexMerge(t) + } + } else { + // otherwise, suspend the limit out of index merge reader. + t = cop.convertToRootTask(p.SCtx()) + sunk = p.sinkIntoIndexMerge(t) + } + } else { + // Whatever the remained case is, we directly convert to it to root task. + t = cop.convertToRootTask(p.SCtx()) + } + } else if mpp, ok := t.(*mppTask); ok { + newCount := p.Offset + p.Count + childProfile := mpp.plan().StatsInfo() + stats := deriveLimitStats(childProfile, float64(newCount)) + pushedDownLimit := PhysicalLimit{Count: newCount, PartitionBy: newPartitionBy}.Init(p.SCtx(), stats, p.SelectBlockOffset()) + mpp = attachPlan2Task(pushedDownLimit, mpp).(*mppTask) + pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) + t = mpp.convertToRootTask(p.SCtx()) + } + if sunk { + return t + } + // Skip limit with partition on the root. This is a derived topN and window function + // will take care of the filter. + if len(p.GetPartitionBy()) > 0 { + return t + } + return attachPlan2Task(p, t) +} + +func (p *PhysicalLimit) sinkIntoIndexLookUp(t task) bool { + root := t.(*rootTask) + reader, isDoubleRead := root.p.(*PhysicalIndexLookUpReader) + proj, isProj := root.p.(*PhysicalProjection) + if !isDoubleRead && !isProj { + return false + } + if isProj { + reader, isDoubleRead = proj.Children()[0].(*PhysicalIndexLookUpReader) + if !isDoubleRead { + return false + } + } + + // We can sink Limit into IndexLookUpReader only if tablePlan contains no Selection. + ts, isTableScan := reader.tablePlan.(*PhysicalTableScan) + if !isTableScan { + return false + } + + // If this happens, some Projection Operator must be inlined into this Limit. (issues/14428) + // For example, if the original plan is `IndexLookUp(col1, col2) -> Limit(col1, col2) -> Project(col1)`, + // then after inlining the Project, it will be `IndexLookUp(col1, col2) -> Limit(col1)` here. + // If the Limit is sunk into the IndexLookUp, the IndexLookUp's schema needs to be updated as well, + // So we add an extra projection to solve the problem. + if p.Schema().Len() != reader.Schema().Len() { + extraProj := PhysicalProjection{ + Exprs: expression.Column2Exprs(p.schema.Columns), + }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), nil) + extraProj.SetSchema(p.schema) + // If the root.p is already a Projection. We left the optimization for the later Projection Elimination. + extraProj.SetChildren(root.p) + root.p = extraProj + } + + reader.PushedLimit = &PushedDownLimit{ + Offset: p.Offset, + Count: p.Count, + } + originStats := ts.StatsInfo() + ts.SetStats(p.StatsInfo()) + if originStats != nil { + // keep the original stats version + ts.StatsInfo().StatsVersion = originStats.StatsVersion + } + reader.SetStats(p.StatsInfo()) + if isProj { + proj.SetStats(p.StatsInfo()) + } + return true +} + +func (p *PhysicalLimit) sinkIntoIndexMerge(t task) bool { + root := t.(*rootTask) + imReader, isIm := root.p.(*PhysicalIndexMergeReader) + proj, isProj := root.p.(*PhysicalProjection) + if !isIm && !isProj { + return false + } + if isProj { + imReader, isIm = proj.Children()[0].(*PhysicalIndexMergeReader) + if !isIm { + return false + } + } + ts, ok := imReader.tablePlan.(*PhysicalTableScan) + if !ok { + return false + } + imReader.PushedLimit = &PushedDownLimit{ + Count: p.Count, + Offset: p.Offset, + } + // since ts.statsInfo.rowcount may dramatically smaller than limit.statsInfo. + // like limit: rowcount=1 + // ts: rowcount=0.0025 + originStats := ts.StatsInfo() + if originStats != nil { + // keep the original stats version + ts.StatsInfo().StatsVersion = originStats.StatsVersion + if originStats.RowCount < p.StatsInfo().RowCount { + ts.StatsInfo().RowCount = originStats.RowCount + } + } + needProj := p.schema.Len() != root.p.Schema().Len() + if !needProj { + for i := 0; i < p.schema.Len(); i++ { + if !p.schema.Columns[i].Equal(nil, root.p.Schema().Columns[i]) { + needProj = true + break + } + } + } + if needProj { + extraProj := PhysicalProjection{ + Exprs: expression.Column2Exprs(p.schema.Columns), + }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), nil) + extraProj.SetSchema(p.schema) + // If the root.p is already a Projection. We left the optimization for the later Projection Elimination. + extraProj.SetChildren(root.p) + root.p = extraProj + } + return true +} + +func (p *PhysicalSort) attach2Task(tasks ...task) task { + t := tasks[0].copy() + t = attachPlan2Task(p, t) + return t +} + +func (p *NominalSort) attach2Task(tasks ...task) task { + if p.OnlyColumn { + return tasks[0] + } + t := tasks[0].copy() + t = attachPlan2Task(p, t) + return t +} + +func (p *PhysicalTopN) getPushedDownTopN(childPlan PhysicalPlan) *PhysicalTopN { + newByItems := make([]*util.ByItems, 0, len(p.ByItems)) + for _, expr := range p.ByItems { + newByItems = append(newByItems, expr.Clone()) + } + newPartitionBy := make([]property.SortItem, 0, len(p.GetPartitionBy())) + for _, expr := range p.GetPartitionBy() { + newPartitionBy = append(newPartitionBy, expr.Clone()) + } + newCount := p.Offset + p.Count + childProfile := childPlan.StatsInfo() + // Strictly speaking, for the row count of pushed down TopN, we should multiply newCount with "regionNum", + // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. + stats := deriveLimitStats(childProfile, float64(newCount)) + topN := PhysicalTopN{ + ByItems: newByItems, + PartitionBy: newPartitionBy, + Count: newCount, + }.Init(p.SCtx(), stats, p.SelectBlockOffset(), p.GetChildReqProps(0)) + topN.SetChildren(childPlan) + return topN +} + +// canPushToIndexPlan checks if this TopN can be pushed to the index side of copTask. +// It can be pushed to the index side when all columns used by ByItems are available from the index side and there's no prefix index column. +func (*PhysicalTopN) canPushToIndexPlan(indexPlan PhysicalPlan, byItemCols []*expression.Column) bool { + // If we call canPushToIndexPlan and there's no index plan, we should go into the index merge case. + // Index merge case is specially handled for now. So we directly return false here. + // So we directly return false. + if indexPlan == nil { + return false + } + schema := indexPlan.Schema() + for _, col := range byItemCols { + pos := schema.ColumnIndex(col) + if pos == -1 { + return false + } + if schema.Columns[pos].IsPrefix { + return false + } + } + return true +} + +// canExpressionConvertedToPB checks whether each of the the expression in TopN can be converted to pb. +func (p *PhysicalTopN) canExpressionConvertedToPB(storeTp kv.StoreType) bool { + exprs := make([]expression.Expression, 0, len(p.ByItems)) + for _, item := range p.ByItems { + exprs = append(exprs, item.Expr) + } + return expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, exprs, p.SCtx().GetClient(), storeTp) +} + +// containVirtualColumn checks whether TopN.ByItems contains virtual generated columns. +func (p *PhysicalTopN) containVirtualColumn(tCols []*expression.Column) bool { + tColSet := make(map[int64]struct{}, len(tCols)) + for _, tCol := range tCols { + if tCol.ID > 0 && tCol.VirtualExpr != nil { + tColSet[tCol.ID] = struct{}{} + } + } + for _, by := range p.ByItems { + cols := expression.ExtractColumns(by.Expr) + for _, col := range cols { + if _, ok := tColSet[col.ID]; ok { + // A column with ID > 0 indicates that the column can be resolved by data source. + return true + } + } + } + return false +} + +// canPushDownToTiKV checks whether this topN can be pushed down to TiKV. +func (p *PhysicalTopN) canPushDownToTiKV(copTask *copTask) bool { + if !p.canExpressionConvertedToPB(kv.TiKV) { + return false + } + if len(copTask.rootTaskConds) != 0 { + return false + } + if !copTask.indexPlanFinished && len(copTask.idxMergePartPlans) > 0 { + for _, partialPlan := range copTask.idxMergePartPlans { + if p.containVirtualColumn(partialPlan.Schema().Columns) { + return false + } + } + } else if p.containVirtualColumn(copTask.plan().Schema().Columns) { + return false + } + return true +} + +// canPushDownToTiFlash checks whether this topN can be pushed down to TiFlash. +func (p *PhysicalTopN) canPushDownToTiFlash(mppTask *mppTask) bool { + if !p.canExpressionConvertedToPB(kv.TiFlash) { + return false + } + if p.containVirtualColumn(mppTask.plan().Schema().Columns) { + return false + } + return true +} + +func (p *PhysicalTopN) attach2Task(tasks ...task) task { + t := tasks[0].copy() + cols := make([]*expression.Column, 0, len(p.ByItems)) + for _, item := range p.ByItems { + cols = append(cols, expression.ExtractColumns(item.Expr)...) + } + needPushDown := len(cols) > 0 + if copTask, ok := t.(*copTask); ok && needPushDown && p.canPushDownToTiKV(copTask) && len(copTask.rootTaskConds) == 0 { + // If all columns in topN are from index plan, we push it to index plan, otherwise we finish the index plan and + // push it to table plan. + var pushedDownTopN *PhysicalTopN + if !copTask.indexPlanFinished && p.canPushToIndexPlan(copTask.indexPlan, cols) { + pushedDownTopN = p.getPushedDownTopN(copTask.indexPlan) + copTask.indexPlan = pushedDownTopN + } else { + // It works for both normal index scan and index merge scan. + copTask.finishIndexPlan() + pushedDownTopN = p.getPushedDownTopN(copTask.tablePlan) + copTask.tablePlan = pushedDownTopN + } + } else if mppTask, ok := t.(*mppTask); ok && needPushDown && p.canPushDownToTiFlash(mppTask) { + pushedDownTopN := p.getPushedDownTopN(mppTask.p) + mppTask.p = pushedDownTopN + } + rootTask := t.convertToRootTask(p.SCtx()) + // Skip TopN with partition on the root. This is a derived topN and window function + // will take care of the filter. + if len(p.GetPartitionBy()) > 0 { + return t + } + return attachPlan2Task(p, rootTask) +} + +func (p *PhysicalExpand) attach2Task(tasks ...task) task { + t := tasks[0].copy() + // current expand can only be run in MPP TiFlash mode. + if mpp, ok := t.(*mppTask); ok { + p.SetChildren(mpp.p) + mpp.p = p + return mpp + } + return invalidTask +} + +func (p *PhysicalProjection) attach2Task(tasks ...task) task { + t := tasks[0].copy() + if cop, ok := t.(*copTask); ok { + if (len(cop.rootTaskConds) == 0 && len(cop.idxMergePartPlans) == 0) && expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, p.Exprs, p.SCtx().GetClient(), cop.getStoreType()) { + copTask := attachPlan2Task(p, cop) + return copTask + } + } else if mpp, ok := t.(*mppTask); ok { + if expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, p.Exprs, p.SCtx().GetClient(), kv.TiFlash) { + p.SetChildren(mpp.p) + mpp.p = p + return mpp + } + } + t = t.convertToRootTask(p.SCtx()) + t = attachPlan2Task(p, t) + if root, ok := tasks[0].(*rootTask); ok && root.isEmpty { + t.(*rootTask).isEmpty = true + } + return t +} + +func (p *PhysicalUnionAll) attach2MppTasks(tasks ...task) task { + t := &mppTask{p: p} + childPlans := make([]PhysicalPlan, 0, len(tasks)) + for _, tk := range tasks { + if mpp, ok := tk.(*mppTask); ok && !tk.invalid() { + childPlans = append(childPlans, mpp.plan()) + } else if root, ok := tk.(*rootTask); ok && root.isEmpty { + continue + } else { + return invalidTask + } + } + if len(childPlans) == 0 { + return invalidTask + } + p.SetChildren(childPlans...) + return t +} + +func (p *PhysicalUnionAll) attach2Task(tasks ...task) task { + for _, t := range tasks { + if _, ok := t.(*mppTask); ok { + if p.TP() == plancodec.TypePartitionUnion { + // In attach2MppTasks(), will attach PhysicalUnion to mppTask directly. + // But PartitionUnion cannot pushdown to tiflash, so here disable PartitionUnion pushdown to tiflash explicitly. + // For now, return invalidTask immediately, we can refine this by letting childTask of PartitionUnion convert to rootTask. + return invalidTask + } + return p.attach2MppTasks(tasks...) + } + } + t := &rootTask{p: p} + childPlans := make([]PhysicalPlan, 0, len(tasks)) + for _, task := range tasks { + task = task.convertToRootTask(p.SCtx()) + childPlans = append(childPlans, task.plan()) + } + p.SetChildren(childPlans...) + return t +} + +func (sel *PhysicalSelection) attach2Task(tasks ...task) task { + if mppTask, _ := tasks[0].(*mppTask); mppTask != nil { // always push to mpp task. + sc := sel.SCtx().GetSessionVars().StmtCtx + if expression.CanExprsPushDown(sc, sel.Conditions, sel.SCtx().GetClient(), kv.TiFlash) { + return attachPlan2Task(sel, mppTask.copy()) + } + } + t := tasks[0].convertToRootTask(sel.SCtx()) + return attachPlan2Task(sel, t) +} + +// CheckAggCanPushCop checks whether the aggFuncs and groupByItems can +// be pushed down to coprocessor. +func CheckAggCanPushCop(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc, groupByItems []expression.Expression, storeType kv.StoreType) bool { + sc := sctx.GetSessionVars().StmtCtx + client := sctx.GetClient() + ret := true + reason := "" + for _, aggFunc := range aggFuncs { + // if the aggFunc contain VirtualColumn or CorrelatedColumn, it can not be pushed down. + if expression.ContainVirtualColumn(aggFunc.Args) || expression.ContainCorrelatedColumn(aggFunc.Args) { + reason = "expressions of AggFunc `" + aggFunc.Name + "` contain virtual column or correlated column, which is not supported now" + ret = false + break + } + if !aggregation.CheckAggPushDown(aggFunc, storeType) { + reason = "AggFunc `" + aggFunc.Name + "` is not supported now" + ret = false + break + } + if !expression.CanExprsPushDownWithExtraInfo(sc, aggFunc.Args, client, storeType, aggFunc.Name == ast.AggFuncSum) { + reason = "arguments of AggFunc `" + aggFunc.Name + "` contains unsupported exprs" + ret = false + break + } + orderBySize := len(aggFunc.OrderByItems) + if orderBySize > 0 { + exprs := make([]expression.Expression, 0, orderBySize) + for _, item := range aggFunc.OrderByItems { + exprs = append(exprs, item.Expr) + } + if !expression.CanExprsPushDownWithExtraInfo(sc, exprs, client, storeType, false) { + reason = "arguments of AggFunc `" + aggFunc.Name + "` contains unsupported exprs in order-by clause" + ret = false + break + } + } + pb, _ := aggregation.AggFuncToPBExpr(sctx, client, aggFunc, storeType) + if pb == nil { + reason = "AggFunc `" + aggFunc.Name + "` can not be converted to pb expr" + ret = false + break + } + } + if ret && expression.ContainVirtualColumn(groupByItems) { + reason = "groupByItems contain virtual columns, which is not supported now" + ret = false + } + if ret && !expression.CanExprsPushDown(sc, groupByItems, client, storeType) { + reason = "groupByItems contain unsupported exprs" + ret = false + } + + if !ret { + storageName := storeType.Name() + if storeType == kv.UnSpecified { + storageName = "storage layer" + } + warnErr := errors.New("Aggregation can not be pushed to " + storageName + " because " + reason) + if sc.InExplainStmt { + sc.AppendWarning(warnErr) + } else { + sc.AppendExtraWarning(warnErr) + } + } + return ret +} + +// AggInfo stores the information of an Aggregation. +type AggInfo struct { + AggFuncs []*aggregation.AggFuncDesc + GroupByItems []expression.Expression + Schema *expression.Schema +} + +// BuildFinalModeAggregation splits either LogicalAggregation or PhysicalAggregation to finalAgg and partial1Agg, +// returns the information of partial and final agg. +// partialIsCop means whether partial agg is a cop task. When partialIsCop is false, +// we do not set the AggMode for partialAgg cause it may be split further when +// building the aggregate executor(e.g. buildHashAgg will split the AggDesc further for parallel executing). +// firstRowFuncMap is a map between partial first_row to final first_row, will be used in RemoveUnnecessaryFirstRow +func BuildFinalModeAggregation( + sctx sessionctx.Context, original *AggInfo, partialIsCop bool, isMPPTask bool) (partial, final *AggInfo, firstRowFuncMap map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc) { + firstRowFuncMap = make(map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc, len(original.AggFuncs)) + partial = &AggInfo{ + AggFuncs: make([]*aggregation.AggFuncDesc, 0, len(original.AggFuncs)), + GroupByItems: original.GroupByItems, + Schema: expression.NewSchema(), + } + partialCursor := 0 + final = &AggInfo{ + AggFuncs: make([]*aggregation.AggFuncDesc, len(original.AggFuncs)), + GroupByItems: make([]expression.Expression, 0, len(original.GroupByItems)), + Schema: original.Schema, + } + + partialGbySchema := expression.NewSchema() + // add group by columns + for _, gbyExpr := range partial.GroupByItems { + var gbyCol *expression.Column + if col, ok := gbyExpr.(*expression.Column); ok { + gbyCol = col + } else { + gbyCol = &expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: gbyExpr.GetType(), + } + } + partialGbySchema.Append(gbyCol) + final.GroupByItems = append(final.GroupByItems, gbyCol) + } + + // TODO: Refactor the way of constructing aggregation functions. + // This for loop is ugly, but I do not find a proper way to reconstruct + // it right away. + + // group_concat is special when pushing down, it cannot take the two phase execution if no distinct but with orderBy, and other cases are also different: + // for example: group_concat([distinct] expr0, expr1[, order by expr2] separator ‘,’) + // no distinct, no orderBy: can two phase + // [final agg] group_concat(col#1,’,’) + // [part agg] group_concat(expr0, expr1,’,’) -> col#1 + // no distinct, orderBy: only one phase + // distinct, no orderBy: can two phase + // [final agg] group_concat(distinct col#0, col#1,’,’) + // [part agg] group by expr0 ->col#0, expr1 -> col#1 + // distinct, orderBy: can two phase + // [final agg] group_concat(distinct col#0, col#1, order by col#2,’,’) + // [part agg] group by expr0 ->col#0, expr1 -> col#1; agg function: firstrow(expr2)-> col#2 + + for i, aggFunc := range original.AggFuncs { + finalAggFunc := &aggregation.AggFuncDesc{HasDistinct: false} + finalAggFunc.Name = aggFunc.Name + finalAggFunc.OrderByItems = aggFunc.OrderByItems + args := make([]expression.Expression, 0, len(aggFunc.Args)) + if aggFunc.HasDistinct { + /* + eg: SELECT COUNT(DISTINCT a), SUM(b) FROM t GROUP BY c + + change from + [root] group by: c, funcs:count(distinct a), funcs:sum(b) + to + [root] group by: c, funcs:count(distinct a), funcs:sum(b) + [cop]: group by: c, a + */ + // onlyAddFirstRow means if the distinctArg does not occur in group by items, + // it should be replaced with a firstrow() agg function, needed for the order by items of group_concat() + getDistinctExpr := func(distinctArg expression.Expression, onlyAddFirstRow bool) (ret expression.Expression) { + // 1. add all args to partial.GroupByItems + foundInGroupBy := false + for j, gbyExpr := range partial.GroupByItems { + if gbyExpr.Equal(sctx, distinctArg) && gbyExpr.GetType().Equal(distinctArg.GetType()) { + // if the two expressions exactly the same in terms of data types and collation, then can avoid it. + foundInGroupBy = true + ret = partialGbySchema.Columns[j] + break + } + } + if !foundInGroupBy { + var gbyCol *expression.Column + if col, ok := distinctArg.(*expression.Column); ok { + gbyCol = col + } else { + gbyCol = &expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: distinctArg.GetType(), + } + } + // 2. add group by items if needed + if !onlyAddFirstRow { + partial.GroupByItems = append(partial.GroupByItems, distinctArg) + partialGbySchema.Append(gbyCol) + ret = gbyCol + } + // 3. add firstrow() if needed + if !partialIsCop || onlyAddFirstRow { + // if partial is a cop task, firstrow function is redundant since group by items are outputted + // by group by schema, and final functions use group by schema as their arguments. + // if partial agg is not cop, we must append firstrow function & schema, to output the group by + // items. + // maybe we can unify them sometime. + // only add firstrow for order by items of group_concat() + firstRow, err := aggregation.NewAggFuncDesc(sctx, ast.AggFuncFirstRow, []expression.Expression{distinctArg}, false) + if err != nil { + panic("NewAggFuncDesc FirstRow meets error: " + err.Error()) + } + partial.AggFuncs = append(partial.AggFuncs, firstRow) + newCol, _ := gbyCol.Clone().(*expression.Column) + newCol.RetType = firstRow.RetTp + partial.Schema.Append(newCol) + if onlyAddFirstRow { + ret = newCol + } + partialCursor++ + } + } + return ret + } + + for j, distinctArg := range aggFunc.Args { + // the last arg of ast.AggFuncGroupConcat is the separator, so just put it into the final agg + if aggFunc.Name == ast.AggFuncGroupConcat && j+1 == len(aggFunc.Args) { + args = append(args, distinctArg) + continue + } + args = append(args, getDistinctExpr(distinctArg, false)) + } + + byItems := make([]*util.ByItems, 0, len(aggFunc.OrderByItems)) + for _, byItem := range aggFunc.OrderByItems { + byItems = append(byItems, &util.ByItems{Expr: getDistinctExpr(byItem.Expr, true), Desc: byItem.Desc}) + } + + if aggFunc.HasDistinct && isMPPTask && aggFunc.GroupingID > 0 { + // keep the groupingID as it was, otherwise the new split final aggregate's ganna lost its groupingID info. + finalAggFunc.GroupingID = aggFunc.GroupingID + } + + finalAggFunc.OrderByItems = byItems + finalAggFunc.HasDistinct = aggFunc.HasDistinct + // In logical optimize phase, the Agg->PartitionUnion->TableReader may become + // Agg1->PartitionUnion->Agg2->TableReader, and the Agg2 is a partial aggregation. + // So in the push down here, we need to add a new if-condition check: + // If the original agg mode is partial already, the finalAggFunc's mode become Partial2. + if aggFunc.Mode == aggregation.CompleteMode { + finalAggFunc.Mode = aggregation.CompleteMode + } else if aggFunc.Mode == aggregation.Partial1Mode || aggFunc.Mode == aggregation.Partial2Mode { + finalAggFunc.Mode = aggregation.Partial2Mode + } + } else { + if aggFunc.Name == ast.AggFuncGroupConcat && len(aggFunc.OrderByItems) > 0 { + // group_concat can only run in one phase if it has order by items but without distinct property + partial = nil + final = original + return + } + if aggregation.NeedCount(finalAggFunc.Name) { + // only Avg and Count need count + if isMPPTask && finalAggFunc.Name == ast.AggFuncCount { + // For MPP Task, the final count() is changed to sum(). + // Note: MPP mode does not run avg() directly, instead, avg() -> sum()/(case when count() = 0 then 1 else count() end), + // so we do not process it here. + finalAggFunc.Name = ast.AggFuncSum + } else { + // avg branch + ft := types.NewFieldType(mysql.TypeLonglong) + ft.SetFlen(21) + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + partial.Schema.Append(&expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: ft, + }) + args = append(args, partial.Schema.Columns[partialCursor]) + partialCursor++ + } + } + if finalAggFunc.Name == ast.AggFuncApproxCountDistinct { + ft := types.NewFieldType(mysql.TypeString) + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + ft.AddFlag(mysql.NotNullFlag) + partial.Schema.Append(&expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: ft, + }) + args = append(args, partial.Schema.Columns[partialCursor]) + partialCursor++ + } + if aggregation.NeedValue(finalAggFunc.Name) { + partial.Schema.Append(&expression.Column{ + UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), + RetType: original.Schema.Columns[i].GetType(), + }) + args = append(args, partial.Schema.Columns[partialCursor]) + partialCursor++ + } + if aggFunc.Name == ast.AggFuncAvg { + cntAgg := aggFunc.Clone() + cntAgg.Name = ast.AggFuncCount + err := cntAgg.TypeInfer(sctx) + if err != nil { // must not happen + partial = nil + final = original + return + } + partial.Schema.Columns[partialCursor-2].RetType = cntAgg.RetTp + // we must call deep clone in this case, to avoid sharing the arguments. + sumAgg := aggFunc.Clone() + sumAgg.Name = ast.AggFuncSum + sumAgg.TypeInfer4AvgSum(sumAgg.RetTp) + partial.Schema.Columns[partialCursor-1].RetType = sumAgg.RetTp + partial.AggFuncs = append(partial.AggFuncs, cntAgg, sumAgg) + } else if aggFunc.Name == ast.AggFuncApproxCountDistinct || aggFunc.Name == ast.AggFuncGroupConcat { + newAggFunc := aggFunc.Clone() + newAggFunc.Name = aggFunc.Name + newAggFunc.RetTp = partial.Schema.Columns[partialCursor-1].GetType() + partial.AggFuncs = append(partial.AggFuncs, newAggFunc) + if aggFunc.Name == ast.AggFuncGroupConcat { + // append the last separator arg + args = append(args, aggFunc.Args[len(aggFunc.Args)-1]) + } + } else { + // other agg desc just split into two parts + partialFuncDesc := aggFunc.Clone() + partial.AggFuncs = append(partial.AggFuncs, partialFuncDesc) + if aggFunc.Name == ast.AggFuncFirstRow { + firstRowFuncMap[partialFuncDesc] = finalAggFunc + } + } + + // In logical optimize phase, the Agg->PartitionUnion->TableReader may become + // Agg1->PartitionUnion->Agg2->TableReader, and the Agg2 is a partial aggregation. + // So in the push down here, we need to add a new if-condition check: + // If the original agg mode is partial already, the finalAggFunc's mode become Partial2. + if aggFunc.Mode == aggregation.CompleteMode { + finalAggFunc.Mode = aggregation.FinalMode + } else if aggFunc.Mode == aggregation.Partial1Mode || aggFunc.Mode == aggregation.Partial2Mode { + finalAggFunc.Mode = aggregation.Partial2Mode + } + } + + finalAggFunc.Args = args + finalAggFunc.RetTp = aggFunc.RetTp + final.AggFuncs[i] = finalAggFunc + } + partial.Schema.Append(partialGbySchema.Columns...) + if partialIsCop { + for _, f := range partial.AggFuncs { + f.Mode = aggregation.Partial1Mode + } + } + return +} + +// convertAvgForMPP converts avg(arg) to sum(arg)/(case when count(arg)=0 then 1 else count(arg) end), in detail: +// 1.rewrite avg() in the final aggregation to count() and sum(), and reconstruct its schema. +// 2.replace avg() with sum(arg)/(case when count(arg)=0 then 1 else count(arg) end) and reuse the original schema of the final aggregation. +// If there is no avg, nothing is changed and return nil. +func (p *basePhysicalAgg) convertAvgForMPP() *PhysicalProjection { + newSchema := expression.NewSchema() + newSchema.Keys = p.schema.Keys + newSchema.UniqueKeys = p.schema.UniqueKeys + newAggFuncs := make([]*aggregation.AggFuncDesc, 0, 2*len(p.AggFuncs)) + exprs := make([]expression.Expression, 0, 2*len(p.schema.Columns)) + // add agg functions schema + for i, aggFunc := range p.AggFuncs { + if aggFunc.Name == ast.AggFuncAvg { + // inset a count(column) + avgCount := aggFunc.Clone() + avgCount.Name = ast.AggFuncCount + err := avgCount.TypeInfer(p.SCtx()) + if err != nil { // must not happen + return nil + } + newAggFuncs = append(newAggFuncs, avgCount) + avgCountCol := &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: avgCount.RetTp, + } + newSchema.Append(avgCountCol) + // insert a sum(column) + avgSum := aggFunc.Clone() + avgSum.Name = ast.AggFuncSum + avgSum.TypeInfer4AvgSum(avgSum.RetTp) + newAggFuncs = append(newAggFuncs, avgSum) + avgSumCol := &expression.Column{ + UniqueID: p.schema.Columns[i].UniqueID, + RetType: avgSum.RetTp, + } + newSchema.Append(avgSumCol) + // avgSumCol/(case when avgCountCol=0 then 1 else avgCountCol end) + eq := expression.NewFunctionInternal(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeTiny), avgCountCol, expression.NewZero()) + caseWhen := expression.NewFunctionInternal(p.SCtx(), ast.Case, avgCountCol.RetType, eq, expression.NewOne(), avgCountCol) + divide := expression.NewFunctionInternal(p.SCtx(), ast.Div, avgSumCol.RetType, avgSumCol, caseWhen) + divide.(*expression.ScalarFunction).RetType = p.schema.Columns[i].RetType + exprs = append(exprs, divide) + } else { + // other non-avg agg use the old schema as it did. + newAggFuncs = append(newAggFuncs, aggFunc) + newSchema.Append(p.schema.Columns[i]) + exprs = append(exprs, p.schema.Columns[i]) + } + } + // no avgs + // for final agg, always add project due to in-compatibility between TiDB and TiFlash + if len(p.schema.Columns) == len(newSchema.Columns) && !p.IsFinalAgg() { + return nil + } + // add remaining columns to exprs + for i := len(p.AggFuncs); i < len(p.schema.Columns); i++ { + exprs = append(exprs, p.schema.Columns[i]) + } + proj := PhysicalProjection{ + Exprs: exprs, + CalculateNoDelay: false, + AvoidColumnEvaluator: false, + }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), p.GetChildReqProps(0).CloneEssentialFields()) + proj.SetSchema(p.schema) + + p.AggFuncs = newAggFuncs + p.schema = newSchema + + return proj +} + +func (p *basePhysicalAgg) newPartialAggregate(copTaskType kv.StoreType, isMPPTask bool) (partial, final PhysicalPlan) { + // Check if this aggregation can push down. + if !CheckAggCanPushCop(p.SCtx(), p.AggFuncs, p.GroupByItems, copTaskType) { + return nil, p.self + } + partialPref, finalPref, firstRowFuncMap := BuildFinalModeAggregation(p.SCtx(), &AggInfo{ + AggFuncs: p.AggFuncs, + GroupByItems: p.GroupByItems, + Schema: p.Schema().Clone(), + }, true, isMPPTask) + if partialPref == nil { + return nil, p.self + } + if p.TP() == plancodec.TypeStreamAgg && len(partialPref.GroupByItems) != len(finalPref.GroupByItems) { + return nil, p.self + } + // Remove unnecessary FirstRow. + partialPref.AggFuncs = RemoveUnnecessaryFirstRow(p.SCtx(), + finalPref.GroupByItems, partialPref.AggFuncs, partialPref.GroupByItems, partialPref.Schema, firstRowFuncMap) + if copTaskType == kv.TiDB { + // For partial agg of TiDB cop task, since TiDB coprocessor reuse the TiDB executor, + // and TiDB aggregation executor won't output the group by value, + // so we need add `firstrow` aggregation function to output the group by value. + aggFuncs, err := genFirstRowAggForGroupBy(p.SCtx(), partialPref.GroupByItems) + if err != nil { + return nil, p.self + } + partialPref.AggFuncs = append(partialPref.AggFuncs, aggFuncs...) + } + p.AggFuncs = partialPref.AggFuncs + p.GroupByItems = partialPref.GroupByItems + p.schema = partialPref.Schema + partialAgg := p.self + // Create physical "final" aggregation. + prop := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} + if p.TP() == plancodec.TypeStreamAgg { + finalAgg := basePhysicalAgg{ + AggFuncs: finalPref.AggFuncs, + GroupByItems: finalPref.GroupByItems, + MppRunMode: p.MppRunMode, + }.initForStream(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), prop) + finalAgg.schema = finalPref.Schema + return partialAgg, finalAgg + } + + finalAgg := basePhysicalAgg{ + AggFuncs: finalPref.AggFuncs, + GroupByItems: finalPref.GroupByItems, + MppRunMode: p.MppRunMode, + }.initForHash(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), prop) + finalAgg.schema = finalPref.Schema + // partialAgg and finalAgg use the same ref of stats + return partialAgg, finalAgg +} + +func (p *basePhysicalAgg) scale3StageForDistinctAgg() (bool, expression.GroupingSets) { + if p.canUse3Stage4SingleDistinctAgg() { + return true, nil + } + return p.canUse3Stage4MultiDistinctAgg() +} + +// canUse3Stage4MultiDistinctAgg returns true if this agg can use 3 stage for multi distinct aggregation +func (p *basePhysicalAgg) canUse3Stage4MultiDistinctAgg() (can bool, gss expression.GroupingSets) { + if !p.SCtx().GetSessionVars().Enable3StageDistinctAgg || !p.SCtx().GetSessionVars().Enable3StageMultiDistinctAgg || len(p.GroupByItems) > 0 { + return false, nil + } + defer func() { + // some clean work. + if !can { + for _, fun := range p.AggFuncs { + fun.GroupingID = 0 + } + } + }() + // groupingSets is alias of []GroupingSet, the below equal to = make([]GroupingSet, 0, 2) + groupingSets := make(expression.GroupingSets, 0, 2) + for _, fun := range p.AggFuncs { + if fun.HasDistinct { + if fun.Name != ast.AggFuncCount { + // now only for multi count(distinct x) + return false, nil + } + for _, arg := range fun.Args { + // bail out when args are not simple column, see GitHub issue #35417 + if _, ok := arg.(*expression.Column); !ok { + return false, nil + } + } + // here it's a valid count distinct agg with normal column args, collecting its distinct expr. + groupingSets = append(groupingSets, expression.GroupingSet{fun.Args}) + // groupingID now is the offset of target grouping in GroupingSets. + // todo: it may be changed after grouping set merge in the future. + fun.GroupingID = len(groupingSets) + } else if len(fun.Args) > 1 { + return false, nil + } + // banned group_concat(x order by y) + if len(fun.OrderByItems) > 0 || fun.Mode != aggregation.CompleteMode { + return false, nil + } + } + compressed := groupingSets.Merge() + if len(compressed) != len(groupingSets) { + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("Some grouping sets should be merged")) + // todo arenatlx: some grouping set should be merged which is not supported by now temporarily. + return false, nil + } + if groupingSets.NeedCloneColumn() { + // todo: column clone haven't implemented. + return false, nil + } + if len(groupingSets) > 1 { + // fill the grouping ID for normal agg. + for _, fun := range p.AggFuncs { + if fun.GroupingID == 0 { + // the grouping ID hasn't set. find the targeting grouping set. + groupingSetOffset := groupingSets.TargetOne(fun.Args) + if groupingSetOffset == -1 { + // todo: if we couldn't find a existed current valid group layout, we need to copy the column out from being filled with null value. + p.SCtx().GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("couldn't find a proper group set for normal agg")) + return false, nil + } + // starting with 1 + fun.GroupingID = groupingSetOffset + 1 + } + } + return true, groupingSets + } + return false, nil +} + +// canUse3Stage4SingleDistinctAgg returns true if this agg can use 3 stage for distinct aggregation +func (p *basePhysicalAgg) canUse3Stage4SingleDistinctAgg() bool { + num := 0 + if !p.SCtx().GetSessionVars().Enable3StageDistinctAgg || len(p.GroupByItems) > 0 { + return false + } + for _, fun := range p.AggFuncs { + if fun.HasDistinct { + num++ + if num > 1 || fun.Name != ast.AggFuncCount { + return false + } + for _, arg := range fun.Args { + // bail out when args are not simple column, see GitHub issue #35417 + if _, ok := arg.(*expression.Column); !ok { + return false + } + } + } else if len(fun.Args) > 1 { + return false + } + + if len(fun.OrderByItems) > 0 || fun.Mode != aggregation.CompleteMode { + return false + } + } + return num == 1 +} + +func genFirstRowAggForGroupBy(ctx sessionctx.Context, groupByItems []expression.Expression) ([]*aggregation.AggFuncDesc, error) { + aggFuncs := make([]*aggregation.AggFuncDesc, 0, len(groupByItems)) + for _, groupBy := range groupByItems { + agg, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncFirstRow, []expression.Expression{groupBy}, false) + if err != nil { + return nil, err + } + aggFuncs = append(aggFuncs, agg) + } + return aggFuncs, nil +} + +// RemoveUnnecessaryFirstRow removes unnecessary FirstRow of the aggregation. This function can be +// used for both LogicalAggregation and PhysicalAggregation. +// When the select column is same with the group by key, the column can be removed and gets value from the group by key. +// e.g +// select a, count(b) from t group by a; +// The schema is [firstrow(a), count(b), a]. The column firstrow(a) is unnecessary. +// Can optimize the schema to [count(b), a] , and change the index to get value. +func RemoveUnnecessaryFirstRow( + sctx sessionctx.Context, + finalGbyItems []expression.Expression, + partialAggFuncs []*aggregation.AggFuncDesc, + partialGbyItems []expression.Expression, + partialSchema *expression.Schema, + firstRowFuncMap map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc) []*aggregation.AggFuncDesc { + partialCursor := 0 + newAggFuncs := make([]*aggregation.AggFuncDesc, 0, len(partialAggFuncs)) + for _, aggFunc := range partialAggFuncs { + if aggFunc.Name == ast.AggFuncFirstRow { + canOptimize := false + for j, gbyExpr := range partialGbyItems { + if j >= len(finalGbyItems) { + // after distinct push, len(partialGbyItems) may larger than len(finalGbyItems) + // for example, + // select /*+ HASH_AGG() */ a, count(distinct a) from t; + // will generate to, + // HashAgg root funcs:count(distinct a), funcs:firstrow(a)" + // HashAgg cop group by:a, funcs:firstrow(a)->Column#6" + // the firstrow in root task can not be removed. + break + } + if gbyExpr.Equal(sctx, aggFunc.Args[0]) { + canOptimize = true + firstRowFuncMap[aggFunc].Args[0] = finalGbyItems[j] + break + } + } + if canOptimize { + partialSchema.Columns = append(partialSchema.Columns[:partialCursor], partialSchema.Columns[partialCursor+1:]...) + continue + } + } + partialCursor += computePartialCursorOffset(aggFunc.Name) + newAggFuncs = append(newAggFuncs, aggFunc) + } + return newAggFuncs +} + +func computePartialCursorOffset(name string) int { + offset := 0 + if aggregation.NeedCount(name) { + offset++ + } + if aggregation.NeedValue(name) { + offset++ + } + if name == ast.AggFuncApproxCountDistinct { + offset++ + } + return offset +} + +func (p *PhysicalStreamAgg) attach2Task(tasks ...task) task { + t := tasks[0].copy() + if cop, ok := t.(*copTask); ok { + // We should not push agg down across + // 1. double read, since the data of second read is ordered by handle instead of index. The `extraHandleCol` is added + // if the double read needs to keep order. So we just use it to decided + // whether the following plan is double read with order reserved. + // 2. the case that there's filters should be calculated on TiDB side. + // 3. the case of index merge + if (cop.indexPlan != nil && cop.tablePlan != nil && cop.keepOrder) || len(cop.rootTaskConds) > 0 || len(cop.idxMergePartPlans) > 0 { + t = cop.convertToRootTask(p.SCtx()) + attachPlan2Task(p, t) + } else { + storeType := cop.getStoreType() + // TiFlash doesn't support Stream Aggregation + if storeType == kv.TiFlash && len(p.GroupByItems) > 0 { + return invalidTask + } + partialAgg, finalAgg := p.newPartialAggregate(storeType, false) + if partialAgg != nil { + if cop.tablePlan != nil { + cop.finishIndexPlan() + partialAgg.SetChildren(cop.tablePlan) + cop.tablePlan = partialAgg + // If needExtraProj is true, a projection will be created above the PhysicalIndexLookUpReader to make sure + // the schema is the same as the original DataSource schema. + // However, we pushed down the agg here, the partial agg was placed on the top of tablePlan, and the final + // agg will be placed above the PhysicalIndexLookUpReader, and the schema will be set correctly for them. + // If we add the projection again, the projection will be between the PhysicalIndexLookUpReader and + // the partial agg, and the schema will be broken. + cop.needExtraProj = false + } else { + partialAgg.SetChildren(cop.indexPlan) + cop.indexPlan = partialAgg + } + } + t = cop.convertToRootTask(p.SCtx()) + attachPlan2Task(finalAgg, t) + } + } else if mpp, ok := t.(*mppTask); ok { + t = mpp.convertToRootTask(p.SCtx()) + attachPlan2Task(p, t) + } else { + attachPlan2Task(p, t) + } + return t +} + +// cpuCostDivisor computes the concurrency to which we would amortize CPU cost +// for hash aggregation. +func (p *PhysicalHashAgg) cpuCostDivisor(hasDistinct bool) (divisor, con float64) { + if hasDistinct { + return 0, 0 + } + sessionVars := p.SCtx().GetSessionVars() + finalCon, partialCon := sessionVars.HashAggFinalConcurrency(), sessionVars.HashAggPartialConcurrency() + // According to `ValidateSetSystemVar`, `finalCon` and `partialCon` cannot be less than or equal to 0. + if finalCon == 1 && partialCon == 1 { + return 0, 0 + } + // It is tricky to decide which concurrency we should use to amortize CPU cost. Since cost of hash + // aggregation is tend to be under-estimated as explained in `attach2Task`, we choose the smaller + // concurrecy to make some compensation. + return math.Min(float64(finalCon), float64(partialCon)), float64(finalCon + partialCon) +} + +func (p *PhysicalHashAgg) attach2TaskForMpp1Phase(mpp *mppTask) task { + // 1-phase agg: when the partition columns can be satisfied, where the plan does not need to enforce Exchange + // only push down the original agg + proj := p.convertAvgForMPP() + attachPlan2Task(p.self, mpp) + if proj != nil { + attachPlan2Task(proj, mpp) + } + return mpp +} + +// scaleStats4GroupingSets scale the derived stats because the lower source has been expanded. +// +// parent OP <- logicalAgg <- children OP (derived stats) +// | +// v +// parent OP <- physicalAgg <- children OP (stats used) +// | +// +----------+----------+----------+ +// Final Mid Partial Expand +// +// physical agg stats is reasonable from the whole, because expand operator is designed to facilitate +// the Mid and Partial Agg, which means when leaving the Final, its output rowcount could be exactly +// the same as what it derived(estimated) before entering physical optimization phase. +// +// From the cost model correctness, for these inserted sub-agg and even expand operator, we should +// recompute the stats for them particularly. +// +// for example: grouping sets {},{}, group by items {a,b,c,groupingID} +// after expand: +// +// a, b, c, groupingID +// ... null c 1 ---+ +// ... null c 1 +------- replica group 1 +// ... null c 1 ---+ +// null ... c 2 ---+ +// null ... c 2 +------- replica group 2 +// null ... c 2 ---+ +// +// since null value is seen the same when grouping data (groupingID in one replica is always the same): +// - so the num of group in replica 1 is equal to NDV(a,c) +// - so the num of group in replica 2 is equal to NDV(b,c) +// +// in a summary, the total num of group of all replica is equal to = Σ:NDV(each-grouping-set-cols, normal-group-cols) +func (p *PhysicalHashAgg) scaleStats4GroupingSets(groupingSets expression.GroupingSets, groupingIDCol *expression.Column, + childSchema *expression.Schema, childStats *property.StatsInfo) { + idSets := groupingSets.AllSetsColIDs() + normalGbyCols := make([]*expression.Column, 0, len(p.GroupByItems)) + for _, gbyExpr := range p.GroupByItems { + cols := expression.ExtractColumns(gbyExpr) + for _, col := range cols { + if !idSets.Has(int(col.UniqueID)) && col.UniqueID != groupingIDCol.UniqueID { + normalGbyCols = append(normalGbyCols, col) + } + } + } + sumNDV := float64(0) + for _, groupingSet := range groupingSets { + // for every grouping set, pick its cols out, and combine with normal group cols to get the ndv. + groupingSetCols := groupingSet.ExtractCols() + groupingSetCols = append(groupingSetCols, normalGbyCols...) + ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(groupingSetCols, childSchema, childStats) + sumNDV += ndv + } + // After group operator, all same rows are grouped into one row, that means all + // change the sub-agg's stats + if p.StatsInfo() != nil { + // equivalence to a new cloned one. (cause finalAgg and partialAgg may share a same copy of stats) + cpStats := p.StatsInfo().Scale(1) + cpStats.RowCount = sumNDV + // We cannot estimate the ColNDVs for every output, so we use a conservative strategy. + for k := range cpStats.ColNDVs { + cpStats.ColNDVs[k] = sumNDV + } + // for old groupNDV, if it's containing one more grouping set cols, just plus the NDV where the col is excluded. + // for example: old grouping NDV(b,c), where b is in grouping sets {},{}. so when countering the new NDV: + // cases: + // new grouping NDV(b,c) := old NDV(b,c) + NDV(null, c) = old NDV(b,c) + DNV(c). + // new grouping NDV(a,b,c) := old NDV(a,b,c) + NDV(null,b,c) + NDV(a,null,c) = old NDV(a,b,c) + NDV(b,c) + NDV(a,c) + allGroupingSetsIDs := groupingSets.AllSetsColIDs() + for _, oneGNDV := range cpStats.GroupNDVs { + newGNDV := oneGNDV.NDV + intersectionIDs := make([]int64, 0, len(oneGNDV.Cols)) + for i, id := range oneGNDV.Cols { + if allGroupingSetsIDs.Has(int(id)) { + // when meet an id in grouping sets, skip it (cause its null) and append the rest ids to count the incrementNDV. + beforeLen := len(intersectionIDs) + intersectionIDs = append(intersectionIDs, oneGNDV.Cols[i:]...) + incrementNDV, _ := cardinality.EstimateColsDNVWithMatchedLenFromUniqueIDs(intersectionIDs, childSchema, childStats) + newGNDV += incrementNDV + // restore the before intersectionIDs slice. + intersectionIDs = intersectionIDs[:beforeLen] + } + // insert ids one by one. + intersectionIDs = append(intersectionIDs, id) + } + oneGNDV.NDV = newGNDV + } + p.SetStats(cpStats) + } +} + +// adjust3StagePhaseAgg generate 3 stage aggregation for single/multi count distinct if applicable. +// +// select count(distinct a), count(b) from foo +// +// will generate plan: +// +// HashAgg sum(#1), sum(#2) -> final agg +// +- Exchange Passthrough +// +- HashAgg count(distinct a) #1, sum(#3) #2 -> middle agg +// +- Exchange HashPartition by a +// +- HashAgg count(b) #3, group by a -> partial agg +// +- TableScan foo +// +// select count(distinct a), count(distinct b), count(c) from foo +// +// will generate plan: +// +// HashAgg sum(#1), sum(#2), sum(#3) -> final agg +// +- Exchange Passthrough +// +- HashAgg count(distinct a) #1, count(distinct b) #2, sum(#4) #3 -> middle agg +// +- Exchange HashPartition by a,b,groupingID +// +- HashAgg count(c) #4, group by a,b,groupingID -> partial agg +// +- Expand {}, {} -> expand +// +- TableScan foo +func (p *PhysicalHashAgg) adjust3StagePhaseAgg(partialAgg, finalAgg PhysicalPlan, canUse3StageAgg bool, + groupingSets expression.GroupingSets, mpp *mppTask) (final, mid, part, proj4Part PhysicalPlan, _ error) { + if !(partialAgg != nil && canUse3StageAgg) { + // quick path: return the original finalAgg and partiAgg. + return finalAgg, nil, partialAgg, nil, nil + } + if len(groupingSets) == 0 { + // single distinct agg mode. + clonedAgg, err := finalAgg.Clone() + if err != nil { + return nil, nil, nil, nil, err + } + + // step1: adjust middle agg. + middleHashAgg := clonedAgg.(*PhysicalHashAgg) + distinctPos := 0 + middleSchema := expression.NewSchema() + schemaMap := make(map[int64]*expression.Column, len(middleHashAgg.AggFuncs)) + for i, fun := range middleHashAgg.AggFuncs { + col := &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: fun.RetTp, + } + if fun.HasDistinct { + distinctPos = i + fun.Mode = aggregation.Partial1Mode + } else { + fun.Mode = aggregation.Partial2Mode + originalCol := fun.Args[0].(*expression.Column) + // mapping the current partial output column with the agg origin arg column. (final agg arg should use this one) + schemaMap[originalCol.UniqueID] = col + } + middleSchema.Append(col) + } + middleHashAgg.schema = middleSchema + + // step2: adjust final agg. + finalHashAgg := finalAgg.(*PhysicalHashAgg) + finalAggDescs := make([]*aggregation.AggFuncDesc, 0, len(finalHashAgg.AggFuncs)) + for i, fun := range finalHashAgg.AggFuncs { + newArgs := make([]expression.Expression, 0, 1) + if distinctPos == i { + // change count(distinct) to sum() + fun.Name = ast.AggFuncSum + fun.HasDistinct = false + newArgs = append(newArgs, middleSchema.Columns[i]) + } else { + for _, arg := range fun.Args { + newCol, err := arg.RemapColumn(schemaMap) + if err != nil { + return nil, nil, nil, nil, err + } + newArgs = append(newArgs, newCol) + } + } + fun.Mode = aggregation.FinalMode + fun.Args = newArgs + finalAggDescs = append(finalAggDescs, fun) + } + finalHashAgg.AggFuncs = finalAggDescs + // partialAgg is im-mutated from args. + return finalHashAgg, middleHashAgg, partialAgg, nil, nil + } + // multi distinct agg mode, having grouping sets. + // set the default expression to constant 1 for the convenience to choose default group set data. + var groupingIDCol expression.Expression + // enforce Expand operator above the children. + // physical plan is enumerated without children from itself, use mpp subtree instead p.children. + // scale(len(groupingSets)) will change the NDV, while Expand doesn't change the NDV and groupNDV. + stats := mpp.p.StatsInfo().Scale(float64(1)) + stats.RowCount = stats.RowCount * float64(len(groupingSets)) + physicalExpand := PhysicalExpand{ + GroupingSets: groupingSets, + }.Init(p.SCtx(), stats, mpp.p.SelectBlockOffset()) + // generate a new column as groupingID to identify which this row is targeting for. + tp := types.NewFieldType(mysql.TypeLonglong) + tp.SetFlag(mysql.UnsignedFlag | mysql.NotNullFlag) + groupingIDCol = &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: tp, + } + // append the physical expand op with groupingID column. + physicalExpand.SetSchema(mpp.p.Schema().Clone()) + physicalExpand.schema.Append(groupingIDCol.(*expression.Column)) + physicalExpand.GroupingIDCol = groupingIDCol.(*expression.Column) + // attach PhysicalExpand to mpp + attachPlan2Task(physicalExpand, mpp) + + // having group sets + clonedAgg, err := finalAgg.Clone() + if err != nil { + return nil, nil, nil, nil, err + } + cloneHashAgg := clonedAgg.(*PhysicalHashAgg) + // Clone(), it will share same base-plan elements from the finalAgg, including id,tp,stats. Make a new one here. + cloneHashAgg.Plan = base.NewBasePlan(cloneHashAgg.SCtx(), cloneHashAgg.TP(), cloneHashAgg.SelectBlockOffset()) + cloneHashAgg.SetStats(finalAgg.StatsInfo()) // reuse the final agg stats here. + + // step1: adjust partial agg, for normal agg here, adjust it to target for specified group data. + // Since we may substitute the first arg of normal agg with case-when expression here, append a + // customized proj here rather than depending on postOptimize to insert a blunt one for us. + // + // proj4Partial output all the base col from lower op + caseWhen proj cols. + proj4Partial := new(PhysicalProjection).Init(p.SCtx(), mpp.p.StatsInfo(), mpp.p.SelectBlockOffset()) + for _, col := range mpp.p.Schema().Columns { + proj4Partial.Exprs = append(proj4Partial.Exprs, col) + } + proj4Partial.SetSchema(mpp.p.Schema().Clone()) + + partialHashAgg := partialAgg.(*PhysicalHashAgg) + partialHashAgg.GroupByItems = append(partialHashAgg.GroupByItems, groupingIDCol) + partialHashAgg.schema.Append(groupingIDCol.(*expression.Column)) + // it will create a new stats for partial agg. + partialHashAgg.scaleStats4GroupingSets(groupingSets, groupingIDCol.(*expression.Column), proj4Partial.Schema(), proj4Partial.StatsInfo()) + for _, fun := range partialHashAgg.AggFuncs { + if !fun.HasDistinct { + // for normal agg phase1, we should also modify them to target for specified group data. + // Expr = (case when groupingID = targeted_groupingID then arg else null end) + eqExpr := expression.NewFunctionInternal(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeTiny), groupingIDCol, expression.NewUInt64Const(fun.GroupingID)) + caseWhen := expression.NewFunctionInternal(p.SCtx(), ast.Case, fun.Args[0].GetType(), eqExpr, fun.Args[0], expression.NewNull()) + caseWhenProjCol := &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: fun.Args[0].GetType(), + } + proj4Partial.Exprs = append(proj4Partial.Exprs, caseWhen) + proj4Partial.Schema().Append(caseWhenProjCol) + fun.Args[0] = caseWhenProjCol + } + } + + // step2: adjust middle agg + // middleHashAgg shared the same stats with the final agg does. + middleHashAgg := cloneHashAgg + middleSchema := expression.NewSchema() + schemaMap := make(map[int64]*expression.Column, len(middleHashAgg.AggFuncs)) + for _, fun := range middleHashAgg.AggFuncs { + col := &expression.Column{ + UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), + RetType: fun.RetTp, + } + if fun.HasDistinct { + // let count distinct agg aggregate on whole-scope data rather using case-when expr to target on specified group. (agg null strict attribute) + fun.Mode = aggregation.Partial1Mode + } else { + fun.Mode = aggregation.Partial2Mode + originalCol := fun.Args[0].(*expression.Column) + // record the origin column unique id down before change it to be case when expr. + // mapping the current partial output column with the agg origin arg column. (final agg arg should use this one) + schemaMap[originalCol.UniqueID] = col + } + middleSchema.Append(col) + } + middleHashAgg.schema = middleSchema + + // step3: adjust final agg + finalHashAgg := finalAgg.(*PhysicalHashAgg) + finalAggDescs := make([]*aggregation.AggFuncDesc, 0, len(finalHashAgg.AggFuncs)) + for i, fun := range finalHashAgg.AggFuncs { + newArgs := make([]expression.Expression, 0, 1) + if fun.HasDistinct { + // change count(distinct) agg to sum() + fun.Name = ast.AggFuncSum + fun.HasDistinct = false + // count(distinct a,b) -> become a single partial result col. + newArgs = append(newArgs, middleSchema.Columns[i]) + } else { + // remap final normal agg args to be output schema of middle normal agg. + for _, arg := range fun.Args { + newCol, err := arg.RemapColumn(schemaMap) + if err != nil { + return nil, nil, nil, nil, err + } + newArgs = append(newArgs, newCol) + } + } + fun.Mode = aggregation.FinalMode + fun.Args = newArgs + fun.GroupingID = 0 + finalAggDescs = append(finalAggDescs, fun) + } + finalHashAgg.AggFuncs = finalAggDescs + return finalHashAgg, middleHashAgg, partialHashAgg, proj4Partial, nil +} + +func (p *PhysicalHashAgg) attach2TaskForMpp(tasks ...task) task { + t := tasks[0].copy() + mpp, ok := t.(*mppTask) + if !ok { + return invalidTask + } + switch p.MppRunMode { + case Mpp1Phase: + // 1-phase agg: when the partition columns can be satisfied, where the plan does not need to enforce Exchange + // only push down the original agg + proj := p.convertAvgForMPP() + attachPlan2Task(p, mpp) + if proj != nil { + attachPlan2Task(proj, mpp) + } + return mpp + case Mpp2Phase: + // TODO: when partition property is matched by sub-plan, we actually needn't do extra an exchange and final agg. + proj := p.convertAvgForMPP() + partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, true) + if partialAgg == nil { + return invalidTask + } + attachPlan2Task(partialAgg, mpp) + partitionCols := p.MppPartitionCols + if len(partitionCols) == 0 { + items := finalAgg.(*PhysicalHashAgg).GroupByItems + partitionCols = make([]*property.MPPPartitionColumn, 0, len(items)) + for _, expr := range items { + col, ok := expr.(*expression.Column) + if !ok { + return invalidTask + } + partitionCols = append(partitionCols, &property.MPPPartitionColumn{ + Col: col, + CollateID: property.GetCollateIDByNameForPartition(col.GetType().GetCollate()), + }) + } + } + prop := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.HashType, MPPPartitionCols: partitionCols} + newMpp := mpp.enforceExchangerImpl(prop) + if newMpp.invalid() { + return newMpp + } + attachPlan2Task(finalAgg, newMpp) + // TODO: how to set 2-phase cost? + if proj != nil { + attachPlan2Task(proj, newMpp) + } + return newMpp + case MppTiDB: + partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, false) + if partialAgg != nil { + attachPlan2Task(partialAgg, mpp) + } + t = mpp.convertToRootTask(p.SCtx()) + attachPlan2Task(finalAgg, t) + return t + case MppScalar: + prop := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.SinglePartitionType} + if !mpp.needEnforceExchanger(prop) { + // On the one hand: when the low layer already satisfied the single partition layout, just do the all agg computation in the single node. + return p.attach2TaskForMpp1Phase(mpp) + } + // On the other hand: try to split the mppScalar agg into multi phases agg **down** to multi nodes since data already distributed across nodes. + // we have to check it before the content of p has been modified + canUse3StageAgg, groupingSets := p.scale3StageForDistinctAgg() + proj := p.convertAvgForMPP() + partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, true) + if finalAgg == nil { + return invalidTask + } + + final, middle, partial, proj4Partial, err := p.adjust3StagePhaseAgg(partialAgg, finalAgg, canUse3StageAgg, groupingSets, mpp) + if err != nil { + return invalidTask + } + + // partial agg proj would be null if one scalar agg cannot run in two-phase mode + if proj4Partial != nil { + attachPlan2Task(proj4Partial, mpp) + } + + // partial agg would be null if one scalar agg cannot run in two-phase mode + if partial != nil { + attachPlan2Task(partial, mpp) + } + + if middle != nil && canUse3StageAgg { + items := partial.(*PhysicalHashAgg).GroupByItems + partitionCols := make([]*property.MPPPartitionColumn, 0, len(items)) + for _, expr := range items { + col, ok := expr.(*expression.Column) + if !ok { + continue + } + partitionCols = append(partitionCols, &property.MPPPartitionColumn{ + Col: col, + CollateID: property.GetCollateIDByNameForPartition(col.GetType().GetCollate()), + }) + } + + exProp := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.HashType, MPPPartitionCols: partitionCols} + newMpp := mpp.enforceExchanger(exProp) + attachPlan2Task(middle, newMpp) + mpp = newMpp + } + + // prop here still be the first generated single-partition requirement. + newMpp := mpp.enforceExchanger(prop) + attachPlan2Task(final, newMpp) + if proj == nil { + proj = PhysicalProjection{ + Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)), + }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset()) + for _, col := range p.Schema().Columns { + proj.Exprs = append(proj.Exprs, col) + } + proj.SetSchema(p.schema) + } + attachPlan2Task(proj, newMpp) + return newMpp + default: + return invalidTask + } +} + +func (p *PhysicalHashAgg) attach2Task(tasks ...task) task { + t := tasks[0].copy() + final := p + if cop, ok := t.(*copTask); ok { + if len(cop.rootTaskConds) == 0 && len(cop.idxMergePartPlans) == 0 { + copTaskType := cop.getStoreType() + partialAgg, finalAgg := p.newPartialAggregate(copTaskType, false) + if finalAgg != nil { + final = finalAgg.(*PhysicalHashAgg) + } + if partialAgg != nil { + if cop.tablePlan != nil { + cop.finishIndexPlan() + partialAgg.SetChildren(cop.tablePlan) + cop.tablePlan = partialAgg + // If needExtraProj is true, a projection will be created above the PhysicalIndexLookUpReader to make sure + // the schema is the same as the original DataSource schema. + // However, we pushed down the agg here, the partial agg was placed on the top of tablePlan, and the final + // agg will be placed above the PhysicalIndexLookUpReader, and the schema will be set correctly for them. + // If we add the projection again, the projection will be between the PhysicalIndexLookUpReader and + // the partial agg, and the schema will be broken. + cop.needExtraProj = false + } else { + partialAgg.SetChildren(cop.indexPlan) + cop.indexPlan = partialAgg + } + } + // In `newPartialAggregate`, we are using stats of final aggregation as stats + // of `partialAgg`, so the network cost of transferring result rows of `partialAgg` + // to TiDB is normally under-estimated for hash aggregation, since the group-by + // column may be independent of the column used for region distribution, so a closer + // estimation of network cost for hash aggregation may multiply the number of + // regions involved in the `partialAgg`, which is unknown however. + t = cop.convertToRootTask(p.SCtx()) + attachPlan2Task(finalAgg, t) + } else { + t = cop.convertToRootTask(p.SCtx()) + attachPlan2Task(p, t) + } + } else if _, ok := t.(*mppTask); ok { + return final.attach2TaskForMpp(tasks...) + } else { + attachPlan2Task(p, t) + } + return t +} + +func (p *PhysicalWindow) attach2TaskForMPP(mpp *mppTask) task { + // FIXME: currently, tiflash's join has different schema with TiDB, + // so we have to rebuild the schema of join and operators which may inherit schema from join. + // for window, we take the sub-plan's schema, and the schema generated by windowDescs. + columns := p.Schema().Clone().Columns[len(p.Schema().Columns)-len(p.WindowFuncDescs):] + p.schema = expression.MergeSchema(mpp.plan().Schema(), expression.NewSchema(columns...)) + + failpoint.Inject("CheckMPPWindowSchemaLength", func() { + if len(p.Schema().Columns) != len(mpp.plan().Schema().Columns)+len(p.WindowFuncDescs) { + panic("mpp physical window has incorrect schema length") + } + }) + + return attachPlan2Task(p, mpp) +} + +func (p *PhysicalWindow) attach2Task(tasks ...task) task { + if mpp, ok := tasks[0].copy().(*mppTask); ok && p.storeTp == kv.TiFlash { + return p.attach2TaskForMPP(mpp) + } + t := tasks[0].convertToRootTask(p.SCtx()) + return attachPlan2Task(p.self, t) +} + +func (p *PhysicalCTEStorage) attach2Task(tasks ...task) task { + t := tasks[0].copy() + if mpp, ok := t.(*mppTask); ok { + p.SetChildren(t.plan()) + return &mppTask{ + p: p, + partTp: mpp.partTp, + hashCols: mpp.hashCols, + tblColHists: mpp.tblColHists, + } + } + t.convertToRootTask(p.SCtx()) + p.SetChildren(t.plan()) + return &rootTask{ + p: p, + } +} + +func (p *PhysicalSequence) attach2Task(tasks ...task) task { + for _, t := range tasks { + _, isMpp := t.(*mppTask) + if !isMpp { + return tasks[len(tasks)-1] + } + } + + lastTask := tasks[len(tasks)-1].(*mppTask) + + children := make([]PhysicalPlan, 0, len(tasks)) + for _, t := range tasks { + children = append(children, t.plan()) + } + + p.SetChildren(children...) + + mppTask := &mppTask{ + p: p, + partTp: lastTask.partTp, + hashCols: lastTask.hashCols, + tblColHists: lastTask.tblColHists, + } + return mppTask +} + +// mppTask can not : +// 1. keep order +// 2. support double read +// 3. consider virtual columns. +// 4. TODO: partition prune after close +type mppTask struct { + p PhysicalPlan + + partTp property.MPPPartitionType + hashCols []*property.MPPPartitionColumn + + // rootTaskConds record filters of TableScan that cannot be pushed down to TiFlash. + + // For logical plan like: HashAgg -> Selection -> TableScan, if filters in Selection cannot be pushed to TiFlash. + // Planner will generate physical plan like: PhysicalHashAgg -> PhysicalSelection -> TableReader -> PhysicalTableScan(cop tiflash) + // Because planner will make mppTask invalid directly then use copTask directly. + + // But in DisaggregatedTiFlash mode, cop and batchCop protocol is disabled, so we have to consider this situation for mppTask. + // When generating PhysicalTableScan, if prop.TaskTp is RootTaskType, mppTask will be converted to rootTask, + // and filters in rootTaskConds will be added in a Selection which will be executed in TiDB. + // So physical plan be like: PhysicalHashAgg -> PhysicalSelection -> TableReader -> ExchangeSender -> PhysicalTableScan(mpp tiflash) + rootTaskConds []expression.Expression + tblColHists *statistics.HistColl +} + +func (t *mppTask) count() float64 { + return t.p.StatsInfo().RowCount +} + +func (t *mppTask) copy() task { + nt := *t + return &nt +} + +func (t *mppTask) plan() PhysicalPlan { + return t.p +} + +func (t *mppTask) invalid() bool { + return t.p == nil +} + +func (t *mppTask) convertToRootTask(ctx sessionctx.Context) *rootTask { + return t.copy().(*mppTask).convertToRootTaskImpl(ctx) +} + +// MemoryUsage return the memory usage of mppTask +func (t *mppTask) MemoryUsage() (sum int64) { + if t == nil { + return + } + + sum = size.SizeOfInterface + size.SizeOfInt + size.SizeOfSlice + int64(cap(t.hashCols))*size.SizeOfPointer + if t.p != nil { + sum += t.p.MemoryUsage() + } + return +} + +func collectPartitionInfosFromMPPPlan(p *PhysicalTableReader, mppPlan PhysicalPlan) { + switch x := mppPlan.(type) { + case *PhysicalTableScan: + p.PartitionInfos = append(p.PartitionInfos, tableScanAndPartitionInfo{x, x.PartitionInfo}) + default: + for _, ch := range mppPlan.Children() { + collectPartitionInfosFromMPPPlan(p, ch) + } + } +} + +func collectRowSizeFromMPPPlan(mppPlan PhysicalPlan) (rowSize float64) { + if mppPlan != nil && mppPlan.StatsInfo() != nil && mppPlan.StatsInfo().HistColl != nil { + return cardinality.GetAvgRowSize(mppPlan.SCtx(), mppPlan.StatsInfo().HistColl, mppPlan.Schema().Columns, false, false) + } + return 1 // use 1 as lower-bound for safety +} + +func accumulateNetSeekCost4MPP(p PhysicalPlan) (cost float64) { + if ts, ok := p.(*PhysicalTableScan); ok { + return float64(len(ts.Ranges)) * float64(len(ts.Columns)) * ts.SCtx().GetSessionVars().GetSeekFactor(ts.Table) + } + for _, c := range p.Children() { + cost += accumulateNetSeekCost4MPP(c) + } + return +} + +func tryExpandVirtualColumn(p PhysicalPlan) { + if ts, ok := p.(*PhysicalTableScan); ok { + ts.Columns = ExpandVirtualColumn(ts.Columns, ts.schema, ts.Table.Columns) + return + } + for _, child := range p.Children() { + tryExpandVirtualColumn(child) + } +} + +func (t *mppTask) convertToRootTaskImpl(ctx sessionctx.Context) *rootTask { + // In disaggregated-tiflash mode, need to consider generated column. + tryExpandVirtualColumn(t.p) + sender := PhysicalExchangeSender{ + ExchangeType: tipb.ExchangeType_PassThrough, + }.Init(ctx, t.p.StatsInfo()) + sender.SetChildren(t.p) + + p := PhysicalTableReader{ + tablePlan: sender, + StoreType: kv.TiFlash, + }.Init(ctx, t.p.SelectBlockOffset()) + p.SetStats(t.p.StatsInfo()) + collectPartitionInfosFromMPPPlan(p, t.p) + rt := &rootTask{ + p: p, + } + + if len(t.rootTaskConds) > 0 { + // Some Filter cannot be pushed down to TiFlash, need to add Selection in rootTask, + // so this Selection will be executed in TiDB. + _, isTableScan := t.p.(*PhysicalTableScan) + _, isSelection := t.p.(*PhysicalSelection) + if isSelection { + _, isTableScan = t.p.Children()[0].(*PhysicalTableScan) + } + if !isTableScan { + // Need to make sure oriTaskPlan is TableScan, because rootTaskConds is part of TableScan.FilterCondition. + // It's only for TableScan. This is ensured by converting mppTask to rootTask just after TableScan is built, + // so no other operators are added into this mppTask. + logutil.BgLogger().Error("expect Selection or TableScan for mppTask.p", zap.String("mppTask.p", t.p.TP())) + return invalidTask + } + selectivity, _, err := cardinality.Selectivity(ctx, t.tblColHists, t.rootTaskConds, nil) + if err != nil { + logutil.BgLogger().Debug("calculate selectivity failed, use selection factor", zap.Error(err)) + selectivity = SelectionFactor + } + sel := PhysicalSelection{Conditions: t.rootTaskConds}.Init(ctx, rt.p.StatsInfo().Scale(selectivity), rt.p.SelectBlockOffset()) + sel.fromDataSource = true + sel.SetChildren(rt.p) + rt.p = sel + } + return rt +} + +func (t *mppTask) needEnforceExchanger(prop *property.PhysicalProperty) bool { + switch prop.MPPPartitionTp { + case property.AnyType: + return false + case property.BroadcastType: + return true + case property.SinglePartitionType: + return t.partTp != property.SinglePartitionType + default: + if t.partTp != property.HashType { + return true + } + // TODO: consider equalivant class + // TODO: `prop.IsSubsetOf` is enough, instead of equal. + // for example, if already partitioned by hash(B,C), then same (A,B,C) must distribute on a same node. + if len(prop.MPPPartitionCols) != len(t.hashCols) { + return true + } + for i, col := range prop.MPPPartitionCols { + if !col.Equal(t.hashCols[i]) { + return true + } + } + return false + } +} + +func (t *mppTask) enforceExchanger(prop *property.PhysicalProperty) *mppTask { + if !t.needEnforceExchanger(prop) { + return t + } + return t.copy().(*mppTask).enforceExchangerImpl(prop) +} + +func (t *mppTask) enforceExchangerImpl(prop *property.PhysicalProperty) *mppTask { + if collate.NewCollationEnabled() && !t.p.SCtx().GetSessionVars().HashExchangeWithNewCollation && prop.MPPPartitionTp == property.HashType { + for _, col := range prop.MPPPartitionCols { + if types.IsString(col.Col.RetType.GetType()) { + t.p.SCtx().GetSessionVars().RaiseWarningWhenMPPEnforced("MPP mode may be blocked because when `new_collation_enabled` is true, HashJoin or HashAgg with string key is not supported now.") + return &mppTask{} + } + } + } + ctx := t.p.SCtx() + sender := PhysicalExchangeSender{ + ExchangeType: prop.MPPPartitionTp.ToExchangeType(), + HashCols: prop.MPPPartitionCols, + }.Init(ctx, t.p.StatsInfo()) + + if ctx.GetSessionVars().ChooseMppVersion() >= kv.MppVersionV1 { + sender.CompressionMode = ctx.GetSessionVars().ChooseMppExchangeCompressionMode() + } + + sender.SetChildren(t.p) + receiver := PhysicalExchangeReceiver{}.Init(ctx, t.p.StatsInfo()) + receiver.SetChildren(sender) + return &mppTask{ + p: receiver, + partTp: prop.MPPPartitionTp, + hashCols: prop.MPPPartitionCols, + } +} diff --git a/pkg/planner/core/telemetry.go b/pkg/planner/core/telemetry.go new file mode 100644 index 0000000000000..1d07f4a9ac543 --- /dev/null +++ b/pkg/planner/core/telemetry.go @@ -0,0 +1,55 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package core + +import ( + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/plancodec" +) + +// IsTiFlashContained returns whether the plan contains TiFlash related executors. +func IsTiFlashContained(plan Plan) (tiFlashPushDown, tiFlashExchangePushDown bool) { + if plan == nil { + return + } + var tiflashProcess func(p Plan) + tiflashProcess = func(p Plan) { + if exp, isExplain := p.(*Explain); isExplain { + p = exp.TargetPlan + if p == nil { + return + } + } + pp, isPhysical := p.(PhysicalPlan) + if !isPhysical { + return + } + if tableReader, ok := pp.(*PhysicalTableReader); ok { + tiFlashPushDown = tableReader.StoreType == kv.TiFlash + if tiFlashPushDown && tableReader.GetTablePlan().TP() == plancodec.TypeExchangeSender { + tiFlashExchangePushDown = true + } + return + } + for _, child := range pp.Children() { + tiflashProcess(child) + if tiFlashPushDown { + return + } + } + } + tiflashProcess(plan) + return +} diff --git a/planner/core/testdata/index_merge_suite_in.json b/pkg/planner/core/testdata/index_merge_suite_in.json similarity index 100% rename from planner/core/testdata/index_merge_suite_in.json rename to pkg/planner/core/testdata/index_merge_suite_in.json diff --git a/planner/core/testdata/index_merge_suite_out.json b/pkg/planner/core/testdata/index_merge_suite_out.json similarity index 100% rename from planner/core/testdata/index_merge_suite_out.json rename to pkg/planner/core/testdata/index_merge_suite_out.json diff --git a/planner/core/testdata/plan_suite_unexported_in.json b/pkg/planner/core/testdata/plan_suite_unexported_in.json similarity index 100% rename from planner/core/testdata/plan_suite_unexported_in.json rename to pkg/planner/core/testdata/plan_suite_unexported_in.json diff --git a/planner/core/testdata/plan_suite_unexported_out.json b/pkg/planner/core/testdata/plan_suite_unexported_out.json similarity index 100% rename from planner/core/testdata/plan_suite_unexported_out.json rename to pkg/planner/core/testdata/plan_suite_unexported_out.json diff --git a/planner/core/testdata/runtime_filter_generator_suite_in.json b/pkg/planner/core/testdata/runtime_filter_generator_suite_in.json similarity index 100% rename from planner/core/testdata/runtime_filter_generator_suite_in.json rename to pkg/planner/core/testdata/runtime_filter_generator_suite_in.json diff --git a/planner/core/testdata/runtime_filter_generator_suite_out.json b/pkg/planner/core/testdata/runtime_filter_generator_suite_out.json similarity index 100% rename from planner/core/testdata/runtime_filter_generator_suite_out.json rename to pkg/planner/core/testdata/runtime_filter_generator_suite_out.json diff --git a/pkg/planner/core/tests/prepare/BUILD.bazel b/pkg/planner/core/tests/prepare/BUILD.bazel new file mode 100644 index 0000000000000..106222cc3069d --- /dev/null +++ b/pkg/planner/core/tests/prepare/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "prepare_test", + timeout = "short", + srcs = [ + "main_test.go", + "prepare_test.go", + ], + flaky = True, + shard_count = 21, + deps = [ + "//pkg/errno", + "//pkg/executor", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/hint", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/core/tests/prepare/issue/BUILD.bazel b/pkg/planner/core/tests/prepare/issue/BUILD.bazel new file mode 100644 index 0000000000000..bdfc796445bc9 --- /dev/null +++ b/pkg/planner/core/tests/prepare/issue/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "issue_test", + timeout = "short", + srcs = [ + "issue_test.go", + "main_test.go", + ], + flaky = True, + deps = [ + "//pkg/parser/auth", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/core/tests/prepare/issue/issue_test.go b/pkg/planner/core/tests/prepare/issue/issue_test.go similarity index 98% rename from planner/core/tests/prepare/issue/issue_test.go rename to pkg/planner/core/tests/prepare/issue/issue_test.go index 72604d5f030ce..e3bbb26563855 100644 --- a/planner/core/tests/prepare/issue/issue_test.go +++ b/pkg/planner/core/tests/prepare/issue/issue_test.go @@ -17,8 +17,8 @@ package issue import ( "testing" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/planner/core/tests/prepare/issue/main_test.go b/pkg/planner/core/tests/prepare/issue/main_test.go new file mode 100644 index 0000000000000..cef07c27d30f9 --- /dev/null +++ b/pkg/planner/core/tests/prepare/issue/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package issue + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/planner/core/tests/prepare/main_test.go b/pkg/planner/core/tests/prepare/main_test.go new file mode 100644 index 0000000000000..f58acb5396398 --- /dev/null +++ b/pkg/planner/core/tests/prepare/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package prepare + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + flag.Parse() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/planner/core/tests/prepare/prepare_test.go b/pkg/planner/core/tests/prepare/prepare_test.go similarity index 99% rename from planner/core/tests/prepare/prepare_test.go rename to pkg/planner/core/tests/prepare/prepare_test.go index 431c908cdddfa..a681bdddc347f 100644 --- a/planner/core/tests/prepare/prepare_test.go +++ b/pkg/planner/core/tests/prepare/prepare_test.go @@ -24,19 +24,19 @@ import ( "testing" "time" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" diff --git a/planner/core/tiflash_selection_late_materialization.go b/pkg/planner/core/tiflash_selection_late_materialization.go similarity index 97% rename from planner/core/tiflash_selection_late_materialization.go rename to pkg/planner/core/tiflash_selection_late_materialization.go index ce303624a5598..2a06ae67376e6 100644 --- a/planner/core/tiflash_selection_late_materialization.go +++ b/pkg/planner/core/tiflash_selection_late_materialization.go @@ -18,12 +18,12 @@ import ( "cmp" "slices" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/planner/core/trace.go b/pkg/planner/core/trace.go new file mode 100644 index 0000000000000..e6c81dea4641a --- /dev/null +++ b/pkg/planner/core/trace.go @@ -0,0 +1,31 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package core + +import ( + "github.com/pingcap/tidb/pkg/parser/ast" +) + +// Trace represents a trace plan. +type Trace struct { + baseSchemaProducer + + StmtNode ast.StmtNode + Format string + + // OptimizerTrace indicates `trace plan target = 'xxx' ` case + OptimizerTrace bool + OptimizerTraceTarget string +} diff --git a/pkg/planner/core/util.go b/pkg/planner/core/util.go new file mode 100644 index 0000000000000..1b0a5c7e804f8 --- /dev/null +++ b/pkg/planner/core/util.go @@ -0,0 +1,474 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package core + +import ( + "fmt" + "slices" + "sort" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core/internal/base" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/set" + "github.com/pingcap/tidb/pkg/util/size" +) + +// AggregateFuncExtractor visits Expr tree. +// It collects AggregateFuncExpr from AST Node. +type AggregateFuncExtractor struct { + // skipAggMap stores correlated aggregate functions which have been built in outer query, + // so extractor in sub-query will skip these aggregate functions. + skipAggMap map[*ast.AggregateFuncExpr]*expression.CorrelatedColumn + // AggFuncs is the collected AggregateFuncExprs. + AggFuncs []*ast.AggregateFuncExpr +} + +// Enter implements Visitor interface. +func (*AggregateFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { + switch n.(type) { + case *ast.SelectStmt, *ast.SetOprStmt: + return n, true + } + return n, false +} + +// Leave implements Visitor interface. +func (a *AggregateFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { + //nolint: revive + switch v := n.(type) { + case *ast.AggregateFuncExpr: + if _, ok := a.skipAggMap[v]; !ok { + a.AggFuncs = append(a.AggFuncs, v) + } + } + return n, true +} + +// WindowFuncExtractor visits Expr tree. +// It converts ColunmNameExpr to WindowFuncExpr and collects WindowFuncExpr. +type WindowFuncExtractor struct { + // WindowFuncs is the collected WindowFuncExprs. + windowFuncs []*ast.WindowFuncExpr +} + +// Enter implements Visitor interface. +func (*WindowFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { + switch n.(type) { + case *ast.SelectStmt, *ast.SetOprStmt: + return n, true + } + return n, false +} + +// Leave implements Visitor interface. +func (a *WindowFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { + //nolint: revive + switch v := n.(type) { + case *ast.WindowFuncExpr: + a.windowFuncs = append(a.windowFuncs, v) + } + return n, true +} + +// logicalSchemaProducer stores the schema for the logical plans who can produce schema directly. +type logicalSchemaProducer struct { + schema *expression.Schema + names types.NameSlice + baseLogicalPlan +} + +// Schema implements the Plan.Schema interface. +func (s *logicalSchemaProducer) Schema() *expression.Schema { + if s.schema == nil { + if len(s.Children()) == 1 { + // default implementation for plans has only one child: proprgate child schema. + // multi-children plans are likely to have particular implementation. + s.schema = s.Children()[0].Schema().Clone() + } else { + s.schema = expression.NewSchema() + } + } + return s.schema +} + +func (s *logicalSchemaProducer) OutputNames() types.NameSlice { + if s.names == nil && len(s.Children()) == 1 { + // default implementation for plans has only one child: proprgate child `OutputNames`. + // multi-children plans are likely to have particular implementation. + s.names = s.Children()[0].OutputNames() + } + return s.names +} + +func (s *logicalSchemaProducer) SetOutputNames(names types.NameSlice) { + s.names = names +} + +// SetSchema implements the Plan.SetSchema interface. +func (s *logicalSchemaProducer) SetSchema(schema *expression.Schema) { + s.schema = schema +} + +func (s *logicalSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { + s.schema = schema + s.names = names +} + +// inlineProjection prunes unneeded columns inline a executor. +func (s *logicalSchemaProducer) inlineProjection(parentUsedCols []*expression.Column, opt *logicalOptimizeOp) { + prunedColumns := make([]*expression.Column, 0) + used := expression.GetUsedList(parentUsedCols, s.Schema()) + for i := len(used) - 1; i >= 0; i-- { + if !used[i] { + prunedColumns = append(prunedColumns, s.Schema().Columns[i]) + s.schema.Columns = append(s.Schema().Columns[:i], s.Schema().Columns[i+1:]...) + } + } + appendColumnPruneTraceStep(s.self, prunedColumns, opt) +} + +// physicalSchemaProducer stores the schema for the physical plans who can produce schema directly. +type physicalSchemaProducer struct { + schema *expression.Schema + basePhysicalPlan +} + +func (s *physicalSchemaProducer) cloneWithSelf(newSelf PhysicalPlan) (*physicalSchemaProducer, error) { + base, err := s.basePhysicalPlan.cloneWithSelf(newSelf) + if err != nil { + return nil, err + } + return &physicalSchemaProducer{ + basePhysicalPlan: *base, + schema: s.Schema().Clone(), + }, nil +} + +// Schema implements the Plan.Schema interface. +func (s *physicalSchemaProducer) Schema() *expression.Schema { + if s.schema == nil { + if len(s.Children()) == 1 { + // default implementation for plans has only one child: proprgate child schema. + // multi-children plans are likely to have particular implementation. + s.schema = s.Children()[0].Schema().Clone() + } else { + s.schema = expression.NewSchema() + } + } + return s.schema +} + +// SetSchema implements the Plan.SetSchema interface. +func (s *physicalSchemaProducer) SetSchema(schema *expression.Schema) { + s.schema = schema +} + +// MemoryUsage return the memory usage of physicalSchemaProducer +func (s *physicalSchemaProducer) MemoryUsage() (sum int64) { + if s == nil { + return + } + + sum = s.basePhysicalPlan.MemoryUsage() + size.SizeOfPointer + return +} + +// baseSchemaProducer stores the schema for the base plans who can produce schema directly. +type baseSchemaProducer struct { + schema *expression.Schema + names types.NameSlice + base.Plan +} + +// OutputNames returns the outputting names of each column. +func (s *baseSchemaProducer) OutputNames() types.NameSlice { + return s.names +} + +func (s *baseSchemaProducer) SetOutputNames(names types.NameSlice) { + s.names = names +} + +// Schema implements the Plan.Schema interface. +func (s *baseSchemaProducer) Schema() *expression.Schema { + if s.schema == nil { + s.schema = expression.NewSchema() + } + return s.schema +} + +// SetSchema implements the Plan.SetSchema interface. +func (s *baseSchemaProducer) SetSchema(schema *expression.Schema) { + s.schema = schema +} + +func (s *baseSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { + s.schema = schema + s.names = names +} + +// MemoryUsage return the memory usage of baseSchemaProducer +func (s *baseSchemaProducer) MemoryUsage() (sum int64) { + if s == nil { + return + } + + sum = size.SizeOfPointer + size.SizeOfSlice + int64(cap(s.names))*size.SizeOfPointer + s.Plan.MemoryUsage() + if s.schema != nil { + sum += s.schema.MemoryUsage() + } + for _, name := range s.names { + sum += name.MemoryUsage() + } + return +} + +// Schema implements the Plan.Schema interface. +func (p *LogicalMaxOneRow) Schema() *expression.Schema { + s := p.Children()[0].Schema().Clone() + resetNotNullFlag(s, 0, s.Len()) + return s +} + +func buildLogicalJoinSchema(joinType JoinType, join LogicalPlan) *expression.Schema { + leftSchema := join.Children()[0].Schema() + switch joinType { + case SemiJoin, AntiSemiJoin: + return leftSchema.Clone() + case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: + newSchema := leftSchema.Clone() + newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) + return newSchema + } + newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema()) + if joinType == LeftOuterJoin { + resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len()) + } else if joinType == RightOuterJoin { + resetNotNullFlag(newSchema, 0, leftSchema.Len()) + } + return newSchema +} + +// BuildPhysicalJoinSchema builds the schema of PhysicalJoin from it's children's schema. +func BuildPhysicalJoinSchema(joinType JoinType, join PhysicalPlan) *expression.Schema { + leftSchema := join.Children()[0].Schema() + switch joinType { + case SemiJoin, AntiSemiJoin: + return leftSchema.Clone() + case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: + newSchema := leftSchema.Clone() + newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) + return newSchema + } + newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema()) + if joinType == LeftOuterJoin { + resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len()) + } else if joinType == RightOuterJoin { + resetNotNullFlag(newSchema, 0, leftSchema.Len()) + } + return newSchema +} + +// GetStatsInfoFromFlatPlan gets the statistics info from a FlatPhysicalPlan. +func GetStatsInfoFromFlatPlan(flat *FlatPhysicalPlan) map[string]uint64 { + res := make(map[string]uint64) + for _, op := range flat.Main { + switch p := op.Origin.(type) { + case *PhysicalIndexScan: + if _, ok := res[p.Table.Name.O]; p.StatsInfo() != nil && !ok { + res[p.Table.Name.O] = p.StatsInfo().StatsVersion + } + case *PhysicalTableScan: + if _, ok := res[p.Table.Name.O]; p.StatsInfo() != nil && !ok { + res[p.Table.Name.O] = p.StatsInfo().StatsVersion + } + } + } + return res +} + +// GetStatsInfo gets the statistics info from a physical plan tree. +// Deprecated: FlattenPhysicalPlan() + GetStatsInfoFromFlatPlan() is preferred. +func GetStatsInfo(i interface{}) map[string]uint64 { + if i == nil { + // it's a workaround for https://github.com/pingcap/tidb/issues/17419 + // To entirely fix this, uncomment the assertion in TestPreparedIssue17419 + return nil + } + p := i.(Plan) + var physicalPlan PhysicalPlan + switch x := p.(type) { + case *Insert: + physicalPlan = x.SelectPlan + case *Update: + physicalPlan = x.SelectPlan + case *Delete: + physicalPlan = x.SelectPlan + case PhysicalPlan: + physicalPlan = x + } + + if physicalPlan == nil { + return nil + } + + statsInfos := make(map[string]uint64) + statsInfos = CollectPlanStatsVersion(physicalPlan, statsInfos) + return statsInfos +} + +// extractStringFromStringSet helps extract string info from set.StringSet. +func extractStringFromStringSet(set set.StringSet) string { + if len(set) < 1 { + return "" + } + l := make([]string, 0, len(set)) + for k := range set { + l = append(l, fmt.Sprintf(`"%s"`, k)) + } + slices.Sort(l) + return strings.Join(l, ",") +} + +// extractStringFromStringSlice helps extract string info from []string. +func extractStringFromStringSlice(ss []string) string { + if len(ss) < 1 { + return "" + } + slices.Sort(ss) + return strings.Join(ss, ",") +} + +// extractStringFromUint64Slice helps extract string info from uint64 slice. +func extractStringFromUint64Slice(slice []uint64) string { + if len(slice) < 1 { + return "" + } + l := make([]string, 0, len(slice)) + for _, k := range slice { + l = append(l, fmt.Sprintf(`%d`, k)) + } + slices.Sort(l) + return strings.Join(l, ",") +} + +// extractStringFromBoolSlice helps extract string info from bool slice. +func extractStringFromBoolSlice(slice []bool) string { + if len(slice) < 1 { + return "" + } + l := make([]string, 0, len(slice)) + for _, k := range slice { + l = append(l, fmt.Sprintf(`%t`, k)) + } + slices.Sort(l) + return strings.Join(l, ",") +} + +func tableHasDirtyContent(ctx sessionctx.Context, tableInfo *model.TableInfo) bool { + pi := tableInfo.GetPartitionInfo() + if pi == nil { + return ctx.HasDirtyContent(tableInfo.ID) + } + // Currently, we add UnionScan on every partition even though only one partition's data is changed. + // This is limited by current implementation of Partition Prune. It'll be updated once we modify that part. + for _, partition := range pi.Definitions { + if ctx.HasDirtyContent(partition.ID) { + return true + } + } + return false +} + +func clonePhysicalPlan(plans []PhysicalPlan) ([]PhysicalPlan, error) { + cloned := make([]PhysicalPlan, 0, len(plans)) + for _, p := range plans { + c, err := p.Clone() + if err != nil { + return nil, err + } + cloned = append(cloned, c) + } + return cloned, nil +} + +// GetPhysID returns the physical table ID. +func GetPhysID(tblInfo *model.TableInfo, partitionExpr *tables.PartitionExpr, colPos int, d types.Datum) (int64, error) { + pi := tblInfo.GetPartitionInfo() + if pi == nil { + return tblInfo.ID, nil + } + + if partitionExpr == nil { + return tblInfo.ID, nil + } + + switch pi.Type { + case model.PartitionTypeHash: + intVal := d.GetInt64() + partIdx := mathutil.Abs(intVal % int64(pi.Num)) + return pi.Definitions[partIdx].ID, nil + case model.PartitionTypeKey: + if partitionExpr.ForKeyPruning == nil || + len(pi.Columns) > 1 { + return 0, errors.Errorf("unsupported partition type in BatchGet") + } + newKeyPartExpr := tables.ForKeyPruning{ + KeyPartCols: []*expression.Column{{ + Index: colPos, + UniqueID: partitionExpr.KeyPartCols[0].UniqueID, + }}, + } + partIdx, err := newKeyPartExpr.LocateKeyPartition(pi.Num, []types.Datum{d}) + if err != nil { + return 0, errors.Errorf("unsupported partition type in BatchGet") + } + return pi.Definitions[partIdx].ID, nil + case model.PartitionTypeRange: + // we've check the type assertions in func TryFastPlan + col, ok := partitionExpr.Expr.(*expression.Column) + if !ok { + return 0, errors.Errorf("unsupported partition type in BatchGet") + } + unsigned := mysql.HasUnsignedFlag(col.GetType().GetFlag()) + ranges := partitionExpr.ForRangePruning + length := len(ranges.LessThan) + intVal := d.GetInt64() + partIdx := sort.Search(length, func(i int) bool { + return ranges.Compare(i, intVal, unsigned) > 0 + }) + if partIdx >= 0 && partIdx < length { + return pi.Definitions[partIdx].ID, nil + } + case model.PartitionTypeList: + isNull := false // we've guaranteed this in the build process of either TryFastPlan or buildBatchPointGet + intVal := d.GetInt64() + partIdx := partitionExpr.ForListPruning.LocatePartition(intVal, isNull) + if partIdx >= 0 { + return pi.Definitions[partIdx].ID, nil + } + } + + return 0, errors.Errorf("dual partition") +} diff --git a/pkg/planner/funcdep/BUILD.bazel b/pkg/planner/funcdep/BUILD.bazel new file mode 100644 index 0000000000000..f746f3ca098c3 --- /dev/null +++ b/pkg/planner/funcdep/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "funcdep", + srcs = [ + "doc.go", + "fast_int_set.go", + "fd_graph.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/funcdep", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/logutil", + "@org_golang_x_tools//container/intsets", + ], +) + +go_test( + name = "funcdep_test", + timeout = "short", + srcs = [ + "extract_fd_test.go", + "fast_int_set_bench_test.go", + "fast_int_set_test.go", + "fd_graph_test.go", + "main_test.go", + ], + embed = [":funcdep"], + flaky = True, + shard_count = 15, + deps = [ + "//pkg/domain", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessiontxn", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/hint", + "@com_github_stretchr_testify//require", + "@org_golang_x_exp//maps", + "@org_golang_x_tools//container/intsets", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/funcdep/doc.go b/pkg/planner/funcdep/doc.go similarity index 100% rename from planner/funcdep/doc.go rename to pkg/planner/funcdep/doc.go diff --git a/planner/funcdep/extract_fd_test.go b/pkg/planner/funcdep/extract_fd_test.go similarity index 98% rename from planner/funcdep/extract_fd_test.go rename to pkg/planner/funcdep/extract_fd_test.go index f7656aca0319c..0e7b27196e2f9 100644 --- a/planner/funcdep/extract_fd_test.go +++ b/pkg/planner/funcdep/extract_fd_test.go @@ -19,14 +19,14 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/hint" "github.com/stretchr/testify/require" ) diff --git a/planner/funcdep/fast_int_set.go b/pkg/planner/funcdep/fast_int_set.go similarity index 100% rename from planner/funcdep/fast_int_set.go rename to pkg/planner/funcdep/fast_int_set.go diff --git a/planner/funcdep/fast_int_set_bench_test.go b/pkg/planner/funcdep/fast_int_set_bench_test.go similarity index 100% rename from planner/funcdep/fast_int_set_bench_test.go rename to pkg/planner/funcdep/fast_int_set_bench_test.go diff --git a/planner/funcdep/fast_int_set_test.go b/pkg/planner/funcdep/fast_int_set_test.go similarity index 100% rename from planner/funcdep/fast_int_set_test.go rename to pkg/planner/funcdep/fast_int_set_test.go diff --git a/planner/funcdep/fd_graph.go b/pkg/planner/funcdep/fd_graph.go similarity index 99% rename from planner/funcdep/fd_graph.go rename to pkg/planner/funcdep/fd_graph.go index 14eef66e47b22..0818a474ddca6 100644 --- a/planner/funcdep/fd_graph.go +++ b/pkg/planner/funcdep/fd_graph.go @@ -18,7 +18,7 @@ import ( "fmt" "strings" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" ) type fdEdge struct { diff --git a/planner/funcdep/fd_graph_test.go b/pkg/planner/funcdep/fd_graph_test.go similarity index 100% rename from planner/funcdep/fd_graph_test.go rename to pkg/planner/funcdep/fd_graph_test.go diff --git a/pkg/planner/funcdep/main_test.go b/pkg/planner/funcdep/main_test.go new file mode 100644 index 0000000000000..61bf6ff8367f7 --- /dev/null +++ b/pkg/planner/funcdep/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package funcdep + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/planner/implementation/BUILD.bazel b/pkg/planner/implementation/BUILD.bazel new file mode 100644 index 0000000000000..77ac6b0767bff --- /dev/null +++ b/pkg/planner/implementation/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "implementation", + srcs = [ + "base.go", + "datasource.go", + "join.go", + "simple_plans.go", + "sort.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/implementation", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/planner/cardinality", + "//pkg/planner/core", + "//pkg/planner/memo", + "//pkg/statistics", + ], +) + +go_test( + name = "implementation_test", + timeout = "short", + srcs = [ + "base_test.go", + "main_test.go", + ], + embed = [":implementation"], + flaky = True, + deps = [ + "//pkg/domain", + "//pkg/planner/core", + "//pkg/planner/memo", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/planner/implementation/base.go b/pkg/planner/implementation/base.go new file mode 100644 index 0000000000000..37bd970dc381d --- /dev/null +++ b/pkg/planner/implementation/base.go @@ -0,0 +1,66 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package implementation + +import ( + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" +) + +type baseImpl struct { + cost float64 + plan plannercore.PhysicalPlan +} + +func (impl *baseImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { + impl.cost = 0 + for _, child := range children { + impl.cost += child.GetCost() + } + return impl.cost +} + +func (impl *baseImpl) SetCost(cost float64) { + impl.cost = cost +} + +func (impl *baseImpl) GetCost() float64 { + return impl.cost +} + +func (impl *baseImpl) GetPlan() plannercore.PhysicalPlan { + return impl.plan +} + +func (impl *baseImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { + childrenPlan := make([]plannercore.PhysicalPlan, len(children)) + for i, child := range children { + childrenPlan[i] = child.GetPlan() + } + impl.plan.SetChildren(childrenPlan...) + return impl +} + +func (*baseImpl) ScaleCostLimit(costLimit float64) float64 { + return costLimit +} + +func (*baseImpl) GetCostLimit(costLimit float64, children ...memo.Implementation) float64 { + childrenCost := 0.0 + for _, child := range children { + childrenCost += child.GetCost() + } + return costLimit - childrenCost +} diff --git a/pkg/planner/implementation/base_test.go b/pkg/planner/implementation/base_test.go new file mode 100644 index 0000000000000..02f3d6b6fd0f1 --- /dev/null +++ b/pkg/planner/implementation/base_test.go @@ -0,0 +1,43 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package implementation + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/domain" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/stretchr/testify/require" + "go.opencensus.io/stats/view" +) + +func TestBaseImplementation(t *testing.T) { + defer view.Stop() + sctx := plannercore.MockContext() + defer func() { + domain.GetDomain(sctx).StatsHandle().Close() + }() + p := plannercore.PhysicalLimit{}.Init(sctx, nil, 0, nil) + impl := &baseImpl{plan: p} + require.Equal(t, p, impl.GetPlan()) + + cost := impl.CalcCost(10, []memo.Implementation{}...) + require.Equal(t, 0.0, cost) + require.Equal(t, 0.0, impl.GetCost()) + + impl.SetCost(6.0) + require.Equal(t, 6.0, impl.GetCost()) +} diff --git a/planner/implementation/datasource.go b/pkg/planner/implementation/datasource.go similarity index 95% rename from planner/implementation/datasource.go rename to pkg/planner/implementation/datasource.go index ae23134119ee1..b9c70e0959c3c 100644 --- a/planner/implementation/datasource.go +++ b/pkg/planner/implementation/datasource.go @@ -17,13 +17,13 @@ package implementation import ( "math" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/statistics" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" + "github.com/pingcap/tidb/pkg/statistics" ) // TableDualImpl implementation of PhysicalTableDual. diff --git a/pkg/planner/implementation/join.go b/pkg/planner/implementation/join.go new file mode 100644 index 0000000000000..3f9313326107d --- /dev/null +++ b/pkg/planner/implementation/join.go @@ -0,0 +1,74 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package implementation + +import ( + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" +) + +// HashJoinImpl is the implementation for PhysicalHashJoin. +type HashJoinImpl struct { + baseImpl +} + +// CalcCost implements Implementation CalcCost interface. +func (impl *HashJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { + hashJoin := impl.plan.(*plannercore.PhysicalHashJoin) + // The children here are only used to calculate the cost. + hashJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) + selfCost := hashJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), false, 0, nil) + impl.cost = selfCost + children[0].GetCost() + children[1].GetCost() + return impl.cost +} + +// AttachChildren implements Implementation AttachChildren interface. +func (impl *HashJoinImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { + hashJoin := impl.plan.(*plannercore.PhysicalHashJoin) + hashJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) + return impl +} + +// NewHashJoinImpl creates a new HashJoinImpl. +func NewHashJoinImpl(hashJoin *plannercore.PhysicalHashJoin) *HashJoinImpl { + return &HashJoinImpl{baseImpl{plan: hashJoin}} +} + +// MergeJoinImpl is the implementation for PhysicalMergeJoin. +type MergeJoinImpl struct { + baseImpl +} + +// CalcCost implements Implementation CalcCost interface. +func (impl *MergeJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { + mergeJoin := impl.plan.(*plannercore.PhysicalMergeJoin) + // The children here are only used to calculate the cost. + mergeJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) + selfCost := mergeJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), 0) + impl.cost = selfCost + children[0].GetCost() + children[1].GetCost() + return impl.cost +} + +// AttachChildren implements Implementation AttachChildren interface. +func (impl *MergeJoinImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { + mergeJoin := impl.plan.(*plannercore.PhysicalMergeJoin) + mergeJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) + return impl +} + +// NewMergeJoinImpl creates a new MergeJoinImpl. +func NewMergeJoinImpl(mergeJoin *plannercore.PhysicalMergeJoin) *MergeJoinImpl { + return &MergeJoinImpl{baseImpl{plan: mergeJoin}} +} diff --git a/pkg/planner/implementation/main_test.go b/pkg/planner/implementation/main_test.go new file mode 100644 index 0000000000000..ed8082971b249 --- /dev/null +++ b/pkg/planner/implementation/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package implementation + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/planner/implementation/simple_plans.go b/pkg/planner/implementation/simple_plans.go similarity index 98% rename from planner/implementation/simple_plans.go rename to pkg/planner/implementation/simple_plans.go index 12b0e3b14ffe0..283cf151412e9 100644 --- a/planner/implementation/simple_plans.go +++ b/pkg/planner/implementation/simple_plans.go @@ -15,8 +15,8 @@ package implementation import ( - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" ) // ProjectionImpl is the implementation of PhysicalProjection. diff --git a/pkg/planner/implementation/sort.go b/pkg/planner/implementation/sort.go new file mode 100644 index 0000000000000..a3cbad7fa29ba --- /dev/null +++ b/pkg/planner/implementation/sort.go @@ -0,0 +1,65 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package implementation + +import ( + "math" + + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/memo" +) + +// SortImpl implementation of PhysicalSort. +type SortImpl struct { + baseImpl +} + +// NewSortImpl creates a new sort Implementation. +func NewSortImpl(sort *plannercore.PhysicalSort) *SortImpl { + return &SortImpl{baseImpl{plan: sort}} +} + +// CalcCost calculates the cost of the sort Implementation. +func (impl *SortImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { + cnt := math.Min(children[0].GetPlan().StatsInfo().RowCount, impl.plan.GetChildReqProps(0).ExpectedCnt) + sort := impl.plan.(*plannercore.PhysicalSort) + impl.cost = sort.GetCost(cnt, children[0].GetPlan().Schema()) + children[0].GetCost() + return impl.cost +} + +// AttachChildren implements Implementation AttachChildren interface. +func (impl *SortImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { + sort := impl.plan.(*plannercore.PhysicalSort) + sort.SetChildren(children[0].GetPlan()) + // When the Sort orderByItems contain ScalarFunction, we need + // to inject two Projections below and above the Sort. + impl.plan = plannercore.InjectProjBelowSort(sort, sort.ByItems) + return impl +} + +// NominalSortImpl is the implementation of NominalSort. +type NominalSortImpl struct { + baseImpl +} + +// AttachChildren implements Implementation AttachChildren interface. +func (*NominalSortImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { + return children[0] +} + +// NewNominalSortImpl creates a new NominalSort Implementation. +func NewNominalSortImpl(sort *plannercore.NominalSort) *NominalSortImpl { + return &NominalSortImpl{baseImpl{plan: sort}} +} diff --git a/pkg/planner/memo/BUILD.bazel b/pkg/planner/memo/BUILD.bazel new file mode 100644 index 0000000000000..dd538beb3c894 --- /dev/null +++ b/pkg/planner/memo/BUILD.bazel @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "memo", + srcs = [ + "expr_iterator.go", + "group.go", + "group_expr.go", + "implementation.go", + "pattern.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/memo", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/planner/core", + "//pkg/planner/property", + ], +) + +go_test( + name = "memo_test", + timeout = "short", + srcs = [ + "expr_iterator_test.go", + "group_expr_test.go", + "group_test.go", + "main_test.go", + "pattern_test.go", + ], + embed = [":memo"], + flaky = True, + shard_count = 22, + deps = [ + "//pkg/domain", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/planner/property", + "//pkg/sessionctx/variable", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/memo/expr_iterator.go b/pkg/planner/memo/expr_iterator.go similarity index 100% rename from planner/memo/expr_iterator.go rename to pkg/planner/memo/expr_iterator.go diff --git a/planner/memo/expr_iterator_test.go b/pkg/planner/memo/expr_iterator_test.go similarity index 98% rename from planner/memo/expr_iterator_test.go rename to pkg/planner/memo/expr_iterator_test.go index 37282457f4413..e5cd97223dc9f 100644 --- a/planner/memo/expr_iterator_test.go +++ b/pkg/planner/memo/expr_iterator_test.go @@ -17,9 +17,9 @@ package memo import ( "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/pkg/planner/memo/group.go b/pkg/planner/memo/group.go new file mode 100644 index 0000000000000..62b69c16e3ee4 --- /dev/null +++ b/pkg/planner/memo/group.go @@ -0,0 +1,272 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package memo + +import ( + "container/list" + "fmt" + + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/property" +) + +// EngineType is determined by whether it's above or below `Gather`s. +// Plan will choose the different engine to be implemented/executed on according to its EngineType. +// Different engine may support different operators with different cost, so we should design +// different transformation and implementation rules for each engine. +type EngineType uint + +const ( + // EngineTiDB stands for groups which is above `Gather`s and will be executed in TiDB layer. + EngineTiDB EngineType = 1 << iota + // EngineTiKV stands for groups which is below `Gather`s and will be executed in TiKV layer. + EngineTiKV + // EngineTiFlash stands for groups which is below `Gather`s and will be executed in TiFlash layer. + EngineTiFlash +) + +// EngineTypeSet is the bit set of EngineTypes. +type EngineTypeSet uint + +const ( + // EngineTiDBOnly is the EngineTypeSet for EngineTiDB only. + EngineTiDBOnly = EngineTypeSet(EngineTiDB) + // EngineTiKVOnly is the EngineTypeSet for EngineTiKV only. + EngineTiKVOnly = EngineTypeSet(EngineTiKV) + // EngineTiFlashOnly is the EngineTypeSet for EngineTiFlash only. + EngineTiFlashOnly = EngineTypeSet(EngineTiFlash) + // EngineTiKVOrTiFlash is the EngineTypeSet for (EngineTiKV | EngineTiFlash). + EngineTiKVOrTiFlash = EngineTypeSet(EngineTiKV | EngineTiFlash) + // EngineAll is the EngineTypeSet for all of the EngineTypes. + EngineAll = EngineTypeSet(EngineTiDB | EngineTiKV | EngineTiFlash) +) + +// Contains checks whether the EngineTypeSet contains the EngineType. +func (e EngineTypeSet) Contains(tp EngineType) bool { + return uint(e)&uint(tp) != 0 +} + +// String implements fmt.Stringer interface. +func (e EngineType) String() string { + switch e { + case EngineTiDB: + return "EngineTiDB" + case EngineTiKV: + return "EngineTiKV" + case EngineTiFlash: + return "EngineTiFlash" + } + return "UnknownEngineType" +} + +// ExploreMark is uses to mark whether a Group or GroupExpr has +// been fully explored by a transformation rule batch. +type ExploreMark int + +// SetExplored sets the roundth bit. +func (m *ExploreMark) SetExplored(round int) { + *m |= 1 << round +} + +// SetUnexplored unsets the roundth bit. +func (m *ExploreMark) SetUnexplored(round int) { + *m &= ^(1 << round) +} + +// Explored returns whether the roundth bit has been set. +func (m *ExploreMark) Explored(round int) bool { + return *m&(1< 0 { + sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("SET_VAR is used in the SQL")) + } + + txnManger := sessiontxn.GetTxnManager(sctx) + if _, isolationReadContainTiKV := sessVars.IsolationReadEngines[kv.TiKV]; isolationReadContainTiKV { + var fp core.Plan + if fpv, ok := sctx.Value(core.PointPlanKey).(core.PointPlanVal); ok { + // point plan is already tried in a multi-statement query. + fp = fpv.Plan + } else { + fp = core.TryFastPlan(sctx, node) + } + if fp != nil { + return fp, fp.OutputNames(), nil + } + } + if err := txnManger.AdviseWarmup(); err != nil { + return nil, nil, err + } + + enableUseBinding := sessVars.UsePlanBaselines + stmtNode, isStmtNode := node.(ast.StmtNode) + bindRecord, scope, match := matchSQLBinding(sctx, stmtNode) + useBinding := enableUseBinding && isStmtNode && match + if sessVars.StmtCtx.EnableOptimizerDebugTrace { + failpoint.Inject("SetBindingTimeToZero", func(val failpoint.Value) { + if val.(bool) && bindRecord != nil { + bindRecord = bindRecord.Copy() + for i := range bindRecord.Bindings { + bindRecord.Bindings[i].CreateTime = types.ZeroTime + bindRecord.Bindings[i].UpdateTime = types.ZeroTime + } + } + }) + debugtrace.RecordAnyValuesWithNames(sctx, + "Used binding", useBinding, + "Enable binding", enableUseBinding, + "IsStmtNode", isStmtNode, + "Matched", match, + "Scope", scope, + "Matched bindings", bindRecord, + ) + } + if isStmtNode { + // add the extra Limit after matching the bind record + stmtNode = core.TryAddExtraLimit(sctx, stmtNode) + node = stmtNode + } + + // try to get Plan from the NonPrepared Plan Cache + if sctx.GetSessionVars().EnableNonPreparedPlanCache && + isStmtNode && + !useBinding { // TODO: support binding + cachedPlan, names, ok, err := getPlanFromNonPreparedPlanCache(ctx, sctx, stmtNode, is) + if err != nil { + return nil, nil, err + } + if ok { + return cachedPlan, names, nil + } + } + + var ( + names types.NameSlice + bestPlan, bestPlanFromBind core.Plan + chosenBinding bindinfo.Binding + err error + ) + if useBinding { + minCost := math.MaxFloat64 + var bindStmtHints stmtctx.StmtHints + originHints := hint.CollectHint(stmtNode) + // bindRecord must be not nil when coming here, try to find the best binding. + for _, binding := range bindRecord.Bindings { + if !binding.IsBindingEnabled() { + continue + } + if sessVars.StmtCtx.EnableOptimizerDebugTrace { + core.DebugTraceTryBinding(sctx, binding.Hint) + } + metrics.BindUsageCounter.WithLabelValues(scope).Inc() + hint.BindHint(stmtNode, binding.Hint) + curStmtHints, _, curWarns := handleStmtHints(binding.Hint.GetFirstTableHints()) + sessVars.StmtCtx.StmtHints = curStmtHints + // update session var by hint /set_var/ + for name, val := range sessVars.StmtCtx.StmtHints.SetVars { + oldV, err := sessVars.SetSystemVarWithOldValAsRet(name, val) + if err != nil { + sessVars.StmtCtx.AppendWarning(err) + } + sessVars.StmtCtx.AddSetVarHintRestore(name, oldV) + } + plan, curNames, cost, err := optimize(ctx, sctx, node, is) + if err != nil { + binding.Status = bindinfo.Invalid + handleInvalidBindRecord(ctx, sctx, scope, bindinfo.BindRecord{ + OriginalSQL: bindRecord.OriginalSQL, + Db: bindRecord.Db, + Bindings: []bindinfo.Binding{binding}, + }) + continue + } + if cost < minCost { + bindStmtHints, warns, minCost, names, bestPlanFromBind, chosenBinding = curStmtHints, curWarns, cost, curNames, plan, binding + } + } + if bestPlanFromBind == nil { + sessVars.StmtCtx.AppendWarning(errors.New("no plan generated from bindings")) + } else { + bestPlan = bestPlanFromBind + sessVars.StmtCtx.StmtHints = bindStmtHints + for _, warn := range warns { + sessVars.StmtCtx.AppendWarning(warn) + } + sessVars.StmtCtx.BindSQL = chosenBinding.BindSQL + sessVars.FoundInBinding = true + if sessVars.StmtCtx.InVerboseExplain { + sessVars.StmtCtx.AppendNote(errors.Errorf("Using the bindSQL: %v", chosenBinding.BindSQL)) + } else { + sessVars.StmtCtx.AppendExtraNote(errors.Errorf("Using the bindSQL: %v", chosenBinding.BindSQL)) + } + if len(tableHints) > 0 { + sessVars.StmtCtx.AppendWarning(errors.Errorf("The system ignores the hints in the current query and uses the hints specified in the bindSQL: %v", chosenBinding.BindSQL)) + } + } + // Restore the hint to avoid changing the stmt node. + hint.BindHint(stmtNode, originHints) + } + + if sessVars.StmtCtx.EnableOptimizerDebugTrace && bestPlanFromBind != nil { + core.DebugTraceBestBinding(sctx, chosenBinding.Hint) + } + // No plan found from the bindings, or the bindings are ignored. + if bestPlan == nil { + sessVars.StmtCtx.StmtHints = originStmtHints + bestPlan, names, _, err = optimize(ctx, sctx, node, is) + if err != nil { + return nil, nil, err + } + } + + // Add a baseline evolution task if: + // 1. the returned plan is from bindings; + // 2. the query is a select statement; + // 3. the original binding contains no read_from_storage hint; + // 4. the plan when ignoring bindings contains no tiflash hint; + // 5. the pending verified binding has not been added already; + savedStmtHints := sessVars.StmtCtx.StmtHints + defer func() { + sessVars.StmtCtx.StmtHints = savedStmtHints + }() + if sessVars.EvolvePlanBaselines && bestPlanFromBind != nil && + sessVars.SelectLimit == math.MaxUint64 { // do not evolve this query if sql_select_limit is enabled + // Check bestPlanFromBind firstly to avoid nil stmtNode. + if _, ok := stmtNode.(*ast.SelectStmt); ok && !bindRecord.Bindings[0].Hint.ContainTableHint(core.HintReadFromStorage) { + sessVars.StmtCtx.StmtHints = originStmtHints + defPlan, _, _, err := optimize(ctx, sctx, node, is) + if err != nil { + // Ignore this evolution task. + return bestPlan, names, nil + } + defPlanHints := core.GenHintsFromPhysicalPlan(defPlan) + for _, hint := range defPlanHints { + if hint.HintName.String() == core.HintReadFromStorage { + return bestPlan, names, nil + } + } + // The hints generated from the plan do not contain the statement hints of the query, add them back. + for _, off := range originStmtHintsOffs { + defPlanHints = append(defPlanHints, tableHints[off]) + } + defPlanHintsStr := hint.RestoreOptimizerHints(defPlanHints) + binding := bindRecord.FindBinding(defPlanHintsStr) + if binding == nil { + handleEvolveTasks(ctx, sctx, bindRecord, stmtNode, defPlanHintsStr) + } + } + } + + return bestPlan, names, nil +} + +// OptimizeForForeignKeyCascade does optimization and creates a Plan for foreign key cascade. +// The node must be prepared first. +// Compare to Optimize, OptimizeForForeignKeyCascade only build plan by StmtNode, +// doesn't consider plan cache and plan binding, also doesn't do privilege check. +func OptimizeForForeignKeyCascade(ctx context.Context, sctx sessionctx.Context, node ast.StmtNode, is infoschema.InfoSchema) (core.Plan, error) { + builder := planBuilderPool.Get().(*core.PlanBuilder) + defer planBuilderPool.Put(builder.ResetForReuse()) + hintProcessor := &hint.BlockHintProcessor{Ctx: sctx} + builder.Init(sctx, is, hintProcessor) + p, err := builder.Build(ctx, node) + if err != nil { + return nil, err + } + if err := core.CheckTableLock(sctx, is, builder.GetVisitInfo()); err != nil { + return nil, err + } + return p, nil +} + +func allowInReadOnlyMode(sctx sessionctx.Context, node ast.Node) (bool, error) { + pm := privilege.GetPrivilegeManager(sctx) + if pm == nil { + return true, nil + } + roles := sctx.GetSessionVars().ActiveRoles + // allow replication thread + // NOTE: it is required, whether SEM is enabled or not, only user with explicit RESTRICTED_REPLICA_WRITER_ADMIN granted can ignore the restriction, so we need to surpass the case that if SEM is not enabled, SUPER will has all privileges + if pm.HasExplicitlyGrantedDynamicPrivilege(roles, "RESTRICTED_REPLICA_WRITER_ADMIN", false) { + return true, nil + } + + switch node.(type) { + // allow change variables (otherwise can't unset read-only mode) + case *ast.SetStmt, + // allow analyze table + *ast.AnalyzeTableStmt, + *ast.UseStmt, + *ast.ShowStmt, + *ast.CreateBindingStmt, + *ast.DropBindingStmt, + *ast.PrepareStmt, + *ast.BeginStmt, + *ast.RollbackStmt: + return true, nil + case *ast.CommitStmt: + txn, err := sctx.Txn(true) + if err != nil { + return false, err + } + if !txn.IsReadOnly() { + return false, txn.Rollback() + } + return true, nil + } + + vars := sctx.GetSessionVars() + return IsReadOnly(node, vars), nil +} + +var planBuilderPool = sync.Pool{ + New: func() interface{} { + return core.NewPlanBuilder() + }, +} + +// optimizeCnt is a global variable only used for test. +var optimizeCnt int + +func optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (core.Plan, types.NameSlice, float64, error) { + failpoint.Inject("checkOptimizeCountOne", func(val failpoint.Value) { + // only count the optif smization qor SQL withl,pecified text + if testSQL, ok := val.(string); ok && testSQL == node.OriginalText() { + optimizeCnt++ + if optimizeCnt > 1 { + failpoint.Return(nil, nil, 0, errors.New("gofail wrong optimizerCnt error")) + } + } + }) + failpoint.Inject("mockHighLoadForOptimize", func() { + sqlPrefixes := []string{"select"} + topsql.MockHighCPULoad(sctx.GetSessionVars().StmtCtx.OriginalSQL, sqlPrefixes, 10) + }) + sessVars := sctx.GetSessionVars() + if sessVars.StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(sctx) + defer debugtrace.LeaveContextCommon(sctx) + } + + // build logical plan + hintProcessor := &hint.BlockHintProcessor{Ctx: sctx} + node.Accept(hintProcessor) + defer hintProcessor.HandleUnusedViewHints() + builder := planBuilderPool.Get().(*core.PlanBuilder) + defer planBuilderPool.Put(builder.ResetForReuse()) + builder.Init(sctx, is, hintProcessor) + p, err := buildLogicalPlan(ctx, sctx, node, builder) + if err != nil { + return nil, nil, 0, err + } + + activeRoles := sessVars.ActiveRoles + // Check privilege. Maybe it's better to move this to the Preprocess, but + // we need the table information to check privilege, which is collected + // into the visitInfo in the logical plan builder. + if pm := privilege.GetPrivilegeManager(sctx); pm != nil { + visitInfo := core.VisitInfo4PrivCheck(is, node, builder.GetVisitInfo()) + if err := core.CheckPrivilege(activeRoles, pm, visitInfo); err != nil { + return nil, nil, 0, err + } + } + + if err := core.CheckTableLock(sctx, is, builder.GetVisitInfo()); err != nil { + return nil, nil, 0, err + } + + names := p.OutputNames() + + // Handle the non-logical plan statement. + logic, isLogicalPlan := p.(core.LogicalPlan) + if !isLogicalPlan { + return p, names, 0, nil + } + + // Handle the logical plan statement, use cascades planner if enabled. + if sessVars.GetEnableCascadesPlanner() { + finalPlan, cost, err := cascades.DefaultOptimizer.FindBestPlan(sctx, logic) + return finalPlan, names, cost, err + } + + beginOpt := time.Now() + finalPlan, cost, err := core.DoOptimize(ctx, sctx, builder.GetOptFlag(), logic) + // TODO: capture plan replayer here if it matches sql and plan digest + + sessVars.DurationOptimization = time.Since(beginOpt) + return finalPlan, names, cost, err +} + +// OptimizeExecStmt to handle the "execute" statement +func OptimizeExecStmt(ctx context.Context, sctx sessionctx.Context, + execAst *ast.ExecuteStmt, is infoschema.InfoSchema) (core.Plan, types.NameSlice, error) { + builder := planBuilderPool.Get().(*core.PlanBuilder) + defer planBuilderPool.Put(builder.ResetForReuse()) + builder.Init(sctx, is, nil) + + p, err := buildLogicalPlan(ctx, sctx, execAst, builder) + if err != nil { + return nil, nil, err + } + exec, ok := p.(*core.Execute) + if !ok { + return nil, nil, errors.Errorf("invalid result plan type, should be Execute") + } + plan, names, err := core.GetPlanFromSessionPlanCache(ctx, sctx, false, is, exec.PrepStmt, exec.Params) + if err != nil { + return nil, nil, err + } + exec.Plan = plan + exec.SetOutputNames(names) + exec.Stmt = exec.PrepStmt.PreparedAst.Stmt + return exec, names, nil +} + +func buildLogicalPlan(ctx context.Context, sctx sessionctx.Context, node ast.Node, builder *core.PlanBuilder) (core.Plan, error) { + sctx.GetSessionVars().PlanID.Store(0) + sctx.GetSessionVars().PlanColumnID.Store(0) + sctx.GetSessionVars().MapScalarSubQ = nil + sctx.GetSessionVars().MapHashCode2UniqueID4ExtendedCol = nil + + failpoint.Inject("mockRandomPlanID", func() { + sctx.GetSessionVars().PlanID.Store(rand.Int31n(1000)) // nolint:gosec + }) + + // reset fields about rewrite + sctx.GetSessionVars().RewritePhaseInfo.Reset() + beginRewrite := time.Now() + p, err := builder.Build(ctx, node) + if err != nil { + return nil, err + } + sctx.GetSessionVars().RewritePhaseInfo.DurationRewrite = time.Since(beginRewrite) + if exec, ok := p.(*core.Execute); ok && exec.PrepStmt != nil { + sctx.GetSessionVars().StmtCtx.Tables = core.GetDBTableInfo(exec.PrepStmt.VisitInfos) + } else { + sctx.GetSessionVars().StmtCtx.Tables = core.GetDBTableInfo(builder.GetVisitInfo()) + } + return p, nil +} + +// ExtractSelectAndNormalizeDigest extract the select statement and normalize it. +func ExtractSelectAndNormalizeDigest(stmtNode ast.StmtNode, specifiledDB string, forBinding bool) (ast.StmtNode, string, string, error) { + switch x := stmtNode.(type) { + case *ast.ExplainStmt: + // This function is only used to find bind record. + // For some SQLs, such as `explain select * from t`, they will be entered here many times, + // but some of them do not want to obtain bind record. + // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. + // For these cases, we need return "" as normalize SQL and hash. + if len(x.Text()) == 0 { + return x.Stmt, "", "", nil + } + switch x.Stmt.(type) { + case *ast.SelectStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: + var normalizeSQL string + if forBinding { + // Apply additional binding rules if enabled + normalizeSQL = parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(x.Stmt, specifiledDB, x.Text())) + } else { + normalizeSQL = parser.Normalize(utilparser.RestoreWithDefaultDB(x.Stmt, specifiledDB, x.Text())) + } + normalizeSQL = core.EraseLastSemicolonInSQL(normalizeSQL) + hash := parser.DigestNormalized(normalizeSQL) + return x.Stmt, normalizeSQL, hash.String(), nil + case *ast.SetOprStmt: + core.EraseLastSemicolon(x) + var normalizeExplainSQL string + var explainSQL string + if specifiledDB != "" { + explainSQL = utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text()) + } else { + explainSQL = x.Text() + } + + if forBinding { + // Apply additional binding rules + normalizeExplainSQL = parser.NormalizeForBinding(explainSQL) + } else { + normalizeExplainSQL = parser.Normalize(x.Text()) + } + + idx := strings.Index(normalizeExplainSQL, "select") + parenthesesIdx := strings.Index(normalizeExplainSQL, "(") + if parenthesesIdx != -1 && parenthesesIdx < idx { + idx = parenthesesIdx + } + // If the SQL is `EXPLAIN ((VALUES ROW ()) ORDER BY 1);`, the idx will be -1. + if idx == -1 { + hash := parser.DigestNormalized(normalizeExplainSQL) + return x.Stmt, normalizeExplainSQL, hash.String(), nil + } + normalizeSQL := normalizeExplainSQL[idx:] + hash := parser.DigestNormalized(normalizeSQL) + return x.Stmt, normalizeSQL, hash.String(), nil + } + case *ast.SelectStmt, *ast.SetOprStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: + core.EraseLastSemicolon(x) + // This function is only used to find bind record. + // For some SQLs, such as `explain select * from t`, they will be entered here many times, + // but some of them do not want to obtain bind record. + // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. + // For these cases, we need return "" as normalize SQL and hash. + if len(x.Text()) == 0 { + return x, "", "", nil + } + + var normalizedSQL string + var hash *parser.Digest + if forBinding { + // Apply additional binding rules + normalizedSQL, hash = parser.NormalizeDigestForBinding(utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text())) + } else { + normalizedSQL, hash = parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text())) + } + + return x, normalizedSQL, hash.String(), nil + } + return nil, "", "", nil +} + +func getBindRecord(ctx sessionctx.Context, stmt ast.StmtNode) (*bindinfo.BindRecord, string, error) { + // When the domain is initializing, the bind will be nil. + if ctx.Value(bindinfo.SessionBindInfoKeyType) == nil { + return nil, "", nil + } + stmtNode, normalizedSQL, hash, err := ExtractSelectAndNormalizeDigest(stmt, ctx.GetSessionVars().CurrentDB, true) + if err != nil || stmtNode == nil { + return nil, "", err + } + sessionHandle := ctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) + bindRecord := sessionHandle.GetBindRecord(hash, normalizedSQL, "") + if bindRecord != nil { + if bindRecord.HasEnabledBinding() { + return bindRecord, metrics.ScopeSession, nil + } + return nil, "", nil + } + globalHandle := domain.GetDomain(ctx).BindHandle() + if globalHandle == nil { + return nil, "", nil + } + bindRecord = globalHandle.GetBindRecord(hash, normalizedSQL, "") + return bindRecord, metrics.ScopeGlobal, nil +} + +func handleInvalidBindRecord(ctx context.Context, sctx sessionctx.Context, level string, bindRecord bindinfo.BindRecord) { + sessionHandle := sctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) + err := sessionHandle.DropBindRecord(bindRecord.OriginalSQL, bindRecord.Db, &bindRecord.Bindings[0]) + if err != nil { + logutil.Logger(ctx).Info("drop session bindings failed") + } + if level == metrics.ScopeSession { + return + } + + globalHandle := domain.GetDomain(sctx).BindHandle() + globalHandle.AddDropInvalidBindTask(&bindRecord) +} + +func handleEvolveTasks(ctx context.Context, sctx sessionctx.Context, br *bindinfo.BindRecord, stmtNode ast.StmtNode, planHint string) { + bindSQL := bindinfo.GenerateBindSQL(ctx, stmtNode, planHint, false, br.Db) + if bindSQL == "" { + return + } + charset, collation := sctx.GetSessionVars().GetCharsetInfo() + _, sqlDigestWithDB := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmtNode, br.Db, br.OriginalSQL)) + binding := bindinfo.Binding{ + BindSQL: bindSQL, + Status: bindinfo.PendingVerify, + Charset: charset, + Collation: collation, + Source: bindinfo.Evolve, + SQLDigest: sqlDigestWithDB.String(), + } + globalHandle := domain.GetDomain(sctx).BindHandle() + globalHandle.AddEvolvePlanTask(br.OriginalSQL, br.Db, binding) +} + +func handleStmtHints(hints []*ast.TableOptimizerHint) (stmtHints stmtctx.StmtHints, offs []int, warns []error) { + if len(hints) == 0 { + return + } + hintOffs := make(map[string]int, len(hints)) + var forceNthPlan *ast.TableOptimizerHint + var memoryQuotaHintCnt, useToJAHintCnt, useCascadesHintCnt, noIndexMergeHintCnt, readReplicaHintCnt, maxExecutionTimeCnt, forceNthPlanCnt, straightJoinHintCnt, resourceGroupHintCnt int + setVars := make(map[string]string) + setVarsOffs := make([]int, 0, len(hints)) + for i, hint := range hints { + switch hint.HintName.L { + case "memory_quota": + hintOffs[hint.HintName.L] = i + memoryQuotaHintCnt++ + case "resource_group": + hintOffs[hint.HintName.L] = i + resourceGroupHintCnt++ + case "use_toja": + hintOffs[hint.HintName.L] = i + useToJAHintCnt++ + case "use_cascades": + hintOffs[hint.HintName.L] = i + useCascadesHintCnt++ + case "no_index_merge": + hintOffs[hint.HintName.L] = i + noIndexMergeHintCnt++ + case "read_consistent_replica": + hintOffs[hint.HintName.L] = i + readReplicaHintCnt++ + case "max_execution_time": + hintOffs[hint.HintName.L] = i + maxExecutionTimeCnt++ + case "nth_plan": + forceNthPlanCnt++ + forceNthPlan = hint + case "straight_join": + hintOffs[hint.HintName.L] = i + straightJoinHintCnt++ + case "set_var": + setVarHint := hint.HintData.(ast.HintSetVar) + + // Not all session variables are permitted for use with SET_VAR + sysVar := variable.GetSysVar(setVarHint.VarName) + if sysVar == nil { + warns = append(warns, core.ErrUnresolvedHintName.GenWithStackByArgs(setVarHint.VarName, hint.HintName.String())) + continue + } + if !sysVar.IsHintUpdatableVerfied { + warns = append(warns, core.ErrNotHintUpdatable.GenWithStackByArgs(setVarHint.VarName)) + } + // If several hints with the same variable name appear in the same statement, the first one is applied and the others are ignored with a warning + if _, ok := setVars[setVarHint.VarName]; ok { + msg := fmt.Sprintf("%s(%s=%s)", hint.HintName.String(), setVarHint.VarName, setVarHint.Value) + warns = append(warns, core.ErrWarnConflictingHint.GenWithStackByArgs(msg)) + continue + } + setVars[setVarHint.VarName] = setVarHint.Value + setVarsOffs = append(setVarsOffs, i) + } + } + stmtHints.OriginalTableHints = hints + stmtHints.SetVars = setVars + + // Handle MEMORY_QUOTA + if memoryQuotaHintCnt != 0 { + memoryQuotaHint := hints[hintOffs["memory_quota"]] + if memoryQuotaHintCnt > 1 { + warn := errors.Errorf("MEMORY_QUOTA() is defined more than once, only the last definition takes effect: MEMORY_QUOTA(%v)", memoryQuotaHint.HintData.(int64)) + warns = append(warns, warn) + } + // Executor use MemoryQuota <= 0 to indicate no memory limit, here use < 0 to handle hint syntax error. + if memoryQuota := memoryQuotaHint.HintData.(int64); memoryQuota < 0 { + delete(hintOffs, "memory_quota") + warn := errors.New("The use of MEMORY_QUOTA hint is invalid, valid usage: MEMORY_QUOTA(10 MB) or MEMORY_QUOTA(10 GB)") + warns = append(warns, warn) + } else { + stmtHints.HasMemQuotaHint = true + stmtHints.MemQuotaQuery = memoryQuota + if memoryQuota == 0 { + warn := errors.New("Setting the MEMORY_QUOTA to 0 means no memory limit") + warns = append(warns, warn) + } + } + } + // Handle USE_TOJA + if useToJAHintCnt != 0 { + useToJAHint := hints[hintOffs["use_toja"]] + if useToJAHintCnt > 1 { + warn := errors.Errorf("USE_TOJA() is defined more than once, only the last definition takes effect: USE_TOJA(%v)", useToJAHint.HintData.(bool)) + warns = append(warns, warn) + } + stmtHints.HasAllowInSubqToJoinAndAggHint = true + stmtHints.AllowInSubqToJoinAndAgg = useToJAHint.HintData.(bool) + } + // Handle USE_CASCADES + if useCascadesHintCnt != 0 { + useCascadesHint := hints[hintOffs["use_cascades"]] + if useCascadesHintCnt > 1 { + warn := errors.Errorf("USE_CASCADES() is defined more than once, only the last definition takes effect: USE_CASCADES(%v)", useCascadesHint.HintData.(bool)) + warns = append(warns, warn) + } + stmtHints.HasEnableCascadesPlannerHint = true + stmtHints.EnableCascadesPlanner = useCascadesHint.HintData.(bool) + } + // Handle NO_INDEX_MERGE + if noIndexMergeHintCnt != 0 { + if noIndexMergeHintCnt > 1 { + warn := errors.New("NO_INDEX_MERGE() is defined more than once, only the last definition takes effect") + warns = append(warns, warn) + } + stmtHints.NoIndexMergeHint = true + } + // Handle straight_join + if straightJoinHintCnt != 0 { + if straightJoinHintCnt > 1 { + warn := errors.New("STRAIGHT_JOIN() is defined more than once, only the last definition takes effect") + warns = append(warns, warn) + } + stmtHints.StraightJoinOrder = true + } + // Handle READ_CONSISTENT_REPLICA + if readReplicaHintCnt != 0 { + if readReplicaHintCnt > 1 { + warn := errors.New("READ_CONSISTENT_REPLICA() is defined more than once, only the last definition takes effect") + warns = append(warns, warn) + } + stmtHints.HasReplicaReadHint = true + stmtHints.ReplicaRead = byte(kv.ReplicaReadFollower) + } + // Handle MAX_EXECUTION_TIME + if maxExecutionTimeCnt != 0 { + maxExecutionTime := hints[hintOffs["max_execution_time"]] + if maxExecutionTimeCnt > 1 { + warn := errors.Errorf("MAX_EXECUTION_TIME() is defined more than once, only the last definition takes effect: MAX_EXECUTION_TIME(%v)", maxExecutionTime.HintData.(uint64)) + warns = append(warns, warn) + } + stmtHints.HasMaxExecutionTime = true + stmtHints.MaxExecutionTime = maxExecutionTime.HintData.(uint64) + } + // Handle RESOURCE_GROUP + if resourceGroupHintCnt != 0 { + resourceGroup := hints[hintOffs["resource_group"]] + if resourceGroupHintCnt > 1 { + warn := errors.Errorf("RESOURCE_GROUP() is defined more than once, only the last definition takes effect: RESOURCE_GROUP(%v)", resourceGroup.HintData.(string)) + warns = append(warns, warn) + } + stmtHints.HasResourceGroup = true + stmtHints.ResourceGroup = resourceGroup.HintData.(string) + } + // Handle NTH_PLAN + if forceNthPlanCnt != 0 { + if forceNthPlanCnt > 1 { + warn := errors.Errorf("NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(%v)", forceNthPlan.HintData.(int64)) + warns = append(warns, warn) + } + stmtHints.ForceNthPlan = forceNthPlan.HintData.(int64) + if stmtHints.ForceNthPlan < 1 { + stmtHints.ForceNthPlan = -1 + warn := errors.Errorf("the hintdata for NTH_PLAN() is too small, hint ignored") + warns = append(warns, warn) + } + } else { + stmtHints.ForceNthPlan = -1 + } + for _, off := range hintOffs { + offs = append(offs, off) + } + offs = append(offs, setVarsOffs...) + // let hint is always ordered, it is convenient to human compare and test. + sort.Ints(offs) + return +} + +func init() { + core.OptimizeAstNode = Optimize + core.IsReadOnly = IsReadOnly + core.ExtractSelectAndNormalizeDigest = ExtractSelectAndNormalizeDigest +} diff --git a/pkg/planner/property/BUILD.bazel b/pkg/planner/property/BUILD.bazel new file mode 100644 index 0000000000000..90a53db31684e --- /dev/null +++ b/pkg/planner/property/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "property", + srcs = [ + "logical_property.go", + "physical_property.go", + "stats_info.go", + "task_type.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/property", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/size", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-tipb", + ], +) diff --git a/planner/property/logical_property.go b/pkg/planner/property/logical_property.go similarity index 95% rename from planner/property/logical_property.go rename to pkg/planner/property/logical_property.go index 786185aa940fd..d7bbf132979ef 100644 --- a/planner/property/logical_property.go +++ b/pkg/planner/property/logical_property.go @@ -15,7 +15,7 @@ package property import ( - "github.com/pingcap/tidb/expression" + "github.com/pingcap/tidb/pkg/expression" ) // LogicalProperty stands for logical properties such as schema of expression, diff --git a/planner/property/physical_property.go b/pkg/planner/property/physical_property.go similarity index 98% rename from planner/property/physical_property.go rename to pkg/planner/property/physical_property.go index 50e05d6d24f60..f572e9edac550 100644 --- a/planner/property/physical_property.go +++ b/pkg/planner/property/physical_property.go @@ -20,11 +20,11 @@ import ( "unsafe" "github.com/pingcap/log" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/size" "github.com/pingcap/tipb/go-tipb" ) diff --git a/planner/property/stats_info.go b/pkg/planner/property/stats_info.go similarity index 97% rename from planner/property/stats_info.go rename to pkg/planner/property/stats_info.go index d63271dd77e94..19fc7dc00fd6d 100644 --- a/planner/property/stats_info.go +++ b/pkg/planner/property/stats_info.go @@ -17,8 +17,8 @@ package property import ( "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/statistics" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/statistics" ) // GroupNDV stores the NDV of a group of columns. diff --git a/planner/property/task_type.go b/pkg/planner/property/task_type.go similarity index 100% rename from planner/property/task_type.go rename to pkg/planner/property/task_type.go diff --git a/pkg/planner/util/BUILD.bazel b/pkg/planner/util/BUILD.bazel new file mode 100644 index 0000000000000..5c07c753230cd --- /dev/null +++ b/pkg/planner/util/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "util", + srcs = [ + "byitem.go", + "misc.go", + "path.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/util", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/ranger", + "//pkg/util/size", + ], +) + +go_test( + name = "util_test", + timeout = "short", + srcs = [ + "main_test.go", + "path_test.go", + ], + embed = [":util"], + flaky = True, + deps = [ + "//pkg/domain", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/ranger", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/util/byitem.go b/pkg/planner/util/byitem.go similarity index 91% rename from planner/util/byitem.go rename to pkg/planner/util/byitem.go index 77c947e2aa54f..878ea0fb046fb 100644 --- a/planner/util/byitem.go +++ b/pkg/planner/util/byitem.go @@ -17,9 +17,9 @@ package util import ( "fmt" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/size" ) // ByItems wraps a "by" item. diff --git a/pkg/planner/util/debugtrace/BUILD.bazel b/pkg/planner/util/debugtrace/BUILD.bazel new file mode 100644 index 0000000000000..fded8bf9198d6 --- /dev/null +++ b/pkg/planner/util/debugtrace/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "debugtrace", + srcs = ["base.go"], + importpath = "github.com/pingcap/tidb/pkg/planner/util/debugtrace", + visibility = ["//visibility:public"], + deps = ["//pkg/sessionctx"], +) diff --git a/pkg/planner/util/debugtrace/base.go b/pkg/planner/util/debugtrace/base.go new file mode 100644 index 0000000000000..1b2399a128274 --- /dev/null +++ b/pkg/planner/util/debugtrace/base.go @@ -0,0 +1,162 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package debugtrace + +import ( + "bytes" + "encoding/json" + "runtime" + + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// OptimizerDebugTraceRoot is for recording the optimizer debug trace. +// Each debug information, which is a "step" and can be any type, is placed +// in a "context" (baseDebugTraceContext) as an interface{}. +// Overall, the debug trace is a tree-like hierarchical structure of baseDebugTraceContext. +// This structure can reflect the function call hierarchy of each step during optimization. +// In the end, the entire recorded baseDebugTraceContext will be marshalled to JSON as the result. +// +// EnterContextCommon and LeaveContextCommon can be used to maintain the context easily. +// Usually you just need to add the code below at the beginning of a function: +// +// if StmtCtx.EnableOptimizerDebugTrace { +// EnterContextCommon(ds.ctx) +// defer LeaveContextCommon(ds.ctx) +// } +// +// To record debug information, AppendStepToCurrentContext and AppendStepWithNameToCurrentContext +// are provided as low-level methods. +// RecordAnyValuesWithNames handles some common logic for better usability, so it should +// be the most commonly used function for recording simple information. +// If the tracing logic is more complicated or need extra MarshalJSON logic, you should implement +// separate logic like in planner/core/debug_trace.go and statistics/debug_trace.go +type OptimizerDebugTraceRoot struct { + traceCtx baseDebugTraceContext + // currentCtx indicates in which baseDebugTraceContext we should record the debug information. + currentCtx *baseDebugTraceContext +} + +// MarshalJSON overrides the default MarshalJSON behavior and marshals the unexported traceCtx. +func (root *OptimizerDebugTraceRoot) MarshalJSON() ([]byte, error) { + return EncodeJSONCommon(root.traceCtx.steps) +} + +// baseDebugTraceContext is the core of the debug trace. +// The steps field can be used to record any information, or point to another baseDebugTraceContext. +type baseDebugTraceContext struct { + name string + steps []interface{} + parentCtx *baseDebugTraceContext +} + +func (c *baseDebugTraceContext) MarshalJSON() ([]byte, error) { + var tmp, content interface{} + if len(c.steps) > 1 { + content = c.steps + } else if len(c.steps) == 1 { + content = c.steps[0] + } + if len(c.name) > 0 { + tmp = map[string]interface{}{ + c.name: content, + } + } else { + tmp = content + } + return EncodeJSONCommon(tmp) +} + +// AppendStepToCurrentContext records debug information to the current context of the debug trace. +func (root *OptimizerDebugTraceRoot) AppendStepToCurrentContext(step interface{}) { + root.currentCtx.steps = append(root.currentCtx.steps, step) +} + +// AppendStepWithNameToCurrentContext records debug information and a name to the current context of the debug trace. +func (root *OptimizerDebugTraceRoot) AppendStepWithNameToCurrentContext(step interface{}, name string) { + tmp := map[string]interface{}{ + name: step, + } + root.currentCtx.steps = append(root.currentCtx.steps, tmp) +} + +// GetOrInitDebugTraceRoot returns the debug trace root. +// If it's not initialized, it will initialize it first. +func GetOrInitDebugTraceRoot(sctx sessionctx.Context) *OptimizerDebugTraceRoot { + stmtCtx := sctx.GetSessionVars().StmtCtx + res, ok := stmtCtx.OptimizerDebugTrace.(*OptimizerDebugTraceRoot) + if !ok || res == nil { + trace := &OptimizerDebugTraceRoot{} + trace.currentCtx = &trace.traceCtx + // Though it's not needed in theory, we set the parent of the top level context to itself for safety. + trace.traceCtx.parentCtx = &trace.traceCtx + stmtCtx.OptimizerDebugTrace = trace + } + return stmtCtx.OptimizerDebugTrace.(*OptimizerDebugTraceRoot) +} + +// EncodeJSONCommon contains some common logic for the debug trace, +// like disabling EscapeHTML and recording error. +func EncodeJSONCommon(input interface{}) ([]byte, error) { + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... + encoder.SetEscapeHTML(false) + err := encoder.Encode(input) + if err != nil { + err = encoder.Encode(err) + } + return buf.Bytes(), err +} + +// EnterContextCommon records the function name of the caller, +// then creates and enter a new context for this debug trace structure. +func EnterContextCommon(sctx sessionctx.Context) { + root := GetOrInitDebugTraceRoot(sctx) + funcName := "Fail to get function name." + pc, _, _, ok := runtime.Caller(1) + if ok { + funcName = runtime.FuncForPC(pc).Name() + } + newCtx := &baseDebugTraceContext{ + name: funcName, + parentCtx: root.currentCtx, + } + root.currentCtx.steps = append(root.currentCtx.steps, newCtx) + root.currentCtx = newCtx +} + +// LeaveContextCommon makes the debug trace goes to its parent context. +func LeaveContextCommon(sctx sessionctx.Context) { + root := GetOrInitDebugTraceRoot(sctx) + root.currentCtx = root.currentCtx.parentCtx +} + +// RecordAnyValuesWithNames is a general debug trace logic for recording some values of any type with a name. +// The vals arguments should be a slice like ["name1", value1, "name2", value2]. +// The names must be string, the values can be any type. +func RecordAnyValuesWithNames( + s sessionctx.Context, + vals ...interface{}, +) { + root := GetOrInitDebugTraceRoot(s) + tmp := make(map[string]interface{}, len(vals)/2) + for i := 0; i < len(vals); i += 2 { + str, _ := vals[i].(string) + val := vals[i+1] + tmp[str] = val + } + root.AppendStepToCurrentContext(tmp) +} diff --git a/pkg/planner/util/fixcontrol/BUILD.bazel b/pkg/planner/util/fixcontrol/BUILD.bazel new file mode 100644 index 0000000000000..8201f7b5d57c1 --- /dev/null +++ b/pkg/planner/util/fixcontrol/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "fixcontrol", + srcs = [ + "get.go", + "set.go", + ], + importpath = "github.com/pingcap/tidb/pkg/planner/util/fixcontrol", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "fixcontrol_test", + timeout = "short", + srcs = [ + "fixcontrol_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + deps = [ + ":fixcontrol", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_golang_x_exp//maps", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/planner/util/fixcontrol/fixcontrol_test.go b/pkg/planner/util/fixcontrol/fixcontrol_test.go similarity index 95% rename from planner/util/fixcontrol/fixcontrol_test.go rename to pkg/planner/util/fixcontrol/fixcontrol_test.go index 070ff97d14091..3cc9940ef9e1a 100644 --- a/planner/util/fixcontrol/fixcontrol_test.go +++ b/pkg/planner/util/fixcontrol/fixcontrol_test.go @@ -17,9 +17,9 @@ package fixcontrol_test import ( "testing" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" "github.com/stretchr/testify/require" "golang.org/x/exp/maps" ) diff --git a/planner/util/fixcontrol/get.go b/pkg/planner/util/fixcontrol/get.go similarity index 100% rename from planner/util/fixcontrol/get.go rename to pkg/planner/util/fixcontrol/get.go diff --git a/pkg/planner/util/fixcontrol/main_test.go b/pkg/planner/util/fixcontrol/main_test.go new file mode 100644 index 0000000000000..4dbc318379cbc --- /dev/null +++ b/pkg/planner/util/fixcontrol/main_test.go @@ -0,0 +1,51 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package fixcontrol_test + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + + testDataMap.LoadTestSuiteData("testdata", "fix_control_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/planner/util/fixcontrol/set.go b/pkg/planner/util/fixcontrol/set.go similarity index 100% rename from planner/util/fixcontrol/set.go rename to pkg/planner/util/fixcontrol/set.go diff --git a/planner/util/fixcontrol/testdata/fix_control_suite_in.json b/pkg/planner/util/fixcontrol/testdata/fix_control_suite_in.json similarity index 100% rename from planner/util/fixcontrol/testdata/fix_control_suite_in.json rename to pkg/planner/util/fixcontrol/testdata/fix_control_suite_in.json diff --git a/planner/util/fixcontrol/testdata/fix_control_suite_out.json b/pkg/planner/util/fixcontrol/testdata/fix_control_suite_out.json similarity index 100% rename from planner/util/fixcontrol/testdata/fix_control_suite_out.json rename to pkg/planner/util/fixcontrol/testdata/fix_control_suite_out.json diff --git a/pkg/planner/util/main_test.go b/pkg/planner/util/main_test.go new file mode 100644 index 0000000000000..e5becaafd566e --- /dev/null +++ b/pkg/planner/util/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package util + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/planner/util/misc.go b/pkg/planner/util/misc.go new file mode 100644 index 0000000000000..c4bf10f15047a --- /dev/null +++ b/pkg/planner/util/misc.go @@ -0,0 +1,61 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package util + +import ( + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/ranger" +) + +// CloneExprs uses Expression.Clone to clone a slice of Expression. +func CloneExprs(exprs []expression.Expression) []expression.Expression { + cloned := make([]expression.Expression, 0, len(exprs)) + for _, e := range exprs { + cloned = append(cloned, e.Clone()) + } + return cloned +} + +// CloneCols uses (*Column).Clone to clone a slice of *Column. +func CloneCols(cols []*expression.Column) []*expression.Column { + cloned := make([]*expression.Column, 0, len(cols)) + for _, c := range cols { + if c == nil { + cloned = append(cloned, nil) + continue + } + cloned = append(cloned, c.Clone().(*expression.Column)) + } + return cloned +} + +// CloneColInfos uses (*ColumnInfo).Clone to clone a slice of *ColumnInfo. +func CloneColInfos(cols []*model.ColumnInfo) []*model.ColumnInfo { + cloned := make([]*model.ColumnInfo, 0, len(cols)) + for _, c := range cols { + cloned = append(cloned, c.Clone()) + } + return cloned +} + +// CloneRanges uses (*Range).Clone to clone a slice of *Range. +func CloneRanges(ranges []*ranger.Range) []*ranger.Range { + cloned := make([]*ranger.Range, 0, len(ranges)) + for _, r := range ranges { + cloned = append(cloned, r.Clone()) + } + return cloned +} diff --git a/planner/util/path.go b/pkg/planner/util/path.go similarity index 97% rename from planner/util/path.go rename to pkg/planner/util/path.go index 7f959176aef61..e0990380c9ff5 100644 --- a/planner/util/path.go +++ b/pkg/planner/util/path.go @@ -17,14 +17,14 @@ package util import ( "slices" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" ) // AccessPath indicates the way we access a table: by using single index, or by using multiple indexes, diff --git a/planner/util/path_test.go b/pkg/planner/util/path_test.go similarity index 92% rename from planner/util/path_test.go rename to pkg/planner/util/path_test.go index 39d1cf03b09d4..596b8aa693906 100644 --- a/planner/util/path_test.go +++ b/pkg/planner/util/path_test.go @@ -17,13 +17,13 @@ package util_test import ( "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/planner/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/stretchr/testify/require" ) diff --git a/pkg/plugin/BUILD.bazel b/pkg/plugin/BUILD.bazel new file mode 100644 index 0000000000000..0638939238dff --- /dev/null +++ b/pkg/plugin/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "plugin", + srcs = [ + "audit.go", + "const.go", + "errors.go", + "helper.go", + "plugin.go", + "spi.go", + ], + importpath = "github.com/pingcap/tidb/pkg/plugin", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/errno", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "plugin_test", + timeout = "short", + srcs = [ + "const_test.go", + "helper_test.go", + "integration_test.go", + "main_test.go", + "plugin_test.go", + "spi_test.go", + ], + embed = [":plugin"], + flaky = True, + shard_count = 11, + deps = [ + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/plugin/README.md b/pkg/plugin/README.md similarity index 100% rename from plugin/README.md rename to pkg/plugin/README.md diff --git a/plugin/audit.go b/pkg/plugin/audit.go similarity index 98% rename from plugin/audit.go rename to pkg/plugin/audit.go index 44891107359e6..d7672a26b4a71 100644 --- a/plugin/audit.go +++ b/pkg/plugin/audit.go @@ -19,7 +19,7 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessionctx/variable" ) // GeneralEvent presents TiDB generate event. diff --git a/pkg/plugin/conn_ip_example/BUILD.bazel b/pkg/plugin/conn_ip_example/BUILD.bazel new file mode 100644 index 0000000000000..f720963731967 --- /dev/null +++ b/pkg/plugin/conn_ip_example/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "conn_ip_example_lib", + srcs = ["conn_ip_example.go"], + importpath = "github.com/pingcap/tidb/pkg/plugin/conn_ip_example", + visibility = ["//visibility:private"], + deps = [ + "//pkg/plugin", + "//pkg/sessionctx/variable", + ], +) + +go_test( + name = "conn_ip_example_test", + timeout = "short", + srcs = [ + "conn_ip_example_test.go", + "main_test.go", + ], + embed = [":conn_ip_example_lib"], + flaky = True, + deps = [ + "//pkg/plugin", + "//pkg/sessionctx/variable", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/plugin/conn_ip_example/conn_ip_example.go b/pkg/plugin/conn_ip_example/conn_ip_example.go similarity index 98% rename from plugin/conn_ip_example/conn_ip_example.go rename to pkg/plugin/conn_ip_example/conn_ip_example.go index 082a7b4cdc04b..8b42c5143a7ba 100644 --- a/plugin/conn_ip_example/conn_ip_example.go +++ b/pkg/plugin/conn_ip_example/conn_ip_example.go @@ -20,8 +20,8 @@ import ( "strings" "sync/atomic" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/sessionctx/variable" ) // Accumulator of connection diff --git a/plugin/conn_ip_example/conn_ip_example_test.go b/pkg/plugin/conn_ip_example/conn_ip_example_test.go similarity index 97% rename from plugin/conn_ip_example/conn_ip_example_test.go rename to pkg/plugin/conn_ip_example/conn_ip_example_test.go index 8b7cd09b04018..4a2b7490c6a56 100644 --- a/plugin/conn_ip_example/conn_ip_example_test.go +++ b/pkg/plugin/conn_ip_example/conn_ip_example_test.go @@ -20,8 +20,8 @@ import ( "sync/atomic" "testing" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/require" ) diff --git a/pkg/plugin/conn_ip_example/main_test.go b/pkg/plugin/conn_ip_example/main_test.go new file mode 100644 index 0000000000000..44e6371253967 --- /dev/null +++ b/pkg/plugin/conn_ip_example/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package main + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/plugin/conn_ip_example/manifest.toml b/pkg/plugin/conn_ip_example/manifest.toml similarity index 100% rename from plugin/conn_ip_example/manifest.toml rename to pkg/plugin/conn_ip_example/manifest.toml diff --git a/plugin/const.go b/pkg/plugin/const.go similarity index 100% rename from plugin/const.go rename to pkg/plugin/const.go diff --git a/plugin/const_test.go b/pkg/plugin/const_test.go similarity index 100% rename from plugin/const_test.go rename to pkg/plugin/const_test.go diff --git a/pkg/plugin/errors.go b/pkg/plugin/errors.go new file mode 100644 index 0000000000000..ea1380576519a --- /dev/null +++ b/pkg/plugin/errors.go @@ -0,0 +1,29 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package plugin + +import ( + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +var ( + errInvalidPluginID = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginID) + errInvalidPluginManifest = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginManifest) + errInvalidPluginName = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginName) + errInvalidPluginVersion = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginVersion) + errDuplicatePlugin = dbterror.ClassPlugin.NewStd(errno.ErrDuplicatePlugin) + errRequireVersionCheckFail = dbterror.ClassPlugin.NewStd(errno.ErrRequireVersionCheckFail) +) diff --git a/plugin/helper.go b/pkg/plugin/helper.go similarity index 100% rename from plugin/helper.go rename to pkg/plugin/helper.go diff --git a/plugin/helper_test.go b/pkg/plugin/helper_test.go similarity index 100% rename from plugin/helper_test.go rename to pkg/plugin/helper_test.go diff --git a/pkg/plugin/integration_test.go b/pkg/plugin/integration_test.go new file mode 100644 index 0000000000000..9e9447a1a1fdf --- /dev/null +++ b/pkg/plugin/integration_test.go @@ -0,0 +1,786 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package plugin_test + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +// Audit tests cannot run in parallel. +func TestAuditLogNormal(t *testing.T) { + store := testkit.CreateMockStore(t) + sv := server.CreateMockServer(t, store) + defer sv.Close() + conn := server.CreateMockConn(t, sv) + defer conn.Close() + session.DisableStats4Test() + session.SetSchemaLease(0) + + type normalTest struct { + sql string + text string + rows uint64 + stmtType string + dbs string + tables string + cmd string + event plugin.GeneralEvent + resCnt int + } + + tests := []normalTest{ + { + sql: "CREATE DATABASE mynewdatabase", + stmtType: "CreateDatabase", + dbs: "mynewdatabase", + }, + { + sql: "CREATE TABLE t1 (a INT NOT NULL)", + stmtType: "CreateTable", + dbs: "test", + tables: "t1", + }, + { + sql: "CREATE TABLE t2 LIKE t1", + stmtType: "CreateTable", + dbs: "test,test", + tables: "t2,t1", + }, + { + sql: "CREATE INDEX a ON t1 (a)", + stmtType: "CreateIndex", + dbs: "test", + tables: "t1", + }, + { + sql: "CREATE SEQUENCE seq", + stmtType: "other", + dbs: "test", + tables: "seq", + }, + { + sql: " create temporary table t3 (a int)", + stmtType: "CreateTable", + dbs: "test", + tables: "t3", + }, + { + sql: "create global temporary table t4 (a int) on commit delete rows", + stmtType: "CreateTable", + dbs: "test", + tables: "t4", + }, + { + sql: "CREATE VIEW v1 AS SELECT * FROM t1 WHERE a> 2", + stmtType: "CreateView", + dbs: "test,test", + tables: "t1,v1", + }, + { + sql: "USE test", + stmtType: "Use", + }, + { + sql: "DROP DATABASE mynewdatabase", + stmtType: "DropDatabase", + dbs: "mynewdatabase", + }, + { + sql: "SHOW CREATE SEQUENCE seq", + stmtType: "Show", + dbs: "test", + tables: "seq", + }, + { + sql: "DROP SEQUENCE seq", + stmtType: "other", + dbs: "test", + tables: "seq", + }, + { + sql: "DROP TABLE t4", + stmtType: "DropTable", + dbs: "test", + tables: "t4", + }, + { + sql: "DROP VIEW v1", + stmtType: "DropView", + dbs: "test", + tables: "v1", + }, + { + sql: "ALTER TABLE t1 ADD COLUMN c1 INT NOT NULL", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 MODIFY c1 BIGINT", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 ADD INDEX (c1)", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 ALTER INDEX c1 INVISIBLE", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 RENAME INDEX c1 TO c2", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 DROP INDEX c2", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 CHANGE c1 c2 INT", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "ALTER TABLE t1 DROP COLUMN c2", + stmtType: "AlterTable", + dbs: "test", + tables: "t1", + }, + { + sql: "CREATE SESSION BINDING FOR SELECT * FROM t1 WHERE a = 123 USING SELECT * FROM t1 IGNORE INDEX (a) WHERE a = 123", + stmtType: "CreateBinding", + }, + { + sql: "DROP SESSION BINDING FOR SELECT * FROM t1 WHERE a = 123", + stmtType: "DropBinding", + }, + // { + // sql: "LOAD STATS '/tmp/stats.json'", + // stmtType: "other", + // }, + // { + // sql: "DROP STATS t", + // stmtType: "other", + // }, + { + sql: "RENAME TABLE t2 TO t5", + stmtType: "other", + dbs: "test,test", + tables: "t2,t5", + }, + { + sql: "TRUNCATE t1", + stmtType: "TruncateTable", + dbs: "test", + tables: "t1", + }, + // { + // sql: "FLASHBACK TABLE t TO t1", + // stmtType: "other", + // dbs: "test", + // tables: "t1", + // }, + // { + // sql: "RECOVER TABLE t1", + // stmtType: "other", + // dbs: "test", + // tables: "t1,t2", + // }, + { + sql: "ALTER DATABASE test DEFAULT CHARACTER SET = utf8mb4", + stmtType: "other", + dbs: "test", + }, + { + sql: "ADMIN RELOAD opt_rule_blacklist", + stmtType: "other", + }, + // { + // sql: "ADMIN PLUGINS ENABLE audit_test", + // stmtType: "other", + // }, + { + sql: "ADMIN FLUSH bindings", + stmtType: "other", + }, + // { + // sql: "ADMIN REPAIR TABLE t1 CREATE TABLE (id int)", + // stmtType: "other", + // dbs: "test", + // tables: "t1", + // }, + { + sql: "ADMIN SHOW SLOW RECENT 10", + stmtType: "other", + }, + { + sql: "ADMIN SHOW DDL JOBS", + stmtType: "other", + }, + // { + // sql: "ADMIN CANCEL DDL JOBS 1", + // stmtType: "other", + // }, + { + sql: "ADMIN CHECKSUM TABLE t1", + stmtType: "other", + // dbs: "test", + // tables: "t1", + }, + { + sql: "ADMIN CHECK TABLE t1", + stmtType: "other", + // dbs: "test", + // tables: "t1", + }, + { + sql: "ADMIN CHECK INDEX t1 a", + stmtType: "other", + // dbs: "test", + // tables: "t1", + }, + { + sql: "CREATE USER 'newuser' IDENTIFIED BY 'newuserpassword'", + stmtType: "CreateUser", + }, + { + sql: "ALTER USER 'newuser' IDENTIFIED BY 'newnewpassword'", + stmtType: "other", + }, + { + sql: "CREATE ROLE analyticsteam", + stmtType: "CreateUser", + }, + { + sql: "GRANT SELECT ON test.* TO analyticsteam", + stmtType: "Grant", + dbs: "test", + }, + { + sql: "GRANT analyticsteam TO 'newuser'", + stmtType: "other", + }, + { + sql: "SET DEFAULT ROLE analyticsteam TO newuser;", + stmtType: "other", + }, + { + sql: "REVOKE SELECT ON test.* FROM 'analyticsteam'", + stmtType: "Revoke", + dbs: "test", + }, + { + sql: "DROP ROLE analyticsteam", + stmtType: "other", + }, + { + sql: "FLUSH PRIVILEGES", + stmtType: "other", + }, + { + sql: "SET PASSWORD FOR 'newuser' = 'test'", + stmtType: "Set", + }, + // { + // sql: "SET ROLE ALL", + // stmtType: "other", + // }, + { + sql: "DROP USER 'newuser'", + stmtType: "other", + }, + { + sql: "analyze table t1", + stmtType: "AnalyzeTable", + dbs: "test", + tables: "t1", + }, + { + sql: "SPLIT TABLE t1 BETWEEN (0) AND (1000000000) REGIONS 16", + stmtType: "other", + // dbs: "test", + // tables: "t1", + }, + // { + // sql: "BACKUP DATABASE `test` TO '.'", + // stmtType: "other", + // dbs: "test", + // }, + // { + // sql: "RESTORE DATABASE * FROM '.'", + // stmtType: "other", + // }, + // { + // sql: "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID 'drainer1'", + // stmtType: "other", + // }, + // { + // sql: "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID 'pump1'", + // stmtType: "other", + // }, + { + sql: "BEGIN", + stmtType: "Begin", + }, + { + sql: "ROLLBACK", + stmtType: "Rollback", + }, + { + sql: "START TRANSACTION", + stmtType: "Begin", + }, + { + sql: "COMMIT", + stmtType: "Commit", + }, + // { + // sql: "SHOW DRAINER STATUS", + // stmtType: "Show", + // }, + // { + // sql: "SHOW PUMP STATUS", + // stmtType: "Show", + // }, + // { + // sql: "SHOW GRANTS", + // stmtType: "Show", + // }, + { + sql: "SHOW PROCESSLIST", + stmtType: "Show", + }, + // { + // sql: "SHOW BACKUPS", + // stmtType: "Show", + // }, + // { + // sql: "SHOW RESTORES", + // stmtType: "Show", + // }, + { + sql: "show analyze status", + stmtType: "Show", + }, + { + sql: "SHOW SESSION BINDINGS", + stmtType: "Show", + }, + { + sql: "SHOW BUILTINS", + stmtType: "Show", + }, + { + sql: "SHOW CHARACTER SET", + stmtType: "Show", + }, + { + sql: "SHOW COLLATION", + stmtType: "Show", + }, + { + sql: "show columns from t1", + stmtType: "Show", + }, + { + sql: "show fields from t1", + stmtType: "Show", + }, + // { + // sql: "SHOW CONFIG", + // stmtType: "Show", + // }, + { + sql: "SHOW CREATE TABLE t1", + stmtType: "Show", + dbs: "test", + tables: "t1", + }, + { + sql: "SHOW CREATE USER 'root'", + stmtType: "Show", + }, + { + sql: "SHOW DATABASES", + stmtType: "Show", + }, + { + sql: "SHOW ENGINES", + stmtType: "Show", + }, + { + sql: "SHOW ERRORS", + stmtType: "Show", + }, + { + sql: "SHOW INDEXES FROM t1", + stmtType: "Show", + }, + { + sql: "SHOW MASTER STATUS", + stmtType: "Show", + }, + { + sql: "SHOW PLUGINS", + stmtType: "Show", + }, + { + sql: "show privileges", + stmtType: "Show", + }, + { + sql: "SHOW PROFILES", + stmtType: "Show", + }, + // { + // sql: "SHOW PUMP STATUS", + // stmtType: "Show", + // }, + { + sql: "SHOW SCHEMAS", + stmtType: "Show", + }, + { + sql: "SHOW STATS_HEALTHY", + stmtType: "Show", + dbs: "mysql", + }, + { + sql: "show stats_histograms", + stmtType: "Show", + dbs: "mysql", + tables: "stats_histograms", + }, + { + sql: "show stats_meta", + stmtType: "Show", + dbs: "mysql", + tables: "stats_meta", + }, + { + sql: "show status", + stmtType: "Show", + }, + { + sql: "show table t1 next_row_id", + stmtType: "Show", + dbs: "test", + tables: "t1", + }, + { + sql: "show table t1 regions", + stmtType: "Show", + }, + { + sql: "SHOW TABLE STATUS LIKE 't1'", + stmtType: "Show", + resCnt: 3, // Start + SHOW TABLE + Internal SELECT .. FROM IS.TABLES in current session + }, + { + sql: "SHOW TABLES", + stmtType: "Show", + }, + { + sql: "SHOW VARIABLES", + stmtType: "Show", + }, + { + sql: "SHOW WARNINGS", + stmtType: "Show", + }, + { + sql: "SET @number = 5", + stmtType: "Set", + }, + { + sql: "SET NAMES utf8", + stmtType: "Set", + }, + { + sql: "SET CHARACTER SET utf8mb4", + stmtType: "Set", + }, + { + sql: "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", + stmtType: "Set", + }, + { + sql: "SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER'", + stmtType: "Set", + }, + { + sql: "PREPARE mystmt FROM 'SELECT ? as num FROM DUAL'", + stmtType: "Prepare", + }, + { + sql: "EXECUTE mystmt USING @number", + text: "SELECT ? as num FROM DUAL", + stmtType: "Select", + }, + { + sql: "DEALLOCATE PREPARE mystmt", + stmtType: "Deallocate", + }, + { + sql: "INSERT INTO t1 VALUES (1), (2)", + stmtType: "Insert", + dbs: "test", + tables: "t1", + rows: 2, + }, + { + sql: "DELETE FROM t1 WHERE a = 2", + stmtType: "Delete", + dbs: "test", + tables: "t1", + rows: 1, + }, + { + sql: "REPLACE INTO t1 VALUES(3)", + stmtType: "Replace", + dbs: "test", + tables: "t1", + rows: 1, + }, + { + sql: "UPDATE t1 SET a=5 WHERE a=1", + stmtType: "Update", + dbs: "test", + tables: "t1", + rows: 1, + }, + { + sql: "DO 1", + stmtType: "other", + }, + // { + // sql: "LOAD DATA LOCAL INFILE 'data.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES (id)", + // stmtType: "LoadData", + // dbs: "test", + // tables: "t1", + // }, + { + sql: "SELECT * FROM t1", + stmtType: "Select", + dbs: "test", + tables: "t1", + }, + { + sql: "SELECT 1", + stmtType: "Select", + }, + { + sql: "TABLE t1", + stmtType: "Select", + dbs: "test", + tables: "t1", + }, + { + sql: "EXPLAIN ANALYZE SELECT * FROM t1 WHERE a = 1", + stmtType: "ExplainAnalyzeSQL", + // dbs: "test", + // tables: "t1", + }, + { + sql: "EXPLAIN SELECT * FROM t1", + stmtType: "ExplainSQL", + // dbs: "test", + // tables: "t1", + }, + { + sql: "EXPLAIN SELECT * FROM t1 WHERE a = 1", + stmtType: "ExplainSQL", + // dbs: "test", + // tables: "t1", + }, + { + sql: "DESC SELECT * FROM t1 WHERE a = 1", + stmtType: "ExplainSQL", + // dbs: "test", + // tables: "t1", + }, + { + sql: "DESCRIBE SELECT * FROM t1 WHERE a = 1", + stmtType: "ExplainSQL", + // dbs: "test", + // tables: "t1", + }, + { + sql: "trace format='row' select * from t1", + stmtType: "Trace", + // dbs: "test", + // tables: "t1", + }, + { + sql: "flush status", + stmtType: "other", + }, + { + sql: "FLUSH TABLES", + stmtType: "other", + }, + // { + // sql: "KILL TIDB 2", + // stmtType: "other", + // }, + // { + // sql: "SHUTDOWN", + // stmtType: "Shutdow", + // }, + // { + // sql: "ALTER INSTANCE RELOAD TLS", + // stmtType: "other", + // }, + } + + testResults := make([]normalTest, 0) + dbNames := make([]string, 0) + tableNames := make([]string, 0) + onGeneralEvent := func(ctx context.Context, sctx *variable.SessionVars, event plugin.GeneralEvent, cmd string) { + dbNames = dbNames[:0] + tableNames = tableNames[:0] + for _, value := range sctx.StmtCtx.Tables { + dbNames = append(dbNames, value.DB) + tableNames = append(tableNames, value.Table) + } + audit := normalTest{ + text: sctx.StmtCtx.OriginalSQL, + rows: sctx.StmtCtx.AffectedRows(), + stmtType: sctx.StmtCtx.StmtType, + dbs: strings.Join(dbNames, ","), + tables: strings.Join(tableNames, ","), + cmd: cmd, + event: event, + } + testResults = append(testResults, audit) + } + loadPlugin(t, onGeneralEvent) + defer plugin.Shutdown(context.Background()) + + require.NoError(t, conn.HandleQuery(context.Background(), "use test")) + for _, test := range tests { + testResults = testResults[:0] + errMsg := fmt.Sprintf("statement: %s", test.sql) + query := append([]byte{mysql.ComQuery}, []byte(test.sql)...) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + err := conn.Dispatch(ctx, query) + require.NoError(t, err, errMsg) + resultCount := test.resCnt + if resultCount == 0 { + resultCount = 2 + } + require.Equal(t, resultCount, len(testResults), errMsg) + + result := testResults[0] + require.Equal(t, "Query", result.cmd, errMsg) + require.Equal(t, plugin.Starting, result.event, errMsg) + + result = testResults[resultCount-1] + require.Equal(t, "Query", result.cmd, errMsg) + if test.text == "" { + require.Equal(t, test.sql, result.text, errMsg) + } else { + require.Equal(t, test.text, result.text, errMsg) + } + require.Equal(t, test.rows, result.rows, errMsg) + require.Equal(t, test.stmtType, result.stmtType, errMsg) + require.Equal(t, test.dbs, result.dbs, errMsg) + require.Equal(t, test.tables, result.tables, errMsg) + require.Equal(t, "Query", result.cmd, errMsg) + require.Equal(t, plugin.Completed, result.event, errMsg) + for i := 1; i < resultCount-1; i++ { + result = testResults[i] + require.Equal(t, "Query", result.cmd, errMsg) + require.Equal(t, plugin.Completed, result.event, errMsg) + } + } +} + +func loadPlugin(t *testing.T, onGeneralEvent func(context.Context, *variable.SessionVars, plugin.GeneralEvent, string)) { + ctx := context.Background() + pluginName := "audit_test" + pluginVersion := uint16(1) + pluginSign := pluginName + "-" + strconv.Itoa(int(pluginVersion)) + + cfg := plugin.Config{ + Plugins: []string{pluginSign}, + PluginDir: "", + EnvVersion: map[string]uint16{"go": 1112}, + } + + validate := func(ctx context.Context, manifest *plugin.Manifest) error { + return nil + } + onInit := func(ctx context.Context, manifest *plugin.Manifest) error { + return nil + } + onShutdown := func(ctx context.Context, manifest *plugin.Manifest) error { + return nil + } + onConnectionEvent := func(ctx context.Context, event plugin.ConnectionEvent, info *variable.ConnectionInfo) error { + return nil + } + + // setup load test hook. + loadOne := func(p *plugin.Plugin, dir string, pluginID plugin.ID) (manifest func() *plugin.Manifest, err error) { + return func() *plugin.Manifest { + m := &plugin.AuditManifest{ + Manifest: plugin.Manifest{ + Kind: plugin.Audit, + Name: pluginName, + Version: pluginVersion, + OnInit: onInit, + OnShutdown: onShutdown, + Validate: validate, + }, + OnGeneralEvent: onGeneralEvent, + OnConnectionEvent: onConnectionEvent, + } + return plugin.ExportManifest(m) + }, nil + } + plugin.SetTestHook(loadOne) + + // trigger load. + err := plugin.Load(ctx, cfg) + require.NoErrorf(t, err, "load plugin [%s] fail, error [%s]\n", pluginSign, err) + + err = plugin.Init(ctx, cfg) + require.NoErrorf(t, err, "init plugin [%s] fail, error [%s]\n", pluginSign, err) +} diff --git a/pkg/plugin/main_test.go b/pkg/plugin/main_test.go new file mode 100644 index 0000000000000..64640a479dc24 --- /dev/null +++ b/pkg/plugin/main_test.go @@ -0,0 +1,36 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package plugin + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("time.Sleep"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/plugin/plugin.go b/pkg/plugin/plugin.go similarity index 99% rename from plugin/plugin.go rename to pkg/plugin/plugin.go index 1590d057bbde2..2d5c57ab62687 100644 --- a/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -25,9 +25,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/plugin/plugin_test.go b/pkg/plugin/plugin_test.go similarity index 99% rename from plugin/plugin_test.go rename to pkg/plugin/plugin_test.go index 650ad432feb65..bc09aa7da1469 100644 --- a/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -21,7 +21,7 @@ import ( "testing" "unsafe" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/require" ) diff --git a/plugin/spi.go b/pkg/plugin/spi.go similarity index 100% rename from plugin/spi.go rename to pkg/plugin/spi.go diff --git a/plugin/spi_test.go b/pkg/plugin/spi_test.go similarity index 94% rename from plugin/spi_test.go rename to pkg/plugin/spi_test.go index d5f8fbdcd4d25..7cfb0973fe028 100644 --- a/plugin/spi_test.go +++ b/pkg/plugin/spi_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/require" ) diff --git a/pkg/privilege/BUILD.bazel b/pkg/privilege/BUILD.bazel new file mode 100644 index 0000000000000..2a8e6e17cba93 --- /dev/null +++ b/pkg/privilege/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "privilege", + srcs = ["privilege.go"], + importpath = "github.com/pingcap/tidb/pkg/privilege", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/privilege/conn", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/types", + ], +) diff --git a/pkg/privilege/conn/BUILD.bazel b/pkg/privilege/conn/BUILD.bazel new file mode 100644 index 0000000000000..06cad5a21aa28 --- /dev/null +++ b/pkg/privilege/conn/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "conn", + srcs = ["conn.go"], + importpath = "github.com/pingcap/tidb/pkg/privilege/conn", + visibility = ["//visibility:public"], +) diff --git a/privilege/conn/conn.go b/pkg/privilege/conn/conn.go similarity index 100% rename from privilege/conn/conn.go rename to pkg/privilege/conn/conn.go diff --git a/privilege/privilege.go b/pkg/privilege/privilege.go similarity index 95% rename from privilege/privilege.go rename to pkg/privilege/privilege.go index 609e48062ba12..1414238159842 100644 --- a/privilege/privilege.go +++ b/pkg/privilege/privilege.go @@ -15,12 +15,12 @@ package privilege import ( - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege/conn" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege/conn" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" ) type keyType int diff --git a/pkg/privilege/privileges/BUILD.bazel b/pkg/privilege/privileges/BUILD.bazel new file mode 100644 index 0000000000000..83a6920f734d4 --- /dev/null +++ b/pkg/privilege/privileges/BUILD.bazel @@ -0,0 +1,88 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "privileges", + srcs = [ + "cache.go", + "errors.go", + "privileges.go", + "tidb_auth_token.go", + ], + importpath = "github.com/pingcap/tidb/pkg/privilege/privileges", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/extension", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/privilege", + "//pkg/privilege/conn", + "//pkg/privilege/privileges/ldap", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/sem", + "//pkg/util/sqlexec", + "//pkg/util/stringutil", + "@com_github_lestrrat_go_jwx_v2//jwk", + "@com_github_lestrrat_go_jwx_v2//jws", + "@com_github_lestrrat_go_jwx_v2//jwt", + "@com_github_lestrrat_go_jwx_v2//jwt/openid", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "privileges_test", + timeout = "short", + srcs = [ + "cache_test.go", + "main_test.go", + "privileges_test.go", + "tidb_auth_token_test.go", + ], + embed = [":privileges"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/privilege", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/util", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/hack", + "//pkg/util/sem", + "//pkg/util/sqlexec", + "@com_github_lestrrat_go_jwx_v2//jwa", + "@com_github_lestrrat_go_jwx_v2//jwk", + "@com_github_lestrrat_go_jwx_v2//jws", + "@com_github_lestrrat_go_jwx_v2//jwt", + "@com_github_lestrrat_go_jwx_v2//jwt/openid", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/privilege/privileges/cache.go b/pkg/privilege/privileges/cache.go new file mode 100644 index 0000000000000..1a1ab654c3613 --- /dev/null +++ b/pkg/privilege/privileges/cache.go @@ -0,0 +1,1659 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package privileges + +import ( + "bytes" + "cmp" + "context" + "encoding/json" + "fmt" + "net" + "slices" + "strings" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "go.uber.org/zap" +) + +var ( + userTablePrivilegeMask = computePrivMask(mysql.AllGlobalPrivs) + dbTablePrivilegeMask = computePrivMask(mysql.AllDBPrivs) + tablePrivMask = computePrivMask(mysql.AllTablePrivs) +) + +const globalDBVisible = mysql.CreatePriv | mysql.SelectPriv | mysql.InsertPriv | mysql.UpdatePriv | mysql.DeletePriv | mysql.ShowDBPriv | mysql.DropPriv | mysql.AlterPriv | mysql.IndexPriv | mysql.CreateViewPriv | mysql.ShowViewPriv | mysql.GrantPriv | mysql.TriggerPriv | mysql.ReferencesPriv | mysql.ExecutePriv + +const ( + sqlLoadRoleGraph = "SELECT HIGH_PRIORITY FROM_USER, FROM_HOST, TO_USER, TO_HOST FROM mysql.role_edges" + sqlLoadGlobalPrivTable = "SELECT HIGH_PRIORITY Host,User,Priv FROM mysql.global_priv" + sqlLoadDBTable = "SELECT HIGH_PRIORITY Host,DB,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Index_priv,References_priv,Lock_tables_priv,Create_tmp_table_priv,Event_priv,Create_routine_priv,Alter_routine_priv,Alter_priv,Execute_priv,Create_view_priv,Show_view_priv,Trigger_priv FROM mysql.db ORDER BY host, db, user" + sqlLoadTablePrivTable = "SELECT HIGH_PRIORITY Host,DB,User,Table_name,Grantor,Timestamp,Table_priv,Column_priv FROM mysql.tables_priv" + sqlLoadColumnsPrivTable = "SELECT HIGH_PRIORITY Host,DB,User,Table_name,Column_name,Timestamp,Column_priv FROM mysql.columns_priv" + sqlLoadDefaultRoles = "SELECT HIGH_PRIORITY HOST, USER, DEFAULT_ROLE_HOST, DEFAULT_ROLE_USER FROM mysql.default_roles" + // list of privileges from mysql.Priv2UserCol + sqlLoadUserTable = `SELECT HIGH_PRIORITY Host,User,authentication_string, + Create_priv, Select_priv, Insert_priv, Update_priv, Delete_priv, Show_db_priv, Super_priv, + Create_user_priv,Create_tablespace_priv,Trigger_priv,Drop_priv,Process_priv,Grant_priv, + References_priv,Alter_priv,Execute_priv,Index_priv,Create_view_priv,Show_view_priv, + Create_role_priv,Drop_role_priv,Create_tmp_table_priv,Lock_tables_priv,Create_routine_priv, + Alter_routine_priv,Event_priv,Shutdown_priv,Reload_priv,File_priv,Config_priv,Repl_client_priv,Repl_slave_priv, + Account_locked,Plugin,Token_issuer,User_attributes,password_expired,password_last_changed,password_lifetime FROM mysql.user` + sqlLoadGlobalGrantsTable = `SELECT HIGH_PRIORITY Host,User,Priv,With_Grant_Option FROM mysql.global_grants` +) + +func computePrivMask(privs []mysql.PrivilegeType) mysql.PrivilegeType { + var mask mysql.PrivilegeType + for _, p := range privs { + mask |= p + } + return mask +} + +// baseRecord is used to represent a base record in privilege cache, +// it only store Host and User field, and it should be nested in other record type. +type baseRecord struct { + Host string // max length 60, primary key + User string // max length 32, primary key + + // patChars is compiled from Host, cached for pattern match performance. + patChars []byte + patTypes []byte + + // IPv4 with netmask, cached for host match performance. + hostIPNet *net.IPNet +} + +// MetadataInfo is the User_attributes->>"$.metadata". +type MetadataInfo struct { + Email string +} + +// UserAttributesInfo is the 'User_attributes' in privilege cache. +type UserAttributesInfo struct { + MetadataInfo + PasswordLocking +} + +// UserRecord is used to represent a user record in privilege cache. +type UserRecord struct { + baseRecord + UserAttributesInfo + + AuthenticationString string + Privileges mysql.PrivilegeType + AccountLocked bool // A role record when this field is true + AuthPlugin string + AuthTokenIssuer string + PasswordExpired bool + PasswordLastChanged time.Time + PasswordLifeTime int64 + ResourceGroup string +} + +// NewUserRecord return a UserRecord, only use for unit test. +func NewUserRecord(host, user string) UserRecord { + return UserRecord{ + baseRecord: baseRecord{ + Host: host, + User: user, + }, + } +} + +type globalPrivRecord struct { + baseRecord + + Priv GlobalPrivValue + Broken bool +} + +type dynamicPrivRecord struct { + baseRecord + + PrivilegeName string + GrantOption bool +} + +// SSLType is enum value for GlobalPrivValue.SSLType. +// the value is compatible with MySQL storage json value. +type SSLType int + +const ( + // SslTypeNotSpecified indicates . + SslTypeNotSpecified SSLType = iota - 1 + // SslTypeNone indicates not require use ssl. + SslTypeNone + // SslTypeAny indicates require use ssl but not validate cert. + SslTypeAny + // SslTypeX509 indicates require use ssl and validate cert. + SslTypeX509 + // SslTypeSpecified indicates require use ssl and validate cert's subject or issuer. + SslTypeSpecified +) + +// GlobalPrivValue is store json format for priv column in mysql.global_priv. +type GlobalPrivValue struct { + SSLType SSLType `json:"ssl_type,omitempty"` + SSLCipher string `json:"ssl_cipher,omitempty"` + X509Issuer string `json:"x509_issuer,omitempty"` + X509Subject string `json:"x509_subject,omitempty"` + SAN string `json:"san,omitempty"` + SANs map[util.SANType][]string `json:"-"` +} + +// RequireStr returns describe string after `REQUIRE` clause. +func (g *GlobalPrivValue) RequireStr() string { + require := "NONE" + switch g.SSLType { + case SslTypeAny: + require = "SSL" + case SslTypeX509: + require = "X509" + case SslTypeSpecified: + var s []string + if len(g.SSLCipher) > 0 { + s = append(s, "CIPHER") + s = append(s, "'"+g.SSLCipher+"'") + } + if len(g.X509Issuer) > 0 { + s = append(s, "ISSUER") + s = append(s, "'"+g.X509Issuer+"'") + } + if len(g.X509Subject) > 0 { + s = append(s, "SUBJECT") + s = append(s, "'"+g.X509Subject+"'") + } + if len(g.SAN) > 0 { + s = append(s, "SAN") + s = append(s, "'"+g.SAN+"'") + } + if len(s) > 0 { + require = strings.Join(s, " ") + } + } + return require +} + +type dbRecord struct { + baseRecord + + DB string + Privileges mysql.PrivilegeType + + dbPatChars []byte + dbPatTypes []byte +} + +type tablesPrivRecord struct { + baseRecord + + DB string + TableName string + Grantor string + Timestamp time.Time + TablePriv mysql.PrivilegeType + ColumnPriv mysql.PrivilegeType +} + +type columnsPrivRecord struct { + baseRecord + + DB string + TableName string + ColumnName string + Timestamp time.Time + ColumnPriv mysql.PrivilegeType +} + +// defaultRoleRecord is used to cache mysql.default_roles +type defaultRoleRecord struct { + baseRecord + + DefaultRoleUser string + DefaultRoleHost string +} + +// roleGraphEdgesTable is used to cache relationship between and role. +type roleGraphEdgesTable struct { + roleList map[string]*auth.RoleIdentity +} + +// Find method is used to find role from table +func (g roleGraphEdgesTable) Find(user, host string) bool { + if host == "" { + host = "%" + } + key := user + "@" + host + if g.roleList == nil { + return false + } + _, ok := g.roleList[key] + return ok +} + +// MySQLPrivilege is the in-memory cache of mysql privilege tables. +type MySQLPrivilege struct { + // In MySQL, a user identity consists of a user + host. + // Either portion of user or host can contain wildcards, + // requiring the privileges system to use a list-like + // structure instead of a hash. + + // TiDB contains a sensible behavior difference from MySQL, + // which is that usernames can not contain wildcards. + // This means that DB-records are organized in both a + // slice (p.DB) and a Map (p.DBMap). + + // This helps in the case that there are a number of users with + // non-full privileges (i.e. user.db entries). + User []UserRecord + UserMap map[string][]UserRecord // Accelerate User searching + Global map[string][]globalPrivRecord + Dynamic map[string][]dynamicPrivRecord + DB []dbRecord + DBMap map[string][]dbRecord // Accelerate DB searching + TablesPriv []tablesPrivRecord + TablesPrivMap map[string][]tablesPrivRecord // Accelerate TablesPriv searching + ColumnsPriv []columnsPrivRecord + DefaultRoles []defaultRoleRecord + RoleGraph map[string]roleGraphEdgesTable +} + +// FindAllUserEffectiveRoles is used to find all effective roles grant to this user. +// This method will filter out the roles that are not granted to the user but are still in activeRoles +func (p *MySQLPrivilege) FindAllUserEffectiveRoles(user, host string, activeRoles []*auth.RoleIdentity) []*auth.RoleIdentity { + grantedActiveRoles := make([]*auth.RoleIdentity, 0, len(activeRoles)) + for _, role := range activeRoles { + if p.FindRole(user, host, role) { + grantedActiveRoles = append(grantedActiveRoles, role) + } + } + return p.FindAllRole(grantedActiveRoles) +} + +// FindAllRole is used to find all roles grant to this user. +func (p *MySQLPrivilege) FindAllRole(activeRoles []*auth.RoleIdentity) []*auth.RoleIdentity { + queue, head := make([]*auth.RoleIdentity, 0, len(activeRoles)), 0 + queue = append(queue, activeRoles...) + // Using breadth first search to find all roles grant to this user. + visited, ret := make(map[string]bool), make([]*auth.RoleIdentity, 0) + for head < len(queue) { + role := queue[head] + if _, ok := visited[role.String()]; !ok { + visited[role.String()] = true + ret = append(ret, role) + key := role.Username + "@" + role.Hostname + if edgeTable, ok := p.RoleGraph[key]; ok { + for _, v := range edgeTable.roleList { + if _, ok := visited[v.String()]; !ok { + queue = append(queue, v) + } + } + } + } + head++ + } + return ret +} + +// FindRole is used to detect whether there is edges between users and roles. +func (p *MySQLPrivilege) FindRole(user string, host string, role *auth.RoleIdentity) bool { + rec := p.matchUser(user, host) + r := p.matchUser(role.Username, role.Hostname) + if rec != nil && r != nil { + key := rec.User + "@" + rec.Host + return p.RoleGraph[key].Find(role.Username, role.Hostname) + } + return false +} + +// LoadAll loads the tables from database to memory. +func (p *MySQLPrivilege) LoadAll(ctx sessionctx.Context) error { + err := p.LoadUserTable(ctx) + if err != nil { + logutil.BgLogger().Warn("load mysql.user fail", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.user") + } + + err = p.LoadGlobalPrivTable(ctx) + if err != nil { + return errors.Trace(err) + } + + err = p.LoadGlobalGrantsTable(ctx) + if err != nil { + return errors.Trace(err) + } + + err = p.LoadDBTable(ctx) + if err != nil { + if !noSuchTable(err) { + logutil.BgLogger().Warn("load mysql.db fail", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.db") + } + logutil.BgLogger().Warn("mysql.db maybe missing") + } + + err = p.LoadTablesPrivTable(ctx) + if err != nil { + if !noSuchTable(err) { + logutil.BgLogger().Warn("load mysql.tables_priv fail", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.tables_priv") + } + logutil.BgLogger().Warn("mysql.tables_priv missing") + } + + err = p.LoadDefaultRoles(ctx) + if err != nil { + if !noSuchTable(err) { + logutil.BgLogger().Warn("load mysql.roles", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.roles") + } + logutil.BgLogger().Warn("mysql.default_roles missing") + } + + err = p.LoadColumnsPrivTable(ctx) + if err != nil { + if !noSuchTable(err) { + logutil.BgLogger().Warn("load mysql.columns_priv", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.columns_priv") + } + logutil.BgLogger().Warn("mysql.columns_priv missing") + } + + err = p.LoadRoleGraph(ctx) + if err != nil { + if !noSuchTable(err) { + logutil.BgLogger().Warn("load mysql.role_edges", zap.Error(err)) + return errLoadPrivilege.FastGen("mysql.role_edges") + } + logutil.BgLogger().Warn("mysql.role_edges missing") + } + return nil +} + +func noSuchTable(err error) bool { + e1 := errors.Cause(err) + if e2, ok := e1.(*terror.Error); ok { + if terror.ErrCode(e2.Code()) == terror.ErrCode(mysql.ErrNoSuchTable) { + return true + } + } + return false +} + +// LoadRoleGraph loads the mysql.role_edges table from database. +func (p *MySQLPrivilege) LoadRoleGraph(ctx sessionctx.Context) error { + p.RoleGraph = make(map[string]roleGraphEdgesTable) + err := p.loadTable(ctx, sqlLoadRoleGraph, p.decodeRoleEdgesTable) + if err != nil { + return errors.Trace(err) + } + return nil +} + +// LoadUserTable loads the mysql.user table from database. +func (p *MySQLPrivilege) LoadUserTable(ctx sessionctx.Context) error { + err := p.loadTable(ctx, sqlLoadUserTable, p.decodeUserTableRow) + if err != nil { + return errors.Trace(err) + } + // See https://dev.mysql.com/doc/refman/8.0/en/connection-access.html + // When multiple matches are possible, the server must determine which of them to use. It resolves this issue as follows: + // 1. Whenever the server reads the user table into memory, it sorts the rows. + // 2. When a client attempts to connect, the server looks through the rows in sorted order. + // 3. The server uses the first row that matches the client host name and user name. + // The server uses sorting rules that order rows with the most-specific Host values first. + p.SortUserTable() + p.buildUserMap() + return nil +} + +func (p *MySQLPrivilege) buildUserMap() { + userMap := make(map[string][]UserRecord, len(p.User)) + for _, record := range p.User { + userMap[record.User] = append(userMap[record.User], record) + } + p.UserMap = userMap +} + +func compareBaseRecord(x, y *baseRecord) int { + // Compare two item by user's host first. + c1 := compareHost(x.Host, y.Host) + if c1 != 0 { + return c1 + } + // Then, compare item by user's name value. + return cmp.Compare(x.User, y.User) +} + +func compareUserRecord(x, y UserRecord) int { + return compareBaseRecord(&x.baseRecord, &y.baseRecord) +} + +// compareHost compares two host string using some special rules, return value 1, 0, -1 means > = <. +// TODO: Check how MySQL do it exactly, instead of guess its rules. +func compareHost(x, y string) int { + // The more-specific, the smaller it is. + // The pattern '%' means “any host” and is least specific. + if y == `%` { + if x == `%` { + return 0 + } + return -1 + } + + // The empty string '' also means “any host” but sorts after '%'. + if y == "" { + if x == "" { + return 0 + } + return -1 + } + + // One of them end with `%`. + xEnd := strings.HasSuffix(x, `%`) + yEnd := strings.HasSuffix(y, `%`) + if xEnd || yEnd { + switch { + case !xEnd && yEnd: + return -1 + case xEnd && !yEnd: + return 1 + case xEnd && yEnd: + // 192.168.199.% smaller than 192.168.% + // A not very accurate comparison, compare them by length. + if len(x) > len(y) { + return -1 + } + } + return 0 + } + + // For other case, the order is nondeterministic. + switch x < y { + case true: + return -1 + case false: + return 1 + } + return 0 +} + +// SortUserTable sorts p.User in the MySQLPrivilege struct. +func (p MySQLPrivilege) SortUserTable() { + slices.SortFunc(p.User, compareUserRecord) +} + +// LoadGlobalPrivTable loads the mysql.global_priv table from database. +func (p *MySQLPrivilege) LoadGlobalPrivTable(ctx sessionctx.Context) error { + return p.loadTable(ctx, sqlLoadGlobalPrivTable, p.decodeGlobalPrivTableRow) +} + +// LoadGlobalGrantsTable loads the mysql.global_priv table from database. +func (p *MySQLPrivilege) LoadGlobalGrantsTable(ctx sessionctx.Context) error { + return p.loadTable(ctx, sqlLoadGlobalGrantsTable, p.decodeGlobalGrantsTableRow) +} + +// LoadDBTable loads the mysql.db table from database. +func (p *MySQLPrivilege) LoadDBTable(ctx sessionctx.Context) error { + err := p.loadTable(ctx, sqlLoadDBTable, p.decodeDBTableRow) + if err != nil { + return err + } + p.buildDBMap() + return nil +} + +func compareDBRecord(x, y dbRecord) int { + return compareBaseRecord(&x.baseRecord, &y.baseRecord) +} + +func (p *MySQLPrivilege) buildDBMap() { + dbMap := make(map[string][]dbRecord, len(p.DB)) + for _, record := range p.DB { + dbMap[record.User] = append(dbMap[record.User], record) + } + + // Sort the records to make the matching rule work. + for _, records := range dbMap { + slices.SortFunc(records, compareDBRecord) + } + p.DBMap = dbMap +} + +// LoadTablesPrivTable loads the mysql.tables_priv table from database. +func (p *MySQLPrivilege) LoadTablesPrivTable(ctx sessionctx.Context) error { + err := p.loadTable(ctx, sqlLoadTablePrivTable, p.decodeTablesPrivTableRow) + if err != nil { + return err + } + p.buildTablesPrivMap() + return nil +} + +func (p *MySQLPrivilege) buildTablesPrivMap() { + tablesPrivMap := make(map[string][]tablesPrivRecord, len(p.TablesPriv)) + for _, record := range p.TablesPriv { + tablesPrivMap[record.User] = append(tablesPrivMap[record.User], record) + } + p.TablesPrivMap = tablesPrivMap +} + +// LoadColumnsPrivTable loads the mysql.columns_priv table from database. +func (p *MySQLPrivilege) LoadColumnsPrivTable(ctx sessionctx.Context) error { + return p.loadTable(ctx, sqlLoadColumnsPrivTable, p.decodeColumnsPrivTableRow) +} + +// LoadDefaultRoles loads the mysql.columns_priv table from database. +func (p *MySQLPrivilege) LoadDefaultRoles(ctx sessionctx.Context) error { + return p.loadTable(ctx, sqlLoadDefaultRoles, p.decodeDefaultRoleTableRow) +} + +func (p *MySQLPrivilege) loadTable(sctx sessionctx.Context, sql string, + decodeTableRow func(chunk.Row, []*ast.ResultField) error) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rs.Close) + fs := rs.Fields() + req := rs.NewChunk(nil) + for { + err = rs.Next(context.TODO(), req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + return nil + } + it := chunk.NewIterator4Chunk(req) + for row := it.Begin(); row != it.End(); row = it.Next() { + err = decodeTableRow(row, fs) + if err != nil { + return errors.Trace(err) + } + } + // NOTE: decodeTableRow decodes data from a chunk Row, that is a shallow copy. + // The result will reference memory in the chunk, so the chunk must not be reused + // here, otherwise some werid bug will happen! + req = chunk.Renew(req, sctx.GetSessionVars().MaxChunkSize) + } +} + +// parseHostIPNet parses an IPv4 address and its subnet mask (e.g. `127.0.0.0/255.255.255.0`), +// return the `IPNet` struct which represent the IP range info (e.g. `127.0.0.1 ~ 127.0.0.255`). +// `IPNet` is used to check if a giving IP (e.g. `127.0.0.1`) is in its IP range by call `IPNet.Contains(ip)`. +func parseHostIPNet(s string) *net.IPNet { + i := strings.IndexByte(s, '/') + if i < 0 { + return nil + } + hostIP := net.ParseIP(s[:i]).To4() + if hostIP == nil { + return nil + } + maskIP := net.ParseIP(s[i+1:]).To4() + if maskIP == nil { + return nil + } + mask := net.IPv4Mask(maskIP[0], maskIP[1], maskIP[2], maskIP[3]) + // We must ensure that: & == + // e.g. `127.0.0.1/255.0.0.0` is an illegal string, + // because `127.0.0.1` & `255.0.0.0` == `127.0.0.0`, but != `127.0.0.1` + // see https://dev.mysql.com/doc/refman/5.7/en/account-names.html + if !hostIP.Equal(hostIP.Mask(mask)) { + return nil + } + return &net.IPNet{ + IP: hostIP, + Mask: mask, + } +} + +func (record *baseRecord) assignUserOrHost(row chunk.Row, i int, f *ast.ResultField) { + switch f.ColumnAsName.L { + case "user": + record.User = row.GetString(i) + case "host": + record.Host = row.GetString(i) + record.patChars, record.patTypes = stringutil.CompilePatternBytes(record.Host, '\\') + record.hostIPNet = parseHostIPNet(record.Host) + } +} + +func (p *MySQLPrivilege) decodeUserTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value UserRecord + for i, f := range fs { + switch { + case f.ColumnAsName.L == "authentication_string": + value.AuthenticationString = row.GetString(i) + case f.ColumnAsName.L == "account_locked": + if row.GetEnum(i).String() == "Y" { + value.AccountLocked = true + } + case f.ColumnAsName.L == "plugin": + if row.GetString(i) != "" { + value.AuthPlugin = row.GetString(i) + } else { + value.AuthPlugin = mysql.AuthNativePassword + } + case f.ColumnAsName.L == "token_issuer": + value.AuthTokenIssuer = row.GetString(i) + case f.ColumnAsName.L == "user_attributes": + if row.IsNull(i) { + continue + } + bj := row.GetJSON(i) + pathExpr, err := types.ParseJSONPathExpr("$.metadata.email") + if err != nil { + return err + } + if emailBJ, found := bj.Extract([]types.JSONPathExpression{pathExpr}); found { + email, err := emailBJ.Unquote() + if err != nil { + return err + } + value.Email = email + } + pathExpr, err = types.ParseJSONPathExpr("$.resource_group") + if err != nil { + return err + } + if resourceGroup, found := bj.Extract([]types.JSONPathExpression{pathExpr}); found { + resourceGroup, err := resourceGroup.Unquote() + if err != nil { + return err + } + value.ResourceGroup = resourceGroup + } + passwordLocking := PasswordLocking{} + if err := passwordLocking.ParseJSON(bj); err != nil { + return err + } + value.FailedLoginAttempts = passwordLocking.FailedLoginAttempts + value.PasswordLockTimeDays = passwordLocking.PasswordLockTimeDays + value.FailedLoginCount = passwordLocking.FailedLoginCount + value.AutoLockedLastChanged = passwordLocking.AutoLockedLastChanged + value.AutoAccountLocked = passwordLocking.AutoAccountLocked + case f.ColumnAsName.L == "password_expired": + if row.GetEnum(i).String() == "Y" { + value.PasswordExpired = true + } + case f.ColumnAsName.L == "password_last_changed": + t := row.GetTime(i) + gotime, err := t.GoTime(time.Local) + if err != nil { + return err + } + value.PasswordLastChanged = gotime + case f.ColumnAsName.L == "password_lifetime": + if row.IsNull(i) { + value.PasswordLifeTime = -1 + continue + } + value.PasswordLifeTime = row.GetInt64(i) + case f.Column.GetType() == mysql.TypeEnum: + if row.GetEnum(i).String() != "Y" { + continue + } + priv, ok := mysql.Col2PrivType[f.ColumnAsName.O] + if !ok { + return errInvalidPrivilegeType.GenWithStack(f.ColumnAsName.O) + } + value.Privileges |= priv + default: + value.assignUserOrHost(row, i, f) + } + } + p.User = append(p.User, value) + return nil +} + +func (p *MySQLPrivilege) decodeGlobalPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value globalPrivRecord + for i, f := range fs { + if f.ColumnAsName.L == "priv" { + privData := row.GetString(i) + if len(privData) > 0 { + var privValue GlobalPrivValue + err := json.Unmarshal(hack.Slice(privData), &privValue) + if err != nil { + logutil.BgLogger().Error("one user global priv data is broken, forbidden login until data be fixed", + zap.String("user", value.User), zap.String("host", value.Host)) + value.Broken = true + } else { + value.Priv.SSLType = privValue.SSLType + value.Priv.SSLCipher = privValue.SSLCipher + value.Priv.X509Issuer = privValue.X509Issuer + value.Priv.X509Subject = privValue.X509Subject + value.Priv.SAN = privValue.SAN + if len(value.Priv.SAN) > 0 { + value.Priv.SANs, err = util.ParseAndCheckSAN(value.Priv.SAN) + if err != nil { + value.Broken = true + } + } + } + } + } else { + value.assignUserOrHost(row, i, f) + } + } + if p.Global == nil { + p.Global = make(map[string][]globalPrivRecord) + } + p.Global[value.User] = append(p.Global[value.User], value) + return nil +} + +func (p *MySQLPrivilege) decodeGlobalGrantsTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value dynamicPrivRecord + for i, f := range fs { + switch f.ColumnAsName.L { + case "priv": + value.PrivilegeName = strings.ToUpper(row.GetString(i)) + case "with_grant_option": + value.GrantOption = row.GetEnum(i).String() == "Y" + default: + value.assignUserOrHost(row, i, f) + } + } + if p.Dynamic == nil { + p.Dynamic = make(map[string][]dynamicPrivRecord) + } + p.Dynamic[value.User] = append(p.Dynamic[value.User], value) + return nil +} + +func (p *MySQLPrivilege) decodeDBTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value dbRecord + for i, f := range fs { + switch { + case f.ColumnAsName.L == "db": + value.DB = row.GetString(i) + value.dbPatChars, value.dbPatTypes = stringutil.CompilePatternBytes(strings.ToUpper(value.DB), '\\') + case f.Column.GetType() == mysql.TypeEnum: + if row.GetEnum(i).String() != "Y" { + continue + } + priv, ok := mysql.Col2PrivType[f.ColumnAsName.O] + if !ok { + return errInvalidPrivilegeType.GenWithStack("Unknown Privilege Type!") + } + value.Privileges |= priv + default: + value.assignUserOrHost(row, i, f) + } + } + p.DB = append(p.DB, value) + return nil +} + +func (p *MySQLPrivilege) decodeTablesPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value tablesPrivRecord + for i, f := range fs { + switch f.ColumnAsName.L { + case "db": + value.DB = row.GetString(i) + case "table_name": + value.TableName = row.GetString(i) + case "table_priv": + value.TablePriv = decodeSetToPrivilege(row.GetSet(i)) + case "column_priv": + value.ColumnPriv = decodeSetToPrivilege(row.GetSet(i)) + default: + value.assignUserOrHost(row, i, f) + } + } + p.TablesPriv = append(p.TablesPriv, value) + return nil +} + +func (p *MySQLPrivilege) decodeRoleEdgesTable(row chunk.Row, fs []*ast.ResultField) error { + var fromUser, fromHost, toHost, toUser string + for i, f := range fs { + switch f.ColumnAsName.L { + case "from_host": + fromHost = row.GetString(i) + case "from_user": + fromUser = row.GetString(i) + case "to_host": + toHost = row.GetString(i) + case "to_user": + toUser = row.GetString(i) + } + } + fromKey := fromUser + "@" + fromHost + toKey := toUser + "@" + toHost + roleGraph, ok := p.RoleGraph[toKey] + if !ok { + roleGraph = roleGraphEdgesTable{roleList: make(map[string]*auth.RoleIdentity)} + p.RoleGraph[toKey] = roleGraph + } + roleGraph.roleList[fromKey] = &auth.RoleIdentity{Username: fromUser, Hostname: fromHost} + return nil +} + +func (p *MySQLPrivilege) decodeDefaultRoleTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value defaultRoleRecord + for i, f := range fs { + switch f.ColumnAsName.L { + case "default_role_host": + value.DefaultRoleHost = row.GetString(i) + case "default_role_user": + value.DefaultRoleUser = row.GetString(i) + default: + value.assignUserOrHost(row, i, f) + } + } + p.DefaultRoles = append(p.DefaultRoles, value) + return nil +} + +func (p *MySQLPrivilege) decodeColumnsPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { + var value columnsPrivRecord + for i, f := range fs { + switch f.ColumnAsName.L { + case "db": + value.DB = row.GetString(i) + case "table_name": + value.TableName = row.GetString(i) + case "column_name": + value.ColumnName = row.GetString(i) + case "timestamp": + var err error + value.Timestamp, err = row.GetTime(i).GoTime(time.Local) + if err != nil { + return errors.Trace(err) + } + case "column_priv": + value.ColumnPriv = decodeSetToPrivilege(row.GetSet(i)) + default: + value.assignUserOrHost(row, i, f) + } + } + p.ColumnsPriv = append(p.ColumnsPriv, value) + return nil +} + +func decodeSetToPrivilege(s types.Set) mysql.PrivilegeType { + var ret mysql.PrivilegeType + if s.Name == "" { + return ret + } + for _, str := range strings.Split(s.Name, ",") { + priv, ok := mysql.SetStr2Priv[str] + if !ok { + logutil.BgLogger().Warn("unsupported privilege", zap.String("type", str)) + continue + } + ret |= priv + } + return ret +} + +// hostMatch checks if giving IP is in IP range of hostname. +// In MySQL, the hostname of user can be set to `/` +// e.g. `127.0.0.0/255.255.255.0` represent IP range from `127.0.0.1` to `127.0.0.255`, +// only IP addresses that satisfy this condition range can be login with this user. +// See https://dev.mysql.com/doc/refman/5.7/en/account-names.html +func (record *baseRecord) hostMatch(s string) bool { + if record.hostIPNet == nil { + if record.Host == "localhost" && net.ParseIP(s).IsLoopback() { + return true + } + return false + } + ip := net.ParseIP(s).To4() + if ip == nil { + return false + } + return record.hostIPNet.Contains(ip) +} + +func (record *baseRecord) match(user, host string) bool { + return record.User == user && (patternMatch(host, record.patChars, record.patTypes) || + record.hostMatch(host)) +} + +func (record *baseRecord) fullyMatch(user, host string) bool { + return record.User == user && record.Host == host +} + +func (record *dbRecord) match(user, host, db string) bool { + return record.baseRecord.match(user, host) && + patternMatch(strings.ToUpper(db), record.dbPatChars, record.dbPatTypes) +} + +func (record *tablesPrivRecord) match(user, host, db, table string) bool { + return record.baseRecord.match(user, host) && + strings.EqualFold(record.DB, db) && + strings.EqualFold(record.TableName, table) +} + +func (record *columnsPrivRecord) match(user, host, db, table, col string) bool { + return record.baseRecord.match(user, host) && + strings.EqualFold(record.DB, db) && + strings.EqualFold(record.TableName, table) && + strings.EqualFold(record.ColumnName, col) +} + +// patternMatch matches "%" the same way as ".*" in regular expression, for example, +// "10.0.%" would match "10.0.1" "10.0.1.118" ... +func patternMatch(str string, patChars, patTypes []byte) bool { + return stringutil.DoMatchBytes(str, patChars, patTypes) +} + +// matchIdentity finds an identity to match a user + host +// using the correct rules according to MySQL. +func (p *MySQLPrivilege) matchIdentity(user, host string, skipNameResolve bool) *UserRecord { + for i := 0; i < len(p.User); i++ { + record := &p.User[i] + if record.match(user, host) { + return record + } + } + + // If skip-name resolve is not enabled, and the host is not localhost + // we can fallback and try to resolve with all addrs that match. + // TODO: this is imported from previous code in session.Auth(), and can be improved in future. + if !skipNameResolve && host != variable.DefHostname { + addrs, err := net.LookupAddr(host) + if err != nil { + logutil.BgLogger().Warn( + "net.LookupAddr returned an error during auth check", + zap.String("host", host), + zap.Error(err), + ) + return nil + } + for _, addr := range addrs { + for i := 0; i < len(p.User); i++ { + record := &p.User[i] + if record.match(user, addr) { + return record + } + } + } + } + return nil +} + +// matchResoureGroup finds an identity to match resource group. +func (p *MySQLPrivilege) matchResoureGroup(resourceGroupName string) *UserRecord { + for i := 0; i < len(p.User); i++ { + record := &p.User[i] + if record.ResourceGroup == resourceGroupName { + return record + } + } + return nil +} + +// connectionVerification verifies the username + hostname according to exact +// match from the mysql.user privilege table. call matchIdentity() first if you +// do not have an exact match yet. +func (p *MySQLPrivilege) connectionVerification(user, host string) *UserRecord { + records, exists := p.UserMap[user] + if exists { + for i := 0; i < len(records); i++ { + record := &records[i] + if record.Host == host { // exact match + return record + } + } + } + return nil +} + +func (p *MySQLPrivilege) matchGlobalPriv(user, host string) *globalPrivRecord { + uGlobal, exists := p.Global[user] + if !exists { + return nil + } + for i := 0; i < len(uGlobal); i++ { + record := &uGlobal[i] + if record.match(user, host) { + return record + } + } + return nil +} + +func (p *MySQLPrivilege) matchUser(user, host string) *UserRecord { + records, exists := p.UserMap[user] + if exists { + for i := 0; i < len(records); i++ { + record := &records[i] + if record.match(user, host) { + return record + } + } + } + return nil +} + +func (p *MySQLPrivilege) matchDB(user, host, db string) *dbRecord { + records, exists := p.DBMap[user] + if exists { + for i := 0; i < len(records); i++ { + record := &records[i] + if record.match(user, host, db) { + return record + } + } + } + return nil +} + +func (p *MySQLPrivilege) matchTables(user, host, db, table string) *tablesPrivRecord { + records, exists := p.TablesPrivMap[user] + if exists { + for i := 0; i < len(records); i++ { + record := &records[i] + if record.match(user, host, db, table) { + return record + } + } + } + return nil +} + +func (p *MySQLPrivilege) matchColumns(user, host, db, table, column string) *columnsPrivRecord { + for i := 0; i < len(p.ColumnsPriv); i++ { + record := &p.ColumnsPriv[i] + if record.match(user, host, db, table, column) { + return record + } + } + return nil +} + +// HasExplicitlyGrantedDynamicPrivilege checks if a user has a DYNAMIC privilege +// without accepting SUPER privilege as a fallback. +func (p *MySQLPrivilege) HasExplicitlyGrantedDynamicPrivilege(activeRoles []*auth.RoleIdentity, user, host, privName string, withGrant bool) bool { + privName = strings.ToUpper(privName) + roleList := p.FindAllUserEffectiveRoles(user, host, activeRoles) + roleList = append(roleList, &auth.RoleIdentity{Username: user, Hostname: host}) + // Loop through each of the roles and return on first match + // If grantable is required, ensure the record has the GrantOption set. + for _, r := range roleList { + u := r.Username + h := r.Hostname + for _, record := range p.Dynamic[u] { + if record.match(u, h) { + if withGrant && !record.GrantOption { + continue + } + if record.PrivilegeName == privName { + return true + } + } + } + } + return false +} + +// RequestDynamicVerification checks all roles for a specific DYNAMIC privilege. +func (p *MySQLPrivilege) RequestDynamicVerification(activeRoles []*auth.RoleIdentity, user, host, privName string, withGrant bool) bool { + privName = strings.ToUpper(privName) + if p.HasExplicitlyGrantedDynamicPrivilege(activeRoles, user, host, privName, withGrant) { + return true + } + // If SEM is enabled, and the privilege is of type restricted, do not fall through + // To using SUPER as a replacement privilege. + if sem.IsEnabled() && sem.IsRestrictedPrivilege(privName) { + return false + } + // For compatibility reasons, the SUPER privilege also has all DYNAMIC privileges granted to it (dynamic privs are a super replacement) + // This may be changed in future, but will require a bootstrap task to assign all dynamic privileges + // to users with SUPER, otherwise tasks such as BACKUP and ROLE_ADMIN will start to fail. + // The visitInfo system will also need modification to support OR conditions. + if withGrant && !p.RequestVerification(activeRoles, user, host, "", "", "", mysql.GrantPriv) { + return false + } + return p.RequestVerification(activeRoles, user, host, "", "", "", mysql.SuperPriv) +} + +// RequestVerification checks whether the user have sufficient privileges to do the operation. +func (p *MySQLPrivilege) RequestVerification(activeRoles []*auth.RoleIdentity, user, host, db, table, column string, priv mysql.PrivilegeType) bool { + if priv == mysql.UsagePriv { + return true + } + + roleList := p.FindAllUserEffectiveRoles(user, host, activeRoles) + roleList = append(roleList, &auth.RoleIdentity{Username: user, Hostname: host}) + + var userPriv, dbPriv, tablePriv, columnPriv mysql.PrivilegeType + for _, r := range roleList { + userRecord := p.matchUser(r.Username, r.Hostname) + if userRecord != nil { + userPriv |= userRecord.Privileges + } + } + if userPriv&priv > 0 { + return true + } + + for _, r := range roleList { + dbRecord := p.matchDB(r.Username, r.Hostname, db) + if dbRecord != nil { + dbPriv |= dbRecord.Privileges + } + } + if dbPriv&priv > 0 { + return true + } + + for _, r := range roleList { + tableRecord := p.matchTables(r.Username, r.Hostname, db, table) + if tableRecord != nil { + tablePriv |= tableRecord.TablePriv + if column != "" { + columnPriv |= tableRecord.ColumnPriv + } + } + } + if tablePriv&priv > 0 || columnPriv&priv > 0 { + return true + } + + columnPriv = 0 + for _, r := range roleList { + columnRecord := p.matchColumns(r.Username, r.Hostname, db, table, column) + if columnRecord != nil { + columnPriv |= columnRecord.ColumnPriv + } + } + if columnPriv&priv > 0 { + return true + } + + return priv == 0 +} + +// DBIsVisible checks whether the user can see the db. +func (p *MySQLPrivilege) DBIsVisible(user, host, db string) bool { + if record := p.matchUser(user, host); record != nil { + if record.Privileges&globalDBVisible > 0 { + return true + } + // For metrics_schema, `PROCESS` can also work. + if record.Privileges&mysql.ProcessPriv > 0 && strings.EqualFold(db, util.MetricSchemaName.O) { + return true + } + } + + // INFORMATION_SCHEMA is visible to all users. + if strings.EqualFold(db, "INFORMATION_SCHEMA") { + return true + } + + if record := p.matchDB(user, host, db); record != nil { + if record.Privileges > 0 { + return true + } + } + + for _, record := range p.TablesPriv { + if record.baseRecord.match(user, host) && + strings.EqualFold(record.DB, db) { + if record.TablePriv != 0 || record.ColumnPriv != 0 { + return true + } + } + } + + for _, record := range p.ColumnsPriv { + if record.baseRecord.match(user, host) && + strings.EqualFold(record.DB, db) { + if record.ColumnPriv != 0 { + return true + } + } + } + + return false +} + +func (p *MySQLPrivilege) showGrants(ctx sessionctx.Context, user, host string, roles []*auth.RoleIdentity) []string { + var gs []string //nolint: prealloc + var sortFromIdx int + var hasGlobalGrant = false + // Some privileges may granted from role inheritance. + // We should find these inheritance relationship. + allRoles := p.FindAllUserEffectiveRoles(user, host, roles) + // Show global grants. + var currentPriv mysql.PrivilegeType + var userExists = false + // Check whether user exists. + if userList, ok := p.UserMap[user]; ok { + for _, record := range userList { + if record.fullyMatch(user, host) { + userExists = true + break + } + } + if !userExists { + return gs + } + } + var g string + for _, record := range p.User { + if record.fullyMatch(user, host) { + hasGlobalGrant = true + currentPriv |= record.Privileges + } else { + for _, r := range allRoles { + if record.baseRecord.match(r.Username, r.Hostname) { + hasGlobalGrant = true + currentPriv |= record.Privileges + } + } + } + } + g = userPrivToString(currentPriv) + if len(g) > 0 { + var s string + if (currentPriv & mysql.GrantPriv) > 0 { + s = fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s' WITH GRANT OPTION`, g, user, host) + } else { + s = fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s'`, g, user, host) + } + gs = append(gs, s) + } + + // This is a mysql convention. + if len(gs) == 0 && hasGlobalGrant { + var s string + if (currentPriv & mysql.GrantPriv) > 0 { + s = fmt.Sprintf("GRANT USAGE ON *.* TO '%s'@'%s' WITH GRANT OPTION", user, host) + } else { + s = fmt.Sprintf("GRANT USAGE ON *.* TO '%s'@'%s'", user, host) + } + gs = append(gs, s) + } + + // Show db scope grants. + sortFromIdx = len(gs) + dbPrivTable := make(map[string]mysql.PrivilegeType) + for _, record := range p.DB { + if record.fullyMatch(user, host) { + dbPrivTable[record.DB] |= record.Privileges + } else { + for _, r := range allRoles { + if record.baseRecord.match(r.Username, r.Hostname) { + dbPrivTable[record.DB] |= record.Privileges + } + } + } + } + + sqlMode := ctx.GetSessionVars().SQLMode + for dbName, priv := range dbPrivTable { + dbName = stringutil.Escape(dbName, sqlMode) + g := dbPrivToString(priv) + if len(g) > 0 { + var s string + if (priv & mysql.GrantPriv) > 0 { + s = fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s' WITH GRANT OPTION`, g, dbName, user, host) + } else { + s = fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s'`, g, dbName, user, host) + } + gs = append(gs, s) + } else if len(g) == 0 && (priv&mysql.GrantPriv) > 0 { + // We have GRANT OPTION on the db, but no privilege granted. + // Wo we need to print a special USAGE line. + s := fmt.Sprintf(`GRANT USAGE ON %s.* TO '%s'@'%s' WITH GRANT OPTION`, dbName, user, host) + gs = append(gs, s) + } + } + slices.Sort(gs[sortFromIdx:]) + + // Show table scope grants. + sortFromIdx = len(gs) + tablePrivTable := make(map[string]mysql.PrivilegeType) + for _, record := range p.TablesPriv { + recordKey := stringutil.Escape(record.DB, sqlMode) + "." + stringutil.Escape(record.TableName, sqlMode) + if user == record.User && host == record.Host { + tablePrivTable[recordKey] |= record.TablePriv + } else { + for _, r := range allRoles { + if record.baseRecord.match(r.Username, r.Hostname) { + tablePrivTable[recordKey] |= record.TablePriv + } + } + } + } + for k, priv := range tablePrivTable { + g := tablePrivToString(priv) + if len(g) > 0 { + var s string + if (priv & mysql.GrantPriv) > 0 { + s = fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s' WITH GRANT OPTION`, g, k, user, host) + } else { + s = fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s'`, g, k, user, host) + } + gs = append(gs, s) + } else if len(g) == 0 && (priv&mysql.GrantPriv) > 0 { + // We have GRANT OPTION on the table, but no privilege granted. + // Wo we need to print a special USAGE line. + s := fmt.Sprintf(`GRANT USAGE ON %s TO '%s'@'%s' WITH GRANT OPTION`, k, user, host) + gs = append(gs, s) + } + } + slices.Sort(gs[sortFromIdx:]) + + // Show column scope grants, column and table are combined. + // A map of "DB.Table" => Priv(col1, col2 ...) + sortFromIdx = len(gs) + columnPrivTable := make(map[string]privOnColumns) + for i := range p.ColumnsPriv { + record := p.ColumnsPriv[i] + if !collectColumnGrant(&record, user, host, columnPrivTable, sqlMode) { + for _, r := range allRoles { + collectColumnGrant(&record, r.Username, r.Hostname, columnPrivTable, sqlMode) + } + } + } + for k, v := range columnPrivTable { + privCols := privOnColumnsToString(v) + s := fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s'`, privCols, k, user, host) + gs = append(gs, s) + } + slices.Sort(gs[sortFromIdx:]) + + // Show role grants. + graphKey := user + "@" + host + edgeTable, ok := p.RoleGraph[graphKey] + g = "" + if ok { + sortedRes := make([]string, 0, 10) + for k := range edgeTable.roleList { + role := strings.Split(k, "@") + roleName, roleHost := role[0], role[1] + tmp := fmt.Sprintf("'%s'@'%s'", roleName, roleHost) + sortedRes = append(sortedRes, tmp) + } + slices.Sort(sortedRes) + for i, r := range sortedRes { + g += r + if i != len(sortedRes)-1 { + g += ", " + } + } + s := fmt.Sprintf(`GRANT %s TO '%s'@'%s'`, g, user, host) + gs = append(gs, s) + } + + // If the SHOW GRANTS is for the current user, there might be activeRoles (allRoles) + // The convention is to merge the Dynamic privileges assigned to the user with + // inherited dynamic privileges from those roles + dynamicPrivsMap := make(map[string]bool) // privName, grantable + for _, record := range p.Dynamic[user] { + if record.fullyMatch(user, host) { + dynamicPrivsMap[record.PrivilegeName] = record.GrantOption + } + } + for _, r := range allRoles { + for _, record := range p.Dynamic[r.Username] { + if record.fullyMatch(r.Username, r.Hostname) { + // If the record already exists in the map and it's grantable + // skip doing anything, because we might inherit a non-grantable permission + // from a role, and don't want to clobber the existing privilege. + if grantable, ok := dynamicPrivsMap[record.PrivilegeName]; ok && grantable { + continue + } + dynamicPrivsMap[record.PrivilegeName] = record.GrantOption + } + } + } + + // Convert the map to a slice so it can be sorted to be deterministic and joined + var dynamicPrivs, grantableDynamicPrivs []string + for privName, grantable := range dynamicPrivsMap { + if grantable { + grantableDynamicPrivs = append(grantableDynamicPrivs, privName) + } else { + dynamicPrivs = append(dynamicPrivs, privName) + } + } + + // Merge the DYNAMIC privs into a line for non-grantable and then grantable. + if len(dynamicPrivs) > 0 { + slices.Sort(dynamicPrivs) + s := fmt.Sprintf("GRANT %s ON *.* TO '%s'@'%s'", strings.Join(dynamicPrivs, ","), user, host) + gs = append(gs, s) + } + if len(grantableDynamicPrivs) > 0 { + slices.Sort(grantableDynamicPrivs) + s := fmt.Sprintf("GRANT %s ON *.* TO '%s'@'%s' WITH GRANT OPTION", strings.Join(grantableDynamicPrivs, ","), user, host) + gs = append(gs, s) + } + return gs +} + +type columnStr = string +type columnStrs = []columnStr +type privOnColumns = map[mysql.PrivilegeType]columnStrs + +func privOnColumnsToString(p privOnColumns) string { + var buf bytes.Buffer + idx := 0 + for _, priv := range mysql.AllColumnPrivs { + v, ok := p[priv] + if !ok || len(v) == 0 { + continue + } + + if idx > 0 { + buf.WriteString(", ") + } + privStr := PrivToString(priv, mysql.AllColumnPrivs, mysql.Priv2Str) + fmt.Fprintf(&buf, "%s(", privStr) + for i, col := range v { + if i > 0 { + fmt.Fprintf(&buf, ", ") + } + buf.WriteString(col) + } + buf.WriteString(")") + idx++ + } + return buf.String() +} + +func collectColumnGrant(record *columnsPrivRecord, user, host string, columnPrivTable map[string]privOnColumns, sqlMode mysql.SQLMode) bool { + if record.baseRecord.match(user, host) { + recordKey := stringutil.Escape(record.DB, sqlMode) + "." + stringutil.Escape(record.TableName, sqlMode) + + privColumns, ok := columnPrivTable[recordKey] + if !ok { + privColumns = make(map[mysql.PrivilegeType]columnStrs) + } + + for _, priv := range mysql.AllColumnPrivs { + if priv&record.ColumnPriv > 0 { + old := privColumns[priv] + privColumns[priv] = append(old, record.ColumnName) + columnPrivTable[recordKey] = privColumns + } + } + return true + } + return false +} + +func userPrivToString(privs mysql.PrivilegeType) string { + if (privs & ^mysql.GrantPriv) == userTablePrivilegeMask { + return mysql.AllPrivilegeLiteral + } + return PrivToString(privs, mysql.AllGlobalPrivs, mysql.Priv2Str) +} + +func dbPrivToString(privs mysql.PrivilegeType) string { + if (privs & ^mysql.GrantPriv) == dbTablePrivilegeMask { + return mysql.AllPrivilegeLiteral + } + return PrivToString(privs, mysql.AllDBPrivs, mysql.Priv2SetStr) +} + +func tablePrivToString(privs mysql.PrivilegeType) string { + if (privs & ^mysql.GrantPriv) == tablePrivMask { + return mysql.AllPrivilegeLiteral + } + return PrivToString(privs, mysql.AllTablePrivs, mysql.Priv2Str) +} + +// PrivToString converts the privileges to string. +func PrivToString(priv mysql.PrivilegeType, allPrivs []mysql.PrivilegeType, allPrivNames map[mysql.PrivilegeType]string) string { + pstrs := make([]string, 0, 20) + for _, p := range allPrivs { + if priv&p == 0 { + continue + } + s := strings.ToUpper(allPrivNames[p]) + pstrs = append(pstrs, s) + } + return strings.Join(pstrs, ",") +} + +// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGES table. +func (p *MySQLPrivilege) UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum { + // Seeing all users requires SELECT ON * FROM mysql.* + // The SUPER privilege (or any other dynamic privilege) doesn't help here. + // This is verified against MySQL. + showOtherUsers := p.RequestVerification(activeRoles, user, host, mysql.SystemDB, "", "", mysql.SelectPriv) + var rows [][]types.Datum + for _, u := range p.User { + if showOtherUsers || u.match(user, host) { + rows = appendUserPrivilegesTableRow(rows, u) + } + } + for _, dynamicPrivs := range p.Dynamic { + for _, dynamicPriv := range dynamicPrivs { + if showOtherUsers || dynamicPriv.match(user, host) { + rows = appendDynamicPrivRecord(rows, dynamicPriv) + } + } + } + return rows +} + +func appendDynamicPrivRecord(rows [][]types.Datum, user dynamicPrivRecord) [][]types.Datum { + isGrantable := "NO" + if user.GrantOption { + isGrantable = "YES" + } + grantee := fmt.Sprintf("'%s'@'%s'", user.User, user.Host) + record := types.MakeDatums(grantee, "def", user.PrivilegeName, isGrantable) + return append(rows, record) +} + +func appendUserPrivilegesTableRow(rows [][]types.Datum, user UserRecord) [][]types.Datum { + var isGrantable string + if user.Privileges&mysql.GrantPriv > 0 { + isGrantable = "YES" + } else { + isGrantable = "NO" + } + grantee := fmt.Sprintf("'%s'@'%s'", user.User, user.Host) + if user.Privileges <= 1 { + // The "USAGE" row only appears if the user has no non-DYNAMIC privileges. + // This behavior was observed in MySQL. + record := types.MakeDatums(grantee, "def", "USAGE", "NO") + return append(rows, record) + } + for _, priv := range mysql.AllGlobalPrivs { + if user.Privileges&priv > 0 { + privilegeType := strings.ToUpper(mysql.Priv2Str[priv]) + // +---------------------------+---------------+-------------------------+--------------+ + // | GRANTEE | TABLE_CATALOG | PRIVILEGE_TYPE | IS_GRANTABLE | + // +---------------------------+---------------+-------------------------+--------------+ + // | 'root'@'localhost' | def | SELECT | YES | + record := types.MakeDatums(grantee, "def", privilegeType, isGrantable) + rows = append(rows, record) + } + } + return rows +} + +func (p *MySQLPrivilege) getDefaultRoles(user, host string) []*auth.RoleIdentity { + ret := make([]*auth.RoleIdentity, 0) + for _, r := range p.DefaultRoles { + if r.match(user, host) { + ret = append(ret, &auth.RoleIdentity{Username: r.DefaultRoleUser, Hostname: r.DefaultRoleHost}) + } + } + return ret +} + +func (p *MySQLPrivilege) getAllRoles(user, host string) []*auth.RoleIdentity { + key := user + "@" + host + edgeTable, ok := p.RoleGraph[key] + ret := make([]*auth.RoleIdentity, 0, len(edgeTable.roleList)) + if ok { + for _, r := range edgeTable.roleList { + ret = append(ret, r) + } + } + return ret +} + +// Handle wraps MySQLPrivilege providing thread safe access. +type Handle struct { + priv atomic.Value +} + +// NewHandle returns a Handle. +func NewHandle() *Handle { + return &Handle{} +} + +// Get the MySQLPrivilege for read. +func (h *Handle) Get() *MySQLPrivilege { + return h.priv.Load().(*MySQLPrivilege) +} + +// Update loads all the privilege info from kv storage. +func (h *Handle) Update(ctx sessionctx.Context) error { + var priv MySQLPrivilege + err := priv.LoadAll(ctx) + if err != nil { + return err + } + + h.priv.Store(&priv) + return nil +} diff --git a/pkg/privilege/privileges/cache_test.go b/pkg/privilege/privileges/cache_test.go new file mode 100644 index 0000000000000..8af63cb523d66 --- /dev/null +++ b/pkg/privilege/privileges/cache_test.go @@ -0,0 +1,574 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package privileges_test + +import ( + "fmt" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestLoadUserTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table user;") + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadUserTable(tk.Session())) + require.Len(t, p.User, 0) + + // Host | User | authentication_string | Select_priv | Insert_priv | Update_priv | Delete_priv | Create_priv | Drop_priv | Process_priv | Grant_priv | References_priv | Alter_priv | Show_db_priv | Super_priv | Execute_priv | Index_priv | Create_user_priv | Trigger_priv + tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Select_priv) VALUES ("%", "root", "", "Y")`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Insert_priv) VALUES ("%", "root1", "admin", "Y")`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Update_priv, Show_db_priv, References_priv) VALUES ("%", "root11", "", "Y", "Y", "Y")`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Create_user_priv, Index_priv, Execute_priv, Create_view_priv, Show_view_priv, Show_db_priv, Super_priv, Trigger_priv) VALUES ("%", "root111", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, user_attributes, token_issuer) VALUES ("%", "root1111", "{\"metadata\": {\"email\": \"user@pingcap.com\"}}", "")`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, password_expired, password_last_changed, password_lifetime) VALUES ("%", "root2", "Y", "2022-10-10 12:00:00", 3)`) + tk.MustExec(`INSERT INTO mysql.user (Host, User, password_expired, password_last_changed) VALUES ("%", "root3", "N", "2022-10-10 12:00:00")`) + + p = privileges.MySQLPrivilege{} + require.NoError(t, p.LoadUserTable(tk.Session())) + require.Len(t, p.User, len(p.UserMap)) + + user := p.User + require.Equal(t, "root", user[0].User) + require.Equal(t, mysql.SelectPriv, user[0].Privileges) + require.Equal(t, mysql.InsertPriv, user[1].Privileges) + require.Equal(t, mysql.UpdatePriv|mysql.ShowDBPriv|mysql.ReferencesPriv, user[2].Privileges) + require.Equal(t, mysql.CreateUserPriv|mysql.IndexPriv|mysql.ExecutePriv|mysql.CreateViewPriv|mysql.ShowViewPriv|mysql.ShowDBPriv|mysql.SuperPriv|mysql.TriggerPriv, user[3].Privileges) + require.Equal(t, "user@pingcap.com", user[4].Email) + require.Equal(t, "", user[4].AuthTokenIssuer) + require.Equal(t, true, user[5].PasswordExpired) + require.Equal(t, time.Date(2022, 10, 10, 12, 0, 0, 0, time.Local), user[5].PasswordLastChanged) + require.Equal(t, int64(3), user[5].PasswordLifeTime) + require.Equal(t, false, user[6].PasswordExpired) + require.Equal(t, time.Date(2022, 10, 10, 12, 0, 0, 0, time.Local), user[6].PasswordLastChanged) + require.Equal(t, int64(-1), user[6].PasswordLifeTime) +} + +func TestLoadGlobalPrivTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table global_priv") + + tk.MustExec(`INSERT INTO mysql.global_priv VALUES ("%", "tu", "{\"access\":0,\"plugin\":\"mysql_native_password\",\"ssl_type\":3, + \"ssl_cipher\":\"cipher\",\"x509_subject\":\"\C=ZH1\", \"x509_issuer\":\"\C=ZH2\", \"san\":\"\IP:127.0.0.1, IP:1.1.1.1, DNS:pingcap.com, URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1\", \"password_last_changed\":1}")`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadGlobalPrivTable(tk.Session())) + require.Equal(t, `%`, p.Global["tu"][0].Host) + require.Equal(t, `tu`, p.Global["tu"][0].User) + require.Equal(t, privileges.SslTypeSpecified, p.Global["tu"][0].Priv.SSLType) + require.Equal(t, "C=ZH2", p.Global["tu"][0].Priv.X509Issuer) + require.Equal(t, "C=ZH1", p.Global["tu"][0].Priv.X509Subject) + require.Equal(t, "IP:127.0.0.1, IP:1.1.1.1, DNS:pingcap.com, URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1", p.Global["tu"][0].Priv.SAN) + require.Len(t, p.Global["tu"][0].Priv.SANs[util.IP], 2) + require.Equal(t, "pingcap.com", p.Global["tu"][0].Priv.SANs[util.DNS][0]) + require.Equal(t, "spiffe://mesh.pingcap.com/ns/timesh/sa/me1", p.Global["tu"][0].Priv.SANs[util.URI][0]) +} + +func TestLoadDBTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table db;") + + tk.MustExec(`INSERT INTO mysql.db (Host, DB, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv) VALUES ("%", "information_schema", "root", "Y", "Y", "Y", "Y", "Y")`) + tk.MustExec(`INSERT INTO mysql.db (Host, DB, User, Drop_priv, Grant_priv, Index_priv, Alter_priv, Create_view_priv, Show_view_priv, Execute_priv) VALUES ("%", "mysql", "root1", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadDBTable(tk.Session())) + require.Len(t, p.DB, len(p.DBMap)) + + require.Equal(t, mysql.SelectPriv|mysql.InsertPriv|mysql.UpdatePriv|mysql.DeletePriv|mysql.CreatePriv, p.DB[0].Privileges) + require.Equal(t, mysql.DropPriv|mysql.GrantPriv|mysql.IndexPriv|mysql.AlterPriv|mysql.CreateViewPriv|mysql.ShowViewPriv|mysql.ExecutePriv, p.DB[1].Privileges) +} + +func TestLoadTablesPrivTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table tables_priv") + + tk.MustExec(`INSERT INTO mysql.tables_priv VALUES ("%", "db", "user", "table", "grantor", "2017-01-04 16:33:42.235831", "Grant,Index,Alter", "Insert,Update")`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadTablesPrivTable(tk.Session())) + require.Len(t, p.TablesPriv, len(p.TablesPrivMap)) + + require.Equal(t, `%`, p.TablesPriv[0].Host) + require.Equal(t, "db", p.TablesPriv[0].DB) + require.Equal(t, "user", p.TablesPriv[0].User) + require.Equal(t, "table", p.TablesPriv[0].TableName) + require.Equal(t, mysql.GrantPriv|mysql.IndexPriv|mysql.AlterPriv, p.TablesPriv[0].TablePriv) + require.Equal(t, mysql.InsertPriv|mysql.UpdatePriv, p.TablesPriv[0].ColumnPriv) +} + +func TestLoadColumnsPrivTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table columns_priv") + + tk.MustExec(`INSERT INTO mysql.columns_priv VALUES ("%", "db", "user", "table", "column", "2017-01-04 16:33:42.235831", "Insert,Update")`) + tk.MustExec(`INSERT INTO mysql.columns_priv VALUES ("127.0.0.1", "db", "user", "table", "column", "2017-01-04 16:33:42.235831", "Select")`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadColumnsPrivTable(tk.Session())) + require.Equal(t, `%`, p.ColumnsPriv[0].Host) + require.Equal(t, "db", p.ColumnsPriv[0].DB) + require.Equal(t, "user", p.ColumnsPriv[0].User) + require.Equal(t, "table", p.ColumnsPriv[0].TableName) + require.Equal(t, "column", p.ColumnsPriv[0].ColumnName) + require.Equal(t, mysql.InsertPriv|mysql.UpdatePriv, p.ColumnsPriv[0].ColumnPriv) + require.Equal(t, mysql.SelectPriv, p.ColumnsPriv[1].ColumnPriv) +} + +func TestLoadDefaultRoleTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table default_roles") + + tk.MustExec(`INSERT INTO mysql.default_roles VALUES ("%", "test_default_roles", "localhost", "r_1")`) + tk.MustExec(`INSERT INTO mysql.default_roles VALUES ("%", "test_default_roles", "localhost", "r_2")`) + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadDefaultRoles(tk.Session())) + require.Equal(t, `%`, p.DefaultRoles[0].Host) + require.Equal(t, "test_default_roles", p.DefaultRoles[0].User) + require.Equal(t, "localhost", p.DefaultRoles[0].DefaultRoleHost) + require.Equal(t, "r_1", p.DefaultRoles[0].DefaultRoleUser) + require.Equal(t, "localhost", p.DefaultRoles[1].DefaultRoleHost) +} + +func TestPatternMatch(t *testing.T) { + store := createStoreAndPrepareDB(t) + + activeRoles := make([]*auth.RoleIdentity, 0) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("USE MYSQL;") + tk.MustExec("TRUNCATE TABLE mysql.user") + tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("10.0.%", "root", "Y", "Y")`) + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadUserTable(tk.Session())) + require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1", "test", "", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1.118", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "114.114.114.114", "test", "", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "root", "114.114.114.114", "test", "", "", mysql.PrivilegeType(0))) + require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1.118", "test", "", "", mysql.ShutdownPriv)) + + tk.MustExec("TRUNCATE TABLE mysql.user") + tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("", "root", "Y", "N")`) + p = privileges.MySQLPrivilege{} + require.NoError(t, p.LoadUserTable(tk.Session())) + require.True(t, p.RequestVerification(activeRoles, "root", "", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "notnull", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "", "test", "", "", mysql.ShutdownPriv)) + + // Pattern match for DB. + tk.MustExec("TRUNCATE TABLE mysql.user") + tk.MustExec("TRUNCATE TABLE mysql.db") + tk.MustExec(`INSERT INTO mysql.db (user,host,db,select_priv) values ('genius', '%', 'te%', 'Y')`) + require.NoError(t, p.LoadDBTable(tk.Session())) + require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "test", "", "", mysql.SelectPriv)) +} + +func TestHostMatch(t *testing.T) { + store := createStoreAndPrepareDB(t) + + activeRoles := make([]*auth.RoleIdentity, 0) + + tk := testkit.NewTestKit(t, store) + // Host name can be IPv4 address + netmask. + tk.MustExec("USE MYSQL;") + tk.MustExec("TRUNCATE TABLE mysql.user") + tk.MustExec(`INSERT INTO mysql.user (HOST, USER, authentication_string, Select_priv, Shutdown_priv) VALUES ("172.0.0.0/255.0.0.0", "root", "", "Y", "Y")`) + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadUserTable(tk.Session())) + require.True(t, p.RequestVerification(activeRoles, "root", "172.0.0.1", "test", "", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "root", "172.1.1.1", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "198.0.0.1", "test", "", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "root", "198.0.0.1", "test", "", "", mysql.PrivilegeType(0))) + require.True(t, p.RequestVerification(activeRoles, "root", "172.0.0.1", "test", "", "", mysql.ShutdownPriv)) + tk.MustExec(`TRUNCATE TABLE mysql.user`) + + // Invalid host name, the user can be created, but cannot login. + cases := []string{ + "127.0.0.0/24", + "127.0.0.1/255.0.0.0", + "127.0.0.0/255.0.0", + "127.0.0.0/255.0.0.0.0", + "127%/255.0.0.0", + "127.0.0.0/%", + "127.0.0.%/%", + "127%/%", + } + for _, IPMask := range cases { + sql := fmt.Sprintf(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("%s", "root", "Y", "Y")`, IPMask) + tk.MustExec(sql) + p = privileges.MySQLPrivilege{} + require.NoError(t, p.LoadUserTable(tk.Session())) + require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv), fmt.Sprintf("test case: %s", IPMask)) + require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.0", "test", "", "", mysql.SelectPriv), fmt.Sprintf("test case: %s", IPMask)) + require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.ShutdownPriv), fmt.Sprintf("test case: %s", IPMask)) + } + + // Netmask notation cannot be used for IPv6 addresses. + tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("2001:db8::/ffff:ffff::", "root", "Y", "Y")`) + p = privileges.MySQLPrivilege{} + require.NoError(t, p.LoadUserTable(tk.Session())) + require.False(t, p.RequestVerification(activeRoles, "root", "2001:db8::1234", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "2001:db8::", "test", "", "", mysql.SelectPriv)) + require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.ShutdownPriv)) +} + +func TestCaseInsensitive(t *testing.T) { + store := createStoreAndPrepareDB(t) + + activeRoles := make([]*auth.RoleIdentity, 0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE TCTrain;") + tk.MustExec("CREATE TABLE TCTrain.TCTrainOrder (id int);") + tk.MustExec("TRUNCATE TABLE mysql.user") + tk.MustExec(`INSERT INTO mysql.db VALUES ("127.0.0.1", "TCTrain", "genius", "Y", "Y", "Y", "Y", "Y", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N")`) + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadDBTable(tk.Session())) + // DB and Table names are case-insensitive in MySQL. + require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "TCTrain", "TCTrainOrder", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "TCTRAIN", "TCTRAINORDER", "", mysql.SelectPriv)) + require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "tctrain", "tctrainorder", "", mysql.SelectPriv)) +} + +func TestLoadRoleGraph(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use mysql;") + tk.MustExec("truncate table user;") + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadDBTable(tk.Session())) + require.Len(t, p.User, 0) + + tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_1", "%", "user2")`) + tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_2", "%", "root")`) + tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_3", "%", "user1")`) + tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_4", "%", "root")`) + + p = privileges.MySQLPrivilege{} + require.NoError(t, p.LoadRoleGraph(tk.Session())) + graph := p.RoleGraph + require.True(t, graph["root@%"].Find("r_2", "%")) + require.True(t, graph["root@%"].Find("r_4", "%")) + require.True(t, graph["user2@%"].Find("r_1", "%")) + require.True(t, graph["user1@%"].Find("r_3", "%")) + _, ok := graph["illedal"] + require.False(t, ok) + require.False(t, graph["root@%"].Find("r_1", "%")) +} + +func TestRoleGraphBFS(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE ROLE r_1, r_2, r_3, r_4, r_5, r_6;`) + tk.MustExec(`GRANT r_2 TO r_1;`) + tk.MustExec(`GRANT r_3 TO r_2;`) + tk.MustExec(`GRANT r_4 TO r_3;`) + tk.MustExec(`GRANT r_1 TO r_4;`) + tk.MustExec(`GRANT r_5 TO r_3, r_6;`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadRoleGraph(tk.Session())) + + activeRoles := make([]*auth.RoleIdentity, 0) + ret := p.FindAllRole(activeRoles) + require.Len(t, ret, 0) + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_1", Hostname: "%"}) + ret = p.FindAllRole(activeRoles) + require.Len(t, ret, 5) + + activeRoles = make([]*auth.RoleIdentity, 0) + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_6", Hostname: "%"}) + ret = p.FindAllRole(activeRoles) + require.Len(t, ret, 2) + + activeRoles = make([]*auth.RoleIdentity, 0) + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_3", Hostname: "%"}) + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_6", Hostname: "%"}) + ret = p.FindAllRole(activeRoles) + require.Len(t, ret, 6) +} + +func TestFindAllUserEffectiveRoles(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER u1`) + tk.MustExec(`CREATE ROLE r_1, r_2, r_3, r_4;`) + tk.MustExec(`GRANT r_3 to r_1`) + tk.MustExec(`GRANT r_4 to r_2`) + tk.MustExec(`GRANT r_1 to u1`) + tk.MustExec(`GRANT r_2 to u1`) + + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadAll(tk.Session())) + ret := p.FindAllUserEffectiveRoles("u1", "%", []*auth.RoleIdentity{ + {Username: "r_1", Hostname: "%"}, + {Username: "r_2", Hostname: "%"}, + }) + require.Equal(t, 4, len(ret)) + require.Equal(t, "r_1", ret[0].Username) + require.Equal(t, "r_2", ret[1].Username) + require.Equal(t, "r_3", ret[2].Username) + require.Equal(t, "r_4", ret[3].Username) + + tk.MustExec(`REVOKE r_2 from u1`) + require.NoError(t, p.LoadAll(tk.Session())) + ret = p.FindAllUserEffectiveRoles("u1", "%", []*auth.RoleIdentity{ + {Username: "r_1", Hostname: "%"}, + {Username: "r_2", Hostname: "%"}, + }) + require.Equal(t, 2, len(ret)) + require.Equal(t, "r_1", ret[0].Username) + require.Equal(t, "r_3", ret[1].Username) +} + +func TestAbnormalMySQLTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + + // Simulate the case mysql.user is synchronized from MySQL. + tk.MustExec("DROP TABLE mysql.user;") + tk.MustExec("USE mysql;") + tk.MustExec(`CREATE TABLE user ( + Host char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + User char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + Password char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', + Select_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Insert_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Update_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Delete_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Drop_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Reload_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Shutdown_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Process_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + File_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Config_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Grant_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + References_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Index_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Alter_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Show_db_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Super_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_tmp_table_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Lock_tables_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Execute_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Repl_slave_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Repl_client_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Show_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Alter_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_user_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Event_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Trigger_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_tablespace_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Drop_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Account_locked ENUM('N','Y') NOT NULL DEFAULT 'N', + ssl_type enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '', + ssl_cipher blob NOT NULL, + x509_issuer blob NOT NULL, + x509_subject blob NOT NULL, + max_questions int(11) unsigned NOT NULL DEFAULT '0', + max_updates int(11) unsigned NOT NULL DEFAULT '0', + max_connections int(11) unsigned NOT NULL DEFAULT '0', + max_user_connections int(11) unsigned NOT NULL DEFAULT '0', + plugin char(64) COLLATE utf8_bin DEFAULT 'mysql_native_password', + authentication_string text COLLATE utf8_bin, + token_issuer varchar(255), + user_attributes json, + password_expired ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + password_last_changed TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + password_lifetime SMALLINT UNSIGNED, + PRIMARY KEY (Host,User) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges';`) + tk.MustExec(`INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'mysql_native_password','', '', 'null', 'N', current_timestamp(), null); +`) + var p privileges.MySQLPrivilege + require.NoError(t, p.LoadUserTable(tk.Session())) + activeRoles := make([]*auth.RoleIdentity, 0) + // MySQL mysql.user table schema is not identical to TiDB, check it doesn't break privilege. + require.True(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) + + // Absent of those tables doesn't cause error. + tk.MustExec("DROP TABLE mysql.db;") + tk.MustExec("DROP TABLE mysql.tables_priv;") + tk.MustExec("DROP TABLE mysql.columns_priv;") + require.NoError(t, p.LoadAll(tk.Session())) +} + +func TestSortUserTable(t *testing.T) { + var p privileges.MySQLPrivilege + p.User = []privileges.UserRecord{ + privileges.NewUserRecord(`%`, "root"), + privileges.NewUserRecord(`%`, "jeffrey"), + privileges.NewUserRecord("localhost", "root"), + privileges.NewUserRecord("localhost", ""), + } + p.SortUserTable() + result := []privileges.UserRecord{ + privileges.NewUserRecord("localhost", "root"), + privileges.NewUserRecord("localhost", ""), + privileges.NewUserRecord(`%`, "jeffrey"), + privileges.NewUserRecord(`%`, "root"), + } + checkUserRecord(t, p.User, result) + + p.User = []privileges.UserRecord{ + privileges.NewUserRecord(`%`, "jeffrey"), + privileges.NewUserRecord("h1.example.net", ""), + } + p.SortUserTable() + result = []privileges.UserRecord{ + privileges.NewUserRecord("h1.example.net", ""), + privileges.NewUserRecord(`%`, "jeffrey"), + } + checkUserRecord(t, p.User, result) + + p.User = []privileges.UserRecord{ + privileges.NewUserRecord(`192.168.%`, "xxx"), + privileges.NewUserRecord(`192.168.199.%`, "xxx"), + } + p.SortUserTable() + result = []privileges.UserRecord{ + privileges.NewUserRecord(`192.168.199.%`, "xxx"), + privileges.NewUserRecord(`192.168.%`, "xxx"), + } + checkUserRecord(t, p.User, result) +} + +func TestGlobalPrivValueRequireStr(t *testing.T) { + var ( + none = privileges.GlobalPrivValue{SSLType: privileges.SslTypeNone} + tls = privileges.GlobalPrivValue{SSLType: privileges.SslTypeAny} + x509 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeX509} + spec = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, SSLCipher: "c1", X509Subject: "s1", X509Issuer: "i1"} + spec2 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, X509Subject: "s1", X509Issuer: "i1"} + spec3 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, X509Issuer: "i1"} + spec4 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified} + ) + require.Equal(t, "NONE", none.RequireStr()) + require.Equal(t, "SSL", tls.RequireStr()) + require.Equal(t, "X509", x509.RequireStr()) + require.Equal(t, "CIPHER 'c1' ISSUER 'i1' SUBJECT 's1'", spec.RequireStr()) + require.Equal(t, "ISSUER 'i1' SUBJECT 's1'", spec2.RequireStr()) + require.Equal(t, "ISSUER 'i1'", spec3.RequireStr()) + require.Equal(t, "NONE", spec4.RequireStr()) +} + +func checkUserRecord(t *testing.T, x, y []privileges.UserRecord) { + require.Equal(t, len(x), len(y)) + for i := 0; i < len(x); i++ { + require.Equal(t, x[i].User, y[i].User) + require.Equal(t, x[i].Host, y[i].Host) + } +} + +func TestDBIsVisible(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database visdb") + p := privileges.MySQLPrivilege{} + require.NoError(t, p.LoadAll(tk.Session())) + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_role_priv, Super_priv) VALUES ("%", "testvisdb", "Y", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible := p.DBIsVisible("testvisdb", "%", "visdb") + require.False(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Select_priv) VALUES ("%", "testvisdb2", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb2", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_priv) VALUES ("%", "testvisdb3", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb3", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Insert_priv) VALUES ("%", "testvisdb4", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb4", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Update_priv) VALUES ("%", "testvisdb5", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb5", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_view_priv) VALUES ("%", "testvisdb6", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb6", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Trigger_priv) VALUES ("%", "testvisdb7", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb7", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, References_priv) VALUES ("%", "testvisdb8", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb8", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") + + tk.MustExec(`INSERT INTO mysql.user (Host, User, Execute_priv) VALUES ("%", "testvisdb9", "Y")`) + require.NoError(t, p.LoadUserTable(tk.Session())) + isVisible = p.DBIsVisible("testvisdb9", "%", "visdb") + require.True(t, isVisible) + tk.MustExec("TRUNCATE TABLE mysql.user") +} diff --git a/pkg/privilege/privileges/errors.go b/pkg/privilege/privileges/errors.go new file mode 100644 index 0000000000000..2caaade103fe4 --- /dev/null +++ b/pkg/privilege/privileges/errors.go @@ -0,0 +1,31 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package privileges + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// error definitions. +var ( + errInvalidPrivilegeType = dbterror.ClassPrivilege.NewStd(mysql.ErrInvalidPrivilegeType) + ErrNonexistingGrant = dbterror.ClassPrivilege.NewStd(mysql.ErrNonexistingGrant) + errLoadPrivilege = dbterror.ClassPrivilege.NewStd(mysql.ErrLoadPrivilege) + ErrAccessDenied = dbterror.ClassPrivilege.NewStd(mysql.ErrAccessDenied) + errAccountHasBeenLocked = dbterror.ClassPrivilege.NewStd(mysql.ErrAccountHasBeenLocked) + ErUserAccessDeniedForUserAccountBlockedByPasswordLock = dbterror.ClassPrivilege.NewStd(mysql.ErUserAccessDeniedForUserAccountBlockedByPasswordLock) + ErrMustChangePasswordLogin = dbterror.ClassPrivilege.NewStd(mysql.ErrMustChangePasswordLogin) +) diff --git a/pkg/privilege/privileges/ldap/BUILD.bazel b/pkg/privilege/privileges/ldap/BUILD.bazel new file mode 100644 index 0000000000000..2a8af88053949 --- /dev/null +++ b/pkg/privilege/privileges/ldap/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ldap", + srcs = [ + "const.go", + "ldap_common.go", + "sasl.go", + "simple.go", + ], + importpath = "github.com/pingcap/tidb/pkg/privilege/privileges/ldap", + visibility = ["//visibility:public"], + deps = [ + "//pkg/privilege/conn", + "@com_github_go_ldap_ldap_v3//:ldap", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "ldap_test", + timeout = "short", + srcs = ["ldap_common_test.go"], + embed = [":ldap"], + embedsrcs = [ + "test/ca.crt", + "test/ldap.crt", + "test/ldap.key", + ], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/privilege/privileges/ldap/const.go b/pkg/privilege/privileges/ldap/const.go similarity index 100% rename from privilege/privileges/ldap/const.go rename to pkg/privilege/privileges/ldap/const.go diff --git a/privilege/privileges/ldap/ldap_common.go b/pkg/privilege/privileges/ldap/ldap_common.go similarity index 100% rename from privilege/privileges/ldap/ldap_common.go rename to pkg/privilege/privileges/ldap/ldap_common.go diff --git a/privilege/privileges/ldap/ldap_common_test.go b/pkg/privilege/privileges/ldap/ldap_common_test.go similarity index 100% rename from privilege/privileges/ldap/ldap_common_test.go rename to pkg/privilege/privileges/ldap/ldap_common_test.go diff --git a/privilege/privileges/ldap/sasl.go b/pkg/privilege/privileges/ldap/sasl.go similarity index 98% rename from privilege/privileges/ldap/sasl.go rename to pkg/privilege/privileges/ldap/sasl.go index ea3850f58b57c..0456ef6849eac 100644 --- a/privilege/privileges/ldap/sasl.go +++ b/pkg/privilege/privileges/ldap/sasl.go @@ -19,7 +19,7 @@ import ( "github.com/go-ldap/ldap/v3" "github.com/pingcap/errors" - "github.com/pingcap/tidb/privilege/conn" + "github.com/pingcap/tidb/pkg/privilege/conn" ) type ldapSASLAuthImpl struct { diff --git a/privilege/privileges/ldap/simple.go b/pkg/privilege/privileges/ldap/simple.go similarity index 100% rename from privilege/privileges/ldap/simple.go rename to pkg/privilege/privileges/ldap/simple.go diff --git a/privilege/privileges/ldap/test/ca.crt b/pkg/privilege/privileges/ldap/test/ca.crt similarity index 100% rename from privilege/privileges/ldap/test/ca.crt rename to pkg/privilege/privileges/ldap/test/ca.crt diff --git a/privilege/privileges/ldap/test/ldap.crt b/pkg/privilege/privileges/ldap/test/ldap.crt similarity index 100% rename from privilege/privileges/ldap/test/ldap.crt rename to pkg/privilege/privileges/ldap/test/ldap.crt diff --git a/privilege/privileges/ldap/test/ldap.key b/pkg/privilege/privileges/ldap/test/ldap.key similarity index 100% rename from privilege/privileges/ldap/test/ldap.key rename to pkg/privilege/privileges/ldap/test/ldap.key diff --git a/pkg/privilege/privileges/main_test.go b/pkg/privilege/privileges/main_test.go new file mode 100644 index 0000000000000..9586bfbcca9f5 --- /dev/null +++ b/pkg/privilege/privileges/main_test.go @@ -0,0 +1,41 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package privileges_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/privilege/privileges.(*JWKSImpl).LoadJWKS4AuthToken.func1"), + } + testsetup.SetupForCommonTest() + + session.SetSchemaLease(0) + session.DisableStats4Test() + + goleak.VerifyTestMain(m, opts...) +} diff --git a/privilege/privileges/privileges.go b/pkg/privilege/privileges/privileges.go similarity index 98% rename from privilege/privileges/privileges.go rename to pkg/privilege/privileges/privileges.go index 4c75f847d0cae..d394bacdb69ae 100644 --- a/privilege/privileges/privileges.go +++ b/pkg/privilege/privileges/privileges.go @@ -27,22 +27,22 @@ import ( jwtRepo "github.com/lestrrat-go/jwx/v2/jwt" "github.com/lestrrat-go/jwx/v2/jwt/openid" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/conn" - "github.com/pingcap/tidb/privilege/privileges/ldap" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/conn" + "github.com/pingcap/tidb/pkg/privilege/privileges/ldap" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/sem" "go.uber.org/zap" ) diff --git a/pkg/privilege/privileges/privileges_test.go b/pkg/privilege/privileges/privileges_test.go new file mode 100644 index 0000000000000..90f406444111b --- /dev/null +++ b/pkg/privilege/privileges/privileges_test.go @@ -0,0 +1,2117 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package privileges_test + +import ( + "bytes" + "context" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/json" + "fmt" + "net/url" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" +) + +func TestCheckDBPrivilege(t *testing.T) { + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'testcheck'@'localhost';`) + rootTk.MustExec(`CREATE USER 'testcheck_tmp'@'localhost';`) + + tk := testkit.NewTestKit(t, store) + activeRoles := make([]*auth.RoleIdentity, 0) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "testcheck", Hostname: "localhost"}, nil, nil, nil)) + pc := privilege.GetPrivilegeManager(tk.Session()) + require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) + + rootTk.MustExec(`GRANT SELECT ON *.* TO 'testcheck'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) + require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) + + rootTk.MustExec(`GRANT Update ON test.* TO 'testcheck'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) + + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "testcheck", Hostname: "localhost"}) + rootTk.MustExec(`GRANT 'testcheck'@'localhost' TO 'testcheck_tmp'@'localhost';`) + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "testcheck_tmp", Hostname: "localhost"}, nil, nil, nil)) + pc = privilege.GetPrivilegeManager(tk2.Session()) + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) +} + +func TestCheckTablePrivilege(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'test1'@'localhost';`) + rootTk.MustExec(`CREATE USER 'test1_tmp'@'localhost';`) + + tk := testkit.NewTestKit(t, store) + activeRoles := make([]*auth.RoleIdentity, 0) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test1", Hostname: "localhost"}, nil, nil, nil)) + pc := privilege.GetPrivilegeManager(tk.Session()) + require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) + + rootTk.MustExec(`GRANT SELECT ON *.* TO 'test1'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) + require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) + + rootTk.MustExec(`GRANT Update ON test.* TO 'test1'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) + require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) + + activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "test1", Hostname: "localhost"}) + tk2 := testkit.NewTestKit(t, store) + rootTk.MustExec(`GRANT 'test1'@'localhost' TO 'test1_tmp'@'localhost';`) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "test1_tmp", Hostname: "localhost"}, nil, nil, nil)) + pc2 := privilege.GetPrivilegeManager(tk2.Session()) + require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) + require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) + require.False(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) + + rootTk.MustExec(`GRANT Index ON test.test TO 'test1'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) + require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) +} + +func TestCheckViewPrivilege(t *testing.T) { + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec("use test") + rootTk.MustExec(`CREATE USER 'vuser'@'localhost';`) + rootTk.MustExec(`CREATE VIEW v AS SELECT * FROM test;`) + + tk := testkit.NewTestKit(t, store) + activeRoles := make([]*auth.RoleIdentity, 0) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "vuser", Hostname: "localhost"}, nil, nil, nil)) + pc := privilege.GetPrivilegeManager(tk.Session()) + require.False(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) + + rootTk.MustExec(`GRANT SELECT ON test.v TO 'vuser'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) + require.False(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.ShowViewPriv)) + + rootTk.MustExec(`GRANT SHOW VIEW ON test.v TO 'vuser'@'localhost';`) + require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) + require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.ShowViewPriv)) +} + +func TestCheckPrivilegeWithRoles(t *testing.T) { + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'test_role'@'localhost';`) + rootTk.MustExec(`CREATE ROLE r_1, r_2, r_3;`) + rootTk.MustExec(`GRANT r_1, r_2, r_3 TO 'test_role'@'localhost';`) + + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_role", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`SET ROLE r_1, r_2;`) + rootTk.MustExec(`SET DEFAULT ROLE r_1 TO 'test_role'@'localhost';`) + // test bogus role for current user. + err := tk.ExecToErr(`SET DEFAULT ROLE roledoesnotexist TO 'test_role'@'localhost';`) + require.True(t, terror.ErrorEqual(err, exeerrors.ErrRoleNotGranted)) + + rootTk.MustExec(`GRANT SELECT ON test.* TO r_1;`) + pc := privilege.GetPrivilegeManager(tk.Session()) + activeRoles := tk.Session().GetSessionVars().ActiveRoles + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) + require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) + rootTk.MustExec(`GRANT UPDATE ON test.* TO r_2;`) + require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) + + tk.MustExec(`SET ROLE NONE;`) + require.Equal(t, 0, len(tk.Session().GetSessionVars().ActiveRoles)) + tk.MustExec(`SET ROLE DEFAULT;`) + require.Equal(t, 1, len(tk.Session().GetSessionVars().ActiveRoles)) + tk.MustExec(`SET ROLE ALL;`) + require.Equal(t, 3, len(tk.Session().GetSessionVars().ActiveRoles)) + tk.MustExec(`SET ROLE ALL EXCEPT r_1, r_2;`) + require.Equal(t, 1, len(tk.Session().GetSessionVars().ActiveRoles)) +} + +// TestErrorMessage checks that the identity in error messages matches the mysql.user table one. +// MySQL is inconsistent in its error messages, as some match the loginHost and others the +// identity from mysql.user. In TiDB we now use the identity from mysql.user in error messages +// for consistency. +func TestErrorMessage(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER wildcard`) + rootTk.MustExec(`CREATE USER specifichost@192.168.1.1`) + rootTk.MustExec(`GRANT SELECT on test.* TO wildcard`) + rootTk.MustExec(`GRANT SELECT on test.* TO specifichost@192.168.1.1`) + + wildTk := testkit.NewTestKit(t, store) + + // The session.Auth() func will populate the AuthUsername and AuthHostname fields. + // We don't have to explicitly specify them. + require.NoError(t, wildTk.Session().Auth(&auth.UserIdentity{Username: "wildcard", Hostname: "192.168.1.1"}, nil, nil, nil)) + require.EqualError(t, wildTk.ExecToErr("use mysql;"), "[executor:1044]Access denied for user 'wildcard'@'%' to database 'mysql'") + + specificTk := testkit.NewTestKit(t, store) + require.NoError(t, specificTk.Session().Auth(&auth.UserIdentity{Username: "specifichost", Hostname: "192.168.1.1"}, nil, nil, nil)) + require.EqualError(t, specificTk.ExecToErr("use mysql;"), "[executor:1044]Access denied for user 'specifichost'@'192.168.1.1' to database 'mysql'") +} + +func TestDropTablePrivileges(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + ctx, _ := tk.Session().(sessionctx.Context) + tk.MustExec(`CREATE TABLE todrop(c int);`) + // ctx.GetSessionVars().User = "root@localhost" + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`CREATE USER 'drop'@'localhost';`) + tk.MustExec(`GRANT Select ON test.todrop TO 'drop'@'localhost';`) + + // ctx.GetSessionVars().User = "drop@localhost" + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "drop", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`SELECT * FROM todrop;`) + require.Error(t, tk.ExecToErr("DROP TABLE todrop;")) + + tk = testkit.NewTestKit(t, store) + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "root", Hostname: "localhost"} + tk.MustExec(`GRANT Drop ON test.todrop TO 'drop'@'localhost';`) + + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + ctx.GetSessionVars().User = &auth.UserIdentity{Username: "drop", Hostname: "localhost"} + tk.MustExec(`DROP TABLE todrop;`) +} + +func TestAlterUserStmt(t *testing.T) { + store := createStoreAndPrepareDB(t) + tk := testkit.NewTestKit(t, store) + + // high privileged user setting password for other user (passes) + tk.MustExec("CREATE USER superuser2, nobodyuser2, nobodyuser3, nobodyuser4, nobodyuser5, semuser1, semuser2, semuser3, semuser4") + tk.MustExec("GRANT ALL ON *.* TO superuser2") + tk.MustExec("GRANT CREATE USER ON *.* TO nobodyuser2") + tk.MustExec("GRANT SYSTEM_USER ON *.* TO nobodyuser4") + tk.MustExec("GRANT UPDATE ON mysql.user TO nobodyuser5, semuser1") + tk.MustExec("GRANT RESTRICTED_TABLES_ADMIN ON *.* TO semuser1") + tk.MustExec("GRANT RESTRICTED_USER_ADMIN ON *.* TO semuser1, semuser2, semuser3") + tk.MustExec("GRANT SYSTEM_USER ON *.* to semuser3") // user is both restricted + has SYSTEM_USER (or super) + + sem.Enable() + defer sem.Disable() + + // When SEM is enabled, even though we have UPDATE privilege on mysql.user, it explicitly + // denies writeable privileges to system schemas unless RESTRICTED_TABLES_ADMIN is granted. + // so the previous method of granting to the table instead of CREATE USER will fail now. + // This is intentional because SEM plugs directly into the privilege manager to DENY + // any request for UpdatePriv on mysql.user even if the privilege exists in the internal mysql.user table. + + // UpdatePriv on mysql.user + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "nobodyuser5", Hostname: "localhost"}, nil, nil, nil)) + err := tk.ExecToErr("ALTER USER 'nobodyuser2' IDENTIFIED BY 'newpassword'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") + + // actual CreateUserPriv + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "nobodyuser2", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("ALTER USER 'nobodyuser2' IDENTIFIED BY ''") + tk.MustExec("ALTER USER 'nobodyuser3' IDENTIFIED BY ''") + + // UpdatePriv on mysql.user but also has RESTRICTED_TABLES_ADMIN + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "semuser1", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("ALTER USER 'nobodyuser2' IDENTIFIED BY ''") + tk.MustExec("ALTER USER 'nobodyuser3' IDENTIFIED BY ''") + + // As it has (RESTRICTED_TABLES_ADMIN + UpdatePriv on mysql.user) + RESTRICTED_USER_ADMIN it can modify other restricted_user_admins like semuser2 + // and it can modify semuser3 because RESTRICTED_USER_ADMIN does not also need SYSTEM_USER + tk.MustExec("ALTER USER 'semuser1' IDENTIFIED BY ''") + tk.MustExec("ALTER USER 'semuser2' IDENTIFIED BY ''") + tk.MustExec("ALTER USER 'semuser3' IDENTIFIED BY ''") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "superuser2", Hostname: "localhost"}, nil, nil, nil)) + err = tk.ExecToErr("ALTER USER 'semuser1' IDENTIFIED BY 'newpassword'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_USER_ADMIN privilege(s) for this operation") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "semuser4", Hostname: "localhost"}, nil, nil, nil)) + // has restricted_user_admin but not CREATE USER or (update on mysql.user + RESTRICTED_TABLES_ADMIN) + tk.MustExec("ALTER USER 'semuser4' IDENTIFIED BY ''") // can modify self + err = tk.ExecToErr("ALTER USER 'nobodyuser3' IDENTIFIED BY 'newpassword'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") + err = tk.ExecToErr("ALTER USER 'semuser1' IDENTIFIED BY 'newpassword'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") + err = tk.ExecToErr("ALTER USER 'semuser3' IDENTIFIED BY 'newpassword'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") +} + +func TestShowViewPriv(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`DROP VIEW IF EXISTS test.v`) + tk.MustExec(`CREATE VIEW test.v AS SELECT 1`) + tk.MustExec("CREATE USER vnobody, vshowview, vselect, vshowandselect") + tk.MustExec("GRANT SHOW VIEW ON test.v TO vshowview") + tk.MustExec("GRANT SELECT ON test.v TO vselect") + tk.MustExec("GRANT SHOW VIEW, SELECT ON test.v TO vshowandselect") + + tests := []struct { + userName string + showViewErr string + showTableErr string + explainErr string + explainRes string + descErr string + descRes string + tablesNum string + columnsNum string + }{ + {"vnobody", + "[planner:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", + "[planner:1142]SHOW VIEW command denied to user 'vnobody'@'%' for table 'v'", + "[executor:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", + "", + "[executor:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", + "", + "0", + "0", + }, + {"vshowview", + "[planner:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", + "", + "[executor:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", + "", + "[executor:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", + "", + "1", + "0", + }, + {"vselect", + "[planner:1142]SHOW VIEW command denied to user 'vselect'@'%' for table 'v'", + "[planner:1142]SHOW VIEW command denied to user 'vselect'@'%' for table 'v'", + "", + "1 bigint(1) NO ", + "", + "1 bigint(1) NO ", + "1", + "1", + }, + {"vshowandselect", + "", + "", + "", + "1 bigint(1) NO ", + "", + "1 bigint(1) NO ", + "1", + "1", + }, + } + + for _, test := range tests { + tk.Session().Auth(&auth.UserIdentity{Username: test.userName, Hostname: "localhost"}, nil, nil, nil) + err := tk.ExecToErr("SHOW CREATE VIEW test.v") + if test.showViewErr != "" { + require.EqualError(t, err, test.showViewErr, test) + } else { + require.NoError(t, err, test) + } + err = tk.ExecToErr("SHOW CREATE TABLE test.v") + if test.showTableErr != "" { + require.EqualError(t, err, test.showTableErr, test) + } else { + require.NoError(t, err, test) + } + if test.explainErr != "" { + err = tk.QueryToErr("explain test.v") + require.EqualError(t, err, test.explainErr, test) + } else { + tk.MustQuery("explain test.v").Check(testkit.Rows(test.explainRes)) + } + if test.descErr != "" { + err = tk.QueryToErr("explain test.v") + require.EqualError(t, err, test.descErr, test) + } else { + tk.MustQuery("desc test.v").Check(testkit.Rows(test.descRes)) + } + tk.MustQuery("select count(*) from information_schema.tables where table_schema='test' and table_name='v'").Check(testkit.Rows(test.tablesNum)) + tk.MustQuery("select count(*) from information_schema.columns where table_schema='test' and table_name='v'").Check(testkit.Rows(test.columnsNum)) + } +} + +func TestCheckCertBasedAuth(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER 'r1'@'localhost';`) + tk.MustExec(`CREATE USER 'r2'@'localhost' require none;`) + tk.MustExec(`CREATE USER 'r3'@'localhost' require ssl;`) + tk.MustExec(`CREATE USER 'r4'@'localhost' require x509;`) + tk.MustExec(`CREATE USER 'r5'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' + subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1' cipher 'TLS_AES_128_GCM_SHA256'`) + tk.MustExec(`CREATE USER 'r6'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' + subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) + tk.MustExec(`CREATE USER 'r7_issuer_only'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin'`) + tk.MustExec(`CREATE USER 'r8_subject_only'@'localhost' require subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) + tk.MustExec(`CREATE USER 'r9_subject_disorder'@'localhost' require subject '/ST=Beijing/C=ZH/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) + tk.MustExec(`CREATE USER 'r10_issuer_disorder'@'localhost' require issuer '/ST=California/C=US/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin'`) + tk.MustExec(`CREATE USER 'r11_cipher_only'@'localhost' require cipher 'TLS_AES_256_GCM_SHA384'`) + tk.MustExec(`CREATE USER 'r12_old_tidb_user'@'localhost'`) + tk.MustExec("DELETE FROM mysql.global_priv WHERE `user` = 'r12_old_tidb_user' and `host` = 'localhost'") + tk.MustExec(`CREATE USER 'r13_broken_user'@'localhost'require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' + subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) + tk.MustExec("UPDATE mysql.global_priv set priv = 'abc' where `user` = 'r13_broken_user' and `host` = 'localhost'") + tk.MustExec(`CREATE USER 'r14_san_only_pass'@'localhost' require san 'URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1'`) + tk.MustExec(`CREATE USER 'r15_san_only_fail'@'localhost' require san 'URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me2'`) + + defer func() { + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("drop user 'r1'@'localhost'") + tk.MustExec("drop user 'r2'@'localhost'") + tk.MustExec("drop user 'r3'@'localhost'") + tk.MustExec("drop user 'r4'@'localhost'") + tk.MustExec("drop user 'r5'@'localhost'") + tk.MustExec("drop user 'r6'@'localhost'") + tk.MustExec("drop user 'r7_issuer_only'@'localhost'") + tk.MustExec("drop user 'r8_subject_only'@'localhost'") + tk.MustExec("drop user 'r9_subject_disorder'@'localhost'") + tk.MustExec("drop user 'r10_issuer_disorder'@'localhost'") + tk.MustExec("drop user 'r11_cipher_only'@'localhost'") + tk.MustExec("drop user 'r12_old_tidb_user'@'localhost'") + tk.MustExec("drop user 'r13_broken_user'@'localhost'") + tk.MustExec("drop user 'r14_san_only_pass'@'localhost'") + tk.MustExec("drop user 'r15_san_only_fail'@'localhost'") + }() + + // test without ssl or ca + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + + // test use ssl without ca + tk.Session().GetSessionVars().TLSConnectionState = &tls.ConnectionState{VerifiedChains: nil} + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + + // test use ssl with signed but info wrong ca. + tk.Session().GetSessionVars().TLSConnectionState = &tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{{}}}} + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + + // test a all pass case + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "US"), + util.MockPkixAttribute(util.Province, "California"), + util.MockPkixAttribute(util.Locality, "San Francisco"), + util.MockPkixAttribute(util.Organization, "PingCAP"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "TiDB admin"), + }, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "ZH"), + util.MockPkixAttribute(util.Province, "Beijing"), + util.MockPkixAttribute(util.Locality, "Haidian"), + util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "tester1"), + }, + }, + tls.TLS_AES_128_GCM_SHA256, func(cert *x509.Certificate) { + var url url.URL + err := url.UnmarshalBinary([]byte("spiffe://mesh.pingcap.com/ns/timesh/sa/me1")) + require.NoError(t, err) + cert.URIs = append(cert.URIs, &url) + }) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r14_san_only_pass", Hostname: "localhost"}, nil, nil, nil)) + + // test require but give nothing + tk.Session().GetSessionVars().TLSConnectionState = nil + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + + // test mismatch cipher + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "US"), + util.MockPkixAttribute(util.Province, "California"), + util.MockPkixAttribute(util.Locality, "San Francisco"), + util.MockPkixAttribute(util.Organization, "PingCAP"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "TiDB admin"), + }, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "ZH"), + util.MockPkixAttribute(util.Province, "Beijing"), + util.MockPkixAttribute(util.Locality, "Haidian"), + util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "tester1"), + }, + }, + tls.TLS_AES_256_GCM_SHA384) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r6", Hostname: "localhost"}, nil, nil, nil)) // not require cipher + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r11_cipher_only", Hostname: "localhost"}, nil, nil, nil)) + + // test only subject or only issuer + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "US"), + util.MockPkixAttribute(util.Province, "California"), + util.MockPkixAttribute(util.Locality, "San Francisco"), + util.MockPkixAttribute(util.Organization, "PingCAP"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "TiDB admin"), + }, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "AZ"), + util.MockPkixAttribute(util.Province, "Beijing"), + util.MockPkixAttribute(util.Locality, "Shijingshang"), + util.MockPkixAttribute(util.Organization, "CAPPing.Inc"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "tester2"), + }, + }, + tls.TLS_AES_128_GCM_SHA256) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r7_issuer_only", Hostname: "localhost"}, nil, nil, nil)) + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "AU"), + util.MockPkixAttribute(util.Province, "California"), + util.MockPkixAttribute(util.Locality, "San Francisco"), + util.MockPkixAttribute(util.Organization, "PingCAP"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "TiDB admin2"), + }, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "ZH"), + util.MockPkixAttribute(util.Province, "Beijing"), + util.MockPkixAttribute(util.Locality, "Haidian"), + util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "tester1"), + }, + }, + tls.TLS_AES_128_GCM_SHA256) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r8_subject_only", Hostname: "localhost"}, nil, nil, nil)) + + // test disorder issuer or subject + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{}, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "ZH"), + util.MockPkixAttribute(util.Province, "Beijing"), + util.MockPkixAttribute(util.Locality, "Haidian"), + util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "tester1"), + }, + }, + tls.TLS_AES_128_GCM_SHA256) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r9_subject_disorder", Hostname: "localhost"}, nil, nil, nil)) + tk.Session().GetSessionVars().TLSConnectionState = connectionState( + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + util.MockPkixAttribute(util.Country, "US"), + util.MockPkixAttribute(util.Province, "California"), + util.MockPkixAttribute(util.Locality, "San Francisco"), + util.MockPkixAttribute(util.Organization, "PingCAP"), + util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), + util.MockPkixAttribute(util.CommonName, "TiDB admin"), + }, + }, + pkix.Name{ + Names: []pkix.AttributeTypeAndValue{}, + }, + tls.TLS_AES_128_GCM_SHA256) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r10_issuer_disorder", Hostname: "localhost"}, nil, nil, nil)) + + // test mismatch san + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r15_san_only_fail", Hostname: "localhost"}, nil, nil, nil)) + + // test old data and broken data + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r12_old_tidb_user", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r13_broken_user", Hostname: "localhost"}, nil, nil, nil)) +} + +func connectionState(issuer, subject pkix.Name, cipher uint16, opt ...func(c *x509.Certificate)) *tls.ConnectionState { + cert := &x509.Certificate{Issuer: issuer, Subject: subject} + for _, o := range opt { + o(cert) + } + return &tls.ConnectionState{ + VerifiedChains: [][]*x509.Certificate{{cert}}, + CipherSuite: cipher, + } +} + +func TestCheckAuthenticate(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER 'u1'@'localhost';`) + tk.MustExec(`CREATE USER 'u2'@'localhost' identified by 'abc';`) + tk.MustExec(`CREATE USER 'u3@example.com'@'localhost';`) + tk.MustExec(`CREATE USER u4@localhost;`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) + salt := []byte{85, 92, 45, 22, 58, 79, 107, 6, 122, 125, 58, 80, 12, 90, 103, 32, 90, 10, 74, 82} + authentication := []byte{24, 180, 183, 225, 166, 6, 81, 102, 70, 248, 199, 143, 91, 204, 169, 9, 161, 171, 203, 33} + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, authentication, salt, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u3@example.com", Hostname: "localhost"}, nil, nil, nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u4", Hostname: "localhost"}, nil, nil, nil)) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("drop user 'u1'@'localhost'") + tk1.MustExec("drop user 'u2'@'localhost'") + tk1.MustExec("drop user 'u3@example.com'@'localhost'") + tk1.MustExec("drop user u4@localhost") + + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u3@example.com", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u4", Hostname: "localhost"}, nil, nil, nil)) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("create role 'r1'@'localhost'") + tk2.MustExec("create role 'r2'@'localhost'") + tk2.MustExec("create role 'r3@example.com'@'localhost'") + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3@example.com", Hostname: "localhost"}, nil, nil, nil)) + + tk1.MustExec("drop user 'r1'@'localhost'") + tk1.MustExec("drop user 'r2'@'localhost'") + tk1.MustExec("drop user 'r3@example.com'@'localhost'") +} + +func TestUseDB(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + // high privileged user + tk.MustExec("CREATE USER 'usesuper'") + tk.MustExec("CREATE USER 'usenobody'") + tk.MustExec("GRANT ALL ON *.* TO 'usesuper'") + // without grant option + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) + require.Error(t, tk.ExecToErr("GRANT SELECT ON mysql.* TO 'usenobody'")) + // with grant option + tk = testkit.NewTestKit(t, store) + // high privileged user + tk.MustExec("GRANT ALL ON *.* TO 'usesuper' WITH GRANT OPTION") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("use mysql") + // low privileged user + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil, nil)) + err := tk.ExecToErr("use mysql") + require.Error(t, err) + + // try again after privilege granted + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("GRANT SELECT ON mysql.* TO 'usenobody'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("use mysql") + + // test `use db` for role. + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec(`CREATE DATABASE app_db`) + tk.MustExec(`CREATE ROLE 'app_developer'`) + tk.MustExec(`GRANT ALL ON app_db.* TO 'app_developer'`) + tk.MustExec(`CREATE USER 'dev'@'localhost'`) + tk.MustExec(`GRANT 'app_developer' TO 'dev'@'localhost'`) + tk.MustExec(`SET DEFAULT ROLE 'app_developer' TO 'dev'@'localhost'`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dev", Hostname: "localhost", AuthUsername: "dev", AuthHostname: "localhost"}, nil, nil, nil)) + tk.MustExec("use app_db") + err = tk.ExecToErr("use mysql") + require.Error(t, err) +} + +func TestConfigPrivilege(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`DROP USER IF EXISTS tcd1`) + tk.MustExec(`CREATE USER tcd1`) + tk.MustExec(`GRANT ALL ON *.* to tcd1`) + tk.MustExec(`DROP USER IF EXISTS tcd2`) + tk.MustExec(`CREATE USER tcd2`) + tk.MustExec(`GRANT ALL ON *.* to tcd2`) + tk.MustExec(`REVOKE CONFIG ON *.* FROM tcd2`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tcd1", Hostname: "localhost", AuthHostname: "tcd1", AuthUsername: "%"}, nil, nil, nil)) + tk.MustExec(`SHOW CONFIG`) + tk.MustExec(`SET CONFIG TIKV testkey="testval"`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tcd2", Hostname: "localhost", AuthHostname: "tcd2", AuthUsername: "%"}, nil, nil, nil)) + err := tk.ExecToErr(`SHOW CONFIG`) + require.Error(t, err) + require.Regexp(t, "you need \\(at least one of\\) the CONFIG privilege\\(s\\) for this operation$", err.Error()) + err = tk.ExecToErr(`SET CONFIG TIKV testkey="testval"`) + require.Error(t, err) + require.Regexp(t, "you need \\(at least one of\\) the CONFIG privilege\\(s\\) for this operation$", err.Error()) + tk.MustExec(`DROP USER tcd1, tcd2`) +} + +func TestShowCreateTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER tsct1, tsct2`) + tk.MustExec(`GRANT select ON mysql.* to tsct2`) + + // should fail + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tsct1", Hostname: "localhost", AuthUsername: "tsct1", AuthHostname: "%"}, nil, nil, nil)) + err := tk.ExecToErr(`SHOW CREATE TABLE mysql.user`) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + + // should pass + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tsct2", Hostname: "localhost", AuthUsername: "tsct2", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec(`SHOW CREATE TABLE mysql.user`) +} + +func TestAnalyzeTable(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + // high privileged user + tk.MustExec("CREATE USER 'asuper'") + tk.MustExec("CREATE USER 'anobody'") + tk.MustExec("GRANT ALL ON *.* TO 'asuper' WITH GRANT OPTION") + tk.MustExec("CREATE DATABASE atest") + tk.MustExec("use atest") + tk.MustExec("CREATE TABLE t1 (a int)") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("analyze table mysql.user") + // low privileged user + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) + err := tk.ExecToErr("analyze table t1") + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + require.EqualError(t, err, "[planner:1142]INSERT command denied to user 'anobody'@'%' for table 't1'") + + err = tk.ExecToErr("select * from t1") + require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'anobody'@'%' for table 't1'") + + // try again after SELECT privilege granted + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("GRANT SELECT ON atest.* TO 'anobody'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) + err = tk.ExecToErr("analyze table t1") + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + require.EqualError(t, err, "[planner:1142]INSERT command denied to user 'anobody'@'%' for table 't1'") + // Add INSERT privilege and it should work. + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("GRANT INSERT ON atest.* TO 'anobody'") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) + tk.MustExec("analyze table t1") +} + +func TestSystemSchema(t *testing.T) { + store := createStoreAndPrepareDB(t) + + // This test tests no privilege check for INFORMATION_SCHEMA database. + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER 'u1'@'localhost';`) + tk.MustExec(`GRANT SELECT ON *.* TO 'u1'@'localhost';`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`select * from information_schema.tables`) + tk.MustExec(`select * from information_schema.key_column_usage`) + err := tk.ExecToErr("create table information_schema.t(a int)") + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "denied to user")) + err = tk.ExecToErr("drop table information_schema.tables") + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "denied to user")) + err = tk.ExecToErr("update information_schema.tables set table_name = 'tst' where table_name = 'mysql'") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) + + // Test metric_schema. + tk.MustExec(`select * from metrics_schema.tidb_query_duration`) + err = tk.ExecToErr("drop table metrics_schema.tidb_query_duration") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + err = tk.ExecToErr("update metrics_schema.tidb_query_duration set instance = 'tst'") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) + err = tk.ExecToErr("delete from metrics_schema.tidb_query_duration") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + err = tk.ExecToErr("create table metric_schema.t(a int)") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + + tk.MustGetErrCode("create table metrics_schema.t (id int);", errno.ErrTableaccessDenied) + tk.MustGetErrCode("create table performance_schema.t (id int);", errno.ErrTableaccessDenied) +} + +func TestPerformanceSchema(t *testing.T) { + store := createStoreAndPrepareDB(t) + + // This test tests no privilege check for INFORMATION_SCHEMA database. + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER 'u1'@'localhost';`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) + err := tk.ExecToErr("select * from performance_schema.events_statements_summary_by_digest where schema_name = 'tst'") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`GRANT SELECT ON *.* TO 'u1'@'localhost';`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("select * from performance_schema.events_statements_summary_by_digest where schema_name = 'tst'") + tk.MustExec(`select * from performance_schema.events_statements_summary_by_digest`) + err = tk.ExecToErr("drop table performance_schema.events_statements_summary_by_digest") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + err = tk.ExecToErr("update performance_schema.events_statements_summary_by_digest set schema_name = 'tst'") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) + err = tk.ExecToErr("delete from performance_schema.events_statements_summary_by_digest") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + err = tk.ExecToErr("create table performance_schema.t(a int)") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) +} + +func TestMetricsSchema(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER nobody, msprocess, msselect") + tk.MustExec("GRANT Process ON *.* TO msprocess") + tk.MustExec("GRANT SELECT ON metrics_schema.* TO msselect") + + tests := []struct { + stmt string + user string + checkErr func(err error) + }{ + { + "SHOW CREATE DATABASE metrics_schema", + "nobody", + func(err error) { + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, exeerrors.ErrDBaccessDenied)) + }, + }, + { + "SHOW CREATE DATABASE metrics_schema", + "msprocess", + func(err error) { + require.NoError(t, err) + }, + }, + { + "SHOW CREATE DATABASE metrics_schema", + "msselect", + func(err error) { + require.NoError(t, err) + }, + }, + { + "SELECT * FROM metrics_schema.up", + "nobody", + func(err error) { + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + }, + }, + { + "SELECT * FROM metrics_schema.up", + "msprocess", + func(err error) { + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "pd unavailable")) + }, + }, + { + "SELECT * FROM metrics_schema.up", + "msselect", + func(err error) { + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "pd unavailable")) + }, + }, + { + "SELECT * FROM information_schema.metrics_summary", + "nobody", + func(err error) { + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrSpecificAccessDenied)) + }, + }, + { + "SELECT * FROM information_schema.metrics_summary", + "msprocess", + func(err error) { + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "pd unavailable")) + }, + }, + { + "SELECT * FROM information_schema.metrics_summary_by_label", + "nobody", + func(err error) { + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrSpecificAccessDenied)) + }, + }, + { + "SELECT * FROM information_schema.metrics_summary_by_label", + "msprocess", + func(err error) { + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "pd unavailable")) + }, + }, + } + + for _, test := range tests { + tk.Session().Auth(&auth.UserIdentity{ + Username: test.user, + Hostname: "localhost", + }, nil, nil, nil) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + rs, err := tk.Session().ExecuteInternal(ctx, test.stmt) + if err == nil { + _, err = session.GetRows4Test(context.Background(), tk.Session(), rs) + } + if rs != nil { + require.NoError(t, rs.Close()) + } + test.checkErr(err) + } +} + +func TestAdminCommand(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`CREATE USER 'test_admin'@'localhost';`) + tk.MustExec(`CREATE TABLE t(a int)`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_admin", Hostname: "localhost"}, nil, nil, nil)) + err := tk.ExecToErr("ADMIN SHOW DDL JOBS") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) + err = tk.ExecToErr("ADMIN CHECK TABLE t") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("ADMIN SHOW DDL JOBS") +} + +func TestLoadDataPrivilege(t *testing.T) { + // Create file. + path := "/tmp/load_data_priv.csv" + fp, err := os.Create(path) + require.NoError(t, err) + require.NotNil(t, fp) + defer func() { + require.NoError(t, fp.Close()) + require.NoError(t, os.Remove(path)) + }() + _, err = fp.WriteString("1\n") + require.NoError(t, err) + + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`CREATE USER 'test_load'@'localhost';`) + tk.MustExec(`CREATE TABLE t_load(a int)`) + + tk.MustExec(`GRANT SELECT on *.* to 'test_load'@'localhost'`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) + err = tk.ExecToErr("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' INTO TABLE t_load") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`GRANT INSERT on *.* to 'test_load'@'localhost'`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' INTO TABLE t_load") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + tk.MustExec(`GRANT INSERT on *.* to 'test_load'@'localhost'`) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) + err = tk.ExecToErr("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' REPLACE INTO TABLE t_load") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) +} + +func TestAuthHost(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + tk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'test_auth_host'@'%';`) + rootTk.MustExec(`GRANT ALL ON *.* TO 'test_auth_host'@'%' WITH GRANT OPTION;`) + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_auth_host", Hostname: "192.168.0.10"}, nil, nil, nil)) + tk.MustExec("CREATE USER 'test_auth_host'@'192.168.%';") + tk.MustExec("GRANT SELECT ON *.* TO 'test_auth_host'@'192.168.%';") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_auth_host", Hostname: "192.168.0.10"}, nil, nil, nil)) + err := tk.ExecToErr("create user test_auth_host_a") + require.Error(t, err) + + rootTk.MustExec("DROP USER 'test_auth_host'@'192.168.%';") + rootTk.MustExec("DROP USER 'test_auth_host'@'%';") +} + +func TestDefaultRoles(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'testdefault'@'localhost';`) + rootTk.MustExec(`CREATE ROLE 'testdefault_r1'@'localhost', 'testdefault_r2'@'localhost';`) + rootTk.MustExec(`GRANT 'testdefault_r1'@'localhost', 'testdefault_r2'@'localhost' TO 'testdefault'@'localhost';`) + + tk := testkit.NewTestKit(t, store) + pc := privilege.GetPrivilegeManager(tk.Session()) + + ret := pc.GetDefaultRoles("testdefault", "localhost") + require.Len(t, ret, 0) + + rootTk.MustExec(`SET DEFAULT ROLE ALL TO 'testdefault'@'localhost';`) + ret = pc.GetDefaultRoles("testdefault", "localhost") + require.Len(t, ret, 2) + + rootTk.MustExec(`SET DEFAULT ROLE NONE TO 'testdefault'@'localhost';`) + ret = pc.GetDefaultRoles("testdefault", "localhost") + require.Len(t, ret, 0) +} + +func TestUserTableConsistency(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewAsyncTestKit(t, store) + ctx := tk.OpenSession(context.Background(), "test") + tk.MustExec(ctx, "create user superadmin") + tk.MustExec(ctx, "grant all privileges on *.* to 'superadmin'") + + // GrantPriv is not in AllGlobalPrivs any more, see pingcap/parser#581 + require.Equal(t, len(mysql.Priv2UserCol), len(mysql.AllGlobalPrivs)+1) + + var buf bytes.Buffer + var res bytes.Buffer + buf.WriteString("select ") + i := 0 + for _, priv := range mysql.AllGlobalPrivs { + if i != 0 { + buf.WriteString(", ") + res.WriteString(" ") + } + buf.WriteString(mysql.Priv2UserCol[priv]) + res.WriteString("Y") + i++ + } + buf.WriteString(" from mysql.user where user = 'superadmin'") + tk.MustQuery(ctx, buf.String()).Check(testkit.Rows(res.String())) +} + +func TestDynamicPrivs(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec("CREATE USER notsuper") + rootTk.MustExec("CREATE USER otheruser") + rootTk.MustExec("CREATE ROLE anyrolename") + + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "notsuper", Hostname: "%"}, nil, nil, nil)) + + // test SYSTEM_VARIABLES_ADMIN + err := tk.ExecToErr("SET GLOBAL wait_timeout = 86400") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") + rootTk.MustExec("GRANT SYSTEM_VARIABLES_admin ON *.* TO notsuper") + tk.MustExec("SET GLOBAL wait_timeout = 86400") + + // test ROLE_ADMIN + err = tk.ExecToErr("GRANT anyrolename TO otheruser") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or ROLE_ADMIN privilege(s) for this operation") + rootTk.MustExec("GRANT ROLE_ADMIN ON *.* TO notsuper") + tk.MustExec("GRANT anyrolename TO otheruser") + + // revoke SYSTEM_VARIABLES_ADMIN, confirm it is dropped + rootTk.MustExec("REVOKE SYSTEM_VARIABLES_AdmIn ON *.* FROM notsuper") + err = tk.ExecToErr("SET GLOBAL wait_timeout = 86000") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") + + // grant super, confirm that it is also a substitute for SYSTEM_VARIABLES_ADMIN + rootTk.MustExec("GRANT SUPER ON *.* TO notsuper") + tk.MustExec("SET GLOBAL wait_timeout = 86400") + + // revoke SUPER, assign SYSTEM_VARIABLES_ADMIN to anyrolename. + // confirm that a dynamic privilege can be inherited from a role. + rootTk.MustExec("REVOKE SUPER ON *.* FROM notsuper") + rootTk.MustExec("GRANT SYSTEM_VARIABLES_AdmIn ON *.* TO anyrolename") + rootTk.MustExec("GRANT anyrolename TO notsuper") + + // It's not a default role, this should initially fail: + err = tk.ExecToErr("SET GLOBAL wait_timeout = 86400") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") + tk.MustExec("SET ROLE anyrolename") + tk.MustExec("SET GLOBAL wait_timeout = 87000") +} + +func TestDynamicGrantOption(t *testing.T) { + store := createStoreAndPrepareDB(t) + + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec("CREATE USER varuser1") + rootTk.MustExec("CREATE USER varuser2") + rootTk.MustExec("CREATE USER varuser3") + + rootTk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser1") + rootTk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser2 WITH GRANT OPTION") + + tk1 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "varuser1", Hostname: "%"}, nil, nil, nil)) + err := tk1.ExecToErr("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser3") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the GRANT OPTION privilege(s) for this operation") + + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "varuser2", Hostname: "%"}, nil, nil, nil)) + tk2.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser3") +} + +func TestSecurityEnhancedModeRestrictedTables(t *testing.T) { + store := createStoreAndPrepareDB(t) + + // This provides an integration test of the tests in util/security/security_test.go + cloudAdminTK := testkit.NewTestKit(t, store) + cloudAdminTK.MustExec("CREATE USER cloudadmin") + cloudAdminTK.MustExec("GRANT RESTRICTED_TABLES_ADMIN, SELECT ON *.* to cloudadmin") + cloudAdminTK.MustExec("GRANT CREATE ON mysql.* to cloudadmin") + cloudAdminTK.MustExec("CREATE USER uroot") + cloudAdminTK.MustExec("GRANT ALL ON *.* to uroot WITH GRANT OPTION") // A "MySQL" all powerful user. + require.NoError(t, cloudAdminTK.Session().Auth(&auth.UserIdentity{Username: "cloudadmin", Hostname: "%"}, nil, nil, nil)) + urootTk := testkit.NewTestKit(t, store) + require.NoError(t, urootTk.Session().Auth(&auth.UserIdentity{Username: "uroot", Hostname: "%"}, nil, nil, nil)) + + sem.Enable() + defer sem.Disable() + + err := urootTk.ExecToErr("use metrics_schema") + require.EqualError(t, err, "[executor:1044]Access denied for user 'uroot'@'%' to database 'metrics_schema'") + + err = urootTk.ExecToErr("SELECT * FROM metrics_schema.uptime") + require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'uroot'@'%' for table 'uptime'") + + err = urootTk.ExecToErr("CREATE TABLE mysql.abcd (a int)") + require.EqualError(t, err, "[planner:1142]CREATE command denied to user 'uroot'@'%' for table 'abcd'") + + cloudAdminTK.MustExec("USE metrics_schema") + cloudAdminTK.MustExec("SELECT * FROM metrics_schema.uptime") + cloudAdminTK.MustExec("CREATE TABLE mysql.abcd (a int)") +} + +func TestSecurityEnhancedModeInfoschema(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("CREATE USER uroot1, uroot2, uroot3") + tk.MustExec("GRANT SUPER ON *.* to uroot1 WITH GRANT OPTION") // super not process + tk.MustExec("GRANT SUPER, PROCESS, RESTRICTED_TABLES_ADMIN ON *.* to uroot2 WITH GRANT OPTION") + tk.Session().Auth(&auth.UserIdentity{ + Username: "uroot1", + Hostname: "localhost", + }, nil, nil, nil) + + sem.Enable() + defer sem.Disable() + + // Even though we have super, we still can't read protected information from tidb_servers_info, cluster_* tables + tk.MustQuery(`SELECT COUNT(*) FROM information_schema.tidb_servers_info WHERE ip IS NOT NULL`).Check(testkit.Rows("0")) + err := tk.QueryToErr(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NOT NULL`) + require.Error(t, err) + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") + + // That is unless we have the RESTRICTED_TABLES_ADMIN privilege + tk.Session().Auth(&auth.UserIdentity{ + Username: "uroot2", + Hostname: "localhost", + }, nil, nil, nil) + + // flip from is NOT NULL etc + tk.MustQuery(`SELECT COUNT(*) FROM information_schema.tidb_servers_info WHERE ip IS NULL`).Check(testkit.Rows("0")) + tk.MustQuery(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NULL`).Check(testkit.Rows("0")) +} + +func TestSecurityEnhancedLocalBackupRestore(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER backuprestore") + tk.MustExec("GRANT BACKUP_ADMIN,RESTORE_ADMIN ON *.* to backuprestore") + tk.Session().Auth(&auth.UserIdentity{ + Username: "backuprestore", + Hostname: "localhost", + }, nil, nil, nil) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + // Prior to SEM nolocal has permission, the error should be because backup requires tikv + _, err := tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'Local:///tmp/test';") + require.EqualError(t, err, "BACKUP requires tikv store, not unistore") + + _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'LOCAl:///tmp/test';") + require.EqualError(t, err, "RESTORE requires tikv store, not unistore") + + sem.Enable() + defer sem.Disable() + + // With SEM enabled nolocal does not have permission, but yeslocal does. + _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'local:///tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") + + _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'file:///tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") + + _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO '/tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") + + _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'LOCAl:///tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") + + _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'hdfs:///tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'hdfs storage' is not supported when security enhanced mode is enabled") + + _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'HDFS:///tmp/test';") + require.EqualError(t, err, "[planner:8132]Feature 'hdfs storage' is not supported when security enhanced mode is enabled") +} + +func TestSecurityEnhancedModeSysVars(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER svroot1, svroot2") + tk.MustExec("GRANT SUPER ON *.* to svroot1 WITH GRANT OPTION") + tk.MustExec("GRANT SELECT ON performance_schema.* to svroot1") + tk.MustExec("GRANT SUPER, RESTRICTED_VARIABLES_ADMIN ON *.* to svroot2") + tk.MustExec("GRANT SELECT ON performance_schema.* to svroot2") + + sem.Enable() + defer sem.Disable() + + // svroot1 has SUPER but in SEM will be restricted + tk.Session().Auth(&auth.UserIdentity{ + Username: "svroot1", + Hostname: "localhost", + AuthUsername: "uroot", + AuthHostname: "%", + }, nil, nil, nil) + + tk.MustQuery(`SHOW VARIABLES LIKE 'tidb_force_priority'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_force_priority'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_force_priority'`).Check(testkit.Rows()) + tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_enable_telemetry'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows()) + tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) + tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) + + _, err := tk.Exec("SET @@global.tidb_force_priority = 'NO_PRIORITY'") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + _, err = tk.Exec("SET GLOBAL tidb_enable_telemetry = OFF") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + _, err = tk.Exec("SET GLOBAL tidb_top_sql_max_time_series_count = 100") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + + _, err = tk.Exec("SELECT @@global.tidb_force_priority") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + _, err = tk.Exec("SELECT @@global.tidb_enable_telemetry") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + _, err = tk.Exec("SELECT @@global.tidb_top_sql_max_time_series_count") + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") + + tk.Session().Auth(&auth.UserIdentity{ + Username: "svroot2", + Hostname: "localhost", + AuthUsername: "uroot", + AuthHostname: "%", + }, nil, nil, nil) + + tk.MustQuery(`SHOW VARIABLES LIKE 'tidb_force_priority'`).Check(testkit.Rows("tidb_force_priority NO_PRIORITY")) + tk.MustQuery(`SELECT COUNT(*) FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows("1")) + tk.MustQuery(`SELECT COUNT(*) FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows("1")) + tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_enable_telemetry'`).Check(testkit.Rows("tidb_enable_telemetry OFF")) + tk.MustQuery(`SELECT COUNT(*) FROM information_schema.variables_info WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows("1")) + tk.MustQuery(`SELECT COUNT(*) FROM performance_schema.session_variables WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows("1")) + + // should not actually make any change. + tk.MustExec("SET @@global.tidb_force_priority = 'NO_PRIORITY'") + tk.MustExec("SET GLOBAL tidb_enable_telemetry = ON") + + tk.MustQuery(`SELECT @@global.tidb_force_priority`).Check(testkit.Rows("NO_PRIORITY")) + tk.MustQuery(`SELECT @@global.tidb_enable_telemetry`).Check(testkit.Rows("1")) + + tk.MustQuery(`SELECT @@hostname`).Check(testkit.Rows(variable.DefHostname)) + sem.Disable() + if hostname, err := os.Hostname(); err == nil { + tk.MustQuery(`SELECT @@hostname`).Check(testkit.Rows(hostname)) + } +} + +// TestViewDefiner tests that default roles are correctly applied in the algorithm definer +// See: https://github.com/pingcap/tidb/issues/24414 +func TestViewDefiner(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE issue24414") + tk.MustExec("USE issue24414") + tk.MustExec(`create table table1( + col1 int, + col2 int, + col3 int + )`) + tk.MustExec(`insert into table1 values (1,1,1),(2,2,2)`) + tk.MustExec(`CREATE ROLE 'ACL-mobius-admin'`) + tk.MustExec(`CREATE USER 'mobius-admin'`) + tk.MustExec(`CREATE USER 'mobius-admin-no-role'`) + tk.MustExec(`GRANT Select,Insert,Update,Delete,Create,Drop,Alter,Index,Create View,Show View ON issue24414.* TO 'ACL-mobius-admin'@'%'`) + tk.MustExec(`GRANT Select,Insert,Update,Delete,Create,Drop,Alter,Index,Create View,Show View ON issue24414.* TO 'mobius-admin-no-role'@'%'`) + tk.MustExec(`GRANT 'ACL-mobius-admin'@'%' to 'mobius-admin'@'%'`) + tk.MustExec(`SET DEFAULT ROLE ALL TO 'mobius-admin'`) + // create tables + tk.MustExec(`CREATE ALGORITHM = UNDEFINED DEFINER = 'mobius-admin'@'127.0.0.1' SQL SECURITY DEFINER VIEW test_view (col1 , col2 , col3) AS SELECT * from table1`) + tk.MustExec(`CREATE ALGORITHM = UNDEFINED DEFINER = 'mobius-admin-no-role'@'127.0.0.1' SQL SECURITY DEFINER VIEW test_view2 (col1 , col2 , col3) AS SELECT * from table1`) + + // all examples should work + tk.MustExec("select * from test_view") + tk.MustExec("select * from test_view2") +} + +func TestSecurityEnhancedModeRestrictedUsers(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER ruroot1, ruroot2, ruroot3") + tk.MustExec("CREATE ROLE notimportant") + tk.MustExec("GRANT SUPER, CREATE USER ON *.* to ruroot1 WITH GRANT OPTION") + tk.MustExec("GRANT SUPER, RESTRICTED_USER_ADMIN, CREATE USER ON *.* to ruroot2 WITH GRANT OPTION") + tk.MustExec("GRANT RESTRICTED_USER_ADMIN ON *.* to ruroot3") + tk.MustExec("GRANT notimportant TO ruroot2, ruroot3") + + sem.Enable() + defer sem.Disable() + + stmts := []string{ + "SET PASSWORD for ruroot3 = 'newpassword'", + "REVOKE notimportant FROM ruroot3", + "REVOKE SUPER ON *.* FROM ruroot3", + "DROP USER ruroot3", + } + + // ruroot1 has SUPER but in SEM will be restricted + tk.Session().Auth(&auth.UserIdentity{ + Username: "ruroot1", + Hostname: "localhost", + AuthUsername: "uroot", + AuthHostname: "%", + }, nil, nil, nil) + + for _, stmt := range stmts { + _, err := tk.Exec(stmt) + require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_USER_ADMIN privilege(s) for this operation") + } + + // Switch to ruroot2, it should be permitted + tk.Session().Auth(&auth.UserIdentity{ + Username: "ruroot2", + Hostname: "localhost", + AuthUsername: "uroot", + AuthHostname: "%", + }, nil, nil, nil) + + for _, stmt := range stmts { + tk.MustExec(stmt) + } +} + +func TestDynamicPrivsRegistration(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + pm := privilege.GetPrivilegeManager(tk.Session()) + + count := len(privileges.GetDynamicPrivileges()) + + require.False(t, pm.IsDynamicPrivilege("ACDC_ADMIN")) + require.Nil(t, privileges.RegisterDynamicPrivilege("ACDC_ADMIN")) + require.True(t, pm.IsDynamicPrivilege("ACDC_ADMIN")) + require.Len(t, privileges.GetDynamicPrivileges(), count+1) + + require.False(t, pm.IsDynamicPrivilege("iAmdynamIC")) + require.Nil(t, privileges.RegisterDynamicPrivilege("IAMdynamic")) + require.True(t, pm.IsDynamicPrivilege("IAMdyNAMIC")) + require.Len(t, privileges.GetDynamicPrivileges(), count+2) + + require.Equal(t, "privilege name is longer than 32 characters", privileges.RegisterDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS").Error()) + require.False(t, pm.IsDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS")) + + tk = testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER privassigntest") + + // Check that all privileges registered are assignable to users, + // including the recently registered ACDC_ADMIN + for _, priv := range privileges.GetDynamicPrivileges() { + sqlGrant, err := sqlexec.EscapeSQL("GRANT %n ON *.* TO privassigntest", priv) + require.NoError(t, err) + tk.MustExec(sqlGrant) + } + // Check that all privileges registered are revokable + for _, priv := range privileges.GetDynamicPrivileges() { + sqlGrant, err := sqlexec.EscapeSQL("REVOKE %n ON *.* FROM privassigntest", priv) + require.NoError(t, err) + tk.MustExec(sqlGrant) + } +} + +func TestInfoSchemaUserPrivileges(t *testing.T) { + // Being able to read all privileges from information_schema.user_privileges requires a very specific set of permissions. + // SUPER user is not sufficient. It was observed in MySQL to require SELECT on mysql.* + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE USER isnobody, isroot, isselectonmysqluser, isselectonmysql") + tk.MustExec("GRANT SUPER ON *.* TO isroot") + tk.MustExec("GRANT SELECT ON mysql.user TO isselectonmysqluser") + tk.MustExec("GRANT SELECT ON mysql.* TO isselectonmysql") + + // First as Nobody + tk.Session().Auth(&auth.UserIdentity{ + Username: "isnobody", + Hostname: "localhost", + }, nil, nil, nil) + + // I can see myself, but I can not see other users + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO")) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows()) + + // Basically the same result as as isselectonmysqluser + tk.Session().Auth(&auth.UserIdentity{ + Username: "isselectonmysqluser", + Hostname: "localhost", + }, nil, nil, nil) + + // Now as isselectonmysqluser + // Tests discovered issue that SELECT on mysql.user is not sufficient. It must be on mysql.* + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO")) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysql'@'%'"`).Check(testkit.Rows()) + + // Now as root + tk.Session().Auth(&auth.UserIdentity{ + Username: "isroot", + Hostname: "localhost", + }, nil, nil, nil) + + // I can see myself, but I can not see other users + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows()) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def SUPER NO")) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows()) + + // Now as isselectonmysqluser + tk.Session().Auth(&auth.UserIdentity{ + Username: "isselectonmysql", + Hostname: "localhost", + }, nil, nil, nil) + + // Now as isselectonmysqluser + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO")) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def SUPER NO")) + tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO")) +} + +// Issues https://github.com/pingcap/tidb/issues/25972 and https://github.com/pingcap/tidb/issues/26451 +func TestGrantOptionAndRevoke(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("DROP USER IF EXISTS u1, u2, u3, ruser") + tk.MustExec("CREATE USER u1, u2, u3, ruser") + tk.MustExec("GRANT ALL ON *.* TO ruser WITH GRANT OPTION") + tk.MustExec("GRANT SELECT ON *.* TO u1 WITH GRANT OPTION") + tk.MustExec("GRANT UPDATE, DELETE on db.* TO u1") + + tk.Session().Auth(&auth.UserIdentity{ + Username: "ruser", + Hostname: "localhost", + }, nil, nil, nil) + + tk.MustQuery(`SHOW GRANTS FOR u1`).Check(testkit.Rows("GRANT SELECT ON *.* TO 'u1'@'%' WITH GRANT OPTION", "GRANT UPDATE,DELETE ON `db`.* TO 'u1'@'%'")) + + tk.MustExec("GRANT SELECT ON d1.* to u2") + tk.MustExec("GRANT SELECT ON d2.* to u2 WITH GRANT OPTION") + tk.MustExec("GRANT SELECT ON d3.* to u2") + tk.MustExec("GRANT SELECT ON d4.* to u2") + tk.MustExec("GRANT SELECT ON d5.* to u2") + tk.MustQuery(`SHOW GRANTS FOR u2;`).Sort().Check(testkit.Rows( + "GRANT SELECT ON `d1`.* TO 'u2'@'%'", + "GRANT SELECT ON `d2`.* TO 'u2'@'%' WITH GRANT OPTION", + "GRANT SELECT ON `d3`.* TO 'u2'@'%'", + "GRANT SELECT ON `d4`.* TO 'u2'@'%'", + "GRANT SELECT ON `d5`.* TO 'u2'@'%'", + "GRANT USAGE ON *.* TO 'u2'@'%'", + )) + + tk.MustExec("grant all on hchwang.* to u3 with grant option") + tk.MustQuery(`SHOW GRANTS FOR u3;`).Check(testkit.Rows("GRANT USAGE ON *.* TO 'u3'@'%'", "GRANT ALL PRIVILEGES ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION")) + tk.MustExec("revoke all on hchwang.* from u3") + tk.MustQuery(`SHOW GRANTS FOR u3;`).Check(testkit.Rows("GRANT USAGE ON *.* TO 'u3'@'%'", "GRANT USAGE ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION")) + + // Same again but with column privileges. + + tk.MustExec("DROP TABLE IF EXISTS test.testgrant") + tk.MustExec("CREATE TABLE test.testgrant (a int)") + tk.MustExec("grant all on test.testgrant to u3 with grant option") + tk.MustExec("revoke all on test.testgrant from u3") + tk.MustQuery(`SHOW GRANTS FOR u3`).Sort().Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'u3'@'%'", + "GRANT USAGE ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION", + "GRANT USAGE ON `test`.`testgrant` TO 'u3'@'%' WITH GRANT OPTION", + )) +} + +func createStoreAndPrepareDB(t *testing.T) kv.Storage { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database if not exists test") + tk.MustExec("create database if not exists test1") + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));`) + tk.MustExec(fmt.Sprintf("create database if not exists %s;", mysql.SystemDB)) + tk.MustExec(session.CreateUserTable) + tk.MustExec(session.CreateDBPrivTable) + tk.MustExec(session.CreateTablePrivTable) + tk.MustExec(session.CreateColumnPrivTable) + return store +} + +func TestDashboardClientDynamicPriv(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE ROLE dc_r1") + tk.MustExec("CREATE USER dc_u1") + tk.MustExec("GRANT dc_r1 TO dc_u1") + tk.MustExec("SET DEFAULT ROLE dc_r1 TO dc_u1") + + tk1 := testkit.NewTestKit(t, store) + tk1.Session().Auth(&auth.UserIdentity{ + Username: "dc_u1", + Hostname: "localhost", + }, nil, nil, nil) + tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'dc_u1'@'%'", + "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", + )) + tk.MustExec("GRANT DASHBOARD_CLIENT ON *.* TO dc_r1") + tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'dc_u1'@'%'", + "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", + "GRANT DASHBOARD_CLIENT ON *.* TO 'dc_u1'@'%'", + )) + tk.MustExec("REVOKE DASHBOARD_CLIENT ON *.* FROM dc_r1") + tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'dc_u1'@'%'", + "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", + )) + tk.MustExec("GRANT DASHBOARD_CLIENT ON *.* TO dc_u1") + tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'dc_u1'@'%'", + "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", + "GRANT DASHBOARD_CLIENT ON *.* TO 'dc_u1'@'%'", + )) + tk.MustExec("REVOKE DASHBOARD_CLIENT ON *.* FROM dc_u1") + tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( + "GRANT USAGE ON *.* TO 'dc_u1'@'%'", + "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", + )) +} + +func TestGrantCreateTmpTables(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE create_tmp_table_db") + tk.MustExec("USE create_tmp_table_db") + tk.MustExec("CREATE USER u1") + tk.MustExec("CREATE TABLE create_tmp_table_table (a int)") + tk.MustExec("GRANT CREATE TEMPORARY TABLES on create_tmp_table_db.* to u1") + tk.MustExec("GRANT CREATE TEMPORARY TABLES on *.* to u1") + tk.MustGetErrCode("GRANT CREATE TEMPORARY TABLES on create_tmp_table_db.tmp to u1", mysql.ErrIllegalGrantForTable) + // Must set a session user to avoid null pointer dereference + tk.Session().Auth(&auth.UserIdentity{ + Username: "root", + Hostname: "localhost", + }, nil, nil, nil) + tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( + `GRANT CREATE TEMPORARY TABLES ON *.* TO 'u1'@'%'`, + "GRANT CREATE TEMPORARY TABLES ON `create_tmp_table_db`.* TO 'u1'@'%'")) + tk.MustExec("DROP USER u1") + tk.MustExec("DROP DATABASE create_tmp_table_db") +} + +func TestCreateTmpTablesPriv(t *testing.T) { + store := createStoreAndPrepareDB(t) + + createStmt := "CREATE TEMPORARY TABLE test.tmp(id int)" + dropStmt := "DROP TEMPORARY TABLE IF EXISTS test.tmp" + + tk := testkit.NewTestKit(t, store) + tk.MustExec(dropStmt) + tk.MustExec("CREATE TABLE test.t(id int primary key)") + tk.MustExec("CREATE SEQUENCE test.tmp") + tk.MustExec("CREATE USER vcreate, vcreate_tmp, vcreate_tmp_all") + tk.MustExec("GRANT CREATE, USAGE ON test.* TO vcreate") + tk.MustExec("GRANT CREATE TEMPORARY TABLES, USAGE ON test.* TO vcreate_tmp") + tk.MustExec("GRANT CREATE TEMPORARY TABLES, USAGE ON *.* TO vcreate_tmp_all") + + tk.Session().Auth(&auth.UserIdentity{Username: "vcreate", Hostname: "localhost"}, nil, nil, nil) + err := tk.ExecToErr(createStmt) + require.EqualError(t, err, "[planner:1044]Access denied for user 'vcreate'@'%' to database 'test'") + tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp", Hostname: "localhost"}, nil, nil, nil) + tk.MustExec(createStmt) + tk.MustExec(dropStmt) + tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp_all", Hostname: "localhost"}, nil, nil, nil) + // TODO: issue #29280 to be fixed. + //err = tk.ExecToErr(createStmt) + //require.EqualError(t, err, "[planner:1044]Access denied for user 'vcreate_tmp_all'@'%' to database 'test'") + + tests := []struct { + sql string + errcode int + }{ + { + sql: "create temporary table tmp(id int primary key)", + }, + { + sql: "insert into tmp value(1)", + }, + { + sql: "insert into tmp value(1) on duplicate key update id=1", + }, + { + sql: "replace tmp values(1)", + }, + { + sql: "insert into tmp select * from t", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "update tmp set id=1 where id=1", + }, + { + sql: "update tmp t1, t t2 set t1.id=t2.id where t1.id=t2.id", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "delete from tmp where id=1", + }, + { + sql: "delete t1 from tmp t1 join t t2 where t1.id=t2.id", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "select * from tmp where id=1", + }, + { + sql: "select * from tmp where id in (1,2)", + }, + { + sql: "select * from tmp", + }, + { + sql: "select * from tmp join t where tmp.id=t.id", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "(select * from tmp) union (select * from t)", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "create temporary table tmp1 like t", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "create table tmp(id int primary key)", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "create table t(id int primary key)", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "analyze table tmp", + }, + { + sql: "analyze table tmp, t", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "show create table tmp", + }, + // TODO: issue #29281 to be fixed. + //{ + // sql: "show create table t", + // errcode: mysql.ErrTableaccessDenied, + //}, + { + sql: "drop sequence tmp", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "alter table tmp add column c1 char(10)", + errcode: errno.ErrUnsupportedDDLOperation, + }, + { + sql: "truncate table tmp", + }, + { + sql: "drop temporary table t", + errcode: mysql.ErrBadTable, + }, + { + sql: "drop table t", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "drop table t, tmp", + errcode: mysql.ErrTableaccessDenied, + }, + { + sql: "drop temporary table tmp", + }, + } + + tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp", Hostname: "localhost"}, nil, nil, nil) + tk.MustExec("use test") + tk.MustExec(dropStmt) + for _, test := range tests { + if test.errcode == 0 { + tk.MustExec(test.sql) + } else { + tk.MustGetErrCode(test.sql, test.errcode) + } + } + for i, test := range tests { + preparedStmt := fmt.Sprintf("prepare stmt%d from '%s'", i, test.sql) + executeStmt := fmt.Sprintf("execute stmt%d", i) + if test.errcode == 0 { + tk.MustExec(preparedStmt) + tk.MustExec(executeStmt) + } else { + _, err := tk.Exec(preparedStmt) + if err != nil { + tk.MustGetErrCode(preparedStmt, test.errcode) + } else { + tk.MustGetErrCode(executeStmt, test.errcode) + } + } + } +} + +func TestGrantEvent(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("CREATE DATABASE event_db") + tk.MustExec("USE event_db") + tk.MustExec("CREATE USER u1") + tk.MustExec("CREATE TABLE event_table (a int)") + tk.MustExec("GRANT EVENT on event_db.* to u1") + tk.MustExec("GRANT EVENT on *.* to u1") + // Must set a session user to avoid null pointer dereferencing + tk.Session().Auth(&auth.UserIdentity{ + Username: "root", + Hostname: "localhost", + }, nil, nil, nil) + tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( + `GRANT EVENT ON *.* TO 'u1'@'%'`, + "GRANT EVENT ON `event_db`.* TO 'u1'@'%'")) + tk.MustExec("DROP USER u1") + tk.MustExec("DROP DATABASE event_db") +} + +func TestSkipGrantTable(t *testing.T) { + save := config.GetGlobalConfig() + config.UpdateGlobal(func(c *config.Config) { c.Security.SkipGrantTable = true }) + defer config.StoreGlobalConfig(save) + + store := createStoreAndPrepareDB(t) + + // Issue 29317 + tk := testkit.NewTestKit(t, store) + tk.MustExec(`CREATE USER 'test1'@'%';`) + tk.MustExec(`GRANT BACKUP_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTORE_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RELOAD ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SHUTDOWN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTRICTED_CONNECTION_ADMIN, CONNECTION_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTRICTED_USER_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT PROCESS ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SHUTDOWN ON *.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SELECT, INSERT, UPDATE, DELETE ON mysql.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SELECT ON information_schema.* TO 'test1'@'%';`) + tk.MustExec(`GRANT SELECT ON performance_schema.* TO 'test1'@'%';`) + tk.MustExec(`GRANT ALL PRIVILEGES ON *.* TO root;`) + tk.MustExec(`revoke SHUTDOWN on *.* from root;`) + tk.MustExec(`revoke CONFIG on *.* from root;`) + + tk.MustExec(`CREATE USER 'test2'@'%' IDENTIFIED BY '12345';`) + tk.MustExec(`GRANT PROCESS, CONFIG ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT SHOW DATABASES ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT DASHBOARD_CLIENT ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'test2'@'%';`) + tk.MustExec(`GRANT RESTRICTED_USER_ADMIN ON *.* TO 'test2'@'%';`) +} + +func TestIssue29823(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create user u1") + tk.MustExec("create role r1") + tk.MustExec("create table t1 (c1 int)") + tk.MustExec("grant select on t1 to r1") + tk.MustExec("grant r1 to u1") + + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "%"}, nil, nil, nil)) + tk2.MustExec("set role all") + tk2.MustQuery("select current_role()").Check(testkit.Rows("`r1`@`%`")) + tk2.MustQuery("select * from test.t1").Check(testkit.Rows()) + tk2.MustQuery("show databases like 'test'").Check(testkit.Rows("test")) + tk2.MustQuery("show tables from test").Check(testkit.Rows("t1")) + + tk.MustExec("revoke r1 from u1") + tk2.MustQuery("select current_role()").Check(testkit.Rows("`r1`@`%`")) + err := tk2.ExecToErr("select * from test.t1") + require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'u1'@'%' for table 't1'") + tk2.MustQuery("show databases like 'test'").Check(testkit.Rows()) + err = tk2.QueryToErr("show tables from test") + require.EqualError(t, err, "[executor:1044]Access denied for user 'u1'@'%' to database 'test'") +} + +func TestIssue37488(t *testing.T) { + store := createStoreAndPrepareDB(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE USER dba_test@'%';") + tk.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO 'dba_test'@'%';") + tk.MustExec("CREATE USER dba_test@'192.168.%';") + tk.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER ON test.* TO 'dba_test'@'192.168.%';") + + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dba_test", Hostname: "192.168.13.15"}, nil, nil, nil)) + tk.MustQuery("select current_user()").Check(testkit.Rows("dba_test@192.168.%")) + tk.MustExec("DROP TABLE IF EXISTS a;") // succ +} + +func TestCheckPasswordExpired(t *testing.T) { + sessionVars := variable.NewSessionVars(nil) + sessionVars.GlobalVarsAccessor = variable.NewMockGlobalAccessor4Tests() + record := privileges.NewUserRecord("%", "root") + userPrivilege := privileges.NewUserPrivileges(privileges.NewHandle(), nil) + + record.PasswordExpired = true + _, err := userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") + + record.PasswordExpired = false + err = sessionVars.GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.DefaultPasswordLifetime, "2") + require.NoError(t, err) + // use default_password_lifetime + record.PasswordLifeTime = -1 + record.PasswordLastChanged = time.Now().AddDate(0, 0, -2) + time.Sleep(time.Second) + _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") + record.PasswordLastChanged = time.Now().AddDate(0, 0, -1) + _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.NoError(t, err) + + // never expire + record.PasswordLifeTime = 0 + record.PasswordLastChanged = time.Now().AddDate(0, 0, -10) + _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.NoError(t, err) + + // expire with the specified time + record.PasswordLifeTime = 3 + record.PasswordLastChanged = time.Now().AddDate(0, 0, -3) + time.Sleep(time.Second) + _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") + record.PasswordLastChanged = time.Now().AddDate(0, 0, -2) + _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) + require.NoError(t, err) +} + +func TestPasswordExpireWithoutSandBoxMode(t *testing.T) { + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) + + // PASSWORD EXPIRE + user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost"} + tk := testkit.NewTestKit(t, store) + err := tk.Session().Auth(user, nil, nil, nil) + require.ErrorContains(t, err, "Your password has expired") + + // PASSWORD EXPIRE NEVER + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' IDENTIFIED BY '' PASSWORD EXPIRE NEVER`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + + // PASSWORD EXPIRE INTERVAL N DAY + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE INTERVAL 2 DAY`) + rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 1 DAY) where user='testuser'`) + rootTk.MustExec(`FLUSH PRIVILEGES`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 2 DAY) where user='testuser'`) + rootTk.MustExec(`FLUSH PRIVILEGES`) + time.Sleep(2 * time.Second) + err = tk.Session().Auth(user, nil, nil, nil) + require.ErrorContains(t, err, "Your password has expired") + + // PASSWORD EXPIRE DEFAULT + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE DEFAULT`) + rootTk.MustExec(`SET GLOBAL default_password_lifetime = 2`) + err = tk.Session().Auth(user, nil, nil, nil) + require.ErrorContains(t, err, "Your password has expired") + rootTk.MustExec(`SET GLOBAL default_password_lifetime = 3`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) +} + +func TestPasswordExpireWithSandBoxMode(t *testing.T) { + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) + variable.IsSandBoxModeEnabled.Store(true) + + // PASSWORD EXPIRE + user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost"} + tk := testkit.NewTestKit(t, store) + err := tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.True(t, tk.Session().InSandBoxMode()) + tk.Session().DisableSandBoxMode() + + // PASSWORD EXPIRE NEVER + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' IDENTIFIED BY '' PASSWORD EXPIRE NEVER`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.False(t, tk.Session().InSandBoxMode()) + + // PASSWORD EXPIRE INTERVAL N DAY + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE INTERVAL 2 DAY`) + rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 1 DAY) where user='testuser'`) + rootTk.MustExec(`FLUSH PRIVILEGES`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.False(t, tk.Session().InSandBoxMode()) + rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 2 DAY) where user='testuser'`) + rootTk.MustExec(`FLUSH PRIVILEGES`) + time.Sleep(2 * time.Second) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.True(t, tk.Session().InSandBoxMode()) + tk.Session().DisableSandBoxMode() + + // PASSWORD EXPIRE DEFAULT + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE DEFAULT`) + rootTk.MustExec(`SET GLOBAL default_password_lifetime = 2`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.True(t, tk.Session().InSandBoxMode()) + tk.Session().DisableSandBoxMode() + rootTk.MustExec(`SET GLOBAL default_password_lifetime = 3`) + err = tk.Session().Auth(user, nil, nil, nil) + require.NoError(t, err) + require.False(t, tk.Session().InSandBoxMode()) +} + +func TestVerificationInfoWithSessionTokenPlugin(t *testing.T) { + // prepare signing certs + tempDir := t.TempDir() + certPath := filepath.Join(tempDir, "test1_cert.pem") + keyPath := filepath.Join(tempDir, "test1_key.pem") + err := util.CreateCertificates(certPath, keyPath, 4096, x509.RSA, x509.UnknownSignatureAlgorithm) + require.NoError(t, err) + sessionstates.SetKeyPath(keyPath) + sessionstates.SetCertPath(certPath) + + // prepare user + store := createStoreAndPrepareDB(t) + rootTk := testkit.NewTestKit(t, store) + rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) + // prepare session token + token, err := sessionstates.CreateSessionToken("testuser") + require.NoError(t, err) + tokenBytes, err := json.Marshal(token) + require.NoError(t, err) + + // Test password expiration without sandbox. + user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost", AuthPlugin: mysql.AuthTiDBSessionToken} + tk := testkit.NewTestKit(t, store) + err = tk.Session().Auth(user, tokenBytes, nil, nil) + require.NoError(t, err) + require.False(t, tk.Session().InSandBoxMode()) + + // Test password expiration with sandbox. + variable.IsSandBoxModeEnabled.Store(true) + err = tk.Session().Auth(user, tokenBytes, nil, nil) + require.NoError(t, err) + require.False(t, tk.Session().InSandBoxMode()) + + // Enable resource group. + variable.EnableResourceControl.Store(true) + err = tk.Session().Auth(user, tokenBytes, nil, nil) + require.NoError(t, err) + require.Equal(t, "default", tk.Session().GetSessionVars().ResourceGroupName) + + // Non-default resource group. + rootTk.MustExec("CREATE RESOURCE GROUP rg1 RU_PER_SEC = 999") + rootTk.MustExec(`ALTER USER 'testuser'@'localhost' RESOURCE GROUP rg1`) + err = tk.Session().Auth(user, tokenBytes, nil, nil) + require.NoError(t, err) + require.Equal(t, "rg1", tk.Session().GetSessionVars().ResourceGroupName) + + // Wrong token + err = tk.Session().Auth(user, nil, nil, nil) + require.ErrorContains(t, err, "Access denied") +} + +func TestNilHandleInConnectionVerification(t *testing.T) { + config.GetGlobalConfig().Security.SkipGrantTable = true + privileges.SkipWithGrant = true + defer func() { + config.GetGlobalConfig().Security.SkipGrantTable = false + privileges.SkipWithGrant = false + }() + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, nil, nil)) +} + +func testShowGrantsSQLMode(t *testing.T, tk *testkit.TestKit, expected []string) { + pc := privilege.GetPrivilegeManager(tk.Session()) + gs, err := pc.ShowGrants(tk.Session(), &auth.UserIdentity{Username: "show_sql_mode", Hostname: "localhost"}, nil) + require.NoError(t, err) + require.Len(t, gs, 2) + require.True(t, testutil.CompareUnorderedStringSlice(gs, expected), fmt.Sprintf("gs: %v, expected: %v", gs, expected)) +} + +func TestShowGrantsSQLMode(t *testing.T) { + store := createStoreAndPrepareDB(t) + + tk := testkit.NewTestKit(t, store) + ctx, _ := tk.Session().(sessionctx.Context) + tk.MustExec(`CREATE USER 'show_sql_mode'@'localhost' identified by '123';`) + tk.MustExec(`GRANT Select ON test.* TO 'show_sql_mode'@'localhost';`) + + testShowGrantsSQLMode(t, tk, []string{ + "GRANT USAGE ON *.* TO 'show_sql_mode'@'localhost'", + "GRANT SELECT ON `test`.* TO 'show_sql_mode'@'localhost'", + }) + + ctx.GetSessionVars().SQLMode = mysql.SetSQLMode(ctx.GetSessionVars().SQLMode, mysql.ModeANSIQuotes) + testShowGrantsSQLMode(t, tk, []string{ + "GRANT USAGE ON *.* TO 'show_sql_mode'@'localhost'", + "GRANT SELECT ON \"test\".* TO 'show_sql_mode'@'localhost'", + }) +} diff --git a/privilege/privileges/tidb_auth_token.go b/pkg/privilege/privileges/tidb_auth_token.go similarity index 98% rename from privilege/privileges/tidb_auth_token.go rename to pkg/privilege/privileges/tidb_auth_token.go index db15f469f841c..713d1908246e1 100644 --- a/privilege/privileges/tidb_auth_token.go +++ b/pkg/privilege/privileges/tidb_auth_token.go @@ -27,7 +27,7 @@ import ( jwsRepo "github.com/lestrrat-go/jwx/v2/jws" jwtRepo "github.com/lestrrat-go/jwx/v2/jwt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/privilege/privileges/tidb_auth_token_test.go b/pkg/privilege/privileges/tidb_auth_token_test.go similarity index 99% rename from privilege/privileges/tidb_auth_token_test.go rename to pkg/privilege/privileges/tidb_auth_token_test.go index 4e036f00995d0..8fc8739892f0d 100644 --- a/privilege/privileges/tidb_auth_token_test.go +++ b/pkg/privilege/privileges/tidb_auth_token_test.go @@ -29,7 +29,7 @@ import ( jwsRepo "github.com/lestrrat-go/jwx/v2/jws" jwtRepo "github.com/lestrrat-go/jwx/v2/jwt" "github.com/lestrrat-go/jwx/v2/jwt/openid" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/require" ) diff --git a/pkg/resourcemanager/BUILD.bazel b/pkg/resourcemanager/BUILD.bazel new file mode 100644 index 0000000000000..7274b0e295c11 --- /dev/null +++ b/pkg/resourcemanager/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "resourcemanager", + srcs = [ + "rm.go", + "schedule.go", + ], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager", + visibility = ["//visibility:public"], + deps = [ + "//pkg/resourcemanager/scheduler", + "//pkg/resourcemanager/util", + "//pkg/util", + "//pkg/util/cpu", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "resourcemanager_test", + timeout = "short", + srcs = ["schedule_test.go"], + embed = [":resourcemanager"], + flaky = True, + deps = [ + "//pkg/resourcemanager/scheduler", + "//pkg/resourcemanager/util", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/resourcemanager/pool/BUILD.bazel b/pkg/resourcemanager/pool/BUILD.bazel new file mode 100644 index 0000000000000..beedcd1587357 --- /dev/null +++ b/pkg/resourcemanager/pool/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pool", + srcs = ["basepool.go"], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/pool", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_atomic//:atomic"], +) diff --git a/resourcemanager/pool/basepool.go b/pkg/resourcemanager/pool/basepool.go similarity index 100% rename from resourcemanager/pool/basepool.go rename to pkg/resourcemanager/pool/basepool.go diff --git a/pkg/resourcemanager/pool/spool/BUILD.bazel b/pkg/resourcemanager/pool/spool/BUILD.bazel new file mode 100644 index 0000000000000..03bec05e729c0 --- /dev/null +++ b/pkg/resourcemanager/pool/spool/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "spool", + srcs = [ + "option.go", + "spool.go", + ], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/pool/spool", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/resourcemanager", + "//pkg/resourcemanager/pool", + "//pkg/resourcemanager/poolmanager", + "//pkg/resourcemanager/util", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_sasha_s_go_deadlock//:go-deadlock", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "spool_test", + timeout = "short", + srcs = [ + "main_test.go", + "spool_test.go", + ], + embed = [":spool"], + flaky = True, + race = "on", + shard_count = 6, + deps = [ + "//pkg/resourcemanager/pool", + "//pkg/resourcemanager/util", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/resourcemanager/pool/spool/main_test.go b/pkg/resourcemanager/pool/spool/main_test.go new file mode 100644 index 0000000000000..338d85c617dd6 --- /dev/null +++ b/pkg/resourcemanager/pool/spool/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package spool + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/resourcemanager/pool/spool/option.go b/pkg/resourcemanager/pool/spool/option.go similarity index 100% rename from resourcemanager/pool/spool/option.go rename to pkg/resourcemanager/pool/spool/option.go diff --git a/resourcemanager/pool/spool/spool.go b/pkg/resourcemanager/pool/spool/spool.go similarity index 94% rename from resourcemanager/pool/spool/spool.go rename to pkg/resourcemanager/pool/spool/spool.go index 116b4756221e8..0982d1f85893f 100644 --- a/resourcemanager/pool/spool/spool.go +++ b/pkg/resourcemanager/pool/spool/spool.go @@ -19,13 +19,13 @@ import ( "sync/atomic" "time" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/resourcemanager" - "github.com/pingcap/tidb/resourcemanager/pool" - "github.com/pingcap/tidb/resourcemanager/poolmanager" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/resourcemanager" + "github.com/pingcap/tidb/pkg/resourcemanager/pool" + "github.com/pingcap/tidb/pkg/resourcemanager/poolmanager" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/prometheus/client_golang/prometheus" "github.com/sasha-s/go-deadlock" "go.uber.org/zap" diff --git a/resourcemanager/pool/spool/spool_test.go b/pkg/resourcemanager/pool/spool/spool_test.go similarity index 98% rename from resourcemanager/pool/spool/spool_test.go rename to pkg/resourcemanager/pool/spool/spool_test.go index c602e653348fa..a13823c235e53 100644 --- a/resourcemanager/pool/spool/spool_test.go +++ b/pkg/resourcemanager/pool/spool/spool_test.go @@ -21,8 +21,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/resourcemanager/pool" - "github.com/pingcap/tidb/resourcemanager/util" + "github.com/pingcap/tidb/pkg/resourcemanager/pool" + "github.com/pingcap/tidb/pkg/resourcemanager/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/resourcemanager/pool/workerpool/BUILD.bazel b/pkg/resourcemanager/pool/workerpool/BUILD.bazel new file mode 100644 index 0000000000000..1074b1e701ff1 --- /dev/null +++ b/pkg/resourcemanager/pool/workerpool/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "workerpool", + srcs = ["workerpool.go"], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/pool/workerpool", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/resourcemanager/util", + "//pkg/util", + "//pkg/util/syncutil", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "workerpool_test", + timeout = "short", + srcs = [ + "main_test.go", + "workpool_test.go", + ], + embed = [":workerpool"], + flaky = True, + race = "on", + shard_count = 4, + deps = [ + "//pkg/resourcemanager/util", + "//pkg/testkit/testsetup", + "//pkg/util/logutil", + "@com_github_stretchr_testify//require", + "@org_golang_x_sync//errgroup", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/resourcemanager/pool/workerpool/main_test.go b/pkg/resourcemanager/pool/workerpool/main_test.go new file mode 100644 index 0000000000000..03371df4293fd --- /dev/null +++ b/pkg/resourcemanager/pool/workerpool/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package workerpool + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/resourcemanager/pool/workerpool/workerpool.go b/pkg/resourcemanager/pool/workerpool/workerpool.go similarity index 96% rename from resourcemanager/pool/workerpool/workerpool.go rename to pkg/resourcemanager/pool/workerpool/workerpool.go index 8be568c741a93..caee5704a671c 100644 --- a/resourcemanager/pool/workerpool/workerpool.go +++ b/pkg/resourcemanager/pool/workerpool/workerpool.go @@ -18,10 +18,10 @@ import ( "context" "time" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/resourcemanager/util" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/syncutil" atomicutil "go.uber.org/atomic" ) diff --git a/resourcemanager/pool/workerpool/workpool_test.go b/pkg/resourcemanager/pool/workerpool/workpool_test.go similarity index 97% rename from resourcemanager/pool/workerpool/workpool_test.go rename to pkg/resourcemanager/pool/workerpool/workpool_test.go index 560b46720e1ab..074fe8ef76e94 100644 --- a/resourcemanager/pool/workerpool/workpool_test.go +++ b/pkg/resourcemanager/pool/workerpool/workpool_test.go @@ -20,8 +20,8 @@ import ( "sync/atomic" "testing" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" "golang.org/x/sync/errgroup" diff --git a/pkg/resourcemanager/poolmanager/BUILD.bazel b/pkg/resourcemanager/poolmanager/BUILD.bazel new file mode 100644 index 0000000000000..804fae51265f8 --- /dev/null +++ b/pkg/resourcemanager/poolmanager/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "poolmanager", + srcs = [ + "task_manager.go", + "task_manager_iterator.go", + "task_manager_scheduler.go", + ], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/poolmanager", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_atomic//:atomic"], +) diff --git a/resourcemanager/poolmanager/task_manager.go b/pkg/resourcemanager/poolmanager/task_manager.go similarity index 100% rename from resourcemanager/poolmanager/task_manager.go rename to pkg/resourcemanager/poolmanager/task_manager.go diff --git a/resourcemanager/poolmanager/task_manager_iterator.go b/pkg/resourcemanager/poolmanager/task_manager_iterator.go similarity index 100% rename from resourcemanager/poolmanager/task_manager_iterator.go rename to pkg/resourcemanager/poolmanager/task_manager_iterator.go diff --git a/resourcemanager/poolmanager/task_manager_scheduler.go b/pkg/resourcemanager/poolmanager/task_manager_scheduler.go similarity index 100% rename from resourcemanager/poolmanager/task_manager_scheduler.go rename to pkg/resourcemanager/poolmanager/task_manager_scheduler.go diff --git a/resourcemanager/rm.go b/pkg/resourcemanager/rm.go similarity index 92% rename from resourcemanager/rm.go rename to pkg/resourcemanager/rm.go index 025eb0fcbc129..6a09fccfcf508 100644 --- a/resourcemanager/rm.go +++ b/pkg/resourcemanager/rm.go @@ -18,10 +18,10 @@ import ( "time" "github.com/google/uuid" - "github.com/pingcap/tidb/resourcemanager/scheduler" - "github.com/pingcap/tidb/resourcemanager/util" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/cpu" + "github.com/pingcap/tidb/pkg/resourcemanager/scheduler" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/cpu" ) // InstanceResourceManager is a local instance resource manager diff --git a/resourcemanager/schedule.go b/pkg/resourcemanager/schedule.go similarity index 95% rename from resourcemanager/schedule.go rename to pkg/resourcemanager/schedule.go index 737fe2a71f6de..563eb4744b7ca 100644 --- a/resourcemanager/schedule.go +++ b/pkg/resourcemanager/schedule.go @@ -18,8 +18,8 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/resourcemanager/scheduler" - "github.com/pingcap/tidb/resourcemanager/util" + "github.com/pingcap/tidb/pkg/resourcemanager/scheduler" + "github.com/pingcap/tidb/pkg/resourcemanager/util" "go.uber.org/zap" ) diff --git a/resourcemanager/schedule_test.go b/pkg/resourcemanager/schedule_test.go similarity index 90% rename from resourcemanager/schedule_test.go rename to pkg/resourcemanager/schedule_test.go index 30947fb4f47e3..389066aba7479 100644 --- a/resourcemanager/schedule_test.go +++ b/pkg/resourcemanager/schedule_test.go @@ -17,8 +17,8 @@ package resourcemanager import ( "testing" - "github.com/pingcap/tidb/resourcemanager/scheduler" - "github.com/pingcap/tidb/resourcemanager/util" + "github.com/pingcap/tidb/pkg/resourcemanager/scheduler" + "github.com/pingcap/tidb/pkg/resourcemanager/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/resourcemanager/scheduler/BUILD.bazel b/pkg/resourcemanager/scheduler/BUILD.bazel new file mode 100644 index 0000000000000..b9fd4c0d01525 --- /dev/null +++ b/pkg/resourcemanager/scheduler/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "scheduler", + srcs = [ + "cpu_scheduler.go", + "scheduler.go", + ], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/scheduler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/resourcemanager/util", + "//pkg/util/cpu", + ], +) diff --git a/resourcemanager/scheduler/cpu_scheduler.go b/pkg/resourcemanager/scheduler/cpu_scheduler.go similarity index 92% rename from resourcemanager/scheduler/cpu_scheduler.go rename to pkg/resourcemanager/scheduler/cpu_scheduler.go index 7b0f487916876..1ebd04da5c0cf 100644 --- a/resourcemanager/scheduler/cpu_scheduler.go +++ b/pkg/resourcemanager/scheduler/cpu_scheduler.go @@ -17,8 +17,8 @@ package scheduler import ( "time" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/util/cpu" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/util/cpu" ) // CPUScheduler is a cpu scheduler diff --git a/pkg/resourcemanager/scheduler/scheduler.go b/pkg/resourcemanager/scheduler/scheduler.go new file mode 100644 index 0000000000000..dfb632981dedc --- /dev/null +++ b/pkg/resourcemanager/scheduler/scheduler.go @@ -0,0 +1,36 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package scheduler + +import ( + "github.com/pingcap/tidb/pkg/resourcemanager/util" +) + +// Command is the command for scheduler +type Command int + +const ( + // Downclock is to reduce the number of concurrency. + Downclock Command = iota + // Hold is to hold the number of concurrency. + Hold + // Overclock is to increase the number of concurrency. + Overclock +) + +// Scheduler is a scheduler interface +type Scheduler interface { + Tune(component util.Component, p util.GoroutinePool) Command +} diff --git a/pkg/resourcemanager/util/BUILD.bazel b/pkg/resourcemanager/util/BUILD.bazel new file mode 100644 index 0000000000000..eda1af406175b --- /dev/null +++ b/pkg/resourcemanager/util/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "util", + srcs = [ + "mock_gpool.go", + "shard_pool_map.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/resourcemanager/util", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/intest", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "util_test", + timeout = "short", + srcs = ["shard_pool_map_test.go"], + embed = [":util"], + flaky = True, + deps = [ + "//pkg/util/intest", + "@com_github_stretchr_testify//require", + ], +) diff --git a/resourcemanager/util/mock_gpool.go b/pkg/resourcemanager/util/mock_gpool.go similarity index 100% rename from resourcemanager/util/mock_gpool.go rename to pkg/resourcemanager/util/mock_gpool.go diff --git a/resourcemanager/util/shard_pool_map.go b/pkg/resourcemanager/util/shard_pool_map.go similarity index 97% rename from resourcemanager/util/shard_pool_map.go rename to pkg/resourcemanager/util/shard_pool_map.go index 3b3975b3b933f..daa970eb2418e 100644 --- a/resourcemanager/util/shard_pool_map.go +++ b/pkg/resourcemanager/util/shard_pool_map.go @@ -18,7 +18,7 @@ import ( "sync" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/util/intest" ) const shard = 8 diff --git a/resourcemanager/util/shard_pool_map_test.go b/pkg/resourcemanager/util/shard_pool_map_test.go similarity index 97% rename from resourcemanager/util/shard_pool_map_test.go rename to pkg/resourcemanager/util/shard_pool_map_test.go index 996853d13d2a0..f018cefb082ed 100644 --- a/resourcemanager/util/shard_pool_map_test.go +++ b/pkg/resourcemanager/util/shard_pool_map_test.go @@ -19,7 +19,7 @@ import ( "sync/atomic" "testing" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/util/intest" "github.com/stretchr/testify/require" ) diff --git a/resourcemanager/util/util.go b/pkg/resourcemanager/util/util.go similarity index 100% rename from resourcemanager/util/util.go rename to pkg/resourcemanager/util/util.go diff --git a/pkg/server/BUILD.bazel b/pkg/server/BUILD.bazel new file mode 100644 index 0000000000000..a8f6075d6b464 --- /dev/null +++ b/pkg/server/BUILD.bazel @@ -0,0 +1,187 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "server", + srcs = [ + "conn.go", + "conn_stmt.go", + "driver.go", + "driver_tidb.go", + "extension.go", + "extract.go", + "http_handler.go", + "http_status.go", + "mock_conn.go", + "rpc_server.go", + "server.go", + "stat.go", + "tokenlimiter.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server", + visibility = ["//visibility:public"], + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor", + "//pkg/executor/mppcoordmanager", + "//pkg/expression", + "//pkg/extension", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/plugin", + "//pkg/privilege", + "//pkg/privilege/conn", + "//pkg/privilege/privileges", + "//pkg/privilege/privileges/ldap", + "//pkg/server/err", + "//pkg/server/handler", + "//pkg/server/handler/extactorhandler", + "//pkg/server/handler/optimizor", + "//pkg/server/handler/tikvhandler", + "//pkg/server/handler/ttlhandler", + "//pkg/server/internal", + "//pkg/server/internal/column", + "//pkg/server/internal/dump", + "//pkg/server/internal/handshake", + "//pkg/server/internal/parse", + "//pkg/server/internal/resultset", + "//pkg/server/internal/util", + "//pkg/server/metrics", + "//pkg/session", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/statistics/handle", + "//pkg/store", + "//pkg/store/driver/error", + "//pkg/store/helper", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/arena", + "//pkg/util/chunk", + "//pkg/util/cpuprofile", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/execdetails", + "//pkg/util/fastrand", + "//pkg/util/hack", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/printer", + "//pkg/util/sqlexec", + "//pkg/util/sys/linux", + "//pkg/util/timeutil", + "//pkg/util/tls", + "//pkg/util/topsql", + "//pkg/util/topsql/state", + "//pkg/util/topsql/stmtstats", + "//pkg/util/tracing", + "//pkg/util/versioninfo", + "@com_github_blacktear23_go_proxyprotocol//:go-proxyprotocol", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_fn//:fn", + "@com_github_pingcap_kvproto//pkg/autoid", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/diagnosticspb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_kvproto//pkg/tikvpb", + "@com_github_pingcap_sysutil//:sysutil", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_golang//prometheus/promhttp", + "@com_github_soheilhy_cmux//:cmux", + "@com_github_stretchr_testify//require", + "@com_github_tiancaiamao_appdash//traceapp", + "@com_github_tikv_client_go_v2//util", + "@com_sourcegraph_sourcegraph_appdash_data//:appdash-data", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//channelz/service", + "@org_golang_google_grpc//keepalive", + "@org_golang_google_grpc//peer", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "server_test", + timeout = "short", + srcs = [ + "conn_stmt_test.go", + "conn_test.go", + "driver_tidb_test.go", + "main_test.go", + "mock_conn_test.go", + "server_test.go", + "stat_test.go", + "tidb_library_test.go", + "tidb_test.go", + ], + data = glob(["testdata/**"]), + embed = [":server"], + flaky = True, + shard_count = 48, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/extension", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/server/internal", + "//pkg/server/internal/column", + "//pkg/server/internal/handshake", + "//pkg/server/internal/parse", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/arena", + "//pkg/util/chunk", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/replayer", + "//pkg/util/syncutil", + "//pkg/util/topsql/state", + "@com_github_docker_go_units//:go-units", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/server/conn.go b/pkg/server/conn.go similarity index 97% rename from server/conn.go rename to pkg/server/conn.go index b617930a29b82..ee9566dea4448 100644 --- a/server/conn.go +++ b/pkg/server/conn.go @@ -56,52 +56,52 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/conn" - "github.com/pingcap/tidb/privilege/privileges/ldap" - servererr "github.com/pingcap/tidb/server/err" - "github.com/pingcap/tidb/server/handler/tikvhandler" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/server/internal/dump" - "github.com/pingcap/tidb/server/internal/handshake" - "github.com/pingcap/tidb/server/internal/parse" - "github.com/pingcap/tidb/server/internal/resultset" - util2 "github.com/pingcap/tidb/server/internal/util" - server_metrics "github.com/pingcap/tidb/server/metrics" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - tlsutil "github.com/pingcap/tidb/util/tls" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/conn" + "github.com/pingcap/tidb/pkg/privilege/privileges/ldap" + servererr "github.com/pingcap/tidb/pkg/server/err" + "github.com/pingcap/tidb/pkg/server/handler/tikvhandler" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/server/internal/dump" + "github.com/pingcap/tidb/pkg/server/internal/handshake" + "github.com/pingcap/tidb/pkg/server/internal/parse" + "github.com/pingcap/tidb/pkg/server/internal/resultset" + util2 "github.com/pingcap/tidb/pkg/server/internal/util" + server_metrics "github.com/pingcap/tidb/pkg/server/metrics" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + tlsutil "github.com/pingcap/tidb/pkg/util/tls" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/prometheus/client_golang/prometheus" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" diff --git a/server/conn_stmt.go b/pkg/server/conn_stmt.go similarity index 95% rename from server/conn_stmt.go rename to pkg/server/conn_stmt.go index d20907687e0c4..93e7e1511f4d1 100644 --- a/server/conn_stmt.go +++ b/pkg/server/conn_stmt.go @@ -44,26 +44,26 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/server/internal/dump" - "github.com/pingcap/tidb/server/internal/parse" - "github.com/pingcap/tidb/server/internal/resultset" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/server/internal/dump" + "github.com/pingcap/tidb/pkg/server/internal/parse" + "github.com/pingcap/tidb/pkg/server/internal/resultset" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/tikv/client-go/v2/util" "go.uber.org/zap" ) diff --git a/server/conn_stmt_test.go b/pkg/server/conn_stmt_test.go similarity index 97% rename from server/conn_stmt_test.go rename to pkg/server/conn_stmt_test.go index fc14f3283c61f..0cdf29d6d5147 100644 --- a/server/conn_stmt_test.go +++ b/pkg/server/conn_stmt_test.go @@ -30,14 +30,14 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) @@ -260,9 +260,9 @@ func TestCursorFetchShouldSpill(t *testing.T) { srv.SetDomain(dom) defer srv.Close() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/testCursorFetchSpill", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/testCursorFetchSpill", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/testCursorFetchSpill")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/testCursorFetchSpill")) }() appendUint32 := binary.LittleEndian.AppendUint32 diff --git a/server/conn_test.go b/pkg/server/conn_test.go similarity index 90% rename from server/conn_test.go rename to pkg/server/conn_test.go index 5ac672ce7d490..4813508c532a7 100644 --- a/server/conn_test.go +++ b/pkg/server/conn_test.go @@ -31,26 +31,26 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/server/internal/handshake" - "github.com/pingcap/tidb/server/internal/parse" - "github.com/pingcap/tidb/server/internal/testutil" - serverutil "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/server/internal/handshake" + "github.com/pingcap/tidb/pkg/server/internal/parse" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + serverutil "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/testutils" @@ -673,9 +673,9 @@ func TestConnExecutionTimeout(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) // There is no underlying netCon, use failpoint to avoid panic - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeClientConn", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeClientConn", "return(1)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeClientConn")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeClientConn")) }() tk := testkit.NewTestKit(t, store) @@ -1005,14 +1005,14 @@ func TestTiFlashFallback(t *testing.T) { tk.MustExec(dml) tk.MustQuery("select count(*) from t").Check(testkit.Rows("50")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/internal/mpp/ReduceCopNextMaxBackoff", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/ReduceCopNextMaxBackoff", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/internal/mpp/ReduceCopNextMaxBackoff", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/ReduceCopNextMaxBackoff", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/internal/mpp/ReduceCopNextMaxBackoff")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/ReduceCopNextMaxBackoff")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/internal/mpp/ReduceCopNextMaxBackoff")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/ReduceCopNextMaxBackoff")) }() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) // test COM_STMT_EXECUTE ctx := context.Background() tk.MustExec("set @@tidb_allow_fallback_to_tikv='tiflash'") @@ -1025,19 +1025,19 @@ func TestTiFlashFallback(t *testing.T) { require.Error(t, cc.handleStmtExecute(ctx, []byte{0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0})) tk.MustExec("set @@tidb_allow_fallback_to_tikv=''") require.Error(t, cc.handleStmtExecute(ctx, []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0})) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/fetchNextErr", "return(\"firstNext\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/fetchNextErr", "return(\"firstNext\")")) // test COM_STMT_EXECUTE (cursor mode) tk.MustExec("set @@tidb_allow_fallback_to_tikv='tiflash'") require.NoError(t, cc.handleStmtExecute(ctx, []byte{0x1, 0x0, 0x0, 0x0, 0x1, 0x1, 0x0, 0x0, 0x0})) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/fetchNextErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/fetchNextErr")) // test that TiDB would not retry if the first execution already sends data to client - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/fetchNextErr", "return(\"secondNext\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/fetchNextErr", "return(\"secondNext\")")) tk.MustExec("set @@tidb_allow_fallback_to_tikv='tiflash'") require.Error(t, cc.handleQuery(ctx, "select * from t t1 join t t2 on t1.a = t2.a")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/fetchNextErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/fetchNextErr")) // simple TiFlash query (unary + non-streaming) tk.MustExec("set @@tidb_allow_batch_cop=0; set @@tidb_allow_mpp=0;") @@ -1048,31 +1048,31 @@ func TestTiFlashFallback(t *testing.T) { // TiFlash query based on batch cop (batch + streaming) tk.MustExec("set @@tidb_allow_batch_cop=1; set @@tidb_allow_mpp=0;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) testFallbackWork(t, tk, cc, "select count(*) from t") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/batchCopRecvTimeout", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/batchCopRecvTimeout", "return(true)")) testFallbackWork(t, tk, cc, "select count(*) from t") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/batchCopRecvTimeout")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/batchCopRecvTimeout")) // TiFlash MPP query (MPP + streaming) tk.MustExec("set @@tidb_allow_batch_cop=0; set @@tidb_allow_mpp=1;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/mppDispatchTimeout", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppDispatchTimeout", "return(true)")) testFallbackWork(t, tk, cc, "select * from t t1 join t t2 on t1.a = t2.a") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/mppDispatchTimeout")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppDispatchTimeout")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/mppRecvTimeout", "return(-1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppRecvTimeout", "return(-1)")) testFallbackWork(t, tk, cc, "select * from t t1 join t t2 on t1.a = t2.a") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/mppRecvTimeout")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppRecvTimeout")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/establishMppConnectionErr", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/establishMppConnectionErr", "return(true)")) testFallbackWork(t, tk, cc, "select * from t t1 join t t2 on t1.a = t2.a") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/establishMppConnectionErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/establishMppConnectionErr")) // When fallback is not set, TiFlash mpp will return the original error message - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/mppDispatchTimeout", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/mppDispatchTimeout", "return(true)")) tk.MustExec("set @@tidb_allow_fallback_to_tikv=''") tk.MustExec("set @@tidb_allow_mpp=ON") tk.MustExec("set @@tidb_enforce_mpp=ON") @@ -1152,7 +1152,7 @@ func TestHandleAuthPlugin(t *testing.T) { require.NoError(t, err) // 8.0 or newer client trying to authenticate with caching_sha2_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1170,10 +1170,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthNativePassword), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // client trying to authenticate with tidb_sm3_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1191,7 +1191,7 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthNativePassword), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // MySQL 5.1 or older client, without authplugin support cc = &clientConn{ @@ -1211,10 +1211,10 @@ func TestHandleAuthPlugin(t *testing.T) { require.NoError(t, err) // === Target account has mysql_native_password === - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeUser", "return(\"mysql_native_password\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeUser", "return(\"mysql_native_password\")")) // 5.7 or newer client trying to authenticate with mysql_native_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1232,10 +1232,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthNativePassword), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // 8.0 or newer client trying to authenticate with caching_sha2_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1253,10 +1253,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthNativePassword), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // client trying to authenticate with tidb_sm3_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1274,7 +1274,7 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthNativePassword), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // MySQL 5.1 or older client, without authplugin support cc = &clientConn{ @@ -1292,13 +1292,13 @@ func TestHandleAuthPlugin(t *testing.T) { } err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeUser")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeUser")) // === Target account has caching_sha2_password === - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeUser", "return(\"caching_sha2_password\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeUser", "return(\"caching_sha2_password\")")) // 5.7 or newer client trying to authenticate with mysql_native_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1316,10 +1316,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthCachingSha2Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // 8.0 or newer client trying to authenticate with caching_sha2_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1337,10 +1337,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthCachingSha2Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // client trying to authenticate with tidb_sm3_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1358,7 +1358,7 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthCachingSha2Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // MySQL 5.1 or older client, without authplugin support cc = &clientConn{ @@ -1376,13 +1376,13 @@ func TestHandleAuthPlugin(t *testing.T) { } err = cc.handleAuthPlugin(ctx, &resp) require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeUser")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeUser")) // === Target account has tidb_sm3_password === - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeUser", "return(\"tidb_sm3_password\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeUser", "return(\"tidb_sm3_password\")")) // 5.7 or newer client trying to authenticate with mysql_native_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1400,10 +1400,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthTiDBSM3Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // 8.0 or newer client trying to authenticate with caching_sha2_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1421,10 +1421,10 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthTiDBSM3Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // client trying to authenticate with tidb_sm3_password - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) cc = &clientConn{ connectionID: 1, alloc: arena.NewAllocator(1024), @@ -1442,7 +1442,7 @@ func TestHandleAuthPlugin(t *testing.T) { err = cc.handleAuthPlugin(ctx, &resp) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthTiDBSM3Password), resp.Auth) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) // MySQL 5.1 or older client, without authplugin support cc = &clientConn{ @@ -1460,7 +1460,7 @@ func TestHandleAuthPlugin(t *testing.T) { } err = cc.handleAuthPlugin(ctx, &resp) require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeUser")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeUser")) } func TestChangeUserAuth(t *testing.T) { @@ -1505,9 +1505,9 @@ func TestChangeUserAuth(t *testing.T) { data = append(data, 0, 0) data = append(data, "unknown"...) data = append(data, 0) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/ChangeUserAuthSwitch", fmt.Sprintf("return(\"%s\")", t.Name()))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/ChangeUserAuthSwitch", fmt.Sprintf("return(\"%s\")", t.Name()))) err = cc.handleChangeUser(ctx, data) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/ChangeUserAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/ChangeUserAuthSwitch")) require.EqualError(t, err, t.Name()) } @@ -1543,9 +1543,9 @@ func TestAuthPlugin2(t *testing.T) { } cc.isUnixSocket = true - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) respAuthSwitch, err := cc.checkAuthPlugin(ctx, &resp) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) require.Equal(t, []byte(mysql.AuthNativePassword), respAuthSwitch) require.NoError(t, err) } @@ -1967,12 +1967,12 @@ func TestLDAPAuthSwitch(t *testing.T) { cc.SetCtx(tc) cc.isUnixSocket = true - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/FakeAuthSwitch", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch", "return(1)")) respAuthSwitch, err := cc.checkAuthPlugin(context.Background(), &handshake.Response41{ Capability: mysql.ClientProtocol41 | mysql.ClientPluginAuth, User: "test_simple_ldap", }) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/FakeAuthSwitch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/FakeAuthSwitch")) require.NoError(t, err) require.Equal(t, []byte(mysql.AuthMySQLClearPassword), respAuthSwitch) } diff --git a/server/driver.go b/pkg/server/driver.go similarity index 93% rename from server/driver.go rename to pkg/server/driver.go index b7b7baa2b9bc8..91a8ebf59787b 100644 --- a/server/driver.go +++ b/pkg/server/driver.go @@ -18,10 +18,10 @@ import ( "context" "crypto/tls" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/server/internal/resultset" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/server/internal/resultset" + "github.com/pingcap/tidb/pkg/util/chunk" ) // IDriver opens IContext. diff --git a/server/driver_tidb.go b/pkg/server/driver_tidb.go similarity index 94% rename from server/driver_tidb.go rename to pkg/server/driver_tidb.go index d8e7bc1b8bb7d..9e70f5ad18d30 100644 --- a/server/driver_tidb.go +++ b/pkg/server/driver_tidb.go @@ -21,23 +21,23 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - servererr "github.com/pingcap/tidb/server/err" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/server/internal/resultset" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + servererr "github.com/pingcap/tidb/pkg/server/err" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/server/internal/resultset" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" ) // TiDBDriver implements IDriver. diff --git a/server/driver_tidb_test.go b/pkg/server/driver_tidb_test.go similarity index 92% rename from server/driver_tidb_test.go rename to pkg/server/driver_tidb_test.go index 4a09bbfc46f0d..af824ad912c9f 100644 --- a/server/driver_tidb_test.go +++ b/pkg/server/driver_tidb_test.go @@ -17,12 +17,12 @@ package server import ( "testing" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/err/BUILD.bazel b/pkg/server/err/BUILD.bazel new file mode 100644 index 0000000000000..d8635324afeb1 --- /dev/null +++ b/pkg/server/err/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "err", + srcs = ["error.go"], + importpath = "github.com/pingcap/tidb/pkg/server/err", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/util/dbterror", + ], +) diff --git a/pkg/server/err/error.go b/pkg/server/err/error.go new file mode 100644 index 0000000000000..952811951147c --- /dev/null +++ b/pkg/server/err/error.go @@ -0,0 +1,47 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package err + +import ( + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +var ( + // ErrInvalidType is returned when the type of a value is not expected. + ErrInvalidType = dbterror.ClassServer.NewStd(errno.ErrInvalidType) + // ErrInvalidSequence is returned when the sequence of method invocations is not correct. + ErrInvalidSequence = dbterror.ClassServer.NewStd(errno.ErrInvalidSequence) + // ErrNotAllowedCommand is returned when the used command is not in the expected list. + ErrNotAllowedCommand = dbterror.ClassServer.NewStd(errno.ErrNotAllowedCommand) + // ErrAccessDenied is returned when the user does not have sufficient privileges. + ErrAccessDenied = dbterror.ClassServer.NewStd(errno.ErrAccessDenied) + // ErrAccessDeniedNoPassword is returned when trying to use an anonymous user without password is not allowed. + ErrAccessDeniedNoPassword = dbterror.ClassServer.NewStd(errno.ErrAccessDeniedNoPassword) + // ErrConCount is returned when too many connections are established by the user. + ErrConCount = dbterror.ClassServer.NewStd(errno.ErrConCount) + // ErrSecureTransportRequired is returned when the user tries to connect without SSL. + ErrSecureTransportRequired = dbterror.ClassServer.NewStd(errno.ErrSecureTransportRequired) + // ErrMultiStatementDisabled is returned when the user tries to send multiple statements in one statement. + ErrMultiStatementDisabled = dbterror.ClassServer.NewStd(errno.ErrMultiStatementDisabled) + // ErrNewAbortingConnection is returned when the user tries to connect with an aborting connection. + ErrNewAbortingConnection = dbterror.ClassServer.NewStd(errno.ErrNewAbortingConnection) + // ErrNotSupportedAuthMode is returned when the user uses an unsupported authentication method. + ErrNotSupportedAuthMode = dbterror.ClassServer.NewStd(errno.ErrNotSupportedAuthMode) + // ErrNetPacketTooLarge is returned when the user sends a packet too large. + ErrNetPacketTooLarge = dbterror.ClassServer.NewStd(errno.ErrNetPacketTooLarge) + // ErrMustChangePassword is returned when the user must change the password. + ErrMustChangePassword = dbterror.ClassServer.NewStd(errno.ErrMustChangePassword) +) diff --git a/pkg/server/extension.go b/pkg/server/extension.go new file mode 100644 index 0000000000000..2ffc23047613e --- /dev/null +++ b/pkg/server/extension.go @@ -0,0 +1,254 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package server + +import ( + "fmt" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" +) + +func (cc *clientConn) onExtensionConnEvent(tp extension.ConnEventTp, err error) { + if cc.extensions == nil { + return + } + + var connInfo *variable.ConnectionInfo + var activeRoles []*auth.RoleIdentity + var sessionAlias string + if ctx := cc.getCtx(); ctx != nil { + sessVars := ctx.GetSessionVars() + connInfo = sessVars.ConnectionInfo + sessionAlias = sessVars.SessionAlias + activeRoles = sessVars.ActiveRoles + } + + if connInfo == nil { + connInfo = cc.connectInfo() + } + + info := &extension.ConnEventInfo{ + ConnectionInfo: connInfo, + SessionAlias: sessionAlias, + ActiveRoles: activeRoles, + Error: err, + } + + cc.extensions.OnConnectionEvent(tp, info) +} + +func (cc *clientConn) onExtensionStmtEnd(node interface{}, stmtCtxValid bool, err error, args ...expression.Expression) { + if !cc.extensions.HasStmtEventListeners() { + return + } + + ctx := cc.getCtx() + if ctx == nil { + return + } + + tp := extension.StmtSuccess + if err != nil { + tp = extension.StmtError + } + + sessVars := ctx.GetSessionVars() + info := &stmtEventInfo{ + sessVars: sessVars, + err: err, + } + + switch stmt := node.(type) { + case *ast.ExecuteStmt: + info.executeStmt = stmt + info.stmtNode = stmt + case PreparedStatement: + info.executeStmtID = uint32(stmt.ID()) + prepared, _ := sessVars.GetPreparedStmtByID(info.executeStmtID) + info.executeStmt = &ast.ExecuteStmt{ + PrepStmt: prepared, + BinaryArgs: args, + } + info.stmtNode = info.executeStmt + case ast.StmtNode: + info.stmtNode = stmt + } + + if stmtCtxValid { + info.sc = sessVars.StmtCtx + } else { + info.sc = stmtctx.NewStmtCtx() + } + cc.extensions.OnStmtEvent(tp, info) +} + +// onSQLParseFailed will be called when sql parse failed +func (cc *clientConn) onExtensionSQLParseFailed(sql string, err error) { + if !cc.extensions.HasStmtEventListeners() { + return + } + + cc.extensions.OnStmtEvent(extension.StmtError, &stmtEventInfo{ + sessVars: cc.getCtx().GetSessionVars(), + err: err, + failedParseText: sql, + }) +} + +func (cc *clientConn) onExtensionBinaryExecuteEnd(prep PreparedStatement, args []expression.Expression, stmtCtxValid bool, err error) { + cc.onExtensionStmtEnd(prep, stmtCtxValid, err, args...) +} + +type stmtEventInfo struct { + sessVars *variable.SessionVars + sc *stmtctx.StatementContext + stmtNode ast.StmtNode + // execute info + executeStmtID uint32 + executeStmt *ast.ExecuteStmt + executePreparedCached bool + executePreparedCache *core.PlanCacheStmt + // error will only be valid when the stmt is failed + err error + // failedParseText will only present on parse failed + failedParseText string +} + +func (e *stmtEventInfo) ConnectionInfo() *variable.ConnectionInfo { + return e.sessVars.ConnectionInfo +} + +func (e *stmtEventInfo) SessionAlias() string { + return e.sessVars.SessionAlias +} + +func (e *stmtEventInfo) StmtNode() ast.StmtNode { + return e.stmtNode +} + +func (e *stmtEventInfo) ExecuteStmtNode() *ast.ExecuteStmt { + return e.executeStmt +} + +func (e *stmtEventInfo) ExecutePreparedStmt() ast.StmtNode { + if cache := e.ensureExecutePreparedCache(); cache != nil { + return cache.PreparedAst.Stmt + } + return nil +} + +func (e *stmtEventInfo) PreparedParams() []types.Datum { + return e.sessVars.PlanCacheParams.AllParamValues() +} + +func (e *stmtEventInfo) OriginalText() string { + if sql := e.ensureStmtContextOriginalSQL(); sql != "" { + return sql + } + + if e.executeStmtID != 0 { + return binaryExecuteStmtText(e.executeStmtID) + } + + return e.failedParseText +} + +func (e *stmtEventInfo) SQLDigest() (normalized string, digest *parser.Digest) { + if sql := e.ensureStmtContextOriginalSQL(); sql != "" { + return e.sc.SQLDigest() + } + + if e.executeStmtID != 0 { + return binaryExecuteStmtText(e.executeStmtID), nil + } + + return e.failedParseText, nil +} + +func (e *stmtEventInfo) User() *auth.UserIdentity { + return e.sessVars.User +} + +func (e *stmtEventInfo) ActiveRoles() []*auth.RoleIdentity { + return e.sessVars.ActiveRoles +} + +func (e *stmtEventInfo) CurrentDB() string { + return e.sessVars.CurrentDB +} + +func (e *stmtEventInfo) AffectedRows() uint64 { + if e.sc == nil || e.err != nil { + return 0 + } + return e.sc.AffectedRows() +} + +func (e *stmtEventInfo) RelatedTables() []stmtctx.TableEntry { + if e.sc == nil { + return nil + } + return e.sc.Tables +} + +func (e *stmtEventInfo) GetError() error { + return e.err +} + +func (e *stmtEventInfo) ensureExecutePreparedCache() *core.PlanCacheStmt { + if e.executeStmt == nil { + return nil + } + + if !e.executePreparedCached { + e.executePreparedCache, _ = core.GetPreparedStmt(e.executeStmt, e.sessVars) + e.executePreparedCached = true + } + + return e.executePreparedCache +} + +func (e *stmtEventInfo) ensureStmtContextOriginalSQL() string { + if e.sc == nil { + return "" + } + + if sql := e.sc.OriginalSQL; sql != "" { + return sql + } + + if planCache := e.ensureExecutePreparedCache(); planCache != nil { + e.sc.OriginalSQL = planCache.PreparedAst.Stmt.Text() + e.sc.InitSQLDigest(planCache.NormalizedSQL, planCache.SQLDigest) + } + + if e.sc.OriginalSQL == "" && e.executeStmtID == 0 && e.stmtNode != nil { + e.sc.OriginalSQL = e.stmtNode.Text() + } + + return e.sc.OriginalSQL +} + +func binaryExecuteStmtText(id uint32) string { + return fmt.Sprintf("BINARY EXECUTE (ID %d)", id) +} diff --git a/pkg/server/extract.go b/pkg/server/extract.go new file mode 100644 index 0000000000000..e8c20481d62fc --- /dev/null +++ b/pkg/server/extract.go @@ -0,0 +1,26 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package server + +import "github.com/pingcap/tidb/pkg/server/handler/extactorhandler" + +// newExtractServeHandler returns extractTaskServeHandler +func (s *Server) newExtractServeHandler() *extactorhandler.ExtractTaskServeHandler { + esh := &extactorhandler.ExtractTaskServeHandler{} + if s.dom != nil { + esh = extactorhandler.NewExtractTaskServeHandler(s.dom.GetExtractHandle()) + } + return esh +} diff --git a/pkg/server/handler/BUILD.bazel b/pkg/server/handler/BUILD.bazel new file mode 100644 index 0000000000000..0aff0b1c56151 --- /dev/null +++ b/pkg/server/handler/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "handler", + srcs = [ + "tikv_handler.go", + "upgrade_handler.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server/handler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain/infosync", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/sessionctx/stmtctx", + "//pkg/store/driver/error", + "//pkg/store/helper", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/logutil", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/server/handler/extactorhandler/BUILD.bazel b/pkg/server/handler/extactorhandler/BUILD.bazel new file mode 100644 index 0000000000000..658fa2420872d --- /dev/null +++ b/pkg/server/handler/extactorhandler/BUILD.bazel @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "extactorhandler", + srcs = ["extactor.go"], + importpath = "github.com/pingcap/tidb/pkg/server/handler/extactorhandler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/server/handler", + "//pkg/types", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "extactorhandler_test", + timeout = "short", + srcs = [ + "extract_test.go", + "main_test.go", + ], + flaky = True, + deps = [ + ":extactorhandler", + "//pkg/config", + "//pkg/metrics", + "//pkg/server", + "//pkg/server/internal/testserverclient", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "//pkg/session", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/topsql/state", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/server/handler/extactorhandler/extactor.go b/pkg/server/handler/extactorhandler/extactor.go similarity index 96% rename from server/handler/extactorhandler/extactor.go rename to pkg/server/handler/extactorhandler/extactor.go index 0b1d920e1b98c..7e2a1ccd95c75 100644 --- a/server/handler/extactorhandler/extactor.go +++ b/pkg/server/handler/extactorhandler/extactor.go @@ -27,10 +27,10 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/server/handler/extactorhandler/extract_test.go b/pkg/server/handler/extactorhandler/extract_test.go new file mode 100644 index 0000000000000..7f2144a26c512 --- /dev/null +++ b/pkg/server/handler/extactorhandler/extract_test.go @@ -0,0 +1,125 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package extactorhandler_test + +import ( + "database/sql" + "fmt" + "net/http" + "net/url" + "os" + "testing" + "time" + + "github.com/gorilla/mux" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + server2 "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/handler/extactorhandler" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + "github.com/stretchr/testify/require" +) + +func TestExtractHandler(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + + driver := server2.NewTiDBDriver(store) + client := testserverclient.NewTestServerClient() + cfg := util.NewTestConfig() + cfg.Port = client.Port + cfg.Status.StatusPort = client.StatusPort + cfg.Status.ReportStatus = true + + server, err := server2.NewServer(cfg, driver) + require.NoError(t, err) + defer server.Close() + + dom, err := session.GetDomain(store) + require.NoError(t, err) + server.SetDomain(dom) + + client.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + client.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + client.WaitUntilServerOnline() + startTime := time.Now() + time.Sleep(time.Second) + prepareData4ExtractPlanTask(t, client) + time.Sleep(time.Second) + endTime := time.Now() + eh := &extactorhandler.ExtractTaskServeHandler{ExtractHandler: dom.GetExtractHandle()} + router := mux.NewRouter() + router.Handle("/extract_task/dump", eh) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/extractTaskServeHandler", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/extractTaskServeHandler")) + }() + resp0, err := client.FetchStatus(fmt.Sprintf("/extract_task/dump?type=plan&begin=%s&end=%s", + url.QueryEscape(startTime.Format(types.TimeFormat)), url.QueryEscape(endTime.Format(types.TimeFormat)))) + require.NoError(t, err) + defer func() { + require.NoError(t, resp0.Body.Close()) + }() + require.Equal(t, resp0.StatusCode, http.StatusOK) + resp0, err = client.FetchStatus("/extract_task/dump?type=plan") + require.NoError(t, err) + defer func() { + require.NoError(t, resp0.Body.Close()) + }() + require.Equal(t, resp0.StatusCode, http.StatusOK) +} + +func prepareData4ExtractPlanTask(t *testing.T, client *testserverclient.TestServerClient) { + db, err := sql.Open("mysql", client.GetDSN()) + require.NoError(t, err, "Error connecting") + defer func() { + err := db.Close() + require.NoError(t, err) + }() + tk := testkit.NewDBTestKit(t, db) + tk.MustExec("use test") + tk.MustExec("create table t(id int)") + tk.MustExec("select * from t") +} + +func setupStmtSummary() { + stmtsummaryv2.Setup(&stmtsummaryv2.Config{ + Filename: "tidb-statements.log", + }) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = true + }) +} + +func closeStmtSummary() { + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = false + }) + stmtsummaryv2.GlobalStmtSummary.Close() + stmtsummaryv2.GlobalStmtSummary = nil + _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) +} diff --git a/pkg/server/handler/extactorhandler/main_test.go b/pkg/server/handler/extactorhandler/main_test.go new file mode 100644 index 0000000000000..c166e0f511bb8 --- /dev/null +++ b/pkg/server/handler/extactorhandler/main_test.go @@ -0,0 +1,73 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package extactorhandler_test + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + server.RunInGoTest = true + testsetup.SetupForCommonTest() + topsqlstate.EnableTopSQL() + unistore.CheckResourceTagForTopSQLInGoTest = true + + // AsyncCommit will make DDL wait 2.5s before changing to the next state. + // Set schema lease to avoid it from making CI slow. + session.SetSchemaLease(0) + + tikv.EnableFailpoints() + + metrics.RegisterMetrics() + + // sanity check: the global config should not be changed by other pkg init function. + // see also https://github.com/pingcap/tidb/issues/22162 + defaultConfig := config.NewConfig() + globalConfig := config.GetGlobalConfig() + if !reflect.DeepEqual(defaultConfig, globalConfig) { + _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") + _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("time.Sleep"), + goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/server.NewServer.func1"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/server/handler/optimizor/BUILD.bazel b/pkg/server/handler/optimizor/BUILD.bazel new file mode 100644 index 0000000000000..b6483f0f756d8 --- /dev/null +++ b/pkg/server/handler/optimizor/BUILD.bazel @@ -0,0 +1,74 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "optimizor", + srcs = [ + "optimize_trace.go", + "plan_replayer.go", + "statistics_handler.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server/handler/optimizor", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/server/handler", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle", + "//pkg/statistics/handle/storage", + "//pkg/table", + "//pkg/types", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/replayer", + "@com_github_burntsushi_toml//:toml", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "optimizor_test", + timeout = "short", + srcs = [ + "main_test.go", + "optimize_trace_test.go", + "plan_replayer_test.go", + "statistics_handler_test.go", + ], + flaky = True, + shard_count = 4, + deps = [ + ":optimizor", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/server", + "//pkg/server/internal/testserverclient", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "//pkg/session", + "//pkg/statistics/handle/globalstats", + "//pkg/statistics/handle/storage", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/topsql/state", + "@com_github_burntsushi_toml//:toml", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/server/handler/optimizor/main_test.go b/pkg/server/handler/optimizor/main_test.go new file mode 100644 index 0000000000000..6e4cfe2f8f3b5 --- /dev/null +++ b/pkg/server/handler/optimizor/main_test.go @@ -0,0 +1,73 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package optimizor_test + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + server.RunInGoTest = true + testsetup.SetupForCommonTest() + topsqlstate.EnableTopSQL() + unistore.CheckResourceTagForTopSQLInGoTest = true + + // AsyncCommit will make DDL wait 2.5s before changing to the next state. + // Set schema lease to avoid it from making CI slow. + session.SetSchemaLease(0) + + tikv.EnableFailpoints() + + metrics.RegisterMetrics() + + // sanity check: the global config should not be changed by other pkg init function. + // see also https://github.com/pingcap/tidb/issues/22162 + defaultConfig := config.NewConfig() + globalConfig := config.GetGlobalConfig() + if !reflect.DeepEqual(defaultConfig, globalConfig) { + _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") + _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("time.Sleep"), + goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/server.NewServer.func1"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/server/handler/optimizor/optimize_trace.go b/pkg/server/handler/optimizor/optimize_trace.go similarity index 91% rename from server/handler/optimizor/optimize_trace.go rename to pkg/server/handler/optimizor/optimize_trace.go index 3e4407bc01059..f7becf5bf1091 100644 --- a/server/handler/optimizor/optimize_trace.go +++ b/pkg/server/handler/optimizor/optimize_trace.go @@ -20,10 +20,10 @@ import ( "path/filepath" "github.com/gorilla/mux" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/util" ) // OptimizeTraceHandler serve http diff --git a/server/handler/optimizor/optimize_trace_test.go b/pkg/server/handler/optimizor/optimize_trace_test.go similarity index 88% rename from server/handler/optimizor/optimize_trace_test.go rename to pkg/server/handler/optimizor/optimize_trace_test.go index 806f2cfc29c68..41af62ba6d1ba 100644 --- a/server/handler/optimizor/optimize_trace_test.go +++ b/pkg/server/handler/optimizor/optimize_trace_test.go @@ -21,13 +21,13 @@ import ( "testing" "github.com/gorilla/mux" - server2 "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/handler/optimizor" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" + server2 "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/handler/optimizor" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/handler/optimizor/plan_replayer.go b/pkg/server/handler/optimizor/plan_replayer.go new file mode 100644 index 0000000000000..12ea19f964ed9 --- /dev/null +++ b/pkg/server/handler/optimizor/plan_replayer.go @@ -0,0 +1,375 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package optimizor + +import ( + "archive/zip" + "bytes" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/BurntSushi/toml" + "github.com/gorilla/mux" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/replayer" + "go.uber.org/zap" +) + +// PlanReplayerHandler is the handler for dumping plan replayer file. +type PlanReplayerHandler struct { + is infoschema.InfoSchema + statsHandle *handle.Handle + infoGetter *infosync.InfoSyncer + address string + statusPort uint +} + +// NewPlanReplayerHandler creates a new PlanReplayerHandler. +func NewPlanReplayerHandler(is infoschema.InfoSchema, statsHandle *handle.Handle, infoGetter *infosync.InfoSyncer, address string, statusPort uint) *PlanReplayerHandler { + return &PlanReplayerHandler{ + is: is, + statsHandle: statsHandle, + infoGetter: infoGetter, + address: address, + statusPort: statusPort, + } +} + +// ServeHTTP handles request of dumping plan replayer file. +func (prh PlanReplayerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + params := mux.Vars(req) + name := params[handler.FileName] + handler := downloadFileHandler{ + filePath: filepath.Join(replayer.GetPlanReplayerDirName(), name), + fileName: name, + infoGetter: prh.infoGetter, + address: prh.address, + statusPort: prh.statusPort, + urlPath: fmt.Sprintf("plan_replayer/dump/%s", name), + downloadedFilename: "plan_replayer", + scheme: util.InternalHTTPSchema(), + statsHandle: prh.statsHandle, + is: prh.is, + } + handleDownloadFile(handler, w, req) +} + +func handleDownloadFile(dfHandler downloadFileHandler, w http.ResponseWriter, req *http.Request) { + params := mux.Vars(req) + name := params[handler.FileName] + path := dfHandler.filePath + isForwarded := len(req.URL.Query().Get("forward")) > 0 + localAddr := net.JoinHostPort(dfHandler.address, strconv.Itoa(int(dfHandler.statusPort))) + exist, err := isExists(path) + if err != nil { + handler.WriteError(w, err) + return + } + if exist { + //nolint: gosec + file, err := os.Open(path) + if err != nil { + handler.WriteError(w, err) + return + } + content, err := io.ReadAll(file) + if err != nil { + handler.WriteError(w, err) + return + } + err = file.Close() + if err != nil { + handler.WriteError(w, err) + return + } + if dfHandler.downloadedFilename == "plan_replayer" { + content, err = handlePlanReplayerCaptureFile(content, path, dfHandler) + if err != nil { + handler.WriteError(w, err) + return + } + } + _, err = w.Write(content) + if err != nil { + handler.WriteError(w, err) + return + } + w.Header().Set("Content-Type", "application/zip") + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", dfHandler.downloadedFilename)) + logutil.BgLogger().Info("return dump file successfully", zap.String("filename", name), + zap.String("address", localAddr), zap.Bool("forwarded", isForwarded)) + return + } + // handler.infoGetter will be nil only in unit test + // or we couldn't find file for forward request, return 404 + if dfHandler.infoGetter == nil || isForwarded { + logutil.BgLogger().Info("failed to find dump file", zap.String("filename", name), + zap.String("address", localAddr), zap.Bool("forwarded", isForwarded)) + w.WriteHeader(http.StatusNotFound) + return + } + // If we didn't find file in origin request, try to broadcast the request to all remote tidb-servers + topos, err := dfHandler.infoGetter.GetAllTiDBTopology(req.Context()) + if err != nil { + handler.WriteError(w, err) + return + } + client := util.InternalHTTPClient() + // transfer each remote tidb-server and try to find dump file + for _, topo := range topos { + if topo.IP == dfHandler.address && topo.StatusPort == dfHandler.statusPort { + continue + } + remoteAddr := net.JoinHostPort(topo.IP, strconv.Itoa(int(topo.StatusPort))) + url := fmt.Sprintf("%s://%s/%s?forward=true", dfHandler.scheme, remoteAddr, dfHandler.urlPath) + resp, err := client.Get(url) + if err != nil { + logutil.BgLogger().Error("forward request failed", + zap.String("remote-addr", remoteAddr), zap.Error(err)) + continue + } + if resp.StatusCode != http.StatusOK { + logutil.BgLogger().Info("can't find file in remote server", zap.String("filename", name), + zap.String("remote-addr", remoteAddr), zap.Int("status-code", resp.StatusCode)) + continue + } + content, err := io.ReadAll(resp.Body) + if err != nil { + handler.WriteError(w, err) + return + } + err = resp.Body.Close() + if err != nil { + handler.WriteError(w, err) + return + } + _, err = w.Write(content) + if err != nil { + handler.WriteError(w, err) + return + } + // find dump file in one remote tidb-server, return file directly + w.Header().Set("Content-Type", "application/zip") + w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", dfHandler.downloadedFilename)) + logutil.BgLogger().Info("return dump file successfully in remote server", + zap.String("filename", name), zap.String("remote-addr", remoteAddr)) + return + } + // we can't find dump file in any tidb-server, return 404 directly + logutil.BgLogger().Info("can't find dump file in any remote server", zap.String("filename", name)) + w.WriteHeader(http.StatusNotFound) + _, err = fmt.Fprintf(w, "can't find dump file %s in any remote server", name) + handler.WriteError(w, err) +} + +type downloadFileHandler struct { + scheme string + filePath string + fileName string + infoGetter *infosync.InfoSyncer + address string + statusPort uint + urlPath string + downloadedFilename string + + statsHandle *handle.Handle + is infoschema.InfoSchema +} + +func isExists(path string) (bool, error) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} + +func handlePlanReplayerCaptureFile(content []byte, path string, handler downloadFileHandler) ([]byte, error) { + if !strings.HasPrefix(handler.filePath, "capture_replayer") { + return content, nil + } + b := bytes.NewReader(content) + zr, err := zip.NewReader(b, int64(len(content))) + if err != nil { + return nil, err + } + startTS, err := loadSQLMetaFile(zr) + if err != nil { + return nil, err + } + if startTS == 0 { + return content, nil + } + tbls, err := loadSchemaMeta(zr, handler.is) + if err != nil { + return nil, err + } + for _, tbl := range tbls { + jsonStats, _, err := handler.statsHandle.DumpHistoricalStatsBySnapshot(tbl.dbName, tbl.info, startTS) + if err != nil { + return nil, err + } + tbl.jsonStats = jsonStats + } + newPath, err := dumpJSONStatsIntoZip(tbls, content, path) + if err != nil { + return nil, err + } + //nolint: gosec + file, err := os.Open(newPath) + if err != nil { + return nil, err + } + content, err = io.ReadAll(file) + if err != nil { + return nil, err + } + err = file.Close() + if err != nil { + return nil, err + } + return content, nil +} + +func loadSQLMetaFile(z *zip.Reader) (uint64, error) { + for _, zipFile := range z.File { + if zipFile.Name == domain.PlanReplayerSQLMetaFile { + varMap := make(map[string]string) + v, err := zipFile.Open() + if err != nil { + return 0, errors.AddStack(err) + } + //nolint: errcheck,all_revive,revive + defer v.Close() + _, err = toml.NewDecoder(v).Decode(&varMap) + if err != nil { + return 0, errors.AddStack(err) + } + startTS, err := strconv.ParseUint(varMap[domain.PlanReplayerSQLMetaStartTS], 10, 64) + if err != nil { + return 0, err + } + return startTS, nil + } + } + return 0, nil +} + +func loadSchemaMeta(z *zip.Reader, is infoschema.InfoSchema) (map[int64]*tblInfo, error) { + r := make(map[int64]*tblInfo, 0) + for _, zipFile := range z.File { + if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) { + v, err := zipFile.Open() + if err != nil { + return nil, errors.AddStack(err) + } + //nolint: errcheck,all_revive,revive + defer v.Close() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(v) + if err != nil { + return nil, errors.AddStack(err) + } + rows := strings.Split(buf.String(), "\n") + for _, row := range rows { + s := strings.Split(row, ";") + databaseName := s[0] + tableName := s[1] + t, err := is.TableByName(model.NewCIStr(databaseName), model.NewCIStr(tableName)) + if err != nil { + return nil, err + } + r[t.Meta().ID] = &tblInfo{ + info: t.Meta(), + dbName: databaseName, + tblName: tableName, + } + } + break + } + } + return r, nil +} + +func dumpJSONStatsIntoZip(tbls map[int64]*tblInfo, content []byte, path string) (string, error) { + zr, err := zip.NewReader(bytes.NewReader(content), int64(len(content))) + if err != nil { + return "", err + } + newPath := strings.Replace(path, "capture_replayer", "copy_capture_replayer", 1) + zf, err := os.Create(newPath) + if err != nil { + return "", err + } + zw := zip.NewWriter(zf) + for _, f := range zr.File { + err = zw.Copy(f) + if err != nil { + logutil.BgLogger().Error("copy plan replayer zip file failed", zap.Error(err)) + return "", err + } + } + for _, tbl := range tbls { + w, err := zw.Create(fmt.Sprintf("stats/%v.%v.json", tbl.dbName, tbl.tblName)) + if err != nil { + return "", err + } + data, err := json.Marshal(tbl.jsonStats) + if err != nil { + return "", err + } + _, err = w.Write(data) + if err != nil { + return "", err + } + } + err = zw.Close() + if err != nil { + logutil.BgLogger().Error("Closing file failed", zap.Error(err)) + return "", err + } + err = zf.Close() + if err != nil { + logutil.BgLogger().Error("Closing file failed", zap.Error(err)) + return "", err + } + return newPath, nil +} + +type tblInfo struct { + info *model.TableInfo + jsonStats *storage.JSONTable + dbName string + tblName string +} diff --git a/pkg/server/handler/optimizor/plan_replayer_test.go b/pkg/server/handler/optimizor/plan_replayer_test.go new file mode 100644 index 0000000000000..7545fa2f280a0 --- /dev/null +++ b/pkg/server/handler/optimizor/plan_replayer_test.go @@ -0,0 +1,452 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package optimizor_test + +import ( + "archive/zip" + "bytes" + "database/sql" + "encoding/json" + "fmt" + "io" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "testing" + "time" + + "github.com/BurntSushi/toml" + "github.com/go-sql-driver/mysql" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" +) + +var expectedFilesInReplayer = []string{ + "config.toml", + "debug_trace/debug_trace0.json", + "explain.txt", + "global_bindings.sql", + "meta.txt", + "schema/planreplayer.t.schema.txt", + "schema/schema_meta.txt", + "session_bindings.sql", + "sql/sql0.sql", + "sql_meta.toml", + "stats/planreplayer.t.json", + "statsMem/planreplayer.t.txt", + "table_tiflash_replica.txt", + "variables.toml", +} + +var expectedFilesInReplayerForCapture = []string{ + "config.toml", + "debug_trace/debug_trace0.json", + "explain/sql.txt", + "global_bindings.sql", + "meta.txt", + "schema/planreplayer.t.schema.txt", + "schema/schema_meta.txt", + "session_bindings.sql", + "sql/sql0.sql", + "sql_meta.toml", + "stats/planreplayer.t.json", + "statsMem/planreplayer.t.txt", + "table_tiflash_replica.txt", + "variables.toml", +} + +func prepareServerAndClientForTest(t *testing.T, store kv.Storage, dom *domain.Domain) (srv *server.Server, client *testserverclient.TestServerClient) { + driver := server.NewTiDBDriver(store) + client = testserverclient.NewTestServerClient() + + cfg := util.NewTestConfig() + cfg.Port = client.Port + cfg.Status.StatusPort = client.StatusPort + cfg.Status.ReportStatus = true + + srv, err := server.NewServer(cfg, driver) + srv.SetDomain(dom) + require.NoError(t, err) + go func() { + err := srv.Run() + require.NoError(t, err) + }() + + client.Port = testutil.GetPortFromTCPAddr(srv.ListenAddr()) + client.StatusPort = testutil.GetPortFromTCPAddr(srv.StatusListenerAddr()) + client.WaitUntilServerOnline() + return +} + +func TestDumpPlanReplayerAPI(t *testing.T) { + store := testkit.CreateMockStore(t) + dom, err := session.GetDomain(store) + require.NoError(t, err) + // 1. setup and prepare plan replayer files by manual command and capture + server, client := prepareServerAndClientForTest(t, store, dom) + defer server.Close() + + filename, fileNameFromCapture := prepareData4PlanReplayer(t, client, dom) + + // 2. check the contents of the plan replayer zip files. + + var filesInReplayer []string + collectFileNameAndAssertFileSize := func(f *zip.File) { + // collect file name + filesInReplayer = append(filesInReplayer, f.Name) + // except for {global,session}_bindings.sql and table_tiflash_replica.txt, the file should not be empty + if !strings.Contains(f.Name, "table_tiflash_replica.txt") && + !strings.Contains(f.Name, "bindings.sql") { + require.NotZero(t, f.UncompressedSize64, f.Name) + } + } + + // 2-1. check the plan replayer file from manual command + resp0, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", filename)) + require.NoError(t, err) + defer func() { + require.NoError(t, resp0.Body.Close()) + }() + body, err := io.ReadAll(resp0.Body) + require.NoError(t, err) + forEachFileInZipBytes(t, body, collectFileNameAndAssertFileSize) + slices.Sort(filesInReplayer) + require.Equal(t, expectedFilesInReplayer, filesInReplayer) + + // 2-2. check the plan replayer file from capture + resp1, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", fileNameFromCapture)) + require.NoError(t, err) + defer func() { + require.NoError(t, resp1.Body.Close()) + }() + body, err = io.ReadAll(resp1.Body) + require.NoError(t, err) + filesInReplayer = filesInReplayer[:0] + forEachFileInZipBytes(t, body, collectFileNameAndAssertFileSize) + slices.Sort(filesInReplayer) + require.Equal(t, expectedFilesInReplayerForCapture, filesInReplayer) + + // 3. check plan replayer load + + // 3-1. write the plan replayer file from manual command to a file + path := "/tmp/plan_replayer.zip" + fp, err := os.Create(path) + require.NoError(t, err) + require.NotNil(t, fp) + defer func() { + require.NoError(t, fp.Close()) + require.NoError(t, os.Remove(path)) + }() + + _, err = io.Copy(fp, bytes.NewReader(body)) + require.NoError(t, err) + require.NoError(t, fp.Sync()) + + // 3-2. connect to tidb and use PLAN REPLAYER LOAD to load this file + db, err := sql.Open("mysql", client.GetDSN(func(config *mysql.Config) { + config.AllowAllFiles = true + })) + require.NoError(t, err, "Error connecting") + defer func() { + err := db.Close() + require.NoError(t, err) + }() + tk := testkit.NewDBTestKit(t, db) + + tk.MustExec("use planReplayer") + tk.MustExec("drop table planReplayer.t") + tk.MustExec(`plan replayer load "/tmp/plan_replayer.zip"`) + + // 3-3. assert that the count and modify count in the stats is as expected + rows := tk.MustQuery("show stats_meta") + require.True(t, rows.Next(), "unexpected data") + var dbName, tableName string + var modifyCount, count int64 + var other interface{} + err = rows.Scan(&dbName, &tableName, &other, &other, &modifyCount, &count) + require.NoError(t, err) + require.Equal(t, "planReplayer", dbName) + require.Equal(t, "t", tableName) + require.Equal(t, int64(4), modifyCount) + require.Equal(t, int64(8), count) +} + +// prepareData4PlanReplayer trigger tidb to dump 2 plan replayer files, +// one by manual command, the other by capture, and return the filenames. +func prepareData4PlanReplayer(t *testing.T, client *testserverclient.TestServerClient, dom *domain.Domain) (string, string) { + h := dom.StatsHandle() + replayerHandle := dom.GetPlanReplayerHandle() + db, err := sql.Open("mysql", client.GetDSN()) + require.NoError(t, err, "Error connecting") + defer func() { + err := db.Close() + require.NoError(t, err) + }() + tk := testkit.NewDBTestKit(t, db) + + tk.MustExec("create database planReplayer") + tk.MustExec("use planReplayer") + tk.MustExec("create table t(a int)") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + tk.MustExec("insert into t values(1), (2), (3), (4)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t") + tk.MustExec("insert into t values(5), (6), (7), (8)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + rows := tk.MustQuery("plan replayer dump explain select * from t") + require.True(t, rows.Next(), "unexpected data") + var filename string + require.NoError(t, rows.Scan(&filename)) + require.NoError(t, rows.Close()) + rows = tk.MustQuery("select @@tidb_last_plan_replayer_token") + require.True(t, rows.Next(), "unexpected data") + var filename2 string + require.NoError(t, rows.Scan(&filename2)) + require.NoError(t, rows.Close()) + require.Equal(t, filename, filename2) + + tk.MustExec("plan replayer capture 'e5796985ccafe2f71126ed6c0ac939ffa015a8c0744a24b7aee6d587103fd2f7' '*'") + tk.MustQuery("select * from t") + task := replayerHandle.DrainTask() + require.NotNil(t, task) + worker := replayerHandle.GetWorker() + require.True(t, worker.HandleTask(task)) + rows = tk.MustQuery("select token from mysql.plan_replayer_status where length(sql_digest) > 0") + require.True(t, rows.Next(), "unexpected data") + var filename3 string + require.NoError(t, rows.Scan(&filename3)) + require.NoError(t, rows.Close()) + + return filename, filename3 +} + +func forEachFileInZipBytes(t *testing.T, b []byte, fn func(file *zip.File)) { + br := bytes.NewReader(b) + z, err := zip.NewReader(br, int64(len(b))) + require.NoError(t, err) + for _, f := range z.File { + fn(f) + } +} + +func fetchZipFromPlanReplayerAPI(t *testing.T, client *testserverclient.TestServerClient, filename string) *zip.Reader { + resp0, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", filename)) + require.NoError(t, err) + defer func() { + require.NoError(t, resp0.Body.Close()) + }() + body, err := io.ReadAll(resp0.Body) + require.NoError(t, err) + b := bytes.NewReader(body) + z, err := zip.NewReader(b, int64(len(body))) + require.NoError(t, err) + return z +} + +func getInfoFromPlanReplayerZip( + t *testing.T, + z *zip.Reader, +) ( + jsonTbls []*storage.JSONTable, + metas []map[string]string, + errMsgs []string, +) { + for _, zipFile := range z.File { + if strings.HasPrefix(zipFile.Name, "stats/") { + jsonTbl := &storage.JSONTable{} + r, err := zipFile.Open() + require.NoError(t, err) + //nolint: all_revive + defer func() { + require.NoError(t, r.Close()) + }() + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + require.NoError(t, err) + err = json.Unmarshal(buf.Bytes(), jsonTbl) + require.NoError(t, err) + + jsonTbls = append(jsonTbls, jsonTbl) + } else if zipFile.Name == "sql_meta.toml" { + meta := make(map[string]string) + r, err := zipFile.Open() + require.NoError(t, err) + //nolint: all_revive + defer func() { + require.NoError(t, r.Close()) + }() + _, err = toml.NewDecoder(r).Decode(&meta) + require.NoError(t, err) + + metas = append(metas, meta) + } else if zipFile.Name == "errors.txt" { + r, err := zipFile.Open() + require.NoError(t, err) + //nolint: all_revive + defer func() { + require.NoError(t, r.Close()) + }() + content, err := io.ReadAll(r) + require.NoError(t, err) + errMsgs = strings.Split(string(content), "\n") + } + } + return +} + +func TestDumpPlanReplayerAPIWithHistoryStats(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats", "return(true)")) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/sendHistoricalStats")) + }() + store := testkit.CreateMockStore(t) + dom, err := session.GetDomain(store) + require.NoError(t, err) + server, client := prepareServerAndClientForTest(t, store, dom) + defer server.Close() + statsHandle := dom.StatsHandle() + hsWorker := dom.GetHistoricalStatsWorker() + + // 1. prepare test data + + // time1, ts1: before everything starts + tk := testkit.NewTestKit(t, store) + time1 := time.Now() + ts1 := oracle.GoTimeToTS(time1) + + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int, index ia(a))") + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + + // 1-1. first insert and first analyze, trigger first dump history stats + tk.MustExec("insert into t value(1,1,1), (2,2,2), (3,3,3)") + tk.MustExec("analyze table t with 1 samplerate") + tblID := hsWorker.GetOneHistoricalStatsTable() + err = hsWorker.DumpHistoricalStats(tblID, statsHandle) + require.NoError(t, err) + + // time2, stats1: after first analyze + time2 := time.Now() + ts2 := oracle.GoTimeToTS(time2) + stats1, err := statsHandle.DumpStatsToJSON("test", tblInfo, nil, true) + require.NoError(t, err) + + // 1-2. second insert and second analyze, trigger second dump history stats + tk.MustExec("insert into t value(4,4,4), (5,5,5), (6,6,6)") + tk.MustExec("analyze table t with 1 samplerate") + tblID = hsWorker.GetOneHistoricalStatsTable() + err = hsWorker.DumpHistoricalStats(tblID, statsHandle) + require.NoError(t, err) + + // time3, stats2: after second analyze + time3 := time.Now() + ts3 := oracle.GoTimeToTS(time3) + stats2, err := statsHandle.DumpStatsToJSON("test", tblInfo, nil, true) + require.NoError(t, err) + + // 2. get the plan replayer and assert + + template := "plan replayer dump with stats as of timestamp '%s' explain %s" + query := "select * from t where a > 1" + + // 2-1. specify time1 to get the plan replayer + filename1 := tk.MustQuery( + fmt.Sprintf(template, strconv.FormatUint(ts1, 10), query), + ).Rows()[0][0].(string) + zip1 := fetchZipFromPlanReplayerAPI(t, client, filename1) + jsonTbls1, metas1, errMsg1 := getInfoFromPlanReplayerZip(t, zip1) + + // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above + require.Len(t, metas1, 1) + require.Contains(t, metas1[0], "historicalStatsTS") + tsInReplayerMeta1, err := strconv.ParseUint(metas1[0]["historicalStatsTS"], 10, 64) + require.NoError(t, err) + require.Equal(t, ts1, tsInReplayerMeta1) + + // the result is the same as stats2, and IsHistoricalStats is false. + require.Len(t, jsonTbls1, 1) + require.False(t, jsonTbls1[0].IsHistoricalStats) + require.Equal(t, jsonTbls1[0], stats2) + + // because we failed to get historical stats, there's an error message. + require.Equal(t, []string{"Historical stats for test.t are unavailable, fallback to latest stats", ""}, errMsg1) + + // 2-2. specify time2 to get the plan replayer + filename2 := tk.MustQuery( + fmt.Sprintf(template, time2.Format("2006-01-02 15:04:05.000000"), query), + ).Rows()[0][0].(string) + zip2 := fetchZipFromPlanReplayerAPI(t, client, filename2) + jsonTbls2, metas2, errMsg2 := getInfoFromPlanReplayerZip(t, zip2) + + // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above + require.Len(t, metas2, 1) + require.Contains(t, metas2[0], "historicalStatsTS") + tsInReplayerMeta2, err := strconv.ParseUint(metas2[0]["historicalStatsTS"], 10, 64) + require.NoError(t, err) + require.Equal(t, ts2, tsInReplayerMeta2) + + // the result is the same as stats1, and IsHistoricalStats is true. + require.Len(t, jsonTbls2, 1) + require.True(t, jsonTbls2[0].IsHistoricalStats) + jsonTbls2[0].IsHistoricalStats = false + require.Equal(t, jsonTbls2[0], stats1) + + // succeeded to get historical stats, there should be no error message. + require.Empty(t, errMsg2) + + // 2-3. specify time3 to get the plan replayer + filename3 := tk.MustQuery( + fmt.Sprintf(template, time3.Format("2006-01-02T15:04:05.000000Z07:00"), query), + ).Rows()[0][0].(string) + zip3 := fetchZipFromPlanReplayerAPI(t, client, filename3) + jsonTbls3, metas3, errMsg3 := getInfoFromPlanReplayerZip(t, zip3) + + // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above + require.Len(t, metas3, 1) + require.Contains(t, metas3[0], "historicalStatsTS") + tsInReplayerMeta3, err := strconv.ParseUint(metas3[0]["historicalStatsTS"], 10, 64) + require.NoError(t, err) + require.Equal(t, ts3, tsInReplayerMeta3) + + // the result is the same as stats2, and IsHistoricalStats is true. + require.Len(t, jsonTbls3, 1) + require.True(t, jsonTbls3[0].IsHistoricalStats) + jsonTbls3[0].IsHistoricalStats = false + require.Equal(t, jsonTbls3[0], stats2) + + // succeeded to get historical stats, there should be no error message. + require.Empty(t, errMsg3) + + // 3. remove the plan replayer files generated during the test + gcHandler := dom.GetDumpFileGCChecker() + gcHandler.GCDumpFiles(0, 0) +} diff --git a/server/handler/optimizor/statistics_handler.go b/pkg/server/handler/optimizor/statistics_handler.go similarity index 91% rename from server/handler/optimizor/statistics_handler.go rename to pkg/server/handler/optimizor/statistics_handler.go index f56056f510134..1c7b56f14aaa9 100644 --- a/server/handler/optimizor/statistics_handler.go +++ b/pkg/server/handler/optimizor/statistics_handler.go @@ -21,15 +21,15 @@ import ( "time" "github.com/gorilla/mux" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" ) diff --git a/server/handler/optimizor/statistics_handler_test.go b/pkg/server/handler/optimizor/statistics_handler_test.go similarity index 94% rename from server/handler/optimizor/statistics_handler_test.go rename to pkg/server/handler/optimizor/statistics_handler_test.go index cc09025f8c8e2..498c3febb0668 100644 --- a/server/handler/optimizor/statistics_handler_test.go +++ b/pkg/server/handler/optimizor/statistics_handler_test.go @@ -25,16 +25,16 @@ import ( "github.com/go-sql-driver/mysql" "github.com/gorilla/mux" - "github.com/pingcap/tidb/parser/model" - server2 "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/handler/optimizor" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/statistics/handle/globalstats" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/model" + server2 "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/handler/optimizor" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/statistics/handle/globalstats" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/handler/tests/BUILD.bazel b/pkg/server/handler/tests/BUILD.bazel new file mode 100644 index 0000000000000..f992b3170f853 --- /dev/null +++ b/pkg/server/handler/tests/BUILD.bazel @@ -0,0 +1,60 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tests_test", + timeout = "short", + srcs = [ + "http_handler_serial_test.go", + "http_handler_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 36, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/server", + "//pkg/server/handler", + "//pkg/server/handler/optimizor", + "//pkg/server/handler/tikvhandler", + "//pkg/server/internal/testserverclient", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/store/helper", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/deadlockhistory", + "//pkg/util/rowcodec", + "//pkg/util/topsql/state", + "//pkg/util/versioninfo", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/server/handler/tests/http_handler_serial_test.go b/pkg/server/handler/tests/http_handler_serial_test.go similarity index 94% rename from server/handler/tests/http_handler_serial_test.go rename to pkg/server/handler/tests/http_handler_serial_test.go index 3ba23889c75bc..3d54b87d0846e 100644 --- a/server/handler/tests/http_handler_serial_test.go +++ b/pkg/server/handler/tests/http_handler_serial_test.go @@ -33,18 +33,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/server/handler/tikvhandler" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/config" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/server/handler/tikvhandler" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/versioninfo" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -249,8 +249,10 @@ func TestRegionsFromMeta(t *testing.T) { } // test no panic - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/errGetRegionByIDEmpty", `return(true)`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/errGetRegionByIDEmpty")) }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/errGetRegionByIDEmpty", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/errGetRegionByIDEmpty")) + }() resp1, err := ts.FetchStatus("/regions/meta") require.NoError(t, err) defer func() { require.NoError(t, resp1.Body.Close()) }() @@ -298,9 +300,9 @@ func TestTiFlashReplica(t *testing.T) { require.NoError(t, resp.Body.Close()) require.Equal(t, 0, len(data)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) }() dbt.MustExec("use tidb") @@ -451,15 +453,15 @@ func TestFailpointHandler(t *testing.T) { ts.stopServer(t) // enable failpoint integration and start server - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/enableTestAPI", "return")) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/enableTestAPI")) }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/enableTestAPI", "return")) + defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/enableTestAPI")) }() ts.startServer(t) resp, err = ts.FetchStatus("/fail/") require.NoError(t, err) require.Equal(t, http.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) require.NoError(t, err) - require.True(t, strings.Contains(string(b), "github.com/pingcap/tidb/server/enableTestAPI=return")) + require.True(t, strings.Contains(string(b), "github.com/pingcap/tidb/pkg/server/enableTestAPI=return")) require.NoError(t, resp.Body.Close()) } @@ -476,8 +478,8 @@ func TestTestHandler(t *testing.T) { ts.stopServer(t) // enable failpoint integration and start server - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/enableTestAPI", "return")) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/enableTestAPI")) }() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/enableTestAPI", "return")) + defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/enableTestAPI")) }() ts.startServer(t) resp, err = ts.FetchStatus("/test/gc/gc") diff --git a/server/handler/tests/http_handler_test.go b/pkg/server/handler/tests/http_handler_test.go similarity index 96% rename from server/handler/tests/http_handler_test.go rename to pkg/server/handler/tests/http_handler_test.go index bbc5973b3351b..002022b60a881 100644 --- a/server/handler/tests/http_handler_test.go +++ b/pkg/server/handler/tests/http_handler_test.go @@ -35,35 +35,35 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - server2 "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/server/handler/optimizor" - "github.com/pingcap/tidb/server/handler/tikvhandler" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/core" + server2 "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/server/handler/optimizor" + "github.com/pingcap/tidb/pkg/server/handler/tikvhandler" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" @@ -1431,13 +1431,13 @@ func testUpgradeShow(t *testing.T, ts *basicHTTPHandlerTestSuite) { // test upgrade show for 1 server checkUpgradeShow(1, 100, 0) // test upgrade show for 3 servers - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) - defer failpoint.Disable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo") // test upgrade show again with 3 different version servers checkUpgradeShow(3, 33, 3) // test upgrade show again with 3 servers of the same version mockedAllServerInfos["s2"].Version = mockedAllServerInfos["s0"].Version mockedAllServerInfos["s2"].GitHash = mockedAllServerInfos["s0"].GitHash - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockGetAllServerInfo", makeFailpointRes(mockedAllServerInfos))) checkUpgradeShow(3, 100, 0) } diff --git a/pkg/server/handler/tests/main_test.go b/pkg/server/handler/tests/main_test.go new file mode 100644 index 0000000000000..577f97de761d8 --- /dev/null +++ b/pkg/server/handler/tests/main_test.go @@ -0,0 +1,73 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + server.RunInGoTest = true + testsetup.SetupForCommonTest() + topsqlstate.EnableTopSQL() + unistore.CheckResourceTagForTopSQLInGoTest = true + + // AsyncCommit will make DDL wait 2.5s before changing to the next state. + // Set schema lease to avoid it from making CI slow. + session.SetSchemaLease(0) + + tikv.EnableFailpoints() + + metrics.RegisterMetrics() + + // sanity check: the global config should not be changed by other pkg init function. + // see also https://github.com/pingcap/tidb/issues/22162 + defaultConfig := config.NewConfig() + globalConfig := config.GetGlobalConfig() + if !reflect.DeepEqual(defaultConfig, globalConfig) { + _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") + _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("time.Sleep"), + goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/server.NewServer.func1"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/server/handler/tikv_handler.go b/pkg/server/handler/tikv_handler.go new file mode 100644 index 0000000000000..5dd0c8af3f4c4 --- /dev/null +++ b/pkg/server/handler/tikv_handler.go @@ -0,0 +1,278 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package handler + +import ( + "context" + "encoding/hex" + "fmt" + "net/url" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/tikv/client-go/v2/tikv" +) + +// TikvHandlerTool is a tool to handle TiKV data. +type TikvHandlerTool struct { + helper.Helper +} + +// NewTikvHandlerTool creates a new TikvHandlerTool. +func NewTikvHandlerTool(store helper.Storage) *TikvHandlerTool { + return &TikvHandlerTool{Helper: *helper.NewHelper(store)} +} + +type mvccKV struct { + Key string `json:"key"` + RegionID uint64 `json:"region_id"` + Value *kvrpcpb.MvccGetByKeyResponse `json:"value"` +} + +// GetRegionIDByKey gets the region id by the key. +func (t *TikvHandlerTool) GetRegionIDByKey(encodedKey []byte) (uint64, error) { + keyLocation, err := t.RegionCache.LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) + if err != nil { + return 0, derr.ToTiDBErr(err) + } + return keyLocation.Region.GetID(), nil +} + +// GetHandle gets the handle of the record. +func (t *TikvHandlerTool) GetHandle(tb table.PhysicalTable, params map[string]string, values url.Values) (kv.Handle, error) { + var handle kv.Handle + if intHandleStr, ok := params[Handle]; ok { + if tb.Meta().IsCommonHandle { + return nil, errors.BadRequestf("For clustered index tables, please use query strings to specify the column values.") + } + intHandle, err := strconv.ParseInt(intHandleStr, 0, 64) + if err != nil { + return nil, errors.Trace(err) + } + handle = kv.IntHandle(intHandle) + } else { + tblInfo := tb.Meta() + pkIdx := tables.FindPrimaryIndex(tblInfo) + if pkIdx == nil || !tblInfo.IsCommonHandle { + return nil, errors.BadRequestf("Clustered common handle not found.") + } + cols := tblInfo.Cols() + pkCols := make([]*model.ColumnInfo, 0, len(pkIdx.Columns)) + for _, idxCol := range pkIdx.Columns { + pkCols = append(pkCols, cols[idxCol.Offset]) + } + sc := stmtctx.NewStmtCtx() + sc.SetTimeZone(time.UTC) + pkDts, err := t.formValue2DatumRow(sc, values, pkCols) + if err != nil { + return nil, errors.Trace(err) + } + tablecodec.TruncateIndexValues(tblInfo, pkIdx, pkDts) + var handleBytes []byte + handleBytes, err = codec.EncodeKey(sc, nil, pkDts...) + if err != nil { + return nil, errors.Trace(err) + } + handle, err = kv.NewCommonHandle(handleBytes) + if err != nil { + return nil, errors.Trace(err) + } + } + return handle, nil +} + +// GetMvccByIdxValue gets the mvcc by the index value. +func (t *TikvHandlerTool) GetMvccByIdxValue(idx table.Index, values url.Values, idxCols []*model.ColumnInfo, handle kv.Handle) ([]*helper.MvccKV, error) { + // HTTP request is not a database session, set timezone to UTC directly here. + // See https://github.com/pingcap/tidb/blob/master/docs/tidb_http_api.md for more details. + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + idxRow, err := t.formValue2DatumRow(sc, values, idxCols) + if err != nil { + return nil, errors.Trace(err) + } + encodedKey, _, err := idx.GenIndexKey(sc, idxRow, handle, nil) + if err != nil { + return nil, errors.Trace(err) + } + data, err := t.GetMvccByEncodedKey(encodedKey) + if err != nil { + return nil, err + } + regionID, err := t.GetRegionIDByKey(encodedKey) + if err != nil { + return nil, err + } + idxData := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), RegionID: regionID, Value: data} + tablecodec.IndexKey2TempIndexKey(encodedKey) + data, err = t.GetMvccByEncodedKey(encodedKey) + if err != nil { + return nil, err + } + regionID, err = t.GetRegionIDByKey(encodedKey) + if err != nil { + return nil, err + } + tempIdxData := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), RegionID: regionID, Value: data} + return append([]*helper.MvccKV{}, idxData, tempIdxData), err +} + +// formValue2DatumRow converts URL query string to a Datum Row. +func (*TikvHandlerTool) formValue2DatumRow(sc *stmtctx.StatementContext, values url.Values, idxCols []*model.ColumnInfo) ([]types.Datum, error) { + data := make([]types.Datum, len(idxCols)) + for i, col := range idxCols { + colName := col.Name.String() + vals, ok := values[colName] + if !ok { + return nil, errors.BadRequestf("Missing value for index column %s.", colName) + } + + switch len(vals) { + case 0: + data[i].SetNull() + case 1: + bDatum := types.NewStringDatum(vals[0]) + cDatum, err := bDatum.ConvertTo(sc, &col.FieldType) + if err != nil { + return nil, errors.Trace(err) + } + data[i] = cDatum + default: + return nil, errors.BadRequestf("Invalid query form for column '%s', it's values are %v."+ + " Column value should be unique for one index record.", colName, vals) + } + } + return data, nil +} + +// GetTableID gets the table ID by the database name and table name. +func (t *TikvHandlerTool) GetTableID(dbName, tableName string) (int64, error) { + tbl, err := t.GetTable(dbName, tableName) + if err != nil { + return 0, errors.Trace(err) + } + return tbl.GetPhysicalID(), nil +} + +// GetTable gets the table by the database name and table name. +func (t *TikvHandlerTool) GetTable(dbName, tableName string) (table.PhysicalTable, error) { + schema, err := t.Schema() + if err != nil { + return nil, errors.Trace(err) + } + tableName, partitionName := ExtractTableAndPartitionName(tableName) + tableVal, err := schema.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName)) + if err != nil { + return nil, errors.Trace(err) + } + return t.GetPartition(tableVal, partitionName) +} + +// GetPartition gets the partition by the table and partition name. +func (*TikvHandlerTool) GetPartition(tableVal table.Table, partitionName string) (table.PhysicalTable, error) { + if pt, ok := tableVal.(table.PartitionedTable); ok { + if partitionName == "" { + return tableVal.(table.PhysicalTable), errors.New("work on partitioned table, please specify the table name like this: table(partition)") + } + tblInfo := pt.Meta() + pid, err := tables.FindPartitionByName(tblInfo, partitionName) + if err != nil { + return nil, errors.Trace(err) + } + return pt.GetPartition(pid), nil + } + if partitionName != "" { + return nil, fmt.Errorf("%s is not a partitionted table", tableVal.Meta().Name) + } + return tableVal.(table.PhysicalTable), nil +} + +// Schema gets the schema. +func (t *TikvHandlerTool) Schema() (infoschema.InfoSchema, error) { + dom, err := session.GetDomain(t.Store) + if err != nil { + return nil, err + } + return dom.InfoSchema(), nil +} + +// HandleMvccGetByHex handles the request of getting mvcc by hex encoded key. +func (t *TikvHandlerTool) HandleMvccGetByHex(params map[string]string) (*mvccKV, error) { + encodedKey, err := hex.DecodeString(params[HexKey]) + if err != nil { + return nil, errors.Trace(err) + } + data, err := t.GetMvccByEncodedKey(encodedKey) + if err != nil { + return nil, errors.Trace(err) + } + regionID, err := t.GetRegionIDByKey(encodedKey) + if err != nil { + return nil, err + } + return &mvccKV{Key: strings.ToUpper(params[HexKey]), Value: data, RegionID: regionID}, nil +} + +// RegionMeta contains a region's peer detail +type RegionMeta struct { + ID uint64 `json:"region_id"` + Leader *metapb.Peer `json:"leader"` + Peers []*metapb.Peer `json:"peers"` + RegionEpoch *metapb.RegionEpoch `json:"region_epoch"` +} + +// GetRegionsMeta gets regions meta by regionIDs +func (t *TikvHandlerTool) GetRegionsMeta(regionIDs []uint64) ([]RegionMeta, error) { + regions := make([]RegionMeta, len(regionIDs)) + for i, regionID := range regionIDs { + region, err := t.RegionCache.PDClient().GetRegionByID(context.TODO(), regionID) + if err != nil { + return nil, errors.Trace(err) + } + + failpoint.Inject("errGetRegionByIDEmpty", func(val failpoint.Value) { + if val.(bool) { + region.Meta = nil + } + }) + + if region.Meta == nil { + return nil, errors.Errorf("region not found for regionID %q", regionID) + } + regions[i] = RegionMeta{ + ID: regionID, + Leader: region.Leader, + Peers: region.Meta.Peers, + RegionEpoch: region.Meta.RegionEpoch, + } + } + return regions, nil +} diff --git a/pkg/server/handler/tikvhandler/BUILD.bazel b/pkg/server/handler/tikvhandler/BUILD.bazel new file mode 100644 index 0000000000000..b5dffea18f4e5 --- /dev/null +++ b/pkg/server/handler/tikvhandler/BUILD.bazel @@ -0,0 +1,44 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tikvhandler", + srcs = ["tikv_handler.go"], + importpath = "github.com/pingcap/tidb/pkg/server/handler/tikvhandler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/executor", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/server/handler", + "//pkg/session", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/variable", + "//pkg/store/gcworker", + "//pkg/store/helper", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/deadlockhistory", + "//pkg/util/gcutil", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/pdapi", + "//pkg/util/sqlexec", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/server/handler/tikvhandler/tikv_handler.go b/pkg/server/handler/tikvhandler/tikv_handler.go new file mode 100644 index 0000000000000..3cfe18cc2e7e0 --- /dev/null +++ b/pkg/server/handler/tikvhandler/tikv_handler.go @@ -0,0 +1,2023 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package tikvhandler + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "math" + "net/http" + "net/url" + "runtime" + "strconv" + "strings" + "sync/atomic" + "time" + + "github.com/gorilla/mux" + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" + "github.com/pingcap/tidb/pkg/util/gcutil" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" +) + +func writeError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte(err.Error())) + terror.Log(errors.Trace(err)) +} + +func writeData(w http.ResponseWriter, data interface{}) { + js, err := json.MarshalIndent(data, "", " ") + if err != nil { + writeError(w, err) + return + } + // write response + w.Header().Set(handler.HeaderContentType, handler.ContentTypeJSON) + w.WriteHeader(http.StatusOK) + _, err = w.Write(js) + terror.Log(errors.Trace(err)) +} + +// SettingsHandler is the handler for list tidb server settings. +type SettingsHandler struct { + *handler.TikvHandlerTool +} + +// NewSettingsHandler creates a new SettingsHandler. +func NewSettingsHandler(tool *handler.TikvHandlerTool) *SettingsHandler { + return &SettingsHandler{tool} +} + +// BinlogRecover is used to recover binlog service. +// When config binlog IgnoreError, binlog service will stop after meeting the first error. +// It can be recovered using HTTP API. +type BinlogRecover struct{} + +// SchemaHandler is the handler for list database or table schemas. +type SchemaHandler struct { + *handler.TikvHandlerTool +} + +// NewSchemaHandler creates a new SchemaHandler. +func NewSchemaHandler(tool *handler.TikvHandlerTool) *SchemaHandler { + return &SchemaHandler{tool} +} + +// SchemaStorageHandler is the handler for list database or table schemas. +type SchemaStorageHandler struct { + *handler.TikvHandlerTool +} + +// NewSchemaStorageHandler creates a new SchemaStorageHandler. +func NewSchemaStorageHandler(tool *handler.TikvHandlerTool) *SchemaStorageHandler { + return &SchemaStorageHandler{tool} +} + +// DBTableHandler is the handler for list table's regions. +type DBTableHandler struct { + *handler.TikvHandlerTool +} + +// NewDBTableHandler creates a new DBTableHandler. +func NewDBTableHandler(tool *handler.TikvHandlerTool) *DBTableHandler { + return &DBTableHandler{tool} +} + +// FlashReplicaHandler is the handler for flash replica. +type FlashReplicaHandler struct { + *handler.TikvHandlerTool +} + +// NewFlashReplicaHandler creates a new FlashReplicaHandler. +func NewFlashReplicaHandler(tool *handler.TikvHandlerTool) *FlashReplicaHandler { + return &FlashReplicaHandler{tool} +} + +// RegionHandler is the common field for http handler. It contains +// some common functions for all handlers. +type RegionHandler struct { + *handler.TikvHandlerTool +} + +// NewRegionHandler creates a new RegionHandler. +func NewRegionHandler(tool *handler.TikvHandlerTool) *RegionHandler { + return &RegionHandler{tool} +} + +// TableHandler is the handler for list table's regions. +type TableHandler struct { + *handler.TikvHandlerTool + op string +} + +// NewTableHandler creates a new TableHandler. +func NewTableHandler(tool *handler.TikvHandlerTool, op string) *TableHandler { + return &TableHandler{tool, op} +} + +// DDLHistoryJobHandler is the handler for list job history. +type DDLHistoryJobHandler struct { + *handler.TikvHandlerTool +} + +// NewDDLHistoryJobHandler creates a new DDLHistoryJobHandler. +func NewDDLHistoryJobHandler(tool *handler.TikvHandlerTool) *DDLHistoryJobHandler { + return &DDLHistoryJobHandler{tool} +} + +// DDLResignOwnerHandler is the handler for resigning ddl owner. +type DDLResignOwnerHandler struct { + store kv.Storage +} + +// NewDDLResignOwnerHandler creates a new DDLResignOwnerHandler. +func NewDDLResignOwnerHandler(store kv.Storage) *DDLResignOwnerHandler { + return &DDLResignOwnerHandler{store} +} + +// ServerInfoHandler is the handler for getting statistics. +type ServerInfoHandler struct { + *handler.TikvHandlerTool +} + +// NewServerInfoHandler creates a new ServerInfoHandler. +func NewServerInfoHandler(tool *handler.TikvHandlerTool) *ServerInfoHandler { + return &ServerInfoHandler{tool} +} + +// AllServerInfoHandler is the handler for getting all servers information. +type AllServerInfoHandler struct { + *handler.TikvHandlerTool +} + +// NewAllServerInfoHandler creates a new AllServerInfoHandler. +func NewAllServerInfoHandler(tool *handler.TikvHandlerTool) *AllServerInfoHandler { + return &AllServerInfoHandler{tool} +} + +// ProfileHandler is the handler for getting profile. +type ProfileHandler struct { + *handler.TikvHandlerTool +} + +// NewProfileHandler creates a new ProfileHandler. +func NewProfileHandler(tool *handler.TikvHandlerTool) *ProfileHandler { + return &ProfileHandler{tool} +} + +// DDLHookHandler is the handler for use pre-defined ddl callback. +// It's convenient to provide some APIs for integration tests. +type DDLHookHandler struct { + store kv.Storage +} + +// NewDDLHookHandler creates a new DDLHookHandler. +func NewDDLHookHandler(store kv.Storage) *DDLHookHandler { + return &DDLHookHandler{store} +} + +// ValueHandler is the handler for get value. +type ValueHandler struct { +} + +// LabelHandler is the handler for set labels +type LabelHandler struct{} + +const ( + // OpTableRegions is the operation for getting regions of a table. + OpTableRegions = "regions" + // OpTableRanges is the operation for getting ranges of a table. + OpTableRanges = "ranges" + // OpTableDiskUsage is the operation for getting disk usage of a table. + OpTableDiskUsage = "disk-usage" + // OpTableScatter is the operation for scattering a table. + OpTableScatter = "scatter-table" + // OpStopTableScatter is the operation for stopping scattering a table. + OpStopTableScatter = "stop-scatter-table" +) + +// MvccTxnHandler is the handler for txn debugger. +type MvccTxnHandler struct { + *handler.TikvHandlerTool + op string +} + +// NewMvccTxnHandler creates a new MvccTxnHandler. +func NewMvccTxnHandler(tool *handler.TikvHandlerTool, op string) *MvccTxnHandler { + return &MvccTxnHandler{tool, op} +} + +const ( + // OpMvccGetByHex is the operation for getting mvcc value by hex format. + OpMvccGetByHex = "hex" + // OpMvccGetByKey is the operation for getting mvcc value by key. + OpMvccGetByKey = "key" + // OpMvccGetByIdx is the operation for getting mvcc value by idx. + OpMvccGetByIdx = "idx" + // OpMvccGetByTxn is the operation for getting mvcc value by txn. + OpMvccGetByTxn = "txn" +) + +// ServeHTTP handles request of list a database or table's schemas. +func (ValueHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // parse params + params := mux.Vars(req) + + colID, err := strconv.ParseInt(params[handler.ColumnID], 0, 64) + if err != nil { + writeError(w, err) + return + } + colTp, err := strconv.ParseInt(params[handler.ColumnTp], 0, 64) + if err != nil { + writeError(w, err) + return + } + colFlag, err := strconv.ParseUint(params[handler.ColumnFlag], 0, 64) + if err != nil { + writeError(w, err) + return + } + colLen, err := strconv.ParseInt(params[handler.ColumnLen], 0, 64) + if err != nil { + writeError(w, err) + return + } + + // Get the unchanged binary. + if req.URL == nil { + err = errors.BadRequestf("Invalid URL") + writeError(w, err) + return + } + values := make(url.Values) + + err = parseQuery(req.URL.RawQuery, values, false) + if err != nil { + writeError(w, err) + return + } + if len(values[handler.RowBin]) != 1 { + err = errors.BadRequestf("Invalid Query:%v", values[handler.RowBin]) + writeError(w, err) + return + } + bin := values[handler.RowBin][0] + valData, err := base64.StdEncoding.DecodeString(bin) + if err != nil { + writeError(w, err) + return + } + // Construct field type. + defaultDecimal := 6 + ft := types.NewFieldTypeBuilder().SetType(byte(colTp)).SetFlag(uint(colFlag)).SetFlen(int(colLen)).SetDecimal(defaultDecimal).BuildP() + // Decode a column. + m := make(map[int64]*types.FieldType, 1) + m[colID] = ft + loc := time.UTC + vals, err := tablecodec.DecodeRowToDatumMap(valData, m, loc) + if err != nil { + writeError(w, err) + return + } + + v := vals[colID] + val, err := v.ToString() + if err != nil { + writeError(w, err) + return + } + writeData(w, val) +} + +// TableRegions is the response data for list table's regions. +// It contains regions list for record and indices. +type TableRegions struct { + TableName string `json:"name"` + TableID int64 `json:"id"` + RecordRegions []handler.RegionMeta `json:"record_regions"` + Indices []IndexRegions `json:"indices"` +} + +// RangeDetail contains detail information about a particular range +type RangeDetail struct { + StartKey []byte `json:"start_key"` + EndKey []byte `json:"end_key"` + StartKeyHex string `json:"start_key_hex"` + EndKeyHex string `json:"end_key_hex"` +} + +func createRangeDetail(start, end []byte) RangeDetail { + return RangeDetail{ + StartKey: start, + EndKey: end, + StartKeyHex: hex.EncodeToString(start), + EndKeyHex: hex.EncodeToString(end), + } +} + +// TableRanges is the response data for list table's ranges. +// It contains ranges list for record and indices as well as the whole table. +type TableRanges struct { + TableName string `json:"name"` + TableID int64 `json:"id"` + Range RangeDetail `json:"table"` + Record RangeDetail `json:"record"` + Index RangeDetail `json:"index"` + Indices map[string]RangeDetail `json:"indices,omitempty"` +} + +// IndexRegions is the region info for one index. +type IndexRegions struct { + Name string `json:"name"` + ID int64 `json:"id"` + Regions []handler.RegionMeta `json:"regions"` +} + +// RegionDetail is the response data for get region by ID +// it includes indices and records detail in current region. +type RegionDetail struct { + RangeDetail `json:",inline"` + RegionID uint64 `json:"region_id"` + Frames []*helper.FrameItem `json:"frames"` +} + +// addTableInRange insert a table into RegionDetail +// with index's id or record in the range if r. +func (rt *RegionDetail) addTableInRange(dbName string, curTable *model.TableInfo, r *helper.RegionFrameRange) { + tName := curTable.Name.String() + tID := curTable.ID + pi := curTable.GetPartitionInfo() + isCommonHandle := curTable.IsCommonHandle + for _, index := range curTable.Indices { + if index.Primary && isCommonHandle { + continue + } + if pi != nil { + for _, def := range pi.Definitions { + if f := r.GetIndexFrame(def.ID, index.ID, dbName, fmt.Sprintf("%s(%s)", tName, def.Name.O), index.Name.String()); f != nil { + rt.Frames = append(rt.Frames, f) + } + } + } else if f := r.GetIndexFrame(tID, index.ID, dbName, tName, index.Name.String()); f != nil { + rt.Frames = append(rt.Frames, f) + } + } + + if pi != nil { + for _, def := range pi.Definitions { + if f := r.GetRecordFrame(def.ID, dbName, fmt.Sprintf("%s(%s)", tName, def.Name.O), isCommonHandle); f != nil { + rt.Frames = append(rt.Frames, f) + } + } + } else if f := r.GetRecordFrame(tID, dbName, tName, isCommonHandle); f != nil { + rt.Frames = append(rt.Frames, f) + } +} + +// FrameItem includes a index's or record's meta data with table's info. +type FrameItem struct { + DBName string `json:"db_name"` + TableName string `json:"table_name"` + TableID int64 `json:"table_id"` + IsRecord bool `json:"is_record"` + RecordID int64 `json:"record_id,omitempty"` + IndexName string `json:"index_name,omitempty"` + IndexID int64 `json:"index_id,omitempty"` + IndexValues []string `json:"index_values,omitempty"` +} + +// ServeHTTP handles request of list tidb server settings. +func (h SettingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method == "POST" { + err := req.ParseForm() + if err != nil { + writeError(w, err) + return + } + if levelStr := req.Form.Get("log_level"); levelStr != "" { + err1 := logutil.SetLevel(levelStr) + if err1 != nil { + writeError(w, err1) + return + } + + config.GetGlobalConfig().Log.Level = levelStr + } + if generalLog := req.Form.Get("tidb_general_log"); generalLog != "" { + switch generalLog { + case "0": + variable.ProcessGeneralLog.Store(false) + case "1": + variable.ProcessGeneralLog.Store(true) + default: + writeError(w, errors.New("illegal argument")) + return + } + } + if asyncCommit := req.Form.Get("tidb_enable_async_commit"); asyncCommit != "" { + s, err := session.CreateSession(h.Store) + if err != nil { + writeError(w, err) + return + } + defer s.Close() + + switch asyncCommit { + case "0": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableAsyncCommit, variable.Off) + case "1": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableAsyncCommit, variable.On) + default: + writeError(w, errors.New("illegal argument")) + return + } + if err != nil { + writeError(w, err) + return + } + } + if onePC := req.Form.Get("tidb_enable_1pc"); onePC != "" { + s, err := session.CreateSession(h.Store) + if err != nil { + writeError(w, err) + return + } + defer s.Close() + + switch onePC { + case "0": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnable1PC, variable.Off) + case "1": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnable1PC, variable.On) + default: + writeError(w, errors.New("illegal argument")) + return + } + if err != nil { + writeError(w, err) + return + } + } + if ddlSlowThreshold := req.Form.Get("ddl_slow_threshold"); ddlSlowThreshold != "" { + threshold, err1 := strconv.Atoi(ddlSlowThreshold) + if err1 != nil { + writeError(w, err1) + return + } + if threshold > 0 { + atomic.StoreUint32(&variable.DDLSlowOprThreshold, uint32(threshold)) + } + } + if checkMb4ValueInUtf8 := req.Form.Get("check_mb4_value_in_utf8"); checkMb4ValueInUtf8 != "" { + switch checkMb4ValueInUtf8 { + case "0": + config.GetGlobalConfig().Instance.CheckMb4ValueInUTF8.Store(false) + case "1": + config.GetGlobalConfig().Instance.CheckMb4ValueInUTF8.Store(true) + default: + writeError(w, errors.New("illegal argument")) + return + } + } + if deadlockHistoryCapacity := req.Form.Get("deadlock_history_capacity"); deadlockHistoryCapacity != "" { + capacity, err := strconv.Atoi(deadlockHistoryCapacity) + if err != nil { + writeError(w, errors.New("illegal argument")) + return + } else if capacity < 0 || capacity > 10000 { + writeError(w, errors.New("deadlock_history_capacity out of range, should be in 0 to 10000")) + return + } + cfg := config.GetGlobalConfig() + cfg.PessimisticTxn.DeadlockHistoryCapacity = uint(capacity) + config.StoreGlobalConfig(cfg) + deadlockhistory.GlobalDeadlockHistory.Resize(uint(capacity)) + } + if deadlockCollectRetryable := req.Form.Get("deadlock_history_collect_retryable"); deadlockCollectRetryable != "" { + collectRetryable, err := strconv.ParseBool(deadlockCollectRetryable) + if err != nil { + writeError(w, errors.New("illegal argument")) + return + } + cfg := config.GetGlobalConfig() + cfg.PessimisticTxn.DeadlockHistoryCollectRetryable = collectRetryable + config.StoreGlobalConfig(cfg) + } + if mutationChecker := req.Form.Get("tidb_enable_mutation_checker"); mutationChecker != "" { + s, err := session.CreateSession(h.Store) + if err != nil { + writeError(w, err) + return + } + defer s.Close() + + switch mutationChecker { + case "0": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableMutationChecker, variable.Off) + case "1": + err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableMutationChecker, variable.On) + default: + writeError(w, errors.New("illegal argument")) + return + } + if err != nil { + writeError(w, err) + return + } + } + if transactionSummaryCapacity := req.Form.Get("transaction_summary_capacity"); transactionSummaryCapacity != "" { + capacity, err := strconv.Atoi(transactionSummaryCapacity) + if err != nil { + writeError(w, errors.New("illegal argument")) + return + } else if capacity < 0 || capacity > 5000 { + writeError(w, errors.New("transaction_summary_capacity out of range, should be in 0 to 5000")) + return + } + cfg := config.GetGlobalConfig() + cfg.TrxSummary.TransactionSummaryCapacity = uint(capacity) + config.StoreGlobalConfig(cfg) + txninfo.Recorder.ResizeSummaries(uint(capacity)) + } + if transactionIDDigestMinDuration := req.Form.Get("transaction_id_digest_min_duration"); transactionIDDigestMinDuration != "" { + duration, err := strconv.Atoi(transactionIDDigestMinDuration) + if err != nil { + writeError(w, errors.New("illegal argument")) + return + } else if duration < 0 || duration > 2147483647 { + writeError(w, errors.New("transaction_id_digest_min_duration out of range, should be in 0 to 2147483647")) + return + } + cfg := config.GetGlobalConfig() + cfg.TrxSummary.TransactionIDDigestMinDuration = uint(duration) + config.StoreGlobalConfig(cfg) + txninfo.Recorder.SetMinDuration(time.Duration(duration) * time.Millisecond) + } + } else { + writeData(w, config.GetGlobalConfig()) + } +} + +// ServeHTTP recovers binlog service. +func (BinlogRecover) ServeHTTP(w http.ResponseWriter, req *http.Request) { + op := req.FormValue(handler.Operation) + switch op { + case "reset": + binloginfo.ResetSkippedCommitterCounter() + case "nowait": + err := binloginfo.DisableSkipBinlogFlag() + if err != nil { + writeError(w, err) + return + } + case "status": + default: + sec, err := strconv.ParseInt(req.FormValue(handler.Seconds), 10, 64) + if sec <= 0 || err != nil { + sec = 1800 + } + err = binloginfo.DisableSkipBinlogFlag() + if err != nil { + writeError(w, err) + return + } + timeout := time.Duration(sec) * time.Second + err = binloginfo.WaitBinlogRecover(timeout) + if err != nil { + writeError(w, err) + return + } + } + writeData(w, binloginfo.GetBinlogStatus()) +} + +// TableFlashReplicaInfo is the replica information of a table. +type TableFlashReplicaInfo struct { + // Modifying the field name needs to negotiate with TiFlash colleague. + ID int64 `json:"id"` + ReplicaCount uint64 `json:"replica_count"` + LocationLabels []string `json:"location_labels"` + Available bool `json:"available"` + HighPriority bool `json:"high_priority"` +} + +// ServeHTTP implements the HTTPHandler interface. +func (h FlashReplicaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method == http.MethodPost { + h.handleStatusReport(w, req) + return + } + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + replicaInfos := make([]*TableFlashReplicaInfo, 0) + allDBs := schema.AllSchemas() + for _, db := range allDBs { + tbls := schema.SchemaTables(db.Name) + for _, tbl := range tbls { + replicaInfos = h.getTiFlashReplicaInfo(tbl.Meta(), replicaInfos) + } + } + dropedOrTruncateReplicaInfos, err := h.getDropOrTruncateTableTiflash(schema) + if err != nil { + writeError(w, err) + return + } + replicaInfos = append(replicaInfos, dropedOrTruncateReplicaInfos...) + writeData(w, replicaInfos) +} + +func (FlashReplicaHandler) getTiFlashReplicaInfo(tblInfo *model.TableInfo, replicaInfos []*TableFlashReplicaInfo) []*TableFlashReplicaInfo { + if tblInfo.TiFlashReplica == nil { + return replicaInfos + } + if pi := tblInfo.GetPartitionInfo(); pi != nil { + for _, p := range pi.Definitions { + replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ + ID: p.ID, + ReplicaCount: tblInfo.TiFlashReplica.Count, + LocationLabels: tblInfo.TiFlashReplica.LocationLabels, + Available: tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID), + }) + } + for _, p := range pi.AddingDefinitions { + replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ + ID: p.ID, + ReplicaCount: tblInfo.TiFlashReplica.Count, + LocationLabels: tblInfo.TiFlashReplica.LocationLabels, + Available: tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID), + HighPriority: true, + }) + } + return replicaInfos + } + replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ + ID: tblInfo.ID, + ReplicaCount: tblInfo.TiFlashReplica.Count, + LocationLabels: tblInfo.TiFlashReplica.LocationLabels, + Available: tblInfo.TiFlashReplica.Available, + }) + return replicaInfos +} + +func (h FlashReplicaHandler) getDropOrTruncateTableTiflash(currentSchema infoschema.InfoSchema) ([]*TableFlashReplicaInfo, error) { + s, err := session.CreateSession(h.Store) + if err != nil { + return nil, errors.Trace(err) + } + defer s.Close() + + store := domain.GetDomain(s).Store() + txn, err := store.Begin() + if err != nil { + return nil, errors.Trace(err) + } + gcSafePoint, err := gcutil.GetGCSafePoint(s) + if err != nil { + return nil, err + } + replicaInfos := make([]*TableFlashReplicaInfo, 0) + uniqueIDMap := make(map[int64]struct{}) + handleJobAndTableInfo := func(job *model.Job, tblInfo *model.TableInfo) (bool, error) { + // Avoid duplicate table ID info. + if _, ok := currentSchema.TableByID(tblInfo.ID); ok { + return false, nil + } + if _, ok := uniqueIDMap[tblInfo.ID]; ok { + return false, nil + } + uniqueIDMap[tblInfo.ID] = struct{}{} + replicaInfos = h.getTiFlashReplicaInfo(tblInfo, replicaInfos) + return false, nil + } + dom := domain.GetDomain(s) + fn := func(jobs []*model.Job) (bool, error) { + return executor.GetDropOrTruncateTableInfoFromJobs(jobs, gcSafePoint, dom, handleJobAndTableInfo) + } + err = ddl.IterAllDDLJobs(s, txn, fn) + if err != nil { + if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { + // The err indicate that current ddl job and remain DDL jobs was been deleted by GC, + // just ignore the error and return directly. + return replicaInfos, nil + } + return nil, err + } + return replicaInfos, nil +} + +type tableFlashReplicaStatus struct { + // Modifying the field name needs to negotiate with TiFlash colleague. + ID int64 `json:"id"` + // RegionCount is the number of regions that need sync. + RegionCount uint64 `json:"region_count"` + // FlashRegionCount is the number of regions that already sync completed. + FlashRegionCount uint64 `json:"flash_region_count"` +} + +// checkTableFlashReplicaAvailable uses to check the available status of table flash replica. +func (tf *tableFlashReplicaStatus) checkTableFlashReplicaAvailable() bool { + return tf.FlashRegionCount == tf.RegionCount +} + +func (h FlashReplicaHandler) handleStatusReport(w http.ResponseWriter, req *http.Request) { + var status tableFlashReplicaStatus + err := json.NewDecoder(req.Body).Decode(&status) + if err != nil { + writeError(w, err) + return + } + do, err := session.GetDomain(h.Store) + if err != nil { + writeError(w, err) + return + } + s, err := session.CreateSession(h.Store) + if err != nil { + writeError(w, err) + return + } + defer s.Close() + + available := status.checkTableFlashReplicaAvailable() + err = do.DDL().UpdateTableReplicaInfo(s, status.ID, available) + if err != nil { + writeError(w, err) + } + if available { + var tableInfo model.TableInfo + tableInfo.ID = status.ID + err = infosync.DeleteTiFlashTableSyncProgress(&tableInfo) + } else { + progress := float64(status.FlashRegionCount) / float64(status.RegionCount) + err = infosync.UpdateTiFlashProgressCache(status.ID, progress) + } + if err != nil { + writeError(w, err) + } + + logutil.BgLogger().Info("handle flash replica report", zap.Int64("table ID", status.ID), zap.Uint64("region count", + status.RegionCount), + zap.Uint64("flash region count", status.FlashRegionCount), + zap.Error(err)) +} + +// SchemaTableStorage is the schema table storage info. +type SchemaTableStorage struct { + TableSchema string `json:"table_schema"` + TableName string `json:"table_name"` + TableRows int64 `json:"table_rows"` + AvgRowLength int64 `json:"avg_row_length"` + DataLength int64 `json:"data_length"` + MaxDataLength int64 `json:"max_data_length"` + IndexLength int64 `json:"index_length"` + DataFree int64 `json:"data_free"` +} + +func getSchemaTablesStorageInfo(h *SchemaStorageHandler, schema *model.CIStr, table *model.CIStr) (messages []*SchemaTableStorage, err error) { + var s session.Session + if s, err = session.CreateSession(h.Store); err != nil { + return + } + defer s.Close() + + sctx := s.(sessionctx.Context) + condition := make([]string, 0) + params := make([]interface{}, 0) + + if schema != nil { + condition = append(condition, `TABLE_SCHEMA = %?`) + params = append(params, schema.O) + } + if table != nil { + condition = append(condition, `TABLE_NAME = %?`) + params = append(params, table.O) + } + + sql := `select TABLE_SCHEMA,TABLE_NAME,TABLE_ROWS,AVG_ROW_LENGTH,DATA_LENGTH,MAX_DATA_LENGTH,INDEX_LENGTH,DATA_FREE from INFORMATION_SCHEMA.TABLES` + if len(condition) > 0 { + //nolint: gosec + sql += ` WHERE ` + strings.Join(condition, ` AND `) + } + var results sqlexec.RecordSet + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + if results, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql, params...); err != nil { + logutil.BgLogger().Error(`ExecuteInternal`, zap.Error(err)) + } else if results != nil { + messages = make([]*SchemaTableStorage, 0) + defer terror.Call(results.Close) + for { + req := results.NewChunk(nil) + if err = results.Next(ctx, req); err != nil { + break + } + + if req.NumRows() == 0 { + break + } + + for i := 0; i < req.NumRows(); i++ { + messages = append(messages, &SchemaTableStorage{ + TableSchema: req.GetRow(i).GetString(0), + TableName: req.GetRow(i).GetString(1), + TableRows: req.GetRow(i).GetInt64(2), + AvgRowLength: req.GetRow(i).GetInt64(3), + DataLength: req.GetRow(i).GetInt64(4), + MaxDataLength: req.GetRow(i).GetInt64(5), + IndexLength: req.GetRow(i).GetInt64(6), + DataFree: req.GetRow(i).GetInt64(7), + }) + } + } + } + return +} + +// ServeHTTP handles request of list a database or table's schemas. +func (h SchemaStorageHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + + // parse params + params := mux.Vars(req) + + var ( + dbName *model.CIStr + tableName *model.CIStr + isSingle bool + ) + + if reqDbName, ok := params[handler.DBName]; ok { + cDBName := model.NewCIStr(reqDbName) + // all table schemas in a specified database + schemaInfo, exists := schema.SchemaByName(cDBName) + if !exists { + writeError(w, infoschema.ErrDatabaseNotExists.GenWithStackByArgs(reqDbName)) + return + } + dbName = &schemaInfo.Name + + if reqTableName, ok := params[handler.TableName]; ok { + // table schema of a specified table name + cTableName := model.NewCIStr(reqTableName) + data, e := schema.TableByName(cDBName, cTableName) + if e != nil { + writeError(w, e) + return + } + tableName = &data.Meta().Name + isSingle = true + } + } + + if results, e := getSchemaTablesStorageInfo(&h, dbName, tableName); e != nil { + writeError(w, e) + } else { + if isSingle { + writeData(w, results[0]) + } else { + writeData(w, results) + } + } +} + +// WriteDBTablesData writes all the table data in a database. The format is the marshal result of []*model.TableInfo, you can +// unmarshal it to []*model.TableInfo. In this function, we manually construct the marshal result so that the memory +// can be deallocated quickly. +// For every table in the input, we marshal them. The result such as {tb1} {tb2} {tb3}. +// Then we add some bytes to make it become [{tb1}, {tb2}, {tb3}], so we can unmarshal it to []*model.TableInfo. +// Note: It would return StatusOK even if errors occur. But if errors occur, there must be some bugs. +func WriteDBTablesData(w http.ResponseWriter, tbs []table.Table) { + if len(tbs) == 0 { + writeData(w, []*model.TableInfo{}) + return + } + w.Header().Set(handler.HeaderContentType, handler.ContentTypeJSON) + // We assume that marshal is always OK. + w.WriteHeader(http.StatusOK) + _, err := w.Write(hack.Slice("[\n")) + if err != nil { + terror.Log(errors.Trace(err)) + return + } + init := false + for _, tb := range tbs { + if init { + _, err = w.Write(hack.Slice(",\n")) + if err != nil { + terror.Log(errors.Trace(err)) + return + } + } else { + init = true + } + js, err := json.MarshalIndent(tb.Meta(), "", " ") + if err != nil { + terror.Log(errors.Trace(err)) + return + } + _, err = w.Write(js) + if err != nil { + terror.Log(errors.Trace(err)) + return + } + } + _, err = w.Write(hack.Slice("\n]")) + terror.Log(errors.Trace(err)) +} + +// ServeHTTP handles request of list a database or table's schemas. +func (h SchemaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + + // parse params + params := mux.Vars(req) + + if dbName, ok := params[handler.DBName]; ok { + cDBName := model.NewCIStr(dbName) + if tableName, ok := params[handler.TableName]; ok { + // table schema of a specified table name + cTableName := model.NewCIStr(tableName) + data, err := schema.TableByName(cDBName, cTableName) + if err != nil { + writeError(w, err) + return + } + writeData(w, data.Meta()) + return + } + // all table schemas in a specified database + if schema.SchemaExists(cDBName) { + tbs := schema.SchemaTables(cDBName) + WriteDBTablesData(w, tbs) + return + } + writeError(w, infoschema.ErrDatabaseNotExists.GenWithStackByArgs(dbName)) + return + } + + if tableID := req.FormValue(handler.TableIDQuery); len(tableID) > 0 { + // table schema of a specified tableID + tid, err := strconv.Atoi(tableID) + if err != nil { + writeError(w, err) + return + } + if tid < 0 { + writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) + return + } + if data, ok := schema.TableByID(int64(tid)); ok { + writeData(w, data.Meta()) + return + } + // The tid maybe a partition ID of the partition-table. + tbl, _, _ := schema.FindTableByPartitionID(int64(tid)) + if tbl == nil { + writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) + return + } + writeData(w, tbl.Meta()) + return + } + + // all databases' schemas + writeData(w, schema.AllSchemas()) +} + +// ServeHTTP handles table related requests, such as table's region information, disk usage. +func (h *TableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // parse params + params := mux.Vars(req) + dbName := params[handler.DBName] + tableName := params[handler.TableName] + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + + tableName, partitionName := handler.ExtractTableAndPartitionName(tableName) + tableVal, err := schema.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName)) + if err != nil { + writeError(w, err) + return + } + switch h.op { + case OpTableRegions: + h.handleRegionRequest(tableVal, w) + case OpTableRanges: + h.handleRangeRequest(tableVal, w) + case OpTableDiskUsage: + h.handleDiskUsageRequest(tableVal, w) + case OpTableScatter: + // supports partition table, only get one physical table, prevent too many scatter schedulers. + ptbl, err := h.GetPartition(tableVal, partitionName) + if err != nil { + writeError(w, err) + return + } + h.handleScatterTableRequest(ptbl, w) + case OpStopTableScatter: + ptbl, err := h.GetPartition(tableVal, partitionName) + if err != nil { + writeError(w, err) + return + } + h.handleStopScatterTableRequest(ptbl, w) + default: + writeError(w, errors.New("method not found")) + } +} + +// ServeHTTP handles request of ddl jobs history. +func (h DDLHistoryJobHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + var jobID, limitID int + var err error + if jobValue := req.FormValue(handler.JobID); len(jobValue) > 0 { + jobID, err = strconv.Atoi(jobValue) + if err != nil { + writeError(w, err) + return + } + if jobID < 1 { + writeError(w, errors.New("ddl history start_job_id must be greater than 0")) + return + } + } + if limitValue := req.FormValue(handler.Limit); len(limitValue) > 0 { + limitID, err = strconv.Atoi(limitValue) + if err != nil { + writeError(w, err) + return + } + if limitID < 1 { + writeError(w, errors.New("ddl history limit must be greater than 0")) + return + } + } + + jobs, err := h.getHistoryDDL(jobID, limitID) + if err != nil { + writeError(w, err) + return + } + writeData(w, jobs) +} + +func (h DDLHistoryJobHandler) getHistoryDDL(jobID, limit int) (jobs []*model.Job, err error) { + txn, err := h.Store.Begin() + if err != nil { + return nil, errors.Trace(err) + } + txnMeta := meta.NewMeta(txn) + + if jobID == 0 && limit == 0 { + jobs, err = ddl.GetAllHistoryDDLJobs(txnMeta) + } else { + jobs, err = ddl.ScanHistoryDDLJobs(txnMeta, int64(jobID), limit) + } + if err != nil { + return nil, errors.Trace(err) + } + return jobs, nil +} + +func (h DDLResignOwnerHandler) resignDDLOwner() error { + dom, err := session.GetDomain(h.store) + if err != nil { + return errors.Trace(err) + } + + ownerMgr := dom.DDL().OwnerManager() + err = ownerMgr.ResignOwner(context.Background()) + if err != nil { + return errors.Trace(err) + } + return nil +} + +// ServeHTTP handles request of resigning ddl owner. +func (h DDLResignOwnerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + writeError(w, errors.Errorf("This api only support POST method")) + return + } + + err := h.resignDDLOwner() + if err != nil { + log.Error("failed to resign DDL owner", zap.Error(err)) + writeError(w, err) + return + } + + writeData(w, "success!") +} + +func (h *TableHandler) getPDAddr() ([]string, error) { + etcd, ok := h.Store.(kv.EtcdBackend) + if !ok { + return nil, errors.New("not implemented") + } + pdAddrs, err := etcd.EtcdAddrs() + if err != nil { + return nil, err + } + if len(pdAddrs) == 0 { + return nil, errors.New("pd unavailable") + } + return pdAddrs, nil +} + +func (h *TableHandler) addScatterSchedule(startKey, endKey []byte, name string) error { + pdAddrs, err := h.getPDAddr() + if err != nil { + return err + } + input := map[string]string{ + "name": "scatter-range", + "start_key": url.QueryEscape(string(startKey)), + "end_key": url.QueryEscape(string(endKey)), + "range_name": name, + } + v, err := json.Marshal(input) + if err != nil { + return err + } + scheduleURL := fmt.Sprintf("%s://%s/pd/api/v1/schedulers", util.InternalHTTPSchema(), pdAddrs[0]) + resp, err := util.InternalHTTPClient().Post(scheduleURL, "application/json", bytes.NewBuffer(v)) + if err != nil { + return err + } + if err := resp.Body.Close(); err != nil { + log.Error("failed to close response body", zap.Error(err)) + } + return nil +} + +func (h *TableHandler) deleteScatterSchedule(name string) error { + pdAddrs, err := h.getPDAddr() + if err != nil { + return err + } + scheduleURL := fmt.Sprintf("%s://%s/pd/api/v1/schedulers/scatter-range-%s", util.InternalHTTPSchema(), pdAddrs[0], name) + req, err := http.NewRequest(http.MethodDelete, scheduleURL, nil) + if err != nil { + return err + } + resp, err := util.InternalHTTPClient().Do(req) + if err != nil { + return err + } + if err := resp.Body.Close(); err != nil { + log.Error("failed to close response body", zap.Error(err)) + } + return nil +} + +func (h *TableHandler) handleScatterTableRequest(tbl table.PhysicalTable, w http.ResponseWriter) { + // for record + tableID := tbl.GetPhysicalID() + startKey, endKey := tablecodec.GetTableHandleKeyRange(tableID) + startKey = codec.EncodeBytes([]byte{}, startKey) + endKey = codec.EncodeBytes([]byte{}, endKey) + tableName := fmt.Sprintf("%s-%d", tbl.Meta().Name.String(), tableID) + err := h.addScatterSchedule(startKey, endKey, tableName) + if err != nil { + writeError(w, errors.Annotate(err, "scatter record error")) + return + } + // for indices + for _, index := range tbl.Indices() { + indexID := index.Meta().ID + indexName := index.Meta().Name.String() + startKey, endKey := tablecodec.GetTableIndexKeyRange(tableID, indexID) + startKey = codec.EncodeBytes([]byte{}, startKey) + endKey = codec.EncodeBytes([]byte{}, endKey) + name := tableName + "-" + indexName + err := h.addScatterSchedule(startKey, endKey, name) + if err != nil { + writeError(w, errors.Annotatef(err, "scatter index(%s) error", name)) + return + } + } + writeData(w, "success!") +} + +func (h *TableHandler) handleStopScatterTableRequest(tbl table.PhysicalTable, w http.ResponseWriter) { + // for record + tableName := fmt.Sprintf("%s-%d", tbl.Meta().Name.String(), tbl.GetPhysicalID()) + err := h.deleteScatterSchedule(tableName) + if err != nil { + writeError(w, errors.Annotate(err, "stop scatter record error")) + return + } + // for indices + for _, index := range tbl.Indices() { + indexName := index.Meta().Name.String() + name := tableName + "-" + indexName + err := h.deleteScatterSchedule(name) + if err != nil { + writeError(w, errors.Annotatef(err, "delete scatter index(%s) error", name)) + return + } + } + writeData(w, "success!") +} + +func (h *TableHandler) handleRegionRequest(tbl table.Table, w http.ResponseWriter) { + pi := tbl.Meta().GetPartitionInfo() + if pi != nil { + // Partitioned table. + var data []*TableRegions + for _, def := range pi.Definitions { + tableRegions, err := h.getRegionsByID(tbl, def.ID, def.Name.O) + if err != nil { + writeError(w, err) + return + } + + data = append(data, tableRegions) + } + writeData(w, data) + return + } + + meta := tbl.Meta() + tableRegions, err := h.getRegionsByID(tbl, meta.ID, meta.Name.O) + if err != nil { + writeError(w, err) + return + } + + writeData(w, tableRegions) +} + +func createTableRanges(tblID int64, tblName string, indices []*model.IndexInfo) *TableRanges { + indexPrefix := tablecodec.GenTableIndexPrefix(tblID) + recordPrefix := tablecodec.GenTableRecordPrefix(tblID) + tableEnd := tablecodec.EncodeTablePrefix(tblID + 1) + ranges := &TableRanges{ + TableName: tblName, + TableID: tblID, + Range: createRangeDetail(tablecodec.EncodeTablePrefix(tblID), tableEnd), + Record: createRangeDetail(recordPrefix, tableEnd), + Index: createRangeDetail(indexPrefix, recordPrefix), + } + if len(indices) != 0 { + indexRanges := make(map[string]RangeDetail) + for _, index := range indices { + start := tablecodec.EncodeTableIndexPrefix(tblID, index.ID) + end := tablecodec.EncodeTableIndexPrefix(tblID, index.ID+1) + indexRanges[index.Name.String()] = createRangeDetail(start, end) + } + ranges.Indices = indexRanges + } + return ranges +} + +func (*TableHandler) handleRangeRequest(tbl table.Table, w http.ResponseWriter) { + meta := tbl.Meta() + pi := meta.GetPartitionInfo() + if pi != nil { + // Partitioned table. + var data []*TableRanges + for _, def := range pi.Definitions { + data = append(data, createTableRanges(def.ID, def.Name.String(), meta.Indices)) + } + writeData(w, data) + return + } + + writeData(w, createTableRanges(meta.ID, meta.Name.String(), meta.Indices)) +} + +func (h *TableHandler) getRegionsByID(tbl table.Table, id int64, name string) (*TableRegions, error) { + // for record + startKey, endKey := tablecodec.GetTableHandleKeyRange(id) + ctx := context.Background() + pdCli := h.RegionCache.PDClient() + regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) + if err != nil { + return nil, err + } + + recordRegions := make([]handler.RegionMeta, 0, len(regions)) + for _, region := range regions { + meta := handler.RegionMeta{ + ID: region.Meta.Id, + Leader: region.Leader, + Peers: region.Meta.Peers, + RegionEpoch: region.Meta.RegionEpoch, + } + recordRegions = append(recordRegions, meta) + } + + // for indices + indices := make([]IndexRegions, len(tbl.Indices())) + for i, index := range tbl.Indices() { + indexID := index.Meta().ID + indices[i].Name = index.Meta().Name.String() + indices[i].ID = indexID + startKey, endKey := tablecodec.GetTableIndexKeyRange(id, indexID) + regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) + if err != nil { + return nil, err + } + indexRegions := make([]handler.RegionMeta, 0, len(regions)) + for _, region := range regions { + meta := handler.RegionMeta{ + ID: region.Meta.Id, + Leader: region.Leader, + Peers: region.Meta.Peers, + RegionEpoch: region.Meta.RegionEpoch, + } + indexRegions = append(indexRegions, meta) + } + indices[i].Regions = indexRegions + } + + return &TableRegions{ + TableName: name, + TableID: id, + Indices: indices, + RecordRegions: recordRegions, + }, nil +} + +func (h *TableHandler) handleDiskUsageRequest(tbl table.Table, w http.ResponseWriter) { + tableID := tbl.Meta().ID + var stats helper.PDRegionStats + err := h.GetPDRegionStats(tableID, &stats, false) + if err != nil { + writeError(w, err) + return + } + writeData(w, stats.StorageSize) +} + +// ServeHTTP handles request of get region by ID. +func (h RegionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // parse and check params + params := mux.Vars(req) + if _, ok := params[handler.RegionID]; !ok { + router := mux.CurrentRoute(req).GetName() + if router == "RegionsMeta" { + startKey := []byte{'m'} + endKey := []byte{'n'} + + recordRegionIDs, err := h.RegionCache.ListRegionIDsInKeyRange(tikv.NewBackofferWithVars(context.Background(), 500, nil), startKey, endKey) + if err != nil { + writeError(w, err) + return + } + + recordRegions, err := h.GetRegionsMeta(recordRegionIDs) + if err != nil { + writeError(w, err) + return + } + writeData(w, recordRegions) + return + } + if router == "RegionHot" { + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + hotRead, err := h.ScrapeHotInfo(pdapi.HotRead, schema.AllSchemas()) + if err != nil { + writeError(w, err) + return + } + hotWrite, err := h.ScrapeHotInfo(pdapi.HotWrite, schema.AllSchemas()) + if err != nil { + writeError(w, err) + return + } + writeData(w, map[string]interface{}{ + "write": hotWrite, + "read": hotRead, + }) + return + } + return + } + + regionIDInt, err := strconv.ParseInt(params[handler.RegionID], 0, 64) + if err != nil { + writeError(w, err) + return + } + regionID := uint64(regionIDInt) + + // locate region + region, err := h.RegionCache.LocateRegionByID(tikv.NewBackofferWithVars(context.Background(), 500, nil), regionID) + if err != nil { + writeError(w, err) + return + } + + frameRange, err := helper.NewRegionFrameRange(region) + if err != nil { + writeError(w, err) + return + } + + // create RegionDetail from RegionFrameRange + regionDetail := &RegionDetail{ + RegionID: regionID, + RangeDetail: createRangeDetail(region.StartKey, region.EndKey), + } + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + // Since we need a database's name for each frame, and a table's database name can not + // get from table's ID directly. Above all, here do dot process like + // `for id in [frameRange.firstTableID,frameRange.endTableID]` + // on [frameRange.firstTableID,frameRange.endTableID] is small enough. + for _, db := range schema.AllSchemas() { + if util.IsMemDB(db.Name.L) { + continue + } + for _, tableVal := range db.Tables { + regionDetail.addTableInRange(db.Name.String(), tableVal, frameRange) + } + } + writeData(w, regionDetail) +} + +// parseQuery is used to parse query string in URL with shouldUnescape, due to golang http package can not distinguish +// query like "?a=" and "?a". We rewrite it to separate these two queries. e.g. +// "?a=" which means that a is an empty string ""; +// "?a" which means that a is null. +// If shouldUnescape is true, we use QueryUnescape to handle keys and values that will be put in m. +// If shouldUnescape is false, we don't use QueryUnescap to handle. +func parseQuery(query string, m url.Values, shouldUnescape bool) error { + var err error + for query != "" { + key := query + if i := strings.IndexAny(key, "&;"); i >= 0 { + key, query = key[:i], key[i+1:] + } else { + query = "" + } + if key == "" { + continue + } + if i := strings.Index(key, "="); i >= 0 { + value := "" + key, value = key[:i], key[i+1:] + if shouldUnescape { + key, err = url.QueryUnescape(key) + if err != nil { + return errors.Trace(err) + } + value, err = url.QueryUnescape(value) + if err != nil { + return errors.Trace(err) + } + } + m[key] = append(m[key], value) + } else { + if shouldUnescape { + key, err = url.QueryUnescape(key) + if err != nil { + return errors.Trace(err) + } + } + if _, ok := m[key]; !ok { + m[key] = nil + } + } + } + return errors.Trace(err) +} + +// ServeHTTP handles request of list a table's regions. +func (h MvccTxnHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + var data interface{} + params := mux.Vars(req) + var err error + switch h.op { + case OpMvccGetByHex: + data, err = h.HandleMvccGetByHex(params) + case OpMvccGetByIdx, OpMvccGetByKey: + if req.URL == nil { + err = errors.BadRequestf("Invalid URL") + break + } + values := make(url.Values) + err = parseQuery(req.URL.RawQuery, values, true) + if err == nil { + if h.op == OpMvccGetByIdx { + data, err = h.handleMvccGetByIdx(params, values) + } else { + data, err = h.handleMvccGetByKey(params, values) + } + } + case OpMvccGetByTxn: + data, err = h.handleMvccGetByTxn(params) + default: + err = errors.NotSupportedf("Operation not supported.") + } + if err != nil { + writeError(w, err) + } else { + writeData(w, data) + } +} + +// handleMvccGetByIdx gets MVCC info by an index key. +func (h MvccTxnHandler) handleMvccGetByIdx(params map[string]string, values url.Values) (interface{}, error) { + dbName := params[handler.DBName] + tableName := params[handler.TableName] + + t, err := h.GetTable(dbName, tableName) + if err != nil { + return nil, errors.Trace(err) + } + handle, err := h.GetHandle(t, params, values) + if err != nil { + return nil, errors.Trace(err) + } + + var idxCols []*model.ColumnInfo + var idx table.Index + for _, v := range t.Indices() { + if strings.EqualFold(v.Meta().Name.String(), params[handler.IndexName]) { + for _, c := range v.Meta().Columns { + idxCols = append(idxCols, t.Meta().Columns[c.Offset]) + } + idx = v + break + } + } + if idx == nil { + return nil, errors.NotFoundf("Index %s not found!", params[handler.IndexName]) + } + return h.GetMvccByIdxValue(idx, values, idxCols, handle) +} + +func (h MvccTxnHandler) handleMvccGetByKey(params map[string]string, values url.Values) (interface{}, error) { + dbName := params[handler.DBName] + tableName := params[handler.TableName] + tb, err := h.GetTable(dbName, tableName) + if err != nil { + return nil, errors.Trace(err) + } + handle, err := h.GetHandle(tb, params, values) + if err != nil { + return nil, err + } + + encodedKey := tablecodec.EncodeRecordKey(tb.RecordPrefix(), handle) + data, err := h.GetMvccByEncodedKey(encodedKey) + if err != nil { + return nil, err + } + regionID, err := h.GetRegionIDByKey(encodedKey) + if err != nil { + return nil, err + } + resp := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), Value: data, RegionID: regionID} + if len(values.Get("decode")) == 0 { + return resp, nil + } + colMap := make(map[int64]*types.FieldType, 3) + for _, col := range tb.Meta().Columns { + colMap[col.ID] = &(col.FieldType) + } + + respValue := resp.Value + var result interface{} = resp + if respValue.Info != nil { + datas := make(map[string]map[string]string) + for _, w := range respValue.Info.Writes { + if len(w.ShortValue) > 0 { + datas[strconv.FormatUint(w.StartTs, 10)], err = h.decodeMvccData(w.ShortValue, colMap, tb.Meta()) + } + } + + for _, v := range respValue.Info.Values { + if len(v.Value) > 0 { + datas[strconv.FormatUint(v.StartTs, 10)], err = h.decodeMvccData(v.Value, colMap, tb.Meta()) + } + } + + if len(datas) > 0 { + re := map[string]interface{}{ + "key": resp.Key, + "info": respValue.Info, + "data": datas, + } + if err != nil { + re["decode_error"] = err.Error() + } + result = re + } + } + + return result, nil +} + +func (MvccTxnHandler) decodeMvccData(bs []byte, colMap map[int64]*types.FieldType, tb *model.TableInfo) (map[string]string, error) { + rs, err := tablecodec.DecodeRowToDatumMap(bs, colMap, time.UTC) + record := make(map[string]string, len(tb.Columns)) + for _, col := range tb.Columns { + if c, ok := rs[col.ID]; ok { + data := "nil" + if !c.IsNull() { + data, err = c.ToString() + } + record[col.Name.O] = data + } + } + return record, err +} + +func (h *MvccTxnHandler) handleMvccGetByTxn(params map[string]string) (interface{}, error) { + startTS, err := strconv.ParseInt(params[handler.StartTS], 0, 64) + if err != nil { + return nil, errors.Trace(err) + } + tableID, err := h.GetTableID(params[handler.DBName], params[handler.TableName]) + if err != nil { + return nil, errors.Trace(err) + } + startKey := tablecodec.EncodeTablePrefix(tableID) + endKey := tablecodec.EncodeRowKeyWithHandle(tableID, kv.IntHandle(math.MaxInt64)) + return h.GetMvccByStartTs(uint64(startTS), startKey, endKey) +} + +// ServerInfo is used to report the servers info when do http request. +type ServerInfo struct { + IsOwner bool `json:"is_owner"` + MaxProcs int `json:"max_procs"` + GOGC int `json:"gogc"` + *infosync.ServerInfo +} + +// ServeHTTP handles request of ddl server info. +func (h ServerInfoHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + do, err := session.GetDomain(h.Store) + if err != nil { + writeError(w, errors.New("create session error")) + log.Error("failed to get session domain", zap.Error(err)) + return + } + info := ServerInfo{} + info.ServerInfo, err = infosync.GetServerInfo() + if err != nil { + writeError(w, err) + log.Error("failed to get server info", zap.Error(err)) + return + } + info.IsOwner = do.DDL().OwnerManager().IsOwner() + info.MaxProcs = runtime.GOMAXPROCS(0) + info.GOGC = util.GetGOGC() + writeData(w, info) +} + +// ClusterServerInfo is used to report cluster servers info when do http request. +type ClusterServerInfo struct { + ServersNum int `json:"servers_num,omitempty"` + OwnerID string `json:"owner_id"` + IsAllServerVersionConsistent bool `json:"is_all_server_version_consistent,omitempty"` + AllServersDiffVersions []infosync.ServerVersionInfo `json:"all_servers_diff_versions,omitempty"` + AllServersInfo map[string]*infosync.ServerInfo `json:"all_servers_info,omitempty"` +} + +// ServeHTTP handles request of all ddl servers info. +func (h AllServerInfoHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { + do, err := session.GetDomain(h.Store) + if err != nil { + writeError(w, errors.New("create session error")) + log.Error("failed to get session domain", zap.Error(err)) + return + } + ctx := context.Background() + allServersInfo, err := infosync.GetAllServerInfo(ctx) + if err != nil { + writeError(w, errors.New("ddl server information not found")) + log.Error("failed to get all server info", zap.Error(err)) + return + } + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + ownerID, err := do.DDL().OwnerManager().GetOwnerID(ctx) + cancel() + if err != nil { + writeError(w, errors.New("ddl server information not found")) + log.Error("failed to get owner id", zap.Error(err)) + return + } + allVersionsMap := map[infosync.ServerVersionInfo]struct{}{} + allVersions := make([]infosync.ServerVersionInfo, 0, len(allServersInfo)) + for _, v := range allServersInfo { + if _, ok := allVersionsMap[v.ServerVersionInfo]; ok { + continue + } + allVersionsMap[v.ServerVersionInfo] = struct{}{} + allVersions = append(allVersions, v.ServerVersionInfo) + } + clusterInfo := ClusterServerInfo{ + ServersNum: len(allServersInfo), + OwnerID: ownerID, + // len(allVersions) = 1 indicates there has only 1 tidb version in cluster, so all server versions are consistent. + IsAllServerVersionConsistent: len(allVersions) == 1, + AllServersInfo: allServersInfo, + } + // if IsAllServerVersionConsistent is false, return the all tidb servers version. + if !clusterInfo.IsAllServerVersionConsistent { + clusterInfo.AllServersDiffVersions = allVersions + } + writeData(w, clusterInfo) +} + +// DBTableInfo is used to report the database, table information and the current schema version. +type DBTableInfo struct { + DBInfo *model.DBInfo `json:"db_info"` + TableInfo *model.TableInfo `json:"table_info"` + SchemaVersion int64 `json:"schema_version"` +} + +// ServeHTTP handles request of database information and table information by tableID. +func (h DBTableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + params := mux.Vars(req) + tableID := params[handler.TableID] + physicalID, err := strconv.Atoi(tableID) + if err != nil { + writeError(w, errors.Errorf("Wrong tableID: %v", tableID)) + return + } + + schema, err := h.Schema() + if err != nil { + writeError(w, err) + return + } + + dbTblInfo := DBTableInfo{ + SchemaVersion: schema.SchemaMetaVersion(), + } + tbl, ok := schema.TableByID(int64(physicalID)) + if ok { + dbTblInfo.TableInfo = tbl.Meta() + dbInfo, ok := schema.SchemaByTable(dbTblInfo.TableInfo) + if !ok { + logutil.BgLogger().Error("can not find the database of the table", zap.Int64("table id", dbTblInfo.TableInfo.ID), zap.String("table name", dbTblInfo.TableInfo.Name.L)) + writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) + return + } + dbTblInfo.DBInfo = dbInfo + writeData(w, dbTblInfo) + return + } + // The physicalID maybe a partition ID of the partition-table. + tbl, dbInfo, _ := schema.FindTableByPartitionID(int64(physicalID)) + if tbl == nil { + writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) + return + } + dbTblInfo.TableInfo = tbl.Meta() + dbTblInfo.DBInfo = dbInfo + writeData(w, dbTblInfo) +} + +// ServeHTTP handles request of TiDB metric profile. +func (h ProfileHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + sctx, err := session.CreateSession(h.Store) + if err != nil { + writeError(w, err) + return + } + defer sctx.Close() + + var start, end time.Time + if req.FormValue("end") != "" { + end, err = time.ParseInLocation(time.RFC3339, req.FormValue("end"), sctx.GetSessionVars().Location()) + if err != nil { + writeError(w, err) + return + } + } else { + end = time.Now() + } + if req.FormValue("start") != "" { + start, err = time.ParseInLocation(time.RFC3339, req.FormValue("start"), sctx.GetSessionVars().Location()) + if err != nil { + writeError(w, err) + return + } + } else { + start = end.Add(-time.Minute * 10) + } + valueTp := req.FormValue("type") + pb, err := executor.NewProfileBuilder(sctx, start, end, valueTp) + if err != nil { + writeError(w, err) + return + } + err = pb.Collect() + if err != nil { + writeError(w, err) + return + } + _, err = w.Write(pb.Build()) + terror.Log(errors.Trace(err)) +} + +// TestHandler is the handler for tests. It's convenient to provide some APIs for integration tests. +type TestHandler struct { + *handler.TikvHandlerTool + gcIsRunning uint32 +} + +// NewTestHandler creates a new TestHandler. +func NewTestHandler(tool *handler.TikvHandlerTool, gcIsRunning uint32) *TestHandler { + return &TestHandler{ + TikvHandlerTool: tool, + gcIsRunning: gcIsRunning, + } +} + +// ServeHTTP handles test related requests. +func (h *TestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + params := mux.Vars(req) + mod := strings.ToLower(params["mod"]) + op := strings.ToLower(params["op"]) + + switch mod { + case "gc": + h.handleGC(op, w, req) + default: + writeError(w, errors.NotSupportedf("module(%s)", mod)) + } +} + +// Supported operations: +// - resolvelock?safepoint={uint64}&physical={bool}: +// - safepoint: resolve all locks whose timestamp is less than the safepoint. +// - physical: whether it uses physical(green GC) mode to scan locks. Default is true. +func (h *TestHandler) handleGC(op string, w http.ResponseWriter, req *http.Request) { + if !atomic.CompareAndSwapUint32(&h.gcIsRunning, 0, 1) { + writeError(w, errors.New("GC is running")) + return + } + defer atomic.StoreUint32(&h.gcIsRunning, 0) + + switch op { + case "resolvelock": + h.handleGCResolveLocks(w, req) + default: + writeError(w, errors.NotSupportedf("operation(%s)", op)) + } +} + +func (h *TestHandler) handleGCResolveLocks(w http.ResponseWriter, req *http.Request) { + s := req.FormValue("safepoint") + safePoint, err := strconv.ParseUint(s, 10, 64) + if err != nil { + writeError(w, errors.Errorf("parse safePoint(%s) failed", s)) + return + } + usePhysical := true + s = req.FormValue("physical") + if s != "" { + usePhysical, err = strconv.ParseBool(s) + if err != nil { + writeError(w, errors.Errorf("parse physical(%s) failed", s)) + return + } + } + + ctx := req.Context() + logutil.Logger(ctx).Info("start resolving locks", zap.Uint64("safePoint", safePoint), zap.Bool("physical", usePhysical)) + physicalUsed, err := gcworker.RunResolveLocks(ctx, h.Store, h.RegionCache.PDClient(), safePoint, "testGCWorker", 3, usePhysical) + if err != nil { + writeError(w, errors.Annotate(err, "resolveLocks failed")) + } else { + writeData(w, map[string]interface{}{ + "physicalUsed": physicalUsed, + }) + } +} + +// ServeHTTP handles request of resigning ddl owner. +func (h DDLHookHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + writeError(w, errors.Errorf("This api only support POST method")) + return + } + + dom, err := session.GetDomain(h.store) + if err != nil { + log.Error("failed to get session domain", zap.Error(err)) + writeError(w, err) + } + + newCallbackFunc, err := ddl.GetCustomizedHook(req.FormValue("ddl_hook")) + if err != nil { + log.Error("failed to get customized hook", zap.Error(err)) + writeError(w, err) + } + callback := newCallbackFunc(dom) + + dom.DDL().SetHook(callback) + writeData(w, "success!") + + ctx := req.Context() + logutil.Logger(ctx).Info("change ddl hook success", zap.String("to_ddl_hook", req.FormValue("ddl_hook"))) +} + +// ServeHTTP handles request of set server labels. +func (LabelHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + writeError(w, errors.Errorf("This api only support POST method")) + return + } + + labels := make(map[string]string) + err := json.NewDecoder(req.Body).Decode(&labels) + if err != nil { + writeError(w, err) + return + } + + if len(labels) > 0 { + cfg := *config.GetGlobalConfig() + // Be careful of data race. The key & value of cfg.Labels must not be changed. + if cfg.Labels != nil { + for k, v := range cfg.Labels { + if _, found := labels[k]; !found { + labels[k] = v + } + } + } + cfg.Labels = labels + config.StoreGlobalConfig(&cfg) + logutil.BgLogger().Info("update server labels", zap.Any("labels", cfg.Labels)) + } + + writeData(w, config.GetGlobalConfig().Labels) +} diff --git a/pkg/server/handler/ttlhandler/BUILD.bazel b/pkg/server/handler/ttlhandler/BUILD.bazel new file mode 100644 index 0000000000000..a2c736db837d6 --- /dev/null +++ b/pkg/server/handler/ttlhandler/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "ttlhandler", + srcs = ["ttl.go"], + importpath = "github.com/pingcap/tidb/pkg/server/handler/ttlhandler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/server/handler", + "//pkg/session", + "//pkg/ttl/client", + "//pkg/util/logutil", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/server/handler/ttlhandler/ttl.go b/pkg/server/handler/ttlhandler/ttl.go new file mode 100644 index 0000000000000..6c9c00531c5b5 --- /dev/null +++ b/pkg/server/handler/ttlhandler/ttl.go @@ -0,0 +1,73 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package ttlhandler + +import ( + "net/http" + "strings" + + "github.com/gorilla/mux" + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/session" + ttlcient "github.com/pingcap/tidb/pkg/ttl/client" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// TTLJobTriggerHandler is used to trigger a TTL job manually +type TTLJobTriggerHandler struct { + store kv.Storage +} + +// NewTTLJobTriggerHandler returns a new TTLJobTriggerHandler +func NewTTLJobTriggerHandler(store kv.Storage) *TTLJobTriggerHandler { + return &TTLJobTriggerHandler{store: store} +} + +// ServeHTTP handles request of triger a ttl job +func (h TTLJobTriggerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodPost { + handler.WriteError(w, errors.Errorf("This api only support POST method")) + return + } + + params := mux.Vars(req) + dbName := strings.ToLower(params["db"]) + tableName := strings.ToLower(params["table"]) + + ctx := req.Context() + dom, err := session.GetDomain(h.store) + if err != nil { + log.Error("failed to get session domain", zap.Error(err)) + handler.WriteError(w, err) + return + } + + cli := dom.TTLJobManager().GetCommandCli() + resp, err := ttlcient.TriggerNewTTLJob(ctx, cli, dbName, tableName) + if err != nil { + log.Error("failed to trigger new TTL job", zap.Error(err)) + handler.WriteError(w, err) + return + } + handler.WriteData(w, resp) + logutil.Logger(ctx).Info("trigger TTL job manually successfully", + zap.String("dbName", dbName), + zap.String("tableName", tableName), + zap.Any("response", resp)) +} diff --git a/server/handler/upgrade_handler.go b/pkg/server/handler/upgrade_handler.go similarity index 97% rename from server/handler/upgrade_handler.go rename to pkg/server/handler/upgrade_handler.go index fd95c0cbcd6c2..0762143cc90aa 100644 --- a/server/handler/upgrade_handler.go +++ b/pkg/server/handler/upgrade_handler.go @@ -22,10 +22,10 @@ import ( "github.com/gorilla/mux" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/server/handler/util.go b/pkg/server/handler/util.go new file mode 100644 index 0000000000000..068040d1db5c5 --- /dev/null +++ b/pkg/server/handler/util.go @@ -0,0 +1,108 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package handler + +import ( + "encoding/json" + "net/http" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" +) + +//revive:disable +const ( + DBName = "db" + HexKey = "hexKey" + IndexName = "index" + Handle = "handle" + RegionID = "regionID" + StartTS = "startTS" + TableName = "table" + TableID = "tableID" + ColumnID = "colID" + ColumnTp = "colTp" + ColumnFlag = "colFlag" + ColumnLen = "colLen" + RowBin = "rowBin" + Snapshot = "snapshot" + FileName = "filename" + DumpPartitionStats = "dumpPartitionStats" + Begin = "begin" + End = "end" +) + +// For extract task handler +const ( + Type = "type" + IsDump = "isDump" + + // For extract plan task handler + IsSkipStats = "isSkipStats" + IsHistoryView = "isHistoryView" +) + +// For query string +const ( + TableIDQuery = "table_id" + Limit = "limit" + JobID = "start_job_id" + Operation = "op" + Seconds = "seconds" +) + +const ( + HeaderContentType = "Content-Type" + ContentTypeJSON = "application/json" +) + +//revive:enable + +// WriteError writes error to response. +func WriteError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusBadRequest) + _, err = w.Write([]byte(err.Error())) + terror.Log(errors.Trace(err)) +} + +// WriteData writes data to response. +func WriteData(w http.ResponseWriter, data interface{}) { + js, err := json.MarshalIndent(data, "", " ") + if err != nil { + WriteError(w, err) + return + } + // write response + w.Header().Set(HeaderContentType, ContentTypeJSON) + w.WriteHeader(http.StatusOK) + _, err = w.Write(js) + terror.Log(errors.Trace(err)) +} + +// ExtractTableAndPartitionName extracts table name and partition name from this "table(partition)": +func ExtractTableAndPartitionName(str string) (table, partition string) { + // extract table name and partition name from this "table(partition)": + // A sane person would not let the the table name or partition name contain '('. + start := strings.IndexByte(str, '(') + if start == -1 { + return str, "" + } + end := strings.IndexByte(str, ')') + if end == -1 { + return str, "" + } + return str[:start], str[start+1 : end] +} diff --git a/server/http_handler.go b/pkg/server/http_handler.go similarity index 85% rename from server/http_handler.go rename to pkg/server/http_handler.go index d10e99af3314b..0bc01c83570ad 100644 --- a/server/http_handler.go +++ b/pkg/server/http_handler.go @@ -15,13 +15,13 @@ package server import ( - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/server/handler/optimizor" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/store/helper" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/server/handler/optimizor" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/store/helper" ) // NewTikvHandlerTool checks and prepares for tikv handler. diff --git a/server/http_status.go b/pkg/server/http_status.go similarity index 96% rename from server/http_status.go rename to pkg/server/http_status.go index 7f6a2aa0126a7..8d45b6220e11b 100644 --- a/server/http_status.go +++ b/pkg/server/http_status.go @@ -39,24 +39,24 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/fn" pb "github.com/pingcap/kvproto/pkg/autoid" - autoid "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/server/handler/optimizor" - "github.com/pingcap/tidb/server/handler/tikvhandler" - "github.com/pingcap/tidb/server/handler/ttlhandler" - util2 "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/printer" - "github.com/pingcap/tidb/util/versioninfo" + autoid "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/server/handler/optimizor" + "github.com/pingcap/tidb/pkg/server/handler/tikvhandler" + "github.com/pingcap/tidb/pkg/server/handler/ttlhandler" + util2 "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/printer" + "github.com/pingcap/tidb/pkg/util/versioninfo" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/soheilhy/cmux" "github.com/tiancaiamao/appdash/traceapp" diff --git a/pkg/server/internal/BUILD.bazel b/pkg/server/internal/BUILD.bazel new file mode 100644 index 0000000000000..9b7576c20c57c --- /dev/null +++ b/pkg/server/internal/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "internal", + srcs = ["packetio.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/server/err", + "//pkg/server/internal/util", + "//pkg/server/metrics", + "//pkg/sessionctx/variable", + "@com_github_klauspost_compress//zstd", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "internal_test", + timeout = "short", + srcs = ["packetio_test.go"], + embed = [":internal"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/parser/mysql", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "@com_github_klauspost_compress//zstd", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/server/internal/column/BUILD.bazel b/pkg/server/internal/column/BUILD.bazel new file mode 100644 index 0000000000000..794b8a607f50b --- /dev/null +++ b/pkg/server/internal/column/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "column", + srcs = [ + "column.go", + "convert.go", + "result_encoder.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server/internal/column", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/server/err", + "//pkg/server/internal/dump", + "//pkg/server/internal/util", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/hack", + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "column_test", + timeout = "short", + srcs = [ + "column_test.go", + "result_encoder_test.go", + ], + embed = [":column"], + flaky = True, + shard_count = 5, + deps = [ + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/server/internal/util", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/server/internal/column/column.go b/pkg/server/internal/column/column.go new file mode 100644 index 0000000000000..c69d41ff4a28b --- /dev/null +++ b/pkg/server/internal/column/column.go @@ -0,0 +1,250 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package column + +import ( + "fmt" + "math" + "strconv" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/err" + "github.com/pingcap/tidb/pkg/server/internal/dump" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" +) + +const maxColumnNameSize = 256 + +// Info contains information of a column +type Info struct { + DefaultValue any + Schema string + Table string + OrgTable string + Name string + OrgName string + ColumnLength uint32 + Charset uint16 + Flag uint16 + Decimal uint8 + Type uint8 +} + +// Dump dumps Info to bytes. +func (column *Info) Dump(buffer []byte, d *ResultEncoder) []byte { + return column.dump(buffer, d, false) +} + +// DumpWithDefault dumps Info to bytes, including column defaults. This is used for ComFieldList responses. +func (column *Info) DumpWithDefault(buffer []byte, d *ResultEncoder) []byte { + return column.dump(buffer, d, true) +} + +func (column *Info) dump(buffer []byte, d *ResultEncoder, withDefault bool) []byte { + if d == nil { + d = NewResultEncoder(charset.CharsetUTF8MB4) + } + nameDump, orgnameDump := []byte(column.Name), []byte(column.OrgName) + if len(nameDump) > maxColumnNameSize { + nameDump = nameDump[0:maxColumnNameSize] + } + if len(orgnameDump) > maxColumnNameSize { + orgnameDump = orgnameDump[0:maxColumnNameSize] + } + buffer = dump.LengthEncodedString(buffer, []byte("def")) + buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.Schema))) + buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.Table))) + buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.OrgTable))) + buffer = dump.LengthEncodedString(buffer, d.EncodeMeta(nameDump)) + buffer = dump.LengthEncodedString(buffer, d.EncodeMeta(orgnameDump)) + + buffer = append(buffer, 0x0c) + buffer = dump.Uint16(buffer, d.ColumnTypeInfoCharsetID(column)) + buffer = dump.Uint32(buffer, column.ColumnLength) + buffer = append(buffer, dumpType(column.Type)) + buffer = dump.Uint16(buffer, DumpFlag(column.Type, column.Flag)) + buffer = append(buffer, column.Decimal) + buffer = append(buffer, 0, 0) + + if withDefault { + switch column.DefaultValue { + case "CURRENT_TIMESTAMP", "CURRENT_DATE", nil: + buffer = append(buffer, 251) // NULL + default: + defaultValStr := fmt.Sprintf("%v", column.DefaultValue) + buffer = dump.LengthEncodedString(buffer, []byte(defaultValStr)) + } + } + + return buffer +} + +// DumpFlag dumps flag of a column. +func DumpFlag(tp byte, flag uint16) uint16 { + switch tp { + case mysql.TypeSet: + return flag | uint16(mysql.SetFlag) + case mysql.TypeEnum: + return flag | uint16(mysql.EnumFlag) + default: + return flag + } +} + +func dumpType(tp byte) byte { + switch tp { + case mysql.TypeSet, mysql.TypeEnum: + return mysql.TypeString + default: + return tp + } +} + +// DumpTextRow dumps a row to bytes. +func DumpTextRow(buffer []byte, columns []*Info, row chunk.Row, d *ResultEncoder) ([]byte, error) { + if d == nil { + d = NewResultEncoder(charset.CharsetUTF8MB4) + } + tmp := make([]byte, 0, 20) + for i, col := range columns { + if row.IsNull(i) { + buffer = append(buffer, 0xfb) + continue + } + switch col.Type { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong: + tmp = strconv.AppendInt(tmp[:0], row.GetInt64(i), 10) + buffer = dump.LengthEncodedString(buffer, tmp) + case mysql.TypeYear: + year := row.GetInt64(i) + tmp = tmp[:0] + if year == 0 { + tmp = append(tmp, '0', '0', '0', '0') + } else { + tmp = strconv.AppendInt(tmp, year, 10) + } + buffer = dump.LengthEncodedString(buffer, tmp) + case mysql.TypeLonglong: + if mysql.HasUnsignedFlag(uint(columns[i].Flag)) { + tmp = strconv.AppendUint(tmp[:0], row.GetUint64(i), 10) + } else { + tmp = strconv.AppendInt(tmp[:0], row.GetInt64(i), 10) + } + buffer = dump.LengthEncodedString(buffer, tmp) + case mysql.TypeFloat: + prec := -1 + if columns[i].Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec && col.Table == "" { + prec = int(col.Decimal) + } + tmp = util.AppendFormatFloat(tmp[:0], float64(row.GetFloat32(i)), prec, 32) + buffer = dump.LengthEncodedString(buffer, tmp) + case mysql.TypeDouble: + prec := types.UnspecifiedLength + if col.Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec && col.Table == "" { + prec = int(col.Decimal) + } + tmp = util.AppendFormatFloat(tmp[:0], row.GetFloat64(i), prec, 64) + buffer = dump.LengthEncodedString(buffer, tmp) + case mysql.TypeNewDecimal: + buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetMyDecimal(i).String())) + case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBit, + mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: + d.UpdateDataEncoding(col.Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(row.GetBytes(i))) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetTime(i).String())) + case mysql.TypeDuration: + dur := row.GetDuration(i, int(col.Decimal)) + buffer = dump.LengthEncodedString(buffer, hack.Slice(dur.String())) + case mysql.TypeEnum: + d.UpdateDataEncoding(col.Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetEnum(i).String()))) + case mysql.TypeSet: + d.UpdateDataEncoding(col.Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetSet(i).String()))) + case mysql.TypeJSON: + // The collation of JSON type is always binary. + // To compatible with MySQL, here we treat it as utf-8. + d.UpdateDataEncoding(mysql.DefaultCollationID) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetJSON(i).String()))) + default: + return nil, err.ErrInvalidType.GenWithStack("invalid type %v", columns[i].Type) + } + } + return buffer, nil +} + +// DumpBinaryRow dumps a row to bytes. +func DumpBinaryRow(buffer []byte, columns []*Info, row chunk.Row, d *ResultEncoder) ([]byte, error) { + if d == nil { + d = NewResultEncoder(charset.CharsetUTF8MB4) + } + buffer = append(buffer, mysql.OKHeader) + nullBitmapOff := len(buffer) + numBytes4Null := (len(columns) + 7 + 2) / 8 + for i := 0; i < numBytes4Null; i++ { + buffer = append(buffer, 0) + } + for i := range columns { + if row.IsNull(i) { + bytePos := (i + 2) / 8 + bitPos := byte((i + 2) % 8) + buffer[nullBitmapOff+bytePos] |= 1 << bitPos + continue + } + switch columns[i].Type { + case mysql.TypeTiny: + buffer = append(buffer, byte(row.GetInt64(i))) + case mysql.TypeShort, mysql.TypeYear: + buffer = dump.Uint16(buffer, uint16(row.GetInt64(i))) + case mysql.TypeInt24, mysql.TypeLong: + buffer = dump.Uint32(buffer, uint32(row.GetInt64(i))) + case mysql.TypeLonglong: + buffer = dump.Uint64(buffer, row.GetUint64(i)) + case mysql.TypeFloat: + buffer = dump.Uint32(buffer, math.Float32bits(row.GetFloat32(i))) + case mysql.TypeDouble: + buffer = dump.Uint64(buffer, math.Float64bits(row.GetFloat64(i))) + case mysql.TypeNewDecimal: + buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetMyDecimal(i).String())) + case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBit, + mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: + d.UpdateDataEncoding(columns[i].Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(row.GetBytes(i))) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + buffer = dump.BinaryDateTime(buffer, row.GetTime(i)) + case mysql.TypeDuration: + buffer = append(buffer, dump.BinaryTime(row.GetDuration(i, 0).Duration)...) + case mysql.TypeEnum: + d.UpdateDataEncoding(columns[i].Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetEnum(i).String()))) + case mysql.TypeSet: + d.UpdateDataEncoding(columns[i].Charset) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetSet(i).String()))) + case mysql.TypeJSON: + // The collation of JSON type is always binary. + // To compatible with MySQL, here we treat it as utf-8. + d.UpdateDataEncoding(mysql.DefaultCollationID) + buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetJSON(i).String()))) + default: + return nil, err.ErrInvalidType.GenWithStack("invalid type %v", columns[i].Type) + } + } + return buffer, nil +} diff --git a/pkg/server/internal/column/column_test.go b/pkg/server/internal/column/column_test.go new file mode 100644 index 0000000000000..b32d07b028f02 --- /dev/null +++ b/pkg/server/internal/column/column_test.go @@ -0,0 +1,252 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package column + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestDumpColumn(t *testing.T) { + info := Info{ + Schema: "testSchema", + Table: "testTable", + OrgTable: "testOrgTable", + Name: "testName", + OrgName: "testOrgName", + ColumnLength: 1, + Charset: 106, + Flag: 0, + Decimal: 1, + Type: 14, + DefaultValue: []byte{5, 2}, + } + r := info.Dump(nil, nil) + exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x8, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0} + require.Equal(t, exp, r) + + require.Equal(t, uint16(mysql.SetFlag), DumpFlag(mysql.TypeSet, 0)) + require.Equal(t, uint16(mysql.EnumFlag), DumpFlag(mysql.TypeEnum, 0)) + require.Equal(t, uint16(0), DumpFlag(mysql.TypeString, 0)) + + require.Equal(t, mysql.TypeString, dumpType(mysql.TypeSet)) + require.Equal(t, mysql.TypeString, dumpType(mysql.TypeEnum)) + require.Equal(t, mysql.TypeBit, dumpType(mysql.TypeBit)) +} + +func TestDumpColumnWithDefault(t *testing.T) { + info := Info{ + Schema: "testSchema", + Table: "testTable", + OrgTable: "testOrgTable", + Name: "testName", + OrgName: "testOrgName", + ColumnLength: 1, + Charset: 106, + Flag: 0, + Decimal: 1, + Type: 14, + DefaultValue: "test", + } + r := info.DumpWithDefault(nil, nil) + exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x8, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0, 0x4, 0x74, 0x65, 0x73, 0x74} + require.Equal(t, exp, r) + + require.Equal(t, uint16(mysql.SetFlag), DumpFlag(mysql.TypeSet, 0)) + require.Equal(t, uint16(mysql.EnumFlag), DumpFlag(mysql.TypeEnum, 0)) + require.Equal(t, uint16(0), DumpFlag(mysql.TypeString, 0)) + + require.Equal(t, mysql.TypeString, dumpType(mysql.TypeSet)) + require.Equal(t, mysql.TypeString, dumpType(mysql.TypeEnum)) + require.Equal(t, mysql.TypeBit, dumpType(mysql.TypeBit)) +} + +func TestColumnNameLimit(t *testing.T) { + aLongName := make([]byte, 0, 300) + for i := 0; i < 300; i++ { + aLongName = append(aLongName, 'a') + } + info := Info{ + Schema: "testSchema", + Table: "testTable", + OrgTable: "testOrgTable", + Name: string(aLongName), + OrgName: "testOrgName", + ColumnLength: 1, + Charset: 106, + Flag: 0, + Decimal: 1, + Type: 14, + DefaultValue: []byte{5, 2}, + } + r := info.Dump(nil, nil) + exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xfc, 0x0, 0x1, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0} + require.Equal(t, exp, r) +} + +func TestDumpTextValue(t *testing.T) { + columns := []*Info{{ + Type: mysql.TypeLonglong, + Decimal: mysql.NotFixedDec, + }} + + dp := NewResultEncoder(charset.CharsetUTF8MB4) + null := types.NewIntDatum(0) + null.SetNull() + bs, err := DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{null}).ToRow(), dp) + require.NoError(t, err) + _, isNull, _, err := util.ParseLengthEncodedBytes(bs) + require.NoError(t, err) + require.True(t, isNull) + + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewIntDatum(10)}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "10", mustDecodeStr(t, bs)) + + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewUintDatum(11)}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "11", mustDecodeStr(t, bs)) + + columns[0].Flag |= uint16(mysql.UnsignedFlag) + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewUintDatum(11)}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "11", mustDecodeStr(t, bs)) + + columns[0].Type = mysql.TypeFloat + columns[0].Decimal = 1 + f32 := types.NewFloat32Datum(1.2) + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f32}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "1.2", mustDecodeStr(t, bs)) + + columns[0].Decimal = 2 + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f32}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "1.20", mustDecodeStr(t, bs)) + + f64 := types.NewFloat64Datum(2.2) + columns[0].Type = mysql.TypeDouble + columns[0].Decimal = 1 + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f64}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "2.2", mustDecodeStr(t, bs)) + + columns[0].Decimal = 2 + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f64}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "2.20", mustDecodeStr(t, bs)) + + columns[0].Type = mysql.TypeBlob + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewBytesDatum([]byte("foo"))}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "foo", mustDecodeStr(t, bs)) + + columns[0].Type = mysql.TypeVarchar + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewStringDatum("bar")}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "bar", mustDecodeStr(t, bs)) + + dp = NewResultEncoder("gbk") + columns[0].Type = mysql.TypeVarchar + dt := []types.Datum{types.NewStringDatum("一")} + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums(dt).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, []byte{0xd2, 0xbb}, []byte(mustDecodeStr(t, bs))) + + columns[0].Charset = uint16(mysql.CharsetNameToID("gbk")) + dp = NewResultEncoder("binary") + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums(dt).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, []byte{0xd2, 0xbb}, []byte(mustDecodeStr(t, bs))) + + var d types.Datum + + sc := mock.NewContext().GetSessionVars().StmtCtx + sc.IgnoreZeroInDate = true + losAngelesTz, err := time.LoadLocation("America/Los_Angeles") + require.NoError(t, err) + sc.SetTimeZone(losAngelesTz) + + time, err := types.ParseTime(sc, "2017-01-05 23:59:59.575601", mysql.TypeDatetime, 0, nil) + require.NoError(t, err) + d.SetMysqlTime(time) + columns[0].Type = mysql.TypeDatetime + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "2017-01-06 00:00:00", mustDecodeStr(t, bs)) + + duration, _, err := types.ParseDuration(sc, "11:30:45", 0) + require.NoError(t, err) + d.SetMysqlDuration(duration) + columns[0].Type = mysql.TypeDuration + columns[0].Decimal = 0 + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "11:30:45", mustDecodeStr(t, bs)) + + d.SetMysqlDecimal(types.NewDecFromStringForTest("1.23")) + columns[0].Type = mysql.TypeNewDecimal + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "1.23", mustDecodeStr(t, bs)) + + year := types.NewIntDatum(0) + columns[0].Type = mysql.TypeYear + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{year}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "0000", mustDecodeStr(t, bs)) + + year.SetInt64(1984) + columns[0].Type = mysql.TypeYear + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{year}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "1984", mustDecodeStr(t, bs)) + + enum := types.NewMysqlEnumDatum(types.Enum{Name: "ename", Value: 0}) + columns[0].Type = mysql.TypeEnum + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{enum}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "ename", mustDecodeStr(t, bs)) + + set := types.Datum{} + set.SetMysqlSet(types.Set{Name: "sname", Value: 0}, mysql.DefaultCollationName) + columns[0].Type = mysql.TypeSet + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{set}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, "sname", mustDecodeStr(t, bs)) + + js := types.Datum{} + binaryJSON, err := types.ParseBinaryJSONFromString(`{"a": 1, "b": 2}`) + require.NoError(t, err) + js.SetMysqlJSON(binaryJSON) + columns[0].Type = mysql.TypeJSON + bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{js}).ToRow(), dp) + require.NoError(t, err) + require.Equal(t, `{"a": 1, "b": 2}`, mustDecodeStr(t, bs)) +} + +func mustDecodeStr(t *testing.T, b []byte) string { + str, _, _, err := util.ParseLengthEncodedBytes(b) + require.NoError(t, err) + return string(str) +} diff --git a/pkg/server/internal/column/convert.go b/pkg/server/internal/column/convert.go new file mode 100644 index 0000000000000..ffda8db1d1edc --- /dev/null +++ b/pkg/server/internal/column/convert.go @@ -0,0 +1,91 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package column + +import ( + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" +) + +// ConvertColumnInfo converts `*ast.ResultField` to `*Info` +func ConvertColumnInfo(fld *ast.ResultField) (ci *Info) { + ci = &Info{ + Name: fld.ColumnAsName.O, + OrgName: fld.Column.Name.O, + Table: fld.TableAsName.O, + Schema: fld.DBName.O, + Flag: uint16(fld.Column.GetFlag()), + Charset: uint16(mysql.CharsetNameToID(fld.Column.GetCharset())), + Type: fld.Column.GetType(), + DefaultValue: fld.Column.GetDefaultValue(), + } + + if fld.EmptyOrgName { + ci.OrgName = "" + } + if fld.Table != nil { + ci.OrgTable = fld.Table.Name.O + } + if fld.Column.GetFlen() != types.UnspecifiedLength { + ci.ColumnLength = uint32(fld.Column.GetFlen()) + } + if fld.Column.GetType() == mysql.TypeNewDecimal { + // Consider the negative sign. + ci.ColumnLength++ + if fld.Column.GetDecimal() > types.DefaultFsp { + // Consider the decimal point. + ci.ColumnLength++ + } + } else if types.IsString(fld.Column.GetType()) || + fld.Column.GetType() == mysql.TypeEnum || fld.Column.GetType() == mysql.TypeSet { // issue #18870 + // Fix issue #4540. + // The flen is a hint, not a precise value, so most client will not use the value. + // But we found in rare MySQL client, like Navicat for MySQL(version before 12) will truncate + // the `show create table` result. To fix this case, we must use a large enough flen to prevent + // the truncation, in MySQL, it will multiply bytes length by a multiple based on character set. + // For examples: + // * latin, the multiple is 1 + // * gb2312, the multiple is 2 + // * Utf-8, the multiple is 3 + // * utf8mb4, the multiple is 4 + // We used to check non-string types to avoid the truncation problem in some MySQL + // client such as Navicat. Now we only allow string type enter this branch. + charsetDesc, err := charset.GetCharsetInfo(fld.Column.GetCharset()) + if err != nil { + ci.ColumnLength *= 4 + } else { + ci.ColumnLength *= uint32(charsetDesc.Maxlen) + } + } + + if fld.Column.GetDecimal() == types.UnspecifiedLength { + if fld.Column.GetType() == mysql.TypeDuration { + ci.Decimal = uint8(types.DefaultFsp) + } else { + ci.Decimal = mysql.NotFixedDec + } + } else { + ci.Decimal = uint8(fld.Column.GetDecimal()) + } + + // Keep things compatible for old clients. + // Refer to mysql-server/sql/protocol.cc send_result_set_metadata() + if ci.Type == mysql.TypeVarchar { + ci.Type = mysql.TypeVarString + } + return +} diff --git a/server/internal/column/result_encoder.go b/pkg/server/internal/column/result_encoder.go similarity index 97% rename from server/internal/column/result_encoder.go rename to pkg/server/internal/column/result_encoder.go index f5f539c5bcf9d..0f87e1b7e1eae 100644 --- a/server/internal/column/result_encoder.go +++ b/pkg/server/internal/column/result_encoder.go @@ -38,9 +38,9 @@ package column import ( "bytes" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/server/internal/column/result_encoder_test.go b/pkg/server/internal/column/result_encoder_test.go similarity index 100% rename from server/internal/column/result_encoder_test.go rename to pkg/server/internal/column/result_encoder_test.go diff --git a/pkg/server/internal/dump/BUILD.bazel b/pkg/server/internal/dump/BUILD.bazel new file mode 100644 index 0000000000000..df6051b402bbf --- /dev/null +++ b/pkg/server/internal/dump/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "dump", + srcs = ["dump.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal/dump", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/parser/mysql", + "//pkg/types", + ], +) + +go_test( + name = "dump_test", + timeout = "short", + srcs = ["dump_test.go"], + embed = [":dump"], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/server/internal/dump/dump.go b/pkg/server/internal/dump/dump.go new file mode 100644 index 0000000000000..ea5d07272bd30 --- /dev/null +++ b/pkg/server/internal/dump/dump.go @@ -0,0 +1,168 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// The MIT License (MIT) +// +// Copyright (c) 2014 wandoulabs +// Copyright (c) 2014 siddontang +// +// 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. + +package dump + +import ( + "encoding/binary" + "time" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" +) + +// LengthEncodedString dumps a string as length encoded byte slice. +func LengthEncodedString(buffer []byte, bytes []byte) []byte { + buffer = LengthEncodedInt(buffer, uint64(len(bytes))) + buffer = append(buffer, bytes...) + return buffer +} + +// LengthEncodedInt dumps an integer as length encoded byte slice. +func LengthEncodedInt(buffer []byte, n uint64) []byte { + switch { + case n <= 250: + return append(buffer, byte(n)) + + case n <= 0xffff: + return append(buffer, 0xfc, byte(n), byte(n>>8)) + + case n <= 0xffffff: + return append(buffer, 0xfd, byte(n), byte(n>>8), byte(n>>16)) + + case n <= 0xffffffffffffffff: + return append(buffer, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24), + byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) + } + + return buffer +} + +// Uint16 dumps an uint16 as byte slice. +func Uint16(buffer []byte, n uint16) []byte { + buffer = append(buffer, byte(n)) + buffer = append(buffer, byte(n>>8)) + return buffer +} + +// Uint32 dumps an uint32 as byte slice. +func Uint32(buffer []byte, n uint32) []byte { + buffer = append(buffer, byte(n)) + buffer = append(buffer, byte(n>>8)) + buffer = append(buffer, byte(n>>16)) + buffer = append(buffer, byte(n>>24)) + return buffer +} + +// Uint64 dumps an uint64 as byte slice. +func Uint64(buffer []byte, n uint64) []byte { + buffer = append(buffer, byte(n)) + buffer = append(buffer, byte(n>>8)) + buffer = append(buffer, byte(n>>16)) + buffer = append(buffer, byte(n>>24)) + buffer = append(buffer, byte(n>>32)) + buffer = append(buffer, byte(n>>40)) + buffer = append(buffer, byte(n>>48)) + buffer = append(buffer, byte(n>>56)) + return buffer +} + +// BinaryTime dumps a time as binary byte slice. +func BinaryTime(dur time.Duration) (data []byte) { + if dur == 0 { + return []byte{0} + } + data = make([]byte, 13) + data[0] = 12 + if dur < 0 { + data[1] = 1 + dur = -dur + } + days := dur / (24 * time.Hour) + dur -= days * 24 * time.Hour //nolint:durationcheck + data[2] = byte(days) + hours := dur / time.Hour + dur -= hours * time.Hour //nolint:durationcheck + data[6] = byte(hours) + minutes := dur / time.Minute + dur -= minutes * time.Minute //nolint:durationcheck + data[7] = byte(minutes) + seconds := dur / time.Second + dur -= seconds * time.Second //nolint:durationcheck + data[8] = byte(seconds) + if dur == 0 { + data[0] = 8 + return data[:9] + } + binary.LittleEndian.PutUint32(data[9:13], uint32(dur/time.Microsecond)) + return +} + +// BinaryDateTime dumps a datetime as binary byte slice. +func BinaryDateTime(data []byte, t types.Time) []byte { + year, mon, day := t.Year(), t.Month(), t.Day() + switch t.Type() { + case mysql.TypeTimestamp, mysql.TypeDatetime: + if t.IsZero() { + // All zero. + data = append(data, 0) + } else if t.Microsecond() != 0 { + // Has micro seconds. + data = append(data, 11) + data = Uint16(data, uint16(year)) + data = append(data, byte(mon), byte(day), byte(t.Hour()), byte(t.Minute()), byte(t.Second())) + data = Uint32(data, uint32(t.Microsecond())) + } else if t.Hour() != 0 || t.Minute() != 0 || t.Second() != 0 { + // Has HH:MM:SS + data = append(data, 7) + data = Uint16(data, uint16(year)) + data = append(data, byte(mon), byte(day), byte(t.Hour()), byte(t.Minute()), byte(t.Second())) + } else { + // Only YY:MM:DD + data = append(data, 4) + data = Uint16(data, uint16(year)) + data = append(data, byte(mon), byte(day)) + } + case mysql.TypeDate: + if t.IsZero() { + data = append(data, 0) + } else { + data = append(data, 4) + data = Uint16(data, uint16(year)) // year + data = append(data, byte(mon), byte(day)) + } + } + return data +} diff --git a/pkg/server/internal/dump/dump_test.go b/pkg/server/internal/dump/dump_test.go new file mode 100644 index 0000000000000..1c2b67d8da555 --- /dev/null +++ b/pkg/server/internal/dump/dump_test.go @@ -0,0 +1,125 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package dump + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestDumpBinaryTime(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + parsedTime, err := types.ParseTimestamp(sc, "0000-00-00 00:00:00.000000") + require.NoError(t, err) + d := BinaryDateTime(nil, parsedTime) + require.Equal(t, []byte{0}, d) + + parsedTime, err = types.ParseTimestamp(stmtctx.NewStmtCtxWithTimeZone(time.Local), "1991-05-01 01:01:01.100001") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + // 199 & 7 composed to uint16 1991 (litter-endian) + // 160 & 134 & 1 & 0 composed to uint32 1000001 (litter-endian) + require.Equal(t, []byte{11, 199, 7, 5, 1, 1, 1, 1, 161, 134, 1, 0}, d) + + parsedTime, err = types.ParseDatetime(sc, "0000-00-00 00:00:00.000000") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + require.Equal(t, []byte{0}, d) + + parsedTime, err = types.ParseDatetime(sc, "1993-07-13 01:01:01.000000") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + // 201 & 7 composed to uint16 1993 (litter-endian) + require.Equal(t, []byte{7, 201, 7, 7, 13, 1, 1, 1}, d) + + parsedTime, err = types.ParseDate(sc, "0000-00-00") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + require.Equal(t, []byte{0}, d) + parsedTime, err = types.ParseDate(sc, "1992-06-01") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + // 200 & 7 composed to uint16 1992 (litter-endian) + require.Equal(t, []byte{4, 200, 7, 6, 1}, d) + + parsedTime, err = types.ParseDate(sc, "0000-00-00") + require.NoError(t, err) + d = BinaryDateTime(nil, parsedTime) + require.Equal(t, []byte{0}, d) + + myDuration, _, err := types.ParseDuration(sc, "0000-00-00 00:00:00.000000", 6) + require.NoError(t, err) + d = BinaryTime(myDuration.Duration) + require.Equal(t, []byte{0}, d) + + d = BinaryTime(0) + require.Equal(t, []byte{0}, d) + + d = BinaryTime(-1) + require.Equal(t, []byte{12, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, d) + + d = BinaryTime(time.Nanosecond + 86400*1000*time.Microsecond) + require.Equal(t, []byte{12, 0, 0, 0, 0, 0, 0, 1, 26, 128, 26, 6, 0}, d) +} + +func TestDumpLengthEncodedInt(t *testing.T) { + testCases := []struct { + num uint64 + buffer []byte + }{ + { + uint64(0), + []byte{0x00}, + }, + { + uint64(513), + []byte{'\xfc', '\x01', '\x02'}, + }, + { + uint64(197121), + []byte{'\xfd', '\x01', '\x02', '\x03'}, + }, + { + uint64(578437695752307201), + []byte{'\xfe', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08'}, + }, + } + for _, tc := range testCases { + b := LengthEncodedInt(nil, tc.num) + require.Equal(t, tc.buffer, b) + } +} + +func TestDumpUint(t *testing.T) { + testCases := []uint64{ + 0, + 1, + 1<<64 - 1, + } + parseUint64 := func(b []byte) uint64 { + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | + uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | + uint64(b[6])<<48 | uint64(b[7])<<56 + } + for _, tc := range testCases { + b := Uint64(nil, tc) + require.Len(t, b, 8) + require.Equal(t, tc, parseUint64(b)) + } +} diff --git a/pkg/server/internal/handshake/BUILD.bazel b/pkg/server/internal/handshake/BUILD.bazel new file mode 100644 index 0000000000000..0c09a57570c98 --- /dev/null +++ b/pkg/server/internal/handshake/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "handshake", + srcs = ["handshake.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal/handshake", + visibility = ["//pkg/server:__subpackages__"], + deps = ["@com_github_klauspost_compress//zstd"], +) diff --git a/server/internal/handshake/handshake.go b/pkg/server/internal/handshake/handshake.go similarity index 100% rename from server/internal/handshake/handshake.go rename to pkg/server/internal/handshake/handshake.go diff --git a/server/internal/packetio.go b/pkg/server/internal/packetio.go similarity index 97% rename from server/internal/packetio.go rename to pkg/server/internal/packetio.go index 2ffbf5ed7fc69..2fae04517bcd1 100644 --- a/server/internal/packetio.go +++ b/pkg/server/internal/packetio.go @@ -44,12 +44,12 @@ import ( "github.com/klauspost/compress/zstd" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server/err" - "github.com/pingcap/tidb/server/internal/util" - server_metrics "github.com/pingcap/tidb/server/metrics" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server/err" + "github.com/pingcap/tidb/pkg/server/internal/util" + server_metrics "github.com/pingcap/tidb/pkg/server/metrics" + "github.com/pingcap/tidb/pkg/sessionctx/variable" ) const defaultWriterSize = 16 * 1024 diff --git a/server/internal/packetio_test.go b/pkg/server/internal/packetio_test.go similarity index 98% rename from server/internal/packetio_test.go rename to pkg/server/internal/packetio_test.go index 8dc18a343ba43..0953f573e0774 100644 --- a/server/internal/packetio_test.go +++ b/pkg/server/internal/packetio_test.go @@ -22,9 +22,9 @@ import ( "testing" "github.com/klauspost/compress/zstd" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/internal/parse/BUILD.bazel b/pkg/server/internal/parse/BUILD.bazel new file mode 100644 index 0000000000000..5dd9a9d717262 --- /dev/null +++ b/pkg/server/internal/parse/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "parse", + srcs = ["parse.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal/parse", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/server/internal/handshake", + "//pkg/server/internal/util", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/logutil", + "@com_github_klauspost_compress//zstd", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "parse_test", + timeout = "short", + srcs = [ + "handshake_test.go", + "parse_test.go", + ], + embed = [":parse"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/expression", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/server/internal/handshake", + "//pkg/server/internal/util", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "@com_github_stretchr_testify//require", + ], +) diff --git a/server/internal/parse/handshake_test.go b/pkg/server/internal/parse/handshake_test.go similarity index 97% rename from server/internal/parse/handshake_test.go rename to pkg/server/internal/parse/handshake_test.go index f9e1d3ed764fd..20749144afab6 100644 --- a/server/internal/parse/handshake_test.go +++ b/pkg/server/internal/parse/handshake_test.go @@ -18,7 +18,7 @@ import ( "context" "testing" - "github.com/pingcap/tidb/server/internal/handshake" + "github.com/pingcap/tidb/pkg/server/internal/handshake" "github.com/stretchr/testify/require" ) diff --git a/server/internal/parse/parse.go b/pkg/server/internal/parse/parse.go similarity index 96% rename from server/internal/parse/parse.go rename to pkg/server/internal/parse/parse.go index bed7c6e10fe10..e55ca68eb6656 100644 --- a/server/internal/parse/parse.go +++ b/pkg/server/internal/parse/parse.go @@ -22,17 +22,17 @@ import ( "math" "github.com/klauspost/compress/zstd" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal/handshake" - util2 "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal/handshake" + util2 "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/server/internal/parse/parse_test.go b/pkg/server/internal/parse/parse_test.go similarity index 95% rename from server/internal/parse/parse_test.go rename to pkg/server/internal/parse/parse_test.go index 01a1a7efc48b1..b8345cceca2eb 100644 --- a/server/internal/parse/parse_test.go +++ b/pkg/server/internal/parse/parse_test.go @@ -17,12 +17,12 @@ package parse import ( "testing" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/internal/resultset/BUILD.bazel b/pkg/server/internal/resultset/BUILD.bazel new file mode 100644 index 0000000000000..da47cac931127 --- /dev/null +++ b/pkg/server/internal/resultset/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "resultset", + srcs = [ + "cursor.go", + "resultset.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server/internal/resultset", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/planner/core", + "//pkg/server/internal/column", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + ], +) diff --git a/server/internal/resultset/cursor.go b/pkg/server/internal/resultset/cursor.go similarity index 97% rename from server/internal/resultset/cursor.go rename to pkg/server/internal/resultset/cursor.go index 0e9aa91467f5c..1b07a96668ee4 100644 --- a/server/internal/resultset/cursor.go +++ b/pkg/server/internal/resultset/cursor.go @@ -14,7 +14,7 @@ package resultset -import "github.com/pingcap/tidb/util/chunk" +import "github.com/pingcap/tidb/pkg/util/chunk" // CursorResultSet extends the `ResultSet` to provide the ability to store an iterator type CursorResultSet interface { diff --git a/server/internal/resultset/resultset.go b/pkg/server/internal/resultset/resultset.go similarity index 93% rename from server/internal/resultset/resultset.go rename to pkg/server/internal/resultset/resultset.go index d7dd361ff3c7f..ca800055276c4 100644 --- a/server/internal/resultset/resultset.go +++ b/pkg/server/internal/resultset/resultset.go @@ -18,11 +18,11 @@ import ( "context" "sync/atomic" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) // ResultSet is the result set of an query. diff --git a/pkg/server/internal/testserverclient/BUILD.bazel b/pkg/server/internal/testserverclient/BUILD.bazel new file mode 100644 index 0000000000000..1f751d9f38580 --- /dev/null +++ b/pkg/server/internal/testserverclient/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testserverclient", + srcs = ["server_client.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal/testserverclient", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/server", + "//pkg/testkit", + "//pkg/testkit/testenv", + "//pkg/util/versioninfo", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@org_uber_go_zap//:zap", + ], +) diff --git a/server/internal/testserverclient/server_client.go b/pkg/server/internal/testserverclient/server_client.go similarity index 99% rename from server/internal/testserverclient/server_client.go rename to pkg/server/internal/testserverclient/server_client.go index 7a4b3b37b1335..f02a288baa514 100644 --- a/server/internal/testserverclient/server_client.go +++ b/pkg/server/internal/testserverclient/server_client.go @@ -35,13 +35,13 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testenv" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testenv" + "github.com/pingcap/tidb/pkg/util/versioninfo" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -1391,12 +1391,12 @@ func (cli *TestServerClient) RunTestLoadData(t *testing.T, server *server.Server require.Falsef(t, rows.Next(), "unexpected data") require.NoError(t, rows.Close()) // fail error processing test - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/commitOneTaskErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/commitOneTaskErr", "return")) _, err1 = dbt.GetDB().Exec(fmt.Sprintf(`load data local infile %q into table pn FIELDS TERMINATED BY ',' with thread=1`, path)) mysqlErr, ok := err1.(*mysql.MySQLError) require.True(t, ok) require.Equal(t, "mock commit one task error", mysqlErr.Message) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/commitOneTaskErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/commitOneTaskErr")) dbt.MustExec("drop table if exists pn") }) diff --git a/pkg/server/internal/testutil/BUILD.bazel b/pkg/server/internal/testutil/BUILD.bazel new file mode 100644 index 0000000000000..62982e0289789 --- /dev/null +++ b/pkg/server/internal/testutil/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/server/internal/testutil", + visibility = ["//pkg/server:__subpackages__"], +) diff --git a/server/internal/testutil/testutil.go b/pkg/server/internal/testutil/testutil.go similarity index 100% rename from server/internal/testutil/testutil.go rename to pkg/server/internal/testutil/testutil.go diff --git a/pkg/server/internal/util/BUILD.bazel b/pkg/server/internal/util/BUILD.bazel new file mode 100644 index 0000000000000..eb027c3457139 --- /dev/null +++ b/pkg/server/internal/util/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "util", + srcs = [ + "buffered_read_conn.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/server/internal/util", + visibility = ["//pkg/server:__subpackages__"], + deps = [ + "//pkg/config", + "//pkg/parser/charset", + ], +) + +go_test( + name = "util_test", + timeout = "short", + srcs = ["util_test.go"], + embed = [":util"], + flaky = True, + shard_count = 4, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/server/internal/util/buffered_read_conn.go b/pkg/server/internal/util/buffered_read_conn.go similarity index 100% rename from server/internal/util/buffered_read_conn.go rename to pkg/server/internal/util/buffered_read_conn.go diff --git a/pkg/server/internal/util/util.go b/pkg/server/internal/util/util.go new file mode 100644 index 0000000000000..18952ef710845 --- /dev/null +++ b/pkg/server/internal/util/util.go @@ -0,0 +1,230 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at http://mozilla.org/MPL/2.0/. + +// The MIT License (MIT) +// +// Copyright (c) 2014 wandoulabs +// Copyright (c) 2014 siddontang +// +// 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. + +package util + +import ( + "bytes" + "io" + "math" + "net/http" + "strconv" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/charset" +) + +// ParseNullTermString parses a null terminated string. +func ParseNullTermString(b []byte) (str []byte, remain []byte) { + off := bytes.IndexByte(b, 0) + if off == -1 { + return nil, b + } + return b[:off], b[off+1:] +} + +// ParseLengthEncodedInt parses a length encoded integer. +func ParseLengthEncodedInt(b []byte) (num uint64, isNull bool, n int) { + switch b[0] { + // 251: NULL + case 0xfb: + n = 1 + isNull = true + return + + // 252: value of following 2 + case 0xfc: + num = uint64(b[1]) | uint64(b[2])<<8 + n = 3 + return + + // 253: value of following 3 + case 0xfd: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 + n = 4 + return + + // 254: value of following 8 + case 0xfe: + num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | + uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | + uint64(b[7])<<48 | uint64(b[8])<<56 + n = 9 + return + } + + // https://dev.mysql.com/doc/internals/en/integer.html#length-encoded-integer: If the first byte of a packet is a length-encoded integer and its byte value is 0xfe, you must check the length of the packet to verify that it has enough space for a 8-byte integer. + // TODO: 0xff is undefined + + // 0-250: value of first byte + num = uint64(b[0]) + n = 1 + return +} + +// ParseLengthEncodedBytes parses a length encoded byte slice. +func ParseLengthEncodedBytes(b []byte) ([]byte, bool, int, error) { + // Get length + num, isNull, n := ParseLengthEncodedInt(b) + if num < 1 { + return nil, isNull, n, nil + } + + n += int(num) + + // Check data length + if len(b) >= n { + return b[n-int(num) : n], false, n, nil + } + + return nil, false, n, io.EOF +} + +// InputDecoder is used to decode input. +type InputDecoder struct { + encoding charset.Encoding +} + +// NewInputDecoder creates a new InputDecoder. +func NewInputDecoder(chs string) *InputDecoder { + return &InputDecoder{ + encoding: charset.FindEncodingTakeUTF8AsNoop(chs), + } +} + +// DecodeInput decodes input. +func (i *InputDecoder) DecodeInput(src []byte) []byte { + result, err := i.encoding.Transform(nil, src, charset.OpDecode) + if err != nil { + return src + } + return result +} + +// LengthEncodedIntSize returns the size of length encoded integer. +func LengthEncodedIntSize(n uint64) int { + switch { + case n <= 250: + return 1 + + case n <= 0xffff: + return 3 + + case n <= 0xffffff: + return 4 + } + + return 9 +} + +const ( + expFormatBig = 1e15 + expFormatSmall = 1e-15 + defaultMySQLPrec = 5 +) + +// AppendFormatFloat appends a float64 to dst in MySQL format. +func AppendFormatFloat(in []byte, fVal float64, prec, bitSize int) []byte { + absVal := math.Abs(fVal) + if absVal > math.MaxFloat64 || math.IsNaN(absVal) { + return []byte{'0'} + } + isEFormat := false + if bitSize == 32 { + isEFormat = float32(absVal) >= expFormatBig || (float32(absVal) != 0 && float32(absVal) < expFormatSmall) + } else { + isEFormat = absVal >= expFormatBig || (absVal != 0 && absVal < expFormatSmall) + } + var out []byte + if isEFormat { + if bitSize == 32 { + prec = defaultMySQLPrec + } + out = strconv.AppendFloat(in, fVal, 'e', prec, bitSize) + valStr := out[len(in):] + // remove the '+' from the string for compatibility. + plusPos := bytes.IndexByte(valStr, '+') + if plusPos > 0 { + plusPosInOut := len(in) + plusPos + out = append(out[:plusPosInOut], out[plusPosInOut+1:]...) + } + // remove extra '0' + ePos := bytes.IndexByte(valStr, 'e') + pointPos := bytes.IndexByte(valStr, '.') + ePosInOut := len(in) + ePos + pointPosInOut := len(in) + pointPos + validPos := ePosInOut + for i := ePosInOut - 1; i >= pointPosInOut; i-- { + if !(out[i] == '0' || out[i] == '.') { + break + } + validPos = i + } + out = append(out[:validPos], out[ePosInOut:]...) + } else { + out = strconv.AppendFloat(in, fVal, 'f', prec, bitSize) + } + return out +} + +// CorsHandler adds Cors Header if `cors` config is set. +type CorsHandler struct { + handler http.Handler + cfg *config.Config +} + +// NewCorsHandler creates a new CorsHandler. +func NewCorsHandler(handler http.Handler, cfg *config.Config) http.Handler { + return CorsHandler{handler: handler, cfg: cfg} +} + +// ServeHTTP implements http.Handler interface. +func (h CorsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + if h.cfg.Cors != "" { + w.Header().Set("Access-Control-Allow-Origin", h.cfg.Cors) + w.Header().Set("Access-Control-Allow-Methods", "GET") + } + h.handler.ServeHTTP(w, req) +} + +// NewTestConfig creates a new config for test. +func NewTestConfig() *config.Config { + cfg := config.NewConfig() + cfg.Host = "127.0.0.1" + cfg.Status.StatusHost = "127.0.0.1" + cfg.Security.AutoTLS = false + cfg.Socket = "" + return cfg +} diff --git a/server/internal/util/util_test.go b/pkg/server/internal/util/util_test.go similarity index 100% rename from server/internal/util/util_test.go rename to pkg/server/internal/util/util_test.go diff --git a/pkg/server/main_test.go b/pkg/server/main_test.go new file mode 100644 index 0000000000000..08e8e6a13ac56 --- /dev/null +++ b/pkg/server/main_test.go @@ -0,0 +1,84 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package server + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper, 1) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + RunInGoTest = true // flag for NewServer to known it is running in test environment + // Enable TopSQL for all test, and check the resource tag for each RPC request. + // This is used to detect which codes are not tracked by TopSQL. + topsqlstate.EnableTopSQL() + unistore.CheckResourceTagForTopSQLInGoTest = true + + // AsyncCommit will make DDL wait 2.5s before changing to the next state. + // Set schema lease to avoid it from making CI slow. + session.SetSchemaLease(0) + + tikv.EnableFailpoints() + + metrics.RegisterMetrics() + + // sanity check: the global config should not be changed by other pkg init function. + // see also https://github.com/pingcap/tidb/issues/22162 + defaultConfig := config.NewConfig() + globalConfig := config.GetGlobalConfig() + if !reflect.DeepEqual(defaultConfig, globalConfig) { + _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") + _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) + } + testDataMap.LoadTestSuiteData("testdata", "optimizer_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("time.Sleep"), + goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/server.NewServer.func1"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/server/metrics/BUILD.bazel b/pkg/server/metrics/BUILD.bazel new file mode 100644 index 0000000000000..7e84c7ec918d2 --- /dev/null +++ b/pkg/server/metrics/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/server/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/parser/mysql", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/server/metrics/metrics.go b/pkg/server/metrics/metrics.go new file mode 100644 index 0000000000000..c07faadf7616d --- /dev/null +++ b/pkg/server/metrics/metrics.go @@ -0,0 +1,95 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/prometheus/client_golang/prometheus" +) + +// server metrics vars +var ( + QueryTotalCountOk []prometheus.Counter + QueryTotalCountErr []prometheus.Counter + + DisconnectNormal prometheus.Counter + DisconnectByClientWithError prometheus.Counter + DisconnectErrorUndetermined prometheus.Counter + + ConnIdleDurationHistogramNotInTxn prometheus.Observer + ConnIdleDurationHistogramInTxn prometheus.Observer + + AffectedRowsCounterInsert prometheus.Counter + AffectedRowsCounterUpdate prometheus.Counter + AffectedRowsCounterDelete prometheus.Counter + AffectedRowsCounterReplace prometheus.Counter + + ReadPacketBytes prometheus.Counter + WritePacketBytes prometheus.Counter +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init server metrics vars. +func InitMetricsVars() { + QueryTotalCountOk = []prometheus.Counter{ + mysql.ComSleep: metrics.QueryTotalCounter.WithLabelValues("Sleep", "OK"), + mysql.ComQuit: metrics.QueryTotalCounter.WithLabelValues("Quit", "OK"), + mysql.ComInitDB: metrics.QueryTotalCounter.WithLabelValues("InitDB", "OK"), + mysql.ComQuery: metrics.QueryTotalCounter.WithLabelValues("Query", "OK"), + mysql.ComPing: metrics.QueryTotalCounter.WithLabelValues("Ping", "OK"), + mysql.ComFieldList: metrics.QueryTotalCounter.WithLabelValues("FieldList", "OK"), + mysql.ComStmtPrepare: metrics.QueryTotalCounter.WithLabelValues("StmtPrepare", "OK"), + mysql.ComStmtExecute: metrics.QueryTotalCounter.WithLabelValues("StmtExecute", "OK"), + mysql.ComStmtFetch: metrics.QueryTotalCounter.WithLabelValues("StmtFetch", "OK"), + mysql.ComStmtClose: metrics.QueryTotalCounter.WithLabelValues("StmtClose", "OK"), + mysql.ComStmtSendLongData: metrics.QueryTotalCounter.WithLabelValues("StmtSendLongData", "OK"), + mysql.ComStmtReset: metrics.QueryTotalCounter.WithLabelValues("StmtReset", "OK"), + mysql.ComSetOption: metrics.QueryTotalCounter.WithLabelValues("SetOption", "OK"), + } + QueryTotalCountErr = []prometheus.Counter{ + mysql.ComSleep: metrics.QueryTotalCounter.WithLabelValues("Sleep", "Error"), + mysql.ComQuit: metrics.QueryTotalCounter.WithLabelValues("Quit", "Error"), + mysql.ComInitDB: metrics.QueryTotalCounter.WithLabelValues("InitDB", "Error"), + mysql.ComQuery: metrics.QueryTotalCounter.WithLabelValues("Query", "Error"), + mysql.ComPing: metrics.QueryTotalCounter.WithLabelValues("Ping", "Error"), + mysql.ComFieldList: metrics.QueryTotalCounter.WithLabelValues("FieldList", "Error"), + mysql.ComStmtPrepare: metrics.QueryTotalCounter.WithLabelValues("StmtPrepare", "Error"), + mysql.ComStmtExecute: metrics.QueryTotalCounter.WithLabelValues("StmtExecute", "Error"), + mysql.ComStmtFetch: metrics.QueryTotalCounter.WithLabelValues("StmtFetch", "Error"), + mysql.ComStmtClose: metrics.QueryTotalCounter.WithLabelValues("StmtClose", "Error"), + mysql.ComStmtSendLongData: metrics.QueryTotalCounter.WithLabelValues("StmtSendLongData", "Error"), + mysql.ComStmtReset: metrics.QueryTotalCounter.WithLabelValues("StmtReset", "Error"), + mysql.ComSetOption: metrics.QueryTotalCounter.WithLabelValues("SetOption", "Error"), + } + + DisconnectNormal = metrics.DisconnectionCounter.WithLabelValues(metrics.LblOK) + DisconnectByClientWithError = metrics.DisconnectionCounter.WithLabelValues(metrics.LblError) + DisconnectErrorUndetermined = metrics.DisconnectionCounter.WithLabelValues("undetermined") + + ConnIdleDurationHistogramNotInTxn = metrics.ConnIdleDurationHistogram.WithLabelValues("0") + ConnIdleDurationHistogramInTxn = metrics.ConnIdleDurationHistogram.WithLabelValues("1") + + AffectedRowsCounterInsert = metrics.AffectedRowsCounter.WithLabelValues("Insert") + AffectedRowsCounterUpdate = metrics.AffectedRowsCounter.WithLabelValues("Update") + AffectedRowsCounterDelete = metrics.AffectedRowsCounter.WithLabelValues("Delete") + AffectedRowsCounterReplace = metrics.AffectedRowsCounter.WithLabelValues("Replace") + + ReadPacketBytes = metrics.PacketIOCounter.WithLabelValues("read") + WritePacketBytes = metrics.PacketIOCounter.WithLabelValues("write") +} diff --git a/server/mock_conn.go b/pkg/server/mock_conn.go similarity index 90% rename from server/mock_conn.go rename to pkg/server/mock_conn.go index 74bb530ed02ab..b18507eceb4af 100644 --- a/server/mock_conn.go +++ b/pkg/server/mock_conn.go @@ -21,16 +21,16 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/intest" "github.com/stretchr/testify/require" ) diff --git a/server/mock_conn_test.go b/pkg/server/mock_conn_test.go similarity index 94% rename from server/mock_conn_test.go rename to pkg/server/mock_conn_test.go index 3ef07777d8141..da2c69e1bf9e1 100644 --- a/server/mock_conn_test.go +++ b/pkg/server/mock_conn_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/server/rpc_server.go b/pkg/server/rpc_server.go similarity index 93% rename from server/rpc_server.go rename to pkg/server/rpc_server.go index 7de24b76031b5..4145cfbdbec9b 100644 --- a/server/rpc_server.go +++ b/pkg/server/rpc_server.go @@ -25,19 +25,19 @@ import ( "github.com/pingcap/kvproto/pkg/mpp" "github.com/pingcap/kvproto/pkg/tikvpb" "github.com/pingcap/sysutil" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/executor/mppcoordmanager" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/topsql" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/executor/mppcoordmanager" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/topsql" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" diff --git a/pkg/server/server.go b/pkg/server/server.go new file mode 100644 index 0000000000000..e03350cb4dd0c --- /dev/null +++ b/pkg/server/server.go @@ -0,0 +1,1095 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// The MIT License (MIT) +// +// Copyright (c) 2014 wandoulabs +// Copyright (c) 2014 siddontang +// +// 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. + +package server + +import ( + "context" + "crypto/tls" + "fmt" + "io" + "net" + "net/http" //nolint:goimports + _ "net/http/pprof" // #nosec G108 for pprof + "os" + "os/user" + "reflect" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/blacktear23/go-proxyprotocol" + "github.com/pingcap/errors" + autoid "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege/privileges" + servererr "github.com/pingcap/tidb/pkg/server/err" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/fastrand" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sys/linux" + "github.com/pingcap/tidb/pkg/util/timeutil" + uatomic "go.uber.org/atomic" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +var ( + serverPID int + osUser string + osVersion string + // RunInGoTest represents whether we are run code in test. + RunInGoTest bool +) + +func init() { + serverPID = os.Getpid() + currentUser, err := user.Current() + if err != nil { + osUser = "" + } else { + osUser = currentUser.Name + } + osVersion, err = linux.OSVersion() + if err != nil { + osVersion = "" + } +} + +// DefaultCapability is the capability of the server when it is created using the default configuration. +// When server is configured with SSL, the server will have extra capabilities compared to DefaultCapability. +const defaultCapability = mysql.ClientLongPassword | mysql.ClientLongFlag | + mysql.ClientConnectWithDB | mysql.ClientProtocol41 | + mysql.ClientTransactions | mysql.ClientSecureConnection | mysql.ClientFoundRows | + mysql.ClientMultiStatements | mysql.ClientMultiResults | mysql.ClientLocalFiles | + mysql.ClientConnectAtts | mysql.ClientPluginAuth | mysql.ClientInteractive | + mysql.ClientDeprecateEOF | mysql.ClientCompress | mysql.ClientZstdCompressionAlgorithm + +// Server is the MySQL protocol server +type Server struct { + cfg *config.Config + tlsConfig unsafe.Pointer // *tls.Config + driver IDriver + listener net.Listener + socket net.Listener + concurrentLimiter *TokenLimiter + + rwlock sync.RWMutex + clients map[uint64]*clientConn + + capability uint32 + dom *domain.Domain + + statusAddr string + statusListener net.Listener + statusServer *http.Server + grpcServer *grpc.Server + inShutdownMode *uatomic.Bool + health *uatomic.Bool + + sessionMapMutex sync.Mutex + internalSessions map[interface{}]struct{} + autoIDService *autoid.Service + authTokenCancelFunc context.CancelFunc + wg sync.WaitGroup + printMDLLogTime time.Time +} + +// NewTestServer creates a new Server for test. +func NewTestServer(cfg *config.Config) *Server { + return &Server{ + cfg: cfg, + } +} + +// Socket returns the server's socket file. +func (s *Server) Socket() net.Listener { + return s.socket +} + +// Listener returns the server's listener. +func (s *Server) Listener() net.Listener { + return s.listener +} + +// ListenAddr returns the server's listener's network address. +func (s *Server) ListenAddr() net.Addr { + return s.listener.Addr() +} + +// StatusListenerAddr returns the server's status listener's network address. +func (s *Server) StatusListenerAddr() net.Addr { + return s.statusListener.Addr() +} + +// BitwiseXorCapability gets the capability of the server. +func (s *Server) BitwiseXorCapability(capability uint32) { + s.capability ^= capability +} + +// BitwiseOrAssignCapability adds the capability to the server. +func (s *Server) BitwiseOrAssignCapability(capability uint32) { + s.capability |= capability +} + +// GetStatusServerAddr gets statusServer address for MppCoordinatorManager usage +func (s *Server) GetStatusServerAddr() (on bool, addr string) { + if !s.cfg.Status.ReportStatus { + return false, "" + } + if strings.Contains(s.statusAddr, config.DefStatusHost) { + if len(s.cfg.AdvertiseAddress) != 0 { + return true, strings.ReplaceAll(s.statusAddr, config.DefStatusHost, s.cfg.AdvertiseAddress) + } + return false, "" + } + return true, s.statusAddr +} + +// ConnectionCount gets current connection count. +func (s *Server) ConnectionCount() int { + s.rwlock.RLock() + cnt := len(s.clients) + s.rwlock.RUnlock() + return cnt +} + +func (s *Server) getToken() *Token { + start := time.Now() + tok := s.concurrentLimiter.Get() + metrics.TokenGauge.Inc() + // Note that data smaller than one microsecond is ignored, because that case can be viewed as non-block. + metrics.GetTokenDurationHistogram.Observe(float64(time.Since(start).Nanoseconds() / 1e3)) + return tok +} + +func (s *Server) releaseToken(token *Token) { + s.concurrentLimiter.Put(token) + metrics.TokenGauge.Dec() +} + +// SetDomain use to set the server domain. +func (s *Server) SetDomain(dom *domain.Domain) { + s.dom = dom +} + +// newConn creates a new *clientConn from a net.Conn. +// It allocates a connection ID and random salt data for authentication. +func (s *Server) newConn(conn net.Conn) *clientConn { + cc := newClientConn(s) + if tcpConn, ok := conn.(*net.TCPConn); ok { + if err := tcpConn.SetKeepAlive(s.cfg.Performance.TCPKeepAlive); err != nil { + logutil.BgLogger().Error("failed to set tcp keep alive option", zap.Error(err)) + } + if err := tcpConn.SetNoDelay(s.cfg.Performance.TCPNoDelay); err != nil { + logutil.BgLogger().Error("failed to set tcp no delay option", zap.Error(err)) + } + } + cc.setConn(conn) + cc.salt = fastrand.Buf(20) + return cc +} + +// NewServer creates a new Server. +func NewServer(cfg *config.Config, driver IDriver) (*Server, error) { + s := &Server{ + cfg: cfg, + driver: driver, + concurrentLimiter: NewTokenLimiter(cfg.TokenLimit), + clients: make(map[uint64]*clientConn), + internalSessions: make(map[interface{}]struct{}, 100), + health: uatomic.NewBool(true), + inShutdownMode: uatomic.NewBool(false), + printMDLLogTime: time.Now(), + } + s.capability = defaultCapability + setTxnScope() + setSystemTimeZoneVariable() + + tlsConfig, autoReload, err := util.LoadTLSCertificates( + s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert, + s.cfg.Security.AutoTLS, s.cfg.Security.RSAKeySize) + + // LoadTLSCertificates will auto generate certificates if autoTLS is enabled. + // It only returns an error if certificates are specified and invalid. + // In which case, we should halt server startup as a misconfiguration could + // lead to a connection downgrade. + if err != nil { + return nil, errors.Trace(err) + } + + // Automatically reload auto-generated certificates. + // The certificates are re-created every 30 days and are valid for 90 days. + if autoReload { + go func() { + for range time.Tick(time.Hour * 24 * 30) { // 30 days + logutil.BgLogger().Info("Rotating automatically created TLS Certificates") + tlsConfig, _, err = util.LoadTLSCertificates( + s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert, + s.cfg.Security.AutoTLS, s.cfg.Security.RSAKeySize) + if err != nil { + logutil.BgLogger().Warn("TLS Certificate rotation failed", zap.Error(err)) + } + atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(tlsConfig)) + } + }() + } + + if tlsConfig != nil { + setSSLVariable(s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert) + atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(tlsConfig)) + logutil.BgLogger().Info("mysql protocol server secure connection is enabled", + zap.Bool("client verification enabled", len(variable.GetSysVar("ssl_ca").Value) > 0)) + } + if s.tlsConfig != nil { + s.capability |= mysql.ClientSSL + } + + if s.cfg.Host != "" && (s.cfg.Port != 0 || RunInGoTest) { + addr := net.JoinHostPort(s.cfg.Host, strconv.Itoa(int(s.cfg.Port))) + tcpProto := "tcp" + if s.cfg.EnableTCP4Only { + tcpProto = "tcp4" + } + if s.listener, err = net.Listen(tcpProto, addr); err != nil { + return nil, errors.Trace(err) + } + logutil.BgLogger().Info("server is running MySQL protocol", zap.String("addr", addr)) + if RunInGoTest && s.cfg.Port == 0 { + s.cfg.Port = uint(s.listener.Addr().(*net.TCPAddr).Port) + } + } + + if s.cfg.Socket != "" { + if err := cleanupStaleSocket(s.cfg.Socket); err != nil { + return nil, errors.Trace(err) + } + + if s.socket, err = net.Listen("unix", s.cfg.Socket); err != nil { + return nil, errors.Trace(err) + } + logutil.BgLogger().Info("server is running MySQL protocol", zap.String("socket", s.cfg.Socket)) + } + + if s.socket == nil && s.listener == nil { + err = errors.New("Server not configured to listen on either -socket or -host and -port") + return nil, errors.Trace(err) + } + + if s.cfg.ProxyProtocol.Networks != "" { + proxyTarget := s.listener + if proxyTarget == nil { + proxyTarget = s.socket + } + ppListener, err := proxyprotocol.NewLazyListener(proxyTarget, s.cfg.ProxyProtocol.Networks, + int(s.cfg.ProxyProtocol.HeaderTimeout), s.cfg.ProxyProtocol.Fallbackable) + if err != nil { + logutil.BgLogger().Error("ProxyProtocol networks parameter invalid") + return nil, errors.Trace(err) + } + if s.listener != nil { + s.listener = ppListener + logutil.BgLogger().Info("server is running MySQL protocol (through PROXY protocol)", zap.String("host", s.cfg.Host)) + } else { + s.socket = ppListener + logutil.BgLogger().Info("server is running MySQL protocol (through PROXY protocol)", zap.String("socket", s.cfg.Socket)) + } + } + + if s.cfg.Status.ReportStatus { + if err = s.listenStatusHTTPServer(); err != nil { + return nil, errors.Trace(err) + } + } + + // Automatically reload JWKS for tidb_auth_token. + if len(s.cfg.Security.AuthTokenJWKS) > 0 { + var ( + timeInterval time.Duration + err error + ctx context.Context + ) + if timeInterval, err = time.ParseDuration(s.cfg.Security.AuthTokenRefreshInterval); err != nil { + logutil.BgLogger().Error("Fail to parse security.auth-token-refresh-interval. Use default value", + zap.String("security.auth-token-refresh-interval", s.cfg.Security.AuthTokenRefreshInterval)) + timeInterval = config.DefAuthTokenRefreshInterval + } + ctx, s.authTokenCancelFunc = context.WithCancel(context.Background()) + if err = privileges.GlobalJWKS.LoadJWKS4AuthToken(ctx, &s.wg, s.cfg.Security.AuthTokenJWKS, timeInterval); err != nil { + logutil.BgLogger().Error("Fail to load JWKS from the path", zap.String("jwks", s.cfg.Security.AuthTokenJWKS)) + } + } + + variable.RegisterStatistics(s) + + return s, nil +} + +func cleanupStaleSocket(socket string) error { + sockStat, err := os.Stat(socket) + if err != nil { + return nil + } + + if sockStat.Mode().Type() != os.ModeSocket { + return fmt.Errorf( + "the specified socket file %s is a %s instead of a socket file", + socket, sockStat.Mode().String()) + } + + if _, err = net.Dial("unix", socket); err == nil { + return fmt.Errorf("unix socket %s exists and is functional, not removing it", socket) + } + + if err2 := os.Remove(socket); err2 != nil { + return fmt.Errorf("failed to cleanup stale Unix socket file %s: %w", socket, err) + } + + return nil +} + +func setSSLVariable(ca, key, cert string) { + variable.SetSysVar("have_openssl", "YES") + variable.SetSysVar("have_ssl", "YES") + variable.SetSysVar("ssl_cert", cert) + variable.SetSysVar("ssl_key", key) + variable.SetSysVar("ssl_ca", ca) +} + +func setTxnScope() { + variable.SetSysVar(variable.TiDBTxnScope, func() string { + if !variable.EnableLocalTxn.Load() { + return kv.GlobalTxnScope + } + if txnScope := config.GetTxnScopeFromConfig(); txnScope == kv.GlobalTxnScope { + return kv.GlobalTxnScope + } + return kv.LocalTxnScope + }()) +} + +// Export config-related metrics +func (s *Server) reportConfig() { + metrics.ConfigStatus.WithLabelValues("token-limit").Set(float64(s.cfg.TokenLimit)) + metrics.ConfigStatus.WithLabelValues("max_connections").Set(float64(s.cfg.Instance.MaxConnections)) +} + +// Run runs the server. +func (s *Server) Run() error { + metrics.ServerEventCounter.WithLabelValues(metrics.EventStart).Inc() + s.reportConfig() + + // Start HTTP API to report tidb info such as TPS. + if s.cfg.Status.ReportStatus { + s.startStatusHTTP() + } + // If error should be reported and exit the server it can be sent on this + // channel. Otherwise, end with sending a nil error to signal "done" + errChan := make(chan error, 2) + go s.startNetworkListener(s.listener, false, errChan) + go s.startNetworkListener(s.socket, true, errChan) + err := <-errChan + if err != nil { + return err + } + return <-errChan +} + +func (s *Server) startNetworkListener(listener net.Listener, isUnixSocket bool, errChan chan error) { + if listener == nil { + errChan <- nil + return + } + for { + conn, err := listener.Accept() + if err != nil { + if opErr, ok := err.(*net.OpError); ok { + if opErr.Err.Error() == "use of closed network connection" { + if s.inShutdownMode.Load() { + errChan <- nil + } else { + errChan <- err + } + return + } + } + + // If we got PROXY protocol error, we should continue to accept. + if proxyprotocol.IsProxyProtocolError(err) { + logutil.BgLogger().Error("PROXY protocol failed", zap.Error(err)) + continue + } + + logutil.BgLogger().Error("accept failed", zap.Error(err)) + errChan <- err + return + } + + logutil.BgLogger().Debug("accept new connection success") + + clientConn := s.newConn(conn) + if isUnixSocket { + var ( + uc *net.UnixConn + ok bool + ) + if clientConn.ppEnabled { + // Using reflect to get Raw Conn object from proxy protocol wrapper connection object + ppv := reflect.ValueOf(conn) + vconn := ppv.Elem().FieldByName("Conn") + rconn := vconn.Interface() + uc, ok = rconn.(*net.UnixConn) + } else { + uc, ok = conn.(*net.UnixConn) + } + if !ok { + logutil.BgLogger().Error("Expected UNIX socket, but got something else") + return + } + + clientConn.isUnixSocket = true + clientConn.peerHost = "localhost" + clientConn.socketCredUID, err = linux.GetSockUID(*uc) + if err != nil { + logutil.BgLogger().Error("Failed to get UNIX socket peer credentials", zap.Error(err)) + return + } + } + + err = nil + if !clientConn.ppEnabled { + // Check audit plugins when ProxyProtocol not enabled + err = s.checkAuditPlugin(clientConn) + } + if err != nil { + continue + } + + if s.dom != nil && s.dom.IsLostConnectionToPD() { + logutil.BgLogger().Warn("reject connection due to lost connection to PD") + terror.Log(clientConn.Close()) + continue + } + + go s.onConn(clientConn) + } +} + +func (*Server) checkAuditPlugin(clientConn *clientConn) error { + return plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { + authPlugin := plugin.DeclareAuditManifest(p.Manifest) + if authPlugin.OnConnectionEvent == nil { + return nil + } + host, _, err := clientConn.PeerHost("", false) + if err != nil { + logutil.BgLogger().Error("get peer host failed", zap.Error(err)) + terror.Log(clientConn.Close()) + return errors.Trace(err) + } + if err = authPlugin.OnConnectionEvent(context.Background(), plugin.PreAuth, + &variable.ConnectionInfo{Host: host}); err != nil { + logutil.BgLogger().Info("do connection event failed", zap.Error(err)) + terror.Log(clientConn.Close()) + return errors.Trace(err) + } + return nil + }) +} + +func (s *Server) startShutdown() { + logutil.BgLogger().Info("setting tidb-server to report unhealthy (shutting-down)") + s.health.Store(false) + // give the load balancer a chance to receive a few unhealthy health reports + // before acquiring the s.rwlock and blocking connections. + waitTime := time.Duration(s.cfg.GracefulWaitBeforeShutdown) * time.Second + if waitTime > 0 { + logutil.BgLogger().Info("waiting for stray connections before starting shutdown process", zap.Duration("waitTime", waitTime)) + time.Sleep(waitTime) + } +} + +func (s *Server) closeListener() { + if s.listener != nil { + err := s.listener.Close() + terror.Log(errors.Trace(err)) + s.listener = nil + } + if s.socket != nil { + err := s.socket.Close() + terror.Log(errors.Trace(err)) + s.socket = nil + } + if s.statusServer != nil { + err := s.statusServer.Close() + terror.Log(errors.Trace(err)) + s.statusServer = nil + } + if s.grpcServer != nil { + s.grpcServer.Stop() + s.grpcServer = nil + } + if s.autoIDService != nil { + s.autoIDService.Close() + } + if s.authTokenCancelFunc != nil { + s.authTokenCancelFunc() + } + s.wg.Wait() + metrics.ServerEventCounter.WithLabelValues(metrics.EventClose).Inc() +} + +// Close closes the server. +func (s *Server) Close() { + s.startShutdown() + s.rwlock.Lock() // // prevent new connections + defer s.rwlock.Unlock() + s.inShutdownMode.Store(true) + s.closeListener() +} + +func (s *Server) registerConn(conn *clientConn) bool { + s.rwlock.Lock() + defer s.rwlock.Unlock() + connections := len(s.clients) + + logger := logutil.BgLogger() + if s.inShutdownMode.Load() { + logger.Info("close connection directly when shutting down") + terror.Log(closeConn(conn, connections)) + return false + } + s.clients[conn.connectionID] = conn + connections = len(s.clients) + metrics.ConnGauge.Set(float64(connections)) + return true +} + +// onConn runs in its own goroutine, handles queries from this connection. +func (s *Server) onConn(conn *clientConn) { + // init the connInfo + _, _, err := conn.PeerHost("", false) + if err != nil { + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Error("get peer host failed", zap.Error(err)) + terror.Log(conn.Close()) + return + } + + extensions, err := extension.GetExtensions() + if err != nil { + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Error("error in get extensions", zap.Error(err)) + terror.Log(conn.Close()) + return + } + + if sessExtensions := extensions.NewSessionExtensions(); sessExtensions != nil { + conn.extensions = sessExtensions + conn.onExtensionConnEvent(extension.ConnConnected, nil) + defer func() { + conn.onExtensionConnEvent(extension.ConnDisconnected, nil) + }() + } + + ctx := logutil.WithConnID(context.Background(), conn.connectionID) + + if err := conn.handshake(ctx); err != nil { + conn.onExtensionConnEvent(extension.ConnHandshakeRejected, err) + if plugin.IsEnable(plugin.Audit) && conn.getCtx() != nil { + conn.getCtx().GetSessionVars().ConnectionInfo = conn.connectInfo() + err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { + authPlugin := plugin.DeclareAuditManifest(p.Manifest) + if authPlugin.OnConnectionEvent != nil { + pluginCtx := context.WithValue(context.Background(), plugin.RejectReasonCtxValue{}, err.Error()) + return authPlugin.OnConnectionEvent(pluginCtx, plugin.Reject, conn.ctx.GetSessionVars().ConnectionInfo) + } + return nil + }) + terror.Log(err) + } + switch errors.Cause(err) { + case io.EOF: + // `EOF` means the connection is closed normally, we do not treat it as a noticeable error and log it in 'DEBUG' level. + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Debug("EOF", zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) + case servererr.ErrConCount: + if err := conn.writeError(ctx, err); err != nil { + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Warn("error in writing errConCount", zap.Error(err), + zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) + } + default: + metrics.HandShakeErrorCounter.Inc() + logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). + Warn("Server.onConn handshake", zap.Error(err), + zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) + } + terror.Log(conn.Close()) + return + } + + logutil.Logger(ctx).Debug("new connection", zap.String("remoteAddr", conn.bufReadConn.RemoteAddr().String())) + + defer func() { + terror.Log(conn.Close()) + logutil.Logger(ctx).Debug("connection closed") + }() + + if !s.registerConn(conn) { + return + } + + sessionVars := conn.ctx.GetSessionVars() + sessionVars.ConnectionInfo = conn.connectInfo() + conn.onExtensionConnEvent(extension.ConnHandshakeAccepted, nil) + err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { + authPlugin := plugin.DeclareAuditManifest(p.Manifest) + if authPlugin.OnConnectionEvent != nil { + return authPlugin.OnConnectionEvent(context.Background(), plugin.Connected, sessionVars.ConnectionInfo) + } + return nil + }) + if err != nil { + return + } + + connectedTime := time.Now() + conn.Run(ctx) + + err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { + authPlugin := plugin.DeclareAuditManifest(p.Manifest) + if authPlugin.OnConnectionEvent != nil { + sessionVars.ConnectionInfo.Duration = float64(time.Since(connectedTime)) / float64(time.Millisecond) + err := authPlugin.OnConnectionEvent(context.Background(), plugin.Disconnect, sessionVars.ConnectionInfo) + if err != nil { + logutil.BgLogger().Warn("do connection event failed", zap.String("plugin", authPlugin.Name), zap.Error(err)) + } + } + return nil + }) + if err != nil { + return + } +} + +func (cc *clientConn) connectInfo() *variable.ConnectionInfo { + connType := variable.ConnTypeSocket + sslVersion := "" + if cc.isUnixSocket { + connType = variable.ConnTypeUnixSocket + } else if cc.tlsConn != nil { + connType = variable.ConnTypeTLS + sslVersionNum := cc.tlsConn.ConnectionState().Version + switch sslVersionNum { + case tls.VersionTLS10: + sslVersion = "TLSv1.0" + case tls.VersionTLS11: + sslVersion = "TLSv1.1" + case tls.VersionTLS12: + sslVersion = "TLSv1.2" + case tls.VersionTLS13: + sslVersion = "TLSv1.3" + default: + sslVersion = fmt.Sprintf("Unknown TLS version: %d", sslVersionNum) + } + } + connInfo := &variable.ConnectionInfo{ + ConnectionID: cc.connectionID, + ConnectionType: connType, + Host: cc.peerHost, + ClientIP: cc.peerHost, + ClientPort: cc.peerPort, + ServerID: 1, + ServerIP: cc.serverHost, + ServerPort: int(cc.server.cfg.Port), + User: cc.user, + ServerOSLoginUser: osUser, + OSVersion: osVersion, + ServerVersion: mysql.TiDBReleaseVersion, + SSLVersion: sslVersion, + PID: serverPID, + DB: cc.dbname, + AuthMethod: cc.authPlugin, + Attributes: cc.attrs, + } + return connInfo +} + +func (s *Server) checkConnectionCount() error { + // When the value of Instance.MaxConnections is 0, the number of connections is unlimited. + if int(s.cfg.Instance.MaxConnections) == 0 { + return nil + } + + s.rwlock.RLock() + conns := len(s.clients) + s.rwlock.RUnlock() + + if conns >= int(s.cfg.Instance.MaxConnections) { + logutil.BgLogger().Error("too many connections", + zap.Uint32("max connections", s.cfg.Instance.MaxConnections), zap.Error(servererr.ErrConCount)) + return servererr.ErrConCount + } + return nil +} + +// ShowProcessList implements the SessionManager interface. +func (s *Server) ShowProcessList() map[uint64]*util.ProcessInfo { + rs := make(map[uint64]*util.ProcessInfo) + for connID, pi := range s.getUserProcessList() { + rs[connID] = pi + } + if s.dom != nil { + for connID, pi := range s.dom.SysProcTracker().GetSysProcessList() { + rs[connID] = pi + } + } + return rs +} + +func (s *Server) getUserProcessList() map[uint64]*util.ProcessInfo { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + rs := make(map[uint64]*util.ProcessInfo) + for _, client := range s.clients { + if pi := client.ctx.ShowProcess(); pi != nil { + rs[pi.ID] = pi + } + } + return rs +} + +// ShowTxnList shows all txn info for displaying in `TIDB_TRX` +func (s *Server) ShowTxnList() []*txninfo.TxnInfo { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + rs := make([]*txninfo.TxnInfo, 0, len(s.clients)) + for _, client := range s.clients { + if client.ctx.Session != nil { + info := client.ctx.Session.TxnInfo() + if info != nil { + rs = append(rs, info) + } + } + } + return rs +} + +// GetProcessInfo implements the SessionManager interface. +func (s *Server) GetProcessInfo(id uint64) (*util.ProcessInfo, bool) { + s.rwlock.RLock() + conn, ok := s.clients[id] + s.rwlock.RUnlock() + if !ok { + if s.dom != nil { + if pinfo, ok2 := s.dom.SysProcTracker().GetSysProcessList()[id]; ok2 { + return pinfo, true + } + } + return &util.ProcessInfo{}, false + } + return conn.ctx.ShowProcess(), ok +} + +// GetConAttrs returns the connection attributes +func (s *Server) GetConAttrs(user *auth.UserIdentity) map[uint64]map[string]string { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + rs := make(map[uint64]map[string]string) + for _, client := range s.clients { + if user != nil { + if user.Username != client.user { + continue + } + if user.Hostname != client.peerHost { + continue + } + } + if pi := client.ctx.ShowProcess(); pi != nil { + rs[pi.ID] = client.attrs + } + } + return rs +} + +// Kill implements the SessionManager interface. +func (s *Server) Kill(connectionID uint64, query bool, maxExecutionTime bool) { + logutil.BgLogger().Info("kill", zap.Uint64("conn", connectionID), zap.Bool("query", query)) + metrics.ServerEventCounter.WithLabelValues(metrics.EventKill).Inc() + + s.rwlock.RLock() + defer s.rwlock.RUnlock() + conn, ok := s.clients[connectionID] + if !ok && s.dom != nil { + s.dom.SysProcTracker().KillSysProcess(connectionID) + return + } + + if !query { + // Mark the client connection status as WaitShutdown, when clientConn.Run detect + // this, it will end the dispatch loop and exit. + conn.setStatus(connStatusWaitShutdown) + } + killQuery(conn, maxExecutionTime) +} + +// UpdateTLSConfig implements the SessionManager interface. +func (s *Server) UpdateTLSConfig(cfg *tls.Config) { + atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(cfg)) +} + +// GetTLSConfig implements the SessionManager interface. +func (s *Server) GetTLSConfig() *tls.Config { + return (*tls.Config)(atomic.LoadPointer(&s.tlsConfig)) +} + +func killQuery(conn *clientConn, maxExecutionTime bool) { + sessVars := conn.ctx.GetSessionVars() + if maxExecutionTime { + atomic.StoreUint32(&sessVars.Killed, 2) + } else { + atomic.StoreUint32(&sessVars.Killed, 1) + } + conn.mu.RLock() + cancelFunc := conn.mu.cancelFunc + conn.mu.RUnlock() + + if cancelFunc != nil { + cancelFunc() + } + if conn.bufReadConn != nil { + if err := conn.bufReadConn.SetReadDeadline(time.Now()); err != nil { + logutil.BgLogger().Warn("error setting read deadline for kill.", zap.Error(err)) + } + } +} + +// KillSysProcesses kill sys processes such as auto analyze. +func (s *Server) KillSysProcesses() { + if s.dom == nil { + return + } + sysProcTracker := s.dom.SysProcTracker() + for connID := range sysProcTracker.GetSysProcessList() { + sysProcTracker.KillSysProcess(connID) + } +} + +// KillAllConnections implements the SessionManager interface. +// KillAllConnections kills all connections. +func (s *Server) KillAllConnections() { + logutil.BgLogger().Info("kill all connections.", zap.String("category", "server")) + + s.rwlock.RLock() + defer s.rwlock.RUnlock() + for _, conn := range s.clients { + conn.setStatus(connStatusShutdown) + if err := conn.closeWithoutLock(); err != nil { + terror.Log(err) + } + killQuery(conn, false) + } + + s.KillSysProcesses() +} + +// DrainClients drain all connections in drainWait. +// After drainWait duration, we kill all connections still not quit explicitly and wait for cancelWait. +func (s *Server) DrainClients(drainWait time.Duration, cancelWait time.Duration) { + logger := logutil.BgLogger() + logger.Info("start drain clients") + + conns := make(map[uint64]*clientConn) + + s.rwlock.Lock() + for k, v := range s.clients { + conns[k] = v + } + s.rwlock.Unlock() + + allDone := make(chan struct{}) + quitWaitingForConns := make(chan struct{}) + defer close(quitWaitingForConns) + go func() { + defer close(allDone) + for _, conn := range conns { + if !conn.getCtx().GetSessionVars().InTxn() { + continue + } + select { + case <-conn.quit: + case <-quitWaitingForConns: + return + } + } + }() + + select { + case <-allDone: + logger.Info("all sessions quit in drain wait time") + case <-time.After(drainWait): + logger.Info("timeout waiting all sessions quit") + } + + s.KillAllConnections() + + select { + case <-allDone: + case <-time.After(cancelWait): + logger.Warn("some sessions do not quit in cancel wait time") + } +} + +// ServerID implements SessionManager interface. +func (s *Server) ServerID() uint64 { + return s.dom.ServerID() +} + +// GetAutoAnalyzeProcID implements SessionManager interface. +func (s *Server) GetAutoAnalyzeProcID() uint64 { + return s.dom.GetAutoAnalyzeProcID() +} + +// StoreInternalSession implements SessionManager interface. +// @param addr The address of a session.session struct variable +func (s *Server) StoreInternalSession(se interface{}) { + s.sessionMapMutex.Lock() + s.internalSessions[se] = struct{}{} + s.sessionMapMutex.Unlock() +} + +// DeleteInternalSession implements SessionManager interface. +// @param addr The address of a session.session struct variable +func (s *Server) DeleteInternalSession(se interface{}) { + s.sessionMapMutex.Lock() + delete(s.internalSessions, se) + s.sessionMapMutex.Unlock() +} + +// GetInternalSessionStartTSList implements SessionManager interface. +func (s *Server) GetInternalSessionStartTSList() []uint64 { + s.sessionMapMutex.Lock() + defer s.sessionMapMutex.Unlock() + tsList := make([]uint64, 0, len(s.internalSessions)) + analyzeProcID := s.GetAutoAnalyzeProcID() + for se := range s.internalSessions { + if ts, processInfoID := session.GetStartTSFromSession(se); ts != 0 { + if processInfoID == analyzeProcID { + continue + } + tsList = append(tsList, ts) + } + } + return tsList +} + +// InternalSessionExists is used for test +func (s *Server) InternalSessionExists(se interface{}) bool { + s.sessionMapMutex.Lock() + _, ok := s.internalSessions[se] + s.sessionMapMutex.Unlock() + return ok +} + +// setSysTimeZoneOnce is used for parallel run tests. When several servers are running, +// only the first will actually do setSystemTimeZoneVariable, thus we can avoid data race. +var setSysTimeZoneOnce = &sync.Once{} + +func setSystemTimeZoneVariable() { + setSysTimeZoneOnce.Do(func() { + tz, err := timeutil.GetSystemTZ() + if err != nil { + logutil.BgLogger().Error( + "Error getting SystemTZ, use default value instead", + zap.Error(err), + zap.String("default system_time_zone", variable.GetSysVar("system_time_zone").Value)) + return + } + variable.SetSysVar("system_time_zone", tz) + }) +} + +// CheckOldRunningTxn implements SessionManager interface. +func (s *Server) CheckOldRunningTxn(job2ver map[int64]int64, job2ids map[int64]string) { + s.rwlock.RLock() + defer s.rwlock.RUnlock() + + printLog := false + if time.Since(s.printMDLLogTime) > 10*time.Second { + printLog = true + s.printMDLLogTime = time.Now() + } + for _, client := range s.clients { + if client.ctx.Session != nil { + session.RemoveLockDDLJobs(client.ctx.Session, job2ver, job2ids, printLog) + } + } +} + +// KillNonFlashbackClusterConn implements SessionManager interface. +func (s *Server) KillNonFlashbackClusterConn() { + s.rwlock.RLock() + connIDs := make([]uint64, 0, len(s.clients)) + for _, client := range s.clients { + if client.ctx.Session != nil { + processInfo := client.ctx.Session.ShowProcess() + ddl, ok := processInfo.StmtCtx.GetPlan().(*core.DDL) + if !ok { + connIDs = append(connIDs, client.connectionID) + continue + } + _, ok = ddl.Statement.(*ast.FlashBackToTimestampStmt) + if !ok { + connIDs = append(connIDs, client.connectionID) + continue + } + } + } + s.rwlock.RUnlock() + for _, id := range connIDs { + s.Kill(id, false, false) + } +} diff --git a/server/server_test.go b/pkg/server/server_test.go similarity index 88% rename from server/server_test.go rename to pkg/server/server_test.go index 07fd9dcb5ec35..c1b04d04bf6fa 100644 --- a/server/server_test.go +++ b/pkg/server/server_test.go @@ -24,28 +24,28 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/replayer" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/replayer" "github.com/stretchr/testify/require" ) -// cmd: go test -run=^TestOptimizerDebugTrace$ --tags=intest github.com/pingcap/tidb/server +// cmd: go test -run=^TestOptimizerDebugTrace$ --tags=intest github.com/pingcap/tidb/pkg/server // If you want to update the test result, please run the following command: -// cmd: go test -run=^TestOptimizerDebugTrace$ --tags=intest github.com/pingcap/tidb/server --record +// cmd: go test -run=^TestOptimizerDebugTrace$ --tags=intest github.com/pingcap/tidb/pkg/server --record func TestOptimizerDebugTrace(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/SetBindingTimeToZero", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/core/DebugTraceStableStatsTbl", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/SetBindingTimeToZero", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/DebugTraceStableStatsTbl", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/SetBindingTimeToZero")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/core/DebugTraceStableStatsTbl")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/SetBindingTimeToZero")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/DebugTraceStableStatsTbl")) }() store, dom := testkit.CreateMockStoreAndDomain(t) tk := testkit.NewTestKit(t, store) diff --git a/pkg/server/stat.go b/pkg/server/stat.go new file mode 100644 index 0000000000000..ce7e0e41ad055 --- /dev/null +++ b/pkg/server/stat.go @@ -0,0 +1,76 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package server + +import ( + "crypto/x509" + "time" + + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/server/handler/tikvhandler" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +var ( + serverNotAfter = "Ssl_server_not_after" + serverNotBefore = "Ssl_server_not_before" + upTime = "Uptime" +) + +var defaultStatus = map[string]*variable.StatusVal{ + serverNotAfter: {Scope: variable.ScopeGlobal | variable.ScopeSession, Value: ""}, + serverNotBefore: {Scope: variable.ScopeGlobal | variable.ScopeSession, Value: ""}, + upTime: {Scope: variable.ScopeGlobal, Value: 0}, +} + +// GetScope gets the Status variables scope. +func (*Server) GetScope(_ string) variable.ScopeFlag { + return variable.DefaultStatusVarScopeFlag +} + +// Stats returns the server statistics. +func (s *Server) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { + m := make(map[string]interface{}, len(defaultStatus)) + + for name, v := range defaultStatus { + m[name] = v.Value + } + + tlsConfig := s.GetTLSConfig() + if tlsConfig != nil { + if len(tlsConfig.Certificates) == 1 { + pc, err := x509.ParseCertificate(tlsConfig.Certificates[0].Certificate[0]) + if err != nil { + logutil.BgLogger().Error("Failed to parse TLS certficates to get server status", zap.Error(err)) + } else { + m[serverNotAfter] = pc.NotAfter.Format("Jan _2 15:04:05 2006 MST") + m[serverNotBefore] = pc.NotBefore.Format("Jan _2 15:04:05 2006 MST") + } + } + } + + var err error + info := tikvhandler.ServerInfo{} + info.ServerInfo, err = infosync.GetServerInfo() + if err != nil { + logutil.BgLogger().Error("Failed to get ServerInfo for uptime status", zap.Error(err)) + } else { + m[upTime] = int64(time.Since(time.Unix(info.ServerInfo.StartTimestamp, 0)).Seconds()) + } + + return m, nil +} diff --git a/pkg/server/stat_test.go b/pkg/server/stat_test.go new file mode 100644 index 0000000000000..31f7983cd000d --- /dev/null +++ b/pkg/server/stat_test.go @@ -0,0 +1,65 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package server + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/stretchr/testify/require" +) + +func TestUptime(t *testing.T) { + var err error + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/infosync/mockServerInfo", "return(true)")) + defer func() { + err := failpoint.Disable("github.com/pingcap/tidb/pkg/domain/infosync/mockServerInfo") + require.NoError(t, err) + }() + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + + dom, err := session.BootstrapSession(store) + defer func() { + dom.Close() + err := store.Close() + require.NoError(t, err) + }() + require.NoError(t, err) + + _, err = infosync.GlobalInfoSyncerInit(context.Background(), dom.DDL().GetID(), dom.ServerID, dom.GetEtcdClient(), dom.GetEtcdClient(), dom.GetPDClient(), keyspace.CodecV1, true) + require.NoError(t, err) + + tidbdrv := NewTiDBDriver(store) + cfg := util.NewTestConfig() + cfg.Socket = "" + cfg.Port = 0 + cfg.Status.StatusPort = 0 + server, err := NewServer(cfg, tidbdrv) + require.NoError(t, err) + + stats, err := server.Stats(nil) + require.NoError(t, err) + require.GreaterOrEqual(t, stats[upTime].(int64), int64(time.Since(time.Unix(1282967700, 0)).Seconds())) +} diff --git a/server/testdata/optimizer_suite_in.json b/pkg/server/testdata/optimizer_suite_in.json similarity index 100% rename from server/testdata/optimizer_suite_in.json rename to pkg/server/testdata/optimizer_suite_in.json diff --git a/server/testdata/optimizer_suite_out.json b/pkg/server/testdata/optimizer_suite_out.json similarity index 84% rename from server/testdata/optimizer_suite_out.json rename to pkg/server/testdata/optimizer_suite_out.json index 4ec5fac8998a5..db4e392d330ee 100644 --- a/server/testdata/optimizer_suite_out.json +++ b/pkg/server/testdata/optimizer_suite_out.json @@ -20,14 +20,14 @@ } }, { - "github.com/pingcap/tidb/planner.Optimize": [ + "github.com/pingcap/tidb/pkg/planner.Optimize": [ { "Parameter datums for EXECUTE": [ "KindInt64 127" ] }, { - "github.com/pingcap/tidb/planner.Optimize": [ + "github.com/pingcap/tidb/pkg/planner.Optimize": [ { "Enable binding": true, "IsStmtNode": true, @@ -37,15 +37,15 @@ "Used binding": false }, { - "github.com/pingcap/tidb/planner.optimize": { - "github.com/pingcap/tidb/planner/core.DoOptimize": [ + "github.com/pingcap/tidb/pkg/planner.optimize": { + "github.com/pingcap/tidb/pkg/planner/core.DoOptimize": [ { - "github.com/pingcap/tidb/planner/core.logicalOptimize": null + "github.com/pingcap/tidb/pkg/planner/core.logicalOptimize": null }, { - "github.com/pingcap/tidb/planner/core.physicalOptimize": [ + "github.com/pingcap/tidb/pkg/planner/core.physicalOptimize": [ { - "github.com/pingcap/tidb/planner/core.getStatsTable": { + "github.com/pingcap/tidb/pkg/planner/core.getStatsTable": { "CountIsZero": false, "HandleIsNil": false, "InputPhysicalID": 100, @@ -95,10 +95,10 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).DeriveStats": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).DeriveStats": [ { - "github.com/pingcap/tidb/planner/core.(*DataSource).fillIndexPath": { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).fillIndexPath": { + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -106,7 +106,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": true, "IsInvalid": true, "TotalCount": 0 @@ -120,8 +120,8 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveStatsByFilter": { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveStatsByFilter": { + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "lt(test.t.col1, 127)", @@ -129,7 +129,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -137,7 +137,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": false, "InValidForCollPseudo": true, "IsInvalid": true, @@ -152,7 +152,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -160,7 +160,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": true, "IsInvalid": true, "TotalCount": 0 @@ -187,17 +187,17 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": [ { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveTablePathStats": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveTablePathStats": null }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveIndexPathStats": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveIndexPathStats": null } ] }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).generateIndexMergePath": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).generateIndexMergePath": null }, { "Access paths": [ @@ -246,7 +246,7 @@ } }, { - "github.com/pingcap/tidb/planner.Optimize": [ + "github.com/pingcap/tidb/pkg/planner.Optimize": [ { "Enable binding": true, "IsStmtNode": true, @@ -275,15 +275,15 @@ "Trying Hint": "use index ()" }, { - "github.com/pingcap/tidb/planner.optimize": { - "github.com/pingcap/tidb/planner/core.DoOptimize": [ + "github.com/pingcap/tidb/pkg/planner.optimize": { + "github.com/pingcap/tidb/pkg/planner/core.DoOptimize": [ { - "github.com/pingcap/tidb/planner/core.logicalOptimize": null + "github.com/pingcap/tidb/pkg/planner/core.logicalOptimize": null }, { - "github.com/pingcap/tidb/planner/core.physicalOptimize": [ + "github.com/pingcap/tidb/pkg/planner/core.physicalOptimize": [ { - "github.com/pingcap/tidb/planner/core.getStatsTable": { + "github.com/pingcap/tidb/pkg/planner/core.getStatsTable": { "CountIsZero": false, "HandleIsNil": false, "InputPhysicalID": 100, @@ -333,17 +333,17 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).DeriveStats": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).DeriveStats": [ { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveStatsByFilter": { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveStatsByFilter": { + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "eq(test.t.col1, 100)" ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -351,7 +351,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": false, "InValidForCollPseudo": true, "IsInvalid": true, @@ -366,7 +366,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -374,7 +374,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": true, "IsInvalid": true, "TotalCount": 0 @@ -400,12 +400,12 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveTablePathStats": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": { + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveTablePathStats": null } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).generateIndexMergePath": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).generateIndexMergePath": null }, { "Access paths": [ @@ -446,14 +446,14 @@ } }, { - "github.com/pingcap/tidb/planner.Optimize": [ + "github.com/pingcap/tidb/pkg/planner.Optimize": [ { "Parameter datums for EXECUTE": [ "KindInt64 1" ] }, { - "github.com/pingcap/tidb/planner.Optimize": [ + "github.com/pingcap/tidb/pkg/planner.Optimize": [ { "Enable binding": true, "IsStmtNode": true, @@ -463,15 +463,15 @@ "Used binding": false }, { - "github.com/pingcap/tidb/planner.optimize": { - "github.com/pingcap/tidb/planner/core.DoOptimize": [ + "github.com/pingcap/tidb/pkg/planner.optimize": { + "github.com/pingcap/tidb/pkg/planner/core.DoOptimize": [ { - "github.com/pingcap/tidb/planner/core.logicalOptimize": null + "github.com/pingcap/tidb/pkg/planner/core.logicalOptimize": null }, { - "github.com/pingcap/tidb/planner/core.physicalOptimize": [ + "github.com/pingcap/tidb/pkg/planner/core.physicalOptimize": [ { - "github.com/pingcap/tidb/planner/core.getStatsTable": { + "github.com/pingcap/tidb/pkg/planner/core.getStatsTable": { "CountIsZero": false, "HandleIsNil": false, "InputPhysicalID": 100, @@ -521,10 +521,10 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).DeriveStats": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).DeriveStats": [ { - "github.com/pingcap/tidb/planner/core.(*DataSource).fillIndexPath": { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).fillIndexPath": { + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -534,7 +534,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": true, "IsInvalid": true, "TotalCount": 0 @@ -548,15 +548,15 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveStatsByFilter": { - "github.com/pingcap/tidb/planner/cardinality.Selectivity": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveStatsByFilter": { + "github.com/pingcap/tidb/pkg/planner/cardinality.Selectivity": [ { "Input Expressions": [ "in(test.t.col1, 1, 2, 3)" ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByColumnRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByColumnRanges": [ { "ID": 1, "Ranges": [ @@ -566,7 +566,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Column).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Column).IsInvalid": { "EssentialLoaded": false, "InValidForCollPseudo": true, "IsInvalid": true, @@ -581,7 +581,7 @@ ] }, { - "github.com/pingcap/tidb/planner/cardinality.GetRowCountByIndexRanges": [ + "github.com/pingcap/tidb/pkg/planner/cardinality.GetRowCountByIndexRanges": [ { "ID": 1, "Ranges": [ @@ -591,7 +591,7 @@ ] }, { - "github.com/pingcap/tidb/statistics.(*Index).IsInvalid": { + "github.com/pingcap/tidb/pkg/statistics.(*Index).IsInvalid": { "CollPseudo": true, "IsInvalid": true, "TotalCount": 0 @@ -617,17 +617,17 @@ } }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": [ + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).derivePathStatsAndTryHeuristics": [ { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveTablePathStats": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveTablePathStats": null }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).deriveIndexPathStats": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).deriveIndexPathStats": null } ] }, { - "github.com/pingcap/tidb/planner/core.(*DataSource).generateIndexMergePath": null + "github.com/pingcap/tidb/pkg/planner/core.(*DataSource).generateIndexMergePath": null }, { "Access paths": [ diff --git a/pkg/server/tests/BUILD.bazel b/pkg/server/tests/BUILD.bazel new file mode 100644 index 0000000000000..f9226a369d1d0 --- /dev/null +++ b/pkg/server/tests/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tests_test", + timeout = "short", + srcs = [ + "main_test.go", + "tidb_serial_test.go", + "tidb_test.go", + ], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/extension", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/server", + "//pkg/server/internal/column", + "//pkg/server/internal/resultset", + "//pkg/server/internal/testserverclient", + "//pkg/server/internal/testutil", + "//pkg/server/internal/util", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/cpuprofile", + "//pkg/util/plancodec", + "//pkg/util/resourcegrouptag", + "//pkg/util/topsql", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/collector/mock", + "//pkg/util/topsql/state", + "//pkg/util/topsql/stmtstats", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/server/tests/main_test.go b/pkg/server/tests/main_test.go new file mode 100644 index 0000000000000..e757e293d04b4 --- /dev/null +++ b/pkg/server/tests/main_test.go @@ -0,0 +1,73 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "fmt" + "os" + "reflect" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + server.RunInGoTest = true + testsetup.SetupForCommonTest() + topsqlstate.EnableTopSQL() + unistore.CheckResourceTagForTopSQLInGoTest = true + + // AsyncCommit will make DDL wait 2.5s before changing to the next state. + // Set schema lease to avoid it from making CI slow. + session.SetSchemaLease(0) + + tikv.EnableFailpoints() + + metrics.RegisterMetrics() + + // sanity check: the global config should not be changed by other pkg init function. + // see also https://github.com/pingcap/tidb/issues/22162 + defaultConfig := config.NewConfig() + globalConfig := config.GetGlobalConfig() + if !reflect.DeepEqual(defaultConfig, globalConfig) { + _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") + _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("time.Sleep"), + goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/server.NewServer.func1"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/server/tests/tidb_serial_test.go b/pkg/server/tests/tidb_serial_test.go similarity index 97% rename from server/tests/tidb_serial_test.go rename to pkg/server/tests/tidb_serial_test.go index 44fb4c182f74a..8f7e263100528 100644 --- a/server/tests/tidb_serial_test.go +++ b/pkg/server/tests/tidb_serial_test.go @@ -27,15 +27,15 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - util2 "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/config" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + util2 "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) @@ -82,11 +82,11 @@ func TestConfigDefaultValue(t *testing.T) { // Fix issue#22540. Change tidb_dml_batch_size, // then check if load data into table with auto random column works properly. func TestLoadDataAutoRandom(t *testing.T) { - err := failpoint.Enable("github.com/pingcap/tidb/executor/BeforeCommitWork", "sleep(1000)") + err := failpoint.Enable("github.com/pingcap/tidb/pkg/executor/BeforeCommitWork", "sleep(1000)") require.NoError(t, err) defer func() { //nolint:errcheck - _ = failpoint.Disable("github.com/pingcap/tidb/executor/BeforeCommitWork") + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/executor/BeforeCommitWork") }() ts := createTidbTestSuite(t) diff --git a/pkg/server/tests/tidb_test.go b/pkg/server/tests/tidb_test.go new file mode 100644 index 0000000000000..f4f15f5d7ab08 --- /dev/null +++ b/pkg/server/tests/tidb_test.go @@ -0,0 +1,3124 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "database/sql" + "encoding/binary" + "encoding/pem" + "fmt" + "io" + "math/big" + "net" + "net/http" + "os" + "path/filepath" + "strings" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/go-sql-driver/mysql" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + server2 "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/server/internal/column" + "github.com/pingcap/tidb/pkg/server/internal/resultset" + "github.com/pingcap/tidb/pkg/server/internal/testserverclient" + "github.com/pingcap/tidb/pkg/server/internal/testutil" + util2 "github.com/pingcap/tidb/pkg/server/internal/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/topsql" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + mockTopSQLTraceCPU "github.com/pingcap/tidb/pkg/util/topsql/collector/mock" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikvrpc" + "go.opencensus.io/stats/view" +) + +type tidbTestSuite struct { + *testserverclient.TestServerClient + tidbdrv *server2.TiDBDriver + server *server2.Server + domain *domain.Domain + store kv.Storage +} + +func createTidbTestSuite(t *testing.T) *tidbTestSuite { + cfg := util2.NewTestConfig() + cfg.Port = 0 + cfg.Status.ReportStatus = true + cfg.Status.StatusPort = 0 + cfg.Status.RecordDBLabel = true + cfg.Performance.TCPKeepAlive = true + return createTidbTestSuiteWithCfg(t, cfg) +} + +func createTidbTestSuiteWithCfg(t *testing.T, cfg *config.Config) *tidbTestSuite { + ts := &tidbTestSuite{TestServerClient: testserverclient.NewTestServerClient()} + + // setup tidbTestSuite + var err error + ts.store, err = mockstore.NewMockStore() + session.DisableStats4Test() + require.NoError(t, err) + ts.domain, err = session.BootstrapSession(ts.store) + require.NoError(t, err) + ts.tidbdrv = server2.NewTiDBDriver(ts.store) + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + ts.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + ts.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) + ts.server = server + ts.server.SetDomain(ts.domain) + ts.domain.InfoSyncer().SetSessionManager(ts.server) + go func() { + err := ts.server.Run() + require.NoError(t, err) + }() + ts.WaitUntilServerOnline() + + t.Cleanup(func() { + if ts.domain != nil { + ts.domain.Close() + } + if ts.server != nil { + ts.server.Close() + } + if ts.store != nil { + require.NoError(t, ts.store.Close()) + } + view.Stop() + }) + return ts +} + +type tidbTestTopSQLSuite struct { + *tidbTestSuite +} + +func createTidbTestTopSQLSuite(t *testing.T) *tidbTestTopSQLSuite { + base := createTidbTestSuite(t) + + ts := &tidbTestTopSQLSuite{base} + + // Initialize global variable for top-sql test. + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + err := db.Close() + require.NoError(t, err) + }() + + dbt := testkit.NewDBTestKit(t, db) + topsqlstate.GlobalState.PrecisionSeconds.Store(1) + topsqlstate.GlobalState.ReportIntervalSeconds.Store(2) + dbt.MustExec("set @@global.tidb_top_sql_max_time_series_count=5;") + + require.NoError(t, cpuprofile.StartCPUProfiler()) + t.Cleanup(func() { + cpuprofile.StopCPUProfiler() + topsqlstate.GlobalState.PrecisionSeconds.Store(topsqlstate.DefTiDBTopSQLPrecisionSeconds) + topsqlstate.GlobalState.ReportIntervalSeconds.Store(topsqlstate.DefTiDBTopSQLReportIntervalSeconds) + view.Stop() + }) + return ts +} + +func TestRegression(t *testing.T) { + ts := createTidbTestSuite(t) + if testserverclient.Regression { + ts.RunTestRegression(t, nil, "Regression") + } +} + +func TestUint64(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestPrepareResultFieldType(t) +} + +func TestSpecialType(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestSpecialType(t) +} + +func TestPreparedString(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestPreparedString(t) +} + +func TestPreparedTimestamp(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestPreparedTimestamp(t) +} + +func TestConcurrentUpdate(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestConcurrentUpdate(t) +} + +func TestErrorCode(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestErrorCode(t) +} + +func TestAuth(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestAuth(t) + ts.RunTestIssue3682(t) + ts.RunTestAccountLock(t) +} + +func TestIssues(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestIssue3662(t) + ts.RunTestIssue3680(t) + ts.RunTestIssue22646(t) +} + +func TestDBNameEscape(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestDBNameEscape(t) +} + +func TestResultFieldTableIsNull(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestResultFieldTableIsNull(t) +} + +func TestStatusAPI(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestStatusAPI(t) +} + +func TestStatusPort(t *testing.T) { + ts := createTidbTestSuite(t) + + cfg := util2.NewTestConfig() + cfg.Port = 0 + cfg.Status.ReportStatus = true + cfg.Status.StatusPort = ts.StatusPort + cfg.Performance.TCPKeepAlive = true + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.Error(t, err) + require.Nil(t, server) +} + +func TestStatusAPIWithTLS(t *testing.T) { + ts := createTidbTestSuite(t) + + dir := t.TempDir() + + fileName := func(file string) string { + return filepath.Join(dir, file) + } + + caCert, caKey, err := generateCert(0, "TiDB CA 2", nil, nil, fileName("ca-key-2.pem"), fileName("ca-cert-2.pem")) + require.NoError(t, err) + _, _, err = generateCert(1, "tidb-server-2", caCert, caKey, fileName("server-key-2.pem"), fileName("server-cert-2.pem")) + require.NoError(t, err) + + cli := testserverclient.NewTestServerClient() + cli.StatusScheme = "https" + cfg := util2.NewTestConfig() + cfg.Port = cli.Port + cfg.Status.StatusPort = cli.StatusPort + cfg.Security.ClusterSSLCA = fileName("ca-cert-2.pem") + cfg.Security.ClusterSSLCert = fileName("server-cert-2.pem") + cfg.Security.ClusterSSLKey = fileName("server-key-2.pem") + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + + // https connection should work. + ts.RunTestStatusAPI(t) + + // but plain http connection should fail. + cli.StatusScheme = "http" + //nolint:bodyclose + _, err = cli.FetchStatus("/status") + require.Error(t, err) + + server.Close() +} + +func TestStatusAPIWithTLSCNCheck(t *testing.T) { + ts := createTidbTestSuite(t) + + dir := t.TempDir() + + caPath := filepath.Join(dir, "ca-cert-cn.pem") + serverKeyPath := filepath.Join(dir, "server-key-cn.pem") + serverCertPath := filepath.Join(dir, "server-cert-cn.pem") + client1KeyPath := filepath.Join(dir, "client-key-cn-check-a.pem") + client1CertPath := filepath.Join(dir, "client-cert-cn-check-a.pem") + client2KeyPath := filepath.Join(dir, "client-key-cn-check-b.pem") + client2CertPath := filepath.Join(dir, "client-cert-cn-check-b.pem") + + caCert, caKey, err := generateCert(0, "TiDB CA CN CHECK", nil, nil, filepath.Join(dir, "ca-key-cn.pem"), caPath) + require.NoError(t, err) + _, _, err = generateCert(1, "tidb-server-cn-check", caCert, caKey, serverKeyPath, serverCertPath) + require.NoError(t, err) + _, _, err = generateCert(2, "tidb-client-cn-check-a", caCert, caKey, client1KeyPath, client1CertPath, func(c *x509.Certificate) { + c.Subject.CommonName = "tidb-client-1" + }) + require.NoError(t, err) + _, _, err = generateCert(3, "tidb-client-cn-check-b", caCert, caKey, client2KeyPath, client2CertPath, func(c *x509.Certificate) { + c.Subject.CommonName = "tidb-client-2" + }) + require.NoError(t, err) + + cli := testserverclient.NewTestServerClient() + cli.StatusScheme = "https" + cfg := util2.NewTestConfig() + cfg.Port = cli.Port + cfg.Status.StatusPort = cli.StatusPort + cfg.Security.ClusterSSLCA = caPath + cfg.Security.ClusterSSLCert = serverCertPath + cfg.Security.ClusterSSLKey = serverKeyPath + cfg.Security.ClusterVerifyCN = []string{"tidb-client-2"} + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + defer server.Close() + time.Sleep(time.Millisecond * 100) + + hc := newTLSHttpClient(t, caPath, + client1CertPath, + client1KeyPath, + ) + //nolint:bodyclose + _, err = hc.Get(cli.StatusURL("/status")) + require.Error(t, err) + + hc = newTLSHttpClient(t, caPath, + client2CertPath, + client2KeyPath, + ) + resp, err := hc.Get(cli.StatusURL("/status")) + require.NoError(t, err) + require.Nil(t, resp.Body.Close()) +} + +func newTLSHttpClient(t *testing.T, caFile, certFile, keyFile string) *http.Client { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + require.NoError(t, err) + caCert, err := os.ReadFile(caFile) + require.NoError(t, err) + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + InsecureSkipVerify: true, + } + tlsConfig.BuildNameToCertificate() + return &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}} +} + +func TestMultiStatements(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunFailedTestMultiStatements(t) + ts.RunTestMultiStatements(t) +} + +func TestSocketForwarding(t *testing.T) { + tempDir := t.TempDir() + socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK + + ts := createTidbTestSuite(t) + + cli := testserverclient.NewTestServerClient() + cfg := util2.NewTestConfig() + cfg.Socket = socketFile + cfg.Port = cli.Port + os.Remove(cfg.Socket) + cfg.Status.ReportStatus = false + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + defer server.Close() + + cli.RunTestRegression(t, func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.Addr = socketFile + config.DBName = "test" + config.Params = map[string]string{"sql_mode": "'STRICT_ALL_TABLES'"} + }, "SocketRegression") +} + +func TestSocket(t *testing.T) { + tempDir := t.TempDir() + socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK + + cfg := util2.NewTestConfig() + cfg.Socket = socketFile + cfg.Port = 0 + os.Remove(cfg.Socket) + cfg.Host = "" + cfg.Status.ReportStatus = false + + ts := createTidbTestSuite(t) + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + defer server.Close() + + confFunc := func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.Addr = socketFile + config.DBName = "test" + config.Params = map[string]string{"sql_mode": "STRICT_ALL_TABLES"} + } + // a fake server client, config is override, just used to run tests + cli := testserverclient.NewTestServerClient() + cli.WaitUntilCustomServerCanConnect(confFunc) + cli.RunTestRegression(t, confFunc, "SocketRegression") +} + +func TestSocketAndIp(t *testing.T) { + tempDir := t.TempDir() + socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK + + cli := testserverclient.NewTestServerClient() + cfg := util2.NewTestConfig() + cfg.Socket = socketFile + cfg.Port = cli.Port + cfg.Status.ReportStatus = false + + ts := createTidbTestSuite(t) + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + cli.WaitUntilServerCanConnect() + defer server.Close() + + // Test with Socket connection + Setup user1@% for all host access + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + defer func() { + cli.RunTests(t, func(config *mysql.Config) { + config.User = "root" + }, + func(dbt *testkit.DBTestKit) { + dbt.MustExec("DROP USER IF EXISTS 'user1'@'%'") + dbt.MustExec("DROP USER IF EXISTS 'user1'@'localhost'") + dbt.MustExec("DROP USER IF EXISTS 'user1'@'127.0.0.1'") + }) + }() + cli.RunTests(t, func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.Addr = socketFile + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "root@localhost") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + dbt.MustQuery("CREATE USER user1@'%'") + dbt.MustQuery("GRANT SELECT ON test.* TO user1@'%'") + }) + // Test with Network interface connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "user1@127.0.0.1") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") + rows = dbt.MustQuery("select host from information_schema.processlist where user = 'user1'") + records := cli.Rows(t, rows) + require.Contains(t, records[0], ":", "Missing : in is.processlist") + }) + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") + }) + + // Setup user1@127.0.0.1 for loop back network interface access + cli.RunTests(t, func(config *mysql.Config) { + config.User = "root" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "root@127.0.0.1") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + dbt.MustQuery("CREATE USER user1@127.0.0.1") + dbt.MustQuery("GRANT SELECT,INSERT ON test.* TO user1@'127.0.0.1'") + }) + // Test with Network interface connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "user1@127.0.0.1") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'127.0.0.1'\nGRANT SELECT,INSERT ON `test`.* TO 'user1'@'127.0.0.1'") + }) + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") + }) + + // Setup user1@localhost for socket (and if MySQL compatible; loop back network interface access) + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "root" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "root@localhost") + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + dbt.MustExec("CREATE USER user1@localhost") + dbt.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO user1@localhost") + }) + // Test with Network interface connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "user1@127.0.0.1") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'127.0.0.1'\nGRANT SELECT,INSERT ON `test`.* TO 'user1'@'127.0.0.1'") + require.NoError(t, rows.Close()) + }) + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'localhost'\nGRANT SELECT,INSERT,UPDATE,DELETE ON `test`.* TO 'user1'@'localhost'") + require.NoError(t, rows.Close()) + }) +} + +// TestOnlySocket for server configuration without network interface for mysql clients +func TestOnlySocket(t *testing.T) { + tempDir := t.TempDir() + socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK + + cli := testserverclient.NewTestServerClient() + cfg := util2.NewTestConfig() + cfg.Socket = socketFile + cfg.Host = "" // No network interface listening for mysql traffic + cfg.Status.ReportStatus = false + + ts := createTidbTestSuite(t) + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + defer server.Close() + require.Nil(t, server.Listener()) + require.NotNil(t, server.Socket()) + + // Test with Socket connection + Setup user1@% for all host access + defer func() { + cli.RunTests(t, func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.Addr = socketFile + }, + func(dbt *testkit.DBTestKit) { + dbt.MustExec("DROP USER IF EXISTS 'user1'@'%'") + dbt.MustExec("DROP USER IF EXISTS 'user1'@'localhost'") + dbt.MustExec("DROP USER IF EXISTS 'user1'@'127.0.0.1'") + }) + }() + cli.RunTests(t, func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.Addr = socketFile + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "root@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + require.NoError(t, rows.Close()) + dbt.MustExec("CREATE USER user1@'%'") + dbt.MustExec("GRANT SELECT ON test.* TO user1@'%'") + }) + // Test with Network interface connection with all hosts, should fail since server not configured + db, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { + config.User = "root" + config.DBName = "test" + })) + require.NoErrorf(t, err, "Open failed") + err = db.Ping() + require.Errorf(t, err, "Connect succeeded when not configured!?!") + db.Close() + db, err = sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { + config.User = "user1" + config.DBName = "test" + })) + require.NoErrorf(t, err, "Open failed") + err = db.Ping() + require.Errorf(t, err, "Connect succeeded when not configured!?!") + db.Close() + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") + require.NoError(t, rows.Close()) + }) + + // Setup user1@127.0.0.1 for loop back network interface access + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "root" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "root@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + require.NoError(t, rows.Close()) + dbt.MustExec("CREATE USER user1@127.0.0.1") + dbt.MustExec("GRANT SELECT,INSERT ON test.* TO user1@'127.0.0.1'") + }) + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") + require.NoError(t, rows.Close()) + }) + + // Setup user1@localhost for socket (and if MySQL compatible; loop back network interface access) + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "root" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "root@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + require.NoError(t, rows.Close()) + dbt.MustExec("CREATE USER user1@localhost") + dbt.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO user1@localhost") + }) + // Test with unix domain socket file connection with all hosts + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "user1" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "user1@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'localhost'\nGRANT SELECT,INSERT,UPDATE,DELETE ON `test`.* TO 'user1'@'localhost'") + require.NoError(t, rows.Close()) + }) +} + +// generateCert generates a private key and a certificate in PEM format based on parameters. +// If parentCert and parentCertKey is specified, the new certificate will be signed by the parentCert. +// Otherwise, the new certificate will be self-signed and is a CA. +func generateCert(sn int, commonName string, parentCert *x509.Certificate, parentCertKey *rsa.PrivateKey, outKeyFile string, outCertFile string, opts ...func(c *x509.Certificate)) (*x509.Certificate, *rsa.PrivateKey, error) { + privateKey, err := rsa.GenerateKey(rand.Reader, 528) + if err != nil { + return nil, nil, errors.Trace(err) + } + notBefore := time.Now().Add(-10 * time.Minute).UTC() + notAfter := notBefore.Add(1 * time.Hour).UTC() + + template := x509.Certificate{ + SerialNumber: big.NewInt(int64(sn)), + Subject: pkix.Name{CommonName: commonName, Names: []pkix.AttributeTypeAndValue{util.MockPkixAttribute(util.CommonName, commonName)}}, + DNSNames: []string{commonName}, + NotBefore: notBefore, + NotAfter: notAfter, + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + for _, opt := range opts { + opt(&template) + } + + var parent *x509.Certificate + var priv *rsa.PrivateKey + + if parentCert == nil || parentCertKey == nil { + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + parent = &template + priv = privateKey + } else { + parent = parentCert + priv = parentCertKey + } + + derBytes, err := x509.CreateCertificate(rand.Reader, &template, parent, &privateKey.PublicKey, priv) + if err != nil { + return nil, nil, errors.Trace(err) + } + + cert, err := x509.ParseCertificate(derBytes) + if err != nil { + return nil, nil, errors.Trace(err) + } + + certOut, err := os.Create(outCertFile) + if err != nil { + return nil, nil, errors.Trace(err) + } + err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + if err != nil { + return nil, nil, errors.Trace(err) + } + err = certOut.Close() + if err != nil { + return nil, nil, errors.Trace(err) + } + + keyOut, err := os.OpenFile(outKeyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return nil, nil, errors.Trace(err) + } + err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) + if err != nil { + return nil, nil, errors.Trace(err) + } + err = keyOut.Close() + if err != nil { + return nil, nil, errors.Trace(err) + } + + return cert, privateKey, nil +} + +// registerTLSConfig registers a mysql client TLS config. +// See https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig for details. +func registerTLSConfig(configName string, caCertPath string, clientCertPath string, clientKeyPath string, serverName string, verifyServer bool) error { + rootCertPool := x509.NewCertPool() + data, err := os.ReadFile(caCertPath) + if err != nil { + return err + } + if ok := rootCertPool.AppendCertsFromPEM(data); !ok { + return errors.New("Failed to append PEM") + } + clientCert := make([]tls.Certificate, 0, 1) + certs, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath) + if err != nil { + return err + } + clientCert = append(clientCert, certs) + tlsConfig := &tls.Config{ + RootCAs: rootCertPool, + Certificates: clientCert, + ServerName: serverName, + InsecureSkipVerify: !verifyServer, + } + return mysql.RegisterTLSConfig(configName, tlsConfig) +} + +func TestSystemTimeZone(t *testing.T) { + ts := createTidbTestSuite(t) + + tk := testkit.NewTestKit(t, ts.store) + cfg := util2.NewTestConfig() + cfg.Port, cfg.Status.StatusPort = 0, 0 + cfg.Status.ReportStatus = false + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + defer server.Close() + + tz1 := tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'").Rows() + tk.MustQuery("select @@system_time_zone").Check(tz1) +} + +func TestInternalSessionTxnStartTS(t *testing.T) { + ts := createTidbTestSuite(t) + + se, err := session.CreateSession4Test(ts.store) + require.NoError(t, err) + + _, err = se.Execute(context.Background(), "set global tidb_enable_metadata_lock=0") + require.NoError(t, err) + + count := 10 + stmts := make([]ast.StmtNode, count) + for i := 0; i < count; i++ { + stmt, err := session.ParseWithParams4Test(context.Background(), se, "select * from mysql.user limit 1") + require.NoError(t, err) + stmts[i] = stmt + } + // Test an issue that sysSessionPool doesn't call session's Close, cause + // asyncGetTSWorker goroutine leak. + var wg util.WaitGroupWrapper + for i := 0; i < count; i++ { + s := stmts[i] + wg.Run(func() { + _, _, err := session.ExecRestrictedStmt4Test(context.Background(), se, s) + require.NoError(t, err) + }) + } + + wg.Wait() +} + +func TestClientWithCollation(t *testing.T) { + ts := createTidbTestSuite(t) + + ts.RunTestClientWithCollation(t) +} + +func TestCreateTableFlen(t *testing.T) { + ts := createTidbTestSuite(t) + + // issue #4540 + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + _, err = Execute(context.Background(), qctx, "use test;") + require.NoError(t, err) + + ctx := context.Background() + testSQL := "CREATE TABLE `t1` (" + + "`a` char(36) NOT NULL," + + "`b` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + + "`c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + + "`d` varchar(50) DEFAULT ''," + + "`e` char(36) NOT NULL DEFAULT ''," + + "`f` char(36) NOT NULL DEFAULT ''," + + "`g` char(1) NOT NULL DEFAULT 'N'," + + "`h` varchar(100) NOT NULL," + + "`i` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + + "`j` varchar(10) DEFAULT ''," + + "`k` varchar(10) DEFAULT ''," + + "`l` varchar(20) DEFAULT ''," + + "`m` varchar(20) DEFAULT ''," + + "`n` varchar(30) DEFAULT ''," + + "`o` varchar(100) DEFAULT ''," + + "`p` varchar(50) DEFAULT ''," + + "`q` varchar(50) DEFAULT ''," + + "`r` varchar(100) DEFAULT ''," + + "`s` varchar(20) DEFAULT ''," + + "`t` varchar(50) DEFAULT ''," + + "`u` varchar(100) DEFAULT ''," + + "`v` varchar(50) DEFAULT ''," + + "`w` varchar(300) NOT NULL," + + "`x` varchar(250) DEFAULT ''," + + "`y` decimal(20)," + + "`z` decimal(20, 4)," + + "PRIMARY KEY (`a`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" + _, err = Execute(ctx, qctx, testSQL) + require.NoError(t, err) + rs, err := Execute(ctx, qctx, "show create table t1") + require.NoError(t, err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + cols := rs.Columns() + require.NoError(t, err) + require.Len(t, cols, 2) + require.Equal(t, 5*tmysql.MaxBytesOfCharacter, int(cols[0].ColumnLength)) + require.Equal(t, len(req.GetRow(0).GetString(1))*tmysql.MaxBytesOfCharacter, int(cols[1].ColumnLength)) + + // for issue#5246 + rs, err = Execute(ctx, qctx, "select y, z from t1") + require.NoError(t, err) + cols = rs.Columns() + require.Len(t, cols, 2) + require.Equal(t, 21, int(cols[0].ColumnLength)) + require.Equal(t, 22, int(cols[1].ColumnLength)) + rs.Close() +} + +func Execute(ctx context.Context, qc *server2.TiDBContext, sql string) (resultset.ResultSet, error) { + stmts, err := qc.Parse(ctx, sql) + if err != nil { + return nil, err + } + if len(stmts) != 1 { + panic("wrong input for Execute: " + sql) + } + return qc.ExecuteStmt(ctx, stmts[0]) +} + +func TestShowTablesFlen(t *testing.T) { + ts := createTidbTestSuite(t) + + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + ctx := context.Background() + _, err = Execute(ctx, qctx, "use test;") + require.NoError(t, err) + + testSQL := "create table abcdefghijklmnopqrstuvwxyz (i int)" + _, err = Execute(ctx, qctx, testSQL) + require.NoError(t, err) + rs, err := Execute(ctx, qctx, "show tables") + require.NoError(t, err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + require.NoError(t, err) + cols := rs.Columns() + require.NoError(t, err) + require.Len(t, cols, 1) + require.Equal(t, 26*tmysql.MaxBytesOfCharacter, int(cols[0].ColumnLength)) +} + +func checkColNames(t *testing.T, columns []*column.Info, names ...string) { + for i, name := range names { + require.Equal(t, name, columns[i].Name) + require.Equal(t, name, columns[i].OrgName) + } +} + +func TestFieldList(t *testing.T) { + ts := createTidbTestSuite(t) + + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + _, err = Execute(context.Background(), qctx, "use test;") + require.NoError(t, err) + + ctx := context.Background() + testSQL := `create table t ( + c_bit bit(10), + c_int_d int, + c_bigint_d bigint, + c_float_d float, + c_double_d double, + c_decimal decimal(6, 3), + c_datetime datetime(2), + c_time time(3), + c_date date, + c_timestamp timestamp(4) DEFAULT CURRENT_TIMESTAMP(4), + c_char char(20), + c_varchar varchar(20), + c_text_d text, + c_binary binary(20), + c_blob_d blob, + c_set set('a', 'b', 'c'), + c_enum enum('a', 'b', 'c'), + c_json JSON, + c_year year + )` + _, err = Execute(ctx, qctx, testSQL) + require.NoError(t, err) + colInfos, err := qctx.FieldList("t") + require.NoError(t, err) + require.Len(t, colInfos, 19) + + checkColNames(t, colInfos, "c_bit", "c_int_d", "c_bigint_d", "c_float_d", + "c_double_d", "c_decimal", "c_datetime", "c_time", "c_date", "c_timestamp", + "c_char", "c_varchar", "c_text_d", "c_binary", "c_blob_d", "c_set", "c_enum", + "c_json", "c_year") + + for _, cols := range colInfos { + require.Equal(t, "test", cols.Schema) + } + + for _, cols := range colInfos { + require.Equal(t, "t", cols.Table) + } + + for i, col := range colInfos { + switch i { + case 10, 11, 12, 15, 16: + // c_char char(20), c_varchar varchar(20), c_text_d text, + // c_set set('a', 'b', 'c'), c_enum enum('a', 'b', 'c') + require.Equalf(t, uint16(tmysql.CharsetNameToID(tmysql.DefaultCharset)), col.Charset, "index %d", i) + continue + } + + require.Equalf(t, uint16(tmysql.CharsetNameToID("binary")), col.Charset, "index %d", i) + } + + // c_decimal decimal(6, 3) + require.Equal(t, uint8(3), colInfos[5].Decimal) + + // for issue#10513 + tooLongColumnAsName := "COALESCE(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,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,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,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,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,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,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,0,0,0,0)" + columnAsName := tooLongColumnAsName[:tmysql.MaxAliasIdentifierLen] + + rs, err := Execute(ctx, qctx, "select "+tooLongColumnAsName) + require.NoError(t, err) + cols := rs.Columns() + require.Equal(t, "", cols[0].OrgName) + require.Equal(t, columnAsName, cols[0].Name) + rs.Close() + + rs, err = Execute(ctx, qctx, "select c_bit as '"+tooLongColumnAsName+"' from t") + require.NoError(t, err) + cols = rs.Columns() + require.Equal(t, "c_bit", cols[0].OrgName) + require.Equal(t, columnAsName, cols[0].Name) + rs.Close() +} + +func TestClientErrors(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestInfoschemaClientErrors(t) +} + +func TestInitConnect(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestInitConnect(t) +} + +func TestSumAvg(t *testing.T) { + ts := createTidbTestSuite(t) + ts.RunTestSumAvg(t) +} + +func TestNullFlag(t *testing.T) { + ts := createTidbTestSuite(t) + + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + + ctx := context.Background() + { + // issue #9689 + rs, err := Execute(ctx, qctx, "select 1") + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.NotNullFlag | tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } + + { + // issue #19025 + rs, err := Execute(ctx, qctx, "select convert('{}', JSON)") + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } + + { + // issue #18488 + _, err := Execute(ctx, qctx, "use test") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "CREATE TABLE `test` (`iD` bigint(20) NOT NULL, `INT_TEST` int(11) DEFAULT NULL);") + require.NoError(t, err) + rs, err := Execute(ctx, qctx, `SELECT id + int_test as res FROM test GROUP BY res ORDER BY res;`) + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } + + { + rs, err := Execute(ctx, qctx, "select if(1, null, 1) ;") + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } + { + rs, err := Execute(ctx, qctx, "select CASE 1 WHEN 2 THEN 1 END ;") + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } + { + rs, err := Execute(ctx, qctx, "select NULL;") + require.NoError(t, err) + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.BinaryFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) + rs.Close() + } +} + +func TestNO_DEFAULT_VALUEFlag(t *testing.T) { + ts := createTidbTestSuite(t) + + // issue #21465 + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + + ctx := context.Background() + _, err = Execute(ctx, qctx, "use test") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "drop table if exists t") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "create table t(c1 int key, c2 int);") + require.NoError(t, err) + rs, err := Execute(ctx, qctx, "select c1 from t;") + require.NoError(t, err) + defer rs.Close() + cols := rs.Columns() + require.Len(t, cols, 1) + expectFlag := uint16(tmysql.NotNullFlag | tmysql.PriKeyFlag | tmysql.NoDefaultValueFlag) + require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) +} + +func TestGracefulShutdown(t *testing.T) { + ts := createTidbTestSuite(t) + + cli := testserverclient.NewTestServerClient() + cfg := util2.NewTestConfig() + cfg.GracefulWaitBeforeShutdown = 2 // wait before shutdown + cfg.Port = 0 + cfg.Status.StatusPort = 0 + cfg.Status.ReportStatus = true + cfg.Performance.TCPKeepAlive = true + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + require.NotNil(t, server) + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + + resp, err := cli.FetchStatus("/status") // server is up + require.NoError(t, err) + require.Nil(t, resp.Body.Close()) + + go server.Close() + time.Sleep(time.Millisecond * 500) + + resp, _ = cli.FetchStatus("/status") // should return 5xx code + require.Equal(t, 500, resp.StatusCode) + require.Nil(t, resp.Body.Close()) + + time.Sleep(time.Second * 2) + + //nolint:bodyclose + _, err = cli.FetchStatus("/status") // Status is gone + require.Error(t, err) + require.Regexp(t, "connect: connection refused$", err.Error()) +} + +func TestPessimisticInsertSelectForUpdate(t *testing.T) { + ts := createTidbTestSuite(t) + + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + defer qctx.Close() + ctx := context.Background() + _, err = Execute(ctx, qctx, "use test;") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "drop table if exists t1, t2") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "create table t1 (id int)") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "create table t2 (id int)") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "insert into t1 select 1") + require.NoError(t, err) + _, err = Execute(ctx, qctx, "begin pessimistic") + require.NoError(t, err) + rs, err := Execute(ctx, qctx, "INSERT INTO t2 (id) select id from t1 where id = 1 for update") + require.NoError(t, err) + require.Nil(t, rs) // should be no delay +} + +func TestTopSQLCatchRunningSQL(t *testing.T) { + ts := createTidbTestTopSQLSuite(t) + + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + }() + + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("drop database if exists topsql") + dbt.MustExec("create database topsql") + dbt.MustExec("use topsql;") + dbt.MustExec("create table t (a int, b int);") + + for i := 0; i < 5000; i++ { + dbt.MustExec(fmt.Sprintf("insert into t values (%v, %v)", i, i)) + } + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachPlan", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachPlan")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop")) + }() + + mc := mockTopSQLTraceCPU.NewTopSQLCollector() + topsql.SetupTopSQLForTest(mc) + sqlCPUCollector := collector.NewSQLCPUCollector(mc) + sqlCPUCollector.Start() + defer sqlCPUCollector.Stop() + + query := "select count(*) from t as t0 join t as t1 on t0.a != t1.a;" + needEnableTopSQL := int64(0) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for { + select { + case <-ctx.Done(): + return + default: + } + if atomic.LoadInt64(&needEnableTopSQL) == 1 { + time.Sleep(2 * time.Millisecond) + topsqlstate.EnableTopSQL() + atomic.StoreInt64(&needEnableTopSQL, 0) + } + time.Sleep(time.Millisecond) + } + }() + execFn := func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + atomic.StoreInt64(&needEnableTopSQL, 1) + mustQuery(t, dbt, query) + topsqlstate.DisableTopSQL() + } + check := func() { + require.NoError(t, ctx.Err()) + stats := mc.GetSQLStatsBySQLWithRetry(query, true) + require.Greaterf(t, len(stats), 0, query) + } + ts.testCase(t, mc, execFn, check) + cancel() + wg.Wait() +} + +func TestTopSQLCPUProfile(t *testing.T) { + ts := createTidbTestTopSQLSuite(t) + + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + }() + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachSQL", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachPlan", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachSQL")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/topsql/mockHighLoadForEachPlan")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop")) + }() + + topsqlstate.EnableTopSQL() + defer topsqlstate.DisableTopSQL() + + mc := mockTopSQLTraceCPU.NewTopSQLCollector() + topsql.SetupTopSQLForTest(mc) + sqlCPUCollector := collector.NewSQLCPUCollector(mc) + sqlCPUCollector.Start() + defer sqlCPUCollector.Stop() + + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("drop database if exists topsql") + dbt.MustExec("create database topsql") + dbt.MustExec("use topsql;") + dbt.MustExec("create table t (a int auto_increment, b int, unique index idx(a));") + dbt.MustExec("create table t1 (a int auto_increment, b int, unique index idx(a));") + dbt.MustExec("create table t2 (a int auto_increment, b int, unique index idx(a));") + dbt.MustExec("set @@global.tidb_txn_mode = 'pessimistic'") + + checkFn := func(sql, planRegexp string) { + stats := mc.GetSQLStatsBySQLWithRetry(sql, len(planRegexp) > 0) + // since 1 sql may has many plan, check `len(stats) > 0` instead of `len(stats) == 1`. + require.Greaterf(t, len(stats), 0, "sql: "+sql) + + for _, s := range stats { + sqlStr := mc.GetSQL(s.SQLDigest) + encodedPlan := mc.GetPlan(s.PlanDigest) + // Normalize the user SQL before check. + normalizedSQL := parser.Normalize(sql) + require.Equalf(t, normalizedSQL, sqlStr, "sql: %v", sql) + // decode plan before check. + normalizedPlan, err := plancodec.DecodeNormalizedPlan(encodedPlan) + require.NoError(t, err) + // remove '\n' '\t' before do regexp match. + normalizedPlan = strings.Replace(normalizedPlan, "\n", " ", -1) + normalizedPlan = strings.Replace(normalizedPlan, "\t", " ", -1) + require.Regexpf(t, planRegexp, normalizedPlan, "sql: %v", sql) + } + } + + // Test case 1: DML query: insert/update/replace/delete/select + cases1 := []struct { + sql string + planRegexp string + }{ + {sql: "insert into t () values (),(),(),(),(),(),();", planRegexp: ""}, + {sql: "insert into t (b) values (1),(1),(1),(1),(1),(1),(1),(1);", planRegexp: ""}, + {sql: "update t set b=a where b is null limit 1;", planRegexp: ".*Limit.*TableReader.*"}, + {sql: "delete from t where b = a limit 2;", planRegexp: ".*Limit.*TableReader.*"}, + {sql: "replace into t (b) values (1),(1),(1),(1),(1),(1),(1),(1);", planRegexp: ""}, + {sql: "select * from t use index(idx) where a<10;", planRegexp: ".*IndexLookUp.*"}, + {sql: "select * from t ignore index(idx) where a>1000000000;", planRegexp: ".*TableReader.*"}, + {sql: "select /*+ HASH_JOIN(t1, t2) */ * from t t1 join t t2 on t1.a=t2.a where t1.b is not null;", planRegexp: ".*HashJoin.*"}, + {sql: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t t1 join t t2 on t2.a=t1.a where t1.b is not null;", planRegexp: ".*IndexHashJoin.*"}, + {sql: "select * from t where a=1;", planRegexp: ".*Point_Get.*"}, + {sql: "select * from t where a in (1,2,3,4)", planRegexp: ".*Batch_Point_Get.*"}, + } + execFn := func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases1 { + sqlStr := ca.sql + if strings.HasPrefix(sqlStr, "select") { + mustQuery(t, dbt, sqlStr) + } else { + dbt.MustExec(sqlStr) + } + } + } + check := func() { + for _, ca := range cases1 { + checkFn(ca.sql, ca.planRegexp) + } + } + ts.testCase(t, mc, execFn, check) + + // Test case 2: prepare/execute sql + cases2 := []struct { + prepare string + args []interface{} + planRegexp string + }{ + {prepare: "insert into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, + {prepare: "replace into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, + {prepare: "update t1 set b=a where b is null limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, + {prepare: "delete from t1 where b = a limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, + {prepare: "replace into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, + {prepare: "select * from t1 use index(idx) where a?;", args: []interface{}{1000000000}, planRegexp: ".*TableReader.*"}, + {prepare: "select /*+ HASH_JOIN(t1, t2) */ * from t1 t1 join t1 t2 on t1.a=t2.a where t1.b is not null;", args: nil, planRegexp: ".*HashJoin.*"}, + {prepare: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t1 t1 join t1 t2 on t2.a=t1.a where t1.b is not null;", args: nil, planRegexp: ".*IndexHashJoin.*"}, + {prepare: "select * from t1 where a=?;", args: []interface{}{1}, planRegexp: ".*Point_Get.*"}, + {prepare: "select * from t1 where a in (?,?,?,?)", args: []interface{}{1, 2, 3, 4}, planRegexp: ".*Batch_Point_Get.*"}, + } + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases2 { + prepare, args := ca.prepare, ca.args + stmt := dbt.MustPrepare(prepare) + if strings.HasPrefix(prepare, "select") { + rows, err := stmt.Query(args...) + require.NoError(t, err) + for rows.Next() { + } + require.NoError(t, rows.Close()) + } else { + _, err = stmt.Exec(args...) + require.NoError(t, err) + } + } + } + check = func() { + for _, ca := range cases2 { + checkFn(ca.prepare, ca.planRegexp) + } + } + ts.testCase(t, mc, execFn, check) + + // Test case 3: prepare, execute stmt using @val... + cases3 := []struct { + prepare string + args []interface{} + planRegexp string + }{ + {prepare: "insert into t2 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, + {prepare: "update t2 set b=a where b is null limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, + {prepare: "delete from t2 where b = a limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, + {prepare: "replace into t2 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, + {prepare: "select * from t2 use index(idx) where a?;", args: []interface{}{1000000000}, planRegexp: ".*TableReader.*"}, + {prepare: "select /*+ HASH_JOIN(t1, t2) */ * from t2 t1 join t2 t2 on t1.a=t2.a where t1.b is not null;", args: nil, planRegexp: ".*HashJoin.*"}, + {prepare: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t2 t1 join t2 t2 on t2.a=t1.a where t1.b is not null;", args: nil, planRegexp: ".*IndexHashJoin.*"}, + {prepare: "select * from t2 where a=?;", args: []interface{}{1}, planRegexp: ".*Point_Get.*"}, + {prepare: "select * from t2 where a in (?,?,?,?)", args: []interface{}{1, 2, 3, 4}, planRegexp: ".*Batch_Point_Get.*"}, + } + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases3 { + prepare, args := ca.prepare, ca.args + dbt.MustExec(fmt.Sprintf("prepare stmt from '%v'", prepare)) + + var params []string + for i := range args { + param := 'a' + i + dbt.MustExec(fmt.Sprintf("set @%c=%v", param, args[i])) + params = append(params, fmt.Sprintf("@%c", param)) + } + + sqlStr := "execute stmt" + if len(params) > 0 { + sqlStr += " using " + sqlStr += strings.Join(params, ",") + } + if strings.HasPrefix(prepare, "select") { + mustQuery(t, dbt, sqlStr) + } else { + dbt.MustExec(sqlStr) + } + } + } + check = func() { + for _, ca := range cases3 { + checkFn(ca.prepare, ca.planRegexp) + } + } + ts.testCase(t, mc, execFn, check) + + // Test case for other statements + cases4 := []struct { + sql string + plan string + isQuery bool + }{ + {"begin", "", false}, + {"insert into t () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),()", "", false}, + {"commit", "", false}, + {"analyze table t", "", false}, + {"explain analyze select sum(a+b) from t", ".*TableReader.*", true}, + {"trace select sum(b*a), sum(a+b) from t", "", true}, + {"set global tidb_stmt_summary_history_size=5;", "", false}, + } + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases4 { + if ca.isQuery { + mustQuery(t, dbt, ca.sql) + } else { + dbt.MustExec(ca.sql) + } + } + } + check = func() { + for _, ca := range cases4 { + checkFn(ca.sql, ca.plan) + } + // check for internal SQL. + checkFn("replace into mysql.global_variables (variable_name,variable_value) values ('tidb_stmt_summary_history_size', '5')", "") + } + ts.testCase(t, mc, execFn, check) + + // Test case for multi-statement. + cases5 := []string{ + "delete from t limit 1;", + "update t set b=1 where b is null limit 1;", + "select sum(a+b*2) from t;", + } + multiStatement5 := strings.Join(cases5, "") + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("SET tidb_multi_statement_mode='ON'") + dbt.MustExec(multiStatement5) + } + check = func() { + for _, sqlStr := range cases5 { + checkFn(sqlStr, ".*TableReader.*") + } + } + ts.testCase(t, mc, execFn, check) + + // Test case for multi-statement, but first statements execute failed + cases6 := []string{ + "delete from t_not_exist;", + "update t set a=1 where a is null limit 1;", + } + multiStatement6 := strings.Join(cases6, "") + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("SET tidb_multi_statement_mode='ON'") + _, err := db.Exec(multiStatement6) + require.NotNil(t, err) + require.Equal(t, "Error 1146: Table 'topsql.t_not_exist' doesn't exist", err.Error()) + } + check = func() { + for i := 1; i < len(cases6); i++ { + sqlStr := cases6[i] + stats := mc.GetSQLStatsBySQL(sqlStr, false) + require.Equal(t, 0, len(stats), sqlStr) + } + } + ts.testCase(t, mc, execFn, check) + + // Test case for multi-statement, the first statements execute success but the second statement execute failed. + cases7 := []string{ + "update t set a=1 where a <0 limit 1;", + "delete from t_not_exist;", + } + multiStatement7 := strings.Join(cases7, "") + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("SET tidb_multi_statement_mode='ON'") + _, err = db.Exec(multiStatement7) + require.NotNil(t, err) + require.Equal(t, "Error 1146 (42S02): Table 'topsql.t_not_exist' doesn't exist", err.Error()) + } + check = func() { + checkFn(cases7[0], "") // the first statement execute success, should have topsql data. + } + ts.testCase(t, mc, execFn, check) + + // Test case for statement with wrong syntax. + wrongSyntaxSQL := "select * froms t" + execFn = func(db *sql.DB) { + _, err = db.Exec(wrongSyntaxSQL) + require.NotNil(t, err) + require.Regexp(t, "Error 1064: You have an error in your SQL syntax...", err.Error()) + } + check = func() { + stats := mc.GetSQLStatsBySQL(wrongSyntaxSQL, false) + require.Equal(t, 0, len(stats), wrongSyntaxSQL) + } + ts.testCase(t, mc, execFn, check) + + // Test case for high cost of plan optimize. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/mockHighLoadForOptimize", "return")) + selectSQL := "select sum(a+b), count(distinct b) from t where a+b >0" + updateSQL := "update t set a=a+100 where a > 10000000" + selectInPlanSQL := "select * from t where exists (select 1 from t1 where t1.a = 1);" + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + mustQuery(t, dbt, selectSQL) + dbt.MustExec(updateSQL) + mustQuery(t, dbt, selectInPlanSQL) + } + check = func() { + checkFn(selectSQL, "") + checkFn(updateSQL, "") + selectCPUTime := mc.GetSQLCPUTimeBySQL(selectSQL) + updateCPUTime := mc.GetSQLCPUTimeBySQL(updateSQL) + require.Less(t, updateCPUTime, selectCPUTime) + checkFn(selectInPlanSQL, "") + } + ts.testCase(t, mc, execFn, check) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/mockHighLoadForOptimize")) + + // Test case for DDL execute failed but should still have CPU data. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockHighLoadForAddIndex", "return")) + dbt.MustExec(fmt.Sprintf("insert into t values (%v,%v), (%v, %v);", 2000, 1, 2001, 1)) + addIndexStr := "alter table t add unique index idx_b (b)" + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("alter table t drop index if exists idx_b") + _, err := db.Exec(addIndexStr) + require.NotNil(t, err) + require.Equal(t, "Error 1062 (23000): Duplicate entry '1' for key 't.idx_b'", err.Error()) + } + check = func() { + checkFn(addIndexStr, "") + } + ts.testCase(t, mc, execFn, check) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockHighLoadForAddIndex")) + + // Test case for execute failed cause by storage error. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/handleTaskOnceError", `return(true)`)) + execFailedQuery := "select * from t where a*b < 1000" + execFn = func(db *sql.DB) { + _, err = db.Query(execFailedQuery) + require.NotNil(t, err) + require.Equal(t, "Error 1105 (HY000): mock handleTaskOnce error", err.Error()) + } + check = func() { + checkFn(execFailedQuery, "") + } + ts.testCase(t, mc, execFn, check) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/handleTaskOnceError")) +} + +func (ts *tidbTestTopSQLSuite) testCase(t *testing.T, mc *mockTopSQLTraceCPU.TopSQLCollector, execFn func(db *sql.DB), checkFn func()) { + var wg sync.WaitGroup + ctx, cancel := context.WithCancel(context.Background()) + wg.Add(1) + go func() { + defer wg.Done() + ts.loopExec(ctx, t, execFn) + }() + + checkFn() + cancel() + wg.Wait() + mc.Reset() +} + +func mustQuery(t *testing.T, dbt *testkit.DBTestKit, query string) { + rows := dbt.MustQuery(query) + for rows.Next() { + } + err := rows.Close() + require.NoError(t, err) +} + +type mockCollector struct { + f func(data stmtstats.StatementStatsMap) +} + +func newMockCollector(f func(data stmtstats.StatementStatsMap)) stmtstats.Collector { + return &mockCollector{f: f} +} + +func (c *mockCollector) CollectStmtStatsMap(data stmtstats.StatementStatsMap) { + c.f(data) +} + +func waitCollected(ch chan struct{}) { + select { + case <-ch: + case <-time.After(time.Second * 3): + } +} + +func TestTopSQLStatementStats(t *testing.T) { + ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) + + const ExecCountPerSQL = 2 + // Test for CRUD. + cases1 := []string{ + "insert into t values (%d, sleep(0.1))", + "update t set a = %[1]d + 1000 where a = %[1]d and sleep(0.1);", + "select a from t where b = %d and sleep(0.1);", + "select a from t where a = %d and sleep(0.1);", // test for point-get + "delete from t where a = %d and sleep(0.1);", + "insert into t values (%d, sleep(0.1)) on duplicate key update b = b+1", + } + var wg sync.WaitGroup + sqlDigests := map[stmtstats.BinaryDigest]string{} + for i, ca := range cases1 { + sqlStr := fmt.Sprintf(ca, i) + _, digest := parser.NormalizeDigest(sqlStr) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = sqlStr + } + wg.Add(1) + go func() { + defer wg.Done() + for _, ca := range cases1 { + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use stmtstats;") + for n := 0; n < ExecCountPerSQL; n++ { + sqlStr := fmt.Sprintf(ca, n) + if strings.HasPrefix(strings.ToLower(sqlStr), "select") { + mustQuery(t, dbt, sqlStr) + } else { + dbt.MustExec(sqlStr) + } + } + err = db.Close() + require.NoError(t, err) + } + }() + + // Test for prepare stmt/execute stmt + cases2 := []struct { + prepare string + execStmt string + setSQLsGen func(idx int) []string + execSQL string + }{ + { + prepare: "prepare stmt from 'insert into t2 values (?, sleep(?))';", + execStmt: "insert into t2 values (1, sleep(0.1))", + setSQLsGen: func(idx int) []string { + return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + prepare: "prepare stmt from 'update t2 set a = a + 1000 where a = ? and sleep(?);';", + execStmt: "update t2 set a = a + 1000 where a = 1 and sleep(0.1);", + setSQLsGen: func(idx int) []string { + return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + // test for point-get + prepare: "prepare stmt from 'select a, sleep(?) from t2 where a = ?';", + execStmt: "select a, sleep(?) from t2 where a = ?", + setSQLsGen: func(idx int) []string { + return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + prepare: "prepare stmt from 'select a, sleep(?) from t2 where b = ?';", + execStmt: "select a, sleep(?) from t2 where b = ?", + setSQLsGen: func(idx int) []string { + return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + prepare: "prepare stmt from 'delete from t2 where sleep(?) and a = ?';", + execStmt: "delete from t2 where sleep(0.1) and a = 1", + setSQLsGen: func(idx int) []string { + return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + prepare: "prepare stmt from 'insert into t2 values (?, sleep(?)) on duplicate key update b = b+1';", + execStmt: "insert into t2 values (1, sleep(0.1)) on duplicate key update b = b+1", + setSQLsGen: func(idx int) []string { + return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} + }, + execSQL: "execute stmt using @a, @b;", + }, + { + prepare: "prepare stmt from 'set global tidb_enable_top_sql = (? = sleep(?))';", + execStmt: "set global tidb_enable_top_sql = (0 = sleep(0.1))", + setSQLsGen: func(idx int) []string { + return []string{"set @a=0", "set @b=0.1"} + }, + execSQL: "execute stmt using @a, @b;", + }, + } + for _, ca := range cases2 { + _, digest := parser.NormalizeDigest(ca.execStmt) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.execStmt + } + wg.Add(1) + go func() { + defer wg.Done() + for _, ca := range cases2 { + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use stmtstats;") + // prepare stmt + dbt.MustExec(ca.prepare) + for n := 0; n < ExecCountPerSQL; n++ { + setSQLs := ca.setSQLsGen(n) + for _, setSQL := range setSQLs { + dbt.MustExec(setSQL) + } + if strings.HasPrefix(strings.ToLower(ca.execStmt), "select") { + mustQuery(t, dbt, ca.execSQL) + } else { + dbt.MustExec(ca.execSQL) + } + } + err = db.Close() + require.NoError(t, err) + } + }() + + // Test for prepare by db client prepare/exec interface. + cases3 := []struct { + prepare string + execStmt string + argsGen func(idx int) []interface{} + }{ + { + prepare: "insert into t3 values (?, sleep(?))", + argsGen: func(idx int) []interface{} { + return []interface{}{idx, 0.1} + }, + }, + { + prepare: "update t3 set a = a + 1000 where a = ? and sleep(?)", + argsGen: func(idx int) []interface{} { + return []interface{}{idx, 0.1} + }, + }, + { + // test for point-get + prepare: "select a, sleep(?) from t3 where a = ?", + argsGen: func(idx int) []interface{} { + return []interface{}{0.1, idx} + }, + }, + { + prepare: "select a, sleep(?) from t3 where b = ?", + argsGen: func(idx int) []interface{} { + return []interface{}{0.1, idx} + }, + }, + { + prepare: "delete from t3 where sleep(?) and a = ?", + argsGen: func(idx int) []interface{} { + return []interface{}{0.1, idx} + }, + }, + { + prepare: "insert into t3 values (?, sleep(?)) on duplicate key update b = b+1", + argsGen: func(idx int) []interface{} { + return []interface{}{idx, 0.1} + }, + }, + { + prepare: "set global tidb_enable_1pc = (? = sleep(?))", + argsGen: func(idx int) []interface{} { + return []interface{}{0, 0.1} + }, + }, + } + for _, ca := range cases3 { + _, digest := parser.NormalizeDigest(ca.prepare) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.prepare + } + wg.Add(1) + go func() { + defer wg.Done() + for _, ca := range cases3 { + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use stmtstats;") + // prepare stmt + stmt, err := db.Prepare(ca.prepare) + require.NoError(t, err) + for n := 0; n < ExecCountPerSQL; n++ { + args := ca.argsGen(n) + if strings.HasPrefix(strings.ToLower(ca.prepare), "select") { + row, err := stmt.Query(args...) + require.NoError(t, err) + err = row.Close() + require.NoError(t, err) + } else { + _, err := stmt.Exec(args...) + require.NoError(t, err) + } + } + err = db.Close() + require.NoError(t, err) + } + }() + + wg.Wait() + // Wait for collect. + waitCollected(collectedNotifyCh) + + found := 0 + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + found++ + require.Equal(t, uint64(ExecCountPerSQL), item.ExecCount, sqlStr) + require.Equal(t, uint64(ExecCountPerSQL), item.DurationCount, sqlStr) + require.True(t, item.SumDurationNs > uint64(time.Millisecond*100*ExecCountPerSQL), sqlStr) + require.True(t, item.SumDurationNs < uint64(time.Millisecond*300*ExecCountPerSQL), sqlStr) + if strings.HasPrefix(sqlStr, "set global") { + // set global statement use internal SQL to change global variable, so itself doesn't have KV request. + continue + } + var kvSum uint64 + for _, kvCount := range item.KvStatsItem.KvExecCount { + kvSum += kvCount + } + require.Equal(t, uint64(ExecCountPerSQL), kvSum) + tagChecker.checkExist(t, digest.SQLDigest, sqlStr) + } + } + require.Equal(t, len(sqlDigests), found) + require.Equal(t, 20, found) +} + +type resourceTagChecker struct { + sync.Mutex + sqlDigest2Reqs map[stmtstats.BinaryDigest]map[tikvrpc.CmdType]struct{} +} + +func (c *resourceTagChecker) checkExist(t *testing.T, digest stmtstats.BinaryDigest, sqlStr string) { + if strings.HasPrefix(sqlStr, "set global") { + // `set global` statement will use another internal sql to execute, so `set global` statement won't + // send RPC request. + return + } + if strings.HasPrefix(sqlStr, "trace") { + // `trace` statement will use another internal sql to execute, so remove the `trace` prefix before check. + _, sqlDigest := parser.NormalizeDigest(strings.TrimPrefix(sqlStr, "trace")) + digest = stmtstats.BinaryDigest(sqlDigest.Bytes()) + } + + c.Lock() + defer c.Unlock() + _, ok := c.sqlDigest2Reqs[digest] + require.True(t, ok, sqlStr) +} + +func (c *resourceTagChecker) checkReqExist(t *testing.T, digest stmtstats.BinaryDigest, sqlStr string, reqs ...tikvrpc.CmdType) { + if len(reqs) == 0 { + return + } + c.Lock() + defer c.Unlock() + reqMap, ok := c.sqlDigest2Reqs[digest] + require.True(t, ok, sqlStr) + for _, req := range reqs { + _, ok := reqMap[req] + require.True(t, ok, fmt.Sprintf("sql: %v, expect: %v, got: %v", sqlStr, reqs, reqMap)) + } +} + +func setupForTestTopSQLStatementStats(t *testing.T) (*tidbTestSuite, stmtstats.StatementStatsMap, *resourceTagChecker, chan struct{}) { + // Prepare stmt stats. + stmtstats.SetupAggregator() + + // Register stmt stats collector. + var mu sync.Mutex + collectedNotifyCh := make(chan struct{}) + total := stmtstats.StatementStatsMap{} + mockCollector := newMockCollector(func(data stmtstats.StatementStatsMap) { + mu.Lock() + defer mu.Unlock() + total.Merge(data) + select { + case collectedNotifyCh <- struct{}{}: + default: + } + }) + stmtstats.RegisterCollector(mockCollector) + + ts := createTidbTestSuite(t) + + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + err := db.Close() + require.NoError(t, err) + }() + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCClientSendHook", `return(true)`)) + + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("drop database if exists stmtstats") + dbt.MustExec("create database stmtstats") + dbt.MustExec("use stmtstats;") + dbt.MustExec("create table t (a int, b int, unique index idx(a));") + dbt.MustExec("create table t2 (a int, b int, unique index idx(a));") + dbt.MustExec("create table t3 (a int, b int, unique index idx(a));") + + // Enable TopSQL + topsqlstate.EnableTopSQL() + config.UpdateGlobal(func(conf *config.Config) { + conf.TopSQL.ReceiverAddress = "mock-agent" + }) + + tagChecker := &resourceTagChecker{ + sqlDigest2Reqs: make(map[stmtstats.BinaryDigest]map[tikvrpc.CmdType]struct{}), + } + unistoreRPCClientSendHook := func(req *tikvrpc.Request) { + tag := req.GetResourceGroupTag() + if len(tag) == 0 || ddlutil.IsInternalResourceGroupTaggerForTopSQL(tag) { + // Ignore for internal background request. + return + } + sqlDigest, err := resourcegrouptag.DecodeResourceGroupTag(tag) + require.NoError(t, err) + tagChecker.Lock() + defer tagChecker.Unlock() + + reqMap, ok := tagChecker.sqlDigest2Reqs[stmtstats.BinaryDigest(sqlDigest)] + if !ok { + reqMap = make(map[tikvrpc.CmdType]struct{}) + } + reqMap[req.Type] = struct{}{} + tagChecker.sqlDigest2Reqs[stmtstats.BinaryDigest(sqlDigest)] = reqMap + } + unistore.UnistoreRPCClientSendHook.Store(&unistoreRPCClientSendHook) + + t.Cleanup(func() { + stmtstats.UnregisterCollector(mockCollector) + err = failpoint.Disable("github.com/pingcap/tidb/pkg/domain/skipLoadSysVarCacheLoop") + require.NoError(t, err) + err = failpoint.Disable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/unistoreRPCClientSendHook") + require.NoError(t, err) + stmtstats.CloseAggregator() + view.Stop() + }) + + return ts, total, tagChecker, collectedNotifyCh +} + +func TestTopSQLStatementStats2(t *testing.T) { + ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) + + const ExecCountPerSQL = 3 + sqlDigests := map[stmtstats.BinaryDigest]string{} + + // Test case for other statements + cases4 := []struct { + sql string + plan string + isQuery bool + }{ + {"insert into t () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),()", "", false}, + {"analyze table t", "", false}, + {"explain analyze select sum(a+b) from t", ".*TableReader.*", true}, + {"trace select sum(b*a), sum(a+b) from t", "", true}, + {"set global tidb_stmt_summary_history_size=5;", "", false}, + {"select * from stmtstats.t where exists (select 1 from stmtstats.t2 where t2.a = 1);", ".*TableReader.*", true}, + } + executeCaseFn := func(execFn func(db *sql.DB)) { + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use stmtstats;") + require.NoError(t, err) + + for n := 0; n < ExecCountPerSQL; n++ { + execFn(db) + } + err = db.Close() + require.NoError(t, err) + } + execFn := func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases4 { + if ca.isQuery { + mustQuery(t, dbt, ca.sql) + } else { + dbt.MustExec(ca.sql) + } + } + } + for _, ca := range cases4 { + _, digest := parser.NormalizeDigest(ca.sql) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.sql + } + executeCaseFn(execFn) + + // Test case for multi-statement. + cases5 := []string{ + "delete from t limit 1;", + "update t set b=1 where b is null limit 1;", + "select sum(a+b*2) from t;", + } + multiStatement5 := strings.Join(cases5, "") + // Test case for multi-statement, but first statements execute failed + cases6 := []string{ + "delete from t6_not_exist;", + "update t set a=1 where a is null limit 1;", + } + multiStatement6 := strings.Join(cases6, "") + // Test case for multi-statement, the first statements execute success but the second statement execute failed. + cases7 := []string{ + "update t set a=1 where a <0 limit 1;", + "delete from t7_not_exist;", + } + // Test case for DDL. + cases8 := []string{ + "create table if not exists t10 (a int, b int)", + "alter table t drop index if exists idx_b", + "alter table t add index idx_b (b)", + } + multiStatement7 := strings.Join(cases7, "") + execFn = func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("SET tidb_multi_statement_mode='ON'") + dbt.MustExec(multiStatement5) + + _, err := db.Exec(multiStatement6) + require.NotNil(t, err) + require.Equal(t, "Error 1146 (42S02): Table 'stmtstats.t6_not_exist' doesn't exist", err.Error()) + + _, err = db.Exec(multiStatement7) + require.NotNil(t, err) + require.Equal(t, "Error 1146 (42S02): Table 'stmtstats.t7_not_exist' doesn't exist", err.Error()) + + for _, ca := range cases8 { + dbt.MustExec(ca) + } + } + executeCaseFn(execFn) + sqlStrs := append([]string{}, cases5...) + sqlStrs = append(sqlStrs, cases7[0]) + sqlStrs = append(sqlStrs, cases8...) + for _, sqlStr := range sqlStrs { + _, digest := parser.NormalizeDigest(sqlStr) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = sqlStr + } + + // Wait for collect. + waitCollected(collectedNotifyCh) + + foundMap := map[stmtstats.BinaryDigest]string{} + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + require.Equal(t, uint64(ExecCountPerSQL), item.ExecCount, sqlStr) + require.True(t, item.SumDurationNs > 1, sqlStr) + foundMap[digest.SQLDigest] = sqlStr + tagChecker.checkExist(t, digest.SQLDigest, sqlStr) + // The special check uses to test the issue #33202. + if strings.Contains(strings.ToLower(sqlStr), "add index") { + tagChecker.checkReqExist(t, digest.SQLDigest, sqlStr, tikvrpc.CmdScan) + } + } + } + require.Equal(t, len(sqlDigests), len(foundMap), fmt.Sprintf("%v !=\n %v", sqlDigests, foundMap)) +} + +func TestTopSQLStatementStats3(t *testing.T) { + ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) + + err := failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockSleepInTableReaderNext", "return(2000)") + require.NoError(t, err) + defer func() { + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockSleepInTableReaderNext") + }() + + cases := []string{ + "select count(a+b) from stmtstats.t", + "select * from stmtstats.t where b is null", + "update stmtstats.t set b = 1 limit 10", + "delete from stmtstats.t limit 1", + } + var wg sync.WaitGroup + sqlDigests := map[stmtstats.BinaryDigest]string{} + for _, ca := range cases { + wg.Add(1) + go func(sqlStr string) { + defer wg.Done() + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + require.NoError(t, err) + if strings.HasPrefix(sqlStr, "select") { + mustQuery(t, dbt, sqlStr) + } else { + dbt.MustExec(sqlStr) + } + err = db.Close() + require.NoError(t, err) + }(ca) + _, digest := parser.NormalizeDigest(ca) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca + } + // Wait for collect. + waitCollected(collectedNotifyCh) + + foundMap := map[stmtstats.BinaryDigest]string{} + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + // since the SQL doesn't execute finish, the ExecCount should be recorded, + // but the DurationCount and SumDurationNs should be 0. + require.Equal(t, uint64(1), item.ExecCount, sqlStr) + require.Equal(t, uint64(0), item.DurationCount, sqlStr) + require.Equal(t, uint64(0), item.SumDurationNs, sqlStr) + foundMap[digest.SQLDigest] = sqlStr + } + } + + // wait sql execute finish. + wg.Wait() + // Wait for collect. + waitCollected(collectedNotifyCh) + + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + require.Equal(t, uint64(1), item.ExecCount, sqlStr) + require.Equal(t, uint64(1), item.DurationCount, sqlStr) + require.Less(t, uint64(0), item.SumDurationNs, sqlStr) + foundMap[digest.SQLDigest] = sqlStr + tagChecker.checkExist(t, digest.SQLDigest, sqlStr) + } + } +} + +func TestTopSQLStatementStats4(t *testing.T) { + ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) + + err := failpoint.Enable("github.com/pingcap/tidb/pkg/executor/mockSleepInTableReaderNext", "return(2000)") + require.NoError(t, err) + defer func() { + _ = failpoint.Disable("github.com/pingcap/tidb/pkg/executor/mockSleepInTableReaderNext") + }() + + cases := []struct { + prepare string + sql string + args []interface{} + }{ + {prepare: "select count(a+b) from stmtstats.t", sql: "select count(a+b) from stmtstats.t"}, + {prepare: "select * from stmtstats.t where b is null", sql: "select * from stmtstats.t where b is null"}, + {prepare: "update stmtstats.t set b = ? limit ?", sql: "update stmtstats.t set b = 1 limit 10", args: []interface{}{1, 10}}, + {prepare: "delete from stmtstats.t limit ?", sql: "delete from stmtstats.t limit 1", args: []interface{}{1}}, + } + var wg sync.WaitGroup + sqlDigests := map[stmtstats.BinaryDigest]string{} + for _, ca := range cases { + wg.Add(1) + go func(prepare string, args []interface{}) { + defer wg.Done() + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + stmt, err := db.Prepare(prepare) + require.NoError(t, err) + if strings.HasPrefix(prepare, "select") { + rows, err := stmt.Query(args...) + require.NoError(t, err) + for rows.Next() { + } + err = rows.Close() + require.NoError(t, err) + } else { + _, err := stmt.Exec(args...) + require.NoError(t, err) + } + err = db.Close() + require.NoError(t, err) + }(ca.prepare, ca.args) + _, digest := parser.NormalizeDigest(ca.sql) + sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.sql + } + // Wait for collect. + waitCollected(collectedNotifyCh) + + foundMap := map[stmtstats.BinaryDigest]string{} + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + // since the SQL doesn't execute finish, the ExecCount should be recorded, + // but the DurationCount and SumDurationNs should be 0. + require.Equal(t, uint64(1), item.ExecCount, sqlStr) + require.Equal(t, uint64(0), item.DurationCount, sqlStr) + require.Equal(t, uint64(0), item.SumDurationNs, sqlStr) + foundMap[digest.SQLDigest] = sqlStr + } + } + + // wait sql execute finish. + wg.Wait() + // Wait for collect. + waitCollected(collectedNotifyCh) + + for digest, item := range total { + if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { + require.Equal(t, uint64(1), item.ExecCount, sqlStr) + require.Equal(t, uint64(1), item.DurationCount, sqlStr) + require.Less(t, uint64(0), item.SumDurationNs, sqlStr) + foundMap[digest.SQLDigest] = sqlStr + tagChecker.checkExist(t, digest.SQLDigest, sqlStr) + } + } +} + +func TestTopSQLResourceTag(t *testing.T) { + ts, _, tagChecker, _ := setupForTestTopSQLStatementStats(t) + defer func() { + topsqlstate.DisableTopSQL() + }() + + loadDataFile, err := os.CreateTemp("", "load_data_test0.csv") + require.NoError(t, err) + defer func() { + path := loadDataFile.Name() + err = loadDataFile.Close() + require.NoError(t, err) + err = os.Remove(path) + require.NoError(t, err) + }() + _, err = loadDataFile.WriteString( + "31 31\n" + + "32 32\n" + + "33 33\n") + require.NoError(t, err) + + // Test case for other statements + cases := []struct { + sql string + isQuery bool + reqs []tikvrpc.CmdType + }{ + // Test for curd. + {"insert into t values (1,1), (3,3)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"insert into t values (1,2) on duplicate key update a = 2", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, + {"update t set b=b+1 where a=3", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdGet}}, + {"update t set b=b+1 where a>1", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdCop}}, + {"delete from t where a=3", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdGet}}, + {"delete from t where a>1", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdCop}}, + {"insert ignore into t values (2,2), (3,3)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, + {"select * from t where a in (1,2,3,4)", true, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, + {"select * from t where a = 1", true, []tikvrpc.CmdType{tikvrpc.CmdGet}}, + {"select * from t where b > 0", true, []tikvrpc.CmdType{tikvrpc.CmdCop}}, + {"replace into t values (2,2), (4,4)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, + + // Test for DDL + {"create database test_db0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"create table test_db0.test_t0 (a int, b int, index idx(a))", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"create table test_db0.test_t1 (a int, b int, index idx(a))", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"alter table test_db0.test_t0 add column c int", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"drop table test_db0.test_t0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"drop database test_db0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + {"alter table t modify column b double", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdScan, tikvrpc.CmdCop}}, + {"alter table t add index idx2 (b,a)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdScan, tikvrpc.CmdCop}}, + {"alter table t drop index idx2", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + + // Test for transaction + {"begin", false, nil}, + {"insert into t2 values (10,10), (11,11)", false, nil}, + {"insert ignore into t2 values (20,20), (21,21)", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, + {"commit", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, + + // Test for other statements. + {"set @@global.tidb_enable_1pc = 1", false, nil}, + {fmt.Sprintf("load data local infile %q into table t2", loadDataFile.Name()), false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, + {"admin check table t", false, nil}, + {"admin check index t idx", false, nil}, + {"admin recover index t idx", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, + {"admin cleanup index t idx", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, + } + + internalCases := []struct { + sql string + reqs []tikvrpc.CmdType + }{ + {"replace into mysql.global_variables (variable_name,variable_value) values ('tidb_enable_1pc', '1')", []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, + {"select /*+ read_from_storage(tikv[`stmtstats`.`t`]) */ bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index() where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, + {"select bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index(`idx`) where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, + {"select /*+ read_from_storage(tikv[`stmtstats`.`t`]) */ bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index() where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, + {"select bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index(`idx`) where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, + } + executeCaseFn := func(execFn func(db *sql.DB)) { + dsn := ts.GetDSN(func(config *mysql.Config) { + config.AllowAllFiles = true + config.Params["sql_mode"] = "''" + }) + db, err := sql.Open("mysql", dsn) + require.NoError(t, err) + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use stmtstats;") + require.NoError(t, err) + + execFn(db) + err = db.Close() + require.NoError(t, err) + } + execFn := func(db *sql.DB) { + dbt := testkit.NewDBTestKit(t, db) + for _, ca := range cases { + if ca.isQuery { + mustQuery(t, dbt, ca.sql) + } else { + dbt.MustExec(ca.sql) + } + } + } + executeCaseFn(execFn) + + for _, ca := range cases { + _, digest := parser.NormalizeDigest(ca.sql) + tagChecker.checkReqExist(t, stmtstats.BinaryDigest(digest.Bytes()), ca.sql, ca.reqs...) + } + for _, ca := range internalCases { + _, digest := parser.NormalizeDigest(ca.sql) + tagChecker.checkReqExist(t, stmtstats.BinaryDigest(digest.Bytes()), ca.sql, ca.reqs...) + } +} + +func (ts *tidbTestTopSQLSuite) loopExec(ctx context.Context, t *testing.T, fn func(db *sql.DB)) { + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err, "Error connecting") + defer func() { + err := db.Close() + require.NoError(t, err) + }() + dbt := testkit.NewDBTestKit(t, db) + dbt.MustExec("use topsql;") + for { + select { + case <-ctx.Done(): + return + default: + } + fn(db) + } +} + +func TestLocalhostClientMapping(t *testing.T) { + tempDir := t.TempDir() + socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK + + cli := testserverclient.NewTestServerClient() + cfg := util2.NewTestConfig() + cfg.Socket = socketFile + cfg.Port = cli.Port + cfg.Status.ReportStatus = false + + ts := createTidbTestSuite(t) + + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + go func() { + err := server.Run() + require.NoError(t, err) + }() + defer server.Close() + cli.WaitUntilServerCanConnect() + + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + // Create a db connection for root + db, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { + config.User = "root" + config.Net = "unix" + config.DBName = "test" + config.Addr = socketFile + })) + require.NoErrorf(t, err, "Open failed") + err = db.Ping() + require.NoErrorf(t, err, "Ping failed") + defer db.Close() + dbt := testkit.NewDBTestKit(t, db) + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "root@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") + require.NoError(t, rows.Close()) + + dbt.MustExec("CREATE USER 'localhostuser'@'localhost'") + dbt.MustExec("CREATE USER 'localhostuser'@'%'") + defer func() { + dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'%'") + dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'localhost'") + dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'127.0.0.1'") + }() + + dbt.MustExec("GRANT SELECT ON test.* TO 'localhostuser'@'%'") + dbt.MustExec("GRANT SELECT,UPDATE ON test.* TO 'localhostuser'@'localhost'") + + // Test with loopback interface - Should get access to localhostuser@localhost! + cli.RunTests(t, func(config *mysql.Config) { + config.User = "localhostuser" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + // NOTICE: this is not compatible with MySQL! (MySQL would report localhostuser@localhost also for 127.0.0.1) + cli.CheckRows(t, rows, "localhostuser@127.0.0.1") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'localhostuser'@'localhost'\nGRANT SELECT,UPDATE ON `test`.* TO 'localhostuser'@'localhost'") + require.NoError(t, rows.Close()) + }) + + dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'localhost'") + dbt.MustExec("CREATE USER 'localhostuser'@'127.0.0.1'") + dbt.MustExec("GRANT SELECT,UPDATE ON test.* TO 'localhostuser'@'127.0.0.1'") + // Test with unix domain socket file connection - Should get access to '%' + cli.RunTests(t, func(config *mysql.Config) { + config.Net = "unix" + config.Addr = socketFile + config.User = "localhostuser" + config.DBName = "test" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("select user()") + cli.CheckRows(t, rows, "localhostuser@localhost") + require.NoError(t, rows.Close()) + rows = dbt.MustQuery("show grants") + cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'localhostuser'@'%'\nGRANT SELECT ON `test`.* TO 'localhostuser'@'%'") + require.NoError(t, rows.Close()) + }) + + // Test if only localhost exists + dbt.MustExec("DROP USER 'localhostuser'@'%'") + dbSocket, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { + config.User = "localhostuser" + config.Net = "unix" + config.DBName = "test" + config.Addr = socketFile + })) + require.NoErrorf(t, err, "Open failed") + defer dbSocket.Close() + err = dbSocket.Ping() + require.Errorf(t, err, "Connection successful without matching host for unix domain socket!") +} + +func TestRcReadCheckTS(t *testing.T) { + ts := createTidbTestSuite(t) + + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + err := db.Close() + require.NoError(t, err) + }() + + db2, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + err := db2.Close() + require.NoError(t, err) + }() + tk2 := testkit.NewDBTestKit(t, db2) + tk2.MustExec("set @@tidb_enable_async_commit = 0") + tk2.MustExec("set @@tidb_enable_1pc = 0") + + cli := testserverclient.NewTestServerClient() + + tk := testkit.NewDBTestKit(t, db) + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(c1 int key, c2 int)") + tk.MustExec("insert into t1 values(1, 10), (2, 20), (3, 30)") + + tk.MustExec(`set tidb_rc_read_check_ts = 'on';`) + tk.MustExec(`set tx_isolation = 'READ-COMMITTED';`) + tk.MustExec("begin pessimistic") + // Test point get retry. + rows := tk.MustQuery("select * from t1 where c1 = 1") + cli.CheckRows(t, rows, "1 10") + tk2.MustExec("update t1 set c2 = c2 + 1") + rows = tk.MustQuery("select * from t1 where c1 = 1") + cli.CheckRows(t, rows, "1 11") + // Test batch point get retry. + rows = tk.MustQuery("select * from t1 where c1 in (1, 3)") + cli.CheckRows(t, rows, "1 11", "3 31") + tk2.MustExec("update t1 set c2 = c2 + 1") + rows = tk.MustQuery("select * from t1 where c1 in (1, 3)") + cli.CheckRows(t, rows, "1 12", "3 32") + // Test scan retry. + rows = tk.MustQuery("select * from t1") + cli.CheckRows(t, rows, "1 12", "2 22", "3 32") + tk2.MustExec("update t1 set c2 = c2 + 1") + rows = tk.MustQuery("select * from t1") + cli.CheckRows(t, rows, "1 13", "2 23", "3 33") + // Test reverse scan retry. + rows = tk.MustQuery("select * from t1 order by c1 desc") + cli.CheckRows(t, rows, "3 33", "2 23", "1 13") + tk2.MustExec("update t1 set c2 = c2 + 1") + rows = tk.MustQuery("select * from t1 order by c1 desc") + cli.CheckRows(t, rows, "3 34", "2 24", "1 14") + + // Test retry caused by ongoing prewrite lock. + // As the `defaultLockTTL` is 3s and it's difficult to change it here, the lock + // test is implemented in the uft test cases. +} + +type connEventLogs struct { + sync.Mutex + types []extension.ConnEventTp + infos []extension.ConnEventInfo +} + +func (l *connEventLogs) add(tp extension.ConnEventTp, info *extension.ConnEventInfo) { + l.Lock() + defer l.Unlock() + l.types = append(l.types, tp) + l.infos = append(l.infos, *info) +} + +func (l *connEventLogs) reset() { + l.Lock() + defer l.Unlock() + l.types = l.types[:0] + l.infos = l.infos[:0] +} + +func (l *connEventLogs) check(fn func()) { + l.Lock() + defer l.Unlock() + fn() +} + +func (l *connEventLogs) waitEvent(tp extension.ConnEventTp) error { + totalSleep := 0 + for { + l.Lock() + if l.types[len(l.types)-1] == tp { + l.Unlock() + return nil + } + l.Unlock() + if totalSleep >= 10000 { + break + } + time.Sleep(time.Millisecond * 100) + totalSleep += 100 + } + return errors.New("timeout") +} + +func TestExtensionConnEvent(t *testing.T) { + defer extension.Reset() + extension.Reset() + + logs := &connEventLogs{} + require.NoError(t, extension.Register("test", extension.WithSessionHandlerFactory(func() *extension.SessionHandler { + return &extension.SessionHandler{ + OnConnectionEvent: logs.add, + } + }))) + require.NoError(t, extension.Setup()) + + ts := createTidbTestSuite(t) + // createTidbTestSuite create an inner connection, so wait the previous connection closed + require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) + + // test for login success + logs.reset() + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + }() + + conn, err := db.Conn(context.Background()) + require.NoError(t, err) + defer func() { + _ = conn.Close() + }() + + var expectedConn2 variable.ConnectionInfo + require.NoError(t, logs.waitEvent(extension.ConnHandshakeAccepted)) + logs.check(func() { + require.Equal(t, []extension.ConnEventTp{ + extension.ConnConnected, + extension.ConnHandshakeAccepted, + }, logs.types) + conn1 := logs.infos[0] + require.Equal(t, "127.0.0.1", conn1.ClientIP) + require.Equal(t, "127.0.0.1", conn1.ServerIP) + require.Empty(t, conn1.User) + require.Empty(t, conn1.DB) + require.Equal(t, int(ts.Port), conn1.ServerPort) + require.NotEqual(t, conn1.ServerPort, conn1.ClientPort) + require.NotEmpty(t, conn1.ConnectionID) + require.Nil(t, conn1.ActiveRoles) + require.NoError(t, conn1.Error) + require.Empty(t, conn1.SessionAlias) + + expectedConn2 = *(conn1.ConnectionInfo) + expectedConn2.User = "root" + expectedConn2.DB = "test" + require.Equal(t, []*auth.RoleIdentity{}, logs.infos[1].ActiveRoles) + require.Nil(t, logs.infos[1].Error) + require.Equal(t, expectedConn2, *(logs.infos[1].ConnectionInfo)) + require.Empty(t, logs.infos[1].SessionAlias) + }) + + _, err = conn.ExecContext(context.TODO(), "create role r1@'%'") + require.NoError(t, err) + _, err = conn.ExecContext(context.TODO(), "grant r1 TO root") + require.NoError(t, err) + _, err = conn.ExecContext(context.TODO(), "set role all") + require.NoError(t, err) + _, err = conn.ExecContext(context.TODO(), "set @@tidb_session_alias='alias123'") + require.NoError(t, err) + + require.NoError(t, conn.Close()) + require.NoError(t, db.Close()) + require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) + logs.check(func() { + require.Equal(t, 3, len(logs.infos)) + require.Equal(t, 1, len(logs.infos[2].ActiveRoles)) + require.Equal(t, auth.RoleIdentity{ + Username: "r1", + Hostname: "%", + }, *logs.infos[2].ActiveRoles[0]) + require.Nil(t, logs.infos[2].Error) + require.Equal(t, expectedConn2, *(logs.infos[2].ConnectionInfo)) + require.Equal(t, "alias123", logs.infos[2].SessionAlias) + }) + + // test for login failed + logs.reset() + cfg := mysql.NewConfig() + cfg.User = "noexist" + cfg.Net = "tcp" + cfg.Addr = fmt.Sprintf("127.0.0.1:%d", ts.Port) + cfg.DBName = "test" + + db, err = sql.Open("mysql", cfg.FormatDSN()) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + }() + + _, err = db.Conn(context.Background()) + require.Error(t, err) + require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) + logs.check(func() { + require.Equal(t, []extension.ConnEventTp{ + extension.ConnConnected, + extension.ConnHandshakeRejected, + extension.ConnDisconnected, + }, logs.types) + conn1 := logs.infos[0] + require.Equal(t, "127.0.0.1", conn1.ClientIP) + require.Equal(t, "127.0.0.1", conn1.ServerIP) + require.Empty(t, conn1.User) + require.Empty(t, conn1.DB) + require.Equal(t, int(ts.Port), conn1.ServerPort) + require.NotEqual(t, conn1.ServerPort, conn1.ClientPort) + require.NotEmpty(t, conn1.ConnectionID) + require.Nil(t, conn1.ActiveRoles) + require.NoError(t, conn1.Error) + require.Empty(t, conn1.SessionAlias) + + expectedConn2 = *(conn1.ConnectionInfo) + expectedConn2.User = "noexist" + expectedConn2.DB = "test" + require.Equal(t, []*auth.RoleIdentity{}, logs.infos[1].ActiveRoles) + require.EqualError(t, logs.infos[1].Error, "[server:1045]Access denied for user 'noexist'@'127.0.0.1' (using password: NO)") + require.Equal(t, expectedConn2, *(logs.infos[1].ConnectionInfo)) + require.Empty(t, logs.infos[2].SessionAlias) + }) +} + +func TestSandBoxMode(t *testing.T) { + ts := createTidbTestSuite(t) + qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) + require.NoError(t, err) + _, err = Execute(context.Background(), qctx, "create user testuser;") + require.NoError(t, err) + qctx.Session.GetSessionVars().User = &auth.UserIdentity{Username: "testuser", AuthUsername: "testuser", AuthHostname: "%"} + + alterPwdStmts := []string{ + "set password = '1234';", + "alter user testuser identified by '1234';", + "alter user current_user() identified by '1234';", + } + + for _, alterPwdStmt := range alterPwdStmts { + require.False(t, qctx.Session.InSandBoxMode()) + _, err = Execute(context.Background(), qctx, "select 1;") + require.NoError(t, err) + + qctx.Session.EnableSandBoxMode() + require.True(t, qctx.Session.InSandBoxMode()) + _, err = Execute(context.Background(), qctx, "select 1;") + require.Error(t, err) + _, err = Execute(context.Background(), qctx, "alter user testuser identified with 'mysql_native_password';") + require.Error(t, err) + _, err = Execute(context.Background(), qctx, alterPwdStmt) + require.NoError(t, err) + _, err = Execute(context.Background(), qctx, "select 1;") + require.NoError(t, err) + } +} + +// See: https://github.com/pingcap/tidb/issues/40979 +// Reusing memory of `chunk.Chunk` may cause some systems variable's memory value to be modified unexpectedly. +func TestChunkReuseCorruptSysVarString(t *testing.T) { + ts := createTidbTestSuite(t) + + db, err := sql.Open("mysql", ts.GetDSN()) + require.NoError(t, err) + defer func() { + require.NoError(t, db.Close()) + }() + + conn, err := db.Conn(context.Background()) + require.NoError(t, err) + defer func() { + require.NoError(t, conn.Close()) + }() + + rs, err := conn.QueryContext(context.Background(), "show tables in test") + ts.Rows(t, rs) + require.NoError(t, err) + + _, err = conn.ExecContext(context.Background(), "set @@time_zone=(select 'Asia/Shanghai')") + require.NoError(t, err) + + rs, err = conn.QueryContext(context.Background(), "select TIDB_TABLE_ID from information_schema.tables where TABLE_SCHEMA='aaaa'") + ts.Rows(t, rs) + require.NoError(t, err) + + rs, err = conn.QueryContext(context.Background(), "select @@time_zone") + require.NoError(t, err) + defer func() { + require.NoError(t, rs.Close()) + }() + + rows := ts.Rows(t, rs) + require.Equal(t, 1, len(rows)) + require.Equal(t, "Asia/Shanghai", rows[0]) +} + +type mockProxyProtocolProxy struct { + frontend string + backend string + clientAddr string + backendIsSock bool + ln net.Listener + run atomic.Bool +} + +func newMockProxyProtocolProxy(frontend, backend, clientAddr string, backendIsSock bool) *mockProxyProtocolProxy { + return &mockProxyProtocolProxy{ + frontend: frontend, + backend: backend, + clientAddr: clientAddr, + backendIsSock: backendIsSock, + ln: nil, + } +} + +func (p *mockProxyProtocolProxy) ListenAddr() net.Addr { + return p.ln.Addr() +} + +func (p *mockProxyProtocolProxy) Run() (err error) { + p.run.Store(true) + p.ln, err = net.Listen("tcp", p.frontend) + if err != nil { + return err + } + for p.run.Load() { + conn, err := p.ln.Accept() + if err != nil { + break + } + go p.onConn(conn) + } + return nil +} + +func (p *mockProxyProtocolProxy) Close() error { + p.run.Store(false) + if p.ln != nil { + return p.ln.Close() + } + return nil +} + +func (p *mockProxyProtocolProxy) connectToBackend() (net.Conn, error) { + if p.backendIsSock { + return net.Dial("unix", p.backend) + } + return net.Dial("tcp", p.backend) +} + +func (p *mockProxyProtocolProxy) onConn(conn net.Conn) { + bconn, err := p.connectToBackend() + if err != nil { + conn.Close() + fmt.Println(err) + } + defer bconn.Close() + ppHeader := p.generateProxyProtocolHeaderV2("tcp4", p.clientAddr, p.frontend) + bconn.Write(ppHeader) + p.proxyPipe(conn, bconn) +} + +func (p *mockProxyProtocolProxy) proxyPipe(p1, p2 io.ReadWriteCloser) { + defer p1.Close() + defer p2.Close() + + // start proxy + p1die := make(chan struct{}) + go func() { io.Copy(p1, p2); close(p1die) }() + + p2die := make(chan struct{}) + go func() { io.Copy(p2, p1); close(p2die) }() + + // wait for proxy termination + select { + case <-p1die: + case <-p2die: + } +} + +func (p *mockProxyProtocolProxy) generateProxyProtocolHeaderV2(network, srcAddr, dstAddr string) []byte { + var ( + proxyProtocolV2Sig = []byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A} + v2CmdPos = 12 + v2FamlyPos = 13 + ) + saddr, _ := net.ResolveTCPAddr(network, srcAddr) + daddr, _ := net.ResolveTCPAddr(network, dstAddr) + buffer := make([]byte, 1024) + copy(buffer, proxyProtocolV2Sig) + // Command + buffer[v2CmdPos] = 0x21 + // Famly + if network == "tcp4" { + buffer[v2FamlyPos] = 0x11 + binary.BigEndian.PutUint16(buffer[14:14+2], 12) + copy(buffer[16:16+4], []byte(saddr.IP.To4())) + copy(buffer[20:20+4], []byte(daddr.IP.To4())) + binary.BigEndian.PutUint16(buffer[24:24+2], uint16(saddr.Port)) + binary.BigEndian.PutUint16(buffer[26:26+2], uint16(saddr.Port)) + return buffer[0:28] + } else if network == "tcp6" { + buffer[v2FamlyPos] = 0x21 + binary.BigEndian.PutUint16(buffer[14:14+2], 36) + copy(buffer[16:16+16], []byte(saddr.IP.To16())) + copy(buffer[32:32+16], []byte(daddr.IP.To16())) + binary.BigEndian.PutUint16(buffer[48:48+2], uint16(saddr.Port)) + binary.BigEndian.PutUint16(buffer[50:50+2], uint16(saddr.Port)) + return buffer[0:52] + } + return buffer +} + +func TestProxyProtocolWithIpFallbackable(t *testing.T) { + cfg := util2.NewTestConfig() + cfg.Port = 4999 + cfg.Status.ReportStatus = false + // Setup proxy protocol config + cfg.ProxyProtocol.Networks = "*" + cfg.ProxyProtocol.Fallbackable = true + + ts := createTidbTestSuite(t) + + // Prepare Server + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 100) + defer func() { + server.Close() + }() + + require.NotNil(t, server.Listener()) + require.Nil(t, server.Socket()) + + // Prepare Proxy + ppProxy := newMockProxyProtocolProxy("127.0.0.1:5000", "127.0.0.1:4999", "192.168.1.2:60055", false) + go func() { + ppProxy.Run() + }() + time.Sleep(time.Millisecond * 100) + defer func() { + ppProxy.Close() + }() + + cli := testserverclient.NewTestServerClient() + cli.Port = testutil.GetPortFromTCPAddr(ppProxy.ListenAddr()) + cli.WaitUntilServerCanConnect() + + cli.RunTests(t, + func(config *mysql.Config) { + config.User = "root" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("SHOW PROCESSLIST;") + records := cli.Rows(t, rows) + require.Contains(t, records[0], "192.168.1.2:60055") + }, + ) + + cli2 := testserverclient.NewTestServerClient() + cli2.Port = 4999 + cli2.RunTests(t, + func(config *mysql.Config) { + config.User = "root" + }, + func(dbt *testkit.DBTestKit) { + rows := dbt.MustQuery("SHOW PROCESSLIST;") + records := cli.Rows(t, rows) + require.Contains(t, records[0], "127.0.0.1:") + }, + ) +} + +func TestProxyProtocolWithIpNoFallbackable(t *testing.T) { + cfg := util2.NewTestConfig() + cfg.Port = 0 + cfg.Status.ReportStatus = false + // Setup proxy protocol config + cfg.ProxyProtocol.Networks = "*" + cfg.ProxyProtocol.Fallbackable = false + + ts := createTidbTestSuite(t) + + // Prepare Server + server, err := server2.NewServer(cfg, ts.tidbdrv) + require.NoError(t, err) + server.SetDomain(ts.domain) + go func() { + err := server.Run() + require.NoError(t, err) + }() + time.Sleep(time.Millisecond * 1000) + defer func() { + server.Close() + }() + + require.NotNil(t, server.Listener()) + require.Nil(t, server.Socket()) + + cli := testserverclient.NewTestServerClient() + cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) + dsn := cli.GetDSN(func(config *mysql.Config) { + config.User = "root" + config.DBName = "test" + }) + db, err := sql.Open("mysql", dsn) + require.Nil(t, err) + err = db.Ping() + require.NotNil(t, err) + db.Close() +} diff --git a/server/tidb_library_test.go b/pkg/server/tidb_library_test.go similarity index 92% rename from server/tidb_library_test.go rename to pkg/server/tidb_library_test.go index ca687540a20a9..75d75dbf3e2ce 100644 --- a/server/tidb_library_test.go +++ b/pkg/server/tidb_library_test.go @@ -19,9 +19,9 @@ import ( "testing" "github.com/docker/go-units" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util/syncutil" "github.com/stretchr/testify/require" ) diff --git a/pkg/server/tidb_test.go b/pkg/server/tidb_test.go new file mode 100644 index 0000000000000..423b45040c973 --- /dev/null +++ b/pkg/server/tidb_test.go @@ -0,0 +1,133 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package server + +import ( + "bufio" + "bytes" + "context" + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/server/internal" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/arena" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/stretchr/testify/require" +) + +func TestRcReadCheckTSConflict(t *testing.T) { + store := testkit.CreateMockStore(t) + + cc := &clientConn{ + alloc: arena.NewAllocator(1024), + chunkAlloc: chunk.NewAllocator(), + pkt: internal.NewPacketIOForTest(bufio.NewWriter(bytes.NewBuffer(nil))), + } + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_rc_read_check_ts = ON") + tk.RefreshSession() + cc.SetCtx(&TiDBContext{Session: tk.Session(), stmts: make(map[int]*TiDBStatement)}) + + tk.MustExec("use test") + tk.MustExec("create table t(a int not null primary key, b int not null)") + dml := "insert into t values" + for i := 0; i < 50; i++ { + dml += fmt.Sprintf("(%v, 0)", i) + if i != 49 { + dml += "," + } + } + tk.MustExec(dml) + tk.MustQuery("select count(*) from t").Check(testkit.Rows("50")) + require.Equal(t, "ON", tk.MustQuery("show variables like 'tidb_rc_read_check_ts'").Rows()[0][1]) + + ctx := context.Background() + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/server/fetchNextErr", "return(\"secondNextAndRetConflict\")")) + err := cc.handleQuery(ctx, "select * from t limit 20") + require.NoError(t, err) + + err = cc.handleQuery(ctx, "select * from t t1 join t t2") + require.Equal(t, kv.ErrWriteConflict, err) + + tk.MustExec("set session tidb_max_chunk_size = 4096") + require.Equal(t, "4096", tk.MustQuery("show variables like 'tidb_max_chunk_size'").Rows()[0][1]) + err = cc.handleQuery(ctx, "select * from t t1 join t t2") + require.NoError(t, err) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/server/fetchNextErr")) + + tk.MustExec("drop table t") +} + +func TestRcReadCheckTSConflictExtra(t *testing.T) { + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/CallOnStmtRetry", "return")) + defer func() { + defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/CallOnStmtRetry")) + }() + store := testkit.CreateMockStore(t) + + ctx := context.Background() + cc := &clientConn{ + alloc: arena.NewAllocator(1024), + chunkAlloc: chunk.NewAllocator(), + pkt: internal.NewPacketIOForTest(bufio.NewWriter(bytes.NewBuffer(nil))), + } + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_rc_read_check_ts = ON") + + se := tk.Session() + cc.SetCtx(&TiDBContext{Session: se, stmts: make(map[int]*TiDBStatement)}) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(id1 int, id2 int, id3 int, PRIMARY KEY(id1), UNIQUE KEY udx_id2 (id2))") + tk.MustExec("insert into t1 values (1, 1, 1)") + tk.MustExec("insert into t1 values (10, 10, 10)") + require.Equal(t, "ON", tk.MustQuery("show variables like 'tidb_rc_read_check_ts'").Rows()[0][1]) + + tk.MustExec("set transaction_isolation = 'READ-COMMITTED'") + tk2.MustExec("set transaction_isolation = 'READ-COMMITTED'") + + // Execute in text protocol + se.SetValue(sessiontxn.CallOnStmtRetryCount, 0) + tk.MustExec("begin pessimistic") + tk2.MustExec("update t1 set id3 = id3 + 1 where id1 = 1") + err := cc.handleQuery(ctx, "select * from t1 where id1 = 1") + require.NoError(t, err) + tk.MustExec("commit") + count, ok := se.Value(sessiontxn.CallOnStmtRetryCount).(int) + require.Equal(t, true, ok) + require.Equal(t, 1, count) + + // Execute in prepare binary protocol + se.SetValue(sessiontxn.CallOnStmtRetryCount, 0) + tk.MustExec("begin pessimistic") + tk2.MustExec("update t1 set id3 = id3 + 1 where id1 = 1") + require.NoError(t, cc.HandleStmtPrepare(ctx, "select * from t1 where id1 = 1")) + require.NoError(t, cc.handleStmtExecute(ctx, []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0})) + tk.MustExec("commit") + count, ok = se.Value(sessiontxn.CallOnStmtRetryCount).(int) + require.Equal(t, true, ok) + require.Equal(t, 1, count) + + tk.MustExec("drop table t1") +} diff --git a/server/tokenlimiter.go b/pkg/server/tokenlimiter.go similarity index 100% rename from server/tokenlimiter.go rename to pkg/server/tokenlimiter.go diff --git a/pkg/session/BUILD.bazel b/pkg/session/BUILD.bazel new file mode 100644 index 0000000000000..2454f7028edfb --- /dev/null +++ b/pkg/session/BUILD.bazel @@ -0,0 +1,168 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "session", + srcs = [ + "advisory_locks.go", + "bootstrap.go", + "mock_bootstrap.go", + "nontransactional.go", + "session.go", + "sync_upgrade.go", + "testutil.go", #keep + "tidb.go", + "txn.go", + "txnmanager.go", + ], + importpath = "github.com/pingcap/tidb/pkg/session", + visibility = ["//visibility:public"], + deps = [ + "//pkg/bindinfo", + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/placement", + "//pkg/ddl/schematracker", + "//pkg/ddl/syncer", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/errno", + "//pkg/executor", + "//pkg/expression", + "//pkg/extension", + "//pkg/extension/extensionimpl", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/metrics", + "//pkg/owner", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/planner", + "//pkg/planner/core", + "//pkg/plugin", + "//pkg/privilege", + "//pkg/privilege/conn", + "//pkg/privilege/privileges", + "//pkg/session/metrics", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/isolation", + "//pkg/sessiontxn/staleread", + "//pkg/statistics/handle/usage", + "//pkg/store/driver/error", + "//pkg/store/driver/txn", + "//pkg/store/helper", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/table/tables", + "//pkg/table/temptable", + "//pkg/tablecodec", + "//pkg/telemetry", + "//pkg/testkit/testenv", + "//pkg/timer/tablestore", + "//pkg/ttl/ttlworker", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/execdetails", + "//pkg/util/intest", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/logutil/consistency", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/parser", + "//pkg/util/sem", + "//pkg/util/sli", + "//pkg/util/sqlexec", + "//pkg/util/syncutil", + "//pkg/util/tableutil", + "//pkg/util/timeutil", + "//pkg/util/topsql", + "//pkg/util/topsql/state", + "//pkg/util/topsql/stmtstats", + "//pkg/util/tracing", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "session_test", + timeout = "moderate", + srcs = [ + "bench_test.go", + "bootstrap_test.go", + "index_usage_sync_lease_test.go", + "main_test.go", + "tidb_test.go", + ], + data = glob(["testdata/**"]), + embed = [":session"], + flaky = True, + race = "on", + shard_count = 50, + deps = [ + "//pkg/autoid_service", + "//pkg/bindinfo", + "//pkg/config", + "//pkg/domain", + "//pkg/executor", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/telemetry", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_atomic//:atomic", #keep + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) diff --git a/session/OWNERS b/pkg/session/OWNERS similarity index 100% rename from session/OWNERS rename to pkg/session/OWNERS diff --git a/session/advisory_locks.go b/pkg/session/advisory_locks.go similarity index 97% rename from session/advisory_locks.go rename to pkg/session/advisory_locks.go index 9b5f55639e93a..0f6430d9696f0 100644 --- a/session/advisory_locks.go +++ b/pkg/session/advisory_locks.go @@ -17,8 +17,8 @@ package session import ( "context" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" ) // Advisory Locks are the locks in GET_LOCK() and RELEASE_LOCK(). diff --git a/pkg/session/bench_test.go b/pkg/session/bench_test.go new file mode 100644 index 0000000000000..0064336dd5d3c --- /dev/null +++ b/pkg/session/bench_test.go @@ -0,0 +1,1945 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/log" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +var smallCount = 100 +var bigCount = 10000 + +func prepareBenchSession() (Session, *domain.Domain, kv.Storage) { + config.UpdateGlobal(func(cfg *config.Config) { + cfg.Instance.EnableSlowLog.Store(false) + }) + + store, err := mockstore.NewMockStore() + if err != nil { + logutil.BgLogger().Fatal(err.Error()) + } + domain, err := BootstrapSession(store) + if err != nil { + logutil.BgLogger().Fatal(err.Error()) + } + log.SetLevel(zapcore.ErrorLevel) + se, err := CreateSession4Test(store) + if err != nil { + logutil.BgLogger().Fatal(err.Error()) + } + mustExecute(se, "use test") + return se, domain, store +} + +func prepareBenchData(se Session, colType string, valueFormat string, valueCount int) { + mustExecute(se, "drop table if exists t") + mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s, index idx (col))", colType)) + mustExecute(se, "begin") + for i := 0; i < valueCount; i++ { + mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") + } + mustExecute(se, "commit") +} + +func prepareNonclusteredBenchData(se Session, colType string, valueFormat string, valueCount int) { + mustExecute(se, "drop table if exists t") + mustExecute(se, fmt.Sprintf("create table t (pk int primary key /*T![clustered_index] NONCLUSTERED */ auto_increment, col %s, index idx (col))", colType)) + mustExecute(se, "begin") + for i := 0; i < valueCount; i++ { + mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") + } + mustExecute(se, "commit") +} + +func prepareSortBenchData(se Session, colType string, valueFormat string, valueCount int) { + mustExecute(se, "drop table if exists t") + mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s)", colType)) + mustExecute(se, "begin") + r := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < valueCount; i++ { + if i%1000 == 0 { + mustExecute(se, "commit") + mustExecute(se, "begin") + } + mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, r.Intn(valueCount))+")") + } + mustExecute(se, "commit") +} + +func prepareJoinBenchData(se Session, colType string, valueFormat string, valueCount int) { + mustExecute(se, "drop table if exists t") + mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s)", colType)) + mustExecute(se, "begin") + for i := 0; i < valueCount; i++ { + mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") + } + mustExecute(se, "commit") +} + +func readResult(ctx context.Context, rs sqlexec.RecordSet, count int) { + req := rs.NewChunk(nil) + for count > 0 { + err := rs.Next(ctx, req) + if err != nil { + logutil.Logger(ctx).Fatal("read result failed", zap.Error(err)) + } + if req.NumRows() == 0 { + logutil.Logger(ctx).Fatal(strconv.Itoa(count)) + } + count -= req.NumRows() + } + rs.Close() +} + +func hasPlan(ctx context.Context, b *testing.B, se Session, plan string) { + find := false + rs, err := se.Execute(ctx, "explain select * from t where col = 'hello 64'") + if err != nil { + b.Fatal(err) + } + rows, err := ResultSetToStringSlice(ctx, se, rs[0]) + if err != nil { + b.Fatal(err) + } + for i := range rows { + if strings.Contains(rows[i][0], plan) { + find = true + } + } + if !find { + b.Fatal(fmt.Printf("plan not contain `%s`", plan)) + } +} + +func BenchmarkBasic(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select 1") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkTableScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "int", "%v", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], smallCount) + } + b.StopTimer() +} + +func BenchmarkExplainTableScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "int", "%v", 0) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "explain select * from t") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkTableLookup(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "int", "%d", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where pk = 64") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkExplainTableLookup(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "int", "%d", 0) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "explain select * from t where pk = 64") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkStringIndexScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "varchar(255)", "'hello %d'", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col > 'hello'") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], smallCount) + } + b.StopTimer() +} + +func BenchmarkExplainStringIndexScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "varchar(255)", "'hello %d'", 0) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "explain select * from t where col > 'hello'") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkPointGet(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, "create table t (pk int primary key)") + mustExecute(se, "insert t values (61),(62),(63),(64)") + b.ResetTimer() + alloc := chunk.NewAllocator() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where pk = 64") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkBatchPointGet(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, "create table t (pk int primary key)") + mustExecute(se, "insert t values (61),(62),(63),(64)") + alloc := chunk.NewAllocator() + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where pk in (61, 64, 67)") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkPreparedPointGet(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, "create table t (pk int primary key)") + mustExecute(se, "insert t values (61),(62),(63),(64)") + + stmtID, _, _, err := se.PrepareStmt("select * from t where pk = ?") + if err != nil { + b.Fatal(err) + } + + params := expression.Args2Expressions4Test(64) + alloc := chunk.NewAllocator() + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.ExecutePreparedStmt(ctx, stmtID, params) + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs, alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkStringIndexLookup(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareNonclusteredBenchData(se, "varchar(255)", "'hello %d'", smallCount) + hasPlan(ctx, b, se, "IndexLookUp") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col = 'hello 64'") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkIntegerIndexScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "int", "%v", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col >= 0") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], smallCount) + } + b.StopTimer() +} + +func BenchmarkIntegerIndexLookup(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareNonclusteredBenchData(se, "int", "%v", smallCount) + hasPlan(ctx, b, se, "IndexLookUp") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col = 64") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkDecimalIndexScan(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareBenchData(se, "decimal(32,6)", "%v.1234", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col >= 0") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], smallCount) + } + b.StopTimer() +} + +func BenchmarkDecimalIndexLookup(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareNonclusteredBenchData(se, "decimal(32,6)", "%v.1234", smallCount) + hasPlan(ctx, b, se, "IndexLookUp") + + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where col = 64.1234") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkInsertWithIndex(b *testing.B) { + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, `set @@tidb_enable_mutation_checker = 0`) + mustExecute(se, "drop table if exists t") + mustExecute(se, "create table t (pk int primary key, col int, index idx (col))") + b.ResetTimer() + for i := 0; i < b.N; i++ { + mustExecute(se, fmt.Sprintf("insert t values (%d, %d)", i, i)) + } + b.StopTimer() +} + +func BenchmarkInsertNoIndex(b *testing.B) { + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, "drop table if exists t") + mustExecute(se, "create table t (pk int primary key, col int)") + b.ResetTimer() + for i := 0; i < b.N; i++ { + mustExecute(se, fmt.Sprintf("insert t values (%d, %d)", i, i)) + } + b.StopTimer() +} + +func BenchmarkSort(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareSortBenchData(se, "int", "%v", bigCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t order by col limit 50") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 50) + } + b.StopTimer() +} + +func BenchmarkSort2(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareSortBenchData(se, "int", "%v", 1000000) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t order by col") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1000000) + } + b.StopTimer() +} + +func BenchmarkJoin(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareJoinBenchData(se, "int", "%v", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t a join t b on a.col = b.col") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], smallCount) + } + b.StopTimer() +} + +func BenchmarkJoinLimit(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + prepareJoinBenchData(se, "int", "%v", smallCount) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t a join t b on a.col = b.col limit 1") + if err != nil { + b.Fatal(err) + } + readResult(ctx, rs[0], 1) + } + b.StopTimer() +} + +func BenchmarkPartitionPruning(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + mustExecute(se, `create table t (id int, dt datetime) +partition by range (to_days(dt)) ( +partition p0 values less than (737515), +partition p1 values less than (737516), +partition p2 values less than (737517), +partition p3 values less than (737518), +partition p4 values less than (737519), +partition p5 values less than (737520), +partition p6 values less than (737521), +partition p7 values less than (737522), +partition p8 values less than (737523), +partition p9 values less than (737524), +partition p10 values less than (737525), +partition p11 values less than (737526), +partition p12 values less than (737527), +partition p13 values less than (737528), +partition p14 values less than (737529), +partition p15 values less than (737530), +partition p16 values less than (737531), +partition p17 values less than (737532), +partition p18 values less than (737533), +partition p19 values less than (737534), +partition p20 values less than (737535), +partition p21 values less than (737536), +partition p22 values less than (737537), +partition p23 values less than (737538), +partition p24 values less than (737539), +partition p25 values less than (737540), +partition p26 values less than (737541), +partition p27 values less than (737542), +partition p28 values less than (737543), +partition p29 values less than (737544), +partition p30 values less than (737545), +partition p31 values less than (737546), +partition p32 values less than (737547), +partition p33 values less than (737548), +partition p34 values less than (737549), +partition p35 values less than (737550), +partition p36 values less than (737551), +partition p37 values less than (737552), +partition p38 values less than (737553), +partition p39 values less than (737554), +partition p40 values less than (737555), +partition p41 values less than (737556), +partition p42 values less than (737557), +partition p43 values less than (737558), +partition p44 values less than (737559), +partition p45 values less than (737560), +partition p46 values less than (737561), +partition p47 values less than (737562), +partition p48 values less than (737563), +partition p49 values less than (737564), +partition p50 values less than (737565), +partition p51 values less than (737566), +partition p52 values less than (737567), +partition p53 values less than (737568), +partition p54 values less than (737569), +partition p55 values less than (737570), +partition p56 values less than (737571), +partition p57 values less than (737572), +partition p58 values less than (737573), +partition p59 values less than (737574), +partition p60 values less than (737575), +partition p61 values less than (737576), +partition p62 values less than (737577), +partition p63 values less than (737578), +partition p64 values less than (737579), +partition p65 values less than (737580), +partition p66 values less than (737581), +partition p67 values less than (737582), +partition p68 values less than (737583), +partition p69 values less than (737584), +partition p70 values less than (737585), +partition p71 values less than (737586), +partition p72 values less than (737587), +partition p73 values less than (737588), +partition p74 values less than (737589), +partition p75 values less than (737590), +partition p76 values less than (737591), +partition p77 values less than (737592), +partition p78 values less than (737593), +partition p79 values less than (737594), +partition p80 values less than (737595), +partition p81 values less than (737596), +partition p82 values less than (737597), +partition p83 values less than (737598), +partition p84 values less than (737599), +partition p85 values less than (737600), +partition p86 values less than (737601), +partition p87 values less than (737602), +partition p88 values less than (737603), +partition p89 values less than (737604), +partition p90 values less than (737605), +partition p91 values less than (737606), +partition p92 values less than (737607), +partition p93 values less than (737608), +partition p94 values less than (737609), +partition p95 values less than (737610), +partition p96 values less than (737611), +partition p97 values less than (737612), +partition p98 values less than (737613), +partition p99 values less than (737614), +partition p100 values less than (737615), +partition p101 values less than (737616), +partition p102 values less than (737617), +partition p103 values less than (737618), +partition p104 values less than (737619), +partition p105 values less than (737620), +partition p106 values less than (737621), +partition p107 values less than (737622), +partition p108 values less than (737623), +partition p109 values less than (737624), +partition p110 values less than (737625), +partition p111 values less than (737626), +partition p112 values less than (737627), +partition p113 values less than (737628), +partition p114 values less than (737629), +partition p115 values less than (737630), +partition p116 values less than (737631), +partition p117 values less than (737632), +partition p118 values less than (737633), +partition p119 values less than (737634), +partition p120 values less than (737635), +partition p121 values less than (737636), +partition p122 values less than (737637), +partition p123 values less than (737638), +partition p124 values less than (737639), +partition p125 values less than (737640), +partition p126 values less than (737641), +partition p127 values less than (737642), +partition p128 values less than (737643), +partition p129 values less than (737644), +partition p130 values less than (737645), +partition p131 values less than (737646), +partition p132 values less than (737647), +partition p133 values less than (737648), +partition p134 values less than (737649), +partition p135 values less than (737650), +partition p136 values less than (737651), +partition p137 values less than (737652), +partition p138 values less than (737653), +partition p139 values less than (737654), +partition p140 values less than (737655), +partition p141 values less than (737656), +partition p142 values less than (737657), +partition p143 values less than (737658), +partition p144 values less than (737659), +partition p145 values less than (737660), +partition p146 values less than (737661), +partition p147 values less than (737662), +partition p148 values less than (737663), +partition p149 values less than (737664), +partition p150 values less than (737665), +partition p151 values less than (737666), +partition p152 values less than (737667), +partition p153 values less than (737668), +partition p154 values less than (737669), +partition p155 values less than (737670), +partition p156 values less than (737671), +partition p157 values less than (737672), +partition p158 values less than (737673), +partition p159 values less than (737674), +partition p160 values less than (737675), +partition p161 values less than (737676), +partition p162 values less than (737677), +partition p163 values less than (737678), +partition p164 values less than (737679), +partition p165 values less than (737680), +partition p166 values less than (737681), +partition p167 values less than (737682), +partition p168 values less than (737683), +partition p169 values less than (737684), +partition p170 values less than (737685), +partition p171 values less than (737686), +partition p172 values less than (737687), +partition p173 values less than (737688), +partition p174 values less than (737689), +partition p175 values less than (737690), +partition p176 values less than (737691), +partition p177 values less than (737692), +partition p178 values less than (737693), +partition p179 values less than (737694), +partition p180 values less than (737695), +partition p181 values less than (737696), +partition p182 values less than (737697), +partition p183 values less than (737698), +partition p184 values less than (737699), +partition p185 values less than (737700), +partition p186 values less than (737701), +partition p187 values less than (737702), +partition p188 values less than (737703), +partition p189 values less than (737704), +partition p190 values less than (737705), +partition p191 values less than (737706), +partition p192 values less than (737707), +partition p193 values less than (737708), +partition p194 values less than (737709), +partition p195 values less than (737710), +partition p196 values less than (737711), +partition p197 values less than (737712), +partition p198 values less than (737713), +partition p199 values less than (737714), +partition p200 values less than (737715), +partition p201 values less than (737716), +partition p202 values less than (737717), +partition p203 values less than (737718), +partition p204 values less than (737719), +partition p205 values less than (737720), +partition p206 values less than (737721), +partition p207 values less than (737722), +partition p208 values less than (737723), +partition p209 values less than (737724), +partition p210 values less than (737725), +partition p211 values less than (737726), +partition p212 values less than (737727), +partition p213 values less than (737728), +partition p214 values less than (737729), +partition p215 values less than (737730), +partition p216 values less than (737731), +partition p217 values less than (737732), +partition p218 values less than (737733), +partition p219 values less than (737734), +partition p220 values less than (737735), +partition p221 values less than (737736), +partition p222 values less than (737737), +partition p223 values less than (737738), +partition p224 values less than (737739), +partition p225 values less than (737740), +partition p226 values less than (737741), +partition p227 values less than (737742), +partition p228 values less than (737743), +partition p229 values less than (737744), +partition p230 values less than (737745), +partition p231 values less than (737746), +partition p232 values less than (737747), +partition p233 values less than (737748), +partition p234 values less than (737749), +partition p235 values less than (737750), +partition p236 values less than (737751), +partition p237 values less than (737752), +partition p238 values less than (737753), +partition p239 values less than (737754), +partition p240 values less than (737755), +partition p241 values less than (737756), +partition p242 values less than (737757), +partition p243 values less than (737758), +partition p244 values less than (737759), +partition p245 values less than (737760), +partition p246 values less than (737761), +partition p247 values less than (737762), +partition p248 values less than (737763), +partition p249 values less than (737764), +partition p250 values less than (737765), +partition p251 values less than (737766), +partition p252 values less than (737767), +partition p253 values less than (737768), +partition p254 values less than (737769), +partition p255 values less than (737770), +partition p256 values less than (737771), +partition p257 values less than (737772), +partition p258 values less than (737773), +partition p259 values less than (737774), +partition p260 values less than (737775), +partition p261 values less than (737776), +partition p262 values less than (737777), +partition p263 values less than (737778), +partition p264 values less than (737779), +partition p265 values less than (737780), +partition p266 values less than (737781), +partition p267 values less than (737782), +partition p268 values less than (737783), +partition p269 values less than (737784), +partition p270 values less than (737785), +partition p271 values less than (737786), +partition p272 values less than (737787), +partition p273 values less than (737788), +partition p274 values less than (737789), +partition p275 values less than (737790), +partition p276 values less than (737791), +partition p277 values less than (737792), +partition p278 values less than (737793), +partition p279 values less than (737794), +partition p280 values less than (737795), +partition p281 values less than (737796), +partition p282 values less than (737797), +partition p283 values less than (737798), +partition p284 values less than (737799), +partition p285 values less than (737800), +partition p286 values less than (737801), +partition p287 values less than (737802), +partition p288 values less than (737803), +partition p289 values less than (737804), +partition p290 values less than (737805), +partition p291 values less than (737806), +partition p292 values less than (737807), +partition p293 values less than (737808), +partition p294 values less than (737809), +partition p295 values less than (737810), +partition p296 values less than (737811), +partition p297 values less than (737812), +partition p298 values less than (737813), +partition p299 values less than (737814), +partition p300 values less than (737815), +partition p301 values less than (737816), +partition p302 values less than (737817), +partition p303 values less than (737818), +partition p304 values less than (737819), +partition p305 values less than (737820), +partition p306 values less than (737821), +partition p307 values less than (737822), +partition p308 values less than (737823), +partition p309 values less than (737824), +partition p310 values less than (737825), +partition p311 values less than (737826), +partition p312 values less than (737827), +partition p313 values less than (737828), +partition p314 values less than (737829), +partition p315 values less than (737830), +partition p316 values less than (737831), +partition p317 values less than (737832), +partition p318 values less than (737833), +partition p319 values less than (737834), +partition p320 values less than (737835), +partition p321 values less than (737836), +partition p322 values less than (737837), +partition p323 values less than (737838), +partition p324 values less than (737839), +partition p325 values less than (737840), +partition p326 values less than (737841), +partition p327 values less than (737842), +partition p328 values less than (737843), +partition p329 values less than (737844), +partition p330 values less than (737845), +partition p331 values less than (737846), +partition p332 values less than (737847), +partition p333 values less than (737848), +partition p334 values less than (737849), +partition p335 values less than (737850), +partition p336 values less than (737851), +partition p337 values less than (737852), +partition p338 values less than (737853), +partition p339 values less than (737854), +partition p340 values less than (737855), +partition p341 values less than (737856), +partition p342 values less than (737857), +partition p343 values less than (737858), +partition p344 values less than (737859), +partition p345 values less than (737860), +partition p346 values less than (737861), +partition p347 values less than (737862), +partition p348 values less than (737863), +partition p349 values less than (737864), +partition p350 values less than (737865), +partition p351 values less than (737866), +partition p352 values less than (737867), +partition p353 values less than (737868), +partition p354 values less than (737869), +partition p355 values less than (737870), +partition p356 values less than (737871), +partition p357 values less than (737872), +partition p358 values less than (737873), +partition p359 values less than (737874), +partition p360 values less than (737875), +partition p361 values less than (737876), +partition p362 values less than (737877), +partition p363 values less than (737878), +partition p364 values less than (737879), +partition p365 values less than (737880), +partition p366 values less than (737881), +partition p367 values less than (737882), +partition p368 values less than (737883), +partition p369 values less than (737884), +partition p370 values less than (737885), +partition p371 values less than (737886), +partition p372 values less than (737887), +partition p373 values less than (737888), +partition p374 values less than (737889), +partition p375 values less than (737890), +partition p376 values less than (737891), +partition p377 values less than (737892), +partition p378 values less than (737893), +partition p379 values less than (737894), +partition p380 values less than (737895), +partition p381 values less than (737896), +partition p382 values less than (737897), +partition p383 values less than (737898), +partition p384 values less than (737899), +partition p385 values less than (737900), +partition p386 values less than (737901), +partition p387 values less than (737902), +partition p388 values less than (737903), +partition p389 values less than (737904), +partition p390 values less than (737905), +partition p391 values less than (737906), +partition p392 values less than (737907), +partition p393 values less than (737908), +partition p394 values less than (737909), +partition p395 values less than (737910), +partition p396 values less than (737911), +partition p397 values less than (737912), +partition p398 values less than (737913), +partition p399 values less than (737914), +partition p400 values less than (737915), +partition p401 values less than (737916), +partition p402 values less than (737917), +partition p403 values less than (737918), +partition p404 values less than (737919), +partition p405 values less than (737920), +partition p406 values less than (737921), +partition p407 values less than (737922), +partition p408 values less than (737923), +partition p409 values less than (737924), +partition p410 values less than (737925), +partition p411 values less than (737926), +partition p412 values less than (737927), +partition p413 values less than (737928), +partition p414 values less than (737929), +partition p415 values less than (737930), +partition p416 values less than (737931), +partition p417 values less than (737932), +partition p418 values less than (737933), +partition p419 values less than (737934), +partition p420 values less than (737935), +partition p421 values less than (737936), +partition p422 values less than (737937), +partition p423 values less than (737938), +partition p424 values less than (737939), +partition p425 values less than (737940), +partition p426 values less than (737941), +partition p427 values less than (737942), +partition p428 values less than (737943), +partition p429 values less than (737944), +partition p430 values less than (737945), +partition p431 values less than (737946), +partition p432 values less than (737947), +partition p433 values less than (737948), +partition p434 values less than (737949), +partition p435 values less than (737950), +partition p436 values less than (737951), +partition p437 values less than (737952), +partition p438 values less than (737953), +partition p439 values less than (737954), +partition p440 values less than (737955), +partition p441 values less than (737956), +partition p442 values less than (737957), +partition p443 values less than (737958), +partition p444 values less than (737959), +partition p445 values less than (737960), +partition p446 values less than (737961), +partition p447 values less than (737962), +partition p448 values less than (737963), +partition p449 values less than (737964), +partition p450 values less than (737965), +partition p451 values less than (737966), +partition p452 values less than (737967), +partition p453 values less than (737968), +partition p454 values less than (737969), +partition p455 values less than (737970), +partition p456 values less than (737971), +partition p457 values less than (737972), +partition p458 values less than (737973), +partition p459 values less than (737974), +partition p460 values less than (737975), +partition p461 values less than (737976), +partition p462 values less than (737977), +partition p463 values less than (737978), +partition p464 values less than (737979), +partition p465 values less than (737980), +partition p466 values less than (737981), +partition p467 values less than (737982), +partition p468 values less than (737983), +partition p469 values less than (737984), +partition p470 values less than (737985), +partition p471 values less than (737986), +partition p472 values less than (737987), +partition p473 values less than (737988), +partition p474 values less than (737989), +partition p475 values less than (737990), +partition p476 values less than (737991), +partition p477 values less than (737992), +partition p478 values less than (737993), +partition p479 values less than (737994), +partition p480 values less than (737995), +partition p481 values less than (737996), +partition p482 values less than (737997), +partition p483 values less than (737998), +partition p484 values less than (737999), +partition p485 values less than (738000), +partition p486 values less than (738001), +partition p487 values less than (738002), +partition p488 values less than (738003), +partition p489 values less than (738004), +partition p490 values less than (738005), +partition p491 values less than (738006), +partition p492 values less than (738007), +partition p493 values less than (738008), +partition p494 values less than (738009), +partition p495 values less than (738010), +partition p496 values less than (738011), +partition p497 values less than (738012), +partition p498 values less than (738013), +partition p499 values less than (738014), +partition p500 values less than (738015), +partition p501 values less than (738016), +partition p502 values less than (738017), +partition p503 values less than (738018), +partition p504 values less than (738019), +partition p505 values less than (738020), +partition p506 values less than (738021), +partition p507 values less than (738022), +partition p508 values less than (738023), +partition p509 values less than (738024), +partition p510 values less than (738025), +partition p511 values less than (738026), +partition p512 values less than (738027), +partition p513 values less than (738028), +partition p514 values less than (738029), +partition p515 values less than (738030), +partition p516 values less than (738031), +partition p517 values less than (738032), +partition p518 values less than (738033), +partition p519 values less than (738034), +partition p520 values less than (738035), +partition p521 values less than (738036), +partition p522 values less than (738037), +partition p523 values less than (738038), +partition p524 values less than (738039), +partition p525 values less than (738040), +partition p526 values less than (738041), +partition p527 values less than (738042), +partition p528 values less than (738043), +partition p529 values less than (738044), +partition p530 values less than (738045), +partition p531 values less than (738046), +partition p532 values less than (738047), +partition p533 values less than (738048), +partition p534 values less than (738049), +partition p535 values less than (738050), +partition p536 values less than (738051), +partition p537 values less than (738052), +partition p538 values less than (738053), +partition p539 values less than (738054), +partition p540 values less than (738055), +partition p541 values less than (738056), +partition p542 values less than (738057), +partition p543 values less than (738058), +partition p544 values less than (738059), +partition p545 values less than (738060), +partition p546 values less than (738061), +partition p547 values less than (738062), +partition p548 values less than (738063), +partition p549 values less than (738064), +partition p550 values less than (738065), +partition p551 values less than (738066), +partition p552 values less than (738067), +partition p553 values less than (738068), +partition p554 values less than (738069), +partition p555 values less than (738070), +partition p556 values less than (738071), +partition p557 values less than (738072), +partition p558 values less than (738073), +partition p559 values less than (738074), +partition p560 values less than (738075), +partition p561 values less than (738076), +partition p562 values less than (738077), +partition p563 values less than (738078), +partition p564 values less than (738079), +partition p565 values less than (738080), +partition p566 values less than (738081), +partition p567 values less than (738082), +partition p568 values less than (738083), +partition p569 values less than (738084), +partition p570 values less than (738085), +partition p571 values less than (738086), +partition p572 values less than (738087), +partition p573 values less than (738088), +partition p574 values less than (738089), +partition p575 values less than (738090), +partition p576 values less than (738091), +partition p577 values less than (738092), +partition p578 values less than (738093), +partition p579 values less than (738094), +partition p580 values less than (738095), +partition p581 values less than (738096), +partition p582 values less than (738097), +partition p583 values less than (738098), +partition p584 values less than (738099), +partition p585 values less than (738100), +partition p586 values less than (738101), +partition p587 values less than (738102), +partition p588 values less than (738103), +partition p589 values less than (738104), +partition p590 values less than (738105), +partition p591 values less than (738106), +partition p592 values less than (738107), +partition p593 values less than (738108), +partition p594 values less than (738109), +partition p595 values less than (738110), +partition p596 values less than (738111), +partition p597 values less than (738112), +partition p598 values less than (738113), +partition p599 values less than (738114), +partition p600 values less than (738115), +partition p601 values less than (738116), +partition p602 values less than (738117), +partition p603 values less than (738118), +partition p604 values less than (738119), +partition p605 values less than (738120), +partition p606 values less than (738121), +partition p607 values less than (738122), +partition p608 values less than (738123), +partition p609 values less than (738124), +partition p610 values less than (738125), +partition p611 values less than (738126), +partition p612 values less than (738127), +partition p613 values less than (738128), +partition p614 values less than (738129), +partition p615 values less than (738130), +partition p616 values less than (738131), +partition p617 values less than (738132), +partition p618 values less than (738133), +partition p619 values less than (738134), +partition p620 values less than (738135), +partition p621 values less than (738136), +partition p622 values less than (738137), +partition p623 values less than (738138), +partition p624 values less than (738139), +partition p625 values less than (738140), +partition p626 values less than (738141), +partition p627 values less than (738142), +partition p628 values less than (738143), +partition p629 values less than (738144), +partition p630 values less than (738145), +partition p631 values less than (738146), +partition p632 values less than (738147), +partition p633 values less than (738148), +partition p634 values less than (738149), +partition p635 values less than (738150), +partition p636 values less than (738151), +partition p637 values less than (738152), +partition p638 values less than (738153), +partition p639 values less than (738154), +partition p640 values less than (738155), +partition p641 values less than (738156), +partition p642 values less than (738157), +partition p643 values less than (738158), +partition p644 values less than (738159), +partition p645 values less than (738160), +partition p646 values less than (738161), +partition p647 values less than (738162), +partition p648 values less than (738163), +partition p649 values less than (738164), +partition p650 values less than (738165), +partition p651 values less than (738166), +partition p652 values less than (738167), +partition p653 values less than (738168), +partition p654 values less than (738169), +partition p655 values less than (738170), +partition p656 values less than (738171), +partition p657 values less than (738172), +partition p658 values less than (738173), +partition p659 values less than (738174), +partition p660 values less than (738175), +partition p661 values less than (738176), +partition p662 values less than (738177), +partition p663 values less than (738178), +partition p664 values less than (738179), +partition p665 values less than (738180), +partition p666 values less than (738181), +partition p667 values less than (738182), +partition p668 values less than (738183), +partition p669 values less than (738184), +partition p670 values less than (738185), +partition p671 values less than (738186), +partition p672 values less than (738187), +partition p673 values less than (738188), +partition p674 values less than (738189), +partition p675 values less than (738190), +partition p676 values less than (738191), +partition p677 values less than (738192), +partition p678 values less than (738193), +partition p679 values less than (738194), +partition p680 values less than (738195), +partition p681 values less than (738196), +partition p682 values less than (738197), +partition p683 values less than (738198), +partition p684 values less than (738199), +partition p685 values less than (738200), +partition p686 values less than (738201), +partition p687 values less than (738202), +partition p688 values less than (738203), +partition p689 values less than (738204), +partition p690 values less than (738205), +partition p691 values less than (738206), +partition p692 values less than (738207), +partition p693 values less than (738208), +partition p694 values less than (738209), +partition p695 values less than (738210), +partition p696 values less than (738211), +partition p697 values less than (738212), +partition p698 values less than (738213), +partition p699 values less than (738214), +partition p700 values less than (738215), +partition p701 values less than (738216), +partition p702 values less than (738217), +partition p703 values less than (738218), +partition p704 values less than (738219), +partition p705 values less than (738220), +partition p706 values less than (738221), +partition p707 values less than (738222), +partition p708 values less than (738223), +partition p709 values less than (738224), +partition p710 values less than (738225), +partition p711 values less than (738226), +partition p712 values less than (738227), +partition p713 values less than (738228), +partition p714 values less than (738229), +partition p715 values less than (738230), +partition p716 values less than (738231), +partition p717 values less than (738232), +partition p718 values less than (738233), +partition p719 values less than (738234), +partition p720 values less than (738235), +partition p721 values less than (738236), +partition p722 values less than (738237), +partition p723 values less than (738238), +partition p724 values less than (738239), +partition p725 values less than (738240), +partition p726 values less than (738241), +partition p727 values less than (738242), +partition p728 values less than (738243), +partition p729 values less than (738244), +partition p730 values less than (738245), +partition p731 values less than (738246), +partition p732 values less than (738247), +partition p733 values less than (738248), +partition p734 values less than (738249), +partition p735 values less than (738250), +partition p736 values less than (738251), +partition p737 values less than (738252), +partition p738 values less than (738253), +partition p739 values less than (738254), +partition p740 values less than (738255), +partition p741 values less than (738256), +partition p742 values less than (738257), +partition p743 values less than (738258), +partition p744 values less than (738259), +partition p745 values less than (738260), +partition p746 values less than (738261), +partition p747 values less than (738262), +partition p748 values less than (738263), +partition p749 values less than (738264), +partition p750 values less than (738265), +partition p751 values less than (738266), +partition p752 values less than (738267), +partition p753 values less than (738268), +partition p754 values less than (738269), +partition p755 values less than (738270), +partition p756 values less than (738271), +partition p757 values less than (738272), +partition p758 values less than (738273), +partition p759 values less than (738274), +partition p760 values less than (738275), +partition p761 values less than (738276), +partition p762 values less than (738277), +partition p763 values less than (738278), +partition p764 values less than (738279), +partition p765 values less than (738280), +partition p766 values less than (738281), +partition p767 values less than (738282), +partition p768 values less than (738283), +partition p769 values less than (738284), +partition p770 values less than (738285), +partition p771 values less than (738286), +partition p772 values less than (738287), +partition p773 values less than (738288), +partition p774 values less than (738289), +partition p775 values less than (738290), +partition p776 values less than (738291), +partition p777 values less than (738292), +partition p778 values less than (738293), +partition p779 values less than (738294), +partition p780 values less than (738295), +partition p781 values less than (738296), +partition p782 values less than (738297), +partition p783 values less than (738298), +partition p784 values less than (738299), +partition p785 values less than (738300), +partition p786 values less than (738301), +partition p787 values less than (738302), +partition p788 values less than (738303), +partition p789 values less than (738304), +partition p790 values less than (738305), +partition p791 values less than (738306), +partition p792 values less than (738307), +partition p793 values less than (738308), +partition p794 values less than (738309), +partition p795 values less than (738310), +partition p796 values less than (738311), +partition p797 values less than (738312), +partition p798 values less than (738313), +partition p799 values less than (738314), +partition p800 values less than (738315), +partition p801 values less than (738316), +partition p802 values less than (738317), +partition p803 values less than (738318), +partition p804 values less than (738319), +partition p805 values less than (738320), +partition p806 values less than (738321), +partition p807 values less than (738322), +partition p808 values less than (738323), +partition p809 values less than (738324), +partition p810 values less than (738325), +partition p811 values less than (738326), +partition p812 values less than (738327), +partition p813 values less than (738328), +partition p814 values less than (738329), +partition p815 values less than (738330), +partition p816 values less than (738331), +partition p817 values less than (738332), +partition p818 values less than (738333), +partition p819 values less than (738334), +partition p820 values less than (738335), +partition p821 values less than (738336), +partition p822 values less than (738337), +partition p823 values less than (738338), +partition p824 values less than (738339), +partition p825 values less than (738340), +partition p826 values less than (738341), +partition p827 values less than (738342), +partition p828 values less than (738343), +partition p829 values less than (738344), +partition p830 values less than (738345), +partition p831 values less than (738346), +partition p832 values less than (738347), +partition p833 values less than (738348), +partition p834 values less than (738349), +partition p835 values less than (738350), +partition p836 values less than (738351), +partition p837 values less than (738352), +partition p838 values less than (738353), +partition p839 values less than (738354), +partition p840 values less than (738355), +partition p841 values less than (738356), +partition p842 values less than (738357), +partition p843 values less than (738358), +partition p844 values less than (738359), +partition p845 values less than (738360), +partition p846 values less than (738361), +partition p847 values less than (738362), +partition p848 values less than (738363), +partition p849 values less than (738364), +partition p850 values less than (738365), +partition p851 values less than (738366), +partition p852 values less than (738367), +partition p853 values less than (738368), +partition p854 values less than (738369), +partition p855 values less than (738370), +partition p856 values less than (738371), +partition p857 values less than (738372), +partition p858 values less than (738373), +partition p859 values less than (738374), +partition p860 values less than (738375), +partition p861 values less than (738376), +partition p862 values less than (738377), +partition p863 values less than (738378), +partition p864 values less than (738379), +partition p865 values less than (738380), +partition p866 values less than (738381), +partition p867 values less than (738382), +partition p868 values less than (738383), +partition p869 values less than (738384), +partition p870 values less than (738385), +partition p871 values less than (738386), +partition p872 values less than (738387), +partition p873 values less than (738388), +partition p874 values less than (738389), +partition p875 values less than (738390), +partition p876 values less than (738391), +partition p877 values less than (738392), +partition p878 values less than (738393), +partition p879 values less than (738394), +partition p880 values less than (738395), +partition p881 values less than (738396), +partition p882 values less than (738397), +partition p883 values less than (738398), +partition p884 values less than (738399), +partition p885 values less than (738400), +partition p886 values less than (738401), +partition p887 values less than (738402), +partition p888 values less than (738403), +partition p889 values less than (738404), +partition p890 values less than (738405), +partition p891 values less than (738406), +partition p892 values less than (738407), +partition p893 values less than (738408), +partition p894 values less than (738409), +partition p895 values less than (738410), +partition p896 values less than (738411), +partition p897 values less than (738412), +partition p898 values less than (738413), +partition p899 values less than (738414), +partition p900 values less than (738415), +partition p901 values less than (738416), +partition p902 values less than (738417), +partition p903 values less than (738418), +partition p904 values less than (738419), +partition p905 values less than (738420), +partition p906 values less than (738421), +partition p907 values less than (738422), +partition p908 values less than (738423), +partition p909 values less than (738424), +partition p910 values less than (738425), +partition p911 values less than (738426), +partition p912 values less than (738427), +partition p913 values less than (738428), +partition p914 values less than (738429), +partition p915 values less than (738430), +partition p916 values less than (738431), +partition p917 values less than (738432), +partition p918 values less than (738433), +partition p919 values less than (738434), +partition p920 values less than (738435), +partition p921 values less than (738436), +partition p922 values less than (738437), +partition p923 values less than (738438), +partition p924 values less than (738439), +partition p925 values less than (738440), +partition p926 values less than (738441), +partition p927 values less than (738442), +partition p928 values less than (738443), +partition p929 values less than (738444), +partition p930 values less than (738445), +partition p931 values less than (738446), +partition p932 values less than (738447), +partition p933 values less than (738448), +partition p934 values less than (738449), +partition p935 values less than (738450), +partition p936 values less than (738451), +partition p937 values less than (738452), +partition p938 values less than (738453), +partition p939 values less than (738454), +partition p940 values less than (738455), +partition p941 values less than (738456), +partition p942 values less than (738457), +partition p943 values less than (738458), +partition p944 values less than (738459), +partition p945 values less than (738460), +partition p946 values less than (738461), +partition p947 values less than (738462), +partition p948 values less than (738463), +partition p949 values less than (738464), +partition p950 values less than (738465), +partition p951 values less than (738466), +partition p952 values less than (738467), +partition p953 values less than (738468), +partition p954 values less than (738469), +partition p955 values less than (738470), +partition p956 values less than (738471), +partition p957 values less than (738472), +partition p958 values less than (738473), +partition p959 values less than (738474), +partition p960 values less than (738475), +partition p961 values less than (738476), +partition p962 values less than (738477), +partition p963 values less than (738478), +partition p964 values less than (738479), +partition p965 values less than (738480), +partition p966 values less than (738481), +partition p967 values less than (738482), +partition p968 values less than (738483), +partition p969 values less than (738484), +partition p970 values less than (738485), +partition p971 values less than (738486), +partition p972 values less than (738487), +partition p973 values less than (738488), +partition p974 values less than (738489), +partition p975 values less than (738490), +partition p976 values less than (738491), +partition p977 values less than (738492), +partition p978 values less than (738493), +partition p979 values less than (738494), +partition p980 values less than (738495), +partition p981 values less than (738496), +partition p982 values less than (738497), +partition p983 values less than (738498), +partition p984 values less than (738499), +partition p985 values less than (738500), +partition p986 values less than (738501), +partition p987 values less than (738502), +partition p988 values less than (738503), +partition p989 values less than (738504), +partition p990 values less than (738505), +partition p991 values less than (738506), +partition p992 values less than (738507), +partition p993 values less than (738508), +partition p994 values less than (738509), +partition p995 values less than (738510), +partition p996 values less than (738511), +partition p997 values less than (738512), +partition p998 values less than (738513), +partition p999 values less than (738514), +partition p1000 values less than (738515), +partition p1001 values less than (738516), +partition p1002 values less than (738517), +partition p1003 values less than (738518), +partition p1004 values less than (738519), +partition p1005 values less than (738520), +partition p1006 values less than (738521), +partition p1007 values less than (738522), +partition p1008 values less than (738523), +partition p1009 values less than (738524), +partition p1010 values less than (738525), +partition p1011 values less than (738526), +partition p1012 values less than (738527), +partition p1013 values less than (738528), +partition p1014 values less than (738529), +partition p1015 values less than (738530), +partition p1016 values less than (738531), +partition p1017 values less than (738532), +partition p1018 values less than (738533), +partition p1019 values less than (738534), +partition p1020 values less than (738535), +partition p1021 values less than (738536), +partition p1022 values less than (738537), +partition p1023 values less than (738538) +)`) + + _, err := se.Execute(ctx, "analyze table t") + if err != nil { + b.Fatal(err) + } + alloc := chunk.NewAllocator() + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where dt > to_days('2019-04-01 21:00:00') and dt < to_days('2019-04-07 23:59:59')") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkRangeColumnPartitionPruning(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + var build strings.Builder + build.WriteString(`create table t (id int, dt date) partition by range columns (dt) (`) + start := time.Date(2020, 5, 15, 0, 0, 0, 0, time.UTC) + for i := 0; i < 1023; i++ { + start = start.Add(24 * time.Hour) + fmt.Fprintf(&build, "partition p%d values less than ('%s'),\n", i, start.Format(time.DateOnly)) + } + build.WriteString("partition p1023 values less than maxvalue)") + mustExecute(se, build.String()) + alloc := chunk.NewAllocator() + _, err := se.Execute(ctx, "analyze table t") + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where dt > '2020-05-01' and dt < '2020-06-07'") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkHashPartitionPruningPointSelect(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + alloc := chunk.NewAllocator() + mustExecute(se, `create table t (id int, dt datetime) partition by hash(id) partitions 1024;`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where id = 2330") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkHashPartitionPruningMultiSelect(b *testing.B) { + ctx := context.Background() + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + alloc := chunk.NewAllocator() + mustExecute(se, `create table t (id int, dt datetime) partition by hash(id) partitions 1024;`) + b.ResetTimer() + for i := 0; i < b.N; i++ { + rs, err := se.Execute(ctx, "select * from t where id = 2330") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + rs, err = se.Execute(ctx, "select * from t where id = 1233 or id = 1512") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + rs, err = se.Execute(ctx, "select * from t where id in (117, 1233, 15678)") + if err != nil { + b.Fatal(err) + } + _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) + if err != nil { + b.Fatal(err) + } + alloc.Reset() + } + b.StopTimer() +} + +func BenchmarkInsertIntoSelect(b *testing.B) { + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, `set @@tidb_enable_mutation_checker = 0`) + mustExecute(se, `set @@tmp_table_size = 1000000000`) + mustExecute(se, `create global temporary table tmp (id int, dt varchar(512)) on commit delete rows`) + mustExecute(se, `create table src (id int, dt varchar(512))`) + for i := 0; i < 100; i++ { + mustExecute(se, "begin") + for lines := 0; lines < 100; lines++ { + mustExecute(se, "insert into src values (42, repeat('x', 512)), (66, repeat('x', 512))") + } + mustExecute(se, "commit") + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + mustExecute(se, "insert into tmp select * from src") + } + b.StopTimer() +} + +func BenchmarkCompileStmt(b *testing.B) { + // See issue https://github.com/pingcap/tidb/issues/27633 + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + + mustExecute(se, `CREATE TABLE item1 ( + a varchar(200) DEFAULT NULL, + b varchar(480) DEFAULT NULL, + c varchar(200) DEFAULT NULL, + d varchar(200) DEFAULT NULL, + e varchar(200) DEFAULT NULL, + f varchar(200) DEFAULT NULL, + g varchar(3999) DEFAULT NULL, + h bigint(38) DEFAULT NULL, + i varchar(80) DEFAULT NULL, + j bigint(38) DEFAULT NULL, + k varchar(480) DEFAULT NULL, + l varchar(480) DEFAULT NULL, + m decimal(18,4) DEFAULT NULL, + n decimal(18,4) DEFAULT NULL, + o decimal(22,8) DEFAULT NULL, + p varchar(8) DEFAULT NULL, + q decimal(18,4) DEFAULT NULL, + r decimal(18,4) DEFAULT NULL, + s varchar(40) DEFAULT NULL, + t decimal(18,4) DEFAULT NULL, + u decimal(18,4) DEFAULT NULL, + v decimal(18,4) DEFAULT NULL, + w decimal(18,5) DEFAULT NULL, + x decimal(12,8) DEFAULT NULL, + y varchar(40) DEFAULT NULL, + z decimal(12,8) DEFAULT NULL, + a1 decimal(18,4) DEFAULT NULL, + b1 decimal(18,4) DEFAULT NULL, + c1 decimal(18,4) DEFAULT NULL, + d1 decimal(18,4) DEFAULT NULL, + e1 decimal(12,8) DEFAULT NULL, + f1 varchar(40) DEFAULT NULL, + g1 decimal(18,4) DEFAULT NULL, + h1 decimal(18,4) DEFAULT NULL, + i1 decimal(18,4) DEFAULT NULL, + j1 decimal(18,4) DEFAULT NULL, + k1 varchar(40) DEFAULT NULL, + l1 decimal(14,8) DEFAULT NULL, + m1 bigint(38) DEFAULT NULL, + n1 varchar(8) DEFAULT NULL, + o1 varchar(40) DEFAULT NULL, + p1 decimal(12,8) DEFAULT NULL, + q1 varchar(480) DEFAULT NULL, + r1 varchar(480) DEFAULT NULL, + s1 decimal(12,8) DEFAULT NULL, + t1 decimal(14,10) DEFAULT NULL, + u1 decimal(18,4) DEFAULT NULL, + v1 decimal(18,4) DEFAULT NULL, + w1 varchar(8) DEFAULT NULL, + x1 decimal(18,4) DEFAULT NULL, + y1 datetime DEFAULT NULL, + z1 datetime DEFAULT NULL, + a2 decimal(18,4) DEFAULT NULL, + b2 decimal(18,4) DEFAULT NULL, + c2 decimal(18,4) DEFAULT NULL, + d2 decimal(18,4) DEFAULT NULL, + e2 decimal(12,8) DEFAULT NULL, + f2 varchar(40) DEFAULT NULL, + g2 decimal(18,4) DEFAULT NULL, + h2 decimal(18,4) DEFAULT NULL, + i2 decimal(18,4) DEFAULT NULL, + j2 decimal(18,4) DEFAULT NULL, + k2 varchar(40) DEFAULT NULL, + l2 decimal(14,8) DEFAULT NULL, + m2 bigint(38) DEFAULT NULL, + n2 varchar(8) DEFAULT NULL, + o2 varchar(40) DEFAULT NULL, + p2 decimal(12,8) DEFAULT NULL, + q2 varchar(480) DEFAULT NULL, + r2 varchar(480) DEFAULT NULL, + s2 decimal(12,8) DEFAULT NULL, + t2 decimal(14,10) DEFAULT NULL, + u2 decimal(18,4) DEFAULT NULL, + v2 decimal(18,4) DEFAULT NULL, + w2 varchar(8) DEFAULT NULL, + x2 decimal(18,4) DEFAULT NULL, + y2 datetime DEFAULT NULL, + z2 datetime DEFAULT NULL)`) + + mustExecute(se, `CREATE TABLE item2 like item1`) + + stmtID, _, _, err := se.PrepareStmt("insert into item2 select * from item1 where a1 = ?") + if err != nil { + b.Fatal(err) + } + prepStmt, err := se.GetSessionVars().GetPreparedStmtByID(stmtID) + if err != nil { + b.Fatal(err) + } + + args := expression.Args2Expressions4Test(3401544) + + b.ResetTimer() + stmtExec := &ast.ExecuteStmt{PrepStmt: prepStmt, BinaryArgs: args} + compiler := executor.Compiler{Ctx: se} + for i := 0; i < b.N; i++ { + _, err := compiler.Compile(context.Background(), stmtExec) + if err != nil { + b.Fatal(err) + } + } + b.StopTimer() +} + +func BenchmarkAutoIncrement(b *testing.B) { + se, do, st := prepareBenchSession() + defer func() { + se.Close() + do.Close() + st.Close() + }() + mustExecute(se, "create table auto_inc (id int unsigned key nonclustered auto_increment) shard_row_id_bits=4 auto_id_cache 1;") + mustExecute(se, "set @@tidb_enable_mutation_checker = false") + b.ResetTimer() + for i := 0; i < b.N; i++ { + mustExecute(se, "insert into auto_inc values ()") + } + b.StopTimer() +} + +// TestBenchDaily collects the daily benchmark test result and generates a json output file. +// The format of the json output is described by the BenchOutput. +// Used by this command in the Makefile +// +// make bench-daily TO=xxx.json +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkPreparedPointGet, + BenchmarkPointGet, + BenchmarkBatchPointGet, + BenchmarkBasic, + BenchmarkTableScan, + BenchmarkTableLookup, + BenchmarkExplainTableLookup, + BenchmarkStringIndexScan, + BenchmarkExplainStringIndexScan, + BenchmarkStringIndexLookup, + BenchmarkIntegerIndexScan, + BenchmarkIntegerIndexLookup, + BenchmarkDecimalIndexScan, + BenchmarkDecimalIndexLookup, + BenchmarkInsertWithIndex, + BenchmarkInsertNoIndex, + BenchmarkSort, + BenchmarkJoin, + BenchmarkJoinLimit, + BenchmarkPartitionPruning, + BenchmarkRangeColumnPartitionPruning, + BenchmarkHashPartitionPruningPointSelect, + BenchmarkHashPartitionPruningMultiSelect, + BenchmarkInsertIntoSelect, + BenchmarkCompileStmt, + BenchmarkAutoIncrement, + ) +} diff --git a/pkg/session/bootstrap.go b/pkg/session/bootstrap.go new file mode 100644 index 0000000000000..88221c6ab62cc --- /dev/null +++ b/pkg/session/bootstrap.go @@ -0,0 +1,3152 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package session + +import ( + "context" + "encoding/hex" + "fmt" + "os" + osuser "os/user" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table/tables" + timertable "github.com/pingcap/tidb/pkg/timer/tablestore" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/timeutil" + "go.etcd.io/etcd/client/v3/concurrency" + "go.uber.org/zap" +) + +const ( + // CreateUserTable is the SQL statement creates User table in system db. + // WARNING: There are some limitations on altering the schema of mysql.user table. + // Adding columns that are nullable or have default values is permitted. + // But operations like dropping or renaming columns may break the compatibility with BR. + // REFERENCE ISSUE: https://github.com/pingcap/tidb/issues/38785 + CreateUserTable = `CREATE TABLE IF NOT EXISTS mysql.user ( + Host CHAR(255), + User CHAR(32), + authentication_string TEXT, + plugin CHAR(64), + Select_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Insert_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Update_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Delete_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Drop_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Process_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Grant_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + References_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Alter_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Show_db_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Super_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Execute_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Index_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_user_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Repl_slave_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Repl_client_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Drop_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Account_locked ENUM('N','Y') NOT NULL DEFAULT 'N', + Shutdown_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Reload_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + FILE_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Config_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_Tablespace_Priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Password_reuse_history smallint unsigned DEFAULT NULL, + Password_reuse_time smallint unsigned DEFAULT NULL, + User_attributes json, + Token_issuer VARCHAR(255), + Password_expired ENUM('N','Y') NOT NULL DEFAULT 'N', + Password_last_changed TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + Password_lifetime SMALLINT UNSIGNED DEFAULT NULL, + PRIMARY KEY (Host, User));` + // CreateGlobalPrivTable is the SQL statement creates Global scope privilege table in system db. + CreateGlobalPrivTable = "CREATE TABLE IF NOT EXISTS mysql.global_priv (" + + "Host CHAR(255) NOT NULL DEFAULT ''," + + "User CHAR(80) NOT NULL DEFAULT ''," + + "Priv LONGTEXT NOT NULL DEFAULT ''," + + "PRIMARY KEY (Host, User)" + + ")" + + // For `mysql.db`, `mysql.tables_priv` and `mysql.columns_priv` table, we have a slight different + // schema definition with MySQL: columns `DB`/`Table_name`/`Column_name` are defined with case-insensitive + // collation(in MySQL, they are case-sensitive). + + // The reason behind this is that when writing those records, MySQL always converts those names into lower case + // while TiDB does not do so in early implementations, which makes some 'GRANT'/'REVOKE' operations case-sensitive. + + // In order to fix this, we decide to explicitly set case-insensitive collation for the related columns here, to + // make sure: + // * The 'GRANT'/'REVOKE' could be case-insensitive for new clusters(compatible with MySQL). + // * Keep all behaviors unchanged for upgraded cluster. + + // CreateDBPrivTable is the SQL statement creates DB scope privilege table in system db. + CreateDBPrivTable = `CREATE TABLE IF NOT EXISTS mysql.db ( + Host CHAR(255), + DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + User CHAR(32), + Select_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Insert_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Update_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Delete_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Drop_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Grant_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + References_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Index_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Alter_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Execute_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', + PRIMARY KEY (Host, DB, User));` + // CreateTablePrivTable is the SQL statement creates table scope privilege table in system db. + CreateTablePrivTable = `CREATE TABLE IF NOT EXISTS mysql.tables_priv ( + Host CHAR(255), + DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + User CHAR(32), + Table_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + Grantor CHAR(77), + Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + Table_priv SET('Select','Insert','Update','Delete','Create','Drop','Grant','Index','Alter','Create View','Show View','Trigger','References'), + Column_priv SET('Select','Insert','Update','References'), + PRIMARY KEY (Host, DB, User, Table_name));` + // CreateColumnPrivTable is the SQL statement creates column scope privilege table in system db. + CreateColumnPrivTable = `CREATE TABLE IF NOT EXISTS mysql.columns_priv( + Host CHAR(255), + DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + User CHAR(32), + Table_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + Column_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, + Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + Column_priv SET('Select','Insert','Update','References'), + PRIMARY KEY (Host, DB, User, Table_name, Column_name));` + // CreateGlobalVariablesTable is the SQL statement creates global variable table in system db. + // TODO: MySQL puts GLOBAL_VARIABLES table in INFORMATION_SCHEMA db. + // INFORMATION_SCHEMA is a virtual db in TiDB. So we put this table in system db. + // Maybe we will put it back to INFORMATION_SCHEMA. + CreateGlobalVariablesTable = `CREATE TABLE IF NOT EXISTS mysql.GLOBAL_VARIABLES( + VARIABLE_NAME VARCHAR(64) NOT NULL PRIMARY KEY, + VARIABLE_VALUE VARCHAR(1024) DEFAULT NULL);` + // CreateTiDBTable is the SQL statement creates a table in system db. + // This table is a key-value struct contains some information used by TiDB. + // Currently we only put bootstrapped in it which indicates if the system is already bootstrapped. + CreateTiDBTable = `CREATE TABLE IF NOT EXISTS mysql.tidb( + VARIABLE_NAME VARCHAR(64) NOT NULL PRIMARY KEY, + VARIABLE_VALUE VARCHAR(1024) DEFAULT NULL, + COMMENT VARCHAR(1024));` + + // CreateHelpTopic is the SQL statement creates help_topic table in system db. + // See: https://dev.mysql.com/doc/refman/5.5/en/system-database.html#system-database-help-tables + CreateHelpTopic = `CREATE TABLE IF NOT EXISTS mysql.help_topic ( + help_topic_id INT(10) UNSIGNED NOT NULL, + name CHAR(64) NOT NULL, + help_category_id SMALLINT(5) UNSIGNED NOT NULL, + description TEXT NOT NULL, + example TEXT NOT NULL, + url TEXT NOT NULL, + PRIMARY KEY (help_topic_id) clustered, + UNIQUE KEY name (name) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help topics';` + + // CreateStatsMetaTable stores the meta of table statistics. + CreateStatsMetaTable = `CREATE TABLE IF NOT EXISTS mysql.stats_meta ( + version BIGINT(64) UNSIGNED NOT NULL, + table_id BIGINT(64) NOT NULL, + modify_count BIGINT(64) NOT NULL DEFAULT 0, + count BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, + snapshot BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, + INDEX idx_ver(version), + UNIQUE INDEX tbl(table_id) + );` + + // CreateStatsColsTable stores the statistics of table columns. + CreateStatsColsTable = `CREATE TABLE IF NOT EXISTS mysql.stats_histograms ( + table_id BIGINT(64) NOT NULL, + is_index TINYINT(2) NOT NULL, + hist_id BIGINT(64) NOT NULL, + distinct_count BIGINT(64) NOT NULL, + null_count BIGINT(64) NOT NULL DEFAULT 0, + tot_col_size BIGINT(64) NOT NULL DEFAULT 0, + modify_count BIGINT(64) NOT NULL DEFAULT 0, + version BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, + cm_sketch BLOB(6291456), + stats_ver BIGINT(64) NOT NULL DEFAULT 0, + flag BIGINT(64) NOT NULL DEFAULT 0, + correlation DOUBLE NOT NULL DEFAULT 0, + last_analyze_pos LONGBLOB DEFAULT NULL, + UNIQUE INDEX tbl(table_id, is_index, hist_id) + );` + + // CreateStatsBucketsTable stores the histogram info for every table columns. + CreateStatsBucketsTable = `CREATE TABLE IF NOT EXISTS mysql.stats_buckets ( + table_id BIGINT(64) NOT NULL, + is_index TINYINT(2) NOT NULL, + hist_id BIGINT(64) NOT NULL, + bucket_id BIGINT(64) NOT NULL, + count BIGINT(64) NOT NULL, + repeats BIGINT(64) NOT NULL, + upper_bound LONGBLOB NOT NULL, + lower_bound LONGBLOB , + ndv BIGINT NOT NULL DEFAULT 0, + UNIQUE INDEX tbl(table_id, is_index, hist_id, bucket_id) + );` + + // CreateGCDeleteRangeTable stores schemas which can be deleted by DeleteRange. + CreateGCDeleteRangeTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range ( + job_id BIGINT NOT NULL COMMENT "the DDL job ID", + element_id BIGINT NOT NULL COMMENT "the schema element ID", + start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + ts BIGINT NOT NULL COMMENT "timestamp in uint64", + UNIQUE KEY delete_range_index (job_id, element_id) + );` + + // CreateGCDeleteRangeDoneTable stores schemas which are already deleted by DeleteRange. + CreateGCDeleteRangeDoneTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range_done ( + job_id BIGINT NOT NULL COMMENT "the DDL job ID", + element_id BIGINT NOT NULL COMMENT "the schema element ID", + start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", + ts BIGINT NOT NULL COMMENT "timestamp in uint64", + UNIQUE KEY delete_range_done_index (job_id, element_id) + );` + + // CreateStatsFeedbackTable stores the feedback info which is used to update stats. + // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. + CreateStatsFeedbackTable = `CREATE TABLE IF NOT EXISTS mysql.stats_feedback ( + table_id BIGINT(64) NOT NULL, + is_index TINYINT(2) NOT NULL, + hist_id BIGINT(64) NOT NULL, + feedback BLOB NOT NULL, + INDEX hist(table_id, is_index, hist_id) + );` + + // CreateBindInfoTable stores the sql bind info which is used to update globalBindCache. + CreateBindInfoTable = `CREATE TABLE IF NOT EXISTS mysql.bind_info ( + original_sql TEXT NOT NULL, + bind_sql TEXT NOT NULL, + default_db TEXT NOT NULL, + status TEXT NOT NULL, + create_time TIMESTAMP(3) NOT NULL, + update_time TIMESTAMP(3) NOT NULL, + charset TEXT NOT NULL, + collation TEXT NOT NULL, + source VARCHAR(10) NOT NULL DEFAULT 'unknown', + sql_digest varchar(64), + plan_digest varchar(64), + INDEX sql_index(original_sql(700),default_db(68)) COMMENT "accelerate the speed when add global binding query", + INDEX time_index(update_time) COMMENT "accelerate the speed when querying with last update time" + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` + + // CreateRoleEdgesTable stores the role and user relationship information. + CreateRoleEdgesTable = `CREATE TABLE IF NOT EXISTS mysql.role_edges ( + FROM_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', + FROM_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', + TO_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', + TO_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', + WITH_ADMIN_OPTION ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N', + PRIMARY KEY (FROM_HOST,FROM_USER,TO_HOST,TO_USER) + );` + + // CreateDefaultRolesTable stores the active roles for a user. + CreateDefaultRolesTable = `CREATE TABLE IF NOT EXISTS mysql.default_roles ( + HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', + USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', + DEFAULT_ROLE_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '%', + DEFAULT_ROLE_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', + PRIMARY KEY (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) + )` + + // CreateStatsTopNTable stores topn data of a cmsketch with top n. + CreateStatsTopNTable = `CREATE TABLE IF NOT EXISTS mysql.stats_top_n ( + table_id BIGINT(64) NOT NULL, + is_index TINYINT(2) NOT NULL, + hist_id BIGINT(64) NOT NULL, + value LONGBLOB, + count BIGINT(64) UNSIGNED NOT NULL, + INDEX tbl(table_id, is_index, hist_id) + );` + + // CreateStatsFMSketchTable stores FMSketch data of a column histogram. + CreateStatsFMSketchTable = `CREATE TABLE IF NOT EXISTS mysql.stats_fm_sketch ( + table_id BIGINT(64) NOT NULL, + is_index TINYINT(2) NOT NULL, + hist_id BIGINT(64) NOT NULL, + value LONGBLOB, + INDEX tbl(table_id, is_index, hist_id) + );` + + // CreateExprPushdownBlacklist stores the expressions which are not allowed to be pushed down. + CreateExprPushdownBlacklist = `CREATE TABLE IF NOT EXISTS mysql.expr_pushdown_blacklist ( + name CHAR(100) NOT NULL, + store_type CHAR(100) NOT NULL DEFAULT 'tikv,tiflash,tidb', + reason VARCHAR(200) + );` + + // CreateOptRuleBlacklist stores the list of disabled optimizing operations. + CreateOptRuleBlacklist = `CREATE TABLE IF NOT EXISTS mysql.opt_rule_blacklist ( + name CHAR(100) NOT NULL + );` + + // CreateStatsExtended stores the registered extended statistics. + CreateStatsExtended = `CREATE TABLE IF NOT EXISTS mysql.stats_extended ( + name varchar(32) NOT NULL, + type tinyint(4) NOT NULL, + table_id bigint(64) NOT NULL, + column_ids varchar(32) NOT NULL, + stats blob DEFAULT NULL, + version bigint(64) unsigned NOT NULL, + status tinyint(4) NOT NULL, + PRIMARY KEY(name, table_id), + KEY idx_1 (table_id, status, version), + KEY idx_2 (status, version) + );` + + // CreateSchemaIndexUsageTable stores the index usage information. + CreateSchemaIndexUsageTable = `CREATE TABLE IF NOT EXISTS mysql.schema_index_usage ( + TABLE_ID bigint(64), + INDEX_ID bigint(21), + QUERY_COUNT bigint(64), + ROWS_SELECTED bigint(64), + LAST_USED_AT timestamp, + PRIMARY KEY(TABLE_ID, INDEX_ID) + );` + // CreateGlobalGrantsTable stores dynamic privs + CreateGlobalGrantsTable = `CREATE TABLE IF NOT EXISTS mysql.global_grants ( + USER char(32) NOT NULL DEFAULT '', + HOST char(255) NOT NULL DEFAULT '', + PRIV char(32) NOT NULL DEFAULT '', + WITH_GRANT_OPTION enum('N','Y') NOT NULL DEFAULT 'N', + PRIMARY KEY (USER,HOST,PRIV) + );` + // CreateCapturePlanBaselinesBlacklist stores the baseline capture filter rules. + CreateCapturePlanBaselinesBlacklist = `CREATE TABLE IF NOT EXISTS mysql.capture_plan_baselines_blacklist ( + id bigint(64) auto_increment, + filter_type varchar(32) NOT NULL COMMENT "type of the filter, only db, table and frequency supported now", + filter_value varchar(32) NOT NULL, + key idx(filter_type), + primary key(id) + );` + // CreateColumnStatsUsageTable stores the column stats usage information. + CreateColumnStatsUsageTable = `CREATE TABLE IF NOT EXISTS mysql.column_stats_usage ( + table_id BIGINT(64) NOT NULL, + column_id BIGINT(64) NOT NULL, + last_used_at TIMESTAMP, + last_analyzed_at TIMESTAMP, + PRIMARY KEY (table_id, column_id) CLUSTERED + );` + // CreateTableCacheMetaTable stores the cached table meta lock information. + CreateTableCacheMetaTable = `CREATE TABLE IF NOT EXISTS mysql.table_cache_meta ( + tid bigint(11) NOT NULL DEFAULT 0, + lock_type enum('NONE','READ', 'INTEND', 'WRITE') NOT NULL DEFAULT 'NONE', + lease bigint(20) NOT NULL DEFAULT 0, + oldReadLease bigint(20) NOT NULL DEFAULT 0, + PRIMARY KEY (tid) + );` + // CreateAnalyzeOptionsTable stores the analyze options used by analyze and auto analyze. + CreateAnalyzeOptionsTable = `CREATE TABLE IF NOT EXISTS mysql.analyze_options ( + table_id BIGINT(64) NOT NULL, + sample_num BIGINT(64) NOT NULL DEFAULT 0, + sample_rate DOUBLE NOT NULL DEFAULT -1, + buckets BIGINT(64) NOT NULL DEFAULT 0, + topn BIGINT(64) NOT NULL DEFAULT -1, + column_choice enum('DEFAULT','ALL','PREDICATE','LIST') NOT NULL DEFAULT 'DEFAULT', + column_ids TEXT(19372), + PRIMARY KEY (table_id) CLUSTERED + );` + // CreateStatsHistory stores the historical stats. + CreateStatsHistory = `CREATE TABLE IF NOT EXISTS mysql.stats_history ( + table_id bigint(64) NOT NULL, + stats_data longblob NOT NULL, + seq_no bigint(64) NOT NULL comment 'sequence number of the gzipped data slice', + version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN', + create_time datetime(6) NOT NULL, + UNIQUE KEY table_version_seq (table_id, version, seq_no), + KEY table_create_time (table_id, create_time, seq_no), + KEY idx_create_time (create_time) + );` + // CreateStatsMetaHistory stores the historical meta stats. + CreateStatsMetaHistory = `CREATE TABLE IF NOT EXISTS mysql.stats_meta_history ( + table_id bigint(64) NOT NULL, + modify_count bigint(64) NOT NULL, + count bigint(64) NOT NULL, + version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN', + source varchar(40) NOT NULL, + create_time datetime(6) NOT NULL, + UNIQUE KEY table_version (table_id, version), + KEY table_create_time (table_id, create_time), + KEY idx_create_time (create_time) + );` + // CreateAnalyzeJobs stores the analyze jobs. + CreateAnalyzeJobs = `CREATE TABLE IF NOT EXISTS mysql.analyze_jobs ( + id BIGINT(64) UNSIGNED NOT NULL AUTO_INCREMENT, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + table_schema CHAR(64) NOT NULL DEFAULT '', + table_name CHAR(64) NOT NULL DEFAULT '', + partition_name CHAR(64) NOT NULL DEFAULT '', + job_info TEXT NOT NULL, + processed_rows BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, + start_time TIMESTAMP, + end_time TIMESTAMP, + state ENUM('pending', 'running', 'finished', 'failed') NOT NULL, + fail_reason TEXT, + instance VARCHAR(512) NOT NULL comment 'address of the TiDB instance executing the analyze job', + process_id BIGINT(64) UNSIGNED comment 'ID of the process executing the analyze job', + PRIMARY KEY (id), + KEY (update_time) + );` + // CreateAdvisoryLocks stores the advisory locks (get_lock, release_lock). + CreateAdvisoryLocks = `CREATE TABLE IF NOT EXISTS mysql.advisory_locks ( + lock_name VARCHAR(64) NOT NULL PRIMARY KEY + );` + // CreateMDLView is a view about metadata locks. + CreateMDLView = `CREATE OR REPLACE VIEW mysql.tidb_mdl_view as ( + SELECT job_id, + db_name, + table_name, + query, + session_id, + txnstart, + tidb_decode_sql_digests(all_sql_digests, 4096) AS SQL_DIGESTS + FROM information_schema.ddl_jobs, + information_schema.cluster_tidb_trx, + information_schema.cluster_processlist + WHERE (ddl_jobs.state != 'synced' and ddl_jobs.state != 'cancelled') + AND Find_in_set(ddl_jobs.table_id, cluster_tidb_trx.related_table_ids) + AND cluster_tidb_trx.session_id = cluster_processlist.id + );` + + // CreatePlanReplayerStatusTable is a table about plan replayer status + CreatePlanReplayerStatusTable = `CREATE TABLE IF NOT EXISTS mysql.plan_replayer_status ( + sql_digest VARCHAR(128), + plan_digest VARCHAR(128), + origin_sql TEXT, + token VARCHAR(128), + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + fail_reason TEXT, + instance VARCHAR(512) NOT NULL comment 'address of the TiDB instance executing the plan replayer job');` + + // CreatePlanReplayerTaskTable is a table about plan replayer capture task + CreatePlanReplayerTaskTable = `CREATE TABLE IF NOT EXISTS mysql.plan_replayer_task ( + sql_digest VARCHAR(128) NOT NULL, + plan_digest VARCHAR(128) NOT NULL, + update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (sql_digest,plan_digest));` + + // CreateStatsTableLocked stores the locked tables + CreateStatsTableLocked = `CREATE TABLE IF NOT EXISTS mysql.stats_table_locked( + table_id bigint(64) NOT NULL, + modify_count bigint(64) NOT NULL DEFAULT 0, + count bigint(64) NOT NULL DEFAULT 0, + version bigint(64) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (table_id));` + + // CreatePasswordHistory is a table save history passwd. + CreatePasswordHistory = `CREATE TABLE IF NOT EXISTS mysql.password_history ( + Host char(255) NOT NULL DEFAULT '', + User char(32) NOT NULL DEFAULT '', + Password_timestamp timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + Password text, + PRIMARY KEY (Host,User,Password_timestamp ) + ) COMMENT='Password history for user accounts' ` + + // CreateTTLTableStatus is a table about TTL job schedule + CreateTTLTableStatus = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_table_status ( + table_id bigint(64) PRIMARY KEY, + parent_table_id bigint(64), + table_statistics text DEFAULT NULL, + last_job_id varchar(64) DEFAULT NULL, + last_job_start_time timestamp NULL DEFAULT NULL, + last_job_finish_time timestamp NULL DEFAULT NULL, + last_job_ttl_expire timestamp NULL DEFAULT NULL, + last_job_summary text DEFAULT NULL, + current_job_id varchar(64) DEFAULT NULL, + current_job_owner_id varchar(64) DEFAULT NULL, + current_job_owner_addr varchar(256) DEFAULT NULL, + current_job_owner_hb_time timestamp, + current_job_start_time timestamp NULL DEFAULT NULL, + current_job_ttl_expire timestamp NULL DEFAULT NULL, + current_job_state text DEFAULT NULL, + current_job_status varchar(64) DEFAULT NULL, + current_job_status_update_time timestamp NULL DEFAULT NULL);` + + // CreateTTLTask is a table about parallel ttl tasks + CreateTTLTask = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_task ( + job_id varchar(64) NOT NULL, + table_id bigint(64) NOT NULL, + scan_id int NOT NULL, + scan_range_start BLOB, + scan_range_end BLOB, + expire_time timestamp NOT NULL, + owner_id varchar(64) DEFAULT NULL, + owner_addr varchar(64) DEFAULT NULL, + owner_hb_time timestamp DEFAULT NULL, + status varchar(64) DEFAULT 'waiting', + status_update_time timestamp NULL DEFAULT NULL, + state text, + created_time timestamp NOT NULL, + primary key(job_id, scan_id), + key(created_time));` + + // CreateTTLJobHistory is a table that stores ttl job's history + CreateTTLJobHistory = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_job_history ( + job_id varchar(64) PRIMARY KEY, + table_id bigint(64) NOT NULL, + parent_table_id bigint(64) NOT NULL, + table_schema varchar(64) NOT NULL, + table_name varchar(64) NOT NULL, + partition_name varchar(64) DEFAULT NULL, + create_time timestamp NOT NULL, + finish_time timestamp NOT NULL, + ttl_expire timestamp NOT NULL, + summary_text text, + expired_rows bigint(64) DEFAULT NULL, + deleted_rows bigint(64) DEFAULT NULL, + error_delete_rows bigint(64) DEFAULT NULL, + status varchar(64) NOT NULL, + key(table_schema, table_name, create_time), + key(parent_table_id, create_time), + key(create_time) + );` + + // CreateGlobalTask is a table about global task. + CreateGlobalTask = `CREATE TABLE IF NOT EXISTS mysql.tidb_global_task ( + id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + task_key VARCHAR(256) NOT NULL, + type VARCHAR(256) NOT NULL, + dispatcher_id VARCHAR(256), + state VARCHAR(64) NOT NULL, + start_time TIMESTAMP, + state_update_time TIMESTAMP, + meta LONGBLOB, + concurrency INT(11), + step INT(11), + error BLOB, + key(state), + UNIQUE KEY task_key(task_key) + );` + + // CreateGlobalTaskHistory is a table about history global task. + CreateGlobalTaskHistory = `CREATE TABLE IF NOT EXISTS mysql.tidb_global_task_history ( + id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + task_key VARCHAR(256) NOT NULL, + type VARCHAR(256) NOT NULL, + dispatcher_id VARCHAR(256), + state VARCHAR(64) NOT NULL, + start_time TIMESTAMP, + state_update_time TIMESTAMP, + meta LONGBLOB, + concurrency INT(11), + step INT(11), + error BLOB, + key(state), + UNIQUE KEY task_key(task_key) + );` + + // CreateDistFrameworkMeta create a system table that distributed task framework use to store meta information + CreateDistFrameworkMeta = `CREATE TABLE IF NOT EXISTS mysql.dist_framework_meta ( + host VARCHAR(100) NOT NULL PRIMARY KEY, + role VARCHAR(64), + keyspace_id bigint(8) NOT NULL DEFAULT -1);` + + // CreateLoadDataJobs is a table that LOAD DATA uses + CreateLoadDataJobs = `CREATE TABLE IF NOT EXISTS mysql.load_data_jobs ( + job_id bigint(64) NOT NULL AUTO_INCREMENT, + expected_status ENUM('running', 'paused', 'canceled') NOT NULL DEFAULT 'running', + create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + start_time TIMESTAMP(6) NULL DEFAULT NULL, + update_time TIMESTAMP(6) NULL DEFAULT NULL, + end_time TIMESTAMP(6) NULL DEFAULT NULL, + data_source TEXT NOT NULL, + table_schema VARCHAR(64) NOT NULL, + table_name VARCHAR(64) NOT NULL, + import_mode VARCHAR(64) NOT NULL, + create_user VARCHAR(32) NOT NULL, + progress TEXT DEFAULT NULL, + result_message TEXT DEFAULT NULL, + error_message TEXT DEFAULT NULL, + PRIMARY KEY (job_id), + KEY (create_time), + KEY (create_user));` + + // CreateRunawayTable stores the query which is identified as runaway or quarantined because of in watch list. + CreateRunawayTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_queries ( + resource_group_name varchar(32) not null, + time TIMESTAMP NOT NULL, + match_type varchar(12) NOT NULL, + action varchar(12) NOT NULL, + original_sql TEXT NOT NULL, + plan_digest TEXT NOT NULL, + tidb_server varchar(512), + INDEX plan_index(plan_digest(64)) COMMENT "accelerate the speed when select runaway query", + INDEX time_index(time) COMMENT "accelerate the speed when querying with active watch" + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` + + // CreateRunawayWatchTable stores the condition which is used to check whether query should be quarantined. + CreateRunawayWatchTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_watch ( + id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + resource_group_name varchar(32) not null, + start_time datetime(6) NOT NULL, + end_time datetime(6), + watch bigint(10) NOT NULL, + watch_text TEXT NOT NULL, + source varchar(512) NOT NULL, + action bigint(10), + INDEX sql_index(resource_group_name,watch_text(700)) COMMENT "accelerate the speed when select quarantined query", + INDEX time_index(end_time) COMMENT "accelerate the speed when querying with active watch" + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` + + // CreateDoneRunawayWatchTable stores the condition which is used to check whether query should be quarantined. + CreateDoneRunawayWatchTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_watch_done ( + id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, + record_id BIGINT(20) not null, + resource_group_name varchar(32) not null, + start_time datetime(6) NOT NULL, + end_time datetime(6), + watch bigint(10) NOT NULL, + watch_text TEXT NOT NULL, + source varchar(512) NOT NULL, + action bigint(10), + done_time TIMESTAMP(6) NOT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` + + // CreateImportJobs is a table that IMPORT INTO uses. + CreateImportJobs = `CREATE TABLE IF NOT EXISTS mysql.tidb_import_jobs ( + id bigint(64) NOT NULL AUTO_INCREMENT, + create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + start_time TIMESTAMP(6) NULL DEFAULT NULL, + update_time TIMESTAMP(6) NULL DEFAULT NULL, + end_time TIMESTAMP(6) NULL DEFAULT NULL, + table_schema VARCHAR(64) NOT NULL, + table_name VARCHAR(64) NOT NULL, + table_id bigint(64) NOT NULL, + created_by VARCHAR(300) NOT NULL, + parameters text NOT NULL, + source_file_size bigint(64) NOT NULL, + status VARCHAR(64) NOT NULL, + step VARCHAR(64) NOT NULL, + summary text DEFAULT NULL, + error_message TEXT DEFAULT NULL, + PRIMARY KEY (id), + KEY (created_by), + KEY (status));` +) + +// CreateTimers is a table to store all timers for tidb +var CreateTimers = timertable.CreateTimerTableSQL("mysql", "tidb_timers") + +// bootstrap initiates system DB for a store. +func bootstrap(s Session) { + startTime := time.Now() + err := InitMDLVariableForBootstrap(s.GetStore()) + if err != nil { + logutil.BgLogger().Fatal("init metadata lock error", + zap.Error(err)) + } + dom := domain.GetDomain(s) + for { + b, err := checkBootstrapped(s) + if err != nil { + logutil.BgLogger().Fatal("check bootstrap error", + zap.Error(err)) + } + // For rolling upgrade, we can't do upgrade only in the owner. + if b { + upgrade(s) + logutil.BgLogger().Info("upgrade successful in bootstrap", + zap.Duration("take time", time.Since(startTime))) + return + } + // To reduce conflict when multiple TiDB-server start at the same time. + // Actually only one server need to do the bootstrap. So we chose DDL owner to do this. + if dom.DDL().OwnerManager().IsOwner() { + doDDLWorks(s) + doDMLWorks(s) + runBootstrapSQLFile = true + logutil.BgLogger().Info("bootstrap successful", + zap.Duration("take time", time.Since(startTime))) + return + } + time.Sleep(200 * time.Millisecond) + } +} + +const ( + // varTrue is the true value in mysql.TiDB table for boolean columns. + varTrue = "True" + // varFalse is the false value in mysql.TiDB table for boolean columns. + varFalse = "False" + // The variable name in mysql.TiDB table. + // It is used for checking if the store is bootstrapped by any TiDB server. + // If the value is `True`, the store is already bootstrapped by a TiDB server. + bootstrappedVar = "bootstrapped" + // The variable name in mysql.TiDB table. + // It is used for getting the version of the TiDB server which bootstrapped the store. + tidbServerVersionVar = "tidb_server_version" + // The variable name in mysql.tidb table and it will be used when we want to know + // system timezone. + tidbSystemTZ = "system_tz" + // The variable name in mysql.tidb table and it will indicate if the new collations are enabled in the TiDB cluster. + tidbNewCollationEnabled = "new_collation_enabled" + // The variable name in mysql.tidb table and it records the default value of + // mem-quota-query when upgrade from v3.0.x to v4.0.9+. + tidbDefMemoryQuotaQuery = "default_memory_quota_query" + // The variable name in mysql.tidb table and it records the default value of + // oom-action when upgrade from v3.0.x to v4.0.11+. + tidbDefOOMAction = "default_oom_action" + // Const for TiDB server version 2. + version2 = 2 + version3 = 3 + version4 = 4 + version5 = 5 + version6 = 6 + version7 = 7 + version8 = 8 + version9 = 9 + version10 = 10 + version11 = 11 + version12 = 12 + version13 = 13 + version14 = 14 + version15 = 15 + version16 = 16 + version17 = 17 + version18 = 18 + version19 = 19 + version20 = 20 + version21 = 21 + version22 = 22 + version23 = 23 + version24 = 24 + version25 = 25 + version26 = 26 + version27 = 27 + version28 = 28 + // version29 is not needed. + version30 = 30 + version31 = 31 + version32 = 32 + version33 = 33 + version34 = 34 + version35 = 35 + version36 = 36 + version37 = 37 + version38 = 38 + // version39 will be redone in version46 so it's skipped here. + // version40 is the version that introduce new collation in TiDB, + // see https://github.com/pingcap/tidb/pull/14574 for more details. + version40 = 40 + version41 = 41 + // version42 add storeType and reason column in expr_pushdown_blacklist + version42 = 42 + // version43 updates global variables related to statement summary. + version43 = 43 + // version44 delete tidb_isolation_read_engines from mysql.global_variables to avoid unexpected behavior after upgrade. + version44 = 44 + // version45 introduces CONFIG_PRIV for SET CONFIG statements. + version45 = 45 + // version46 fix a bug in v3.1.1. + version46 = 46 + // version47 add Source to bindings to indicate the way binding created. + version47 = 47 + // version48 reset all deprecated concurrency related system-variables if they were all default value. + // version49 introduces mysql.stats_extended table. + // Both version48 and version49 will be redone in version55 and version56 so they're skipped here. + // version50 add mysql.schema_index_usage table. + version50 = 50 + // version51 introduces CreateTablespacePriv to mysql.user. + // version51 will be redone in version63 so it's skipped here. + // version52 change mysql.stats_histograms cm_sketch column from blob to blob(6291456) + version52 = 52 + // version53 introduce Global variable tidb_enable_strict_double_type_check + version53 = 53 + // version54 writes a variable `mem_quota_query` to mysql.tidb if it's a cluster upgraded from v3.0.x to v4.0.9+. + version54 = 54 + // version55 fixes the bug that upgradeToVer48 would be missed when upgrading from v4.0 to a new version + version55 = 55 + // version56 fixes the bug that upgradeToVer49 would be missed when upgrading from v4.0 to a new version + version56 = 56 + // version57 fixes the bug of concurrent create / drop binding + version57 = 57 + // version58 add `Repl_client_priv` and `Repl_slave_priv` to `mysql.user` + // version58 will be redone in version64 so it's skipped here. + // version59 add writes a variable `oom-action` to mysql.tidb if it's a cluster upgraded from v3.0.x to v4.0.11+. + version59 = 59 + // version60 redesigns `mysql.stats_extended` + version60 = 60 + // version61 will be redone in version67 + // version62 add column ndv for mysql.stats_buckets. + version62 = 62 + // version63 fixes the bug that upgradeToVer51 would be missed when upgrading from v4.0 to a new version + version63 = 63 + // version64 is redone upgradeToVer58 after upgradeToVer63, this is to preserve the order of the columns in mysql.user + version64 = 64 + // version65 add mysql.stats_fm_sketch table. + version65 = 65 + // version66 enables the feature `track_aggregate_memory_usage` by default. + version66 = 66 + // version67 restore all SQL bindings. + version67 = 67 + // version68 update the global variable 'tidb_enable_clustered_index' from 'off' to 'int_only'. + version68 = 68 + // version69 adds mysql.global_grants for DYNAMIC privileges + version69 = 69 + // version70 adds mysql.user.plugin to allow multiple authentication plugins + version70 = 70 + // version71 forces tidb_multi_statement_mode=OFF when tidb_multi_statement_mode=WARN + // This affects upgrades from v4.0 where the default was WARN. + version71 = 71 + // version72 adds snapshot column for mysql.stats_meta + version72 = 72 + // version73 adds mysql.capture_plan_baselines_blacklist table + version73 = 73 + // version74 changes global variable `tidb_stmt_summary_max_stmt_count` value from 200 to 3000. + version74 = 74 + // version75 update mysql.*.host from char(60) to char(255) + version75 = 75 + // version76 update mysql.columns_priv from SET('Select','Insert','Update') to SET('Select','Insert','Update','References') + version76 = 76 + // version77 adds mysql.column_stats_usage table + version77 = 77 + // version78 updates mysql.stats_buckets.lower_bound, mysql.stats_buckets.upper_bound and mysql.stats_histograms.last_analyze_pos from BLOB to LONGBLOB. + version78 = 78 + // version79 adds the mysql.table_cache_meta table + version79 = 79 + // version80 fixes the issue https://github.com/pingcap/tidb/issues/25422. + // If the TiDB upgrading from the 4.x to a newer version, we keep the tidb_analyze_version to 1. + version80 = 80 + // version81 insert "tidb_enable_index_merge|off" to mysql.GLOBAL_VARIABLES if there is no tidb_enable_index_merge. + // This will only happens when we upgrade a cluster before 4.0.0 to 4.0.0+. + version81 = 81 + // version82 adds the mysql.analyze_options table + version82 = 82 + // version83 adds the tables mysql.stats_history + version83 = 83 + // version84 adds the tables mysql.stats_meta_history + version84 = 84 + // version85 updates bindings with status 'using' in mysql.bind_info table to 'enabled' status + version85 = 85 + // version86 update mysql.tables_priv from SET('Select','Insert','Update') to SET('Select','Insert','Update','References'). + version86 = 86 + // version87 adds the mysql.analyze_jobs table + version87 = 87 + // version88 fixes the issue https://github.com/pingcap/tidb/issues/33650. + version88 = 88 + // version89 adds the tables mysql.advisory_locks + version89 = 89 + // version90 converts enable-batch-dml, mem-quota-query, query-log-max-len, committer-concurrency, run-auto-analyze, and oom-action to a sysvar + version90 = 90 + // version91 converts prepared-plan-cache to sysvars + version91 = 91 + // version92 for concurrent ddl. + version92 = 92 + // version93 converts oom-use-tmp-storage to a sysvar + version93 = 93 + version94 = 94 + // version95 add a column `User_attributes` to `mysql.user` + version95 = 95 + // version97 sets tidb_opt_range_max_size to 0 when a cluster upgrades from some version lower than v6.4.0 to v6.4.0+. + // It promises the compatibility of building ranges behavior. + version97 = 97 + // version98 add a column `Token_issuer` to `mysql.user` + version98 = 98 + version99 = 99 + // version100 converts server-memory-quota to a sysvar + version100 = 100 + // version101 add mysql.plan_replayer_status table + version101 = 101 + // version102 add mysql.plan_replayer_task table + version102 = 102 + // version103 adds the tables mysql.stats_table_locked + version103 = 103 + // version104 add `sql_digest` and `plan_digest` to `bind_info` + version104 = 104 + // version105 insert "tidb_cost_model_version|1" to mysql.GLOBAL_VARIABLES if there is no tidb_cost_model_version. + // This will only happens when we upgrade a cluster before 6.0. + version105 = 105 + // version106 add mysql.password_history, and Password_reuse_history, Password_reuse_time into mysql.user. + version106 = 106 + // version107 add columns related to password expiration into mysql.user + version107 = 107 + // version108 adds the table tidb_ttl_table_status + version108 = 108 + // version109 sets tidb_enable_gc_aware_memory_track to off when a cluster upgrades from some version lower than v6.5.0. + version109 = 109 + // ... + // [version110, version129] is the version range reserved for patches of 6.5.x + // ... + // version110 sets tidb_stats_load_pseudo_timeout to ON when a cluster upgrades from some version lower than v6.5.0. + version110 = 110 + // version130 add column source to mysql.stats_meta_history + version130 = 130 + // version131 adds the table tidb_ttl_task and tidb_ttl_job_history + version131 = 131 + // version132 modifies the view tidb_mdl_view + version132 = 132 + // version133 sets tidb_server_memory_limit to "80%" + version133 = 133 + // version134 modifies the following global variables default value: + // - foreign_key_checks: off -> on + // - tidb_enable_foreign_key: off -> on + // - tidb_store_batch_size: 0 -> 4 + version134 = 134 + // version135 sets tidb_opt_advanced_join_hint to off when a cluster upgrades from some version lower than v7.0. + version135 = 135 + // version136 prepare the tables for the distributed task. + version136 = 136 + // version137 introduces some reserved resource groups + version137 = 137 + // version 138 set tidb_enable_null_aware_anti_join to true + version138 = 138 + // version 139 creates mysql.load_data_jobs table for LOAD DATA statement + version139 = 139 + // version 140 add column task_key to mysql.tidb_global_task + version140 = 140 + // version 141 + // set the value of `tidb_session_plan_cache_size` to "tidb_prepared_plan_cache_size" if there is no `tidb_session_plan_cache_size`. + // update tidb_load_based_replica_read_threshold from 0 to 4 + // This will only happens when we upgrade a cluster before 7.1. + version141 = 141 + // version 142 insert "tidb_enable_non_prepared_plan_cache|0" to mysql.GLOBAL_VARIABLES if there is no tidb_enable_non_prepared_plan_cache. + // This will only happens when we upgrade a cluster before 6.5. + version142 = 142 + // version 143 add column `error` to `mysql.tidb_global_task` and `mysql.tidb_background_subtask` + version143 = 143 + // version 144 turn off `tidb_plan_cache_invalidation_on_fresh_stats`, which is introduced in 7.1-rc, + // if it's upgraded from an existing old version cluster. + version144 = 144 + // version 145 to only add a version make we know when we support upgrade state. + version145 = 145 + // version 146 add index for mysql.stats_meta_history and mysql.stats_history. + version146 = 146 + // ... + // [version147, version166] is the version range reserved for patches of 7.1.x + // ... + // version 167 add column `step` to `mysql.tidb_background_subtask` + version167 = 167 + version168 = 168 + // version 169 + // create table `mysql.tidb_runaway_quarantined_watch` and table `mysql.tidb_runaway_queries` + // to save runaway query records and persist runaway watch at 7.2 version. + // but due to ver171 recreate `mysql.tidb_runaway_watch`, + // no need to create table `mysql.tidb_runaway_quarantined_watch`, so delete it. + version169 = 169 + version170 = 170 + // version 171 + // keep the tidb_server length same as instance in other tables. + version171 = 171 + // version 172 + // create table `mysql.tidb_runaway_watch` and table `mysql.tidb_runaway_watch_done` + // to persist runaway watch and deletion of runaway watch at 7.3. + version172 = 172 + // version 173 add column `summary` to `mysql.tidb_background_subtask`. + version173 = 173 + // version 174 + // add column `step`, `error`; delete unique key; and add key idx_state_update_time + // to `mysql.tidb_background_subtask_history`. + version174 = 174 + + // version 175 + // update normalized bindings of `in (?)` to `in (...)` to solve #44298. + version175 = 175 + + // version 176 + // add `mysql.tidb_global_task_history`. + version176 = 176 +) + +// currentBootstrapVersion is defined as a variable, so we can modify its value for testing. +// please make sure this is the largest version +var currentBootstrapVersion int64 = version176 + +// DDL owner key's expired time is ManagerSessionTTL seconds, we should wait the time and give more time to have a chance to finish it. +var internalSQLTimeout = owner.ManagerSessionTTL + 15 + +// whether to run the sql file in bootstrap. +var runBootstrapSQLFile = false + +// DisableRunBootstrapSQLFileInTest only used for test +func DisableRunBootstrapSQLFileInTest() { + if intest.InTest { + runBootstrapSQLFile = false + } +} + +var ( + bootstrapVersion = []func(Session, int64){ + upgradeToVer2, + upgradeToVer3, + upgradeToVer4, + upgradeToVer5, + upgradeToVer6, + upgradeToVer7, + upgradeToVer8, + upgradeToVer9, + upgradeToVer10, + upgradeToVer11, + upgradeToVer12, + upgradeToVer13, + upgradeToVer14, + upgradeToVer15, + upgradeToVer16, + upgradeToVer17, + upgradeToVer18, + upgradeToVer19, + upgradeToVer20, + upgradeToVer21, + upgradeToVer22, + upgradeToVer23, + upgradeToVer24, + upgradeToVer25, + upgradeToVer26, + upgradeToVer27, + upgradeToVer28, + upgradeToVer29, + upgradeToVer30, + upgradeToVer31, + upgradeToVer32, + upgradeToVer33, + upgradeToVer34, + upgradeToVer35, + upgradeToVer36, + upgradeToVer37, + upgradeToVer38, + // We will redo upgradeToVer39 in upgradeToVer46, + // so upgradeToVer39 is skipped here. + upgradeToVer40, + upgradeToVer41, + upgradeToVer42, + upgradeToVer43, + upgradeToVer44, + upgradeToVer45, + upgradeToVer46, + upgradeToVer47, + // We will redo upgradeToVer48 and upgradeToVer49 in upgradeToVer55 and upgradeToVer56, + // so upgradeToVer48 and upgradeToVer49 is skipped here. + upgradeToVer50, + // We will redo upgradeToVer51 in upgradeToVer63, it is skipped here. + upgradeToVer52, + upgradeToVer53, + upgradeToVer54, + upgradeToVer55, + upgradeToVer56, + upgradeToVer57, + // We will redo upgradeToVer58 in upgradeToVer64, it is skipped here. + upgradeToVer59, + upgradeToVer60, + // We will redo upgradeToVer61 in upgradeToVer67, it is skipped here. + upgradeToVer62, + upgradeToVer63, + upgradeToVer64, + upgradeToVer65, + upgradeToVer66, + upgradeToVer67, + upgradeToVer68, + upgradeToVer69, + upgradeToVer70, + upgradeToVer71, + upgradeToVer72, + upgradeToVer73, + upgradeToVer74, + upgradeToVer75, + upgradeToVer76, + upgradeToVer77, + upgradeToVer78, + upgradeToVer79, + upgradeToVer80, + upgradeToVer81, + upgradeToVer82, + upgradeToVer83, + upgradeToVer84, + upgradeToVer85, + upgradeToVer86, + upgradeToVer87, + upgradeToVer88, + upgradeToVer89, + upgradeToVer90, + upgradeToVer91, + upgradeToVer93, + upgradeToVer94, + upgradeToVer95, + // We will redo upgradeToVer96 in upgradeToVer100, it is skipped here. + upgradeToVer97, + upgradeToVer98, + upgradeToVer100, + upgradeToVer101, + upgradeToVer102, + upgradeToVer103, + upgradeToVer104, + upgradeToVer105, + upgradeToVer106, + upgradeToVer107, + upgradeToVer108, + upgradeToVer109, + upgradeToVer110, + upgradeToVer130, + upgradeToVer131, + upgradeToVer132, + upgradeToVer133, + upgradeToVer134, + upgradeToVer135, + upgradeToVer136, + upgradeToVer137, + upgradeToVer138, + upgradeToVer139, + upgradeToVer140, + upgradeToVer141, + upgradeToVer142, + upgradeToVer143, + upgradeToVer144, + // We will only use Ver145 to differentiate versions, so it is skipped here. + upgradeToVer146, + upgradeToVer167, + upgradeToVer168, + upgradeToVer169, + upgradeToVer170, + upgradeToVer171, + upgradeToVer172, + upgradeToVer173, + upgradeToVer174, + upgradeToVer175, + upgradeToVer176, + } +) + +func checkBootstrapped(s Session) (bool, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + // Check if system db exists. + _, err := s.ExecuteInternal(ctx, "USE %n", mysql.SystemDB) + if err != nil && infoschema.ErrDatabaseNotExists.NotEqual(err) { + logutil.BgLogger().Fatal("check bootstrap error", + zap.Error(err)) + } + // Check bootstrapped variable value in TiDB table. + sVal, _, err := getTiDBVar(s, bootstrappedVar) + if err != nil { + if infoschema.ErrTableNotExists.Equal(err) { + return false, nil + } + return false, errors.Trace(err) + } + isBootstrapped := sVal == varTrue + if isBootstrapped { + // Make sure that doesn't affect the following operations. + if err = s.CommitTxn(ctx); err != nil { + return false, errors.Trace(err) + } + } + return isBootstrapped, nil +} + +// getTiDBVar gets variable value from mysql.tidb table. +// Those variables are used by TiDB server. +func getTiDBVar(s Session, name string) (sVal string, isNull bool, e error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, `SELECT HIGH_PRIORITY VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME= %?`, + mysql.SystemDB, + mysql.TiDBTable, + name, + ) + if err != nil { + return "", true, errors.Trace(err) + } + if rs == nil { + return "", true, errors.New("Wrong number of Recordset") + } + defer terror.Call(rs.Close) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + if err != nil || req.NumRows() == 0 { + return "", true, errors.Trace(err) + } + row := req.GetRow(0) + if row.IsNull(0) { + return "", true, nil + } + return row.GetString(0), false, nil +} + +var ( + // SupportUpgradeHTTPOpVer is exported for testing. + // The minimum version of the upgrade by paused user DDL can be notified through the HTTP API. + SupportUpgradeHTTPOpVer int64 = version174 +) + +// upgrade function will do some upgrade works, when the system is bootstrapped by low version TiDB server +// For example, add new system variables into mysql.global_variables table. +func upgrade(s Session) { + ver, err := getBootstrapVersion(s) + terror.MustNil(err) + if ver >= currentBootstrapVersion { + // It is already bootstrapped/upgraded by a higher version TiDB server. + return + } + printClusterState(s, ver) + + // Only upgrade from under version92 and this TiDB is not owner set. + // The owner in older tidb does not support concurrent DDL, we should add the internal DDL to job queue. + if ver < version92 { + useConcurrentDDL, err := checkOwnerVersion(context.Background(), domain.GetDomain(s)) + if err != nil { + logutil.BgLogger().Fatal("[upgrade] upgrade failed", zap.Error(err)) + } + if !useConcurrentDDL { + // Use another variable DDLForce2Queue but not EnableConcurrentDDL since in upgrade it may set global variable, the initial step will + // overwrite variable EnableConcurrentDDL. + variable.DDLForce2Queue.Store(true) + } + } + // Do upgrade works then update bootstrap version. + isNull, err := InitMDLVariableForUpgrade(s.GetStore()) + if err != nil { + logutil.BgLogger().Fatal("[upgrade] init metadata lock failed", zap.Error(err)) + } + + if isNull { + upgradeToVer99Before(s) + } + + // It is only used in test. + addMockBootstrapVersionForTest(s) + for _, upgrade := range bootstrapVersion { + upgrade(s, ver) + } + if isNull { + upgradeToVer99After(s) + } + + variable.DDLForce2Queue.Store(false) + updateBootstrapVer(s) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + _, err = s.ExecuteInternal(ctx, "COMMIT") + + if err != nil { + sleepTime := 1 * time.Second + logutil.BgLogger().Info("update bootstrap ver failed", + zap.Error(err), zap.Duration("sleeping time", sleepTime)) + time.Sleep(sleepTime) + // Check if TiDB is already upgraded. + v, err1 := getBootstrapVersion(s) + if err1 != nil { + logutil.BgLogger().Fatal("upgrade failed", zap.Error(err1)) + } + if v >= currentBootstrapVersion { + // It is already bootstrapped/upgraded by a higher version TiDB server. + return + } + logutil.BgLogger().Fatal("[upgrade] upgrade failed", + zap.Int64("from", ver), + zap.Int64("to", currentBootstrapVersion), + zap.Error(err)) + } +} + +// checkOwnerVersion is used to wait the DDL owner to be elected in the cluster and check it is the same version as this TiDB. +func checkOwnerVersion(ctx context.Context, dom *domain.Domain) (bool, error) { + ticker := time.NewTicker(100 * time.Millisecond) + defer ticker.Stop() + logutil.BgLogger().Info("Waiting for the DDL owner to be elected in the cluster") + for { + select { + case <-ctx.Done(): + return false, ctx.Err() + case <-ticker.C: + ownerID, err := dom.DDL().OwnerManager().GetOwnerID(ctx) + if err == concurrency.ErrElectionNoLeader { + continue + } + info, err := infosync.GetAllServerInfo(ctx) + if err != nil { + return false, err + } + if s, ok := info[ownerID]; ok { + return s.Version == mysql.ServerVersion, nil + } + } + } +} + +// upgradeToVer2 updates to version 2. +func upgradeToVer2(s Session, ver int64) { + if ver >= version2 { + return + } + // Version 2 add two system variable for DistSQL concurrency controlling. + // Insert distsql related system variable. + distSQLVars := []string{variable.TiDBDistSQLScanConcurrency} + values := make([]string, 0, len(distSQLVars)) + for _, v := range distSQLVars { + value := fmt.Sprintf(`("%s", "%s")`, v, variable.GetSysVar(v).Value) + values = append(values, value) + } + sql := fmt.Sprintf("INSERT HIGH_PRIORITY IGNORE INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, + strings.Join(values, ", ")) + mustExecute(s, sql) +} + +// upgradeToVer3 updates to version 3. +func upgradeToVer3(s Session, ver int64) { + if ver >= version3 { + return + } + // Version 3 fix tx_read_only variable value. + mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n SET variable_value = '0' WHERE variable_name = 'tx_read_only';", mysql.SystemDB, mysql.GlobalVariablesTable) +} + +// upgradeToVer4 updates to version 4. +func upgradeToVer4(s Session, ver int64) { + if ver >= version4 { + return + } + mustExecute(s, CreateStatsMetaTable) +} + +func upgradeToVer5(s Session, ver int64) { + if ver >= version5 { + return + } + mustExecute(s, CreateStatsColsTable) + mustExecute(s, CreateStatsBucketsTable) +} + +func upgradeToVer6(s Session, ver int64) { + if ver >= version6 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Super_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_db_priv`", infoschema.ErrColumnExists) + // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Super_priv='Y'") +} + +func upgradeToVer7(s Session, ver int64) { + if ver >= version7 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Process_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Drop_priv`", infoschema.ErrColumnExists) + // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Process_priv='Y'") +} + +func upgradeToVer8(s Session, ver int64) { + if ver >= version8 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + // This is a dummy upgrade, it checks whether upgradeToVer7 success, if not, do it again. + if _, err := s.ExecuteInternal(ctx, "SELECT HIGH_PRIORITY `Process_priv` FROM mysql.user LIMIT 0"); err == nil { + return + } + upgradeToVer7(s, ver) +} + +func upgradeToVer9(s Session, ver int64) { + if ver >= version9 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Trigger_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", infoschema.ErrColumnExists) + // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Trigger_priv='Y'") +} + +func doReentrantDDL(s Session, sql string, ignorableErrs ...error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(internalSQLTimeout)*time.Second) + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) + _, err := s.ExecuteInternal(ctx, sql) + defer cancel() + for _, ignorableErr := range ignorableErrs { + if terror.ErrorEqual(err, ignorableErr) { + return + } + } + if err != nil { + logutil.BgLogger().Fatal("doReentrantDDL error", zap.Error(err)) + } +} + +func upgradeToVer10(s Session, ver int64) { + if ver >= version10 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets CHANGE COLUMN `value` `upper_bound` BLOB NOT NULL", infoschema.ErrColumnNotExists, infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets ADD COLUMN `lower_bound` BLOB", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `null_count` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN distinct_ratio", dbterror.ErrCantDropFieldOrKey) + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN use_count_to_estimate", dbterror.ErrCantDropFieldOrKey) +} + +func upgradeToVer11(s Session, ver int64) { + if ver >= version11 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `References_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET References_priv='Y'") +} + +func upgradeToVer12(s Session, ver int64) { + if ver >= version12 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + _, err := s.ExecuteInternal(ctx, "BEGIN") + terror.MustNil(err) + sql := "SELECT HIGH_PRIORITY user, host, password FROM mysql.user WHERE password != ''" + rs, err := s.ExecuteInternal(ctx, sql) + if terror.ErrorEqual(err, core.ErrUnknownColumn) { + sql := "SELECT HIGH_PRIORITY user, host, authentication_string FROM mysql.user WHERE authentication_string != ''" + rs, err = s.ExecuteInternal(ctx, sql) + } + terror.MustNil(err) + sqls := make([]string, 0, 1) + defer terror.Call(rs.Close) + req := rs.NewChunk(nil) + it := chunk.NewIterator4Chunk(req) + err = rs.Next(ctx, req) + for err == nil && req.NumRows() != 0 { + for row := it.Begin(); row != it.End(); row = it.Next() { + user := row.GetString(0) + host := row.GetString(1) + pass := row.GetString(2) + var newPass string + newPass, err = oldPasswordUpgrade(pass) + terror.MustNil(err) + updateSQL := fmt.Sprintf(`UPDATE HIGH_PRIORITY mysql.user SET password = "%s" WHERE user="%s" AND host="%s"`, newPass, user, host) + sqls = append(sqls, updateSQL) + } + err = rs.Next(ctx, req) + } + terror.MustNil(err) + + for _, sql := range sqls { + mustExecute(s, sql) + } + + sql = fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES ("%s", "%d", "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%d"`, + mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, version12, version12) + mustExecute(s, sql) + + mustExecute(s, "COMMIT") +} + +func upgradeToVer13(s Session, ver int64) { + if ver >= version13 { + return + } + sqls := []string{ + "ALTER TABLE mysql.user ADD COLUMN `Create_tmp_table_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Super_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Lock_tables_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Create_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Show_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Create_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Alter_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", + "ALTER TABLE mysql.user ADD COLUMN `Event_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", + } + for _, sql := range sqls { + doReentrantDDL(s, sql, infoschema.ErrColumnExists) + } + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tmp_table_priv='Y',Lock_tables_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Event_priv='Y' WHERE Super_priv='Y'") + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_view_priv='Y',Show_view_priv='Y' WHERE Create_priv='Y'") +} + +func upgradeToVer14(s Session, ver int64) { + if ver >= version14 { + return + } + sqls := []string{ + "ALTER TABLE mysql.db ADD COLUMN `References_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Create_tmp_table_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Alter_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Lock_tables_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Create_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Lock_tables_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Show_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Create_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Alter_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Event_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", + "ALTER TABLE mysql.db ADD COLUMN `Trigger_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Event_priv`", + } + for _, sql := range sqls { + doReentrantDDL(s, sql, infoschema.ErrColumnExists) + } +} + +func upgradeToVer15(s Session, ver int64) { + if ver >= version15 { + return + } + doReentrantDDL(s, CreateGCDeleteRangeTable) +} + +func upgradeToVer16(s Session, ver int64) { + if ver >= version16 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `cm_sketch` BLOB", infoschema.ErrColumnExists) +} + +func upgradeToVer17(s Session, ver int64) { + if ver >= version17 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user MODIFY User CHAR(32)") +} + +func upgradeToVer18(s Session, ver int64) { + if ver >= version18 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `tot_col_size` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) +} + +func upgradeToVer19(s Session, ver int64) { + if ver >= version19 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.db MODIFY User CHAR(32)") + doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY User CHAR(32)") + doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY User CHAR(32)") +} + +func upgradeToVer20(s Session, ver int64) { + if ver >= version20 { + return + } + // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. + doReentrantDDL(s, CreateStatsFeedbackTable) +} + +func upgradeToVer21(s Session, ver int64) { + if ver >= version21 { + return + } + mustExecute(s, CreateGCDeleteRangeDoneTable) + + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX job_id", dbterror.ErrCantDropFieldOrKey) + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range ADD UNIQUE INDEX delete_range_index (job_id, element_id)", dbterror.ErrDupKeyName) + doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX element_id", dbterror.ErrCantDropFieldOrKey) +} + +func upgradeToVer22(s Session, ver int64) { + if ver >= version22 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `stats_ver` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) +} + +func upgradeToVer23(s Session, ver int64) { + if ver >= version23 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `flag` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) +} + +// writeSystemTZ writes system timezone info into mysql.tidb +func writeSystemTZ(s Session) { + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, "TiDB Global System Timezone.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE= %?`, + mysql.SystemDB, + mysql.TiDBTable, + tidbSystemTZ, + timeutil.InferSystemTZ(), + timeutil.InferSystemTZ(), + ) +} + +// upgradeToVer24 initializes `System` timezone according to docs/design/2018-09-10-adding-tz-env.md +func upgradeToVer24(s Session, ver int64) { + if ver >= version24 { + return + } + writeSystemTZ(s) +} + +// upgradeToVer25 updates tidb_max_chunk_size to new low bound value 32 if previous value is small than 32. +func upgradeToVer25(s Session, ver int64) { + if ver >= version25 { + return + } + sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %[1]s.%[2]s SET VARIABLE_VALUE = '%[4]d' WHERE VARIABLE_NAME = '%[3]s' AND VARIABLE_VALUE < %[4]d", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBMaxChunkSize, variable.DefInitChunkSize) + mustExecute(s, sql) +} + +func upgradeToVer26(s Session, ver int64) { + if ver >= version26 { + return + } + mustExecute(s, CreateRoleEdgesTable) + mustExecute(s, CreateDefaultRolesTable) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Create_role_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Drop_role_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Account_locked` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + // user with Create_user_Priv privilege should have Create_view_priv and Show_view_priv after upgrade to v3.0 + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_role_priv='Y',Drop_role_priv='Y' WHERE Create_user_priv='Y'") + // user with Create_Priv privilege should have Create_view_priv and Show_view_priv after upgrade to v3.0 + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_view_priv='Y',Show_view_priv='Y' WHERE Create_priv='Y'") +} + +func upgradeToVer27(s Session, ver int64) { + if ver >= version27 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `correlation` DOUBLE NOT NULL DEFAULT 0", infoschema.ErrColumnExists) +} + +func upgradeToVer28(s Session, ver int64) { + if ver >= version28 { + return + } + doReentrantDDL(s, CreateBindInfoTable) +} + +func upgradeToVer29(s Session, ver int64) { + // upgradeToVer29 only need to be run when the current version is 28. + if ver != version28 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.bind_info CHANGE create_time create_time TIMESTAMP(3)") + doReentrantDDL(s, "ALTER TABLE mysql.bind_info CHANGE update_time update_time TIMESTAMP(3)") + doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD INDEX sql_index (original_sql(1024),default_db(1024))", dbterror.ErrDupKeyName) +} + +func upgradeToVer30(s Session, ver int64) { + if ver >= version30 { + return + } + mustExecute(s, CreateStatsTopNTable) +} + +func upgradeToVer31(s Session, ver int64) { + if ver >= version31 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `last_analyze_pos` BLOB DEFAULT NULL", infoschema.ErrColumnExists) +} + +func upgradeToVer32(s Session, ver int64) { + if ver >= version32 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY table_priv SET('Select','Insert','Update','Delete','Create','Drop','Grant', 'Index', 'Alter', 'Create View', 'Show View', 'Trigger', 'References')") +} + +func upgradeToVer33(s Session, ver int64) { + if ver >= version33 { + return + } + doReentrantDDL(s, CreateExprPushdownBlacklist) +} + +func upgradeToVer34(s Session, ver int64) { + if ver >= version34 { + return + } + doReentrantDDL(s, CreateOptRuleBlacklist) +} + +func upgradeToVer35(s Session, ver int64) { + if ver >= version35 { + return + } + sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %s.%s SET VARIABLE_NAME = '%s' WHERE VARIABLE_NAME = 'tidb_back_off_weight'", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBBackOffWeight) + mustExecute(s, sql) +} + +func upgradeToVer36(s Session, ver int64) { + if ver >= version36 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Shutdown_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + // A root user will have those privileges after upgrading. + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Shutdown_priv='Y' WHERE Super_priv='Y'") + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tmp_table_priv='Y',Lock_tables_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Event_priv='Y' WHERE Super_priv='Y'") +} + +func upgradeToVer37(s Session, ver int64) { + if ver >= version37 { + return + } + // when upgrade from old tidb and no 'tidb_enable_window_function' in GLOBAL_VARIABLES, init it with 0. + sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%d')", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableWindowFunction, 0) + mustExecute(s, sql) +} + +func upgradeToVer38(s Session, ver int64) { + if ver >= version38 { + return + } + doReentrantDDL(s, CreateGlobalPrivTable) +} + +func writeNewCollationParameter(s Session, flag bool) { + comment := "If the new collations are enabled. Do not edit it." + b := varFalse + if flag { + b = varTrue + } + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, + mysql.SystemDB, mysql.TiDBTable, tidbNewCollationEnabled, b, comment, b, + ) +} + +func upgradeToVer40(s Session, ver int64) { + if ver >= version40 { + return + } + // There is no way to enable new collation for an existing TiDB cluster. + writeNewCollationParameter(s, false) +} + +func upgradeToVer41(s Session, ver int64) { + if ver >= version41 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `password` `authentication_string` TEXT", infoschema.ErrColumnExists, infoschema.ErrColumnNotExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `password` TEXT as (`authentication_string`)", infoschema.ErrColumnExists) +} + +// writeDefaultExprPushDownBlacklist writes default expr pushdown blacklist into mysql.expr_pushdown_blacklist +func writeDefaultExprPushDownBlacklist(s Session) { + mustExecute(s, "INSERT HIGH_PRIORITY INTO mysql.expr_pushdown_blacklist VALUES"+ + "('date_add','tiflash', 'DST(daylight saving time) does not take effect in TiFlash date_add')") +} + +func upgradeToVer42(s Session, ver int64) { + if ver >= version42 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.expr_pushdown_blacklist ADD COLUMN `store_type` CHAR(100) NOT NULL DEFAULT 'tikv,tiflash,tidb'", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.expr_pushdown_blacklist ADD COLUMN `reason` VARCHAR(200)", infoschema.ErrColumnExists) + writeDefaultExprPushDownBlacklist(s) +} + +// Convert statement summary global variables to non-empty values. +func writeStmtSummaryVars(s Session) { + sql := "UPDATE %n.%n SET variable_value= %? WHERE variable_name= %? AND variable_value=''" + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, variable.BoolToOnOff(variable.DefTiDBEnableStmtSummary), variable.TiDBEnableStmtSummary) + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, variable.BoolToOnOff(variable.DefTiDBStmtSummaryInternalQuery), variable.TiDBStmtSummaryInternalQuery) + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.Itoa(variable.DefTiDBStmtSummaryRefreshInterval), variable.TiDBStmtSummaryRefreshInterval) + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.Itoa(variable.DefTiDBStmtSummaryHistorySize), variable.TiDBStmtSummaryHistorySize) + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.FormatUint(uint64(variable.DefTiDBStmtSummaryMaxStmtCount), 10), variable.TiDBStmtSummaryMaxStmtCount) + mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.FormatUint(uint64(variable.DefTiDBStmtSummaryMaxSQLLength), 10), variable.TiDBStmtSummaryMaxSQLLength) +} + +func upgradeToVer43(s Session, ver int64) { + if ver >= version43 { + return + } + writeStmtSummaryVars(s) +} + +func upgradeToVer44(s Session, ver int64) { + if ver >= version44 { + return + } + mustExecute(s, "DELETE FROM mysql.global_variables where variable_name = \"tidb_isolation_read_engines\"") +} + +func upgradeToVer45(s Session, ver int64) { + if ver >= version45 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Config_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Config_priv='Y' WHERE Super_priv='Y'") +} + +// In v3.1.1, we wrongly replace the context of upgradeToVer39 with upgradeToVer44. If we upgrade from v3.1.1 to a newer version, +// upgradeToVer39 will be missed. So we redo upgradeToVer39 here to make sure the upgrading from v3.1.1 succeed. +func upgradeToVer46(s Session, ver int64) { + if ver >= version46 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Reload_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `File_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Reload_priv='Y' WHERE Super_priv='Y'") + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET File_priv='Y' WHERE Super_priv='Y'") +} + +func upgradeToVer47(s Session, ver int64) { + if ver >= version47 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN `source` varchar(10) NOT NULL default 'unknown'", infoschema.ErrColumnExists) +} + +func upgradeToVer50(s Session, ver int64) { + if ver >= version50 { + return + } + doReentrantDDL(s, CreateSchemaIndexUsageTable) +} + +func upgradeToVer52(s Session, ver int64) { + if ver >= version52 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms MODIFY cm_sketch BLOB(6291456)") +} + +func upgradeToVer53(s Session, ver int64) { + if ver >= version53 { + return + } + // when upgrade from old tidb and no `tidb_enable_strict_double_type_check` in GLOBAL_VARIABLES, init it with 1` + sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%d')", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableStrictDoubleTypeCheck, 0) + mustExecute(s, sql) +} + +func upgradeToVer54(s Session, ver int64) { + if ver >= version54 { + return + } + // The mem-query-quota default value is 32GB by default in v3.0, and 1GB by + // default in v4.0. + // If a cluster is upgraded from v3.0.x (bootstrapVer <= version38) to + // v4.0.9+, we'll write the default value to mysql.tidb. Thus we can get the + // default value of mem-quota-query, and promise the compatibility even if + // the tidb-server restarts. + // If it's a newly deployed cluster, we do not need to write the value into + // mysql.tidb, since no compatibility problem will happen. + + // This bootstrap task becomes obsolete in TiDB 5.0+, because it appears that the + // default value of mem-quota-query changes back to 1GB. In TiDB 6.1+ mem-quota-query + // is no longer a config option, but instead a system variable (tidb_mem_quota_query). + + if ver <= version38 { + writeMemoryQuotaQuery(s) + } +} + +// When cherry-pick upgradeToVer52 to v4.0, we wrongly name it upgradeToVer48. +// If we upgrade from v4.0 to a newer version, the real upgradeToVer48 will be missed. +// So we redo upgradeToVer48 here to make sure the upgrading from v4.0 succeeds. +func upgradeToVer55(s Session, ver int64) { + if ver >= version55 { + return + } + defValues := map[string]string{ + variable.TiDBIndexLookupConcurrency: "4", + variable.TiDBIndexLookupJoinConcurrency: "4", + variable.TiDBHashAggFinalConcurrency: "4", + variable.TiDBHashAggPartialConcurrency: "4", + variable.TiDBWindowConcurrency: "4", + variable.TiDBProjectionConcurrency: "4", + variable.TiDBHashJoinConcurrency: "5", + } + names := make([]string, 0, len(defValues)) + for n := range defValues { + names = append(names, n) + } + + selectSQL := "select HIGH_PRIORITY * from mysql.global_variables where variable_name in ('" + strings.Join(names, quoteCommaQuote) + "')" + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, selectSQL) + terror.MustNil(err) + defer terror.Call(rs.Close) + req := rs.NewChunk(nil) + it := chunk.NewIterator4Chunk(req) + err = rs.Next(ctx, req) + for err == nil && req.NumRows() != 0 { + for row := it.Begin(); row != it.End(); row = it.Next() { + n := strings.ToLower(row.GetString(0)) + v := row.GetString(1) + if defValue, ok := defValues[n]; !ok || defValue != v { + return + } + } + err = rs.Next(ctx, req) + } + terror.MustNil(err) + + mustExecute(s, "BEGIN") + v := strconv.Itoa(variable.ConcurrencyUnset) + sql := fmt.Sprintf("UPDATE %s.%s SET variable_value='%%s' WHERE variable_name='%%s'", mysql.SystemDB, mysql.GlobalVariablesTable) + for _, name := range names { + mustExecute(s, fmt.Sprintf(sql, v, name)) + } + mustExecute(s, "COMMIT") +} + +// When cherry-pick upgradeToVer54 to v4.0, we wrongly name it upgradeToVer49. +// If we upgrade from v4.0 to a newer version, the real upgradeToVer49 will be missed. +// So we redo upgradeToVer49 here to make sure the upgrading from v4.0 succeeds. +func upgradeToVer56(s Session, ver int64) { + if ver >= version56 { + return + } + doReentrantDDL(s, CreateStatsExtended) +} + +func upgradeToVer57(s Session, ver int64) { + if ver >= version57 { + return + } + insertBuiltinBindInfoRow(s) +} + +func initBindInfoTable(s Session) { + mustExecute(s, CreateBindInfoTable) + insertBuiltinBindInfoRow(s) +} + +func insertBuiltinBindInfoRow(s Session) { + mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.bind_info(original_sql, bind_sql, default_db, status, create_time, update_time, charset, collation, source) + VALUES (%?, %?, "mysql", %?, "0000-00-00 00:00:00", "0000-00-00 00:00:00", "", "", %?)`, + bindinfo.BuiltinPseudoSQL4BindLock, bindinfo.BuiltinPseudoSQL4BindLock, bindinfo.Builtin, bindinfo.Builtin, + ) +} + +func upgradeToVer59(s Session, ver int64) { + if ver >= version59 { + return + } + // The oom-action default value is log by default in v3.0, and cancel by + // default in v4.0.11+. + // If a cluster is upgraded from v3.0.x (bootstrapVer <= version59) to + // v4.0.11+, we'll write the default value to mysql.tidb. Thus we can get + // the default value of oom-action, and promise the compatibility even if + // the tidb-server restarts. + // If it's a newly deployed cluster, we do not need to write the value into + // mysql.tidb, since no compatibility problem will happen. + writeOOMAction(s) +} + +func upgradeToVer60(s Session, ver int64) { + if ver >= version60 { + return + } + mustExecute(s, "DROP TABLE IF EXISTS mysql.stats_extended") + doReentrantDDL(s, CreateStatsExtended) +} + +type bindInfo struct { + bindSQL string + status string + createTime types.Time + charset string + collation string + source string +} + +func upgradeToVer67(s Session, ver int64) { + if ver >= version67 { + return + } + bindMap := make(map[string]bindInfo) + h := &bindinfo.BindHandle{} + var err error + mustExecute(s, "BEGIN PESSIMISTIC") + + defer func() { + if err != nil { + mustExecute(s, "ROLLBACK") + return + } + + mustExecute(s, "COMMIT") + }() + mustExecute(s, h.LockBindInfoSQL()) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + var rs sqlexec.RecordSet + rs, err = s.ExecuteInternal(ctx, + `SELECT bind_sql, default_db, status, create_time, charset, collation, source + FROM mysql.bind_info + WHERE source != 'builtin' + ORDER BY update_time DESC`) + if err != nil { + logutil.BgLogger().Fatal("upgradeToVer67 error", zap.Error(err)) + } + req := rs.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + p := parser.New() + now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) + for { + err = rs.Next(context.TODO(), req) + if err != nil { + logutil.BgLogger().Fatal("upgradeToVer67 error", zap.Error(err)) + } + if req.NumRows() == 0 { + break + } + updateBindInfo(iter, p, bindMap) + } + terror.Call(rs.Close) + + mustExecute(s, "DELETE FROM mysql.bind_info where source != 'builtin'") + for original, bind := range bindMap { + mustExecute(s, fmt.Sprintf("INSERT INTO mysql.bind_info VALUES(%s, %s, '', %s, %s, %s, %s, %s, %s)", + expression.Quote(original), + expression.Quote(bind.bindSQL), + expression.Quote(bind.status), + expression.Quote(bind.createTime.String()), + expression.Quote(now.String()), + expression.Quote(bind.charset), + expression.Quote(bind.collation), + expression.Quote(bind.source), + )) + } +} + +func updateBindInfo(iter *chunk.Iterator4Chunk, p *parser.Parser, bindMap map[string]bindInfo) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + bind := row.GetString(0) + db := row.GetString(1) + status := row.GetString(2) + + if status != bindinfo.Enabled && status != bindinfo.Using && status != bindinfo.Builtin { + continue + } + + charset := row.GetString(4) + collation := row.GetString(5) + stmt, err := p.ParseOneStmt(bind, charset, collation) + if err != nil { + logutil.BgLogger().Fatal("updateBindInfo error", zap.Error(err)) + } + originWithDB := parser.Normalize(utilparser.RestoreWithDefaultDB(stmt, db, bind)) + if _, ok := bindMap[originWithDB]; ok { + // The results are sorted in descending order of time. + // And in the following cases, duplicate originWithDB may occur + // originalText |bindText |DB + // `select * from t` |`select /*+ use_index(t, idx) */ * from t` |`test` + // `select * from test.t` |`select /*+ use_index(t, idx) */ * from test.t`|`` + // Therefore, if repeated, we can skip to keep the latest binding. + continue + } + bindMap[originWithDB] = bindInfo{ + bindSQL: utilparser.RestoreWithDefaultDB(stmt, db, bind), + status: status, + createTime: row.GetTime(3), + charset: charset, + collation: collation, + source: row.GetString(6), + } + } +} + +func writeMemoryQuotaQuery(s Session) { + comment := "memory_quota_query is 32GB by default in v3.0.x, 1GB by default in v4.0.x+" + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, + mysql.SystemDB, mysql.TiDBTable, tidbDefMemoryQuotaQuery, 32<<30, comment, 32<<30, + ) +} + +func upgradeToVer62(s Session, ver int64) { + if ver >= version62 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets ADD COLUMN `ndv` bigint not null default 0", infoschema.ErrColumnExists) +} + +func upgradeToVer63(s Session, ver int64) { + if ver >= version63 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Create_tablespace_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tablespace_priv='Y' where Super_priv='Y'") +} + +func upgradeToVer64(s Session, ver int64) { + if ver >= version64 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Repl_slave_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Repl_client_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Repl_slave_priv`", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Repl_slave_priv='Y',Repl_client_priv='Y' where Super_priv='Y'") +} + +func upgradeToVer65(s Session, ver int64) { + if ver >= version65 { + return + } + doReentrantDDL(s, CreateStatsFMSketchTable) +} + +func upgradeToVer66(s Session, ver int64) { + if ver >= version66 { + return + } + mustExecute(s, "set @@global.tidb_track_aggregate_memory_usage = 1") +} + +func upgradeToVer68(s Session, ver int64) { + if ver >= version68 { + return + } + mustExecute(s, "DELETE FROM mysql.global_variables where VARIABLE_NAME = 'tidb_enable_clustered_index' and VARIABLE_VALUE = 'OFF'") +} + +func upgradeToVer69(s Session, ver int64) { + if ver >= version69 { + return + } + doReentrantDDL(s, CreateGlobalGrantsTable) +} + +func upgradeToVer70(s Session, ver int64) { + if ver >= version70 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN plugin CHAR(64) AFTER authentication_string", infoschema.ErrColumnExists) + mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET plugin='mysql_native_password'") +} + +func upgradeToVer71(s Session, ver int64) { + if ver >= version71 { + return + } + mustExecute(s, "UPDATE mysql.global_variables SET VARIABLE_VALUE='OFF' WHERE VARIABLE_NAME = 'tidb_multi_statement_mode' AND VARIABLE_VALUE = 'WARN'") +} + +func upgradeToVer72(s Session, ver int64) { + if ver >= version72 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_meta ADD COLUMN snapshot BIGINT(64) UNSIGNED NOT NULL DEFAULT 0", infoschema.ErrColumnExists) +} + +func upgradeToVer73(s Session, ver int64) { + if ver >= version73 { + return + } + doReentrantDDL(s, CreateCapturePlanBaselinesBlacklist) +} + +func upgradeToVer74(s Session, ver int64) { + if ver >= version74 { + return + } + // The old default value of `tidb_stmt_summary_max_stmt_count` is 200, we want to enlarge this to the new default value when TiDB upgrade. + mustExecute(s, fmt.Sprintf("UPDATE mysql.global_variables SET VARIABLE_VALUE='%[1]v' WHERE VARIABLE_NAME = 'tidb_stmt_summary_max_stmt_count' AND CAST(VARIABLE_VALUE AS SIGNED) = 200", variable.DefTiDBStmtSummaryMaxStmtCount)) +} + +func upgradeToVer75(s Session, ver int64) { + if ver >= version75 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user MODIFY COLUMN Host CHAR(255)") + doReentrantDDL(s, "ALTER TABLE mysql.global_priv MODIFY COLUMN Host CHAR(255)") + doReentrantDDL(s, "ALTER TABLE mysql.db MODIFY COLUMN Host CHAR(255)") + doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY COLUMN Host CHAR(255)") + doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY COLUMN Host CHAR(255)") +} + +func upgradeToVer76(s Session, ver int64) { + if ver >= version76 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY COLUMN Column_priv SET('Select','Insert','Update','References')") +} + +func upgradeToVer77(s Session, ver int64) { + if ver >= version77 { + return + } + doReentrantDDL(s, CreateColumnStatsUsageTable) +} + +func upgradeToVer78(s Session, ver int64) { + if ver >= version78 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets MODIFY upper_bound LONGBLOB NOT NULL") + doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets MODIFY lower_bound LONGBLOB") + doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms MODIFY last_analyze_pos LONGBLOB DEFAULT NULL") +} + +func upgradeToVer79(s Session, ver int64) { + if ver >= version79 { + return + } + doReentrantDDL(s, CreateTableCacheMetaTable) +} + +func upgradeToVer80(s Session, ver int64) { + if ver >= version80 { + return + } + // Check if tidb_analyze_version exists in mysql.GLOBAL_VARIABLES. + // If not, insert "tidb_analyze_version | 1" since this is the old behavior before we introduce this variable. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBAnalyzeVersion) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBAnalyzeVersion, 1) +} + +// For users that upgrade TiDB from a pre-4.0 version, we want to disable index merge by default. +// This helps minimize query plan regressions. +func upgradeToVer81(s Session, ver int64) { + if ver >= version81 { + return + } + // Check if tidb_enable_index_merge exists in mysql.GLOBAL_VARIABLES. + // If not, insert "tidb_enable_index_merge | off". + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableIndexMerge) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableIndexMerge, variable.Off) +} + +func upgradeToVer82(s Session, ver int64) { + if ver >= version82 { + return + } + doReentrantDDL(s, CreateAnalyzeOptionsTable) +} + +func upgradeToVer83(s Session, ver int64) { + if ver >= version83 { + return + } + doReentrantDDL(s, CreateStatsHistory) +} + +func upgradeToVer84(s Session, ver int64) { + if ver >= version84 { + return + } + doReentrantDDL(s, CreateStatsMetaHistory) +} + +func upgradeToVer85(s Session, ver int64) { + if ver >= version85 { + return + } + mustExecute(s, fmt.Sprintf("UPDATE HIGH_PRIORITY mysql.bind_info SET status= '%s' WHERE status = '%s'", bindinfo.Enabled, bindinfo.Using)) +} + +func upgradeToVer86(s Session, ver int64) { + if ver >= version86 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY COLUMN Column_priv SET('Select','Insert','Update','References')") +} + +func upgradeToVer87(s Session, ver int64) { + if ver >= version87 { + return + } + doReentrantDDL(s, CreateAnalyzeJobs) +} + +func upgradeToVer88(s Session, ver int64) { + if ver >= version88 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `Repl_slave_priv` `Repl_slave_priv` ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER `Execute_priv`") + doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `Repl_client_priv` `Repl_client_priv` ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER `Repl_slave_priv`") +} + +func upgradeToVer89(s Session, ver int64) { + if ver >= version89 { + return + } + doReentrantDDL(s, CreateAdvisoryLocks) +} + +// importConfigOption is a one-time import. +// It is intended to be used to convert a config option to a sysvar. +// It reads the config value from the tidb-server executing the bootstrap +// (not guaranteed to be the same on all servers), and writes a message +// to the error log. The message is important since the behavior is weird +// (changes to the config file will no longer take effect past this point). +func importConfigOption(s Session, configName, svName, valStr string) { + message := fmt.Sprintf("%s is now configured by the system variable %s. One-time importing the value specified in tidb.toml file", configName, svName) + logutil.BgLogger().Warn(message, zap.String("value", valStr)) + // We use insert ignore, since if its a duplicate we don't want to overwrite any user-set values. + sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%s')", + mysql.SystemDB, mysql.GlobalVariablesTable, svName, valStr) + mustExecute(s, sql) +} + +func upgradeToVer90(s Session, ver int64) { + if ver >= version90 { + return + } + valStr := variable.BoolToOnOff(config.GetGlobalConfig().EnableBatchDML) + importConfigOption(s, "enable-batch-dml", variable.TiDBEnableBatchDML, valStr) + valStr = fmt.Sprint(config.GetGlobalConfig().MemQuotaQuery) + importConfigOption(s, "mem-quota-query", variable.TiDBMemQuotaQuery, valStr) + valStr = fmt.Sprint(config.GetGlobalConfig().Log.QueryLogMaxLen) + importConfigOption(s, "query-log-max-len", variable.TiDBQueryLogMaxLen, valStr) + valStr = fmt.Sprint(config.GetGlobalConfig().Performance.CommitterConcurrency) + importConfigOption(s, "committer-concurrency", variable.TiDBCommitterConcurrency, valStr) + valStr = variable.BoolToOnOff(config.GetGlobalConfig().Performance.RunAutoAnalyze) + importConfigOption(s, "run-auto-analyze", variable.TiDBEnableAutoAnalyze, valStr) + valStr = config.GetGlobalConfig().OOMAction + importConfigOption(s, "oom-action", variable.TiDBMemOOMAction, valStr) +} + +func upgradeToVer91(s Session, ver int64) { + if ver >= version91 { + return + } + valStr := variable.BoolToOnOff(config.GetGlobalConfig().PreparedPlanCache.Enabled) + importConfigOption(s, "prepared-plan-cache.enable", variable.TiDBEnablePrepPlanCache, valStr) + + valStr = strconv.Itoa(int(config.GetGlobalConfig().PreparedPlanCache.Capacity)) + importConfigOption(s, "prepared-plan-cache.capacity", variable.TiDBPrepPlanCacheSize, valStr) + + valStr = strconv.FormatFloat(config.GetGlobalConfig().PreparedPlanCache.MemoryGuardRatio, 'f', -1, 64) + importConfigOption(s, "prepared-plan-cache.memory-guard-ratio", variable.TiDBPrepPlanCacheMemoryGuardRatio, valStr) +} + +func upgradeToVer93(s Session, ver int64) { + if ver >= version93 { + return + } + valStr := variable.BoolToOnOff(config.GetGlobalConfig().OOMUseTmpStorage) + importConfigOption(s, "oom-use-tmp-storage", variable.TiDBEnableTmpStorageOnOOM, valStr) +} + +func upgradeToVer94(s Session, ver int64) { + if ver >= version94 { + return + } + mustExecute(s, CreateMDLView) +} + +func upgradeToVer95(s Session, ver int64) { + if ver >= version95 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `User_attributes` JSON") +} + +func upgradeToVer97(s Session, ver int64) { + if ver >= version97 { + return + } + // Check if tidb_opt_range_max_size exists in mysql.GLOBAL_VARIABLES. + // If not, insert "tidb_opt_range_max_size | 0" since this is the old behavior before we introduce this variable. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptRangeMaxSize) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptRangeMaxSize, 0) +} + +func upgradeToVer98(s Session, ver int64) { + if ver >= version98 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Token_issuer` varchar(255)") +} + +func upgradeToVer99Before(s Session) { + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableMDL, 0) +} + +func upgradeToVer99After(s Session) { + sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %[1]s.%[2]s SET VARIABLE_VALUE = %[4]d WHERE VARIABLE_NAME = '%[3]s'", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableMDL, 1) + mustExecute(s, sql) + err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), s.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + return t.SetMetadataLock(true) + }) + terror.MustNil(err) +} + +func upgradeToVer100(s Session, ver int64) { + if ver >= version100 { + return + } + valStr := strconv.Itoa(int(config.GetGlobalConfig().Performance.ServerMemoryQuota)) + importConfigOption(s, "performance.server-memory-quota", variable.TiDBServerMemoryLimit, valStr) +} + +func upgradeToVer101(s Session, ver int64) { + if ver >= version101 { + return + } + doReentrantDDL(s, CreatePlanReplayerStatusTable) +} + +func upgradeToVer102(s Session, ver int64) { + if ver >= version102 { + return + } + doReentrantDDL(s, CreatePlanReplayerTaskTable) +} + +func upgradeToVer103(s Session, ver int64) { + if ver >= version103 { + return + } + doReentrantDDL(s, CreateStatsTableLocked) +} + +func upgradeToVer104(s Session, ver int64) { + if ver >= version104 { + return + } + + doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN IF NOT EXISTS `sql_digest` varchar(64)") + doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN IF NOT EXISTS `plan_digest` varchar(64)") +} + +// For users that upgrade TiDB from a pre-6.0 version, we want to disable tidb cost model2 by default to keep plans unchanged. +func upgradeToVer105(s Session, ver int64) { + if ver >= version105 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBCostModelVersion) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBCostModelVersion, "1") +} + +func upgradeToVer106(s Session, ver int64) { + if ver >= version106 { + return + } + doReentrantDDL(s, CreatePasswordHistory) + doReentrantDDL(s, "Alter table mysql.user add COLUMN IF NOT EXISTS `Password_reuse_history` smallint unsigned DEFAULT NULL AFTER `Create_Tablespace_Priv` ") + doReentrantDDL(s, "Alter table mysql.user add COLUMN IF NOT EXISTS `Password_reuse_time` smallint unsigned DEFAULT NULL AFTER `Password_reuse_history`") +} + +func upgradeToVer107(s Session, ver int64) { + if ver >= version107 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_expired` ENUM('N','Y') NOT NULL DEFAULT 'N'") + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_last_changed` TIMESTAMP DEFAULT CURRENT_TIMESTAMP()") + doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_lifetime` SMALLINT UNSIGNED DEFAULT NULL") +} + +func upgradeToVer108(s Session, ver int64) { + if ver >= version108 { + return + } + doReentrantDDL(s, CreateTTLTableStatus) +} + +// For users that upgrade TiDB from a 6.2-6.4 version, we want to disable tidb gc_aware_memory_track by default. +func upgradeToVer109(s Session, ver int64) { + if ver >= version109 { + return + } + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableGCAwareMemoryTrack, 0) +} + +// For users that upgrade TiDB from a 5.4-6.4 version, we want to enable tidb tidb_stats_load_pseudo_timeout by default. +func upgradeToVer110(s Session, ver int64) { + if ver >= version110 { + return + } + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBStatsLoadPseudoTimeout, 1) +} + +func upgradeToVer130(s Session, ver int64) { + if ver >= version130 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_meta_history ADD COLUMN IF NOT EXISTS `source` varchar(40) NOT NULL after `version`;") +} + +func upgradeToVer131(s Session, ver int64) { + if ver >= version131 { + return + } + doReentrantDDL(s, CreateTTLTask) + doReentrantDDL(s, CreateTTLJobHistory) +} + +func upgradeToVer132(s Session, ver int64) { + if ver >= version132 { + return + } + doReentrantDDL(s, CreateMDLView) +} + +func upgradeToVer133(s Session, ver int64) { + if ver >= version133 { + return + } + mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n set VARIABLE_VALUE = %? where VARIABLE_NAME = %? and VARIABLE_VALUE = %?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.DefTiDBServerMemoryLimit, variable.TiDBServerMemoryLimit, "0") +} + +func upgradeToVer134(s Session, ver int64) { + if ver >= version134 { + return + } + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.ForeignKeyChecks, variable.On) + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableForeignKey, variable.On) + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableHistoricalStats, variable.On) + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnablePlanReplayerCapture, variable.On) + mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n SET VARIABLE_VALUE = %? WHERE VARIABLE_NAME = %? AND VARIABLE_VALUE = %?;", mysql.SystemDB, mysql.GlobalVariablesTable, "4", variable.TiDBStoreBatchSize, "0") +} + +// For users that upgrade TiDB from a pre-7.0 version, we want to set tidb_opt_advanced_join_hint to off by default to keep plans unchanged. +func upgradeToVer135(s Session, ver int64) { + if ver >= version135 { + return + } + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptAdvancedJoinHint) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptAdvancedJoinHint, false) +} + +func upgradeToVer136(s Session, ver int64) { + if ver >= version136 { + return + } + mustExecute(s, CreateGlobalTask) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask DROP INDEX namespace", dbterror.ErrCantDropFieldOrKey) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD INDEX idx_task_key(task_key)", dbterror.ErrDupKeyName) +} + +func upgradeToVer137(_ Session, _ int64) { + // NOOP, we don't depend on ddl to init the default group due to backward compatible issue. +} + +// For users that upgrade TiDB from a version below 7.0, we want to enable tidb tidb_enable_null_aware_anti_join by default. +func upgradeToVer138(s Session, ver int64) { + if ver >= version138 { + return + } + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptimizerEnableNAAJ, variable.On) +} + +func upgradeToVer139(s Session, ver int64) { + if ver >= version139 { + return + } + mustExecute(s, CreateLoadDataJobs) +} + +func upgradeToVer140(s Session, ver int64) { + if ver >= version140 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD COLUMN `task_key` VARCHAR(256) NOT NULL AFTER `id`", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD UNIQUE KEY task_key(task_key)", dbterror.ErrDupKeyName) +} + +// upgradeToVer141 sets the value of `tidb_session_plan_cache_size` as `tidb_prepared_plan_cache_size` for compatibility, +// and update tidb_load_based_replica_read_threshold from 0 to 4. +func upgradeToVer141(s Session, ver int64) { + if ver >= version141 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPrepPlanCacheSize) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + if err != nil || req.NumRows() == 0 { + return + } + row := req.GetRow(0) + if row.IsNull(0) { + return + } + val := row.GetString(0) + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBSessionPlanCacheSize, val) + mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBLoadBasedReplicaReadThreshold, variable.DefTiDBLoadBasedReplicaReadThreshold.String()) +} + +func upgradeToVer142(s Session, ver int64) { + if ver >= version142 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableNonPreparedPlanCache) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableNonPreparedPlanCache, variable.Off) +} + +func upgradeToVer143(s Session, ver int64) { + if ver >= version143 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) +} + +func upgradeToVer144(s Session, ver int64) { + if ver >= version144 { + return + } + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPlanCacheInvalidationOnFreshStats) + terror.MustNil(err) + req := rs.NewChunk(nil) + err = rs.Next(ctx, req) + terror.MustNil(err) + if req.NumRows() != 0 { + return + } + + mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", + mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPlanCacheInvalidationOnFreshStats, variable.Off) +} + +func upgradeToVer146(s Session, ver int64) { + if ver >= version146 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.stats_meta_history ADD INDEX idx_create_time (create_time)", dbterror.ErrDupKeyName) + doReentrantDDL(s, "ALTER TABLE mysql.stats_history ADD INDEX idx_create_time (create_time)", dbterror.ErrDupKeyName) +} + +func upgradeToVer167(s Session, ver int64) { + if ver >= version167 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `step` INT AFTER `id`", infoschema.ErrColumnExists) +} + +func upgradeToVer168(s Session, ver int64) { + if ver >= version168 { + return + } + mustExecute(s, CreateImportJobs) +} + +func upgradeToVer169(s Session, ver int64) { + if ver >= version169 { + return + } + mustExecute(s, CreateRunawayTable) +} + +func upgradeToVer170(s Session, ver int64) { + if ver >= version170 { + return + } + mustExecute(s, CreateTimers) +} + +func upgradeToVer171(s Session, ver int64) { + if ver >= version171 { + return + } + mustExecute(s, "ALTER TABLE mysql.tidb_runaway_queries CHANGE COLUMN `tidb_server` `tidb_server` varchar(512)") +} + +func upgradeToVer172(s Session, ver int64) { + if ver >= version172 { + return + } + mustExecute(s, "DROP TABLE IF EXISTS mysql.tidb_runaway_quarantined_watch") + mustExecute(s, CreateRunawayWatchTable) + mustExecute(s, CreateDoneRunawayWatchTable) +} + +func upgradeToVer173(s Session, ver int64) { + if ver >= version173 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `summary` JSON", infoschema.ErrColumnExists) +} + +func upgradeToVer174(s Session, ver int64) { + if ver >= version174 { + return + } + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD COLUMN `step` INT AFTER `id`", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history DROP INDEX `namespace`", dbterror.ErrCantDropFieldOrKey) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD INDEX `idx_task_key`(`task_key`)", dbterror.ErrDupKeyName) + doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD INDEX `idx_state_update_time`(`state_update_time`)", dbterror.ErrDupKeyName) +} + +// upgradeToVer175 updates normalized bindings of `in (?)` to `in (...)` to solve +// the issue #44298 that bindings for `in (?)` can't work for `in (?, ?, ?)`. +// After this update, multiple bindings may have the same `original_sql`, but it's OK, and +// for safety, don't remove duplicated bindings when upgrading. +func upgradeToVer175(s Session, ver int64) { + if ver >= version175 { + return + } + + var err error + mustExecute(s, "BEGIN PESSIMISTIC") + defer func() { + if err != nil { + mustExecute(s, "ROLLBACK") + return + } + mustExecute(s, "COMMIT") + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + rs, err := s.ExecuteInternal(ctx, "SELECT original_sql, bind_sql FROM mysql.bind_info WHERE source != 'builtin'") + if err != nil { + logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) + return + } + req := rs.NewChunk(nil) + for { + err = rs.Next(ctx, req) + if err != nil { + logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) + return + } + if req.NumRows() == 0 { + break + } + for i := 0; i < req.NumRows(); i++ { + originalNormalizedSQL, bindSQL := req.GetRow(i).GetString(0), req.GetRow(i).GetString(1) + newNormalizedSQL := parser.NormalizeForBinding(bindSQL) + // update `in (?)` to `in (...)` + if originalNormalizedSQL == newNormalizedSQL { + continue // no need to update + } + mustExecute(s, fmt.Sprintf("UPDATE mysql.bind_info SET original_sql='%s' WHERE original_sql='%s'", newNormalizedSQL, originalNormalizedSQL)) + } + req.Reset() + } + if err := rs.Close(); err != nil { + logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) + } +} + +func upgradeToVer176(s Session, ver int64) { + if ver >= version176 { + return + } + mustExecute(s, CreateGlobalTaskHistory) +} + +func writeOOMAction(s Session) { + comment := "oom-action is `log` by default in v3.0.x, `cancel` by default in v4.0.11+" + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE= %?`, + mysql.SystemDB, mysql.TiDBTable, tidbDefOOMAction, variable.OOMActionLog, comment, variable.OOMActionLog, + ) +} + +// updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. +func updateBootstrapVer(s Session) { + // Update bootstrap version. + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, + mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion, currentBootstrapVersion, + ) +} + +// getBootstrapVersion gets bootstrap version from mysql.tidb table; +func getBootstrapVersion(s Session) (int64, error) { + sVal, isNull, err := getTiDBVar(s, tidbServerVersionVar) + if err != nil { + return 0, errors.Trace(err) + } + if isNull { + return 0, nil + } + return strconv.ParseInt(sVal, 10, 64) +} + +// doDDLWorks executes DDL statements in bootstrap stage. +func doDDLWorks(s Session) { + // Create a test database. + mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") + // Create system db. + mustExecute(s, "CREATE DATABASE IF NOT EXISTS %n", mysql.SystemDB) + // Create user table. + mustExecute(s, CreateUserTable) + // Create password history. + mustExecute(s, CreatePasswordHistory) + // Create privilege tables. + mustExecute(s, CreateGlobalPrivTable) + mustExecute(s, CreateDBPrivTable) + mustExecute(s, CreateTablePrivTable) + mustExecute(s, CreateColumnPrivTable) + // Create global system variable table. + mustExecute(s, CreateGlobalVariablesTable) + // Create TiDB table. + mustExecute(s, CreateTiDBTable) + // Create help table. + mustExecute(s, CreateHelpTopic) + // Create stats_meta table. + mustExecute(s, CreateStatsMetaTable) + // Create stats_columns table. + mustExecute(s, CreateStatsColsTable) + // Create stats_buckets table. + mustExecute(s, CreateStatsBucketsTable) + // Create gc_delete_range table. + mustExecute(s, CreateGCDeleteRangeTable) + // Create gc_delete_range_done table. + mustExecute(s, CreateGCDeleteRangeDoneTable) + // Create stats_feedback table. + // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. + mustExecute(s, CreateStatsFeedbackTable) + // Create role_edges table. + mustExecute(s, CreateRoleEdgesTable) + // Create default_roles table. + mustExecute(s, CreateDefaultRolesTable) + // Create bind_info table. + initBindInfoTable(s) + // Create stats_topn_store table. + mustExecute(s, CreateStatsTopNTable) + // Create expr_pushdown_blacklist table. + mustExecute(s, CreateExprPushdownBlacklist) + // Create opt_rule_blacklist table. + mustExecute(s, CreateOptRuleBlacklist) + // Create stats_extended table. + mustExecute(s, CreateStatsExtended) + // Create schema_index_usage. + mustExecute(s, CreateSchemaIndexUsageTable) + // Create stats_fm_sketch table. + mustExecute(s, CreateStatsFMSketchTable) + // Create global_grants + mustExecute(s, CreateGlobalGrantsTable) + // Create capture_plan_baselines_blacklist + mustExecute(s, CreateCapturePlanBaselinesBlacklist) + // Create column_stats_usage table + mustExecute(s, CreateColumnStatsUsageTable) + // Create table_cache_meta table. + mustExecute(s, CreateTableCacheMetaTable) + // Create analyze_options table. + mustExecute(s, CreateAnalyzeOptionsTable) + // Create stats_history table. + mustExecute(s, CreateStatsHistory) + // Create stats_meta_history table. + mustExecute(s, CreateStatsMetaHistory) + // Create analyze_jobs table. + mustExecute(s, CreateAnalyzeJobs) + // Create advisory_locks table. + mustExecute(s, CreateAdvisoryLocks) + // Create mdl view. + mustExecute(s, CreateMDLView) + // Create plan_replayer_status table + mustExecute(s, CreatePlanReplayerStatusTable) + // Create plan_replayer_task table + mustExecute(s, CreatePlanReplayerTaskTable) + // Create stats_meta_table_locked table + mustExecute(s, CreateStatsTableLocked) + // Create tidb_ttl_table_status table + mustExecute(s, CreateTTLTableStatus) + // Create tidb_ttl_task table + mustExecute(s, CreateTTLTask) + // Create tidb_ttl_job_history table + mustExecute(s, CreateTTLJobHistory) + // Create tidb_global_task table + mustExecute(s, CreateGlobalTask) + // Create tidb_global_task_history table + mustExecute(s, CreateGlobalTaskHistory) + // Create load_data_jobs + mustExecute(s, CreateLoadDataJobs) + // Create tidb_import_jobs + mustExecute(s, CreateImportJobs) + // create runaway_watch + mustExecute(s, CreateRunawayWatchTable) + // create runaway_queries + mustExecute(s, CreateRunawayTable) + // create tidb_timers + mustExecute(s, CreateTimers) + // create runaway_watch done + mustExecute(s, CreateDoneRunawayWatchTable) + // create dist_framework_meta + mustExecute(s, CreateDistFrameworkMeta) +} + +// doBootstrapSQLFile executes SQL commands in a file as the last stage of bootstrap. +// It is useful for setting the initial value of GLOBAL variables. +func doBootstrapSQLFile(s Session) error { + sqlFile := config.GetGlobalConfig().InitializeSQLFile + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + if sqlFile == "" { + return nil + } + logutil.BgLogger().Info("executing -initialize-sql-file", zap.String("file", sqlFile)) + b, err := os.ReadFile(sqlFile) //nolint:gosec + if err != nil { + if intest.InTest { + return err + } + logutil.BgLogger().Fatal("unable to read InitializeSQLFile", zap.Error(err)) + } + stmts, err := s.Parse(ctx, string(b)) + if err != nil { + if intest.InTest { + return err + } + logutil.BgLogger().Fatal("unable to parse InitializeSQLFile", zap.Error(err)) + } + for _, stmt := range stmts { + rs, err := s.ExecuteStmt(ctx, stmt) + if err != nil { + logutil.BgLogger().Warn("InitializeSQLFile error", zap.Error(err)) + } + if rs != nil { + // I don't believe we need to drain the result-set in bootstrap mode + // but if required we can do this here in future. + if err := rs.Close(); err != nil { + logutil.BgLogger().Fatal("unable to close result", zap.Error(err)) + } + } + } + return nil +} + +// doDMLWorks executes DML statements in bootstrap stage. +// All the statements run in a single transaction. +func doDMLWorks(s Session) { + mustExecute(s, "BEGIN") + if config.GetGlobalConfig().Security.SecureBootstrap { + // If secure bootstrap is enabled, we create a root@localhost account which can login with auth_socket. + // i.e. mysql -S /tmp/tidb.sock -uroot + // The auth_socket plugin will validate that the user matches $USER. + u, err := osuser.Current() + if err != nil { + logutil.BgLogger().Fatal("failed to read current user. unable to secure bootstrap.", zap.Error(err)) + } + mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user (Host,User,authentication_string,plugin,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Process_priv,Grant_priv,References_priv,Alter_priv,Show_db_priv, + Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Index_priv,Create_user_priv,Event_priv,Repl_slave_priv,Repl_client_priv,Trigger_priv,Create_role_priv,Drop_role_priv,Account_locked, + Shutdown_priv,Reload_priv,FILE_priv,Config_priv,Create_Tablespace_Priv,User_attributes,Token_issuer) VALUES + ("localhost", "root", %?, "auth_socket", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", null, "")`, u.Username) + } else { + mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user (Host,User,authentication_string,plugin,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Process_priv,Grant_priv,References_priv,Alter_priv,Show_db_priv, + Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Index_priv,Create_user_priv,Event_priv,Repl_slave_priv,Repl_client_priv,Trigger_priv,Create_role_priv,Drop_role_priv,Account_locked, + Shutdown_priv,Reload_priv,FILE_priv,Config_priv,Create_Tablespace_Priv,User_attributes,Token_issuer) VALUES + ("%", "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", null, "")`) + } + + // For GLOBAL scoped system variables, insert the initial value + // into the mysql.global_variables table. This is only run on initial + // bootstrap, and in some cases we will use a different default value + // for new installs versus existing installs. + + values := make([]string, 0, len(variable.GetSysVars())) + for k, v := range variable.GetSysVars() { + if !v.HasGlobalScope() { + continue + } + vVal := v.Value + switch v.Name { + case variable.TiDBTxnMode: + if config.GetGlobalConfig().Store == "tikv" || config.GetGlobalConfig().Store == "unistore" { + vVal = "pessimistic" + } + case variable.TiDBEnableAsyncCommit, variable.TiDBEnable1PC: + if config.GetGlobalConfig().Store == "tikv" { + vVal = variable.On + } + case variable.TiDBMemOOMAction: + if intest.InTest { + vVal = variable.OOMActionLog + } + case variable.TiDBEnableAutoAnalyze: + if intest.InTest { + vVal = variable.Off + } + // For the following sysvars, we change the default + // FOR NEW INSTALLS ONLY. In most cases you don't want to do this. + // It is better to change the value in the Sysvar struct, so that + // all installs will have the same value. + case variable.TiDBRowFormatVersion: + vVal = strconv.Itoa(variable.DefTiDBRowFormatV2) + case variable.TiDBTxnAssertionLevel: + vVal = variable.AssertionFastStr + case variable.TiDBEnableMutationChecker: + vVal = variable.On + case variable.TiDBPessimisticTransactionFairLocking: + vVal = variable.On + } + + // sanitize k and vVal + value := fmt.Sprintf(`("%s", "%s")`, sqlexec.EscapeString(k), sqlexec.EscapeString(vVal)) + values = append(values, value) + } + sql := fmt.Sprintf("INSERT HIGH_PRIORITY INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, + strings.Join(values, ", ")) + mustExecute(s, sql) + + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES(%?, %?, "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, + mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, varTrue, varTrue, + ) + + mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES(%?, %?, "Bootstrap version. Do not delete.")`, + mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion, + ) + writeSystemTZ(s) + + writeNewCollationParameter(s, config.GetGlobalConfig().NewCollationsEnabledOnFirstBootstrap) + + writeStmtSummaryVars(s) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + _, err := s.ExecuteInternal(ctx, "COMMIT") + if err != nil { + sleepTime := 1 * time.Second + logutil.BgLogger().Info("doDMLWorks failed", zap.Error(err), zap.Duration("sleeping time", sleepTime)) + time.Sleep(sleepTime) + // Check if TiDB is already bootstrapped. + b, err1 := checkBootstrapped(s) + if err1 != nil { + logutil.BgLogger().Fatal("doDMLWorks failed", zap.Error(err1)) + } + if b { + return + } + logutil.BgLogger().Fatal("doDMLWorks failed", zap.Error(err)) + } +} + +func mustExecute(s Session, sql string, args ...interface{}) { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(internalSQLTimeout)*time.Second) + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) + _, err := s.ExecuteInternal(ctx, sql, args...) + defer cancel() + if err != nil { + logutil.BgLogger().Fatal("mustExecute error", zap.Error(err), zap.Stack("stack")) + } +} + +// oldPasswordUpgrade upgrade password to MySQL compatible format +func oldPasswordUpgrade(pass string) (string, error) { + hash1, err := hex.DecodeString(pass) + if err != nil { + return "", errors.Trace(err) + } + + hash2 := auth.Sha1Hash(hash1) + newpass := fmt.Sprintf("*%X", hash2) + return newpass, nil +} + +// rebuildAllPartitionValueMapAndSorted rebuilds all value map and sorted info for list column partitions with InfoSchema. +func rebuildAllPartitionValueMapAndSorted(s *session) { + type partitionExpr interface { + PartitionExpr() *tables.PartitionExpr + } + + p := parser.New() + is := s.GetInfoSchema().(infoschema.InfoSchema) + for _, dbInfo := range is.AllSchemas() { + for _, t := range is.SchemaTables(dbInfo.Name) { + pi := t.Meta().GetPartitionInfo() + if pi == nil || pi.Type != model.PartitionTypeList { + continue + } + + pe := t.(partitionExpr).PartitionExpr() + for _, cp := range pe.ColPrunes { + if err := cp.RebuildPartitionValueMapAndSorted(p, pi.Definitions); err != nil { + logutil.BgLogger().Warn("build list column partition value map and sorted failed") + break + } + } + } + } +} diff --git a/pkg/session/bootstrap_test.go b/pkg/session/bootstrap_test.go new file mode 100644 index 0000000000000..88ffb531ce4bb --- /dev/null +++ b/pkg/session/bootstrap_test.go @@ -0,0 +1,2089 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "fmt" + "sort" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/stretchr/testify/require" +) + +// This test file have many problem. +// 1. Please use testkit to create dom, session and store. +// 2. Don't use CreateStoreAndBootstrap and BootstrapSession together. It will cause data race. +// Please do not add any test here. You can add test case at the bootstrap_update_test.go. After All problem fixed, +// We will overwrite this file by update_test.go. +func TestBootstrap(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + MustExec(t, se, "set global tidb_txn_mode=''") + MustExec(t, se, "use mysql") + r := MustExecToRecodeSet(t, se, "select * from user") + require.NotNil(t, r) + + ctx := context.Background() + req := r.NewChunk(nil) + err := r.Next(ctx, req) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + + rows := statistics.RowToDatums(req.GetRow(0), r.Fields()) + match(t, rows, `%`, "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", nil, nil, nil, "", "N", time.Now(), nil) + r.Close() + + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "anyhost"}, []byte(""), []byte(""), nil)) + + MustExec(t, se, "use test") + + // Check privilege tables. + MustExec(t, se, "SELECT * from mysql.global_priv") + MustExec(t, se, "SELECT * from mysql.db") + MustExec(t, se, "SELECT * from mysql.tables_priv") + MustExec(t, se, "SELECT * from mysql.columns_priv") + MustExec(t, se, "SELECT * from mysql.global_grants") + + // Check privilege tables. + r = MustExecToRecodeSet(t, se, "SELECT COUNT(*) from mysql.global_variables") + require.NotNil(t, r) + + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, globalVarsCount(), req.GetRow(0).GetInt64(0)) + require.NoError(t, r.Close()) + + // Check a storage operations are default autocommit after the second start. + MustExec(t, se, "USE test") + MustExec(t, se, "drop table if exists t") + MustExec(t, se, "create table t (id int)") + unsetStoreBootstrapped(store.UUID()) + se.Close() + + se, err = CreateSession4Test(store) + require.NoError(t, err) + MustExec(t, se, "USE test") + MustExec(t, se, "insert t values (?)", 3) + + se, err = CreateSession4Test(store) + require.NoError(t, err) + MustExec(t, se, "USE test") + r = MustExecToRecodeSet(t, se, "select * from t") + require.NotNil(t, r) + + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + rows = statistics.RowToDatums(req.GetRow(0), r.Fields()) + match(t, rows, 3) + MustExec(t, se, "drop table if exists t") + se.Close() + + // Try to do bootstrap dml jobs on an already bootstrapped TiDB system will not cause fatal. + // For https://github.com/pingcap/tidb/issues/1096 + se, err = CreateSession4Test(store) + require.NoError(t, err) + doDMLWorks(se) + r = MustExecToRecodeSet(t, se, "select * from mysql.expr_pushdown_blacklist where name = 'date_add'") + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) + se.Close() +} + +func globalVarsCount() int64 { + var count int64 + for _, v := range variable.GetSysVars() { + if v.HasGlobalScope() { + count++ + } + } + return count +} + +// testBootstrapWithError : +// When a session failed in bootstrap process (for example, the session is killed after doDDLWorks()). +// We should make sure that the following session could finish the bootstrap process. +func TestBootstrapWithError(t *testing.T) { + ctx := context.Background() + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + // bootstrap + { + se := &session{ + store: store, + sessionVars: variable.NewSessionVars(nil), + } + globalVarsAccessor := variable.NewMockGlobalAccessor4Tests() + se.GetSessionVars().GlobalVarsAccessor = globalVarsAccessor + se.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) + se.txn.init() + se.mu.values = make(map[fmt.Stringer]interface{}) + se.SetValue(sessionctx.Initing, true) + err := InitDDLJobTables(store, meta.BaseDDLTableVersion) + require.NoError(t, err) + err = InitMDLTable(store) + require.NoError(t, err) + err = InitDDLJobTables(store, meta.BackfillTableVersion) + require.NoError(t, err) + dom, err := domap.Get(store) + require.NoError(t, err) + domain.BindDomain(se, dom) + b, err := checkBootstrapped(se) + require.False(t, b) + require.NoError(t, err) + doDDLWorks(se) + } + + dom, err := domap.Get(store) + require.NoError(t, err) + dom.Close() + + dom1, err := BootstrapSession(store) + require.NoError(t, err) + defer dom1.Close() + + se := CreateSessionAndSetID(t, store) + MustExec(t, se, "USE mysql") + r := MustExecToRecodeSet(t, se, `select * from user`) + req := r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + + row := req.GetRow(0) + rows := statistics.RowToDatums(row, r.Fields()) + match(t, rows, `%`, "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", nil, nil, nil, "", "N", time.Now(), nil) + require.NoError(t, r.Close()) + + MustExec(t, se, "USE test") + // Check privilege tables. + MustExec(t, se, "SELECT * from mysql.global_priv") + MustExec(t, se, "SELECT * from mysql.db") + MustExec(t, se, "SELECT * from mysql.tables_priv") + MustExec(t, se, "SELECT * from mysql.columns_priv") + // Check role tables. + MustExec(t, se, "SELECT * from mysql.role_edges") + MustExec(t, se, "SELECT * from mysql.default_roles") + // Check global variables. + r = MustExecToRecodeSet(t, se, "SELECT COUNT(*) from mysql.global_variables") + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + v := req.GetRow(0) + require.Equal(t, globalVarsCount(), v.GetInt64(0)) + require.NoError(t, r.Close()) + + r = MustExecToRecodeSet(t, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstrapped"`) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + row = req.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, []byte("True"), row.GetBytes(0)) + require.NoError(t, r.Close()) + + MustExec(t, se, "SELECT * from mysql.tidb_background_subtask") + MustExec(t, se, "SELECT * from mysql.tidb_background_subtask_history") + + // Check tidb_ttl_table_status table + MustExec(t, se, "SELECT * from mysql.tidb_ttl_table_status") +} + +func TestDDLTableCreateBackfillTable(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + se := CreateSessionAndSetID(t, store) + + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + ver, err := m.CheckDDLTableVersion() + require.NoError(t, err) + require.GreaterOrEqual(t, ver, meta.BackfillTableVersion) + + // downgrade `mDDLTableVersion` + m.SetDDLTables(meta.MDLTableVersion) + MustExec(t, se, "drop table mysql.tidb_background_subtask") + MustExec(t, se, "drop table mysql.tidb_background_subtask_history") + err = txn.Commit(context.Background()) + require.NoError(t, err) + + // to upgrade session for create ddl related tables + dom.Close() + dom, err = BootstrapSession(store) + require.NoError(t, err) + + se = CreateSessionAndSetID(t, store) + MustExec(t, se, "select * from mysql.tidb_background_subtask") + MustExec(t, se, "select * from mysql.tidb_background_subtask_history") + dom.Close() +} + +// TestUpgrade tests upgrading +func TestUpgrade(t *testing.T) { + ctx := context.Background() + + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + se := CreateSessionAndSetID(t, store) + + MustExec(t, se, "USE mysql") + + // bootstrap with currentBootstrapVersion + r := MustExecToRecodeSet(t, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) + req := r.NewChunk(nil) + err := r.Next(ctx, req) + row := req.GetRow(0) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + require.Equal(t, 1, row.Len()) + require.Equal(t, []byte(fmt.Sprintf("%d", currentBootstrapVersion)), row.GetBytes(0)) + require.NoError(t, r.Close()) + + se1 := CreateSessionAndSetID(t, store) + ver, err := getBootstrapVersion(se1) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // Do something to downgrade the store. + // downgrade meta bootstrap version + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(1)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, se1, `delete from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) + MustExec(t, se1, fmt.Sprintf(`delete from mysql.global_variables where VARIABLE_NAME="%s"`, variable.TiDBDistSQLScanConcurrency)) + MustExec(t, se1, `commit`) + unsetStoreBootstrapped(store.UUID()) + // Make sure the version is downgraded. + r = MustExecToRecodeSet(t, se1, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) + require.NoError(t, r.Close()) + + ver, err = getBootstrapVersion(se1) + require.NoError(t, err) + require.Equal(t, int64(0), ver) + dom.Close() + // Create a new session then upgrade() will run automatically. + dom, err = BootstrapSession(store) + require.NoError(t, err) + + se2 := CreateSessionAndSetID(t, store) + r = MustExecToRecodeSet(t, se2, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + row = req.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, []byte(fmt.Sprintf("%d", currentBootstrapVersion)), row.GetBytes(0)) + require.NoError(t, r.Close()) + + ver, err = getBootstrapVersion(se2) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // Verify that 'new_collation_enabled' is false. + r = MustExecToRecodeSet(t, se2, fmt.Sprintf(`SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME='%s'`, tidbNewCollationEnabled)) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + require.Equal(t, "False", req.GetRow(0).GetString(0)) + require.NoError(t, r.Close()) + dom.Close() +} + +func TestIssue17979_1(t *testing.T) { + ctx := context.Background() + + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + // test issue 20900, upgrade from v3.0 to v4.0.11+ + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(58)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, "update mysql.tidb set variable_value='58' where variable_name='tidb_server_version'") + MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_oom_action'") + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(58), ver) + dom.Close() + domV4, err := BootstrapSession(store) + require.NoError(t, err) + seV4 := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seV4) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + r := MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_oom_action'") + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, variable.OOMActionLog, req.GetRow(0).GetString(0)) + domV4.Close() +} + +func TestIssue17979_2(t *testing.T) { + ctx := context.Background() + + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // test issue 20900, upgrade from v4.0.11 to v4.0.11 + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(59)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, "update mysql.tidb set variable_value=59 where variable_name='tidb_server_version'") + MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_iim_action'") + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(59), ver) + dom.Close() + domV4, err := BootstrapSession(store) + require.NoError(t, err) + defer domV4.Close() + seV4 := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seV4) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + r := MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_oom_action'") + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, 0, req.NumRows()) +} + +// TestIssue20900_2 tests that a user can upgrade from TiDB 2.1 to latest, +// and their configuration remains similar. This helps protect against the +// case that a user had a 32G query memory limit in 2.1, but it is now a 1G limit +// in TiDB 4.0+. I tested this process, and it does correctly upgrade from 2.1 -> 4.0, +// but from 4.0 -> 5.0, the new default is picked up. + +func TestIssue20900_2(t *testing.T) { + ctx := context.Background() + + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // test issue 20900, upgrade from v4.0.8 to v4.0.9+ + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(52)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, "update mysql.tidb set variable_value=52 where variable_name='tidb_server_version'") + MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_memory_quota_query'") + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(52), ver) + dom.Close() + domV4, err := BootstrapSession(store) + require.NoError(t, err) + seV4 := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seV4) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + r := MustExecToRecodeSet(t, seV4, "select @@tidb_mem_quota_query") + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, "1073741824", req.GetRow(0).GetString(0)) + require.Equal(t, int64(1073741824), seV4.GetSessionVars().MemQuotaQuery) + r = MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_memory_quota_query'") + req = r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, 0, req.NumRows()) + domV4.Close() +} + +func TestANSISQLMode(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + se := CreateSessionAndSetID(t, store) + + MustExec(t, se, "USE mysql") + MustExec(t, se, `set @@global.sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ANSI"`) + MustExec(t, se, `delete from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) + unsetStoreBootstrapped(store.UUID()) + se.Close() + + // Do some clean up, BootstrapSession will not create a new domain otherwise. + dom.Close() + domap.Delete(store) + + // Set ANSI sql_mode and bootstrap again, to cover a bugfix. + // Once we have a SQL like that: + // select variable_value from mysql.tidb where variable_name = "system_tz" + // it fails to execute in the ANSI sql_mode, and makes TiDB cluster fail to bootstrap. + dom1, err := BootstrapSession(store) + require.NoError(t, err) + defer dom1.Close() + se = CreateSessionAndSetID(t, store) + MustExec(t, se, "select @@global.sql_mode") + se.Close() +} + +func TestOldPasswordUpgrade(t *testing.T) { + pwd := "abc" + oldpwd := fmt.Sprintf("%X", auth.Sha1Hash([]byte(pwd))) + newpwd, err := oldPasswordUpgrade(oldpwd) + require.NoError(t, err) + require.Equal(t, "*0D3CED9BEC10A777AEC23CCC353A8C08A633045E", newpwd) +} + +func TestBootstrapInitExpensiveQueryHandle(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + se, err := createSession(store) + require.NoError(t, err) + dom := domain.GetDomain(se) + require.NotNil(t, dom) + defer dom.Close() + require.NotNil(t, dom.ExpensiveQueryHandle()) +} + +func TestStmtSummary(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + r := MustExecToRecodeSet(t, se, "select variable_value from mysql.global_variables where variable_name='tidb_enable_stmt_summary'") + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + row := req.GetRow(0) + require.Equal(t, []byte("ON"), row.GetBytes(0)) + require.NoError(t, r.Close()) +} + +type bindTestStruct struct { + originText string + bindText string + db string + originWithDB string + bindWithDB string + deleteText string +} + +func TestUpdateBindInfo(t *testing.T) { + bindCases := []bindTestStruct{ + { + originText: "select * from t where a > ?", + bindText: "select /*+ use_index(t, idxb) */ * from t where a > 1", + db: "test", + originWithDB: "select * from `test` . `t` where `a` > ?", + bindWithDB: "SELECT /*+ use_index(`t` `idxb`)*/ * FROM `test`.`t` WHERE `a` > 1", + deleteText: "select * from test.t where a > 1", + }, + { + originText: "select count ( ? ), max ( a ) from t group by b", + bindText: "select /*+ use_index(t, idx) */ count(1), max(a) from t group by b", + db: "test", + originWithDB: "select count ( ? ) , max ( `a` ) from `test` . `t` group by `b`", + bindWithDB: "SELECT /*+ use_index(`t` `idx`)*/ count(1),max(`a`) FROM `test`.`t` GROUP BY `b`", + deleteText: "select count(1), max(a) from test.t group by b", + }, + { + originText: "select * from `test` . `t` where `a` = (_charset) ?", + bindText: "SELECT * FROM test.t WHERE a = _utf8\\'ab\\'", + db: "test", + originWithDB: "select * from `test` . `t` where `a` = ?", + bindWithDB: "SELECT * FROM `test`.`t` WHERE `a` = 'ab'", + deleteText: "select * from test.t where a = 'c'", + }, + } + + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") + MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") + for _, bindCase := range bindCases { + sql := fmt.Sprintf("insert into mysql.bind_info values('%s', '%s', '%s', 'enabled', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')", + bindCase.originText, + bindCase.bindText, + bindCase.db, + ) + MustExec(t, se, sql) + + upgradeToVer67(se, version66) + r := MustExecToRecodeSet(t, se, `select original_sql, bind_sql, default_db, status from mysql.bind_info where source != 'builtin'`) + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + row := req.GetRow(0) + require.Equal(t, bindCase.originWithDB, row.GetString(0)) + require.Equal(t, bindCase.bindWithDB, row.GetString(1)) + require.Equal(t, "", row.GetString(2)) + require.Equal(t, bindinfo.Enabled, row.GetString(3)) + require.NoError(t, r.Close()) + sql = fmt.Sprintf("drop global binding for %s", bindCase.deleteText) + MustExec(t, se, sql) + r = MustExecToRecodeSet(t, se, `select original_sql, bind_sql, status from mysql.bind_info where source != 'builtin'`) + require.NoError(t, r.Next(ctx, req)) + row = req.GetRow(0) + require.Equal(t, bindCase.originWithDB, row.GetString(0)) + require.Equal(t, bindCase.bindWithDB, row.GetString(1)) + require.Equal(t, "deleted", row.GetString(2)) + require.NoError(t, r.Close()) + sql = fmt.Sprintf("delete from mysql.bind_info where original_sql = '%s'", bindCase.originWithDB) + MustExec(t, se, sql) + } +} + +func TestUpdateDuplicateBindInfo(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") + MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") + + MustExec(t, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'enabled', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + // The latest one. + MustExec(t, se, `insert into mysql.bind_info values('select * from test . t', 'select /*+ use_index(t, idx_b)*/ * from test.t', 'test', 'enabled', '2021-01-04 14:50:58.257', '2021-01-09 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + + MustExec(t, se, `insert into mysql.bind_info values('select * from t where a < ?', 'select * from t use index(idx) where a < 1', 'test', 'deleted', '2021-06-04 17:04:43.333', '2021-06-04 17:04:43.335', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from t where a < ?', 'select * from t ignore index(idx) where a < 1', 'test', 'enabled', '2021-06-04 17:04:43.335', '2021-06-04 17:04:43.335', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from test . t where a <= ?', 'select * from test.t use index(idx) where a <= 1', '', 'deleted', '2021-06-04 17:04:43.345', '2021-06-04 17:04:45.334', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from test . t where a <= ?', 'select * from test.t ignore index(idx) where a <= 1', '', 'enabled', '2021-06-04 17:04:45.334', '2021-06-04 17:04:45.334', 'utf8', 'utf8_general_ci', 'manual')`) + + upgradeToVer67(se, version66) + + r := MustExecToRecodeSet(t, se, `select original_sql, bind_sql, default_db, status, create_time from mysql.bind_info where source != 'builtin' order by create_time`) + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, 3, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, "select * from `test` . `t`", row.GetString(0)) + require.Equal(t, "SELECT /*+ use_index(`t` `idx_b`)*/ * FROM `test`.`t`", row.GetString(1)) + require.Equal(t, "", row.GetString(2)) + require.Equal(t, bindinfo.Enabled, row.GetString(3)) + require.Equal(t, "2021-01-04 14:50:58.257", row.GetTime(4).String()) + row = req.GetRow(1) + require.Equal(t, "select * from `test` . `t` where `a` < ?", row.GetString(0)) + require.Equal(t, "SELECT * FROM `test`.`t` IGNORE INDEX (`idx`) WHERE `a` < 1", row.GetString(1)) + require.Equal(t, "", row.GetString(2)) + require.Equal(t, bindinfo.Enabled, row.GetString(3)) + require.Equal(t, "2021-06-04 17:04:43.335", row.GetTime(4).String()) + row = req.GetRow(2) + require.Equal(t, "select * from `test` . `t` where `a` <= ?", row.GetString(0)) + require.Equal(t, "SELECT * FROM `test`.`t` IGNORE INDEX (`idx`) WHERE `a` <= 1", row.GetString(1)) + require.Equal(t, "", row.GetString(2)) + require.Equal(t, bindinfo.Enabled, row.GetString(3)) + require.Equal(t, "2021-06-04 17:04:45.334", row.GetTime(4).String()) + + require.NoError(t, r.Close()) + MustExec(t, se, "delete from mysql.bind_info where original_sql = 'select * from test . t'") +} + +func TestUpgradeClusteredIndexDefaultValue(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + seV67 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(67)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV67, "update mysql.tidb set variable_value='67' where variable_name='tidb_server_version'") + MustExec(t, seV67, "UPDATE mysql.global_variables SET VARIABLE_VALUE = 'OFF' where VARIABLE_NAME = 'tidb_enable_clustered_index'") + require.Equal(t, uint64(1), seV67.GetSessionVars().StmtCtx.AffectedRows()) + MustExec(t, seV67, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV67) + require.NoError(t, err) + require.Equal(t, int64(67), ver) + dom.Close() + + domV68, err := BootstrapSession(store) + require.NoError(t, err) + seV68 := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seV68) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + r := MustExecToRecodeSet(t, seV68, `select @@global.tidb_enable_clustered_index, @@session.tidb_enable_clustered_index`) + req := r.NewChunk(nil) + require.NoError(t, r.Next(context.Background(), req)) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, "ON", row.GetString(0)) + require.Equal(t, "ON", row.GetString(1)) + domV68.Close() +} + +func TestForIssue23387(t *testing.T) { + // For issue https://github.com/pingcap/tidb/issues/23387 + saveCurrentBootstrapVersion := currentBootstrapVersion + currentBootstrapVersion = version57 + + // Bootstrap to an old version, create a user. + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { require.NoError(t, store.Close()) }() + dom, err := BootstrapSession(store) + require.NoError(t, err) + + se := CreateSessionAndSetID(t, store) + se.Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, []byte("012345678901234567890"), nil) + MustExec(t, se, "create user quatest") + dom.Close() + // Upgrade to a newer version, check the user's privilege. + currentBootstrapVersion = saveCurrentBootstrapVersion + dom, err = BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + + se = CreateSessionAndSetID(t, store) + se.Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, []byte("012345678901234567890"), nil) + rs, err := exec(se, "show grants for quatest") + require.NoError(t, err) + rows, err := ResultSetToStringSlice(context.Background(), se, rs) + require.NoError(t, err) + require.Len(t, rows, 1) + require.Equal(t, "GRANT USAGE ON *.* TO 'quatest'@'%'", rows[0][0]) +} + +func TestReferencesPrivilegeOnColumn(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + defer func() { + MustExec(t, se, "drop user if exists issue28531") + MustExec(t, se, "drop table if exists t1") + }() + + MustExec(t, se, "create user if not exists issue28531") + MustExec(t, se, "use test") + MustExec(t, se, "drop table if exists t1") + MustExec(t, se, "create table t1 (a int)") + MustExec(t, se, "GRANT select (a), update (a),insert(a), references(a) on t1 to issue28531") +} + +func TestAnalyzeVersionUpgradeFrom300To500(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // Upgrade from 3.0.0 to 5.1+ or above. + ver300 := 33 + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver300)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) + MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBAnalyzeVersion)) + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(ver300), ver) + + // We are now in 3.0.0, check tidb_analyze_version should not exist. + res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBAnalyzeVersion)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + dom.Close() + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in version no lower than 5.x, tidb_enable_index_merge should be 1. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_analyze_version") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, "1", row.GetString(0)) +} + +func TestIndexMergeInNewCluster(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + // Indicates we are in a new cluster. + require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) + dom, err := BootstrapSession(store) + require.NoError(t, err) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + // In a new created cluster(above 5.4+), tidb_enable_index_merge is 1 by default. + MustExec(t, se, "use test;") + r := MustExecToRecodeSet(t, se, "select @@tidb_enable_index_merge;") + require.NotNil(t, r) + + ctx := context.Background() + chk := r.NewChunk(nil) + err = r.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, int64(1), row.GetInt64(0)) +} + +func TestIndexMergeUpgradeFrom300To540(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // Upgrade from 3.0.0 to 5.4+. + ver300 := 33 + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver300)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) + MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(ver300), ver) + + // We are now in 3.0.0, check tidb_enable_index_merge shoudle not exist. + res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + dom.Close() + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 5.x, tidb_enable_index_merge should be off. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_enable_index_merge") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, int64(0), row.GetInt64(0)) +} + +func TestIndexMergeUpgradeFrom400To540(t *testing.T) { + for i := 0; i < 2; i++ { + func() { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 4.0.0 to 5.4+. + ver400 := 46 + seV4 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver400)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV4, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver400)) + MustExec(t, seV4, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", variable.Off, variable.TiDBEnableIndexMerge)) + MustExec(t, seV4, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV4) + require.NoError(t, err) + require.Equal(t, int64(ver400), ver) + + // We are now in 4.0.0, tidb_enable_index_merge is off. + res := MustExecToRecodeSet(t, seV4, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, variable.Off, row.GetString(1)) + + if i == 0 { + // For the first time, We set tidb_enable_index_merge as on. + // And after upgrade to 5.x, tidb_enable_index_merge should remains to be on. + // For the second it should be off. + MustExec(t, seV4, "set global tidb_enable_index_merge = on") + } + dom.Close() + // Upgrade to 5.x. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 5.x, tidb_enable_index_merge should be on because we enable it in 4.0.0. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_enable_index_merge") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 1, row.Len()) + if i == 0 { + require.Equal(t, int64(1), row.GetInt64(0)) + } else { + require.Equal(t, int64(0), row.GetInt64(0)) + } + }() + } +} + +func TestUpgradeToVer85(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") + MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") + + MustExec(t, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'using', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from t1', 'select /*+ use_index(t1, idx_a)*/ * from t1', 'test', 'enabled', '2021-01-05 14:50:58.257', '2021-01-05 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from t2', 'select /*+ use_index(t2, idx_a)*/ * from t2', 'test', 'disabled', '2021-01-06 14:50:58.257', '2021-01-06 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from t3', 'select /*+ use_index(t3, idx_a)*/ * from t3', 'test', 'deleted', '2021-01-07 14:50:58.257', '2021-01-07 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + MustExec(t, se, `insert into mysql.bind_info values('select * from t4', 'select /*+ use_index(t4, idx_a)*/ * from t4', 'test', 'invalid', '2021-01-08 14:50:58.257', '2021-01-08 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) + upgradeToVer85(se, version84) + + r := MustExecToRecodeSet(t, se, `select count(*) from mysql.bind_info where status = 'enabled'`) + req := r.NewChunk(nil) + require.NoError(t, r.Next(ctx, req)) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, int64(2), row.GetInt64(0)) + + require.NoError(t, r.Close()) + MustExec(t, se, "delete from mysql.bind_info where default_db = 'test'") +} + +func TestTiDBEnablePagingVariable(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + se := CreateSessionAndSetID(t, store) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + + for _, sql := range []string{ + "select @@global.tidb_enable_paging", + "select @@session.tidb_enable_paging", + } { + r := MustExecToRecodeSet(t, se, sql) + require.NotNil(t, r) + + req := r.NewChunk(nil) + err := r.Next(context.Background(), req) + require.NoError(t, err) + require.NotEqual(t, 0, req.NumRows()) + + rows := statistics.RowToDatums(req.GetRow(0), r.Fields()) + if variable.DefTiDBEnablePaging { + match(t, rows, "1") + } else { + match(t, rows, "0") + } + r.Close() + } +} + +func TestTiDBOptRangeMaxSizeWhenUpgrading(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // Upgrade from v6.3.0 to v6.4.0+. + ver94 := 94 + seV630 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver94)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV630, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver94)) + MustExec(t, seV630, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptRangeMaxSize)) + MustExec(t, seV630, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV630) + require.NoError(t, err) + require.Equal(t, int64(ver94), ver) + + // We are now in 6.3.0, check tidb_opt_range_max_size should not exist. + res := MustExecToRecodeSet(t, seV630, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptRangeMaxSize)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + dom.Close() + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in version no lower than v6.4.0, tidb_opt_range_max_size should be 0. + res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_opt_range_max_size") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, "0", row.GetString(0)) + + res = MustExecToRecodeSet(t, seCurVer, "select @@global.tidb_opt_range_max_size") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, "0", row.GetString(0)) +} + +func TestTiDBOptAdvancedJoinHintWhenUpgrading(t *testing.T) { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // Upgrade from v6.6.0 to v7.0.0+. + ver134 := 134 + seV660 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver134)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV660, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver134)) + MustExec(t, seV660, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptAdvancedJoinHint)) + MustExec(t, seV660, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV660) + require.NoError(t, err) + require.Equal(t, int64(ver134), ver) + + // We are now in 6.6.0, check tidb_opt_advanced_join_hint should not exist. + res := MustExecToRecodeSet(t, seV660, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptAdvancedJoinHint)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + dom.Close() + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in version no lower than v7.0.0, tidb_opt_advanced_join_hint should be false. + res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_opt_advanced_join_hint;") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, int64(0), row.GetInt64(0)) + + res = MustExecToRecodeSet(t, seCurVer, "select @@global.tidb_opt_advanced_join_hint;") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, int64(0), row.GetInt64(0)) +} + +func TestTiDBOptAdvancedJoinHintInNewCluster(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + // Indicates we are in a new cluster. + require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) + dom, err := BootstrapSession(store) + require.NoError(t, err) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + // In a new created cluster(above 7.0+), tidb_opt_advanced_join_hint is true by default. + MustExec(t, se, "use test;") + r := MustExecToRecodeSet(t, se, "select @@tidb_opt_advanced_join_hint;") + require.NotNil(t, r) + + ctx := context.Background() + chk := r.NewChunk(nil) + err = r.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, int64(1), row.GetInt64(0)) +} + +func TestTiDBCostModelInNewCluster(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + // Indicates we are in a new cluster. + require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) + dom, err := BootstrapSession(store) + require.NoError(t, err) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + se := CreateSessionAndSetID(t, store) + + // In a new created cluster(above 6.5+), tidb_cost_model_version is 2 by default. + MustExec(t, se, "use test;") + r := MustExecToRecodeSet(t, se, "select @@tidb_cost_model_version;") + require.NotNil(t, r) + + ctx := context.Background() + chk := r.NewChunk(nil) + err = r.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, "2", row.GetString(0)) +} + +func TestTiDBCostModelUpgradeFrom300To650(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // Upgrade from 3.0.0 to 6.5+. + ver300 := 33 + seV3 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver300)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) + MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) + MustExec(t, seV3, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV3) + require.NoError(t, err) + require.Equal(t, int64(ver300), ver) + + // We are now in 3.0.0, check TiDBCostModelVersion should not exist. + res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5+, TiDBCostModelVersion should be 1. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_cost_model_version") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 1, row.Len()) + require.Equal(t, "1", row.GetString(0)) +} + +func TestTiDBCostModelUpgradeFrom610To650(t *testing.T) { + for i := 0; i < 2; i++ { + func() { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.1 to 6.5+. + ver61 := 91 + seV61 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver61)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV61, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver61)) + MustExec(t, seV61, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "1", variable.TiDBCostModelVersion)) + MustExec(t, seV61, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV61) + require.NoError(t, err) + require.Equal(t, int64(ver61), ver) + + // We are now in 6.1, tidb_cost_model_version is 1. + res := MustExecToRecodeSet(t, seV61, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "1", row.GetString(1)) + res.Close() + + if i == 0 { + // For the first time, We set tidb_cost_model_version to 2. + // And after upgrade to 6.5, tidb_cost_model_version should be 2. + // For the second it should be 1. + MustExec(t, seV61, "set global tidb_cost_model_version = 2") + } + dom.Close() + // Upgrade to 6.5. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_cost_model_version") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 1, row.Len()) + if i == 0 { + require.Equal(t, "2", row.GetString(0)) + } else { + require.Equal(t, "1", row.GetString(0)) + } + res.Close() + }() + } +} + +func TestTiDBGCAwareUpgradeFrom630To650(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.3 to 6.5+. + ver63 := version93 + seV63 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver63)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV63, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver63)) + MustExec(t, seV63, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "1", variable.TiDBEnableGCAwareMemoryTrack)) + MustExec(t, seV63, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV63) + require.NoError(t, err) + require.Equal(t, int64(ver63), ver) + + // We are now in 6.3, tidb_enable_gc_aware_memory_track is ON. + res := MustExecToRecodeSet(t, seV63, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableGCAwareMemoryTrack)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "1", row.GetString(1)) + + // Upgrade to 6.5. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5. + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableGCAwareMemoryTrack)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "0", row.GetString(1)) +} + +func TestTiDBServerMemoryLimitUpgradeTo651_1(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.5.0 to 6.5.1+. + ver132 := version132 + seV132 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver132)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV132, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver132)) + MustExec(t, seV132, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBServerMemoryLimit)) + MustExec(t, seV132, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV132) + require.NoError(t, err) + require.Equal(t, int64(ver132), ver) + + // We are now in 6.5.0, tidb_server_memory_limit is 0. + res := MustExecToRecodeSet(t, seV132, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "0", row.GetString(1)) + + // Upgrade to 6.5.1+. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5.1+. + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, variable.DefTiDBServerMemoryLimit, row.GetString(1)) +} + +func TestTiDBServerMemoryLimitUpgradeTo651_2(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.5.0 to 6.5.1+. + ver132 := version132 + seV132 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver132)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV132, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver132)) + MustExec(t, seV132, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "70%", variable.TiDBServerMemoryLimit)) + MustExec(t, seV132, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV132) + require.NoError(t, err) + require.Equal(t, int64(ver132), ver) + + // We are now in 6.5.0, tidb_server_memory_limit is "70%". + res := MustExecToRecodeSet(t, seV132, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "70%", row.GetString(1)) + + // Upgrade to 6.5.1+. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5.1+. + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "70%", row.GetString(1)) +} + +func TestTiDBGlobalVariablesDefaultValueUpgradeFrom630To660(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.3.0 to 6.6.0. + ver630 := version93 + seV630 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver630)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV630, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver630)) + MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnableForeignKey)) + MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.ForeignKeyChecks)) + MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnableHistoricalStats)) + MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnablePlanReplayerCapture)) + MustExec(t, seV630, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV630) + require.NoError(t, err) + require.Equal(t, int64(ver630), ver) + + // We are now in 6.3.0. + upgradeVars := []string{variable.TiDBEnableForeignKey, variable.ForeignKeyChecks, variable.TiDBEnableHistoricalStats, variable.TiDBEnablePlanReplayerCapture} + varsValueList := []string{"OFF", "OFF", "OFF", "OFF"} + for i := range upgradeVars { + res := MustExecToRecodeSet(t, seV630, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", upgradeVars[i])) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, varsValueList[i], row.GetString(1)) + } + + // Upgrade to 6.6.0. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seV660 := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seV660) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.6.0. + varsValueList = []string{"ON", "ON", "ON", "ON"} + for i := range upgradeVars { + res := MustExecToRecodeSet(t, seV660, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", upgradeVars[i])) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, varsValueList[i], row.GetString(1)) + } +} + +func TestTiDBStoreBatchSizeUpgradeFrom650To660(t *testing.T) { + for i := 0; i < 2; i++ { + func() { + ctx := context.Background() + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.5 to 6.6. + ver65 := version132 + seV65 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver65)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV65, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver65)) + MustExec(t, seV65, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBStoreBatchSize)) + MustExec(t, seV65, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV65) + require.NoError(t, err) + require.Equal(t, int64(ver65), ver) + + // We are now in 6.5, tidb_store_batch_size is 0. + res := MustExecToRecodeSet(t, seV65, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStoreBatchSize)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "0", row.GetString(1)) + res.Close() + + if i == 0 { + // For the first time, We set tidb_store_batch_size to 1. + // And after upgrade to 6.6, tidb_store_batch_size should be 1. + // For the second it should be the latest default value. + MustExec(t, seV65, "set global tidb_store_batch_size = 1") + } + dom.Close() + // Upgrade to 6.6. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.6. + res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_store_batch_size") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 1, row.Len()) + if i == 0 { + require.Equal(t, "1", row.GetString(0)) + } else { + require.Equal(t, "4", row.GetString(0)) + } + res.Close() + }() + } +} + +func TestTiDBUpgradeToVer136(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + + ver135 := version135 + seV135 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver135)) + require.NoError(t, err) + MustExec(t, seV135, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver135)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV135) + require.NoError(t, err) + require.Equal(t, int64(ver135), ver) + + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(seV135) + require.NoError(t, err) + require.Less(t, int64(ver135), ver) + dom.Close() +} + +func TestTiDBUpgradeToVer140(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + + ver139 := version139 + resetTo139 := func(s Session) { + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver139)) + require.NoError(t, err) + MustExec(t, s, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver139)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(s) + require.NoError(t, err) + require.Equal(t, int64(ver139), ver) + } + + // drop column task_key and then upgrade + s := CreateSessionAndSetID(t, store) + MustExec(t, s, "alter table mysql.tidb_global_task drop column task_key") + resetTo139(s) + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err := getBootstrapVersion(s) + require.NoError(t, err) + require.Less(t, int64(ver139), ver) + dom.Close() + + // upgrade with column task_key exists + s = CreateSessionAndSetID(t, store) + resetTo139(s) + dom, err = BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(s) + require.NoError(t, err) + require.Less(t, int64(ver139), ver) + dom.Close() +} + +func TestTiDBNonPrepPlanCacheUpgradeFrom540To700(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // bootstrap to 5.4 + ver54 := version82 + seV54 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver54)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV54, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver54)) + MustExec(t, seV54, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) + MustExec(t, seV54, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV54) + require.NoError(t, err) + require.Equal(t, int64(ver54), ver) + + // We are now in 5.4, check TiDBCostModelVersion should not exist. + res := MustExecToRecodeSet(t, seV54, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 0, chk.NumRows()) + + // Upgrade to 7.0 + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 7.0 + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "OFF", row.GetString(1)) // tidb_enable_non_prepared_plan_cache = off + + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBNonPreparedPlanCacheSize)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "100", row.GetString(1)) // tidb_non_prepared_plan_cache_size = 100 +} + +func TestTiDBStatsLoadPseudoTimeoutUpgradeFrom610To650(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 6.1 to 6.5+. + ver61 := version91 + seV61 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver61)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV61, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver61)) + MustExec(t, seV61, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBStatsLoadPseudoTimeout)) + MustExec(t, seV61, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV61) + require.NoError(t, err) + require.Equal(t, int64(ver61), ver) + + // We are now in 6.1, tidb_stats_load_pseudo_timeout is OFF. + res := MustExecToRecodeSet(t, seV61, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStatsLoadPseudoTimeout)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "0", row.GetString(1)) + + // Upgrade to 6.5. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 6.5. + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStatsLoadPseudoTimeout)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "1", row.GetString(1)) +} + +func TestTiDBTiDBOptTiDBOptimizerEnableNAAJWhenUpgradingToVer138(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + ver137 := version137 + seV137 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver137)) + require.NoError(t, err) + MustExec(t, seV137, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver137)) + MustExec(t, seV137, "update mysql.GLOBAL_VARIABLES set variable_value='OFF' where variable_name='tidb_enable_null_aware_anti_join'") + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV137) + require.NoError(t, err) + require.Equal(t, int64(ver137), ver) + + res := MustExecToRecodeSet(t, seV137, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_enable_null_aware_anti_join'") + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "OFF", row.GetString(1)) + + // Upgrade to version 138. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + res = MustExecToRecodeSet(t, seCurVer, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_enable_null_aware_anti_join'") + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "ON", row.GetString(1)) +} + +func TestTiDBUpgradeToVer143(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + + ver142 := version142 + seV142 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver142)) + require.NoError(t, err) + MustExec(t, seV142, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver142)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV142) + require.NoError(t, err) + require.Equal(t, int64(ver142), ver) + + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(seV142) + require.NoError(t, err) + require.Less(t, int64(ver142), ver) + dom.Close() +} + +func TestTiDBLoadBasedReplicaReadThresholdUpgradingToVer141(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // upgrade from 7.0 to 7.1. + ver70 := version139 + seV70 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver70)) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) + MustExec(t, seV70, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver70)) + MustExec(t, seV70, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBLoadBasedReplicaReadThreshold)) + MustExec(t, seV70, "commit") + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV70) + require.NoError(t, err) + require.Equal(t, int64(ver70), ver) + + // We are now in 7.0, tidb_load_based_replica_read_threshold is 0. + res := MustExecToRecodeSet(t, seV70, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBLoadBasedReplicaReadThreshold)) + chk := res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "0", row.GetString(1)) + + // Upgrade to 7.1. + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err = getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // We are now in 7.1. + res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBLoadBasedReplicaReadThreshold)) + chk = res.NewChunk(nil) + err = res.Next(ctx, chk) + require.NoError(t, err) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, 2, row.Len()) + require.Equal(t, "1s", row.GetString(1)) +} + +func TestTiDBPlanCacheInvalidationOnFreshStatsWhenUpgradingToVer144(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // bootstrap as version143 + ver143 := version143 + seV143 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver143)) + require.NoError(t, err) + MustExec(t, seV143, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver143)) + // simulate a real ver143 where `tidb_plan_cache_invalidation_on_fresh_stats` doesn't exist yet + MustExec(t, seV143, "delete from mysql.GLOBAL_VARIABLES where variable_name='tidb_plan_cache_invalidation_on_fresh_stats'") + err = txn.Commit(context.Background()) + require.NoError(t, err) + unsetStoreBootstrapped(store.UUID()) + + // upgrade to ver144 + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err := getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // the value in the table is set to OFF automatically + res := MustExecToRecodeSet(t, seCurVer, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_plan_cache_invalidation_on_fresh_stats'") + chk := res.NewChunk(nil) + require.NoError(t, res.Next(ctx, chk)) + require.Equal(t, 1, chk.NumRows()) + row := chk.GetRow(0) + require.Equal(t, "OFF", row.GetString(1)) + + // the session and global variable is also OFF + res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_plan_cache_invalidation_on_fresh_stats, @@global.tidb_plan_cache_invalidation_on_fresh_stats") + chk = res.NewChunk(nil) + require.NoError(t, res.Next(ctx, chk)) + require.Equal(t, 1, chk.NumRows()) + row = chk.GetRow(0) + require.Equal(t, int64(0), row.GetInt64(0)) + require.Equal(t, int64(0), row.GetInt64(1)) +} + +func TestTiDBUpgradeToVer145(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + + ver144 := version144 + seV144 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver144)) + require.NoError(t, err) + MustExec(t, seV144, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver144)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV144) + require.NoError(t, err) + require.Equal(t, int64(ver144), ver) + + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(seV144) + require.NoError(t, err) + require.Less(t, int64(ver144), ver) + dom.Close() +} + +func TestTiDBUpgradeToVer170(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + ver169 := version169 + seV169 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver169)) + require.NoError(t, err) + MustExec(t, seV169, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver169)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV169) + require.NoError(t, err) + require.Equal(t, int64(ver169), ver) + + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(seV169) + require.NoError(t, err) + require.Less(t, int64(ver169), ver) + dom.Close() +} + +func TestTiDBBindingInListToVer175(t *testing.T) { + ctx := context.Background() + store, _ := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + + // bootstrap as version174 + ver174 := version174 + seV174 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver174)) + require.NoError(t, err) + MustExec(t, seV174, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver174)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + unsetStoreBootstrapped(store.UUID()) + + // create some bindings at version174 + MustExec(t, seV174, "use test") + MustExec(t, seV174, "create table t (a int, b int, c int, key(c))") + MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ... )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1,2,3)', 'test', 'enabled', '2023-09-13 14:41:38.319', '2023-09-13 14:41:35.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") + MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ? )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1)', 'test', 'enabled', '2023-09-13 14:41:38.319', '2023-09-13 14:41:36.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") + MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ? ) and `b` in ( ... )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3)', 'test', 'enabled', '2023-09-13 14:41:37.319', '2023-09-13 14:41:38.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") + + showBindings := func(s Session) (records []string) { + MustExec(t, s, "admin reload bindings") + res := MustExecToRecodeSet(t, s, "show global bindings") + chk := res.NewChunk(nil) + for { + require.NoError(t, res.Next(ctx, chk)) + if chk.NumRows() == 0 { + break + } + for i := 0; i < chk.NumRows(); i++ { + originalSQL := chk.GetRow(i).GetString(0) + bindSQL := chk.GetRow(i).GetString(1) + records = append(records, fmt.Sprintf("%s:%s", bindSQL, originalSQL)) + } + } + require.NoError(t, res.Close()) + sort.Strings(records) + return + } + bindings := showBindings(seV174) + // on ver174, `in (1)` and `in (1,2,3)` have different normalized results: `in (?)` and `in (...)` + require.Equal(t, []string{"SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3):select * from `test` . `t` where `a` in ( ? ) and `b` in ( ... )", + "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1):select * from `test` . `t` where `a` in ( ? )", + "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1,2,3):select * from `test` . `t` where `a` in ( ... )"}, bindings) + + // upgrade to ver175 + domCurVer, err := BootstrapSession(store) + require.NoError(t, err) + defer domCurVer.Close() + seCurVer := CreateSessionAndSetID(t, store) + ver, err := getBootstrapVersion(seCurVer) + require.NoError(t, err) + require.Equal(t, currentBootstrapVersion, ver) + + // `in (?)` becomes to `in ( ... )` + bindings = showBindings(seCurVer) + require.Equal(t, []string{"SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3):select * from `test` . `t` where `a` in ( ... ) and `b` in ( ... )", + "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1):select * from `test` . `t` where `a` in ( ... )"}, bindings) + + planFromBinding := func(s Session, q string) { + MustExec(t, s, q) + res := MustExecToRecodeSet(t, s, "select @@last_plan_from_binding") + chk := res.NewChunk(nil) + require.NoError(t, res.Next(ctx, chk)) + require.Equal(t, int64(1), chk.GetRow(0).GetInt64(0)) + require.NoError(t, res.Close()) + } + planFromBinding(seCurVer, "select * from test.t where a in (1)") + planFromBinding(seCurVer, "select * from test.t where a in (1,2,3)") + planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7)") + planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7) and b in(1)") + planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7) and b in(1,2,3,4)") + planFromBinding(seCurVer, "select * from test.t where a in (7) and b in(1,2,3,4)") +} + +func TestTiDBUpgradeToVer176(t *testing.T) { + store, _ := CreateStoreAndBootstrap(t) + defer func() { + require.NoError(t, store.Close()) + }() + ver175 := version175 + seV175 := CreateSessionAndSetID(t, store) + txn, err := store.Begin() + require.NoError(t, err) + m := meta.NewMeta(txn) + err = m.FinishBootstrap(int64(ver175)) + require.NoError(t, err) + MustExec(t, seV175, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver175)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + unsetStoreBootstrapped(store.UUID()) + ver, err := getBootstrapVersion(seV175) + require.NoError(t, err) + require.Equal(t, int64(ver175), ver) + + dom, err := BootstrapSession(store) + require.NoError(t, err) + ver, err = getBootstrapVersion(seV175) + require.NoError(t, err) + require.Less(t, int64(ver175), ver) + dom.Close() +} diff --git a/pkg/session/bootstraptest/BUILD.bazel b/pkg/session/bootstraptest/BUILD.bazel new file mode 100644 index 0000000000000..67477675738cf --- /dev/null +++ b/pkg/session/bootstraptest/BUILD.bazel @@ -0,0 +1,36 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "bootstraptest_test", + timeout = "short", + srcs = [ + "bootstrap_upgrade_test.go", #keep + "main_test.go", + ], + flaky = True, + shard_count = 11, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/util/callback", + "//pkg/kv", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/server/handler", + "//pkg/session", #keep + "//pkg/sessionctx", + "//pkg/testkit", #keep + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", #keep + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/session/bootstraptest/bootstrap_upgrade_test.go b/pkg/session/bootstraptest/bootstrap_upgrade_test.go similarity index 97% rename from session/bootstraptest/bootstrap_upgrade_test.go rename to pkg/session/bootstraptest/bootstrap_upgrade_test.go index 6dca6a0d61647..d4f681d4f58a1 100644 --- a/session/bootstraptest/bootstrap_upgrade_test.go +++ b/pkg/session/bootstraptest/bootstrap_upgrade_test.go @@ -25,19 +25,19 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - tidb_util "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server/handler" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + tidb_util "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" atomicutil "go.uber.org/atomic" ) @@ -540,8 +540,8 @@ func TestUpgradeVersionForResumeJob(t *testing.T) { require.NoError(t, err) require.Equal(t, session.CurrentBootstrapVersion-1, ver) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockResumeAllJobsFailed", `return(true)`)) - defer failpoint.Disable("github.com/pingcap/tidb/session/mockResumeAllJobsFailed") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockResumeAllJobsFailed", `return(true)`)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockResumeAllJobsFailed") // Add a paused DDL job before upgrade. session.MustExec(t, seV, "create table test.upgrade_tbl(a int, b int)") diff --git a/pkg/session/bootstraptest/main_test.go b/pkg/session/bootstraptest/main_test.go new file mode 100644 index 0000000000000..3b96891b56af6 --- /dev/null +++ b/pkg/session/bootstraptest/main_test.go @@ -0,0 +1,63 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package bootstraptest + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlDeleteWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/clusteredindextest/BUILD.bazel b/pkg/session/clusteredindextest/BUILD.bazel new file mode 100644 index 0000000000000..a1d61c12e84be --- /dev/null +++ b/pkg/session/clusteredindextest/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "clusteredindextest_test", + timeout = "short", + srcs = [ + "clustered_index_test.go", + "main_test.go", + ], + data = glob(["testdata/**"]), + flaky = True, + race = "on", + shard_count = 3, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/session/clusteredindextest/clustered_index_test.go b/pkg/session/clusteredindextest/clustered_index_test.go similarity index 98% rename from session/clusteredindextest/clustered_index_test.go rename to pkg/session/clusteredindextest/clustered_index_test.go index e68380142ea13..8eb3e9dc2d4b5 100644 --- a/session/clusteredindextest/clustered_index_test.go +++ b/pkg/session/clusteredindextest/clustered_index_test.go @@ -20,9 +20,9 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/session/clusteredindextest/main_test.go b/pkg/session/clusteredindextest/main_test.go new file mode 100644 index 0000000000000..8cb8bd03d5f15 --- /dev/null +++ b/pkg/session/clusteredindextest/main_test.go @@ -0,0 +1,65 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package clusteredindextest + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + + session.SetSchemaLease(20 * time.Millisecond) + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/session/index_usage_sync_lease_test.go b/pkg/session/index_usage_sync_lease_test.go similarity index 100% rename from session/index_usage_sync_lease_test.go rename to pkg/session/index_usage_sync_lease_test.go diff --git a/pkg/session/main_test.go b/pkg/session/main_test.go new file mode 100644 index 0000000000000..24e62127f0e75 --- /dev/null +++ b/pkg/session/main_test.go @@ -0,0 +1,80 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package session + +import ( + "flag" + "fmt" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + + SetSchemaLease(20 * time.Millisecond) + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func match(t *testing.T, row []types.Datum, expected ...interface{}) { + require.Len(t, row, len(expected)) + for i := range row { + if _, ok := expected[i].(time.Time); ok { + // Since password_last_changed is set to default current_timestamp, we pass this check. + continue + } + got := fmt.Sprintf("%v", row[i].GetValue()) + need := fmt.Sprintf("%v", expected[i]) + require.Equal(t, need, got, i) + } +} diff --git a/pkg/session/metrics/BUILD.bazel b/pkg/session/metrics/BUILD.bazel new file mode 100644 index 0000000000000..78cf4465a8c4b --- /dev/null +++ b/pkg/session/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/session/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/session/metrics/metrics.go b/pkg/session/metrics/metrics.go new file mode 100644 index 0000000000000..31aa904291bce --- /dev/null +++ b/pkg/session/metrics/metrics.go @@ -0,0 +1,146 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// session metrics vars +var ( + NonTransactionalDeleteCount prometheus.Counter + NonTransactionalInsertCount prometheus.Counter + NonTransactionalUpdateCount prometheus.Counter + + StatementPerTransactionPessimisticOKInternal prometheus.Observer + StatementPerTransactionPessimisticOKGeneral prometheus.Observer + StatementPerTransactionPessimisticErrorInternal prometheus.Observer + StatementPerTransactionPessimisticErrorGeneral prometheus.Observer + StatementPerTransactionOptimisticOKInternal prometheus.Observer + StatementPerTransactionOptimisticOKGeneral prometheus.Observer + StatementPerTransactionOptimisticErrorInternal prometheus.Observer + StatementPerTransactionOptimisticErrorGeneral prometheus.Observer + TransactionDurationPessimisticCommitInternal prometheus.Observer + TransactionDurationPessimisticCommitGeneral prometheus.Observer + TransactionDurationPessimisticAbortInternal prometheus.Observer + TransactionDurationPessimisticAbortGeneral prometheus.Observer + TransactionDurationOptimisticCommitInternal prometheus.Observer + TransactionDurationOptimisticCommitGeneral prometheus.Observer + TransactionDurationOptimisticAbortInternal prometheus.Observer + TransactionDurationOptimisticAbortGeneral prometheus.Observer + TransactionRetryInternal prometheus.Observer + TransactionRetryGeneral prometheus.Observer + + SessionExecuteCompileDurationInternal prometheus.Observer + SessionExecuteCompileDurationGeneral prometheus.Observer + SessionExecuteParseDurationInternal prometheus.Observer + SessionExecuteParseDurationGeneral prometheus.Observer + + TelemetryCTEUsageRecurCTE prometheus.Counter + TelemetryCTEUsageNonRecurCTE prometheus.Counter + TelemetryCTEUsageNotCTE prometheus.Counter + TelemetryMultiSchemaChangeUsage prometheus.Counter + TelemetryFlashbackClusterUsage prometheus.Counter + + TelemetryTablePartitionUsage prometheus.Counter + TelemetryTablePartitionListUsage prometheus.Counter + TelemetryTablePartitionRangeUsage prometheus.Counter + TelemetryTablePartitionHashUsage prometheus.Counter + TelemetryTablePartitionRangeColumnsUsage prometheus.Counter + TelemetryTablePartitionRangeColumnsGt1Usage prometheus.Counter + TelemetryTablePartitionRangeColumnsGt2Usage prometheus.Counter + TelemetryTablePartitionRangeColumnsGt3Usage prometheus.Counter + TelemetryTablePartitionListColumnsUsage prometheus.Counter + TelemetryTablePartitionMaxPartitionsUsage prometheus.Counter + TelemetryTablePartitionCreateIntervalUsage prometheus.Counter + TelemetryTablePartitionAddIntervalUsage prometheus.Counter + TelemetryTablePartitionDropIntervalUsage prometheus.Counter + TelemetryExchangePartitionUsage prometheus.Counter + TelemetryTableCompactPartitionUsage prometheus.Counter + TelemetryReorganizePartitionUsage prometheus.Counter + + TelemetryLockUserUsage prometheus.Counter + TelemetryUnlockUserUsage prometheus.Counter + TelemetryCreateOrAlterUserUsage prometheus.Counter + + TelemetryIndexMerge prometheus.Counter + TelemetryStoreBatchedUsage prometheus.Counter +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init session metrics vars. +func InitMetricsVars() { + NonTransactionalDeleteCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "delete"}) + NonTransactionalInsertCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "insert"}) + NonTransactionalUpdateCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "update"}) + + StatementPerTransactionPessimisticOKInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblOK, metrics.LblInternal) + StatementPerTransactionPessimisticOKGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblOK, metrics.LblGeneral) + StatementPerTransactionPessimisticErrorInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblError, metrics.LblInternal) + StatementPerTransactionPessimisticErrorGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblError, metrics.LblGeneral) + StatementPerTransactionOptimisticOKInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblOK, metrics.LblInternal) + StatementPerTransactionOptimisticOKGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblOK, metrics.LblGeneral) + StatementPerTransactionOptimisticErrorInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblError, metrics.LblInternal) + StatementPerTransactionOptimisticErrorGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblError, metrics.LblGeneral) + TransactionDurationPessimisticCommitInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblCommit, metrics.LblInternal) + TransactionDurationPessimisticCommitGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblCommit, metrics.LblGeneral) + TransactionDurationPessimisticAbortInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblAbort, metrics.LblInternal) + TransactionDurationPessimisticAbortGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblAbort, metrics.LblGeneral) + TransactionDurationOptimisticCommitInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblCommit, metrics.LblInternal) + TransactionDurationOptimisticCommitGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblCommit, metrics.LblGeneral) + TransactionDurationOptimisticAbortInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblAbort, metrics.LblInternal) + TransactionDurationOptimisticAbortGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblAbort, metrics.LblGeneral) + TransactionRetryInternal = metrics.SessionRetry.WithLabelValues(metrics.LblInternal) + TransactionRetryGeneral = metrics.SessionRetry.WithLabelValues(metrics.LblGeneral) + + SessionExecuteCompileDurationInternal = metrics.SessionExecuteCompileDuration.WithLabelValues(metrics.LblInternal) + SessionExecuteCompileDurationGeneral = metrics.SessionExecuteCompileDuration.WithLabelValues(metrics.LblGeneral) + SessionExecuteParseDurationInternal = metrics.SessionExecuteParseDuration.WithLabelValues(metrics.LblInternal) + SessionExecuteParseDurationGeneral = metrics.SessionExecuteParseDuration.WithLabelValues(metrics.LblGeneral) + + TelemetryCTEUsageRecurCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("recurCTE") + TelemetryCTEUsageNonRecurCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("nonRecurCTE") + TelemetryCTEUsageNotCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("notCTE") + TelemetryMultiSchemaChangeUsage = metrics.TelemetryMultiSchemaChangeCnt + TelemetryFlashbackClusterUsage = metrics.TelemetryFlashbackClusterCnt + + TelemetryTablePartitionUsage = metrics.TelemetryTablePartitionCnt + TelemetryTablePartitionListUsage = metrics.TelemetryTablePartitionListCnt + TelemetryTablePartitionRangeUsage = metrics.TelemetryTablePartitionRangeCnt + TelemetryTablePartitionHashUsage = metrics.TelemetryTablePartitionHashCnt + TelemetryTablePartitionRangeColumnsUsage = metrics.TelemetryTablePartitionRangeColumnsCnt + TelemetryTablePartitionRangeColumnsGt1Usage = metrics.TelemetryTablePartitionRangeColumnsGt1Cnt + TelemetryTablePartitionRangeColumnsGt2Usage = metrics.TelemetryTablePartitionRangeColumnsGt2Cnt + TelemetryTablePartitionRangeColumnsGt3Usage = metrics.TelemetryTablePartitionRangeColumnsGt3Cnt + TelemetryTablePartitionListColumnsUsage = metrics.TelemetryTablePartitionListColumnsCnt + TelemetryTablePartitionMaxPartitionsUsage = metrics.TelemetryTablePartitionMaxPartitionsCnt + TelemetryTablePartitionCreateIntervalUsage = metrics.TelemetryTablePartitionCreateIntervalPartitionsCnt + TelemetryTablePartitionAddIntervalUsage = metrics.TelemetryTablePartitionAddIntervalPartitionsCnt + TelemetryTablePartitionDropIntervalUsage = metrics.TelemetryTablePartitionDropIntervalPartitionsCnt + TelemetryExchangePartitionUsage = metrics.TelemetryExchangePartitionCnt + TelemetryTableCompactPartitionUsage = metrics.TelemetryCompactPartitionCnt + TelemetryReorganizePartitionUsage = metrics.TelemetryReorganizePartitionCnt + + TelemetryLockUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("lockUser") + TelemetryUnlockUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("unlockUser") + TelemetryCreateOrAlterUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("createOrAlterUser") + + TelemetryIndexMerge = metrics.TelemetryIndexMergeUsage + TelemetryStoreBatchedUsage = metrics.TelemetryStoreBatchedQueryCnt +} diff --git a/session/mock_bootstrap.go b/pkg/session/mock_bootstrap.go similarity index 99% rename from session/mock_bootstrap.go rename to pkg/session/mock_bootstrap.go index 7f2940650cb6d..f693304acccbf 100644 --- a/session/mock_bootstrap.go +++ b/pkg/session/mock_bootstrap.go @@ -22,7 +22,7 @@ import ( "flag" "time" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" atomicutil "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/session/nontransactional.go b/pkg/session/nontransactional.go similarity index 96% rename from session/nontransactional.go rename to pkg/session/nontransactional.go index dc034827b8d62..05cedbb096f34 100644 --- a/session/nontransactional.go +++ b/pkg/session/nontransactional.go @@ -22,27 +22,27 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/planner/core" - session_metrics "github.com/pingcap/tidb/session/metrics" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/planner/core" + session_metrics "github.com/pingcap/tidb/pkg/session/metrics" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/pkg/session/nontransactionaltest/BUILD.bazel b/pkg/session/nontransactionaltest/BUILD.bazel new file mode 100644 index 0000000000000..49b0ba9a0dc8a --- /dev/null +++ b/pkg/session/nontransactionaltest/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "nontransactionaltest_test", + timeout = "short", + srcs = [ + "main_test.go", + "nontransactional_test.go", + ], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/config", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/nontransactionaltest/main_test.go b/pkg/session/nontransactionaltest/main_test.go new file mode 100644 index 0000000000000..b7eb2b041b8b5 --- /dev/null +++ b/pkg/session/nontransactionaltest/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package nontransactionaltest + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/session/nontransactionaltest/nontransactional_test.go b/pkg/session/nontransactionaltest/nontransactional_test.go similarity index 92% rename from session/nontransactionaltest/nontransactional_test.go rename to pkg/session/nontransactionaltest/nontransactional_test.go index 1c741897317a4..1247d6e30e92f 100644 --- a/session/nontransactionaltest/nontransactional_test.go +++ b/pkg/session/nontransactionaltest/nontransactional_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" tikvutil "github.com/tikv/client-go/v2/util" ) @@ -132,8 +132,8 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { tk.MustExec(fmt.Sprintf("insert into t values ('%d', %d)", i, i*2)) } tk.MustExec("set @@tidb_nontransactional_ignore_error=1") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `return(true)`)) - defer failpoint.Disable("github.com/pingcap/tidb/session/batchDMLError") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `return(true)`)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/session/batchDMLError") err := tk.ExecToErr("batch on a limit 3 insert into t1 select * from t") require.EqualError( t, err, @@ -163,7 +163,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { tk.MustExec("set @@tidb_nontransactional_ignore_error=1") require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 insert into t1 select * from t") require.ErrorContains( @@ -171,7 +171,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { "33/34 jobs failed in the non-transactional DML: job id: 2, estimated size: 3, sql: INSERT INTO `test`.`t1` SELECT * FROM `test`.`t` WHERE `a` BETWEEN 3 AND 5, injected batch(non-transactional) DML error;\n", ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 insert into t1 select * from t on duplicate key update t1.b=t.b") require.ErrorContains( @@ -180,7 +180,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 update t set b = 42") require.ErrorContains( @@ -189,7 +189,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 delete from t") require.ErrorContains( @@ -205,7 +205,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { tk.MustExec("set @@tidb_nontransactional_ignore_error=0") require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 insert into t1 select * from t") require.EqualError( @@ -214,7 +214,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 insert into t1 select * from t on duplicate key update t1.b=t.b") require.EqualError( @@ -223,7 +223,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 update t set b = b + 42") require.EqualError( @@ -232,7 +232,7 @@ func TestNonTransactionalDMLErrorMessage(t *testing.T) { ) require.NoError( - t, failpoint.Enable("github.com/pingcap/tidb/session/batchDMLError", `1*return(false)->return(true)`), + t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/batchDMLError", `1*return(false)->return(true)`), ) err = tk.ExecToErr("batch on a limit 3 delete from t") require.EqualError( diff --git a/pkg/session/resourcegrouptest/BUILD.bazel b/pkg/session/resourcegrouptest/BUILD.bazel new file mode 100644 index 0000000000000..bfc41da9ad232 --- /dev/null +++ b/pkg/session/resourcegrouptest/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "resourcegrouptest_test", + timeout = "short", + srcs = ["resource_group_test.go"], + flaky = True, + deps = [ + "//pkg/testkit", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/session/resourcegrouptest/resource_group_test.go b/pkg/session/resourcegrouptest/resource_group_test.go new file mode 100644 index 0000000000000..4f00389e00eb6 --- /dev/null +++ b/pkg/session/resourcegrouptest/resource_group_test.go @@ -0,0 +1,59 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package resourcegrouptest + +import ( + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestResourceGroupHintInTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("create resource group rg1 ru_per_sec=1000") + tk.MustExec("create resource group rg2 ru_per_sec=1000") + tk.MustExec("use test;") + tk.MustExec("create table t (id int primary key, val int)") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/TxnResouceGroupChecker", `return("default")`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/kv/TxnResouceGroupChecker")) + }() + tk.MustExec("insert into t values (1, 1);") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/TxnResouceGroupChecker", `return("rg1")`)) + tk.MustExec("insert /*+ RESOURCE_GROUP(rg1) */ into t values (2, 2);") + tk.MustExec("BEGIN;") + // for pessimistic lock the resource group should be rg1 + tk.MustExec("insert /*+ RESOURCE_GROUP(rg1) */ into t values (3, 3);") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/TxnResouceGroupChecker", `return("rg2")`)) + // for final prewrite/commit the resource group should be rg2 + tk.MustExec("update /*+ RESOURCE_GROUP(rg2) */ t set val = val + 1 where id = 3;") + tk.MustExec("COMMIT;") + + tk.MustExec("SET @@autocommit=1;") + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/kv/TxnResouceGroupChecker", `return("default")`)) + tk.MustExec("insert /*+ RESOURCE_GROUP(not_exist_group) */ into t values (4, 4);") + + tk.MustExec("BEGIN;") + // for pessimistic lock the resource group should be rg1 + tk.MustExec("insert /*+ RESOURCE_GROUP(unknown_1) */ into t values (5, 5);") + // for final prewrite/commit the resource group should be rg2 + tk.MustExec("update /*+ RESOURCE_GROUP(unknown_2) */ t set val = val + 1 where id = 5;") + tk.MustExec("COMMIT;") +} diff --git a/pkg/session/schematest/BUILD.bazel b/pkg/session/schematest/BUILD.bazel new file mode 100644 index 0000000000000..80c0b51627be6 --- /dev/null +++ b/pkg/session/schematest/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "schematest_test", + timeout = "short", + srcs = [ + "main_test.go", + "schema_test.go", + ], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/server", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/schematest/main_test.go b/pkg/session/schematest/main_test.go new file mode 100644 index 0000000000000..eed409eeab44b --- /dev/null +++ b/pkg/session/schematest/main_test.go @@ -0,0 +1,62 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package schematest + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/schematest/schema_test.go b/pkg/session/schematest/schema_test.go new file mode 100644 index 0000000000000..9190eb07204e0 --- /dev/null +++ b/pkg/session/schematest/schema_test.go @@ -0,0 +1,681 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schematest + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" +) + +func createMockStoreForSchemaTest(t *testing.T, opts ...mockstore.MockTiKVStoreOption) kv.Storage { + store, err := mockstore.NewMockStore(opts...) + require.NoError(t, err) + session.DisableStats4Test() + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + + dom.SetStatsUpdating(true) + + sv := server.CreateMockServer(t, store) + sv.SetDomain(dom) + dom.InfoSyncer().SetSessionManager(sv) + + t.Cleanup(func() { + dom.Close() + require.NoError(t, store.Close()) + }) + return store +} + +func TestPrepareStmtCommitWhenSchemaChanged(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk1.MustExec("set global tidb_enable_metadata_lock=0") + tk2.MustExec("use test") + + tk1.MustExec("create table t (a int, b int)") + tk2.MustExec("prepare stmt from 'insert into t values (?, ?)'") + tk2.MustExec("set @a = 1") + + // Commit find unrelated schema change. + tk2.MustExec("begin") + tk1.MustExec("create table t1 (id int)") + tk2.MustExec("execute stmt using @a, @a") + tk2.MustExec("commit") + + tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk2.MustExec("begin") + tk1.MustExec("alter table t drop column b") + tk2.MustExec("execute stmt using @a, @a") + err := tk2.ExecToErr("commit") + require.True(t, terror.ErrorEqual(err, plannercore.ErrWrongValueCountOnRow), fmt.Sprintf("err %v", err)) +} + +func TestCommitWhenSchemaChanged(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("create table t (a int, b int)") + + tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk2.MustExec("begin") + tk2.MustExec("insert into t values (1, 1)") + + tk1.MustExec("alter table t drop column b") + + // When tk2 commit, it will find schema already changed. + tk2.MustExec("insert into t values (4, 4)") + err := tk2.ExecToErr("commit") + require.True(t, terror.ErrorEqual(err, plannercore.ErrWrongValueCountOnRow), fmt.Sprintf("err %v", err)) +} + +func TestRetrySchemaChangeForEmptyChange(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("create table t (i int)") + tk1.MustExec("create table t1 (i int)") + tk1.MustExec("begin") + tk2.MustExec("alter table t add j int") + tk1.MustExec("select * from t for update") + tk1.MustExec("update t set i = -i") + tk1.MustExec("delete from t") + tk1.MustExec("insert into t1 values (1)") + tk1.MustExec("commit") +} + +func TestRetrySchemaChange(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("create table t (a int primary key, b int)") + tk1.MustExec("insert into t values (1, 1)") + + tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk2.MustExec("begin") + tk2.MustExec("update t set b = 5 where a = 1") + + tk1.MustExec("alter table t add index b_i (b)") + + run := false + hook := func() { + if !run { + tk1.MustExec("update t set b = 3 where a = 1") + run = true + } + } + + // In order to cover a bug that statement history is not updated during retry. + // See https://github.com/pingcap/tidb/pull/5202 + // Step1: when tk2 commit, it find schema changed and retry(). + // Step2: during retry, hook() is called, tk1 update primary key. + // Step3: tk2 continue commit in retry() meet a retryable error(write conflict), retry again. + // Step4: tk2 retry() success, if it use the stale statement, data and index will inconsistent. + fpName := "github.com/pingcap/tidb/pkg/session/preCommitHook" + require.NoError(t, failpoint.Enable(fpName, "return")) + defer func() { require.NoError(t, failpoint.Disable(fpName)) }() + + ctx := context.WithValue(context.Background(), "__preCommitHook", hook) + require.NoError(t, tk2.Session().CommitTxn(ctx)) + tk1.MustQuery("select * from t where t.b = 5").Check(testkit.Rows("1 5")) +} + +func TestRetryMissingUnionScan(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("create table t (a int primary key, b int unique, c int)") + tk1.MustExec("insert into t values (1, 1, 1)") + + tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk2.MustExec("begin") + tk2.MustExec("update t set b = 1, c = 2 where b = 2") + tk2.MustExec("update t set b = 1, c = 2 where a = 1") + + // Create a conflict to reproduces the bug that the second update statement in retry + // has a dirty table but doesn't use UnionScan. + tk1.MustExec("update t set b = 2 where a = 1") + + tk2.MustExec("commit") +} + +func TestTableReaderChunk(t *testing.T) { + // Since normally a single region mock tikv only returns one partial result we need to manually split the + // table to test multiple chunks. + var cluster testutils.Cluster + store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cluster = c + })) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table chk (a int)") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) + } + tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("chk")) + require.NoError(t, err) + tableStart := tablecodec.GenTableRecordPrefix(tbl.Meta().ID) + cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 10) + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + tk.MustExec("set tidb_init_chunk_size = 2") + defer func() { + tk.MustExec(fmt.Sprintf("set tidb_init_chunk_size = %d", variable.DefInitChunkSize)) + }() + rs, err := tk.Exec("select * from chk") + require.NoError(t, err) + defer func() { require.NoError(t, rs.Close()) }() + + req := rs.NewChunk(nil) + var count int + var numChunks int + for { + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + numRows := req.NumRows() + if numRows == 0 { + break + } + for i := 0; i < numRows; i++ { + require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) + count++ + } + numChunks++ + } + require.Equal(t, 100, count) + // FIXME: revert this result to new group value after distsql can handle initChunkSize. + require.Equal(t, 1, numChunks) +} + +func TestInsertExecChunk(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table test1(a int)") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert test1 values (%d)", i)) + } + tk.MustExec("create table test2(a int)") + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + tk.MustExec("insert into test2(a) select a from test1;") + + rs, err := tk.Exec("select * from test2") + require.NoError(t, err) + defer func() { require.NoError(t, rs.Close()) }() + var idx int + for { + req := rs.NewChunk(nil) + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + if req.NumRows() == 0 { + break + } + + for rowIdx := 0; rowIdx < req.NumRows(); rowIdx++ { + row := req.GetRow(rowIdx) + require.Equal(t, int64(idx), row.GetInt64(0)) + idx++ + } + } + require.Equal(t, 100, idx) +} + +func TestUpdateExecChunk(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table chk(a int)") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) + } + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("update chk set a = a + 100 where a = %d", i)) + } + + rs, err := tk.Exec("select * from chk") + require.NoError(t, err) + defer func() { require.NoError(t, rs.Close()) }() + var idx int + for { + req := rs.NewChunk(nil) + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + if req.NumRows() == 0 { + break + } + + for rowIdx := 0; rowIdx < req.NumRows(); rowIdx++ { + row := req.GetRow(rowIdx) + require.Equal(t, int64(idx+100), row.GetInt64(0)) + idx++ + } + } + + require.Equal(t, 100, idx) +} + +func TestDeleteExecChunk(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table chk(a int)") + + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) + } + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + + for i := 0; i < 99; i++ { + tk.MustExec(fmt.Sprintf("delete from chk where a = %d", i)) + } + + rs, err := tk.Exec("select * from chk") + require.NoError(t, err) + defer func() { require.NoError(t, rs.Close()) }() + + req := rs.NewChunk(nil) + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + + row := req.GetRow(0) + require.Equal(t, int64(99), row.GetInt64(0)) +} + +func TestDeleteMultiTableExecChunk(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table chk1(a int)") + tk.MustExec("create table chk2(a int)") + + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert chk1 values (%d)", i)) + } + + for i := 0; i < 50; i++ { + tk.MustExec(fmt.Sprintf("insert chk2 values (%d)", i)) + } + + tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) + + tk.MustExec("delete chk1, chk2 from chk1 inner join chk2 where chk1.a = chk2.a") + + rs, err := tk.Exec("select * from chk1") + require.NoError(t, err) + + var idx int + for { + req := rs.NewChunk(nil) + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + + if req.NumRows() == 0 { + break + } + + for i := 0; i < req.NumRows(); i++ { + row := req.GetRow(i) + require.Equal(t, int64(idx+50), row.GetInt64(0)) + idx++ + } + } + require.Equal(t, 50, idx) + require.NoError(t, rs.Close()) + + rs, err = tk.Exec("select * from chk2") + require.NoError(t, err) + + req := rs.NewChunk(nil) + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) + require.NoError(t, rs.Close()) +} + +func TestIndexLookUpReaderChunk(t *testing.T) { + // Since normally a single region mock tikv only returns one partial result we need to manually split the + // table to test multiple chunks. + var cluster testutils.Cluster + store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithSingleStore(c) + cluster = c + })) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists chk") + tk.MustExec("create table chk (k int unique, c int)") + for i := 0; i < 100; i++ { + tk.MustExec(fmt.Sprintf("insert chk values (%d, %d)", i, i)) + } + tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("chk")) + require.NoError(t, err) + indexStart := tablecodec.EncodeTableIndexPrefix(tbl.Meta().ID, tbl.Indices()[0].Meta().ID) + cluster.SplitKeys(indexStart, indexStart.PrefixNext(), 10) + + tk.Session().GetSessionVars().IndexLookupSize = 10 + rs, err := tk.Exec("select * from chk order by k") + require.NoError(t, err) + req := rs.NewChunk(nil) + var count int + for { + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + numRows := req.NumRows() + if numRows == 0 { + break + } + for i := 0; i < numRows; i++ { + require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) + require.Equal(t, int64(count), req.GetRow(i).GetInt64(1)) + count++ + } + } + require.Equal(t, 100, count) + require.NoError(t, rs.Close()) + + rs, err = tk.Exec("select k from chk where c < 90 order by k") + require.NoError(t, err) + req = rs.NewChunk(nil) + count = 0 + for { + err = rs.Next(context.TODO(), req) + require.NoError(t, err) + numRows := req.NumRows() + if numRows == 0 { + break + } + for i := 0; i < numRows; i++ { + require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) + count++ + } + } + require.Equal(t, 90, count) + require.NoError(t, rs.Close()) +} + +func TestTxnSize(t *testing.T) { + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists txn_size") + tk.MustExec("create table txn_size (k int , v varchar(64))") + tk.MustExec("begin") + tk.MustExec("insert txn_size values (1, 'dfaasdfsdf')") + tk.MustExec("insert txn_size values (2, 'dsdfaasdfsdf')") + tk.MustExec("insert txn_size values (3, 'abcdefghijkl')") + txn, err := tk.Session().Txn(false) + require.NoError(t, err) + require.Greater(t, txn.Size(), 0) +} + +func TestValidationRecursion(t *testing.T) { + // We have to expect that validation functions will call GlobalVarsAccessor.GetGlobalSysVar(). + // This tests for a regression where GetGlobalSysVar() can not safely call the validation + // function because it might cause infinite recursion. + // See: https://github.com/pingcap/tidb/issues/30255 + sv := variable.SysVar{Scope: variable.ScopeGlobal, Name: "mynewsysvar", Value: "test", Validation: func(vars *variable.SessionVars, normalizedValue string, originalValue string, scope variable.ScopeFlag) (string, error) { + return vars.GlobalVarsAccessor.GetGlobalSysVar("mynewsysvar") + }} + variable.RegisterSysVar(&sv) + + store := createMockStoreForSchemaTest(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + val, err := sv.Validate(tk.Session().GetSessionVars(), "test2", variable.ScopeGlobal) + require.NoError(t, err) + require.Equal(t, "test", val) +} + +func TestGlobalAndLocalTxn(t *testing.T) { + // Because the PD config of check_dev_2 test is not compatible with local/global txn yet, + // so we will skip this test for now. + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_enable_local_txn = on;") + tk.MustExec("drop table if exists t1") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + tk.MustExec("create placement policy p1 leader_constraints='[+zone=dc-1]'") + tk.MustExec("create placement policy p2 leader_constraints='[+zone=dc-2]'") + tk.MustExec(`create table t1 (c int) +PARTITION BY RANGE (c) ( + PARTITION p0 VALUES LESS THAN (100) placement policy p1, + PARTITION p1 VALUES LESS THAN (200) placement policy p2 +);`) + defer func() { + tk.MustExec("drop table if exists t1") + tk.MustExec("drop placement policy if exists p1") + tk.MustExec("drop placement policy if exists p2") + }() + + // set txn_scope to global + tk.MustExec(fmt.Sprintf("set @@session.txn_scope = '%s';", kv.GlobalTxnScope)) + result := tk.MustQuery("select @@txn_scope;") + result.Check(testkit.Rows(kv.GlobalTxnScope)) + + // test global txn auto commit + tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with global scope + result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope + require.Equal(t, 1, len(result.Rows())) + + // begin and commit with global txn scope + tk.MustExec("begin") + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().TxnCtx.TxnScope) + require.True(t, txn.Valid()) + tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with global scope + result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope + require.Equal(t, 2, len(result.Rows())) + require.True(t, txn.Valid()) + tk.MustExec("commit") + result = tk.MustQuery("select * from t1") + require.Equal(t, 2, len(result.Rows())) + + // begin and rollback with global txn scope + tk.MustExec("begin") + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().TxnCtx.TxnScope) + require.True(t, txn.Valid()) + tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with global scope + result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope + require.Equal(t, 3, len(result.Rows())) + require.True(t, txn.Valid()) + tk.MustExec("rollback") + result = tk.MustQuery("select * from t1") + require.Equal(t, 2, len(result.Rows())) + + timeBeforeWriting := time.Now() + tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with global scope + result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope + require.Equal(t, 3, len(result.Rows())) + + failpoint.Enable("tikvclient/injectTxnScope", `return("dc-1")`) + defer failpoint.Disable("tikvclient/injectTxnScope") + // set txn_scope to local + tk.MustExec("set @@session.txn_scope = 'local';") + result = tk.MustQuery("select @@txn_scope;") + result.Check(testkit.Rows("local")) + + // test local txn auto commit + tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope + result = tk.MustQuery("select * from t1 where c = 1") // point get dc-1 with dc-1 scope + require.Equal(t, 3, len(result.Rows())) + result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope + require.Equal(t, 3, len(result.Rows())) + + // begin and commit with dc-1 txn scope + tk.MustExec("begin") + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + require.True(t, txn.Valid()) + tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope + result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope + require.Equal(t, 4, len(result.Rows())) + require.True(t, txn.Valid()) + tk.MustExec("commit") + result = tk.MustQuery("select * from t1 where c < 100") + require.Equal(t, 4, len(result.Rows())) + + // begin and rollback with dc-1 txn scope + tk.MustExec("begin") + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + require.True(t, txn.Valid()) + tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope + result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope + require.Equal(t, 5, len(result.Rows())) + require.True(t, txn.Valid()) + tk.MustExec("rollback") + result = tk.MustQuery("select * from t1 where c < 100") + require.Equal(t, 4, len(result.Rows())) + + // test wrong scope local txn auto commit + _, err = tk.Exec("insert into t1 (c) values (101)") // write dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*out of txn_scope.*", err) + err = tk.ExecToErr("select * from t1 where c = 101") // point get dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + tk.MustExec("begin") + err = tk.ExecToErr("select * from t1 where c = 101") // point get dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + tk.MustExec("commit") + + // begin and commit reading & writing the data in dc-2 with dc-1 txn scope + tk.MustExec("begin") + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + require.True(t, txn.Valid()) + tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with dc-1 scope + err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + tk.MustExec("insert into t1 (c) values (99)") // write dc-1 with dc-1 scope + result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope + require.Equal(t, 5, len(result.Rows())) + require.True(t, txn.Valid()) + _, err = tk.Exec("commit") + require.Error(t, err) + require.Regexp(t, ".*out of txn_scope.*", err) + // Won't read the value 99 because the previous commit failed + result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope + require.Equal(t, 4, len(result.Rows())) + + // Stale Read will ignore the cross-dc txn scope. + require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + result = tk.MustQuery("select @@txn_scope;") + result.Check(testkit.Rows("local")) + err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope + require.Error(t, err) + require.Regexp(t, ".*can not be read by.*", err) + // Read dc-2 with Stale Read (in dc-1 scope) + timestamp := timeBeforeWriting.Format(time.RFC3339Nano) + // TODO: check the result of Stale Read when we figure out how to make the time precision more accurate. + tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c = 101", timestamp)) + tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c > 100", timestamp)) + tk.MustExec(fmt.Sprintf("START TRANSACTION READ ONLY AS OF TIMESTAMP '%s'", timestamp)) + tk.MustExec("select * from t1 where c = 101") + tk.MustExec("select * from t1 where c > 100") + tk.MustExec("commit") + tk.MustExec("set @@tidb_replica_read='closest-replicas'") + tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c > 100", timestamp)) + tk.MustExec(fmt.Sprintf("START TRANSACTION READ ONLY AS OF TIMESTAMP '%s'", timestamp)) + tk.MustExec("select * from t1 where c = 101") + tk.MustExec("select * from t1 where c > 100") + tk.MustExec("commit") + + tk.MustExec("set global tidb_enable_local_txn = off;") +} diff --git a/pkg/session/session.go b/pkg/session/session.go new file mode 100644 index 0000000000000..4fc30d8786929 --- /dev/null +++ b/pkg/session/session.go @@ -0,0 +1,4434 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package session + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/hex" + "encoding/json" + stderrs "errors" + "fmt" + "math" + "math/rand" + "runtime/pprof" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/bindinfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/extension/extensionimpl" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/plugin" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/privilege/conn" + "github.com/pingcap/tidb/pkg/privilege/privileges" + session_metrics "github.com/pingcap/tidb/pkg/session/metrics" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics/handle/usage" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/ttl/ttlworker" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sem" + "github.com/pingcap/tidb/pkg/util/sli" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" + "github.com/pingcap/tidb/pkg/util/tableutil" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tidb/pkg/util/topsql" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tipb/go-binlog" + tikverr "github.com/tikv/client-go/v2/error" + tikvstore "github.com/tikv/client-go/v2/kv" + "github.com/tikv/client-go/v2/oracle" + tikvutil "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +// Session context, it is consistent with the lifecycle of a client connection. +type Session interface { + sessionctx.Context + Status() uint16 // Flag of current status, such as autocommit. + LastInsertID() uint64 // LastInsertID is the last inserted auto_increment ID. + LastMessage() string // LastMessage is the info message that may be generated by last command + AffectedRows() uint64 // Affected rows by latest executed stmt. + // Execute is deprecated, and only used by plugins. Use ExecuteStmt() instead. + Execute(context.Context, string) ([]sqlexec.RecordSet, error) // Execute a sql statement. + // ExecuteStmt executes a parsed statement. + ExecuteStmt(context.Context, ast.StmtNode) (sqlexec.RecordSet, error) + // Parse is deprecated, use ParseWithParams() instead. + Parse(ctx context.Context, sql string) ([]ast.StmtNode, error) + // ExecuteInternal is a helper around ParseWithParams() and ExecuteStmt(). It is not allowed to execute multiple statements. + ExecuteInternal(context.Context, string, ...interface{}) (sqlexec.RecordSet, error) + String() string // String is used to debug. + CommitTxn(context.Context) error + RollbackTxn(context.Context) + // PrepareStmt executes prepare statement in binary protocol. + PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) + // ExecutePreparedStmt executes a prepared statement. + // Deprecated: please use ExecuteStmt, this function is left for testing only. + // TODO: remove ExecutePreparedStmt. + ExecutePreparedStmt(ctx context.Context, stmtID uint32, param []expression.Expression) (sqlexec.RecordSet, error) + DropPreparedStmt(stmtID uint32) error + // SetSessionStatesHandler sets SessionStatesHandler for type stateType. + SetSessionStatesHandler(stateType sessionstates.SessionStateType, handler sessionctx.SessionStatesHandler) + SetClientCapability(uint32) // Set client capability flags. + SetConnectionID(uint64) + SetCommandValue(byte) + SetProcessInfo(string, time.Time, byte, uint64) + SetTLSState(*tls.ConnectionState) + SetCollation(coID int) error + SetSessionManager(util.SessionManager) + Close() + Auth(user *auth.UserIdentity, auth, salt []byte, authConn conn.AuthConn) error + AuthWithoutVerification(user *auth.UserIdentity) bool + AuthPluginForUser(user *auth.UserIdentity) (string, error) + MatchIdentity(username, remoteHost string) (*auth.UserIdentity, error) + // Return the information of the txn current running + TxnInfo() *txninfo.TxnInfo + // PrepareTxnCtx is exported for test. + PrepareTxnCtx(context.Context) error + // FieldList returns fields list of a table. + FieldList(tableName string) (fields []*ast.ResultField, err error) + SetPort(port string) + + // set cur session operations allowed when tikv disk full happens. + SetDiskFullOpt(level kvrpcpb.DiskFullOpt) + GetDiskFullOpt() kvrpcpb.DiskFullOpt + ClearDiskFullOpt() + + // SetExtensions sets the `*extension.SessionExtensions` object + SetExtensions(extensions *extension.SessionExtensions) +} + +func init() { + executor.CreateSession = func(ctx sessionctx.Context) (sessionctx.Context, error) { + return CreateSession(ctx.GetStore()) + } + executor.CloseSession = func(ctx sessionctx.Context) { + if se, ok := ctx.(Session); ok { + se.Close() + } + } +} + +var _ Session = (*session)(nil) + +type stmtRecord struct { + st sqlexec.Statement + stmtCtx *stmtctx.StatementContext +} + +// StmtHistory holds all histories of statements in a txn. +type StmtHistory struct { + history []*stmtRecord +} + +// Add appends a stmt to history list. +func (h *StmtHistory) Add(st sqlexec.Statement, stmtCtx *stmtctx.StatementContext) { + s := &stmtRecord{ + st: st, + stmtCtx: stmtCtx, + } + h.history = append(h.history, s) +} + +// Count returns the count of the history. +func (h *StmtHistory) Count() int { + return len(h.history) +} + +type session struct { + // processInfo is used by ShowProcess(), and should be modified atomically. + processInfo atomic.Value + txn LazyTxn + + mu struct { + sync.RWMutex + values map[fmt.Stringer]interface{} + } + + currentCtx context.Context // only use for runtime.trace, Please NEVER use it. + currentPlan plannercore.Plan + + store kv.Storage + + sessionPlanCache sessionctx.PlanCache + + sessionVars *variable.SessionVars + sessionManager util.SessionManager + + statsCollector *usage.SessionStatsItem + // ddlOwnerManager is used in `select tidb_is_ddl_owner()` statement; + ddlOwnerManager owner.Manager + // lockedTables use to record the table locks hold by the session. + lockedTables map[int64]model.TableLockTpInfo + + // client shared coprocessor client per session + client kv.Client + + mppClient kv.MPPClient + + // indexUsageCollector collects index usage information. + idxUsageCollector *usage.SessionIndexUsageCollector + + functionUsageMu struct { + syncutil.RWMutex + builtinFunctionUsage telemetry.BuiltinFunctionsUsage + } + // allowed when tikv disk full happened. + diskFullOpt kvrpcpb.DiskFullOpt + + // StmtStats is used to count various indicators of each SQL in this session + // at each point in time. These data will be periodically taken away by the + // background goroutine. The background goroutine will continue to aggregate + // all the local data in each session, and finally report them to the remote + // regularly. + stmtStats *stmtstats.StatementStats + + // Used to encode and decode each type of session states. + sessionStatesHandlers map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler + + // Contains a list of sessions used to collect advisory locks. + advisoryLocks map[string]*advisoryLock + + extensions *extension.SessionExtensions + + sandBoxMode bool +} + +var parserPool = &sync.Pool{New: func() interface{} { return parser.New() }} + +// AddTableLock adds table lock to the session lock map. +func (s *session) AddTableLock(locks []model.TableLockTpInfo) { + for _, l := range locks { + // read only lock is session unrelated, skip it when adding lock to session. + if l.Tp != model.TableLockReadOnly { + s.lockedTables[l.TableID] = l + } + } +} + +// ReleaseTableLocks releases table lock in the session lock map. +func (s *session) ReleaseTableLocks(locks []model.TableLockTpInfo) { + for _, l := range locks { + delete(s.lockedTables, l.TableID) + } +} + +// ReleaseTableLockByTableIDs releases table lock in the session lock map by table ID. +func (s *session) ReleaseTableLockByTableIDs(tableIDs []int64) { + for _, tblID := range tableIDs { + delete(s.lockedTables, tblID) + } +} + +// CheckTableLocked checks the table lock. +func (s *session) CheckTableLocked(tblID int64) (bool, model.TableLockType) { + lt, ok := s.lockedTables[tblID] + if !ok { + return false, model.TableLockNone + } + return true, lt.Tp +} + +// GetAllTableLocks gets all table locks table id and db id hold by the session. +func (s *session) GetAllTableLocks() []model.TableLockTpInfo { + lockTpInfo := make([]model.TableLockTpInfo, 0, len(s.lockedTables)) + for _, tl := range s.lockedTables { + lockTpInfo = append(lockTpInfo, tl) + } + return lockTpInfo +} + +// HasLockedTables uses to check whether this session locked any tables. +// If so, the session can only visit the table which locked by self. +func (s *session) HasLockedTables() bool { + b := len(s.lockedTables) > 0 + return b +} + +// ReleaseAllTableLocks releases all table locks hold by the session. +func (s *session) ReleaseAllTableLocks() { + s.lockedTables = make(map[int64]model.TableLockTpInfo) +} + +// IsDDLOwner checks whether this session is DDL owner. +func (s *session) IsDDLOwner() bool { + return s.ddlOwnerManager.IsOwner() +} + +func (s *session) cleanRetryInfo() { + if s.sessionVars.RetryInfo.Retrying { + return + } + + retryInfo := s.sessionVars.RetryInfo + defer retryInfo.Clean() + if len(retryInfo.DroppedPreparedStmtIDs) == 0 { + return + } + + planCacheEnabled := s.GetSessionVars().EnablePreparedPlanCache + var cacheKey kvcache.Key + var err error + var preparedAst *ast.Prepared + var stmtText, stmtDB string + if planCacheEnabled { + firstStmtID := retryInfo.DroppedPreparedStmtIDs[0] + if preparedPointer, ok := s.sessionVars.PreparedStmts[firstStmtID]; ok { + preparedObj, ok := preparedPointer.(*plannercore.PlanCacheStmt) + if ok { + preparedAst = preparedObj.PreparedAst + stmtText, stmtDB = preparedObj.StmtText, preparedObj.StmtDB + bindSQL, _ := plannercore.GetBindSQL4PlanCache(s, preparedObj) + cacheKey, err = plannercore.NewPlanCacheKey(s.sessionVars, stmtText, stmtDB, preparedAst.SchemaVersion, + 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) + if err != nil { + logutil.Logger(s.currentCtx).Warn("clean cached plan failed", zap.Error(err)) + return + } + } + } + } + for i, stmtID := range retryInfo.DroppedPreparedStmtIDs { + if planCacheEnabled { + if i > 0 && preparedAst != nil { + plannercore.SetPstmtIDSchemaVersion(cacheKey, stmtText, preparedAst.SchemaVersion, s.sessionVars.IsolationReadEngines) + } + if !s.sessionVars.IgnorePreparedCacheCloseStmt { // keep the plan in cache + s.GetSessionPlanCache().Delete(cacheKey) + } + } + s.sessionVars.RemovePreparedStmt(stmtID) + } +} + +func (s *session) Status() uint16 { + return s.sessionVars.Status +} + +func (s *session) LastInsertID() uint64 { + if s.sessionVars.StmtCtx.LastInsertID > 0 { + return s.sessionVars.StmtCtx.LastInsertID + } + return s.sessionVars.StmtCtx.InsertID +} + +func (s *session) LastMessage() string { + return s.sessionVars.StmtCtx.GetMessage() +} + +func (s *session) AffectedRows() uint64 { + return s.sessionVars.StmtCtx.AffectedRows() +} + +func (s *session) SetClientCapability(capability uint32) { + s.sessionVars.ClientCapability = capability +} + +func (s *session) SetConnectionID(connectionID uint64) { + s.sessionVars.ConnectionID = connectionID +} + +func (s *session) SetTLSState(tlsState *tls.ConnectionState) { + // If user is not connected via TLS, then tlsState == nil. + if tlsState != nil { + s.sessionVars.TLSConnectionState = tlsState + } +} + +func (s *session) SetCommandValue(command byte) { + atomic.StoreUint32(&s.sessionVars.CommandValue, uint32(command)) +} + +func (s *session) SetCollation(coID int) error { + cs, co, err := charset.GetCharsetInfoByID(coID) + if err != nil { + return err + } + // If new collations are enabled, switch to the default + // collation if this one is not supported. + co = collate.SubstituteMissingCollationToDefault(co) + for _, v := range variable.SetNamesVariables { + terror.Log(s.sessionVars.SetSystemVarWithoutValidation(v, cs)) + } + return s.sessionVars.SetSystemVarWithoutValidation(variable.CollationConnection, co) +} + +func (s *session) GetSessionPlanCache() sessionctx.PlanCache { + // use the prepared plan cache + if !s.GetSessionVars().EnablePreparedPlanCache && !s.GetSessionVars().EnableNonPreparedPlanCache { + return nil + } + if s.sessionPlanCache == nil { // lazy construction + s.sessionPlanCache = plannercore.NewLRUPlanCache(uint(s.GetSessionVars().SessionPlanCacheSize), + variable.PreparedPlanCacheMemoryGuardRatio.Load(), plannercore.PreparedPlanCacheMaxMemory.Load(), s, false) + } + return s.sessionPlanCache +} + +func (s *session) SetSessionManager(sm util.SessionManager) { + s.sessionManager = sm +} + +func (s *session) GetSessionManager() util.SessionManager { + return s.sessionManager +} + +func (s *session) UpdateColStatsUsage(predicateColumns []model.TableItemID) { + if s.statsCollector == nil { + return + } + t := time.Now() + colMap := make(map[model.TableItemID]time.Time, len(predicateColumns)) + for _, col := range predicateColumns { + if col.IsIndex { + continue + } + colMap[col] = t + } + s.statsCollector.UpdateColStatsUsage(colMap) +} + +// StoreIndexUsage stores index usage information in idxUsageCollector. +func (s *session) StoreIndexUsage(tblID int64, idxID int64, rowsSelected int64) { + if s.idxUsageCollector == nil { + return + } + s.idxUsageCollector.Update(tblID, idxID, &usage.IndexUsageInformation{QueryCount: 1, RowsSelected: rowsSelected}) +} + +// FieldList returns fields list of a table. +func (s *session) FieldList(tableName string) ([]*ast.ResultField, error) { + is := s.GetInfoSchema().(infoschema.InfoSchema) + dbName := model.NewCIStr(s.GetSessionVars().CurrentDB) + tName := model.NewCIStr(tableName) + pm := privilege.GetPrivilegeManager(s) + if pm != nil && s.sessionVars.User != nil { + if !pm.RequestVerification(s.sessionVars.ActiveRoles, dbName.O, tName.O, "", mysql.AllPrivMask) { + user := s.sessionVars.User + u := user.Username + h := user.Hostname + if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { + u = user.AuthUsername + h = user.AuthHostname + } + return nil, plannercore.ErrTableaccessDenied.GenWithStackByArgs("SELECT", u, h, tableName) + } + } + table, err := is.TableByName(dbName, tName) + if err != nil { + return nil, err + } + + cols := table.Cols() + fields := make([]*ast.ResultField, 0, len(cols)) + for _, col := range table.Cols() { + rf := &ast.ResultField{ + ColumnAsName: col.Name, + TableAsName: tName, + DBName: dbName, + Table: table.Meta(), + Column: col.ColumnInfo, + } + fields = append(fields, rf) + } + return fields, nil +} + +// TxnInfo returns a pointer to a *copy* of the internal TxnInfo, thus is *read only* +func (s *session) TxnInfo() *txninfo.TxnInfo { + s.txn.mu.RLock() + // Copy on read to get a snapshot, this API shouldn't be frequently called. + txnInfo := s.txn.mu.TxnInfo + s.txn.mu.RUnlock() + + if txnInfo.StartTS == 0 { + return nil + } + + processInfo := s.ShowProcess() + if processInfo == nil { + return nil + } + txnInfo.ConnectionID = processInfo.ID + txnInfo.Username = processInfo.User + txnInfo.CurrentDB = processInfo.DB + txnInfo.RelatedTableIDs = make(map[int64]struct{}) + s.GetSessionVars().GetRelatedTableForMDL().Range(func(key, value interface{}) bool { + txnInfo.RelatedTableIDs[key.(int64)] = struct{}{} + return true + }) + + return &txnInfo +} + +func (s *session) doCommit(ctx context.Context) error { + if !s.txn.Valid() { + return nil + } + + // to avoid session set overlap the txn set. + if s.GetDiskFullOpt() != kvrpcpb.DiskFullOpt_NotAllowedOnFull { + s.txn.SetDiskFullOpt(s.GetDiskFullOpt()) + } + + defer func() { + s.txn.changeToInvalid() + s.sessionVars.SetInTxn(false) + s.ClearDiskFullOpt() + }() + // check if the transaction is read-only + if s.txn.IsReadOnly() { + return nil + } + // check if the cluster is read-only + if !s.sessionVars.InRestrictedSQL && variable.RestrictedReadOnly.Load() || variable.VarTiDBSuperReadOnly.Load() { + // It is not internal SQL, and the cluster has one of RestrictedReadOnly or SuperReadOnly + // We need to privilege check again: a privilege check occurred during planning, but we need + // to prevent the case that a long running auto-commit statement is now trying to commit. + pm := privilege.GetPrivilegeManager(s) + roles := s.sessionVars.ActiveRoles + if pm != nil && !pm.HasExplicitlyGrantedDynamicPrivilege(roles, "RESTRICTED_REPLICA_WRITER_ADMIN", false) { + s.RollbackTxn(ctx) + return plannercore.ErrSQLInReadOnlyMode + } + } + err := s.checkPlacementPolicyBeforeCommit() + if err != nil { + return err + } + // mockCommitError and mockGetTSErrorInRetry use to test PR #8743. + failpoint.Inject("mockCommitError", func(val failpoint.Value) { + if val.(bool) { + if _, err := failpoint.Eval("tikvclient/mockCommitErrorOpt"); err == nil { + failpoint.Return(kv.ErrTxnRetryable) + } + } + }) + + if s.sessionVars.BinlogClient != nil { + prewriteValue := binloginfo.GetPrewriteValue(s, false) + if prewriteValue != nil { + prewriteData, err := prewriteValue.Marshal() + if err != nil { + return errors.Trace(err) + } + info := &binloginfo.BinlogInfo{ + Data: &binlog.Binlog{ + Tp: binlog.BinlogType_Prewrite, + PrewriteValue: prewriteData, + }, + Client: s.sessionVars.BinlogClient, + } + s.txn.SetOption(kv.BinlogInfo, info) + } + } + + sessVars := s.GetSessionVars() + // Get the related table or partition IDs. + relatedPhysicalTables := sessVars.TxnCtx.TableDeltaMap + // Get accessed temporary tables in the transaction. + temporaryTables := sessVars.TxnCtx.TemporaryTables + physicalTableIDs := make([]int64, 0, len(relatedPhysicalTables)) + for id := range relatedPhysicalTables { + // Schema change on global temporary tables doesn't affect transactions. + if _, ok := temporaryTables[id]; ok { + continue + } + physicalTableIDs = append(physicalTableIDs, id) + } + needCheckSchema := true + // Set this option for 2 phase commit to validate schema lease. + if s.GetSessionVars().TxnCtx != nil { + needCheckSchema = !s.GetSessionVars().TxnCtx.EnableMDL + } + s.txn.SetOption(kv.SchemaChecker, domain.NewSchemaChecker(domain.GetDomain(s), s.GetInfoSchema().SchemaMetaVersion(), physicalTableIDs, needCheckSchema)) + s.txn.SetOption(kv.InfoSchema, s.sessionVars.TxnCtx.InfoSchema) + s.txn.SetOption(kv.CommitHook, func(info string, _ error) { s.sessionVars.LastTxnInfo = info }) + s.txn.SetOption(kv.EnableAsyncCommit, sessVars.EnableAsyncCommit) + s.txn.SetOption(kv.Enable1PC, sessVars.Enable1PC) + s.txn.SetOption(kv.ResourceGroupTagger, sessVars.StmtCtx.GetResourceGroupTagger()) + s.txn.SetOption(kv.ExplicitRequestSourceType, sessVars.ExplicitRequestSourceType) + if sessVars.StmtCtx.KvExecCounter != nil { + // Bind an interceptor for client-go to count the number of SQL executions of each TiKV. + s.txn.SetOption(kv.RPCInterceptor, sessVars.StmtCtx.KvExecCounter.RPCInterceptor()) + } + // priority of the sysvar is lower than `start transaction with causal consistency only` + if val := s.txn.GetOption(kv.GuaranteeLinearizability); val == nil || val.(bool) { + // We needn't ask the TiKV client to guarantee linearizability for auto-commit transactions + // because the property is naturally holds: + // We guarantee the commitTS of any transaction must not exceed the next timestamp from the TSO. + // An auto-commit transaction fetches its startTS from the TSO so its commitTS > its startTS > the commitTS + // of any previously committed transactions. + s.txn.SetOption(kv.GuaranteeLinearizability, + sessVars.TxnCtx.IsExplicit && sessVars.GuaranteeLinearizability) + } + if tables := sessVars.TxnCtx.TemporaryTables; len(tables) > 0 { + s.txn.SetOption(kv.KVFilter, temporaryTableKVFilter(tables)) + } + + var txnSource uint64 + if val := s.txn.GetOption(kv.TxnSource); val != nil { + txnSource, _ = val.(uint64) + } + // If the transaction is started by CDC, we need to set the CDCWriteSource option. + if sessVars.CDCWriteSource != 0 { + err := kv.SetCDCWriteSource(&txnSource, sessVars.CDCWriteSource) + if err != nil { + return errors.Trace(err) + } + + s.txn.SetOption(kv.TxnSource, txnSource) + } + + if tables := sessVars.TxnCtx.CachedTables; len(tables) > 0 { + c := cachedTableRenewLease{tables: tables} + now := time.Now() + err := c.start(ctx) + defer c.stop(ctx) + sessVars.StmtCtx.WaitLockLeaseTime += time.Since(now) + if err != nil { + return errors.Trace(err) + } + s.txn.SetOption(kv.CommitTSUpperBoundCheck, c.commitTSCheck) + } + + err = s.commitTxnWithTemporaryData(tikvutil.SetSessionID(ctx, sessVars.ConnectionID), &s.txn) + if err != nil { + err = s.handleAssertionFailure(ctx, err) + } + return err +} + +type cachedTableRenewLease struct { + tables map[int64]interface{} + lease []uint64 // Lease for each visited cached tables. + exit chan struct{} +} + +func (c *cachedTableRenewLease) start(ctx context.Context) error { + c.exit = make(chan struct{}) + c.lease = make([]uint64, len(c.tables)) + wg := make(chan error, len(c.tables)) + ith := 0 + for _, raw := range c.tables { + tbl := raw.(table.CachedTable) + go tbl.WriteLockAndKeepAlive(ctx, c.exit, &c.lease[ith], wg) + ith++ + } + + // Wait for all LockForWrite() return, this function can return. + var err error + for ; ith > 0; ith-- { + tmp := <-wg + if tmp != nil { + err = tmp + } + } + return err +} + +func (c *cachedTableRenewLease) stop(_ context.Context) { + close(c.exit) +} + +func (c *cachedTableRenewLease) commitTSCheck(commitTS uint64) bool { + for i := 0; i < len(c.lease); i++ { + lease := atomic.LoadUint64(&c.lease[i]) + if commitTS >= lease { + // Txn fails to commit because the write lease is expired. + return false + } + } + return true +} + +// handleAssertionFailure extracts the possible underlying assertionFailed error, +// gets the corresponding MVCC history and logs it. +// If it's not an assertion failure, returns the original error. +func (s *session) handleAssertionFailure(ctx context.Context, err error) error { + var assertionFailure *tikverr.ErrAssertionFailed + if !stderrs.As(err, &assertionFailure) { + return err + } + key := assertionFailure.Key + newErr := kv.ErrAssertionFailed.GenWithStackByArgs( + hex.EncodeToString(key), assertionFailure.Assertion.String(), assertionFailure.StartTs, + assertionFailure.ExistingStartTs, assertionFailure.ExistingCommitTs, + ) + + if s.GetSessionVars().EnableRedactLog { + return newErr + } + + var decodeFunc func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) + // if it's a record key or an index key, decode it + if infoSchema, ok := s.sessionVars.TxnCtx.InfoSchema.(infoschema.InfoSchema); ok && + infoSchema != nil && (tablecodec.IsRecordKey(key) || tablecodec.IsIndexKey(key)) { + tableOrPartitionID := tablecodec.DecodeTableID(key) + tbl, ok := infoSchema.TableByID(tableOrPartitionID) + if !ok { + tbl, _, _ = infoSchema.FindTableByPartitionID(tableOrPartitionID) + } + if tbl == nil { + logutil.Logger(ctx).Warn("cannot find table by id", zap.Int64("tableID", tableOrPartitionID), zap.String("key", hex.EncodeToString(key))) + return newErr + } + + if tablecodec.IsRecordKey(key) { + decodeFunc = consistency.DecodeRowMvccData(tbl.Meta()) + } else { + tableInfo := tbl.Meta() + _, indexID, _, e := tablecodec.DecodeIndexKey(key) + if e != nil { + logutil.Logger(ctx).Error("assertion failed but cannot decode index key", zap.Error(e)) + return newErr + } + var indexInfo *model.IndexInfo + for _, idx := range tableInfo.Indices { + if idx.ID == indexID { + indexInfo = idx + break + } + } + if indexInfo == nil { + return newErr + } + decodeFunc = consistency.DecodeIndexMvccData(indexInfo) + } + } + if store, ok := s.store.(helper.Storage); ok { + content := consistency.GetMvccByKey(store, key, decodeFunc) + logutil.Logger(ctx).Error("assertion failed", zap.String("message", newErr.Error()), zap.String("mvcc history", content)) + } + return newErr +} + +func (s *session) commitTxnWithTemporaryData(ctx context.Context, txn kv.Transaction) error { + sessVars := s.sessionVars + txnTempTables := sessVars.TxnCtx.TemporaryTables + if len(txnTempTables) == 0 { + failpoint.Inject("mockSleepBeforeTxnCommit", func(v failpoint.Value) { + ms := v.(int) + time.Sleep(time.Millisecond * time.Duration(ms)) + }) + return txn.Commit(ctx) + } + + sessionData := sessVars.TemporaryTableData + var ( + stage kv.StagingHandle + localTempTables *infoschema.SessionTables + ) + + if sessVars.LocalTemporaryTables != nil { + localTempTables = sessVars.LocalTemporaryTables.(*infoschema.SessionTables) + } else { + localTempTables = new(infoschema.SessionTables) + } + + defer func() { + // stage != kv.InvalidStagingHandle means error occurs, we need to cleanup sessionData + if stage != kv.InvalidStagingHandle { + sessionData.Cleanup(stage) + } + }() + + for tblID, tbl := range txnTempTables { + if !tbl.GetModified() { + continue + } + + if tbl.GetMeta().TempTableType != model.TempTableLocal { + continue + } + if _, ok := localTempTables.TableByID(tblID); !ok { + continue + } + + if stage == kv.InvalidStagingHandle { + stage = sessionData.Staging() + } + + tblPrefix := tablecodec.EncodeTablePrefix(tblID) + endKey := tablecodec.EncodeTablePrefix(tblID + 1) + + txnMemBuffer := s.txn.GetMemBuffer() + iter, err := txnMemBuffer.Iter(tblPrefix, endKey) + if err != nil { + return err + } + + for iter.Valid() { + key := iter.Key() + if !bytes.HasPrefix(key, tblPrefix) { + break + } + + value := iter.Value() + if len(value) == 0 { + err = sessionData.DeleteTableKey(tblID, key) + } else { + err = sessionData.SetTableKey(tblID, key, iter.Value()) + } + + if err != nil { + return err + } + + err = iter.Next() + if err != nil { + return err + } + } + } + + err := txn.Commit(ctx) + if err != nil { + return err + } + + if stage != kv.InvalidStagingHandle { + sessionData.Release(stage) + stage = kv.InvalidStagingHandle + } + + return nil +} + +type temporaryTableKVFilter map[int64]tableutil.TempTable + +func (m temporaryTableKVFilter) IsUnnecessaryKeyValue(key, value []byte, flags tikvstore.KeyFlags) (bool, error) { + tid := tablecodec.DecodeTableID(key) + if _, ok := m[tid]; ok { + return true, nil + } + + // This is the default filter for all tables. + defaultFilter := txn.TiDBKVFilter{} + return defaultFilter.IsUnnecessaryKeyValue(key, value, flags) +} + +// errIsNoisy is used to filter DUPLCATE KEY errors. +// These can observed by users in INFORMATION_SCHEMA.CLIENT_ERRORS_SUMMARY_GLOBAL instead. +// +// The rationale for filtering these errors is because they are "client generated errors". i.e. +// of the errors defined in kv/error.go, these look to be clearly related to a client-inflicted issue, +// and the server is only responsible for handling the error correctly. It does not need to log. +func errIsNoisy(err error) bool { + if kv.ErrKeyExists.Equal(err) { + return true + } + if storeerr.ErrLockAcquireFailAndNoWaitSet.Equal(err) { + return true + } + return false +} + +func (s *session) doCommitWithRetry(ctx context.Context) error { + defer func() { + s.GetSessionVars().SetTxnIsolationLevelOneShotStateForNextTxn() + s.txn.changeToInvalid() + s.cleanRetryInfo() + sessiontxn.GetTxnManager(s).OnTxnEnd() + }() + if !s.txn.Valid() { + // If the transaction is invalid, maybe it has already been rolled back by the client. + return nil + } + isInternalTxn := false + if internal := s.txn.GetOption(kv.RequestSourceInternal); internal != nil && internal.(bool) { + isInternalTxn = true + } + var err error + txnSize := s.txn.Size() + isPessimistic := s.txn.IsPessimistic() + r, ctx := tracing.StartRegionEx(ctx, "session.doCommitWithRetry") + defer r.End() + + err = s.doCommit(ctx) + if err != nil { + // polish the Write Conflict error message + newErr := s.tryReplaceWriteConflictError(err) + if newErr != nil { + err = newErr + } + + commitRetryLimit := s.sessionVars.RetryLimit + if !s.sessionVars.TxnCtx.CouldRetry { + commitRetryLimit = 0 + } + // Don't retry in BatchInsert mode. As a counter-example, insert into t1 select * from t2, + // BatchInsert already commit the first batch 1000 rows, then it commit 1000-2000 and retry the statement, + // Finally t1 will have more data than t2, with no errors return to user! + if s.isTxnRetryableError(err) && !s.sessionVars.BatchInsert && commitRetryLimit > 0 && !isPessimistic { + logutil.Logger(ctx).Warn("sql", + zap.String("label", s.GetSQLLabel()), + zap.Error(err), + zap.String("txn", s.txn.GoString())) + // Transactions will retry 2 ~ commitRetryLimit times. + // We make larger transactions retry less times to prevent cluster resource outage. + txnSizeRate := float64(txnSize) / float64(kv.TxnTotalSizeLimit.Load()) + maxRetryCount := commitRetryLimit - int64(float64(commitRetryLimit-1)*txnSizeRate) + err = s.retry(ctx, uint(maxRetryCount)) + } else if !errIsNoisy(err) { + logutil.Logger(ctx).Warn("can not retry txn", + zap.String("label", s.GetSQLLabel()), + zap.Error(err), + zap.Bool("IsBatchInsert", s.sessionVars.BatchInsert), + zap.Bool("IsPessimistic", isPessimistic), + zap.Bool("InRestrictedSQL", s.sessionVars.InRestrictedSQL), + zap.Int64("tidb_retry_limit", s.sessionVars.RetryLimit), + zap.Bool("tidb_disable_txn_auto_retry", s.sessionVars.DisableTxnAutoRetry)) + } + } + counter := s.sessionVars.TxnCtx.StatementCount + duration := time.Since(s.GetSessionVars().TxnCtx.CreateTime).Seconds() + s.recordOnTransactionExecution(err, counter, duration, isInternalTxn) + + if err != nil { + if !errIsNoisy(err) { + logutil.Logger(ctx).Warn("commit failed", + zap.String("finished txn", s.txn.GoString()), + zap.Error(err)) + } + return err + } + s.updateStatsDeltaToCollector() + return nil +} + +// adds more information about the table in the error message +// precondition: oldErr is a 9007:WriteConflict Error +func (s *session) tryReplaceWriteConflictError(oldErr error) (newErr error) { + if !kv.ErrWriteConflict.Equal(oldErr) { + return nil + } + if errors.RedactLogEnabled.Load() { + return nil + } + originErr := errors.Cause(oldErr) + inErr, _ := originErr.(*errors.Error) + args := inErr.Args() + is := sessiontxn.GetTxnManager(s).GetTxnInfoSchema() + if is == nil { + return nil + } + newKeyTableField, ok := addTableNameInTableIDField(args[3], is) + if ok { + args[3] = newKeyTableField + } + newPrimaryKeyTableField, ok := addTableNameInTableIDField(args[5], is) + if ok { + args[5] = newPrimaryKeyTableField + } + return kv.ErrWriteConflict.FastGenByArgs(args...) +} + +// precondition: is != nil +func addTableNameInTableIDField(tableIDField interface{}, is infoschema.InfoSchema) (enhancedMsg string, done bool) { + keyTableID, ok := tableIDField.(string) + if !ok { + return "", false + } + stringsInTableIDField := strings.Split(keyTableID, "=") + if len(stringsInTableIDField) == 0 { + return "", false + } + tableIDStr := stringsInTableIDField[len(stringsInTableIDField)-1] + tableID, err := strconv.ParseInt(tableIDStr, 10, 64) + if err != nil { + return "", false + } + var tableName string + tbl, ok := is.TableByID(tableID) + if !ok { + tableName = "unknown" + } else { + dbInfo, ok := is.SchemaByTable(tbl.Meta()) + if !ok { + tableName = "unknown." + tbl.Meta().Name.String() + } else { + tableName = dbInfo.Name.String() + "." + tbl.Meta().Name.String() + } + } + enhancedMsg = keyTableID + ", tableName=" + tableName + return enhancedMsg, true +} + +func (s *session) updateStatsDeltaToCollector() { + mapper := s.GetSessionVars().TxnCtx.TableDeltaMap + if s.statsCollector != nil && mapper != nil { + for _, item := range mapper { + if item.TableID > 0 { + s.statsCollector.Update(item.TableID, item.Delta, item.Count, &item.ColSize) + } + } + } +} + +func (s *session) CommitTxn(ctx context.Context) error { + r, ctx := tracing.StartRegionEx(ctx, "session.CommitTxn") + defer r.End() + + var commitDetail *tikvutil.CommitDetails + ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) + err := s.doCommitWithRetry(ctx) + if commitDetail != nil { + s.sessionVars.StmtCtx.MergeExecDetails(nil, commitDetail) + } + + // record the TTLInsertRows in the metric + metrics.TTLInsertRowsCount.Add(float64(s.sessionVars.TxnCtx.InsertTTLRowsCount)) + + failpoint.Inject("keepHistory", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(err) + } + }) + s.sessionVars.TxnCtx.Cleanup() + s.sessionVars.CleanupTxnReadTSIfUsed() + return err +} + +func (s *session) RollbackTxn(ctx context.Context) { + r, ctx := tracing.StartRegionEx(ctx, "session.RollbackTxn") + defer r.End() + + if s.txn.Valid() { + terror.Log(s.txn.Rollback()) + } + if ctx.Value(inCloseSession{}) == nil { + s.cleanRetryInfo() + } + s.txn.changeToInvalid() + s.sessionVars.TxnCtx.Cleanup() + s.sessionVars.CleanupTxnReadTSIfUsed() + s.sessionVars.SetInTxn(false) + sessiontxn.GetTxnManager(s).OnTxnEnd() +} + +func (s *session) GetClient() kv.Client { + return s.client +} + +func (s *session) GetMPPClient() kv.MPPClient { + return s.mppClient +} + +func (s *session) String() string { + // TODO: how to print binded context in values appropriately? + sessVars := s.sessionVars + data := map[string]interface{}{ + "id": sessVars.ConnectionID, + "user": sessVars.User, + "currDBName": sessVars.CurrentDB, + "status": sessVars.Status, + "strictMode": sessVars.StrictSQLMode, + } + if s.txn.Valid() { + // if txn is committed or rolled back, txn is nil. + data["txn"] = s.txn.String() + } + if sessVars.SnapshotTS != 0 { + data["snapshotTS"] = sessVars.SnapshotTS + } + if sessVars.StmtCtx.LastInsertID > 0 { + data["lastInsertID"] = sessVars.StmtCtx.LastInsertID + } + if len(sessVars.PreparedStmts) > 0 { + data["preparedStmtCount"] = len(sessVars.PreparedStmts) + } + b, err := json.MarshalIndent(data, "", " ") + terror.Log(errors.Trace(err)) + return string(b) +} + +const sqlLogMaxLen = 1024 + +// SchemaChangedWithoutRetry is used for testing. +var SchemaChangedWithoutRetry uint32 + +func (s *session) GetSQLLabel() string { + if s.sessionVars.InRestrictedSQL { + return metrics.LblInternal + } + return metrics.LblGeneral +} + +func (s *session) isInternal() bool { + return s.sessionVars.InRestrictedSQL +} + +func (*session) isTxnRetryableError(err error) bool { + if atomic.LoadUint32(&SchemaChangedWithoutRetry) == 1 { + return kv.IsTxnRetryableError(err) + } + return kv.IsTxnRetryableError(err) || domain.ErrInfoSchemaChanged.Equal(err) +} + +func (s *session) checkTxnAborted(stmt sqlexec.Statement) error { + var err error + if atomic.LoadUint32(&s.GetSessionVars().TxnCtx.LockExpire) == 0 { + return nil + } + err = kv.ErrLockExpire + // If the transaction is aborted, the following statements do not need to execute, except `commit` and `rollback`, + // because they are used to finish the aborted transaction. + if _, ok := stmt.(*executor.ExecStmt).StmtNode.(*ast.CommitStmt); ok { + return nil + } + if _, ok := stmt.(*executor.ExecStmt).StmtNode.(*ast.RollbackStmt); ok { + return nil + } + return err +} + +func (s *session) retry(ctx context.Context, maxCnt uint) (err error) { + var retryCnt uint + defer func() { + s.sessionVars.RetryInfo.Retrying = false + // retryCnt only increments on retryable error, so +1 here. + if s.sessionVars.InRestrictedSQL { + session_metrics.TransactionRetryInternal.Observe(float64(retryCnt + 1)) + } else { + session_metrics.TransactionRetryGeneral.Observe(float64(retryCnt + 1)) + } + s.sessionVars.SetInTxn(false) + if err != nil { + s.RollbackTxn(ctx) + } + s.txn.changeToInvalid() + }() + + connID := s.sessionVars.ConnectionID + s.sessionVars.RetryInfo.Retrying = true + if atomic.LoadUint32(&s.sessionVars.TxnCtx.ForUpdate) == 1 { + err = ErrForUpdateCantRetry.GenWithStackByArgs(connID) + return err + } + + nh := GetHistory(s) + var schemaVersion int64 + sessVars := s.GetSessionVars() + orgStartTS := sessVars.TxnCtx.StartTS + label := s.GetSQLLabel() + for { + if err = s.PrepareTxnCtx(ctx); err != nil { + return err + } + s.sessionVars.RetryInfo.ResetOffset() + for i, sr := range nh.history { + st := sr.st + s.sessionVars.StmtCtx = sr.stmtCtx + s.sessionVars.StmtCtx.ResetForRetry() + s.sessionVars.PlanCacheParams.Reset() + schemaVersion, err = st.RebuildPlan(ctx) + if err != nil { + return err + } + + if retryCnt == 0 { + // We do not have to log the query every time. + // We print the queries at the first try only. + sql := sqlForLog(st.GetTextToLog(false)) + if !sessVars.EnableRedactLog { + sql += sessVars.PlanCacheParams.String() + } + logutil.Logger(ctx).Warn("retrying", + zap.Int64("schemaVersion", schemaVersion), + zap.Uint("retryCnt", retryCnt), + zap.Int("queryNum", i), + zap.String("sql", sql)) + } else { + logutil.Logger(ctx).Warn("retrying", + zap.Int64("schemaVersion", schemaVersion), + zap.Uint("retryCnt", retryCnt), + zap.Int("queryNum", i)) + } + _, digest := s.sessionVars.StmtCtx.SQLDigest() + s.txn.onStmtStart(digest.String()) + if err = sessiontxn.GetTxnManager(s).OnStmtStart(ctx, st.GetStmtNode()); err == nil { + _, err = st.Exec(ctx) + } + s.txn.onStmtEnd() + if err != nil { + s.StmtRollback(ctx, false) + break + } + s.StmtCommit(ctx) + } + logutil.Logger(ctx).Warn("transaction association", + zap.Uint64("retrying txnStartTS", s.GetSessionVars().TxnCtx.StartTS), + zap.Uint64("original txnStartTS", orgStartTS)) + failpoint.Inject("preCommitHook", func() { + hook, ok := ctx.Value("__preCommitHook").(func()) + if ok { + hook() + } + }) + if err == nil { + err = s.doCommit(ctx) + if err == nil { + break + } + } + if !s.isTxnRetryableError(err) { + logutil.Logger(ctx).Warn("sql", + zap.String("label", label), + zap.Stringer("session", s), + zap.Error(err)) + metrics.SessionRetryErrorCounter.WithLabelValues(label, metrics.LblUnretryable).Inc() + return err + } + retryCnt++ + if retryCnt >= maxCnt { + logutil.Logger(ctx).Warn("sql", + zap.String("label", label), + zap.Uint("retry reached max count", retryCnt)) + metrics.SessionRetryErrorCounter.WithLabelValues(label, metrics.LblReachMax).Inc() + return err + } + logutil.Logger(ctx).Warn("sql", + zap.String("label", label), + zap.Error(err), + zap.String("txn", s.txn.GoString())) + kv.BackOff(retryCnt) + s.txn.changeToInvalid() + s.sessionVars.SetInTxn(false) + } + return err +} + +func sqlForLog(sql string) string { + if len(sql) > sqlLogMaxLen { + sql = sql[:sqlLogMaxLen] + fmt.Sprintf("(len:%d)", len(sql)) + } + return executor.QueryReplacer.Replace(sql) +} + +type sessionPool interface { + Get() (pools.Resource, error) + Put(pools.Resource) +} + +func (s *session) sysSessionPool() sessionPool { + return domain.GetDomain(s).SysSessionPool() +} + +func createSessionFunc(store kv.Storage) pools.Factory { + return func() (pools.Resource, error) { + se, err := createSession(store) + if err != nil { + return nil, err + } + err = se.sessionVars.SetSystemVar(variable.AutoCommit, "1") + if err != nil { + return nil, err + } + err = se.sessionVars.SetSystemVar(variable.MaxExecutionTime, "0") + if err != nil { + return nil, errors.Trace(err) + } + err = se.sessionVars.SetSystemVar(variable.MaxAllowedPacket, strconv.FormatUint(variable.DefMaxAllowedPacket, 10)) + if err != nil { + return nil, errors.Trace(err) + } + err = se.sessionVars.SetSystemVar(variable.TiDBEnableWindowFunction, variable.BoolToOnOff(variable.DefEnableWindowFunction)) + if err != nil { + return nil, errors.Trace(err) + } + err = se.sessionVars.SetSystemVar(variable.TiDBConstraintCheckInPlacePessimistic, variable.On) + if err != nil { + return nil, errors.Trace(err) + } + se.sessionVars.CommonGlobalLoaded = true + se.sessionVars.InRestrictedSQL = true + // Internal session uses default format to prevent memory leak problem. + se.sessionVars.EnableChunkRPC = false + return se, nil + } +} + +func createSessionWithDomainFunc(store kv.Storage) func(*domain.Domain) (pools.Resource, error) { + return func(dom *domain.Domain) (pools.Resource, error) { + se, err := CreateSessionWithDomain(store, dom) + if err != nil { + return nil, err + } + err = se.sessionVars.SetSystemVar(variable.AutoCommit, "1") + if err != nil { + return nil, err + } + err = se.sessionVars.SetSystemVar(variable.MaxExecutionTime, "0") + if err != nil { + return nil, errors.Trace(err) + } + err = se.sessionVars.SetSystemVar(variable.MaxAllowedPacket, strconv.FormatUint(variable.DefMaxAllowedPacket, 10)) + if err != nil { + return nil, errors.Trace(err) + } + err = se.sessionVars.SetSystemVar(variable.TiDBConstraintCheckInPlacePessimistic, variable.On) + if err != nil { + return nil, errors.Trace(err) + } + se.sessionVars.CommonGlobalLoaded = true + se.sessionVars.InRestrictedSQL = true + // Internal session uses default format to prevent memory leak problem. + se.sessionVars.EnableChunkRPC = false + return se, nil + } +} + +func drainRecordSet(ctx context.Context, se *session, rs sqlexec.RecordSet, alloc chunk.Allocator) ([]chunk.Row, error) { + var rows []chunk.Row + var req *chunk.Chunk + req = rs.NewChunk(alloc) + for { + err := rs.Next(ctx, req) + if err != nil || req.NumRows() == 0 { + return rows, err + } + iter := chunk.NewIterator4Chunk(req) + for r := iter.Begin(); r != iter.End(); r = iter.Next() { + rows = append(rows, r) + } + req = chunk.Renew(req, se.sessionVars.MaxChunkSize) + } +} + +// getTableValue executes restricted sql and the result is one column. +// It returns a string value. +func (s *session) getTableValue(ctx context.Context, tblName string, varName string) (string, error) { + if ctx.Value(kv.RequestSourceKey) == nil { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnSysVar) + } + rows, fields, err := s.ExecRestrictedSQL(ctx, nil, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?", mysql.SystemDB, tblName, varName) + if err != nil { + return "", err + } + if len(rows) == 0 { + return "", errResultIsEmpty + } + d := rows[0].GetDatum(0, &fields[0].Column.FieldType) + value, err := d.ToString() + if err != nil { + return "", err + } + return value, nil +} + +// replaceGlobalVariablesTableValue executes restricted sql updates the variable value +// It will then notify the etcd channel that the value has changed. +func (s *session) replaceGlobalVariablesTableValue(ctx context.Context, varName, val string, updateLocal bool) error { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnSysVar) + _, _, err := s.ExecRestrictedSQL(ctx, nil, `REPLACE INTO %n.%n (variable_name, variable_value) VALUES (%?, %?)`, mysql.SystemDB, mysql.GlobalVariablesTable, varName, val) + if err != nil { + return err + } + domain.GetDomain(s).NotifyUpdateSysVarCache(updateLocal) + return err +} + +// GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface. +func (s *session) GetGlobalSysVar(name string) (string, error) { + if s.Value(sessionctx.Initing) != nil { + // When running bootstrap or upgrade, we should not access global storage. + return "", nil + } + + sv := variable.GetSysVar(name) + if sv == nil { + // It might be a recently unregistered sysvar. We should return unknown + // since GetSysVar is the canonical version, but we can update the cache + // so the next request doesn't attempt to load this. + logutil.BgLogger().Info("sysvar does not exist. sysvar cache may be stale", zap.String("name", name)) + return "", variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + + sysVar, err := domain.GetDomain(s).GetGlobalVar(name) + if err != nil { + // The sysvar exists, but there is no cache entry yet. + // This might be because the sysvar was only recently registered. + // In which case it is safe to return the default, but we can also + // update the cache for the future. + logutil.BgLogger().Info("sysvar not in cache yet. sysvar cache may be stale", zap.String("name", name)) + sysVar, err = s.getTableValue(context.TODO(), mysql.GlobalVariablesTable, name) + if err != nil { + return sv.Value, nil + } + } + // It might have been written from an earlier TiDB version, so we should do type validation + // See https://github.com/pingcap/tidb/issues/30255 for why we don't do full validation. + // If validation fails, we should return the default value: + // See: https://github.com/pingcap/tidb/pull/31566 + sysVar, err = sv.ValidateFromType(s.GetSessionVars(), sysVar, variable.ScopeGlobal) + if err != nil { + return sv.Value, nil + } + return sysVar, nil +} + +// SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface. +// it is called (but skipped) when setting instance scope +func (s *session) SetGlobalSysVar(ctx context.Context, name string, value string) (err error) { + sv := variable.GetSysVar(name) + if sv == nil { + return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + if value, err = sv.Validate(s.sessionVars, value, variable.ScopeGlobal); err != nil { + return err + } + if err = sv.SetGlobalFromHook(ctx, s.sessionVars, value, false); err != nil { + return err + } + if sv.HasInstanceScope() { // skip for INSTANCE scope + return nil + } + if sv.GlobalConfigName != "" { + domain.GetDomain(s).NotifyGlobalConfigChange(sv.GlobalConfigName, variable.OnOffToTrueFalse(value)) + } + return s.replaceGlobalVariablesTableValue(context.TODO(), sv.Name, value, true) +} + +// SetGlobalSysVarOnly updates the sysvar, but does not call the validation function or update aliases. +// This is helpful to prevent duplicate warnings being appended from aliases, or recursion. +// updateLocal indicates whether to rebuild the local SysVar Cache. This is helpful to prevent recursion. +func (s *session) SetGlobalSysVarOnly(ctx context.Context, name string, value string, updateLocal bool) (err error) { + sv := variable.GetSysVar(name) + if sv == nil { + return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + if err = sv.SetGlobalFromHook(ctx, s.sessionVars, value, true); err != nil { + return err + } + if sv.HasInstanceScope() { // skip for INSTANCE scope + return nil + } + return s.replaceGlobalVariablesTableValue(ctx, sv.Name, value, updateLocal) +} + +// SetTiDBTableValue implements GlobalVarAccessor.SetTiDBTableValue interface. +func (s *session) SetTiDBTableValue(name, value, comment string) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnSysVar) + _, _, err := s.ExecRestrictedSQL(ctx, nil, `REPLACE INTO mysql.tidb (variable_name, variable_value, comment) VALUES (%?, %?, %?)`, name, value, comment) + return err +} + +// GetTiDBTableValue implements GlobalVarAccessor.GetTiDBTableValue interface. +func (s *session) GetTiDBTableValue(name string) (string, error) { + return s.getTableValue(context.TODO(), mysql.TiDBTable, name) +} + +var _ sqlexec.SQLParser = &session{} + +func (s *session) ParseSQL(ctx context.Context, sql string, params ...parser.ParseParam) ([]ast.StmtNode, []error, error) { + defer tracing.StartRegion(ctx, "ParseSQL").End() + + p := parserPool.Get().(*parser.Parser) + defer parserPool.Put(p) + + sqlMode := s.sessionVars.SQLMode + if s.isInternal() { + sqlMode = mysql.DelSQLMode(sqlMode, mysql.ModeNoBackslashEscapes) + } + p.SetSQLMode(sqlMode) + p.SetParserConfig(s.sessionVars.BuildParserConfig()) + tmp, warn, err := p.ParseSQL(sql, params...) + // The []ast.StmtNode is referenced by the parser, to reuse the parser, make a copy of the result. + res := make([]ast.StmtNode, len(tmp)) + copy(res, tmp) + return res, warn, err +} + +func (s *session) SetProcessInfo(sql string, t time.Time, command byte, maxExecutionTime uint64) { + // If command == mysql.ComSleep, it means the SQL execution is finished. The processinfo is reset to SLEEP. + // If the SQL finished and the session is not in transaction, the current start timestamp need to reset to 0. + // Otherwise, it should be set to the transaction start timestamp. + // Why not reset the transaction start timestamp to 0 when transaction committed? + // Because the select statement and other statements need this timestamp to read data, + // after the transaction is committed. e.g. SHOW MASTER STATUS; + var curTxnStartTS uint64 + var curTxnCreateTime time.Time + if command != mysql.ComSleep || s.GetSessionVars().InTxn() { + curTxnStartTS = s.sessionVars.TxnCtx.StartTS + curTxnCreateTime = s.sessionVars.TxnCtx.CreateTime + } + // Set curTxnStartTS to SnapshotTS directly when the session is trying to historic read. + // It will avoid the session meet GC lifetime too short error. + if s.GetSessionVars().SnapshotTS != 0 { + curTxnStartTS = s.GetSessionVars().SnapshotTS + } + p := s.currentPlan + if explain, ok := p.(*plannercore.Explain); ok && explain.Analyze && explain.TargetPlan != nil { + p = explain.TargetPlan + } + + pi := util.ProcessInfo{ + ID: s.sessionVars.ConnectionID, + Port: s.sessionVars.Port, + DB: s.sessionVars.CurrentDB, + Command: command, + Plan: p, + PlanExplainRows: plannercore.GetExplainRowsForPlan(p), + RuntimeStatsColl: s.sessionVars.StmtCtx.RuntimeStatsColl, + Time: t, + State: s.Status(), + Info: sql, + CurTxnStartTS: curTxnStartTS, + CurTxnCreateTime: curTxnCreateTime, + StmtCtx: s.sessionVars.StmtCtx, + RefCountOfStmtCtx: &s.sessionVars.RefCountOfStmtCtx, + MemTracker: s.sessionVars.MemTracker, + DiskTracker: s.sessionVars.DiskTracker, + StatsInfo: plannercore.GetStatsInfo, + OOMAlarmVariablesInfo: s.getOomAlarmVariablesInfo(), + TableIDs: s.sessionVars.StmtCtx.TableIDs, + IndexNames: s.sessionVars.StmtCtx.IndexNames, + MaxExecutionTime: maxExecutionTime, + RedactSQL: s.sessionVars.EnableRedactLog, + ResourceGroupName: s.sessionVars.ResourceGroupName, + SessionAlias: s.sessionVars.SessionAlias, + } + oldPi := s.ShowProcess() + if p == nil { + // Store the last valid plan when the current plan is nil. + // This is for `explain for connection` statement has the ability to query the last valid plan. + if oldPi != nil && oldPi.Plan != nil && len(oldPi.PlanExplainRows) > 0 { + pi.Plan = oldPi.Plan + pi.PlanExplainRows = oldPi.PlanExplainRows + pi.RuntimeStatsColl = oldPi.RuntimeStatsColl + } + } + // We set process info before building plan, so we extended execution time. + if oldPi != nil && oldPi.Info == pi.Info && oldPi.Command == pi.Command { + pi.Time = oldPi.Time + } + if oldPi != nil && oldPi.CurTxnStartTS != 0 && oldPi.CurTxnStartTS == pi.CurTxnStartTS { + // Keep the last expensive txn log time, avoid print too many expensive txn logs. + pi.ExpensiveTxnLogTime = oldPi.ExpensiveTxnLogTime + } + _, digest := s.sessionVars.StmtCtx.SQLDigest() + pi.Digest = digest.String() + // DO NOT reset the currentPlan to nil until this query finishes execution, otherwise reentrant calls + // of SetProcessInfo would override Plan and PlanExplainRows to nil. + if command == mysql.ComSleep { + s.currentPlan = nil + } + if s.sessionVars.User != nil { + pi.User = s.sessionVars.User.Username + pi.Host = s.sessionVars.User.Hostname + } + s.processInfo.Store(&pi) +} + +// UpdateProcessInfo updates the session's process info for the running statement. +func (s *session) UpdateProcessInfo() { + pi := s.ShowProcess() + if pi == nil || pi.CurTxnStartTS != 0 { + return + } + // Update the current transaction start timestamp. + pi.CurTxnStartTS = s.sessionVars.TxnCtx.StartTS + pi.CurTxnCreateTime = s.sessionVars.TxnCtx.CreateTime +} + +func (s *session) getOomAlarmVariablesInfo() util.OOMAlarmVariablesInfo { + return util.OOMAlarmVariablesInfo{ + SessionAnalyzeVersion: s.sessionVars.AnalyzeVersion, + SessionEnabledRateLimitAction: s.sessionVars.EnabledRateLimitAction, + SessionMemQuotaQuery: s.sessionVars.MemQuotaQuery, + } +} + +func (s *session) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { + s.diskFullOpt = level +} + +func (s *session) GetDiskFullOpt() kvrpcpb.DiskFullOpt { + return s.diskFullOpt +} + +func (s *session) ClearDiskFullOpt() { + s.diskFullOpt = kvrpcpb.DiskFullOpt_NotAllowedOnFull +} + +func (s *session) ExecuteInternal(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, err error) { + origin := s.sessionVars.InRestrictedSQL + s.sessionVars.InRestrictedSQL = true + defer func() { + s.sessionVars.InRestrictedSQL = origin + // Restore the goroutine label by using the original ctx after execution is finished. + pprof.SetGoroutineLabels(ctx) + }() + + r, ctx := tracing.StartRegionEx(ctx, "session.ExecuteInternal") + defer r.End() + logutil.Eventf(ctx, "execute: %s", sql) + + stmtNode, err := s.ParseWithParams(ctx, sql, args...) + if err != nil { + return nil, err + } + + rs, err = s.ExecuteStmt(ctx, stmtNode) + if err != nil { + s.sessionVars.StmtCtx.AppendError(err) + } + if rs == nil { + return nil, err + } + + return rs, err +} + +// Execute is deprecated, we can remove it as soon as plugins are migrated. +func (s *session) Execute(ctx context.Context, sql string) (recordSets []sqlexec.RecordSet, err error) { + r, ctx := tracing.StartRegionEx(ctx, "session.Execute") + defer r.End() + logutil.Eventf(ctx, "execute: %s", sql) + + stmtNodes, err := s.Parse(ctx, sql) + if err != nil { + return nil, err + } + if len(stmtNodes) != 1 { + return nil, errors.New("Execute() API doesn't support multiple statements any more") + } + + rs, err := s.ExecuteStmt(ctx, stmtNodes[0]) + if err != nil { + s.sessionVars.StmtCtx.AppendError(err) + } + if rs == nil { + return nil, err + } + return []sqlexec.RecordSet{rs}, err +} + +// Parse parses a query string to raw ast.StmtNode. +func (s *session) Parse(ctx context.Context, sql string) ([]ast.StmtNode, error) { + logutil.Logger(ctx).Debug("parse", zap.String("sql", sql)) + parseStartTime := time.Now() + stmts, warns, err := s.ParseSQL(ctx, sql, s.sessionVars.GetParseParams()...) + if err != nil { + s.rollbackOnError(ctx) + err = util.SyntaxError(err) + + // Only print log message when this SQL is from the user. + // Mute the warning for internal SQLs. + if !s.sessionVars.InRestrictedSQL { + if s.sessionVars.EnableRedactLog { + logutil.Logger(ctx).Debug("parse SQL failed", zap.Error(err), zap.String("SQL", sql)) + } else { + logutil.Logger(ctx).Warn("parse SQL failed", zap.Error(err), zap.String("SQL", sql)) + } + s.sessionVars.StmtCtx.AppendError(err) + } + return nil, err + } + + durParse := time.Since(parseStartTime) + s.GetSessionVars().DurationParse = durParse + isInternal := s.isInternal() + if isInternal { + session_metrics.SessionExecuteParseDurationInternal.Observe(durParse.Seconds()) + } else { + session_metrics.SessionExecuteParseDurationGeneral.Observe(durParse.Seconds()) + } + for _, warn := range warns { + s.sessionVars.StmtCtx.AppendWarning(util.SyntaxWarn(warn)) + } + return stmts, nil +} + +// ParseWithParams parses a query string, with arguments, to raw ast.StmtNode. +// Note that it will not do escaping if no variable arguments are passed. +func (s *session) ParseWithParams(ctx context.Context, sql string, args ...interface{}) (ast.StmtNode, error) { + var err error + if len(args) > 0 { + sql, err = sqlexec.EscapeSQL(sql, args...) + if err != nil { + return nil, err + } + } + + internal := s.isInternal() + + var stmts []ast.StmtNode + var warns []error + parseStartTime := time.Now() + if internal { + // Do no respect the settings from clients, if it is for internal usage. + // Charsets from clients may give chance injections. + // Refer to https://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string/12118602. + stmts, warns, err = s.ParseSQL(ctx, sql) + } else { + stmts, warns, err = s.ParseSQL(ctx, sql, s.sessionVars.GetParseParams()...) + } + if len(stmts) != 1 && err == nil { + err = errors.New("run multiple statements internally is not supported") + } + if err != nil { + s.rollbackOnError(ctx) + logSQL := sql[:mathutil.Min(500, len(sql))] + if s.sessionVars.EnableRedactLog { + logutil.Logger(ctx).Debug("parse SQL failed", zap.Error(err), zap.String("SQL", logSQL)) + } else { + logutil.Logger(ctx).Warn("parse SQL failed", zap.Error(err), zap.String("SQL", logSQL)) + } + return nil, util.SyntaxError(err) + } + durParse := time.Since(parseStartTime) + if internal { + session_metrics.SessionExecuteParseDurationInternal.Observe(durParse.Seconds()) + } else { + session_metrics.SessionExecuteParseDurationGeneral.Observe(durParse.Seconds()) + } + for _, warn := range warns { + s.sessionVars.StmtCtx.AppendWarning(util.SyntaxWarn(warn)) + } + if topsqlstate.TopSQLEnabled() { + normalized, digest := parser.NormalizeDigest(sql) + if digest != nil { + // Reset the goroutine label when internal sql execute finish. + // Specifically reset in ExecRestrictedStmt function. + s.sessionVars.StmtCtx.IsSQLRegistered.Store(true) + topsql.AttachAndRegisterSQLInfo(ctx, normalized, digest, s.sessionVars.InRestrictedSQL) + } + } + return stmts[0], nil +} + +// GetAdvisoryLock acquires an advisory lock of lockName. +// Note that a lock can be acquired multiple times by the same session, +// in which case we increment a reference count. +// Each lock needs to be held in a unique session because +// we need to be able to ROLLBACK in any arbitrary order +// in order to release the locks. +func (s *session) GetAdvisoryLock(lockName string, timeout int64) error { + if lock, ok := s.advisoryLocks[lockName]; ok { + lock.IncrReferences() + return nil + } + sess, err := createSession(s.store) + if err != nil { + return err + } + infosync.StoreInternalSession(sess) + lock := &advisoryLock{session: sess, ctx: context.TODO(), owner: s.ShowProcess().ID} + err = lock.GetLock(lockName, timeout) + if err != nil { + return err + } + s.advisoryLocks[lockName] = lock + return nil +} + +// IsUsedAdvisoryLock checks if a lockName is already in use +func (s *session) IsUsedAdvisoryLock(lockName string) uint64 { + // Same session + if lock, ok := s.advisoryLocks[lockName]; ok { + return lock.owner + } + + // Check for transaction on advisory_locks table + sess, err := createSession(s.store) + if err != nil { + return 0 + } + lock := &advisoryLock{session: sess, ctx: context.TODO(), owner: s.ShowProcess().ID} + err = lock.IsUsedLock(lockName) + if err != nil { + // TODO: Return actual owner pid + // TODO: Check for mysql.ErrLockWaitTimeout and DeadLock + return 1 + } + return 0 +} + +// ReleaseAdvisoryLock releases an advisory locks held by the session. +// It returns FALSE if no lock by this name was held (by this session), +// and TRUE if a lock was held and "released". +// Note that the lock is not actually released if there are multiple +// references to the same lockName by the session, instead the reference +// count is decremented. +func (s *session) ReleaseAdvisoryLock(lockName string) (released bool) { + if lock, ok := s.advisoryLocks[lockName]; ok { + lock.DecrReferences() + if lock.ReferenceCount() <= 0 { + lock.Close() + delete(s.advisoryLocks, lockName) + infosync.DeleteInternalSession(lock.session) + } + return true + } + return false +} + +// ReleaseAllAdvisoryLocks releases all advisory locks held by the session +// and returns a count of the locks that were released. +// The count is based on unique locks held, so multiple references +// to the same lock do not need to be accounted for. +func (s *session) ReleaseAllAdvisoryLocks() int { + var count int + for lockName, lock := range s.advisoryLocks { + lock.Close() + count += lock.ReferenceCount() + delete(s.advisoryLocks, lockName) + infosync.DeleteInternalSession(lock.session) + } + return count +} + +// GetExtensions returns the `*extension.SessionExtensions` object +func (s *session) GetExtensions() *extension.SessionExtensions { + return s.extensions +} + +// SetExtensions sets the `*extension.SessionExtensions` object +func (s *session) SetExtensions(extensions *extension.SessionExtensions) { + s.extensions = extensions +} + +// InSandBoxMode indicates that this session is in sandbox mode +func (s *session) InSandBoxMode() bool { + return s.sandBoxMode +} + +// EnableSandBoxMode enable the sandbox mode. +func (s *session) EnableSandBoxMode() { + s.sandBoxMode = true +} + +// DisableSandBoxMode enable the sandbox mode. +func (s *session) DisableSandBoxMode() { + s.sandBoxMode = false +} + +// ParseWithParams4Test wrapper (s *session) ParseWithParams for test +func ParseWithParams4Test(ctx context.Context, s Session, + sql string, args ...interface{}) (ast.StmtNode, error) { + return s.(*session).ParseWithParams(ctx, sql, args) +} + +var _ sqlexec.RestrictedSQLExecutor = &session{} +var _ sqlexec.SQLExecutor = &session{} + +// ExecRestrictedStmt implements RestrictedSQLExecutor interface. +func (s *session) ExecRestrictedStmt(ctx context.Context, stmtNode ast.StmtNode, opts ...sqlexec.OptionFuncAlias) ( + []chunk.Row, []*ast.ResultField, error) { + defer pprof.SetGoroutineLabels(ctx) + execOption := sqlexec.GetExecOption(opts) + var se *session + var clean func() + var err error + if execOption.UseCurSession { + se, clean, err = s.useCurrentSession(execOption) + } else { + se, clean, err = s.getInternalSession(execOption) + } + if err != nil { + return nil, nil, err + } + defer clean() + + startTime := time.Now() + metrics.SessionRestrictedSQLCounter.Inc() + ctx = context.WithValue(ctx, execdetails.StmtExecDetailKey, &execdetails.StmtExecDetails{}) + ctx = context.WithValue(ctx, tikvutil.ExecDetailsKey, &tikvutil.ExecDetails{}) + rs, err := se.ExecuteStmt(ctx, stmtNode) + if err != nil { + se.sessionVars.StmtCtx.AppendError(err) + } + if rs == nil { + return nil, nil, err + } + defer func() { + if closeErr := rs.Close(); closeErr != nil { + err = closeErr + } + }() + var rows []chunk.Row + rows, err = drainRecordSet(ctx, se, rs, nil) + if err != nil { + return nil, nil, err + } + + vars := se.GetSessionVars() + for _, dbName := range GetDBNames(vars) { + metrics.QueryDurationHistogram.WithLabelValues(metrics.LblInternal, dbName, vars.ResourceGroupName).Observe(time.Since(startTime).Seconds()) + } + return rows, rs.Fields(), err +} + +// ExecRestrictedStmt4Test wrapper `(s *session) ExecRestrictedStmt` for test. +func ExecRestrictedStmt4Test(ctx context.Context, s Session, + stmtNode ast.StmtNode, opts ...sqlexec.OptionFuncAlias) ( + []chunk.Row, []*ast.ResultField, error) { + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) + return s.(*session).ExecRestrictedStmt(ctx, stmtNode, opts...) +} + +// only set and clean session with execOption +func (s *session) useCurrentSession(execOption sqlexec.ExecOption) (*session, func(), error) { + var err error + orgSnapshotInfoSchema, orgSnapshotTS := s.sessionVars.SnapshotInfoschema, s.sessionVars.SnapshotTS + if execOption.SnapshotTS != 0 { + if err = s.sessionVars.SetSystemVar(variable.TiDBSnapshot, strconv.FormatUint(execOption.SnapshotTS, 10)); err != nil { + return nil, nil, err + } + s.sessionVars.SnapshotInfoschema, err = getSnapshotInfoSchema(s, execOption.SnapshotTS) + if err != nil { + return nil, nil, err + } + } + prevStatsVer := s.sessionVars.AnalyzeVersion + if execOption.AnalyzeVer != 0 { + s.sessionVars.AnalyzeVersion = execOption.AnalyzeVer + } + prevAnalyzeSnapshot := s.sessionVars.EnableAnalyzeSnapshot + if execOption.AnalyzeSnapshot != nil { + s.sessionVars.EnableAnalyzeSnapshot = *execOption.AnalyzeSnapshot + } + prePruneMode := s.sessionVars.PartitionPruneMode.Load() + if len(execOption.PartitionPruneMode) > 0 { + s.sessionVars.PartitionPruneMode.Store(execOption.PartitionPruneMode) + } + prevSQL := s.sessionVars.StmtCtx.OriginalSQL + prevStmtType := s.sessionVars.StmtCtx.StmtType + prevTables := s.sessionVars.StmtCtx.Tables + return s, func() { + s.sessionVars.AnalyzeVersion = prevStatsVer + s.sessionVars.EnableAnalyzeSnapshot = prevAnalyzeSnapshot + if err := s.sessionVars.SetSystemVar(variable.TiDBSnapshot, ""); err != nil { + logutil.BgLogger().Error("set tidbSnapshot error", zap.Error(err)) + } + s.sessionVars.SnapshotInfoschema = orgSnapshotInfoSchema + s.sessionVars.SnapshotTS = orgSnapshotTS + s.sessionVars.PartitionPruneMode.Store(prePruneMode) + s.sessionVars.StmtCtx.OriginalSQL = prevSQL + s.sessionVars.StmtCtx.StmtType = prevStmtType + s.sessionVars.StmtCtx.Tables = prevTables + s.sessionVars.MemTracker.Detach() + }, nil +} + +func (s *session) getInternalSession(execOption sqlexec.ExecOption) (*session, func(), error) { + tmp, err := s.sysSessionPool().Get() + if err != nil { + return nil, nil, errors.Trace(err) + } + se := tmp.(*session) + + // The special session will share the `InspectionTableCache` with current session + // if the current session in inspection mode. + if cache := s.sessionVars.InspectionTableCache; cache != nil { + se.sessionVars.InspectionTableCache = cache + } + if ok := s.sessionVars.OptimizerUseInvisibleIndexes; ok { + se.sessionVars.OptimizerUseInvisibleIndexes = true + } + + if execOption.SnapshotTS != 0 { + if err := se.sessionVars.SetSystemVar(variable.TiDBSnapshot, strconv.FormatUint(execOption.SnapshotTS, 10)); err != nil { + return nil, nil, err + } + se.sessionVars.SnapshotInfoschema, err = getSnapshotInfoSchema(s, execOption.SnapshotTS) + if err != nil { + return nil, nil, err + } + } + + prevStatsVer := se.sessionVars.AnalyzeVersion + if execOption.AnalyzeVer != 0 { + se.sessionVars.AnalyzeVersion = execOption.AnalyzeVer + } + + prevAnalyzeSnapshot := se.sessionVars.EnableAnalyzeSnapshot + if execOption.AnalyzeSnapshot != nil { + se.sessionVars.EnableAnalyzeSnapshot = *execOption.AnalyzeSnapshot + } + + prePruneMode := se.sessionVars.PartitionPruneMode.Load() + if len(execOption.PartitionPruneMode) > 0 { + se.sessionVars.PartitionPruneMode.Store(execOption.PartitionPruneMode) + } + + return se, func() { + se.sessionVars.AnalyzeVersion = prevStatsVer + se.sessionVars.EnableAnalyzeSnapshot = prevAnalyzeSnapshot + if err := se.sessionVars.SetSystemVar(variable.TiDBSnapshot, ""); err != nil { + logutil.BgLogger().Error("set tidbSnapshot error", zap.Error(err)) + } + se.sessionVars.SnapshotInfoschema = nil + se.sessionVars.SnapshotTS = 0 + if !execOption.IgnoreWarning { + if se != nil && se.GetSessionVars().StmtCtx.WarningCount() > 0 { + warnings := se.GetSessionVars().StmtCtx.GetWarnings() + s.GetSessionVars().StmtCtx.AppendWarnings(warnings) + } + } + se.sessionVars.PartitionPruneMode.Store(prePruneMode) + se.sessionVars.OptimizerUseInvisibleIndexes = false + se.sessionVars.InspectionTableCache = nil + se.sessionVars.MemTracker.Detach() + s.sysSessionPool().Put(tmp) + }, nil +} + +func (s *session) withRestrictedSQLExecutor(ctx context.Context, opts []sqlexec.OptionFuncAlias, fn func(context.Context, *session) ([]chunk.Row, []*ast.ResultField, error)) ([]chunk.Row, []*ast.ResultField, error) { + execOption := sqlexec.GetExecOption(opts) + var se *session + var clean func() + var err error + if execOption.UseCurSession { + se, clean, err = s.useCurrentSession(execOption) + } else { + se, clean, err = s.getInternalSession(execOption) + } + if err != nil { + return nil, nil, errors.Trace(err) + } + defer clean() + if execOption.TrackSysProcID > 0 { + err = execOption.TrackSysProc(execOption.TrackSysProcID, se) + if err != nil { + return nil, nil, errors.Trace(err) + } + // unTrack should be called before clean (return sys session) + defer execOption.UnTrackSysProc(execOption.TrackSysProcID) + } + return fn(ctx, se) +} + +func (s *session) ExecRestrictedSQL(ctx context.Context, opts []sqlexec.OptionFuncAlias, sql string, params ...interface{}) ([]chunk.Row, []*ast.ResultField, error) { + return s.withRestrictedSQLExecutor(ctx, opts, func(ctx context.Context, se *session) ([]chunk.Row, []*ast.ResultField, error) { + stmt, err := se.ParseWithParams(ctx, sql, params...) + if err != nil { + return nil, nil, errors.Trace(err) + } + defer pprof.SetGoroutineLabels(ctx) + startTime := time.Now() + metrics.SessionRestrictedSQLCounter.Inc() + ctx = context.WithValue(ctx, execdetails.StmtExecDetailKey, &execdetails.StmtExecDetails{}) + ctx = context.WithValue(ctx, tikvutil.ExecDetailsKey, &tikvutil.ExecDetails{}) + rs, err := se.ExecuteInternalStmt(ctx, stmt) + if err != nil { + se.sessionVars.StmtCtx.AppendError(err) + } + if rs == nil { + return nil, nil, err + } + defer func() { + if closeErr := rs.Close(); closeErr != nil { + err = closeErr + } + }() + var rows []chunk.Row + rows, err = drainRecordSet(ctx, se, rs, nil) + if err != nil { + return nil, nil, err + } + + vars := se.GetSessionVars() + for _, dbName := range GetDBNames(vars) { + metrics.QueryDurationHistogram.WithLabelValues(metrics.LblInternal, dbName, vars.ResourceGroupName).Observe(time.Since(startTime).Seconds()) + } + return rows, rs.Fields(), err + }) +} + +// ExecuteInternalStmt execute internal stmt +func (s *session) ExecuteInternalStmt(ctx context.Context, stmtNode ast.StmtNode) (sqlexec.RecordSet, error) { + origin := s.sessionVars.InRestrictedSQL + s.sessionVars.InRestrictedSQL = true + defer func() { + s.sessionVars.InRestrictedSQL = origin + // Restore the goroutine label by using the original ctx after execution is finished. + pprof.SetGoroutineLabels(ctx) + }() + return s.ExecuteStmt(ctx, stmtNode) +} + +func (s *session) ExecuteStmt(ctx context.Context, stmtNode ast.StmtNode) (sqlexec.RecordSet, error) { + r, ctx := tracing.StartRegionEx(ctx, "session.ExecuteStmt") + defer r.End() + + if err := s.PrepareTxnCtx(ctx); err != nil { + return nil, err + } + if err := s.loadCommonGlobalVariablesIfNeeded(); err != nil { + return nil, err + } + + sessVars := s.sessionVars + sessVars.StartTime = time.Now() + + // Some executions are done in compile stage, so we reset them before compile. + if err := executor.ResetContextOfStmt(s, stmtNode); err != nil { + return nil, err + } + normalizedSQL, digest := s.sessionVars.StmtCtx.SQLDigest() + cmdByte := byte(atomic.LoadUint32(&s.GetSessionVars().CommandValue)) + if topsqlstate.TopSQLEnabled() { + s.sessionVars.StmtCtx.IsSQLRegistered.Store(true) + ctx = topsql.AttachAndRegisterSQLInfo(ctx, normalizedSQL, digest, s.sessionVars.InRestrictedSQL) + } + if sessVars.InPlanReplayer { + sessVars.StmtCtx.EnableOptimizerDebugTrace = true + } else if dom := domain.GetDomain(s); dom != nil && !sessVars.InRestrictedSQL { + // This is the earliest place we can get the SQL digest for this execution. + // If we find this digest is registered for PLAN REPLAYER CAPTURE, we need to enable optimizer debug trace no matter + // the plan digest will be matched or not. + if planReplayerHandle := dom.GetPlanReplayerHandle(); planReplayerHandle != nil { + tasks := planReplayerHandle.GetTasks() + for _, task := range tasks { + if task.SQLDigest == digest.String() { + sessVars.StmtCtx.EnableOptimizerDebugTrace = true + } + } + } + } + if sessVars.StmtCtx.EnableOptimizerDebugTrace { + plannercore.DebugTraceReceivedCommand(s, cmdByte, stmtNode) + } + + if err := s.validateStatementInTxn(stmtNode); err != nil { + return nil, err + } + + if err := s.validateStatementReadOnlyInStaleness(stmtNode); err != nil { + return nil, err + } + + // Uncorrelated subqueries will execute once when building plan, so we reset process info before building plan. + s.currentPlan = nil // reset current plan + s.SetProcessInfo(stmtNode.Text(), time.Now(), cmdByte, 0) + s.txn.onStmtStart(digest.String()) + defer sessiontxn.GetTxnManager(s).OnStmtEnd() + defer s.txn.onStmtEnd() + + if err := s.onTxnManagerStmtStartOrRetry(ctx, stmtNode); err != nil { + return nil, err + } + + failpoint.Inject("mockStmtSlow", func(val failpoint.Value) { + if strings.Contains(stmtNode.Text(), "/* sleep */") { + v, _ := val.(int) + time.Sleep(time.Duration(v) * time.Millisecond) + } + }) + + var stmtLabel string + if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { + prepareStmt, err := plannercore.GetPreparedStmt(execStmt, s.sessionVars) + if err == nil && prepareStmt.PreparedAst != nil { + stmtLabel = ast.GetStmtLabel(prepareStmt.PreparedAst.Stmt) + } + } + if stmtLabel == "" { + stmtLabel = ast.GetStmtLabel(stmtNode) + } + s.setRequestSource(ctx, stmtLabel, stmtNode) + + // Backup the original resource group name since sql hint might change it during optimization + originalResourceGroup := s.GetSessionVars().ResourceGroupName + + // Transform abstract syntax tree to a physical plan(stored in executor.ExecStmt). + compiler := executor.Compiler{Ctx: s} + stmt, err := compiler.Compile(ctx, stmtNode) + // session resource-group might be changed by query hint, ensure restore it back when + // the execution finished. + if sessVars.ResourceGroupName != originalResourceGroup { + // if target resource group doesn't exist, fallback to the origin resource group. + if _, ok := domain.GetDomain(s).InfoSchema().ResourceGroupByName(model.NewCIStr(sessVars.ResourceGroupName)); !ok { + logutil.Logger(ctx).Warn("Unknown resource group from hint", zap.String("name", sessVars.ResourceGroupName)) + sessVars.ResourceGroupName = originalResourceGroup + // if we are in a txn, should also reset the txn resource group. + if txn, err := s.Txn(false); err == nil && txn != nil && txn.Valid() { + kv.SetTxnResourceGroup(txn, originalResourceGroup) + } + } else { + defer func() { + // Restore the resource group for the session + sessVars.ResourceGroupName = originalResourceGroup + }() + } + } + if err != nil { + s.rollbackOnError(ctx) + + // Only print log message when this SQL is from the user. + // Mute the warning for internal SQLs. + if !s.sessionVars.InRestrictedSQL { + if !variable.ErrUnknownSystemVar.Equal(err) { + sql := stmtNode.Text() + if s.sessionVars.EnableRedactLog { + sql = parser.Normalize(sql) + } + logutil.Logger(ctx).Warn("compile SQL failed", zap.Error(err), + zap.String("SQL", sql)) + } + } + return nil, err + } + + durCompile := time.Since(s.sessionVars.StartTime) + s.GetSessionVars().DurationCompile = durCompile + if s.isInternal() { + session_metrics.SessionExecuteCompileDurationInternal.Observe(durCompile.Seconds()) + } else { + session_metrics.SessionExecuteCompileDurationGeneral.Observe(durCompile.Seconds()) + } + s.currentPlan = stmt.Plan + if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { + if execStmt.Name == "" { + // for exec-stmt on bin-protocol, ignore the plan detail in `show process` to gain performance benefits. + s.currentPlan = nil + } + } + + // Execute the physical plan. + logStmt(stmt, s) + + var recordSet sqlexec.RecordSet + if stmt.PsStmt != nil { // point plan short path + recordSet, err = stmt.PointGet(ctx) + s.txn.changeToInvalid() + } else { + recordSet, err = runStmt(ctx, s, stmt) + } + + // Observe the resource group query total counter if the resource control is enabled and the + // current session is attached with a resource group. + resourceGroupName := s.GetSessionVars().ResourceGroupName + if len(resourceGroupName) > 0 { + metrics.ResourceGroupQueryTotalCounter.WithLabelValues(resourceGroupName).Inc() + } + + if err != nil { + if !errIsNoisy(err) { + logutil.Logger(ctx).Warn("run statement failed", + zap.Int64("schemaVersion", s.GetInfoSchema().SchemaMetaVersion()), + zap.Error(err), + zap.String("session", s.String())) + } + return recordSet, err + } + if !s.isInternal() && config.GetGlobalConfig().EnableTelemetry { + telemetry.CurrentExecuteCount.Inc() + tiFlashPushDown, tiFlashExchangePushDown := plannercore.IsTiFlashContained(stmt.Plan) + if tiFlashPushDown { + telemetry.CurrentTiFlashPushDownCount.Inc() + } + if tiFlashExchangePushDown { + telemetry.CurrentTiFlashExchangePushDownCount.Inc() + } + } + return recordSet, nil +} + +func (s *session) onTxnManagerStmtStartOrRetry(ctx context.Context, node ast.StmtNode) error { + if s.sessionVars.RetryInfo.Retrying { + return sessiontxn.GetTxnManager(s).OnStmtRetry(ctx) + } + return sessiontxn.GetTxnManager(s).OnStmtStart(ctx, node) +} + +func (s *session) validateStatementInTxn(stmtNode ast.StmtNode) error { + vars := s.GetSessionVars() + if _, ok := stmtNode.(*ast.ImportIntoStmt); ok && vars.InTxn() { + return errors.New("cannot run IMPORT INTO in explicit transaction") + } + return nil +} + +func (s *session) validateStatementReadOnlyInStaleness(stmtNode ast.StmtNode) error { + vars := s.GetSessionVars() + if !vars.TxnCtx.IsStaleness && vars.TxnReadTS.PeakTxnReadTS() == 0 && !vars.EnableExternalTSRead || vars.InRestrictedSQL { + return nil + } + errMsg := "only support read-only statement during read-only staleness transactions" + node := stmtNode.(ast.Node) + switch v := node.(type) { + case *ast.SplitRegionStmt: + return nil + case *ast.SelectStmt: + // select lock statement needs start a transaction which will be conflict to stale read, + // we forbid select lock statement in stale read for now. + if v.LockInfo != nil { + return errors.New("select lock hasn't been supported in stale read yet") + } + if !planner.IsReadOnly(stmtNode, vars) { + return errors.New(errMsg) + } + return nil + case *ast.ExplainStmt, *ast.DoStmt, *ast.ShowStmt, *ast.SetOprStmt, *ast.ExecuteStmt, *ast.SetOprSelectList: + if !planner.IsReadOnly(stmtNode, vars) { + return errors.New(errMsg) + } + return nil + default: + } + // covered DeleteStmt/InsertStmt/UpdateStmt/CallStmt/LoadDataStmt + if _, ok := stmtNode.(ast.DMLNode); ok { + return errors.New(errMsg) + } + return nil +} + +// fileTransInConnKeys contains the keys of queries that will be handled by handleFileTransInConn. +var fileTransInConnKeys = []fmt.Stringer{ + executor.LoadDataVarKey, + executor.LoadStatsVarKey, + executor.IndexAdviseVarKey, + executor.PlanReplayerLoadVarKey, +} + +func (s *session) hasFileTransInConn() bool { + s.mu.RLock() + defer s.mu.RUnlock() + + for _, k := range fileTransInConnKeys { + v := s.mu.values[k] + if v != nil { + return true + } + } + return false +} + +// runStmt executes the sqlexec.Statement and commit or rollback the current transaction. +func runStmt(ctx context.Context, se *session, s sqlexec.Statement) (rs sqlexec.RecordSet, err error) { + failpoint.Inject("assertTxnManagerInRunStmt", func() { + sessiontxn.RecordAssert(se, "assertTxnManagerInRunStmt", true) + if stmt, ok := s.(*executor.ExecStmt); ok { + sessiontxn.AssertTxnManagerInfoSchema(se, stmt.InfoSchema) + } + }) + + r, ctx := tracing.StartRegionEx(ctx, "session.runStmt") + defer r.End() + if r.Span != nil { + r.Span.LogKV("sql", s.OriginText()) + } + + se.SetValue(sessionctx.QueryString, s.OriginText()) + if _, ok := s.(*executor.ExecStmt).StmtNode.(ast.DDLNode); ok { + se.SetValue(sessionctx.LastExecuteDDL, true) + } else { + se.ClearValue(sessionctx.LastExecuteDDL) + } + + sessVars := se.sessionVars + + // Record diagnostic information for DML statements + if stmt, ok := s.(*executor.ExecStmt).StmtNode.(ast.DMLNode); ok { + // Keep the previous queryInfo for `show session_states` because the statement needs to encode it. + if showStmt, ok := stmt.(*ast.ShowStmt); !ok || showStmt.Tp != ast.ShowSessionStates { + defer func() { + sessVars.LastQueryInfo = sessionstates.QueryInfo{ + TxnScope: sessVars.CheckAndGetTxnScope(), + StartTS: sessVars.TxnCtx.StartTS, + ForUpdateTS: sessVars.TxnCtx.GetForUpdateTS(), + } + if err != nil { + sessVars.LastQueryInfo.ErrMsg = err.Error() + } + }() + } + } + + // Save origTxnCtx here to avoid it reset in the transaction retry. + origTxnCtx := sessVars.TxnCtx + err = se.checkTxnAborted(s) + if err != nil { + return nil, err + } + + rs, err = s.Exec(ctx) + se.updateTelemetryMetric(s.(*executor.ExecStmt)) + sessVars.TxnCtx.StatementCount++ + if rs != nil { + if se.GetSessionVars().StmtCtx.IsExplainAnalyzeDML { + if !sessVars.InTxn() { + se.StmtCommit(ctx) + if err := se.CommitTxn(ctx); err != nil { + return nil, err + } + } + } + return &execStmtResult{ + RecordSet: rs, + sql: s, + se: se, + }, err + } + + err = finishStmt(ctx, se, err, s) + if se.hasFileTransInConn() { + // The query will be handled later in handleFileTransInConn, + // then should call the ExecStmt.FinishExecuteStmt to finish this statement. + se.SetValue(ExecStmtVarKey, s.(*executor.ExecStmt)) + } else { + // If it is not a select statement or special query, we record its slow log here, + // then it could include the transaction commit time. + s.(*executor.ExecStmt).FinishExecuteStmt(origTxnCtx.StartTS, err, false) + } + return nil, err +} + +// ExecStmtVarKeyType is a dummy type to avoid naming collision in context. +type ExecStmtVarKeyType int + +// String defines a Stringer function for debugging and pretty printing. +func (ExecStmtVarKeyType) String() string { + return "exec_stmt_var_key" +} + +// ExecStmtVarKey is a variable key for ExecStmt. +const ExecStmtVarKey ExecStmtVarKeyType = 0 + +// execStmtResult is the return value of ExecuteStmt and it implements the sqlexec.RecordSet interface. +// Why we need a struct to wrap a RecordSet and provide another RecordSet? +// This is because there are so many session state related things that definitely not belongs to the original +// RecordSet, so this struct exists and RecordSet.Close() is overrided handle that. +type execStmtResult struct { + sqlexec.RecordSet + se *session + sql sqlexec.Statement +} + +func (rs *execStmtResult) Close() error { + se := rs.se + if err := rs.RecordSet.Close(); err != nil { + return finishStmt(context.Background(), se, err, rs.sql) + } + return finishStmt(context.Background(), se, nil, rs.sql) +} + +// rollbackOnError makes sure the next statement starts a new transaction with the latest InfoSchema. +func (s *session) rollbackOnError(ctx context.Context) { + if !s.sessionVars.InTxn() { + s.RollbackTxn(ctx) + } +} + +// PrepareStmt is used for executing prepare statement in binary protocol +func (s *session) PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) { + defer func() { + if s.sessionVars.StmtCtx != nil { + s.sessionVars.StmtCtx.DetachMemDiskTracker() + } + }() + if s.sessionVars.TxnCtx.InfoSchema == nil { + // We don't need to create a transaction for prepare statement, just get information schema will do. + s.sessionVars.TxnCtx.InfoSchema = domain.GetDomain(s).InfoSchema() + } + err = s.loadCommonGlobalVariablesIfNeeded() + if err != nil { + return + } + + ctx := context.Background() + // NewPrepareExec may need startTS to build the executor, for example prepare statement has subquery in int. + // So we have to call PrepareTxnCtx here. + if err = s.PrepareTxnCtx(ctx); err != nil { + return + } + + prepareStmt := &ast.PrepareStmt{SQLText: sql} + if err = s.onTxnManagerStmtStartOrRetry(ctx, prepareStmt); err != nil { + return + } + + if err = sessiontxn.GetTxnManager(s).AdviseWarmup(); err != nil { + return + } + prepareExec := executor.NewPrepareExec(s, sql) + err = prepareExec.Next(ctx, nil) + // Rollback even if err is nil. + s.rollbackOnError(ctx) + + if err != nil { + return + } + return prepareExec.ID, prepareExec.ParamCount, prepareExec.Fields, nil +} + +// ExecutePreparedStmt executes a prepared statement. +func (s *session) ExecutePreparedStmt(ctx context.Context, stmtID uint32, params []expression.Expression) (sqlexec.RecordSet, error) { + prepStmt, err := s.sessionVars.GetPreparedStmtByID(stmtID) + if err != nil { + err = plannercore.ErrStmtNotFound + logutil.Logger(ctx).Error("prepared statement not found", zap.Uint32("stmtID", stmtID)) + return nil, err + } + stmt, ok := prepStmt.(*plannercore.PlanCacheStmt) + if !ok { + return nil, errors.Errorf("invalid PlanCacheStmt type") + } + execStmt := &ast.ExecuteStmt{ + BinaryArgs: params, + PrepStmt: stmt, + } + return s.ExecuteStmt(ctx, execStmt) +} + +func (s *session) DropPreparedStmt(stmtID uint32) error { + vars := s.sessionVars + if _, ok := vars.PreparedStmts[stmtID]; !ok { + return plannercore.ErrStmtNotFound + } + vars.RetryInfo.DroppedPreparedStmtIDs = append(vars.RetryInfo.DroppedPreparedStmtIDs, stmtID) + return nil +} + +func (s *session) Txn(active bool) (kv.Transaction, error) { + if !active { + return &s.txn, nil + } + _, err := sessiontxn.GetTxnManager(s).ActivateTxn() + s.SetMemoryFootprintChangeHook() + return &s.txn, err +} + +func (s *session) SetValue(key fmt.Stringer, value interface{}) { + s.mu.Lock() + s.mu.values[key] = value + s.mu.Unlock() +} + +func (s *session) Value(key fmt.Stringer) interface{} { + s.mu.RLock() + value := s.mu.values[key] + s.mu.RUnlock() + return value +} + +func (s *session) ClearValue(key fmt.Stringer) { + s.mu.Lock() + delete(s.mu.values, key) + s.mu.Unlock() +} + +type inCloseSession struct{} + +// Close function does some clean work when session end. +// Close should release the table locks which hold by the session. +func (s *session) Close() { + // TODO: do clean table locks when session exited without execute Close. + // TODO: do clean table locks when tidb-server was `kill -9`. + if s.HasLockedTables() && config.TableLockEnabled() { + if ds := config.TableLockDelayClean(); ds > 0 { + time.Sleep(time.Duration(ds) * time.Millisecond) + } + lockedTables := s.GetAllTableLocks() + err := domain.GetDomain(s).DDL().UnlockTables(s, lockedTables) + if err != nil { + logutil.BgLogger().Error("release table lock failed", zap.Uint64("conn", s.sessionVars.ConnectionID)) + } + } + s.ReleaseAllAdvisoryLocks() + if s.statsCollector != nil { + s.statsCollector.Delete() + } + if s.idxUsageCollector != nil { + s.idxUsageCollector.Delete() + } + telemetry.GlobalBuiltinFunctionsUsage.Collect(s.GetBuiltinFunctionUsage()) + bindValue := s.Value(bindinfo.SessionBindInfoKeyType) + if bindValue != nil { + bindValue.(*bindinfo.SessionHandle).Close() + } + ctx := context.WithValue(context.TODO(), inCloseSession{}, struct{}{}) + s.RollbackTxn(ctx) + if s.sessionVars != nil { + s.sessionVars.WithdrawAllPreparedStmt() + } + if s.stmtStats != nil { + s.stmtStats.SetFinished() + } + s.ClearDiskFullOpt() + if s.sessionPlanCache != nil { + s.sessionPlanCache.Close() + } +} + +// GetSessionVars implements the context.Context interface. +func (s *session) GetSessionVars() *variable.SessionVars { + return s.sessionVars +} + +func (s *session) AuthPluginForUser(user *auth.UserIdentity) (string, error) { + pm := privilege.GetPrivilegeManager(s) + authplugin, err := pm.GetAuthPluginForConnection(user.Username, user.Hostname) + if err != nil { + return "", err + } + return authplugin, nil +} + +// Auth validates a user using an authentication string and salt. +// If the password fails, it will keep trying other users until exhausted. +// This means it can not be refactored to use MatchIdentity yet. +func (s *session) Auth(user *auth.UserIdentity, authentication, salt []byte, authConn conn.AuthConn) error { + hasPassword := "YES" + if len(authentication) == 0 { + hasPassword = "NO" + } + pm := privilege.GetPrivilegeManager(s) + authUser, err := s.MatchIdentity(user.Username, user.Hostname) + if err != nil { + return privileges.ErrAccessDenied.FastGenByArgs(user.Username, user.Hostname, hasPassword) + } + // Check whether continuous login failure is enabled to lock the account. + // If enabled, determine whether to unlock the account and notify TiDB to update the cache. + enableAutoLock := pm.IsAccountAutoLockEnabled(authUser.Username, authUser.Hostname) + if enableAutoLock { + err = failedLoginTrackingBegin(s) + if err != nil { + return err + } + lockStatusChanged, err := verifyAccountAutoLock(s, authUser.Username, authUser.Hostname) + if err != nil { + rollbackErr := failedLoginTrackingRollback(s) + if rollbackErr != nil { + return rollbackErr + } + return err + } + err = failedLoginTrackingCommit(s) + if err != nil { + rollbackErr := failedLoginTrackingRollback(s) + if rollbackErr != nil { + return rollbackErr + } + return err + } + if lockStatusChanged { + // Notification auto unlock. + err = domain.GetDomain(s).NotifyUpdatePrivilege() + if err != nil { + return err + } + } + } + + info, err := pm.ConnectionVerification(user, authUser.Username, authUser.Hostname, authentication, salt, s.sessionVars, authConn) + if err != nil { + if info.FailedDueToWrongPassword { + // when user enables the account locking function for consecutive login failures, + // the system updates the login failure count and determines whether to lock the account when authentication fails. + if enableAutoLock { + err := failedLoginTrackingBegin(s) + if err != nil { + return err + } + lockStatusChanged, passwordLocking, trackingErr := authFailedTracking(s, authUser.Username, authUser.Hostname) + if trackingErr != nil { + if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { + return rollBackErr + } + return trackingErr + } + if err := failedLoginTrackingCommit(s); err != nil { + if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { + return rollBackErr + } + return err + } + if lockStatusChanged { + // Notification auto lock. + err := autolockAction(s, passwordLocking, authUser.Username, authUser.Hostname) + if err != nil { + return err + } + } + } + } + return err + } + + if variable.EnableResourceControl.Load() && info.ResourceGroupName != "" { + s.sessionVars.ResourceGroupName = strings.ToLower(info.ResourceGroupName) + } + + if info.InSandBoxMode { + // Enter sandbox mode, only execute statement for resetting password. + s.EnableSandBoxMode() + } + if enableAutoLock { + err := failedLoginTrackingBegin(s) + if err != nil { + return err + } + // The password is correct. If the account is not locked, the number of login failure statistics will be cleared. + err = authSuccessClearCount(s, authUser.Username, authUser.Hostname) + if err != nil { + if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { + return rollBackErr + } + return err + } + err = failedLoginTrackingCommit(s) + if err != nil { + if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { + return rollBackErr + } + return err + } + } + pm.AuthSuccess(authUser.Username, authUser.Hostname) + user.AuthUsername = authUser.Username + user.AuthHostname = authUser.Hostname + s.sessionVars.User = user + s.sessionVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) + return nil +} + +func authSuccessClearCount(s *session, user string, host string) error { + // Obtain accurate lock status and failure count information. + passwordLocking, err := getFailedLoginUserAttributes(s, user, host) + if err != nil { + return err + } + // If the account is locked, it may be caused by the untimely update of the cache, + // directly report the account lock. + if passwordLocking.AutoAccountLocked { + if passwordLocking.PasswordLockTimeDays == -1 { + return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, + "unlimited", "unlimited") + } + + lds := strconv.FormatInt(passwordLocking.PasswordLockTimeDays, 10) + return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, lds, lds) + } + if passwordLocking.FailedLoginCount != 0 { + // If the number of account login failures is not zero, it will be updated to 0. + passwordLockingJSON := privileges.BuildSuccessPasswordLockingJSON(passwordLocking.FailedLoginAttempts, + passwordLocking.PasswordLockTimeDays) + if passwordLockingJSON != "" { + if err := s.passwordLocking(user, host, passwordLockingJSON); err != nil { + return err + } + } + } + return nil +} + +func verifyAccountAutoLock(s *session, user, host string) (bool, error) { + pm := privilege.GetPrivilegeManager(s) + // Use the cache to determine whether to unlock the account. + // If the account needs to be unlocked, read the database information to determine whether + // the account needs to be unlocked. Otherwise, an error message is displayed. + lockStatusInMemory, err := pm.VerifyAccountAutoLockInMemory(user, host) + if err != nil { + return false, err + } + // If the lock status in the cache is Unlock, the automatic unlock is skipped. + // If memory synchronization is slow and there is a lock in the database, it will be processed upon successful login. + if !lockStatusInMemory { + return false, nil + } + lockStatusChanged := false + var plJSON string + // After checking the cache, obtain the latest data from the database and determine + // whether to automatically unlock the database to prevent repeated unlock errors. + pl, err := getFailedLoginUserAttributes(s, user, host) + if err != nil { + return false, err + } + if pl.AutoAccountLocked { + // If it is locked, need to check whether it can be automatically unlocked. + lockTimeDay := pl.PasswordLockTimeDays + if lockTimeDay == -1 { + return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, "unlimited", "unlimited") + } + lastChanged := pl.AutoLockedLastChanged + d := time.Now().Unix() - lastChanged + if d <= lockTimeDay*24*60*60 { + lds := strconv.FormatInt(lockTimeDay, 10) + rds := strconv.FormatInt(int64(math.Ceil(float64(lockTimeDay)-float64(d)/(24*60*60))), 10) + return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, lds, rds) + } + // Generate unlock json string. + plJSON = privileges.BuildPasswordLockingJSON(pl.FailedLoginAttempts, + pl.PasswordLockTimeDays, "N", 0, time.Now().Format(time.UnixDate)) + } + if plJSON != "" { + lockStatusChanged = true + if err = s.passwordLocking(user, host, plJSON); err != nil { + return false, err + } + } + return lockStatusChanged, nil +} + +func authFailedTracking(s *session, user string, host string) (bool, *privileges.PasswordLocking, error) { + // Obtain the number of consecutive password login failures. + passwordLocking, err := getFailedLoginUserAttributes(s, user, host) + if err != nil { + return false, nil, err + } + // Consecutive wrong password login failure times +1, + // If the lock condition is satisfied, the lock status is updated and the update cache is notified. + lockStatusChanged, err := userAutoAccountLocked(s, user, host, passwordLocking) + if err != nil { + return false, nil, err + } + return lockStatusChanged, passwordLocking, nil +} + +func autolockAction(s *session, passwordLocking *privileges.PasswordLocking, user, host string) error { + // Don't want to update the cache frequently, and only trigger the update cache when the lock status is updated. + err := domain.GetDomain(s).NotifyUpdatePrivilege() + if err != nil { + return err + } + // The number of failed login attempts reaches FAILED_LOGIN_ATTEMPTS. + // An error message is displayed indicating permission denial and account lock. + if passwordLocking.PasswordLockTimeDays == -1 { + return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, + "unlimited", "unlimited") + } + lds := strconv.FormatInt(passwordLocking.PasswordLockTimeDays, 10) + return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, lds, lds) +} + +func (s *session) passwordLocking(user string, host string, newAttributesStr string) error { + sql := new(strings.Builder) + sqlexec.MustFormatSQL(sql, "UPDATE %n.%n SET ", mysql.SystemDB, mysql.UserTable) + sqlexec.MustFormatSQL(sql, "user_attributes=json_merge_patch(coalesce(user_attributes, '{}'), %?)", newAttributesStr) + sqlexec.MustFormatSQL(sql, " WHERE Host=%? and User=%?;", host, user) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + _, err := s.ExecuteInternal(ctx, sql.String()) + return err +} + +func failedLoginTrackingBegin(s *session) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + _, err := s.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") + return err +} + +func failedLoginTrackingCommit(s *session) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + _, err := s.ExecuteInternal(ctx, "COMMIT") + if err != nil { + _, rollBackErr := s.ExecuteInternal(ctx, "ROLLBACK") + if rollBackErr != nil { + return rollBackErr + } + } + return err +} + +func failedLoginTrackingRollback(s *session) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + _, err := s.ExecuteInternal(ctx, "ROLLBACK") + return err +} + +// getFailedLoginUserAttributes queries the exact number of consecutive password login failures (concurrency is not allowed). +func getFailedLoginUserAttributes(s *session, user string, host string) (*privileges.PasswordLocking, error) { + passwordLocking := &privileges.PasswordLocking{} + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) + rs, err := s.ExecuteInternal(ctx, `SELECT user_attributes from mysql.user WHERE USER = %? AND HOST = %? for update`, user, host) + if err != nil { + return passwordLocking, err + } + defer func() { + if closeErr := rs.Close(); closeErr != nil { + err = closeErr + } + }() + req := rs.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + err = rs.Next(ctx, req) + if err != nil { + return passwordLocking, err + } + if req.NumRows() == 0 { + return passwordLocking, fmt.Errorf("user_attributes by `%s`@`%s` not found", user, host) + } + row := iter.Begin() + if !row.IsNull(0) { + passwordLockingJSON := row.GetJSON(0) + return passwordLocking, passwordLocking.ParseJSON(passwordLockingJSON) + } + return passwordLocking, fmt.Errorf("user_attributes by `%s`@`%s` not found", user, host) +} + +func userAutoAccountLocked(s *session, user string, host string, pl *privileges.PasswordLocking) (bool, error) { + // Indicates whether the user needs to update the lock status change. + lockStatusChanged := false + // The number of consecutive login failures is stored in the database. + // If the current login fails, one is added to the number of consecutive login failures + // stored in the database to determine whether the user needs to be locked and the number of update failures. + failedLoginCount := pl.FailedLoginCount + 1 + // If the cache is not updated, but it is already locked, it will report that the account is locked. + if pl.AutoAccountLocked { + if pl.PasswordLockTimeDays == -1 { + return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, + "unlimited", "unlimited") + } + lds := strconv.FormatInt(pl.PasswordLockTimeDays, 10) + return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, lds, lds) + } + + autoAccountLocked := "N" + autoLockedLastChanged := "" + if pl.FailedLoginAttempts == 0 || pl.PasswordLockTimeDays == 0 { + return false, nil + } + + if failedLoginCount >= pl.FailedLoginAttempts { + autoLockedLastChanged = time.Now().Format(time.UnixDate) + autoAccountLocked = "Y" + lockStatusChanged = true + } + + newAttributesStr := privileges.BuildPasswordLockingJSON(pl.FailedLoginAttempts, + pl.PasswordLockTimeDays, autoAccountLocked, failedLoginCount, autoLockedLastChanged) + if newAttributesStr != "" { + return lockStatusChanged, s.passwordLocking(user, host, newAttributesStr) + } + return lockStatusChanged, nil +} + +// MatchIdentity finds the matching username + password in the MySQL privilege tables +// for a username + hostname, since MySQL can have wildcards. +func (s *session) MatchIdentity(username, remoteHost string) (*auth.UserIdentity, error) { + pm := privilege.GetPrivilegeManager(s) + var success bool + var skipNameResolve bool + var user = &auth.UserIdentity{} + varVal, err := s.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.SkipNameResolve) + if err == nil && variable.TiDBOptOn(varVal) { + skipNameResolve = true + } + user.Username, user.Hostname, success = pm.MatchIdentity(username, remoteHost, skipNameResolve) + if success { + return user, nil + } + // This error will not be returned to the user, access denied will be instead + return nil, fmt.Errorf("could not find matching user in MatchIdentity: %s, %s", username, remoteHost) +} + +// AuthWithoutVerification is required by the ResetConnection RPC +func (s *session) AuthWithoutVerification(user *auth.UserIdentity) bool { + pm := privilege.GetPrivilegeManager(s) + authUser, err := s.MatchIdentity(user.Username, user.Hostname) + if err != nil { + return false + } + if pm.GetAuthWithoutVerification(authUser.Username, authUser.Hostname) { + user.AuthUsername = authUser.Username + user.AuthHostname = authUser.Hostname + s.sessionVars.User = user + s.sessionVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) + return true + } + return false +} + +// SetSessionStatesHandler implements the Session.SetSessionStatesHandler interface. +func (s *session) SetSessionStatesHandler(stateType sessionstates.SessionStateType, handler sessionctx.SessionStatesHandler) { + s.sessionStatesHandlers[stateType] = handler +} + +// CreateSession4Test creates a new session environment for test. +func CreateSession4Test(store kv.Storage) (Session, error) { + se, err := CreateSession4TestWithOpt(store, nil) + if err == nil { + // Cover both chunk rpc encoding and default encoding. + // nolint:gosec + if rand.Intn(2) == 0 { + se.GetSessionVars().EnableChunkRPC = false + } else { + se.GetSessionVars().EnableChunkRPC = true + } + } + return se, err +} + +// Opt describes the option for creating session +type Opt struct { + PreparedPlanCache sessionctx.PlanCache +} + +// CreateSession4TestWithOpt creates a new session environment for test. +func CreateSession4TestWithOpt(store kv.Storage, opt *Opt) (Session, error) { + s, err := CreateSessionWithOpt(store, opt) + if err == nil { + // initialize session variables for test. + s.GetSessionVars().InitChunkSize = 2 + s.GetSessionVars().MaxChunkSize = 32 + s.GetSessionVars().MinPagingSize = variable.DefMinPagingSize + s.GetSessionVars().EnablePaging = variable.DefTiDBEnablePaging + err = s.GetSessionVars().SetSystemVarWithoutValidation(variable.CharacterSetConnection, "utf8mb4") + } + return s, err +} + +// CreateSession creates a new session environment. +func CreateSession(store kv.Storage) (Session, error) { + return CreateSessionWithOpt(store, nil) +} + +// CreateSessionWithOpt creates a new session environment with option. +// Use default option if opt is nil. +func CreateSessionWithOpt(store kv.Storage, opt *Opt) (Session, error) { + s, err := createSessionWithOpt(store, opt) + if err != nil { + return nil, err + } + + // Add auth here. + do, err := domap.Get(store) + if err != nil { + return nil, err + } + extensions, err := extension.GetExtensions() + if err != nil { + return nil, err + } + pm := privileges.NewUserPrivileges(do.PrivilegeHandle(), extensions) + privilege.BindPrivilegeManager(s, pm) + + // Add stats collector, and it will be freed by background stats worker + // which periodically updates stats using the collected data. + if do.StatsHandle() != nil && do.StatsUpdating() { + s.statsCollector = do.StatsHandle().NewSessionStatsItem().(*usage.SessionStatsItem) + if GetIndexUsageSyncLease() > 0 { + s.idxUsageCollector = do.StatsHandle().NewSessionIndexUsageCollector().(*usage.SessionIndexUsageCollector) + } + } + + return s, nil +} + +// loadCollationParameter loads collation parameter from mysql.tidb +func loadCollationParameter(ctx context.Context, se *session) (bool, error) { + para, err := se.getTableValue(ctx, mysql.TiDBTable, tidbNewCollationEnabled) + if err != nil { + return false, err + } + if para == varTrue { + return true, nil + } else if para == varFalse { + return false, nil + } + logutil.BgLogger().Warn( + "Unexpected value of 'new_collation_enabled' in 'mysql.tidb', use 'False' instead", + zap.String("value", para)) + return false, nil +} + +type tableBasicInfo struct { + SQL string + id int64 +} + +var ( + errResultIsEmpty = dbterror.ClassExecutor.NewStd(errno.ErrResultIsEmpty) + // DDLJobTables is a list of tables definitions used in concurrent DDL. + DDLJobTables = []tableBasicInfo{ + {ddl.JobTableSQL, ddl.JobTableID}, + {ddl.ReorgTableSQL, ddl.ReorgTableID}, + {ddl.HistoryTableSQL, ddl.HistoryTableID}, + } + // BackfillTables is a list of tables definitions used in dist reorg DDL. + BackfillTables = []tableBasicInfo{ + {ddl.BackgroundSubtaskTableSQL, ddl.BackgroundSubtaskTableID}, + {ddl.BackgroundSubtaskHistoryTableSQL, ddl.BackgroundSubtaskHistoryTableID}, + } + mdlTable = "create table mysql.tidb_mdl_info(job_id BIGINT NOT NULL PRIMARY KEY, version BIGINT NOT NULL, table_ids text(65535));" +) + +func splitAndScatterTable(store kv.Storage, tableIDs []int64) { + if s, ok := store.(kv.SplittableStore); ok && atomic.LoadUint32(&ddl.EnableSplitTableRegion) == 1 { + ctxWithTimeout, cancel := context.WithTimeout(context.Background(), variable.DefWaitSplitRegionTimeout*time.Second) + var regionIDs []uint64 + for _, id := range tableIDs { + regionIDs = append(regionIDs, ddl.SplitRecordRegion(ctxWithTimeout, s, id, id, variable.DefTiDBScatterRegion)) + } + if variable.DefTiDBScatterRegion { + ddl.WaitScatterRegionFinish(ctxWithTimeout, s, regionIDs...) + } + cancel() + } +} + +// InitDDLJobTables is to create tidb_ddl_job, tidb_ddl_reorg and tidb_ddl_history, or tidb_background_subtask and tidb_background_subtask_history. +func InitDDLJobTables(store kv.Storage, targetVer meta.DDLTableVersion) error { + targetTables := DDLJobTables + if targetVer == meta.BackfillTableVersion { + targetTables = BackfillTables + } + return kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + tableVer, err := t.CheckDDLTableVersion() + if err != nil || tableVer >= targetVer { + return errors.Trace(err) + } + dbID, err := t.CreateMySQLDatabaseIfNotExists() + if err != nil { + return err + } + if err = createAndSplitTables(store, t, dbID, targetTables); err != nil { + return err + } + return t.SetDDLTables(targetVer) + }) +} + +func createAndSplitTables(store kv.Storage, t *meta.Meta, dbID int64, tables []tableBasicInfo) error { + tableIDs := make([]int64, 0, len(tables)) + for _, tbl := range tables { + tableIDs = append(tableIDs, tbl.id) + } + splitAndScatterTable(store, tableIDs) + p := parser.New() + for _, tbl := range tables { + stmt, err := p.ParseOneStmt(tbl.SQL, "", "") + if err != nil { + return errors.Trace(err) + } + tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) + if err != nil { + return errors.Trace(err) + } + tblInfo.State = model.StatePublic + tblInfo.ID = tbl.id + tblInfo.UpdateTS = t.StartTS + err = t.CreateTableOrView(dbID, tblInfo) + if err != nil { + return errors.Trace(err) + } + } + return nil +} + +// InitMDLTable is to create tidb_mdl_info, which is used for metadata lock. +func InitMDLTable(store kv.Storage) error { + return kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + ver, err := t.CheckDDLTableVersion() + if err != nil || ver >= meta.MDLTableVersion { + return errors.Trace(err) + } + dbID, err := t.CreateMySQLDatabaseIfNotExists() + if err != nil { + return err + } + splitAndScatterTable(store, []int64{ddl.MDLTableID}) + p := parser.New() + stmt, err := p.ParseOneStmt(mdlTable, "", "") + if err != nil { + return errors.Trace(err) + } + tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) + if err != nil { + return errors.Trace(err) + } + tblInfo.State = model.StatePublic + tblInfo.ID = ddl.MDLTableID + tblInfo.UpdateTS = t.StartTS + err = t.CreateTableOrView(dbID, tblInfo) + if err != nil { + return errors.Trace(err) + } + + return t.SetDDLTables(meta.MDLTableVersion) + }) +} + +// InitMDLVariableForBootstrap initializes the metadata lock variable. +func InitMDLVariableForBootstrap(store kv.Storage) error { + err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + return t.SetMetadataLock(true) + }) + if err != nil { + return err + } + variable.EnableMDL.Store(true) + return nil +} + +// InitMDLVariableForUpgrade initializes the metadata lock variable. +func InitMDLVariableForUpgrade(store kv.Storage) (bool, error) { + isNull := false + enable := false + var err error + err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + enable, isNull, err = t.GetMetadataLock() + if err != nil { + return err + } + return nil + }) + if isNull || !enable { + variable.EnableMDL.Store(false) + } else { + variable.EnableMDL.Store(true) + } + return isNull, err +} + +// InitMDLVariable initializes the metadata lock variable. +func InitMDLVariable(store kv.Storage) error { + isNull := false + enable := false + var err error + err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + enable, isNull, err = t.GetMetadataLock() + if err != nil { + return err + } + if isNull { + // Workaround for version: nightly-2022-11-07 to nightly-2022-11-17. + enable = true + logutil.BgLogger().Warn("metadata lock is null") + err = t.SetMetadataLock(true) + if err != nil { + return err + } + } + return nil + }) + variable.EnableMDL.Store(enable) + return err +} + +// BootstrapSession bootstrap session and domain. +func BootstrapSession(store kv.Storage) (*domain.Domain, error) { + return bootstrapSessionImpl(store, createSessions) +} + +// BootstrapSession4DistExecution bootstrap session and dom for Distributed execution test, only for unit testing. +func BootstrapSession4DistExecution(store kv.Storage) (*domain.Domain, error) { + return bootstrapSessionImpl(store, createSessions4DistExecution) +} + +func bootstrapSessionImpl(store kv.Storage, createSessionsImpl func(store kv.Storage, cnt int) ([]*session, error)) (*domain.Domain, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + cfg := config.GetGlobalConfig() + if len(cfg.Instance.PluginLoad) > 0 { + err := plugin.Load(context.Background(), plugin.Config{ + Plugins: strings.Split(cfg.Instance.PluginLoad, ","), + PluginDir: cfg.Instance.PluginDir, + }) + if err != nil { + return nil, err + } + } + err := InitDDLJobTables(store, meta.BaseDDLTableVersion) + if err != nil { + return nil, err + } + err = InitMDLTable(store) + if err != nil { + return nil, err + } + err = InitDDLJobTables(store, meta.BackfillTableVersion) + if err != nil { + return nil, err + } + ver := getStoreBootstrapVersion(store) + if ver == notBootstrapped { + runInBootstrapSession(store, bootstrap) + } else if ver < currentBootstrapVersion { + runInBootstrapSession(store, upgrade) + } else { + err = InitMDLVariable(store) + if err != nil { + return nil, err + } + } + + analyzeConcurrencyQuota := int(config.GetGlobalConfig().Performance.AnalyzePartitionConcurrencyQuota) + concurrency := int(config.GetGlobalConfig().Performance.StatsLoadConcurrency) + ses, err := createSessionsImpl(store, 10) + if err != nil { + return nil, err + } + ses[0].GetSessionVars().InRestrictedSQL = true + + // get system tz from mysql.tidb + tz, err := ses[0].getTableValue(ctx, mysql.TiDBTable, tidbSystemTZ) + if err != nil { + return nil, err + } + timeutil.SetSystemTZ(tz) + + // get the flag from `mysql`.`tidb` which indicating if new collations are enabled. + newCollationEnabled, err := loadCollationParameter(ctx, ses[0]) + if err != nil { + return nil, err + } + collate.SetNewCollationEnabledForTest(newCollationEnabled) + // To deal with the location partition failure caused by inconsistent NewCollationEnabled values(see issue #32416). + rebuildAllPartitionValueMapAndSorted(ses[0]) + + dom := domain.GetDomain(ses[0]) + + // We should make the load bind-info loop before other loops which has internal SQL. + // Because the internal SQL may access the global bind-info handler. As the result, the data race occurs here as the + // LoadBindInfoLoop inits global bind-info handler. + err = dom.LoadBindInfoLoop(ses[1], ses[2]) + if err != nil { + return nil, err + } + + if !config.GetGlobalConfig().Security.SkipGrantTable { + err = dom.LoadPrivilegeLoop(ses[3]) + if err != nil { + return nil, err + } + } + + // Rebuild sysvar cache in a loop + err = dom.LoadSysVarCacheLoop(ses[4]) + if err != nil { + return nil, err + } + + if config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler { + // Invalid client-go tiflash_compute store cache if necessary. + err = dom.WatchTiFlashComputeNodeChange() + if err != nil { + return nil, err + } + } + + if err = extensionimpl.Bootstrap(context.Background(), dom); err != nil { + return nil, err + } + + if len(cfg.Instance.PluginLoad) > 0 { + err := plugin.Init(context.Background(), plugin.Config{EtcdClient: dom.GetEtcdClient()}) + if err != nil { + return nil, err + } + } + + err = executor.LoadExprPushdownBlacklist(ses[5]) + if err != nil { + return nil, err + } + err = executor.LoadOptRuleBlacklist(ctx, ses[5]) + if err != nil { + return nil, err + } + + if dom.GetEtcdClient() != nil { + // We only want telemetry data in production-like clusters. When TiDB is deployed over other engines, + // for example, unistore engine (used for local tests), we just skip it. Its etcd client is nil. + if config.GetGlobalConfig().EnableTelemetry { + // There is no way to turn telemetry on with global variable `tidb_enable_telemetry` + // when it is disabled in config. See IsTelemetryEnabled function in telemetry/telemetry.go + go func() { + dom.TelemetryReportLoop(ses[5]) + dom.TelemetryRotateSubWindowLoop(ses[5]) + }() + } + } + + planReplayerWorkerCnt := config.GetGlobalConfig().Performance.PlanReplayerDumpWorkerConcurrency + planReplayerWorkersSctx := make([]sessionctx.Context, planReplayerWorkerCnt) + pworkerSes, err := createSessions(store, int(planReplayerWorkerCnt)) + if err != nil { + return nil, err + } + for i := 0; i < int(planReplayerWorkerCnt); i++ { + planReplayerWorkersSctx[i] = pworkerSes[i] + } + // setup plan replayer handle + dom.SetupPlanReplayerHandle(ses[6], planReplayerWorkersSctx) + dom.StartPlanReplayerHandle() + // setup dumpFileGcChecker + dom.SetupDumpFileGCChecker(ses[7]) + dom.DumpFileGcCheckerLoop() + // setup historical stats worker + dom.SetupHistoricalStatsWorker(ses[8]) + dom.StartHistoricalStatsWorker() + failToLoadOrParseSQLFile := false // only used for unit test + if runBootstrapSQLFile { + pm := &privileges.UserPrivileges{ + Handle: dom.PrivilegeHandle(), + } + privilege.BindPrivilegeManager(ses[9], pm) + if err := doBootstrapSQLFile(ses[9]); err != nil && intest.InTest { + failToLoadOrParseSQLFile = true + } + } + // A sub context for update table stats, and other contexts for concurrent stats loading. + cnt := 1 + concurrency + syncStatsCtxs, err := createSessions(store, cnt) + if err != nil { + return nil, err + } + subCtxs := make([]sessionctx.Context, cnt) + for i := 0; i < cnt; i++ { + subCtxs[i] = sessionctx.Context(syncStatsCtxs[i]) + } + + // setup extract Handle + extractWorkers := 1 + sctxs, err := createSessions(store, extractWorkers) + if err != nil { + return nil, err + } + extractWorkerSctxs := make([]sessionctx.Context, 0) + for _, sctx := range sctxs { + extractWorkerSctxs = append(extractWorkerSctxs, sctx) + } + dom.SetupExtractHandle(extractWorkerSctxs) + + // setup init stats loader + initStatsCtx, err := createSession(store) + if err != nil { + return nil, err + } + if err = dom.LoadAndUpdateStatsLoop(subCtxs, initStatsCtx); err != nil { + return nil, err + } + + // start TTL job manager after setup stats collector + // because TTL could modify a lot of columns, and need to trigger auto analyze + ttlworker.AttachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { + if s, ok := s.(*session); ok { + return attachStatsCollector(s, dom) + } + return s + } + ttlworker.DetachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { + if s, ok := s.(*session); ok { + return detachStatsCollector(s) + } + return s + } + dom.StartTTLJobManager() + + analyzeCtxs, err := createSessions(store, analyzeConcurrencyQuota) + if err != nil { + return nil, err + } + subCtxs2 := make([]sessionctx.Context, analyzeConcurrencyQuota) + for i := 0; i < analyzeConcurrencyQuota; i++ { + subCtxs2[i] = analyzeCtxs[i] + } + dom.SetupAnalyzeExec(subCtxs2) + dom.LoadSigningCertLoop(cfg.Security.SessionTokenSigningCert, cfg.Security.SessionTokenSigningKey) + + if raw, ok := store.(kv.EtcdBackend); ok { + err = raw.StartGCWorker() + if err != nil { + return nil, err + } + } + + // This only happens in testing, since the failure of loading or parsing sql file + // would panic the bootstrapping. + if intest.InTest && failToLoadOrParseSQLFile { + dom.Close() + return nil, errors.New("Fail to load or parse sql file") + } + err = dom.InitDistTaskLoop(ctx) + if err != nil { + return nil, err + } + return dom, err +} + +// GetDomain gets the associated domain for store. +func GetDomain(store kv.Storage) (*domain.Domain, error) { + return domap.Get(store) +} + +// runInBootstrapSession create a special session for bootstrap to run. +// If no bootstrap and storage is remote, we must use a little lease time to +// bootstrap quickly, after bootstrapped, we will reset the lease time. +// TODO: Using a bootstrap tool for doing this may be better later. +func runInBootstrapSession(store kv.Storage, bootstrap func(Session)) { + s, err := createSession(store) + if err != nil { + // Bootstrap fail will cause program exit. + logutil.BgLogger().Fatal("createSession error", zap.Error(err)) + } + // For the bootstrap SQLs, the following variables should be compatible with old TiDB versions. + s.sessionVars.EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly + + s.SetValue(sessionctx.Initing, true) + bootstrap(s) + finishBootstrap(store) + s.ClearValue(sessionctx.Initing) + + dom := domain.GetDomain(s) + dom.Close() + if intest.InTest { + infosync.MockGlobalServerInfoManagerEntry.Close() + } + domap.Delete(store) +} + +func createSessions(store kv.Storage, cnt int) ([]*session, error) { + return createSessionsImpl(store, cnt, createSession) +} + +func createSessions4DistExecution(store kv.Storage, cnt int) ([]*session, error) { + domap.Delete(store) + + return createSessionsImpl(store, cnt, createSession4DistExecution) +} + +func createSessionsImpl(store kv.Storage, cnt int, createSessionImpl func(kv.Storage) (*session, error)) ([]*session, error) { + // Then we can create new dom + ses := make([]*session, cnt) + for i := 0; i < cnt; i++ { + se, err := createSessionImpl(store) + if err != nil { + return nil, err + } + ses[i] = se + } + + return ses, nil +} + +// createSession creates a new session. +// Please note that such a session is not tracked by the internal session list. +// This means the min ts reporter is not aware of it and may report a wrong min start ts. +// In most cases you should use a session pool in domain instead. +func createSession(store kv.Storage) (*session, error) { + return createSessionWithOpt(store, nil) +} + +func createSession4DistExecution(store kv.Storage) (*session, error) { + return createSessionWithOpt(store, nil) +} + +func createSessionWithOpt(store kv.Storage, opt *Opt) (*session, error) { + dom, err := domap.Get(store) + if err != nil { + return nil, err + } + s := &session{ + store: store, + ddlOwnerManager: dom.DDL().OwnerManager(), + client: store.GetClient(), + mppClient: store.GetMPPClient(), + stmtStats: stmtstats.CreateStatementStats(), + sessionStatesHandlers: make(map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler), + } + s.sessionVars = variable.NewSessionVars(s) + + s.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) + if opt != nil && opt.PreparedPlanCache != nil { + s.sessionPlanCache = opt.PreparedPlanCache + } + s.mu.values = make(map[fmt.Stringer]interface{}) + s.lockedTables = make(map[int64]model.TableLockTpInfo) + s.advisoryLocks = make(map[string]*advisoryLock) + + domain.BindDomain(s, dom) + // session implements variable.GlobalVarAccessor. Bind it to ctx. + s.sessionVars.GlobalVarsAccessor = s + s.sessionVars.BinlogClient = binloginfo.GetPumpsClient() + s.txn.init() + + sessionBindHandle := bindinfo.NewSessionBindHandle() + s.SetValue(bindinfo.SessionBindInfoKeyType, sessionBindHandle) + s.SetSessionStatesHandler(sessionstates.StateBinding, sessionBindHandle) + return s, nil +} + +// attachStatsCollector attaches the stats collector in the dom for the session +func attachStatsCollector(s *session, dom *domain.Domain) *session { + if dom.StatsHandle() != nil && dom.StatsUpdating() { + if s.statsCollector == nil { + s.statsCollector = dom.StatsHandle().NewSessionStatsItem().(*usage.SessionStatsItem) + } + if s.idxUsageCollector == nil && GetIndexUsageSyncLease() > 0 { + s.idxUsageCollector = dom.StatsHandle().NewSessionIndexUsageCollector().(*usage.SessionIndexUsageCollector) + } + } + + return s +} + +// detachStatsCollector removes the stats collector in the session +func detachStatsCollector(s *session) *session { + if s.statsCollector != nil { + s.statsCollector.Delete() + s.statsCollector = nil + } + if s.idxUsageCollector != nil { + s.idxUsageCollector.Delete() + s.idxUsageCollector = nil + } + return s +} + +// CreateSessionWithDomain creates a new Session and binds it with a Domain. +// We need this because when we start DDL in Domain, the DDL need a session +// to change some system tables. But at that time, we have been already in +// a lock context, which cause we can't call createSession directly. +func CreateSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, error) { + s := &session{ + store: store, + sessionVars: variable.NewSessionVars(nil), + client: store.GetClient(), + mppClient: store.GetMPPClient(), + stmtStats: stmtstats.CreateStatementStats(), + sessionStatesHandlers: make(map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler), + } + s.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) + s.mu.values = make(map[fmt.Stringer]interface{}) + s.lockedTables = make(map[int64]model.TableLockTpInfo) + domain.BindDomain(s, dom) + // session implements variable.GlobalVarAccessor. Bind it to ctx. + s.sessionVars.GlobalVarsAccessor = s + s.txn.init() + return s, nil +} + +const ( + notBootstrapped = 0 +) + +func getStoreBootstrapVersion(store kv.Storage) int64 { + storeBootstrappedLock.Lock() + defer storeBootstrappedLock.Unlock() + // check in memory + _, ok := storeBootstrapped[store.UUID()] + if ok { + return currentBootstrapVersion + } + + var ver int64 + // check in kv store + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + var err error + t := meta.NewMeta(txn) + ver, err = t.GetBootstrapVersion() + return err + }) + if err != nil { + logutil.BgLogger().Fatal("check bootstrapped failed", + zap.Error(err)) + } + + if ver > notBootstrapped { + // here mean memory is not ok, but other server has already finished it + storeBootstrapped[store.UUID()] = true + } + + modifyBootstrapVersionForTest(ver) + return ver +} + +func finishBootstrap(store kv.Storage) { + setStoreBootstrapped(store.UUID()) + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + t := meta.NewMeta(txn) + err := t.FinishBootstrap(currentBootstrapVersion) + return err + }) + if err != nil { + logutil.BgLogger().Fatal("finish bootstrap failed", + zap.Error(err)) + } +} + +const quoteCommaQuote = "', '" + +// loadCommonGlobalVariablesIfNeeded loads and applies commonly used global variables for the session. +func (s *session) loadCommonGlobalVariablesIfNeeded() error { + vars := s.sessionVars + if vars.CommonGlobalLoaded { + return nil + } + if s.Value(sessionctx.Initing) != nil { + // When running bootstrap or upgrade, we should not access global storage. + return nil + } + + vars.CommonGlobalLoaded = true + + // Deep copy sessionvar cache + sessionCache, err := domain.GetDomain(s).GetSessionCache() + if err != nil { + return err + } + for varName, varVal := range sessionCache { + if _, ok := vars.GetSystemVar(varName); !ok { + err = vars.SetSystemVarWithRelaxedValidation(varName, varVal) + if err != nil { + if variable.ErrUnknownSystemVar.Equal(err) { + continue // sessionCache is stale; sysvar has likely been unregistered + } + return err + } + } + } + // when client set Capability Flags CLIENT_INTERACTIVE, init wait_timeout with interactive_timeout + if vars.ClientCapability&mysql.ClientInteractive > 0 { + if varVal, ok := vars.GetSystemVar(variable.InteractiveTimeout); ok { + if err := vars.SetSystemVar(variable.WaitTimeout, varVal); err != nil { + return err + } + } + } + return nil +} + +// PrepareTxnCtx begins a transaction, and creates a new transaction context. +// It is called before we execute a sql query. +func (s *session) PrepareTxnCtx(ctx context.Context) error { + s.currentCtx = ctx + if s.txn.validOrPending() { + return nil + } + + txnMode := ast.Optimistic + if !s.sessionVars.IsAutocommit() || config.GetGlobalConfig().PessimisticTxn.PessimisticAutoCommit.Load() { + if s.sessionVars.TxnMode == ast.Pessimistic { + txnMode = ast.Pessimistic + } + } + + if s.sessionVars.RetryInfo.Retrying { + txnMode = ast.Pessimistic + } + + return sessiontxn.GetTxnManager(s).EnterNewTxn(ctx, &sessiontxn.EnterNewTxnRequest{ + Type: sessiontxn.EnterNewTxnBeforeStmt, + TxnMode: txnMode, + }) +} + +// PrepareTSFuture uses to try to get ts future. +func (s *session) PrepareTSFuture(ctx context.Context, future oracle.Future, scope string) error { + if s.txn.Valid() { + return errors.New("cannot prepare ts future when txn is valid") + } + + failpoint.Inject("assertTSONotRequest", func() { + if _, ok := future.(sessiontxn.ConstantFuture); !ok && !s.isInternal() { + panic("tso shouldn't be requested") + } + }) + + failpoint.InjectContext(ctx, "mockGetTSFail", func() { + future = txnFailFuture{} + }) + + s.txn.changeToPending(&txnFuture{ + future: future, + store: s.store, + txnScope: scope, + }) + return nil +} + +// GetPreparedTxnFuture returns the TxnFuture if it is valid or pending. +// It returns nil otherwise. +func (s *session) GetPreparedTxnFuture() sessionctx.TxnFuture { + if !s.txn.validOrPending() { + return nil + } + return &s.txn +} + +// RefreshTxnCtx implements context.RefreshTxnCtx interface. +func (s *session) RefreshTxnCtx(ctx context.Context) error { + var commitDetail *tikvutil.CommitDetails + ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) + err := s.doCommit(ctx) + if commitDetail != nil { + s.GetSessionVars().StmtCtx.MergeExecDetails(nil, commitDetail) + } + if err != nil { + return err + } + + s.updateStatsDeltaToCollector() + + return sessiontxn.NewTxn(ctx, s) +} + +// GetStore gets the store of session. +func (s *session) GetStore() kv.Storage { + return s.store +} + +func (s *session) ShowProcess() *util.ProcessInfo { + var pi *util.ProcessInfo + tmp := s.processInfo.Load() + if tmp != nil { + pi = tmp.(*util.ProcessInfo) + } + return pi +} + +// GetStartTSFromSession returns the startTS in the session `se` +func GetStartTSFromSession(se interface{}) (startTS, processInfoID uint64) { + tmp, ok := se.(*session) + if !ok { + logutil.BgLogger().Error("GetStartTSFromSession failed, can't transform to session struct") + return 0, 0 + } + txnInfo := tmp.TxnInfo() + if txnInfo != nil { + startTS = txnInfo.StartTS + processInfoID = txnInfo.ConnectionID + } + + logutil.BgLogger().Debug( + "GetStartTSFromSession getting startTS of internal session", + zap.Uint64("startTS", startTS), zap.Time("start time", oracle.GetTimeFromTS(startTS))) + + return startTS, processInfoID +} + +// logStmt logs some crucial SQL including: CREATE USER/GRANT PRIVILEGE/CHANGE PASSWORD/DDL etc and normal SQL +// if variable.ProcessGeneralLog is set. +func logStmt(execStmt *executor.ExecStmt, s *session) { + vars := s.GetSessionVars() + isCrucial := false + switch stmt := execStmt.StmtNode.(type) { + case *ast.DropIndexStmt: + isCrucial = true + if stmt.IsHypo { + isCrucial = false + } + case *ast.CreateIndexStmt: + isCrucial = true + if stmt.IndexOption != nil && stmt.IndexOption.Tp == model.IndexTypeHypo { + isCrucial = false + } + case *ast.CreateUserStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.SetPwdStmt, *ast.GrantStmt, + *ast.RevokeStmt, *ast.AlterTableStmt, *ast.CreateDatabaseStmt, *ast.CreateTableStmt, + *ast.DropDatabaseStmt, *ast.DropTableStmt, *ast.RenameTableStmt, *ast.TruncateTableStmt, + *ast.RenameUserStmt: + isCrucial = true + } + + if isCrucial { + user := vars.User + schemaVersion := s.GetInfoSchema().SchemaMetaVersion() + if ss, ok := execStmt.StmtNode.(ast.SensitiveStmtNode); ok { + logutil.BgLogger().Info("CRUCIAL OPERATION", + zap.Uint64("conn", vars.ConnectionID), + zap.Int64("schemaVersion", schemaVersion), + zap.String("secure text", ss.SecureText()), + zap.Stringer("user", user)) + } else { + logutil.BgLogger().Info("CRUCIAL OPERATION", + zap.Uint64("conn", vars.ConnectionID), + zap.Int64("schemaVersion", schemaVersion), + zap.String("cur_db", vars.CurrentDB), + zap.String("sql", execStmt.StmtNode.Text()), + zap.Stringer("user", user)) + } + } else { + logGeneralQuery(execStmt, s, false) + } +} + +func logGeneralQuery(execStmt *executor.ExecStmt, s *session, isPrepared bool) { + vars := s.GetSessionVars() + if variable.ProcessGeneralLog.Load() && !vars.InRestrictedSQL { + var query string + if isPrepared { + query = execStmt.OriginText() + } else { + query = execStmt.GetTextToLog(false) + } + + query = executor.QueryReplacer.Replace(query) + if !vars.EnableRedactLog { + query += vars.PlanCacheParams.String() + } + logutil.BgLogger().Info("GENERAL_LOG", + zap.Uint64("conn", vars.ConnectionID), + zap.String("session_alias", vars.SessionAlias), + zap.String("user", vars.User.LoginString()), + zap.Int64("schemaVersion", s.GetInfoSchema().SchemaMetaVersion()), + zap.Uint64("txnStartTS", vars.TxnCtx.StartTS), + zap.Uint64("forUpdateTS", vars.TxnCtx.GetForUpdateTS()), + zap.Bool("isReadConsistency", vars.IsIsolation(ast.ReadCommitted)), + zap.String("currentDB", vars.CurrentDB), + zap.Bool("isPessimistic", vars.TxnCtx.IsPessimistic), + zap.String("sessionTxnMode", vars.GetReadableTxnMode()), + zap.String("sql", query)) + } +} + +func (s *session) recordOnTransactionExecution(err error, counter int, duration float64, isInternal bool) { + if s.sessionVars.TxnCtx.IsPessimistic { + if err != nil { + if isInternal { + session_metrics.TransactionDurationPessimisticAbortInternal.Observe(duration) + session_metrics.StatementPerTransactionPessimisticErrorInternal.Observe(float64(counter)) + } else { + session_metrics.TransactionDurationPessimisticAbortGeneral.Observe(duration) + session_metrics.StatementPerTransactionPessimisticErrorGeneral.Observe(float64(counter)) + } + } else { + if isInternal { + session_metrics.TransactionDurationPessimisticCommitInternal.Observe(duration) + session_metrics.StatementPerTransactionPessimisticOKInternal.Observe(float64(counter)) + } else { + session_metrics.TransactionDurationPessimisticCommitGeneral.Observe(duration) + session_metrics.StatementPerTransactionPessimisticOKGeneral.Observe(float64(counter)) + } + } + } else { + if err != nil { + if isInternal { + session_metrics.TransactionDurationOptimisticAbortInternal.Observe(duration) + session_metrics.StatementPerTransactionOptimisticErrorInternal.Observe(float64(counter)) + } else { + session_metrics.TransactionDurationOptimisticAbortGeneral.Observe(duration) + session_metrics.StatementPerTransactionOptimisticErrorGeneral.Observe(float64(counter)) + } + } else { + if isInternal { + session_metrics.TransactionDurationOptimisticCommitInternal.Observe(duration) + session_metrics.StatementPerTransactionOptimisticOKInternal.Observe(float64(counter)) + } else { + session_metrics.TransactionDurationOptimisticCommitGeneral.Observe(duration) + session_metrics.StatementPerTransactionOptimisticOKGeneral.Observe(float64(counter)) + } + } + } +} + +func (s *session) checkPlacementPolicyBeforeCommit() error { + var err error + // Get the txnScope of the transaction we're going to commit. + txnScope := s.GetSessionVars().TxnCtx.TxnScope + if txnScope == "" { + txnScope = kv.GlobalTxnScope + } + if txnScope != kv.GlobalTxnScope { + is := s.GetInfoSchema().(infoschema.InfoSchema) + deltaMap := s.GetSessionVars().TxnCtx.TableDeltaMap + for physicalTableID := range deltaMap { + var tableName string + var partitionName string + tblInfo, _, partInfo := is.FindTableByPartitionID(physicalTableID) + if tblInfo != nil && partInfo != nil { + tableName = tblInfo.Meta().Name.String() + partitionName = partInfo.Name.String() + } else { + tblInfo, _ := is.TableByID(physicalTableID) + tableName = tblInfo.Meta().Name.String() + } + bundle, ok := is.PlacementBundleByPhysicalTableID(physicalTableID) + if !ok { + errMsg := fmt.Sprintf("table %v doesn't have placement policies with txn_scope %v", + tableName, txnScope) + if len(partitionName) > 0 { + errMsg = fmt.Sprintf("table %v's partition %v doesn't have placement policies with txn_scope %v", + tableName, partitionName, txnScope) + } + err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) + break + } + dcLocation, ok := bundle.GetLeaderDC(placement.DCLabelKey) + if !ok { + errMsg := fmt.Sprintf("table %v's leader placement policy is not defined", tableName) + if len(partitionName) > 0 { + errMsg = fmt.Sprintf("table %v's partition %v's leader placement policy is not defined", tableName, partitionName) + } + err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) + break + } + if dcLocation != txnScope { + errMsg := fmt.Sprintf("table %v's leader location %v is out of txn_scope %v", tableName, dcLocation, txnScope) + if len(partitionName) > 0 { + errMsg = fmt.Sprintf("table %v's partition %v's leader location %v is out of txn_scope %v", + tableName, partitionName, dcLocation, txnScope) + } + err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) + break + } + // FIXME: currently we assume the physicalTableID is the partition ID. In future, we should consider the situation + // if the physicalTableID belongs to a Table. + partitionID := physicalTableID + tbl, _, partitionDefInfo := is.FindTableByPartitionID(partitionID) + if tbl != nil { + tblInfo := tbl.Meta() + state := tblInfo.Partition.GetStateByID(partitionID) + if state == model.StateGlobalTxnOnly { + err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs( + fmt.Sprintf("partition %s of table %s can not be written by local transactions when its placement policy is being altered", + tblInfo.Name, partitionDefInfo.Name)) + break + } + } + } + } + return err +} + +func (s *session) SetPort(port string) { + s.sessionVars.Port = port +} + +// GetTxnWriteThroughputSLI implements the Context interface. +func (s *session) GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI { + return &s.txn.writeSLI +} + +// GetInfoSchema returns snapshotInfoSchema if snapshot schema is set. +// Transaction infoschema is returned if inside an explicit txn. +// Otherwise the latest infoschema is returned. +func (s *session) GetInfoSchema() sessionctx.InfoschemaMetaVersion { + vars := s.GetSessionVars() + var is infoschema.InfoSchema + if snap, ok := vars.SnapshotInfoschema.(infoschema.InfoSchema); ok { + logutil.BgLogger().Info("use snapshot schema", zap.Uint64("conn", vars.ConnectionID), zap.Int64("schemaVersion", snap.SchemaMetaVersion())) + is = snap + } else { + vars.TxnCtxMu.Lock() + if vars.TxnCtx != nil { + if tmp, ok := vars.TxnCtx.InfoSchema.(infoschema.InfoSchema); ok { + is = tmp + } + } + vars.TxnCtxMu.Unlock() + } + + if is == nil { + is = domain.GetDomain(s).InfoSchema() + } + + // Override the infoschema if the session has temporary table. + return temptable.AttachLocalTemporaryTableInfoSchema(s, is) +} + +func (s *session) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { + is := domain.GetDomain(s).InfoSchema() + extIs := &infoschema.SessionExtendedInfoSchema{InfoSchema: is} + return temptable.AttachLocalTemporaryTableInfoSchema(s, extIs) +} + +func getSnapshotInfoSchema(s sessionctx.Context, snapshotTS uint64) (infoschema.InfoSchema, error) { + is, err := domain.GetDomain(s).GetSnapshotInfoSchema(snapshotTS) + if err != nil { + return nil, err + } + // Set snapshot does not affect the witness of the local temporary table. + // The session always see the latest temporary tables. + return temptable.AttachLocalTemporaryTableInfoSchema(s, is), nil +} + +func (s *session) updateTelemetryMetric(es *executor.ExecStmt) { + if es.Ti == nil { + return + } + if s.isInternal() { + return + } + + ti := es.Ti + if ti.UseRecursive { + session_metrics.TelemetryCTEUsageRecurCTE.Inc() + } else if ti.UseNonRecursive { + session_metrics.TelemetryCTEUsageNonRecurCTE.Inc() + } else { + session_metrics.TelemetryCTEUsageNotCTE.Inc() + } + + if ti.UseIndexMerge { + session_metrics.TelemetryIndexMerge.Inc() + } + + if ti.UseMultiSchemaChange { + session_metrics.TelemetryMultiSchemaChangeUsage.Inc() + } + + if ti.UseFlashbackToCluster { + session_metrics.TelemetryFlashbackClusterUsage.Inc() + } + + if ti.UseExchangePartition { + session_metrics.TelemetryExchangePartitionUsage.Inc() + } + + if ti.PartitionTelemetry != nil { + if ti.PartitionTelemetry.UseTablePartition { + session_metrics.TelemetryTablePartitionUsage.Inc() + session_metrics.TelemetryTablePartitionMaxPartitionsUsage.Add(float64(ti.PartitionTelemetry.TablePartitionMaxPartitionsNum)) + } + if ti.PartitionTelemetry.UseTablePartitionList { + session_metrics.TelemetryTablePartitionListUsage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionRange { + session_metrics.TelemetryTablePartitionRangeUsage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionHash { + session_metrics.TelemetryTablePartitionHashUsage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionRangeColumns { + session_metrics.TelemetryTablePartitionRangeColumnsUsage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt1 { + session_metrics.TelemetryTablePartitionRangeColumnsGt1Usage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt2 { + session_metrics.TelemetryTablePartitionRangeColumnsGt2Usage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt3 { + session_metrics.TelemetryTablePartitionRangeColumnsGt3Usage.Inc() + } + if ti.PartitionTelemetry.UseTablePartitionListColumns { + session_metrics.TelemetryTablePartitionListColumnsUsage.Inc() + } + if ti.PartitionTelemetry.UseCreateIntervalPartition { + session_metrics.TelemetryTablePartitionCreateIntervalUsage.Inc() + } + if ti.PartitionTelemetry.UseAddIntervalPartition { + session_metrics.TelemetryTablePartitionAddIntervalUsage.Inc() + } + if ti.PartitionTelemetry.UseDropIntervalPartition { + session_metrics.TelemetryTablePartitionDropIntervalUsage.Inc() + } + if ti.PartitionTelemetry.UseCompactTablePartition { + session_metrics.TelemetryTableCompactPartitionUsage.Inc() + } + if ti.PartitionTelemetry.UseReorganizePartition { + session_metrics.TelemetryReorganizePartitionUsage.Inc() + } + } + + if ti.AccountLockTelemetry != nil { + session_metrics.TelemetryLockUserUsage.Add(float64(ti.AccountLockTelemetry.LockUser)) + session_metrics.TelemetryUnlockUserUsage.Add(float64(ti.AccountLockTelemetry.UnlockUser)) + session_metrics.TelemetryCreateOrAlterUserUsage.Add(float64(ti.AccountLockTelemetry.CreateOrAlterUser)) + } + + if ti.UseTableLookUp.Load() && s.sessionVars.StoreBatchSize > 0 { + session_metrics.TelemetryStoreBatchedUsage.Inc() + } +} + +// GetBuiltinFunctionUsage returns the replica of counting of builtin function usage +func (s *session) GetBuiltinFunctionUsage() map[string]uint32 { + replica := make(map[string]uint32) + s.functionUsageMu.RLock() + defer s.functionUsageMu.RUnlock() + for key, value := range s.functionUsageMu.builtinFunctionUsage { + replica[key] = value + } + return replica +} + +// BuiltinFunctionUsageInc increase the counting of the builtin function usage +func (s *session) BuiltinFunctionUsageInc(scalarFuncSigName string) { + s.functionUsageMu.Lock() + defer s.functionUsageMu.Unlock() + s.functionUsageMu.builtinFunctionUsage.Inc(scalarFuncSigName) +} + +func (s *session) GetStmtStats() *stmtstats.StatementStats { + return s.stmtStats +} + +// SetMemoryFootprintChangeHook sets the hook that is called when the memdb changes its size. +// Call this after s.txn becomes valid, since TxnInfo is initialized when the txn becomes valid. +func (s *session) SetMemoryFootprintChangeHook() { + if config.GetGlobalConfig().Performance.TxnTotalSizeLimit != config.DefTxnTotalSizeLimit { + // if the user manually specifies the config, don't involve the new memory tracker mechanism, let the old config + // work as before. + return + } + hook := func(mem uint64) { + if s.sessionVars.MemDBFootprint == nil { + tracker := memory.NewTracker(memory.LabelForMemDB, -1) + tracker.AttachTo(s.sessionVars.MemTracker) + s.sessionVars.MemDBFootprint = tracker + } + s.sessionVars.MemDBFootprint.ReplaceBytesUsed(int64(mem)) + } + s.txn.SetMemoryFootprintChangeHook(hook) +} + +// EncodeSessionStates implements SessionStatesHandler.EncodeSessionStates interface. +func (s *session) EncodeSessionStates(ctx context.Context, + _ sessionctx.Context, sessionStates *sessionstates.SessionStates) error { + // Transaction status is hard to encode, so we do not support it. + s.txn.mu.Lock() + valid := s.txn.Valid() + s.txn.mu.Unlock() + if valid { + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has an active transaction") + } + // Data in local temporary tables is hard to encode, so we do not support it. + // Check temporary tables here to avoid circle dependency. + if s.sessionVars.LocalTemporaryTables != nil { + localTempTables := s.sessionVars.LocalTemporaryTables.(*infoschema.SessionTables) + if localTempTables.Count() > 0 { + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has local temporary tables") + } + } + // The advisory locks will be released when the session is closed. + if len(s.advisoryLocks) > 0 { + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has advisory locks") + } + // The TableInfo stores session ID and server ID, so the session cannot be migrated. + if len(s.lockedTables) > 0 { + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has locked tables") + } + // It's insecure to migrate sandBoxMode because users can fake it. + if s.InSandBoxMode() { + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session is in sandbox mode") + } + + if err := s.sessionVars.EncodeSessionStates(ctx, sessionStates); err != nil { + return err + } + + hasRestrictVarPriv := false + checker := privilege.GetPrivilegeManager(s) + if checker == nil || checker.RequestDynamicVerification(s.sessionVars.ActiveRoles, "RESTRICTED_VARIABLES_ADMIN", false) { + hasRestrictVarPriv = true + } + // Encode session variables. We put it here instead of SessionVars to avoid cycle import. + sessionStates.SystemVars = make(map[string]string) + for _, sv := range variable.GetSysVars() { + switch { + case sv.HasNoneScope(), !sv.HasSessionScope(): + // Hidden attribute is deprecated. + // None-scoped variables cannot be modified. + // Noop variables should also be migrated even if they are noop. + continue + case sv.ReadOnly: + // Skip read-only variables here. We encode them into SessionStates manually. + continue + } + // Get all session variables because the default values may change between versions. + val, keep, err := s.sessionVars.GetSessionStatesSystemVar(sv.Name) + switch { + case err != nil: + return err + case !keep: + continue + case !hasRestrictVarPriv && sem.IsEnabled() && sem.IsInvisibleSysVar(sv.Name): + // If the variable has a global scope, it should be the same with the global one. + // Otherwise, it should be the same with the default value. + defaultVal := sv.Value + if sv.HasGlobalScope() { + // If the session value is the same with the global one, skip it. + if defaultVal, err = sv.GetGlobalFromHook(ctx, s.sessionVars); err != nil { + return err + } + } + if val != defaultVal { + // Case 1: the RESTRICTED_VARIABLES_ADMIN is revoked after setting the session variable. + // Case 2: the global variable is updated after the session is created. + // In any case, the variable can't be set in the new session, so give up. + return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs(fmt.Sprintf("session has set invisible variable '%s'", sv.Name)) + } + default: + sessionStates.SystemVars[sv.Name] = val + } + } + + // Encode prepared statements and sql bindings. + for _, handler := range s.sessionStatesHandlers { + if err := handler.EncodeSessionStates(ctx, s, sessionStates); err != nil { + return err + } + } + return nil +} + +// DecodeSessionStates implements SessionStatesHandler.DecodeSessionStates interface. +func (s *session) DecodeSessionStates(ctx context.Context, + _ sessionctx.Context, sessionStates *sessionstates.SessionStates) error { + // Decode prepared statements and sql bindings. + for _, handler := range s.sessionStatesHandlers { + if err := handler.DecodeSessionStates(ctx, s, sessionStates); err != nil { + return err + } + } + + // Decode session variables. + names := variable.OrderByDependency(sessionStates.SystemVars) + // Some variables must be set before others, e.g. tidb_enable_noop_functions should be before noop variables. + for _, name := range names { + val := sessionStates.SystemVars[name] + // Experimental system variables may change scope, data types, or even be removed. + // We just ignore the errors and continue. + if err := s.sessionVars.SetSystemVar(name, val); err != nil { + logutil.Logger(ctx).Warn("set session variable during decoding session states error", + zap.String("name", name), zap.String("value", val), zap.Error(err)) + } + } + + // Decoding session vars / prepared statements may override stmt ctx, such as warnings, + // so we decode stmt ctx at last. + return s.sessionVars.DecodeSessionStates(ctx, sessionStates) +} + +func (s *session) setRequestSource(ctx context.Context, stmtLabel string, stmtNode ast.StmtNode) { + if !s.isInternal() { + if txn, _ := s.Txn(false); txn != nil && txn.Valid() { + txn.SetOption(kv.RequestSourceType, stmtLabel) + } + s.sessionVars.RequestSourceType = stmtLabel + return + } + if source := ctx.Value(kv.RequestSourceKey); source != nil { + requestSource := source.(kv.RequestSource) + if requestSource.RequestSourceType != "" { + s.sessionVars.RequestSourceType = requestSource.RequestSourceType + return + } + } + // panic in test mode in case there are requests without source in the future. + // log warnings in production mode. + if intest.InTest { + panic("unexpected no source type context, if you see this error, " + + "the `RequestSourceTypeKey` is missing in your context") + } else { + logutil.Logger(ctx).Warn("unexpected no source type context, if you see this warning, "+ + "the `RequestSourceTypeKey` is missing in the context", + zap.Bool("internal", s.isInternal()), + zap.String("sql", stmtNode.Text())) + } +} + +// RemoveLockDDLJobs removes the DDL jobs which doesn't get the metadata lock from job2ver. +func RemoveLockDDLJobs(s Session, job2ver map[int64]int64, job2ids map[int64]string, printLog bool) { + sv := s.GetSessionVars() + if sv.InRestrictedSQL { + return + } + sv.TxnCtxMu.Lock() + defer sv.TxnCtxMu.Unlock() + if sv.TxnCtx == nil { + return + } + sv.GetRelatedTableForMDL().Range(func(tblID, value any) bool { + for jobID, ver := range job2ver { + ids := util.Str2Int64Map(job2ids[jobID]) + if _, ok := ids[tblID.(int64)]; ok && value.(int64) < ver { + delete(job2ver, jobID) + elapsedTime := time.Since(oracle.GetTimeFromTS(sv.TxnCtx.StartTS)) + if elapsedTime > time.Minute && printLog { + logutil.BgLogger().Info("old running transaction block DDL", zap.Int64("table ID", tblID.(int64)), zap.Int64("jobID", jobID), zap.Uint64("connection ID", sv.ConnectionID), zap.Duration("elapsed time", elapsedTime)) + } else { + logutil.BgLogger().Debug("old running transaction block DDL", zap.Int64("table ID", tblID.(int64)), zap.Int64("jobID", jobID), zap.Uint64("connection ID", sv.ConnectionID), zap.Duration("elapsed time", elapsedTime)) + } + } + } + return true + }) +} + +// GetDBNames gets the sql layer database names from the session. +func GetDBNames(seVar *variable.SessionVars) []string { + dbNames := make(map[string]struct{}) + if seVar == nil || !config.GetGlobalConfig().Status.RecordDBLabel { + return []string{""} + } + if seVar.StmtCtx != nil { + for _, t := range seVar.StmtCtx.Tables { + dbNames[t.DB] = struct{}{} + } + } + if len(dbNames) == 0 { + dbNames[seVar.CurrentDB] = struct{}{} + } + ns := make([]string, 0, len(dbNames)) + for n := range dbNames { + ns = append(ns, n) + } + return ns +} diff --git a/session/sync_upgrade.go b/pkg/session/sync_upgrade.go similarity index 95% rename from session/sync_upgrade.go rename to pkg/session/sync_upgrade.go index ae51998830648..97f1a01b8589e 100644 --- a/session/sync_upgrade.go +++ b/pkg/session/sync_upgrade.go @@ -19,12 +19,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/syncer" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/syncer" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/owner" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/session/temporarytabletest/BUILD.bazel b/pkg/session/temporarytabletest/BUILD.bazel new file mode 100644 index 0000000000000..3e60094771458 --- /dev/null +++ b/pkg/session/temporarytabletest/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "temporarytabletest_test", + timeout = "short", + srcs = [ + "main_test.go", + "temporary_table_test.go", + ], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/temporarytabletest/main_test.go b/pkg/session/temporarytabletest/main_test.go new file mode 100644 index 0000000000000..46facff98e529 --- /dev/null +++ b/pkg/session/temporarytabletest/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package temporarytabletest + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/temporarytabletest/temporary_table_test.go b/pkg/session/temporarytabletest/temporary_table_test.go new file mode 100644 index 0000000000000..ac3431626acc4 --- /dev/null +++ b/pkg/session/temporarytabletest/temporary_table_test.go @@ -0,0 +1,517 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package temporarytabletest + +import ( + "fmt" + "sort" + "strconv" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestLocalTemporaryTableUpdate(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create temporary table tmp1 (id int primary key, u int unique, v int)") + + idList := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + insertRecords := func(idList []int) { + for _, id := range idList { + tk.MustExec("insert into tmp1 values (?, ?, ?)", id, id+100, id+1000) + } + } + + checkNoChange := func() { + expect := make([]string, 0) + for _, id := range idList { + expect = append(expect, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) + } + tk.MustQuery("select * from tmp1").Check(testkit.Rows(expect...)) + } + + checkUpdatesAndDeletes := func(updates []string, deletes []int) { + modifyMap := make(map[int]string) + for _, m := range updates { + parts := strings.Split(strings.TrimSpace(m), " ") + require.NotZero(t, len(parts)) + id, err := strconv.Atoi(parts[0]) + require.NoError(t, err) + modifyMap[id] = m + } + + for _, d := range deletes { + modifyMap[d] = "" + } + + expect := make([]string, 0) + for _, id := range idList { + modify, exist := modifyMap[id] + if !exist { + expect = append(expect, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) + continue + } + + if modify != "" { + expect = append(expect, modify) + } + + delete(modifyMap, id) + } + + otherIds := make([]int, 0) + for id := range modifyMap { + otherIds = append(otherIds, id) + } + + sort.Ints(otherIds) + for _, id := range otherIds { + modify, exist := modifyMap[id] + require.True(t, exist) + expect = append(expect, modify) + } + + tk.MustQuery("select * from tmp1").Check(testkit.Rows(expect...)) + } + + type checkSuccess struct { + update []string + delete []int + } + + type checkError struct { + err error + } + + cases := []struct { + sql string + checkResult interface{} + additionalCheck func(error) + }{ + // update with point get for primary key + {"update tmp1 set v=999 where id=1", checkSuccess{[]string{"1 101 999"}, nil}, nil}, + {"update tmp1 set id=12 where id=1", checkSuccess{[]string{"12 101 1001"}, []int{1}}, nil}, + {"update tmp1 set id=1 where id=1", checkSuccess{nil, nil}, nil}, + {"update tmp1 set u=101 where id=1", checkSuccess{nil, nil}, nil}, + {"update tmp1 set v=999 where id=100", checkSuccess{nil, nil}, nil}, + {"update tmp1 set u=102 where id=100", checkSuccess{nil, nil}, nil}, + {"update tmp1 set u=21 where id=1", checkSuccess{[]string{"1 21 1001"}, nil}, func(_ error) { + // check index deleted + tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u=101").Check(testkit.Rows()) + tk.MustQuery("show warnings").Check(testkit.Rows()) + }}, + {"update tmp1 set id=2 where id=1", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set u=102 where id=1", checkError{kv.ErrKeyExists}, nil}, + // update with batch point get for primary key + {"update tmp1 set v=v+1000 where id in (1, 3, 5)", checkSuccess{[]string{"1 101 2001", "3 103 2003", "5 105 2005"}, nil}, nil}, + {"update tmp1 set u=u+1 where id in (9, 100)", checkSuccess{[]string{"9 110 1009"}, nil}, nil}, + {"update tmp1 set u=101 where id in (100, 101)", checkSuccess{nil, nil}, nil}, + {"update tmp1 set id=id+1 where id in (8, 9)", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set u=u+1 where id in (8, 9)", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set id=id+20 where id in (1, 3, 5)", checkSuccess{[]string{"21 101 1001", "23 103 1003", "25 105 1005"}, []int{1, 3, 5}}, nil}, + {"update tmp1 set u=u+100 where id in (1, 3, 5)", checkSuccess{[]string{"1 201 1001", "3 203 1003", "5 205 1005"}, nil}, func(_ error) { + // check index deleted + tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u in (101, 103, 105)").Check(testkit.Rows()) + tk.MustQuery("show warnings").Check(testkit.Rows()) + }}, + // update with point get for unique key + {"update tmp1 set v=888 where u=101", checkSuccess{[]string{"1 101 888"}, nil}, nil}, + {"update tmp1 set id=21 where u=101", checkSuccess{[]string{"21 101 1001"}, []int{1}}, nil}, + {"update tmp1 set v=888 where u=201", checkSuccess{nil, nil}, nil}, + {"update tmp1 set u=201 where u=101", checkSuccess{[]string{"1 201 1001"}, nil}, nil}, + {"update tmp1 set id=2 where u=101", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set u=102 where u=101", checkError{kv.ErrKeyExists}, nil}, + // update with batch point get for unique key + {"update tmp1 set v=v+1000 where u in (101, 103)", checkSuccess{[]string{"1 101 2001", "3 103 2003"}, nil}, nil}, + {"update tmp1 set v=v+1000 where u in (201, 203)", checkSuccess{nil, nil}, nil}, + {"update tmp1 set v=v+1000 where u in (101, 110)", checkSuccess{[]string{"1 101 2001"}, nil}, nil}, + {"update tmp1 set id=id+1 where u in (108, 109)", checkError{kv.ErrKeyExists}, nil}, + // update with table scan and index scan + {"update tmp1 set v=v+1000 where id<3", checkSuccess{[]string{"1 101 2001", "2 102 2002"}, nil}, nil}, + {"update /*+ use_index(tmp1, u) */ tmp1 set v=v+1000 where u>107", checkSuccess{[]string{"8 108 2008", "9 109 2009"}, nil}, nil}, + {"update tmp1 set v=v+1000 where v>=1007 or v<=1002", checkSuccess{[]string{"1 101 2001", "2 102 2002", "7 107 2007", "8 108 2008", "9 109 2009"}, nil}, nil}, + {"update tmp1 set v=v+1000 where id>=10", checkSuccess{nil, nil}, nil}, + {"update tmp1 set id=id+1 where id>7", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set id=id+1 where id>8", checkSuccess{[]string{"10 109 1009"}, []int{9}}, nil}, + {"update tmp1 set u=u+1 where u>107", checkError{kv.ErrKeyExists}, nil}, + {"update tmp1 set u=u+1 where u>108", checkSuccess{[]string{"9 110 1009"}, nil}, nil}, + {"update /*+ use_index(tmp1, u) */ tmp1 set v=v+1000 where u>108 or u<102", checkSuccess{[]string{"1 101 2001", "9 109 2009"}, nil}, nil}, + } + + executeSQL := func(sql string, checkResult interface{}, additionalCheck func(error)) (err error) { + switch check := checkResult.(type) { + case checkSuccess: + tk.MustExec(sql) + tk.MustQuery("show warnings").Check(testkit.Rows()) + checkUpdatesAndDeletes(check.update, check.delete) + case checkError: + err = tk.ExecToErr(sql) + require.Error(t, err) + expectedErr, _ := check.err.(*terror.Error) + require.True(t, expectedErr.Equal(err)) + checkNoChange() + default: + t.Fail() + } + + if additionalCheck != nil { + additionalCheck(err) + } + return + } + + for _, sqlCase := range cases { + // update records in txn and records are inserted in txn + tk.MustExec("begin") + insertRecords(idList) + _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) + tk.MustExec("rollback") + tk.MustQuery("select * from tmp1").Check(testkit.Rows()) + + // update records out of txn + insertRecords(idList) + _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) + tk.MustExec("delete from tmp1") + + // update records in txn and rollback + insertRecords(idList) + tk.MustExec("begin") + _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) + tk.MustExec("rollback") + // rollback left records unmodified + checkNoChange() + + // update records in txn and commit + tk.MustExec("begin") + err := executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) + tk.MustExec("commit") + if err != nil { + checkNoChange() + } else { + r, _ := sqlCase.checkResult.(checkSuccess) + checkUpdatesAndDeletes(r.update, r.delete) + } + if sqlCase.additionalCheck != nil { + sqlCase.additionalCheck(err) + } + tk.MustExec("delete from tmp1") + tk.MustQuery("select * from tmp1").Check(testkit.Rows()) + } +} + +func TestRetryGlobalTemporaryTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists normal_table") + tk.MustExec("create table normal_table(a int primary key, b int)") + defer tk.MustExec("drop table if exists normal_table") + tk.MustExec("drop table if exists temp_table") + tk.MustExec("create global temporary table temp_table(a int primary key, b int) on commit delete rows") + defer tk.MustExec("drop table if exists temp_table") + + // insert select + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + tk.MustExec("insert normal_table value(100, 100)") + tk.MustExec("set @@autocommit = 0") + // used to make conflicts + tk.MustExec("update normal_table set b=b+1 where a=100") + tk.MustExec("insert temp_table value(1, 1)") + tk.MustExec("insert normal_table select * from temp_table") + require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) + + // try to conflict with tk + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("update normal_table set b=b+1 where a=100") + + // It will retry internally. + tk.MustExec("commit") + tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 1", "100 102")) + tk.MustQuery("select a, b from temp_table order by a").Check(testkit.Rows()) + + // update multi-tables + tk.MustExec("update normal_table set b=b+1 where a=100") + tk.MustExec("insert temp_table value(1, 2)") + // before update: normal_table=(1 1) (100 102), temp_table=(1 2) + tk.MustExec("update normal_table, temp_table set normal_table.b=temp_table.b where normal_table.a=temp_table.a") + require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) + + // try to conflict with tk + tk1.MustExec("update normal_table set b=b+1 where a=100") + + // It will retry internally. + tk.MustExec("commit") + tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 104")) +} + +func TestRetryLocalTemporaryTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists normal_table") + tk.MustExec("create table normal_table(a int primary key, b int)") + defer tk.MustExec("drop table if exists normal_table") + tk.MustExec("drop table if exists temp_table") + tk.MustExec("create temporary table l_temp_table(a int primary key, b int)") + defer tk.MustExec("drop table if exists l_temp_table") + + // insert select + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + tk.MustExec("insert normal_table value(100, 100)") + tk.MustExec("set @@autocommit = 0") + // used to make conflicts + tk.MustExec("update normal_table set b=b+1 where a=100") + tk.MustExec("insert l_temp_table value(1, 2)") + tk.MustExec("insert normal_table select * from l_temp_table") + require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) + + // try to conflict with tk + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("update normal_table set b=b+1 where a=100") + + // It will retry internally. + tk.MustExec("commit") + tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 102")) + tk.MustQuery("select a, b from l_temp_table order by a").Check(testkit.Rows("1 2")) + + // update multi-tables + tk.MustExec("update normal_table set b=b+1 where a=100") + tk.MustExec("insert l_temp_table value(3, 4)") + // before update: normal_table=(1 1) (100 102), temp_table=(1 2) + tk.MustExec("update normal_table, l_temp_table set normal_table.b=l_temp_table.b where normal_table.a=l_temp_table.a") + require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) + + // try to conflict with tk + tk1.MustExec("update normal_table set b=b+1 where a=100") + + // It will retry internally. + tk.MustExec("commit") + tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 104")) +} + +func TestLocalTemporaryTableDelete(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create temporary table tmp1 (id int primary key, u int unique, v int)") + + insertRecords := func(idList []int) { + for _, id := range idList { + tk.MustExec("insert into tmp1 values (?, ?, ?)", id, id+100, id+1000) + } + } + + checkAllExistRecords := func(idList []int) { + sort.Ints(idList) + expectedResult := make([]string, 0, len(idList)) + expectedIndexResult := make([]string, 0, len(idList)) + for _, id := range idList { + expectedResult = append(expectedResult, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) + expectedIndexResult = append(expectedIndexResult, fmt.Sprintf("%d", id+100)) + } + tk.MustQuery("select * from tmp1 order by id").Check(testkit.Rows(expectedResult...)) + + // check index deleted + tk.MustQuery("select /*+ use_index(tmp1, u) */ u from tmp1 order by u").Check(testkit.Rows(expectedIndexResult...)) + tk.MustQuery("show warnings").Check(testkit.Rows()) + } + + assertDelete := func(sql string, deleted []int) { + idList := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} + + deletedMap := make(map[int]bool) + for _, id := range deleted { + deletedMap[id] = true + } + + keepList := make([]int, 0) + for _, id := range idList { + if _, exist := deletedMap[id]; !exist { + keepList = append(keepList, id) + } + } + + // delete records in txn and records are inserted in txn + tk.MustExec("begin") + insertRecords(idList) + tk.MustExec(sql) + tk.MustQuery("show warnings").Check(testkit.Rows()) + checkAllExistRecords(keepList) + tk.MustExec("rollback") + checkAllExistRecords([]int{}) + + // delete records out of txn + insertRecords(idList) + tk.MustExec(sql) + checkAllExistRecords(keepList) + + // delete records in txn + insertRecords(deleted) + tk.MustExec("begin") + tk.MustExec(sql) + checkAllExistRecords(keepList) + + // test rollback + tk.MustExec("rollback") + checkAllExistRecords(idList) + + // test commit + tk.MustExec("begin") + tk.MustExec(sql) + tk.MustExec("commit") + checkAllExistRecords(keepList) + + tk.MustExec("delete from tmp1") + checkAllExistRecords([]int{}) + } + + assertDelete("delete from tmp1 where id=1", []int{1}) + assertDelete("delete from tmp1 where id in (1, 3, 5)", []int{1, 3, 5}) + assertDelete("delete from tmp1 where u=102", []int{2}) + assertDelete("delete from tmp1 where u in (103, 107, 108)", []int{3, 7, 8}) + assertDelete("delete from tmp1 where id=10", []int{}) + assertDelete("delete from tmp1 where id in (10, 12)", []int{}) + assertDelete("delete from tmp1 where u=110", []int{}) + assertDelete("delete from tmp1 where u in (111, 112)", []int{}) + assertDelete("delete from tmp1 where id in (1, 11, 5)", []int{1, 5}) + assertDelete("delete from tmp1 where u in (102, 121, 106)", []int{2, 6}) + assertDelete("delete from tmp1 where id<3", []int{1, 2}) + assertDelete("delete from tmp1 where u>107", []int{8, 9}) + assertDelete("delete /*+ use_index(tmp1, u) */ from tmp1 where u>105 and u<107", []int{6}) + assertDelete("delete from tmp1 where v>=1006 or v<=1002", []int{1, 2, 6, 7, 8, 9}) +} + +func TestSchemaCheckerTempTable(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk1.MustExec("set global tidb_enable_metadata_lock=0") + tk2.MustExec("use test") + + // create table + tk1.MustExec(`drop table if exists normal_table`) + tk1.MustExec(`create table normal_table (id int, c int);`) + defer tk1.MustExec(`drop table if exists normal_table`) + tk1.MustExec(`drop table if exists temp_table`) + tk1.MustExec(`create global temporary table temp_table (id int primary key, c int) on commit delete rows;`) + defer tk1.MustExec(`drop table if exists temp_table`) + + // The schema version is out of date in the first transaction, and the SQL can't be retried. + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 1) + defer func() { + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 0) + }() + + // It's fine to change the schema of temporary tables. + tk1.MustExec(`begin;`) + tk2.MustExec(`alter table temp_table modify column c tinyint;`) + tk1.MustExec(`insert into temp_table values(3, 3);`) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c int;`) + tk1.MustQuery(`select * from temp_table for update;`).Check(testkit.Rows()) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c smallint;`) + tk1.MustExec(`insert into temp_table values(3, 4);`) + tk1.MustQuery(`select * from temp_table for update;`).Check(testkit.Rows("3 4")) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c bigint;`) + tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows()) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c smallint;`) + tk1.MustExec("insert into temp_table values (1, 2), (2, 3), (4, 5)") + tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows("1 2")) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c int;`) + tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows()) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c bigint;`) + tk1.MustQuery(`select * from temp_table where id in (1, 2, 3) for update;`).Check(testkit.Rows()) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c int;`) + tk1.MustExec("insert into temp_table values (1, 2), (2, 3), (4, 5)") + tk1.MustQuery(`select * from temp_table where id in (1, 2, 3) for update;`).Check(testkit.Rows("1 2", "2 3")) + tk1.MustExec(`commit;`) + + tk1.MustExec("insert into normal_table values(1, 2)") + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table temp_table modify column c int;`) + tk1.MustExec(`insert into temp_table values(1, 5);`) + tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows("1 5 1 2")) + tk1.MustExec(`commit;`) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table normal_table modify column c bigint;`) + tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows()) + tk1.MustExec(`commit;`) + + // Truncate will modify table ID. + tk1.MustExec(`begin;`) + tk2.MustExec(`truncate table temp_table;`) + tk1.MustExec(`insert into temp_table values(3, 3);`) + tk1.MustExec(`commit;`) + + // It reports error when also changing the schema of a normal table. + tk1.MustExec(`begin;`) + tk2.MustExec(`alter table normal_table modify column c bigint;`) + tk1.MustExec(`insert into temp_table values(3, 3);`) + tk1.MustExec(`insert into normal_table values(3, 3);`) + err := tk1.ExecToErr(`commit;`) + require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) + + tk1.MustExec("begin pessimistic") + tk2.MustExec(`alter table normal_table modify column c int;`) + tk1.MustExec(`insert into temp_table values(1, 6);`) + tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows("1 6 1 2")) + err = tk1.ExecToErr(`commit;`) + require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) +} diff --git a/pkg/session/test/BUILD.bazel b/pkg/session/test/BUILD.bazel new file mode 100644 index 0000000000000..6bbcc53ed021d --- /dev/null +++ b/pkg/session/test/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "test_test", + timeout = "short", + srcs = [ + "main_test.go", + "session_test.go", + ], + flaky = True, + shard_count = 26, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util", + "//pkg/util/sqlexec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/common/BUILD.bazel b/pkg/session/test/common/BUILD.bazel new file mode 100644 index 0000000000000..9090e70422b23 --- /dev/null +++ b/pkg/session/test/common/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "common_test", + timeout = "short", + srcs = [ + "common_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 8, + deps = [ + "//pkg/config", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/common/common_test.go b/pkg/session/test/common/common_test.go new file mode 100644 index 0000000000000..4965170db5202 --- /dev/null +++ b/pkg/session/test/common/common_test.go @@ -0,0 +1,365 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package common + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestMiscs(t *testing.T) { + store := testkit.CreateMockStore(t) + + // TestString + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("select 1") + // here to check the panic bug in String() when txn is nil after committed. + t.Log(tk.Session().String()) + + // TestLastExecuteDDLFlag + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(id int)") + require.NotNil(t, tk.Session().Value(sessionctx.LastExecuteDDL)) + tk.MustExec("insert into t1 values (1)") + require.Nil(t, tk.Session().Value(sessionctx.LastExecuteDDL)) + + // TestSession + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("ROLLBACK;") + tk.Session().Close() +} + +func TestPrepare(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id TEXT)") + tk.MustExec(`INSERT INTO t VALUES ("id");`) + id, ps, _, err := tk.Session().PrepareStmt("select id+? from t") + ctx := context.Background() + require.NoError(t, err) + require.Equal(t, uint32(1), id) + require.Equal(t, 1, ps) + tk.MustExec(`set @a=1`) + rs, err := tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test("1")) + require.NoError(t, err) + require.NoError(t, rs.Close()) + err = tk.Session().DropPreparedStmt(id) + require.NoError(t, err) + + tk.MustExec("prepare stmt from 'select 1+?'") + tk.MustExec("set @v1=100") + tk.MustQuery("execute stmt using @v1").Check(testkit.Rows("101")) + + tk.MustExec("set @v2=200") + tk.MustQuery("execute stmt using @v2").Check(testkit.Rows("201")) + + tk.MustExec("set @v3=300") + tk.MustQuery("execute stmt using @v3").Check(testkit.Rows("301")) + tk.MustExec("deallocate prepare stmt") + + // Execute prepared statements for more than one time. + tk.MustExec("create table multiexec (a int, b int)") + tk.MustExec("insert multiexec values (1, 1), (2, 2)") + id, _, _, err = tk.Session().PrepareStmt("select a from multiexec where b = ? order by b") + require.NoError(t, err) + rs, err = tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + require.NoError(t, rs.Close()) + rs, err = tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test(2)) + require.NoError(t, err) + require.NoError(t, rs.Close()) +} + +func TestIndexColumnLength(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (c1 int, c2 blob);") + tk.MustExec("create index idx_c1 on t(c1);") + tk.MustExec("create index idx_c2 on t(c2(6));") + + is := dom.InfoSchema() + tab, err2 := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err2) + + idxC1Cols := tables.FindIndexByColName(tab, "c1").Meta().Columns + require.Equal(t, types.UnspecifiedLength, idxC1Cols[0].Length) + + idxC2Cols := tables.FindIndexByColName(tab, "c2").Meta().Columns + require.Equal(t, 6, idxC2Cols[0].Length) +} + +// test for https://github.com/pingcap/tidb/pull/461 +func TestUnique(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + tk.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk1.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk.MustExec(`CREATE TABLE test ( id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, val int UNIQUE, PRIMARY KEY (id)); `) + tk.MustExec("begin;") + tk.MustExec("insert into test(id, val) values(1, 1);") + tk1.MustExec("begin;") + tk1.MustExec("insert into test(id, val) values(2, 2);") + tk2.MustExec("begin;") + tk2.MustExec("insert into test(id, val) values(1, 2);") + tk2.MustExec("commit;") + _, err := tk.Exec("commit") + require.Error(t, err) + // Check error type and error message + require.True(t, terror.ErrorEqual(err, kv.ErrKeyExists), fmt.Sprintf("err %v", err)) + require.Equal(t, "previous statement: insert into test(id, val) values(1, 1);: [kv:1062]Duplicate entry '1' for key 'test.PRIMARY'", err.Error()) + + _, err = tk1.Exec("commit") + require.Error(t, err) + require.True(t, terror.ErrorEqual(err, kv.ErrKeyExists), fmt.Sprintf("err %v", err)) + require.Equal(t, "previous statement: insert into test(id, val) values(2, 2);: [kv:1062]Duplicate entry '2' for key 'test.val'", err.Error()) + + // Test for https://github.com/pingcap/tidb/issues/463 + tk.MustExec("drop table test;") + tk.MustExec(`CREATE TABLE test ( + id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + val int UNIQUE, + PRIMARY KEY (id) + );`) + tk.MustExec("insert into test(id, val) values(1, 1);") + _, err = tk.Exec("insert into test(id, val) values(2, 1);") + require.Error(t, err) + tk.MustExec("insert into test(id, val) values(2, 2);") + + tk.MustExec("begin;") + tk.MustExec("insert into test(id, val) values(3, 3);") + _, err = tk.Exec("insert into test(id, val) values(4, 3);") + require.Error(t, err) + tk.MustExec("insert into test(id, val) values(4, 4);") + tk.MustExec("commit;") + + tk1.MustExec("begin;") + tk1.MustExec("insert into test(id, val) values(5, 6);") + tk.MustExec("begin;") + tk.MustExec("insert into test(id, val) values(20, 6);") + tk.MustExec("commit;") + _, _ = tk1.Exec("commit") + tk1.MustExec("insert into test(id, val) values(5, 5);") + + tk.MustExec("drop table test;") + tk.MustExec(`CREATE TABLE test ( + id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + val1 int UNIQUE, + val2 int UNIQUE, + PRIMARY KEY (id) + );`) + tk.MustExec("insert into test(id, val1, val2) values(1, 1, 1);") + tk.MustExec("insert into test(id, val1, val2) values(2, 2, 2);") + _, _ = tk.Exec("update test set val1 = 3, val2 = 2 where id = 1;") + tk.MustExec("insert into test(id, val1, val2) values(3, 3, 3);") +} + +func TestTableInfoMeta(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + checkResult := func(affectedRows uint64, insertID uint64) { + gotRows := tk.Session().AffectedRows() + require.Equal(t, affectedRows, gotRows) + + gotID := tk.Session().LastInsertID() + require.Equal(t, insertID, gotID) + } + + // create table + tk.MustExec("CREATE TABLE tbl_test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") + + // insert data + tk.MustExec(`INSERT INTO tbl_test VALUES (1, "hello");`) + checkResult(1, 0) + + tk.MustExec(`INSERT INTO tbl_test VALUES (2, "hello");`) + checkResult(1, 0) + + tk.MustExec(`UPDATE tbl_test SET name = "abc" where id = 2;`) + checkResult(1, 0) + + tk.MustExec(`DELETE from tbl_test where id = 2;`) + checkResult(1, 0) + + // select data + tk.MustQuery("select * from tbl_test").Check(testkit.Rows("1 hello")) +} + +func TestLastMessage(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id TEXT)") + + // Insert + tk.MustExec(`INSERT INTO t VALUES ("a");`) + tk.CheckLastMessage("") + tk.MustExec(`INSERT INTO t VALUES ("b"), ("c");`) + tk.CheckLastMessage("Records: 2 Duplicates: 0 Warnings: 0") + + // Update + tk.MustExec(`UPDATE t set id = 'c' where id = 'a';`) + require.Equal(t, uint64(1), tk.Session().AffectedRows()) + tk.CheckLastMessage("Rows matched: 1 Changed: 1 Warnings: 0") + tk.MustExec(`UPDATE t set id = 'a' where id = 'a';`) + require.Equal(t, uint64(0), tk.Session().AffectedRows()) + tk.CheckLastMessage("Rows matched: 0 Changed: 0 Warnings: 0") + + // Replace + tk.MustExec(`drop table if exists t, t1; + create table t (c1 int PRIMARY KEY, c2 int); + create table t1 (a1 int, a2 int);`) + tk.MustExec(`INSERT INTO t VALUES (1,1)`) + tk.MustExec(`REPLACE INTO t VALUES (2,2)`) + tk.CheckLastMessage("") + tk.MustExec(`INSERT INTO t1 VALUES (1,10), (3,30);`) + tk.CheckLastMessage("Records: 2 Duplicates: 0 Warnings: 0") + tk.MustExec(`REPLACE INTO t SELECT * from t1`) + tk.CheckLastMessage("Records: 2 Duplicates: 1 Warnings: 0") + + // Check insert with CLIENT_FOUND_ROWS is set + tk.Session().SetClientCapability(mysql.ClientFoundRows) + tk.MustExec(`drop table if exists t, t1; + create table t (c1 int PRIMARY KEY, c2 int); + create table t1 (a1 int, a2 int);`) + tk.MustExec(`INSERT INTO t1 VALUES (1, 10), (2, 2), (3, 30);`) + tk.MustExec(`INSERT INTO t1 VALUES (1, 10), (2, 20), (3, 30);`) + tk.MustExec(`INSERT INTO t SELECT * FROM t1 ON DUPLICATE KEY UPDATE c2=a2;`) + tk.CheckLastMessage("Records: 6 Duplicates: 3 Warnings: 0") +} + +func TestQueryString(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table mutil1 (a int);create table multi2 (a int)") + queryStr := tk.Session().Value(sessionctx.QueryString) + require.Equal(t, "create table multi2 (a int)", queryStr) + + // Test execution of DDL through the "ExecutePreparedStmt" interface. + tk.MustExec("use test") + tk.MustExec("CREATE TABLE t (id bigint PRIMARY KEY, age int)") + tk.MustExec("show create table t") + id, _, _, err := tk.Session().PrepareStmt("CREATE TABLE t2(id bigint PRIMARY KEY, age int)") + require.NoError(t, err) + _, err = tk.Session().ExecutePreparedStmt(context.Background(), id, expression.Args2Expressions4Test()) + require.NoError(t, err) + qs := tk.Session().Value(sessionctx.QueryString) + require.Equal(t, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)", qs.(string)) + + // Test execution of DDL through the "Execute" interface. + tk.MustExec("use test") + tk.MustExec("drop table t2") + tk.MustExec("prepare stmt from 'CREATE TABLE t2(id bigint PRIMARY KEY, age int)'") + tk.MustExec("execute stmt") + qs = tk.Session().Value(sessionctx.QueryString) + require.Equal(t, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)", qs.(string)) +} + +func TestAffectedRows(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id TEXT)") + tk.MustExec(`INSERT INTO t VALUES ("a");`) + require.Equal(t, 1, int(tk.Session().AffectedRows())) + tk.MustExec(`INSERT INTO t VALUES ("b");`) + require.Equal(t, 1, int(tk.Session().AffectedRows())) + tk.MustExec(`UPDATE t set id = 'c' where id = 'a';`) + require.Equal(t, 1, int(tk.Session().AffectedRows())) + tk.MustExec(`UPDATE t set id = 'a' where id = 'a';`) + require.Equal(t, 0, int(tk.Session().AffectedRows())) + tk.MustQuery(`SELECT * from t`).Check(testkit.Rows("c", "b")) + require.Equal(t, 0, int(tk.Session().AffectedRows())) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int, data int)") + tk.MustExec(`INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`) + tk.MustExec(`UPDATE t set id = 1 where data = 0;`) + require.Equal(t, 1, int(tk.Session().AffectedRows())) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int, c1 timestamp);") + tk.MustExec(`insert t(id) values(1);`) + tk.MustExec(`UPDATE t set id = 1 where id = 1;`) + require.Equal(t, 0, int(tk.Session().AffectedRows())) + + // With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, + // 2 if an existing row is updated, and 0 if an existing row is set to its current values. + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (c1 int PRIMARY KEY, c2 int);") + tk.MustExec(`insert t values(1, 1);`) + tk.MustExec(`insert into t values (1, 1) on duplicate key update c2=2;`) + require.Equal(t, 2, int(tk.Session().AffectedRows())) + tk.MustExec(`insert into t values (1, 1) on duplicate key update c2=2;`) + require.Equal(t, 0, int(tk.Session().AffectedRows())) + tk.MustExec("drop table if exists test") + createSQL := `CREATE TABLE test ( + id VARCHAR(36) PRIMARY KEY NOT NULL, + factor INTEGER NOT NULL DEFAULT 2);` + tk.MustExec(createSQL) + insertSQL := `INSERT INTO test(id) VALUES('id') ON DUPLICATE KEY UPDATE factor=factor+3;` + tk.MustExec(insertSQL) + require.Equal(t, 1, int(tk.Session().AffectedRows())) + tk.MustExec(insertSQL) + require.Equal(t, 2, int(tk.Session().AffectedRows())) + tk.MustExec(insertSQL) + require.Equal(t, 2, int(tk.Session().AffectedRows())) + + tk.Session().SetClientCapability(mysql.ClientFoundRows) + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int, data int)") + tk.MustExec(`INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`) + tk.MustExec(`UPDATE t set id = 1 where data = 0;`) + require.Equal(t, 2, int(tk.Session().AffectedRows())) +} diff --git a/pkg/session/test/common/main_test.go b/pkg/session/test/common/main_test.go new file mode 100644 index 0000000000000..7eff34b528405 --- /dev/null +++ b/pkg/session/test/common/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package common + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/main_test.go b/pkg/session/test/main_test.go new file mode 100644 index 0000000000000..4924098dc59a2 --- /dev/null +++ b/pkg/session/test/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package test + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/meta/BUILD.bazel b/pkg/session/test/meta/BUILD.bazel new file mode 100644 index 0000000000000..e6cc77a8af18d --- /dev/null +++ b/pkg/session/test/meta/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "meta_test", + timeout = "short", + srcs = [ + "main_test.go", + "session_test.go", + ], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/config", + "//pkg/ddl", + "//pkg/metrics", + "//pkg/session", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/meta/main_test.go b/pkg/session/test/meta/main_test.go new file mode 100644 index 0000000000000..8597e36fc096e --- /dev/null +++ b/pkg/session/test/meta/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package meta + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/meta/session_test.go b/pkg/session/test/meta/session_test.go new file mode 100644 index 0000000000000..055b036b61434 --- /dev/null +++ b/pkg/session/test/meta/session_test.go @@ -0,0 +1,174 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package meta_test + +import ( + "fmt" + "reflect" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/prometheus/client_golang/prometheus" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +func TestInitMetaTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + for _, sql := range session.DDLJobTables { + tk.MustExec(sql.SQL) + } + + for _, sql := range session.BackfillTables { + tk.MustExec(sql.SQL) + } + + tbls := map[string]struct{}{ + "tidb_ddl_job": {}, + "tidb_ddl_reorg": {}, + "tidb_ddl_history": {}, + "tidb_background_subtask": {}, + "tidb_background_subtask_history": {}, + } + + for tbl := range tbls { + metaInMySQL := external.GetTableByName(t, tk, "mysql", tbl).Meta().Clone() + metaInTest := external.GetTableByName(t, tk, "test", tbl).Meta().Clone() + + require.Greater(t, metaInMySQL.ID, int64(0)) + require.Greater(t, metaInMySQL.UpdateTS, uint64(0)) + + metaInTest.ID = metaInMySQL.ID + metaInMySQL.UpdateTS = metaInTest.UpdateTS + require.True(t, reflect.DeepEqual(metaInMySQL, metaInTest)) + } +} + +func TestMetaTableRegion(t *testing.T) { + enableSplitTableRegionVal := atomic.LoadUint32(&ddl.EnableSplitTableRegion) + atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) + defer atomic.StoreUint32(&ddl.EnableSplitTableRegion, enableSplitTableRegionVal) + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + + ddlReorgTableRegionID := tk.MustQuery("show table mysql.tidb_ddl_reorg regions").Rows()[0][0] + ddlReorgTableRegionStartKey := tk.MustQuery("show table mysql.tidb_ddl_reorg regions").Rows()[0][1] + require.Equal(t, ddlReorgTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.ReorgTableID)) + + ddlJobTableRegionID := tk.MustQuery("show table mysql.tidb_ddl_job regions").Rows()[0][0] + ddlJobTableRegionStartKey := tk.MustQuery("show table mysql.tidb_ddl_job regions").Rows()[0][1] + require.Equal(t, ddlJobTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.JobTableID)) + + require.NotEqual(t, ddlJobTableRegionID, ddlReorgTableRegionID) + + ddlBackfillTableRegionID := tk.MustQuery("show table mysql.tidb_background_subtask regions").Rows()[0][0] + ddlBackfillTableRegionStartKey := tk.MustQuery("show table mysql.tidb_background_subtask regions").Rows()[0][1] + require.Equal(t, ddlBackfillTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.BackgroundSubtaskTableID)) + ddlBackfillHistoryTableRegionID := tk.MustQuery("show table mysql.tidb_background_subtask_history regions").Rows()[0][0] + ddlBackfillHistoryTableRegionStartKey := tk.MustQuery("show table mysql.tidb_background_subtask_history regions").Rows()[0][1] + require.Equal(t, ddlBackfillHistoryTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.BackgroundSubtaskHistoryTableID)) + + require.NotEqual(t, ddlBackfillTableRegionID, ddlBackfillHistoryTableRegionID) +} + +func MustReadCounter(t *testing.T, m prometheus.Counter) float64 { + pb := &dto.Metric{} + require.NoError(t, m.Write(pb)) + return pb.GetCounter().GetValue() +} + +func TestRecordTTLRows(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec("use test") + tk.MustExec("create table t(created_at datetime) TTL = created_at + INTERVAL 1 DAY") + // simple insert should be recorded + tk.MustExec("insert into t values (NOW())") + require.Equal(t, 1.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) + + // insert in a explicit transaction should be recorded + tk.MustExec("begin") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("commit") + require.Equal(t, 2.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) + + // insert multiple rows should be the same + tk.MustExec("begin") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("commit") + require.Equal(t, 4.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) + + // rollback will remove all recorded TTL rows + tk.MustExec("begin") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("rollback") + require.Equal(t, 6.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) + + // savepoint will save the recorded TTL rows + tk.MustExec("begin") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("savepoint insert1") + tk.MustExec("insert into t values (NOW())") + tk.MustExec("rollback to insert1") + tk.MustExec("commit") + require.Equal(t, 7.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) +} + +func TestInformationSchemaCreateTime(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (c int)") + tk.MustExec(`set @@time_zone = 'Asia/Shanghai'`) + ret := tk.MustQuery("select create_time from information_schema.tables where table_name='t';") + // Make sure t1 is greater than t. + time.Sleep(time.Second) + tk.MustExec("alter table t modify c int default 11") + ret1 := tk.MustQuery("select create_time from information_schema.tables where table_name='t';") + ret2 := tk.MustQuery("show table status like 't'") + require.Equal(t, ret2.Rows()[0][11].(string), ret1.Rows()[0][0].(string)) + typ1, err := types.ParseDatetime(nil, ret.Rows()[0][0].(string)) + require.NoError(t, err) + typ2, err := types.ParseDatetime(nil, ret1.Rows()[0][0].(string)) + require.NoError(t, err) + r := typ2.Compare(typ1) + require.Equal(t, 1, r) + // Check that time_zone changes makes the create_time different + tk.MustExec(`set @@time_zone = 'Europe/Amsterdam'`) + ret = tk.MustQuery(`select create_time from information_schema.tables where table_name='t'`) + ret2 = tk.MustQuery(`show table status like 't'`) + require.Equal(t, ret2.Rows()[0][11].(string), ret.Rows()[0][0].(string)) + typ3, err := types.ParseDatetime(nil, ret.Rows()[0][0].(string)) + require.NoError(t, err) + // Asia/Shanghai 2022-02-17 17:40:05 > Europe/Amsterdam 2022-02-17 10:40:05 + r = typ2.Compare(typ3) + require.Equal(t, 1, r) +} diff --git a/pkg/session/test/privileges/BUILD.bazel b/pkg/session/test/privileges/BUILD.bazel new file mode 100644 index 0000000000000..461850b395826 --- /dev/null +++ b/pkg/session/test/privileges/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "privileges_test", + timeout = "short", + srcs = [ + "main_test.go", + "privileges_test.go", + ], + flaky = True, + shard_count = 5, + deps = [ + "//pkg/config", + "//pkg/parser/auth", + "//pkg/privilege/privileges", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/privileges/main_test.go b/pkg/session/test/privileges/main_test.go new file mode 100644 index 0000000000000..95522ec4f3944 --- /dev/null +++ b/pkg/session/test/privileges/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package privileges + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/privileges/privileges_test.go b/pkg/session/test/privileges/privileges_test.go new file mode 100644 index 0000000000000..2d2160298b21a --- /dev/null +++ b/pkg/session/test/privileges/privileges_test.go @@ -0,0 +1,173 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package privileges + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/privilege/privileges" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestSkipWithGrant(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + save2 := privileges.SkipWithGrant + + privileges.SkipWithGrant = false + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "user_not_exist"}, []byte("yyy"), []byte("zzz"), nil)) + + privileges.SkipWithGrant = true + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: `%`}, []byte("yyy"), []byte("zzz"), nil)) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, []byte(""), []byte(""), nil)) + tk.MustExec("use test") + tk.MustExec("create table t (id int)") + tk.MustExec("create role r_1") + tk.MustExec("grant r_1 to root") + tk.MustExec("set role all") + tk.MustExec("show grants for root") + privileges.SkipWithGrant = save2 +} +func TestGrantViewRelated(t *testing.T) { + store := testkit.CreateMockStore(t) + + tkRoot := testkit.NewTestKit(t, store) + tkUser := testkit.NewTestKit(t, store) + tkRoot.MustExec("use test") + tkUser.MustExec("use test") + + tkRoot.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + + tkRoot.MustExec("create table if not exists t (a int)") + tkRoot.MustExec("create view v_version29 as select * from t") + tkRoot.MustExec("create user 'u_version29'@'%'") + tkRoot.MustExec("grant select on t to u_version29@'%'") + + tkUser.Session().Auth(&auth.UserIdentity{Username: "u_version29", Hostname: "localhost", CurrentUser: true, AuthUsername: "u_version29", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) + + tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) + require.Error(t, tkUser.ExecToErr("select * from test.v_version29;")) + tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) + require.Error(t, tkUser.ExecToErr("create view v_version29_c as select * from t;")) + + tkRoot.MustExec(`grant show view, select on v_version29 to 'u_version29'@'%'`) + tkRoot.MustQuery("select table_priv from mysql.tables_priv where host='%' and db='test' and user='u_version29' and table_name='v_version29'").Check(testkit.Rows("Select,Show View")) + + tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) + tkUser.MustQuery("show create view v_version29;") + require.Error(t, tkUser.ExecToErr("create view v_version29_c as select * from v_version29;")) + + tkRoot.MustExec("create view v_version29_c as select * from v_version29;") + tkRoot.MustExec(`grant create view on v_version29_c to 'u_version29'@'%'`) // Can't grant privilege on a non-exist table/view. + tkRoot.MustQuery("select table_priv from mysql.tables_priv where host='%' and db='test' and user='u_version29' and table_name='v_version29_c'").Check(testkit.Rows("Create View")) + tkRoot.MustExec("drop view v_version29_c") + + tkRoot.MustExec(`grant select on v_version29 to 'u_version29'@'%'`) + tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) + tkUser.MustExec("create view v_version29_c as select * from v_version29;") +} + +func TestUpdatePrivilege(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t1, t2;") + tk.MustExec("create table t1 (id int);") + tk.MustExec("create table t2 (id int);") + tk.MustExec("insert into t1 values (1);") + tk.MustExec("insert into t2 values (2);") + tk.MustExec("create user xxx;") + tk.MustExec("grant all on test.t1 to xxx;") + tk.MustExec("grant select on test.t2 to xxx;") + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: "localhost"}, []byte(""), []byte(""), nil)) + + tk1.MustMatchErrMsg("update t2 set id = 666 where id = 1;", "privilege check.*") + + // Cover a bug that t1 and t2 both require update privilege. + // In fact, the privlege check for t1 should be update, and for t2 should be select. + tk1.MustExec("update t1,t2 set t1.id = t2.id;") + + // Fix issue 8911 + tk.MustExec("create database weperk") + tk.MustExec("use weperk") + tk.MustExec("create table tb_wehub_server (id int, active_count int, used_count int)") + tk.MustExec("create user 'weperk'") + tk.MustExec("grant all privileges on weperk.* to 'weperk'@'%'") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "weperk", Hostname: "%"}, []byte(""), []byte(""), nil)) + tk1.MustExec("use weperk") + tk1.MustExec("update tb_wehub_server a set a.active_count=a.active_count+1,a.used_count=a.used_count+1 where id=1") + + tk.MustExec("create database service") + tk.MustExec("create database report") + tk.MustExec(`CREATE TABLE service.t1 ( + id int(11) DEFAULT NULL, + a bigint(20) NOT NULL, + b text DEFAULT NULL, + PRIMARY KEY (a) +)`) + tk.MustExec(`CREATE TABLE report.t2 ( + a bigint(20) DEFAULT NULL, + c bigint(20) NOT NULL +)`) + tk.MustExec("grant all privileges on service.* to weperk") + tk.MustExec("grant all privileges on report.* to weperk") + tk1.Session().GetSessionVars().CurrentDB = "" + tk1.MustExec(`update service.t1 s, +report.t2 t +set s.a = t.a +WHERE +s.a = t.a +and t.c >= 1 and t.c <= 10000 +and s.b !='xx';`) + + // Fix issue 10028 + tk.MustExec("create database ap") + tk.MustExec("create database tp") + tk.MustExec("grant all privileges on ap.* to xxx") + tk.MustExec("grant select on tp.* to xxx") + tk.MustExec("create table tp.record( id int,name varchar(128),age int)") + tk.MustExec("insert into tp.record (id,name,age) values (1,'john',18),(2,'lary',19),(3,'lily',18)") + tk.MustExec("create table ap.record( id int,name varchar(128),age int)") + tk.MustExec("insert into ap.record(id) values(1)") + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: "localhost"}, []byte(""), []byte(""), nil)) + tk1.MustExec("update ap.record t inner join tp.record tt on t.id=tt.id set t.name=tt.name") +} + +func TestDBUserNameLength(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table if not exists t (a int)") + // Test username length can be longer than 16. + tk.MustExec(`CREATE USER 'abcddfjakldfjaldddds'@'%' identified by ''`) + tk.MustExec(`grant all privileges on test.* to 'abcddfjakldfjaldddds'@'%'`) + tk.MustExec(`grant all privileges on test.t to 'abcddfjakldfjaldddds'@'%'`) +} + +func TestSessionAuth(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "Any not exist username with zero password!", Hostname: "anyhost"}, []byte(""), []byte(""), nil)) +} diff --git a/pkg/session/test/session_test.go b/pkg/session/test/session_test.go new file mode 100644 index 0000000000000..1290de0691a70 --- /dev/null +++ b/pkg/session/test/session_test.go @@ -0,0 +1,1048 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package test + +import ( + "context" + "fmt" + "net" + "os" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikvrpc" + "github.com/tikv/client-go/v2/tikvrpc/interceptor" +) + +func TestSchemaCheckerSQL(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk1 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1.MustExec("use test") + + // create table + tk.MustExec(`create table t (id int, c int);`) + tk.MustExec(`create table t1 (id int, c int);`) + // insert data + tk.MustExec(`insert into t values(1, 1);`) + + // The schema version is out of date in the first transaction, but the SQL can be retried. + tk.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk.MustExec(`begin;`) + tk1.MustExec(`alter table t add index idx(c);`) + tk.MustExec(`insert into t values(2, 2);`) + tk.MustExec(`commit;`) + + // The schema version is out of date in the first transaction, and the SQL can't be retried. + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 1) + defer func() { + atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 0) + }() + tk.MustExec(`begin;`) + tk1.MustExec(`alter table t modify column c bigint;`) + tk.MustExec(`insert into t values(3, 3);`) + err := tk.ExecToErr(`commit;`) + require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) + + // But the transaction related table IDs aren't in the updated table IDs. + tk.MustExec(`begin;`) + tk1.MustExec(`alter table t add index idx2(c);`) + tk.MustExec(`insert into t1 values(4, 4);`) + tk.MustExec(`commit;`) + + // Test for "select for update". + tk.MustExec(`begin;`) + tk1.MustExec(`alter table t add index idx3(c);`) + tk.MustQuery(`select * from t for update`) + require.Error(t, tk.ExecToErr(`commit;`)) + + // Repeated tests for partitioned table + tk.MustExec(`create table pt (id int, c int) partition by hash (id) partitions 3`) + tk.MustExec(`insert into pt values(1, 1);`) + // The schema version is out of date in the first transaction, and the SQL can't be retried. + tk.MustExec(`begin;`) + tk1.MustExec(`alter table pt modify column c bigint;`) + tk.MustExec(`insert into pt values(3, 3);`) + err = tk.ExecToErr(`commit;`) + require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) + + // But the transaction related table IDs aren't in the updated table IDs. + tk.MustExec(`begin;`) + tk1.MustExec(`alter table pt add index idx2(c);`) + tk.MustExec(`insert into t1 values(4, 4);`) + tk.MustExec(`commit;`) + + // Test for "select for update". + tk.MustExec(`begin;`) + tk1.MustExec(`alter table pt add index idx3(c);`) + tk.MustQuery(`select * from pt for update`) + require.Error(t, tk.ExecToErr(`commit;`)) + + // Test for "select for update". + tk.MustExec(`begin;`) + tk1.MustExec(`alter table pt add index idx4(c);`) + tk.MustQuery(`select * from pt partition (p1) for update`) + require.Error(t, tk.ExecToErr(`commit;`)) +} + +func TestLoadSchemaFailed(t *testing.T) { + originalRetryTime := domain.SchemaOutOfDateRetryTimes.Load() + originalRetryInterval := domain.SchemaOutOfDateRetryInterval.Load() + domain.SchemaOutOfDateRetryTimes.Store(3) + domain.SchemaOutOfDateRetryInterval.Store(20 * time.Millisecond) + defer func() { + domain.SchemaOutOfDateRetryTimes.Store(originalRetryTime) + domain.SchemaOutOfDateRetryInterval.Store(originalRetryInterval) + }() + + store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) + + tk := testkit.NewTestKit(t, store) + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk.MustExec("create table t (a int);") + tk.MustExec("create table t1 (a int);") + tk.MustExec("create table t2 (a int);") + + tk1.MustExec("begin") + tk2.MustExec("begin") + + // Make sure loading information schema is failed and server is invalid. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed")) + }() + require.Error(t, domain.GetDomain(tk.Session()).Reload()) + + lease := domain.GetDomain(tk.Session()).DDL().GetLease() + time.Sleep(lease * 2) + + // Make sure executing insert statement is failed when server is invalid. + require.Error(t, tk.ExecToErr("insert t values (100);")) + + tk1.MustExec("insert t1 values (100);") + tk2.MustExec("insert t2 values (100);") + + require.Error(t, tk1.ExecToErr("commit")) + + ver, err := store.CurrentVersion(kv.GlobalTxnScope) + require.NoError(t, err) + require.NotNil(t, ver) + + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/ErrorMockReloadFailed")) + time.Sleep(lease * 2) + + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (a int);") + tk.MustExec("insert t values (100);") + // Make sure insert to table t2 transaction executes. + tk2.MustExec("commit") +} + +func TestWriteOnMultipleCachedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists ct1, ct2") + tk.MustExec("create table ct1 (id int, c int)") + tk.MustExec("create table ct2 (id int, c int)") + tk.MustExec("alter table ct1 cache") + tk.MustExec("alter table ct2 cache") + tk.MustQuery("select * from ct1").Check(testkit.Rows()) + tk.MustQuery("select * from ct2").Check(testkit.Rows()) + + lastReadFromCache := func(tk *testkit.TestKit) bool { + return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache + } + + cached := false + for i := 0; i < 50; i++ { + tk.MustQuery("select * from ct1") + if lastReadFromCache(tk) { + cached = true + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, cached) + + tk.MustExec("begin") + tk.MustExec("insert into ct1 values (3, 4)") + tk.MustExec("insert into ct2 values (5, 6)") + tk.MustExec("commit") + + tk.MustQuery("select * from ct1").Check(testkit.Rows("3 4")) + tk.MustQuery("select * from ct2").Check(testkit.Rows("5 6")) + + // cleanup + tk.MustExec("alter table ct1 nocache") + tk.MustExec("alter table ct2 nocache") +} + +func TestFixSetTiDBSnapshotTS(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + safePointName := "tikv_gc_safe_point" + safePointValue := "20160102-15:04:05 -0700" + safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" + updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') + ON DUPLICATE KEY + UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) + tk.MustExec(updateSafePoint) + tk.MustExec("create database t123") + time.Sleep(time.Second) + ts := time.Now().Format("2006-1-2 15:04:05") + time.Sleep(time.Second) + tk.MustExec("drop database t123") + tk.MustMatchErrMsg("use t123", ".*Unknown database.*") + tk.MustExec(fmt.Sprintf("set @@tidb_snapshot='%s'", ts)) + tk.MustExec("use t123") + // update any session variable and assert whether infoschema is changed + tk.MustExec("SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER';") + tk.MustExec("use t123") +} + +func TestPrepareZero(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(v timestamp)") + tk.MustExec("prepare s1 from 'insert into t (v) values (?)'") + tk.MustExec("set @v1='0'") + require.Error(t, tk.ExecToErr("execute s1 using @v1")) + tk.MustExec("set @v2='" + types.ZeroDatetimeStr + "'") + tk.MustExec("set @orig_sql_mode=@@sql_mode; set @@sql_mode='';") + tk.MustExec("execute s1 using @v2") + tk.MustQuery("select v from t").Check(testkit.Rows("0000-00-00 00:00:00")) + tk.MustExec("set @@sql_mode=@orig_sql_mode;") +} + +func TestPrimaryKeyAutoIncrement(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, name varchar(255) UNIQUE NOT NULL, status int)") + tk.MustExec("insert t (name) values (?)", "abc") + id := tk.Session().LastInsertID() + require.NotZero(t, id) + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustQuery("select * from t").Check(testkit.Rows(fmt.Sprintf("%d abc ", id))) + + tk.MustExec("update t set name = 'abc', status = 1 where id = ?", id) + tk1.MustQuery("select * from t").Check(testkit.Rows(fmt.Sprintf("%d abc 1", id))) + + // Check for pass bool param to tidb prepared statement + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id tinyint)") + tk.MustExec("insert t values (?)", true) + tk.MustQuery("select * from t").Check(testkit.Rows("1")) +} + +func TestParseWithParams(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + se := tk.Session() + exec := se.(sqlexec.RestrictedSQLExecutor) + + // test compatibility with ExcuteInternal + _, err := exec.ParseWithParams(context.TODO(), "SELECT 4") + require.NoError(t, err) + + // test charset attack + stmt, err := exec.ParseWithParams(context.TODO(), "SELECT * FROM test WHERE name = %? LIMIT 1", "\xbf\x27 OR 1=1 /*") + require.NoError(t, err) + + var sb strings.Builder + ctx := format.NewRestoreCtx(format.RestoreStringDoubleQuotes, &sb) + err = stmt.Restore(ctx) + require.NoError(t, err) + require.Equal(t, "SELECT * FROM test WHERE name=_utf8mb4\"\xbf' OR 1=1 /*\" LIMIT 1", sb.String()) + + // test invalid sql + _, err = exec.ParseWithParams(context.TODO(), "SELECT") + require.Regexp(t, ".*You have an error in your SQL syntax.*", err) + + // test invalid arguments to escape + _, err = exec.ParseWithParams(context.TODO(), "SELECT %?, %?", 3) + require.Regexp(t, "missing arguments.*", err) + + // test noescape + stmt, err = exec.ParseWithParams(context.TODO(), "SELECT 3") + require.NoError(t, err) + + sb.Reset() + ctx = format.NewRestoreCtx(0, &sb) + err = stmt.Restore(ctx) + require.NoError(t, err) + require.Equal(t, "SELECT 3", sb.String()) +} + +func TestDoDDLJobQuit(t *testing.T) { + // This is required since mock tikv does not support paging. + failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging", `return`) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging")) + }() + + // test https://github.com/pingcap/tidb/issues/18714, imitate DM's use environment + // use isolated store, because in below failpoint we will cancel its context + store, err := mockstore.NewMockStore(mockstore.WithStoreType(mockstore.MockTiKV)) + require.NoError(t, err) + defer func() { require.NoError(t, store.Close()) }() + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + se, err := session.CreateSession(store) + require.NoError(t, err) + defer se.Close() + + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/storeCloseInLoop", `return`)) + defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/storeCloseInLoop")) }() + + // this DDL call will enter deadloop before this fix + err = dom.DDL().CreateSchema(se, &ast.CreateDatabaseStmt{Name: model.NewCIStr("testschema")}) + require.Equal(t, "context canceled", err.Error()) +} + +func TestProcessInfoIssue22068(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int)") + var wg util.WaitGroupWrapper + wg.Run(func() { + tk.MustQuery("select 1 from t where a = (select sleep(5));").Check(testkit.Rows()) + }) + time.Sleep(2 * time.Second) + pi := tk.Session().ShowProcess() + require.NotNil(t, pi) + require.Equal(t, "select 1 from t where a = (select sleep(5));", pi.Info) + require.Nil(t, pi.Plan) + wg.Wait() +} + +func TestPerStmtTaskID(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table task_id (v int)") + + tk.MustExec("begin") + tk.MustExec("select * from task_id where v > 10") + taskID1 := tk.Session().GetSessionVars().StmtCtx.TaskID + tk.MustExec("select * from task_id where v < 5") + taskID2 := tk.Session().GetSessionVars().StmtCtx.TaskID + tk.MustExec("commit") + + require.NotEqual(t, taskID1, taskID2) +} + +func TestStmtHints(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + // Test MEMORY_QUOTA hint + tk.MustExec("select /*+ MEMORY_QUOTA(1 MB) */ 1;") + val := int64(1) * 1024 * 1024 + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + tk.MustExec("select /*+ MEMORY_QUOTA(1 GB) */ 1;") + val = int64(1) * 1024 * 1024 * 1024 + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + tk.MustExec("select /*+ MEMORY_QUOTA(1 GB), MEMORY_QUOTA(1 MB) */ 1;") + val = int64(1) * 1024 * 1024 + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + tk.MustExec("select /*+ MEMORY_QUOTA(0 GB) */ 1;") + val = int64(0) + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "Setting the MEMORY_QUOTA to 0 means no memory limit") + + tk.MustExec("use test") + tk.MustExec("create table t1(a int);") + tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 (a) values (1);") + val = int64(1) * 1024 * 1024 + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + + tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 select /*+ MEMORY_QUOTA(3 MB) */ * from t1;") + val = int64(1) * 1024 * 1024 + require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "[util:3126]Hint MEMORY_QUOTA(`3145728`) is ignored as conflicting/duplicated.") + + // Test NO_INDEX_MERGE hint + tk.Session().GetSessionVars().SetEnableIndexMerge(true) + tk.MustExec("select /*+ NO_INDEX_MERGE() */ 1;") + require.True(t, tk.Session().GetSessionVars().StmtCtx.NoIndexMergeHint) + tk.MustExec("select /*+ NO_INDEX_MERGE(), NO_INDEX_MERGE() */ 1;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.True(t, tk.Session().GetSessionVars().GetEnableIndexMerge()) + + // Test STRAIGHT_JOIN hint + tk.MustExec("select /*+ straight_join() */ 1;") + require.True(t, tk.Session().GetSessionVars().StmtCtx.StraightJoinOrder) + tk.MustExec("select /*+ straight_join(), straight_join() */ 1;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + + // Test USE_TOJA hint + tk.Session().GetSessionVars().SetAllowInSubqToJoinAndAgg(true) + tk.MustExec("select /*+ USE_TOJA(false) */ 1;") + require.False(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) + tk.Session().GetSessionVars().SetAllowInSubqToJoinAndAgg(false) + tk.MustExec("select /*+ USE_TOJA(true) */ 1;") + require.True(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) + tk.MustExec("select /*+ USE_TOJA(false), USE_TOJA(true) */ 1;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.True(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) + + // Test USE_CASCADES hint + tk.Session().GetSessionVars().SetEnableCascadesPlanner(true) + tk.MustExec("select /*+ USE_CASCADES(false) */ 1;") + require.False(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) + tk.Session().GetSessionVars().SetEnableCascadesPlanner(false) + tk.MustExec("select /*+ USE_CASCADES(true) */ 1;") + require.True(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) + tk.MustExec("select /*+ USE_CASCADES(false), USE_CASCADES(true) */ 1;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "USE_CASCADES() is defined more than once, only the last definition takes effect: USE_CASCADES(true)") + require.True(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) + + // Test READ_CONSISTENT_REPLICA hint + tk.Session().GetSessionVars().SetReplicaRead(kv.ReplicaReadLeader) + tk.MustExec("select /*+ READ_CONSISTENT_REPLICA() */ 1;") + require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) + tk.MustExec("select /*+ READ_CONSISTENT_REPLICA(), READ_CONSISTENT_REPLICA() */ 1;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) +} + +func TestRollbackOnCompileError(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + tk.MustExec("insert t values (1)") + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustQuery("select * from t").Check(testkit.Rows("1")) + + tk.MustExec("rename table t to t2") + var meetErr bool + for i := 0; i < 100; i++ { + _, err := tk2.Exec("insert t values (1)") + if err != nil { + meetErr = true + break + } + } + require.True(t, meetErr) + + tk.MustExec("rename table t2 to t") + var recoverErr bool + for i := 0; i < 100; i++ { + _, err := tk2.Exec("insert t values (1)") + if err == nil { + recoverErr = true + break + } + } + require.True(t, recoverErr) +} + +func TestResultField(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (id int);") + + tk.MustExec(`INSERT INTO t VALUES (1);`) + tk.MustExec(`INSERT INTO t VALUES (2);`) + r, err := tk.Exec(`SELECT count(*) from t;`) + require.NoError(t, err) + defer r.Close() + fields := r.Fields() + require.NoError(t, err) + require.Len(t, fields, 1) + field := fields[0].Column + require.Equal(t, mysql.TypeLonglong, field.GetType()) + require.Equal(t, 21, field.GetFlen()) +} + +// Testcase for https://github.com/pingcap/tidb/issues/325 +func TestResultType(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + rs, err := tk.Exec(`select cast(null as char(30))`) + require.NoError(t, err) + req := rs.NewChunk(nil) + err = rs.Next(context.Background(), req) + require.NoError(t, err) + require.True(t, req.GetRow(0).IsNull(0)) + require.Equal(t, mysql.TypeVarString, rs.Fields()[0].Column.FieldType.GetType()) +} + +func TestFieldText(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + tests := []struct { + sql string + field string + }{ + {"select distinct(a) from t", "a"}, + {"select (1)", "1"}, + {"select (1+1)", "(1+1)"}, + {"select a from t", "a"}, + {"select ((a+1)) from t", "((a+1))"}, + {"select 1 /*!32301 +1 */;", "1 +1 "}, + {"select /*!32301 1 +1 */;", "1 +1 "}, + {"/*!32301 select 1 +1 */;", "1 +1 "}, + {"select 1 + /*!32301 1 +1 */;", "1 + 1 +1 "}, + {"select 1 /*!32301 + 1, 1 */;", "1 + 1"}, + {"select /*!32301 1, 1 +1 */;", "1"}, + {"select /*!32301 1 + 1, */ +1;", "1 + 1"}, + } + for _, tt := range tests { + result, err := tk.Exec(tt.sql) + require.NoError(t, err) + require.Equal(t, tt.field, result.Fields()[0].ColumnAsName.O) + result.Close() + } +} + +// TestRowLock . See http://dev.mysql.com/doc/refman/5.7/en/commit.html. +func TestRowLock(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + tk.MustExec("drop table if exists t") + txn, err := tk.Session().Txn(true) + require.True(t, kv.ErrInvalidTxn.Equal(err)) + require.False(t, txn.Valid()) + tk.MustExec("create table t (c1 int, c2 int, c3 int)") + tk.MustExec("insert t values (11, 2, 3)") + tk.MustExec("insert t values (12, 2, 3)") + tk.MustExec("insert t values (13, 2, 3)") + + tk1.MustExec("set @@tidb_disable_txn_auto_retry = 0") + tk1.MustExec("begin") + tk1.MustExec("update t set c2=21 where c1=11") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=211 where c1=11") + tk2.MustExec("commit") + + // tk1 will retry and the final value is 21 + tk1.MustExec("commit") + + // Check the result is correct + tk.MustQuery("select c2 from t where c1=11").Check(testkit.Rows("21")) + + tk1.MustExec("begin") + tk1.MustExec("update t set c2=21 where c1=11") + + tk2.MustExec("begin") + tk2.MustExec("update t set c2=22 where c1=12") + tk2.MustExec("commit") + + tk1.MustExec("commit") +} + +func TestMatchIdentity(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE USER `useridentity`@`%`") + tk.MustExec("CREATE USER `useridentity`@`localhost`") + tk.MustExec("CREATE USER `useridentity`@`192.168.1.1`") + tk.MustExec("CREATE USER `useridentity`@`example.com`") + + // The MySQL matching rule is most specific to least specific. + // So if I log in from 192.168.1.1 I should match that entry always. + identity, err := tk.Session().MatchIdentity("useridentity", "192.168.1.1") + require.NoError(t, err) + require.Equal(t, "useridentity", identity.Username) + require.Equal(t, "192.168.1.1", identity.Hostname) + + // If I log in from localhost, I should match localhost + identity, err = tk.Session().MatchIdentity("useridentity", "localhost") + require.NoError(t, err) + require.Equal(t, "useridentity", identity.Username) + require.Equal(t, "localhost", identity.Hostname) + + // If I log in from 192.168.1.2 I should match wildcard. + identity, err = tk.Session().MatchIdentity("useridentity", "192.168.1.2") + require.NoError(t, err) + require.Equal(t, "useridentity", identity.Username) + require.Equal(t, "%", identity.Hostname) + + identity, err = tk.Session().MatchIdentity("useridentity", "127.0.0.1") + require.NoError(t, err) + require.Equal(t, "useridentity", identity.Username) + require.Equal(t, "localhost", identity.Hostname) + + // This uses the lookup of example.com to get an IP address. + // We then login with that IP address, but expect it to match the example.com + // entry in the privileges table (by reverse lookup). + ips, err := net.LookupHost("example.com") + require.NoError(t, err) + identity, err = tk.Session().MatchIdentity("useridentity", ips[0]) + require.NoError(t, err) + require.Equal(t, "useridentity", identity.Username) + // FIXME: we *should* match example.com instead + // as long as skip-name-resolve is not set (DEFAULT) + require.Equal(t, "%", identity.Hostname) +} + +func TestBinaryReadOnly(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (i int key)") + id, _, _, err := tk.Session().PrepareStmt("select i from t where i = ?") + require.NoError(t, err) + id2, _, _, err := tk.Session().PrepareStmt("insert into t values (?)") + require.NoError(t, err) + tk.MustExec("set autocommit = 0") + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + _, err = tk.Session().ExecutePreparedStmt(context.Background(), id, expression.Args2Expressions4Test(1)) + require.NoError(t, err) + require.Equal(t, 0, session.GetHistory(tk.Session()).Count()) + tk.MustExec("insert into t values (1)") + require.Equal(t, 1, session.GetHistory(tk.Session()).Count()) + _, err = tk.Session().ExecutePreparedStmt(context.Background(), id2, expression.Args2Expressions4Test(2)) + require.NoError(t, err) + require.Equal(t, 2, session.GetHistory(tk.Session()).Count()) + tk.MustExec("commit") +} + +func TestHandleAssertionFailureForPartitionedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + se := tk.Session() + se.SetConnectionID(1) + tk.MustExec("use test") + tk.MustExec("create table t (a int, b int, c int, primary key(a, b)) partition by range (a) (partition p0 values less than (10), partition p1 values less than (20))") + failpoint.Enable("github.com/pingcap/tidb/pkg/table/tables/addRecordForceAssertExist", "return") + defer failpoint.Disable("github.com/pingcap/tidb/pkg/table/tables/addRecordForceAssertExist") + + ctx, hook := testutil.WithLogHook(context.TODO(), t, "table") + _, err := tk.ExecWithContext(ctx, "insert into t values (1, 1, 1)") + require.ErrorContains(t, err, "assertion") + hook.CheckLogCount(t, 0) +} + +func TestRandomBinary(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + allBytes := [][]byte{ + {4, 0, 0, 0, 0, 0, 0, 4, '2'}, + {4, 0, 0, 0, 0, 0, 0, 4, '.'}, + {4, 0, 0, 0, 0, 0, 0, 4, '*'}, + {4, 0, 0, 0, 0, 0, 0, 4, '('}, + {4, 0, 0, 0, 0, 0, 0, 4, '\''}, + {4, 0, 0, 0, 0, 0, 0, 4, '!'}, + {4, 0, 0, 0, 0, 0, 0, 4, 29}, + {4, 0, 0, 0, 0, 0, 0, 4, 28}, + {4, 0, 0, 0, 0, 0, 0, 4, 23}, + {4, 0, 0, 0, 0, 0, 0, 4, 16}, + } + sql := "insert into mysql.stats_top_n (table_id, is_index, hist_id, value, count) values " + var val string + for i, bytes := range allBytes { + if i == 0 { + val += sqlexec.MustEscapeSQL("(874, 0, 1, %?, 3)", bytes) + } else { + val += sqlexec.MustEscapeSQL(",(874, 0, 1, %?, 3)", bytes) + } + } + sql += val + tk.MustExec("set sql_mode = 'NO_BACKSLASH_ESCAPES';") + _, err := tk.Session().ExecuteInternal(ctx, sql) + require.NoError(t, err) +} + +func TestSQLModeOp(t *testing.T) { + s := mysql.ModeNoBackslashEscapes | mysql.ModeOnlyFullGroupBy + d := mysql.DelSQLMode(s, mysql.ModeANSIQuotes) + require.Equal(t, s, d) + + d = mysql.DelSQLMode(s, mysql.ModeNoBackslashEscapes) + require.Equal(t, mysql.ModeOnlyFullGroupBy, d) + + s = mysql.ModeNoBackslashEscapes | mysql.ModeOnlyFullGroupBy + a := mysql.SetSQLMode(s, mysql.ModeOnlyFullGroupBy) + require.Equal(t, s, a) + + a = mysql.SetSQLMode(s, mysql.ModeAllowInvalidDates) + require.Equal(t, mysql.ModeNoBackslashEscapes|mysql.ModeOnlyFullGroupBy|mysql.ModeAllowInvalidDates, a) +} + +func TestRequestSource(t *testing.T) { + store := testkit.CreateMockStore(t, mockstore.WithStoreType(mockstore.MockTiKV)) + tk := testkit.NewTestKit(t, store) + withCheckInterceptor := func(source string) interceptor.RPCInterceptor { + return interceptor.NewRPCInterceptor("kv-request-source-verify", func(next interceptor.RPCInterceptorFunc) interceptor.RPCInterceptorFunc { + return func(target string, req *tikvrpc.Request) (*tikvrpc.Response, error) { + requestSource := "" + readType := "" + switch r := req.Req.(type) { + case *kvrpcpb.PrewriteRequest: + requestSource = r.GetContext().GetRequestSource() + case *kvrpcpb.CommitRequest: + requestSource = r.GetContext().GetRequestSource() + case *coprocessor.Request: + readType = "leader_" // read request will be attached with read type + requestSource = r.GetContext().GetRequestSource() + case *kvrpcpb.GetRequest: + readType = "leader_" // read request will be attached with read type + requestSource = r.GetContext().GetRequestSource() + case *kvrpcpb.BatchGetRequest: + readType = "leader_" // read request will be attached with read type + requestSource = r.GetContext().GetRequestSource() + } + require.Equal(t, readType+source, requestSource) + return next(target, req) + } + }) + } + ctx := context.Background() + tk.MustExecWithContext(ctx, "use test") + tk.MustExecWithContext(ctx, "create table t(a int primary key, b int)") + tk.MustExecWithContext(ctx, "set @@tidb_request_source_type = 'lightning'") + tk.MustQueryWithContext(ctx, "select @@tidb_request_source_type").Check(testkit.Rows("lightning")) + insertCtx := interceptor.WithRPCInterceptor(context.Background(), withCheckInterceptor("external_Insert_lightning")) + tk.MustExecWithContext(insertCtx, "insert into t values(1, 1)") + selectCtx := interceptor.WithRPCInterceptor(context.Background(), withCheckInterceptor("external_Select_lightning")) + tk.MustExecWithContext(selectCtx, "select count(*) from t;") + tk.MustQueryWithContext(selectCtx, "select b from t where a = 1;") + tk.MustQueryWithContext(selectCtx, "select b from t where a in (1, 2, 3);") +} + +func TestEmptyInitSQLFile(t *testing.T) { + // A non-existent sql file would stop the bootstrap of the tidb cluster + store, err := mockstore.NewMockStore() + require.NoError(t, err) + config.GetGlobalConfig().InitializeSQLFile = "non-existent.sql" + defer func() { + require.NoError(t, store.Close()) + config.GetGlobalConfig().InitializeSQLFile = "" + }() + + dom, err := session.BootstrapSession(store) + require.Nil(t, dom) + require.Error(t, err) +} + +func TestInitSystemVariable(t *testing.T) { + // We create an initialize-sql-file and then bootstrap the server with it. + // The observed behavior should be that tidb_enable_noop_variables is now + // disabled, and the feature works as expected. + initializeSQLFile, err := os.CreateTemp("", "init.sql") + require.NoError(t, err) + defer func() { + path := initializeSQLFile.Name() + err = initializeSQLFile.Close() + require.NoError(t, err) + err = os.Remove(path) + require.NoError(t, err) + }() + // Implicitly test multi-line init files + _, err = initializeSQLFile.WriteString( + "CREATE DATABASE initsqlfiletest;\n" + + "SET GLOBAL tidb_enable_noop_variables = OFF;\n") + require.NoError(t, err) + + // Create a mock store + // Set the config parameter for initialize sql file + store, err := mockstore.NewMockStore() + require.NoError(t, err) + config.GetGlobalConfig().InitializeSQLFile = initializeSQLFile.Name() + defer func() { + require.NoError(t, store.Close()) + config.GetGlobalConfig().InitializeSQLFile = "" + }() + + // Bootstrap with the InitializeSQLFile config option + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + se := session.CreateSessionAndSetID(t, store) + ctx := context.Background() + r := session.MustExecToRecodeSet(t, se, `SHOW VARIABLES LIKE 'query_cache_type'`) + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) // not shown in noopvariables mode + require.NoError(t, r.Close()) + + r = session.MustExecToRecodeSet(t, se, `SHOW VARIABLES LIKE 'tidb_enable_noop_variables'`) + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, []byte("OFF"), row.GetBytes(1)) + require.NoError(t, r.Close()) +} + +func TestInitUsers(t *testing.T) { + // Two sql files are set to 'initialize-sql-file' one after another, + // and only the first one is executed. + var err error + sqlFiles := make([]*os.File, 2) + for i, name := range []string{"1.sql", "2.sql"} { + sqlFiles[i], err = os.CreateTemp("", name) + require.NoError(t, err) + } + defer func() { + for _, sqlFile := range sqlFiles { + path := sqlFile.Name() + err = sqlFile.Close() + require.NoError(t, err) + err = os.Remove(path) + require.NoError(t, err) + } + }() + _, err = sqlFiles[0].WriteString(` +CREATE USER cloud_admin; +GRANT BACKUP_ADMIN, RESTORE_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT DASHBOARD_CLIENT on *.* TO 'cloud_admin'@'%'; +GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_USER_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT RESTRICTED_REPLICA_WRITER_ADMIN ON *.* TO 'cloud_admin'@'%'; +GRANT CREATE USER ON *.* TO 'cloud_admin'@'%'; +GRANT RELOAD ON *.* TO 'cloud_admin'@'%'; +GRANT PROCESS ON *.* TO 'cloud_admin'@'%'; +GRANT SELECT, INSERT, UPDATE, DELETE ON mysql.* TO 'cloud_admin'@'%'; +GRANT SELECT ON information_schema.* TO 'cloud_admin'@'%'; +GRANT SELECT ON performance_schema.* TO 'cloud_admin'@'%'; +GRANT SHOW DATABASES on *.* TO 'cloud_admin'@'%'; +GRANT REFERENCES ON *.* TO 'cloud_admin'@'%'; +GRANT SELECT ON *.* TO 'cloud_admin'@'%'; +GRANT INDEX ON *.* TO 'cloud_admin'@'%'; +GRANT INSERT ON *.* TO 'cloud_admin'@'%'; +GRANT UPDATE ON *.* TO 'cloud_admin'@'%'; +GRANT DELETE ON *.* TO 'cloud_admin'@'%'; +GRANT CREATE ON *.* TO 'cloud_admin'@'%'; +GRANT DROP ON *.* TO 'cloud_admin'@'%'; +GRANT ALTER ON *.* TO 'cloud_admin'@'%'; +GRANT CREATE VIEW ON *.* TO 'cloud_admin'@'%'; +GRANT SHUTDOWN, CONFIG ON *.* TO 'cloud_admin'@'%'; +REVOKE SHUTDOWN, CONFIG ON *.* FROM root; + +DROP USER root; +`) + require.NoError(t, err) + _, err = sqlFiles[1].WriteString("drop user cloud_admin;") + require.NoError(t, err) + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + config.GetGlobalConfig().InitializeSQLFile = sqlFiles[0].Name() + defer func() { + require.NoError(t, store.Close()) + config.GetGlobalConfig().InitializeSQLFile = "" + }() + + // Bootstrap with the first sql file + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + se := session.CreateSessionAndSetID(t, store) + ctx := context.Background() + // 'cloud_admin' has been created successfully + r := session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'cloud_admin'`) + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, "cloud_admin", row.GetString(0)) + require.NoError(t, r.Close()) + // 'root' has been deleted successfully + r = session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'root'`) + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) + require.NoError(t, r.Close()) + dom.Close() + + session.DisableRunBootstrapSQLFileInTest() + + // Bootstrap with the second sql file, which would not been executed. + config.GetGlobalConfig().InitializeSQLFile = sqlFiles[1].Name() + dom, err = session.BootstrapSession(store) + require.NoError(t, err) + se = session.CreateSessionAndSetID(t, store) + r = session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'cloud_admin'`) + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + row = req.GetRow(0) + require.Equal(t, "cloud_admin", row.GetString(0)) + require.NoError(t, r.Close()) + dom.Close() +} + +func TestErrorHappenWhileInit(t *testing.T) { + // 1. parser error in sql file (1.sql) makes the bootstrap panic + // 2. other errors in sql file (2.sql) will be ignored + var err error + sqlFiles := make([]*os.File, 2) + for i, name := range []string{"1.sql", "2.sql"} { + sqlFiles[i], err = os.CreateTemp("", name) + require.NoError(t, err) + } + defer func() { + for _, sqlFile := range sqlFiles { + path := sqlFile.Name() + err = sqlFile.Close() + require.NoError(t, err) + err = os.Remove(path) + require.NoError(t, err) + } + }() + _, err = sqlFiles[0].WriteString("create table test.t (c in);") + require.NoError(t, err) + _, err = sqlFiles[1].WriteString(` +create table test.t (c int); +insert into test.t values ("abc"); -- invalid statement +`) + require.NoError(t, err) + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + config.GetGlobalConfig().InitializeSQLFile = sqlFiles[0].Name() + defer func() { + config.GetGlobalConfig().InitializeSQLFile = "" + }() + + // Bootstrap with the first sql file + dom, err := session.BootstrapSession(store) + require.Nil(t, dom) + require.Error(t, err) + require.NoError(t, store.Close()) + + session.DisableRunBootstrapSQLFileInTest() + + // Bootstrap with the second sql file, which would not been executed. + store, err = mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + config.GetGlobalConfig().InitializeSQLFile = sqlFiles[1].Name() + dom, err = session.BootstrapSession(store) + require.NoError(t, err) + se := session.CreateSessionAndSetID(t, store) + ctx := context.Background() + _ = session.MustExecToRecodeSet(t, se, `use test;`) + require.NoError(t, err) + // Table t has been created. + r := session.MustExecToRecodeSet(t, se, `show tables;`) + require.NoError(t, err) + req := r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 1, req.NumRows()) + row := req.GetRow(0) + require.Equal(t, "t", row.GetString(0)) + require.NoError(t, r.Close()) + // But data is failed to inserted since the error + r = session.MustExecToRecodeSet(t, se, `select * from test.t`) + require.NoError(t, err) + req = r.NewChunk(nil) + err = r.Next(ctx, req) + require.NoError(t, err) + require.Equal(t, 0, req.NumRows()) + require.NoError(t, r.Close()) + dom.Close() +} diff --git a/pkg/session/test/txn/BUILD.bazel b/pkg/session/test/txn/BUILD.bazel new file mode 100644 index 0000000000000..d3626ad0ca883 --- /dev/null +++ b/pkg/session/test/txn/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "txn_test", + timeout = "short", + srcs = [ + "main_test.go", + "txn_test.go", + ], + flaky = True, + race = "on", + shard_count = 11, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/txn/main_test.go b/pkg/session/test/txn/main_test.go new file mode 100644 index 0000000000000..ca5dc13ce6545 --- /dev/null +++ b/pkg/session/test/txn/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package txn + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/txn/txn_test.go b/pkg/session/test/txn/txn_test.go new file mode 100644 index 0000000000000..a0af220f1077a --- /dev/null +++ b/pkg/session/test/txn/txn_test.go @@ -0,0 +1,621 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package txn + +import ( + "context" + "fmt" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +// TestAutocommit . See https://dev.mysql.com/doc/internals/en/status-flags.html +func TestAutocommit(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("drop table if exists t;") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("insert t values ()") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("begin") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("insert t values ()") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("drop table if exists t") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + tk.MustExec("set autocommit=0") + require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) + tk.MustExec("insert t values ()") + require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) + tk.MustExec("commit") + require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) + tk.MustExec("drop table if exists t") + require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) + tk.MustExec("set autocommit='On'") + require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) + + // When autocommit is 0, transaction start ts should be the first *valid* + // statement, rather than *any* statement. + tk.MustExec("create table t (id int key)") + tk.MustExec("set @@autocommit = 0") + tk.MustExec("rollback") + tk.MustExec("set @@autocommit = 0") + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("insert into t select 1") + //nolint:all_revive,revive + tk.MustQuery("select * from t").Check(testkit.Rows("1")) + tk.MustExec("delete from t") + + // When the transaction is rolled back, the global set statement would succeed. + tk.MustExec("set @@global.autocommit = 0") + tk.MustExec("begin") + tk.MustExec("insert into t values (1)") + tk.MustExec("set @@global.autocommit = 1") + tk.MustExec("rollback") + tk.MustQuery("select count(*) from t where id = 1").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.autocommit").Check(testkit.Rows("1")) + + // When the transaction is committed because of switching mode, the session set statement shold succeed. + tk.MustExec("set autocommit = 0") + tk.MustExec("begin") + tk.MustExec("insert into t values (1)") + tk.MustExec("set autocommit = 1") + tk.MustExec("rollback") + tk.MustQuery("select count(*) from t where id = 1").Check(testkit.Rows("1")) + tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) + + tk.MustExec("set autocommit = 0") + tk.MustExec("insert into t values (2)") + tk.MustExec("set autocommit = 1") + tk.MustExec("rollback") + tk.MustQuery("select count(*) from t where id = 2").Check(testkit.Rows("1")) + tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) + + // Set should not take effect if the mode is not changed. + tk.MustExec("set autocommit = 0") + tk.MustExec("begin") + tk.MustExec("insert into t values (3)") + tk.MustExec("set autocommit = 0") + tk.MustExec("rollback") + tk.MustQuery("select count(*) from t where id = 3").Check(testkit.Rows("0")) + tk.MustQuery("select @@autocommit").Check(testkit.Rows("0")) + + tk.MustExec("set autocommit = 1") + tk.MustExec("begin") + tk.MustExec("insert into t values (4)") + tk.MustExec("set autocommit = 1") + tk.MustExec("rollback") + tk.MustQuery("select count(*) from t where id = 4").Check(testkit.Rows("0")) + tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) +} + +// TestTxnLazyInitialize tests that when autocommit = 0, not all statement starts +// a new transaction. +func TestTxnLazyInitialize(t *testing.T) { + testTxnLazyInitialize(t, false) + testTxnLazyInitialize(t, true) +} + +func testTxnLazyInitialize(t *testing.T, isPessimistic bool) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id int)") + if isPessimistic { + tk.MustExec("set tidb_txn_mode = 'pessimistic'") + } + + tk.MustExec("set @@autocommit = 0") + _, err := tk.Session().Txn(true) + require.True(t, kv.ErrInvalidTxn.Equal(err)) + txn, err := tk.Session().Txn(false) + require.NoError(t, err) + require.False(t, txn.Valid()) + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + + // Those statements should not start a new transaction automatically. + tk.MustQuery("select 1") + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + + tk.MustExec("set @@tidb_general_log = 0") + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + + tk.MustQuery("explain select * from t") + tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) + + // Begin statement should start a new transaction. + tk.MustExec("begin") + txn, err = tk.Session().Txn(false) + require.NoError(t, err) + require.True(t, txn.Valid()) + tk.MustExec("rollback") + + tk.MustExec("select * from t") + txn, err = tk.Session().Txn(false) + require.NoError(t, err) + require.True(t, txn.Valid()) + tk.MustExec("rollback") + + tk.MustExec("insert into t values (1)") + txn, err = tk.Session().Txn(false) + require.NoError(t, err) + require.True(t, txn.Valid()) + tk.MustExec("rollback") +} + +func TestDisableTxnAutoRetry(t *testing.T) { + store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + + tk1.MustExec("use test") + tk2.MustExec("use test") + + tk1.MustExec("create table no_retry (id int)") + tk1.MustExec("insert into no_retry values (1)") + tk1.MustExec("set @@tidb_disable_txn_auto_retry = 1") + + tk1.MustExec("begin") + tk1.MustExec("update no_retry set id = 2") + + tk2.MustExec("begin") + tk2.MustExec("update no_retry set id = 3") + tk2.MustExec("commit") + + // No auto retry because tidb_disable_txn_auto_retry is set to 1. + _, err := tk1.Session().Execute(context.Background(), "commit") + require.Error(t, err) + + // session 1 starts a transaction early. + // execute a select statement to clear retry history. + tk1.MustExec("select 1") + err = tk1.Session().PrepareTxnCtx(context.Background()) + require.NoError(t, err) + // session 2 update the value. + tk2.MustExec("update no_retry set id = 4") + // AutoCommit update will retry, so it would not fail. + tk1.MustExec("update no_retry set id = 5") + + // RestrictedSQL should retry. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + tk1.Session().ExecuteInternal(ctx, "begin") + + tk2.MustExec("update no_retry set id = 6") + + tk1.Session().ExecuteInternal(ctx, "update no_retry set id = 7") + tk1.Session().ExecuteInternal(ctx, "commit") + + // test for disable transaction local latch + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.TxnLocalLatches.Enabled = false + }) + tk1.MustExec("begin") + tk1.MustExec("update no_retry set id = 9") + + tk2.MustExec("update no_retry set id = 8") + + _, err = tk1.Session().Execute(context.Background(), "commit") + require.Error(t, err) + require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) + require.Contains(t, err.Error(), kv.TxnRetryableMark) + tk1.MustExec("rollback") + + config.UpdateGlobal(func(conf *config.Config) { + conf.TxnLocalLatches.Enabled = true + }) + tk1.MustExec("begin") + tk2.MustExec("alter table no_retry add index idx(id)") + tk2.MustQuery("select * from no_retry").Check(testkit.Rows("8")) + tk1.MustExec("update no_retry set id = 10") + _, err = tk1.Session().Execute(context.Background(), "commit") + require.Error(t, err) + + // set autocommit to begin and commit + tk1.MustExec("set autocommit = 0") + tk1.MustQuery("select * from no_retry").Check(testkit.Rows("8")) + tk2.MustExec("update no_retry set id = 11") + tk1.MustExec("update no_retry set id = 12") + _, err = tk1.Session().Execute(context.Background(), "set autocommit = 1") + require.Error(t, err) + require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) + require.Contains(t, err.Error(), kv.TxnRetryableMark) + tk1.MustExec("rollback") + tk2.MustQuery("select * from no_retry").Check(testkit.Rows("11")) + + tk1.MustExec("set autocommit = 0") + tk1.MustQuery("select * from no_retry").Check(testkit.Rows("11")) + tk2.MustExec("update no_retry set id = 13") + tk1.MustExec("update no_retry set id = 14") + _, err = tk1.Session().Execute(context.Background(), "commit") + require.Error(t, err) + require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) + require.Contains(t, err.Error(), kv.TxnRetryableMark) + tk1.MustExec("rollback") + tk2.MustQuery("select * from no_retry").Check(testkit.Rows("13")) +} + +// The Read-only flags are checked in the planning stage of queries, +// but this test checks we check them again at commit time. +// The main use case for this is a long-running auto-commit statement. +func TestAutoCommitRespectsReadOnly(t *testing.T) { + store := testkit.CreateMockStore(t) + var wg sync.WaitGroup + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + + tk1.MustExec("create table test.auto_commit_test (a int)") + wg.Add(1) + go func() { + err := tk1.ExecToErr("INSERT INTO test.auto_commit_test VALUES (SLEEP(1))") + require.True(t, terror.ErrorEqual(err, plannercore.ErrSQLInReadOnlyMode), fmt.Sprintf("err %v", err)) + wg.Done() + }() + tk2.MustExec("SET GLOBAL tidb_restricted_read_only = 1") + err := tk2.ExecToErr("INSERT INTO test.auto_commit_test VALUES (0)") // should also be an error + require.True(t, terror.ErrorEqual(err, plannercore.ErrSQLInReadOnlyMode), fmt.Sprintf("err %v", err)) + // Reset and check with the privilege to ignore the readonly flag and continue to insert. + wg.Wait() + tk1.MustExec("SET GLOBAL tidb_restricted_read_only = 0") + tk1.MustExec("SET GLOBAL tidb_super_read_only = 0") + tk1.MustExec("GRANT RESTRICTED_REPLICA_WRITER_ADMIN on *.* to 'root'") + + wg.Add(1) + go func() { + tk1.MustExec("INSERT INTO test.auto_commit_test VALUES (SLEEP(1))") + wg.Done() + }() + tk2.MustExec("SET GLOBAL tidb_restricted_read_only = 1") + tk2.MustExec("INSERT INTO test.auto_commit_test VALUES (0)") + + // wait for go routines + wg.Wait() + tk1.MustExec("SET GLOBAL tidb_restricted_read_only = 0") + tk1.MustExec("SET GLOBAL tidb_super_read_only = 0") +} + +func TestRetryForCurrentTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table history (a int)") + tk.MustExec("insert history values (1)") + + // Firstly, enable retry. + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + tk.MustExec("begin") + tk.MustExec("update history set a = 2") + // Disable retry now. + tk.MustExec("set tidb_disable_txn_auto_retry = 1") + + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("update history set a = 3") + + tk.MustExec("commit") + tk.MustQuery("select * from history").Check(testkit.Rows("2")) +} + +func TestBatchCommit(t *testing.T) { + store := testkit.CreateMockStore(t) + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set tidb_batch_commit = 1") + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + tk.MustExec("create table t (id int)") + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.StmtCountLimit = 3 + }) + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk.MustExec("SET SESSION autocommit = 1") + tk.MustExec("begin") + tk.MustExec("insert into t values (1)") + tk1.MustQuery("select * from t").Check(testkit.Rows()) + tk.MustExec("insert into t values (2)") + tk1.MustQuery("select * from t").Check(testkit.Rows()) + tk.MustExec("rollback") + tk1.MustQuery("select * from t").Check(testkit.Rows()) + + // The above rollback will not make the session in transaction. + tk.MustExec("insert into t values (1)") + tk1.MustQuery("select * from t").Check(testkit.Rows("1")) + tk.MustExec("delete from t") + + tk.MustExec("begin") + tk.MustExec("insert into t values (5)") + tk1.MustQuery("select * from t").Check(testkit.Rows()) + tk.MustExec("insert into t values (6)") + tk1.MustQuery("select * from t").Check(testkit.Rows()) + tk.MustExec("insert into t values (7)") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) + + // The session is still in transaction. + tk.MustExec("insert into t values (8)") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) + tk.MustExec("insert into t values (9)") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) + tk.MustExec("insert into t values (10)") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) + tk.MustExec("commit") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7", "8", "9", "10")) + + // The above commit will not make the session in transaction. + tk.MustExec("insert into t values (11)") + tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7", "8", "9", "10", "11")) + + tk.MustExec("delete from t") + tk.MustExec("SET SESSION autocommit = 0") + tk.MustExec("insert into t values (1)") + tk.MustExec("insert into t values (2)") + tk.MustExec("insert into t values (3)") + tk.MustExec("rollback") + tk1.MustExec("insert into t values (4)") + tk1.MustExec("insert into t values (5)") + tk.MustQuery("select * from t").Check(testkit.Rows("4", "5")) +} + +func TestTxnRetryErrMsg(t *testing.T) { + store := testkit.CreateMockStore(t) + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("create table no_retry (id int)") + tk1.MustExec("insert into no_retry values (1)") + tk1.MustExec("begin") + tk2.MustExec("use test") + tk2.MustExec("update no_retry set id = id + 1") + tk1.MustExec("update no_retry set id = id + 1") + require.NoError(t, failpoint.Enable("tikvclient/mockRetryableErrorResp", `return(true)`)) + _, err := tk1.Session().Execute(context.Background(), "commit") + require.NoError(t, failpoint.Disable("tikvclient/mockRetryableErrorResp")) + require.Error(t, err) + require.True(t, kv.ErrTxnRetryable.Equal(err), "error: %s", err) + require.True(t, strings.Contains(err.Error(), "mock retryable error"), "error: %s", err) + require.True(t, strings.Contains(err.Error(), kv.TxnRetryableMark), "error: %s", err) +} + +func TestSetTxnScope(t *testing.T) { + // Check the default value of @@tidb_enable_local_txn and @@txn_scope whitout configuring the zone label. + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Check the default value of @@tidb_enable_local_txn and @@txn_scope with configuring the zone label. + require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) + + // @@tidb_enable_local_txn is off without configuring the zone label. + tk = testkit.NewTestKit(t, store) + tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to local. + err := tk.ExecToErr("set @@txn_scope = 'local';") + require.Error(t, err) + require.Regexp(t, `.*txn_scope can not be set to local when tidb_enable_local_txn is off.*`, err) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to global. + tk.MustExec("set @@txn_scope = 'global';") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + + // @@tidb_enable_local_txn is off with configuring the zone label. + require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to local. + err = tk.ExecToErr("set @@txn_scope = 'local';") + require.Error(t, err) + require.Regexp(t, `.*txn_scope can not be set to local when tidb_enable_local_txn is off.*`, err) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to global. + tk.MustExec("set @@txn_scope = 'global';") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) + + // @@tidb_enable_local_txn is on without configuring the zone label. + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_enable_local_txn = on;") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to local. + err = tk.ExecToErr("set @@txn_scope = 'local';") + require.Error(t, err) + require.Regexp(t, `.*txn_scope can not be set to local when zone label is empty or "global".*`, err) + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to global. + tk.MustExec("set @@txn_scope = 'global';") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + + // @@tidb_enable_local_txn is on with configuring the zone label. + require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) + tk = testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_enable_local_txn = on;") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.LocalTxnScope)) + require.Equal(t, "bj", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to global. + tk.MustExec("set @@txn_scope = 'global';") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) + require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Set @@txn_scope to local. + tk.MustExec("set @@txn_scope = 'local';") + tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.LocalTxnScope)) + require.Equal(t, "bj", tk.Session().GetSessionVars().CheckAndGetTxnScope()) + // Try to set @@txn_scope to an invalid value. + err = tk.ExecToErr("set @@txn_scope='foo'") + require.Error(t, err) + require.Regexp(t, `.*txn_scope value should be global or local.*`, err) + require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) +} + +func TestErrorRollback(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_rollback") + tk.MustExec("create table t_rollback (c1 int, c2 int, primary key(c1))") + tk.MustExec("insert into t_rollback values (0, 0)") + + var wg sync.WaitGroup + cnt := 4 + wg.Add(cnt) + num := 20 + + for i := 0; i < cnt; i++ { + go func() { + defer wg.Done() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_retry_limit = 100") + for j := 0; j < num; j++ { + _, _ = tk.Exec("insert into t_rollback values (1, 1)") + tk.MustExec("update t_rollback set c2 = c2 + 1 where c1 = 0") + } + }() + } + + wg.Wait() + tk.MustQuery("select c2 from t_rollback where c1 = 0").Check(testkit.Rows(fmt.Sprint(cnt * num))) +} + +// TestInTrans . See https://dev.mysql.com/doc/internals/en/status-flags.html +func TestInTrans(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") + tk.MustExec("insert t values ()") + tk.MustExec("begin") + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.True(t, txn.Valid()) + tk.MustExec("insert t values ()") + require.True(t, txn.Valid()) + tk.MustExec("drop table if exists t;") + require.False(t, txn.Valid()) + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") + require.False(t, txn.Valid()) + tk.MustExec("insert t values ()") + require.False(t, txn.Valid()) + tk.MustExec("commit") + tk.MustExec("insert t values ()") + + tk.MustExec("set autocommit=0") + tk.MustExec("begin") + require.True(t, txn.Valid()) + tk.MustExec("insert t values ()") + require.True(t, txn.Valid()) + tk.MustExec("commit") + require.False(t, txn.Valid()) + tk.MustExec("insert t values ()") + require.True(t, txn.Valid()) + tk.MustExec("commit") + require.False(t, txn.Valid()) + + tk.MustExec("set autocommit=1") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") + tk.MustExec("begin") + require.True(t, txn.Valid()) + tk.MustExec("insert t values ()") + require.True(t, txn.Valid()) + tk.MustExec("rollback") + require.False(t, txn.Valid()) +} + +func TestCommitRetryCount(t *testing.T) { + store := testkit.CreateMockStore(t) + + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + tk1.MustExec("create table no_retry (id int)") + tk1.MustExec("insert into no_retry values (1)") + tk1.MustExec("set @@tidb_retry_limit = 0") + + tk1.MustExec("begin") + tk1.MustExec("update no_retry set id = 2") + + tk2.MustExec("begin") + tk2.MustExec("update no_retry set id = 3") + tk2.MustExec("commit") + + // No auto retry because retry limit is set to 0. + require.Error(t, tk1.ExecToErr("commit")) +} diff --git a/pkg/session/test/variable/BUILD.bazel b/pkg/session/test/variable/BUILD.bazel new file mode 100644 index 0000000000000..5ac4b9fb2415f --- /dev/null +++ b/pkg/session/test/variable/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "variable_test", + timeout = "short", + srcs = [ + "main_test.go", + "variable_test.go", + ], + flaky = True, + shard_count = 10, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/variable/main_test.go b/pkg/session/test/variable/main_test.go new file mode 100644 index 0000000000000..2856f3b8a0fb5 --- /dev/null +++ b/pkg/session/test/variable/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/session/test/variable/variable_test.go b/pkg/session/test/variable/variable_test.go new file mode 100644 index 0000000000000..5e5adbf023d4d --- /dev/null +++ b/pkg/session/test/variable/variable_test.go @@ -0,0 +1,388 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/require" +) + +func TestForbidSettingBothTSVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + // For mock tikv, safe point is not initialized, we manually insert it for snapshot to use. + safePointName := "tikv_gc_safe_point" + safePointValue := "20060102-15:04:05 -0700" + safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" + updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') + ON DUPLICATE KEY + UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) + tk.MustExec(updateSafePoint) + + // Set tidb_snapshot and assert tidb_read_staleness + tk.MustExec("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'") + tk.MustGetErrMsg("set @@tidb_read_staleness='-5'", "tidb_snapshot should be clear before setting tidb_read_staleness") + tk.MustExec("set @@tidb_snapshot = ''") + tk.MustExec("set @@tidb_read_staleness='-5'") + + // Set tidb_read_staleness and assert tidb_snapshot + tk.MustExec("set @@tidb_read_staleness='-5'") + tk.MustGetErrMsg("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'", "tidb_read_staleness should be clear before setting tidb_snapshot") + tk.MustExec("set @@tidb_read_staleness = ''") + tk.MustExec("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'") +} + +func TestCoprocessorOOMAction(t *testing.T) { + // Assert Coprocessor OOMAction + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_enable_rate_limit_action=true") + tk.MustExec("create database testoom") + tk.MustExec("use testoom") + tk.MustExec(`set @@tidb_wait_split_region_finish=1`) + // create table for non keep-order case + tk.MustExec("drop table if exists t5") + tk.MustExec("create table t5(id int)") + tk.MustQuery(`split table t5 between (0) and (10000) regions 10`).Check(testkit.Rows("9 1")) + // create table for keep-order case + tk.MustExec("drop table if exists t6") + tk.MustExec("create table t6(id int, index(id))") + tk.MustQuery(`split table t6 between (0) and (10000) regions 10`).Check(testkit.Rows("10 1")) + tk.MustQuery("split table t6 INDEX id between (0) and (10000) regions 10;").Check(testkit.Rows("10 1")) + count := 10 + for i := 0; i < count; i++ { + tk.MustExec(fmt.Sprintf("insert into t5 (id) values (%v)", i)) + tk.MustExec(fmt.Sprintf("insert into t6 (id) values (%v)", i)) + } + + testcases := []struct { + name string + sql string + }{ + { + name: "keep Order", + sql: "select id from t6 order by id", + }, + { + name: "non keep Order", + sql: "select id from t5", + }, + } + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/testRateLimitActionMockConsumeAndAssert", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/testRateLimitActionMockConsumeAndAssert")) + }() + + enableOOM := func(tk *testkit.TestKit, name, sql string) { + t.Logf("enable OOM, testcase: %v", name) + // larger than 4 copResponse, smaller than 5 copResponse + quota := 5*copr.MockResponseSizeForTest - 100 + defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") + tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") + tk.MustExec("use testoom") + tk.MustExec("set @@tidb_enable_rate_limit_action=1") + tk.MustExec("set @@tidb_distsql_scan_concurrency = 10") + tk.MustExec(fmt.Sprintf("set @@tidb_mem_quota_query=%v;", quota)) + var expect []string + for i := 0; i < count; i++ { + expect = append(expect, fmt.Sprintf("%v", i)) + } + tk.MustQuery(sql).Sort().Check(testkit.Rows(expect...)) + // assert oom action worked by max consumed > memory quota + require.Greater(t, tk.Session().GetSessionVars().StmtCtx.MemTracker.MaxConsumed(), int64(quota)) + } + + disableOOM := func(tk *testkit.TestKit, name, sql string) { + t.Logf("disable OOM, testcase: %v", name) + quota := 5*copr.MockResponseSizeForTest - 100 + tk.MustExec("use testoom") + tk.MustExec("set @@tidb_enable_rate_limit_action=0") + tk.MustExec("set @@tidb_distsql_scan_concurrency = 10") + tk.MustExec(fmt.Sprintf("set @@tidb_mem_quota_query=%v;", quota)) + err := tk.QueryToErr(sql) + require.Error(t, err) + require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) + } + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/testRateLimitActionMockWaitMax", `return(true)`)) + // assert oom action and switch + for _, testcase := range testcases { + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(se) + enableOOM(tk, testcase.name, testcase.sql) + tk.MustExec("set @@tidb_enable_rate_limit_action = 0") + disableOOM(tk, testcase.name, testcase.sql) + tk.MustExec("set @@tidb_enable_rate_limit_action = 1") + enableOOM(tk, testcase.name, testcase.sql) + se.Close() + } + globaltk := testkit.NewTestKit(t, store) + globaltk.MustExec("use testoom") + globaltk.MustExec("set global tidb_enable_rate_limit_action= 0") + for _, testcase := range testcases { + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(se) + disableOOM(tk, testcase.name, testcase.sql) + se.Close() + } + globaltk.MustExec("set global tidb_enable_rate_limit_action= 1") + for _, testcase := range testcases { + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(se) + enableOOM(tk, testcase.name, testcase.sql) + se.Close() + } + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/testRateLimitActionMockWaitMax")) + + // assert oom fallback + for _, testcase := range testcases { + t.Log(testcase.name) + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + tk.SetSession(se) + tk.MustExec("use testoom") + tk.MustExec("set tidb_distsql_scan_concurrency = 1") + tk.MustExec("set @@tidb_mem_quota_query=1;") + err = tk.QueryToErr(testcase.sql) + require.Error(t, err) + require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) + se.Close() + } +} + +func TestStatementCountLimit(t *testing.T) { + store := testkit.CreateMockStore(t) + setTxnTk := testkit.NewTestKit(t, store) + setTxnTk.MustExec("set global tidb_txn_mode=''") + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table stmt_count_limit (id int)") + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.StmtCountLimit = 3 + }) + tk.MustExec("set tidb_disable_txn_auto_retry = 0") + tk.MustExec("begin") + tk.MustExec("insert into stmt_count_limit values (1)") + tk.MustExec("insert into stmt_count_limit values (2)") + _, err := tk.Exec("insert into stmt_count_limit values (3)") + require.Error(t, err) + + // begin is counted into history but this one is not. + tk.MustExec("SET SESSION autocommit = false") + tk.MustExec("insert into stmt_count_limit values (1)") + tk.MustExec("insert into stmt_count_limit values (2)") + tk.MustExec("insert into stmt_count_limit values (3)") + _, err = tk.Exec("insert into stmt_count_limit values (4)") + require.Error(t, err) +} + +func TestCorrectScopeError(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeNone, Name: "sv_none", Value: "acdc"}) + variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeGlobal, Name: "sv_global", Value: "acdc"}) + variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeSession, Name: "sv_session", Value: "acdc"}) + variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeGlobal | variable.ScopeSession, Name: "sv_both", Value: "acdc"}) + + // check set behavior + + // none + _, err := tk.Exec("SET sv_none='acdc'") + require.Equal(t, "[variable:1238]Variable 'sv_none' is a read only variable", err.Error()) + _, err = tk.Exec("SET GLOBAL sv_none='acdc'") + require.Equal(t, "[variable:1238]Variable 'sv_none' is a read only variable", err.Error()) + + // global + tk.MustExec("SET GLOBAL sv_global='acdc'") + _, err = tk.Exec("SET sv_global='acdc'") + require.Equal(t, "[variable:1229]Variable 'sv_global' is a GLOBAL variable and should be set with SET GLOBAL", err.Error()) + + // session + _, err = tk.Exec("SET GLOBAL sv_session='acdc'") + require.Equal(t, "[variable:1228]Variable 'sv_session' is a SESSION variable and can't be used with SET GLOBAL", err.Error()) + tk.MustExec("SET sv_session='acdc'") + + // both + tk.MustExec("SET GLOBAL sv_both='acdc'") + tk.MustExec("SET sv_both='acdc'") + + // unregister + variable.UnregisterSysVar("sv_none") + variable.UnregisterSysVar("sv_global") + variable.UnregisterSysVar("sv_session") + variable.UnregisterSysVar("sv_both") +} + +func TestReadDMLBatchSize(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set global tidb_dml_batch_size=1000") + se, err := session.CreateSession(store) + require.NoError(t, err) + + // `select 1` to load the global variables. + _, _ = se.Execute(context.TODO(), "select 1") + require.Equal(t, 1000, se.GetSessionVars().DMLBatchSize) +} + +func TestSetEnableRateLimitAction(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("set @@tidb_enable_rate_limit_action=true") + // assert default value + result := tk.MustQuery("select @@tidb_enable_rate_limit_action;") + result.Check(testkit.Rows("1")) + tk.MustExec("use test") + tk.MustExec("create table tmp123(id int)") + rs, err := tk.Exec("select * from tmp123;") + require.NoError(t, err) + haveRateLimitAction := false + action := tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false) + for ; action != nil; action = action.GetFallback() { + if action.GetPriority() == memory.DefRateLimitPriority { + haveRateLimitAction = true + break + } + } + require.True(t, haveRateLimitAction) + err = rs.Close() + require.NoError(t, err) + + // assert set sys variable + tk.MustExec("set global tidb_enable_rate_limit_action= '0';") + tk.Session().Close() + + tk.RefreshSession() + result = tk.MustQuery("select @@tidb_enable_rate_limit_action;") + result.Check(testkit.Rows("0")) + + haveRateLimitAction = false + action = tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false) + for ; action != nil; action = action.GetFallback() { + if action.GetPriority() == memory.DefRateLimitPriority { + haveRateLimitAction = true + break + } + } + require.False(t, haveRateLimitAction) +} + +func TestMaxExecutionTime(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("use test") + tk.MustExec("create table MaxExecTime( id int,name varchar(128),age int);") + tk.MustExec("begin") + tk.MustExec("insert into MaxExecTime (id,name,age) values (1,'john',18),(2,'lary',19),(3,'lily',18);") + + tk.MustQuery("select /*+ MAX_EXECUTION_TIME(1000) MAX_EXECUTION_TIME(500) */ * FROM MaxExecTime;") + require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) + require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "MAX_EXECUTION_TIME() is defined more than once, only the last definition takes effect: MAX_EXECUTION_TIME(500)") + require.True(t, tk.Session().GetSessionVars().StmtCtx.HasMaxExecutionTime) + require.Equal(t, uint64(500), tk.Session().GetSessionVars().StmtCtx.MaxExecutionTime) + + tk.MustQuery("select @@MAX_EXECUTION_TIME;").Check(testkit.Rows("0")) + tk.MustQuery("select @@global.MAX_EXECUTION_TIME;").Check(testkit.Rows("0")) + tk.MustQuery("select /*+ MAX_EXECUTION_TIME(1000) */ * FROM MaxExecTime;") + + tk.MustExec("set @@global.MAX_EXECUTION_TIME = 300;") + tk.MustQuery("select * FROM MaxExecTime;") + + tk.MustExec("set @@MAX_EXECUTION_TIME = 150;") + tk.MustQuery("select * FROM MaxExecTime;") + + tk.MustQuery("select @@global.MAX_EXECUTION_TIME;").Check(testkit.Rows("300")) + tk.MustQuery("select @@MAX_EXECUTION_TIME;").Check(testkit.Rows("150")) + + tk.MustExec("set @@global.MAX_EXECUTION_TIME = 0;") + tk.MustExec("set @@MAX_EXECUTION_TIME = 0;") + tk.MustExec("commit") + tk.MustExec("drop table if exists MaxExecTime;") +} + +func TestReplicaRead(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + require.Equal(t, kv.ReplicaReadLeader, tk.Session().GetSessionVars().GetReplicaRead()) + tk.MustExec("set @@tidb_replica_read = 'follower';") + require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) + tk.MustExec("set @@tidb_replica_read = 'leader';") + require.Equal(t, kv.ReplicaReadLeader, tk.Session().GetSessionVars().GetReplicaRead()) +} + +func TestIsolationRead(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + require.Len(t, tk.Session().GetSessionVars().GetIsolationReadEngines(), 3) + tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") + engines := tk.Session().GetSessionVars().GetIsolationReadEngines() + require.Len(t, engines, 1) + _, hasTiFlash := engines[kv.TiFlash] + _, hasTiKV := engines[kv.TiKV] + require.True(t, hasTiFlash) + require.False(t, hasTiKV) +} + +func TestIndexMergeRuntimeStats(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_enable_index_merge = 1") + tk.MustExec("create table t1(id int primary key, a int, b int, c int, d int)") + tk.MustExec("create index t1a on t1(a)") + tk.MustExec("create index t1b on t1(b)") + tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") + rows := tk.MustQuery("explain analyze select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4;").Rows() + require.Len(t, rows, 4) + explain := fmt.Sprintf("%v", rows[0]) + pattern := ".*time:.*loops:.*index_task:{fetch_handle:.*, merge:.*}.*table_task:{num.*concurrency.*fetch_row.*wait_time.*}.*" + require.Regexp(t, pattern, explain) + tableRangeExplain := fmt.Sprintf("%v", rows[1]) + indexExplain := fmt.Sprintf("%v", rows[2]) + tableExplain := fmt.Sprintf("%v", rows[3]) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableRangeExplain) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) + require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustQuery("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by a").Check(testkit.Rows("1 1 1 1 1", "5 5 5 5 5")) +} diff --git a/pkg/session/test/vars/BUILD.bazel b/pkg/session/test/vars/BUILD.bazel new file mode 100644 index 0000000000000..c4cbd087f307a --- /dev/null +++ b/pkg/session/test/vars/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "vars_test", + timeout = "short", + srcs = [ + "main_test.go", + "vars_test.go", + ], + flaky = True, + shard_count = 7, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//txnkv/transaction", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/session/test/vars/main_test.go b/pkg/session/test/vars/main_test.go new file mode 100644 index 0000000000000..045c176dae505 --- /dev/null +++ b/pkg/session/test/vars/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package vars + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/session/test/vars/vars_test.go b/pkg/session/test/vars/vars_test.go similarity index 98% rename from session/test/vars/vars_test.go rename to pkg/session/test/vars/vars_test.go index ba804e3c5a067..4668201346680 100644 --- a/session/test/vars/vars_test.go +++ b/pkg/session/test/vars/vars_test.go @@ -22,12 +22,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - tikv "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + tikv "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/txnkv/transaction" ) diff --git a/pkg/session/testutil.go b/pkg/session/testutil.go new file mode 100644 index 0000000000000..f73533a508454 --- /dev/null +++ b/pkg/session/testutil.go @@ -0,0 +1,95 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit/testenv" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" + atomicutil "go.uber.org/atomic" +) + +var ( + // GetBootstrapVersion is used in test + GetBootstrapVersion = getBootstrapVersion + // CurrentBootstrapVersion is used in test + CurrentBootstrapVersion = currentBootstrapVersion + // UnsetStoreBootstrapped is used in test + UnsetStoreBootstrapped = unsetStoreBootstrapped +) + +// CreateStoreAndBootstrap creates a mock store and bootstrap it. +func CreateStoreAndBootstrap(t *testing.T) (kv.Storage, *domain.Domain) { + testenv.SetGOMAXPROCSForTest() + store, err := mockstore.NewMockStore() + require.NoError(t, err) + dom, err := BootstrapSession(store) + require.NoError(t, err) + return store, dom +} + +var sessionKitIDGenerator atomicutil.Uint64 + +// CreateSessionAndSetID creates a session and set connection ID. +func CreateSessionAndSetID(t *testing.T, store kv.Storage) Session { + se, err := CreateSession4Test(store) + se.SetConnectionID(sessionKitIDGenerator.Inc()) + require.NoError(t, err) + return se +} + +// MustExec executes a sql statement and asserts no error occurs. +func MustExec(t *testing.T, se Session, sql string, args ...interface{}) { + rs, err := exec(se, sql, args...) + require.NoError(t, err) + if rs != nil { + require.NoError(t, rs.Close()) + } +} + +// MustExecToRecodeSet executes a sql statement and asserts no error occurs. +func MustExecToRecodeSet(t *testing.T, se Session, sql string, args ...interface{}) sqlexec.RecordSet { + rs, err := exec(se, sql, args...) + require.NoError(t, err) + return rs +} + +func exec(se Session, sql string, args ...interface{}) (sqlexec.RecordSet, error) { + ctx := context.Background() + if len(args) == 0 { + rs, err := se.Execute(ctx, sql) + if err == nil && len(rs) > 0 { + return rs[0], nil + } + return nil, err + } + stmtID, _, _, err := se.PrepareStmt(sql) + if err != nil { + return nil, err + } + params := expression.Args2Expressions4Test(args...) + rs, err := se.ExecutePreparedStmt(ctx, stmtID, params) + if err != nil { + return nil, err + } + return rs, nil +} diff --git a/session/tidb.go b/pkg/session/tidb.go similarity index 93% rename from session/tidb.go rename to pkg/session/tidb.go index 87fdb26d91d36..d6c9c59a6a4d3 100644 --- a/session/tidb.go +++ b/pkg/session/tidb.go @@ -25,26 +25,26 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - session_metrics "github.com/pingcap/tidb/session/metrics" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + session_metrics "github.com/pingcap/tidb/pkg/session/metrics" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/syncutil" "go.uber.org/zap" ) diff --git a/pkg/session/tidb_test.go b/pkg/session/tidb_test.go new file mode 100644 index 0000000000000..527f2cba37c98 --- /dev/null +++ b/pkg/session/tidb_test.go @@ -0,0 +1,114 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestDomapHandleNil(t *testing.T) { + // this is required for enterprise plugins + // ref: https://github.com/pingcap/tidb/issues/37319 + require.NotPanics(t, func() { + _, _ = domap.Get(nil) + }) +} + +func TestSysSessionPoolGoroutineLeak(t *testing.T) { + store, dom := CreateStoreAndBootstrap(t) + defer func() { require.NoError(t, store.Close()) }() + defer dom.Close() + + se, err := createSession(store) + require.NoError(t, err) + + count := 200 + stmts := make([]ast.StmtNode, count) + for i := 0; i < count; i++ { + stmt, err := se.ParseWithParams(context.Background(), "select * from mysql.user limit 1") + require.NoError(t, err) + stmts[i] = stmt + } + // Test an issue that sysSessionPool doesn't call session's Close, cause + // asyncGetTSWorker goroutine leak. + var wg util.WaitGroupWrapper + for i := 0; i < count; i++ { + s := stmts[i] + wg.Run(func() { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + _, _, err := se.ExecRestrictedStmt(ctx, s) + require.NoError(t, err) + }) + } + wg.Wait() +} + +func TestParseErrorWarn(t *testing.T) { + ctx := core.MockContext() + defer func() { + domain.GetDomain(ctx).StatsHandle().Close() + }() + nodes, err := Parse(ctx, "select /*+ adf */ 1") + require.NoError(t, err) + require.Len(t, nodes, 1) + require.Len(t, ctx.GetSessionVars().StmtCtx.GetWarnings(), 1) + + _, err = Parse(ctx, "select") + require.Error(t, err) +} + +func TestKeysNeedLock(t *testing.T) { + rowKey := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) + uniqueIndexKey := tablecodec.EncodeIndexSeekKey(1, 1, []byte{1}) + nonUniqueIndexKey := tablecodec.EncodeIndexSeekKey(1, 2, []byte{1}) + uniqueValue := make([]byte, 8) + uniqueUntouched := append(uniqueValue, '1') + nonUniqueVal := []byte{'0'} + nonUniqueUntouched := []byte{'1'} + var deleteVal []byte + rowVal := []byte{'a', 'b', 'c'} + tests := []struct { + key []byte + val []byte + need bool + }{ + {rowKey, rowVal, true}, + {rowKey, deleteVal, true}, + {nonUniqueIndexKey, nonUniqueVal, false}, + {nonUniqueIndexKey, nonUniqueUntouched, false}, + {uniqueIndexKey, uniqueValue, true}, + {uniqueIndexKey, uniqueUntouched, false}, + {uniqueIndexKey, deleteVal, false}, + } + + for _, test := range tests { + need := keyNeedToLock(test.key, test.val, 0) + require.Equal(t, test.need, need) + + flag := kv.KeyFlags(1) + need = keyNeedToLock(test.key, test.val, flag) + require.True(t, flag.HasPresumeKeyNotExists()) + require.True(t, need) + } +} diff --git a/pkg/session/txn.go b/pkg/session/txn.go new file mode 100644 index 0000000000000..8bf5d66ef529c --- /dev/null +++ b/pkg/session/txn.go @@ -0,0 +1,749 @@ +// Copyright 2018 PingCAP, Inc. + +// 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. + +package session + +import ( + "bytes" + "context" + "fmt" + "runtime/trace" + "strings" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sli" + "github.com/pingcap/tidb/pkg/util/syncutil" + "github.com/pingcap/tipb/go-binlog" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" +) + +// LazyTxn wraps kv.Transaction to provide a new kv.Transaction. +// 1. It holds all statement related modification in the buffer before flush to the txn, +// so if execute statement meets error, the txn won't be made dirty. +// 2. It's a lazy transaction, that means it's a txnFuture before StartTS() is really need. +type LazyTxn struct { + // States of a LazyTxn should be one of the followings: + // Invalid: kv.Transaction == nil && txnFuture == nil + // Pending: kv.Transaction == nil && txnFuture != nil + // Valid: kv.Transaction != nil && txnFuture == nil + kv.Transaction + txnFuture *txnFuture + + initCnt int + stagingHandle kv.StagingHandle + mutations map[int64]*binlog.TableMutation + writeSLI sli.TxnWriteThroughputSLI + + enterFairLockingOnValid bool + + // TxnInfo is added for the lock view feature, the data is frequent modified but + // rarely read (just in query select * from information_schema.tidb_trx). + // The data in this session would be query by other sessions, so Mutex is necessary. + // Since read is rare, the reader can copy-on-read to get a data snapshot. + mu struct { + syncutil.RWMutex + txninfo.TxnInfo + } + + // mark the txn enables lazy uniqueness check in pessimistic transactions. + lazyUniquenessCheckEnabled bool +} + +// GetTableInfo returns the cached index name. +func (txn *LazyTxn) GetTableInfo(id int64) *model.TableInfo { + return txn.Transaction.GetTableInfo(id) +} + +// CacheTableInfo caches the index name. +func (txn *LazyTxn) CacheTableInfo(id int64, info *model.TableInfo) { + txn.Transaction.CacheTableInfo(id, info) +} + +func (txn *LazyTxn) init() { + txn.mutations = make(map[int64]*binlog.TableMutation) + txn.mu.Lock() + defer txn.mu.Unlock() + txn.mu.TxnInfo = txninfo.TxnInfo{} +} + +// call this under lock! +func (txn *LazyTxn) updateState(state txninfo.TxnRunningState) { + if txn.mu.TxnInfo.State != state { + lastState := txn.mu.TxnInfo.State + lastStateChangeTime := txn.mu.TxnInfo.LastStateChangeTime + txn.mu.TxnInfo.State = state + txn.mu.TxnInfo.LastStateChangeTime = time.Now() + if !lastStateChangeTime.IsZero() { + hasLockLbl := !txn.mu.TxnInfo.BlockStartTime.IsZero() + txninfo.TxnDurationHistogram(lastState, hasLockLbl).Observe(time.Since(lastStateChangeTime).Seconds()) + } + txninfo.TxnStatusEnteringCounter(state).Inc() + } +} + +func (txn *LazyTxn) initStmtBuf() { + if txn.Transaction == nil { + return + } + buf := txn.Transaction.GetMemBuffer() + txn.initCnt = buf.Len() + txn.stagingHandle = buf.Staging() +} + +// countHint is estimated count of mutations. +func (txn *LazyTxn) countHint() int { + if txn.stagingHandle == kv.InvalidStagingHandle { + return 0 + } + return txn.Transaction.GetMemBuffer().Len() - txn.initCnt +} + +func (txn *LazyTxn) flushStmtBuf() { + if txn.stagingHandle == kv.InvalidStagingHandle { + return + } + buf := txn.Transaction.GetMemBuffer() + + if txn.lazyUniquenessCheckEnabled { + keysNeedSetPersistentPNE := kv.FindKeysInStage(buf, txn.stagingHandle, func(k kv.Key, flags kv.KeyFlags, v []byte) bool { + return flags.HasPresumeKeyNotExists() + }) + for _, key := range keysNeedSetPersistentPNE { + buf.UpdateFlags(key, kv.SetPreviousPresumeKeyNotExists) + } + } + + buf.Release(txn.stagingHandle) + txn.initCnt = buf.Len() +} + +func (txn *LazyTxn) cleanupStmtBuf() { + if txn.stagingHandle == kv.InvalidStagingHandle { + return + } + buf := txn.Transaction.GetMemBuffer() + buf.Cleanup(txn.stagingHandle) + txn.initCnt = buf.Len() + + txn.mu.Lock() + defer txn.mu.Unlock() + txn.mu.TxnInfo.EntriesCount = uint64(txn.Transaction.Len()) +} + +// resetTxnInfo resets the transaction info. +// Note: call it under lock! +func (txn *LazyTxn) resetTxnInfo( + startTS uint64, + state txninfo.TxnRunningState, + entriesCount uint64, + currentSQLDigest string, + allSQLDigests []string, +) { + if !txn.mu.LastStateChangeTime.IsZero() { + lastState := txn.mu.State + hasLockLbl := !txn.mu.BlockStartTime.IsZero() + txninfo.TxnDurationHistogram(lastState, hasLockLbl).Observe(time.Since(txn.mu.TxnInfo.LastStateChangeTime).Seconds()) + } + if txn.mu.TxnInfo.StartTS != 0 { + txninfo.Recorder.OnTrxEnd(&txn.mu.TxnInfo) + } + txn.mu.TxnInfo = txninfo.TxnInfo{} + txn.mu.TxnInfo.StartTS = startTS + txn.mu.TxnInfo.State = state + txninfo.TxnStatusEnteringCounter(state).Inc() + txn.mu.TxnInfo.LastStateChangeTime = time.Now() + txn.mu.TxnInfo.EntriesCount = entriesCount + + txn.mu.TxnInfo.CurrentSQLDigest = currentSQLDigest + txn.mu.TxnInfo.AllSQLDigests = allSQLDigests +} + +// Size implements the MemBuffer interface. +func (txn *LazyTxn) Size() int { + if txn.Transaction == nil { + return 0 + } + return txn.Transaction.Size() +} + +// Mem implements the MemBuffer interface. +func (txn *LazyTxn) Mem() uint64 { + if txn.Transaction == nil { + return 0 + } + return txn.Transaction.Mem() +} + +// SetMemoryFootprintChangeHook sets the hook to be called when the memory footprint of this transaction changes. +func (txn *LazyTxn) SetMemoryFootprintChangeHook(hook func(uint64)) { + if txn.Transaction == nil { + return + } + txn.Transaction.SetMemoryFootprintChangeHook(hook) +} + +// Valid implements the kv.Transaction interface. +func (txn *LazyTxn) Valid() bool { + return txn.Transaction != nil && txn.Transaction.Valid() +} + +func (txn *LazyTxn) pending() bool { + return txn.Transaction == nil && txn.txnFuture != nil +} + +func (txn *LazyTxn) validOrPending() bool { + return txn.txnFuture != nil || txn.Valid() +} + +func (txn *LazyTxn) String() string { + if txn.Transaction != nil { + return txn.Transaction.String() + } + if txn.txnFuture != nil { + res := "txnFuture" + if txn.enterFairLockingOnValid { + res += " (pending fair locking)" + } + return res + } + return "invalid transaction" +} + +// GoString implements the "%#v" format for fmt.Printf. +func (txn *LazyTxn) GoString() string { + var s strings.Builder + s.WriteString("Txn{") + if txn.pending() { + s.WriteString("state=pending") + } else if txn.Valid() { + s.WriteString("state=valid") + fmt.Fprintf(&s, ", txnStartTS=%d", txn.Transaction.StartTS()) + if len(txn.mutations) > 0 { + fmt.Fprintf(&s, ", len(mutations)=%d, %#v", len(txn.mutations), txn.mutations) + } + } else { + s.WriteString("state=invalid") + } + + s.WriteString("}") + return s.String() +} + +// GetOption implements the GetOption +func (txn *LazyTxn) GetOption(opt int) interface{} { + if txn.Transaction == nil { + if opt == kv.TxnScope { + return "" + } + return nil + } + return txn.Transaction.GetOption(opt) +} + +func (txn *LazyTxn) changeToPending(future *txnFuture) { + txn.Transaction = nil + txn.txnFuture = future +} + +func (txn *LazyTxn) changePendingToValid(ctx context.Context, sctx sessionctx.Context) error { + if txn.txnFuture == nil { + return errors.New("transaction future is not set") + } + + future := txn.txnFuture + txn.txnFuture = nil + + defer trace.StartRegion(ctx, "WaitTsoFuture").End() + t, err := future.wait() + if err != nil { + txn.Transaction = nil + return err + } + txn.Transaction = t + txn.initStmtBuf() + + if txn.enterFairLockingOnValid { + txn.enterFairLockingOnValid = false + err = txn.Transaction.StartFairLocking() + if err != nil { + return err + } + } + + // The txnInfo may already recorded the first statement (usually "begin") when it's pending, so keep them. + txn.mu.Lock() + defer txn.mu.Unlock() + txn.resetTxnInfo( + t.StartTS(), + txninfo.TxnIdle, + uint64(txn.Transaction.Len()), + txn.mu.TxnInfo.CurrentSQLDigest, + txn.mu.TxnInfo.AllSQLDigests) + + // set resource group name for kv request such as lock pessimistic keys. + kv.SetTxnResourceGroup(txn, sctx.GetSessionVars().ResourceGroupName) + + return nil +} + +func (txn *LazyTxn) changeToInvalid() { + if txn.stagingHandle != kv.InvalidStagingHandle { + txn.Transaction.GetMemBuffer().Cleanup(txn.stagingHandle) + } + txn.stagingHandle = kv.InvalidStagingHandle + txn.Transaction = nil + txn.txnFuture = nil + + txn.enterFairLockingOnValid = false + + txn.mu.Lock() + lastState := txn.mu.TxnInfo.State + lastStateChangeTime := txn.mu.TxnInfo.LastStateChangeTime + hasLock := !txn.mu.TxnInfo.BlockStartTime.IsZero() + if txn.mu.TxnInfo.StartTS != 0 { + txninfo.Recorder.OnTrxEnd(&txn.mu.TxnInfo) + } + txn.mu.TxnInfo = txninfo.TxnInfo{} + txn.mu.Unlock() + if !lastStateChangeTime.IsZero() { + txninfo.TxnDurationHistogram(lastState, hasLock).Observe(time.Since(lastStateChangeTime).Seconds()) + } +} + +func (txn *LazyTxn) onStmtStart(currentSQLDigest string) { + if len(currentSQLDigest) == 0 { + return + } + + txn.mu.Lock() + defer txn.mu.Unlock() + txn.updateState(txninfo.TxnRunning) + txn.mu.TxnInfo.CurrentSQLDigest = currentSQLDigest + // Keeps at most 50 history sqls to avoid consuming too much memory. + const maxTransactionStmtHistory int = 50 + if len(txn.mu.TxnInfo.AllSQLDigests) < maxTransactionStmtHistory { + txn.mu.TxnInfo.AllSQLDigests = append(txn.mu.TxnInfo.AllSQLDigests, currentSQLDigest) + } +} + +func (txn *LazyTxn) onStmtEnd() { + txn.mu.Lock() + defer txn.mu.Unlock() + txn.mu.TxnInfo.CurrentSQLDigest = "" + txn.updateState(txninfo.TxnIdle) +} + +var hasMockAutoIncIDRetry = int64(0) + +func enableMockAutoIncIDRetry() { + atomic.StoreInt64(&hasMockAutoIncIDRetry, 1) +} + +func mockAutoIncIDRetry() bool { + return atomic.LoadInt64(&hasMockAutoIncIDRetry) == 1 +} + +var mockAutoRandIDRetryCount = int64(0) + +func needMockAutoRandIDRetry() bool { + return atomic.LoadInt64(&mockAutoRandIDRetryCount) > 0 +} + +func decreaseMockAutoRandIDRetryCount() { + atomic.AddInt64(&mockAutoRandIDRetryCount, -1) +} + +// ResetMockAutoRandIDRetryCount set the number of occurrences of +// `kv.ErrTxnRetryable` when calling TxnState.Commit(). +func ResetMockAutoRandIDRetryCount(failTimes int64) { + atomic.StoreInt64(&mockAutoRandIDRetryCount, failTimes) +} + +// Commit overrides the Transaction interface. +func (txn *LazyTxn) Commit(ctx context.Context) error { + defer txn.reset() + if len(txn.mutations) != 0 || txn.countHint() != 0 { + logutil.BgLogger().Error("the code should never run here", + zap.String("TxnState", txn.GoString()), + zap.Int("staging handler", int(txn.stagingHandle)), + zap.Int("mutations", txn.countHint()), + zap.Stack("something must be wrong")) + return errors.Trace(kv.ErrInvalidTxn) + } + + txn.mu.Lock() + txn.updateState(txninfo.TxnCommitting) + txn.mu.Unlock() + + failpoint.Inject("mockSlowCommit", func(_ failpoint.Value) {}) + + // mockCommitError8942 is used for PR #8942. + failpoint.Inject("mockCommitError8942", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(kv.ErrTxnRetryable) + } + }) + + // mockCommitRetryForAutoIncID is used to mock an commit retry for adjustAutoIncrementDatum. + failpoint.Inject("mockCommitRetryForAutoIncID", func(val failpoint.Value) { + if val.(bool) && !mockAutoIncIDRetry() { + enableMockAutoIncIDRetry() + failpoint.Return(kv.ErrTxnRetryable) + } + }) + + failpoint.Inject("mockCommitRetryForAutoRandID", func(val failpoint.Value) { + if val.(bool) && needMockAutoRandIDRetry() { + decreaseMockAutoRandIDRetryCount() + failpoint.Return(kv.ErrTxnRetryable) + } + }) + + return txn.Transaction.Commit(ctx) +} + +// Rollback overrides the Transaction interface. +func (txn *LazyTxn) Rollback() error { + defer txn.reset() + txn.mu.Lock() + txn.updateState(txninfo.TxnRollingBack) + txn.mu.Unlock() + // mockSlowRollback is used to mock a rollback which takes a long time + failpoint.Inject("mockSlowRollback", func(_ failpoint.Value) {}) + return txn.Transaction.Rollback() +} + +// RollbackMemDBToCheckpoint overrides the Transaction interface. +func (txn *LazyTxn) RollbackMemDBToCheckpoint(savepoint *tikv.MemDBCheckpoint) { + txn.flushStmtBuf() + txn.Transaction.RollbackMemDBToCheckpoint(savepoint) + txn.cleanup() +} + +// LockKeys wraps the inner transaction's `LockKeys` to record the status +func (txn *LazyTxn) LockKeys(ctx context.Context, lockCtx *kv.LockCtx, keys ...kv.Key) error { + return txn.LockKeysFunc(ctx, lockCtx, nil, keys...) +} + +// LockKeysFunc Wrap the inner transaction's `LockKeys` to record the status +func (txn *LazyTxn) LockKeysFunc(ctx context.Context, lockCtx *kv.LockCtx, fn func(), keys ...kv.Key) error { + failpoint.Inject("beforeLockKeys", func() {}) + t := time.Now() + + var originState txninfo.TxnRunningState + txn.mu.Lock() + originState = txn.mu.TxnInfo.State + txn.updateState(txninfo.TxnLockAcquiring) + txn.mu.TxnInfo.BlockStartTime.Valid = true + txn.mu.TxnInfo.BlockStartTime.Time = t + txn.mu.Unlock() + lockFunc := func() { + if fn != nil { + fn() + } + txn.mu.Lock() + defer txn.mu.Unlock() + txn.updateState(originState) + txn.mu.TxnInfo.BlockStartTime.Valid = false + txn.mu.TxnInfo.EntriesCount = uint64(txn.Transaction.Len()) + } + return txn.Transaction.LockKeysFunc(ctx, lockCtx, lockFunc, keys...) +} + +// StartFairLocking wraps the inner transaction to support using fair locking with lazy initialization. +func (txn *LazyTxn) StartFairLocking() error { + if txn.Valid() { + return txn.Transaction.StartFairLocking() + } else if !txn.pending() { + err := errors.New("trying to start fair locking on a transaction in invalid state") + logutil.BgLogger().Error("unexpected error when starting fair locking", zap.Error(err), zap.Stringer("txn", txn)) + return err + } + txn.enterFairLockingOnValid = true + return nil +} + +// RetryFairLocking wraps the inner transaction to support using fair locking with lazy initialization. +func (txn *LazyTxn) RetryFairLocking(ctx context.Context) error { + if txn.Valid() { + return txn.Transaction.RetryFairLocking(ctx) + } else if !txn.pending() { + err := errors.New("trying to retry fair locking on a transaction in invalid state") + logutil.BgLogger().Error("unexpected error when retrying fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) + return err + } + return nil +} + +// CancelFairLocking wraps the inner transaction to support using fair locking with lazy initialization. +func (txn *LazyTxn) CancelFairLocking(ctx context.Context) error { + if txn.Valid() { + return txn.Transaction.CancelFairLocking(ctx) + } else if !txn.pending() { + err := errors.New("trying to cancel fair locking on a transaction in invalid state") + logutil.BgLogger().Error("unexpected error when cancelling fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) + return err + } + if !txn.enterFairLockingOnValid { + err := errors.New("trying to cancel fair locking when it's not started") + logutil.BgLogger().Error("unexpected error when cancelling fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) + return err + } + txn.enterFairLockingOnValid = false + return nil +} + +// DoneFairLocking wraps the inner transaction to support using fair locking with lazy initialization. +func (txn *LazyTxn) DoneFairLocking(ctx context.Context) error { + if txn.Valid() { + return txn.Transaction.DoneFairLocking(ctx) + } + if !txn.pending() { + err := errors.New("trying to cancel fair locking on a transaction in invalid state") + logutil.BgLogger().Error("unexpected error when finishing fair locking") + return err + } + if !txn.enterFairLockingOnValid { + err := errors.New("trying to finish fair locking when it's not started") + logutil.BgLogger().Error("unexpected error when finishing fair locking") + return err + } + txn.enterFairLockingOnValid = false + return nil +} + +// IsInFairLockingMode wraps the inner transaction to support using fair locking with lazy initialization. +func (txn *LazyTxn) IsInFairLockingMode() bool { + if txn.Valid() { + return txn.Transaction.IsInFairLockingMode() + } else if txn.pending() { + return txn.enterFairLockingOnValid + } else { + return false + } +} + +func (txn *LazyTxn) reset() { + txn.cleanup() + txn.changeToInvalid() +} + +func (txn *LazyTxn) cleanup() { + txn.cleanupStmtBuf() + txn.initStmtBuf() + for key := range txn.mutations { + delete(txn.mutations, key) + } +} + +// KeysNeedToLock returns the keys need to be locked. +func (txn *LazyTxn) KeysNeedToLock() ([]kv.Key, error) { + if txn.stagingHandle == kv.InvalidStagingHandle { + return nil, nil + } + keys := make([]kv.Key, 0, txn.countHint()) + buf := txn.Transaction.GetMemBuffer() + buf.InspectStage(txn.stagingHandle, func(k kv.Key, flags kv.KeyFlags, v []byte) { + if !keyNeedToLock(k, v, flags) { + return + } + keys = append(keys, k) + }) + + return keys, nil +} + +// Wait converts pending txn to valid +func (txn *LazyTxn) Wait(ctx context.Context, sctx sessionctx.Context) (kv.Transaction, error) { + if !txn.validOrPending() { + return txn, errors.AddStack(kv.ErrInvalidTxn) + } + if txn.pending() { + defer func(begin time.Time) { + sctx.GetSessionVars().DurationWaitTS = time.Since(begin) + }(time.Now()) + + // Transaction is lazy initialized. + // PrepareTxnCtx is called to get a tso future, makes s.txn a pending txn, + // If Txn() is called later, wait for the future to get a valid txn. + if err := txn.changePendingToValid(ctx, sctx); err != nil { + logutil.BgLogger().Error("active transaction fail", + zap.Error(err)) + txn.cleanup() + sctx.GetSessionVars().TxnCtx.StartTS = 0 + return txn, err + } + txn.lazyUniquenessCheckEnabled = !sctx.GetSessionVars().ConstraintCheckInPlacePessimistic + } + return txn, nil +} + +func keyNeedToLock(k, v []byte, flags kv.KeyFlags) bool { + isTableKey := bytes.HasPrefix(k, tablecodec.TablePrefix()) + if !isTableKey { + // meta key always need to lock. + return true + } + + // a pessimistic locking is skipped, perform the conflict check and + // constraint check (more accurately, PresumeKeyNotExist) in prewrite (or later pessimistic locking) + if flags.HasNeedConstraintCheckInPrewrite() { + return false + } + + if flags.HasPresumeKeyNotExists() { + return true + } + + // lock row key, primary key and unique index for delete operation, + if len(v) == 0 { + return flags.HasNeedLocked() || tablecodec.IsRecordKey(k) + } + + if tablecodec.IsUntouchedIndexKValue(k, v) { + return false + } + + if !tablecodec.IsIndexKey(k) { + return true + } + + if tablecodec.IsTempIndexKey(k) { + tmpVal, err := tablecodec.DecodeTempIndexValue(v) + if err != nil { + logutil.BgLogger().Warn("decode temp index value failed", zap.Error(err)) + return false + } + current := tmpVal.Current() + return current.Handle != nil || tablecodec.IndexKVIsUnique(current.Value) + } + + return tablecodec.IndexKVIsUnique(v) +} + +func getBinlogMutation(ctx sessionctx.Context, tableID int64) *binlog.TableMutation { + bin := binloginfo.GetPrewriteValue(ctx, true) + for i := range bin.Mutations { + if bin.Mutations[i].TableId == tableID { + return &bin.Mutations[i] + } + } + idx := len(bin.Mutations) + bin.Mutations = append(bin.Mutations, binlog.TableMutation{TableId: tableID}) + return &bin.Mutations[idx] +} + +func mergeToMutation(m1, m2 *binlog.TableMutation) { + m1.InsertedRows = append(m1.InsertedRows, m2.InsertedRows...) + m1.UpdatedRows = append(m1.UpdatedRows, m2.UpdatedRows...) + m1.DeletedIds = append(m1.DeletedIds, m2.DeletedIds...) + m1.DeletedPks = append(m1.DeletedPks, m2.DeletedPks...) + m1.DeletedRows = append(m1.DeletedRows, m2.DeletedRows...) + m1.Sequence = append(m1.Sequence, m2.Sequence...) +} + +type txnFailFuture struct{} + +func (txnFailFuture) Wait() (uint64, error) { + return 0, errors.New("mock get timestamp fail") +} + +// txnFuture is a promise, which promises to return a txn in future. +type txnFuture struct { + future oracle.Future + store kv.Storage + txnScope string +} + +func (tf *txnFuture) wait() (kv.Transaction, error) { + startTS, err := tf.future.Wait() + failpoint.Inject("txnFutureWait", func() {}) + if err == nil { + return tf.store.Begin(tikv.WithTxnScope(tf.txnScope), tikv.WithStartTS(startTS)) + } else if config.GetGlobalConfig().Store == "unistore" { + return nil, err + } + + logutil.BgLogger().Warn("wait tso failed", zap.Error(err)) + // It would retry get timestamp. + return tf.store.Begin(tikv.WithTxnScope(tf.txnScope)) +} + +// HasDirtyContent checks whether there's dirty update on the given table. +// Put this function here is to avoid cycle import. +func (s *session) HasDirtyContent(tid int64) bool { + if s.txn.Transaction == nil { + return false + } + seekKey := tablecodec.EncodeTablePrefix(tid) + it, err := s.txn.GetMemBuffer().Iter(seekKey, nil) + terror.Log(err) + return it.Valid() && bytes.HasPrefix(it.Key(), seekKey) +} + +// StmtCommit implements the sessionctx.Context interface. +func (s *session) StmtCommit(ctx context.Context) { + defer func() { + s.txn.cleanup() + }() + + txnManager := sessiontxn.GetTxnManager(s) + err := txnManager.OnStmtCommit(ctx) + if err != nil { + logutil.Logger(ctx).Error("txnManager failed to handle OnStmtCommit", zap.Error(err)) + } + + st := &s.txn + st.flushStmtBuf() + + // Need to flush binlog. + for tableID, delta := range st.mutations { + mutation := getBinlogMutation(s, tableID) + mergeToMutation(mutation, delta) + } +} + +// StmtRollback implements the sessionctx.Context interface. +func (s *session) StmtRollback(ctx context.Context, isForPessimisticRetry bool) { + txnManager := sessiontxn.GetTxnManager(s) + err := txnManager.OnStmtRollback(ctx, isForPessimisticRetry) + if err != nil { + logutil.Logger(ctx).Error("txnManager failed to handle OnStmtRollback", zap.Error(err)) + } + s.txn.cleanup() +} + +// StmtGetMutation implements the sessionctx.Context interface. +func (s *session) StmtGetMutation(tableID int64) *binlog.TableMutation { + st := &s.txn + if _, ok := st.mutations[tableID]; !ok { + st.mutations[tableID] = &binlog.TableMutation{TableId: tableID} + } + return st.mutations[tableID] +} diff --git a/pkg/session/txninfo/BUILD.bazel b/pkg/session/txninfo/BUILD.bazel new file mode 100644 index 0000000000000..8062dd58b6d28 --- /dev/null +++ b/pkg/session/txninfo/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "txninfo", + srcs = [ + "summary.go", + "txn_info.go", + ], + importpath = "github.com/pingcap/tidb/pkg/session/txninfo", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/logutil", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/session/txninfo/summary.go b/pkg/session/txninfo/summary.go new file mode 100644 index 0000000000000..b4e102c9b2ad5 --- /dev/null +++ b/pkg/session/txninfo/summary.go @@ -0,0 +1,162 @@ +// Copyright 2021 PingCAP, Inc. + +// 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. + +package txninfo + +import ( + "container/list" + "encoding/json" + "fmt" + "hash/fnv" + "sync" + "time" + + "github.com/pingcap/tidb/pkg/types" + "github.com/tikv/client-go/v2/oracle" +) + +func digest(digests []string) uint64 { + // We use FNV-1a hash to generate the 64bit digest + // since 64bit digest use less memory and FNV-1a is faster than most of other hash algorithms + // You can refer to https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed + hash := fnv.New64a() + for _, digest := range digests { + hash.Write([]byte(digest)) + } + return hash.Sum64() +} + +type trxSummaryEntry struct { + trxDigest uint64 + digests []string +} + +type trxSummaries struct { + capacity uint + + // lru cache for digest -> trxSummaryEntry + elements map[uint64]*list.Element + cache *list.List +} + +func newTrxSummaries(capacity uint) trxSummaries { + return trxSummaries{ + capacity: capacity, + cache: list.New(), + elements: make(map[uint64]*list.Element), + } +} + +func (s *trxSummaries) onTrxEnd(digests []string) { + key := digest(digests) + element, exists := s.elements[key] + if exists { + s.cache.MoveToFront(element) + return + } + e := trxSummaryEntry{ + trxDigest: key, + digests: digests, + } + s.elements[key] = s.cache.PushFront(e) + if uint(s.cache.Len()) > s.capacity { + last := s.cache.Back() + delete(s.elements, last.Value.(trxSummaryEntry).trxDigest) + s.cache.Remove(last) + } +} + +func (s *trxSummaries) dumpTrxSummary() [][]types.Datum { + var result [][]types.Datum + for element := s.cache.Front(); element != nil; element = element.Next() { + sqls := element.Value.(trxSummaryEntry).digests + // for consistency with other digests in TiDB, we calculate sum256 here to generate varchar(64) digest + digest := fmt.Sprintf("%x", element.Value.(trxSummaryEntry).trxDigest) + + res, err := json.Marshal(sqls) + if err != nil { + panic(err) + } + + result = append(result, []types.Datum{ + types.NewDatum(digest), + types.NewDatum(string(res)), + }) + } + return result +} + +func (s *trxSummaries) resize(capacity uint) { + s.capacity = capacity + for uint(s.cache.Len()) > s.capacity { + last := s.cache.Back() + delete(s.elements, last.Value.(trxSummaryEntry).trxDigest) + s.cache.Remove(last) + } +} + +// TrxHistoryRecorder is a history recorder for transaction. +type TrxHistoryRecorder struct { + mu sync.Mutex + minDuration time.Duration + summaries trxSummaries +} + +// DumpTrxSummary dumps the transaction summary to Datum for displaying in `TRX_SUMMARY` table. +func (recorder *TrxHistoryRecorder) DumpTrxSummary() [][]types.Datum { + recorder.mu.Lock() + defer recorder.mu.Unlock() + return recorder.summaries.dumpTrxSummary() +} + +// OnTrxEnd should be called when a transaction ends, ie. leaves `TIDB_TRX` table. +func (recorder *TrxHistoryRecorder) OnTrxEnd(info *TxnInfo) { + now := time.Now() + startTime := time.UnixMilli(oracle.ExtractPhysical(info.StartTS)) + recorder.mu.Lock() + defer recorder.mu.Unlock() + if now.Sub(startTime) < recorder.minDuration { + return + } + recorder.summaries.onTrxEnd(info.AllSQLDigests) +} + +func newTrxHistoryRecorder(summariesCap uint) TrxHistoryRecorder { + return TrxHistoryRecorder{ + summaries: newTrxSummaries(summariesCap), + minDuration: 1 * time.Second, + } +} + +// Clean clears the history recorder. For test only. +func (recorder *TrxHistoryRecorder) Clean() { + recorder.summaries.cache = list.New() +} + +// SetMinDuration sets the minimum duration for a transaction to be recorded. +func (recorder *TrxHistoryRecorder) SetMinDuration(d time.Duration) { + recorder.mu.Lock() + defer recorder.mu.Unlock() + recorder.minDuration = d +} + +// ResizeSummaries resizes the summaries capacity. +func (recorder *TrxHistoryRecorder) ResizeSummaries(capacity uint) { + recorder.mu.Lock() + defer recorder.mu.Unlock() + recorder.summaries.resize(capacity) +} + +// Recorder is the recorder instance. +var Recorder = newTrxHistoryRecorder(0) diff --git a/session/txninfo/txn_info.go b/pkg/session/txninfo/txn_info.go similarity index 98% rename from session/txninfo/txn_info.go rename to pkg/session/txninfo/txn_info.go index cfcdb43d7b000..50e60da829a46 100644 --- a/session/txninfo/txn_info.go +++ b/pkg/session/txninfo/txn_info.go @@ -20,10 +20,10 @@ import ( "strings" "time" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/prometheus/client_golang/prometheus" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" diff --git a/session/txnmanager.go b/pkg/session/txnmanager.go similarity index 96% rename from session/txnmanager.go rename to pkg/session/txnmanager.go index 2e41a4c040f89..4caf790643e2e 100644 --- a/session/txnmanager.go +++ b/pkg/session/txnmanager.go @@ -21,15 +21,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/pkg/sessionctx/BUILD.bazel b/pkg/sessionctx/BUILD.bazel new file mode 100644 index 0000000000000..b18e2816cd015 --- /dev/null +++ b/pkg/sessionctx/BUILD.bazel @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sessionctx", + srcs = ["context.go"], + importpath = "github.com/pingcap/tidb/pkg/sessionctx", + visibility = ["//visibility:public"], + deps = [ + "//pkg/extension", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/kvcache", + "//pkg/util/plancache", + "//pkg/util/sli", + "//pkg/util/topsql/stmtstats", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_tikv_client_go_v2//oracle", + ], +) + +go_test( + name = "sessionctx_test", + timeout = "short", + srcs = [ + "context_test.go", + "main_test.go", + ], + embed = [":sessionctx"], + flaky = True, + race = "on", + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/sessionctx/binloginfo/BUILD.bazel b/pkg/sessionctx/binloginfo/BUILD.bazel new file mode 100644 index 0000000000000..0fed91dcb4bb5 --- /dev/null +++ b/pkg/sessionctx/binloginfo/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "binloginfo", + srcs = ["binloginfo.go"], + importpath = "github.com/pingcap/tidb/pkg/sessionctx/binloginfo", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/format", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/tidb-binlog/node", + "//pkg/tidb-binlog/pump_client", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_tipb//go-binlog", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "binloginfo_test", + timeout = "short", + srcs = [ + "binloginfo_test.go", + "main_test.go", + ], + embed = [":binloginfo"], + flaky = True, + shard_count = 11, + deps = [ + "//pkg/autoid_service", + "//pkg/ddl", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/tidb-binlog/pump_client", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_stretchr_testify//require", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/sessionctx/binloginfo/binloginfo.go b/pkg/sessionctx/binloginfo/binloginfo.go similarity index 95% rename from sessionctx/binloginfo/binloginfo.go rename to pkg/sessionctx/binloginfo/binloginfo.go index 81d449cc1972c..433384fce36f0 100644 --- a/sessionctx/binloginfo/binloginfo.go +++ b/pkg/sessionctx/binloginfo/binloginfo.go @@ -21,16 +21,16 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/tidb-binlog/node" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-binlog" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/sessionctx/binloginfo/binloginfo_test.go b/pkg/sessionctx/binloginfo/binloginfo_test.go similarity index 97% rename from sessionctx/binloginfo/binloginfo_test.go rename to pkg/sessionctx/binloginfo/binloginfo_test.go index 2c77f8b5d66d7..5fa7da2039163 100644 --- a/sessionctx/binloginfo/binloginfo_test.go +++ b/pkg/sessionctx/binloginfo/binloginfo_test.go @@ -26,22 +26,22 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tipb/go-binlog" "github.com/stretchr/testify/require" "google.golang.org/grpc" @@ -83,7 +83,7 @@ type binlogSuite struct { const maxRecvMsgSize = 64 * 1024 func createBinlogSuite(t *testing.T) (s *binlogSuite) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/driver/txn/mockSyncBinlogCommit", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/driver/txn/mockSyncBinlogCommit", `return(true)`)) s = new(binlogSuite) store := testkit.CreateMockStore(t) @@ -121,7 +121,7 @@ func createBinlogSuite(t *testing.T) (s *binlogSuite) { if err != nil { require.EqualError(t, err, fmt.Sprintf("remove %v: no such file or directory", unixFile)) } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/driver/txn/mockSyncBinlogCommit")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/driver/txn/mockSyncBinlogCommit")) }) return diff --git a/pkg/sessionctx/binloginfo/main_test.go b/pkg/sessionctx/binloginfo/main_test.go new file mode 100644 index 0000000000000..decd2ab61dc0f --- /dev/null +++ b/pkg/sessionctx/binloginfo/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package binloginfo + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/sessionctx/context.go b/pkg/sessionctx/context.go new file mode 100644 index 0000000000000..15e646e799618 --- /dev/null +++ b/pkg/sessionctx/context.go @@ -0,0 +1,266 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package sessionctx + +import ( + "context" + "fmt" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/kvcache" + utilpc "github.com/pingcap/tidb/pkg/util/plancache" + "github.com/pingcap/tidb/pkg/util/sli" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "github.com/pingcap/tipb/go-binlog" + "github.com/tikv/client-go/v2/oracle" +) + +// InfoschemaMetaVersion is a workaround. Due to circular dependency, +// can not return the complete interface. But SchemaMetaVersion is widely used for logging. +// So we give a convenience for that. +// FIXME: remove this interface +type InfoschemaMetaVersion interface { + SchemaMetaVersion() int64 +} + +// SessionStatesHandler is an interface for encoding and decoding session states. +type SessionStatesHandler interface { + // EncodeSessionStates encodes session states into a JSON. + EncodeSessionStates(context.Context, Context, *sessionstates.SessionStates) error + // DecodeSessionStates decodes a map into session states. + DecodeSessionStates(context.Context, Context, *sessionstates.SessionStates) error +} + +// PlanCache is an interface for prepare and non-prepared plan cache +type PlanCache interface { + Get(key kvcache.Key, opts *utilpc.PlanCacheMatchOpts) (value kvcache.Value, ok bool) + Put(key kvcache.Key, value kvcache.Value, opts *utilpc.PlanCacheMatchOpts) + Delete(key kvcache.Key) + DeleteAll() + Size() int + SetCapacity(capacity uint) error + Close() +} + +// Context is an interface for transaction and executive args environment. +type Context interface { + SessionStatesHandler + // SetDiskFullOpt set the disk full opt when tikv disk full happened. + SetDiskFullOpt(level kvrpcpb.DiskFullOpt) + // RollbackTxn rolls back the current transaction. + RollbackTxn(ctx context.Context) + // CommitTxn commits the current transaction. + CommitTxn(ctx context.Context) error + // Txn returns the current transaction which is created before executing a statement. + // The returned kv.Transaction is not nil, but it maybe pending or invalid. + // If the active parameter is true, call this function will wait for the pending txn + // to become valid. + Txn(active bool) (kv.Transaction, error) + + // GetClient gets a kv.Client. + GetClient() kv.Client + + // GetMPPClient gets a kv.MPPClient. + GetMPPClient() kv.MPPClient + + // SetValue saves a value associated with this context for key. + SetValue(key fmt.Stringer, value interface{}) + + // Value returns the value associated with this context for key. + Value(key fmt.Stringer) interface{} + + // ClearValue clears the value associated with this context for key. + ClearValue(key fmt.Stringer) + + // Deprecated: the semantics of session.GetInfoSchema() is ambiguous + // If you want to get the infoschema of the current transaction in SQL layer, use sessiontxn.GetTxnManager(ctx).GetTxnInfoSchema() + // If you want to get the latest infoschema use `GetDomainInfoSchema` + GetInfoSchema() InfoschemaMetaVersion + + // GetDomainInfoSchema returns the latest information schema in domain + // Different with `domain.InfoSchema()`, the information schema returned by this method + // includes the temporary table definitions stored in session + GetDomainInfoSchema() InfoschemaMetaVersion + + GetSessionVars() *variable.SessionVars + + GetSessionManager() util.SessionManager + + // RefreshTxnCtx commits old transaction without retry, + // and creates a new transaction. + // now just for load data and batch insert. + RefreshTxnCtx(context.Context) error + + // GetStore returns the store of session. + GetStore() kv.Storage + + // GetSessionPlanCache returns the session-level cache of the physical plan. + GetSessionPlanCache() PlanCache + + // UpdateColStatsUsage updates the column stats usage. + UpdateColStatsUsage(predicateColumns []model.TableItemID) + + // HasDirtyContent checks whether there's dirty update on the given table. + HasDirtyContent(tid int64) bool + + // StmtCommit flush all changes by the statement to the underlying transaction. + StmtCommit(ctx context.Context) + // StmtRollback provides statement level rollback. The parameter `forPessimisticRetry` should be true iff it's used + // for auto-retrying execution of DMLs in pessimistic transactions. + StmtRollback(ctx context.Context, isForPessimisticRetry bool) + // StmtGetMutation gets the binlog mutation for current statement. + StmtGetMutation(int64) *binlog.TableMutation + // IsDDLOwner checks whether this session is DDL owner. + IsDDLOwner() bool + // AddTableLock adds table lock to the session lock map. + AddTableLock([]model.TableLockTpInfo) + // ReleaseTableLocks releases table locks in the session lock map. + ReleaseTableLocks(locks []model.TableLockTpInfo) + // ReleaseTableLockByTableIDs releases table locks in the session lock map by table IDs. + ReleaseTableLockByTableIDs(tableIDs []int64) + // CheckTableLocked checks the table lock. + CheckTableLocked(tblID int64) (bool, model.TableLockType) + // GetAllTableLocks gets all table locks table id and db id hold by the session. + GetAllTableLocks() []model.TableLockTpInfo + // ReleaseAllTableLocks releases all table locks hold by the session. + ReleaseAllTableLocks() + // HasLockedTables uses to check whether this session locked any tables. + HasLockedTables() bool + // PrepareTSFuture uses to prepare timestamp by future. + PrepareTSFuture(ctx context.Context, future oracle.Future, scope string) error + // GetPreparedTxnFuture returns the TxnFuture if it is valid or pending. + // It returns nil otherwise. + GetPreparedTxnFuture() TxnFuture + // StoreIndexUsage stores the index usage information. + StoreIndexUsage(tblID int64, idxID int64, rowsSelected int64) + // GetTxnWriteThroughputSLI returns the TxnWriteThroughputSLI. + GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI + // GetBuiltinFunctionUsage returns the BuiltinFunctionUsage of current Context, which is not thread safe. + // Use primitive map type to prevent circular import. Should convert it to telemetry.BuiltinFunctionUsage before using. + GetBuiltinFunctionUsage() map[string]uint32 + // BuiltinFunctionUsageInc increase the counting of each builtin function usage + // Notice that this is a thread safe function + BuiltinFunctionUsageInc(scalarFuncSigName string) + // GetStmtStats returns stmtstats.StatementStats owned by implementation. + GetStmtStats() *stmtstats.StatementStats + // ShowProcess returns ProcessInfo running in current Context + ShowProcess() *util.ProcessInfo + // GetAdvisoryLock acquires an advisory lock (aka GET_LOCK()). + GetAdvisoryLock(string, int64) error + // IsUsedAdvisoryLock checks for existing locks (aka IS_USED_LOCK()). + IsUsedAdvisoryLock(string) uint64 + // ReleaseAdvisoryLock releases an advisory lock (aka RELEASE_LOCK()). + ReleaseAdvisoryLock(string) bool + // ReleaseAllAdvisoryLocks releases all advisory locks that this session holds. + ReleaseAllAdvisoryLocks() int + // GetExtensions returns the `*extension.SessionExtensions` object + GetExtensions() *extension.SessionExtensions + // InSandBoxMode indicates that this Session is in sandbox mode + // Ref about sandbox mode: https://dev.mysql.com/doc/refman/8.0/en/expired-password-handling.html + InSandBoxMode() bool + // EnableSandBoxMode enable the sandbox mode of this Session + EnableSandBoxMode() + // DisableSandBoxMode enable the sandbox mode of this Session + DisableSandBoxMode() +} + +// TxnFuture is an interface where implementations have a kv.Transaction field and after +// calling Wait of the TxnFuture, the kv.Transaction will become valid. +type TxnFuture interface { + // Wait converts pending txn to valid + Wait(ctx context.Context, sctx Context) (kv.Transaction, error) +} + +type basicCtxType int + +func (t basicCtxType) String() string { + switch t { + case QueryString: + return "query_string" + case Initing: + return "initing" + case LastExecuteDDL: + return "last_execute_ddl" + } + return "unknown" +} + +// Context keys. +const ( + // QueryString is the key for original query string. + QueryString basicCtxType = 1 + // Initing is the key for indicating if the server is running bootstrap or upgrade job. + Initing basicCtxType = 2 + // LastExecuteDDL is the key for whether the session execute a ddl command last time. + LastExecuteDDL basicCtxType = 3 +) + +// ValidateSnapshotReadTS strictly validates that readTS does not exceed the PD timestamp +func ValidateSnapshotReadTS(ctx context.Context, sctx Context, readTS uint64) error { + latestTS, err := sctx.GetStore().GetOracle().GetLowResolutionTimestamp(ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) + // If we fail to get latestTS or the readTS exceeds it, get a timestamp from PD to double check + if err != nil || readTS > latestTS { + metrics.ValidateReadTSFromPDCount.Inc() + currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) + if err != nil { + return errors.Errorf("fail to validate read timestamp: %v", err) + } + if readTS > currentVer.Ver { + return errors.Errorf("cannot set read timestamp to a future time") + } + } + return nil +} + +// How far future from now ValidateStaleReadTS allows at most +const allowedTimeFromNow = 100 * time.Millisecond + +// ValidateStaleReadTS validates that readTS does not exceed the current time not strictly. +func ValidateStaleReadTS(ctx context.Context, sctx Context, readTS uint64) error { + currentTS, err := sctx.GetSessionVars().StmtCtx.GetStaleTSO() + if currentTS == 0 || err != nil { + currentTS, err = sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) + } + // If we fail to calculate currentTS from local time, fallback to get a timestamp from PD + if err != nil { + metrics.ValidateReadTSFromPDCount.Inc() + currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) + if err != nil { + return errors.Errorf("fail to validate read timestamp: %v", err) + } + currentTS = currentVer.Ver + } + if oracle.GetTimeFromTS(readTS).After(oracle.GetTimeFromTS(currentTS).Add(allowedTimeFromNow)) { + return errors.Errorf("cannot set read timestamp to a future time") + } + return nil +} + +// SysProcTracker is used to track background sys processes +type SysProcTracker interface { + Track(id uint64, proc Context) error + UnTrack(id uint64) + GetSysProcessList() map[uint64]*util.ProcessInfo + KillSysProcess(id uint64) +} diff --git a/sessionctx/context_test.go b/pkg/sessionctx/context_test.go similarity index 100% rename from sessionctx/context_test.go rename to pkg/sessionctx/context_test.go diff --git a/pkg/sessionctx/main_test.go b/pkg/sessionctx/main_test.go new file mode 100644 index 0000000000000..45d1a23c66737 --- /dev/null +++ b/pkg/sessionctx/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package sessionctx + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/sessionctx/sessionstates/BUILD.bazel b/pkg/sessionctx/sessionstates/BUILD.bazel new file mode 100644 index 0000000000000..116e3555871d7 --- /dev/null +++ b/pkg/sessionctx/sessionstates/BUILD.bazel @@ -0,0 +1,51 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sessionstates", + srcs = [ + "session_states.go", + "session_token.go", + ], + importpath = "github.com/pingcap/tidb/pkg/sessionctx/sessionstates", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/parser/model", + "//pkg/parser/types", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "sessionstates_test", + timeout = "short", + srcs = [ + "session_states_test.go", + "session_token_test.go", + ], + embed = [":sessionstates"], + flaky = True, + shard_count = 15, + deps = [ + "//pkg/config", + "//pkg/errno", + "//pkg/expression", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/server", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/util", + "//pkg/util/sem", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + ], +) diff --git a/sessionctx/sessionstates/session_states.go b/pkg/sessionctx/sessionstates/session_states.go similarity index 93% rename from sessionctx/sessionstates/session_states.go rename to pkg/sessionctx/sessionstates/session_states.go index d39dd89643ac2..833c0c3b9c09d 100644 --- a/sessionctx/sessionstates/session_states.go +++ b/pkg/sessionctx/sessionstates/session_states.go @@ -15,12 +15,12 @@ package sessionstates import ( - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - ptypes "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + ptypes "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" ) // SessionStateType is the type of session states. diff --git a/sessionctx/sessionstates/session_states_test.go b/pkg/sessionctx/sessionstates/session_states_test.go similarity index 99% rename from sessionctx/sessionstates/session_states_test.go rename to pkg/sessionctx/sessionstates/session_states_test.go index e3eaf89dd3ef9..b1257775952f2 100644 --- a/sessionctx/sessionstates/session_states_test.go +++ b/pkg/sessionctx/sessionstates/session_states_test.go @@ -23,17 +23,17 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/sem" "github.com/stretchr/testify/require" ) diff --git a/sessionctx/sessionstates/session_token.go b/pkg/sessionctx/sessionstates/session_token.go similarity index 99% rename from sessionctx/sessionstates/session_token.go rename to pkg/sessionctx/sessionstates/session_token.go index 3724a7aafa853..5702d4966f118 100644 --- a/sessionctx/sessionstates/session_token.go +++ b/pkg/sessionctx/sessionstates/session_token.go @@ -31,7 +31,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/sessionctx/sessionstates/session_token_test.go b/pkg/sessionctx/sessionstates/session_token_test.go similarity index 98% rename from sessionctx/sessionstates/session_token_test.go rename to pkg/sessionctx/sessionstates/session_token_test.go index d55f9537d3106..42d4230d390a4 100644 --- a/sessionctx/sessionstates/session_token_test.go +++ b/pkg/sessionctx/sessionstates/session_token_test.go @@ -23,12 +23,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) var ( - mockNowOffset = "github.com/pingcap/tidb/sessionctx/sessionstates/mockNowOffset" + mockNowOffset = "github.com/pingcap/tidb/pkg/sessionctx/sessionstates/mockNowOffset" ) func TestSetCertAndKey(t *testing.T) { diff --git a/pkg/sessionctx/stmtctx/BUILD.bazel b/pkg/sessionctx/stmtctx/BUILD.bazel new file mode 100644 index 0000000000000..feae45e7a227a --- /dev/null +++ b/pkg/sessionctx/stmtctx/BUILD.bazel @@ -0,0 +1,58 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stmtctx", + srcs = ["stmtctx.go"], + importpath = "github.com/pingcap/tidb/pkg/sessionctx/stmtctx", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/types/context", + "//pkg/util/disk", + "//pkg/util/execdetails", + "//pkg/util/intest", + "//pkg/util/memory", + "//pkg/util/nocopy", + "//pkg/util/resourcegrouptag", + "//pkg/util/topsql/stmtstats", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//util", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "stmtctx_test", + timeout = "short", + srcs = [ + "main_test.go", + "stmtctx_test.go", + ], + embed = [":stmtctx"], + flaky = True, + shard_count = 10, + deps = [ + "//pkg/kv", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/types/context", + "//pkg/util/execdetails", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/sessionctx/stmtctx/main_test.go b/pkg/sessionctx/stmtctx/main_test.go new file mode 100644 index 0000000000000..d714954969ec5 --- /dev/null +++ b/pkg/sessionctx/stmtctx/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package stmtctx + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/sessionctx/stmtctx/stmtctx.go b/pkg/sessionctx/stmtctx/stmtctx.go similarity index 98% rename from sessionctx/stmtctx/stmtctx.go rename to pkg/sessionctx/stmtctx/stmtctx.go index 083fa27db8be3..5e612eb4320a4 100644 --- a/sessionctx/stmtctx/stmtctx.go +++ b/pkg/sessionctx/stmtctx/stmtctx.go @@ -28,22 +28,22 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - typectx "github.com/pingcap/tidb/types/context" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/nocopy" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + typectx "github.com/pingcap/tidb/pkg/types/context" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/nocopy" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/tikv/client-go/v2/tikvrpc" "github.com/tikv/client-go/v2/util" atomic2 "go.uber.org/atomic" diff --git a/sessionctx/stmtctx/stmtctx_test.go b/pkg/sessionctx/stmtctx/stmtctx_test.go similarity index 97% rename from sessionctx/stmtctx/stmtctx_test.go rename to pkg/sessionctx/stmtctx/stmtctx_test.go index a9d03bcc42429..c9f986abf808c 100644 --- a/sessionctx/stmtctx/stmtctx_test.go +++ b/pkg/sessionctx/stmtctx/stmtctx_test.go @@ -25,13 +25,13 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - typectx "github.com/pingcap/tidb/types/context" - "github.com/pingcap/tidb/util/execdetails" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + typectx "github.com/pingcap/tidb/pkg/types/context" + "github.com/pingcap/tidb/pkg/util/execdetails" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" "go.uber.org/atomic" diff --git a/pkg/sessionctx/variable/BUILD.bazel b/pkg/sessionctx/variable/BUILD.bazel new file mode 100644 index 0000000000000..cf4cca28db875 --- /dev/null +++ b/pkg/sessionctx/variable/BUILD.bazel @@ -0,0 +1,125 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "variable", + srcs = [ + "error.go", + "mock_globalaccessor.go", + "noop.go", + "removed.go", + "sequence_state.go", + "session.go", + "setvar_affect.go", + "statusvar.go", + "sysvar.go", + "tidb_vars.go", + "variable.go", + "varsutil.go", + ], + importpath = "github.com/pingcap/tidb/pkg/sessionctx/variable", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/keyspace", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/planner/util/fixcontrol", + "//pkg/privilege/privileges/ldap", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable/featuretag/disttask", + "//pkg/tidb-binlog/pump_client", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/disk", + "//pkg/util/distrole", + "//pkg/util/execdetails", + "//pkg/util/gctuner", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/paging", + "//pkg/util/replayer", + "//pkg/util/rowcodec", + "//pkg/util/size", + "//pkg/util/stmtsummary/v2:stmtsummary", + "//pkg/util/stringutil", + "//pkg/util/tableutil", + "//pkg/util/tiflash", + "//pkg/util/tiflashcompute", + "//pkg/util/tikvutil", + "//pkg/util/timeutil", + "//pkg/util/tls", + "//pkg/util/topsql/state", + "//pkg/util/versioninfo", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//util", + "@com_github_twmb_murmur3//:murmur3", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "variable_test", + timeout = "short", + srcs = [ + "main_test.go", + "mock_globalaccessor_test.go", + "removed_test.go", + "session_test.go", + "statusvar_test.go", + "sysvar_test.go", + "variable_test.go", + "varsutil_test.go", + ], + embed = [":variable"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/execdetails", + "//pkg/util/gctuner", + "//pkg/util/memory", + "//pkg/util/mock", + "//pkg/util/timeutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/sessionctx/variable/OWNERS b/pkg/sessionctx/variable/OWNERS similarity index 100% rename from sessionctx/variable/OWNERS rename to pkg/sessionctx/variable/OWNERS diff --git a/pkg/sessionctx/variable/error.go b/pkg/sessionctx/variable/error.go new file mode 100644 index 0000000000000..a7ff35abdc702 --- /dev/null +++ b/pkg/sessionctx/variable/error.go @@ -0,0 +1,51 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package variable + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + pmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// Error instances. +var ( + errWarnDeprecatedSyntax = dbterror.ClassVariable.NewStd(mysql.ErrWarnDeprecatedSyntax) + ErrSnapshotTooOld = dbterror.ClassVariable.NewStd(mysql.ErrSnapshotTooOld) + ErrUnsupportedValueForVar = dbterror.ClassVariable.NewStd(mysql.ErrUnsupportedValueForVar) + ErrUnknownSystemVar = dbterror.ClassVariable.NewStd(mysql.ErrUnknownSystemVariable) + ErrIncorrectScope = dbterror.ClassVariable.NewStd(mysql.ErrIncorrectGlobalLocalVar) + ErrUnknownTimeZone = dbterror.ClassVariable.NewStd(mysql.ErrUnknownTimeZone) + ErrReadOnly = dbterror.ClassVariable.NewStd(mysql.ErrVariableIsReadonly) + ErrWrongValueForVar = dbterror.ClassVariable.NewStd(mysql.ErrWrongValueForVar) + ErrWrongTypeForVar = dbterror.ClassVariable.NewStd(mysql.ErrWrongTypeForVar) + ErrTruncatedWrongValue = dbterror.ClassVariable.NewStd(mysql.ErrTruncatedWrongValue) + ErrMaxPreparedStmtCountReached = dbterror.ClassVariable.NewStd(mysql.ErrMaxPreparedStmtCountReached) + ErrUnsupportedIsolationLevel = dbterror.ClassVariable.NewStd(mysql.ErrUnsupportedIsolationLevel) + errUnknownSystemVariable = dbterror.ClassVariable.NewStd(mysql.ErrUnknownSystemVariable) + errGlobalVariable = dbterror.ClassVariable.NewStd(mysql.ErrGlobalVariable) + errLocalVariable = dbterror.ClassVariable.NewStd(mysql.ErrLocalVariable) + errValueNotSupportedWhen = dbterror.ClassVariable.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("%s = OFF is not supported when %s = ON", nil)) + ErrStmtNotFound = dbterror.ClassOptimizer.NewStd(mysql.ErrPreparedStmtNotFound) + ErrNotValidPassword = dbterror.ClassExecutor.NewStd(mysql.ErrNotValidPassword) + // ErrFunctionsNoopImpl is an error to say the behavior is protected by the tidb_enable_noop_functions sysvar. + // This is copied from expression.ErrFunctionsNoopImpl to prevent circular dependencies. + // It needs to be public for tests. + ErrFunctionsNoopImpl = dbterror.ClassVariable.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("function %s has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", nil)) + ErrVariableNoLongerSupported = dbterror.ClassVariable.NewStd(mysql.ErrVariableNoLongerSupported) + ErrInvalidDefaultUTF8MB4Collation = dbterror.ClassVariable.NewStd(mysql.ErrInvalidDefaultUTF8MB4Collation) + ErrWarnDeprecatedSyntaxNoReplacement = dbterror.ClassVariable.NewStdErr(mysql.ErrWarnDeprecatedSyntaxNoReplacement, pmysql.Message("Updating '%s' is deprecated. It will be made read-only in a future release.", nil)) + ErrWarnDeprecatedSyntaxSimpleMsg = dbterror.ClassVariable.NewStdErr(mysql.ErrWarnDeprecatedSyntaxNoReplacement, pmysql.Message("%s is deprecated and will be removed in a future release.", nil)) +) diff --git a/pkg/sessionctx/variable/featuretag/disttask/BUILD.bazel b/pkg/sessionctx/variable/featuretag/disttask/BUILD.bazel new file mode 100644 index 0000000000000..5fbbb215e7b08 --- /dev/null +++ b/pkg/sessionctx/variable/featuretag/disttask/BUILD.bazel @@ -0,0 +1,11 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "disttask", + srcs = [ + "default.go", + "non_default.go", #keep + ], + importpath = "github.com/pingcap/tidb/pkg/sessionctx/variable/featuretag/disttask", + visibility = ["//visibility:public"], +) diff --git a/sessionctx/variable/featuretag/disttask/default.go b/pkg/sessionctx/variable/featuretag/disttask/default.go similarity index 100% rename from sessionctx/variable/featuretag/disttask/default.go rename to pkg/sessionctx/variable/featuretag/disttask/default.go diff --git a/sessionctx/variable/featuretag/disttask/non_default.go b/pkg/sessionctx/variable/featuretag/disttask/non_default.go similarity index 100% rename from sessionctx/variable/featuretag/disttask/non_default.go rename to pkg/sessionctx/variable/featuretag/disttask/non_default.go diff --git a/pkg/sessionctx/variable/main_test.go b/pkg/sessionctx/variable/main_test.go new file mode 100644 index 0000000000000..7fdcaff28c86e --- /dev/null +++ b/pkg/sessionctx/variable/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/sessionctx/variable/mock_globalaccessor.go b/pkg/sessionctx/variable/mock_globalaccessor.go similarity index 100% rename from sessionctx/variable/mock_globalaccessor.go rename to pkg/sessionctx/variable/mock_globalaccessor.go diff --git a/sessionctx/variable/mock_globalaccessor_test.go b/pkg/sessionctx/variable/mock_globalaccessor_test.go similarity index 100% rename from sessionctx/variable/mock_globalaccessor_test.go rename to pkg/sessionctx/variable/mock_globalaccessor_test.go diff --git a/sessionctx/variable/noop.go b/pkg/sessionctx/variable/noop.go similarity index 100% rename from sessionctx/variable/noop.go rename to pkg/sessionctx/variable/noop.go diff --git a/sessionctx/variable/removed.go b/pkg/sessionctx/variable/removed.go similarity index 100% rename from sessionctx/variable/removed.go rename to pkg/sessionctx/variable/removed.go diff --git a/sessionctx/variable/removed_test.go b/pkg/sessionctx/variable/removed_test.go similarity index 100% rename from sessionctx/variable/removed_test.go rename to pkg/sessionctx/variable/removed_test.go diff --git a/sessionctx/variable/sequence_state.go b/pkg/sessionctx/variable/sequence_state.go similarity index 100% rename from sessionctx/variable/sequence_state.go rename to pkg/sessionctx/variable/sequence_state.go diff --git a/pkg/sessionctx/variable/session.go b/pkg/sessionctx/variable/session.go new file mode 100644 index 0000000000000..4f4ff2d2b5afa --- /dev/null +++ b/pkg/sessionctx/variable/session.go @@ -0,0 +1,3659 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/binary" + "encoding/json" + "fmt" + "math" + "math/rand" + "net" + "slices" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + ptypes "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + pumpcli "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/replayer" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tableutil" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/util/timeutil" + tikvstore "github.com/tikv/client-go/v2/kv" + "github.com/tikv/client-go/v2/tikv" + "github.com/twmb/murmur3" + atomic2 "go.uber.org/atomic" + "golang.org/x/exp/maps" +) + +var ( + // PreparedStmtCount is exported for test. + PreparedStmtCount int64 + // enableAdaptiveReplicaRead indicates whether closest adaptive replica read + // can be enabled. We forces disable replica read when tidb server in missing + // in regions that contains tikv server to avoid read traffic skew. + enableAdaptiveReplicaRead uint32 = 1 +) + +// ConnStatusShutdown indicates that the connection status is closed by server. +// This code is put here because of package imports, and this value is the original server.connStatusShutdown. +const ConnStatusShutdown int32 = 2 + +// SetEnableAdaptiveReplicaRead set `enableAdaptiveReplicaRead` with given value. +// return true if the value is changed. +func SetEnableAdaptiveReplicaRead(enabled bool) bool { + value := uint32(0) + if enabled { + value = 1 + } + return atomic.SwapUint32(&enableAdaptiveReplicaRead, value) != value +} + +// IsAdaptiveReplicaReadEnabled returns whether adaptive closest replica read can be enabled. +func IsAdaptiveReplicaReadEnabled() bool { + return atomic.LoadUint32(&enableAdaptiveReplicaRead) > 0 +} + +// RetryInfo saves retry information. +type RetryInfo struct { + Retrying bool + DroppedPreparedStmtIDs []uint32 + autoIncrementIDs retryInfoAutoIDs + autoRandomIDs retryInfoAutoIDs + LastRcReadTS uint64 +} + +// ReuseChunkPool save Alloc object +type ReuseChunkPool struct { + mu sync.Mutex + Alloc chunk.Allocator +} + +// Clean does some clean work. +func (r *RetryInfo) Clean() { + r.autoIncrementIDs.clean() + r.autoRandomIDs.clean() + + if len(r.DroppedPreparedStmtIDs) > 0 { + r.DroppedPreparedStmtIDs = r.DroppedPreparedStmtIDs[:0] + } +} + +// ResetOffset resets the current retry offset. +func (r *RetryInfo) ResetOffset() { + r.autoIncrementIDs.resetOffset() + r.autoRandomIDs.resetOffset() +} + +// AddAutoIncrementID adds id to autoIncrementIDs. +func (r *RetryInfo) AddAutoIncrementID(id int64) { + r.autoIncrementIDs.autoIDs = append(r.autoIncrementIDs.autoIDs, id) +} + +// GetCurrAutoIncrementID gets current autoIncrementID. +func (r *RetryInfo) GetCurrAutoIncrementID() (int64, bool) { + return r.autoIncrementIDs.getCurrent() +} + +// AddAutoRandomID adds id to autoRandomIDs. +func (r *RetryInfo) AddAutoRandomID(id int64) { + r.autoRandomIDs.autoIDs = append(r.autoRandomIDs.autoIDs, id) +} + +// GetCurrAutoRandomID gets current AutoRandomID. +func (r *RetryInfo) GetCurrAutoRandomID() (int64, bool) { + return r.autoRandomIDs.getCurrent() +} + +type retryInfoAutoIDs struct { + currentOffset int + autoIDs []int64 +} + +func (r *retryInfoAutoIDs) resetOffset() { + r.currentOffset = 0 +} + +func (r *retryInfoAutoIDs) clean() { + r.currentOffset = 0 + if len(r.autoIDs) > 0 { + r.autoIDs = r.autoIDs[:0] + } +} + +func (r *retryInfoAutoIDs) getCurrent() (int64, bool) { + if r.currentOffset >= len(r.autoIDs) { + return 0, false + } + id := r.autoIDs[r.currentOffset] + r.currentOffset++ + return id, true +} + +// TransactionContext is used to store variables that has transaction scope. +type TransactionContext struct { + TxnCtxNoNeedToRestore + TxnCtxNeedToRestore +} + +// TxnCtxNeedToRestore stores transaction variables which need to be restored when rolling back to a savepoint. +type TxnCtxNeedToRestore struct { + // TableDeltaMap is used in the schema validator for DDL changes in one table not to block others. + // It's also used in the statistics updating. + // Note: for the partitioned table, it stores all the partition IDs. + TableDeltaMap map[int64]TableDelta + + // pessimisticLockCache is the cache for pessimistic locked keys, + // The value never changes during the transaction. + pessimisticLockCache map[string][]byte + + // CachedTables is not nil if the transaction write on cached table. + CachedTables map[int64]interface{} + + // InsertTTLRowsCount counts how many rows are inserted in this statement + InsertTTLRowsCount int +} + +// TxnCtxNoNeedToRestore stores transaction variables which do not need to restored when rolling back to a savepoint. +type TxnCtxNoNeedToRestore struct { + forUpdateTS uint64 + Binlog interface{} + InfoSchema interface{} + History interface{} + StartTS uint64 + + // ShardStep indicates the max size of continuous rowid shard in one transaction. + ShardStep int + shardRemain int + currentShard int64 + + // unchangedKeys is used to store the unchanged keys that needs to lock for pessimistic transaction. + unchangedKeys map[string]struct{} + + PessimisticCacheHit int + + // CreateTime For metrics. + CreateTime time.Time + StatementCount int + CouldRetry bool + IsPessimistic bool + // IsStaleness indicates whether the txn is read only staleness txn. + IsStaleness bool + // IsExplicit indicates whether the txn is an interactive txn, which is typically started with a BEGIN + // or START TRANSACTION statement, or by setting autocommit to 0. + IsExplicit bool + Isolation string + LockExpire uint32 + ForUpdate uint32 + // TxnScope indicates the value of txn_scope + TxnScope string + + // Savepoints contains all definitions of the savepoint of a transaction at runtime, the order of the SavepointRecord is the same with the SAVEPOINT statements. + // It is used for a lookup when running `ROLLBACK TO` statement. + Savepoints []SavepointRecord + + // TableDeltaMap lock to prevent potential data race + tdmLock sync.Mutex + + // TemporaryTables is used to store transaction-specific information for global temporary tables. + // It can also be stored in sessionCtx with local temporary tables, but it's easier to clean this data after transaction ends. + TemporaryTables map[int64]tableutil.TempTable + // EnableMDL indicates whether to enable the MDL lock for the transaction. + EnableMDL bool + // relatedTableForMDL records the `lock` table for metadata lock. It maps from int64 to int64(version). + relatedTableForMDL *sync.Map + + // FairLockingUsed marking whether at least one of the statements in the transaction was executed in + // fair locking mode. + FairLockingUsed bool + // FairLockingEffective marking whether at least one of the statements in the transaction was executed in + // fair locking mode, and it takes effect (which is determined according to whether lock-with-conflict + // has occurred during execution of any statement). + FairLockingEffective bool + + // CurrentStmtPessimisticLockCache is the cache for pessimistic locked keys in the current statement. + // It is merged into `pessimisticLockCache` after a statement finishes. + // Read results cannot be directly written into pessimisticLockCache because failed statement need to rollback + // its pessimistic locks. + CurrentStmtPessimisticLockCache map[string][]byte +} + +// SavepointRecord indicates a transaction's savepoint record. +type SavepointRecord struct { + // name is the name of the savepoint + Name string + // MemDBCheckpoint is the transaction's memdb checkpoint. + MemDBCheckpoint *tikv.MemDBCheckpoint + // TxnCtxSavepoint is the savepoint of TransactionContext + TxnCtxSavepoint TxnCtxNeedToRestore +} + +// GetCurrentShard returns the shard for the next `count` IDs. +func (s *SessionVars) GetCurrentShard(count int) int64 { + tc := s.TxnCtx + if s.shardRand == nil { + s.shardRand = rand.New(rand.NewSource(int64(tc.StartTS))) // #nosec G404 + } + if tc.shardRemain <= 0 { + tc.updateShard(s.shardRand) + tc.shardRemain = tc.ShardStep + } + tc.shardRemain -= count + return tc.currentShard +} + +func (tc *TransactionContext) updateShard(shardRand *rand.Rand) { + var buf [8]byte + binary.LittleEndian.PutUint64(buf[:], shardRand.Uint64()) + tc.currentShard = int64(murmur3.Sum32(buf[:])) +} + +// AddUnchangedKeyForLock adds an unchanged key for pessimistic lock. +func (tc *TransactionContext) AddUnchangedKeyForLock(key []byte) { + if tc.unchangedKeys == nil { + tc.unchangedKeys = map[string]struct{}{} + } + tc.unchangedKeys[string(key)] = struct{}{} +} + +// CollectUnchangedKeysForLock collects unchanged keys for pessimistic lock. +func (tc *TransactionContext) CollectUnchangedKeysForLock(buf []kv.Key) []kv.Key { + for key := range tc.unchangedKeys { + buf = append(buf, kv.Key(key)) + } + tc.unchangedKeys = nil + return buf +} + +// UpdateDeltaForTable updates the delta info for some table. +func (tc *TransactionContext) UpdateDeltaForTable(physicalTableID int64, delta int64, count int64, colSize map[int64]int64) { + tc.tdmLock.Lock() + defer tc.tdmLock.Unlock() + if tc.TableDeltaMap == nil { + tc.TableDeltaMap = make(map[int64]TableDelta) + } + item := tc.TableDeltaMap[physicalTableID] + if item.ColSize == nil && colSize != nil { + item.ColSize = make(map[int64]int64, len(colSize)) + } + item.Delta += delta + item.Count += count + item.TableID = physicalTableID + for key, val := range colSize { + item.ColSize[key] += val + } + tc.TableDeltaMap[physicalTableID] = item +} + +// GetKeyInPessimisticLockCache gets a key in pessimistic lock cache. +func (tc *TransactionContext) GetKeyInPessimisticLockCache(key kv.Key) (val []byte, ok bool) { + if tc.pessimisticLockCache == nil && tc.CurrentStmtPessimisticLockCache == nil { + return nil, false + } + if tc.CurrentStmtPessimisticLockCache != nil { + val, ok = tc.CurrentStmtPessimisticLockCache[string(key)] + if ok { + tc.PessimisticCacheHit++ + return + } + } + if tc.pessimisticLockCache != nil { + val, ok = tc.pessimisticLockCache[string(key)] + if ok { + tc.PessimisticCacheHit++ + } + } + return +} + +// SetPessimisticLockCache sets a key value pair in pessimistic lock cache. +// The value is buffered in the statement cache until the current statement finishes. +func (tc *TransactionContext) SetPessimisticLockCache(key kv.Key, val []byte) { + if tc.CurrentStmtPessimisticLockCache == nil { + tc.CurrentStmtPessimisticLockCache = make(map[string][]byte) + } + tc.CurrentStmtPessimisticLockCache[string(key)] = val +} + +// Cleanup clears up transaction info that no longer use. +func (tc *TransactionContext) Cleanup() { + // tc.InfoSchema = nil; we cannot do it now, because some operation like handleFieldList depend on this. + tc.Binlog = nil + tc.History = nil + tc.tdmLock.Lock() + tc.TableDeltaMap = nil + tc.relatedTableForMDL = nil + tc.tdmLock.Unlock() + tc.pessimisticLockCache = nil + tc.CurrentStmtPessimisticLockCache = nil + tc.IsStaleness = false + tc.Savepoints = nil + tc.EnableMDL = false +} + +// ClearDelta clears the delta map. +func (tc *TransactionContext) ClearDelta() { + tc.tdmLock.Lock() + tc.TableDeltaMap = nil + tc.tdmLock.Unlock() +} + +// GetForUpdateTS returns the ts for update. +func (tc *TransactionContext) GetForUpdateTS() uint64 { + if tc.forUpdateTS > tc.StartTS { + return tc.forUpdateTS + } + return tc.StartTS +} + +// SetForUpdateTS sets the ts for update. +func (tc *TransactionContext) SetForUpdateTS(forUpdateTS uint64) { + if forUpdateTS > tc.forUpdateTS { + tc.forUpdateTS = forUpdateTS + } +} + +// GetCurrentSavepoint gets TransactionContext's savepoint. +func (tc *TransactionContext) GetCurrentSavepoint() TxnCtxNeedToRestore { + tableDeltaMap := make(map[int64]TableDelta, len(tc.TableDeltaMap)) + for k, v := range tc.TableDeltaMap { + tableDeltaMap[k] = v.Clone() + } + pessimisticLockCache := make(map[string][]byte, len(tc.pessimisticLockCache)) + maps.Copy(pessimisticLockCache, tc.pessimisticLockCache) + CurrentStmtPessimisticLockCache := make(map[string][]byte, len(tc.CurrentStmtPessimisticLockCache)) + maps.Copy(CurrentStmtPessimisticLockCache, tc.CurrentStmtPessimisticLockCache) + cachedTables := make(map[int64]interface{}, len(tc.CachedTables)) + maps.Copy(cachedTables, tc.CachedTables) + return TxnCtxNeedToRestore{ + TableDeltaMap: tableDeltaMap, + pessimisticLockCache: pessimisticLockCache, + CachedTables: cachedTables, + InsertTTLRowsCount: tc.InsertTTLRowsCount, + } +} + +// RestoreBySavepoint restores TransactionContext to the specify savepoint. +func (tc *TransactionContext) RestoreBySavepoint(savepoint TxnCtxNeedToRestore) { + tc.TableDeltaMap = savepoint.TableDeltaMap + tc.pessimisticLockCache = savepoint.pessimisticLockCache + tc.CachedTables = savepoint.CachedTables + tc.InsertTTLRowsCount = savepoint.InsertTTLRowsCount +} + +// AddSavepoint adds a new savepoint. +func (tc *TransactionContext) AddSavepoint(name string, memdbCheckpoint *tikv.MemDBCheckpoint) { + name = strings.ToLower(name) + tc.DeleteSavepoint(name) + + record := SavepointRecord{ + Name: name, + MemDBCheckpoint: memdbCheckpoint, + TxnCtxSavepoint: tc.GetCurrentSavepoint(), + } + tc.Savepoints = append(tc.Savepoints, record) +} + +// DeleteSavepoint deletes the savepoint, return false indicate the savepoint name doesn't exists. +func (tc *TransactionContext) DeleteSavepoint(name string) bool { + name = strings.ToLower(name) + for i, sp := range tc.Savepoints { + if sp.Name == name { + tc.Savepoints = append(tc.Savepoints[:i], tc.Savepoints[i+1:]...) + return true + } + } + return false +} + +// ReleaseSavepoint deletes the named savepoint and the later savepoints, return false indicate the named savepoint doesn't exists. +func (tc *TransactionContext) ReleaseSavepoint(name string) bool { + name = strings.ToLower(name) + for i, sp := range tc.Savepoints { + if sp.Name == name { + tc.Savepoints = append(tc.Savepoints[:i]) + return true + } + } + return false +} + +// RollbackToSavepoint rollbacks to the specified savepoint by name. +func (tc *TransactionContext) RollbackToSavepoint(name string) *SavepointRecord { + name = strings.ToLower(name) + for idx, sp := range tc.Savepoints { + if name == sp.Name { + tc.RestoreBySavepoint(sp.TxnCtxSavepoint) + tc.Savepoints = tc.Savepoints[:idx+1] + return &tc.Savepoints[idx] + } + } + return nil +} + +// FlushStmtPessimisticLockCache merges the current statement pessimistic lock cache into transaction pessimistic lock +// cache. The caller may need to clear the stmt cache itself. +func (tc *TransactionContext) FlushStmtPessimisticLockCache() { + if tc.CurrentStmtPessimisticLockCache == nil { + return + } + if tc.pessimisticLockCache == nil { + tc.pessimisticLockCache = make(map[string][]byte) + } + for key, val := range tc.CurrentStmtPessimisticLockCache { + tc.pessimisticLockCache[key] = val + } + tc.CurrentStmtPessimisticLockCache = nil +} + +// WriteStmtBufs can be used by insert/replace/delete/update statement. +// TODO: use a common memory pool to replace this. +type WriteStmtBufs struct { + // RowValBuf is used by tablecodec.EncodeRow, to reduce runtime.growslice. + RowValBuf []byte + // AddRowValues use to store temp insert rows value, to reduce memory allocations when importing data. + AddRowValues []types.Datum + + // IndexValsBuf is used by index.FetchValues + IndexValsBuf []types.Datum + // IndexKeyBuf is used by index.GenIndexKey + IndexKeyBuf []byte +} + +func (ib *WriteStmtBufs) clean() { + ib.RowValBuf = nil + ib.AddRowValues = nil + ib.IndexValsBuf = nil + ib.IndexKeyBuf = nil +} + +// TableSnapshot represents a data snapshot of the table contained in `information_schema`. +type TableSnapshot struct { + Rows [][]types.Datum + Err error +} + +type txnIsolationLevelOneShotState uint + +// RewritePhaseInfo records some information about the rewrite phase +type RewritePhaseInfo struct { + // DurationRewrite is the duration of rewriting the SQL. + DurationRewrite time.Duration + + // DurationPreprocessSubQuery is the duration of pre-processing sub-queries. + DurationPreprocessSubQuery time.Duration + + // PreprocessSubQueries is the number of pre-processed sub-queries. + PreprocessSubQueries int +} + +// Reset resets all fields in RewritePhaseInfo. +func (r *RewritePhaseInfo) Reset() { + r.DurationRewrite = 0 + r.DurationPreprocessSubQuery = 0 + r.PreprocessSubQueries = 0 +} + +// TemporaryTableData is a interface to maintain temporary data in session +type TemporaryTableData interface { + kv.Retriever + // Staging create a new staging buffer inside the MemBuffer. + // Subsequent writes will be temporarily stored in this new staging buffer. + // When you think all modifications looks good, you can call `Release` to public all of them to the upper level buffer. + Staging() kv.StagingHandle + // Release publish all modifications in the latest staging buffer to upper level. + Release(kv.StagingHandle) + // Cleanup cleanups the resources referenced by the StagingHandle. + // If the changes are not published by `Release`, they will be discarded. + Cleanup(kv.StagingHandle) + // GetTableSize get the size of a table + GetTableSize(tblID int64) int64 + // DeleteTableKey removes the entry for key k from table + DeleteTableKey(tblID int64, k kv.Key) error + // SetTableKey sets the entry for k from table + SetTableKey(tblID int64, k kv.Key, val []byte) error +} + +// temporaryTableData is used for store temporary table data in session +type temporaryTableData struct { + kv.MemBuffer + tblSize map[int64]int64 +} + +// NewTemporaryTableData creates a new TemporaryTableData +func NewTemporaryTableData(memBuffer kv.MemBuffer) TemporaryTableData { + return &temporaryTableData{ + MemBuffer: memBuffer, + tblSize: make(map[int64]int64), + } +} + +// GetTableSize get the size of a table +func (d *temporaryTableData) GetTableSize(tblID int64) int64 { + if tblSize, ok := d.tblSize[tblID]; ok { + return tblSize + } + return 0 +} + +// DeleteTableKey removes the entry for key k from table +func (d *temporaryTableData) DeleteTableKey(tblID int64, k kv.Key) error { + bufferSize := d.MemBuffer.Size() + defer d.updateTblSize(tblID, bufferSize) + + return d.MemBuffer.Delete(k) +} + +// SetTableKey sets the entry for k from table +func (d *temporaryTableData) SetTableKey(tblID int64, k kv.Key, val []byte) error { + bufferSize := d.MemBuffer.Size() + defer d.updateTblSize(tblID, bufferSize) + + return d.MemBuffer.Set(k, val) +} + +func (d *temporaryTableData) updateTblSize(tblID int64, beforeSize int) { + delta := int64(d.MemBuffer.Size() - beforeSize) + d.tblSize[tblID] = d.GetTableSize(tblID) + delta +} + +const ( + // oneShotDef means default, that is tx_isolation_one_shot not set. + oneShotDef txnIsolationLevelOneShotState = iota + // oneShotSet means it's set in current transaction. + oneShotSet + // onsShotUse means it should be used in current transaction. + oneShotUse +) + +// ReadConsistencyLevel is the level of read consistency. +type ReadConsistencyLevel string + +const ( + // ReadConsistencyStrict means read by strict consistency, default value. + ReadConsistencyStrict ReadConsistencyLevel = "strict" + // ReadConsistencyWeak means read can be weak consistency. + ReadConsistencyWeak ReadConsistencyLevel = "weak" +) + +// IsWeak returns true only if it's a weak-consistency read. +func (r ReadConsistencyLevel) IsWeak() bool { + return r == ReadConsistencyWeak +} + +func validateReadConsistencyLevel(val string) error { + switch v := ReadConsistencyLevel(strings.ToLower(val)); v { + case ReadConsistencyStrict, ReadConsistencyWeak: + return nil + default: + return ErrWrongTypeForVar.GenWithStackByArgs(TiDBReadConsistency) + } +} + +// SetUserVarVal set user defined variables' value +func (s *SessionVars) SetUserVarVal(name string, dt types.Datum) { + s.userVars.lock.Lock() + defer s.userVars.lock.Unlock() + s.userVars.values[name] = dt +} + +// GetUserVarVal get user defined variables' value +func (s *SessionVars) GetUserVarVal(name string) (types.Datum, bool) { + s.userVars.lock.RLock() + defer s.userVars.lock.RUnlock() + dt, ok := s.userVars.values[name] + return dt, ok +} + +// SetUserVarType set user defined variables' type +func (s *SessionVars) SetUserVarType(name string, ft *types.FieldType) { + s.userVars.lock.Lock() + defer s.userVars.lock.Unlock() + s.userVars.types[name] = ft +} + +// GetUserVarType get user defined variables' type +func (s *SessionVars) GetUserVarType(name string) (*types.FieldType, bool) { + s.userVars.lock.RLock() + defer s.userVars.lock.RUnlock() + ft, ok := s.userVars.types[name] + return ft, ok +} + +// HookContext contains the necessary variables for executing set/get hook +type HookContext interface { + GetStore() kv.Storage +} + +// SessionVars is to handle user-defined or global variables in the current session. +type SessionVars struct { + Concurrency + MemQuota + BatchSize + // DMLBatchSize indicates the number of rows batch-committed for a statement. + // It will be used when using LOAD DATA or BatchInsert or BatchDelete is on. + DMLBatchSize int + RetryLimit int64 + DisableTxnAutoRetry bool + userVars struct { + // lock is for user defined variables. values and types is read/write protected. + lock sync.RWMutex + // values stores the Datum for user variables + values map[string]types.Datum + // types stores the FieldType for user variables, it cannot be inferred from values when values have not been set yet. + types map[string]*types.FieldType + } + // systems variables, don't modify it directly, use GetSystemVar/SetSystemVar method. + systems map[string]string + // stmtVars variables are temporarily set by SET_VAR hint + // It only take effect for the duration of a single statement + stmtVars map[string]string + // SysWarningCount is the system variable "warning_count", because it is on the hot path, so we extract it from the systems + SysWarningCount int + // SysErrorCount is the system variable "error_count", because it is on the hot path, so we extract it from the systems + SysErrorCount uint16 + // nonPreparedPlanCacheStmts stores PlanCacheStmts for non-prepared plan cache. + nonPreparedPlanCacheStmts *kvcache.SimpleLRUCache + // PreparedStmts stores prepared statement. + PreparedStmts map[uint32]interface{} + PreparedStmtNameToID map[string]uint32 + // preparedStmtID is id of prepared statement. + preparedStmtID uint32 + // Parameter values for plan cache. + PlanCacheParams *PlanCacheParamList + LastUpdateTime4PC types.Time + + // ActiveRoles stores active roles for current user + ActiveRoles []*auth.RoleIdentity + + RetryInfo *RetryInfo + // TxnCtx Should be reset on transaction finished. + TxnCtx *TransactionContext + // TxnCtxMu is used to protect TxnCtx. + TxnCtxMu sync.Mutex + + // TxnManager is used to manage txn context in session + TxnManager interface{} + + // KVVars is the variables for KV storage. + KVVars *tikvstore.Variables + + // txnIsolationLevelOneShot is used to implements "set transaction isolation level ..." + txnIsolationLevelOneShot struct { + state txnIsolationLevelOneShotState + value string + } + + // Status stands for the session status. e.g. in transaction or not, auto commit is on or off, and so on. + Status uint16 + + // ClientCapability is client's capability. + ClientCapability uint32 + + // TLSConnectionState is the TLS connection state (nil if not using TLS). + TLSConnectionState *tls.ConnectionState + + // ConnectionID is the connection id of the current session. + ConnectionID uint64 + + // PlanID is the unique id of logical and physical plan. + PlanID atomic.Int32 + + // PlanColumnID is the unique id for column when building plan. + PlanColumnID atomic.Int64 + + // MapScalarSubQ maps the scalar sub queries from its ID to its struct. + MapScalarSubQ []interface{} + + // MapHashCode2UniqueID4ExtendedCol map the expr's hash code to specified unique ID. + MapHashCode2UniqueID4ExtendedCol map[string]int + + // User is the user identity with which the session login. + User *auth.UserIdentity + + // Port is the port of the connected socket + Port string + + // CurrentDB is the default database of this session. + CurrentDB string + + // CurrentDBChanged indicates if the CurrentDB has been updated, and if it is we should print it into + // the slow log to make it be compatible with MySQL, https://github.com/pingcap/tidb/issues/17846. + CurrentDBChanged bool + + // StrictSQLMode indicates if the session is in strict mode. + StrictSQLMode bool + + // CommonGlobalLoaded indicates if common global variable has been loaded for this session. + CommonGlobalLoaded bool + + // InRestrictedSQL indicates if the session is handling restricted SQL execution. + InRestrictedSQL bool + + // SnapshotTS is used for reading history data. For simplicity, SnapshotTS only supports distsql request. + SnapshotTS uint64 + + // TxnReadTS is used for staleness transaction, it provides next staleness transaction startTS. + TxnReadTS *TxnReadTS + + // SnapshotInfoschema is used with SnapshotTS, when the schema version at snapshotTS less than current schema + // version, we load an old version schema for query. + SnapshotInfoschema interface{} + + // BinlogClient is used to write binlog. + BinlogClient *pumpcli.PumpsClient + + // GlobalVarsAccessor is used to set and get global variables. + GlobalVarsAccessor GlobalVarAccessor + + // LastFoundRows is the number of found rows of last query statement + LastFoundRows uint64 + + // StmtCtx holds variables for current executing statement. + StmtCtx *stmtctx.StatementContext + + // RefCountOfStmtCtx indicates the reference count of StmtCtx. When the + // StmtCtx is accessed by other sessions, e.g. oom-alarm-handler/expensive-query-handler, add one first. + // Note: this variable should be accessed and updated by atomic operations. + RefCountOfStmtCtx stmtctx.ReferenceCount + + // AllowAggPushDown can be set to false to forbid aggregation push down. + AllowAggPushDown bool + + // AllowDeriveTopN is used to enable/disable derived TopN optimization. + AllowDeriveTopN bool + + // AllowCartesianBCJ means allow broadcast CARTESIAN join, 0 means not allow, 1 means allow broadcast CARTESIAN join + // but the table size should under the broadcast threshold, 2 means allow broadcast CARTESIAN join even if the table + // size exceeds the broadcast threshold + AllowCartesianBCJ int + + // MPPOuterJoinFixedBuildSide means in MPP plan, always use right(left) table as build side for left(right) out join + MPPOuterJoinFixedBuildSide bool + + // AllowDistinctAggPushDown can be set true to allow agg with distinct push down to tikv/tiflash. + AllowDistinctAggPushDown bool + + // EnableSkewDistinctAgg can be set true to allow skew distinct aggregate rewrite + EnableSkewDistinctAgg bool + + // Enable3StageDistinctAgg indicates whether to allow 3 stage distinct aggregate + Enable3StageDistinctAgg bool + + // Enable3StageMultiDistinctAgg indicates whether to allow 3 stage multi distinct aggregate + Enable3StageMultiDistinctAgg bool + + ExplainNonEvaledSubQuery bool + + // MultiStatementMode permits incorrect client library usage. Not recommended to be turned on. + MultiStatementMode int + + // InMultiStmts indicates whether the statement is a multi-statement like `update t set a=1; update t set b=2;`. + InMultiStmts bool + + // AllowWriteRowID variable is currently not recommended to be turned on. + AllowWriteRowID bool + + // AllowBatchCop means if we should send batch coprocessor to TiFlash. Default value is 1, means to use batch cop in case of aggregation and join. + // Value set to 2 means to force to send batch cop for any query. Value set to 0 means never use batch cop. + AllowBatchCop int + + // allowMPPExecution means if we should use mpp way to execute query. + // Default value is `true`, means to be determined by the optimizer. + // Value set to `false` means never use mpp. + allowMPPExecution bool + + // allowTiFlashCop means if we must use mpp way to execute query. + // Default value is `false`, means to be determined by the optimizer. + // Value set to `true` means we may fall back to TiFlash cop if possible. + allowTiFlashCop bool + + // HashExchangeWithNewCollation means if we support hash exchange when new collation is enabled. + // Default value is `true`, means support hash exchange when new collation is enabled. + // Value set to `false` means not use hash exchange when new collation is enabled. + HashExchangeWithNewCollation bool + + // enforceMPPExecution means if we should enforce mpp way to execute query. + // Default value is `false`, means to be determined by variable `allowMPPExecution`. + // Value set to `true` means enforce use mpp. + // Note if you want to set `enforceMPPExecution` to `true`, you must set `allowMPPExecution` to `true` first. + enforceMPPExecution bool + + // TiFlashMaxThreads is the maximum number of threads to execute the request which is pushed down to tiflash. + // Default value is -1, means it will not be pushed down to tiflash. + // If the value is bigger than -1, it will be pushed down to tiflash and used to create db context in tiflash. + TiFlashMaxThreads int64 + + // TiFlashMaxBytesBeforeExternalJoin is the maximum bytes used by a TiFlash join before spill to disk + // Default value is -1, means it will not be pushed down to TiFlash + // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means + // not limit and spill will never happen + TiFlashMaxBytesBeforeExternalJoin int64 + + // TiFlashMaxBytesBeforeExternalGroupBy is the maximum bytes used by a TiFlash hash aggregation before spill to disk + // Default value is -1, means it will not be pushed down to TiFlash + // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means + // not limit and spill will never happen + TiFlashMaxBytesBeforeExternalGroupBy int64 + + // TiFlashMaxBytesBeforeExternalSort is the maximum bytes used by a TiFlash sort/TopN before spill to disk + // Default value is -1, means it will not be pushed down to TiFlash + // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means + // not limit and spill will never happen + TiFlashMaxBytesBeforeExternalSort int64 + + // TiFlash max query memory per node, -1 and 0 means no limit, and the default value is 0 + // If TiFlashMaxQueryMemoryPerNode > 0 && TiFlashQuerySpillRatio > 0, it will trigger auto spill in TiFlash side, and when auto spill + // is triggered, per executor's memory usage threshold set by TiFlashMaxBytesBeforeExternalJoin/TiFlashMaxBytesBeforeExternalGroupBy/TiFlashMaxBytesBeforeExternalSort will be ignored. + TiFlashMaxQueryMemoryPerNode int64 + + // TiFlashQuerySpillRatio is the percentage threshold to trigger auto spill in TiFlash if TiFlashMaxQueryMemoryPerNode is set + TiFlashQuerySpillRatio float64 + + // TiDBAllowAutoRandExplicitInsert indicates whether explicit insertion on auto_random column is allowed. + AllowAutoRandExplicitInsert bool + + // BroadcastJoinThresholdSize is used to limit the size of smaller table. + // It's unit is bytes, if the size of small table is larger than it, we will not use bcj. + BroadcastJoinThresholdSize int64 + + // BroadcastJoinThresholdCount is used to limit the total count of smaller table. + // If we can't estimate the size of one side of join child, we will check if its row number exceeds this limitation. + BroadcastJoinThresholdCount int64 + + // PreferBCJByExchangeDataSize indicates the method used to choose mpp broadcast join + // false: choose mpp broadcast join by `BroadcastJoinThresholdSize` and `BroadcastJoinThresholdCount` + // true: compare data exchange size of join and choose the smallest one + PreferBCJByExchangeDataSize bool + + // LimitPushDownThreshold determines if push Limit or TopN down to TiKV forcibly. + LimitPushDownThreshold int64 + + // CorrelationThreshold is the guard to enable row count estimation using column order correlation. + CorrelationThreshold float64 + + // EnableCorrelationAdjustment is used to indicate if correlation adjustment is enabled. + EnableCorrelationAdjustment bool + + // CorrelationExpFactor is used to control the heuristic approach of row count estimation when CorrelationThreshold is not met. + CorrelationExpFactor int + + // cpuFactor is the CPU cost of processing one expression for one row. + cpuFactor float64 + // copCPUFactor is the CPU cost of processing one expression for one row in coprocessor. + copCPUFactor float64 + // networkFactor is the network cost of transferring 1 byte data. + networkFactor float64 + // ScanFactor is the IO cost of scanning 1 byte data on TiKV and TiFlash. + scanFactor float64 + // descScanFactor is the IO cost of scanning 1 byte data on TiKV and TiFlash in desc order. + descScanFactor float64 + // seekFactor is the IO cost of seeking the start value of a range in TiKV or TiFlash. + seekFactor float64 + // memoryFactor is the memory cost of storing one tuple. + memoryFactor float64 + // diskFactor is the IO cost of reading/writing one byte to temporary disk. + diskFactor float64 + // concurrencyFactor is the CPU cost of additional one goroutine. + concurrencyFactor float64 + + // enableForceInlineCTE is used to enable/disable force inline CTE. + enableForceInlineCTE bool + + // CopTiFlashConcurrencyFactor is the concurrency number of computation in tiflash coprocessor. + CopTiFlashConcurrencyFactor float64 + + // CurrInsertValues is used to record current ValuesExpr's values. + // See http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values + CurrInsertValues chunk.Row + + // In https://github.com/pingcap/tidb/issues/14164, we can see that MySQL can enter the column that is not in the insert's SELECT's output. + // We store the extra columns in this variable. + CurrInsertBatchExtraCols [][]types.Datum + + // Per-connection time zones. Each client that connects has its own time zone setting, given by the session time_zone variable. + // See https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html + TimeZone *time.Location + + SQLMode mysql.SQLMode + + // AutoIncrementIncrement and AutoIncrementOffset indicates the autoID's start value and increment. + AutoIncrementIncrement int + + AutoIncrementOffset int + + /* TiDB system variables */ + + // SkipASCIICheck check on input value. + SkipASCIICheck bool + + // SkipUTF8Check check on input value. + SkipUTF8Check bool + + // DefaultCollationForUTF8MB4 indicates the default collation of UTF8MB4. + DefaultCollationForUTF8MB4 string + + // BatchInsert indicates if we should split insert data into multiple batches. + BatchInsert bool + + // BatchDelete indicates if we should split delete data into multiple batches. + BatchDelete bool + + // BatchCommit indicates if we should split the transaction into multiple batches. + BatchCommit bool + + // IDAllocator is provided by kvEncoder, if it is provided, we will use it to alloc auto id instead of using + // Table.alloc. + IDAllocator autoid.Allocator + + // OptimizerSelectivityLevel defines the level of the selectivity estimation in plan. + OptimizerSelectivityLevel int + + // OptimizerEnableNewOnlyFullGroupByCheck enables the new only_full_group_by check which is implemented by maintaining functional dependency. + OptimizerEnableNewOnlyFullGroupByCheck bool + + // EnableOuterJoinWithJoinReorder enables TiDB to involve the outer join into the join reorder. + EnableOuterJoinReorder bool + + // OptimizerEnableNAAJ enables TiDB to use null-aware anti join. + OptimizerEnableNAAJ bool + + // EnableTablePartition enables table partition feature. + EnableTablePartition string + + // EnableListTablePartition enables list table partition feature. + EnableListTablePartition bool + + // EnableCascadesPlanner enables the cascades planner. + EnableCascadesPlanner bool + + // EnableWindowFunction enables the window function. + EnableWindowFunction bool + + // EnablePipelinedWindowExec enables executing window functions in a pipelined manner. + EnablePipelinedWindowExec bool + + // AllowProjectionPushDown enables pushdown projection on TiKV. + AllowProjectionPushDown bool + + // EnableStrictDoubleTypeCheck enables table field double type check. + EnableStrictDoubleTypeCheck bool + + // EnableVectorizedExpression enables the vectorized expression evaluation. + EnableVectorizedExpression bool + + // DDLReorgPriority is the operation priority of adding indices. + DDLReorgPriority int + + // EnableAutoIncrementInGenerated is used to control whether to allow auto incremented columns in generated columns. + EnableAutoIncrementInGenerated bool + + // EnablePointGetCache is used to cache value for point get for read only scenario. + EnablePointGetCache bool + + // PlacementMode the placement mode we use + // strict: Check placement settings strictly in ddl operations + // ignore: Ignore all placement settings in ddl operations + PlacementMode string + + // WaitSplitRegionFinish defines the split region behaviour is sync or async. + WaitSplitRegionFinish bool + + // WaitSplitRegionTimeout defines the split region timeout. + WaitSplitRegionTimeout uint64 + + // EnableChunkRPC indicates whether the coprocessor request can use chunk API. + EnableChunkRPC bool + + writeStmtBufs WriteStmtBufs + + // ConstraintCheckInPlace indicates whether to check the constraint when the SQL executing. + ConstraintCheckInPlace bool + + // CommandValue indicates which command current session is doing. + CommandValue uint32 + + // TiDBOptJoinReorderThreshold defines the minimal number of join nodes + // to use the greedy join reorder algorithm. + TiDBOptJoinReorderThreshold int + + // SlowQueryFile indicates which slow query log file for SLOW_QUERY table to parse. + SlowQueryFile string + + // EnableFastAnalyze indicates whether to take fast analyze. + EnableFastAnalyze bool + + // TxnMode indicates should be pessimistic or optimistic. + TxnMode string + + // LowResolutionTSO is used for reading data with low resolution TSO which is updated once every two seconds. + LowResolutionTSO bool + + // MaxExecutionTime is the timeout for select statement, in milliseconds. + // If the value is 0, timeouts are not enabled. + // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_execution_time + MaxExecutionTime uint64 + + // TiKVClientReadTimeout is the timeout for readonly kv request in milliseconds, 0 means using default value + // See https://github.com/pingcap/tidb/blob/7105505a78fc886c33258caa5813baf197b15247/docs/design/2023-06-30-configurable-kv-timeout.md?plain=1#L14-L15 + TiKVClientReadTimeout uint64 + + // Killed is a flag to indicate that this query is killed. + Killed uint32 + + // ConnectionStatus indicates current connection status. + ConnectionStatus int32 + + // ConnectionInfo indicates current connection info used by current session. + ConnectionInfo *ConnectionInfo + + // NoopFuncsMode allows OFF/ON/WARN values as 0/1/2. + NoopFuncsMode int + + // StartTime is the start time of the last query. + StartTime time.Time + + // DurationParse is the duration of parsing SQL string to AST of the last query. + DurationParse time.Duration + + // DurationCompile is the duration of compiling AST to execution plan of the last query. + DurationCompile time.Duration + + // RewritePhaseInfo records all information about the rewriting phase. + RewritePhaseInfo + + // DurationOptimization is the duration of optimizing a query. + DurationOptimization time.Duration + + // DurationWaitTS is the duration of waiting for a snapshot TS + DurationWaitTS time.Duration + + // PrevStmt is used to store the previous executed statement in the current session. + PrevStmt fmt.Stringer + + // prevStmtDigest is used to store the digest of the previous statement in the current session. + prevStmtDigest string + + // AllowRemoveAutoInc indicates whether a user can drop the auto_increment column attribute or not. + AllowRemoveAutoInc bool + + // UsePlanBaselines indicates whether we will use plan baselines to adjust plan. + UsePlanBaselines bool + + // EvolvePlanBaselines indicates whether we will evolve the plan baselines. + EvolvePlanBaselines bool + + // EnableExtendedStats indicates whether we enable the extended statistics feature. + EnableExtendedStats bool + + // Unexported fields should be accessed and set through interfaces like GetReplicaRead() and SetReplicaRead(). + + // allowInSubqToJoinAndAgg can be set to false to forbid rewriting the semi join to inner join with agg. + allowInSubqToJoinAndAgg bool + + // preferRangeScan allows optimizer to always prefer range scan over table scan. + preferRangeScan bool + + // EnableIndexMerge enables the generation of IndexMergePath. + enableIndexMerge bool + + // replicaRead is used for reading data from replicas, only follower is supported at this time. + replicaRead kv.ReplicaReadType + // ReplicaClosestReadThreshold is the minimum response body size that a cop request should be sent to the closest replica. + // this variable only take effect when `tidb_follower_read` = 'closest-adaptive' + ReplicaClosestReadThreshold int64 + + // IsolationReadEngines is used to isolation read, tidb only read from the stores whose engine type is in the engines. + IsolationReadEngines map[kv.StoreType]struct{} + + mppVersion kv.MppVersion + + mppExchangeCompressionMode kv.ExchangeCompressionMode + + PlannerSelectBlockAsName atomic.Pointer[[]ast.HintTable] + + // LockWaitTimeout is the duration waiting for pessimistic lock in milliseconds + LockWaitTimeout int64 + + // MetricSchemaStep indicates the step when query metric schema. + MetricSchemaStep int64 + + // CDCWriteSource indicates the following data is written by TiCDC if it is not 0. + CDCWriteSource uint64 + + // MetricSchemaRangeDuration indicates the step when query metric schema. + MetricSchemaRangeDuration int64 + + // Some data of cluster-level memory tables will be retrieved many times in different inspection rules, + // and the cost of retrieving some data is expensive. We use the `TableSnapshot` to cache those data + // and obtain them lazily, and provide a consistent view of inspection tables for each inspection rules. + // All cached snapshots will be released at the end of retrieving + InspectionTableCache map[string]TableSnapshot + + // RowEncoder is reused in session for encode row data. + RowEncoder rowcodec.Encoder + + // SequenceState cache all sequence's latest value accessed by lastval() builtins. It's a session scoped + // variable, and all public methods of SequenceState are currently-safe. + SequenceState *SequenceState + + // WindowingUseHighPrecision determines whether to compute window operations without loss of precision. + // see https://dev.mysql.com/doc/refman/8.0/en/window-function-optimization.html for more details. + WindowingUseHighPrecision bool + + // FoundInPlanCache indicates whether this statement was found in plan cache. + FoundInPlanCache bool + // PrevFoundInPlanCache indicates whether the last statement was found in plan cache. + PrevFoundInPlanCache bool + + // FoundInBinding indicates whether the execution plan is matched with the hints in the binding. + FoundInBinding bool + // PrevFoundInBinding indicates whether the last execution plan is matched with the hints in the binding. + PrevFoundInBinding bool + + // OptimizerUseInvisibleIndexes indicates whether optimizer can use invisible index + OptimizerUseInvisibleIndexes bool + + // SelectLimit limits the max counts of select statement's output + SelectLimit uint64 + + // EnableClusteredIndex indicates whether to enable clustered index when creating a new table. + EnableClusteredIndex ClusteredIndexDefMode + + // PresumeKeyNotExists indicates lazy existence checking is enabled. + PresumeKeyNotExists bool + + // EnableParallelApply indicates that thether to use parallel apply. + EnableParallelApply bool + + // EnableRedactLog indicates that whether redact log. + EnableRedactLog bool + + // ShardAllocateStep indicates the max size of continuous rowid shard in one transaction. + ShardAllocateStep int64 + + // LastTxnInfo keeps track the info of last committed transaction. + LastTxnInfo string + + // LastQueryInfo keeps track the info of last query. + LastQueryInfo sessionstates.QueryInfo + + // LastDDLInfo keeps track the info of last DDL. + LastDDLInfo sessionstates.LastDDLInfo + + // PartitionPruneMode indicates how and when to prune partitions. + PartitionPruneMode atomic2.String + + // TxnScope indicates the scope of the transactions. It should be `global` or equal to the value of key `zone` in config.Labels. + TxnScope kv.TxnScopeVar + + // EnabledRateLimitAction indicates whether enabled ratelimit action during coprocessor + EnabledRateLimitAction bool + + // EnableAsyncCommit indicates whether to enable the async commit feature. + EnableAsyncCommit bool + + // Enable1PC indicates whether to enable the one-phase commit feature. + Enable1PC bool + + // GuaranteeLinearizability indicates whether to guarantee linearizability + GuaranteeLinearizability bool + + // AnalyzeVersion indicates how TiDB collect and use analyzed statistics. + AnalyzeVersion int + + // DisableHashJoin indicates whether to disable hash join. + DisableHashJoin bool + + // EnableHistoricalStats indicates whether to enable historical statistics. + EnableHistoricalStats bool + + // EnableIndexMergeJoin indicates whether to enable index merge join. + EnableIndexMergeJoin bool + + // TrackAggregateMemoryUsage indicates whether to track the memory usage of aggregate function. + TrackAggregateMemoryUsage bool + + // TiDBEnableExchangePartition indicates whether to enable exchange partition + TiDBEnableExchangePartition bool + + // AllowFallbackToTiKV indicates the engine types whose unavailability triggers fallback to TiKV. + // Now we only support TiFlash. + AllowFallbackToTiKV map[kv.StoreType]struct{} + + // CTEMaxRecursionDepth indicates The common table expression (CTE) maximum recursion depth. + // see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_cte_max_recursion_depth + CTEMaxRecursionDepth int + + // The temporary table size threshold, which is different from MySQL. See https://github.com/pingcap/tidb/issues/28691. + TMPTableSize int64 + + // EnableStableResultMode if stabilize query results. + EnableStableResultMode bool + + // EnablePseudoForOutdatedStats if using pseudo for outdated stats + EnablePseudoForOutdatedStats bool + + // RegardNULLAsPoint if regard NULL as Point + RegardNULLAsPoint bool + + // LocalTemporaryTables is *infoschema.LocalTemporaryTables, use interface to avoid circle dependency. + // It's nil if there is no local temporary table. + LocalTemporaryTables interface{} + + // TemporaryTableData stores committed kv values for temporary table for current session. + TemporaryTableData TemporaryTableData + + // MPPStoreFailTTL indicates the duration that protect TiDB from sending task to a new recovered TiFlash. + MPPStoreFailTTL string + + // ReadStaleness indicates the staleness duration for the following query + ReadStaleness time.Duration + + // cachedStmtCtx is used to optimze the object allocation. + cachedStmtCtx [2]stmtctx.StatementContext + + // Rng stores the rand_seed1 and rand_seed2 for Rand() function + Rng *mathutil.MysqlRng + + // EnablePaging indicates whether enable paging in coprocessor requests. + EnablePaging bool + + // EnableLegacyInstanceScope says if SET SESSION can be used to set an instance + // scope variable. The default is TRUE. + EnableLegacyInstanceScope bool + + // ReadConsistency indicates the read consistency requirement. + ReadConsistency ReadConsistencyLevel + + // StatsLoadSyncWait indicates how long to wait for stats load before timeout. + StatsLoadSyncWait int64 + + // SysdateIsNow indicates whether Sysdate is an alias of Now function + SysdateIsNow bool + // EnableMutationChecker indicates whether to check data consistency for mutations + EnableMutationChecker bool + // AssertionLevel controls how strict the assertions on data mutations should be. + AssertionLevel AssertionLevel + // IgnorePreparedCacheCloseStmt controls if ignore the close-stmt command for prepared statement. + IgnorePreparedCacheCloseStmt bool + // EnableNewCostInterface is a internal switch to indicates whether to use the new cost calculation interface. + EnableNewCostInterface bool + // CostModelVersion is a internal switch to indicates the Cost Model Version. + CostModelVersion int + // IndexJoinDoubleReadPenaltyCostRate indicates whether to add some penalty cost to IndexJoin and how much of it. + IndexJoinDoubleReadPenaltyCostRate float64 + + // BatchPendingTiFlashCount shows the threshold of pending TiFlash tables when batch adding. + BatchPendingTiFlashCount int + // RcWriteCheckTS indicates whether some special write statements don't get latest tso from PD at RC + RcWriteCheckTS bool + // RemoveOrderbyInSubquery indicates whether to remove ORDER BY in subquery. + RemoveOrderbyInSubquery bool + // NonTransactionalIgnoreError indicates whether to ignore error in non-transactional statements. + // When set to false, returns immediately when it meets the first error. + NonTransactionalIgnoreError bool + + // MaxAllowedPacket indicates the maximum size of a packet for the MySQL protocol. + MaxAllowedPacket uint64 + + // TiFlash related optimization, only for MPP. + TiFlashFineGrainedShuffleStreamCount int64 + TiFlashFineGrainedShuffleBatchSize uint64 + + // RequestSourceType is the type of inner request. + RequestSourceType string + // ExplicitRequestSourceType is the type of origin external request. + ExplicitRequestSourceType string + + // MemoryDebugModeMinHeapInUse indicated the minimum heapInUse threshold that triggers the memoryDebugMode. + MemoryDebugModeMinHeapInUse int64 + // MemoryDebugModeAlarmRatio indicated the allowable bias ratio of memory tracking accuracy check. + // When `(memory trakced by tidb) * (1+MemoryDebugModeAlarmRatio) < actual heapInUse`, an alarm log will be recorded. + MemoryDebugModeAlarmRatio int64 + + // EnableAnalyzeSnapshot indicates whether to read data on snapshot when collecting statistics. + // When it is false, ANALYZE reads the latest data. + // When it is true, ANALYZE reads data on the snapshot at the beginning of ANALYZE. + EnableAnalyzeSnapshot bool + + // DefaultStrMatchSelectivity adjust the estimation strategy for string matching expressions that can't be estimated by building into range. + // when > 0: it's the selectivity for the expression. + // when = 0: try to use TopN to evaluate the like expression to estimate the selectivity. + DefaultStrMatchSelectivity float64 + + // TiFlashFastScan indicates whether use fast scan in TiFlash + TiFlashFastScan bool + + // PrimaryKeyRequired indicates if sql_require_primary_key sysvar is set + PrimaryKeyRequired bool + + // EnablePreparedPlanCache indicates whether to enable prepared plan cache. + EnablePreparedPlanCache bool + + // PreparedPlanCacheSize controls the size of prepared plan cache. + PreparedPlanCacheSize uint64 + + // PreparedPlanCacheMonitor indicates whether to enable prepared plan cache monitor. + EnablePreparedPlanCacheMemoryMonitor bool + + // EnablePlanCacheForParamLimit controls whether the prepare statement with parameterized limit can be cached + EnablePlanCacheForParamLimit bool + + // EnablePlanCacheForSubquery controls whether the prepare statement with sub query can be cached + EnablePlanCacheForSubquery bool + + // EnableNonPreparedPlanCache indicates whether to enable non-prepared plan cache. + EnableNonPreparedPlanCache bool + + // EnableNonPreparedPlanCacheForDML indicates whether to enable non-prepared plan cache for DML statements. + EnableNonPreparedPlanCacheForDML bool + + // PlanCacheInvalidationOnFreshStats controls if plan cache will be invalidated automatically when + // related stats are analyzed after the plan cache is generated. + PlanCacheInvalidationOnFreshStats bool + + // NonPreparedPlanCacheSize controls the size of non-prepared plan cache. + NonPreparedPlanCacheSize uint64 + + // PlanCacheMaxPlanSize controls the maximum size of a plan that can be cached. + PlanCacheMaxPlanSize uint64 + + // SessionPlanCacheSize controls the size of session plan cache. + SessionPlanCacheSize uint64 + + // ConstraintCheckInPlacePessimistic controls whether to skip the locking of some keys in pessimistic transactions. + // Postpone the conflict check and constraint check to prewrite or later pessimistic locking requests. + ConstraintCheckInPlacePessimistic bool + + // EnableTiFlashReadForWriteStmt indicates whether to enable TiFlash to read for write statements. + EnableTiFlashReadForWriteStmt bool + + // EnableUnsafeSubstitute indicates whether to enable generate column takes unsafe substitute. + EnableUnsafeSubstitute bool + + // ForeignKeyChecks indicates whether to enable foreign key constraint check. + ForeignKeyChecks bool + + // RangeMaxSize is the max memory limit for ranges. When the optimizer estimates that the memory usage of complete + // ranges would exceed the limit, it chooses less accurate ranges such as full range. 0 indicates that there is no + // memory limit for ranges. + RangeMaxSize int64 + + // LastPlanReplayerToken indicates the last plan replayer token + LastPlanReplayerToken string + + // InPlanReplayer means we are now executing a statement for a PLAN REPLAYER SQL. + // Note that PLAN REPLAYER CAPTURE is not included here. + InPlanReplayer bool + + // AnalyzePartitionConcurrency indicates concurrency for partitions in Analyze + AnalyzePartitionConcurrency int + // AnalyzePartitionMergeConcurrency indicates concurrency for merging partition stats + AnalyzePartitionMergeConcurrency int + + // EnableExternalTSRead indicates whether to enable read through external ts + EnableExternalTSRead bool + + HookContext + + // MemTracker indicates the memory tracker of current session. + MemTracker *memory.Tracker + // MemDBDBFootprint tracks the memory footprint of memdb, and is attached to `MemTracker` + MemDBFootprint *memory.Tracker + DiskTracker *memory.Tracker + + // OptPrefixIndexSingleScan indicates whether to do some optimizations to avoid double scan for prefix index. + // When set to true, `col is (not) null`(`col` is index prefix column) is regarded as index filter rather than table filter. + OptPrefixIndexSingleScan bool + + // ChunkPool Several chunks and columns are cached + ChunkPool ReuseChunkPool + // EnableReuseCheck indicates request chunk whether use chunk alloc + EnableReuseCheck bool + + // EnableAdvancedJoinHint indicates whether the join method hint is compatible with join order hint. + EnableAdvancedJoinHint bool + + // preuseChunkAlloc indicates whether pre statement use chunk alloc + // like select @@last_sql_use_alloc + preUseChunkAlloc bool + + // EnablePlanReplayerCapture indicates whether enabled plan replayer capture + EnablePlanReplayerCapture bool + + // EnablePlanReplayedContinuesCapture indicates whether enabled plan replayer continues capture + EnablePlanReplayedContinuesCapture bool + + // PlanReplayerFinishedTaskKey used to record the finished plan replayer task key in order not to record the + // duplicate task in plan replayer continues capture + PlanReplayerFinishedTaskKey map[replayer.PlanReplayerTaskKey]struct{} + + // StoreBatchSize indicates the batch size limit of store batch, set this field to 0 to disable store batch. + StoreBatchSize int + + // shardRand is used by TxnCtx, for the GetCurrentShard() method. + shardRand *rand.Rand + + // Resource group name + ResourceGroupName string + + // PessimisticTransactionFairLocking controls whether fair locking for pessimistic transaction + // is enabled. + PessimisticTransactionFairLocking bool + + // EnableINLJoinInnerMultiPattern indicates whether enable multi pattern for index join inner side + // For now it is not public to user + EnableINLJoinInnerMultiPattern bool + + // Enable late materialization: push down some selection condition to tablescan. + EnableLateMaterialization bool + + // EnableRowLevelChecksum indicates whether row level checksum is enabled. + EnableRowLevelChecksum bool + + // TiFlashComputeDispatchPolicy indicates how to dipatch task to tiflash_compute nodes. + // Only for disaggregated-tiflash mode. + TiFlashComputeDispatchPolicy tiflashcompute.DispatchPolicy + + // SlowTxnThreshold is the threshold of slow transaction logs + SlowTxnThreshold uint64 + + // LoadBasedReplicaReadThreshold is the threshold for the estimated wait duration of a store. + // If exceeding the threshold, try other stores using replica read. + LoadBasedReplicaReadThreshold time.Duration + + // OptOrderingIdxSelThresh is the threshold for optimizer to consider the ordering index. + // If there exists an index whose estimated selectivity is smaller than this threshold, the optimizer won't + // use the ExpectedCnt to adjust the estimated row count for index scan. + OptOrderingIdxSelThresh float64 + + // EnableMPPSharedCTEExecution indicates whether we enable the shared CTE execution strategy on MPP side. + EnableMPPSharedCTEExecution bool + + // OptimizerFixControl control some details of the optimizer behavior through the tidb_opt_fix_control variable. + OptimizerFixControl map[uint64]string + + // FastCheckTable is used to control whether fast check table is enabled. + FastCheckTable bool + + // HypoIndexes are for the Index Advisor. + HypoIndexes map[string]map[string]map[string]*model.IndexInfo // dbName -> tblName -> idxName -> idxInfo + + // TiFlashReplicaRead indicates the policy of TiFlash node selection when the query needs the TiFlash engine. + TiFlashReplicaRead tiflash.ReplicaRead + + // HypoTiFlashReplicas are for the Index Advisor. + HypoTiFlashReplicas map[string]map[string]struct{} // dbName -> tblName -> whether to have replicas + + // Runtime Filter Group + // Runtime filter type: only support IN or MIN_MAX now. + // Runtime filter type can take multiple values at the same time. + runtimeFilterTypes []RuntimeFilterType + // Runtime filter mode: only support OFF, LOCAL now + runtimeFilterMode RuntimeFilterMode + + // Whether to lock duplicate keys in INSERT IGNORE and REPLACE statements, + // or unchanged unique keys in UPDATE statements, see PR #42210 and #42713 + LockUnchangedKeys bool + + // AnalyzeSkipColumnTypes indicates the column types whose statistics would not be collected when executing the ANALYZE command. + AnalyzeSkipColumnTypes map[string]struct{} + + // SkipMissingPartitionStats controls how to handle missing partition stats when merging partition stats to global stats. + // When set to true, skip missing partition stats and continue to merge other partition stats to global stats. + // When set to false, give up merging partition stats to global stats. + SkipMissingPartitionStats bool + + // SessionAlias is the identifier of the session + SessionAlias string + + // OptObjective indicates whether the optimizer should be more stable, predictable or more aggressive. + // For now, the possible values and corresponding behaviors are: + // OptObjectiveModerate: The default value. The optimizer considers the real-time stats (real-time row count, modify count). + // OptObjectiveDeterminate: The optimizer doesn't consider the real-time stats. + OptObjective string +} + +// GetOptimizerFixControlMap returns the specified value of the optimizer fix control. +func (s *SessionVars) GetOptimizerFixControlMap() map[uint64]string { + return s.OptimizerFixControl +} + +// planReplayerSessionFinishedTaskKeyLen is used to control the max size for the finished plan replayer task key in session +// in order to control the used memory +const planReplayerSessionFinishedTaskKeyLen = 128 + +// AddPlanReplayerFinishedTaskKey record finished task key in session +func (s *SessionVars) AddPlanReplayerFinishedTaskKey(key replayer.PlanReplayerTaskKey) { + if len(s.PlanReplayerFinishedTaskKey) >= planReplayerSessionFinishedTaskKeyLen { + s.initializePlanReplayerFinishedTaskKey() + } + s.PlanReplayerFinishedTaskKey[key] = struct{}{} +} + +func (s *SessionVars) initializePlanReplayerFinishedTaskKey() { + s.PlanReplayerFinishedTaskKey = make(map[replayer.PlanReplayerTaskKey]struct{}, planReplayerSessionFinishedTaskKeyLen) +} + +// CheckPlanReplayerFinishedTaskKey check whether the key exists +func (s *SessionVars) CheckPlanReplayerFinishedTaskKey(key replayer.PlanReplayerTaskKey) bool { + if s.PlanReplayerFinishedTaskKey == nil { + s.initializePlanReplayerFinishedTaskKey() + return false + } + _, ok := s.PlanReplayerFinishedTaskKey[key] + return ok +} + +// IsPlanReplayerCaptureEnabled indicates whether capture or continues capture enabled +func (s *SessionVars) IsPlanReplayerCaptureEnabled() bool { + return s.EnablePlanReplayerCapture || s.EnablePlanReplayedContinuesCapture +} + +// GetNewChunkWithCapacity Attempt to request memory from the chunk pool +// thread safety +func (s *SessionVars) GetNewChunkWithCapacity(fields []*types.FieldType, capacity int, maxCachesize int, pool chunk.Allocator) *chunk.Chunk { + if pool == nil { + return chunk.New(fields, capacity, maxCachesize) + } + s.ChunkPool.mu.Lock() + defer s.ChunkPool.mu.Unlock() + if pool.CheckReuseAllocSize() && (!s.GetUseChunkAlloc()) { + s.StmtCtx.SetUseChunkAlloc() + } + chk := pool.Alloc(fields, capacity, maxCachesize) + return chk +} + +// ExchangeChunkStatus give the status to preUseChunkAlloc +func (s *SessionVars) ExchangeChunkStatus() { + s.preUseChunkAlloc = s.GetUseChunkAlloc() +} + +// GetUseChunkAlloc return useChunkAlloc status +func (s *SessionVars) GetUseChunkAlloc() bool { + return s.StmtCtx.GetUseChunkAllocStatus() +} + +// SetAlloc Attempt to set the buffer pool address +func (s *SessionVars) SetAlloc(alloc chunk.Allocator) { + if !s.EnableReuseCheck { + return + } + s.ChunkPool.Alloc = alloc +} + +// IsAllocValid check if chunk reuse is enable or ChunkPool is inused. +func (s *SessionVars) IsAllocValid() bool { + if !s.EnableReuseCheck { + return false + } + s.ChunkPool.mu.Lock() + defer s.ChunkPool.mu.Unlock() + return s.ChunkPool.Alloc != nil +} + +// ClearAlloc indicates stop reuse chunk +func (s *SessionVars) ClearAlloc(alloc *chunk.Allocator, b bool) { + if !b { + s.ChunkPool.Alloc = nil + return + } + + // If an error is reported, re-apply for alloc + // Prevent the goroutine left before, affecting the execution of the next sql + // issuse 38918 + s.ChunkPool.mu.Lock() + s.ChunkPool.Alloc = nil + s.ChunkPool.mu.Unlock() + *alloc = chunk.NewAllocator() +} + +// GetPreparedStmtByName returns the prepared statement specified by stmtName. +func (s *SessionVars) GetPreparedStmtByName(stmtName string) (interface{}, error) { + stmtID, ok := s.PreparedStmtNameToID[stmtName] + if !ok { + return nil, ErrStmtNotFound + } + return s.GetPreparedStmtByID(stmtID) +} + +// GetPreparedStmtByID returns the prepared statement specified by stmtID. +func (s *SessionVars) GetPreparedStmtByID(stmtID uint32) (interface{}, error) { + stmt, ok := s.PreparedStmts[stmtID] + if !ok { + return nil, ErrStmtNotFound + } + return stmt, nil +} + +// InitStatementContext initializes a StatementContext, the object is reused to reduce allocation. +func (s *SessionVars) InitStatementContext() *stmtctx.StatementContext { + sc := &s.cachedStmtCtx[0] + if sc == s.StmtCtx { + sc = &s.cachedStmtCtx[1] + } + if s.RefCountOfStmtCtx.TryFreeze() { + sc.Reset() + s.RefCountOfStmtCtx.UnFreeze() + } else { + sc = stmtctx.NewStmtCtx() + } + return sc +} + +// IsMPPAllowed returns whether mpp execution is allowed. +func (s *SessionVars) IsMPPAllowed() bool { + return s.allowMPPExecution +} + +// IsTiFlashCopBanned returns whether cop execution is allowed. +func (s *SessionVars) IsTiFlashCopBanned() bool { + return !s.allowTiFlashCop +} + +// IsMPPEnforced returns whether mpp execution is enforced. +func (s *SessionVars) IsMPPEnforced() bool { + return s.allowMPPExecution && s.enforceMPPExecution +} + +// ChooseMppVersion indicates the mpp-version used to build mpp plan, if mpp-version is unspecified, use the latest version. +func (s *SessionVars) ChooseMppVersion() kv.MppVersion { + if s.mppVersion == kv.MppVersionUnspecified { + return kv.GetNewestMppVersion() + } + return s.mppVersion +} + +// ChooseMppExchangeCompressionMode indicates the data compression method in mpp exchange operator +func (s *SessionVars) ChooseMppExchangeCompressionMode() kv.ExchangeCompressionMode { + if s.mppExchangeCompressionMode == kv.ExchangeCompressionModeUnspecified { + // If unspecified, use recommended mode + return kv.RecommendedExchangeCompressionMode + } + return s.mppExchangeCompressionMode +} + +// RaiseWarningWhenMPPEnforced will raise a warning when mpp mode is enforced and executing explain statement. +// TODO: Confirm whether this function will be inlined and +// omit the overhead of string construction when calling with false condition. +func (s *SessionVars) RaiseWarningWhenMPPEnforced(warning string) { + if !s.IsMPPEnforced() { + return + } + if s.StmtCtx.InExplainStmt { + s.StmtCtx.AppendWarning(errors.New(warning)) + } else { + s.StmtCtx.AppendExtraWarning(errors.New(warning)) + } +} + +// CheckAndGetTxnScope will return the transaction scope we should use in the current session. +func (s *SessionVars) CheckAndGetTxnScope() string { + if s.InRestrictedSQL || !EnableLocalTxn.Load() { + return kv.GlobalTxnScope + } + if s.TxnScope.GetVarValue() == kv.LocalTxnScope { + return s.TxnScope.GetTxnScope() + } + return kv.GlobalTxnScope +} + +// IsDynamicPartitionPruneEnabled indicates whether dynamic partition prune enabled +// Note that: IsDynamicPartitionPruneEnabled only indicates whether dynamic partition prune mode is enabled according to +// session variable, it isn't guaranteed to be used during query due to other conditions checking. +func (s *SessionVars) IsDynamicPartitionPruneEnabled() bool { + return PartitionPruneMode(s.PartitionPruneMode.Load()) == Dynamic +} + +// IsRowLevelChecksumEnabled indicates whether row level checksum is enabled for current session, that is +// tidb_enable_row_level_checksum is on and tidb_row_format_version is 2 and it's not a internal session. +func (s *SessionVars) IsRowLevelChecksumEnabled() bool { + return s.EnableRowLevelChecksum && s.RowEncoder.Enable && !s.InRestrictedSQL +} + +// BuildParserConfig generate parser.ParserConfig for initial parser +func (s *SessionVars) BuildParserConfig() parser.ParserConfig { + return parser.ParserConfig{ + EnableWindowFunction: s.EnableWindowFunction, + EnableStrictDoubleTypeCheck: s.EnableStrictDoubleTypeCheck, + SkipPositionRecording: true, + } +} + +// AllocNewPlanID alloc new ID +func (s *SessionVars) AllocNewPlanID() int { + return int(s.PlanID.Add(1)) +} + +const ( + // PlacementModeStrict indicates all placement operations should be checked strictly in ddl + PlacementModeStrict string = "STRICT" + // PlacementModeIgnore indicates ignore all placement operations in ddl + PlacementModeIgnore string = "IGNORE" +) + +// PartitionPruneMode presents the prune mode used. +type PartitionPruneMode string + +const ( + // Static indicates only prune at plan phase. + Static PartitionPruneMode = "static" + // Dynamic indicates only prune at execute phase. + Dynamic PartitionPruneMode = "dynamic" + + // Don't use out-of-date mode. + + // StaticOnly is out-of-date. + StaticOnly PartitionPruneMode = "static-only" + // DynamicOnly is out-of-date. + DynamicOnly PartitionPruneMode = "dynamic-only" + // StaticButPrepareDynamic is out-of-date. + StaticButPrepareDynamic PartitionPruneMode = "static-collect-dynamic" +) + +// Valid indicate PruneMode is validated. +func (p PartitionPruneMode) Valid() bool { + switch p { + case Static, Dynamic, StaticOnly, DynamicOnly: + return true + default: + return false + } +} + +// Update updates out-of-date PruneMode. +func (p PartitionPruneMode) Update() PartitionPruneMode { + switch p { + case StaticOnly, StaticButPrepareDynamic: + return Static + case DynamicOnly: + return Dynamic + default: + return p + } +} + +// PlanCacheParamList stores the parameters for plan cache. +// Use attached methods to access or modify parameter values instead of accessing them directly. +type PlanCacheParamList struct { + paramValues []types.Datum + forNonPrepCache bool +} + +// NewPlanCacheParamList creates a new PlanCacheParams. +func NewPlanCacheParamList() *PlanCacheParamList { + p := &PlanCacheParamList{paramValues: make([]types.Datum, 0, 8)} + p.Reset() + return p +} + +// Reset resets the PlanCacheParams. +func (p *PlanCacheParamList) Reset() { + p.paramValues = p.paramValues[:0] + p.forNonPrepCache = false +} + +// String implements the fmt.Stringer interface. +func (p *PlanCacheParamList) String() string { + if p == nil || len(p.paramValues) == 0 || + p.forNonPrepCache { // hide non-prep parameter values by default + return "" + } + return " [arguments: " + types.DatumsToStrNoErr(p.paramValues) + "]" +} + +// Append appends a parameter value to the PlanCacheParams. +func (p *PlanCacheParamList) Append(vs ...types.Datum) { + p.paramValues = append(p.paramValues, vs...) +} + +// SetForNonPrepCache sets the flag forNonPrepCache. +func (p *PlanCacheParamList) SetForNonPrepCache(flag bool) { + p.forNonPrepCache = flag +} + +// GetParamValue returns the value of the parameter at the specified index. +func (p *PlanCacheParamList) GetParamValue(idx int) types.Datum { + return p.paramValues[idx] +} + +// AllParamValues returns all parameter values. +func (p *PlanCacheParamList) AllParamValues() []types.Datum { + return p.paramValues +} + +// ConnectionInfo presents the connection information, which is mainly used by audit logs. +type ConnectionInfo struct { + ConnectionID uint64 + ConnectionType string + Host string + ClientIP string + ClientPort string + ServerID int + ServerIP string + ServerPort int + Duration float64 + User string + ServerOSLoginUser string + OSVersion string + ClientVersion string + ServerVersion string + SSLVersion string + PID int + DB string + AuthMethod string + Attributes map[string]string +} + +const ( + // ConnTypeSocket indicates socket without TLS. + ConnTypeSocket string = "TCP" + // ConnTypeUnixSocket indicates Unix Socket. + ConnTypeUnixSocket string = "UnixSocket" + // ConnTypeTLS indicates socket with TLS. + ConnTypeTLS string = "SSL/TLS" +) + +// IsSecureTransport checks whether the connection is secure. +func (connInfo *ConnectionInfo) IsSecureTransport() bool { + switch connInfo.ConnectionType { + case ConnTypeUnixSocket, ConnTypeTLS: + return true + } + return false +} + +// NewSessionVars creates a session vars object. +func NewSessionVars(hctx HookContext) *SessionVars { + vars := &SessionVars{ + userVars: struct { + lock sync.RWMutex + values map[string]types.Datum + types map[string]*types.FieldType + }{ + values: make(map[string]types.Datum), + types: make(map[string]*types.FieldType), + }, + systems: make(map[string]string), + stmtVars: make(map[string]string), + PreparedStmts: make(map[uint32]interface{}), + PreparedStmtNameToID: make(map[string]uint32), + PlanCacheParams: NewPlanCacheParamList(), + TxnCtx: &TransactionContext{}, + RetryInfo: &RetryInfo{}, + ActiveRoles: make([]*auth.RoleIdentity, 0, 10), + StrictSQLMode: true, + AutoIncrementIncrement: DefAutoIncrementIncrement, + AutoIncrementOffset: DefAutoIncrementOffset, + Status: mysql.ServerStatusAutocommit, + StmtCtx: stmtctx.NewStmtCtx(), + AllowAggPushDown: false, + AllowCartesianBCJ: DefOptCartesianBCJ, + MPPOuterJoinFixedBuildSide: DefOptMPPOuterJoinFixedBuildSide, + BroadcastJoinThresholdSize: DefBroadcastJoinThresholdSize, + BroadcastJoinThresholdCount: DefBroadcastJoinThresholdSize, + OptimizerSelectivityLevel: DefTiDBOptimizerSelectivityLevel, + EnableOuterJoinReorder: DefTiDBEnableOuterJoinReorder, + RetryLimit: DefTiDBRetryLimit, + DisableTxnAutoRetry: DefTiDBDisableTxnAutoRetry, + DDLReorgPriority: kv.PriorityLow, + allowInSubqToJoinAndAgg: DefOptInSubqToJoinAndAgg, + preferRangeScan: DefOptPreferRangeScan, + EnableCorrelationAdjustment: DefOptEnableCorrelationAdjustment, + LimitPushDownThreshold: DefOptLimitPushDownThreshold, + CorrelationThreshold: DefOptCorrelationThreshold, + CorrelationExpFactor: DefOptCorrelationExpFactor, + cpuFactor: DefOptCPUFactor, + copCPUFactor: DefOptCopCPUFactor, + CopTiFlashConcurrencyFactor: DefOptTiFlashConcurrencyFactor, + networkFactor: DefOptNetworkFactor, + scanFactor: DefOptScanFactor, + descScanFactor: DefOptDescScanFactor, + seekFactor: DefOptSeekFactor, + memoryFactor: DefOptMemoryFactor, + diskFactor: DefOptDiskFactor, + concurrencyFactor: DefOptConcurrencyFactor, + enableForceInlineCTE: DefOptForceInlineCTE, + EnableVectorizedExpression: DefEnableVectorizedExpression, + CommandValue: uint32(mysql.ComSleep), + TiDBOptJoinReorderThreshold: DefTiDBOptJoinReorderThreshold, + SlowQueryFile: config.GetGlobalConfig().Log.SlowQueryFile, + WaitSplitRegionFinish: DefTiDBWaitSplitRegionFinish, + WaitSplitRegionTimeout: DefWaitSplitRegionTimeout, + enableIndexMerge: DefTiDBEnableIndexMerge, + NoopFuncsMode: TiDBOptOnOffWarn(DefTiDBEnableNoopFuncs), + replicaRead: kv.ReplicaReadLeader, + AllowRemoveAutoInc: DefTiDBAllowRemoveAutoInc, + UsePlanBaselines: DefTiDBUsePlanBaselines, + EvolvePlanBaselines: DefTiDBEvolvePlanBaselines, + EnableExtendedStats: false, + IsolationReadEngines: make(map[kv.StoreType]struct{}), + LockWaitTimeout: DefInnodbLockWaitTimeout * 1000, + MetricSchemaStep: DefTiDBMetricSchemaStep, + MetricSchemaRangeDuration: DefTiDBMetricSchemaRangeDuration, + SequenceState: NewSequenceState(), + WindowingUseHighPrecision: true, + PrevFoundInPlanCache: DefTiDBFoundInPlanCache, + FoundInPlanCache: DefTiDBFoundInPlanCache, + PrevFoundInBinding: DefTiDBFoundInBinding, + FoundInBinding: DefTiDBFoundInBinding, + SelectLimit: math.MaxUint64, + AllowAutoRandExplicitInsert: DefTiDBAllowAutoRandExplicitInsert, + EnableClusteredIndex: DefTiDBEnableClusteredIndex, + EnableParallelApply: DefTiDBEnableParallelApply, + ShardAllocateStep: DefTiDBShardAllocateStep, + PartitionPruneMode: *atomic2.NewString(DefTiDBPartitionPruneMode), + TxnScope: kv.NewDefaultTxnScopeVar(), + EnabledRateLimitAction: DefTiDBEnableRateLimitAction, + EnableAsyncCommit: DefTiDBEnableAsyncCommit, + Enable1PC: DefTiDBEnable1PC, + GuaranteeLinearizability: DefTiDBGuaranteeLinearizability, + AnalyzeVersion: DefTiDBAnalyzeVersion, + EnableIndexMergeJoin: DefTiDBEnableIndexMergeJoin, + AllowFallbackToTiKV: make(map[kv.StoreType]struct{}), + CTEMaxRecursionDepth: DefCTEMaxRecursionDepth, + TMPTableSize: DefTiDBTmpTableMaxSize, + MPPStoreFailTTL: DefTiDBMPPStoreFailTTL, + Rng: mathutil.NewWithTime(), + StatsLoadSyncWait: StatsLoadSyncWait.Load(), + EnableLegacyInstanceScope: DefEnableLegacyInstanceScope, + RemoveOrderbyInSubquery: DefTiDBRemoveOrderbyInSubquery, + EnableSkewDistinctAgg: DefTiDBSkewDistinctAgg, + Enable3StageDistinctAgg: DefTiDB3StageDistinctAgg, + MaxAllowedPacket: DefMaxAllowedPacket, + TiFlashFastScan: DefTiFlashFastScan, + EnableTiFlashReadForWriteStmt: true, + ForeignKeyChecks: DefTiDBForeignKeyChecks, + HookContext: hctx, + EnableReuseCheck: DefTiDBEnableReusechunk, + preUseChunkAlloc: DefTiDBUseAlloc, + ChunkPool: ReuseChunkPool{Alloc: nil}, + mppExchangeCompressionMode: DefaultExchangeCompressionMode, + mppVersion: kv.MppVersionUnspecified, + EnableLateMaterialization: DefTiDBOptEnableLateMaterialization, + TiFlashComputeDispatchPolicy: tiflashcompute.DispatchPolicyConsistentHash, + ResourceGroupName: resourcegroup.DefaultResourceGroupName, + DefaultCollationForUTF8MB4: mysql.DefaultCollationName, + } + vars.KVVars = tikvstore.NewVariables(&vars.Killed) + vars.Concurrency = Concurrency{ + indexLookupConcurrency: DefIndexLookupConcurrency, + indexSerialScanConcurrency: DefIndexSerialScanConcurrency, + indexLookupJoinConcurrency: DefIndexLookupJoinConcurrency, + hashJoinConcurrency: DefTiDBHashJoinConcurrency, + projectionConcurrency: DefTiDBProjectionConcurrency, + distSQLScanConcurrency: DefDistSQLScanConcurrency, + hashAggPartialConcurrency: DefTiDBHashAggPartialConcurrency, + hashAggFinalConcurrency: DefTiDBHashAggFinalConcurrency, + windowConcurrency: DefTiDBWindowConcurrency, + mergeJoinConcurrency: DefTiDBMergeJoinConcurrency, + streamAggConcurrency: DefTiDBStreamAggConcurrency, + indexMergeIntersectionConcurrency: DefTiDBIndexMergeIntersectionConcurrency, + ExecutorConcurrency: DefExecutorConcurrency, + } + vars.MemQuota = MemQuota{ + MemQuotaQuery: DefTiDBMemQuotaQuery, + MemQuotaApplyCache: DefTiDBMemQuotaApplyCache, + } + vars.BatchSize = BatchSize{ + IndexJoinBatchSize: DefIndexJoinBatchSize, + IndexLookupSize: DefIndexLookupSize, + InitChunkSize: DefInitChunkSize, + MaxChunkSize: DefMaxChunkSize, + MinPagingSize: DefMinPagingSize, + MaxPagingSize: DefMaxPagingSize, + } + vars.DMLBatchSize = DefDMLBatchSize + vars.AllowBatchCop = DefTiDBAllowBatchCop + vars.allowMPPExecution = DefTiDBAllowMPPExecution + vars.HashExchangeWithNewCollation = DefTiDBHashExchangeWithNewCollation + vars.enforceMPPExecution = DefTiDBEnforceMPPExecution + vars.TiFlashMaxThreads = DefTiFlashMaxThreads + vars.TiFlashMaxBytesBeforeExternalJoin = DefTiFlashMaxBytesBeforeExternalJoin + vars.TiFlashMaxBytesBeforeExternalGroupBy = DefTiFlashMaxBytesBeforeExternalGroupBy + vars.TiFlashMaxBytesBeforeExternalSort = DefTiFlashMaxBytesBeforeExternalSort + vars.TiFlashMaxQueryMemoryPerNode = DefTiFlashMemQuotaQueryPerNode + vars.TiFlashQuerySpillRatio = DefTiFlashQuerySpillRatio + vars.MPPStoreFailTTL = DefTiDBMPPStoreFailTTL + vars.DiskTracker = disk.NewTracker(memory.LabelForSession, -1) + vars.MemTracker = memory.NewTracker(memory.LabelForSession, vars.MemQuotaQuery) + vars.MemTracker.IsRootTrackerOfSess = true + + for _, engine := range config.GetGlobalConfig().IsolationRead.Engines { + switch engine { + case kv.TiFlash.Name(): + vars.IsolationReadEngines[kv.TiFlash] = struct{}{} + case kv.TiKV.Name(): + vars.IsolationReadEngines[kv.TiKV] = struct{}{} + case kv.TiDB.Name(): + vars.IsolationReadEngines[kv.TiDB] = struct{}{} + } + } + if !EnableLocalTxn.Load() { + vars.TxnScope = kv.NewGlobalTxnScopeVar() + } + if EnableRowLevelChecksum.Load() { + vars.EnableRowLevelChecksum = true + } + vars.systems[CharacterSetConnection], vars.systems[CollationConnection] = charset.GetDefaultCharsetAndCollate() + return vars +} + +// GetAllowInSubqToJoinAndAgg get AllowInSubqToJoinAndAgg from sql hints and SessionVars.allowInSubqToJoinAndAgg. +func (s *SessionVars) GetAllowInSubqToJoinAndAgg() bool { + if s.StmtCtx.HasAllowInSubqToJoinAndAggHint { + return s.StmtCtx.AllowInSubqToJoinAndAgg + } + return s.allowInSubqToJoinAndAgg +} + +// SetAllowInSubqToJoinAndAgg set SessionVars.allowInSubqToJoinAndAgg. +func (s *SessionVars) SetAllowInSubqToJoinAndAgg(val bool) { + s.allowInSubqToJoinAndAgg = val +} + +// GetAllowPreferRangeScan get preferRangeScan from SessionVars.preferRangeScan. +func (s *SessionVars) GetAllowPreferRangeScan() bool { + return s.preferRangeScan +} + +// SetAllowPreferRangeScan set SessionVars.preferRangeScan. +func (s *SessionVars) SetAllowPreferRangeScan(val bool) { + s.preferRangeScan = val +} + +// GetEnableCascadesPlanner get EnableCascadesPlanner from sql hints and SessionVars.EnableCascadesPlanner. +func (s *SessionVars) GetEnableCascadesPlanner() bool { + if s.StmtCtx.HasEnableCascadesPlannerHint { + return s.StmtCtx.EnableCascadesPlanner + } + return s.EnableCascadesPlanner +} + +// SetEnableCascadesPlanner set SessionVars.EnableCascadesPlanner. +func (s *SessionVars) SetEnableCascadesPlanner(val bool) { + s.EnableCascadesPlanner = val +} + +// GetEnableIndexMerge get EnableIndexMerge from SessionVars.enableIndexMerge. +func (s *SessionVars) GetEnableIndexMerge() bool { + return s.enableIndexMerge +} + +// SetEnableIndexMerge set SessionVars.enableIndexMerge. +func (s *SessionVars) SetEnableIndexMerge(val bool) { + s.enableIndexMerge = val +} + +// GetEnablePseudoForOutdatedStats get EnablePseudoForOutdatedStats from SessionVars.EnablePseudoForOutdatedStats. +func (s *SessionVars) GetEnablePseudoForOutdatedStats() bool { + return s.EnablePseudoForOutdatedStats +} + +// SetEnablePseudoForOutdatedStats set SessionVars.EnablePseudoForOutdatedStats. +func (s *SessionVars) SetEnablePseudoForOutdatedStats(val bool) { + s.EnablePseudoForOutdatedStats = val +} + +// GetReplicaRead get ReplicaRead from sql hints and SessionVars.replicaRead. +func (s *SessionVars) GetReplicaRead() kv.ReplicaReadType { + if s.StmtCtx.HasReplicaReadHint { + return kv.ReplicaReadType(s.StmtCtx.ReplicaRead) + } + // if closest-adaptive is unavailable, fallback to leader read + if s.replicaRead == kv.ReplicaReadClosestAdaptive && !IsAdaptiveReplicaReadEnabled() { + return kv.ReplicaReadLeader + } + return s.replicaRead +} + +// SetReplicaRead set SessionVars.replicaRead. +func (s *SessionVars) SetReplicaRead(val kv.ReplicaReadType) { + s.replicaRead = val +} + +// IsReplicaReadClosestAdaptive returns whether adaptive closest replica can be enabled. +func (s *SessionVars) IsReplicaReadClosestAdaptive() bool { + return s.replicaRead == kv.ReplicaReadClosestAdaptive && IsAdaptiveReplicaReadEnabled() +} + +// GetWriteStmtBufs get pointer of SessionVars.writeStmtBufs. +func (s *SessionVars) GetWriteStmtBufs() *WriteStmtBufs { + return &s.writeStmtBufs +} + +// GetSplitRegionTimeout gets split region timeout. +func (s *SessionVars) GetSplitRegionTimeout() time.Duration { + return time.Duration(s.WaitSplitRegionTimeout) * time.Second +} + +// GetIsolationReadEngines gets isolation read engines. +func (s *SessionVars) GetIsolationReadEngines() map[kv.StoreType]struct{} { + return s.IsolationReadEngines +} + +// CleanBuffers cleans the temporary bufs +func (s *SessionVars) CleanBuffers() { + s.GetWriteStmtBufs().clean() +} + +// AllocPlanColumnID allocates column id for plan. +func (s *SessionVars) AllocPlanColumnID() int64 { + return s.PlanColumnID.Add(1) +} + +// RegisterScalarSubQ register a scalar sub query into the map. This will be used for EXPLAIN. +func (s *SessionVars) RegisterScalarSubQ(scalarSubQ interface{}) { + s.MapScalarSubQ = append(s.MapScalarSubQ, scalarSubQ) +} + +// GetCharsetInfo gets charset and collation for current context. +// What character set should the server translate a statement to after receiving it? +// For this, the server uses the character_set_connection and collation_connection system variables. +// It converts statements sent by the client from character_set_client to character_set_connection +// (except for string literals that have an introducer such as _latin1 or _utf8). +// collation_connection is important for comparisons of literal strings. +// For comparisons of strings with column values, collation_connection does not matter because columns +// have their own collation, which has a higher collation precedence. +// See https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html +func (s *SessionVars) GetCharsetInfo() (charset, collation string) { + charset = s.systems[CharacterSetConnection] + collation = s.systems[CollationConnection] + return +} + +// GetParseParams gets the parse parameters from session variables. +func (s *SessionVars) GetParseParams() []parser.ParseParam { + chs, coll := s.GetCharsetInfo() + cli, err := s.GetSessionOrGlobalSystemVar(context.Background(), CharacterSetClient) + if err != nil { + cli = "" + } + return []parser.ParseParam{ + parser.CharsetConnection(chs), + parser.CollationConnection(coll), + parser.CharsetClient(cli), + } +} + +// SetStringUserVar set the value and collation for user defined variable. +func (s *SessionVars) SetStringUserVar(name string, strVal string, collation string) { + name = strings.ToLower(name) + if len(collation) > 0 { + s.SetUserVarVal(name, types.NewCollationStringDatum(stringutil.Copy(strVal), collation)) + } else { + _, collation = s.GetCharsetInfo() + s.SetUserVarVal(name, types.NewCollationStringDatum(stringutil.Copy(strVal), collation)) + } +} + +// UnsetUserVar unset an user defined variable by name. +func (s *SessionVars) UnsetUserVar(varName string) { + varName = strings.ToLower(varName) + s.userVars.lock.Lock() + defer s.userVars.lock.Unlock() + delete(s.userVars.values, varName) + delete(s.userVars.types, varName) +} + +// SetLastInsertID saves the last insert id to the session context. +// TODO: we may store the result for last_insert_id sys var later. +func (s *SessionVars) SetLastInsertID(insertID uint64) { + s.StmtCtx.LastInsertID = insertID +} + +// SetStatusFlag sets the session server status variable. +// If on is true sets the flag in session status, +// otherwise removes the flag. +func (s *SessionVars) SetStatusFlag(flag uint16, on bool) { + if on { + s.Status |= flag + return + } + s.Status &= ^flag +} + +// GetStatusFlag gets the session server status variable, returns true if it is on. +func (s *SessionVars) GetStatusFlag(flag uint16) bool { + return s.Status&flag > 0 +} + +// SetInTxn sets whether the session is in transaction. +// It also updates the IsExplicit flag in TxnCtx if val is true. +func (s *SessionVars) SetInTxn(val bool) { + s.SetStatusFlag(mysql.ServerStatusInTrans, val) + if val { + s.TxnCtx.IsExplicit = val + } +} + +// InTxn returns if the session is in transaction. +func (s *SessionVars) InTxn() bool { + return s.GetStatusFlag(mysql.ServerStatusInTrans) +} + +// IsAutocommit returns if the session is set to autocommit. +func (s *SessionVars) IsAutocommit() bool { + return s.GetStatusFlag(mysql.ServerStatusAutocommit) +} + +// IsIsolation if true it means the transaction is at that isolation level. +func (s *SessionVars) IsIsolation(isolation string) bool { + if s.TxnCtx.Isolation != "" { + return s.TxnCtx.Isolation == isolation + } + if s.txnIsolationLevelOneShot.state == oneShotUse { + s.TxnCtx.Isolation = s.txnIsolationLevelOneShot.value + } + if s.TxnCtx.Isolation == "" { + s.TxnCtx.Isolation, _ = s.GetSystemVar(TxnIsolation) + } + return s.TxnCtx.Isolation == isolation +} + +// IsolationLevelForNewTxn returns the isolation level if we want to enter a new transaction +func (s *SessionVars) IsolationLevelForNewTxn() (isolation string) { + if s.InTxn() { + if s.txnIsolationLevelOneShot.state == oneShotSet { + isolation = s.txnIsolationLevelOneShot.value + } + } else { + if s.txnIsolationLevelOneShot.state == oneShotUse { + isolation = s.txnIsolationLevelOneShot.value + } + } + + if isolation == "" { + isolation, _ = s.GetSystemVar(TxnIsolation) + } + + return +} + +// SetTxnIsolationLevelOneShotStateForNextTxn sets the txnIsolationLevelOneShot.state for next transaction. +func (s *SessionVars) SetTxnIsolationLevelOneShotStateForNextTxn() { + if isoLevelOneShot := &s.txnIsolationLevelOneShot; isoLevelOneShot.state != oneShotDef { + switch isoLevelOneShot.state { + case oneShotSet: + isoLevelOneShot.state = oneShotUse + case oneShotUse: + isoLevelOneShot.state = oneShotDef + isoLevelOneShot.value = "" + } + } +} + +// IsPessimisticReadConsistency if true it means the statement is in an read consistency pessimistic transaction. +func (s *SessionVars) IsPessimisticReadConsistency() bool { + return s.TxnCtx.IsPessimistic && s.IsIsolation(ast.ReadCommitted) +} + +// GetNextPreparedStmtID generates and returns the next session scope prepared statement id. +func (s *SessionVars) GetNextPreparedStmtID() uint32 { + s.preparedStmtID++ + return s.preparedStmtID +} + +// SetNextPreparedStmtID sets the next prepared statement id. It's only used in restoring session states. +func (s *SessionVars) SetNextPreparedStmtID(preparedStmtID uint32) { + s.preparedStmtID = preparedStmtID +} + +// Location returns the value of time_zone session variable. If it is nil, then return time.Local. +func (s *SessionVars) Location() *time.Location { + loc := s.TimeZone + if loc == nil { + loc = timeutil.SystemLocation() + } + return loc +} + +// GetSystemVar gets the string value of a system variable. +func (s *SessionVars) GetSystemVar(name string) (string, bool) { + if name == WarningCount { + return strconv.Itoa(s.SysWarningCount), true + } else if name == ErrorCount { + return strconv.Itoa(int(s.SysErrorCount)), true + } + if val, ok := s.stmtVars[name]; ok { + return val, ok + } + val, ok := s.systems[name] + return val, ok +} + +func (s *SessionVars) setDDLReorgPriority(val string) { + val = strings.ToLower(val) + switch val { + case "priority_low": + s.DDLReorgPriority = kv.PriorityLow + case "priority_normal": + s.DDLReorgPriority = kv.PriorityNormal + case "priority_high": + s.DDLReorgPriority = kv.PriorityHigh + default: + s.DDLReorgPriority = kv.PriorityLow + } +} + +type planCacheStmtKey string + +func (k planCacheStmtKey) Hash() []byte { + return []byte(k) +} + +// AddNonPreparedPlanCacheStmt adds this PlanCacheStmt into non-preapred plan-cache stmt cache +func (s *SessionVars) AddNonPreparedPlanCacheStmt(sql string, stmt interface{}) { + if s.nonPreparedPlanCacheStmts == nil { + s.nonPreparedPlanCacheStmts = kvcache.NewSimpleLRUCache(uint(s.SessionPlanCacheSize), 0, 0) + } + s.nonPreparedPlanCacheStmts.Put(planCacheStmtKey(sql), stmt) +} + +// GetNonPreparedPlanCacheStmt gets the PlanCacheStmt. +func (s *SessionVars) GetNonPreparedPlanCacheStmt(sql string) interface{} { + if s.nonPreparedPlanCacheStmts == nil { + return nil + } + stmt, _ := s.nonPreparedPlanCacheStmts.Get(planCacheStmtKey(sql)) + return stmt +} + +// AddPreparedStmt adds prepareStmt to current session and count in global. +func (s *SessionVars) AddPreparedStmt(stmtID uint32, stmt interface{}) error { + if _, exists := s.PreparedStmts[stmtID]; !exists { + maxPreparedStmtCount := MaxPreparedStmtCountValue.Load() + newPreparedStmtCount := atomic.AddInt64(&PreparedStmtCount, 1) + if maxPreparedStmtCount >= 0 && newPreparedStmtCount > maxPreparedStmtCount { + atomic.AddInt64(&PreparedStmtCount, -1) + return ErrMaxPreparedStmtCountReached.GenWithStackByArgs(maxPreparedStmtCount) + } + metrics.PreparedStmtGauge.Set(float64(newPreparedStmtCount)) + } + s.PreparedStmts[stmtID] = stmt + return nil +} + +// RemovePreparedStmt removes preparedStmt from current session and decrease count in global. +func (s *SessionVars) RemovePreparedStmt(stmtID uint32) { + _, exists := s.PreparedStmts[stmtID] + if !exists { + return + } + delete(s.PreparedStmts, stmtID) + afterMinus := atomic.AddInt64(&PreparedStmtCount, -1) + metrics.PreparedStmtGauge.Set(float64(afterMinus)) +} + +// WithdrawAllPreparedStmt remove all preparedStmt in current session and decrease count in global. +func (s *SessionVars) WithdrawAllPreparedStmt() { + psCount := len(s.PreparedStmts) + if psCount == 0 { + return + } + afterMinus := atomic.AddInt64(&PreparedStmtCount, -int64(psCount)) + metrics.PreparedStmtGauge.Set(float64(afterMinus)) +} + +// SetStmtVar sets the value of a system variable temporarily +func (s *SessionVars) setStmtVar(name string, val string) error { + s.stmtVars[name] = val + return nil +} + +// ClearStmtVars clear temporarily system variables. +func (s *SessionVars) ClearStmtVars() { + s.stmtVars = make(map[string]string) +} + +// GetSessionOrGlobalSystemVar gets a system variable. +// If it is a session only variable, use the default value defined in code. +// Returns error if there is no such variable. +func (s *SessionVars) GetSessionOrGlobalSystemVar(ctx context.Context, name string) (string, error) { + sv := GetSysVar(name) + if sv == nil { + return "", ErrUnknownSystemVar.GenWithStackByArgs(name) + } + if sv.HasNoneScope() { + return sv.Value, nil + } + if sv.HasSessionScope() { + // Populate the value to s.systems if it is not there already. + // in future should be already loaded on session init + if sv.GetSession != nil { + // shortcut to the getter, we won't use the value + return sv.GetSessionFromHook(s) + } + if _, ok := s.systems[sv.Name]; !ok { + if sv.HasGlobalScope() { + if val, err := s.GlobalVarsAccessor.GetGlobalSysVar(sv.Name); err == nil { + s.systems[sv.Name] = val + } + } else { + s.systems[sv.Name] = sv.Value // no global scope, use default + } + } + return sv.GetSessionFromHook(s) + } + return sv.GetGlobalFromHook(ctx, s) +} + +// GetSessionStatesSystemVar gets the session variable value for session states. +// It's only used for encoding session states when migrating a session. +// The returned boolean indicates whether to keep this value in the session states. +func (s *SessionVars) GetSessionStatesSystemVar(name string) (string, bool, error) { + sv := GetSysVar(name) + if sv == nil { + return "", false, ErrUnknownSystemVar.GenWithStackByArgs(name) + } + // Call GetStateValue first if it exists. Otherwise, call GetSession. + if sv.GetStateValue != nil { + return sv.GetStateValue(s) + } + if sv.GetSession != nil { + val, err := sv.GetSessionFromHook(s) + return val, err == nil, err + } + // Only get the cached value. No need to check the global or default value. + if val, ok := s.systems[sv.Name]; ok { + return val, true, nil + } + return "", false, nil +} + +// GetGlobalSystemVar gets a global system variable. +func (s *SessionVars) GetGlobalSystemVar(ctx context.Context, name string) (string, error) { + sv := GetSysVar(name) + if sv == nil { + return "", ErrUnknownSystemVar.GenWithStackByArgs(name) + } + return sv.GetGlobalFromHook(ctx, s) +} + +// SetSystemVar sets the value of a system variable for session scope. +// Values are automatically normalized (i.e. oN / on / 1 => ON) +// and the validation function is run. To set with less validation, see +// SetSystemVarWithRelaxedValidation. +func (s *SessionVars) SetSystemVar(name string, val string) error { + sv := GetSysVar(name) + if sv == nil { + return ErrUnknownSystemVar.GenWithStackByArgs(name) + } + val, err := sv.Validate(s, val, ScopeSession) + if err != nil { + return err + } + return sv.SetSessionFromHook(s, val) +} + +// SetSystemVarWithOldValAsRet is wrapper of SetSystemVar. Return the old value for later use. +func (s *SessionVars) SetSystemVarWithOldValAsRet(name string, val string) (string, error) { + sv := GetSysVar(name) + if sv == nil { + return "", ErrUnknownSystemVar.GenWithStackByArgs(name) + } + val, err := sv.Validate(s, val, ScopeSession) + if err != nil { + return "", err + } + // The map s.systems[sv.Name] is lazy initialized. If we directly read it, we might read empty result. + // Since this code path is not a hot path, we directly call GetSessionOrGlobalSystemVar to get the value safely. + oldV, err := s.GetSessionOrGlobalSystemVar(context.Background(), sv.Name) + if err != nil { + return "", err + } + return oldV, sv.SetSessionFromHook(s, val) +} + +// SetSystemVarWithoutValidation sets the value of a system variable for session scope. +// Deprecated: Values are NOT normalized or Validated. +func (s *SessionVars) SetSystemVarWithoutValidation(name string, val string) error { + sv := GetSysVar(name) + if sv == nil { + return ErrUnknownSystemVar.GenWithStackByArgs(name) + } + return sv.SetSessionFromHook(s, val) +} + +// SetSystemVarWithRelaxedValidation sets the value of a system variable for session scope. +// Validation functions are called, but scope validation is skipped. +// Errors are not expected to be returned because this could cause upgrade issues. +func (s *SessionVars) SetSystemVarWithRelaxedValidation(name string, val string) error { + sv := GetSysVar(name) + if sv == nil { + return ErrUnknownSystemVar.GenWithStackByArgs(name) + } + val = sv.ValidateWithRelaxedValidation(s, val, ScopeSession) + return sv.SetSessionFromHook(s, val) +} + +// GetReadableTxnMode returns the session variable TxnMode but rewrites it to "OPTIMISTIC" when it's empty. +func (s *SessionVars) GetReadableTxnMode() string { + txnMode := s.TxnMode + if txnMode == "" { + txnMode = ast.Optimistic + } + return txnMode +} + +// SetPrevStmtDigest sets the digest of the previous statement. +func (s *SessionVars) SetPrevStmtDigest(prevStmtDigest string) { + s.prevStmtDigest = prevStmtDigest +} + +// GetPrevStmtDigest returns the digest of the previous statement. +func (s *SessionVars) GetPrevStmtDigest() string { + // Because `prevStmt` may be truncated, so it's senseless to normalize it. + // Even if `prevStmtDigest` is empty but `prevStmt` is not, just return it anyway. + return s.prevStmtDigest +} + +// LazyCheckKeyNotExists returns if we can lazy check key not exists. +func (s *SessionVars) LazyCheckKeyNotExists() bool { + return s.PresumeKeyNotExists || (s.TxnCtx != nil && s.TxnCtx.IsPessimistic && !s.StmtCtx.DupKeyAsWarning) +} + +// GetTemporaryTable returns a TempTable by tableInfo. +func (s *SessionVars) GetTemporaryTable(tblInfo *model.TableInfo) tableutil.TempTable { + if tblInfo.TempTableType != model.TempTableNone { + if s.TxnCtx.TemporaryTables == nil { + s.TxnCtx.TemporaryTables = make(map[int64]tableutil.TempTable) + } + tempTables := s.TxnCtx.TemporaryTables + tempTable, ok := tempTables[tblInfo.ID] + if !ok { + tempTable = tableutil.TempTableFromMeta(tblInfo) + tempTables[tblInfo.ID] = tempTable + } + return tempTable + } + + return nil +} + +// EncodeSessionStates saves session states into SessionStates. +func (s *SessionVars) EncodeSessionStates(_ context.Context, sessionStates *sessionstates.SessionStates) (err error) { + // Encode user-defined variables. + s.userVars.lock.RLock() + sessionStates.UserVars = make(map[string]*types.Datum, len(s.userVars.values)) + sessionStates.UserVarTypes = make(map[string]*ptypes.FieldType, len(s.userVars.types)) + for name, userVar := range s.userVars.values { + sessionStates.UserVars[name] = userVar.Clone() + } + for name, userVarType := range s.userVars.types { + sessionStates.UserVarTypes[name] = userVarType.Clone() + } + s.userVars.lock.RUnlock() + + // Encode other session contexts. + sessionStates.PreparedStmtID = s.preparedStmtID + sessionStates.Status = s.Status + sessionStates.CurrentDB = s.CurrentDB + sessionStates.LastTxnInfo = s.LastTxnInfo + if s.LastQueryInfo.StartTS != 0 { + sessionStates.LastQueryInfo = &s.LastQueryInfo + } + if s.LastDDLInfo.SeqNum != 0 { + sessionStates.LastDDLInfo = &s.LastDDLInfo + } + sessionStates.LastFoundRows = s.LastFoundRows + sessionStates.SequenceLatestValues = s.SequenceState.GetAllStates() + sessionStates.FoundInPlanCache = s.PrevFoundInPlanCache + sessionStates.FoundInBinding = s.PrevFoundInBinding + sessionStates.ResourceGroupName = s.ResourceGroupName + sessionStates.HypoIndexes = s.HypoIndexes + sessionStates.HypoTiFlashReplicas = s.HypoTiFlashReplicas + + // Encode StatementContext. We encode it here to avoid circle dependency. + sessionStates.LastAffectedRows = s.StmtCtx.PrevAffectedRows + sessionStates.LastInsertID = s.StmtCtx.PrevLastInsertID + sessionStates.Warnings = s.StmtCtx.GetWarnings() + return +} + +// DecodeSessionStates restores session states from SessionStates. +func (s *SessionVars) DecodeSessionStates(_ context.Context, sessionStates *sessionstates.SessionStates) (err error) { + // Decode user-defined variables. + for name, userVar := range sessionStates.UserVars { + s.SetUserVarVal(name, *userVar.Clone()) + } + for name, userVarType := range sessionStates.UserVarTypes { + s.SetUserVarType(name, userVarType.Clone()) + } + + // Decode other session contexts. + s.preparedStmtID = sessionStates.PreparedStmtID + s.Status = sessionStates.Status + s.CurrentDB = sessionStates.CurrentDB + s.LastTxnInfo = sessionStates.LastTxnInfo + if sessionStates.LastQueryInfo != nil { + s.LastQueryInfo = *sessionStates.LastQueryInfo + } + if sessionStates.LastDDLInfo != nil { + s.LastDDLInfo = *sessionStates.LastDDLInfo + } + s.LastFoundRows = sessionStates.LastFoundRows + s.SequenceState.SetAllStates(sessionStates.SequenceLatestValues) + s.FoundInPlanCache = sessionStates.FoundInPlanCache + s.FoundInBinding = sessionStates.FoundInBinding + s.ResourceGroupName = sessionStates.ResourceGroupName + s.HypoIndexes = sessionStates.HypoIndexes + s.HypoTiFlashReplicas = sessionStates.HypoTiFlashReplicas + + // Decode StatementContext. + s.StmtCtx.SetAffectedRows(uint64(sessionStates.LastAffectedRows)) + s.StmtCtx.PrevLastInsertID = sessionStates.LastInsertID + s.StmtCtx.SetWarnings(sessionStates.Warnings) + return +} + +// TableDelta stands for the changed count for one table or partition. +type TableDelta struct { + Delta int64 + Count int64 + ColSize map[int64]int64 + InitTime time.Time // InitTime is the time that this delta is generated. + TableID int64 +} + +// Clone returns a cloned TableDelta. +func (td TableDelta) Clone() TableDelta { + colSize := make(map[int64]int64, len(td.ColSize)) + maps.Copy(colSize, td.ColSize) + return TableDelta{ + Delta: td.Delta, + Count: td.Count, + ColSize: colSize, + InitTime: td.InitTime, + TableID: td.TableID, + } +} + +// ConcurrencyUnset means the value the of the concurrency related variable is unset. +const ConcurrencyUnset = -1 + +// Concurrency defines concurrency values. +type Concurrency struct { + // indexLookupConcurrency is the number of concurrent index lookup worker. + // indexLookupConcurrency is deprecated, use ExecutorConcurrency instead. + indexLookupConcurrency int + + // indexLookupJoinConcurrency is the number of concurrent index lookup join inner worker. + // indexLookupJoinConcurrency is deprecated, use ExecutorConcurrency instead. + indexLookupJoinConcurrency int + + // distSQLScanConcurrency is the number of concurrent dist SQL scan worker. + distSQLScanConcurrency int + + // hashJoinConcurrency is the number of concurrent hash join outer worker. + // hashJoinConcurrency is deprecated, use ExecutorConcurrency instead. + hashJoinConcurrency int + + // projectionConcurrency is the number of concurrent projection worker. + // projectionConcurrency is deprecated, use ExecutorConcurrency instead. + projectionConcurrency int + + // hashAggPartialConcurrency is the number of concurrent hash aggregation partial worker. + // hashAggPartialConcurrency is deprecated, use ExecutorConcurrency instead. + hashAggPartialConcurrency int + + // hashAggFinalConcurrency is the number of concurrent hash aggregation final worker. + // hashAggFinalConcurrency is deprecated, use ExecutorConcurrency instead. + hashAggFinalConcurrency int + + // windowConcurrency is the number of concurrent window worker. + // windowConcurrency is deprecated, use ExecutorConcurrency instead. + windowConcurrency int + + // mergeJoinConcurrency is the number of concurrent merge join worker + mergeJoinConcurrency int + + // streamAggConcurrency is the number of concurrent stream aggregation worker. + // streamAggConcurrency is deprecated, use ExecutorConcurrency instead. + streamAggConcurrency int + + // indexMergeIntersectionConcurrency is the number of indexMergeProcessWorker + // Only meaningful for dynamic pruned partition table. + indexMergeIntersectionConcurrency int + + // indexSerialScanConcurrency is the number of concurrent index serial scan worker. + indexSerialScanConcurrency int + + // ExecutorConcurrency is the number of concurrent worker for all executors. + ExecutorConcurrency int + + // SourceAddr is the source address of request. Available in coprocessor ONLY. + SourceAddr net.TCPAddr +} + +// SetIndexLookupConcurrency set the number of concurrent index lookup worker. +func (c *Concurrency) SetIndexLookupConcurrency(n int) { + c.indexLookupConcurrency = n +} + +// SetIndexLookupJoinConcurrency set the number of concurrent index lookup join inner worker. +func (c *Concurrency) SetIndexLookupJoinConcurrency(n int) { + c.indexLookupJoinConcurrency = n +} + +// SetDistSQLScanConcurrency set the number of concurrent dist SQL scan worker. +func (c *Concurrency) SetDistSQLScanConcurrency(n int) { + c.distSQLScanConcurrency = n +} + +// SetHashJoinConcurrency set the number of concurrent hash join outer worker. +func (c *Concurrency) SetHashJoinConcurrency(n int) { + c.hashJoinConcurrency = n +} + +// SetProjectionConcurrency set the number of concurrent projection worker. +func (c *Concurrency) SetProjectionConcurrency(n int) { + c.projectionConcurrency = n +} + +// SetHashAggPartialConcurrency set the number of concurrent hash aggregation partial worker. +func (c *Concurrency) SetHashAggPartialConcurrency(n int) { + c.hashAggPartialConcurrency = n +} + +// SetHashAggFinalConcurrency set the number of concurrent hash aggregation final worker. +func (c *Concurrency) SetHashAggFinalConcurrency(n int) { + c.hashAggFinalConcurrency = n +} + +// SetWindowConcurrency set the number of concurrent window worker. +func (c *Concurrency) SetWindowConcurrency(n int) { + c.windowConcurrency = n +} + +// SetMergeJoinConcurrency set the number of concurrent merge join worker. +func (c *Concurrency) SetMergeJoinConcurrency(n int) { + c.mergeJoinConcurrency = n +} + +// SetStreamAggConcurrency set the number of concurrent stream aggregation worker. +func (c *Concurrency) SetStreamAggConcurrency(n int) { + c.streamAggConcurrency = n +} + +// SetIndexMergeIntersectionConcurrency set the number of concurrent intersection process worker. +func (c *Concurrency) SetIndexMergeIntersectionConcurrency(n int) { + c.indexMergeIntersectionConcurrency = n +} + +// SetIndexSerialScanConcurrency set the number of concurrent index serial scan worker. +func (c *Concurrency) SetIndexSerialScanConcurrency(n int) { + c.indexSerialScanConcurrency = n +} + +// IndexLookupConcurrency return the number of concurrent index lookup worker. +func (c *Concurrency) IndexLookupConcurrency() int { + if c.indexLookupConcurrency != ConcurrencyUnset { + return c.indexLookupConcurrency + } + return c.ExecutorConcurrency +} + +// IndexLookupJoinConcurrency return the number of concurrent index lookup join inner worker. +func (c *Concurrency) IndexLookupJoinConcurrency() int { + if c.indexLookupJoinConcurrency != ConcurrencyUnset { + return c.indexLookupJoinConcurrency + } + return c.ExecutorConcurrency +} + +// DistSQLScanConcurrency return the number of concurrent dist SQL scan worker. +func (c *Concurrency) DistSQLScanConcurrency() int { + return c.distSQLScanConcurrency +} + +// HashJoinConcurrency return the number of concurrent hash join outer worker. +func (c *Concurrency) HashJoinConcurrency() int { + if c.hashJoinConcurrency != ConcurrencyUnset { + return c.hashJoinConcurrency + } + return c.ExecutorConcurrency +} + +// ProjectionConcurrency return the number of concurrent projection worker. +func (c *Concurrency) ProjectionConcurrency() int { + if c.projectionConcurrency != ConcurrencyUnset { + return c.projectionConcurrency + } + return c.ExecutorConcurrency +} + +// HashAggPartialConcurrency return the number of concurrent hash aggregation partial worker. +func (c *Concurrency) HashAggPartialConcurrency() int { + if c.hashAggPartialConcurrency != ConcurrencyUnset { + return c.hashAggPartialConcurrency + } + return c.ExecutorConcurrency +} + +// HashAggFinalConcurrency return the number of concurrent hash aggregation final worker. +func (c *Concurrency) HashAggFinalConcurrency() int { + if c.hashAggFinalConcurrency != ConcurrencyUnset { + return c.hashAggFinalConcurrency + } + return c.ExecutorConcurrency +} + +// WindowConcurrency return the number of concurrent window worker. +func (c *Concurrency) WindowConcurrency() int { + if c.windowConcurrency != ConcurrencyUnset { + return c.windowConcurrency + } + return c.ExecutorConcurrency +} + +// MergeJoinConcurrency return the number of concurrent merge join worker. +func (c *Concurrency) MergeJoinConcurrency() int { + if c.mergeJoinConcurrency != ConcurrencyUnset { + return c.mergeJoinConcurrency + } + return c.ExecutorConcurrency +} + +// StreamAggConcurrency return the number of concurrent stream aggregation worker. +func (c *Concurrency) StreamAggConcurrency() int { + if c.streamAggConcurrency != ConcurrencyUnset { + return c.streamAggConcurrency + } + return c.ExecutorConcurrency +} + +// IndexMergeIntersectionConcurrency return the number of concurrent process worker. +func (c *Concurrency) IndexMergeIntersectionConcurrency() int { + if c.indexMergeIntersectionConcurrency != ConcurrencyUnset { + return c.indexMergeIntersectionConcurrency + } + return c.ExecutorConcurrency +} + +// IndexSerialScanConcurrency return the number of concurrent index serial scan worker. +// This option is not sync with ExecutorConcurrency since it's used by Analyze table. +func (c *Concurrency) IndexSerialScanConcurrency() int { + return c.indexSerialScanConcurrency +} + +// UnionConcurrency return the num of concurrent union worker. +func (c *Concurrency) UnionConcurrency() int { + return c.ExecutorConcurrency +} + +// MemQuota defines memory quota values. +type MemQuota struct { + // MemQuotaQuery defines the memory quota for a query. + MemQuotaQuery int64 + // MemQuotaApplyCache defines the memory capacity for apply cache. + MemQuotaApplyCache int64 +} + +// BatchSize defines batch size values. +type BatchSize struct { + // IndexJoinBatchSize is the batch size of a index lookup join. + IndexJoinBatchSize int + + // IndexLookupSize is the number of handles for an index lookup task in index double read executor. + IndexLookupSize int + + // InitChunkSize defines init row count of a Chunk during query execution. + InitChunkSize int + + // MaxChunkSize defines max row count of a Chunk during query execution. + MaxChunkSize int + + // MinPagingSize defines the min size used by the coprocessor paging protocol. + MinPagingSize int + + // MinPagingSize defines the max size used by the coprocessor paging protocol. + MaxPagingSize int +} + +const ( + // SlowLogRowPrefixStr is slow log row prefix. + SlowLogRowPrefixStr = "# " + // SlowLogSpaceMarkStr is slow log space mark. + SlowLogSpaceMarkStr = ": " + // SlowLogSQLSuffixStr is slow log suffix. + SlowLogSQLSuffixStr = ";" + // SlowLogTimeStr is slow log field name. + SlowLogTimeStr = "Time" + // SlowLogStartPrefixStr is slow log start row prefix. + SlowLogStartPrefixStr = SlowLogRowPrefixStr + SlowLogTimeStr + SlowLogSpaceMarkStr + // SlowLogTxnStartTSStr is slow log field name. + SlowLogTxnStartTSStr = "Txn_start_ts" + // SlowLogKeyspaceName is slow log field name. + SlowLogKeyspaceName = "Keyspace_name" + // SlowLogKeyspaceID is slow log field name. + SlowLogKeyspaceID = "Keyspace_ID" + // SlowLogUserAndHostStr is the user and host field name, which is compatible with MySQL. + SlowLogUserAndHostStr = "User@Host" + // SlowLogUserStr is slow log field name. + SlowLogUserStr = "User" + // SlowLogHostStr only for slow_query table usage. + SlowLogHostStr = "Host" + // SlowLogConnIDStr is slow log field name. + SlowLogConnIDStr = "Conn_ID" + // SlowLogSessAliasStr is the session alias set by user + SlowLogSessAliasStr = "Session_alias" + // SlowLogQueryTimeStr is slow log field name. + SlowLogQueryTimeStr = "Query_time" + // SlowLogParseTimeStr is the parse sql time. + SlowLogParseTimeStr = "Parse_time" + // SlowLogCompileTimeStr is the compile plan time. + SlowLogCompileTimeStr = "Compile_time" + // SlowLogRewriteTimeStr is the rewrite time. + SlowLogRewriteTimeStr = "Rewrite_time" + // SlowLogOptimizeTimeStr is the optimization time. + SlowLogOptimizeTimeStr = "Optimize_time" + // SlowLogWaitTSTimeStr is the time of waiting TS. + SlowLogWaitTSTimeStr = "Wait_TS" + // SlowLogPreprocSubQueriesStr is the number of pre-processed sub-queries. + SlowLogPreprocSubQueriesStr = "Preproc_subqueries" + // SlowLogPreProcSubQueryTimeStr is the total time of pre-processing sub-queries. + SlowLogPreProcSubQueryTimeStr = "Preproc_subqueries_time" + // SlowLogDBStr is slow log field name. + SlowLogDBStr = "DB" + // SlowLogIsInternalStr is slow log field name. + SlowLogIsInternalStr = "Is_internal" + // SlowLogIndexNamesStr is slow log field name. + SlowLogIndexNamesStr = "Index_names" + // SlowLogDigestStr is slow log field name. + SlowLogDigestStr = "Digest" + // SlowLogQuerySQLStr is slow log field name. + SlowLogQuerySQLStr = "Query" // use for slow log table, slow log will not print this field name but print sql directly. + // SlowLogStatsInfoStr is plan stats info. + SlowLogStatsInfoStr = "Stats" + // SlowLogNumCopTasksStr is the number of cop-tasks. + SlowLogNumCopTasksStr = "Num_cop_tasks" + // SlowLogCopProcAvg is the average process time of all cop-tasks. + SlowLogCopProcAvg = "Cop_proc_avg" + // SlowLogCopProcP90 is the p90 process time of all cop-tasks. + SlowLogCopProcP90 = "Cop_proc_p90" + // SlowLogCopProcMax is the max process time of all cop-tasks. + SlowLogCopProcMax = "Cop_proc_max" + // SlowLogCopProcAddr is the address of TiKV where the cop-task which cost max process time run. + SlowLogCopProcAddr = "Cop_proc_addr" + // SlowLogCopWaitAvg is the average wait time of all cop-tasks. + SlowLogCopWaitAvg = "Cop_wait_avg" // #nosec G101 + // SlowLogCopWaitP90 is the p90 wait time of all cop-tasks. + SlowLogCopWaitP90 = "Cop_wait_p90" // #nosec G101 + // SlowLogCopWaitMax is the max wait time of all cop-tasks. + SlowLogCopWaitMax = "Cop_wait_max" + // SlowLogCopWaitAddr is the address of TiKV where the cop-task which cost wait process time run. + SlowLogCopWaitAddr = "Cop_wait_addr" // #nosec G101 + // SlowLogCopBackoffPrefix contains backoff information. + SlowLogCopBackoffPrefix = "Cop_backoff_" + // SlowLogMemMax is the max number bytes of memory used in this statement. + SlowLogMemMax = "Mem_max" + // SlowLogDiskMax is the nax number bytes of disk used in this statement. + SlowLogDiskMax = "Disk_max" + // SlowLogPrepared is used to indicate whether this sql execute in prepare. + SlowLogPrepared = "Prepared" + // SlowLogPlanFromCache is used to indicate whether this plan is from plan cache. + SlowLogPlanFromCache = "Plan_from_cache" + // SlowLogPlanFromBinding is used to indicate whether this plan is matched with the hints in the binding. + SlowLogPlanFromBinding = "Plan_from_binding" + // SlowLogHasMoreResults is used to indicate whether this sql has more following results. + SlowLogHasMoreResults = "Has_more_results" + // SlowLogSucc is used to indicate whether this sql execute successfully. + SlowLogSucc = "Succ" + // SlowLogPrevStmt is used to show the previous executed statement. + SlowLogPrevStmt = "Prev_stmt" + // SlowLogPlan is used to record the query plan. + SlowLogPlan = "Plan" + // SlowLogPlanDigest is used to record the query plan digest. + SlowLogPlanDigest = "Plan_digest" + // SlowLogBinaryPlan is used to record the binary plan. + SlowLogBinaryPlan = "Binary_plan" + // SlowLogPlanPrefix is the prefix of the plan value. + SlowLogPlanPrefix = ast.TiDBDecodePlan + "('" + // SlowLogBinaryPlanPrefix is the prefix of the binary plan value. + SlowLogBinaryPlanPrefix = ast.TiDBDecodeBinaryPlan + "('" + // SlowLogPlanSuffix is the suffix of the plan value. + SlowLogPlanSuffix = "')" + // SlowLogPrevStmtPrefix is the prefix of Prev_stmt in slow log file. + SlowLogPrevStmtPrefix = SlowLogPrevStmt + SlowLogSpaceMarkStr + // SlowLogKVTotal is the total time waiting for kv. + SlowLogKVTotal = "KV_total" + // SlowLogPDTotal is the total time waiting for pd. + SlowLogPDTotal = "PD_total" + // SlowLogBackoffTotal is the total time doing backoff. + SlowLogBackoffTotal = "Backoff_total" + // SlowLogWriteSQLRespTotal is the total time used to write response to client. + SlowLogWriteSQLRespTotal = "Write_sql_response_total" + // SlowLogExecRetryCount is the execution retry count. + SlowLogExecRetryCount = "Exec_retry_count" + // SlowLogExecRetryTime is the execution retry time. + SlowLogExecRetryTime = "Exec_retry_time" + // SlowLogBackoffDetail is the detail of backoff. + SlowLogBackoffDetail = "Backoff_Detail" + // SlowLogResultRows is the row count of the SQL result. + SlowLogResultRows = "Result_rows" + // SlowLogWarnings is the warnings generated during executing the statement. + // Note that some extra warnings would also be printed through slow log. + SlowLogWarnings = "Warnings" + // SlowLogIsExplicitTxn is used to indicate whether this sql execute in explicit transaction or not. + SlowLogIsExplicitTxn = "IsExplicitTxn" + // SlowLogIsWriteCacheTable is used to indicate whether writing to the cache table need to wait for the read lock to expire. + SlowLogIsWriteCacheTable = "IsWriteCacheTable" + // SlowLogIsSyncStatsFailed is used to indicate whether any failure happen during sync stats + SlowLogIsSyncStatsFailed = "IsSyncStatsFailed" +) + +// GenerateBinaryPlan decides whether we should record binary plan in slow log and stmt summary. +// It's controlled by the global variable `tidb_generate_binary_plan`. +var GenerateBinaryPlan atomic2.Bool + +// JSONSQLWarnForSlowLog helps to print the SQLWarn through the slow log in JSON format. +type JSONSQLWarnForSlowLog struct { + Level string + Message string + // IsExtra means this SQL Warn is expected to be recorded only under some conditions (like in EXPLAIN) and should + // haven't been recorded as a warning now, but we recorded it anyway to help diagnostics. + IsExtra bool `json:",omitempty"` +} + +// SlowQueryLogItems is a collection of items that should be included in the +// slow query log. +type SlowQueryLogItems struct { + TxnTS uint64 + KeyspaceName string + KeyspaceID uint32 + SQL string + Digest string + TimeTotal time.Duration + TimeParse time.Duration + TimeCompile time.Duration + TimeOptimize time.Duration + TimeWaitTS time.Duration + IndexNames string + CopTasks *stmtctx.CopTasksDetails + ExecDetail execdetails.ExecDetails + MemMax int64 + DiskMax int64 + Succ bool + Prepared bool + PlanFromCache bool + PlanFromBinding bool + HasMoreResults bool + PrevStmt string + Plan string + PlanDigest string + BinaryPlan string + RewriteInfo RewritePhaseInfo + KVTotal time.Duration + PDTotal time.Duration + BackoffTotal time.Duration + WriteSQLRespTotal time.Duration + ExecRetryCount uint + ExecRetryTime time.Duration + ResultRows int64 + IsExplicitTxn bool + IsWriteCacheTable bool + UsedStats map[int64]*stmtctx.UsedStatsInfoForTable + IsSyncStatsFailed bool + Warnings []JSONSQLWarnForSlowLog +} + +// SlowLogFormat uses for formatting slow log. +// The slow log output is like below: +// # Time: 2019-04-28T15:24:04.309074+08:00 +// # Txn_start_ts: 406315658548871171 +// # Keyspace_name: keyspace_a +// # Keyspace_ID: 1 +// # User@Host: root[root] @ localhost [127.0.0.1] +// # Conn_ID: 6 +// # Query_time: 4.895492 +// # Process_time: 0.161 Request_count: 1 Total_keys: 100001 Processed_keys: 100000 +// # DB: test +// # Index_names: [t1.idx1,t2.idx2] +// # Is_internal: false +// # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 +// # Stats: t1:1,t2:2 +// # Num_cop_tasks: 10 +// # Cop_process: Avg_time: 1s P90_time: 2s Max_time: 3s Max_addr: 10.6.131.78 +// # Cop_wait: Avg_time: 10ms P90_time: 20ms Max_time: 30ms Max_Addr: 10.6.131.79 +// # Memory_max: 4096 +// # Disk_max: 65535 +// # Succ: true +// # Prev_stmt: begin; +// select * from t_slim; +func (s *SessionVars) SlowLogFormat(logItems *SlowQueryLogItems) string { + var buf bytes.Buffer + + writeSlowLogItem(&buf, SlowLogTxnStartTSStr, strconv.FormatUint(logItems.TxnTS, 10)) + if logItems.KeyspaceName != "" { + writeSlowLogItem(&buf, SlowLogKeyspaceName, logItems.KeyspaceName) + writeSlowLogItem(&buf, SlowLogKeyspaceID, fmt.Sprintf("%d", logItems.KeyspaceID)) + } + + if s.User != nil { + hostAddress := s.User.Hostname + if s.ConnectionInfo != nil { + hostAddress = s.ConnectionInfo.ClientIP + } + writeSlowLogItem(&buf, SlowLogUserAndHostStr, fmt.Sprintf("%s[%s] @ %s [%s]", s.User.Username, s.User.Username, s.User.Hostname, hostAddress)) + } + if s.ConnectionID != 0 { + writeSlowLogItem(&buf, SlowLogConnIDStr, strconv.FormatUint(s.ConnectionID, 10)) + } + if s.SessionAlias != "" { + writeSlowLogItem(&buf, SlowLogSessAliasStr, s.SessionAlias) + } + if logItems.ExecRetryCount > 0 { + buf.WriteString(SlowLogRowPrefixStr) + buf.WriteString(SlowLogExecRetryTime) + buf.WriteString(SlowLogSpaceMarkStr) + buf.WriteString(strconv.FormatFloat(logItems.ExecRetryTime.Seconds(), 'f', -1, 64)) + buf.WriteString(" ") + buf.WriteString(SlowLogExecRetryCount) + buf.WriteString(SlowLogSpaceMarkStr) + buf.WriteString(strconv.Itoa(int(logItems.ExecRetryCount))) + buf.WriteString("\n") + } + writeSlowLogItem(&buf, SlowLogQueryTimeStr, strconv.FormatFloat(logItems.TimeTotal.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogParseTimeStr, strconv.FormatFloat(logItems.TimeParse.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogCompileTimeStr, strconv.FormatFloat(logItems.TimeCompile.Seconds(), 'f', -1, 64)) + + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v", SlowLogRewriteTimeStr, + SlowLogSpaceMarkStr, strconv.FormatFloat(logItems.RewriteInfo.DurationRewrite.Seconds(), 'f', -1, 64))) + if logItems.RewriteInfo.PreprocessSubQueries > 0 { + buf.WriteString(fmt.Sprintf(" %v%v%v %v%v%v", SlowLogPreprocSubQueriesStr, SlowLogSpaceMarkStr, logItems.RewriteInfo.PreprocessSubQueries, + SlowLogPreProcSubQueryTimeStr, SlowLogSpaceMarkStr, strconv.FormatFloat(logItems.RewriteInfo.DurationPreprocessSubQuery.Seconds(), 'f', -1, 64))) + } + buf.WriteString("\n") + + writeSlowLogItem(&buf, SlowLogOptimizeTimeStr, strconv.FormatFloat(logItems.TimeOptimize.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogWaitTSTimeStr, strconv.FormatFloat(logItems.TimeWaitTS.Seconds(), 'f', -1, 64)) + + if execDetailStr := logItems.ExecDetail.String(); len(execDetailStr) > 0 { + buf.WriteString(SlowLogRowPrefixStr + execDetailStr + "\n") + } + + if len(s.CurrentDB) > 0 { + writeSlowLogItem(&buf, SlowLogDBStr, strings.ToLower(s.CurrentDB)) + } + if len(logItems.IndexNames) > 0 { + writeSlowLogItem(&buf, SlowLogIndexNamesStr, logItems.IndexNames) + } + + writeSlowLogItem(&buf, SlowLogIsInternalStr, strconv.FormatBool(s.InRestrictedSQL)) + if len(logItems.Digest) > 0 { + writeSlowLogItem(&buf, SlowLogDigestStr, logItems.Digest) + } + if len(logItems.UsedStats) > 0 { + buf.WriteString(SlowLogRowPrefixStr + SlowLogStatsInfoStr + SlowLogSpaceMarkStr) + firstComma := false + keys := maps.Keys(logItems.UsedStats) + slices.Sort(keys) + for _, id := range keys { + usedStatsForTbl := logItems.UsedStats[id] + if usedStatsForTbl == nil { + continue + } + if firstComma { + buf.WriteString(",") + } + usedStatsForTbl.WriteToSlowLog(&buf) + firstComma = true + } + + buf.WriteString("\n") + } + if logItems.CopTasks != nil { + writeSlowLogItem(&buf, SlowLogNumCopTasksStr, strconv.FormatInt(int64(logItems.CopTasks.NumCopTasks), 10)) + if logItems.CopTasks.NumCopTasks > 0 { + // make the result stable + backoffs := make([]string, 0, 3) + for backoff := range logItems.CopTasks.TotBackoffTimes { + backoffs = append(backoffs, backoff) + } + slices.Sort(backoffs) + + if logItems.CopTasks.NumCopTasks == 1 { + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v", + SlowLogCopProcAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgProcessTime.Seconds(), + SlowLogCopProcAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessAddress) + "\n") + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v", + SlowLogCopWaitAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgWaitTime.Seconds(), + SlowLogCopWaitAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitAddress) + "\n") + for _, backoff := range backoffs { + backoffPrefix := SlowLogCopBackoffPrefix + backoff + "_" + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v\n", + backoffPrefix+"total_times", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTimes[backoff], + backoffPrefix+"total_time", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTime[backoff].Seconds(), + )) + } + } else { + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v", + SlowLogCopProcAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgProcessTime.Seconds(), + SlowLogCopProcP90, SlowLogSpaceMarkStr, logItems.CopTasks.P90ProcessTime.Seconds(), + SlowLogCopProcMax, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessTime.Seconds(), + SlowLogCopProcAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessAddress) + "\n") + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v", + SlowLogCopWaitAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgWaitTime.Seconds(), + SlowLogCopWaitP90, SlowLogSpaceMarkStr, logItems.CopTasks.P90WaitTime.Seconds(), + SlowLogCopWaitMax, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitTime.Seconds(), + SlowLogCopWaitAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitAddress) + "\n") + for _, backoff := range backoffs { + backoffPrefix := SlowLogCopBackoffPrefix + backoff + "_" + buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v %v%v%v %v%v%v\n", + backoffPrefix+"total_times", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTimes[backoff], + backoffPrefix+"total_time", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTime[backoff].Seconds(), + backoffPrefix+"max_time", SlowLogSpaceMarkStr, logItems.CopTasks.MaxBackoffTime[backoff].Seconds(), + backoffPrefix+"max_addr", SlowLogSpaceMarkStr, logItems.CopTasks.MaxBackoffAddress[backoff], + backoffPrefix+"avg_time", SlowLogSpaceMarkStr, logItems.CopTasks.AvgBackoffTime[backoff].Seconds(), + backoffPrefix+"p90_time", SlowLogSpaceMarkStr, logItems.CopTasks.P90BackoffTime[backoff].Seconds(), + )) + } + } + } + } + if logItems.MemMax > 0 { + writeSlowLogItem(&buf, SlowLogMemMax, strconv.FormatInt(logItems.MemMax, 10)) + } + if logItems.DiskMax > 0 { + writeSlowLogItem(&buf, SlowLogDiskMax, strconv.FormatInt(logItems.DiskMax, 10)) + } + + writeSlowLogItem(&buf, SlowLogPrepared, strconv.FormatBool(logItems.Prepared)) + writeSlowLogItem(&buf, SlowLogPlanFromCache, strconv.FormatBool(logItems.PlanFromCache)) + writeSlowLogItem(&buf, SlowLogPlanFromBinding, strconv.FormatBool(logItems.PlanFromBinding)) + writeSlowLogItem(&buf, SlowLogHasMoreResults, strconv.FormatBool(logItems.HasMoreResults)) + writeSlowLogItem(&buf, SlowLogKVTotal, strconv.FormatFloat(logItems.KVTotal.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogPDTotal, strconv.FormatFloat(logItems.PDTotal.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogBackoffTotal, strconv.FormatFloat(logItems.BackoffTotal.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogWriteSQLRespTotal, strconv.FormatFloat(logItems.WriteSQLRespTotal.Seconds(), 'f', -1, 64)) + writeSlowLogItem(&buf, SlowLogResultRows, strconv.FormatInt(logItems.ResultRows, 10)) + if len(logItems.Warnings) > 0 { + buf.WriteString(SlowLogRowPrefixStr + SlowLogWarnings + SlowLogSpaceMarkStr) + jsonEncoder := json.NewEncoder(&buf) + jsonEncoder.SetEscapeHTML(false) + // Note that the Encode() will append a '\n' so we don't need to add another. + err := jsonEncoder.Encode(logItems.Warnings) + if err != nil { + buf.WriteString(err.Error()) + } + } + writeSlowLogItem(&buf, SlowLogSucc, strconv.FormatBool(logItems.Succ)) + writeSlowLogItem(&buf, SlowLogIsExplicitTxn, strconv.FormatBool(logItems.IsExplicitTxn)) + writeSlowLogItem(&buf, SlowLogIsSyncStatsFailed, strconv.FormatBool(logItems.IsSyncStatsFailed)) + if s.StmtCtx.WaitLockLeaseTime > 0 { + writeSlowLogItem(&buf, SlowLogIsWriteCacheTable, strconv.FormatBool(logItems.IsWriteCacheTable)) + } + if len(logItems.Plan) != 0 { + writeSlowLogItem(&buf, SlowLogPlan, logItems.Plan) + } + if len(logItems.PlanDigest) != 0 { + writeSlowLogItem(&buf, SlowLogPlanDigest, logItems.PlanDigest) + } + if len(logItems.BinaryPlan) != 0 { + writeSlowLogItem(&buf, SlowLogBinaryPlan, logItems.BinaryPlan) + } + if logItems.PrevStmt != "" { + writeSlowLogItem(&buf, SlowLogPrevStmt, logItems.PrevStmt) + } + + if s.CurrentDBChanged { + buf.WriteString(fmt.Sprintf("use %s;\n", strings.ToLower(s.CurrentDB))) + s.CurrentDBChanged = false + } + + buf.WriteString(logItems.SQL) + if len(logItems.SQL) == 0 || logItems.SQL[len(logItems.SQL)-1] != ';' { + buf.WriteString(";") + } + + return buf.String() +} + +// writeSlowLogItem writes a slow log item in the form of: "# ${key}:${value}" +func writeSlowLogItem(buf *bytes.Buffer, key, value string) { + buf.WriteString(SlowLogRowPrefixStr + key + SlowLogSpaceMarkStr + value + "\n") +} + +// TxnReadTS indicates the value and used situation for tx_read_ts +type TxnReadTS struct { + readTS uint64 + used bool +} + +// NewTxnReadTS creates TxnReadTS +func NewTxnReadTS(ts uint64) *TxnReadTS { + return &TxnReadTS{ + readTS: ts, + used: false, + } +} + +// UseTxnReadTS returns readTS, and mark used as true +func (t *TxnReadTS) UseTxnReadTS() uint64 { + if t == nil { + return 0 + } + t.used = true + return t.readTS +} + +// SetTxnReadTS update readTS, and refresh used +func (t *TxnReadTS) SetTxnReadTS(ts uint64) { + if t == nil { + return + } + t.used = false + t.readTS = ts +} + +// PeakTxnReadTS returns readTS +func (t *TxnReadTS) PeakTxnReadTS() uint64 { + if t == nil { + return 0 + } + return t.readTS +} + +// CleanupTxnReadTSIfUsed cleans txnReadTS if used +func (s *SessionVars) CleanupTxnReadTSIfUsed() { + if s.TxnReadTS == nil { + return + } + if s.TxnReadTS.used && s.TxnReadTS.readTS > 0 { + s.TxnReadTS = NewTxnReadTS(0) + s.SnapshotInfoschema = nil + } +} + +// GetCPUFactor returns the session variable cpuFactor +func (s *SessionVars) GetCPUFactor() float64 { + return s.cpuFactor +} + +// GetCopCPUFactor returns the session variable copCPUFactor +func (s *SessionVars) GetCopCPUFactor() float64 { + return s.copCPUFactor +} + +// GetMemoryFactor returns the session variable memoryFactor +func (s *SessionVars) GetMemoryFactor() float64 { + return s.memoryFactor +} + +// GetDiskFactor returns the session variable diskFactor +func (s *SessionVars) GetDiskFactor() float64 { + return s.diskFactor +} + +// GetConcurrencyFactor returns the session variable concurrencyFactor +func (s *SessionVars) GetConcurrencyFactor() float64 { + return s.concurrencyFactor +} + +// GetNetworkFactor returns the session variable networkFactor +// returns 0 when tbl is a temporary table. +func (s *SessionVars) GetNetworkFactor(tbl *model.TableInfo) float64 { + if tbl != nil { + if tbl.TempTableType != model.TempTableNone { + return 0 + } + } + return s.networkFactor +} + +// GetScanFactor returns the session variable scanFactor +// returns 0 when tbl is a temporary table. +func (s *SessionVars) GetScanFactor(tbl *model.TableInfo) float64 { + if tbl != nil { + if tbl.TempTableType != model.TempTableNone { + return 0 + } + } + return s.scanFactor +} + +// GetDescScanFactor returns the session variable descScanFactor +// returns 0 when tbl is a temporary table. +func (s *SessionVars) GetDescScanFactor(tbl *model.TableInfo) float64 { + if tbl != nil { + if tbl.TempTableType != model.TempTableNone { + return 0 + } + } + return s.descScanFactor +} + +// GetSeekFactor returns the session variable seekFactor +// returns 0 when tbl is a temporary table. +func (s *SessionVars) GetSeekFactor(tbl *model.TableInfo) float64 { + if tbl != nil { + if tbl.TempTableType != model.TempTableNone { + return 0 + } + } + return s.seekFactor +} + +// EnableEvalTopNEstimationForStrMatch means if we need to evaluate expression with TopN to improve estimation. +// Currently, it's only for string matching functions (like and regexp). +func (s *SessionVars) EnableEvalTopNEstimationForStrMatch() bool { + return s.DefaultStrMatchSelectivity == 0 +} + +// GetStrMatchDefaultSelectivity means the default selectivity for like and regexp. +// Note: 0 is a special value, which means the default selectivity is 0.1 and TopN assisted estimation is enabled. +func (s *SessionVars) GetStrMatchDefaultSelectivity() float64 { + if s.DefaultStrMatchSelectivity == 0 { + return 0.1 + } + return s.DefaultStrMatchSelectivity +} + +// GetNegateStrMatchDefaultSelectivity means the default selectivity for not like and not regexp. +// Note: +// +// 0 is a special value, which means the default selectivity is 0.9 and TopN assisted estimation is enabled. +// 0.8 (the default value) is also a special value. For backward compatibility, when the variable is set to 0.8, we +// keep the default selectivity of like/regexp and not like/regexp all 0.8. +func (s *SessionVars) GetNegateStrMatchDefaultSelectivity() float64 { + if s.DefaultStrMatchSelectivity == DefTiDBDefaultStrMatchSelectivity { + return DefTiDBDefaultStrMatchSelectivity + } + return 1 - s.GetStrMatchDefaultSelectivity() +} + +// GetRelatedTableForMDL gets the related table for metadata lock. +func (s *SessionVars) GetRelatedTableForMDL() *sync.Map { + s.TxnCtx.tdmLock.Lock() + defer s.TxnCtx.tdmLock.Unlock() + if s.TxnCtx.relatedTableForMDL == nil { + s.TxnCtx.relatedTableForMDL = new(sync.Map) + } + return s.TxnCtx.relatedTableForMDL +} + +// EnableForceInlineCTE returns the session variable enableForceInlineCTE +func (s *SessionVars) EnableForceInlineCTE() bool { + return s.enableForceInlineCTE +} + +// IsRuntimeFilterEnabled return runtime filter mode whether OFF +func (s *SessionVars) IsRuntimeFilterEnabled() bool { + return s.runtimeFilterMode != RFOff +} + +// GetRuntimeFilterTypes return the session variable runtimeFilterTypes +func (s *SessionVars) GetRuntimeFilterTypes() []RuntimeFilterType { + return s.runtimeFilterTypes +} + +// GetRuntimeFilterMode return the session variable runtimeFilterMode +func (s *SessionVars) GetRuntimeFilterMode() RuntimeFilterMode { + return s.runtimeFilterMode +} + +// GetTiKVClientReadTimeout returns readonly kv request timeout, prefer query hint over session variable +func (s *SessionVars) GetTiKVClientReadTimeout() uint64 { + return s.TiKVClientReadTimeout +} + +// RuntimeFilterType type of runtime filter "IN" +type RuntimeFilterType int64 + +// In type of runtime filter, like "t.k1 in (?)" +// MinMax type of runtime filter, like "t.k1 < ? and t.k1 > ?" +const ( + In RuntimeFilterType = iota + MinMax + // todo BloomFilter, bf/in +) + +// String convert Runtime Filter Type to String name +func (rfType RuntimeFilterType) String() string { + switch rfType { + case In: + return "IN" + case MinMax: + return "MIN_MAX" + default: + return "" + } +} + +// RuntimeFilterTypeStringToType convert RuntimeFilterTypeNameString to RuntimeFilterType +// If name is legal, it will return Runtime Filter Type and true +// Else, it will return -1 and false +// The second param means the convert is ok or not. Ture is ok, false means it is illegal name +// At present, we only support two names: "IN" and "MIN_MAX" +func RuntimeFilterTypeStringToType(name string) (RuntimeFilterType, bool) { + switch name { + case "IN": + return In, true + case "MIN_MAX": + return MinMax, true + default: + return -1, false + } +} + +// ToRuntimeFilterType convert session var value to RuntimeFilterType list +// If sessionVarValue is legal, it will return RuntimeFilterType list and true +// The second param means the convert is ok or not. Ture is ok, false means it is illegal value +// The legal value should be comma-separated, eg: "IN,MIN_MAX" +func ToRuntimeFilterType(sessionVarValue string) ([]RuntimeFilterType, bool) { + typeNameList := strings.Split(sessionVarValue, ",") + rfTypeMap := make(map[RuntimeFilterType]bool) + for _, typeName := range typeNameList { + rfType, ok := RuntimeFilterTypeStringToType(strings.ToUpper(typeName)) + if !ok { + return nil, ok + } + rfTypeMap[rfType] = true + } + rfTypeList := make([]RuntimeFilterType, 0, len(rfTypeMap)) + for rfType := range rfTypeMap { + rfTypeList = append(rfTypeList, rfType) + } + return rfTypeList, true +} + +// RuntimeFilterMode the mode of runtime filter "OFF", "LOCAL" +type RuntimeFilterMode int64 + +// RFOff disable runtime filter +// RFLocal enable local runtime filter +// RFGlobal enable local and global runtime filter +const ( + RFOff RuntimeFilterMode = iota + 1 + RFLocal + RFGlobal +) + +// String convert Runtime Filter Mode to String name +func (rfMode RuntimeFilterMode) String() string { + switch rfMode { + case RFOff: + return "OFF" + case RFLocal: + return "LOCAL" + case RFGlobal: + return "GLOBAL" + default: + return "" + } +} + +// RuntimeFilterModeStringToMode convert RuntimeFilterModeString to RuntimeFilterMode +// If name is legal, it will return Runtime Filter Mode and true +// Else, it will return -1 and false +// The second param means the convert is ok or not. Ture is ok, false means it is illegal name +// At present, we only support one name: "OFF", "LOCAL" +func RuntimeFilterModeStringToMode(name string) (RuntimeFilterMode, bool) { + switch name { + case "OFF": + return RFOff, true + case "LOCAL": + return RFLocal, true + default: + return -1, false + } +} + +const ( + // OptObjectiveModerate is a possible value and the default value for TiDBOptObjective. + // Please see comments of SessionVars.OptObjective for details. + OptObjectiveModerate string = "moderate" + // OptObjectiveDeterminate is a possible value for TiDBOptObjective. + OptObjectiveDeterminate = "determinate" +) + +// GetOptObjective return the session variable "tidb_opt_objective". +// Please see comments of SessionVars.OptObjective for details. +func (s *SessionVars) GetOptObjective() string { + return s.OptObjective +} diff --git a/pkg/sessionctx/variable/session_test.go b/pkg/sessionctx/variable/session_test.go new file mode 100644 index 0000000000000..b45046836d8fe --- /dev/null +++ b/pkg/sessionctx/variable/session_test.go @@ -0,0 +1,538 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package variable_test + +import ( + "context" + "strconv" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + util2 "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/util" +) + +func TestSetSystemVariable(t *testing.T) { + v := variable.NewSessionVars(nil) + v.GlobalVarsAccessor = variable.NewMockGlobalAccessor4Tests() + v.TimeZone = time.UTC + mtx := new(sync.Mutex) + + testCases := []struct { + key string + value string + err bool + }{ + {variable.TxnIsolation, "SERIALIZABLE", true}, + {variable.TimeZone, "xyz", true}, + {variable.TiDBOptAggPushDown, "1", false}, + {variable.TiDBOptDeriveTopN, "1", false}, + {variable.TiDBOptDistinctAggPushDown, "1", false}, + {variable.TiDBMemQuotaQuery, "1024", false}, + {variable.TiDBMemQuotaApplyCache, "1024", false}, + {variable.TiDBEnableStmtSummary, "1", true}, // now global only + {variable.TiDBEnableRowLevelChecksum, "1", true}, + } + + for _, tc := range testCases { + // copy iterator variable into a new variable, see issue #27779 + tc := tc + t.Run(tc.key, func(t *testing.T) { + mtx.Lock() + err := v.SetSystemVar(tc.key, tc.value) + mtx.Unlock() + if tc.err { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestSession(t *testing.T) { + ctx := mock.NewContext() + + ss := ctx.GetSessionVars().StmtCtx + require.NotNil(t, ss) + + // For AffectedRows + ss.AddAffectedRows(1) + require.Equal(t, uint64(1), ss.AffectedRows()) + ss.AddAffectedRows(1) + require.Equal(t, uint64(2), ss.AffectedRows()) + + // For RecordRows + ss.AddRecordRows(1) + require.Equal(t, uint64(1), ss.RecordRows()) + ss.AddRecordRows(1) + require.Equal(t, uint64(2), ss.RecordRows()) + + // For FoundRows + ss.AddFoundRows(1) + require.Equal(t, uint64(1), ss.FoundRows()) + ss.AddFoundRows(1) + require.Equal(t, uint64(2), ss.FoundRows()) + + // For UpdatedRows + ss.AddUpdatedRows(1) + require.Equal(t, uint64(1), ss.UpdatedRows()) + ss.AddUpdatedRows(1) + require.Equal(t, uint64(2), ss.UpdatedRows()) + + // For TouchedRows + ss.AddTouchedRows(1) + require.Equal(t, uint64(1), ss.TouchedRows()) + ss.AddTouchedRows(1) + require.Equal(t, uint64(2), ss.TouchedRows()) + + // For CopiedRows + ss.AddCopiedRows(1) + require.Equal(t, uint64(1), ss.CopiedRows()) + ss.AddCopiedRows(1) + require.Equal(t, uint64(2), ss.CopiedRows()) + + // For last insert id + ctx.GetSessionVars().SetLastInsertID(1) + require.Equal(t, uint64(1), ctx.GetSessionVars().StmtCtx.LastInsertID) + + ss.ResetForRetry() + require.Equal(t, uint64(0), ss.AffectedRows()) + require.Equal(t, uint64(0), ss.FoundRows()) + require.Equal(t, uint64(0), ss.UpdatedRows()) + require.Equal(t, uint64(0), ss.RecordRows()) + require.Equal(t, uint64(0), ss.TouchedRows()) + require.Equal(t, uint64(0), ss.CopiedRows()) + require.Equal(t, uint16(0), ss.WarningCount()) +} + +func TestAllocMPPID(t *testing.T) { + ctx := mock.NewContext() + require.Equal(t, int64(1), plannercore.AllocMPPTaskID(ctx)) + require.Equal(t, int64(2), plannercore.AllocMPPTaskID(ctx)) + require.Equal(t, int64(3), plannercore.AllocMPPTaskID(ctx)) +} + +func TestSlowLogFormat(t *testing.T) { + ctx := mock.NewContext() + + seVar := ctx.GetSessionVars() + require.NotNil(t, seVar) + + seVar.User = &auth.UserIdentity{Username: "root", Hostname: "192.168.0.1"} + seVar.ConnectionInfo = &variable.ConnectionInfo{ClientIP: "192.168.0.1"} + seVar.ConnectionID = 1 + seVar.SessionAlias = "aliasabc" + // the out put of the loged CurrentDB should be 'test', should be to lower cased. + seVar.CurrentDB = "TeST" + seVar.InRestrictedSQL = true + seVar.StmtCtx.WaitLockLeaseTime = 1 + txnTS := uint64(406649736972468225) + costTime := time.Second + execDetail := execdetails.ExecDetails{ + BackoffTime: time.Millisecond, + RequestCount: 2, + ScanDetail: &util.ScanDetail{ + ProcessedKeys: 20001, + TotalKeys: 10000, + }, + DetailsNeedP90: execdetails.DetailsNeedP90{ + TimeDetail: util.TimeDetail{ + ProcessTime: time.Second * time.Duration(2), + WaitTime: time.Minute, + }, + }, + } + usedStats1 := &stmtctx.UsedStatsInfoForTable{ + Name: "t1", + TblInfo: nil, + Version: 123, + RealtimeCount: 1000, + ModifyCount: 0, + ColumnStatsLoadStatus: map[int64]string{2: "allEvicted", 3: "onlyCmsEvicted"}, + IndexStatsLoadStatus: map[int64]string{1: "allLoaded", 2: "allLoaded"}, + } + usedStats2 := &stmtctx.UsedStatsInfoForTable{ + Name: "t2", + TblInfo: nil, + Version: 0, + RealtimeCount: 10000, + ModifyCount: 0, + ColumnStatsLoadStatus: map[int64]string{2: "unInitialized"}, + } + + copTasks := &stmtctx.CopTasksDetails{ + NumCopTasks: 10, + AvgProcessTime: time.Second, + P90ProcessTime: time.Second * 2, + MaxProcessAddress: "10.6.131.78", + MaxProcessTime: time.Second * 3, + AvgWaitTime: time.Millisecond * 10, + P90WaitTime: time.Millisecond * 20, + MaxWaitTime: time.Millisecond * 30, + MaxWaitAddress: "10.6.131.79", + MaxBackoffTime: make(map[string]time.Duration), + AvgBackoffTime: make(map[string]time.Duration), + P90BackoffTime: make(map[string]time.Duration), + TotBackoffTime: make(map[string]time.Duration), + TotBackoffTimes: make(map[string]int), + MaxBackoffAddress: make(map[string]string), + } + + backoffs := []string{"rpcTiKV", "rpcPD", "regionMiss"} + for _, backoff := range backoffs { + copTasks.MaxBackoffTime[backoff] = time.Millisecond * 200 + copTasks.MaxBackoffAddress[backoff] = "127.0.0.1" + copTasks.AvgBackoffTime[backoff] = time.Millisecond * 200 + copTasks.P90BackoffTime[backoff] = time.Millisecond * 200 + copTasks.TotBackoffTime[backoff] = time.Millisecond * 200 + copTasks.TotBackoffTimes[backoff] = 200 + } + + var memMax int64 = 2333 + var diskMax int64 = 6666 + resultFields := `# Txn_start_ts: 406649736972468225 +# Keyspace_name: keyspace_a +# Keyspace_ID: 1 +# User@Host: root[root] @ 192.168.0.1 [192.168.0.1] +# Conn_ID: 1 +# Session_alias: aliasabc +# Exec_retry_time: 5.1 Exec_retry_count: 3 +# Query_time: 1 +# Parse_time: 0.00000001 +# Compile_time: 0.00000001 +# Rewrite_time: 0.000000003 Preproc_subqueries: 2 Preproc_subqueries_time: 0.000000002 +# Optimize_time: 0.00000001 +# Wait_TS: 0.000000003 +# Process_time: 2 Wait_time: 60 Backoff_time: 0.001 Request_count: 2 Process_keys: 20001 Total_keys: 10000 +# DB: test +# Index_names: [t1:a,t2:b] +# Is_internal: true +# Digest: e5796985ccafe2f71126ed6c0ac939ffa015a8c0744a24b7aee6d587103fd2f7 +# Stats: t1:123[1000;0][ID 1:allLoaded,ID 2:allLoaded][ID 2:allEvicted,ID 3:onlyCmsEvicted],t2:pseudo[10000;0] +# Num_cop_tasks: 10 +# Cop_proc_avg: 1 Cop_proc_p90: 2 Cop_proc_max: 3 Cop_proc_addr: 10.6.131.78 +# Cop_wait_avg: 0.01 Cop_wait_p90: 0.02 Cop_wait_max: 0.03 Cop_wait_addr: 10.6.131.79 +# Cop_backoff_regionMiss_total_times: 200 Cop_backoff_regionMiss_total_time: 0.2 Cop_backoff_regionMiss_max_time: 0.2 Cop_backoff_regionMiss_max_addr: 127.0.0.1 Cop_backoff_regionMiss_avg_time: 0.2 Cop_backoff_regionMiss_p90_time: 0.2 +# Cop_backoff_rpcPD_total_times: 200 Cop_backoff_rpcPD_total_time: 0.2 Cop_backoff_rpcPD_max_time: 0.2 Cop_backoff_rpcPD_max_addr: 127.0.0.1 Cop_backoff_rpcPD_avg_time: 0.2 Cop_backoff_rpcPD_p90_time: 0.2 +# Cop_backoff_rpcTiKV_total_times: 200 Cop_backoff_rpcTiKV_total_time: 0.2 Cop_backoff_rpcTiKV_max_time: 0.2 Cop_backoff_rpcTiKV_max_addr: 127.0.0.1 Cop_backoff_rpcTiKV_avg_time: 0.2 Cop_backoff_rpcTiKV_p90_time: 0.2 +# Mem_max: 2333 +# Disk_max: 6666 +# Prepared: true +# Plan_from_cache: true +# Plan_from_binding: true +# Has_more_results: true +# KV_total: 10 +# PD_total: 11 +# Backoff_total: 12 +# Write_sql_response_total: 1 +# Result_rows: 12345 +# Succ: true +# IsExplicitTxn: true +# IsSyncStatsFailed: false +# IsWriteCacheTable: true` + sql := "select * from t;" + _, digest := parser.NormalizeDigest(sql) + logItems := &variable.SlowQueryLogItems{ + TxnTS: txnTS, + KeyspaceName: "keyspace_a", + KeyspaceID: 1, + SQL: sql, + Digest: digest.String(), + TimeTotal: costTime, + TimeParse: time.Duration(10), + TimeCompile: time.Duration(10), + TimeOptimize: time.Duration(10), + TimeWaitTS: time.Duration(3), + IndexNames: "[t1:a,t2:b]", + CopTasks: copTasks, + ExecDetail: execDetail, + MemMax: memMax, + DiskMax: diskMax, + Prepared: true, + PlanFromCache: true, + PlanFromBinding: true, + HasMoreResults: true, + KVTotal: 10 * time.Second, + PDTotal: 11 * time.Second, + BackoffTotal: 12 * time.Second, + WriteSQLRespTotal: 1 * time.Second, + ResultRows: 12345, + Succ: true, + RewriteInfo: variable.RewritePhaseInfo{ + DurationRewrite: 3, + DurationPreprocessSubQuery: 2, + PreprocessSubQueries: 2, + }, + ExecRetryCount: 3, + ExecRetryTime: 5*time.Second + time.Millisecond*100, + IsExplicitTxn: true, + IsWriteCacheTable: true, + UsedStats: map[int64]*stmtctx.UsedStatsInfoForTable{1: usedStats1, 2: usedStats2}, + } + logString := seVar.SlowLogFormat(logItems) + require.Equal(t, resultFields+"\n"+sql, logString) + + seVar.CurrentDBChanged = true + logString = seVar.SlowLogFormat(logItems) + require.Equal(t, resultFields+"\n"+"use test;\n"+sql, logString) + require.False(t, seVar.CurrentDBChanged) +} + +func TestIsolationRead(t *testing.T) { + defer config.RestoreFunc()() + config.UpdateGlobal(func(conf *config.Config) { + conf.IsolationRead.Engines = []string{"tiflash", "tidb"} + }) + sessVars := variable.NewSessionVars(nil) + _, ok := sessVars.IsolationReadEngines[kv.TiDB] + require.True(t, ok) + _, ok = sessVars.IsolationReadEngines[kv.TiKV] + require.False(t, ok) + _, ok = sessVars.IsolationReadEngines[kv.TiFlash] + require.True(t, ok) +} + +func TestTableDeltaClone(t *testing.T) { + td0 := variable.TableDelta{ + Delta: 1, + Count: 2, + ColSize: map[int64]int64{1: 1, 2: 2}, + InitTime: time.Now(), + TableID: 5, + } + td1 := td0.Clone() + require.Equal(t, td0, td1) + td0.ColSize[3] = 3 + require.NotEqual(t, td0, td1) + + td2 := td0.Clone() + require.Equal(t, td0, td2) + td0.InitTime = td0.InitTime.Add(time.Second) + require.NotEqual(t, td0, td2) +} + +func TestTransactionContextSavepoint(t *testing.T) { + tc := &variable.TransactionContext{ + TxnCtxNeedToRestore: variable.TxnCtxNeedToRestore{ + TableDeltaMap: map[int64]variable.TableDelta{ + 1: { + Delta: 1, + Count: 2, + ColSize: map[int64]int64{1: 1}, + InitTime: time.Now(), + TableID: 5, + }, + }, + }, + } + tc.SetPessimisticLockCache([]byte{'a'}, []byte{'a'}) + tc.FlushStmtPessimisticLockCache() + + tc.AddSavepoint("S1", nil) + require.Equal(t, 1, len(tc.Savepoints)) + require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) + require.Equal(t, "s1", tc.Savepoints[0].Name) + + succ := tc.DeleteSavepoint("s2") + require.False(t, succ) + require.Equal(t, 1, len(tc.Savepoints)) + + tc.TableDeltaMap[1].ColSize[2] = 2 + tc.TableDeltaMap[2] = variable.TableDelta{ + Delta: 6, + Count: 7, + ColSize: map[int64]int64{8: 8}, + InitTime: time.Now(), + TableID: 9, + } + tc.SetPessimisticLockCache([]byte{'b'}, []byte{'b'}) + tc.FlushStmtPessimisticLockCache() + + tc.AddSavepoint("S2", nil) + require.Equal(t, 2, len(tc.Savepoints)) + require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) + require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap[1].ColSize)) + require.Equal(t, "s1", tc.Savepoints[0].Name) + require.Equal(t, 2, len(tc.Savepoints[1].TxnCtxSavepoint.TableDeltaMap)) + require.Equal(t, "s2", tc.Savepoints[1].Name) + + tc.TableDeltaMap[3] = variable.TableDelta{ + Delta: 10, + Count: 11, + ColSize: map[int64]int64{12: 12}, + InitTime: time.Now(), + TableID: 13, + } + tc.SetPessimisticLockCache([]byte{'c'}, []byte{'c'}) + tc.FlushStmtPessimisticLockCache() + + tc.AddSavepoint("s2", nil) + require.Equal(t, 2, len(tc.Savepoints)) + require.Equal(t, 3, len(tc.Savepoints[1].TxnCtxSavepoint.TableDeltaMap)) + require.Equal(t, "s2", tc.Savepoints[1].Name) + + tc.RollbackToSavepoint("s1") + require.Equal(t, 1, len(tc.Savepoints)) + require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) + require.Equal(t, "s1", tc.Savepoints[0].Name) + val, ok := tc.GetKeyInPessimisticLockCache([]byte{'a'}) + require.True(t, ok) + require.Equal(t, []byte{'a'}, val) + val, ok = tc.GetKeyInPessimisticLockCache([]byte{'b'}) + require.False(t, ok) + require.Nil(t, val) + + succ = tc.DeleteSavepoint("s1") + require.True(t, succ) + require.Equal(t, 0, len(tc.Savepoints)) +} + +func TestNonPreparedPlanCacheStmt(t *testing.T) { + sessVars := variable.NewSessionVars(nil) + sessVars.SessionPlanCacheSize = 100 + sql1 := "select * from t where a>?" + sql2 := "select * from t where a 0 { + // Can use percentage format when TiDB can obtain physical memory + // Test Percentage Format + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1%") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + if total/100 > uint64(512<<20) { + require.Equal(t, memory.ServerMemoryLimit.Load(), total/100) + require.Equal(t, "1%", val) + } else { + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) + require.Equal(t, "512MB", val) + } + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "0%") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "100%") + require.Error(t, err) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "75%") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, "75%", val) + require.Equal(t, memory.ServerMemoryLimit.Load(), total/100*75) + } + // Test can't obtain physical memory + require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/memory/GetMemTotalError", `return(true)`)) + require.Error(t, mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "75%")) + require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/memory/GetMemTotalError")) + + // Test byteSize format + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1234") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) + require.Equal(t, "512MB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1234567890123") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(1234567890123)) + require.Equal(t, "1234567890123", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "10KB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) + require.Equal(t, "512MB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "12345678KB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(12345678<<10)) + require.Equal(t, "12345678KB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "10MB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) + require.Equal(t, "512MB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "700MB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(700<<20)) + require.Equal(t, "700MB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "20GB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(20<<30)) + require.Equal(t, "20GB", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "2TB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(2<<40)) + require.Equal(t, "2TB", val) + + // Test error + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "123aaa123") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "700MBaa") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "a700MB") + require.Error(t, err) +} + +func TestTiDBServerMemoryLimitSessMinSize(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + var ( + err error + val string + ) + + serverMemroyLimitSessMinSize := GetSysVar(TiDBServerMemoryLimitSessMinSize) + // Check default value + require.Equal(t, serverMemroyLimitSessMinSize.Value, strconv.FormatInt(DefTiDBServerMemoryLimitSessMinSize, 10)) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "123456") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(123456)) + require.Equal(t, "123456", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "100") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(128)) + require.Equal(t, "128", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "123MB") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) + require.NoError(t, err) + require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(123<<20)) + require.Equal(t, "128974848", val) +} + +func TestTiDBServerMemoryLimitGCTrigger(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + var ( + err error + val string + ) + + serverMemroyLimitGCTrigger := GetSysVar(TiDBServerMemoryLimitGCTrigger) + // Check default value + require.Equal(t, serverMemroyLimitGCTrigger.Value, strconv.FormatFloat(DefTiDBServerMemoryLimitGCTrigger, 'f', -1, 64)) + defer func() { + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, strconv.FormatFloat(DefTiDBServerMemoryLimitGCTrigger, 'f', -1, 64)) + require.NoError(t, err) + }() + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "0.8") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitGCTrigger) + require.NoError(t, err) + require.Equal(t, gctuner.GlobalMemoryLimitTuner.GetPercentage(), 0.8) + require.Equal(t, "0.8", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "90%") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitGCTrigger) + require.NoError(t, err) + require.Equal(t, gctuner.GlobalMemoryLimitTuner.GetPercentage(), 0.9) + require.Equal(t, "0.9", val) + + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "100%") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "101%") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "99%") + require.NoError(t, err) + + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerThreshold, "0.4") + require.NoError(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "49%") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "51%") + require.NoError(t, err) + + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMaxValue, "50") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "200") + require.NoError(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "1000") + require.Error(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "100") + require.NoError(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMaxValue, "200") + require.NoError(t, err) +} + +func TestSetAggPushDownGlobally(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + val, err := mock.GetGlobalSysVar(TiDBOptAggPushDown) + require.NoError(t, err) + require.Equal(t, "OFF", val) + err = mock.SetGlobalSysVar(context.Background(), TiDBOptAggPushDown, "ON") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBOptAggPushDown) + require.NoError(t, err) + require.Equal(t, "ON", val) +} + +func TestSetDeriveTopNGlobally(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + val, err := mock.GetGlobalSysVar(TiDBOptDeriveTopN) + require.NoError(t, err) + require.Equal(t, "OFF", val) + err = mock.SetGlobalSysVar(context.Background(), TiDBOptDeriveTopN, "ON") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBOptDeriveTopN) + require.NoError(t, err) + require.Equal(t, "ON", val) +} + +func TestSetJobScheduleWindow(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + // default value + val, err := mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) + require.NoError(t, err) + require.Equal(t, "00:00 +0000", val) + + // set and get variable in UTC + vars.TimeZone = time.UTC + err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) + require.NoError(t, err) + require.Equal(t, "16:11 +0000", val) + + // set variable in UTC, get it in Asia/Shanghai + vars.TimeZone = time.UTC + err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") + require.NoError(t, err) + vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) + require.NoError(t, err) + require.Equal(t, "16:11 +0000", val) + + // set variable in Asia/Shanghai, get it it UTC + vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") + require.NoError(t, err) + vars.TimeZone = time.UTC + val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) + require.NoError(t, err) + require.Equal(t, "16:11 +0800", val) +} + +func TestTiDBEnableResourceControl(t *testing.T) { + // setup the hooks for test + // NOTE: the default system variable is true but the switch is false + // It is initialized at the first call of `rebuildSysVarCache` + enable := false + EnableGlobalResourceControlFunc = func() { enable = true } + DisableGlobalResourceControlFunc = func() { enable = false } + setGlobalResourceControlFunc := func(enable bool) { + if enable { + EnableGlobalResourceControlFunc() + } else { + DisableGlobalResourceControlFunc() + } + } + SetGlobalResourceControl.Store(&setGlobalResourceControlFunc) + + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + resourceControlEnabled := GetSysVar(TiDBEnableResourceControl) + + // Default true + require.Equal(t, resourceControlEnabled.Value, On) + require.Equal(t, enable, false) + + // Set to On(init at start) + err := mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, On) + require.NoError(t, err) + val, err1 := mock.GetGlobalSysVar(TiDBEnableResourceControl) + require.NoError(t, err1) + require.Equal(t, On, val) + require.Equal(t, enable, true) + + // Set to Off + err = mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, Off) + require.NoError(t, err) + val, err1 = mock.GetGlobalSysVar(TiDBEnableResourceControl) + require.NoError(t, err1) + require.Equal(t, Off, val) + require.Equal(t, enable, false) + + // Set to On again + err = mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, On) + require.NoError(t, err) + val, err1 = mock.GetGlobalSysVar(TiDBEnableResourceControl) + require.NoError(t, err1) + require.Equal(t, On, val) + require.Equal(t, enable, true) +} + +func TestTiDBEnableRowLevelChecksum(t *testing.T) { + ctx := context.Background() + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + + // default to false + val, err := mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) + require.NoError(t, err) + require.Equal(t, Off, val) + + // enable + err = mock.SetGlobalSysVar(ctx, TiDBEnableRowLevelChecksum, On) + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) + require.NoError(t, err) + require.Equal(t, On, val) + + // disable + err = mock.SetGlobalSysVar(ctx, TiDBEnableRowLevelChecksum, Off) + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) + require.NoError(t, err) + require.Equal(t, Off, val) +} + +func TestTiDBTiFlashReplicaRead(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + tidbTiFlashReplicaRead := GetSysVar(TiFlashReplicaRead) + // Check default value + require.Equal(t, DefTiFlashReplicaRead, tidbTiFlashReplicaRead.Value) + + err := mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "all_replicas") + require.NoError(t, err) + val, err := mock.GetGlobalSysVar(TiFlashReplicaRead) + require.NoError(t, err) + require.Equal(t, "all_replicas", val) + + err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "closest_adaptive") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) + require.NoError(t, err) + require.Equal(t, "closest_adaptive", val) + + err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "closest_replicas") + require.NoError(t, err) + val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) + require.NoError(t, err) + require.Equal(t, "closest_replicas", val) + + err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, DefTiFlashReplicaRead) + require.NoError(t, err) + err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "random") + require.Error(t, err) + val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) + require.NoError(t, err) + require.Equal(t, DefTiFlashReplicaRead, val) +} + +func TestSetTiDBCloudStorageURI(t *testing.T) { + vars := NewSessionVars(nil) + mock := NewMockGlobalAccessor4Tests() + mock.SessionVars = vars + vars.GlobalVarsAccessor = mock + cloudStorageURI := GetSysVar(TiDBCloudStorageURI) + require.Len(t, CloudStorageURI.Load(), 0) + defer func() { + CloudStorageURI.Store("") + }() + + // Default empty + require.Len(t, cloudStorageURI.Value, 0) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + // Set to noop + noopURI := "noop://blackhole?access-key=hello&secret-access-key=world" + err := mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, noopURI) + require.NoError(t, err) + val, err1 := mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) + require.NoError(t, err1) + require.Equal(t, noopURI, val) + require.Equal(t, noopURI, CloudStorageURI.Load()) + + // Set to s3, should fail + err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, "s3://blackhole") + require.ErrorContains(t, err, "bucket blackhole") + + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + })) + defer s.Close() + + // Set to s3, should return uri without variable + s3URI := "s3://tiflow-test/?access-key=testid&secret-access-key=testkey8&session-token=testtoken&endpoint=" + s.URL + err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, s3URI) + require.NoError(t, err) + val, err1 = mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) + require.NoError(t, err1) + require.True(t, strings.HasPrefix(val, "s3://tiflow-test/")) + require.Contains(t, val, "access-key=xxxxxx") + require.Contains(t, val, "secret-access-key=xxxxxx") + require.Contains(t, val, "session-token=xxxxxx") + require.Equal(t, s3URI, CloudStorageURI.Load()) + + // Set to empty, should return no error + err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, "") + require.NoError(t, err) + val, err1 = mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) + require.NoError(t, err1) + require.Len(t, val, 0) + cancel() +} diff --git a/sessionctx/variable/tidb_vars.go b/pkg/sessionctx/variable/tidb_vars.go similarity index 99% rename from sessionctx/variable/tidb_vars.go rename to pkg/sessionctx/variable/tidb_vars.go index 5269ff957fdf1..da7dc29765ab6 100644 --- a/sessionctx/variable/tidb_vars.go +++ b/pkg/sessionctx/variable/tidb_vars.go @@ -20,15 +20,15 @@ import ( "math" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable/featuretag/disttask" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/paging" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable/featuretag/disttask" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/pingcap/tidb/pkg/util/size" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" "go.uber.org/atomic" ) diff --git a/pkg/sessionctx/variable/variable.go b/pkg/sessionctx/variable/variable.go new file mode 100644 index 0000000000000..5aff7f6724929 --- /dev/null +++ b/pkg/sessionctx/variable/variable.go @@ -0,0 +1,664 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "context" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "golang.org/x/exp/maps" +) + +// ScopeFlag is for system variable whether can be changed in global/session dynamically or not. +type ScopeFlag uint8 + +// TypeFlag is the SysVar type, which doesn't exactly match MySQL types. +type TypeFlag byte + +const ( + // ScopeNone means the system variable can not be changed dynamically. + ScopeNone ScopeFlag = 0 + // ScopeGlobal means the system variable can be changed globally. + ScopeGlobal ScopeFlag = 1 << 0 + // ScopeSession means the system variable can only be changed in current session. + ScopeSession ScopeFlag = 1 << 1 + // ScopeInstance means it is similar to global but doesn't propagate to other TiDB servers. + ScopeInstance ScopeFlag = 1 << 2 + + // TypeStr is the default + TypeStr TypeFlag = iota + // TypeBool for boolean + TypeBool + // TypeInt for integer + TypeInt + // TypeEnum for Enum + TypeEnum + // TypeFloat for Double + TypeFloat + // TypeUnsigned for Unsigned integer + TypeUnsigned + // TypeTime for time of day (a TiDB extension) + TypeTime + // TypeDuration for a golang duration (a TiDB extension) + TypeDuration + + // On is the canonical string for ON + On = "ON" + // Off is the canonical string for OFF + Off = "OFF" + // Warn means return warnings + Warn = "WARN" + // IntOnly means enable for int type + IntOnly = "INT_ONLY" + + // AssertionStrictStr is a choice of variable TiDBTxnAssertionLevel that means full assertions should be performed, + // even if the performance might be slowed down. + AssertionStrictStr = "STRICT" + // AssertionFastStr is a choice of variable TiDBTxnAssertionLevel that means assertions that doesn't affect + // performance should be performed. + AssertionFastStr = "FAST" + // AssertionOffStr is a choice of variable TiDBTxnAssertionLevel that means no assertion should be performed. + AssertionOffStr = "OFF" + // OOMActionCancel constants represents the valid action configurations for OOMAction "CANCEL". + OOMActionCancel = "CANCEL" + // OOMActionLog constants represents the valid action configurations for OOMAction "LOG". + OOMActionLog = "LOG" +) + +// Global config name list. +const ( + GlobalConfigEnableTopSQL = "enable_resource_metering" + GlobalConfigSourceID = "source_id" +) + +func (s ScopeFlag) String() string { + var scopes []string + if s == ScopeNone { + return "NONE" + } + if s&ScopeSession != 0 { + scopes = append(scopes, "SESSION") + } + if s&ScopeGlobal != 0 { + scopes = append(scopes, "GLOBAL") + } + if s&ScopeInstance != 0 { + scopes = append(scopes, "INSTANCE") + } + return strings.Join(scopes, ",") +} + +// SysVar is for system variable. +// All the fields of SysVar should be READ ONLY after created. +type SysVar struct { + // Scope is for whether can be changed or not + Scope ScopeFlag + // Name is the variable name. + Name string + // Value is the variable value. + Value string + // Type is the MySQL type (optional) + Type TypeFlag + // MinValue will automatically be validated when specified (optional) + MinValue int64 + // MaxValue will automatically be validated when specified (optional) + MaxValue uint64 + // AutoConvertNegativeBool applies to boolean types (optional) + AutoConvertNegativeBool bool + // ReadOnly applies to all types + ReadOnly bool + // PossibleValues applies to ENUM type + PossibleValues []string + // AllowEmpty is a special TiDB behavior which means "read value from config" (do not use) + AllowEmpty bool + // AllowEmptyAll is a special behavior that only applies to TiDBCapturePlanBaseline, TiDBTxnMode (do not use) + AllowEmptyAll bool + // AllowAutoValue means that the special value "-1" is permitted, even when outside of range. + AllowAutoValue bool + // Validation is a callback after the type validation has been performed, but before the Set function + Validation func(*SessionVars, string, string, ScopeFlag) (string, error) + // SetSession is called after validation but before updating systems[]. It also doubles as an Init function + // and will be called on all variables in builtinGlobalVariable, regardless of their scope. + SetSession func(*SessionVars, string) error + // SetGlobal is called after validation + SetGlobal func(context.Context, *SessionVars, string) error + // IsHintUpdatableVerfied indicate whether we've confirmed that SET_VAR() hint is worked for this hint. + IsHintUpdatableVerfied bool + // Deprecated: Hidden previously meant that the variable still responds to SET but doesn't show up in SHOW VARIABLES + // However, this feature is no longer used. All variables are visble. + Hidden bool + // Aliases is a list of sysvars that should also be updated when this sysvar is updated. + // Updating aliases calls the SET function of the aliases, but does not update their aliases (preventing SET recursion) + Aliases []string + // GetSession is a getter function for session scope. + // It can be used by instance-scoped variables to overwrite the previously expected value. + GetSession func(*SessionVars) (string, error) + // GetGlobal is a getter function for global scope. + GetGlobal func(context.Context, *SessionVars) (string, error) + // GetStateValue gets the value for session states, which is used for migrating sessions. + // We need a function to override GetSession sometimes, because GetSession may not return the real value. + GetStateValue func(*SessionVars) (string, bool, error) + // Depended indicates whether other variables depend on this one. That is, if this one is not correctly set, + // another variable cannot be set either. + // This flag is used to decide the order to replay session variables. + Depended bool + // skipInit defines if the sysvar should be loaded into the session on init. + // This is only important to set for sysvars that include session scope, + // since global scoped sysvars are not-applicable. + skipInit bool + // IsNoop defines if the sysvar is a noop included for MySQL compatibility + IsNoop bool + // GlobalConfigName is the global config name of this global variable. + // If the global variable has the global config name, + // it should store the global config into PD(etcd) too when set global variable. + GlobalConfigName string + // RequireDynamicPrivileges is a function to return a dynamic privilege list to check the set sysvar privilege + RequireDynamicPrivileges func(isGlobal bool, sem bool) []string +} + +// GetGlobalFromHook calls the GetSession func if it exists. +func (sv *SysVar) GetGlobalFromHook(ctx context.Context, s *SessionVars) (string, error) { + // Call the Getter if there is one defined. + if sv.GetGlobal != nil { + val, err := sv.GetGlobal(ctx, s) + if err != nil { + return val, err + } + // Ensure that the results from the getter are validated + // Since some are read directly from tables. + return sv.ValidateWithRelaxedValidation(s, val, ScopeGlobal), nil + } + if sv.HasNoneScope() { + return sv.Value, nil + } + return s.GlobalVarsAccessor.GetGlobalSysVar(sv.Name) +} + +// GetSessionFromHook calls the GetSession func if it exists. +func (sv *SysVar) GetSessionFromHook(s *SessionVars) (string, error) { + if sv.HasNoneScope() { + return sv.Value, nil + } + // Call the Getter if there is one defined. + if sv.GetSession != nil { + val, err := sv.GetSession(s) + if err != nil { + return val, err + } + // Ensure that the results from the getter are validated + // Since some are read directly from tables. + return sv.ValidateWithRelaxedValidation(s, val, ScopeSession), nil + } + var ( + ok bool + val string + ) + if val, ok = s.stmtVars[sv.Name]; ok { + return val, nil + } + if val, ok = s.systems[sv.Name]; !ok { + return val, errors.New("sysvar has not yet loaded") + } + return val, nil +} + +// SetSessionFromHook calls the SetSession func if it exists. +func (sv *SysVar) SetSessionFromHook(s *SessionVars, val string) error { + if sv.SetSession != nil { + if err := sv.SetSession(s, val); err != nil { + return err + } + } + s.systems[sv.Name] = val + + // Call the Set function on all the aliases for this sysVar + // Skipping the validation function, and not calling aliases of + // aliases. By skipping the validation function it means that things + // like duplicate warnings should not appear. + + if sv.Aliases != nil { + for _, aliasName := range sv.Aliases { + aliasSv := GetSysVar(aliasName) + if aliasSv.SetSession != nil { + if err := aliasSv.SetSession(s, val); err != nil { + return err + } + } + s.systems[aliasSv.Name] = val + } + } + return nil +} + +// SetGlobalFromHook calls the SetGlobal func if it exists. +func (sv *SysVar) SetGlobalFromHook(ctx context.Context, s *SessionVars, val string, skipAliases bool) error { + if sv.SetGlobal != nil { + return sv.SetGlobal(ctx, s, val) + } + + // Call the SetGlobalSysVarOnly function on all the aliases for this sysVar + // which skips the validation function and when SetGlobalFromHook is called again + // it will be with skipAliases=true. This helps break recursion because + // most aliases are reciprocal. + + if !skipAliases && sv.Aliases != nil { + for _, aliasName := range sv.Aliases { + if err := s.GlobalVarsAccessor.SetGlobalSysVarOnly(ctx, aliasName, val, true); err != nil { + return err + } + } + } + return nil +} + +// HasNoneScope returns true if the scope for the sysVar is None. +func (sv *SysVar) HasNoneScope() bool { + return sv.Scope == ScopeNone +} + +// HasSessionScope returns true if the scope for the sysVar includes session. +func (sv *SysVar) HasSessionScope() bool { + return sv.Scope&ScopeSession != 0 +} + +// HasGlobalScope returns true if the scope for the sysVar includes global. +func (sv *SysVar) HasGlobalScope() bool { + return sv.Scope&ScopeGlobal != 0 +} + +// HasInstanceScope returns true if the scope for the sysVar includes instance +func (sv *SysVar) HasInstanceScope() bool { + return sv.Scope&ScopeInstance != 0 +} + +// Validate checks if system variable satisfies specific restriction. +func (sv *SysVar) Validate(vars *SessionVars, value string, scope ScopeFlag) (string, error) { + // Check that the scope is correct first. + if err := sv.validateScope(scope); err != nil { + return value, err + } + // Normalize the value and apply validation based on type. + // i.e. TypeBool converts 1/on/ON to ON. + normalizedValue, err := sv.ValidateFromType(vars, value, scope) + if err != nil { + return normalizedValue, err + } + // If type validation was successful, call the (optional) validation function + if sv.Validation != nil { + return sv.Validation(vars, normalizedValue, value, scope) + } + return normalizedValue, nil +} + +// ValidateFromType provides automatic validation based on the SysVar's type +func (sv *SysVar) ValidateFromType(vars *SessionVars, value string, scope ScopeFlag) (string, error) { + // Some sysvars in TiDB have a special behavior where the empty string means + // "use the config file value". This needs to be cleaned up once the behavior + // for instance variables is determined. + if value == "" && ((sv.AllowEmpty && scope == ScopeSession) || sv.AllowEmptyAll) { + return value, nil + } + // Provide validation using the SysVar struct + switch sv.Type { + case TypeUnsigned: + return sv.checkUInt64SystemVar(value, vars) + case TypeInt: + return sv.checkInt64SystemVar(value, vars) + case TypeBool: + return sv.checkBoolSystemVar(value, vars) + case TypeFloat: + return sv.checkFloatSystemVar(value, vars) + case TypeEnum: + return sv.checkEnumSystemVar(value, vars) + case TypeTime: + return sv.checkTimeSystemVar(value, vars) + case TypeDuration: + return sv.checkDurationSystemVar(value, vars) + } + return value, nil // typeString +} + +func (sv *SysVar) validateScope(scope ScopeFlag) error { + if sv.ReadOnly || sv.Scope == ScopeNone { + return ErrIncorrectScope.FastGenByArgs(sv.Name, "read only") + } + if scope == ScopeGlobal && !(sv.HasGlobalScope() || sv.HasInstanceScope()) { + return errLocalVariable.FastGenByArgs(sv.Name) + } + if scope == ScopeSession && !sv.HasSessionScope() { + return errGlobalVariable.FastGenByArgs(sv.Name) + } + return nil +} + +// ValidateWithRelaxedValidation normalizes values but can not return errors. +// Normalization+validation needs to be applied when reading values because older versions of TiDB +// may be less sophisticated in normalizing values. But errors should be caught and handled, +// because otherwise there will be upgrade issues. +func (sv *SysVar) ValidateWithRelaxedValidation(vars *SessionVars, value string, scope ScopeFlag) string { + warns := vars.StmtCtx.GetWarnings() + defer func() { + vars.StmtCtx.SetWarnings(warns) // RelaxedValidation = trim warnings too. + }() + normalizedValue, err := sv.ValidateFromType(vars, value, scope) + if err != nil { + return normalizedValue + } + if sv.Validation != nil { + normalizedValue, err = sv.Validation(vars, normalizedValue, value, scope) + if err != nil { + return normalizedValue + } + } + return normalizedValue +} + +const ( + localDayTimeFormat = "15:04" + // FullDayTimeFormat is the full format of analyze start time and end time. + FullDayTimeFormat = "15:04 -0700" +) + +func (sv *SysVar) checkTimeSystemVar(value string, vars *SessionVars) (string, error) { + var t time.Time + var err error + if len(value) <= len(localDayTimeFormat) { + t, err = time.ParseInLocation(localDayTimeFormat, value, vars.Location()) + } else { + t, err = time.ParseInLocation(FullDayTimeFormat, value, vars.Location()) + } + if err != nil { + return "", err + } + // Add a modern date to it, as the timezone shift can differ across the history + // For example, the Asia/Shanghai refers to +08:05 before 1900 + now := time.Now() + t = time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) + return t.Format(FullDayTimeFormat), nil +} + +func (sv *SysVar) checkDurationSystemVar(value string, vars *SessionVars) (string, error) { + d, err := time.ParseDuration(value) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + // Check for min/max violations + if int64(d) < sv.MinValue { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return time.Duration(sv.MinValue).String(), nil + } + if uint64(d) > sv.MaxValue { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return time.Duration(sv.MaxValue).String(), nil + } + // return a string representation of the duration + return d.String(), nil +} + +func (sv *SysVar) checkUInt64SystemVar(value string, vars *SessionVars) (string, error) { + if sv.AllowAutoValue && value == "-1" { + return value, nil + } + if len(value) == 0 { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + if value[0] == '-' { + _, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatInt(sv.MinValue, 10), nil + } + val, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + if val < uint64(sv.MinValue) { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatInt(sv.MinValue, 10), nil + } + if val > sv.MaxValue { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatUint(sv.MaxValue, 10), nil + } + return value, nil +} + +func (sv *SysVar) checkInt64SystemVar(value string, vars *SessionVars) (string, error) { + if sv.AllowAutoValue && value == "-1" { + return value, nil + } + val, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + if val < sv.MinValue { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatInt(sv.MinValue, 10), nil + } + if val > int64(sv.MaxValue) { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatUint(sv.MaxValue, 10), nil + } + return value, nil +} + +func (sv *SysVar) checkEnumSystemVar(value string, vars *SessionVars) (string, error) { + // The value could be either a string or the ordinal position in the PossibleValues. + // This allows for the behavior 0 = OFF, 1 = ON, 2 = DEMAND etc. + var iStr string + for i, v := range sv.PossibleValues { + iStr = strconv.Itoa(i) + if strings.EqualFold(value, v) || strings.EqualFold(value, iStr) { + return v, nil + } + } + return value, ErrWrongValueForVar.GenWithStackByArgs(sv.Name, value) +} + +func (sv *SysVar) checkFloatSystemVar(value string, vars *SessionVars) (string, error) { + if len(value) == 0 { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + val, err := strconv.ParseFloat(value, 64) + if err != nil { + return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) + } + if val < float64(sv.MinValue) { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatInt(sv.MinValue, 10), nil + } + if val > float64(sv.MaxValue) { + vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) + return strconv.FormatUint(sv.MaxValue, 10), nil + } + return value, nil +} + +func (sv *SysVar) checkBoolSystemVar(value string, vars *SessionVars) (string, error) { + if strings.EqualFold(value, "ON") { + return On, nil + } else if strings.EqualFold(value, "OFF") { + return Off, nil + } + val, err := strconv.ParseInt(value, 10, 64) + if err == nil { + // There are two types of conversion rules for integer values. + // The default only allows 0 || 1, but a subset of values convert any + // negative integer to 1. + if !sv.AutoConvertNegativeBool { + if val == 0 { + return Off, nil + } else if val == 1 { + return On, nil + } + } else { + if val == 1 || val < 0 { + return On, nil + } else if val == 0 { + return Off, nil + } + } + } + return value, ErrWrongValueForVar.GenWithStackByArgs(sv.Name, value) +} + +// GetNativeValType attempts to convert the val to the approx MySQL non-string type +// TODO: only return 3 types now, support others like DOUBLE, TIME later +func (sv *SysVar) GetNativeValType(val string) (types.Datum, byte, uint) { + switch sv.Type { + case TypeUnsigned: + u, err := strconv.ParseUint(val, 10, 64) + if err != nil { + u = 0 + } + return types.NewUintDatum(u), mysql.TypeLonglong, mysql.UnsignedFlag | mysql.BinaryFlag + case TypeBool: + optVal := int64(0) // OFF + if TiDBOptOn(val) { + optVal = 1 + } + return types.NewIntDatum(optVal), mysql.TypeLonglong, mysql.BinaryFlag + } + return types.NewStringDatum(val), mysql.TypeVarString, 0 +} + +// SkipInit returns true if when a new session is created we should "skip" copying +// an initial value to it (and call the SetSession func if it exists) +func (sv *SysVar) SkipInit() bool { + if sv.skipInit || sv.IsNoop { + return true + } + return !sv.HasSessionScope() +} + +// SkipSysvarCache returns true if the sysvar should not re-execute on peers +// This doesn't make sense for the GC variables because they are based in tikv +// tables. We'd effectively be reading and writing to the same table, which +// could be in an unsafe manner. In future these variables might be converted +// to not use a different table internally, but to do that we need to first +// fix upgrade/downgrade so we know that older servers won't be in the cluster +// which update only these values. +func (sv *SysVar) SkipSysvarCache() bool { + switch sv.Name { + case TiDBGCEnable, TiDBGCRunInterval, TiDBGCLifetime, + TiDBGCConcurrency, TiDBGCScanLockMode, TiDBExternalTS: + return true + } + return false +} + +var sysVars map[string]*SysVar +var sysVarsLock sync.RWMutex + +// RegisterSysVar adds a sysvar to the SysVars list +func RegisterSysVar(sv *SysVar) { + name := strings.ToLower(sv.Name) + sysVarsLock.Lock() + sysVars[name] = sv + sysVarsLock.Unlock() +} + +// UnregisterSysVar removes a sysvar from the SysVars list +// currently only used in tests. +func UnregisterSysVar(name string) { + name = strings.ToLower(name) + sysVarsLock.Lock() + delete(sysVars, name) + sysVarsLock.Unlock() +} + +// GetSysVar returns sys var info for name as key. +func GetSysVar(name string) *SysVar { + name = strings.ToLower(name) + sysVarsLock.RLock() + defer sysVarsLock.RUnlock() + + return sysVars[name] +} + +// SetSysVar sets a sysvar. In fact, SysVar is immutable. +// SetSysVar is implemented by register a new SysVar with the same name again. +// This will not propagate to the cluster, so it should only be +// used for instance scoped AUTO variables such as system_time_zone. +func SetSysVar(name string, value string) { + old := GetSysVar(name) + tmp := *old + tmp.Value = value + RegisterSysVar(&tmp) +} + +// GetSysVars deep copies the sysVars list under a RWLock +func GetSysVars() map[string]*SysVar { + sysVarsLock.RLock() + defer sysVarsLock.RUnlock() + m := make(map[string]*SysVar, len(sysVars)) + for name, sv := range sysVars { + tmp := *sv + m[name] = &tmp + } + return m +} + +// OrderByDependency orders the vars by dependency. The depended sys vars are in the front. +// Unknown sys vars are treated as not depended. +func OrderByDependency(names map[string]string) []string { + depended, notDepended := make([]string, 0, len(names)), make([]string, 0, len(names)) + sysVarsLock.RLock() + defer sysVarsLock.RUnlock() + for name := range names { + if sv, ok := sysVars[name]; ok && sv.Depended { + depended = append(depended, name) + } else { + notDepended = append(notDepended, name) + } + } + return append(depended, notDepended...) +} + +func init() { + sysVars = make(map[string]*SysVar) + setHintUpdatable(defaultSysVars) + // Destroy the map after init. + maps.Clear(isHintUpdatableVerified) + for _, v := range defaultSysVars { + RegisterSysVar(v) + } + for _, v := range noopSysVars { + v.IsNoop = true + RegisterSysVar(v) + } +} + +// GlobalVarAccessor is the interface for accessing global scope system and status variables. +type GlobalVarAccessor interface { + // GetGlobalSysVar gets the global system variable value for name. + GetGlobalSysVar(name string) (string, error) + // SetGlobalSysVar sets the global system variable name to value. + SetGlobalSysVar(ctx context.Context, name string, value string) error + // SetGlobalSysVarOnly sets the global system variable without calling the validation function or updating aliases. + SetGlobalSysVarOnly(ctx context.Context, name string, value string, updateLocal bool) error + // GetTiDBTableValue gets a value from mysql.tidb for the key 'name' + GetTiDBTableValue(name string) (string, error) + // SetTiDBTableValue sets a value+comment for the mysql.tidb key 'name' + SetTiDBTableValue(name, value, comment string) error +} diff --git a/pkg/sessionctx/variable/variable_test.go b/pkg/sessionctx/variable/variable_test.go new file mode 100644 index 0000000000000..99365b72d0cc4 --- /dev/null +++ b/pkg/sessionctx/variable/variable_test.go @@ -0,0 +1,720 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package variable + +import ( + "context" + "encoding/json" + "fmt" + "runtime" + "slices" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestSysVar(t *testing.T) { + f := GetSysVar("autocommit") + require.NotNil(t, f) + + f = GetSysVar("wrong-var-name") + require.Nil(t, f) + + f = GetSysVar("explicit_defaults_for_timestamp") + require.NotNil(t, f) + require.Equal(t, "ON", f.Value) + + f = GetSysVar("port") + require.NotNil(t, f) + require.Equal(t, "4000", f.Value) + + f = GetSysVar("tidb_low_resolution_tso") + require.Equal(t, "OFF", f.Value) + + f = GetSysVar("tidb_replica_read") + require.Equal(t, "leader", f.Value) + + f = GetSysVar("tidb_enable_table_partition") + require.Equal(t, "ON", f.Value) + + f = GetSysVar("version_compile_os") + require.Equal(t, runtime.GOOS, f.Value) + + f = GetSysVar("version_compile_machine") + require.Equal(t, runtime.GOARCH, f.Value) + + // default enable vectorized_expression + f = GetSysVar("tidb_enable_vectorized_expression") + require.Equal(t, "ON", f.Value) +} + +func TestError(t *testing.T) { + kvErrs := []*terror.Error{ + ErrUnsupportedValueForVar, + ErrUnknownSystemVar, + ErrIncorrectScope, + ErrUnknownTimeZone, + ErrReadOnly, + ErrWrongValueForVar, + ErrWrongTypeForVar, + ErrTruncatedWrongValue, + ErrMaxPreparedStmtCountReached, + ErrUnsupportedIsolationLevel, + } + for _, err := range kvErrs { + require.True(t, terror.ToSQLError(err).Code != mysql.ErrUnknown) + } +} + +func TestRegistrationOfNewSysVar(t *testing.T) { + count := len(GetSysVars()) + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeBool, SetSession: func(s *SessionVars, val string) error { + return fmt.Errorf("set should fail") + }} + + RegisterSysVar(&sv) + require.Len(t, GetSysVars(), count+1) + + sysVar := GetSysVar("mynewsysvar") + require.NotNil(t, sysVar) + + vars := NewSessionVars(nil) + + // It is a boolean, try to set it to a bogus value + _, err := sysVar.Validate(vars, "ABCD", ScopeSession) + require.Error(t, err) + + // Boolean oN or 1 converts to canonical ON or OFF + normalizedVal, err := sysVar.Validate(vars, "oN", ScopeSession) + require.Equal(t, "ON", normalizedVal) + require.NoError(t, err) + normalizedVal, err = sysVar.Validate(vars, "0", ScopeSession) + require.Equal(t, "OFF", normalizedVal) + require.NoError(t, err) + + err = sysVar.SetSessionFromHook(vars, "OFF") // default is on + require.Equal(t, "set should fail", err.Error()) + + // Test unregistration restores previous count + UnregisterSysVar("mynewsysvar") + require.Equal(t, len(GetSysVars()), count) +} + +func TestIntValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeInt, MinValue: 10, MaxValue: 300, AllowAutoValue: true} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "oN", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + val, err := sv.Validate(vars, "301", ScopeSession) + require.NoError(t, err) + require.Equal(t, "300", val) + + val, err = sv.Validate(vars, "5", ScopeSession) + require.NoError(t, err) + require.Equal(t, "10", val) + + val, err = sv.Validate(vars, "300", ScopeSession) + require.NoError(t, err) + require.Equal(t, "300", val) + + // out of range but permitted due to auto value + val, err = sv.Validate(vars, "-1", ScopeSession) + require.NoError(t, err) + require.Equal(t, "-1", val) +} + +func TestUintValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeUnsigned, MinValue: 10, MaxValue: 300, AllowAutoValue: true} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "oN", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + _, err = sv.Validate(vars, "", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + val, err := sv.Validate(vars, "301", ScopeSession) + require.NoError(t, err) + require.Equal(t, "300", val) + + val, err = sv.Validate(vars, "-301", ScopeSession) + require.NoError(t, err) + require.Equal(t, "10", val) + + _, err = sv.Validate(vars, "-ERR", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + val, err = sv.Validate(vars, "5", ScopeSession) + require.NoError(t, err) + require.Equal(t, "10", val) + + val, err = sv.Validate(vars, "300", ScopeSession) + require.NoError(t, err) + require.Equal(t, "300", val) + + // out of range but permitted due to auto value + val, err = sv.Validate(vars, "-1", ScopeSession) + require.NoError(t, err) + require.Equal(t, "-1", val) +} + +func TestEnumValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "randomstring", ScopeSession) + require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of 'randomstring'", err.Error()) + + val, err := sv.Validate(vars, "oFf", ScopeSession) + require.NoError(t, err) + require.Equal(t, "OFF", val) + + val, err = sv.Validate(vars, "On", ScopeSession) + require.NoError(t, err) + require.Equal(t, "ON", val) + + val, err = sv.Validate(vars, "auto", ScopeSession) + require.NoError(t, err) + require.Equal(t, "AUTO", val) + + // Also settable by numeric offset. + val, err = sv.Validate(vars, "2", ScopeSession) + require.NoError(t, err) + require.Equal(t, "AUTO", val) +} + +func TestDurationValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "10m0s", Type: TypeDuration, MinValue: int64(time.Second), MaxValue: uint64(time.Hour)} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "1hr", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + val, err := sv.Validate(vars, "1ms", ScopeSession) + require.NoError(t, err) + require.Equal(t, "1s", val) // truncates to min + + val, err = sv.Validate(vars, "2h10m", ScopeSession) + require.NoError(t, err) + require.Equal(t, "1h0m0s", val) // truncates to max +} + +func TestFloatValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "10m0s", Type: TypeFloat, MinValue: 2, MaxValue: 7} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "stringval", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + _, err = sv.Validate(vars, "", ScopeSession) + require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) + + val, err := sv.Validate(vars, "1.1", ScopeSession) + require.NoError(t, err) + require.Equal(t, "2", val) // truncates to min + + val, err = sv.Validate(vars, "22", ScopeSession) + require.NoError(t, err) + require.Equal(t, "7", val) // truncates to max +} + +func TestBoolValidation(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool} + vars := NewSessionVars(nil) + + _, err := sv.Validate(vars, "0.000", ScopeSession) + require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of '0.000'", err.Error()) + _, err = sv.Validate(vars, "1.000", ScopeSession) + require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of '1.000'", err.Error()) + val, err := sv.Validate(vars, "0", ScopeSession) + require.NoError(t, err) + require.Equal(t, Off, val) + val, err = sv.Validate(vars, "1", ScopeSession) + require.NoError(t, err) + require.Equal(t, On, val) + val, err = sv.Validate(vars, "OFF", ScopeSession) + require.NoError(t, err) + require.Equal(t, Off, val) + val, err = sv.Validate(vars, "ON", ScopeSession) + require.NoError(t, err) + require.Equal(t, On, val) + val, err = sv.Validate(vars, "off", ScopeSession) + require.NoError(t, err) + require.Equal(t, Off, val) + val, err = sv.Validate(vars, "on", ScopeSession) + require.NoError(t, err) + require.Equal(t, On, val) + + // test AutoConvertNegativeBool + sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool, AutoConvertNegativeBool: true} + val, err = sv.Validate(vars, "-1", ScopeSession) + require.NoError(t, err) + require.Equal(t, On, val) + val, err = sv.Validate(vars, "1", ScopeSession) + require.NoError(t, err) + require.Equal(t, On, val) + val, err = sv.Validate(vars, "0", ScopeSession) + require.NoError(t, err) + require.Equal(t, Off, val) +} + +func TestTimeValidation(t *testing.T) { + sv := SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: "23:59 +0000", Type: TypeTime} + vars := NewSessionVars(nil) + + val, err := sv.Validate(vars, "23:59 +0000", ScopeSession) + require.NoError(t, err) + require.Equal(t, "23:59 +0000", val) + + val, err = sv.Validate(vars, "3:00 +0000", ScopeSession) + require.NoError(t, err) + require.Equal(t, "03:00 +0000", val) + + _, err = sv.Validate(vars, "0.000", ScopeSession) + require.Error(t, err) +} + +func TestGetNativeValType(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool} + + nativeVal, nativeType, flag := sv.GetNativeValType("ON") + require.Equal(t, mysql.TypeLonglong, nativeType) + require.Equal(t, mysql.BinaryFlag, flag) + require.Equal(t, types.NewIntDatum(1), nativeVal) + + nativeVal, nativeType, flag = sv.GetNativeValType("OFF") + require.Equal(t, mysql.TypeLonglong, nativeType) + require.Equal(t, mysql.BinaryFlag, flag) + require.Equal(t, types.NewIntDatum(0), nativeVal) + + nativeVal, nativeType, flag = sv.GetNativeValType("bogus") + require.Equal(t, mysql.TypeLonglong, nativeType) + require.Equal(t, mysql.BinaryFlag, flag) + require.Equal(t, types.NewIntDatum(0), nativeVal) + + sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeUnsigned} + nativeVal, nativeType, flag = sv.GetNativeValType("1234") + require.Equal(t, mysql.TypeLonglong, nativeType) + require.Equal(t, mysql.UnsignedFlag|mysql.BinaryFlag, flag) + require.Equal(t, types.NewUintDatum(1234), nativeVal) + nativeVal, nativeType, flag = sv.GetNativeValType("bogus") + require.Equal(t, mysql.TypeLonglong, nativeType) + require.Equal(t, mysql.UnsignedFlag|mysql.BinaryFlag, flag) + require.Equal(t, types.NewUintDatum(0), nativeVal) // converts to zero + + sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "abc"} + nativeVal, nativeType, flag = sv.GetNativeValType("1234") + require.Equal(t, mysql.TypeVarString, nativeType) + require.Equal(t, uint(0), flag) + require.Equal(t, types.NewStringDatum("1234"), nativeVal) +} + +func TestSynonyms(t *testing.T) { + sysVar := GetSysVar(TxnIsolation) + require.NotNil(t, sysVar) + + vars := NewSessionVars(nil) + + // It does not permit SERIALIZABLE by default. + _, err := sysVar.Validate(vars, "SERIALIZABLE", ScopeSession) + require.Error(t, err) + require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error()) + + // Enable Skip isolation check + require.Nil(t, GetSysVar(TiDBSkipIsolationLevelCheck).SetSessionFromHook(vars, "ON")) + + // Serializable is now permitted. + _, err = sysVar.Validate(vars, "SERIALIZABLE", ScopeSession) + require.NoError(t, err) + + // Currently TiDB returns a warning because of SERIALIZABLE, but in future + // it may also return a warning because TxnIsolation is deprecated. + + warn := vars.StmtCtx.GetWarnings()[0].Err + require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", warn.Error()) + + require.Nil(t, sysVar.SetSessionFromHook(vars, "SERIALIZABLE")) + + // When we set TxnIsolation, it also updates TransactionIsolation. + require.Equal(t, "SERIALIZABLE", vars.systems[TxnIsolation]) + require.Equal(t, vars.systems[TxnIsolation], vars.systems[TransactionIsolation]) +} + +func TestDeprecation(t *testing.T) { + sysVar := GetSysVar(TiDBIndexLookupConcurrency) + require.NotNil(t, sysVar) + + vars := NewSessionVars(nil) + + _, err := sysVar.Validate(vars, "123", ScopeSession) + require.NoError(t, err) + + // There was no error but there is a deprecation warning. + warn := vars.StmtCtx.GetWarnings()[0].Err + require.Equal(t, "[variable:1287]'tidb_index_lookup_concurrency' is deprecated and will be removed in a future release. Please use tidb_executor_concurrency instead", warn.Error()) +} + +func TestScope(t *testing.T) { + sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.True(t, sv.HasSessionScope()) + require.True(t, sv.HasGlobalScope()) + require.False(t, sv.HasInstanceScope()) + require.False(t, sv.HasNoneScope()) + + sv = SysVar{Scope: ScopeGlobal, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.False(t, sv.HasSessionScope()) + require.True(t, sv.HasGlobalScope()) + require.False(t, sv.HasInstanceScope()) + require.False(t, sv.HasNoneScope()) + + sv = SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.True(t, sv.HasSessionScope()) + require.False(t, sv.HasGlobalScope()) + require.False(t, sv.HasInstanceScope()) + require.False(t, sv.HasNoneScope()) + + sv = SysVar{Scope: ScopeNone, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.False(t, sv.HasSessionScope()) + require.False(t, sv.HasGlobalScope()) + require.False(t, sv.HasInstanceScope()) + require.True(t, sv.HasNoneScope()) + + sv = SysVar{Scope: ScopeInstance, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.False(t, sv.HasSessionScope()) + require.False(t, sv.HasGlobalScope()) + require.True(t, sv.HasInstanceScope()) + require.False(t, sv.HasNoneScope()) + + sv = SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} + require.Error(t, sv.validateScope(ScopeGlobal)) +} + +func TestBuiltInCase(t *testing.T) { + // All Sysvars should have lower case names. + // This tests builtins. + for name := range GetSysVars() { + require.Equal(t, strings.ToLower(name), name) + } +} + +// TestIsNoop is used by the documentation to auto-generate docs for real sysvars. +func TestIsNoop(t *testing.T) { + sv := GetSysVar(TiDBMultiStatementMode) + require.False(t, sv.IsNoop) + + sv = GetSysVar(InnodbLockWaitTimeout) + require.False(t, sv.IsNoop) + + sv = GetSysVar(InnodbFastShutdown) + require.True(t, sv.IsNoop) + + sv = GetSysVar(ReadOnly) + require.True(t, sv.IsNoop) +} + +// TestDefaultValuesAreSettable that sysvars defaults are logically valid. i.e. +// the default itself must validate without error provided the scope and read-only is correct. +// The default values should also be normalized for consistency. +func TestDefaultValuesAreSettable(t *testing.T) { + vars := NewSessionVars(nil) + vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests() + for _, sv := range GetSysVars() { + if sv.HasSessionScope() && !sv.ReadOnly { + val, err := sv.Validate(vars, sv.Value, ScopeSession) + require.NoError(t, err) + require.Equal(t, val, sv.Value) + } + + if sv.HasGlobalScope() && !sv.ReadOnly { + val, err := sv.Validate(vars, sv.Value, ScopeGlobal) + require.NoError(t, err) + require.Equal(t, val, sv.Value) + } + } +} + +func TestLimitBetweenVariable(t *testing.T) { + require.Less(t, DefTiDBGOGCTunerThreshold+0.05, DefTiDBServerMemoryLimitGCTrigger) +} + +// TestSysVarNameIsLowerCase tests that no new sysvars are added with uppercase characters. +// In MySQL variables are always lowercase, and can be set in a case-insensitive way. +func TestSysVarNameIsLowerCase(t *testing.T) { + for _, sv := range GetSysVars() { + require.Equal(t, strings.ToLower(sv.Name), sv.Name, "sysvar name contains uppercase characters") + } +} + +// TestSettersandGetters tests that sysvars are logically correct with getter and setter functions. +// i.e. it doesn't make sense to have a SetSession function on a variable that is only globally scoped. +func TestSettersandGetters(t *testing.T) { + for _, sv := range GetSysVars() { + if !sv.HasSessionScope() { + require.Nil(t, sv.SetSession) + require.Nil(t, sv.GetSession) + } + if !sv.HasGlobalScope() && !sv.HasInstanceScope() { + require.Nil(t, sv.SetGlobal) + if sv.Name == Timestamp { + // The Timestamp sysvar will have GetGlobal func even though it does not have global scope. + // It's GetGlobal func will only be called when "set timestamp = default". + continue + } + require.Nil(t, sv.GetGlobal) + } + } +} + +// TestSkipInitIsUsed ensures that no new variables are added with skipInit: true. +// This feature is deprecated, and if you need to run code to differentiate between init and "SET" (rare), +// you can instead check if s.StmtCtx.StmtType == "Set". +// The reason it is deprecated is that the behavior is typically wrong: +// it means session settings won't inherit from global and don't apply until you first set +// them in each session. This is a very weird behavior. +// See: https://github.com/pingcap/tidb/issues/35051 +func TestSkipInitIsUsed(t *testing.T) { + for _, sv := range GetSysVars() { + if sv.skipInit { + // skipInit only ever applied to session scope, so if anyone is setting it on + // a variable without session, that doesn't make sense. + require.True(t, sv.HasSessionScope(), fmt.Sprintf("skipInit has no effect on a variable without session scope: %s", sv.Name)) + // Since SetSession is the "init function" there is no init function to skip. + require.NotNil(t, sv.SetSession, fmt.Sprintf("skipInit has no effect on variables without an init (setsession) func: %s", sv.Name)) + // Skipinit has no use on noop funcs, since noop funcs always skipinit. + require.False(t, sv.IsNoop, fmt.Sprintf("skipInit has no effect on noop variables: %s", sv.Name)) + + // Test for variables that have a default of "0" or "OFF" + // If it is session-only scoped there is likely no bug now. + // If it is also global-scoped, then there is a bug as soon as the global changes. + if !(sv.Name == RandSeed1 || sv.Name == RandSeed2) { + // The bug is because the tests might not realize the SetSession func was not called on init, + // because it would initialize some session field to the empty value anyway. + require.NotEqual(t, "0", sv.Value, fmt.Sprintf("default value is zero: %s", sv.Name)) + require.NotEqual(t, "OFF", sv.Value, fmt.Sprintf("default value is OFF: %s", sv.Name)) + } + + // Many of these variables might allow skipInit to be removed, + // they need to be checked first. The purpose of this test is to make + // sure we don't introduce any new variables with skipInit, which seems + // to be a problem. + switch sv.Name { + case TiDBTxnScope, + TiDBSnapshot, + TiDBEnableChunkRPC, + TxnIsolationOneShot, + TiDBDDLReorgPriority, + TiDBSlowQueryFile, + TiDBWaitSplitRegionFinish, + TiDBWaitSplitRegionTimeout, + TiDBMetricSchemaStep, + TiDBMetricSchemaRangeDuration, + RandSeed1, + RandSeed2, + CollationDatabase, + CollationConnection, + CharsetDatabase, + CharacterSetConnection, + CharacterSetServer, + TiDBOptTiFlashConcurrencyFactor, + TiDBOptSeekFactor: + continue + } + require.Equal(t, false, sv.skipInit, fmt.Sprintf("skipInit should not be set on new system variables. variable %s is in violation", sv.Name)) + } + } +} + +func TestScopeToString(t *testing.T) { + require.Equal(t, "GLOBAL", ScopeGlobal.String()) + require.Equal(t, "SESSION", ScopeSession.String()) + require.Equal(t, "INSTANCE", ScopeInstance.String()) + require.Equal(t, "NONE", ScopeNone.String()) + tmp := ScopeSession + ScopeGlobal + require.Equal(t, "SESSION,GLOBAL", tmp.String()) + // this is not currently possible, but might be in future. + // *but* global + instance is not possible. these are mutually exclusive by design. + tmp = ScopeSession + ScopeInstance + require.Equal(t, "SESSION,INSTANCE", tmp.String()) +} + +func TestValidateWithRelaxedValidation(t *testing.T) { + sv := GetSysVar(SecureAuth) + vars := NewSessionVars(nil) + val := sv.ValidateWithRelaxedValidation(vars, "1", ScopeGlobal) + require.Equal(t, "ON", val) + + // Relaxed validation catches the error and squashes it. + // The incorrect value is returned as-is. + // I am not sure this is the correct behavior, we might need to + // change it to return the default instead in future. + sv = GetSysVar(DefaultAuthPlugin) + val = sv.ValidateWithRelaxedValidation(vars, "RandomText", ScopeGlobal) + require.Equal(t, "RandomText", val) + + // Validation func fails, the error is also caught and squashed. + // The incorrect value is returned as-is. + sv = GetSysVar(InitConnect) + val = sv.ValidateWithRelaxedValidation(vars, "RandomText - should be valid SQL", ScopeGlobal) + require.Equal(t, "RandomText - should be valid SQL", val) +} + +func TestInstanceConfigHasMatchingSysvar(t *testing.T) { + // This tests that each item in [instance] has a sysvar of the same name. + // The whole point of moving items to [instance] is to unify the name between + // config and sysvars. See: docs/design/2021-12-08-instance-scope.md#introduction + cfg, err := config.GetJSONConfig() + require.NoError(t, err) + var v interface{} + json.Unmarshal([]byte(cfg), &v) + data := v.(map[string]interface{}) + for k, v := range data { + if k != "instance" { + continue + } + instanceSection := v.(map[string]interface{}) + for instanceName := range instanceSection { + // Need to check there is a sysvar named instanceName. + sv := GetSysVar(instanceName) + require.NotNil(t, sv, fmt.Sprintf("config option: instance.%v requires a matching sysvar of the same name", instanceName)) + } + } +} + +func TestInstanceScope(t *testing.T) { + // Instance scope used to be settable via "SET SESSION", which is weird to any MySQL user. + // It is now settable via SET GLOBAL, but to work correctly a sysvar can only ever + // be INSTANCE scoped or GLOBAL scoped, never *both* at the same time (at least for now). + // Otherwise the semantics are confusing to users for how precedence applies. + + for _, sv := range GetSysVars() { + require.False(t, sv.HasGlobalScope() && sv.HasInstanceScope(), "sysvar %s has both instance and global scope", sv.Name) + if sv.HasInstanceScope() { + require.Nil(t, sv.GetSession) + require.Nil(t, sv.SetSession) + } + } + + count := len(GetSysVars()) + sv := SysVar{Scope: ScopeInstance, Name: "newinstancesysvar", Value: On, Type: TypeBool, + SetGlobal: func(_ context.Context, s *SessionVars, val string) error { + return fmt.Errorf("set should fail") + }, + GetGlobal: func(_ context.Context, s *SessionVars) (string, error) { + return "", fmt.Errorf("get should fail") + }, + } + + RegisterSysVar(&sv) + require.Len(t, GetSysVars(), count+1) + + sysVar := GetSysVar("newinstancesysvar") + require.NotNil(t, sysVar) + + vars := NewSessionVars(nil) + + // It is a boolean, try to set it to a bogus value + _, err := sysVar.Validate(vars, "ABCD", ScopeInstance) + require.Error(t, err) + + // Boolean oN or 1 converts to canonical ON or OFF + normalizedVal, err := sysVar.Validate(vars, "oN", ScopeInstance) + require.Equal(t, "ON", normalizedVal) + require.NoError(t, err) + normalizedVal, err = sysVar.Validate(vars, "0", ScopeInstance) + require.Equal(t, "OFF", normalizedVal) + require.NoError(t, err) + + err = sysVar.SetGlobalFromHook(context.Background(), vars, "OFF", true) // default is on + require.Equal(t, "set should fail", err.Error()) + + // Test unregistration restores previous count + UnregisterSysVar("newinstancesysvar") + require.Equal(t, len(GetSysVars()), count) +} + +func TestSetSysVar(t *testing.T) { + vars := NewSessionVars(nil) + vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests() + originalVal := GetSysVar(SystemTimeZone).Value + SetSysVar(SystemTimeZone, "America/New_York") + require.Equal(t, "America/New_York", GetSysVar(SystemTimeZone).Value) + // Test alternative Get + val, err := GetSysVar(SystemTimeZone).GetGlobalFromHook(context.Background(), vars) + require.Nil(t, err) + require.Equal(t, "America/New_York", val) + SetSysVar(SystemTimeZone, originalVal) // restore + require.Equal(t, originalVal, GetSysVar(SystemTimeZone).Value) +} + +func TestSkipSysvarCache(t *testing.T) { + require.True(t, GetSysVar(TiDBGCEnable).SkipSysvarCache()) + require.True(t, GetSysVar(TiDBGCRunInterval).SkipSysvarCache()) + require.True(t, GetSysVar(TiDBGCLifetime).SkipSysvarCache()) + require.True(t, GetSysVar(TiDBGCConcurrency).SkipSysvarCache()) + require.True(t, GetSysVar(TiDBGCScanLockMode).SkipSysvarCache()) + require.False(t, GetSysVar(TiDBEnableAsyncCommit).SkipSysvarCache()) +} + +func TestTimeValidationWithTimezone(t *testing.T) { + sv := SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: "23:59 +0000", Type: TypeTime} + vars := NewSessionVars(nil) + + // In timezone UTC + vars.TimeZone = time.UTC + val, err := sv.Validate(vars, "23:59", ScopeSession) + require.NoError(t, err) + require.Equal(t, "23:59 +0000", val) + + // In timezone Asia/Shanghai + vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + val, err = sv.Validate(vars, "23:59", ScopeSession) + require.NoError(t, err) + require.Equal(t, "23:59 +0800", val) +} + +func TestOrderByDependency(t *testing.T) { + // Some other exceptions: + // - tidb_snapshot and tidb_read_staleness can not be set at the same time. It doesn't affect dependency. + vars := map[string]string{ + "unknown": "1", + TxReadOnly: "1", + SQLAutoIsNull: "1", + TiDBEnableNoopFuncs: "1", + TiDBEnforceMPPExecution: "1", + TiDBAllowMPPExecution: "1", + TiDBTxnScope: kv.LocalTxnScope, + TiDBEnableLocalTxn: "1", + TiDBEnablePlanReplayerContinuousCapture: "1", + TiDBEnableHistoricalStats: "1", + } + names := OrderByDependency(vars) + require.Greater(t, slices.Index(names, TxReadOnly), slices.Index(names, TiDBEnableNoopFuncs)) + require.Greater(t, slices.Index(names, SQLAutoIsNull), slices.Index(names, TiDBEnableNoopFuncs)) + require.Greater(t, slices.Index(names, TiDBEnforceMPPExecution), slices.Index(names, TiDBAllowMPPExecution)) + // Depended variables below are global variables, so actually it doesn't matter. + require.Greater(t, slices.Index(names, TiDBTxnScope), slices.Index(names, TiDBEnableLocalTxn)) + require.Greater(t, slices.Index(names, TiDBEnablePlanReplayerContinuousCapture), slices.Index(names, TiDBEnableHistoricalStats)) + require.Contains(t, names, "unknown") +} diff --git a/sessionctx/variable/varsutil.go b/pkg/sessionctx/variable/varsutil.go similarity index 98% rename from sessionctx/variable/varsutil.go rename to pkg/sessionctx/variable/varsutil.go index 6a826cbb91cba..37abdc355f08d 100644 --- a/sessionctx/variable/varsutil.go +++ b/pkg/sessionctx/variable/varsutil.go @@ -25,12 +25,12 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/tikv/client-go/v2/oracle" ) diff --git a/sessionctx/variable/varsutil_test.go b/pkg/sessionctx/variable/varsutil_test.go similarity index 99% rename from sessionctx/variable/varsutil_test.go rename to pkg/sessionctx/variable/varsutil_test.go index c1d7da47c6433..975cea729ac48 100644 --- a/sessionctx/variable/varsutil_test.go +++ b/pkg/sessionctx/variable/varsutil_test.go @@ -21,10 +21,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" "github.com/stretchr/testify/require" ) diff --git a/pkg/sessiontxn/BUILD.bazel b/pkg/sessiontxn/BUILD.bazel new file mode 100644 index 0000000000000..8a68b49ed6001 --- /dev/null +++ b/pkg/sessiontxn/BUILD.bazel @@ -0,0 +1,55 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sessiontxn", + srcs = [ + "failpoint.go", + "future.go", + "interface.go", + ], + importpath = "github.com/pingcap/tidb/pkg/sessiontxn", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/sessionctx", + "//pkg/util/stringutil", + ], +) + +go_test( + name = "sessiontxn_test", + timeout = "short", + srcs = [ + "txn_context_test.go", + "txn_manager_test.go", + "txn_rc_tso_optimize_test.go", + ], + flaky = True, + shard_count = 25, + deps = [ + ":sessiontxn", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessiontxn/internal", + "//pkg/sessiontxn/isolation", + "//pkg/sessiontxn/staleread", + "//pkg/table/temptable", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testfork", + "//pkg/testkit/testsetup", + "//tests/realtikvtest", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/sessiontxn/failpoint.go b/pkg/sessiontxn/failpoint.go new file mode 100644 index 0000000000000..3f237711fed49 --- /dev/null +++ b/pkg/sessiontxn/failpoint.go @@ -0,0 +1,181 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package sessiontxn + +import ( + "fmt" + "time" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/stringutil" +) + +// AssertRecordsKey is used to save failPoint invoke records +// Only for test +var AssertRecordsKey stringutil.StringerStr = "assertTxnManagerRecords" + +// AssertTxnInfoSchemaKey is used to set the expected infoschema that should be check in failPoint +// Only for test +var AssertTxnInfoSchemaKey stringutil.StringerStr = "assertTxnInfoSchemaKey" + +// AssertTxnInfoSchemaAfterRetryKey is used to set the expected infoschema that should be check in failPoint after retry +// Only for test +var AssertTxnInfoSchemaAfterRetryKey stringutil.StringerStr = "assertTxnInfoSchemaAfterRetryKey" + +// BreakPointBeforeExecutorFirstRun is the key for the stop point where session stops before executor's first run +// Only for test +var BreakPointBeforeExecutorFirstRun = "beforeExecutorFirstRun" + +// BreakPointOnStmtRetryAfterLockError s the key for the stop point where session stops after OnStmtRetry when lock error happens +// Only for test +var BreakPointOnStmtRetryAfterLockError = "lockErrorAndThenOnStmtRetryCalled" + +// TsoRequestCount is the key for recording tso request counts in some places +var TsoRequestCount stringutil.StringerStr = "tsoRequestCount" + +// TsoWaitCount doesn't include begin and commit +var TsoWaitCount stringutil.StringerStr = "tsoWaitCount" + +// TsoUseConstantCount is the key for constant tso counter +var TsoUseConstantCount stringutil.StringerStr = "tsoUseConstantCount" + +// CallOnStmtRetryCount is the key for recording calling OnStmtRetry at RC isolation level +var CallOnStmtRetryCount stringutil.StringerStr = "callOnStmtRetryCount" + +// AssertLockErr is used to record the lock errors we encountered +// Only for test +var AssertLockErr stringutil.StringerStr = "assertLockError" + +// RecordAssert is used only for test +func RecordAssert(sctx sessionctx.Context, name string, value interface{}) { + records, ok := sctx.Value(AssertRecordsKey).(map[string]interface{}) + if !ok { + records = make(map[string]interface{}) + sctx.SetValue(AssertRecordsKey, records) + } + records[name] = value +} + +// AssertTxnManagerInfoSchema is used only for test +func AssertTxnManagerInfoSchema(sctx sessionctx.Context, is interface{}) { + assertVersion := func(expected interface{}) { + if expected == nil { + return + } + + expectVer := expected.(infoschema.InfoSchema).SchemaMetaVersion() + gotVer := GetTxnManager(sctx).GetTxnInfoSchema().SchemaMetaVersion() + if gotVer != expectVer { + panic(fmt.Sprintf("Txn schema version not match, expect:%d, got:%d", expectVer, gotVer)) + } + } + + if localTables := sctx.GetSessionVars().LocalTemporaryTables; localTables != nil { + got, ok := GetTxnManager(sctx).GetTxnInfoSchema().(*infoschema.SessionExtendedInfoSchema) + if !ok { + panic("Expected to be a SessionExtendedInfoSchema") + } + + if got.LocalTemporaryTables != localTables { + panic("Local tables should be the same with the one in session") + } + } + + assertVersion(is) + assertVersion(sctx.Value(AssertTxnInfoSchemaKey)) +} + +// AssertTxnManagerReadTS is used only for test +func AssertTxnManagerReadTS(sctx sessionctx.Context, expected uint64) { + actual, err := GetTxnManager(sctx).GetStmtReadTS() + if err != nil { + panic(err) + } + + if actual != expected { + panic(fmt.Sprintf("Txn read ts not match, expect:%d, got:%d", expected, actual)) + } +} + +// AddAssertEntranceForLockError is used only for test +func AddAssertEntranceForLockError(sctx sessionctx.Context, name string) { + records, ok := sctx.Value(AssertLockErr).(map[string]int) + if !ok { + records = make(map[string]int) + sctx.SetValue(AssertLockErr, records) + } + if v, ok := records[name]; ok { + records[name] = v + 1 + } else { + records[name] = 1 + } +} + +// TsoRequestCountInc is used only for test +// When it is called, there is a tso cmd request. +func TsoRequestCountInc(sctx sessionctx.Context) { + count, ok := sctx.Value(TsoRequestCount).(uint64) + if !ok { + count = 0 + } + count++ + sctx.SetValue(TsoRequestCount, count) +} + +// TsoWaitCountInc is used only for test +// When it is called, there is a waiting tso operation +func TsoWaitCountInc(sctx sessionctx.Context) { + count, ok := sctx.Value(TsoWaitCount).(uint64) + if !ok { + count = 0 + } + count++ + sctx.SetValue(TsoWaitCount, count) +} + +// TsoUseConstantCountInc is used to test constant tso count +func TsoUseConstantCountInc(sctx sessionctx.Context) { + count, ok := sctx.Value(TsoUseConstantCount).(uint64) + if !ok { + count = 0 + } + count++ + sctx.SetValue(TsoUseConstantCount, count) +} + +// OnStmtRetryCountInc is used only for test. +// When it is called, there is calling `(p *PessimisticRCTxnContextProvider) OnStmtRetry`. +func OnStmtRetryCountInc(sctx sessionctx.Context) { + count, ok := sctx.Value(CallOnStmtRetryCount).(int) + if !ok { + count = 0 + } + count++ + sctx.SetValue(CallOnStmtRetryCount, count) +} + +// ExecTestHook is used only for test. It consumes hookKey in session wait do what it gets from it. +func ExecTestHook(sctx sessionctx.Context, hookKey fmt.Stringer) { + c := sctx.Value(hookKey) + if ch, ok := c.(chan func()); ok { + select { + case fn := <-ch: + fn() + case <-time.After(time.Second * 10): + panic("timeout waiting for chan") + } + } +} diff --git a/sessiontxn/future.go b/pkg/sessiontxn/future.go similarity index 100% rename from sessiontxn/future.go rename to pkg/sessiontxn/future.go diff --git a/pkg/sessiontxn/interface.go b/pkg/sessiontxn/interface.go new file mode 100644 index 0000000000000..ec40a79e17d5b --- /dev/null +++ b/pkg/sessiontxn/interface.go @@ -0,0 +1,237 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package sessiontxn + +import ( + "context" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// EnterNewTxnType is the type to enter a new txn +type EnterNewTxnType int + +const ( + // EnterNewTxnDefault means to enter a new txn. Its behavior is more straight-forward + // just starting a new txn right now without any scenario assumptions. + EnterNewTxnDefault EnterNewTxnType = iota + // EnterNewTxnWithBeginStmt indicates to enter a new txn when execute 'BEGIN' or 'START TRANSACTION' + EnterNewTxnWithBeginStmt + // EnterNewTxnBeforeStmt indicates to enter a new txn before each statement when the txn is not present + // If `EnterNewTxnBeforeStmt` is used, the new txn will always act as the 'lazy' mode. That means the inner transaction + // is only activated when needed to reduce unnecessary overhead. + EnterNewTxnBeforeStmt + // EnterNewTxnWithReplaceProvider indicates to replace the current provider. Now only stale read are using this + EnterNewTxnWithReplaceProvider +) + +// EnterNewTxnRequest is the request when entering a new transaction +type EnterNewTxnRequest struct { + // Type is the type for new entering a new txn + Type EnterNewTxnType + // provider is the context provider + Provider TxnContextProvider + // txnMode is the transaction mode for the new txn. It has 3 values: `ast.Pessimistic` ,`ast.Optimistic` or empty/ + // When the value is empty, it means the value will be determined from sys vars. + TxnMode string + // causalConsistencyOnly means whether enable causal consistency for transactions, default is false + CausalConsistencyOnly bool + // staleReadTS indicates the read ts for the stale read transaction. + // The default value is zero which means not a stale read transaction. + StaleReadTS uint64 +} + +// StmtErrorHandlePoint is where the error is being handled +type StmtErrorHandlePoint int + +const ( + // StmtErrAfterQuery means we are handling an error after the query failed + StmtErrAfterQuery StmtErrorHandlePoint = iota + // StmtErrAfterPessimisticLock means we are handling an error after pessimistic lock failed. + StmtErrAfterPessimisticLock +) + +// StmtErrorAction is the next action advice when an error occurs when executing a statement +type StmtErrorAction int + +const ( + // StmtActionError means the error should be returned directly without any retry + StmtActionError StmtErrorAction = iota + // StmtActionRetryReady means the error is caused by this component, and it is ready for retry. + StmtActionRetryReady + // StmtActionNoIdea means the error is not caused by this component, and whether retry or not should be determined by other components. + // If the user do not know whether to retry or not, it is advised to return the original error. + StmtActionNoIdea +) + +// ErrorAction returns StmtActionError with specified error +func ErrorAction(err error) (StmtErrorAction, error) { + return StmtActionError, err +} + +// RetryReady returns StmtActionRetryReady, nil +func RetryReady() (StmtErrorAction, error) { + return StmtActionRetryReady, nil +} + +// NoIdea returns StmtActionNoIdea, nil +func NoIdea() (StmtErrorAction, error) { + return StmtActionNoIdea, nil +} + +// TxnAdvisable providers a collection of optimizations within transaction +type TxnAdvisable interface { + // AdviseWarmup provides warmup for inner state + AdviseWarmup() error + // AdviseOptimizeWithPlan providers optimization according to the plan + AdviseOptimizeWithPlan(plan interface{}) error +} + +// OptimizeWithPlanAndThenWarmUp first do `AdviseOptimizeWithPlan` to optimize the txn with plan +// and then do `AdviseWarmup` to do some tso fetch if necessary +func OptimizeWithPlanAndThenWarmUp(sctx sessionctx.Context, plan interface{}) error { + txnManager := GetTxnManager(sctx) + if err := txnManager.AdviseOptimizeWithPlan(plan); err != nil { + return err + } + return txnManager.AdviseWarmup() +} + +// TxnContextProvider provides txn context +type TxnContextProvider interface { + TxnAdvisable + // GetTxnInfoSchema returns the information schema used by txn + GetTxnInfoSchema() infoschema.InfoSchema + // GetTxnScope returns the current txn scope + GetTxnScope() string + // GetReadReplicaScope returns the read replica scope + GetReadReplicaScope() string + // GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) + GetStmtReadTS() (uint64, error) + // GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update + GetStmtForUpdateTS() (uint64, error) + // GetSnapshotWithStmtReadTS gets snapshot with read ts + GetSnapshotWithStmtReadTS() (kv.Snapshot, error) + // GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts + GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) + + // OnInitialize is the hook that should be called when enter a new txn with this provider + OnInitialize(ctx context.Context, enterNewTxnType EnterNewTxnType) error + // OnStmtStart is the hook that should be called when a new statement started + OnStmtStart(ctx context.Context, node ast.StmtNode) error + // OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or + // a pessimistic select-for-update statement. + OnPessimisticStmtStart(ctx context.Context) error + // OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or + // select-for-update statement. + OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error + // OnStmtErrorForNextAction is the hook that should be called when a new statement get an error + OnStmtErrorForNextAction(ctx context.Context, point StmtErrorHandlePoint, err error) (StmtErrorAction, error) + // OnStmtRetry is the hook that should be called when a statement is retried internally. + OnStmtRetry(ctx context.Context) error + // OnStmtCommit is the hook that should be called when a statement is executed successfully. + OnStmtCommit(ctx context.Context) error + // OnStmtRollback is the hook that should be called when a statement fails to execute. + OnStmtRollback(ctx context.Context, isForPessimisticRetry bool) error + // OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. + OnLocalTemporaryTableCreated() + // ActivateTxn activates the transaction. + ActivateTxn() (kv.Transaction, error) +} + +// TxnManager is an interface providing txn context management in session +type TxnManager interface { + TxnAdvisable + // GetTxnInfoSchema returns the information schema used by txn + // If the session is not in any transaction, for example: between two autocommit statements, + // this method will return the latest information schema in session that is same with `sessionctx.GetDomainInfoSchema()` + GetTxnInfoSchema() infoschema.InfoSchema + // GetTxnScope returns the current txn scope + GetTxnScope() string + // GetReadReplicaScope returns the read replica scope + GetReadReplicaScope() string + // GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) + // Calling this method will activate the txn implicitly if current read is not stale/historical read + GetStmtReadTS() (uint64, error) + // GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update + // Calling this method will activate the txn implicitly if current read is not stale/historical read + GetStmtForUpdateTS() (uint64, error) + // GetContextProvider returns the current TxnContextProvider + GetContextProvider() TxnContextProvider + // GetSnapshotWithStmtReadTS gets snapshot with read ts + GetSnapshotWithStmtReadTS() (kv.Snapshot, error) + // GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts + GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) + + // EnterNewTxn enters a new transaction. + EnterNewTxn(ctx context.Context, req *EnterNewTxnRequest) error + // OnTxnEnd is the hook that should be called after transaction commit or rollback + OnTxnEnd() + // OnStmtStart is the hook that should be called when a new statement started + OnStmtStart(ctx context.Context, node ast.StmtNode) error + // OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or + // a pessimistic select-for-update statement. + OnPessimisticStmtStart(ctx context.Context) error + // OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or + // select-for-update statement. + OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error + // OnStmtErrorForNextAction is the hook that should be called when a new statement get an error + // This method is not required to be called for every error in the statement, + // it is only required to be called for some errors handled in some specified points given by the parameter `point`. + // When the return error is not nil the return action is 'StmtActionError' and vice versa. + OnStmtErrorForNextAction(ctx context.Context, point StmtErrorHandlePoint, err error) (StmtErrorAction, error) + // OnStmtRetry is the hook that should be called when a statement retry + OnStmtRetry(ctx context.Context) error + // OnStmtCommit is the hook that should be called when a statement is executed successfully. + OnStmtCommit(ctx context.Context) error + // OnStmtRollback is the hook that should be called when a statement fails to execute. + OnStmtRollback(ctx context.Context, isForPessimisticRetry bool) error + // OnStmtEnd is called when a statement ends, together with txn.onStmtEnd() + OnStmtEnd() + // OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. + OnLocalTemporaryTableCreated() + // ActivateTxn activates the transaction. + ActivateTxn() (kv.Transaction, error) + // GetCurrentStmt returns the current statement node + GetCurrentStmt() ast.StmtNode +} + +// NewTxn starts a new optimistic and active txn, it can be used for the below scenes: +// 1. Commit the current transaction and do some work in a new transaction for some specific operations, for example: DDL +// 2. Some background job need to do something in a transaction. +// In other scenes like 'BEGIN', 'START TRANSACTION' or prepare transaction in a new statement, +// you should use `TxnManager`.`EnterNewTxn` and pass the relevant to it. +func NewTxn(ctx context.Context, sctx sessionctx.Context) error { + return GetTxnManager(sctx).EnterNewTxn(ctx, &EnterNewTxnRequest{ + Type: EnterNewTxnDefault, + TxnMode: ast.Optimistic, + }) +} + +// NewTxnInStmt is like `NewTxn` but it will call `OnStmtStart` after it. +// It should be used when a statement already started. +func NewTxnInStmt(ctx context.Context, sctx sessionctx.Context) error { + if err := NewTxn(ctx, sctx); err != nil { + return err + } + txnManager := GetTxnManager(sctx) + return txnManager.OnStmtStart(ctx, txnManager.GetCurrentStmt()) +} + +// GetTxnManager returns the TxnManager object from session context +var GetTxnManager func(sctx sessionctx.Context) TxnManager diff --git a/pkg/sessiontxn/internal/BUILD.bazel b/pkg/sessiontxn/internal/BUILD.bazel new file mode 100644 index 0000000000000..a7e2729fe35a8 --- /dev/null +++ b/pkg/sessiontxn/internal/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = ["txn.go"], + importpath = "github.com/pingcap/tidb/pkg/sessiontxn/internal", + visibility = ["//pkg/sessiontxn:__subpackages__"], + deps = [ + "//pkg/kv", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/util/logutil", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/sessiontxn/internal/txn.go b/pkg/sessiontxn/internal/txn.go new file mode 100644 index 0000000000000..5b03a284c1440 --- /dev/null +++ b/pkg/sessiontxn/internal/txn.go @@ -0,0 +1,82 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "context" + + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// SetTxnAssertionLevel sets assertion level of a transactin. Note that assertion level should be set only once just +// after creating a new transaction. +func SetTxnAssertionLevel(txn kv.Transaction, assertionLevel variable.AssertionLevel) { + switch assertionLevel { + case variable.AssertionLevelOff: + txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Off) + case variable.AssertionLevelFast: + txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Fast) + case variable.AssertionLevelStrict: + txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Strict) + } +} + +// CommitBeforeEnterNewTxn is called before entering a new transaction. It checks whether the old +// txn is valid in which case we should commit it first. +func CommitBeforeEnterNewTxn(ctx context.Context, sctx sessionctx.Context) error { + txn, err := sctx.Txn(false) + if err != nil { + return err + } + if txn.Valid() { + txnStartTS := txn.StartTS() + txnScope := sctx.GetSessionVars().TxnCtx.TxnScope + err = sctx.CommitTxn(ctx) + if err != nil { + return err + } + logutil.Logger(ctx).Info("Try to create a new txn inside a transaction auto commit", + zap.Int64("schemaVersion", sctx.GetInfoSchema().SchemaMetaVersion()), + zap.Uint64("txnStartTS", txnStartTS), + zap.String("txnScope", txnScope)) + } + return nil +} + +// GetSnapshotWithTS returns a snapshot with ts. +func GetSnapshotWithTS(s sessionctx.Context, ts uint64, interceptor kv.SnapshotInterceptor) kv.Snapshot { + snap := s.GetStore().GetSnapshot(kv.Version{Ver: ts}) + if interceptor != nil { + snap.SetOption(kv.SnapInterceptor, interceptor) + } + if s.GetSessionVars().InRestrictedSQL { + snap.SetOption(kv.RequestSourceInternal, true) + } + if tp := s.GetSessionVars().RequestSourceType; tp != "" { + snap.SetOption(kv.RequestSourceType, tp) + } + if tp := s.GetSessionVars().ExplicitRequestSourceType; tp != "" { + snap.SetOption(kv.ExplicitRequestSourceType, tp) + } + if s.GetSessionVars().LoadBasedReplicaReadThreshold > 0 { + snap.SetOption(kv.LoadBasedReplicaReadThreshold, s.GetSessionVars().LoadBasedReplicaReadThreshold) + } + return snap +} diff --git a/pkg/sessiontxn/isolation/BUILD.bazel b/pkg/sessiontxn/isolation/BUILD.bazel new file mode 100644 index 0000000000000..c9b7b3c3c1c58 --- /dev/null +++ b/pkg/sessiontxn/isolation/BUILD.bazel @@ -0,0 +1,76 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "isolation", + srcs = [ + "base.go", + "optimistic.go", + "readcommitted.go", + "repeatable_read.go", + "serializable.go", + ], + importpath = "github.com/pingcap/tidb/pkg/sessiontxn/isolation", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/internal", + "//pkg/sessiontxn/isolation/metrics", + "//pkg/sessiontxn/staleread", + "//pkg/table/temptable", + "//pkg/util/logutil", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "isolation_test", + timeout = "short", + srcs = [ + "main_test.go", + "optimistic_test.go", + "readcommitted_test.go", + "repeatable_read_test.go", + "serializable_test.go", + ], + flaky = True, + shard_count = 29, + deps = [ + ":isolation", + "//pkg/config", + "//pkg/executor", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/planner", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessiontxn", + "//pkg/testkit", + "//pkg/testkit/testfork", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/sessiontxn/isolation/base.go b/pkg/sessiontxn/isolation/base.go new file mode 100644 index 0000000000000..198cd707b18f7 --- /dev/null +++ b/pkg/sessiontxn/isolation/base.go @@ -0,0 +1,575 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package isolation + +import ( + "context" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/internal" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/tikv/client-go/v2/oracle" +) + +// baseTxnContextProvider is a base class for the transaction context providers that implement `TxnContextProvider` in different isolation. +// It provides some common functions below: +// - Provides a default `OnInitialize` method to initialize its inner state. +// - Provides some methods like `activateTxn` and `prepareTxn` to manage the inner transaction. +// - Provides default methods `GetTxnInfoSchema`, `GetStmtReadTS` and `GetStmtForUpdateTS` and return the snapshot information schema or ts when `tidb_snapshot` is set. +// - Provides other default methods like `Advise`, `OnStmtStart`, `OnStmtRetry` and `OnStmtErrorForNextAction` +// +// The subclass can set some inner property of `baseTxnContextProvider` when it is constructed. +// For example, `getStmtReadTSFunc` and `getStmtForUpdateTSFunc` should be set, and they will be called when `GetStmtReadTS` +// or `GetStmtForUpdate` to get the timestamp that should be used by the corresponding isolation level. +type baseTxnContextProvider struct { + // States that should be initialized when baseTxnContextProvider is created and should not be changed after that + sctx sessionctx.Context + causalConsistencyOnly bool + onInitializeTxnCtx func(*variable.TransactionContext) + onTxnActiveFunc func(kv.Transaction, sessiontxn.EnterNewTxnType) + getStmtReadTSFunc func() (uint64, error) + getStmtForUpdateTSFunc func() (uint64, error) + + // Runtime states + ctx context.Context + infoSchema infoschema.InfoSchema + txn kv.Transaction + isTxnPrepared bool + enterNewTxnType sessiontxn.EnterNewTxnType + // constStartTS is only used by point get max ts optimization currently. + // When constStartTS != 0, we use constStartTS directly without fetching it from tso. + // To save the cpu cycles `PrepareTSFuture` will also not be called when warmup (postpone to activate txn). + constStartTS uint64 +} + +// OnInitialize is the hook that should be called when enter a new txn with this provider +func (p *baseTxnContextProvider) OnInitialize(ctx context.Context, tp sessiontxn.EnterNewTxnType) (err error) { + if p.getStmtReadTSFunc == nil || p.getStmtForUpdateTSFunc == nil { + return errors.New("ts functions should not be nil") + } + + p.ctx = ctx + sessVars := p.sctx.GetSessionVars() + activeNow := true + switch tp { + case sessiontxn.EnterNewTxnDefault: + // As we will enter a new txn, we need to commit the old txn if it's still valid. + // There are two main steps here to enter a new txn: + // 1. prepareTxnWithOracleTS + // 2. ActivateTxn + if err := internal.CommitBeforeEnterNewTxn(p.ctx, p.sctx); err != nil { + return err + } + if err := p.prepareTxnWithOracleTS(); err != nil { + return err + } + case sessiontxn.EnterNewTxnWithBeginStmt: + if !canReuseTxnWhenExplicitBegin(p.sctx) { + // As we will enter a new txn, we need to commit the old txn if it's still valid. + // There are two main steps here to enter a new txn: + // 1. prepareTxnWithOracleTS + // 2. ActivateTxn + if err := internal.CommitBeforeEnterNewTxn(p.ctx, p.sctx); err != nil { + return err + } + if err := p.prepareTxnWithOracleTS(); err != nil { + return err + } + } + sessVars.SetInTxn(true) + case sessiontxn.EnterNewTxnBeforeStmt: + activeNow = false + default: + return errors.Errorf("Unsupported type: %v", tp) + } + + p.enterNewTxnType = tp + p.infoSchema = p.sctx.GetDomainInfoSchema().(infoschema.InfoSchema) + txnCtx := &variable.TransactionContext{ + TxnCtxNoNeedToRestore: variable.TxnCtxNoNeedToRestore{ + CreateTime: time.Now(), + InfoSchema: p.infoSchema, + ShardStep: int(sessVars.ShardAllocateStep), + TxnScope: sessVars.CheckAndGetTxnScope(), + }, + } + if p.onInitializeTxnCtx != nil { + p.onInitializeTxnCtx(txnCtx) + } + sessVars.TxnCtxMu.Lock() + sessVars.TxnCtx = txnCtx + sessVars.TxnCtxMu.Unlock() + if variable.EnableMDL.Load() { + sessVars.TxnCtx.EnableMDL = true + } + + txn, err := p.sctx.Txn(false) + if err != nil { + return err + } + p.isTxnPrepared = txn.Valid() || p.sctx.GetPreparedTxnFuture() != nil + if activeNow { + _, err = p.ActivateTxn() + } + + return err +} + +// GetTxnInfoSchema returns the information schema used by txn +func (p *baseTxnContextProvider) GetTxnInfoSchema() infoschema.InfoSchema { + if is := p.sctx.GetSessionVars().SnapshotInfoschema; is != nil { + return is.(infoschema.InfoSchema) + } + if _, ok := p.infoSchema.(*infoschema.SessionExtendedInfoSchema); !ok { + p.infoSchema = &infoschema.SessionExtendedInfoSchema{ + InfoSchema: p.infoSchema, + } + p.sctx.GetSessionVars().TxnCtx.InfoSchema = p.infoSchema + } + return p.infoSchema +} + +// GetTxnScope returns the current txn scope +func (p *baseTxnContextProvider) GetTxnScope() string { + return p.sctx.GetSessionVars().TxnCtx.TxnScope +} + +// GetReadReplicaScope returns the read replica scope +func (p *baseTxnContextProvider) GetReadReplicaScope() string { + if txnScope := p.GetTxnScope(); txnScope != kv.GlobalTxnScope && txnScope != "" { + // In local txn, we should use txnScope as the readReplicaScope + return txnScope + } + + if p.sctx.GetSessionVars().GetReplicaRead().IsClosestRead() { + // If closest read is set, we should use the scope where instance located. + return config.GetTxnScopeFromConfig() + } + + // When it is not local txn or closet read, we should use global scope + return kv.GlobalReplicaScope +} + +// GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) +func (p *baseTxnContextProvider) GetStmtReadTS() (uint64, error) { + if _, err := p.ActivateTxn(); err != nil { + return 0, err + } + + if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { + return snapshotTS, nil + } + return p.getStmtReadTSFunc() +} + +// GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update +func (p *baseTxnContextProvider) GetStmtForUpdateTS() (uint64, error) { + if _, err := p.ActivateTxn(); err != nil { + return 0, err + } + + if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { + return snapshotTS, nil + } + return p.getStmtForUpdateTSFunc() +} + +// OnStmtStart is the hook that should be called when a new statement started +func (p *baseTxnContextProvider) OnStmtStart(ctx context.Context, _ ast.StmtNode) error { + p.ctx = ctx + return nil +} + +// OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or +// a pessimistic select-for-update statements. +func (p *baseTxnContextProvider) OnPessimisticStmtStart(_ context.Context) error { + return nil +} + +// OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or +// select-for-update statement. +func (p *baseTxnContextProvider) OnPessimisticStmtEnd(_ context.Context, _ bool) error { + return nil +} + +// OnStmtRetry is the hook that should be called when a statement is retried internally. +func (p *baseTxnContextProvider) OnStmtRetry(ctx context.Context) error { + p.ctx = ctx + p.sctx.GetSessionVars().TxnCtx.CurrentStmtPessimisticLockCache = nil + return nil +} + +// OnStmtCommit is the hook that should be called when a statement is executed successfully. +func (p *baseTxnContextProvider) OnStmtCommit(_ context.Context) error { + return nil +} + +// OnStmtRollback is the hook that should be called when a statement fails to execute. +func (p *baseTxnContextProvider) OnStmtRollback(_ context.Context, _ bool) error { + return nil +} + +// OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. +func (p *baseTxnContextProvider) OnLocalTemporaryTableCreated() { + p.infoSchema = temptable.AttachLocalTemporaryTableInfoSchema(p.sctx, p.infoSchema) + p.sctx.GetSessionVars().TxnCtx.InfoSchema = p.infoSchema + if p.txn != nil && p.txn.Valid() { + if interceptor := temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema); interceptor != nil { + p.txn.SetOption(kv.SnapInterceptor, interceptor) + } + } +} + +// OnStmtErrorForNextAction is the hook that should be called when a new statement get an error +func (p *baseTxnContextProvider) OnStmtErrorForNextAction(ctx context.Context, point sessiontxn.StmtErrorHandlePoint, err error) (sessiontxn.StmtErrorAction, error) { + switch point { + case sessiontxn.StmtErrAfterPessimisticLock: + // for pessimistic lock error, return the error by default + return sessiontxn.ErrorAction(err) + default: + return sessiontxn.NoIdea() + } +} + +func (p *baseTxnContextProvider) getTxnStartTS() (uint64, error) { + txn, err := p.ActivateTxn() + if err != nil { + return 0, err + } + return txn.StartTS(), nil +} + +// ActivateTxn activates the transaction and set the relevant context variables. +func (p *baseTxnContextProvider) ActivateTxn() (kv.Transaction, error) { + if p.txn != nil { + return p.txn, nil + } + + if err := p.prepareTxn(); err != nil { + return nil, err + } + + if p.constStartTS != 0 { + if err := p.replaceTxnTsFuture(sessiontxn.ConstantFuture(p.constStartTS)); err != nil { + return nil, err + } + } + + txnFuture := p.sctx.GetPreparedTxnFuture() + txn, err := txnFuture.Wait(p.ctx, p.sctx) + if err != nil { + return nil, err + } + + sessVars := p.sctx.GetSessionVars() + sessVars.TxnCtxMu.Lock() + sessVars.TxnCtx.StartTS = txn.StartTS() + sessVars.TxnCtxMu.Unlock() + if sessVars.MemDBFootprint != nil { + sessVars.MemDBFootprint.Detach() + } + sessVars.MemDBFootprint = nil + + if p.enterNewTxnType == sessiontxn.EnterNewTxnBeforeStmt && !sessVars.IsAutocommit() && sessVars.SnapshotTS == 0 { + sessVars.SetInTxn(true) + } + + txn.SetVars(sessVars.KVVars) + + readReplicaType := sessVars.GetReplicaRead() + if readReplicaType.IsFollowerRead() { + txn.SetOption(kv.ReplicaRead, readReplicaType) + } + + if interceptor := temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema); interceptor != nil { + txn.SetOption(kv.SnapInterceptor, interceptor) + } + + if sessVars.StmtCtx.WeakConsistency { + txn.SetOption(kv.IsolationLevel, kv.RC) + } + + internal.SetTxnAssertionLevel(txn, sessVars.AssertionLevel) + + if p.causalConsistencyOnly { + txn.SetOption(kv.GuaranteeLinearizability, false) + } + + if p.onTxnActiveFunc != nil { + p.onTxnActiveFunc(txn, p.enterNewTxnType) + } + + if p.sctx.GetSessionVars().InRestrictedSQL { + txn.SetOption(kv.RequestSourceInternal, true) + } + + if tp := p.sctx.GetSessionVars().RequestSourceType; tp != "" { + txn.SetOption(kv.RequestSourceType, tp) + } + + if sessVars.LoadBasedReplicaReadThreshold > 0 { + txn.SetOption(kv.LoadBasedReplicaReadThreshold, sessVars.LoadBasedReplicaReadThreshold) + } + + p.txn = txn + return txn, nil +} + +// prepareTxn prepares txn with an oracle ts future. If the snapshotTS is set, +// the txn is prepared with it. +func (p *baseTxnContextProvider) prepareTxn() error { + if p.isTxnPrepared { + return nil + } + + if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { + return p.replaceTxnTsFuture(sessiontxn.ConstantFuture(snapshotTS)) + } + + future := newOracleFuture(p.ctx, p.sctx, p.sctx.GetSessionVars().TxnCtx.TxnScope) + return p.replaceTxnTsFuture(future) +} + +// prepareTxnWithOracleTS +// The difference between prepareTxnWithOracleTS and prepareTxn is that prepareTxnWithOracleTS +// does not consider snapshotTS +func (p *baseTxnContextProvider) prepareTxnWithOracleTS() error { + if p.isTxnPrepared { + return nil + } + + future := newOracleFuture(p.ctx, p.sctx, p.sctx.GetSessionVars().TxnCtx.TxnScope) + return p.replaceTxnTsFuture(future) +} + +func (p *baseTxnContextProvider) forcePrepareConstStartTS(ts uint64) error { + if p.txn != nil { + return errors.New("cannot force prepare const start ts because txn is active") + } + p.constStartTS = ts + p.isTxnPrepared = true + return nil +} + +func (p *baseTxnContextProvider) replaceTxnTsFuture(future oracle.Future) error { + txn, err := p.sctx.Txn(false) + if err != nil { + return err + } + + if txn.Valid() { + return nil + } + + txnScope := p.sctx.GetSessionVars().TxnCtx.TxnScope + if err = p.sctx.PrepareTSFuture(p.ctx, future, txnScope); err != nil { + return err + } + + p.isTxnPrepared = true + return nil +} + +func (p *baseTxnContextProvider) isTidbSnapshotEnabled() bool { + return p.sctx.GetSessionVars().SnapshotTS != 0 +} + +// isBeginStmtWithStaleRead indicates whether the current statement is `BeginStmt` type with stale read +// Because stale read will use `staleread.StalenessTxnContextProvider` for query, so if `staleread.IsStmtStaleness()` +// returns true in other providers, it means the current statement is `BeginStmt` with stale read +func (p *baseTxnContextProvider) isBeginStmtWithStaleRead() bool { + return staleread.IsStmtStaleness(p.sctx) +} + +// AdviseWarmup provides warmup for inner state +func (p *baseTxnContextProvider) AdviseWarmup() error { + if p.isTxnPrepared || p.isBeginStmtWithStaleRead() { + // When executing `START TRANSACTION READ ONLY AS OF ...` no need to warmUp + return nil + } + return p.prepareTxn() +} + +// AdviseOptimizeWithPlan providers optimization according to the plan +func (p *baseTxnContextProvider) AdviseOptimizeWithPlan(_ interface{}) error { + return nil +} + +// GetSnapshotWithStmtReadTS gets snapshot with read ts +func (p *baseTxnContextProvider) GetSnapshotWithStmtReadTS() (kv.Snapshot, error) { + ts, err := p.GetStmtReadTS() + if err != nil { + return nil, err + } + + return p.getSnapshotByTS(ts) +} + +// GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts +func (p *baseTxnContextProvider) GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) { + ts, err := p.GetStmtForUpdateTS() + if err != nil { + return nil, err + } + + return p.getSnapshotByTS(ts) +} + +// getSnapshotByTS get snapshot from store according to the snapshotTS and set the transaction related +// options before return +func (p *baseTxnContextProvider) getSnapshotByTS(snapshotTS uint64) (kv.Snapshot, error) { + txn, err := p.sctx.Txn(false) + if err != nil { + return nil, err + } + + txnCtx := p.sctx.GetSessionVars().TxnCtx + if txn.Valid() && txnCtx.StartTS == txnCtx.GetForUpdateTS() && txnCtx.StartTS == snapshotTS { + return txn.GetSnapshot(), nil + } + + sessVars := p.sctx.GetSessionVars() + snapshot := internal.GetSnapshotWithTS( + p.sctx, + snapshotTS, + temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema), + ) + + replicaReadType := sessVars.GetReplicaRead() + if replicaReadType.IsFollowerRead() && + !sessVars.StmtCtx.RCCheckTS && + !sessVars.RcWriteCheckTS { + snapshot.SetOption(kv.ReplicaRead, replicaReadType) + } + + return snapshot, nil +} + +// canReuseTxnWhenExplicitBegin returns whether we should reuse the txn when starting a transaction explicitly +func canReuseTxnWhenExplicitBegin(sctx sessionctx.Context) bool { + sessVars := sctx.GetSessionVars() + txnCtx := sessVars.TxnCtx + // If BEGIN is the first statement in TxnCtx, we can reuse the existing transaction, without the + // need to call NewTxn, which commits the existing transaction and begins a new one. + // If the last un-committed/un-rollback transaction is a time-bounded read-only transaction, we should + // always create a new transaction. + // If the variable `tidb_snapshot` is set, we should always create a new transaction because the current txn may be + // initialized with snapshot ts. + return txnCtx.History == nil && !txnCtx.IsStaleness && sessVars.SnapshotTS == 0 +} + +// newOracleFuture creates new future according to the scope and the session context +func newOracleFuture(ctx context.Context, sctx sessionctx.Context, scope string) oracle.Future { + r, ctx := tracing.StartRegionEx(ctx, "isolation.newOracleFuture") + defer r.End() + + failpoint.Inject("requestTsoFromPD", func() { + sessiontxn.TsoRequestCountInc(sctx) + }) + + oracleStore := sctx.GetStore().GetOracle() + option := &oracle.Option{TxnScope: scope} + + if sctx.GetSessionVars().LowResolutionTSO { + return oracleStore.GetLowResolutionTimestampAsync(ctx, option) + } + return oracleStore.GetTimestampAsync(ctx, option) +} + +// funcFuture implements oracle.Future +type funcFuture func() (uint64, error) + +// Wait returns a ts got from the func +func (f funcFuture) Wait() (uint64, error) { + return f() +} + +// basePessimisticTxnContextProvider extends baseTxnContextProvider with some functionalities that are commonly used in +// pessimistic transactions. +type basePessimisticTxnContextProvider struct { + baseTxnContextProvider +} + +// OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or +// a pessimistic select-for-update statements. +func (p *basePessimisticTxnContextProvider) OnPessimisticStmtStart(ctx context.Context) error { + if err := p.baseTxnContextProvider.OnPessimisticStmtStart(ctx); err != nil { + return err + } + if p.sctx.GetSessionVars().PessimisticTransactionFairLocking && + p.txn != nil && + p.sctx.GetSessionVars().ConnectionID != 0 && + !p.sctx.GetSessionVars().InRestrictedSQL { + if err := p.txn.StartFairLocking(); err != nil { + return err + } + } + return nil +} + +// OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or +// select-for-update statement. +func (p *basePessimisticTxnContextProvider) OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error { + if err := p.baseTxnContextProvider.OnPessimisticStmtEnd(ctx, isSuccessful); err != nil { + return err + } + if p.txn != nil && p.txn.IsInFairLockingMode() { + if isSuccessful { + if err := p.txn.DoneFairLocking(ctx); err != nil { + return err + } + } else { + if err := p.txn.CancelFairLocking(ctx); err != nil { + return err + } + } + } + + if isSuccessful { + p.sctx.GetSessionVars().TxnCtx.FlushStmtPessimisticLockCache() + } else { + p.sctx.GetSessionVars().TxnCtx.CurrentStmtPessimisticLockCache = nil + } + return nil +} + +func (p *basePessimisticTxnContextProvider) retryFairLockingIfNeeded(ctx context.Context) error { + if p.txn != nil && p.txn.IsInFairLockingMode() { + if err := p.txn.RetryFairLocking(ctx); err != nil { + return err + } + } + return nil +} + +func (p *basePessimisticTxnContextProvider) cancelFairLockingIfNeeded(ctx context.Context) error { + if p.txn != nil && p.txn.IsInFairLockingMode() { + if err := p.txn.CancelFairLocking(ctx); err != nil { + return err + } + } + return nil +} diff --git a/pkg/sessiontxn/isolation/main_test.go b/pkg/sessiontxn/isolation/main_test.go new file mode 100644 index 0000000000000..d6814c0d71f78 --- /dev/null +++ b/pkg/sessiontxn/isolation/main_test.go @@ -0,0 +1,176 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package isolation_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/client.(*mockClient).WatchCommand.func1"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*JobManager).jobLoop"), + } + goleak.VerifyTestMain(m, opts...) +} + +func getOracleTS(t testing.TB, sctx sessionctx.Context) uint64 { + ts, err := sctx.GetStore().GetOracle().GetTimestamp(context.TODO(), &oracle.Option{TxnScope: oracle.GlobalTxnScope}) + require.NoError(t, err) + return ts +} + +type txnAssert[T sessiontxn.TxnContextProvider] struct { + sctx sessionctx.Context + isolation string + minStartTime time.Time + active bool + inTxn bool + minStartTS uint64 + startTS uint64 + causalConsistencyOnly bool + couldRetry bool +} + +func (a *txnAssert[T]) Check(t testing.TB) { + provider := sessiontxn.GetTxnManager(a.sctx).GetContextProvider() + sessVars := a.sctx.GetSessionVars() + txnCtx := sessVars.TxnCtx + + if sessVars.SnapshotInfoschema == nil { + require.Same(t, provider.GetTxnInfoSchema(), txnCtx.InfoSchema) + } else { + require.Equal(t, sessVars.SnapshotInfoschema.(infoschema.InfoSchema).SchemaMetaVersion(), provider.GetTxnInfoSchema().SchemaMetaVersion()) + } + require.Equal(t, a.isolation, txnCtx.Isolation) + require.Equal(t, a.isolation != "", txnCtx.IsPessimistic) + require.Equal(t, sessVars.CheckAndGetTxnScope(), txnCtx.TxnScope) + require.Equal(t, sessVars.ShardAllocateStep, int64(txnCtx.ShardStep)) + require.False(t, txnCtx.IsStaleness) + require.GreaterOrEqual(t, txnCtx.CreateTime.UnixNano(), a.minStartTime.UnixNano()) + require.Equal(t, a.inTxn, sessVars.InTxn()) + require.Equal(t, a.inTxn, txnCtx.IsExplicit) + require.Equal(t, a.couldRetry, txnCtx.CouldRetry) + require.Equal(t, assertTxnScope, txnCtx.TxnScope) + require.Equal(t, assertTxnScope, provider.GetTxnScope()) + require.Equal(t, assertReplicaReadScope, provider.GetReadReplicaScope()) + + txn, err := a.sctx.Txn(false) + require.NoError(t, err) + require.Equal(t, a.active, txn.Valid()) + if !a.active { + require.False(t, a.inTxn) + require.Zero(t, a.startTS) + require.Zero(t, txnCtx.StartTS) + } else { + require.True(t, a.minStartTS != 0 || a.startTS != 0) + require.Greater(t, txnCtx.StartTS, a.minStartTS) + if a.startTS != 0 { + require.Equal(t, a.startTS, txnCtx.StartTS) + } + require.Equal(t, txnCtx.StartTS, txn.StartTS()) + require.Same(t, sessVars.KVVars, txn.GetVars()) + require.Equal(t, txnCtx.TxnScope, txn.GetOption(kv.TxnScope)) + require.Equal(t, a.causalConsistencyOnly, !txn.GetOption(kv.GuaranteeLinearizability).(bool)) + require.Equal(t, txnCtx.IsPessimistic, txn.IsPessimistic()) + } + // The next line is testing the provider has the type T, if not, the cast will panic + _ = provider.(T) +} + +func activeSnapshotTxnAssert(sctx sessionctx.Context, ts uint64, isolation string) *txnAssert[sessiontxn.TxnContextProvider] { + return &txnAssert[sessiontxn.TxnContextProvider]{ + sctx: sctx, + minStartTime: time.Now(), + startTS: ts, + active: true, + inTxn: false, + isolation: isolation, + } +} + +func (a *txnAssert[T]) CheckAndGetProvider(t testing.TB) T { + a.Check(t) + return sessiontxn.GetTxnManager(a.sctx).GetContextProvider().(T) +} + +var assertTxnScope = kv.GlobalTxnScope +var assertReplicaReadScope = kv.GlobalReplicaScope + +func forkScopeSettings(t *testfork.T, store kv.Storage) func() { + tk := testkit.NewTestKit(t, store) + failPointEnabled := false + clearFunc := func() { + assertTxnScope = kv.GlobalTxnScope + assertReplicaReadScope = kv.GlobalReplicaScope + tk.MustExec("set @@global.tidb_replica_read='leader'") + tk.MustExec("set @@global.tidb_enable_local_txn=0") + if failPointEnabled { + require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) + } + } + + clearFunc() + success := false + defer func() { + if !success { + clearFunc() + } + }() + + zone := testfork.PickEnum(t, "", "bj") + if zone != "" { + require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", fmt.Sprintf(`return("%v")`, zone))) + failPointEnabled = true + if testfork.PickEnum(t, "", "enableLocalTxn") != "" { + tk.MustExec("set @@global.tidb_enable_local_txn=1") + assertTxnScope = zone + assertReplicaReadScope = zone + } + } + + if testfork.PickEnum(t, "", "closetRead") != "" { + tk.MustExec("set @@global.tidb_replica_read='closest-replicas'") + if zone != "" { + assertReplicaReadScope = zone + } + } + + success = true + return clearFunc +} diff --git a/pkg/sessiontxn/isolation/metrics/BUILD.bazel b/pkg/sessiontxn/isolation/metrics/BUILD.bazel new file mode 100644 index 0000000000000..779c9efc6bcd9 --- /dev/null +++ b/pkg/sessiontxn/isolation/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/sessiontxn/isolation/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/sessiontxn/isolation/metrics/metrics.go b/pkg/sessiontxn/isolation/metrics/metrics.go new file mode 100644 index 0000000000000..1b1c2904ba2a3 --- /dev/null +++ b/pkg/sessiontxn/isolation/metrics/metrics.go @@ -0,0 +1,36 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package isolation + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// isolation metrics vars +var ( + RcReadCheckTSWriteConfilictCounter prometheus.Counter + RcWriteCheckTSWriteConfilictCounter prometheus.Counter +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init isolation metrics vars. +func InitMetricsVars() { + RcReadCheckTSWriteConfilictCounter = metrics.RCCheckTSWriteConfilictCounter.WithLabelValues(metrics.LblRCReadCheckTS) + RcWriteCheckTSWriteConfilictCounter = metrics.RCCheckTSWriteConfilictCounter.WithLabelValues(metrics.LblRCWriteCheckTS) +} diff --git a/sessiontxn/isolation/optimistic.go b/pkg/sessiontxn/isolation/optimistic.go similarity index 94% rename from sessiontxn/isolation/optimistic.go rename to pkg/sessiontxn/isolation/optimistic.go index 9a1f8d58aabbd..c31c7ffe6886d 100644 --- a/sessiontxn/isolation/optimistic.go +++ b/pkg/sessiontxn/isolation/optimistic.go @@ -17,13 +17,13 @@ package isolation import ( "math" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/sessiontxn/isolation/optimistic_test.go b/pkg/sessiontxn/isolation/optimistic_test.go similarity index 96% rename from sessiontxn/isolation/optimistic_test.go rename to pkg/sessiontxn/isolation/optimistic_test.go index 2ef474ccbaa76..2b1108b230ad0 100644 --- a/sessiontxn/isolation/optimistic_test.go +++ b/pkg/sessiontxn/isolation/optimistic_test.go @@ -23,17 +23,17 @@ import ( "time" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" "github.com/stretchr/testify/require" tikverr "github.com/tikv/client-go/v2/error" ) diff --git a/sessiontxn/isolation/readcommitted.go b/pkg/sessiontxn/isolation/readcommitted.go similarity index 96% rename from sessiontxn/isolation/readcommitted.go rename to pkg/sessiontxn/isolation/readcommitted.go index 510f6b407c9a7..a8d773b112a8e 100644 --- a/sessiontxn/isolation/readcommitted.go +++ b/pkg/sessiontxn/isolation/readcommitted.go @@ -20,15 +20,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - isolation_metrics "github.com/pingcap/tidb/sessiontxn/isolation/metrics" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + isolation_metrics "github.com/pingcap/tidb/pkg/sessiontxn/isolation/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" diff --git a/sessiontxn/isolation/readcommitted_test.go b/pkg/sessiontxn/isolation/readcommitted_test.go similarity index 96% rename from sessiontxn/isolation/readcommitted_test.go rename to pkg/sessiontxn/isolation/readcommitted_test.go index eb70c94a58a95..22ef2fd776a20 100644 --- a/sessiontxn/isolation/readcommitted_test.go +++ b/pkg/sessiontxn/isolation/readcommitted_test.go @@ -24,19 +24,19 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" "github.com/stretchr/testify/require" tikverr "github.com/tikv/client-go/v2/error" ) @@ -453,7 +453,7 @@ func TestTidbSnapshotVarInRC(t *testing.T) { } func TestConflictErrorsInRC(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -534,7 +534,7 @@ func TestConflictErrorsInRC(t *testing.T) { tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func activeRCTxnAssert(t testing.TB, sctx sessionctx.Context, inTxn bool) *txnAssert[*isolation.PessimisticRCTxnContextProvider] { diff --git a/sessiontxn/isolation/repeatable_read.go b/pkg/sessiontxn/isolation/repeatable_read.go similarity index 96% rename from sessiontxn/isolation/repeatable_read.go rename to pkg/sessiontxn/isolation/repeatable_read.go index e64a066d47d89..a40155850bd82 100644 --- a/sessiontxn/isolation/repeatable_read.go +++ b/pkg/sessiontxn/isolation/repeatable_read.go @@ -20,14 +20,14 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/util/logutil" tikverr "github.com/tikv/client-go/v2/error" "go.uber.org/zap" ) diff --git a/sessiontxn/isolation/repeatable_read_test.go b/pkg/sessiontxn/isolation/repeatable_read_test.go similarity index 94% rename from sessiontxn/isolation/repeatable_read_test.go rename to pkg/sessiontxn/isolation/repeatable_read_test.go index 5177a2d7c9472..2e91a17f673fa 100644 --- a/sessiontxn/isolation/repeatable_read_test.go +++ b/pkg/sessiontxn/isolation/repeatable_read_test.go @@ -23,17 +23,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" "github.com/stretchr/testify/require" tikverr "github.com/tikv/client-go/v2/error" ) @@ -471,7 +471,7 @@ var errorsInInsert = []string{ } func TestConflictErrorInInsertInRR(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -496,11 +496,11 @@ func TestConflictErrorInInsertInRR(t *testing.T) { se.SetValue(sessiontxn.AssertLockErr, nil) tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func TestConflictErrorInPointGetForUpdateInRR(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -534,12 +534,12 @@ func TestConflictErrorInPointGetForUpdateInRR(t *testing.T) { tk.MustExec("commit") tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } // Delete should get the latest ts and thus does not incur write conflict func TestConflictErrorInDeleteInRR(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -574,11 +574,11 @@ func TestConflictErrorInDeleteInRR(t *testing.T) { tk.MustQuery("select * from t for update").Check(testkit.Rows()) tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func TestConflictErrorInUpdateInRR(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -611,11 +611,11 @@ func TestConflictErrorInUpdateInRR(t *testing.T) { tk.MustQuery("select * from t for update").Check(testkit.Rows("1 41", "2 22")) tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func TestConflictErrorInOtherQueryContainingPointGet(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -638,7 +638,7 @@ func TestConflictErrorInOtherQueryContainingPointGet(t *testing.T) { require.Equal(t, records["errWriteConflict"], 1) tk.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func activePessimisticRRAssert(t testing.TB, sctx sessionctx.Context, diff --git a/sessiontxn/isolation/serializable.go b/pkg/sessiontxn/isolation/serializable.go similarity index 91% rename from sessiontxn/isolation/serializable.go rename to pkg/sessiontxn/isolation/serializable.go index 77bda169836fa..a3f27464e525c 100644 --- a/sessiontxn/isolation/serializable.go +++ b/pkg/sessiontxn/isolation/serializable.go @@ -17,11 +17,11 @@ package isolation import ( "context" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" ) // PessimisticSerializableTxnContextProvider provides txn context for isolation level oracle-like serializable diff --git a/sessiontxn/isolation/serializable_test.go b/pkg/sessiontxn/isolation/serializable_test.go similarity index 95% rename from sessiontxn/isolation/serializable_test.go rename to pkg/sessiontxn/isolation/serializable_test.go index 85390d447286d..8cc23636b79eb 100644 --- a/sessiontxn/isolation/serializable_test.go +++ b/pkg/sessiontxn/isolation/serializable_test.go @@ -22,17 +22,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" "github.com/stretchr/testify/require" tikverr "github.com/tikv/client-go/v2/error" ) diff --git a/pkg/sessiontxn/staleread/BUILD.bazel b/pkg/sessiontxn/staleread/BUILD.bazel new file mode 100644 index 0000000000000..409cbd1ef7280 --- /dev/null +++ b/pkg/sessiontxn/staleread/BUILD.bazel @@ -0,0 +1,68 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "staleread", + srcs = [ + "errors.go", + "failpoint.go", + "processor.go", + "provider.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/sessiontxn/staleread", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/sessiontxn/internal", + "//pkg/table/temptable", + "//pkg/types", + "//pkg/util/dbterror", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//oracle", + ], +) + +go_test( + name = "staleread_test", + timeout = "short", + srcs = [ + "externalts_test.go", + "main_test.go", + "processor_test.go", + "provider_test.go", + ], + flaky = True, + shard_count = 6, + deps = [ + ":staleread", + "//pkg/domain", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/sessionctx", + "//pkg/sessiontxn", + "//pkg/table/temptable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/sessiontxn/staleread/errors.go b/pkg/sessiontxn/staleread/errors.go new file mode 100644 index 0000000000000..9d600b173f04e --- /dev/null +++ b/pkg/sessiontxn/staleread/errors.go @@ -0,0 +1,24 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package staleread + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +var ( + errAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) +) diff --git a/sessiontxn/staleread/externalts_test.go b/pkg/sessiontxn/staleread/externalts_test.go similarity index 94% rename from sessiontxn/staleread/externalts_test.go rename to pkg/sessiontxn/staleread/externalts_test.go index 683ec1f363045..8b12f3cf101ec 100644 --- a/sessiontxn/staleread/externalts_test.go +++ b/pkg/sessiontxn/staleread/externalts_test.go @@ -18,9 +18,9 @@ import ( "context" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/sessiontxn/staleread/failpoint.go b/pkg/sessiontxn/staleread/failpoint.go new file mode 100644 index 0000000000000..e7740d746378d --- /dev/null +++ b/pkg/sessiontxn/staleread/failpoint.go @@ -0,0 +1,37 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package staleread + +import ( + "fmt" + + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" +) + +// AssertStmtStaleness is used only for test +func AssertStmtStaleness(sctx sessionctx.Context, expected bool) { + actual := IsStmtStaleness(sctx) + if actual != expected { + panic(fmt.Sprintf("stmtctx isStaleness wrong, expected:%v, got:%v", expected, actual)) + } + + if expected { + provider := sessiontxn.GetTxnManager(sctx).GetContextProvider() + if _, ok := provider.(*StalenessTxnContextProvider); !ok { + panic(fmt.Sprintf("stale read should be StalenessTxnContextProvider but current provider is: %T", provider)) + } + } +} diff --git a/pkg/sessiontxn/staleread/main_test.go b/pkg/sessiontxn/staleread/main_test.go new file mode 100644 index 0000000000000..afdeee22b656a --- /dev/null +++ b/pkg/sessiontxn/staleread/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. +package staleread_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/sessiontxn/staleread/processor.go b/pkg/sessiontxn/staleread/processor.go similarity index 97% rename from sessiontxn/staleread/processor.go rename to pkg/sessiontxn/staleread/processor.go index af91ffd1b175e..969d2269d8c59 100644 --- a/sessiontxn/staleread/processor.go +++ b/pkg/sessiontxn/staleread/processor.go @@ -18,12 +18,12 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table/temptable" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table/temptable" ) // enforce implement Processor interface diff --git a/sessiontxn/staleread/processor_test.go b/pkg/sessiontxn/staleread/processor_test.go similarity index 97% rename from sessiontxn/staleread/processor_test.go rename to pkg/sessiontxn/staleread/processor_test.go index e0f9d5895e49f..8a7e9813301ba 100644 --- a/sessiontxn/staleread/processor_test.go +++ b/pkg/sessiontxn/staleread/processor_test.go @@ -22,14 +22,14 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) diff --git a/sessiontxn/staleread/provider.go b/pkg/sessiontxn/staleread/provider.go similarity index 95% rename from sessiontxn/staleread/provider.go rename to pkg/sessiontxn/staleread/provider.go index 308ed2b33cbf3..fbd90680eff76 100644 --- a/sessiontxn/staleread/provider.go +++ b/pkg/sessiontxn/staleread/provider.go @@ -19,15 +19,15 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/internal" - "github.com/pingcap/tidb/table/temptable" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/internal" + "github.com/pingcap/tidb/pkg/table/temptable" ) // StalenessTxnContextProvider implements sessiontxn.TxnContextProvider diff --git a/sessiontxn/staleread/provider_test.go b/pkg/sessiontxn/staleread/provider_test.go similarity index 93% rename from sessiontxn/staleread/provider_test.go rename to pkg/sessiontxn/staleread/provider_test.go index 6e8b6d8ec445e..65503b23b3249 100644 --- a/sessiontxn/staleread/provider_test.go +++ b/pkg/sessiontxn/staleread/provider_test.go @@ -19,12 +19,12 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) diff --git a/pkg/sessiontxn/staleread/util.go b/pkg/sessiontxn/staleread/util.go new file mode 100644 index 0000000000000..fde24b7b7c8ed --- /dev/null +++ b/pkg/sessiontxn/staleread/util.go @@ -0,0 +1,94 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package staleread + +import ( + "context" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/types" + "github.com/tikv/client-go/v2/oracle" +) + +// CalculateAsOfTsExpr calculates the TsExpr of AsOfClause to get a StartTS. +func CalculateAsOfTsExpr(ctx context.Context, sctx sessionctx.Context, tsExpr ast.ExprNode) (uint64, error) { + sctx.GetSessionVars().StmtCtx.SetStaleTSOProvider(func() (uint64, error) { + failpoint.Inject("mockStaleReadTSO", func(val failpoint.Value) (uint64, error) { + return uint64(val.(int)), nil + }) + // this function accepts a context, but we don't need it when there is a valid cached ts. + // in most cases, the stale read ts can be calculated from `cached ts + time since cache - staleness`, + // this can be more accurate than `time.Now() - staleness`, because TiDB's local time can drift. + return sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) + }) + tsVal, err := expression.EvalAstExpr(sctx, tsExpr) + if err != nil { + return 0, err + } + + if tsVal.IsNull() { + return 0, errAsOf.FastGenWithCause("as of timestamp cannot be NULL") + } + + toTypeTimestamp := types.NewFieldType(mysql.TypeTimestamp) + // We need at least the millionsecond here, so set fsp to 3. + toTypeTimestamp.SetDecimal(3) + tsTimestamp, err := tsVal.ConvertTo(sctx.GetSessionVars().StmtCtx, toTypeTimestamp) + if err != nil { + return 0, err + } + tsTime, err := tsTimestamp.GetMysqlTime().GoTime(sctx.GetSessionVars().Location()) + if err != nil { + return 0, err + } + return oracle.GoTimeToTS(tsTime), nil +} + +// CalculateTsWithReadStaleness calculates the TsExpr for readStaleness duration +func CalculateTsWithReadStaleness(sctx sessionctx.Context, readStaleness time.Duration) (uint64, error) { + nowVal, err := expression.GetStmtTimestamp(sctx) + if err != nil { + return 0, err + } + tsVal := nowVal.Add(readStaleness) + minTsVal := expression.GetMinSafeTime(sctx) + return oracle.GoTimeToTS(expression.CalAppropriateTime(tsVal, nowVal, minTsVal)), nil +} + +// IsStmtStaleness indicates whether the current statement is staleness or not +func IsStmtStaleness(sctx sessionctx.Context) bool { + return sctx.GetSessionVars().StmtCtx.IsStaleness +} + +// GetExternalTimestamp returns the external timestamp in cache, or get and store it in cache +func GetExternalTimestamp(ctx context.Context, sctx sessionctx.Context) (uint64, error) { + // Try to get from the stmt cache to make sure this function is deterministic. + stmtCtx := sctx.GetSessionVars().StmtCtx + externalTimestamp, err := stmtCtx.GetOrEvaluateStmtCache(stmtctx.StmtExternalTSCacheKey, func() (interface{}, error) { + return variable.GetExternalTimestamp(ctx) + }) + + if err != nil { + return 0, errAsOf.FastGenWithCause(err.Error()) + } + return externalTimestamp.(uint64), nil +} diff --git a/sessiontxn/txn_context_test.go b/pkg/sessiontxn/txn_context_test.go similarity index 94% rename from sessiontxn/txn_context_test.go rename to pkg/sessiontxn/txn_context_test.go index d2e5a483c3297..4a2e58027cf30 100644 --- a/sessiontxn/txn_context_test.go +++ b/pkg/sessiontxn/txn_context_test.go @@ -22,16 +22,16 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testfork" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) @@ -49,14 +49,14 @@ func TestMain(m *testing.M) { } func setupTxnContextTest(t *testing.T) (kv.Storage, *domain.Domain) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertTxnManagerInCompile", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertTxnManagerInRebuildPlan", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertTxnManagerAfterBuildExecutor", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertTxnManagerAfterPessimisticLockErrorRetry", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertTxnManagerInShortPointGetPlan", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTxnManagerInRunStmt", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTxnManagerInCachedPlanExec", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/assertTxnManagerForUpdateTSEqual", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInCompile", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInRebuildPlan", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerAfterBuildExecutor", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerAfterPessimisticLockErrorRetry", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInShortPointGetPlan", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTxnManagerInRunStmt", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTxnManagerInCachedPlanExec", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/assertTxnManagerForUpdateTSEqual", "return")) store, do := testkit.CreateMockStoreAndDomain(t) @@ -76,14 +76,14 @@ func setupTxnContextTest(t *testing.T) (kv.Storage, *domain.Domain) { tk.MustExec("insert into tmp values(10)") t.Cleanup(func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertTxnManagerInCompile")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertTxnManagerInRebuildPlan")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertTxnManagerAfterBuildExecutor")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertTxnManagerAfterPessimisticLockErrorRetry")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertTxnManagerInShortPointGetPlan")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTxnManagerInRunStmt")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTxnManagerInCachedPlanExec")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/assertTxnManagerForUpdateTSEqual")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInCompile")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInRebuildPlan")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerAfterBuildExecutor")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerAfterPessimisticLockErrorRetry")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertTxnManagerInShortPointGetPlan")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTxnManagerInRunStmt")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTxnManagerInCachedPlanExec")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/assertTxnManagerForUpdateTSEqual")) tk.Session().SetValue(sessiontxn.AssertRecordsKey, nil) tk.Session().SetValue(sessiontxn.AssertTxnInfoSchemaKey, nil) @@ -883,9 +883,9 @@ func TestTSOCmdCountForPrepareExecute(t *testing.T) { // After the bug fix, the tso request count recovers, so we use this workload to record the current tso request count // to reject future works that accidentally causes tso request increasing. // Note, we do not record all tso requests but some typical requests. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) }() store := testkit.CreateMockStore(t) @@ -937,9 +937,9 @@ func TestTSOCmdCountForTextSql(t *testing.T) { // After the bug fix, the tso request count recovers, so we use this workload to record the current tso request count // to reject future works that accidentally causes tso request increasing. // Note, we do not record all tso requests but some typical requests. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) }() store := testkit.CreateMockStore(t) diff --git a/sessiontxn/txn_manager_test.go b/pkg/sessiontxn/txn_manager_test.go similarity index 97% rename from sessiontxn/txn_manager_test.go rename to pkg/sessiontxn/txn_manager_test.go index 2b33a4be2bdb5..1f09f8d661968 100644 --- a/sessiontxn/txn_manager_test.go +++ b/pkg/sessiontxn/txn_manager_test.go @@ -19,18 +19,18 @@ import ( "context" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/internal" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/internal" + "github.com/pingcap/tidb/pkg/sessiontxn/staleread" + "github.com/pingcap/tidb/pkg/table/temptable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" diff --git a/sessiontxn/txn_rc_tso_optimize_test.go b/pkg/sessiontxn/txn_rc_tso_optimize_test.go similarity index 94% rename from sessiontxn/txn_rc_tso_optimize_test.go rename to pkg/sessiontxn/txn_rc_tso_optimize_test.go index f321d40340496..f8bedac912af0 100644 --- a/sessiontxn/txn_rc_tso_optimize_test.go +++ b/pkg/sessiontxn/txn_rc_tso_optimize_test.go @@ -20,23 +20,23 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/isolation" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/sessiontxn/isolation" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) func TestRcTSOCmdCountForPrepareExecuteNormal(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture")) }() store := testkit.CreateMockStore(t) @@ -141,13 +141,13 @@ func TestRcTSOCmdCountForPrepareExecuteNormal(t *testing.T) { } func TestRcTSOCmdCountForPrepareExecuteExtra(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture")) }() store := testkit.CreateMockStore(t) @@ -420,9 +420,9 @@ func TestRcTSOCmdCountForPrepareExecuteExtra(t *testing.T) { } func TestRcTSOCmdCountForTextSQLExecuteNormal(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) }() store := testkit.CreateMockStore(t) @@ -486,13 +486,13 @@ func assertAllTsoCounter(t *testing.T, } func TestRcTSOCmdCountForTextSQLExecuteExtra(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture", "return")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/requestTsoFromPD")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/tsoUseConstantFuture")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/waitTsoOfOracleFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/requestTsoFromPD")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/tsoUseConstantFuture")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/sessiontxn/isolation/waitTsoOfOracleFuture")) }() store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -665,7 +665,7 @@ func TestRcTSOCmdCountForTextSQLExecuteExtra(t *testing.T) { } func TestConflictErrorsUseRcWriteCheckTs(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -788,7 +788,7 @@ func TestConflictErrorsUseRcWriteCheckTs(t *testing.T) { _, ok = se.Value(sessiontxn.AssertLockErr).(map[string]int) require.Equal(t, false, ok) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func TestRcWaitTSInSlowLog(t *testing.T) { diff --git a/pkg/statistics/BUILD.bazel b/pkg/statistics/BUILD.bazel new file mode 100644 index 0000000000000..d9c949de9369d --- /dev/null +++ b/pkg/statistics/BUILD.bazel @@ -0,0 +1,102 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "statistics", + srcs = [ + "analyze.go", + "analyze_jobs.go", + "builder.go", + "cmsketch.go", + "cmsketch_util.go", + "column.go", + "debugtrace.go", + "estimate.go", + "fmsketch.go", + "histogram.go", + "index.go", + "row_sampler.go", + "sample.go", + "scalar.go", + "table.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/util/debugtrace", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/fastrand", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/ranger", + "//pkg/util/sqlexec", + "@com_github_dolthub_swiss//:swiss", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_twmb_murmur3//:murmur3", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "statistics_test", + timeout = "short", + srcs = [ + "cmsketch_test.go", + "fmsketch_test.go", + "histogram_bench_test.go", + "histogram_test.go", + "integration_test.go", + "main_test.go", + "sample_test.go", + "scalar_test.go", + "statistics_test.go", + ], + data = glob(["testdata/**"]), + embed = [":statistics"], + flaky = True, + shard_count = 34, + deps = [ + "//pkg/config", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics/handle/autoanalyze", + "//pkg/testkit", + "//pkg/testkit/testdata", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/ranger", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/statistics/analyze.go b/pkg/statistics/analyze.go similarity index 100% rename from statistics/analyze.go rename to pkg/statistics/analyze.go diff --git a/statistics/analyze_jobs.go b/pkg/statistics/analyze_jobs.go similarity index 100% rename from statistics/analyze_jobs.go rename to pkg/statistics/analyze_jobs.go diff --git a/pkg/statistics/builder.go b/pkg/statistics/builder.go new file mode 100644 index 0000000000000..6812015018ef7 --- /dev/null +++ b/pkg/statistics/builder.go @@ -0,0 +1,461 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "bytes" + "math" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" +) + +// SortedBuilder is used to build histograms for PK and index. +type SortedBuilder struct { + sc *stmtctx.StatementContext + hist *Histogram + numBuckets int64 + valuesPerBucket int64 + lastNumber int64 + bucketIdx int64 + Count int64 + needBucketNDV bool +} + +// NewSortedBuilder creates a new SortedBuilder. +func NewSortedBuilder(sc *stmtctx.StatementContext, numBuckets, id int64, tp *types.FieldType, statsVer int) *SortedBuilder { + return &SortedBuilder{ + sc: sc, + numBuckets: numBuckets, + valuesPerBucket: 1, + hist: NewHistogram(id, 0, 0, 0, tp, int(numBuckets), 0), + needBucketNDV: statsVer >= Version2, + } +} + +// Hist returns the histogram built by SortedBuilder. +func (b *SortedBuilder) Hist() *Histogram { + return b.hist +} + +// Iterate updates the histogram incrementally. +func (b *SortedBuilder) Iterate(data types.Datum) error { + b.Count++ + appendBucket := b.hist.AppendBucket + if b.needBucketNDV { + appendBucket = func(lower, upper *types.Datum, count, repeat int64) { + b.hist.AppendBucketWithNDV(lower, upper, count, repeat, 1) + } + } + if b.Count == 1 { + appendBucket(&data, &data, 1, 1) + b.hist.NDV = 1 + return nil + } + cmp, err := b.hist.GetUpper(int(b.bucketIdx)).Compare(b.sc, &data, collate.GetBinaryCollator()) + if err != nil { + return errors.Trace(err) + } + if cmp == 0 { + // The new item has the same value as current bucket value, to ensure that + // a same value only stored in a single bucket, we do not increase bucketIdx even if it exceeds + // valuesPerBucket. + b.hist.Buckets[b.bucketIdx].Count++ + b.hist.Buckets[b.bucketIdx].Repeat++ + } else if b.hist.Buckets[b.bucketIdx].Count+1-b.lastNumber <= b.valuesPerBucket { + // The bucket still have room to store a new item, update the bucket. + b.hist.updateLastBucket(&data, b.hist.Buckets[b.bucketIdx].Count+1, 1, b.needBucketNDV) + b.hist.NDV++ + } else { + // All buckets are full, we should merge buckets. + if b.bucketIdx+1 == b.numBuckets { + b.hist.mergeBuckets(int(b.bucketIdx)) + b.valuesPerBucket *= 2 + b.bucketIdx = b.bucketIdx / 2 + if b.bucketIdx == 0 { + b.lastNumber = 0 + } else { + b.lastNumber = b.hist.Buckets[b.bucketIdx-1].Count + } + } + // We may merge buckets, so we should check it again. + if b.hist.Buckets[b.bucketIdx].Count+1-b.lastNumber <= b.valuesPerBucket { + b.hist.updateLastBucket(&data, b.hist.Buckets[b.bucketIdx].Count+1, 1, b.needBucketNDV) + } else { + b.lastNumber = b.hist.Buckets[b.bucketIdx].Count + b.bucketIdx++ + appendBucket(&data, &data, b.lastNumber+1, 1) + } + b.hist.NDV++ + } + return nil +} + +// BuildColumnHist build a histogram for a column. +// numBuckets: number of buckets for the histogram. +// id: the id of the table. +// collector: the collector of samples. +// tp: the FieldType for the column. +// count: represents the row count for the column. +// ndv: represents the number of distinct values for the column. +// nullCount: represents the number of null values for the column. +func BuildColumnHist(ctx sessionctx.Context, numBuckets, id int64, collector *SampleCollector, tp *types.FieldType, count int64, ndv int64, nullCount int64) (*Histogram, error) { + if ndv > count { + ndv = count + } + if count == 0 || len(collector.Samples) == 0 { + return NewHistogram(id, ndv, nullCount, 0, tp, 0, collector.TotalSize), nil + } + sc := ctx.GetSessionVars().StmtCtx + samples := collector.Samples + samples, err := SortSampleItems(sc, samples) + if err != nil { + return nil, err + } + hg := NewHistogram(id, ndv, nullCount, 0, tp, int(numBuckets), collector.TotalSize) + + corrXYSum, err := buildHist(sc, hg, samples, count, ndv, numBuckets, nil) + if err != nil { + return nil, err + } + hg.Correlation = calcCorrelation(int64(len(samples)), corrXYSum) + return hg, nil +} + +// buildHist builds histogram from samples and other information. +// It stores the built histogram in hg and return corrXYSum used for calculating the correlation. +func buildHist(sc *stmtctx.StatementContext, hg *Histogram, samples []*SampleItem, count, ndv, numBuckets int64, memTracker *memory.Tracker) (corrXYSum float64, err error) { + sampleNum := int64(len(samples)) + // As we use samples to build the histogram, the bucket number and repeat should multiply a factor. + sampleFactor := float64(count) / float64(sampleNum) + ndvFactor := float64(count) / float64(ndv) + if ndvFactor > sampleFactor { + ndvFactor = sampleFactor + } + // Since bucket count is increased by sampleFactor, so the actual max values per bucket is + // floor(valuesPerBucket/sampleFactor)*sampleFactor, which may less than valuesPerBucket, + // thus we need to add a sampleFactor to avoid building too many buckets. + valuesPerBucket := float64(count)/float64(numBuckets) + sampleFactor + + bucketIdx := 0 + var lastCount int64 + corrXYSum = float64(0) + hg.AppendBucket(&samples[0].Value, &samples[0].Value, int64(sampleFactor), int64(ndvFactor)) + bufferedMemSize := int64(0) + bufferedReleaseSize := int64(0) + defer func() { + if memTracker != nil { + memTracker.Consume(bufferedMemSize) + memTracker.Release(bufferedReleaseSize) + } + }() + var upper = new(types.Datum) + for i := int64(1); i < sampleNum; i++ { + corrXYSum += float64(i) * float64(samples[i].Ordinal) + hg.UpperToDatum(bucketIdx, upper) + if memTracker != nil { + // tmp memory usage + deltaSize := upper.MemUsage() + memTracker.BufferedConsume(&bufferedMemSize, deltaSize) + memTracker.BufferedRelease(&bufferedReleaseSize, deltaSize) + } + cmp, err := upper.Compare(sc, &samples[i].Value, collate.GetBinaryCollator()) + if err != nil { + return 0, errors.Trace(err) + } + totalCount := float64(i+1) * sampleFactor + if cmp == 0 { + // The new item has the same value as current bucket value, to ensure that + // a same value only stored in a single bucket, we do not increase bucketIdx even if it exceeds + // valuesPerBucket. + hg.Buckets[bucketIdx].Count = int64(totalCount) + if hg.Buckets[bucketIdx].Repeat == int64(ndvFactor) { + hg.Buckets[bucketIdx].Repeat = int64(2 * sampleFactor) + } else { + hg.Buckets[bucketIdx].Repeat += int64(sampleFactor) + } + } else if totalCount-float64(lastCount) <= valuesPerBucket { + // The bucket still have room to store a new item, update the bucket. + hg.updateLastBucket(&samples[i].Value, int64(totalCount), int64(ndvFactor), false) + } else { + lastCount = hg.Buckets[bucketIdx].Count + // The bucket is full, store the item in the next bucket. + bucketIdx++ + hg.AppendBucket(&samples[i].Value, &samples[i].Value, int64(totalCount), int64(ndvFactor)) + } + } + return corrXYSum, nil +} + +// calcCorrelation computes column order correlation with the handle. +func calcCorrelation(sampleNum int64, corrXYSum float64) float64 { + if sampleNum == 1 { + return 1 + } + // X means the ordinal of the item in original sequence, Y means the ordinal of the item in the + // sorted sequence, we know that X and Y value sets are both: + // 0, 1, ..., sampleNum-1 + // we can simply compute sum(X) = sum(Y) = + // (sampleNum-1)*sampleNum / 2 + // and sum(X^2) = sum(Y^2) = + // (sampleNum-1)*sampleNum*(2*sampleNum-1) / 6 + // We use "Pearson correlation coefficient" to compute the order correlation of columns, + // the formula is based on https://en.wikipedia.org/wiki/Pearson_correlation_coefficient. + // Note that (itemsCount*corrX2Sum - corrXSum*corrXSum) would never be zero when sampleNum is larger than 1. + itemsCount := float64(sampleNum) + corrXSum := (itemsCount - 1) * itemsCount / 2.0 + corrX2Sum := (itemsCount - 1) * itemsCount * (2*itemsCount - 1) / 6.0 + return (itemsCount*corrXYSum - corrXSum*corrXSum) / (itemsCount*corrX2Sum - corrXSum*corrXSum) +} + +// BuildColumn builds histogram from samples for column. +func BuildColumn(ctx sessionctx.Context, numBuckets, id int64, collector *SampleCollector, tp *types.FieldType) (*Histogram, error) { + return BuildColumnHist(ctx, numBuckets, id, collector, tp, collector.Count, collector.FMSketch.NDV(), collector.NullCount) +} + +// BuildHistAndTopN build a histogram and TopN for a column or an index from samples. +func BuildHistAndTopN( + ctx sessionctx.Context, + numBuckets, numTopN int, + id int64, + collector *SampleCollector, + tp *types.FieldType, + isColumn bool, + memTracker *memory.Tracker, +) (*Histogram, *TopN, error) { + bufferedMemSize := int64(0) + bufferedReleaseSize := int64(0) + defer func() { + if memTracker != nil { + memTracker.Consume(bufferedMemSize) + memTracker.Release(bufferedReleaseSize) + } + }() + var getComparedBytes func(datum types.Datum) ([]byte, error) + if isColumn { + getComparedBytes = func(datum types.Datum) ([]byte, error) { + encoded, err := codec.EncodeKey(ctx.GetSessionVars().StmtCtx, nil, datum) + if memTracker != nil { + // tmp memory usage + deltaSize := int64(cap(encoded)) + memTracker.BufferedConsume(&bufferedMemSize, deltaSize) + memTracker.BufferedRelease(&bufferedReleaseSize, deltaSize) + } + return encoded, err + } + } else { + getComparedBytes = func(datum types.Datum) ([]byte, error) { + return datum.GetBytes(), nil + } + } + count := collector.Count + ndv := collector.FMSketch.NDV() + nullCount := collector.NullCount + if ndv > count { + ndv = count + } + if count == 0 || len(collector.Samples) == 0 || ndv == 0 { + return NewHistogram(id, ndv, nullCount, 0, tp, 0, collector.TotalSize), nil, nil + } + sc := ctx.GetSessionVars().StmtCtx + samples := collector.Samples + samples, err := SortSampleItems(sc, samples) + if err != nil { + return nil, nil, err + } + hg := NewHistogram(id, ndv, nullCount, 0, tp, numBuckets, collector.TotalSize) + + sampleNum := int64(len(samples)) + // As we use samples to build the histogram, the bucket number and repeat should multiply a factor. + sampleFactor := float64(count) / float64(len(samples)) + + // Step1: collect topn from samples + + // the topNList is always sorted by count from more to less + topNList := make([]TopNMeta, 0, numTopN) + cur, err := getComparedBytes(samples[0].Value) + if err != nil { + return nil, nil, errors.Trace(err) + } + curCnt := float64(0) + var corrXYSum float64 + + // Iterate through the samples + for i := int64(0); i < sampleNum; i++ { + if isColumn { + corrXYSum += float64(i) * float64(samples[i].Ordinal) + } + + sampleBytes, err := getComparedBytes(samples[i].Value) + if err != nil { + return nil, nil, errors.Trace(err) + } + // case 1, this value is equal to the last one: current count++ + if bytes.Equal(cur, sampleBytes) { + curCnt++ + continue + } + // case 2, meet a different value: counting for the "current" is complete + // case 2-1, now topn is empty: append the "current" count directly + if len(topNList) == 0 { + topNList = append(topNList, TopNMeta{Encoded: cur, Count: uint64(curCnt)}) + cur, curCnt = sampleBytes, 1 + continue + } + // case 2-2, now topn is full, and the "current" count is less than the least count in the topn: no need to insert the "current" + if len(topNList) >= numTopN && uint64(curCnt) <= topNList[len(topNList)-1].Count { + cur, curCnt = sampleBytes, 1 + continue + } + // case 2-3, now topn is not full, or the "current" count is larger than the least count in the topn: need to find a slot to insert the "current" + j := len(topNList) + for ; j > 0; j-- { + if uint64(curCnt) < topNList[j-1].Count { + break + } + } + topNList = append(topNList, TopNMeta{}) + copy(topNList[j+1:], topNList[j:]) + topNList[j] = TopNMeta{Encoded: cur, Count: uint64(curCnt)} + if len(topNList) > numTopN { + topNList = topNList[:numTopN] + } + cur, curCnt = sampleBytes, 1 + } + + // Calc the correlation of the column between the handle column. + if isColumn { + hg.Correlation = calcCorrelation(sampleNum, corrXYSum) + } + + // Handle the counting for the last value. Basically equal to the case 2 above. + // now topn is empty: append the "current" count directly + if len(topNList) == 0 { + topNList = append(topNList, TopNMeta{Encoded: cur, Count: uint64(curCnt)}) + } else if len(topNList) < numTopN || uint64(curCnt) > topNList[len(topNList)-1].Count { + // now topn is not full, or the "current" count is larger than the least count in the topn: need to find a slot to insert the "current" + j := len(topNList) + for ; j > 0; j-- { + if uint64(curCnt) < topNList[j-1].Count { + break + } + } + topNList = append(topNList, TopNMeta{}) + copy(topNList[j+1:], topNList[j:]) + topNList[j] = TopNMeta{Encoded: cur, Count: uint64(curCnt)} + if len(topNList) > numTopN { + topNList = topNList[:numTopN] + } + } + + topNList = pruneTopNItem(topNList, ndv, nullCount, sampleNum, count) + + // Step2: exclude topn from samples + for i := int64(0); i < int64(len(samples)); i++ { + sampleBytes, err := getComparedBytes(samples[i].Value) + if err != nil { + return nil, nil, errors.Trace(err) + } + for j := 0; j < len(topNList); j++ { + if bytes.Equal(sampleBytes, topNList[j].Encoded) { + // find the same value in topn: need to skip over this value in samples + copy(samples[i:], samples[uint64(i)+topNList[j].Count:]) + samples = samples[:uint64(len(samples))-topNList[j].Count] + i-- + continue + } + } + } + + for i := 0; i < len(topNList); i++ { + topNList[i].Count *= uint64(sampleFactor) + } + topn := &TopN{TopN: topNList} + + if uint64(count) <= topn.TotalCount() || int(hg.NDV) <= len(topn.TopN) { + // TopN includes all sample data + return hg, topn, nil + } + + // Step3: build histogram with the rest samples + if len(samples) > 0 { + _, err = buildHist(sc, hg, samples, count-int64(topn.TotalCount()), ndv-int64(len(topn.TopN)), int64(numBuckets), memTracker) + if err != nil { + return nil, nil, err + } + } + + return hg, topn, nil +} + +// pruneTopNItem tries to prune the least common values in the top-n list if it is not significantly more common than the values not in the list. +// +// We assume that the ones not in the top-n list's selectivity is 1/remained_ndv which is the internal implementation of EqualRowCount +func pruneTopNItem(topns []TopNMeta, ndv, nullCount, sampleRows, totalRows int64) []TopNMeta { + // If the sampleRows holds all rows, or NDV of samples equals to actual NDV, we just return the TopN directly. + if sampleRows == totalRows || totalRows <= 1 || int64(len(topns)) >= ndv { + return topns + } + // Sum the occurrence except the least common one from the top-n list. To check whether the lest common one is worth + // storing later. + sumCount := uint64(0) + for i := 0; i < len(topns)-1; i++ { + sumCount += topns[i].Count + } + topNNum := len(topns) + for topNNum > 0 { + // Selectivity for the ones not in the top-n list. + // (1 - things in top-n list - null) / remained ndv. + selectivity := 1.0 - float64(sumCount)/float64(sampleRows) - float64(nullCount)/float64(totalRows) + if selectivity < 0.0 { + selectivity = 0 + } + if selectivity > 1 { + selectivity = 1 + } + otherNDV := float64(ndv) - (float64(topNNum) - 1) + if otherNDV > 1 { + selectivity /= otherNDV + } + totalRowsN := float64(totalRows) + n := float64(sampleRows) + k := totalRowsN * float64(topns[topNNum-1].Count) / n + // Since we are sampling without replacement. The distribution would be a hypergeometric distribution. + // Thus the variance is the following formula. + variance := n * k * (totalRowsN - k) * (totalRowsN - n) / (totalRowsN * totalRowsN * (totalRowsN - 1)) + stddev := math.Sqrt(variance) + // We choose the bound that plus two stddev of the sample frequency, plus an additional 0.5 for the continuity correction. + // Note: + // The mean + 2 * stddev is known as Wald confidence interval, plus 0.5 would be continuity-corrected Wald interval + if float64(topns[topNNum-1].Count) > selectivity*n+2*stddev+0.5 { + // Estimated selectivity of this item in the TopN is significantly higher than values not in TopN. + // So this value, and all other values in the TopN (selectivity of which is higher than this value) are + // worth being remained in the TopN list, and we stop pruning now. + break + } + // Current one is not worth storing, remove it and subtract it from sumCount, go to next one. + topNNum-- + if topNNum == 0 { + break + } + sumCount -= topns[topNNum-1].Count + } + return topns[:topNNum] +} diff --git a/statistics/cmsketch.go b/pkg/statistics/cmsketch.go similarity index 98% rename from statistics/cmsketch.go rename to pkg/statistics/cmsketch.go index 4662618717da7..1476c17ebe86c 100644 --- a/statistics/cmsketch.go +++ b/pkg/statistics/cmsketch.go @@ -28,15 +28,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/pingcap/tipb/go-tipb" "github.com/twmb/murmur3" ) diff --git a/statistics/cmsketch_test.go b/pkg/statistics/cmsketch_test.go similarity index 97% rename from statistics/cmsketch_test.go rename to pkg/statistics/cmsketch_test.go index c010c6de6cb44..9e221645f221e 100644 --- a/statistics/cmsketch_test.go +++ b/pkg/statistics/cmsketch_test.go @@ -22,11 +22,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/statistics/cmsketch_util.go b/pkg/statistics/cmsketch_util.go similarity index 94% rename from statistics/cmsketch_util.go rename to pkg/statistics/cmsketch_util.go index 131141405a66e..0322b1e2cd79c 100644 --- a/statistics/cmsketch_util.go +++ b/pkg/statistics/cmsketch_util.go @@ -17,9 +17,9 @@ package statistics import ( "time" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/hack" ) // DatumMapCache is used to store the mapping from the string type to datum type. diff --git a/pkg/statistics/column.go b/pkg/statistics/column.go new file mode 100644 index 0000000000000..5f2d38efdf011 --- /dev/null +++ b/pkg/statistics/column.go @@ -0,0 +1,268 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "strconv" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// Column represents a column histogram. +type Column struct { + LastAnalyzePos types.Datum + CMSketch *CMSketch + TopN *TopN + FMSketch *FMSketch + Info *model.ColumnInfo + Histogram + + // StatsLoadedStatus indicates the status of column statistics + StatsLoadedStatus + // PhysicalID is the physical table id, + // or it could possibly be -1, which means "stats not available". + // The -1 case could happen in a pseudo stats table, and in this case, this stats should not trigger stats loading. + PhysicalID int64 + Flag int64 + StatsVer int64 // StatsVer is the version of the current stats, used to maintain compatibility + + IsHandle bool +} + +// Copy copies the column. +func (c *Column) Copy() *Column { + if c == nil { + return nil + } + nc := &Column{ + PhysicalID: c.PhysicalID, + Flag: c.Flag, + StatsVer: c.StatsVer, + IsHandle: c.IsHandle, + } + c.LastAnalyzePos.Copy(&nc.LastAnalyzePos) + if c.CMSketch != nil { + nc.CMSketch = c.CMSketch.Copy() + } + if c.TopN != nil { + nc.TopN = c.TopN.Copy() + } + if c.FMSketch != nil { + nc.FMSketch = c.FMSketch.Copy() + } + if c.Info != nil { + nc.Info = c.Info.Clone() + } + nc.Histogram = *c.Histogram.Copy() + nc.StatsLoadedStatus = c.StatsLoadedStatus.Copy() + return nc +} + +func (c *Column) String() string { + return c.Histogram.ToString(0) +} + +// TotalRowCount returns the total count of this column. +func (c *Column) TotalRowCount() float64 { + if c.StatsVer >= Version2 { + return c.Histogram.TotalRowCount() + float64(c.TopN.TotalCount()) + } + return c.Histogram.TotalRowCount() +} + +// NotNullCount returns the count of this column which is not null. +func (c *Column) NotNullCount() float64 { + if c.StatsVer >= Version2 { + return c.Histogram.NotNullCount() + float64(c.TopN.TotalCount()) + } + return c.Histogram.NotNullCount() +} + +// GetIncreaseFactor get the increase factor to adjust the final estimated count when the table is modified. +func (c *Column) GetIncreaseFactor(realtimeRowCount int64) float64 { + columnCount := c.TotalRowCount() + if columnCount == 0 { + // avoid dividing by 0 + return 1.0 + } + return float64(realtimeRowCount) / columnCount +} + +// MemoryUsage returns the total memory usage of Histogram, CMSketch, FMSketch in Column. +// We ignore the size of other metadata in Column +func (c *Column) MemoryUsage() CacheItemMemoryUsage { + var sum int64 + columnMemUsage := &ColumnMemUsage{ + ColumnID: c.Info.ID, + } + histogramMemUsage := c.Histogram.MemoryUsage() + columnMemUsage.HistogramMemUsage = histogramMemUsage + sum = histogramMemUsage + if c.CMSketch != nil { + cmSketchMemUsage := c.CMSketch.MemoryUsage() + columnMemUsage.CMSketchMemUsage = cmSketchMemUsage + sum += cmSketchMemUsage + } + if c.TopN != nil { + topnMemUsage := c.TopN.MemoryUsage() + columnMemUsage.TopNMemUsage = topnMemUsage + sum += topnMemUsage + } + if c.FMSketch != nil { + fmSketchMemUsage := c.FMSketch.MemoryUsage() + columnMemUsage.FMSketchMemUsage = fmSketchMemUsage + sum += fmSketchMemUsage + } + columnMemUsage.TotalMemUsage = sum + return columnMemUsage +} + +// HistogramNeededItems stores the columns/indices whose Histograms need to be loaded from physical kv layer. +// Currently, we only load index/pk's Histogram from kv automatically. Columns' are loaded by needs. +var HistogramNeededItems = neededStatsMap{items: map[model.TableItemID]struct{}{}} + +// IsInvalid checks if this column is invalid. +// If this column has histogram but not loaded yet, +// then we mark it as need histogram. +func (c *Column) IsInvalid( + sctx sessionctx.Context, + collPseudo bool, +) (res bool) { + var totalCount float64 + var ndv int64 + var inValidForCollPseudo, essentialLoaded bool + if sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(sctx) + defer func() { + debugtrace.RecordAnyValuesWithNames(sctx, + "IsInvalid", res, + "InValidForCollPseudo", inValidForCollPseudo, + "TotalCount", totalCount, + "NDV", ndv, + "EssentialLoaded", essentialLoaded, + ) + debugtrace.LeaveContextCommon(sctx) + }() + } + if sctx != nil { + stmtctx := sctx.GetSessionVars().StmtCtx + if (!c.IsStatsInitialized() || c.IsLoadNeeded()) && stmtctx != nil { + if stmtctx.StatsLoad.Timeout > 0 { + logutil.BgLogger().Warn("Hist for column should already be loaded as sync but not found.", + zap.String(strconv.FormatInt(c.Info.ID, 10), c.Info.Name.O)) + } + // In some tests, the c.Info is not set, so we add this check here. + // When we are using stats from PseudoTable(), the table ID will possibly be -1. + // In this case, we don't trigger stats loading. + if c.Info != nil && c.PhysicalID > 0 { + HistogramNeededItems.insert(model.TableItemID{TableID: c.PhysicalID, ID: c.Info.ID, IsIndex: false}) + } + } + } + if collPseudo { + inValidForCollPseudo = true + return true + } + // In some cases, some statistics in column would be evicted + // For example: the cmsketch of the column might be evicted while the histogram and the topn are still exists + // In this case, we will think this column as valid due to we can still use the rest of the statistics to do optimize. + totalCount = c.TotalRowCount() + essentialLoaded = c.IsEssentialStatsLoaded() + ndv = c.Histogram.NDV + return totalCount == 0 || (!essentialLoaded && ndv > 0) +} + +// ItemID implements TableCacheItem +func (c *Column) ItemID() int64 { + return c.Info.ID +} + +// DropUnnecessaryData drops the unnecessary data for the column. +func (c *Column) DropUnnecessaryData() { + if c.StatsVer < Version2 { + c.CMSketch = nil + } + c.TopN = nil + c.Histogram.Bounds = chunk.NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeBlob)}, 0) + c.Histogram.Buckets = make([]Bucket, 0) + c.Histogram.Scalars = make([]scalar, 0) + c.evictedStatus = AllEvicted +} + +// IsAllEvicted indicates whether all stats evicted +func (c *Column) IsAllEvicted() bool { + return c.statsInitialized && c.evictedStatus >= AllEvicted +} + +// GetEvictedStatus indicates the evicted status +func (c *Column) GetEvictedStatus() int { + return c.evictedStatus +} + +// IsStatsInitialized indicates whether stats is initialized +func (c *Column) IsStatsInitialized() bool { + return c.statsInitialized +} + +// GetStatsVer indicates the stats version +func (c *Column) GetStatsVer() int64 { + return c.StatsVer +} + +// IsCMSExist indicates whether CMSketch exists +func (c *Column) IsCMSExist() bool { + return c.CMSketch != nil +} + +// StatusToString gets the string info of StatsLoadedStatus +func (s StatsLoadedStatus) StatusToString() string { + if !s.statsInitialized { + return "unInitialized" + } + switch s.evictedStatus { + case AllLoaded: + return "allLoaded" + case AllEvicted: + return "allEvicted" + } + return "unknown" +} + +// IsAnalyzed indicates whether the column is analyzed. +// The set of IsAnalyzed columns is a subset of the set of StatsAvailable columns. +func (c *Column) IsAnalyzed() bool { + return c.GetStatsVer() != Version0 +} + +// StatsAvailable indicates whether the column stats are collected. +// Note: +// 1. The function merely talks about whether the stats are collected, regardless of the stats loaded status. +// 2. The function is used to decide StatsLoadedStatus.statsInitialized when reading the column stats from storage. +// 3. There are two cases that StatsAvailable is true: +// a. IsAnalyzed is true. +// b. The column is newly-added/modified and its stats are generated according to the default value. +func (c *Column) StatsAvailable() bool { + // Typically, when the column is analyzed, StatsVer is set to Version1/Version2, so we check IsAnalyzed(). + // However, when we add/modify a column, its stats are generated according to the default value without setting + // StatsVer, so we check NDV > 0 || NullCount > 0 for the case. + return c.IsAnalyzed() || c.NDV > 0 || c.NullCount > 0 +} diff --git a/pkg/statistics/debugtrace.go b/pkg/statistics/debugtrace.go new file mode 100644 index 0000000000000..2b6503a6437f8 --- /dev/null +++ b/pkg/statistics/debugtrace.go @@ -0,0 +1,267 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "slices" + + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "golang.org/x/exp/maps" +) + +/* + Below is debug trace for statistics.Table and types related to fields in statistics.Table. +*/ + +// StatsTblTraceInfo is simplified from Table and used for debug trace. +type StatsTblTraceInfo struct { + Columns []*statsTblColOrIdxInfo + Indexes []*statsTblColOrIdxInfo + PhysicalID int64 + Version uint64 + Count int64 + ModifyCount int64 +} + +type statsTblColOrIdxInfo struct { + CMSketchInfo *cmSketchInfo + Name string + LoadingStatus string + + ID int64 + NDV int64 + NullCount int64 + LastUpdateVersion uint64 + TotColSize int64 + Correlation float64 + StatsVer int64 + + HistogramSize int + TopNSize int +} + +type cmSketchInfo struct { + Depth int32 + Width int32 + Count uint64 + DefaultValue uint64 +} + +func traceCMSketchInfo(cms *CMSketch) *cmSketchInfo { + if cms == nil { + return nil + } + return &cmSketchInfo{ + Depth: cms.depth, + Width: cms.width, + Count: cms.count, + DefaultValue: cms.defaultValue, + } +} + +func traceColStats(colStats *Column, id int64, out *statsTblColOrIdxInfo) { + if colStats == nil { + return + } + out.ID = id + if colStats.Info != nil { + out.Name = colStats.Info.Name.O + } + out.NDV = colStats.NDV + out.NullCount = colStats.NullCount + out.LastUpdateVersion = colStats.LastUpdateVersion + out.TotColSize = colStats.TotColSize + out.Correlation = colStats.Correlation + out.StatsVer = colStats.StatsVer + out.LoadingStatus = colStats.StatsLoadedStatus.StatusToString() + if colStats.Histogram.Buckets == nil { + out.HistogramSize = -1 + } else { + out.HistogramSize = len(colStats.Histogram.Buckets) + } + if colStats.TopN == nil { + out.TopNSize = -1 + } else { + out.TopNSize = len(colStats.TopN.TopN) + } + out.CMSketchInfo = traceCMSketchInfo(colStats.CMSketch) +} + +func traceIdxStats(idxStats *Index, id int64, out *statsTblColOrIdxInfo) { + if idxStats == nil { + return + } + out.ID = id + if idxStats.Info != nil { + out.Name = idxStats.Info.Name.O + } + out.NDV = idxStats.NDV + out.NullCount = idxStats.NullCount + out.LastUpdateVersion = idxStats.LastUpdateVersion + out.TotColSize = idxStats.TotColSize + out.Correlation = idxStats.Correlation + out.StatsVer = idxStats.StatsVer + out.LoadingStatus = idxStats.StatsLoadedStatus.StatusToString() + if idxStats.Histogram.Buckets == nil { + out.HistogramSize = -1 + } else { + out.HistogramSize = len(idxStats.Histogram.Buckets) + } + if idxStats.TopN == nil { + out.TopNSize = -1 + } else { + out.TopNSize = len(idxStats.TopN.TopN) + } + out.CMSketchInfo = traceCMSketchInfo(idxStats.CMSketch) +} + +// TraceStatsTbl converts a Table to StatsTblTraceInfo, which is suitable for the debug trace. +func TraceStatsTbl(statsTbl *Table) *StatsTblTraceInfo { + if statsTbl == nil { + return nil + } + // Collect table level information + colNum := len(statsTbl.Columns) + idxNum := len(statsTbl.Indices) + traceInfo := &StatsTblTraceInfo{ + PhysicalID: statsTbl.PhysicalID, + Version: statsTbl.Version, + Count: statsTbl.RealtimeCount, + ModifyCount: statsTbl.ModifyCount, + Columns: make([]*statsTblColOrIdxInfo, colNum), + Indexes: make([]*statsTblColOrIdxInfo, idxNum), + } + + // Collect information for each Column + colTraces := make([]statsTblColOrIdxInfo, colNum) + colIDs := maps.Keys(statsTbl.Columns) + slices.Sort(colIDs) + for i, id := range colIDs { + colStatsTrace := &colTraces[i] + traceInfo.Columns[i] = colStatsTrace + colStats := statsTbl.Columns[id] + traceColStats(colStats, id, colStatsTrace) + } + + // Collect information for each Index + idxTraces := make([]statsTblColOrIdxInfo, idxNum) + idxIDs := maps.Keys(statsTbl.Indices) + slices.Sort(idxIDs) + for i, id := range idxIDs { + idxStatsTrace := &idxTraces[i] + traceInfo.Indexes[i] = idxStatsTrace + idxStats := statsTbl.Indices[id] + traceIdxStats(idxStats, id, idxStatsTrace) + } + return traceInfo +} + +/* + Below is debug trace for (*Histogram).locateBucket(). +*/ + +type locateBucketInfo struct { + Value string + BucketIdx int + Exceed bool + InBucket bool + MatchLastValue bool +} + +func debugTraceLocateBucket( + s sessionctx.Context, + value *types.Datum, + exceed bool, + bucketIdx int, + inBucket bool, + matchLastValue bool, +) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := &locateBucketInfo{ + Value: value.String(), + Exceed: exceed, + BucketIdx: bucketIdx, + InBucket: inBucket, + MatchLastValue: matchLastValue, + } + root.AppendStepWithNameToCurrentContext(traceInfo, "Locate value in buckets") +} + +/* + Below is debug trace for used buckets in the histograms during estimation. +*/ + +type bucketInfo struct { + Index int + Count int64 + Repeat int64 +} + +// DebugTraceBuckets is used to trace the buckets used in the histogram. +func DebugTraceBuckets(s sessionctx.Context, hg *Histogram, bucketIdxs []int) { + root := debugtrace.GetOrInitDebugTraceRoot(s) + buckets := make([]bucketInfo, len(bucketIdxs)) + for i := range buckets { + idx := bucketIdxs[i] + buckets[i].Index = idx + if idx >= len(hg.Buckets) || idx < 0 { + buckets[i].Repeat = -1 + buckets[i].Count = -1 + continue + } + bkt := hg.Buckets[idx] + buckets[i].Repeat = bkt.Repeat + buckets[i].Count = bkt.Count + } + root.AppendStepWithNameToCurrentContext(buckets, "Related Buckets in Histogram") +} + +/* + Below is debug trace for used TopN range during estimation. +*/ + +type topNRangeInfo struct { + FirstEncoded []byte + LastEncoded []byte + Count []uint64 + FirstIdx int + LastIdx int +} + +func debugTraceTopNRange(s sessionctx.Context, t *TopN, startIdx, endIdx int) { + if endIdx <= startIdx { + return + } + root := debugtrace.GetOrInitDebugTraceRoot(s) + traceInfo := new(topNRangeInfo) + traceInfo.FirstIdx = startIdx + traceInfo.LastIdx = endIdx - 1 + if startIdx < len(t.TopN) { + traceInfo.FirstEncoded = t.TopN[startIdx].Encoded + } + if endIdx-1 < len(t.TopN) { + traceInfo.LastEncoded = t.TopN[endIdx-1].Encoded + } + cnts := make([]uint64, endIdx-startIdx) + for topNIdx, i := startIdx, 0; topNIdx < endIdx; { + cnts[i] = t.TopN[topNIdx].Count + topNIdx++ + i++ + } + traceInfo.Count = cnts + root.AppendStepWithNameToCurrentContext(traceInfo, "Related TopN Range") +} diff --git a/statistics/estimate.go b/pkg/statistics/estimate.go similarity index 97% rename from statistics/estimate.go rename to pkg/statistics/estimate.go index d92864786922d..bb1bc2fe5542e 100644 --- a/statistics/estimate.go +++ b/pkg/statistics/estimate.go @@ -17,7 +17,7 @@ package statistics import ( "math" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // calculateEstimateNDV calculates the estimate ndv of a sampled data from a multisize with size total. diff --git a/statistics/fmsketch.go b/pkg/statistics/fmsketch.go similarity index 97% rename from statistics/fmsketch.go rename to pkg/statistics/fmsketch.go index 1139c8188fff1..74ee24a922c70 100644 --- a/statistics/fmsketch.go +++ b/pkg/statistics/fmsketch.go @@ -20,9 +20,9 @@ import ( "github.com/dolthub/swiss" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/pingcap/tipb/go-tipb" "github.com/twmb/murmur3" ) diff --git a/statistics/fmsketch_test.go b/pkg/statistics/fmsketch_test.go similarity index 97% rename from statistics/fmsketch_test.go rename to pkg/statistics/fmsketch_test.go index d714c25b6cbda..434d9300710d6 100644 --- a/statistics/fmsketch_test.go +++ b/pkg/statistics/fmsketch_test.go @@ -19,8 +19,8 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/handle/BUILD.bazel b/pkg/statistics/handle/BUILD.bazel new file mode 100644 index 0000000000000..eeb277b1dce1b --- /dev/null +++ b/pkg/statistics/handle/BUILD.bazel @@ -0,0 +1,90 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "handle", + srcs = [ + "bootstrap.go", + "ddl.go", + "dump.go", + "handle.go", + "handle_hist.go", + "update.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl/util", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/statistics", + "//pkg/statistics/handle/autoanalyze", + "//pkg/statistics/handle/cache", + "//pkg/statistics/handle/extstats", + "//pkg/statistics/handle/globalstats", + "//pkg/statistics/handle/history", + "//pkg/statistics/handle/lockstats", + "//pkg/statistics/handle/metrics", + "//pkg/statistics/handle/storage", + "//pkg/statistics/handle/usage", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tiancaiamao_gp//:gp", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "handle_test", + timeout = "short", + srcs = [ + "ddl_test.go", + "dump_test.go", + "gc_test.go", + "handle_hist_test.go", + "main_test.go", + ], + embed = [":handle"], + flaky = True, + race = "on", + shard_count = 27, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/parser/model", + "//pkg/planner/cardinality", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/globalstats", + "//pkg/statistics/handle/internal", + "//pkg/statistics/handle/storage", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/mathutil", + "//pkg/util/mock", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/statistics/handle/autoanalyze/BUILD.bazel b/pkg/statistics/handle/autoanalyze/BUILD.bazel new file mode 100644 index 0000000000000..3952d803d4b5d --- /dev/null +++ b/pkg/statistics/handle/autoanalyze/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "autoanalyze", + srcs = ["autoanalyze.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "//pkg/util/timeutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "autoanalyze_test", + timeout = "short", + srcs = ["autoanalyze_test.go"], + flaky = True, + shard_count = 6, + deps = [ + ":autoanalyze", + "//pkg/parser/model", + "//pkg/statistics", + "//pkg/testkit", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + ], +) diff --git a/statistics/handle/autoanalyze/autoanalyze.go b/pkg/statistics/handle/autoanalyze/autoanalyze.go similarity index 96% rename from statistics/handle/autoanalyze/autoanalyze.go rename to pkg/statistics/handle/autoanalyze/autoanalyze.go index 8cd7b5e808771..de2921eab5a4c 100644 --- a/statistics/handle/autoanalyze/autoanalyze.go +++ b/pkg/statistics/handle/autoanalyze/autoanalyze.go @@ -24,20 +24,20 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - statsutil "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + statsutil "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/timeutil" "go.uber.org/zap" ) diff --git a/statistics/handle/autoanalyze/autoanalyze_test.go b/pkg/statistics/handle/autoanalyze/autoanalyze_test.go similarity index 98% rename from statistics/handle/autoanalyze/autoanalyze_test.go rename to pkg/statistics/handle/autoanalyze/autoanalyze_test.go index 5e6c25d3a03a2..4cada5ca98ed2 100644 --- a/statistics/handle/autoanalyze/autoanalyze_test.go +++ b/pkg/statistics/handle/autoanalyze/autoanalyze_test.go @@ -20,10 +20,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) diff --git a/pkg/statistics/handle/bootstrap.go b/pkg/statistics/handle/bootstrap.go new file mode 100644 index 0000000000000..a7b4bd4baec6b --- /dev/null +++ b/pkg/statistics/handle/bootstrap.go @@ -0,0 +1,557 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "context" + "strconv" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +func (h *Handle) initStatsMeta4Chunk(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + physicalID := row.GetInt64(1) + // The table is read-only. Please do not modify it. + table, ok := h.TableInfoByID(is, physicalID) + if !ok { + logutil.BgLogger().Debug("unknown physical ID in stats meta table, maybe it has been dropped", zap.Int64("ID", physicalID)) + continue + } + tableInfo := table.Meta() + newHistColl := statistics.HistColl{ + PhysicalID: physicalID, + HavePhysicalID: true, + RealtimeCount: row.GetInt64(3), + ModifyCount: row.GetInt64(2), + Columns: make(map[int64]*statistics.Column, len(tableInfo.Columns)), + Indices: make(map[int64]*statistics.Index, len(tableInfo.Indices)), + } + tbl := &statistics.Table{ + HistColl: newHistColl, + Version: row.GetUint64(0), + Name: getFullTableName(is, tableInfo), + } + cache.Put(physicalID, tbl) // put this table again since it is updated + } +} + +func (h *Handle) initStatsMeta(is infoschema.InfoSchema) (util.StatsCache, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY version, table_id, modify_count, count from mysql.stats_meta" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return nil, errors.Trace(err) + } + defer terror.Call(rc.Close) + tables, err := cache.NewStatsCacheImpl() + if err != nil { + return nil, err + } + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return nil, errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsMeta4Chunk(is, tables, iter) + } + return tables, nil +} + +func (h *Handle) initStatsHistograms4ChunkLite(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + tblID := row.GetInt64(0) + table, ok := cache.Get(tblID) + if !ok { + continue + } + isIndex := row.GetInt64(1) + id := row.GetInt64(2) + ndv := row.GetInt64(3) + version := row.GetUint64(4) + nullCount := row.GetInt64(5) + statsVer := row.GetInt64(7) + flag := row.GetInt64(9) + lastAnalyzePos := row.GetDatum(10, types.NewFieldType(mysql.TypeBlob)) + tbl, _ := h.TableInfoByID(is, table.PhysicalID) + if isIndex > 0 { + var idxInfo *model.IndexInfo + for _, idx := range tbl.Meta().Indices { + if idx.ID == id { + idxInfo = idx + break + } + } + if idxInfo == nil { + continue + } + hist := statistics.NewHistogram(id, ndv, nullCount, version, types.NewFieldType(mysql.TypeBlob), 0, 0) + index := &statistics.Index{ + Histogram: *hist, + Info: idxInfo, + StatsVer: statsVer, + Flag: flag, + PhysicalID: tblID, + } + lastAnalyzePos.Copy(&index.LastAnalyzePos) + if index.IsAnalyzed() { + index.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() + } + table.Indices[hist.ID] = index + } else { + var colInfo *model.ColumnInfo + for _, col := range tbl.Meta().Columns { + if col.ID == id { + colInfo = col + break + } + } + if colInfo == nil { + continue + } + hist := statistics.NewHistogram(id, ndv, nullCount, version, &colInfo.FieldType, 0, row.GetInt64(6)) + hist.Correlation = row.GetFloat64(8) + col := &statistics.Column{ + Histogram: *hist, + PhysicalID: tblID, + Info: colInfo, + IsHandle: tbl.Meta().PKIsHandle && mysql.HasPriKeyFlag(colInfo.GetFlag()), + Flag: flag, + StatsVer: statsVer, + } + lastAnalyzePos.Copy(&col.LastAnalyzePos) + if col.StatsAvailable() { + col.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() + } + table.Columns[hist.ID] = col + } + cache.Put(tblID, table) // put this table again since it is updated + } +} + +func (h *Handle) initStatsHistograms4Chunk(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + tblID, statsVer := row.GetInt64(0), row.GetInt64(8) + table, ok := cache.Get(tblID) + table = table.Copy() + if !ok { + continue + } + id, ndv, nullCount, version, totColSize := row.GetInt64(2), row.GetInt64(3), row.GetInt64(5), row.GetUint64(4), row.GetInt64(7) + lastAnalyzePos := row.GetDatum(11, types.NewFieldType(mysql.TypeBlob)) + tbl, _ := h.TableInfoByID(is, table.PhysicalID) + if row.GetInt64(1) > 0 { + var idxInfo *model.IndexInfo + for _, idx := range tbl.Meta().Indices { + if idx.ID == id { + idxInfo = idx + break + } + } + if idxInfo == nil { + continue + } + cms, topN, err := statistics.DecodeCMSketchAndTopN(row.GetBytes(6), nil) + if err != nil { + cms = nil + terror.Log(errors.Trace(err)) + } + hist := statistics.NewHistogram(id, ndv, nullCount, version, types.NewFieldType(mysql.TypeBlob), chunk.InitialCapacity, 0) + index := &statistics.Index{ + Histogram: *hist, + CMSketch: cms, + TopN: topN, + Info: idxInfo, + StatsVer: statsVer, + Flag: row.GetInt64(10), + PhysicalID: tblID, + } + if statsVer != statistics.Version0 { + index.StatsLoadedStatus = statistics.NewStatsFullLoadStatus() + } + lastAnalyzePos.Copy(&index.LastAnalyzePos) + table.Indices[hist.ID] = index + } else { + var colInfo *model.ColumnInfo + for _, col := range tbl.Meta().Columns { + if col.ID == id { + colInfo = col + break + } + } + if colInfo == nil { + continue + } + hist := statistics.NewHistogram(id, ndv, nullCount, version, &colInfo.FieldType, 0, totColSize) + hist.Correlation = row.GetFloat64(9) + col := &statistics.Column{ + Histogram: *hist, + PhysicalID: table.PhysicalID, + Info: colInfo, + IsHandle: tbl.Meta().PKIsHandle && mysql.HasPriKeyFlag(colInfo.GetFlag()), + Flag: row.GetInt64(10), + StatsVer: statsVer, + } + lastAnalyzePos.Copy(&col.LastAnalyzePos) + table.Columns[hist.ID] = col + } + cache.Put(tblID, table) // put this table again since it is updated + } +} + +func (h *Handle) initStatsHistogramsLite(is infoschema.InfoSchema, cache util.StatsCache) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY table_id, is_index, hist_id, distinct_count, version, null_count, tot_col_size, stats_ver, correlation, flag, last_analyze_pos from mysql.stats_histograms" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rc.Close) + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsHistograms4ChunkLite(is, cache, iter) + } + return nil +} + +func (h *Handle) initStatsHistograms(is infoschema.InfoSchema, cache util.StatsCache) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY table_id, is_index, hist_id, distinct_count, version, null_count, cm_sketch, tot_col_size, stats_ver, correlation, flag, last_analyze_pos from mysql.stats_histograms" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rc.Close) + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsHistograms4Chunk(is, cache, iter) + } + return nil +} + +func (*Handle) initStatsTopN4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { + affectedIndexes := make(map[*statistics.Index]struct{}) + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + table, ok := cache.Get(row.GetInt64(0)) + if !ok { + continue + } + table = table.Copy() + idx, ok := table.Indices[row.GetInt64(1)] + if !ok || (idx.CMSketch == nil && idx.StatsVer <= statistics.Version1) { + continue + } + if idx.TopN == nil { + idx.TopN = statistics.NewTopN(32) + } + affectedIndexes[idx] = struct{}{} + data := make([]byte, len(row.GetBytes(2))) + copy(data, row.GetBytes(2)) + idx.TopN.AppendTopN(data, row.GetUint64(3)) + cache.Put(table.PhysicalID, table) // put this table again since it is updated + } + for idx := range affectedIndexes { + idx.TopN.Sort() + } +} + +func (h *Handle) initStatsTopN(cache util.StatsCache) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY table_id, hist_id, value, count from mysql.stats_top_n where is_index = 1" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rc.Close) + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsTopN4Chunk(cache, iter) + } + return nil +} + +func (*Handle) initStatsFMSketch4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + table, ok := cache.Get(row.GetInt64(0)) + if !ok { + continue + } + fms, err := statistics.DecodeFMSketch(row.GetBytes(3)) + if err != nil { + fms = nil + terror.Log(errors.Trace(err)) + } + + isIndex := row.GetInt64(1) + id := row.GetInt64(2) + if isIndex == 1 { + if idxStats, ok := table.Indices[id]; ok { + idxStats.FMSketch = fms + } + } else { + if colStats, ok := table.Columns[id]; ok { + colStats.FMSketch = fms + } + } + cache.Put(table.PhysicalID, table) // put this table again since it is updated + } +} + +func (h *Handle) initStatsFMSketch(cache util.StatsCache) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY table_id, is_index, hist_id, value from mysql.stats_fm_sketch" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rc.Close) + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsFMSketch4Chunk(cache, iter) + } + return nil +} + +func (*Handle) initStatsBuckets4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + tableID, isIndex, histID := row.GetInt64(0), row.GetInt64(1), row.GetInt64(2) + table, ok := cache.Get(tableID) + if !ok { + continue + } + table = table.Copy() + var lower, upper types.Datum + var hist *statistics.Histogram + if isIndex > 0 { + index, ok := table.Indices[histID] + if !ok { + continue + } + hist = &index.Histogram + lower, upper = types.NewBytesDatum(row.GetBytes(5)), types.NewBytesDatum(row.GetBytes(6)) + } else { + column, ok := table.Columns[histID] + if !ok { + continue + } + if !mysql.HasPriKeyFlag(column.Info.GetFlag()) { + continue + } + hist = &column.Histogram + d := types.NewBytesDatum(row.GetBytes(5)) + // Setting TimeZone to time.UTC aligns with HistogramFromStorage and can fix #41938. However, #41985 still exist. + // TODO: do the correct time zone conversion for timestamp-type columns' upper/lower bounds. + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + sc.AllowInvalidDate = true + sc.IgnoreZeroInDate = true + var err error + lower, err = d.ConvertTo(sc, &column.Info.FieldType) + if err != nil { + logutil.BgLogger().Debug("decode bucket lower bound failed", zap.Error(err)) + delete(table.Columns, histID) + continue + } + d = types.NewBytesDatum(row.GetBytes(6)) + upper, err = d.ConvertTo(sc, &column.Info.FieldType) + if err != nil { + logutil.BgLogger().Debug("decode bucket upper bound failed", zap.Error(err)) + delete(table.Columns, histID) + continue + } + } + hist.AppendBucketWithNDV(&lower, &upper, row.GetInt64(3), row.GetInt64(4), row.GetInt64(7)) + cache.Put(tableID, table) // put this table again since it is updated + } +} + +func (h *Handle) initStatsBuckets(cache util.StatsCache) error { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) + sql := "select HIGH_PRIORITY table_id, is_index, hist_id, count, repeats, lower_bound, upper_bound, ndv from mysql.stats_buckets order by table_id, is_index, hist_id, bucket_id" + rc, err := util.Exec(h.initStatsCtx, sql) + if err != nil { + return errors.Trace(err) + } + defer terror.Call(rc.Close) + req := rc.NewChunk(nil) + iter := chunk.NewIterator4Chunk(req) + for { + err := rc.Next(ctx, req) + if err != nil { + return errors.Trace(err) + } + if req.NumRows() == 0 { + break + } + h.initStatsBuckets4Chunk(cache, iter) + } + tables := cache.Values() + for _, table := range tables { + for _, idx := range table.Indices { + for i := 1; i < idx.Len(); i++ { + idx.Buckets[i].Count += idx.Buckets[i-1].Count + } + idx.PreCalculateScalar() + } + for _, col := range table.Columns { + for i := 1; i < col.Len(); i++ { + col.Buckets[i].Count += col.Buckets[i-1].Count + } + col.PreCalculateScalar() + } + cache.Put(table.PhysicalID, table) // put this table again since it is updated + } + return nil +} + +// InitStatsLite initiates the stats cache. The function is liter and faster than InitStats. +// Column/index stats are not loaded, i.e., we only load scalars such as NDV, NullCount, Correlation and don't load CMSketch/Histogram/TopN. +func (h *Handle) InitStatsLite(is infoschema.InfoSchema) (err error) { + defer func() { + _, err1 := util.Exec(h.initStatsCtx, "commit") + if err == nil && err1 != nil { + err = err1 + } + }() + _, err = util.Exec(h.initStatsCtx, "begin") + if err != nil { + return err + } + cache, err := h.initStatsMeta(is) + if err != nil { + return errors.Trace(err) + } + err = h.initStatsHistogramsLite(is, cache) + if err != nil { + return errors.Trace(err) + } + h.Replace(cache) + return nil +} + +// InitStats initiates the stats cache. +// Index/PK stats are fully loaded. +// Column stats are not loaded, i.e., we only load scalars such as NDV, NullCount, Correlation and don't load CMSketch/Histogram/TopN. +func (h *Handle) InitStats(is infoschema.InfoSchema) (err error) { + loadFMSketch := config.GetGlobalConfig().Performance.EnableLoadFMSketch + defer func() { + _, err1 := util.Exec(h.initStatsCtx, "commit") + if err == nil && err1 != nil { + err = err1 + } + }() + _, err = util.Exec(h.initStatsCtx, "begin") + if err != nil { + return err + } + cache, err := h.initStatsMeta(is) + if err != nil { + return errors.Trace(err) + } + err = h.initStatsHistograms(is, cache) + if err != nil { + return errors.Trace(err) + } + err = h.initStatsTopN(cache) + if err != nil { + return err + } + if loadFMSketch { + err = h.initStatsFMSketch(cache) + if err != nil { + return err + } + } + err = h.initStatsBuckets(cache) + if err != nil { + return errors.Trace(err) + } + // Set columns' stats status. + for _, table := range cache.Values() { + for _, col := range table.Columns { + if col.StatsAvailable() { + if mysql.HasPriKeyFlag(col.Info.GetFlag()) { + col.StatsLoadedStatus = statistics.NewStatsFullLoadStatus() + } else { + col.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() + } + } + } + } + h.Replace(cache) + return nil +} + +func getFullTableName(is infoschema.InfoSchema, tblInfo *model.TableInfo) string { + for _, schema := range is.AllSchemas() { + if t, err := is.TableByName(schema.Name, tblInfo.Name); err == nil { + if t.Meta().ID == tblInfo.ID { + return schema.Name.O + "." + tblInfo.Name.O + } + } + } + return strconv.FormatInt(tblInfo.ID, 10) +} diff --git a/pkg/statistics/handle/cache/BUILD.bazel b/pkg/statistics/handle/cache/BUILD.bazel new file mode 100644 index 0000000000000..5027658fb5197 --- /dev/null +++ b/pkg/statistics/handle/cache/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cache", + srcs = [ + "statscache.go", + "statscacheinner.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/cache/internal", + "//pkg/statistics/handle/cache/internal/lfu", + "//pkg/statistics/handle/cache/internal/mapcache", + "//pkg/statistics/handle/cache/internal/metrics", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/syncutil", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "cache_test", + timeout = "short", + srcs = ["bench_test.go"], + embed = [":cache"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/statistics", + "//pkg/statistics/handle/cache/internal/testutil", + "//pkg/statistics/handle/util", + "//pkg/util/benchdaily", + ], +) diff --git a/pkg/statistics/handle/cache/bench_test.go b/pkg/statistics/handle/cache/bench_test.go new file mode 100644 index 0000000000000..f781ddb1c0cdf --- /dev/null +++ b/pkg/statistics/handle/cache/bench_test.go @@ -0,0 +1,180 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cache + +import ( + "math/rand" + "sync" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/testutil" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/benchdaily" +) + +func benchCopyAndUpdate(b *testing.B, c util.StatsCache) { + var wg sync.WaitGroup + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) + t1.PhysicalID = rand.Int63() + c.UpdateStatsCache([]*statistics.Table{t1}, nil) + }() + } + wg.Wait() + b.StopTimer() +} + +func benchPutGet(b *testing.B, c util.StatsCache) { + var wg sync.WaitGroup + b.ResetTimer() + for i := 0; i < b.N; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) + t1.PhysicalID = rand.Int63() + c.UpdateStatsCache([]*statistics.Table{t1}, nil) + }(i) + } + for i := 0; i < b.N; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + c.Get(int64(i)) + }(i) + } + wg.Wait() + b.StopTimer() +} + +func benchGet(b *testing.B, c util.StatsCache) { + var w sync.WaitGroup + for i := 0; i < b.N; i++ { + w.Add(1) + go func(i int) { + defer w.Done() + t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) + t1.PhysicalID = rand.Int63() + c.UpdateStatsCache([]*statistics.Table{t1}, nil) + }(i) + } + w.Wait() + b.ResetTimer() + var wg sync.WaitGroup + for i := 0; i < b.N; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + c.Get(int64(i)) + }(i) + } + wg.Wait() + b.StopTimer() +} + +func BenchmarkStatsCacheLFUCopyAndUpdate(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchCopyAndUpdate(b, cache) +} + +func BenchmarkStatsCacheMapCacheCopyAndUpdate(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = false + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchCopyAndUpdate(b, cache) +} + +func BenchmarkLFUCachePutGet(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchPutGet(b, cache) +} + +func BenchmarkMapCachePutGet(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = false + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchPutGet(b, cache) +} + +func BenchmarkLFUCacheGet(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchGet(b, cache) +} + +func BenchmarkMapCacheGet(b *testing.B) { + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = false + }) + cache, err := NewStatsCacheImpl() + if err != nil { + b.Fail() + } + benchGet(b, cache) +} + +func TestBenchDaily(*testing.T) { + benchdaily.Run( + BenchmarkStatsCacheLFUCopyAndUpdate, + BenchmarkStatsCacheMapCacheCopyAndUpdate, + BenchmarkLFUCachePutGet, + BenchmarkMapCachePutGet, + BenchmarkLFUCacheGet, + BenchmarkMapCacheGet, + ) +} diff --git a/pkg/statistics/handle/cache/internal/BUILD.bazel b/pkg/statistics/handle/cache/internal/BUILD.bazel new file mode 100644 index 0000000000000..bfe8b29fcbfab --- /dev/null +++ b/pkg/statistics/handle/cache/internal/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "cache", + srcs = ["inner.go"], + importpath = "github.com/pingcap/tidb/statistics/handle/internal/cache", + visibility = ["//pkg/statistics/handle:__subpackages__"], + deps = ["//pkg/statistics"], +) + +go_library( + name = "internal", + srcs = ["inner.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal", + visibility = ["//pkg/statistics/handle/cache:__subpackages__"], + deps = ["//pkg/statistics"], +) diff --git a/statistics/handle/cache/internal/inner.go b/pkg/statistics/handle/cache/internal/inner.go similarity index 97% rename from statistics/handle/cache/internal/inner.go rename to pkg/statistics/handle/cache/internal/inner.go index c493aed6384c3..607de40de7d34 100644 --- a/statistics/handle/cache/internal/inner.go +++ b/pkg/statistics/handle/cache/internal/inner.go @@ -15,7 +15,7 @@ package internal import ( - "github.com/pingcap/tidb/statistics" + "github.com/pingcap/tidb/pkg/statistics" ) // StatsCacheInner is the interface to manage the statsCache, it can be implemented by map, lru cache or other structures. diff --git a/pkg/statistics/handle/cache/internal/lfu/BUILD.bazel b/pkg/statistics/handle/cache/internal/lfu/BUILD.bazel new file mode 100644 index 0000000000000..ae179f12be3e9 --- /dev/null +++ b/pkg/statistics/handle/cache/internal/lfu/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lfu", + srcs = [ + "key_set.go", + "key_set_shard.go", + "lfu_cache.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/lfu", + visibility = ["//pkg/statistics/handle/cache:__subpackages__"], + deps = [ + "//pkg/statistics", + "//pkg/statistics/handle/cache/internal", + "//pkg/statistics/handle/cache/internal/metrics", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "@com_github_dgraph_io_ristretto//:ristretto", + "@org_golang_x_exp//maps", + "@org_golang_x_exp//rand", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "lfu_test", + timeout = "short", + srcs = ["lfu_cache_test.go"], + embed = [":lfu"], + flaky = True, + race = "on", + shard_count = 9, + deps = [ + "//pkg/statistics", + "//pkg/statistics/handle/cache/internal/testutil", + "@com_github_stretchr_testify//require", + ], +) diff --git a/statistics/handle/cache/internal/lfu/key_set.go b/pkg/statistics/handle/cache/internal/lfu/key_set.go similarity index 97% rename from statistics/handle/cache/internal/lfu/key_set.go rename to pkg/statistics/handle/cache/internal/lfu/key_set.go index d65df658637fb..4467dab30c592 100644 --- a/statistics/handle/cache/internal/lfu/key_set.go +++ b/pkg/statistics/handle/cache/internal/lfu/key_set.go @@ -17,7 +17,7 @@ package lfu import ( "sync" - "github.com/pingcap/tidb/statistics" + "github.com/pingcap/tidb/pkg/statistics" "golang.org/x/exp/maps" ) diff --git a/statistics/handle/cache/internal/lfu/key_set_shard.go b/pkg/statistics/handle/cache/internal/lfu/key_set_shard.go similarity index 97% rename from statistics/handle/cache/internal/lfu/key_set_shard.go rename to pkg/statistics/handle/cache/internal/lfu/key_set_shard.go index e859a4fe4c0bf..f07990400001e 100644 --- a/statistics/handle/cache/internal/lfu/key_set_shard.go +++ b/pkg/statistics/handle/cache/internal/lfu/key_set_shard.go @@ -15,7 +15,7 @@ package lfu import ( - "github.com/pingcap/tidb/statistics" + "github.com/pingcap/tidb/pkg/statistics" ) const keySetCnt = 256 diff --git a/statistics/handle/cache/internal/lfu/lfu_cache.go b/pkg/statistics/handle/cache/internal/lfu/lfu_cache.go similarity index 95% rename from statistics/handle/cache/internal/lfu/lfu_cache.go rename to pkg/statistics/handle/cache/internal/lfu/lfu_cache.go index 804cfb8091715..0382b592b0061 100644 --- a/statistics/handle/cache/internal/lfu/lfu_cache.go +++ b/pkg/statistics/handle/cache/internal/lfu/lfu_cache.go @@ -19,13 +19,13 @@ import ( "sync/atomic" "github.com/dgraph-io/ristretto" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal" - "github.com/pingcap/tidb/statistics/handle/cache/internal/metrics" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/metrics" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" "golang.org/x/exp/rand" ) diff --git a/statistics/handle/cache/internal/lfu/lfu_cache_test.go b/pkg/statistics/handle/cache/internal/lfu/lfu_cache_test.go similarity index 98% rename from statistics/handle/cache/internal/lfu/lfu_cache_test.go rename to pkg/statistics/handle/cache/internal/lfu/lfu_cache_test.go index 9a0a7e46228dc..65c03a553b789 100644 --- a/statistics/handle/cache/internal/lfu/lfu_cache_test.go +++ b/pkg/statistics/handle/cache/internal/lfu/lfu_cache_test.go @@ -20,8 +20,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal/testutil" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/testutil" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/handle/cache/internal/mapcache/BUILD.bazel b/pkg/statistics/handle/cache/internal/mapcache/BUILD.bazel new file mode 100644 index 0000000000000..38c800e866f91 --- /dev/null +++ b/pkg/statistics/handle/cache/internal/mapcache/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mapcache", + srcs = ["map_cache.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/mapcache", + visibility = ["//pkg/statistics/handle:__subpackages__"], + deps = [ + "//pkg/statistics", + "//pkg/statistics/handle/cache/internal", + ], +) diff --git a/statistics/handle/cache/internal/mapcache/map_cache.go b/pkg/statistics/handle/cache/internal/mapcache/map_cache.go similarity index 96% rename from statistics/handle/cache/internal/mapcache/map_cache.go rename to pkg/statistics/handle/cache/internal/mapcache/map_cache.go index 7a7285483610e..37144b1c70128 100644 --- a/statistics/handle/cache/internal/mapcache/map_cache.go +++ b/pkg/statistics/handle/cache/internal/mapcache/map_cache.go @@ -15,8 +15,8 @@ package mapcache import ( - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal" ) type cacheItem struct { diff --git a/pkg/statistics/handle/cache/internal/metrics/BUILD.bazel b/pkg/statistics/handle/cache/internal/metrics/BUILD.bazel new file mode 100644 index 0000000000000..ef6ab59d3873d --- /dev/null +++ b/pkg/statistics/handle/cache/internal/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/metrics", + visibility = ["//pkg/statistics/handle/cache:__subpackages__"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/statistics/handle/cache/internal/metrics/metrics.go b/pkg/statistics/handle/cache/internal/metrics/metrics.go new file mode 100644 index 0000000000000..198265153461c --- /dev/null +++ b/pkg/statistics/handle/cache/internal/metrics/metrics.go @@ -0,0 +1,68 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +var ( + // MissCounter is the counter of missing cache. + MissCounter prometheus.Counter + // HitCounter is the counter of hitting cache. + HitCounter prometheus.Counter + // UpdateCounter is the counter of updating cache. + UpdateCounter prometheus.Counter + // DelCounter is the counter of deleting cache. + DelCounter prometheus.Counter + // EvictCounter is the counter of evicting cache. + EvictCounter prometheus.Counter + // RejectCounter is the counter of reject cache. + RejectCounter prometheus.Counter + // CostGauge is the gauge of cost time. + CostGauge prometheus.Gauge + // CapacityGauge is the gauge of capacity. + CapacityGauge prometheus.Gauge +) + +func init() { + initMetricsVars() +} + +// initMetricsVars init copr metrics vars. +func initMetricsVars() { + metrics.StatsCacheCounter = metrics.NewCounterVec( + prometheus.CounterOpts{ + Namespace: "tidb", + Subsystem: "statistics", + Name: "stats_cache_op", + Help: "Counter for statsCache operation", + }, []string{metrics.LblType}) + metrics.StatsCacheGauge = metrics.NewGaugeVec(prometheus.GaugeOpts{ + Namespace: "tidb", + Subsystem: "statistics", + Name: "stats_cache_val", + Help: "gauge of stats cache value", + }, []string{metrics.LblType}) + MissCounter = metrics.StatsCacheCounter.WithLabelValues("miss") + HitCounter = metrics.StatsCacheCounter.WithLabelValues("hit") + UpdateCounter = metrics.StatsCacheCounter.WithLabelValues("update") + DelCounter = metrics.StatsCacheCounter.WithLabelValues("del") + EvictCounter = metrics.StatsCacheCounter.WithLabelValues("evict") + RejectCounter = metrics.StatsCacheCounter.WithLabelValues("reject") + CostGauge = metrics.StatsCacheGauge.WithLabelValues("track") + CapacityGauge = metrics.StatsCacheGauge.WithLabelValues("capacity") +} diff --git a/pkg/statistics/handle/cache/internal/testutil/BUILD.bazel b/pkg/statistics/handle/cache/internal/testutil/BUILD.bazel new file mode 100644 index 0000000000000..fbe51e74eaa7f --- /dev/null +++ b/pkg/statistics/handle/cache/internal/testutil/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/testutil", + visibility = ["//pkg/statistics/handle/cache:__subpackages__"], + deps = [ + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/statistics", + "//pkg/types", + ], +) diff --git a/pkg/statistics/handle/cache/internal/testutil/testutil.go b/pkg/statistics/handle/cache/internal/testutil/testutil.go new file mode 100644 index 0000000000000..42963be6ed9f3 --- /dev/null +++ b/pkg/statistics/handle/cache/internal/testutil/testutil.go @@ -0,0 +1,91 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package testutil + +import ( + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" +) + +// NewMockStatisticsTable creates a mock statistics table with given columns and indices. +// each column and index consumes 4 bytes memory +func NewMockStatisticsTable(columns int, indices int, withCMS, withTopN, withHist bool) *statistics.Table { + t := &statistics.Table{} + t.Columns = make(map[int64]*statistics.Column) + t.Indices = make(map[int64]*statistics.Index) + for i := 1; i <= columns; i++ { + t.Columns[int64(i)] = &statistics.Column{ + Info: &model.ColumnInfo{ID: int64(i)}, + StatsLoadedStatus: statistics.NewStatsFullLoadStatus(), + } + if withCMS { + t.Columns[int64(i)].CMSketch = statistics.NewCMSketch(1, 1) + } + if withTopN { + t.Columns[int64(i)].TopN = statistics.NewTopN(1) + t.Columns[int64(i)].TopN.AppendTopN([]byte{}, 1) + } + if withHist { + t.Columns[int64(i)].Histogram = *statistics.NewHistogram(0, 10, 0, 0, types.NewFieldType(mysql.TypeBlob), 1, 0) + } + } + for i := 1; i <= indices; i++ { + t.Indices[int64(i)] = &statistics.Index{ + Info: &model.IndexInfo{ID: int64(i)}, + StatsLoadedStatus: statistics.NewStatsFullLoadStatus(), + } + if withCMS { + t.Indices[int64(i)].CMSketch = statistics.NewCMSketch(1, 1) + } + if withTopN { + t.Indices[int64(i)].TopN = statistics.NewTopN(1) + t.Indices[int64(i)].TopN.AppendTopN([]byte{}, 1) + } + if withHist { + t.Indices[int64(i)].Histogram = *statistics.NewHistogram(0, 10, 0, 0, types.NewFieldType(mysql.TypeBlob), 1, 0) + } + } + return t +} + +// MockTableAppendColumn appends a column to the table. +func MockTableAppendColumn(t *statistics.Table) { + index := int64(len(t.Columns) + 1) + t.Columns[index] = &statistics.Column{ + Info: &model.ColumnInfo{ID: index}, + CMSketch: statistics.NewCMSketch(1, 1), + } +} + +// MockTableAppendIndex appends an index to the table. +func MockTableAppendIndex(t *statistics.Table) { + index := int64(len(t.Indices) + 1) + t.Indices[index] = &statistics.Index{ + Info: &model.IndexInfo{ID: index}, + CMSketch: statistics.NewCMSketch(1, 1), + } +} + +// MockTableRemoveColumn removes the last column of the table. +func MockTableRemoveColumn(t *statistics.Table) { + delete(t.Columns, int64(len(t.Columns))) +} + +// MockTableRemoveIndex removes the last index of the table. +func MockTableRemoveIndex(t *statistics.Table) { + delete(t.Indices, int64(len(t.Indices))) +} diff --git a/statistics/handle/cache/statscache.go b/pkg/statistics/handle/cache/statscache.go similarity index 92% rename from statistics/handle/cache/statscache.go rename to pkg/statistics/handle/cache/statscache.go index 51e9887eb8eed..2175f06e4b990 100644 --- a/statistics/handle/cache/statscache.go +++ b/pkg/statistics/handle/cache/statscache.go @@ -17,11 +17,11 @@ package cache import ( "sync/atomic" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal/metrics" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/metrics" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/statistics/handle/cache/statscacheinner.go b/pkg/statistics/handle/cache/statscacheinner.go similarity index 93% rename from statistics/handle/cache/statscacheinner.go rename to pkg/statistics/handle/cache/statscacheinner.go index 156fa709093e3..b7d11255c82b1 100644 --- a/statistics/handle/cache/statscacheinner.go +++ b/pkg/statistics/handle/cache/statscacheinner.go @@ -20,20 +20,20 @@ import ( "sync/atomic" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal" - "github.com/pingcap/tidb/statistics/handle/cache/internal/lfu" - "github.com/pingcap/tidb/statistics/handle/cache/internal/mapcache" - "github.com/pingcap/tidb/statistics/handle/cache/internal/metrics" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/lfu" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/mapcache" + "github.com/pingcap/tidb/pkg/statistics/handle/cache/internal/metrics" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/syncutil" "go.uber.org/zap" ) diff --git a/pkg/statistics/handle/ddl.go b/pkg/statistics/handle/ddl.go new file mode 100644 index 0000000000000..50c01ce37dda8 --- /dev/null +++ b/pkg/statistics/handle/ddl.go @@ -0,0 +1,393 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "context" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + statsutil "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// HandleDDLEvent begins to process a ddl task. +func (h *Handle) HandleDDLEvent(t *util.Event) error { + switch t.Tp { + case model.ActionCreateTable, model.ActionTruncateTable: + ids, err := h.getInitStateTableIDs(t.TableInfo) + if err != nil { + return err + } + for _, id := range ids { + if err := h.insertTableStats2KV(t.TableInfo, id); err != nil { + return err + } + } + case model.ActionDropTable: + ids, err := h.getInitStateTableIDs(t.TableInfo) + if err != nil { + return err + } + for _, id := range ids { + if err := h.resetTableStats2KVForDrop(id); err != nil { + return err + } + } + case model.ActionAddColumn, model.ActionModifyColumn: + ids, err := h.getInitStateTableIDs(t.TableInfo) + if err != nil { + return err + } + for _, id := range ids { + if err := h.insertColStats2KV(id, t.ColumnInfos); err != nil { + return err + } + } + case model.ActionAddTablePartition, model.ActionTruncateTablePartition: + for _, def := range t.PartInfo.Definitions { + if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { + return err + } + } + case model.ActionDropTablePartition: + pruneMode, err := h.GetCurrentPruneMode() + if err != nil { + return err + } + if variable.PartitionPruneMode(pruneMode) == variable.Dynamic && t.PartInfo != nil { + if err := h.updateGlobalStats(t.TableInfo); err != nil { + return err + } + } + for _, def := range t.PartInfo.Definitions { + if err := h.resetTableStats2KVForDrop(def.ID); err != nil { + return err + } + } + case model.ActionReorganizePartition: + for _, def := range t.PartInfo.Definitions { + // TODO: Should we trigger analyze instead of adding 0s? + if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { + return err + } + // Do not update global stats, since the data have not changed! + } + case model.ActionAlterTablePartitioning: + // Add partitioning + for _, def := range t.PartInfo.Definitions { + // TODO: Should we trigger analyze instead of adding 0s? + if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { + return err + } + } + fallthrough + case model.ActionRemovePartitioning: + // Change id for global stats, since the data has not changed! + // Note that t.TableInfo is the current (new) table info + // and t.PartInfo.NewTableID is actually the old table ID! + // (see onReorganizePartition) + return h.changeGlobalStatsID(t.PartInfo.NewTableID, t.TableInfo.ID) + case model.ActionFlashbackCluster: + return h.updateStatsVersion() + } + return nil +} + +// analyzeOptionDefault saves the default values of NumBuckets and NumTopN. +// These values will be used in dynamic mode when we drop table partition and then need to merge global-stats. +// These values originally came from the analyzeOptionDefault structure in the planner/core/planbuilder.go file. +var analyzeOptionDefault = map[ast.AnalyzeOptionType]uint64{ + ast.AnalyzeOptNumBuckets: 256, + ast.AnalyzeOptNumTopN: 20, +} + +// updateStatsVersion will set statistics version to the newest TS, +// then tidb-server will reload automatic. +func (h *Handle) updateStatsVersion() error { + return h.callWithSCtx(func(sctx sessionctx.Context) error { + return storage.UpdateStatsVersion(sctx) + }, statsutil.FlagWrapTxn) +} + +// updateGlobalStats will trigger the merge of global-stats when we drop table partition +func (h *Handle) updateGlobalStats(tblInfo *model.TableInfo) error { + // We need to merge the partition-level stats to global-stats when we drop table partition in dynamic mode. + return h.callWithSCtx(func(sctx sessionctx.Context) error { + tableID := tblInfo.ID + is := sessiontxn.GetTxnManager(sctx).GetTxnInfoSchema() + globalStats, err := h.TableStatsFromStorage(tblInfo, tableID, true, 0) + if err != nil { + return err + } + // If we do not currently have global-stats, no new global-stats will be generated. + if globalStats == nil { + return nil + } + opts := make(map[ast.AnalyzeOptionType]uint64, len(analyzeOptionDefault)) + for key, val := range analyzeOptionDefault { + opts[key] = val + } + // Use current global-stats related information to construct the opts for `MergePartitionStats2GlobalStats` function. + globalColStatsTopNNum, globalColStatsBucketNum := 0, 0 + for colID := range globalStats.Columns { + globalColStatsTopN := globalStats.Columns[colID].TopN + if globalColStatsTopN != nil && len(globalColStatsTopN.TopN) > globalColStatsTopNNum { + globalColStatsTopNNum = len(globalColStatsTopN.TopN) + } + globalColStats := globalStats.Columns[colID] + if globalColStats != nil && len(globalColStats.Buckets) > globalColStatsBucketNum { + globalColStatsBucketNum = len(globalColStats.Buckets) + } + } + if globalColStatsTopNNum != 0 { + opts[ast.AnalyzeOptNumTopN] = uint64(globalColStatsTopNNum) + } + if globalColStatsBucketNum != 0 { + opts[ast.AnalyzeOptNumBuckets] = uint64(globalColStatsBucketNum) + } + // Generate the new column global-stats + newColGlobalStats, err := h.mergePartitionStats2GlobalStats(opts, is, tblInfo, false, nil, nil) + if err != nil { + return err + } + if len(newColGlobalStats.MissingPartitionStats) > 0 { + logutil.BgLogger().Warn("missing partition stats when merging global stats", zap.String("table", tblInfo.Name.L), + zap.String("item", "columns"), zap.Strings("missing", newColGlobalStats.MissingPartitionStats)) + } + for i := 0; i < newColGlobalStats.Num; i++ { + hg, cms, topN := newColGlobalStats.Hg[i], newColGlobalStats.Cms[i], newColGlobalStats.TopN[i] + if hg == nil { + // All partitions have no stats so global stats are not created. + continue + } + // fms for global stats doesn't need to dump to kv. + err = h.SaveStatsToStorage(tableID, newColGlobalStats.Count, newColGlobalStats.ModifyCount, + 0, hg, cms, topN, 2, 1, false, StatsMetaHistorySourceSchemaChange) + if err != nil { + return err + } + } + + // Generate the new index global-stats + globalIdxStatsTopNNum, globalIdxStatsBucketNum := 0, 0 + for _, idx := range tblInfo.Indices { + globalIdxStatsTopN := globalStats.Indices[idx.ID].TopN + if globalIdxStatsTopN != nil && len(globalIdxStatsTopN.TopN) > globalIdxStatsTopNNum { + globalIdxStatsTopNNum = len(globalIdxStatsTopN.TopN) + } + globalIdxStats := globalStats.Indices[idx.ID] + if globalIdxStats != nil && len(globalIdxStats.Buckets) > globalIdxStatsBucketNum { + globalIdxStatsBucketNum = len(globalIdxStats.Buckets) + } + if globalIdxStatsTopNNum != 0 { + opts[ast.AnalyzeOptNumTopN] = uint64(globalIdxStatsTopNNum) + } + if globalIdxStatsBucketNum != 0 { + opts[ast.AnalyzeOptNumBuckets] = uint64(globalIdxStatsBucketNum) + } + newIndexGlobalStats, err := h.mergePartitionStats2GlobalStats(opts, is, tblInfo, true, []int64{idx.ID}, nil) + if err != nil { + return err + } + if len(newIndexGlobalStats.MissingPartitionStats) > 0 { + logutil.BgLogger().Warn("missing partition stats when merging global stats", zap.String("table", tblInfo.Name.L), + zap.String("item", "index "+idx.Name.L), zap.Strings("missing", newIndexGlobalStats.MissingPartitionStats)) + } + for i := 0; i < newIndexGlobalStats.Num; i++ { + hg, cms, topN := newIndexGlobalStats.Hg[i], newIndexGlobalStats.Cms[i], newIndexGlobalStats.TopN[i] + if hg == nil { + // All partitions have no stats so global stats are not created. + continue + } + // fms for global stats doesn't need to dump to kv. + err = h.SaveStatsToStorage(tableID, newIndexGlobalStats.Count, newIndexGlobalStats.ModifyCount, 1, hg, cms, topN, 2, 1, false, StatsMetaHistorySourceSchemaChange) + if err != nil { + return err + } + } + } + return nil + }) +} + +func (h *Handle) changeGlobalStatsID(from, to int64) (err error) { + return h.callWithSCtx(func(sctx sessionctx.Context) error { + for _, table := range []string{"stats_meta", "stats_top_n", "stats_fm_sketch", "stats_buckets", "stats_histograms", "column_stats_usage"} { + _, err = statsutil.Exec(sctx, "update mysql."+table+" set table_id = %? where table_id = %?", to, from) + if err != nil { + return err + } + } + return nil + }, statsutil.FlagWrapTxn) +} + +func (h *Handle) getInitStateTableIDs(tblInfo *model.TableInfo) (ids []int64, err error) { + pi := tblInfo.GetPartitionInfo() + if pi == nil { + return []int64{tblInfo.ID}, nil + } + ids = make([]int64, 0, len(pi.Definitions)+1) + for _, def := range pi.Definitions { + ids = append(ids, def.ID) + } + pruneMode, err := h.GetCurrentPruneMode() + if err != nil { + return nil, err + } + if variable.PartitionPruneMode(pruneMode) == variable.Dynamic { + ids = append(ids, tblInfo.ID) + } + return ids, nil +} + +// DDLEventCh returns ddl events channel in handle. +func (h *Handle) DDLEventCh() chan *util.Event { + return h.ddlEventCh +} + +// insertTableStats2KV inserts a record standing for a new table to stats_meta and inserts some records standing for the +// new columns and indices which belong to this table. +func (h *Handle) insertTableStats2KV(info *model.TableInfo, physicalID int64) (err error) { + statsVer := uint64(0) + defer func() { + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) + } + }() + + return h.callWithSCtx(func(sctx sessionctx.Context) error { + startTS, err := statsutil.GetStartTS(sctx) + if err != nil { + return errors.Trace(err) + } + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id) values(%?, %?)", startTS, physicalID); err != nil { + return err + } + statsVer = startTS + for _, col := range info.Columns { + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, version) values(%?, 0, %?, 0, %?)", physicalID, col.ID, startTS); err != nil { + return err + } + } + for _, idx := range info.Indices { + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, version) values(%?, 1, %?, 0, %?)", physicalID, idx.ID, startTS); err != nil { + return err + } + } + return nil + }, statsutil.FlagWrapTxn) +} + +// resetTableStats2KV resets the count to 0. +func (h *Handle) resetTableStats2KVForDrop(physicalID int64) (err error) { + statsVer := uint64(0) + defer func() { + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) + } + }() + + return h.callWithSCtx(func(sctx sessionctx.Context) error { + startTS, err := statsutil.GetStartTS(sctx) + if err != nil { + return errors.Trace(err) + } + if _, err := statsutil.Exec(sctx, "update mysql.stats_meta set version=%? where table_id =%?", startTS, physicalID); err != nil { + return err + } + return nil + }, statsutil.FlagWrapTxn) +} + +// insertColStats2KV insert a record to stats_histograms with distinct_count 1 and insert a bucket to stats_buckets with default value. +// This operation also updates version. +func (h *Handle) insertColStats2KV(physicalID int64, colInfos []*model.ColumnInfo) (err error) { + statsVer := uint64(0) + defer func() { + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) + } + }() + + return h.callWithSCtx(func(sctx sessionctx.Context) error { + startTS, err := statsutil.GetStartTS(sctx) + if err != nil { + return errors.Trace(err) + } + + // First of all, we update the version. + _, err = statsutil.Exec(sctx, "update mysql.stats_meta set version = %? where table_id = %?", startTS, physicalID) + if err != nil { + return err + } + statsVer = startTS + // If we didn't update anything by last SQL, it means the stats of this table does not exist. + if sctx.GetSessionVars().StmtCtx.AffectedRows() > 0 { + // By this step we can get the count of this table, then we can sure the count and repeats of bucket. + var rs sqlexec.RecordSet + rs, err = statsutil.Exec(sctx, "select count from mysql.stats_meta where table_id = %?", physicalID) + if err != nil { + return err + } + defer terror.Call(rs.Close) + req := rs.NewChunk(nil) + err = rs.Next(context.Background(), req) + if err != nil { + return err + } + count := req.GetRow(0).GetInt64(0) + for _, colInfo := range colInfos { + value := types.NewDatum(colInfo.GetOriginDefaultValue()) + value, err = value.ConvertTo(sctx.GetSessionVars().StmtCtx, &colInfo.FieldType) + if err != nil { + return err + } + if value.IsNull() { + // If the adding column has default value null, all the existing rows have null value on the newly added column. + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (version, table_id, is_index, hist_id, distinct_count, null_count) values (%?, %?, 0, %?, 0, %?)", startTS, physicalID, colInfo.ID, count); err != nil { + return err + } + } else { + // If this stats exists, we insert histogram meta first, the distinct_count will always be one. + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (version, table_id, is_index, hist_id, distinct_count, tot_col_size) values (%?, %?, 0, %?, 1, %?)", startTS, physicalID, colInfo.ID, int64(len(value.GetBytes()))*count); err != nil { + return err + } + value, err = value.ConvertTo(sctx.GetSessionVars().StmtCtx, types.NewFieldType(mysql.TypeBlob)) + if err != nil { + return err + } + // There must be only one bucket for this new column and the value is the default value. + if _, err := statsutil.Exec(sctx, "insert into mysql.stats_buckets (table_id, is_index, hist_id, bucket_id, repeats, count, lower_bound, upper_bound) values (%?, 0, %?, 0, %?, %?, %?, %?)", physicalID, colInfo.ID, count, count, value.GetBytes(), value.GetBytes()); err != nil { + return err + } + } + } + } + return nil + }, statsutil.FlagWrapTxn) +} diff --git a/pkg/statistics/handle/ddl_test.go b/pkg/statistics/handle/ddl_test.go new file mode 100644 index 0000000000000..1eb754d5014eb --- /dev/null +++ b/pkg/statistics/handle/ddl_test.go @@ -0,0 +1,286 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handle_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestDDLAfterLoad(t *testing.T) { + store, do := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + testKit.MustExec("analyze table t") + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + recordCount := 1000 + for i := 0; i < recordCount; i++ { + testKit.MustExec("insert into t values (?, ?)", i, i+1) + } + testKit.MustExec("analyze table t") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + // add column + testKit.MustExec("alter table t add column c10 int") + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + + sctx := mock.NewContext() + count := cardinality.ColumnGreaterRowCount(sctx, statsTbl, types.NewDatum(recordCount+1), tableInfo.Columns[0].ID) + require.Equal(t, 0.0, count) + count = cardinality.ColumnGreaterRowCount(sctx, statsTbl, types.NewDatum(recordCount+1), tableInfo.Columns[2].ID) + require.Equal(t, 333, int(count)) +} + +func TestDDLTable(t *testing.T) { + store, do := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := do.StatsHandle() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + statsTbl := h.GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + testKit.MustExec("create table t1 (c1 int, c2 int, index idx(c1))") + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo = tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + statsTbl = h.GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + testKit.MustExec("truncate table t1") + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo = tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + statsTbl = h.GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) +} + +func TestDDLHistogram(t *testing.T) { + store, do := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + h := do.StatsHandle() + + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + <-h.DDLEventCh() + testKit.MustExec("insert into t values(1,2),(3,4)") + testKit.MustExec("analyze table t") + + testKit.MustExec("alter table t add column c_null int") + err := h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is := do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + require.True(t, statsTbl.Columns[tableInfo.Columns[2].ID].IsStatsInitialized()) + require.Equal(t, int64(2), statsTbl.Columns[tableInfo.Columns[2].ID].NullCount) + require.Equal(t, int64(0), statsTbl.Columns[tableInfo.Columns[2].ID].Histogram.NDV) + + testKit.MustExec("alter table t add column c3 int NOT NULL") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + require.True(t, statsTbl.Columns[tableInfo.Columns[3].ID].IsStatsInitialized()) + sctx := mock.NewContext() + count, err := cardinality.ColumnEqualRowCount(sctx, statsTbl, types.NewIntDatum(0), tableInfo.Columns[3].ID) + require.NoError(t, err) + require.Equal(t, float64(2), count) + count, err = cardinality.ColumnEqualRowCount(sctx, statsTbl, types.NewIntDatum(1), tableInfo.Columns[3].ID) + require.NoError(t, err) + require.Equal(t, float64(0), count) + + testKit.MustExec("alter table t add column c4 datetime NOT NULL default CURRENT_TIMESTAMP") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + // If we don't use original default value, we will get a pseudo table. + require.False(t, statsTbl.Pseudo) + + testKit.MustExec("alter table t add column c5 varchar(15) DEFAULT '123'") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + require.True(t, statsTbl.Columns[tableInfo.Columns[5].ID].IsStatsInitialized()) + require.Equal(t, 3.0, cardinality.AvgColSize(statsTbl.Columns[tableInfo.Columns[5].ID], statsTbl.RealtimeCount, false)) + + testKit.MustExec("alter table t add column c6 varchar(15) DEFAULT '123', add column c7 varchar(15) DEFAULT '123'") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + testKit.MustExec("create index i on t(c2, c1)") + testKit.MustExec("analyze table t") + rs := testKit.MustQuery("select count(*) from mysql.stats_histograms where table_id = ? and hist_id = 1 and is_index =1", tableInfo.ID) + rs.Check(testkit.Rows("1")) + rs = testKit.MustQuery("select count(*) from mysql.stats_buckets where table_id = ? and hist_id = 1 and is_index = 1", tableInfo.ID) + rs.Check(testkit.Rows("0")) + rs = testKit.MustQuery("select count(*) from mysql.stats_top_n where table_id = ? and hist_id = 1 and is_index = 1", tableInfo.ID) + rs.Check(testkit.Rows("2")) +} + +func TestDDLPartition(t *testing.T) { + store, do := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + for i, pruneMode := range []string{"static", "dynamic"} { + testKit.MustExec("set @@tidb_partition_prune_mode=`" + pruneMode + "`") + testKit.MustExec("set global tidb_partition_prune_mode=`" + pruneMode + "`") + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + h := do.StatsHandle() + if i == 1 { + err := h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + } + createTable := `CREATE TABLE t (a int, b int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21) +)` + testKit.MustExec(createTable) + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + pi := tableInfo.GetPartitionInfo() + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.False(t, statsTbl.Pseudo, "for %v", pruneMode) + } + + testKit.MustExec("insert into t values (1,2),(6,2),(11,2),(16,2)") + testKit.MustExec("analyze table t") + testKit.MustExec("alter table t add column c varchar(15) DEFAULT '123'") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + require.Nil(t, h.Update(is)) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + pi = tableInfo.GetPartitionInfo() + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.False(t, statsTbl.Pseudo) + require.Equal(t, 3.0, cardinality.AvgColSize(statsTbl.Columns[tableInfo.Columns[2].ID], statsTbl.RealtimeCount, false)) + } + + addPartition := "alter table t add partition (partition p4 values less than (26))" + testKit.MustExec(addPartition) + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + pi = tableInfo.GetPartitionInfo() + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.False(t, statsTbl.Pseudo) + } + + truncatePartition := "alter table t truncate partition p4" + testKit.MustExec(truncatePartition) + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + pi = tableInfo.GetPartitionInfo() + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.False(t, statsTbl.Pseudo) + } + + reorganizePartition := "alter table t reorganize partition p0,p1 into (partition p0 values less than (11))" + testKit.MustExec(reorganizePartition) + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + pi = tableInfo.GetPartitionInfo() + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.False(t, statsTbl.Pseudo) + } + } +} diff --git a/pkg/statistics/handle/dump.go b/pkg/statistics/handle/dump.go new file mode 100644 index 0000000000000..d2344f1da60e2 --- /dev/null +++ b/pkg/statistics/handle/dump.go @@ -0,0 +1,327 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "context" + "fmt" + "runtime" + "sync" + "sync/atomic" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle/globalstats" + handle_metrics "github.com/pingcap/tidb/pkg/statistics/handle/metrics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + utilstats "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +// TestLoadStatsErr is only for test. +type TestLoadStatsErr struct{} + +// DumpStatsToJSON dumps statistic to json. +func (h *Handle) DumpStatsToJSON(dbName string, tableInfo *model.TableInfo, + historyStatsExec sqlexec.RestrictedSQLExecutor, dumpPartitionStats bool) (*storage.JSONTable, error) { + var snapshot uint64 + if historyStatsExec != nil { + sctx := historyStatsExec.(sessionctx.Context) + snapshot = sctx.GetSessionVars().SnapshotTS + } + return h.DumpStatsToJSONBySnapshot(dbName, tableInfo, snapshot, dumpPartitionStats) +} + +// DumpHistoricalStatsBySnapshot dumped json tables from mysql.stats_meta_history and mysql.stats_history. +// As implemented in getTableHistoricalStatsToJSONWithFallback, if historical stats are nonexistent, it will fall back +// to the latest stats, and these table names (and partition names) will be returned in fallbackTbls. +func (h *Handle) DumpHistoricalStatsBySnapshot( + dbName string, + tableInfo *model.TableInfo, + snapshot uint64, +) ( + jt *storage.JSONTable, + fallbackTbls []string, + err error, +) { + historicalStatsEnabled, err := h.CheckHistoricalStatsEnable() + if err != nil { + return nil, nil, errors.Errorf("check %v failed: %v", variable.TiDBEnableHistoricalStats, err) + } + if !historicalStatsEnabled { + return nil, nil, errors.Errorf("%v should be enabled", variable.TiDBEnableHistoricalStats) + } + + defer func() { + if err == nil { + handle_metrics.DumpHistoricalStatsSuccessCounter.Inc() + } else { + handle_metrics.DumpHistoricalStatsFailedCounter.Inc() + } + }() + pi := tableInfo.GetPartitionInfo() + if pi == nil { + jt, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, tableInfo.ID, snapshot) + if fallback { + fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s", dbName, tableInfo.Name.O)) + } + return jt, fallbackTbls, err + } + jsonTbl := &storage.JSONTable{ + DatabaseName: dbName, + TableName: tableInfo.Name.L, + Partitions: make(map[string]*storage.JSONTable, len(pi.Definitions)), + } + for _, def := range pi.Definitions { + tbl, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, def.ID, snapshot) + if err != nil { + return nil, nil, errors.Trace(err) + } + if fallback { + fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s %s", dbName, tableInfo.Name.O, def.Name.O)) + } + jsonTbl.Partitions[def.Name.L] = tbl + } + tbl, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, tableInfo.ID, snapshot) + if err != nil { + return nil, nil, err + } + if fallback { + fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s global", dbName, tableInfo.Name.O)) + } + // dump its global-stats if existed + if tbl != nil { + jsonTbl.Partitions[globalstats.TiDBGlobalStats] = tbl + } + return jsonTbl, fallbackTbls, nil +} + +// DumpStatsToJSONBySnapshot dumps statistic to json. +func (h *Handle) DumpStatsToJSONBySnapshot(dbName string, tableInfo *model.TableInfo, snapshot uint64, dumpPartitionStats bool) (*storage.JSONTable, error) { + pruneMode, err := h.GetCurrentPruneMode() + if err != nil { + return nil, err + } + isDynamicMode := variable.PartitionPruneMode(pruneMode) == variable.Dynamic + pi := tableInfo.GetPartitionInfo() + if pi == nil { + return h.tableStatsToJSON(dbName, tableInfo, tableInfo.ID, snapshot) + } + jsonTbl := &storage.JSONTable{ + DatabaseName: dbName, + TableName: tableInfo.Name.L, + Partitions: make(map[string]*storage.JSONTable, len(pi.Definitions)), + } + // dump partition stats only if in static mode or enable dumpPartitionStats flag in dynamic mode + if !isDynamicMode || dumpPartitionStats { + for _, def := range pi.Definitions { + tbl, err := h.tableStatsToJSON(dbName, tableInfo, def.ID, snapshot) + if err != nil { + return nil, errors.Trace(err) + } + if tbl == nil { + continue + } + jsonTbl.Partitions[def.Name.L] = tbl + } + } + // dump its global-stats if existed + tbl, err := h.tableStatsToJSON(dbName, tableInfo, tableInfo.ID, snapshot) + if err != nil { + return nil, errors.Trace(err) + } + if tbl != nil { + jsonTbl.Partitions[globalstats.TiDBGlobalStats] = tbl + } + return jsonTbl, nil +} + +// getTableHistoricalStatsToJSONWithFallback try to get table historical stats, if not exist, directly fallback to the +// latest stats, and the second return value would be true. +func (h *Handle) getTableHistoricalStatsToJSONWithFallback( + dbName string, + tableInfo *model.TableInfo, + physicalID int64, + snapshot uint64, +) ( + *storage.JSONTable, + bool, + error, +) { + jt, exist, err := h.tableHistoricalStatsToJSON(physicalID, snapshot) + if err != nil { + return nil, false, err + } + if !exist { + jt, err = h.tableStatsToJSON(dbName, tableInfo, physicalID, 0) + fallback := true + if snapshot == 0 { + fallback = false + } + return jt, fallback, err + } + return jt, false, nil +} + +func (h *Handle) tableHistoricalStatsToJSON(physicalID int64, snapshot uint64) (jt *storage.JSONTable, exist bool, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + jt, exist, err = storage.TableHistoricalStatsToJSON(sctx, physicalID, snapshot) + return err + }, utilstats.FlagWrapTxn) + return +} + +func (h *Handle) tableStatsToJSON(dbName string, tableInfo *model.TableInfo, physicalID int64, snapshot uint64) (*storage.JSONTable, error) { + tbl, err := h.TableStatsFromStorage(tableInfo, physicalID, true, snapshot) + if err != nil || tbl == nil { + return nil, err + } + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + tbl.Version, tbl.ModifyCount, tbl.RealtimeCount, err = storage.StatsMetaByTableIDFromStorage(sctx, physicalID, snapshot) + return err + }) + if err != nil { + return nil, err + } + jsonTbl, err := storage.GenJSONTableFromStats(dbName, tableInfo, tbl) + if err != nil { + return nil, err + } + return jsonTbl, nil +} + +// LoadStatsFromJSON will load statistic from JSONTable, and save it to the storage. +// In final, it will also udpate the stats cache. +func (h *Handle) LoadStatsFromJSON(ctx context.Context, is infoschema.InfoSchema, + jsonTbl *storage.JSONTable, concurrencyForPartition uint8) error { + if err := h.LoadStatsFromJSONNoUpdate(ctx, is, jsonTbl, concurrencyForPartition); err != nil { + return errors.Trace(err) + } + return errors.Trace(h.Update(is)) +} + +// LoadStatsFromJSONNoUpdate will load statistic from JSONTable, and save it to the storage. +func (h *Handle) LoadStatsFromJSONNoUpdate(ctx context.Context, is infoschema.InfoSchema, + jsonTbl *storage.JSONTable, concurrencyForPartition uint8) error { + nCPU := uint8(runtime.GOMAXPROCS(0)) + if concurrencyForPartition == 0 { + concurrencyForPartition = nCPU / 2 // default + } + if concurrencyForPartition > nCPU { + concurrencyForPartition = nCPU // for safety + } + + table, err := is.TableByName(model.NewCIStr(jsonTbl.DatabaseName), model.NewCIStr(jsonTbl.TableName)) + if err != nil { + return errors.Trace(err) + } + tableInfo := table.Meta() + pi := tableInfo.GetPartitionInfo() + if pi == nil || jsonTbl.Partitions == nil { + err := h.loadStatsFromJSON(tableInfo, tableInfo.ID, jsonTbl) + if err != nil { + return errors.Trace(err) + } + } else { + // load partition statistics concurrently + taskCh := make(chan model.PartitionDefinition, len(pi.Definitions)) + for _, def := range pi.Definitions { + taskCh <- def + } + close(taskCh) + var wg sync.WaitGroup + e := new(atomic.Pointer[error]) + for i := 0; i < int(concurrencyForPartition); i++ { + wg.Add(1) + h.gpool.Go(func() { + defer func() { + if r := recover(); r != nil { + err := fmt.Errorf("%v", r) + e.CompareAndSwap(nil, &err) + } + wg.Done() + }() + + for def := range taskCh { + tbl := jsonTbl.Partitions[def.Name.L] + if tbl == nil { + continue + } + + loadFunc := h.loadStatsFromJSON + if intest.InTest && ctx.Value(TestLoadStatsErr{}) != nil { + loadFunc = ctx.Value(TestLoadStatsErr{}).(func(*model.TableInfo, int64, *storage.JSONTable) error) + } + + err := loadFunc(tableInfo, def.ID, tbl) + if err != nil { + e.CompareAndSwap(nil, &err) + return + } + if e.Load() != nil { + return + } + } + }) + } + wg.Wait() + if e.Load() != nil { + return *e.Load() + } + + // load global-stats if existed + if globalStats, ok := jsonTbl.Partitions[globalstats.TiDBGlobalStats]; ok { + if err := h.loadStatsFromJSON(tableInfo, tableInfo.ID, globalStats); err != nil { + return errors.Trace(err) + } + } + } + return nil +} + +func (h *Handle) loadStatsFromJSON(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { + tbl, err := storage.TableStatsFromJSON(tableInfo, physicalID, jsonTbl) + if err != nil { + return errors.Trace(err) + } + + for _, col := range tbl.Columns { + // loadStatsFromJSON doesn't support partition table now. + // The table level count and modify_count would be overridden by the SaveMetaToStorage below, so we don't need + // to care about them here. + err = h.SaveStatsToStorage(tbl.PhysicalID, tbl.RealtimeCount, 0, 0, &col.Histogram, col.CMSketch, col.TopN, int(col.GetStatsVer()), 1, false, StatsMetaHistorySourceLoadStats) + if err != nil { + return errors.Trace(err) + } + } + for _, idx := range tbl.Indices { + // loadStatsFromJSON doesn't support partition table now. + // The table level count and modify_count would be overridden by the SaveMetaToStorage below, so we don't need + // to care about them here. + err = h.SaveStatsToStorage(tbl.PhysicalID, tbl.RealtimeCount, 0, 1, &idx.Histogram, idx.CMSketch, idx.TopN, int(idx.GetStatsVer()), 1, false, StatsMetaHistorySourceLoadStats) + if err != nil { + return errors.Trace(err) + } + } + err = h.SaveExtendedStatsToStorage(tbl.PhysicalID, tbl.ExtendedStats, true) + if err != nil { + return errors.Trace(err) + } + return h.SaveMetaToStorage(tbl.PhysicalID, tbl.RealtimeCount, tbl.ModifyCount, StatsMetaHistorySourceLoadStats) +} diff --git a/pkg/statistics/handle/dump_test.go b/pkg/statistics/handle/dump_test.go new file mode 100644 index 0000000000000..175739e73d109 --- /dev/null +++ b/pkg/statistics/handle/dump_test.go @@ -0,0 +1,619 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package handle_test + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/statistics/handle/globalstats" + "github.com/pingcap/tidb/pkg/statistics/handle/internal" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func requireTableEqual(t *testing.T, a *statistics.Table, b *statistics.Table) { + require.Equal(t, b.RealtimeCount, a.RealtimeCount) + require.Equal(t, b.ModifyCount, a.ModifyCount) + require.Equal(t, len(b.Columns), len(a.Columns)) + for i := range a.Columns { + require.True(t, statistics.HistogramEqual(&a.Columns[i].Histogram, &b.Columns[i].Histogram, false)) + if a.Columns[i].CMSketch == nil { + require.Nil(t, b.Columns[i].CMSketch) + } else { + require.True(t, a.Columns[i].CMSketch.Equal(b.Columns[i].CMSketch)) + } + // The nil case has been considered in (*TopN).Equal() so we don't need to consider it here. + require.Truef(t, a.Columns[i].TopN.Equal(b.Columns[i].TopN), "%v, %v", a.Columns[i].TopN, b.Columns[i].TopN) + } + require.Equal(t, len(b.Indices), len(a.Indices)) + for i := range a.Indices { + require.True(t, statistics.HistogramEqual(&a.Indices[i].Histogram, &b.Indices[i].Histogram, false)) + if a.Indices[i].CMSketch == nil { + require.Nil(t, b.Indices[i].CMSketch) + } else { + require.True(t, a.Indices[i].CMSketch.Equal(b.Indices[i].CMSketch)) + } + require.True(t, a.Indices[i].TopN.Equal(b.Indices[i].TopN)) + } + require.True(t, internal.IsSameExtendedStats(a.ExtendedStats, b.ExtendedStats)) +} + +func cleanStats(tk *testkit.TestKit, do *domain.Domain) { + tk.MustExec("use test") + r := tk.MustQuery("show tables") + for _, tb := range r.Rows() { + tableName := tb[0] + tk.MustExec(fmt.Sprintf("drop table %v", tableName)) + } + tk.MustExec("delete from mysql.stats_meta") + tk.MustExec("delete from mysql.stats_histograms") + tk.MustExec("delete from mysql.stats_buckets") + tk.MustExec("delete from mysql.stats_extended") + tk.MustExec("delete from mysql.stats_fm_sketch") + tk.MustExec("delete from mysql.schema_index_usage") + tk.MustExec("delete from mysql.column_stats_usage") + do.StatsHandle().Clear() +} + +func TestConversion(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + tk.MustExec("create table t (a int, b int)") + tk.MustExec("create index c on t(a,b)") + tk.MustExec("insert into t(a,b) values (3, 1),(2, 1),(1, 10)") + tk.MustExec("analyze table t") + tk.MustExec("insert into t(a,b) values (1, 1),(3, 1),(5, 10)") + is := dom.InfoSchema() + h := dom.StatsHandle() + require.Nil(t, h.DumpStatsDeltaToKV(true)) + require.Nil(t, h.Update(is)) + + tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + jsonTbl, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, jsonTbl) + require.NoError(t, err) + + tbl := h.GetTableStats(tableInfo.Meta()) + requireTableEqual(t, loadTbl, tbl) + cleanStats(tk, dom) + var wg util.WaitGroupWrapper + wg.Run(func() { + require.Nil(t, h.Update(is)) + }) + err = h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0) + wg.Wait() + require.NoError(t, err) + loadTblInStorage := h.GetTableStats(tableInfo.Meta()) + requireTableEqual(t, loadTblInStorage, tbl) +} + +func getStatsJSON(t *testing.T, dom *domain.Domain, db, tableName string) *storage.JSONTable { + is := dom.InfoSchema() + h := dom.StatsHandle() + require.Nil(t, h.Update(is)) + table, err := is.TableByName(model.NewCIStr(db), model.NewCIStr(tableName)) + require.NoError(t, err) + tableInfo := table.Meta() + jsonTbl, err := h.DumpStatsToJSON("test", tableInfo, nil, true) + require.NoError(t, err) + return jsonTbl +} + +func TestDumpGlobalStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 2") + tk.MustExec("insert into t values (1), (2)") + tk.MustExec("analyze table t") + + // global-stats is not existed + stats := getStatsJSON(t, dom, "test", "t") + require.NotNil(t, stats.Partitions["p0"]) + require.NotNil(t, stats.Partitions["p1"]) + require.Nil(t, stats.Partitions[globalstats.TiDBGlobalStats]) + + // global-stats is existed + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t") + stats = getStatsJSON(t, dom, "test", "t") + require.NotNil(t, stats.Partitions["p0"]) + require.NotNil(t, stats.Partitions["p1"]) + require.NotNil(t, stats.Partitions[globalstats.TiDBGlobalStats]) +} + +func TestLoadGlobalStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 2") + tk.MustExec("insert into t values (1), (2)") + tk.MustExec("analyze table t") + globalStats := getStatsJSON(t, dom, "test", "t") + + // remove all statistics + tk.MustExec("delete from mysql.stats_meta") + tk.MustExec("delete from mysql.stats_histograms") + tk.MustExec("delete from mysql.stats_buckets") + dom.StatsHandle().Clear() + clearedStats := getStatsJSON(t, dom, "test", "t") + require.Equal(t, 0, len(clearedStats.Partitions)) + + // load global-stats back + require.Nil(t, dom.StatsHandle().LoadStatsFromJSON(context.Background(), dom.InfoSchema(), globalStats, 0)) + loadedStats := getStatsJSON(t, dom, "test", "t") + require.Equal(t, 3, len(loadedStats.Partitions)) // p0, p1, global +} + +func TestLoadPartitionStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 8") + vals := make([]string, 0, 5000) + for i := 0; i < 5000; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i)) + } + tk.MustExec("insert into t values " + strings.Join(vals, ",")) + tk.MustExec("analyze table t") + + table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + jsonTbl, err := dom.StatsHandle().DumpStatsToJSON("test", tableInfo, nil, true) + require.NoError(t, err) + pi := tableInfo.GetPartitionInfo() + originPartStats := make([]*statistics.Table, 0, len(pi.Definitions)) + for _, def := range pi.Definitions { + originPartStats = append(originPartStats, dom.StatsHandle().GetPartitionStats(tableInfo, def.ID)) + } + originGlobalStats := dom.StatsHandle().GetTableStats(tableInfo) + + // remove all statistics + tk.MustExec("delete from mysql.stats_meta") + tk.MustExec("delete from mysql.stats_histograms") + tk.MustExec("delete from mysql.stats_buckets") + dom.StatsHandle().Clear() + clearedStats := getStatsJSON(t, dom, "test", "t") + require.Equal(t, 0, len(clearedStats.Partitions)) + + // load stats back + require.Nil(t, dom.StatsHandle().LoadStatsFromJSON(context.Background(), dom.InfoSchema(), jsonTbl, 0)) + + // compare + for i, def := range pi.Definitions { + newPartStats := dom.StatsHandle().GetPartitionStats(tableInfo, def.ID) + requireTableEqual(t, originPartStats[i], newPartStats) + } + requireTableEqual(t, originGlobalStats, dom.StatsHandle().GetTableStats(tableInfo)) +} + +func TestLoadPartitionStatsErrPanic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 8") + vals := make([]string, 0, 5000) + for i := 0; i < 5000; i++ { + vals = append(vals, fmt.Sprintf("(%v)", i)) + } + tk.MustExec("insert into t values " + strings.Join(vals, ",")) + tk.MustExec("analyze table t") + + table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + jsonTbl, err := dom.StatsHandle().DumpStatsToJSON("test", tableInfo, nil, true) + require.NoError(t, err) + + ctx := context.WithValue(context.Background(), handle.TestLoadStatsErr{}, func(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { + return errors.New("ERROR") + }) + err = dom.StatsHandle().LoadStatsFromJSON(ctx, dom.InfoSchema(), jsonTbl, 0) + require.ErrorContains(t, err, "ERROR") + ctx = context.WithValue(context.Background(), handle.TestLoadStatsErr{}, func(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { + panic("PANIC") + }) + err = dom.StatsHandle().LoadStatsFromJSON(ctx, dom.InfoSchema(), jsonTbl, 0) + require.ErrorContains(t, err, "PANIC") // recover panic as an error +} + +func TestDumpPartitions(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + createTable := `CREATE TABLE t (a int, b int, primary key(a), index idx(b)) +PARTITION BY RANGE ( a ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21) +)` + tk.MustExec(createTable) + for i := 1; i < 21; i++ { + tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d)`, i, i)) + } + tk.MustExec("analyze table t") + is := dom.InfoSchema() + h := dom.StatsHandle() + require.Nil(t, h.Update(is)) + + table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := table.Meta() + jsonTbl, err := h.DumpStatsToJSON("test", tableInfo, nil, true) + require.NoError(t, err) + pi := tableInfo.GetPartitionInfo() + originTables := make([]*statistics.Table, 0, len(pi.Definitions)) + for _, def := range pi.Definitions { + originTables = append(originTables, h.GetPartitionStats(tableInfo, def.ID)) + } + + tk.MustExec("delete from mysql.stats_meta") + tk.MustExec("delete from mysql.stats_histograms") + tk.MustExec("delete from mysql.stats_buckets") + h.Clear() + + err = h.LoadStatsFromJSON(context.Background(), dom.InfoSchema(), jsonTbl, 0) + require.NoError(t, err) + for i, def := range pi.Definitions { + tt := h.GetPartitionStats(tableInfo, def.ID) + requireTableEqual(t, originTables[i], tt) + } +} + +func TestDumpAlteredTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + h := dom.StatsHandle() + oriLease := h.Lease() + h.SetLease(1) + defer func() { h.SetLease(oriLease) }() + tk.MustExec("create table t(a int, b int)") + tk.MustExec("analyze table t") + tk.MustExec("alter table t drop column a") + table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + _, err = h.DumpStatsToJSON("test", table.Meta(), nil, true) + require.NoError(t, err) +} + +func TestDumpCMSketchWithTopN(t *testing.T) { + // Just test if we can store and recover the Top N elements stored in database. + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t(a int)") + testKit.MustExec("insert into t values (1),(3),(4),(2),(5)") + testKit.MustExec("analyze table t") + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := dom.StatsHandle() + require.Nil(t, h.Update(is)) + + // Insert 30 fake data + fakeData := make([][]byte, 0, 30) + for i := 0; i < 30; i++ { + fakeData = append(fakeData, []byte(fmt.Sprintf("%01024d", i))) + } + cms, _, _, _ := statistics.NewCMSketchAndTopN(5, 2048, fakeData, 20, 100) + + stat := h.GetTableStats(tableInfo) + err = h.SaveStatsToStorage(tableInfo.ID, 1, 0, 0, &stat.Columns[tableInfo.Columns[0].ID].Histogram, cms, nil, statistics.Version2, 1, false, handle.StatsMetaHistorySourceLoadStats) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + + stat = h.GetTableStats(tableInfo) + cmsFromStore := stat.Columns[tableInfo.Columns[0].ID].CMSketch + require.NotNil(t, cmsFromStore) + require.True(t, cms.Equal(cmsFromStore)) + + jsonTable, err := h.DumpStatsToJSON("test", tableInfo, nil, true) + require.NoError(t, err) + err = h.LoadStatsFromJSON(context.Background(), is, jsonTable, 0) + require.NoError(t, err) + stat = h.GetTableStats(tableInfo) + cmsFromJSON := stat.Columns[tableInfo.Columns[0].ID].CMSketch.Copy() + require.True(t, cms.Equal(cmsFromJSON)) +} + +func TestDumpPseudoColumns(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t(a int, b int, index idx(a))") + // Force adding an pseudo tables in stats cache. + testKit.MustQuery("select * from t") + testKit.MustExec("analyze table t index idx") + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + h := dom.StatsHandle() + _, err = h.DumpStatsToJSON("test", tbl.Meta(), nil, true) + require.NoError(t, err) +} + +func TestDumpExtendedStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,5),(2,4),(3,3),(4,2),(5,1)") + h := dom.StatsHandle() + require.Nil(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + + is := dom.InfoSchema() + tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tbl := h.GetTableStats(tableInfo.Meta()) + jsonTbl, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, jsonTbl) + require.NoError(t, err) + requireTableEqual(t, loadTbl, tbl) + + cleanStats(tk, dom) + wg := util.WaitGroupWrapper{} + wg.Run(func() { + require.Nil(t, h.Update(is)) + }) + err = h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0) + wg.Wait() + require.NoError(t, err) + loadTblInStorage := h.GetTableStats(tableInfo.Meta()) + requireTableEqual(t, loadTblInStorage, tbl) +} + +func TestDumpVer2Stats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10))") + tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") + // mark column stats as needed + tk.MustExec("select * from t where a = 3") + tk.MustExec("select * from t where b = 'bbb'") + tk.MustExec("alter table t add index single(a)") + tk.MustExec("alter table t add index multi(a, b)") + tk.MustExec("analyze table t with 2 topn") + h := dom.StatsHandle() + is := dom.InfoSchema() + tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + storageTbl, err := h.TableStatsFromStorage(tableInfo.Meta(), tableInfo.Meta().ID, false, 0) + require.NoError(t, err) + + dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + + jsonBytes, err := json.MarshalIndent(dumpJSONTable, "", " ") + require.NoError(t, err) + + loadJSONTable := &storage.JSONTable{} + err = json.Unmarshal(jsonBytes, loadJSONTable) + require.NoError(t, err) + + loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, loadJSONTable) + require.NoError(t, err) + + // assert that a statistics.Table from storage dumped into JSON text and then unmarshalled into a statistics.Table keeps unchanged + requireTableEqual(t, loadTbl, storageTbl) + + // assert that this statistics.Table is the same as the one in stats cache + statsCacheTbl := h.GetTableStats(tableInfo.Meta()) + requireTableEqual(t, loadTbl, statsCacheTbl) + + err = h.LoadStatsFromJSON(context.Background(), is, loadJSONTable, 0) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + statsCacheTbl = h.GetTableStats(tableInfo.Meta()) + // assert that after the JSONTable above loaded into storage then updated into the stats cache, + // the statistics.Table in the stats cache is the same as the unmarshalled statistics.Table + requireTableEqual(t, statsCacheTbl, loadTbl) +} + +func TestLoadStatsForNewCollation(t *testing.T) { + // This test is almost the same as TestDumpVer2Stats, except that: b varchar(10) => b varchar(3) collate utf8mb4_unicode_ci + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(3) collate utf8mb4_unicode_ci)") + tk.MustExec("insert into t value(1, 'aaa'), (1, 'aaa'), (3, 'aab'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc'), (7, 'Ste')") + // mark column stats as needed + tk.MustExec("select * from t where a = 3") + tk.MustExec("select * from t where b = 'bbb'") + tk.MustExec("alter table t add index single(a)") + tk.MustExec("alter table t add index multi(a, b)") + tk.MustExec("analyze table t with 2 topn") + h := dom.StatsHandle() + is := dom.InfoSchema() + tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + storageTbl, err := h.TableStatsFromStorage(tableInfo.Meta(), tableInfo.Meta().ID, false, 0) + require.NoError(t, err) + + dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + + jsonBytes, err := json.MarshalIndent(dumpJSONTable, "", " ") + require.NoError(t, err) + + loadJSONTable := &storage.JSONTable{} + err = json.Unmarshal(jsonBytes, loadJSONTable) + require.NoError(t, err) + + loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, loadJSONTable) + require.NoError(t, err) + + // assert that a statistics.Table from storage dumped into JSON text and then unmarshalled into a statistics.Table keeps unchanged + requireTableEqual(t, loadTbl, storageTbl) + + // assert that this statistics.Table is the same as the one in stats cache + statsCacheTbl := h.GetTableStats(tableInfo.Meta()) + requireTableEqual(t, loadTbl, statsCacheTbl) + + err = h.LoadStatsFromJSON(context.Background(), is, loadJSONTable, 0) + require.NoError(t, err) + require.Nil(t, h.Update(is)) + statsCacheTbl = h.GetTableStats(tableInfo.Meta()) + // assert that after the JSONTable above loaded into storage then updated into the stats cache, + // the statistics.Table in the stats cache is the same as the unmarshalled statistics.Table + requireTableEqual(t, statsCacheTbl, loadTbl) +} + +func TestJSONTableToBlocks(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10))") + tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") + // mark column stats as needed + tk.MustExec("select * from t where a = 3") + tk.MustExec("select * from t where b = 'bbb'") + tk.MustExec("alter table t add index single(a)") + tk.MustExec("alter table t add index multi(a, b)") + tk.MustExec("analyze table t with 2 topn") + h := dom.StatsHandle() + is := dom.InfoSchema() + tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + jsOrigin, _ := json.Marshal(dumpJSONTable) + + blockSize := 30 + js, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) + require.NoError(t, err) + dumpJSONBlocks, err := storage.JSONTableToBlocks(js, blockSize) + require.NoError(t, err) + jsConverted, err := storage.BlocksToJSONTable(dumpJSONBlocks) + require.NoError(t, err) + jsonStr, err := json.Marshal(jsConverted) + require.NoError(t, err) + require.JSONEq(t, string(jsOrigin), string(jsonStr)) +} + +func TestLoadStatsFromOldVersion(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, index idx(b))") + h := dom.StatsHandle() + is := dom.InfoSchema() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + require.NoError(t, h.Update(is)) + + statsJSONFromOldVersion := `{ + "database_name": "test", + "table_name": "t", + "columns": { + "a": { + "histogram": { + "ndv": 0 + }, + "cm_sketch": null, + "null_count": 0, + "tot_col_size": 256, + "last_update_version": 440735055846047747, + "correlation": 0 + }, + "b": { + "histogram": { + "ndv": 0 + }, + "cm_sketch": null, + "null_count": 0, + "tot_col_size": 256, + "last_update_version": 440735055846047747, + "correlation": 0 + } + }, + "indices": { + "idx": { + "histogram": { + "ndv": 0 + }, + "cm_sketch": null, + "null_count": 0, + "tot_col_size": 0, + "last_update_version": 440735055846047747, + "correlation": 0 + } + }, + "count": 256, + "modify_count": 256, + "partitions": null +}` + jsonTbl := &storage.JSONTable{} + require.NoError(t, json.Unmarshal([]byte(statsJSONFromOldVersion), jsonTbl)) + require.NoError(t, h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0)) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + statsTbl := h.GetTableStats(tbl.Meta()) + for _, col := range statsTbl.Columns { + require.False(t, col.IsStatsInitialized()) + } + for _, idx := range statsTbl.Indices { + require.False(t, idx.IsStatsInitialized()) + } +} diff --git a/pkg/statistics/handle/extstats/BUILD.bazel b/pkg/statistics/handle/extstats/BUILD.bazel new file mode 100644 index 0000000000000..a3442b1168c27 --- /dev/null +++ b/pkg/statistics/handle/extstats/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "extstats", + srcs = ["extended_stats.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/extstats", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/statistics", + "//pkg/statistics/handle/util", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) diff --git a/statistics/handle/extstats/extended_stats.go b/pkg/statistics/handle/extstats/extended_stats.go similarity index 93% rename from statistics/handle/extstats/extended_stats.go rename to pkg/statistics/handle/extstats/extended_stats.go index c4505aa8407d5..4de8bd9f3f522 100644 --- a/statistics/handle/extstats/extended_stats.go +++ b/pkg/statistics/handle/extstats/extended_stats.go @@ -18,13 +18,13 @@ import ( "encoding/json" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/statistics/handle/gc_test.go b/pkg/statistics/handle/gc_test.go similarity index 98% rename from statistics/handle/gc_test.go rename to pkg/statistics/handle/gc_test.go index 1f3cc2ce2ec36..5fefeef6b1f02 100644 --- a/statistics/handle/gc_test.go +++ b/pkg/statistics/handle/gc_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/handle/globalstats/BUILD.bazel b/pkg/statistics/handle/globalstats/BUILD.bazel new file mode 100644 index 0000000000000..15db656e4439c --- /dev/null +++ b/pkg/statistics/handle/globalstats/BUILD.bazel @@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "globalstats", + srcs = [ + "global_stats.go", + "global_stats_async.go", + "merge_worker.go", + "topn.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/globalstats", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/statistics/handle/storage", + "//pkg/statistics/handle/util", + "//pkg/table", + "//pkg/types", + "//pkg/util/hack", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tiancaiamao_gp//:gp", + "@org_golang_x_sync//errgroup", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "globalstats_test", + timeout = "short", + srcs = ["topn_bench_test.go"], + embed = [":globalstats"], + flaky = True, + deps = [ + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "@com_github_stretchr_testify//require", + "@com_github_tiancaiamao_gp//:gp", + ], +) diff --git a/statistics/handle/globalstats/global_stats.go b/pkg/statistics/handle/globalstats/global_stats.go similarity index 93% rename from statistics/handle/globalstats/global_stats.go rename to pkg/statistics/handle/globalstats/global_stats.go index cde00f7e33e2b..48647b03f7b42 100644 --- a/statistics/handle/globalstats/global_stats.go +++ b/pkg/statistics/handle/globalstats/global_stats.go @@ -16,13 +16,13 @@ package globalstats import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tiancaiamao/gp" "go.uber.org/zap" ) diff --git a/statistics/handle/globalstats/global_stats_async.go b/pkg/statistics/handle/globalstats/global_stats_async.go similarity index 97% rename from statistics/handle/globalstats/global_stats_async.go rename to pkg/statistics/handle/globalstats/global_stats_async.go index 8e616ea444416..257bb1135e193 100644 --- a/statistics/handle/globalstats/global_stats_async.go +++ b/pkg/statistics/handle/globalstats/global_stats_async.go @@ -22,15 +22,15 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" "github.com/tiancaiamao/gp" "golang.org/x/sync/errgroup" ) diff --git a/statistics/handle/globalstats/merge_worker.go b/pkg/statistics/handle/globalstats/merge_worker.go similarity index 98% rename from statistics/handle/globalstats/merge_worker.go rename to pkg/statistics/handle/globalstats/merge_worker.go index 3edcb860a3465..74600eb7acffb 100644 --- a/statistics/handle/globalstats/merge_worker.go +++ b/pkg/statistics/handle/globalstats/merge_worker.go @@ -20,8 +20,8 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/util/hack" ) // StatsWrapper wrapper stats diff --git a/pkg/statistics/handle/globalstats/topn.go b/pkg/statistics/handle/globalstats/topn.go new file mode 100644 index 0000000000000..8251070f42e1d --- /dev/null +++ b/pkg/statistics/handle/globalstats/topn.go @@ -0,0 +1,115 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package globalstats + +import ( + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/tiancaiamao/gp" +) + +func mergeGlobalStatsTopN(gp *gp.Pool, sc sessionctx.Context, wrapper *StatsWrapper, + timeZone *time.Location, version int, n uint32, isIndex bool) (*statistics.TopN, + []statistics.TopNMeta, []*statistics.Histogram, error) { + mergeConcurrency := sc.GetSessionVars().AnalyzePartitionMergeConcurrency + killed := &sc.GetSessionVars().Killed + // use original method if concurrency equals 1 or for version1 + if mergeConcurrency < 2 { + return statistics.MergePartTopN2GlobalTopN(timeZone, version, wrapper.AllTopN, n, wrapper.AllHg, isIndex, killed) + } + batchSize := len(wrapper.AllTopN) / mergeConcurrency + if batchSize < 1 { + batchSize = 1 + } else if batchSize > MaxPartitionMergeBatchSize { + batchSize = MaxPartitionMergeBatchSize + } + return MergeGlobalStatsTopNByConcurrency(gp, mergeConcurrency, batchSize, wrapper, timeZone, version, n, isIndex, killed) +} + +// MergeGlobalStatsTopNByConcurrency merge partition topN by concurrency +// To merge global stats topn by concurrency, we will separate the partition topn in concurrency part and deal it with different worker. +// mergeConcurrency is used to control the total concurrency of the running worker, and mergeBatchSize is sued to control +// the partition size for each worker to solve it +func MergeGlobalStatsTopNByConcurrency(gp *gp.Pool, mergeConcurrency, mergeBatchSize int, wrapper *StatsWrapper, + timeZone *time.Location, version int, n uint32, isIndex bool, killed *uint32) (*statistics.TopN, + []statistics.TopNMeta, []*statistics.Histogram, error) { + if len(wrapper.AllTopN) < mergeConcurrency { + mergeConcurrency = len(wrapper.AllTopN) + } + tasks := make([]*TopnStatsMergeTask, 0) + for start := 0; start < len(wrapper.AllTopN); { + end := start + mergeBatchSize + if end > len(wrapper.AllTopN) { + end = len(wrapper.AllTopN) + } + task := NewTopnStatsMergeTask(start, end) + tasks = append(tasks, task) + start = end + } + var wg sync.WaitGroup + taskNum := len(tasks) + taskCh := make(chan *TopnStatsMergeTask, taskNum) + respCh := make(chan *TopnStatsMergeResponse, taskNum) + for i := 0; i < mergeConcurrency; i++ { + worker := NewTopnStatsMergeWorker(taskCh, respCh, wrapper, killed) + wg.Add(1) + gp.Go(func() { + defer wg.Done() + worker.Run(timeZone, isIndex, n, version) + }) + } + for _, task := range tasks { + taskCh <- task + } + close(taskCh) + wg.Wait() + close(respCh) + resps := make([]*TopnStatsMergeResponse, 0) + + // handle Error + hasErr := false + errMsg := make([]string, 0) + for resp := range respCh { + if resp.Err != nil { + hasErr = true + errMsg = append(errMsg, resp.Err.Error()) + } + resps = append(resps, resp) + } + if hasErr { + return nil, nil, nil, errors.New(strings.Join(errMsg, ",")) + } + + // fetch the response from each worker and merge them into global topn stats + sorted := make([]statistics.TopNMeta, 0, mergeConcurrency) + leftTopn := make([]statistics.TopNMeta, 0) + for _, resp := range resps { + if resp.TopN != nil { + sorted = append(sorted, resp.TopN.TopN...) + } + leftTopn = append(leftTopn, resp.PopedTopn...) + } + + globalTopN, popedTopn := statistics.GetMergedTopNFromSortedSlice(sorted, n) + + result := append(leftTopn, popedTopn...) + statistics.SortTopnMeta(result) + return globalTopN, result, wrapper.AllHg, nil +} diff --git a/statistics/handle/globalstats/topn_bench_test.go b/pkg/statistics/handle/globalstats/topn_bench_test.go similarity index 92% rename from statistics/handle/globalstats/topn_bench_test.go rename to pkg/statistics/handle/globalstats/topn_bench_test.go index 25c73095a3019..a5a2e095f939a 100644 --- a/statistics/handle/globalstats/topn_bench_test.go +++ b/pkg/statistics/handle/globalstats/topn_bench_test.go @@ -19,17 +19,17 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" "github.com/tiancaiamao/gp" ) -// cmd: go test -run=^$ -bench=BenchmarkMergePartTopN2GlobalTopNWithHists -benchmem github.com/pingcap/tidb/statistics/handle/globalstats +// cmd: go test -run=^$ -bench=BenchmarkMergePartTopN2GlobalTopNWithHists -benchmem github.com/pingcap/tidb/pkg/statistics/handle/globalstats func benchmarkMergePartTopN2GlobalTopNWithHists(partitions int, b *testing.B) { loc := time.UTC sc := stmtctx.NewStmtCtxWithTimeZone(loc) @@ -80,7 +80,7 @@ func benchmarkMergePartTopN2GlobalTopNWithHists(partitions int, b *testing.B) { } } -// cmd: go test -run=^$ -bench=BenchmarkMergeGlobalStatsTopNByConcurrencyWithHists -benchmem github.com/pingcap/tidb/statistics/handle/globalstats +// cmd: go test -run=^$ -bench=BenchmarkMergeGlobalStatsTopNByConcurrencyWithHists -benchmem github.com/pingcap/tidb/pkg/statistics/handle/globalstats func benchmarkMergeGlobalStatsTopNByConcurrencyWithHists(partitions int, b *testing.B) { loc := time.UTC sc := stmtctx.NewStmtCtxWithTimeZone(loc) diff --git a/pkg/statistics/handle/handle.go b/pkg/statistics/handle/handle.go new file mode 100644 index 0000000000000..d70377a77fd41 --- /dev/null +++ b/pkg/statistics/handle/handle.go @@ -0,0 +1,528 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "fmt" + "math" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + ddlUtil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/extstats" + "github.com/pingcap/tidb/pkg/statistics/handle/globalstats" + "github.com/pingcap/tidb/pkg/statistics/handle/history" + "github.com/pingcap/tidb/pkg/statistics/handle/lockstats" + handle_metrics "github.com/pingcap/tidb/pkg/statistics/handle/metrics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/statistics/handle/usage" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/tiancaiamao/gp" + atomic2 "go.uber.org/atomic" + "go.uber.org/zap" +) + +// Handle can update stats info periodically. +type Handle struct { + pool util.SessionPool + + // initStatsCtx is the ctx only used for initStats + initStatsCtx sessionctx.Context + + // sysProcTracker is used to track sys process like analyze + sysProcTracker sessionctx.SysProcTracker + + // TableInfoGetter is used to fetch table meta info. + util.TableInfoGetter + + // StatsGC is used to GC stats. + util.StatsGC + + // StatsUsage is used to track the usage of column / index statistics. + util.StatsUsage + + // StatsHistory is used to manage historical stats. + util.StatsHistory + + // StatsAnalyze is used to handle auto-analyze and manage analyze jobs. + util.StatsAnalyze + + // StatsLock is used to manage locked stats. + util.StatsLock + + // This gpool is used to reuse goroutine in the mergeGlobalStatsTopN. + gpool *gp.Pool + + // autoAnalyzeProcIDGetter is used to generate auto analyze ID. + autoAnalyzeProcIDGetter func() uint64 + + InitStatsDone chan struct{} + + // ddlEventCh is a channel to notify a ddl operation has happened. + // It is sent only by owner or the drop stats executor, and read by stats handle. + ddlEventCh chan *ddlUtil.Event + + // StatsCache ... + util.StatsCache + + // StatsLoad is used to load stats concurrently + StatsLoad StatsLoad + + lease atomic2.Duration +} + +func (h *Handle) execRows(sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, rerr error) { + _ = h.callWithSCtx(func(sctx sessionctx.Context) error { + rows, fields, rerr = util.ExecRows(sctx, sql, args...) + return nil + }) + return +} + +// Clear the statsCache, only for test. +func (h *Handle) Clear() { + h.StatsCache.Clear() + for len(h.ddlEventCh) > 0 { + <-h.ddlEventCh + } + h.ResetSessionStatsList() +} + +// NewHandle creates a Handle for update stats. +func NewHandle(_, initStatsCtx sessionctx.Context, lease time.Duration, pool util.SessionPool, tracker sessionctx.SysProcTracker, autoAnalyzeProcIDGetter func() uint64) (*Handle, error) { + cfg := config.GetGlobalConfig() + handle := &Handle{ + gpool: gp.New(math.MaxInt16, time.Minute), + ddlEventCh: make(chan *ddlUtil.Event, 1000), + pool: pool, + sysProcTracker: tracker, + autoAnalyzeProcIDGetter: autoAnalyzeProcIDGetter, + InitStatsDone: make(chan struct{}), + TableInfoGetter: util.NewTableInfoGetter(), + StatsAnalyze: autoanalyze.NewStatsAnalyze(pool), + StatsLock: lockstats.NewStatsLock(pool), + } + handle.StatsGC = storage.NewStatsGC(pool, lease, handle.TableInfoGetter, handle.MarkExtendedStatsDeleted) + + handle.initStatsCtx = initStatsCtx + handle.lease.Store(lease) + statsCache, err := cache.NewStatsCacheImpl() + if err != nil { + return nil, err + } + handle.StatsCache = statsCache + handle.StatsHistory = history.NewStatsHistory(pool, handle.StatsCache) + handle.StatsUsage = usage.NewStatsUsageImpl(pool, handle.TableInfoGetter, handle.StatsCache, + handle.StatsHistory, handle.GetLockedTables, handle.GetPartitionStats) + handle.StatsLoad.SubCtxs = make([]sessionctx.Context, cfg.Performance.StatsLoadConcurrency) + handle.StatsLoad.NeededItemsCh = make(chan *NeededItemTask, cfg.Performance.StatsLoadQueueSize) + handle.StatsLoad.TimeoutItemsCh = make(chan *NeededItemTask, cfg.Performance.StatsLoadQueueSize) + handle.StatsLoad.WorkingColMap = map[model.TableItemID][]chan stmtctx.StatsLoadResult{} + return handle, nil +} + +// Lease returns the stats lease. +func (h *Handle) Lease() time.Duration { + return h.lease.Load() +} + +// SetLease sets the stats lease. +func (h *Handle) SetLease(lease time.Duration) { + h.lease.Store(lease) +} + +// UpdateStatsHealthyMetrics updates stats healthy distribution metrics according to stats cache. +func (h *Handle) UpdateStatsHealthyMetrics() { + distribution := make([]int64, 5) + for _, tbl := range h.Values() { + healthy, ok := tbl.GetStatsHealthy() + if !ok { + continue + } + if healthy < 50 { + distribution[0]++ + } else if healthy < 80 { + distribution[1]++ + } else if healthy < 100 { + distribution[2]++ + } else { + distribution[3]++ + } + distribution[4]++ + } + for i, val := range distribution { + handle_metrics.StatsHealthyGauges[i].Set(float64(val)) + } +} + +// Update reads stats meta from store and updates the stats map. +func (h *Handle) Update(is infoschema.InfoSchema) error { + lastVersion := h.MaxTableStatsVersion() + // We need this because for two tables, the smaller version may write later than the one with larger version. + // Consider the case that there are two tables A and B, their version and commit time is (A0, A1) and (B0, B1), + // and A0 < B0 < B1 < A1. We will first read the stats of B, and update the lastVersion to B0, but we cannot read + // the table stats of A0 if we read stats that greater than lastVersion which is B0. + // We can read the stats if the diff between commit time and version is less than three lease. + offset := util.DurationToTS(3 * h.Lease()) + if h.MaxTableStatsVersion() >= offset { + lastVersion = lastVersion - offset + } else { + lastVersion = 0 + } + rows, _, err := h.execRows("SELECT version, table_id, modify_count, count from mysql.stats_meta where version > %? order by version", lastVersion) + if err != nil { + return errors.Trace(err) + } + tables := make([]*statistics.Table, 0, len(rows)) + deletedTableIDs := make([]int64, 0, len(rows)) + for _, row := range rows { + version := row.GetUint64(0) + physicalID := row.GetInt64(1) + modifyCount := row.GetInt64(2) + count := row.GetInt64(3) + table, ok := h.TableInfoByID(is, physicalID) + if !ok { + logutil.BgLogger().Debug("unknown physical ID in stats meta table, maybe it has been dropped", zap.Int64("ID", physicalID)) + deletedTableIDs = append(deletedTableIDs, physicalID) + continue + } + tableInfo := table.Meta() + if oldTbl, ok := h.Get(physicalID); ok && oldTbl.Version >= version && tableInfo.UpdateTS == oldTbl.TblInfoUpdateTS { + continue + } + tbl, err := h.TableStatsFromStorage(tableInfo, physicalID, false, 0) + // Error is not nil may mean that there are some ddl changes on this table, we will not update it. + if err != nil { + logutil.BgLogger().Error("error occurred when read table stats", zap.String("category", "stats"), zap.String("table", tableInfo.Name.O), zap.Error(err)) + continue + } + if tbl == nil { + deletedTableIDs = append(deletedTableIDs, physicalID) + continue + } + tbl.Version = version + tbl.RealtimeCount = count + tbl.ModifyCount = modifyCount + tbl.Name = getFullTableName(is, tableInfo) + tbl.TblInfoUpdateTS = tableInfo.UpdateTS + tables = append(tables, tbl) + } + h.UpdateStatsCache(tables, deletedTableIDs) + return nil +} + +// MergePartitionStats2GlobalStatsByTableID merge the partition-level stats to global-level stats based on the tableID. +func (h *Handle) MergePartitionStats2GlobalStatsByTableID(sc sessionctx.Context, + opts map[ast.AnalyzeOptionType]uint64, is infoschema.InfoSchema, + physicalID int64, + isIndex bool, + histIDs []int64, + _ map[int64]*statistics.Table, +) (globalStats *globalstats.GlobalStats, err error) { + return globalstats.MergePartitionStats2GlobalStatsByTableID(sc, h.gpool, opts, is, physicalID, isIndex, histIDs, h.TableInfoByID, h.callWithSCtx) +} + +func (h *Handle) loadTablePartitionStats(tableInfo *model.TableInfo, partitionDef *model.PartitionDefinition) (*statistics.Table, error) { + var partitionStats *statistics.Table + partitionStats, err := h.TableStatsFromStorage(tableInfo, partitionDef.ID, true, 0) + if err != nil { + return nil, err + } + // if the err == nil && partitionStats == nil, it means we lack the partition-level stats which the physicalID is equal to partitionID. + if partitionStats == nil { + errMsg := fmt.Sprintf("table `%s` partition `%s`", tableInfo.Name.L, partitionDef.Name.L) + err = types.ErrPartitionStatsMissing.GenWithStackByArgs(errMsg) + return nil, err + } + return partitionStats, nil +} + +// MergePartitionStats2GlobalStatsByTableID merge the partition-level stats to global-level stats based on the tableInfo. +func (h *Handle) mergePartitionStats2GlobalStats( + opts map[ast.AnalyzeOptionType]uint64, + is infoschema.InfoSchema, + globalTableInfo *model.TableInfo, + isIndex bool, + histIDs []int64, + _ map[int64]*statistics.Table, +) (gstats *globalstats.GlobalStats, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + gstats, err = globalstats.MergePartitionStats2GlobalStats(sctx, h.gpool, opts, is, globalTableInfo, isIndex, + histIDs, h.TableInfoByID, h.callWithSCtx) + return err + }) + return +} + +// GetTableStats retrieves the statistics table from cache, and the cache will be updated by a goroutine. +// TODO: remove GetTableStats later on. +func (h *Handle) GetTableStats(tblInfo *model.TableInfo) *statistics.Table { + return h.GetPartitionStats(tblInfo, tblInfo.ID) +} + +// GetPartitionStats retrieves the partition stats from cache. +// TODO: remove GetPartitionStats later on. +func (h *Handle) GetPartitionStats(tblInfo *model.TableInfo, pid int64) *statistics.Table { + var tbl *statistics.Table + if h == nil { + tbl = statistics.PseudoTable(tblInfo, false) + tbl.PhysicalID = pid + return tbl + } + tbl, ok := h.Get(pid) + if !ok { + tbl = statistics.PseudoTable(tblInfo, false) + tbl.PhysicalID = pid + if tblInfo.GetPartitionInfo() == nil || h.Len() < 64 { + h.UpdateStatsCache([]*statistics.Table{tbl}, nil) + } + return tbl + } + return tbl +} + +// LoadNeededHistograms will load histograms for those needed columns/indices. +func (h *Handle) LoadNeededHistograms() (err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + loadFMSketch := config.GetGlobalConfig().Performance.EnableLoadFMSketch + return storage.LoadNeededHistograms(sctx, h.StatsCache, loadFMSketch) + }, util.FlagWrapTxn) + return err +} + +// FlushStats flushes the cached stats update into store. +func (h *Handle) FlushStats() { + for len(h.ddlEventCh) > 0 { + e := <-h.ddlEventCh + if err := h.HandleDDLEvent(e); err != nil { + logutil.BgLogger().Error("handle ddl event fail", zap.String("category", "stats"), zap.Error(err)) + } + } + if err := h.DumpStatsDeltaToKV(true); err != nil { + logutil.BgLogger().Error("dump stats delta fail", zap.String("category", "stats"), zap.Error(err)) + } +} + +// TableStatsFromStorage loads table stats info from storage. +func (h *Handle) TableStatsFromStorage(tableInfo *model.TableInfo, physicalID int64, loadAll bool, snapshot uint64) (statsTbl *statistics.Table, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + var ok bool + statsTbl, ok = h.Get(physicalID) + if !ok { + statsTbl = nil + } + statsTbl, err = storage.TableStatsFromStorage(sctx, snapshot, tableInfo, physicalID, loadAll, h.Lease(), statsTbl) + return err + }, util.FlagWrapTxn) + return +} + +// StatsMetaCountAndModifyCount reads count and modify_count for the given table from mysql.stats_meta. +func (h *Handle) StatsMetaCountAndModifyCount(tableID int64) (count, modifyCount int64, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + count, modifyCount, _, err = storage.StatsMetaCountAndModifyCount(sctx, tableID) + return err + }, util.FlagWrapTxn) + return +} + +// SaveTableStatsToStorage saves the stats of a table to storage. +func (h *Handle) SaveTableStatsToStorage(results *statistics.AnalyzeResults, analyzeSnapshot bool, source string) (err error) { + return h.callWithSCtx(func(sctx sessionctx.Context) error { + return SaveTableStatsToStorage(sctx, results, analyzeSnapshot, source) + }) +} + +// SaveTableStatsToStorage saves the stats of a table to storage. +func SaveTableStatsToStorage(sctx sessionctx.Context, results *statistics.AnalyzeResults, analyzeSnapshot bool, source string) error { + statsVer, err := storage.SaveTableStatsToStorage(sctx, results, analyzeSnapshot) + if err == nil && statsVer != 0 { + tableID := results.TableID.GetStatisticsID() + if err1 := history.RecordHistoricalStatsMeta(sctx, tableID, statsVer, source); err1 != nil { + logutil.BgLogger().Error("record historical stats meta failed", + zap.Int64("table-id", tableID), + zap.Uint64("version", statsVer), + zap.String("source", source), + zap.Error(err1)) + } + } + return err +} + +// SaveStatsToStorage saves the stats to storage. +// If count is negative, both count and modify count would not be used and not be written to the table. Unless, corresponding +// fields in the stats_meta table will be updated. +// TODO: refactor to reduce the number of parameters +func (h *Handle) SaveStatsToStorage(tableID int64, count, modifyCount int64, isIndex int, hg *statistics.Histogram, + cms *statistics.CMSketch, topN *statistics.TopN, statsVersion int, isAnalyzed int64, updateAnalyzeTime bool, source string) (err error) { + var statsVer uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + statsVer, err = storage.SaveStatsToStorage(sctx, tableID, + count, modifyCount, isIndex, hg, cms, topN, statsVersion, isAnalyzed, updateAnalyzeTime) + return err + }) + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(tableID, statsVer, source) + } + return +} + +// SaveMetaToStorage will save stats_meta to storage. +func (h *Handle) SaveMetaToStorage(tableID, count, modifyCount int64, source string) (err error) { + var statsVer uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + statsVer, err = storage.SaveMetaToStorage(sctx, tableID, count, modifyCount) + return err + }) + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(tableID, statsVer, source) + } + return +} + +// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta. +func (h *Handle) InsertExtendedStats(statsName string, colIDs []int64, tp int, tableID int64, ifNotExists bool) (err error) { + var statsVer uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + statsVer, err = storage.InsertExtendedStats(sctx, h.StatsCache, statsName, colIDs, tp, tableID, ifNotExists) + return err + }) + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) + } + return +} + +// MarkExtendedStatsDeleted update the status of mysql.stats_extended to be `deleted` and the version of mysql.stats_meta. +func (h *Handle) MarkExtendedStatsDeleted(statsName string, tableID int64, ifExists bool) (err error) { + var statsVer uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + statsVer, err = storage.MarkExtendedStatsDeleted(sctx, h.StatsCache, statsName, tableID, ifExists) + return err + }) + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) + } + return +} + +const updateStatsCacheRetryCnt = 5 + +// ReloadExtendedStatistics drops the cache for extended statistics and reload data from mysql.stats_extended. +// TODO: move this method to the `extstats` package. +func (h *Handle) ReloadExtendedStatistics() error { + return h.callWithSCtx(func(sctx sessionctx.Context) error { + tables := make([]*statistics.Table, 0, h.Len()) + for _, tbl := range h.Values() { + t, err := storage.ExtendedStatsFromStorage(sctx, tbl.Copy(), tbl.PhysicalID, true) + if err != nil { + return err + } + tables = append(tables, t) + } + h.UpdateStatsCache(tables, nil) + return nil + }, util.FlagWrapTxn) +} + +// BuildExtendedStats build extended stats for column groups if needed based on the column samples. +func (h *Handle) BuildExtendedStats(tableID int64, cols []*model.ColumnInfo, collectors []*statistics.SampleCollector) (es *statistics.ExtendedStatsColl, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + es, err = extstats.BuildExtendedStats(sctx, tableID, cols, collectors) + return err + }) + return es, err +} + +// SaveExtendedStatsToStorage writes extended stats of a table into mysql.stats_extended. +func (h *Handle) SaveExtendedStatsToStorage(tableID int64, extStats *statistics.ExtendedStatsColl, isLoad bool) (err error) { + var statsVer uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + statsVer, err = storage.SaveExtendedStatsToStorage(sctx, tableID, extStats, isLoad) + return err + }) + if err == nil && statsVer != 0 { + h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) + } + return +} + +// CheckAnalyzeVersion checks whether all the statistics versions of this table's columns and indexes are the same. +func (h *Handle) CheckAnalyzeVersion(tblInfo *model.TableInfo, physicalIDs []int64, version *int) bool { + // We simply choose one physical id to get its stats. + var tbl *statistics.Table + for _, pid := range physicalIDs { + tbl = h.GetPartitionStats(tblInfo, pid) + if !tbl.Pseudo { + break + } + } + if tbl == nil || tbl.Pseudo { + return true + } + return statistics.CheckAnalyzeVerOnTable(tbl, version) +} + +// RecordHistoricalStatsToStorage records the given table's stats data to mysql.stats_history +func (h *Handle) RecordHistoricalStatsToStorage(dbName string, tableInfo *model.TableInfo, physicalID int64, isPartition bool) (uint64, error) { + var js *storage.JSONTable + var err error + if isPartition { + js, err = h.tableStatsToJSON(dbName, tableInfo, physicalID, 0) + } else { + js, err = h.DumpStatsToJSON(dbName, tableInfo, nil, true) + } + if err != nil { + return 0, errors.Trace(err) + } + + var version uint64 + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + version, err = history.RecordHistoricalStatsToStorage(sctx, physicalID, js) + return err + }, util.FlagWrapTxn) + return version, err +} + +// Close stops the background +func (h *Handle) Close() { + h.gpool.Close() + h.StatsCache.Close() +} + +const ( + // StatsMetaHistorySourceAnalyze indicates stats history meta source from analyze + StatsMetaHistorySourceAnalyze = "analyze" + // StatsMetaHistorySourceLoadStats indicates stats history meta source from load stats + StatsMetaHistorySourceLoadStats = "load stats" + // StatsMetaHistorySourceFlushStats indicates stats history meta source from flush stats + StatsMetaHistorySourceFlushStats = "flush stats" + // StatsMetaHistorySourceSchemaChange indicates stats history meta source from schema change + StatsMetaHistorySourceSchemaChange = "schema change" + // StatsMetaHistorySourceExtendedStats indicates stats history meta source from extended stats + StatsMetaHistorySourceExtendedStats = "extended stats" +) diff --git a/statistics/handle/handle_hist.go b/pkg/statistics/handle/handle_hist.go similarity index 96% rename from statistics/handle/handle_hist.go rename to pkg/statistics/handle/handle_hist.go index dc4730ae48b3c..924fda56698b0 100644 --- a/statistics/handle/handle_hist.go +++ b/pkg/statistics/handle/handle_hist.go @@ -21,18 +21,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/storage" - utilstats "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + utilstats "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/statistics/handle/handle_hist_test.go b/pkg/statistics/handle/handle_hist_test.go similarity index 90% rename from statistics/handle/handle_hist_test.go rename to pkg/statistics/handle/handle_hist_test.go index 3f8f65741be68..28d95020510ce 100644 --- a/statistics/handle/handle_hist_test.go +++ b/pkg/statistics/handle/handle_hist_test.go @@ -19,12 +19,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/require" ) @@ -40,14 +40,14 @@ func TestSyncLoadSkipUnAnalyzedItems(t *testing.T) { h.SetLease(1) // no item would be loaded - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/statistics/handle/assertSyncLoadItems", `return(0)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/statistics/handle/assertSyncLoadItems", `return(0)`)) tk.MustQuery("trace plan select * from t where a > 10") - failpoint.Disable("github.com/pingcap/tidb/statistics/handle/assertSyncLoadItems") + failpoint.Disable("github.com/pingcap/tidb/pkg/statistics/handle/assertSyncLoadItems") tk.MustExec("analyze table t1") // one column would be loaded - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/statistics/handle/assertSyncLoadItems", `return(1)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/statistics/handle/assertSyncLoadItems", `return(1)`)) tk.MustQuery("trace plan select * from t1 where a > 10") - failpoint.Disable("github.com/pingcap/tidb/statistics/handle/assertSyncLoadItems") + failpoint.Disable("github.com/pingcap/tidb/pkg/statistics/handle/assertSyncLoadItems") } func TestConcurrentLoadHist(t *testing.T) { @@ -175,11 +175,11 @@ func TestConcurrentLoadHistWithPanicAndFail(t *testing.T) { inTerms string }{ { - failPath: "github.com/pingcap/tidb/statistics/handle/mockReadStatsForOnePanic", + failPath: "github.com/pingcap/tidb/pkg/statistics/handle/mockReadStatsForOnePanic", inTerms: "panic", }, { - failPath: "github.com/pingcap/tidb/statistics/handle/mockReadStatsForOneFail", + failPath: "github.com/pingcap/tidb/pkg/statistics/handle/mockReadStatsForOneFail", inTerms: "return(true)", }, } diff --git a/pkg/statistics/handle/handletest/BUILD.bazel b/pkg/statistics/handle/handletest/BUILD.bazel new file mode 100644 index 0000000000000..dcce57477791f --- /dev/null +++ b/pkg/statistics/handle/handletest/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "handletest_test", + timeout = "short", + srcs = [ + "handle_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 37, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/parser/model", + "//pkg/planner/cardinality", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle", + "//pkg/statistics/handle/internal", + "//pkg/statistics/handle/util", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/ranger", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/statistics/handle/handletest/analyze/BUILD.bazel b/pkg/statistics/handle/handletest/analyze/BUILD.bazel new file mode 100644 index 0000000000000..cb68fafef5c99 --- /dev/null +++ b/pkg/statistics/handle/handletest/analyze/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "analyze_test", + timeout = "short", + srcs = [ + "analyze_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/domain", + "//pkg/parser/model", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/statistics/handle/handletest/analyze/analyze_test.go b/pkg/statistics/handle/handletest/analyze/analyze_test.go new file mode 100644 index 0000000000000..7fa3e193a9ad9 --- /dev/null +++ b/pkg/statistics/handle/handletest/analyze/analyze_test.go @@ -0,0 +1,246 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package analyze + +import ( + "bytes" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +// nolint:unused +func checkForGlobalStatsWithOpts(t *testing.T, dom *domain.Domain, db, tt, pp string, topn, buckets int) { + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(db), model.NewCIStr(tt)) + require.NoError(t, err) + + tblInfo := tbl.Meta() + physicalID := tblInfo.ID + if pp != "global" { + for _, def := range tbl.Meta().GetPartitionInfo().Definitions { + if def.Name.L == pp { + physicalID = def.ID + } + } + } + tblStats, err := dom.StatsHandle().TableStatsFromStorage(tblInfo, physicalID, true, 0) + require.NoError(t, err) + + delta := buckets/2 + 10 + for _, idxStats := range tblStats.Indices { + if len(idxStats.Buckets) == 0 { + continue // it's not loaded + } + numTopN := idxStats.TopN.Num() + numBuckets := len(idxStats.Buckets) + // since the hist-building algorithm doesn't stipulate the final bucket number to be equal to the expected number exactly, + // we have to check the results by a range here. + require.Equal(t, topn, numTopN) + require.GreaterOrEqual(t, numBuckets, buckets-delta) + require.LessOrEqual(t, numBuckets, buckets+delta) + } + for _, colStats := range tblStats.Columns { + if len(colStats.Buckets) == 0 { + continue // it's not loaded + } + numTopN := colStats.TopN.Num() + numBuckets := len(colStats.Buckets) + require.Equal(t, topn, numTopN) + require.GreaterOrEqual(t, numBuckets, buckets-delta) + require.LessOrEqual(t, numBuckets, buckets+delta) + } +} + +// nolint:unused +func prepareForGlobalStatsWithOptsV2(t *testing.T, dom *domain.Domain, tk *testkit.TestKit, tblName, dbName string) { + tk.MustExec("create database if not exists " + dbName) + tk.MustExec("use " + dbName) + tk.MustExec("drop table if exists " + tblName) + tk.MustExec(` create table ` + tblName + ` (a int, key(a)) partition by range (a) ` + + `(partition p0 values less than (100000), partition p1 values less than (200000))`) + buf1 := bytes.NewBufferString("insert into " + tblName + " values (0)") + buf2 := bytes.NewBufferString("insert into " + tblName + " values (100000)") + for i := 0; i < 1000; i++ { + buf1.WriteString(fmt.Sprintf(", (%v)", 2)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100002)) + buf1.WriteString(fmt.Sprintf(", (%v)", 1)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100001)) + buf1.WriteString(fmt.Sprintf(", (%v)", 0)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100000)) + } + for i := 0; i < 5000; i += 3 { + buf1.WriteString(fmt.Sprintf(", (%v)", i)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100000+i)) + } + tk.MustExec(buf1.String()) + tk.MustExec(buf2.String()) + tk.MustExec("set @@tidb_analyze_version=2") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) +} + +// nolint:unused +func prepareForGlobalStatsWithOpts(t *testing.T, dom *domain.Domain, tk *testkit.TestKit, tblName, dbName string) { + tk.MustExec("create database if not exists " + dbName) + tk.MustExec("use " + dbName) + tk.MustExec("drop table if exists " + tblName) + tk.MustExec(` create table ` + tblName + ` (a int, key(a)) partition by range (a) ` + + `(partition p0 values less than (100000), partition p1 values less than (200000))`) + buf1 := bytes.NewBufferString("insert into " + tblName + " values (0)") + buf2 := bytes.NewBufferString("insert into " + tblName + " values (100000)") + for i := 0; i < 5000; i += 3 { + buf1.WriteString(fmt.Sprintf(", (%v)", i)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100000+i)) + } + for i := 0; i < 1000; i++ { + buf1.WriteString(fmt.Sprintf(", (%v)", 0)) + buf2.WriteString(fmt.Sprintf(", (%v)", 100000)) + } + tk.MustExec(buf1.String()) + tk.MustExec(buf2.String()) + tk.MustExec("set @@tidb_analyze_version=2") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) +} + +func TestAnalyzeVirtualCol(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int generated always as (-a) virtual, c int generated always as (-a) stored, index (c))") + tk.MustExec("insert into t(a) values(2),(1),(1),(3),(NULL)") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("analyze table t") + require.Len(t, tk.MustQuery("show stats_histograms where table_name ='t'").Rows(), 3) +} + +func TestAnalyzeGlobalStatsWithOpts1(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + prepareForGlobalStatsWithOpts(t, dom, tk, "test_gstats_opt", "test_gstats_opt") + + // nolint:unused + type opt struct { + topn int + buckets int + err bool + } + + cases := []opt{ + {1, 37, false}, + {2, 47, false}, + {10, 77, false}, + {77, 219, false}, + {-31, 222, true}, + {10, -77, true}, + {100000, 47, true}, + {77, 47000, true}, + } + for _, ca := range cases { + sql := fmt.Sprintf("analyze table test_gstats_opt with %v topn, %v buckets", ca.topn, ca.buckets) + if !ca.err { + tk.MustExec(sql) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "global", ca.topn, ca.buckets) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "p0", ca.topn, ca.buckets) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "p1", ca.topn, ca.buckets) + } else { + err := tk.ExecToErr(sql) + require.Error(t, err) + } + } +} + +func TestAnalyzeGlobalStatsWithOpts2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + }() + tk.MustExec("set global tidb_persist_analyze_options=false") + prepareForGlobalStatsWithOptsV2(t, dom, tk, "test_gstats_opt2", "test_gstats_opt2") + + tk.MustExec("analyze table test_gstats_opt2 with 2 topn, 10 buckets, 1000 samples") + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 2, 10) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 2, 10) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 2, 10) + + // analyze a partition to let its options be different with others' + tk.MustExec("analyze table test_gstats_opt2 partition p0 with 3 topn, 20 buckets") + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 3, 20) // use new options + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 3, 20) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 2, 10) + + tk.MustExec("analyze table test_gstats_opt2 partition p1 with 1 topn, 15 buckets") + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 1, 15) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 3, 20) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 1, 15) + + tk.MustExec("analyze table test_gstats_opt2 partition p0 with 2 topn, 10 buckets") // change back to 2 topn + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 2, 10) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 2, 10) + checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 1, 15) +} + +func TestAnalyzeWithDynamicPartitionPruneMode(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec(`create table t (a int, key(a)) partition by range(a) + (partition p0 values less than (10), + partition p1 values less than (22))`) + tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) + tk.MustExec(`analyze table t with 1 topn, 2 buckets`) + rows := tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() + require.Len(t, rows, 2) + require.Equal(t, "4", rows[1][6]) + tk.MustExec("insert into t values (1), (2), (2)") + tk.MustExec("analyze table t partition p0 with 1 topn, 2 buckets") + rows = tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() + require.Len(t, rows, 2) + require.Equal(t, "5", rows[1][6]) + tk.MustExec("insert into t values (3)") + tk.MustExec("analyze table t partition p0 index a with 1 topn, 2 buckets") + rows = tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() + require.Len(t, rows, 1) + require.Equal(t, "6", rows[0][6]) +} + +func TestFMSWithAnalyzePartition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec(`create table t (a int, key(a)) partition by range(a) + (partition p0 values less than (10), + partition p1 values less than (22))`) + tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("0")) + tk.MustExec("analyze table t partition p0 with 1 topn, 2 buckets") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows( + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", + "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", + )) + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("2")) +} diff --git a/pkg/statistics/handle/handletest/analyze/main_test.go b/pkg/statistics/handle/handletest/analyze/main_test.go new file mode 100644 index 0000000000000..5e316bafb8bf7 --- /dev/null +++ b/pkg/statistics/handle/handletest/analyze/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package analyze + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/handletest/globalstats/BUILD.bazel b/pkg/statistics/handle/handletest/globalstats/BUILD.bazel new file mode 100644 index 0000000000000..16db6ada4cfb4 --- /dev/null +++ b/pkg/statistics/handle/handletest/globalstats/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "globalstats_test", + timeout = "short", + srcs = [ + "globalstats_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 14, + deps = [ + "//pkg/config", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/statistics/handle/handletest/globalstats/globalstats_test.go b/pkg/statistics/handle/handletest/globalstats/globalstats_test.go similarity index 99% rename from statistics/handle/handletest/globalstats/globalstats_test.go rename to pkg/statistics/handle/handletest/globalstats/globalstats_test.go index bd00035e6f9ac..fa45bf0d93790 100644 --- a/statistics/handle/handletest/globalstats/globalstats_test.go +++ b/pkg/statistics/handle/handletest/globalstats/globalstats_test.go @@ -20,9 +20,9 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -68,7 +68,7 @@ func simpleTest(t *testing.T) { } func TestGlobalStatsPanicInIOWorker(t *testing.T) { - fpName := "github.com/pingcap/tidb/statistics/handle/globalstats/PanicInIOWorker" + fpName := "github.com/pingcap/tidb/pkg/statistics/handle/globalstats/PanicInIOWorker" require.NoError(t, failpoint.Enable(fpName, "panic(\"inject panic\")")) defer func() { require.NoError(t, failpoint.Disable(fpName)) @@ -77,7 +77,7 @@ func TestGlobalStatsPanicInIOWorker(t *testing.T) { } func TestGlobalStatsPanicInCPUWorker(t *testing.T) { - fpName := "github.com/pingcap/tidb/statistics/handle/globalstats/PanicInCPUWorker" + fpName := "github.com/pingcap/tidb/pkg/statistics/handle/globalstats/PanicInCPUWorker" require.NoError(t, failpoint.Enable(fpName, "panic(\"inject panic\")")) defer func() { require.NoError(t, failpoint.Disable(fpName)) @@ -1017,8 +1017,8 @@ func TestGlobalStatsIndexNDV(t *testing.T) { } func TestGlobalStats(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) tk.MustExec("use test") diff --git a/pkg/statistics/handle/handletest/globalstats/main_test.go b/pkg/statistics/handle/handletest/globalstats/main_test.go new file mode 100644 index 0000000000000..12246d7c02b0b --- /dev/null +++ b/pkg/statistics/handle/handletest/globalstats/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package globalstats + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/handletest/handle_test.go b/pkg/statistics/handle/handletest/handle_test.go new file mode 100644 index 0000000000000..ab60147c2c783 --- /dev/null +++ b/pkg/statistics/handle/handletest/handle_test.go @@ -0,0 +1,1687 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handletest + +import ( + "fmt" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle" + "github.com/pingcap/tidb/pkg/statistics/handle/internal" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" +) + +func TestEmptyTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int, key cc1(c1), key cc2(c2))") + testKit.MustExec("analyze table t") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + count := cardinality.ColumnGreaterRowCount(mock.NewContext(), statsTbl, types.NewDatum(1), tableInfo.Columns[0].ID) + require.Equal(t, 0.0, count) +} + +func TestColumnIDs(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + testKit.MustExec("insert into t values(1, 2)") + testKit.MustExec("analyze table t") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + sctx := mock.NewContext() + ran := &ranger.Range{ + LowVal: []types.Datum{types.MinNotNullDatum()}, + HighVal: []types.Datum{types.NewIntDatum(2)}, + LowExclude: false, + HighExclude: true, + Collators: collate.GetBinaryCollatorSlice(1), + } + count, err := cardinality.GetRowCountByColumnRanges(sctx, &statsTbl.HistColl, tableInfo.Columns[0].ID, []*ranger.Range{ran}) + require.NoError(t, err) + require.Equal(t, float64(1), count) + + // Drop a column and the offset changed, + testKit.MustExec("alter table t drop column c1") + is = do.InfoSchema() + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + // At that time, we should get c2's stats instead of c1's. + count, err = cardinality.GetRowCountByColumnRanges(sctx, &statsTbl.HistColl, tableInfo.Columns[0].ID, []*ranger.Range{ran}) + require.NoError(t, err) + require.Equal(t, 0.0, count) +} + +func TestDurationToTS(t *testing.T) { + tests := []time.Duration{time.Millisecond, time.Second, time.Minute, time.Hour} + for _, test := range tests { + ts := util.DurationToTS(test) + require.Equal(t, int64(test), oracle.ExtractPhysical(ts)*int64(time.Millisecond)) + } +} + +func TestVersion(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit2 := testkit.NewTestKit(t, store) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t1 (c1 int, c2 int)") + testKit.MustExec("analyze table t1") + do := dom + is := do.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + h, err := handle.NewHandle(testKit.Session(), testKit2.Session(), time.Millisecond, do.SysSessionPool(), do.SysProcTracker(), do.GetAutoAnalyzeProcID) + defer func() { + h.Close() + }() + require.NoError(t, err) + unit := oracle.ComposeTS(1, 0) + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", 2*unit, tableInfo1.ID) + + require.NoError(t, h.Update(is)) + require.Equal(t, 2*unit, h.MaxTableStatsVersion()) + statsTbl1 := h.GetTableStats(tableInfo1) + require.False(t, statsTbl1.Pseudo) + + testKit.MustExec("create table t2 (c1 int, c2 int)") + testKit.MustExec("analyze table t2") + is = do.InfoSchema() + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tableInfo2 := tbl2.Meta() + // A smaller version write, and we can still read it. + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", unit, tableInfo2.ID) + require.NoError(t, h.Update(is)) + require.Equal(t, 2*unit, h.MaxTableStatsVersion()) + statsTbl2 := h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + + testKit.MustExec("insert t1 values(1,2)") + testKit.MustExec("analyze table t1") + offset := 3 * unit + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", offset+4, tableInfo1.ID) + require.NoError(t, h.Update(is)) + require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) + statsTbl1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(1), statsTbl1.RealtimeCount) + + testKit.MustExec("insert t2 values(1,2)") + testKit.MustExec("analyze table t2") + // A smaller version write, and we can still read it. + testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", offset+3, tableInfo2.ID) + require.NoError(t, h.Update(is)) + require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) + statsTbl2 = h.GetTableStats(tableInfo2) + require.Equal(t, int64(1), statsTbl2.RealtimeCount) + + testKit.MustExec("insert t2 values(1,2)") + testKit.MustExec("analyze table t2") + // A smaller version write, and we cannot read it. Because at this time, lastThree Version is 4. + testKit.MustExec("update mysql.stats_meta set version = 1 where table_id = ?", tableInfo2.ID) + require.NoError(t, h.Update(is)) + require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) + statsTbl2 = h.GetTableStats(tableInfo2) + require.Equal(t, int64(1), statsTbl2.RealtimeCount) + + // We add an index and analyze it, but DDL doesn't load. + testKit.MustExec("alter table t2 add column c3 int") + testKit.MustExec("analyze table t2") + // load it with old schema. + require.NoError(t, h.Update(is)) + statsTbl2 = h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + require.Nil(t, statsTbl2.Columns[int64(3)]) + // Next time DDL updated. + is = do.InfoSchema() + require.NoError(t, h.Update(is)) + statsTbl2 = h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + // We can read it without analyze again! Thanks for PrevLastVersion. + require.NotNil(t, statsTbl2.Columns[int64(3)]) + // assert WithGetTableStatsByQuery get the same result + statsTbl2 = h.GetTableStats(tableInfo2) + require.False(t, statsTbl2.Pseudo) + require.NotNil(t, statsTbl2.Columns[int64(3)]) +} + +func TestLoadHist(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 varchar(12), c2 char(12))") + do := dom + h := do.StatsHandle() + err := h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + rowCount := 10 + for i := 0; i < rowCount; i++ { + testKit.MustExec("insert into t values('a','ddd')") + } + testKit.MustExec("analyze table t") + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + oldStatsTbl := h.GetTableStats(tableInfo) + for i := 0; i < rowCount; i++ { + testKit.MustExec("insert into t values('bb','sdfga')") + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + err = h.Update(do.InfoSchema()) + require.NoError(t, err) + newStatsTbl := h.GetTableStats(tableInfo) + // The stats table is updated. + require.False(t, oldStatsTbl == newStatsTbl) + // Only the TotColSize of histograms is updated. + for id, hist := range oldStatsTbl.Columns { + require.Less(t, hist.TotColSize, newStatsTbl.Columns[id].TotColSize) + + temp := hist.TotColSize + hist.TotColSize = newStatsTbl.Columns[id].TotColSize + require.True(t, statistics.HistogramEqual(&hist.Histogram, &newStatsTbl.Columns[id].Histogram, false)) + hist.TotColSize = temp + + require.True(t, hist.CMSketch.Equal(newStatsTbl.Columns[id].CMSketch)) + require.Equal(t, newStatsTbl.Columns[id].Info, hist.Info) + } + // Add column c3, we only update c3. + testKit.MustExec("alter table t add column c3 int") + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + require.NoError(t, h.Update(is)) + newStatsTbl2 := h.GetTableStats(tableInfo) + require.False(t, newStatsTbl2 == newStatsTbl) + // The histograms is not updated. + for id, hist := range newStatsTbl.Columns { + require.Equal(t, newStatsTbl2.Columns[id], hist) + } + require.Greater(t, newStatsTbl2.Columns[int64(3)].LastUpdateVersion, newStatsTbl2.Columns[int64(1)].LastUpdateVersion) +} + +func TestCorrelation(t *testing.T) { + store := testkit.CreateMockStore(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t(c1 int primary key, c2 int)") + testKit.MustExec("select * from t where c1 > 10 and c2 > 10") + testKit.MustExec("insert into t values(1,1),(3,12),(4,20),(2,7),(5,21)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result := testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + testKit.MustExec("insert into t values(8,18)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) + + testKit.MustExec("truncate table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 0) + testKit.MustExec("insert into t values(1,21),(3,12),(4,7),(2,20),(5,1)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "-1", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "-1", result.Rows()[1][9]) + testKit.MustExec("insert into t values(8,4)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "-0.9428571428571428", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "-0.9428571428571428", result.Rows()[1][9]) + + testKit.MustExec("truncate table t") + testKit.MustExec("insert into t values (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),(18,1),(19,1),(20,2),(21,2),(22,2),(23,2),(24,2),(25,2)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + + testKit.MustExec("drop table t") + testKit.MustExec("create table t(c1 int, c2 int)") + testKit.MustExec("insert into t values(1,1),(2,7),(3,12),(4,20),(5,21),(8,18)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) + + testKit.MustExec("truncate table t") + testKit.MustExec("insert into t values(1,1),(2,7),(3,12),(8,18),(4,20),(5,21)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0.8285714285714286", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + testKit.MustExec("set @@session.tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() + require.Len(t, result.Rows(), 2) + require.Equal(t, "0.8285714285714286", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + + testKit.MustExec("drop table t") + testKit.MustExec("create table t(c1 int primary key, c2 int, c3 int, key idx_c2(c2))") + testKit.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3)") + testKit.MustExec("set @@session.tidb_analyze_version=1") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 0").Sort() + require.Len(t, result.Rows(), 3) + require.Equal(t, "0", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + require.Equal(t, "1", result.Rows()[2][9]) + result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 1").Sort() + require.Len(t, result.Rows(), 1) + require.Equal(t, "0", result.Rows()[0][9]) + testKit.MustExec("set @@tidb_analyze_version=2") + testKit.MustExec("analyze table t") + result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 0").Sort() + require.Len(t, result.Rows(), 3) + require.Equal(t, "1", result.Rows()[0][9]) + require.Equal(t, "1", result.Rows()[1][9]) + require.Equal(t, "1", result.Rows()[2][9]) + result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 1").Sort() + require.Len(t, result.Rows(), 1) + require.Equal(t, "0", result.Rows()[0][9]) +} + +func TestMergeGlobalTopN(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test;") + tk.MustExec("drop table if exists t;") + tk.MustExec("set @@session.tidb_analyze_version=2;") + tk.MustExec("set @@session.tidb_partition_prune_mode='dynamic';") + tk.MustExec(`create table t (a int, b int, key(b)) partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20) + );`) + tk.MustExec("insert into t values(1, 1), (1, 1), (1, 1), (1, 1), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), " + + "(11, 11), (11, 11), (11, 11), (12, 12), (12, 12), (12, 12), (13, 3), (13, 3);") + tk.MustExec("analyze table t with 2 topn;") + // The top2 values in partition p0 are 1(count = 4) and 3(count = 3). + tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'p0';").Check(testkit.Rows( + ("test t p0 b 0 1 4"), + ("test t p0 b 0 3 3"), + ("test t p0 b 1 1 4"), + ("test t p0 b 1 3 3"))) + // The top2 values in partition p1 are 11(count = 3) and 12(count = 3). + tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'p1';").Check(testkit.Rows( + ("test t p1 b 0 11 3"), + ("test t p1 b 0 12 3"), + ("test t p1 b 1 11 3"), + ("test t p1 b 1 12 3"))) + // The top2 values in global are 1(count = 4) and 3(count = 5). + // Notice: The value 3 does not appear in the topN structure of partition one. + // But we can still use the histogram to calculate its accurate value. + tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'global';").Check(testkit.Rows( + ("test t global b 0 1 4"), + ("test t global b 0 3 5"), + ("test t global b 1 1 4"), + ("test t global b 1 3 5"))) +} + +func TestExtendedStatsOps(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int primary key, b int, c int, d int)") + tk.MustExec("insert into t values(1,1,5,1),(2,2,4,2),(3,3,3,3),(4,4,2,4),(5,5,1,5)") + tk.MustExec("analyze table t") + err := tk.ExecToErr("alter table not_exist_db.t add stats_extended s1 correlation(b,c)") + require.Equal(t, "[schema:1146]Table 'not_exist_db.t' doesn't exist", err.Error()) + err = tk.ExecToErr("alter table not_exist_tbl add stats_extended s1 correlation(b,c)") + require.Equal(t, "[schema:1146]Table 'test.not_exist_tbl' doesn't exist", err.Error()) + err = tk.ExecToErr("alter table t add stats_extended s1 correlation(b,e)") + require.Equal(t, "[schema:1054]Unknown column 'e' in 't'", err.Error()) + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustQuery("show warnings").Check(testkit.Rows( + "Warning 1105 No need to create correlation statistics on the integer primary key column", + )) + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) + err = tk.ExecToErr("alter table t add stats_extended s1 correlation(b,c,d)") + require.Equal(t, "Only support Correlation and Dependency statistics types on 2 columns", err.Error()) + + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) + tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "2 [2,3] 0", + )) + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) + + tk.MustExec("update mysql.stats_extended set status = 1 where name = 's1'") + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 1) + + tk.MustExec("alter table t drop stats_extended s1") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "2 [2,3] 2", + )) + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) +} + +func TestAdminReloadStatistics1(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int primary key, b int, c int, d int)") + tk.MustExec("insert into t values(1,1,5,1),(2,2,4,2),(3,3,3,3),(4,4,2,4),(5,5,1,5)") + tk.MustExec("analyze table t") + tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "2 [2,3] 0", + )) + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) + + tk.MustExec("update mysql.stats_extended set status = 1 where name = 's1'") + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 1) + + tk.MustExec("delete from mysql.stats_extended where name = 's1'") + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 1) + + tk.MustExec("admin reload stats_extended") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) +} + +func TestAdminReloadStatistics2(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "1.000000 1", + )) + rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + + tk.MustExec("delete from mysql.stats_extended where name = 's1'") + is := dom.InfoSchema() + dom.StatsHandle().Update(is) + tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + + tk.MustExec("admin reload stats_extended") + tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 0) +} + +func TestCorrelationStatsCompute(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("insert into t values(1,1,5),(2,2,4),(3,3,3),(4,4,2),(5,5,1)") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Check(testkit.Rows()) + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("alter table t add stats_extended s2 correlation(a,c)") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 0", + "2 [1,3] 0", + )) + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) + + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 1.000000 1", + "2 [1,3] -1.000000 1", + )) + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 1.000000 1", + "2 [1,3] -1.000000 1", + )) + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 2) + foundS1, foundS2 := false, false + for name, item := range statsTbl.ExtendedStats.Stats { + switch name { + case "s1": + foundS1 = true + require.Equal(t, float64(1), item.ScalarVals) + case "s2": + foundS2 = true + require.Equal(t, float64(-1), item.ScalarVals) + default: + require.FailNow(t, "Unexpected extended stats in cache") + } + } + require.True(t, foundS1 && foundS2) + + // Check that table with NULLs won't cause panic + tk.MustExec("delete from t") + tk.MustExec("insert into t values(1,null,2), (2,null,null)") + tk.MustExec("set @@session.tidb_analyze_version=1") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 0.000000 1", + "2 [1,3] 1.000000 1", + )) + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 0.000000 1", + "2 [1,3] 1.000000 1", + )) + tk.MustExec("insert into t values(3,3,3)") + tk.MustExec("set @@session.tidb_analyze_version=1") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 1.000000 1", + "2 [1,3] 1.000000 1", + )) + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("analyze table t") + tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( + "2 [1,2] 1.000000 1", + "2 [1,3] 1.000000 1", + )) +} + +func TestSyncStatsExtendedRemoval(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 1) + item := statsTbl.ExtendedStats.Stats["s1"] + require.NotNil(t, item) + result := tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 1) + + tk.MustExec("alter table t drop stats_extended s1") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.NotNil(t, statsTbl) + require.NotNil(t, statsTbl.ExtendedStats) + require.Len(t, statsTbl.ExtendedStats.Stats, 0) + result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 0) +} + +func TestStaticPartitionPruneMode(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") + tk.MustExec("use test") + tk.MustExec(`create table t (a int, key(a)) partition by range(a) + (partition p0 values less than (10), + partition p1 values less than (22))`) + tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) + tk.MustExec(`analyze table t`) + require.True(t, tk.MustNoGlobalStats("t")) + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") + require.True(t, tk.MustNoGlobalStats("t")) + + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") + tk.MustExec(`insert into t values (4), (5), (6)`) + tk.MustExec(`analyze table t partition p0`) + require.True(t, tk.MustNoGlobalStats("t")) + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") + require.True(t, tk.MustNoGlobalStats("t")) + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") +} + +func TestMergeIdxHist(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") + defer tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") + tk.MustExec("use test") + tk.MustExec(` + create table t (a int, key(a)) + partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20))`) + tk.MustExec("set @@tidb_analyze_version=2") + defer tk.MustExec("set @@tidb_analyze_version=1") + tk.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (6), (null), (11), (12), (13), (14), (15), (16), (17), (18), (19), (19)") + + tk.MustExec("analyze table t with 2 topn, 2 buckets") + rows := tk.MustQuery("show stats_buckets where partition_name like 'global'") + require.Len(t, rows.Rows(), 4) +} + +func TestPartitionPruneModeSessionVariable(t *testing.T) { + failpoint.Enable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune", `return(true)`) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/planner/core/forceDynamicPrune") + + store := testkit.CreateMockStore(t) + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk1.MustExec("set tidb_cost_model_version=1") + tk1.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") + tk1.MustExec(`set @@tidb_analyze_version=2`) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + tk2.MustExec("set tidb_cost_model_version=1") + tk2.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Static) + "'") + tk2.MustExec(`set @@tidb_analyze_version=2`) + + tk1.MustExec(`create table t (a int, key(a)) partition by range(a) + (partition p0 values less than (10), + partition p1 values less than (22))`) + + tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "TableReader 10000.00 root partition:all data:TableFullScan", + "└─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", + )) + tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "PartitionUnion 20000.00 root ", + "├─TableReader 10000.00 root data:TableFullScan", + "│ └─TableFullScan 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", + "└─TableReader 10000.00 root data:TableFullScan", + " └─TableFullScan 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo", + )) + + tk1.MustExec(`insert into t values (1), (2), (3), (10), (11)`) + tk1.MustExec(`analyze table t with 1 topn, 2 buckets`) + tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "TableReader 5.00 root partition:all data:TableFullScan", + "└─TableFullScan 5.00 cop[tikv] table:t keep order:false", + )) + tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "PartitionUnion 5.00 root ", + "├─TableReader 3.00 root data:TableFullScan", + "│ └─TableFullScan 3.00 cop[tikv] table:t, partition:p0 keep order:false", + "└─TableReader 2.00 root data:TableFullScan", + " └─TableFullScan 2.00 cop[tikv] table:t, partition:p1 keep order:false", + )) + + tk1.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Static) + "'") + tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "PartitionUnion 5.00 root ", + "├─TableReader 3.00 root data:TableFullScan", + "│ └─TableFullScan 3.00 cop[tikv] table:t, partition:p0 keep order:false", + "└─TableReader 2.00 root data:TableFullScan", + " └─TableFullScan 2.00 cop[tikv] table:t, partition:p1 keep order:false", + )) + tk2.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") + tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( + "TableReader 5.00 root partition:all data:TableFullScan", + "└─TableFullScan 5.00 cop[tikv] table:t keep order:false", + )) +} + +func TestIndexUsageInformation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + session.SetIndexUsageSyncLease(1) + defer session.SetIndexUsageSyncLease(0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t_idx(a int, b int)") + tk.MustExec("create unique index idx_a on t_idx(a)") + tk.MustExec("create unique index idx_b on t_idx(b)") + tk.MustQuery("select a from t_idx where a=1") + querySQL := `select idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected + from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables + where tables.table_schema = idx.table_schema + AND tables.table_name = idx.table_name + AND tables.tidb_table_id = stats.table_id + AND idx.index_id = stats.index_id + AND idx.table_name = "t_idx"` + do := dom + err := do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 1 0", + )) + tk.MustExec("insert into t_idx values(1, 0)") + tk.MustQuery("select a from t_idx where a=1") + tk.MustQuery("select a from t_idx where a=1") + err = do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 3 2", + )) + tk.MustQuery("select b from t_idx where b=0") + tk.MustQuery("select b from t_idx where b=0") + err = do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Sort().Check(testkit.Rows( + "test t_idx idx_a 3 2", + "test t_idx idx_b 2 2", + )) +} + +// Functional Test:test batch insert +func TestIndexUsageInformationMultiIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + session.SetIndexUsageSyncLease(1) + defer session.SetIndexUsageSyncLease(0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + //len(column) = 11.len(index) = 11 + tk.MustExec("create table t_idx(a int, b int, c int, d int, e int, f int, g int, h int, i int, j int, k int)") + tk.MustExec("create unique index idx_a on t_idx(a)") + tk.MustExec("create unique index idx_b on t_idx(b)") + tk.MustExec("create unique index idx_c on t_idx(c)") + tk.MustExec("create unique index idx_d on t_idx(d)") + tk.MustExec("create unique index idx_e on t_idx(e)") + tk.MustExec("create unique index idx_f on t_idx(f)") + tk.MustExec("create unique index idx_g on t_idx(g)") + tk.MustExec("create unique index idx_h on t_idx(h)") + tk.MustExec("create unique index idx_i on t_idx(i)") + tk.MustExec("create unique index idx_j on t_idx(j)") + tk.MustExec("create unique index idx_k on t_idx(k)") + + tk.MustQuery("select a from t_idx where a=1") + querySQL := `select idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected + from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables + where tables.table_schema = idx.table_schema + AND tables.table_name = idx.table_name + AND tables.tidb_table_id = stats.table_id + AND idx.index_id = stats.index_id + AND idx.table_name = "t_idx" ORDER BY idx.key_name` + do := dom + err := do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 1 0", + )) + tk.MustExec("insert into t_idx values(1,1,1,1,1,1,1,1,1,1,1)") + tk.MustQuery("select a from t_idx where a=1") + tk.MustQuery("select a from t_idx where a=1") + err = do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 3 2", + )) + + tk.MustQuery("select a from t_idx where a=1") + tk.MustQuery("select b from t_idx where b=1") + tk.MustQuery("select c from t_idx where c=1") + tk.MustQuery("select d from t_idx where d=1") + tk.MustQuery("select e from t_idx where e=1") + tk.MustQuery("select f from t_idx where f=1") + tk.MustQuery("select g from t_idx where g=1") + tk.MustQuery("select h from t_idx where h=1") + tk.MustQuery("select i from t_idx where i=1") + tk.MustQuery("select j from t_idx where j=1") + tk.MustQuery("select k from t_idx where k=1") + + err = do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 4 3", + "test t_idx idx_b 1 1", + "test t_idx idx_c 1 1", + "test t_idx idx_d 1 1", + "test t_idx idx_e 1 1", + "test t_idx idx_f 1 1", + "test t_idx idx_g 1 1", + "test t_idx idx_h 1 1", + "test t_idx idx_i 1 1", + "test t_idx idx_j 1 1", + "test t_idx idx_k 1 1", + )) + + tk.MustQuery("select a from t_idx where a=1") + tk.MustQuery("select b from t_idx where b=1") + tk.MustQuery("select c from t_idx where c=1") + tk.MustQuery("select d from t_idx where d=1") + tk.MustQuery("select e from t_idx where e=1") + tk.MustQuery("select f from t_idx where f=1") + tk.MustQuery("select g from t_idx where g=1") + tk.MustQuery("select h from t_idx where h=1") + tk.MustQuery("select i from t_idx where i=1") + tk.MustQuery("select j from t_idx where j=1") + tk.MustQuery("select k from t_idx where k=1") + + err = do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows( + "test t_idx idx_a 5 4", + "test t_idx idx_b 2 2", + "test t_idx idx_c 2 2", + "test t_idx idx_d 2 2", + "test t_idx idx_e 2 2", + "test t_idx idx_f 2 2", + "test t_idx idx_g 2 2", + "test t_idx idx_h 2 2", + "test t_idx idx_i 2 2", + "test t_idx idx_j 2 2", + "test t_idx idx_k 2 2", + )) +} + +//cd statistics/handle +//go test -run BenchmarkIndexUsageInformationInsert -bench BenchmarkIndexUsageInformationInsert -benchmem -benchtime=20s +//old 6998 3379135 ns/op 994594 B/op 12659 allocs/op +//new 18472 1299401 ns/op 473919 B/op 5628 allocs/op + +func BenchmarkIndexUsageInformationInsert(b *testing.B) { + //init + b.StopTimer() + store, dom := testkit.CreateMockStoreAndDomain(b) + session.SetIndexUsageSyncLease(1) + defer session.SetIndexUsageSyncLease(0) + tk := testkit.NewTestKit(b, store) + tk.MustExec("use test") + //len(column) = 11.len(index) = 11 + tk.MustExec("create table t_idx(a int, b int, c int, d int, e int, f int, g int, h int, i int, j int, k int)") + tk.MustExec("create unique index idx_a on t_idx(a)") + tk.MustExec("create unique index idx_b on t_idx(b)") + tk.MustExec("create unique index idx_c on t_idx(c)") + tk.MustExec("create unique index idx_d on t_idx(d)") + tk.MustExec("create unique index idx_e on t_idx(e)") + tk.MustExec("create unique index idx_f on t_idx(f)") + tk.MustExec("create unique index idx_g on t_idx(g)") + tk.MustExec("create unique index idx_h on t_idx(h)") + tk.MustExec("create unique index idx_i on t_idx(i)") + tk.MustExec("create unique index idx_j on t_idx(j)") + tk.MustExec("create unique index idx_k on t_idx(k)") + b.StartTimer() + + for i := 0; i < b.N; i++ { + tk.MustQuery("select a from t_idx where a=1") + tk.MustQuery("select b from t_idx where b=1") + tk.MustQuery("select c from t_idx where c=1") + tk.MustQuery("select d from t_idx where d=1") + tk.MustQuery("select e from t_idx where e=1") + tk.MustQuery("select f from t_idx where f=1") + tk.MustQuery("select g from t_idx where g=1") + tk.MustQuery("select h from t_idx where h=1") + tk.MustQuery("select i from t_idx where i=1") + tk.MustQuery("select j from t_idx where j=1") + tk.MustQuery("select k from t_idx where k=1") + do := dom + err := do.StatsHandle().DumpIndexUsageToKV() + require.NoError(b, err) + } +} + +func TestGCIndexUsageInformation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + session.SetIndexUsageSyncLease(1) + defer session.SetIndexUsageSyncLease(0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t_idx(a int, b int)") + tk.MustExec("create unique index idx_a on t_idx(a)") + tk.MustQuery("select a from t_idx where a=1") + do := dom + err := do.StatsHandle().DumpIndexUsageToKV() + require.NoError(t, err) + querySQL := `select count(distinct idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected) + from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables + where tables.table_schema = idx.table_schema + AND tables.table_name = idx.table_name + AND tables.tidb_table_id = stats.table_id + AND idx.index_id = stats.index_id + AND idx.table_name = "t_idx"` + tk.MustQuery(querySQL).Check(testkit.Rows("1")) + tk.MustExec("drop index `idx_a` on t_idx") + err = do.StatsHandle().GCIndexUsage() + require.NoError(t, err) + tk.MustQuery(querySQL).Check(testkit.Rows("0")) +} + +func TestRepetitiveAddDropExtendedStats(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( + "s1 0", + )) + result := tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 0) + tk.MustExec("analyze table t") + tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( + "s1 1", + )) + result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 1) + tk.MustExec("alter table t drop stats_extended s1") + tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( + "s1 2", + )) + result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 0) + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( + "s1 0", + )) + result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 0) + tk.MustExec("analyze table t") + tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( + "s1 1", + )) + result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") + require.Len(t, result.Rows(), 1) +} + +func TestDuplicateFMSketch(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + defer tk.MustExec("set @@tidb_partition_prune_mode='static'") + tk.MustExec("create table t(a int, b int, c int) partition by hash(a) partitions 3") + tk.MustExec("insert into t values (1, 1, 1)") + tk.MustExec("analyze table t") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("9")) + tk.MustExec("analyze table t") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("9")) + + tk.MustExec("alter table t drop column b") + require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), time.Duration(0))) + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) +} + +func TestIndexFMSketch(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, index ia(a), index ibc(b, c)) partition by hash(a) partitions 3") + tk.MustExec("insert into t values (1, 1, 1)") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + defer tk.MustExec("set @@tidb_partition_prune_mode='static'") + tk.MustExec("analyze table t index ia") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("3")) + tk.MustExec("analyze table t index ibc") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) + tk.MustExec("analyze table t") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("15")) + tk.MustExec("drop table if exists t") + require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) + + // clustered index + tk.MustExec("drop table if exists t") + tk.MustExec("set @@tidb_enable_clustered_index=ON") + tk.MustExec("create table t (a datetime, b datetime, primary key (a)) partition by hash(year(a)) partitions 3") + tk.MustExec("insert into t values ('2000-01-01', '2000-01-01')") + tk.MustExec("analyze table t") + tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) + tk.MustExec("drop table if exists t") + require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) + + // test NDV + checkNDV := func(rows, ndv int) { + tk.MustExec("analyze table t") + rs := tk.MustQuery("select value from mysql.stats_fm_sketch").Rows() + require.Len(t, rs, rows) + for i := range rs { + fm, err := statistics.DecodeFMSketch([]byte(rs[i][0].(string))) + require.NoError(t, err) + require.Equal(t, int64(ndv), fm.NDV()) + } + } + + tk.MustExec("set @@tidb_enable_clustered_index=OFF") + tk.MustExec("create table t(a int, key(a)) partition by hash(a) partitions 3") + tk.MustExec("insert into t values (1), (2), (2), (3)") + checkNDV(6, 1) + tk.MustExec("insert into t values (4), (5), (6)") + checkNDV(6, 2) + tk.MustExec("insert into t values (2), (5)") + checkNDV(6, 2) + tk.MustExec("drop table if exists t") + require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) + + // clustered index + tk.MustExec("set @@tidb_enable_clustered_index=ON") + tk.MustExec("create table t (a datetime, b datetime, primary key (a)) partition by hash(year(a)) partitions 3") + tk.MustExec("insert into t values ('2000-01-01', '2001-01-01'), ('2001-01-01', '2001-01-01'), ('2002-01-01', '2001-01-01')") + checkNDV(6, 1) + tk.MustExec("insert into t values ('1999-01-01', '1998-01-01'), ('1997-01-02', '1999-01-02'), ('1998-01-03', '1999-01-03')") + checkNDV(6, 2) +} + +func TestShowExtendedStats4DropColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int)") + tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("alter table t add stats_extended s2 correlation(b,c)") + tk.MustExec("analyze table t") + rows := tk.MustQuery("show stats_extended").Sort().Rows() + require.Len(t, rows, 2) + require.Equal(t, "s1", rows[0][2]) + require.Equal(t, "[a,b]", rows[0][3]) + require.Equal(t, "s2", rows[1][2]) + require.Equal(t, "[b,c]", rows[1][3]) + + tk.MustExec("alter table t drop column b") + rows = tk.MustQuery("show stats_extended").Rows() + require.Len(t, rows, 0) + + // Previously registered extended stats should be invalid for re-created columns. + tk.MustExec("alter table t add column b int") + rows = tk.MustQuery("show stats_extended").Rows() + require.Len(t, rows, 0) +} + +func TestExtStatsOnReCreatedTable(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + rows := tk.MustQuery("select table_id, stats from mysql.stats_extended where name = 's1'").Rows() + require.Len(t, rows, 1) + tableID1 := rows[0][0] + require.Equal(t, "1.000000", rows[0][1]) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "1.000000", rows[0][5]) + + tk.MustExec("drop table t") + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 0) + + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,3),(2,2),(3,1)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + rows = tk.MustQuery("select table_id, stats from mysql.stats_extended where name = 's1' order by stats").Rows() + require.Len(t, rows, 2) + tableID2 := rows[0][0] + require.NotEqual(t, tableID1, tableID2) + require.Equal(t, tableID1, rows[1][0]) + require.Equal(t, "-1.000000", rows[0][1]) + require.Equal(t, "1.000000", rows[1][1]) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "-1.000000", rows[0][5]) +} + +func TestExtStatsOnReCreatedColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] 1.000000", + )) + rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "[a,b]", rows[0][3]) + require.Equal(t, "1.000000", rows[0][5]) + + tk.MustExec("alter table t drop column b") + tk.MustExec("alter table t add column b int") + tk.MustQuery("select * from t").Sort().Check(testkit.Rows( + "1 ", + "2 ", + "3 ", + )) + tk.MustExec("update t set b = 3 where a = 1") + tk.MustExec("update t set b = 2 where a = 2") + tk.MustExec("update t set b = 1 where a = 3") + tk.MustQuery("select * from t").Sort().Check(testkit.Rows( + "1 3", + "2 2", + "3 1", + )) + tk.MustExec("analyze table t") + // Previous extended stats would not be collected and would not take effect anymore, it will be removed by stats GC. + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] 1.000000", + )) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 0) +} + +func TestExtStatsOnRenamedColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] 1.000000", + )) + rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "[a,b]", rows[0][3]) + require.Equal(t, "1.000000", rows[0][5]) + + tk.MustExec("alter table t rename column b to c") + tk.MustExec("update t set c = 3 where a = 1") + tk.MustExec("update t set c = 2 where a = 2") + tk.MustExec("update t set c = 1 where a = 3") + tk.MustQuery("select * from t").Sort().Check(testkit.Rows( + "1 3", + "2 2", + "3 1", + )) + tk.MustExec("analyze table t") + // Previous extended stats would still be collected and take effect. + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] -1.000000", + )) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "[a,c]", rows[0][3]) + require.Equal(t, "-1.000000", rows[0][5]) +} + +func TestExtStatsOnModifiedColumn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set session tidb_enable_extended_stats = on") + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values(1,1),(2,2),(3,3)") + tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") + tk.MustExec("analyze table t") + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] 1.000000", + )) + rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "[a,b]", rows[0][3]) + require.Equal(t, "1.000000", rows[0][5]) + + tk.MustExec("alter table t modify column b bigint") + tk.MustExec("update t set b = 3 where a = 1") + tk.MustExec("update t set b = 2 where a = 2") + tk.MustExec("update t set b = 1 where a = 3") + tk.MustQuery("select * from t").Sort().Check(testkit.Rows( + "1 3", + "2 2", + "3 1", + )) + tk.MustExec("analyze table t") + // Previous extended stats would still be collected and take effect. + tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( + "[1,2] -1.000000", + )) + rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "[a,b]", rows[0][3]) + require.Equal(t, "-1.000000", rows[0][5]) +} + +func TestCorrelationWithDefinedCollate(t *testing.T) { + store := testkit.CreateMockStore(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t(a int primary key, b varchar(8) character set utf8mb4 collate utf8mb4_general_ci, c varchar(8) character set utf8mb4 collate utf8mb4_bin)") + testKit.MustExec("insert into t values(1,'aa','aa'),(2,'Cb','Cb'),(3,'CC','CC')") + testKit.MustExec("analyze table t") + testKit.MustQuery("select a from t order by b").Check(testkit.Rows( + "1", + "2", + "3", + )) + testKit.MustQuery("select a from t order by c").Check(testkit.Rows( + "3", + "2", + "1", + )) + rows := testKit.MustQuery("show stats_histograms where table_name = 't'").Sort().Rows() + require.Len(t, rows, 3) + require.Equal(t, "1", rows[1][9]) + require.Equal(t, "-1", rows[2][9]) + testKit.MustExec("set session tidb_enable_extended_stats = on") + testKit.MustExec("alter table t add stats_extended s1 correlation(b,c)") + testKit.MustExec("analyze table t") + rows = testKit.MustQuery("show stats_extended where stats_name = 's1'").Sort().Rows() + require.Len(t, rows, 1) + require.Equal(t, "[b,c]", rows[0][3]) + require.Equal(t, "-1.000000", rows[0][5]) +} + +func TestLoadHistogramWithCollate(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t(a varchar(10) collate utf8mb4_unicode_ci);") + testKit.MustExec("insert into t values('abcdefghij');") + testKit.MustExec("insert into t values('abcdufghij');") + testKit.MustExec("analyze table t with 0 topn;") + do := dom + h := do.StatsHandle() + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + _, err = h.TableStatsFromStorage(tblInfo, tblInfo.ID, true, 0) + require.NoError(t, err) +} + +func TestStatsCacheUpdateSkip(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + do := dom + h := do.StatsHandle() + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + testKit.MustExec("insert into t values(1, 2)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec("analyze table t") + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl1 := h.GetTableStats(tableInfo) + require.False(t, statsTbl1.Pseudo) + h.Update(is) + statsTbl2 := h.GetTableStats(tableInfo) + require.Equal(t, statsTbl2, statsTbl1) +} + +func TestIssues24349(t *testing.T) { + store := testkit.CreateMockStore(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set @@tidb_partition_prune_mode='dynamic'") + testKit.MustExec("set @@tidb_analyze_version=2") + defer testKit.MustExec("set @@tidb_analyze_version=1") + defer testKit.MustExec("set @@tidb_partition_prune_mode='static'") + testKit.MustExec("create table t (a int, b int) partition by hash(a) partitions 3") + testKit.MustExec("insert into t values (0, 3), (0, 3), (0, 3), (0, 2), (1, 1), (1, 2), (1, 2), (1, 2), (1, 3), (1, 4), (2, 1), (2, 1)") + testKit.MustExec("analyze table t with 1 topn, 3 buckets") + testKit.MustQuery("show stats_buckets where partition_name='global'").Check(testkit.Rows( + "test t global a 0 0 2 2 0 2 0", + "test t global b 0 0 3 1 1 2 0", + "test t global b 0 1 10 1 4 4 0", + )) +} + +func testIncrementalModifyCountUpdateHelper(analyzeSnapshot bool) func(*testing.T) { + return func(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + if analyzeSnapshot { + tk.MustExec("set @@session.tidb_enable_analyze_snapshot = on") + } else { + tk.MustExec("set @@session.tidb_enable_analyze_snapshot = off") + } + tk.MustExec("create table t(a int)") + tk.MustExec("set @@session.tidb_analyze_version = 2") + h := dom.StatsHandle() + err := h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + tid := tblInfo.ID + + tk.MustExec("insert into t values(1),(2),(3)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + err = h.Update(dom.InfoSchema()) + require.NoError(t, err) + tk.MustExec("analyze table t") + tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( + "3 0", + )) + + tk.MustExec("begin") + txn, err := tk.Session().Txn(false) + require.NoError(t, err) + startTS := txn.StartTS() + tk.MustExec("commit") + + tk.MustExec("insert into t values(4),(5),(6)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + err = h.Update(dom.InfoSchema()) + require.NoError(t, err) + + // Simulate that the analyze would start before and finish after the second insert. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS))) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectBaseCount", "return(3)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/injectBaseModifyCount", "return(0)")) + tk.MustExec("analyze table t") + if analyzeSnapshot { + // Check the count / modify_count changes during the analyze are not lost. + tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( + "6 3", + )) + // Check the histogram is correct for the snapshot analyze. + tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( + "3", + )) + } else { + // Since analyze use max ts to read data, it finds the row count is 6 and directly set count to 6 rather than incrementally update it. + // But it still incrementally updates modify_count. + tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( + "6 3", + )) + // Check the histogram is collected from the latest data rather than the snapshot at startTS. + tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( + "6", + )) + } + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectAnalyzeSnapshot")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectBaseCount")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/injectBaseModifyCount")) + } +} + +func TestIncrementalModifyCountUpdate(t *testing.T) { + for _, analyzeSnapshot := range []bool{true, false} { + t.Run(fmt.Sprintf("%s-%t", t.Name(), analyzeSnapshot), testIncrementalModifyCountUpdateHelper(analyzeSnapshot)) + } +} + +func TestRecordHistoricalStatsToStorage(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 2") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10))") + tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") + // mark column stats as needed + tk.MustExec("select * from t where a = 3") + tk.MustExec("select * from t where b = 'bbb'") + tk.MustExec("alter table t add index single(a)") + tk.MustExec("alter table t add index multi(a, b)") + tk.MustExec("analyze table t with 2 topn") + + tableInfo, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + version, err := dom.StatsHandle().RecordHistoricalStatsToStorage("t", tableInfo.Meta(), tableInfo.Meta().ID, false) + require.NoError(t, err) + + rows := tk.MustQuery(fmt.Sprintf("select count(*) from mysql.stats_history where version = '%d'", version)).Rows() + num, _ := strconv.Atoi(rows[0][0].(string)) + require.GreaterOrEqual(t, num, 1) +} + +func TestEvictedColumnLoadedStatus(t *testing.T) { + t.Skip("skip this test because it is useless") + restore := config.RestoreFunc() + defer restore() + config.UpdateGlobal(func(conf *config.Config) { + conf.Performance.EnableStatsCacheMemQuota = true + }) + store, dom := testkit.CreateMockStoreAndDomain(t) + dom.StatsHandle().SetLease(0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_analyze_version = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("analyze table test.t") + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.Nil(t, err) + tblStats := domain.GetDomain(tk.Session()).StatsHandle().GetTableStats(tbl.Meta()) + for _, col := range tblStats.Columns { + require.True(t, col.IsStatsInitialized()) + } + + domain.GetDomain(tk.Session()).StatsHandle().SetStatsCacheCapacity(1) + tblStats = domain.GetDomain(tk.Session()).StatsHandle().GetTableStats(tbl.Meta()) + for _, col := range tblStats.Columns { + require.True(t, col.IsStatsInitialized()) + } +} + +func TestUninitializedStatsStatus(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + dom.StatsHandle().SetLease(0) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b int, c int, index idx_a(a))") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1,2,2), (3,4,4), (5,6,6), (7,8,8), (9,10,10)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + is := dom.InfoSchema() + require.NoError(t, h.Update(is)) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + tblStats := h.GetTableStats(tblInfo) + for _, col := range tblStats.Columns { + require.False(t, col.IsStatsInitialized()) + } + for _, idx := range tblStats.Indices { + require.False(t, idx.IsStatsInitialized()) + } + tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Check(testkit.Rows()) + checkStatsPseudo := func() { + rows := tk.MustQuery("explain select * from t").Rows() + operatorInfo := rows[len(rows)-1][4].(string) + require.True(t, strings.Contains(operatorInfo, "stats:pseudo")) + } + tk.MustExec("set @@tidb_enable_pseudo_for_outdated_stats = true") + checkStatsPseudo() + tk.MustExec("set @@tidb_enable_pseudo_for_outdated_stats = false") + checkStatsPseudo() +} + +func TestIssue39336(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(` +create table t1 ( + a datetime(3) default null, + b int +) partition by range (b) ( + partition p0 values less than (1000), + partition p1 values less than (maxvalue) +)`) + tk.MustExec("set @@sql_mode=''") + tk.MustExec("set @@tidb_analyze_version=2") + tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") + tk.MustExec(` +insert into t1 values +('1000-00-09 00:00:00.000', 1), +('1000-00-06 00:00:00.000', 1), +('1000-00-06 00:00:00.000', 1), +('2022-11-23 14:24:30.000', 1), +('2022-11-23 14:24:32.000', 1), +('2022-11-23 14:24:33.000', 1), +('2022-11-23 14:24:35.000', 1), +('2022-11-23 14:25:08.000', 1001), +('2022-11-23 14:25:09.000', 1001)`) + tk.MustExec("analyze table t1 with 0 topn") + rows := tk.MustQuery("show analyze status where job_info like 'merge global stats%'").Rows() + require.Len(t, rows, 1) + require.Equal(t, "finished", rows[0][7]) +} + +func checkAllEvicted(t *testing.T, statsTbl *statistics.Table) { + for _, col := range statsTbl.Columns { + require.True(t, col.IsAllEvicted()) + } + for _, idx := range statsTbl.Indices { + require.True(t, idx.IsAllEvicted()) + } +} + +func TestInitStatsLite(t *testing.T) { + oriVal := config.GetGlobalConfig().Performance.LiteInitStats + config.GetGlobalConfig().Performance.LiteInitStats = true + defer func() { + config.GetGlobalConfig().Performance.LiteInitStats = oriVal + }() + + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c int, primary key(a), key idxb(b), key idxc(c))") + tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,6,6),(7,7,7),(8,8,8),(9,9,9)") + + h := dom.StatsHandle() + // set lease > 0 to trigger on-demand stats load. + h.SetLease(time.Millisecond) + defer func() { + h.SetLease(0) + }() + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + colBID := tblInfo.Columns[1].ID + colCID := tblInfo.Columns[2].ID + idxBID := tblInfo.Indices[0].ID + idxCID := tblInfo.Indices[1].ID + + tk.MustExec("analyze table t with 2 topn, 2 buckets") + statsTbl0 := h.GetTableStats(tblInfo) + checkAllEvicted(t, statsTbl0) + + h.Clear() + require.NoError(t, h.InitStatsLite(is)) + statsTbl1 := h.GetTableStats(tblInfo) + checkAllEvicted(t, statsTbl1) + internal.AssertTableEqual(t, statsTbl0, statsTbl1) + + // async stats load + tk.MustExec("set @@tidb_stats_load_sync_wait = 0") + tk.MustExec("explain select * from t where b > 1") + require.NoError(t, h.LoadNeededHistograms()) + statsTbl2 := h.GetTableStats(tblInfo) + colBStats1 := statsTbl2.Columns[colBID] + require.True(t, colBStats1.IsFullLoad()) + idxBStats1 := statsTbl2.Indices[idxBID] + require.True(t, idxBStats1.IsFullLoad()) + + // sync stats load + tk.MustExec("set @@tidb_stats_load_sync_wait = 60000") + tk.MustExec("explain select * from t where c > 1") + statsTbl3 := h.GetTableStats(tblInfo) + colCStats1 := statsTbl3.Columns[colCID] + require.True(t, colCStats1.IsFullLoad()) + idxCStats1 := statsTbl3.Indices[idxCID] + require.True(t, idxCStats1.IsFullLoad()) + + // update stats + tk.MustExec("analyze table t with 1 topn, 3 buckets") + statsTbl4 := h.GetTableStats(tblInfo) + colBStats2 := statsTbl4.Columns[colBID] + require.True(t, colBStats2.IsFullLoad()) + require.Greater(t, colBStats2.LastUpdateVersion, colBStats1.LastUpdateVersion) + idxBStats2 := statsTbl4.Indices[idxBID] + require.True(t, idxBStats2.IsFullLoad()) + require.Greater(t, idxBStats2.LastUpdateVersion, idxBStats1.LastUpdateVersion) + colCStats2 := statsTbl4.Columns[colCID] + require.True(t, colCStats2.IsFullLoad()) + require.Greater(t, colCStats2.LastUpdateVersion, colCStats1.LastUpdateVersion) + idxCStats2 := statsTbl4.Indices[idxCID] + require.True(t, idxCStats2.IsFullLoad()) + require.Greater(t, idxCStats2.LastUpdateVersion, idxCStats1.LastUpdateVersion) +} + +func TestSkipMissingPartitionStats(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("set @@tidb_skip_missing_partition_stats = 1") + tk.MustExec("create table t (a int, b int, c int, index idx_b(b)) partition by range (a) (partition p0 values less than (100), partition p1 values less than (200), partition p2 values less than (300))") + tk.MustExec("insert into t values (1,1,1), (2,2,2), (101,101,101), (102,102,102), (201,201,201), (202,202,202)") + h := dom.StatsHandle() + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t partition p0, p1") + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + globalStats := h.GetTableStats(tblInfo) + require.Equal(t, 6, int(globalStats.RealtimeCount)) + require.Equal(t, 2, int(globalStats.ModifyCount)) + for _, col := range globalStats.Columns { + require.True(t, col.IsStatsInitialized()) + } + for _, idx := range globalStats.Indices { + require.True(t, idx.IsStatsInitialized()) + } +} diff --git a/pkg/statistics/handle/handletest/lockstats/BUILD.bazel b/pkg/statistics/handle/handletest/lockstats/BUILD.bazel new file mode 100644 index 0000000000000..d5b0aaa559a1f --- /dev/null +++ b/pkg/statistics/handle/handletest/lockstats/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "lockstats_test", + timeout = "short", + srcs = [ + "lock_partition_stats_test.go", + "lock_table_stats_test.go", + "main_test.go", + ], + flaky = True, + shard_count = 21, + deps = [ + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/statistics/handle/handletest/lockstats/lock_partition_stats_test.go b/pkg/statistics/handle/handletest/lockstats/lock_partition_stats_test.go similarity index 99% rename from statistics/handle/handletest/lockstats/lock_partition_stats_test.go rename to pkg/statistics/handle/handletest/lockstats/lock_partition_stats_test.go index 93dac6490bee9..fbc176e151eaa 100644 --- a/statistics/handle/handletest/lockstats/lock_partition_stats_test.go +++ b/pkg/statistics/handle/handletest/lockstats/lock_partition_stats_test.go @@ -20,10 +20,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/statistics/handle/handletest/lockstats/lock_table_stats_test.go b/pkg/statistics/handle/handletest/lockstats/lock_table_stats_test.go similarity index 98% rename from statistics/handle/handletest/lockstats/lock_table_stats_test.go rename to pkg/statistics/handle/handletest/lockstats/lock_table_stats_test.go index e150f74af1b1c..762a7d44c6696 100644 --- a/statistics/handle/handletest/lockstats/lock_table_stats_test.go +++ b/pkg/statistics/handle/handletest/lockstats/lock_table_stats_test.go @@ -20,11 +20,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/handle/handletest/lockstats/main_test.go b/pkg/statistics/handle/handletest/lockstats/main_test.go new file mode 100644 index 0000000000000..32a94995f02d3 --- /dev/null +++ b/pkg/statistics/handle/handletest/lockstats/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package lockstats + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/handletest/main_test.go b/pkg/statistics/handle/handletest/main_test.go new file mode 100644 index 0000000000000..8937f9012b800 --- /dev/null +++ b/pkg/statistics/handle/handletest/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package handletest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/handletest/statstest/BUILD.bazel b/pkg/statistics/handle/handletest/statstest/BUILD.bazel new file mode 100644 index 0000000000000..9f74a3d9982a6 --- /dev/null +++ b/pkg/statistics/handle/handletest/statstest/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "statstest_test", + timeout = "short", + srcs = [ + "main_test.go", + "stats_test.go", + ], + flaky = True, + race = "on", + shard_count = 8, + deps = [ + "//pkg/config", + "//pkg/parser/model", + "//pkg/statistics/handle/internal", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/statistics/handle/handletest/statstest/main_test.go b/pkg/statistics/handle/handletest/statstest/main_test.go new file mode 100644 index 0000000000000..ff45bb491696a --- /dev/null +++ b/pkg/statistics/handle/handletest/statstest/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package statstest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/handletest/statstest/stats_test.go b/pkg/statistics/handle/handletest/statstest/stats_test.go new file mode 100644 index 0000000000000..caa0167589e2f --- /dev/null +++ b/pkg/statistics/handle/handletest/statstest/stats_test.go @@ -0,0 +1,290 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package statstest + +import ( + "fmt" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics/handle/internal" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestStatsCache(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + testKit.MustExec("insert into t values(1, 2)") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.True(t, statsTbl.Pseudo) + testKit.MustExec("analyze table t") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + testKit.MustExec("create index idx_t on t(c1)") + do.InfoSchema() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + // If index is build, but stats is not updated. statsTbl can also work. + require.False(t, statsTbl.Pseudo) + // But the added index will not work. + require.Nil(t, statsTbl.Indices[int64(1)]) + + testKit.MustExec("analyze table t") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + // If the new schema drop a column, the table stats can still work. + testKit.MustExec("alter table t drop column c2") + is = do.InfoSchema() + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) + + // If the new schema add a column, the table stats can still work. + testKit.MustExec("alter table t add column c10 int") + is = do.InfoSchema() + + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) +} + +func TestStatsCacheMemTracker(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int,c3 int)") + testKit.MustExec("insert into t values(1, 2, 3)") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + statsTbl := do.StatsHandle().GetTableStats(tableInfo) + require.True(t, statsTbl.MemoryUsage().TotalMemUsage == 0) + require.True(t, statsTbl.Pseudo) + + testKit.MustExec("analyze table t") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + + require.False(t, statsTbl.Pseudo) + testKit.MustExec("create index idx_t on t(c1)") + do.InfoSchema() + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + + // If index is build, but stats is not updated. statsTbl can also work. + require.False(t, statsTbl.Pseudo) + // But the added index will not work. + require.Nil(t, statsTbl.Indices[int64(1)]) + + testKit.MustExec("analyze table t") + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + + require.False(t, statsTbl.Pseudo) + + // If the new schema drop a column, the table stats can still work. + testKit.MustExec("alter table t drop column c2") + is = do.InfoSchema() + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.True(t, statsTbl.MemoryUsage().TotalMemUsage > 0) + require.False(t, statsTbl.Pseudo) + + // If the new schema add a column, the table stats can still work. + testKit.MustExec("alter table t add column c10 int") + is = do.InfoSchema() + + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl = do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl.Pseudo) +} + +func TestStatsStoreAndLoad(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (c1 int, c2 int)") + recordCount := 1000 + for i := 0; i < recordCount; i++ { + testKit.MustExec("insert into t values (?, ?)", i, i+1) + } + testKit.MustExec("create index idx_t on t(c2)") + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + + testKit.MustExec("analyze table t") + statsTbl1 := do.StatsHandle().GetTableStats(tableInfo) + + do.StatsHandle().Clear() + err = do.StatsHandle().Update(is) + require.NoError(t, err) + statsTbl2 := do.StatsHandle().GetTableStats(tableInfo) + require.False(t, statsTbl2.Pseudo) + require.Equal(t, int64(recordCount), statsTbl2.RealtimeCount) + internal.AssertTableEqual(t, statsTbl1, statsTbl2) +} + +func testInitStatsMemTrace(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int, c int, primary key(a), key idx(b))") + tk.MustExec("insert into t1 values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,7,8)") + tk.MustExec("analyze table t1") + for i := 2; i < 10; i++ { + tk.MustExec(fmt.Sprintf("create table t%v (a int, b int, c int, primary key(a), key idx(b))", i)) + tk.MustExec(fmt.Sprintf("insert into t%v select * from t1", i)) + tk.MustExec(fmt.Sprintf("analyze table t%v", i)) + } + h := dom.StatsHandle() + is := dom.InfoSchema() + h.Clear() + require.Equal(t, h.MemConsumed(), int64(0)) + require.NoError(t, h.InitStats(is)) + + var memCostTot int64 + for i := 1; i < 10; i++ { + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr(fmt.Sprintf("t%v", i))) + require.NoError(t, err) + tStats := h.GetTableStats(tbl.Meta()) + memCostTot += tStats.MemoryUsage().TotalMemUsage + } + + require.Equal(t, h.MemConsumed(), memCostTot) +} + +func TestInitStatsMemTraceWithLite(t *testing.T) { + testInitStatsMemTraceFunc(t, true) +} + +func TestInitStatsMemTraceWithoutLite(t *testing.T) { + testInitStatsMemTraceFunc(t, false) +} + +func testInitStatsMemTraceFunc(t *testing.T, liteInitStats bool) { + originValue := config.GetGlobalConfig().Performance.LiteInitStats + defer func() { + config.GetGlobalConfig().Performance.LiteInitStats = originValue + }() + config.GetGlobalConfig().Performance.LiteInitStats = liteInitStats + testInitStatsMemTrace(t) +} + +func TestInitStats(t *testing.T) { + originValue := config.GetGlobalConfig().Performance.LiteInitStats + defer func() { + config.GetGlobalConfig().Performance.LiteInitStats = originValue + }() + config.GetGlobalConfig().Performance.LiteInitStats = false + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set @@session.tidb_analyze_version = 1") + testKit.MustExec("create table t(a int, b int, c int, primary key(a), key idx(b))") + testKit.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,7,8)") + testKit.MustExec("analyze table t") + h := dom.StatsHandle() + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + // `Update` will not use load by need strategy when `Lease` is 0, and `InitStats` is only called when + // `Lease` is not 0, so here we just change it. + h.SetLease(time.Millisecond) + + h.Clear() + require.NoError(t, h.InitStats(is)) + table0 := h.GetTableStats(tbl.Meta()) + cols := table0.Columns + require.Equal(t, uint8(0x36), cols[1].LastAnalyzePos.GetBytes()[0]) + require.Equal(t, uint8(0x37), cols[2].LastAnalyzePos.GetBytes()[0]) + require.Equal(t, uint8(0x38), cols[3].LastAnalyzePos.GetBytes()[0]) + h.Clear() + require.NoError(t, h.Update(is)) + table1 := h.GetTableStats(tbl.Meta()) + internal.AssertTableEqual(t, table0, table1) + h.SetLease(0) +} + +func TestInitStatsVer2(t *testing.T) { + originValue := config.GetGlobalConfig().Performance.LiteInitStats + defer func() { + config.GetGlobalConfig().Performance.LiteInitStats = originValue + }() + config.GetGlobalConfig().Performance.LiteInitStats = false + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("create table t(a int, b int, c int, index idx(a), index idxab(a, b))") + tk.MustExec("insert into t values(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (4, 4, 4), (4, 4, 4)") + tk.MustExec("analyze table t with 2 topn, 3 buckets") + h := dom.StatsHandle() + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + // `Update` will not use load by need strategy when `Lease` is 0, and `InitStats` is only called when + // `Lease` is not 0, so here we just change it. + h.SetLease(time.Millisecond) + + h.Clear() + require.NoError(t, h.InitStats(is)) + table0 := h.GetTableStats(tbl.Meta()) + cols := table0.Columns + require.Equal(t, uint8(0x33), cols[1].LastAnalyzePos.GetBytes()[0]) + require.Equal(t, uint8(0x33), cols[2].LastAnalyzePos.GetBytes()[0]) + require.Equal(t, uint8(0x33), cols[3].LastAnalyzePos.GetBytes()[0]) + h.Clear() + require.NoError(t, h.InitStats(is)) + table1 := h.GetTableStats(tbl.Meta()) + internal.AssertTableEqual(t, table0, table1) + h.SetLease(0) +} + +func TestInitStatsIssue41938(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set @@global.tidb_analyze_version=1") + tk.MustExec("set @@session.tidb_analyze_version=1") + tk.MustExec("create table t1 (a timestamp primary key)") + tk.MustExec("insert into t1 values ('2023-03-07 14:24:30'), ('2023-03-07 14:24:31'), ('2023-03-07 14:24:32'), ('2023-03-07 14:24:33')") + tk.MustExec("analyze table t1 with 0 topn") + h := dom.StatsHandle() + // `InitStats` is only called when `Lease` is not 0, so here we just change it. + h.SetLease(time.Millisecond) + h.Clear() + require.NoError(t, h.InitStats(dom.InfoSchema())) + h.SetLease(0) +} diff --git a/pkg/statistics/handle/history/BUILD.bazel b/pkg/statistics/handle/history/BUILD.bazel new file mode 100644 index 0000000000000..a2eed563ae5ce --- /dev/null +++ b/pkg/statistics/handle/history/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "history", + srcs = ["history_stats.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/history", + visibility = ["//visibility:public"], + deps = [ + "//pkg/sessionctx", + "//pkg/statistics/handle/cache", + "//pkg/statistics/handle/storage", + "//pkg/statistics/handle/util", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) diff --git a/statistics/handle/history/history_stats.go b/pkg/statistics/handle/history/history_stats.go similarity index 94% rename from statistics/handle/history/history_stats.go rename to pkg/statistics/handle/history/history_stats.go index 6595c3624f77f..9290cb73f65f5 100644 --- a/statistics/handle/history/history_stats.go +++ b/pkg/statistics/handle/history/history_stats.go @@ -18,11 +18,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/statistics/handle/internal/BUILD.bazel b/pkg/statistics/handle/internal/BUILD.bazel new file mode 100644 index 0000000000000..60ab59d4a6312 --- /dev/null +++ b/pkg/statistics/handle/internal/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "internal", + srcs = ["testutil.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/internal", + visibility = ["//pkg/statistics/handle:__subpackages__"], + deps = [ + "//pkg/statistics", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/statistics/handle/internal/testutil.go b/pkg/statistics/handle/internal/testutil.go new file mode 100644 index 0000000000000..9757fc7fbb7b7 --- /dev/null +++ b/pkg/statistics/handle/internal/testutil.go @@ -0,0 +1,80 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package internal + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/statistics" + "github.com/stretchr/testify/require" +) + +// AssertTableEqual is to assert whether two table is equal +func AssertTableEqual(t *testing.T, a *statistics.Table, b *statistics.Table) { + require.Equal(t, b.RealtimeCount, a.RealtimeCount) + require.Equal(t, b.ModifyCount, a.ModifyCount) + require.Len(t, a.Columns, len(b.Columns)) + for i := range a.Columns { + require.True(t, statistics.HistogramEqual(&a.Columns[i].Histogram, &b.Columns[i].Histogram, false)) + if a.Columns[i].CMSketch == nil { + require.Nil(t, b.Columns[i].CMSketch) + } else { + require.True(t, a.Columns[i].CMSketch.Equal(b.Columns[i].CMSketch)) + } + // The nil case has been considered in (*TopN).Equal() so we don't need to consider it here. + require.Truef(t, a.Columns[i].TopN.Equal(b.Columns[i].TopN), "%v, %v", a.Columns[i].TopN, b.Columns[i].TopN) + } + require.Len(t, a.Indices, len(b.Indices)) + for i := range a.Indices { + require.True(t, statistics.HistogramEqual(&a.Indices[i].Histogram, &b.Indices[i].Histogram, false)) + if a.Indices[i].CMSketch == nil { + require.Nil(t, b.Indices[i].CMSketch) + } else { + require.True(t, a.Indices[i].CMSketch.Equal(b.Indices[i].CMSketch)) + } + require.True(t, a.Indices[i].TopN.Equal(b.Indices[i].TopN)) + } + require.True(t, IsSameExtendedStats(a.ExtendedStats, b.ExtendedStats)) +} + +// IsSameExtendedStats is to judge whether the extended states is the same. +func IsSameExtendedStats(a, b *statistics.ExtendedStatsColl) bool { + aEmpty := (a == nil) || len(a.Stats) == 0 + bEmpty := (b == nil) || len(b.Stats) == 0 + if (aEmpty && !bEmpty) || (!aEmpty && bEmpty) { + return false + } + if aEmpty && bEmpty { + return true + } + if len(a.Stats) != len(b.Stats) { + return false + } + for aKey, aItem := range a.Stats { + bItem, ok := b.Stats[aKey] + if !ok { + return false + } + for i, id := range aItem.ColIDs { + if id != bItem.ColIDs[i] { + return false + } + } + if (aItem.Tp != bItem.Tp) || (aItem.ScalarVals != bItem.ScalarVals) || (aItem.StringVals != bItem.StringVals) { + return false + } + } + return true +} diff --git a/pkg/statistics/handle/lockstats/BUILD.bazel b/pkg/statistics/handle/lockstats/BUILD.bazel new file mode 100644 index 0000000000000..fb53d496aba57 --- /dev/null +++ b/pkg/statistics/handle/lockstats/BUILD.bazel @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lockstats", + srcs = [ + "lock_stats.go", + "query_lock.go", + "unlock_stats.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/lockstats", + visibility = ["//visibility:public"], + deps = [ + "//pkg/sessionctx", + "//pkg/statistics/handle/cache", + "//pkg/statistics/handle/util", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "lockstats_test", + timeout = "short", + srcs = [ + "lock_stats_test.go", + "query_lock_test.go", + "unlock_stats_test.go", + ], + embed = [":lockstats"], + flaky = True, + shard_count = 13, + deps = [ + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/mock", + "//pkg/util/sqlexec/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_mock//gomock", + ], +) diff --git a/statistics/handle/lockstats/lock_stats.go b/pkg/statistics/handle/lockstats/lock_stats.go similarity index 98% rename from statistics/handle/lockstats/lock_stats.go rename to pkg/statistics/handle/lockstats/lock_stats.go index d15101d0867ba..e19e5a8493308 100644 --- a/statistics/handle/lockstats/lock_stats.go +++ b/pkg/statistics/handle/lockstats/lock_stats.go @@ -19,10 +19,10 @@ import ( "slices" "strings" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/statistics/handle/lockstats/lock_stats_test.go b/pkg/statistics/handle/lockstats/lock_stats_test.go similarity index 97% rename from statistics/handle/lockstats/lock_stats_test.go rename to pkg/statistics/handle/lockstats/lock_stats_test.go index ba4315c7284da..2433ecab9e257 100644 --- a/statistics/handle/lockstats/lock_stats_test.go +++ b/pkg/statistics/handle/lockstats/lock_stats_test.go @@ -18,11 +18,11 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/statistics/handle/lockstats/query_lock.go b/pkg/statistics/handle/lockstats/query_lock.go similarity index 94% rename from statistics/handle/lockstats/query_lock.go rename to pkg/statistics/handle/lockstats/query_lock.go index 5b3b6c56ea194..f823f42d44c4b 100644 --- a/statistics/handle/lockstats/query_lock.go +++ b/pkg/statistics/handle/lockstats/query_lock.go @@ -15,8 +15,8 @@ package lockstats import ( - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/util" ) const selectSQL = "SELECT table_id FROM mysql.stats_table_locked" diff --git a/statistics/handle/lockstats/query_lock_test.go b/pkg/statistics/handle/lockstats/query_lock_test.go similarity index 92% rename from statistics/handle/lockstats/query_lock_test.go rename to pkg/statistics/handle/lockstats/query_lock_test.go index eac173b4e6bfb..799283e156385 100644 --- a/statistics/handle/lockstats/query_lock_test.go +++ b/pkg/statistics/handle/lockstats/query_lock_test.go @@ -19,12 +19,12 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - statsutil "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec/mock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + statsutil "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec/mock" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" "go.uber.org/mock/gomock" diff --git a/statistics/handle/lockstats/unlock_stats.go b/pkg/statistics/handle/lockstats/unlock_stats.go similarity index 97% rename from statistics/handle/lockstats/unlock_stats.go rename to pkg/statistics/handle/lockstats/unlock_stats.go index ee3caccbeddfd..56bf40aa8f6ec 100644 --- a/statistics/handle/lockstats/unlock_stats.go +++ b/pkg/statistics/handle/lockstats/unlock_stats.go @@ -16,9 +16,9 @@ package lockstats import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/util" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/util" "go.uber.org/zap" ) diff --git a/statistics/handle/lockstats/unlock_stats_test.go b/pkg/statistics/handle/lockstats/unlock_stats_test.go similarity index 96% rename from statistics/handle/lockstats/unlock_stats_test.go rename to pkg/statistics/handle/lockstats/unlock_stats_test.go index 306ffa3328eb3..750e935d1d429 100644 --- a/statistics/handle/lockstats/unlock_stats_test.go +++ b/pkg/statistics/handle/lockstats/unlock_stats_test.go @@ -18,13 +18,13 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - mockctx "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/sqlexec/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + mockctx "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/sqlexec/mock" "github.com/stretchr/testify/require" "go.uber.org/mock/gomock" ) diff --git a/pkg/statistics/handle/main_test.go b/pkg/statistics/handle/main_test.go new file mode 100644 index 0000000000000..5145d6e4b1801 --- /dev/null +++ b/pkg/statistics/handle/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/metrics/BUILD.bazel b/pkg/statistics/handle/metrics/BUILD.bazel new file mode 100644 index 0000000000000..ca1dc316ff4ce --- /dev/null +++ b/pkg/statistics/handle/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/statistics/handle/metrics/metrics.go b/pkg/statistics/handle/metrics/metrics.go new file mode 100644 index 0000000000000..175fef6359df4 --- /dev/null +++ b/pkg/statistics/handle/metrics/metrics.go @@ -0,0 +1,47 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// statistics metrics vars +var ( + StatsHealthyGauges []prometheus.Gauge + + DumpHistoricalStatsSuccessCounter prometheus.Counter + DumpHistoricalStatsFailedCounter prometheus.Counter +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init statistics metrics vars. +func InitMetricsVars() { + StatsHealthyGauges = []prometheus.Gauge{ + metrics.StatsHealthyGauge.WithLabelValues("[0,50)"), + metrics.StatsHealthyGauge.WithLabelValues("[50,80)"), + metrics.StatsHealthyGauge.WithLabelValues("[80,100)"), + metrics.StatsHealthyGauge.WithLabelValues("[100,100]"), + // [0,100] should always be the last + metrics.StatsHealthyGauge.WithLabelValues("[0,100]"), + } + + DumpHistoricalStatsSuccessCounter = metrics.HistoricalStatsCounter.WithLabelValues("dump", "success") + DumpHistoricalStatsFailedCounter = metrics.HistoricalStatsCounter.WithLabelValues("dump", "fail") +} diff --git a/pkg/statistics/handle/storage/BUILD.bazel b/pkg/statistics/handle/storage/BUILD.bazel new file mode 100644 index 0000000000000..5ac1cb1ecadd5 --- /dev/null +++ b/pkg/statistics/handle/storage/BUILD.bazel @@ -0,0 +1,56 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "storage", + srcs = [ + "gc.go", + "json.go", + "read.go", + "save.go", + "update.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/storage", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/infoschema", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/cache", + "//pkg/statistics/handle/lockstats", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/compress", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/sqlexec", + "@com_github_klauspost_compress//gzip", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "storage_test", + timeout = "short", + srcs = ["read_test.go"], + flaky = True, + deps = [ + "//pkg/parser/model", + "//pkg/planner/cardinality", + "//pkg/testkit", + "//pkg/types", + "@com_github_stretchr_testify//require", + ], +) diff --git a/statistics/handle/storage/gc.go b/pkg/statistics/handle/storage/gc.go similarity index 96% rename from statistics/handle/storage/gc.go rename to pkg/statistics/handle/storage/gc.go index fadc0f36d5656..a407b3db0de6e 100644 --- a/statistics/handle/storage/gc.go +++ b/pkg/statistics/handle/storage/gc.go @@ -22,17 +22,17 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/lockstats" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/lockstats" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/oracle" "go.uber.org/zap" ) diff --git a/statistics/handle/storage/json.go b/pkg/statistics/handle/storage/json.go similarity index 96% rename from statistics/handle/storage/json.go rename to pkg/statistics/handle/storage/json.go index da8bc786f0873..f2c8c2cbf65f2 100644 --- a/statistics/handle/storage/json.go +++ b/pkg/statistics/handle/storage/json.go @@ -22,15 +22,15 @@ import ( "github.com/klauspost/compress/gzip" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - compressutil "github.com/pingcap/tidb/util/compress" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + compressutil "github.com/pingcap/tidb/pkg/util/compress" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" ) diff --git a/statistics/handle/storage/read.go b/pkg/statistics/handle/storage/read.go similarity index 97% rename from statistics/handle/storage/read.go rename to pkg/statistics/handle/storage/read.go index d695bac8f3f90..6c1e4318dc21d 100644 --- a/statistics/handle/storage/read.go +++ b/pkg/statistics/handle/storage/read.go @@ -23,20 +23,20 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/statistics/handle/storage/read_test.go b/pkg/statistics/handle/storage/read_test.go similarity index 96% rename from statistics/handle/storage/read_test.go rename to pkg/statistics/handle/storage/read_test.go index 0ba828cb18db7..f67fad1de7772 100644 --- a/statistics/handle/storage/read_test.go +++ b/pkg/statistics/handle/storage/read_test.go @@ -17,10 +17,10 @@ package storage_test import ( "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/statistics/handle/storage/save.go b/pkg/statistics/handle/storage/save.go similarity index 97% rename from statistics/handle/storage/save.go rename to pkg/statistics/handle/storage/save.go index 9ae757f62ddab..8ff6660728028 100644 --- a/statistics/handle/storage/save.go +++ b/pkg/statistics/handle/storage/save.go @@ -20,16 +20,16 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" "go.uber.org/zap" ) diff --git a/pkg/statistics/handle/storage/update.go b/pkg/statistics/handle/storage/update.go new file mode 100644 index 0000000000000..50ca730eeeb2c --- /dev/null +++ b/pkg/statistics/handle/storage/update.go @@ -0,0 +1,220 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package storage + +import ( + "encoding/json" + "fmt" + "slices" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/cache" + statsutil "github.com/pingcap/tidb/pkg/statistics/handle/util" +) + +// UpdateStatsVersion will set statistics version to the newest TS, +// then tidb-server will reload automatic. +func UpdateStatsVersion(sctx sessionctx.Context) error { + startTS, err := statsutil.GetStartTS(sctx) + if err != nil { + return errors.Trace(err) + } + if _, err = statsutil.Exec(sctx, "update mysql.stats_meta set version = %?", startTS); err != nil { + return err + } + if _, err = statsutil.Exec(sctx, "update mysql.stats_extended set version = %?", startTS); err != nil { + return err + } + if _, err = statsutil.Exec(sctx, "update mysql.stats_histograms set version = %?", startTS); err != nil { + return err + } + return nil +} + +// UpdateStatsMeta update the stats meta stat for this Table. +func UpdateStatsMeta( + sctx sessionctx.Context, + startTS uint64, + delta variable.TableDelta, + id int64, + isLocked bool, +) (err error) { + if isLocked { + // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_table_locked. + // Note: For locked tables, it is possible that the record gets deleted. So it can be negative. + _, err = statsutil.Exec(sctx, "insert into mysql.stats_table_locked (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+ + "update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", + startTS, id, delta.Count, delta.Delta) + } else { + if delta.Delta < 0 { + // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. + _, err = statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, 0) on duplicate key "+ + "update version = values(version), modify_count = modify_count + values(modify_count), count = if(count > %?, count - %?, 0)", + startTS, id, delta.Count, -delta.Delta, -delta.Delta) + } else { + // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. + _, err = statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+ + "update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", startTS, + id, delta.Count, delta.Delta) + } + cache.TableRowStatsCache.Invalidate(id) + } + return err +} + +// DumpTableStatColSizeToKV dumps the column size stats to storage. +func DumpTableStatColSizeToKV(sctx sessionctx.Context, id int64, delta variable.TableDelta) error { + if len(delta.ColSize) == 0 { + return nil + } + values := make([]string, 0, len(delta.ColSize)) + for histID, deltaColSize := range delta.ColSize { + if deltaColSize == 0 { + continue + } + values = append(values, fmt.Sprintf("(%d, 0, %d, 0, %d)", id, histID, deltaColSize)) + } + if len(values) == 0 { + return nil + } + sql := fmt.Sprintf("insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, tot_col_size) "+ + "values %s on duplicate key update tot_col_size = tot_col_size + values(tot_col_size)", strings.Join(values, ",")) + _, _, err := statsutil.ExecRows(sctx, sql) + return errors.Trace(err) +} + +// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta. +func InsertExtendedStats(sctx sessionctx.Context, + statsCache statsutil.StatsCache, + statsName string, colIDs []int64, tp int, tableID int64, ifNotExists bool) (statsVer uint64, err error) { + slices.Sort(colIDs) + bytes, err := json.Marshal(colIDs) + if err != nil { + return 0, errors.Trace(err) + } + strColIDs := string(bytes) + + _, err = statsutil.Exec(sctx, "begin pessimistic") + if err != nil { + return 0, errors.Trace(err) + } + defer func() { + err = statsutil.FinishTransaction(sctx, err) + }() + // No need to use `exec.ExecuteInternal` since we have acquired the lock. + rows, _, err := statsutil.ExecRows(sctx, "SELECT name, type, column_ids FROM mysql.stats_extended WHERE table_id = %? and status in (%?, %?)", tableID, statistics.ExtendedStatsInited, statistics.ExtendedStatsAnalyzed) + if err != nil { + return 0, errors.Trace(err) + } + for _, row := range rows { + currStatsName := row.GetString(0) + currTp := row.GetInt64(1) + currStrColIDs := row.GetString(2) + if currStatsName == statsName { + if ifNotExists { + return 0, nil + } + return 0, errors.Errorf("extended statistics '%s' for the specified table already exists", statsName) + } + if tp == int(currTp) && currStrColIDs == strColIDs { + return 0, errors.Errorf("extended statistics '%s' with same type on same columns already exists", statsName) + } + } + version, err := statsutil.GetStartTS(sctx) + if err != nil { + return 0, errors.Trace(err) + } + // Bump version in `mysql.stats_meta` to trigger stats cache refresh. + if _, err = statsutil.Exec(sctx, "UPDATE mysql.stats_meta SET version = %? WHERE table_id = %?", version, tableID); err != nil { + return 0, err + } + statsVer = version + // Remove the existing 'deleted' records. + if _, err = statsutil.Exec(sctx, "DELETE FROM mysql.stats_extended WHERE name = %? and table_id = %?", statsName, tableID); err != nil { + return 0, err + } + // Remove the cache item, which is necessary for cases like a cluster with 3 tidb instances, e.g, a, b and c. + // If tidb-a executes `alter table drop stats_extended` to mark the record as 'deleted', and before this operation + // is synchronized to other tidb instances, tidb-b executes `alter table add stats_extended`, which would delete + // the record from the table, tidb-b should delete the cached item synchronously. While for tidb-c, it has to wait for + // next `Update()` to remove the cached item then. + removeExtendedStatsItem(statsCache, tableID, statsName) + const sql = "INSERT INTO mysql.stats_extended(name, type, table_id, column_ids, version, status) VALUES (%?, %?, %?, %?, %?, %?)" + if _, err = statsutil.Exec(sctx, sql, statsName, tp, tableID, strColIDs, version, statistics.ExtendedStatsInited); err != nil { + return 0, err + } + return +} + +// SaveExtendedStatsToStorage writes extended stats of a table into mysql.stats_extended. +func SaveExtendedStatsToStorage(sctx sessionctx.Context, + tableID int64, extStats *statistics.ExtendedStatsColl, isLoad bool) (statsVer uint64, err error) { + if extStats == nil || len(extStats.Stats) == 0 { + return 0, nil + } + + _, err = statsutil.Exec(sctx, "begin pessimistic") + if err != nil { + return 0, errors.Trace(err) + } + defer func() { + err = statsutil.FinishTransaction(sctx, err) + }() + version, err := statsutil.GetStartTS(sctx) + if err != nil { + return 0, errors.Trace(err) + } + for name, item := range extStats.Stats { + bytes, err := json.Marshal(item.ColIDs) + if err != nil { + return 0, errors.Trace(err) + } + strColIDs := string(bytes) + var statsStr string + switch item.Tp { + case ast.StatsTypeCardinality, ast.StatsTypeCorrelation: + statsStr = fmt.Sprintf("%f", item.ScalarVals) + case ast.StatsTypeDependency: + statsStr = item.StringVals + } + // If isLoad is true, it's INSERT; otherwise, it's UPDATE. + if _, err := statsutil.Exec(sctx, "replace into mysql.stats_extended values (%?, %?, %?, %?, %?, %?, %?)", name, item.Tp, tableID, strColIDs, statsStr, version, statistics.ExtendedStatsAnalyzed); err != nil { + return 0, err + } + } + if !isLoad { + if _, err := statsutil.Exec(sctx, "UPDATE mysql.stats_meta SET version = %? WHERE table_id = %?", version, tableID); err != nil { + return 0, err + } + statsVer = version + } + return statsVer, nil +} + +func removeExtendedStatsItem(statsCache statsutil.StatsCache, + tableID int64, statsName string) { + tbl, ok := statsCache.Get(tableID) + if !ok || tbl.ExtendedStats == nil || len(tbl.ExtendedStats.Stats) == 0 { + return + } + newTbl := tbl.Copy() + delete(newTbl.ExtendedStats.Stats, statsName) + statsCache.UpdateStatsCache([]*statistics.Table{newTbl}, nil) +} diff --git a/pkg/statistics/handle/update.go b/pkg/statistics/handle/update.go new file mode 100644 index 0000000000000..c83c33ea1661b --- /dev/null +++ b/pkg/statistics/handle/update.go @@ -0,0 +1,58 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package handle + +import ( + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + utilstats "github.com/pingcap/tidb/pkg/statistics/handle/util" +) + +func (h *Handle) callWithSCtx(f func(sctx sessionctx.Context) error, flags ...int) (err error) { + return utilstats.CallWithSCtx(h.pool, f, flags...) +} + +const ( + // StatsOwnerKey is the stats owner path that is saved to etcd. + StatsOwnerKey = "/tidb/stats/owner" + // StatsPrompt is the prompt for stats owner manager. + StatsPrompt = "stats" +) + +// HandleAutoAnalyze analyzes the newly created table or index. +func (h *Handle) HandleAutoAnalyze(is infoschema.InfoSchema) (analyzed bool) { + _ = h.callWithSCtx(func(sctx sessionctx.Context) error { + analyzed = autoanalyze.HandleAutoAnalyze(sctx, &autoanalyze.Opt{ + StatsLease: h.Lease(), + GetLockedTables: h.GetLockedTables, + GetTableStats: h.GetTableStats, + GetPartitionStats: h.GetPartitionStats, + SysProcTracker: h.sysProcTracker, + AutoAnalyzeProcIDGetter: h.autoAnalyzeProcIDGetter, + }, is) + return nil + }) + return +} + +// GetCurrentPruneMode returns the current latest partitioning table prune mode. +func (h *Handle) GetCurrentPruneMode() (mode string, err error) { + err = h.callWithSCtx(func(sctx sessionctx.Context) error { + mode = sctx.GetSessionVars().PartitionPruneMode.Load() + return nil + }) + return +} diff --git a/pkg/statistics/handle/updatetest/BUILD.bazel b/pkg/statistics/handle/updatetest/BUILD.bazel new file mode 100644 index 0000000000000..6b08edca6b991 --- /dev/null +++ b/pkg/statistics/handle/updatetest/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "updatetest_test", + timeout = "short", + srcs = [ + "main_test.go", + "update_test.go", + ], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/cardinality", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/autoanalyze", + "//pkg/statistics/handle/usage", + "//pkg/statistics/handle/util", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/ranger", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/statistics/handle/updatetest/main_test.go b/pkg/statistics/handle/updatetest/main_test.go new file mode 100644 index 0000000000000..c90aded5dfabd --- /dev/null +++ b/pkg/statistics/handle/updatetest/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package updatetest + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/statistics/handle/updatetest/update_test.go b/pkg/statistics/handle/updatetest/update_test.go new file mode 100644 index 0000000000000..01ef3342f6aea --- /dev/null +++ b/pkg/statistics/handle/updatetest/update_test.go @@ -0,0 +1,1310 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package updatetest + +import ( + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/cardinality" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/statistics/handle/usage" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" +) + +func TestSingleSessionInsert(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set @@session.tidb_analyze_version = 1") + testKit.MustExec("create table t1 (c1 int, c2 int)") + testKit.MustExec("create table t2 (c1 int, c2 int)") + + rowCount1 := 10 + rowCount2 := 20 + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + for i := 0; i < rowCount2; i++ { + testKit.MustExec("insert into t2 values(1, 2)") + } + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + h := dom.StatsHandle() + + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 := h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tableInfo2 := tbl2.Meta() + stats2 := h.GetTableStats(tableInfo2) + require.Equal(t, int64(rowCount2), stats2.RealtimeCount) + + testKit.MustExec("analyze table t1") + // Test update in a txn. + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1*2), stats1.RealtimeCount) + + // Test IncreaseFactor. + count, err := cardinality.ColumnEqualRowCount(testKit.Session(), stats1, types.NewIntDatum(1), tableInfo1.Columns[0].ID) + require.NoError(t, err) + require.Equal(t, float64(rowCount1*2), count) + + testKit.MustExec("begin") + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + testKit.MustExec("commit") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1*3), stats1.RealtimeCount) + + testKit.MustExec("begin") + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + for i := 0; i < rowCount1; i++ { + testKit.MustExec("delete from t1 limit 1") + } + for i := 0; i < rowCount2; i++ { + testKit.MustExec("update t2 set c2 = c1") + } + testKit.MustExec("commit") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1*3), stats1.RealtimeCount) + stats2 = h.GetTableStats(tableInfo2) + require.Equal(t, int64(rowCount2), stats2.RealtimeCount) + + testKit.MustExec("begin") + testKit.MustExec("delete from t1") + testKit.MustExec("commit") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(0), stats1.RealtimeCount) + + rs := testKit.MustQuery("select modify_count from mysql.stats_meta") + rs.Check(testkit.Rows("40", "70")) + + rs = testKit.MustQuery("select tot_col_size from mysql.stats_histograms").Sort() + rs.Check(testkit.Rows("0", "0", "20", "20")) + + // test dump delta only when `modify count / count` is greater than the ratio. + originValue := usage.DumpStatsDeltaRatio + usage.DumpStatsDeltaRatio = 0.5 + defer func() { + usage.DumpStatsDeltaRatio = originValue + }() + usage.DumpStatsDeltaRatio = 0.5 + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values (1,2)") + } + err = h.DumpStatsDeltaToKV(false) + require.NoError(t, err) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + // not dumped + testKit.MustExec("insert into t1 values (1,2)") + err = h.DumpStatsDeltaToKV(false) + require.NoError(t, err) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + h.FlushStats() + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1+1), stats1.RealtimeCount) +} + +func TestRollback(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (a int, b int)") + testKit.MustExec("begin") + testKit.MustExec("insert into t values (1,2)") + testKit.MustExec("rollback") + + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := dom.StatsHandle() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + + stats := h.GetTableStats(tableInfo) + require.Equal(t, int64(0), stats.RealtimeCount) + require.Equal(t, int64(0), stats.ModifyCount) +} + +func TestMultiSession(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t1 (c1 int, c2 int)") + + rowCount1 := 10 + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + + testKit1 := testkit.NewTestKit(t, store) + for i := 0; i < rowCount1; i++ { + testKit1.MustExec("insert into test.t1 values(1, 2)") + } + testKit2 := testkit.NewTestKit(t, store) + for i := 0; i < rowCount1; i++ { + testKit2.MustExec("delete from test.t1 limit 1") + } + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + h := dom.StatsHandle() + + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 := h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + + for i := 0; i < rowCount1; i++ { + testKit1.MustExec("insert into test.t1 values(1, 2)") + } + + for i := 0; i < rowCount1; i++ { + testKit2.MustExec("delete from test.t1 limit 1") + } + + testKit.Session().Close() + testKit2.Session().Close() + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1*2), stats1.RealtimeCount) + testKit.RefreshSession() + rs := testKit.MustQuery("select modify_count from mysql.stats_meta") + rs.Check(testkit.Rows("60")) +} + +func TestTxnWithFailure(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t1 (c1 int primary key, c2 int)") + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + h := dom.StatsHandle() + + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + + rowCount1 := 10 + testKit.MustExec("begin") + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(?, 2)", i) + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 := h.GetTableStats(tableInfo1) + // have not commit + require.Equal(t, int64(0), stats1.RealtimeCount) + testKit.MustExec("commit") + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + _, err = testKit.Exec("insert into t1 values(0, 2)") + require.Error(t, err) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1), stats1.RealtimeCount) + + testKit.MustExec("insert into t1 values(-1, 2)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(rowCount1+1), stats1.RealtimeCount) +} + +func TestUpdatePartition(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + pruneMode, err := dom.StatsHandle().GetCurrentPruneMode() + require.NoError(t, err) + testKit.MustQuery("select @@tidb_partition_prune_mode").Check(testkit.Rows(pruneMode)) + testKit.MustExec("use test") + testkit.WithPruneMode(testKit, variable.Static, func() { + require.NoError(t, err) + testKit.MustExec("drop table if exists t") + createTable := `CREATE TABLE t (a int, b char(5)) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11))` + testKit.MustExec(createTable) + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := do.StatsHandle() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + pi := tableInfo.GetPartitionInfo() + require.Len(t, pi.Definitions, 2) + bColID := tableInfo.Columns[1].ID + + testKit.MustExec(`insert into t values (1, "a"), (7, "a")`) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.Equal(t, int64(1), statsTbl.ModifyCount) + require.Equal(t, int64(1), statsTbl.RealtimeCount) + require.Equal(t, int64(2), statsTbl.Columns[bColID].TotColSize) + } + + testKit.MustExec(`update t set a = a + 1, b = "aa"`) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.Equal(t, int64(2), statsTbl.ModifyCount) + require.Equal(t, int64(1), statsTbl.RealtimeCount) + require.Equal(t, int64(3), statsTbl.Columns[bColID].TotColSize) + } + + testKit.MustExec("delete from t") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.Equal(t, int64(3), statsTbl.ModifyCount) + require.Equal(t, int64(0), statsTbl.RealtimeCount) + require.Equal(t, int64(0), statsTbl.Columns[bColID].TotColSize) + } + // assert WithGetTableStatsByQuery get the same result + for _, def := range pi.Definitions { + statsTbl := h.GetPartitionStats(tableInfo, def.ID) + require.Equal(t, int64(3), statsTbl.ModifyCount) + require.Equal(t, int64(0), statsTbl.RealtimeCount) + require.Equal(t, int64(0), statsTbl.Columns[bColID].TotColSize) + } + }) +} + +func TestAutoUpdate(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testkit.WithPruneMode(testKit, variable.Static, func() { + testKit.MustExec("use test") + testKit.MustExec("create table t (a varchar(20))") + + autoanalyze.AutoAnalyzeMinCnt = 0 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.2") + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") + }() + + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := do.StatsHandle() + + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + require.NoError(t, h.Update(is)) + stats := h.GetTableStats(tableInfo) + require.Equal(t, int64(0), stats.RealtimeCount) + + _, err = testKit.Exec("insert into t values ('ss'), ('ss'), ('ss'), ('ss'), ('ss')") + require.NoError(t, err) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + stats = h.GetTableStats(tableInfo) + require.Equal(t, int64(5), stats.RealtimeCount) + require.Equal(t, int64(0), stats.ModifyCount) + for _, item := range stats.Columns { + // TotColSize = 5*(2(length of 'ss') + 1(size of len byte)). + require.Equal(t, int64(15), item.TotColSize) + break + } + + // Test that even if the table is recently modified, we can still analyze the table. + h.SetLease(time.Second) + defer func() { h.SetLease(0) }() + _, err = testKit.Exec("insert into t values ('fff')") + require.NoError(t, err) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + stats = h.GetTableStats(tableInfo) + require.Equal(t, int64(6), stats.RealtimeCount) + require.Equal(t, int64(1), stats.ModifyCount) + + _, err = testKit.Exec("insert into t values ('fff')") + require.NoError(t, err) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + stats = h.GetTableStats(tableInfo) + require.Equal(t, int64(7), stats.RealtimeCount) + require.Equal(t, int64(0), stats.ModifyCount) + + _, err = testKit.Exec("insert into t values ('eee')") + require.NoError(t, err) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + stats = h.GetTableStats(tableInfo) + require.Equal(t, int64(8), stats.RealtimeCount) + // Modify count is non-zero means that we do not analyze the table. + require.Equal(t, int64(1), stats.ModifyCount) + for _, item := range stats.Columns { + // TotColSize = 27, because the table has not been analyzed, and insert statement will add 3(length of 'eee') to TotColSize. + require.Equal(t, int64(27), item.TotColSize) + break + } + + testKit.MustExec("analyze table t") + _, err = testKit.Exec("create index idx on t(a)") + require.NoError(t, err) + is = do.InfoSchema() + tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo = tbl.Meta() + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + testKit.MustExec("explain select * from t where a > 'a'") + require.NoError(t, h.LoadNeededHistograms()) + stats = h.GetTableStats(tableInfo) + require.Equal(t, int64(8), stats.RealtimeCount) + require.Equal(t, int64(0), stats.ModifyCount) + hg, ok := stats.Indices[tableInfo.Indices[0].ID] + require.True(t, ok) + require.Equal(t, int64(3), hg.NDV) + require.Equal(t, 0, hg.Len()) + require.Equal(t, 3, hg.TopN.Num()) + }) +} + +func TestAutoUpdatePartition(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testkit.WithPruneMode(testKit, variable.Static, func() { + testKit.MustExec("use test") + testKit.MustExec("drop table if exists t") + testKit.MustExec("create table t (a int) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (6))") + testKit.MustExec("analyze table t") + + autoanalyze.AutoAnalyzeMinCnt = 0 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.6") + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") + }() + + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + pi := tableInfo.GetPartitionInfo() + h := do.StatsHandle() + + require.NoError(t, h.Update(is)) + stats := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, int64(0), stats.RealtimeCount) + + testKit.MustExec("insert into t values (1)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + h.HandleAutoAnalyze(is) + stats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, int64(1), stats.RealtimeCount) + require.Equal(t, int64(0), stats.ModifyCount) + }) +} + +func TestIssue25700(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("CREATE TABLE `t` ( `ldecimal` decimal(32,4) DEFAULT NULL, `rdecimal` decimal(32,4) DEFAULT NULL, `gen_col` decimal(36,4) GENERATED ALWAYS AS (`ldecimal` + `rdecimal`) VIRTUAL, `col_timestamp` timestamp(3) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") + tk.MustExec("analyze table t") + tk.MustExec("INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) VALUES (2265.2200, 9843.4100, '1999-12-31 16:00:00')" + strings.Repeat(", (2265.2200, 9843.4100, '1999-12-31 16:00:00')", int(autoanalyze.AutoAnalyzeMinCnt))) + require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) + require.NoError(t, dom.StatsHandle().Update(dom.InfoSchema())) + + require.True(t, dom.StatsHandle().HandleAutoAnalyze(dom.InfoSchema())) + require.Equal(t, "finished", tk.MustQuery("show analyze status").Rows()[1][7]) +} + +func appendBucket(h *statistics.Histogram, l, r int64) { + lower, upper := types.NewIntDatum(l), types.NewIntDatum(r) + h.AppendBucket(&lower, &upper, 0, 0) +} + +func TestSplitRange(t *testing.T) { + h := statistics.NewHistogram(0, 0, 0, 0, types.NewFieldType(mysql.TypeLong), 5, 0) + appendBucket(h, 1, 1) + appendBucket(h, 2, 5) + appendBucket(h, 7, 7) + appendBucket(h, 8, 8) + appendBucket(h, 10, 13) + + tests := []struct { + points []int64 + exclude []bool + result string + }{ + { + points: []int64{1, 1}, + exclude: []bool{false, false}, + result: "[1,1]", + }, + { + points: []int64{0, 1, 3, 8, 8, 20}, + exclude: []bool{true, false, true, false, true, false}, + result: "(0,1],(3,7),[7,8),[8,8],(8,10),[10,20]", + }, + { + points: []int64{8, 10, 20, 30}, + exclude: []bool{false, false, true, true}, + result: "[8,10),[10,10],(20,30)", + }, + { + // test remove invalid range + points: []int64{8, 9}, + exclude: []bool{false, true}, + result: "[8,9)", + }, + } + for _, test := range tests { + ranges := make([]*ranger.Range, 0, len(test.points)/2) + for i := 0; i < len(test.points); i += 2 { + ranges = append(ranges, &ranger.Range{ + LowVal: []types.Datum{types.NewIntDatum(test.points[i])}, + LowExclude: test.exclude[i], + HighVal: []types.Datum{types.NewIntDatum(test.points[i+1])}, + HighExclude: test.exclude[i+1], + Collators: collate.GetBinaryCollatorSlice(1), + }) + } + ranges, _ = h.SplitRange(nil, ranges, false) + var ranStrs []string + for _, ran := range ranges { + ranStrs = append(ranStrs, ran.String()) + } + require.Equal(t, test.result, strings.Join(ranStrs, ",")) + } +} + +func TestOutOfOrderUpdate(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("create table t (a int, b int)") + testKit.MustExec("insert into t values (1,2)") + + do := dom + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + h := do.StatsHandle() + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + + // Simulate the case that another tidb has inserted some value, but delta info has not been dumped to kv yet. + testKit.MustExec("insert into t values (2,2),(4,5)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 1 where table_id = %d", tableInfo.ID)) + + testKit.MustExec("delete from t") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + // If count < -Delta, then update count to 0. + // Check https://github.com/pingcap/tidb/pull/38301#discussion_r1094050951 for details. + testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("0")) + + // Now another tidb has updated the delta info. + testKit.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 3 where table_id = %d", tableInfo.ID)) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("3")) +} + +func TestLoadHistCorrelation(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + origLease := h.Lease() + h.SetLease(time.Second) + defer func() { h.SetLease(origLease) }() + testKit.MustExec("use test") + testKit.MustExec("create table t(c int)") + testKit.MustExec("insert into t values(1),(2),(3),(4),(5)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec("analyze table t") + h.Clear() + require.NoError(t, h.Update(dom.InfoSchema())) + result := testKit.MustQuery("show stats_histograms where Table_name = 't'") + // After https://github.com/pingcap/tidb/pull/37444, `show stats_histograms` displays the columns whose hist/topn/cmsketch + // are not loaded and their stats status is allEvicted. + require.Len(t, result.Rows(), 1) + testKit.MustExec("explain select * from t where c = 1") + require.NoError(t, h.LoadNeededHistograms()) + result = testKit.MustQuery("show stats_histograms where Table_name = 't'") + require.Len(t, result.Rows(), 1) + require.Equal(t, "1", result.Rows()[0][9]) +} + +func BenchmarkHandleAutoAnalyze(b *testing.B) { + store, dom := testkit.CreateMockStoreAndDomain(b) + testKit := testkit.NewTestKit(b, store) + testKit.MustExec("use test") + h := dom.StatsHandle() + is := dom.InfoSchema() + for i := 0; i < b.N; i++ { + h.HandleAutoAnalyze(is) + } +} + +// subtraction parses the number for counter and returns new - old. +// string for counter will be `label: counter: ` +func subtraction(newMetric *dto.Metric, oldMetric *dto.Metric) int { + return int(*(newMetric.Counter.Value) - *(oldMetric.Counter.Value)) +} + +func TestMergeTopN(t *testing.T) { + // Move this test to here to avoid race test. + tests := []struct { + topnNum int + n int + maxTopNVal int + maxTopNCnt int + }{ + { + topnNum: 10, + n: 5, + maxTopNVal: 50, + maxTopNCnt: 100, + }, + { + topnNum: 1, + n: 5, + maxTopNVal: 50, + maxTopNCnt: 100, + }, + { + topnNum: 5, + n: 5, + maxTopNVal: 5, + maxTopNCnt: 100, + }, + { + topnNum: 5, + n: 5, + maxTopNVal: 10, + maxTopNCnt: 100, + }, + } + for _, test := range tests { + topnNum, n := test.topnNum, test.n + maxTopNVal, maxTopNCnt := test.maxTopNVal, test.maxTopNCnt + + // the number of maxTopNVal should be bigger than n. + ok := maxTopNVal >= n + require.Equal(t, true, ok) + + topNs := make([]*statistics.TopN, 0, topnNum) + res := make(map[int]uint64) + rand.Seed(time.Now().Unix()) + for i := 0; i < topnNum; i++ { + topN := statistics.NewTopN(n) + occur := make(map[int]bool) + for j := 0; j < n; j++ { + // The range of numbers in the topn structure is in [0, maxTopNVal) + // But there cannot be repeated occurrences of value in a topN structure. + randNum := rand.Intn(maxTopNVal) + for occur[randNum] { + randNum = rand.Intn(maxTopNVal) + } + occur[randNum] = true + tString := []byte(fmt.Sprintf("%d", randNum)) + // The range of the number of occurrences in the topn structure is in [0, maxTopNCnt) + randCnt := uint64(rand.Intn(maxTopNCnt)) + res[randNum] += randCnt + topNMeta := statistics.TopNMeta{Encoded: tString, Count: randCnt} + topN.TopN = append(topN.TopN, topNMeta) + } + topNs = append(topNs, topN) + } + topN, remainTopN := statistics.MergeTopN(topNs, uint32(n)) + cnt := len(topN.TopN) + var minTopNCnt uint64 + for _, topNMeta := range topN.TopN { + val, err := strconv.Atoi(string(topNMeta.Encoded)) + require.NoError(t, err) + require.Equal(t, res[val], topNMeta.Count) + minTopNCnt = topNMeta.Count + } + if remainTopN != nil { + cnt += len(remainTopN) + for _, remainTopNMeta := range remainTopN { + val, err := strconv.Atoi(string(remainTopNMeta.Encoded)) + require.NoError(t, err) + require.Equal(t, res[val], remainTopNMeta.Count) + // The count of value in remainTopN may equal to the min count of value in TopN. + ok = minTopNCnt >= remainTopNMeta.Count + require.Equal(t, true, ok) + } + } + require.Equal(t, len(res), cnt) + } +} + +func TestStatsVariables(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + sctx := tk.Session().(sessionctx.Context) + + pruneMode, err := h.GetCurrentPruneMode() + require.NoError(t, err) + require.Equal(t, string(variable.Dynamic), pruneMode) + err = util.UpdateSCtxVarsForStats(sctx) + require.NoError(t, err) + require.Equal(t, 2, sctx.GetSessionVars().AnalyzeVersion) + require.Equal(t, true, sctx.GetSessionVars().EnableHistoricalStats) + require.Equal(t, string(variable.Dynamic), sctx.GetSessionVars().PartitionPruneMode.Load()) + require.Equal(t, false, sctx.GetSessionVars().EnableAnalyzeSnapshot) + require.Equal(t, true, sctx.GetSessionVars().SkipMissingPartitionStats) + + tk.MustExec(`set global tidb_analyze_version=1`) + tk.MustExec(`set global tidb_partition_prune_mode='static'`) + tk.MustExec(`set global tidb_enable_historical_stats=0`) + tk.MustExec(`set global tidb_enable_analyze_snapshot=1`) + tk.MustExec(`set global tidb_skip_missing_partition_stats=0`) + + pruneMode, err = h.GetCurrentPruneMode() + require.NoError(t, err) + require.Equal(t, string(variable.Static), pruneMode) + err = util.UpdateSCtxVarsForStats(sctx) + require.NoError(t, err) + require.Equal(t, 1, sctx.GetSessionVars().AnalyzeVersion) + require.Equal(t, false, sctx.GetSessionVars().EnableHistoricalStats) + require.Equal(t, string(variable.Static), sctx.GetSessionVars().PartitionPruneMode.Load()) + require.Equal(t, true, sctx.GetSessionVars().EnableAnalyzeSnapshot) + require.Equal(t, false, sctx.GetSessionVars().SkipMissingPartitionStats) +} + +func TestAutoUpdatePartitionInDynamicOnlyMode(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testkit.WithPruneMode(testKit, variable.DynamicOnly, func() { + testKit.MustExec("use test") + testKit.MustExec("set @@tidb_analyze_version = 2;") + testKit.MustExec("drop table if exists t") + testKit.MustExec(`create table t (a int, b varchar(10), index idx_ab(a, b)) + partition by range (a) ( + partition p0 values less than (10), + partition p1 values less than (20), + partition p2 values less than (30))`) + + do := dom + is := do.InfoSchema() + h := do.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + + testKit.MustExec("insert into t values (1, 'a'), (2, 'b'), (11, 'c'), (12, 'd'), (21, 'e'), (22, 'f')") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + testKit.MustExec("set @@tidb_analyze_version = 2") + testKit.MustExec("analyze table t") + + autoanalyze.AutoAnalyzeMinCnt = 0 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.1") + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") + }() + + require.NoError(t, h.Update(is)) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tableInfo := tbl.Meta() + pi := tableInfo.GetPartitionInfo() + globalStats := h.GetTableStats(tableInfo) + partitionStats := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, int64(6), globalStats.RealtimeCount) + require.Equal(t, int64(0), globalStats.ModifyCount) + require.Equal(t, int64(2), partitionStats.RealtimeCount) + require.Equal(t, int64(0), partitionStats.ModifyCount) + + testKit.MustExec("insert into t values (3, 'g')") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + globalStats = h.GetTableStats(tableInfo) + partitionStats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, int64(7), globalStats.RealtimeCount) + require.Equal(t, int64(1), globalStats.ModifyCount) + require.Equal(t, int64(3), partitionStats.RealtimeCount) + require.Equal(t, int64(1), partitionStats.ModifyCount) + + h.HandleAutoAnalyze(is) + require.NoError(t, h.Update(is)) + globalStats = h.GetTableStats(tableInfo) + partitionStats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) + require.Equal(t, int64(7), globalStats.RealtimeCount) + require.Equal(t, int64(0), globalStats.ModifyCount) + require.Equal(t, int64(3), partitionStats.RealtimeCount) + require.Equal(t, int64(0), partitionStats.ModifyCount) + }) +} + +func TestAutoAnalyzeRatio(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + is := dom.InfoSchema() + require.NoError(t, h.Update(is)) + // To pass the stats.Pseudo check in autoAnalyzeTable + tk.MustExec("analyze table t") + tk.MustExec("explain select * from t where a = 1") + require.NoError(t, h.LoadNeededHistograms()) + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + + getStatsHealthy := func() int { + rows := tk.MustQuery("show stats_healthy where db_name = 'test' and table_name = 't'").Rows() + require.Len(t, rows, 1) + healthy, err := strconv.Atoi(rows[0][3].(string)) + require.NoError(t, err) + return healthy + } + + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 10)) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 44) + require.True(t, h.HandleAutoAnalyze(is)) + + tk.MustExec("delete from t limit 12") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 61) + require.False(t, h.HandleAutoAnalyze(is)) + + tk.MustExec("delete from t limit 4") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 48) + require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) +} + +func TestDumpColumnStatsUsage(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("create table t2(a int, b int)") + tk.MustExec("create table t3(a int, b int) partition by range(a) (partition p0 values less than (10), partition p1 values less than maxvalue)") + tk.MustExec("insert into t1 values (1, 2), (3, 4)") + tk.MustExec("insert into t2 values (5, 6), (7, 8)") + tk.MustExec("insert into t3 values (1, 2), (3, 4), (11, 12), (13, 14)") + tk.MustExec("select * from t1 where a > 1") + tk.MustExec("select * from t2 where b < 10") + require.NoError(t, h.DumpColStatsUsageToKV()) + // t1.a is collected as predicate column + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't2'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t2", "", "b"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + + tk.MustExec("analyze table t1") + tk.MustExec("select * from t1 where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + // t1.a updates last_used_at first and then updates last_analyzed_at while t1.b updates last_analyzed_at first and then updates last_used_at. + // Check both of them behave as expected. + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() + require.Len(t, rows, 2) + require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) != "") + require.Equal(t, []interface{}{"test", "t1", "", "b"}, rows[1][:4]) + require.True(t, rows[1][4].(string) != "") + require.True(t, rows[1][5].(string) != "") + + // Test partition table. + // No matter whether it is static or dynamic pruning mode, we record predicate columns using table ID rather than partition ID. + for _, val := range []string{string(variable.Static), string(variable.Dynamic)} { + tk.MustExec(fmt.Sprintf("set @@tidb_partition_prune_mode = '%v'", val)) + tk.MustExec("delete from mysql.column_stats_usage") + tk.MustExec("select * from t3 where a < 5") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't3'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t3", "global", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + } + + // Test non-correlated subquery. + // Non-correlated subquery will be executed during the plan building phase, which cannot be done by mock in (*testPlanSuite).TestCollectPredicateColumns. + // Hence we put the test of collecting predicate columns for non-correlated subquery here. + tk.MustExec("delete from mysql.column_stats_usage") + tk.MustExec("select * from t2 where t2.a > (select count(*) from t1 where t1.b > 1)") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t1", "", "b"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't2'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t2", "", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") +} + +func TestCollectPredicateColumnsFromExecute(t *testing.T) { + for _, val := range []bool{false, true} { + func(planCache bool) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set tidb_enable_prepared_plan_cache=" + variable.BoolToOnOff(planCache)) + + originalVal2 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal2)) + }() + tk.MustExec("set global tidb_enable_column_tracking = 1") + + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t1(a int, b int)") + tk.MustExec("prepare stmt from 'select * from t1 where a > ?'") + require.NoError(t, h.DumpColStatsUsageToKV()) + // Prepare only converts sql string to ast and doesn't do optimization, so no predicate column is collected. + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Check(testkit.Rows()) + tk.MustExec("set @p1 = 1") + tk.MustExec("execute stmt using @p1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + + tk.MustExec("delete from mysql.column_stats_usage") + tk.MustExec("set @p2 = 2") + tk.MustExec("execute stmt using @p2") + if planCache { + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) + require.NoError(t, h.DumpColStatsUsageToKV()) + // If the second execution uses the cached plan, no predicate column is collected. + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Check(testkit.Rows()) + } else { + tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) + require.NoError(t, h.DumpColStatsUsageToKV()) + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() + require.Len(t, rows, 1) + require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) + require.True(t, rows[0][4].(string) != "") + require.True(t, rows[0][5].(string) == "") + } + }(val) + } +} + +func TestEnableAndDisableColumnTracking(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t (a int, b int, c int)") + + originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) + }() + + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() + require.Len(t, rows, 1) + require.Equal(t, "b", rows[0][3]) + + tk.MustExec("set global tidb_enable_column_tracking = 0") + // After tidb_enable_column_tracking is set to 0, the predicate columns collected before are invalidated. + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) + + // Sleep for 1.5s to let `last_used_at` be larger than `tidb_disable_tracking_time`. + time.Sleep(1500 * time.Millisecond) + tk.MustExec("select * from t where a > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + // We don't collect predicate columns when tidb_enable_column_tracking = 0 + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) + + tk.MustExec("set global tidb_enable_column_tracking = 1") + tk.MustExec("select * from t where b < 1 and c > 1") + require.NoError(t, h.DumpColStatsUsageToKV()) + rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Sort().Rows() + require.Len(t, rows, 2) + require.Equal(t, "b", rows[0][3]) + require.Equal(t, "c", rows[1][3]) + + // Test invalidating predicate columns again in order to check that tidb_disable_tracking_time can be updated. + tk.MustExec("set global tidb_enable_column_tracking = 0") + tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) +} + +func TestStatsLockUnlockForAutoAnalyze(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + is := dom.InfoSchema() + require.NoError(t, h.Update(is)) + // To pass the stats.Pseudo check in autoAnalyzeTable + tk.MustExec("analyze table t") + tk.MustExec("explain select * from t where a = 1") + require.NoError(t, h.LoadNeededHistograms()) + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 10)) + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.True(t, h.HandleAutoAnalyze(is)) + + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.Nil(t, err) + + tblStats := h.GetTableStats(tbl.Meta()) + for _, col := range tblStats.Columns { + require.True(t, col.IsStatsInitialized()) + } + + tk.MustExec("lock stats t") + + tk.MustExec("delete from t limit 12") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.False(t, h.HandleAutoAnalyze(is)) + + tblStats1 := h.GetTableStats(tbl.Meta()) + require.Equal(t, tblStats, tblStats1) + + tk.MustExec("unlock stats t") + + tk.MustExec("delete from t limit 4") + + rows := tk.MustQuery("select count(*) from t").Rows() + num, _ := strconv.Atoi(rows[0][0].(string)) + require.Equal(t, num, 15) + + tk.MustExec("analyze table t") + + tblStats2 := h.GetTableStats(tbl.Meta()) + require.Equal(t, int64(15), tblStats2.RealtimeCount) +} + +func TestStatsLockForDelta(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + testKit := testkit.NewTestKit(t, store) + testKit.MustExec("use test") + testKit.MustExec("set @@session.tidb_analyze_version = 1") + testKit.MustExec("create table t1 (c1 int, c2 int)") + testKit.MustExec("create table t2 (c1 int, c2 int)") + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tableInfo1 := tbl1.Meta() + h := dom.StatsHandle() + + testKit.MustExec("lock stats t1") + + rowCount1 := 10 + rowCount2 := 20 + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + for i := 0; i < rowCount2; i++ { + testKit.MustExec("insert into t2 values(1, 2)") + } + + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + err = h.HandleDDLEvent(<-h.DDLEventCh()) + require.NoError(t, err) + + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 := h.GetTableStats(tableInfo1) + require.Equal(t, stats1.RealtimeCount, int64(0)) + + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tableInfo2 := tbl2.Meta() + stats2 := h.GetTableStats(tableInfo2) + require.Equal(t, int64(rowCount2), stats2.RealtimeCount) + + testKit.MustExec("analyze table t1") + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, stats1.RealtimeCount, int64(0)) + + testKit.MustExec("unlock stats t1") + + testKit.MustExec("analyze table t1") + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(20), stats1.RealtimeCount) + + for i := 0; i < rowCount1; i++ { + testKit.MustExec("insert into t1 values(1, 2)") + } + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + stats1 = h.GetTableStats(tableInfo1) + require.Equal(t, int64(30), stats1.RealtimeCount) +} + +func TestFillMissingStatsMeta(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int)") + tk.MustExec("create table t2 (a int, b int) partition by range (a) (partition p0 values less than (10), partition p1 values less than (maxvalue))") + + tk.MustQuery("select * from mysql.stats_meta").Check(testkit.Rows()) + + is := dom.InfoSchema() + tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tbl1ID := tbl1.Meta().ID + tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tbl2Info := tbl2.Meta() + tbl2ID := tbl2Info.ID + require.Len(t, tbl2Info.Partition.Definitions, 2) + p0ID := tbl2Info.Partition.Definitions[0].ID + p1ID := tbl2Info.Partition.Definitions[1].ID + h := dom.StatsHandle() + + checkStatsMeta := func(id int64, expectedModifyCount, expectedCount string) int64 { + rows := tk.MustQuery(fmt.Sprintf("select version, modify_count, count from mysql.stats_meta where table_id = %v", id)).Rows() + require.Len(t, rows, 1) + ver, err := strconv.ParseInt(rows[0][0].(string), 10, 64) + require.NoError(t, err) + require.Equal(t, expectedModifyCount, rows[0][1]) + require.Equal(t, expectedCount, rows[0][2]) + return ver + } + + tk.MustExec("insert into t1 values (1, 2), (3, 4)") + require.NoError(t, h.DumpStatsDeltaToKV(false)) + require.NoError(t, h.Update(is)) + ver1 := checkStatsMeta(tbl1ID, "2", "2") + tk.MustExec("delete from t1 where a = 1") + require.NoError(t, h.DumpStatsDeltaToKV(false)) + require.NoError(t, h.Update(is)) + ver2 := checkStatsMeta(tbl1ID, "3", "1") + require.Greater(t, ver2, ver1) + + tk.MustExec("insert into t2 values (1, 2), (3, 4)") + require.NoError(t, h.DumpStatsDeltaToKV(false)) + require.NoError(t, h.Update(is)) + checkStatsMeta(p0ID, "2", "2") + globalVer1 := checkStatsMeta(tbl2ID, "2", "2") + tk.MustExec("insert into t2 values (11, 12)") + require.NoError(t, h.DumpStatsDeltaToKV(false)) + require.NoError(t, h.Update(is)) + checkStatsMeta(p1ID, "1", "1") + globalVer2 := checkStatsMeta(tbl2ID, "3", "3") + require.Greater(t, globalVer2, globalVer1) +} + +func TestNotDumpSysTable(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t1 (a int, b int)") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustQuery("select count(1) from mysql.stats_meta").Check(testkit.Rows("1")) + // After executing `delete from mysql.stats_meta`, a delta for mysql.stats_meta is created but it would not be dumped. + tk.MustExec("delete from mysql.stats_meta") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("mysql"), model.NewCIStr("stats_meta")) + require.NoError(t, err) + tblID := tbl.Meta().ID + tk.MustQuery(fmt.Sprintf("select * from mysql.stats_meta where table_id = %v", tblID)).Check(testkit.Rows()) +} + +func TestAutoAnalyzePartitionTableAfterAddingIndex(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + oriMinCnt := autoanalyze.AutoAnalyzeMinCnt + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + defer func() { + autoanalyze.AutoAnalyzeMinCnt = oriMinCnt + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + autoanalyze.AutoAnalyzeMinCnt = 0 + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + tk.MustExec("set global tidb_analyze_version = 2") + tk.MustExec("set global tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("use test") + tk.MustExec("create table t (a int, b int) partition by range (a) (PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN MAXVALUE)") + h := dom.StatsHandle() + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1,2), (3,4), (11,12),(13,14)") + tk.MustExec("set session tidb_analyze_version = 2") + tk.MustExec("set session tidb_partition_prune_mode = 'dynamic'") + tk.MustExec("analyze table t") + require.False(t, h.HandleAutoAnalyze(dom.InfoSchema())) + tk.MustExec("alter table t add index idx(a)") + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + idxInfo := tblInfo.Indices[0] + require.Nil(t, h.GetTableStats(tblInfo).Indices[idxInfo.ID]) + require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) + require.NotNil(t, h.GetTableStats(tblInfo).Indices[idxInfo.ID]) +} diff --git a/pkg/statistics/handle/usage/BUILD.bazel b/pkg/statistics/handle/usage/BUILD.bazel new file mode 100644 index 0000000000000..94c182b87e03d --- /dev/null +++ b/pkg/statistics/handle/usage/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "usage", + srcs = [ + "index_usage.go", + "predicate_column.go", + "session_stats_collect.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/usage", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/statistics/handle/storage", + "//pkg/statistics/handle/util", + "//pkg/types", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "usage_test", + timeout = "short", + srcs = ["session_stats_collect_test.go"], + embed = [":usage"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/statistics/handle/usage/index_usage.go b/pkg/statistics/handle/usage/index_usage.go similarity index 97% rename from statistics/handle/usage/index_usage.go rename to pkg/statistics/handle/usage/index_usage.go index 4851fdc83dba8..f7bb5f7259fe8 100644 --- a/statistics/handle/usage/index_usage.go +++ b/pkg/statistics/handle/usage/index_usage.go @@ -20,10 +20,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) // NewSessionIndexUsageCollector creates a new IndexUsageCollector on the list. diff --git a/statistics/handle/usage/predicate_column.go b/pkg/statistics/handle/usage/predicate_column.go similarity index 95% rename from statistics/handle/usage/predicate_column.go rename to pkg/statistics/handle/usage/predicate_column.go index 85b47be62dfcb..b280ebbace1ae 100644 --- a/statistics/handle/usage/predicate_column.go +++ b/pkg/statistics/handle/usage/predicate_column.go @@ -19,14 +19,14 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - utilstats "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + utilstats "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/statistics/handle/usage/session_stats_collect.go b/pkg/statistics/handle/usage/session_stats_collect.go similarity index 97% rename from statistics/handle/usage/session_stats_collect.go rename to pkg/statistics/handle/usage/session_stats_collect.go index ee5b8c06183f1..341073900b529 100644 --- a/statistics/handle/usage/session_stats_collect.go +++ b/pkg/statistics/handle/usage/session_stats_collect.go @@ -22,15 +22,15 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle/storage" - utilstats "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics/handle/storage" + utilstats "github.com/pingcap/tidb/pkg/statistics/handle/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) var ( diff --git a/statistics/handle/usage/session_stats_collect_test.go b/pkg/statistics/handle/usage/session_stats_collect_test.go similarity index 100% rename from statistics/handle/usage/session_stats_collect_test.go rename to pkg/statistics/handle/usage/session_stats_collect_test.go diff --git a/pkg/statistics/handle/util/BUILD.bazel b/pkg/statistics/handle/util/BUILD.bazel new file mode 100644 index 0000000000000..34746004b306e --- /dev/null +++ b/pkg/statistics/handle/util/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "util", + srcs = [ + "interfaces.go", + "table_info.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/statistics/handle/util", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/table", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/intest", + "//pkg/util/sqlexec", + "//pkg/util/sqlexec/mock", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//oracle", + ], +) diff --git a/statistics/handle/util/interfaces.go b/pkg/statistics/handle/util/interfaces.go similarity index 97% rename from statistics/handle/util/interfaces.go rename to pkg/statistics/handle/util/interfaces.go index 11ab78a6018d4..6b10235ad2f01 100644 --- a/statistics/handle/util/interfaces.go +++ b/pkg/statistics/handle/util/interfaces.go @@ -17,10 +17,10 @@ package util import ( "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/types" ) // StatsGC is used to GC unnecessary stats. diff --git a/statistics/handle/util/table_info.go b/pkg/statistics/handle/util/table_info.go similarity index 96% rename from statistics/handle/util/table_info.go rename to pkg/statistics/handle/util/table_info.go index c7cc5b73dba3b..27f4a9771f471 100644 --- a/statistics/handle/util/table_info.go +++ b/pkg/statistics/handle/util/table_info.go @@ -17,8 +17,8 @@ package util import ( "sync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/table" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/table" ) // TableInfoGetter is used to get table meta info. diff --git a/pkg/statistics/handle/util/util.go b/pkg/statistics/handle/util/util.go new file mode 100644 index 0000000000000..c5565a02d0141 --- /dev/null +++ b/pkg/statistics/handle/util/util.go @@ -0,0 +1,215 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package util + +import ( + "context" + "strconv" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/sqlexec/mock" + "github.com/tikv/client-go/v2/oracle" +) + +var ( + // UseCurrentSessionOpt to make sure the sql is executed in current session. + UseCurrentSessionOpt = []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession} + + // StatsCtx is used to mark the request is from stats module. + StatsCtx = kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) +) + +// SessionPool is used to recycle sessionctx. +type SessionPool interface { + Get() (pools.Resource, error) + Put(pools.Resource) +} + +// FinishTransaction will execute `commit` when error is nil, otherwise `rollback`. +func FinishTransaction(sctx sessionctx.Context, err error) error { + if err == nil { + _, _, err = ExecRows(sctx, "commit") + } else { + _, _, err1 := ExecRows(sctx, "rollback") + terror.Log(errors.Trace(err1)) + } + return errors.Trace(err) +} + +var ( + // FlagWrapTxn indicates whether to wrap a transaction. + FlagWrapTxn = 0 +) + +// CallWithSCtx allocates a sctx from the pool and call the f(). +func CallWithSCtx(pool SessionPool, f func(sctx sessionctx.Context) error, flags ...int) (err error) { + se, err := pool.Get() + if err != nil { + return err + } + defer func() { + if err == nil { // only recycle when no error + pool.Put(se) + } + }() + sctx := se.(sessionctx.Context) + if err := UpdateSCtxVarsForStats(sctx); err != nil { // update stats variables automatically + return err + } + + wrapTxn := false + for _, flag := range flags { + if flag == FlagWrapTxn { + wrapTxn = true + } + } + if wrapTxn { + err = WrapTxn(sctx, f) + } else { + err = f(sctx) + } + return err +} + +// UpdateSCtxVarsForStats updates all necessary variables that may affect the behavior of statistics. +func UpdateSCtxVarsForStats(sctx sessionctx.Context) error { + // analyzer version + verInString, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBAnalyzeVersion) + if err != nil { + return err + } + ver, err := strconv.ParseInt(verInString, 10, 64) + if err != nil { + return err + } + sctx.GetSessionVars().AnalyzeVersion = int(ver) + + // enable historical stats + val, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableHistoricalStats) + if err != nil { + return err + } + sctx.GetSessionVars().EnableHistoricalStats = variable.TiDBOptOn(val) + + // partition mode + pruneMode, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBPartitionPruneMode) + if err != nil { + return err + } + sctx.GetSessionVars().PartitionPruneMode.Store(pruneMode) + + // enable analyze snapshot + analyzeSnapshot, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableAnalyzeSnapshot) + if err != nil { + return err + } + sctx.GetSessionVars().EnableAnalyzeSnapshot = variable.TiDBOptOn(analyzeSnapshot) + + // enable skip column types + val, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBAnalyzeSkipColumnTypes) + if err != nil { + return err + } + sctx.GetSessionVars().AnalyzeSkipColumnTypes = variable.ParseAnalyzeSkipColumnTypes(val) + + // skip missing partition stats + val, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBSkipMissingPartitionStats) + if err != nil { + return err + } + sctx.GetSessionVars().SkipMissingPartitionStats = variable.TiDBOptOn(val) + verInString, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBMergePartitionStatsConcurrency) + if err != nil { + return err + } + ver, err = strconv.ParseInt(verInString, 10, 64) + if err != nil { + return err + } + sctx.GetSessionVars().AnalyzePartitionMergeConcurrency = int(ver) + return nil +} + +// WrapTxn uses a transaction here can let different SQLs in this operation have the same data visibility. +func WrapTxn(sctx sessionctx.Context, f func(sctx sessionctx.Context) error) (err error) { + // TODO: check whether this sctx is already in a txn + if _, _, err := ExecRows(sctx, "begin"); err != nil { + return err + } + defer func() { + err = FinishTransaction(sctx, err) + }() + err = f(sctx) + return +} + +// GetStartTS gets the start ts from current transaction. +func GetStartTS(sctx sessionctx.Context) (uint64, error) { + txn, err := sctx.Txn(true) + if err != nil { + return 0, err + } + return txn.StartTS(), nil +} + +// Exec is a helper function to execute sql and return RecordSet. +func Exec(sctx sessionctx.Context, sql string, args ...interface{}) (sqlexec.RecordSet, error) { + sqlExec, ok := sctx.(sqlexec.SQLExecutor) + if !ok { + return nil, errors.Errorf("invalid sql executor") + } + // TODO: use RestrictedSQLExecutor + ExecOptionUseCurSession instead of SQLExecutor + return sqlExec.ExecuteInternal(StatsCtx, sql, args...) +} + +// ExecRows is a helper function to execute sql and return rows and fields. +func ExecRows(sctx sessionctx.Context, sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, err error) { + if intest.InTest { + if v := sctx.Value(mock.MockRestrictedSQLExecutorKey{}); v != nil { + return v.(*mock.MockRestrictedSQLExecutor).ExecRestrictedSQL(StatsCtx, + UseCurrentSessionOpt, sql, args...) + } + } + + sqlExec, ok := sctx.(sqlexec.RestrictedSQLExecutor) + if !ok { + return nil, nil, errors.Errorf("invalid sql executor") + } + return sqlExec.ExecRestrictedSQL(StatsCtx, UseCurrentSessionOpt, sql, args...) +} + +// ExecWithOpts is a helper function to execute sql and return rows and fields. +func ExecWithOpts(sctx sessionctx.Context, opts []sqlexec.OptionFuncAlias, sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, err error) { + sqlExec, ok := sctx.(sqlexec.RestrictedSQLExecutor) + if !ok { + return nil, nil, errors.Errorf("invalid sql executor") + } + return sqlExec.ExecRestrictedSQL(StatsCtx, opts, sql, args...) +} + +// DurationToTS converts duration to timestamp. +func DurationToTS(d time.Duration) uint64 { + return oracle.ComposeTS(d.Nanoseconds()/int64(time.Millisecond), 0) +} diff --git a/statistics/histogram.go b/pkg/statistics/histogram.go similarity index 98% rename from statistics/histogram.go rename to pkg/statistics/histogram.go index 5671afaf46004..7223cf7f35083 100644 --- a/statistics/histogram.go +++ b/pkg/statistics/histogram.go @@ -26,20 +26,20 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/pingcap/tipb/go-tipb" "github.com/twmb/murmur3" ) diff --git a/statistics/histogram_bench_test.go b/pkg/statistics/histogram_bench_test.go similarity index 93% rename from statistics/histogram_bench_test.go rename to pkg/statistics/histogram_bench_test.go index 1a4b8e3db9746..7729b51d25aa3 100644 --- a/statistics/histogram_bench_test.go +++ b/pkg/statistics/histogram_bench_test.go @@ -19,10 +19,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) @@ -95,7 +95,7 @@ func benchmarkMergePartitionHist2GlobalHist(b *testing.B, partition int) { var benchmarkPartitionSize = []int{1000, 10000, 100000} -// cmd: go test -run=^$ -bench=BenchmarkMergePartitionHist2GlobalHist -benchmem github.com/pingcap/tidb/statistics +// cmd: go test -run=^$ -bench=BenchmarkMergePartitionHist2GlobalHist -benchmem github.com/pingcap/tidb/pkg/statistics func BenchmarkMergePartitionHist2GlobalHist(b *testing.B) { for _, size := range benchmarkPartitionSize { b.Run(fmt.Sprintf("Size%d", size), func(b *testing.B) { diff --git a/statistics/histogram_test.go b/pkg/statistics/histogram_test.go similarity index 98% rename from statistics/histogram_test.go rename to pkg/statistics/histogram_test.go index c03f442e97876..15da03047b8fd 100644 --- a/statistics/histogram_test.go +++ b/pkg/statistics/histogram_test.go @@ -18,11 +18,11 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/index.go b/pkg/statistics/index.go new file mode 100644 index 0000000000000..6ae66914e8a3e --- /dev/null +++ b/pkg/statistics/index.go @@ -0,0 +1,226 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util/debugtrace" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/twmb/murmur3" +) + +// Index represents an index histogram. +type Index struct { + LastAnalyzePos types.Datum + CMSketch *CMSketch + TopN *TopN + FMSketch *FMSketch + Info *model.IndexInfo + Histogram + StatsLoadedStatus + StatsVer int64 // StatsVer is the version of the current stats, used to maintain compatibility + Flag int64 + // PhysicalID is the physical table id, + // or it could possibly be -1, which means "stats not available". + // The -1 case could happen in a pseudo stats table, and in this case, this stats should not trigger stats loading. + PhysicalID int64 +} + +// Copy copies the index. +func (idx *Index) Copy() *Index { + if idx == nil { + return nil + } + nc := &Index{ + PhysicalID: idx.PhysicalID, + Flag: idx.Flag, + StatsVer: idx.StatsVer, + } + idx.LastAnalyzePos.Copy(&nc.LastAnalyzePos) + if idx.CMSketch != nil { + nc.CMSketch = idx.CMSketch.Copy() + } + if idx.TopN != nil { + nc.TopN = idx.TopN.Copy() + } + if idx.FMSketch != nil { + nc.FMSketch = idx.FMSketch.Copy() + } + if idx.Info != nil { + nc.Info = idx.Info.Clone() + } + nc.Histogram = *idx.Histogram.Copy() + nc.StatsLoadedStatus = idx.StatsLoadedStatus.Copy() + return nc +} + +// ItemID implements TableCacheItem +func (idx *Index) ItemID() int64 { + return idx.Info.ID +} + +// IsAllEvicted indicates whether all stats evicted +func (idx *Index) IsAllEvicted() bool { + return idx.statsInitialized && idx.evictedStatus >= AllEvicted +} + +// GetEvictedStatus returns the evicted status +func (idx *Index) GetEvictedStatus() int { + return idx.evictedStatus +} + +// DropUnnecessaryData drops unnecessary data for index. +func (idx *Index) DropUnnecessaryData() { + if idx.GetStatsVer() < Version2 { + idx.CMSketch = nil + } + idx.TopN = nil + idx.Histogram.Bounds = chunk.NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeBlob)}, 0) + idx.Histogram.Buckets = make([]Bucket, 0) + idx.Histogram.Scalars = make([]scalar, 0) + idx.evictedStatus = AllEvicted +} + +func (idx *Index) isStatsInitialized() bool { + return idx.statsInitialized +} + +// GetStatsVer returns the version of the current stats +func (idx *Index) GetStatsVer() int64 { + return idx.StatsVer +} + +// IsCMSExist returns whether CMSketch exists. +func (idx *Index) IsCMSExist() bool { + return idx.CMSketch != nil +} + +// IsEvicted returns whether index statistics got evicted +func (idx *Index) IsEvicted() bool { + return idx.evictedStatus != AllLoaded +} + +func (idx *Index) String() string { + return idx.Histogram.ToString(len(idx.Info.Columns)) +} + +// TotalRowCount returns the total count of this index. +func (idx *Index) TotalRowCount() float64 { + if idx.StatsVer >= Version2 { + return idx.Histogram.TotalRowCount() + float64(idx.TopN.TotalCount()) + } + return idx.Histogram.TotalRowCount() +} + +// IsInvalid checks if this index is invalid. +func (idx *Index) IsInvalid(sctx sessionctx.Context, collPseudo bool) (res bool) { + idx.CheckStats() + var totalCount float64 + if sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(sctx) + defer func() { + debugtrace.RecordAnyValuesWithNames(sctx, + "IsInvalid", res, + "CollPseudo", collPseudo, + "TotalCount", totalCount, + ) + debugtrace.LeaveContextCommon(sctx) + }() + } + totalCount = idx.TotalRowCount() + return (collPseudo) || totalCount == 0 +} + +// EvictAllStats evicts all stats +// Note that this function is only used for test +func (idx *Index) EvictAllStats() { + idx.Histogram.Buckets = nil + idx.CMSketch = nil + idx.TopN = nil + idx.StatsLoadedStatus.evictedStatus = AllEvicted +} + +// MemoryUsage returns the total memory usage of a Histogram and CMSketch in Index. +// We ignore the size of other metadata in Index. +func (idx *Index) MemoryUsage() CacheItemMemoryUsage { + var sum int64 + indexMemUsage := &IndexMemUsage{ + IndexID: idx.Info.ID, + } + histMemUsage := idx.Histogram.MemoryUsage() + indexMemUsage.HistogramMemUsage = histMemUsage + sum = histMemUsage + if idx.CMSketch != nil { + cmSketchMemUsage := idx.CMSketch.MemoryUsage() + indexMemUsage.CMSketchMemUsage = cmSketchMemUsage + sum += cmSketchMemUsage + } + if idx.TopN != nil { + topnMemUsage := idx.TopN.MemoryUsage() + indexMemUsage.TopNMemUsage = topnMemUsage + sum += topnMemUsage + } + indexMemUsage.TotalMemUsage = sum + return indexMemUsage +} + +// QueryBytes is used to query the count of specified bytes. +// The input sctx is just for debug trace, you can pass nil safely if that's not needed. +func (idx *Index) QueryBytes(sctx sessionctx.Context, d []byte) (result uint64) { + if sctx != nil && sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { + debugtrace.EnterContextCommon(sctx) + defer func() { + debugtrace.RecordAnyValuesWithNames(sctx, "Result", result) + debugtrace.LeaveContextCommon(sctx) + }() + } + h1, h2 := murmur3.Sum128(d) + if idx.TopN != nil { + if count, ok := idx.TopN.QueryTopN(sctx, d); ok { + return count + } + } + if idx.CMSketch != nil { + return idx.CMSketch.queryHashValue(sctx, h1, h2) + } + v, _ := idx.Histogram.EqualRowCount(sctx, types.NewBytesDatum(d), idx.StatsVer >= Version2) + return uint64(v) +} + +// CheckStats will check if the index stats need to be updated. +func (idx *Index) CheckStats() { + // When we are using stats from PseudoTable(), all column/index ID will be -1. + if idx.IsFullLoad() || idx.PhysicalID <= 0 { + return + } + HistogramNeededItems.insert(model.TableItemID{TableID: idx.PhysicalID, ID: idx.Info.ID, IsIndex: true}) +} + +// GetIncreaseFactor get the increase factor to adjust the final estimated count when the table is modified. +func (idx *Index) GetIncreaseFactor(realtimeRowCount int64) float64 { + columnCount := idx.TotalRowCount() + if columnCount == 0 { + return 1.0 + } + return float64(realtimeRowCount) / columnCount +} + +// IsAnalyzed indicates whether the index is analyzed. +func (idx *Index) IsAnalyzed() bool { + return idx.StatsVer != Version0 +} diff --git a/pkg/statistics/integration_test.go b/pkg/statistics/integration_test.go new file mode 100644 index 0000000000000..9b51915b174c8 --- /dev/null +++ b/pkg/statistics/integration_test.go @@ -0,0 +1,483 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package statistics_test + +import ( + "fmt" + "math" + "strconv" + "strings" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/stretchr/testify/require" +) + +func TestChangeVerTo2Behavior(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + }() + tk.MustExec("set global tidb_persist_analyze_options=false") + + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("insert into t values(1, 1), (1, 2), (1, 3)") + tk.MustExec("analyze table t") + is := dom.InfoSchema() + tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + h := dom.StatsHandle() + require.NoError(t, h.Update(is)) + statsTblT := h.GetTableStats(tblT.Meta()) + // Analyze table with version 1 success, all statistics are version 1. + for _, col := range statsTblT.Columns { + require.Equal(t, int64(1), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("analyze table t index idx") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("analyze table t index") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("analyze table t ") + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, col := range statsTblT.Columns { + require.Equal(t, int64(2), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("analyze table t index idx") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", + "Warning 1105 The version 2 would collect all statistics not only the selected indexes")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("analyze table t index") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", + "Warning 1105 The version 2 would collect all statistics not only the selected indexes")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("analyze table t ") + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, col := range statsTblT.Columns { + require.Equal(t, int64(1), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } +} + +func TestChangeVerTo2BehaviorWithPersistedOptions(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) + defer func() { + tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) + }() + tk.MustExec("set global tidb_persist_analyze_options=true") + + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index idx(a))") + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("insert into t values(1, 1), (1, 2), (1, 3)") + tk.MustExec("analyze table t") + is := dom.InfoSchema() + tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + h := dom.StatsHandle() + require.NoError(t, h.Update(is)) + statsTblT := h.GetTableStats(tblT.Meta()) + // Analyze table with version 1 success, all statistics are version 1. + for _, col := range statsTblT.Columns { + require.Equal(t, int64(1), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("set @@session.tidb_analyze_version = 2") + tk.MustExec("analyze table t index idx") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("analyze table t index") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } + tk.MustExec("analyze table t ") + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, col := range statsTblT.Columns { + require.Equal(t, int64(2), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("set @@session.tidb_analyze_version = 1") + tk.MustExec("analyze table t index idx") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", + "Warning 1105 The version 2 would collect all statistics not only the selected indexes", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) // since fallback to ver2 path, should do samplerate adjustment + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("analyze table t index") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", + "Warning 1105 The version 2 would collect all statistics not only the selected indexes", + "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(2), idx.GetStatsVer()) + } + tk.MustExec("analyze table t ") + require.NoError(t, h.Update(is)) + statsTblT = h.GetTableStats(tblT.Meta()) + for _, col := range statsTblT.Columns { + require.Equal(t, int64(1), col.GetStatsVer()) + } + for _, idx := range statsTblT.Indices { + require.Equal(t, int64(1), idx.GetStatsVer()) + } +} + +func TestExpBackoffEstimation(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // estRows won't be updated if hit cache. + tk.MustExec("set tidb_cost_model_version=2") + tk.MustExec("create table exp_backoff(a int, b int, c int, d int, index idx(a, b, c, d))") + tk.MustExec("insert into exp_backoff values(1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 2, 3), (1, 2, 2, 4), (1, 2, 3, 5)") + tk.MustExec("set @@session.tidb_analyze_version=2") + tk.MustExec("analyze table exp_backoff") + var ( + input []string + output [][]string + ) + integrationSuiteData := statistics.GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + inputLen := len(input) + // The test cases are: + // Query a = 1, b = 1, c = 1, d >= 3 and d <= 5 separately. We got 5, 3, 2, 3. + // And then query and a = 1 and b = 1 and c = 1 and d >= 3 and d <= 5. It's result should follow the exp backoff, + // which is 2/5 * (3/5)^{1/2} * (3/5)*{1/4} * 1^{1/8} * 5 = 1.3634. + for i := 0; i < inputLen-1; i++ { + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(input[i]).Rows()) + }) + tk.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) + } + + // The last case is that no column is loaded and we get no stats at all. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/planner/cardinality/cleanEstResults", `return(true)`)) + testdata.OnRecord(func() { + output[inputLen-1] = testdata.ConvertRowsToStrings(tk.MustQuery(input[inputLen-1]).Rows()) + }) + tk.MustQuery(input[inputLen-1]).Check(testkit.Rows(output[inputLen-1]...)) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/planner/cardinality/cleanEstResults")) +} + +func TestNULLOnFullSampling(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("set @@session.tidb_analyze_version = 2;") + tk.MustExec("create table t(a int, index idx(a))") + tk.MustExec("insert into t values(1), (1), (1), (2), (2), (3), (4), (null), (null), (null)") + var ( + input []string + output [][]string + ) + tk.MustExec("analyze table t with 2 topn") + is := dom.InfoSchema() + tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + h := dom.StatsHandle() + require.NoError(t, h.Update(is)) + statsTblT := h.GetTableStats(tblT.Meta()) + // Check the null count is 3. + for _, col := range statsTblT.Columns { + require.Equal(t, int64(3), col.NullCount) + } + integrationSuiteData := statistics.GetIntegrationSuiteData() + integrationSuiteData.LoadTestCases(t, &input, &output) + // Check the topn and buckets contains no null values. + for i := 0; i < len(input); i++ { + testdata.OnRecord(func() { + output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(input[i]).Rows()) + }) + tk.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) + } +} + +func TestAnalyzeSnapshot(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("set @@session.tidb_analyze_version = 2;") + tk.MustExec("create table t(a int, index(a))") + tk.MustExec("insert into t values(1), (1), (1)") + tk.MustExec("analyze table t") + rows := tk.MustQuery("select count, snapshot, version from mysql.stats_meta").Rows() + require.Len(t, rows, 1) + require.Equal(t, "3", rows[0][0]) + s1Str := rows[0][1].(string) + s1, err := strconv.ParseUint(s1Str, 10, 64) + require.NoError(t, err) + require.True(t, s1 < math.MaxUint64) + + // TestHistogramsWithSameTxnTS + v1 := rows[0][2].(string) + rows = tk.MustQuery("select version from mysql.stats_histograms").Rows() + require.Len(t, rows, 2) + v2 := rows[0][0].(string) + require.Equal(t, v1, v2) + v3 := rows[1][0].(string) + require.Equal(t, v2, v3) + + tk.MustExec("insert into t values(1), (1), (1)") + tk.MustExec("analyze table t") + rows = tk.MustQuery("select count, snapshot from mysql.stats_meta").Rows() + require.Len(t, rows, 1) + require.Equal(t, "6", rows[0][0]) + s2Str := rows[0][1].(string) + s2, err := strconv.ParseUint(s2Str, 10, 64) + require.NoError(t, err) + require.True(t, s2 < math.MaxUint64) + require.True(t, s2 > s1) +} + +func TestOutdatedStatsCheck(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) + oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) + autoanalyze.AutoAnalyzeMinCnt = 0 + defer func() { + autoanalyze.AutoAnalyzeMinCnt = 1000 + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) + tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) + }() + tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") + tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") + tk.MustExec("set session tidb_enable_pseudo_for_outdated_stats=1") + + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t (a int)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) // 20 rows + require.NoError(t, h.DumpStatsDeltaToKV(true)) + is := dom.InfoSchema() + require.NoError(t, h.Update(is)) + // To pass the stats.Pseudo check in autoAnalyzeTable + tk.MustExec("analyze table t") + tk.MustExec("explain select * from t where a = 1") + require.NoError(t, h.LoadNeededHistograms()) + + getStatsHealthy := func() int { + rows := tk.MustQuery("show stats_healthy where db_name = 'test' and table_name = 't'").Rows() + require.Len(t, rows, 1) + healthy, err := strconv.Atoi(rows[0][3].(string)) + require.NoError(t, err) + return healthy + } + + tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 13)) // 34 rows + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 30) + require.False(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) + tk.MustExec("insert into t values (1)") // 35 rows + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 25) + require.True(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) + + tk.MustExec("analyze table t") + + tk.MustExec("delete from t limit 24") // 11 rows + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 31) + require.False(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) + + tk.MustExec("delete from t limit 1") // 10 rows + require.NoError(t, h.DumpStatsDeltaToKV(true)) + require.NoError(t, h.Update(is)) + require.Equal(t, getStatsHealthy(), 28) + require.True(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) +} + +func hasPseudoStats(rows [][]interface{}) bool { + for i := range rows { + if strings.Contains(rows[i][4].(string), "stats:pseudo") { + return true + } + } + return false +} + +func TestShowHistogramsLoadStatus(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + origLease := h.Lease() + h.SetLease(time.Second) + defer func() { h.SetLease(origLease) }() + tk.MustExec("use test") + tk.MustExec("create table t(a int primary key, b int, c int, index idx(b, c))") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1,2,3), (4,5,6)") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t") + require.NoError(t, h.Update(dom.InfoSchema())) + rows := tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Rows() + for _, row := range rows { + require.Equal(t, "allEvicted", row[10].(string)) + } +} + +func TestSingleColumnIndexNDV(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, c varchar(20), d varchar(20), index idx_a(a), index idx_b(b), index idx_c(c), index idx_d(d))") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t values (1, 1, 'xxx', 'zzz'), (2, 2, 'yyy', 'zzz'), (1, 3, null, 'zzz')") + for i := 0; i < 5; i++ { + tk.MustExec("insert into t select * from t") + } + tk.MustExec("analyze table t") + rows := tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Sort().Rows() + expectedResults := [][]string{ + {"a", "2", "0"}, {"b", "3", "0"}, {"c", "2", "32"}, {"d", "1", "0"}, + {"idx_a", "2", "0"}, {"idx_b", "3", "0"}, {"idx_c", "2", "32"}, {"idx_d", "1", "0"}, + } + for i, row := range rows { + require.Equal(t, expectedResults[i][0], row[3]) // column_name + require.Equal(t, expectedResults[i][1], row[6]) // distinct_count + require.Equal(t, expectedResults[i][2], row[7]) // null_count + } +} + +func TestColumnStatsLazyLoad(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + originLease := h.Lease() + defer h.SetLease(originLease) + // Set `Lease` to `Millisecond` to enable column stats lazy load. + h.SetLease(time.Millisecond) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int)") + tk.MustExec("insert into t values (1,2), (3,4), (5,6), (7,8)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("analyze table t") + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + c1 := tblInfo.Columns[0] + c2 := tblInfo.Columns[1] + require.True(t, h.GetTableStats(tblInfo).Columns[c1.ID].IsAllEvicted()) + require.True(t, h.GetTableStats(tblInfo).Columns[c2.ID].IsAllEvicted()) + tk.MustExec("analyze table t") + require.True(t, h.GetTableStats(tblInfo).Columns[c1.ID].IsAllEvicted()) + require.True(t, h.GetTableStats(tblInfo).Columns[c2.ID].IsAllEvicted()) +} + +func TestUpdateNotLoadIndexFMSketch(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + h := dom.StatsHandle() + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index idx(a)) partition by range (a) (partition p0 values less than (10),partition p1 values less than maxvalue)") + tk.MustExec("insert into t values (1,2), (3,4), (5,6), (7,8)") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("analyze table t") + is := dom.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tbl.Meta() + idxInfo := tblInfo.Indices[0] + p0 := tblInfo.Partition.Definitions[0] + p1 := tblInfo.Partition.Definitions[1] + require.Nil(t, h.GetPartitionStats(tblInfo, p0.ID).Indices[idxInfo.ID].FMSketch) + require.Nil(t, h.GetPartitionStats(tblInfo, p1.ID).Indices[idxInfo.ID].FMSketch) + h.Clear() + require.NoError(t, h.Update(is)) + require.Nil(t, h.GetPartitionStats(tblInfo, p0.ID).Indices[idxInfo.ID].FMSketch) + require.Nil(t, h.GetPartitionStats(tblInfo, p1.ID).Indices[idxInfo.ID].FMSketch) +} + +func TestIssue44369(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + h := dom.StatsHandle() + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int, b int, index iab(a,b));") + require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) + tk.MustExec("insert into t value(1,1);") + require.NoError(t, h.DumpStatsDeltaToKV(true)) + tk.MustExec("analyze table t;") + is := dom.InfoSchema() + require.NoError(t, h.Update(is)) + tk.MustExec("alter table t rename column b to bb;") + tk.MustExec("select * from t where a = 10 and bb > 20;") +} diff --git a/pkg/statistics/main_test.go b/pkg/statistics/main_test.go new file mode 100644 index 0000000000000..818e3bf1e46e3 --- /dev/null +++ b/pkg/statistics/main_test.go @@ -0,0 +1,147 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "flag" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit/testdata" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +var testDataMap = make(testdata.BookKeeper, 3) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + if !flag.Parsed() { + flag.Parse() + } + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + testDataMap.LoadTestSuiteData("testdata", "integration_suite") + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + callback := func(i int) int { + testDataMap.GenerateOutputIfNeeded() + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} + +func GetIntegrationSuiteData() testdata.TestData { + return testDataMap["integration_suite"] +} + +// TestStatistics batches tests sharing a test suite to reduce the setups +// overheads. +func TestStatistics(t *testing.T) { + // fmsketch_test.go + t.Run("SubTestSketch", SubTestSketch()) + t.Run("SubTestSketchProtoConversion", SubTestSketchProtoConversion()) + t.Run("SubTestFMSketchCoding", SubTestFMSketchCoding()) + + // statistics_test.go + t.Run("SubTestColumnRange", SubTestColumnRange()) + t.Run("SubTestIntColumnRanges", SubTestIntColumnRanges()) + t.Run("SubTestIndexRanges", SubTestIndexRanges()) + + // statistics_serial_test.go + t.Run("SubTestBuild", SubTestBuild()) + t.Run("SubTestHistogramProtoConversion", SubTestHistogramProtoConversion()) +} + +func createTestStatisticsSamples(t *testing.T) *testStatisticsSamples { + s := new(testStatisticsSamples) + + s.count = 100000 + samples := make([]*SampleItem, 10000) + for i := 0; i < len(samples); i++ { + samples[i] = &SampleItem{} + } + start := 1000 + samples[0].Value.SetInt64(0) + for i := 1; i < start; i++ { + samples[i].Value.SetInt64(2) + } + for i := start; i < len(samples); i++ { + samples[i].Value.SetInt64(int64(i)) + } + for i := start; i < len(samples); i += 3 { + samples[i].Value.SetInt64(samples[i].Value.GetInt64() + 1) + } + for i := start; i < len(samples); i += 5 { + samples[i].Value.SetInt64(samples[i].Value.GetInt64() + 2) + } + sc := stmtctx.NewStmtCtx() + + var err error + s.samples, err = SortSampleItems(sc, samples) + require.NoError(t, err) + + rc := &recordSet{ + data: make([]types.Datum, s.count), + count: s.count, + cursor: 0, + } + rc.setFields(mysql.TypeLonglong) + rc.data[0].SetInt64(0) + for i := 1; i < start; i++ { + rc.data[i].SetInt64(2) + } + for i := start; i < rc.count; i++ { + rc.data[i].SetInt64(int64(i)) + } + for i := start; i < rc.count; i += 3 { + rc.data[i].SetInt64(rc.data[i].GetInt64() + 1) + } + for i := start; i < rc.count; i += 5 { + rc.data[i].SetInt64(rc.data[i].GetInt64() + 2) + } + require.NoError(t, types.SortDatums(sc, rc.data)) + + s.rc = rc + + pk := &recordSet{ + data: make([]types.Datum, s.count), + count: s.count, + cursor: 0, + } + pk.setFields(mysql.TypeLonglong) + for i := 0; i < rc.count; i++ { + pk.data[i].SetInt64(int64(i)) + } + s.pk = pk + + return s +} diff --git a/statistics/row_sampler.go b/pkg/statistics/row_sampler.go similarity index 97% rename from statistics/row_sampler.go rename to pkg/statistics/row_sampler.go index 3764277cb6016..3c26c366ad2b7 100644 --- a/statistics/row_sampler.go +++ b/pkg/statistics/row_sampler.go @@ -20,15 +20,15 @@ import ( "math/rand" "unsafe" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/statistics/sample.go b/pkg/statistics/sample.go new file mode 100644 index 0000000000000..c813f7e7886ff --- /dev/null +++ b/pkg/statistics/sample.go @@ -0,0 +1,318 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "context" + "slices" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/fastrand" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tipb/go-tipb" + "github.com/twmb/murmur3" +) + +// SampleItem is an item of sampled column value. +type SampleItem struct { + // Value is the sampled column value. + Value types.Datum + // Handle is the handle of the sample in its key. + // This property is used to calculate Ordinal in fast analyze. + Handle kv.Handle + // Ordinal is original position of this item in SampleCollector before sorting. This + // is used for computing correlation. + Ordinal int +} + +// EmptySampleItemSize is the size of empty SampleItem, 96 = 72 (datum) + 8 (int) + 16. +const EmptySampleItemSize = int64(unsafe.Sizeof(SampleItem{})) + +// CopySampleItems returns a deep copy of SampleItem slice. +func CopySampleItems(items []*SampleItem) []*SampleItem { + n := make([]*SampleItem, len(items)) + for i, item := range items { + ni := *item + n[i] = &ni + } + return n +} + +// SortSampleItems shallow copies and sorts a slice of SampleItem. +func SortSampleItems(sc *stmtctx.StatementContext, items []*SampleItem) ([]*SampleItem, error) { + sortedItems := make([]*SampleItem, len(items)) + copy(sortedItems, items) + var err error + slices.SortStableFunc(sortedItems, func(i, j *SampleItem) int { + var cmp int + cmp, err = i.Value.Compare(sc, &j.Value, collate.GetBinaryCollator()) + if err != nil { + return -1 + } + return cmp + }) + return sortedItems, err +} + +// SampleCollector will collect Samples and calculate the count and ndv of an attribute. +type SampleCollector struct { + FMSketch *FMSketch + CMSketch *CMSketch + TopN *TopN + Samples []*SampleItem + seenValues int64 // seenValues is the current seen values. + NullCount int64 + Count int64 // Count is the number of non-null rows. + MaxSampleSize int64 + TotalSize int64 // TotalSize is the total size of column. + MemSize int64 // major memory size of this sample collector. + IsMerger bool +} + +// MergeSampleCollector merges two sample collectors. +func (c *SampleCollector) MergeSampleCollector(sc *stmtctx.StatementContext, rc *SampleCollector) { + c.NullCount += rc.NullCount + c.Count += rc.Count + c.TotalSize += rc.TotalSize + c.FMSketch.MergeFMSketch(rc.FMSketch) + if rc.CMSketch != nil { + err := c.CMSketch.MergeCMSketch(rc.CMSketch) + terror.Log(errors.Trace(err)) + } + for _, item := range rc.Samples { + err := c.collect(sc, item.Value) + terror.Log(errors.Trace(err)) + } +} + +// SampleCollectorToProto converts SampleCollector to its protobuf representation. +func SampleCollectorToProto(c *SampleCollector) *tipb.SampleCollector { + collector := &tipb.SampleCollector{ + NullCount: c.NullCount, + Count: c.Count, + FmSketch: FMSketchToProto(c.FMSketch), + TotalSize: &c.TotalSize, + } + if c.CMSketch != nil { + collector.CmSketch = CMSketchToProto(c.CMSketch, nil) + } + for _, item := range c.Samples { + collector.Samples = append(collector.Samples, item.Value.GetBytes()) + } + return collector +} + +// MaxSampleValueLength defines the max length of the useful samples. If one sample value exceeds the max length, we drop it before building the stats. +const MaxSampleValueLength = mysql.MaxFieldVarCharLength / 2 + +// SampleCollectorFromProto converts SampleCollector from its protobuf representation. +func SampleCollectorFromProto(collector *tipb.SampleCollector) *SampleCollector { + s := &SampleCollector{ + NullCount: collector.NullCount, + Count: collector.Count, + FMSketch: FMSketchFromProto(collector.FmSketch), + } + if collector.TotalSize != nil { + s.TotalSize = *collector.TotalSize + } + s.CMSketch, s.TopN = CMSketchAndTopNFromProto(collector.CmSketch) + for _, val := range collector.Samples { + // When store the histogram bucket boundaries to kv, we need to limit the length of the value. + if len(val) <= MaxSampleValueLength { + item := &SampleItem{Value: types.NewBytesDatum(val)} + s.Samples = append(s.Samples, item) + } + } + return s +} + +func (c *SampleCollector) collect(sc *stmtctx.StatementContext, d types.Datum) error { + if !c.IsMerger { + if d.IsNull() { + c.NullCount++ + return nil + } + c.Count++ + if err := c.FMSketch.InsertValue(sc, d); err != nil { + return errors.Trace(err) + } + if c.CMSketch != nil { + c.CMSketch.InsertBytes(d.GetBytes()) + } + // Minus one is to remove the flag byte. + c.TotalSize += int64(len(d.GetBytes()) - 1) + } + c.seenValues++ + // The following code use types.CloneDatum(d) because d may have a deep reference + // to the underlying slice, GC can't free them which lead to memory leak eventually. + // TODO: Refactor the proto to avoid copying here. + if len(c.Samples) < int(c.MaxSampleSize) { + newItem := &SampleItem{} + d.Copy(&newItem.Value) + c.Samples = append(c.Samples, newItem) + } else { + shouldAdd := int64(fastrand.Uint64N(uint64(c.seenValues))) < c.MaxSampleSize + if shouldAdd { + idx := int(fastrand.Uint32N(uint32(c.MaxSampleSize))) + newItem := &SampleItem{} + d.Copy(&newItem.Value) + // To keep the order of the elements, we use delete and append, not direct replacement. + c.Samples = append(c.Samples[:idx], c.Samples[idx+1:]...) + c.Samples = append(c.Samples, newItem) + } + } + return nil +} + +// CalcTotalSize is to calculate total size based on samples. +func (c *SampleCollector) CalcTotalSize() { + c.TotalSize = 0 + for _, item := range c.Samples { + c.TotalSize += int64(len(item.Value.GetBytes())) + } +} + +// SampleBuilder is used to build samples for columns. +// Also, if primary key is handle, it will directly build histogram for it. +type SampleBuilder struct { + RecordSet sqlexec.RecordSet + Sc *stmtctx.StatementContext + PkBuilder *SortedBuilder + Collators []collate.Collator + ColsFieldType []*types.FieldType + ColLen int // ColLen is the number of columns need to be sampled. + MaxBucketSize int64 + MaxSampleSize int64 + MaxFMSketchSize int64 + CMSketchDepth int32 + CMSketchWidth int32 +} + +// CollectColumnStats collects sample from the result set using Reservoir Sampling algorithm, +// and estimates NDVs using FM Sketch during the collecting process. +// It returns the sample collectors which contain total count, null count, distinct values count and CM Sketch. +// It also returns the statistic builder for PK which contains the histogram. +// See https://en.wikipedia.org/wiki/Reservoir_sampling +func (s SampleBuilder) CollectColumnStats() ([]*SampleCollector, *SortedBuilder, error) { + collectors := make([]*SampleCollector, s.ColLen) + for i := range collectors { + collectors[i] = &SampleCollector{ + MaxSampleSize: s.MaxSampleSize, + FMSketch: NewFMSketch(int(s.MaxFMSketchSize)), + } + } + if s.CMSketchDepth > 0 && s.CMSketchWidth > 0 { + for i := range collectors { + collectors[i].CMSketch = NewCMSketch(s.CMSketchDepth, s.CMSketchWidth) + } + } + ctx := context.TODO() + req := s.RecordSet.NewChunk(nil) + it := chunk.NewIterator4Chunk(req) + for { + err := s.RecordSet.Next(ctx, req) + if err != nil { + return nil, nil, errors.Trace(err) + } + if req.NumRows() == 0 { + return collectors, s.PkBuilder, nil + } + if len(s.RecordSet.Fields()) == 0 { + return nil, nil, errors.Errorf("collect column stats failed: record set has 0 field") + } + for row := it.Begin(); row != it.End(); row = it.Next() { + datums := RowToDatums(row, s.RecordSet.Fields()) + if s.PkBuilder != nil { + err = s.PkBuilder.Iterate(datums[0]) + if err != nil { + return nil, nil, errors.Trace(err) + } + datums = datums[1:] + } + for i, val := range datums { + if s.Collators[i] != nil && !val.IsNull() { + decodedVal, err := tablecodec.DecodeColumnValue(val.GetBytes(), s.ColsFieldType[i], s.Sc.TimeZone()) + if err != nil { + return nil, nil, err + } + decodedVal.SetBytesAsString(s.Collators[i].Key(decodedVal.GetString()), decodedVal.Collation(), uint32(decodedVal.Length())) + encodedKey, err := tablecodec.EncodeValue(s.Sc, nil, decodedVal) + if err != nil { + return nil, nil, err + } + val.SetBytes(encodedKey) + } + err = collectors[i].collect(s.Sc, val) + if err != nil { + return nil, nil, errors.Trace(err) + } + } + } + } +} + +// RowToDatums converts row to datum slice. +func RowToDatums(row chunk.Row, fields []*ast.ResultField) []types.Datum { + datums := make([]types.Datum, len(fields)) + for i, f := range fields { + datums[i] = row.GetDatum(i, &f.Column.FieldType) + } + return datums +} + +// ExtractTopN extracts the topn from the CM Sketch. +func (c *SampleCollector) ExtractTopN(numTop uint32, sc *stmtctx.StatementContext, tp *types.FieldType, timeZone *time.Location) error { + if numTop == 0 { + return nil + } + values := make([][]byte, 0, len(c.Samples)) + for _, sample := range c.Samples { + values = append(values, sample.Value.GetBytes()) + } + helper := newTopNHelper(values, numTop) + cms := c.CMSketch + c.TopN = NewTopN(int(helper.actualNumTop)) + // Process them decreasingly so we can handle most frequent values first and reduce the probability of hash collision + // by small values. + for i := uint32(0); i < helper.actualNumTop; i++ { + h1, h2 := murmur3.Sum128(helper.sorted[i].data) + realCnt := cms.queryHashValue(nil, h1, h2) + // Because the encode of topn is the new encode type. But analyze proto returns the old encode type for a sample datum, + // we should decode it and re-encode it to get the correct bytes. + d, err := tablecodec.DecodeColumnValue(helper.sorted[i].data, tp, timeZone) + if err != nil { + return err + } + data, err := tablecodec.EncodeValue(sc, nil, d) + if err != nil { + return err + } + cms.SubValue(h1, h2, realCnt) + c.TopN.AppendTopN(data, realCnt) + } + c.TopN.Sort() + return nil +} diff --git a/pkg/statistics/sample_test.go b/pkg/statistics/sample_test.go new file mode 100644 index 0000000000000..ea7bc8f47eb12 --- /dev/null +++ b/pkg/statistics/sample_test.go @@ -0,0 +1,319 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "math/rand" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" +) + +func recordSetForWeightSamplingTest(size int) *recordSet { + r := &recordSet{ + data: make([]types.Datum, 0, size), + count: size, + } + for i := 0; i < size; i++ { + r.data = append(r.data, types.NewIntDatum(int64(i))) + } + r.setFields(mysql.TypeLonglong) + return r +} + +func recordSetForDistributedSamplingTest(size, batch int) []*recordSet { + sets := make([]*recordSet, 0, batch) + batchSize := size / batch + for i := 0; i < batch; i++ { + r := &recordSet{ + data: make([]types.Datum, 0, batchSize), + count: batchSize, + } + for j := 0; j < size/batch; j++ { + r.data = append(r.data, types.NewIntDatum(int64(j+batchSize*i))) + } + r.setFields(mysql.TypeLonglong) + sets = append(sets, r) + } + return sets +} + +func TestWeightedSampling(t *testing.T) { + sampleNum := int64(20) + rowNum := 100 + loopCnt := 1000 + rs := recordSetForWeightSamplingTest(rowNum) + sc := mock.NewContext().GetSessionVars().StmtCtx + // The loop which is commented out is used for stability test. + // This test can run 800 times in a row without any failure. + // for x := 0; x < 800; x++ { + itemCnt := make([]int, rowNum) + for loopI := 0; loopI < loopCnt; loopI++ { + builder := &RowSampleBuilder{ + Sc: sc, + RecordSet: rs, + ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, + Collators: make([]collate.Collator, 1), + ColGroups: nil, + MaxSampleSize: int(sampleNum), + MaxFMSketchSize: 1000, + Rng: rand.New(rand.NewSource(time.Now().UnixNano())), + } + collector, err := builder.Collect() + require.NoError(t, err) + for i := 0; i < int(sampleNum); i++ { + a := collector.Base().Samples[i].Columns[0].GetInt64() + itemCnt[a]++ + } + require.Nil(t, rs.Close()) + } + expFrequency := float64(sampleNum) * float64(loopCnt) / float64(rowNum) + delta := 0.5 + for _, cnt := range itemCnt { + if float64(cnt) < expFrequency/(1+delta) || float64(cnt) > expFrequency*(1+delta) { + require.Truef(t, false, "The frequency %v is exceed the Chernoff Bound", cnt) + } + } +} + +func TestDistributedWeightedSampling(t *testing.T) { + sampleNum := int64(10) + rowNum := 100 + loopCnt := 1500 + batch := 5 + sets := recordSetForDistributedSamplingTest(rowNum, batch) + sc := mock.NewContext().GetSessionVars().StmtCtx + // The loop which is commented out is used for stability test. + // This test can run 800 times in a row without any failure. + // for x := 0; x < 800; x++ { + itemCnt := make([]int, rowNum) + for loopI := 1; loopI < loopCnt; loopI++ { + rootRowCollector := NewReservoirRowSampleCollector(int(sampleNum), 1) + rootRowCollector.FMSketches = append(rootRowCollector.FMSketches, NewFMSketch(1000)) + for i := 0; i < batch; i++ { + builder := &RowSampleBuilder{ + Sc: sc, + RecordSet: sets[i], + ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, + Collators: make([]collate.Collator, 1), + ColGroups: nil, + MaxSampleSize: int(sampleNum), + MaxFMSketchSize: 1000, + Rng: rand.New(rand.NewSource(time.Now().UnixNano())), + } + collector, err := builder.Collect() + require.NoError(t, err) + rootRowCollector.MergeCollector(collector) + require.Nil(t, sets[i].Close()) + } + for _, sample := range rootRowCollector.Samples { + itemCnt[sample.Columns[0].GetInt64()]++ + } + } + expFrequency := float64(sampleNum) * float64(loopCnt) / float64(rowNum) + delta := 0.5 + for _, cnt := range itemCnt { + if float64(cnt) < expFrequency/(1+delta) || float64(cnt) > expFrequency*(1+delta) { + require.Truef(t, false, "the frequency %v is exceed the Chernoff Bound", cnt) + } + } +} + +func TestBuildStatsOnRowSample(t *testing.T) { + ctx := mock.NewContext() + sketch := NewFMSketch(1000) + data := make([]*SampleItem, 0, 8) + for i := 1; i <= 1000; i++ { + d := types.NewIntDatum(int64(i)) + err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) + require.NoError(t, err) + data = append(data, &SampleItem{Value: d}) + } + for i := 1; i < 10; i++ { + d := types.NewIntDatum(int64(2)) + err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) + require.NoError(t, err) + data = append(data, &SampleItem{Value: d}) + } + for i := 1; i < 7; i++ { + d := types.NewIntDatum(int64(4)) + err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) + require.NoError(t, err) + data = append(data, &SampleItem{Value: d}) + } + for i := 1; i < 5; i++ { + d := types.NewIntDatum(int64(7)) + err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) + require.NoError(t, err) + data = append(data, &SampleItem{Value: d}) + } + for i := 1; i < 3; i++ { + d := types.NewIntDatum(int64(11)) + err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) + require.NoError(t, err) + data = append(data, &SampleItem{Value: d}) + } + collector := &SampleCollector{ + Samples: data, + NullCount: 0, + Count: int64(len(data)), + FMSketch: sketch, + TotalSize: int64(len(data)) * 8, + } + tp := types.NewFieldType(mysql.TypeLonglong) + hist, topN, err := BuildHistAndTopN(ctx, 5, 4, 1, collector, tp, true, nil) + require.Nilf(t, err, "%+v", err) + topNStr, err := topN.DecodedString(ctx, []byte{tp.GetType()}) + require.NoError(t, err) + require.Equal(t, "TopN{length: 4, [(2, 10), (4, 7), (7, 5), (11, 3)]}", topNStr) + require.Equal(t, "column:1 ndv:1000 totColSize:8168\n"+ + "num: 200 lower_bound: 1 upper_bound: 204 repeats: 1 ndv: 0\n"+ + "num: 200 lower_bound: 205 upper_bound: 404 repeats: 1 ndv: 0\n"+ + "num: 200 lower_bound: 405 upper_bound: 604 repeats: 1 ndv: 0\n"+ + "num: 200 lower_bound: 605 upper_bound: 804 repeats: 1 ndv: 0\n"+ + "num: 196 lower_bound: 805 upper_bound: 1000 repeats: 1 ndv: 0", hist.ToString(0)) +} + +type testSampleSuite struct { + count int + rs sqlexec.RecordSet +} + +func TestSampleSerial(t *testing.T) { + s := createTestSampleSuite() + t.Run("SubTestCollectColumnStats", SubTestCollectColumnStats(s)) + t.Run("SubTestMergeSampleCollector", SubTestMergeSampleCollector(s)) + t.Run("SubTestCollectorProtoConversion", SubTestCollectorProtoConversion(s)) +} + +func createTestSampleSuite() *testSampleSuite { + s := new(testSampleSuite) + s.count = 10000 + rs := &recordSet{ + data: make([]types.Datum, s.count), + count: s.count, + cursor: 0, + firstIsID: true, + } + rs.setFields(mysql.TypeLonglong, mysql.TypeLonglong) + start := 1000 // 1000 values is null + for i := start; i < rs.count; i++ { + rs.data[i].SetInt64(int64(i)) + } + for i := start; i < rs.count; i += 3 { + rs.data[i].SetInt64(rs.data[i].GetInt64() + 1) + } + for i := start; i < rs.count; i += 5 { + rs.data[i].SetInt64(rs.data[i].GetInt64() + 2) + } + s.rs = rs + return s +} + +func SubTestCollectColumnStats(s *testSampleSuite) func(*testing.T) { + return func(t *testing.T) { + sc := mock.NewContext().GetSessionVars().StmtCtx + builder := SampleBuilder{ + Sc: sc, + RecordSet: s.rs, + ColLen: 1, + PkBuilder: NewSortedBuilder(sc, 256, 1, types.NewFieldType(mysql.TypeLonglong), Version2), + MaxSampleSize: 10000, + MaxBucketSize: 256, + MaxFMSketchSize: 1000, + CMSketchWidth: 2048, + CMSketchDepth: 8, + Collators: make([]collate.Collator, 1), + ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, + } + require.Nil(t, s.rs.Close()) + collectors, pkBuilder, err := builder.CollectColumnStats() + require.NoError(t, err) + + require.Equal(t, int64(s.count), collectors[0].NullCount+collectors[0].Count) + require.Equal(t, int64(6232), collectors[0].FMSketch.NDV()) + require.Equal(t, uint64(collectors[0].Count), collectors[0].CMSketch.TotalCount()) + require.Equal(t, int64(s.count), pkBuilder.Count) + require.Equal(t, int64(s.count), pkBuilder.Hist().NDV) + } +} + +func SubTestMergeSampleCollector(s *testSampleSuite) func(*testing.T) { + return func(t *testing.T) { + builder := SampleBuilder{ + Sc: mock.NewContext().GetSessionVars().StmtCtx, + RecordSet: s.rs, + ColLen: 2, + MaxSampleSize: 1000, + MaxBucketSize: 256, + MaxFMSketchSize: 1000, + CMSketchWidth: 2048, + CMSketchDepth: 8, + Collators: make([]collate.Collator, 2), + ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong), types.NewFieldType(mysql.TypeLonglong)}, + } + require.Nil(t, s.rs.Close()) + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + collectors, pkBuilder, err := builder.CollectColumnStats() + require.NoError(t, err) + require.Nil(t, pkBuilder) + require.Len(t, collectors, 2) + collectors[0].IsMerger = true + collectors[0].MergeSampleCollector(sc, collectors[1]) + require.Equal(t, int64(9280), collectors[0].FMSketch.NDV()) + require.Len(t, collectors[0].Samples, 1000) + require.Equal(t, int64(1000), collectors[0].NullCount) + require.Equal(t, int64(19000), collectors[0].Count) + require.Equal(t, uint64(collectors[0].Count), collectors[0].CMSketch.TotalCount()) + } +} + +func SubTestCollectorProtoConversion(s *testSampleSuite) func(*testing.T) { + return func(t *testing.T) { + builder := SampleBuilder{ + Sc: mock.NewContext().GetSessionVars().StmtCtx, + RecordSet: s.rs, + ColLen: 2, + MaxSampleSize: 10000, + MaxBucketSize: 256, + MaxFMSketchSize: 1000, + CMSketchWidth: 2048, + CMSketchDepth: 8, + Collators: make([]collate.Collator, 2), + ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong), types.NewFieldType(mysql.TypeLonglong)}, + } + require.Nil(t, s.rs.Close()) + collectors, pkBuilder, err := builder.CollectColumnStats() + require.NoError(t, err) + require.Nil(t, pkBuilder) + for _, collector := range collectors { + p := SampleCollectorToProto(collector) + s := SampleCollectorFromProto(p) + require.Equal(t, s.Count, collector.Count) + require.Equal(t, s.NullCount, collector.NullCount) + require.Equal(t, s.CMSketch.TotalCount(), collector.CMSketch.TotalCount()) + require.Equal(t, s.FMSketch.NDV(), collector.FMSketch.NDV()) + require.Equal(t, s.TotalSize, collector.TotalSize) + require.Equal(t, len(s.Samples), len(collector.Samples)) + } + } +} diff --git a/statistics/scalar.go b/pkg/statistics/scalar.go similarity index 98% rename from statistics/scalar.go rename to pkg/statistics/scalar.go index 323a05ef0a112..efdabfe97c76d 100644 --- a/statistics/scalar.go +++ b/pkg/statistics/scalar.go @@ -19,10 +19,10 @@ import ( "math" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // calcFraction is used to calculate the fraction of the interval [lower, upper] that lies within the [lower, value] diff --git a/statistics/scalar_test.go b/pkg/statistics/scalar_test.go similarity index 99% rename from statistics/scalar_test.go rename to pkg/statistics/scalar_test.go index f3ef2947206d6..97ed17e438a72 100644 --- a/statistics/scalar_test.go +++ b/pkg/statistics/scalar_test.go @@ -18,8 +18,8 @@ import ( "math" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/statistics/statistics_test.go b/pkg/statistics/statistics_test.go similarity index 97% rename from statistics/statistics_test.go rename to pkg/statistics/statistics_test.go index ae77884315c91..d126d5922e8dc 100644 --- a/statistics/statistics_test.go +++ b/pkg/statistics/statistics_test.go @@ -21,18 +21,18 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/require" ) diff --git a/pkg/statistics/table.go b/pkg/statistics/table.go new file mode 100644 index 0000000000000..64f017e17a353 --- /dev/null +++ b/pkg/statistics/table.go @@ -0,0 +1,685 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package statistics + +import ( + "cmp" + "fmt" + "slices" + "strings" + "sync" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/ranger" + "go.uber.org/atomic" + "golang.org/x/exp/maps" +) + +const ( + // PseudoVersion means the pseudo statistics version is 0. + PseudoVersion uint64 = 0 + + // PseudoRowCount export for other pkg to use. + // When we haven't analyzed a table, we use pseudo statistics to estimate costs. + // It has row count 10000, equal condition selects 1/1000 of total rows, less condition selects 1/3 of total rows, + // between condition selects 1/40 of total rows. + PseudoRowCount = 10000 +) + +var ( + // Below functions are used to solve cycle import problem. + // Note: all functions below will be removed after finishing moving all estimation functions into the cardinality package. + + // GetRowCountByIndexRanges is a function type to get row count by index ranges. + GetRowCountByIndexRanges func(sctx sessionctx.Context, coll *HistColl, idxID int64, indexRanges []*ranger.Range) (result float64, err error) + + // GetRowCountByIntColumnRanges is a function type to get row count by int column ranges. + GetRowCountByIntColumnRanges func(sctx sessionctx.Context, coll *HistColl, colID int64, intRanges []*ranger.Range) (result float64, err error) + + // GetRowCountByColumnRanges is a function type to get row count by column ranges. + GetRowCountByColumnRanges func(sctx sessionctx.Context, coll *HistColl, colID int64, colRanges []*ranger.Range) (result float64, err error) +) + +// Table represents statistics for a table. +type Table struct { + ExtendedStats *ExtendedStatsColl + Name string + HistColl + Version uint64 + // TblInfoUpdateTS is the UpdateTS of the TableInfo used when filling this struct. + // It is the schema version of the corresponding table. It is used to skip redundant + // loading of stats, i.e, if the cached stats is already update-to-date with mysql.stats_xxx tables, + // and the schema of the table does not change, we don't need to load the stats for this + // table again. + TblInfoUpdateTS uint64 +} + +// ExtendedStatsItem is the cached item of a mysql.stats_extended record. +type ExtendedStatsItem struct { + StringVals string + ColIDs []int64 + ScalarVals float64 + Tp uint8 +} + +// ExtendedStatsColl is a collection of cached items for mysql.stats_extended records. +type ExtendedStatsColl struct { + Stats map[string]*ExtendedStatsItem + LastUpdateVersion uint64 +} + +// NewExtendedStatsColl allocate an ExtendedStatsColl struct. +func NewExtendedStatsColl() *ExtendedStatsColl { + return &ExtendedStatsColl{Stats: make(map[string]*ExtendedStatsItem)} +} + +const ( + // ExtendedStatsInited is the status for extended stats which are just registered but have not been analyzed yet. + ExtendedStatsInited uint8 = iota + // ExtendedStatsAnalyzed is the status for extended stats which have been collected in analyze. + ExtendedStatsAnalyzed + // ExtendedStatsDeleted is the status for extended stats which were dropped. These "deleted" records would be removed from storage by GCStats(). + ExtendedStatsDeleted +) + +// HistColl is a collection of histogram. It collects enough information for plan to calculate the selectivity. +type HistColl struct { + Columns map[int64]*Column + Indices map[int64]*Index + // Idx2ColumnIDs maps the index id to its column ids. It's used to calculate the selectivity in planner. + Idx2ColumnIDs map[int64][]int64 + // ColID2IdxIDs maps the column id to a list index ids whose first column is it. It's used to calculate the selectivity in planner. + ColID2IdxIDs map[int64][]int64 + PhysicalID int64 + // TODO: add AnalyzeCount here + RealtimeCount int64 // RealtimeCount is the current table row count, maintained by applying stats delta based on AnalyzeCount. + ModifyCount int64 // Total modify count in a table. + + // HavePhysicalID is true means this HistColl is from single table and have its ID's information. + // The physical id is used when try to load column stats from storage. + HavePhysicalID bool + Pseudo bool +} + +// TableMemoryUsage records tbl memory usage +type TableMemoryUsage struct { + ColumnsMemUsage map[int64]CacheItemMemoryUsage + IndicesMemUsage map[int64]CacheItemMemoryUsage + TableID int64 + TotalMemUsage int64 +} + +// TotalIdxTrackingMemUsage returns total indices' tracking memory usage +func (t *TableMemoryUsage) TotalIdxTrackingMemUsage() (sum int64) { + for _, idx := range t.IndicesMemUsage { + sum += idx.TrackingMemUsage() + } + return sum +} + +// TotalColTrackingMemUsage returns total columns' tracking memory usage +func (t *TableMemoryUsage) TotalColTrackingMemUsage() (sum int64) { + for _, col := range t.ColumnsMemUsage { + sum += col.TrackingMemUsage() + } + return sum +} + +// TotalTrackingMemUsage return total tracking memory usage +func (t *TableMemoryUsage) TotalTrackingMemUsage() int64 { + return t.TotalIdxTrackingMemUsage() + t.TotalColTrackingMemUsage() +} + +// TableCacheItem indicates the unit item stored in statsCache, eg: Column/Index +type TableCacheItem interface { + ItemID() int64 + MemoryUsage() CacheItemMemoryUsage + IsAllEvicted() bool + GetEvictedStatus() int + + DropUnnecessaryData() + IsStatsInitialized() bool + GetStatsVer() int64 +} + +// CacheItemMemoryUsage indicates the memory usage of TableCacheItem +type CacheItemMemoryUsage interface { + ItemID() int64 + TotalMemoryUsage() int64 + TrackingMemUsage() int64 + HistMemUsage() int64 + TopnMemUsage() int64 + CMSMemUsage() int64 +} + +// ColumnMemUsage records column memory usage +type ColumnMemUsage struct { + ColumnID int64 + HistogramMemUsage int64 + CMSketchMemUsage int64 + FMSketchMemUsage int64 + TopNMemUsage int64 + TotalMemUsage int64 +} + +// TotalMemoryUsage implements CacheItemMemoryUsage +func (c *ColumnMemUsage) TotalMemoryUsage() int64 { + return c.TotalMemUsage +} + +// ItemID implements CacheItemMemoryUsage +func (c *ColumnMemUsage) ItemID() int64 { + return c.ColumnID +} + +// TrackingMemUsage implements CacheItemMemoryUsage +func (c *ColumnMemUsage) TrackingMemUsage() int64 { + return c.CMSketchMemUsage + c.TopNMemUsage + c.HistogramMemUsage +} + +// HistMemUsage implements CacheItemMemoryUsage +func (c *ColumnMemUsage) HistMemUsage() int64 { + return c.HistogramMemUsage +} + +// TopnMemUsage implements CacheItemMemoryUsage +func (c *ColumnMemUsage) TopnMemUsage() int64 { + return c.TopNMemUsage +} + +// CMSMemUsage implements CacheItemMemoryUsage +func (c *ColumnMemUsage) CMSMemUsage() int64 { + return c.CMSketchMemUsage +} + +// IndexMemUsage records index memory usage +type IndexMemUsage struct { + IndexID int64 + HistogramMemUsage int64 + CMSketchMemUsage int64 + TopNMemUsage int64 + TotalMemUsage int64 +} + +// TotalMemoryUsage implements CacheItemMemoryUsage +func (c *IndexMemUsage) TotalMemoryUsage() int64 { + return c.TotalMemUsage +} + +// ItemID implements CacheItemMemoryUsage +func (c *IndexMemUsage) ItemID() int64 { + return c.IndexID +} + +// TrackingMemUsage implements CacheItemMemoryUsage +func (c *IndexMemUsage) TrackingMemUsage() int64 { + return c.CMSketchMemUsage + c.TopNMemUsage + c.HistogramMemUsage +} + +// HistMemUsage implements CacheItemMemoryUsage +func (c *IndexMemUsage) HistMemUsage() int64 { + return c.HistogramMemUsage +} + +// TopnMemUsage implements CacheItemMemoryUsage +func (c *IndexMemUsage) TopnMemUsage() int64 { + return c.TopNMemUsage +} + +// CMSMemUsage implements CacheItemMemoryUsage +func (c *IndexMemUsage) CMSMemUsage() int64 { + return c.CMSketchMemUsage +} + +// MemoryUsage returns the total memory usage of this Table. +// it will only calc the size of Columns and Indices stats data of table. +// We ignore the size of other metadata in Table +func (t *Table) MemoryUsage() *TableMemoryUsage { + tMemUsage := &TableMemoryUsage{ + TableID: t.PhysicalID, + ColumnsMemUsage: make(map[int64]CacheItemMemoryUsage), + IndicesMemUsage: make(map[int64]CacheItemMemoryUsage), + } + for _, col := range t.Columns { + if col != nil { + colMemUsage := col.MemoryUsage() + tMemUsage.ColumnsMemUsage[colMemUsage.ItemID()] = colMemUsage + tMemUsage.TotalMemUsage += colMemUsage.TotalMemoryUsage() + } + } + for _, index := range t.Indices { + if index != nil { + idxMemUsage := index.MemoryUsage() + tMemUsage.IndicesMemUsage[idxMemUsage.ItemID()] = idxMemUsage + tMemUsage.TotalMemUsage += idxMemUsage.TotalMemoryUsage() + } + } + return tMemUsage +} + +// Copy copies the current table. +func (t *Table) Copy() *Table { + newHistColl := HistColl{ + PhysicalID: t.PhysicalID, + HavePhysicalID: t.HavePhysicalID, + RealtimeCount: t.RealtimeCount, + Columns: make(map[int64]*Column, len(t.Columns)), + Indices: make(map[int64]*Index, len(t.Indices)), + Pseudo: t.Pseudo, + ModifyCount: t.ModifyCount, + } + for id, col := range t.Columns { + newHistColl.Columns[id] = col.Copy() + } + for id, idx := range t.Indices { + newHistColl.Indices[id] = idx.Copy() + } + nt := &Table{ + HistColl: newHistColl, + Version: t.Version, + Name: t.Name, + TblInfoUpdateTS: t.TblInfoUpdateTS, + } + if t.ExtendedStats != nil { + newExtStatsColl := &ExtendedStatsColl{ + Stats: make(map[string]*ExtendedStatsItem), + LastUpdateVersion: t.ExtendedStats.LastUpdateVersion, + } + for name, item := range t.ExtendedStats.Stats { + newExtStatsColl.Stats[name] = item + } + nt.ExtendedStats = newExtStatsColl + } + return nt +} + +// ShallowCopy copies the current table. +// It's different from Copy(). Only the struct Table (and also the embedded HistColl) is copied here. +// The internal containers, like t.Columns and t.Indices, and the stats, like TopN and Histogram are not copied. +func (t *Table) ShallowCopy() *Table { + newHistColl := HistColl{ + PhysicalID: t.PhysicalID, + HavePhysicalID: t.HavePhysicalID, + RealtimeCount: t.RealtimeCount, + Columns: t.Columns, + Indices: t.Indices, + Pseudo: t.Pseudo, + ModifyCount: t.ModifyCount, + } + nt := &Table{ + HistColl: newHistColl, + Version: t.Version, + Name: t.Name, + TblInfoUpdateTS: t.TblInfoUpdateTS, + ExtendedStats: t.ExtendedStats, + } + return nt +} + +// String implements Stringer interface. +func (t *Table) String() string { + strs := make([]string, 0, len(t.Columns)+1) + strs = append(strs, fmt.Sprintf("Table:%d RealtimeCount:%d", t.PhysicalID, t.RealtimeCount)) + cols := make([]*Column, 0, len(t.Columns)) + for _, col := range t.Columns { + cols = append(cols, col) + } + slices.SortFunc(cols, func(i, j *Column) int { return cmp.Compare(i.ID, j.ID) }) + for _, col := range cols { + strs = append(strs, col.String()) + } + idxs := make([]*Index, 0, len(t.Indices)) + for _, idx := range t.Indices { + idxs = append(idxs, idx) + } + slices.SortFunc(idxs, func(i, j *Index) int { return cmp.Compare(i.ID, j.ID) }) + for _, idx := range idxs { + strs = append(strs, idx.String()) + } + // TODO: concat content of ExtendedStatsColl + return strings.Join(strs, "\n") +} + +// IndexStartWithColumn finds the first index whose first column is the given column. +func (t *Table) IndexStartWithColumn(colName string) *Index { + for _, index := range t.Indices { + if index.Info.Columns[0].Name.L == colName { + return index + } + } + return nil +} + +// ColumnByName finds the statistics.Column for the given column. +func (t *Table) ColumnByName(colName string) *Column { + for _, c := range t.Columns { + if c.Info.Name.L == colName { + return c + } + } + return nil +} + +// GetStatsInfo returns their statistics according to the ID of the column or index, including histogram, CMSketch, TopN and FMSketch. +// +// needCopy: In order to protect the item in the cache from being damaged, we need to copy the item. +func (t *Table) GetStatsInfo(id int64, isIndex bool, needCopy bool) (*Histogram, *CMSketch, *TopN, *FMSketch, bool) { + if isIndex { + if idxStatsInfo, ok := t.Indices[id]; ok { + if needCopy { + return idxStatsInfo.Histogram.Copy(), + idxStatsInfo.CMSketch.Copy(), idxStatsInfo.TopN.Copy(), idxStatsInfo.FMSketch.Copy(), true + } + return &idxStatsInfo.Histogram, + idxStatsInfo.CMSketch, idxStatsInfo.TopN, idxStatsInfo.FMSketch, true + } + // newly added index which is not analyzed yet + return nil, nil, nil, nil, false + } + if colStatsInfo, ok := t.Columns[id]; ok { + if needCopy { + return colStatsInfo.Histogram.Copy(), colStatsInfo.CMSketch.Copy(), + colStatsInfo.TopN.Copy(), colStatsInfo.FMSketch.Copy(), true + } + return &colStatsInfo.Histogram, colStatsInfo.CMSketch, + colStatsInfo.TopN, colStatsInfo.FMSketch, true + } + // newly added column which is not analyzed yet + return nil, nil, nil, nil, false +} + +// GetAnalyzeRowCount tries to get the row count of a column or an index if possible. +// This method is useful because this row count doesn't consider the modify count. +func (coll *HistColl) GetAnalyzeRowCount() float64 { + ids := maps.Keys(coll.Columns) + slices.Sort(ids) + for _, id := range ids { + col := coll.Columns[id] + if col != nil && col.IsFullLoad() { + return col.TotalRowCount() + } + } + ids = maps.Keys(coll.Indices) + slices.Sort(ids) + for _, id := range ids { + idx := coll.Indices[id] + if idx == nil { + continue + } + if idx.Info != nil && idx.Info.MVIndex { + continue + } + if idx.IsFullLoad() { + return idx.TotalRowCount() + } + } + return -1 +} + +// GetStatsHealthy calculates stats healthy if the table stats is not pseudo. +// If the table stats is pseudo, it returns 0, false, otherwise it returns stats healthy, true. +func (t *Table) GetStatsHealthy() (int64, bool) { + if t == nil || t.Pseudo { + return 0, false + } + var healthy int64 + count := float64(t.RealtimeCount) + if histCount := t.GetAnalyzeRowCount(); histCount > 0 { + count = histCount + } + if float64(t.ModifyCount) < count { + healthy = int64((1.0 - float64(t.ModifyCount)/count) * 100.0) + } else if t.ModifyCount == 0 { + healthy = 100 + } + return healthy, true +} + +type neededStatsMap struct { + items map[model.TableItemID]struct{} + m sync.RWMutex +} + +func (n *neededStatsMap) AllItems() []model.TableItemID { + n.m.RLock() + keys := make([]model.TableItemID, 0, len(n.items)) + for key := range n.items { + keys = append(keys, key) + } + n.m.RUnlock() + return keys +} + +func (n *neededStatsMap) insert(col model.TableItemID) { + n.m.Lock() + n.items[col] = struct{}{} + n.m.Unlock() +} + +func (n *neededStatsMap) Delete(col model.TableItemID) { + n.m.Lock() + delete(n.items, col) + n.m.Unlock() +} + +func (n *neededStatsMap) Length() int { + n.m.RLock() + defer n.m.RUnlock() + return len(n.items) +} + +// RatioOfPseudoEstimate means if modifyCount / statsTblCount is greater than this ratio, we think the stats is invalid +// and use pseudo estimation. +var RatioOfPseudoEstimate = atomic.NewFloat64(0.7) + +// IsInitialized returns true if any column/index stats of the table is initialized. +func (t *Table) IsInitialized() bool { + for _, col := range t.Columns { + if col != nil && col.IsStatsInitialized() { + return true + } + } + for _, idx := range t.Indices { + if idx != nil && idx.IsStatsInitialized() { + return true + } + } + return false +} + +// IsOutdated returns true if the table stats is outdated. +func (t *Table) IsOutdated() bool { + rowcount := t.GetAnalyzeRowCount() + if rowcount < 0 { + rowcount = float64(t.RealtimeCount) + } + if rowcount > 0 && float64(t.ModifyCount)/rowcount > RatioOfPseudoEstimate.Load() { + return true + } + return false +} + +// ReleaseAndPutToPool releases data structures of Table and put itself back to pool. +func (t *Table) ReleaseAndPutToPool() { + for _, col := range t.Columns { + col.FMSketch.DestroyAndPutToPool() + } + maps.Clear(t.Columns) + for _, idx := range t.Indices { + idx.FMSketch.DestroyAndPutToPool() + } + maps.Clear(t.Indices) +} + +// ID2UniqueID generates a new HistColl whose `Columns` is built from UniqueID of given columns. +func (coll *HistColl) ID2UniqueID(columns []*expression.Column) *HistColl { + cols := make(map[int64]*Column) + for _, col := range columns { + colHist, ok := coll.Columns[col.ID] + if ok { + cols[col.UniqueID] = colHist + } + } + newColl := &HistColl{ + PhysicalID: coll.PhysicalID, + HavePhysicalID: coll.HavePhysicalID, + Pseudo: coll.Pseudo, + RealtimeCount: coll.RealtimeCount, + ModifyCount: coll.ModifyCount, + Columns: cols, + } + return newColl +} + +// GenerateHistCollFromColumnInfo generates a new HistColl whose ColID2IdxIDs and IdxID2ColIDs is built from the given parameter. +func (coll *HistColl) GenerateHistCollFromColumnInfo(tblInfo *model.TableInfo, columns []*expression.Column) *HistColl { + newColHistMap := make(map[int64]*Column) + colInfoID2UniqueID := make(map[int64]int64, len(columns)) + idxID2idxInfo := make(map[int64]*model.IndexInfo) + for _, col := range columns { + colInfoID2UniqueID[col.ID] = col.UniqueID + } + for id, colHist := range coll.Columns { + uniqueID, ok := colInfoID2UniqueID[id] + // Collect the statistics by the given columns. + if ok { + newColHistMap[uniqueID] = colHist + } + } + for _, idxInfo := range tblInfo.Indices { + idxID2idxInfo[idxInfo.ID] = idxInfo + } + newIdxHistMap := make(map[int64]*Index) + idx2Columns := make(map[int64][]int64) + colID2IdxIDs := make(map[int64][]int64) + for id, idxHist := range coll.Indices { + idxInfo := idxID2idxInfo[id] + if idxInfo == nil { + continue + } + ids := make([]int64, 0, len(idxInfo.Columns)) + for _, idxCol := range idxInfo.Columns { + uniqueID, ok := colInfoID2UniqueID[tblInfo.Columns[idxCol.Offset].ID] + if !ok { + break + } + ids = append(ids, uniqueID) + } + // If the length of the id list is 0, this index won't be used in this query. + if len(ids) == 0 { + continue + } + colID2IdxIDs[ids[0]] = append(colID2IdxIDs[ids[0]], idxHist.ID) + newIdxHistMap[idxHist.ID] = idxHist + idx2Columns[idxHist.ID] = ids + } + for _, idxIDs := range colID2IdxIDs { + slices.Sort(idxIDs) + } + newColl := &HistColl{ + PhysicalID: coll.PhysicalID, + HavePhysicalID: coll.HavePhysicalID, + Pseudo: coll.Pseudo, + RealtimeCount: coll.RealtimeCount, + ModifyCount: coll.ModifyCount, + Columns: newColHistMap, + Indices: newIdxHistMap, + ColID2IdxIDs: colID2IdxIDs, + Idx2ColumnIDs: idx2Columns, + } + return newColl +} + +// PseudoTable creates a pseudo table statistics. +// Usually, we don't want to trigger stats loading for pseudo table. +// But there are exceptional cases. In such cases, we should pass allowTriggerLoading as true. +// Such case could possibly happen in getStatsTable(). +func PseudoTable(tblInfo *model.TableInfo, allowTriggerLoading bool) *Table { + const fakePhysicalID int64 = -1 + pseudoHistColl := HistColl{ + RealtimeCount: PseudoRowCount, + PhysicalID: tblInfo.ID, + HavePhysicalID: true, + Columns: make(map[int64]*Column, len(tblInfo.Columns)), + Indices: make(map[int64]*Index, len(tblInfo.Indices)), + Pseudo: true, + } + t := &Table{ + HistColl: pseudoHistColl, + } + for _, col := range tblInfo.Columns { + // The column is public to use. Also we should check the column is not hidden since hidden means that it's used by expression index. + // We would not collect stats for the hidden column and we won't use the hidden column to estimate. + // Thus we don't create pseudo stats for it. + if col.State == model.StatePublic && !col.Hidden { + t.Columns[col.ID] = &Column{ + PhysicalID: fakePhysicalID, + Info: col, + IsHandle: tblInfo.PKIsHandle && mysql.HasPriKeyFlag(col.GetFlag()), + Histogram: *NewHistogram(col.ID, 0, 0, 0, &col.FieldType, 0, 0), + } + if allowTriggerLoading { + t.Columns[col.ID].PhysicalID = tblInfo.ID + } + } + } + for _, idx := range tblInfo.Indices { + if idx.State == model.StatePublic { + t.Indices[idx.ID] = &Index{ + PhysicalID: fakePhysicalID, + Info: idx, + Histogram: *NewHistogram(idx.ID, 0, 0, 0, types.NewFieldType(mysql.TypeBlob), 0, 0), + } + if allowTriggerLoading { + t.Indices[idx.ID].PhysicalID = tblInfo.ID + } + } + } + return t +} + +// CheckAnalyzeVerOnTable checks whether the given version is the one from the tbl. +// If not, it will return false and set the version to the tbl's. +// We use this check to make sure all the statistics of the table are in the same version. +func CheckAnalyzeVerOnTable(tbl *Table, version *int) bool { + for _, col := range tbl.Columns { + if !col.IsAnalyzed() { + continue + } + if col.StatsVer != int64(*version) { + *version = int(col.StatsVer) + return false + } + // If we found one column and the version is the same, we can directly return since all the versions from this table is the same. + return true + } + for _, idx := range tbl.Indices { + if !idx.IsAnalyzed() { + continue + } + if idx.StatsVer != int64(*version) { + *version = int(idx.StatsVer) + return false + } + // If we found one column and the version is the same, we can directly return since all the versions from this table is the same. + return true + } + // This table has no statistics yet. We can directly return true. + return true +} diff --git a/statistics/testdata/integration_suite_in.json b/pkg/statistics/testdata/integration_suite_in.json similarity index 100% rename from statistics/testdata/integration_suite_in.json rename to pkg/statistics/testdata/integration_suite_in.json diff --git a/statistics/testdata/integration_suite_out.json b/pkg/statistics/testdata/integration_suite_out.json similarity index 100% rename from statistics/testdata/integration_suite_out.json rename to pkg/statistics/testdata/integration_suite_out.json diff --git a/pkg/store/BUILD.bazel b/pkg/store/BUILD.bazel new file mode 100644 index 0000000000000..a62f479d4d58e --- /dev/null +++ b/pkg/store/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "store", + srcs = ["store.go"], + importpath = "github.com/pingcap/tidb/pkg/store", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/pdpb", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "store_test", + timeout = "short", + srcs = [ + "batch_coprocessor_test.go", + "main_test.go", + "store_test.go", + ], + embed = [":store"], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//testutils", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/batch_coprocessor_test.go b/pkg/store/batch_coprocessor_test.go new file mode 100644 index 0000000000000..df8a021c721de --- /dev/null +++ b/pkg/store/batch_coprocessor_test.go @@ -0,0 +1,118 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package store + +import ( + "context" + "fmt" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" +) + +func createMockTiKVStoreOptions(tiflashNum int) []mockstore.MockTiKVStoreOption { + return []mockstore.MockTiKVStoreOption{ + mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockCluster := c.(*unistore.Cluster) + _, _, region1 := mockstore.BootstrapWithSingleStore(c) + tiflashIdx := 0 + for tiflashIdx < tiflashNum { + store2 := c.AllocID() + peer2 := c.AllocID() + addr2 := fmt.Sprintf("tiflash%d", tiflashIdx) + mockCluster.AddStore(store2, addr2, &metapb.StoreLabel{Key: "engine", Value: "tiflash"}) + mockCluster.AddPeer(region1, store2, peer2) + tiflashIdx++ + } + }), + mockstore.WithStoreType(mockstore.EmbedUnistore), + } +} + +func TestStoreErr(t *testing.T) { + store := testkit.CreateMockStore(t, createMockTiKVStoreOptions(1)...) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) + }() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int not null, b int not null)") + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tk.MustExec("insert into t values(1,0)") + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") + tk.MustExec("set @@session.tidb_allow_mpp=OFF") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopCancelled", "1*return(true)")) + + err = tk.QueryToErr("select count(*) from t") + require.Equal(t, context.Canceled, errors.Cause(err)) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0", "1*return(\"tiflash0\")")) + + tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) + err = tk.QueryToErr("select count(*) from t") + require.Error(t, err) +} + +func TestStoreSwitchPeer(t *testing.T) { + store := testkit.CreateMockStore(t, createMockTiKVStoreOptions(2)...) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`)) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount")) + }() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(a int not null, b int not null)") + tk.MustExec("alter table t set tiflash replica 1") + tb := external.GetTableByName(t, tk, "test", "t") + tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") + + err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) + require.NoError(t, err) + + tk.MustExec("insert into t values(1,0)") + tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") + tk.MustExec("set @@session.tidb_allow_mpp=OFF") + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) + + tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) + + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/mockstore/unistore/BatchCopRpcErrtiflash1", "return(\"tiflash1\")")) + err = tk.QueryToErr("select count(*) from t") + require.Error(t, err) +} diff --git a/pkg/store/copr/BUILD.bazel b/pkg/store/copr/BUILD.bazel new file mode 100644 index 0000000000000..9ad1a7250ccac --- /dev/null +++ b/pkg/store/copr/BUILD.bazel @@ -0,0 +1,105 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "copr", + srcs = [ + "batch_coprocessor.go", + "batch_request_sender.go", + "coprocessor.go", + "coprocessor_cache.go", + "key_ranges.go", + "mpp.go", + "mpp_probe.go", + "region_cache.go", + "store.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/copr", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/ddl/placement", + "//pkg/domain/infosync", + "//pkg/domain/resourcegroup", + "//pkg/errno", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/terror", + "//pkg/sessionctx/variable", + "//pkg/store/copr/metrics", + "//pkg/store/driver/backoff", + "//pkg/store/driver/error", + "//pkg/store/driver/options", + "//pkg/util", + "//pkg/util/execdetails", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "//pkg/util/paging", + "//pkg/util/tiflash", + "//pkg/util/tiflashcompute", + "//pkg/util/tracing", + "//pkg/util/trxevents", + "@com_github_dgraph_io_ristretto//:ristretto", + "@com_github_gogo_protobuf//proto", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//metrics", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@com_github_tikv_client_go_v2//txnkv/txnlock", + "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@com_github_twmb_murmur3//:murmur3", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//status", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "copr_test", + timeout = "short", + srcs = [ + "batch_coprocessor_test.go", + "coprocessor_cache_test.go", + "coprocessor_test.go", + "key_ranges_test.go", + "main_test.go", + "mpp_probe_test.go", + ], + embed = [":copr"], + flaky = True, + race = "on", + shard_count = 28, + deps = [ + "//pkg/kv", + "//pkg/store/driver/backoff", + "//pkg/testkit/testsetup", + "//pkg/util/logutil", + "//pkg/util/paging", + "//pkg/util/trxevents", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_stathat_consistent//:consistent", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/store/copr/batch_coprocessor.go b/pkg/store/copr/batch_coprocessor.go similarity index 99% rename from store/copr/batch_coprocessor.go rename to pkg/store/copr/batch_coprocessor.go index 293262254abb3..0f30348d310a5 100644 --- a/store/copr/batch_coprocessor.go +++ b/pkg/store/copr/batch_coprocessor.go @@ -34,16 +34,16 @@ import ( "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/driver/backoff" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/tiflashcompute" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" "github.com/tikv/client-go/v2/metrics" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/pkg/store/copr/batch_coprocessor_test.go b/pkg/store/copr/batch_coprocessor_test.go new file mode 100644 index 0000000000000..e94d2c17effe9 --- /dev/null +++ b/pkg/store/copr/batch_coprocessor_test.go @@ -0,0 +1,284 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "context" + "math/rand" + "sort" + "strconv" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stathat/consistent" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" +) + +// StoreID: [1, storeCount] +func buildStoreTaskMap(storeCount int) map[uint64]*batchCopTask { + storeTasks := make(map[uint64]*batchCopTask) + for i := 0; i < storeCount; i++ { + storeTasks[uint64(i+1)] = &batchCopTask{} + } + return storeTasks +} + +func buildRegionInfos(storeCount, regionCount, replicaNum int) []RegionInfo { + var ss []string + for i := 0; i < regionCount; i++ { + s := strconv.Itoa(i) + ss = append(ss, s) + } + sort.Strings(ss) + + storeIDExist := func(storeID uint64, storeIDs []uint64) bool { + for _, i := range storeIDs { + if i == storeID { + return true + } + } + return false + } + + randomStores := func(storeCount, replicaNum int) []uint64 { + var storeIDs []uint64 + for len(storeIDs) < replicaNum { + t := uint64(rand.Intn(storeCount) + 1) + if storeIDExist(t, storeIDs) { + continue + } + storeIDs = append(storeIDs, t) + } + return storeIDs + } + + var startKey string + regionInfos := make([]RegionInfo, 0, len(ss)) + for i, s := range ss { + var ri RegionInfo + ri.Region = tikv.NewRegionVerID(uint64(i), 1, 1) + ri.Meta = nil + ri.AllStores = randomStores(storeCount, replicaNum) + + var keyRange kv.KeyRange + if len(startKey) == 0 { + keyRange.StartKey = nil + } else { + keyRange.StartKey = kv.Key(startKey) + } + keyRange.EndKey = kv.Key(s) + ri.Ranges = NewKeyRanges([]kv.KeyRange{keyRange}) + regionInfos = append(regionInfos, ri) + startKey = s + } + return regionInfos +} + +func calcReginCount(tasks []*batchCopTask) int { + count := 0 + for _, task := range tasks { + count += len(task.regionInfos) + } + return count +} + +func TestBalanceBatchCopTaskWithContinuity(t *testing.T) { + for replicaNum := 1; replicaNum < 6; replicaNum++ { + storeCount := 10 + regionCount := 100000 + storeTasks := buildStoreTaskMap(storeCount) + regionInfos := buildRegionInfos(storeCount, regionCount, replicaNum) + tasks, score := balanceBatchCopTaskWithContinuity(storeTasks, regionInfos, 20) + require.True(t, isBalance(score)) + require.Equal(t, regionCount, calcReginCount(tasks)) + } + + { + storeCount := 10 + regionCount := 100 + replicaNum := 2 + storeTasks := buildStoreTaskMap(storeCount) + regionInfos := buildRegionInfos(storeCount, regionCount, replicaNum) + tasks, _ := balanceBatchCopTaskWithContinuity(storeTasks, regionInfos, 20) + require.True(t, tasks == nil) + } +} + +func TestBalanceBatchCopTaskWithEmptyTaskSet(t *testing.T) { + { + var nilTaskSet []*batchCopTask + nilResult := balanceBatchCopTask(nil, nil, nilTaskSet, false, 0) + require.True(t, nilResult == nil) + } + + { + emptyTaskSet := make([]*batchCopTask, 0) + emptyResult := balanceBatchCopTask(nil, nil, emptyTaskSet, false, 0) + require.True(t, emptyResult != nil) + require.True(t, len(emptyResult) == 0) + } +} + +func TestDeepCopyStoreTaskMap(t *testing.T) { + storeTasks1 := buildStoreTaskMap(10) + for _, task := range storeTasks1 { + task.regionInfos = append(task.regionInfos, RegionInfo{}) + } + + storeTasks2 := deepCopyStoreTaskMap(storeTasks1) + for _, task := range storeTasks2 { + task.regionInfos = append(task.regionInfos, RegionInfo{}) + } + + for _, task := range storeTasks1 { + require.Equal(t, 1, len(task.regionInfos)) + } + + for _, task := range storeTasks2 { + require.Equal(t, 2, len(task.regionInfos)) + } +} + +// Make sure no duplicated ip:addr. +func generateOneAddr() string { + var ip string + for i := 0; i < 4; i++ { + if i != 0 { + ip += "." + } + ip += strconv.Itoa(rand.Intn(255)) + } + return ip + ":" + strconv.Itoa(rand.Intn(65535)) +} + +func generateDifferentAddrs(num int) (res []string) { + addrMap := make(map[string]struct{}) + for len(addrMap) < num { + addr := generateOneAddr() + if _, ok := addrMap[addr]; !ok { + addrMap[addr] = struct{}{} + } + } + for addr := range addrMap { + res = append(res, addr) + } + return +} + +func TestConsistentHash(t *testing.T) { + allAddrs := generateDifferentAddrs(100) + + computeNodes := allAddrs[:30] + storageNodes := allAddrs[30:] + firstRoundMap := make(map[string]string) + for round := 0; round < 100; round++ { + hasher := consistent.New() + rand.Shuffle(len(computeNodes), func(i, j int) { + computeNodes[i], computeNodes[j] = computeNodes[j], computeNodes[i] + }) + for _, computeNode := range computeNodes { + hasher.Add(computeNode) + } + for _, storageNode := range storageNodes { + computeNode, err := hasher.Get(storageNode) + require.NoError(t, err) + if round == 0 { + firstRoundMap[storageNode] = computeNode + } else { + firstRoundAddr, ok := firstRoundMap[storageNode] + require.True(t, ok) + require.Equal(t, firstRoundAddr, computeNode) + } + } + } +} + +func TestDispatchPolicyRR(t *testing.T) { + allAddrs := generateDifferentAddrs(100) + for i := 0; i < 100; i++ { + regCnt := rand.Intn(10000) + regIDs := make([]tikv.RegionVerID, 0, regCnt) + for i := 0; i < regCnt; i++ { + regIDs = append(regIDs, tikv.NewRegionVerID(uint64(i), 0, 0)) + } + + rpcCtxs, err := getTiFlashComputeRPCContextByRoundRobin(regIDs, allAddrs) + require.NoError(t, err) + require.Equal(t, len(rpcCtxs), len(regIDs)) + checkMap := make(map[string]int, len(rpcCtxs)) + for _, c := range rpcCtxs { + if v, ok := checkMap[c.Addr]; !ok { + checkMap[c.Addr] = 1 + } else { + checkMap[c.Addr] = v + 1 + } + } + actCnt := 0 + for _, v := range checkMap { + actCnt += v + } + require.Equal(t, regCnt, actCnt) + if len(regIDs) < len(allAddrs) { + require.Equal(t, len(regIDs), len(checkMap)) + exp := -1 + for _, v := range checkMap { + if exp == -1 { + exp = v + } else { + require.Equal(t, exp, v) + } + } + } else { + // Using RR, it means region cnt for each tiflash_compute node should be almost same. + minV := regCnt + for _, v := range checkMap { + if v < minV { + minV = v + } + } + for k, v := range checkMap { + checkMap[k] = v - minV + } + for _, v := range checkMap { + require.True(t, v == 0 || v == 1) + } + } + } +} + +func TestTopoFetcherBackoff(t *testing.T) { + fetchTopoBo := backoff.NewBackofferWithVars(context.Background(), fetchTopoMaxBackoff, nil) + expectErr := errors.New("Cannot find proper topo from AutoScaler") + var retryNum int + start := time.Now() + for { + retryNum++ + if err := fetchTopoBo.Backoff(tikv.BoTiFlashRPC(), expectErr); err != nil { + break + } + logutil.BgLogger().Info("TestTopoFetcherBackoff", zap.Any("retryNum", retryNum)) + } + dura := time.Since(start) + // fetchTopoMaxBackoff is milliseconds. + require.GreaterOrEqual(t, dura, time.Duration(fetchTopoMaxBackoff*1000)) + require.GreaterOrEqual(t, dura, 30*time.Second) + require.LessOrEqual(t, dura, 50*time.Second) +} diff --git a/store/copr/batch_request_sender.go b/pkg/store/copr/batch_request_sender.go similarity index 99% rename from store/copr/batch_request_sender.go rename to pkg/store/copr/batch_request_sender.go index 2e2df9bd10076..52ae8735925c1 100644 --- a/store/copr/batch_request_sender.go +++ b/pkg/store/copr/batch_request_sender.go @@ -21,7 +21,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/pkg/store/copr/copr_test/BUILD.bazel b/pkg/store/copr/copr_test/BUILD.bazel new file mode 100644 index 0000000000000..0c99139d846ff --- /dev/null +++ b/pkg/store/copr/copr_test/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "copr_test_test", + timeout = "short", + srcs = [ + "coprocessor_test.go", + "main_test.go", + ], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/store/copr", + "//pkg/store/mockstore", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/copr/copr_test/coprocessor_test.go b/pkg/store/copr/copr_test/coprocessor_test.go new file mode 100644 index 0000000000000..72505d0c68357 --- /dev/null +++ b/pkg/store/copr/copr_test/coprocessor_test.go @@ -0,0 +1,184 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package copr_test + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" +) + +func TestBuildCopIteratorWithRowCountHint(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithMultiRegions(c, []byte("g"), []byte("n"), []byte("t")) + }), + ) + require.NoError(t, err) + defer require.NoError(t, store.Close()) + copClient := store.GetClient().(*copr.CopClient) + ctx := context.Background() + killed := uint32(0) + vars := kv.NewVariables(&killed) + opt := &kv.ClientSendOption{} + + ranges := copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") + req := &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, copr.CopSmallTaskRow}), + Concurrency: 15, + } + it, errRes := copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + conc, smallConc := it.GetConcurrency() + rateLimit := it.GetSendRate() + require.Equal(t, conc, 1) + require.Equal(t, smallConc, 1) + require.Equal(t, rateLimit.GetCapacity(), 2) + + ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), + Concurrency: 15, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + conc, smallConc = it.GetConcurrency() + rateLimit = it.GetSendRate() + require.Equal(t, conc, 1) + require.Equal(t, smallConc, 2) + require.Equal(t, rateLimit.GetCapacity(), 3) + + // cross-region long range + ranges = copr.BuildKeyRanges("a", "z") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{10}), + Concurrency: 15, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + conc, smallConc = it.GetConcurrency() + rateLimit = it.GetSendRate() + require.Equal(t, conc, 1) + require.Equal(t, smallConc, 2) + require.Equal(t, rateLimit.GetCapacity(), 3) + + ranges = copr.BuildKeyRanges("a", "z") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{copr.CopSmallTaskRow + 1}), + Concurrency: 15, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + conc, smallConc = it.GetConcurrency() + rateLimit = it.GetSendRate() + require.Equal(t, conc, 4) + require.Equal(t, smallConc, 0) + require.Equal(t, rateLimit.GetCapacity(), 4) +} + +func TestBuildCopIteratorWithBatchStoreCopr(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + store, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithMultiRegions(c, []byte("g"), []byte("n"), []byte("t")) + }), + ) + require.NoError(t, err) + defer require.NoError(t, store.Close()) + copClient := store.GetClient().(*copr.CopClient) + ctx := context.Background() + killed := uint32(0) + vars := kv.NewVariables(&killed) + opt := &kv.ClientSendOption{} + + ranges := copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") + req := &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), + Concurrency: 15, + StoreBatchSize: 1, + } + it, errRes := copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + tasks := it.GetTasks() + require.Equal(t, len(tasks), 2) + require.Equal(t, len(tasks[0].ToPBBatchTasks()), 1) + require.Equal(t, tasks[0].RowCountHint, 5) + require.Equal(t, len(tasks[1].ToPBBatchTasks()), 1) + require.Equal(t, tasks[1].RowCountHint, 9) + + ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), + Concurrency: 15, + StoreBatchSize: 3, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + tasks = it.GetTasks() + require.Equal(t, len(tasks), 1) + require.Equal(t, len(tasks[0].ToPBBatchTasks()), 3) + require.Equal(t, tasks[0].RowCountHint, 14) + + // paging will disable store batch. + ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), + Concurrency: 15, + StoreBatchSize: 3, + Paging: struct { + Enable bool + MinPagingSize uint64 + MaxPagingSize uint64 + }{ + Enable: true, + MinPagingSize: 1, + MaxPagingSize: 1024, + }, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + tasks = it.GetTasks() + require.Equal(t, len(tasks), 4) + + // only small tasks will be batched. + ranges = copr.BuildKeyRanges("a", "b", "h", "i", "o", "p") + req = &kv.Request{ + Tp: kv.ReqTypeDAG, + KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 33, 32}), + Concurrency: 15, + StoreBatchSize: 3, + } + it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) + require.Nil(t, errRes) + tasks = it.GetTasks() + require.Equal(t, len(tasks), 2) + require.Equal(t, len(tasks[0].ToPBBatchTasks()), 1) + require.Equal(t, len(tasks[1].ToPBBatchTasks()), 0) +} diff --git a/pkg/store/copr/copr_test/main_test.go b/pkg/store/copr/copr_test/main_test.go new file mode 100644 index 0000000000000..8aa21cc3baed8 --- /dev/null +++ b/pkg/store/copr/copr_test/main_test.go @@ -0,0 +1,61 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package copr_test + +import ( + "flag" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testmain.ShortCircuitForBench(m) + + testsetup.SetupForCommonTest() + + flag.Parse() + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + tikv.EnableFailpoints() + opts := []goleak.Option{ + // TODO: figure the reason and shorten this list + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), + goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), + goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/store/copr/coprocessor.go b/pkg/store/copr/coprocessor.go new file mode 100644 index 0000000000000..770b6a3dd6546 --- /dev/null +++ b/pkg/store/copr/coprocessor.go @@ -0,0 +1,2091 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "context" + "fmt" + "math" + "net" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/gogo/protobuf/proto" + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/errorpb" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/resourcegroup" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + tidbmetrics "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + copr_metrics "github.com/pingcap/tidb/pkg/store/copr/metrics" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/driver/options" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tidb/pkg/util/trxevents" + "github.com/pingcap/tipb/go-tipb" + "github.com/tikv/client-go/v2/metrics" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + "github.com/tikv/client-go/v2/tikvrpc/interceptor" + "github.com/tikv/client-go/v2/txnkv/txnlock" + "github.com/tikv/client-go/v2/txnkv/txnsnapshot" + "github.com/tikv/client-go/v2/util" + "go.uber.org/zap" +) + +// Maximum total sleep time(in ms) for kv/cop commands. +const ( + copBuildTaskMaxBackoff = 5000 + CopNextMaxBackoff = 20000 + CopSmallTaskRow = 32 // 32 is the initial batch size of TiKV + smallTaskSigma = 0.5 + smallConcPerCore = 20 +) + +// CopClient is coprocessor client. +type CopClient struct { + kv.RequestTypeSupportedChecker + store *Store + replicaReadSeed uint32 +} + +// Send builds the request and gets the coprocessor iterator response. +func (c *CopClient) Send(ctx context.Context, req *kv.Request, variables interface{}, option *kv.ClientSendOption) kv.Response { + vars, ok := variables.(*tikv.Variables) + if !ok { + return copErrorResponse{errors.Errorf("unsupported variables:%+v", variables)} + } + if req.StoreType == kv.TiFlash && req.BatchCop { + logutil.BgLogger().Debug("send batch requests") + return c.sendBatch(ctx, req, vars, option) + } + ctx = context.WithValue(ctx, tikv.TxnStartKey(), req.StartTs) + ctx = context.WithValue(ctx, util.RequestSourceKey, req.RequestSource) + ctx = interceptor.WithRPCInterceptor(ctx, interceptor.GetRPCInterceptorFromCtx(ctx)) + enabledRateLimitAction := option.EnabledRateLimitAction + sessionMemTracker := option.SessionMemTracker + it, errRes := c.BuildCopIterator(ctx, req, vars, option) + if errRes != nil { + return errRes + } + ctx = context.WithValue(ctx, tikv.RPCCancellerCtxKey{}, it.rpcCancel) + if sessionMemTracker != nil && enabledRateLimitAction { + sessionMemTracker.FallbackOldAndSetNewAction(it.actionOnExceed) + } + it.open(ctx, enabledRateLimitAction, option.EnableCollectExecutionInfo) + return it +} + +// BuildCopIterator builds the iterator without calling `open`. +func (c *CopClient) BuildCopIterator(ctx context.Context, req *kv.Request, vars *tikv.Variables, option *kv.ClientSendOption) (*copIterator, kv.Response) { + eventCb := option.EventCb + failpoint.Inject("DisablePaging", func(_ failpoint.Value) { + req.Paging.Enable = false + }) + if req.StoreType == kv.TiDB { + // coprocessor on TiDB doesn't support paging + req.Paging.Enable = false + } + if req.Tp != kv.ReqTypeDAG { + // coprocessor request but type is not DAG + req.Paging.Enable = false + } + failpoint.Inject("checkKeyRangeSortedForPaging", func(_ failpoint.Value) { + if req.Paging.Enable { + if !req.KeyRanges.IsFullySorted() { + logutil.BgLogger().Fatal("distsql request key range not sorted!") + } + } + }) + if !checkStoreBatchCopr(req) { + req.StoreBatchSize = 0 + } + + bo := backoff.NewBackofferWithVars(ctx, copBuildTaskMaxBackoff, vars) + var ( + tasks []*copTask + err error + ) + tryRowHint := optRowHint(req) + elapsed := time.Duration(0) + buildOpt := &buildCopTaskOpt{ + req: req, + cache: c.store.GetRegionCache(), + eventCb: eventCb, + respChan: req.KeepOrder, + elapsed: &elapsed, + } + buildTaskFunc := func(ranges []kv.KeyRange, hints []int) error { + keyRanges := NewKeyRanges(ranges) + if tryRowHint { + buildOpt.rowHints = hints + } + tasksFromRanges, err := buildCopTasks(bo, keyRanges, buildOpt) + if err != nil { + return err + } + if len(tasks) == 0 { + tasks = tasksFromRanges + return nil + } + tasks = append(tasks, tasksFromRanges...) + return nil + } + // Here we build the task by partition, not directly by region. + // This is because it's possible that TiDB merge multiple small partition into one region which break some assumption. + // Keep it split by partition would be more safe. + err = req.KeyRanges.ForEachPartitionWithErr(buildTaskFunc) + // only batch store requests in first build. + req.StoreBatchSize = 0 + reqType := "null" + if req.ClosestReplicaReadAdjuster != nil { + reqType = "miss" + if req.ClosestReplicaReadAdjuster(req, len(tasks)) { + reqType = "hit" + } + } + tidbmetrics.DistSQLCoprClosestReadCounter.WithLabelValues(reqType).Inc() + if err != nil { + return nil, copErrorResponse{err} + } + it := &copIterator{ + store: c.store, + req: req, + concurrency: req.Concurrency, + finishCh: make(chan struct{}), + vars: vars, + memTracker: req.MemTracker, + replicaReadSeed: c.replicaReadSeed, + rpcCancel: tikv.NewRPCanceller(), + buildTaskElapsed: *buildOpt.elapsed, + runawayChecker: req.RunawayChecker, + } + it.tasks = tasks + if it.concurrency > len(tasks) { + it.concurrency = len(tasks) + } + if tryRowHint { + var smallTasks int + smallTasks, it.smallTaskConcurrency = smallTaskConcurrency(tasks, c.store.numcpu) + if len(tasks)-smallTasks < it.concurrency { + it.concurrency = len(tasks) - smallTasks + } + } + if it.concurrency < 1 { + // Make sure that there is at least one worker. + it.concurrency = 1 + } + + if it.req.KeepOrder { + // Don't set high concurrency for the keep order case. It wastes a lot of memory and gains nothing. + // TL;DR + // Because for a keep order coprocessor request, the cop tasks are handled one by one, if we set a + // higher concurrency, the data is just cached and not consumed for a while, this increase the memory usage. + // Set concurrency to 2 can reduce the memory usage and I've tested that it does not necessarily + // decrease the performance. + // For ReqTypeAnalyze, we keep its concurrency to avoid slow analyze(see https://github.com/pingcap/tidb/issues/40162 for details). + if it.concurrency > 2 && it.req.Tp != kv.ReqTypeAnalyze { + oldConcurrency := it.concurrency + partitionNum := req.KeyRanges.PartitionNum() + if partitionNum > it.concurrency { + partitionNum = it.concurrency + } + it.concurrency = 2 + if it.concurrency < partitionNum { + it.concurrency = partitionNum + } + + failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { + if val.(bool) { + // When the concurrency is too small, test case tests/realtikvtest/sessiontest.TestCoprocessorOOMAction can't trigger OOM condition + it.concurrency = oldConcurrency + } + }) + } + if it.smallTaskConcurrency > 20 { + it.smallTaskConcurrency = 20 + } + it.sendRate = util.NewRateLimit(2 * (it.concurrency + it.smallTaskConcurrency)) + it.respChan = nil + } else { + it.respChan = make(chan *copResponse) + it.sendRate = util.NewRateLimit(it.concurrency + it.smallTaskConcurrency) + } + it.actionOnExceed = newRateLimitAction(uint(it.sendRate.GetCapacity())) + return it, nil +} + +// copTask contains a related Region and KeyRange for a kv.Request. +type copTask struct { + taskID uint64 + region tikv.RegionVerID + bucketsVer uint64 + ranges *KeyRanges + + respChan chan *copResponse + storeAddr string + cmdType tikvrpc.CmdType + storeType kv.StoreType + + eventCb trxevents.EventCallback + paging bool + pagingSize uint64 + pagingTaskIdx uint32 + + partitionIndex int64 // used by balanceBatchCopTask in PartitionTableScan + requestSource util.RequestSource + RowCountHint int // used for extra concurrency of small tasks, -1 for unknown row count + batchTaskList map[uint64]*batchedCopTask + + // when this task is batched and the leader's wait duration exceeds the load-based threshold, + // we set this field to the target replica store ID and redirect the request to the replica. + redirect2Replica *uint64 + busyThreshold time.Duration + meetLockFallback bool + + // timeout value for one kv readonly request + tikvClientReadTimeout uint64 + // firstReadType is used to indicate the type of first read when retrying. + firstReadType string +} + +type batchedCopTask struct { + task *copTask + region coprocessor.RegionInfo + storeID uint64 + peer *metapb.Peer + loadBasedReplicaRetry bool +} + +func (r *copTask) String() string { + return fmt.Sprintf("region(%d %d %d) ranges(%d) store(%s)", + r.region.GetID(), r.region.GetConfVer(), r.region.GetVer(), r.ranges.Len(), r.storeAddr) +} + +func (r *copTask) ToPBBatchTasks() []*coprocessor.StoreBatchTask { + if len(r.batchTaskList) == 0 { + return nil + } + pbTasks := make([]*coprocessor.StoreBatchTask, 0, len(r.batchTaskList)) + for _, task := range r.batchTaskList { + storeBatchTask := &coprocessor.StoreBatchTask{ + RegionId: task.region.GetRegionId(), + RegionEpoch: task.region.GetRegionEpoch(), + Peer: task.peer, + Ranges: task.region.GetRanges(), + TaskId: task.task.taskID, + } + pbTasks = append(pbTasks, storeBatchTask) + } + return pbTasks +} + +// rangesPerTask limits the length of the ranges slice sent in one copTask. +const rangesPerTask = 25000 + +type buildCopTaskOpt struct { + req *kv.Request + cache *RegionCache + eventCb trxevents.EventCallback + respChan bool + rowHints []int + elapsed *time.Duration + // ignoreTiKVClientReadTimeout is used to ignore tikv_client_read_timeout configuration, use default timeout instead. + ignoreTiKVClientReadTimeout bool +} + +func buildCopTasks(bo *Backoffer, ranges *KeyRanges, opt *buildCopTaskOpt) ([]*copTask, error) { + req, cache, eventCb, hints := opt.req, opt.cache, opt.eventCb, opt.rowHints + start := time.Now() + cmdType := tikvrpc.CmdCop + if req.StoreType == kv.TiDB { + return buildTiDBMemCopTasks(ranges, req) + } + rangesLen := ranges.Len() + // something went wrong, disable hints to avoid out of range index. + if len(hints) != rangesLen { + hints = nil + } + + rangesPerTaskLimit := rangesPerTask + failpoint.Inject("setRangesPerTask", func(val failpoint.Value) { + if v, ok := val.(int); ok { + rangesPerTaskLimit = v + } + }) + + // TODO(youjiali1995): is there any request type that needn't be splitted by buckets? + locs, err := cache.SplitKeyRangesByBuckets(bo, ranges) + if err != nil { + return nil, errors.Trace(err) + } + // Channel buffer is 2 for handling region split. + // In a common case, two region split tasks will not be blocked. + chanSize := 2 + // in paging request, a request will be returned in multi batches, + // enlarge the channel size to avoid the request blocked by buffer full. + if req.Paging.Enable { + chanSize = 18 + } + + var builder taskBuilder + if req.StoreBatchSize > 0 && hints != nil { + builder = newBatchTaskBuilder(bo, req, cache, req.ReplicaRead) + } else { + builder = newLegacyTaskBuilder(len(locs)) + } + origRangeIdx := 0 + for _, loc := range locs { + // TiKV will return gRPC error if the message is too large. So we need to limit the length of the ranges slice + // to make sure the message can be sent successfully. + rLen := loc.Ranges.Len() + // If this is a paging request, we set the paging size to minPagingSize, + // the size will grow every round. + pagingSize := uint64(0) + if req.Paging.Enable { + pagingSize = req.Paging.MinPagingSize + } + for i := 0; i < rLen; { + nextI := mathutil.Min(i+rangesPerTaskLimit, rLen) + hint := -1 + // calculate the row count hint + if hints != nil { + startKey, endKey := loc.Ranges.RefAt(i).StartKey, loc.Ranges.RefAt(nextI-1).EndKey + // move to the previous range if startKey of current range is lower than endKey of previous location. + // In the following example, task1 will move origRangeIdx to region(i, z). + // When counting the row hint for task2, we need to move origRangeIdx back to region(a, h). + // |<- region(a, h) ->| |<- region(i, z) ->| + // |<- task1 ->| |<- task2 ->| ... + if origRangeIdx > 0 && ranges.RefAt(origRangeIdx-1).EndKey.Cmp(startKey) > 0 { + origRangeIdx-- + } + hint = 0 + for nextOrigRangeIdx := origRangeIdx; nextOrigRangeIdx < ranges.Len(); nextOrigRangeIdx++ { + rangeStart := ranges.RefAt(nextOrigRangeIdx).StartKey + if rangeStart.Cmp(endKey) > 0 { + origRangeIdx = nextOrigRangeIdx + break + } + hint += hints[nextOrigRangeIdx] + } + } + task := &copTask{ + region: loc.Location.Region, + bucketsVer: loc.getBucketVersion(), + ranges: loc.Ranges.Slice(i, nextI), + cmdType: cmdType, + storeType: req.StoreType, + eventCb: eventCb, + paging: req.Paging.Enable, + pagingSize: pagingSize, + requestSource: req.RequestSource, + RowCountHint: hint, + busyThreshold: req.StoreBusyThreshold, + } + if !opt.ignoreTiKVClientReadTimeout { + task.tikvClientReadTimeout = req.TiKVClientReadTimeout + } + // only keep-order need chan inside task. + // tasks by region error will reuse the channel of parent task. + if req.KeepOrder && opt.respChan { + task.respChan = make(chan *copResponse, chanSize) + } + if err = builder.handle(task); err != nil { + return nil, err + } + i = nextI + if req.Paging.Enable { + if req.LimitSize != 0 && req.LimitSize < pagingSize { + // disable paging for small limit. + task.paging = false + task.pagingSize = 0 + } else { + pagingSize = paging.GrowPagingSize(pagingSize, req.Paging.MaxPagingSize) + } + } + } + } + + if req.Desc { + builder.reverse() + } + tasks := builder.build() + elapsed := time.Since(start) + if elapsed > time.Millisecond*500 { + logutil.BgLogger().Warn("buildCopTasks takes too much time", + zap.Duration("elapsed", elapsed), + zap.Int("range len", rangesLen), + zap.Int("task len", len(tasks))) + } + if elapsed > time.Millisecond { + defer tracing.StartRegion(bo.GetCtx(), "copr.buildCopTasks").End() + } + if opt.elapsed != nil { + *opt.elapsed = *opt.elapsed + elapsed + } + metrics.TxnRegionsNumHistogramWithCoprocessor.Observe(float64(builder.regionNum())) + return tasks, nil +} + +type taskBuilder interface { + handle(*copTask) error + reverse() + build() []*copTask + regionNum() int +} + +type legacyTaskBuilder struct { + tasks []*copTask +} + +func newLegacyTaskBuilder(hint int) *legacyTaskBuilder { + return &legacyTaskBuilder{ + tasks: make([]*copTask, 0, hint), + } +} + +func (b *legacyTaskBuilder) handle(task *copTask) error { + b.tasks = append(b.tasks, task) + return nil +} + +func (b *legacyTaskBuilder) regionNum() int { + return len(b.tasks) +} + +func (b *legacyTaskBuilder) reverse() { + reverseTasks(b.tasks) +} + +func (b *legacyTaskBuilder) build() []*copTask { + return b.tasks +} + +type storeReplicaKey struct { + storeID uint64 + replicaRead bool +} + +type batchStoreTaskBuilder struct { + bo *Backoffer + req *kv.Request + cache *RegionCache + taskID uint64 + limit int + store2Idx map[storeReplicaKey]int + tasks []*copTask + replicaRead kv.ReplicaReadType +} + +func newBatchTaskBuilder(bo *Backoffer, req *kv.Request, cache *RegionCache, replicaRead kv.ReplicaReadType) *batchStoreTaskBuilder { + return &batchStoreTaskBuilder{ + bo: bo, + req: req, + cache: cache, + taskID: 0, + limit: req.StoreBatchSize, + store2Idx: make(map[storeReplicaKey]int, 16), + tasks: make([]*copTask, 0, 16), + replicaRead: replicaRead, + } +} + +func (b *batchStoreTaskBuilder) handle(task *copTask) (err error) { + b.taskID++ + task.taskID = b.taskID + handled := false + defer func() { + if !handled && err == nil { + // fallback to non-batch way. It's mainly caused by region miss. + b.tasks = append(b.tasks, task) + } + }() + // only batch small tasks for memory control. + if b.limit <= 0 || !isSmallTask(task) { + return nil + } + batchedTask, err := b.cache.BuildBatchTask(b.bo, b.req, task, b.replicaRead) + if err != nil { + return err + } + if batchedTask == nil { + return nil + } + key := storeReplicaKey{ + storeID: batchedTask.storeID, + replicaRead: batchedTask.loadBasedReplicaRetry, + } + if idx, ok := b.store2Idx[key]; !ok || len(b.tasks[idx].batchTaskList) >= b.limit { + if batchedTask.loadBasedReplicaRetry { + // If the task is dispatched to leader because all followers are busy, + // task.redirect2Replica != nil means the busy threshold shouldn't take effect again. + batchedTask.task.redirect2Replica = &batchedTask.storeID + } + b.tasks = append(b.tasks, batchedTask.task) + b.store2Idx[key] = len(b.tasks) - 1 + } else { + if b.tasks[idx].batchTaskList == nil { + b.tasks[idx].batchTaskList = make(map[uint64]*batchedCopTask, b.limit) + // disable paging for batched task. + b.tasks[idx].paging = false + b.tasks[idx].pagingSize = 0 + } + if task.RowCountHint > 0 { + b.tasks[idx].RowCountHint += task.RowCountHint + } + b.tasks[idx].batchTaskList[task.taskID] = batchedTask + } + handled = true + return nil +} + +func (b *batchStoreTaskBuilder) regionNum() int { + // we allocate b.taskID for each region task, so the final b.taskID is equal to the related region number. + return int(b.taskID) +} + +func (b *batchStoreTaskBuilder) reverse() { + reverseTasks(b.tasks) +} + +func (b *batchStoreTaskBuilder) build() []*copTask { + return b.tasks +} + +func buildTiDBMemCopTasks(ranges *KeyRanges, req *kv.Request) ([]*copTask, error) { + servers, err := infosync.GetAllServerInfo(context.Background()) + if err != nil { + return nil, err + } + cmdType := tikvrpc.CmdCop + tasks := make([]*copTask, 0, len(servers)) + for _, ser := range servers { + if req.TiDBServerID > 0 && req.TiDBServerID != ser.ServerIDGetter() { + continue + } + + addr := net.JoinHostPort(ser.IP, strconv.FormatUint(uint64(ser.StatusPort), 10)) + tasks = append(tasks, &copTask{ + ranges: ranges, + respChan: make(chan *copResponse, 2), + cmdType: cmdType, + storeType: req.StoreType, + storeAddr: addr, + RowCountHint: -1, + }) + } + return tasks, nil +} + +func reverseTasks(tasks []*copTask) { + for i := 0; i < len(tasks)/2; i++ { + j := len(tasks) - i - 1 + tasks[i], tasks[j] = tasks[j], tasks[i] + } +} + +func isSmallTask(task *copTask) bool { + // strictly, only RowCountHint == -1 stands for unknown task rows, + // but when RowCountHint == 0, it may be caused by initialized value, + // to avoid the future bugs, let the tasks with RowCountHint == 0 be non-small tasks. + return task.RowCountHint > 0 && + (len(task.batchTaskList) == 0 && task.RowCountHint <= CopSmallTaskRow) || + (len(task.batchTaskList) > 0 && task.RowCountHint <= 2*CopSmallTaskRow) +} + +// smallTaskConcurrency counts the small tasks of tasks, +// then returns the task count and extra concurrency for small tasks. +func smallTaskConcurrency(tasks []*copTask, numcpu int) (int, int) { + res := 0 + for _, task := range tasks { + if isSmallTask(task) { + res++ + } + } + if res == 0 { + return 0, 0 + } + // Calculate the extra concurrency for small tasks + // extra concurrency = tasks / (1 + sigma * sqrt(log(tasks ^ 2))) + extraConc := int(float64(res) / (1 + smallTaskSigma*math.Sqrt(2*math.Log(float64(res))))) + if numcpu <= 0 { + numcpu = 1 + } + smallTaskConcurrencyLimit := smallConcPerCore * numcpu + if extraConc > smallTaskConcurrencyLimit { + extraConc = smallTaskConcurrencyLimit + } + return res, extraConc +} + +// CopInfo is used to expose functions of copIterator. +type CopInfo interface { + // GetConcurrency returns the concurrency and small task concurrency. + GetConcurrency() (int, int) + // GetStoreBatchInfo returns the batched and fallback num. + GetStoreBatchInfo() (uint64, uint64) + // GetBuildTaskElapsed returns the duration of building task. + GetBuildTaskElapsed() time.Duration +} + +type copIterator struct { + store *Store + req *kv.Request + concurrency int + smallTaskConcurrency int + finishCh chan struct{} + + // If keepOrder, results are stored in copTask.respChan, read them out one by one. + tasks []*copTask + // curr indicates the curr id of the finished copTask + curr int + + // sendRate controls the sending rate of copIteratorTaskSender + sendRate *util.RateLimit + + // Otherwise, results are stored in respChan. + respChan chan *copResponse + + vars *tikv.Variables + + memTracker *memory.Tracker + + replicaReadSeed uint32 + + rpcCancel *tikv.RPCCanceller + + wg sync.WaitGroup + // closed represents when the Close is called. + // There are two cases we need to close the `finishCh` channel, one is when context is done, the other one is + // when the Close is called. we use atomic.CompareAndSwap `closed` to make sure the channel is not closed twice. + closed uint32 + + resolvedLocks util.TSSet + committedLocks util.TSSet + + actionOnExceed *rateLimitAction + pagingTaskIdx uint32 + + buildTaskElapsed time.Duration + storeBatchedNum atomic.Uint64 + storeBatchedFallbackNum atomic.Uint64 + + runawayChecker *resourcegroup.RunawayChecker +} + +// copIteratorWorker receives tasks from copIteratorTaskSender, handles tasks and sends the copResponse to respChan. +type copIteratorWorker struct { + taskCh <-chan *copTask + wg *sync.WaitGroup + store *Store + req *kv.Request + respChan chan<- *copResponse + finishCh <-chan struct{} + vars *tikv.Variables + kvclient *txnsnapshot.ClientHelper + + memTracker *memory.Tracker + + replicaReadSeed uint32 + + enableCollectExecutionInfo bool + pagingTaskIdx *uint32 + + storeBatchedNum *atomic.Uint64 + storeBatchedFallbackNum *atomic.Uint64 +} + +// copIteratorTaskSender sends tasks to taskCh then wait for the workers to exit. +type copIteratorTaskSender struct { + taskCh chan<- *copTask + smallTaskCh chan<- *copTask + wg *sync.WaitGroup + tasks []*copTask + finishCh <-chan struct{} + respChan chan<- *copResponse + sendRate *util.RateLimit +} + +type copResponse struct { + pbResp *coprocessor.Response + detail *CopRuntimeStats + startKey kv.Key + err error + respSize int64 + respTime time.Duration +} + +const sizeofExecDetails = int(unsafe.Sizeof(execdetails.ExecDetails{})) + +// GetData implements the kv.ResultSubset GetData interface. +func (rs *copResponse) GetData() []byte { + return rs.pbResp.Data +} + +// GetStartKey implements the kv.ResultSubset GetStartKey interface. +func (rs *copResponse) GetStartKey() kv.Key { + return rs.startKey +} + +func (rs *copResponse) GetCopRuntimeStats() *CopRuntimeStats { + return rs.detail +} + +// MemSize returns how many bytes of memory this response use +func (rs *copResponse) MemSize() int64 { + if rs.respSize != 0 { + return rs.respSize + } + if rs == finCopResp { + return 0 + } + + // ignore rs.err + rs.respSize += int64(cap(rs.startKey)) + if rs.detail != nil { + rs.respSize += int64(sizeofExecDetails) + } + if rs.pbResp != nil { + // Using a approximate size since it's hard to get a accurate value. + rs.respSize += int64(rs.pbResp.Size()) + } + return rs.respSize +} + +func (rs *copResponse) RespTime() time.Duration { + return rs.respTime +} + +const minLogCopTaskTime = 300 * time.Millisecond + +// When the worker finished `handleTask`, we need to notify the copIterator that there is one task finished. +// For the non-keep-order case, we send a finCopResp into the respCh after `handleTask`. When copIterator recv +// finCopResp from the respCh, it will be aware that there is one task finished. +var finCopResp *copResponse + +func init() { + finCopResp = &copResponse{} +} + +// run is a worker function that get a copTask from channel, handle it and +// send the result back. +func (worker *copIteratorWorker) run(ctx context.Context) { + defer func() { + failpoint.Inject("ticase-4169", func(val failpoint.Value) { + if val.(bool) { + worker.memTracker.Consume(10 * MockResponseSizeForTest) + worker.memTracker.Consume(10 * MockResponseSizeForTest) + } + }) + worker.wg.Done() + }() + for task := range worker.taskCh { + respCh := worker.respChan + if respCh == nil { + respCh = task.respChan + } + worker.handleTask(ctx, task, respCh) + if worker.respChan != nil { + // When a task is finished by the worker, send a finCopResp into channel to notify the copIterator that + // there is a task finished. + worker.sendToRespCh(finCopResp, worker.respChan, false) + } + if task.respChan != nil { + close(task.respChan) + } + if worker.finished() { + return + } + } +} + +// open starts workers and sender goroutines. +func (it *copIterator) open(ctx context.Context, enabledRateLimitAction, enableCollectExecutionInfo bool) { + taskCh := make(chan *copTask, 1) + smallTaskCh := make(chan *copTask, 1) + it.wg.Add(it.concurrency + it.smallTaskConcurrency) + // Start it.concurrency number of workers to handle cop requests. + for i := 0; i < it.concurrency+it.smallTaskConcurrency; i++ { + var ch chan *copTask + if i < it.concurrency { + ch = taskCh + } else { + ch = smallTaskCh + } + worker := &copIteratorWorker{ + taskCh: ch, + wg: &it.wg, + store: it.store, + req: it.req, + respChan: it.respChan, + finishCh: it.finishCh, + vars: it.vars, + kvclient: txnsnapshot.NewClientHelper(it.store.store, &it.resolvedLocks, &it.committedLocks, false), + memTracker: it.memTracker, + replicaReadSeed: it.replicaReadSeed, + enableCollectExecutionInfo: enableCollectExecutionInfo, + pagingTaskIdx: &it.pagingTaskIdx, + storeBatchedNum: &it.storeBatchedNum, + storeBatchedFallbackNum: &it.storeBatchedFallbackNum, + } + go worker.run(ctx) + } + taskSender := &copIteratorTaskSender{ + taskCh: taskCh, + smallTaskCh: smallTaskCh, + wg: &it.wg, + tasks: it.tasks, + finishCh: it.finishCh, + sendRate: it.sendRate, + } + taskSender.respChan = it.respChan + it.actionOnExceed.setEnabled(enabledRateLimitAction) + failpoint.Inject("ticase-4171", func(val failpoint.Value) { + if val.(bool) { + it.memTracker.Consume(10 * MockResponseSizeForTest) + it.memTracker.Consume(10 * MockResponseSizeForTest) + } + }) + go taskSender.run(it.req.ConnID) +} + +func (sender *copIteratorTaskSender) run(connID uint64) { + // Send tasks to feed the worker goroutines. + for _, t := range sender.tasks { + // we control the sending rate to prevent all tasks + // being done (aka. all of the responses are buffered) by copIteratorWorker. + // We keep the number of inflight tasks within the number of 2 * concurrency when Keep Order is true. + // If KeepOrder is false, the number equals the concurrency. + // It sends one more task if a task has been finished in copIterator.Next. + exit := sender.sendRate.GetToken(sender.finishCh) + if exit { + break + } + var sendTo chan<- *copTask + if isSmallTask(t) { + sendTo = sender.smallTaskCh + } else { + sendTo = sender.taskCh + } + exit = sender.sendToTaskCh(t, sendTo) + if exit { + break + } + if connID > 0 { + failpoint.Inject("pauseCopIterTaskSender", func() {}) + } + } + close(sender.taskCh) + close(sender.smallTaskCh) + + // Wait for worker goroutines to exit. + sender.wg.Wait() + if sender.respChan != nil { + close(sender.respChan) + } +} + +func (it *copIterator) recvFromRespCh(ctx context.Context, respCh <-chan *copResponse) (resp *copResponse, ok bool, exit bool) { + ticker := time.NewTicker(3 * time.Second) + defer ticker.Stop() + for { + select { + case resp, ok = <-respCh: + if it.memTracker != nil && resp != nil { + consumed := resp.MemSize() + failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { + if val.(bool) { + if resp != finCopResp { + consumed = MockResponseSizeForTest + } + } + }) + it.memTracker.Consume(-consumed) + } + return + case <-it.finishCh: + exit = true + return + case <-ticker.C: + if atomic.LoadUint32(it.vars.Killed) == 1 { + resp = &copResponse{err: derr.ErrQueryInterrupted} + ok = true + return + } + case <-ctx.Done(): + // We select the ctx.Done() in the thread of `Next` instead of in the worker to avoid the cost of `WithCancel`. + if atomic.CompareAndSwapUint32(&it.closed, 0, 1) { + close(it.finishCh) + } + exit = true + return + } + } +} + +// GetConcurrency returns the concurrency and small task concurrency. +func (it *copIterator) GetConcurrency() (int, int) { + return it.concurrency, it.smallTaskConcurrency +} + +// GetStoreBatchInfo returns the batched and fallback num. +func (it *copIterator) GetStoreBatchInfo() (uint64, uint64) { + return it.storeBatchedNum.Load(), it.storeBatchedFallbackNum.Load() +} + +// GetBuildTaskElapsed returns the duration of building task. +func (it *copIterator) GetBuildTaskElapsed() time.Duration { + return it.buildTaskElapsed +} + +// GetSendRate returns the rate-limit object. +func (it *copIterator) GetSendRate() *util.RateLimit { + return it.sendRate +} + +// GetTasks returns the built tasks. +func (it *copIterator) GetTasks() []*copTask { + return it.tasks +} + +func (sender *copIteratorTaskSender) sendToTaskCh(t *copTask, sendTo chan<- *copTask) (exit bool) { + select { + case sendTo <- t: + case <-sender.finishCh: + exit = true + } + return +} + +func (worker *copIteratorWorker) sendToRespCh(resp *copResponse, respCh chan<- *copResponse, checkOOM bool) (exit bool) { + if worker.memTracker != nil && checkOOM { + consumed := resp.MemSize() + failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { + if val.(bool) { + if resp != finCopResp { + consumed = MockResponseSizeForTest + } + } + }) + failpoint.Inject("ConsumeRandomPanic", nil) + worker.memTracker.Consume(consumed) + } + select { + case respCh <- resp: + case <-worker.finishCh: + exit = true + } + return +} + +// MockResponseSizeForTest mock the response size +const MockResponseSizeForTest = 100 * 1024 * 1024 + +// Next returns next coprocessor result. +// NOTE: Use nil to indicate finish, so if the returned ResultSubset is not nil, reader should continue to call Next(). +func (it *copIterator) Next(ctx context.Context) (kv.ResultSubset, error) { + var ( + resp *copResponse + ok bool + closed bool + ) + defer func() { + if resp == nil { + failpoint.Inject("ticase-4170", func(val failpoint.Value) { + if val.(bool) { + it.memTracker.Consume(10 * MockResponseSizeForTest) + it.memTracker.Consume(10 * MockResponseSizeForTest) + } + }) + } + }() + // wait unit at least 5 copResponse received. + failpoint.Inject("testRateLimitActionMockWaitMax", func(val failpoint.Value) { + if val.(bool) { + // we only need to trigger oom at least once. + if len(it.tasks) > 9 { + for it.memTracker.MaxConsumed() < 5*MockResponseSizeForTest { + time.Sleep(10 * time.Millisecond) + } + } + } + }) + // If data order matters, response should be returned in the same order as copTask slice. + // Otherwise all responses are returned from a single channel. + if it.respChan != nil { + // Get next fetched resp from chan + resp, ok, closed = it.recvFromRespCh(ctx, it.respChan) + if !ok || closed { + it.actionOnExceed.close() + return nil, nil + } + if resp == finCopResp { + it.actionOnExceed.destroyTokenIfNeeded(func() { + it.sendRate.PutToken() + }) + return it.Next(ctx) + } + } else { + for { + if it.curr >= len(it.tasks) { + // Resp will be nil if iterator is finishCh. + it.actionOnExceed.close() + return nil, nil + } + task := it.tasks[it.curr] + resp, ok, closed = it.recvFromRespCh(ctx, task.respChan) + if closed { + // Close() is already called, so Next() is invalid. + return nil, nil + } + if ok { + break + } + it.actionOnExceed.destroyTokenIfNeeded(func() { + it.sendRate.PutToken() + }) + // Switch to next task. + it.tasks[it.curr] = nil + it.curr++ + } + } + + if resp.err != nil { + return nil, errors.Trace(resp.err) + } + + err := it.store.CheckVisibility(it.req.StartTs) + if err != nil { + return nil, errors.Trace(err) + } + return resp, nil +} + +// Associate each region with an independent backoffer. In this way, when multiple regions are +// unavailable, TiDB can execute very quickly without blocking +func chooseBackoffer(ctx context.Context, backoffermap map[uint64]*Backoffer, task *copTask, worker *copIteratorWorker) *Backoffer { + bo, ok := backoffermap[task.region.GetID()] + if ok { + return bo + } + boMaxSleep := CopNextMaxBackoff + failpoint.Inject("ReduceCopNextMaxBackoff", func(value failpoint.Value) { + if value.(bool) { + boMaxSleep = 2 + } + }) + newbo := backoff.NewBackofferWithVars(ctx, boMaxSleep, worker.vars) + backoffermap[task.region.GetID()] = newbo + return newbo +} + +// handleTask handles single copTask, sends the result to channel, retry automatically on error. +func (worker *copIteratorWorker) handleTask(ctx context.Context, task *copTask, respCh chan<- *copResponse) { + defer func() { + r := recover() + if r != nil { + logutil.BgLogger().Error("copIteratorWork meet panic", + zap.Any("r", r), + zap.Stack("stack trace")) + resp := &copResponse{err: errors.Errorf("%v", r)} + // if panic has happened, set checkOOM to false to avoid another panic. + worker.sendToRespCh(resp, respCh, false) + } + }() + remainTasks := []*copTask{task} + backoffermap := make(map[uint64]*Backoffer) + for len(remainTasks) > 0 { + curTask := remainTasks[0] + bo := chooseBackoffer(ctx, backoffermap, curTask, worker) + tasks, err := worker.handleTaskOnce(bo, curTask, respCh) + if err != nil { + resp := &copResponse{err: errors.Trace(err)} + worker.sendToRespCh(resp, respCh, true) + return + } + if worker.finished() { + break + } + if len(tasks) > 0 { + remainTasks = append(tasks, remainTasks[1:]...) + } else { + remainTasks = remainTasks[1:] + } + } +} + +// handleTaskOnce handles single copTask, successful results are send to channel. +// If error happened, returns error. If region split or meet lock, returns the remain tasks. +func (worker *copIteratorWorker) handleTaskOnce(bo *Backoffer, task *copTask, ch chan<- *copResponse) ([]*copTask, error) { + failpoint.Inject("handleTaskOnceError", func(val failpoint.Value) { + if val.(bool) { + failpoint.Return(nil, errors.New("mock handleTaskOnce error")) + } + }) + + if task.paging { + task.pagingTaskIdx = atomic.AddUint32(worker.pagingTaskIdx, 1) + } + + copReq := coprocessor.Request{ + Tp: worker.req.Tp, + StartTs: worker.req.StartTs, + Data: worker.req.Data, + Ranges: task.ranges.ToPBRanges(), + SchemaVer: worker.req.SchemaVar, + PagingSize: task.pagingSize, + Tasks: task.ToPBBatchTasks(), + } + + cacheKey, cacheValue := worker.buildCacheKey(task, &copReq) + + replicaRead := worker.req.ReplicaRead + rgName := worker.req.ResourceGroupName + if task.storeType == kv.TiFlash && !variable.EnableResourceControl.Load() { + // By calling variable.EnableGlobalResourceControlFunc() and setting global variables, + // tikv/client-go can sense whether the rg function is enabled + // But for tiflash, it check if rgName is empty to decide if resource control is enabled or not. + rgName = "" + } + req := tikvrpc.NewReplicaReadRequest(task.cmdType, &copReq, options.GetTiKVReplicaReadType(replicaRead), &worker.replicaReadSeed, kvrpcpb.Context{ + IsolationLevel: isolationLevelToPB(worker.req.IsolationLevel), + Priority: priorityToPB(worker.req.Priority), + NotFillCache: worker.req.NotFillCache, + RecordTimeStat: true, + RecordScanStat: true, + TaskId: worker.req.TaskID, + ResourceControlContext: &kvrpcpb.ResourceControlContext{ + ResourceGroupName: rgName, + }, + BusyThresholdMs: uint32(task.busyThreshold.Milliseconds()), + BucketsVersion: task.bucketsVer, + }) + req.InputRequestSource = task.requestSource.GetRequestSource() + if task.firstReadType != "" { + req.ReadType = task.firstReadType + req.IsRetryRequest = true + } + if worker.req.ResourceGroupTagger != nil { + worker.req.ResourceGroupTagger(req) + } + timeout := config.GetGlobalConfig().TiKVClient.CoprReqTimeout + if task.tikvClientReadTimeout > 0 { + timeout = time.Duration(task.tikvClientReadTimeout) * time.Millisecond + } + failpoint.Inject("sleepCoprRequest", func(v failpoint.Value) { + //nolint:durationcheck + time.Sleep(time.Millisecond * time.Duration(v.(int))) + }) + + if worker.req.RunawayChecker != nil { + if err := worker.req.RunawayChecker.BeforeCopRequest(req); err != nil { + return nil, err + } + } + req.StoreTp = getEndPointType(task.storeType) + startTime := time.Now() + if worker.kvclient.Stats == nil { + worker.kvclient.Stats = make(map[tikvrpc.CmdType]*tikv.RPCRuntimeStats) + } + // set ReadReplicaScope and TxnScope so that req.IsStaleRead will be true when it's a global scope stale read. + req.ReadReplicaScope = worker.req.ReadReplicaScope + req.TxnScope = worker.req.TxnScope + if task.meetLockFallback { + req.DisableStaleReadMeetLock() + } else if worker.req.IsStaleness { + req.EnableStaleWithMixedReplicaRead() + } + staleRead := req.GetStaleRead() + ops := make([]tikv.StoreSelectorOption, 0, 2) + if len(worker.req.MatchStoreLabels) > 0 { + ops = append(ops, tikv.WithMatchLabels(worker.req.MatchStoreLabels)) + } + if task.redirect2Replica != nil { + req.ReplicaRead = true + req.ReplicaReadType = options.GetTiKVReplicaReadType(kv.ReplicaReadFollower) + ops = append(ops, tikv.WithMatchStores([]uint64{*task.redirect2Replica})) + } + resp, rpcCtx, storeAddr, err := worker.kvclient.SendReqCtx(bo.TiKVBackoffer(), req, task.region, + timeout, getEndPointType(task.storeType), task.storeAddr, ops...) + err = derr.ToTiDBErr(err) + if err != nil { + if task.storeType == kv.TiDB { + err = worker.handleTiDBSendReqErr(err, task, ch) + return nil, err + } + return nil, errors.Trace(err) + } + + // Set task.storeAddr field so its task.String() method have the store address information. + task.storeAddr = storeAddr + + costTime := time.Since(startTime) + copResp := resp.Resp.(*coprocessor.Response) + + if costTime > minLogCopTaskTime { + worker.logTimeCopTask(costTime, task, bo, copResp) + } + if worker.req.RunawayChecker != nil { + worker.req.RunawayChecker.AfterCopRequest() + } + + storeID := strconv.FormatUint(req.Context.GetPeer().GetStoreId(), 10) + isInternal := util.IsRequestSourceInternal(&task.requestSource) + scope := metrics.LblGeneral + if isInternal { + scope = metrics.LblInternal + } + metrics.TiKVCoprocessorHistogram.WithLabelValues(storeID, strconv.FormatBool(staleRead), scope).Observe(costTime.Seconds()) + if copResp != nil { + tidbmetrics.DistSQLCoprRespBodySize.WithLabelValues(storeAddr).Observe(float64(len(copResp.Data))) + } + + var remains []*copTask + if worker.req.Paging.Enable { + remains, err = worker.handleCopPagingResult(bo, rpcCtx, &copResponse{pbResp: copResp}, cacheKey, cacheValue, task, ch, costTime) + } else { + // Handles the response for non-paging copTask. + remains, err = worker.handleCopResponse(bo, rpcCtx, &copResponse{pbResp: copResp}, cacheKey, cacheValue, task, ch, nil, costTime) + } + if req.ReadType != "" { + for _, remain := range remains { + remain.firstReadType = req.ReadType + } + } + return remains, err +} + +const ( + minLogBackoffTime = 100 + minLogKVProcessTime = 100 +) + +func (worker *copIteratorWorker) logTimeCopTask(costTime time.Duration, task *copTask, bo *Backoffer, resp *coprocessor.Response) { + logStr := fmt.Sprintf("[TIME_COP_PROCESS] resp_time:%s txnStartTS:%d region_id:%d store_addr:%s", costTime, worker.req.StartTs, task.region.GetID(), task.storeAddr) + if bo.GetTotalSleep() > minLogBackoffTime { + backoffTypes := strings.ReplaceAll(fmt.Sprintf("%v", bo.TiKVBackoffer().GetTypes()), " ", ",") + logStr += fmt.Sprintf(" backoff_ms:%d backoff_types:%s", bo.GetTotalSleep(), backoffTypes) + } + // resp might be nil, but it is safe to call resp.GetXXX here. + detailV2 := resp.GetExecDetailsV2() + detail := resp.GetExecDetails() + var timeDetail *kvrpcpb.TimeDetail + if detailV2 != nil && detailV2.TimeDetail != nil { + timeDetail = detailV2.TimeDetail + } else if detail != nil && detail.TimeDetail != nil { + timeDetail = detail.TimeDetail + } + if timeDetail != nil { + logStr += fmt.Sprintf(" kv_process_ms:%d", timeDetail.ProcessWallTimeMs) + logStr += fmt.Sprintf(" kv_wait_ms:%d", timeDetail.WaitWallTimeMs) + logStr += fmt.Sprintf(" kv_read_ms:%d", timeDetail.KvReadWallTimeMs) + if timeDetail.ProcessWallTimeMs <= minLogKVProcessTime { + logStr = strings.Replace(logStr, "TIME_COP_PROCESS", "TIME_COP_WAIT", 1) + } + } + + if detailV2 != nil && detailV2.ScanDetailV2 != nil { + logStr += fmt.Sprintf(" processed_versions:%d", detailV2.ScanDetailV2.ProcessedVersions) + logStr += fmt.Sprintf(" total_versions:%d", detailV2.ScanDetailV2.TotalVersions) + logStr += fmt.Sprintf(" rocksdb_delete_skipped_count:%d", detailV2.ScanDetailV2.RocksdbDeleteSkippedCount) + logStr += fmt.Sprintf(" rocksdb_key_skipped_count:%d", detailV2.ScanDetailV2.RocksdbKeySkippedCount) + logStr += fmt.Sprintf(" rocksdb_cache_hit_count:%d", detailV2.ScanDetailV2.RocksdbBlockCacheHitCount) + logStr += fmt.Sprintf(" rocksdb_read_count:%d", detailV2.ScanDetailV2.RocksdbBlockReadCount) + logStr += fmt.Sprintf(" rocksdb_read_byte:%d", detailV2.ScanDetailV2.RocksdbBlockReadByte) + } else if detail != nil && detail.ScanDetail != nil { + logStr = appendScanDetail(logStr, "write", detail.ScanDetail.Write) + logStr = appendScanDetail(logStr, "data", detail.ScanDetail.Data) + logStr = appendScanDetail(logStr, "lock", detail.ScanDetail.Lock) + } + logutil.Logger(bo.GetCtx()).Info(logStr) +} + +func appendScanDetail(logStr string, columnFamily string, scanInfo *kvrpcpb.ScanInfo) string { + if scanInfo != nil { + logStr += fmt.Sprintf(" scan_total_%s:%d", columnFamily, scanInfo.Total) + logStr += fmt.Sprintf(" scan_processed_%s:%d", columnFamily, scanInfo.Processed) + } + return logStr +} + +func (worker *copIteratorWorker) handleCopPagingResult(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue, task *copTask, ch chan<- *copResponse, costTime time.Duration) ([]*copTask, error) { + remainedTasks, err := worker.handleCopResponse(bo, rpcCtx, resp, cacheKey, cacheValue, task, ch, nil, costTime) + if err != nil || len(remainedTasks) != 0 { + // If there is region error or lock error, keep the paging size and retry. + for _, remainedTask := range remainedTasks { + remainedTask.pagingSize = task.pagingSize + } + return remainedTasks, errors.Trace(err) + } + pagingRange := resp.pbResp.Range + // only paging requests need to calculate the next ranges + if pagingRange == nil { + // If the storage engine doesn't support paging protocol, it should have return all the region data. + // So we finish here. + return nil, nil + } + + // calculate next ranges and grow the paging size + task.ranges = worker.calculateRemain(task.ranges, pagingRange, worker.req.Desc) + if task.ranges.Len() == 0 { + return nil, nil + } + + task.pagingSize = paging.GrowPagingSize(task.pagingSize, worker.req.Paging.MaxPagingSize) + return []*copTask{task}, nil +} + +// handleCopResponse checks coprocessor Response for region split and lock, +// returns more tasks when that happens, or handles the response if no error. +// if we're handling coprocessor paging response, lastRange is the range of last +// successful response, otherwise it's nil. +func (worker *copIteratorWorker) handleCopResponse(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue, task *copTask, ch chan<- *copResponse, lastRange *coprocessor.KeyRange, costTime time.Duration) ([]*copTask, error) { + if ver := resp.pbResp.GetLatestBucketsVersion(); task.bucketsVer < ver { + worker.store.GetRegionCache().UpdateBucketsIfNeeded(task.region, ver) + } + if regionErr := resp.pbResp.GetRegionError(); regionErr != nil { + if rpcCtx != nil && task.storeType == kv.TiDB { + resp.err = errors.Errorf("error: %v", regionErr) + worker.sendToRespCh(resp, ch, true) + return nil, nil + } + errStr := fmt.Sprintf("region_id:%v, region_ver:%v, store_type:%s, peer_addr:%s, error:%s", + task.region.GetID(), task.region.GetVer(), task.storeType.Name(), task.storeAddr, regionErr.String()) + if err := bo.Backoff(tikv.BoRegionMiss(), errors.New(errStr)); err != nil { + return nil, errors.Trace(err) + } + // We may meet RegionError at the first packet, but not during visiting the stream. + remains, err := buildCopTasks(bo, task.ranges, &buildCopTaskOpt{ + req: worker.req, + cache: worker.store.GetRegionCache(), + respChan: false, + eventCb: task.eventCb, + ignoreTiKVClientReadTimeout: true, + }) + if err != nil { + return remains, err + } + return worker.handleBatchRemainsOnErr(bo, rpcCtx, remains, resp.pbResp, task, ch) + } + if lockErr := resp.pbResp.GetLocked(); lockErr != nil { + if err := worker.handleLockErr(bo, lockErr, task); err != nil { + return nil, err + } + task.meetLockFallback = true + return worker.handleBatchRemainsOnErr(bo, rpcCtx, []*copTask{task}, resp.pbResp, task, ch) + } + if otherErr := resp.pbResp.GetOtherError(); otherErr != "" { + err := errors.Errorf("other error: %s", otherErr) + + firstRangeStartKey := task.ranges.At(0).StartKey + lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey + + logutil.Logger(bo.GetCtx()).Warn("other error", + zap.Uint64("txnStartTS", worker.req.StartTs), + zap.Uint64("regionID", task.region.GetID()), + zap.Uint64("bucketsVer", task.bucketsVer), + zap.Uint64("latestBucketsVer", resp.pbResp.GetLatestBucketsVersion()), + zap.Int("rangeNums", task.ranges.Len()), + zap.ByteString("firstRangeStartKey", firstRangeStartKey), + zap.ByteString("lastRangeEndKey", lastRangeEndKey), + zap.String("storeAddr", task.storeAddr), + zap.Error(err)) + if strings.Contains(err.Error(), "write conflict") { + return nil, kv.ErrWriteConflict.FastGen("%s", otherErr) + } + return nil, errors.Trace(err) + } + // When the request is using paging API, the `Range` is not nil. + if resp.pbResp.Range != nil { + resp.startKey = resp.pbResp.Range.Start + } else if task.ranges != nil && task.ranges.Len() > 0 { + resp.startKey = task.ranges.At(0).StartKey + } + worker.handleCollectExecutionInfo(bo, rpcCtx, resp) + resp.respTime = costTime + + if err := worker.handleCopCache(task, resp, cacheKey, cacheValue); err != nil { + return nil, err + } + + pbResp := resp.pbResp + worker.sendToRespCh(resp, ch, true) + return worker.handleBatchCopResponse(bo, rpcCtx, pbResp, task.batchTaskList, ch) +} + +func (worker *copIteratorWorker) handleBatchRemainsOnErr(bo *Backoffer, rpcCtx *tikv.RPCContext, remains []*copTask, resp *coprocessor.Response, task *copTask, ch chan<- *copResponse) ([]*copTask, error) { + if len(task.batchTaskList) == 0 { + return remains, nil + } + batchedTasks := task.batchTaskList + task.batchTaskList = nil + batchedRemains, err := worker.handleBatchCopResponse(bo, rpcCtx, resp, batchedTasks, ch) + if err != nil { + return nil, err + } + return append(remains, batchedRemains...), nil +} + +// handle the batched cop response. +// tasks will be changed, so the input tasks should not be used after calling this function. +func (worker *copIteratorWorker) handleBatchCopResponse(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *coprocessor.Response, + tasks map[uint64]*batchedCopTask, ch chan<- *copResponse) (remainTasks []*copTask, err error) { + if len(tasks) == 0 { + return nil, nil + } + batchedNum := len(tasks) + busyThresholdFallback := false + defer func() { + if err != nil { + return + } + if !busyThresholdFallback { + worker.storeBatchedNum.Add(uint64(batchedNum - len(remainTasks))) + worker.storeBatchedFallbackNum.Add(uint64(len(remainTasks))) + } + }() + appendRemainTasks := func(tasks ...*copTask) { + if remainTasks == nil { + // allocate size fo remain length + remainTasks = make([]*copTask, 0, len(tasks)) + } + remainTasks = append(remainTasks, tasks...) + } + // need Addr for recording details. + var dummyRPCCtx *tikv.RPCContext + if rpcCtx != nil { + dummyRPCCtx = &tikv.RPCContext{ + Addr: rpcCtx.Addr, + } + } + batchResps := resp.GetBatchResponses() + for _, batchResp := range batchResps { + taskID := batchResp.GetTaskId() + batchedTask, ok := tasks[taskID] + if !ok { + return nil, errors.Errorf("task id %d not found", batchResp.GetTaskId()) + } + delete(tasks, taskID) + resp := &copResponse{ + pbResp: &coprocessor.Response{ + Data: batchResp.Data, + ExecDetailsV2: batchResp.ExecDetailsV2, + }, + } + task := batchedTask.task + failpoint.Inject("batchCopRegionError", func() { + batchResp.RegionError = &errorpb.Error{} + }) + if regionErr := batchResp.GetRegionError(); regionErr != nil { + errStr := fmt.Sprintf("region_id:%v, region_ver:%v, store_type:%s, peer_addr:%s, error:%s", + task.region.GetID(), task.region.GetVer(), task.storeType.Name(), task.storeAddr, regionErr.String()) + if err := bo.Backoff(tikv.BoRegionMiss(), errors.New(errStr)); err != nil { + return nil, errors.Trace(err) + } + remains, err := buildCopTasks(bo, task.ranges, &buildCopTaskOpt{ + req: worker.req, + cache: worker.store.GetRegionCache(), + respChan: false, + eventCb: task.eventCb, + ignoreTiKVClientReadTimeout: true, + }) + if err != nil { + return nil, err + } + appendRemainTasks(remains...) + continue + } + //TODO: handle locks in batch + if lockErr := batchResp.GetLocked(); lockErr != nil { + if err := worker.handleLockErr(bo, resp.pbResp.GetLocked(), task); err != nil { + return nil, err + } + task.meetLockFallback = true + appendRemainTasks(task) + continue + } + if otherErr := batchResp.GetOtherError(); otherErr != "" { + err := errors.Errorf("other error: %s", otherErr) + + firstRangeStartKey := task.ranges.At(0).StartKey + lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey + + logutil.Logger(bo.GetCtx()).Warn("other error", + zap.Uint64("txnStartTS", worker.req.StartTs), + zap.Uint64("regionID", task.region.GetID()), + zap.Uint64("bucketsVer", task.bucketsVer), + // TODO: add bucket version in log + //zap.Uint64("latestBucketsVer", batchResp.GetLatestBucketsVersion()), + zap.Int("rangeNums", task.ranges.Len()), + zap.ByteString("firstRangeStartKey", firstRangeStartKey), + zap.ByteString("lastRangeEndKey", lastRangeEndKey), + zap.String("storeAddr", task.storeAddr), + zap.Error(err)) + if strings.Contains(err.Error(), "write conflict") { + return nil, kv.ErrWriteConflict.FastGen("%s", otherErr) + } + return nil, errors.Trace(err) + } + worker.handleCollectExecutionInfo(bo, dummyRPCCtx, resp) + worker.sendToRespCh(resp, ch, true) + } + for _, t := range tasks { + task := t.task + // when the error is generated by client or a load-based server busy, + // response is empty by design, skip warning for this case. + if len(batchResps) != 0 { + firstRangeStartKey := task.ranges.At(0).StartKey + lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey + logutil.Logger(bo.GetCtx()).Error("response of batched task missing", + zap.Uint64("id", task.taskID), + zap.Uint64("txnStartTS", worker.req.StartTs), + zap.Uint64("regionID", task.region.GetID()), + zap.Uint64("bucketsVer", task.bucketsVer), + zap.Int("rangeNums", task.ranges.Len()), + zap.ByteString("firstRangeStartKey", firstRangeStartKey), + zap.ByteString("lastRangeEndKey", lastRangeEndKey), + zap.String("storeAddr", task.storeAddr)) + } + appendRemainTasks(t.task) + } + if regionErr := resp.GetRegionError(); regionErr != nil && regionErr.ServerIsBusy != nil && + regionErr.ServerIsBusy.EstimatedWaitMs > 0 && len(remainTasks) != 0 { + if len(batchResps) != 0 { + return nil, errors.New("store batched coprocessor with server is busy error shouldn't contain responses") + } + busyThresholdFallback = true + handler := newBatchTaskBuilder(bo, worker.req, worker.store.GetRegionCache(), kv.ReplicaReadFollower) + for _, task := range remainTasks { + // do not set busy threshold again. + task.busyThreshold = 0 + if err = handler.handle(task); err != nil { + return nil, err + } + } + remainTasks = handler.build() + } + return remainTasks, nil +} + +func (worker *copIteratorWorker) handleLockErr(bo *Backoffer, lockErr *kvrpcpb.LockInfo, task *copTask) error { + if lockErr == nil { + return nil + } + resolveLockDetail := worker.getLockResolverDetails() + // Be care that we didn't redact the SQL statement because the log is DEBUG level. + if task.eventCb != nil { + task.eventCb(trxevents.WrapCopMeetLock(&trxevents.CopMeetLock{ + LockInfo: lockErr, + })) + } else { + logutil.Logger(bo.GetCtx()).Debug("coprocessor encounters lock", + zap.Stringer("lock", lockErr)) + } + resolveLocksOpts := txnlock.ResolveLocksOptions{ + CallerStartTS: worker.req.StartTs, + Locks: []*txnlock.Lock{txnlock.NewLock(lockErr)}, + Detail: resolveLockDetail, + } + resolveLocksRes, err1 := worker.kvclient.ResolveLocksWithOpts(bo.TiKVBackoffer(), resolveLocksOpts) + err1 = derr.ToTiDBErr(err1) + if err1 != nil { + return errors.Trace(err1) + } + msBeforeExpired := resolveLocksRes.TTL + if msBeforeExpired > 0 { + if err := bo.BackoffWithMaxSleepTxnLockFast(int(msBeforeExpired), errors.New(lockErr.String())); err != nil { + return errors.Trace(err) + } + } + return nil +} + +func (worker *copIteratorWorker) buildCacheKey(task *copTask, copReq *coprocessor.Request) (cacheKey []byte, cacheValue *coprCacheValue) { + // If there are many ranges, it is very likely to be a TableLookupRequest. They are not worth to cache since + // computing is not the main cost. Ignore requests with many ranges directly to avoid slowly building the cache key. + if task.cmdType == tikvrpc.CmdCop && worker.store.coprCache != nil && worker.req.Cacheable && worker.store.coprCache.CheckRequestAdmission(len(copReq.Ranges)) { + cKey, err := coprCacheBuildKey(copReq) + if err == nil { + cacheKey = cKey + cValue := worker.store.coprCache.Get(cKey) + copReq.IsCacheEnabled = true + + if cValue != nil && cValue.RegionID == task.region.GetID() && cValue.TimeStamp <= worker.req.StartTs { + // Append cache version to the request to skip Coprocessor computation if possible + // when request result is cached + copReq.CacheIfMatchVersion = cValue.RegionDataVersion + cacheValue = cValue + } else { + copReq.CacheIfMatchVersion = 0 + } + } else { + logutil.BgLogger().Warn("Failed to build copr cache key", zap.Error(err)) + } + } + return +} + +func (worker *copIteratorWorker) handleCopCache(task *copTask, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue) error { + if resp.pbResp.IsCacheHit { + if cacheValue == nil { + return errors.New("Internal error: received illegal TiKV response") + } + copr_metrics.CoprCacheCounterHit.Add(1) + // Cache hit and is valid: use cached data as response data and we don't update the cache. + data := make([]byte, len(cacheValue.Data)) + copy(data, cacheValue.Data) + resp.pbResp.Data = data + if worker.req.Paging.Enable { + var start, end []byte + if cacheValue.PageStart != nil { + start = make([]byte, len(cacheValue.PageStart)) + copy(start, cacheValue.PageStart) + } + if cacheValue.PageEnd != nil { + end = make([]byte, len(cacheValue.PageEnd)) + copy(end, cacheValue.PageEnd) + } + // When paging protocol is used, the response key range is part of the cache data. + if start != nil || end != nil { + resp.pbResp.Range = &coprocessor.KeyRange{ + Start: start, + End: end, + } + } else { + resp.pbResp.Range = nil + } + } + resp.detail.CoprCacheHit = true + return nil + } + copr_metrics.CoprCacheCounterMiss.Add(1) + // Cache not hit or cache hit but not valid: update the cache if the response can be cached. + if cacheKey != nil && resp.pbResp.CanBeCached && resp.pbResp.CacheLastVersion > 0 { + if resp.detail != nil { + if worker.store.coprCache.CheckResponseAdmission(resp.pbResp.Data.Size(), resp.detail.TimeDetail.ProcessTime, task.pagingTaskIdx) { + data := make([]byte, len(resp.pbResp.Data)) + copy(data, resp.pbResp.Data) + + newCacheValue := coprCacheValue{ + Data: data, + TimeStamp: worker.req.StartTs, + RegionID: task.region.GetID(), + RegionDataVersion: resp.pbResp.CacheLastVersion, + } + // When paging protocol is used, the response key range is part of the cache data. + if r := resp.pbResp.GetRange(); r != nil { + newCacheValue.PageStart = append([]byte{}, r.GetStart()...) + newCacheValue.PageEnd = append([]byte{}, r.GetEnd()...) + } + worker.store.coprCache.Set(cacheKey, &newCacheValue) + } + } + } + return nil +} + +func (worker *copIteratorWorker) getLockResolverDetails() *util.ResolveLockDetail { + if !worker.enableCollectExecutionInfo { + return nil + } + return &util.ResolveLockDetail{} +} + +func (worker *copIteratorWorker) handleCollectExecutionInfo(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse) { + defer func() { + worker.kvclient.Stats = nil + }() + if !worker.enableCollectExecutionInfo { + return + } + failpoint.Inject("disable-collect-execution", func(val failpoint.Value) { + if val.(bool) { + panic("shouldn't reachable") + } + }) + if resp.detail == nil { + resp.detail = new(CopRuntimeStats) + } + resp.detail.Stats = worker.kvclient.Stats + backoffTimes := bo.GetBackoffTimes() + resp.detail.BackoffTime = time.Duration(bo.GetTotalSleep()) * time.Millisecond + resp.detail.BackoffSleep = make(map[string]time.Duration, len(backoffTimes)) + resp.detail.BackoffTimes = make(map[string]int, len(backoffTimes)) + for backoff := range backoffTimes { + resp.detail.BackoffTimes[backoff] = backoffTimes[backoff] + resp.detail.BackoffSleep[backoff] = time.Duration(bo.GetBackoffSleepMS()[backoff]) * time.Millisecond + } + if rpcCtx != nil { + resp.detail.CalleeAddress = rpcCtx.Addr + } + sd := &util.ScanDetail{} + td := util.TimeDetail{} + if pbDetails := resp.pbResp.ExecDetailsV2; pbDetails != nil { + // Take values in `ExecDetailsV2` first. + if pbDetails.TimeDetail != nil || pbDetails.TimeDetailV2 != nil { + td.MergeFromTimeDetail(pbDetails.TimeDetailV2, pbDetails.TimeDetail) + } + if scanDetailV2 := pbDetails.ScanDetailV2; scanDetailV2 != nil { + sd.MergeFromScanDetailV2(scanDetailV2) + } + } else if pbDetails := resp.pbResp.ExecDetails; pbDetails != nil { + if timeDetail := pbDetails.TimeDetail; timeDetail != nil { + td.MergeFromTimeDetail(nil, timeDetail) + } + if scanDetail := pbDetails.ScanDetail; scanDetail != nil { + if scanDetail.Write != nil { + sd.ProcessedKeys = scanDetail.Write.Processed + sd.TotalKeys = scanDetail.Write.Total + } + } + } + resp.detail.ScanDetail = sd + resp.detail.TimeDetail = td +} + +// CopRuntimeStats contains execution detail information. +type CopRuntimeStats struct { + execdetails.ExecDetails + tikv.RegionRequestRuntimeStats + + CoprCacheHit bool +} + +func (worker *copIteratorWorker) handleTiDBSendReqErr(err error, task *copTask, ch chan<- *copResponse) error { + errCode := errno.ErrUnknown + errMsg := err.Error() + if terror.ErrorEqual(err, derr.ErrTiKVServerTimeout) { + errCode = errno.ErrTiKVServerTimeout + errMsg = "TiDB server timeout, address is " + task.storeAddr + } + if terror.ErrorEqual(err, derr.ErrTiFlashServerTimeout) { + errCode = errno.ErrTiFlashServerTimeout + errMsg = "TiDB server timeout, address is " + task.storeAddr + } + selResp := tipb.SelectResponse{ + Warnings: []*tipb.Error{ + { + Code: int32(errCode), + Msg: errMsg, + }, + }, + } + data, err := proto.Marshal(&selResp) + if err != nil { + return errors.Trace(err) + } + resp := &copResponse{ + pbResp: &coprocessor.Response{ + Data: data, + }, + detail: &CopRuntimeStats{}, + } + worker.sendToRespCh(resp, ch, true) + return nil +} + +// calculateRetry splits the input ranges into two, and take one of them according to desc flag. +// It's used in paging API, to calculate which range is consumed and what needs to be retry. +// For example: +// ranges: [r1 --> r2) [r3 --> r4) +// split: [s1 --> s2) +// In normal scan order, all data before s1 is consumed, so the retry ranges should be [s1 --> r2) [r3 --> r4) +// In reverse scan order, all data after s2 is consumed, so the retry ranges should be [r1 --> r2) [r3 --> s2) +func (worker *copIteratorWorker) calculateRetry(ranges *KeyRanges, split *coprocessor.KeyRange, desc bool) *KeyRanges { + if split == nil { + return ranges + } + if desc { + left, _ := ranges.Split(split.End) + return left + } + _, right := ranges.Split(split.Start) + return right +} + +// calculateRemain calculates the remain ranges to be processed, it's used in paging API. +// For example: +// ranges: [r1 --> r2) [r3 --> r4) +// split: [s1 --> s2) +// In normal scan order, all data before s2 is consumed, so the remained ranges should be [s2 --> r4) +// In reverse scan order, all data after s1 is consumed, so the remained ranges should be [r1 --> s1) +func (worker *copIteratorWorker) calculateRemain(ranges *KeyRanges, split *coprocessor.KeyRange, desc bool) *KeyRanges { + if split == nil { + return ranges + } + if desc { + left, _ := ranges.Split(split.Start) + return left + } + _, right := ranges.Split(split.End) + return right +} + +// finished checks the flags and finished channel, it tells whether the worker is finished. +func (worker *copIteratorWorker) finished() bool { + if worker.vars != nil && worker.vars.Killed != nil && atomic.LoadUint32(worker.vars.Killed) == 1 { + return true + } + select { + case <-worker.finishCh: + return true + default: + return false + } +} + +func (it *copIterator) Close() error { + if atomic.CompareAndSwapUint32(&it.closed, 0, 1) { + close(it.finishCh) + } + it.rpcCancel.CancelAll() + it.actionOnExceed.close() + it.wg.Wait() + return nil +} + +// copErrorResponse returns error when calling Next() +type copErrorResponse struct{ error } + +func (it copErrorResponse) Next(ctx context.Context) (kv.ResultSubset, error) { + return nil, it.error +} + +func (it copErrorResponse) Close() error { + return nil +} + +// rateLimitAction an OOM Action which is used to control the token if OOM triggered. The token number should be +// set on initial. Each time the Action is triggered, one token would be destroyed. If the count of the token is less +// than 2, the action would be delegated to the fallback action. +type rateLimitAction struct { + memory.BaseOOMAction + // enabled indicates whether the rateLimitAction is permitted to Action. 1 means permitted, 0 denied. + enabled uint32 + // totalTokenNum indicates the total token at initial + totalTokenNum uint + cond struct { + sync.Mutex + // exceeded indicates whether have encountered OOM situation. + exceeded bool + // remainingTokenNum indicates the count of tokens which still exists + remainingTokenNum uint + once sync.Once + // triggerCountForTest indicates the total count of the rateLimitAction's Action being executed + triggerCountForTest uint + } +} + +func newRateLimitAction(totalTokenNumber uint) *rateLimitAction { + return &rateLimitAction{ + totalTokenNum: totalTokenNumber, + cond: struct { + sync.Mutex + exceeded bool + remainingTokenNum uint + once sync.Once + triggerCountForTest uint + }{ + Mutex: sync.Mutex{}, + exceeded: false, + remainingTokenNum: totalTokenNumber, + once: sync.Once{}, + }, + } +} + +// Action implements ActionOnExceed.Action +func (e *rateLimitAction) Action(t *memory.Tracker) { + if !e.isEnabled() { + if fallback := e.GetFallback(); fallback != nil { + fallback.Action(t) + } + return + } + e.conditionLock() + defer e.conditionUnlock() + e.cond.once.Do(func() { + if e.cond.remainingTokenNum < 2 { + e.setEnabled(false) + logutil.BgLogger().Info("memory exceeds quota, rateLimitAction delegate to fallback action", + zap.Uint("total token count", e.totalTokenNum)) + if fallback := e.GetFallback(); fallback != nil { + fallback.Action(t) + } + return + } + failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { + if val.(bool) { + if e.cond.triggerCountForTest+e.cond.remainingTokenNum != e.totalTokenNum { + panic("triggerCount + remainingTokenNum not equal to totalTokenNum") + } + } + }) + logutil.BgLogger().Info("memory exceeds quota, destroy one token now.", + zap.Int64("consumed", t.BytesConsumed()), + zap.Int64("quota", t.GetBytesLimit()), + zap.Uint("total token count", e.totalTokenNum), + zap.Uint("remaining token count", e.cond.remainingTokenNum)) + e.cond.exceeded = true + e.cond.triggerCountForTest++ + }) +} + +// GetPriority get the priority of the Action. +func (e *rateLimitAction) GetPriority() int64 { + return memory.DefRateLimitPriority +} + +// destroyTokenIfNeeded will check the `exceed` flag after copWorker finished one task. +// If the exceed flag is true and there is no token been destroyed before, one token will be destroyed, +// or the token would be return back. +func (e *rateLimitAction) destroyTokenIfNeeded(returnToken func()) { + if !e.isEnabled() { + returnToken() + return + } + e.conditionLock() + defer e.conditionUnlock() + if !e.cond.exceeded { + returnToken() + return + } + // If actionOnExceed has been triggered and there is no token have been destroyed before, + // destroy one token. + e.cond.remainingTokenNum = e.cond.remainingTokenNum - 1 + e.cond.exceeded = false + e.cond.once = sync.Once{} +} + +func (e *rateLimitAction) conditionLock() { + e.cond.Lock() +} + +func (e *rateLimitAction) conditionUnlock() { + e.cond.Unlock() +} + +func (e *rateLimitAction) close() { + if !e.isEnabled() { + return + } + e.setEnabled(false) + e.conditionLock() + defer e.conditionUnlock() + e.cond.exceeded = false + e.SetFinished() +} + +func (e *rateLimitAction) setEnabled(enabled bool) { + newValue := uint32(0) + if enabled { + newValue = uint32(1) + } + atomic.StoreUint32(&e.enabled, newValue) +} + +func (e *rateLimitAction) isEnabled() bool { + return atomic.LoadUint32(&e.enabled) > 0 +} + +// priorityToPB converts priority type to wire type. +func priorityToPB(pri int) kvrpcpb.CommandPri { + switch pri { + case kv.PriorityLow: + return kvrpcpb.CommandPri_Low + case kv.PriorityHigh: + return kvrpcpb.CommandPri_High + default: + return kvrpcpb.CommandPri_Normal + } +} + +func isolationLevelToPB(level kv.IsoLevel) kvrpcpb.IsolationLevel { + switch level { + case kv.RC: + return kvrpcpb.IsolationLevel_RC + case kv.SI: + return kvrpcpb.IsolationLevel_SI + case kv.RCCheckTS: + return kvrpcpb.IsolationLevel_RCCheckTS + default: + return kvrpcpb.IsolationLevel_SI + } +} + +// BuildKeyRanges is used for test, quickly build key ranges from paired keys. +func BuildKeyRanges(keys ...string) []kv.KeyRange { + var ranges []kv.KeyRange + for i := 0; i < len(keys); i += 2 { + ranges = append(ranges, kv.KeyRange{ + StartKey: []byte(keys[i]), + EndKey: []byte(keys[i+1]), + }) + } + return ranges +} + +func optRowHint(req *kv.Request) bool { + opt := true + if req.StoreType == kv.TiDB { + return false + } + if req.RequestSource.RequestSourceInternal || req.Tp != kv.ReqTypeDAG { + // disable extra concurrency for internal tasks. + return false + } + failpoint.Inject("disableFixedRowCountHint", func(_ failpoint.Value) { + opt = false + }) + return opt +} + +func checkStoreBatchCopr(req *kv.Request) bool { + if req.Tp != kv.ReqTypeDAG || req.StoreType != kv.TiKV { + return false + } + // TODO: support keep-order batch + if req.ReplicaRead != kv.ReplicaReadLeader || req.KeepOrder { + // Disable batch copr for follower read + return false + } + // Disable batch copr when paging is enabled. + if req.Paging.Enable { + return false + } + // Disable it for internal requests to avoid regression. + if req.RequestSource.RequestSourceInternal { + return false + } + return true +} diff --git a/store/copr/coprocessor_cache.go b/pkg/store/copr/coprocessor_cache.go similarity index 98% rename from store/copr/coprocessor_cache.go rename to pkg/store/copr/coprocessor_cache.go index b469200121f4e..51979e80f1745 100644 --- a/store/copr/coprocessor_cache.go +++ b/pkg/store/copr/coprocessor_cache.go @@ -25,7 +25,7 @@ import ( "github.com/dgraph-io/ristretto" "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" - copr_metrics "github.com/pingcap/tidb/store/copr/metrics" + copr_metrics "github.com/pingcap/tidb/pkg/store/copr/metrics" "github.com/tikv/client-go/v2/config" ) diff --git a/store/copr/coprocessor_cache_test.go b/pkg/store/copr/coprocessor_cache_test.go similarity index 99% rename from store/copr/coprocessor_cache_test.go rename to pkg/store/copr/coprocessor_cache_test.go index 87639a0bf6684..811960a3d56de 100644 --- a/store/copr/coprocessor_cache_test.go +++ b/pkg/store/copr/coprocessor_cache_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/config" ) diff --git a/pkg/store/copr/coprocessor_test.go b/pkg/store/copr/coprocessor_test.go new file mode 100644 index 0000000000000..219a457f88420 --- /dev/null +++ b/pkg/store/copr/coprocessor_test.go @@ -0,0 +1,793 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "context" + "testing" + + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/util/paging" + "github.com/pingcap/tidb/pkg/util/trxevents" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" + "github.com/tikv/client-go/v2/tikv" +) + +func buildTestCopTasks(bo *Backoffer, cache *RegionCache, ranges *KeyRanges, req *kv.Request, eventCb trxevents.EventCallback) ([]*copTask, error) { + return buildCopTasks(bo, ranges, &buildCopTaskOpt{ + req: req, + cache: cache, + eventCb: eventCb, + respChan: true, + }) +} + +func TestBuildTasksWithoutBuckets(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + + _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + req := &kv.Request{} + flashReq := &kv.Request{} + flashReq.StoreType = kv.TiFlash + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("m", "n"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[1], 0, "m", "n") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("m", "n"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[1], 0, "m", "n") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "k"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") + taskEqual(t, tasks[1], regionIDs[1], 0, "g", "k") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "k"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") + taskEqual(t, tasks[1], regionIDs[1], 0, "g", "k") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "x"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 4) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") + taskEqual(t, tasks[1], regionIDs[1], 0, "g", "n") + taskEqual(t, tasks[2], regionIDs[2], 0, "n", "t") + taskEqual(t, tasks[3], regionIDs[3], 0, "t", "x") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "x"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 4) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") + taskEqual(t, tasks[1], regionIDs[1], 0, "g", "n") + taskEqual(t, tasks[2], regionIDs[2], 0, "n", "t") + taskEqual(t, tasks[3], regionIDs[3], 0, "t", "x") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "b", "c"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "b", "c") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "b", "c"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "b", "c") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "e", "f"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "e", "f") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "e", "f"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "e", "f") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n", "o", "p"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") + taskEqual(t, tasks[1], regionIDs[2], 0, "o", "p") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n", "o", "p"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") + taskEqual(t, tasks[1], regionIDs[2], 0, "o", "p") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("h", "k", "m", "p"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[1], 0, "h", "k", "m", "n") + taskEqual(t, tasks[1], regionIDs[2], 0, "n", "p") + + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("h", "k", "m", "p"), flashReq, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[1], 0, "h", "k", "m", "n") + taskEqual(t, tasks[1], regionIDs[2], 0, "n", "p") +} + +func TestBuildTasksByBuckets(t *testing.T) { + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + + // region: nil------------------n-----------x-----------nil + // buckets: nil----c----g----k---n----t------x-----------nil + _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("n"), []byte("x")) + cluster.SplitRegionBuckets(regionIDs[0], [][]byte{{}, {'c'}, {'g'}, {'k'}, {'n'}}, regionIDs[0]) + cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'t'}, {'x'}}, regionIDs[1]) + cluster.SplitRegionBuckets(regionIDs[2], [][]byte{{'x'}, {}}, regionIDs[2]) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + // one range per bucket + // region: nil------------------n-----------x-----------nil + // buckets: nil----c----g----k---n----t------x-----------nil + // range&task: a-b c-d h-i k---n o-p u--x-----------nil + req := &kv.Request{} + regionRanges := []struct { + regionID uint64 + ranges []string + }{ + {regionIDs[0], []string{"a", "b", "c", "d", "h", "i", "k", "n"}}, + {regionIDs[1], []string{"o", "p", "u", "x"}}, + {regionIDs[2], []string{"x", ""}}, + } + for _, regionRange := range regionRanges { + regionID, ranges := regionRange.regionID, regionRange.ranges + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges(ranges...), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(ranges)/2) + for i, task := range tasks { + taskEqual(t, task, regionID, regionID, ranges[2*i], ranges[2*i+1]) + } + } + + // one request multiple regions + allRanges := []string{} + for _, regionRange := range regionRanges { + allRanges = append(allRanges, regionRange.ranges...) + } + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges(allRanges...), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(allRanges)/2) + taskIdx := 0 + for _, regionRange := range regionRanges { + regionID, ranges := regionRange.regionID, regionRange.ranges + for i := 0; i < len(ranges); i += 2 { + taskEqual(t, tasks[taskIdx], regionID, regionID, ranges[i], ranges[i+1]) + taskIdx++ + } + } + + // serveral ranges per bucket + // region: nil---------------------------n-----------x-----------nil + // buckets: nil-----c-------g-------k-----n----t------x-----------nil + // ranges: nil-a b-c d-e f-g h-i j-k-l m-n + // tasks: nil-a b-c + // d-e f-g + // h-i j-k + // k-l m-n + keyRanges := []string{ + "", "a", "b", "c", + "d", "e", "f", "g", + "h", "i", "j", "k", + "k", "l", "m", "n", + } + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges(keyRanges...), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(keyRanges)/4) + for i, task := range tasks { + taskEqual(t, task, regionIDs[0], regionIDs[0], keyRanges[4*i], keyRanges[4*i+1], keyRanges[4*i+2], keyRanges[4*i+3]) + } + + // cross bucket ranges + // buckets: nil-----c-------g---------k---n----t------x-----------nil + // ranges: nil-------d e---h i---j + // tasks: nil-----c + // c-d e-g + // g-h i---j + keyRanges = []string{ + "", "d", "e", "h", "i", "j", + } + expectedTaskRanges := [][]string{ + {"", "c"}, + {"c", "d", "e", "g"}, + {"g", "h", "i", "j"}, + } + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges(keyRanges...), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(expectedTaskRanges)) + for i, task := range tasks { + taskEqual(t, task, regionIDs[0], regionIDs[0], expectedTaskRanges[i]...) + } + + // cross several buckets ranges + // region: n ----------------------------- x + // buckets: n -- q -- r -- t -- u -- v -- x + // ranges: n--o p--q s ------------ w + // tasks: n--o p--q + // s--t + // t -- u + // u -- v + // v--w + expectedTaskRanges = [][]string{ + {"n", "o", "p", "q"}, + {"s", "t"}, + {"t", "u"}, + {"u", "v"}, + {"v", "w"}, + } + cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'q'}, {'r'}, {'t'}, {'u'}, {'v'}, {'x'}}, regionIDs[1]) + cache = NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "o", "p", "q", "s", "w"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(expectedTaskRanges)) + for i, task := range tasks { + taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) + } + + // out of range buckets + // region: n------------------x + // buckets: q---s---u + // ranges: n-o p ----s t---v w-x + // tasks: n-o p-q + // q--s + // t-u + // u-v w-x + expectedTaskRanges = [][]string{ + {"n", "o", "p", "q"}, + {"q", "s"}, + {"t", "u"}, + {"u", "v", "w", "x"}, + } + cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'q'}, {'s'}, {'u'}}, regionIDs[1]) + cache = NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "o", "p", "s", "t", "v", "w", "x"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(expectedTaskRanges)) + for i, task := range tasks { + taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) + } + + // out of range buckets + // region: n------------x + // buckets: g-------t---------z + // ranges: o-p u-w + // tasks: o-p + // u-w + expectedTaskRanges = [][]string{ + {"o", "p"}, + {"u", "w"}, + } + cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'g'}, {'t'}, {'z'}}, regionIDs[1]) + cache = NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("o", "p", "u", "w"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(expectedTaskRanges)) + for i, task := range tasks { + taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) + } + + // cover the whole region + // region: n--------------x + // buckets: n -- q -- r -- x + // ranges: n--------------x + // tasks: o -- q + // q -- r + // r -- x + expectedTaskRanges = [][]string{ + {"n", "q"}, + {"q", "r"}, + {"r", "x"}, + } + cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'q'}, {'r'}, {'x'}}, regionIDs[1]) + cache = NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "x"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, len(expectedTaskRanges)) + for i, task := range tasks { + taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) + } +} + +func TestSplitRegionRanges(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + + testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + ranges, err := cache.SplitRegionRanges(bo, BuildKeyRanges("a", "c"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 1) + rangeEqual(t, ranges, "a", "c") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("h", "y"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 3) + rangeEqual(t, ranges, "h", "n", "n", "t", "t", "y") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("s", "z"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 2) + rangeEqual(t, ranges, "s", "t", "t", "z") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("s", "s"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 1) + rangeEqual(t, ranges, "s", "s") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("t", "t"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 1) + rangeEqual(t, ranges, "t", "t") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("t", "u"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 1) + rangeEqual(t, ranges, "t", "u") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("u", "z"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 1) + rangeEqual(t, ranges, "u", "z") + + // min --> max + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("a", "z"), UnspecifiedLimit) + require.NoError(t, err) + require.Len(t, ranges, 4) + rangeEqual(t, ranges, "a", "g", "g", "n", "n", "t", "t", "z") + + ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("a", "z"), 3) + require.NoError(t, err) + require.Len(t, ranges, 3) + rangeEqual(t, ranges, "a", "g", "g", "n", "n", "t") +} + +func TestRebuild(t *testing.T) { + // nil --- 'm' --- nil + // <- 0 -> <- 1 -> + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + + storeID, regionIDs, peerIDs := testutils.BootstrapWithMultiRegions(cluster, []byte("m")) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + req := &kv.Request{} + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "z"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 2) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "m") + taskEqual(t, tasks[1], regionIDs[1], 0, "m", "z") + + // nil -- 'm' -- 'q' -- nil + // <- 0 -> <--1-> <-2--> + regionIDs = append(regionIDs, cluster.AllocID()) + peerIDs = append(peerIDs, cluster.AllocID()) + cluster.Split(regionIDs[1], regionIDs[2], []byte("q"), []uint64{peerIDs[2]}, storeID) + cache.InvalidateCachedRegion(tasks[1].region) + + req.Desc = true + tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "z"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 3) + taskEqual(t, tasks[2], regionIDs[0], 0, "a", "m") + taskEqual(t, tasks[1], regionIDs[1], 0, "m", "q") + taskEqual(t, tasks[0], regionIDs[2], 0, "q", "z") +} + +func buildCopRanges(keys ...string) *KeyRanges { + return NewKeyRanges(BuildKeyRanges(keys...)) +} + +func taskEqual(t *testing.T, task *copTask, regionID, bucketsVer uint64, keys ...string) { + require.Equal(t, task.region.GetID(), regionID) + require.Equal(t, task.bucketsVer, bucketsVer) + for i := 0; i < task.ranges.Len(); i++ { + r := task.ranges.At(i) + require.Equal(t, string(r.StartKey), keys[2*i]) + require.Equal(t, string(r.EndKey), keys[2*i+1]) + } +} + +func rangeEqual(t *testing.T, ranges []kv.KeyRange, keys ...string) { + for i := 0; i < len(ranges); i++ { + r := ranges[i] + require.Equal(t, string(r.StartKey), keys[2*i]) + require.Equal(t, string(r.EndKey), keys[2*i+1]) + } +} + +func TestBuildPagingTasks(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + + _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + req := &kv.Request{} + req.Paging.Enable = true + req.Paging.MinPagingSize = paging.MinPagingSize + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") + require.True(t, tasks[0].paging) + require.Equal(t, tasks[0].pagingSize, paging.MinPagingSize) +} + +func TestBuildPagingTasksDisablePagingForSmallLimit(t *testing.T) { + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + + req := &kv.Request{} + req.Paging.Enable = true + req.Paging.MinPagingSize = paging.MinPagingSize + req.LimitSize = 1 + tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) + require.NoError(t, err) + require.Len(t, tasks, 1) + require.Len(t, tasks, 1) + taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") + require.False(t, tasks[0].paging) + require.Equal(t, tasks[0].pagingSize, uint64(0)) +} + +func toCopRange(r kv.KeyRange) *coprocessor.KeyRange { + coprRange := coprocessor.KeyRange{} + coprRange.Start = r.StartKey + coprRange.End = r.EndKey + return &coprRange +} + +func toRange(r *KeyRanges) []kv.KeyRange { + ranges := make([]kv.KeyRange, 0, r.Len()) + if r.first != nil { + ranges = append(ranges, *r.first) + } + ranges = append(ranges, r.mid...) + if r.last != nil { + ranges = append(ranges, *r.last) + } + return ranges +} + +func TestCalculateRetry(t *testing.T) { + worker := copIteratorWorker{} + + // split in one range + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("b", "c")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) + rangeEqual(t, toRange(retry), "b", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("e", "f")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) + rangeEqual(t, toRange(retry), "a", "c", "e", "f") + } + + // across ranges + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("b", "f")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) + rangeEqual(t, toRange(retry), "b", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("b", "f")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) + rangeEqual(t, toRange(retry), "a", "c", "e", "f") + } + + // exhaust the ranges + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "g")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) + rangeEqual(t, toRange(retry), "a", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "g")[0] + retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) + rangeEqual(t, toRange(retry), "a", "c", "e", "g") + } + + // nil range + { + ranges := BuildKeyRanges("a", "c", "e", "g") + retry := worker.calculateRetry(NewKeyRanges(ranges), nil, false) + rangeEqual(t, toRange(retry), "a", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + retry := worker.calculateRetry(NewKeyRanges(ranges), nil, true) + rangeEqual(t, toRange(retry), "a", "c", "e", "g") + } +} + +func TestCalculateRemain(t *testing.T) { + worker := copIteratorWorker{} + + // split in one range + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "b")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) + rangeEqual(t, toRange(remain), "b", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("f", "g")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) + rangeEqual(t, toRange(remain), "a", "c", "e", "f") + } + + // across ranges + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "f")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) + rangeEqual(t, toRange(remain), "f", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("b", "g")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) + rangeEqual(t, toRange(remain), "a", "b") + } + + // exhaust the ranges + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "g")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) + require.Equal(t, remain.Len(), 0) + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + split := BuildKeyRanges("a", "g")[0] + remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) + require.Equal(t, remain.Len(), 0) + } + + // nil range + { + ranges := BuildKeyRanges("a", "c", "e", "g") + remain := worker.calculateRemain(NewKeyRanges(ranges), nil, false) + rangeEqual(t, toRange(remain), "a", "c", "e", "g") + } + { + ranges := BuildKeyRanges("a", "c", "e", "g") + remain := worker.calculateRemain(NewKeyRanges(ranges), nil, true) + rangeEqual(t, toRange(remain), "a", "c", "e", "g") + } +} + +func TestBasicSmallTaskConc(t *testing.T) { + require.False(t, isSmallTask(&copTask{RowCountHint: -1})) + require.False(t, isSmallTask(&copTask{RowCountHint: 0})) + require.True(t, isSmallTask(&copTask{RowCountHint: 1})) + require.True(t, isSmallTask(&copTask{RowCountHint: 6})) + require.True(t, isSmallTask(&copTask{RowCountHint: CopSmallTaskRow})) + require.False(t, isSmallTask(&copTask{RowCountHint: CopSmallTaskRow + 1})) + _, conc := smallTaskConcurrency([]*copTask{}, 16) + require.GreaterOrEqual(t, conc, 0) +} + +func TestBuildCopTasksWithRowCountHint(t *testing.T) { + // nil --- 'g' --- 'n' --- 't' --- nil + // <- 0 -> <- 1 -> <- 2 -> <- 3 -> + mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + defer func() { + pdClient.Close() + err = mockClient.Close() + require.NoError(t, err) + }() + _, _, _ = testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) + pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + defer pdCli.Close() + cache := NewRegionCache(tikv.NewRegionCache(pdCli)) + defer cache.Close() + + bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) + req := &kv.Request{} + ranges := buildCopRanges("a", "c", "d", "e", "h", "x", "y", "z") + tasks, err := buildCopTasks(bo, ranges, &buildCopTaskOpt{ + req: req, + cache: cache, + rowHints: []int{1, 1, 3, CopSmallTaskRow}, + }) + require.Nil(t, err) + require.Equal(t, len(tasks), 4) + // task[0] ["a"-"c", "d"-"e"] + require.Equal(t, tasks[0].RowCountHint, 2) + // task[1] ["h"-"n"] + require.Equal(t, tasks[1].RowCountHint, 3) + // task[2] ["n"-"t"] + require.Equal(t, tasks[2].RowCountHint, 3) + // task[3] ["t"-"x", "y"-"z"] + require.Equal(t, tasks[3].RowCountHint, 3+CopSmallTaskRow) + _, conc := smallTaskConcurrency(tasks, 16) + require.Equal(t, conc, 1) + + ranges = buildCopRanges("a", "c", "d", "e", "h", "x", "y", "z") + tasks, err = buildCopTasks(bo, ranges, &buildCopTaskOpt{ + req: req, + cache: cache, + rowHints: []int{1, 1, 3, 3}, + }) + require.Nil(t, err) + require.Equal(t, len(tasks), 4) + // task[0] ["a"-"c", "d"-"e"] + require.Equal(t, tasks[0].RowCountHint, 2) + // task[1] ["h"-"n"] + require.Equal(t, tasks[1].RowCountHint, 3) + // task[2] ["n"-"t"] + require.Equal(t, tasks[2].RowCountHint, 3) + // task[3] ["t"-"x", "y"-"z"] + require.Equal(t, tasks[3].RowCountHint, 6) + _, conc = smallTaskConcurrency(tasks, 16) + require.Equal(t, conc, 2) + + // cross-region long range + ranges = buildCopRanges("a", "z") + tasks, err = buildCopTasks(bo, ranges, &buildCopTaskOpt{ + req: req, + cache: cache, + rowHints: []int{10}, + }) + require.Nil(t, err) + require.Equal(t, len(tasks), 4) + // task[0] ["a"-"g"] + require.Equal(t, tasks[0].RowCountHint, 10) + // task[1] ["g"-"n"] + require.Equal(t, tasks[1].RowCountHint, 10) + // task[2] ["n"-"t"] + require.Equal(t, tasks[2].RowCountHint, 10) + // task[3] ["t"-"z"] + require.Equal(t, tasks[3].RowCountHint, 10) +} + +func TestSmallTaskConcurrencyLimit(t *testing.T) { + smallTaskCount := 1000 + tasks := make([]*copTask, 0, smallTaskCount) + for i := 0; i < smallTaskCount; i++ { + tasks = append(tasks, &copTask{ + RowCountHint: 1, + }) + } + count, conc := smallTaskConcurrency(tasks, 1) + require.Equal(t, smallConcPerCore, conc) + require.Equal(t, smallTaskCount, count) + // also handle 0 value. + count, conc = smallTaskConcurrency(tasks, 0) + require.Equal(t, smallConcPerCore, conc) + require.Equal(t, smallTaskCount, count) +} diff --git a/store/copr/key_ranges.go b/pkg/store/copr/key_ranges.go similarity index 99% rename from store/copr/key_ranges.go rename to pkg/store/copr/key_ranges.go index 67effbbc8b7a1..7324dde426958 100644 --- a/store/copr/key_ranges.go +++ b/pkg/store/copr/key_ranges.go @@ -21,7 +21,7 @@ import ( "unsafe" "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // KeyRanges is like []kv.KeyRange, but may has extra elements at head/tail. diff --git a/store/copr/key_ranges_test.go b/pkg/store/copr/key_ranges_test.go similarity index 99% rename from store/copr/key_ranges_test.go rename to pkg/store/copr/key_ranges_test.go index d2be4676dd589..e2893a3fef1c6 100644 --- a/store/copr/key_ranges_test.go +++ b/pkg/store/copr/key_ranges_test.go @@ -17,7 +17,7 @@ package copr import ( "testing" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" ) diff --git a/pkg/store/copr/main_test.go b/pkg/store/copr/main_test.go new file mode 100644 index 0000000000000..2322592fcc3ac --- /dev/null +++ b/pkg/store/copr/main_test.go @@ -0,0 +1,46 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +type main struct { + m *testing.M +} + +func (m *main) Run() int { + c := m.m.Run() + // In order for MVCCLevelDB to close, we need to wait one second + time.Sleep(time.Second) + return c +} + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/pingcap/goleveldb/leveldb.(*DB).mpoolDrain"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(&main{m: m}, opts...) +} diff --git a/pkg/store/copr/metrics/BUILD.bazel b/pkg/store/copr/metrics/BUILD.bazel new file mode 100644 index 0000000000000..1fb445ee388d0 --- /dev/null +++ b/pkg/store/copr/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/store/copr/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/store/copr/metrics/metrics.go b/pkg/store/copr/metrics/metrics.go new file mode 100644 index 0000000000000..484d922466735 --- /dev/null +++ b/pkg/store/copr/metrics/metrics.go @@ -0,0 +1,38 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// copr metrics vars +var ( + CoprCacheCounterEvict prometheus.Counter + CoprCacheCounterHit prometheus.Counter + CoprCacheCounterMiss prometheus.Counter +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init copr metrics vars. +func InitMetricsVars() { + CoprCacheCounterEvict = metrics.DistSQLCoprCacheCounter.WithLabelValues("evict") + CoprCacheCounterHit = metrics.DistSQLCoprCacheCounter.WithLabelValues("hit") + CoprCacheCounterMiss = metrics.DistSQLCoprCacheCounter.WithLabelValues("miss") +} diff --git a/pkg/store/copr/mpp.go b/pkg/store/copr/mpp.go new file mode 100644 index 0000000000000..cd0695a3e0d9d --- /dev/null +++ b/pkg/store/copr/mpp.go @@ -0,0 +1,338 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "context" + "strconv" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/mpp" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/tiflash" + "github.com/pingcap/tidb/pkg/util/tiflashcompute" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + pd "github.com/tikv/pd/client" + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// MPPClient servers MPP requests. +type MPPClient struct { + store *kvStore +} + +type mppStoreCnt struct { + cnt int32 + lastUpdate int64 + initFlag int32 +} + +// GetAddress returns the network address. +func (c *batchCopTask) GetAddress() string { + return c.storeAddr +} + +// ConstructMPPTasks receives ScheduleRequest, which are actually collects of kv ranges. We allocates MPPTaskMeta for them and returns. +func (c *MPPClient) ConstructMPPTasks(ctx context.Context, req *kv.MPPBuildTasksRequest, ttl time.Duration, dispatchPolicy tiflashcompute.DispatchPolicy, tiflashReplicaReadPolicy tiflash.ReplicaRead, appendWarning func(error)) ([]kv.MPPTaskMeta, error) { + ctx = context.WithValue(ctx, tikv.TxnStartKey(), req.StartTS) + bo := backoff.NewBackofferWithVars(ctx, copBuildTaskMaxBackoff, nil) + var tasks []*batchCopTask + var err error + if req.PartitionIDAndRanges != nil { + rangesForEachPartition := make([]*KeyRanges, len(req.PartitionIDAndRanges)) + partitionIDs := make([]int64, len(req.PartitionIDAndRanges)) + for i, p := range req.PartitionIDAndRanges { + rangesForEachPartition[i] = NewKeyRanges(p.KeyRanges) + partitionIDs[i] = p.ID + } + tasks, err = buildBatchCopTasksForPartitionedTable(ctx, bo, c.store, rangesForEachPartition, kv.TiFlash, true, ttl, true, 20, partitionIDs, dispatchPolicy, tiflashReplicaReadPolicy, appendWarning) + } else { + if req.KeyRanges == nil { + return nil, errors.New("KeyRanges in MPPBuildTasksRequest is nil") + } + ranges := NewKeyRanges(req.KeyRanges) + tasks, err = buildBatchCopTasksForNonPartitionedTable(ctx, bo, c.store, ranges, kv.TiFlash, true, ttl, true, 20, dispatchPolicy, tiflashReplicaReadPolicy, appendWarning) + } + + if err != nil { + return nil, errors.Trace(err) + } + mppTasks := make([]kv.MPPTaskMeta, 0, len(tasks)) + for _, copTask := range tasks { + mppTasks = append(mppTasks, copTask) + } + return mppTasks, nil +} + +// DispatchMPPTask dispatch mpp task, and returns valid response when retry = false and err is nil +func (c *MPPClient) DispatchMPPTask(param kv.DispatchMPPTaskParam) (resp *mpp.DispatchTaskResponse, retry bool, err error) { + req := param.Req + var regionInfos []*coprocessor.RegionInfo + originalTask, ok := req.Meta.(*batchCopTask) + if ok { + for _, ri := range originalTask.regionInfos { + regionInfos = append(regionInfos, ri.toCoprocessorRegionInfo()) + } + } + + // meta for current task. + taskMeta := &mpp.TaskMeta{StartTs: req.StartTs, QueryTs: req.MppQueryID.QueryTs, LocalQueryId: req.MppQueryID.LocalQueryID, TaskId: req.ID, ServerId: req.MppQueryID.ServerID, + GatherId: req.GatherID, + Address: req.Meta.GetAddress(), + CoordinatorAddress: req.CoordinatorAddress, + ReportExecutionSummary: req.ReportExecutionSummary, + MppVersion: req.MppVersion.ToInt64(), + ResourceGroupName: req.ResourceGroupName, + } + + mppReq := &mpp.DispatchTaskRequest{ + Meta: taskMeta, + EncodedPlan: req.Data, + // TODO: This is only an experience value. It's better to be configurable. + Timeout: 60, + SchemaVer: req.SchemaVar, + Regions: regionInfos, + } + if originalTask != nil { + mppReq.TableRegions = originalTask.PartitionTableRegions + if mppReq.TableRegions != nil { + mppReq.Regions = nil + } + } + + wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPTask, mppReq, kvrpcpb.Context{}) + wrappedReq.StoreTp = getEndPointType(kv.TiFlash) + + // TODO: Handle dispatch task response correctly, including retry logic and cancel logic. + var rpcResp *tikvrpc.Response + invalidPDCache := config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler + bo := backoff.NewBackofferWithTikvBo(param.Bo) + + // If copTasks is not empty, we should send request according to region distribution. + // Or else it's the task without region, which always happens in high layer task without table. + // In that case + if originalTask != nil { + sender := NewRegionBatchRequestSender(c.store.GetRegionCache(), c.store.GetTiKVClient(), param.EnableCollectExecutionInfo) + rpcResp, retry, _, err = sender.SendReqToAddr(bo, originalTask.ctx, originalTask.regionInfos, wrappedReq, tikv.ReadTimeoutMedium) + // No matter what the rpc error is, we won't retry the mpp dispatch tasks. + // TODO: If we want to retry, we must redo the plan fragment cutting and task scheduling. + // That's a hard job but we can try it in the future. + if sender.GetRPCError() != nil { + logutil.BgLogger().Warn("mpp dispatch meet io error", zap.String("error", sender.GetRPCError().Error()), zap.Uint64("timestamp", taskMeta.StartTs), zap.Int64("task", taskMeta.TaskId), zap.Int64("mpp-version", taskMeta.MppVersion)) + if invalidPDCache { + c.store.GetRegionCache().InvalidateTiFlashComputeStores() + } + err = sender.GetRPCError() + } + } else { + rpcResp, err = c.store.GetTiKVClient().SendRequest(param.Ctx, req.Meta.GetAddress(), wrappedReq, tikv.ReadTimeoutMedium) + if errors.Cause(err) == context.Canceled || status.Code(errors.Cause(err)) == codes.Canceled { + retry = false + } else if err != nil { + if invalidPDCache { + c.store.GetRegionCache().InvalidateTiFlashComputeStores() + } + if bo.Backoff(tikv.BoTiFlashRPC(), err) == nil { + retry = true + } + } + } + + if err != nil || retry { + return nil, retry, err + } + + realResp := rpcResp.Resp.(*mpp.DispatchTaskResponse) + if realResp.Error != nil { + return realResp, false, nil + } + + if len(realResp.RetryRegions) > 0 { + logutil.BgLogger().Info("TiFlash found " + strconv.Itoa(len(realResp.RetryRegions)) + " stale regions. Only first " + strconv.Itoa(mathutil.Min(10, len(realResp.RetryRegions))) + " regions will be logged if the log level is higher than Debug") + for index, retry := range realResp.RetryRegions { + id := tikv.NewRegionVerID(retry.Id, retry.RegionEpoch.ConfVer, retry.RegionEpoch.Version) + if index < 10 || log.GetLevel() <= zap.DebugLevel { + logutil.BgLogger().Info("invalid region because tiflash detected stale region", zap.String("region id", id.String())) + } + c.store.GetRegionCache().InvalidateCachedRegionWithReason(id, tikv.EpochNotMatch) + } + } + return realResp, retry, err +} + +// CancelMPPTasks cancels mpp tasks +// NOTE: We do not retry here, because retry is helpless when errors result from TiFlash or Network. If errors occur, the execution on TiFlash will finally stop after some minutes. +// This function is exclusively called, and only the first call succeeds sending tasks and setting all tasks as cancelled, while others will not work. +func (c *MPPClient) CancelMPPTasks(param kv.CancelMPPTasksParam) { + usedStoreAddrs := param.StoreAddr + reqs := param.Reqs + if len(usedStoreAddrs) == 0 || len(reqs) == 0 { + return + } + + firstReq := reqs[0] + killReq := &mpp.CancelTaskRequest{ + Meta: &mpp.TaskMeta{StartTs: firstReq.StartTs, GatherId: firstReq.GatherID, QueryTs: firstReq.MppQueryID.QueryTs, LocalQueryId: firstReq.MppQueryID.LocalQueryID, ServerId: firstReq.MppQueryID.ServerID, MppVersion: firstReq.MppVersion.ToInt64(), ResourceGroupName: firstReq.ResourceGroupName}, + } + + wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPCancel, killReq, kvrpcpb.Context{}) + wrappedReq.StoreTp = getEndPointType(kv.TiFlash) + + // send cancel cmd to all stores where tasks run + invalidPDCache := config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler + wg := util.WaitGroupWrapper{} + gotErr := atomic.Bool{} + for addr := range usedStoreAddrs { + storeAddr := addr + wg.Run(func() { + _, err := c.store.GetTiKVClient().SendRequest(context.Background(), storeAddr, wrappedReq, tikv.ReadTimeoutShort) + logutil.BgLogger().Debug("cancel task", zap.Uint64("query id ", firstReq.StartTs), zap.String("on addr", storeAddr), zap.Int64("mpp-version", firstReq.MppVersion.ToInt64())) + if err != nil { + logutil.BgLogger().Error("cancel task error", zap.Error(err), zap.Uint64("query id", firstReq.StartTs), zap.String("on addr", storeAddr), zap.Int64("mpp-version", firstReq.MppVersion.ToInt64())) + if invalidPDCache { + gotErr.CompareAndSwap(false, true) + } + } + }) + } + wg.Wait() + if invalidPDCache && gotErr.Load() { + c.store.GetRegionCache().InvalidateTiFlashComputeStores() + } +} + +// EstablishMPPConns build a mpp connection to receive data, return valid response when err is nil +func (c *MPPClient) EstablishMPPConns(param kv.EstablishMPPConnsParam) (*tikvrpc.MPPStreamResponse, error) { + req := param.Req + taskMeta := param.TaskMeta + connReq := &mpp.EstablishMPPConnectionRequest{ + SenderMeta: taskMeta, + ReceiverMeta: &mpp.TaskMeta{ + StartTs: req.StartTs, + GatherId: req.GatherID, + QueryTs: req.MppQueryID.QueryTs, + LocalQueryId: req.MppQueryID.LocalQueryID, + ServerId: req.MppQueryID.ServerID, + MppVersion: req.MppVersion.ToInt64(), + TaskId: -1, + ResourceGroupName: req.ResourceGroupName, + }, + } + + var err error + + wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPConn, connReq, kvrpcpb.Context{}) + wrappedReq.StoreTp = getEndPointType(kv.TiFlash) + + // Drain results from root task. + // We don't need to process any special error. When we meet errors, just let it fail. + rpcResp, err := c.store.GetTiKVClient().SendRequest(param.Ctx, req.Meta.GetAddress(), wrappedReq, TiFlashReadTimeoutUltraLong) + + if err != nil { + logutil.BgLogger().Warn("establish mpp connection meet error and cannot retry", zap.String("error", err.Error()), zap.Uint64("timestamp", taskMeta.StartTs), zap.Int64("task", taskMeta.TaskId), zap.Int64("mpp-version", taskMeta.MppVersion)) + if config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler { + c.store.GetRegionCache().InvalidateTiFlashComputeStores() + } + return nil, err + } + + streamResponse := rpcResp.Resp.(*tikvrpc.MPPStreamResponse) + return streamResponse, nil +} + +// CheckVisibility checks if it is safe to read using given ts. +func (c *MPPClient) CheckVisibility(startTime uint64) error { + return c.store.CheckVisibility(startTime) +} + +func (c *mppStoreCnt) getMPPStoreCount(ctx context.Context, pdClient pd.Client, TTL int64) (int, error) { + failpoint.Inject("mppStoreCountSetLastUpdateTime", func(value failpoint.Value) { + v, _ := strconv.ParseInt(value.(string), 10, 0) + c.lastUpdate = v + }) + + lastUpdate := atomic.LoadInt64(&c.lastUpdate) + now := time.Now().UnixMicro() + isInit := atomic.LoadInt32(&c.initFlag) != 0 + + if now-lastUpdate < TTL { + if isInit { + return int(atomic.LoadInt32(&c.cnt)), nil + } + } + + failpoint.Inject("mppStoreCountSetLastUpdateTimeP2", func(value failpoint.Value) { + v, _ := strconv.ParseInt(value.(string), 10, 0) + c.lastUpdate = v + }) + + if !atomic.CompareAndSwapInt64(&c.lastUpdate, lastUpdate, now) { + if isInit { + return int(atomic.LoadInt32(&c.cnt)), nil + } + // if has't initialized, always fetch latest mpp store info + } + + // update mpp store cache + cnt := 0 + stores, err := pdClient.GetAllStores(ctx, pd.WithExcludeTombstone()) + + failpoint.Inject("mppStoreCountPDError", func(value failpoint.Value) { + if value.(bool) { + err = errors.New("failed to get mpp store count") + } + }) + + if err != nil { + // always to update cache next time + atomic.StoreInt32(&c.initFlag, 0) + return 0, err + } + for _, s := range stores { + if !tikv.LabelFilterNoTiFlashWriteNode(s.GetLabels()) { + continue + } + cnt += 1 + } + failpoint.Inject("mppStoreCountSetMPPCnt", func(value failpoint.Value) { + cnt = value.(int) + }) + + if !isInit || atomic.LoadInt64(&c.lastUpdate) == now { + atomic.StoreInt32(&c.cnt, int32(cnt)) + atomic.StoreInt32(&c.initFlag, 1) + } + + return cnt, nil +} + +// GetMPPStoreCount returns number of TiFlash stores +func (c *MPPClient) GetMPPStoreCount() (int, error) { + return c.store.mppStoreCnt.getMPPStoreCount(c.store.store.Ctx(), c.store.store.GetPDClient(), 120*1e6 /* TTL 120sec */) +} diff --git a/store/copr/mpp_probe.go b/pkg/store/copr/mpp_probe.go similarity index 98% rename from store/copr/mpp_probe.go rename to pkg/store/copr/mpp_probe.go index 0a0eba286648e..ad63199e49dc4 100644 --- a/store/copr/mpp_probe.go +++ b/pkg/store/copr/mpp_probe.go @@ -23,8 +23,8 @@ import ( "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" "go.uber.org/zap" diff --git a/store/copr/mpp_probe_test.go b/pkg/store/copr/mpp_probe_test.go similarity index 100% rename from store/copr/mpp_probe_test.go rename to pkg/store/copr/mpp_probe_test.go diff --git a/store/copr/region_cache.go b/pkg/store/copr/region_cache.go similarity index 97% rename from store/copr/region_cache.go rename to pkg/store/copr/region_cache.go index b6465ef8aefd0..14ee9fa8357b6 100644 --- a/store/copr/region_cache.go +++ b/pkg/store/copr/region_cache.go @@ -23,11 +23,11 @@ import ( "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/driver/options" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/kv" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/driver/options" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/tikv/client-go/v2/metrics" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" diff --git a/pkg/store/copr/store.go b/pkg/store/copr/store.go new file mode 100644 index 0000000000000..c5115ae1c4729 --- /dev/null +++ b/pkg/store/copr/store.go @@ -0,0 +1,143 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package copr + +import ( + "context" + "math/rand" + "runtime" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + tidb_config "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/driver/backoff" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/tikv/client-go/v2/config" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" +) + +type kvStore struct { + store *tikv.KVStore + mppStoreCnt *mppStoreCnt +} + +// GetRegionCache returns the region cache instance. +func (s *kvStore) GetRegionCache() *RegionCache { + return &RegionCache{s.store.GetRegionCache()} +} + +// CheckVisibility checks if it is safe to read using given ts. +func (s *kvStore) CheckVisibility(startTime uint64) error { + err := s.store.CheckVisibility(startTime) + return derr.ToTiDBErr(err) +} + +// GetTiKVClient gets the client instance. +func (s *kvStore) GetTiKVClient() tikv.Client { + client := s.store.GetTiKVClient() + return &tikvClient{c: client} +} + +type tikvClient struct { + c tikv.Client +} + +func (c *tikvClient) Close() error { + err := c.c.Close() + return derr.ToTiDBErr(err) +} + +func (c *tikvClient) CloseAddr(addr string) error { + err := c.c.CloseAddr(addr) + return derr.ToTiDBErr(err) +} + +// SendRequest sends Request. +func (c *tikvClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { + res, err := c.c.SendRequest(ctx, addr, req, timeout) + return res, derr.ToTiDBErr(err) +} + +// Store wraps tikv.KVStore and provides coprocessor utilities. +type Store struct { + *kvStore + coprCache *coprCache + replicaReadSeed uint32 + numcpu int +} + +// NewStore creates a new store instance. +func NewStore(s *tikv.KVStore, coprCacheConfig *config.CoprocessorCache) (*Store, error) { + coprCache, err := newCoprCache(coprCacheConfig) + if err != nil { + return nil, errors.Trace(err) + } + + /* #nosec G404 */ + return &Store{ + kvStore: &kvStore{store: s, mppStoreCnt: &mppStoreCnt{}}, + coprCache: coprCache, + replicaReadSeed: rand.Uint32(), + numcpu: runtime.GOMAXPROCS(0), + }, nil +} + +// Close releases resources allocated for coprocessor. +func (s *Store) Close() { + if s.coprCache != nil { + s.coprCache.cache.Close() + } +} + +func (s *Store) nextReplicaReadSeed() uint32 { + return atomic.AddUint32(&s.replicaReadSeed, 1) +} + +// GetClient gets a client instance. +func (s *Store) GetClient() kv.Client { + return &CopClient{ + store: s, + replicaReadSeed: s.nextReplicaReadSeed(), + } +} + +// GetMPPClient gets a mpp client instance. +func (s *Store) GetMPPClient() kv.MPPClient { + return &MPPClient{ + store: s.kvStore, + } +} + +func getEndPointType(t kv.StoreType) tikvrpc.EndpointType { + switch t { + case kv.TiKV: + return tikvrpc.TiKV + case kv.TiFlash: + if tidb_config.GetGlobalConfig().DisaggregatedTiFlash { + return tikvrpc.TiFlashCompute + } + return tikvrpc.TiFlash + case kv.TiDB: + return tikvrpc.TiDB + default: + return tikvrpc.TiKV + } +} + +// Backoffer wraps tikv.Backoffer and converts the error which returns by the functions of tikv.Backoffer to tidb error. +type Backoffer = backoff.Backoffer diff --git a/pkg/store/driver/BUILD.bazel b/pkg/store/driver/BUILD.bazel new file mode 100644 index 0000000000000..cf59533038170 --- /dev/null +++ b/pkg/store/driver/BUILD.bazel @@ -0,0 +1,67 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "driver", + srcs = ["tikv_driver.go"], + importpath = "github.com/pingcap/tidb/pkg/store/driver", + visibility = ["//visibility:public"], + deps = [ + "//pkg/executor/importer", + "//pkg/kv", + "//pkg/sessionctx/variable", + "//pkg/store/copr", + "//pkg/store/driver/error", + "//pkg/store/driver/txn", + "//pkg/store/gcworker", + "//pkg/util/logutil", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//keepalive", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "driver_test", + timeout = "short", + srcs = [ + "client_test.go", + "config_test.go", + "main_test.go", + "snap_interceptor_test.go", + "sql_fail_test.go", + "txn_test.go", + ], + embed = [":driver"], + flaky = True, + shard_count = 8, + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/store/copr", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//mock", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/driver/backoff/BUILD.bazel b/pkg/store/driver/backoff/BUILD.bazel new file mode 100644 index 0000000000000..ddd54805fa6c0 --- /dev/null +++ b/pkg/store/driver/backoff/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "backoff", + srcs = ["backoff.go"], + importpath = "github.com/pingcap/tidb/pkg/store/driver/backoff", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/store/driver/error", + "@com_github_tikv_client_go_v2//tikv", + ], +) diff --git a/store/driver/backoff/backoff.go b/pkg/store/driver/backoff/backoff.go similarity index 96% rename from store/driver/backoff/backoff.go rename to pkg/store/driver/backoff/backoff.go index 2914c9bfe7de8..86f8f7c7bc897 100644 --- a/store/driver/backoff/backoff.go +++ b/pkg/store/driver/backoff/backoff.go @@ -17,8 +17,8 @@ package backoff import ( "context" - "github.com/pingcap/tidb/kv" - derr "github.com/pingcap/tidb/store/driver/error" + "github.com/pingcap/tidb/pkg/kv" + derr "github.com/pingcap/tidb/pkg/store/driver/error" "github.com/tikv/client-go/v2/tikv" ) diff --git a/pkg/store/driver/client_test.go b/pkg/store/driver/client_test.go new file mode 100644 index 0000000000000..cb0c4abaa20c1 --- /dev/null +++ b/pkg/store/driver/client_test.go @@ -0,0 +1,111 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package driver + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" +) + +type mockTiKVClient struct { + tikv.Client + mock.Mock +} + +func (c *mockTiKVClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { + args := c.Called(ctx, addr, req, timeout) + var resp *tikvrpc.Response + if v := args.Get(0); v != nil { + resp = v.(*tikvrpc.Response) + } + return resp, args.Error(1) +} + +func TestInjectTracingClient(t *testing.T) { + cases := []struct { + name string + trace *model.TraceInfo + existSourceStmt *kvrpcpb.SourceStmt + }{ + { + name: "trace is nil", + trace: nil, + }, + { + name: "trace not nil", + trace: &model.TraceInfo{ + ConnectionID: 123, + SessionAlias: "alias123", + }, + }, + { + name: "only connection id in trace valid", + trace: &model.TraceInfo{ + ConnectionID: 456, + }, + }, + { + name: "only session alias in trace valid and sourceStmt exists", + trace: &model.TraceInfo{ + SessionAlias: "alias456", + }, + existSourceStmt: &kvrpcpb.SourceStmt{}, + }, + } + + cli := &mockTiKVClient{} + inject := injectTraceClient{Client: cli} + for _, c := range cases { + ctx := context.Background() + if c.trace != nil { + ctx = tracing.ContextWithTraceInfo(ctx, c.trace) + } + + req := &tikvrpc.Request{} + expectedResp := &tikvrpc.Response{} + verifySendRequest := func(args mock.Arguments) { + inj := args.Get(2).(*tikvrpc.Request) + if c.trace == nil { + require.Nil(t, inj.Context.SourceStmt, c.name) + } else { + require.NotNil(t, inj.Context.SourceStmt, c.name) + require.Equal(t, c.trace.ConnectionID, inj.Context.SourceStmt.ConnectionId, c.name) + require.Equal(t, c.trace.SessionAlias, inj.Context.SourceStmt.SessionAlias, c.name) + } + } + + cli.On("SendRequest", ctx, "addr1", req, time.Second).Return(expectedResp, nil).Once().Run(verifySendRequest) + resp, err := inject.SendRequest(ctx, "addr1", req, time.Second) + cli.AssertExpectations(t) + require.NoError(t, err) + require.Same(t, expectedResp, resp) + + expectedErr := errors.New("mockErr") + cli.On("SendRequest", ctx, "addr2", req, time.Minute).Return(nil, expectedErr).Once().Run(verifySendRequest) + resp, err = inject.SendRequest(ctx, "addr2", req, time.Minute) + require.Same(t, expectedErr, err) + require.Nil(t, resp) + } +} diff --git a/store/driver/config_test.go b/pkg/store/driver/config_test.go similarity index 100% rename from store/driver/config_test.go rename to pkg/store/driver/config_test.go diff --git a/pkg/store/driver/error/BUILD.bazel b/pkg/store/driver/error/BUILD.bazel new file mode 100644 index 0000000000000..a600ccc2f27b3 --- /dev/null +++ b/pkg/store/driver/error/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "error", + srcs = ["error.go"], + importpath = "github.com/pingcap/tidb/pkg/store/driver/error", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/util/dbterror", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_pd_client//errs", + ], +) + +go_test( + name = "error_test", + timeout = "short", + srcs = ["error_test.go"], + embed = [":error"], + flaky = True, + deps = [ + "//pkg/parser/terror", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//assert", + "@com_github_tikv_client_go_v2//error", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/driver/error/error.go b/pkg/store/driver/error/error.go new file mode 100644 index 0000000000000..4b65d0512de86 --- /dev/null +++ b/pkg/store/driver/error/error.go @@ -0,0 +1,187 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package error //nolint: predeclared + +import ( + stderrs "errors" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/dbterror" + tikverr "github.com/tikv/client-go/v2/error" + pderr "github.com/tikv/pd/client/errs" +) + +// tikv error instance +var ( + // ErrTokenLimit is the error that token is up to the limit. + ErrTokenLimit = dbterror.ClassTiKV.NewStd(errno.ErrTiKVStoreLimit) + // ErrTiKVServerTimeout is the error when tikv server is timeout. + ErrTiKVServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrTiKVServerTimeout) + ErrTiFlashServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrTiFlashServerTimeout) + // ErrGCTooEarly is the error that GC life time is shorter than transaction duration + ErrGCTooEarly = dbterror.ClassTiKV.NewStd(errno.ErrGCTooEarly) + // ErrTiKVStaleCommand is the error that the command is stale in tikv. + ErrTiKVStaleCommand = dbterror.ClassTiKV.NewStd(errno.ErrTiKVStaleCommand) + // ErrQueryInterrupted is the error when the query is interrupted. + ErrQueryInterrupted = dbterror.ClassTiKV.NewStd(errno.ErrQueryInterrupted) + // ErrTiKVMaxTimestampNotSynced is the error that tikv's max timestamp is not synced. + ErrTiKVMaxTimestampNotSynced = dbterror.ClassTiKV.NewStd(errno.ErrTiKVMaxTimestampNotSynced) + // ErrLockAcquireFailAndNoWaitSet is the error that acquire the lock failed while no wait is setted. + ErrLockAcquireFailAndNoWaitSet = dbterror.ClassTiKV.NewStd(errno.ErrLockAcquireFailAndNoWaitSet) + ErrResolveLockTimeout = dbterror.ClassTiKV.NewStd(errno.ErrResolveLockTimeout) + // ErrLockWaitTimeout is the error that wait for the lock is timeout. + ErrLockWaitTimeout = dbterror.ClassTiKV.NewStd(errno.ErrLockWaitTimeout) + // ErrTiKVServerBusy is the error when tikv server is busy. + ErrTiKVServerBusy = dbterror.ClassTiKV.NewStd(errno.ErrTiKVServerBusy) + // ErrTiFlashServerBusy is the error that tiflash server is busy. + ErrTiFlashServerBusy = dbterror.ClassTiKV.NewStd(errno.ErrTiFlashServerBusy) + // ErrPDServerTimeout is the error when pd server is timeout. + ErrPDServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrPDServerTimeout) + // ErrRegionUnavailable is the error when region is not available. + ErrRegionUnavailable = dbterror.ClassTiKV.NewStd(errno.ErrRegionUnavailable) + // ErrResourceGroupNotExists is the error when resource group does not exist. + ErrResourceGroupNotExists = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupNotExists) + // ErrResourceGroupConfigUnavailable is the error when resource group configuration is unavailable. + ErrResourceGroupConfigUnavailable = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupConfigUnavailable) + // ErrResourceGroupThrottled is the error when resource group is exceeded quota limitation + ErrResourceGroupThrottled = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupThrottled) + // ErrUnknown is the unknow error. + ErrUnknown = dbterror.ClassTiKV.NewStd(errno.ErrUnknown) +) + +// Registers error returned from TiKV. +var ( + _ = dbterror.ClassTiKV.NewStd(errno.ErrDataOutOfRange) + _ = dbterror.ClassTiKV.NewStd(errno.ErrTruncatedWrongValue) + _ = dbterror.ClassTiKV.NewStd(errno.ErrDivisionByZero) +) + +// ToTiDBErr checks and converts a tikv error to a tidb error. +func ToTiDBErr(err error) error { + if err == nil { + return nil + } + if tikverr.IsErrNotFound(err) { + return kv.ErrNotExist + } + + var writeConflictInLatch *tikverr.ErrWriteConflictInLatch + if stderrs.As(err, &writeConflictInLatch) { + return kv.ErrWriteConflictInTiDB.FastGenByArgs(writeConflictInLatch.StartTS) + } + + var txnTooLarge *tikverr.ErrTxnTooLarge + if stderrs.As(err, &txnTooLarge) { + return kv.ErrTxnTooLarge.GenWithStackByArgs(txnTooLarge.Size) + } + + if stderrs.Is(err, tikverr.ErrCannotSetNilValue) { + return kv.ErrCannotSetNilValue + } + + var entryTooLarge *tikverr.ErrEntryTooLarge + if stderrs.As(err, &entryTooLarge) { + return kv.ErrEntryTooLarge.GenWithStackByArgs(entryTooLarge.Limit, entryTooLarge.Size) + } + + if stderrs.Is(err, tikverr.ErrInvalidTxn) { + return kv.ErrInvalidTxn + } + + if stderrs.Is(err, tikverr.ErrTiKVServerTimeout) { + return ErrTiKVServerTimeout + } + + var pdServerTimeout *tikverr.ErrPDServerTimeout + if stderrs.As(err, &pdServerTimeout) { + return ErrPDServerTimeout.GenWithStackByArgs(pdServerTimeout.Error()) + } + + if stderrs.Is(err, tikverr.ErrTiFlashServerTimeout) { + return ErrTiFlashServerTimeout + } + + if stderrs.Is(err, tikverr.ErrQueryInterrupted) { + return ErrQueryInterrupted + } + + if stderrs.Is(err, tikverr.ErrTiKVServerBusy) { + return ErrTiKVServerBusy + } + + if stderrs.Is(err, tikverr.ErrTiFlashServerBusy) { + return ErrTiFlashServerBusy + } + + var gcTooEarly *tikverr.ErrGCTooEarly + if stderrs.As(err, &gcTooEarly) { + return ErrGCTooEarly.GenWithStackByArgs(gcTooEarly.TxnStartTS, gcTooEarly.GCSafePoint) + } + + if stderrs.Is(err, tikverr.ErrTiKVStaleCommand) { + return ErrTiKVStaleCommand + } + + if stderrs.Is(err, tikverr.ErrTiKVMaxTimestampNotSynced) { + return ErrTiKVMaxTimestampNotSynced + } + + if stderrs.Is(err, tikverr.ErrLockAcquireFailAndNoWaitSet) { + return ErrLockAcquireFailAndNoWaitSet + } + + if stderrs.Is(err, tikverr.ErrResolveLockTimeout) { + return ErrResolveLockTimeout + } + + if stderrs.Is(err, tikverr.ErrLockWaitTimeout) { + return ErrLockWaitTimeout + } + + if stderrs.Is(err, tikverr.ErrRegionUnavailable) { + return ErrRegionUnavailable + } + + var tokenLimit *tikverr.ErrTokenLimit + if stderrs.As(err, &tokenLimit) { + return ErrTokenLimit.GenWithStackByArgs(tokenLimit.StoreID) + } + + if stderrs.Is(err, tikverr.ErrUnknown) { + return ErrUnknown + } + + if tikverr.IsErrorUndetermined(err) { + return terror.ErrResultUndetermined + } + + var errGetResourceGroup *pderr.ErrClientGetResourceGroup + if stderrs.As(err, &errGetResourceGroup) { + return ErrResourceGroupNotExists.FastGenByArgs(errGetResourceGroup.ResourceGroupName) + } + + if stderrs.Is(err, pderr.ErrClientResourceGroupConfigUnavailable) { + return ErrResourceGroupConfigUnavailable + } + + if stderrs.Is(err, pderr.ErrClientResourceGroupThrottled) { + return ErrResourceGroupThrottled + } + + return errors.Trace(err) +} diff --git a/pkg/store/driver/error/error_test.go b/pkg/store/driver/error/error_test.go new file mode 100644 index 0000000000000..32be5d1708973 --- /dev/null +++ b/pkg/store/driver/error/error_test.go @@ -0,0 +1,53 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package error //nolint: predeclared + +import ( + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/assert" + tikverr "github.com/tikv/client-go/v2/error" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} + +func TestConvertError(t *testing.T) { + wrapFuncs := []func(error) error{ + func(e error) error { return e }, + errors.Trace, + errors.WithStack, + func(e error) error { return errors.Wrap(e, "dummy") }, + } + + // All derived versions converts to `terror.ErrResultUndetermined`. + e := tikverr.ErrResultUndetermined + for _, f := range wrapFuncs { + tidbErr := ToTiDBErr(f(e)) + assert.True(t, errors.ErrorEqual(tidbErr, terror.ErrResultUndetermined)) + } +} diff --git a/pkg/store/driver/main_test.go b/pkg/store/driver/main_test.go new file mode 100644 index 0000000000000..7f1e5224acaa3 --- /dev/null +++ b/pkg/store/driver/main_test.go @@ -0,0 +1,162 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package driver + +import ( + "context" + "flag" + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/copr" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +var ( + pdAddrs = flag.String("pd-addrs", "127.0.0.1:2379", "pd addrs") + withTiKV = flag.Bool("with-tikv", false, "run tests with TiKV cluster started. (not use the mock server)") +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} + +func createTestStore(t *testing.T) (kv.Storage, *domain.Domain) { + if *withTiKV { + return createTiKVStore(t) + } + return createUnistore(t) +} + +func createTiKVStore(t *testing.T) (kv.Storage, *domain.Domain) { + var d TiKVDriver + store, err := d.Open(fmt.Sprintf("tikv://%s", *pdAddrs)) + require.NoError(t, err) + + // clear storage + txn, err := store.Begin() + require.NoError(t, err) + iter, err := txn.Iter(nil, nil) + require.NoError(t, err) + for iter.Valid() { + require.NoError(t, txn.Delete(iter.Key())) + require.NoError(t, iter.Next()) + } + require.NoError(t, txn.Commit(context.Background())) + + session.ResetStoreForWithTiKVTest(store) + + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + + t.Cleanup(func() { + dom.Close() + require.NoError(t, store.Close()) + }) + + return store, dom +} + +func createUnistore(t *testing.T) (kv.Storage, *domain.Domain) { + client, pdClient, cluster, err := unistore.New("") + require.NoError(t, err) + + unistore.BootstrapWithSingleStore(cluster) + kvStore, err := tikv.NewTestTiKVStore(client, pdClient, nil, nil, 0) + require.NoError(t, err) + + coprStore, err := copr.NewStore(kvStore, nil) + require.NoError(t, err) + + store := &tikvStore{KVStore: kvStore, coprStore: coprStore} + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + + t.Cleanup(func() { + dom.Close() + require.NoError(t, store.Close()) + }) + + return store, dom +} + +func prepareSnapshot(t *testing.T, store kv.Storage, data [][]interface{}) kv.Snapshot { + txn, err := store.Begin() + require.NoError(t, err) + defer func() { + if txn.Valid() { + require.NoError(t, txn.Rollback()) + } + }() + + for _, d := range data { + err = txn.Set(makeBytes(d[0]), makeBytes(d[1])) + require.NoError(t, err) + } + + err = txn.Commit(context.Background()) + require.NoError(t, err) + + return store.GetSnapshot(kv.MaxVersion) +} + +func makeBytes(s interface{}) []byte { + if s == nil { + return nil + } + + switch key := s.(type) { + case string: + return []byte(key) + default: + return key.([]byte) + } +} + +func clearStoreData(t *testing.T, store kv.Storage) { + txn, err := store.Begin() + require.NoError(t, err) + defer func() { + if txn.Valid() { + require.NoError(t, txn.Rollback()) + } + }() + + iter, err := txn.Iter(nil, nil) + require.NoError(t, err) + defer iter.Close() + + for iter.Valid() { + require.NoError(t, txn.Delete(iter.Key())) + require.NoError(t, iter.Next()) + } + + require.NoError(t, txn.Commit(context.Background())) +} diff --git a/pkg/store/driver/options/BUILD.bazel b/pkg/store/driver/options/BUILD.bazel new file mode 100644 index 0000000000000..9b65831c3f0b3 --- /dev/null +++ b/pkg/store/driver/options/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "options", + srcs = ["options.go"], + importpath = "github.com/pingcap/tidb/pkg/store/driver/options", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "@com_github_tikv_client_go_v2//kv", + ], +) diff --git a/pkg/store/driver/options/options.go b/pkg/store/driver/options/options.go new file mode 100644 index 0000000000000..dbec555ea0ca3 --- /dev/null +++ b/pkg/store/driver/options/options.go @@ -0,0 +1,41 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package options + +import ( + "github.com/pingcap/tidb/pkg/kv" + storekv "github.com/tikv/client-go/v2/kv" +) + +// GetTiKVReplicaReadType maps kv.ReplicaReadType to tikv/kv.ReplicaReadType. +func GetTiKVReplicaReadType(t kv.ReplicaReadType) storekv.ReplicaReadType { + switch t { + case kv.ReplicaReadLeader: + return storekv.ReplicaReadLeader + case kv.ReplicaReadFollower: + return storekv.ReplicaReadFollower + case kv.ReplicaReadMixed: + return storekv.ReplicaReadMixed + case kv.ReplicaReadClosest: + return storekv.ReplicaReadMixed + case kv.ReplicaReadClosestAdaptive: + return storekv.ReplicaReadMixed + case kv.ReplicaReadLearner: + return storekv.ReplicaReadLearner + case kv.ReplicaReadPreferLeader: + return storekv.ReplicaReadPreferLeader + } + return 0 +} diff --git a/store/driver/snap_interceptor_test.go b/pkg/store/driver/snap_interceptor_test.go similarity index 98% rename from store/driver/snap_interceptor_test.go rename to pkg/store/driver/snap_interceptor_test.go index 96ad23aa2b4af..ae5e1af657ec6 100644 --- a/store/driver/snap_interceptor_test.go +++ b/pkg/store/driver/snap_interceptor_test.go @@ -19,8 +19,8 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore" "github.com/stretchr/testify/require" ) diff --git a/store/driver/sql_fail_test.go b/pkg/store/driver/sql_fail_test.go similarity index 95% rename from store/driver/sql_fail_test.go rename to pkg/store/driver/sql_fail_test.go index fe733968a7f96..804100cf0a26e 100644 --- a/store/driver/sql_fail_test.go +++ b/pkg/store/driver/sql_fail_test.go @@ -20,8 +20,8 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/store/driver/tikv_driver.go b/pkg/store/driver/tikv_driver.go similarity index 96% rename from store/driver/tikv_driver.go rename to pkg/store/driver/tikv_driver.go index 8155b38395f3b..f73d910195137 100644 --- a/store/driver/tikv_driver.go +++ b/pkg/store/driver/tikv_driver.go @@ -27,15 +27,15 @@ import ( "github.com/pingcap/errors" deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - derr "github.com/pingcap/tidb/store/driver/error" - txn_driver "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/copr" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + txn_driver "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tracing" "github.com/tikv/client-go/v2/config" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/pkg/store/driver/txn/BUILD.bazel b/pkg/store/driver/txn/BUILD.bazel new file mode 100644 index 0000000000000..95dc8746027ac --- /dev/null +++ b/pkg/store/driver/txn/BUILD.bazel @@ -0,0 +1,70 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "txn", + srcs = [ + "batch_getter.go", + "binlog.go", + "error.go", + "scanner.go", + "snapshot.go", + "txn_driver.go", + "union_iter.go", + "unionstore_driver.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/driver/txn", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/binloginfo", + "//pkg/store/driver/error", + "//pkg/store/driver/options", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@com_github_tikv_client_go_v2//txnkv", + "@com_github_tikv_client_go_v2//txnkv/transaction", + "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", + "@com_github_tikv_client_go_v2//txnkv/txnutil", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "txn_test", + timeout = "short", + srcs = [ + "batch_getter_test.go", + "driver_test.go", + "main_test.go", + "union_iter_test.go", + ], + embed = [":txn"], + flaky = True, + shard_count = 5, + deps = [ + "//pkg/kv", + "//pkg/testkit/testsetup", + "//pkg/util/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/store/driver/txn/batch_getter.go b/pkg/store/driver/txn/batch_getter.go similarity index 99% rename from store/driver/txn/batch_getter.go rename to pkg/store/driver/txn/batch_getter.go index 19f588373d1cc..0777982808f99 100644 --- a/store/driver/txn/batch_getter.go +++ b/pkg/store/driver/txn/batch_getter.go @@ -18,7 +18,7 @@ import ( "context" "unsafe" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" tikverr "github.com/tikv/client-go/v2/error" "github.com/tikv/client-go/v2/txnkv/transaction" ) diff --git a/store/driver/txn/batch_getter_test.go b/pkg/store/driver/txn/batch_getter_test.go similarity index 98% rename from store/driver/txn/batch_getter_test.go rename to pkg/store/driver/txn/batch_getter_test.go index f291390e2f92c..1b07532be3062 100644 --- a/store/driver/txn/batch_getter_test.go +++ b/pkg/store/driver/txn/batch_getter_test.go @@ -17,7 +17,7 @@ import ( "context" "testing" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" ) diff --git a/store/driver/txn/binlog.go b/pkg/store/driver/txn/binlog.go similarity index 95% rename from store/driver/txn/binlog.go rename to pkg/store/driver/txn/binlog.go index 86989c315d448..b17bcbaed25fa 100644 --- a/store/driver/txn/binlog.go +++ b/pkg/store/driver/txn/binlog.go @@ -19,8 +19,8 @@ import ( "sync" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/pingcap/tipb/go-binlog" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" diff --git a/store/driver/txn/driver_test.go b/pkg/store/driver/txn/driver_test.go similarity index 99% rename from store/driver/txn/driver_test.go rename to pkg/store/driver/txn/driver_test.go index 26b2db3a1f699..9933ad9ffb6e2 100644 --- a/store/driver/txn/driver_test.go +++ b/pkg/store/driver/txn/driver_test.go @@ -18,7 +18,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/require" ) diff --git a/pkg/store/driver/txn/error.go b/pkg/store/driver/txn/error.go new file mode 100644 index 0000000000000..dddbb4dd0ff3d --- /dev/null +++ b/pkg/store/driver/txn/error.go @@ -0,0 +1,258 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package txn + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + tikverr "github.com/tikv/client-go/v2/error" + "go.uber.org/zap" +) + +func genKeyExistsError(name string, value string, err error) error { + if err != nil { + logutil.BgLogger().Info("extractKeyExistsErr meets error", zap.Error(err)) + } + return kv.ErrKeyExists.FastGenByArgs(value, name) +} + +// ExtractKeyExistsErrFromHandle returns a ErrKeyExists error from a handle key. +func ExtractKeyExistsErrFromHandle(key kv.Key, value []byte, tblInfo *model.TableInfo) error { + name := tblInfo.Name.String() + ".PRIMARY" + _, handle, err := tablecodec.DecodeRecordKey(key) + if err != nil { + return genKeyExistsError(name, key.String(), err) + } + + if handle.IsInt() { + if pkInfo := tblInfo.GetPkColInfo(); pkInfo != nil { + if mysql.HasUnsignedFlag(pkInfo.GetFlag()) { + handleStr := strconv.FormatUint(uint64(handle.IntValue()), 10) + return genKeyExistsError(name, handleStr, nil) + } + } + return genKeyExistsError(name, handle.String(), nil) + } + + if len(value) == 0 { + return genKeyExistsError(name, handle.String(), errors.New("missing value")) + } + + idxInfo := tables.FindPrimaryIndex(tblInfo) + if idxInfo == nil { + return genKeyExistsError(name, handle.String(), errors.New("cannot find index info")) + } + + cols := make(map[int64]*types.FieldType, len(tblInfo.Columns)) + for _, col := range tblInfo.Columns { + cols[col.ID] = &(col.FieldType) + } + handleColIDs := make([]int64, 0, len(idxInfo.Columns)) + for _, col := range idxInfo.Columns { + handleColIDs = append(handleColIDs, tblInfo.Columns[col.Offset].ID) + } + + row, err := tablecodec.DecodeRowToDatumMap(value, cols, time.Local) + if err != nil { + return genKeyExistsError(name, handle.String(), err) + } + + data, err := tablecodec.DecodeHandleToDatumMap(handle, handleColIDs, cols, time.Local, row) + if err != nil { + return genKeyExistsError(name, handle.String(), err) + } + + valueStr := make([]string, 0, len(data)) + for _, col := range idxInfo.Columns { + d := data[tblInfo.Columns[col.Offset].ID] + str, err := d.ToString() + if err != nil { + return genKeyExistsError(name, key.String(), err) + } + if col.Length > 0 && len(str) > col.Length { + str = str[:col.Length] + } + if types.IsBinaryStr(&tblInfo.Columns[col.Offset].FieldType) || types.IsTypeBit(&tblInfo.Columns[col.Offset].FieldType) { + str = util.FmtNonASCIIPrintableCharToHex(str) + } + valueStr = append(valueStr, str) + } + return genKeyExistsError(name, strings.Join(valueStr, "-"), nil) +} + +// ExtractKeyExistsErrFromIndex returns a ErrKeyExists error from a index key. +func ExtractKeyExistsErrFromIndex(key kv.Key, value []byte, tblInfo *model.TableInfo, indexID int64) error { + var idxInfo *model.IndexInfo + for _, index := range tblInfo.Indices { + if index.ID == indexID { + idxInfo = index + } + } + if idxInfo == nil { + return genKeyExistsError("UNKNOWN", key.String(), errors.New("cannot find index info")) + } + name := tblInfo.Name.String() + "." + idxInfo.Name.String() + + if len(value) == 0 { + return genKeyExistsError(name, key.String(), errors.New("missing value")) + } + + colInfo := tables.BuildRowcodecColInfoForIndexColumns(idxInfo, tblInfo) + values, err := tablecodec.DecodeIndexKV(key, value, len(idxInfo.Columns), tablecodec.HandleNotNeeded, colInfo) + if err != nil { + return genKeyExistsError(name, key.String(), err) + } + valueStr := make([]string, 0, len(values)) + for i, val := range values { + d, err := tablecodec.DecodeColumnValue(val, colInfo[i].Ft, time.Local) + if err != nil { + return genKeyExistsError(name, key.String(), err) + } + str, err := d.ToString() + if err != nil { + return genKeyExistsError(name, key.String(), err) + } + if types.IsBinaryStr(colInfo[i].Ft) || types.IsTypeBit(colInfo[i].Ft) { + str = util.FmtNonASCIIPrintableCharToHex(str) + } + valueStr = append(valueStr, str) + } + return genKeyExistsError(name, strings.Join(valueStr, "-"), nil) +} + +func extractKeyErr(err error) error { + if err == nil { + return nil + } + if e, ok := errors.Cause(err).(*tikverr.ErrWriteConflict); ok { + return newWriteConflictError(e.WriteConflict) + } + if e, ok := errors.Cause(err).(*tikverr.ErrRetryable); ok { + notFoundDetail := prettyLockNotFoundKey(e.Retryable) + return kv.ErrTxnRetryable.GenWithStackByArgs(e.Retryable + " " + notFoundDetail) + } + return derr.ToTiDBErr(err) +} + +func newWriteConflictError(conflict *kvrpcpb.WriteConflict) error { + if conflict == nil { + return kv.ErrWriteConflict + } + var bufConflictKeyTableID bytes.Buffer // table id part of conflict key, which is used to be parsed by upper level to provide more information about the table + var bufConflictKeyRest bytes.Buffer // the rest part of conflict key + var bufPrimaryKeyTableID bytes.Buffer // table id part of primary key + var bufPrimaryKeyRest bytes.Buffer // the rest part of primary key + prettyWriteKey(&bufConflictKeyTableID, &bufConflictKeyRest, conflict.Key) + bufConflictKeyRest.WriteString(", originalKey=" + hex.EncodeToString(conflict.Key)) + bufConflictKeyRest.WriteString(", primary=") + prettyWriteKey(&bufPrimaryKeyTableID, &bufPrimaryKeyRest, conflict.Primary) + bufPrimaryKeyRest.WriteString(", originalPrimaryKey=" + hex.EncodeToString(conflict.Primary)) + return kv.ErrWriteConflict.FastGenByArgs(conflict.StartTs, conflict.ConflictTs, conflict.ConflictCommitTs, + bufConflictKeyTableID.String(), bufConflictKeyRest.String(), bufPrimaryKeyTableID.String(), + bufPrimaryKeyRest.String(), conflict.Reason.String(), + ) +} + +func prettyWriteKey(bufTableID, bufRest *bytes.Buffer, key []byte) { + tableID, indexID, indexValues, err := tablecodec.DecodeIndexKey(key) + if err == nil { + _, err1 := fmt.Fprintf(bufTableID, "{tableID=%d", tableID) + if err1 != nil { + logutil.BgLogger().Error("error", zap.Error(err1)) + } + _, err1 = fmt.Fprintf(bufRest, ", indexID=%d, indexValues={", indexID) + if err1 != nil { + logutil.BgLogger().Error("error", zap.Error(err1)) + } + for _, v := range indexValues { + _, err2 := fmt.Fprintf(bufRest, "%s, ", v) + if err2 != nil { + logutil.BgLogger().Error("error", zap.Error(err2)) + } + } + bufRest.WriteString("}}") + return + } + + tableID, handle, err := tablecodec.DecodeRecordKey(key) + if err == nil { + _, err3 := fmt.Fprintf(bufTableID, "{tableID=%d", tableID) + if err3 != nil { + logutil.BgLogger().Error("error", zap.Error(err3)) + } + _, err3 = fmt.Fprintf(bufRest, ", handle=%s}", handle.String()) + if err3 != nil { + logutil.BgLogger().Error("error", zap.Error(err3)) + } + return + } + + mKey, mField, err := tablecodec.DecodeMetaKey(key) + if err == nil { + _, err3 := fmt.Fprintf(bufRest, "{metaKey=true, key=%s, field=%s}", string(mKey), string(mField)) + if err3 != nil { + logutil.Logger(context.Background()).Error("error", zap.Error(err3)) + } + return + } + + _, err4 := fmt.Fprintf(bufRest, "%#v", key) + if err4 != nil { + logutil.BgLogger().Error("error", zap.Error(err4)) + } +} + +func prettyLockNotFoundKey(rawRetry string) string { + if !strings.Contains(rawRetry, "TxnLockNotFound") { + return "" + } + start := strings.Index(rawRetry, "[") + if start == -1 { + return "" + } + rawRetry = rawRetry[start:] + end := strings.Index(rawRetry, "]") + if end == -1 { + return "" + } + rawRetry = rawRetry[:end+1] + var key []byte + err := json.Unmarshal([]byte(rawRetry), &key) + if err != nil { + return "" + } + var buf1 bytes.Buffer + var buf2 bytes.Buffer + prettyWriteKey(&buf1, &buf2, key) + return buf1.String() + buf2.String() +} diff --git a/pkg/store/driver/txn/main_test.go b/pkg/store/driver/txn/main_test.go new file mode 100644 index 0000000000000..03165ac094bde --- /dev/null +++ b/pkg/store/driver/txn/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package txn + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/store/driver/txn/scanner.go b/pkg/store/driver/txn/scanner.go similarity index 96% rename from store/driver/txn/scanner.go rename to pkg/store/driver/txn/scanner.go index d39a8028622c7..7d498b72835de 100644 --- a/store/driver/txn/scanner.go +++ b/pkg/store/driver/txn/scanner.go @@ -15,7 +15,7 @@ package txn import ( - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/tikv/client-go/v2/txnkv/txnsnapshot" ) diff --git a/store/driver/txn/snapshot.go b/pkg/store/driver/txn/snapshot.go similarity index 97% rename from store/driver/txn/snapshot.go rename to pkg/store/driver/txn/snapshot.go index 7425edec1fa58..d83025fd8c99a 100644 --- a/store/driver/txn/snapshot.go +++ b/pkg/store/driver/txn/snapshot.go @@ -20,9 +20,9 @@ import ( "unsafe" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/kv" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/driver/options" + "github.com/pingcap/tidb/pkg/kv" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/driver/options" "github.com/tikv/client-go/v2/tikvrpc" "github.com/tikv/client-go/v2/tikvrpc/interceptor" "github.com/tikv/client-go/v2/txnkv" diff --git a/store/driver/txn/txn_driver.go b/pkg/store/driver/txn/txn_driver.go similarity index 97% rename from store/driver/txn/txn_driver.go rename to pkg/store/driver/txn/txn_driver.go index f36972197398e..4f1c9e075cf04 100644 --- a/store/driver/txn/txn_driver.go +++ b/pkg/store/driver/txn/txn_driver.go @@ -24,14 +24,14 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/binloginfo" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/driver/options" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/tracing" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/driver/options" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/tracing" tikverr "github.com/tikv/client-go/v2/error" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/tikv" diff --git a/store/driver/txn/union_iter.go b/pkg/store/driver/txn/union_iter.go similarity index 99% rename from store/driver/txn/union_iter.go rename to pkg/store/driver/txn/union_iter.go index e041379fc92c9..0e47776f2f79b 100644 --- a/store/driver/txn/union_iter.go +++ b/pkg/store/driver/txn/union_iter.go @@ -17,7 +17,7 @@ package txn import ( "bytes" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // UnionIter implements kv.Iterator diff --git a/store/driver/txn/union_iter_test.go b/pkg/store/driver/txn/union_iter_test.go similarity index 98% rename from store/driver/txn/union_iter_test.go rename to pkg/store/driver/txn/union_iter_test.go index abbe6031e6bdb..6594603ecbc19 100644 --- a/store/driver/txn/union_iter_test.go +++ b/pkg/store/driver/txn/union_iter_test.go @@ -18,8 +18,8 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/assert" ) diff --git a/store/driver/txn/unionstore_driver.go b/pkg/store/driver/txn/unionstore_driver.go similarity index 98% rename from store/driver/txn/unionstore_driver.go rename to pkg/store/driver/txn/unionstore_driver.go index 1ff192f62a04c..1718707fa4b72 100644 --- a/store/driver/txn/unionstore_driver.go +++ b/pkg/store/driver/txn/unionstore_driver.go @@ -17,8 +17,8 @@ package txn import ( "context" - "github.com/pingcap/tidb/kv" - derr "github.com/pingcap/tidb/store/driver/error" + "github.com/pingcap/tidb/pkg/kv" + derr "github.com/pingcap/tidb/pkg/store/driver/error" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/tikv" ) diff --git a/pkg/store/driver/txn_test.go b/pkg/store/driver/txn_test.go new file mode 100644 index 0000000000000..020679b27ae05 --- /dev/null +++ b/pkg/store/driver/txn_test.go @@ -0,0 +1,222 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package driver + +import ( + "context" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/stretchr/testify/require" +) + +type mockErrInterceptor struct { + err error +} + +func (m *mockErrInterceptor) OnGet(_ context.Context, _ kv.Snapshot, _ kv.Key) ([]byte, error) { + return nil, m.err +} + +func (m *mockErrInterceptor) OnBatchGet(_ context.Context, _ kv.Snapshot, _ []kv.Key) (map[string][]byte, error) { + return nil, m.err +} + +func (m *mockErrInterceptor) OnIter(_ kv.Snapshot, _ kv.Key, _ kv.Key) (kv.Iterator, error) { + return nil, m.err +} + +func (m *mockErrInterceptor) OnIterReverse(_ kv.Snapshot, _ kv.Key, _ kv.Key) (kv.Iterator, error) { + return nil, m.err +} + +func TestTxnGet(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + clearStoreData(t, store) + + prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}}) + txn, err := store.Begin() + require.NoError(t, err) + require.NotNil(t, txn) + + // should return snapshot value if no dirty data + v, err := txn.Get(context.Background(), kv.Key("k1")) + require.NoError(t, err) + require.Equal(t, []byte("v1"), v) + + // insert but not commit + err = txn.Set(kv.Key("k1"), kv.Key("v1+")) + require.NoError(t, err) + + // should return dirty data if dirty data exists + v, err = txn.Get(context.Background(), kv.Key("k1")) + require.NoError(t, err) + require.Equal(t, []byte("v1+"), v) + + err = txn.Set(kv.Key("k2"), []byte("v2+")) + require.NoError(t, err) + + // should return dirty data if dirty data exists + v, err = txn.Get(context.Background(), kv.Key("k2")) + require.NoError(t, err) + require.Equal(t, []byte("v2+"), v) + + // delete but not commit + err = txn.Delete(kv.Key("k1")) + require.NoError(t, err) + + // should return kv.ErrNotExist if deleted + v, err = txn.Get(context.Background(), kv.Key("k1")) + require.Nil(t, v) + require.True(t, kv.ErrNotExist.Equal(err)) + + // should return kv.ErrNotExist if not exist + v, err = txn.Get(context.Background(), kv.Key("kn")) + require.Nil(t, v) + require.True(t, kv.ErrNotExist.Equal(err)) + + // make snapshot returns error + errInterceptor := &mockErrInterceptor{err: errors.New("error")} + txn.SetOption(kv.SnapInterceptor, errInterceptor) + + // should return kv.ErrNotExist because k1 is deleted in memBuff + v, err = txn.Get(context.Background(), kv.Key("k1")) + require.Nil(t, v) + require.True(t, kv.ErrNotExist.Equal(err)) + + // should return dirty data because k2 is in memBuff + v, err = txn.Get(context.Background(), kv.Key("k2")) + require.NoError(t, err) + require.Equal(t, []byte("v2+"), v) + + // should return error because kn is read from snapshot + v, err = txn.Get(context.Background(), kv.Key("kn")) + require.Nil(t, v) + require.Equal(t, errInterceptor.err, err) +} + +func TestTxnBatchGet(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + clearStoreData(t, store) + + prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}}) + txn, err := store.Begin() + require.NoError(t, err) + + result, err := txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k2"), kv.Key("k3"), kv.Key("kn")}) + require.NoError(t, err) + require.Equal(t, 3, len(result)) + require.Equal(t, []byte("v1"), result["k1"]) + require.Equal(t, []byte("v2"), result["k2"]) + require.Equal(t, []byte("v3"), result["k3"]) + + // make some dirty data + err = txn.Set(kv.Key("k1"), []byte("v1+")) + require.NoError(t, err) + err = txn.Set(kv.Key("k4"), []byte("v4+")) + require.NoError(t, err) + err = txn.Delete(kv.Key("k2")) + require.NoError(t, err) + + result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k2"), kv.Key("k3"), kv.Key("k4"), kv.Key("kn")}) + require.NoError(t, err) + require.Equal(t, 3, len(result)) + require.Equal(t, []byte("v1+"), result["k1"]) + require.Equal(t, []byte("v3"), result["k3"]) + require.Equal(t, []byte("v4+"), result["k4"]) + + // return data if not read from snapshot + result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k4")}) + require.NoError(t, err) + require.Equal(t, 2, len(result)) + require.Equal(t, []byte("v1+"), result["k1"]) + require.Equal(t, []byte("v4+"), result["k4"]) + + // make snapshot returns error + errInterceptor := &mockErrInterceptor{err: errors.New("error")} + txn.SetOption(kv.SnapInterceptor, errInterceptor) + + // fails if read from snapshot + result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k3")}) + require.Nil(t, result) + require.Equal(t, errInterceptor.err, err) + result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k3"), kv.Key("k4")}) + require.Nil(t, result) + require.Equal(t, errInterceptor.err, err) + result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k4"), kv.Key("kn")}) + require.Nil(t, result) + require.Equal(t, errInterceptor.err, err) +} + +func TestTxnScan(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + clearStoreData(t, store) + + prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}, {"k3", "v3"}, {"k5", "v5"}, {"k7", "v7"}, {"k9", "v9"}}) + txn, err := store.Begin() + require.NoError(t, err) + + iter, err := txn.Iter(kv.Key("k3"), kv.Key("k9")) + require.NoError(t, err) + checkIter(t, iter, [][]interface{}{{"k3", "v3"}, {"k5", "v5"}, {"k7", "v7"}}) + + iter, err = txn.IterReverse(kv.Key("k9"), nil) + require.NoError(t, err) + checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k5", "v5"}, {"k3", "v3"}, {"k1", "v1"}}) + + iter, err = txn.IterReverse(kv.Key("k9"), kv.Key("k3")) + require.NoError(t, err) + checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k5", "v5"}, {"k3", "v3"}}) + + // make some dirty data + err = txn.Set(kv.Key("k1"), []byte("v1+")) + require.NoError(t, err) + err = txn.Set(kv.Key("k3"), []byte("v3+")) + require.NoError(t, err) + err = txn.Set(kv.Key("k31"), []byte("v31+")) + require.NoError(t, err) + err = txn.Delete(kv.Key("k5")) + require.NoError(t, err) + + iter, err = txn.Iter(kv.Key("k3"), kv.Key("k9")) + require.NoError(t, err) + checkIter(t, iter, [][]interface{}{{"k3", "v3+"}, {"k31", "v31+"}, {"k7", "v7"}}) + + iter, err = txn.IterReverse(kv.Key("k9"), nil) + require.NoError(t, err) + checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k31", "v31+"}, {"k3", "v3+"}, {"k1", "v1+"}}) + + // make snapshot returns error + errInterceptor := &mockErrInterceptor{err: errors.New("error")} + txn.SetOption(kv.SnapInterceptor, errInterceptor) + + iter, err = txn.Iter(kv.Key("k1"), kv.Key("k2")) + require.Equal(t, errInterceptor.err, err) + require.Nil(t, iter) +} diff --git a/pkg/store/gcworker/BUILD.bazel b/pkg/store/gcworker/BUILD.bazel new file mode 100644 index 0000000000000..5745182657b9f --- /dev/null +++ b/pkg/store/gcworker/BUILD.bazel @@ -0,0 +1,80 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "gcworker", + srcs = ["gc_worker.go"], + importpath = "github.com/pingcap/tidb/pkg/store/gcworker", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/ddl/label", + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/privilege", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/tablecodec", + "//pkg/util/codec", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_tikv_client_go_v2//error", + "@com_github_tikv_client_go_v2//kv", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//txnkv/rangetask", + "@com_github_tikv_client_go_v2//txnkv/txnlock", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "gcworker_test", + timeout = "short", + srcs = [ + "gc_worker_test.go", + "main_test.go", + ], + embed = [":gcworker"], + flaky = True, + race = "on", + shard_count = 29, + deps = [ + "//pkg/ddl/placement", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//oracle/oracles", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//txnkv/txnlock", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/store/gcworker/gc_worker.go b/pkg/store/gcworker/gc_worker.go similarity index 99% rename from store/gcworker/gc_worker.go rename to pkg/store/gcworker/gc_worker.go index df5dc5a21a3be..caedf9e9ec33e 100644 --- a/store/gcworker/gc_worker.go +++ b/pkg/store/gcworker/gc_worker.go @@ -34,22 +34,22 @@ import ( "github.com/pingcap/kvproto/pkg/errorpb" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/label" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/label" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" tikverr "github.com/tikv/client-go/v2/error" tikvstore "github.com/tikv/client-go/v2/kv" "github.com/tikv/client-go/v2/oracle" diff --git a/store/gcworker/gc_worker_test.go b/pkg/store/gcworker/gc_worker_test.go similarity index 96% rename from store/gcworker/gc_worker_test.go rename to pkg/store/gcworker/gc_worker_test.go index e51628520c8a2..944417dcfe006 100644 --- a/store/gcworker/gc_worker_test.go +++ b/pkg/store/gcworker/gc_worker_test.go @@ -31,14 +31,14 @@ import ( "github.com/pingcap/kvproto/pkg/errorpb" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/oracle/oracles" @@ -683,9 +683,9 @@ func TestDeleteRangesFailure(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { failType := test.failType - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/mockHistoryJobForGC", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJobForGC", "return(1)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/mockHistoryJobForGC")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJobForGC")) }() // Put some delete range tasks. @@ -845,7 +845,7 @@ Loop: } func TestUnsafeDestroyRangeForRaftkv2(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/util/IsRaftKv2", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/util/IsRaftKv2", "return(true)")) s := createGCWorkerSuite(t) // Put some delete range tasks. @@ -1141,9 +1141,9 @@ func TestResolveLockRangeMeetRegionEnlargeCausedByRegionMerge(t *testing.T) { // TODO: Update the test code. // This test rely on the obsolete mock tikv, but mock tikv does not implement paging. // So use this failpoint to force non-paging protocol. - failpoint.Enable("github.com/pingcap/tidb/store/copr/DisablePaging", `return`) + failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging", `return`) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/DisablePaging")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging")) }() s := createGCWorkerSuiteWithStoreType(t, mockstore.MockTiKV) @@ -1720,11 +1720,11 @@ func TestResolveLocksPhysical(t *testing.T) { locks := []*kvrpcpb.LockInfo{{Key: []byte{0}}} return &tikvrpc.Response{Resp: &kvrpcpb.PhysicalScanLockResponse{Locks: locks, Error: ""}}, nil } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/resolveLocksAcrossRegionsErr", "return(100)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/resolveLocksAcrossRegionsErr", "return(100)")) physicalUsed, err = s.gcWorker.resolveLocks(ctx, safePoint, 3, true) require.False(t, physicalUsed) require.NoError(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/resolveLocksAcrossRegionsErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/resolveLocksAcrossRegionsErr")) // Shouldn't fall back when fails to scan locks less than 3 times. reset() @@ -1767,7 +1767,7 @@ func TestResolveLocksPhysical(t *testing.T) { reset() var wg sync.WaitGroup wg.Add(1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers", "pause")) go func() { defer wg.Done() physicalUsed, err := s.gcWorker.resolveLocks(ctx, safePoint, 3, true) @@ -1786,13 +1786,13 @@ func TestResolveLocksPhysical(t *testing.T) { } return alwaysSucceedHandler(addr, req) } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers")) wg.Wait() // Shouldn't fall back when a store is removed. reset() wg.Add(1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers", "pause")) go func() { defer wg.Done() physicalUsed, err := s.gcWorker.resolveLocks(ctx, safePoint, 3, true) @@ -1802,13 +1802,13 @@ func TestResolveLocksPhysical(t *testing.T) { // Sleep to let the goroutine pause. time.Sleep(500 * time.Millisecond) s.cluster.RemoveStore(100) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers")) wg.Wait() // Should fall back when a cleaned store becomes dirty. reset() wg.Add(1) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers", "pause")) go func() { defer wg.Done() physicalUsed, err := s.gcWorker.resolveLocks(ctx, safePoint, 3, true) @@ -1839,7 +1839,7 @@ func TestResolveLocksPhysical(t *testing.T) { return alwaysSucceedHandler(addr, req) } } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/beforeCheckLockObservers")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/beforeCheckLockObservers")) wg.Wait() // Shouldn't fall back when fails to remove lock observers. @@ -1876,9 +1876,9 @@ func TestPhysicalScanLockDeadlock(t *testing.T) { // Sleep 1000ms to let the main goroutine block on sending tasks. // Inject error to the goroutine resolving locks so that the main goroutine will block forever if it doesn't handle channels properly. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/resolveLocksAcrossRegionsErr", "return(1000)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/resolveLocksAcrossRegionsErr", "return(1000)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/resolveLocksAcrossRegionsErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/resolveLocksAcrossRegionsErr")) }() done := make(chan interface{}) @@ -1899,19 +1899,19 @@ func TestPhysicalScanLockDeadlock(t *testing.T) { func TestGCPlacementRules(t *testing.T) { s := createGCWorkerSuite(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/mockHistoryJobForGC", "return(10)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJobForGC", "return(10)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/mockHistoryJobForGC")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJobForGC")) }() gcPlacementRuleCache := make(map[int64]interface{}) deletePlacementRuleCounter := 0 - require.NoError(t, failpoint.EnableWith("github.com/pingcap/tidb/store/gcworker/gcDeletePlacementRuleCounter", "return", func() error { + require.NoError(t, failpoint.EnableWith("github.com/pingcap/tidb/pkg/store/gcworker/gcDeletePlacementRuleCounter", "return", func() error { deletePlacementRuleCounter++ return nil })) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/gcDeletePlacementRuleCounter")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/gcDeletePlacementRuleCounter")) }() bundleID := "TiDB_DDL_10" @@ -1952,9 +1952,9 @@ func TestGCPlacementRules(t *testing.T) { func TestGCLabelRules(t *testing.T) { s := createGCWorkerSuite(t) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/gcworker/mockHistoryJob", "return(\"schema/d1/t1\")")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJob", "return(\"schema/d1/t1\")")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/gcworker/mockHistoryJob")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/gcworker/mockHistoryJob")) }() dr := util.DelRangeTask{JobID: 1, ElementID: 1} diff --git a/pkg/store/gcworker/main_test.go b/pkg/store/gcworker/main_test.go new file mode 100644 index 0000000000000..56fc39a194aac --- /dev/null +++ b/pkg/store/gcworker/main_test.go @@ -0,0 +1,44 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package gcworker + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + tikv.EnableFailpoints() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + } + callback := func(i int) int { + // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/store/helper/BUILD.bazel b/pkg/store/helper/BUILD.bazel new file mode 100644 index 0000000000000..2758031eda44d --- /dev/null +++ b/pkg/store/helper/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "helper", + srcs = ["helper.go"], + importpath = "github.com/pingcap/tidb/pkg/store/helper", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/placement", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/store/driver/error", + "//pkg/tablecodec", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/logutil", + "//pkg/util/pdapi", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//txnkv/txnlock", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "helper_test", + timeout = "short", + srcs = [ + "helper_test.go", + "main_test.go", + ], + embed = [":helper"], + flaky = True, + shard_count = 6, + deps = [ + "//pkg/parser/model", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/util/pdapi", + "@com_github_gorilla_mux//:mux", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/store/helper/helper.go b/pkg/store/helper/helper.go new file mode 100644 index 0000000000000..370262edb0a6b --- /dev/null +++ b/pkg/store/helper/helper.go @@ -0,0 +1,1221 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package helper + +import ( + "bufio" + "bytes" + "cmp" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math" + "net/http" + "net/url" + "slices" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/ddl/placement" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + derr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + "github.com/tikv/client-go/v2/txnkv/txnlock" + "go.uber.org/zap" +) + +// Storage represents a storage that connects TiKV. +// Methods copied from kv.Storage and tikv.Storage due to limitation of go1.13. +type Storage interface { + Begin(opts ...tikv.TxnOption) (kv.Transaction, error) + GetSnapshot(ver kv.Version) kv.Snapshot + GetClient() kv.Client + GetMPPClient() kv.MPPClient + Close() error + UUID() string + CurrentVersion(txnScope string) (kv.Version, error) + CurrentTimestamp(txnScop string) (uint64, error) + GetOracle() oracle.Oracle + SupportDeleteRange() (supported bool) + Name() string + Describe() string + ShowStatus(ctx context.Context, key string) (interface{}, error) + GetMemCache() kv.MemManager + GetRegionCache() *tikv.RegionCache + SendReq(bo *tikv.Backoffer, req *tikvrpc.Request, regionID tikv.RegionVerID, timeout time.Duration) (*tikvrpc.Response, error) + GetLockResolver() *txnlock.LockResolver + GetSafePointKV() tikv.SafePointKV + UpdateSPCache(cachedSP uint64, cachedTime time.Time) + SetOracle(oracle oracle.Oracle) + SetTiKVClient(client tikv.Client) + GetTiKVClient() tikv.Client + Closed() <-chan struct{} + GetMinSafeTS(txnScope string) uint64 + GetLockWaits() ([]*deadlockpb.WaitForEntry, error) + GetCodec() tikv.Codec +} + +// Helper is a middleware to get some information from tikv/pd. It can be used for TiDB's http api or mem table. +type Helper struct { + Store Storage + RegionCache *tikv.RegionCache +} + +// NewHelper gets a Helper from Storage +func NewHelper(store Storage) *Helper { + return &Helper{ + Store: store, + RegionCache: store.GetRegionCache(), + } +} + +// GetMvccByEncodedKey get the MVCC value by the specific encoded key. +func (h *Helper) GetMvccByEncodedKey(encodedKey kv.Key) (*kvrpcpb.MvccGetByKeyResponse, error) { + keyLocation, err := h.RegionCache.LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) + if err != nil { + return nil, derr.ToTiDBErr(err) + } + + tikvReq := tikvrpc.NewRequest(tikvrpc.CmdMvccGetByKey, &kvrpcpb.MvccGetByKeyRequest{Key: encodedKey}) + kvResp, err := h.Store.SendReq(tikv.NewBackofferWithVars(context.Background(), 500, nil), tikvReq, keyLocation.Region, time.Minute) + if err != nil { + logutil.BgLogger().Info("get MVCC by encoded key failed", + zap.Stringer("encodeKey", encodedKey), + zap.Reflect("region", keyLocation.Region), + zap.Stringer("keyLocation", keyLocation), + zap.Reflect("kvResp", kvResp), + zap.Error(err)) + return nil, errors.Trace(err) + } + return kvResp.Resp.(*kvrpcpb.MvccGetByKeyResponse), nil +} + +// MvccKV wraps the key's mvcc info in tikv. +type MvccKV struct { + Key string `json:"key"` + RegionID uint64 `json:"region_id"` + Value *kvrpcpb.MvccGetByKeyResponse `json:"value"` +} + +// GetMvccByStartTs gets Mvcc info by startTS from tikv. +func (h *Helper) GetMvccByStartTs(startTS uint64, startKey, endKey kv.Key) (*MvccKV, error) { + bo := tikv.NewBackofferWithVars(context.Background(), 5000, nil) + for { + curRegion, err := h.RegionCache.LocateKey(bo, startKey) + if err != nil { + logutil.BgLogger().Error("get MVCC by startTS failed", zap.Uint64("txnStartTS", startTS), + zap.Stringer("startKey", startKey), zap.Error(err)) + return nil, derr.ToTiDBErr(err) + } + + tikvReq := tikvrpc.NewRequest(tikvrpc.CmdMvccGetByStartTs, &kvrpcpb.MvccGetByStartTsRequest{ + StartTs: startTS, + }) + tikvReq.Context.Priority = kvrpcpb.CommandPri_Low + kvResp, err := h.Store.SendReq(bo, tikvReq, curRegion.Region, time.Hour) + if err != nil { + logutil.BgLogger().Error("get MVCC by startTS failed", + zap.Uint64("txnStartTS", startTS), + zap.Stringer("startKey", startKey), + zap.Reflect("region", curRegion.Region), + zap.Stringer("curRegion", curRegion), + zap.Reflect("kvResp", kvResp), + zap.Error(err)) + return nil, errors.Trace(err) + } + data := kvResp.Resp.(*kvrpcpb.MvccGetByStartTsResponse) + if err := data.GetRegionError(); err != nil { + logutil.BgLogger().Warn("get MVCC by startTS failed", + zap.Uint64("txnStartTS", startTS), + zap.Stringer("startKey", startKey), + zap.Reflect("region", curRegion.Region), + zap.Stringer("curRegion", curRegion), + zap.Reflect("kvResp", kvResp), + zap.Stringer("error", err)) + continue + } + + if len(data.GetError()) > 0 { + logutil.BgLogger().Error("get MVCC by startTS failed", + zap.Uint64("txnStartTS", startTS), + zap.Stringer("startKey", startKey), + zap.Reflect("region", curRegion.Region), + zap.Stringer("curRegion", curRegion), + zap.Reflect("kvResp", kvResp), + zap.String("error", data.GetError())) + return nil, errors.New(data.GetError()) + } + + key := data.GetKey() + if len(key) > 0 { + resp := &kvrpcpb.MvccGetByKeyResponse{Info: data.Info, RegionError: data.RegionError, Error: data.Error} + return &MvccKV{Key: strings.ToUpper(hex.EncodeToString(key)), Value: resp, RegionID: curRegion.Region.GetID()}, nil + } + + if len(endKey) > 0 && curRegion.Contains(endKey) { + return nil, nil + } + if len(curRegion.EndKey) == 0 { + return nil, nil + } + startKey = curRegion.EndKey + } +} + +// StoreHotRegionInfos records all hog region stores. +// it's the response of PD. +type StoreHotRegionInfos struct { + AsPeer map[uint64]*HotRegionsStat `json:"as_peer"` + AsLeader map[uint64]*HotRegionsStat `json:"as_leader"` +} + +// HotRegionsStat records echo store's hot region. +// it's the response of PD. +type HotRegionsStat struct { + RegionsStat []RegionStat `json:"statistics"` +} + +// RegionStat records each hot region's statistics +// it's the response of PD. +type RegionStat struct { + RegionID uint64 `json:"region_id"` + FlowBytes float64 `json:"flow_bytes"` + HotDegree int `json:"hot_degree"` +} + +// RegionMetric presents the final metric output entry. +type RegionMetric struct { + FlowBytes uint64 `json:"flow_bytes"` + MaxHotDegree int `json:"max_hot_degree"` + Count int `json:"region_count"` +} + +// ScrapeHotInfo gets the needed hot region information by the url given. +func (h *Helper) ScrapeHotInfo(rw string, allSchemas []*model.DBInfo) ([]HotTableIndex, error) { + regionMetrics, err := h.FetchHotRegion(rw) + if err != nil { + return nil, err + } + return h.FetchRegionTableIndex(regionMetrics, allSchemas) +} + +// FetchHotRegion fetches the hot region information from PD's http api. +func (h *Helper) FetchHotRegion(rw string) (map[uint64]RegionMetric, error) { + var regionResp StoreHotRegionInfos + if err := h.requestPD("FetchHotRegion", "GET", rw, nil, ®ionResp); err != nil { + return nil, err + } + metricCnt := 0 + for _, hotRegions := range regionResp.AsLeader { + metricCnt += len(hotRegions.RegionsStat) + } + metric := make(map[uint64]RegionMetric, metricCnt) + for _, hotRegions := range regionResp.AsLeader { + for _, region := range hotRegions.RegionsStat { + metric[region.RegionID] = RegionMetric{FlowBytes: uint64(region.FlowBytes), MaxHotDegree: region.HotDegree} + } + } + return metric, nil +} + +// TblIndex stores the things to index one table. +type TblIndex struct { + DbName string + TableName string + TableID int64 + IndexName string + IndexID int64 +} + +// FrameItem includes a index's or record's meta data with table's info. +type FrameItem struct { + DBName string `json:"db_name"` + TableName string `json:"table_name"` + TableID int64 `json:"table_id"` + IsRecord bool `json:"is_record"` + RecordID int64 `json:"record_id,omitempty"` + IndexName string `json:"index_name,omitempty"` + IndexID int64 `json:"index_id,omitempty"` + IndexValues []string `json:"index_values,omitempty"` +} + +// RegionFrameRange contains a frame range info which the region covered. +type RegionFrameRange struct { + First *FrameItem // start frame of the region + Last *FrameItem // end frame of the region + region *tikv.KeyLocation // the region +} + +// HotTableIndex contains region and its table/index info. +type HotTableIndex struct { + RegionID uint64 `json:"region_id"` + RegionMetric *RegionMetric `json:"region_metric"` + DbName string `json:"db_name"` + TableName string `json:"table_name"` + TableID int64 `json:"table_id"` + IndexName string `json:"index_name"` + IndexID int64 `json:"index_id"` +} + +// FetchRegionTableIndex constructs a map that maps a table to its hot region information by the given raw hot RegionMetric metrics. +func (h *Helper) FetchRegionTableIndex(metrics map[uint64]RegionMetric, allSchemas []*model.DBInfo) ([]HotTableIndex, error) { + hotTables := make([]HotTableIndex, 0, len(metrics)) + for regionID, regionMetric := range metrics { + regionMetric := regionMetric + t := HotTableIndex{RegionID: regionID, RegionMetric: ®ionMetric} + region, err := h.RegionCache.LocateRegionByID(tikv.NewBackofferWithVars(context.Background(), 500, nil), regionID) + if err != nil { + logutil.BgLogger().Error("locate region failed", zap.Error(err)) + continue + } + + hotRange, err := NewRegionFrameRange(region) + if err != nil { + return nil, err + } + f := h.FindTableIndexOfRegion(allSchemas, hotRange) + if f != nil { + t.DbName = f.DBName + t.TableName = f.TableName + t.TableID = f.TableID + t.IndexName = f.IndexName + t.IndexID = f.IndexID + } + hotTables = append(hotTables, t) + } + + return hotTables, nil +} + +// FindTableIndexOfRegion finds what table is involved in this hot region. And constructs the new frame item for future use. +func (*Helper) FindTableIndexOfRegion(allSchemas []*model.DBInfo, hotRange *RegionFrameRange) *FrameItem { + for _, db := range allSchemas { + for _, tbl := range db.Tables { + if f := findRangeInTable(hotRange, db, tbl); f != nil { + return f + } + } + } + return nil +} + +func findRangeInTable(hotRange *RegionFrameRange, db *model.DBInfo, tbl *model.TableInfo) *FrameItem { + pi := tbl.GetPartitionInfo() + if pi == nil { + return findRangeInPhysicalTable(hotRange, tbl.ID, db.Name.O, tbl.Name.O, tbl.Indices, tbl.IsCommonHandle) + } + + for _, def := range pi.Definitions { + tablePartition := fmt.Sprintf("%s(%s)", tbl.Name.O, def.Name) + if f := findRangeInPhysicalTable(hotRange, def.ID, db.Name.O, tablePartition, tbl.Indices, tbl.IsCommonHandle); f != nil { + return f + } + } + return nil +} + +func findRangeInPhysicalTable(hotRange *RegionFrameRange, physicalID int64, dbName, tblName string, indices []*model.IndexInfo, isCommonHandle bool) *FrameItem { + if f := hotRange.GetRecordFrame(physicalID, dbName, tblName, isCommonHandle); f != nil { + return f + } + for _, idx := range indices { + if f := hotRange.GetIndexFrame(physicalID, idx.ID, dbName, tblName, idx.Name.O); f != nil { + return f + } + } + return nil +} + +// NewRegionFrameRange init a NewRegionFrameRange with region info. +func NewRegionFrameRange(region *tikv.KeyLocation) (idxRange *RegionFrameRange, err error) { + var first, last *FrameItem + // check and init first frame + if len(region.StartKey) > 0 { + first, err = NewFrameItemFromRegionKey(region.StartKey) + if err != nil { + return + } + } else { // empty startKey means start with -infinite + first = &FrameItem{ + IndexID: int64(math.MinInt64), + IsRecord: false, + TableID: int64(math.MinInt64), + } + } + + // check and init last frame + if len(region.EndKey) > 0 { + last, err = NewFrameItemFromRegionKey(region.EndKey) + if err != nil { + return + } + } else { // empty endKey means end with +infinite + last = &FrameItem{ + TableID: int64(math.MaxInt64), + IndexID: int64(math.MaxInt64), + IsRecord: true, + } + } + + idxRange = &RegionFrameRange{ + region: region, + First: first, + Last: last, + } + return idxRange, nil +} + +// NewFrameItemFromRegionKey creates a FrameItem with region's startKey or endKey, +// returns err when key is illegal. +func NewFrameItemFromRegionKey(key []byte) (frame *FrameItem, err error) { + frame = &FrameItem{} + frame.TableID, frame.IndexID, frame.IsRecord, err = tablecodec.DecodeKeyHead(key) + if err == nil { + if frame.IsRecord { + var handle kv.Handle + _, handle, err = tablecodec.DecodeRecordKey(key) + if err == nil { + if handle.IsInt() { + frame.RecordID = handle.IntValue() + } else { + data, err := handle.Data() + if err != nil { + return nil, err + } + frame.IndexName = "PRIMARY" + frame.IndexValues = make([]string, 0, len(data)) + for _, datum := range data { + str, err := datum.ToString() + if err != nil { + return nil, err + } + frame.IndexValues = append(frame.IndexValues, str) + } + } + } + } else { + _, _, frame.IndexValues, err = tablecodec.DecodeIndexKey(key) + } + logutil.BgLogger().Warn("decode region key failed", zap.ByteString("key", key), zap.Error(err)) + // Ignore decode errors. + err = nil + return + } + if bytes.HasPrefix(key, tablecodec.TablePrefix()) { + // If SplitTable is enabled, the key may be `t{id}`. + if len(key) == tablecodec.TableSplitKeyLen { + frame.TableID = tablecodec.DecodeTableID(key) + return frame, nil + } + return nil, errors.Trace(err) + } + + // key start with tablePrefix must be either record key or index key + // That's means table's record key and index key are always together + // in the continuous interval. And for key with prefix smaller than + // tablePrefix, is smaller than all tables. While for key with prefix + // bigger than tablePrefix, means is bigger than all tables. + err = nil + if bytes.Compare(key, tablecodec.TablePrefix()) < 0 { + frame.TableID = math.MinInt64 + frame.IndexID = math.MinInt64 + frame.IsRecord = false + return + } + // bigger than tablePrefix, means is bigger than all tables. + frame.TableID = math.MaxInt64 + frame.TableID = math.MaxInt64 + frame.IsRecord = true + return +} + +// GetRecordFrame returns the record frame of a table. If the table's records +// are not covered by this frame range, it returns nil. +func (r *RegionFrameRange) GetRecordFrame(tableID int64, dbName, tableName string, isCommonHandle bool) (f *FrameItem) { + if tableID == r.First.TableID && r.First.IsRecord { + r.First.DBName, r.First.TableName = dbName, tableName + f = r.First + } else if tableID == r.Last.TableID && r.Last.IsRecord { + r.Last.DBName, r.Last.TableName = dbName, tableName + f = r.Last + } else if tableID >= r.First.TableID && tableID < r.Last.TableID { + f = &FrameItem{ + DBName: dbName, + TableName: tableName, + TableID: tableID, + IsRecord: true, + } + } + if f != nil && f.IsRecord && isCommonHandle { + f.IndexName = "PRIMARY" + } + return +} + +// GetIndexFrame returns the indnex frame of a table. If the table's indices are +// not covered by this frame range, it returns nil. +func (r *RegionFrameRange) GetIndexFrame(tableID, indexID int64, dbName, tableName, indexName string) *FrameItem { + if tableID == r.First.TableID && !r.First.IsRecord && indexID == r.First.IndexID { + r.First.DBName, r.First.TableName, r.First.IndexName = dbName, tableName, indexName + return r.First + } + if tableID == r.Last.TableID && indexID == r.Last.IndexID { + r.Last.DBName, r.Last.TableName, r.Last.IndexName = dbName, tableName, indexName + return r.Last + } + + greaterThanFirst := tableID > r.First.TableID || (tableID == r.First.TableID && !r.First.IsRecord && indexID > r.First.IndexID) + lessThanLast := tableID < r.Last.TableID || (tableID == r.Last.TableID && (r.Last.IsRecord || indexID < r.Last.IndexID)) + if greaterThanFirst && lessThanLast { + return &FrameItem{ + DBName: dbName, + TableName: tableName, + TableID: tableID, + IsRecord: false, + IndexName: indexName, + IndexID: indexID, + } + } + return nil +} + +// RegionPeer stores information of one peer. +type RegionPeer struct { + ID int64 `json:"id"` + StoreID int64 `json:"store_id"` + IsLearner bool `json:"is_learner"` +} + +// RegionEpoch stores the information about its epoch. +type RegionEpoch struct { + ConfVer int64 `json:"conf_ver"` + Version int64 `json:"version"` +} + +// RegionPeerStat stores one field `DownSec` which indicates how long it's down than `RegionPeer`. +type RegionPeerStat struct { + Peer RegionPeer `json:"peer"` + DownSec int64 `json:"down_seconds"` +} + +// RegionInfo stores the information of one region. +type RegionInfo struct { + ID int64 `json:"id"` + StartKey string `json:"start_key"` + EndKey string `json:"end_key"` + Epoch RegionEpoch `json:"epoch"` + Peers []RegionPeer `json:"peers"` + Leader RegionPeer `json:"leader"` + DownPeers []RegionPeerStat `json:"down_peers"` + PendingPeers []RegionPeer `json:"pending_peers"` + WrittenBytes uint64 `json:"written_bytes"` + ReadBytes uint64 `json:"read_bytes"` + ApproximateSize int64 `json:"approximate_size"` + ApproximateKeys int64 `json:"approximate_keys"` + + ReplicationStatus *ReplicationStatus `json:"replication_status,omitempty"` +} + +// RegionsInfo stores the information of regions. +type RegionsInfo struct { + Count int64 `json:"count"` + Regions []RegionInfo `json:"regions"` +} + +// NewRegionsInfo returns RegionsInfo +func NewRegionsInfo() *RegionsInfo { + return &RegionsInfo{ + Regions: make([]RegionInfo, 0), + } +} + +// Merge merged 2 regionsInfo into one +func (r *RegionsInfo) Merge(other *RegionsInfo) *RegionsInfo { + newRegionsInfo := &RegionsInfo{ + Regions: make([]RegionInfo, 0, r.Count+other.Count), + } + m := make(map[int64]RegionInfo, r.Count+other.Count) + for _, region := range r.Regions { + m[region.ID] = region + } + for _, region := range other.Regions { + m[region.ID] = region + } + for _, region := range m { + newRegionsInfo.Regions = append(newRegionsInfo.Regions, region) + } + newRegionsInfo.Count = int64(len(newRegionsInfo.Regions)) + return newRegionsInfo +} + +// ReplicationStatus represents the replication mode status of the region. +type ReplicationStatus struct { + State string `json:"state"` + StateID int64 `json:"state_id"` +} + +// TableInfo stores the information of a table or an index +type TableInfo struct { + DB *model.DBInfo + Table *model.TableInfo + IsPartition bool + Partition *model.PartitionDefinition + IsIndex bool + Index *model.IndexInfo +} + +type withKeyRange interface { + getStartKey() string + getEndKey() string +} + +// isIntersecting returns true if x and y intersect. +func isIntersecting(x, y withKeyRange) bool { + return isIntersectingKeyRange(x, y.getStartKey(), y.getEndKey()) +} + +// isIntersectingKeyRange returns true if [startKey, endKey) intersect with x. +func isIntersectingKeyRange(x withKeyRange, startKey, endKey string) bool { + return !isBeforeKeyRange(x, startKey, endKey) && !isBehindKeyRange(x, startKey, endKey) +} + +// isBehind returns true is x is behind y +func isBehind(x, y withKeyRange) bool { + return isBehindKeyRange(x, y.getStartKey(), y.getEndKey()) +} + +// IsBefore returns true is x is before [startKey, endKey) +func isBeforeKeyRange(x withKeyRange, startKey, _ string) bool { + return x.getEndKey() != "" && x.getEndKey() <= startKey +} + +// IsBehind returns true is x is behind [startKey, endKey) +func isBehindKeyRange(x withKeyRange, _, endKey string) bool { + return endKey != "" && x.getStartKey() >= endKey +} + +func (r *RegionInfo) getStartKey() string { return r.StartKey } +func (r *RegionInfo) getEndKey() string { return r.EndKey } + +// TableInfoWithKeyRange stores table or index informations with its key range. +type TableInfoWithKeyRange struct { + *TableInfo + StartKey string + EndKey string +} + +func (t TableInfoWithKeyRange) getStartKey() string { return t.StartKey } +func (t TableInfoWithKeyRange) getEndKey() string { return t.EndKey } + +// NewTableWithKeyRange constructs TableInfoWithKeyRange for given table, it is exported only for test. +func NewTableWithKeyRange(db *model.DBInfo, table *model.TableInfo) TableInfoWithKeyRange { + return newTableInfoWithKeyRange(db, table, nil, nil) +} + +// NewIndexWithKeyRange constructs TableInfoWithKeyRange for given index, it is exported only for test. +func NewIndexWithKeyRange(db *model.DBInfo, table *model.TableInfo, index *model.IndexInfo) TableInfoWithKeyRange { + return newTableInfoWithKeyRange(db, table, nil, index) +} + +// FilterMemDBs filters memory databases in the input schemas. +func (*Helper) FilterMemDBs(oldSchemas []*model.DBInfo) (schemas []*model.DBInfo) { + for _, dbInfo := range oldSchemas { + if util.IsMemDB(dbInfo.Name.L) { + continue + } + schemas = append(schemas, dbInfo) + } + return +} + +// GetRegionsTableInfo returns a map maps region id to its tables or indices. +// Assuming tables or indices key ranges never intersect. +// Regions key ranges can intersect. +func (h *Helper) GetRegionsTableInfo(regionsInfo *RegionsInfo, schemas []*model.DBInfo) map[int64][]TableInfo { + tables := h.GetTablesInfoWithKeyRange(schemas) + + regions := make([]*RegionInfo, 0, len(regionsInfo.Regions)) + for i := 0; i < len(regionsInfo.Regions); i++ { + regions = append(regions, ®ionsInfo.Regions[i]) + } + + tableInfos := h.ParseRegionsTableInfos(regions, tables) + return tableInfos +} + +func newTableInfoWithKeyRange(db *model.DBInfo, table *model.TableInfo, partition *model.PartitionDefinition, index *model.IndexInfo) TableInfoWithKeyRange { + var sk, ek []byte + if partition == nil && index == nil { + sk, ek = tablecodec.GetTableHandleKeyRange(table.ID) + } else if partition != nil && index == nil { + sk, ek = tablecodec.GetTableHandleKeyRange(partition.ID) + } else if partition == nil && index != nil { + sk, ek = tablecodec.GetTableIndexKeyRange(table.ID, index.ID) + } else { + sk, ek = tablecodec.GetTableIndexKeyRange(partition.ID, index.ID) + } + startKey := bytesKeyToHex(codec.EncodeBytes(nil, sk)) + endKey := bytesKeyToHex(codec.EncodeBytes(nil, ek)) + return TableInfoWithKeyRange{ + &TableInfo{ + DB: db, + Table: table, + IsPartition: partition != nil, + Partition: partition, + IsIndex: index != nil, + Index: index, + }, + startKey, + endKey, + } +} + +// GetTablesInfoWithKeyRange returns a slice containing tableInfos with key ranges of all tables in schemas. +func (*Helper) GetTablesInfoWithKeyRange(schemas []*model.DBInfo) []TableInfoWithKeyRange { + tables := []TableInfoWithKeyRange{} + for _, db := range schemas { + for _, table := range db.Tables { + if table.Partition != nil { + for i := range table.Partition.Definitions { + tables = append(tables, newTableInfoWithKeyRange(db, table, &table.Partition.Definitions[i], nil)) + } + } else { + tables = append(tables, newTableInfoWithKeyRange(db, table, nil, nil)) + } + for _, index := range table.Indices { + if table.Partition == nil || index.Global { + tables = append(tables, newTableInfoWithKeyRange(db, table, nil, index)) + continue + } + for i := range table.Partition.Definitions { + tables = append(tables, newTableInfoWithKeyRange(db, table, &table.Partition.Definitions[i], index)) + } + } + } + } + slices.SortFunc(tables, func(i, j TableInfoWithKeyRange) int { + return cmp.Compare(i.getStartKey(), j.getStartKey()) + }) + return tables +} + +// ParseRegionsTableInfos parses the tables or indices in regions according to key range. +func (*Helper) ParseRegionsTableInfos(regionsInfo []*RegionInfo, tables []TableInfoWithKeyRange) map[int64][]TableInfo { + tableInfos := make(map[int64][]TableInfo, len(regionsInfo)) + + if len(tables) == 0 || len(regionsInfo) == 0 { + return tableInfos + } + // tables is sorted in GetTablesInfoWithKeyRange func + slices.SortFunc(regionsInfo, func(i, j *RegionInfo) int { + return cmp.Compare(i.getStartKey(), j.getStartKey()) + }) + + idx := 0 +OutLoop: + for _, region := range regionsInfo { + id := region.ID + tableInfos[id] = []TableInfo{} + for isBehind(region, &tables[idx]) { + idx++ + if idx >= len(tables) { + break OutLoop + } + } + for i := idx; i < len(tables) && isIntersecting(region, &tables[i]); i++ { + tableInfos[id] = append(tableInfos[id], *tables[i].TableInfo) + } + } + + return tableInfos +} + +func bytesKeyToHex(key []byte) string { + return strings.ToUpper(hex.EncodeToString(key)) +} + +// GetRegionsInfo gets the region information of current store by using PD's api. +func (h *Helper) GetRegionsInfo() (*RegionsInfo, error) { + var regionsInfo RegionsInfo + err := h.requestPD("GetRegions", "GET", pdapi.Regions, nil, ®ionsInfo) + return ®ionsInfo, err +} + +// GetStoreRegionsInfo gets the region in given store. +func (h *Helper) GetStoreRegionsInfo(storeID uint64) (*RegionsInfo, error) { + var regionsInfo RegionsInfo + err := h.requestPD("GetStoreRegions", "GET", pdapi.StoreRegions+"/"+strconv.FormatUint(storeID, 10), nil, ®ionsInfo) + return ®ionsInfo, err +} + +// GetRegionInfoByID gets the region information of the region ID by using PD's api. +func (h *Helper) GetRegionInfoByID(regionID uint64) (*RegionInfo, error) { + var regionInfo RegionInfo + err := h.requestPD("GetRegionByID", "GET", pdapi.RegionByID+"/"+strconv.FormatUint(regionID, 10), nil, ®ionInfo) + return ®ionInfo, err +} + +// GetRegionsInfoByRange scans region by key range +func (h *Helper) GetRegionsInfoByRange(sk, ek []byte) (*RegionsInfo, error) { + var regionsInfo RegionsInfo + err := h.requestPD("GetRegionByRange", "GET", fmt.Sprintf("%v?key=%s&end_key=%s&limit=-1", pdapi.ScanRegions, + url.QueryEscape(string(sk)), url.QueryEscape(string(ek))), nil, ®ionsInfo) + return ®ionsInfo, err +} + +// GetRegionByKey gets regioninfo by key +func (h *Helper) GetRegionByKey(k []byte) (*RegionInfo, error) { + var regionInfo RegionInfo + err := h.requestPD("GetRegionByKey", "GET", fmt.Sprintf("%v/%v", pdapi.RegionKey, url.QueryEscape(string(k))), nil, ®ionInfo) + return ®ionInfo, err +} + +// request PD API, decode the response body into res +func (h *Helper) requestPD(apiName, method, uri string, body io.Reader, res interface{}) error { + etcd, ok := h.Store.(kv.EtcdBackend) + if !ok { + return errors.WithStack(errors.New("not implemented")) + } + pdHosts, err := etcd.EtcdAddrs() + if err != nil { + return err + } + if len(pdHosts) == 0 { + return errors.New("pd unavailable") + } + for _, host := range pdHosts { + err = requestPDForOneHost(host, apiName, method, uri, body, res) + if err == nil { + break + } + // Try to request from another PD node when some nodes may down. + } + return err +} + +func requestPDForOneHost(host, apiName, method, uri string, body io.Reader, res interface{}) error { + urlVar := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), host, uri) + logutil.BgLogger().Debug("RequestPD URL", zap.String("url", urlVar)) + req, err := http.NewRequest(method, urlVar, body) + if err != nil { + logutil.BgLogger().Warn("requestPDForOneHost new request failed", + zap.String("url", urlVar), zap.Error(err)) + return errors.Trace(err) + } + start := time.Now() + resp, err := util.InternalHTTPClient().Do(req) + if err != nil { + metrics.PDAPIRequestCounter.WithLabelValues(apiName, "network error").Inc() + logutil.BgLogger().Warn("requestPDForOneHost do request failed", + zap.String("url", urlVar), zap.Error(err)) + return errors.Trace(err) + } + metrics.PDAPIExecutionHistogram.WithLabelValues(apiName).Observe(time.Since(start).Seconds()) + metrics.PDAPIRequestCounter.WithLabelValues(apiName, resp.Status).Inc() + defer func() { + err = resp.Body.Close() + if err != nil { + logutil.BgLogger().Warn("requestPDForOneHost close body failed", + zap.String("url", urlVar), zap.Error(err)) + } + }() + + if resp.StatusCode != http.StatusOK { + logFields := []zap.Field{ + zap.String("url", urlVar), + zap.String("status", resp.Status), + } + + bs, readErr := io.ReadAll(resp.Body) + if readErr != nil { + logFields = append(logFields, zap.NamedError("readBodyError", err)) + } else { + logFields = append(logFields, zap.ByteString("body", bs)) + } + + logutil.BgLogger().Warn("requestPDForOneHost failed with non 200 status", logFields...) + return errors.Errorf("PD request failed with status: '%s'", resp.Status) + } + + err = json.NewDecoder(resp.Body).Decode(res) + if err != nil { + return errors.Trace(err) + } + return nil +} + +// StoresStat stores all information get from PD's api. +type StoresStat struct { + Count int `json:"count"` + Stores []StoreStat `json:"stores"` +} + +// StoreStat stores information of one store. +type StoreStat struct { + Store StoreBaseStat `json:"store"` + Status StoreDetailStat `json:"status"` +} + +// StoreBaseStat stores the basic information of one store. +type StoreBaseStat struct { + ID int64 `json:"id"` + Address string `json:"address"` + State int64 `json:"state"` + StateName string `json:"state_name"` + Version string `json:"version"` + Labels []StoreLabel `json:"labels"` + StatusAddress string `json:"status_address"` + GitHash string `json:"git_hash"` + StartTimestamp int64 `json:"start_timestamp"` +} + +// StoreLabel stores the information of one store label. +type StoreLabel struct { + Key string `json:"key"` + Value string `json:"value"` +} + +// StoreDetailStat stores the detail information of one store. +type StoreDetailStat struct { + Capacity string `json:"capacity"` + Available string `json:"available"` + LeaderCount int64 `json:"leader_count"` + LeaderWeight float64 `json:"leader_weight"` + LeaderScore float64 `json:"leader_score"` + LeaderSize int64 `json:"leader_size"` + RegionCount int64 `json:"region_count"` + RegionWeight float64 `json:"region_weight"` + RegionScore float64 `json:"region_score"` + RegionSize int64 `json:"region_size"` + StartTs time.Time `json:"start_ts"` + LastHeartbeatTs time.Time `json:"last_heartbeat_ts"` + Uptime string `json:"uptime"` +} + +// GetStoresStat gets the TiKV store information by accessing PD's api. +func (h *Helper) GetStoresStat() (*StoresStat, error) { + var storesStat StoresStat + err := h.requestPD("GetStoresStat", "GET", pdapi.Stores, nil, &storesStat) + return &storesStat, err +} + +// GetPDAddr return the PD Address. +func (h *Helper) GetPDAddr() ([]string, error) { + etcd, ok := h.Store.(kv.EtcdBackend) + if !ok { + return nil, errors.New("not implemented") + } + pdAddrs, err := etcd.EtcdAddrs() + if err != nil { + return nil, err + } + if len(pdAddrs) == 0 { + return nil, errors.New("pd unavailable") + } + return pdAddrs, nil +} + +// PDRegionStats is the json response from PD. +type PDRegionStats struct { + Count int `json:"count"` + EmptyCount int `json:"empty_count"` + StorageSize int64 `json:"storage_size"` + StorageKeys int64 `json:"storage_keys"` + StoreLeaderCount map[uint64]int `json:"store_leader_count"` + StorePeerCount map[uint64]int `json:"store_peer_count"` +} + +// GetPDRegionStats get the RegionStats by tableID. +func (h *Helper) GetPDRegionStats(tableID int64, stats *PDRegionStats, noIndexStats bool) error { + pdAddrs, err := h.GetPDAddr() + if err != nil { + return errors.Trace(err) + } + + var startKey, endKey []byte + if noIndexStats { + startKey = tablecodec.GenTableRecordPrefix(tableID) + endKey = kv.Key(startKey).PrefixNext() + } else { + startKey = tablecodec.EncodeTablePrefix(tableID) + endKey = kv.Key(startKey).PrefixNext() + } + startKey = codec.EncodeBytes([]byte{}, startKey) + endKey = codec.EncodeBytes([]byte{}, endKey) + + statURL := fmt.Sprintf("%s://%s/pd/api/v1/stats/region?start_key=%s&end_key=%s", + util.InternalHTTPSchema(), + pdAddrs[0], + url.QueryEscape(string(startKey)), + url.QueryEscape(string(endKey))) + + resp, err := util.InternalHTTPClient().Get(statURL) + if err != nil { + return errors.Trace(err) + } + defer func() { + if err = resp.Body.Close(); err != nil { + logutil.BgLogger().Error("err", zap.Error(err)) + } + }() + if resp.StatusCode != http.StatusOK { + body, err := io.ReadAll(resp.Body) + if err != nil { + return errors.Errorf("GetPDRegionStats %d: %s", resp.StatusCode, err) + } + return errors.Errorf("GetPDRegionStats %d: %s", resp.StatusCode, string(body)) + } + dec := json.NewDecoder(resp.Body) + + return dec.Decode(stats) +} + +// DeletePlacementRule is to delete placement rule for certain group. +func (h *Helper) DeletePlacementRule(group string, ruleID string) error { + pdAddrs, err := h.GetPDAddr() + if err != nil { + return errors.Trace(err) + } + + deleteURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rule/%v/%v", + util.InternalHTTPSchema(), + pdAddrs[0], + group, + ruleID, + ) + + req, err := http.NewRequest("DELETE", deleteURL, nil) + if err != nil { + return errors.Trace(err) + } + + resp, err := util.InternalHTTPClient().Do(req) + if err != nil { + return errors.Trace(err) + } + defer func() { + if err = resp.Body.Close(); err != nil { + logutil.BgLogger().Error("err", zap.Error(err)) + } + }() + if resp.StatusCode != http.StatusOK { + return errors.New("DeletePlacementRule returns error") + } + return nil +} + +// SetPlacementRule is a helper function to set placement rule. +func (h *Helper) SetPlacementRule(rule placement.Rule) error { + pdAddrs, err := h.GetPDAddr() + if err != nil { + return errors.Trace(err) + } + m, _ := json.Marshal(rule) + + postURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rule", + util.InternalHTTPSchema(), + pdAddrs[0], + ) + buf := bytes.NewBuffer(m) + resp, err := util.InternalHTTPClient().Post(postURL, "application/json", buf) + if err != nil { + return errors.Trace(err) + } + defer func() { + if err = resp.Body.Close(); err != nil { + logutil.BgLogger().Error("err", zap.Error(err)) + } + }() + if resp.StatusCode != http.StatusOK { + return errors.New("SetPlacementRule returns error") + } + return nil +} + +// GetGroupRules to get all placement rule in a certain group. +func (h *Helper) GetGroupRules(group string) ([]placement.Rule, error) { + pdAddrs, err := h.GetPDAddr() + if err != nil { + return nil, errors.Trace(err) + } + + getURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rules/group/%s", + util.InternalHTTPSchema(), + pdAddrs[0], + group, + ) + + resp, err := util.InternalHTTPClient().Get(getURL) + if err != nil { + return nil, errors.Trace(err) + } + defer func() { + if err = resp.Body.Close(); err != nil { + logutil.BgLogger().Error("err", zap.Error(err)) + } + }() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New("GetGroupRules returns error") + } + + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(resp.Body) + if err != nil { + return nil, errors.Trace(err) + } + + var rules []placement.Rule + err = json.Unmarshal(buf.Bytes(), &rules) + if err != nil { + return nil, errors.Trace(err) + } + + return rules, nil +} + +// PostAccelerateSchedule sends `regions/accelerate-schedule` request. +func (h *Helper) PostAccelerateSchedule(tableID int64) error { + pdAddrs, err := h.GetPDAddr() + if err != nil { + return errors.Trace(err) + } + startKey := tablecodec.GenTableRecordPrefix(tableID) + endKey := tablecodec.EncodeTablePrefix(tableID + 1) + startKey = codec.EncodeBytes([]byte{}, startKey) + endKey = codec.EncodeBytes([]byte{}, endKey) + + postURL := fmt.Sprintf("%s://%s/pd/api/v1/regions/accelerate-schedule", + util.InternalHTTPSchema(), + pdAddrs[0]) + + input := map[string]string{ + "start_key": url.QueryEscape(string(startKey)), + "end_key": url.QueryEscape(string(endKey)), + } + v, err := json.Marshal(input) + if err != nil { + return errors.Trace(err) + } + resp, err := util.InternalHTTPClient().Post(postURL, "application/json", bytes.NewBuffer(v)) + if err != nil { + return errors.Trace(err) + } + defer func() { + if err = resp.Body.Close(); err != nil { + logutil.BgLogger().Error("err", zap.Error(err)) + } + }() + return nil +} + +// GetTiFlashTableIDFromEndKey computes tableID from pd rule's endKey. +func GetTiFlashTableIDFromEndKey(endKey string) int64 { + e, _ := hex.DecodeString(endKey) + _, decodedEndKey, _ := codec.DecodeBytes(e, []byte{}) + tableID := tablecodec.DecodeTableID(decodedEndKey) + tableID-- + return tableID +} + +// ComputeTiFlashStatus is helper function for CollectTiFlashStatus. +func ComputeTiFlashStatus(reader *bufio.Reader, regionReplica *map[int64]int) error { + ns, err := reader.ReadString('\n') + if err != nil { + return errors.Trace(err) + } + // The count + ns = strings.Trim(ns, "\r\n\t") + n, err := strconv.ParseInt(ns, 10, 64) + if err != nil { + return errors.Trace(err) + } + // The regions + regions, err := reader.ReadString('\n') + if err != nil { + return errors.Trace(err) + } + regions = strings.Trim(regions, "\r\n\t") + splits := strings.Split(regions, " ") + realN := int64(0) + for _, s := range splits { + // For (`table`, `store`), has region `r` + if s == "" { + continue + } + realN++ + r, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return errors.Trace(err) + } + if c, ok := (*regionReplica)[r]; ok { + (*regionReplica)[r] = c + 1 + } else { + (*regionReplica)[r] = 1 + } + } + if n != realN { + logutil.BgLogger().Warn("ComputeTiFlashStatus count check failed", zap.Int64("claim", n), zap.Int64("real", realN)) + } + return nil +} + +// CollectTiFlashStatus query sync status of one table from TiFlash store. +// `regionReplica` is a map from RegionID to count of TiFlash Replicas in this region. +func CollectTiFlashStatus(statusAddress string, keyspaceID tikv.KeyspaceID, tableID int64, regionReplica *map[int64]int) error { + // The new query schema is like: http:///tiflash/sync-status/keyspace//table/. + // For TiDB forward compatibility, we define the Nullspace as the "keyspace" of the old table. + // The query URL is like: http:///sync-status/keyspace//table/ + // The old query schema is like: http:///sync-status/ + // This API is preserved in TiFlash for compatibility with old versions of TiDB. + statURL := fmt.Sprintf("%s://%s/tiflash/sync-status/keyspace/%d/table/%d", + util.InternalHTTPSchema(), + statusAddress, + keyspaceID, + tableID, + ) + resp, err := util.InternalHTTPClient().Get(statURL) + if err != nil { + return errors.Trace(err) + } + + defer func() { + err = resp.Body.Close() + if err != nil { + logutil.BgLogger().Error("close body failed", zap.Error(err)) + } + }() + + reader := bufio.NewReader(resp.Body) + if err = ComputeTiFlashStatus(reader, regionReplica); err != nil { + return errors.Trace(err) + } + return nil +} diff --git a/pkg/store/helper/helper_test.go b/pkg/store/helper/helper_test.go new file mode 100644 index 0000000000000..2be42a85198e0 --- /dev/null +++ b/pkg/store/helper/helper_test.go @@ -0,0 +1,482 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package helper_test + +import ( + "bufio" + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/gorilla/mux" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/pdapi" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" + "go.opencensus.io/stats/view" + "go.uber.org/zap" +) + +func TestHotRegion(t *testing.T) { + store := createMockStore(t) + + h := helper.Helper{ + Store: store, + RegionCache: store.GetRegionCache(), + } + regionMetric, err := h.FetchHotRegion(pdapi.HotRead) + require.NoError(t, err) + + expected := map[uint64]helper.RegionMetric{ + 2: { + FlowBytes: 100, + MaxHotDegree: 1, + Count: 0, + }, + 4: { + FlowBytes: 200, + MaxHotDegree: 2, + Count: 0, + }, + } + require.Equal(t, expected, regionMetric) + + dbInfo := &model.DBInfo{ + Name: model.NewCIStr("test"), + } + require.NoError(t, err) + + res, err := h.FetchRegionTableIndex(regionMetric, []*model.DBInfo{dbInfo}) + require.NotEqual(t, res[0].RegionMetric, res[1].RegionMetric) + require.NoError(t, err) +} + +func TestGetRegionsTableInfo(t *testing.T) { + store := createMockStore(t) + + h := helper.NewHelper(store) + regionsInfo := getMockTiKVRegionsInfo() + schemas := getMockRegionsTableInfoSchema() + tableInfos := h.GetRegionsTableInfo(regionsInfo, schemas) + require.Equal(t, getRegionsTableInfoAns(schemas), tableInfos) +} + +func TestTiKVRegionsInfo(t *testing.T) { + store := createMockStore(t) + + h := helper.Helper{ + Store: store, + RegionCache: store.GetRegionCache(), + } + regionsInfo, err := h.GetRegionsInfo() + require.NoError(t, err) + require.Equal(t, getMockTiKVRegionsInfo(), regionsInfo) +} + +func TestTiKVStoresStat(t *testing.T) { + store := createMockStore(t) + + h := helper.Helper{ + Store: store, + RegionCache: store.GetRegionCache(), + } + + stat, err := h.GetStoresStat() + require.NoError(t, err) + + data, err := json.Marshal(stat) + require.NoError(t, err) + + expected := `{"count":1,"stores":[{"store":{"id":1,"address":"127.0.0.1:20160","state":0,"state_name":"Up","version":"3.0.0-beta","labels":[{"key":"test","value":"test"}],"status_address":"","git_hash":"","start_timestamp":0},"status":{"capacity":"60 GiB","available":"100 GiB","leader_count":10,"leader_weight":999999.999999,"leader_score":999999.999999,"leader_size":1000,"region_count":200,"region_weight":999999.999999,"region_score":999999.999999,"region_size":1000,"start_ts":"2019-04-23T19:30:30+08:00","last_heartbeat_ts":"2019-04-23T19:31:30+08:00","uptime":"1h30m"}}]}` + require.Equal(t, expected, string(data)) +} + +type mockStore struct { + helper.Storage + pdAddrs []string +} + +func (s *mockStore) EtcdAddrs() ([]string, error) { + return s.pdAddrs, nil +} + +func (s *mockStore) StartGCWorker() error { + panic("not implemented") +} + +func (s *mockStore) TLSConfig() *tls.Config { + panic("not implemented") +} + +func (s *mockStore) Name() string { + return "mock store" +} + +func (s *mockStore) Describe() string { + return "" +} + +func createMockStore(t *testing.T) (store helper.Storage) { + s, err := mockstore.NewMockStore( + mockstore.WithClusterInspector(func(c testutils.Cluster) { + mockstore.BootstrapWithMultiRegions(c, []byte("x")) + }), + ) + require.NoError(t, err) + + server := mockPDHTTPServer() + + store = &mockStore{ + s.(helper.Storage), + []string{"invalid_pd_address", server.URL[len("http://"):]}, + } + + t.Cleanup(func() { + server.Close() + view.Stop() + require.NoError(t, store.Close()) + }) + + return +} + +func mockPDHTTPServer() *httptest.Server { + router := mux.NewRouter() + router.HandleFunc(pdapi.HotRead, mockHotRegionResponse) + router.HandleFunc(pdapi.Regions, mockTiKVRegionsInfoResponse) + router.HandleFunc(pdapi.Stores, mockStoreStatResponse) + serverMux := http.NewServeMux() + serverMux.Handle("/", router) + return httptest.NewServer(serverMux) +} + +func mockHotRegionResponse(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + regionsStat := helper.HotRegionsStat{ + RegionsStat: []helper.RegionStat{ + { + FlowBytes: 100, + RegionID: 2, + HotDegree: 1, + }, + { + FlowBytes: 200, + RegionID: 4, + HotDegree: 2, + }, + }, + } + resp := helper.StoreHotRegionInfos{ + AsLeader: make(map[uint64]*helper.HotRegionsStat), + } + resp.AsLeader[0] = ®ionsStat + data, err := json.MarshalIndent(resp, "", " ") + if err != nil { + log.Panic("json marshal failed", zap.Error(err)) + } + _, err = w.Write(data) + if err != nil { + log.Panic("write http response failed", zap.Error(err)) + } +} + +func getMockRegionsTableInfoSchema() []*model.DBInfo { + return []*model.DBInfo{ + { + Name: model.NewCIStr("test"), + Tables: []*model.TableInfo{ + { + ID: 41, + Indices: []*model.IndexInfo{{ID: 1}}, + }, + { + ID: 63, + Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}}, + }, + { + ID: 66, + Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}, {ID: 3}}, + }, + }, + }, + } +} + +func getRegionsTableInfoAns(dbs []*model.DBInfo) map[int64][]helper.TableInfo { + ans := make(map[int64][]helper.TableInfo) + db := dbs[0] + ans[1] = []helper.TableInfo{} + ans[2] = []helper.TableInfo{ + {db, db.Tables[0], false, nil, true, db.Tables[0].Indices[0]}, + {db, db.Tables[0], false, nil, false, nil}, + } + ans[3] = []helper.TableInfo{ + {db, db.Tables[1], false, nil, true, db.Tables[1].Indices[0]}, + {db, db.Tables[1], false, nil, true, db.Tables[1].Indices[1]}, + {db, db.Tables[1], false, nil, false, nil}, + } + ans[4] = []helper.TableInfo{ + {db, db.Tables[2], false, nil, false, nil}, + } + ans[5] = []helper.TableInfo{ + {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[2]}, + {db, db.Tables[2], false, nil, false, nil}, + } + ans[6] = []helper.TableInfo{ + {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[0]}, + } + ans[7] = []helper.TableInfo{ + {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[1]}, + } + ans[8] = []helper.TableInfo{ + {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[1]}, + {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[2]}, + {db, db.Tables[2], false, nil, false, nil}, + } + return ans +} + +func getMockTiKVRegionsInfo() *helper.RegionsInfo { + regions := []helper.RegionInfo{ + { + ID: 1, + StartKey: "", + EndKey: "12341234", + Epoch: helper.RegionEpoch{ + ConfVer: 1, + Version: 1, + }, + Peers: []helper.RegionPeer{ + {ID: 2, StoreID: 1}, + {ID: 15, StoreID: 51}, + {ID: 66, StoreID: 99, IsLearner: true}, + {ID: 123, StoreID: 111, IsLearner: true}, + }, + Leader: helper.RegionPeer{ + ID: 2, + StoreID: 1, + }, + DownPeers: []helper.RegionPeerStat{ + { + helper.RegionPeer{ID: 66, StoreID: 99, IsLearner: true}, + 120, + }, + }, + PendingPeers: []helper.RegionPeer{ + {ID: 15, StoreID: 51}, + }, + WrittenBytes: 100, + ReadBytes: 1000, + ApproximateKeys: 200, + ApproximateSize: 500, + }, + // table: 41, record + index: 1 + { + ID: 2, + StartKey: "7480000000000000FF295F698000000000FF0000010000000000FA", + EndKey: "7480000000000000FF2B5F698000000000FF0000010000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 3, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 3, StoreID: 1}, + }, + // table: 63, record + index: 1, 2 + { + ID: 3, + StartKey: "7480000000000000FF3F5F698000000000FF0000010000000000FA", + EndKey: "7480000000000000FF425F698000000000FF0000010000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 4, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 4, StoreID: 1}, + }, + // table: 66, record + { + ID: 4, + StartKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", + EndKey: "", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 5, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 5, StoreID: 1}, + }, + // table: 66, record + index: 3 + { + ID: 5, + StartKey: "7480000000000000FF425F698000000000FF0000030000000000FA", + EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 6, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 6, StoreID: 1}, + }, + // table: 66, index: 1 + { + ID: 6, + StartKey: "7480000000000000FF425F698000000000FF0000010000000000FA", + EndKey: "7480000000000000FF425F698000000000FF0000020000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 7, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 7, StoreID: 1}, + }, + // table: 66, index: 2 + { + ID: 7, + StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA", + EndKey: "7480000000000000FF425F698000000000FF0000030000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 8, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 8, StoreID: 1}, + }, + // merge region 7, 5 + { + ID: 8, + StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA", + EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", + Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []helper.RegionPeer{{ID: 9, StoreID: 1}}, + Leader: helper.RegionPeer{ID: 9, StoreID: 1}, + }, + } + return &helper.RegionsInfo{ + Count: int64(len(regions)), + Regions: regions, + } +} + +func mockTiKVRegionsInfoResponse(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + resp := getMockTiKVRegionsInfo() + data, err := json.MarshalIndent(resp, "", " ") + if err != nil { + log.Panic("json marshal failed", zap.Error(err)) + } + _, err = w.Write(data) + if err != nil { + log.Panic("write http response failed", zap.Error(err)) + } +} + +func mockStoreStatResponse(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + startTs, err := time.Parse(time.RFC3339, "2019-04-23T19:30:30+08:00") + if err != nil { + log.Panic("mock tikv store api response failed", zap.Error(err)) + } + lastHeartbeatTs, err := time.Parse(time.RFC3339, "2019-04-23T19:31:30+08:00") + if err != nil { + log.Panic("mock tikv store api response failed", zap.Error(err)) + } + storesStat := helper.StoresStat{ + Count: 1, + Stores: []helper.StoreStat{ + { + Store: helper.StoreBaseStat{ + ID: 1, + Address: "127.0.0.1:20160", + State: 0, + StateName: "Up", + Version: "3.0.0-beta", + Labels: []helper.StoreLabel{ + { + Key: "test", + Value: "test", + }, + }, + }, + Status: helper.StoreDetailStat{ + Capacity: "60 GiB", + Available: "100 GiB", + LeaderCount: 10, + LeaderWeight: 999999.999999, + LeaderScore: 999999.999999, + LeaderSize: 1000, + RegionCount: 200, + RegionWeight: 999999.999999, + RegionScore: 999999.999999, + RegionSize: 1000, + StartTs: startTs, + LastHeartbeatTs: lastHeartbeatTs, + Uptime: "1h30m", + }, + }, + }, + } + data, err := json.MarshalIndent(storesStat, "", " ") + if err != nil { + log.Panic("json marshal failed", zap.Error(err)) + } + _, err = w.Write(data) + if err != nil { + log.Panic("write http response failed", zap.Error(err)) + } +} + +func TestComputeTiFlashStatus(t *testing.T) { + regionReplica := make(map[int64]int) + // There are no region in this TiFlash store. + br1 := bufio.NewReader(strings.NewReader("0\n\n")) + // There are 2 regions 1009/1010 in this TiFlash store. + br2 := bufio.NewReader(strings.NewReader("2\n1009 1010 \n")) + err := helper.ComputeTiFlashStatus(br1, ®ionReplica) + require.NoError(t, err) + err = helper.ComputeTiFlashStatus(br2, ®ionReplica) + require.NoError(t, err) + require.Equal(t, len(regionReplica), 2) + v, ok := regionReplica[1009] + require.Equal(t, v, 1) + require.Equal(t, ok, true) + v, ok = regionReplica[1010] + require.Equal(t, v, 1) + require.Equal(t, ok, true) + + regionReplica2 := make(map[int64]int) + var sb strings.Builder + for i := 1000; i < 3000; i++ { + sb.WriteString(fmt.Sprintf("%v ", i)) + } + s := fmt.Sprintf("2000\n%v\n", sb.String()) + require.NoError(t, helper.ComputeTiFlashStatus(bufio.NewReader(strings.NewReader(s)), ®ionReplica2)) + require.Equal(t, 2000, len(regionReplica2)) + for i := 1000; i < 3000; i++ { + _, ok := regionReplica2[int64(i)] + require.True(t, ok) + } +} + +// TestTableRange tests the first part of GetPDRegionStats. +func TestTableRange(t *testing.T) { + startKey := tablecodec.GenTableRecordPrefix(1) + endKey := startKey.PrefixNext() + // t+id+_r + require.Equal(t, "7480000000000000015f72", startKey.String()) + // t+id+_s + require.Equal(t, "7480000000000000015f73", endKey.String()) + + startKey = tablecodec.EncodeTablePrefix(1) + endKey = startKey.PrefixNext() + // t+id + require.Equal(t, "748000000000000001", startKey.String()) + // t+(id+1) + require.Equal(t, "748000000000000002", endKey.String()) +} diff --git a/pkg/store/helper/main_test.go b/pkg/store/helper/main_test.go new file mode 100644 index 0000000000000..35406a5675883 --- /dev/null +++ b/pkg/store/helper/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package helper + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/main_test.go b/pkg/store/main_test.go new file mode 100644 index 0000000000000..30f695b07771c --- /dev/null +++ b/pkg/store/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package store + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/mockstore/BUILD.bazel b/pkg/store/mockstore/BUILD.bazel new file mode 100644 index 0000000000000..97677e14d3a3d --- /dev/null +++ b/pkg/store/mockstore/BUILD.bazel @@ -0,0 +1,56 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mockstore", + srcs = [ + "mockstore.go", + "redirector.go", + "tikv.go", + "unistore.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/store/mockstore/mockcopr", + "//pkg/store/mockstore/mockstorage", + "//pkg/store/mockstore/unistore", + "//pkg/testkit/testenv", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//util", + "@com_github_tikv_pd_client//:client", + ], +) + +go_test( + name = "mockstore_test", + timeout = "short", + srcs = [ + "cluster_test.go", + "main_test.go", + "tikv_test.go", + ], + embed = [":mockstore"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/sessionctx/stmtctx", + "//pkg/tablecodec", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/rowcodec", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/mockstore/cluster_test.go b/pkg/store/mockstore/cluster_test.go new file mode 100644 index 0000000000000..52d23e4092d65 --- /dev/null +++ b/pkg/store/mockstore/cluster_test.go @@ -0,0 +1,132 @@ +// Copyright 2016-present, PingCAP, Inc. +// +// 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. + +package mockstore + +import ( + "bytes" + "context" + "math" + "strconv" + "testing" + "time" + + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/testutils" + "github.com/tikv/client-go/v2/tikv" +) + +func TestClusterSplit(t *testing.T) { + rpcClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) + require.NoError(t, err) + testutils.BootstrapWithSingleStore(cluster) + mvccStore := rpcClient.MvccStore + + store, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) + require.NoError(t, err) + defer store.Close() + + txn, err := store.Begin() + require.NoError(t, err) + + // Mock inserting many rows in a table. + tblID := int64(1) + idxID := int64(2) + colID := int64(3) + handle := int64(1) + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + for i := 0; i < 1000; i++ { + rowKey := tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(handle)) + colValue := types.NewStringDatum(strconv.Itoa(int(handle))) + // TODO: Should use session's TimeZone instead of UTC. + rd := rowcodec.Encoder{Enable: true} + rowValue, err1 := tablecodec.EncodeRow(sc, []types.Datum{colValue}, []int64{colID}, nil, nil, &rd) + require.NoError(t, err1) + txn.Set(rowKey, rowValue) + + encodedIndexValue, err1 := codec.EncodeKey(sc, nil, []types.Datum{colValue, types.NewIntDatum(handle)}...) + require.NoError(t, err1) + idxKey := tablecodec.EncodeIndexSeekKey(tblID, idxID, encodedIndexValue) + txn.Set(idxKey, []byte{'0'}) + handle++ + } + err = txn.Commit(context.Background()) + require.NoError(t, err) + + // Split Table into 10 regions. + tableStart := tablecodec.GenTableRecordPrefix(tblID) + cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 10) + + // 10 table regions and first region and last region. + regions := cluster.GetAllRegions() + require.Len(t, regions, 12) + + allKeysMap := make(map[string]bool) + recordPrefix := tablecodec.GenTableRecordPrefix(tblID) + for _, region := range regions { + startKey := toRawKey(region.Meta.StartKey) + endKey := toRawKey(region.Meta.EndKey) + if !bytes.HasPrefix(startKey, recordPrefix) { + continue + } + pairs := mvccStore.Scan(startKey, endKey, math.MaxInt64, math.MaxUint64, kvrpcpb.IsolationLevel_SI, nil) + if len(pairs) > 0 { + require.Len(t, pairs, 100) + } + for _, pair := range pairs { + allKeysMap[string(pair.Key)] = true + } + } + require.Len(t, allKeysMap, 1000) + + indexStart := tablecodec.EncodeTableIndexPrefix(tblID, idxID) + cluster.SplitKeys(indexStart, indexStart.PrefixNext(), 10) + + allIndexMap := make(map[string]bool) + indexPrefix := tablecodec.EncodeTableIndexPrefix(tblID, idxID) + regions = cluster.GetAllRegions() + for _, region := range regions { + startKey := toRawKey(region.Meta.StartKey) + endKey := toRawKey(region.Meta.EndKey) + if !bytes.HasPrefix(startKey, indexPrefix) { + continue + } + pairs := mvccStore.Scan(startKey, endKey, math.MaxInt64, math.MaxUint64, kvrpcpb.IsolationLevel_SI, nil) + if len(pairs) > 0 { + require.Len(t, pairs, 100) + } + for _, pair := range pairs { + allIndexMap[string(pair.Key)] = true + } + } + require.Len(t, allIndexMap, 1000) +} + +func toRawKey(k []byte) []byte { + if len(k) == 0 { + return nil + } + _, k, err := codec.DecodeBytes(k, nil) + if err != nil { + panic(err) + } + return k +} diff --git a/pkg/store/mockstore/main_test.go b/pkg/store/mockstore/main_test.go new file mode 100644 index 0000000000000..f182f7617bc90 --- /dev/null +++ b/pkg/store/mockstore/main_test.go @@ -0,0 +1,40 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mockstore + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + callback := func(i int) int { + // wait for leveldb to close, leveldb will be closed in one second + time.Sleep(time.Second) + return i + } + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/pkg/store/mockstore/mockcopr/BUILD.bazel b/pkg/store/mockstore/mockcopr/BUILD.bazel new file mode 100644 index 0000000000000..3eb69ae39b10a --- /dev/null +++ b/pkg/store/mockstore/mockcopr/BUILD.bazel @@ -0,0 +1,76 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mockcopr", + srcs = [ + "aggregate.go", + "analyze.go", + "checksum.go", + "cop_handler_dag.go", + "copr_handler.go", + "executor.go", + "rpc_copr.go", + "topn.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/mockcopr", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/rowcodec", + "//pkg/util/timeutil", + "@com_github_golang_protobuf//proto", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//metadata", + ], +) + +go_test( + name = "mockcopr_test", + timeout = "short", + srcs = [ + "executor_test.go", + "main_test.go", + ], + embed = [":mockcopr"], + flaky = True, + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/store/mockstore/mockstorage", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/store/mockstore/mockcopr/aggregate.go b/pkg/store/mockstore/mockcopr/aggregate.go similarity index 97% rename from store/mockstore/mockcopr/aggregate.go rename to pkg/store/mockstore/mockcopr/aggregate.go index 0443534a574d9..a7f1dccf63421 100644 --- a/store/mockstore/mockcopr/aggregate.go +++ b/pkg/store/mockstore/mockcopr/aggregate.go @@ -19,12 +19,12 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" ) type aggCtxsMapper map[string][]*aggregation.AggEvaluateContext diff --git a/pkg/store/mockstore/mockcopr/analyze.go b/pkg/store/mockstore/mockcopr/analyze.go new file mode 100644 index 0000000000000..d4b19736b9ad2 --- /dev/null +++ b/pkg/store/mockstore/mockcopr/analyze.go @@ -0,0 +1,294 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package mockcopr + +import ( + "context" + + "github.com/golang/protobuf/proto" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/pingcap/tipb/go-tipb" +) + +func (h coprHandler) handleCopAnalyzeRequest(req *coprocessor.Request) *coprocessor.Response { + resp := &coprocessor.Response{} + if len(req.Ranges) == 0 { + return resp + } + if req.GetTp() != kv.ReqTypeAnalyze { + return resp + } + if err := h.CheckRequestContext(req.GetContext()); err != nil { + resp.RegionError = err + return resp + } + analyzeReq := new(tipb.AnalyzeReq) + err := proto.Unmarshal(req.Data, analyzeReq) + if err != nil { + resp.OtherError = err.Error() + return resp + } + if analyzeReq.Tp == tipb.AnalyzeType_TypeIndex { + resp, err = h.handleAnalyzeIndexReq(req, analyzeReq) + } else { + resp, err = h.handleAnalyzeColumnsReq(req, analyzeReq) + } + if err != nil { + resp.OtherError = err.Error() + } + return resp +} + +func (h coprHandler) handleAnalyzeIndexReq(req *coprocessor.Request, analyzeReq *tipb.AnalyzeReq) (*coprocessor.Response, error) { + ranges, err := h.extractKVRanges(req.Ranges, false) + if err != nil { + return nil, errors.Trace(err) + } + startTS := req.StartTs + if startTS == 0 { + startTS = analyzeReq.GetStartTsFallback() + } + e := &indexScanExec{ + colsLen: int(analyzeReq.IdxReq.NumColumns), + kvRanges: ranges, + startTS: startTS, + isolationLevel: h.GetIsolationLevel(), + mvccStore: h.GetMVCCStore(), + IndexScan: &tipb.IndexScan{Desc: false}, + execDetail: new(execDetail), + hdStatus: tablecodec.HandleNotNeeded, + } + statsBuilder := statistics.NewSortedBuilder(flagsToStatementContext(analyzeReq.Flags), analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), statistics.Version1) + var cms *statistics.CMSketch + if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { + cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) + } + ctx := context.TODO() + var values [][]byte + for { + values, err = e.Next(ctx) + if err != nil { + return nil, errors.Trace(err) + } + if values == nil { + break + } + var value []byte + for _, val := range values { + value = append(value, val...) + if cms != nil { + cms.InsertBytes(value) + } + } + err = statsBuilder.Iterate(types.NewBytesDatum(value)) + if err != nil { + return nil, errors.Trace(err) + } + } + hg := statistics.HistogramToProto(statsBuilder.Hist()) + var cm *tipb.CMSketch + if cms != nil { + cm = statistics.CMSketchToProto(cms, nil) + } + data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm}) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +type analyzeColumnsExec struct { + tblExec *tableScanExec + fields []*ast.ResultField +} + +func (h coprHandler) handleAnalyzeColumnsReq(req *coprocessor.Request, analyzeReq *tipb.AnalyzeReq) (_ *coprocessor.Response, err error) { + sc := flagsToStatementContext(analyzeReq.Flags) + tz, err := timeutil.ConstructTimeZone("", int(analyzeReq.TimeZoneOffset)) + if err != nil { + return nil, errors.Trace(err) + } + sc.SetTimeZone(tz) + + evalCtx := &evalContext{sc: sc} + columns := analyzeReq.ColReq.ColumnsInfo + evalCtx.setColumnInfo(columns) + ranges, err := h.extractKVRanges(req.Ranges, false) + if err != nil { + return nil, errors.Trace(err) + } + startTS := req.StartTs + if startTS == 0 { + startTS = analyzeReq.GetStartTsFallback() + } + colInfos := make([]rowcodec.ColInfo, len(columns)) + for i := range columns { + col := columns[i] + colInfos[i] = rowcodec.ColInfo{ + ID: col.ColumnId, + Ft: evalCtx.fieldTps[i], + IsPKHandle: col.GetPkHandle(), + } + } + defVal := func(i int) ([]byte, error) { + col := columns[i] + if col.DefaultVal == nil { + return nil, nil + } + // col.DefaultVal always be varint `[flag]+[value]`. + if len(col.DefaultVal) < 1 { + panic("invalid default value") + } + return col.DefaultVal, nil + } + rd := rowcodec.NewByteDecoder(colInfos, []int64{-1}, defVal, nil) + e := &analyzeColumnsExec{ + tblExec: &tableScanExec{ + TableScan: &tipb.TableScan{Columns: columns}, + kvRanges: ranges, + colIDs: evalCtx.colIDs, + startTS: startTS, + isolationLevel: h.GetIsolationLevel(), + mvccStore: h.GetMVCCStore(), + execDetail: new(execDetail), + rd: rd, + }, + } + e.fields = make([]*ast.ResultField, len(columns)) + for i := range e.fields { + rf := new(ast.ResultField) + rf.Column = new(model.ColumnInfo) + ft := types.FieldType{} + ft.SetType(mysql.TypeBlob) + ft.SetFlen(mysql.MaxBlobWidth) + ft.SetCharset(mysql.DefaultCharset) + ft.SetCollate(mysql.DefaultCollationName) + rf.Column.FieldType = ft + e.fields[i] = rf + } + + pkID := int64(-1) + numCols := len(columns) + if columns[0].GetPkHandle() { + pkID = columns[0].ColumnId + columns = columns[1:] + numCols-- + } + collators := make([]collate.Collator, numCols) + fts := make([]*types.FieldType, numCols) + for i, col := range columns { + ft := fieldTypeFromPBColumn(col) + fts[i] = ft + if ft.EvalType() == types.ETString { + collators[i] = collate.GetCollator(ft.GetCollate()) + } + } + colReq := analyzeReq.ColReq + builder := statistics.SampleBuilder{ + Sc: sc, + RecordSet: e, + ColLen: numCols, + MaxBucketSize: colReq.BucketSize, + MaxFMSketchSize: colReq.SketchSize, + MaxSampleSize: colReq.SampleSize, + Collators: collators, + ColsFieldType: fts, + } + if pkID != -1 { + builder.PkBuilder = statistics.NewSortedBuilder(sc, builder.MaxBucketSize, pkID, types.NewFieldType(mysql.TypeBlob), statistics.Version1) + } + if colReq.CmsketchWidth != nil && colReq.CmsketchDepth != nil { + builder.CMSketchWidth = *colReq.CmsketchWidth + builder.CMSketchDepth = *colReq.CmsketchDepth + } + collectors, pkBuilder, err := builder.CollectColumnStats() + if err != nil { + return nil, errors.Trace(err) + } + colResp := &tipb.AnalyzeColumnsResp{} + if pkID != -1 { + colResp.PkHist = statistics.HistogramToProto(pkBuilder.Hist()) + } + for _, c := range collectors { + colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) + } + data, err := proto.Marshal(colResp) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +// Fields implements the sqlexec.RecordSet Fields interface. +func (e *analyzeColumnsExec) Fields() []*ast.ResultField { + return e.fields +} + +func (e *analyzeColumnsExec) getNext(ctx context.Context) ([]types.Datum, error) { + values, err := e.tblExec.Next(ctx) + if err != nil { + return nil, errors.Trace(err) + } + if values == nil { + return nil, nil + } + datumRow := make([]types.Datum, 0, len(values)) + for _, val := range values { + d := types.NewBytesDatum(val) + if len(val) == 1 && val[0] == codec.NilFlag { + d.SetNull() + } + datumRow = append(datumRow, d) + } + return datumRow, nil +} + +func (e *analyzeColumnsExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + row, err := e.getNext(ctx) + if row == nil || err != nil { + return errors.Trace(err) + } + for i := 0; i < len(row); i++ { + req.AppendDatum(i, &row[i]) + } + return nil +} + +func (e *analyzeColumnsExec) NewChunk(_ chunk.Allocator) *chunk.Chunk { + fields := make([]*types.FieldType, 0, len(e.fields)) + for _, field := range e.fields { + fields = append(fields, &field.Column.FieldType) + } + return chunk.NewChunkWithCapacity(fields, 1) +} + +// Close implements the sqlexec.RecordSet Close interface. +func (e *analyzeColumnsExec) Close() error { + return nil +} diff --git a/store/mockstore/mockcopr/checksum.go b/pkg/store/mockstore/mockcopr/checksum.go similarity index 100% rename from store/mockstore/mockcopr/checksum.go rename to pkg/store/mockstore/mockcopr/checksum.go diff --git a/store/mockstore/mockcopr/cop_handler_dag.go b/pkg/store/mockstore/mockcopr/cop_handler_dag.go similarity index 97% rename from store/mockstore/mockcopr/cop_handler_dag.go rename to pkg/store/mockstore/mockcopr/cop_handler_dag.go index 180dc281fa2cf..ce459c4b44bb0 100644 --- a/store/mockstore/mockcopr/cop_handler_dag.go +++ b/pkg/store/mockstore/mockcopr/cop_handler_dag.go @@ -25,21 +25,21 @@ import ( "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/errorpb" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/testutils" "google.golang.org/grpc" diff --git a/store/mockstore/mockcopr/copr_handler.go b/pkg/store/mockstore/mockcopr/copr_handler.go similarity index 98% rename from store/mockstore/mockcopr/copr_handler.go rename to pkg/store/mockstore/mockcopr/copr_handler.go index f3c5d19b9d18c..600e454506595 100644 --- a/store/mockstore/mockcopr/copr_handler.go +++ b/pkg/store/mockstore/mockcopr/copr_handler.go @@ -19,7 +19,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/testutils" ) diff --git a/pkg/store/mockstore/mockcopr/executor.go b/pkg/store/mockstore/mockcopr/executor.go new file mode 100644 index 0000000000000..9b86ca5bda76a --- /dev/null +++ b/pkg/store/mockstore/mockcopr/executor.go @@ -0,0 +1,675 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package mockcopr + +import ( + "bytes" + "context" + "sort" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tipb/go-tipb" + "github.com/tikv/client-go/v2/testutils" +) + +var ( + _ executor = &tableScanExec{} + _ executor = &indexScanExec{} + _ executor = &selectionExec{} + _ executor = &limitExec{} + _ executor = &topNExec{} +) + +type execDetail struct { + timeProcessed time.Duration + numProducedRows int + numIterations int +} + +func (e *execDetail) update(begin time.Time, row [][]byte) { + e.timeProcessed += time.Since(begin) + e.numIterations++ + if row != nil { + e.numProducedRows++ + } +} + +type executor interface { + SetSrcExec(executor) + GetSrcExec() executor + ResetCounts() + Counts() []int64 + Next(ctx context.Context) ([][]byte, error) + // ExecDetails returns its and its children's execution details. + // The order is same as DAGRequest.Executors, which children are in front of parents. + ExecDetails() []*execDetail +} + +type tableScanExec struct { + *tipb.TableScan + colIDs map[int64]int + kvRanges []kv.KeyRange + startTS uint64 + isolationLevel kvrpcpb.IsolationLevel + resolvedLocks []uint64 + mvccStore testutils.MVCCStore + cursor int + seekKey []byte + start int + counts []int64 + execDetail *execDetail + rd *rowcodec.BytesDecoder + + src executor +} + +func (e *tableScanExec) ExecDetails() []*execDetail { + var suffix []*execDetail + if e.src != nil { + suffix = e.src.ExecDetails() + } + return append(suffix, e.execDetail) +} + +func (e *tableScanExec) SetSrcExec(exec executor) { + e.src = exec +} + +func (e *tableScanExec) GetSrcExec() executor { + return e.src +} + +func (e *tableScanExec) ResetCounts() { + if e.counts != nil { + e.start = e.cursor + e.counts[e.start] = 0 + } +} + +func (e *tableScanExec) Counts() []int64 { + if e.counts == nil { + return nil + } + if e.seekKey == nil { + return e.counts[e.start:e.cursor] + } + return e.counts[e.start : e.cursor+1] +} + +func (e *tableScanExec) Next(ctx context.Context) (value [][]byte, err error) { + defer func(begin time.Time) { + e.execDetail.update(begin, value) + }(time.Now()) + for e.cursor < len(e.kvRanges) { + ran := e.kvRanges[e.cursor] + if ran.IsPoint() { + value, err = e.getRowFromPoint(ran) + if err != nil { + return nil, errors.Trace(err) + } + e.cursor++ + if value == nil { + continue + } + if e.counts != nil { + e.counts[e.cursor-1]++ + } + return value, nil + } + value, err = e.getRowFromRange(ran) + if err != nil { + return nil, errors.Trace(err) + } + if value == nil { + e.seekKey = nil + e.cursor++ + continue + } + if e.counts != nil { + e.counts[e.cursor]++ + } + return value, nil + } + + return nil, nil +} + +func (e *tableScanExec) getRowFromPoint(ran kv.KeyRange) ([][]byte, error) { + val, err := e.mvccStore.Get(ran.StartKey, e.startTS, e.isolationLevel, e.resolvedLocks) + if err != nil { + return nil, errors.Trace(err) + } + if len(val) == 0 { + return nil, nil + } + handle, err := tablecodec.DecodeRowKey(ran.StartKey) + if err != nil { + return nil, errors.Trace(err) + } + row, err := getRowData(e.Columns, e.colIDs, handle.IntValue(), val, e.rd) + if err != nil { + return nil, errors.Trace(err) + } + return row, nil +} + +func (e *tableScanExec) getRowFromRange(ran kv.KeyRange) ([][]byte, error) { + if e.seekKey == nil { + if e.Desc { + e.seekKey = ran.EndKey + } else { + e.seekKey = ran.StartKey + } + } + var pairs []testutils.MVCCPair + var pair testutils.MVCCPair + if e.Desc { + pairs = e.mvccStore.ReverseScan(ran.StartKey, e.seekKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) + } else { + pairs = e.mvccStore.Scan(e.seekKey, ran.EndKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) + } + if len(pairs) > 0 { + pair = pairs[0] + } + if pair.Err != nil { + // TODO: Handle lock error. + return nil, errors.Trace(pair.Err) + } + if pair.Key == nil { + return nil, nil + } + if e.Desc { + if bytes.Compare(pair.Key, ran.StartKey) < 0 { + return nil, nil + } + e.seekKey = tablecodec.TruncateToRowKeyLen(pair.Key) + } else { + if bytes.Compare(pair.Key, ran.EndKey) >= 0 { + return nil, nil + } + e.seekKey = kv.Key(pair.Key).PrefixNext() + } + + handle, err := tablecodec.DecodeRowKey(pair.Key) + if err != nil { + return nil, errors.Trace(err) + } + row, err := getRowData(e.Columns, e.colIDs, handle.IntValue(), pair.Value, e.rd) + if err != nil { + return nil, errors.Trace(err) + } + return row, nil +} + +type indexScanExec struct { + *tipb.IndexScan + colsLen int + kvRanges []kv.KeyRange + startTS uint64 + isolationLevel kvrpcpb.IsolationLevel + resolvedLocks []uint64 + mvccStore testutils.MVCCStore + cursor int + seekKey []byte + hdStatus tablecodec.HandleStatus + start int + counts []int64 + execDetail *execDetail + colInfos []rowcodec.ColInfo + + src executor +} + +func (e *indexScanExec) ExecDetails() []*execDetail { + var suffix []*execDetail + if e.src != nil { + suffix = e.src.ExecDetails() + } + return append(suffix, e.execDetail) +} + +func (e *indexScanExec) SetSrcExec(exec executor) { + e.src = exec +} + +func (e *indexScanExec) GetSrcExec() executor { + return e.src +} + +func (e *indexScanExec) ResetCounts() { + if e.counts != nil { + e.start = e.cursor + e.counts[e.start] = 0 + } +} + +func (e *indexScanExec) Counts() []int64 { + if e.counts == nil { + return nil + } + if e.seekKey == nil { + return e.counts[e.start:e.cursor] + } + return e.counts[e.start : e.cursor+1] +} + +func (e *indexScanExec) isUnique() bool { + return e.Unique != nil && *e.Unique +} + +func (e *indexScanExec) Next(ctx context.Context) (value [][]byte, err error) { + defer func(begin time.Time) { + e.execDetail.update(begin, value) + }(time.Now()) + for e.cursor < len(e.kvRanges) { + ran := e.kvRanges[e.cursor] + if ran.IsPoint() && e.isUnique() { + value, err = e.getRowFromPoint(ran) + if err != nil { + return nil, errors.Trace(err) + } + e.cursor++ + if value == nil { + continue + } + if e.counts != nil { + e.counts[e.cursor-1]++ + } + } else { + value, err = e.getRowFromRange(ran) + if err != nil { + return nil, errors.Trace(err) + } + if value == nil { + e.cursor++ + e.seekKey = nil + continue + } + if e.counts != nil { + e.counts[e.cursor]++ + } + } + return value, nil + } + + return nil, nil +} + +// getRowFromPoint is only used for unique key. +func (e *indexScanExec) getRowFromPoint(ran kv.KeyRange) ([][]byte, error) { + val, err := e.mvccStore.Get(ran.StartKey, e.startTS, e.isolationLevel, e.resolvedLocks) + if err != nil { + return nil, errors.Trace(err) + } + if len(val) == 0 { + return nil, nil + } + return tablecodec.DecodeIndexKV(ran.StartKey, val, e.colsLen, e.hdStatus, e.colInfos) +} + +func (e *indexScanExec) getRowFromRange(ran kv.KeyRange) ([][]byte, error) { + if e.seekKey == nil { + if e.Desc { + e.seekKey = ran.EndKey + } else { + e.seekKey = ran.StartKey + } + } + var pairs []testutils.MVCCPair + var pair testutils.MVCCPair + if e.Desc { + pairs = e.mvccStore.ReverseScan(ran.StartKey, e.seekKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) + } else { + pairs = e.mvccStore.Scan(e.seekKey, ran.EndKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) + } + if len(pairs) > 0 { + pair = pairs[0] + } + if pair.Err != nil { + // TODO: Handle lock error. + return nil, errors.Trace(pair.Err) + } + if pair.Key == nil { + return nil, nil + } + if e.Desc { + if bytes.Compare(pair.Key, ran.StartKey) < 0 { + return nil, nil + } + e.seekKey = pair.Key + } else { + if bytes.Compare(pair.Key, ran.EndKey) >= 0 { + return nil, nil + } + e.seekKey = kv.Key(pair.Key).PrefixNext() + } + return tablecodec.DecodeIndexKV(pair.Key, pair.Value, e.colsLen, e.hdStatus, e.colInfos) +} + +type selectionExec struct { + conditions []expression.Expression + relatedColOffsets []int + row []types.Datum + evalCtx *evalContext + src executor + execDetail *execDetail +} + +func (e *selectionExec) ExecDetails() []*execDetail { + var suffix []*execDetail + if e.src != nil { + suffix = e.src.ExecDetails() + } + return append(suffix, e.execDetail) +} + +func (e *selectionExec) SetSrcExec(exec executor) { + e.src = exec +} + +func (e *selectionExec) GetSrcExec() executor { + return e.src +} + +func (e *selectionExec) ResetCounts() { + e.src.ResetCounts() +} + +func (e *selectionExec) Counts() []int64 { + return e.src.Counts() +} + +// evalBool evaluates expression to a boolean value. +func evalBool(exprs []expression.Expression, row []types.Datum, ctx *stmtctx.StatementContext) (bool, error) { + for _, expr := range exprs { + data, err := expr.Eval(chunk.MutRowFromDatums(row).ToRow()) + if err != nil { + return false, errors.Trace(err) + } + if data.IsNull() { + return false, nil + } + + isBool, err := data.ToBool(ctx) + isBool, err = expression.HandleOverflowOnSelection(ctx, isBool, err) + if err != nil { + return false, errors.Trace(err) + } + if isBool == 0 { + return false, nil + } + } + return true, nil +} + +func (e *selectionExec) Next(ctx context.Context) (value [][]byte, err error) { + defer func(begin time.Time) { + e.execDetail.update(begin, value) + }(time.Now()) + for { + value, err = e.src.Next(ctx) + if err != nil { + return nil, errors.Trace(err) + } + if value == nil { + return nil, nil + } + + err = e.evalCtx.decodeRelatedColumnVals(e.relatedColOffsets, value, e.row) + if err != nil { + return nil, errors.Trace(err) + } + match, err := evalBool(e.conditions, e.row, e.evalCtx.sc) + if err != nil { + return nil, errors.Trace(err) + } + if match { + return value, nil + } + } +} + +type topNExec struct { + heap *topNHeap + evalCtx *evalContext + relatedColOffsets []int + orderByExprs []expression.Expression + row []types.Datum + cursor int + executed bool + execDetail *execDetail + + src executor +} + +func (e *topNExec) ExecDetails() []*execDetail { + var suffix []*execDetail + if e.src != nil { + suffix = e.src.ExecDetails() + } + return append(suffix, e.execDetail) +} + +func (e *topNExec) SetSrcExec(src executor) { + e.src = src +} + +func (e *topNExec) GetSrcExec() executor { + return e.src +} + +func (e *topNExec) ResetCounts() { + e.src.ResetCounts() +} + +func (e *topNExec) Counts() []int64 { + return e.src.Counts() +} + +func (e *topNExec) innerNext(ctx context.Context) (bool, error) { + value, err := e.src.Next(ctx) + if err != nil { + return false, errors.Trace(err) + } + if value == nil { + return false, nil + } + err = e.evalTopN(value) + if err != nil { + return false, errors.Trace(err) + } + return true, nil +} + +func (e *topNExec) Next(ctx context.Context) (value [][]byte, err error) { + defer func(begin time.Time) { + e.execDetail.update(begin, value) + }(time.Now()) + if !e.executed { + for { + hasMore, err := e.innerNext(ctx) + if err != nil { + return nil, errors.Trace(err) + } + if !hasMore { + sort.Sort(&e.heap.topNSorter) + break + } + } + e.executed = true + } + if e.cursor >= len(e.heap.rows) { + return nil, nil + } + row := e.heap.rows[e.cursor] + e.cursor++ + + return row.data, nil +} + +// evalTopN evaluates the top n elements from the data. The input receives a record including its handle and data. +// And this function will check if this record can replace one of the old records. +func (e *topNExec) evalTopN(value [][]byte) error { + newRow := &sortRow{ + key: make([]types.Datum, len(e.orderByExprs)), + } + err := e.evalCtx.decodeRelatedColumnVals(e.relatedColOffsets, value, e.row) + if err != nil { + return errors.Trace(err) + } + for i, expr := range e.orderByExprs { + newRow.key[i], err = expr.Eval(chunk.MutRowFromDatums(e.row).ToRow()) + if err != nil { + return errors.Trace(err) + } + } + + if e.heap.tryToAddRow(newRow) { + newRow.data = append(newRow.data, value...) + } + return errors.Trace(e.heap.err) +} + +type limitExec struct { + limit uint64 + cursor uint64 + + src executor + + execDetail *execDetail +} + +func (e *limitExec) ExecDetails() []*execDetail { + var suffix []*execDetail + if e.src != nil { + suffix = e.src.ExecDetails() + } + return append(suffix, e.execDetail) +} + +func (e *limitExec) SetSrcExec(src executor) { + e.src = src +} + +func (e *limitExec) GetSrcExec() executor { + return e.src +} + +func (e *limitExec) ResetCounts() { + e.src.ResetCounts() +} + +func (e *limitExec) Counts() []int64 { + return e.src.Counts() +} + +func (e *limitExec) Next(ctx context.Context) (value [][]byte, err error) { + defer func(begin time.Time) { + e.execDetail.update(begin, value) + }(time.Now()) + if e.cursor >= e.limit { + return nil, nil + } + + value, err = e.src.Next(ctx) + if err != nil { + return nil, errors.Trace(err) + } + if value == nil { + return nil, nil + } + e.cursor++ + return value, nil +} + +func hasColVal(data [][]byte, colIDs map[int64]int, id int64) bool { + offset, ok := colIDs[id] + if ok && data[offset] != nil { + return true + } + return false +} + +// getRowData decodes raw byte slice to row data. +func getRowData(columns []*tipb.ColumnInfo, colIDs map[int64]int, handle int64, value []byte, rd *rowcodec.BytesDecoder) ([][]byte, error) { + if rowcodec.IsNewFormat(value) { + return rd.DecodeToBytes(colIDs, kv.IntHandle(handle), value, nil) + } + values, err := tablecodec.CutRowNew(value, colIDs) + if err != nil { + return nil, errors.Trace(err) + } + if values == nil { + values = make([][]byte, len(colIDs)) + } + // Fill the handle and null columns. + for _, col := range columns { + id := col.GetColumnId() + offset := colIDs[id] + if col.GetPkHandle() || id == model.ExtraHandleID { + var handleDatum types.Datum + if mysql.HasUnsignedFlag(uint(col.GetFlag())) { + // PK column is Unsigned. + handleDatum = types.NewUintDatum(uint64(handle)) + } else { + handleDatum = types.NewIntDatum(handle) + } + handleData, err1 := codec.EncodeValue(nil, nil, handleDatum) + if err1 != nil { + return nil, errors.Trace(err1) + } + values[offset] = handleData + continue + } + if hasColVal(values, colIDs, id) { + continue + } + if len(col.DefaultVal) > 0 { + values[offset] = col.DefaultVal + continue + } + if mysql.HasNotNullFlag(uint(col.GetFlag())) { + return nil, errors.Errorf("Miss column %d", id) + } + + values[offset] = []byte{codec.NilFlag} + } + + return values, nil +} + +func convertToExprs(sc *stmtctx.StatementContext, fieldTps []*types.FieldType, pbExprs []*tipb.Expr) ([]expression.Expression, error) { + exprs := make([]expression.Expression, 0, len(pbExprs)) + for _, expr := range pbExprs { + e, err := expression.PBToExpr(expr, fieldTps, sc) + if err != nil { + return nil, errors.Trace(err) + } + exprs = append(exprs, e) + } + return exprs, nil +} diff --git a/pkg/store/mockstore/mockcopr/executor_test.go b/pkg/store/mockstore/mockcopr/executor_test.go new file mode 100644 index 0000000000000..d3b201072e61d --- /dev/null +++ b/pkg/store/mockstore/mockcopr/executor_test.go @@ -0,0 +1,139 @@ +// Copyright 2019-present, PingCAP, Inc. +// +// 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. + +package mockcopr_test + +import ( + "context" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore/mockcopr" + "github.com/pingcap/tidb/pkg/store/mockstore/mockstorage" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/testutils" + "github.com/tikv/client-go/v2/tikv" +) + +// This test checks the resolve lock functionality. When a txn meets the lock of a large transaction, +// it should not block by the lock. +func TestResolvedLargeTxnLocks(t *testing.T) { + // This is required since mock tikv does not support paging. + failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging", `return`) + defer func() { + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/DisablePaging")) + }() + + rpcClient, cluster, pdClient, err := testutils.NewMockTiKV("", mockcopr.NewCoprRPCHandler()) + require.NoError(t, err) + + testutils.BootstrapWithSingleStore(cluster) + mvccStore := rpcClient.MvccStore + tikvStore, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) + require.NoError(t, err) + + store, err := mockstorage.NewMockStorage(tikvStore) + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + session.SetSchemaLease(0) + session.DisableStats4Test() + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + defer dom.Close() + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (id int primary key, val int)") + dom = domain.GetDomain(tk.Session()) + schema := dom.InfoSchema() + tbl, err := schema.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + tk.MustExec("insert into t values (1, 1)") + + o := store.GetOracle() + tso, err := o.GetTimestamp(context.Background(), &oracle.Option{TxnScope: kv.GlobalTxnScope}) + require.NoError(t, err) + + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + pairs := mvccStore.Scan(key, nil, 1, tso, kvrpcpb.IsolationLevel_SI, nil) + require.Len(t, pairs, 1) + require.Nil(t, pairs[0].Err) + + // Simulate a large txn (holding a pk lock with large TTL). + // Secondary lock 200ms, primary lock 100s + require.True(t, prewriteMVCCStore(mvccStore, putMutations("primary", "value"), "primary", tso, 100000)) + require.True(t, prewriteMVCCStore(mvccStore, putMutations(string(key), "value"), "primary", tso, 200)) + + // Simulate the action of reading meet the lock of a large txn. + // The lock of the large transaction should not block read. + // The first time, this query should meet a lock on the secondary key, then resolve lock. + // After that, the query should read the previous version data. + tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) + + // Cover BatchGet. + tk.MustQuery("select * from t where id in (1)").Check(testkit.Rows("1 1")) + + // Cover PointGet. + tk.MustExec("begin") + tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 1")) + tk.MustExec("rollback") + + // And check the large txn is still alive. + pairs = mvccStore.Scan([]byte("primary"), nil, 1, tso, kvrpcpb.IsolationLevel_SI, nil) + require.Len(t, pairs, 1) + _, ok := errors.Cause(pairs[0].Err).(*testutils.ErrLocked) + require.True(t, ok) +} + +func putMutations(kvpairs ...string) []*kvrpcpb.Mutation { + var mutations []*kvrpcpb.Mutation + for i := 0; i < len(kvpairs); i += 2 { + mutations = append(mutations, &kvrpcpb.Mutation{ + Op: kvrpcpb.Op_Put, + Key: []byte(kvpairs[i]), + Value: []byte(kvpairs[i+1]), + }) + } + return mutations +} + +func prewriteMVCCStore(store testutils.MVCCStore, mutations []*kvrpcpb.Mutation, primary string, startTS uint64, ttl uint64) bool { + req := &kvrpcpb.PrewriteRequest{ + Mutations: mutations, + PrimaryLock: []byte(primary), + StartVersion: startTS, + LockTtl: ttl, + MinCommitTs: startTS + 1, + } + errs := store.Prewrite(req) + for _, err := range errs { + if err != nil { + return false + } + } + return true +} diff --git a/pkg/store/mockstore/mockcopr/main_test.go b/pkg/store/mockstore/mockcopr/main_test.go new file mode 100644 index 0000000000000..8926b86bc1dd1 --- /dev/null +++ b/pkg/store/mockstore/mockcopr/main_test.go @@ -0,0 +1,40 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mockcopr + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + callback := func(i int) int { + // wait for leveldb to close, leveldb will be closed in one second + time.Sleep(time.Second) + return i + } + goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) +} diff --git a/store/mockstore/mockcopr/rpc_copr.go b/pkg/store/mockstore/mockcopr/rpc_copr.go similarity index 98% rename from store/mockstore/mockcopr/rpc_copr.go rename to pkg/store/mockstore/mockcopr/rpc_copr.go index 84ddd951d2040..059dc074da6e1 100644 --- a/store/mockstore/mockcopr/rpc_copr.go +++ b/pkg/store/mockstore/mockcopr/rpc_copr.go @@ -22,7 +22,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/tikv/client-go/v2/testutils" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/pkg/store/mockstore/mockcopr/topn.go b/pkg/store/mockstore/mockcopr/topn.go new file mode 100644 index 0000000000000..083d90d2a3876 --- /dev/null +++ b/pkg/store/mockstore/mockcopr/topn.go @@ -0,0 +1,141 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package mockcopr + +import ( + "container/heap" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tipb/go-tipb" +) + +type sortRow struct { + key []types.Datum + data [][]byte +} + +// topNSorter implements sort.Interface. When all rows have been processed, the topNSorter will sort the whole data in heap. +type topNSorter struct { + orderByItems []*tipb.ByItem + rows []*sortRow + err error + sc *stmtctx.StatementContext +} + +func (t *topNSorter) Len() int { + return len(t.rows) +} + +func (t *topNSorter) Swap(i, j int) { + t.rows[i], t.rows[j] = t.rows[j], t.rows[i] +} + +func (t *topNSorter) Less(i, j int) bool { + for index, by := range t.orderByItems { + v1 := t.rows[i].key[index] + v2 := t.rows[j].key[index] + + ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) + if err != nil { + t.err = errors.Trace(err) + return true + } + + if by.Desc { + ret = -ret + } + + if ret < 0 { + return true + } else if ret > 0 { + return false + } + } + + return false +} + +// topNHeap holds the top n elements using heap structure. It implements heap.Interface. +// When we insert a row, topNHeap will check if the row can become one of the top n element or not. +type topNHeap struct { + topNSorter + + // totalCount is equal to the limit count, which means the max size of heap. + totalCount int + // heapSize means the current size of this heap. + heapSize int +} + +func (t *topNHeap) Len() int { + return t.heapSize +} + +func (t *topNHeap) Push(x interface{}) { + t.rows = append(t.rows, x.(*sortRow)) + t.heapSize++ +} + +func (t *topNHeap) Pop() interface{} { + return nil +} + +func (t *topNHeap) Less(i, j int) bool { + for index, by := range t.orderByItems { + v1 := t.rows[i].key[index] + v2 := t.rows[j].key[index] + + ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) + if err != nil { + t.err = errors.Trace(err) + return true + } + + if by.Desc { + ret = -ret + } + + if ret > 0 { + return true + } else if ret < 0 { + return false + } + } + + return false +} + +// tryToAddRow tries to add a row to heap. +// When this row is not less than any rows in heap, it will never become the top n element. +// Then this function returns false. +func (t *topNHeap) tryToAddRow(row *sortRow) bool { + success := false + if t.heapSize == t.totalCount { + t.rows = append(t.rows, row) + // When this row is less than the top element, it will replace it and adjust the heap structure. + if t.Less(0, t.heapSize) { + t.Swap(0, t.heapSize) + heap.Fix(t, 0) + success = true + } + t.rows = t.rows[:t.heapSize] + } else { + heap.Push(t, row) + success = true + } + return success +} diff --git a/pkg/store/mockstore/mockstorage/BUILD.bazel b/pkg/store/mockstore/mockstorage/BUILD.bazel new file mode 100644 index 0000000000000..18b59519db24c --- /dev/null +++ b/pkg/store/mockstore/mockstorage/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mockstorage", + srcs = ["storage.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/mockstorage", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/store/copr", + "//pkg/store/driver/txn", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_client_go_v2//tikv", + ], +) diff --git a/pkg/store/mockstore/mockstorage/storage.go b/pkg/store/mockstore/mockstorage/storage.go new file mode 100644 index 0000000000000..b0f39578e098c --- /dev/null +++ b/pkg/store/mockstore/mockstorage/storage.go @@ -0,0 +1,139 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mockstorage + +import ( + "context" + "crypto/tls" + + deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/copr" + driver "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/tikv/client-go/v2/config" + "github.com/tikv/client-go/v2/tikv" +) + +// Wraps tikv.KVStore and make it compatible with kv.Storage. +type mockStorage struct { + *tikv.KVStore + *copr.Store + memCache kv.MemManager + LockWaits []*deadlockpb.WaitForEntry +} + +// NewMockStorage wraps tikv.KVStore as kv.Storage. +func NewMockStorage(tikvStore *tikv.KVStore) (kv.Storage, error) { + coprConfig := config.DefaultConfig().TiKVClient.CoprCache + coprStore, err := copr.NewStore(tikvStore, &coprConfig) + if err != nil { + return nil, err + } + return &mockStorage{ + KVStore: tikvStore, + Store: coprStore, + memCache: kv.NewCacheDB(), + }, nil +} + +func (s *mockStorage) EtcdAddrs() ([]string, error) { + return nil, nil +} + +func (s *mockStorage) TLSConfig() *tls.Config { + return nil +} + +// GetMemCache return memory mamager of the storage +func (s *mockStorage) GetMemCache() kv.MemManager { + return s.memCache +} + +func (s *mockStorage) StartGCWorker() error { + return nil +} + +func (s *mockStorage) Name() string { + return "mock-storage" +} + +func (s *mockStorage) Describe() string { + return "" +} + +// Begin a global transaction. +func (s *mockStorage) Begin(opts ...tikv.TxnOption) (kv.Transaction, error) { + txn, err := s.KVStore.Begin(opts...) + return newTiKVTxn(txn, err) +} + +// ShowStatus returns the specified status of the storage +func (s *mockStorage) ShowStatus(ctx context.Context, key string) (interface{}, error) { + return nil, kv.ErrNotImplemented +} + +// GetSnapshot gets a snapshot that is able to read any data which data is <= ver. +// if ver is MaxVersion or > current max committed version, we will use current version for this snapshot. +func (s *mockStorage) GetSnapshot(ver kv.Version) kv.Snapshot { + return driver.NewSnapshot(s.KVStore.GetSnapshot(ver.Ver)) +} + +// CurrentVersion returns current max committed version with the given txnScope (local or global). +func (s *mockStorage) CurrentVersion(txnScope string) (kv.Version, error) { + ver, err := s.KVStore.CurrentTimestamp(txnScope) + return kv.NewVersion(ver), err +} + +// GetMinSafeTS return the minimal SafeTS of the storage with given txnScope. +func (s *mockStorage) GetMinSafeTS(txnScope string) uint64 { + return 0 +} + +func newTiKVTxn(txn *tikv.KVTxn, err error) (kv.Transaction, error) { + if err != nil { + return nil, err + } + return driver.NewTiKVTxn(txn), nil +} + +func (s *mockStorage) GetLockWaits() ([]*deadlockpb.WaitForEntry, error) { + return s.LockWaits, nil +} + +func (s *mockStorage) Close() error { + select { + case <-s.KVStore.Closed(): + return nil + default: + s.Store.Close() + return s.KVStore.Close() + } +} + +func (s *mockStorage) GetCodec() tikv.Codec { + pdClient := s.KVStore.GetPDClient() + pdCodecCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) + return pdCodecCli.GetCodec() +} + +// MockLockWaitSetter is used to set the mocked lock wait information, which helps implementing tests that uses the +// GetLockWaits function. +type MockLockWaitSetter interface { + SetMockLockWaits(lockWaits []*deadlockpb.WaitForEntry) +} + +func (s *mockStorage) SetMockLockWaits(lockWaits []*deadlockpb.WaitForEntry) { + s.LockWaits = lockWaits +} diff --git a/pkg/store/mockstore/mockstore.go b/pkg/store/mockstore/mockstore.go new file mode 100644 index 0000000000000..4d2bed7ce0bc7 --- /dev/null +++ b/pkg/store/mockstore/mockstore.go @@ -0,0 +1,236 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package mockstore + +import ( + "net/url" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/testkit/testenv" + "github.com/tikv/client-go/v2/testutils" + "github.com/tikv/client-go/v2/tikv" + pd "github.com/tikv/pd/client" +) + +// MockTiKVDriver is in memory mock TiKV driver. +type MockTiKVDriver struct{} + +// Open creates a MockTiKV storage. +func (d MockTiKVDriver) Open(path string) (kv.Storage, error) { + u, err := url.Parse(path) + if err != nil { + return nil, errors.Trace(err) + } + if !strings.EqualFold(u.Scheme, "mocktikv") { + return nil, errors.Errorf("Uri scheme expected(mocktikv) but found (%s)", u.Scheme) + } + + opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(MockTiKV)} + txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches + if txnLocalLatches.Enabled { + opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) + } + + return NewMockStore(opts...) +} + +// EmbedUnistoreDriver is in embedded unistore driver. +type EmbedUnistoreDriver struct{} + +// Open creates a EmbedUnistore storage. +func (d EmbedUnistoreDriver) Open(path string) (kv.Storage, error) { + u, err := url.Parse(path) + if err != nil { + return nil, errors.Trace(err) + } + if !strings.EqualFold(u.Scheme, "unistore") { + return nil, errors.Errorf("Uri scheme expected(unistore) but found (%s)", u.Scheme) + } + + opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(EmbedUnistore)} + txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches + if txnLocalLatches.Enabled { + opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) + } + + return NewMockStore(opts...) +} + +// StoreType is the type of backend mock storage. +type StoreType uint8 + +const ( + // MockTiKV is the mock storage based on goleveldb. + MockTiKV StoreType = iota + // EmbedUnistore is the mock storage based on unistore. + EmbedUnistore + + defaultStoreType = EmbedUnistore +) + +type mockOptions struct { + clusterInspector func(testutils.Cluster) + clientHijacker func(tikv.Client) tikv.Client + pdClientHijacker func(pd.Client) pd.Client + path string + txnLocalLatches uint + storeType StoreType + ddlCheckerHijack bool +} + +// MockTiKVStoreOption is used to control some behavior of mock tikv. +type MockTiKVStoreOption func(*mockOptions) + +// WithMultipleOptions merges multiple options into one option. +func WithMultipleOptions(opts ...MockTiKVStoreOption) MockTiKVStoreOption { + return func(args *mockOptions) { + for _, opt := range opts { + opt(args) + } + } +} + +// WithClientHijacker hijacks KV client's behavior, makes it easy to simulate the network +// problem between TiDB and TiKV. +func WithClientHijacker(hijacker func(tikv.Client) tikv.Client) MockTiKVStoreOption { + return func(c *mockOptions) { + c.clientHijacker = hijacker + } +} + +// WithPDClientHijacker hijacks PD client's behavior, makes it easy to simulate the network +// problem between TiDB and PD. +func WithPDClientHijacker(hijacker func(pd.Client) pd.Client) MockTiKVStoreOption { + return func(c *mockOptions) { + c.pdClientHijacker = hijacker + } +} + +// WithClusterInspector lets user to inspect the mock cluster handler. +func WithClusterInspector(inspector func(testutils.Cluster)) MockTiKVStoreOption { + return func(c *mockOptions) { + c.clusterInspector = inspector + } +} + +// WithStoreType lets user choose the backend storage's type. +func WithStoreType(tp StoreType) MockTiKVStoreOption { + return func(c *mockOptions) { + c.storeType = tp + } +} + +// WithPath specifies the mocktikv path. +func WithPath(path string) MockTiKVStoreOption { + return func(c *mockOptions) { + c.path = path + } +} + +// WithTxnLocalLatches enable txnLocalLatches, when capacity > 0. +func WithTxnLocalLatches(capacity uint) MockTiKVStoreOption { + return func(c *mockOptions) { + c.txnLocalLatches = capacity + } +} + +// WithDDLChecker prepare injected DDL implementation for the domain of this store. It must be done before bootstrap to +// avoid data race with dom.ddl. +func WithDDLChecker() MockTiKVStoreOption { + return func(c *mockOptions) { + c.ddlCheckerHijack = true + } +} + +// DDLCheckerInjector is used to break import cycle. +var DDLCheckerInjector func(kv.Storage) kv.Storage + +// NewMockStore creates a mocked tikv store, the path is the file path to store the data. +// If path is an empty string, a memory storage will be created. +func NewMockStore(options ...MockTiKVStoreOption) (kv.Storage, error) { + testenv.SetGOMAXPROCSForTest() + opt := mockOptions{ + clusterInspector: func(c testutils.Cluster) { + BootstrapWithSingleStore(c) + }, + storeType: defaultStoreType, + } + for _, f := range options { + f(&opt) + } + + var ( + store kv.Storage + err error + ) + + switch opt.storeType { + case MockTiKV: + store, err = newMockTikvStore(&opt) + case EmbedUnistore: + store, err = newUnistore(&opt) + default: + panic("unsupported mockstore") + } + if err != nil { + return nil, errors.Trace(err) + } + + if opt.ddlCheckerHijack { + store = DDLCheckerInjector(store) + } + return store, nil +} + +// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. +func BootstrapWithSingleStore(cluster testutils.Cluster) (storeID, peerID, regionID uint64) { + switch x := cluster.(type) { + case *testutils.MockCluster: + return testutils.BootstrapWithSingleStore(x) + case *unistore.Cluster: + return unistore.BootstrapWithSingleStore(x) + default: + panic("unsupported cluster type") + } +} + +// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. +func BootstrapWithMultiStores(cluster testutils.Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { + switch x := cluster.(type) { + case *testutils.MockCluster: + return testutils.BootstrapWithMultiStores(x, n) + case *unistore.Cluster: + return unistore.BootstrapWithMultiStores(x, n) + default: + panic("unsupported cluster type") + } +} + +// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 +// Store. The number of Regions will be len(splitKeys) + 1. +func BootstrapWithMultiRegions(cluster testutils.Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { + switch x := cluster.(type) { + case *testutils.MockCluster: + return testutils.BootstrapWithMultiRegions(x, splitKeys...) + case *unistore.Cluster: + return unistore.BootstrapWithMultiRegions(x, splitKeys...) + default: + panic("unsupported cluster type") + } +} diff --git a/store/mockstore/redirector.go b/pkg/store/mockstore/redirector.go similarity index 98% rename from store/mockstore/redirector.go rename to pkg/store/mockstore/redirector.go index e506f16857bbf..8385d09770838 100644 --- a/store/mockstore/redirector.go +++ b/pkg/store/mockstore/redirector.go @@ -19,7 +19,7 @@ import ( "sync" "time" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/pkg/store/mockstore/tikv.go b/pkg/store/mockstore/tikv.go new file mode 100644 index 0000000000000..d2cfedfe609ba --- /dev/null +++ b/pkg/store/mockstore/tikv.go @@ -0,0 +1,40 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package mockstore + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore/mockcopr" + "github.com/pingcap/tidb/pkg/store/mockstore/mockstorage" + "github.com/tikv/client-go/v2/testutils" + "github.com/tikv/client-go/v2/tikv" +) + +// newMockTikvStore creates a mocked tikv store, the path is the file path to store the data. +// If path is an empty string, a memory storage will be created. +func newMockTikvStore(opt *mockOptions) (kv.Storage, error) { + client, cluster, pdClient, err := testutils.NewMockTiKV(opt.path, mockcopr.NewCoprRPCHandler()) + if err != nil { + return nil, errors.Trace(err) + } + opt.clusterInspector(cluster) + + kvstore, err := tikv.NewTestTiKVStore(newClientRedirector(client), pdClient, opt.clientHijacker, opt.pdClientHijacker, opt.txnLocalLatches) + if err != nil { + return nil, err + } + return mockstorage.NewMockStorage(kvstore) +} diff --git a/store/mockstore/tikv_test.go b/pkg/store/mockstore/tikv_test.go similarity index 97% rename from store/mockstore/tikv_test.go rename to pkg/store/mockstore/tikv_test.go index 31b16c1e89a07..4e936d124d74e 100644 --- a/store/mockstore/tikv_test.go +++ b/pkg/store/mockstore/tikv_test.go @@ -17,7 +17,7 @@ package mockstore import ( "testing" - tidbcfg "github.com/pingcap/tidb/config" + tidbcfg "github.com/pingcap/tidb/pkg/config" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/config" ) diff --git a/store/mockstore/unistore.go b/pkg/store/mockstore/unistore.go similarity index 89% rename from store/mockstore/unistore.go rename to pkg/store/mockstore/unistore.go index 7e04b2a8ae737..d234f7cbe8893 100644 --- a/store/mockstore/unistore.go +++ b/pkg/store/mockstore/unistore.go @@ -16,9 +16,9 @@ package mockstore import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore/mockstorage" - "github.com/pingcap/tidb/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore/mockstorage" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/util" ) diff --git a/pkg/store/mockstore/unistore/BUILD.bazel b/pkg/store/mockstore/unistore/BUILD.bazel new file mode 100644 index 0000000000000..2280ed08334da --- /dev/null +++ b/pkg/store/mockstore/unistore/BUILD.bazel @@ -0,0 +1,64 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "unistore", + srcs = [ + "cluster.go", + "mock.go", + "pd.go", + "raw_handler.go", + "rpc.go", + "testutil.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain/infosync", + "//pkg/parser/terror", + "//pkg/store/mockstore/unistore/config", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/server", + "//pkg/store/mockstore/unistore/tikv", + "//pkg/tablecodec", + "//pkg/util/codec", + "//pkg/util/topsql/state", + "@com_github_golang_protobuf//proto", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/debugpb", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/keyspacepb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/meta_storagepb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_kvproto//pkg/pdpb", + "@com_github_pingcap_kvproto//pkg/resource_manager", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_pd_client//:client", + "@org_golang_google_grpc//metadata", + ], +) + +go_test( + name = "unistore_test", + timeout = "short", + srcs = [ + "main_test.go", + "pd_test.go", + "raw_handler_test.go", + ], + embed = [":unistore"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/mockstore/unistore/client/BUILD.bazel b/pkg/store/mockstore/unistore/client/BUILD.bazel new file mode 100644 index 0000000000000..dded3cfae2fd2 --- /dev/null +++ b/pkg/store/mockstore/unistore/client/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "client", + srcs = ["client.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/client", + visibility = ["//visibility:public"], + deps = ["@com_github_tikv_client_go_v2//tikvrpc"], +) diff --git a/store/mockstore/unistore/client/client.go b/pkg/store/mockstore/unistore/client/client.go similarity index 100% rename from store/mockstore/unistore/client/client.go rename to pkg/store/mockstore/unistore/client/client.go diff --git a/pkg/store/mockstore/unistore/cluster.go b/pkg/store/mockstore/unistore/cluster.go new file mode 100644 index 0000000000000..ed1a15b361595 --- /dev/null +++ b/pkg/store/mockstore/unistore/cluster.go @@ -0,0 +1,151 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package unistore + +import ( + "fmt" + "sync" + "time" + + "github.com/pingcap/kvproto/pkg/metapb" + us "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/tikv/client-go/v2/testutils" +) + +type delayKey struct { + startTS uint64 + regionID uint64 +} + +var _ testutils.Cluster = new(Cluster) + +// Cluster simulates a TiKV cluster. It focuses on management and the change of +// meta data. A Cluster mainly includes following 3 kinds of meta data: +// 1. Region: A Region is a fragment of TiKV's data whose range is [start, end). +// The data of a Region is duplicated to multiple Peers and distributed in +// multiple Stores. +// 2. Peer: A Peer is a replica of a Region's data. All peers of a Region form +// a group, each group elects a Leader to provide services. +// 3. Store: A Store is a storage/service node. Try to think it as a TiKV server +// process. Only the store with request's Region's leader Peer could respond +// to client's request. +type Cluster struct { + *us.MockRegionManager + + // delayEvents is used to control the execution sequence of rpc requests for test. + delayEvents map[delayKey]time.Duration + delayMu sync.Mutex +} + +func newCluster(rm *us.MockRegionManager) *Cluster { + return &Cluster{ + MockRegionManager: rm, + delayEvents: make(map[delayKey]time.Duration), + } +} + +// ScheduleDelay schedules a delay event for a transaction on a region. +func (c *Cluster) ScheduleDelay(startTS, regionID uint64, dur time.Duration) { + c.delayMu.Lock() + c.delayEvents[delayKey{startTS: startTS, regionID: regionID}] = dur + c.delayMu.Unlock() +} + +func (c *Cluster) handleDelay(startTS, regionID uint64) { + key := delayKey{startTS: startTS, regionID: regionID} + c.delayMu.Lock() + dur, ok := c.delayEvents[key] + if ok { + delete(c.delayEvents, key) + } + c.delayMu.Unlock() + if ok { + time.Sleep(dur) + } +} + +// SplitRaw splits region for raw KV. +func (c *Cluster) SplitRaw(regionID, newRegionID uint64, rawKey []byte, peerIDs []uint64, leaderPeerID uint64) *metapb.Region { + encodedKey := codec.EncodeBytes(nil, rawKey) + return c.MockRegionManager.SplitRaw(regionID, newRegionID, encodedKey, peerIDs, leaderPeerID) +} + +// SplitKeys evenly splits the start, end key into "count" regions. +func (c *Cluster) SplitKeys(start, end []byte, count int) { + c.MockRegionManager.SplitKeys(start, end, count) +} + +// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. +func BootstrapWithSingleStore(cluster *Cluster) (storeID, peerID, regionID uint64) { + storeID, regionID, peerID = cluster.AllocID(), cluster.AllocID(), cluster.AllocID() + store := &metapb.Store{ + Id: storeID, + Address: fmt.Sprintf("store%d", storeID), + } + region := &metapb.Region{ + Id: regionID, + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []*metapb.Peer{{Id: peerID, StoreId: storeID}}, + } + if err := cluster.Bootstrap([]*metapb.Store{store}, region); err != nil { + panic(err) + } + return +} + +// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. +func BootstrapWithMultiStores(cluster *Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { + storeIDs = cluster.AllocIDs(n) + peerIDs = cluster.AllocIDs(n) + leaderPeer = peerIDs[0] + regionID = cluster.AllocID() + stores := make([]*metapb.Store, n) + for i, storeID := range storeIDs { + stores[i] = &metapb.Store{ + Id: storeID, + Address: fmt.Sprintf("store%d", storeID), + } + } + peers := make([]*metapb.Peer, n) + for i, peerID := range peerIDs { + peers[i] = &metapb.Peer{ + Id: peerID, + StoreId: storeIDs[i], + } + } + region := &metapb.Region{ + Id: regionID, + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: peers, + } + if err := cluster.Bootstrap(stores, region); err != nil { + panic(err) + } + return +} + +// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 +// Store. The number of Regions will be len(splitKeys) + 1. +func BootstrapWithMultiRegions(cluster *Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { + var firstRegionID, firstPeerID uint64 + storeID, firstPeerID, firstRegionID = BootstrapWithSingleStore(cluster) + regionIDs = append([]uint64{firstRegionID}, cluster.AllocIDs(len(splitKeys))...) + peerIDs = append([]uint64{firstPeerID}, cluster.AllocIDs(len(splitKeys))...) + for i, k := range splitKeys { + cluster.Split(regionIDs[i], regionIDs[i+1], k, []uint64{peerIDs[i]}, peerIDs[i]) + } + return +} diff --git a/pkg/store/mockstore/unistore/config/BUILD.bazel b/pkg/store/mockstore/unistore/config/BUILD.bazel new file mode 100644 index 0000000000000..668126c850a80 --- /dev/null +++ b/pkg/store/mockstore/unistore/config/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "config", + srcs = ["config.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_badger//options", + "@com_github_pingcap_log//:log", + ], +) diff --git a/store/mockstore/unistore/config/config-template.toml b/pkg/store/mockstore/unistore/config/config-template.toml similarity index 100% rename from store/mockstore/unistore/config/config-template.toml rename to pkg/store/mockstore/unistore/config/config-template.toml diff --git a/store/mockstore/unistore/config/config.go b/pkg/store/mockstore/unistore/config/config.go similarity index 100% rename from store/mockstore/unistore/config/config.go rename to pkg/store/mockstore/unistore/config/config.go diff --git a/pkg/store/mockstore/unistore/cophandler/BUILD.bazel b/pkg/store/mockstore/unistore/cophandler/BUILD.bazel new file mode 100644 index 0000000000000..16c2131d60083 --- /dev/null +++ b/pkg/store/mockstore/unistore/cophandler/BUILD.bazel @@ -0,0 +1,87 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cophandler", + srcs = [ + "analyze.go", + "closure_exec.go", + "cop_handler.go", + "mpp.go", + "mpp_exec.go", + "topn.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/expression/aggregation", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/statistics", + "//pkg/store/mockstore/unistore/client", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/tikv/dbreader", + "//pkg/store/mockstore/unistore/tikv/kverrors", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/rowcodec", + "//pkg/util/timeutil", + "@com_github_golang_protobuf//proto", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_twmb_murmur3//:murmur3", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "cophandler_test", + timeout = "short", + srcs = [ + "cop_handler_test.go", + "main_test.go", + ], + embed = [":cophandler"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/tikv/dbreader", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/rowcodec", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/mockstore/unistore/cophandler/analyze.go b/pkg/store/mockstore/unistore/cophandler/analyze.go new file mode 100644 index 0000000000000..770507c5fd35e --- /dev/null +++ b/pkg/store/mockstore/unistore/cophandler/analyze.go @@ -0,0 +1,664 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package cophandler + +import ( + "bytes" + "context" + "math" + "math/rand" + "slices" + "time" + + "github.com/golang/protobuf/proto" + "github.com/pingcap/badger/y" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tipb/go-tipb" + "github.com/twmb/murmur3" +) + +// handleCopAnalyzeRequest handles coprocessor analyze request. +func handleCopAnalyzeRequest(dbReader *dbreader.DBReader, req *coprocessor.Request) *coprocessor.Response { + resp := &coprocessor.Response{} + if len(req.Ranges) == 0 { + return resp + } + if req.GetTp() != kv.ReqTypeAnalyze { + return resp + } + analyzeReq := new(tipb.AnalyzeReq) + err := proto.Unmarshal(req.Data, analyzeReq) + if err != nil { + resp.OtherError = err.Error() + return resp + } + ranges, err := extractKVRanges(dbReader.StartKey, dbReader.EndKey, req.Ranges, false) + if err != nil { + resp.OtherError = err.Error() + return resp + } + y.Assert(len(ranges) >= 1) + if analyzeReq.Tp == tipb.AnalyzeType_TypeIndex { + resp, err = handleAnalyzeIndexReq(dbReader, ranges, analyzeReq, req.StartTs) + } else if analyzeReq.Tp == tipb.AnalyzeType_TypeCommonHandle { + resp, err = handleAnalyzeCommonHandleReq(dbReader, ranges, analyzeReq, req.StartTs) + } else if analyzeReq.Tp == tipb.AnalyzeType_TypeColumn { + resp, err = handleAnalyzeColumnsReq(dbReader, ranges, analyzeReq, req.StartTs) + } else if analyzeReq.Tp == tipb.AnalyzeType_TypeMixed { + resp, err = handleAnalyzeMixedReq(dbReader, ranges, analyzeReq, req.StartTs) + } else { + resp, err = handleAnalyzeFullSamplingReq(dbReader, ranges, analyzeReq, req.StartTs) + } + if err != nil { + resp = &coprocessor.Response{ + OtherError: err.Error(), + } + } + return resp +} + +func handleAnalyzeIndexReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { + statsVer := int32(statistics.Version1) + if analyzeReq.IdxReq.Version != nil { + statsVer = *analyzeReq.IdxReq.Version + } + sctx := flagsToStatementContext(analyzeReq.Flags) + processor := &analyzeIndexProcessor{ + sctx: sctx, + colLen: int(analyzeReq.IdxReq.NumColumns), + statsBuilder: statistics.NewSortedBuilder(sctx, analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), int(statsVer)), + statsVer: statsVer, + } + if analyzeReq.IdxReq.TopNSize != nil { + processor.topNCount = *analyzeReq.IdxReq.TopNSize + } + if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { + processor.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) + } + processor.fms = statistics.NewFMSketch(int(analyzeReq.IdxReq.SketchSize)) + for _, ran := range rans { + err := dbReader.Scan(ran.StartKey, ran.EndKey, math.MaxInt64, startTS, processor) + if err != nil { + return nil, err + } + } + if statsVer >= statistics.Version2 { + if processor.topNCurValuePair.Count != 0 { + processor.topNValuePairs = append(processor.topNValuePairs, processor.topNCurValuePair) + } + slices.SortFunc(processor.topNValuePairs, statistics.TopnMetaCompare) + if len(processor.topNValuePairs) > int(processor.topNCount) { + processor.topNValuePairs = processor.topNValuePairs[:processor.topNCount] + } + } + hg := statistics.HistogramToProto(processor.statsBuilder.Hist()) + var cm *tipb.CMSketch + if processor.cms != nil { + if statsVer >= statistics.Version2 { + for _, valueCnt := range processor.topNValuePairs { + h1, h2 := murmur3.Sum128(valueCnt.Encoded) + processor.cms.SubValue(h1, h2, valueCnt.Count) + } + } + cm = statistics.CMSketchToProto(processor.cms, &statistics.TopN{TopN: processor.topNValuePairs}) + } + data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm, Collector: &tipb.SampleCollector{FmSketch: statistics.FMSketchToProto(processor.fms)}}) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +func handleAnalyzeCommonHandleReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { + statsVer := statistics.Version1 + if analyzeReq.IdxReq.Version != nil { + statsVer = int(*analyzeReq.IdxReq.Version) + } + processor := &analyzeCommonHandleProcessor{ + colLen: int(analyzeReq.IdxReq.NumColumns), + statsBuilder: statistics.NewSortedBuilder(flagsToStatementContext(analyzeReq.Flags), analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), statsVer), + } + if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { + processor.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) + } + for _, ran := range rans { + err := dbReader.Scan(ran.StartKey, ran.EndKey, math.MaxInt64, startTS, processor) + if err != nil { + return nil, err + } + } + hg := statistics.HistogramToProto(processor.statsBuilder.Hist()) + var cm *tipb.CMSketch + if processor.cms != nil { + cm = statistics.CMSketchToProto(processor.cms, nil) + } + data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm}) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +type analyzeIndexProcessor struct { + skipVal + + sctx *stmtctx.StatementContext + colLen int + statsBuilder *statistics.SortedBuilder + cms *statistics.CMSketch + fms *statistics.FMSketch + rowBuf []byte + + statsVer int32 + topNCount int32 + topNValuePairs []statistics.TopNMeta + topNCurValuePair statistics.TopNMeta +} + +func (p *analyzeIndexProcessor) Process(key, _ []byte) error { + values, _, err := tablecodec.CutIndexKeyNew(key, p.colLen) + if err != nil { + return err + } + p.rowBuf = p.rowBuf[:0] + + for _, val := range values { + p.rowBuf = append(p.rowBuf, val...) + if p.cms != nil { + p.cms.InsertBytes(p.rowBuf) + } + } + + if p.fms != nil { + if err := p.fms.InsertValue(p.sctx, types.NewBytesDatum(safeCopy(p.rowBuf))); err != nil { + return err + } + } + + if p.statsVer >= statistics.Version2 { + if bytes.Equal(p.topNCurValuePair.Encoded, p.rowBuf) { + p.topNCurValuePair.Count++ + } else { + if p.topNCurValuePair.Count > 0 { + p.topNValuePairs = append(p.topNValuePairs, p.topNCurValuePair) + } + p.topNCurValuePair.Encoded = safeCopy(p.rowBuf) + p.topNCurValuePair.Count = 1 + } + } + rowData := safeCopy(p.rowBuf) + err = p.statsBuilder.Iterate(types.NewBytesDatum(rowData)) + if err != nil { + return err + } + return nil +} + +type analyzeCommonHandleProcessor struct { + skipVal + + colLen int + statsBuilder *statistics.SortedBuilder + cms *statistics.CMSketch + rowBuf []byte +} + +func (p *analyzeCommonHandleProcessor) Process(key, value []byte) error { + values, _, err := tablecodec.CutCommonHandle(key, p.colLen) + if err != nil { + return err + } + p.rowBuf = p.rowBuf[:0] + for _, val := range values { + p.rowBuf = append(p.rowBuf, val...) + if p.cms != nil { + p.cms.InsertBytes(p.rowBuf) + } + } + rowData := safeCopy(p.rowBuf) + err = p.statsBuilder.Iterate(types.NewBytesDatum(rowData)) + if err != nil { + return err + } + return nil +} + +type analyzeColumnsExec struct { + skipVal + reader *dbreader.DBReader + ranges []kv.KeyRange + curRan int + seekKey []byte + endKey []byte + startTS uint64 + + chk *chunk.Chunk + decoder *rowcodec.ChunkDecoder + req *chunk.Chunk + evalCtx *evalContext + fields []*ast.ResultField +} + +func buildBaseAnalyzeColumnsExec(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*analyzeColumnsExec, *statistics.SampleBuilder, int64, error) { + sc := flagsToStatementContext(analyzeReq.Flags) + sc.SetTimeZone(time.FixedZone("UTC", int(analyzeReq.TimeZoneOffset))) + evalCtx := &evalContext{sc: sc} + columns := analyzeReq.ColReq.ColumnsInfo + evalCtx.setColumnInfo(columns) + if len(analyzeReq.ColReq.PrimaryColumnIds) > 0 { + evalCtx.primaryCols = analyzeReq.ColReq.PrimaryColumnIds + } + decoder, err := newRowDecoder(evalCtx.columnInfos, evalCtx.fieldTps, evalCtx.primaryCols, evalCtx.sc.TimeZone()) + if err != nil { + return nil, nil, -1, err + } + e := &analyzeColumnsExec{ + reader: dbReader, + seekKey: rans[0].StartKey, + endKey: rans[0].EndKey, + ranges: rans, + curRan: 0, + startTS: startTS, + chk: chunk.NewChunkWithCapacity(evalCtx.fieldTps, 1), + decoder: decoder, + evalCtx: evalCtx, + } + e.fields = make([]*ast.ResultField, len(columns)) + for i := range e.fields { + rf := new(ast.ResultField) + rf.Column = new(model.ColumnInfo) + ft := types.FieldType{} + ft.SetType(mysql.TypeBlob) + ft.SetFlen(mysql.MaxBlobWidth) + ft.SetCharset(charset.CharsetUTF8) + ft.SetCollate(charset.CollationUTF8) + rf.Column.FieldType = ft + e.fields[i] = rf + } + + pkID := int64(-1) + numCols := len(columns) + if columns[0].GetPkHandle() { + pkID = columns[0].ColumnId + columns = columns[1:] + numCols-- + } + collators := make([]collate.Collator, numCols) + fts := make([]*types.FieldType, numCols) + for i, col := range columns { + ft := fieldTypeFromPBColumn(col) + fts[i] = ft + if ft.EvalType() == types.ETString { + collators[i] = collate.GetCollator(ft.GetCollate()) + } + } + colReq := analyzeReq.ColReq + builder := statistics.SampleBuilder{ + Sc: sc, + ColLen: numCols, + MaxBucketSize: colReq.BucketSize, + MaxFMSketchSize: colReq.SketchSize, + MaxSampleSize: colReq.SampleSize, + Collators: collators, + ColsFieldType: fts, + } + statsVer := statistics.Version1 + if analyzeReq.ColReq.Version != nil { + statsVer = int(*analyzeReq.ColReq.Version) + } + if pkID != -1 { + builder.PkBuilder = statistics.NewSortedBuilder(sc, builder.MaxBucketSize, pkID, types.NewFieldType(mysql.TypeBlob), statsVer) + } + if colReq.CmsketchWidth != nil && colReq.CmsketchDepth != nil { + builder.CMSketchWidth = *colReq.CmsketchWidth + builder.CMSketchDepth = *colReq.CmsketchDepth + } + return e, &builder, pkID, nil +} + +func handleAnalyzeColumnsReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { + recordSet, builder, pkID, err := buildBaseAnalyzeColumnsExec(dbReader, rans, analyzeReq, startTS) + if err != nil { + return nil, err + } + builder.RecordSet = recordSet + collectors, pkBuilder, err := builder.CollectColumnStats() + if err != nil { + return nil, errors.Trace(err) + } + colResp := &tipb.AnalyzeColumnsResp{} + if pkID != -1 { + colResp.PkHist = statistics.HistogramToProto(pkBuilder.Hist()) + } + for _, c := range collectors { + colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) + } + data, err := proto.Marshal(colResp) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +func handleAnalyzeFullSamplingReq( + dbReader *dbreader.DBReader, + rans []kv.KeyRange, + analyzeReq *tipb.AnalyzeReq, + startTS uint64, +) (*coprocessor.Response, error) { + sc := flagsToStatementContext(analyzeReq.Flags) + sc.SetTimeZone(time.FixedZone("UTC", int(analyzeReq.TimeZoneOffset))) + evalCtx := &evalContext{sc: sc} + columns := analyzeReq.ColReq.ColumnsInfo + evalCtx.setColumnInfo(columns) + if len(analyzeReq.ColReq.PrimaryColumnIds) > 0 { + evalCtx.primaryCols = analyzeReq.ColReq.PrimaryColumnIds + } + decoder, err := newRowDecoder(evalCtx.columnInfos, evalCtx.fieldTps, evalCtx.primaryCols, evalCtx.sc.TimeZone()) + if err != nil { + return nil, err + } + e := &analyzeColumnsExec{ + reader: dbReader, + seekKey: rans[0].StartKey, + endKey: rans[0].EndKey, + ranges: rans, + curRan: 0, + startTS: startTS, + chk: chunk.NewChunkWithCapacity(evalCtx.fieldTps, 1), + decoder: decoder, + evalCtx: evalCtx, + } + e.fields = make([]*ast.ResultField, len(columns)) + for i := range e.fields { + rf := new(ast.ResultField) + rf.Column = new(model.ColumnInfo) + ft := types.FieldType{} + ft.SetType(mysql.TypeBlob) + ft.SetFlen(mysql.MaxBlobWidth) + ft.SetCharset(charset.CharsetUTF8) + ft.SetCollate(charset.CollationUTF8) + rf.Column.FieldType = ft + e.fields[i] = rf + } + + numCols := len(columns) + collators := make([]collate.Collator, numCols) + fts := make([]*types.FieldType, numCols) + for i, col := range columns { + ft := fieldTypeFromPBColumn(col) + fts[i] = ft + if ft.EvalType() == types.ETString { + collators[i] = collate.GetCollator(ft.GetCollate()) + } + } + colGroups := make([][]int64, 0, len(analyzeReq.ColReq.ColumnGroups)) + for _, group := range analyzeReq.ColReq.ColumnGroups { + colOffsets := make([]int64, len(group.ColumnOffsets)) + copy(colOffsets, group.ColumnOffsets) + colGroups = append(colGroups, colOffsets) + } + colReq := analyzeReq.ColReq + /* #nosec G404 */ + builder := &statistics.RowSampleBuilder{ + Sc: sc, + RecordSet: e, + ColsFieldType: fts, + Collators: collators, + ColGroups: colGroups, + MaxSampleSize: int(colReq.SampleSize), + MaxFMSketchSize: int(colReq.SketchSize), + SampleRate: colReq.GetSampleRate(), + Rng: rand.New(rand.NewSource(time.Now().UnixNano())), + } + collector, err := builder.Collect() + if err != nil { + return nil, err + } + colResp := &tipb.AnalyzeColumnsResp{} + colResp.RowCollector = collector.Base().ToProto() + data, err := colResp.Marshal() + if err != nil { + return nil, err + } + return &coprocessor.Response{Data: data}, nil +} + +// Fields implements the sqlexec.RecordSet Fields interface. +func (e *analyzeColumnsExec) Fields() []*ast.ResultField { + return e.fields +} + +func (e *analyzeColumnsExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + e.req = req + err := e.reader.Scan(e.seekKey, e.endKey, math.MaxInt64, e.startTS, e) + if err != nil { + return err + } + if req.NumRows() < req.Capacity() { + if e.curRan == len(e.ranges)-1 { + e.seekKey = e.endKey + } else { + e.curRan++ + e.seekKey = e.ranges[e.curRan].StartKey + e.endKey = e.ranges[e.curRan].EndKey + } + } + return nil +} + +func (e *analyzeColumnsExec) Process(key, value []byte) error { + handle, err := tablecodec.DecodeRowKey(key) + if err != nil { + return errors.Trace(err) + } + err = e.decoder.DecodeToChunk(value, handle, e.chk) + if err != nil { + return errors.Trace(err) + } + row := e.chk.GetRow(0) + for i, tp := range e.evalCtx.fieldTps { + d := row.GetDatum(i, tp) + if d.IsNull() { + e.req.AppendNull(i) + continue + } + + value, err := tablecodec.EncodeValue(e.evalCtx.sc, nil, d) + if err != nil { + return err + } + e.req.AppendBytes(i, value) + } + e.chk.Reset() + if e.req.NumRows() == e.req.Capacity() { + e.seekKey = kv.Key(key).PrefixNext() + return dbreader.ErrScanBreak + } + return nil +} + +func (e *analyzeColumnsExec) NewChunk(_ chunk.Allocator) *chunk.Chunk { + fields := make([]*types.FieldType, 0, len(e.fields)) + for _, field := range e.fields { + fields = append(fields, &field.Column.FieldType) + } + return chunk.NewChunkWithCapacity(fields, 1024) +} + +// Close implements the sqlexec.RecordSet Close interface. +func (e *analyzeColumnsExec) Close() error { + return nil +} + +func handleAnalyzeMixedReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { + statsVer := int32(statistics.Version1) + if analyzeReq.IdxReq.Version != nil { + statsVer = *analyzeReq.IdxReq.Version + } + colExec, builder, _, err := buildBaseAnalyzeColumnsExec(dbReader, rans, analyzeReq, startTS) + if err != nil { + return nil, err + } + sctx := flagsToStatementContext(analyzeReq.Flags) + e := &analyzeMixedExec{ + sctx: sctx, + analyzeColumnsExec: *colExec, + colLen: int(analyzeReq.IdxReq.NumColumns), + statsBuilder: statistics.NewSortedBuilder(sctx, analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), int(statsVer)), + statsVer: statsVer, + } + builder.RecordSet = e + if analyzeReq.IdxReq.TopNSize != nil { + e.topNCount = *analyzeReq.IdxReq.TopNSize + } + if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { + e.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) + } + e.fms = statistics.NewFMSketch(int(analyzeReq.IdxReq.SketchSize)) + collectors, _, err := builder.CollectColumnStats() + if err != nil { + return nil, errors.Trace(err) + } + // columns + colResp := &tipb.AnalyzeColumnsResp{} + for _, c := range collectors { + colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) + } + // common handle + if statsVer >= statistics.Version2 { + if e.topNCurValuePair.Count != 0 { + e.topNValuePairs = append(e.topNValuePairs, e.topNCurValuePair) + } + slices.SortFunc(e.topNValuePairs, statistics.TopnMetaCompare) + if len(e.topNValuePairs) > int(e.topNCount) { + e.topNValuePairs = e.topNValuePairs[:e.topNCount] + } + } + hg := statistics.HistogramToProto(e.statsBuilder.Hist()) + var cm *tipb.CMSketch + if e.cms != nil { + if statsVer >= statistics.Version2 { + for _, valueCnt := range e.topNValuePairs { + h1, h2 := murmur3.Sum128(valueCnt.Encoded) + e.cms.SubValue(h1, h2, valueCnt.Count) + } + } + cm = statistics.CMSketchToProto(e.cms, &statistics.TopN{TopN: e.topNValuePairs}) + } + fms := statistics.FMSketchToProto(e.fms) + commonHandleResp := &tipb.AnalyzeIndexResp{Hist: hg, Cms: cm, Collector: &tipb.SampleCollector{FmSketch: fms}} + resp := &tipb.AnalyzeMixedResp{ + ColumnsResp: colResp, + IndexResp: commonHandleResp, + } + data, err := proto.Marshal(resp) + if err != nil { + return nil, errors.Trace(err) + } + return &coprocessor.Response{Data: data}, nil +} + +type analyzeMixedExec struct { + analyzeColumnsExec + + sctx *stmtctx.StatementContext + colLen int + statsBuilder *statistics.SortedBuilder + fms *statistics.FMSketch + cms *statistics.CMSketch + rowBuf []byte + + statsVer int32 + topNCount int32 + topNValuePairs []statistics.TopNMeta + topNCurValuePair statistics.TopNMeta +} + +func (e *analyzeMixedExec) Process(key, value []byte) error { + // common handle + values, _, err := tablecodec.CutCommonHandle(key, e.colLen) + if err != nil { + return err + } + e.rowBuf = e.rowBuf[:0] + for _, val := range values { + e.rowBuf = append(e.rowBuf, val...) + if e.cms != nil { + e.cms.InsertBytes(e.rowBuf) + } + } + + if e.fms != nil { + if err := e.fms.InsertValue(e.sctx, types.NewBytesDatum(e.rowBuf)); err != nil { + return err + } + } + + if e.statsVer >= statistics.Version2 { + if bytes.Equal(e.topNCurValuePair.Encoded, e.rowBuf) { + e.topNCurValuePair.Count++ + } else { + if e.topNCurValuePair.Count > 0 { + e.topNValuePairs = append(e.topNValuePairs, e.topNCurValuePair) + } + e.topNCurValuePair.Encoded = safeCopy(e.rowBuf) + e.topNCurValuePair.Count = 1 + } + } + rowData := safeCopy(e.rowBuf) + err = e.statsBuilder.Iterate(types.NewBytesDatum(rowData)) + if err != nil { + return err + } + + // columns + err = e.analyzeColumnsExec.Process(key, value) + return err +} + +func (e *analyzeMixedExec) Next(ctx context.Context, req *chunk.Chunk) error { + req.Reset() + e.req = req + err := e.reader.Scan(e.seekKey, e.endKey, math.MaxInt64, e.startTS, e) + if err != nil { + return err + } + if req.NumRows() < req.Capacity() { + if e.curRan == len(e.ranges)-1 { + e.seekKey = e.endKey + } else { + e.curRan++ + e.seekKey = e.ranges[e.curRan].StartKey + e.endKey = e.ranges[e.curRan].EndKey + } + } + return nil +} diff --git a/store/mockstore/unistore/cophandler/closure_exec.go b/pkg/store/mockstore/unistore/cophandler/closure_exec.go similarity index 97% rename from store/mockstore/unistore/cophandler/closure_exec.go rename to pkg/store/mockstore/unistore/cophandler/closure_exec.go index 1e707b9b2955a..1ba3eaf9d51aa 100644 --- a/store/mockstore/unistore/cophandler/closure_exec.go +++ b/pkg/store/mockstore/unistore/cophandler/closure_exec.go @@ -23,24 +23,24 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - mockpkg "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + mockpkg "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/pingcap/tipb/go-tipb" ) diff --git a/store/mockstore/unistore/cophandler/cop_handler.go b/pkg/store/mockstore/unistore/cophandler/cop_handler.go similarity index 95% rename from store/mockstore/unistore/cophandler/cop_handler.go rename to pkg/store/mockstore/unistore/cophandler/cop_handler.go index b0fd540672229..c0e9d67842630 100644 --- a/store/mockstore/unistore/cophandler/cop_handler.go +++ b/pkg/store/mockstore/unistore/cophandler/cop_handler.go @@ -26,23 +26,23 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/client" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/client" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/pingcap/tipb/go-tipb" ) diff --git a/store/mockstore/unistore/cophandler/cop_handler_test.go b/pkg/store/mockstore/unistore/cophandler/cop_handler_test.go similarity index 96% rename from store/mockstore/unistore/cophandler/cop_handler_test.go rename to pkg/store/mockstore/unistore/cophandler/cop_handler_test.go index ed72642600fd5..a3e7ff46811cd 100644 --- a/store/mockstore/unistore/cophandler/cop_handler_test.go +++ b/pkg/store/mockstore/unistore/cophandler/cop_handler_test.go @@ -26,18 +26,18 @@ import ( "github.com/pingcap/badger/y" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/pkg/store/mockstore/unistore/cophandler/main_test.go b/pkg/store/mockstore/unistore/cophandler/main_test.go new file mode 100644 index 0000000000000..70ebd2afc4fe2 --- /dev/null +++ b/pkg/store/mockstore/unistore/cophandler/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cophandler + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/mockstore/unistore/cophandler/mpp.go b/pkg/store/mockstore/unistore/cophandler/mpp.go new file mode 100644 index 0000000000000..b4f8ebfaae5bf --- /dev/null +++ b/pkg/store/mockstore/unistore/cophandler/mpp.go @@ -0,0 +1,680 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package cophandler + +import ( + "context" + "sync" + "time" + + "github.com/golang/protobuf/proto" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/coprocessor" + "github.com/pingcap/kvproto/pkg/mpp" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/client" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/atomic" +) + +const ( + // MPPErrTunnelNotFound means you can't find an expected tunnel. + MPPErrTunnelNotFound = iota + // MPPErrEstablishConnMultiTimes means we receive the Establish requests at least twice. + MPPErrEstablishConnMultiTimes + // MPPErrMPPGatherIDMismatch means we get mismatched gather id, usually a bug in MPP coordinator + MPPErrMPPGatherIDMismatch +) + +const ( + // ErrExecutorNotSupportedMsg is the message for executor not supported. + ErrExecutorNotSupportedMsg = "executor not supported: " +) + +type mppExecBuilder struct { + sc *stmtctx.StatementContext + dbReader *dbreader.DBReader + mppCtx *MPPCtx + dagReq *tipb.DAGRequest + dagCtx *dagContext + counts []int64 + ndvs []int64 + paging *coprocessor.KeyRange + pagingSize uint64 +} + +func (b *mppExecBuilder) buildMPPTableScan(pb *tipb.TableScan) (*tableScanExec, error) { + ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, pb.Desc) + if err != nil { + return nil, errors.Trace(err) + } + ts := &tableScanExec{ + baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx}, + startTS: b.dagCtx.startTS, + kvRanges: ranges, + dbReader: b.dbReader, + counts: b.counts, + ndvs: b.ndvs, + desc: pb.Desc, + paging: b.paging, + } + if b.dagCtx != nil { + ts.lockStore = b.dagCtx.lockStore + ts.resolvedLocks = b.dagCtx.resolvedLocks + } + for i, col := range pb.Columns { + if col.ColumnId == model.ExtraPhysTblID { + ts.physTblIDColIdx = new(int) + *ts.physTblIDColIdx = i + } + ft := fieldTypeFromPBColumn(col) + ts.fieldTypes = append(ts.fieldTypes, ft) + } + ts.decoder, err = newRowDecoder(pb.Columns, ts.fieldTypes, pb.PrimaryColumnIds, b.sc.TimeZone()) + return ts, err +} + +func (b *mppExecBuilder) buildMPPPartitionTableScan(pb *tipb.PartitionTableScan) (*tableScanExec, error) { + ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, false) + if err != nil { + return nil, errors.Trace(err) + } + ts := &tableScanExec{ + baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx}, + startTS: b.dagCtx.startTS, + kvRanges: ranges, + dbReader: b.dbReader, + } + for i, col := range pb.Columns { + if col.ColumnId == model.ExtraPhysTblID { + ts.physTblIDColIdx = new(int) + *ts.physTblIDColIdx = i + } + ft := fieldTypeFromPBColumn(col) + ts.fieldTypes = append(ts.fieldTypes, ft) + } + ts.decoder, err = newRowDecoder(pb.Columns, ts.fieldTypes, pb.PrimaryColumnIds, b.sc.TimeZone()) + return ts, err +} + +func (b *mppExecBuilder) buildIdxScan(pb *tipb.IndexScan) (*indexScanExec, error) { + ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, pb.Desc) + if err != nil { + return nil, errors.Trace(err) + } + numCols := len(pb.Columns) + numIdxCols := numCols + colInfos := make([]rowcodec.ColInfo, 0, numCols) + fieldTypes := make([]*types.FieldType, 0, numCols) + primaryColIds := pb.GetPrimaryColumnIds() + + lastCol := pb.Columns[numCols-1] + var physTblIDColIdx *int + if lastCol.GetColumnId() == model.ExtraPhysTblID { + numIdxCols-- + physTblIDColIdx = new(int) + *physTblIDColIdx = numIdxCols + lastCol = pb.Columns[numIdxCols-1] + } + if lastCol.GetColumnId() == model.ExtraPidColID { + numIdxCols-- + lastCol = pb.Columns[numIdxCols-1] + } + + hdlStatus := tablecodec.HandleDefault + if len(primaryColIds) == 0 { + if lastCol.GetPkHandle() { + if mysql.HasUnsignedFlag(uint(lastCol.GetFlag())) { + hdlStatus = tablecodec.HandleIsUnsigned + } + numIdxCols-- + } else if lastCol.ColumnId == model.ExtraHandleID { + numIdxCols-- + } + } else { + numIdxCols -= len(primaryColIds) + } + + for _, col := range pb.Columns { + ft := fieldTypeFromPBColumn(col) + fieldTypes = append(fieldTypes, ft) + colInfos = append(colInfos, rowcodec.ColInfo{ + ID: col.ColumnId, + Ft: ft, + IsPKHandle: col.GetPkHandle(), + }) + } + + var prevVals [][]byte + if b.dagReq.GetCollectRangeCounts() { + prevVals = make([][]byte, numIdxCols) + } + idxScan := &indexScanExec{ + baseMPPExec: baseMPPExec{sc: b.sc, fieldTypes: fieldTypes}, + startTS: b.dagCtx.startTS, + kvRanges: ranges, + dbReader: b.dbReader, + lockStore: b.dagCtx.lockStore, + resolvedLocks: b.dagCtx.resolvedLocks, + counts: b.counts, + ndvs: b.ndvs, + prevVals: prevVals, + colInfos: colInfos, + numIdxCols: numIdxCols, + hdlStatus: hdlStatus, + desc: pb.Desc, + physTblIDColIdx: physTblIDColIdx, + paging: b.paging, + } + return idxScan, nil +} + +func (b *mppExecBuilder) buildLimit(pb *tipb.Limit) (*limitExec, error) { + child, err := b.buildMPPExecutor(pb.Child) + if err != nil { + return nil, err + } + exec := &limitExec{ + baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, fieldTypes: child.getFieldTypes(), children: []mppExec{child}}, + limit: pb.GetLimit(), + } + return exec, nil +} + +func (b *mppExecBuilder) buildExpand(pb *tipb.Expand) (mppExec, error) { + child, err := b.buildMPPExecutor(pb.Child) + if err != nil { + return nil, err + } + exec := &expandExec{ + baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, children: []mppExec{child}}, + } + + childFieldTypes := child.getFieldTypes() + // convert the grouping sets. + tidbGss := expression.GroupingSets{} + for _, gs := range pb.GroupingSets { + tidbGs := expression.GroupingSet{} + for _, groupingExprs := range gs.GroupingExprs { + tidbGroupingExprs, err := convertToExprs(b.sc, childFieldTypes, groupingExprs.GroupingExpr) + if err != nil { + return nil, err + } + tidbGs = append(tidbGs, tidbGroupingExprs) + } + tidbGss = append(tidbGss, tidbGs) + } + exec.groupingSets = tidbGss + inGroupingSetMap := make(map[int]struct{}, len(exec.groupingSets)) + for _, gs := range exec.groupingSets { + // for every grouping set, collect column offsets under this grouping set. + for _, groupingExprs := range gs { + for _, groupingExpr := range groupingExprs { + col, ok := groupingExpr.(*expression.Column) + if !ok { + return nil, errors.New("grouping set expr is not column ref") + } + inGroupingSetMap[col.Index] = struct{}{} + } + } + } + mutatedFieldTypes := make([]*types.FieldType, 0, len(childFieldTypes)) + // change the field types return from children tobe nullable. + for offset, f := range childFieldTypes { + cf := f.Clone() + if _, ok := inGroupingSetMap[offset]; ok { + // remove the not null flag, make it nullable. + cf.SetFlag(cf.GetFlag() & ^mysql.NotNullFlag) + } + mutatedFieldTypes = append(mutatedFieldTypes, cf) + } + + // adding groupingID uint64|not-null as last one field types. + groupingIDFieldType := types.NewFieldType(mysql.TypeLonglong) + groupingIDFieldType.SetFlag(mysql.NotNullFlag | mysql.UnsignedFlag) + mutatedFieldTypes = append(mutatedFieldTypes, groupingIDFieldType) + + exec.fieldTypes = mutatedFieldTypes + return exec, nil +} + +func (b *mppExecBuilder) buildTopN(pb *tipb.TopN) (mppExec, error) { + child, err := b.buildMPPExecutor(pb.Child) + if err != nil { + return nil, err + } + pbConds := make([]*tipb.Expr, len(pb.OrderBy)) + for i, item := range pb.OrderBy { + pbConds[i] = item.Expr + } + heap := &topNHeap{ + totalCount: int(pb.Limit), + topNSorter: topNSorter{ + orderByItems: pb.OrderBy, + sc: b.sc, + }, + } + fieldTps := child.getFieldTypes() + var conds []expression.Expression + if conds, err = convertToExprs(b.sc, fieldTps, pbConds); err != nil { + return nil, errors.Trace(err) + } + exec := &topNExec{ + baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, fieldTypes: fieldTps, children: []mppExec{child}}, + heap: heap, + conds: conds, + row: newTopNSortRow(len(conds)), + topn: pb.Limit, + } + + // When using paging protocol, if paging size < topN limit, the topN exec degenerate to do nothing. + if b.paging != nil && b.pagingSize < pb.Limit { + exec.dummy = true + } + + return exec, nil +} + +func (b *mppExecBuilder) buildMPPExchangeSender(pb *tipb.ExchangeSender) (*exchSenderExec, error) { + child, err := b.buildMPPExecutor(pb.Child) + if err != nil { + return nil, err + } + + e := &exchSenderExec{ + baseMPPExec: baseMPPExec{ + sc: b.sc, + mppCtx: b.mppCtx, + children: []mppExec{child}, + fieldTypes: child.getFieldTypes(), + }, + exchangeTp: pb.Tp, + } + if pb.Tp == tipb.ExchangeType_Hash { + // remove the limitation of len(pb.PartitionKeys) == 1 + for _, partitionKey := range pb.PartitionKeys { + expr, err := expression.PBToExpr(partitionKey, child.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + col, ok := expr.(*expression.Column) + if !ok { + return nil, errors.New("Hash key must be column type") + } + e.hashKeyOffsets = append(e.hashKeyOffsets, col.Index) + e.hashKeyTypes = append(e.hashKeyTypes, e.fieldTypes[col.Index]) + } + } + + for _, taskMeta := range pb.EncodedTaskMeta { + targetTask := new(mpp.TaskMeta) + err := targetTask.Unmarshal(taskMeta) + if err != nil { + return nil, err + } + tunnel := &ExchangerTunnel{ + DataCh: make(chan *tipb.Chunk, 10), + sourceTask: b.mppCtx.TaskHandler.Meta, + targetTask: targetTask, + connectedCh: make(chan struct{}), + ErrCh: make(chan error, 1), + } + e.tunnels = append(e.tunnels, tunnel) + err = b.mppCtx.TaskHandler.registerTunnel(tunnel) + if err != nil { + return nil, err + } + } + e.outputOffsets = b.dagReq.OutputOffsets + return e, nil +} + +func (b *mppExecBuilder) buildMPPExchangeReceiver(pb *tipb.ExchangeReceiver) (*exchRecvExec, error) { + e := &exchRecvExec{ + baseMPPExec: baseMPPExec{ + sc: b.sc, + mppCtx: b.mppCtx, + }, + exchangeReceiver: pb, + } + + for _, pbType := range pb.FieldTypes { + tp := expression.FieldTypeFromPB(pbType) + if tp.GetType() == mysql.TypeEnum { + tp.SetElems(append(tp.GetElems(), pbType.Elems...)) + } + e.fieldTypes = append(e.fieldTypes, tp) + } + return e, nil +} + +func (b *mppExecBuilder) buildMPPJoin(pb *tipb.Join, children []*tipb.Executor) (*joinExec, error) { + e := &joinExec{ + baseMPPExec: baseMPPExec{ + sc: b.sc, + mppCtx: b.mppCtx, + }, + Join: pb, + hashMap: make(map[string][]chunk.Row), + buildSideIdx: pb.InnerIdx, + } + leftCh, err := b.buildMPPExecutor(children[0]) + if err != nil { + return nil, errors.Trace(err) + } + rightCh, err := b.buildMPPExecutor(children[1]) + if err != nil { + return nil, errors.Trace(err) + } + if pb.JoinType == tipb.JoinType_TypeLeftOuterJoin { + for _, tp := range rightCh.getFieldTypes() { + tp.DelFlag(mysql.NotNullFlag) + } + defaultInner := chunk.MutRowFromTypes(rightCh.getFieldTypes()) + for i := range rightCh.getFieldTypes() { + defaultInner.SetDatum(i, types.NewDatum(nil)) + } + e.defaultInner = defaultInner.ToRow() + } else if pb.JoinType == tipb.JoinType_TypeRightOuterJoin { + for _, tp := range leftCh.getFieldTypes() { + tp.DelFlag(mysql.NotNullFlag) + } + defaultInner := chunk.MutRowFromTypes(leftCh.getFieldTypes()) + for i := range leftCh.getFieldTypes() { + defaultInner.SetDatum(i, types.NewDatum(nil)) + } + e.defaultInner = defaultInner.ToRow() + } + // because the field type is immutable, so this kind of appending is safe. + e.fieldTypes = append(leftCh.getFieldTypes(), rightCh.getFieldTypes()...) + if pb.InnerIdx == 1 { + e.probeChild = leftCh + e.buildChild = rightCh + probeExpr, err := expression.PBToExpr(pb.LeftJoinKeys[0], leftCh.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.probeKey = probeExpr.(*expression.Column) + buildExpr, err := expression.PBToExpr(pb.RightJoinKeys[0], rightCh.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.buildKey = buildExpr.(*expression.Column) + } else { + e.probeChild = rightCh + e.buildChild = leftCh + buildExpr, err := expression.PBToExpr(pb.LeftJoinKeys[0], leftCh.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.buildKey = buildExpr.(*expression.Column) + probeExpr, err := expression.PBToExpr(pb.RightJoinKeys[0], rightCh.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.probeKey = probeExpr.(*expression.Column) + } + e.comKeyTp = types.AggFieldType([]*types.FieldType{e.probeKey.RetType, e.buildKey.RetType}) + if e.comKeyTp.GetType() == mysql.TypeNewDecimal { + e.comKeyTp.SetFlen(mysql.MaxDecimalWidth) + e.comKeyTp.SetDecimal(mysql.MaxDecimalScale) + } + return e, nil +} + +func (b *mppExecBuilder) buildMPPProj(proj *tipb.Projection) (*projExec, error) { + e := &projExec{} + + chExec, err := b.buildMPPExecutor(proj.Child) + if err != nil { + return nil, errors.Trace(err) + } + e.children = []mppExec{chExec} + + for _, pbExpr := range proj.Exprs { + expr, err := expression.PBToExpr(pbExpr, chExec.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.exprs = append(e.exprs, expr) + e.fieldTypes = append(e.fieldTypes, expr.GetType()) + } + return e, nil +} + +func (b *mppExecBuilder) buildMPPSel(sel *tipb.Selection) (*selExec, error) { + chExec, err := b.buildMPPExecutor(sel.Child) + if err != nil { + return nil, errors.Trace(err) + } + e := &selExec{ + baseMPPExec: baseMPPExec{ + fieldTypes: chExec.getFieldTypes(), + sc: b.sc, + mppCtx: b.mppCtx, + children: []mppExec{chExec}, + }, + } + + for _, pbExpr := range sel.Conditions { + expr, err := expression.PBToExpr(pbExpr, chExec.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.conditions = append(e.conditions, expr) + } + return e, nil +} + +func (b *mppExecBuilder) buildMPPAgg(agg *tipb.Aggregation) (*aggExec, error) { + e := &aggExec{ + groups: make(map[string]struct{}), + aggCtxsMap: make(map[string][]*aggregation.AggEvaluateContext), + processed: false, + } + + chExec, err := b.buildMPPExecutor(agg.Child) + if err != nil { + return nil, errors.Trace(err) + } + e.children = []mppExec{chExec} + for _, aggFunc := range agg.AggFunc { + ft := expression.PbTypeToFieldType(aggFunc.FieldType) + e.fieldTypes = append(e.fieldTypes, ft) + aggExpr, err := aggregation.NewDistAggFunc(aggFunc, chExec.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.aggExprs = append(e.aggExprs, aggExpr) + } + e.sc = b.sc + + for _, gby := range agg.GroupBy { + ft := expression.PbTypeToFieldType(gby.FieldType) + e.fieldTypes = append(e.fieldTypes, ft) + e.groupByTypes = append(e.groupByTypes, ft) + gbyExpr, err := expression.PBToExpr(gby, chExec.getFieldTypes(), b.sc) + if err != nil { + return nil, errors.Trace(err) + } + e.groupByExprs = append(e.groupByExprs, gbyExpr) + } + return e, nil +} + +func (b *mppExecBuilder) buildMPPExecutor(exec *tipb.Executor) (mppExec, error) { + switch exec.Tp { + case tipb.ExecType_TypeTableScan: + ts := exec.TblScan + return b.buildMPPTableScan(ts) + case tipb.ExecType_TypeExchangeReceiver: + rec := exec.ExchangeReceiver + return b.buildMPPExchangeReceiver(rec) + case tipb.ExecType_TypeExchangeSender: + send := exec.ExchangeSender + return b.buildMPPExchangeSender(send) + case tipb.ExecType_TypeJoin: + join := exec.Join + return b.buildMPPJoin(join, join.Children) + case tipb.ExecType_TypeAggregation, tipb.ExecType_TypeStreamAgg: + agg := exec.Aggregation + return b.buildMPPAgg(agg) + case tipb.ExecType_TypeProjection: + return b.buildMPPProj(exec.Projection) + case tipb.ExecType_TypeSelection: + return b.buildMPPSel(exec.Selection) + case tipb.ExecType_TypeIndexScan: + return b.buildIdxScan(exec.IdxScan) + case tipb.ExecType_TypeLimit: + return b.buildLimit(exec.Limit) + case tipb.ExecType_TypeTopN: + return b.buildTopN(exec.TopN) + case tipb.ExecType_TypePartitionTableScan: + ts := exec.PartitionTableScan + return b.buildMPPPartitionTableScan(ts) + case tipb.ExecType_TypeExpand: + return b.buildExpand(exec.Expand) + default: + return nil, errors.Errorf(ErrExecutorNotSupportedMsg + exec.Tp.String()) + } +} + +// HandleMPPDAGReq handles a cop request that is converted from mpp request. +// It returns nothing. Real data will return by stream rpc. +func HandleMPPDAGReq(dbReader *dbreader.DBReader, req *coprocessor.Request, mppCtx *MPPCtx) *coprocessor.Response { + dagReq := new(tipb.DAGRequest) + err := proto.Unmarshal(req.Data, dagReq) + if err != nil { + return &coprocessor.Response{OtherError: err.Error()} + } + dagCtx := &dagContext{ + dbReader: dbReader, + startTS: req.StartTs, + keyRanges: req.Ranges, + } + builder := mppExecBuilder{ + dbReader: dbReader, + mppCtx: mppCtx, + sc: flagsToStatementContext(dagReq.Flags), + dagReq: dagReq, + dagCtx: dagCtx, + } + mppExec, err := builder.buildMPPExecutor(dagReq.RootExecutor) + if err != nil { + panic("build error: " + err.Error()) + } + err = mppExec.open() + if err != nil { + panic("open phase find error: " + err.Error()) + } + _, err = mppExec.next() + if err != nil { + panic("running phase find error: " + err.Error()) + } + return &coprocessor.Response{} +} + +// MPPTaskHandler exists in a single store. +type MPPTaskHandler struct { + // When a connect request comes, it contains server task (source) and client task (target), Exchanger dataCh set will find dataCh by client task. + tunnelSetLock sync.Mutex + TunnelSet map[int64]*ExchangerTunnel + + Meta *mpp.TaskMeta + RPCClient client.Client + + Status atomic.Int32 + Err error +} + +// HandleEstablishConn handles EstablishMPPConnectionRequest +func (h *MPPTaskHandler) HandleEstablishConn(_ context.Context, req *mpp.EstablishMPPConnectionRequest) (*ExchangerTunnel, error) { + meta := req.ReceiverMeta + for i := 0; i < 10; i++ { + tunnel, err := h.getAndActiveTunnel(req) + if err == nil { + return tunnel, nil + } + if err.Code == MPPErrMPPGatherIDMismatch { + return nil, errors.Errorf(err.Msg) + } + time.Sleep(time.Second) + } + return nil, errors.Errorf("cannot find client task %d registered in server task %d", meta.TaskId, req.SenderMeta.TaskId) +} + +func (h *MPPTaskHandler) registerTunnel(tunnel *ExchangerTunnel) error { + if h.Meta.GatherId != tunnel.sourceTask.GatherId { + return errors.Errorf("mpp gather id mismatch, maybe a bug in MPP coordinator") + } + if h.Meta.GatherId != tunnel.targetTask.GatherId { + return errors.Errorf("mpp gather id mismatch, maybe a bug in MPP coordinator") + } + taskID := tunnel.targetTask.TaskId + h.tunnelSetLock.Lock() + defer h.tunnelSetLock.Unlock() + _, ok := h.TunnelSet[taskID] + if ok { + return errors.Errorf("task id %d has been registered", taskID) + } + h.TunnelSet[taskID] = tunnel + return nil +} + +func (h *MPPTaskHandler) getAndActiveTunnel(req *mpp.EstablishMPPConnectionRequest) (*ExchangerTunnel, *mpp.Error) { + if h.Meta.GatherId != req.ReceiverMeta.GatherId { + return nil, &mpp.Error{Code: MPPErrMPPGatherIDMismatch, Msg: "mpp gather id mismatch, maybe a bug in MPP coordinator"} + } + targetID := req.ReceiverMeta.TaskId + h.tunnelSetLock.Lock() + defer h.tunnelSetLock.Unlock() + if tunnel, ok := h.TunnelSet[targetID]; ok { + close(tunnel.connectedCh) + return tunnel, nil + } + // We dont find this dataCh, may be task not ready or have been deleted. + return nil, &mpp.Error{Code: MPPErrTunnelNotFound, Msg: "task not found, please wait for a while"} +} + +// ExchangerTunnel contains a channel that can transfer data. +// Only One Sender and Receiver use this channel, so it's safe to close it by sender. +type ExchangerTunnel struct { + DataCh chan *tipb.Chunk + + sourceTask *mpp.TaskMeta // source task is nearer to the data source + targetTask *mpp.TaskMeta // target task is nearer to the client end , as tidb. + + connectedCh chan struct{} + ErrCh chan error +} + +// RecvChunk recive tipb chunk +func (tunnel *ExchangerTunnel) RecvChunk() (tipbChunk *tipb.Chunk, err error) { + tipbChunk = <-tunnel.DataCh + select { + case err = <-tunnel.ErrCh: + default: + } + return tipbChunk, err +} diff --git a/store/mockstore/unistore/cophandler/mpp_exec.go b/pkg/store/mockstore/unistore/cophandler/mpp_exec.go similarity index 97% rename from store/mockstore/unistore/cophandler/mpp_exec.go rename to pkg/store/mockstore/unistore/cophandler/mpp_exec.go index 80e6caf3141f9..6ec94bcd43f87 100644 --- a/store/mockstore/unistore/cophandler/mpp_exec.go +++ b/pkg/store/mockstore/unistore/cophandler/mpp_exec.go @@ -28,19 +28,19 @@ import ( "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/expression/aggregation" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/pkg/store/mockstore/unistore/cophandler/topn.go b/pkg/store/mockstore/unistore/cophandler/topn.go new file mode 100644 index 0000000000000..de2798a2902b9 --- /dev/null +++ b/pkg/store/mockstore/unistore/cophandler/topn.go @@ -0,0 +1,150 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package cophandler + +import ( + "cmp" + "container/heap" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tipb/go-tipb" +) + +type sortRow struct { + key []types.Datum + data [][]byte +} + +// topNSorter implements sort.Interface. When all rows have been processed, the topNSorter will sort the whole data in heap. +type topNSorter struct { + orderByItems []*tipb.ByItem + rows []*sortRow + err error + sc *stmtctx.StatementContext +} + +func (t *topNSorter) Len() int { + return len(t.rows) +} + +func (t *topNSorter) Swap(i, j int) { + t.rows[i], t.rows[j] = t.rows[j], t.rows[i] +} + +func (t *topNSorter) Less(i, j int) bool { + for index, by := range t.orderByItems { + v1 := t.rows[i].key[index] + v2 := t.rows[j].key[index] + + ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) + if err != nil { + t.err = errors.Trace(err) + return true + } + + if by.Desc { + ret = -ret + } + + if ret < 0 { + return true + } else if ret > 0 { + return false + } + } + + return false +} + +// topNHeap holds the top n elements using heap structure. It implements heap.Interface. +// When we insert a row, topNHeap will check if the row can become one of the top n element or not. +type topNHeap struct { + topNSorter + + // totalCount is equal to the limit count, which means the max size of heap. + totalCount int + // heapSize means the current size of this heap. + heapSize int +} + +func (t *topNHeap) Len() int { + return t.heapSize +} + +func (t *topNHeap) Push(x interface{}) { + t.rows = append(t.rows, x.(*sortRow)) + t.heapSize++ +} + +func (t *topNHeap) Pop() interface{} { + return nil +} + +func (t *topNHeap) Less(i, j int) bool { + for index, by := range t.orderByItems { + v1 := t.rows[i].key[index] + v2 := t.rows[j].key[index] + + var ret int + var err error + if expression.FieldTypeFromPB(by.GetExpr().GetFieldType()).GetType() == mysql.TypeEnum { + ret = cmp.Compare(v1.GetUint64(), v2.GetUint64()) + } else { + ret, err = v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) + if err != nil { + t.err = errors.Trace(err) + return true + } + } + + if by.Desc { + ret = -ret + } + + if ret > 0 { + return true + } else if ret < 0 { + return false + } + } + + return false +} + +// tryToAddRow tries to add a row to heap. +// When this row is not less than any rows in heap, it will never become the top n element. +// Then this function returns false. +func (t *topNHeap) tryToAddRow(row *sortRow) bool { + success := false + if t.heapSize == t.totalCount { + t.rows = append(t.rows, row) + // When this row is less than the top element, it will replace it and adjust the heap structure. + if t.Less(0, t.heapSize) { + t.Swap(0, t.heapSize) + heap.Fix(t, 0) + success = true + } + t.rows = t.rows[:t.heapSize] + } else { + heap.Push(t, row) + success = true + } + return success +} diff --git a/pkg/store/mockstore/unistore/lockstore/BUILD.bazel b/pkg/store/mockstore/unistore/lockstore/BUILD.bazel new file mode 100644 index 0000000000000..ff10d4779f9ac --- /dev/null +++ b/pkg/store/mockstore/unistore/lockstore/BUILD.bazel @@ -0,0 +1,35 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lockstore", + srcs = [ + "arena.go", + "iterator.go", + "load_dump.go", + "lockstore.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "lockstore_test", + timeout = "short", + srcs = [ + "lockstore_test.go", + "main_test.go", + ], + embed = [":lockstore"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/store/mockstore/unistore/lockstore/arena.go b/pkg/store/mockstore/unistore/lockstore/arena.go similarity index 100% rename from store/mockstore/unistore/lockstore/arena.go rename to pkg/store/mockstore/unistore/lockstore/arena.go diff --git a/store/mockstore/unistore/lockstore/iterator.go b/pkg/store/mockstore/unistore/lockstore/iterator.go similarity index 100% rename from store/mockstore/unistore/lockstore/iterator.go rename to pkg/store/mockstore/unistore/lockstore/iterator.go diff --git a/store/mockstore/unistore/lockstore/load_dump.go b/pkg/store/mockstore/unistore/lockstore/load_dump.go similarity index 100% rename from store/mockstore/unistore/lockstore/load_dump.go rename to pkg/store/mockstore/unistore/lockstore/load_dump.go diff --git a/store/mockstore/unistore/lockstore/lockstore.go b/pkg/store/mockstore/unistore/lockstore/lockstore.go similarity index 100% rename from store/mockstore/unistore/lockstore/lockstore.go rename to pkg/store/mockstore/unistore/lockstore/lockstore.go diff --git a/store/mockstore/unistore/lockstore/lockstore_test.go b/pkg/store/mockstore/unistore/lockstore/lockstore_test.go similarity index 100% rename from store/mockstore/unistore/lockstore/lockstore_test.go rename to pkg/store/mockstore/unistore/lockstore/lockstore_test.go diff --git a/pkg/store/mockstore/unistore/lockstore/main_test.go b/pkg/store/mockstore/unistore/lockstore/main_test.go new file mode 100644 index 0000000000000..06fbeddf91b88 --- /dev/null +++ b/pkg/store/mockstore/unistore/lockstore/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package lockstore + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/mockstore/unistore/main_test.go b/pkg/store/mockstore/unistore/main_test.go new file mode 100644 index 0000000000000..956f743a5513d --- /dev/null +++ b/pkg/store/mockstore/unistore/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package unistore + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/mockstore/unistore/metrics/BUILD.bazel b/pkg/store/mockstore/unistore/metrics/BUILD.bazel new file mode 100644 index 0000000000000..6bdf2886c5fa1 --- /dev/null +++ b/pkg/store/mockstore/unistore/metrics/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/metrics", + visibility = ["//visibility:public"], + deps = ["@com_github_prometheus_client_golang//prometheus"], +) diff --git a/store/mockstore/unistore/metrics/metrics.go b/pkg/store/mockstore/unistore/metrics/metrics.go similarity index 100% rename from store/mockstore/unistore/metrics/metrics.go rename to pkg/store/mockstore/unistore/metrics/metrics.go diff --git a/pkg/store/mockstore/unistore/mock.go b/pkg/store/mockstore/unistore/mock.go new file mode 100644 index 0000000000000..d96426491c5ca --- /dev/null +++ b/pkg/store/mockstore/unistore/mock.go @@ -0,0 +1,72 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package unistore + +import ( + "os" + + "github.com/pingcap/errors" + usconf "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" + ussvr "github.com/pingcap/tidb/pkg/store/mockstore/unistore/server" + pd "github.com/tikv/pd/client" +) + +// New creates an embed unistore client, pd client and cluster handler. +func New(path string) (*RPCClient, pd.Client, *Cluster, error) { + persistent := true + if path == "" { + var err error + if path, err = os.MkdirTemp("", "tidb-unistore-temp"); err != nil { + return nil, nil, nil, err + } + persistent = false + } + + if err := os.MkdirAll(path, 0750); err != nil { + return nil, nil, nil, err + } + + conf := usconf.DefaultConf + conf.Engine.ValueThreshold = 0 + conf.Engine.DBPath = path + conf.Server.Raft = false + + if !persistent { + conf.Engine.VolatileMode = true + conf.Engine.MaxMemTableSize = 12 << 20 + conf.Engine.SyncWrite = false + conf.Engine.NumCompactors = 1 + conf.Engine.CompactL0WhenClose = false + conf.Engine.VlogFileSize = 16 << 20 + } + + srv, rm, pd, err := ussvr.NewMock(&conf, 1) + if err != nil { + return nil, nil, nil, errors.Trace(err) + } + + cluster := newCluster(rm) + client := &RPCClient{ + usSvr: srv, + cluster: cluster, + path: path, + persistent: persistent, + rawHandler: newRawHandler(), + } + srv.RPCClient = client + pdClient := newPDClient(pd) + + return client, pdClient, cluster, nil +} diff --git a/pkg/store/mockstore/unistore/pd.go b/pkg/store/mockstore/unistore/pd.go new file mode 100644 index 0000000000000..04b6bae42ee14 --- /dev/null +++ b/pkg/store/mockstore/unistore/pd.go @@ -0,0 +1,271 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package unistore + +import ( + "context" + "errors" + "math" + "sync" + "sync/atomic" + + "github.com/pingcap/kvproto/pkg/keyspacepb" + "github.com/pingcap/kvproto/pkg/meta_storagepb" + "github.com/pingcap/kvproto/pkg/pdpb" + rmpb "github.com/pingcap/kvproto/pkg/resource_manager" + "github.com/pingcap/tidb/pkg/domain/infosync" + us "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv" + "github.com/tikv/client-go/v2/oracle" + pd "github.com/tikv/pd/client" +) + +var _ pd.Client = new(pdClient) + +type pdClient struct { + *us.MockPD + pd.ResourceManagerClient + + serviceSafePoints map[string]uint64 + gcSafePointMu sync.Mutex + globalConfig map[string]string + externalTimestamp atomic.Uint64 +} + +func newPDClient(pd *us.MockPD) *pdClient { + return &pdClient{ + MockPD: pd, + ResourceManagerClient: infosync.NewMockResourceManagerClient(), + serviceSafePoints: make(map[string]uint64), + globalConfig: make(map[string]string), + } +} + +func (c *pdClient) LoadGlobalConfig(ctx context.Context, names []string, configPath string) ([]pd.GlobalConfigItem, int64, error) { + ret := make([]pd.GlobalConfigItem, len(names)) + for i, name := range names { + if r, ok := c.globalConfig["/global/config/"+name]; ok { + ret[i] = pd.GlobalConfigItem{Name: "/global/config/" + name, Value: r, EventType: pdpb.EventType_PUT} + } else { + ret[i] = pd.GlobalConfigItem{Name: "/global/config/" + name, Value: ""} + } + } + return ret, 0, nil +} + +func (c *pdClient) StoreGlobalConfig(ctx context.Context, configPath string, items []pd.GlobalConfigItem) error { + for _, item := range items { + c.globalConfig["/global/config/"+item.Name] = item.Value + } + return nil +} + +func (c *pdClient) WatchGlobalConfig(ctx context.Context, configPath string, revision int64) (chan []pd.GlobalConfigItem, error) { + globalConfigWatcherCh := make(chan []pd.GlobalConfigItem, 16) + go func() { + defer func() { + if r := recover(); r != nil { + return + } + }() + for i := 0; i < 10; i++ { + for k, v := range c.globalConfig { + globalConfigWatcherCh <- []pd.GlobalConfigItem{{Name: k, Value: v}} + } + } + }() + return globalConfigWatcherCh, nil +} + +func (c *pdClient) GetLocalTS(ctx context.Context, dcLocation string) (int64, int64, error) { + return c.GetTS(ctx) +} + +func (c *pdClient) GetTSAsync(ctx context.Context) pd.TSFuture { + return &mockTSFuture{c, ctx, false} +} + +func (c *pdClient) GetLocalTSAsync(ctx context.Context, dcLocation string) pd.TSFuture { + return &mockTSFuture{c, ctx, false} +} + +type mockTSFuture struct { + pdc *pdClient + ctx context.Context + used bool +} + +func (m *mockTSFuture) Wait() (int64, int64, error) { + if m.used { + return 0, 0, errors.New("cannot wait tso twice") + } + m.used = true + return m.pdc.GetTS(m.ctx) +} + +func (c *pdClient) GetLeaderAddr() string { return "mockpd" } + +func (c *pdClient) UpdateServiceGCSafePoint(ctx context.Context, serviceID string, ttl int64, safePoint uint64) (uint64, error) { + c.gcSafePointMu.Lock() + defer c.gcSafePointMu.Unlock() + + if ttl == 0 { + delete(c.serviceSafePoints, serviceID) + } else { + var minSafePoint uint64 = math.MaxUint64 + for _, ssp := range c.serviceSafePoints { + if ssp < minSafePoint { + minSafePoint = ssp + } + } + + if len(c.serviceSafePoints) == 0 || minSafePoint <= safePoint { + c.serviceSafePoints[serviceID] = safePoint + } + } + + // The minSafePoint may have changed. Reload it. + var minSafePoint uint64 = math.MaxUint64 + for _, ssp := range c.serviceSafePoints { + if ssp < minSafePoint { + minSafePoint = ssp + } + } + return minSafePoint, nil +} + +func (c *pdClient) GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error) { + return &pdpb.GetOperatorResponse{Status: pdpb.OperatorStatus_SUCCESS}, nil +} + +func (c *pdClient) GetAllMembers(ctx context.Context) ([]*pdpb.Member, error) { + return nil, nil +} + +func (c *pdClient) ScatterRegions(ctx context.Context, regionsID []uint64, opts ...pd.RegionsOption) (*pdpb.ScatterRegionResponse, error) { + return nil, nil +} + +func (c *pdClient) SplitRegions(ctx context.Context, splitKeys [][]byte, opts ...pd.RegionsOption) (*pdpb.SplitRegionsResponse, error) { + return nil, nil +} + +func (c *pdClient) SplitAndScatterRegions(ctx context.Context, splitKeys [][]byte, opts ...pd.RegionsOption) (*pdpb.SplitAndScatterRegionsResponse, error) { + return nil, nil +} + +func (c *pdClient) GetRegionFromMember(ctx context.Context, key []byte, memberURLs []string) (*pd.Region, error) { + return nil, nil +} + +func (c *pdClient) UpdateOption(option pd.DynamicOption, value interface{}) error { + return nil +} + +func (c *pdClient) GetAllKeyspaces(ctx context.Context, startID uint32, limit uint32) ([]*keyspacepb.KeyspaceMeta, error) { + return nil, nil +} + +// LoadKeyspace loads and returns target keyspace's metadata. +func (c *pdClient) LoadKeyspace(ctx context.Context, name string) (*keyspacepb.KeyspaceMeta, error) { + return nil, nil +} + +// WatchKeyspaces watches keyspace meta changes. +// It returns a stream of slices of keyspace metadata. +// The first message in stream contains all current keyspaceMeta, +// all subsequent messages contains new put events for all keyspaces. +func (c *pdClient) WatchKeyspaces(ctx context.Context) (chan []*keyspacepb.KeyspaceMeta, error) { + return nil, nil +} + +func (c *pdClient) UpdateKeyspaceState(ctx context.Context, id uint32, state keyspacepb.KeyspaceState) (*keyspacepb.KeyspaceMeta, error) { + return nil, nil +} + +func (c *pdClient) AcquireTokenBuckets(ctx context.Context, request *rmpb.TokenBucketsRequest) ([]*rmpb.TokenBucketResponse, error) { + return nil, nil +} + +func (c *pdClient) SetExternalTimestamp(ctx context.Context, newTimestamp uint64) error { + p, l, err := c.GetTS(ctx) + if err != nil { + return err + } + + currentTSO := oracle.ComposeTS(p, l) + if newTimestamp > currentTSO { + return errors.New("external timestamp is greater than global tso") + } + for { + externalTimestamp := c.externalTimestamp.Load() + if externalTimestamp > newTimestamp { + return errors.New("cannot decrease the external timestamp") + } else if externalTimestamp == newTimestamp { + return nil + } + + if c.externalTimestamp.CompareAndSwap(externalTimestamp, newTimestamp) { + return nil + } + } +} + +func (c *pdClient) GetExternalTimestamp(ctx context.Context) (uint64, error) { + return c.externalTimestamp.Load(), nil +} + +func (c *pdClient) GetTSWithinKeyspace(ctx context.Context, keyspaceID uint32) (int64, int64, error) { + return 0, 0, nil +} + +func (c *pdClient) GetTSWithinKeyspaceAsync(ctx context.Context, keyspaceID uint32) pd.TSFuture { + return nil +} + +func (c *pdClient) GetLocalTSWithinKeyspace(ctx context.Context, dcLocation string, keyspaceID uint32) (int64, int64, error) { + return 0, 0, nil +} + +func (c *pdClient) GetLocalTSWithinKeyspaceAsync(ctx context.Context, dcLocation string, keyspaceID uint32) pd.TSFuture { + return nil +} + +func (c *pdClient) Get(ctx context.Context, key []byte, opts ...pd.OpOption) (*meta_storagepb.GetResponse, error) { + return nil, nil +} + +func (c *pdClient) Put(ctx context.Context, key []byte, value []byte, opts ...pd.OpOption) (*meta_storagepb.PutResponse, error) { + return nil, nil +} + +func (c *pdClient) GetMinTS(ctx context.Context) (int64, int64, error) { + return 0, 0, nil +} + +func (c *pdClient) LoadResourceGroups(ctx context.Context) ([]*rmpb.ResourceGroup, int64, error) { + return nil, 0, nil +} + +func (c *pdClient) UpdateGCSafePointV2(ctx context.Context, keyspaceID uint32, safePoint uint64) (uint64, error) { + panic("unimplemented") +} + +func (c *pdClient) UpdateServiceSafePointV2(ctx context.Context, keyspaceID uint32, serviceID string, ttl int64, safePoint uint64) (uint64, error) { + panic("unimplemented") +} + +func (c *pdClient) WatchGCSafePointV2(ctx context.Context, revision int64) (chan []*pdpb.SafePointEvent, error) { + panic("unimplemented") +} diff --git a/pkg/store/mockstore/unistore/pd/BUILD.bazel b/pkg/store/mockstore/unistore/pd/BUILD.bazel new file mode 100644 index 0000000000000..4c6e9f562b7b9 --- /dev/null +++ b/pkg/store/mockstore/unistore/pd/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pd", + srcs = ["client.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/pdpb", + "@com_github_pingcap_log//:log", + "@com_github_tikv_pd_client//:client", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_zap//:zap", + ], +) diff --git a/store/mockstore/unistore/pd/client.go b/pkg/store/mockstore/unistore/pd/client.go similarity index 100% rename from store/mockstore/unistore/pd/client.go rename to pkg/store/mockstore/unistore/pd/client.go diff --git a/store/mockstore/unistore/pd_test.go b/pkg/store/mockstore/unistore/pd_test.go similarity index 100% rename from store/mockstore/unistore/pd_test.go rename to pkg/store/mockstore/unistore/pd_test.go diff --git a/store/mockstore/unistore/raw_handler.go b/pkg/store/mockstore/unistore/raw_handler.go similarity index 98% rename from store/mockstore/unistore/raw_handler.go rename to pkg/store/mockstore/unistore/raw_handler.go index 3e8aa30423ec7..c99d6280e1058 100644 --- a/store/mockstore/unistore/raw_handler.go +++ b/pkg/store/mockstore/unistore/raw_handler.go @@ -20,7 +20,7 @@ import ( "sync" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" ) type rawHandler struct { diff --git a/store/mockstore/unistore/raw_handler_test.go b/pkg/store/mockstore/unistore/raw_handler_test.go similarity index 100% rename from store/mockstore/unistore/raw_handler_test.go rename to pkg/store/mockstore/unistore/raw_handler_test.go diff --git a/store/mockstore/unistore/rpc.go b/pkg/store/mockstore/unistore/rpc.go similarity index 99% rename from store/mockstore/unistore/rpc.go rename to pkg/store/mockstore/unistore/rpc.go index d08ff4b9c1036..ccbeb28748a44 100644 --- a/store/mockstore/unistore/rpc.go +++ b/pkg/store/mockstore/unistore/rpc.go @@ -32,9 +32,9 @@ import ( "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/parser/terror" - us "github.com/pingcap/tidb/store/mockstore/unistore/tikv" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/terror" + us "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/tikv/client-go/v2/tikvrpc" "google.golang.org/grpc/metadata" ) diff --git a/pkg/store/mockstore/unistore/server/BUILD.bazel b/pkg/store/mockstore/unistore/server/BUILD.bazel new file mode 100644 index 0000000000000..143c1e89a0360 --- /dev/null +++ b/pkg/store/mockstore/unistore/server/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "server", + srcs = ["server.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/server", + visibility = ["//visibility:public"], + deps = [ + "//pkg/store/mockstore/unistore/config", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/pd", + "//pkg/store/mockstore/unistore/tikv", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//options", + "@com_github_pingcap_errors//:errors", + ], +) diff --git a/pkg/store/mockstore/unistore/server/server.go b/pkg/store/mockstore/unistore/server/server.go new file mode 100644 index 0000000000000..0ba425ce66850 --- /dev/null +++ b/pkg/store/mockstore/unistore/server/server.go @@ -0,0 +1,151 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package server + +import ( + "context" + "path/filepath" + + "github.com/pingcap/badger" + "github.com/pingcap/badger/options" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" +) + +const ( + subPathRaft = "raft" + subPathKV = "kv" +) + +// NewMock returns a new *tikv.Server, a new *tikv.MockRegionManager and a new *tikv.MockPD. +func NewMock(conf *config.Config, clusterID uint64) (*tikv.Server, *tikv.MockRegionManager, *tikv.MockPD, error) { + physical, logical := tikv.GetTS() + ts := uint64(physical)<<18 + uint64(logical) + + safePoint := &tikv.SafePoint{} + db, err := createDB(subPathKV, safePoint, &conf.Engine) + if err != nil { + return nil, nil, nil, err + } + bundle := &mvcc.DBBundle{ + DB: db, + LockStore: lockstore.NewMemStore(8 << 20), + StateTS: ts, + } + + rm, err := tikv.NewMockRegionManager(bundle, clusterID, tikv.RegionOptions{ + StoreAddr: conf.Server.StoreAddr, + PDAddr: conf.Server.PDAddr, + RegionSize: conf.Server.RegionSize, + }) + if err != nil { + return nil, nil, nil, err + } + pdClient := tikv.NewMockPD(rm) + svr, err := setupStandAlongInnerServer(bundle, safePoint, rm, pdClient, conf) + if err != nil { + return nil, nil, nil, err + } + return svr, rm, pdClient, nil +} + +// New returns a new *tikv.Server. +func New(conf *config.Config, pdClient pd.Client) (*tikv.Server, error) { + physical, logical, err := pdClient.GetTS(context.Background()) + if err != nil { + return nil, err + } + ts := uint64(physical)<<18 + uint64(logical) + + safePoint := &tikv.SafePoint{} + db, err := createDB(subPathKV, safePoint, &conf.Engine) + if err != nil { + return nil, err + } + bundle := &mvcc.DBBundle{ + DB: db, + LockStore: lockstore.NewMemStore(8 << 20), + StateTS: ts, + } + if conf.Server.Raft { + return nil, errors.New("not support raftstore") + } + rm := tikv.NewStandAloneRegionManager(bundle, getRegionOptions(conf), pdClient) + return setupStandAlongInnerServer(bundle, safePoint, rm, pdClient, conf) +} + +func getRegionOptions(conf *config.Config) tikv.RegionOptions { + return tikv.RegionOptions{ + StoreAddr: conf.Server.StoreAddr, + PDAddr: conf.Server.PDAddr, + RegionSize: conf.Server.RegionSize, + } +} + +func setupStandAlongInnerServer(bundle *mvcc.DBBundle, safePoint *tikv.SafePoint, rm tikv.RegionManager, pdClient pd.Client, conf *config.Config) (*tikv.Server, error) { + innerServer := tikv.NewStandAlongInnerServer(bundle) + innerServer.Setup(pdClient) + store := tikv.NewMVCCStore(conf, bundle, conf.Engine.DBPath, safePoint, tikv.NewDBWriter(bundle), pdClient) + store.DeadlockDetectSvr.ChangeRole(tikv.Leader) + + if err := innerServer.Start(pdClient); err != nil { + return nil, err + } + + store.StartDeadlockDetection(false) + + return tikv.NewServer(rm, store, innerServer), nil +} + +func createDB(subPath string, safePoint *tikv.SafePoint, conf *config.Engine) (*badger.DB, error) { + opts := badger.DefaultOptions + opts.NumCompactors = conf.NumCompactors + opts.ValueThreshold = conf.ValueThreshold + if subPath == subPathRaft { + // Do not need to write blob for raft engine because it will be deleted soon. + return nil, errors.New("not support " + subPathRaft) + } + opts.ManagedTxns = true + opts.ValueLogWriteOptions.WriteBufferSize = 4 * 1024 * 1024 + opts.Dir = filepath.Join(conf.DBPath, subPath) + opts.ValueDir = opts.Dir + opts.ValueLogFileSize = conf.VlogFileSize + opts.ValueLogMaxNumFiles = 3 + opts.MaxMemTableSize = conf.MaxMemTableSize + opts.TableBuilderOptions.MaxTableSize = conf.MaxTableSize + opts.NumMemtables = conf.NumMemTables + opts.NumLevelZeroTables = conf.NumL0Tables + opts.NumLevelZeroTablesStall = conf.NumL0TablesStall + opts.LevelOneSize = conf.L1Size + opts.SyncWrites = conf.SyncWrite + compressionPerLevel := make([]options.CompressionType, len(conf.Compression)) + for i := range opts.TableBuilderOptions.CompressionPerLevel { + compressionPerLevel[i] = config.ParseCompression(conf.Compression[i]) + } + opts.TableBuilderOptions.CompressionPerLevel = compressionPerLevel + opts.MaxBlockCacheSize = conf.BlockCacheSize + opts.MaxIndexCacheSize = conf.IndexCacheSize + opts.TableBuilderOptions.SuRFStartLevel = conf.SurfStartLevel + if safePoint != nil { + opts.CompactionFilterFactory = safePoint.CreateCompactionFilter + } + opts.CompactL0WhenClose = conf.CompactL0WhenClose + opts.VolatileMode = conf.VolatileMode + return badger.Open(opts) +} diff --git a/pkg/store/mockstore/unistore/testutil.go b/pkg/store/mockstore/unistore/testutil.go new file mode 100644 index 0000000000000..fd957434f0afa --- /dev/null +++ b/pkg/store/mockstore/unistore/testutil.go @@ -0,0 +1,116 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package unistore + +import ( + "errors" + "fmt" + "runtime" + + "github.com/pingcap/tidb/pkg/tablecodec" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/tikv/client-go/v2/tikvrpc" +) + +func checkResourceTagForTopSQL(req *tikvrpc.Request) error { + if !topsqlstate.TopSQLEnabled() { + return nil + } + tag := req.GetResourceGroupTag() + if len(tag) > 0 { + return nil + } + + startKey, err := getReqStartKey(req) + if err != nil { + return err + } + var tid int64 + if tablecodec.IsRecordKey(startKey) { + tid, _, _ = tablecodec.DecodeRecordKey(startKey) + } + if tablecodec.IsIndexKey(startKey) { + tid, _, _, _ = tablecodec.DecodeIndexKey(startKey) + } + // since the error maybe "invalid record key", should just ignore check resource tag for this request. + if tid > 0 { + stack := getStack() + return fmt.Errorf("%v req does not set the resource tag, tid: %v, stack: %v", + req.Type.String(), tid, string(stack)) + } + return nil +} + +func getReqStartKey(req *tikvrpc.Request) ([]byte, error) { + switch req.Type { + case tikvrpc.CmdGet: + request := req.Get() + return request.Key, nil + case tikvrpc.CmdScan: + request := req.Scan() + return request.StartKey, nil + case tikvrpc.CmdPrewrite: + request := req.Prewrite() + return request.Mutations[0].Key, nil + case tikvrpc.CmdCommit: + request := req.Commit() + return request.Keys[0], nil + case tikvrpc.CmdCleanup: + request := req.Cleanup() + return request.Key, nil + case tikvrpc.CmdBatchGet: + request := req.BatchGet() + return request.Keys[0], nil + case tikvrpc.CmdBatchRollback: + request := req.BatchRollback() + return request.Keys[0], nil + case tikvrpc.CmdScanLock: + request := req.ScanLock() + return request.StartKey, nil + case tikvrpc.CmdPessimisticLock: + request := req.PessimisticLock() + return request.PrimaryLock, nil + case tikvrpc.CmdCheckSecondaryLocks: + request := req.CheckSecondaryLocks() + return request.Keys[0], nil + case tikvrpc.CmdCop, tikvrpc.CmdCopStream: + request := req.Cop() + return request.Ranges[0].Start, nil + case tikvrpc.CmdGC, tikvrpc.CmdDeleteRange, tikvrpc.CmdTxnHeartBeat, tikvrpc.CmdRawGet, + tikvrpc.CmdRawBatchGet, tikvrpc.CmdRawPut, tikvrpc.CmdRawBatchPut, tikvrpc.CmdRawDelete, tikvrpc.CmdRawBatchDelete, tikvrpc.CmdRawDeleteRange, + tikvrpc.CmdRawScan, tikvrpc.CmdGetKeyTTL, tikvrpc.CmdRawCompareAndSwap, tikvrpc.CmdUnsafeDestroyRange, tikvrpc.CmdRegisterLockObserver, + tikvrpc.CmdCheckLockObserver, tikvrpc.CmdRemoveLockObserver, tikvrpc.CmdPhysicalScanLock, tikvrpc.CmdStoreSafeTS, + tikvrpc.CmdLockWaitInfo, tikvrpc.CmdMvccGetByKey, tikvrpc.CmdMvccGetByStartTs, tikvrpc.CmdSplitRegion, + tikvrpc.CmdDebugGetRegionProperties, tikvrpc.CmdEmpty: + // Ignore those requests since now, since it is no business with TopSQL. + return nil, nil + case tikvrpc.CmdBatchCop, tikvrpc.CmdMPPTask, tikvrpc.CmdMPPConn, tikvrpc.CmdMPPCancel, tikvrpc.CmdMPPAlive: + // Ignore mpp requests. + return nil, nil + case tikvrpc.CmdResolveLock, tikvrpc.CmdCheckTxnStatus, tikvrpc.CmdPessimisticRollback: + // TODO: add resource tag for those request. https://github.com/pingcap/tidb/issues/33621 + return nil, nil + default: + return nil, errors.New("unknown request, check the new type RPC request here") + } +} + +func getStack() []byte { + const size = 1024 * 64 + buf := make([]byte, size) + stackSize := runtime.Stack(buf, false) + buf = buf[:stackSize] + return buf +} diff --git a/pkg/store/mockstore/unistore/tikv/BUILD.bazel b/pkg/store/mockstore/unistore/tikv/BUILD.bazel new file mode 100644 index 0000000000000..793f41ef45357 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/BUILD.bazel @@ -0,0 +1,89 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tikv", + srcs = [ + "deadlock.go", + "detector.go", + "inner_server.go", + "mock_region.go", + "mvcc.go", + "region.go", + "server.go", + "server_batch.go", + "util.go", + "write.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/sessionctx/stmtctx", + "//pkg/store/mockstore/unistore/client", + "//pkg/store/mockstore/unistore/config", + "//pkg/store/mockstore/unistore/cophandler", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/metrics", + "//pkg/store/mockstore/unistore/pd", + "//pkg/store/mockstore/unistore/tikv/dbreader", + "//pkg/store/mockstore/unistore/tikv/kverrors", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "//pkg/store/mockstore/unistore/tikv/pberror", + "//pkg/store/mockstore/unistore/util/lockwaiter", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/mathutil", + "//pkg/util/rowcodec", + "@com_github_dgryski_go_farm//:go-farm", + "@com_github_gogo_protobuf//proto", + "@com_github_google_btree//:btree", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/errorpb", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/mpp", + "@com_github_pingcap_kvproto//pkg/pdpb", + "@com_github_pingcap_kvproto//pkg/tikvpb", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_pd_client//:client", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "tikv_test", + timeout = "short", + srcs = [ + "detector_test.go", + "main_test.go", + "mvcc_test.go", + ], + embed = [":tikv"], + flaky = True, + shard_count = 28, + deps = [ + "//pkg/store/mockstore/unistore/config", + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/store/mockstore/unistore/tikv/kverrors", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "//pkg/store/mockstore/unistore/util/lockwaiter", + "//pkg/testkit/testsetup", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/store/mockstore/unistore/tikv/dbreader/BUILD.bazel b/pkg/store/mockstore/unistore/tikv/dbreader/BUILD.bazel new file mode 100644 index 0000000000000..973cc126dde80 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/dbreader/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "dbreader", + srcs = ["db_reader.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader", + visibility = ["//visibility:public"], + deps = [ + "//pkg/store/mockstore/unistore/tikv/kverrors", + "//pkg/store/mockstore/unistore/tikv/mvcc", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + ], +) diff --git a/store/mockstore/unistore/tikv/dbreader/db_reader.go b/pkg/store/mockstore/unistore/tikv/dbreader/db_reader.go similarity index 98% rename from store/mockstore/unistore/tikv/dbreader/db_reader.go rename to pkg/store/mockstore/unistore/tikv/dbreader/db_reader.go index e88099b4a21fc..964446f0defb6 100644 --- a/store/mockstore/unistore/tikv/dbreader/db_reader.go +++ b/pkg/store/mockstore/unistore/tikv/dbreader/db_reader.go @@ -36,8 +36,8 @@ import ( "github.com/pingcap/badger/y" "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" ) // NewDBReader returns a new *DBReader. diff --git a/store/mockstore/unistore/tikv/deadlock.go b/pkg/store/mockstore/unistore/tikv/deadlock.go similarity index 97% rename from store/mockstore/unistore/tikv/deadlock.go rename to pkg/store/mockstore/unistore/tikv/deadlock.go index 7e8b0179e082d..fd4e90e35f9e5 100644 --- a/store/mockstore/unistore/tikv/deadlock.go +++ b/pkg/store/mockstore/unistore/tikv/deadlock.go @@ -22,9 +22,9 @@ import ( deadlockPb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/pd" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/store/mockstore/unistore/util/lockwaiter" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/util/lockwaiter" "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" diff --git a/store/mockstore/unistore/tikv/detector.go b/pkg/store/mockstore/unistore/tikv/detector.go similarity index 98% rename from store/mockstore/unistore/tikv/detector.go rename to pkg/store/mockstore/unistore/tikv/detector.go index bbff15e42034b..5bfddce417625 100644 --- a/store/mockstore/unistore/tikv/detector.go +++ b/pkg/store/mockstore/unistore/tikv/detector.go @@ -34,7 +34,7 @@ import ( deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" "go.uber.org/zap" ) diff --git a/store/mockstore/unistore/tikv/detector_test.go b/pkg/store/mockstore/unistore/tikv/detector_test.go similarity index 100% rename from store/mockstore/unistore/tikv/detector_test.go rename to pkg/store/mockstore/unistore/tikv/detector_test.go diff --git a/store/mockstore/unistore/tikv/inner_server.go b/pkg/store/mockstore/unistore/tikv/inner_server.go similarity index 94% rename from store/mockstore/unistore/tikv/inner_server.go rename to pkg/store/mockstore/unistore/tikv/inner_server.go index 38327558cda44..912b77a3b36e2 100644 --- a/store/mockstore/unistore/tikv/inner_server.go +++ b/pkg/store/mockstore/unistore/tikv/inner_server.go @@ -16,8 +16,8 @@ package tikv import ( "github.com/pingcap/kvproto/pkg/tikvpb" - "github.com/pingcap/tidb/store/mockstore/unistore/pd" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" ) // InnerServer defines the inner server interface. diff --git a/pkg/store/mockstore/unistore/tikv/kverrors/BUILD.bazel b/pkg/store/mockstore/unistore/tikv/kverrors/BUILD.bazel new file mode 100644 index 0000000000000..60d4107df45f7 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/kverrors/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "kverrors", + srcs = ["errors.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors", + visibility = ["//visibility:public"], + deps = [ + "//pkg/store/mockstore/unistore/tikv/mvcc", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + ], +) diff --git a/pkg/store/mockstore/unistore/tikv/kverrors/errors.go b/pkg/store/mockstore/unistore/tikv/kverrors/errors.go new file mode 100644 index 0000000000000..e78212b4f1143 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/kverrors/errors.go @@ -0,0 +1,160 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package kverrors + +import ( + "encoding/hex" + "fmt" + + deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" +) + +// ErrLocked is returned when trying to Read/Write on a locked key. Client should +// backoff or cleanup the lock then retry. +type ErrLocked struct { + Key []byte + Lock *mvcc.Lock +} + +// BuildLockErr generates ErrKeyLocked objects +func BuildLockErr(key []byte, lock *mvcc.Lock) *ErrLocked { + errLocked := &ErrLocked{ + Key: key, + Lock: lock, + } + return errLocked +} + +// Error formats the lock to a string. +func (e *ErrLocked) Error() string { + lock := e.Lock + return fmt.Sprintf( + "key is locked, key: %v, lock: %v", + hex.EncodeToString(e.Key), lock.String(), + ) +} + +// ErrRetryable suggests that client may restart the txn. e.g. write conflict. +type ErrRetryable string + +func (e ErrRetryable) Error() string { + return fmt.Sprintf("retryable: %s", string(e)) +} + +// ErrRetryable +var ( + ErrLockNotFound = ErrRetryable("lock not found") + ErrAlreadyRollback = ErrRetryable("already rollback") + ErrReplaced = ErrRetryable("replaced by another transaction") +) + +// ErrInvalidOp is returned when an operation cannot be completed. +type ErrInvalidOp struct { + Op kvrpcpb.Op +} + +func (e ErrInvalidOp) Error() string { + return fmt.Sprintf("invalid op: %s", e.Op.String()) +} + +// ErrAlreadyCommitted is returned specially when client tries to rollback a +// committed lock. +type ErrAlreadyCommitted uint64 + +func (ErrAlreadyCommitted) Error() string { + return "txn already committed" +} + +// ErrKeyAlreadyExists is returned when a key already exists. +type ErrKeyAlreadyExists struct { + Key []byte +} + +func (ErrKeyAlreadyExists) Error() string { + return "key already exists" +} + +// ErrDeadlock is returned when deadlock is detected. +type ErrDeadlock struct { + LockKey []byte + LockTS uint64 + DeadlockKeyHash uint64 + WaitChain []*deadlockpb.WaitForEntry +} + +func (ErrDeadlock) Error() string { + return "deadlock" +} + +// ErrConflict is the error when the commit meets an write conflict error. +type ErrConflict struct { + StartTS uint64 + ConflictTS uint64 + ConflictCommitTS uint64 + Key []byte + Reason kvrpcpb.WriteConflict_Reason +} + +func (*ErrConflict) Error() string { + return "write conflict" +} + +// ErrCommitExpire is returned when commit key commitTs smaller than lock.MinCommitTs +type ErrCommitExpire struct { + StartTs uint64 + CommitTs uint64 + MinCommitTs uint64 + Key []byte +} + +func (*ErrCommitExpire) Error() string { + return "commit expired" +} + +// ErrTxnNotFound is returned if the required txn info not found on storage +type ErrTxnNotFound struct { + StartTS uint64 + PrimaryKey []byte +} + +func (*ErrTxnNotFound) Error() string { + return "txn not found" +} + +// ErrAssertionFailed is returned if any assertion fails on a transaction request. +type ErrAssertionFailed struct { + StartTS uint64 + Key []byte + Assertion kvrpcpb.Assertion + ExistingStartTS uint64 + ExistingCommitTS uint64 +} + +func (e *ErrAssertionFailed) Error() string { + return fmt.Sprintf("AssertionFailed { StartTS: %v, Key: %v, Assertion: %v, ExistingStartTS: %v, ExistingCommitTS: %v }", + e.StartTS, hex.EncodeToString(e.Key), e.Assertion.String(), e.ExistingStartTS, e.ExistingCommitTS) +} + +// ErrPrimaryMismatch is returned if CheckTxnStatus request is sent to a secondary lock. +type ErrPrimaryMismatch struct { + Key []byte + Lock *mvcc.Lock +} + +func (e *ErrPrimaryMismatch) Error() string { + return fmt.Sprintf("primary mismatch, key: %v, lock: %v", hex.EncodeToString(e.Key), e.Lock.String()) +} diff --git a/pkg/store/mockstore/unistore/tikv/main_test.go b/pkg/store/mockstore/unistore/tikv/main_test.go new file mode 100644 index 0000000000000..68820867df647 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tikv + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/store/mockstore/unistore/tikv/mock_region.go b/pkg/store/mockstore/unistore/tikv/mock_region.go similarity index 98% rename from store/mockstore/unistore/tikv/mock_region.go rename to pkg/store/mockstore/unistore/tikv/mock_region.go index 7bd9c5085df62..9ca2d782fb4eb 100644 --- a/store/mockstore/unistore/tikv/mock_region.go +++ b/pkg/store/mockstore/unistore/tikv/mock_region.go @@ -31,12 +31,12 @@ import ( "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore/unistore/cophandler" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/tikv/client-go/v2/oracle" pdclient "github.com/tikv/pd/client" ) diff --git a/pkg/store/mockstore/unistore/tikv/mvcc.go b/pkg/store/mockstore/unistore/tikv/mvcc.go new file mode 100644 index 0000000000000..4a1fcf34b784b --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/mvcc.go @@ -0,0 +1,1966 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package tikv + +import ( + "bufio" + "bytes" + "cmp" + "context" + "fmt" + "math" + "os" + "slices" + "sort" + "sync/atomic" + "time" + "unsafe" + + "github.com/dgryski/go-farm" + "github.com/pingcap/badger" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/util/lockwaiter" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/tikv/client-go/v2/oracle" + "go.uber.org/zap" +) + +// MVCCStore is a wrapper of badger.DB to provide MVCC functions. +type MVCCStore struct { + dir string + db *badger.DB + lockStore *lockstore.MemStore + dbWriter mvcc.DBWriter + safePoint *SafePoint + pdClient pd.Client + closeCh chan bool + + conf *config.Config + + latestTS uint64 + lockWaiterManager *lockwaiter.Manager + DeadlockDetectCli *DetectorClient + DeadlockDetectSvr *DetectorServer +} + +// NewMVCCStore creates a new MVCCStore +func NewMVCCStore(conf *config.Config, bundle *mvcc.DBBundle, dataDir string, safePoint *SafePoint, + writer mvcc.DBWriter, pdClient pd.Client) *MVCCStore { + store := &MVCCStore{ + db: bundle.DB, + dir: dataDir, + lockStore: bundle.LockStore, + safePoint: safePoint, + pdClient: pdClient, + closeCh: make(chan bool), + dbWriter: writer, + conf: conf, + lockWaiterManager: lockwaiter.NewManager(conf), + } + store.DeadlockDetectSvr = NewDetectorServer() + store.DeadlockDetectCli = NewDetectorClient(store.lockWaiterManager, pdClient) + writer.Open() + if pdClient != nil { + // pdClient is nil in unit test. + go store.runUpdateSafePointLoop() + } + return store +} + +func (store *MVCCStore) updateLatestTS(ts uint64) { + for { + old := atomic.LoadUint64(&store.latestTS) + if old < ts { + if !atomic.CompareAndSwapUint64(&store.latestTS, old, ts) { + continue + } + } + return + } +} + +func (store *MVCCStore) getLatestTS() uint64 { + return atomic.LoadUint64(&store.latestTS) +} + +// Close closes the MVCCStore. +func (store *MVCCStore) Close() error { + store.dbWriter.Close() + close(store.closeCh) + + err := store.dumpMemLocks() + if err != nil { + log.Fatal("dump mem locks failed", zap.Error(err)) + } + return nil +} + +type lockEntryHdr struct { + keyLen uint32 + valLen uint32 +} + +func (store *MVCCStore) dumpMemLocks() error { + tmpFileName := store.dir + "/lock_store.tmp" + f, err := os.OpenFile(tmpFileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) + if err != nil { + return errors.Trace(err) + } + writer := bufio.NewWriter(f) + cnt := 0 + it := store.lockStore.NewIterator() + hdrBuf := make([]byte, 8) + hdr := (*lockEntryHdr)(unsafe.Pointer(&hdrBuf[0])) + for it.SeekToFirst(); it.Valid(); it.Next() { + hdr.keyLen = uint32(len(it.Key())) + hdr.valLen = uint32(len(it.Value())) + _, err = writer.Write(hdrBuf) + if err != nil { + return errors.Trace(err) + } + _, err = writer.Write(it.Key()) + if err != nil { + return errors.Trace(err) + } + _, err = writer.Write(it.Value()) + if err != nil { + return errors.Trace(err) + } + cnt++ + } + err = writer.Flush() + if err != nil { + return errors.Trace(err) + } + err = f.Sync() + if err != nil { + return errors.Trace(err) + } + err = f.Close() + if err != nil { + return errors.Trace(err) + } + return os.Rename(tmpFileName, store.dir+"/lock_store") +} + +func (store *MVCCStore) getDBItems(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation) (items []*badger.Item, err error) { + txn := reqCtx.getDBReader().GetTxn() + keys := make([][]byte, len(mutations)) + for i, m := range mutations { + keys[i] = m.Key + } + return txn.MultiGet(keys) +} + +func sortMutations(mutations []*kvrpcpb.Mutation) []*kvrpcpb.Mutation { + fn := func(i, j *kvrpcpb.Mutation) int { + return bytes.Compare(i.Key, j.Key) + } + if slices.IsSortedFunc(mutations, fn) { + return mutations + } + slices.SortFunc(mutations, fn) + return mutations +} + +func sortPrewrite(req *kvrpcpb.PrewriteRequest) []*kvrpcpb.Mutation { + if len(req.PessimisticActions) == 0 { + return sortMutations(req.Mutations) + } + sorter := pessimisticPrewriteSorter{PrewriteRequest: req} + if sort.IsSorted(sorter) { + return req.Mutations + } + sort.Sort(sorter) + return req.Mutations +} + +type pessimisticPrewriteSorter struct { + *kvrpcpb.PrewriteRequest +} + +func (sorter pessimisticPrewriteSorter) Less(i, j int) bool { + return bytes.Compare(sorter.Mutations[i].Key, sorter.Mutations[j].Key) < 0 +} + +func (sorter pessimisticPrewriteSorter) Len() int { + return len(sorter.Mutations) +} + +func (sorter pessimisticPrewriteSorter) Swap(i, j int) { + sorter.Mutations[i], sorter.Mutations[j] = sorter.Mutations[j], sorter.Mutations[i] + sorter.PessimisticActions[i], sorter.PessimisticActions[j] = sorter.PessimisticActions[j], sorter.PessimisticActions[i] +} + +func sortKeys(keys [][]byte) [][]byte { + if slices.IsSortedFunc(keys, bytes.Compare) { + return keys + } + slices.SortFunc(keys, bytes.Compare) + return keys +} + +// PessimisticLock will add pessimistic lock on key +func (store *MVCCStore) PessimisticLock(reqCtx *requestCtx, req *kvrpcpb.PessimisticLockRequest, resp *kvrpcpb.PessimisticLockResponse) (*lockwaiter.Waiter, error) { + waiter, err := store.pessimisticLockInner(reqCtx, req, resp) + if err != nil && req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { + // The execution of `pessimisticLockInner` is broken by error. If resp.Results is not completely set yet, fill it with LockResultFailed. + for len(resp.Results) < len(req.Mutations) { + resp.Results = append(resp.Results, &kvrpcpb.PessimisticLockKeyResult{ + Type: kvrpcpb.PessimisticLockKeyResultType_LockResultFailed, + }) + } + } + + return waiter, err +} + +func (store *MVCCStore) pessimisticLockInner(reqCtx *requestCtx, req *kvrpcpb.PessimisticLockRequest, resp *kvrpcpb.PessimisticLockResponse) (*lockwaiter.Waiter, error) { + mutations := req.Mutations + if !req.ReturnValues { + mutations = sortMutations(req.Mutations) + } + startTS := req.StartVersion + regCtx := reqCtx.regCtx + hashVals := mutationsToHashVals(mutations) + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + if req.LockOnlyIfExists && !req.ReturnValues { + return nil, errors.New("LockOnlyIfExists is set for LockKeys but ReturnValues is not set") + } + if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock && len(req.Mutations) > 1 { + return nil, errors.New("Trying to lock more than one key in WakeUpModeForceLock, which is not supported yet") + } + batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) + var dup bool + for _, m := range mutations { + lock, err := store.checkConflictInLockStore(reqCtx, m, startTS) + if err != nil { + var resourceGroupTag []byte + if req.Context != nil { + resourceGroupTag = req.Context.ResourceGroupTag + } + return store.handleCheckPessimisticErr(startTS, err, req.IsFirstLock, req.WaitTimeout, m.Key, resourceGroupTag) + } + if lock != nil { + if lock.Op != uint8(kvrpcpb.Op_PessimisticLock) { + return nil, errors.New("lock type not match") + } + if lock.ForUpdateTS >= req.ForUpdateTs { + // It's a duplicate command, we can simply return values. + dup = true + break + } + // Single statement rollback key, we can overwrite it. + } + if bytes.Equal(m.Key, req.PrimaryLock) { + txnStatus := store.checkExtraTxnStatus(reqCtx, m.Key, startTS) + if txnStatus.isRollback { + return nil, kverrors.ErrAlreadyRollback + } else if txnStatus.isOpLockCommitted() { + dup = true + break + } + } + } + items, err := store.getDBItems(reqCtx, mutations) + lockedWithConflictTSList := make([]uint64, 0, len(mutations)) + if err != nil { + return nil, err + } + if !dup { + for i, m := range mutations { + lock, lockedWithConflictTS, err1 := store.buildPessimisticLock(m, items[i], req) + lockedWithConflictTSList = append(lockedWithConflictTSList, lockedWithConflictTS) + if err1 != nil { + return nil, err1 + } + if lock == nil { + continue + } + batch.PessimisticLock(m.Key, lock) + } + err = store.dbWriter.Write(batch) + if err != nil { + return nil, err + } + } + if req.Force { + dbMeta := mvcc.DBUserMeta(items[0].UserMeta()) + val, err1 := items[0].ValueCopy(nil) + if err1 != nil { + return nil, err1 + } + resp.Value = val + resp.CommitTs = dbMeta.CommitTS() + } + + if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeNormal { + if req.ReturnValues || req.CheckExistence { + for _, item := range items { + if item == nil { + if req.ReturnValues { + resp.Values = append(resp.Values, nil) + } + resp.NotFounds = append(resp.NotFounds, true) + continue + } + val, err1 := item.ValueCopy(nil) + if err1 != nil { + return nil, err1 + } + if req.ReturnValues { + resp.Values = append(resp.Values, val) + } + resp.NotFounds = append(resp.NotFounds, len(val) == 0) + } + } + } else if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { + for i, item := range items { + res := &kvrpcpb.PessimisticLockKeyResult{ + Type: kvrpcpb.PessimisticLockKeyResultType_LockResultNormal, + Value: nil, + Existence: false, + LockedWithConflictTs: 0, + } + + if lockedWithConflictTSList[i] != 0 { + res.Type = kvrpcpb.PessimisticLockKeyResultType_LockResultLockedWithConflict + res.LockedWithConflictTs = lockedWithConflictTSList[i] + if item == nil { + res.Value = nil + res.Existence = false + } else { + val, err1 := item.ValueCopy(nil) + if err1 != nil { + return nil, err1 + } + res.Value = val + res.Existence = len(val) != 0 + } + } else if req.ReturnValues { + if item != nil { + val, err1 := item.ValueCopy(nil) + if err1 != nil { + return nil, err1 + } + res.Value = val + res.Existence = len(val) != 0 + } + } else if req.CheckExistence { + if item != nil { + val, err1 := item.ValueCopy(nil) + if err1 != nil { + return nil, err1 + } + res.Existence = len(val) != 0 + } + } + + resp.Results = append(resp.Results, res) + } + } else { + panic("unreachable") + } + return nil, err +} + +// extraTxnStatus can be rollback or Op_Lock that only contains transaction status info, no values. +type extraTxnStatus struct { + commitTS uint64 + isRollback bool +} + +func (s extraTxnStatus) isOpLockCommitted() bool { + return s.commitTS > 0 +} + +func (store *MVCCStore) checkExtraTxnStatus(reqCtx *requestCtx, key []byte, startTS uint64) extraTxnStatus { + txn := reqCtx.getDBReader().GetTxn() + txnStatusKey := mvcc.EncodeExtraTxnStatusKey(key, startTS) + item, err := txn.Get(txnStatusKey) + if err != nil { + return extraTxnStatus{} + } + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if userMeta.CommitTS() == 0 { + return extraTxnStatus{isRollback: true} + } + return extraTxnStatus{commitTS: userMeta.CommitTS()} +} + +// PessimisticRollback implements the MVCCStore interface. +func (store *MVCCStore) PessimisticRollback(reqCtx *requestCtx, req *kvrpcpb.PessimisticRollbackRequest) error { + keys := sortKeys(req.Keys) + hashVals := keysToHashVals(keys...) + regCtx := reqCtx.regCtx + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + startTS := req.StartVersion + var batch mvcc.WriteBatch + for _, k := range keys { + lock := store.getLock(reqCtx, k) + if lock != nil && + lock.Op == uint8(kvrpcpb.Op_PessimisticLock) && + lock.StartTS == startTS && + lock.ForUpdateTS <= req.ForUpdateTs { + if batch == nil { + batch = store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) + } + batch.PessimisticRollback(k) + } + } + var err error + if batch != nil { + err = store.dbWriter.Write(batch) + } + store.lockWaiterManager.WakeUp(startTS, 0, hashVals) + store.DeadlockDetectCli.CleanUp(startTS) + return err +} + +// TxnHeartBeat implements the MVCCStore interface. +func (store *MVCCStore) TxnHeartBeat(reqCtx *requestCtx, req *kvrpcpb.TxnHeartBeatRequest) (lockTTL uint64, err error) { + hashVals := keysToHashVals(req.PrimaryLock) + regCtx := reqCtx.regCtx + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + lock := store.getLock(reqCtx, req.PrimaryLock) + if lock != nil && lock.StartTS == req.StartVersion { + if !bytes.Equal(lock.Primary, req.PrimaryLock) { + return 0, errors.New("heartbeat on non-primary key") + } + if lock.TTL < uint32(req.AdviseLockTtl) { + lock.TTL = uint32(req.AdviseLockTtl) + batch := store.dbWriter.NewWriteBatch(req.StartVersion, 0, reqCtx.rpcCtx) + batch.PessimisticLock(req.PrimaryLock, lock) + err = store.dbWriter.Write(batch) + if err != nil { + return 0, err + } + } + return uint64(lock.TTL), nil + } + return 0, errors.New("lock doesn't exists") +} + +// TxnStatus is the result of `CheckTxnStatus` API. +type TxnStatus struct { + commitTS uint64 + action kvrpcpb.Action + lockInfo *kvrpcpb.LockInfo +} + +// CheckTxnStatus implements the MVCCStore interface. +func (store *MVCCStore) CheckTxnStatus(reqCtx *requestCtx, + req *kvrpcpb.CheckTxnStatusRequest) (txnStatusRes TxnStatus, err error) { + hashVals := keysToHashVals(req.PrimaryKey) + regCtx := reqCtx.regCtx + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + lock := store.getLock(reqCtx, req.PrimaryKey) + batch := store.dbWriter.NewWriteBatch(req.LockTs, 0, reqCtx.rpcCtx) + if lock != nil && lock.StartTS == req.LockTs { + if !bytes.Equal(req.PrimaryKey, lock.Primary) { + return TxnStatus{}, &kverrors.ErrPrimaryMismatch{ + Key: req.PrimaryKey, + Lock: lock, + } + } + + // For an async-commit lock, never roll it back or push forward it MinCommitTS. + if lock.UseAsyncCommit && !req.ForceSyncCommit { + log.S().Debugf("async commit startTS=%v secondaries=%v minCommitTS=%v", lock.StartTS, lock.Secondaries, lock.MinCommitTS) + return TxnStatus{0, kvrpcpb.Action_NoAction, lock.ToLockInfo(req.PrimaryKey)}, nil + } + + // If the lock has already outdated, clean up it. + if uint64(oracle.ExtractPhysical(lock.StartTS))+uint64(lock.TTL) < uint64(oracle.ExtractPhysical(req.CurrentTs)) { + // If the resolving lock and primary lock are both pessimistic type, just pessimistic rollback locks. + if req.ResolvingPessimisticLock && lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { + batch.PessimisticRollback(req.PrimaryKey) + return TxnStatus{0, kvrpcpb.Action_TTLExpirePessimisticRollback, nil}, store.dbWriter.Write(batch) + } + batch.Rollback(req.PrimaryKey, true) + return TxnStatus{0, kvrpcpb.Action_TTLExpireRollback, nil}, store.dbWriter.Write(batch) + } + // If this is a large transaction and the lock is active, push forward the minCommitTS. + // lock.minCommitTS == 0 may be a secondary lock, or not a large transaction. + // For async commit protocol, the minCommitTS is always greater than zero, but async commit will not be a large transaction. + action := kvrpcpb.Action_NoAction + if req.CallerStartTs == maxSystemTS { + action = kvrpcpb.Action_MinCommitTSPushed + } else if lock.MinCommitTS > 0 && !lock.UseAsyncCommit { + action = kvrpcpb.Action_MinCommitTSPushed + // We *must* guarantee the invariance lock.minCommitTS >= callerStartTS + 1 + if lock.MinCommitTS < req.CallerStartTs+1 { + lock.MinCommitTS = req.CallerStartTs + 1 + + // Remove this condition should not affect correctness. + // We do it because pushing forward minCommitTS as far as possible could avoid + // the lock been pushed again several times, and thus reduce write operations. + if lock.MinCommitTS < req.CurrentTs { + lock.MinCommitTS = req.CurrentTs + } + batch.PessimisticLock(req.PrimaryKey, lock) + if err = store.dbWriter.Write(batch); err != nil { + return TxnStatus{0, action, nil}, err + } + } + } + return TxnStatus{0, action, lock.ToLockInfo(req.PrimaryKey)}, nil + } + + // The current transaction lock not exists, check the transaction commit info + commitTS, err := store.checkCommitted(reqCtx.getDBReader(), req.PrimaryKey, req.LockTs) + if commitTS > 0 { + return TxnStatus{commitTS, kvrpcpb.Action_NoAction, nil}, nil + } + // Check if the transaction already rollbacked + status := store.checkExtraTxnStatus(reqCtx, req.PrimaryKey, req.LockTs) + if status.isRollback { + return TxnStatus{0, kvrpcpb.Action_NoAction, nil}, nil + } + if status.isOpLockCommitted() { + commitTS = status.commitTS + return TxnStatus{commitTS, kvrpcpb.Action_NoAction, nil}, nil + } + // If current transaction is not prewritted before, it may be pessimistic lock. + // When pessimistic txn rollback statement, it may not leave a 'rollbacked' tombstone. + // Or maybe caused by concurrent prewrite operation. + // Especially in the non-block reading case, the secondary lock is likely to be + // written before the primary lock. + // Currently client will always set this flag to true when resolving locks + if req.RollbackIfNotExist { + if req.ResolvingPessimisticLock { + return TxnStatus{0, kvrpcpb.Action_LockNotExistDoNothing, nil}, nil + } + batch.Rollback(req.PrimaryKey, false) + err = store.dbWriter.Write(batch) + return TxnStatus{0, kvrpcpb.Action_LockNotExistRollback, nil}, nil + } + return TxnStatus{0, kvrpcpb.Action_NoAction, nil}, &kverrors.ErrTxnNotFound{ + PrimaryKey: req.PrimaryKey, + StartTS: req.LockTs, + } +} + +// SecondaryLocksStatus is the result of `CheckSecondaryLocksStatus` API. +type SecondaryLocksStatus struct { + locks []*kvrpcpb.LockInfo + commitTS uint64 +} + +// CheckSecondaryLocks implements the MVCCStore interface. +func (store *MVCCStore) CheckSecondaryLocks(reqCtx *requestCtx, keys [][]byte, startTS uint64) (SecondaryLocksStatus, error) { + sortKeys(keys) + hashVals := keysToHashVals(keys...) + log.S().Debugf("%d check secondary %v", startTS, hashVals) + regCtx := reqCtx.regCtx + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) + locks := make([]*kvrpcpb.LockInfo, 0, len(keys)) + for i, key := range keys { + lock := store.getLock(reqCtx, key) + if !(lock != nil && lock.StartTS == startTS) { + commitTS, err := store.checkCommitted(reqCtx.getDBReader(), key, startTS) + if err != nil { + return SecondaryLocksStatus{}, err + } + if commitTS > 0 { + return SecondaryLocksStatus{commitTS: commitTS}, nil + } + status := store.checkExtraTxnStatus(reqCtx, key, startTS) + if status.isOpLockCommitted() { + return SecondaryLocksStatus{commitTS: status.commitTS}, nil + } + if !status.isRollback { + batch.Rollback(key, false) + err = store.dbWriter.Write(batch) + } + return SecondaryLocksStatus{commitTS: 0}, err + } + if lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { + batch.Rollback(key, true) + err := store.dbWriter.Write(batch) + if err != nil { + return SecondaryLocksStatus{}, err + } + store.lockWaiterManager.WakeUp(startTS, 0, []uint64{hashVals[i]}) + store.DeadlockDetectCli.CleanUp(startTS) + return SecondaryLocksStatus{commitTS: 0}, nil + } + locks = append(locks, lock.ToLockInfo(key)) + } + return SecondaryLocksStatus{locks: locks}, nil +} + +func (store *MVCCStore) normalizeWaitTime(lockWaitTime int64) time.Duration { + if lockWaitTime > store.conf.PessimisticTxn.WaitForLockTimeout { + lockWaitTime = store.conf.PessimisticTxn.WaitForLockTimeout + } else if lockWaitTime == 0 { + lockWaitTime = store.conf.PessimisticTxn.WaitForLockTimeout + } + return time.Duration(lockWaitTime) * time.Millisecond +} + +func (store *MVCCStore) handleCheckPessimisticErr(startTS uint64, err error, isFirstLock bool, lockWaitTime int64, key []byte, resourceGroupTag []byte) (*lockwaiter.Waiter, error) { + if locked, ok := err.(*kverrors.ErrLocked); ok { + if lockWaitTime != lockwaiter.LockNoWait { + keyHash := farm.Fingerprint64(locked.Key) + waitTimeDuration := store.normalizeWaitTime(lockWaitTime) + lock := locked.Lock + log.S().Debugf("%d blocked by %d on key %d", startTS, lock.StartTS, keyHash) + waiter := store.lockWaiterManager.NewWaiter(startTS, lock.StartTS, keyHash, waitTimeDuration) + if !isFirstLock { + store.DeadlockDetectCli.Detect(startTS, lock.StartTS, keyHash, key, resourceGroupTag) + } + return waiter, err + } + } + return nil, err +} + +// buildPessimisticLock builds the lock according to the request and the current state of the key. +// Returns the built lock, and the LockedWithConflictTS (if any, otherwise 0). +func (store *MVCCStore) buildPessimisticLock(m *kvrpcpb.Mutation, item *badger.Item, + req *kvrpcpb.PessimisticLockRequest) (*mvcc.Lock, uint64, error) { + var lockedWithConflictTS uint64 = 0 + + var writeConflictError error + + if item != nil { + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if !req.Force { + if userMeta.CommitTS() > req.ForUpdateTs { + writeConflictError = &kverrors.ErrConflict{ + StartTS: req.StartVersion, + ConflictTS: userMeta.StartTS(), + ConflictCommitTS: userMeta.CommitTS(), + Key: item.KeyCopy(nil), + Reason: kvrpcpb.WriteConflict_PessimisticRetry, + } + + if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeNormal { + return nil, 0, writeConflictError + } else if req.GetWakeUpMode() != kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { + panic("unreachable") + } + lockedWithConflictTS = userMeta.CommitTS() + } + } + if m.Assertion == kvrpcpb.Assertion_NotExist && !item.IsEmpty() { + if lockedWithConflictTS != 0 { + // If constraint is violated, disable locking with conflict behavior. + if writeConflictError == nil { + panic("unreachable") + } + return nil, 0, writeConflictError + } + return nil, 0, &kverrors.ErrKeyAlreadyExists{Key: m.Key} + } + } + + if ok, err := doesNeedLock(item, req); !ok { + if err != nil { + return nil, 0, err + } + if lockedWithConflictTS > 0 { + // If lockIfOnlyExist is used on a not-existing key, disable locking with conflict behavior. + if writeConflictError == nil { + panic("unreachable") + } + return nil, 0, writeConflictError + } + return nil, 0, nil + } + actualWrittenForUpdateTS := req.ForUpdateTs + if lockedWithConflictTS > 0 { + actualWrittenForUpdateTS = lockedWithConflictTS + } + lock := &mvcc.Lock{ + LockHdr: mvcc.LockHdr{ + StartTS: req.StartVersion, + ForUpdateTS: actualWrittenForUpdateTS, + Op: uint8(kvrpcpb.Op_PessimisticLock), + TTL: uint32(req.LockTtl), + PrimaryLen: uint16(len(req.PrimaryLock)), + }, + Primary: req.PrimaryLock, + } + return lock, lockedWithConflictTS, nil +} + +// Prewrite implements the MVCCStore interface. +func (store *MVCCStore) Prewrite(reqCtx *requestCtx, req *kvrpcpb.PrewriteRequest) error { + mutations := sortPrewrite(req) + regCtx := reqCtx.regCtx + hashVals := mutationsToHashVals(mutations) + + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + isPessimistic := req.ForUpdateTs > 0 + var err error + if isPessimistic { + err = store.prewritePessimistic(reqCtx, mutations, req) + } else { + err = store.prewriteOptimistic(reqCtx, mutations, req) + } + if err != nil { + return err + } + + if reqCtx.onePCCommitTS != 0 { + // TODO: Is it correct to pass the hashVals directly here, considering that some of the keys may + // have no pessimistic lock? + if isPessimistic { + store.lockWaiterManager.WakeUp(req.StartVersion, reqCtx.onePCCommitTS, hashVals) + store.DeadlockDetectCli.CleanUp(req.StartVersion) + } + } + return nil +} + +func (store *MVCCStore) prewriteOptimistic(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, req *kvrpcpb.PrewriteRequest) error { + startTS := req.StartVersion + // Must check the LockStore first. + for _, m := range mutations { + lock, err := store.checkConflictInLockStore(reqCtx, m, startTS) + if err != nil { + return err + } + if lock != nil { + // duplicated command + return nil + } + if bytes.Equal(m.Key, req.PrimaryLock) { + status := store.checkExtraTxnStatus(reqCtx, m.Key, req.StartVersion) + if status.isRollback { + return kverrors.ErrAlreadyRollback + } + if status.isOpLockCommitted() { + // duplicated command + return nil + } + } + } + items, err := store.getDBItems(reqCtx, mutations) + if err != nil { + return err + } + for i, m := range mutations { + item := items[i] + if item != nil { + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if userMeta.CommitTS() > startTS { + return &kverrors.ErrConflict{ + StartTS: startTS, + ConflictTS: userMeta.StartTS(), + ConflictCommitTS: userMeta.CommitTS(), + Key: item.KeyCopy(nil), + Reason: kvrpcpb.WriteConflict_Optimistic, + } + } + } + // Op_CheckNotExists type requests should not add lock + if m.Op == kvrpcpb.Op_CheckNotExists { + if item != nil { + val, err := item.Value() + if err != nil { + return err + } + if len(val) > 0 { + return &kverrors.ErrKeyAlreadyExists{Key: m.Key} + } + } + continue + } + // TODO add memory lock for async commit protocol. + } + return store.prewriteMutations(reqCtx, mutations, req, items) +} + +func (store *MVCCStore) prewritePessimistic(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, req *kvrpcpb.PrewriteRequest) error { + startTS := req.StartVersion + + expectedForUpdateTSMap := make(map[int]uint64, len(req.GetForUpdateTsConstraints())) + for _, constraint := range req.GetForUpdateTsConstraints() { + index := int(constraint.Index) + if index >= len(mutations) { + return errors.Errorf("prewrite request invalid: for_update_ts constraint set for index %v while %v mutations were given", index, len(mutations)) + } + + expectedForUpdateTSMap[index] = constraint.ExpectedForUpdateTs + } + + reader := reqCtx.getDBReader() + txn := reader.GetTxn() + + for i, m := range mutations { + if m.Op == kvrpcpb.Op_CheckNotExists { + return kverrors.ErrInvalidOp{Op: m.Op} + } + lock := store.getLock(reqCtx, m.Key) + isPessimisticLock := len(req.PessimisticActions) > 0 && req.PessimisticActions[i] == kvrpcpb.PrewriteRequest_DO_PESSIMISTIC_CHECK + needConstraintCheck := len(req.PessimisticActions) > 0 && req.PessimisticActions[i] == kvrpcpb.PrewriteRequest_DO_CONSTRAINT_CHECK + lockExists := lock != nil + lockMatch := lockExists && lock.StartTS == startTS + lockConstraintPasses := true + if expectedForUpdateTS, ok := expectedForUpdateTSMap[i]; ok { + if lock.ForUpdateTS != expectedForUpdateTS { + lockConstraintPasses = false + } + } + if isPessimisticLock { + valid := lockExists && lockMatch && lockConstraintPasses + if !valid { + return errors.New("pessimistic lock not found") + } + if lock.Op != uint8(kvrpcpb.Op_PessimisticLock) { + // Duplicated command. + return nil + } + // Do not overwrite lock ttl if prewrite ttl smaller than pessimisitc lock ttl + if uint64(lock.TTL) > req.LockTtl { + req.LockTtl = uint64(lock.TTL) + } + } else if needConstraintCheck { + item, err := txn.Get(m.Key) + if err != nil && err != badger.ErrKeyNotFound { + return errors.Trace(err) + } + // check conflict + if item != nil { + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if userMeta.CommitTS() > startTS { + return &kverrors.ErrConflict{ + StartTS: startTS, + ConflictTS: userMeta.StartTS(), + ConflictCommitTS: userMeta.CommitTS(), + Key: item.KeyCopy(nil), + Reason: kvrpcpb.WriteConflict_LazyUniquenessCheck, + } + } + } + } else { + // non pessimistic lock in pessimistic transaction, e.g. non-unique index. + valid := !lockExists || lockMatch + if !valid { + // Safe to set TTL to zero because the transaction of the lock is committed + // or rollbacked or must be rollbacked. + lock.TTL = 0 + return kverrors.BuildLockErr(m.Key, lock) + } + if lockMatch { + // Duplicate command. + return nil + } + } + // TODO add memory lock for async commit protocol. + } + items, err := store.getDBItems(reqCtx, mutations) + if err != nil { + return err + } + return store.prewriteMutations(reqCtx, mutations, req, items) +} + +func (store *MVCCStore) prewriteMutations(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, + req *kvrpcpb.PrewriteRequest, items []*badger.Item) error { + var minCommitTS uint64 + if req.UseAsyncCommit || req.TryOnePc { + // Get minCommitTS for async commit protocol. After all keys are locked in memory lock. + physical, logical, tsErr := store.pdClient.GetTS(context.Background()) + if tsErr != nil { + return tsErr + } + minCommitTS = uint64(physical)<<18 + uint64(logical) + if req.MaxCommitTs > 0 && minCommitTS > req.MaxCommitTs { + req.UseAsyncCommit = false + req.TryOnePc = false + } + if req.UseAsyncCommit { + reqCtx.asyncMinCommitTS = minCommitTS + } + } + + if req.UseAsyncCommit && minCommitTS > req.MinCommitTs { + req.MinCommitTs = minCommitTS + } + + if req.TryOnePc { + committed, err := store.tryOnePC(reqCtx, mutations, req, items, minCommitTS, req.MaxCommitTs) + if err != nil { + return err + } + // If 1PC succeeded, exit immediately. + if committed { + return nil + } + } + + batch := store.dbWriter.NewWriteBatch(req.StartVersion, 0, reqCtx.rpcCtx) + + for i, m := range mutations { + if m.Op == kvrpcpb.Op_CheckNotExists { + continue + } + lock, err1 := store.buildPrewriteLock(reqCtx, m, items[i], req) + if err1 != nil { + return err1 + } + batch.Prewrite(m.Key, lock) + } + + return store.dbWriter.Write(batch) +} + +func (store *MVCCStore) tryOnePC(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, + req *kvrpcpb.PrewriteRequest, items []*badger.Item, minCommitTS uint64, maxCommitTS uint64) (bool, error) { + if maxCommitTS != 0 && minCommitTS > maxCommitTS { + log.Debug("1pc transaction fallbacks due to minCommitTS exceeds maxCommitTS", + zap.Uint64("startTS", req.StartVersion), + zap.Uint64("minCommitTS", minCommitTS), + zap.Uint64("maxCommitTS", maxCommitTS)) + return false, nil + } + if minCommitTS < req.StartVersion { + log.Fatal("1pc commitTS less than startTS", zap.Uint64("startTS", req.StartVersion), zap.Uint64("minCommitTS", minCommitTS)) + } + + reqCtx.onePCCommitTS = minCommitTS + store.updateLatestTS(minCommitTS) + batch := store.dbWriter.NewWriteBatch(req.StartVersion, minCommitTS, reqCtx.rpcCtx) + + for i, m := range mutations { + if m.Op == kvrpcpb.Op_CheckNotExists { + continue + } + lock, err1 := store.buildPrewriteLock(reqCtx, m, items[i], req) + if err1 != nil { + return false, err1 + } + // batch.Commit will panic if the key is not locked. So there need to be a special function + // for it to commit without deleting lock. + batch.Commit(m.Key, lock) + } + + if err := store.dbWriter.Write(batch); err != nil { + return false, err + } + + return true, nil +} + +func encodeFromOldRow(oldRow, buf []byte) ([]byte, error) { + var ( + colIDs []int64 + datums []types.Datum + ) + for len(oldRow) > 1 { + var d types.Datum + var err error + oldRow, d, err = codec.DecodeOne(oldRow) + if err != nil { + return nil, err + } + colID := d.GetInt64() + oldRow, d, err = codec.DecodeOne(oldRow) + if err != nil { + return nil, err + } + colIDs = append(colIDs, colID) + datums = append(datums, d) + } + var encoder rowcodec.Encoder + return encoder.Encode(stmtctx.NewStmtCtx(), colIDs, datums, buf) +} + +func (store *MVCCStore) buildPrewriteLock(reqCtx *requestCtx, m *kvrpcpb.Mutation, item *badger.Item, + req *kvrpcpb.PrewriteRequest) (*mvcc.Lock, error) { + lock := &mvcc.Lock{ + LockHdr: mvcc.LockHdr{ + StartTS: req.StartVersion, + TTL: uint32(req.LockTtl), + PrimaryLen: uint16(len(req.PrimaryLock)), + MinCommitTS: req.MinCommitTs, + UseAsyncCommit: req.UseAsyncCommit, + SecondaryNum: uint32(len(req.Secondaries)), + }, + Primary: req.PrimaryLock, + Value: m.Value, + Secondaries: req.Secondaries, + } + // Note that this is not fully consistent with TiKV. TiKV doesn't always get the value from Write CF. In + // AssertionLevel_Fast, TiKV skips checking assertion if Write CF is not read, in order not to harm the performance. + // However, unistore can always check it. It's better not to assume the store's behavior about assertion when the + // mode is set to AssertionLevel_Fast. + if req.AssertionLevel != kvrpcpb.AssertionLevel_Off { + if item == nil || item.IsEmpty() { + if m.Assertion == kvrpcpb.Assertion_Exist { + log.Error("ASSERTION FAIL!!! non-exist for must exist key", zap.Stringer("mutation", m)) + return nil, &kverrors.ErrAssertionFailed{ + StartTS: req.StartVersion, + Key: m.Key, + Assertion: m.Assertion, + ExistingStartTS: 0, + ExistingCommitTS: 0, + } + } + } else { + if m.Assertion == kvrpcpb.Assertion_NotExist { + log.Error("ASSERTION FAIL!!! exist for must non-exist key", zap.Stringer("mutation", m)) + userMeta := mvcc.DBUserMeta(item.UserMeta()) + return nil, &kverrors.ErrAssertionFailed{ + StartTS: req.StartVersion, + Key: m.Key, + Assertion: m.Assertion, + ExistingStartTS: userMeta.StartTS(), + ExistingCommitTS: userMeta.CommitTS(), + } + } + } + } + var err error + lock.Op = uint8(m.Op) + if lock.Op == uint8(kvrpcpb.Op_Insert) { + if item != nil && item.ValueSize() > 0 { + return nil, &kverrors.ErrKeyAlreadyExists{Key: m.Key} + } + lock.Op = uint8(kvrpcpb.Op_Put) + } + if rowcodec.IsRowKey(m.Key) && lock.Op == uint8(kvrpcpb.Op_Put) { + if !rowcodec.IsNewFormat(m.Value) { + reqCtx.buf, err = encodeFromOldRow(m.Value, reqCtx.buf) + if err != nil { + log.Error("encode data failed", zap.Binary("value", m.Value), zap.Binary("key", m.Key), zap.Stringer("op", m.Op), zap.Error(err)) + return nil, err + } + + lock.Value = make([]byte, len(reqCtx.buf)) + copy(lock.Value, reqCtx.buf) + } + } + + lock.ForUpdateTS = req.ForUpdateTs + return lock, nil +} + +func (store *MVCCStore) getLock(req *requestCtx, key []byte) *mvcc.Lock { + req.buf = store.lockStore.Get(key, req.buf) + if len(req.buf) == 0 { + return nil + } + lock := mvcc.DecodeLock(req.buf) + return &lock +} + +func (store *MVCCStore) checkConflictInLockStore( + req *requestCtx, mutation *kvrpcpb.Mutation, startTS uint64) (*mvcc.Lock, error) { + req.buf = store.lockStore.Get(mutation.Key, req.buf) + if len(req.buf) == 0 { + return nil, nil + } + lock := mvcc.DecodeLock(req.buf) + if lock.StartTS == startTS { + // Same ts, no need to overwrite. + return &lock, nil + } + return nil, kverrors.BuildLockErr(mutation.Key, &lock) +} + +const maxSystemTS uint64 = math.MaxUint64 + +// Commit implements the MVCCStore interface. +func (store *MVCCStore) Commit(req *requestCtx, keys [][]byte, startTS, commitTS uint64) error { + sortKeys(keys) + store.updateLatestTS(commitTS) + regCtx := req.regCtx + hashVals := keysToHashVals(keys...) + batch := store.dbWriter.NewWriteBatch(startTS, commitTS, req.rpcCtx) + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + var buf []byte + var tmpDiff int + var isPessimisticTxn bool + for _, key := range keys { + var lockErr error + var checkErr error + var lock mvcc.Lock + buf = store.lockStore.Get(key, buf) + if len(buf) == 0 { + // We never commit partial keys in Commit request, so if one lock is not found, + // the others keys must not be found too. + lockErr = kverrors.ErrLockNotFound + } else { + lock = mvcc.DecodeLock(buf) + if lock.StartTS != startTS { + lockErr = kverrors.ErrReplaced + } + } + if lockErr != nil { + // Maybe the secondary keys committed by other concurrent transactions using lock resolver, + // check commit info from store + checkErr = store.handleLockNotFound(req, key, startTS, commitTS) + if checkErr == nil { + continue + } + log.Error("commit failed, no correspond lock found", + zap.Binary("key", key), zap.Uint64("start ts", startTS), zap.String("lock", fmt.Sprintf("%v", lock)), zap.Error(lockErr)) + return lockErr + } + if commitTS < lock.MinCommitTS { + log.Info("trying to commit with smaller commitTs than minCommitTs", + zap.Uint64("commit ts", commitTS), zap.Uint64("min commit ts", lock.MinCommitTS), zap.Binary("key", key)) + return &kverrors.ErrCommitExpire{ + StartTs: startTS, + CommitTs: commitTS, + MinCommitTs: lock.MinCommitTS, + Key: key, + } + } + isPessimisticTxn = lock.ForUpdateTS > 0 + tmpDiff += len(key) + len(lock.Value) + batch.Commit(key, &lock) + } + atomic.AddInt64(regCtx.Diff(), int64(tmpDiff)) + err := store.dbWriter.Write(batch) + store.lockWaiterManager.WakeUp(startTS, commitTS, hashVals) + if isPessimisticTxn { + store.DeadlockDetectCli.CleanUp(startTS) + } + return err +} + +func (store *MVCCStore) handleLockNotFound(reqCtx *requestCtx, key []byte, startTS, commitTS uint64) error { + txn := reqCtx.getDBReader().GetTxn() + txn.SetReadTS(commitTS) + item, err := txn.Get(key) + if err != nil && err != badger.ErrKeyNotFound { + return errors.Trace(err) + } + if item == nil { + return kverrors.ErrLockNotFound + } + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if userMeta.StartTS() == startTS { + // Already committed. + return nil + } + return kverrors.ErrLockNotFound +} + +const ( + rollbackStatusDone = 0 + rollbackStatusNoLock = 1 + rollbackStatusNewLock = 2 + rollbackPessimistic = 3 + rollbackStatusLocked = 4 +) + +// Rollback implements the MVCCStore interface. +func (store *MVCCStore) Rollback(reqCtx *requestCtx, keys [][]byte, startTS uint64) error { + sortKeys(keys) + hashVals := keysToHashVals(keys...) + log.S().Debugf("%d rollback %v", startTS, hashVals) + regCtx := reqCtx.regCtx + batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) + + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + statuses := make([]int, len(keys)) + for i, key := range keys { + var rollbackErr error + statuses[i], rollbackErr = store.rollbackKeyReadLock(reqCtx, batch, key, startTS, 0) + if rollbackErr != nil { + return errors.Trace(rollbackErr) + } + } + for i, key := range keys { + status := statuses[i] + if status == rollbackStatusDone || status == rollbackPessimistic { + // rollback pessimistic lock doesn't need to read db. + continue + } + err := store.rollbackKeyReadDB(reqCtx, batch, key, startTS, statuses[i] == rollbackStatusNewLock) + if err != nil { + return err + } + } + store.DeadlockDetectCli.CleanUp(startTS) + err := store.dbWriter.Write(batch) + return errors.Trace(err) +} + +func (store *MVCCStore) rollbackKeyReadLock(reqCtx *requestCtx, batch mvcc.WriteBatch, key []byte, + startTS, currentTs uint64) (int, error) { + reqCtx.buf = store.lockStore.Get(key, reqCtx.buf) + hasLock := len(reqCtx.buf) > 0 + if hasLock { + lock := mvcc.DecodeLock(reqCtx.buf) + if lock.StartTS < startTS { + // The lock is old, means this is written by an old transaction, and the current transaction may not arrive. + // We should write a rollback lock. + batch.Rollback(key, false) + return rollbackStatusDone, nil + } + if lock.StartTS == startTS { + if currentTs > 0 && uint64(oracle.ExtractPhysical(lock.StartTS))+uint64(lock.TTL) >= uint64(oracle.ExtractPhysical(currentTs)) { + return rollbackStatusLocked, kverrors.BuildLockErr(key, &lock) + } + // We can not simply delete the lock because the prewrite may be sent multiple times. + // To prevent that we update it a rollback lock. + batch.Rollback(key, true) + return rollbackStatusDone, nil + } + // lock.startTS > startTS, go to DB to check if the key is committed. + return rollbackStatusNewLock, nil + } + return rollbackStatusNoLock, nil +} + +func (store *MVCCStore) rollbackKeyReadDB(req *requestCtx, batch mvcc.WriteBatch, key []byte, startTS uint64, hasLock bool) error { + commitTS, err := store.checkCommitted(req.getDBReader(), key, startTS) + if err != nil { + return err + } + if commitTS != 0 { + return kverrors.ErrAlreadyCommitted(commitTS) + } + // commit not found, rollback this key + batch.Rollback(key, false) + return nil +} + +func (store *MVCCStore) checkCommitted(reader *dbreader.DBReader, key []byte, startTS uint64) (uint64, error) { + txn := reader.GetTxn() + item, err := txn.Get(key) + if err != nil && err != badger.ErrKeyNotFound { + return 0, errors.Trace(err) + } + if item == nil { + return 0, nil + } + userMeta := mvcc.DBUserMeta(item.UserMeta()) + if userMeta.StartTS() == startTS { + return userMeta.CommitTS(), nil + } + it := reader.GetIter() + it.SetAllVersions(true) + for it.Seek(key); it.Valid(); it.Next() { + item = it.Item() + if !bytes.Equal(item.Key(), key) { + break + } + userMeta = mvcc.DBUserMeta(item.UserMeta()) + if userMeta.StartTS() == startTS { + return userMeta.CommitTS(), nil + } + } + return 0, nil +} + +// LockPair contains a pair of key and lock. It's used for reading through locks. +type LockPair struct { + key []byte + lock *mvcc.Lock +} + +func getValueFromLock(lock *mvcc.Lock) []byte { + if lock.Op == byte(kvrpcpb.Op_Put) { + // lock owns the value so needn't to safeCopy it. + return lock.Value + } + return nil +} + +// *LockPair is not nil if the lock in the committed timestamp set. Read operations can get value from it without deep copy. +func checkLock(lock mvcc.Lock, key []byte, startTS uint64, resolved []uint64, committed []uint64) (*LockPair, error) { + if inTSSet(lock.StartTS, resolved) { + return nil, nil + } + lockVisible := lock.StartTS <= startTS + isWriteLock := lock.Op == uint8(kvrpcpb.Op_Put) || lock.Op == uint8(kvrpcpb.Op_Del) + isPrimaryGet := startTS == maxSystemTS && bytes.Equal(lock.Primary, key) && !lock.UseAsyncCommit + if lockVisible && isWriteLock && !isPrimaryGet { + if inTSSet(lock.StartTS, committed) { + return &LockPair{safeCopy(key), &lock}, nil + } + return nil, kverrors.BuildLockErr(safeCopy(key), &lock) + } + return nil, nil +} + +// checkLockForRcCheckTS checks the lock for `RcCheckTS` isolation level in transaction read. +func checkLockForRcCheckTS(lock mvcc.Lock, key []byte, startTS uint64, resolved []uint64) error { + if inTSSet(lock.StartTS, resolved) { + return nil + } + isWriteLock := lock.Op == uint8(kvrpcpb.Op_Put) || lock.Op == uint8(kvrpcpb.Op_Del) + if !isWriteLock { + return nil + } + return &kverrors.ErrConflict{ + StartTS: startTS, + ConflictTS: lock.StartTS, + Key: safeCopy(key), + Reason: kvrpcpb.WriteConflict_RcCheckTs, + } +} + +// CheckKeysLockForRcCheckTS is used to check version timestamp if `RcCheckTS` isolation level is used. +func (store *MVCCStore) CheckKeysLockForRcCheckTS(startTS uint64, resolved []uint64, keys ...[]byte) error { + var buf []byte + for _, key := range keys { + buf = store.lockStore.Get(key, buf) + if len(buf) == 0 { + continue + } + lock := mvcc.DecodeLock(buf) + err := checkLockForRcCheckTS(lock, key, startTS, resolved) + if err != nil { + return err + } + } + return nil +} + +// CheckKeysLock implements the MVCCStore interface. +func (store *MVCCStore) CheckKeysLock(startTS uint64, resolved, committed []uint64, keys ...[]byte) ([]*LockPair, error) { + var buf []byte + var lockPairs []*LockPair + for _, key := range keys { + buf = store.lockStore.Get(key, buf) + if len(buf) == 0 { + continue + } + lock := mvcc.DecodeLock(buf) + lockPair, err := checkLock(lock, key, startTS, resolved, committed) + if lockPair != nil { + lockPairs = append(lockPairs, lockPair) + } + if err != nil { + return nil, err + } + } + return lockPairs, nil +} + +// CheckRangeLock implements the MVCCStore interface. +func (store *MVCCStore) CheckRangeLock(startTS uint64, startKey, endKey []byte, resolved []uint64) error { + it := store.lockStore.NewIterator() + for it.Seek(startKey); it.Valid(); it.Next() { + if exceedEndKey(it.Key(), endKey) { + break + } + lock := mvcc.DecodeLock(it.Value()) + _, err := checkLock(lock, it.Key(), startTS, resolved, nil) + if err != nil { + return err + } + } + return nil +} + +// Cleanup implements the MVCCStore interface. +func (store *MVCCStore) Cleanup(reqCtx *requestCtx, key []byte, startTS, currentTs uint64) error { + hashVals := keysToHashVals(key) + regCtx := reqCtx.regCtx + batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) + + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + status, err := store.rollbackKeyReadLock(reqCtx, batch, key, startTS, currentTs) + if err != nil { + return err + } + if status != rollbackStatusDone { + err := store.rollbackKeyReadDB(reqCtx, batch, key, startTS, status == rollbackStatusNewLock) + if err != nil { + return err + } + rbStatus := store.checkExtraTxnStatus(reqCtx, key, startTS) + if rbStatus.isOpLockCommitted() { + return kverrors.ErrAlreadyCommitted(rbStatus.commitTS) + } + } + err = store.dbWriter.Write(batch) + store.lockWaiterManager.WakeUp(startTS, 0, hashVals) + return err +} + +func (store *MVCCStore) appendScannedLock(locks []*kvrpcpb.LockInfo, it *lockstore.Iterator, maxTS uint64) []*kvrpcpb.LockInfo { + lock := mvcc.DecodeLock(it.Value()) + if lock.StartTS < maxTS { + locks = append(locks, lock.ToLockInfo(append([]byte{}, it.Key()...))) + } + return locks +} + +// ScanLock implements the MVCCStore interface. +func (store *MVCCStore) ScanLock(reqCtx *requestCtx, maxTS uint64, limit int) ([]*kvrpcpb.LockInfo, error) { + var locks []*kvrpcpb.LockInfo + it := store.lockStore.NewIterator() + for it.Seek(reqCtx.regCtx.RawStart()); it.Valid(); it.Next() { + if exceedEndKey(it.Key(), reqCtx.regCtx.RawEnd()) { + return locks, nil + } + if len(locks) == limit { + return locks, nil + } + locks = store.appendScannedLock(locks, it, maxTS) + } + return locks, nil +} + +// PhysicalScanLock implements the MVCCStore interface. +func (store *MVCCStore) PhysicalScanLock(startKey []byte, maxTS uint64, limit int) []*kvrpcpb.LockInfo { + var locks []*kvrpcpb.LockInfo + it := store.lockStore.NewIterator() + for it.Seek(startKey); it.Valid(); it.Next() { + if len(locks) == limit { + break + } + locks = store.appendScannedLock(locks, it, maxTS) + } + return locks +} + +// ResolveLock implements the MVCCStore interface. +func (store *MVCCStore) ResolveLock(reqCtx *requestCtx, lockKeys [][]byte, startTS, commitTS uint64) error { + regCtx := reqCtx.regCtx + if len(lockKeys) == 0 { + it := store.lockStore.NewIterator() + for it.Seek(regCtx.RawStart()); it.Valid(); it.Next() { + if exceedEndKey(it.Key(), regCtx.RawEnd()) { + break + } + lock := mvcc.DecodeLock(it.Value()) + if lock.StartTS != startTS { + continue + } + lockKeys = append(lockKeys, safeCopy(it.Key())) + } + if len(lockKeys) == 0 { + return nil + } + } + hashVals := keysToHashVals(lockKeys...) + batch := store.dbWriter.NewWriteBatch(startTS, commitTS, reqCtx.rpcCtx) + + regCtx.AcquireLatches(hashVals) + defer regCtx.ReleaseLatches(hashVals) + + var buf []byte + var tmpDiff int + for _, lockKey := range lockKeys { + buf = store.lockStore.Get(lockKey, buf) + if len(buf) == 0 { + continue + } + lock := mvcc.DecodeLock(buf) + if lock.StartTS != startTS { + continue + } + if commitTS > 0 { + tmpDiff += len(lockKey) + len(lock.Value) + batch.Commit(lockKey, &lock) + } else { + batch.Rollback(lockKey, true) + } + } + atomic.AddInt64(regCtx.Diff(), int64(tmpDiff)) + err := store.dbWriter.Write(batch) + return err +} + +// UpdateSafePoint implements the MVCCStore interface. +func (store *MVCCStore) UpdateSafePoint(safePoint uint64) { + // We use the gcLock to make sure safePoint can only increase. + store.db.UpdateSafeTs(safePoint) + store.safePoint.UpdateTS(safePoint) + log.Info("safePoint is updated to", zap.Uint64("ts", safePoint), zap.Time("time", tsToTime(safePoint))) +} + +func tsToTime(ts uint64) time.Time { + return time.UnixMilli(int64(ts >> 18)) +} + +// StartDeadlockDetection implements the MVCCStore interface. +func (store *MVCCStore) StartDeadlockDetection(isRaft bool) { + if isRaft { + go store.DeadlockDetectCli.sendReqLoop() + return + } + + go func() { + for { + select { + case req := <-store.DeadlockDetectCli.sendCh: + resp := store.DeadlockDetectSvr.Detect(req) + if resp != nil { + store.DeadlockDetectCli.waitMgr.WakeUpForDeadlock(resp) + } + case <-store.closeCh: + return + } + } + }() +} + +// MvccGetByKey gets mvcc information using input key as rawKey +func (store *MVCCStore) MvccGetByKey(reqCtx *requestCtx, key []byte) (*kvrpcpb.MvccInfo, error) { + mvccInfo := &kvrpcpb.MvccInfo{} + lock := store.getLock(reqCtx, key) + if lock != nil { + mvccInfo.Lock = &kvrpcpb.MvccLock{ + Type: kvrpcpb.Op(lock.Op), + StartTs: lock.StartTS, + Primary: lock.Primary, + ShortValue: lock.Value, + } + } + reader := reqCtx.getDBReader() + isRowKey := rowcodec.IsRowKey(key) + // Get commit writes from db + err := reader.GetMvccInfoByKey(key, isRowKey, mvccInfo) + if err != nil { + return nil, err + } + // Get rollback writes from rollback store + err = store.getExtraMvccInfo(key, reqCtx, mvccInfo) + if err != nil { + return nil, err + } + slices.SortFunc(mvccInfo.Writes, func(i, j *kvrpcpb.MvccWrite) int { + return cmp.Compare(j.CommitTs, i.CommitTs) + }) + mvccInfo.Values = make([]*kvrpcpb.MvccValue, len(mvccInfo.Writes)) + for i := 0; i < len(mvccInfo.Writes); i++ { + write := mvccInfo.Writes[i] + mvccInfo.Values[i] = &kvrpcpb.MvccValue{ + StartTs: write.StartTs, + Value: write.ShortValue, + } + } + return mvccInfo, nil +} + +func (store *MVCCStore) getExtraMvccInfo(rawkey []byte, + reqCtx *requestCtx, mvccInfo *kvrpcpb.MvccInfo) error { + it := reqCtx.getDBReader().GetExtraIter() + rbStartKey := mvcc.EncodeExtraTxnStatusKey(rawkey, math.MaxUint64) + rbEndKey := mvcc.EncodeExtraTxnStatusKey(rawkey, 0) + for it.Seek(rbStartKey); it.Valid(); it.Next() { + item := it.Item() + if len(rbEndKey) != 0 && bytes.Compare(item.Key(), rbEndKey) > 0 { + break + } + key := item.Key() + if len(key) == 0 || (key[0] != tableExtraPrefix && key[0] != metaExtraPrefix) { + continue + } + rollbackTs := mvcc.DecodeKeyTS(key) + curRecord := &kvrpcpb.MvccWrite{ + Type: kvrpcpb.Op_Rollback, + StartTs: rollbackTs, + CommitTs: rollbackTs, + } + mvccInfo.Writes = append(mvccInfo.Writes, curRecord) + } + return nil +} + +// MvccGetByStartTs implements the MVCCStore interface. +func (store *MVCCStore) MvccGetByStartTs(reqCtx *requestCtx, startTs uint64) (*kvrpcpb.MvccInfo, []byte, error) { + reader := reqCtx.getDBReader() + startKey := reqCtx.regCtx.RawStart() + endKey := reqCtx.regCtx.RawEnd() + rawKey, err := reader.GetKeyByStartTs(startKey, endKey, startTs) + if err != nil { + return nil, nil, err + } + if rawKey == nil { + return nil, nil, nil + } + res, err := store.MvccGetByKey(reqCtx, rawKey) + if err != nil { + return nil, nil, err + } + return res, rawKey, nil +} + +// DeleteFileInRange implements the MVCCStore interface. +func (store *MVCCStore) DeleteFileInRange(start, end []byte) { + store.db.DeleteFilesInRange(start, end) + start[0]++ + end[0]++ + store.db.DeleteFilesInRange(start, end) +} + +// Get implements the MVCCStore interface. +func (store *MVCCStore) Get(reqCtx *requestCtx, key []byte, version uint64) ([]byte, error) { + if reqCtx.isSnapshotIsolation() { + lockPairs, err := store.CheckKeysLock(version, reqCtx.rpcCtx.ResolvedLocks, reqCtx.rpcCtx.CommittedLocks, key) + if err != nil { + return nil, err + } + if len(lockPairs) != 0 { + return getValueFromLock(lockPairs[0].lock), nil + } + } else if reqCtx.isRcCheckTSIsolationLevel() { + err := store.CheckKeysLockForRcCheckTS(version, reqCtx.rpcCtx.ResolvedLocks, key) + if err != nil { + return nil, err + } + } + val, err := reqCtx.getDBReader().Get(key, version) + if val == nil { + return nil, err + } + return safeCopy(val), err +} + +// BatchGet implements the MVCCStore interface. +func (store *MVCCStore) BatchGet(reqCtx *requestCtx, keys [][]byte, version uint64) []*kvrpcpb.KvPair { + pairs := make([]*kvrpcpb.KvPair, 0, len(keys)) + var remain [][]byte + if reqCtx.isSnapshotIsolation() { + remain = make([][]byte, 0, len(keys)) + for _, key := range keys { + lockPairs, err := store.CheckKeysLock(version, reqCtx.rpcCtx.ResolvedLocks, reqCtx.rpcCtx.CommittedLocks, key) + if err != nil { + pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Error: convertToKeyError(err)}) + } else if len(lockPairs) != 0 { + value := getValueFromLock(lockPairs[0].lock) + if value != nil { + pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Value: value}) + } + } else { + remain = append(remain, key) + } + } + } else if reqCtx.isRcCheckTSIsolationLevel() { + remain = make([][]byte, 0, len(keys)) + for _, key := range keys { + err := store.CheckKeysLockForRcCheckTS(version, reqCtx.rpcCtx.ResolvedLocks, key) + if err != nil { + pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Error: convertToKeyError(err)}) + } else { + remain = append(remain, key) + } + } + } else { + remain = keys + } + batchGetFunc := func(key, value []byte, err error) { + if len(value) != 0 { + pairs = append(pairs, &kvrpcpb.KvPair{ + Key: safeCopy(key), + Value: safeCopy(value), + Error: convertToKeyError(err), + }) + } + } + reqCtx.getDBReader().BatchGet(remain, version, batchGetFunc) + return pairs +} + +func (store *MVCCStore) collectRangeLock(startTS uint64, startKey, endKey []byte, resolved, committed []uint64, + isolationLEvel kvrpcpb.IsolationLevel) []*kvrpcpb.KvPair { + var pairs []*kvrpcpb.KvPair + it := store.lockStore.NewIterator() + for it.Seek(startKey); it.Valid(); it.Next() { + if exceedEndKey(it.Key(), endKey) { + break + } + lock := mvcc.DecodeLock(it.Value()) + if isolationLEvel == kvrpcpb.IsolationLevel_SI { + lockPair, err := checkLock(lock, it.Key(), startTS, resolved, committed) + if lockPair != nil { + pairs = append(pairs, &kvrpcpb.KvPair{ + Key: lockPair.key, + // deleted key's value is nil + Value: getValueFromLock(lockPair.lock), + }) + } else if err != nil { + pairs = append(pairs, &kvrpcpb.KvPair{ + Error: convertToKeyError(err), + Key: safeCopy(it.Key()), + }) + } + } else if isolationLEvel == kvrpcpb.IsolationLevel_RCCheckTS { + err := checkLockForRcCheckTS(lock, it.Key(), startTS, resolved) + if err != nil { + pairs = append(pairs, &kvrpcpb.KvPair{ + Error: convertToKeyError(err), + Key: safeCopy(it.Key()), + }) + } + } + } + return pairs +} + +func inTSSet(startTS uint64, tsSet []uint64) bool { + for _, v := range tsSet { + if startTS == v { + return true + } + } + return false +} + +type kvScanProcessor struct { + pairs []*kvrpcpb.KvPair + sampleStep uint32 + scanCnt uint32 +} + +func (p *kvScanProcessor) Process(key, value []byte) (err error) { + if p.sampleStep > 0 { + p.scanCnt++ + if (p.scanCnt-1)%p.sampleStep != 0 { + return nil + } + } + p.pairs = append(p.pairs, &kvrpcpb.KvPair{ + Key: safeCopy(key), + Value: safeCopy(value), + }) + return nil +} + +func (p *kvScanProcessor) SkipValue() bool { + return false +} + +// Scan implements the MVCCStore interface. +func (store *MVCCStore) Scan(reqCtx *requestCtx, req *kvrpcpb.ScanRequest) []*kvrpcpb.KvPair { + var startKey, endKey []byte + if req.Reverse { + startKey = req.EndKey + if len(startKey) == 0 { + startKey = reqCtx.regCtx.RawStart() + } + endKey = req.StartKey + } else { + startKey = req.StartKey + endKey = req.EndKey + if len(endKey) == 0 { + endKey = reqCtx.regCtx.RawEnd() + } + if len(endKey) == 0 { + // Don't scan internal keys. + endKey = InternalKeyPrefix + } + } + var lockPairs []*kvrpcpb.KvPair + limit := req.GetLimit() + if req.SampleStep == 0 { + if reqCtx.isSnapshotIsolation() || reqCtx.isRcCheckTSIsolationLevel() { + if bytes.Compare(startKey, endKey) <= 0 { + lockPairs = store.collectRangeLock(req.GetVersion(), startKey, endKey, reqCtx.rpcCtx.ResolvedLocks, + reqCtx.rpcCtx.CommittedLocks, reqCtx.rpcCtx.IsolationLevel) + } else { + lockPairs = store.collectRangeLock(req.GetVersion(), endKey, startKey, reqCtx.rpcCtx.ResolvedLocks, + reqCtx.rpcCtx.CommittedLocks, reqCtx.rpcCtx.IsolationLevel) + } + } + } else { + limit = req.SampleStep * limit + } + var scanProc = &kvScanProcessor{ + sampleStep: req.SampleStep, + } + reader := reqCtx.getDBReader() + var err error + if req.Reverse { + err = reader.ReverseScan(startKey, endKey, int(limit), req.GetVersion(), scanProc) + } else { + err = reader.Scan(startKey, endKey, int(limit), req.GetVersion(), scanProc) + } + if err != nil { + scanProc.pairs = append(scanProc.pairs[:0], &kvrpcpb.KvPair{ + Error: convertToKeyError(err), + }) + return scanProc.pairs + } + pairs := append(lockPairs, scanProc.pairs...) + sort.SliceStable(pairs, func(i, j int) bool { + cmp := bytes.Compare(pairs[i].Key, pairs[j].Key) + if req.Reverse { + cmp = -cmp + } + return cmp < 0 + }) + validPairs := pairs[:0] + var prev *kvrpcpb.KvPair + for _, pair := range pairs { + if prev != nil && bytes.Equal(prev.Key, pair.Key) { + continue + } + prev = pair + if pair.Error != nil || len(pair.Value) != 0 { + validPairs = append(validPairs, pair) + if len(validPairs) >= int(limit) { + break + } + } + } + return validPairs +} + +func (store *MVCCStore) runUpdateSafePointLoop() { + var lastSafePoint uint64 + ticker := time.NewTicker(time.Minute) + for { + safePoint, err := store.pdClient.GetGCSafePoint(context.Background()) + if err != nil { + log.Error("get GC safePoint error", zap.Error(err)) + } else if lastSafePoint < safePoint { + store.UpdateSafePoint(safePoint) + lastSafePoint = safePoint + } + select { + case <-store.closeCh: + return + case <-ticker.C: + } + } +} + +// SafePoint represents a safe point. +type SafePoint struct { + timestamp uint64 +} + +// UpdateTS is used to record the timestamp of updating the table's schema information. +// These changing schema operations don't include 'truncate table' and 'rename table'. +func (sp *SafePoint) UpdateTS(ts uint64) { + for { + old := atomic.LoadUint64(&sp.timestamp) + if old < ts { + if !atomic.CompareAndSwapUint64(&sp.timestamp, old, ts) { + continue + } + } + break + } +} + +// CreateCompactionFilter implements badger.CompactionFilterFactory function. +func (sp *SafePoint) CreateCompactionFilter(targetLevel int, startKey, endKey []byte) badger.CompactionFilter { + return &GCCompactionFilter{ + targetLevel: targetLevel, + safePoint: atomic.LoadUint64(&sp.timestamp), + } +} + +// GCCompactionFilter implements the badger.CompactionFilter interface. +type GCCompactionFilter struct { + targetLevel int + safePoint uint64 +} + +const ( + metaPrefix byte = 'm' + // 'm' + 1 = 'n' + metaExtraPrefix byte = 'n' + tablePrefix byte = 't' + // 't' + 1 = 'u + tableExtraPrefix byte = 'u' +) + +// Filter implements the badger.CompactionFilter interface. +// Since we use txn ts as badger version, we only need to filter Delete, Rollback and Op_Lock. +// It is called for the first valid version before safe point, older versions are discarded automatically. +func (f *GCCompactionFilter) Filter(key, value, userMeta []byte) badger.Decision { + switch key[0] { + case metaPrefix, tablePrefix: + // For latest version, we need to remove `delete` key, which has value len 0. + if mvcc.DBUserMeta(userMeta).CommitTS() < f.safePoint && len(value) == 0 { + return badger.DecisionMarkTombstone + } + case metaExtraPrefix, tableExtraPrefix: + // For latest version, we can only remove `delete` key, which has value len 0. + if mvcc.DBUserMeta(userMeta).StartTS() < f.safePoint { + return badger.DecisionDrop + } + } + // Older version are discarded automatically, we need to keep the first valid version. + return badger.DecisionKeep +} + +var ( + baseGuard = badger.Guard{MatchLen: 64, MinSize: 64 * 1024} + raftGuard = badger.Guard{Prefix: []byte{0}, MatchLen: 1, MinSize: 64 * 1024} + metaGuard = badger.Guard{Prefix: []byte{'m'}, MatchLen: 1, MinSize: 64 * 1024} + metaExtraGuard = badger.Guard{Prefix: []byte{'n'}, MatchLen: 1, MinSize: 1} + tableGuard = badger.Guard{Prefix: []byte{'t'}, MatchLen: 9, MinSize: 1 * 1024 * 1024} + tableIndexGuard = badger.Guard{Prefix: []byte{'t'}, MatchLen: 11, MinSize: 1 * 1024 * 1024} + tableExtraGuard = badger.Guard{Prefix: []byte{'u'}, MatchLen: 1, MinSize: 1} +) + +// Guards implements the badger.CompactionFilter interface. +// Guards returns specifications that may splits the SST files +// A key is associated to a guard that has the longest matched Prefix. +func (f *GCCompactionFilter) Guards() []badger.Guard { + if f.targetLevel < 4 { + // do not split index and row for top levels. + return []badger.Guard{ + baseGuard, raftGuard, metaGuard, metaExtraGuard, tableGuard, tableExtraGuard, + } + } + // split index and row for bottom levels. + return []badger.Guard{ + baseGuard, raftGuard, metaGuard, metaExtraGuard, tableIndexGuard, tableExtraGuard, + } +} + +func doesNeedLock(item *badger.Item, + req *kvrpcpb.PessimisticLockRequest) (bool, error) { + if req.LockOnlyIfExists { + if item == nil { + return false, nil + } + val, err := item.Value() + if err != nil { + return false, err + } + if len(val) == 0 { + return false, nil + } + } + return true, nil +} diff --git a/pkg/store/mockstore/unistore/tikv/mvcc/BUILD.bazel b/pkg/store/mockstore/unistore/tikv/mvcc/BUILD.bazel new file mode 100644 index 0000000000000..67e0623365c5e --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/mvcc/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mvcc", + srcs = [ + "db_writer.go", + "mvcc.go", + "tikv.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc", + visibility = ["//visibility:public"], + deps = [ + "//pkg/store/mockstore/unistore/lockstore", + "//pkg/util/codec", + "@com_github_pingcap_badger//:badger", + "@com_github_pingcap_badger//y", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + ], +) diff --git a/store/mockstore/unistore/tikv/mvcc/db_writer.go b/pkg/store/mockstore/unistore/tikv/mvcc/db_writer.go similarity index 96% rename from store/mockstore/unistore/tikv/mvcc/db_writer.go rename to pkg/store/mockstore/unistore/tikv/mvcc/db_writer.go index 4b04fca54de52..c12c785685efd 100644 --- a/store/mockstore/unistore/tikv/mvcc/db_writer.go +++ b/pkg/store/mockstore/unistore/tikv/mvcc/db_writer.go @@ -19,7 +19,7 @@ import ( "github.com/pingcap/badger" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" ) // DBWriter is the interface to persistent data. diff --git a/pkg/store/mockstore/unistore/tikv/mvcc/mvcc.go b/pkg/store/mockstore/unistore/tikv/mvcc/mvcc.go new file mode 100644 index 0000000000000..61b577dbe7695 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/mvcc/mvcc.go @@ -0,0 +1,187 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package mvcc + +import ( + "encoding/binary" + "encoding/hex" + "fmt" + "unsafe" + + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/util/codec" +) + +var defaultEndian = binary.LittleEndian + +// DBUserMeta is the user meta used in DB. +type DBUserMeta []byte + +// DecodeLock decodes data to lock, the primary and value is copied, the secondaries are copied if async commit is enabled. +func DecodeLock(data []byte) (l Lock) { + l.LockHdr = *(*LockHdr)(unsafe.Pointer(&data[0])) + cursor := mvccLockHdrSize + lockBuf := append([]byte{}, data[cursor:]...) + l.Primary = lockBuf[:l.PrimaryLen] + cursor = int(l.PrimaryLen) + if l.LockHdr.SecondaryNum > 0 { + l.Secondaries = make([][]byte, l.LockHdr.SecondaryNum) + for i := uint32(0); i < l.LockHdr.SecondaryNum; i++ { + keyLen := binary.LittleEndian.Uint16(lockBuf[cursor:]) + cursor += 2 + l.Secondaries[i] = lockBuf[cursor : cursor+int(keyLen)] + cursor += int(keyLen) + } + } + l.Value = lockBuf[cursor:] + return +} + +// LockHdr holds fixed size fields for mvcc Lock. +type LockHdr struct { + StartTS uint64 + ForUpdateTS uint64 + MinCommitTS uint64 + TTL uint32 + Op uint8 + HasOldVer bool + PrimaryLen uint16 + UseAsyncCommit bool + SecondaryNum uint32 +} + +const mvccLockHdrSize = int(unsafe.Sizeof(LockHdr{})) + +// Lock is the structure for MVCC lock. +type Lock struct { + LockHdr + Primary []byte + Value []byte + Secondaries [][]byte +} + +// MarshalBinary implements encoding.BinaryMarshaler interface. +func (l *Lock) MarshalBinary() []byte { + lockLen := mvccLockHdrSize + len(l.Primary) + len(l.Value) + length := lockLen + if l.LockHdr.SecondaryNum > 0 { + for _, secondaryKey := range l.Secondaries { + length += 2 + length += len(secondaryKey) + } + } + buf := make([]byte, length) + hdr := (*LockHdr)(unsafe.Pointer(&buf[0])) + *hdr = l.LockHdr + cursor := mvccLockHdrSize + copy(buf[cursor:], l.Primary) + cursor += len(l.Primary) + if l.LockHdr.SecondaryNum > 0 { + for _, secondaryKey := range l.Secondaries { + binary.LittleEndian.PutUint16(buf[cursor:], uint16(len(secondaryKey))) + cursor += 2 + copy(buf[cursor:], secondaryKey) + cursor += len(secondaryKey) + } + } + copy(buf[cursor:], l.Value) + return buf +} + +// ToLockInfo converts an mvcc Lock to kvrpcpb.LockInfo +func (l *Lock) ToLockInfo(key []byte) *kvrpcpb.LockInfo { + return &kvrpcpb.LockInfo{ + PrimaryLock: l.Primary, + LockVersion: l.StartTS, + Key: key, + LockTtl: uint64(l.TTL), + LockType: kvrpcpb.Op(l.Op), + LockForUpdateTs: l.ForUpdateTS, + UseAsyncCommit: l.UseAsyncCommit, + MinCommitTs: l.MinCommitTS, + Secondaries: l.Secondaries, + } +} + +// String implements fmt.Stringer for Lock. +func (l *Lock) String() string { + return fmt.Sprintf( + "Lock { Type: %v, StartTS: %v, ForUpdateTS: %v, Primary: %v, UseAsyncCommit: %v }", + kvrpcpb.Op(l.Op).String(), + l.StartTS, + l.ForUpdateTS, + hex.EncodeToString(l.Primary), + l.UseAsyncCommit, + ) +} + +// UserMeta value for lock. +const ( + LockUserMetaNoneByte = 0 + LockUserMetaDeleteByte = 2 +) + +// UserMeta byte slices for lock. +var ( + LockUserMetaNone = []byte{LockUserMetaNoneByte} + LockUserMetaDelete = []byte{LockUserMetaDeleteByte} +) + +// DecodeKeyTS decodes the TS in a key. +func DecodeKeyTS(buf []byte) uint64 { + tsBin := buf[len(buf)-8:] + _, ts, err := codec.DecodeUintDesc(tsBin) + if err != nil { + panic(err) + } + return ts +} + +// NewDBUserMeta creates a new DBUserMeta. +func NewDBUserMeta(startTS, commitTS uint64) DBUserMeta { + m := make(DBUserMeta, 16) + defaultEndian.PutUint64(m, startTS) + defaultEndian.PutUint64(m[8:], commitTS) + return m +} + +// CommitTS reads the commitTS from the DBUserMeta. +func (m DBUserMeta) CommitTS() uint64 { + return defaultEndian.Uint64(m[8:]) +} + +// StartTS reads the startTS from the DBUserMeta. +func (m DBUserMeta) StartTS() uint64 { + return defaultEndian.Uint64(m[:8]) +} + +// EncodeExtraTxnStatusKey encodes a extra transaction status key. +// It is only used for Rollback and Op_Lock. +func EncodeExtraTxnStatusKey(key []byte, startTS uint64) []byte { + b := append([]byte{}, key...) + ret := codec.EncodeUintDesc(b, startTS) + ret[0]++ + return ret +} + +// DecodeExtraTxnStatusKey decodes a extra transaction status key. +func DecodeExtraTxnStatusKey(extraKey []byte) (key []byte) { + if len(extraKey) <= 9 { + return nil + } + key = append([]byte{}, extraKey[:len(extraKey)-8]...) + key[0]-- + return +} diff --git a/pkg/store/mockstore/unistore/tikv/mvcc/tikv.go b/pkg/store/mockstore/unistore/tikv/mvcc/tikv.go new file mode 100644 index 0000000000000..55bb1f209d746 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/mvcc/tikv.go @@ -0,0 +1,131 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package mvcc + +import ( + "github.com/pingcap/badger/y" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/util/codec" +) + +// WriteType defines a write type. +type WriteType = byte + +// WriteType +const ( + WriteTypeLock WriteType = 'L' + WriteTypeRollback WriteType = 'R' + WriteTypeDelete WriteType = 'D' + WriteTypePut WriteType = 'P' +) + +// WriteCFValue represents a write CF value. +type WriteCFValue struct { + Type WriteType + StartTS uint64 + ShortVal []byte +} + +var errInvalidWriteCFValue = errors.New("invalid write CF value") + +// ParseWriteCFValue parses the []byte data and returns a WriteCFValue. +func ParseWriteCFValue(data []byte) (wv WriteCFValue, err error) { + if len(data) == 0 { + err = errInvalidWriteCFValue + return + } + wv.Type = data[0] + switch wv.Type { + case WriteTypePut, WriteTypeDelete, WriteTypeLock, WriteTypeRollback: + default: + err = errInvalidWriteCFValue + return + } + wv.ShortVal, wv.StartTS, err = codec.DecodeUvarint(data[1:]) + return +} + +const ( + shortValuePrefix = 'v' + forUpdatePrefix = 'f' + minCommitTsPrefix = 'm' + + //ShortValueMaxLen defines max length of short value. + ShortValueMaxLen = 64 +) + +// EncodeWriteCFValue accepts a write cf parameters and return the encoded bytes data. +// Just like the tikv encoding form. See tikv/src/storage/mvcc/write.rs for more detail. +func EncodeWriteCFValue(t WriteType, startTs uint64, shortVal []byte) []byte { + data := make([]byte, 0) + data = append(data, t) + data = codec.EncodeUvarint(data, startTs) + if len(shortVal) != 0 { + data = append(data, byte(shortValuePrefix), byte(len(shortVal))) + return append(data, shortVal...) + } + return data +} + +// EncodeLockCFValue encodes the mvcc lock and returns putLock value and putDefault value if exists. +func EncodeLockCFValue(lock *Lock) ([]byte, []byte) { + data := make([]byte, 0) + switch lock.Op { + case byte(kvrpcpb.Op_Put): + data = append(data, LockTypePut) + case byte(kvrpcpb.Op_Del): + data = append(data, LockTypeDelete) + case byte(kvrpcpb.Op_Lock): + data = append(data, LockTypeLock) + case byte(kvrpcpb.Op_PessimisticLock): + data = append(data, LockTypePessimistic) + default: + panic("invalid lock op") + } + var longValue []byte + data = codec.EncodeUvarint(codec.EncodeCompactBytes(data, lock.Primary), lock.StartTS) + data = codec.EncodeUvarint(data, uint64(lock.TTL)) + if len(lock.Value) <= ShortValueMaxLen { + if len(lock.Value) != 0 { + data = append(data, byte(shortValuePrefix), byte(len(lock.Value))) + data = append(data, lock.Value...) + } + } else { + longValue = y.SafeCopy(nil, lock.Value) + } + if lock.ForUpdateTS > 0 { + data = append(data, byte(forUpdatePrefix)) + data = codec.EncodeUint(data, lock.ForUpdateTS) + } + if lock.MinCommitTS > 0 { + data = append(data, byte(minCommitTsPrefix)) + data = codec.EncodeUint(data, lock.MinCommitTS) + } + return data, longValue +} + +// LockType defines a lock type. +type LockType = byte + +// LockType +const ( + LockTypePut LockType = 'P' + LockTypeDelete LockType = 'D' + LockTypeLock LockType = 'L' + LockTypePessimistic LockType = 'S' +) + +var errInvalidLockCFValue = errors.New("invalid lock CF value") diff --git a/store/mockstore/unistore/tikv/mvcc_test.go b/pkg/store/mockstore/unistore/tikv/mvcc_test.go similarity index 99% rename from store/mockstore/unistore/tikv/mvcc_test.go rename to pkg/store/mockstore/unistore/tikv/mvcc_test.go index 9cb97467bed34..9cfd00d5a06c7 100644 --- a/store/mockstore/unistore/tikv/mvcc_test.go +++ b/pkg/store/mockstore/unistore/tikv/mvcc_test.go @@ -28,11 +28,11 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/store/mockstore/unistore/config" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/store/mockstore/unistore/util/lockwaiter" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/util/lockwaiter" "github.com/stretchr/testify/require" ) diff --git a/pkg/store/mockstore/unistore/tikv/pberror/BUILD.bazel b/pkg/store/mockstore/unistore/tikv/pberror/BUILD.bazel new file mode 100644 index 0000000000000..18e4cef526603 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/pberror/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pberror", + srcs = ["pberror.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/pberror", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_kvproto//pkg/errorpb"], +) diff --git a/store/mockstore/unistore/tikv/pberror/pberror.go b/pkg/store/mockstore/unistore/tikv/pberror/pberror.go similarity index 100% rename from store/mockstore/unistore/tikv/pberror/pberror.go rename to pkg/store/mockstore/unistore/tikv/pberror/pberror.go diff --git a/pkg/store/mockstore/unistore/tikv/region.go b/pkg/store/mockstore/unistore/tikv/region.go new file mode 100644 index 0000000000000..0094d0612b231 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/region.go @@ -0,0 +1,798 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package tikv + +import ( + "bytes" + "context" + "encoding/binary" + "strconv" + "sync" + "sync/atomic" + "time" + "unsafe" + + "github.com/pingcap/badger" + "github.com/pingcap/badger/y" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/errorpb" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/kvproto/pkg/pdpb" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/metrics" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/pd" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/util/codec" + "go.uber.org/zap" +) + +// InternalKey +var ( + InternalKeyPrefix = []byte{0xff} + InternalRegionMetaPrefix = append(InternalKeyPrefix, "region"...) + InternalStoreMetaKey = append(InternalKeyPrefix, "store"...) + InternalSafePointKey = append(InternalKeyPrefix, "safepoint"...) +) + +// InternalRegionMetaKey returns internal region meta key with the given region id. +func InternalRegionMetaKey(regionID uint64) []byte { + return []byte(string(InternalRegionMetaPrefix) + strconv.FormatUint(regionID, 10)) +} + +// RegionCtx defines the region context interface. +type RegionCtx interface { + Meta() *metapb.Region + Diff() *int64 + RawStart() []byte + RawEnd() []byte + AcquireLatches(hashes []uint64) + ReleaseLatches(hashes []uint64) +} + +type regionCtx struct { + meta *metapb.Region + regionEpoch unsafe.Pointer // *metapb.RegionEpoch + rawStartKey []byte + rawEndKey []byte + approximateSize int64 + diff int64 + + latches *latches +} + +type latches struct { + slots [256]map[uint64]*sync.WaitGroup + locks [256]sync.Mutex +} + +func newLatches() *latches { + l := &latches{} + for i := 0; i < 256; i++ { + l.slots[i] = map[uint64]*sync.WaitGroup{} + } + return l +} + +func (l *latches) acquire(keyHashes []uint64) (waitCnt int) { + wg := new(sync.WaitGroup) + wg.Add(1) + for _, hash := range keyHashes { + waitCnt += l.acquireOne(hash, wg) + } + return +} + +func (l *latches) acquireOne(hash uint64, wg *sync.WaitGroup) (waitCnt int) { + slotID := hash >> 56 + for { + m := l.slots[slotID] + l.locks[slotID].Lock() + w, ok := m[hash] + if !ok { + m[hash] = wg + } + l.locks[slotID].Unlock() + if ok { + w.Wait() + waitCnt++ + continue + } + return + } +} + +func (l *latches) release(keyHashes []uint64) { + var w *sync.WaitGroup + for _, hash := range keyHashes { + slotID := hash >> 56 + l.locks[slotID].Lock() + m := l.slots[slotID] + if w == nil { + w = m[hash] + } + delete(m, hash) + l.locks[slotID].Unlock() + } + if w != nil { + w.Done() + } +} + +func newRegionCtx(meta *metapb.Region, latches *latches, _ interface{}) *regionCtx { + regCtx := ®ionCtx{ + meta: meta, + latches: latches, + regionEpoch: unsafe.Pointer(meta.GetRegionEpoch()), + } + regCtx.rawStartKey = regCtx.decodeRawStartKey() + regCtx.rawEndKey = regCtx.decodeRawEndKey() + if len(regCtx.rawEndKey) == 0 { + // Avoid reading internal meta data. + regCtx.rawEndKey = InternalKeyPrefix + } + return regCtx +} + +func (ri *regionCtx) Meta() *metapb.Region { + return ri.meta +} + +func (ri *regionCtx) Diff() *int64 { + return &ri.diff +} + +func (ri *regionCtx) RawStart() []byte { + return ri.rawStartKey +} + +func (ri *regionCtx) RawEnd() []byte { + return ri.rawEndKey +} + +func (ri *regionCtx) getRegionEpoch() *metapb.RegionEpoch { + return (*metapb.RegionEpoch)(atomic.LoadPointer(&ri.regionEpoch)) +} + +func (ri *regionCtx) updateRegionEpoch(epoch *metapb.RegionEpoch) { + atomic.StorePointer(&ri.regionEpoch, (unsafe.Pointer)(epoch)) +} + +func (ri *regionCtx) decodeRawStartKey() []byte { + if len(ri.meta.StartKey) == 0 { + return nil + } + _, rawKey, err := codec.DecodeBytes(ri.meta.StartKey, nil) + if err != nil { + panic("invalid region start key") + } + return rawKey +} + +func (ri *regionCtx) decodeRawEndKey() []byte { + if len(ri.meta.EndKey) == 0 { + return nil + } + _, rawKey, err := codec.DecodeBytes(ri.meta.EndKey, nil) + if err != nil { + panic("invalid region end key") + } + return rawKey +} + +func (ri *regionCtx) greaterEqualEndKey(key []byte) bool { + return len(ri.rawEndKey) > 0 && bytes.Compare(key, ri.rawEndKey) >= 0 +} + +func newPeerMeta(peerID, storeID uint64) *metapb.Peer { + return &metapb.Peer{ + Id: peerID, + StoreId: storeID, + } +} + +func (ri *regionCtx) incConfVer() { + ri.meta.RegionEpoch = &metapb.RegionEpoch{ + ConfVer: ri.meta.GetRegionEpoch().GetConfVer() + 1, + Version: ri.meta.GetRegionEpoch().GetVersion(), + } + ri.updateRegionEpoch(ri.meta.RegionEpoch) +} + +func (ri *regionCtx) addPeer(peerID, storeID uint64) { + ri.meta.Peers = append(ri.meta.Peers, newPeerMeta(peerID, storeID)) + ri.incConfVer() +} + +func (ri *regionCtx) unmarshal(data []byte) error { + ri.approximateSize = int64(binary.LittleEndian.Uint64(data)) + data = data[8:] + ri.meta = &metapb.Region{} + err := ri.meta.Unmarshal(data) + if err != nil { + return errors.Trace(err) + } + ri.rawStartKey = ri.decodeRawStartKey() + ri.rawEndKey = ri.decodeRawEndKey() + ri.regionEpoch = unsafe.Pointer(ri.meta.RegionEpoch) + return nil +} + +func (ri *regionCtx) marshal() []byte { + data := make([]byte, 8+ri.meta.Size()) + binary.LittleEndian.PutUint64(data, uint64(ri.approximateSize)) + _, err := ri.meta.MarshalTo(data[8:]) + if err != nil { + log.Error("region ctx marshal failed", zap.Error(err)) + } + return data +} + +// AcquireLatches add latches for all input hashVals, the input hashVals should be +// sorted and have no duplicates +func (ri *regionCtx) AcquireLatches(hashVals []uint64) { + start := time.Now() + waitCnt := ri.latches.acquire(hashVals) + dur := time.Since(start) + metrics.LatchWait.Observe(dur.Seconds()) + if dur > time.Millisecond*50 { + var id string + if ri.meta == nil { + id = "unknown" + } else { + id = strconv.FormatUint(ri.meta.Id, 10) + } + log.S().Warnf("region %s acquire %d locks takes %v, waitCnt %d", id, len(hashVals), dur, waitCnt) + } +} + +func (ri *regionCtx) ReleaseLatches(hashVals []uint64) { + ri.latches.release(hashVals) +} + +// RegionOptions represents the region options. +type RegionOptions struct { + StoreAddr string + PDAddr string + RegionSize int64 +} + +// RegionManager defines the region manager interface. +type RegionManager interface { + GetRegionFromCtx(ctx *kvrpcpb.Context) (RegionCtx, *errorpb.Error) + GetStoreInfoFromCtx(ctx *kvrpcpb.Context) (string, uint64, *errorpb.Error) + SplitRegion(req *kvrpcpb.SplitRegionRequest) *kvrpcpb.SplitRegionResponse + GetStoreIDByAddr(addr string) (uint64, error) + GetStoreAddrByStoreID(storeID uint64) (string, error) + Close() error +} + +type regionManager struct { + storeMeta *metapb.Store + mu sync.RWMutex + regions map[uint64]*regionCtx + latches *latches +} + +func (rm *regionManager) GetStoreIDByAddr(addr string) (uint64, error) { + if rm.storeMeta.Address != addr { + return 0, errors.New("store not match") + } + return rm.storeMeta.Id, nil +} + +func (rm *regionManager) GetStoreAddrByStoreID(storeID uint64) (string, error) { + if rm.storeMeta.Id != storeID { + return "", errors.New("store not match") + } + return rm.storeMeta.Address, nil +} + +func (rm *regionManager) GetStoreInfoFromCtx(ctx *kvrpcpb.Context) (string, uint64, *errorpb.Error) { + if ctx.GetPeer() != nil && ctx.GetPeer().GetStoreId() != rm.storeMeta.Id { + return "", 0, &errorpb.Error{ + Message: "store not match", + StoreNotMatch: &errorpb.StoreNotMatch{}, + } + } + return rm.storeMeta.Address, rm.storeMeta.Id, nil +} + +func (rm *regionManager) GetRegionFromCtx(ctx *kvrpcpb.Context) (RegionCtx, *errorpb.Error) { + ctxPeer := ctx.GetPeer() + if ctxPeer != nil && ctxPeer.GetStoreId() != rm.storeMeta.Id { + return nil, &errorpb.Error{ + Message: "store not match", + StoreNotMatch: &errorpb.StoreNotMatch{}, + } + } + rm.mu.RLock() + ri := rm.regions[ctx.RegionId] + rm.mu.RUnlock() + if ri == nil { + return nil, &errorpb.Error{ + Message: "region not found", + RegionNotFound: &errorpb.RegionNotFound{ + RegionId: ctx.GetRegionId(), + }, + } + } + // Region epoch does not match. + if rm.isEpochStale(ri.getRegionEpoch(), ctx.GetRegionEpoch()) { + return nil, &errorpb.Error{ + Message: "stale epoch", + EpochNotMatch: &errorpb.EpochNotMatch{ + CurrentRegions: []*metapb.Region{{ + Id: ri.meta.Id, + StartKey: ri.meta.StartKey, + EndKey: ri.meta.EndKey, + RegionEpoch: ri.getRegionEpoch(), + Peers: ri.meta.Peers, + }}, + }, + } + } + return ri, nil +} + +func (rm *regionManager) isEpochStale(lhs, rhs *metapb.RegionEpoch) bool { + return lhs.GetConfVer() != rhs.GetConfVer() || lhs.GetVersion() != rhs.GetVersion() +} + +func (rm *regionManager) loadFromLocal(bundle *mvcc.DBBundle, f func(*regionCtx)) error { + err := bundle.DB.View(func(txn *badger.Txn) error { + item, err1 := txn.Get(InternalStoreMetaKey) + if err1 != nil { + return err1 + } + val, err1 := item.Value() + if err1 != nil { + return err1 + } + err1 = rm.storeMeta.Unmarshal(val) + if err1 != nil { + return err1 + } + // load region meta + opts := badger.DefaultIteratorOptions + it := txn.NewIterator(opts) + defer it.Close() + prefix := InternalRegionMetaPrefix + for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { + item := it.Item() + val, err1 = item.Value() + if err1 != nil { + return err1 + } + r := new(regionCtx) + err := r.unmarshal(val) + if err != nil { + return errors.Trace(err) + } + r.latches = rm.latches + rm.regions[r.meta.Id] = r + f(r) + } + return nil + }) + if err == badger.ErrKeyNotFound { + err = nil + } + return err +} + +// StandAloneRegionManager represents a standalone region manager. +type StandAloneRegionManager struct { + regionManager + bundle *mvcc.DBBundle + pdc pd.Client + clusterID uint64 + regionSize int64 + closeCh chan struct{} + wg sync.WaitGroup +} + +// NewStandAloneRegionManager returns a new standalone region manager. +func NewStandAloneRegionManager(bundle *mvcc.DBBundle, opts RegionOptions, pdc pd.Client) *StandAloneRegionManager { + var err error + clusterID := pdc.GetClusterID(context.TODO()) + log.S().Infof("cluster id %v", clusterID) + rm := &StandAloneRegionManager{ + bundle: bundle, + pdc: pdc, + clusterID: clusterID, + regionSize: opts.RegionSize, + closeCh: make(chan struct{}), + regionManager: regionManager{ + regions: make(map[uint64]*regionCtx), + storeMeta: new(metapb.Store), + latches: newLatches(), + }, + } + err = rm.loadFromLocal(bundle, func(r *regionCtx) { + req := &pdpb.RegionHeartbeatRequest{ + Region: r.meta, + Leader: r.meta.Peers[0], + ApproximateSize: uint64(r.approximateSize), + } + rm.pdc.ReportRegion(req) + }) + if err != nil { + log.Fatal("load from local failed", zap.Error(err)) + } + if rm.storeMeta.Id == 0 { + err = rm.initStore(opts.StoreAddr) + if err != nil { + log.Fatal("init store failed", zap.Error(err)) + } + } + rm.storeMeta.Address = opts.StoreAddr + err = rm.pdc.PutStore(context.TODO(), rm.storeMeta) + if err != nil { + log.Fatal("put store failed", zap.Error(err)) + } + rm.wg.Add(2) + go rm.runSplitWorker() + go rm.storeHeartBeatLoop() + return rm +} + +func (rm *StandAloneRegionManager) initStore(storeAddr string) error { + log.Info("initializing store") + ids, err := rm.allocIDs(3) + if err != nil { + return err + } + storeID, regionID, peerID := ids[0], ids[1], ids[2] + rm.storeMeta.Id = storeID + rm.storeMeta.Address = storeAddr + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + rootRegion := &metapb.Region{ + Id: regionID, + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []*metapb.Peer{{Id: peerID, StoreId: storeID}}, + } + rm.regions[rootRegion.Id] = newRegionCtx(rootRegion, rm.latches, nil) + _, err = rm.pdc.Bootstrap(ctx, rm.storeMeta, rootRegion) + cancel() + if err != nil { + log.Fatal("initialize failed", zap.Error(err)) + } + rm.initialSplit(rootRegion) + storeBuf, err := rm.storeMeta.Marshal() + if err != nil { + log.Fatal("marshal store meta failed", zap.Error(err)) + } + err = rm.bundle.DB.Update(func(txn *badger.Txn) error { + ts := atomic.AddUint64(&rm.bundle.StateTS, 1) + err = txn.SetEntry(&badger.Entry{ + Key: y.KeyWithTs(InternalStoreMetaKey, ts), + Value: storeBuf, + }) + if err != nil { + return err + } + for rid, region := range rm.regions { + regionBuf := region.marshal() + err = txn.SetEntry(&badger.Entry{ + Key: y.KeyWithTs(InternalRegionMetaKey(rid), ts), + Value: regionBuf, + }) + if err != nil { + log.Fatal("save region info failed", zap.Error(err)) + } + } + return nil + }) + for _, region := range rm.regions { + req := &pdpb.RegionHeartbeatRequest{ + Region: region.meta, + Leader: region.meta.Peers[0], + ApproximateSize: uint64(region.approximateSize), + } + rm.pdc.ReportRegion(req) + } + log.Info("Initialize success") + return nil +} + +// initSplit splits the cluster into multiple regions. +func (rm *StandAloneRegionManager) initialSplit(root *metapb.Region) { + root.EndKey = codec.EncodeBytes(nil, []byte{'m'}) + root.RegionEpoch.Version = 2 + rm.regions[root.Id] = newRegionCtx(root, rm.latches, nil) + preSplitStartKeys := [][]byte{{'m'}, {'n'}, {'t'}, {'u'}} + ids, err := rm.allocIDs(len(preSplitStartKeys) * 2) + if err != nil { + log.Fatal("alloc ids failed", zap.Error(err)) + } + for i, startKey := range preSplitStartKeys { + var endKey []byte + if i < len(preSplitStartKeys)-1 { + endKey = codec.EncodeBytes(nil, preSplitStartKeys[i+1]) + } + newRegion := &metapb.Region{ + Id: ids[i*2], + RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, + Peers: []*metapb.Peer{{Id: ids[i*2+1], StoreId: rm.storeMeta.Id}}, + StartKey: codec.EncodeBytes(nil, startKey), + EndKey: endKey, + } + rm.regions[newRegion.Id] = newRegionCtx(newRegion, rm.latches, nil) + } +} + +func (rm *StandAloneRegionManager) allocIDs(n int) ([]uint64, error) { + ids := make([]uint64, n) + for i := 0; i < n; i++ { + id, err := rm.pdc.AllocID(context.Background()) + if err != nil { + return nil, errors.Trace(err) + } + ids[i] = id + } + return ids, nil +} + +func (rm *StandAloneRegionManager) storeHeartBeatLoop() { + defer rm.wg.Done() + ticker := time.Tick(time.Second * 3) + for { + select { + case <-rm.closeCh: + return + case <-ticker: + } + storeStats := new(pdpb.StoreStats) + storeStats.StoreId = rm.storeMeta.Id + storeStats.Available = 1024 * 1024 * 1024 + rm.mu.RLock() + storeStats.RegionCount = uint32(len(rm.regions)) + rm.mu.RUnlock() + storeStats.Capacity = 2048 * 1024 * 1024 + if err := rm.pdc.StoreHeartbeat(context.Background(), storeStats); err != nil { + log.Warn("store heartbeat failed", zap.Error(err)) + } + } +} + +type keySample struct { + key []byte + leftSize int64 +} + +// sampler samples keys in a region for later pick a split key. +type sampler struct { + samples [64]keySample + length int + step int + scanned int + totalSize int64 +} + +func newSampler() *sampler { + return &sampler{step: 1} +} + +func (s *sampler) shrinkIfNeeded() { + if s.length < len(s.samples) { + return + } + for i := 0; i < len(s.samples)/2; i++ { + s.samples[i], s.samples[i*2] = s.samples[i*2], s.samples[i] + } + s.length /= 2 + s.step *= 2 +} + +func (s *sampler) shouldSample() bool { + // It's an optimization for 's.scanned % s.step == 0' + return s.scanned&(s.step-1) == 0 +} + +func (s *sampler) scanKey(key []byte, size int64) { + s.totalSize += size + s.scanned++ + if s.shouldSample() { + sample := s.samples[s.length] + // safe copy the key. + sample.key = append(sample.key[:0], key...) + sample.leftSize = s.totalSize + s.samples[s.length] = sample + s.length++ + s.shrinkIfNeeded() + } +} + +func (s *sampler) getSplitKeyAndSize() ([]byte, int64) { + targetSize := s.totalSize * 2 / 3 + for _, sample := range s.samples[:s.length] { + if sample.leftSize >= targetSize { + return sample.key, sample.leftSize + } + } + return []byte{}, 0 +} + +func (rm *StandAloneRegionManager) runSplitWorker() { + defer rm.wg.Done() + ticker := time.NewTicker(time.Second * 5) + var regionsToCheck []*regionCtx + var regionsToSave []*regionCtx + for { + regionsToCheck = regionsToCheck[:0] + rm.mu.RLock() + for _, ri := range rm.regions { + if ri.approximateSize+atomic.LoadInt64(&ri.diff) > rm.regionSize*3/2 { + regionsToCheck = append(regionsToCheck, ri) + } + } + rm.mu.RUnlock() + for _, ri := range regionsToCheck { + err := rm.splitCheckRegion(ri) + if err != nil { + log.Error("split region failed", zap.Error(err)) + } + } + + regionsToSave = regionsToSave[:0] + rm.mu.RLock() + for _, ri := range rm.regions { + if atomic.LoadInt64(&ri.diff) > rm.regionSize/8 { + regionsToSave = append(regionsToSave, ri) + } + } + rm.mu.RUnlock() + rm.saveSize(regionsToSave) + select { + case <-rm.closeCh: + return + case <-ticker.C: + } + } +} + +func (rm *StandAloneRegionManager) saveSize(regionsToSave []*regionCtx) { + err1 := rm.bundle.DB.Update(func(txn *badger.Txn) error { + ts := atomic.AddUint64(&rm.bundle.StateTS, 1) + for _, ri := range regionsToSave { + ri.approximateSize += atomic.LoadInt64(&ri.diff) + err := txn.SetEntry(&badger.Entry{ + Key: y.KeyWithTs(InternalRegionMetaKey(ri.meta.Id), ts), + Value: ri.marshal(), + }) + if err != nil { + return err + } + } + return nil + }) + if err1 != nil { + log.Error("region manager save size failed", zap.Error(err1)) + } +} + +func (rm *StandAloneRegionManager) splitCheckRegion(region *regionCtx) error { + s := newSampler() + err := rm.bundle.DB.View(func(txn *badger.Txn) error { + iter := txn.NewIterator(badger.IteratorOptions{}) + defer iter.Close() + for iter.Seek(region.rawStartKey); iter.Valid(); iter.Next() { + item := iter.Item() + if region.greaterEqualEndKey(item.Key()) { + break + } + s.scanKey(item.Key(), int64(len(item.Key())+item.ValueSize())) + } + return nil + }) + if err != nil { + log.Error("sample region failed", zap.Error(err)) + return errors.Trace(err) + } + // Need to update the diff to avoid split check again. + atomic.StoreInt64(®ion.diff, s.totalSize-region.approximateSize) + if s.totalSize < rm.regionSize { + return nil + } + splitKey, leftSize := s.getSplitKeyAndSize() + log.Info("try to split region", zap.Uint64("id", region.meta.Id), zap.Binary("split key", splitKey), + zap.Int64("left size", leftSize), zap.Int64("right size", s.totalSize-leftSize)) + err = rm.splitRegion(region, splitKey, s.totalSize, leftSize) + if err != nil { + log.Error("split region failed", zap.Error(err)) + } + return errors.Trace(err) +} + +func (rm *StandAloneRegionManager) splitRegion(oldRegionCtx *regionCtx, splitKey []byte, oldSize, leftSize int64) error { + oldRegion := oldRegionCtx.meta + rightMeta := &metapb.Region{ + Id: oldRegion.Id, + StartKey: codec.EncodeBytes(nil, splitKey), + EndKey: oldRegion.EndKey, + RegionEpoch: &metapb.RegionEpoch{ + ConfVer: oldRegion.RegionEpoch.ConfVer, + Version: oldRegion.RegionEpoch.Version + 1, + }, + Peers: oldRegion.Peers, + } + right := newRegionCtx(rightMeta, rm.latches, nil) + right.approximateSize = oldSize - leftSize + id, err := rm.pdc.AllocID(context.Background()) + if err != nil { + return errors.Trace(err) + } + leftMeta := &metapb.Region{ + Id: id, + StartKey: oldRegion.StartKey, + EndKey: codec.EncodeBytes(nil, splitKey), + RegionEpoch: &metapb.RegionEpoch{ + ConfVer: 1, + Version: 1, + }, + Peers: oldRegion.Peers, + } + left := newRegionCtx(leftMeta, rm.latches, nil) + left.approximateSize = leftSize + err1 := rm.bundle.DB.Update(func(txn *badger.Txn) error { + ts := atomic.AddUint64(&rm.bundle.StateTS, 1) + err := txn.SetEntry(&badger.Entry{ + Key: y.KeyWithTs(InternalRegionMetaKey(left.meta.Id), ts), + Value: left.marshal(), + }) + if err != nil { + return errors.Trace(err) + } + err = txn.SetEntry(&badger.Entry{ + Key: y.KeyWithTs(InternalRegionMetaKey(right.meta.Id), ts), + Value: right.marshal(), + }) + return errors.Trace(err) + }) + if err1 != nil { + return errors.Trace(err1) + } + rm.mu.Lock() + rm.regions[left.meta.Id] = left + rm.regions[right.meta.Id] = right + rm.mu.Unlock() + rm.pdc.ReportRegion(&pdpb.RegionHeartbeatRequest{ + Region: right.meta, + Leader: right.meta.Peers[0], + ApproximateSize: uint64(right.approximateSize), + }) + rm.pdc.ReportRegion(&pdpb.RegionHeartbeatRequest{ + Region: left.meta, + Leader: left.meta.Peers[0], + ApproximateSize: uint64(left.approximateSize), + }) + log.Info("region splitted", zap.Uint64("old id", oldRegion.Id), + zap.Uint64("left id", left.meta.Id), zap.Int64("left size", left.approximateSize), + zap.Uint64("right id", right.meta.Id), zap.Int64("right size", right.approximateSize)) + return nil +} + +// SplitRegion splits a region. +func (rm *StandAloneRegionManager) SplitRegion(req *kvrpcpb.SplitRegionRequest) *kvrpcpb.SplitRegionResponse { + return &kvrpcpb.SplitRegionResponse{} +} + +// Close closes the standalone region manager. +func (rm *StandAloneRegionManager) Close() error { + close(rm.closeCh) + rm.wg.Wait() + return nil +} diff --git a/pkg/store/mockstore/unistore/tikv/server.go b/pkg/store/mockstore/unistore/tikv/server.go new file mode 100644 index 0000000000000..79656e635d4c1 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/server.go @@ -0,0 +1,1175 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package tikv + +import ( + "context" + "io" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/coprocessor" + deadlockPb "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/kvproto/pkg/errorpb" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/kvproto/pkg/mpp" + "github.com/pingcap/kvproto/pkg/tikvpb" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/client" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/cophandler" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/kverrors" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/pberror" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/util/lockwaiter" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +var _ tikvpb.TikvServer = new(Server) + +// Server implements the tikvpb.TikvServer interface. +type Server struct { + // After updating the kvproto, some methods of TikvServer are not implemented. + // Construct `Server` based on `UnimplementedTikvServer`, in order to compile successfully + tikvpb.UnimplementedTikvServer + mvccStore *MVCCStore + regionManager RegionManager + innerServer InnerServer + RPCClient client.Client + refCount int32 + stopped int32 +} + +// NewServer returns a new server. +func NewServer(rm RegionManager, store *MVCCStore, innerServer InnerServer) *Server { + return &Server{ + mvccStore: store, + regionManager: rm, + innerServer: innerServer, + } +} + +// Stop stops the server. +func (svr *Server) Stop() { + atomic.StoreInt32(&svr.stopped, 1) + for { + if atomic.LoadInt32(&svr.refCount) == 0 { + break + } + time.Sleep(time.Millisecond * 10) + } + + if err := svr.mvccStore.Close(); err != nil { + log.Error("close mvcc store failed", zap.Error(err)) + } + if err := svr.regionManager.Close(); err != nil { + log.Error("close region manager failed", zap.Error(err)) + } + if err := svr.innerServer.Stop(); err != nil { + log.Error("close inner server failed", zap.Error(err)) + } +} + +// GetStoreIDByAddr gets a store id by the store address. +func (svr *Server) GetStoreIDByAddr(addr string) (uint64, error) { + return svr.regionManager.GetStoreIDByAddr(addr) +} + +// GetStoreAddrByStoreID gets a store address by the store id. +func (svr *Server) GetStoreAddrByStoreID(storeID uint64) (string, error) { + return svr.regionManager.GetStoreAddrByStoreID(storeID) +} + +type requestCtx struct { + svr *Server + regCtx RegionCtx + regErr *errorpb.Error + buf []byte + reader *dbreader.DBReader + method string + startTime time.Time + rpcCtx *kvrpcpb.Context + storeAddr string + storeID uint64 + asyncMinCommitTS uint64 + onePCCommitTS uint64 +} + +func newRequestCtx(svr *Server, ctx *kvrpcpb.Context, method string) (*requestCtx, error) { + atomic.AddInt32(&svr.refCount, 1) + if atomic.LoadInt32(&svr.stopped) > 0 { + atomic.AddInt32(&svr.refCount, -1) + return nil, kverrors.ErrRetryable("server is closed") + } + req := &requestCtx{ + svr: svr, + method: method, + startTime: time.Now(), + rpcCtx: ctx, + } + req.regCtx, req.regErr = svr.regionManager.GetRegionFromCtx(ctx) + storeAddr, storeID, regErr := svr.regionManager.GetStoreInfoFromCtx(ctx) + req.storeAddr = storeAddr + req.storeID = storeID + if regErr != nil { + req.regErr = regErr + } + return req, nil +} + +// For read-only requests that doesn't acquire latches, this function must be called after all locks has been checked. +func (req *requestCtx) getDBReader() *dbreader.DBReader { + if req.reader == nil { + mvccStore := req.svr.mvccStore + txn := mvccStore.db.NewTransaction(false) + req.reader = dbreader.NewDBReader(req.regCtx.RawStart(), req.regCtx.RawEnd(), txn) + req.reader.RcCheckTS = req.isRcCheckTSIsolationLevel() + } + return req.reader +} + +func (req *requestCtx) isSnapshotIsolation() bool { + return req.rpcCtx.IsolationLevel == kvrpcpb.IsolationLevel_SI +} + +func (req *requestCtx) isRcCheckTSIsolationLevel() bool { + return req.rpcCtx.IsolationLevel == kvrpcpb.IsolationLevel_RCCheckTS +} + +func (req *requestCtx) finish() { + atomic.AddInt32(&req.svr.refCount, -1) + if req.reader != nil { + req.reader.Close() + } +} + +// KvGet implements the tikvpb.TikvServer interface. +func (svr *Server) KvGet(ctx context.Context, req *kvrpcpb.GetRequest) (*kvrpcpb.GetResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvGet") + if err != nil { + return &kvrpcpb.GetResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.GetResponse{RegionError: reqCtx.regErr}, nil + } + val, err := svr.mvccStore.Get(reqCtx, req.Key, req.Version) + return &kvrpcpb.GetResponse{ + Value: val, + Error: convertToKeyError(err), + }, nil +} + +// KvScan implements the tikvpb.TikvServer interface. +func (svr *Server) KvScan(ctx context.Context, req *kvrpcpb.ScanRequest) (*kvrpcpb.ScanResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvScan") + if err != nil { + return &kvrpcpb.ScanResponse{Pairs: []*kvrpcpb.KvPair{{Error: convertToKeyError(err)}}}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.ScanResponse{RegionError: reqCtx.regErr}, nil + } + pairs := svr.mvccStore.Scan(reqCtx, req) + return &kvrpcpb.ScanResponse{ + Pairs: pairs, + }, nil +} + +// KvPessimisticLock implements the tikvpb.TikvServer interface. +func (svr *Server) KvPessimisticLock(ctx context.Context, req *kvrpcpb.PessimisticLockRequest) (*kvrpcpb.PessimisticLockResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "PessimisticLock") + if err != nil { + return &kvrpcpb.PessimisticLockResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.PessimisticLockResponse{RegionError: reqCtx.regErr}, nil + } + resp := &kvrpcpb.PessimisticLockResponse{} + waiter, err := svr.mvccStore.PessimisticLock(reqCtx, req, resp) + resp.Errors, resp.RegionError = convertToPBErrors(err) + if waiter == nil { + return resp, nil + } + result := waiter.Wait() + svr.mvccStore.DeadlockDetectCli.CleanUpWaitFor(req.StartVersion, waiter.LockTS, waiter.KeyHash) + svr.mvccStore.lockWaiterManager.CleanUp(waiter) + if result.WakeupSleepTime == lockwaiter.WaitTimeout { + return resp, nil + } + if result.DeadlockResp != nil { + log.Error("deadlock found", zap.Stringer("entry", &result.DeadlockResp.Entry)) + errLocked := err.(*kverrors.ErrLocked) + deadlockErr := &kverrors.ErrDeadlock{ + LockKey: errLocked.Key, + LockTS: errLocked.Lock.StartTS, + DeadlockKeyHash: result.DeadlockResp.DeadlockKeyHash, + WaitChain: result.DeadlockResp.WaitChain, + } + resp.Errors, resp.RegionError = convertToPBErrors(deadlockErr) + if req.WakeUpMode == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { + resp.Results = []*kvrpcpb.PessimisticLockKeyResult{ + { + Type: kvrpcpb.PessimisticLockKeyResultType_LockResultFailed, + }, + } + } + return resp, nil + } + if result.WakeupSleepTime == lockwaiter.WakeUpThisWaiter { + if req.Force || req.WakeUpMode == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { + req.WaitTimeout = lockwaiter.LockNoWait + resp = &kvrpcpb.PessimisticLockResponse{} + _, err := svr.mvccStore.PessimisticLock(reqCtx, req, resp) + resp.Errors, resp.RegionError = convertToPBErrors(err) + if err == nil { + return resp, nil + } + if _, ok := err.(*kverrors.ErrLocked); !ok { + resp.Errors, resp.RegionError = convertToPBErrors(err) + return resp, nil + } + log.Warn("wakeup force lock request, try lock still failed", zap.Error(err)) + } + } + // The key is rollbacked, we don't have the exact commitTS, but we can use the server's latest. + // Always use the store latest ts since the waiter result commitTs may not be the real conflict ts + conflictCommitTS := svr.mvccStore.getLatestTS() + err = &kverrors.ErrConflict{ + StartTS: req.GetForUpdateTs(), + ConflictTS: waiter.LockTS, + ConflictCommitTS: conflictCommitTS, + } + resp.Errors, _ = convertToPBErrors(err) + return resp, nil +} + +// KVPessimisticRollback implements the tikvpb.TikvServer interface. +func (svr *Server) KVPessimisticRollback(ctx context.Context, req *kvrpcpb.PessimisticRollbackRequest) (*kvrpcpb.PessimisticRollbackResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "PessimisticRollback") + if err != nil { + return &kvrpcpb.PessimisticRollbackResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.PessimisticRollbackResponse{RegionError: reqCtx.regErr}, nil + } + err = svr.mvccStore.PessimisticRollback(reqCtx, req) + resp := &kvrpcpb.PessimisticRollbackResponse{} + resp.Errors, resp.RegionError = convertToPBErrors(err) + return resp, nil +} + +// KvTxnHeartBeat implements the tikvpb.TikvServer interface. +func (svr *Server) KvTxnHeartBeat(ctx context.Context, req *kvrpcpb.TxnHeartBeatRequest) (*kvrpcpb.TxnHeartBeatResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "TxnHeartBeat") + if err != nil { + return &kvrpcpb.TxnHeartBeatResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.TxnHeartBeatResponse{RegionError: reqCtx.regErr}, nil + } + lockTTL, err := svr.mvccStore.TxnHeartBeat(reqCtx, req) + resp := &kvrpcpb.TxnHeartBeatResponse{LockTtl: lockTTL} + resp.Error, resp.RegionError = convertToPBError(err) + return resp, nil +} + +// KvCheckTxnStatus implements the tikvpb.TikvServer interface. +func (svr *Server) KvCheckTxnStatus(ctx context.Context, req *kvrpcpb.CheckTxnStatusRequest) (*kvrpcpb.CheckTxnStatusResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvCheckTxnStatus") + if err != nil { + return &kvrpcpb.CheckTxnStatusResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.CheckTxnStatusResponse{RegionError: reqCtx.regErr}, nil + } + txnStatus, err := svr.mvccStore.CheckTxnStatus(reqCtx, req) + ttl := uint64(0) + if txnStatus.lockInfo != nil { + ttl = txnStatus.lockInfo.LockTtl + } + resp := &kvrpcpb.CheckTxnStatusResponse{ + LockTtl: ttl, + CommitVersion: txnStatus.commitTS, + Action: txnStatus.action, + LockInfo: txnStatus.lockInfo, + } + resp.Error, resp.RegionError = convertToPBError(err) + return resp, nil +} + +// KvCheckSecondaryLocks implements the tikvpb.TikvServer interface. +func (svr *Server) KvCheckSecondaryLocks(ctx context.Context, req *kvrpcpb.CheckSecondaryLocksRequest) (*kvrpcpb.CheckSecondaryLocksResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvCheckSecondaryLocks") + if err != nil { + return &kvrpcpb.CheckSecondaryLocksResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.CheckSecondaryLocksResponse{RegionError: reqCtx.regErr}, nil + } + locksStatus, err := svr.mvccStore.CheckSecondaryLocks(reqCtx, req.Keys, req.StartVersion) + resp := &kvrpcpb.CheckSecondaryLocksResponse{} + if err == nil { + resp.Locks = locksStatus.locks + resp.CommitTs = locksStatus.commitTS + } else { + resp.Error, resp.RegionError = convertToPBError(err) + } + return resp, nil +} + +// KvPrewrite implements the tikvpb.TikvServer interface. +func (svr *Server) KvPrewrite(ctx context.Context, req *kvrpcpb.PrewriteRequest) (*kvrpcpb.PrewriteResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvPrewrite") + if err != nil { + return &kvrpcpb.PrewriteResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.PrewriteResponse{RegionError: reqCtx.regErr}, nil + } + err = svr.mvccStore.Prewrite(reqCtx, req) + resp := &kvrpcpb.PrewriteResponse{} + if reqCtx.asyncMinCommitTS > 0 { + resp.MinCommitTs = reqCtx.asyncMinCommitTS + } + if reqCtx.onePCCommitTS > 0 { + resp.OnePcCommitTs = reqCtx.onePCCommitTS + } + resp.Errors, resp.RegionError = convertToPBErrors(err) + return resp, nil +} + +// KvCommit implements the tikvpb.TikvServer interface. +func (svr *Server) KvCommit(ctx context.Context, req *kvrpcpb.CommitRequest) (*kvrpcpb.CommitResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvCommit") + if err != nil { + return &kvrpcpb.CommitResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.CommitResponse{RegionError: reqCtx.regErr}, nil + } + resp := new(kvrpcpb.CommitResponse) + err = svr.mvccStore.Commit(reqCtx, req.Keys, req.GetStartVersion(), req.GetCommitVersion()) + if err != nil { + resp.Error, resp.RegionError = convertToPBError(err) + } + return resp, nil +} + +// RawGetKeyTTL implements the tikvpb.TikvServer interface. +func (svr *Server) RawGetKeyTTL(ctx context.Context, req *kvrpcpb.RawGetKeyTTLRequest) (*kvrpcpb.RawGetKeyTTLResponse, error) { + // TODO + return &kvrpcpb.RawGetKeyTTLResponse{}, nil +} + +// KvImport implements the tikvpb.TikvServer interface. +func (svr *Server) KvImport(context.Context, *kvrpcpb.ImportRequest) (*kvrpcpb.ImportResponse, error) { + // TODO + return &kvrpcpb.ImportResponse{}, nil +} + +// KvCleanup implements the tikvpb.TikvServer interface. +func (svr *Server) KvCleanup(ctx context.Context, req *kvrpcpb.CleanupRequest) (*kvrpcpb.CleanupResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvCleanup") + if err != nil { + return &kvrpcpb.CleanupResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.CleanupResponse{RegionError: reqCtx.regErr}, nil + } + err = svr.mvccStore.Cleanup(reqCtx, req.Key, req.StartVersion, req.CurrentTs) + resp := new(kvrpcpb.CleanupResponse) + if committed, ok := err.(kverrors.ErrAlreadyCommitted); ok { + resp.CommitVersion = uint64(committed) + } else if err != nil { + log.Error("cleanup failed", zap.Error(err)) + resp.Error, resp.RegionError = convertToPBError(err) + } + return resp, nil +} + +// KvBatchGet implements the tikvpb.TikvServer interface. +func (svr *Server) KvBatchGet(ctx context.Context, req *kvrpcpb.BatchGetRequest) (*kvrpcpb.BatchGetResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvBatchGet") + if err != nil { + return &kvrpcpb.BatchGetResponse{Pairs: []*kvrpcpb.KvPair{{Error: convertToKeyError(err)}}}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.BatchGetResponse{RegionError: reqCtx.regErr}, nil + } + pairs := svr.mvccStore.BatchGet(reqCtx, req.Keys, req.GetVersion()) + return &kvrpcpb.BatchGetResponse{ + Pairs: pairs, + }, nil +} + +// KvBatchRollback implements the tikvpb.TikvServer interface. +func (svr *Server) KvBatchRollback(ctx context.Context, req *kvrpcpb.BatchRollbackRequest) (*kvrpcpb.BatchRollbackResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvBatchRollback") + if err != nil { + return &kvrpcpb.BatchRollbackResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.BatchRollbackResponse{RegionError: reqCtx.regErr}, nil + } + resp := new(kvrpcpb.BatchRollbackResponse) + err = svr.mvccStore.Rollback(reqCtx, req.Keys, req.StartVersion) + resp.Error, resp.RegionError = convertToPBError(err) + return resp, nil +} + +// KvScanLock implements the tikvpb.TikvServer interface. +func (svr *Server) KvScanLock(ctx context.Context, req *kvrpcpb.ScanLockRequest) (*kvrpcpb.ScanLockResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvScanLock") + if err != nil { + return &kvrpcpb.ScanLockResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.ScanLockResponse{RegionError: reqCtx.regErr}, nil + } + log.Debug("kv scan lock") + locks, err := svr.mvccStore.ScanLock(reqCtx, req.MaxVersion, int(req.Limit)) + return &kvrpcpb.ScanLockResponse{Error: convertToKeyError(err), Locks: locks}, nil +} + +// KvResolveLock implements the tikvpb.TikvServer interface. +func (svr *Server) KvResolveLock(ctx context.Context, req *kvrpcpb.ResolveLockRequest) (*kvrpcpb.ResolveLockResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvResolveLock") + if err != nil { + return &kvrpcpb.ResolveLockResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.ResolveLockResponse{RegionError: reqCtx.regErr}, nil + } + resp := &kvrpcpb.ResolveLockResponse{} + if len(req.TxnInfos) > 0 { + for _, txnInfo := range req.TxnInfos { + log.S().Debugf("kv resolve lock region:%d txn:%v", reqCtx.regCtx.Meta().Id, txnInfo.Txn) + err := svr.mvccStore.ResolveLock(reqCtx, nil, txnInfo.Txn, txnInfo.Status) + if err != nil { + resp.Error, resp.RegionError = convertToPBError(err) + break + } + } + } else { + log.S().Debugf("kv resolve lock region:%d txn:%v", reqCtx.regCtx.Meta().Id, req.StartVersion) + err := svr.mvccStore.ResolveLock(reqCtx, req.Keys, req.StartVersion, req.CommitVersion) + resp.Error, resp.RegionError = convertToPBError(err) + } + return resp, nil +} + +// KvGC implements the tikvpb.TikvServer interface. +func (svr *Server) KvGC(ctx context.Context, req *kvrpcpb.GCRequest) (*kvrpcpb.GCResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvGC") + if err != nil { + return &kvrpcpb.GCResponse{Error: convertToKeyError(err)}, nil + } + defer reqCtx.finish() + svr.mvccStore.UpdateSafePoint(req.SafePoint) + return &kvrpcpb.GCResponse{}, nil +} + +// KvDeleteRange implements the tikvpb.TikvServer interface. +func (svr *Server) KvDeleteRange(ctx context.Context, req *kvrpcpb.DeleteRangeRequest) (*kvrpcpb.DeleteRangeResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "KvDeleteRange") + if err != nil { + return &kvrpcpb.DeleteRangeResponse{Error: convertToKeyError(err).String()}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.DeleteRangeResponse{RegionError: reqCtx.regErr}, nil + } + err = svr.mvccStore.dbWriter.DeleteRange(req.StartKey, req.EndKey, reqCtx.regCtx) + if err != nil { + log.Error("delete range failed", zap.Error(err)) + } + return &kvrpcpb.DeleteRangeResponse{}, nil +} + +// RawKV commands. + +// RawGet implements the tikvpb.TikvServer interface. +func (svr *Server) RawGet(context.Context, *kvrpcpb.RawGetRequest) (*kvrpcpb.RawGetResponse, error) { + return &kvrpcpb.RawGetResponse{}, nil +} + +// RawPut implements the tikvpb.TikvServer interface. +func (svr *Server) RawPut(context.Context, *kvrpcpb.RawPutRequest) (*kvrpcpb.RawPutResponse, error) { + return &kvrpcpb.RawPutResponse{}, nil +} + +// RawDelete implements the tikvpb.TikvServer interface. +func (svr *Server) RawDelete(context.Context, *kvrpcpb.RawDeleteRequest) (*kvrpcpb.RawDeleteResponse, error) { + return &kvrpcpb.RawDeleteResponse{}, nil +} + +// RawScan implements the tikvpb.TikvServer interface. +func (svr *Server) RawScan(context.Context, *kvrpcpb.RawScanRequest) (*kvrpcpb.RawScanResponse, error) { + return &kvrpcpb.RawScanResponse{}, nil +} + +// RawBatchDelete implements the tikvpb.TikvServer interface. +func (svr *Server) RawBatchDelete(context.Context, *kvrpcpb.RawBatchDeleteRequest) (*kvrpcpb.RawBatchDeleteResponse, error) { + return &kvrpcpb.RawBatchDeleteResponse{}, nil +} + +// RawBatchGet implements the tikvpb.TikvServer interface. +func (svr *Server) RawBatchGet(context.Context, *kvrpcpb.RawBatchGetRequest) (*kvrpcpb.RawBatchGetResponse, error) { + return &kvrpcpb.RawBatchGetResponse{}, nil +} + +// RawBatchPut implements the tikvpb.TikvServer interface. +func (svr *Server) RawBatchPut(context.Context, *kvrpcpb.RawBatchPutRequest) (*kvrpcpb.RawBatchPutResponse, error) { + return &kvrpcpb.RawBatchPutResponse{}, nil +} + +// RawBatchScan implements the tikvpb.TikvServer interface. +func (svr *Server) RawBatchScan(context.Context, *kvrpcpb.RawBatchScanRequest) (*kvrpcpb.RawBatchScanResponse, error) { + return &kvrpcpb.RawBatchScanResponse{}, nil +} + +// RawDeleteRange implements the tikvpb.TikvServer interface. +func (svr *Server) RawDeleteRange(context.Context, *kvrpcpb.RawDeleteRangeRequest) (*kvrpcpb.RawDeleteRangeResponse, error) { + return &kvrpcpb.RawDeleteRangeResponse{}, nil +} + +// SQL push down commands. + +// Coprocessor implements the tikvpb.TikvServer interface. +func (svr *Server) Coprocessor(ctx context.Context, req *coprocessor.Request) (*coprocessor.Response, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "Coprocessor") + if err != nil { + return &coprocessor.Response{OtherError: convertToKeyError(err).String()}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &coprocessor.Response{RegionError: reqCtx.regErr}, nil + } + resp := cophandler.HandleCopRequest(reqCtx.getDBReader(), svr.mvccStore.lockStore, req) + resp.BatchResponses = svr.StoreBatchCoprocessor(ctx, req) + return resp, nil +} + +// StoreBatchCoprocessor handle batched tasks in the same store. +func (svr *Server) StoreBatchCoprocessor(ctx context.Context, req *coprocessor.Request) []*coprocessor.StoreBatchTaskResponse { + if len(req.Tasks) == 0 { + return nil + } + tasks := req.Tasks + batchResps := make([]*coprocessor.StoreBatchTaskResponse, 0, len(tasks)) + handleBatchResp := func(task *coprocessor.StoreBatchTask) { + var err error + batchResp := &coprocessor.StoreBatchTaskResponse{ + TaskId: task.TaskId, + } + defer func() { + if err != nil { + batchResp.OtherError = err.Error() + } + batchResps = append(batchResps, batchResp) + }() + bytes, err := req.Marshal() + if err != nil { + return + } + taskReq := &coprocessor.Request{} + // deep clone req + if err = taskReq.Unmarshal(bytes); err != nil { + return + } + taskReq.Tasks = nil + taskReq.IsCacheEnabled = false + taskReq.Ranges = task.Ranges + taskReq.Context.RegionId = task.RegionId + taskReq.Context.RegionEpoch = task.RegionEpoch + taskReq.Context.Peer = task.Peer + resp, err := svr.Coprocessor(ctx, taskReq) + if err != nil { + return + } + batchResp.RegionError = resp.RegionError + batchResp.Locked = resp.Locked + batchResp.OtherError = resp.OtherError + batchResp.ExecDetailsV2 = resp.ExecDetailsV2 + batchResp.Data = resp.Data + } + for _, task := range tasks { + handleBatchResp(task) + } + return batchResps +} + +// CoprocessorStream implements the tikvpb.TikvServer interface. +func (svr *Server) CoprocessorStream(*coprocessor.Request, tikvpb.Tikv_CoprocessorStreamServer) error { + // TODO + return nil +} + +// GetLockWaitHistory implements the tikvpb.TikvServer interface. +func (svr *Server) GetLockWaitHistory(context.Context, *kvrpcpb.GetLockWaitHistoryRequest) (*kvrpcpb.GetLockWaitHistoryResponse, error) { + return &kvrpcpb.GetLockWaitHistoryResponse{}, nil +} + +// RegionError represents a region error +type RegionError struct { + err *errorpb.Error +} + +// Error implements Error method. +func (regionError *RegionError) Error() string { + return regionError.err.Message +} + +// BatchCoprocessor implements the tikvpb.TikvServer interface. +func (svr *Server) BatchCoprocessor(req *coprocessor.BatchRequest, batchCopServer tikvpb.Tikv_BatchCoprocessorServer) error { + reqCtxs := make([]*requestCtx, 0, len(req.Regions)) + defer func() { + for _, ctx := range reqCtxs { + ctx.finish() + } + }() + if req.TableRegions != nil { + // Support PartitionTableScan for BatchCop + req.Regions = req.Regions[:] + for _, tr := range req.TableRegions { + req.Regions = append(req.Regions, tr.Regions...) + } + } + for _, ri := range req.Regions { + cop := coprocessor.Request{ + Tp: kv.ReqTypeDAG, + Data: req.Data, + StartTs: req.StartTs, + Ranges: ri.Ranges, + } + regionCtx := *req.Context + regionCtx.RegionEpoch = ri.RegionEpoch + regionCtx.RegionId = ri.RegionId + cop.Context = ®ionCtx + + reqCtx, err := newRequestCtx(svr, ®ionCtx, "Coprocessor") + if err != nil { + return err + } + reqCtxs = append(reqCtxs, reqCtx) + if reqCtx.regErr != nil { + return &RegionError{err: reqCtx.regErr} + } + copResponse := cophandler.HandleCopRequestWithMPPCtx(reqCtx.getDBReader(), svr.mvccStore.lockStore, &cop, nil) + err = batchCopServer.Send(&coprocessor.BatchResponse{Data: copResponse.Data}) + if err != nil { + return err + } + } + return nil +} + +// RawCoprocessor implements the tikvpb.TikvServer interface. +func (svr *Server) RawCoprocessor(context.Context, *kvrpcpb.RawCoprocessorRequest) (*kvrpcpb.RawCoprocessorResponse, error) { + panic("unimplemented") +} + +func (mrm *MockRegionManager) removeMPPTaskHandler(taskID int64, storeID uint64) error { + set := mrm.getMPPTaskSet(storeID) + if set == nil { + return errors.New("cannot find mpp task set for store") + } + set.mu.Lock() + defer set.mu.Unlock() + if _, ok := set.taskHandlers[taskID]; ok { + delete(set.taskHandlers, taskID) + return nil + } + return errors.New("cannot find mpp task") +} + +// IsAlive implements the tikvpb.TikvServer interface. +func (svr *Server) IsAlive(_ context.Context, _ *mpp.IsAliveRequest) (*mpp.IsAliveResponse, error) { + return &mpp.IsAliveResponse{Available: true}, nil +} + +// DispatchMPPTask implements the tikvpb.TikvServer interface. +func (svr *Server) DispatchMPPTask(_ context.Context, _ *mpp.DispatchTaskRequest) (*mpp.DispatchTaskResponse, error) { + panic("todo") +} + +func (svr *Server) executeMPPDispatch(ctx context.Context, req *mpp.DispatchTaskRequest, storeAddr string, storeID uint64, handler *cophandler.MPPTaskHandler) error { + var reqCtx *requestCtx + if len(req.TableRegions) > 0 { + // Simple unistore logic for PartitionTableScan. + for _, tr := range req.TableRegions { + req.Regions = append(req.Regions, tr.Regions...) + } + } + + if len(req.Regions) > 0 { + kvContext := &kvrpcpb.Context{ + RegionId: req.Regions[0].RegionId, + RegionEpoch: req.Regions[0].RegionEpoch, + // this is a hack to reuse task id in kvContext to pass mpp task id + TaskId: uint64(handler.Meta.TaskId), + Peer: &metapb.Peer{StoreId: storeID}, + } + var err error + reqCtx, err = newRequestCtx(svr, kvContext, "Mpp") + if err != nil { + return errors.Trace(err) + } + } + copReq := &coprocessor.Request{ + Tp: kv.ReqTypeDAG, + Data: req.EncodedPlan, + StartTs: req.Meta.StartTs, + } + for _, regionMeta := range req.Regions { + copReq.Ranges = append(copReq.Ranges, regionMeta.Ranges...) + } + var dbreader *dbreader.DBReader + if reqCtx != nil { + dbreader = reqCtx.getDBReader() + } + go func() { + resp := cophandler.HandleCopRequestWithMPPCtx(dbreader, svr.mvccStore.lockStore, copReq, &cophandler.MPPCtx{ + RPCClient: svr.RPCClient, + StoreAddr: storeAddr, + TaskHandler: handler, + Ctx: ctx, + }) + handler.Err = svr.RemoveMPPTaskHandler(req.Meta.TaskId, storeID) + if len(resp.OtherError) > 0 { + handler.Err = errors.New(resp.OtherError) + } + if reqCtx != nil { + reqCtx.finish() + } + }() + return nil +} + +// DispatchMPPTaskWithStoreID implements the tikvpb.TikvServer interface. +func (svr *Server) DispatchMPPTaskWithStoreID(ctx context.Context, req *mpp.DispatchTaskRequest, storeID uint64) (*mpp.DispatchTaskResponse, error) { + mppHandler, err := svr.CreateMPPTaskHandler(req.Meta, storeID) + if err != nil { + return nil, errors.Trace(err) + } + storeAddr, err := svr.GetStoreAddrByStoreID(storeID) + if err != nil { + return nil, err + } + err = svr.executeMPPDispatch(ctx, req, storeAddr, storeID, mppHandler) + resp := &mpp.DispatchTaskResponse{} + if err != nil { + resp.Error = &mpp.Error{Msg: err.Error()} + } + return resp, nil +} + +// CancelMPPTask implements the tikvpb.TikvServer interface. +func (svr *Server) CancelMPPTask(_ context.Context, _ *mpp.CancelTaskRequest) (*mpp.CancelTaskResponse, error) { + panic("todo") +} + +// GetMPPTaskHandler implements the tikvpb.TikvServer interface. +func (svr *Server) GetMPPTaskHandler(taskID int64, storeID uint64) (*cophandler.MPPTaskHandler, error) { + if mrm, ok := svr.regionManager.(*MockRegionManager); ok { + set := mrm.getMPPTaskSet(storeID) + if set == nil { + return nil, errors.New("cannot find mpp task set for store") + } + set.mu.Lock() + defer set.mu.Unlock() + if handler, ok := set.taskHandlers[taskID]; ok { + return handler, nil + } + return nil, nil + } + return nil, errors.New("Only mock region mgr supports get mpp task") +} + +// RemoveMPPTaskHandler implements the tikvpb.TikvServer interface. +func (svr *Server) RemoveMPPTaskHandler(taskID int64, storeID uint64) error { + if mrm, ok := svr.regionManager.(*MockRegionManager); ok { + err := mrm.removeMPPTaskHandler(taskID, storeID) + return errors.Trace(err) + } + return errors.New("Only mock region mgr supports remove mpp task") +} + +// CreateMPPTaskHandler implements the tikvpb.TikvServer interface. +func (svr *Server) CreateMPPTaskHandler(meta *mpp.TaskMeta, storeID uint64) (*cophandler.MPPTaskHandler, error) { + if mrm, ok := svr.regionManager.(*MockRegionManager); ok { + set := mrm.getMPPTaskSet(storeID) + if set == nil { + return nil, errors.New("cannot find mpp task set for store") + } + set.mu.Lock() + defer set.mu.Unlock() + if handler, ok := set.taskHandlers[meta.TaskId]; ok { + return handler, errors.Errorf("Task %d has been created", meta.TaskId) + } + handler := &cophandler.MPPTaskHandler{ + TunnelSet: make(map[int64]*cophandler.ExchangerTunnel), + Meta: meta, + RPCClient: svr.RPCClient, + } + set.taskHandlers[meta.TaskId] = handler + return handler, nil + } + return nil, errors.New("Only mock region mgr supports get mpp task") +} + +// EstablishMPPConnection implements the tikvpb.TikvServer interface. +func (svr *Server) EstablishMPPConnection(*mpp.EstablishMPPConnectionRequest, tikvpb.Tikv_EstablishMPPConnectionServer) error { + panic("todo") +} + +// EstablishMPPConnectionWithStoreID implements the tikvpb.TikvServer interface. +func (svr *Server) EstablishMPPConnectionWithStoreID(req *mpp.EstablishMPPConnectionRequest, server tikvpb.Tikv_EstablishMPPConnectionServer, storeID uint64) error { + var ( + mppHandler *cophandler.MPPTaskHandler + err error + ) + maxRetryTime := 5 + for i := 0; i < maxRetryTime; i++ { + mppHandler, err = svr.GetMPPTaskHandler(req.SenderMeta.TaskId, storeID) + if err != nil { + return errors.Trace(err) + } + if mppHandler != nil { + break + } + time.Sleep(time.Second) + } + if mppHandler == nil { + return errors.New("task not found") + } + ctx1, cancel := context.WithCancel(context.Background()) + defer cancel() + tunnel, err := mppHandler.HandleEstablishConn(ctx1, req) + if err != nil { + return errors.Trace(err) + } + var sendError error + for sendError == nil { + chunk, err := tunnel.RecvChunk() + if err != nil { + sendError = server.Send(&mpp.MPPDataPacket{Error: &mpp.Error{Msg: err.Error()}}) + break + } + if chunk == nil { + // todo return io.EOF error? + break + } + res := tipb.SelectResponse{ + Chunks: []tipb.Chunk{*chunk}, + } + raw, err := res.Marshal() + if err != nil { + sendError = server.Send(&mpp.MPPDataPacket{Error: &mpp.Error{Msg: err.Error()}}) + break + } + sendError = server.Send(&mpp.MPPDataPacket{Data: raw}) + } + return sendError +} + +// Raft commands (tikv <-> tikv). + +// Raft implements the tikvpb.TikvServer interface. +func (svr *Server) Raft(stream tikvpb.Tikv_RaftServer) error { + return svr.innerServer.Raft(stream) +} + +// Snapshot implements the tikvpb.TikvServer interface. +func (svr *Server) Snapshot(stream tikvpb.Tikv_SnapshotServer) error { + return svr.innerServer.Snapshot(stream) +} + +// BatchRaft implements the tikvpb.TikvServer interface. +func (svr *Server) BatchRaft(stream tikvpb.Tikv_BatchRaftServer) error { + return svr.innerServer.BatchRaft(stream) +} + +// Region commands. + +// SplitRegion implements the tikvpb.TikvServer interface. +func (svr *Server) SplitRegion(ctx context.Context, req *kvrpcpb.SplitRegionRequest) (*kvrpcpb.SplitRegionResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "SplitRegion") + if err != nil { + return &kvrpcpb.SplitRegionResponse{RegionError: &errorpb.Error{Message: err.Error()}}, nil + } + defer reqCtx.finish() + return svr.regionManager.SplitRegion(req), nil +} + +// Compact implements the tikvpb.TikvServer interface. +func (svr *Server) Compact(ctx context.Context, req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) { + panic("unimplemented") +} + +// ReadIndex implements the tikvpb.TikvServer interface. +func (svr *Server) ReadIndex(context.Context, *kvrpcpb.ReadIndexRequest) (*kvrpcpb.ReadIndexResponse, error) { + // TODO: + return &kvrpcpb.ReadIndexResponse{}, nil +} + +// transaction debugger commands. + +// MvccGetByKey implements the tikvpb.TikvServer interface. +func (svr *Server) MvccGetByKey(ctx context.Context, req *kvrpcpb.MvccGetByKeyRequest) (*kvrpcpb.MvccGetByKeyResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "MvccGetByKey") + if err != nil { + return &kvrpcpb.MvccGetByKeyResponse{Error: err.Error()}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.MvccGetByKeyResponse{RegionError: reqCtx.regErr}, nil + } + resp := new(kvrpcpb.MvccGetByKeyResponse) + mvccInfo, err := svr.mvccStore.MvccGetByKey(reqCtx, req.GetKey()) + if err != nil { + resp.Error = err.Error() + } + resp.Info = mvccInfo + return resp, nil +} + +// MvccGetByStartTs implements the tikvpb.TikvServer interface. +func (svr *Server) MvccGetByStartTs(ctx context.Context, req *kvrpcpb.MvccGetByStartTsRequest) (*kvrpcpb.MvccGetByStartTsResponse, error) { + reqCtx, err := newRequestCtx(svr, req.Context, "MvccGetByStartTs") + if err != nil { + return &kvrpcpb.MvccGetByStartTsResponse{Error: err.Error()}, nil + } + defer reqCtx.finish() + if reqCtx.regErr != nil { + return &kvrpcpb.MvccGetByStartTsResponse{RegionError: reqCtx.regErr}, nil + } + resp := new(kvrpcpb.MvccGetByStartTsResponse) + mvccInfo, key, err := svr.mvccStore.MvccGetByStartTs(reqCtx, req.StartTs) + if err != nil { + resp.Error = err.Error() + } + resp.Info = mvccInfo + resp.Key = key + return resp, nil +} + +// UnsafeDestroyRange implements the tikvpb.TikvServer interface. +func (svr *Server) UnsafeDestroyRange(ctx context.Context, req *kvrpcpb.UnsafeDestroyRangeRequest) (*kvrpcpb.UnsafeDestroyRangeResponse, error) { + start, end := req.GetStartKey(), req.GetEndKey() + svr.mvccStore.DeleteFileInRange(start, end) + return &kvrpcpb.UnsafeDestroyRangeResponse{}, nil +} + +// GetWaitForEntries tries to get the waitFor entries +// deadlock detection related services +func (svr *Server) GetWaitForEntries(ctx context.Context, + req *deadlockPb.WaitForEntriesRequest) (*deadlockPb.WaitForEntriesResponse, error) { + // TODO + return &deadlockPb.WaitForEntriesResponse{}, nil +} + +// Detect will handle detection rpc from other nodes +func (svr *Server) Detect(stream deadlockPb.Deadlock_DetectServer) error { + for { + req, err := stream.Recv() + if err != nil { + if err == io.EOF { + break + } + return err + } + if !svr.mvccStore.DeadlockDetectSvr.IsLeader() { + log.Warn("detection requests received on non leader node") + break + } + resp := svr.mvccStore.DeadlockDetectSvr.Detect(req) + if resp != nil { + if sendErr := stream.Send(resp); sendErr != nil { + log.Error("send deadlock response failed", zap.Error(sendErr)) + break + } + } + } + return nil +} + +// CheckLockObserver implements the tikvpb.TikvServer interface. +func (svr *Server) CheckLockObserver(context.Context, *kvrpcpb.CheckLockObserverRequest) (*kvrpcpb.CheckLockObserverResponse, error) { + // TODO: implement Observer + return &kvrpcpb.CheckLockObserverResponse{IsClean: true}, nil +} + +// PhysicalScanLock implements the tikvpb.TikvServer interface. +func (svr *Server) PhysicalScanLock(ctx context.Context, req *kvrpcpb.PhysicalScanLockRequest) (*kvrpcpb.PhysicalScanLockResponse, error) { + resp := &kvrpcpb.PhysicalScanLockResponse{} + resp.Locks = svr.mvccStore.PhysicalScanLock(req.StartKey, req.MaxTs, int(req.Limit)) + return resp, nil +} + +// RegisterLockObserver implements the tikvpb.TikvServer interface. +func (svr *Server) RegisterLockObserver(context.Context, *kvrpcpb.RegisterLockObserverRequest) (*kvrpcpb.RegisterLockObserverResponse, error) { + // TODO: implement Observer + return &kvrpcpb.RegisterLockObserverResponse{}, nil +} + +// RemoveLockObserver implements the tikvpb.TikvServer interface. +func (svr *Server) RemoveLockObserver(context.Context, *kvrpcpb.RemoveLockObserverRequest) (*kvrpcpb.RemoveLockObserverResponse, error) { + // TODO: implement Observer + return &kvrpcpb.RemoveLockObserverResponse{}, nil +} + +// CheckLeader implements the tikvpb.TikvServer interface. +func (svr *Server) CheckLeader(context.Context, *kvrpcpb.CheckLeaderRequest) (*kvrpcpb.CheckLeaderResponse, error) { + panic("unimplemented") +} + +// RawCompareAndSwap implements the tikvpb.TikvServer interface. +func (svr *Server) RawCompareAndSwap(context.Context, *kvrpcpb.RawCASRequest) (*kvrpcpb.RawCASResponse, error) { + panic("implement me") +} + +// GetStoreSafeTS implements the tikvpb.TikvServer interface. +func (svr *Server) GetStoreSafeTS(context.Context, *kvrpcpb.StoreSafeTSRequest) (*kvrpcpb.StoreSafeTSResponse, error) { + return &kvrpcpb.StoreSafeTSResponse{}, nil +} + +// GetLockWaitInfo implements the tikvpb.TikvServer interface. +func (svr *Server) GetLockWaitInfo(context.Context, *kvrpcpb.GetLockWaitInfoRequest) (*kvrpcpb.GetLockWaitInfoResponse, error) { + panic("unimplemented") +} + +// RawChecksum implements the tikvpb.TikvServer interface. +func (svr *Server) RawChecksum(context.Context, *kvrpcpb.RawChecksumRequest) (*kvrpcpb.RawChecksumResponse, error) { + panic("unimplemented") +} + +func convertToKeyError(err error) *kvrpcpb.KeyError { + if err == nil { + return nil + } + causeErr := errors.Cause(err) + switch x := causeErr.(type) { + case *kverrors.ErrLocked: + return &kvrpcpb.KeyError{ + Locked: x.Lock.ToLockInfo(x.Key), + } + case kverrors.ErrRetryable: + return &kvrpcpb.KeyError{ + Retryable: x.Error(), + } + case *kverrors.ErrKeyAlreadyExists: + return &kvrpcpb.KeyError{ + AlreadyExist: &kvrpcpb.AlreadyExist{ + Key: x.Key, + }, + } + case *kverrors.ErrConflict: + return &kvrpcpb.KeyError{ + Conflict: &kvrpcpb.WriteConflict{ + StartTs: x.StartTS, + ConflictTs: x.ConflictTS, + ConflictCommitTs: x.ConflictCommitTS, + Key: x.Key, + Reason: x.Reason, + }, + } + case *kverrors.ErrDeadlock: + return &kvrpcpb.KeyError{ + Deadlock: &kvrpcpb.Deadlock{ + LockKey: x.LockKey, + LockTs: x.LockTS, + DeadlockKeyHash: x.DeadlockKeyHash, + WaitChain: x.WaitChain, + }, + } + case *kverrors.ErrCommitExpire: + return &kvrpcpb.KeyError{ + CommitTsExpired: &kvrpcpb.CommitTsExpired{ + StartTs: x.StartTs, + AttemptedCommitTs: x.CommitTs, + Key: x.Key, + MinCommitTs: x.MinCommitTs, + }, + } + case *kverrors.ErrTxnNotFound: + return &kvrpcpb.KeyError{ + TxnNotFound: &kvrpcpb.TxnNotFound{ + StartTs: x.StartTS, + PrimaryKey: x.PrimaryKey, + }, + } + case *kverrors.ErrAssertionFailed: + return &kvrpcpb.KeyError{ + AssertionFailed: &kvrpcpb.AssertionFailed{ + StartTs: x.StartTS, + Key: x.Key, + Assertion: x.Assertion, + ExistingStartTs: x.ExistingStartTS, + ExistingCommitTs: x.ExistingCommitTS, + }, + } + case *kverrors.ErrPrimaryMismatch: + return &kvrpcpb.KeyError{ + PrimaryMismatch: &kvrpcpb.PrimaryMismatch{ + LockInfo: x.Lock.ToLockInfo(x.Key), + }, + } + default: + return &kvrpcpb.KeyError{ + Abort: err.Error(), + } + } +} + +func convertToPBError(err error) (*kvrpcpb.KeyError, *errorpb.Error) { + if regErr := extractRegionError(err); regErr != nil { + return nil, regErr + } + return convertToKeyError(err), nil +} + +func convertToPBErrors(err error) ([]*kvrpcpb.KeyError, *errorpb.Error) { + if err != nil { + if regErr := extractRegionError(err); regErr != nil { + return nil, regErr + } + return []*kvrpcpb.KeyError{convertToKeyError(err)}, nil + } + return nil, nil +} + +func extractRegionError(err error) *errorpb.Error { + if pbError, ok := err.(*pberror.PBError); ok { + return pbError.RequestErr + } + return nil +} diff --git a/store/mockstore/unistore/tikv/server_batch.go b/pkg/store/mockstore/unistore/tikv/server_batch.go similarity index 100% rename from store/mockstore/unistore/tikv/server_batch.go rename to pkg/store/mockstore/unistore/tikv/server_batch.go diff --git a/store/mockstore/unistore/tikv/util.go b/pkg/store/mockstore/unistore/tikv/util.go similarity index 100% rename from store/mockstore/unistore/tikv/util.go rename to pkg/store/mockstore/unistore/tikv/util.go diff --git a/pkg/store/mockstore/unistore/tikv/write.go b/pkg/store/mockstore/unistore/tikv/write.go new file mode 100644 index 0000000000000..0d40dd4bb1c80 --- /dev/null +++ b/pkg/store/mockstore/unistore/tikv/write.go @@ -0,0 +1,354 @@ +// Copyright 2019-present PingCAP, Inc. +// +// 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. + +package tikv + +import ( + "bytes" + "math" + "sync" + "sync/atomic" + + "github.com/pingcap/badger" + "github.com/pingcap/badger/y" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/lockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/dbreader" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/tikv/mvcc" + "github.com/pingcap/tidb/pkg/util/mathutil" + "go.uber.org/zap" +) + +const ( + batchChanSize = 1024 +) + +type writeDBBatch struct { + entries []*badger.Entry + err error + wg sync.WaitGroup +} + +func newWriteDBBatch() *writeDBBatch { + return &writeDBBatch{} +} + +func (batch *writeDBBatch) set(key y.Key, val []byte, userMeta []byte) { + batch.entries = append(batch.entries, &badger.Entry{ + Key: key, + Value: val, + UserMeta: userMeta, + }) +} + +// delete is a badger level operation, only used in DeleteRange, so we don't need to set UserMeta. +// Then we can tell the entry is delete if UserMeta is nil. +func (batch *writeDBBatch) delete(key y.Key) { + batch.entries = append(batch.entries, &badger.Entry{ + Key: key, + }) +} + +type writeLockBatch struct { + entries []*badger.Entry + err error + wg sync.WaitGroup +} + +func (batch *writeLockBatch) set(key, val []byte) { + batch.entries = append(batch.entries, &badger.Entry{ + Key: y.KeyWithTs(key, 0), + Value: val, + UserMeta: mvcc.LockUserMetaNone, + }) +} + +func (batch *writeLockBatch) delete(key []byte) { + batch.entries = append(batch.entries, &badger.Entry{ + Key: y.KeyWithTs(key, 0), + UserMeta: mvcc.LockUserMetaDelete, + }) +} + +type writeDBWorker struct { + batchCh chan *writeDBBatch + writer *dbWriter +} + +func (w writeDBWorker) run() { + defer w.writer.wg.Done() + var batches []*writeDBBatch + for { + for i := range batches { + batches[i] = nil + } + batches = batches[:0] + select { + case <-w.writer.closeCh: + return + case batch := <-w.batchCh: + batches = append(batches, batch) + } + chLen := len(w.batchCh) + for i := 0; i < chLen; i++ { + batches = append(batches, <-w.batchCh) + } + if len(batches) > 0 { + w.updateBatchGroup(batches) + } + } +} + +func (w writeDBWorker) updateBatchGroup(batchGroup []*writeDBBatch) { + e := w.writer.bundle.DB.Update(func(txn *badger.Txn) error { + for _, batch := range batchGroup { + for _, entry := range batch.entries { + var err error + if len(entry.UserMeta) == 0 { + entry.SetDelete() + } + err = txn.SetEntry(entry) + if err != nil { + return err + } + } + } + return nil + }) + for _, batch := range batchGroup { + batch.err = e + batch.wg.Done() + } +} + +type writeLockWorker struct { + batchCh chan *writeLockBatch + writer *dbWriter +} + +func (w writeLockWorker) run() { + defer w.writer.wg.Done() + ls := w.writer.bundle.LockStore + var batches []*writeLockBatch + for { + for i := range batches { + batches[i] = nil + } + batches = batches[:0] + select { + case <-w.writer.closeCh: + return + case batch := <-w.batchCh: + batches = append(batches, batch) + } + chLen := len(w.batchCh) + for i := 0; i < chLen; i++ { + batches = append(batches, <-w.batchCh) + } + hint := new(lockstore.Hint) + var delCnt, insertCnt int + for _, batch := range batches { + for _, entry := range batch.entries { + switch entry.UserMeta[0] { + case mvcc.LockUserMetaDeleteByte: + delCnt++ + // Ignore if the key doesn't exist + ls.DeleteWithHint(entry.Key.UserKey, hint) + default: + insertCnt++ + ls.PutWithHint(entry.Key.UserKey, entry.Value, hint) + } + } + batch.wg.Done() + } + } +} + +type dbWriter struct { + bundle *mvcc.DBBundle + dbCh chan<- *writeDBBatch + lockCh chan<- *writeLockBatch + wg sync.WaitGroup + closeCh chan struct{} + latestTS uint64 +} + +// NewDBWriter returns a new DBWriter. +func NewDBWriter(bundle *mvcc.DBBundle) mvcc.DBWriter { + return &dbWriter{ + bundle: bundle, + closeCh: make(chan struct{}), + } +} + +func (writer *dbWriter) Open() { + writer.wg.Add(2) + + dbCh := make(chan *writeDBBatch, batchChanSize) + writer.dbCh = dbCh + go writeDBWorker{ + batchCh: dbCh, + writer: writer, + }.run() + + lockCh := make(chan *writeLockBatch, batchChanSize) + writer.lockCh = lockCh + go writeLockWorker{ + batchCh: lockCh, + writer: writer, + }.run() +} + +func (writer *dbWriter) Close() { + close(writer.closeCh) + writer.wg.Wait() +} + +func (writer *dbWriter) Write(batch mvcc.WriteBatch) error { + wb := batch.(*writeBatch) + if len(wb.dbBatch.entries) > 0 { + wb.dbBatch.wg.Add(1) + writer.dbCh <- &wb.dbBatch + wb.dbBatch.wg.Wait() + err := wb.dbBatch.err + if err != nil { + return err + } + } + if len(wb.lockBatch.entries) > 0 { + // We must delete lock after commit succeed, or there will be inconsistency. + wb.lockBatch.wg.Add(1) + writer.lockCh <- &wb.lockBatch + wb.lockBatch.wg.Wait() + return wb.lockBatch.err + } + return nil +} + +type writeBatch struct { + startTS uint64 + commitTS uint64 + dbBatch writeDBBatch + lockBatch writeLockBatch +} + +func (wb *writeBatch) Prewrite(key []byte, lock *mvcc.Lock) { + wb.lockBatch.set(key, lock.MarshalBinary()) +} + +func (wb *writeBatch) Commit(key []byte, lock *mvcc.Lock) { + userMeta := mvcc.NewDBUserMeta(wb.startTS, wb.commitTS) + k := y.KeyWithTs(key, wb.commitTS) + if lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { + log.Info("removing a pessimistic lock when committing", zap.Binary("key", key), zap.Uint64("startTS", wb.startTS), zap.Uint64("commitTS", wb.commitTS)) + // Write nothing as if PessimisticRollback is called. + } else if lock.Op != uint8(kvrpcpb.Op_Lock) { + wb.dbBatch.set(k, lock.Value, userMeta) + } else if bytes.Equal(key, lock.Primary) { + opLockKey := y.KeyWithTs(mvcc.EncodeExtraTxnStatusKey(key, wb.startTS), wb.startTS) + wb.dbBatch.set(opLockKey, nil, userMeta) + } + wb.lockBatch.delete(key) +} + +func (wb *writeBatch) Rollback(key []byte, deleteLock bool) { + rollbackKey := y.KeyWithTs(mvcc.EncodeExtraTxnStatusKey(key, wb.startTS), wb.startTS) + userMeta := mvcc.NewDBUserMeta(wb.startTS, 0) + wb.dbBatch.set(rollbackKey, nil, userMeta) + if deleteLock { + wb.lockBatch.delete(key) + } +} + +func (wb *writeBatch) PessimisticLock(key []byte, lock *mvcc.Lock) { + wb.lockBatch.set(key, lock.MarshalBinary()) +} + +func (wb *writeBatch) PessimisticRollback(key []byte) { + wb.lockBatch.delete(key) +} + +func (writer *dbWriter) NewWriteBatch(startTS, commitTS uint64, ctx *kvrpcpb.Context) mvcc.WriteBatch { + if commitTS > 0 { + writer.updateLatestTS(commitTS) + } else { + writer.updateLatestTS(startTS) + } + return &writeBatch{ + startTS: startTS, + commitTS: commitTS, + } +} + +func (writer *dbWriter) getLatestTS() uint64 { + return atomic.LoadUint64(&writer.latestTS) +} + +func (writer *dbWriter) updateLatestTS(ts uint64) { + latestTS := writer.getLatestTS() + if ts != math.MaxUint64 && ts > latestTS { + atomic.CompareAndSwapUint64(&writer.latestTS, latestTS, ts) + } +} + +const delRangeBatchSize = 4096 + +func (writer *dbWriter) DeleteRange(startKey, endKey []byte, latchHandle mvcc.LatchHandle) error { + keys := make([]y.Key, 0, delRangeBatchSize) + txn := writer.bundle.DB.NewTransaction(false) + defer txn.Discard() + reader := dbreader.NewDBReader(startKey, endKey, txn) + keys = writer.collectRangeKeys(reader.GetIter(), startKey, endKey, keys) + reader.Close() + return writer.deleteKeysInBatch(latchHandle, keys, delRangeBatchSize) +} + +func (writer *dbWriter) collectRangeKeys(it *badger.Iterator, startKey, endKey []byte, keys []y.Key) []y.Key { + if len(endKey) == 0 { + panic("invalid end key") + } + for it.Seek(startKey); it.Valid(); it.Next() { + item := it.Item() + key := item.KeyCopy(nil) + if exceedEndKey(key, endKey) { + break + } + keys = append(keys, y.KeyWithTs(key, item.Version())) + } + return keys +} + +func (writer *dbWriter) deleteKeysInBatch(latchHandle mvcc.LatchHandle, keys []y.Key, batchSize int) error { + for len(keys) > 0 { + batchSize := mathutil.Min(len(keys), batchSize) + batchKeys := keys[:batchSize] + keys = keys[batchSize:] + hashVals := userKeysToHashVals(batchKeys...) + dbBatch := newWriteDBBatch() + for _, key := range batchKeys { + key.Version++ + dbBatch.delete(key) + } + latchHandle.AcquireLatches(hashVals) + dbBatch.wg.Add(1) + writer.dbCh <- dbBatch + dbBatch.wg.Wait() + latchHandle.ReleaseLatches(hashVals) + if dbBatch.err != nil { + return dbBatch.err + } + } + return nil +} diff --git a/pkg/store/mockstore/unistore/util/lockwaiter/BUILD.bazel b/pkg/store/mockstore/unistore/util/lockwaiter/BUILD.bazel new file mode 100644 index 0000000000000..0c392363aa398 --- /dev/null +++ b/pkg/store/mockstore/unistore/util/lockwaiter/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "lockwaiter", + srcs = ["lockwaiter.go"], + importpath = "github.com/pingcap/tidb/pkg/store/mockstore/unistore/util/lockwaiter", + visibility = ["//visibility:public"], + deps = [ + "//pkg/store/mockstore/unistore/config", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "lockwaiter_test", + timeout = "short", + srcs = [ + "lockwaiter_test.go", + "main_test.go", + ], + embed = [":lockwaiter"], + flaky = True, + deps = [ + "//pkg/store/mockstore/unistore/config", + "//pkg/testkit/testsetup", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/store/mockstore/unistore/util/lockwaiter/lockwaiter.go b/pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter.go similarity index 99% rename from store/mockstore/unistore/util/lockwaiter/lockwaiter.go rename to pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter.go index 88cddace6cca9..44f340626892d 100644 --- a/store/mockstore/unistore/util/lockwaiter/lockwaiter.go +++ b/pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter.go @@ -22,7 +22,7 @@ import ( "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/config" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" "go.uber.org/zap" ) diff --git a/store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go b/pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go similarity index 98% rename from store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go rename to pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go index 96fb02d8cf6c1..57aeb47bbff53 100644 --- a/store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go +++ b/pkg/store/mockstore/unistore/util/lockwaiter/lockwaiter_test.go @@ -21,7 +21,7 @@ import ( deadlockPb "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/config" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/store/mockstore/unistore/util/lockwaiter/main_test.go b/pkg/store/mockstore/unistore/util/lockwaiter/main_test.go new file mode 100644 index 0000000000000..9a8c5400c95c3 --- /dev/null +++ b/pkg/store/mockstore/unistore/util/lockwaiter/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package lockwaiter + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/store/pdtypes/BUILD.bazel b/pkg/store/pdtypes/BUILD.bazel new file mode 100644 index 0000000000000..ad3652bd98b47 --- /dev/null +++ b/pkg/store/pdtypes/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pdtypes", + srcs = [ + "api.go", + "config.go", + "placement.go", + "region_tree.go", + "statistics.go", + "typeutil.go", + ], + importpath = "github.com/pingcap/tidb/pkg/store/pdtypes", + visibility = ["//visibility:public"], + deps = [ + "@com_github_docker_go_units//:go-units", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_kvproto//pkg/pdpb", + ], +) diff --git a/store/pdtypes/api.go b/pkg/store/pdtypes/api.go similarity index 100% rename from store/pdtypes/api.go rename to pkg/store/pdtypes/api.go diff --git a/store/pdtypes/config.go b/pkg/store/pdtypes/config.go similarity index 100% rename from store/pdtypes/config.go rename to pkg/store/pdtypes/config.go diff --git a/store/pdtypes/placement.go b/pkg/store/pdtypes/placement.go similarity index 100% rename from store/pdtypes/placement.go rename to pkg/store/pdtypes/placement.go diff --git a/store/pdtypes/region_tree.go b/pkg/store/pdtypes/region_tree.go similarity index 100% rename from store/pdtypes/region_tree.go rename to pkg/store/pdtypes/region_tree.go diff --git a/store/pdtypes/statistics.go b/pkg/store/pdtypes/statistics.go similarity index 100% rename from store/pdtypes/statistics.go rename to pkg/store/pdtypes/statistics.go diff --git a/store/pdtypes/typeutil.go b/pkg/store/pdtypes/typeutil.go similarity index 100% rename from store/pdtypes/typeutil.go rename to pkg/store/pdtypes/typeutil.go diff --git a/pkg/store/store.go b/pkg/store/store.go new file mode 100644 index 0000000000000..20cd6520b398f --- /dev/null +++ b/pkg/store/store.go @@ -0,0 +1,123 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package store + +import ( + "net/url" + "strings" + "sync" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/pdpb" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +var stores = make(map[string]kv.Driver) +var storesLock sync.RWMutex + +// Register registers a kv storage with unique name and its associated Driver. +func Register(name string, driver kv.Driver) error { + storesLock.Lock() + defer storesLock.Unlock() + + name = strings.ToLower(name) + + if _, ok := stores[name]; ok { + return errors.Errorf("%s is already registered", name) + } + + stores[name] = driver + return nil +} + +// New creates a kv Storage with path. +// +// The path must be a URL format 'engine://path?params' like the one for +// session.Open() but with the dbname cut off. +// Examples: +// +// goleveldb://relative/path +// boltdb:///absolute/path +// +// The engine should be registered before creating storage. +func New(path string) (kv.Storage, error) { + return newStoreWithRetry(path, util.DefaultMaxRetries) +} + +func newStoreWithRetry(path string, maxRetries int) (kv.Storage, error) { + storeURL, err := url.Parse(path) + if err != nil { + return nil, err + } + + name := strings.ToLower(storeURL.Scheme) + d, ok := loadDriver(name) + if !ok { + return nil, errors.Errorf("invalid uri format, storage %s is not registered", name) + } + + var s kv.Storage + err = util.RunWithRetry(maxRetries, util.RetryInterval, func() (bool, error) { + logutil.BgLogger().Info("new store", zap.String("path", path)) + s, err = d.Open(path) + return isNewStoreRetryableError(err), err + }) + + if err == nil { + logutil.BgLogger().Info("new store with retry success") + } else { + logutil.BgLogger().Warn("new store with retry failed", zap.Error(err)) + } + return s, errors.Trace(err) +} + +func loadDriver(name string) (kv.Driver, bool) { + storesLock.RLock() + defer storesLock.RUnlock() + d, ok := stores[name] + return d, ok +} + +// isOpenRetryableError check if the new store operation should be retried under given error +// currently, it should be retried if: +// +// Transaction conflict and is retryable (kv.IsTxnRetryableError) +// PD is not bootstrapped at the time of request +// Keyspace requested does not exist (request prior to PD keyspace pre-split) +func isNewStoreRetryableError(err error) bool { + if err == nil { + return false + } + return kv.IsTxnRetryableError(err) || IsNotBootstrappedError(err) || IsKeyspaceNotExistError(err) +} + +// IsNotBootstrappedError returns true if the error is pd not bootstrapped error. +func IsNotBootstrappedError(err error) bool { + if err == nil { + return false + } + return strings.Contains(err.Error(), pdpb.ErrorType_NOT_BOOTSTRAPPED.String()) +} + +// IsKeyspaceNotExistError returns true the error is caused by keyspace not exists. +func IsKeyspaceNotExistError(err error) bool { + if err == nil { + return false + } + return strings.Contains(err.Error(), pdpb.ErrorType_ENTRY_NOT_FOUND.String()) +} diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go new file mode 100644 index 0000000000000..a05ea58be6fac --- /dev/null +++ b/pkg/store/store_test.go @@ -0,0 +1,875 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package store + +import ( + "context" + "fmt" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/stretchr/testify/require" + kv2 "github.com/tikv/client-go/v2/kv" +) + +const ( + startIndex = 0 + testCount = 2 + indexStep = 2 +) + +type brokenStore struct{} + +func (s *brokenStore) Open(_ string) (kv.Storage, error) { + return nil, kv.ErrTxnRetryable +} + +func insertData(t *testing.T, txn kv.Transaction) { + for i := startIndex; i < testCount; i++ { + val := encodeInt(i * indexStep) + err := txn.Set(val, val) + require.NoError(t, err) + } +} + +func mustDel(t *testing.T, txn kv.Transaction) { + for i := startIndex; i < testCount; i++ { + val := encodeInt(i * indexStep) + err := txn.Delete(val) + require.NoError(t, err) + } +} + +func encodeInt(n int) []byte { + return []byte(fmt.Sprintf("%010d", n)) +} + +func decodeInt(s []byte) int { + var n int + _, _ = fmt.Sscanf(string(s), "%010d", &n) + return n +} + +func valToStr(iter kv.Iterator) string { + val := iter.Value() + return string(val) +} + +func checkSeek(t *testing.T, txn kv.Transaction) { + for i := startIndex; i < testCount; i++ { + val := encodeInt(i * indexStep) + iter, err := txn.Iter(val, nil) + require.NoError(t, err) + require.Equal(t, val, []byte(iter.Key())) + require.Equal(t, i*indexStep, decodeInt([]byte(valToStr(iter)))) + iter.Close() + } + + // Test iterator Next() + for i := startIndex; i < testCount-1; i++ { + val := encodeInt(i * indexStep) + iter, err := txn.Iter(val, nil) + require.NoError(t, err) + require.Equal(t, val, []byte(iter.Key())) + require.Equal(t, string(val), valToStr(iter)) + + err = iter.Next() + require.NoError(t, err) + require.True(t, iter.Valid()) + + val = encodeInt((i + 1) * indexStep) + require.Equal(t, val, []byte(iter.Key())) + require.Equal(t, string(val), valToStr(iter)) + iter.Close() + } + + // Non exist and beyond maximum seek test + iter, err := txn.Iter(encodeInt(testCount*indexStep), nil) + require.NoError(t, err) + require.False(t, iter.Valid()) + + // Non exist but between existing keys seek test, + // it returns the smallest key that larger than the one we are seeking + inBetween := encodeInt((testCount-1)*indexStep - 1) + last := encodeInt((testCount - 1) * indexStep) + iter, err = txn.Iter(inBetween, nil) + require.NoError(t, err) + require.True(t, iter.Valid()) + require.NotEqual(t, inBetween, []byte(iter.Key())) + require.Equal(t, last, []byte(iter.Key())) + iter.Close() +} + +func mustNotGet(t *testing.T, txn kv.Transaction) { + for i := startIndex; i < testCount; i++ { + s := encodeInt(i * indexStep) + _, err := txn.Get(context.TODO(), s) + require.Error(t, err) + } +} + +func mustGet(t *testing.T, txn kv.Transaction) { + for i := startIndex; i < testCount; i++ { + s := encodeInt(i * indexStep) + val, err := txn.Get(context.TODO(), s) + require.NoError(t, err) + require.Equal(t, string(s), string(val)) + } +} + +func TestNew(t *testing.T) { + store, err := New("goleveldb://relative/path") + require.Error(t, err) + require.Nil(t, store) +} + +func TestGetSet(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + insertData(t, txn) + + mustGet(t, txn) + + // Check transaction results + err = txn.Commit(context.Background()) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + + mustGet(t, txn) + mustDel(t, txn) +} + +func TestSeek(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + insertData(t, txn) + checkSeek(t, txn) + + // Check transaction results + err = txn.Commit(context.Background()) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + + checkSeek(t, txn) + mustDel(t, txn) +} + +func TestInc(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + key := []byte("incKey") + n, err := kv.IncInt64(txn, key, 100) + require.NoError(t, err) + require.Equal(t, int64(100), n) + + // Check transaction results + err = txn.Commit(context.Background()) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + + n, err = kv.IncInt64(txn, key, -200) + require.NoError(t, err) + require.Equal(t, int64(-100), n) + + err = txn.Delete(key) + require.NoError(t, err) + + n, err = kv.IncInt64(txn, key, 100) + require.NoError(t, err) + require.Equal(t, int64(100), n) + + err = txn.Delete(key) + require.NoError(t, err) + + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestDelete(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + insertData(t, txn) + + mustDel(t, txn) + + mustNotGet(t, txn) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + // Try get + txn, err = store.Begin() + require.NoError(t, err) + + mustNotGet(t, txn) + + // Insert again + insertData(t, txn) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + // Delete all + txn, err = store.Begin() + require.NoError(t, err) + + mustDel(t, txn) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + + mustNotGet(t, txn) + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestDelete2(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + val := []byte("test") + require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000001_0003"), val)) + require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000001_0004"), val)) + require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000002_0003"), val)) + require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000002_0004"), val)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + // Delete all + txn, err = store.Begin() + require.NoError(t, err) + + it, err := txn.Iter([]byte("DATA_test_tbl_department_record__0000000001_0003"), nil) + require.NoError(t, err) + for it.Valid() { + err = txn.Delete(it.Key()) + require.NoError(t, err) + err = it.Next() + require.NoError(t, err) + } + err = txn.Commit(context.Background()) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + it, _ = txn.Iter([]byte("DATA_test_tbl_department_record__000000000"), nil) + require.False(t, it.Valid()) + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestSetNil(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + require.NoError(t, err) + err = txn.Set([]byte("1"), nil) + require.Error(t, err) +} + +func TestBasicSeek(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + require.NoError(t, txn.Set([]byte("1"), []byte("1"))) + err = txn.Commit(context.Background()) + require.NoError(t, err) + txn, err = store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + + it, err := txn.Iter([]byte("2"), nil) + require.NoError(t, err) + require.False(t, it.Valid()) + require.NoError(t, txn.Delete([]byte("1"))) +} + +func TestBasicTable(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + for i := 1; i < 5; i++ { + b := []byte(strconv.Itoa(i)) + require.NoError(t, txn.Set(b, b)) + } + err = txn.Commit(context.Background()) + require.NoError(t, err) + txn, err = store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + + err = txn.Set([]byte("1"), []byte("1")) + require.NoError(t, err) + + it, err := txn.Iter([]byte("0"), nil) + require.NoError(t, err) + require.Equal(t, "1", string(it.Key())) + + err = txn.Set([]byte("0"), []byte("0")) + require.NoError(t, err) + it, err = txn.Iter([]byte("0"), nil) + require.NoError(t, err) + require.Equal(t, "0", string(it.Key())) + err = txn.Delete([]byte("0")) + require.NoError(t, err) + + require.NoError(t, txn.Delete([]byte("1"))) + it, err = txn.Iter([]byte("0"), nil) + require.NoError(t, err) + require.Equal(t, "2", string(it.Key())) + + err = txn.Delete([]byte("3")) + require.NoError(t, err) + it, err = txn.Iter([]byte("2"), nil) + require.NoError(t, err) + require.Equal(t, "2", string(it.Key())) + + it, err = txn.Iter([]byte("3"), nil) + require.NoError(t, err) + require.Equal(t, "4", string(it.Key())) + err = txn.Delete([]byte("2")) + require.NoError(t, err) + err = txn.Delete([]byte("4")) + require.NoError(t, err) +} + +func TestRollback(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + err = txn.Rollback() + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + + insertData(t, txn) + + mustGet(t, txn) + + err = txn.Rollback() + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + + for i := startIndex; i < testCount; i++ { + _, err := txn.Get(context.TODO(), []byte(strconv.Itoa(i))) + require.Error(t, err) + } +} + +func TestSeekMin(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + rows := []struct { + key string + value string + }{ + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001", "lock-version"}, + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0002", "1"}, + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0003", "hello"}, + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002", "lock-version"}, + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0002", "2"}, + {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0003", "hello"}, + } + + txn, err := store.Begin() + require.NoError(t, err) + for _, row := range rows { + require.NoError(t, txn.Set([]byte(row.key), []byte(row.value))) + } + + it, err := txn.Iter(nil, nil) + require.NoError(t, err) + for it.Valid() { + require.NoError(t, it.Next()) + } + + it, err = txn.Iter([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"), nil) + require.NoError(t, err) + require.Equal(t, "DATA_test_main_db_tbl_tbl_test_record__00000000000000000001", string(it.Key())) + + for _, row := range rows { + require.NoError(t, txn.Delete([]byte(row.key))) + } +} + +func TestConditionIfNotExist(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + var success int64 + cnt := 100 + b := []byte("1") + var wg sync.WaitGroup + wg.Add(cnt) + for i := 0; i < cnt; i++ { + go func() { + defer wg.Done() + txn, err := store.Begin() + require.NoError(t, err) + err = txn.Set(b, b) + if err != nil { + return + } + err = txn.Commit(context.Background()) + if err == nil { + atomic.AddInt64(&success, 1) + } + }() + } + wg.Wait() + // At least one txn can success. + require.Greater(t, success, int64(0)) + + // Clean up + txn, err := store.Begin() + require.NoError(t, err) + err = txn.Delete(b) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestConditionIfEqual(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + var success int64 + cnt := 100 + b := []byte("1") + var wg sync.WaitGroup + wg.Add(cnt) + + txn, err := store.Begin() + require.NoError(t, err) + require.NoError(t, txn.Set(b, b)) + err = txn.Commit(context.Background()) + require.NoError(t, err) + + for i := 0; i < cnt; i++ { + go func() { + defer wg.Done() + // Use txn1/err1 instead of txn/err is + // to pass `go tool vet -shadow` check. + txn1, err1 := store.Begin() + require.NoError(t, err1) + require.NoError(t, txn1.Set(b, []byte("newValue"))) + err1 = txn1.Commit(context.Background()) + if err1 == nil { + atomic.AddInt64(&success, 1) + } + }() + } + wg.Wait() + require.Greater(t, success, int64(0)) + + // Clean up + txn, err = store.Begin() + require.NoError(t, err) + err = txn.Delete(b) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestConditionUpdate(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + require.NoError(t, txn.Delete([]byte("b"))) + _, err = kv.IncInt64(txn, []byte("a"), 1) + require.NoError(t, err) + err = txn.Commit(context.Background()) + require.NoError(t, err) +} + +func TestDBClose(t *testing.T) { + t.Skip("don't know why it fails.") + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + + txn, err := store.Begin() + require.NoError(t, err) + + err = txn.Set([]byte("a"), []byte("b")) + require.NoError(t, err) + + err = txn.Commit(context.Background()) + require.NoError(t, err) + + ver, err := store.CurrentVersion(kv.GlobalTxnScope) + require.NoError(t, err) + require.Equal(t, 1, kv.MaxVersion.Cmp(ver)) + + snap := store.GetSnapshot(kv.MaxVersion) + + _, err = snap.Get(context.TODO(), []byte("a")) + require.NoError(t, err) + + txn, err = store.Begin() + require.NoError(t, err) + + err = store.Close() + require.NoError(t, err) + + _, err = store.Begin() + require.Error(t, err) + + _ = store.GetSnapshot(kv.MaxVersion) + + err = txn.Set([]byte("a"), []byte("b")) + require.NoError(t, err) + + err = txn.Commit(context.Background()) + require.Error(t, err) +} + +func TestIsolationInc(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + threadCnt := 4 + + ids := make(map[int64]struct{}, threadCnt*100) + var m sync.Mutex + var wg sync.WaitGroup + + wg.Add(threadCnt) + for i := 0; i < threadCnt; i++ { + go func() { + defer wg.Done() + for j := 0; j < 100; j++ { + var id int64 + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + var err1 error + id, err1 = kv.IncInt64(txn, []byte("key"), 1) + return err1 + }) + require.NoError(t, err) + + m.Lock() + _, ok := ids[id] + ids[id] = struct{}{} + m.Unlock() + require.False(t, ok) + } + }() + } + + wg.Wait() + + // delete + txn, err := store.Begin() + require.NoError(t, err) + defer func() { + require.NoError(t, txn.Commit(context.Background())) + }() + require.NoError(t, txn.Delete([]byte("key"))) +} + +func TestIsolationMultiInc(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + threadCnt := 4 + incCnt := 100 + keyCnt := 4 + + keys := make([][]byte, 0, keyCnt) + for i := 0; i < keyCnt; i++ { + keys = append(keys, []byte(fmt.Sprintf("test_key_%d", i))) + } + + var wg sync.WaitGroup + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + wg.Add(threadCnt) + for i := 0; i < threadCnt; i++ { + go func() { + defer wg.Done() + for j := 0; j < incCnt; j++ { + err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + for _, key := range keys { + _, err1 := kv.IncInt64(txn, key, 1) + if err1 != nil { + return err1 + } + } + + return nil + }) + require.NoError(t, err) + } + }() + } + + wg.Wait() + + err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { + for _, key := range keys { + id, err1 := kv.GetInt64(context.TODO(), txn, key) + if err1 != nil { + return err1 + } + require.Equal(t, int64(threadCnt*incCnt), id) + require.NoError(t, txn.Delete(key)) + } + return nil + }) + require.NoError(t, err) +} + +func TestRetryOpenStore(t *testing.T) { + begin := time.Now() + require.NoError(t, Register("dummy", &brokenStore{})) + store, err := newStoreWithRetry("dummy://dummy-store", 3) + if store != nil { + defer func() { + require.NoError(t, store.Close()) + }() + } + require.Error(t, err) + elapse := time.Since(begin) + require.GreaterOrEqual(t, uint64(elapse), uint64(3*time.Second)) +} + +func TestOpenStore(t *testing.T) { + require.NoError(t, Register("open", &brokenStore{})) + store, err := newStoreWithRetry(":", 3) + if store != nil { + defer func() { + require.NoError(t, store.Close()) + }() + } + require.Error(t, err) +} + +func TestRegister(t *testing.T) { + err := Register("retry", &brokenStore{}) + require.NoError(t, err) + err = Register("retry", &brokenStore{}) + require.Error(t, err) +} + +func TestSetAssertion(t *testing.T) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + defer func() { + require.NoError(t, store.Close()) + }() + + txn, err := store.Begin() + require.NoError(t, err) + + mustHaveAssertion := func(key []byte, assertion kv.FlagsOp) { + f, err1 := txn.GetMemBuffer().GetFlags(key) + require.NoError(t, err1) + if assertion == kv.SetAssertExist { + require.True(t, f.HasAssertExists()) + require.False(t, f.HasAssertUnknown()) + } else if assertion == kv.SetAssertNotExist { + require.True(t, f.HasAssertNotExists()) + require.False(t, f.HasAssertUnknown()) + } else if assertion == kv.SetAssertUnknown { + require.True(t, f.HasAssertUnknown()) + } else if assertion == kv.SetAssertNone { + require.False(t, f.HasAssertionFlags()) + } else { + require.FailNow(t, "unreachable") + } + } + + testUnchangeable := func(key []byte, expectAssertion kv.FlagsOp) { + err = txn.SetAssertion(key, kv.SetAssertExist) + require.NoError(t, err) + mustHaveAssertion(key, expectAssertion) + err = txn.SetAssertion(key, kv.SetAssertNotExist) + require.NoError(t, err) + mustHaveAssertion(key, expectAssertion) + err = txn.SetAssertion(key, kv.SetAssertUnknown) + require.NoError(t, err) + mustHaveAssertion(key, expectAssertion) + err = txn.SetAssertion(key, kv.SetAssertNone) + require.NoError(t, err) + mustHaveAssertion(key, expectAssertion) + } + + k1 := []byte("k1") + err = txn.SetAssertion(k1, kv.SetAssertExist) + require.NoError(t, err) + mustHaveAssertion(k1, kv.SetAssertExist) + testUnchangeable(k1, kv.SetAssertExist) + + k2 := []byte("k2") + err = txn.SetAssertion(k2, kv.SetAssertNotExist) + require.NoError(t, err) + mustHaveAssertion(k2, kv.SetAssertNotExist) + testUnchangeable(k2, kv.SetAssertNotExist) + + k3 := []byte("k3") + err = txn.SetAssertion(k3, kv.SetAssertUnknown) + require.NoError(t, err) + mustHaveAssertion(k3, kv.SetAssertUnknown) + testUnchangeable(k3, kv.SetAssertUnknown) + + k4 := []byte("k4") + err = txn.SetAssertion(k4, kv.SetAssertNone) + require.NoError(t, err) + mustHaveAssertion(k4, kv.SetAssertNone) + err = txn.SetAssertion(k4, kv.SetAssertExist) + require.NoError(t, err) + mustHaveAssertion(k4, kv.SetAssertExist) + testUnchangeable(k4, kv.SetAssertExist) + + k5 := []byte("k5") + err = txn.Set(k5, []byte("v5")) + require.NoError(t, err) + mustHaveAssertion(k5, kv.SetAssertNone) + err = txn.SetAssertion(k5, kv.SetAssertNotExist) + require.NoError(t, err) + mustHaveAssertion(k5, kv.SetAssertNotExist) + testUnchangeable(k5, kv.SetAssertNotExist) + + k6 := []byte("k6") + err = txn.SetAssertion(k6, kv.SetAssertNotExist) + require.NoError(t, err) + err = txn.GetMemBuffer().SetWithFlags(k6, []byte("v6"), kv.SetPresumeKeyNotExists) + require.NoError(t, err) + mustHaveAssertion(k6, kv.SetAssertNotExist) + testUnchangeable(k6, kv.SetAssertNotExist) + flags, err := txn.GetMemBuffer().GetFlags(k6) + require.NoError(t, err) + require.True(t, flags.HasPresumeKeyNotExists()) + err = txn.GetMemBuffer().DeleteWithFlags(k6, kv.SetNeedLocked) + mustHaveAssertion(k6, kv.SetAssertNotExist) + testUnchangeable(k6, kv.SetAssertNotExist) + flags, err = txn.GetMemBuffer().GetFlags(k6) + require.NoError(t, err) + require.True(t, flags.HasPresumeKeyNotExists()) + require.True(t, flags.HasNeedLocked()) + + k7 := []byte("k7") + lockCtx := kv2.NewLockCtx(txn.StartTS(), 2000, time.Now()) + err = txn.LockKeys(context.Background(), lockCtx, k7) + require.NoError(t, err) + mustHaveAssertion(k7, kv.SetAssertNone) + + require.NoError(t, txn.Rollback()) +} diff --git a/pkg/structure/BUILD.bazel b/pkg/structure/BUILD.bazel new file mode 100644 index 0000000000000..695a349986abe --- /dev/null +++ b/pkg/structure/BUILD.bazel @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "structure", + srcs = [ + "hash.go", + "list.go", + "string.go", + "structure.go", + "type.go", + ], + importpath = "github.com/pingcap/tidb/pkg/structure", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/kv", + "//pkg/util/codec", + "//pkg/util/dbterror", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "structure_test", + timeout = "short", + srcs = [ + "main_test.go", + "structure_test.go", + ], + embed = [":structure"], + flaky = True, + shard_count = 4, + deps = [ + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/structure/hash.go b/pkg/structure/hash.go similarity index 99% rename from structure/hash.go rename to pkg/structure/hash.go index 90e54c604fac7..e9e70fa9f0a94 100644 --- a/structure/hash.go +++ b/pkg/structure/hash.go @@ -20,7 +20,7 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // HashPair is the pair for (field, value) in a hash. diff --git a/pkg/structure/list.go b/pkg/structure/list.go new file mode 100644 index 0000000000000..5674229665a80 --- /dev/null +++ b/pkg/structure/list.go @@ -0,0 +1,246 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package structure + +import ( + "context" + "encoding/binary" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" +) + +type listMeta struct { + LIndex int64 + RIndex int64 +} + +func (meta listMeta) Value() []byte { + buf := make([]byte, 16) + binary.BigEndian.PutUint64(buf[0:8], uint64(meta.LIndex)) + binary.BigEndian.PutUint64(buf[8:16], uint64(meta.RIndex)) + return buf +} + +func (meta listMeta) IsEmpty() bool { + return meta.LIndex >= meta.RIndex +} + +// LPush prepends one or multiple values to a list. +func (t *TxStructure) LPush(key []byte, values ...[]byte) error { + return t.listPush(key, true, values...) +} + +// RPush appends one or multiple values to a list. +func (t *TxStructure) RPush(key []byte, values ...[]byte) error { + return t.listPush(key, false, values...) +} + +func (t *TxStructure) listPush(key []byte, left bool, values ...[]byte) error { + if t.readWriter == nil { + return ErrWriteOnSnapshot + } + if len(values) == 0 { + return nil + } + + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil { + return errors.Trace(err) + } + + var index int64 + for _, v := range values { + if left { + meta.LIndex-- + index = meta.LIndex + } else { + index = meta.RIndex + meta.RIndex++ + } + + dataKey := t.encodeListDataKey(key, index) + if err = t.readWriter.Set(dataKey, v); err != nil { + return errors.Trace(err) + } + } + + return t.readWriter.Set(metaKey, meta.Value()) +} + +// LPop removes and gets the first element in a list. +func (t *TxStructure) LPop(key []byte) ([]byte, error) { + return t.listPop(key, true) +} + +// RPop removes and gets the last element in a list. +func (t *TxStructure) RPop(key []byte) ([]byte, error) { + return t.listPop(key, false) +} + +func (t *TxStructure) listPop(key []byte, left bool) ([]byte, error) { + if t.readWriter == nil { + return nil, ErrWriteOnSnapshot + } + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil || meta.IsEmpty() { + return nil, errors.Trace(err) + } + + var index int64 + if left { + index = meta.LIndex + meta.LIndex++ + } else { + meta.RIndex-- + index = meta.RIndex + } + + dataKey := t.encodeListDataKey(key, index) + + var data []byte + data, err = t.reader.Get(context.TODO(), dataKey) + if err != nil { + return nil, errors.Trace(err) + } + + if err = t.readWriter.Delete(dataKey); err != nil { + return nil, errors.Trace(err) + } + + if !meta.IsEmpty() { + err = t.readWriter.Set(metaKey, meta.Value()) + } else { + err = t.readWriter.Delete(metaKey) + } + + return data, errors.Trace(err) +} + +// LLen gets the length of a list. +func (t *TxStructure) LLen(key []byte) (int64, error) { + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + return meta.RIndex - meta.LIndex, errors.Trace(err) +} + +// LGetAll gets all elements of this list in order from right to left. +func (t *TxStructure) LGetAll(key []byte) ([][]byte, error) { + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil || meta.IsEmpty() { + return nil, errors.Trace(err) + } + + length := int(meta.RIndex - meta.LIndex) + elements := make([][]byte, 0, length) + for index := meta.RIndex - 1; index >= meta.LIndex; index-- { + e, err := t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) + if err != nil { + return nil, errors.Trace(err) + } + elements = append(elements, e) + } + return elements, nil +} + +// LIndex gets an element from a list by its index. +func (t *TxStructure) LIndex(key []byte, index int64) ([]byte, error) { + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil || meta.IsEmpty() { + return nil, errors.Trace(err) + } + + index = adjustIndex(index, meta.LIndex, meta.RIndex) + + if index >= meta.LIndex && index < meta.RIndex { + return t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) + } + return nil, nil +} + +// LSet updates an element in the list by its index. +func (t *TxStructure) LSet(key []byte, index int64, value []byte) error { + if t.readWriter == nil { + return ErrWriteOnSnapshot + } + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil || meta.IsEmpty() { + return errors.Trace(err) + } + + index = adjustIndex(index, meta.LIndex, meta.RIndex) + + if index >= meta.LIndex && index < meta.RIndex { + return t.readWriter.Set(t.encodeListDataKey(key, index), value) + } + return ErrInvalidListIndex.GenWithStack("invalid list index %d", index) +} + +// LClear removes the list of the key. +func (t *TxStructure) LClear(key []byte) error { + if t.readWriter == nil { + return ErrWriteOnSnapshot + } + metaKey := t.encodeListMetaKey(key) + meta, err := t.loadListMeta(metaKey) + if err != nil || meta.IsEmpty() { + return errors.Trace(err) + } + + for index := meta.LIndex; index < meta.RIndex; index++ { + dataKey := t.encodeListDataKey(key, index) + if err = t.readWriter.Delete(dataKey); err != nil { + return errors.Trace(err) + } + } + + return t.readWriter.Delete(metaKey) +} + +func (t *TxStructure) loadListMeta(metaKey []byte) (listMeta, error) { + v, err := t.reader.Get(context.TODO(), metaKey) + if kv.ErrNotExist.Equal(err) { + err = nil + } + if err != nil { + return listMeta{}, errors.Trace(err) + } + + meta := listMeta{0, 0} + if v == nil { + return meta, nil + } + + if len(v) != 16 { + return meta, ErrInvalidListMetaData + } + + meta.LIndex = int64(binary.BigEndian.Uint64(v[0:8])) + meta.RIndex = int64(binary.BigEndian.Uint64(v[8:16])) + return meta, nil +} + +func adjustIndex(index int64, min, max int64) int64 { + if index >= 0 { + return index + min + } + + return index + max +} diff --git a/pkg/structure/main_test.go b/pkg/structure/main_test.go new file mode 100644 index 0000000000000..a8e0eeaa9c7fb --- /dev/null +++ b/pkg/structure/main_test.go @@ -0,0 +1,35 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package structure + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlDeleteWorker).loop"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/ttl/ttlworker.(*ttlScanWorker).loop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/structure/string.go b/pkg/structure/string.go similarity index 98% rename from structure/string.go rename to pkg/structure/string.go index 00d0195b59f05..0fc37c5b0b7f4 100644 --- a/structure/string.go +++ b/pkg/structure/string.go @@ -19,7 +19,7 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // Set sets the string value of the key. diff --git a/structure/structure.go b/pkg/structure/structure.go similarity index 93% rename from structure/structure.go rename to pkg/structure/structure.go index 7fcf3557bccee..7d916a90720c1 100644 --- a/structure/structure.go +++ b/pkg/structure/structure.go @@ -15,9 +15,9 @@ package structure import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/dbterror" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/dbterror" ) var ( diff --git a/structure/structure_test.go b/pkg/structure/structure_test.go similarity index 97% rename from structure/structure_test.go rename to pkg/structure/structure_test.go index 11fac8f6d5e5f..1a95bb5dedbc7 100644 --- a/structure/structure_test.go +++ b/pkg/structure/structure_test.go @@ -18,11 +18,11 @@ import ( "context" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/structure" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/structure" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/structure/type.go b/pkg/structure/type.go new file mode 100644 index 0000000000000..88b8223600653 --- /dev/null +++ b/pkg/structure/type.go @@ -0,0 +1,127 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package structure + +import ( + "bytes" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/codec" +) + +// TypeFlag is for data structure meta/data flag. +type TypeFlag byte + +const ( + // StringMeta is the flag for string meta. + StringMeta TypeFlag = 'S' + // StringData is the flag for string data. + StringData TypeFlag = 's' + // HashMeta is the flag for hash meta. + HashMeta TypeFlag = 'H' + // HashData is the flag for hash data. + HashData TypeFlag = 'h' + // ListMeta is the flag for list meta. + ListMeta TypeFlag = 'L' + // ListData is the flag for list data. + ListData TypeFlag = 'l' +) + +// Make linter happy, since encodeHashMetaKey is unused in this repo. +var _ = (&TxStructure{}).encodeHashMetaKey + +// EncodeStringDataKey will encode string key. +func (t *TxStructure) EncodeStringDataKey(key []byte) kv.Key { + // for codec Encode, we may add extra bytes data, so here and following encode + // we will use extra length like 4 for a little optimization. + ek := make([]byte, 0, len(t.prefix)+len(key)+24) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + return codec.EncodeUint(ek, uint64(StringData)) +} + +// nolint:unused +func (t *TxStructure) encodeHashMetaKey(key []byte) kv.Key { + ek := make([]byte, 0, len(t.prefix)+codec.EncodedBytesLength(len(key))+8) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + return codec.EncodeUint(ek, uint64(HashMeta)) +} + +func (t *TxStructure) encodeHashDataKey(key []byte, field []byte) kv.Key { + ek := make([]byte, 0, len(t.prefix)+codec.EncodedBytesLength(len(key))+8+codec.EncodedBytesLength(len(field))) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + ek = codec.EncodeUint(ek, uint64(HashData)) + return codec.EncodeBytes(ek, field) +} + +// EncodeHashDataKey exports for tests. +func (t *TxStructure) EncodeHashDataKey(key []byte, field []byte) kv.Key { + return t.encodeHashDataKey(key, field) +} + +func (t *TxStructure) decodeHashDataKey(ek kv.Key) ([]byte, []byte, error) { + var ( + key []byte + field []byte + err error + tp uint64 + ) + + if !bytes.HasPrefix(ek, t.prefix) { + return nil, nil, errors.New("invalid encoded hash data key prefix") + } + + ek = ek[len(t.prefix):] + + ek, key, err = codec.DecodeBytes(ek, nil) + if err != nil { + return nil, nil, errors.Trace(err) + } + + ek, tp, err = codec.DecodeUint(ek) + if err != nil { + return nil, nil, errors.Trace(err) + } else if TypeFlag(tp) != HashData { + return nil, nil, ErrInvalidHashKeyFlag.GenWithStack("invalid encoded hash data key flag %c", byte(tp)) + } + + _, field, err = codec.DecodeBytes(ek, nil) + return key, field, errors.Trace(err) +} + +func (t *TxStructure) hashDataKeyPrefix(key []byte) kv.Key { + ek := make([]byte, 0, len(t.prefix)+len(key)+24) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + return codec.EncodeUint(ek, uint64(HashData)) +} + +func (t *TxStructure) encodeListMetaKey(key []byte) kv.Key { + ek := make([]byte, 0, len(t.prefix)+len(key)+24) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + return codec.EncodeUint(ek, uint64(ListMeta)) +} + +func (t *TxStructure) encodeListDataKey(key []byte, index int64) kv.Key { + ek := make([]byte, 0, len(t.prefix)+len(key)+36) + ek = append(ek, t.prefix...) + ek = codec.EncodeBytes(ek, key) + ek = codec.EncodeUint(ek, uint64(ListData)) + return codec.EncodeInt(ek, index) +} diff --git a/pkg/table/BUILD.bazel b/pkg/table/BUILD.bazel new file mode 100644 index 0000000000000..8f5df5622c166 --- /dev/null +++ b/pkg/table/BUILD.bazel @@ -0,0 +1,69 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "table", + srcs = [ + "column.go", + "constraint.go", + "index.go", + "table.go", + ], + importpath = "github.com/pingcap/tidb/pkg/table", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/sqlexec", + "//pkg/util/timeutil", + "//pkg/util/tracing", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "table_test", + timeout = "short", + srcs = [ + "column_test.go", + "main_test.go", + "table_test.go", + ], + embed = [":table"], + flaky = True, + race = "on", + shard_count = 9, + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/mock", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/table/column.go b/pkg/table/column.go new file mode 100644 index 0000000000000..15be521014ca8 --- /dev/null +++ b/pkg/table/column.go @@ -0,0 +1,742 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2016 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package table + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + field_types "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" + "go.uber.org/zap" +) + +// Column provides meta data describing a table column. +type Column struct { + *model.ColumnInfo + // If this column is a generated column, the expression will be stored here. + GeneratedExpr ast.ExprNode + // If this column has default expr value, this expression will be stored here. + DefaultExpr ast.ExprNode +} + +// String implements fmt.Stringer interface. +func (c *Column) String() string { + ans := []string{c.Name.O, types.TypeToStr(c.GetType(), c.GetCharset())} + if mysql.HasAutoIncrementFlag(c.GetFlag()) { + ans = append(ans, "AUTO_INCREMENT") + } + if mysql.HasNotNullFlag(c.GetFlag()) { + ans = append(ans, "NOT NULL") + } + return strings.Join(ans, " ") +} + +// ToInfo casts Column to model.ColumnInfo +// NOTE: DONT modify return value. +func (c *Column) ToInfo() *model.ColumnInfo { + return c.ColumnInfo +} + +// FindCol finds column in cols by name. +func FindCol(cols []*Column, name string) *Column { + for _, col := range cols { + if strings.EqualFold(col.Name.O, name) { + return col + } + } + return nil +} + +// FindColLowerCase finds column in cols by name. It assumes the name is lowercase. +func FindColLowerCase(cols []*Column, name string) *Column { + for _, col := range cols { + if col.Name.L == name { + return col + } + } + return nil +} + +// ToColumn converts a *model.ColumnInfo to *Column. +func ToColumn(col *model.ColumnInfo) *Column { + return &Column{ + col, + nil, + nil, + } +} + +// FindCols finds columns in cols by names. +// If pkIsHandle is false and name is ExtraHandleName, the extra handle column will be added. +// If any columns don't match, return nil and the first missing column's name. +// Please consider FindColumns() first for a better performance. +func FindCols(cols []*Column, names []string, pkIsHandle bool) ([]*Column, string) { + var rcols []*Column + for _, name := range names { + col := FindCol(cols, name) + if col != nil { + rcols = append(rcols, col) + } else if name == model.ExtraHandleName.L && !pkIsHandle { + col := &Column{} + col.ColumnInfo = model.NewExtraHandleColInfo() + col.ColumnInfo.Offset = len(cols) + rcols = append(rcols, col) + } else { + return nil, name + } + } + + return rcols, "" +} + +// FindColumns finds columns in cols by names with a better performance than FindCols(). +// It assumes names are lowercase. +func FindColumns(cols []*Column, names []string, pkIsHandle bool) (foundCols []*Column, missingOffset int) { + var rcols []*Column + for i, name := range names { + col := FindColLowerCase(cols, name) + if col != nil { + rcols = append(rcols, col) + } else if name == model.ExtraHandleName.L && !pkIsHandle { + col := &Column{} + col.ColumnInfo = model.NewExtraHandleColInfo() + col.ColumnInfo.Offset = len(cols) + rcols = append(rcols, col) + } else { + return nil, i + } + } + return rcols, -1 +} + +// FindOnUpdateCols finds columns which have OnUpdateNow flag. +func FindOnUpdateCols(cols []*Column) []*Column { + var rcols []*Column + for _, col := range cols { + if mysql.HasOnUpdateNowFlag(col.GetFlag()) { + rcols = append(rcols, col) + } + } + + return rcols +} + +// truncateTrailingSpaces truncates trailing spaces for CHAR[(M)] column. +// fix: https://github.com/pingcap/tidb/issues/3660 +func truncateTrailingSpaces(v *types.Datum) { + if v.Kind() == types.KindNull { + return + } + b := v.GetBytes() + length := len(b) + for length > 0 && b[length-1] == ' ' { + length-- + } + b = b[:length] + str := string(hack.String(b)) + v.SetString(str, v.Collation()) +} + +// convertToIncorrectStringErr converts ErrInvalidCharacterString to ErrTruncatedWrongValueForField. +// The first argument is the invalid character in bytes. +func convertToIncorrectStringErr(err error, colName string) error { + inErr, ok := errors.Cause(err).(*errors.Error) + if !ok { + return err + } + args := inErr.Args() + if len(args) != 2 { + return err + } + invalidStrHex, ok := args[1].(string) + if !ok { + return err + } + var res strings.Builder + for i := 0; i < len(invalidStrHex); i++ { + if i%2 == 0 { + res.WriteString("\\x") + } + res.WriteByte(invalidStrHex[i]) + } + return ErrTruncatedWrongValueForField.FastGen("Incorrect string value '%s' for column '%s'", res.String(), colName) +} + +// handleZeroDatetime handles Timestamp/Datetime/Date zero date and invalid dates. +// Currently only called from CastValue. +// returns: +// +// value (possibly adjusted) +// boolean; true if break error/warning handling in CastValue and return what was returned from this +// error +func handleZeroDatetime(ctx sessionctx.Context, col *model.ColumnInfo, casted types.Datum, str string, tmIsInvalid bool) (types.Datum, bool, error) { + sc := ctx.GetSessionVars().StmtCtx + tm := casted.GetMysqlTime() + mode := ctx.GetSessionVars().SQLMode + + var ( + zeroV types.Time + zeroT string + ) + switch col.GetType() { + case mysql.TypeDate: + zeroV, zeroT = types.ZeroDate, types.DateStr + case mysql.TypeDatetime: + zeroV, zeroT = types.ZeroDatetime, types.DateTimeStr + case mysql.TypeTimestamp: + zeroV, zeroT = types.ZeroTimestamp, types.TimestampStr + } + + // ref https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_date + // if NO_ZERO_DATE is not enabled, '0000-00-00' is permitted and inserts produce no warning + // if NO_ZERO_DATE is enabled, '0000-00-00' is permitted and inserts produce a warning + // If NO_ZERO_DATE mode and strict mode are enabled, '0000-00-00' is not permitted and inserts produce an error, unless IGNORE is given as well. For INSERT IGNORE and UPDATE IGNORE, '0000-00-00' is permitted and inserts produce a warning. + // if NO_ZERO_IN_DATE is not enabled, dates with zero parts are permitted and inserts produce no warning + // if NO_ZERO_IN_DATE is enabled, dates with zero parts are inserted as '0000-00-00' and produce a warning + // If NO_ZERO_IN_DATE mode and strict mode are enabled, dates with zero parts are not permitted and inserts produce an error, unless IGNORE is given as well. For INSERT IGNORE and UPDATE IGNORE, dates with zero parts are inserted as '0000-00-00' and produce a warning. + + ignoreErr := sc.DupKeyAsWarning + + // Timestamp in MySQL is since EPOCH 1970-01-01 00:00:00 UTC and can by definition not have invalid dates! + // Zero date is special for MySQL timestamp and *NOT* 1970-01-01 00:00:00, but 0000-00-00 00:00:00! + // in MySQL 8.0, the Timestamp's case is different to Datetime/Date, as shown below: + // + // | | NZD | NZD|ST | ELSE | ELSE|ST | + // | ------------ | ----------------- | ------- | ----------------- | -------- | + // | `0000-00-01` | Truncate + Warning| Error | Truncate + Warning| Error | + // | `0000-00-00` | Success + Warning | Error | Success | Success | + // + // * **NZD**: NO_ZERO_DATE_MODE + // * **ST**: STRICT_TRANS_TABLES + // * **ELSE**: empty or NO_ZERO_IN_DATE_MODE + if tm.IsZero() && col.GetType() == mysql.TypeTimestamp { + innerErr := types.ErrWrongValue.GenWithStackByArgs(zeroT, str) + if mode.HasStrictMode() && !ignoreErr && (tmIsInvalid || mode.HasNoZeroDateMode()) { + return types.NewDatum(zeroV), true, innerErr + } + + if tmIsInvalid || mode.HasNoZeroDateMode() { + sc.AppendWarning(innerErr) + } + return types.NewDatum(zeroV), true, nil + } else if tmIsInvalid && col.GetType() == mysql.TypeTimestamp { + // Prevent from being stored! Invalid timestamp! + if mode.HasStrictMode() { + return types.NewDatum(zeroV), true, types.ErrWrongValue.GenWithStackByArgs(zeroT, str) + } + // no strict mode, truncate to 0000-00-00 00:00:00 + sc.AppendWarning(types.ErrWrongValue.GenWithStackByArgs(zeroT, str)) + return types.NewDatum(zeroV), true, nil + } else if tm.IsZero() || tm.InvalidZero() { + if tm.IsZero() { + // Don't care NoZeroDate mode if time val is invalid. + if !tmIsInvalid && !mode.HasNoZeroDateMode() { + return types.NewDatum(zeroV), true, nil + } + } else if tm.InvalidZero() { + if !mode.HasNoZeroInDateMode() { + return casted, true, nil + } + } + + innerErr := types.ErrWrongValue.GenWithStackByArgs(zeroT, str) + if mode.HasStrictMode() && !ignoreErr { + return types.NewDatum(zeroV), true, innerErr + } + + // TODO: as in MySQL 8.0's implement, warning message is `types.ErrWarnDataOutOfRange`, + // but this error message need a `rowIdx` argument, in this context, the `rowIdx` is missing. + // And refactor this function seems too complicated, so we set the warning message the same to error's. + sc.AppendWarning(innerErr) + return types.NewDatum(zeroV), true, nil + } + + return casted, false, nil +} + +// CastValue casts a value based on column type. +// If forceIgnoreTruncate is true, truncated errors will be ignored. +// If returnErr is true, directly return any conversion errors. +// It's safe now and it's the same as the behavior of select statement. +// Set it to true only in FillVirtualColumnValue and UnionScanExec.Next() +// If the handle of err is changed latter, the behavior of forceIgnoreTruncate also need to change. +// TODO: change the third arg to TypeField. Not pass ColumnInfo. +func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo, returnErr, forceIgnoreTruncate bool) (casted types.Datum, err error) { + sc := ctx.GetSessionVars().StmtCtx + casted, err = val.ConvertTo(sc, &col.FieldType) + // TODO: make sure all truncate errors are handled by ConvertTo. + if returnErr && err != nil { + return casted, err + } + if err != nil && types.ErrTruncated.Equal(err) && col.GetType() != mysql.TypeSet && col.GetType() != mysql.TypeEnum { + str, err1 := val.ToString() + if err1 != nil { + logutil.BgLogger().Warn("Datum ToString failed", zap.Stringer("Datum", val), zap.Error(err1)) + } + err = types.ErrTruncatedWrongVal.GenWithStackByArgs(col.FieldType.CompactStr(), str) + } else if (sc.InInsertStmt || sc.InUpdateStmt) && !casted.IsNull() && + (col.GetType() == mysql.TypeDate || col.GetType() == mysql.TypeDatetime || col.GetType() == mysql.TypeTimestamp) { + str, err1 := val.ToString() + if err1 != nil { + logutil.BgLogger().Warn("Datum ToString failed", zap.Stringer("Datum", val), zap.Error(err1)) + str = val.GetString() + } + if innCasted, exit, innErr := handleZeroDatetime(ctx, col, casted, str, types.ErrWrongValue.Equal(err)); exit { + return innCasted, innErr + } + } else if err != nil && charset.ErrInvalidCharacterString.Equal(err) { + err = convertToIncorrectStringErr(err, col.Name.O) + logutil.BgLogger().Debug("incorrect string value", + zap.Uint64("conn", ctx.GetSessionVars().ConnectionID), zap.Error(err)) + } + + err = sc.HandleTruncate(err) + err = sc.HandleOverflow(err, err) + + if forceIgnoreTruncate { + err = nil + } else if err != nil { + return casted, err + } + + if col.GetType() == mysql.TypeString && !types.IsBinaryStr(&col.FieldType) { + truncateTrailingSpaces(&casted) + } + return casted, err +} + +// ColDesc describes column information like MySQL desc and show columns do. +type ColDesc struct { + Field string + Type string + // Charset is nil if the column doesn't have a charset, or a string indicating the charset name. + Charset interface{} + // Collation is nil if the column doesn't have a collation, or a string indicating the collation name. + Collation interface{} + Null string + Key string + DefaultValue interface{} + Extra string + Privileges string + Comment string +} + +const defaultPrivileges = "select,insert,update,references" + +// NewColDesc returns a new ColDesc for a column. +func NewColDesc(col *Column) *ColDesc { + // TODO: if we have no primary key and a unique index which's columns are all not null + // we will set these columns' flag as PriKeyFlag + // see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html + // create table + name := col.Name + nullFlag := "YES" + if mysql.HasNotNullFlag(col.GetFlag()) { + nullFlag = "NO" + } + keyFlag := "" + if mysql.HasPriKeyFlag(col.GetFlag()) { + keyFlag = "PRI" + } else if mysql.HasUniKeyFlag(col.GetFlag()) { + keyFlag = "UNI" + } else if mysql.HasMultipleKeyFlag(col.GetFlag()) { + keyFlag = "MUL" + } + var defaultValue interface{} + if !mysql.HasNoDefaultValueFlag(col.GetFlag()) { + defaultValue = col.GetDefaultValue() + if defaultValStr, ok := defaultValue.(string); ok { + if (col.GetType() == mysql.TypeTimestamp || col.GetType() == mysql.TypeDatetime) && + strings.EqualFold(defaultValStr, ast.CurrentTimestamp) && + col.GetDecimal() > 0 { + defaultValue = fmt.Sprintf("%s(%d)", defaultValStr, col.GetDecimal()) + } + } + } + + extra := "" + if mysql.HasAutoIncrementFlag(col.GetFlag()) { + extra = "auto_increment" + } else if mysql.HasOnUpdateNowFlag(col.GetFlag()) { + // in order to match the rules of mysql 8.0.16 version + // see https://github.com/pingcap/tidb/issues/10337 + extra = "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + OptionalFsp(&col.FieldType) + } else if col.IsGenerated() { + if col.GeneratedStored { + extra = "STORED GENERATED" + } else { + extra = "VIRTUAL GENERATED" + } + } + + desc := &ColDesc{ + Field: name.O, + Type: col.GetTypeDesc(), + Charset: col.GetCharset(), + Collation: col.GetCollate(), + Null: nullFlag, + Key: keyFlag, + DefaultValue: defaultValue, + Extra: extra, + Privileges: defaultPrivileges, + Comment: col.Comment, + } + if !field_types.HasCharset(&col.ColumnInfo.FieldType) { + desc.Charset = nil + desc.Collation = nil + } + return desc +} + +// ColDescFieldNames returns the fields name in result set for desc and show columns. +func ColDescFieldNames(full bool) []string { + if full { + return []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"} + } + return []string{"Field", "Type", "Null", "Key", "Default", "Extra"} +} + +// CheckOnce checks if there are duplicated column names in cols. +func CheckOnce(cols []*Column) error { + m := map[string]struct{}{} + for _, col := range cols { + name := col.Name + _, ok := m[name.L] + if ok { + return errDuplicateColumn.GenWithStackByArgs(name) + } + + m[name.L] = struct{}{} + } + + return nil +} + +// CheckNotNull checks if nil value set to a column with NotNull flag is set. +// When caller is LOAD DATA, `rowCntInLoadData` should be greater than 0 and it +// will return a ErrWarnNullToNotnull when error. +// Otherwise, it will return a ErrColumnCantNull when error. +func (c *Column) CheckNotNull(data *types.Datum, rowCntInLoadData uint64) error { + if (mysql.HasNotNullFlag(c.GetFlag()) || mysql.HasPreventNullInsertFlag(c.GetFlag())) && data.IsNull() { + if rowCntInLoadData > 0 { + return ErrWarnNullToNotnull.GenWithStackByArgs(c.Name, rowCntInLoadData) + } + return ErrColumnCantNull.GenWithStackByArgs(c.Name) + } + return nil +} + +// HandleBadNull handles the bad null error. +// When caller is LOAD DATA, `rowCntInLoadData` should be greater than 0 the +// error is ErrWarnNullToNotnull. +// Otherwise, the error is ErrColumnCantNull. +// If BadNullAsWarning is true, it will append the error as a warning, else return the error. +func (c *Column) HandleBadNull(d *types.Datum, sc *stmtctx.StatementContext, rowCntInLoadData uint64) error { + if err := c.CheckNotNull(d, rowCntInLoadData); err != nil { + if sc.BadNullAsWarning { + sc.AppendWarning(err) + *d = GetZeroValue(c.ToInfo()) + return nil + } + return err + } + return nil +} + +// IsPKHandleColumn checks if the column is primary key handle column. +func (c *Column) IsPKHandleColumn(tbInfo *model.TableInfo) bool { + return mysql.HasPriKeyFlag(c.GetFlag()) && tbInfo.PKIsHandle +} + +// IsCommonHandleColumn checks if the column is common handle column. +func (c *Column) IsCommonHandleColumn(tbInfo *model.TableInfo) bool { + return mysql.HasPriKeyFlag(c.GetFlag()) && tbInfo.IsCommonHandle +} + +type getColOriginDefaultValue struct { + StrictSQLMode bool +} + +// GetColOriginDefaultValue gets default value of the column from original default value. +func GetColOriginDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { + return getColDefaultValue(ctx, col, col.GetOriginDefaultValue(), nil) +} + +// GetColOriginDefaultValueWithoutStrictSQLMode gets default value of the column from original default value with Strict SQL mode. +func GetColOriginDefaultValueWithoutStrictSQLMode(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { + return getColDefaultValue(ctx, col, col.GetOriginDefaultValue(), &getColOriginDefaultValue{ + StrictSQLMode: false, + }) +} + +// GetColDefaultValue gets default value of the column. +func GetColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { + defaultValue := col.GetDefaultValue() + if !col.DefaultIsExpr { + return getColDefaultValue(ctx, col, defaultValue, nil) + } + return getColDefaultExprValue(ctx, col, defaultValue.(string)) +} + +// EvalColDefaultExpr eval default expr node to explicit default value. +func EvalColDefaultExpr(ctx sessionctx.Context, col *model.ColumnInfo, defaultExpr ast.ExprNode) (types.Datum, error) { + d, err := expression.EvalAstExpr(ctx, defaultExpr) + if err != nil { + return types.Datum{}, err + } + // Check the evaluated data type by cast. + value, err := CastValue(ctx, d, col, false, false) + if err != nil { + return types.Datum{}, err + } + return value, nil +} + +func getColDefaultExprValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultValue string) (types.Datum, error) { + var defaultExpr ast.ExprNode + expr := fmt.Sprintf("select %s", defaultValue) + stmts, _, err := parser.New().ParseSQL(expr) + if err == nil { + defaultExpr = stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr + } + d, err := expression.EvalAstExpr(ctx, defaultExpr) + if err != nil { + return types.Datum{}, err + } + // Check the evaluated data type by cast. + value, err := CastValue(ctx, d, col, false, false) + if err != nil { + return types.Datum{}, err + } + return value, nil +} + +func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVal interface{}, args *getColOriginDefaultValue) (types.Datum, error) { + if defaultVal == nil { + return getColDefaultValueFromNil(ctx, col, args) + } + + switch col.GetType() { + case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDatetime: + default: + value, err := CastValue(ctx, types.NewDatum(defaultVal), col, false, false) + if err != nil { + return types.Datum{}, err + } + return value, nil + } + + // Check and get timestamp/datetime default value. + var needChangeTimeZone bool + var explicitTz *time.Location + // If the column's default value is not ZeroDatetimeStr nor CurrentTimestamp, should use the time zone of the default value itself. + if col.GetType() == mysql.TypeTimestamp { + if vv, ok := defaultVal.(string); ok && vv != types.ZeroDatetimeStr && !strings.EqualFold(vv, ast.CurrentTimestamp) { + needChangeTimeZone = true + // For col.Version = 0, the timezone information of default value is already lost, so use the system timezone as the default value timezone. + explicitTz = timeutil.SystemLocation() + if col.Version >= model.ColumnInfoVersion1 { + explicitTz = time.UTC + } + } + } + value, err := expression.GetTimeValue(ctx, defaultVal, col.GetType(), col.GetDecimal(), explicitTz) + if err != nil { + return types.Datum{}, errGetDefaultFailed.GenWithStackByArgs(col.Name) + } + // If the column's default value is not ZeroDatetimeStr or CurrentTimestamp, convert the default value to the current session time zone. + if needChangeTimeZone { + t := value.GetMysqlTime() + err = t.ConvertTimeZone(explicitTz, ctx.GetSessionVars().Location()) + if err != nil { + return value, err + } + value.SetMysqlTime(t) + } + return value, nil +} + +func getColDefaultValueFromNil(ctx sessionctx.Context, col *model.ColumnInfo, args *getColOriginDefaultValue) (types.Datum, error) { + if !mysql.HasNotNullFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) { + return types.Datum{}, nil + } + if col.GetType() == mysql.TypeEnum { + // For enum type, if no default value and not null is set, + // the default value is the first element of the enum list + if mysql.HasNotNullFlag(col.GetFlag()) { + defEnum, err := types.ParseEnumValue(col.FieldType.GetElems(), 1) + if err != nil { + return types.Datum{}, err + } + return types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()), nil + } + return types.Datum{}, nil + } + if mysql.HasAutoIncrementFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) { + // Auto increment column doesn't have default value and we should not return error. + return GetZeroValue(col), nil + } + vars := ctx.GetSessionVars() + sc := vars.StmtCtx + var strictSQLMode bool + if args != nil { + strictSQLMode = args.StrictSQLMode + } else { + strictSQLMode = vars.StrictSQLMode + } + if !strictSQLMode { + sc.AppendWarning(ErrNoDefaultValue.FastGenByArgs(col.Name)) + if mysql.HasNotNullFlag(col.GetFlag()) { + return GetZeroValue(col), nil + } + if mysql.HasNoDefaultValueFlag(col.GetFlag()) { + return types.Datum{}, nil + } + } + if sc.BadNullAsWarning { + sc.AppendWarning(ErrColumnCantNull.FastGenByArgs(col.Name)) + return GetZeroValue(col), nil + } + return types.Datum{}, ErrNoDefaultValue.GenWithStackByArgs(col.Name) +} + +// GetZeroValue gets zero value for given column type. +func GetZeroValue(col *model.ColumnInfo) types.Datum { + var d types.Datum + switch col.GetType() { + case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong: + if mysql.HasUnsignedFlag(col.GetFlag()) { + d.SetUint64(0) + } else { + d.SetInt64(0) + } + case mysql.TypeYear: + d.SetInt64(0) + case mysql.TypeFloat: + d.SetFloat32(0) + case mysql.TypeDouble: + d.SetFloat64(0) + case mysql.TypeNewDecimal: + d.SetLength(col.GetFlen()) + d.SetFrac(col.GetDecimal()) + d.SetMysqlDecimal(new(types.MyDecimal)) + case mysql.TypeString: + if col.GetFlen() > 0 && col.GetCharset() == charset.CharsetBin { + d.SetBytes(make([]byte, col.GetFlen())) + } else { + d.SetString("", col.GetCollate()) + } + case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + d.SetString("", col.GetCollate()) + case mysql.TypeDuration: + d.SetMysqlDuration(types.ZeroDuration) + case mysql.TypeDate: + d.SetMysqlTime(types.ZeroDate) + case mysql.TypeTimestamp: + d.SetMysqlTime(types.ZeroTimestamp) + case mysql.TypeDatetime: + d.SetMysqlTime(types.ZeroDatetime) + case mysql.TypeBit: + d.SetMysqlBit(types.ZeroBinaryLiteral) + case mysql.TypeSet: + d.SetMysqlSet(types.Set{}, col.GetCollate()) + case mysql.TypeEnum: + d.SetMysqlEnum(types.Enum{}, col.GetCollate()) + case mysql.TypeJSON: + d.SetMysqlJSON(types.CreateBinaryJSON(nil)) + } + return d +} + +// OptionalFsp convert a FieldType.GetDecimal() to string. +func OptionalFsp(fieldType *types.FieldType) string { + fsp := fieldType.GetDecimal() + if fsp == 0 { + return "" + } + return "(" + strconv.Itoa(fsp) + ")" +} + +// FillVirtualColumnValue will calculate the virtual column value by evaluating generated +// expression using rows from a chunk, and then fill this value into the chunk. +func FillVirtualColumnValue(virtualRetTypes []*types.FieldType, virtualColumnIndex []int, + expCols []*expression.Column, colInfos []*model.ColumnInfo, sctx sessionctx.Context, req *chunk.Chunk) error { + if len(virtualColumnIndex) == 0 { + return nil + } + + virCols := chunk.NewChunkWithCapacity(virtualRetTypes, req.Capacity()) + iter := chunk.NewIterator4Chunk(req) + for i, idx := range virtualColumnIndex { + for row := iter.Begin(); row != iter.End(); row = iter.Next() { + datum, err := expCols[idx].EvalVirtualColumn(row) + if err != nil { + return err + } + // Because the expression might return different type from + // the generated column, we should wrap a CAST on the result. + castDatum, err := CastValue(sctx, datum, colInfos[idx], false, true) + if err != nil { + return err + } + + // Clip to zero if get negative value after cast to unsigned. + if mysql.HasUnsignedFlag(colInfos[idx].FieldType.GetFlag()) && !castDatum.IsNull() && !sctx.GetSessionVars().StmtCtx.ShouldClipToZero() { + switch datum.Kind() { + case types.KindInt64: + if datum.GetInt64() < 0 { + castDatum = GetZeroValue(colInfos[idx]) + } + case types.KindFloat32, types.KindFloat64: + if types.RoundFloat(datum.GetFloat64()) < 0 { + castDatum = GetZeroValue(colInfos[idx]) + } + case types.KindMysqlDecimal: + if datum.GetMysqlDecimal().IsNegative() { + castDatum = GetZeroValue(colInfos[idx]) + } + } + } + + // Handle the bad null error. + if (mysql.HasNotNullFlag(colInfos[idx].GetFlag()) || mysql.HasPreventNullInsertFlag(colInfos[idx].GetFlag())) && castDatum.IsNull() { + castDatum = GetZeroValue(colInfos[idx]) + } + virCols.AppendDatum(i, &castDatum) + } + req.SetCol(idx, virCols.Column(i)) + } + return nil +} diff --git a/pkg/table/column_test.go b/pkg/table/column_test.go new file mode 100644 index 0000000000000..48caa4257539d --- /dev/null +++ b/pkg/table/column_test.go @@ -0,0 +1,498 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package table + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestString(t *testing.T) { + col := ToColumn(&model.ColumnInfo{ + FieldType: *types.NewFieldType(mysql.TypeTiny), + State: model.StatePublic, + }) + col.SetFlen(2) + col.SetDecimal(1) + col.SetCharset(mysql.DefaultCharset) + col.SetCollate(mysql.DefaultCollationName) + col.AddFlag(mysql.ZerofillFlag | mysql.UnsignedFlag | mysql.BinaryFlag | mysql.AutoIncrementFlag | mysql.NotNullFlag) + + require.Equal(t, "tinyint(2) unsigned zerofill", col.GetTypeDesc()) + col.ToInfo() + tbInfo := &model.TableInfo{} + require.False(t, col.IsPKHandleColumn(tbInfo)) + tbInfo.PKIsHandle = true + col.AddFlag(mysql.PriKeyFlag) + require.True(t, col.IsPKHandleColumn(tbInfo)) + + cs := col.String() + require.Greater(t, len(cs), 0) + + col.SetType(mysql.TypeEnum) + col.SetFlag(0) + col.SetElems([]string{"a", "b"}) + + require.Equal(t, "enum('a','b')", col.GetTypeDesc()) + + col.SetElems([]string{"'a'", "b"}) + require.Equal(t, "enum('''a''','b')", col.GetTypeDesc()) + + col.SetType(mysql.TypeFloat) + col.SetFlen(8) + col.SetDecimal(-1) + require.Equal(t, "float", col.GetTypeDesc()) + + col.SetDecimal(1) + require.Equal(t, "float(8,1)", col.GetTypeDesc()) + + col.SetType(mysql.TypeDatetime) + col.SetDecimal(6) + require.Equal(t, "datetime(6)", col.GetTypeDesc()) + + col.SetDecimal(0) + require.Equal(t, "datetime", col.GetTypeDesc()) + + col.SetDecimal(-1) + require.Equal(t, "datetime", col.GetTypeDesc()) +} + +func TestFind(t *testing.T) { + cols := []*Column{ + newCol("a"), + newCol("b"), + newCol("c"), + } + c, s := FindCols(cols, []string{"a"}, true) + require.Equal(t, cols[:1], c) + require.Equal(t, "", s) + + c1, s1 := FindCols(cols, []string{"d"}, true) + require.Nil(t, c1) + require.Equal(t, "d", s1) + + cols[0].AddFlag(mysql.OnUpdateNowFlag) + c2 := FindOnUpdateCols(cols) + require.Equal(t, cols[:1], c2) +} + +// checkNotNull checks if row has nil value set to a column with NotNull flag set. +func checkNotNull(cols []*Column, row []types.Datum) error { + for _, c := range cols { + if err := c.CheckNotNull(&row[c.Offset], 0); err != nil { + return err + } + } + return nil +} + +func TestCheck(t *testing.T) { + col := newCol("a") + col.SetFlag(mysql.AutoIncrementFlag) + cols := []*Column{col, col} + err := CheckOnce(cols) + require.Error(t, err) + cols = cols[:1] + err = checkNotNull(cols, types.MakeDatums(nil)) + require.NoError(t, err) + cols[0].AddFlag(mysql.NotNullFlag) + err = checkNotNull(cols, types.MakeDatums(nil)) + require.Error(t, err) + err = CheckOnce([]*Column{}) + require.NoError(t, err) +} + +func TestHandleBadNull(t *testing.T) { + col := newCol("a") + sc := stmtctx.NewStmtCtx() + d := types.Datum{} + err := col.HandleBadNull(&d, sc, 0) + require.NoError(t, err) + cmp, err := d.Compare(sc, &types.Datum{}, collate.GetBinaryCollator()) + require.NoError(t, err) + require.Equal(t, 0, cmp) + + col.AddFlag(mysql.NotNullFlag) + err = col.HandleBadNull(&types.Datum{}, sc, 0) + require.Error(t, err) + + sc.BadNullAsWarning = true + err = col.HandleBadNull(&types.Datum{}, sc, 0) + require.NoError(t, err) +} + +func TestDesc(t *testing.T) { + col := newCol("a") + col.SetFlag(mysql.AutoIncrementFlag | mysql.NotNullFlag | mysql.PriKeyFlag) + NewColDesc(col) + col.SetFlag(mysql.MultipleKeyFlag) + NewColDesc(col) + col.SetFlag(mysql.UniqueKeyFlag | mysql.OnUpdateNowFlag) + desc := NewColDesc(col) + require.Equal(t, "DEFAULT_GENERATED on update CURRENT_TIMESTAMP", desc.Extra) + col.SetFlag(0) + col.GeneratedExprString = "test" + col.GeneratedStored = true + desc = NewColDesc(col) + require.Equal(t, "STORED GENERATED", desc.Extra) + col.GeneratedStored = false + desc = NewColDesc(col) + require.Equal(t, "VIRTUAL GENERATED", desc.Extra) + ColDescFieldNames(false) + ColDescFieldNames(true) +} + +func TestGetZeroValue(t *testing.T) { + tp1 := &types.FieldType{} + tp1.SetType(mysql.TypeLonglong) + tp1.SetFlag(mysql.UnsignedFlag) + + tp2 := &types.FieldType{} + tp2.SetType(mysql.TypeString) + tp2.SetFlen(2) + tp2.SetCharset(charset.CharsetBin) + tp2.SetCollate(charset.CollationBin) + + tp3 := &types.FieldType{} + tp3.SetType(mysql.TypeString) + tp3.SetFlen(2) + tp3.SetCharset(charset.CharsetUTF8MB4) + tp3.SetCollate(charset.CollationBin) + + tests := []struct { + ft *types.FieldType + value types.Datum + }{ + { + types.NewFieldType(mysql.TypeLong), + types.NewIntDatum(0), + }, + { + tp1, + types.NewUintDatum(0), + }, + { + types.NewFieldType(mysql.TypeFloat), + types.NewFloat32Datum(0), + }, + { + types.NewFieldType(mysql.TypeDouble), + types.NewFloat64Datum(0), + }, + { + types.NewFieldType(mysql.TypeNewDecimal), + types.NewDecimalDatum(types.NewDecFromInt(0)), + }, + { + types.NewFieldType(mysql.TypeVarchar), + types.NewStringDatum(""), + }, + { + types.NewFieldType(mysql.TypeBlob), + types.NewStringDatum(""), + }, + { + types.NewFieldType(mysql.TypeDuration), + types.NewDurationDatum(types.ZeroDuration), + }, + { + types.NewFieldType(mysql.TypeDatetime), + types.NewDatum(types.ZeroDatetime), + }, + { + types.NewFieldType(mysql.TypeTimestamp), + types.NewDatum(types.ZeroTimestamp), + }, + { + types.NewFieldType(mysql.TypeDate), + types.NewDatum(types.ZeroDate), + }, + { + types.NewFieldType(mysql.TypeBit), + types.NewMysqlBitDatum(types.ZeroBinaryLiteral), + }, + { + types.NewFieldType(mysql.TypeSet), + types.NewDatum(types.Set{}), + }, + { + types.NewFieldType(mysql.TypeEnum), + types.NewDatum(types.Enum{}), + }, + { + tp2, + types.NewDatum(make([]byte, 2)), + }, + { + tp3, + types.NewDatum(""), + }, + { + types.NewFieldType(mysql.TypeJSON), + types.NewDatum(types.CreateBinaryJSON(nil)), + }, + } + sc := stmtctx.NewStmtCtx() + for _, tt := range tests { + t.Run(fmt.Sprintf("%+v", tt.ft), func(t *testing.T) { + colInfo := &model.ColumnInfo{FieldType: *tt.ft} + zv := GetZeroValue(colInfo) + require.Equal(t, tt.value.Kind(), zv.Kind()) + cmp, err := zv.Compare(sc, &tt.value, collate.GetCollator(tt.ft.GetCollate())) + require.NoError(t, err) + require.Equal(t, 0, cmp) + }) + } +} + +func TestCastValue(t *testing.T) { + ctx := mock.NewContext() + colInfo := model.ColumnInfo{ + FieldType: *types.NewFieldType(mysql.TypeLong), + State: model.StatePublic, + } + colInfo.SetCharset(mysql.UTF8Charset) + val, err := CastValue(ctx, types.Datum{}, &colInfo, false, false) + require.NoError(t, err) + require.Equal(t, int64(0), val.GetInt64()) + + val, err = CastValue(ctx, types.NewDatum("test"), &colInfo, false, false) + require.Error(t, err) + require.Equal(t, int64(0), val.GetInt64()) + + colInfoS := model.ColumnInfo{ + FieldType: *types.NewFieldType(mysql.TypeString), + State: model.StatePublic, + } + val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS, false, false) + require.NoError(t, err) + require.NotNil(t, val) + + colInfoS.SetCharset(mysql.UTF8Charset) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false, false) + require.Error(t, err) + + colInfoS.SetCharset(mysql.UTF8Charset) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false, true) + require.NoError(t, err) + + colInfoS.SetCharset(mysql.UTF8MB4Charset) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false, false) + require.Error(t, err) + + colInfoS.SetCharset(mysql.UTF8MB4Charset) + _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false, true) + require.NoError(t, err) + + colInfoS.SetCharset(charset.CharsetASCII) + _, err = CastValue(ctx, types.NewDatum([]byte{0x32, 0xf0}), &colInfoS, false, false) + require.Error(t, err) + + colInfoS.SetCharset(charset.CharsetASCII) + _, err = CastValue(ctx, types.NewDatum([]byte{0x32, 0xf0}), &colInfoS, false, true) + require.NoError(t, err) + + colInfoS.SetCharset(charset.CharsetUTF8MB4) + colInfoS.SetCollate("utf8mb4_general_ci") + val, err = CastValue(ctx, types.NewBinaryLiteralDatum([]byte{0xE5, 0xA5, 0xBD}), &colInfoS, false, false) + require.NoError(t, err) + require.Equal(t, "utf8mb4_general_ci", val.Collation()) + val, err = CastValue(ctx, types.NewBinaryLiteralDatum([]byte{0xE5, 0xA5, 0xBD, 0x81}), &colInfoS, false, false) + require.Error(t, err, "[table:1366]Incorrect string value '\\x81' for column ''") + require.Equal(t, "utf8mb4_general_ci", val.Collation()) + val, err = CastValue(ctx, types.NewDatum([]byte{0xE5, 0xA5, 0xBD, 0x81}), &colInfoS, false, false) + require.Error(t, err, "[table:1366]Incorrect string value '\\x81' for column ''") + require.Equal(t, "utf8mb4_general_ci", val.Collation()) +} + +func TestGetDefaultValue(t *testing.T) { + var nilDt types.Datum + nilDt.SetNull() + ctx := mock.NewContext() + zeroTimestamp := types.ZeroTimestamp + timestampValue := types.NewTime(types.FromDate(2019, 5, 6, 12, 48, 49, 0), mysql.TypeTimestamp, types.DefaultFsp) + + tp0 := types.FieldType{} + tp0.SetType(mysql.TypeLonglong) + + tp1 := types.FieldType{} + tp1.SetType(mysql.TypeLonglong) + tp1.SetFlag(mysql.NotNullFlag) + + tp2 := types.FieldType{} + tp2.SetType(mysql.TypeEnum) + tp2.SetFlag(mysql.NotNullFlag) + tp2.SetElems([]string{"abc", "def"}) + tp2.SetCollate(mysql.DefaultCollationName) + + tp3 := types.FieldType{} + tp3.SetType(mysql.TypeTimestamp) + tp3.SetFlag(mysql.TimestampFlag) + + tp4 := types.FieldType{} + tp4.SetType(mysql.TypeLonglong) + tp4.SetFlag(mysql.NotNullFlag | mysql.AutoIncrementFlag) + + tests := []struct { + colInfo *model.ColumnInfo + strict bool + val types.Datum + err error + }{ + { + &model.ColumnInfo{ + FieldType: tp1, + OriginDefaultValue: 1.0, + DefaultValue: 1.0, + }, + false, + types.NewIntDatum(1), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp1, + }, + false, + types.NewIntDatum(0), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp0, + }, + false, + types.Datum{}, + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp2, + }, + false, + types.NewMysqlEnumDatum(types.Enum{Name: "abc", Value: 1}), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp3, + OriginDefaultValue: "0000-00-00 00:00:00", + DefaultValue: "0000-00-00 00:00:00", + }, + false, + types.NewDatum(zeroTimestamp), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp3, + OriginDefaultValue: timestampValue.String(), + DefaultValue: timestampValue.String(), + }, + true, + types.NewDatum(timestampValue), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp3, + OriginDefaultValue: "not valid date", + DefaultValue: "not valid date", + }, + true, + types.NewDatum(zeroTimestamp), + errGetDefaultFailed, + }, + { + &model.ColumnInfo{ + FieldType: tp1, + }, + true, + types.NewDatum(zeroTimestamp), + ErrNoDefaultValue, + }, + { + &model.ColumnInfo{ + FieldType: tp4, + }, + true, + types.NewIntDatum(0), + nil, + }, + { + &model.ColumnInfo{ + FieldType: tp1, + DefaultIsExpr: true, + DefaultValue: "1", + }, + false, + nilDt, + nil, + }, + } + + exp := expression.EvalAstExpr + expression.EvalAstExpr = func(sctx sessionctx.Context, expr ast.ExprNode) (types.Datum, error) { + return types.NewIntDatum(1), nil + } + defer func() { + expression.EvalAstExpr = exp + }() + + for _, tt := range tests { + ctx.GetSessionVars().StmtCtx.BadNullAsWarning = !tt.strict + val, err := GetColDefaultValue(ctx, tt.colInfo) + if err != nil { + require.Errorf(t, tt.err, "%v", err) + continue + } + if tt.colInfo.DefaultIsExpr { + require.Equal(t, types.NewIntDatum(1), val) + } else { + require.Equal(t, tt.val, val) + } + } + + for _, tt := range tests { + ctx.GetSessionVars().StmtCtx.BadNullAsWarning = !tt.strict + val, err := GetColOriginDefaultValue(ctx, tt.colInfo) + if err != nil { + require.Errorf(t, tt.err, "%v", err) + continue + } + if !tt.colInfo.DefaultIsExpr { + require.Equal(t, tt.val, val) + } + } +} + +func newCol(name string) *Column { + return ToColumn(&model.ColumnInfo{ + Name: model.NewCIStr(name), + State: model.StatePublic, + }) +} diff --git a/pkg/table/constraint.go b/pkg/table/constraint.go new file mode 100644 index 0000000000000..f796776374bd4 --- /dev/null +++ b/pkg/table/constraint.go @@ -0,0 +1,253 @@ +// Copyright 2023-2023 PingCAP, Inc. +// +// 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. + +package table + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" + "go.uber.org/zap" +) + +// Constraint provides meta and map dependency describing a table constraint. +type Constraint struct { + *model.ConstraintInfo + ConstraintExpr expression.Expression +} + +// LoadCheckConstraint load check constraint +func LoadCheckConstraint(tblInfo *model.TableInfo) ([]*Constraint, error) { + removeInvalidCheckConstraintsInfo(tblInfo) + + constraints := make([]*Constraint, 0, len(tblInfo.Constraints)) + for _, conInfo := range tblInfo.Constraints { + con, err := ToConstraint(conInfo, tblInfo) + if err != nil { + return nil, err + } + constraints = append(constraints, con) + } + return constraints, nil +} + +// Delete invalid check constraint lazily +func removeInvalidCheckConstraintsInfo(tblInfo *model.TableInfo) { + // check if all the columns referenced by check constraint exist + var conInfos []*model.ConstraintInfo + for _, cons := range tblInfo.Constraints { + valid := true + for _, col := range cons.ConstraintCols { + if tblInfo.FindPublicColumnByName(col.L) == nil { + valid = false + break + } + } + if valid { + conInfos = append(conInfos, cons) + } + } + tblInfo.Constraints = conInfos +} + +// ToConstraint converts model.ConstraintInfo to Constraint +func ToConstraint(constraintInfo *model.ConstraintInfo, tblInfo *model.TableInfo) (*Constraint, error) { + ctx := mock.NewContext() + dbName := model.NewCIStr(ctx.GetSessionVars().CurrentDB) + columns, names, err := expression.ColumnInfos2ColumnsAndNames(ctx, dbName, tblInfo.Name, tblInfo.Columns, tblInfo) + if err != nil { + return nil, errors.Trace(err) + } + expr, err := buildConstraintExpression(ctx, constraintInfo.ExprString, columns, names) + if err != nil { + return nil, errors.Trace(err) + } + return &Constraint{ + constraintInfo, + expr, + }, nil +} + +func buildConstraintExpression(ctx sessionctx.Context, exprString string, + columns []*expression.Column, names types.NameSlice) (expression.Expression, error) { + schema := expression.NewSchema(columns...) + exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprString, schema, names) + if err != nil { + // If it got an error here, ddl may hang forever, so this error log is important. + logutil.BgLogger().Error("wrong check constraint expression", zap.String("expression", exprString), zap.Error(err)) + return nil, errors.Trace(err) + } + return exprs[0], nil +} + +// IsSupportedExpr checks whether the check constraint expression is allowed +func IsSupportedExpr(constr *ast.Constraint) (bool, error) { + checker := &checkConstraintChecker{ + allowed: true, + name: constr.Name, + } + constr.Expr.Accept(checker) + return checker.allowed, checker.reason +} + +var unsupportedNodeForCheckConstraint = map[string]struct{}{ + ast.Now: {}, + ast.CurrentTimestamp: {}, + ast.Curdate: {}, + ast.CurrentDate: {}, + ast.Curtime: {}, + ast.CurrentTime: {}, + ast.LocalTime: {}, + ast.LocalTimestamp: {}, + ast.UnixTimestamp: {}, + ast.UTCDate: {}, + ast.UTCTimestamp: {}, + ast.UTCTime: {}, + ast.ConnectionID: {}, + ast.CurrentUser: {}, + ast.SessionUser: {}, + ast.Version: {}, + ast.FoundRows: {}, + ast.LastInsertId: {}, + ast.SystemUser: {}, + ast.User: {}, + ast.Rand: {}, + ast.RowCount: {}, + ast.GetLock: {}, + ast.IsFreeLock: {}, + ast.IsUsedLock: {}, + ast.ReleaseLock: {}, + ast.ReleaseAllLocks: {}, + ast.LoadFile: {}, + ast.UUID: {}, + ast.UUIDShort: {}, + ast.Sleep: {}, +} + +type checkConstraintChecker struct { + allowed bool + reason error + name string +} + +// Enter implements Visitor interface. +func (checker *checkConstraintChecker) Enter(in ast.Node) (out ast.Node, skipChildren bool) { + switch x := in.(type) { + case *ast.FuncCallExpr: + if _, ok := unsupportedNodeForCheckConstraint[x.FnName.L]; ok { + checker.allowed = false + checker.reason = dbterror.ErrCheckConstraintNamedFuncIsNotAllowed.GenWithStackByArgs(checker.name, x.FnName.L) + return in, true + } + case *ast.VariableExpr: + // user defined or system variable is not allowed + checker.allowed = false + checker.reason = dbterror.ErrCheckConstraintVariables.GenWithStackByArgs(checker.name) + return in, true + case *ast.SubqueryExpr, *ast.ExistsSubqueryExpr: + // subquery is not allowed + checker.allowed = false + checker.reason = dbterror.ErrCheckConstraintFuncIsNotAllowed.GenWithStackByArgs(checker.name) + return in, true + case *ast.DefaultExpr: + // default expr is not allowed + checker.allowed = false + checker.reason = dbterror.ErrCheckConstraintNamedFuncIsNotAllowed.GenWithStackByArgs(checker.name, "default") + return in, true + } + return in, false +} + +// Leave implements Visitor interface. +func (checker *checkConstraintChecker) Leave(in ast.Node) (out ast.Node, ok bool) { + return in, checker.allowed +} + +// ContainsAutoIncrementCol checks if there is auto-increment col in given cols +func ContainsAutoIncrementCol(cols []model.CIStr, tblInfo *model.TableInfo) bool { + if autoIncCol := tblInfo.GetAutoIncrementColInfo(); autoIncCol != nil { + for _, col := range cols { + if col.L == autoIncCol.Name.L { + return true + } + } + } + return false +} + +// HasForeignKeyRefAction checks if there is foreign key with referential action in check constraints +func HasForeignKeyRefAction(fkInfos []*model.FKInfo, constraints []*ast.Constraint, checkConstr *ast.Constraint, dependedCols []model.CIStr) error { + if fkInfos != nil { + return checkForeignKeyRefActionByFKInfo(fkInfos, checkConstr, dependedCols) + } + for _, cons := range constraints { + if cons.Tp != ast.ConstraintForeignKey { + continue + } + refCol := cons.Refer + if refCol.OnDelete.ReferOpt != model.ReferOptionNoOption || refCol.OnUpdate.ReferOpt != model.ReferOptionNoOption { + var fkCols []model.CIStr + for _, key := range cons.Keys { + fkCols = append(fkCols, key.Column.Name) + } + for _, col := range dependedCols { + if hasSpecifiedCol(fkCols, col) { + return dbterror.ErrCheckConstraintUsingFKReferActionColumn.GenWithStackByArgs(col.L, checkConstr.Name) + } + } + } + } + return nil +} + +func checkForeignKeyRefActionByFKInfo(fkInfos []*model.FKInfo, checkConstr *ast.Constraint, dependedCols []model.CIStr) error { + for _, fkInfo := range fkInfos { + if fkInfo.OnDelete != 0 || fkInfo.OnUpdate != 0 { + for _, col := range dependedCols { + if hasSpecifiedCol(fkInfo.Cols, col) { + return dbterror.ErrCheckConstraintUsingFKReferActionColumn.GenWithStackByArgs(col.L, checkConstr.Name) + } + } + } + } + return nil +} + +func hasSpecifiedCol(cols []model.CIStr, col model.CIStr) bool { + for _, c := range cols { + if c.L == col.L { + return true + } + } + return false +} + +// IfCheckConstraintExprBoolType checks whether the check expression is bool type +func IfCheckConstraintExprBoolType(info *model.ConstraintInfo, tableInfo *model.TableInfo) error { + cons, err := ToConstraint(info, tableInfo) + if err != nil { + return err + } + if !mysql.HasIsBooleanFlag(cons.ConstraintExpr.GetType().GetFlag()) { + return dbterror.ErrNonBooleanExprForCheckConstraint.GenWithStackByArgs(cons.Name) + } + return nil +} diff --git a/pkg/table/index.go b/pkg/table/index.go new file mode 100644 index 0000000000000..a1f239e71ac6e --- /dev/null +++ b/pkg/table/index.go @@ -0,0 +1,100 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package table + +import ( + "context" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" +) + +// IndexIterator is the interface for iterator of index data on KV store. +type IndexIterator interface { + Next() (k []types.Datum, h kv.Handle, err error) + Close() +} + +// CreateIdxOpt contains the options will be used when creating an index. +type CreateIdxOpt struct { + Ctx context.Context + Untouched bool // If true, the index key/value is no need to commit. + IgnoreAssertion bool + FromBackFill bool +} + +// CreateIdxOptFunc is defined for the Create() method of Index interface. +// Here is a blog post about how to use this pattern: +// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis +type CreateIdxOptFunc func(*CreateIdxOpt) + +// IndexIsUntouched uses to indicate the index kv is untouched. +var IndexIsUntouched CreateIdxOptFunc = func(opt *CreateIdxOpt) { + opt.Untouched = true +} + +// WithIgnoreAssertion uses to indicate the process can ignore assertion. +var WithIgnoreAssertion = func(opt *CreateIdxOpt) { + opt.IgnoreAssertion = true +} + +// FromBackfill indicates that the index is created by DDL backfill worker. +// In the backfill-merge process, the index KVs from DML will be redirected to +// the temp index. On the other hand, the index KVs from DDL backfill worker should +// never be redirected to the temp index. +var FromBackfill = func(opt *CreateIdxOpt) { + opt.FromBackFill = true +} + +// WithCtx returns a CreateIdxFunc. +// This option is used to pass context.Context. +func WithCtx(ctx context.Context) CreateIdxOptFunc { + return func(opt *CreateIdxOpt) { + opt.Ctx = ctx + } +} + +// IndexIter is index kvs iter. +type IndexIter interface { + Next(kb []byte) ([]byte, []byte, bool, error) + Valid() bool +} + +// Index is the interface for index data on KV store. +type Index interface { + // Meta returns IndexInfo. + Meta() *model.IndexInfo + // TableMeta returns TableInfo + TableMeta() *model.TableInfo + // Create supports insert into statement. + Create(ctx sessionctx.Context, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle, handleRestoreData []types.Datum, opts ...CreateIdxOptFunc) (kv.Handle, error) + // Delete supports delete from statement. + Delete(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle) error + // GenIndexKVIter generate index key and value for multi-valued index, use iterator to reduce the memory allocation. + GenIndexKVIter(sc *stmtctx.StatementContext, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum) IndexIter + // Exist supports check index exists or not. + Exist(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle) (bool, kv.Handle, error) + // GenIndexKey generates an index key. If the index is a multi-valued index, use GenIndexKVIter instead. + GenIndexKey(sc *stmtctx.StatementContext, indexedValues []types.Datum, h kv.Handle, buf []byte) (key []byte, distinct bool, err error) + // GenIndexValue generates an index value. + GenIndexValue(sc *stmtctx.StatementContext, distinct bool, indexedValues []types.Datum, h kv.Handle, restoredData []types.Datum) ([]byte, error) + // FetchValues fetched index column values in a row. + // Param columns is a reused buffer, if it is not nil, FetchValues will fill the index values in it, + // and return the buffer, if it is nil, FetchValues will allocate the buffer instead. + FetchValues(row []types.Datum, columns []types.Datum) ([]types.Datum, error) +} diff --git a/pkg/table/main_test.go b/pkg/table/main_test.go new file mode 100644 index 0000000000000..8a462d3cf2754 --- /dev/null +++ b/pkg/table/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package table + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/table/table.go b/pkg/table/table.go new file mode 100644 index 0000000000000..e5bee0d5f14d9 --- /dev/null +++ b/pkg/table/table.go @@ -0,0 +1,285 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package table + +import ( + "context" + "time" + + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/tracing" +) + +// Type is used to distinguish between different tables that store data in different ways. +type Type int16 + +const ( + // NormalTable stores data in tikv, mocktikv and so on. + NormalTable Type = iota + // VirtualTable stores no data, just extract data from the memory struct. + VirtualTable + // ClusterTable contains the `VirtualTable` in the all cluster tidb nodes. + ClusterTable +) + +// IsNormalTable checks whether the table is a normal table type. +func (tp Type) IsNormalTable() bool { + return tp == NormalTable +} + +// IsVirtualTable checks whether the table is a virtual table type. +func (tp Type) IsVirtualTable() bool { + return tp == VirtualTable +} + +// IsClusterTable checks whether the table is a cluster table type. +func (tp Type) IsClusterTable() bool { + return tp == ClusterTable +} + +var ( + // ErrColumnCantNull is used for inserting null to a not null column. + ErrColumnCantNull = dbterror.ClassTable.NewStd(mysql.ErrBadNull) + // ErrUnknownColumn is returned when accessing an unknown column. + ErrUnknownColumn = dbterror.ClassTable.NewStd(mysql.ErrBadField) + errDuplicateColumn = dbterror.ClassTable.NewStd(mysql.ErrFieldSpecifiedTwice) + + // ErrWarnNullToNotnull is like ErrColumnCantNull but it's used in LOAD DATA + ErrWarnNullToNotnull = dbterror.ClassExecutor.NewStd(mysql.ErrWarnNullToNotnull) + + errGetDefaultFailed = dbterror.ClassTable.NewStd(mysql.ErrFieldGetDefaultFailed) + + // ErrNoDefaultValue is used when insert a row, the column value is not given, and the column has not null flag + // and it doesn't have a default value. + ErrNoDefaultValue = dbterror.ClassTable.NewStd(mysql.ErrNoDefaultForField) + // ErrIndexOutBound returns for index column offset out of bound. + ErrIndexOutBound = dbterror.ClassTable.NewStd(mysql.ErrIndexOutBound) + // ErrUnsupportedOp returns for unsupported operation. + ErrUnsupportedOp = dbterror.ClassTable.NewStd(mysql.ErrUnsupportedOp) + // ErrRowNotFound returns for row not found. + ErrRowNotFound = dbterror.ClassTable.NewStd(mysql.ErrRowNotFound) + // ErrTableStateCantNone returns for table none state. + ErrTableStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrTableStateCantNone) + // ErrColumnStateCantNone returns for column none state. + ErrColumnStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrColumnStateCantNone) + // ErrColumnStateNonPublic returns for column non-public state. + ErrColumnStateNonPublic = dbterror.ClassTable.NewStd(mysql.ErrColumnStateNonPublic) + // ErrIndexStateCantNone returns for index none state. + ErrIndexStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrIndexStateCantNone) + // ErrInvalidRecordKey returns for invalid record key. + ErrInvalidRecordKey = dbterror.ClassTable.NewStd(mysql.ErrInvalidRecordKey) + // ErrTruncatedWrongValueForField returns for truncate wrong value for field. + ErrTruncatedWrongValueForField = dbterror.ClassTable.NewStd(mysql.ErrTruncatedWrongValueForField) + // ErrUnknownPartition returns unknown partition error. + ErrUnknownPartition = dbterror.ClassTable.NewStd(mysql.ErrUnknownPartition) + // ErrNoPartitionForGivenValue returns table has no partition for value. + ErrNoPartitionForGivenValue = dbterror.ClassTable.NewStd(mysql.ErrNoPartitionForGivenValue) + // ErrLockOrActiveTransaction returns when execute unsupported statement in a lock session or an active transaction. + ErrLockOrActiveTransaction = dbterror.ClassTable.NewStd(mysql.ErrLockOrActiveTransaction) + // ErrSequenceHasRunOut returns when sequence has run out. + ErrSequenceHasRunOut = dbterror.ClassTable.NewStd(mysql.ErrSequenceRunOut) + // ErrRowDoesNotMatchGivenPartitionSet returns when the destination partition conflict with the partition selection. + ErrRowDoesNotMatchGivenPartitionSet = dbterror.ClassTable.NewStd(mysql.ErrRowDoesNotMatchGivenPartitionSet) + // ErrTempTableFull returns a table is full error, it's used by temporary table now. + ErrTempTableFull = dbterror.ClassTable.NewStd(mysql.ErrRecordFileFull) + // ErrOptOnCacheTable returns when exec unsupported opt at cache mode + ErrOptOnCacheTable = dbterror.ClassDDL.NewStd(mysql.ErrOptOnCacheTable) + // ErrCheckConstraintViolated return when check constraint is violated. + ErrCheckConstraintViolated = dbterror.ClassTable.NewStd(mysql.ErrCheckConstraintViolated) +) + +// RecordIterFunc is used for low-level record iteration. +type RecordIterFunc func(h kv.Handle, rec []types.Datum, cols []*Column) (more bool, err error) + +// AddRecordOpt contains the options will be used when adding a record. +type AddRecordOpt struct { + CreateIdxOpt + IsUpdate bool + ReserveAutoID int +} + +// AddRecordOption is defined for the AddRecord() method of the Table interface. +type AddRecordOption interface { + ApplyOn(*AddRecordOpt) +} + +// WithReserveAutoIDHint tells the AddRecord operation to reserve a batch of auto ID in the stmtctx. +type WithReserveAutoIDHint int + +// ApplyOn implements the AddRecordOption interface. +func (n WithReserveAutoIDHint) ApplyOn(opt *AddRecordOpt) { + opt.ReserveAutoID = int(n) +} + +// ApplyOn implements the AddRecordOption interface, so any CreateIdxOptFunc +// can be passed as the optional argument to the table.AddRecord method. +func (f CreateIdxOptFunc) ApplyOn(opt *AddRecordOpt) { + f(&opt.CreateIdxOpt) +} + +// IsUpdate is a defined value for AddRecordOptFunc. +var IsUpdate AddRecordOption = isUpdate{} + +type isUpdate struct{} + +func (i isUpdate) ApplyOn(opt *AddRecordOpt) { + opt.IsUpdate = true +} + +type columnAPI interface { + // Cols returns the columns of the table which is used in select, including hidden columns. + Cols() []*Column + + // VisibleCols returns the columns of the table which is used in select, excluding hidden columns. + VisibleCols() []*Column + + // HiddenCols returns the hidden columns of the table. + HiddenCols() []*Column + + // WritableCols returns columns of the table in writable states. + // Writable states includes Public, WriteOnly, WriteOnlyReorganization. + WritableCols() []*Column + + // DeletableCols returns columns of the table in deletable states. + // Deletable states includes Public, WriteOnly, WriteOnlyReorganization, DeleteOnly, DeleteReorganization. + DeletableCols() []*Column + + // FullHiddenColsAndVisibleCols returns hidden columns in all states and unhidden columns in public states. + FullHiddenColsAndVisibleCols() []*Column +} + +// Table is used to retrieve and modify rows in table. +type Table interface { + columnAPI + + // Indices returns the indices of the table. + // The caller must be aware of that not all the returned indices are public. + Indices() []Index + + // RecordPrefix returns the record key prefix. + RecordPrefix() kv.Key + // IndexPrefix returns the index key prefix. + IndexPrefix() kv.Key + + // AddRecord inserts a row which should contain only public columns + AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...AddRecordOption) (recordID kv.Handle, err error) + + // UpdateRecord updates a row which should contain only writable columns. + UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error + + // RemoveRecord removes a row in the table. + RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error + + // Allocators returns all allocators. + Allocators(ctx sessionctx.Context) autoid.Allocators + + // Meta returns TableInfo. + Meta() *model.TableInfo + + // Type returns the type of table + Type() Type + + // GetPartitionedTable returns nil if not partitioned + GetPartitionedTable() PartitionedTable +} + +// AllocAutoIncrementValue allocates an auto_increment value for a new row. +func AllocAutoIncrementValue(ctx context.Context, t Table, sctx sessionctx.Context) (int64, error) { + r, ctx := tracing.StartRegionEx(ctx, "table.AllocAutoIncrementValue") + defer r.End() + increment := sctx.GetSessionVars().AutoIncrementIncrement + offset := sctx.GetSessionVars().AutoIncrementOffset + alloc := t.Allocators(sctx).Get(autoid.AutoIncrementType) + _, max, err := alloc.Alloc(ctx, uint64(1), int64(increment), int64(offset)) + if err != nil { + return 0, err + } + return max, err +} + +// AllocBatchAutoIncrementValue allocates batch auto_increment value for rows, returning firstID, increment and err. +// The caller can derive the autoID by adding increment to firstID for N-1 times. +func AllocBatchAutoIncrementValue(ctx context.Context, t Table, sctx sessionctx.Context, N int) (firstID int64, increment int64, err error) { + increment = int64(sctx.GetSessionVars().AutoIncrementIncrement) + offset := int64(sctx.GetSessionVars().AutoIncrementOffset) + alloc := t.Allocators(sctx).Get(autoid.AutoIncrementType) + min, max, err := alloc.Alloc(ctx, uint64(N), increment, offset) + if err != nil { + return min, max, err + } + // SeekToFirstAutoIDUnSigned seeks to first autoID. Because AutoIncrement always allocate from 1, + // signed and unsigned value can be unified as the unsigned handle. + nr := int64(autoid.SeekToFirstAutoIDUnSigned(uint64(min), uint64(increment), uint64(offset))) + return nr, increment, nil +} + +// PhysicalTable is an abstraction for two kinds of table representation: partition or non-partitioned table. +// PhysicalID is a ID that can be used to construct a key ranges, all the data in the key range belongs to the corresponding PhysicalTable. +// For a non-partitioned table, its PhysicalID equals to its TableID; For a partition of a partitioned table, its PhysicalID is the partition's ID. +type PhysicalTable interface { + Table + GetPhysicalID() int64 +} + +// PartitionedTable is a Table, and it has a GetPartition() method. +// GetPartition() gets the partition from a partition table by a physical table ID, +type PartitionedTable interface { + Table + GetPartition(physicalID int64) PhysicalTable + GetPartitionByRow(sessionctx.Context, []types.Datum) (PhysicalTable, error) + GetAllPartitionIDs() []int64 + GetPartitionColumnIDs() []int64 + GetPartitionColumnNames() []model.CIStr + CheckForExchangePartition(ctx sessionctx.Context, pi *model.PartitionInfo, r []types.Datum, partID, ntID int64) error +} + +// TableFromMeta builds a table.Table from *model.TableInfo. +// Currently, it is assigned to tables.TableFromMeta in tidb package's init function. +var TableFromMeta func(allocators autoid.Allocators, tblInfo *model.TableInfo) (Table, error) + +// MockTableFromMeta only serves for test. +var MockTableFromMeta func(tableInfo *model.TableInfo) Table + +// CachedTable is a Table, and it has a UpdateLockForRead() method +// UpdateLockForRead() according to the reasons for not meeting the read conditions, update the lock information, +// And at the same time reload data from the original table. +type CachedTable interface { + Table + + Init(exec sqlexec.SQLExecutor) error + + // TryReadFromCache checks if the cache table is readable. + TryReadFromCache(ts uint64, leaseDuration time.Duration) (kv.MemBuffer, bool) + + // UpdateLockForRead if you cannot meet the conditions of the read buffer, + // you need to update the lock information and read the data from the original table + UpdateLockForRead(ctx context.Context, store kv.Storage, ts uint64, leaseDuration time.Duration) + + // WriteLockAndKeepAlive first obtain the write lock, then it renew the lease to keep the lock alive. + // 'exit' is a channel to tell the keep alive goroutine to exit. + // The result is sent to the 'wg' channel. + WriteLockAndKeepAlive(ctx context.Context, exit chan struct{}, leasePtr *uint64, wg chan error) +} diff --git a/pkg/table/table_test.go b/pkg/table/table_test.go new file mode 100644 index 0000000000000..cfdcdd8e3dafc --- /dev/null +++ b/pkg/table/table_test.go @@ -0,0 +1,43 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package table + +import ( + "testing" + + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/require" +) + +func TestErrorCode(t *testing.T) { + require.Equal(t, mysql.ErrBadNull, int(terror.ToSQLError(ErrColumnCantNull).Code)) + require.Equal(t, mysql.ErrBadField, int(terror.ToSQLError(ErrUnknownColumn).Code)) + require.Equal(t, mysql.ErrFieldSpecifiedTwice, int(terror.ToSQLError(errDuplicateColumn).Code)) + require.Equal(t, mysql.ErrFieldGetDefaultFailed, int(terror.ToSQLError(errGetDefaultFailed).Code)) + require.Equal(t, mysql.ErrNoDefaultForField, int(terror.ToSQLError(ErrNoDefaultValue).Code)) + require.Equal(t, mysql.ErrIndexOutBound, int(terror.ToSQLError(ErrIndexOutBound).Code)) + require.Equal(t, mysql.ErrUnsupportedOp, int(terror.ToSQLError(ErrUnsupportedOp).Code)) + require.Equal(t, mysql.ErrRowNotFound, int(terror.ToSQLError(ErrRowNotFound).Code)) + require.Equal(t, mysql.ErrTableStateCantNone, int(terror.ToSQLError(ErrTableStateCantNone).Code)) + require.Equal(t, mysql.ErrColumnStateCantNone, int(terror.ToSQLError(ErrColumnStateCantNone).Code)) + require.Equal(t, mysql.ErrColumnStateNonPublic, int(terror.ToSQLError(ErrColumnStateNonPublic).Code)) + require.Equal(t, mysql.ErrIndexStateCantNone, int(terror.ToSQLError(ErrIndexStateCantNone).Code)) + require.Equal(t, mysql.ErrInvalidRecordKey, int(terror.ToSQLError(ErrInvalidRecordKey).Code)) + require.Equal(t, mysql.ErrTruncatedWrongValueForField, int(terror.ToSQLError(ErrTruncatedWrongValueForField).Code)) + require.Equal(t, mysql.ErrUnknownPartition, int(terror.ToSQLError(ErrUnknownPartition).Code)) + require.Equal(t, mysql.ErrNoPartitionForGivenValue, int(terror.ToSQLError(ErrNoPartitionForGivenValue).Code)) + require.Equal(t, mysql.ErrLockOrActiveTransaction, int(terror.ToSQLError(ErrLockOrActiveTransaction).Code)) +} diff --git a/pkg/table/tables/BUILD.bazel b/pkg/table/tables/BUILD.bazel new file mode 100644 index 0000000000000..b10e03f4b7d4b --- /dev/null +++ b/pkg/table/tables/BUILD.bazel @@ -0,0 +1,116 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tables", + srcs = [ + "cache.go", + "index.go", + "mutation_checker.go", + "partition.go", + "state_remote.go", + "tables.go", + "testutil.go", + ], + importpath = "github.com/pingcap/tidb/pkg/table/tables", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/binloginfo", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/statistics", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/generatedexpr", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mock", + "//pkg/util/ranger", + "//pkg/util/rowcodec", + "//pkg/util/sqlexec", + "//pkg/util/stringutil", + "//pkg/util/tableutil", + "//pkg/util/tracing", + "@com_github_google_btree//:btree", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "tables_test", + timeout = "short", + srcs = [ + "cache_test.go", + "index_test.go", + "main_test.go", + "mutation_checker_test.go", + "state_remote_test.go", + "tables_test.go", + ], + embed = [":tables"], + flaky = True, + shard_count = 30, + deps = [ + "//pkg/ddl", + "//pkg/ddl/util/callback", + "//pkg/domain", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/helper", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/rowcodec", + "//pkg/util/stmtsummary", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//oracle", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/table/tables/cache.go b/pkg/table/tables/cache.go new file mode 100644 index 0000000000000..2ce866707d610 --- /dev/null +++ b/pkg/table/tables/cache.go @@ -0,0 +1,361 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tables + +import ( + "context" + "sync/atomic" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" +) + +var ( + _ table.CachedTable = &cachedTable{} +) + +type cachedTable struct { + TableCommon + cacheData atomic.Value + totalSize int64 + // StateRemote is not thread-safe, this tokenLimit is used to keep only one visitor. + tokenLimit +} + +type tokenLimit chan StateRemote + +func (t tokenLimit) TakeStateRemoteHandle() StateRemote { + handle := <-t + return handle +} + +func (t tokenLimit) TakeStateRemoteHandleNoWait() StateRemote { + select { + case handle := <-t: + return handle + default: + return nil + } +} + +func (t tokenLimit) PutStateRemoteHandle(handle StateRemote) { + t <- handle +} + +// cacheData pack the cache data and lease. +type cacheData struct { + Start uint64 + Lease uint64 + kv.MemBuffer +} + +func leaseFromTS(ts uint64, leaseDuration time.Duration) uint64 { + physicalTime := oracle.GetTimeFromTS(ts) + lease := oracle.GoTimeToTS(physicalTime.Add(leaseDuration)) + return lease +} + +func newMemBuffer(store kv.Storage) (kv.MemBuffer, error) { + // Here is a trick to get a MemBuffer data, because the internal API is not exposed. + // Create a transaction with start ts 0, and take the MemBuffer out. + buffTxn, err := store.Begin(tikv.WithStartTS(0)) + if err != nil { + return nil, err + } + return buffTxn.GetMemBuffer(), nil +} + +func (c *cachedTable) TryReadFromCache(ts uint64, leaseDuration time.Duration) (kv.MemBuffer, bool /*loading*/) { + tmp := c.cacheData.Load() + if tmp == nil { + return nil, false + } + data := tmp.(*cacheData) + if ts >= data.Start && ts < data.Lease { + leaseTime := oracle.GetTimeFromTS(data.Lease) + nowTime := oracle.GetTimeFromTS(ts) + distance := leaseTime.Sub(nowTime) + + var triggerFailpoint bool + failpoint.Inject("mockRenewLeaseABA1", func(_ failpoint.Value) { + triggerFailpoint = true + }) + + if distance >= 0 && distance <= leaseDuration/2 || triggerFailpoint { + if h := c.TakeStateRemoteHandleNoWait(); h != nil { + go c.renewLease(h, ts, data, leaseDuration) + } + } + // If data is not nil, but data.MemBuffer is nil, it means the data is being + // loading by a background goroutine. + return data.MemBuffer, data.MemBuffer == nil + } + return nil, false +} + +// newCachedTable creates a new CachedTable Instance +func newCachedTable(tbl *TableCommon) (table.Table, error) { + ret := &cachedTable{ + TableCommon: *tbl, + tokenLimit: make(chan StateRemote, 1), + } + return ret, nil +} + +// Init is an extra operation for cachedTable after TableFromMeta, +// Because cachedTable need some additional parameter that can't be passed in TableFromMeta. +func (c *cachedTable) Init(exec sqlexec.SQLExecutor) error { + raw, ok := exec.(sqlExec) + if !ok { + return errors.New("Need sqlExec rather than sqlexec.SQLExecutor") + } + handle := NewStateRemote(raw) + c.PutStateRemoteHandle(handle) + return nil +} + +func (c *cachedTable) loadDataFromOriginalTable(store kv.Storage) (kv.MemBuffer, uint64, int64, error) { + buffer, err := newMemBuffer(store) + if err != nil { + return nil, 0, 0, err + } + var startTS uint64 + totalSize := int64(0) + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnCacheTable) + err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { + prefix := tablecodec.GenTablePrefix(c.tableID) + if err != nil { + return errors.Trace(err) + } + startTS = txn.StartTS() + it, err := txn.Iter(prefix, prefix.PrefixNext()) + if err != nil { + return errors.Trace(err) + } + defer it.Close() + + for it.Valid() && it.Key().HasPrefix(prefix) { + key := it.Key() + value := it.Value() + err = buffer.Set(key, value) + if err != nil { + return errors.Trace(err) + } + totalSize += int64(len(key)) + totalSize += int64(len(value)) + err = it.Next() + if err != nil { + return errors.Trace(err) + } + } + return nil + }) + if err != nil { + return nil, 0, totalSize, err + } + + return buffer, startTS, totalSize, nil +} + +func (c *cachedTable) UpdateLockForRead(ctx context.Context, store kv.Storage, ts uint64, leaseDuration time.Duration) { + if h := c.TakeStateRemoteHandleNoWait(); h != nil { + go c.updateLockForRead(ctx, h, store, ts, leaseDuration) + } +} + +func (c *cachedTable) updateLockForRead(ctx context.Context, handle StateRemote, store kv.Storage, ts uint64, leaseDuration time.Duration) { + defer func() { + if r := recover(); r != nil { + log.Error("panic in the recoverable goroutine", + zap.Any("r", r), + zap.Stack("stack trace")) + } + c.PutStateRemoteHandle(handle) + }() + + // Load data from original table and the update lock information. + tid := c.Meta().ID + lease := leaseFromTS(ts, leaseDuration) + succ, err := handle.LockForRead(ctx, tid, lease) + if err != nil { + log.Warn("lock cached table for read", zap.Error(err)) + return + } + if succ { + c.cacheData.Store(&cacheData{ + Start: ts, + Lease: lease, + MemBuffer: nil, // Async loading, this will be set later. + }) + + // Make the load data process async, in case that loading data takes longer the + // lease duration, then the loaded data get staled and that process repeats forever. + go func() { + start := time.Now() + mb, startTS, totalSize, err := c.loadDataFromOriginalTable(store) + metrics.LoadTableCacheDurationHistogram.Observe(time.Since(start).Seconds()) + if err != nil { + log.Info("load data from table fail", zap.Error(err)) + return + } + + tmp := c.cacheData.Load().(*cacheData) + if tmp != nil && tmp.Start == ts { + c.cacheData.Store(&cacheData{ + Start: startTS, + Lease: tmp.Lease, + MemBuffer: mb, + }) + atomic.StoreInt64(&c.totalSize, totalSize) + } + }() + } + // Current status is not suitable to cache. +} + +const cachedTableSizeLimit = 64 * (1 << 20) + +// AddRecord implements the AddRecord method for the table.Table interface. +func (c *cachedTable) AddRecord(sctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + if atomic.LoadInt64(&c.totalSize) > cachedTableSizeLimit { + return nil, table.ErrOptOnCacheTable.GenWithStackByArgs("table too large") + } + txnCtxAddCachedTable(sctx, c.Meta().ID, c) + return c.TableCommon.AddRecord(sctx, r, opts...) +} + +func txnCtxAddCachedTable(sctx sessionctx.Context, tid int64, handle *cachedTable) { + txnCtx := sctx.GetSessionVars().TxnCtx + if txnCtx.CachedTables == nil { + txnCtx.CachedTables = make(map[int64]interface{}) + } + if _, ok := txnCtx.CachedTables[tid]; !ok { + txnCtx.CachedTables[tid] = handle + } +} + +// UpdateRecord implements table.Table +func (c *cachedTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { + // Prevent furthur writing when the table is already too large. + if atomic.LoadInt64(&c.totalSize) > cachedTableSizeLimit { + return table.ErrOptOnCacheTable.GenWithStackByArgs("table too large") + } + txnCtxAddCachedTable(sctx, c.Meta().ID, c) + return c.TableCommon.UpdateRecord(ctx, sctx, h, oldData, newData, touched) +} + +// RemoveRecord implements table.Table RemoveRecord interface. +func (c *cachedTable) RemoveRecord(sctx sessionctx.Context, h kv.Handle, r []types.Datum) error { + txnCtxAddCachedTable(sctx, c.Meta().ID, c) + return c.TableCommon.RemoveRecord(sctx, h, r) +} + +// TestMockRenewLeaseABA2 is used by test function TestRenewLeaseABAFailPoint. +var TestMockRenewLeaseABA2 chan struct{} + +func (c *cachedTable) renewLease(handle StateRemote, ts uint64, data *cacheData, leaseDuration time.Duration) { + failpoint.Inject("mockRenewLeaseABA2", func(_ failpoint.Value) { + c.PutStateRemoteHandle(handle) + <-TestMockRenewLeaseABA2 + c.TakeStateRemoteHandle() + }) + + defer c.PutStateRemoteHandle(handle) + + tid := c.Meta().ID + lease := leaseFromTS(ts, leaseDuration) + newLease, err := handle.RenewReadLease(context.Background(), tid, data.Lease, lease) + if err != nil { + if !kv.IsTxnRetryableError(err) { + log.Warn("Renew read lease error", zap.Error(err)) + } + return + } + if newLease > 0 { + c.cacheData.Store(&cacheData{ + Start: data.Start, + Lease: newLease, + MemBuffer: data.MemBuffer, + }) + } + + failpoint.Inject("mockRenewLeaseABA2", func(_ failpoint.Value) { + TestMockRenewLeaseABA2 <- struct{}{} + }) +} + +const cacheTableWriteLease = 5 * time.Second + +func (c *cachedTable) WriteLockAndKeepAlive(ctx context.Context, exit chan struct{}, leasePtr *uint64, wg chan error) { + writeLockLease, err := c.lockForWrite(ctx) + atomic.StoreUint64(leasePtr, writeLockLease) + wg <- err + if err != nil { + logutil.Logger(ctx).Warn("lock for write lock fail", zap.String("category", "cached table"), zap.Error(err)) + return + } + + t := time.NewTicker(cacheTableWriteLease / 2) + defer t.Stop() + for { + select { + case <-t.C: + if err := c.renew(ctx, leasePtr); err != nil { + logutil.Logger(ctx).Warn("renew write lock lease fail", zap.String("category", "cached table"), zap.Error(err)) + return + } + case <-exit: + return + } + } +} + +func (c *cachedTable) renew(ctx context.Context, leasePtr *uint64) error { + oldLease := atomic.LoadUint64(leasePtr) + physicalTime := oracle.GetTimeFromTS(oldLease) + newLease := oracle.GoTimeToTS(physicalTime.Add(cacheTableWriteLease)) + + h := c.TakeStateRemoteHandle() + defer c.PutStateRemoteHandle(h) + + succ, err := h.RenewWriteLease(ctx, c.Meta().ID, newLease) + if err != nil { + return errors.Trace(err) + } + if succ { + atomic.StoreUint64(leasePtr, newLease) + } + return nil +} + +func (c *cachedTable) lockForWrite(ctx context.Context) (uint64, error) { + handle := c.TakeStateRemoteHandle() + defer c.PutStateRemoteHandle(handle) + + return handle.LockForWrite(ctx, c.Meta().ID, cacheTableWriteLease) +} diff --git a/pkg/table/tables/cache_test.go b/pkg/table/tables/cache_test.go new file mode 100644 index 0000000000000..165b40dbc0956 --- /dev/null +++ b/pkg/table/tables/cache_test.go @@ -0,0 +1,560 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tables_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/oracle" +) + +func lastReadFromCache(tk *testkit.TestKit) bool { + return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache +} + +func TestCacheTableBasicScan(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists tmp1") + tk.MustExec("create table tmp1 (id int primary key auto_increment, u int unique, v int)") + tk.MustExec("insert into tmp1 values" + + "(1, 101, 1001), (3, 113, 1003), (5, 105, 1005), (7, 117, 1007), (9, 109, 1009)," + + "(10, 110, 1010), (12, 112, 1012), (14, 114, 1014), (16, 116, 1016), (18, 118, 1018)", + ) + tk.MustExec("alter table tmp1 cache") + + // For TableReader + // First read will read from original table + tk.MustQuery("select * from tmp1 where id>3 order by id").Check(testkit.Rows( + "5 105 1005", "7 117 1007", "9 109 1009", + "10 110 1010", "12 112 1012", "14 114 1014", "16 116 1016", "18 118 1018", + )) + // Test for join two cache table + tk.MustExec("drop table if exists join_t1, join_t2, join_t3") + tk.MustExec("create table join_t1 (id int)") + tk.MustExec("insert into join_t1 values(1)") + tk.MustExec("alter table join_t1 cache") + tk.MustQuery("select *from join_t1").Check(testkit.Rows("1")) + tk.MustExec("create table join_t2 (id int)") + tk.MustExec("insert into join_t2 values(2)") + tk.MustExec("alter table join_t2 cache") + tk.MustQuery("select *from join_t2").Check(testkit.Rows("2")) + tk.MustExec("create table join_t3 (id int)") + tk.MustExec("insert into join_t3 values(3)") + planUsed := false + for i := 0; i < 10; i++ { + tk.MustQuery("select *from join_t1 join join_t2").Check(testkit.Rows("1 2")) + if lastReadFromCache(tk) { + planUsed = true + break + } + } + require.True(t, planUsed) + + // Test for join a cache table and a normal table + for i := 0; i < 10; i++ { + tk.MustQuery("select * from join_t1 join join_t3").Check(testkit.Rows("1 3")) + if lastReadFromCache(tk) { + planUsed = true + break + } + } + require.True(t, planUsed) + + // Second read will from cache table + for i := 0; i < 100; i++ { + tk.MustQuery("select * from tmp1 where id>4 order by id").Check(testkit.Rows( + "5 105 1005", "7 117 1007", "9 109 1009", + "10 110 1010", "12 112 1012", "14 114 1014", "16 116 1016", "18 118 1018", + )) + if lastReadFromCache(tk) { + planUsed = true + break + } + } + require.True(t, planUsed) + + // For IndexLookUpReader + for i := 0; i < 10; i++ { + tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u>101 order by u").Check(testkit.Rows( + "5 105 1005", "9 109 1009", "10 110 1010", + "12 112 1012", "3 113 1003", "14 114 1014", "16 116 1016", "7 117 1007", "18 118 1018", + )) + if lastReadFromCache(tk) { + planUsed = true + break + } + } + require.True(t, planUsed) + + // For IndexReader + tk.MustQuery("select /*+ use_index(tmp1, u) */ id,u from tmp1 where u>101 order by id").Check(testkit.Rows( + "3 113", "5 105", "7 117", "9 109", "10 110", + "12 112", "14 114", "16 116", "18 118", + )) + tk.MustQuery("show warnings").Check(testkit.Rows()) + + // For IndexMerge, cache table should not use index merge + tk.MustQuery("select /*+ use_index_merge(tmp1, primary, u) */ * from tmp1 where id>5 or u>110 order by u").Check(testkit.Rows( + "9 109 1009", "10 110 1010", + "12 112 1012", "3 113 1003", "14 114 1014", "16 116 1016", "7 117 1007", "18 118 1018", + )) + + tk.MustQuery("show warnings").Check(testkit.Rows()) +} + +func TestCacheCondition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t2") + tk.MustExec("create table t2 (id int primary key, v int)") + tk.MustExec("alter table t2 cache") + + // Explain should not trigger cache. + for i := 0; i < 10; i++ { + tk.MustQuery("explain select * from t2") + time.Sleep(100 * time.Millisecond) + require.False(t, lastReadFromCache(tk)) + } + + // Insert should not trigger cache. + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("insert into t2 values (%d,%d)", i, i)) + time.Sleep(100 * time.Millisecond) + require.False(t, lastReadFromCache(tk)) + } + + // Update should not trigger cache. + for i := 0; i < 10; i++ { + tk.MustExec("update t2 set v = v + 1 where id > 0") + time.Sleep(100 * time.Millisecond) + require.False(t, lastReadFromCache(tk)) + } + + // Contains PointGet Update should not trigger cache. + for i := 0; i < 10; i++ { + tk.MustExec("update t2 set v = v + 1 where id = 2") + time.Sleep(100 * time.Millisecond) + require.False(t, lastReadFromCache(tk)) + } + + // Contains PointGet Delete should not trigger cache. + for i := 0; i < 10; i++ { + tk.MustExec(fmt.Sprintf("delete from t2 where id = %d", i)) + time.Sleep(100 * time.Millisecond) + require.False(t, lastReadFromCache(tk)) + } + + // Normal query should trigger cache. + tk.MustQuery("select * from t2") + cacheUsed := false + for i := 0; i < 100; i++ { + tk.MustQuery("select * from t2") + if lastReadFromCache(tk) { + cacheUsed = true + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, cacheUsed) +} + +func TestCacheTableBasicReadAndWrite(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk1 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk.MustExec("drop table if exists write_tmp1") + tk.MustExec("create table write_tmp1 (id int primary key auto_increment, u int unique, v int)") + tk.MustExec("insert into write_tmp1 values" + + "(1, 101, 1001), (3, 113, 1003)", + ) + + tk.MustExec("alter table write_tmp1 cache") + // Read and add read lock + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "3 113 1003")) + // read lock should valid + var i int + for i = 0; i < 10; i++ { + if lastReadFromCache(tk) { + break + } + // Wait for the cache to be loaded. + time.Sleep(50 * time.Millisecond) + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "3 113 1003")) + } + require.True(t, i < 10) + + tk.MustExec("use test") + tk1.MustExec("insert into write_tmp1 values (2, 222, 222)") + // write lock exists + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", + "2 222 222", + "3 113 1003")) + require.False(t, lastReadFromCache(tk)) + + // wait write lock expire and check cache can be used again + for !lastReadFromCache(tk) { + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows( + "1 101 1001", + "2 222 222", + "3 113 1003")) + } + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 222", "3 113 1003")) + tk1.MustExec("update write_tmp1 set v = 3333 where id = 2") + for !lastReadFromCache(tk) { + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 3333", "3 113 1003")) + } + tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 3333", "3 113 1003")) +} + +func TestCacheTableComplexRead(t *testing.T) { + store := testkit.CreateMockStore(t) + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + tk1.MustExec("create table complex_cache (id int primary key auto_increment, u int unique, v int)") + tk1.MustExec("insert into complex_cache values" + "(5, 105, 1005), (7, 117, 1007), (9, 109, 1009)") + tk1.MustExec("alter table complex_cache cache") + tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) + var i int + for i = 0; i < 100; i++ { + time.Sleep(100 * time.Millisecond) + tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) + if lastReadFromCache(tk1) { + break + } + } + require.True(t, i < 10) + + tk1.MustExec("begin") + tk2.MustExec("begin") + tk2.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) + for i = 0; i < 10; i++ { + time.Sleep(100 * time.Millisecond) + tk2.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) + if lastReadFromCache(tk2) { + break + } + } + require.True(t, i < 10) + tk2.MustExec("commit") + + tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) + require.True(t, lastReadFromCache(tk1)) + tk1.MustExec("commit") +} + +func TestBeginSleepABA(t *testing.T) { + // During the change "cache1 -> no cache -> cache2", + // cache1 and cache2 may be not the same anymore + // A transaction should not only check the cache exists, but also check the cache unchanged. + + store := testkit.CreateMockStore(t) + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + tk1.MustExec("drop table if exists aba") + tk1.MustExec("create table aba (id int, v int)") + tk1.MustExec("insert into aba values (1, 1)") + tk1.MustExec("alter table aba cache") + tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) + cacheUsed := false + for i := 0; i < 100; i++ { + tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) + if lastReadFromCache(tk1) { + cacheUsed = true + break + } + } + require.True(t, cacheUsed) + + // Begin, read from cache. + tk1.MustExec("begin") + tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) + if !lastReadFromCache(tk1) { + // TODO: should read from cache, but it is not stable + // It is a bug, ref https://github.com/pingcap/tidb/issues/36838 + t.Skip("unstable now, skip") + return + } + + // Another session change the data and make the cache unavailable. + tk2.MustExec("update aba set v = 2") + + // And then make the cache available again. + cacheUsed = false + for i := 0; i < 100; i++ { + tk2.MustQuery("select * from aba").Check(testkit.Rows("1 2")) + if lastReadFromCache(tk2) { + cacheUsed = true + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, cacheUsed) + + // tk1 should not use the staled cache, because the data is changed. + tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) + require.False(t, lastReadFromCache(tk1)) +} + +func TestRenewLease(t *testing.T) { + // Test RenewLeaseForRead + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + se := tk.Session() + tk.MustExec("create table cache_renew_t (id int)") + tk.MustExec("alter table cache_renew_t cache") + tbl, err := se.GetInfoSchema().(infoschema.InfoSchema).TableByName(model.NewCIStr("test"), model.NewCIStr("cache_renew_t")) + require.NoError(t, err) + var i int + tk.MustExec("select * from cache_renew_t") + + tk1 := testkit.NewTestKit(t, store) + remote := tables.NewStateRemote(tk1.Session()) + var leaseBefore uint64 + for i = 0; i < 20; i++ { + time.Sleep(200 * time.Millisecond) + lockType, lease, err := remote.Load(context.Background(), tbl.Meta().ID) + require.NoError(t, err) + if lockType == tables.CachedTableLockRead { + leaseBefore = lease + break + } + } + require.True(t, i < 20) + + for i = 0; i < 20; i++ { + time.Sleep(200 * time.Millisecond) + tk.MustExec("select * from cache_renew_t") + lockType, lease, err := remote.Load(context.Background(), tbl.Meta().ID) + require.NoError(t, err) + require.Equal(t, lockType, tables.CachedTableLockRead) + if leaseBefore != lease { + break + } + } + require.True(t, i < 20) +} + +func TestCacheTableWriteOperatorWaitLockLease(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + se := tk.Session() + + // This line is a hack, if auth user string is "", the statement summary is skipped, + // so it's added to make the later code been covered. + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) + + tk.MustExec("drop table if exists wait_tb1") + tk.MustExec("create table wait_tb1(id int)") + tk.MustExec("alter table wait_tb1 cache") + var i int + for i = 0; i < 10; i++ { + tk.MustQuery("select * from wait_tb1").Check(testkit.Rows()) + if lastReadFromCache(tk) { + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, i < 10) + stmtsummary.StmtSummaryByDigestMap.Clear() + tk.MustExec("insert into wait_tb1 values(1)") + require.True(t, se.GetSessionVars().StmtCtx.WaitLockLeaseTime > 0) + + tk.MustQuery("select DIGEST_TEXT from INFORMATION_SCHEMA.STATEMENTS_SUMMARY where MAX_BACKOFF_TIME > 0 or MAX_WAIT_TIME > 0").Check(testkit.Rows("insert into `wait_tb1` values ( ? )")) +} + +func TestTableCacheLeaseVariable(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + // Check default value. + tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("3")) + + // Check a valid value. + tk.MustExec("set @@global.tidb_table_cache_lease = 1;") + tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("1")) + + // Check a invalid value, the valid range is [2, 10] + tk.MustExec("set @@global.tidb_table_cache_lease = 111;") + tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_table_cache_lease value: '111'")) + tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("10")) + + // Change to a non-default value and verify the behaviour. + tk.MustExec("set @@global.tidb_table_cache_lease = 2;") + + tk.MustExec("drop table if exists test_lease_variable;") + tk.MustExec(`create table test_lease_variable(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) + tk.MustExec(`insert into test_lease_variable(c0, c1, c2) values (1, null, 'green');`) + tk.MustExec(`alter table test_lease_variable cache;`) + + cached := false + for i := 0; i < 20; i++ { + tk.MustQuery("select * from test_lease_variable").Check(testkit.Rows("1 green")) + if lastReadFromCache(tk) { + cached = true + break + } + time.Sleep(50 * time.Millisecond) + } + require.True(t, cached) + + start := time.Now() + tk.MustExec("update test_lease_variable set c0 = 2") + duration := time.Since(start) + + // The lease is 2s, check how long the write operation takes. + require.True(t, duration > time.Second) + require.True(t, duration < 3*time.Second) +} + +func TestMetrics(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists test_metrics;") + tk.MustExec(`create table test_metrics(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) + tk.MustExec(`create table nt (c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) + tk.MustExec(`insert into test_metrics(c0, c1, c2) values (1, null, 'green');`) + tk.MustExec(`alter table test_metrics cache;`) + + tk.MustQuery("select * from test_metrics").Check(testkit.Rows("1 green")) + cached := false + for i := 0; i < 20; i++ { + if lastReadFromCache(tk) { + cached = true + break + } + time.Sleep(50 * time.Millisecond) + tk.MustQuery("select * from test_metrics").Check(testkit.Rows("1 green")) + } + require.True(t, cached) + + counter := metrics.ReadFromTableCacheCounter + pb := &dto.Metric{} + + queries := []string{ + // Table scan + "select * from test_metrics", + // Index scan + "select c0 from test_metrics use index(uk) where c0 > 1", + // Index Lookup + "select c1 from test_metrics use index(uk) where c0 = 1", + // Point Get + "select c0 from test_metrics use index(uk) where c0 = 1", + // // Aggregation + "select count(*) from test_metrics", + // Join + "select * from test_metrics as a join test_metrics as b on a.c0 = b.c0 where a.c1 != 'xxx'", + } + counter.Write(pb) + i := pb.GetCounter().GetValue() + + for _, query := range queries { + tk.MustQuery(query) + i++ + counter.Write(pb) + hit := pb.GetCounter().GetValue() + require.Equal(t, i, hit) + } + + // A counter-example that doesn't increase metrics.ReadFromTableCacheCounter. + tk.MustQuery("select * from nt") + counter.Write(pb) + hit := pb.GetCounter().GetValue() + require.Equal(t, i, hit) +} + +func TestRenewLeaseABAFailPoint(t *testing.T) { + store := testkit.CreateMockStore(t) + + tables.TestMockRenewLeaseABA2 = make(chan struct{}) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t_lease;") + tk.MustExec(`create table t_lease(a int, b int);`) + tk.MustExec(`insert into t_lease values (1, 1)`) + tk.MustExec(`alter table t_lease cache`) + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("use test") + tk2.MustExec("use test") + + // Load the cache data by this query. + var cacheUsed bool + for i := 0; i < 10; i++ { + tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 1")) + if lastReadFromCache(tk) { + cacheUsed = true + break + } + time.Sleep(50 * time.Millisecond) + } + require.True(t, cacheUsed) + + // Renew lease by this query, mock the operation is delayed. + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/table/tables/mockRenewLeaseABA1", `return`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/table/tables/mockRenewLeaseABA2", `return`)) + tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 1")) + + // Make the cache data stale after writing: read lock-> write lock + tk1.MustExec("update t_lease set b = 2 where a = 1") + + // Mock reading from another TiDB instance: write lock -> read lock + is := tk2.Session().GetInfoSchema().(infoschema.InfoSchema) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t_lease")) + require.NoError(t, err) + lease := oracle.GoTimeToTS(time.Now().Add(20 * time.Second)) // A big enough future time + tk2.MustExec("update mysql.table_cache_meta set lock_type = 'READ', lease = ? where tid = ?", lease, tbl.Meta().ID) + + // Then the stagnant renew lease operation finally arrive. + tables.TestMockRenewLeaseABA2 <- struct{}{} + + <-tables.TestMockRenewLeaseABA2 + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/table/tables/mockRenewLeaseABA1")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/table/tables/mockRenewLeaseABA2")) + + // The renew lease operation should not success, + // And the session should not read from a staled cache data. + tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 2")) + require.False(t, lastReadFromCache(tk)) +} diff --git a/pkg/table/tables/index.go b/pkg/table/tables/index.go new file mode 100644 index 0000000000000..f15017bc49868 --- /dev/null +++ b/pkg/table/tables/index.go @@ -0,0 +1,742 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package tables + +import ( + "bytes" + "context" + "sync" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/tracing" +) + +// index is the data structure for index data in the KV store. +type index struct { + idxInfo *model.IndexInfo + tblInfo *model.TableInfo + prefix kv.Key + phyTblID int64 + // initNeedRestoreData is used to initialize `needRestoredData` in `index.Create()`. + // This routine cannot be done in `NewIndex()` because `needRestoreData` relies on `NewCollationEnabled()` and + // the collation global variable is initialized *after* `NewIndex()`. + initNeedRestoreData sync.Once + needRestoredData bool +} + +// NeedRestoredData checks whether the index columns needs restored data. +func NeedRestoredData(idxCols []*model.IndexColumn, colInfos []*model.ColumnInfo) bool { + for _, idxCol := range idxCols { + col := colInfos[idxCol.Offset] + if types.NeedRestoredData(&col.FieldType) { + return true + } + } + return false +} + +// NewIndex builds a new Index object. +func NewIndex(physicalID int64, tblInfo *model.TableInfo, indexInfo *model.IndexInfo) table.Index { + // The prefix can't encode from tblInfo.ID, because table partition may change the id to partition id. + var prefix kv.Key + if indexInfo.Global { + // In glabal index of partition table, prefix start with tblInfo.ID. + prefix = tablecodec.EncodeTableIndexPrefix(tblInfo.ID, indexInfo.ID) + } else { + // Otherwise, start with physicalID. + prefix = tablecodec.EncodeTableIndexPrefix(physicalID, indexInfo.ID) + } + index := &index{ + idxInfo: indexInfo, + tblInfo: tblInfo, + prefix: prefix, + phyTblID: physicalID, + } + return index +} + +// Meta returns index info. +func (c *index) Meta() *model.IndexInfo { + return c.idxInfo +} + +// TableMeta returns table info. +func (c *index) TableMeta() *model.TableInfo { + return c.tblInfo +} + +// GenIndexKey generates storage key for index values. Returned distinct indicates whether the +// indexed values should be distinct in storage (i.e. whether handle is encoded in the key). +func (c *index) GenIndexKey(sc *stmtctx.StatementContext, indexedValues []types.Datum, h kv.Handle, buf []byte) (key []byte, distinct bool, err error) { + idxTblID := c.phyTblID + if c.idxInfo.Global { + idxTblID = c.tblInfo.ID + } + return tablecodec.GenIndexKey(sc, c.tblInfo, c.idxInfo, idxTblID, indexedValues, h, buf) +} + +// GenIndexValue generates the index value. +func (c *index) GenIndexValue(sc *stmtctx.StatementContext, distinct bool, indexedValues []types.Datum, h kv.Handle, restoredData []types.Datum) ([]byte, error) { + c.initNeedRestoreData.Do(func() { + c.needRestoredData = NeedRestoredData(c.idxInfo.Columns, c.tblInfo.Columns) + }) + return tablecodec.GenIndexValuePortal(sc, c.tblInfo, c.idxInfo, c.needRestoredData, distinct, false, indexedValues, h, c.phyTblID, restoredData) +} + +// getIndexedValue will produce the result like: +// 1. If not multi-valued index, return directly. +// 2. (i1, [m1,m2], i2, ...) ==> [(i1, m1, i2, ...), (i1, m2, i2, ...)] +// 3. (i1, null, i2, ...) ==> [(i1, null, i2, ...)] +// 4. (i1, [], i2, ...) ==> nothing. +func (c *index) getIndexedValue(indexedValues []types.Datum) [][]types.Datum { + if !c.idxInfo.MVIndex { + return [][]types.Datum{indexedValues} + } + + vals := make([][]types.Datum, 0, 16) + jsonIdx := 0 + jsonIsNull := false + existsVals := make(map[string]struct{}) + var buf []byte + for !jsonIsNull { + val := make([]types.Datum, 0, len(indexedValues)) + for i, v := range indexedValues { + if !c.tblInfo.Columns[c.idxInfo.Columns[i].Offset].FieldType.IsArray() { + val = append(val, v) + } else { + // if the datum type is not JSON, it must come from cleanup index. + if v.IsNull() || v.Kind() != types.KindMysqlJSON { + val = append(val, v) + jsonIsNull = true + continue + } + elemCount := v.GetMysqlJSON().GetElemCount() + for { + // JSON cannot be indexed, if the value is JSON type, it must be multi-valued index. + if jsonIdx >= elemCount { + goto out + } + binaryJSON := v.GetMysqlJSON().ArrayGetElem(jsonIdx) + jsonIdx++ + buf = buf[:0] + key := string(binaryJSON.HashValue(buf)) + if _, exists := existsVals[key]; exists { + continue + } + existsVals[key] = struct{}{} + val = append(val, types.NewDatum(binaryJSON.GetValue())) + break + } + } + } + vals = append(vals, val) + } +out: + return vals +} + +// Create creates a new entry in the kvIndex data. +// If the index is unique and there is an existing entry with the same key, +// Create will return the existing entry's handle as the first return value, ErrKeyExists as the second return value. +func (c *index) Create(sctx sessionctx.Context, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum, opts ...table.CreateIdxOptFunc) (kv.Handle, error) { + if c.Meta().Unique { + txn.CacheTableInfo(c.phyTblID, c.tblInfo) + } + var opt table.CreateIdxOpt + for _, fn := range opts { + fn(&opt) + } + + indexedValues := c.getIndexedValue(indexedValue) + ctx := opt.Ctx + if ctx != nil { + var r tracing.Region + r, ctx = tracing.StartRegionEx(ctx, "index.Create") + defer r.End() + } else { + ctx = context.TODO() + } + vars := sctx.GetSessionVars() + writeBufs := vars.GetWriteStmtBufs() + skipCheck := vars.StmtCtx.BatchCheck + for _, value := range indexedValues { + key, distinct, err := c.GenIndexKey(vars.StmtCtx, value, h, writeBufs.IndexKeyBuf) + if err != nil { + return nil, err + } + + var ( + tempKey []byte + keyVer byte + keyIsTempIdxKey bool + ) + if !opt.FromBackFill { + key, tempKey, keyVer = GenTempIdxKeyByState(c.idxInfo, key) + if keyVer == TempIndexKeyTypeBackfill || keyVer == TempIndexKeyTypeDelete { + key, tempKey = tempKey, nil + keyIsTempIdxKey = true + } + } + + if opt.Untouched { + txn, err1 := sctx.Txn(true) + if err1 != nil { + return nil, err1 + } + // If the index kv was untouched(unchanged), and the key/value already exists in mem-buffer, + // should not overwrite the key with un-commit flag. + // So if the key exists, just do nothing and return. + v, err := txn.GetMemBuffer().Get(ctx, key) + if err == nil { + if len(v) != 0 { + continue + } + // The key is marked as deleted in the memory buffer, as the existence check is done lazily + // for optimistic transactions by default. The "untouched" key could still exist in the store, + // it's needed to commit this key to do the existence check so unset the untouched flag. + if !txn.IsPessimistic() { + keyFlags, err := txn.GetMemBuffer().GetFlags(key) + if err != nil { + return nil, err + } + if keyFlags.HasPresumeKeyNotExists() { + opt.Untouched = false + } + } + } + } + + // save the key buffer to reuse. + writeBufs.IndexKeyBuf = key + c.initNeedRestoreData.Do(func() { + c.needRestoredData = NeedRestoredData(c.idxInfo.Columns, c.tblInfo.Columns) + }) + idxVal, err := tablecodec.GenIndexValuePortal(sctx.GetSessionVars().StmtCtx, c.tblInfo, c.idxInfo, c.needRestoredData, distinct, opt.Untouched, value, h, c.phyTblID, handleRestoreData) + if err != nil { + return nil, err + } + + opt.IgnoreAssertion = opt.IgnoreAssertion || c.idxInfo.State != model.StatePublic + + if !distinct || skipCheck || opt.Untouched { + val := idxVal + if opt.Untouched && (keyIsTempIdxKey || len(tempKey) > 0) { + // Untouched key-values never occur in the storage and the temp index is not public. + // It is unnecessary to write the untouched temp index key-values. + continue + } + if keyIsTempIdxKey { + tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: distinct} + val = tempVal.Encode(nil) + } + err = txn.GetMemBuffer().Set(key, val) + if err != nil { + return nil, err + } + if len(tempKey) > 0 { + tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: distinct} + val = tempVal.Encode(nil) + err = txn.GetMemBuffer().Set(tempKey, val) + if err != nil { + return nil, err + } + } + if !opt.IgnoreAssertion && (!opt.Untouched) { + if sctx.GetSessionVars().LazyCheckKeyNotExists() && !txn.IsPessimistic() { + err = txn.SetAssertion(key, kv.SetAssertUnknown) + } else { + err = txn.SetAssertion(key, kv.SetAssertNotExist) + } + } + if err != nil { + return nil, err + } + continue + } + + var value []byte + if c.tblInfo.TempTableType != model.TempTableNone { + // Always check key for temporary table because it does not write to TiKV + value, err = txn.Get(ctx, key) + } else if sctx.GetSessionVars().LazyCheckKeyNotExists() && !keyIsTempIdxKey { + // For temp index keys, we can't get the temp value from memory buffer, even if the lazy check is enabled. + // Otherwise, it may cause the temp index value to be overwritten, leading to data inconsistency. + value, err = txn.GetMemBuffer().Get(ctx, key) + } else { + value, err = txn.Get(ctx, key) + } + if err != nil && !kv.IsErrNotFound(err) { + return nil, err + } + var tempIdxVal tablecodec.TempIndexValue + if len(value) > 0 && keyIsTempIdxKey { + tempIdxVal, err = tablecodec.DecodeTempIndexValue(value) + if err != nil { + return nil, err + } + } + // The index key value is not found or deleted. + if err != nil || len(value) == 0 || (!tempIdxVal.IsEmpty() && tempIdxVal.Current().Delete) { + val := idxVal + lazyCheck := sctx.GetSessionVars().LazyCheckKeyNotExists() && err != nil + if keyIsTempIdxKey { + tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: true} + val = tempVal.Encode(value) + } + needPresumeNotExists, err := needPresumeKeyNotExistsFlag(ctx, txn, key, tempKey, h, + keyIsTempIdxKey, c.tblInfo.IsCommonHandle, c.tblInfo.ID) + if err != nil { + return nil, err + } + if lazyCheck { + var flags []kv.FlagsOp + if needPresumeNotExists { + flags = []kv.FlagsOp{kv.SetPresumeKeyNotExists} + } + if !vars.ConstraintCheckInPlacePessimistic && vars.TxnCtx.IsPessimistic && vars.InTxn() && + !vars.InRestrictedSQL && vars.ConnectionID > 0 { + flags = append(flags, kv.SetNeedConstraintCheckInPrewrite) + } + err = txn.GetMemBuffer().SetWithFlags(key, val, flags...) + } else { + err = txn.GetMemBuffer().Set(key, val) + } + if err != nil { + return nil, err + } + if len(tempKey) > 0 { + tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: true} + val = tempVal.Encode(value) + if lazyCheck && needPresumeNotExists { + err = txn.GetMemBuffer().SetWithFlags(tempKey, val, kv.SetPresumeKeyNotExists) + } else { + err = txn.GetMemBuffer().Set(tempKey, val) + } + if err != nil { + return nil, err + } + } + if opt.IgnoreAssertion { + continue + } + if lazyCheck && !txn.IsPessimistic() { + err = txn.SetAssertion(key, kv.SetAssertUnknown) + } else { + err = txn.SetAssertion(key, kv.SetAssertNotExist) + } + if err != nil { + return nil, err + } + continue + } + if c.idxInfo.Global && len(value) != 0 && !bytes.Equal(value, idxVal) { + val := idxVal + err = txn.GetMemBuffer().Set(key, val) + if err != nil { + return nil, err + } + continue + } + + if keyIsTempIdxKey && !tempIdxVal.IsEmpty() { + value = tempIdxVal.Current().Value + } + handle, err := tablecodec.DecodeHandleInUniqueIndexValue(value, c.tblInfo.IsCommonHandle) + if err != nil { + return nil, err + } + return handle, kv.ErrKeyExists + } + return nil, nil +} + +func needPresumeKeyNotExistsFlag(ctx context.Context, txn kv.Transaction, key, tempKey kv.Key, + h kv.Handle, keyIsTempIdxKey bool, isCommon bool, tblID int64) (needFlag bool, err error) { + var uniqueTempKey kv.Key + if keyIsTempIdxKey { + uniqueTempKey = key + } else if len(tempKey) > 0 { + uniqueTempKey = tempKey + } else { + return true, nil + } + foundKey, dupHandle, err := FetchDuplicatedHandle(ctx, uniqueTempKey, true, txn, tblID, isCommon) + if err != nil { + return false, err + } + if foundKey && dupHandle != nil && !dupHandle.Equal(h) { + return false, kv.ErrKeyExists + } + return false, nil +} + +// Delete removes the entry for handle h and indexedValues from KV index. +func (c *index) Delete(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle) error { + indexedValues := c.getIndexedValue(indexedValue) + for _, value := range indexedValues { + key, distinct, err := c.GenIndexKey(sc, value, h, nil) + if err != nil { + return err + } + + key, tempKey, tempKeyVer := GenTempIdxKeyByState(c.idxInfo, key) + var originTempVal []byte + if len(tempKey) > 0 && c.idxInfo.Unique { + // Get the origin value of the unique temporary index key. + // Append the new delete operations to the end of the origin value. + originTempVal, err = getKeyInTxn(context.TODO(), txn, tempKey) + if err != nil { + return err + } + } + tempValElem := tablecodec.TempIndexValueElem{Handle: h, KeyVer: tempKeyVer, Delete: true, Distinct: distinct} + + // If index is global, decode the pid from value (if exists) and compare with c.physicalID. + // Only when pid in value equals to c.physicalID, the key can be deleted. + if c.idxInfo.Global { + if val, err := txn.GetMemBuffer().Get(context.Background(), key); err == nil { + segs := tablecodec.SplitIndexValue(val) + if len(segs.PartitionID) != 0 { + _, pid, err := codec.DecodeInt(segs.PartitionID) + if err != nil { + return err + } + if pid != c.phyTblID { + continue + } + } + } + } + + if distinct { + if len(key) > 0 { + err = txn.GetMemBuffer().DeleteWithFlags(key, kv.SetNeedLocked) + if err != nil { + return err + } + } + if len(tempKey) > 0 { + // Append to the end of the origin value for distinct value. + tempVal := tempValElem.Encode(originTempVal) + err = txn.GetMemBuffer().Set(tempKey, tempVal) + if err != nil { + return err + } + } + } else { + if len(key) > 0 { + err = txn.GetMemBuffer().Delete(key) + if err != nil { + return err + } + } + if len(tempKey) > 0 { + tempVal := tempValElem.Encode(nil) + err = txn.GetMemBuffer().Set(tempKey, tempVal) + if err != nil { + return err + } + } + } + if c.idxInfo.State == model.StatePublic { + // If the index is in public state, delete this index means it must exists. + err = txn.SetAssertion(key, kv.SetAssertExist) + } + if err != nil { + return err + } + } + return nil +} + +func (c *index) GenIndexKVIter(sc *stmtctx.StatementContext, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum) table.IndexIter { + indexedValues := c.getIndexedValue(indexedValue) + return &indexGenerator{ + c: c, + sctx: sc, + indexedVals: indexedValues, + h: h, + handleRestoreData: handleRestoreData, + i: 0, + } +} + +type indexGenerator struct { + c *index + sctx *stmtctx.StatementContext + indexedVals [][]types.Datum + h kv.Handle + handleRestoreData []types.Datum + + i int +} + +func (s *indexGenerator) Next(kb []byte) ([]byte, []byte, bool, error) { + val := s.indexedVals[s.i] + key, distinct, err := s.c.GenIndexKey(s.sctx, val, s.h, kb) + if err != nil { + return nil, nil, false, err + } + idxVal, err := s.c.GenIndexValue(s.sctx, distinct, val, s.h, s.handleRestoreData) + if err != nil { + return nil, nil, false, err + } + s.i++ + return key, idxVal, distinct, err +} + +func (s *indexGenerator) Valid() bool { + return s.i < len(s.indexedVals) +} + +const ( + // TempIndexKeyTypeNone means the key is not a temporary index key. + TempIndexKeyTypeNone byte = 0 + // TempIndexKeyTypeDelete indicates this value is written in the delete-only stage. + TempIndexKeyTypeDelete byte = 'd' + // TempIndexKeyTypeBackfill indicates this value is written in the backfill stage. + TempIndexKeyTypeBackfill byte = 'b' + // TempIndexKeyTypeMerge indicates this value is written in the merge stage. + TempIndexKeyTypeMerge byte = 'm' +) + +// GenTempIdxKeyByState is used to get the key version and the temporary key. +// The tempKeyVer means the temp index key/value version. +func GenTempIdxKeyByState(indexInfo *model.IndexInfo, indexKey kv.Key) (key, tempKey kv.Key, tempKeyVer byte) { + if indexInfo.State != model.StatePublic { + switch indexInfo.BackfillState { + case model.BackfillStateInapplicable: + return indexKey, nil, TempIndexKeyTypeNone + case model.BackfillStateRunning: + // Write to the temporary index. + tablecodec.IndexKey2TempIndexKey(indexKey) + if indexInfo.State == model.StateDeleteOnly { + return nil, indexKey, TempIndexKeyTypeDelete + } + return nil, indexKey, TempIndexKeyTypeBackfill + case model.BackfillStateReadyToMerge, model.BackfillStateMerging: + // Double write + tmp := make([]byte, len(indexKey)) + copy(tmp, indexKey) + tablecodec.IndexKey2TempIndexKey(tmp) + return indexKey, tmp, TempIndexKeyTypeMerge + } + } + return indexKey, nil, TempIndexKeyTypeNone +} + +func (c *index) Exist(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle) (bool, kv.Handle, error) { + indexedValues := c.getIndexedValue(indexedValue) + for _, val := range indexedValues { + key, distinct, err := c.GenIndexKey(sc, val, h, nil) + if err != nil { + return false, nil, err + } + // If index current is in creating status and using ingest mode, we need first + // check key exist status in temp index. + key, tempKey, _ := GenTempIdxKeyByState(c.idxInfo, key) + if len(tempKey) > 0 { + key = tempKey + } + foundKey, dupHandle, err := FetchDuplicatedHandle(context.TODO(), key, distinct, txn, c.tblInfo.ID, c.tblInfo.IsCommonHandle) + if err != nil || !foundKey { + return false, nil, err + } + if dupHandle != nil && !dupHandle.Equal(h) { + return false, nil, err + } + continue + } + return true, h, nil +} + +// FetchDuplicatedHandle is used to find the duplicated row's handle for a given unique index key. +func FetchDuplicatedHandle(ctx context.Context, key kv.Key, distinct bool, + txn kv.Transaction, tableID int64, isCommon bool) (foundKey bool, dupHandle kv.Handle, err error) { + if tablecodec.IsTempIndexKey(key) { + return fetchDuplicatedHandleForTempIndexKey(ctx, key, distinct, txn, tableID, isCommon) + } + // The index key is not from temp index. + val, err := getKeyInTxn(ctx, txn, key) + if err != nil || len(val) == 0 { + return false, nil, err + } + if distinct { + h, err := tablecodec.DecodeHandleInUniqueIndexValue(val, isCommon) + return true, h, err + } + return true, nil, nil +} + +func fetchDuplicatedHandleForTempIndexKey(ctx context.Context, tempKey kv.Key, distinct bool, + txn kv.Transaction, tableID int64, isCommon bool) (foundKey bool, dupHandle kv.Handle, err error) { + tempRawVal, err := getKeyInTxn(ctx, txn, tempKey) + if err != nil { + return false, nil, err + } + if tempRawVal == nil { + originKey := tempKey.Clone() + tablecodec.TempIndexKey2IndexKey(originKey) + originVal, err := getKeyInTxn(ctx, txn, originKey) + if err != nil || originVal == nil { + return false, nil, err + } + if distinct { + originHandle, err := tablecodec.DecodeHandleInUniqueIndexValue(originVal, isCommon) + if err != nil { + return false, nil, err + } + return true, originHandle, err + } + return false, nil, nil + } + tempVal, err := tablecodec.DecodeTempIndexValue(tempRawVal) + if err != nil { + return false, nil, err + } + curElem := tempVal.Current() + if curElem.Delete { + originKey := tempKey.Clone() + tablecodec.TempIndexKey2IndexKey(originKey) + originVal, err := getKeyInTxn(ctx, txn, originKey) + if err != nil || originVal == nil { + return false, nil, err + } + if distinct { + originHandle, err := tablecodec.DecodeHandleInUniqueIndexValue(originVal, isCommon) + if err != nil { + return false, nil, err + } + if originHandle.Equal(curElem.Handle) { + // The key has been deleted. This is not a duplicated key. + return false, nil, nil + } + // The inequality means multiple modifications happened in the same key. + // We use the handle in origin index value to check if the row exists. + recPrefix := tablecodec.GenTableRecordPrefix(tableID) + rowKey := tablecodec.EncodeRecordKey(recPrefix, originHandle) + rowVal, err := getKeyInTxn(ctx, txn, rowKey) + if err != nil || rowVal == nil { + return false, nil, err + } + // The row exists. This is the duplicated key. + return true, originHandle, nil + } + return false, nil, nil + } + // The value in temp index is not the delete marker. + if distinct { + h, err := tablecodec.DecodeHandleInUniqueIndexValue(curElem.Value, isCommon) + return true, h, err + } + return true, nil, nil +} + +// getKeyInTxn gets the value of the key in the transaction, and ignore the ErrNotExist error. +func getKeyInTxn(ctx context.Context, txn kv.Transaction, key kv.Key) ([]byte, error) { + val, err := txn.Get(ctx, key) + if err != nil { + if kv.IsErrNotFound(err) { + return nil, nil + } + return nil, err + } + return val, nil +} + +func (c *index) FetchValues(r []types.Datum, vals []types.Datum) ([]types.Datum, error) { + needLength := len(c.idxInfo.Columns) + if vals == nil || cap(vals) < needLength { + vals = make([]types.Datum, needLength) + } + vals = vals[:needLength] + for i, ic := range c.idxInfo.Columns { + if ic.Offset < 0 || ic.Offset >= len(r) { + return nil, table.ErrIndexOutBound.GenWithStackByArgs(ic.Name, ic.Offset, r) + } + vals[i] = r[ic.Offset] + } + return vals, nil +} + +// FindChangingCol finds the changing column in idxInfo. +func FindChangingCol(cols []*table.Column, idxInfo *model.IndexInfo) *table.Column { + for _, ic := range idxInfo.Columns { + if col := cols[ic.Offset]; col.ChangeStateInfo != nil { + return col + } + } + return nil +} + +// IsIndexWritable check whether the index is writable. +func IsIndexWritable(idx table.Index) bool { + s := idx.Meta().State + if s != model.StateDeleteOnly && s != model.StateDeleteReorganization { + return true + } + return false +} + +// BuildRowcodecColInfoForIndexColumns builds []rowcodec.ColInfo for the given index. +// The result can be used for decoding index key-values. +func BuildRowcodecColInfoForIndexColumns(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) []rowcodec.ColInfo { + colInfo := make([]rowcodec.ColInfo, 0, len(idxInfo.Columns)) + for _, idxCol := range idxInfo.Columns { + col := tblInfo.Columns[idxCol.Offset] + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: col.ID, + IsPKHandle: tblInfo.PKIsHandle && mysql.HasPriKeyFlag(col.GetFlag()), + Ft: rowcodec.FieldTypeFromModelColumn(col), + }) + } + return colInfo +} + +// BuildFieldTypesForIndexColumns builds the index columns field types. +func BuildFieldTypesForIndexColumns(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) []*types.FieldType { + tps := make([]*types.FieldType, 0, len(idxInfo.Columns)) + for _, idxCol := range idxInfo.Columns { + col := tblInfo.Columns[idxCol.Offset] + tps = append(tps, rowcodec.FieldTypeFromModelColumn(col)) + } + return tps +} + +// TryAppendCommonHandleRowcodecColInfos tries to append common handle columns to `colInfo`. +func TryAppendCommonHandleRowcodecColInfos(colInfo []rowcodec.ColInfo, tblInfo *model.TableInfo) []rowcodec.ColInfo { + if !tblInfo.IsCommonHandle || tblInfo.CommonHandleVersion == 0 { + return colInfo + } + if pkIdx := FindPrimaryIndex(tblInfo); pkIdx != nil { + for _, idxCol := range pkIdx.Columns { + col := tblInfo.Columns[idxCol.Offset] + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: col.ID, + Ft: rowcodec.FieldTypeFromModelColumn(col), + }) + } + } + return colInfo +} diff --git a/pkg/table/tables/index_test.go b/pkg/table/tables/index_test.go new file mode 100644 index 0000000000000..2cf4cee9ce5fe --- /dev/null +++ b/pkg/table/tables/index_test.go @@ -0,0 +1,170 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tables_test + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/stretchr/testify/require" +) + +func TestMultiColumnCommonHandle(t *testing.T) { + tblInfo := buildTableInfo(t, "create table t (a int, b int, u varchar(64) unique, nu varchar(64), primary key (a, b), index nu (nu))") + var idxUnique, idxNonUnique table.Index + for _, idxInfo := range tblInfo.Indices { + idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) + if idxInfo.Name.L == "u" { + idxUnique = idx + } else if idxInfo.Name.L == "nu" { + idxNonUnique = idx + } + } + var a, b *model.ColumnInfo + for _, col := range tblInfo.Columns { + if col.Name.String() == "a" { + a = col + } else if col.Name.String() == "b" { + b = col + } + } + require.NotNil(t, a) + require.NotNil(t, b) + + store := testkit.CreateMockStore(t) + txn, err := store.Begin() + require.NoError(t, err) + mockCtx := mock.NewContext() + sc := mockCtx.GetSessionVars().StmtCtx + // create index for "insert t values (3, 2, "abc", "abc") + idxColVals := types.MakeDatums("abc") + handleColVals := types.MakeDatums(3, 2) + encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) + require.NoError(t, err) + commonHandle, err := kv.NewCommonHandle(encodedHandle) + require.NoError(t, err) + _ = idxNonUnique + for _, idx := range []table.Index{idxUnique, idxNonUnique} { + key, _, err := idx.GenIndexKey(sc, idxColVals, commonHandle, nil) + require.NoError(t, err) + _, err = idx.Create(mockCtx, txn, idxColVals, commonHandle, nil) + require.NoError(t, err) + val, err := txn.Get(context.Background(), key) + require.NoError(t, err) + colInfo := tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo) + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: a.ID, + IsPKHandle: false, + Ft: rowcodec.FieldTypeFromModelColumn(a), + }) + colInfo = append(colInfo, rowcodec.ColInfo{ + ID: b.ID, + IsPKHandle: false, + Ft: rowcodec.FieldTypeFromModelColumn(b), + }) + colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, colInfo) + require.NoError(t, err) + require.Len(t, colVals, 3) + _, d, err := codec.DecodeOne(colVals[0]) + require.NoError(t, err) + require.Equal(t, "abc", d.GetString()) + _, d, err = codec.DecodeOne(colVals[1]) + require.NoError(t, err) + require.Equal(t, int64(3), d.GetInt64()) + _, d, err = codec.DecodeOne(colVals[2]) + require.NoError(t, err) + require.Equal(t, int64(2), d.GetInt64()) + handle, err := tablecodec.DecodeIndexHandle(key, val, 1) + require.NoError(t, err) + require.False(t, handle.IsInt()) + require.Equal(t, commonHandle.Encoded(), handle.Encoded()) + } +} + +func TestSingleColumnCommonHandle(t *testing.T) { + tblInfo := buildTableInfo(t, "create table t (a varchar(255) primary key, u int unique, nu int, index nu (nu))") + var idxUnique, idxNonUnique table.Index + for _, idxInfo := range tblInfo.Indices { + idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) + if idxInfo.Name.L == "u" { + idxUnique = idx + } else if idxInfo.Name.L == "nu" { + idxNonUnique = idx + } + } + store := testkit.CreateMockStore(t) + txn, err := store.Begin() + require.NoError(t, err) + + mockCtx := mock.NewContext() + sc := mockCtx.GetSessionVars().StmtCtx + // create index for "insert t values ('abc', 1, 1)" + idxColVals := types.MakeDatums(1) + handleColVals := types.MakeDatums("abc") + encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) + require.NoError(t, err) + commonHandle, err := kv.NewCommonHandle(encodedHandle) + require.NoError(t, err) + + for _, idx := range []table.Index{idxUnique, idxNonUnique} { + key, _, err := idx.GenIndexKey(sc, idxColVals, commonHandle, nil) + require.NoError(t, err) + _, err = idx.Create(mockCtx, txn, idxColVals, commonHandle, nil) + require.NoError(t, err) + val, err := txn.Get(context.Background(), key) + require.NoError(t, err) + colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, + tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) + require.NoError(t, err) + require.Len(t, colVals, 2) + _, d, err := codec.DecodeOne(colVals[0]) + require.NoError(t, err) + require.Equal(t, int64(1), d.GetInt64()) + _, d, err = codec.DecodeOne(colVals[1]) + require.NoError(t, err) + require.Equal(t, "abc", d.GetString()) + handle, err := tablecodec.DecodeIndexHandle(key, val, 1) + require.NoError(t, err) + require.False(t, handle.IsInt()) + require.Equal(t, commonHandle.Encoded(), handle.Encoded()) + + unTouchedVal := append([]byte{1}, val[1:]...) + unTouchedVal = append(unTouchedVal, kv.UnCommitIndexKVFlag) + _, err = tablecodec.DecodeIndexKV(key, unTouchedVal, 1, tablecodec.HandleDefault, + tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) + require.NoError(t, err) + } +} + +func buildTableInfo(t *testing.T, sql string) *model.TableInfo { + stmt, err := parser.New().ParseOneStmt(sql, "", "") + require.NoError(t, err) + tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) + require.NoError(t, err) + return tblInfo +} diff --git a/pkg/table/tables/main_test.go b/pkg/table/tables/main_test.go new file mode 100644 index 0000000000000..eb5437736a86e --- /dev/null +++ b/pkg/table/tables/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tables_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/table/tables/mutation_checker.go b/pkg/table/tables/mutation_checker.go similarity index 97% rename from table/tables/mutation_checker.go rename to pkg/table/tables/mutation_checker.go index 2644d92543fd1..3ecb0b39080c8 100644 --- a/table/tables/mutation_checker.go +++ b/pkg/table/tables/mutation_checker.go @@ -20,18 +20,18 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/rowcodec" "go.uber.org/zap" ) diff --git a/table/tables/mutation_checker_test.go b/pkg/table/tables/mutation_checker_test.go similarity index 95% rename from table/tables/mutation_checker_test.go rename to pkg/table/tables/mutation_checker_test.go index 7c4dd8e140764..bca9cbb53640d 100644 --- a/table/tables/mutation_checker_test.go +++ b/pkg/table/tables/mutation_checker_test.go @@ -19,17 +19,17 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/stretchr/testify/require" ) diff --git a/pkg/table/tables/partition.go b/pkg/table/tables/partition.go new file mode 100644 index 0000000000000..a7af6b55b5239 --- /dev/null +++ b/pkg/table/tables/partition.go @@ -0,0 +1,1902 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package tables + +import ( + "bytes" + "context" + stderr "errors" + "fmt" + "hash/crc32" + "sort" + "strconv" + "strings" + "sync" + + "github.com/google/btree" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/pingcap/tidb/pkg/util/stringutil" + "go.uber.org/zap" +) + +const ( + btreeDegree = 32 +) + +// Both partition and partitionedTable implement the table.Table interface. +var _ table.PhysicalTable = &partition{} +var _ table.Table = &partitionedTable{} + +// partitionedTable implements the table.PartitionedTable interface. +var _ table.PartitionedTable = &partitionedTable{} + +// partition is a feature from MySQL: +// See https://dev.mysql.com/doc/refman/8.0/en/partitioning.html +// A partition table may contain many partitions, each partition has a unique partition +// id. The underlying representation of a partition and a normal table (a table with no +// partitions) is basically the same. +// partition also implements the table.Table interface. +type partition struct { + TableCommon + table *partitionedTable +} + +// GetPhysicalID implements table.Table GetPhysicalID interface. +func (p *partition) GetPhysicalID() int64 { + return p.physicalTableID +} + +// GetPartitionedTable implements table.Table GetPartitionedTable interface. +func (p *partition) GetPartitionedTable() table.PartitionedTable { + return p.table +} + +// GetPartitionedTable implements table.Table GetPartitionedTable interface. +func (t *partitionedTable) GetPartitionedTable() table.PartitionedTable { + return t +} + +// partitionedTable implements the table.PartitionedTable interface. +// partitionedTable is a table, it contains many Partitions. +type partitionedTable struct { + TableCommon + partitionExpr *PartitionExpr + partitions map[int64]*partition + evalBufferTypes []*types.FieldType + evalBufferPool sync.Pool + + // Only used during Reorganize partition + // reorganizePartitions is the currently used partitions that are reorganized + reorganizePartitions map[int64]interface{} + // doubleWritePartitions are the partitions not visible, but we should double write to + doubleWritePartitions map[int64]interface{} + reorgPartitionExpr *PartitionExpr +} + +// TODO: Check which data structures that can be shared between all partitions and which +// needs to be copies +func newPartitionedTable(tbl *TableCommon, tblInfo *model.TableInfo) (table.PartitionedTable, error) { + pi := tblInfo.GetPartitionInfo() + if pi == nil || len(pi.Definitions) == 0 { + return nil, table.ErrUnknownPartition + } + ret := &partitionedTable{TableCommon: *tbl} + partitionExpr, err := newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.Definitions) + if err != nil { + return nil, errors.Trace(err) + } + ret.partitionExpr = partitionExpr + initEvalBufferType(ret) + ret.evalBufferPool = sync.Pool{ + New: func() interface{} { + return initEvalBuffer(ret) + }, + } + if err := initTableIndices(&ret.TableCommon); err != nil { + return nil, errors.Trace(err) + } + partitions := make(map[int64]*partition, len(pi.Definitions)) + for _, p := range pi.Definitions { + var t partition + err := initTableCommonWithIndices(&t.TableCommon, tblInfo, p.ID, tbl.Columns, tbl.allocs, tbl.Constraints) + if err != nil { + return nil, errors.Trace(err) + } + t.table = ret + partitions[p.ID] = &t + } + ret.partitions = partitions + // In StateWriteReorganization we are using the 'old' partition definitions + // and if any new change happens in DroppingDefinitions, it needs to be done + // also in AddingDefinitions (with new evaluation of the new expression) + // In StateDeleteReorganization we are using the 'new' partition definitions + // and if any new change happens in AddingDefinitions, it needs to be done + // also in DroppingDefinitions (since session running on schema version -1) + // should also see the changes + if pi.DDLState == model.StateDeleteReorganization { + origIdx := setIndexesState(ret, pi.DDLState) + defer unsetIndexesState(ret, origIdx) + // TODO: Explicitly explain the different DDL/New fields! + if pi.NewTableID != 0 { + ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.DDLType, pi.DDLExpr, pi.DDLColumns, pi.DroppingDefinitions) + } else { + ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.DroppingDefinitions) + } + if err != nil { + return nil, errors.Trace(err) + } + ret.reorganizePartitions = make(map[int64]interface{}, len(pi.AddingDefinitions)) + for _, def := range pi.AddingDefinitions { + ret.reorganizePartitions[def.ID] = nil + } + ret.doubleWritePartitions = make(map[int64]interface{}, len(pi.DroppingDefinitions)) + for _, def := range pi.DroppingDefinitions { + p, err := initPartition(ret, def) + if err != nil { + return nil, err + } + partitions[def.ID] = p + ret.doubleWritePartitions[def.ID] = nil + } + } else { + if len(pi.AddingDefinitions) > 0 { + origIdx := setIndexesState(ret, pi.DDLState) + defer unsetIndexesState(ret, origIdx) + if pi.NewTableID != 0 { + // REMOVE PARTITIONING or PARTITION BY + ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.DDLType, pi.DDLExpr, pi.DDLColumns, pi.AddingDefinitions) + } else { + // REORGANIZE PARTITION + ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.AddingDefinitions) + } + if err != nil { + return nil, errors.Trace(err) + } + ret.doubleWritePartitions = make(map[int64]interface{}, len(pi.AddingDefinitions)) + for _, def := range pi.AddingDefinitions { + ret.doubleWritePartitions[def.ID] = nil + p, err := initPartition(ret, def) + if err != nil { + return nil, err + } + partitions[def.ID] = p + } + } + if len(pi.DroppingDefinitions) > 0 { + ret.reorganizePartitions = make(map[int64]interface{}, len(pi.DroppingDefinitions)) + for _, def := range pi.DroppingDefinitions { + ret.reorganizePartitions[def.ID] = nil + } + } + } + return ret, nil +} + +func setIndexesState(t *partitionedTable, state model.SchemaState) []*model.IndexInfo { + orig := t.meta.Indices + t.meta.Indices = make([]*model.IndexInfo, 0, len(orig)) + for i := range orig { + t.meta.Indices = append(t.meta.Indices, orig[i].Clone()) + if t.meta.Indices[i].State == model.StatePublic { + switch state { + case model.StateDeleteOnly, model.StateNone: + t.meta.Indices[i].State = model.StateDeleteOnly + case model.StatePublic: + // Keep as is + default: + // use the 'StateWriteReorganization' here, since StateDeleteReorganization + // would skip index writes. + t.meta.Indices[i].State = model.StateWriteReorganization + } + } + } + return orig +} + +func unsetIndexesState(t *partitionedTable, orig []*model.IndexInfo) { + t.meta.Indices = orig +} + +func initPartition(t *partitionedTable, def model.PartitionDefinition) (*partition, error) { + var newPart partition + err := initTableCommonWithIndices(&newPart.TableCommon, t.meta, def.ID, t.Columns, t.allocs, t.Constraints) + if err != nil { + return nil, err + } + newPart.table = t + return &newPart, nil +} + +func newPartitionExpr(tblInfo *model.TableInfo, tp model.PartitionType, expr string, partCols []model.CIStr, defs []model.PartitionDefinition) (*PartitionExpr, error) { + // a partitioned table cannot rely on session context/sql modes, so use a default one! + ctx := mock.NewContext() + dbName := model.NewCIStr(ctx.GetSessionVars().CurrentDB) + columns, names, err := expression.ColumnInfos2ColumnsAndNames(ctx, dbName, tblInfo.Name, tblInfo.Cols(), tblInfo) + if err != nil { + return nil, err + } + switch tp { + case model.PartitionTypeNone: + // Nothing to do + return nil, nil + case model.PartitionTypeRange: + return generateRangePartitionExpr(ctx, expr, partCols, defs, columns, names) + case model.PartitionTypeHash: + return generateHashPartitionExpr(ctx, expr, columns, names) + case model.PartitionTypeKey: + return generateKeyPartitionExpr(ctx, expr, partCols, columns, names) + case model.PartitionTypeList: + return generateListPartitionExpr(ctx, tblInfo, expr, partCols, defs, columns, names) + } + panic("cannot reach here") +} + +// PartitionExpr is the partition definition expressions. +type PartitionExpr struct { + // UpperBounds: (x < y1); (x < y2); (x < y3), used by locatePartition. + UpperBounds []expression.Expression + // OrigExpr is the partition expression ast used in point get. + OrigExpr ast.ExprNode + // Expr is the hash partition expression. + Expr expression.Expression + // Used in the key partition + *ForKeyPruning + // Used in the range pruning process. + *ForRangePruning + // Used in the range column pruning process. + *ForRangeColumnsPruning + // ColOffset is the offsets of partition columns. + ColumnOffset []int + *ForListPruning +} + +// GetPartColumnsForKeyPartition is used to get partition columns for key partition table +func (pe *PartitionExpr) GetPartColumnsForKeyPartition(columns []*expression.Column) ([]*expression.Column, []int) { + schema := expression.NewSchema(columns...) + partCols := make([]*expression.Column, len(pe.ColumnOffset)) + colLen := make([]int, 0, len(pe.ColumnOffset)) + for i, offset := range pe.ColumnOffset { + partCols[i] = schema.Columns[offset] + partCols[i].Index = i + colLen = append(colLen, partCols[i].RetType.GetFlen()) + } + return partCols, colLen +} + +// LocateKeyPartition is the common interface used to locate the destination partition +func (kp *ForKeyPruning) LocateKeyPartition(numParts uint64, r []types.Datum) (int, error) { + h := crc32.NewIEEE() + for _, col := range kp.KeyPartCols { + val := r[col.Index] + if val.Kind() == types.KindNull { + h.Write([]byte{0}) + } else { + data, err := val.ToHashKey() + if err != nil { + return 0, err + } + h.Write(data) + } + } + return int(h.Sum32() % uint32(numParts)), nil +} + +func initEvalBufferType(t *partitionedTable) { + hasExtraHandle := false + numCols := len(t.Cols()) + if !t.Meta().PKIsHandle { + hasExtraHandle = true + numCols++ + } + t.evalBufferTypes = make([]*types.FieldType, numCols) + for i, col := range t.Cols() { + t.evalBufferTypes[i] = &col.FieldType + } + + if hasExtraHandle { + t.evalBufferTypes[len(t.evalBufferTypes)-1] = types.NewFieldType(mysql.TypeLonglong) + } +} + +func initEvalBuffer(t *partitionedTable) *chunk.MutRow { + evalBuffer := chunk.MutRowFromTypes(t.evalBufferTypes) + return &evalBuffer +} + +// ForRangeColumnsPruning is used for range partition pruning. +type ForRangeColumnsPruning struct { + // LessThan contains expressions for [Partition][column]. + // If Maxvalue, then nil + LessThan [][]*expression.Expression +} + +func dataForRangeColumnsPruning(ctx sessionctx.Context, defs []model.PartitionDefinition, schema *expression.Schema, names []*types.FieldName, p *parser.Parser, colOffsets []int) (*ForRangeColumnsPruning, error) { + var res ForRangeColumnsPruning + res.LessThan = make([][]*expression.Expression, 0, len(defs)) + for i := 0; i < len(defs); i++ { + lessThanCols := make([]*expression.Expression, 0, len(defs[i].LessThan)) + for j := range defs[i].LessThan { + if strings.EqualFold(defs[i].LessThan[j], "MAXVALUE") { + // Use a nil pointer instead of math.MaxInt64 to avoid the corner cases. + lessThanCols = append(lessThanCols, nil) + // No column after MAXVALUE matters + break + } + tmp, err := parseSimpleExprWithNames(p, ctx, defs[i].LessThan[j], schema, names) + if err != nil { + return nil, err + } + _, ok := tmp.(*expression.Constant) + if !ok { + return nil, dbterror.ErrPartitionConstDomain + } + // TODO: Enable this for all types! + // Currently it will trigger changes for collation differences + switch schema.Columns[colOffsets[j]].RetType.GetType() { + case mysql.TypeDatetime, mysql.TypeDate: + // Will also fold constant + tmp = expression.BuildCastFunction(ctx, tmp, schema.Columns[colOffsets[j]].RetType) + } + lessThanCols = append(lessThanCols, &tmp) + } + res.LessThan = append(res.LessThan, lessThanCols) + } + return &res, nil +} + +// parseSimpleExprWithNames parses simple expression string to Expression. +// The expression string must only reference the column in the given NameSlice. +func parseSimpleExprWithNames(p *parser.Parser, ctx sessionctx.Context, exprStr string, schema *expression.Schema, names types.NameSlice) (expression.Expression, error) { + exprNode, err := parseExpr(p, exprStr) + if err != nil { + return nil, errors.Trace(err) + } + return expression.RewriteSimpleExprWithNames(ctx, exprNode, schema, names) +} + +// ForKeyPruning is used for key partition pruning. +type ForKeyPruning struct { + KeyPartCols []*expression.Column +} + +// ForListPruning is used for list partition pruning. +type ForListPruning struct { + // LocateExpr uses to locate list partition by row. + LocateExpr expression.Expression + // PruneExpr uses to prune list partition in partition pruner. + PruneExpr expression.Expression + // PruneExprCols is the columns of PruneExpr, it has removed the duplicate columns. + PruneExprCols []*expression.Column + // valueMap is column value -> partition idx, uses to locate list partition. + valueMap map[int64]int + // nullPartitionIdx is the partition idx for null value. + nullPartitionIdx int + // defaultPartitionIdx is the partition idx for default value/fallback. + defaultPartitionIdx int + + // For list columns partition pruning + ColPrunes []*ForListColumnPruning +} + +// btreeListColumnItem is BTree's Item that uses string to compare. +type btreeListColumnItem struct { + key string + location ListPartitionLocation +} + +func newBtreeListColumnItem(key string, location ListPartitionLocation) *btreeListColumnItem { + return &btreeListColumnItem{ + key: key, + location: location, + } +} + +func newBtreeListColumnSearchItem(key string) *btreeListColumnItem { + return &btreeListColumnItem{ + key: key, + } +} + +func (item *btreeListColumnItem) Less(other btree.Item) bool { + return item.key < other.(*btreeListColumnItem).key +} + +func lessBtreeListColumnItem(a, b *btreeListColumnItem) bool { + return a.key < b.key +} + +// ForListColumnPruning is used for list columns partition pruning. +type ForListColumnPruning struct { + ExprCol *expression.Column + valueTp *types.FieldType + valueMap map[string]ListPartitionLocation + sorted *btree.BTreeG[*btreeListColumnItem] + + // To deal with the location partition failure caused by inconsistent NewCollationEnabled values(see issue #32416). + // The following fields are used to delay building valueMap. + ctx sessionctx.Context + tblInfo *model.TableInfo + schema *expression.Schema + names types.NameSlice + colIdx int + + // catch-all partition / DEFAULT + defaultPartID int64 +} + +// ListPartitionGroup indicate the group index of the column value in a partition. +type ListPartitionGroup struct { + // Such as: list columns (a,b) (partition p0 values in ((1,5),(1,6))); + // For the column a which value is 1, the ListPartitionGroup is: + // ListPartitionGroup { + // PartIdx: 0, // 0 is the partition p0 index in all partitions. + // GroupIdxs: []int{0,1}, // p0 has 2 value group: (1,5) and (1,6), and they both contain the column a where value is 1; + // } // the value of GroupIdxs `0,1` is the index of the value group that contain the column a which value is 1. + PartIdx int + GroupIdxs []int +} + +// ListPartitionLocation indicate the partition location for the column value in list columns partition. +// Here is an example: +// Suppose the list columns partition is: list columns (a,b) (partition p0 values in ((1,5),(1,6)), partition p1 values in ((1,7),(9,9))); +// How to express the location of the column a which value is 1? +// For the column a which value is 1, both partition p0 and p1 contain the column a which value is 1. +// In partition p0, both value group0 (1,5) and group1 (1,6) are contain the column a which value is 1. +// In partition p1, value group0 (1,7) contains the column a which value is 1. +// So, the ListPartitionLocation of column a which value is 1 is: +// +// []ListPartitionGroup{ +// { +// PartIdx: 0, // `0` is the partition p0 index in all partitions. +// GroupIdxs: []int{0, 1} // `0,1` is the index of the value group0, group1. +// }, +// { +// PartIdx: 1, // `1` is the partition p1 index in all partitions. +// GroupIdxs: []int{0} // `0` is the index of the value group0. +// }, +// } +type ListPartitionLocation []ListPartitionGroup + +// IsEmpty returns true if the ListPartitionLocation is empty. +func (ps ListPartitionLocation) IsEmpty() bool { + for _, pg := range ps { + if len(pg.GroupIdxs) > 0 { + return false + } + } + return true +} + +func (ps ListPartitionLocation) findByPartitionIdx(partIdx int) int { + for i, p := range ps { + if p.PartIdx == partIdx { + return i + } + } + return -1 +} + +type listPartitionLocationHelper struct { + initialized bool + location ListPartitionLocation +} + +// NewListPartitionLocationHelper returns a new listPartitionLocationHelper. +func NewListPartitionLocationHelper() *listPartitionLocationHelper { + return &listPartitionLocationHelper{} +} + +// GetLocation gets the list partition location. +func (p *listPartitionLocationHelper) GetLocation() ListPartitionLocation { + return p.location +} + +// UnionPartitionGroup unions with the list-partition-value-group. +func (p *listPartitionLocationHelper) UnionPartitionGroup(pg ListPartitionGroup) { + idx := p.location.findByPartitionIdx(pg.PartIdx) + if idx < 0 { + // copy the group idx. + groupIdxs := make([]int, len(pg.GroupIdxs)) + copy(groupIdxs, pg.GroupIdxs) + p.location = append(p.location, ListPartitionGroup{ + PartIdx: pg.PartIdx, + GroupIdxs: groupIdxs, + }) + return + } + p.location[idx].union(pg) +} + +// Union unions with the other location. +func (p *listPartitionLocationHelper) Union(location ListPartitionLocation) { + for _, pg := range location { + p.UnionPartitionGroup(pg) + } +} + +// Intersect intersect with other location. +func (p *listPartitionLocationHelper) Intersect(location ListPartitionLocation) bool { + if !p.initialized { + p.initialized = true + p.location = make([]ListPartitionGroup, 0, len(location)) + p.location = append(p.location, location...) + return true + } + currPgs := p.location + remainPgs := make([]ListPartitionGroup, 0, len(location)) + for _, pg := range location { + idx := currPgs.findByPartitionIdx(pg.PartIdx) + if idx < 0 { + continue + } + if !currPgs[idx].intersect(pg) { + continue + } + remainPgs = append(remainPgs, currPgs[idx]) + } + p.location = remainPgs + return len(remainPgs) > 0 +} + +func (pg *ListPartitionGroup) intersect(otherPg ListPartitionGroup) bool { + if pg.PartIdx != otherPg.PartIdx { + return false + } + var groupIdxs []int + for _, gidx := range otherPg.GroupIdxs { + if pg.findGroupIdx(gidx) { + groupIdxs = append(groupIdxs, gidx) + } + } + pg.GroupIdxs = groupIdxs + return len(groupIdxs) > 0 +} + +func (pg *ListPartitionGroup) union(otherPg ListPartitionGroup) { + if pg.PartIdx != otherPg.PartIdx { + return + } + pg.GroupIdxs = append(pg.GroupIdxs, otherPg.GroupIdxs...) +} + +func (pg *ListPartitionGroup) findGroupIdx(groupIdx int) bool { + for _, gidx := range pg.GroupIdxs { + if gidx == groupIdx { + return true + } + } + return false +} + +// ForRangePruning is used for range partition pruning. +type ForRangePruning struct { + LessThan []int64 + MaxValue bool + Unsigned bool +} + +// dataForRangePruning extracts the less than parts from 'partition p0 less than xx ... partition p1 less than ...' +func dataForRangePruning(sctx sessionctx.Context, defs []model.PartitionDefinition) (*ForRangePruning, error) { + var maxValue bool + var unsigned bool + lessThan := make([]int64, len(defs)) + for i := 0; i < len(defs); i++ { + if strings.EqualFold(defs[i].LessThan[0], "MAXVALUE") { + // Use a bool flag instead of math.MaxInt64 to avoid the corner cases. + maxValue = true + } else { + var err error + lessThan[i], err = strconv.ParseInt(defs[i].LessThan[0], 10, 64) + var numErr *strconv.NumError + if stderr.As(err, &numErr) && numErr.Err == strconv.ErrRange { + var tmp uint64 + tmp, err = strconv.ParseUint(defs[i].LessThan[0], 10, 64) + lessThan[i] = int64(tmp) + unsigned = true + } + if err != nil { + val, ok := fixOldVersionPartitionInfo(sctx, defs[i].LessThan[0]) + if !ok { + logutil.BgLogger().Error("wrong partition definition", zap.String("less than", defs[i].LessThan[0])) + return nil, errors.WithStack(err) + } + lessThan[i] = val + } + } + } + return &ForRangePruning{ + LessThan: lessThan, + MaxValue: maxValue, + Unsigned: unsigned, + }, nil +} + +func fixOldVersionPartitionInfo(sctx sessionctx.Context, str string) (int64, bool) { + // less than value should be calculate to integer before persistent. + // Old version TiDB may not do it and store the raw expression. + tmp, err := parseSimpleExprWithNames(parser.New(), sctx, str, nil, nil) + if err != nil { + return 0, false + } + ret, isNull, err := tmp.EvalInt(sctx, chunk.Row{}) + if err != nil || isNull { + return 0, false + } + return ret, true +} + +func rangePartitionExprStrings(cols []model.CIStr, expr string) []string { + var s []string + if len(cols) > 0 { + s = make([]string, 0, len(cols)) + for _, col := range cols { + s = append(s, stringutil.Escape(col.O, mysql.ModeNone)) + } + } else { + s = []string{expr} + } + return s +} + +func generateKeyPartitionExpr(ctx sessionctx.Context, expr string, partCols []model.CIStr, + columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { + ret := &PartitionExpr{ + ForKeyPruning: &ForKeyPruning{}, + } + _, partColumns, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) + if err != nil { + return nil, errors.Trace(err) + } + ret.ColumnOffset = offset + ret.KeyPartCols = partColumns + + return ret, nil +} + +func generateRangePartitionExpr(ctx sessionctx.Context, expr string, partCols []model.CIStr, + defs []model.PartitionDefinition, columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { + // The caller should assure partition info is not nil. + p := parser.New() + schema := expression.NewSchema(columns...) + partStrs := rangePartitionExprStrings(partCols, expr) + locateExprs, err := getRangeLocateExprs(ctx, p, defs, partStrs, schema, names) + if err != nil { + return nil, errors.Trace(err) + } + ret := &PartitionExpr{ + UpperBounds: locateExprs, + } + + partExpr, _, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) + if err != nil { + return nil, errors.Trace(err) + } + ret.ColumnOffset = offset + + if len(partCols) < 1 { + tmp, err := dataForRangePruning(ctx, defs) + if err != nil { + return nil, errors.Trace(err) + } + ret.Expr = partExpr + ret.ForRangePruning = tmp + } else { + tmp, err := dataForRangeColumnsPruning(ctx, defs, schema, names, p, offset) + if err != nil { + return nil, errors.Trace(err) + } + ret.ForRangeColumnsPruning = tmp + } + return ret, nil +} + +func getRangeLocateExprs(ctx sessionctx.Context, p *parser.Parser, defs []model.PartitionDefinition, partStrs []string, schema *expression.Schema, names types.NameSlice) ([]expression.Expression, error) { + var buf bytes.Buffer + locateExprs := make([]expression.Expression, 0, len(defs)) + for i := 0; i < len(defs); i++ { + if strings.EqualFold(defs[i].LessThan[0], "MAXVALUE") { + // Expr less than maxvalue is always true. + fmt.Fprintf(&buf, "true") + } else { + maxValueFound := false + for j := range partStrs[1:] { + if strings.EqualFold(defs[i].LessThan[j+1], "MAXVALUE") { + // if any column will be less than MAXVALUE, so change < to <= of the previous prefix of columns + fmt.Fprintf(&buf, "((%s) <= (%s))", strings.Join(partStrs[:j+1], ","), strings.Join(defs[i].LessThan[:j+1], ",")) + maxValueFound = true + break + } + } + if !maxValueFound { + fmt.Fprintf(&buf, "((%s) < (%s))", strings.Join(partStrs, ","), strings.Join(defs[i].LessThan, ",")) + } + } + + expr, err := parseSimpleExprWithNames(p, ctx, buf.String(), schema, names) + if err != nil { + // If it got an error here, ddl may hang forever, so this error log is important. + logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", buf.String()), zap.Error(err)) + return nil, errors.Trace(err) + } + locateExprs = append(locateExprs, expr) + buf.Reset() + } + return locateExprs, nil +} + +func getColumnsOffset(cols, columns []*expression.Column) []int { + colsOffset := make([]int, len(cols)) + for i, col := range columns { + if idx := findIdxByColUniqueID(cols, col); idx >= 0 { + colsOffset[idx] = i + } + } + return colsOffset +} + +func findIdxByColUniqueID(cols []*expression.Column, col *expression.Column) int { + for idx, c := range cols { + if c.UniqueID == col.UniqueID { + return idx + } + } + return -1 +} + +func extractPartitionExprColumns(ctx sessionctx.Context, expr string, partCols []model.CIStr, columns []*expression.Column, names types.NameSlice) (expression.Expression, []*expression.Column, []int, error) { + var cols []*expression.Column + var partExpr expression.Expression + if len(partCols) == 0 { + schema := expression.NewSchema(columns...) + exprs, err := expression.ParseSimpleExprsWithNames(ctx, expr, schema, names) + if err != nil { + return nil, nil, nil, err + } + cols = expression.ExtractColumns(exprs[0]) + partExpr = exprs[0] + } else { + for _, col := range partCols { + idx := expression.FindFieldNameIdxByColName(names, col.L) + if idx < 0 { + panic("should never happen") + } + cols = append(cols, columns[idx]) + } + } + offset := getColumnsOffset(cols, columns) + deDupCols := make([]*expression.Column, 0, len(cols)) + for _, col := range cols { + if findIdxByColUniqueID(deDupCols, col) < 0 { + c := col.Clone().(*expression.Column) + deDupCols = append(deDupCols, c) + } + } + return partExpr, deDupCols, offset, nil +} + +func generateListPartitionExpr(ctx sessionctx.Context, tblInfo *model.TableInfo, expr string, partCols []model.CIStr, + defs []model.PartitionDefinition, columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { + // The caller should assure partition info is not nil. + partExpr, exprCols, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) + if err != nil { + return nil, err + } + listPrune := &ForListPruning{} + if len(partCols) == 0 { + err = listPrune.buildListPruner(ctx, expr, defs, exprCols, columns, names) + } else { + err = listPrune.buildListColumnsPruner(ctx, tblInfo, partCols, defs, columns, names) + } + if err != nil { + return nil, err + } + ret := &PartitionExpr{ + ForListPruning: listPrune, + ColumnOffset: offset, + Expr: partExpr, + } + return ret, nil +} + +// Clone a copy of ForListPruning +func (lp *ForListPruning) Clone() *ForListPruning { + ret := *lp + if ret.LocateExpr != nil { + ret.LocateExpr = lp.LocateExpr.Clone() + } + if ret.PruneExpr != nil { + ret.PruneExpr = lp.PruneExpr.Clone() + } + ret.PruneExprCols = make([]*expression.Column, 0, len(lp.PruneExprCols)) + for i := range lp.PruneExprCols { + c := lp.PruneExprCols[i].Clone().(*expression.Column) + ret.PruneExprCols = append(ret.PruneExprCols, c) + } + ret.ColPrunes = make([]*ForListColumnPruning, 0, len(lp.ColPrunes)) + for i := range lp.ColPrunes { + l := *lp.ColPrunes[i] + l.ExprCol = l.ExprCol.Clone().(*expression.Column) + ret.ColPrunes = append(ret.ColPrunes, &l) + } + return &ret +} + +func (lp *ForListPruning) buildListPruner(ctx sessionctx.Context, exprStr string, defs []model.PartitionDefinition, exprCols []*expression.Column, + columns []*expression.Column, names types.NameSlice) error { + schema := expression.NewSchema(columns...) + p := parser.New() + expr, err := parseSimpleExprWithNames(p, ctx, exprStr, schema, names) + if err != nil { + // If it got an error here, ddl may hang forever, so this error log is important. + logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", exprStr), zap.Error(err)) + return errors.Trace(err) + } + // Since need to change the column index of the expression, clone the expression first. + lp.LocateExpr = expr.Clone() + lp.PruneExprCols = exprCols + lp.PruneExpr = expr.Clone() + cols := expression.ExtractColumns(lp.PruneExpr) + for _, c := range cols { + idx := findIdxByColUniqueID(exprCols, c) + if idx < 0 { + return table.ErrUnknownColumn.GenWithStackByArgs(c.OrigName) + } + c.Index = idx + } + err = lp.buildListPartitionValueMap(ctx, defs, schema, names, p) + if err != nil { + return err + } + return nil +} + +func (lp *ForListPruning) buildListColumnsPruner(ctx sessionctx.Context, + tblInfo *model.TableInfo, partCols []model.CIStr, defs []model.PartitionDefinition, + columns []*expression.Column, names types.NameSlice) error { + schema := expression.NewSchema(columns...) + p := parser.New() + colPrunes := make([]*ForListColumnPruning, 0, len(partCols)) + lp.defaultPartitionIdx = -1 + for colIdx := range partCols { + colInfo := model.FindColumnInfo(tblInfo.Columns, partCols[colIdx].L) + if colInfo == nil { + return table.ErrUnknownColumn.GenWithStackByArgs(partCols[colIdx].L) + } + idx := expression.FindFieldNameIdxByColName(names, partCols[colIdx].L) + if idx < 0 { + return table.ErrUnknownColumn.GenWithStackByArgs(partCols[colIdx].L) + } + colPrune := &ForListColumnPruning{ + ctx: ctx, + tblInfo: tblInfo, + schema: schema, + names: names, + colIdx: colIdx, + ExprCol: columns[idx], + valueTp: &colInfo.FieldType, + valueMap: make(map[string]ListPartitionLocation), + sorted: btree.NewG[*btreeListColumnItem](btreeDegree, lessBtreeListColumnItem), + } + err := colPrune.buildPartitionValueMapAndSorted(p, defs) + if err != nil { + return err + } + if colPrune.defaultPartID > 0 { + for i := range defs { + if defs[i].ID == colPrune.defaultPartID { + if lp.defaultPartitionIdx >= 0 && i != lp.defaultPartitionIdx { + // Should be same for all columns, i.e. should never happen! + return table.ErrUnknownPartition + } + lp.defaultPartitionIdx = i + } + } + } + colPrunes = append(colPrunes, colPrune) + } + lp.ColPrunes = colPrunes + return nil +} + +// buildListPartitionValueMap builds list partition value map. +// The map is column value -> partition index. +// colIdx is the column index in the list columns. +func (lp *ForListPruning) buildListPartitionValueMap(ctx sessionctx.Context, defs []model.PartitionDefinition, + schema *expression.Schema, names types.NameSlice, p *parser.Parser) error { + lp.valueMap = map[int64]int{} + lp.nullPartitionIdx = -1 + lp.defaultPartitionIdx = -1 + for partitionIdx, def := range defs { + for _, vs := range def.InValues { + if strings.EqualFold(vs[0], "DEFAULT") { + lp.defaultPartitionIdx = partitionIdx + continue + } + expr, err := parseSimpleExprWithNames(p, ctx, vs[0], schema, names) + if err != nil { + return errors.Trace(err) + } + v, isNull, err := expr.EvalInt(ctx, chunk.Row{}) + if err != nil { + return errors.Trace(err) + } + if isNull { + lp.nullPartitionIdx = partitionIdx + continue + } + lp.valueMap[v] = partitionIdx + } + } + return nil +} + +// LocatePartition locates partition by the column value +func (lp *ForListPruning) LocatePartition(value int64, isNull bool) int { + if isNull { + if lp.nullPartitionIdx >= 0 { + return lp.nullPartitionIdx + } + return lp.defaultPartitionIdx + } + partitionIdx, ok := lp.valueMap[value] + if !ok { + return lp.defaultPartitionIdx + } + return partitionIdx +} + +func (lp *ForListPruning) locateListPartitionByRow(ctx sessionctx.Context, r []types.Datum) (int, error) { + value, isNull, err := lp.LocateExpr.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) + if err != nil { + return -1, errors.Trace(err) + } + idx := lp.LocatePartition(value, isNull) + if idx >= 0 { + return idx, nil + } + if isNull { + return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs("NULL") + } + var valueMsg string + if mysql.HasUnsignedFlag(lp.LocateExpr.GetType().GetFlag()) { + // Handle unsigned value + valueMsg = fmt.Sprintf("%d", uint64(value)) + } else { + valueMsg = fmt.Sprintf("%d", value) + } + return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) +} + +func (lp *ForListPruning) locateListColumnsPartitionByRow(ctx sessionctx.Context, r []types.Datum) (int, error) { + helper := NewListPartitionLocationHelper() + sc := ctx.GetSessionVars().StmtCtx + for _, colPrune := range lp.ColPrunes { + location, err := colPrune.LocatePartition(sc, r[colPrune.ExprCol.Index]) + if err != nil { + return -1, errors.Trace(err) + } + if !helper.Intersect(location) { + break + } + } + location := helper.GetLocation() + if location.IsEmpty() { + if lp.defaultPartitionIdx >= 0 { + return lp.defaultPartitionIdx, nil + } + return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs("from column_list") + } + return location[0].PartIdx, nil +} + +// GetDefaultIdx return the Default partitions index. +func (lp *ForListPruning) GetDefaultIdx() int { + return lp.defaultPartitionIdx +} + +// buildPartitionValueMapAndSorted builds list columns partition value map for the specified column. +// It also builds list columns partition value btree for the specified column. +// colIdx is the specified column index in the list columns. +func (lp *ForListColumnPruning) buildPartitionValueMapAndSorted(p *parser.Parser, + defs []model.PartitionDefinition) error { + l := len(lp.valueMap) + if l != 0 { + return nil + } + + return lp.buildListPartitionValueMapAndSorted(p, defs) +} + +// HasDefault return true if the partition has the DEFAULT value +func (lp *ForListColumnPruning) HasDefault() bool { + return lp.defaultPartID > 0 +} + +// RebuildPartitionValueMapAndSorted rebuilds list columns partition value map for the specified column. +func (lp *ForListColumnPruning) RebuildPartitionValueMapAndSorted(p *parser.Parser, + defs []model.PartitionDefinition) error { + lp.valueMap = make(map[string]ListPartitionLocation, len(lp.valueMap)) + lp.sorted.Clear(false) + return lp.buildListPartitionValueMapAndSorted(p, defs) +} + +func (lp *ForListColumnPruning) buildListPartitionValueMapAndSorted(p *parser.Parser, defs []model.PartitionDefinition) error { + sc := lp.ctx.GetSessionVars().StmtCtx +DEFS: + for partitionIdx, def := range defs { + for groupIdx, vs := range def.InValues { + if len(vs) == 1 && vs[0] == "DEFAULT" { + lp.defaultPartID = def.ID + continue DEFS + } + keyBytes, err := lp.genConstExprKey(lp.ctx, sc, vs[lp.colIdx], lp.schema, lp.names, p) + if err != nil { + return errors.Trace(err) + } + key := string(keyBytes) + location, ok := lp.valueMap[key] + if ok { + idx := location.findByPartitionIdx(partitionIdx) + if idx != -1 { + location[idx].GroupIdxs = append(location[idx].GroupIdxs, groupIdx) + continue + } + } + location = append(location, ListPartitionGroup{ + PartIdx: partitionIdx, + GroupIdxs: []int{groupIdx}, + }) + lp.valueMap[key] = location + lp.sorted.ReplaceOrInsert(newBtreeListColumnItem(key, location)) + } + } + return nil +} + +func (lp *ForListColumnPruning) genConstExprKey(ctx sessionctx.Context, sc *stmtctx.StatementContext, exprStr string, + schema *expression.Schema, names types.NameSlice, p *parser.Parser) ([]byte, error) { + expr, err := parseSimpleExprWithNames(p, ctx, exprStr, schema, names) + if err != nil { + return nil, errors.Trace(err) + } + v, err := expr.Eval(chunk.Row{}) + if err != nil { + return nil, errors.Trace(err) + } + key, err := lp.genKey(sc, v) + if err != nil { + return nil, errors.Trace(err) + } + return key, nil +} + +func (lp *ForListColumnPruning) genKey(sc *stmtctx.StatementContext, v types.Datum) ([]byte, error) { + v, err := v.ConvertTo(sc, lp.valueTp) + if err != nil { + return nil, errors.Trace(err) + } + valByte, err := codec.EncodeKey(sc, nil, v) + return valByte, err +} + +// LocatePartition locates partition by the column value +func (lp *ForListColumnPruning) LocatePartition(sc *stmtctx.StatementContext, v types.Datum) (ListPartitionLocation, error) { + key, err := lp.genKey(sc, v) + if err != nil { + return nil, errors.Trace(err) + } + location, ok := lp.valueMap[string(key)] + if !ok { + return nil, nil + } + return location, nil +} + +// LocateRanges locates partition ranges by the column range +func (lp *ForListColumnPruning) LocateRanges(sc *stmtctx.StatementContext, r *ranger.Range, defaultPartIdx int) ([]ListPartitionLocation, error) { + var lowKey, highKey []byte + var err error + lowVal := r.LowVal[0] + if r.LowVal[0].Kind() == types.KindMinNotNull { + lowVal = types.GetMinValue(lp.ExprCol.GetType()) + } + highVal := r.HighVal[0] + if r.HighVal[0].Kind() == types.KindMaxValue { + highVal = types.GetMaxValue(lp.ExprCol.GetType()) + } + + // For string type, values returned by GetMinValue and GetMaxValue are already encoded, + // so it's unnecessary to invoke genKey to encode them. + if lp.ExprCol.GetType().EvalType() == types.ETString && r.LowVal[0].Kind() == types.KindMinNotNull { + lowKey = (&lowVal).GetBytes() + } else { + lowKey, err = lp.genKey(sc, lowVal) + if err != nil { + return nil, errors.Trace(err) + } + } + + if lp.ExprCol.GetType().EvalType() == types.ETString && r.HighVal[0].Kind() == types.KindMaxValue { + highKey = (&highVal).GetBytes() + } else { + highKey, err = lp.genKey(sc, highVal) + if err != nil { + return nil, errors.Trace(err) + } + } + + if r.LowExclude { + lowKey = kv.Key(lowKey).PrefixNext() + } + if !r.HighExclude { + highKey = kv.Key(highKey).PrefixNext() + } + + locations := make([]ListPartitionLocation, 0, lp.sorted.Len()) + lp.sorted.AscendRange(newBtreeListColumnSearchItem(string(hack.String(lowKey))), newBtreeListColumnSearchItem(string(hack.String(highKey))), func(item *btreeListColumnItem) bool { + locations = append(locations, item.location) + return true + }) + if lp.HasDefault() { + // Add the default partition since there may be a gap + // between the conditions range and the LIST COLUMNS values + locations = append(locations, ListPartitionLocation{ + ListPartitionGroup{ + PartIdx: defaultPartIdx, + GroupIdxs: []int{-1}, // Special group! + }, + }) + } + return locations, nil +} + +func generateHashPartitionExpr(ctx sessionctx.Context, exprStr string, + columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { + // The caller should assure partition info is not nil. + schema := expression.NewSchema(columns...) + origExpr, err := parseExpr(parser.New(), exprStr) + if err != nil { + return nil, err + } + exprs, err := rewritePartitionExpr(ctx, origExpr, schema, names) + if err != nil { + // If it got an error here, ddl may hang forever, so this error log is important. + logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", exprStr), zap.Error(err)) + return nil, errors.Trace(err) + } + // build column offset. + partitionCols := expression.ExtractColumns(exprs) + offset := make([]int, len(partitionCols)) + for i, col := range columns { + for j, partitionCol := range partitionCols { + if partitionCol.UniqueID == col.UniqueID { + offset[j] = i + } + } + } + exprs.HashCode(ctx.GetSessionVars().StmtCtx) + return &PartitionExpr{ + Expr: exprs, + OrigExpr: origExpr, + ColumnOffset: offset, + }, nil +} + +// PartitionExpr returns the partition expression. +func (t *partitionedTable) PartitionExpr() *PartitionExpr { + return t.partitionExpr +} + +func (t *partitionedTable) GetPartitionColumnIDs() []int64 { + // PARTITION BY {LIST|RANGE} COLUMNS uses columns directly without expressions + pi := t.Meta().Partition + if len(pi.Columns) > 0 { + colIDs := make([]int64, 0, len(pi.Columns)) + for _, name := range pi.Columns { + col := table.FindColLowerCase(t.Cols(), name.L) + if col == nil { + // For safety, should not happen + continue + } + colIDs = append(colIDs, col.ID) + } + return colIDs + } + if t.partitionExpr == nil { + return nil + } + + partitionCols := expression.ExtractColumns(t.partitionExpr.Expr) + colIDs := make([]int64, 0, len(partitionCols)) + for _, col := range partitionCols { + colIDs = append(colIDs, col.ID) + } + return colIDs +} + +func (t *partitionedTable) GetPartitionColumnNames() []model.CIStr { + pi := t.Meta().Partition + if len(pi.Columns) > 0 { + return pi.Columns + } + colIDs := t.GetPartitionColumnIDs() + colNames := make([]model.CIStr, 0, len(colIDs)) + for _, colID := range colIDs { + for _, col := range t.Cols() { + if col.ID == colID { + colNames = append(colNames, col.Name) + } + } + } + return colNames +} + +// PartitionRecordKey is exported for test. +func PartitionRecordKey(pid int64, handle int64) kv.Key { + recordPrefix := tablecodec.GenTableRecordPrefix(pid) + return tablecodec.EncodeRecordKey(recordPrefix, kv.IntHandle(handle)) +} + +func (t *partitionedTable) CheckForExchangePartition(ctx sessionctx.Context, pi *model.PartitionInfo, r []types.Datum, partID, ntID int64) error { + defID, err := t.locatePartition(ctx, r) + if err != nil { + return err + } + if defID != partID && defID != ntID { + return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + return nil +} + +// locatePartitionCommon returns the partition idx of the input record. +func (t *partitionedTable) locatePartitionCommon(ctx sessionctx.Context, tp model.PartitionType, partitionExpr *PartitionExpr, num uint64, columnsPartitioned bool, r []types.Datum) (int, error) { + var err error + var idx int + switch tp { + case model.PartitionTypeRange: + if columnsPartitioned { + idx, err = t.locateRangeColumnPartition(ctx, partitionExpr, r) + } else { + idx, err = t.locateRangePartition(ctx, partitionExpr, r) + } + case model.PartitionTypeHash: + // Note that only LIST and RANGE supports REORGANIZE PARTITION + idx, err = t.locateHashPartition(ctx, partitionExpr, num, r) + case model.PartitionTypeKey: + idx, err = partitionExpr.LocateKeyPartition(num, r) + case model.PartitionTypeList: + idx, err = partitionExpr.locateListPartition(ctx, r) + case model.PartitionTypeNone: + idx = 0 + } + if err != nil { + return 0, errors.Trace(err) + } + return idx, nil +} + +func (t *partitionedTable) locatePartition(ctx sessionctx.Context, r []types.Datum) (int64, error) { + pi := t.Meta().GetPartitionInfo() + columnsSet := len(t.meta.Partition.Columns) > 0 + idx, err := t.locatePartitionCommon(ctx, pi.Type, t.partitionExpr, pi.Num, columnsSet, r) + if err != nil { + return 0, errors.Trace(err) + } + return pi.Definitions[idx].ID, nil +} + +func (t *partitionedTable) locateReorgPartition(ctx sessionctx.Context, r []types.Datum) (int64, error) { + pi := t.Meta().GetPartitionInfo() + columnsSet := len(pi.DDLColumns) > 0 + // Note that for KEY/HASH partitioning, since we do not support LINEAR, + // all partitions will be reorganized, + // so we can use the number in Dropping or AddingDefinitions, + // depending on current state. + num := len(pi.AddingDefinitions) + if pi.DDLState == model.StateDeleteReorganization { + num = len(pi.DroppingDefinitions) + } + idx, err := t.locatePartitionCommon(ctx, pi.DDLType, t.reorgPartitionExpr, uint64(num), columnsSet, r) + if err != nil { + return 0, errors.Trace(err) + } + if pi.DDLState == model.StateDeleteReorganization { + return pi.DroppingDefinitions[idx].ID, nil + } + return pi.AddingDefinitions[idx].ID, nil +} + +func (t *partitionedTable) locateRangeColumnPartition(ctx sessionctx.Context, partitionExpr *PartitionExpr, r []types.Datum) (int, error) { + upperBounds := partitionExpr.UpperBounds + var lastError error + evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) + defer t.evalBufferPool.Put(evalBuffer) + idx := sort.Search(len(upperBounds), func(i int) bool { + evalBuffer.SetDatums(r...) + ret, isNull, err := upperBounds[i].EvalInt(ctx, evalBuffer.ToRow()) + if err != nil { + lastError = err + return true // Does not matter, will propagate the last error anyway. + } + if isNull { + // If the column value used to determine the partition is NULL, the row is inserted into the lowest partition. + // See https://dev.mysql.com/doc/mysql-partitioning-excerpt/5.7/en/partitioning-handling-nulls.html + return true // Always less than any other value (NULL cannot be in the partition definition VALUE LESS THAN). + } + return ret > 0 + }) + if lastError != nil { + return 0, errors.Trace(lastError) + } + if idx >= len(upperBounds) { + // The data does not belong to any of the partition returns `table has no partition for value %s`. + var valueMsg string + if t.meta.Partition.Expr != "" { + e, err := expression.ParseSimpleExprWithTableInfo(ctx, t.meta.Partition.Expr, t.meta) + if err == nil { + val, _, err := e.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) + if err == nil { + valueMsg = strconv.FormatInt(val, 10) + } + } + } else { + // When the table is partitioned by range columns. + valueMsg = "from column_list" + } + return 0, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) + } + return idx, nil +} + +func (pe *PartitionExpr) locateListPartition(ctx sessionctx.Context, r []types.Datum) (int, error) { + lp := pe.ForListPruning + if len(lp.ColPrunes) == 0 { + return lp.locateListPartitionByRow(ctx, r) + } + return lp.locateListColumnsPartitionByRow(ctx, r) +} + +func (t *partitionedTable) locateRangePartition(ctx sessionctx.Context, partitionExpr *PartitionExpr, r []types.Datum) (int, error) { + var ( + ret int64 + val int64 + isNull bool + err error + ) + if col, ok := partitionExpr.Expr.(*expression.Column); ok { + if r[col.Index].IsNull() { + isNull = true + } + ret = r[col.Index].GetInt64() + } else { + evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) + defer t.evalBufferPool.Put(evalBuffer) + evalBuffer.SetDatums(r...) + val, isNull, err = partitionExpr.Expr.EvalInt(ctx, evalBuffer.ToRow()) + if err != nil { + return 0, err + } + ret = val + } + unsigned := mysql.HasUnsignedFlag(partitionExpr.Expr.GetType().GetFlag()) + ranges := partitionExpr.ForRangePruning + length := len(ranges.LessThan) + pos := sort.Search(length, func(i int) bool { + if isNull { + return true + } + return ranges.Compare(i, ret, unsigned) > 0 + }) + if isNull { + pos = 0 + } + if pos < 0 || pos >= length { + // The data does not belong to any of the partition returns `table has no partition for value %s`. + var valueMsg string + // TODO: Test with ALTER TABLE t PARTITION BY with a different expression / type + if t.meta.Partition.Expr != "" { + e, err := expression.ParseSimpleExprWithTableInfo(ctx, t.meta.Partition.Expr, t.meta) + if err == nil { + val, _, err := e.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) + if err == nil { + if unsigned { + valueMsg = fmt.Sprintf("%d", uint64(val)) + } else { + valueMsg = fmt.Sprintf("%d", val) + } + } + } + } else { + // When the table is partitioned by range columns. + valueMsg = "from column_list" + } + return 0, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) + } + return pos, nil +} + +// TODO: supports linear hashing +func (t *partitionedTable) locateHashPartition(ctx sessionctx.Context, partExpr *PartitionExpr, numParts uint64, r []types.Datum) (int, error) { + if col, ok := partExpr.Expr.(*expression.Column); ok { + var data types.Datum + switch r[col.Index].Kind() { + case types.KindInt64, types.KindUint64: + data = r[col.Index] + default: + var err error + data, err = r[col.Index].ConvertTo(ctx.GetSessionVars().StmtCtx, types.NewFieldType(mysql.TypeLong)) + if err != nil { + return 0, err + } + } + ret := data.GetInt64() + ret = ret % int64(numParts) + if ret < 0 { + ret = -ret + } + return int(ret), nil + } + evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) + defer t.evalBufferPool.Put(evalBuffer) + evalBuffer.SetDatums(r...) + ret, isNull, err := partExpr.Expr.EvalInt(ctx, evalBuffer.ToRow()) + if err != nil { + return 0, err + } + if isNull { + return 0, nil + } + ret = ret % int64(numParts) + if ret < 0 { + ret = -ret + } + return int(ret), nil +} + +// GetPartition returns a Table, which is actually a partition. +func (t *partitionedTable) GetPartition(pid int64) table.PhysicalTable { + // Attention, can't simply use `return t.partitions[pid]` here. + // Because A nil of type *partition is a kind of `table.PhysicalTable` + part, ok := t.partitions[pid] + if !ok { + // Should never happen! + return nil + } + return part +} + +// GetReorganizedPartitionedTable returns the same table +// but only with the AddingDefinitions used. +func GetReorganizedPartitionedTable(t table.Table) (table.PartitionedTable, error) { + // This is used during Reorganize partitions; All data from DroppingDefinitions + // will be copied to AddingDefinitions, so only setup with AddingDefinitions! + + // Do not change any Definitions of t, but create a new struct. + if t.GetPartitionedTable() == nil { + return nil, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() + } + tblInfo := t.Meta().Clone() + pi := tblInfo.Partition + pi.Definitions = pi.AddingDefinitions + pi.Num = uint64(len(pi.Definitions)) + pi.AddingDefinitions = nil + pi.DroppingDefinitions = nil + + // Reorganized status, use the new values + pi.Type = pi.DDLType + pi.Expr = pi.DDLExpr + pi.Columns = pi.DDLColumns + tblInfo.ID = pi.NewTableID + + constraints, err := table.LoadCheckConstraint(tblInfo) + if err != nil { + return nil, err + } + var tc TableCommon + initTableCommon(&tc, tblInfo, tblInfo.ID, t.Cols(), t.Allocators(nil), constraints) + + // and rebuild the partitioning structure + return newPartitionedTable(&tc, tblInfo) +} + +// GetPartitionByRow returns a Table, which is actually a Partition. +func (t *partitionedTable) GetPartitionByRow(ctx sessionctx.Context, r []types.Datum) (table.PhysicalTable, error) { + pid, err := t.locatePartition(ctx, r) + if err != nil { + return nil, errors.Trace(err) + } + return t.partitions[pid], nil +} + +// GetPartitionByRow returns a Table, which is actually a Partition. +func (t *partitionTableWithGivenSets) GetPartitionByRow(ctx sessionctx.Context, r []types.Datum) (table.PhysicalTable, error) { + pid, err := t.locatePartition(ctx, r) + if err != nil { + return nil, errors.Trace(err) + } + if _, ok := t.givenSetPartitions[pid]; !ok { + return nil, errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + return t.partitions[pid], nil +} + +// checkConstraintForExchangePartition is only used for ExchangePartition by partitionTable during write only state. +// It check if rowData inserted or updated violate checkConstraints of non-partitionTable. +func checkConstraintForExchangePartition(sctx sessionctx.Context, row []types.Datum, partID, ntID int64) error { + type InfoSchema interface { + TableByID(id int64) (val table.Table, ok bool) + } + is, ok := sctx.GetDomainInfoSchema().(InfoSchema) + if !ok { + return errors.Errorf("exchange partition process assert inforSchema failed") + } + nt, tableFound := is.TableByID(ntID) + if !tableFound { + // Now partID is nt tableID. + nt, tableFound = is.TableByID(partID) + if !tableFound { + return errors.Errorf("exchange partition process table by id failed") + } + } + type CheckConstraintTable interface { + CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error + } + cc, ok := nt.(CheckConstraintTable) + if !ok { + return errors.Errorf("exchange partition process assert check constraint failed") + } + err := cc.CheckRowConstraint(sctx, row) + if err != nil { + // TODO: make error include ExchangePartition info. + return err + } + return nil +} + +// AddRecord implements the AddRecord method for the table.Table interface. +func (t *partitionedTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return partitionedTableAddRecord(ctx, t, r, nil, opts) +} + +func partitionedTableAddRecord(ctx sessionctx.Context, t *partitionedTable, r []types.Datum, partitionSelection map[int64]struct{}, opts []table.AddRecordOption) (recordID kv.Handle, err error) { + pid, err := t.locatePartition(ctx, r) + if err != nil { + return nil, errors.Trace(err) + } + + if partitionSelection != nil { + if _, ok := partitionSelection[pid]; !ok { + return nil, errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + } + if t.Meta().Partition.HasTruncatingPartitionID(pid) { + return nil, errors.WithStack(dbterror.ErrInvalidDDLState.GenWithStack("the partition is in not in public")) + } + exchangePartitionInfo := t.Meta().ExchangePartitionInfo + if exchangePartitionInfo != nil && exchangePartitionInfo.ExchangePartitionDefID == pid && + variable.EnableCheckConstraint.Load() { + err = checkConstraintForExchangePartition(ctx, r, pid, exchangePartitionInfo.ExchangePartitionTableID) + if err != nil { + return nil, errors.WithStack(err) + } + } + tbl := t.GetPartition(pid) + recordID, err = tbl.AddRecord(ctx, r, opts...) + if err != nil { + return + } + if t.Meta().Partition.DDLState == model.StateDeleteOnly { + return + } + if _, ok := t.reorganizePartitions[pid]; ok { + // Double write to the ongoing reorganized partition + pid, err = t.locateReorgPartition(ctx, r) + if err != nil { + return nil, errors.Trace(err) + } + tbl = t.GetPartition(pid) + recordID, err = tbl.AddRecord(ctx, r, opts...) + if err != nil { + return + } + } + return +} + +// partitionTableWithGivenSets is used for this kind of grammar: partition (p0,p1) +// Basically it is the same as partitionedTable except that partitionTableWithGivenSets +// checks the given partition set for AddRecord/UpdateRecord operations. +type partitionTableWithGivenSets struct { + *partitionedTable + givenSetPartitions map[int64]struct{} +} + +// NewPartitionTableWithGivenSets creates a new partition table from a partition table. +func NewPartitionTableWithGivenSets(tbl table.PartitionedTable, partitions map[int64]struct{}) table.PartitionedTable { + if raw, ok := tbl.(*partitionedTable); ok { + return &partitionTableWithGivenSets{ + partitionedTable: raw, + givenSetPartitions: partitions, + } + } + return tbl +} + +// AddRecord implements the AddRecord method for the table.Table interface. +func (t *partitionTableWithGivenSets) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + return partitionedTableAddRecord(ctx, t.partitionedTable, r, t.givenSetPartitions, opts) +} + +func (t *partitionTableWithGivenSets) GetAllPartitionIDs() []int64 { + ptIDs := make([]int64, 0, len(t.partitions)) + for id := range t.givenSetPartitions { + ptIDs = append(ptIDs, id) + } + return ptIDs +} + +// RemoveRecord implements table.Table RemoveRecord interface. +func (t *partitionedTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { + pid, err := t.locatePartition(ctx, r) + if err != nil { + return errors.Trace(err) + } + + tbl := t.GetPartition(pid) + err = tbl.RemoveRecord(ctx, h, r) + if err != nil { + return errors.Trace(err) + } + + if _, ok := t.reorganizePartitions[pid]; ok { + pid, err = t.locateReorgPartition(ctx, r) + if err != nil { + return errors.Trace(err) + } + tbl = t.GetPartition(pid) + err = tbl.RemoveRecord(ctx, h, r) + if err != nil { + return errors.Trace(err) + } + } + return nil +} + +func (t *partitionedTable) GetAllPartitionIDs() []int64 { + ptIDs := make([]int64, 0, len(t.partitions)) + for id := range t.partitions { + if _, ok := t.doubleWritePartitions[id]; ok { + continue + } + ptIDs = append(ptIDs, id) + } + return ptIDs +} + +// UpdateRecord implements table.Table UpdateRecord interface. +// `touched` means which columns are really modified, used for secondary indices. +// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. +func (t *partitionedTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { + return partitionedTableUpdateRecord(ctx, sctx, t, h, currData, newData, touched, nil) +} + +func (t *partitionTableWithGivenSets) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { + return partitionedTableUpdateRecord(ctx, sctx, t.partitionedTable, h, currData, newData, touched, t.givenSetPartitions) +} + +func partitionedTableUpdateRecord(gctx context.Context, ctx sessionctx.Context, t *partitionedTable, h kv.Handle, currData, newData []types.Datum, touched []bool, partitionSelection map[int64]struct{}) error { + from, err := t.locatePartition(ctx, currData) + if err != nil { + return errors.Trace(err) + } + to, err := t.locatePartition(ctx, newData) + if err != nil { + return errors.Trace(err) + } + if partitionSelection != nil { + if _, ok := partitionSelection[to]; !ok { + return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + // Should not have been read from this partition! Checked already in GetPartitionByRow() + if _, ok := partitionSelection[from]; !ok { + return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) + } + } + if t.Meta().Partition.HasTruncatingPartitionID(to) { + return errors.WithStack(dbterror.ErrInvalidDDLState.GenWithStack("the partition is in not in public")) + } + exchangePartitionInfo := t.Meta().ExchangePartitionInfo + if exchangePartitionInfo != nil && exchangePartitionInfo.ExchangePartitionDefID == to && + variable.EnableCheckConstraint.Load() { + err = checkConstraintForExchangePartition(ctx, newData, to, exchangePartitionInfo.ExchangePartitionTableID) + if err != nil { + return errors.WithStack(err) + } + } + + // The old and new data locate in different partitions. + // Remove record from old partition and add record to new partition. + if from != to { + _, err = t.GetPartition(to).AddRecord(ctx, newData) + if err != nil { + return errors.Trace(err) + } + // UpdateRecord should be side effect free, but there're two steps here. + // What would happen if step1 succeed but step2 meets error? It's hard + // to rollback. + // So this special order is chosen: add record first, errors such as + // 'Key Already Exists' will generally happen during step1, errors are + // unlikely to happen in step2. + err = t.GetPartition(from).RemoveRecord(ctx, h, currData) + if err != nil { + logutil.BgLogger().Error("update partition record fails", zap.String("message", "new record inserted while old record is not removed"), zap.Error(err)) + return errors.Trace(err) + } + newTo, newFrom := int64(0), int64(0) + if _, ok := t.reorganizePartitions[to]; ok { + newTo, err = t.locateReorgPartition(ctx, newData) + // There might be valid cases when errors should be accepted? + if err != nil { + return errors.Trace(err) + } + } + if _, ok := t.reorganizePartitions[from]; ok { + newFrom, err = t.locateReorgPartition(ctx, currData) + // There might be valid cases when errors should be accepted? + if err != nil { + return errors.Trace(err) + } + } + if newTo == newFrom && newTo != 0 { + // Update needs to be done in StateDeleteOnly as well + tbl := t.GetPartition(newTo) + return tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) + } + if newTo != 0 && t.Meta().GetPartitionInfo().DDLState != model.StateDeleteOnly { + tbl := t.GetPartition(newTo) + _, err = tbl.AddRecord(ctx, newData) + if err != nil { + return errors.Trace(err) + } + } + if newFrom != 0 { + tbl := t.GetPartition(newFrom) + err = tbl.RemoveRecord(ctx, h, currData) + // TODO: Can this happen? When the data is not yet backfilled? + if err != nil { + return errors.Trace(err) + } + } + return nil + } + tbl := t.GetPartition(to) + err = tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) + if err != nil { + return errors.Trace(err) + } + if _, ok := t.reorganizePartitions[to]; ok { + // Even if to == from, in the reorganized partitions they may differ + // like in case of a split + newTo, err := t.locateReorgPartition(ctx, newData) + if err != nil { + return errors.Trace(err) + } + newFrom, err := t.locateReorgPartition(ctx, currData) + if err != nil { + return errors.Trace(err) + } + if newTo == newFrom { + tbl = t.GetPartition(newTo) + if t.Meta().Partition.DDLState == model.StateDeleteOnly { + err = tbl.RemoveRecord(ctx, h, currData) + } else { + err = tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) + } + if err != nil { + return errors.Trace(err) + } + return nil + } + if t.Meta().GetPartitionInfo().DDLState != model.StateDeleteOnly { + tbl = t.GetPartition(newTo) + _, err = tbl.AddRecord(ctx, newData) + if err != nil { + return errors.Trace(err) + } + } + tbl = t.GetPartition(newFrom) + err = tbl.RemoveRecord(ctx, h, currData) + if err != nil { + return errors.Trace(err) + } + } + return nil +} + +// FindPartitionByName finds partition in table meta by name. +func FindPartitionByName(meta *model.TableInfo, parName string) (int64, error) { + // Hash partition table use p0, p1, p2, p3 as partition names automatically. + parName = strings.ToLower(parName) + for _, def := range meta.Partition.Definitions { + if strings.EqualFold(def.Name.L, parName) { + return def.ID, nil + } + } + return -1, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs(parName, meta.Name.O)) +} + +func parseExpr(p *parser.Parser, exprStr string) (ast.ExprNode, error) { + exprStr = "select " + exprStr + stmts, _, err := p.ParseSQL(exprStr) + if err != nil { + return nil, util.SyntaxWarn(err) + } + fields := stmts[0].(*ast.SelectStmt).Fields.Fields + return fields[0].Expr, nil +} + +func rewritePartitionExpr(ctx sessionctx.Context, field ast.ExprNode, schema *expression.Schema, names types.NameSlice) (expression.Expression, error) { + expr, err := expression.RewriteSimpleExprWithNames(ctx, field, schema, names) + return expr, err +} + +func compareUnsigned(v1, v2 int64) int { + switch { + case uint64(v1) > uint64(v2): + return 1 + case uint64(v1) == uint64(v2): + return 0 + } + return -1 +} + +// Compare is to be used in the binary search to locate partition +func (lt *ForRangePruning) Compare(ith int, v int64, unsigned bool) int { + if ith == len(lt.LessThan)-1 { + if lt.MaxValue { + return 1 + } + } + if unsigned { + return compareUnsigned(lt.LessThan[ith], v) + } + switch { + case lt.LessThan[ith] > v: + return 1 + case lt.LessThan[ith] == v: + return 0 + } + return -1 +} diff --git a/table/tables/state_remote.go b/pkg/table/tables/state_remote.go similarity index 98% rename from table/tables/state_remote.go rename to pkg/table/tables/state_remote.go index 2ec1c804b3b1e..d58b340262540 100644 --- a/table/tables/state_remote.go +++ b/pkg/table/tables/state_remote.go @@ -20,10 +20,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/oracle" ) diff --git a/table/tables/state_remote_test.go b/pkg/table/tables/state_remote_test.go similarity index 96% rename from table/tables/state_remote_test.go rename to pkg/table/tables/state_remote_test.go index de9fa25c93639..d8b6cde16ee26 100644 --- a/table/tables/state_remote_test.go +++ b/pkg/table/tables/state_remote_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" ) diff --git a/pkg/table/tables/tables.go b/pkg/table/tables/tables.go new file mode 100644 index 0000000000000..c54fb3f35fbe0 --- /dev/null +++ b/pkg/table/tables/tables.go @@ -0,0 +1,2383 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2013 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package tables + +import ( + "context" + "fmt" + "math" + "sort" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/statistics" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/generatedexpr" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/stringutil" + "github.com/pingcap/tidb/pkg/util/tableutil" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/pingcap/tipb/go-binlog" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +// TableCommon is shared by both Table and partition. +type TableCommon struct { + // TODO: Why do we need tableID, when it is already in meta.ID ? + tableID int64 + // physicalTableID is a unique int64 to identify a physical table. + physicalTableID int64 + Columns []*table.Column + PublicColumns []*table.Column + VisibleColumns []*table.Column + HiddenColumns []*table.Column + WritableColumns []*table.Column + FullHiddenColsAndVisibleColumns []*table.Column + indices []table.Index + meta *model.TableInfo + allocs autoid.Allocators + sequence *sequenceCommon + dependencyColumnOffsets []int + Constraints []*table.Constraint + WritableConstraints []*table.Constraint + + // recordPrefix and indexPrefix are generated using physicalTableID. + recordPrefix kv.Key + indexPrefix kv.Key +} + +// MockTableFromMeta only serves for test. +func MockTableFromMeta(tblInfo *model.TableInfo) table.Table { + columns := make([]*table.Column, 0, len(tblInfo.Columns)) + for _, colInfo := range tblInfo.Columns { + col := table.ToColumn(colInfo) + columns = append(columns, col) + } + + constraints, err := table.LoadCheckConstraint(tblInfo) + if err != nil { + return nil + } + var t TableCommon + initTableCommon(&t, tblInfo, tblInfo.ID, columns, autoid.NewAllocators(false), constraints) + if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { + ret, err := newCachedTable(&t) + if err != nil { + return nil + } + return ret + } + if tblInfo.GetPartitionInfo() == nil { + if err := initTableIndices(&t); err != nil { + return nil + } + return &t + } + + ret, err := newPartitionedTable(&t, tblInfo) + if err != nil { + return nil + } + return ret +} + +// TableFromMeta creates a Table instance from model.TableInfo. +func TableFromMeta(allocs autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) { + if tblInfo.State == model.StateNone { + return nil, table.ErrTableStateCantNone.GenWithStackByArgs(tblInfo.Name) + } + + colsLen := len(tblInfo.Columns) + columns := make([]*table.Column, 0, colsLen) + for i, colInfo := range tblInfo.Columns { + if colInfo.State == model.StateNone { + return nil, table.ErrColumnStateCantNone.GenWithStackByArgs(colInfo.Name) + } + + // Print some information when the column's offset isn't equal to i. + if colInfo.Offset != i { + logutil.BgLogger().Error("wrong table schema", zap.Any("table", tblInfo), zap.Any("column", colInfo), zap.Int("index", i), zap.Int("offset", colInfo.Offset), zap.Int("columnNumber", colsLen)) + } + + col := table.ToColumn(colInfo) + if col.IsGenerated() { + expr, err := generatedexpr.ParseExpression(colInfo.GeneratedExprString) + if err != nil { + return nil, err + } + expr, err = generatedexpr.SimpleResolveName(expr, tblInfo) + if err != nil { + return nil, err + } + col.GeneratedExpr = expr + } + // default value is expr. + if col.DefaultIsExpr { + expr, err := generatedexpr.ParseExpression(colInfo.DefaultValue.(string)) + if err != nil { + return nil, err + } + col.DefaultExpr = expr + } + columns = append(columns, col) + } + + constraints, err := table.LoadCheckConstraint(tblInfo) + if err != nil { + return nil, err + } + var t TableCommon + initTableCommon(&t, tblInfo, tblInfo.ID, columns, allocs, constraints) + if tblInfo.GetPartitionInfo() == nil { + if err := initTableIndices(&t); err != nil { + return nil, err + } + if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { + return newCachedTable(&t) + } + return &t, nil + } + return newPartitionedTable(&t, tblInfo) +} + +// initTableCommon initializes a TableCommon struct. +func initTableCommon(t *TableCommon, tblInfo *model.TableInfo, physicalTableID int64, cols []*table.Column, allocs autoid.Allocators, constraints []*table.Constraint) { + t.tableID = tblInfo.ID + t.physicalTableID = physicalTableID + t.allocs = allocs + t.meta = tblInfo + t.Columns = cols + t.PublicColumns = t.Cols() + t.VisibleColumns = t.VisibleCols() + t.HiddenColumns = t.HiddenCols() + t.WritableColumns = t.WritableCols() + t.FullHiddenColsAndVisibleColumns = t.FullHiddenColsAndVisibleCols() + t.Constraints = constraints + t.WritableConstraints = t.WritableConstraint() + t.recordPrefix = tablecodec.GenTableRecordPrefix(physicalTableID) + t.indexPrefix = tablecodec.GenTableIndexPrefix(physicalTableID) + if tblInfo.IsSequence() { + t.sequence = &sequenceCommon{meta: tblInfo.Sequence} + } + for _, col := range cols { + if col.ChangeStateInfo != nil { + t.dependencyColumnOffsets = append(t.dependencyColumnOffsets, col.ChangeStateInfo.DependencyColumnOffset) + } + } +} + +// initTableIndices initializes the indices of the TableCommon. +func initTableIndices(t *TableCommon) error { + tblInfo := t.meta + for _, idxInfo := range tblInfo.Indices { + if idxInfo.State == model.StateNone { + return table.ErrIndexStateCantNone.GenWithStackByArgs(idxInfo.Name) + } + + // Use partition ID for index, because TableCommon may be table or partition. + idx := NewIndex(t.physicalTableID, tblInfo, idxInfo) + t.indices = append(t.indices, idx) + } + return nil +} + +func initTableCommonWithIndices(t *TableCommon, tblInfo *model.TableInfo, physicalTableID int64, cols []*table.Column, allocs autoid.Allocators, constraints []*table.Constraint) error { + initTableCommon(t, tblInfo, physicalTableID, cols, allocs, constraints) + return initTableIndices(t) +} + +// Indices implements table.Table Indices interface. +func (t *TableCommon) Indices() []table.Index { + return t.indices +} + +// GetWritableIndexByName gets the index meta from the table by the index name. +func GetWritableIndexByName(idxName string, t table.Table) table.Index { + for _, idx := range t.Indices() { + if !IsIndexWritable(idx) { + continue + } + if idxName == idx.Meta().Name.L { + return idx + } + } + return nil +} + +// deletableIndices implements table.Table deletableIndices interface. +func (t *TableCommon) deletableIndices() []table.Index { + // All indices are deletable because we don't need to check StateNone. + return t.indices +} + +// Meta implements table.Table Meta interface. +func (t *TableCommon) Meta() *model.TableInfo { + return t.meta +} + +// GetPhysicalID implements table.Table GetPhysicalID interface. +func (t *TableCommon) GetPhysicalID() int64 { + return t.physicalTableID +} + +// GetPartitionedTable implements table.Table GetPhysicalID interface. +func (t *TableCommon) GetPartitionedTable() table.PartitionedTable { + return nil +} + +type getColsMode int64 + +const ( + _ getColsMode = iota + visible + hidden + full +) + +func (t *TableCommon) getCols(mode getColsMode) []*table.Column { + columns := make([]*table.Column, 0, len(t.Columns)) + for _, col := range t.Columns { + if col.State != model.StatePublic { + continue + } + if (mode == visible && col.Hidden) || (mode == hidden && !col.Hidden) { + continue + } + columns = append(columns, col) + } + return columns +} + +// Cols implements table.Table Cols interface. +func (t *TableCommon) Cols() []*table.Column { + if len(t.PublicColumns) > 0 { + return t.PublicColumns + } + return t.getCols(full) +} + +// VisibleCols implements table.Table VisibleCols interface. +func (t *TableCommon) VisibleCols() []*table.Column { + if len(t.VisibleColumns) > 0 { + return t.VisibleColumns + } + return t.getCols(visible) +} + +// HiddenCols implements table.Table HiddenCols interface. +func (t *TableCommon) HiddenCols() []*table.Column { + if len(t.HiddenColumns) > 0 { + return t.HiddenColumns + } + return t.getCols(hidden) +} + +// WritableCols implements table WritableCols interface. +func (t *TableCommon) WritableCols() []*table.Column { + if len(t.WritableColumns) > 0 { + return t.WritableColumns + } + writableColumns := make([]*table.Column, 0, len(t.Columns)) + for _, col := range t.Columns { + if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { + continue + } + writableColumns = append(writableColumns, col) + } + return writableColumns +} + +// DeletableCols implements table DeletableCols interface. +func (t *TableCommon) DeletableCols() []*table.Column { + return t.Columns +} + +// WritableConstraint returns constraints of the table in writable states. +func (t *TableCommon) WritableConstraint() []*table.Constraint { + if len(t.WritableConstraints) > 0 { + return t.WritableConstraints + } + if t.Constraints == nil { + return nil + } + writeableConstraint := make([]*table.Constraint, 0, len(t.Constraints)) + for _, con := range t.Constraints { + if !con.Enforced { + continue + } + if con.State == model.StateDeleteOnly || con.State == model.StateDeleteReorganization { + continue + } + writeableConstraint = append(writeableConstraint, con) + } + return writeableConstraint +} + +// CheckRowConstraint verify row check constraints. +func (t *TableCommon) CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error { + for _, constraint := range t.WritableConstraint() { + ok, isNull, err := constraint.ConstraintExpr.EvalInt(sctx, chunk.MutRowFromDatums(rowToCheck).ToRow()) + if err != nil { + return err + } + if ok == 0 && !isNull { + return table.ErrCheckConstraintViolated.FastGenByArgs(constraint.Name.O) + } + } + return nil +} + +// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. +func (t *TableCommon) FullHiddenColsAndVisibleCols() []*table.Column { + if len(t.FullHiddenColsAndVisibleColumns) > 0 { + return t.FullHiddenColsAndVisibleColumns + } + + cols := make([]*table.Column, 0, len(t.Columns)) + for _, col := range t.Columns { + if col.Hidden || col.State == model.StatePublic { + cols = append(cols, col) + } + } + return cols +} + +// RecordPrefix implements table.Table interface. +func (t *TableCommon) RecordPrefix() kv.Key { + return t.recordPrefix +} + +// IndexPrefix implements table.Table interface. +func (t *TableCommon) IndexPrefix() kv.Key { + return t.indexPrefix +} + +// RecordKey implements table.Table interface. +func (t *TableCommon) RecordKey(h kv.Handle) kv.Key { + return tablecodec.EncodeRecordKey(t.recordPrefix, h) +} + +// shouldAssert checks if the partition should be in consistent +// state and can have assertion. +func (t *TableCommon) shouldAssert(sctx sessionctx.Context) bool { + p := t.Meta().Partition + if p != nil { + // This disables asserting during Reorganize Partition. + switch sctx.GetSessionVars().AssertionLevel { + case variable.AssertionLevelFast: + // Fast option, just skip assertion for all partitions. + if p.DDLState != model.StateNone && p.DDLState != model.StatePublic { + return false + } + case variable.AssertionLevelStrict: + // Strict, only disable assertion for intermediate partitions. + // If there were an easy way to get from a TableCommon back to the partitioned table... + for i := range p.AddingDefinitions { + if t.physicalTableID == p.AddingDefinitions[i].ID { + return false + } + } + } + } + return true +} + +// UpdateRecord implements table.Table UpdateRecord interface. +// `touched` means which columns are really modified, used for secondary indices. +// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. +func (t *TableCommon) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { + txn, err := sctx.Txn(true) + if err != nil { + return err + } + + memBuffer := txn.GetMemBuffer() + sh := memBuffer.Staging() + defer memBuffer.Cleanup(sh) + + if m := t.Meta(); m.TempTableType != model.TempTableNone { + if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil { + if err := checkTempTableSize(sctx, tmpTable, m); err != nil { + return err + } + defer handleTempTableSize(tmpTable, txn.Size(), txn) + } + } + + var colIDs, binlogColIDs []int64 + var row, binlogOldRow, binlogNewRow []types.Datum + var checksums []uint32 + numColsCap := len(newData) + 1 // +1 for the extra handle column that we may need to append. + colIDs = make([]int64, 0, numColsCap) + row = make([]types.Datum, 0, numColsCap) + if shouldWriteBinlog(sctx, t.meta) { + binlogColIDs = make([]int64, 0, numColsCap) + binlogOldRow = make([]types.Datum, 0, numColsCap) + binlogNewRow = make([]types.Datum, 0, numColsCap) + } + checksumData := t.initChecksumData(sctx, h) + needChecksum := len(checksumData) > 0 + rowToCheck := make([]types.Datum, 0, numColsCap) + + for _, col := range t.Columns { + var value types.Datum + if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { + if col.ChangeStateInfo != nil { + // TODO: Check overflow or ignoreTruncate. + value, err = table.CastValue(sctx, oldData[col.DependencyColumnOffset], col.ColumnInfo, false, false) + if err != nil { + logutil.BgLogger().Info("update record cast value failed", zap.Any("col", col), zap.Uint64("txnStartTS", txn.StartTS()), + zap.String("handle", h.String()), zap.Any("val", oldData[col.DependencyColumnOffset]), zap.Error(err)) + return err + } + oldData = append(oldData, value) + touched = append(touched, touched[col.DependencyColumnOffset]) + } + if needChecksum { + if col.ChangeStateInfo != nil { + // TODO: Check overflow or ignoreTruncate. + v, err := table.CastValue(sctx, newData[col.DependencyColumnOffset], col.ColumnInfo, false, false) + if err != nil { + return err + } + checksumData = t.appendInChangeColForChecksum(sctx, h, checksumData, col.ToInfo(), &newData[col.DependencyColumnOffset], &v) + } else { + v, err := table.GetColOriginDefaultValue(sctx, col.ToInfo()) + if err != nil { + return err + } + checksumData = t.appendNonPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &v) + } + } + continue + } + if col.State != model.StatePublic { + // If col is in write only or write reorganization state we should keep the oldData. + // Because the oldData must be the original data(it's changed by other TiDBs.) or the original default value. + // TODO: Use newData directly. + value = oldData[col.Offset] + if col.ChangeStateInfo != nil { + // TODO: Check overflow or ignoreTruncate. + value, err = table.CastValue(sctx, newData[col.DependencyColumnOffset], col.ColumnInfo, false, false) + if err != nil { + return err + } + newData[col.Offset] = value + touched[col.Offset] = touched[col.DependencyColumnOffset] + checksumData = t.appendInChangeColForChecksum(sctx, h, checksumData, col.ToInfo(), &newData[col.DependencyColumnOffset], &value) + } else if needChecksum { + checksumData = t.appendNonPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &value) + } + } else { + value = newData[col.Offset] + checksumData = t.appendPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &value) + } + if !t.canSkip(col, &value) { + colIDs = append(colIDs, col.ID) + row = append(row, value) + } + rowToCheck = append(rowToCheck, value) + if shouldWriteBinlog(sctx, t.meta) && !t.canSkipUpdateBinlog(col, value) { + binlogColIDs = append(binlogColIDs, col.ID) + binlogOldRow = append(binlogOldRow, oldData[col.Offset]) + binlogNewRow = append(binlogNewRow, value) + } + } + // check data constraint + err = t.CheckRowConstraint(sctx, rowToCheck) + if err != nil { + return err + } + sessVars := sctx.GetSessionVars() + // rebuild index + if !sessVars.InTxn() { + savePresumeKeyNotExist := sessVars.PresumeKeyNotExists + if !sessVars.ConstraintCheckInPlace && sessVars.TxnCtx.IsPessimistic { + sessVars.PresumeKeyNotExists = true + } + err = t.rebuildIndices(sctx, txn, h, touched, oldData, newData, table.WithCtx(ctx)) + sessVars.PresumeKeyNotExists = savePresumeKeyNotExist + if err != nil { + return err + } + } else { + err = t.rebuildIndices(sctx, txn, h, touched, oldData, newData, table.WithCtx(ctx)) + if err != nil { + return err + } + } + + writeBufs := sessVars.GetWriteStmtBufs() + adjustRowValuesBuf(writeBufs, len(row)) + key := t.RecordKey(h) + sc, rd := sessVars.StmtCtx, &sessVars.RowEncoder + checksums, writeBufs.RowValBuf = t.calcChecksums(sctx, h, checksumData, writeBufs.RowValBuf) + writeBufs.RowValBuf, err = tablecodec.EncodeRow(sc, row, colIDs, writeBufs.RowValBuf, writeBufs.AddRowValues, rd, checksums...) + if err != nil { + return err + } + if err = memBuffer.Set(key, writeBufs.RowValBuf); err != nil { + return err + } + + failpoint.Inject("updateRecordForceAssertNotExist", func() { + // Assert the key doesn't exist while it actually exists. This is helpful to test if assertion takes effect. + // Since only the first assertion takes effect, set the injected assertion before setting the correct one to + // override it. + if sctx.GetSessionVars().ConnectionID != 0 { + logutil.BgLogger().Info("force asserting not exist on UpdateRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) + if err = txn.SetAssertion(key, kv.SetAssertNotExist); err != nil { + failpoint.Return(err) + } + } + }) + + if t.shouldAssert(sctx) { + err = txn.SetAssertion(key, kv.SetAssertExist) + } else { + err = txn.SetAssertion(key, kv.SetAssertUnknown) + } + if err != nil { + return err + } + + if err = injectMutationError(t, txn, sh); err != nil { + return err + } + if sessVars.EnableMutationChecker { + if err = CheckDataConsistency(txn, sessVars, t, newData, oldData, memBuffer, sh); err != nil { + return errors.Trace(err) + } + } + + memBuffer.Release(sh) + if shouldWriteBinlog(sctx, t.meta) { + if !t.meta.PKIsHandle && !t.meta.IsCommonHandle { + binlogColIDs = append(binlogColIDs, model.ExtraHandleID) + binlogOldRow = append(binlogOldRow, types.NewIntDatum(h.IntValue())) + binlogNewRow = append(binlogNewRow, types.NewIntDatum(h.IntValue())) + } + err = t.addUpdateBinlog(sctx, binlogOldRow, binlogNewRow, binlogColIDs) + if err != nil { + return err + } + } + colSize := make(map[int64]int64, len(t.Cols())) + for id, col := range t.Cols() { + size, err := codec.EstimateValueSize(sc, newData[id]) + if err != nil { + continue + } + newLen := size - 1 + size, err = codec.EstimateValueSize(sc, oldData[id]) + if err != nil { + continue + } + oldLen := size - 1 + colSize[col.ID] = int64(newLen - oldLen) + } + sessVars.TxnCtx.UpdateDeltaForTable(t.physicalTableID, 0, 1, colSize) + return nil +} + +func (t *TableCommon) rebuildIndices(ctx sessionctx.Context, txn kv.Transaction, h kv.Handle, touched []bool, oldData []types.Datum, newData []types.Datum, opts ...table.CreateIdxOptFunc) error { + for _, idx := range t.deletableIndices() { + if t.meta.IsCommonHandle && idx.Meta().Primary { + continue + } + for _, ic := range idx.Meta().Columns { + if !touched[ic.Offset] { + continue + } + oldVs, err := idx.FetchValues(oldData, nil) + if err != nil { + return err + } + if err = t.removeRowIndex(ctx.GetSessionVars().StmtCtx, h, oldVs, idx, txn); err != nil { + return err + } + break + } + } + for _, idx := range t.Indices() { + if !IsIndexWritable(idx) { + continue + } + if t.meta.IsCommonHandle && idx.Meta().Primary { + continue + } + untouched := true + for _, ic := range idx.Meta().Columns { + if !touched[ic.Offset] { + continue + } + untouched = false + break + } + // If txn is auto commit and index is untouched, no need to write index value. + // If InHandleForeignKeyTrigger or ForeignKeyTriggerCtx.HasFKCascades is true indicate we may have + // foreign key cascade need to handle later, then we still need to write index value, + // otherwise, the later foreign cascade executor may see data-index inconsistency in txn-mem-buffer. + sessVars := ctx.GetSessionVars() + if untouched && !sessVars.InTxn() && + !sessVars.StmtCtx.InHandleForeignKeyTrigger && !sessVars.StmtCtx.ForeignKeyTriggerCtx.HasFKCascades { + continue + } + newVs, err := idx.FetchValues(newData, nil) + if err != nil { + return err + } + if err := t.buildIndexForRow(ctx, h, newVs, newData, idx, txn, untouched, opts...); err != nil { + return err + } + } + return nil +} + +// adjustRowValuesBuf adjust writeBufs.AddRowValues length, AddRowValues stores the inserting values that is used +// by tablecodec.EncodeRow, the encoded row format is `id1, colval, id2, colval`, so the correct length is rowLen * 2. If +// the inserting row has null value, AddRecord will skip it, so the rowLen will be different, so we need to adjust it. +func adjustRowValuesBuf(writeBufs *variable.WriteStmtBufs, rowLen int) { + adjustLen := rowLen * 2 + if writeBufs.AddRowValues == nil || cap(writeBufs.AddRowValues) < adjustLen { + writeBufs.AddRowValues = make([]types.Datum, adjustLen) + } + writeBufs.AddRowValues = writeBufs.AddRowValues[:adjustLen] +} + +// FindPrimaryIndex uses to find primary index in tableInfo. +func FindPrimaryIndex(tblInfo *model.TableInfo) *model.IndexInfo { + var pkIdx *model.IndexInfo + for _, idx := range tblInfo.Indices { + if idx.Primary { + pkIdx = idx + break + } + } + return pkIdx +} + +// CommonAddRecordCtx is used in `AddRecord` to avoid memory malloc for some temp slices. +// This is useful in lightning parse row data to key-values pairs. This can gain upto 5% performance +// improvement in lightning's local mode. +type CommonAddRecordCtx struct { + colIDs []int64 + row []types.Datum +} + +// commonAddRecordKey is used as key in `sessionctx.Context.Value(key)` +type commonAddRecordKey struct{} + +// String implement `stringer.String` for CommonAddRecordKey +func (c commonAddRecordKey) String() string { + return "_common_add_record_context_key" +} + +// addRecordCtxKey is key in `sessionctx.Context` for CommonAddRecordCtx +var addRecordCtxKey = commonAddRecordKey{} + +// SetAddRecordCtx set a CommonAddRecordCtx to session context +func SetAddRecordCtx(ctx sessionctx.Context, r *CommonAddRecordCtx) { + ctx.SetValue(addRecordCtxKey, r) +} + +// ClearAddRecordCtx remove `CommonAddRecordCtx` from session context +func ClearAddRecordCtx(ctx sessionctx.Context) { + ctx.ClearValue(addRecordCtxKey) +} + +// NewCommonAddRecordCtx create a context used for `AddRecord` +func NewCommonAddRecordCtx(size int) *CommonAddRecordCtx { + return &CommonAddRecordCtx{ + colIDs: make([]int64, 0, size), + row: make([]types.Datum, 0, size), + } +} + +// TryGetCommonPkColumnIds get the IDs of primary key column if the table has common handle. +func TryGetCommonPkColumnIds(tbl *model.TableInfo) []int64 { + if !tbl.IsCommonHandle { + return nil + } + pkIdx := FindPrimaryIndex(tbl) + pkColIds := make([]int64, 0, len(pkIdx.Columns)) + for _, idxCol := range pkIdx.Columns { + pkColIds = append(pkColIds, tbl.Columns[idxCol.Offset].ID) + } + return pkColIds +} + +// PrimaryPrefixColumnIDs get prefix column ids in primary key. +func PrimaryPrefixColumnIDs(tbl *model.TableInfo) (prefixCols []int64) { + for _, idx := range tbl.Indices { + if !idx.Primary { + continue + } + for _, col := range idx.Columns { + if col.Length > 0 && tbl.Columns[col.Offset].GetFlen() > col.Length { + prefixCols = append(prefixCols, tbl.Columns[col.Offset].ID) + } + } + } + return +} + +// TryGetCommonPkColumns get the primary key columns if the table has common handle. +func TryGetCommonPkColumns(tbl table.Table) []*table.Column { + if !tbl.Meta().IsCommonHandle { + return nil + } + pkIdx := FindPrimaryIndex(tbl.Meta()) + cols := tbl.Cols() + pkCols := make([]*table.Column, 0, len(pkIdx.Columns)) + for _, idxCol := range pkIdx.Columns { + pkCols = append(pkCols, cols[idxCol.Offset]) + } + return pkCols +} + +func addTemporaryTable(sctx sessionctx.Context, tblInfo *model.TableInfo) tableutil.TempTable { + tempTable := sctx.GetSessionVars().GetTemporaryTable(tblInfo) + tempTable.SetModified(true) + return tempTable +} + +// The size of a temporary table is calculated by accumulating the transaction size delta. +func handleTempTableSize(t tableutil.TempTable, txnSizeBefore int, txn kv.Transaction) { + txnSizeNow := txn.Size() + delta := txnSizeNow - txnSizeBefore + + oldSize := t.GetSize() + newSize := oldSize + int64(delta) + t.SetSize(newSize) +} + +func checkTempTableSize(ctx sessionctx.Context, tmpTable tableutil.TempTable, tblInfo *model.TableInfo) error { + tmpTableSize := tmpTable.GetSize() + if tempTableData := ctx.GetSessionVars().TemporaryTableData; tempTableData != nil { + tmpTableSize += tempTableData.GetTableSize(tblInfo.ID) + } + + if tmpTableSize > ctx.GetSessionVars().TMPTableSize { + return table.ErrTempTableFull.GenWithStackByArgs(tblInfo.Name.O) + } + + return nil +} + +// AddRecord implements table.Table AddRecord interface. +func (t *TableCommon) AddRecord(sctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { + txn, err := sctx.Txn(true) + if err != nil { + return nil, err + } + + var opt table.AddRecordOpt + for _, fn := range opts { + fn.ApplyOn(&opt) + } + + if m := t.Meta(); m.TempTableType != model.TempTableNone { + if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil { + if err := checkTempTableSize(sctx, tmpTable, m); err != nil { + return nil, err + } + defer handleTempTableSize(tmpTable, txn.Size(), txn) + } + } + + var ctx context.Context + if opt.Ctx != nil { + ctx = opt.Ctx + var r tracing.Region + r, ctx = tracing.StartRegionEx(ctx, "table.AddRecord") + defer r.End() + } else { + ctx = context.Background() + } + var hasRecordID bool + cols := t.Cols() + // opt.IsUpdate is a flag for update. + // If handle ID is changed when update, update will remove the old record first, and then call `AddRecord` to add a new record. + // Currently, only insert can set _tidb_rowid, update can not update _tidb_rowid. + if len(r) > len(cols) && !opt.IsUpdate { + // The last value is _tidb_rowid. + recordID = kv.IntHandle(r[len(r)-1].GetInt64()) + hasRecordID = true + } else { + tblInfo := t.Meta() + txn.CacheTableInfo(t.physicalTableID, tblInfo) + if tblInfo.PKIsHandle { + recordID = kv.IntHandle(r[tblInfo.GetPkColInfo().Offset].GetInt64()) + hasRecordID = true + } else if tblInfo.IsCommonHandle { + pkIdx := FindPrimaryIndex(tblInfo) + pkDts := make([]types.Datum, 0, len(pkIdx.Columns)) + for _, idxCol := range pkIdx.Columns { + pkDts = append(pkDts, r[idxCol.Offset]) + } + tablecodec.TruncateIndexValues(tblInfo, pkIdx, pkDts) + var handleBytes []byte + handleBytes, err = codec.EncodeKey(sctx.GetSessionVars().StmtCtx, nil, pkDts...) + if err != nil { + return + } + recordID, err = kv.NewCommonHandle(handleBytes) + if err != nil { + return + } + hasRecordID = true + } + } + if !hasRecordID { + if opt.ReserveAutoID > 0 { + // Reserve a batch of auto ID in the statement context. + // The reserved ID could be used in the future within this statement, by the + // following AddRecord() operation. + // Make the IDs continuous benefit for the performance of TiKV. + stmtCtx := sctx.GetSessionVars().StmtCtx + stmtCtx.BaseRowID, stmtCtx.MaxRowID, err = allocHandleIDs(ctx, sctx, t, uint64(opt.ReserveAutoID)) + if err != nil { + return nil, err + } + } + + recordID, err = AllocHandle(ctx, sctx, t) + if err != nil { + return nil, err + } + } + + var colIDs, binlogColIDs []int64 + var row, binlogRow []types.Datum + var checksums []uint32 + if recordCtx, ok := sctx.Value(addRecordCtxKey).(*CommonAddRecordCtx); ok { + colIDs = recordCtx.colIDs[:0] + row = recordCtx.row[:0] + } else { + colIDs = make([]int64, 0, len(r)) + row = make([]types.Datum, 0, len(r)) + } + memBuffer := txn.GetMemBuffer() + sh := memBuffer.Staging() + defer memBuffer.Cleanup(sh) + + sessVars := sctx.GetSessionVars() + checksumData := t.initChecksumData(sctx, recordID) + needChecksum := len(checksumData) > 0 + + for _, col := range t.Columns { + var value types.Datum + if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { + if needChecksum { + if col.ChangeStateInfo != nil { + // TODO: Check overflow or ignoreTruncate. + v, err := table.CastValue(sctx, r[col.DependencyColumnOffset], col.ColumnInfo, false, false) + if err != nil { + return nil, err + } + checksumData = t.appendInChangeColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &r[col.DependencyColumnOffset], &v) + } else { + v, err := table.GetColOriginDefaultValue(sctx, col.ToInfo()) + if err != nil { + return nil, err + } + checksumData = t.appendNonPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &v) + } + } + continue + } + // In column type change, since we have set the origin default value for changing col, but + // for the new insert statement, we should use the casted value of relative column to insert. + if col.ChangeStateInfo != nil && col.State != model.StatePublic { + // TODO: Check overflow or ignoreTruncate. + value, err = table.CastValue(sctx, r[col.DependencyColumnOffset], col.ColumnInfo, false, false) + if err != nil { + return nil, err + } + if len(r) < len(t.WritableCols()) { + r = append(r, value) + } else { + r[col.Offset] = value + } + row = append(row, value) + colIDs = append(colIDs, col.ID) + checksumData = t.appendInChangeColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &r[col.DependencyColumnOffset], &value) + continue + } + if col.State == model.StatePublic { + value = r[col.Offset] + checksumData = t.appendPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &value) + } else { + // col.ChangeStateInfo must be nil here. + // because `col.State != model.StatePublic` is true here, if col.ChangeStateInfo is not nil, the col should + // be handle by the previous if-block. + + if opt.IsUpdate { + // If `AddRecord` is called by an update, the default value should be handled the update. + value = r[col.Offset] + } else { + // If `AddRecord` is called by an insert and the col is in write only or write reorganization state, we must + // add it with its default value. + value, err = table.GetColOriginDefaultValue(sctx, col.ToInfo()) + if err != nil { + return nil, err + } + // add value to `r` for dirty db in transaction. + // Otherwise when update will panic cause by get value of column in write only state from dirty db. + if col.Offset < len(r) { + r[col.Offset] = value + } else { + r = append(r, value) + } + } + checksumData = t.appendNonPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &value) + } + if !t.canSkip(col, &value) { + colIDs = append(colIDs, col.ID) + row = append(row, value) + } + } + // check data constraint + err = t.CheckRowConstraint(sctx, r) + if err != nil { + return nil, err + } + writeBufs := sessVars.GetWriteStmtBufs() + adjustRowValuesBuf(writeBufs, len(row)) + key := t.RecordKey(recordID) + logutil.BgLogger().Debug("addRecord", + zap.Stringer("key", key)) + sc, rd := sessVars.StmtCtx, &sessVars.RowEncoder + checksums, writeBufs.RowValBuf = t.calcChecksums(sctx, recordID, checksumData, writeBufs.RowValBuf) + writeBufs.RowValBuf, err = tablecodec.EncodeRow(sc, row, colIDs, writeBufs.RowValBuf, writeBufs.AddRowValues, rd, checksums...) + if err != nil { + return nil, err + } + value := writeBufs.RowValBuf + + var setPresume bool + if !sctx.GetSessionVars().StmtCtx.BatchCheck { + if t.meta.TempTableType != model.TempTableNone { + // Always check key for temporary table because it does not write to TiKV + _, err = txn.Get(ctx, key) + } else if sctx.GetSessionVars().LazyCheckKeyNotExists() { + var v []byte + v, err = txn.GetMemBuffer().Get(ctx, key) + if err != nil { + setPresume = true + } + if err == nil && len(v) == 0 { + err = kv.ErrNotExist + } + } else { + _, err = txn.Get(ctx, key) + } + if err == nil { + handleStr := getDuplicateErrorHandleString(t, recordID, r) + return recordID, kv.ErrKeyExists.FastGenByArgs(handleStr, t.Meta().Name.String()+".PRIMARY") + } else if !kv.ErrNotExist.Equal(err) { + return recordID, err + } + } + + if setPresume { + flags := []kv.FlagsOp{kv.SetPresumeKeyNotExists} + if !sessVars.ConstraintCheckInPlacePessimistic && sessVars.TxnCtx.IsPessimistic && sessVars.InTxn() && + !sessVars.InRestrictedSQL && sessVars.ConnectionID > 0 { + flags = append(flags, kv.SetNeedConstraintCheckInPrewrite) + } + err = memBuffer.SetWithFlags(key, value, flags...) + } else { + err = memBuffer.Set(key, value) + } + if err != nil { + return nil, err + } + + failpoint.Inject("addRecordForceAssertExist", func() { + // Assert the key exists while it actually doesn't. This is helpful to test if assertion takes effect. + // Since only the first assertion takes effect, set the injected assertion before setting the correct one to + // override it. + if sctx.GetSessionVars().ConnectionID != 0 { + logutil.BgLogger().Info("force asserting exist on AddRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) + if err = txn.SetAssertion(key, kv.SetAssertExist); err != nil { + failpoint.Return(nil, err) + } + } + }) + if setPresume && !txn.IsPessimistic() { + err = txn.SetAssertion(key, kv.SetAssertUnknown) + } else { + err = txn.SetAssertion(key, kv.SetAssertNotExist) + } + if err != nil { + return nil, err + } + + var createIdxOpts []table.CreateIdxOptFunc + if len(opts) > 0 { + createIdxOpts = make([]table.CreateIdxOptFunc, 0, len(opts)) + for _, fn := range opts { + if raw, ok := fn.(table.CreateIdxOptFunc); ok { + createIdxOpts = append(createIdxOpts, raw) + } + } + } + // Insert new entries into indices. + h, err := t.addIndices(sctx, recordID, r, txn, createIdxOpts) + if err != nil { + return h, err + } + + if err = injectMutationError(t, txn, sh); err != nil { + return nil, err + } + if sessVars.EnableMutationChecker { + if err = CheckDataConsistency(txn, sessVars, t, r, nil, memBuffer, sh); err != nil { + return nil, errors.Trace(err) + } + } + + memBuffer.Release(sh) + + if shouldWriteBinlog(sctx, t.meta) { + // For insert, TiDB and Binlog can use same row and schema. + binlogRow = row + binlogColIDs = colIDs + err = t.addInsertBinlog(sctx, recordID, binlogRow, binlogColIDs) + if err != nil { + return nil, err + } + } + + if sessVars.TxnCtx == nil { + return recordID, nil + } + + if shouldIncreaseTTLMetricCount(t.meta) { + sessVars.TxnCtx.InsertTTLRowsCount += 1 + } + + colSize := make(map[int64]int64, len(r)) + for id, col := range t.Cols() { + size, err := codec.EstimateValueSize(sc, r[id]) + if err != nil { + continue + } + colSize[col.ID] = int64(size) - 1 + } + sessVars.TxnCtx.UpdateDeltaForTable(t.physicalTableID, 1, 1, colSize) + return recordID, nil +} + +// genIndexKeyStr generates index content string representation. +func genIndexKeyStr(colVals []types.Datum) (string, error) { + // Pass pre-composed error to txn. + strVals := make([]string, 0, len(colVals)) + for _, cv := range colVals { + cvs := "NULL" + var err error + if !cv.IsNull() { + cvs, err = types.ToString(cv.GetValue()) + if err != nil { + return "", err + } + } + strVals = append(strVals, cvs) + } + return strings.Join(strVals, "-"), nil +} + +// addIndices adds data into indices. If any key is duplicated, returns the original handle. +func (t *TableCommon) addIndices(sctx sessionctx.Context, recordID kv.Handle, r []types.Datum, txn kv.Transaction, opts []table.CreateIdxOptFunc) (kv.Handle, error) { + writeBufs := sctx.GetSessionVars().GetWriteStmtBufs() + indexVals := writeBufs.IndexValsBuf + skipCheck := sctx.GetSessionVars().StmtCtx.BatchCheck + for _, v := range t.Indices() { + if !IsIndexWritable(v) { + continue + } + if t.meta.IsCommonHandle && v.Meta().Primary { + continue + } + indexVals, err := v.FetchValues(r, indexVals) + if err != nil { + return nil, err + } + var dupErr error + if !skipCheck && v.Meta().Unique { + entryKey, err := genIndexKeyStr(indexVals) + if err != nil { + return nil, err + } + dupErr = kv.ErrKeyExists.FastGenByArgs(entryKey, fmt.Sprintf("%s.%s", v.TableMeta().Name.String(), v.Meta().Name.String())) + } + rsData := TryGetHandleRestoredDataWrapper(t.meta, r, nil, v.Meta()) + if dupHandle, err := v.Create(sctx, txn, indexVals, recordID, rsData, opts...); err != nil { + if kv.ErrKeyExists.Equal(err) { + return dupHandle, dupErr + } + return nil, err + } + } + // save the buffer, multi rows insert can use it. + writeBufs.IndexValsBuf = indexVals + return nil, nil +} + +// RowWithCols is used to get the corresponding column datum values with the given handle. +func RowWithCols(t table.Table, ctx sessionctx.Context, h kv.Handle, cols []*table.Column) ([]types.Datum, error) { + // Get raw row data from kv. + key := tablecodec.EncodeRecordKey(t.RecordPrefix(), h) + txn, err := ctx.Txn(true) + if err != nil { + return nil, err + } + value, err := txn.Get(context.TODO(), key) + if err != nil { + return nil, err + } + v, _, err := DecodeRawRowData(ctx, t.Meta(), h, cols, value) + if err != nil { + return nil, err + } + return v, nil +} + +func containFullColInHandle(meta *model.TableInfo, col *table.Column) (containFullCol bool, idxInHandle int) { + pkIdx := FindPrimaryIndex(meta) + for i, idxCol := range pkIdx.Columns { + if meta.Columns[idxCol.Offset].ID == col.ID { + idxInHandle = i + containFullCol = idxCol.Length == types.UnspecifiedLength + return + } + } + return +} + +// DecodeRawRowData decodes raw row data into a datum slice and a (columnID:columnValue) map. +func DecodeRawRowData(ctx sessionctx.Context, meta *model.TableInfo, h kv.Handle, cols []*table.Column, + value []byte) ([]types.Datum, map[int64]types.Datum, error) { + v := make([]types.Datum, len(cols)) + colTps := make(map[int64]*types.FieldType, len(cols)) + prefixCols := make(map[int64]struct{}) + for i, col := range cols { + if col == nil { + continue + } + if col.IsPKHandleColumn(meta) { + if mysql.HasUnsignedFlag(col.GetFlag()) { + v[i].SetUint64(uint64(h.IntValue())) + } else { + v[i].SetInt64(h.IntValue()) + } + continue + } + if col.IsCommonHandleColumn(meta) && !types.NeedRestoredData(&col.FieldType) { + if containFullCol, idxInHandle := containFullColInHandle(meta, col); containFullCol { + dtBytes := h.EncodedCol(idxInHandle) + _, dt, err := codec.DecodeOne(dtBytes) + if err != nil { + return nil, nil, err + } + dt, err = tablecodec.Unflatten(dt, &col.FieldType, ctx.GetSessionVars().Location()) + if err != nil { + return nil, nil, err + } + v[i] = dt + continue + } + prefixCols[col.ID] = struct{}{} + } + colTps[col.ID] = &col.FieldType + } + rowMap, err := tablecodec.DecodeRowToDatumMap(value, colTps, ctx.GetSessionVars().Location()) + if err != nil { + return nil, rowMap, err + } + defaultVals := make([]types.Datum, len(cols)) + for i, col := range cols { + if col == nil { + continue + } + if col.IsPKHandleColumn(meta) || (col.IsCommonHandleColumn(meta) && !types.NeedRestoredData(&col.FieldType)) { + if _, isPrefix := prefixCols[col.ID]; !isPrefix { + continue + } + } + ri, ok := rowMap[col.ID] + if ok { + v[i] = ri + continue + } + if col.IsVirtualGenerated() { + continue + } + if col.ChangeStateInfo != nil { + v[i], _, err = GetChangingColVal(ctx, cols, col, rowMap, defaultVals) + } else { + v[i], err = GetColDefaultValue(ctx, col, defaultVals) + } + if err != nil { + return nil, rowMap, err + } + } + return v, rowMap, nil +} + +// GetChangingColVal gets the changing column value when executing "modify/change column" statement. +// For statement like update-where, it will fetch the old row out and insert it into kv again. +// Since update statement can see the writable columns, it is responsible for the casting relative column / get the fault value here. +// old row : a-b-[nil] +// new row : a-b-[a'/default] +// Thus the writable new row is corresponding to Write-Only constraints. +func GetChangingColVal(ctx sessionctx.Context, cols []*table.Column, col *table.Column, rowMap map[int64]types.Datum, defaultVals []types.Datum) (_ types.Datum, isDefaultVal bool, err error) { + relativeCol := cols[col.ChangeStateInfo.DependencyColumnOffset] + idxColumnVal, ok := rowMap[relativeCol.ID] + if ok { + idxColumnVal, err = table.CastValue(ctx, idxColumnVal, col.ColumnInfo, false, false) + // TODO: Consider sql_mode and the error msg(encounter this error check whether to rollback). + if err != nil { + return idxColumnVal, false, errors.Trace(err) + } + return idxColumnVal, false, nil + } + + idxColumnVal, err = GetColDefaultValue(ctx, col, defaultVals) + if err != nil { + return idxColumnVal, false, errors.Trace(err) + } + + return idxColumnVal, true, nil +} + +// RemoveRecord implements table.Table RemoveRecord interface. +func (t *TableCommon) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { + txn, err := ctx.Txn(true) + if err != nil { + return err + } + + memBuffer := txn.GetMemBuffer() + sh := memBuffer.Staging() + defer memBuffer.Cleanup(sh) + + logutil.BgLogger().Debug("RemoveRecord", + zap.Stringer("key", t.RecordKey(h))) + err = t.removeRowData(ctx, h) + if err != nil { + return err + } + + if m := t.Meta(); m.TempTableType != model.TempTableNone { + if tmpTable := addTemporaryTable(ctx, m); tmpTable != nil { + if err := checkTempTableSize(ctx, tmpTable, m); err != nil { + return err + } + defer handleTempTableSize(tmpTable, txn.Size(), txn) + } + } + + // The table has non-public column and this column is doing the operation of "modify/change column". + if len(t.Columns) > len(r) && t.Columns[len(r)].ChangeStateInfo != nil { + // The changing column datum derived from related column should be casted here. + // Otherwise, the existed changing indexes will not be deleted. + relatedColDatum := r[t.Columns[len(r)].ChangeStateInfo.DependencyColumnOffset] + value, err := table.CastValue(ctx, relatedColDatum, t.Columns[len(r)].ColumnInfo, false, false) + if err != nil { + logutil.BgLogger().Info("remove record cast value failed", zap.Any("col", t.Columns[len(r)]), + zap.String("handle", h.String()), zap.Any("val", relatedColDatum), zap.Error(err)) + return err + } + r = append(r, value) + } + err = t.removeRowIndices(ctx, h, r) + if err != nil { + return err + } + + sessVars := ctx.GetSessionVars() + sc := sessVars.StmtCtx + if err = injectMutationError(t, txn, sh); err != nil { + return err + } + if sessVars.EnableMutationChecker { + if err = CheckDataConsistency(txn, sessVars, t, nil, r, memBuffer, sh); err != nil { + return errors.Trace(err) + } + } + memBuffer.Release(sh) + + if shouldWriteBinlog(ctx, t.meta) { + cols := t.Cols() + colIDs := make([]int64, 0, len(cols)+1) + for _, col := range cols { + colIDs = append(colIDs, col.ID) + } + var binlogRow []types.Datum + if !t.meta.PKIsHandle && !t.meta.IsCommonHandle { + colIDs = append(colIDs, model.ExtraHandleID) + binlogRow = make([]types.Datum, 0, len(r)+1) + binlogRow = append(binlogRow, r...) + handleData, err := h.Data() + if err != nil { + return err + } + binlogRow = append(binlogRow, handleData...) + } else { + binlogRow = r + } + err = t.addDeleteBinlog(ctx, binlogRow, colIDs) + } + if ctx.GetSessionVars().TxnCtx == nil { + return nil + } + colSize := make(map[int64]int64, len(t.Cols())) + for id, col := range t.Cols() { + size, err := codec.EstimateValueSize(sc, r[id]) + if err != nil { + continue + } + colSize[col.ID] = -int64(size - 1) + } + ctx.GetSessionVars().TxnCtx.UpdateDeltaForTable(t.physicalTableID, -1, 1, colSize) + return err +} + +func (t *TableCommon) addInsertBinlog(ctx sessionctx.Context, h kv.Handle, row []types.Datum, colIDs []int64) error { + mutation := t.getMutation(ctx) + handleData, err := h.Data() + if err != nil { + return err + } + pk, err := codec.EncodeValue(ctx.GetSessionVars().StmtCtx, nil, handleData...) + if err != nil { + return err + } + value, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, row, colIDs, nil, nil) + if err != nil { + return err + } + bin := append(pk, value...) + mutation.InsertedRows = append(mutation.InsertedRows, bin) + mutation.Sequence = append(mutation.Sequence, binlog.MutationType_Insert) + return nil +} + +func (t *TableCommon) addUpdateBinlog(ctx sessionctx.Context, oldRow, newRow []types.Datum, colIDs []int64) error { + old, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, oldRow, colIDs, nil, nil) + if err != nil { + return err + } + newVal, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, newRow, colIDs, nil, nil) + if err != nil { + return err + } + bin := append(old, newVal...) + mutation := t.getMutation(ctx) + mutation.UpdatedRows = append(mutation.UpdatedRows, bin) + mutation.Sequence = append(mutation.Sequence, binlog.MutationType_Update) + return nil +} + +func (t *TableCommon) addDeleteBinlog(ctx sessionctx.Context, r []types.Datum, colIDs []int64) error { + data, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, r, colIDs, nil, nil) + if err != nil { + return err + } + mutation := t.getMutation(ctx) + mutation.DeletedRows = append(mutation.DeletedRows, data) + mutation.Sequence = append(mutation.Sequence, binlog.MutationType_DeleteRow) + return nil +} + +func writeSequenceUpdateValueBinlog(sctx sessionctx.Context, db, sequence string, end int64) error { + // 1: when sequenceCommon update the local cache passively. + // 2: When sequenceCommon setval to the allocator actively. + // Both of this two case means the upper bound the sequence has changed in meta, which need to write the binlog + // to the downstream. + // Sequence sends `select setval(seq, num)` sql string to downstream via `setDDLBinlog`, which is mocked as a DDL binlog. + binlogCli := sctx.GetSessionVars().BinlogClient + sqlMode := sctx.GetSessionVars().SQLMode + sequenceFullName := stringutil.Escape(db, sqlMode) + "." + stringutil.Escape(sequence, sqlMode) + sql := "select setval(" + sequenceFullName + ", " + strconv.FormatInt(end, 10) + ")" + + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) + err := kv.RunInNewTxn(ctx, sctx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + mockJobID, err := m.GenGlobalID() + if err != nil { + return err + } + binloginfo.SetDDLBinlog(binlogCli, txn, mockJobID, int32(model.StatePublic), sql) + return nil + }) + return err +} + +func (t *TableCommon) removeRowData(ctx sessionctx.Context, h kv.Handle) error { + // Remove row data. + txn, err := ctx.Txn(true) + if err != nil { + return err + } + + key := t.RecordKey(h) + failpoint.Inject("removeRecordForceAssertNotExist", func() { + // Assert the key doesn't exist while it actually exists. This is helpful to test if assertion takes effect. + // Since only the first assertion takes effect, set the injected assertion before setting the correct one to + // override it. + if ctx.GetSessionVars().ConnectionID != 0 { + logutil.BgLogger().Info("force asserting not exist on RemoveRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) + if err = txn.SetAssertion(key, kv.SetAssertNotExist); err != nil { + failpoint.Return(err) + } + } + }) + if t.shouldAssert(ctx) { + err = txn.SetAssertion(key, kv.SetAssertExist) + } else { + err = txn.SetAssertion(key, kv.SetAssertUnknown) + } + if err != nil { + return err + } + return txn.Delete(key) +} + +// removeRowIndices removes all the indices of a row. +func (t *TableCommon) removeRowIndices(ctx sessionctx.Context, h kv.Handle, rec []types.Datum) error { + txn, err := ctx.Txn(true) + if err != nil { + return err + } + for _, v := range t.deletableIndices() { + if v.Meta().Primary && (t.Meta().IsCommonHandle || t.Meta().PKIsHandle) { + continue + } + vals, err := v.FetchValues(rec, nil) + if err != nil { + logutil.BgLogger().Info("remove row index failed", zap.Any("index", v.Meta()), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("handle", h.String()), zap.Any("record", rec), zap.Error(err)) + return err + } + if err = v.Delete(ctx.GetSessionVars().StmtCtx, txn, vals, h); err != nil { + if v.Meta().State != model.StatePublic && kv.ErrNotExist.Equal(err) { + // If the index is not in public state, we may have not created the index, + // or already deleted the index, so skip ErrNotExist error. + logutil.BgLogger().Debug("row index not exists", zap.Any("index", v.Meta()), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("handle", h.String())) + continue + } + return err + } + } + return nil +} + +// removeRowIndex implements table.Table RemoveRowIndex interface. +func (t *TableCommon) removeRowIndex(sc *stmtctx.StatementContext, h kv.Handle, vals []types.Datum, idx table.Index, txn kv.Transaction) error { + return idx.Delete(sc, txn, vals, h) +} + +// buildIndexForRow implements table.Table BuildIndexForRow interface. +func (t *TableCommon) buildIndexForRow(ctx sessionctx.Context, h kv.Handle, vals []types.Datum, newData []types.Datum, idx table.Index, txn kv.Transaction, untouched bool, popts ...table.CreateIdxOptFunc) error { + var opts []table.CreateIdxOptFunc + opts = append(opts, popts...) + if untouched { + opts = append(opts, table.IndexIsUntouched) + } + rsData := TryGetHandleRestoredDataWrapper(t.meta, newData, nil, idx.Meta()) + if _, err := idx.Create(ctx, txn, vals, h, rsData, opts...); err != nil { + if kv.ErrKeyExists.Equal(err) { + // Make error message consistent with MySQL. + entryKey, err1 := genIndexKeyStr(vals) + if err1 != nil { + // if genIndexKeyStr failed, return the original error. + return err + } + + return kv.ErrKeyExists.FastGenByArgs(entryKey, fmt.Sprintf("%s.%s", idx.TableMeta().Name.String(), idx.Meta().Name.String())) + } + return err + } + return nil +} + +// IterRecords iterates records in the table and calls fn. +func IterRecords(t table.Table, ctx sessionctx.Context, cols []*table.Column, + fn table.RecordIterFunc) error { + prefix := t.RecordPrefix() + txn, err := ctx.Txn(true) + if err != nil { + return err + } + + startKey := tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) + it, err := txn.Iter(startKey, prefix.PrefixNext()) + if err != nil { + return err + } + defer it.Close() + + if !it.Valid() { + return nil + } + + logutil.BgLogger().Debug("iterate records", zap.ByteString("startKey", startKey), zap.ByteString("key", it.Key()), zap.ByteString("value", it.Value())) + + colMap := make(map[int64]*types.FieldType, len(cols)) + for _, col := range cols { + colMap[col.ID] = &col.FieldType + } + defaultVals := make([]types.Datum, len(cols)) + for it.Valid() && it.Key().HasPrefix(prefix) { + // first kv pair is row lock information. + // TODO: check valid lock + // get row handle + handle, err := tablecodec.DecodeRowKey(it.Key()) + if err != nil { + return err + } + rowMap, err := tablecodec.DecodeRowToDatumMap(it.Value(), colMap, ctx.GetSessionVars().Location()) + if err != nil { + return err + } + pkIds, decodeLoc := TryGetCommonPkColumnIds(t.Meta()), ctx.GetSessionVars().Location() + data := make([]types.Datum, len(cols)) + for _, col := range cols { + if col.IsPKHandleColumn(t.Meta()) { + if mysql.HasUnsignedFlag(col.GetFlag()) { + data[col.Offset].SetUint64(uint64(handle.IntValue())) + } else { + data[col.Offset].SetInt64(handle.IntValue()) + } + continue + } else if mysql.HasPriKeyFlag(col.GetFlag()) { + data[col.Offset], err = tryDecodeColumnFromCommonHandle(col, handle, pkIds, decodeLoc) + if err != nil { + return err + } + continue + } + if _, ok := rowMap[col.ID]; ok { + data[col.Offset] = rowMap[col.ID] + continue + } + data[col.Offset], err = GetColDefaultValue(ctx, col, defaultVals) + if err != nil { + return err + } + } + more, err := fn(handle, data, cols) + if !more || err != nil { + return err + } + + rk := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) + err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) + if err != nil { + return err + } + } + + return nil +} + +func tryDecodeColumnFromCommonHandle(col *table.Column, handle kv.Handle, pkIds []int64, decodeLoc *time.Location) (types.Datum, error) { + for i, hid := range pkIds { + if hid != col.ID { + continue + } + _, d, err := codec.DecodeOne(handle.EncodedCol(i)) + if err != nil { + return types.Datum{}, errors.Trace(err) + } + if d, err = tablecodec.Unflatten(d, &col.FieldType, decodeLoc); err != nil { + return types.Datum{}, err + } + return d, nil + } + return types.Datum{}, nil +} + +// GetColDefaultValue gets a column default value. +// The defaultVals is used to avoid calculating the default value multiple times. +func GetColDefaultValue(ctx sessionctx.Context, col *table.Column, defaultVals []types.Datum) ( + colVal types.Datum, err error) { + if col.GetOriginDefaultValue() == nil && mysql.HasNotNullFlag(col.GetFlag()) { + return colVal, errors.New("Miss column") + } + if defaultVals[col.Offset].IsNull() { + colVal, err = table.GetColOriginDefaultValue(ctx, col.ToInfo()) + if err != nil { + return colVal, err + } + defaultVals[col.Offset] = colVal + } else { + colVal = defaultVals[col.Offset] + } + + return colVal, nil +} + +// AllocHandle allocate a new handle. +// A statement could reserve some ID in the statement context, try those ones first. +func AllocHandle(ctx context.Context, sctx sessionctx.Context, t table.Table) (kv.Handle, error) { + if sctx != nil { + if stmtCtx := sctx.GetSessionVars().StmtCtx; stmtCtx != nil { + // First try to alloc if the statement has reserved auto ID. + if stmtCtx.BaseRowID < stmtCtx.MaxRowID { + stmtCtx.BaseRowID++ + return kv.IntHandle(stmtCtx.BaseRowID), nil + } + } + } + + _, rowID, err := allocHandleIDs(ctx, sctx, t, 1) + return kv.IntHandle(rowID), err +} + +func allocHandleIDs(ctx context.Context, sctx sessionctx.Context, t table.Table, n uint64) (int64, int64, error) { + meta := t.Meta() + base, maxID, err := t.Allocators(sctx).Get(autoid.RowIDAllocType).Alloc(ctx, n, 1, 1) + if err != nil { + return 0, 0, err + } + if meta.ShardRowIDBits > 0 { + shardFmt := autoid.NewShardIDFormat(types.NewFieldType(mysql.TypeLonglong), meta.ShardRowIDBits, autoid.RowIDBitLength) + // Use max record ShardRowIDBits to check overflow. + if OverflowShardBits(maxID, meta.MaxShardRowIDBits, autoid.RowIDBitLength, true) { + // If overflow, the rowID may be duplicated. For examples, + // t.meta.ShardRowIDBits = 4 + // rowID = 0010111111111111111111111111111111111111111111111111111111111111 + // shard = 0100000000000000000000000000000000000000000000000000000000000000 + // will be duplicated with: + // rowID = 0100111111111111111111111111111111111111111111111111111111111111 + // shard = 0010000000000000000000000000000000000000000000000000000000000000 + return 0, 0, autoid.ErrAutoincReadFailed + } + shard := sctx.GetSessionVars().GetCurrentShard(int(n)) + base = shardFmt.Compose(shard, base) + maxID = shardFmt.Compose(shard, maxID) + } + return base, maxID, nil +} + +// OverflowShardBits checks whether the recordID overflow `1<<(typeBitsLength-shardRowIDBits-1) -1`. +func OverflowShardBits(recordID int64, shardRowIDBits uint64, typeBitsLength uint64, reservedSignBit bool) bool { + var signBit uint64 + if reservedSignBit { + signBit = 1 + } + mask := (1< 0 +} + +// Allocators implements table.Table Allocators interface. +func (t *TableCommon) Allocators(ctx sessionctx.Context) autoid.Allocators { + if ctx == nil { + return t.allocs + } else if ctx.GetSessionVars().IDAllocator == nil { + // Use an independent allocator for global temporary tables. + if t.meta.TempTableType == model.TempTableGlobal { + if alloc := ctx.GetSessionVars().GetTemporaryTable(t.meta).GetAutoIDAllocator(); alloc != nil { + return autoid.NewAllocators(false, alloc) + } + // If the session is not in a txn, for example, in "show create table", use the original allocator. + // Otherwise the would be a nil pointer dereference. + } + return t.allocs + } + + // Replace the row id allocator with the one in session variables. + sessAlloc := ctx.GetSessionVars().IDAllocator + allocs := t.allocs.Allocs + retAllocs := make([]autoid.Allocator, 0, len(allocs)) + copy(retAllocs, allocs) + + overwritten := false + for i, a := range retAllocs { + if a.GetType() == autoid.RowIDAllocType { + retAllocs[i] = sessAlloc + overwritten = true + break + } + } + if !overwritten { + retAllocs = append(retAllocs, sessAlloc) + } + return autoid.NewAllocators(t.allocs.SepAutoInc, retAllocs...) +} + +// Type implements table.Table Type interface. +func (t *TableCommon) Type() table.Type { + return table.NormalTable +} + +func shouldWriteBinlog(ctx sessionctx.Context, tblInfo *model.TableInfo) bool { + failpoint.Inject("forceWriteBinlog", func() { + // Just to cover binlog related code in this package, since the `BinlogClient` is + // still nil, mutations won't be written to pump on commit. + failpoint.Return(true) + }) + if ctx.GetSessionVars().BinlogClient == nil { + return false + } + if tblInfo.TempTableType != model.TempTableNone { + return false + } + return !ctx.GetSessionVars().InRestrictedSQL +} + +func shouldIncreaseTTLMetricCount(tblInfo *model.TableInfo) bool { + return tblInfo.TTLInfo != nil +} + +func (t *TableCommon) getMutation(ctx sessionctx.Context) *binlog.TableMutation { + return ctx.StmtGetMutation(t.tableID) +} + +// initChecksumData allocates data for checksum calculation, returns nil if checksum is disabled or unavailable. The +// length of returned data can be considered as the number of checksums we need to write. +func (t *TableCommon) initChecksumData(sctx sessionctx.Context, h kv.Handle) [][]rowcodec.ColData { + if !sctx.GetSessionVars().IsRowLevelChecksumEnabled() { + return nil + } + numNonPubCols := len(t.Columns) - len(t.Cols()) + if numNonPubCols > 1 { + logWithContext(sctx, logutil.BgLogger().Warn, + "skip checksum since the number of non-public columns is greater than 1", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("cols", t.meta.Columns)) + return nil + } + return make([][]rowcodec.ColData, 1+numNonPubCols) +} + +// calcChecksums calculates the checksums of input data. The arg `buf` is used to hold the temporary encoded col data +// and it will be reset for each col, so do NOT pass a buf that contains data you may use later. If the capacity of +// `buf` is enough, it gets returned directly, otherwise a new bytes with larger capacity will be returned, and you can +// hold the returned buf for later use (to avoid memory allocation). +func (t *TableCommon) calcChecksums(sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, buf []byte) ([]uint32, []byte) { + if len(data) == 0 { + return nil, buf + } + checksums := make([]uint32, len(data)) + for i, cols := range data { + row := rowcodec.RowData{Cols: cols, Data: buf} + if !sort.IsSorted(row) { + sort.Sort(row) + } + checksum, err := row.Checksum() + buf = row.Data + if err != nil { + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to encode error", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Error(err)) + return nil, buf + } + checksums[i] = checksum + } + return checksums, buf +} + +// appendPublicColForChecksum appends a public column data for checksum. If the column is in changing, that is, it's the +// old column of an on-going modify-column ddl, then skip it since it will be handle by `appendInChangeColForChecksum`. +func (t *TableCommon) appendPublicColForChecksum( + sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, d *types.Datum, +) [][]rowcodec.ColData { + if len(data) == 0 { // no need for checksum + return nil + } + if c.State != model.StatePublic { // assert col is public + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to inconsistent column state", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) + return nil + } + for _, offset := range t.dependencyColumnOffsets { + if c.Offset == offset { + // the col is in changing, skip it. + return data + } + } + // calculate the checksum with this col + data[0] = appendColForChecksum(data[0], t, c, d) + if len(data) > 1 { + // calculate the extra checksum with this col + data[1] = appendColForChecksum(data[1], t, c, d) + } + return data +} + +// appendNonPublicColForChecksum appends a non-public (but not in-changing) column data for checksum. Two checksums are +// required because there is a non-public column. The first checksum should be calculate with the original (or default) +// value of this column. The extra checksum shall be calculated without this non-public column, thus nothing to do with +// data[1]. +func (t *TableCommon) appendNonPublicColForChecksum( + sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, d *types.Datum, +) [][]rowcodec.ColData { + if size := len(data); size == 0 { // no need for checksum + return nil + } else if size == 1 { // assert that 2 checksums are required + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to inconsistent length of column data", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID)) + return nil + } + if c.State == model.StatePublic || c.ChangeStateInfo != nil { // assert col is not public and is not in changing + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to inconsistent column state", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) + return nil + } + data[0] = appendColForChecksum(data[0], t, c, d) + + return data +} + +// appendInChangeColForChecksum appends an in-changing column data for checksum. Two checksums are required because +// there is a non-public column. The first checksum should be calculate with the old version of this column and the extra +// checksum should be calculated with the new version of column. +func (t *TableCommon) appendInChangeColForChecksum( + sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, oldVal *types.Datum, newVal *types.Datum, +) [][]rowcodec.ColData { + if size := len(data); size == 0 { // no need for checksum + return nil + } else if size == 1 { // assert that 2 checksums are required + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to inconsistent length of column data", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID)) + return nil + } + if c.State == model.StatePublic || c.ChangeStateInfo == nil { // assert col is not public and is in changing + logWithContext(sctx, logutil.BgLogger().Error, + "skip checksum due to inconsistent column state", + zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) + return nil + } + // calculate the checksum with the old version of col + data[0] = appendColForChecksum(data[0], t, t.meta.Columns[c.DependencyColumnOffset], oldVal) + // calculate the extra checksum with the new version of col + data[1] = appendColForChecksum(data[1], t, c, newVal) + + return data +} + +func appendColForChecksum(dst []rowcodec.ColData, t *TableCommon, c *model.ColumnInfo, d *types.Datum) []rowcodec.ColData { + if c.IsGenerated() && !c.GeneratedStored { + return dst + } + if dst == nil { + dst = make([]rowcodec.ColData, 0, len(t.Columns)) + } + return append(dst, rowcodec.ColData{ColumnInfo: c, Datum: d}) +} + +func logWithContext(sctx sessionctx.Context, log func(msg string, fields ...zap.Field), msg string, fields ...zap.Field) { + sessVars := sctx.GetSessionVars() + ctxFields := make([]zap.Field, 0, len(fields)+2) + ctxFields = append(ctxFields, zap.Uint64("conn", sessVars.ConnectionID)) + if sessVars.TxnCtx != nil { + ctxFields = append(ctxFields, zap.Uint64("txnStartTS", sessVars.TxnCtx.StartTS)) + } + ctxFields = append(ctxFields, fields...) + log(msg, ctxFields...) +} + +func (t *TableCommon) canSkip(col *table.Column, value *types.Datum) bool { + return CanSkip(t.Meta(), col, value) +} + +// CanSkip is for these cases, we can skip the columns in encoded row: +// 1. the column is included in primary key; +// 2. the column's default value is null, and the value equals to that but has no origin default; +// 3. the column is virtual generated. +func CanSkip(info *model.TableInfo, col *table.Column, value *types.Datum) bool { + if col.IsPKHandleColumn(info) { + return true + } + if col.IsCommonHandleColumn(info) { + pkIdx := FindPrimaryIndex(info) + for _, idxCol := range pkIdx.Columns { + if info.Columns[idxCol.Offset].ID != col.ID { + continue + } + canSkip := idxCol.Length == types.UnspecifiedLength + canSkip = canSkip && !types.NeedRestoredData(&col.FieldType) + return canSkip + } + } + if col.GetDefaultValue() == nil && value.IsNull() && col.GetOriginDefaultValue() == nil { + return true + } + if col.IsVirtualGenerated() { + return true + } + return false +} + +// canSkipUpdateBinlog checks whether the column can be skipped or not. +func (t *TableCommon) canSkipUpdateBinlog(col *table.Column, value types.Datum) bool { + return col.IsVirtualGenerated() +} + +// FindIndexByColName returns a public table index containing only one column named `name`. +func FindIndexByColName(t table.Table, name string) table.Index { + for _, idx := range t.Indices() { + // only public index can be read. + if idx.Meta().State != model.StatePublic { + continue + } + + if len(idx.Meta().Columns) == 1 && strings.EqualFold(idx.Meta().Columns[0].Name.L, name) { + return idx + } + } + return nil +} + +func getDuplicateErrorHandleString(t table.Table, handle kv.Handle, row []types.Datum) string { + if handle.IsInt() { + return kv.GetDuplicateErrorHandleString(handle) + } + var pk table.Index + for _, idx := range t.Indices() { + if idx.Meta().Primary { + pk = idx + break + } + } + if pk == nil { + return kv.GetDuplicateErrorHandleString(handle) + } + var err error + str := make([]string, len(pk.Meta().Columns)) + for i, col := range pk.Meta().Columns { + str[i], err = row[col.Offset].ToString() + if err != nil { + return kv.GetDuplicateErrorHandleString(handle) + } + } + return strings.Join(str, "-") +} + +func init() { + table.TableFromMeta = TableFromMeta + table.MockTableFromMeta = MockTableFromMeta + tableutil.TempTableFromMeta = TempTableFromMeta +} + +// sequenceCommon cache the sequence value. +// `alter sequence` will invalidate the cached range. +// `setval` will recompute the start position of cached value. +type sequenceCommon struct { + meta *model.SequenceInfo + // base < end when increment > 0. + // base > end when increment < 0. + end int64 + base int64 + // round is used to count the cycle times. + round int64 + mu sync.RWMutex +} + +// GetSequenceBaseEndRound is used in test. +func (s *sequenceCommon) GetSequenceBaseEndRound() (int64, int64, int64) { + s.mu.RLock() + defer s.mu.RUnlock() + return s.base, s.end, s.round +} + +// GetSequenceNextVal implements util.SequenceTable GetSequenceNextVal interface. +// Caching the sequence value in table, we can easily be notified with the cache empty, +// and write the binlogInfo in table level rather than in allocator. +func (t *TableCommon) GetSequenceNextVal(ctx interface{}, dbName, seqName string) (nextVal int64, err error) { + seq := t.sequence + if seq == nil { + // TODO: refine the error. + return 0, errors.New("sequenceCommon is nil") + } + seq.mu.Lock() + defer seq.mu.Unlock() + + err = func() error { + // Check if need to update the cache batch from storage. + // Because seq.base is not always the last allocated value (may be set by setval()). + // So we should try to seek the next value in cache (not just add increment to seq.base). + var ( + updateCache bool + offset int64 + ok bool + ) + if seq.base == seq.end { + // There is no cache yet. + updateCache = true + } else { + // Seek the first valid value in cache. + offset = seq.getOffset() + if seq.meta.Increment > 0 { + nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.base, seq.end) + } else { + nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.end, seq.base) + } + if !ok { + updateCache = true + } + } + if !updateCache { + return nil + } + // Update batch alloc from kv storage. + sequenceAlloc, err1 := getSequenceAllocator(t.allocs) + if err1 != nil { + return err1 + } + var base, end, round int64 + base, end, round, err1 = sequenceAlloc.AllocSeqCache() + if err1 != nil { + return err1 + } + // Only update local cache when alloc succeed. + seq.base = base + seq.end = end + seq.round = round + // write sequence binlog to the pumpClient. + if ctx.(sessionctx.Context).GetSessionVars().BinlogClient != nil { + err = writeSequenceUpdateValueBinlog(ctx.(sessionctx.Context), dbName, seqName, seq.end) + if err != nil { + return err + } + } + // Seek the first valid value in new cache. + // Offset may have changed cause the round is updated. + offset = seq.getOffset() + if seq.meta.Increment > 0 { + nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.base, seq.end) + } else { + nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.end, seq.base) + } + if !ok { + return errors.New("can't find the first value in sequence cache") + } + return nil + }() + // Sequence alloc in kv store error. + if err != nil { + if err == autoid.ErrAutoincReadFailed { + return 0, table.ErrSequenceHasRunOut.GenWithStackByArgs(dbName, seqName) + } + return 0, err + } + seq.base = nextVal + return nextVal, nil +} + +// SetSequenceVal implements util.SequenceTable SetSequenceVal interface. +// The returned bool indicates the newVal is already under the base. +func (t *TableCommon) SetSequenceVal(ctx interface{}, newVal int64, dbName, seqName string) (int64, bool, error) { + seq := t.sequence + if seq == nil { + // TODO: refine the error. + return 0, false, errors.New("sequenceCommon is nil") + } + seq.mu.Lock() + defer seq.mu.Unlock() + + if seq.meta.Increment > 0 { + if newVal <= t.sequence.base { + return 0, true, nil + } + if newVal <= t.sequence.end { + t.sequence.base = newVal + return newVal, false, nil + } + } else { + if newVal >= t.sequence.base { + return 0, true, nil + } + if newVal >= t.sequence.end { + t.sequence.base = newVal + return newVal, false, nil + } + } + + // Invalid the current cache. + t.sequence.base = t.sequence.end + + // Rebase from kv storage. + sequenceAlloc, err := getSequenceAllocator(t.allocs) + if err != nil { + return 0, false, err + } + res, alreadySatisfied, err := sequenceAlloc.RebaseSeq(newVal) + if err != nil { + return 0, false, err + } + if !alreadySatisfied { + // Write sequence binlog to the pumpClient. + if ctx.(sessionctx.Context).GetSessionVars().BinlogClient != nil { + err = writeSequenceUpdateValueBinlog(ctx.(sessionctx.Context), dbName, seqName, seq.end) + if err != nil { + return 0, false, err + } + } + } + // Record the current end after setval succeed. + // Consider the following case. + // create sequence seq + // setval(seq, 100) setval(seq, 50) + // Because no cache (base, end keep 0), so the second setval won't return NULL. + t.sequence.base, t.sequence.end = newVal, newVal + return res, alreadySatisfied, nil +} + +// getOffset is used in under GetSequenceNextVal & SetSequenceVal, which mu is locked. +func (s *sequenceCommon) getOffset() int64 { + offset := s.meta.Start + if s.meta.Cycle && s.round > 0 { + if s.meta.Increment > 0 { + offset = s.meta.MinValue + } else { + offset = s.meta.MaxValue + } + } + return offset +} + +// GetSequenceID implements util.SequenceTable GetSequenceID interface. +func (t *TableCommon) GetSequenceID() int64 { + return t.tableID +} + +// GetSequenceCommon is used in test to get sequenceCommon. +func (t *TableCommon) GetSequenceCommon() *sequenceCommon { + return t.sequence +} + +// TryGetHandleRestoredDataWrapper tries to get the restored data for handle if needed. The argument can be a slice or a map. +func TryGetHandleRestoredDataWrapper(tblInfo *model.TableInfo, row []types.Datum, rowMap map[int64]types.Datum, idx *model.IndexInfo) []types.Datum { + if !collate.NewCollationEnabled() || !tblInfo.IsCommonHandle || tblInfo.CommonHandleVersion == 0 { + return nil + } + rsData := make([]types.Datum, 0, 4) + pkIdx := FindPrimaryIndex(tblInfo) + for _, pkIdxCol := range pkIdx.Columns { + pkCol := tblInfo.Columns[pkIdxCol.Offset] + if !types.NeedRestoredData(&pkCol.FieldType) { + continue + } + var datum types.Datum + if len(rowMap) > 0 { + datum = rowMap[pkCol.ID] + } else { + datum = row[pkCol.Offset] + } + TryTruncateRestoredData(&datum, pkCol, pkIdxCol, idx) + ConvertDatumToTailSpaceCount(&datum, pkCol) + rsData = append(rsData, datum) + } + return rsData +} + +// TryTruncateRestoredData tries to truncate index values. +// Says that primary key(a (8)), +// For index t(a), don't truncate the value. +// For index t(a(9)), truncate to a(9). +// For index t(a(7)), truncate to a(8). +func TryTruncateRestoredData(datum *types.Datum, pkCol *model.ColumnInfo, + pkIdxCol *model.IndexColumn, idx *model.IndexInfo) { + truncateTargetCol := pkIdxCol + for _, idxCol := range idx.Columns { + if idxCol.Offset == pkIdxCol.Offset { + truncateTargetCol = maxIndexLen(pkIdxCol, idxCol) + break + } + } + tablecodec.TruncateIndexValue(datum, truncateTargetCol, pkCol) +} + +// ConvertDatumToTailSpaceCount converts a string datum to an int datum that represents the tail space count. +func ConvertDatumToTailSpaceCount(datum *types.Datum, col *model.ColumnInfo) { + if collate.IsBinCollation(col.GetCollate()) { + *datum = types.NewIntDatum(stringutil.GetTailSpaceCount(datum.GetString())) + } +} + +func maxIndexLen(idxA, idxB *model.IndexColumn) *model.IndexColumn { + if idxA.Length == types.UnspecifiedLength { + return idxA + } + if idxB.Length == types.UnspecifiedLength { + return idxB + } + if idxA.Length > idxB.Length { + return idxA + } + return idxB +} + +func getSequenceAllocator(allocs autoid.Allocators) (autoid.Allocator, error) { + for _, alloc := range allocs.Allocs { + if alloc.GetType() == autoid.SequenceType { + return alloc, nil + } + } + // TODO: refine the error. + return nil, errors.New("sequence allocator is nil") +} + +// BuildTableScanFromInfos build tipb.TableScan with *model.TableInfo and *model.ColumnInfo. +func BuildTableScanFromInfos(tableInfo *model.TableInfo, columnInfos []*model.ColumnInfo) *tipb.TableScan { + pkColIds := TryGetCommonPkColumnIds(tableInfo) + tsExec := &tipb.TableScan{ + TableId: tableInfo.ID, + Columns: util.ColumnsToProto(columnInfos, tableInfo.PKIsHandle, false), + PrimaryColumnIds: pkColIds, + } + if tableInfo.IsCommonHandle { + tsExec.PrimaryPrefixColumnIds = PrimaryPrefixColumnIDs(tableInfo) + } + return tsExec +} + +// BuildPartitionTableScanFromInfos build tipb.PartitonTableScan with *model.TableInfo and *model.ColumnInfo. +func BuildPartitionTableScanFromInfos(tableInfo *model.TableInfo, columnInfos []*model.ColumnInfo, fastScan bool) *tipb.PartitionTableScan { + pkColIds := TryGetCommonPkColumnIds(tableInfo) + tsExec := &tipb.PartitionTableScan{ + TableId: tableInfo.ID, + Columns: util.ColumnsToProto(columnInfos, tableInfo.PKIsHandle, false), + PrimaryColumnIds: pkColIds, + IsFastScan: &fastScan, + } + if tableInfo.IsCommonHandle { + tsExec.PrimaryPrefixColumnIds = PrimaryPrefixColumnIDs(tableInfo) + } + return tsExec +} + +// SetPBColumnsDefaultValue sets the default values of tipb.ColumnInfo. +func SetPBColumnsDefaultValue(ctx sessionctx.Context, pbColumns []*tipb.ColumnInfo, columns []*model.ColumnInfo) error { + for i, c := range columns { + // For virtual columns, we set their default values to NULL so that TiKV will return NULL properly, + // They real values will be computed later. + if c.IsGenerated() && !c.GeneratedStored { + pbColumns[i].DefaultVal = []byte{codec.NilFlag} + } + if c.GetOriginDefaultValue() == nil { + continue + } + + sessVars := ctx.GetSessionVars() + originStrict := sessVars.StrictSQLMode + sessVars.StrictSQLMode = false + d, err := table.GetColOriginDefaultValue(ctx, c) + sessVars.StrictSQLMode = originStrict + if err != nil { + return err + } + + pbColumns[i].DefaultVal, err = tablecodec.EncodeValue(sessVars.StmtCtx, nil, d) + if err != nil { + return err + } + } + return nil +} + +// TemporaryTable is used to store transaction-specific or session-specific information for global / local temporary tables. +// For example, stats and autoID should have their own copies of data, instead of being shared by all sessions. +type TemporaryTable struct { + // Whether it's modified in this transaction. + modified bool + // The stats of this table. So far it's always pseudo stats. + stats *statistics.Table + // The autoID allocator of this table. + autoIDAllocator autoid.Allocator + // Table size. + size int64 + + meta *model.TableInfo +} + +// TempTableFromMeta builds a TempTable from model.TableInfo. +func TempTableFromMeta(tblInfo *model.TableInfo) tableutil.TempTable { + return &TemporaryTable{ + modified: false, + stats: statistics.PseudoTable(tblInfo, false), + autoIDAllocator: autoid.NewAllocatorFromTempTblInfo(tblInfo), + meta: tblInfo, + } +} + +// GetAutoIDAllocator is implemented from TempTable.GetAutoIDAllocator. +func (t *TemporaryTable) GetAutoIDAllocator() autoid.Allocator { + return t.autoIDAllocator +} + +// SetModified is implemented from TempTable.SetModified. +func (t *TemporaryTable) SetModified(modified bool) { + t.modified = modified +} + +// GetModified is implemented from TempTable.GetModified. +func (t *TemporaryTable) GetModified() bool { + return t.modified +} + +// GetStats is implemented from TempTable.GetStats. +func (t *TemporaryTable) GetStats() interface{} { + return t.stats +} + +// GetSize gets the table size. +func (t *TemporaryTable) GetSize() int64 { + return t.size +} + +// SetSize sets the table size. +func (t *TemporaryTable) SetSize(v int64) { + t.size = v +} + +// GetMeta gets the table meta. +func (t *TemporaryTable) GetMeta() *model.TableInfo { + return t.meta +} diff --git a/pkg/table/tables/tables_test.go b/pkg/table/tables/tables_test.go new file mode 100644 index 0000000000000..76561f3feca79 --- /dev/null +++ b/pkg/table/tables/tables_test.go @@ -0,0 +1,1710 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package tables_test + +import ( + "context" + "fmt" + "math" + "sort" + "strconv" + "sync/atomic" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func firstKey(t table.Table) kv.Key { + return tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) +} + +func indexPrefix(t table.PhysicalTable) kv.Key { + return tablecodec.GenTableIndexPrefix(t.GetPhysicalID()) +} + +func seek(t table.PhysicalTable, ctx sessionctx.Context, h kv.Handle) (kv.Handle, bool, error) { + txn, err := ctx.Txn(true) + if err != nil { + return nil, false, err + } + recordPrefix := t.RecordPrefix() + seekKey := tablecodec.EncodeRowKeyWithHandle(t.GetPhysicalID(), h) + iter, err := txn.Iter(seekKey, recordPrefix.PrefixNext()) + if err != nil { + return nil, false, err + } + if !iter.Valid() || !iter.Key().HasPrefix(recordPrefix) { + // No more records in the table, skip to the end. + return nil, false, nil + } + handle, err := tablecodec.DecodeRowKey(iter.Key()) + if err != nil { + return nil, false, err + } + return handle, true, nil +} + +func TestBasic(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "CREATE TABLE test.t (a int primary key auto_increment, b varchar(255) unique)") + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + require.Greater(t, tb.Meta().ID, int64(0)) + require.Equal(t, "t", tb.Meta().Name.L) + require.NotNil(t, tb.Meta()) + require.NotNil(t, tb.Indices()) + require.NotEqual(t, "", string(firstKey(tb))) + require.NotEqual(t, "", string(indexPrefix(tb.(table.PhysicalTable)))) + require.NotEqual(t, "", string(tb.RecordPrefix())) + require.NotNil(t, tables.FindIndexByColName(tb, "b")) + + autoID, err := table.AllocAutoIncrementValue(context.Background(), tb, tk.Session()) + require.NoError(t, err) + require.Greater(t, autoID, int64(0)) + + handle, err := tables.AllocHandle(context.Background(), nil, tb) + require.NoError(t, err) + require.Greater(t, handle.IntValue(), int64(0)) + + ctx := tk.Session() + rid, err := tb.AddRecord(ctx, types.MakeDatums(1, "abc")) + require.NoError(t, err) + require.Greater(t, rid.IntValue(), int64(0)) + row, err := tables.RowWithCols(tb, ctx, rid, tb.Cols()) + require.NoError(t, err) + require.Equal(t, 2, len(row)) + require.Equal(t, int64(1), row[0].GetInt64()) + + _, err = tb.AddRecord(ctx, types.MakeDatums(1, "aba")) + require.Error(t, err) + _, err = tb.AddRecord(ctx, types.MakeDatums(2, "abc")) + require.Error(t, err) + + require.Nil(t, tb.UpdateRecord(context.Background(), ctx, rid, types.MakeDatums(1, "abc"), types.MakeDatums(1, "cba"), []bool{false, true})) + + err = tables.IterRecords(tb, ctx, tb.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { + return true, nil + }) + require.NoError(t, err) + + indexCnt := func() int { + cnt, err1 := countEntriesWithPrefix(ctx, indexPrefix(tb.(table.PhysicalTable))) + require.Nil(t, err1) + return cnt + } + + // RowWithCols test + vals, err := tables.RowWithCols(tb, ctx, kv.IntHandle(1), tb.Cols()) + require.NoError(t, err) + require.Len(t, vals, 2) + require.Equal(t, int64(1), vals[0].GetInt64()) + cols := []*table.Column{tb.Cols()[1]} + vals, err = tables.RowWithCols(tb, ctx, kv.IntHandle(1), cols) + require.NoError(t, err) + require.Len(t, vals, 1) + require.Equal(t, []byte("cba"), vals[0].GetBytes()) + + // Make sure there is index data in the storage. + require.Greater(t, indexCnt(), 0) + require.Nil(t, tb.RemoveRecord(ctx, rid, types.MakeDatums(1, "cba"))) + // Make sure index data is also removed after tb.RemoveRecord(). + require.Equal(t, 0, indexCnt()) + _, err = tb.AddRecord(ctx, types.MakeDatums(1, "abc")) + require.NoError(t, err) + require.Greater(t, indexCnt(), 0) + handle, found, err := seek(tb.(table.PhysicalTable), ctx, kv.IntHandle(0)) + require.Equal(t, int64(1), handle.IntValue()) + require.Equal(t, true, found) + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "drop table test.t") + require.NoError(t, err) + + table.MockTableFromMeta(tb.Meta()) + alc := tb.Allocators(nil).Get(autoid.RowIDAllocType) + require.NotNil(t, alc) + + err = alc.Rebase(context.Background(), 0, false) + require.NoError(t, err) +} + +func countEntriesWithPrefix(ctx sessionctx.Context, prefix []byte) (int, error) { + cnt := 0 + txn, err := ctx.Txn(true) + if err != nil { + return 0, errors.Trace(err) + } + err = util.ScanMetaWithPrefix(txn, prefix, func(k kv.Key, v []byte) bool { + cnt++ + return true + }) + return cnt, err +} + +func TestTypes(t *testing.T) { + ctx := context.Background() + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "CREATE TABLE test.t (c1 tinyint, c2 smallint, c3 int, c4 bigint, c5 text, c6 blob, c7 varchar(64), c8 time, c9 timestamp null default CURRENT_TIMESTAMP, c10 decimal(10,1))") + require.NoError(t, err) + _, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, "insert test.t values (1, 2, 3, 4, '5', '6', '7', '10:10:10', null, 1.4)") + require.NoError(t, err) + rs, err := tk.Session().Execute(ctx, "select * from test.t where c1 = 1") + require.NoError(t, err) + req := rs[0].NewChunk(nil) + err = rs[0].Next(ctx, req) + require.NoError(t, err) + require.False(t, req.NumRows() == 0) + require.Nil(t, rs[0].Close()) + _, err = tk.Session().Execute(ctx, "drop table test.t") + require.NoError(t, err) + + _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (c1 tinyint unsigned, c2 smallint unsigned, c3 int unsigned, c4 bigint unsigned, c5 double, c6 bit(8))") + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, "insert test.t values (1, 2, 3, 4, 5, 6)") + require.NoError(t, err) + rs, err = tk.Session().Execute(ctx, "select * from test.t where c1 = 1") + require.NoError(t, err) + req = rs[0].NewChunk(nil) + err = rs[0].Next(ctx, req) + require.NoError(t, err) + require.False(t, req.NumRows() == 0) + row := req.GetRow(0) + require.Equal(t, types.NewBinaryLiteralFromUint(6, -1), types.BinaryLiteral(row.GetBytes(5))) + require.Nil(t, rs[0].Close()) + _, err = tk.Session().Execute(ctx, "drop table test.t") + require.NoError(t, err) + + _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (c1 enum('a', 'b', 'c'))") + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, "insert test.t values ('a'), (2), ('c')") + require.NoError(t, err) + rs, err = tk.Session().Execute(ctx, "select c1 + 1 from test.t where c1 = 1") + require.NoError(t, err) + req = rs[0].NewChunk(nil) + err = rs[0].Next(ctx, req) + require.NoError(t, err) + require.False(t, req.NumRows() == 0) + require.Equal(t, float64(2), req.GetRow(0).GetFloat64(0)) + require.Nil(t, rs[0].Close()) + _, err = tk.Session().Execute(ctx, "drop table test.t") + require.NoError(t, err) +} + +func TestUniqueIndexMultipleNullEntries(t *testing.T) { + ctx := context.Background() + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(ctx, "drop table if exists test.t") + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (a int primary key auto_increment, b varchar(255) unique)") + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + require.Greater(t, tb.Meta().ID, int64(0)) + require.Equal(t, "t", tb.Meta().Name.L) + require.NotNil(t, tb.Meta()) + require.NotNil(t, tb.Indices()) + require.NotEqual(t, "", string(firstKey(tb))) + require.NotEqual(t, "", string(indexPrefix(tb.(table.PhysicalTable)))) + require.NotEqual(t, "", string(tb.RecordPrefix())) + require.NotNil(t, tables.FindIndexByColName(tb, "b")) + + handle, err := tables.AllocHandle(context.Background(), nil, tb) + require.NoError(t, err) + require.Greater(t, handle.IntValue(), int64(0)) + + autoid, err := table.AllocAutoIncrementValue(context.Background(), tb, tk.Session()) + require.NoError(t, err) + require.Greater(t, autoid, int64(0)) + + sctx := tk.Session() + require.Nil(t, sessiontxn.NewTxn(ctx, sctx)) + _, err = tb.AddRecord(sctx, types.MakeDatums(1, nil)) + require.NoError(t, err) + _, err = tb.AddRecord(sctx, types.MakeDatums(2, nil)) + require.NoError(t, err) + txn, err := sctx.Txn(true) + require.NoError(t, err) + require.Nil(t, txn.Rollback()) + _, err = tk.Session().Execute(context.Background(), "drop table test.t") + require.NoError(t, err) +} + +func TestRowKeyCodec(t *testing.T) { + tableVal := []struct { + tableID int64 + h int64 + ID int64 + }{ + {1, 1234567890, 0}, + {2, 1, 0}, + {3, -1, 0}, + {4, -1, 1}, + } + + for _, v := range tableVal { + b := tablecodec.EncodeRowKeyWithHandle(v.tableID, kv.IntHandle(v.h)) + tableID, handle, err := tablecodec.DecodeRecordKey(b) + require.NoError(t, err) + require.Equal(t, v.tableID, tableID) + require.Equal(t, v.h, handle.IntValue()) + + handle, err = tablecodec.DecodeRowKey(b) + require.NoError(t, err) + require.Equal(t, v.h, handle.IntValue()) + } + + // test error + tbl := []string{ + "", + "x", + "t1", + "t12345678", + "t12345678_i", + "t12345678_r1", + "t12345678_r1234567", + } + + for _, v := range tbl { + _, err := tablecodec.DecodeRowKey(kv.Key(v)) + require.Error(t, err) + } +} + +func TestUnsignedPK(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tPK") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tPK (a bigint unsigned primary key, b varchar(255))") + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tPK")) + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(1, "abc")) + require.NoError(t, err) + pt := tb.(table.PhysicalTable) + row, err := tables.RowWithCols(pt, tk.Session(), rid, tb.Cols()) + require.NoError(t, err) + require.Equal(t, 2, len(row)) + require.Equal(t, types.KindUint64, row[0].Kind()) + tk.Session().StmtCommit(context.Background()) + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.Nil(t, txn.Commit(context.Background())) +} + +func TestIterRecords(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tIter") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tIter (a int primary key, b int)") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "INSERT test.tIter VALUES (-1, 2), (2, NULL)") + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tIter")) + require.NoError(t, err) + totalCount := 0 + err = tables.IterRecords(tb, tk.Session(), tb.Cols(), func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { + totalCount++ + require.False(t, rec[0].IsNull()) + return true, nil + }) + require.NoError(t, err) + require.Equal(t, 2, totalCount) + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.Nil(t, txn.Commit(context.Background())) +} + +func TestTableFromMeta(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("CREATE TABLE meta (a int primary key auto_increment, b varchar(255) unique)") + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + _, err := tk.Session().Txn(true) + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("meta")) + require.NoError(t, err) + tbInfo := tb.Meta().Clone() + + // For test coverage + tbInfo.Columns[0].GeneratedExprString = "a" + _, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) + require.NoError(t, err) + + tbInfo.Columns[0].GeneratedExprString = "test" + _, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) + require.Error(t, err) + tbInfo.Columns[0].State = model.StateNone + tb, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) + require.Nil(t, tb) + require.Error(t, err) + tbInfo.State = model.StateNone + tb, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) + require.Nil(t, tb) + require.Error(t, err) + + tk.MustExec(`create table t_mock (id int) partition by range (id) (partition p0 values less than maxvalue)`) + tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t_mock")) + require.NoError(t, err) + tt := table.MockTableFromMeta(tb.Meta()) + _, ok := tt.(table.PartitionedTable) + require.True(t, ok) + tk.MustExec("drop table t_mock") + require.Equal(t, table.NormalTable, tt.Type()) + + tk.MustExec("create table t_meta (a int) shard_row_id_bits = 15") + tb, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t_meta")) + require.NoError(t, err) + _, err = tables.AllocHandle(context.Background(), tk.Session(), tb) + require.NoError(t, err) + + maxID := 1<<(64-15-1) - 1 + err = tb.Allocators(tk.Session()).Get(autoid.RowIDAllocType).Rebase(context.Background(), int64(maxID), false) + require.NoError(t, err) + + _, err = tables.AllocHandle(context.Background(), tk.Session(), tb) + require.Error(t, err) +} + +func TestHiddenColumn(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("DROP DATABASE IF EXISTS test_hidden;") + tk.MustExec("CREATE DATABASE test_hidden;") + tk.MustExec("USE test_hidden;") + tk.MustExec("CREATE TABLE t (a int primary key, b int as (a+1), c int, d int as (c+1) stored, e int, f tinyint as (a+1));") + tk.MustExec("insert into t values (1, default, 3, default, 5, default);") + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test_hidden"), model.NewCIStr("t")) + require.NoError(t, err) + colInfo := tb.Meta().Columns + // Set column b, d, f to hidden + colInfo[1].Hidden = true + colInfo[3].Hidden = true + colInfo[5].Hidden = true + tc := tb.(*tables.TableCommon) + // Reset related caches + tc.VisibleColumns = nil + tc.WritableColumns = nil + tc.HiddenColumns = nil + tc.FullHiddenColsAndVisibleColumns = nil + + // Basic test + cols := tb.VisibleCols() + require.NotNil(t, table.FindCol(cols, "a")) + require.Nil(t, table.FindCol(cols, "b")) + require.NotNil(t, table.FindCol(cols, "c")) + require.Nil(t, table.FindCol(cols, "d")) + require.NotNil(t, table.FindCol(cols, "e")) + hiddenCols := tb.HiddenCols() + require.Nil(t, table.FindCol(hiddenCols, "a")) + require.NotNil(t, table.FindCol(hiddenCols, "b")) + require.Nil(t, table.FindCol(hiddenCols, "c")) + require.NotNil(t, table.FindCol(hiddenCols, "d")) + require.Nil(t, table.FindCol(hiddenCols, "e")) + colInfo[1].State = model.StateDeleteOnly + colInfo[2].State = model.StateDeleteOnly + fullHiddenColsAndVisibleColumns := tb.FullHiddenColsAndVisibleCols() + require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "a")) + require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "b")) + require.Nil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "c")) + require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "d")) + require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "e")) + // Reset schema states. + colInfo[1].State = model.StatePublic + colInfo[2].State = model.StatePublic + + // Test show create table + tk.MustQuery("show create table t;").Check(testkit.Rows( + "t CREATE TABLE `t` (\n" + + " `a` int(11) NOT NULL,\n" + + " `c` int(11) DEFAULT NULL,\n" + + " `e` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + // Test show (extended) columns + tk.MustQuery("show columns from t").Check(testkit.RowsWithSep("|", + "a|int(11)|NO|PRI||", + "c|int(11)|YES|||", + "e|int(11)|YES|||")) + tk.MustQuery("show extended columns from t").Check(testkit.RowsWithSep("|", + "a|int(11)|NO|PRI||", + "b|int(11)|YES|||VIRTUAL GENERATED", + "c|int(11)|YES|||", + "d|int(11)|YES|||STORED GENERATED", + "e|int(11)|YES|||", + "f|tinyint(4)|YES|||VIRTUAL GENERATED")) + + // `SELECT` statement + tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) + tk.MustQuery("select a, c, e from t;").Check(testkit.Rows("1 3 5")) + + // Can't use hidden columns in `SELECT` statement + tk.MustGetErrMsg("select b from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("select b+1 from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("select b, c from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("select a, d from t;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("select d, b from t;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("select * from t where b > 1;", "[planner:1054]Unknown column 'b' in 'where clause'") + tk.MustGetErrMsg("select * from t order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") + tk.MustGetErrMsg("select * from t group by b;", "[planner:1054]Unknown column 'b' in 'group statement'") + + // Can't use hidden columns in `INSERT` statement + // 1. insert into ... values ... + tk.MustGetErrMsg("insert into t values (1, 2, 3, 4, 5, 6);", "[planner:1136]Column count doesn't match value count at row 1") + tk.MustGetErrMsg("insert into t(b) values (2)", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t(b, c) values (2, 3);", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t(a, d) values (1, 4);", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t(d, b) values (4, 2);", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t(a) values (b);", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t(a) values (d+1);", "[planner:1054]Unknown column 'd' in 'field list'") + // 2. insert into ... set ... + tk.MustGetErrMsg("insert into t set b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t set d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t set a = b;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set a = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") + // 3. insert into ... on duplicated key update ... + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = 3, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = b;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") + // 4. replace into ... set ... + tk.MustGetErrMsg("replace into t set a = 1, b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("replace into t set a = 1, b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("replace into t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("replace into t set a = 1, d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("replace into t set a = 1, c = b;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("replace into t set a = 1, c = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") + // 5. insert into ... select ... + tk.MustExec("create table t1(a int, b int, c int, d int);") + tk.MustGetErrMsg("insert into t1 select b from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t1 select b+1 from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t1 select b, c from t;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("insert into t1 select a, d from t;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t1 select d, b from t;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("insert into t1 select a from t where b > 1;", "[planner:1054]Unknown column 'b' in 'where clause'") + tk.MustGetErrMsg("insert into t1 select a from t order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") + tk.MustGetErrMsg("insert into t1 select a from t group by b;", "[planner:1054]Unknown column 'b' in 'group statement'") + tk.MustExec("drop table t1") + + // `UPDATE` statement + tk.MustGetErrMsg("update t set b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("update t set b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("update t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") + + // FIXME: This sql return unknown column 'd' in MySQL + tk.MustGetErrMsg("update t set d = 4, b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") + + tk.MustGetErrMsg("update t set a = b;", "[planner:1054]Unknown column 'b' in 'field list'") + tk.MustGetErrMsg("update t set a = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") + tk.MustGetErrMsg("update t set a=1 where b=1;", "[planner:1054]Unknown column 'b' in 'where clause'") + tk.MustGetErrMsg("update t set a=1 where c=3 order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") + + // `DELETE` statement + tk.MustExec("delete from t;") + tk.MustQuery("select count(*) from t;").Check(testkit.Rows("0")) + tk.MustExec("insert into t values (1, 3, 5);") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) + tk.MustGetErrMsg("delete from t where b = 1;", "[planner:1054]Unknown column 'b' in 'where clause'") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) + tk.MustGetErrMsg("delete from t order by d = 1;", "[planner:1054]Unknown column 'd' in 'order clause'") + tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) + + // `DROP COLUMN` statement + tk.MustGetErrMsg("ALTER TABLE t DROP COLUMN b;", "[ddl:1091]Can't DROP 'b'; check that column/key exists") + tk.MustQuery("show create table t;").Check(testkit.Rows( + "t CREATE TABLE `t` (\n" + + " `a` int(11) NOT NULL,\n" + + " `c` int(11) DEFAULT NULL,\n" + + " `e` int(11) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustQuery("show extended columns from t").Check(testkit.RowsWithSep("|", + "a|int(11)|NO|PRI||", + "b|int(11)|YES|||VIRTUAL GENERATED", + "c|int(11)|YES|||", + "d|int(11)|YES|||STORED GENERATED", + "e|int(11)|YES|||", + "f|tinyint(4)|YES|||VIRTUAL GENERATED")) +} + +func TestAddRecordWithCtx(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tRecord") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tRecord (a bigint unsigned primary key, b varchar(255))") + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tRecord")) + require.NoError(t, err) + defer func() { + _, err := tk.Session().Execute(context.Background(), "DROP TABLE test.tRecord") + require.NoError(t, err) + }() + + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + _, err = tk.Session().Txn(true) + require.NoError(t, err) + recordCtx := tables.NewCommonAddRecordCtx(len(tb.Cols())) + tables.SetAddRecordCtx(tk.Session(), recordCtx) + defer tables.ClearAddRecordCtx(tk.Session()) + + records := [][]types.Datum{types.MakeDatums(uint64(1), "abc"), types.MakeDatums(uint64(2), "abcd")} + for _, r := range records { + rid, err := tb.AddRecord(tk.Session(), r) + require.NoError(t, err) + row, err := tables.RowWithCols(tb.(table.PhysicalTable), tk.Session(), rid, tb.Cols()) + require.NoError(t, err) + require.Equal(t, len(r), len(row)) + require.Equal(t, types.KindUint64, row[0].Kind()) + } + + i := 0 + err = tables.IterRecords(tb, tk.Session(), tb.Cols(), func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { + i++ + return true, nil + }) + require.NoError(t, err) + require.Equal(t, len(records), i) + + tk.Session().StmtCommit(context.Background()) + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + require.Nil(t, txn.Commit(context.Background())) +} + +func TestConstraintCheckForUniqueIndex(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@autocommit = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists ttt") + tk.MustExec("create table ttt(id int(11) NOT NULL AUTO_INCREMENT,k int(11) NOT NULL DEFAULT '0',c char(120) NOT NULL DEFAULT '',PRIMARY KEY (id),UNIQUE KEY k_1 (k,c))") + tk.MustExec("insert into ttt(k,c) values(1, 'tidb')") + tk.MustExec("insert into ttt(k,c) values(2, 'tidb')") + _, err := tk.Exec("update ttt set k=1 where id=2") + require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) + tk.MustExec("rollback") + + // no auto-commit + tk.MustExec("set @@autocommit = 0") + tk.MustExec("set @@tidb_constraint_check_in_place = 0") + tk.MustExec("begin") + _, err = tk.Exec("update ttt set k=1 where id=2") + require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) + tk.MustExec("rollback") + + tk.MustExec("set @@tidb_constraint_check_in_place = 1") + tk.MustExec("begin") + _, err = tk.Exec("update ttt set k=1 where id=2") + require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) + tk.MustExec("rollback") + + // This test check that with @@tidb_constraint_check_in_place = 0, although there is not KV request for the unique index, the pessimistic lock should still be written. + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk1.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk1.MustExec("set @@tidb_constraint_check_in_place = 0") + tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") + tk1.MustExec("use test") + tk1.MustExec("begin") + tk1.MustExec("update ttt set k=3 where id=2") + + ch := make(chan int, 2) + go func() { + tk2.MustExec("use test") + _, err := tk2.Exec("insert into ttt(k,c) values(3, 'tidb')") + require.Error(t, err) + ch <- 2 + }() + // Sleep 100ms for tk2 to execute, if it's not blocked, 2 should have been sent to the channel. + time.Sleep(100 * time.Millisecond) + ch <- 1 + _, err = tk1.Exec("commit") + require.NoError(t, err) + // The data in channel is 1 means tk2 is blocked, that's the expected behavior. + require.Equal(t, 1, <-ch) + + // Unrelated to the test logic, just wait for the goroutine to exit to avoid leak in test + require.Equal(t, 2, <-ch) +} + +func TestViewColumns(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int primary key, b varchar(20))") + tk.MustExec("drop view if exists v") + tk.MustExec("create view v as select * from t") + tk.MustExec("drop view if exists va") + tk.MustExec("create view va as select count(a) from t") + testCases := []struct { + query string + expected []string + }{ + {"select data_type from INFORMATION_SCHEMA.columns where table_name = 'v'", []string{types.TypeToStr(mysql.TypeLong, ""), types.TypeToStr(mysql.TypeVarchar, "")}}, + {"select data_type from INFORMATION_SCHEMA.columns where table_name = 'va'", []string{types.TypeToStr(mysql.TypeLonglong, "")}}, + } + for _, testCase := range testCases { + tk.MustQuery(testCase.query).Check(testkit.RowsWithSep("|", testCase.expected...)) + } + tk.MustExec("drop table if exists t") + for _, testCase := range testCases { + require.Len(t, tk.MustQuery(testCase.query).Rows(), 0) + tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", + "Warning|1356|View 'test.v' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them", + "Warning|1356|View 'test.va' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them")) + } + + // For issue 43264 + tk.MustExec(`CREATE TABLE User ( + id INT PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NULL +);`) + tk.MustExec(`CREATE VIEW Schwuser AS SELECT u.id, CONCAT(u.first_name, ' ', u.last_name) AS name FROM User u;`) + tk.MustQuery(`select DATA_TYPE from information_schema.columns where TABLE_NAME = 'Schwuser' and column_name = 'name';`).Check( + testkit.Rows("varchar")) +} + +func TestConstraintCheckForOptimisticUntouched(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists test_optimistic_untouched_flag;") + tk.MustExec(`create table test_optimistic_untouched_flag(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) + tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, null, 'green');`) + + // Insert a row with duplicated entry on the unique key, the commit should fail. + tk.MustExec("begin optimistic;") + tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, 'red', 'white');`) + tk.MustExec(`delete from test_optimistic_untouched_flag where c1 is null;`) + tk.MustExec("update test_optimistic_untouched_flag set c2 = 'green' where c2 between 'purple' and 'white';") + err := tk.ExecToErr("commit") + require.Error(t, err) + + tk.MustExec("begin optimistic;") + tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, 'red', 'white');`) + tk.MustExec("update test_optimistic_untouched_flag set c2 = 'green' where c2 between 'purple' and 'white';") + err = tk.ExecToErr("commit") + require.Error(t, err) +} + +func TestTxnAssertion(t *testing.T) { + store := testkit.CreateMockStore(t) + + se, err := session.CreateSession4Test(store) + se.SetConnectionID(1) + require.NoError(t, err) + require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + tk := testkit.NewTestKit(t, store) + tk.SetSession(se) + + fpAdd := "github.com/pingcap/tidb/pkg/table/tables/addRecordForceAssertExist" + fpUpdate := "github.com/pingcap/tidb/pkg/table/tables/updateRecordForceAssertNotExist" + fpRemove := "github.com/pingcap/tidb/pkg/table/tables/removeRecordForceAssertNotExist" + + runStmtInTxn := func(pessimistic bool, stmts ...string) error { + if pessimistic { + tk.MustExec("begin pessimistic") + } else { + tk.MustExec("begin optimistic") + } + for _, stmt := range stmts { + tk.MustExec(stmt) + } + return tk.ExecToErr("commit") + } + + withFailpoint := func(fp string, f func()) { + require.NoError(t, failpoint.Enable(fp, "return")) + defer func() { + require.NoError(t, failpoint.Disable(fp)) + }() + f() + } + + expectAssertionErr := func(assertionLevel string, err error) { + if assertionLevel == "STRICT" { + require.NotNil(t, err) + require.Contains(t, err.Error(), "assertion failed") + } else { + require.NoError(t, err) + } + } + + testAssertionBasicImpl := func(level string, lock bool, lockIdx bool, useCommonHandle bool) { + tk.MustExec("set @@tidb_txn_assertion_level = " + level) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + if useCommonHandle { + tk.MustExec("create table t(id varchar(64) primary key clustered, v int, v2 int, v3 int, v4 varchar(64), index(v2), unique index(v3), index(v4))") + } else { + tk.MustExec("create table t(id int primary key, v int, v2 int, v3 int, v4 varchar(64), index(v2), unique index(v3), index(v4))") + } + + var id1, id2, id3 interface{} + if useCommonHandle { + id1, id2, id3 = "1", "2", "3" + } else { + id1, id2, id3 = 1, 2, 3 + } + + // Auto commit + tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) + tk.MustExec("update t set v = v + 1 where id = ?", id1) + tk.MustExec("delete from t where id = 1") + + // Optimistic + tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) + tk.MustExec("begin optimistic") + if lock { + tk.MustExec("select * from t where id in (?, ?, ?) for update", id1, id2, id3) + } + if lockIdx { + tk.MustExec("select * from t where v3 in (1000, 2000, 3000) for update") + } + tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) + tk.MustExec("update t set v = v + 1 where id = ?", id2) + tk.MustExec("delete from t where id = ?", id3) + tk.MustExec("commit") + + // Pessimistic + tk.MustExec("delete from t") + tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) + tk.MustExec("begin pessimistic") + if lock { + tk.MustExec("select * from t where id in (?, ?, ?) for update", id1, id2, id3) + } + if lockIdx { + tk.MustExec("select * from t where v3 in (1000, 2000, 3000) for update") + } + tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) + tk.MustExec("update t set v = v + 1 where id = ?", id2) + tk.MustExec("delete from t where id = ?", id3) + tk.MustExec("commit") + + // Inject incorrect assertion so that it must fail. + + // Auto commit + tk.MustExec("delete from t") + tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) + withFailpoint(fpAdd, func() { + err = tk.ExecToErr("insert into t values (?, 10, 100, 1000, '10000')", id1) + expectAssertionErr(level, err) + }) + withFailpoint(fpUpdate, func() { + err = tk.ExecToErr("update t set v = v + 1 where id = ?", id2) + expectAssertionErr(level, err) + }) + withFailpoint(fpRemove, func() { + err = tk.ExecToErr("delete from t where id = ?", id3) + expectAssertionErr(level, err) + }) + + var lockStmts []string = nil + if lock { + lockStmts = append(lockStmts, fmt.Sprintf("select * from t where id in (%#v, %#v, %#v) for update", id1, id2, id3)) + } + if lockIdx { + lockStmts = append(lockStmts, "select * from t where v3 in (1000, 2000, 3000) for update") + } + + // Optimistic + tk.MustExec("delete from t") + tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) + withFailpoint(fpAdd, func() { + err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("insert into t values (%#v, 10, 100, 1000, '10000')", id1))...) + expectAssertionErr(level, err) + }) + withFailpoint(fpUpdate, func() { + err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("update t set v = v + 1 where id = %#v", id2))...) + expectAssertionErr(level, err) + }) + withFailpoint(fpRemove, func() { + err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("delete from t where id = %#v", id3))...) + expectAssertionErr(level, err) + }) + + // Pessimistic + tk.MustExec("delete from t") + tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) + withFailpoint(fpAdd, func() { + err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("insert into t values (%#v, 10, 100, 1000, '10000')", id1))...) + expectAssertionErr(level, err) + }) + withFailpoint(fpUpdate, func() { + err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("update t set v = v + 1 where id = %#v", id2))...) + expectAssertionErr(level, err) + }) + withFailpoint(fpRemove, func() { + err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("delete from t where id = %#v", id3))...) + expectAssertionErr(level, err) + }) + } + + for _, level := range []string{"STRICT", "OFF"} { + for _, lock := range []bool{false, true} { + for _, lockIdx := range []bool{false, true} { + for _, useCommonHandle := range []bool{false, true} { + t.Logf("testing testAssertionBasicImpl level: %v, lock: %v, lockIdx: %v, useCommonHandle: %v...", level, lock, lockIdx, useCommonHandle) + testAssertionBasicImpl(level, lock, lockIdx, useCommonHandle) + } + } + } + } + + testUntouchedIndexImpl := func(level string, pessimistic bool) { + tk.MustExec("set @@tidb_txn_assertion_level = " + level) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(id int primary key, v int, v2 int, v3 int, index(v2), unique index(v3))") + tk.MustExec("insert into t values (1, 10, 100, 1000)") + + if pessimistic { + tk.MustExec("begin pessimistic") + } else { + tk.MustExec("begin optimistic") + } + tk.MustExec("update t set v = v + 1 where id = 1") + tk.MustExec("delete from t where id = 1") + tk.MustExec("insert into t values (1, 11, 101, 1001)") + tk.MustExec("commit") + } + + testUntouchedIndexImpl("STRICT", false) + testUntouchedIndexImpl("STRICT", true) + testUntouchedIndexImpl("OFF", false) + testUntouchedIndexImpl("OFF", true) +} + +func TestWriteWithChecksums(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + h := helper.NewHelper(store.(helper.Storage)) + + tkDDL := testkit.NewTestKit(t, store) + tkDDL.MustExec("set global tidb_enable_row_level_checksum = 1") + tkDDL.MustExec("use test") + + tkDML := testkit.NewTestKit(t, store) + tkDML.MustExec("use test") + + type col struct { + ID int64 + Type byte + Data types.Datum + } + isDMLBeforeDDL := func(seq int64) bool { return seq == -1 } + isDMLAfterDDL := func(seq int64) bool { return seq == -2 } + + for _, tt := range []struct { + name string + init []string + schema []col + ddl string + dml func(seq int64, job *model.Job) ([]byte, [][]col) + }{ + { + name: "AddRecord/AddColumn", + init: []string{"create table t (id int primary key, c1 int)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "AddRecord/AddColumnWithDefault", + init: []string{"create table t (id int primary key, c1 int)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int default 42", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(42)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "AddRecord/AddColumnNotNull", + init: []string{"create table t (id int primary key, c1 int)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int not null", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + if isDMLAfterDDL(seq) { + tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) + } else { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + } + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(seq + 2)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "AddRecord/DropColumn", + init: []string{"create table t (id int primary key, c1 int, c2 int)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "AddRecord/DropColumnWithDefault", + init: []string{"create table t (id int primary key, c1 int, c2 int default 42)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(42)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + return key, [][]col{col2, col3} + }, + }, + { + name: "AddRecord/DropColumnNotNull", + init: []string{"create table t (id int primary key, c1 int, c2 int not null)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + if isDMLBeforeDDL(seq) { + tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) + } else { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + } + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(seq + 2)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + return key, [][]col{col2, col3} + }, + }, + { + name: "AddRecord/ChangeColumnType", + init: []string{"create table t (id int primary key, c1 int)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeVarchar}, + }, + ddl: "alter table t change column c1 c1 varchar(10)", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeVarchar, types.NewDatum(strconv.FormatInt(seq+1, 10))}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "AddRecord/ChangeColumnTypeFloat", + init: []string{"create table t (id int primary key, c1 float)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeFloat}, + {ID: 3, Type: mysql.TypeDouble}, + }, + ddl: "alter table t change column c1 c1 double", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + v := float64(seq) * 3.14 + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, v) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeFloat, types.NewDatum(float32(v))}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeFloat, types.NewDatum(float64(float32(v)))}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeDouble, types.NewDatum(v)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "AddRecord/ChangeColumnTypeDouble", + init: []string{"create table t (id int primary key, c1 double)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeDouble}, + {ID: 3, Type: mysql.TypeFloat}, + }, + ddl: "alter table t change column c1 c1 float", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + v := float64(seq) * 3.14 + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, v) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeDouble, types.NewDatum(v)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeFloat, types.NewDatum(float32(v))}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "AddRecord/SetColumnDefault", + init: []string{"create table t (id int primary key, c1 int, c2 int default 1)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t alter column c2 set default 42", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(1)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(42)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, nil + }, + }, + { + name: "AddRecord/DropColumnDefault", + init: []string{"create table t (id int primary key, c1 int, c2 int default 42)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t alter column c2 drop default", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + if isDMLAfterDDL(seq) { + tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) + } else { + tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) + } + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(42)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(seq)}, + {2, mysql.TypeLong, types.NewDatum(seq + 1)}, + {3, mysql.TypeLong, types.NewDatum(seq + 2)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, nil + }, + }, + { + name: "UpdateRecord/AddColumn", + init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "UpdateRecord/AddColumnWithDefault", + init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int default 42", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(42)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "UpdateRecord/AddColumnNotNull", + init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t add column c2 int not null", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col2, col1} + }, + }, + { + name: "UpdateRecord/DropColumn", + init: []string{"create table t (id int primary key, c1 int, c2 int)", "insert into t values (1, 0, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + if job.SchemaState == model.StateWriteOnly { + return key, [][]col{col1, col3} + } + return key, [][]col{col2, col3} + }, + }, + { + name: "UpdateRecord/DropColumnWithDefault", + init: []string{"create table t (id int primary key, c1 int, c2 int default 42)", "insert into t values (1, 0, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(nil)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + if job.SchemaState == model.StateWriteOnly { + return key, [][]col{col1, col3} + } + return key, [][]col{col2, col3} + }, + }, + { + name: "UpdateRecord/DropColumnNotNull", + init: []string{"create table t (id int primary key, c1 int, c2 int not null)", "insert into t values (1, 0, 10)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeLong}, + }, + ddl: "alter table t drop column c2", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(10)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + {3, mysql.TypeLong, types.NewDatum(0)}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + if job.SchemaState == model.StateWriteOnly { + return key, [][]col{col1, col3} + } + return key, [][]col{col2, col3} + }, + }, + { + name: "UpdateRecord/ChangeColumnType", + init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeLong}, + {ID: 3, Type: mysql.TypeVarchar}, + }, + ddl: "alter table t change column c1 c1 varchar(10)", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + tkDML.MustExec("update t set c1 = ? where id = 1", seq) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeLong, types.NewDatum(seq)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {3, mysql.TypeVarchar, types.NewDatum(strconv.FormatInt(seq, 10))}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "UpdateRecord/ChangeColumnTypeFloat", + init: []string{"create table t (id int primary key, c1 float)", "insert into t values (1, 3.14)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeFloat}, + {ID: 3, Type: mysql.TypeDouble}, + }, + ddl: "alter table t change column c1 c1 double", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + v := float64(seq) * 3.14 + tkDML.MustExec("update t set c1 = ? where id = 1", v) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeFloat, types.NewDatum(float32(v))}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeFloat, types.NewDatum(float64(float32(v)))}, + } + col3 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {3, mysql.TypeDouble, types.NewDatum(v)}, + } + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col3} + } + return key, [][]col{col1, col2} + }, + }, + { + name: "UpdateRecord/ChangeColumnTypeDouble", + init: []string{"create table t (id int primary key, c1 double)", "insert into t values (1, 3.14)"}, + schema: []col{ + {ID: 1, Type: mysql.TypeLong}, + {ID: 2, Type: mysql.TypeDouble}, + {ID: 3, Type: mysql.TypeFloat}, + }, + ddl: "alter table t change column c1 c1 float", + dml: func(seq int64, job *model.Job) ([]byte, [][]col) { + v := float64(seq) * 3.14 + tkDML.MustExec("update t set c1 = ? where id = 1", v) + tbl := external.GetTableByName(t, tkDML, "test", "t") + key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) + col1 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeDouble, types.NewDatum(v)}, + } + col2 := []col{ + {1, mysql.TypeLong, types.NewDatum(1)}, + {2, mysql.TypeFloat, types.NewDatum(float32(v))}, + } + + if isDMLBeforeDDL(seq) { + return key, [][]col{col1} + } + if isDMLAfterDDL(seq) { + return key, [][]col{col2} + } + return key, [][]col{col1, col2} + }, + }, + } { + // build row decoder for extracting checksums from row + cols := make([]rowcodec.ColInfo, len(tt.schema)) + for i, col := range tt.schema { + cols[i] = rowcodec.ColInfo{ID: col.ID, Ft: types.NewFieldType(col.Type)} + } + dec := rowcodec.NewDatumMapDecoder(cols, time.UTC) + // build a function for executing dml and validating results + doDML := func(t *testing.T, seq int64, job *model.Job) { + key, rows := tt.dml(seq, job) + // get actualChecksums in row value + actualChecksums := make([]uint32, 0, 2) + data, err := h.GetMvccByEncodedKey(key) + assert.NoError(t, err) + _, err = dec.DecodeToDatumMap(data.Info.Writes[0].ShortValue, nil) + assert.NoError(t, err) + if checksum, ok := dec.GetChecksum(); ok { + actualChecksums = append(actualChecksums, checksum) + if checksum, ok := dec.GetExtraChecksum(); ok { + actualChecksums = append(actualChecksums, checksum) + } + } + // calc expected checksums from row data + expectChecksums := make([]uint32, 0, 2) + for _, row := range rows { + cols := make([]rowcodec.ColData, len(row)) + for i := range row { + ft := types.NewFieldType(row[i].Type) + cols[i] = rowcodec.ColData{ + ColumnInfo: &model.ColumnInfo{ID: row[i].ID, FieldType: *ft}, + Datum: &row[i].Data, + } + } + data := rowcodec.RowData{Cols: cols} + sort.Sort(data) + checksum, err := data.Checksum() + assert.NoError(t, err) + expectChecksums = append(expectChecksums, checksum) + } + // validate checksums + assert.Equal(t, expectChecksums, actualChecksums) + } + + // init and run sub test + tkDDL.MustExec("drop table if exists t") + for _, sql := range tt.init { + tkDDL.MustExec(sql) + } + t.Run(tt.name, func(t *testing.T) { + origHook := dom.DDL().GetHook() + defer dom.DDL().SetHook(origHook) + + var seq int64 + fn := func(job *model.Job) { + if job.State != model.JobStateRunning { + return + } + doDML(t, atomic.AddInt64(&seq, 1), job) + } + cb := &callback.TestDDLCallback{} + cb.OnJobUpdatedExported.Store(&fn) + + dom.DDL().SetHook(cb) + + doDML(t, -1, nil) + tkDDL.MustExec(tt.ddl) + doDML(t, -2, nil) + }) + tkDDL.MustExec("admin check table t") + } +} diff --git a/pkg/table/tables/test/partition/BUILD.bazel b/pkg/table/tables/test/partition/BUILD.bazel new file mode 100644 index 0000000000000..efa74bbe6d1c7 --- /dev/null +++ b/pkg/table/tables/test/partition/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "partition_test", + timeout = "long", + srcs = [ + "main_test.go", + "partition_test.go", + ], + flaky = True, + shard_count = 17, + deps = [ + "//pkg/ddl", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessiontxn", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/table/tables/test/partition/main_test.go b/pkg/table/tables/test/partition/main_test.go new file mode 100644 index 0000000000000..cb52e876dd375 --- /dev/null +++ b/pkg/table/tables/test/partition/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package partition + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/table/tables/test/partition/partition_test.go b/pkg/table/tables/test/partition/partition_test.go new file mode 100644 index 0000000000000..2b6c6c1b9d2ed --- /dev/null +++ b/pkg/table/tables/test/partition/partition_test.go @@ -0,0 +1,3049 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package partition + +import ( + "context" + "fmt" + "math/rand" + "strconv" + "strings" + "testing" + gotime "time" + + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +func TestPartitionAddRecord(t *testing.T) { + createTable1 := `CREATE TABLE test.t1 (id int(11), index(id)) +PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21) +)` + ctx := context.Background() + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + _, err := tk.Session().Execute(ctx, "use test") + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, "drop table if exists t1, t2;") + require.NoError(t, err) + _, err = tk.Session().Execute(ctx, createTable1) + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tbInfo := tb.Meta() + p0 := tbInfo.Partition.Definitions[0] + require.Equal(t, model.NewCIStr("p0"), p0.Name) + require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) + rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(1)) + require.NoError(t, err) + + // Check that add record writes to the partition, rather than the table. + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + val, err := txn.Get(context.TODO(), tables.PartitionRecordKey(p0.ID, rid.IntValue())) + require.NoError(t, err) + require.Greater(t, len(val), 0) + _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) + require.True(t, kv.ErrNotExist.Equal(err)) + + // Cover more code. + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(7)) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(12)) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(16)) + require.NoError(t, err) + + // Make the changes visible. + _, err = tk.Session().Execute(context.Background(), "commit") + require.NoError(t, err) + + // Check index count equals to data count. + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("4")) + tk.MustQuery("select count(*) from t1 use index(id)").Check(testkit.Rows("4")) + tk.MustQuery("select count(*) from t1 use index(id) where id > 6").Check(testkit.Rows("3")) + + // Value must locates in one partition. + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(22)) + require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) + _, err = tk.Session().Execute(context.Background(), "rollback") + require.NoError(t, err) + + createTable2 := `CREATE TABLE test.t2 (id int(11)) +PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p3 VALUES LESS THAN MAXVALUE +)` + _, err = tk.Session().Execute(context.Background(), createTable2) + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) + tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(22)) + require.NoError(t, err) + + createTable3 := `create table test.t3 (id int) partition by range (id) + ( + partition p0 values less than (10) + )` + _, err = tk.Session().Execute(context.Background(), createTable3) + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) + tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t3")) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(11)) + require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(10)) + require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(0)) + require.NoError(t, err) + + createTable4 := `create table test.t4 (a int,b int) partition by range (a+b) + ( + partition p0 values less than (10) + );` + _, err = tk.Session().Execute(context.Background(), createTable4) + require.NoError(t, err) + require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) + tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t4")) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(1, 11)) + require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) +} + +func TestHashPartitionAddRecord(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "use test") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "drop table if exists t1;") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "set @@session.tidb_enable_table_partition = '1';") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), `CREATE TABLE test.t1 (id int(11), index(id)) PARTITION BY HASH (id) partitions 4;`) + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tbInfo := tb.Meta() + p0 := tbInfo.Partition.Definitions[0] + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(8)) + require.NoError(t, err) + + // Check that add record writes to the partition, rather than the table. + txn, err := tk.Session().Txn(true) + require.NoError(t, err) + val, err := txn.Get(context.TODO(), tables.PartitionRecordKey(p0.ID, rid.IntValue())) + require.NoError(t, err) + require.Greater(t, len(val), 0) + _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) + require.True(t, kv.ErrNotExist.Equal(err)) + + // Cover more code. + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(-1)) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(3)) + require.NoError(t, err) + _, err = tb.AddRecord(tk.Session(), types.MakeDatums(6)) + require.NoError(t, err) + + // Make the changes visible. + _, err = tk.Session().Execute(context.Background(), "commit") + require.NoError(t, err) + + // Check index count equals to data count. + tk.MustQuery("select count(*) from t1").Check(testkit.Rows("4")) + tk.MustQuery("select count(*) from t1 use index(id)").Check(testkit.Rows("4")) + tk.MustQuery("select count(*) from t1 use index(id) where id > 2").Check(testkit.Rows("3")) + + // Test for partition expression is negative number. + _, err = tk.Session().Execute(context.Background(), `CREATE TABLE test.t2 (id int(11), index(id)) PARTITION BY HASH (id) partitions 11;`) + require.NoError(t, err) + tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tbInfo = tb.Meta() + for i := 0; i < 11; i++ { + require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) + rid, err = tb.AddRecord(tk.Session(), types.MakeDatums(-i)) + require.NoError(t, err) + txn, err = tk.Session().Txn(true) + require.NoError(t, err) + val, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.Partition.Definitions[i].ID, rid.IntValue())) + require.NoError(t, err) + require.Greater(t, len(val), 0) + _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) + require.True(t, kv.ErrNotExist.Equal(err)) + } + _, err = tk.Session().Execute(context.Background(), "drop table if exists t1, t2;") + require.NoError(t, err) +} + +// TestPartitionGetPhysicalID tests partition.GetPhysicalID(). +func TestPartitionGetPhysicalID(t *testing.T) { + createTable1 := `CREATE TABLE test.t1 (id int(11), index(id)) +PARTITION BY RANGE ( id ) ( + PARTITION p0 VALUES LESS THAN (6), + PARTITION p1 VALUES LESS THAN (11), + PARTITION p2 VALUES LESS THAN (16), + PARTITION p3 VALUES LESS THAN (21) +)` + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "Drop table if exists test.t1;") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), createTable1) + require.NoError(t, err) + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + tbInfo := tb.Meta() + ps := tbInfo.GetPartitionInfo() + require.NotNil(t, ps) + for _, pd := range ps.Definitions { + p := tb.(table.PartitionedTable).GetPartition(pd.ID) + require.NotNil(t, p) + require.Equal(t, p.GetPhysicalID(), pd.ID) + } +} + +func TestGeneratePartitionExpr(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + _, err := tk.Session().Execute(context.Background(), "use test") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), "drop table if exists t1;") + require.NoError(t, err) + _, err = tk.Session().Execute(context.Background(), `create table t1 (id int) + partition by range (id) ( + partition p0 values less than (4), + partition p1 values less than (7), + partition p3 values less than maxvalue)`) + require.NoError(t, err) + + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) + require.NoError(t, err) + type partitionExpr interface { + PartitionExpr() *tables.PartitionExpr + } + pe := tbl.(partitionExpr).PartitionExpr() + + upperBounds := []string{ + "lt(t1.id, 4)", + "lt(t1.id, 7)", + "1", + } + for i, expr := range pe.UpperBounds { + require.Equal(t, upperBounds[i], expr.String()) + } +} + +func TestLocatePartition(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + + tk.MustExec(`CREATE TABLE t ( + id bigint(20) DEFAULT NULL, + type varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci + PARTITION BY LIST COLUMNS(type) + (PARTITION push_event VALUES IN ("PushEvent"), + PARTITION watch_event VALUES IN ("WatchEvent") + )`) + tk.MustExec(`insert into t values (1,"PushEvent"),(2,"WatchEvent"),(3, "WatchEvent")`) + tk.MustExec(`analyze table t`) + + tk1 := testkit.NewTestKit(t, store) + tk2 := testkit.NewTestKit(t, store) + tk3 := testkit.NewTestKit(t, store) + tks := []*testkit.TestKit{tk1, tk2, tk3} + + wg := util.WaitGroupWrapper{} + exec := func(tk0 *testkit.TestKit) { + tk0.MustExec("use test") + tk0.MustQuery("explain format = 'brief' select id, type from t where type = 'WatchEvent';").Check(testkit.Rows(""+ + `TableReader 2.00 root partition:watch_event data:Selection`, + `└─Selection 2.00 cop[tikv] eq(test.t.type, "WatchEvent")`, + ` └─TableFullScan 3.00 cop[tikv] table:t keep order:false`)) + } + + run := func(num int) { + tk := tks[num] + wg.Run(func() { + exec(tk) + }) + } + for i := 0; i < len(tks); i++ { + run(i) + } + wg.Wait() +} + +func TestIssue31629(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_enable_list_partition = 1") + tk.MustExec("create database Issue31629") + defer tk.MustExec("drop database Issue31629") + tk.MustExec("use Issue31629") + // Test following partition types: + // HASH, RANGE, LIST: + // - directly on a single int column + // - with expression on multiple columns + // RANGE/LIST COLUMNS single column + // RANGE/LIST COLUMNS -- Verify that only single column is allowed and no expression + tests := []struct { + create string + fail bool + cols []string + }{ + {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by range(col1) (partition p0 values less than (5),partition p1 values less than (10), partition p2 values less than maxvalue)", false, []string{"col1"}}, + {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by range(Col1+col3) (partition p0 values less than (5),partition p1 values less than (10), partition p2 values less than maxvalue)", false, []string{"Col1", "col3"}}, + {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by hash(col1) partitions 3", false, []string{"col1"}}, + {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by hash(Col1+col3) partitions 3", false, []string{"Col1", "col3"}}, + {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by list(col1) (partition p0 values in (5,6,7,8,9),partition p1 values in (10,11,12,13,14), partition p2 values in (20,21,22,23,24))", false, []string{"col1"}}, + {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by list(Col1+col3) (partition p0 values in (5,6,7,8,9),partition p1 values in (10,11,12,13,14), partition p2 values in (20,21,22,23,24))", false, []string{"Col1", "col3"}}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col2) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, false, []string{"col2"}}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col2,col3) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, true, nil}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col1+1) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, true, nil}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col2) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, false, []string{"col2"}}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col2,col3) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, true, nil}, + {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col1+1) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, true, nil}, + } + + for i, tt := range tests { + createTable := "create table t1 " + tt.create + res, err := tk.Exec(createTable) + if res != nil { + res.Close() + } + if err != nil { + if tt.fail { + continue + } + } + require.Falsef(t, tt.fail, "test %d succeeded but was expected to fail! %s", i, createTable) + require.NoError(t, err) + tk.MustQuery("show warnings").Check(testkit.Rows()) + + tb, err := dom.InfoSchema().TableByName(model.NewCIStr("Issue31629"), model.NewCIStr("t1")) + require.NoError(t, err) + tbp, ok := tb.(table.PartitionedTable) + require.Truef(t, ok, "test %d does not generate a table.PartitionedTable: %s (%T, %+v)", i, createTable, tb, tb) + colNames := tbp.GetPartitionColumnNames() + checkNames := []model.CIStr{model.NewCIStr(tt.cols[0])} + for i := 1; i < len(tt.cols); i++ { + checkNames = append(checkNames, model.NewCIStr(tt.cols[i])) + } + require.ElementsMatchf(t, colNames, checkNames, "test %d %s", i, createTable) + tk.MustExec("drop table t1") + } +} + +func TestExchangePartitionStates(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + dbName := "partSchemaVer" + tk.MustExec("create database " + dbName) + tk.MustExec("use " + dbName) + tk.MustExec(`set @@global.tidb_enable_metadata_lock = ON`) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + dbName) + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec("use " + dbName) + tk4 := testkit.NewTestKit(t, store) + tk4.MustExec("use " + dbName) + tk.MustExec(`create table t (a int primary key, b varchar(255), key (b))`) + tk.MustExec(`create table tp (a int primary key, b varchar(255), key (b)) partition by range (a) (partition p0 values less than (1000000), partition p1M values less than (2000000))`) + tk.MustExec(`insert into t values (1, "1")`) + tk.MustExec(`insert into tp values (2, "2")`) + tk.MustExec(`analyze table t,tp`) + tk.MustExec("BEGIN") + tk.MustQuery(`select * from t`).Check(testkit.Rows("1 1")) + tk.MustQuery(`select * from tp`).Check(testkit.Rows("2 2")) + alterChan := make(chan error) + go func() { + // WITH VALIDATION is the default + err := tk2.ExecToErr(`alter table tp exchange partition p0 with table t`) + alterChan <- err + }() + waitFor := func(tableName, s string, pos int) { + for { + select { + case alterErr := <-alterChan: + require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) + default: + // Alter still running + } + res := tk4.MustQuery(`admin show ddl jobs where db_name = '` + strings.ToLower(dbName) + `' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() + if len(res) == 1 && res[0][pos] == s { + logutil.BgLogger().Info("Got state", zap.String("State", s)) + break + } + gotime.Sleep(50 * gotime.Millisecond) + } + // Sleep 50ms to wait load InforSchema finish, issue #46815. + gotime.Sleep(50 * gotime.Millisecond) + } + waitFor("t", "write only", 4) + tk3.MustExec(`BEGIN`) + tk3.MustExec(`insert into t values (4,"4")`) + tk3.MustContainErrMsg(`insert into t values (1000004,"1000004")`, "[table:1748]Found a row not matching the given partition set") + tk.MustExec(`insert into t values (5,"5")`) + // This should fail the alter table! + tk.MustExec(`insert into t values (1000005,"1000005")`) + + // MDL will block the alter to not continue until all clients + // are in StateWriteOnly, which tk is blocking until it commits + tk.MustExec(`COMMIT`) + waitFor("t", "rollback done", 11) + // MDL will block the alter from finish, tk is in 'rollbacked' schema version + // but the alter is still waiting for tk3 to commit, before continuing + tk.MustExec("BEGIN") + tk.MustExec(`insert into t values (1000006,"1000006")`) + tk.MustExec(`insert into t values (6,"6")`) + tk3.MustExec(`insert into t values (7,"7")`) + tk3.MustContainErrMsg(`insert into t values (1000007,"1000007")`, + "[table:1748]Found a row not matching the given partition set") + tk3.MustExec("COMMIT") + require.ErrorContains(t, <-alterChan, + "[ddl:1737]Found a row that does not match the partition") + tk3.MustExec(`BEGIN`) + tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows( + "1 1", "1000005 1000005", "1000006 1000006", "5 5", "6 6")) + tk.MustQuery(`select * from tp`).Sort().Check(testkit.Rows("2 2")) + tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows( + "1 1", "1000005 1000005", "4 4", "5 5", "7 7")) + tk3.MustQuery(`select * from tp`).Sort().Check(testkit.Rows("2 2")) + tk.MustContainErrMsg(`insert into t values (7,"7")`, + "[kv:1062]Duplicate entry '7' for key 't.PRIMARY'") + tk.MustExec(`insert into t values (8,"8")`) + tk.MustExec(`insert into t values (1000008,"1000008")`) + tk.MustExec(`insert into tp values (9,"9")`) + tk.MustExec(`insert into tp values (1000009,"1000009")`) + tk3.MustExec(`insert into t values (10,"10")`) + tk3.MustExec(`insert into t values (1000010,"1000010")`) + + tk3.MustExec(`COMMIT`) + tk.MustQuery(`show create table tp`).Check(testkit.Rows("" + + "tp CREATE TABLE `tp` (\n" + + " `a` int(11) NOT NULL,\n" + + " `b` varchar(255) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY RANGE (`a`)\n" + + "(PARTITION `p0` VALUES LESS THAN (1000000),\n" + + " PARTITION `p1M` VALUES LESS THAN (2000000))")) + tk.MustQuery(`show create table t`).Check(testkit.Rows("" + + "t CREATE TABLE `t` (\n" + + " `a` int(11) NOT NULL,\n" + + " `b` varchar(255) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + tk.MustExec(`commit`) + tk.MustExec(`insert into t values (11,"11")`) + tk.MustExec(`insert into t values (1000011,"1000011")`) + tk.MustExec(`insert into tp values (12,"12")`) + tk.MustExec(`insert into tp values (1000012,"1000012")`) +} + +// Test partition and non-partition both have check constraints. +func TestExchangePartitionCheckConstraintStates(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec(`create database check_constraint`) + tk.MustExec(`set @@global.tidb_enable_check_constraint = 1`) + tk.MustExec(`use check_constraint`) + tk.MustExec(`create table nt (a int check (a > 75) not ENFORCED, b int check (b > 50) ENFORCED)`) + tk.MustExec(`create table pt (a int check (a < 75) ENFORCED, b int check (b < 75) ENFORCED) partition by range (a) (partition p0 values less than (50), partition p1 values less than (100) )`) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec(`use check_constraint`) + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec(`use check_constraint`) + tk4 := testkit.NewTestKit(t, store) + tk4.MustExec(`use check_constraint`) + // TODO: error message to check. + errMsg := "[table:3819]Check constraint" + + tk2.MustExec("begin") + // Get table mdl. + tk2.MustQuery(`select * from nt`).Check(testkit.Rows()) + tk2.MustQuery(`select * from pt`).Check(testkit.Rows()) + alterChan := make(chan error) + go func() { + err := tk3.ExecToErr(`alter table pt exchange partition p1 with table nt`) + alterChan <- err + }() + waitFor := func(tableName, s string, pos int) { + for { + select { + case alterErr := <-alterChan: + require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) + default: + // Alter still running + } + res := tk4.MustQuery(`admin show ddl jobs where db_name = 'check_constraint' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() + if len(res) == 1 && res[0][pos] == s { + logutil.BgLogger().Info("Got state", zap.String("State", s)) + break + } + gotime.Sleep(50 * gotime.Millisecond) + } + // Sleep 50ms to wait load InforSchema finish. + gotime.Sleep(50 * gotime.Millisecond) + } + waitFor("nt", "write only", 4) + + tk.MustExec(`insert into nt values (60, 60)`) + // violate pt (a < 75) + tk.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) + // violate pt (b < 75) + tk.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) + // violate pt (a < 75) + tk.MustContainErrMsg(`update nt set a = 80 where a = 60`, errMsg) + // violate pt (b < 75) + tk.MustContainErrMsg(`update nt set b = 80 where b = 60`, errMsg) + + tk.MustExec(`insert into pt values (60, 60)`) + // violate nt (b > 50) + tk.MustContainErrMsg(`insert into pt values (60, 50)`, errMsg) + // violate nt (b > 50) + tk.MustContainErrMsg(`update pt set b = 50 where b = 60`, errMsg) + // row in partition p0(less than (50)), is ok. + tk.MustExec(`insert into pt values (30, 50)`) + + tk5 := testkit.NewTestKit(t, store) + tk5.MustExec(`use check_constraint`) + tk5.MustExec("begin") + // Let tk5 get mdl of pt with the version of write-only state. + tk5.MustQuery(`select * from pt`) + + tk6 := testkit.NewTestKit(t, store) + tk6.MustExec(`use check_constraint`) + tk6.MustExec("begin") + // Let tk6 get mdl of nt with the version of write-only state. + tk6.MustQuery(`select * from nt`) + + // Release tk2 mdl, wait ddl enter next state. + tk2.MustExec("commit") + waitFor("pt", "none", 4) + + // violate nt (b > 50) + // Now tk5 handle the sql with MDL: pt version state is write-only, nt version state is none. + tk5.MustContainErrMsg(`insert into pt values (60, 50)`, errMsg) + // Verify exists row(60, 60) in pt. + tk5.MustQuery(`select * from pt where a = 60 and b = 60`).Check(testkit.Rows("60 60")) + // Update oldData and newData both in p1, violate nt (b > 50) + tk5.MustContainErrMsg(`update pt set b = 50 where a = 60 and b = 60`, errMsg) + // Verify exists row(30, 50) in pt. + tk5.MustQuery(`select * from pt where a = 30 and b = 50`).Check(testkit.Rows("30 50")) + // update oldData in p0, newData in p1, violate nt (b > 50) + tk5.MustContainErrMsg(`update pt set a = 60 where a = 30 and b = 50`, errMsg) + + // violate pt (a < 75) + tk6.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) + // violate pt (b < 75) + tk6.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) + // Verify exists row(60, 60) in nt. + tk6.MustQuery(`select * from pt where a = 60 and b = 60`).Check(testkit.Rows("60 60")) + // violate pt (a < 75) + tk6.MustContainErrMsg(`update nt set a = 80 where a = 60 and b = 60`, errMsg) + + // Let tk5, tk6 release mdl. + tk5.MustExec("commit") + tk6.MustExec("commit") + + // Wait ddl finish. + <-alterChan +} + +// Test partition table has check constraints while non-partition table do not have. +func TestExchangePartitionCheckConstraintStatesTwo(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tk.MustExec(`create database check_constraint`) + tk.MustExec(`set @@global.tidb_enable_check_constraint = 1`) + tk.MustExec(`use check_constraint`) + tk.MustExec(`create table nt (a int, b int)`) + tk.MustExec(`create table pt (a int check (a < 75) ENFORCED, b int check (b < 75) ENFORCED) partition by range (a) (partition p0 values less than (50), partition p1 values less than (100) )`) + + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec(`use check_constraint`) + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec(`use check_constraint`) + tk4 := testkit.NewTestKit(t, store) + tk4.MustExec(`use check_constraint`) + // TODO: error message to check. + errMsg := "[table:3819]Check constraint" + + tk2.MustExec("begin") + // Get table mdl. + tk2.MustQuery(`select * from nt`).Check(testkit.Rows()) + alterChan := make(chan error) + go func() { + err := tk3.ExecToErr(`alter table pt exchange partition p1 with table nt`) + alterChan <- err + }() + waitFor := func(tableName, s string, pos int) { + for { + select { + case alterErr := <-alterChan: + require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) + default: + // Alter still running + } + res := tk4.MustQuery(`admin show ddl jobs where db_name = 'check_constraint' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() + if len(res) == 1 && res[0][pos] == s { + logutil.BgLogger().Info("Got state", zap.String("State", s)) + break + } + gotime.Sleep(50 * gotime.Millisecond) + } + // Sleep 50ms to wait load InforSchema finish. + gotime.Sleep(50 * gotime.Millisecond) + } + waitFor("nt", "write only", 4) + + tk.MustExec(`insert into nt values (60, 60)`) + // violate pt (a < 75) + tk.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) + // violate pt (b < 75) + tk.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) + // violate pt (a < 75) + tk.MustContainErrMsg(`update nt set a = 80 where a = 60`, errMsg) + // violate pt (b < 75) + tk.MustContainErrMsg(`update nt set b = 80 where b = 60`, errMsg) + + tk.MustExec(`insert into pt values (60, 60)`) + tk.MustExec(`insert into pt values (60, 50)`) + tk.MustExec(`update pt set b = 50 where b = 60`) + // row in partition p0(less than (50)), is ok. + tk.MustExec(`insert into pt values (30, 50)`) + + // Release tk2 mdl. + tk2.MustExec("commit") + // Wait ddl finish. + <-alterChan +} + +func TestAddKeyPartitionStates(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + dbName := "partSchemaVer" + tk.MustExec("create database " + dbName) + tk.MustExec("use " + dbName) + tk.MustExec(`set @@global.tidb_enable_metadata_lock = ON`) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + dbName) + tk3 := testkit.NewTestKit(t, store) + tk3.MustExec("use " + dbName) + tk4 := testkit.NewTestKit(t, store) + tk4.MustExec("use " + dbName) + tk.MustExec(`create table t (a int primary key, b varchar(255), key (b)) partition by hash (a) partitions 3`) + tk.MustExec(`insert into t values (1, "1")`) + tk.MustExec(`analyze table t`) + tk.MustExec("BEGIN") + tk.MustQuery(`select * from t`).Check(testkit.Rows("1 1")) + tk.MustExec(`insert into t values (2, "2")`) + syncChan := make(chan bool) + go func() { + tk2.MustExec(`alter table t add partition partitions 1`) + syncChan <- true + }() + waitFor := func(i int, s string) { + for { + res := tk4.MustQuery(`admin show ddl jobs where db_name = '` + strings.ToLower(dbName) + `' and table_name = 't' and job_type like 'alter table%'`).Rows() + if len(res) == 1 && res[0][i] == s { + break + } + gotime.Sleep(10 * gotime.Millisecond) + } + // Sleep 50ms to wait load InforSchema finish. + gotime.Sleep(50 * gotime.Millisecond) + } + waitFor(4, "delete only") + tk3.MustExec(`BEGIN`) + tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1")) + tk3.MustExec(`insert into t values (3,"3")`) + + tk.MustExec(`COMMIT`) + waitFor(4, "write only") + tk.MustExec(`BEGIN`) + tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2")) + tk.MustExec(`insert into t values (4,"4")`) + + tk3.MustExec(`COMMIT`) + waitFor(4, "write reorganization") + tk3.MustExec(`BEGIN`) + tk3.MustQuery(`show create table t`).Check(testkit.Rows("" + + "t CREATE TABLE `t` (\n" + + " `a` int(11) NOT NULL,\n" + + " `b` varchar(255) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY HASH (`a`) PARTITIONS 3")) + tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3")) + tk3.MustExec(`insert into t values (5,"5")`) + + tk.MustExec(`COMMIT`) + waitFor(4, "delete reorganization") + tk.MustExec(`BEGIN`) + tk.MustQuery(`show create table t`).Check(testkit.Rows("" + + "t CREATE TABLE `t` (\n" + + " `a` int(11) NOT NULL,\n" + + " `b` varchar(255) DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY HASH (`a`) PARTITIONS 4")) + tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 4")) + tk.MustExec(`insert into t values (6,"6")`) + + tk3.MustExec(`COMMIT`) + tk.MustExec(`COMMIT`) + <-syncChan + tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 4", "5 5", "6 6")) +} + +type compoundSQL struct { + selectSQL string + point bool + batchPoint bool + pruned bool + executeExplain bool + usedPartition []string + notUsedPartition []string + rowCount int +} + +type partTableCase struct { + partitionbySQL string + selectInfo []compoundSQL +} + +func executePartTableCase(t *testing.T, tk *testkit.TestKit, testCases []partTableCase, + createSQL string, insertSQLs []string, dropSQL string) { + for i, testCase := range testCases { + // create table ... partition by key ... + ddlSQL := createSQL + testCase.partitionbySQL + logutil.BgLogger().Info("Partition DDL test", zap.Int("i", i), zap.String("ddlSQL", ddlSQL)) + executeSQLWrapper(t, tk, ddlSQL) + // insert data + for _, insertsql := range insertSQLs { + executeSQLWrapper(t, tk, insertsql) + } + // execute testcases + for j, selInfo := range testCase.selectInfo { + logutil.BgLogger().Info("Select", zap.Int("j", j), zap.String("selectSQL", selInfo.selectSQL)) + tk.MustQuery(selInfo.selectSQL).Check(testkit.Rows(strconv.Itoa(selInfo.rowCount))) + if selInfo.executeExplain { + result := tk.MustQuery("EXPLAIN " + selInfo.selectSQL) + if selInfo.point { + result.CheckContain("Point_Get") + } + if selInfo.batchPoint { + result.CheckContain("Batch_Point_Get") + } + if selInfo.pruned { + for _, part := range selInfo.usedPartition { + result.CheckContain(part) + } + for _, part := range selInfo.notUsedPartition { + result.CheckNotContain(part) + } + } + } + } + executeSQLWrapper(t, tk, dropSQL) + } +} + +func executeSQLWrapper(t *testing.T, tk *testkit.TestKit, SQLString string) { + res, err := tk.Exec(SQLString) + if res != nil { + res.Close() + } + require.Nil(t, err) +} + +func TestKeyPartitionTableBasic(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database partitiondb") + defer tk.MustExec("drop database partitiondb") + tk.MustExec("use partitiondb") + testCases := []struct { + createSQL string + dropSQL string + insertSQL string + selectInfo []compoundSQL + }{ + { + createSQL: "CREATE TABLE tkey0 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3)) PARTITION BY KEY(col3) PARTITIONS 4", + insertSQL: "INSERT INTO tkey0 VALUES(1, '2023-02-22', 1, 1), (2, '2023-02-22', 2, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4)", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey0", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey0 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey0 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey0 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 0, + }, + { + "SELECT count(*) FROM tkey0 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey0 WHERE col3 = 3", + true, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey0 WHERE col3 = 3 or col3 = 4", + false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey0 WHERE col3 >1 AND col3 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, + }, + }, + + dropSQL: "DROP TABLE IF EXISTS tkey0", + }, + { + createSQL: "CREATE TABLE tkey7 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3,col1)) PARTITION BY KEY(col3,col1) PARTITIONS 4", + insertSQL: "INSERT INTO tkey7 VALUES(1, '2023-02-22', 1, 1), (1, '2023-02-22', 2, 1),(2, '2023-02-22', 2, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4),(4, '2023-02-22', 5, 4)", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey7", + false, false, false, false, []string{}, []string{}, 6, + }, + { + "SELECT count(*) FROM tkey7 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey7 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey7 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey7 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey7 WHERE col3 = 3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey7 WHERE col3 = 3 and col1 = 3", + true, false, true, true, []string{"partition:p1"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey7 WHERE col3 = 3 or col3 = 4", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey7 WHERE col3 = 3 and col1 = 3 OR col3 = 4 and col1 = 4", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey7 WHERE col1>1 and col3 >1 AND col3 < 4 and col1<3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey7", + }, + { + createSQL: "CREATE TABLE tkey8 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,PRIMARY KEY (col3,col1)) PARTITION BY KEY(col3,col1) PARTITIONS 4", + insertSQL: "INSERT INTO tkey8 VALUES(1, '2023-02-22', 111, 1), (1, '2023-02-22', 2, 1),(2, '2023-02-22', 218, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4),(4, '2023-02-22', 5, 4),(5, '2023-02-22', 5, 5),(5, '2023-02-22', 50, 2),(6, '2023-02-22', 62, 2),(60, '2023-02-22', 6, 5),(70, '2023-02-22', 50, 2),(80, '2023-02-22', 62, 2),(100, '2023-02-22', 62, 2),(2000, '2023-02-22', 6, 5),(400, '2023-02-22', 50, 2),(90, '2023-02-22', 62, 2)", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey8", + false, false, false, false, []string{}, []string{}, 16, + }, + { + "SELECT count(*) FROM tkey8 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey8 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 7, + }, + { + "SELECT count(*) FROM tkey8 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey8 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey8 WHERE col3 = 3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey8 WHERE col3 = 3 and col1 = 3", + true, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey8 WHERE col3 = 3 or col3 = 4", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey8 WHERE col3 = 3 and col1 = 3 OR col3 = 4 and col1 = 4", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey8 WHERE col1>1 and col3 >1 AND col3 < 4 and col1<3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 0, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey8", + }, + { + createSQL: "CREATE TABLE tkey6 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 VARCHAR(12) NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3)) PARTITION BY KEY(col3) PARTITIONS 4", + insertSQL: "INSERT INTO tkey6 VALUES(1, '2023-02-22', 'linpin', 1), (2, '2023-02-22', 'zhangsan', 2), (3, '2023-02-22', 'anqila', 3), (4, '2023-02-22', 'xingtian', 4),(1, '2023-02-22', 'renleifeng', 5), (2, '2023-02-22', 'peilin', 2),(1, '2023-02-22', 'abcdeeg', 7), (2, '2023-02-22', 'rpstdfed', 8)", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey6", + false, false, false, false, []string{}, []string{}, 8, + }, + { + "SELECT count(*) FROM tkey6 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey6 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey6 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey6 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey6 WHERE col3 = 'linpin'", + true, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey6 WHERE col3 = 'zhangsan' or col3 = 'linpin'", + true, true, true, true, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey6 WHERE col3 > 'linpin' AND col3 < 'qing'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey6", + }, + { + createSQL: "CREATE TABLE tkey2 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(KHH) partitions 4", + insertSQL: "INSERT INTO tkey2 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530')", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey2", + false, false, false, false, []string{}, []string{}, 17, + }, + { + "SELECT count(*) FROM tkey2 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey2 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey2 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey2 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 6, + }, + { + "SELECT count(*) FROM tkey2 WHERE KHH = 'huaian'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey2 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey2 WHERE KHH > 'nanjing' AND KHH < 'suzhou'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey2", + }, + { + createSQL: "CREATE TABLE tkey5 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (KHH, JYRQ, ZJZH))PARTITION BY KEY(KHH) partitions 4", + insertSQL: "INSERT INTO tkey5 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530')", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey5", + false, false, false, false, []string{}, []string{}, 17, + }, + { + "SELECT count(*) FROM tkey5 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey5 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey5 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey5 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 6, + }, + { + "SELECT count(*) FROM tkey5 WHERE KHH = 'huaian'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey5 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey5 WHERE KHH > 'nanjing' AND KHH < 'suzhou'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey5", + }, + { + createSQL: "CREATE TABLE tkey4 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(JYRQ, KHH) partitions 4", + insertSQL: "INSERT INTO tkey4 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530'),(1,'beijing','010'),(2,'beijing','010'),(2,'zzzzwuhan','027')", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey4", + false, false, false, false, []string{}, []string{}, 20, + }, + { + "SELECT count(*) FROM tkey4 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 7, + }, + { + "SELECT count(*) FROM tkey4 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey4 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey4 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey4 WHERE JYRQ = 2", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2 or KHH = 'zhenjiang' and JYRQ = 3", + false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2 or KHH = 'zhenjiang' and JYRQ = 3 or KHH = 'HUAIAN' and JYRQ = 15", + false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey4 WHERE JYRQ = 2 OR JYRQ = 3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey4 WHERE JYRQ = 2 OR JYRQ = 3 OR JYRQ = 15", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey4 WHERE JYRQ >6 AND JYRQ < 10", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey4 WHERE JYRQ >6 and KHH>'lianyungang' AND JYRQ < 10 and KHH<'xuzhou'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey4", + }, + { + createSQL: "CREATE TABLE tkey9 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(JYRQ, KHH, ZJZH) partitions 4", + insertSQL: "INSERT INTO tkey9 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530'),(1,'beijing','010'),(2,'beijing','010'),(2,'zzzzwuhan','027')", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey9", + false, false, false, false, []string{}, []string{}, 20, + }, + { + "SELECT count(*) FROM tkey9 PARTITION(p0)", + false, false, false, false, []string{}, []string{}, 6, + }, + { + "SELECT count(*) FROM tkey9 PARTITION(p1)", + false, false, false, false, []string{}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey9 PARTITION(p2)", + false, false, false, false, []string{}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey9 PARTITION(p3)", + false, false, false, false, []string{}, []string{}, 8, + }, + { + "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH = '0517'", + true, false, true, true, []string{"partition:p0"}, []string{"partition:p3", "partition:p1", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + { + "SELECT count(*) FROM tkey9 WHERE JYRQ = 2", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH='0517' or KHH = 'zhenjiang' and JYRQ = 3 and ZJZH = '0518'", + false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH='0517' or KHH = 'zhenjiang' and JYRQ = 3 and ZJZH = '0518' or KHH = 'NANJING' and JYRQ = 14 and ZJZH = '025'", + false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p2", "partition:p1"}, 3, + }, + { + "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + { + "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 OR JYRQ = 3", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 4, + }, + { + "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 OR JYRQ = 3 OR JYRQ = 15", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey9 WHERE JYRQ >6 AND JYRQ < 10", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, + }, + { + "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 and KHH = 'huaian' OR JYRQ = 3 and KHH = 'zhenjiang'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, + }, + }, + dropSQL: "DROP TABLE IF EXISTS tkey9", + }, + } + + for i, testCase := range testCases { + logutil.BgLogger().Info("Partition DDL test", zap.Int("i", i), zap.String("createSQL", testCase.createSQL)) + executeSQLWrapper(t, tk, testCase.createSQL) + executeSQLWrapper(t, tk, testCase.insertSQL) + for j, selInfo := range testCase.selectInfo { + logutil.BgLogger().Info("Select", zap.Int("j", j), zap.String("selectSQL", selInfo.selectSQL)) + tk.MustQuery(selInfo.selectSQL).Check(testkit.Rows(strconv.Itoa(selInfo.rowCount))) + if selInfo.executeExplain { + result := tk.MustQuery("EXPLAIN " + selInfo.selectSQL) + if selInfo.point { + result.CheckContain("Point_Get") + } + if selInfo.batchPoint { + result.CheckContain("Batch_Point_Get") + } + if selInfo.pruned { + for _, part := range selInfo.usedPartition { + result.CheckContain(part) + } + } + } + } + executeSQLWrapper(t, tk, testCase.dropSQL) + } +} + +func TestKeyPartitionTableAllFeildType(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create database partitiondb3") + defer tk.MustExec("drop database partitiondb3") + tk.MustExec("use partitiondb3") + // partition column is numeric family + createSQL := "create table tkey_numeric(\n" + + "id1 BIT(8) not null,\n" + + "id2 TINYINT not null,\n" + + "id3 BOOL not null,\n" + + "id4 SMALLINT not null,\n" + + "id5 MEDIUMINT not null,\n" + + "id6 INT not null,\n" + + "id7 BIGINT not null,\n" + + "id8 DECIMAL(12,4) not null,\n" + + "id9 FLOAT not null,\n" + + "id10 DOUBLE not null,\n" + + "name varchar(20),\n" + + "primary key(id1,id2,id3,id4,id5,id6,id7,id8,id9,id10)\n" + + ")\n" + dropSQL := "drop table tkey_numeric" + insertSQLS := []string{ + "INSERT INTO tkey_numeric VALUES(1,1,0,1,1,1,1,1.1,120.1,367.45,'linpin'),(12,12,12,12,12,12,12,12.1,1220.1,3267.45,'anqila')", + "INSERT INTO tkey_numeric VALUES(0,2,1,2,2,2,2,2.78,16.78,17.25,'ring'),(33,33,33,33,33,33,33,33.78,336.78,37.25,'black')", + "INSERT INTO tkey_numeric VALUES(2,3,1,3,3,3,3,3.78,26.78,417.25,'liudehua'),(22,23,21,23,23,23,23,32.78,26.72,27.15,'chenchen')", + "INSERT INTO tkey_numeric VALUES(3,3,2,4,4,4,4,4.78,46.48,89.35,'guofucheng'), (4,4,4,5,5,5,5,5.78,56.48,59.35,'zhangxuyou')", + "INSERT INTO tkey_numeric VALUES(5,5,5,5,5,5,5,5.78,56.48,59.35,'xietingfeng'),(34,34,34,34,34,34,34,34.78,346.78,34.25,'dongxu')", + "INSERT INTO tkey_numeric VALUES(250,120,120,250,250,258,348,38.78,186.48,719.35,'chenguanxi'),(35,35,35,250,35,35,35,35.78,356.48,35.35,'chenguanxi')", + } + testCases := []partTableCase{ + { + partitionbySQL: "PARTITION BY KEY(id1) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 5, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id1 = 3", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id1 = 3 or id1 = 4", + false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id1 >1 AND id1 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id2) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id2 = 3", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id2 = 3 or id2 = 4", + false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id2 >1 AND id2 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 3, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id3) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p0", "partition:p1", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id3 = 5", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id3 = 5 or id3 = 4", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id3 >1 AND id3 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id4) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 5, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id4 = 5", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id4 = 5 or id4 = 4", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id4 >1 AND id4 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id5) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p3", "partition:p0"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id5 = 5", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id5 = 5 or id5 = 4", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id5 >1 AND id5 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id6) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id6 = 5", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id6 = 5 or id6 = 4", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id6 >1 AND id6 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id7) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id7 = 5", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id7 = 5 or id7 = 4", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id7 >1 AND id7 < 4", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id8) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id8 = 1.1", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p2", "partition:p0", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id8 = 1.1 or id8 = 33.78", + false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id8 >1 AND id8 < 4", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id9) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id9 = 46.48", + false, false, true, true, []string{}, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id9 = 46.48 or id9 = 336.78", + false, false, true, true, []string{}, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id9 >45 AND id9 < 47", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id10) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_numeric", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_numeric PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id10 = 46.48", + false, false, true, true, []string{}, []string{}, 0, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id10 = 46.48 or id10 = 336.78", + false, false, true, true, []string{}, []string{}, 0, + }, + { + "SELECT count(*) FROM tkey_numeric WHERE id10 >366 AND id10 < 368", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + } + executePartTableCase(t, tk, testCases, createSQL, insertSQLS, dropSQL) + + // partition column is date/time family + createSQL2 := "create table tkey_datetime(\n" + + "id1 DATE not null,\n" + + "id2 TIME not null,\n" + + "id3 DATETIME not null,\n" + + "id4 TIMESTAMP not null,\n" + + "id5 YEAR not null,\n" + + "name varchar(20),\n" + + "primary key(id1, id2, id3, id4, id5)\n" + + ")\n" + dropSQL2 := "drop table tkey_datetime" + insertSQLS2 := []string{ + "insert into tkey_datetime values('2012-04-10', '12:12:12', '2012-04-10 12:12:12', '2012-04-10 12:12:12.12345', 2012, 'linpin')", + "insert into tkey_datetime values('2013-05-11', '13:13:13', '2013-05-11 13:13:13', '2013-05-11 13:13:13.43133', 2013, 'minghua')", + "insert into tkey_datetime values('2014-06-12', '14:14:14', '2014-06-12 14:14:14', '2014-06-12 14:14:14.32344', 2014, 'oyangfeng')", + "insert into tkey_datetime values('2015-07-13', '15:15:15', '2015-07-13 15:15:15', '2015-07-13 15:15:15.42544', 2015, 'pengdehuai')", + "insert into tkey_datetime values('2021-08-14', '16:16:16', '2021-08-14 16:16:16', '2021-08-14 16:16:16.18945', 2021, 'shenwanshan')", + "insert into tkey_datetime values('2022-12-23', '23:12:15', '2022-12-23 23:12:15', '2022-12-23 23:12:15.43133', 2022, 'tangchap')", + "insert into tkey_datetime values('2023-01-12', '20:38:14', '2023-01-12 20:38:14', '2023-01-12 20:38:14.32344', 2023, 'xinyu')", + "insert into tkey_datetime values('2018-07-13', '07:15:15', '2018-07-13 07:15:15', '2018-07-13 07:15:15.42544', 2018, 'zongyang')", + "insert into tkey_datetime values('1980-01-30', '00:12:15', '1980-01-30 00:12:15', '1980-01-30 00:12:15.42544', 1980, 'MAYUWEI')", + "insert into tkey_datetime values('1980-03-30', '00:13:15', '1980-03-30 00:13:15', '1980-03-30 00:13:15.42544', 1980, 'maqinwei')", + } + testCases2 := []partTableCase{ + { + partitionbySQL: "PARTITION BY KEY(id1) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_datetime", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id1 = '2012-04-10'", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id1 = '2012-04-10' or id1 = '2018-07-13'", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id1 >'2012-04-10' AND id1 < '2014-04-10'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id3) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_datetime", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id3 = '2012-04-10 12:12:12'", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id3 = '2012-04-10 12:12:12' or id3 = '2021-08-14 16:16:16'", + false, false, true, true, []string{"partition:p3", "partition:p1"}, []string{"partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id3 >'2012-04-10 12:12:12' AND id3 < '2014-04-10 12:12:12'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id4) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_datetime", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id4 = '2012-04-10 12:12:12'", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id4 = '2012-04-10 12:12:12' or id4 = '2021-08-14 16:16:16'", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id4 >'2012-04-10 12:12:12' AND id4 < '2014-04-10 12:12:12'", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id5) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_datetime", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id5 = 2012", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id5 = 2012 or id5 = 2018", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_datetime WHERE id5 >2012 AND id5 < 2014", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p3", "partition:p0"}, 1, + }, + }, + }, + } + executePartTableCase(t, tk, testCases2, createSQL2, insertSQLS2, dropSQL2) + + // partition column is string family + createSQL3 := "create table tkey_string(\n" + + "id1 CHAR(16) not null,\n" + + "id2 VARCHAR(16) not null,\n" + + "id3 BINARY(16) not null,\n" + + "id4 VARBINARY(16) not null,\n" + + "id5 BLOB not null,\n" + + "id6 TEXT not null,\n" + + "id7 ENUM('x-small', 'small', 'medium', 'large', 'x-large') not null,\n" + + "id8 SET ('a', 'b', 'c', 'd') not null,\n" + + "name varchar(16),\n" + + "primary key(id1, id2, id3, id4, id7, id8)\n" + + ")\n" + dropSQL3 := "drop table tkey_string" + insertSQLS3 := []string{ + "INSERT INTO tkey_string VALUES('huaian','huaian','huaian','huaian','huaian','huaian','x-small','a','linpin')", + "INSERT INTO tkey_string VALUES('nanjing','nanjing','nanjing','nanjing','nanjing','nanjing','small','b','linpin')", + "INSERT INTO tkey_string VALUES('zhenjiang','zhenjiang','zhenjiang','zhenjiang','zhenjiang','zhenjiang','medium','c','linpin')", + "INSERT INTO tkey_string VALUES('suzhou','suzhou','suzhou','suzhou','suzhou','suzhou','large','d','linpin')", + "INSERT INTO tkey_string VALUES('wuxi','wuxi','wuxi','wuxi','wuxi','wuxi','x-large','a','linpin')", + } + testCases3 := []partTableCase{ + { + partitionbySQL: "PARTITION BY KEY(id1) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_string WHERE id1 = 'huaian'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id1 = 'huaian' or id1 = 'suzhou'", + false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id1 >'huaian' AND id1 < 'suzhou'", + false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id2) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_string WHERE id2 = 'huaian'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id2 = 'huaian' or id2 = 'suzhou'", + false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id2 >'huaian' AND id2 < 'suzhou'", + false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id3) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id3 = 0x73757A686F7500000000000000000000", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id3 = 0x73757A686F7500000000000000000000 or id3 = 0x6E616E6A696E67000000000000000000", + false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id3 >0x67756169616E00000000000000000000 AND id3 < 0x6E616E6A696E67000000000000000000", + false, false, true, true, []string{"partition:p1", "partition:p0", "partition:p2", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id4) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_string WHERE id4 = 0x68756169616E", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id4 = 0x68756169616E or id4 = 0x73757A686F75", + false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id4 >0x73757A686F75 AND id4 < 0x78757869", + false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id7) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id7 = 'x-small'", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string WHERE id7 = 'x-small' or id7 = 'large'", + false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id7 > 'large' AND id7 < 'x-small'", + false, false, true, true, []string{"partition:p1", "partition:p0", "partition:p3"}, []string{"partition:p2"}, 3, + }, + }, + }, + { + partitionbySQL: "PARTITION BY KEY(id8) partitions 4", + selectInfo: []compoundSQL{ + { + "SELECT count(*) FROM tkey_string", + false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p0)", + false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p1)", + false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p2)", + false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, + }, + { + "SELECT count(*) FROM tkey_string PARTITION(p3)", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, + }, + { + "SELECT count(*) FROM tkey_string WHERE id8 = 'a'", + false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, + }, + { + "SELECT count(*) FROM tkey_string WHERE id8 = 'a' or id8 = 'b'", + false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 3, + }, + { + "SELECT count(*) FROM tkey_string WHERE id8 > 'a' AND id8 < 'c'", + false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, + }, + }, + }, + } + executePartTableCase(t, tk, testCases3, createSQL3, insertSQLS3, dropSQL3) +} + +func TestPruneModeWarningInfo(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@tidb_partition_prune_mode = 'static'") + tk.MustQuery("show warnings").Check(testkit.Rows()) + tk.MustExec("set session tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats", + "Warning 1105 Please avoid setting partition prune mode to dynamic at session level and set partition prune mode to dynamic at global level")) + tk.MustExec("set global tidb_partition_prune_mode = 'dynamic'") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats")) +} + +type testCallback struct { + ddl.Callback + OnJobRunBeforeExported func(job *model.Job) +} + +func newTestCallBack(t *testing.T, dom *domain.Domain) *testCallback { + defHookFactory, err := ddl.GetCustomizedHook("default_hook") + require.NoError(t, err) + return &testCallback{ + Callback: defHookFactory(dom), + } +} + +func (c *testCallback) OnJobRunBefore(job *model.Job) { + if c.OnJobRunBeforeExported != nil { + c.OnJobRunBeforeExported(job) + } +} + +func TestPartitionByIntListExtensivePart(t *testing.T) { + limitSizeOfTest := true + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + schemaName := "PartitionByIntListExtensive" + tk.MustExec("create database " + schemaName) + tk.MustExec("use " + schemaName) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + schemaName) + + tBase := `(lp tinyint unsigned, a int unsigned, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, key (b), key (c,b), key (d,c), key(e), primary key (a, lp))` + t2Str := `create table t2 ` + tBase + tStr := `create table t ` + tBase + + rows := 100 + pkInserts := 20 + pkUpdates := 20 + pkDeletes := 10 // Enough to delete half of what is inserted? + tStart := []string{ + // Non partitioned + tStr, + // RANGE COLUMNS + tStr + ` partition by list (lp) (partition p0 values in (0,6),partition p1 values in (1), partition p2 values in (2), partition p3 values in (3), partition p4 values in (4,5))`, + // KEY + tStr + ` partition by key(a) partitions 5`, + // HASH + tStr + ` partition by hash(a) partitions 5`, + // HASH with function + tStr + ` partition by hash(a DIV 3) partitions 5`, + } + if limitSizeOfTest { + tStart = tStart[:2] + } + quarterUintRange := 1 << 30 + quarterUintRangeStr := fmt.Sprintf("%d", quarterUintRange) + halfUintRangeStr := fmt.Sprintf("%d", 2*quarterUintRange) + threeQuarterUintRangeStr := fmt.Sprintf("%d", 3*quarterUintRange) + tAlter := []string{ + // LIST + `alter table t partition by list (lp) (partition p0 values in (2), partition p1 values in (1,3,5), partition p2 values in (0,4,6))`, + `alter table t partition by list (lp) (partition p3 values in (3), partition p4 values in (4), partition p2 values in (2), partition p6 values in (6), partition p5 values in (5), partition p1 values in (1), partition p0 values in (0))`, + // LIST COLUMNS + `alter table t partition by list columns (lp) (partition p0 values in (2), partition p1 values in (1,3,5), partition p2 values in (0,4,6))`, + `alter table t partition by list columns (lp) (partition p3 values in (3), partition p4 values in (4), partition p2 values in (2), partition p6 values in (6), partition p5 values in (5), partition p1 values in (1), partition p0 values in (0))`, + // RANGE COLUMNS + `alter table t partition by range (a) (partition pFirst values less than (` + halfUintRangeStr + `), partition pLast values less than (MAXVALUE))`, + // RANGE COLUMNS + `alter table t partition by range (a) (partition pFirst values less than (` + quarterUintRangeStr + `),` + + `partition pLowMid values less than (` + halfUintRangeStr + `),` + + `partition pHighMid values less than (` + threeQuarterUintRangeStr + `),` + + `partition pLast values less than (maxvalue))`, + // KEY + `alter table t partition by key(a) partitions 7`, + `alter table t partition by key(a) partitions 3`, + // Hash + `alter table t partition by hash(a) partitions 7`, + `alter table t partition by hash(a) partitions 3`, + // Hash + `alter table t partition by hash(a DIV 13) partitions 7`, + `alter table t partition by hash(a DIV 13) partitions 3`, + } + if limitSizeOfTest { + tAlter = tAlter[:2] + } + + seed := gotime.Now().UnixNano() + logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) + reorgRand := rand.New(rand.NewSource(seed)) + for _, createSQL := range tStart { + for _, alterSQL := range tAlter { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewIntPK() + getValues := getInt7ValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } + } + for _, createSQL := range tStart[1:] { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewIntPK() + getValues := getInt7ValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } +} + +func getInt7ValuesFunc() func(string, bool, *rand.Rand) string { + cnt := 0 + return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { + s := `(%d, %s, '%s', %d, '%s', '%s', %f, '%s')` + if asAssignment { + s = `lp = %d, a = %s, b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` + } + cnt++ + lp, err := strconv.Atoi(pk) + if err != nil { + lp = 0 + } + return fmt.Sprintf(s, + lp%7, + pk, + randStr(reorgRand.Intn(19), reorgRand), + cnt, //reorgRand.Int31(), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + reorgRand.Float64(), + randStr(512+reorgRand.Intn(1024), reorgRand)) + } +} + +func TestPartitionByIntExtensivePart(t *testing.T) { + limitSizeOfTest := true + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + schemaName := "PartitionByIntExtensive" + tk.MustExec("create database " + schemaName) + tk.MustExec("use " + schemaName) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + schemaName) + + tBase := `(a int unsigned, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))` + t2Str := `create table t2 ` + tBase + tStr := `create table t ` + tBase + + rows := 100 + pkInserts := 20 + pkUpdates := 20 + pkDeletes := 10 // Enough to delete half of what is inserted? + thirdUintRange := 1 << 32 / 2 + thirdUintRangeStr := fmt.Sprintf("%d", thirdUintRange) + twoThirdUintRangeStr := fmt.Sprintf("%d", 2*thirdUintRange) + tStart := []string{ + // Non partitioned + tStr, + // RANGE COLUMNS + tStr + ` partition by range (a) (partition pFirst values less than (` + thirdUintRangeStr + `),` + + `partition pMid values less than (` + twoThirdUintRangeStr + `), partition pLast values less than (maxvalue))`, + // KEY + tStr + ` partition by key(a) partitions 5`, + // HASH + tStr + ` partition by hash(a) partitions 5`, + // HASH with function + tStr + ` partition by hash(a DIV 3) partitions 5`, + } + if limitSizeOfTest { + tStart = tStart[:2] + } + quarterUintRange := 1 << 30 + quarterUintRangeStr := fmt.Sprintf("%d", quarterUintRange) + halfUintRangeStr := fmt.Sprintf("%d", 2*quarterUintRange) + threeQuarterUintRangeStr := fmt.Sprintf("%d", 3*quarterUintRange) + tAlter := []string{ + // RANGE COLUMNS + `alter table t partition by range (a) (partition pFirst values less than (` + halfUintRangeStr + `), partition pLast values less than (MAXVALUE))`, + // RANGE COLUMNS + `alter table t partition by range (a) (partition pFirst values less than (` + quarterUintRangeStr + `),` + + `partition pLowMid values less than (` + halfUintRangeStr + `),` + + `partition pHighMid values less than (` + threeQuarterUintRangeStr + `),` + + `partition pLast values less than (maxvalue))`, + // KEY + `alter table t partition by key(a) partitions 7`, + `alter table t partition by key(a) partitions 3`, + // Hash + `alter table t partition by hash(a) partitions 7`, + `alter table t partition by hash(a) partitions 3`, + // Hash + `alter table t partition by hash(a DIV 13) partitions 7`, + `alter table t partition by hash(a DIV 13) partitions 3`, + } + if limitSizeOfTest { + tAlter = tAlter[:2] + } + + seed := gotime.Now().UnixNano() + logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) + reorgRand := rand.New(rand.NewSource(seed)) + for _, createSQL := range tStart { + for _, alterSQL := range tAlter { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewIntPK() + getValues := getIntValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } + } + for _, createSQL := range tStart[1:] { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewIntPK() + getValues := getIntValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } +} + +func getNewIntPK() func(map[string]struct{}, string, *rand.Rand) string { + return func(m map[string]struct{}, suf string, reorgRand *rand.Rand) string { + uintPK := reorgRand.Uint32() + newPK := strconv.FormatUint(uint64(uintPK), 10) + for _, ok := m[newPK]; ok; { + uintPK = reorgRand.Uint32() + newPK = strconv.FormatUint(uint64(uintPK), 10) + _, ok = m[newPK] + } + m[newPK] = struct{}{} + return newPK + } +} + +func getIntValuesFunc() func(string, bool, *rand.Rand) string { + cnt := 0 + return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { + s := `(%s, '%s', %d, '%s', '%s', %f, '%s')` + if asAssignment { + s = `a = %s, b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` + } + cnt++ + return fmt.Sprintf(s, + pk, + randStr(reorgRand.Intn(19), reorgRand), + cnt, //reorgRand.Int31(), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + reorgRand.Float64(), + randStr(512+reorgRand.Intn(1024), reorgRand)) + } +} + +func TestPartitionByExtensivePart(t *testing.T) { + limitSizeOfTest := true + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + schemaName := "PartitionByExtensive" + tk.MustExec("create database " + schemaName) + tk.MustExec("use " + schemaName) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + schemaName) + + tBase := `(a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))` + t2Str := `create table t2 ` + tBase + tStr := `create table t ` + tBase + + rows := 100 + pkInserts := 20 + pkUpdates := 20 + pkDeletes := 10 // Enough to delete half of what is inserted? + tStart := []string{ + // Non partitioned + tStr, + // RANGE COLUMNS + tStr + ` partition by range columns (a) (partition pNull values less than (""), partition pM values less than ("M"), partition pLast values less than (maxvalue))`, + // KEY + tStr + ` partition by key(a) partitions 5`, + } + if limitSizeOfTest { + tStart = tStart[:2] + } + showCreateStr := "t CREATE TABLE `t` (\n" + + " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + + " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + + " `c` int(11) DEFAULT NULL,\n" + + " `d` datetime DEFAULT NULL,\n" + + " `e` timestamp NULL DEFAULT NULL,\n" + + " `f` double DEFAULT NULL,\n" + + " `g` text DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`),\n" + + " KEY `c` (`c`,`b`),\n" + + " KEY `d` (`d`,`c`),\n" + + " KEY `e` (`e`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + tAlter := []struct{ alter, result string }{ + { + // RANGE COLUMNS + alter: `alter table t partition by range columns (a) (partition pH values less than ("H"), partition pLast values less than (MAXVALUE))`, + result: showCreateStr + + "PARTITION BY RANGE COLUMNS(`a`)\n" + + "(PARTITION `pH` VALUES LESS THAN ('H'),\n" + + " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))", + }, + { + // RANGE COLUMNS + alter: `alter table t partition by range columns (a) (partition pNull values less than (""), partition pG values less than ("G"), partition pR values less than ("R"), partition pLast values less than (maxvalue))`, + result: showCreateStr + + "PARTITION BY RANGE COLUMNS(`a`)\n" + + "(PARTITION `pNull` VALUES LESS THAN (''),\n" + + " PARTITION `pG` VALUES LESS THAN ('G'),\n" + + " PARTITION `pR` VALUES LESS THAN ('R'),\n" + + " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))", + }, + // KEY + { + alter: `alter table t partition by key(a) partitions 7`, + result: showCreateStr + + "PARTITION BY KEY (`a`) PARTITIONS 7", + }, + { + alter: `alter table t partition by key(a) partitions 3`, + result: showCreateStr + + "PARTITION BY KEY (`a`) PARTITIONS 3", + }, + } + if limitSizeOfTest { + tAlter = tAlter[:2] + } + + seed := gotime.Now().UnixNano() + logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) + reorgRand := rand.New(rand.NewSource(seed)) + for _, createSQL := range tStart { + for _, alterSQL := range tAlter { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewStringPK() + getValues := getValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL.alter, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + res := tk.MustQuery(`show create table t`) + res.AddComment("create SQL: " + createSQL + "\nalterSQL: " + alterSQL.alter) + res.Check(testkit.Rows(alterSQL.result)) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } + } + + for _, createSQL := range tStart[1:] { + tk.MustExec(createSQL) + tk.MustExec(t2Str) + getNewPK := getNewStringPK() + getValues := getValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) + tk.MustExec(`drop table t`) + tk.MustExec(`drop table t2`) + } +} + +func TestReorgPartExtensivePart(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + schemaName := "ReorgPartExtensive" + tk.MustExec("create database " + schemaName) + tk.MustExec("use " + schemaName) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use " + schemaName) + // TODO: Handle different column types? + // TODO: Handle index for different column types / combinations as well? + + // Steps: + // - create a table (should at least test both LIST and RANGE partition, Including COLUMNS) + // - add base data + // - start DDL + // - before each (and after?) each state transition: + // - insert, update and delete concurrently, to verify that the states are correctly handled. + // - TODO: Crash (if rollback is needed, then OK, but the rest need to be tested + // - TODO: Fail + // - TODO: run queries that could clash with backfill etc. (How to handle expected errors?) + // - TODO: on both the 'current' state and 'previous' state! + // - run ADMIN CHECK TABLE + // + + tk.MustExec(`create table t (a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a)) partition by range columns (a) (partition pNull values less than (""), partition pM values less than ("M"), partition pLast values less than (maxvalue))`) + tk.MustExec(`create table t2 (a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))`) + + // TODO: Test again with timestamp in col e!! + tk.MustQuery(`show create table t`).Check(testkit.Rows("" + + "t CREATE TABLE `t` (\n" + + " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + + " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + + " `c` int(11) DEFAULT NULL,\n" + + " `d` datetime DEFAULT NULL,\n" + + " `e` timestamp NULL DEFAULT NULL,\n" + + " `f` double DEFAULT NULL,\n" + + " `g` text DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + + "PARTITION BY RANGE COLUMNS(`a`)\n" + + "(PARTITION `pNull` VALUES LESS THAN (''),\n" + + " PARTITION `pM` VALUES LESS THAN ('M'),\n" + + " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))")) + + tk.MustQuery(`show create table t2`).Check(testkit.Rows("" + + "t2 CREATE TABLE `t2` (\n" + + " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + + " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + + " `c` int(11) DEFAULT NULL,\n" + + " `d` datetime DEFAULT NULL,\n" + + " `e` timestamp NULL DEFAULT NULL,\n" + + " `f` double DEFAULT NULL,\n" + + " `g` text DEFAULT NULL,\n" + + " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + + " KEY `b` (`b`),\n" + + " KEY `c` (`c`,`b`),\n" + + " KEY `d` (`d`,`c`),\n" + + " KEY `e` (`e`)\n" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) + + rows := 1000 + pkInserts := 200 + pkUpdates := 200 + pkDeletes := 100 // Enough to delete half of what is inserted? + alterStr := `alter table t reorganize partition pNull, pM, pLast into (partition pI values less than ("I"), partition pQ values less than ("q"), partition pLast values less than (MAXVALUE))` + seed := rand.Int63() + logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) + reorgRand := rand.New(rand.NewSource(seed)) + getNewPK := getNewStringPK() + getValues := getValuesFunc() + checkDMLInAllStates(t, tk, tk2, schemaName, alterStr, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) +} + +func getNewStringPK() func(map[string]struct{}, string, *rand.Rand) string { + return func(m map[string]struct{}, suf string, reorgRand *rand.Rand) string { + newPK := randStr(2+reorgRand.Intn(5), reorgRand) + suf + lowerPK := strings.ToLower(newPK) + for _, ok := m[lowerPK]; ok; { + newPK = randStr(2+reorgRand.Intn(5), reorgRand) + lowerPK = strings.ToLower(newPK) + _, ok = m[lowerPK] + } + m[lowerPK] = struct{}{} + return newPK + } +} + +func getValuesFunc() func(string, bool, *rand.Rand) string { + cnt := 0 + return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { + s := `('%s', '%s', %d, '%s', '%s', %f, '%s')` + if asAssignment { + s = `a = '%s', b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` + } + cnt++ + return fmt.Sprintf(s, + pk, + randStr(reorgRand.Intn(19), reorgRand), + cnt, //reorgRand.Int31(), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), + reorgRand.Float64(), + randStr(512+reorgRand.Intn(1024), reorgRand)) + } +} + +func checkDMLInAllStates(t *testing.T, tk, tk2 *testkit.TestKit, schemaName, alterStr string, + rows, pkInserts, pkUpdates, pkDeletes int, + reorgRand *rand.Rand, + getNewPK func(map[string]struct{}, string, *rand.Rand) string, + getValues func(string, bool, *rand.Rand) string) { + dom := domain.GetDomain(tk.Session()) + originHook := dom.DDL().GetHook() + defer dom.DDL().SetHook(originHook) + hook := newTestCallBack(t, dom) + dom.DDL().SetHook(hook) + + pkMap := make(map[string]struct{}, rows) + pkArray := make([]string, 0, len(pkMap)) + // Generate a start set: + for i := 0; i < rows; i++ { + pk := getNewPK(pkMap, "-o", reorgRand) + pkArray = append(pkArray, pk) + values := getValues(pk, false, reorgRand) + tk.MustExec(`insert into t values ` + values) + tk.MustExec(`insert into t2 values ` + values) + } + tk.MustExec(`analyze table t`) + tk.MustExec(`analyze table t2`) + tk.MustQuery(`select * from t except select * from t2`).Check(testkit.Rows()) + tk.MustQuery(`select * from t2 except select * from t`).Check(testkit.Rows()) + + // How to arrange data for possible collisions? + // change both PK column, SK column and non indexed column! + // Run various changes in transactions, in two concurrent sessions + // + mirror those transactions on a copy of the same table and data without DDL + // to verify expected transaction conflicts! + // We should try to collide: + // Current data : 1-1000 + // insert vN 1-200 // random order, random length of transaction? + // insert vN-1 100-300 // interleaved with vN, random order+length of txn? + // update vN 1-20, 100-120, 200-220, 300-320.. + // update vN-1 10-30, 110-130, 210-230, ... + // delete vN + // delete vN-1 + // insert update delete <- + // insert + // update + // delete + // Note: update the PK so it moves between different before and after partitions + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + currentState := model.StateNone + transitions := 0 + var currTbl table.Table + currSchema := sessiontxn.GetTxnManager(tk2.Session()).GetTxnInfoSchema() + prevTbl, err := currSchema.TableByName(model.NewCIStr(schemaName), model.NewCIStr("t")) + require.NoError(t, err) + var hookErr error + hook.OnJobRunBeforeExported = func(job *model.Job) { + if hookErr != nil { + // Enough to find a single error + return + } + if job.Type == model.ActionReorganizePartition && job.SchemaState != currentState { + transitions++ + // use random generation to possibly trigger txn collisions / deadlocks? + // insert (dup in new/old , non dup) + // update (dup in new/old , non dup as in same old/new partition -> update, different new/old -> insert + delete) + // delete + // verify with select after commit? + + logutil.BgLogger().Info("State before ins/upd/del", zap.Int("transitions", transitions), + zap.Int("rows", len(pkMap)), zap.Stringer("SchemaState", job.SchemaState)) + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + // Start with PK changes (non duplicate keys) + insPK := make([]string, 0, pkInserts) + values := make([]string, 0, pkInserts) + for i := 0; i < pkInserts; i += 2 { + pk := getNewPK(pkMap, "-i0", reorgRand) + logutil.BgLogger().Debug("insert1", zap.String("pk", pk)) + pkArray = append(pkArray, pk) + insPK = append(insPK, pk) + values = append(values, getValues(pk, false, reorgRand)) + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + hookErr = tk2.ExecToErr(`insert into t values ` + strings.Join(values, ",")) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`insert into t2 values ` + strings.Join(values, ",")) + if hookErr != nil { + return + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + currSchema = sessiontxn.GetTxnManager(tk2.Session()).GetTxnInfoSchema() + currTbl, hookErr = currSchema.TableByName(model.NewCIStr(schemaName), model.NewCIStr("t")) + + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using previous schema version + + values = values[:0] + for i := 1; i < pkInserts; i += 2 { + pk := getNewPK(pkMap, "-i1", reorgRand) + logutil.BgLogger().Debug("insert2", zap.String("pk", pk)) + pkArray = append(pkArray, pk) + insPK = append(insPK, pk) + values = append(values, getValues(pk, false, reorgRand)) + } + hookErr = tk2.ExecToErr(`insert into t values ` + strings.Join(values, ",")) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`insert into t2 values ` + strings.Join(values, ",")) + if hookErr != nil { + return + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + rs, err := tk2.Exec(`select count(*) from t`) + if err != nil { + hookErr = err + return + } + tRows := tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + rs, err = tk2.Exec(`select count(*) from t2`) + if err != nil { + hookErr = err + return + } + t2Rows := tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + if tRows != t2Rows { + logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) + } + + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using current schema version + + // Half from insert (1/4 in current schema version) + values = values[:0] + for i := 0; i < pkUpdates; i += 4 { + insIdx := reorgRand.Intn(len(insPK)) + oldPK := insPK[insIdx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + newPK := getNewPK(pkMap, "-u0", reorgRand) + insPK[insIdx] = newPK + idx := len(pkArray) - len(insPK) + insIdx + pkArray[idx] = newPK + value := getValues(newPK, true, reorgRand) + + logutil.BgLogger().Debug("update1", zap.String("old", oldPK), zap.String("value", value)) + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + + // Also do some non-pk column updates! + insIdx = reorgRand.Intn(len(insPK)) + oldPK = insPK[insIdx] + value = getValues(oldPK, true, reorgRand) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using previous schema version + + // Half from insert (1/4 in previous schema version) + values = values[:0] + for i := 1; i < pkUpdates; i += 4 { + insIdx := reorgRand.Intn(len(insPK)) + oldPK := insPK[insIdx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + newPK := getNewPK(pkMap, "-u1", reorgRand) + insPK[insIdx] = newPK + idx := len(pkArray) - len(insPK) + insIdx + pkArray[idx] = newPK + value := getValues(newPK, true, reorgRand) + logutil.BgLogger().Debug("update2", zap.String("old", oldPK), zap.String("value", value)) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + + // Also do some non-pk column updates! + // Note: if PK changes it does RemoveRecord + AddRecord + insIdx = reorgRand.Intn(len(insPK)) + oldPK = insPK[insIdx] + value = getValues(oldPK, true, reorgRand) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + // Half from Old + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using current schema version + + // Half from old (1/4 in current schema version) + values = values[:0] + for i := 2; i < pkUpdates; i += 4 { + idx := reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK := pkArray[idx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + newPK := getNewPK(pkMap, "-u2", reorgRand) + pkArray[idx] = newPK + value := getValues(newPK, true, reorgRand) + logutil.BgLogger().Debug("update3", zap.String("old", oldPK), zap.String("value", value)) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + + // Also do some non-pk column updates! + idx = reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK = pkArray[idx] + value = getValues(oldPK, true, reorgRand) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using previous schema version + + // Half from old (1/4 in previous schema version) + values = values[:0] + for i := 3; i < pkUpdates; i += 4 { + idx := reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK := pkArray[idx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + newPK := getNewPK(pkMap, "-u3", reorgRand) + pkArray[idx] = newPK + value := getValues(newPK, true, reorgRand) + logutil.BgLogger().Debug("update4", zap.String("old", oldPK), zap.String("value", value)) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + + // Also do some non-pk column updates! + idx = reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK = pkArray[idx] + value = getValues(oldPK, true, reorgRand) + + hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + rs, err = tk2.Exec(`select count(*) from t`) + if err != nil { + hookErr = err + return + } + tRows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + rs, err = tk2.Exec(`select count(*) from t2`) + if err != nil { + hookErr = err + return + } + t2Rows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + if tRows != t2Rows { + logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) + } + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using current schema version + + // Half from insert (1/4 in current schema version) + values = values[:0] + for i := 0; i < pkDeletes; i += 4 { + insIdx := reorgRand.Intn(len(insPK)) + oldPK := insPK[insIdx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + idx := len(pkArray) - len(insPK) + insIdx + insPK = append(insPK[:insIdx], insPK[insIdx+1:]...) + pkArray = append(pkArray[:idx], pkArray[idx+1:]...) + logutil.BgLogger().Debug("delete0", zap.String("pk", oldPK)) + + hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using previous schema version + + // Half from insert (1/4 in previous schema version) + values = values[:0] + for i := 1; i < pkDeletes; i += 4 { + insIdx := reorgRand.Intn(len(insPK)) + oldPK := insPK[insIdx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + idx := len(pkArray) - len(insPK) + insIdx + insPK = append(insPK[:insIdx], insPK[insIdx+1:]...) + pkArray = append(pkArray[:idx], pkArray[idx+1:]...) + logutil.BgLogger().Debug("delete1", zap.String("pk", oldPK)) + + hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + // Half from Old + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using current schema version + + // Half from old (1/4 in current schema version) + values = values[:0] + for i := 2; i < pkDeletes; i += 4 { + idx := reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK := pkArray[idx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + pkArray = append(pkArray[:idx], pkArray[idx+1:]...) + logutil.BgLogger().Debug("delete2", zap.String("pk", oldPK)) + + hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + if len(pkMap) != len(pkArray) { + panic("Different length!!!") + } + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using previous schema version + + // Half from old (1/4 in previous schema version) + values = values[:0] + for i := 3; i < pkDeletes; i += 4 { + idx := reorgRand.Intn(len(pkArray) - len(insPK)) + oldPK := pkArray[idx] + lowerPK := strings.ToLower(oldPK) + delete(pkMap, lowerPK) + pkArray = append(pkArray[:idx], pkArray[idx+1:]...) + logutil.BgLogger().Debug("delete3", zap.String("pk", oldPK)) + + hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) + if hookErr != nil { + return + } + } + tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + rs, err = tk2.Exec(`select count(*) from t`) + if err != nil { + hookErr = err + return + } + tRows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + rs, err = tk2.Exec(`select count(*) from t2`) + if err != nil { + hookErr = err + return + } + t2Rows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) + if tRows != t2Rows { + logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) + } + + require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) + // Now using current schema version + tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) + prevTbl = currTbl + logutil.BgLogger().Info("State after ins/upd/del", zap.Int("transitions", transitions), + zap.Int("rows", len(pkMap)), zap.Stringer("SchemaState", job.SchemaState)) + } + } + tk.MustExec(alterStr) + require.NoError(t, hookErr) + tk.MustExec(`admin check table t`) + tk.MustExec(`admin check table t2`) + tk.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) + tk.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) + tk.MustQuery(`select * from t except select * from t2 LIMIT 1`).Check(testkit.Rows()) + tk.MustQuery(`select * from t2 except select * from t LIMIT 1`).Check(testkit.Rows()) +} + +// Emojis fold to a single rune, and ö compares as o, so just complicated having other runes. +// Enough to just distribute between A and Z + testing simple folding +var runes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + +func randStr(n int, r *rand.Rand) string { + var sb strings.Builder + sb.Grow(n) + for i := 0; i < n; i++ { + _, _ = sb.WriteRune(runes[r.Intn(len(runes))]) + } + return sb.String() +} diff --git a/pkg/table/tables/testutil.go b/pkg/table/tables/testutil.go new file mode 100644 index 0000000000000..bc4cf97786c48 --- /dev/null +++ b/pkg/table/tables/testutil.go @@ -0,0 +1,36 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tables + +import "github.com/pingcap/tidb/pkg/table" + +// SwapReorgPartFields swaps the reorganizePartitions field of two partitioned tables. used in tests. +func SwapReorgPartFields(src, dst table.Table) bool { + s, ok := src.(*partitionedTable) + if !ok { + return false + } + d, ok := dst.(*partitionedTable) + if !ok { + return false + } + d.reorganizePartitions, s.reorganizePartitions = s.reorganizePartitions, d.reorganizePartitions + d.meta, s.meta = s.meta, d.meta + d.partitions, s.partitions = s.partitions, d.partitions + d.doubleWritePartitions, s.doubleWritePartitions = s.doubleWritePartitions, d.doubleWritePartitions + d.reorgPartitionExpr, s.reorgPartitionExpr = s.reorgPartitionExpr, d.reorgPartitionExpr + d.partitionExpr, s.partitionExpr = s.partitionExpr, d.partitionExpr + return true +} diff --git a/pkg/table/temptable/BUILD.bazel b/pkg/table/temptable/BUILD.bazel new file mode 100644 index 0000000000000..ef4b9bbcb1e54 --- /dev/null +++ b/pkg/table/temptable/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "temptable", + srcs = [ + "ddl.go", + "infoschema.go", + "interceptor.go", + ], + importpath = "github.com/pingcap/tidb/pkg/table/temptable", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta", + "//pkg/meta/autoid", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/store/driver/txn", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//tikv", + "@org_golang_x_exp//maps", + ], +) + +go_test( + name = "temptable_test", + timeout = "short", + srcs = [ + "ddl_test.go", + "interceptor_test.go", + "main_test.go", + ], + embed = [":temptable"], + flaky = True, + shard_count = 16, + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/store/driver/txn", + "//pkg/store/mockstore", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/table/temptable/ddl.go b/pkg/table/temptable/ddl.go new file mode 100644 index 0000000000000..34c89149a99f5 --- /dev/null +++ b/pkg/table/temptable/ddl.go @@ -0,0 +1,193 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package temptable + +import ( + "bytes" + "context" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/tikv/client-go/v2/tikv" +) + +// TemporaryTableDDL is an interface providing ddl operations for temporary table +type TemporaryTableDDL interface { + CreateLocalTemporaryTable(db *model.DBInfo, info *model.TableInfo) error + DropLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error + TruncateLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error +} + +// temporaryTableDDL implements temptable.TemporaryTableDDL +type temporaryTableDDL struct { + sctx sessionctx.Context +} + +func (d *temporaryTableDDL) CreateLocalTemporaryTable(db *model.DBInfo, info *model.TableInfo) error { + if _, err := ensureSessionData(d.sctx); err != nil { + return err + } + + tbl, err := newTemporaryTableFromTableInfo(d.sctx, info) + if err != nil { + return err + } + + return ensureLocalTemporaryTables(d.sctx).AddTable(db, tbl) +} + +func (d *temporaryTableDDL) DropLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error { + tbl, err := checkLocalTemporaryExistsAndReturn(d.sctx, schema, tblName) + if err != nil { + return err + } + + getLocalTemporaryTables(d.sctx).RemoveTable(schema, tblName) + return d.clearTemporaryTableRecords(tbl.Meta().ID) +} + +func (d *temporaryTableDDL) TruncateLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error { + oldTbl, err := checkLocalTemporaryExistsAndReturn(d.sctx, schema, tblName) + if err != nil { + return err + } + + oldTblInfo := oldTbl.Meta() + newTbl, err := newTemporaryTableFromTableInfo(d.sctx, oldTblInfo.Clone()) + if err != nil { + return err + } + + localTempTables := getLocalTemporaryTables(d.sctx) + db, _ := localTempTables.SchemaByTable(oldTblInfo) + localTempTables.RemoveTable(schema, tblName) + if err = localTempTables.AddTable(db, newTbl); err != nil { + return err + } + + return d.clearTemporaryTableRecords(oldTblInfo.ID) +} + +func (d *temporaryTableDDL) clearTemporaryTableRecords(tblID int64) error { + sessionData := getSessionData(d.sctx) + if sessionData == nil { + return nil + } + + tblPrefix := tablecodec.EncodeTablePrefix(tblID) + endKey := tablecodec.EncodeTablePrefix(tblID + 1) + iter, err := sessionData.Iter(tblPrefix, endKey) + if err != nil { + return err + } + + for iter.Valid() { + key := iter.Key() + if !bytes.HasPrefix(key, tblPrefix) { + break + } + + err = sessionData.DeleteTableKey(tblID, key) + if err != nil { + return err + } + + err = iter.Next() + if err != nil { + return err + } + } + + return nil +} + +func checkLocalTemporaryExistsAndReturn(sctx sessionctx.Context, schema model.CIStr, tblName model.CIStr) (table.Table, error) { + ident := ast.Ident{Schema: schema, Name: tblName} + localTemporaryTables := getLocalTemporaryTables(sctx) + if localTemporaryTables == nil { + return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(ident.String()) + } + + tbl, exist := localTemporaryTables.TableByName(schema, tblName) + if !exist { + return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(ident.String()) + } + + return tbl, nil +} + +func getSessionData(sctx sessionctx.Context) variable.TemporaryTableData { + return sctx.GetSessionVars().TemporaryTableData +} + +func ensureSessionData(sctx sessionctx.Context) (variable.TemporaryTableData, error) { + sessVars := sctx.GetSessionVars() + if sessVars.TemporaryTableData == nil { + // Create this txn just for getting a MemBuffer. It's a little tricky + bufferTxn, err := sctx.GetStore().Begin(tikv.WithStartTS(0)) + if err != nil { + return nil, err + } + + sessVars.TemporaryTableData = variable.NewTemporaryTableData(bufferTxn.GetMemBuffer()) + } + + return sessVars.TemporaryTableData, nil +} + +func newTemporaryTableFromTableInfo(sctx sessionctx.Context, tbInfo *model.TableInfo) (table.Table, error) { + // Local temporary table uses a real table ID. + // We could mock a table ID, but the mocked ID might be identical to an existing + // real table, and then we'll get into trouble. + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnCacheTable) + err := kv.RunInNewTxn(ctx, sctx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { + m := meta.NewMeta(txn) + tblID, err := m.GenGlobalID() + if err != nil { + return errors.Trace(err) + } + tbInfo.ID = tblID + tbInfo.State = model.StatePublic + return nil + }) + if err != nil { + return nil, err + } + + // AutoID is allocated in mocked.. + alloc := autoid.NewAllocatorFromTempTblInfo(tbInfo) + allocs := make([]autoid.Allocator, 0, 1) + if alloc != nil { + allocs = append(allocs, alloc) + } + return tables.TableFromMeta(autoid.NewAllocators(false, allocs...), tbInfo) +} + +// GetTemporaryTableDDL gets the temptable.TemporaryTableDDL from session context +func GetTemporaryTableDDL(sctx sessionctx.Context) TemporaryTableDDL { + return &temporaryTableDDL{ + sctx: sctx, + } +} diff --git a/pkg/table/temptable/ddl_test.go b/pkg/table/temptable/ddl_test.go new file mode 100644 index 0000000000000..40c56218d77ec --- /dev/null +++ b/pkg/table/temptable/ddl_test.go @@ -0,0 +1,226 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package temptable + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func createTestSuite(t *testing.T) (sessionctx.Context, *temporaryTableDDL) { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + + sctx := mock.NewContext() + sctx.Store = store + ddl := GetTemporaryTableDDL(sctx).(*temporaryTableDDL) + t.Cleanup(func() { + require.NoError(t, store.Close()) + }) + + return sctx, ddl +} + +func TestAddLocalTemporaryTable(t *testing.T) { + sctx, ddl := createTestSuite(t) + + sessVars := sctx.GetSessionVars() + + db1 := newMockSchema("db1") + db2 := newMockSchema("db2") + tbl1 := newMockTable("t1") + tbl2 := newMockTable("t2") + + require.Nil(t, sessVars.LocalTemporaryTables) + require.Nil(t, sessVars.TemporaryTableData) + + // insert t1 + err := ddl.CreateLocalTemporaryTable(db1, tbl1) + require.NoError(t, err) + require.NotNil(t, sessVars.LocalTemporaryTables) + require.NotNil(t, sessVars.TemporaryTableData) + require.Equal(t, int64(1), tbl1.ID) + got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1) + + // insert t2 with data + err = ddl.CreateLocalTemporaryTable(db1, tbl2) + require.NoError(t, err) + require.Equal(t, int64(2), tbl2.ID) + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t2")) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl2) + + // should success to set a key for a table + k := tablecodec.EncodeRowKeyWithHandle(tbl1.ID, kv.IntHandle(1)) + err = sessVars.TemporaryTableData.SetTableKey(tbl1.ID, k, []byte("v1")) + require.NoError(t, err) + + val, err := sessVars.TemporaryTableData.Get(context.Background(), k) + require.NoError(t, err) + require.Equal(t, []byte("v1"), val) + + // insert dup table + tbl1x := newMockTable("t1") + err = ddl.CreateLocalTemporaryTable(db1, tbl1x) + require.True(t, infoschema.ErrTableExists.Equal(err)) + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1) + + // insert should be success for same table name in different db + err = ddl.CreateLocalTemporaryTable(db2, tbl1x) + require.NoError(t, err) + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db2"), model.NewCIStr("t1")) + require.Equal(t, int64(4), got.Meta().ID) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1x) + + // tbl1 still exist + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1) +} + +func TestRemoveLocalTemporaryTable(t *testing.T) { + sctx, ddl := createTestSuite(t) + + sessVars := sctx.GetSessionVars() + db1 := newMockSchema("db1") + + // remove when empty + err := ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + + // add one table + tbl1 := newMockTable("t1") + err = ddl.CreateLocalTemporaryTable(db1, tbl1) + require.NoError(t, err) + require.Equal(t, int64(1), tbl1.ID) + k := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) + err = sessVars.TemporaryTableData.SetTableKey(tbl1.ID, k, []byte("v1")) + require.NoError(t, err) + + // remove failed when table not found + err = ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t2")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + + // remove failed when table not found (same table name in different db) + err = ddl.DropLocalTemporaryTable(model.NewCIStr("db2"), model.NewCIStr("t1")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + + // check failed remove should have no effects + got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByID(tbl1.ID) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1) + val, err := sessVars.TemporaryTableData.Get(context.Background(), k) + require.NoError(t, err) + require.Equal(t, []byte("v1"), val) + + // remove success + err = ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.NoError(t, err) + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.Nil(t, got) + require.False(t, exists) + val, err = sessVars.TemporaryTableData.Get(context.Background(), k) + require.NoError(t, err) + require.Equal(t, []byte{}, val) +} + +func TestTruncateLocalTemporaryTable(t *testing.T) { + sctx, ddl := createTestSuite(t) + + sessVars := sctx.GetSessionVars() + db1 := newMockSchema("db1") + + // truncate when empty + err := ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + require.Nil(t, sessVars.LocalTemporaryTables) + require.Nil(t, sessVars.TemporaryTableData) + + // add one table + tbl1 := newMockTable("t1") + err = ddl.CreateLocalTemporaryTable(db1, tbl1) + require.Equal(t, int64(1), tbl1.ID) + require.NoError(t, err) + k := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) + err = sessVars.TemporaryTableData.SetTableKey(1, k, []byte("v1")) + require.NoError(t, err) + + // truncate failed for table not exist + err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t2")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db2"), model.NewCIStr("t1")) + require.True(t, infoschema.ErrTableNotExists.Equal(err)) + + // check failed should have no effects + got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, exists) + require.Equal(t, got.Meta(), tbl1) + val, err := sessVars.TemporaryTableData.Get(context.Background(), k) + require.NoError(t, err) + require.Equal(t, []byte("v1"), val) + + // insert a new tbl + tbl2 := newMockTable("t2") + err = ddl.CreateLocalTemporaryTable(db1, tbl2) + require.Equal(t, int64(2), tbl2.ID) + require.NoError(t, err) + k2 := tablecodec.EncodeRowKeyWithHandle(2, kv.IntHandle(1)) + err = sessVars.TemporaryTableData.SetTableKey(2, k2, []byte("v2")) + require.NoError(t, err) + + // truncate success + err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.NoError(t, err) + got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) + require.True(t, exists) + require.NotEqual(t, got.Meta(), tbl1) + require.Equal(t, int64(3), got.Meta().ID) + val, err = sessVars.TemporaryTableData.Get(context.Background(), k) + require.NoError(t, err) + require.Equal(t, []byte{}, val) + + // truncate just effect its own data + val, err = sessVars.TemporaryTableData.Get(context.Background(), k2) + require.NoError(t, err) + require.Equal(t, []byte("v2"), val) +} + +func newMockTable(tblName string) *model.TableInfo { + c1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("c1"), State: model.StatePublic, Offset: 0, FieldType: *types.NewFieldType(mysql.TypeLonglong)} + c2 := &model.ColumnInfo{ID: 2, Name: model.NewCIStr("c2"), State: model.StatePublic, Offset: 1, FieldType: *types.NewFieldType(mysql.TypeVarchar)} + + tblInfo := &model.TableInfo{Name: model.NewCIStr(tblName), Columns: []*model.ColumnInfo{c1, c2}, PKIsHandle: true} + return tblInfo +} + +func newMockSchema(schemaName string) *model.DBInfo { + return &model.DBInfo{ID: 10, Name: model.NewCIStr(schemaName), State: model.StatePublic} +} diff --git a/pkg/table/temptable/infoschema.go b/pkg/table/temptable/infoschema.go new file mode 100644 index 0000000000000..c42e2d0854f04 --- /dev/null +++ b/pkg/table/temptable/infoschema.go @@ -0,0 +1,68 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package temptable + +import ( + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/sessionctx" +) + +// AttachLocalTemporaryTableInfoSchema attach local temporary table information schema to is +func AttachLocalTemporaryTableInfoSchema(sctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { + localTemporaryTables := getLocalTemporaryTables(sctx) + if localTemporaryTables == nil { + return is + } + if se, ok := is.(*infoschema.SessionExtendedInfoSchema); ok { + se.LocalTemporaryTablesOnce.Do(func() { + se.LocalTemporaryTables = localTemporaryTables + }) + return is + } + + return &infoschema.SessionExtendedInfoSchema{ + InfoSchema: is, + LocalTemporaryTables: localTemporaryTables, + } +} + +// DetachLocalTemporaryTableInfoSchema detach local temporary table information schema from is +func DetachLocalTemporaryTableInfoSchema(is infoschema.InfoSchema) infoschema.InfoSchema { + if attachedInfoSchema, ok := is.(*infoschema.SessionExtendedInfoSchema); ok { + return attachedInfoSchema.DetachTemporaryTableInfoSchema() + } + + return is +} + +func getLocalTemporaryTables(sctx sessionctx.Context) *infoschema.SessionTables { + localTemporaryTables := sctx.GetSessionVars().LocalTemporaryTables + if localTemporaryTables == nil { + return nil + } + + return localTemporaryTables.(*infoschema.SessionTables) +} + +func ensureLocalTemporaryTables(sctx sessionctx.Context) *infoschema.SessionTables { + sessVars := sctx.GetSessionVars() + if sessVars.LocalTemporaryTables == nil { + localTempTables := infoschema.NewSessionTables() + sessVars.LocalTemporaryTables = localTempTables + return localTempTables + } + + return sessVars.LocalTemporaryTables.(*infoschema.SessionTables) +} diff --git a/table/temptable/interceptor.go b/pkg/table/temptable/interceptor.go similarity index 96% rename from table/temptable/interceptor.go rename to pkg/table/temptable/interceptor.go index 9527163f6b68b..9582b061f2633 100644 --- a/table/temptable/interceptor.go +++ b/pkg/table/temptable/interceptor.go @@ -20,12 +20,12 @@ import ( "math" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/tablecodec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/tablecodec" "golang.org/x/exp/maps" ) diff --git a/table/temptable/interceptor_test.go b/pkg/table/temptable/interceptor_test.go similarity index 99% rename from table/temptable/interceptor_test.go rename to pkg/table/temptable/interceptor_test.go index d50852adb8afe..8f3263f562e6e 100644 --- a/table/temptable/interceptor_test.go +++ b/pkg/table/temptable/interceptor_test.go @@ -21,12 +21,12 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/driver/txn" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/table/temptable/main_test.go b/pkg/table/temptable/main_test.go new file mode 100644 index 0000000000000..f21b39ed361c1 --- /dev/null +++ b/pkg/table/temptable/main_test.go @@ -0,0 +1,258 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package temptable + +import ( + "bytes" + "context" + "fmt" + "slices" + "testing" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} + +type mockedInfoSchema struct { + t *testing.T + infoschema.InfoSchema + tables map[int64]model.TempTableType +} + +func newMockedInfoSchema(t *testing.T) *mockedInfoSchema { + return &mockedInfoSchema{ + t: t, + tables: make(map[int64]model.TempTableType), + } +} + +func (is *mockedInfoSchema) AddTable(tempType model.TempTableType, id ...int64) *mockedInfoSchema { + for _, tblID := range id { + is.tables[tblID] = tempType + } + + return is +} + +func (is *mockedInfoSchema) TableByID(tblID int64) (table.Table, bool) { + tempType, ok := is.tables[tblID] + if !ok { + return nil, false + } + + tblInfo := &model.TableInfo{ + ID: tblID, + Name: model.NewCIStr(fmt.Sprintf("tb%d", tblID)), + Columns: []*model.ColumnInfo{{ + ID: 1, + Name: model.NewCIStr("col1"), + Offset: 0, + FieldType: *types.NewFieldType(mysql.TypeLonglong), + State: model.StatePublic, + }}, + Indices: []*model.IndexInfo{}, + TempTableType: tempType, + State: model.StatePublic, + } + + tbl, err := table.TableFromMeta(autoid.NewAllocators(false), tblInfo) + require.NoError(is.t, err) + + return tbl, true +} + +type mockedSnapshot struct { + *mockedRetriever +} + +func newMockedSnapshot(retriever *mockedRetriever) *mockedSnapshot { + return &mockedSnapshot{mockedRetriever: retriever} +} + +func (s *mockedSnapshot) SetOption(_ int, _ interface{}) { + require.FailNow(s.t, "SetOption not supported") +} + +type methodInvoke struct { + Method string + Args []interface{} + Ret []interface{} +} + +type mockedRetriever struct { + t *testing.T + data []*kv.Entry + dataMap map[string][]byte + invokes []*methodInvoke + + allowInvokes map[string]interface{} + errorMap map[string]error +} + +func newMockedRetriever(t *testing.T) *mockedRetriever { + return &mockedRetriever{t: t} +} + +func (r *mockedRetriever) SetData(data []*kv.Entry) *mockedRetriever { + lessFunc := func(i, j *kv.Entry) int { return bytes.Compare(i.Key, j.Key) } + if !slices.IsSortedFunc(data, lessFunc) { + data = append([]*kv.Entry{}, data...) + slices.SortFunc(data, lessFunc) + } + + r.data = data + r.dataMap = make(map[string][]byte) + for _, item := range r.data { + r.dataMap[string(item.Key)] = item.Value + } + return r +} + +func (r *mockedRetriever) InjectMethodError(method string, err error) *mockedRetriever { + if r.errorMap == nil { + r.errorMap = make(map[string]error) + } + r.errorMap[method] = err + return r +} + +func (r *mockedRetriever) SetAllowedMethod(methods ...string) *mockedRetriever { + r.allowInvokes = make(map[string]interface{}) + for _, m := range methods { + r.allowInvokes[m] = struct{}{} + } + return r +} + +func (r *mockedRetriever) ResetInvokes() { + r.invokes = nil +} + +func (r *mockedRetriever) GetInvokes() []*methodInvoke { + return r.invokes +} + +func (r *mockedRetriever) Get(ctx context.Context, k kv.Key) (val []byte, err error) { + r.checkMethodInvokeAllowed("Get") + if err = r.getMethodErr("Get"); err == nil { + var ok bool + val, ok = r.dataMap[string(k)] + if !ok { + err = kv.ErrNotExist + } + } + r.appendInvoke("Get", []interface{}{ctx, k}, []interface{}{val, err}) + return +} + +func (r *mockedRetriever) BatchGet(ctx context.Context, keys []kv.Key) (data map[string][]byte, err error) { + r.checkMethodInvokeAllowed("BatchGet") + if err = r.getMethodErr("BatchGet"); err == nil { + data = make(map[string][]byte) + for _, k := range keys { + val, ok := r.dataMap[string(k)] + if ok { + data[string(k)] = val + } + } + } + + r.appendInvoke("BatchGet", []interface{}{ctx, keys}, []interface{}{data, err}) + return +} + +func (r *mockedRetriever) checkMethodInvokeAllowed(method string) { + require.NotNil(r.t, r.allowInvokes, fmt.Sprintf("Invoke for '%s' is not allowed, should allow it first", method)) + require.Contains(r.t, r.allowInvokes, method, fmt.Sprintf("Invoke for '%s' is not allowed, should allow it first", method)) +} + +func (r *mockedRetriever) Iter(k kv.Key, upperBound kv.Key) (iter kv.Iterator, err error) { + r.checkMethodInvokeAllowed("Iter") + if err = r.getMethodErr("Iter"); err == nil { + data := make([]*kv.Entry, 0) + for _, item := range r.data { + if bytes.Compare(item.Key, k) >= 0 && (len(upperBound) == 0 || bytes.Compare(item.Key, upperBound) < 0) { + data = append(data, item) + } + } + mockIter := mock.NewMockIterFromRecords(r.t, data, true) + if nextErr := r.getMethodErr("IterNext"); nextErr != nil { + mockIter.InjectNextError(nextErr) + } + iter = mockIter + } + r.appendInvoke("Iter", []interface{}{k, upperBound}, []interface{}{iter, err}) + return +} + +func (r *mockedRetriever) IterReverse(k kv.Key, lowerBound kv.Key) (iter kv.Iterator, err error) { + r.checkMethodInvokeAllowed("IterReverse") + if err = r.getMethodErr("IterReverse"); err == nil { + data := make([]*kv.Entry, 0) + for i := 0; i < len(r.data); i++ { + item := r.data[len(r.data)-i-1] + if (len(k) == 0 || bytes.Compare(item.Key, k) < 0) && (len(lowerBound) == 0 || bytes.Compare(item.Key, lowerBound) >= 0) { + data = append(data, item) + } + } + mockIter := mock.NewMockIterFromRecords(r.t, data, true) + if nextErr := r.getMethodErr("IterReverseNext"); nextErr != nil { + mockIter.InjectNextError(nextErr) + } + iter = mockIter + } + r.appendInvoke("IterReverse", []interface{}{k}, []interface{}{iter, err}) + return +} + +func (r *mockedRetriever) appendInvoke(method string, args []interface{}, ret []interface{}) { + r.invokes = append(r.invokes, &methodInvoke{ + Method: method, + Args: args, + Ret: ret, + }) +} + +func (r *mockedRetriever) getMethodErr(method string) error { + if r.errorMap == nil { + return nil + } + + if err, ok := r.errorMap[method]; ok && err != nil { + return err + } + + return nil +} diff --git a/pkg/tablecodec/BUILD.bazel b/pkg/tablecodec/BUILD.bazel new file mode 100644 index 0000000000000..7b74bb2f173cc --- /dev/null +++ b/pkg/tablecodec/BUILD.bazel @@ -0,0 +1,56 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tablecodec", + srcs = ["tablecodec.go"], + importpath = "github.com/pingcap/tidb/pkg/tablecodec", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/structure", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/rowcodec", + "//pkg/util/stringutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_tikv_client_go_v2//tikv", + ], +) + +go_test( + name = "tablecodec_test", + timeout = "short", + srcs = [ + "bench_test.go", + "main_test.go", + "tablecodec_test.go", + ], + embed = [":tablecodec"], + flaky = True, + shard_count = 23, + deps = [ + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/benchdaily", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/rowcodec", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/tablecodec/bench_test.go b/pkg/tablecodec/bench_test.go new file mode 100644 index 0000000000000..e6118e25bd5c3 --- /dev/null +++ b/pkg/tablecodec/bench_test.go @@ -0,0 +1,68 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package tablecodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/benchdaily" +) + +func BenchmarkEncodeRowKeyWithHandle(b *testing.B) { + for i := 0; i < b.N; i++ { + EncodeRowKeyWithHandle(100, kv.IntHandle(100)) + } +} + +func BenchmarkEncodeEndKey(b *testing.B) { + for i := 0; i < b.N; i++ { + EncodeRowKeyWithHandle(100, kv.IntHandle(100)) + EncodeRowKeyWithHandle(100, kv.IntHandle(101)) + } +} + +// BenchmarkEncodeRowKeyWithPrefixNex tests the performance of encoding row key with prefixNext +// PrefixNext() is slow than using EncodeRowKeyWithHandle. +// BenchmarkEncodeEndKey-4 20000000 97.2 ns/op +// BenchmarkEncodeRowKeyWithPrefixNex-4 10000000 121 ns/op +func BenchmarkEncodeRowKeyWithPrefixNex(b *testing.B) { + for i := 0; i < b.N; i++ { + sk := EncodeRowKeyWithHandle(100, kv.IntHandle(100)) + sk.PrefixNext() + } +} + +func BenchmarkDecodeRowKey(b *testing.B) { + rowKey := EncodeRowKeyWithHandle(100, kv.IntHandle(100)) + for i := 0; i < b.N; i++ { + _, err := DecodeRowKey(rowKey) + if err != nil { + b.Fatal(err) + } + } +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkEncodeRowKeyWithHandle, + BenchmarkEncodeEndKey, + BenchmarkEncodeRowKeyWithPrefixNex, + BenchmarkDecodeRowKey, + BenchmarkHasTablePrefix, + BenchmarkHasTablePrefixBuiltin, + BenchmarkEncodeValue, + ) +} diff --git a/pkg/tablecodec/main_test.go b/pkg/tablecodec/main_test.go new file mode 100644 index 0000000000000..3c1d0cdf8fc5c --- /dev/null +++ b/pkg/tablecodec/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tablecodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/tablecodec/rowindexcodec/BUILD.bazel b/pkg/tablecodec/rowindexcodec/BUILD.bazel new file mode 100644 index 0000000000000..d12980defe5d5 --- /dev/null +++ b/pkg/tablecodec/rowindexcodec/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "rowindexcodec", + srcs = ["rowindexcodec.go"], + importpath = "github.com/pingcap/tidb/pkg/tablecodec/rowindexcodec", + visibility = ["//visibility:public"], +) + +go_test( + name = "rowindexcodec_test", + timeout = "short", + srcs = [ + "main_test.go", + "rowindexcodec_test.go", + ], + embed = [":rowindexcodec"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/tablecodec/rowindexcodec/main_test.go b/pkg/tablecodec/rowindexcodec/main_test.go new file mode 100644 index 0000000000000..e61e69cc14f82 --- /dev/null +++ b/pkg/tablecodec/rowindexcodec/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package rowindexcodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/tablecodec/rowindexcodec/rowindexcodec.go b/pkg/tablecodec/rowindexcodec/rowindexcodec.go similarity index 100% rename from tablecodec/rowindexcodec/rowindexcodec.go rename to pkg/tablecodec/rowindexcodec/rowindexcodec.go diff --git a/tablecodec/rowindexcodec/rowindexcodec_test.go b/pkg/tablecodec/rowindexcodec/rowindexcodec_test.go similarity index 100% rename from tablecodec/rowindexcodec/rowindexcodec_test.go rename to pkg/tablecodec/rowindexcodec/rowindexcodec_test.go diff --git a/tablecodec/tablecodec.go b/pkg/tablecodec/tablecodec.go similarity index 99% rename from tablecodec/tablecodec.go rename to pkg/tablecodec/tablecodec.go index 1f8508a2585f6..8cfd7cfb112ce 100644 --- a/tablecodec/tablecodec.go +++ b/pkg/tablecodec/tablecodec.go @@ -24,20 +24,20 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/structure" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/structure" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/rowcodec" + "github.com/pingcap/tidb/pkg/util/stringutil" "github.com/tikv/client-go/v2/tikv" ) diff --git a/tablecodec/tablecodec_test.go b/pkg/tablecodec/tablecodec_test.go similarity index 97% rename from tablecodec/tablecodec_test.go rename to pkg/tablecodec/tablecodec_test.go index a3769481e7f24..b237d594548de 100644 --- a/tablecodec/tablecodec_test.go +++ b/pkg/tablecodec/tablecodec_test.go @@ -22,14 +22,14 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" ) @@ -396,9 +396,9 @@ func TestCutKey(t *testing.T) { } func TestDecodeBadDecical(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/codec/errorInDecodeDecimal", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/codec/errorInDecodeDecimal", `return(true)`)) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/codec/errorInDecodeDecimal")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/codec/errorInDecodeDecimal")) }() dec := types.NewDecFromStringForTest("0.111") b, err := codec.EncodeDecimal(nil, dec, 0, 0) diff --git a/pkg/telemetry/BUILD.bazel b/pkg/telemetry/BUILD.bazel new file mode 100644 index 0000000000000..abba2a9bb3929 --- /dev/null +++ b/pkg/telemetry/BUILD.bazel @@ -0,0 +1,87 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "telemetry", + srcs = [ + "data.go", + "data_cluster_hardware.go", + "data_cluster_info.go", + "data_feature_usage.go", + "data_slow_query.go", + "data_telemetry_host_extra.go", + "data_window.go", + "id.go", + "status.go", + "telemetry.go", + "ttl.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/telemetry", + visibility = ["//visibility:public"], + deps = [ + "//br/pkg/utils", + "//pkg/config", + "//pkg/domain/infosync", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/sqlexec", + "@com_github_google_uuid//:uuid", + "@com_github_iancoleman_strcase//:strcase", + "@com_github_pingcap_errors//:errors", + "@com_github_prometheus_client_golang//api", + "@com_github_prometheus_client_golang//api/prometheus/v1:prometheus", + "@com_github_prometheus_common//model", + "@com_github_shirou_gopsutil_v3//cpu", + "@com_github_shirou_gopsutil_v3//host", + "@com_github_tikv_client_go_v2//metrics", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "telemetry_test", + timeout = "short", + srcs = [ + "data_cluster_hardware_test.go", + "data_feature_usage_test.go", + "data_window_test.go", + "main_test.go", + "telemetry_test.go", + "util_test.go", + ], + embed = [":telemetry"], + flaky = True, + shard_count = 35, + deps = [ + "//pkg/autoid_service", + "//pkg/config", + "//pkg/ddl", + "//pkg/domain", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/store/mockstore", + "//pkg/store/mockstore/unistore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_jeffail_gabs_v2//:gabs", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/telemetry/cte_test/BUILD.bazel b/pkg/telemetry/cte_test/BUILD.bazel new file mode 100644 index 0000000000000..39590ecbd9dd8 --- /dev/null +++ b/pkg/telemetry/cte_test/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "cte_test_test", + timeout = "short", + srcs = ["cte_test.go"], + flaky = True, + race = "on", + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_tests_v3//integration", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/telemetry/cte_test/cte_test.go b/pkg/telemetry/cte_test/cte_test.go new file mode 100644 index 0000000000000..0f61a3485ac97 --- /dev/null +++ b/pkg/telemetry/cte_test/cte_test.go @@ -0,0 +1,133 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cte_test + +import ( + "runtime" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/stretchr/testify/require" + "go.etcd.io/etcd/tests/v3/integration" + "go.opencensus.io/stats/view" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("syscall.Syscall"), + } + + goleak.VerifyTestMain(m, opts...) +} + +// TestCTEPreviewAndReport requires a separated binary +func TestCTEPreviewAndReport(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") + } + integration.BeforeTestExternal(t) + + s := newSuite(t) + defer s.close() + + // By disableing telemetry by default, the global sysvar **and** config file defaults + // are all set to false, so that enabling telemetry in test become more complex. + // As telemetry is a feature that almost no user will manually enable, I'd remove these + // tests for now. + // They should be uncommented once the default behavious changed back to enabled in the + // future, otherwise they could just be deleted. + /* + config.GetGlobalConfig().EnableTelemetry = true + + tk := testkit.NewTestKit(t, s.store) + tk.MustExec("use test") + tk.MustExec("with cte as (select 1) select * from cte") + tk.MustExec("with recursive cte as (select 1) select * from cte") + tk.MustExec("with recursive cte(n) as (select 1 union select * from cte where n < 5) select * from cte") + tk.MustExec("select 1") + + res, err := telemetry.PreviewUsageData(s.se, s.etcdCluster.RandClient()) + require.NoError(t, err) + + jsonParsed, err := gabs.ParseJSON([]byte(res)) + require.NoError(t, err) + require.Equal(t, 1, int(jsonParsed.Path("featureUsage.cte.nonRecursiveCTEUsed").Data().(float64))) + require.Equal(t, 1, int(jsonParsed.Path("featureUsage.cte.recursiveUsed").Data().(float64))) + require.Equal(t, 4, int(jsonParsed.Path("featureUsage.cte.nonCTEUsed").Data().(float64))) + + err = telemetry.ReportUsageData(s.se, s.etcdCluster.RandClient()) + require.NoError(t, err) + + res, err = telemetry.PreviewUsageData(s.se, s.etcdCluster.RandClient()) + require.NoError(t, err) + + jsonParsed, err = gabs.ParseJSON([]byte(res)) + require.NoError(t, err) + require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.nonRecursiveCTEUsed").Data().(float64))) + require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.recursiveUsed").Data().(float64))) + require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.nonCTEUsed").Data().(float64))) + */ +} + +type testSuite struct { + store kv.Storage + dom *domain.Domain + etcdCluster *integration.ClusterV3 + se session.Session + close func() +} + +func newSuite(t *testing.T) *testSuite { + suite := new(testSuite) + + store, err := mockstore.NewMockStore() + require.NoError(t, err) + suite.store = store + + session.SetSchemaLease(0) + session.DisableStats4Test() + + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + suite.dom = dom + + etcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + suite.etcdCluster = etcdCluster + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + suite.se = se + + suite.close = func() { + suite.se.Close() + suite.etcdCluster.Terminate(t) + suite.dom.Close() + err = suite.store.Close() + require.NoError(t, err) + view.Stop() + } + + return suite +} diff --git a/telemetry/data.go b/pkg/telemetry/data.go similarity index 96% rename from telemetry/data.go rename to pkg/telemetry/data.go index 8871c4f7ef304..f528caaf954f9 100644 --- a/telemetry/data.go +++ b/pkg/telemetry/data.go @@ -18,8 +18,8 @@ import ( "context" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" ) type telemetryData struct { diff --git a/telemetry/data_cluster_hardware.go b/pkg/telemetry/data_cluster_hardware.go similarity index 98% rename from telemetry/data_cluster_hardware.go rename to pkg/telemetry/data_cluster_hardware.go index 664fb4153c15a..3879882bd15f9 100644 --- a/telemetry/data_cluster_hardware.go +++ b/pkg/telemetry/data_cluster_hardware.go @@ -22,8 +22,8 @@ import ( "github.com/iancoleman/strcase" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) var ( diff --git a/telemetry/data_cluster_hardware_test.go b/pkg/telemetry/data_cluster_hardware_test.go similarity index 100% rename from telemetry/data_cluster_hardware_test.go rename to pkg/telemetry/data_cluster_hardware_test.go diff --git a/telemetry/data_cluster_info.go b/pkg/telemetry/data_cluster_info.go similarity index 96% rename from telemetry/data_cluster_info.go rename to pkg/telemetry/data_cluster_info.go index 7cda111e785ca..d97415465b791 100644 --- a/telemetry/data_cluster_info.go +++ b/pkg/telemetry/data_cluster_info.go @@ -18,8 +18,8 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/sqlexec" ) type clusterInfoItem struct { diff --git a/telemetry/data_feature_usage.go b/pkg/telemetry/data_feature_usage.go similarity index 97% rename from telemetry/data_feature_usage.go rename to pkg/telemetry/data_feature_usage.go index f53d373891d22..60dfc005be6ac 100644 --- a/telemetry/data_feature_usage.go +++ b/pkg/telemetry/data_feature_usage.go @@ -20,15 +20,15 @@ import ( "strconv" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - m "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/infoschema" + m "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/metrics" "go.uber.org/zap" ) diff --git a/telemetry/data_feature_usage_test.go b/pkg/telemetry/data_feature_usage_test.go similarity index 98% rename from telemetry/data_feature_usage_test.go rename to pkg/telemetry/data_feature_usage_test.go index b2f7c5a5540e4..8b7c5d1b6b02f 100644 --- a/telemetry/data_feature_usage_test.go +++ b/pkg/telemetry/data_feature_usage_test.go @@ -23,13 +23,13 @@ import ( "time" "github.com/pingcap/failpoint" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/testkit" + _ "github.com/pingcap/tidb/pkg/autoid_service" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -818,9 +818,9 @@ func TestStoreBatchCopr(t *testing.T) { require.Equal(t, diff.BatchedFallbackCount, int64(0)) tk.MustExec("insert into tele_batch_t values(1, 1, 1), (2, 2, 2), (3, 3, 3), (5, 5, 5), (7, 7, 7)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/setRangesPerTask", "return(1)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/setRangesPerTask", "return(1)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/setRangesPerTask")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/setRangesPerTask")) }() tk.MustQuery("select * from tele_batch_t force index(i) where k between 1 and 3 and k % 2 != 0").Sort(). Check(testkit.Rows("1 1 1", "3 3 3")) @@ -833,9 +833,9 @@ func TestStoreBatchCopr(t *testing.T) { require.Equal(t, diff.BatchedCount, int64(1)) require.Equal(t, diff.BatchedFallbackCount, int64(0)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/batchCopRegionError", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/store/copr/batchCopRegionError", "return")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/batchCopRegionError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/copr/batchCopRegionError")) }() tk.MustQuery("select * from tele_batch_t force index(i) where k between 1 and 3 and k % 2 != 0").Sort(). Check(testkit.Rows("1 1 1", "3 3 3")) diff --git a/telemetry/data_slow_query.go b/pkg/telemetry/data_slow_query.go similarity index 97% rename from telemetry/data_slow_query.go rename to pkg/telemetry/data_slow_query.go index b686db8d41105..a7faeb6cedfd4 100644 --- a/telemetry/data_slow_query.go +++ b/pkg/telemetry/data_slow_query.go @@ -23,9 +23,9 @@ import ( "time" pingcapErrors "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/logutil" pmodel "github.com/prometheus/common/model" "go.uber.org/zap" ) diff --git a/telemetry/data_telemetry_host_extra.go b/pkg/telemetry/data_telemetry_host_extra.go similarity index 100% rename from telemetry/data_telemetry_host_extra.go rename to pkg/telemetry/data_telemetry_host_extra.go diff --git a/telemetry/data_window.go b/pkg/telemetry/data_window.go similarity index 99% rename from telemetry/data_window.go rename to pkg/telemetry/data_window.go index 49a4037591b16..8a8be188e55b7 100644 --- a/telemetry/data_window.go +++ b/pkg/telemetry/data_window.go @@ -19,8 +19,8 @@ import ( "sync" "time" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/prometheus/client_golang/api" promv1 "github.com/prometheus/client_golang/api/prometheus/v1" pmodel "github.com/prometheus/common/model" diff --git a/telemetry/data_window_test.go b/pkg/telemetry/data_window_test.go similarity index 92% rename from telemetry/data_window_test.go rename to pkg/telemetry/data_window_test.go index f8e3e1101961f..3d7b884c7f282 100644 --- a/telemetry/data_window_test.go +++ b/pkg/telemetry/data_window_test.go @@ -19,12 +19,12 @@ import ( "testing" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/store/mockstore/unistore" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" ) diff --git a/telemetry/id.go b/pkg/telemetry/id.go similarity index 100% rename from telemetry/id.go rename to pkg/telemetry/id.go diff --git a/pkg/telemetry/main_test.go b/pkg/telemetry/main_test.go new file mode 100644 index 0000000000000..4be768056ca02 --- /dev/null +++ b/pkg/telemetry/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package telemetry + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +var ( + GetTxnUsageInfo = getTxnUsageInfo +) + +func GetFeatureUsage(sctx sessionctx.Context) (*featureUsage, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnTelemetry) + return getFeatureUsage(ctx, sctx) +} + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), + goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/telemetry/status.go b/pkg/telemetry/status.go similarity index 100% rename from telemetry/status.go rename to pkg/telemetry/status.go diff --git a/pkg/telemetry/telemetry.go b/pkg/telemetry/telemetry.go new file mode 100644 index 0000000000000..713ab5ed11306 --- /dev/null +++ b/pkg/telemetry/telemetry.go @@ -0,0 +1,178 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package telemetry + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + clientv3 "go.etcd.io/etcd/client/v3" + "go.uber.org/zap" +) + +const ( + // OwnerKey is the telemetry owner path that is saved to etcd. + OwnerKey = "/tidb/telemetry/owner" + // Prompt is the prompt for telemetry owner manager. + Prompt = "telemetry" + // ReportInterval is the interval of the report. + ReportInterval = 6 * time.Hour +) + +const ( + etcdOpTimeout = 3 * time.Second + uploadTimeout = 60 * time.Second + apiEndpoint = "https://telemetry.pingcap.com/api/v1/tidb/report" +) + +func getTelemetryGlobalVariable(ctx sessionctx.Context) (bool, error) { + val, err := ctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableTelemetry) + return variable.TiDBOptOn(val), err +} + +// IsTelemetryEnabled check whether telemetry enabled. +func IsTelemetryEnabled(ctx sessionctx.Context) (bool, error) { + if !config.GetGlobalConfig().EnableTelemetry { + return false, nil + } + enabled, err := getTelemetryGlobalVariable(ctx) + if err != nil { + return false, errors.Trace(err) + } + return enabled, nil +} + +// PreviewUsageData returns a preview of the usage data that is going to be reported. +func PreviewUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) (string, error) { + if etcdClient == nil { + return "", nil + } + if enabled, err := IsTelemetryEnabled(ctx); err != nil || !enabled { + return "", err + } + + trackingID, err := GetTrackingID(etcdClient) + if err != nil { + return "", errors.Trace(err) + } + + // NOTE: trackingID may be empty. However, as a preview data, it is fine. + data := generateTelemetryData(ctx, trackingID) + + prettyJSON, err := json.MarshalIndent(data, "", " ") + if err != nil { + return "", errors.Trace(err) + } + + return string(prettyJSON), nil +} + +func reportUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) (bool, error) { + if etcdClient == nil { + // silently ignore + return false, nil + } + enabled, err := IsTelemetryEnabled(ctx) + if err != nil { + return false, err + } + if !enabled { + return false, errors.Errorf("telemetry is disabled") + } + + trackingID, err := GetTrackingID(etcdClient) + if err != nil { + return false, errors.Trace(err) + } + if len(trackingID) == 0 { + trackingID, err = ResetTrackingID(etcdClient) + if err != nil { + return false, errors.Trace(err) + } + } + + data := generateTelemetryData(ctx, trackingID) + postReportTelemetryData() + + rawJSON, err := json.Marshal(data) + if err != nil { + return false, errors.Trace(err) + } + + // TODO: We should use the context from domain, so that when request is blocked for a long time it will not + // affect TiDB shutdown. + reqCtx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnTelemetry) + reqCtx, cancel := context.WithTimeout(reqCtx, uploadTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(reqCtx, "POST", apiEndpoint, bytes.NewReader(rawJSON)) + if err != nil { + return false, errors.Trace(err) + } + + req.Header.Add("Content-Type", "application/json") + logutil.BgLogger().Info(fmt.Sprintf("Uploading telemetry data to %s", apiEndpoint)) + resp, err := http.DefaultClient.Do(req) + if err != nil { + return false, errors.Trace(err) + } + err = resp.Body.Close() // We don't even want to know any response body. Just close it. + _ = err + if resp.StatusCode != http.StatusOK { + return false, errors.Errorf("Received non-Ok response when reporting usage data, http code: %d", resp.StatusCode) + } + + return true, nil +} + +// ReportUsageData generates the latest usage data and sends it to PingCAP. Status will be saved to etcd. Status update failures will be returned. +func ReportUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) error { + if etcdClient == nil { + // silently ignore + return nil + } + s := status{ + CheckAt: time.Now().Format(time.RFC3339), + } + reported, err := reportUsageData(ctx, etcdClient) + if err != nil { + s.IsError = true + s.ErrorMessage = err.Error() + } else { + s.IsRequestSent = reported + } + + return updateTelemetryStatus(s, etcdClient) +} + +// InitialRun reports the Telmetry configuration and trigger an initial run +func InitialRun(ctx sessionctx.Context, etcdClient *clientv3.Client) error { + enabled, err := IsTelemetryEnabled(ctx) + if err != nil { + return err + } + logutil.BgLogger().Info("Telemetry configuration", zap.String("endpoint", apiEndpoint), zap.Duration("report_interval", ReportInterval), zap.Bool("enabled", enabled)) + return ReportUsageData(ctx, etcdClient) +} diff --git a/telemetry/telemetry_test.go b/pkg/telemetry/telemetry_test.go similarity index 96% rename from telemetry/telemetry_test.go rename to pkg/telemetry/telemetry_test.go index 13c58bdbd5003..55a1a978ea147 100644 --- a/telemetry/telemetry_test.go +++ b/pkg/telemetry/telemetry_test.go @@ -20,10 +20,10 @@ import ( "testing" "github.com/Jeffail/gabs/v2" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/telemetry" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "go.etcd.io/etcd/tests/v3/integration" ) diff --git a/pkg/telemetry/ttl.go b/pkg/telemetry/ttl.go new file mode 100644 index 0000000000000..91cc1ea32ce73 --- /dev/null +++ b/pkg/telemetry/ttl.go @@ -0,0 +1,215 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package telemetry + +import ( + "context" + "fmt" + "math" + "time" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +const ( + // selectDeletedRowsOneDaySQL selects the deleted rows for each table of last day + selectDeletedRowsOneDaySQL = `SELECT parent_table_id, CAST(SUM(deleted_rows) AS SIGNED) + FROM + mysql.tidb_ttl_job_history + WHERE + status != 'running' + AND create_time >= CURDATE() - INTERVAL 7 DAY + AND finish_time >= CURDATE() - INTERVAL 1 DAY + AND finish_time < CURDATE() + GROUP BY parent_table_id;` + // selectDelaySQL selects the deletion delay in minute for each table at the end of last day + selectDelaySQL = `SELECT + parent_table_id, TIMESTAMPDIFF(MINUTE, MIN(tm), CURDATE()) AS ttl_minutes + FROM + ( + SELECT + table_id, + parent_table_id, + MAX(ttl_expire) AS tm + FROM + mysql.tidb_ttl_job_history + WHERE + create_time > CURDATE() - INTERVAL 7 DAY + AND finish_time < CURDATE() + AND status = 'finished' + AND JSON_VALID(summary_text) + AND summary_text ->> "$.scan_task_err" IS NULL + GROUP BY + table_id, parent_table_id + ) t + GROUP BY parent_table_id;` +) + +type ttlHistItem struct { + // LessThan is not null means it collects the count of items with condition [prevLessThan, LessThan) + // Notice that it's type is an int64 pointer to forbid serializing it when it is not set. + LessThan *int64 `json:"less_than,omitempty"` + // LessThanMax is true means the condition is [prevLessThan, MAX) + LessThanMax bool `json:"less_than_max,omitempty"` + // Count is the count of items that fit the condition + Count int64 `json:"count"` +} + +type ttlUsageCounter struct { + TTLJobEnabled bool `json:"ttl_job_enabled"` + TTLTables int64 `json:"ttl_table_count"` + TTLJobEnabledTables int64 `json:"ttl_job_enabled_tables"` + TTLHistDate string `json:"ttl_hist_date"` + TableHistWithDeleteRows []*ttlHistItem `json:"table_hist_with_delete_rows"` + TableHistWithDelayTime []*ttlHistItem `json:"table_hist_with_delay_time"` +} + +func int64Pointer(val int64) *int64 { + v := val + return &v +} + +func (c *ttlUsageCounter) UpdateTableHistWithDeleteRows(rows int64) { + for _, item := range c.TableHistWithDeleteRows { + if item.LessThanMax || rows < *item.LessThan { + item.Count++ + return + } + } +} + +func (c *ttlUsageCounter) UpdateTableHistWithDelayTime(tblCnt int, hours int64) { + for _, item := range c.TableHistWithDelayTime { + if item.LessThanMax || hours < *item.LessThan { + item.Count += int64(tblCnt) + return + } + } +} + +func getTTLUsageInfo(ctx context.Context, sctx sessionctx.Context) (counter *ttlUsageCounter) { + counter = &ttlUsageCounter{ + TTLJobEnabled: variable.EnableTTLJob.Load(), + TTLHistDate: time.Now().Add(-24 * time.Hour).Format(time.DateOnly), + TableHistWithDeleteRows: []*ttlHistItem{ + { + LessThan: int64Pointer(10 * 1000), + }, + { + LessThan: int64Pointer(100 * 1000), + }, + { + LessThan: int64Pointer(1000 * 1000), + }, + { + LessThan: int64Pointer(10000 * 1000), + }, + { + LessThanMax: true, + }, + }, + TableHistWithDelayTime: []*ttlHistItem{ + { + LessThan: int64Pointer(1), + }, + { + LessThan: int64Pointer(6), + }, + { + LessThan: int64Pointer(24), + }, + { + LessThan: int64Pointer(72), + }, + { + LessThanMax: true, + }, + }, + } + + is, ok := sctx.GetDomainInfoSchema().(infoschema.InfoSchema) + if !ok { + // it should never happen + logutil.BgLogger().Error(fmt.Sprintf("GetDomainInfoSchema returns a invalid type: %T", is)) + return + } + + ttlTables := make(map[int64]*model.TableInfo) + for _, db := range is.AllSchemas() { + for _, tbl := range is.SchemaTables(db.Name) { + tblInfo := tbl.Meta() + if tblInfo.State != model.StatePublic || tblInfo.TTLInfo == nil { + continue + } + + counter.TTLTables++ + if tblInfo.TTLInfo.Enable { + counter.TTLJobEnabledTables++ + } + ttlTables[tblInfo.ID] = tblInfo + } + } + + exec := sctx.(sqlexec.RestrictedSQLExecutor) + rows, _, err := exec.ExecRestrictedSQL(ctx, nil, selectDeletedRowsOneDaySQL) + if err != nil { + logutil.BgLogger().Error("exec sql error", zap.String("SQL", selectDeletedRowsOneDaySQL), zap.Error(err)) + } else { + for _, row := range rows { + counter.UpdateTableHistWithDeleteRows(row.GetInt64(1)) + } + } + + rows, _, err = exec.ExecRestrictedSQL(ctx, nil, selectDelaySQL) + if err != nil { + logutil.BgLogger().Error("exec sql error", zap.String("SQL", selectDelaySQL), zap.Error(err)) + } else { + noHistoryTables := len(ttlTables) + for _, row := range rows { + tblID := row.GetInt64(0) + tbl, ok := ttlTables[tblID] + if !ok { + // table not exist, maybe truncated or deleted + continue + } + noHistoryTables-- + + evalIntervalSQL := fmt.Sprintf( + "SELECT TIMESTAMPDIFF(HOUR, CURDATE() - INTERVAL %d MINUTE, CURDATE() - INTERVAL %s %s)", + row.GetInt64(1), tbl.TTLInfo.IntervalExprStr, ast.TimeUnitType(tbl.TTLInfo.IntervalTimeUnit).String(), + ) + + innerRows, _, err := exec.ExecRestrictedSQL(ctx, nil, evalIntervalSQL) + if err != nil || len(innerRows) == 0 { + logutil.BgLogger().Error("exec sql error or empty rows returned", zap.String("SQL", evalIntervalSQL), zap.Error(err)) + continue + } + + hours := innerRows[0].GetInt64(0) + counter.UpdateTableHistWithDelayTime(1, hours) + } + + // When no history found for a table, use max delay + counter.UpdateTableHistWithDelayTime(noHistoryTables, math.MaxInt64) + } + return +} diff --git a/telemetry/util.go b/pkg/telemetry/util.go similarity index 100% rename from telemetry/util.go rename to pkg/telemetry/util.go diff --git a/telemetry/util_test.go b/pkg/telemetry/util_test.go similarity index 100% rename from telemetry/util_test.go rename to pkg/telemetry/util_test.go diff --git a/pkg/testkit/BUILD.bazel b/pkg/testkit/BUILD.bazel new file mode 100644 index 0000000000000..b9922d4065b45 --- /dev/null +++ b/pkg/testkit/BUILD.bazel @@ -0,0 +1,63 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "testkit", + srcs = [ + "asynctestkit.go", + "dbtestkit.go", + "mocksessionmanager.go", + "mockstore.go", + "result.go", + "stepped.go", + "testkit.go", + ], + importpath = "github.com/pingcap/tidb/pkg/testkit", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/schematracker", + "//pkg/domain", + "//pkg/domain/infosync", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/resourcemanager", + "//pkg/session", + "//pkg/session/txninfo", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/store/driver", + "//pkg/store/mockstore", + "//pkg/testkit/testenv", + "//pkg/types", + "//pkg/util", + "//pkg/util/breakpoint", + "//pkg/util/chunk", + "//pkg/util/gctuner", + "//pkg/util/intest", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@io_opencensus_go//stats/view", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "testkit_test", + timeout = "short", + srcs = ["testkit_test.go"], + embed = [":testkit"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/testkit/asynctestkit.go b/pkg/testkit/asynctestkit.go similarity index 97% rename from testkit/asynctestkit.go rename to pkg/testkit/asynctestkit.go index a875088c82abf..2867e4f256eb1 100644 --- a/testkit/asynctestkit.go +++ b/pkg/testkit/asynctestkit.go @@ -23,10 +23,10 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" diff --git a/testkit/dbtestkit.go b/pkg/testkit/dbtestkit.go similarity index 100% rename from testkit/dbtestkit.go rename to pkg/testkit/dbtestkit.go diff --git a/pkg/testkit/ddlhelper/BUILD.bazel b/pkg/testkit/ddlhelper/BUILD.bazel new file mode 100644 index 0000000000000..50ccd41d37437 --- /dev/null +++ b/pkg/testkit/ddlhelper/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "ddlhelper", + srcs = ["helper.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/ddlhelper", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/parser/ast", + "//pkg/parser/model", + ], +) diff --git a/pkg/testkit/ddlhelper/helper.go b/pkg/testkit/ddlhelper/helper.go new file mode 100644 index 0000000000000..fcbfb8ad442cb --- /dev/null +++ b/pkg/testkit/ddlhelper/helper.go @@ -0,0 +1,27 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ddlhelper + +import ( + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" +) + +// BuildTableInfoFromAST builds model.TableInfo from a SQL statement. +// Note: TableID and PartitionID are left as uninitialized value. +func BuildTableInfoFromAST(s *ast.CreateTableStmt) (*model.TableInfo, error) { + return ddl.BuildTableInfoFromAST(s) +} diff --git a/pkg/testkit/external/BUILD.bazel b/pkg/testkit/external/BUILD.bazel new file mode 100644 index 0000000000000..29a20f24882f8 --- /dev/null +++ b/pkg/testkit/external/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "external", + srcs = ["util.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/external", + visibility = ["//visibility:public"], + deps = [ + "//pkg/domain", + "//pkg/parser/model", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "@com_github_stretchr_testify//require", + ], +) diff --git a/pkg/testkit/external/util.go b/pkg/testkit/external/util.go new file mode 100644 index 0000000000000..ac14c83448eb2 --- /dev/null +++ b/pkg/testkit/external/util.go @@ -0,0 +1,72 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package external + +import ( + "fmt" + "strings" + "testing" + + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +// GetTableByName gets table by name for test. +func GetTableByName(t *testing.T, tk *testkit.TestKit, db, table string) table.Table { + dom := domain.GetDomain(tk.Session()) + // Make sure the table schema is the new schema. + require.NoError(t, dom.Reload()) + tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(db), model.NewCIStr(table)) + require.NoError(t, err) + return tbl +} + +// GetModifyColumn is used to get the changed column name after ALTER TABLE. +func GetModifyColumn(t *testing.T, tk *testkit.TestKit, db, tbl, colName string, allColumn bool) *table.Column { + tt := GetTableByName(t, tk, db, tbl) + colName = strings.ToLower(colName) + var cols []*table.Column + if allColumn { + cols = tt.(*tables.TableCommon).Columns + } else { + cols = tt.Cols() + } + for _, col := range cols { + if col.Name.L == colName { + return col + } + } + return nil +} + +// GetIndexID is used to get the index ID from full qualified name. +func GetIndexID(t *testing.T, tk *testkit.TestKit, dbName, tblName, idxName string) int64 { + is := domain.GetDomain(tk.Session()).InfoSchema() + tt, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) + require.NoError(t, err) + + for _, idx := range tt.Indices() { + if idx.Meta().Name.L == idxName { + return idx.Meta().ID + } + } + + require.FailNow(t, fmt.Sprintf("index %s not found(db: %s, tbl: %s)", idxName, dbName, tblName)) + return -1 +} diff --git a/testkit/mocksessionmanager.go b/pkg/testkit/mocksessionmanager.go similarity index 94% rename from testkit/mocksessionmanager.go rename to pkg/testkit/mocksessionmanager.go index 7a96588b2a479..af1aa1022660a 100644 --- a/testkit/mocksessionmanager.go +++ b/pkg/testkit/mocksessionmanager.go @@ -18,14 +18,14 @@ import ( "crypto/tls" "sync" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util" ) // MockSessionManager is a mocked session manager which is used for test. diff --git a/pkg/testkit/mockstore.go b/pkg/testkit/mockstore.go new file mode 100644 index 0000000000000..b5cdb8726d7c7 --- /dev/null +++ b/pkg/testkit/mockstore.go @@ -0,0 +1,256 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +//go:build !codes + +package testkit + +import ( + "flag" + "sync" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl/schematracker" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/resourcemanager" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/store/mockstore" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/gctuner" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/stretchr/testify/require" + "go.opencensus.io/stats/view" + "go.uber.org/zap" +) + +// WithTiKV flag is only used for debugging locally with real tikv cluster. +var WithTiKV = flag.String("with-tikv", "", "address of tikv cluster, if set, running test with real tikv cluster") + +// CreateMockStore return a new mock kv.Storage. +func CreateMockStore(t testing.TB, opts ...mockstore.MockTiKVStoreOption) kv.Storage { + if *WithTiKV != "" { + var d driver.TiKVDriver + var err error + store, err := d.Open("tikv://" + *WithTiKV) + require.NoError(t, err) + + var dom *domain.Domain + dom, err = session.BootstrapSession(store) + t.Cleanup(func() { + dom.Close() + err := store.Close() + require.NoError(t, err) + view.Stop() + }) + require.NoError(t, err) + return store + } + t.Cleanup(func() { + view.Stop() + }) + gctuner.GlobalMemoryLimitTuner.Stop() + store, _ := CreateMockStoreAndDomain(t, opts...) + return store +} + +// DistExecutionContext is the context +// that used in Distributed execution test for Dist task framework and DDL. +type DistExecutionContext struct { + Store kv.Storage + domains []*domain.Domain + deletedDomains []*domain.Domain + t testing.TB + mu sync.Mutex +} + +// InitOwner select the last domain as DDL owner. +func (d *DistExecutionContext) InitOwner() { + d.mu.Lock() + defer d.mu.Unlock() + for _, dom := range d.domains { + dom.DDL().OwnerManager().RetireOwner() + } + err := d.domains[len(d.domains)-1].DDL().OwnerManager().CampaignOwner() + require.NoError(d.t, err) +} + +// SetOwner set one mock domain to DDL Owner by idx. +func (d *DistExecutionContext) SetOwner(idx int) { + d.mu.Lock() + defer d.mu.Unlock() + if idx >= len(d.domains) || idx < 0 { + require.NoError(d.t, errors.New("server idx out of bound")) + return + } + for _, dom := range d.domains { + dom.DDL().OwnerManager().RetireOwner() + } + err := d.domains[idx].DDL().OwnerManager().CampaignOwner() + require.NoError(d.t, err) +} + +// AddDomain add 1 domain which is not ddl owner. +func (d *DistExecutionContext) AddDomain() { + d.mu.Lock() + defer d.mu.Unlock() + dom := bootstrap4DistExecution(d.t, d.Store, 500*time.Millisecond) + dom.InfoSyncer().SetSessionManager(d.domains[0].InfoSyncer().GetSessionManager()) + dom.DDL().OwnerManager().RetireOwner() + d.domains = append(d.domains, dom) +} + +// DeleteDomain delete 1 domain by idx, set server0 as ddl owner if the deleted owner is ddl owner. +func (d *DistExecutionContext) DeleteDomain(idx int) { + d.mu.Lock() + defer d.mu.Unlock() + if idx >= len(d.domains) || idx < 0 { + require.NoError(d.t, errors.New("server idx out of bound")) + return + } + if len(d.domains) == 1 { + require.NoError(d.t, errors.New("can't delete server, since server num = 1")) + return + } + if d.domains[idx].DDL().OwnerManager().IsOwner() { + d.mu.Unlock() + d.SetOwner(0) + d.mu.Lock() + } + + d.deletedDomains = append(d.deletedDomains, d.domains[idx]) + d.domains = append(d.domains[:idx], d.domains[idx+1:]...) + + err := infosync.MockGlobalServerInfoManagerEntry.Delete(idx) + require.NoError(d.t, err) +} + +// Close cleanup running goroutines, release resources used. +func (d *DistExecutionContext) Close() { + d.t.Cleanup(func() { + d.mu.Lock() + defer d.mu.Unlock() + gctuner.GlobalMemoryLimitTuner.Stop() + + var wg tidbutil.WaitGroupWrapper + for _, dom := range d.deletedDomains { + wg.Run(dom.Close) + } + + for _, dom := range d.domains { + wg.Run(dom.Close) + } + + wg.Wait() + err := d.Store.Close() + require.NoError(d.t, err) + }) +} + +// GetDomain get domain by index. +func (d *DistExecutionContext) GetDomain(idx int) *domain.Domain { + return d.domains[idx] +} + +// NewDistExecutionContext create DistExecutionContext for testing. +func NewDistExecutionContext(t testing.TB, serverNum int) *DistExecutionContext { + store, err := mockstore.NewMockStore() + require.NoError(t, err) + gctuner.GlobalMemoryLimitTuner.Stop() + domains := make([]*domain.Domain, 0, serverNum) + sm := MockSessionManager{} + + var domInfo []string + for i := 0; i < serverNum; i++ { + dom := bootstrap4DistExecution(t, store, 500*time.Millisecond) + if i != serverNum-1 { + dom.SetOnClose(func() { /* don't delete the store in domain map */ }) + } + domains = append(domains, dom) + domains[i].InfoSyncer().SetSessionManager(&sm) + domInfo = append(domInfo, dom.DDL().GetID()) + } + logutil.BgLogger().Info("domain DDL IDs", zap.Strings("IDs", domInfo)) + + res := DistExecutionContext{ + schematracker.UnwrapStorage(store), domains, []*domain.Domain{}, t, sync.Mutex{}} + res.InitOwner() + return &res +} + +// CreateMockStoreAndDomain return a new mock kv.Storage and *domain.Domain. +func CreateMockStoreAndDomain(t testing.TB, opts ...mockstore.MockTiKVStoreOption) (kv.Storage, *domain.Domain) { + store, err := mockstore.NewMockStore(opts...) + require.NoError(t, err) + dom := bootstrap(t, store, 500*time.Millisecond) + sm := MockSessionManager{} + dom.InfoSyncer().SetSessionManager(&sm) + t.Cleanup(func() { + view.Stop() + gctuner.GlobalMemoryLimitTuner.Stop() + }) + return schematracker.UnwrapStorage(store), dom +} + +func bootstrap4DistExecution(t testing.TB, store kv.Storage, lease time.Duration) *domain.Domain { + session.SetSchemaLease(lease) + session.DisableStats4Test() + domain.DisablePlanReplayerBackgroundJob4Test() + domain.DisableDumpHistoricalStats4Test() + dom, err := session.BootstrapSession4DistExecution(store) + require.NoError(t, err) + + dom.SetStatsUpdating(true) + return dom +} + +func bootstrap(t testing.TB, store kv.Storage, lease time.Duration) *domain.Domain { + session.SetSchemaLease(lease) + session.DisableStats4Test() + domain.DisablePlanReplayerBackgroundJob4Test() + domain.DisableDumpHistoricalStats4Test() + dom, err := session.BootstrapSession(store) + require.NoError(t, err) + + dom.SetStatsUpdating(true) + + t.Cleanup(func() { + dom.Close() + view.Stop() + err := store.Close() + require.NoError(t, err) + resourcemanager.InstanceResourceManager.Reset() + }) + return dom +} + +// CreateMockStoreWithSchemaLease return a new mock kv.Storage. +func CreateMockStoreWithSchemaLease(t testing.TB, lease time.Duration, opts ...mockstore.MockTiKVStoreOption) kv.Storage { + store, _ := CreateMockStoreAndDomainWithSchemaLease(t, lease, opts...) + return schematracker.UnwrapStorage(store) +} + +// CreateMockStoreAndDomainWithSchemaLease return a new mock kv.Storage and *domain.Domain. +func CreateMockStoreAndDomainWithSchemaLease(t testing.TB, lease time.Duration, opts ...mockstore.MockTiKVStoreOption) (kv.Storage, *domain.Domain) { + store, err := mockstore.NewMockStore(opts...) + require.NoError(t, err) + dom := bootstrap(t, store, lease) + sm := MockSessionManager{} + dom.InfoSyncer().SetSessionManager(&sm) + return schematracker.UnwrapStorage(store), dom +} diff --git a/testkit/result.go b/pkg/testkit/result.go similarity index 100% rename from testkit/result.go rename to pkg/testkit/result.go diff --git a/testkit/stepped.go b/pkg/testkit/stepped.go similarity index 97% rename from testkit/stepped.go rename to pkg/testkit/stepped.go index fc6e434ef85b9..7e606db17291d 100644 --- a/testkit/stepped.go +++ b/pkg/testkit/stepped.go @@ -20,8 +20,8 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/breakpoint" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/breakpoint" "github.com/stretchr/testify/require" ) @@ -191,7 +191,7 @@ func (tk *SteppedTestKit) steppedCommand(cmd steppedTestKitCommand) *SteppedTest tk.tk.Session().SetValue(breakpoint.NotifyBreakPointFuncKey, ctx.notifyBreakPointAndWait) for _, breakPoint := range tk.breakPoints { - path := "github.com/pingcap/tidb/util/breakpoint/" + breakPoint + path := "github.com/pingcap/tidb/pkg/util/breakpoint/" + breakPoint require.NoError(tk.t, failpoint.Enable(path, "return")) breakPointPaths = append(breakPointPaths, path) } diff --git a/pkg/testkit/testdata/BUILD.bazel b/pkg/testkit/testdata/BUILD.bazel new file mode 100644 index 0000000000000..ff78521882317 --- /dev/null +++ b/pkg/testkit/testdata/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testdata", + srcs = ["testdata.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/testdata", + visibility = ["//visibility:public"], + deps = [ + "//pkg/sessionctx/stmtctx", + "@com_github_stretchr_testify//require", + ], +) diff --git a/testkit/testdata/testdata.go b/pkg/testkit/testdata/testdata.go similarity index 99% rename from testkit/testdata/testdata.go rename to pkg/testkit/testdata/testdata.go index be6149c37f0a0..a982d3d60cc00 100644 --- a/testkit/testdata/testdata.go +++ b/pkg/testkit/testdata/testdata.go @@ -31,7 +31,7 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" "github.com/stretchr/testify/require" ) diff --git a/pkg/testkit/testenv/BUILD.bazel b/pkg/testkit/testenv/BUILD.bazel new file mode 100644 index 0000000000000..970c503ebb732 --- /dev/null +++ b/pkg/testkit/testenv/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testenv", + srcs = ["testenv.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/testenv", + visibility = ["//visibility:public"], + deps = ["//pkg/util/mathutil"], +) diff --git a/testkit/testenv/testenv.go b/pkg/testkit/testenv/testenv.go similarity index 94% rename from testkit/testenv/testenv.go rename to pkg/testkit/testenv/testenv.go index c994a13a54a34..f75668fd739cf 100644 --- a/testkit/testenv/testenv.go +++ b/pkg/testkit/testenv/testenv.go @@ -17,7 +17,7 @@ package testenv import ( "runtime" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // SetGOMAXPROCSForTest sets GOMAXPROCS to 16 if it is greater than 16. diff --git a/pkg/testkit/testfork/BUILD.bazel b/pkg/testkit/testfork/BUILD.bazel new file mode 100644 index 0000000000000..904edd3f64bc5 --- /dev/null +++ b/pkg/testkit/testfork/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "testfork", + srcs = ["fork.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/testfork", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) + +go_test( + name = "testfork_test", + timeout = "short", + srcs = ["fork_test.go"], + embed = [":testfork"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/testkit/testfork/fork.go b/pkg/testkit/testfork/fork.go similarity index 100% rename from testkit/testfork/fork.go rename to pkg/testkit/testfork/fork.go diff --git a/testkit/testfork/fork_test.go b/pkg/testkit/testfork/fork_test.go similarity index 100% rename from testkit/testfork/fork_test.go rename to pkg/testkit/testfork/fork_test.go diff --git a/pkg/testkit/testkit.go b/pkg/testkit/testkit.go new file mode 100644 index 0000000000000..1f5f46a723387 --- /dev/null +++ b/pkg/testkit/testkit.go @@ -0,0 +1,676 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +//go:build !codes + +package testkit + +import ( + "context" + "fmt" + "strings" + "sync" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit/testenv" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tipb/go-binlog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + "github.com/tikv/client-go/v2/tikvrpc" + "go.uber.org/atomic" + "google.golang.org/grpc" +) + +var testKitIDGenerator atomic.Uint64 + +// TestKit is a utility to run sql test. +type TestKit struct { + require *require.Assertions + assert *assert.Assertions + t testing.TB + store kv.Storage + session session.Session + alloc chunk.Allocator +} + +// NewTestKit returns a new *TestKit. +func NewTestKit(t testing.TB, store kv.Storage) *TestKit { + require.True(t, intest.InTest, "you should add --tags=intest when to test, see https://pingcap.github.io/tidb-dev-guide/get-started/setup-an-ide.html for help") + testenv.SetGOMAXPROCSForTest() + tk := &TestKit{ + require: require.New(t), + assert: assert.New(t), + t: t, + store: store, + alloc: chunk.NewAllocator(), + } + tk.RefreshSession() + + dom, _ := session.GetDomain(store) + sm := dom.InfoSyncer().GetSessionManager() + if sm != nil { + mockSm, ok := sm.(*MockSessionManager) + if ok { + mockSm.mu.Lock() + if mockSm.Conn == nil { + mockSm.Conn = make(map[uint64]session.Session) + } + mockSm.Conn[tk.session.GetSessionVars().ConnectionID] = tk.session + mockSm.mu.Unlock() + } + tk.session.SetSessionManager(sm) + } + + return tk +} + +// NewTestKitWithSession returns a new *TestKit. +func NewTestKitWithSession(t testing.TB, store kv.Storage, se session.Session) *TestKit { + return &TestKit{ + require: require.New(t), + assert: assert.New(t), + t: t, + store: store, + session: se, + alloc: chunk.NewAllocator(), + } +} + +// RefreshSession set a new session for the testkit +func (tk *TestKit) RefreshSession() { + tk.session = newSession(tk.t, tk.store) + // enforce sysvar cache loading, ref loadCommonGlobalVariableIfNeeded + tk.MustExec("select 3") +} + +// SetSession set the session of testkit +func (tk *TestKit) SetSession(session session.Session) { + tk.session = session + // enforce sysvar cache loading, ref loadCommonGlobalVariableIfNeeded + tk.MustExec("select 3") +} + +// Session return the session associated with the testkit +func (tk *TestKit) Session() session.Session { + return tk.session +} + +// MustExec executes a sql statement and asserts nil error. +func (tk *TestKit) MustExec(sql string, args ...interface{}) { + defer func() { + if tk.alloc != nil { + tk.alloc.Reset() + } + }() + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + tk.MustExecWithContext(ctx, sql, args...) +} + +// MustExecWithContext executes a sql statement and asserts nil error. +func (tk *TestKit) MustExecWithContext(ctx context.Context, sql string, args ...interface{}) { + res, err := tk.ExecWithContext(ctx, sql, args...) + comment := fmt.Sprintf("sql:%s, %v, error stack %v", sql, args, errors.ErrorStack(err)) + tk.require.NoError(err, comment) + + if res != nil { + tk.require.NoError(res.Close()) + } +} + +// MustQuery query the statements and returns result rows. +// If expected result is set it asserts the query result equals expected result. +func (tk *TestKit) MustQuery(sql string, args ...interface{}) *Result { + defer func() { + if tk.alloc != nil { + tk.alloc.Reset() + } + }() + return tk.MustQueryWithContext(context.Background(), sql, args...) +} + +// EventuallyMustQueryAndCheck query the statements and assert that +// result rows.lt will equal the expected results in waitFor time, periodically checking equality each tick. +// Note: retry can't ignore error of the statements. If statements returns error, it will break out. +func (tk *TestKit) EventuallyMustQueryAndCheck(sql string, args []interface{}, + expected [][]interface{}, waitFor time.Duration, tick time.Duration) { + defer func() { + if tk.alloc != nil { + tk.alloc.Reset() + } + }() + tk.require.Eventually(func() bool { + res := tk.MustQueryWithContext(context.Background(), sql, args...) + return res.Equal(expected) + }, waitFor, tick) +} + +// MustQueryWithContext query the statements and returns result rows. +func (tk *TestKit) MustQueryWithContext(ctx context.Context, sql string, args ...interface{}) *Result { + comment := fmt.Sprintf("sql:%s, args:%v", sql, args) + rs, err := tk.ExecWithContext(ctx, sql, args...) + tk.require.NoError(err, comment) + tk.require.NotNil(rs, comment) + return tk.ResultSetToResultWithCtx(ctx, rs, comment) +} + +// MustIndexLookup checks whether the plan for the sql is IndexLookUp. +func (tk *TestKit) MustIndexLookup(sql string, args ...interface{}) *Result { + tk.MustHavePlan(sql, "IndexLookUp", args...) + return tk.MustQuery(sql, args...) +} + +// MustPartition checks if the result execution plan must read specific partitions. +func (tk *TestKit) MustPartition(sql string, partitions string, args ...interface{}) *Result { + rs := tk.MustQuery("explain "+sql, args...) + ok := len(partitions) == 0 + for i := range rs.rows { + if len(partitions) == 0 && strings.Contains(rs.rows[i][3], "partition:") { + ok = false + } + // The data format is "table: t1, partition: p0,p1,p2" + if len(partitions) != 0 && strings.HasSuffix(rs.rows[i][3], "partition:"+partitions) { + ok = true + } + } + tk.require.True(ok) + return tk.MustQuery(sql, args...) +} + +// MustPartitionByList checks if the result execution plan must read specific partitions by list. +func (tk *TestKit) MustPartitionByList(sql string, partitions []string, args ...interface{}) *Result { + rs := tk.MustQuery("explain "+sql, args...) + ok := len(partitions) == 0 + for i := range rs.rows { + if ok { + tk.require.NotContains(rs.rows[i][3], "partition:") + } + for index, partition := range partitions { + if !ok && strings.Contains(rs.rows[i][3], "partition:"+partition) { + partitions = append(partitions[:index], partitions[index+1:]...) + } + } + } + if !ok { + tk.require.Len(partitions, 0) + } + return tk.MustQuery(sql, args...) +} + +// QueryToErr executes a sql statement and discard results. +func (tk *TestKit) QueryToErr(sql string, args ...interface{}) error { + comment := fmt.Sprintf("sql:%s, args:%v", sql, args) + res, err := tk.Exec(sql, args...) + tk.require.NoError(err, comment) + tk.require.NotNil(res, comment) + _, resErr := session.GetRows4Test(context.Background(), tk.session, res) + tk.require.NoError(res.Close()) + return resErr +} + +// ResultSetToResult converts sqlexec.RecordSet to testkit.Result. +// It is used to check results of execute statement in binary mode. +func (tk *TestKit) ResultSetToResult(rs sqlexec.RecordSet, comment string) *Result { + return tk.ResultSetToResultWithCtx(context.Background(), rs, comment) +} + +// ResultSetToResultWithCtx converts sqlexec.RecordSet to testkit.Result. +func (tk *TestKit) ResultSetToResultWithCtx(ctx context.Context, rs sqlexec.RecordSet, comment string) *Result { + rows, err := session.ResultSetToStringSlice(ctx, tk.session, rs) + tk.require.NoError(err, comment) + return &Result{rows: rows, comment: comment, assert: tk.assert, require: tk.require} +} + +func (tk *TestKit) hasPlan(sql string, plan string, args ...interface{}) (bool, *Result) { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][0], plan) { + return true, rs + } + } + return false, rs +} + +// MustHavePlan checks if the result execution plan contains specific plan. +func (tk *TestKit) MustHavePlan(sql string, plan string, args ...interface{}) { + has, rs := tk.hasPlan(sql, plan, args...) + tk.require.True(has, fmt.Sprintf("%s doesn't have plan %s, full plan %v", sql, plan, rs.Rows())) +} + +// MustNotHavePlan checks if the result execution plan contains specific plan. +func (tk *TestKit) MustNotHavePlan(sql string, plan string, args ...interface{}) { + has, rs := tk.hasPlan(sql, plan, args...) + tk.require.False(has, fmt.Sprintf("%s shouldn't have plan %s, full plan %v", sql, plan, rs.Rows())) +} + +// HasTiFlashPlan checks if the result execution plan contains TiFlash plan. +func (tk *TestKit) HasTiFlashPlan(sql string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][2], "tiflash") { + return true + } + } + return false +} + +// HasPlanForLastExecution checks if the execution plan of the last execution contains specific plan. +func (tk *TestKit) HasPlanForLastExecution(plan string) bool { + connID := tk.session.GetSessionVars().ConnectionID + rs := tk.MustQuery(fmt.Sprintf("explain for connection %d", connID)) + for i := range rs.rows { + if strings.Contains(rs.rows[i][0], plan) { + return true + } + } + return false +} + +// HasKeywordInOperatorInfo checks if the result execution plan contains specific keyword in the operator info. +func (tk *TestKit) HasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][4], keyword) { + return true + } + } + return false +} + +// NotHasKeywordInOperatorInfo checks if the result execution plan doesn't contain specific keyword in the operator info. +func (tk *TestKit) NotHasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][4], keyword) { + return false + } + } + return true +} + +// HasPlan4ExplainFor checks if the result execution plan contains specific plan. +func (tk *TestKit) HasPlan4ExplainFor(result *Result, plan string) bool { + for i := range result.rows { + if strings.Contains(result.rows[i][0], plan) { + return true + } + } + return false +} + +// Exec executes a sql statement using the prepared stmt API +func (tk *TestKit) Exec(sql string, args ...interface{}) (sqlexec.RecordSet, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) + return tk.ExecWithContext(ctx, sql, args...) +} + +// ExecWithContext executes a sql statement using the prepared stmt API +func (tk *TestKit) ExecWithContext(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, err error) { + defer tk.Session().GetSessionVars().ClearAlloc(&tk.alloc, err != nil) + if len(args) == 0 { + sc := tk.session.GetSessionVars().StmtCtx + prevWarns := sc.GetWarnings() + var stmts []ast.StmtNode + if len(stmts) == 0 { + var err error + stmts, err = tk.session.Parse(ctx, sql) + if err != nil { + return nil, errors.Trace(err) + } + } + warns := sc.GetWarnings() + parserWarns := warns[len(prevWarns):] + tk.Session().GetSessionVars().SetAlloc(tk.alloc) + var rs0 sqlexec.RecordSet + for i, stmt := range stmts { + var rs sqlexec.RecordSet + var err error + if s, ok := stmt.(*ast.NonTransactionalDMLStmt); ok { + rs, err = session.HandleNonTransactionalDML(ctx, s, tk.Session()) + } else { + rs, err = tk.Session().ExecuteStmt(ctx, stmt) + } + if i == 0 { + rs0 = rs + if len(stmts) > 1 && rs != nil { + // The result of the first statement will be drained and closed later. To avoid leaking + // resource on the statement context, we'll need to store the result and close the + // resultSet before executing other statements. + rs0 = buildRowsRecordSet(ctx, rs) + // the `rs` can be closed safely now + terror.Call(rs.Close) + } + } else if rs != nil { + // other statements are executed, but the `ResultSet` is not returned, so close them here + terror.Call(rs.Close) + } + if err != nil { + tk.session.GetSessionVars().StmtCtx.AppendError(err) + return rs, errors.Trace(err) + } + } + if len(parserWarns) > 0 { + tk.session.GetSessionVars().StmtCtx.AppendWarnings(parserWarns) + } + return rs0, nil + } + + stmtID, _, _, err := tk.session.PrepareStmt(sql) + if err != nil { + return nil, errors.Trace(err) + } + params := expression.Args2Expressions4Test(args...) + tk.Session().GetSessionVars().SetAlloc(tk.alloc) + rs, err = tk.session.ExecutePreparedStmt(ctx, stmtID, params) + if err != nil { + return rs, errors.Trace(err) + } + err = tk.session.DropPreparedStmt(stmtID) + if err != nil { + return rs, errors.Trace(err) + } + return rs, nil +} + +// ExecToErr executes a sql statement and discard results. +func (tk *TestKit) ExecToErr(sql string, args ...interface{}) error { + res, err := tk.Exec(sql, args...) + if res != nil { + tk.require.NoError(res.Close()) + } + return err +} + +// MustExecToErr executes a sql statement and must return Error. +func (tk *TestKit) MustExecToErr(sql string, args ...interface{}) { + res, err := tk.Exec(sql, args...) + if res != nil { + tk.require.NoError(res.Close()) + } + tk.require.Error(err) +} + +func newSession(t testing.TB, store kv.Storage) session.Session { + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + se.SetConnectionID(testKitIDGenerator.Inc()) + return se +} + +// RefreshConnectionID refresh the connection ID for session of the testkit +func (tk *TestKit) RefreshConnectionID() { + if tk.session != nil { + tk.session.SetConnectionID(testKitIDGenerator.Inc()) + } +} + +// MustGetErrCode executes a sql statement and assert it's error code. +func (tk *TestKit) MustGetErrCode(sql string, errCode int) { + _, err := tk.Exec(sql) + tk.require.Errorf(err, "sql: %s", sql) + originErr := errors.Cause(err) + tErr, ok := originErr.(*terror.Error) + tk.require.Truef(ok, "sql: %s, expect type 'terror.Error', but obtain '%T': %v", sql, originErr, originErr) + sqlErr := terror.ToSQLError(tErr) + tk.require.Equalf(errCode, int(sqlErr.Code), "sql: %s, Assertion failed, origin err:\n %v", sql, sqlErr) +} + +// MustGetErrMsg executes a sql statement and assert its error message. +func (tk *TestKit) MustGetErrMsg(sql string, errStr string) { + err := tk.ExecToErr(sql) + tk.require.EqualError(err, errStr) +} + +// MustGetDBError executes a sql statement and assert its terror. +func (tk *TestKit) MustGetDBError(sql string, dberr *terror.Error) { + err := tk.ExecToErr(sql) + tk.require.Truef(terror.ErrorEqual(err, dberr), "err %v", err) +} + +// MustContainErrMsg executes a sql statement and assert its error message containing errStr. +func (tk *TestKit) MustContainErrMsg(sql string, errStr interface{}) { + err := tk.ExecToErr(sql) + tk.require.Error(err) + tk.require.Contains(err.Error(), errStr) +} + +// MustMatchErrMsg executes a sql statement and assert its error message matching errRx. +func (tk *TestKit) MustMatchErrMsg(sql string, errRx interface{}) { + err := tk.ExecToErr(sql) + tk.require.Error(err) + tk.require.Regexp(errRx, err.Error()) +} + +// MustUseIndex checks if the result execution plan contains specific index(es). +func (tk *TestKit) MustUseIndex(sql string, index string, args ...interface{}) bool { + rs := tk.MustQuery("explain "+sql, args...) + for i := range rs.rows { + if strings.Contains(rs.rows[i][3], "index:"+index) { + return true + } + } + return false +} + +// MustUseIndex4ExplainFor checks if the result execution plan contains specific index(es). +func (tk *TestKit) MustUseIndex4ExplainFor(result *Result, index string) bool { + for i := range result.rows { + // It depends on whether we enable to collect the execution info. + if strings.Contains(result.rows[i][3], "index:"+index) { + return true + } + if strings.Contains(result.rows[i][4], "index:"+index) { + return true + } + } + return false +} + +// CheckExecResult checks the affected rows and the insert id after executing MustExec. +func (tk *TestKit) CheckExecResult(affectedRows, insertID int64) { + tk.require.Equal(int64(tk.Session().AffectedRows()), affectedRows) + tk.require.Equal(int64(tk.Session().LastInsertID()), insertID) +} + +// MustPointGet checks whether the plan for the sql is Point_Get. +func (tk *TestKit) MustPointGet(sql string, args ...interface{}) *Result { + rs := tk.MustQuery("explain "+sql, args...) + tk.require.Len(rs.rows, 1) + tk.require.Contains(rs.rows[0][0], "Point_Get", "plan %v", rs.rows[0][0]) + return tk.MustQuery(sql, args...) +} + +// UsedPartitions returns the partition names that will be used or all/dual. +func (tk *TestKit) UsedPartitions(sql string, args ...interface{}) *Result { + rs := tk.MustQuery("explain "+sql, args...) + var usedPartitions [][]string + for i := range rs.rows { + index := strings.Index(rs.rows[i][3], "partition:") + if index != -1 { + p := rs.rows[i][3][index+len("partition:"):] + partitions := strings.Split(strings.SplitN(p, " ", 2)[0], ",") + usedPartitions = append(usedPartitions, partitions) + } + } + comment := fmt.Sprintf("sql:%s, args:%v", sql, args) + return &Result{rows: usedPartitions, comment: comment, assert: tk.assert, require: tk.require} +} + +// WithPruneMode run test case under prune mode. +func WithPruneMode(tk *TestKit, mode variable.PartitionPruneMode, f func()) { + tk.MustExec("set @@tidb_partition_prune_mode=`" + string(mode) + "`") + tk.MustExec("set global tidb_partition_prune_mode=`" + string(mode) + "`") + f() +} + +func containGlobal(rs *Result) bool { + partitionNameCol := 2 + for i := range rs.rows { + if strings.Contains(rs.rows[i][partitionNameCol], "global") { + return true + } + } + return false +} + +// MustNoGlobalStats checks if there is no global stats. +func (tk *TestKit) MustNoGlobalStats(table string) bool { + if containGlobal(tk.MustQuery("show stats_meta where table_name like '" + table + "'")) { + return false + } + if containGlobal(tk.MustQuery("show stats_buckets where table_name like '" + table + "'")) { + return false + } + if containGlobal(tk.MustQuery("show stats_histograms where table_name like '" + table + "'")) { + return false + } + return true +} + +// CheckLastMessage checks last message after executing MustExec +func (tk *TestKit) CheckLastMessage(msg string) { + tk.require.Equal(tk.Session().LastMessage(), msg) +} + +// RequireEqual checks if actual is equal to the expected +func (tk *TestKit) RequireEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + tk.require.Equal(expected, actual, msgAndArgs...) +} + +// RequireNotEqual checks if actual is not equal to the expected +func (tk *TestKit) RequireNotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + tk.require.NotEqual(expected, actual, msgAndArgs...) +} + +// RequireNoError checks if error happens +func (tk *TestKit) RequireNoError(err error, msgAndArgs ...interface{}) { + tk.require.NoError(err, msgAndArgs) +} + +// RegionProperityClient is to get region properties. +type RegionProperityClient struct { + tikv.Client + mu struct { + sync.Mutex + failedOnce bool + count int64 + } +} + +// SendRequest is to mock send request. +func (c *RegionProperityClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { + if req.Type == tikvrpc.CmdDebugGetRegionProperties { + c.mu.Lock() + defer c.mu.Unlock() + c.mu.count++ + // Mock failure once. + if !c.mu.failedOnce { + c.mu.failedOnce = true + return &tikvrpc.Response{}, nil + } + } + return c.Client.SendRequest(ctx, addr, req, timeout) +} + +// MockPumpClient is a mock pump client. +type MockPumpClient struct{} + +// WriteBinlog is a mock method. +func (m MockPumpClient) WriteBinlog(ctx context.Context, in *binlog.WriteBinlogReq, opts ...grpc.CallOption) (*binlog.WriteBinlogResp, error) { + return &binlog.WriteBinlogResp{}, nil +} + +// PullBinlogs is a mock method. +func (m MockPumpClient) PullBinlogs(ctx context.Context, in *binlog.PullBinlogReq, opts ...grpc.CallOption) (binlog.Pump_PullBinlogsClient, error) { + return nil, nil +} + +var _ sqlexec.RecordSet = &rowsRecordSet{} + +type rowsRecordSet struct { + fields []*ast.ResultField + rows []chunk.Row + + idx int + + // this error is stored here to return in the future + err error +} + +func (r *rowsRecordSet) Fields() []*ast.ResultField { + return r.fields +} + +func (r *rowsRecordSet) Next(ctx context.Context, req *chunk.Chunk) error { + if r.err != nil { + return r.err + } + + req.Reset() + for r.idx < len(r.rows) { + if req.IsFull() { + return nil + } + req.AppendRow(r.rows[r.idx]) + r.idx++ + } + return nil +} + +func (r *rowsRecordSet) NewChunk(alloc chunk.Allocator) *chunk.Chunk { + fields := make([]*types.FieldType, 0, len(r.fields)) + for _, field := range r.fields { + fields = append(fields, &field.Column.FieldType) + } + if alloc != nil { + return alloc.Alloc(fields, 0, 1024) + } + return chunk.New(fields, 1024, 1024) +} + +func (r *rowsRecordSet) Close() error { + // do nothing + return nil +} + +// buildRowsRecordSet builds a `rowsRecordSet` from any `RecordSet` by draining all rows from it. +// It's used to store the result temporarily. After building a new RecordSet, the original rs (in the argument) can be +// closed safely. +func buildRowsRecordSet(ctx context.Context, rs sqlexec.RecordSet) sqlexec.RecordSet { + rows, err := session.GetRows4Test(ctx, nil, rs) + if err != nil { + return &rowsRecordSet{ + fields: rs.Fields(), + err: err, + } + } + + return &rowsRecordSet{ + fields: rs.Fields(), + rows: rows, + idx: 0, + } +} diff --git a/testkit/testkit_test.go b/pkg/testkit/testkit_test.go similarity index 100% rename from testkit/testkit_test.go rename to pkg/testkit/testkit_test.go diff --git a/pkg/testkit/testmain/BUILD.bazel b/pkg/testkit/testmain/BUILD.bazel new file mode 100644 index 0000000000000..5667a25a44d0b --- /dev/null +++ b/pkg/testkit/testmain/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testmain", + srcs = [ + "bench.go", + "wrapper.go", + ], + importpath = "github.com/pingcap/tidb/pkg/testkit/testmain", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_goleak//:goleak"], +) diff --git a/testkit/testmain/bench.go b/pkg/testkit/testmain/bench.go similarity index 100% rename from testkit/testmain/bench.go rename to pkg/testkit/testmain/bench.go diff --git a/testkit/testmain/wrapper.go b/pkg/testkit/testmain/wrapper.go similarity index 100% rename from testkit/testmain/wrapper.go rename to pkg/testkit/testmain/wrapper.go diff --git a/pkg/testkit/testsetup/BUILD.bazel b/pkg/testkit/testsetup/BUILD.bazel new file mode 100644 index 0000000000000..0db510570d0e8 --- /dev/null +++ b/pkg/testkit/testsetup/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testsetup", + srcs = ["bridge.go"], + importpath = "github.com/pingcap/tidb/pkg/testkit/testsetup", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) diff --git a/testkit/testsetup/bridge.go b/pkg/testkit/testsetup/bridge.go similarity index 100% rename from testkit/testsetup/bridge.go rename to pkg/testkit/testsetup/bridge.go diff --git a/pkg/testkit/testutil/BUILD.bazel b/pkg/testkit/testutil/BUILD.bazel new file mode 100644 index 0000000000000..6280c49a42d71 --- /dev/null +++ b/pkg/testkit/testutil/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "testutil", + srcs = [ + "handle.go", + "loghook.go", + "require.go", + ], + importpath = "github.com/pingcap/tidb/pkg/testkit/testutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/logutil", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "testutil_test", + timeout = "short", + srcs = ["require_test.go"], + embed = [":testutil"], + flaky = True, + deps = [ + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/testkit/testutil/handle.go b/pkg/testkit/testutil/handle.go new file mode 100644 index 0000000000000..ac625acffcb2d --- /dev/null +++ b/pkg/testkit/testutil/handle.go @@ -0,0 +1,51 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +//go:build !codes + +package testutil + +import ( + "slices" + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/stretchr/testify/require" +) + +// MustNewCommonHandle create a common handle with given values. +func MustNewCommonHandle(t *testing.T, values ...interface{}) kv.Handle { + encoded, err := codec.EncodeKey(stmtctx.NewStmtCtx(), nil, types.MakeDatums(values...)...) + require.NoError(t, err) + ch, err := kv.NewCommonHandle(encoded) + require.NoError(t, err) + return ch +} + +// MaskSortHandles sorts the handles by lowest (fieldTypeBits - 1 - shardBitsCount) bits. +func MaskSortHandles(handles []int64, shardBitsCount int, fieldType byte) []int64 { + typeBitsLength := mysql.DefaultLengthOfMysqlTypes[fieldType] * 8 + const signBitCount = 1 + shiftBitsCount := 64 - typeBitsLength + shardBitsCount + signBitCount + ordered := make([]int64, len(handles)) + for i, h := range handles { + ordered[i] = h << shiftBitsCount >> shiftBitsCount + } + slices.Sort(ordered) + return ordered +} diff --git a/testkit/testutil/loghook.go b/pkg/testkit/testutil/loghook.go similarity index 98% rename from testkit/testutil/loghook.go rename to pkg/testkit/testutil/loghook.go index ff388d671ca71..8ed5dcf8a5298 100644 --- a/testkit/testutil/loghook.go +++ b/pkg/testkit/testutil/loghook.go @@ -21,7 +21,7 @@ import ( "testing" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/testkit/testutil/require.go b/pkg/testkit/testutil/require.go similarity index 93% rename from testkit/testutil/require.go rename to pkg/testkit/testutil/require.go index e73123b262a40..02acb7d604b55 100644 --- a/testkit/testutil/require.go +++ b/pkg/testkit/testutil/require.go @@ -20,10 +20,10 @@ import ( "math/rand" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/testkit/testutil/require_test.go b/pkg/testkit/testutil/require_test.go similarity index 100% rename from testkit/testutil/require_test.go rename to pkg/testkit/testutil/require_test.go diff --git a/tidb-binlog/driver/README.md b/pkg/tidb-binlog/driver/README.md similarity index 100% rename from tidb-binlog/driver/README.md rename to pkg/tidb-binlog/driver/README.md diff --git a/pkg/tidb-binlog/driver/reader/BUILD.bazel b/pkg/tidb-binlog/driver/reader/BUILD.bazel new file mode 100644 index 0000000000000..fe0db09f3669f --- /dev/null +++ b/pkg/tidb-binlog/driver/reader/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "reader", + srcs = [ + "offset.go", + "reader.go", + ], + importpath = "github.com/pingcap/tidb/pkg/tidb-binlog/driver/reader", + visibility = ["//visibility:public"], + deps = [ + "//pkg/tidb-binlog/proto/go-binlog", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_shopify_sarama//:sarama", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "reader_test", + timeout = "short", + srcs = ["offset_test.go"], + embed = [":reader"], + flaky = True, + deps = [ + "//pkg/tidb-binlog/proto/go-binlog", + "@com_github_shopify_sarama//:sarama", + "@com_github_stretchr_testify//require", + ], +) diff --git a/tidb-binlog/driver/reader/offset.go b/pkg/tidb-binlog/driver/reader/offset.go similarity index 98% rename from tidb-binlog/driver/reader/offset.go rename to pkg/tidb-binlog/driver/reader/offset.go index bc91bb3cfb45e..ca67fa841329c 100644 --- a/tidb-binlog/driver/reader/offset.go +++ b/pkg/tidb-binlog/driver/reader/offset.go @@ -20,7 +20,7 @@ import ( "github.com/Shopify/sarama" "github.com/pingcap/errors" "github.com/pingcap/log" - pb "github.com/pingcap/tidb/tidb-binlog/proto/go-binlog" + pb "github.com/pingcap/tidb/pkg/tidb-binlog/proto/go-binlog" "go.uber.org/zap" ) diff --git a/tidb-binlog/driver/reader/offset_test.go b/pkg/tidb-binlog/driver/reader/offset_test.go similarity index 98% rename from tidb-binlog/driver/reader/offset_test.go rename to pkg/tidb-binlog/driver/reader/offset_test.go index 55808cd550d02..e56616ff2040a 100644 --- a/tidb-binlog/driver/reader/offset_test.go +++ b/pkg/tidb-binlog/driver/reader/offset_test.go @@ -20,7 +20,7 @@ import ( "time" "github.com/Shopify/sarama" - pb "github.com/pingcap/tidb/tidb-binlog/proto/go-binlog" + pb "github.com/pingcap/tidb/pkg/tidb-binlog/proto/go-binlog" "github.com/stretchr/testify/require" ) diff --git a/pkg/tidb-binlog/driver/reader/reader.go b/pkg/tidb-binlog/driver/reader/reader.go new file mode 100644 index 0000000000000..87178a9440ec0 --- /dev/null +++ b/pkg/tidb-binlog/driver/reader/reader.go @@ -0,0 +1,239 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package reader + +import ( + "time" + + "github.com/Shopify/sarama" + "github.com/pingcap/errors" + "github.com/pingcap/log" + pb "github.com/pingcap/tidb/pkg/tidb-binlog/proto/go-binlog" + "go.uber.org/zap" +) + +const ( + // KafkaReadTimeout is the timeout for reading from kafka. + KafkaReadTimeout = 10 * time.Minute + // KafkaWaitTimeout is the timeout for waiting for kafka. + KafkaWaitTimeout = 11 * time.Minute +) + +func init() { + // log.SetLevel(log.LOG_LEVEL_NONE) + sarama.MaxResponseSize = 1<<31 - 1 +} + +// Config for Reader +type Config struct { + KafkaAddr []string + // the CommitTs of binlog return by reader will bigger than the config CommitTs + CommitTS int64 + Offset int64 // start at kafka offset + // if Topic is empty, use the default name in drainer _obinlog + Topic string + ClusterID string + // buffer size of messages of the reader internal. + // default value 1. + // suggest only setting the buffer size of this if you want the reader to buffer + // message internal and leave `SaramaBufferSize` as 1 default. + MessageBufferSize int + // the sarama internal buffer size of messages. + SaramaBufferSize int +} + +// nolint: unused, deadcode +func (c *Config) getSaramaBufferSize() int { + if c.SaramaBufferSize > 0 { + return c.SaramaBufferSize + } + + return 1 +} + +func (c *Config) getMessageBufferSize() int { + if c.MessageBufferSize > 0 { + return c.MessageBufferSize + } + + return 1 +} + +func (c *Config) valid() error { + if len(c.Topic) == 0 && len(c.ClusterID) == 0 { + return errors.New("Topic or ClusterID must be set") + } + + return nil +} + +// Message read from reader +type Message struct { + Binlog *pb.Binlog + Offset int64 // kafka offset +} + +// Reader to read binlog from kafka +type Reader struct { + cfg *Config + client sarama.Client + + msgs chan *Message + stop chan struct{} + clusterID string +} + +func (r *Reader) getTopic() (string, int32) { + if r.cfg.Topic != "" { + return r.cfg.Topic, 0 + } + + return r.cfg.ClusterID + "_obinlog", 0 +} + +// NewReader creates an instance of Reader +func NewReader(cfg *Config) (r *Reader, err error) { + err = cfg.valid() + if err != nil { + return r, errors.Trace(err) + } + + r = &Reader{ + cfg: cfg, + stop: make(chan struct{}), + msgs: make(chan *Message, cfg.getMessageBufferSize()), + clusterID: cfg.ClusterID, + } + + conf := sarama.NewConfig() + // set to 10 minutes to prevent i/o timeout when reading huge message + conf.Net.ReadTimeout = KafkaReadTimeout + if cfg.SaramaBufferSize > 0 { + conf.ChannelBufferSize = cfg.SaramaBufferSize + } + + r.client, err = sarama.NewClient(r.cfg.KafkaAddr, conf) + if err != nil { + err = errors.Trace(err) + r = nil + return + } + + if r.cfg.CommitTS > 0 { + r.cfg.Offset, err = r.getOffsetByTS(r.cfg.CommitTS, conf) + if err != nil { + err = errors.Trace(err) + r = nil + return + } + log.Debug("set offset to", zap.Int64("offset", r.cfg.Offset)) + } + + go r.run() + + return +} + +// Close shuts down the reader +func (r *Reader) Close() { + close(r.stop) + + _ = r.client.Close() +} + +// Messages returns a chan that contains unread buffered message +func (r *Reader) Messages() (msgs <-chan *Message) { + return r.msgs +} + +func (r *Reader) getOffsetByTS(ts int64, conf *sarama.Config) (offset int64, err error) { + // set true to retrieve error + conf.Consumer.Return.Errors = true + seeker, err := NewKafkaSeeker(r.cfg.KafkaAddr, conf) + if err != nil { + err = errors.Trace(err) + return + } + + topic, partition := r.getTopic() + log.Debug("get offset", + zap.String("topic", topic), + zap.Int32("partition", partition), + zap.Int64("ts", ts)) + offsets, err := seeker.Seek(topic, ts, []int32{partition}) + if err != nil { + err = errors.Trace(err) + return + } + + offset = offsets[0] + + return +} + +func (r *Reader) run() { + offset := r.cfg.Offset + log.Debug("start at", zap.Int64("offset", offset)) + + consumer, err := sarama.NewConsumerFromClient(r.client) + if err != nil { + log.Fatal("create kafka consumer failed", zap.Error(err)) + } + defer func() { + _ = consumer.Close() + }() + topic, partition := r.getTopic() + partitionConsumer, err := consumer.ConsumePartition(topic, partition, offset) + if err != nil { + log.Fatal("create kafka partition consumer failed", zap.Error(err)) + } + defer func() { + _ = partitionConsumer.Close() + }() + // add select to avoid message blocking while reading + for { + select { + case <-r.stop: + // clean environment + _ = partitionConsumer.Close() + close(r.msgs) + log.Info("reader stop to run") + return + case kmsg := <-partitionConsumer.Messages(): + log.Debug("get kafka message", zap.Int64("offset", kmsg.Offset)) + binlog := new(pb.Binlog) + err := binlog.Unmarshal(kmsg.Value) + if err != nil { + log.Warn("unmarshal binlog failed", zap.Error(err)) + continue + } + if r.cfg.CommitTS > 0 && binlog.CommitTs <= r.cfg.CommitTS { + log.Warn("skip binlog CommitTs", zap.Int64("commitTS", binlog.CommitTs)) + continue + } + + msg := &Message{ + Binlog: binlog, + Offset: kmsg.Offset, + } + select { + case r.msgs <- msg: + case <-r.stop: + // In the next iteration, the <-r.stop would match again and prepare to quit + continue + } + } + } +} diff --git a/pkg/tidb-binlog/node/BUILD.bazel b/pkg/tidb-binlog/node/BUILD.bazel new file mode 100644 index 0000000000000..3c703a81936ec --- /dev/null +++ b/pkg/tidb-binlog/node/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "node", + srcs = [ + "node.go", + "registry.go", + ], + importpath = "github.com/pingcap/tidb/pkg/tidb-binlog/node", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/etcd", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//oracle", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "node_test", + timeout = "short", + srcs = ["registry_test.go"], + embed = [":node"], + flaky = True, + shard_count = 3, + deps = [ + "//pkg/util/etcd", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_tests_v3//integration", + ], +) diff --git a/tidb-binlog/node/node.go b/pkg/tidb-binlog/node/node.go similarity index 100% rename from tidb-binlog/node/node.go rename to pkg/tidb-binlog/node/node.go diff --git a/tidb-binlog/node/registry.go b/pkg/tidb-binlog/node/registry.go similarity index 99% rename from tidb-binlog/node/registry.go rename to pkg/tidb-binlog/node/registry.go index 5d7d07acb2044..27c17a83cada4 100644 --- a/tidb-binlog/node/registry.go +++ b/pkg/tidb-binlog/node/registry.go @@ -23,7 +23,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/etcd" + "github.com/pingcap/tidb/pkg/util/etcd" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/pkg/tidb-binlog/node/registry_test.go b/pkg/tidb-binlog/node/registry_test.go new file mode 100644 index 0000000000000..bbe94f52943dd --- /dev/null +++ b/pkg/tidb-binlog/node/registry_test.go @@ -0,0 +1,111 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package node + +import ( + "context" + "path" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/util/etcd" + "github.com/stretchr/testify/require" + "go.etcd.io/etcd/tests/v3/integration" +) + +var nodePrefix = path.Join(DefaultRootPath, NodePrefix[PumpNode]) + +type RegisrerTestClient interface { + Node(context.Context, string, string) (*Status, int64, error) +} + +func TestUpdateNodeInfo(t *testing.T) { + integration.BeforeTestExternal(t) + testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + defer testEtcdCluster.Terminate(t) + + etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) + r := NewEtcdRegistry(etcdclient, time.Duration(30)*time.Second) + ns := &Status{ + NodeID: "test", + Addr: "test", + State: Online, + IsAlive: true, + } + + err := r.UpdateNode(context.Background(), nodePrefix, ns) + require.NoError(t, err) + mustEqualStatus(t, r, ns.NodeID, ns) + + ns.Addr = "localhost:1234" + err = r.UpdateNode(context.Background(), nodePrefix, ns) + require.NoError(t, err) + mustEqualStatus(t, r, ns.NodeID, ns) + // use Nodes() to query node status + ss, _, err := r.Nodes(context.Background(), nodePrefix) + require.NoError(t, err) + require.Len(t, ss, 1) +} + +func TestRegisterNode(t *testing.T) { + integration.BeforeTestExternal(t) + testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + defer testEtcdCluster.Terminate(t) + + etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) + r := NewEtcdRegistry(etcdclient, time.Duration(5)*time.Second) + + ns := &Status{ + NodeID: "test", + Addr: "test", + State: Online, + IsAlive: true, + } + err := r.UpdateNode(context.Background(), nodePrefix, ns) + require.NoError(t, err) + mustEqualStatus(t, r, ns.NodeID, ns) + + ns.State = Offline + err = r.UpdateNode(context.Background(), nodePrefix, ns) + require.NoError(t, err) + mustEqualStatus(t, r, ns.NodeID, ns) +} + +func TestRefreshNode(t *testing.T) { + integration.BeforeTestExternal(t) + testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) + defer testEtcdCluster.Terminate(t) + + etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) + r := NewEtcdRegistry(etcdclient, time.Duration(5)*time.Second) + + ns := &Status{ + NodeID: "test", + Addr: "test", + State: Online, + IsAlive: true, + } + err := r.UpdateNode(context.Background(), nodePrefix, ns) + require.NoError(t, err) + + ns.IsAlive = true + mustEqualStatus(t, r, ns.NodeID, ns) +} + +func mustEqualStatus(t *testing.T, r RegisrerTestClient, nodeID string, status *Status) { + ns, _, err := r.Node(context.Background(), nodePrefix, nodeID) + require.NoError(t, err) + require.Equal(t, status, ns) +} diff --git a/tidb-binlog/proto/generate-binlog.sh b/pkg/tidb-binlog/proto/generate-binlog.sh similarity index 100% rename from tidb-binlog/proto/generate-binlog.sh rename to pkg/tidb-binlog/proto/generate-binlog.sh diff --git a/pkg/tidb-binlog/proto/go-binlog/BUILD.bazel b/pkg/tidb-binlog/proto/go-binlog/BUILD.bazel new file mode 100644 index 0000000000000..1f2a5aa3e359f --- /dev/null +++ b/pkg/tidb-binlog/proto/go-binlog/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go-binlog", + srcs = ["secondary_binlog.pb.go"], + importpath = "github.com/pingcap/tidb/pkg/tidb-binlog/proto/go-binlog", + visibility = ["//visibility:public"], + deps = ["@com_github_golang_protobuf//proto"], +) diff --git a/tidb-binlog/proto/go-binlog/secondary_binlog.pb.go b/pkg/tidb-binlog/proto/go-binlog/secondary_binlog.pb.go similarity index 100% rename from tidb-binlog/proto/go-binlog/secondary_binlog.pb.go rename to pkg/tidb-binlog/proto/go-binlog/secondary_binlog.pb.go diff --git a/tidb-binlog/proto/proto/secondary_binlog.proto b/pkg/tidb-binlog/proto/proto/secondary_binlog.proto similarity index 100% rename from tidb-binlog/proto/proto/secondary_binlog.proto rename to pkg/tidb-binlog/proto/proto/secondary_binlog.proto diff --git a/pkg/tidb-binlog/pump_client/BUILD.bazel b/pkg/tidb-binlog/pump_client/BUILD.bazel new file mode 100644 index 0000000000000..396ab11497d44 --- /dev/null +++ b/pkg/tidb-binlog/pump_client/BUILD.bazel @@ -0,0 +1,47 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "pump_client", + srcs = [ + "client.go", + "pump.go", + "selector.go", + ], + importpath = "github.com/pingcap/tidb/pkg/tidb-binlog/pump_client", + visibility = ["//visibility:public"], + deps = [ + "//pkg/tidb-binlog/node", + "//pkg/util", + "//pkg/util/etcd", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_tikv_pd_client//:client", + "@io_etcd_go_etcd_api_v3//mvccpb", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//credentials", + "@org_golang_google_grpc//credentials/insecure", + "@org_golang_google_grpc//status", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "pump_client_test", + timeout = "short", + srcs = [ + "bench_test.go", + "client_test.go", + ], + embed = [":pump_client"], + flaky = True, + deps = [ + "//pkg/tidb-binlog/node", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_stretchr_testify//require", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + ], +) diff --git a/tidb-binlog/pump_client/bench_test.go b/pkg/tidb-binlog/pump_client/bench_test.go similarity index 100% rename from tidb-binlog/pump_client/bench_test.go rename to pkg/tidb-binlog/pump_client/bench_test.go diff --git a/pkg/tidb-binlog/pump_client/client.go b/pkg/tidb-binlog/pump_client/client.go new file mode 100644 index 0000000000000..f69b3c2ec9cf2 --- /dev/null +++ b/pkg/tidb-binlog/pump_client/client.go @@ -0,0 +1,614 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package client + +import ( + "context" + "crypto/tls" + "encoding/json" + "path" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/etcd" + pb "github.com/pingcap/tipb/go-binlog" + pd "github.com/tikv/pd/client" + "go.etcd.io/etcd/api/v3/mvccpb" + "go.uber.org/zap" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + // DefaultEtcdTimeout is the default timeout config for etcd. + DefaultEtcdTimeout = 5 * time.Second + + // DefaultRetryTime is the default retry time for each pump. + DefaultRetryTime = 10 + + // DefaultBinlogWriteTimeout is the default max time binlog can use to write to pump. + DefaultBinlogWriteTimeout = 15 * time.Second + + // CheckInterval is the default interval for check unavailable pumps. + CheckInterval = 30 * time.Second +) + +var ( + // ErrNoAvaliablePump means no available pump to write binlog. + ErrNoAvaliablePump = errors.New("no available pump to write binlog") + + // CommitBinlogTimeout is the max retry duration time for write commit/rollback binlog. + CommitBinlogTimeout = 10 * time.Minute + + // RetryInterval is the interval of retrying to write binlog. + RetryInterval = 100 * time.Millisecond +) + +// PumpInfos saves pumps' information in pumps client. +type PumpInfos struct { + // Pumps saves the map of pump's nodeID and pump status. + Pumps map[string]*PumpStatus + + // AvliablePumps saves the whole available pumps' status. + AvaliablePumps map[string]*PumpStatus + + // UnAvaliablePumps saves the unavailable pumps. + // And only pump with Online state in this map need check is it available. + UnAvaliablePumps map[string]*PumpStatus +} + +// NewPumpInfos returns a PumpInfos. +func NewPumpInfos() *PumpInfos { + return &PumpInfos{ + Pumps: make(map[string]*PumpStatus), + AvaliablePumps: make(map[string]*PumpStatus), + UnAvaliablePumps: make(map[string]*PumpStatus), + } +} + +// PumpsClient is the client of pumps. +type PumpsClient struct { + sync.RWMutex + + ctx context.Context + + cancel context.CancelFunc + + wg sync.WaitGroup + + // ClusterID is the cluster ID of this tidb cluster. + ClusterID uint64 + + // the registry of etcd. + EtcdRegistry *node.EtcdRegistry + + // Pumps saves the pumps' information. + Pumps *PumpInfos + + // Selector will select a suitable pump. + Selector PumpSelector + + // the max retry time if write binlog failed, obsolete now. + RetryTime int + + // BinlogWriteTimeout is the max time binlog can use to write to pump. + BinlogWriteTimeout time.Duration + + // Security is the security config + Security *tls.Config + + // binlog socket file path, for compatible with kafka version pump. + binlogSocket string + + nodePath string +} + +// NewPumpsClient returns a PumpsClient. +// TODO: get strategy from etcd, and can update strategy in real-time. Use Range as default now. +func NewPumpsClient(etcdURLs, strategy string, timeout time.Duration, securityOpt pd.SecurityOption) (*PumpsClient, error) { + ectdEndpoints, err := util.ParseHostPortAddr(etcdURLs) + if err != nil { + return nil, errors.Trace(err) + } + + // get clusterid + pdCli, err := pd.NewClient(ectdEndpoints, securityOpt) + if err != nil { + return nil, errors.Trace(err) + } + clusterID := pdCli.GetClusterID(context.Background()) + pdCli.Close() + + security, err := util.ToTLSConfig(securityOpt.CAPath, securityOpt.CertPath, securityOpt.KeyPath) + if err != nil { + return nil, errors.Trace(err) + } + + cli, err := etcd.NewClientFromCfg(ectdEndpoints, DefaultEtcdTimeout, node.DefaultRootPath, security) + if err != nil { + return nil, errors.Trace(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + newPumpsClient := &PumpsClient{ + ctx: ctx, + cancel: cancel, + ClusterID: clusterID, + EtcdRegistry: node.NewEtcdRegistry(cli, DefaultEtcdTimeout), + Pumps: NewPumpInfos(), + Selector: NewSelector(strategy), + BinlogWriteTimeout: timeout, + Security: security, + nodePath: path.Join(node.DefaultRootPath, node.NodePrefix[node.PumpNode]), + } + + revision, err := newPumpsClient.getPumpStatus(ctx) + if err != nil { + return nil, errors.Trace(err) + } + + if len(newPumpsClient.Pumps.Pumps) == 0 { + return nil, errors.New("no pump found in pd") + } + newPumpsClient.Selector.SetPumps(copyPumps(newPumpsClient.Pumps.AvaliablePumps)) + + newPumpsClient.wg.Add(2) + go func() { + newPumpsClient.watchStatus(revision) + newPumpsClient.wg.Done() + }() + + go func() { + newPumpsClient.detect() + newPumpsClient.wg.Done() + }() + + return newPumpsClient, nil +} + +// NewLocalPumpsClient returns a PumpsClient, this PumpsClient will write binlog by socket file. For compatible with kafka version pump. +func NewLocalPumpsClient(etcdURLs, binlogSocket string, timeout time.Duration, securityOpt pd.SecurityOption) (*PumpsClient, error) { + ectdEndpoints, err := util.ParseHostPortAddr(etcdURLs) + if err != nil { + return nil, errors.Trace(err) + } + + // get clusterid + pdCli, err := pd.NewClient(ectdEndpoints, securityOpt) + if err != nil { + return nil, errors.Trace(err) + } + clusterID := pdCli.GetClusterID(context.Background()) + pdCli.Close() + + security, err := util.ToTLSConfig(securityOpt.CAPath, securityOpt.CertPath, securityOpt.KeyPath) + if err != nil { + return nil, errors.Trace(err) + } + + ctx, cancel := context.WithCancel(context.Background()) + newPumpsClient := &PumpsClient{ + ctx: ctx, + cancel: cancel, + ClusterID: clusterID, + Pumps: NewPumpInfos(), + Selector: NewSelector(LocalUnix), + BinlogWriteTimeout: timeout, + Security: security, + binlogSocket: binlogSocket, + } + newPumpsClient.syncLocalPumpStatus(ctx) + + return newPumpsClient, nil +} + +// getLocalPumpStatus sync the local pump. For compatible with kafka version tidb-binlog. +func (c *PumpsClient) syncLocalPumpStatus(_ context.Context) { + nodeStatus := &node.Status{ + NodeID: localPump, + Addr: c.binlogSocket, + IsAlive: true, + State: node.Online, + } + c.addPump(NewPumpStatus(nodeStatus, c.Security), true) +} + +// getPumpStatus gets all the pumps status in the etcd. +func (c *PumpsClient) getPumpStatus(pctx context.Context) (revision int64, err error) { + nodesStatus, revision, err := c.EtcdRegistry.Nodes(pctx, node.NodePrefix[node.PumpNode]) + if err != nil { + return -1, errors.Trace(err) + } + + for _, status := range nodesStatus { + log.Debug("get pump from pd", zap.String("category", "pumps client"), zap.Stringer("pump", status)) + c.addPump(NewPumpStatus(status, c.Security), false) + } + + return revision, nil +} + +// WriteBinlog writes binlog to a suitable pump. Tips: will never return error for commit/rollback binlog. +func (c *PumpsClient) WriteBinlog(binlog *pb.Binlog) error { + c.RLock() + pumpNum := len(c.Pumps.AvaliablePumps) + selector := c.Selector + c.RUnlock() + + var choosePump *PumpStatus + meetError := false + defer func() { + if meetError { + c.checkPumpAvaliable() + } + + selector.Feedback(binlog.StartTs, binlog.Tp, choosePump) + }() + + commitData, err := binlog.Marshal() + if err != nil { + return errors.Trace(err) + } + req := &pb.WriteBinlogReq{ClusterID: c.ClusterID, Payload: commitData} + + retryTime := 0 + var pump *PumpStatus + var resp *pb.WriteBinlogResp + startTime := time.Now() + + for { + if pump == nil || binlog.Tp == pb.BinlogType_Prewrite { + pump = selector.Select(binlog, retryTime) + } + if pump == nil { + err = ErrNoAvaliablePump + break + } + + resp, err = pump.WriteBinlog(req, c.BinlogWriteTimeout) + if err == nil && resp.Errmsg != "" { + err = errors.New(resp.Errmsg) + } + if err == nil { + choosePump = pump + return nil + } + + meetError = true + log.Warn("write binlog to pump failed", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Stringer("binlog type", binlog.Tp), zap.Int64("start ts", binlog.StartTs), zap.Int64("commit ts", binlog.CommitTs), zap.Int("length", len(commitData)), zap.Error(err)) + + if binlog.Tp != pb.BinlogType_Prewrite { + // only use one pump to write commit/rollback binlog, util write success or blocked for ten minutes. And will not return error to tidb. + if time.Since(startTime) > CommitBinlogTimeout { + break + } + } else { + if !isRetryableError(err) { + // this kind of error is not retryable, return directly. + return errors.Trace(err) + } + + // make sure already retry every available pump. + if time.Since(startTime) > c.BinlogWriteTimeout && retryTime > pumpNum { + break + } + + if isConnUnAvliable(err) { + // this kind of error indicate that the grpc connection is not available, may be created the connection again can write success. + pump.ResetGrpcClient() + } + + retryTime++ + } + + if binlog.Tp != pb.BinlogType_Prewrite { + time.Sleep(RetryInterval * 10) + } else { + time.Sleep(RetryInterval) + } + } + + log.Info("write binlog to available pumps all failed, will try unavailable pumps", zap.String("category", "pumps client")) + pump, err1 := c.backoffWriteBinlog(req, binlog.Tp) + if err1 == nil { + choosePump = pump + return nil + } + + return errors.Errorf("write binlog failed, the last error %v", err) +} + +// Return directly for non p-binlog. +// Try every online pump for p-binlog. +func (c *PumpsClient) backoffWriteBinlog(req *pb.WriteBinlogReq, binlogType pb.BinlogType) (pump *PumpStatus, err error) { + if binlogType != pb.BinlogType_Prewrite { + // never return error for commit/rollback binlog. + return nil, nil + } + + c.RLock() + allPumps := copyPumps(c.Pumps.Pumps) + c.RUnlock() + + var resp *pb.WriteBinlogResp + // send binlog to unavailable pumps to retry again. + for _, pump := range allPumps { + if !pump.ShouldBeUsable() { + continue + } + + pump.ResetGrpcClient() + + resp, err = pump.WriteBinlog(req, c.BinlogWriteTimeout) + if err == nil && resp.Errmsg != "" { + err = errors.New(resp.Errmsg) + } + + if err != nil { + log.Warn("try write binlog failed", zap.String("category", "pumps client"), + zap.String("error", err.Error()), + zap.String("NodeID", pump.NodeID)) + continue + } + + // if this pump can write binlog success, set this pump to available. + log.Info("write binlog to pump success, set this pump to available", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID)) + c.setPumpAvailable(pump, true) + return pump, nil + } + + return nil, errors.New("write binlog to unavailable pump failed") +} + +func (c *PumpsClient) checkPumpAvaliable() { + c.RLock() + allPumps := copyPumps(c.Pumps.Pumps) + c.RUnlock() + + for _, pump := range allPumps { + if !pump.IsUsable() { + c.setPumpAvailable(pump, false) + } + } +} + +// setPumpAvailable set pump's isAvailable, and modify UnAvailablePumps or AvailablePumps. +func (c *PumpsClient) setPumpAvailable(pump *PumpStatus, available bool) { + c.Lock() + defer c.Unlock() + + log.Info("set pump available", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Bool("available", available)) + + pump.Reset() + + if available { + delete(c.Pumps.UnAvaliablePumps, pump.NodeID) + if _, ok := c.Pumps.Pumps[pump.NodeID]; ok { + c.Pumps.AvaliablePumps[pump.NodeID] = pump + } + } else { + delete(c.Pumps.AvaliablePumps, pump.NodeID) + if _, ok := c.Pumps.Pumps[pump.NodeID]; ok { + c.Pumps.UnAvaliablePumps[pump.NodeID] = pump + } + } + + c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) +} + +// addPump add a new pump. +func (c *PumpsClient) addPump(pump *PumpStatus, updateSelector bool) { + c.Lock() + defer c.Unlock() + + if pump.ShouldBeUsable() { + c.Pumps.AvaliablePumps[pump.NodeID] = pump + } else { + c.Pumps.UnAvaliablePumps[pump.NodeID] = pump + } + c.Pumps.Pumps[pump.NodeID] = pump + + if updateSelector { + c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) + } +} + +// SetSelectStrategy sets the selector's strategy, strategy should be 'range' or 'hash' now. +func (c *PumpsClient) SetSelectStrategy(strategy string) error { + if strategy != Range && strategy != Hash { + return errors.Errorf("strategy %s is not support", strategy) + } + + c.Lock() + c.Selector = NewSelector(strategy) + c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) + c.Unlock() + return nil +} + +// updatePump update pump's status, and return whether pump's IsAvailable should be changed. +func (c *PumpsClient) updatePump(status *node.Status) (pump *PumpStatus, availableChanged, available bool) { + var ok bool + c.Lock() + if pump, ok = c.Pumps.Pumps[status.NodeID]; ok { + if pump.Status.State != status.State { + if status.State == node.Online { + availableChanged = true + available = true + } else if pump.IsUsable() { + availableChanged = true + available = false + } + } + pump.Status = *status + } + c.Unlock() + + return +} + +// removePump removes a pump, used when pump is offline. +func (c *PumpsClient) removePump(nodeID string) { + c.Lock() + if pump, ok := c.Pumps.Pumps[nodeID]; ok { + pump.Reset() + } + delete(c.Pumps.Pumps, nodeID) + delete(c.Pumps.UnAvaliablePumps, nodeID) + delete(c.Pumps.AvaliablePumps, nodeID) + c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) + c.Unlock() +} + +// exist returns true if pumps client has pump matched this nodeID. +func (c *PumpsClient) exist(nodeID string) bool { + c.RLock() + _, ok := c.Pumps.Pumps[nodeID] + c.RUnlock() + return ok +} + +// watchStatus watchs pump's status in etcd. +func (c *PumpsClient) watchStatus(revision int64) { + rch := c.EtcdRegistry.WatchNode(c.ctx, c.nodePath, revision) + + for { + select { + case <-c.ctx.Done(): + log.Info("watch status finished", zap.String("category", "pumps client")) + return + case wresp := <-rch: + if wresp.Err() != nil { + // meet error, watch from the latest revision. + // pump will update the key periodly, it's ok for we to lost some event here + log.Warn("watch status meet error", zap.String("category", "pumps client"), zap.Error(wresp.Err())) + rch = c.EtcdRegistry.WatchNode(c.ctx, c.nodePath, 0) + continue + } + + for _, ev := range wresp.Events { + status := &node.Status{} + err := json.Unmarshal(ev.Kv.Value, &status) + if err != nil { + log.Error("unmarshal pump status failed", zap.String("category", "pumps client"), zap.ByteString("value", ev.Kv.Value), zap.Error(err)) + continue + } + + switch ev.Type { + case mvccpb.PUT: + if !c.exist(status.NodeID) { + log.Info("find a new pump", zap.String("category", "pumps client"), zap.String("NodeID", status.NodeID)) + c.addPump(NewPumpStatus(status, c.Security), true) + continue + } + + pump, availableChanged, available := c.updatePump(status) + if availableChanged { + log.Info("pump's state is changed", zap.String("category", "pumps client"), zap.String("NodeID", pump.Status.NodeID), zap.String("state", status.State)) + c.setPumpAvailable(pump, available) + } + + case mvccpb.DELETE: + // now will not delete pump node in fact, just for compatibility. + nodeID := node.AnalyzeNodeID(string(ev.Kv.Key)) + log.Info("remove pump", zap.String("category", "pumps client"), zap.String("NodeID", nodeID)) + c.removePump(nodeID) + } + } + } + } +} + +// detect send detect binlog to pumps with online state in UnAvaliablePumps, +func (c *PumpsClient) detect() { + checkTick := time.NewTicker(CheckInterval) + defer checkTick.Stop() + + for { + select { + case <-c.ctx.Done(): + log.Info("heartbeat finished", zap.String("category", "pumps client")) + return + case <-checkTick.C: + // send detect binlog to pump, if this pump can return response without error + // means this pump is available. + needCheckPumps := make([]*PumpStatus, 0, len(c.Pumps.UnAvaliablePumps)) + checkPassPumps := make([]*PumpStatus, 0, 1) + + // TODO: send a more normal request, currently pump will just return success for this empty payload + // not write to Storage + req := &pb.WriteBinlogReq{ClusterID: c.ClusterID, Payload: nil} + c.RLock() + for _, pump := range c.Pumps.UnAvaliablePumps { + if pump.ShouldBeUsable() { + needCheckPumps = append(needCheckPumps, pump) + } + } + c.RUnlock() + + for _, pump := range needCheckPumps { + _, err := pump.WriteBinlog(req, c.BinlogWriteTimeout) + if err == nil { + log.Info("write detect binlog to unavailable pump success", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID)) + checkPassPumps = append(checkPassPumps, pump) + } else { + log.Warn("write detect binlog to pump failed", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Error(err)) + } + } + + for _, pump := range checkPassPumps { + c.setPumpAvailable(pump, true) + } + } + } +} + +// Close closes the PumpsClient. +func (c *PumpsClient) Close() { + log.Info("is closing", zap.String("category", "pumps client")) + c.cancel() + c.wg.Wait() + log.Info("is closed", zap.String("category", "pumps client")) +} + +func isRetryableError(err error) bool { + // ResourceExhausted is a error code in grpc. + // ResourceExhausted indicates some resource has been exhausted, perhaps + // a per-user quota, or perhaps the entire file system is out of space. + // https://github.com/grpc/grpc-go/blob/9cc4fdbde2304827ffdbc7896f49db40c5536600/codes/codes.go#L76 + return !strings.Contains(err.Error(), "ResourceExhausted") +} + +func isConnUnAvliable(err error) bool { + // Unavailable indicates the service is currently unavailable. + // This is a most likely a transient condition and may be corrected + // by retrying with a backoff. + // https://github.com/grpc/grpc-go/blob/76cc50721c5fde18bae10a36f4c202f5f2f95bb7/codes/codes.go#L139 + return status.Code(err) == codes.Unavailable +} + +func copyPumps(pumps map[string]*PumpStatus) []*PumpStatus { + ps := make([]*PumpStatus, 0, len(pumps)) + for _, pump := range pumps { + ps = append(ps, pump) + } + + return ps +} diff --git a/pkg/tidb-binlog/pump_client/client_test.go b/pkg/tidb-binlog/pump_client/client_test.go new file mode 100644 index 0000000000000..6334695bab737 --- /dev/null +++ b/pkg/tidb-binlog/pump_client/client_test.go @@ -0,0 +1,335 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package client + +import ( + "context" + "fmt" + "net" + "os" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" + "github.com/pingcap/tipb/go-binlog" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +var ( + testMaxRecvMsgSize = 1024 + testRetryTime = 5 +) + +type testCase struct { + binlogs []*binlog.Binlog + choosePumps []*PumpStatus + setAvliable []bool + setNodeID []string +} + +func TestSelector(t *testing.T) { + strategys := []string{Hash, Range} + for _, strategy := range strategys { + testSelector(t, strategy) + } +} + +func testSelector(t *testing.T, strategy string) { + pumpsClient := &PumpsClient{ + Pumps: NewPumpInfos(), + Selector: NewSelector(strategy), + BinlogWriteTimeout: DefaultBinlogWriteTimeout, + } + + pumps := []*PumpStatus{{}, {}, {}} + for i, pump := range pumps { + pump.NodeID = fmt.Sprintf("pump%d", i) + pump.State = node.Offline + // set pump client to avoid create grpc client. + pump.Client = binlog.NewPumpClient(nil) + } + + for _, pump := range pumps { + pumpsClient.addPump(pump, false) + } + pumpsClient.Selector.SetPumps(copyPumps(pumpsClient.Pumps.AvaliablePumps)) + + tCase := &testCase{} + + tCase.binlogs = []*binlog.Binlog{ + { + Tp: binlog.BinlogType_Prewrite, + StartTs: 1, + }, { + Tp: binlog.BinlogType_Commit, + StartTs: 1, + CommitTs: 2, + }, { + Tp: binlog.BinlogType_Prewrite, + StartTs: 3, + }, { + Tp: binlog.BinlogType_Commit, + StartTs: 3, + CommitTs: 4, + }, { + Tp: binlog.BinlogType_Prewrite, + StartTs: 5, + }, { + Tp: binlog.BinlogType_Commit, + StartTs: 5, + CommitTs: 6, + }, + } + + tCase.setNodeID = []string{"pump0", "", "pump0", "pump1", "", "pump2"} + tCase.setAvliable = []bool{true, false, false, true, false, true} + tCase.choosePumps = []*PumpStatus{pumpsClient.Pumps.Pumps["pump0"], pumpsClient.Pumps.Pumps["pump0"], nil, + nil, pumpsClient.Pumps.Pumps["pump1"], pumpsClient.Pumps.Pumps["pump1"]} + + for i, nodeID := range tCase.setNodeID { + if nodeID != "" { + pumpsClient.setPumpAvailable(pumpsClient.Pumps.Pumps[nodeID], tCase.setAvliable[i]) + } + pump := pumpsClient.Selector.Select(tCase.binlogs[i], 0) + pumpsClient.Selector.Feedback(tCase.binlogs[i].StartTs, tCase.binlogs[i].Tp, pump) + require.Equal(t, pump, tCase.choosePumps[i]) + } + + for j := 0; j < 10; j++ { + prewriteBinlog := &binlog.Binlog{ + Tp: binlog.BinlogType_Prewrite, + StartTs: int64(j), + } + commitBinlog := &binlog.Binlog{ + Tp: binlog.BinlogType_Commit, + StartTs: int64(j), + } + + pump1 := pumpsClient.Selector.Select(prewriteBinlog, 0) + if j%2 == 0 { + pump1 = pumpsClient.Selector.Select(prewriteBinlog, 1) + } + pumpsClient.Selector.Feedback(prewriteBinlog.StartTs, prewriteBinlog.Tp, pump1) + + pumpsClient.setPumpAvailable(pump1, false) + pump2 := pumpsClient.Selector.Select(commitBinlog, 0) + pumpsClient.Selector.Feedback(commitBinlog.StartTs, commitBinlog.Tp, pump2) + // prewrite binlog and commit binlog with same start ts should choose same pump + require.Equal(t, pump1.NodeID, pump2.NodeID) + pumpsClient.setPumpAvailable(pump1, true) + + // after change strategy, prewrite binlog and commit binlog will choose same pump + pump1 = pumpsClient.Selector.Select(prewriteBinlog, 0) + pumpsClient.Selector.Feedback(prewriteBinlog.StartTs, prewriteBinlog.Tp, pump1) + if strategy == Range { + err := pumpsClient.SetSelectStrategy(Hash) + require.NoError(t, err) + } else { + err := pumpsClient.SetSelectStrategy(Range) + require.NoError(t, err) + } + pump2 = pumpsClient.Selector.Select(commitBinlog, 0) + require.Equal(t, pump1.NodeID, pump2.NodeID) + + // set back + err := pumpsClient.SetSelectStrategy(strategy) + require.NoError(t, err) + } +} + +func TestWriteBinlog(t *testing.T) { + pumpServerConfig := []struct { + addr string + serverMode string + }{ + { + "/tmp/mock-pump.sock", + "unix", + }, { + "127.0.0.1:15049", + "tcp", + }, + } + + // make test faster + RetryInterval = 100 * time.Millisecond + CommitBinlogTimeout = time.Second + + for _, cfg := range pumpServerConfig { + pumpServer, err := createMockPumpServer(cfg.addr, cfg.serverMode, true) + require.NoError(t, err) + + opt := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout(cfg.serverMode, addr, timeout) + }) + clientCon, err := grpc.Dial(cfg.addr, opt, grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(t, err) + require.NotNil(t, clientCon) + pumpClient := mockPumpsClient(binlog.NewPumpClient(clientCon), true) + + // test binlog size bigger than grpc's MaxRecvMsgSize + blog := &binlog.Binlog{ + Tp: binlog.BinlogType_Prewrite, + PrewriteValue: make([]byte, testMaxRecvMsgSize+1), + } + err = pumpClient.WriteBinlog(blog) + require.Error(t, err) + + for i := 0; i < 10; i++ { + // test binlog size smaller than grpc's MaxRecvMsgSize + blog = &binlog.Binlog{ + Tp: binlog.BinlogType_Prewrite, + PrewriteValue: make([]byte, 1), + } + err = pumpClient.WriteBinlog(blog) + require.NoError(t, err) + } + + // after write some binlog, the pump without grpc client will move to unavailable list in pump client. + require.Len(t, pumpClient.Pumps.UnAvaliablePumps, 1) + + // test write commit binlog, will not return error although write binlog failed. + preWriteBinlog := &binlog.Binlog{ + Tp: binlog.BinlogType_Prewrite, + StartTs: 123, + PrewriteValue: make([]byte, 1), + } + commitBinlog := &binlog.Binlog{ + Tp: binlog.BinlogType_Commit, + StartTs: 123, + CommitTs: 123, + PrewriteValue: make([]byte, 1), + } + + err = pumpClient.WriteBinlog(preWriteBinlog) + require.NoError(t, err) + + // test when pump is down + pumpServer.Close() + + // write commit binlog failed will not return error + err = pumpClient.WriteBinlog(commitBinlog) + require.NoError(t, err) + + err = pumpClient.WriteBinlog(blog) + require.Error(t, err) + } +} + +type mockPumpServer struct { + mode string + addr string + server *grpc.Server + + withError bool + retryTime int +} + +// WriteBinlog implements PumpServer interface. +func (p *mockPumpServer) WriteBinlog(ctx context.Context, req *binlog.WriteBinlogReq) (*binlog.WriteBinlogResp, error) { + if !p.withError { + return &binlog.WriteBinlogResp{}, nil + } + + p.retryTime++ + if p.retryTime < testRetryTime { + return &binlog.WriteBinlogResp{}, errors.New("fake error") + } + + // only the last retry will return succuess + p.retryTime = 0 + return &binlog.WriteBinlogResp{}, nil +} + +// PullBinlogs implements PumpServer interface. +func (p *mockPumpServer) PullBinlogs(req *binlog.PullBinlogReq, srv binlog.Pump_PullBinlogsServer) error { + return nil +} + +func (p *mockPumpServer) Close() { + p.server.Stop() + if p.mode == "unix" { + os.Remove(p.addr) + } +} + +func createMockPumpServer(addr string, mode string, withError bool) (*mockPumpServer, error) { + if mode == "unix" { + os.Remove(addr) + } + + l, err := net.Listen(mode, addr) + if err != nil { + return nil, err + } + serv := grpc.NewServer(grpc.MaxRecvMsgSize(testMaxRecvMsgSize)) + pump := &mockPumpServer{ + mode: mode, + addr: addr, + server: serv, + withError: withError, + } + binlog.RegisterPumpServer(serv, pump) + go serv.Serve(l) + + return pump, nil +} + +// mockPumpsClient creates a PumpsClient, used for test. +func mockPumpsClient(client binlog.PumpClient, withBadPump bool) *PumpsClient { + // add a available pump + nodeID1 := "pump-1" + pump1 := &PumpStatus{ + Status: node.Status{ + NodeID: nodeID1, + State: node.Online, + }, + Client: client, + } + + pumps := []*PumpStatus{pump1} + + // add a pump without grpc client + nodeID2 := "pump-2" + pump2 := &PumpStatus{ + Status: node.Status{ + NodeID: nodeID2, + State: node.Online, + }, + } + + pumpInfos := NewPumpInfos() + pumpInfos.Pumps[nodeID1] = pump1 + pumpInfos.AvaliablePumps[nodeID1] = pump1 + + if withBadPump { + pumpInfos.Pumps[nodeID2] = pump2 + pumpInfos.AvaliablePumps[nodeID2] = pump2 + pumps = append(pumps, pump2) + } + + pCli := &PumpsClient{ + ClusterID: 1, + Pumps: pumpInfos, + Selector: NewSelector(Range), + BinlogWriteTimeout: time.Second, + } + pCli.Selector.SetPumps(pumps) + + return pCli +} diff --git a/tidb-binlog/pump_client/pump.go b/pkg/tidb-binlog/pump_client/pump.go similarity index 98% rename from tidb-binlog/pump_client/pump.go rename to pkg/tidb-binlog/pump_client/pump.go index 369a2a98dc083..27b2682cb4d8b 100644 --- a/tidb-binlog/pump_client/pump.go +++ b/pkg/tidb-binlog/pump_client/pump.go @@ -24,7 +24,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/tidb-binlog/node" + "github.com/pingcap/tidb/pkg/tidb-binlog/node" pb "github.com/pingcap/tipb/go-binlog" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/tidb-binlog/pump_client/selector.go b/pkg/tidb-binlog/pump_client/selector.go similarity index 100% rename from tidb-binlog/pump_client/selector.go rename to pkg/tidb-binlog/pump_client/selector.go diff --git a/pkg/timer/BUILD.bazel b/pkg/timer/BUILD.bazel new file mode 100644 index 0000000000000..967cd29c3a9d3 --- /dev/null +++ b/pkg/timer/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "timer_test", + timeout = "short", + srcs = [ + "main_test.go", + "store_intergartion_test.go", + ], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/sessionctx", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/timer/api", + "//pkg/timer/runtime", + "//pkg/timer/tablestore", + "//pkg/util/timeutil", + "@com_github_google_uuid//:uuid", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_tests_v3//integration", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/timer/README.md b/pkg/timer/README.md similarity index 100% rename from timer/README.md rename to pkg/timer/README.md diff --git a/pkg/timer/api/BUILD.bazel b/pkg/timer/api/BUILD.bazel new file mode 100644 index 0000000000000..b9d98e7cc2bb1 --- /dev/null +++ b/pkg/timer/api/BUILD.bazel @@ -0,0 +1,48 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "api", + srcs = [ + "client.go", + "error.go", + "hook.go", + "mem_store.go", + "store.go", + "timer.go", + ], + importpath = "github.com/pingcap/tidb/pkg/timer/api", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/duration", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/timeutil", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_robfig_cron_v3//:cron", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "api_test", + timeout = "short", + srcs = [ + "client_test.go", + "main_test.go", + "schedule_policy_test.go", + "store_test.go", + "timer_test.go", + ], + embed = [":api"], + flaky = True, + race = "on", + shard_count = 13, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/timeutil", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/timer/api/client.go b/pkg/timer/api/client.go new file mode 100644 index 0000000000000..4427a25824af0 --- /dev/null +++ b/pkg/timer/api/client.go @@ -0,0 +1,272 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package api + +import ( + "context" + "encoding/hex" + "strings" + "time" + "unsafe" + + "github.com/google/uuid" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +const ( + clientMaxRetry = 5 + clientRetryBackoff = uint64(1000) +) + +// GetTimerOption is the option to get timers. +type GetTimerOption func(*TimerCond) + +// WithKey indicates to get a timer with the specified key. +func WithKey(key string) GetTimerOption { + return func(cond *TimerCond) { + cond.Key.Set(key) + cond.KeyPrefix = false + } +} + +// WithKeyPrefix to get timers with the indicated key prefix. +func WithKeyPrefix(keyPrefix string) GetTimerOption { + return func(cond *TimerCond) { + cond.Key.Set(keyPrefix) + cond.KeyPrefix = true + } +} + +// WithID indicates to get a timer with the specified id. +func WithID(id string) GetTimerOption { + return func(cond *TimerCond) { + cond.ID.Set(id) + } +} + +// WithTag indicates to get a timer with the specified tags. +func WithTag(tags ...string) GetTimerOption { + return func(cond *TimerCond) { + cond.Tags.Set(tags) + } +} + +// UpdateTimerOption is the option to update the timer. +type UpdateTimerOption func(*TimerUpdate) + +// WithSetEnable indicates to set the timer's `Enable` field. +func WithSetEnable(enable bool) UpdateTimerOption { + return func(update *TimerUpdate) { + update.Enable.Set(enable) + } +} + +// WithSetTimeZone sets the timezone of the timer +func WithSetTimeZone(name string) UpdateTimerOption { + return func(update *TimerUpdate) { + update.TimeZone.Set(name) + } +} + +// WithSetSchedExpr indicates to set the timer's schedule policy. +func WithSetSchedExpr(tp SchedPolicyType, expr string) UpdateTimerOption { + return func(update *TimerUpdate) { + update.SchedPolicyType.Set(tp) + update.SchedPolicyExpr.Set(expr) + } +} + +// WithSetWatermark indicates to set the timer's watermark. +func WithSetWatermark(watermark time.Time) UpdateTimerOption { + return func(update *TimerUpdate) { + update.Watermark.Set(watermark) + } +} + +// WithSetSummaryData indicates to set the timer's summary. +func WithSetSummaryData(summary []byte) UpdateTimerOption { + return func(update *TimerUpdate) { + update.SummaryData.Set(summary) + } +} + +// WithSetTags indicates to set the timer's tags. +func WithSetTags(tags []string) UpdateTimerOption { + return func(update *TimerUpdate) { + update.Tags.Set(tags) + } +} + +// TimerClient is an interface exposed to user to manage timers. +type TimerClient interface { + // GetDefaultNamespace returns the default namespace of this client. + GetDefaultNamespace() string + // CreateTimer creates a new timer. + CreateTimer(ctx context.Context, spec TimerSpec) (*TimerRecord, error) + // GetTimerByID queries the timer by ID. + GetTimerByID(ctx context.Context, timerID string) (*TimerRecord, error) + // GetTimerByKey queries the timer by key. + GetTimerByKey(ctx context.Context, key string) (*TimerRecord, error) + // GetTimers queries timers by options. + GetTimers(ctx context.Context, opts ...GetTimerOption) ([]*TimerRecord, error) + // UpdateTimer updates a timer. + UpdateTimer(ctx context.Context, timerID string, opts ...UpdateTimerOption) error + // ManualTriggerEvent triggers event manually. + ManualTriggerEvent(ctx context.Context, timerID string) (string, error) + // CloseTimerEvent closes the triggering event of a timer. + CloseTimerEvent(ctx context.Context, timerID string, eventID string, opts ...UpdateTimerOption) error + // DeleteTimer deletes a timer. + DeleteTimer(ctx context.Context, timerID string) (bool, error) +} + +// DefaultStoreNamespace is the default namespace. +const DefaultStoreNamespace = "default" + +// defaultTimerClient is the default implement of timer client. +type defaultTimerClient struct { + namespace string + store *TimerStore + retryBackoff uint64 +} + +// NewDefaultTimerClient creates a new defaultTimerClient. +func NewDefaultTimerClient(store *TimerStore) TimerClient { + return &defaultTimerClient{ + namespace: DefaultStoreNamespace, + store: store, + retryBackoff: clientRetryBackoff, + } +} + +func (c *defaultTimerClient) GetDefaultNamespace() string { + return c.namespace +} + +func (c *defaultTimerClient) CreateTimer(ctx context.Context, spec TimerSpec) (*TimerRecord, error) { + if spec.Namespace == "" { + spec.Namespace = c.namespace + } + + timerID, err := c.store.Create(ctx, &TimerRecord{ + TimerSpec: spec, + }) + + if err != nil { + return nil, err + } + return c.store.GetByID(ctx, timerID) +} + +func (c *defaultTimerClient) GetTimerByID(ctx context.Context, timerID string) (*TimerRecord, error) { + return c.store.GetByID(ctx, timerID) +} + +func (c *defaultTimerClient) GetTimerByKey(ctx context.Context, key string) (*TimerRecord, error) { + return c.store.GetByKey(ctx, c.namespace, key) +} + +func (c *defaultTimerClient) GetTimers(ctx context.Context, opts ...GetTimerOption) ([]*TimerRecord, error) { + cond := &TimerCond{} + for _, opt := range opts { + opt(cond) + } + return c.store.List(ctx, cond) +} + +func (c *defaultTimerClient) UpdateTimer(ctx context.Context, timerID string, opts ...UpdateTimerOption) error { + update := &TimerUpdate{} + for _, opt := range opts { + opt(update) + } + return c.store.Update(ctx, timerID, update) +} + +func (c *defaultTimerClient) ManualTriggerEvent(ctx context.Context, timerID string) (string, error) { + reqUUID := uuid.New() + requestID := hex.EncodeToString(reqUUID[:]) + + err := util.RunWithRetry(clientMaxRetry, c.retryBackoff, func() (bool, error) { + timer, err := c.store.GetByID(ctx, timerID) + if err != nil { + return false, err + } + + if timer.EventID != "" { + return false, errors.New("manual trigger is not allowed when event is not closed") + } + + if !timer.Enable { + return false, errors.New("manual trigger is not allowed when timer is disabled") + } + + err = c.store.Update(ctx, timerID, &TimerUpdate{ + ManualRequest: NewOptionalVal(ManualRequest{ + ManualRequestID: requestID, + ManualRequestTime: time.Now(), + ManualTimeout: 2 * time.Minute, + }), + CheckVersion: NewOptionalVal(timer.Version), + }) + + if errors.ErrorEqual(ErrVersionNotMatch, err) { + logutil.BgLogger().Warn("failed to update timer for version not match, retry", zap.String("timerID", timerID)) + return true, err + } + + return false, err + }) + + if err != nil { + return "", err + } + + return requestID, nil +} + +func (c *defaultTimerClient) CloseTimerEvent(ctx context.Context, timerID string, eventID string, opts ...UpdateTimerOption) error { + update := &TimerUpdate{} + for _, opt := range opts { + opt(update) + } + + fields := update.FieldsSet(unsafe.Pointer(&update.Watermark), unsafe.Pointer(&update.SummaryData)) + if len(fields) > 0 { + return errors.Errorf("The field(s) [%s] are not allowed to update when close event", strings.Join(fields, ", ")) + } + + timer, err := c.GetTimerByID(ctx, timerID) + if err != nil { + return err + } + + var zeroTime time.Time + update.CheckEventID.Set(eventID) + update.EventStatus.Set(SchedEventIdle) + update.EventID.Set("") + update.EventData.Set(nil) + update.EventStart.Set(zeroTime) + if !update.Watermark.Present() { + update.Watermark.Set(timer.EventStart) + } + update.EventExtra.Set(EventExtra{}) + return c.store.Update(ctx, timerID, update) +} + +func (c *defaultTimerClient) DeleteTimer(ctx context.Context, timerID string) (bool, error) { + return c.store.Delete(ctx, timerID) +} diff --git a/timer/api/client_test.go b/pkg/timer/api/client_test.go similarity index 100% rename from timer/api/client_test.go rename to pkg/timer/api/client_test.go diff --git a/timer/api/error.go b/pkg/timer/api/error.go similarity index 100% rename from timer/api/error.go rename to pkg/timer/api/error.go diff --git a/timer/api/hook.go b/pkg/timer/api/hook.go similarity index 100% rename from timer/api/hook.go rename to pkg/timer/api/hook.go diff --git a/pkg/timer/api/main_test.go b/pkg/timer/api/main_test.go new file mode 100644 index 0000000000000..a5c704628e325 --- /dev/null +++ b/pkg/timer/api/main_test.go @@ -0,0 +1,27 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package api + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m) +} diff --git a/timer/api/mem_store.go b/pkg/timer/api/mem_store.go similarity index 99% rename from timer/api/mem_store.go rename to pkg/timer/api/mem_store.go index 6dcd59c4f0e8d..803fa1d3fa16e 100644 --- a/timer/api/mem_store.go +++ b/pkg/timer/api/mem_store.go @@ -23,7 +23,7 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/util/timeutil" ) type memStoreWatcher struct { diff --git a/timer/api/schedule_policy_test.go b/pkg/timer/api/schedule_policy_test.go similarity index 100% rename from timer/api/schedule_policy_test.go rename to pkg/timer/api/schedule_policy_test.go diff --git a/timer/api/store.go b/pkg/timer/api/store.go similarity index 100% rename from timer/api/store.go rename to pkg/timer/api/store.go diff --git a/pkg/timer/api/store_test.go b/pkg/timer/api/store_test.go new file mode 100644 index 0000000000000..ff1ba989f9688 --- /dev/null +++ b/pkg/timer/api/store_test.go @@ -0,0 +1,345 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package api + +import ( + "reflect" + "testing" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/stretchr/testify/require" +) + +func TestFieldOptional(t *testing.T) { + var opt1 OptionalVal[string] + require.False(t, opt1.Present()) + s, ok := opt1.Get() + require.False(t, ok) + require.Equal(t, "", s) + + opt1.Set("a1") + require.True(t, opt1.Present()) + s, ok = opt1.Get() + require.True(t, ok) + require.Equal(t, "a1", s) + + opt1.Set("a2") + require.True(t, opt1.Present()) + s, ok = opt1.Get() + require.True(t, ok) + require.Equal(t, "a2", s) + + opt1.Clear() + require.False(t, opt1.Present()) + s, ok = opt1.Get() + require.False(t, ok) + require.Equal(t, "", s) + + type Foo struct { + v int + } + var opt2 OptionalVal[*Foo] + foo := &Foo{v: 1} + + f, ok := opt2.Get() + require.False(t, ok) + require.Nil(t, f) + + opt2.Set(foo) + require.True(t, opt2.Present()) + f, ok = opt2.Get() + require.True(t, ok) + require.Same(t, foo, f) + + opt2.Set(nil) + require.True(t, opt2.Present()) + f, ok = opt2.Get() + require.True(t, ok) + require.Nil(t, f) + + opt2.Clear() + f, ok = opt2.Get() + require.False(t, ok) + require.Nil(t, f) +} + +func TestFieldsReflect(t *testing.T) { + var cond TimerCond + require.Empty(t, cond.FieldsSet()) + + cond.Key.Set("k1") + require.Equal(t, []string{"Key"}, cond.FieldsSet()) + + cond.ID.Set("22") + require.Equal(t, []string{"ID", "Key"}, cond.FieldsSet()) + require.Equal(t, []string{"Key"}, cond.FieldsSet(unsafe.Pointer(&cond.ID))) + + cond.Key.Clear() + require.Equal(t, []string{"ID"}, cond.FieldsSet()) + + cond.KeyPrefix = true + cond.Clear() + require.Empty(t, cond.FieldsSet()) + require.False(t, cond.KeyPrefix) + + var update TimerUpdate + require.Empty(t, update.FieldsSet()) + + update.Watermark.Set(time.Now()) + require.Equal(t, []string{"Watermark"}, update.FieldsSet()) + + update.Enable.Set(true) + require.Equal(t, []string{"Enable", "Watermark"}, update.FieldsSet()) + require.Equal(t, []string{"Watermark"}, update.FieldsSet(unsafe.Pointer(&update.Enable))) + + update.Watermark.Clear() + require.Equal(t, []string{"Enable"}, update.FieldsSet()) + + update.Clear() + require.Empty(t, update.FieldsSet()) +} + +func TestTimerRecordCond(t *testing.T) { + tm := &TimerRecord{ + ID: "123", + TimerSpec: TimerSpec{ + Namespace: "n1", + Key: "/path/to/key", + Tags: []string{"tagA1", "tagA2"}, + }, + } + + // ID + cond := &TimerCond{ID: NewOptionalVal("123")} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{ID: NewOptionalVal("1")} + require.False(t, cond.Match(tm)) + + // Namespace + cond = &TimerCond{Namespace: NewOptionalVal("n1")} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{Namespace: NewOptionalVal("n2")} + require.False(t, cond.Match(tm)) + + // Key + cond = &TimerCond{Key: NewOptionalVal("/path/to/key")} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{Key: NewOptionalVal("/path/to/")} + require.False(t, cond.Match(tm)) + + // keyPrefix + cond = &TimerCond{Key: NewOptionalVal("/path/to/"), KeyPrefix: true} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{Key: NewOptionalVal("/path/to2"), KeyPrefix: true} + require.False(t, cond.Match(tm)) + + // Tags + tm2 := tm.Clone() + tm2.Tags = nil + + cond = &TimerCond{Tags: NewOptionalVal([]string{})} + require.True(t, cond.Match(tm)) + require.True(t, cond.Match(tm2)) + + cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA"})} + require.False(t, cond.Match(tm)) + require.False(t, cond.Match(tm2)) + + cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1"})} + require.True(t, cond.Match(tm)) + require.False(t, cond.Match(tm2)) + + cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1", "tagA2"})} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1", "tagB1"})} + require.False(t, cond.Match(tm)) + + // Combined condition + cond = &TimerCond{ID: NewOptionalVal("123"), Key: NewOptionalVal("/path/to/key")} + require.True(t, cond.Match(tm)) + + cond = &TimerCond{ID: NewOptionalVal("123"), Key: NewOptionalVal("/path/to/")} + require.False(t, cond.Match(tm)) +} + +func TestOperatorCond(t *testing.T) { + tm := &TimerRecord{ + ID: "123", + TimerSpec: TimerSpec{ + Namespace: "n1", + Key: "/path/to/key", + }, + } + + cond1 := &TimerCond{ID: NewOptionalVal("123")} + cond2 := &TimerCond{ID: NewOptionalVal("456")} + cond3 := &TimerCond{Namespace: NewOptionalVal("n1")} + cond4 := &TimerCond{Namespace: NewOptionalVal("n2")} + + require.True(t, And(cond1, cond3).Match(tm)) + require.False(t, And(cond1, cond2, cond3).Match(tm)) + require.False(t, Or(cond2, cond4).Match(tm)) + require.True(t, Or(cond2, cond1, cond4).Match(tm)) + + require.False(t, Not(And(cond1, cond3)).Match(tm)) + require.True(t, Not(And(cond1, cond2, cond3)).Match(tm)) + require.True(t, Not(Or(cond2, cond4)).Match(tm)) + require.False(t, Not(Or(cond2, cond1, cond4)).Match(tm)) + + require.False(t, Not(cond1).Match(tm)) + require.True(t, Not(cond2).Match(tm)) +} + +func TestTimerUpdate(t *testing.T) { + timeutil.SetSystemTZ("Asia/Shanghai") + tpl := TimerRecord{ + ID: "123", + TimerSpec: TimerSpec{ + Namespace: "n1", + Key: "/path/to/key", + }, + Version: 567, + } + tm := tpl.Clone() + + // test check version + update := &TimerUpdate{ + Enable: NewOptionalVal(true), + CheckVersion: NewOptionalVal(uint64(0)), + } + _, err := update.apply(tm) + require.Error(t, err) + require.True(t, errors.ErrorEqual(err, ErrVersionNotMatch)) + require.Equal(t, tpl, *tm) + + // test check event id + update = &TimerUpdate{ + Enable: NewOptionalVal(true), + CheckEventID: NewOptionalVal("aa"), + } + _, err = update.apply(tm) + require.Error(t, err) + require.True(t, errors.ErrorEqual(err, ErrEventIDNotMatch)) + require.Equal(t, tpl, *tm) + + // test apply without check for some common fields + now := time.Now() + update = &TimerUpdate{ + Enable: NewOptionalVal(true), + TimeZone: NewOptionalVal("UTC"), + SchedPolicyType: NewOptionalVal(SchedEventInterval), + SchedPolicyExpr: NewOptionalVal("5h"), + Watermark: NewOptionalVal(now), + SummaryData: NewOptionalVal([]byte("summarydata1")), + EventStatus: NewOptionalVal(SchedEventTrigger), + EventID: NewOptionalVal("event1"), + EventData: NewOptionalVal([]byte("eventdata1")), + EventStart: NewOptionalVal(now.Add(time.Second)), + Tags: NewOptionalVal([]string{"l1", "l2"}), + ManualRequest: NewOptionalVal(ManualRequest{ + ManualRequestID: "req1", + ManualRequestTime: time.Unix(123, 0), + ManualTimeout: time.Minute, + ManualProcessed: true, + ManualEventID: "event1", + }), + EventExtra: NewOptionalVal(EventExtra{ + EventManualRequestID: "req", + EventWatermark: time.Unix(456, 0), + }), + } + + require.Equal(t, reflect.ValueOf(update).Elem().NumField()-2, len(update.FieldsSet())) + record, err := update.apply(tm) + require.NoError(t, err) + require.True(t, record.Enable) + require.Equal(t, "UTC", record.TimeZone) + require.Equal(t, time.UTC, record.Location) + require.Equal(t, SchedEventInterval, record.SchedPolicyType) + require.Equal(t, "5h", record.SchedPolicyExpr) + require.Equal(t, now, record.Watermark) + require.Equal(t, []byte("summarydata1"), record.SummaryData) + require.Equal(t, SchedEventTrigger, record.EventStatus) + require.Equal(t, "event1", record.EventID) + require.Equal(t, []byte("eventdata1"), record.EventData) + require.Equal(t, now.Add(time.Second), record.EventStart) + require.Equal(t, []string{"l1", "l2"}, record.Tags) + require.Equal(t, ManualRequest{ + ManualRequestID: "req1", + ManualRequestTime: time.Unix(123, 0), + ManualTimeout: time.Minute, + ManualProcessed: true, + ManualEventID: "event1", + }, record.ManualRequest) + require.False(t, record.IsManualRequesting()) + require.Equal(t, EventExtra{ + EventManualRequestID: "req", + EventWatermark: time.Unix(456, 0), + }, record.EventExtra) + require.Equal(t, tpl, *tm) + + // test apply without check for ManualRequest and EventExtra + tpl = *record.Clone() + tm = tpl.Clone() + update = &TimerUpdate{ + ManualRequest: NewOptionalVal(ManualRequest{ + ManualRequestID: "req2", + ManualRequestTime: time.Unix(789, 0), + ManualTimeout: time.Minute, + }), + EventExtra: NewOptionalVal(EventExtra{ + EventManualRequestID: "req2", + }), + } + record, err = update.apply(tm) + require.NoError(t, err) + require.Equal(t, ManualRequest{ + ManualRequestID: "req2", + ManualRequestTime: time.Unix(789, 0), + ManualTimeout: time.Minute, + }, record.ManualRequest) + require.True(t, record.IsManualRequesting()) + require.Equal(t, EventExtra{ + EventManualRequestID: "req2", + }, record.EventExtra) + require.Equal(t, tpl, *tm) + + // test apply without check for empty ManualRequest and EventExtra + tpl = *record.Clone() + tm = tpl.Clone() + update = &TimerUpdate{ + ManualRequest: NewOptionalVal(ManualRequest{}), + EventExtra: NewOptionalVal(EventExtra{}), + } + record, err = update.apply(tm) + require.NoError(t, err) + require.Equal(t, ManualRequest{}, record.ManualRequest) + require.False(t, record.IsManualRequesting()) + require.Equal(t, EventExtra{}, record.EventExtra) + require.Equal(t, tpl, *tm) + + emptyUpdate := &TimerUpdate{} + record, err = emptyUpdate.apply(tm) + require.NoError(t, err) + require.Equal(t, tpl, *record) +} diff --git a/pkg/timer/api/timer.go b/pkg/timer/api/timer.go new file mode 100644 index 0000000000000..3b027dd36001b --- /dev/null +++ b/pkg/timer/api/timer.go @@ -0,0 +1,281 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package api + +import ( + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/duration" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/robfig/cron/v3" +) + +// SchedPolicyType is the type of the event schedule policy. +type SchedPolicyType string + +const ( + // SchedEventInterval indicates to schedule events every fixed interval. + SchedEventInterval SchedPolicyType = "INTERVAL" + // SchedEventCron indicates to schedule events by cron expression. + SchedEventCron SchedPolicyType = "CRON" +) + +// SchedEventPolicy is an interface to tell the runtime how to schedule a timer's events. +type SchedEventPolicy interface { + // NextEventTime returns the time to schedule the next timer event. If the second return value is true, + // it means we have a next event schedule after `watermark`. Otherwise, it means there is no more event after `watermark`. + NextEventTime(watermark time.Time) (time.Time, bool) +} + +// SchedIntervalPolicy implements SchedEventPolicy, it is the policy of type `SchedEventInterval`. +type SchedIntervalPolicy struct { + expr string + interval time.Duration +} + +// NewSchedIntervalPolicy creates a new SchedIntervalPolicy. +func NewSchedIntervalPolicy(expr string) (*SchedIntervalPolicy, error) { + interval, err := duration.ParseDuration(expr) + if err != nil { + return nil, errors.Wrapf(err, "invalid schedule event expr '%s'", expr) + } + + return &SchedIntervalPolicy{ + expr: expr, + interval: interval, + }, nil +} + +// NextEventTime returns the next time of the timer event. +// A next event should be triggered after a time indicated by `interval` after watermark. +func (p *SchedIntervalPolicy) NextEventTime(watermark time.Time) (time.Time, bool) { + if watermark.IsZero() { + return watermark, true + } + return watermark.Add(p.interval), true +} + +// CronPolicy implements SchedEventPolicy, it is the policy of type `SchedEventCron`. +type CronPolicy struct { + cronSchedule cron.Schedule +} + +// NewCronPolicy creates a new CronPolicy. +func NewCronPolicy(expr string) (*CronPolicy, error) { + cronSchedule, err := cron.ParseStandard(expr) + if err != nil { + return nil, errors.Wrapf(err, "invalid cron expr '%s'", expr) + } + + return &CronPolicy{ + cronSchedule: cronSchedule, + }, nil +} + +// NextEventTime returns the next time of the timer event. +func (p *CronPolicy) NextEventTime(watermark time.Time) (time.Time, bool) { + next := p.cronSchedule.Next(watermark) + return next, !next.IsZero() +} + +// ManualRequest is the request info to trigger timer manually. +type ManualRequest struct { + // ManualRequestID is the id of manual request. + ManualRequestID string + // ManualRequestTime is the request time. + ManualRequestTime time.Time + // ManualTimeout is the timeout for the request, if the timer is not triggered after timeout, processed will be set to true + // with empty event id. + ManualTimeout time.Duration + // ManualProcessed indicates the request is processed (triggered or timeout). + ManualProcessed bool + // ManualEventID means the triggered event id for the current request. + ManualEventID string +} + +// IsManualRequesting indicates that whether this timer is requesting to trigger event manually. +func (r *ManualRequest) IsManualRequesting() bool { + return r.ManualRequestID != "" && !r.ManualProcessed +} + +// SetProcessed sets the timer's manual request to processed. +func (r *ManualRequest) SetProcessed(eventID string) ManualRequest { + newManual := *r + newManual.ManualProcessed = true + newManual.ManualEventID = eventID + return newManual +} + +// EventExtra stores some extra attributes for event. +type EventExtra struct { + // EventManualRequestID is the related request id of the manual trigger. + // If current event is not triggered manually, it is empty. + EventManualRequestID string + // EventWatermark is the watermark when event triggers. + EventWatermark time.Time +} + +// TimerSpec is the specification of a timer without any runtime status. +type TimerSpec struct { + // Namespace is the namespace of the timer. + Namespace string + // Key is the key of the timer. Key is unique in each namespace. + Key string + // Tags is used to tag a timer. + Tags []string + // Data is a binary which is defined by user. + Data []byte + // TimeZone is the time zone name of the timer to evaluate the schedule policy. + // If TimeZone is empty, it means to use the tidb cluster's time zone. + TimeZone string + // SchedPolicyType is the type of the event schedule policy. + SchedPolicyType SchedPolicyType + // SchedPolicyExpr is the expression of event schedule policy with the type specified by SchedPolicyType. + SchedPolicyExpr string + // HookClass is the class of the hook. + HookClass string + // Watermark indicates the progress the timer's event schedule. + Watermark time.Time + // Enable indicated whether the timer is enabled. + // If it is false, the new timer event will not be scheduled even it is up to time. + Enable bool +} + +// Clone returns a cloned TimerSpec. +func (t *TimerSpec) Clone() *TimerSpec { + clone := *t + return &clone +} + +// Validate validates the TimerSpec. +func (t *TimerSpec) Validate() error { + if t.Namespace == "" { + return errors.New("field 'Namespace' should not be empty") + } + + if t.Key == "" { + return errors.New("field 'Key' should not be empty") + } + + if err := ValidateTimeZone(t.TimeZone); err != nil { + return err + } + + if t.SchedPolicyType == "" { + return errors.New("field 'SchedPolicyType' should not be empty") + } + + if _, err := t.CreateSchedEventPolicy(); err != nil { + return errors.Wrap(err, "schedule event configuration is not valid") + } + + return nil +} + +// CreateSchedEventPolicy creates a SchedEventPolicy according to `SchedPolicyType` and `SchedPolicyExpr`. +func (t *TimerSpec) CreateSchedEventPolicy() (SchedEventPolicy, error) { + return CreateSchedEventPolicy(t.SchedPolicyType, t.SchedPolicyExpr) +} + +// CreateSchedEventPolicy creates a SchedEventPolicy according to `SchedPolicyType` and `SchedPolicyExpr`. +func CreateSchedEventPolicy(tp SchedPolicyType, expr string) (SchedEventPolicy, error) { + switch tp { + case SchedEventInterval: + return NewSchedIntervalPolicy(expr) + case SchedEventCron: + return NewCronPolicy(expr) + default: + return nil, errors.Errorf("invalid schedule event type: '%s'", tp) + } +} + +// SchedEventStatus is the current schedule status of timer's event. +type SchedEventStatus string + +const ( + // SchedEventIdle means the timer is not in trigger state currently. + SchedEventIdle SchedEventStatus = "IDLE" + // SchedEventTrigger means the timer is in trigger state. + SchedEventTrigger SchedEventStatus = "TRIGGER" +) + +// TimerRecord is the timer record saved in the timer store. +type TimerRecord struct { + TimerSpec + // ID is the id of timer, it is unique and auto assigned by the store when created. + ID string + // ManualRequest is the request to trigger timer event manually. + ManualRequest + // EventStatus indicates the current schedule status of the timer's event. + EventStatus SchedEventStatus + // EventID indicates the id of current triggered event. + // If the `EventStatus` is `IDLE`, this value should be empty. + EventID string + // EventData indicates the data of current triggered event. + // If the `EventStatus` is `IDLE`, this value should be empty. + EventData []byte + // EventStart indicates the start time of current triggered event. + // If the `EventStatus` is `IDLE`, `EventStart.IsZero()` should returns true. + EventStart time.Time + // EventExtra stores some extra attributes for event + EventExtra + // SummaryData is a binary which is used to store some summary information of the timer. + // User can update it when closing a timer's event to update the summary. + SummaryData []byte + // CreateTime is the creation time of the timer. + CreateTime time.Time + // Version is the version of the record, when the record updated, version will be increased. + Version uint64 + // Location is used to get the alias of TiDB timezone. + Location *time.Location +} + +// NextEventTime returns the next time for timer to schedule +func (r *TimerRecord) NextEventTime() (tm time.Time, _ bool, _ error) { + if !r.Enable { + return tm, false, nil + } + + watermark := r.Watermark + if loc := r.Location; loc != nil { + watermark = watermark.In(loc) + } + + policy, err := CreateSchedEventPolicy(r.SchedPolicyType, r.SchedPolicyExpr) + if err != nil { + return tm, false, err + } + + tm, ok := policy.NextEventTime(watermark) + return tm, ok, nil +} + +// Clone returns a cloned TimerRecord. +func (r *TimerRecord) Clone() *TimerRecord { + cloned := *r + cloned.TimerSpec = *r.TimerSpec.Clone() + return &cloned +} + +// ValidateTimeZone validates the TimeZone field. +func ValidateTimeZone(tz string) error { + if tz != "" { + if _, err := timeutil.ParseTimeZone(tz); err != nil { + return err + } + } + return nil +} diff --git a/timer/api/timer_test.go b/pkg/timer/api/timer_test.go similarity index 100% rename from timer/api/timer_test.go rename to pkg/timer/api/timer_test.go diff --git a/pkg/timer/main_test.go b/pkg/timer/main_test.go new file mode 100644 index 0000000000000..c0c6b2bf6eff5 --- /dev/null +++ b/pkg/timer/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package timer + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/golang/glog.(*loggingT).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/timer/metrics/BUILD.bazel b/pkg/timer/metrics/BUILD.bazel new file mode 100644 index 0000000000000..8427e48d6244f --- /dev/null +++ b/pkg/timer/metrics/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/timer/metrics", + visibility = ["//visibility:public"], + deps = ["@com_github_prometheus_client_golang//prometheus"], +) diff --git a/timer/metrics/metrics.go b/pkg/timer/metrics/metrics.go similarity index 100% rename from timer/metrics/metrics.go rename to pkg/timer/metrics/metrics.go diff --git a/pkg/timer/runtime/BUILD.bazel b/pkg/timer/runtime/BUILD.bazel new file mode 100644 index 0000000000000..13deabc6f9e38 --- /dev/null +++ b/pkg/timer/runtime/BUILD.bazel @@ -0,0 +1,53 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "runtime", + srcs = [ + "cache.go", + "runtime.go", + "worker.go", + ], + importpath = "github.com/pingcap/tidb/pkg/timer/runtime", + visibility = ["//visibility:public"], + deps = [ + "//pkg/timer/api", + "//pkg/timer/metrics", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/timeutil", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_prometheus_client_golang//prometheus", + "@org_golang_x_exp//maps", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "runtime_test", + timeout = "short", + srcs = [ + "cache_test.go", + "main_test.go", + "runtime_test.go", + "worker_test.go", + ], + embed = [":runtime"], + flaky = True, + race = "on", + shard_count = 23, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/timer/api", + "//pkg/util", + "//pkg/util/mock", + "//pkg/util/timeutil", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@com_github_robfig_cron_v3//:cron", + "@com_github_stretchr_testify//mock", + "@com_github_stretchr_testify//require", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/timer/runtime/cache.go b/pkg/timer/runtime/cache.go new file mode 100644 index 0000000000000..2e9158d94c258 --- /dev/null +++ b/pkg/timer/runtime/cache.go @@ -0,0 +1,243 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package runtime + +import ( + "container/list" + "time" + + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/timeutil" +) + +type runtimeProcStatus int8 + +const ( + procIdle runtimeProcStatus = iota + procTriggering + procWaitTriggerClose +) + +type timerCacheItem struct { + timer *api.TimerRecord + nextEventTime *time.Time + nextTryTriggerTime time.Time + sortEle *list.Element + procStatus runtimeProcStatus + triggerEventID string +} + +func (c *timerCacheItem) update(timer *api.TimerRecord, nowFunc func() time.Time) bool { + if c.timer != nil { + if timer.Version < c.timer.Version { + return false + } + + if timer.Version == c.timer.Version && !locationChanged(timer.Location, c.timer.Location) { + return false + } + } + + timer = timer.Clone() + c.timer = timer + c.nextEventTime = nil + c.nextTryTriggerTime = time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC) + + if timer.Enable { + t, ok, err := timer.NextEventTime() + if err == nil && ok { + c.nextEventTime = &t + } + + if timer.IsManualRequesting() { + now := nowFunc() + c.nextEventTime = &now + } + } + + switch timer.EventStatus { + case api.SchedEventIdle: + if c.nextEventTime != nil { + c.nextTryTriggerTime = *c.nextEventTime + } + case api.SchedEventTrigger: + c.nextTryTriggerTime = timer.EventStart + } + + return true +} + +type timersCache struct { + items map[string]*timerCacheItem + // sorted is the sorted timers by `nextTryTriggerTime` + sorted *list.List + waitCloseTimerIDs map[string]struct{} + nowFunc func() time.Time +} + +func newTimersCache() *timersCache { + return &timersCache{ + items: make(map[string]*timerCacheItem), + sorted: list.New(), + waitCloseTimerIDs: make(map[string]struct{}), + nowFunc: time.Now, + } +} + +func (c *timersCache) updateTimer(timer *api.TimerRecord) bool { + item, ok := c.items[timer.ID] + if !ok { + item = &timerCacheItem{} + c.items[timer.ID] = item + } + + var change bool + if change = item.update(timer, c.nowFunc); change { + c.resort(item) + } + + if item.procStatus == procWaitTriggerClose && item.triggerEventID != timer.EventID { + c.setTimerProcStatus(timer.ID, procIdle, "") + } + + return change +} + +func (c *timersCache) removeTimer(timerID string) bool { + item, ok := c.items[timerID] + if !ok { + return false + } + + delete(c.items, timerID) + c.sorted.Remove(item.sortEle) + delete(c.waitCloseTimerIDs, timerID) + return true +} + +func (c *timersCache) hasTimer(timerID string) (exist bool) { + _, exist = c.items[timerID] + return +} + +func (c *timersCache) partialBatchUpdateTimers(timers []*api.TimerRecord) bool { + change := false + for _, timer := range timers { + if c.updateTimer(timer) { + change = true + } + } + return change +} + +func (c *timersCache) fullUpdateTimers(timers []*api.TimerRecord) { + id2Timer := make(map[string]*api.TimerRecord, len(timers)) + for _, timer := range timers { + id2Timer[timer.ID] = timer + } + + for id := range c.items { + _, ok := id2Timer[id] + if !ok { + c.removeTimer(id) + } + } + c.partialBatchUpdateTimers(timers) +} + +func (c *timersCache) setTimerProcStatus(timerID string, status runtimeProcStatus, triggerEventID string) { + item, ok := c.items[timerID] + if ok { + item.procStatus = status + item.triggerEventID = triggerEventID + if item.procStatus == procWaitTriggerClose { + c.waitCloseTimerIDs[timerID] = struct{}{} + } else { + delete(c.waitCloseTimerIDs, timerID) + } + } +} + +func (c *timersCache) updateNextTryTriggerTime(timerID string, time time.Time) { + item, ok := c.items[timerID] + if !ok { + return + } + + // to make sure try trigger time is always after next event time + if item.timer.EventStatus == api.SchedEventIdle && (item.nextEventTime == nil || time.Before(*item.nextEventTime)) { + return + } + + item.nextTryTriggerTime = time + c.resort(item) +} + +func (c *timersCache) iterTryTriggerTimers(fn func(timer *api.TimerRecord, tryTriggerTime time.Time, nextEventTime *time.Time) bool) { + ele := c.sorted.Front() + for ele != nil { + next := ele.Next() + if item, ok := ele.Value.(*timerCacheItem); ok && item.procStatus == procIdle { + if !fn(item.timer, item.nextTryTriggerTime, item.nextEventTime) { + break + } + } + ele = next + } +} + +func (c *timersCache) resort(item *timerCacheItem) { + ele := item.sortEle + if ele == nil { + ele = c.sorted.PushBack(item) + item.sortEle = ele + } + + nextTrigger := item.nextTryTriggerTime + + if cur := ele.Prev(); cur != nil && cur.Value.(*timerCacheItem).nextTryTriggerTime.After(nextTrigger) { + prev := cur.Prev() + for prev != nil && prev.Value.(*timerCacheItem).nextTryTriggerTime.After(nextTrigger) { + cur = prev + prev = cur.Prev() + } + c.sorted.MoveBefore(ele, cur) + return + } + + if cur := ele.Next(); cur != nil && cur.Value.(*timerCacheItem).nextTryTriggerTime.Before(nextTrigger) { + next := cur.Next() + for next != nil && next.Value.(*timerCacheItem).nextTryTriggerTime.Before(nextTrigger) { + cur = next + next = cur.Next() + } + c.sorted.MoveAfter(ele, cur) + return + } +} + +func locationChanged(a *time.Location, b *time.Location) bool { + if a == b { + return false + } + + if a == nil || b == nil { + return true + } + + _, offset1 := timeutil.Zone(a) + _, offset2 := timeutil.Zone(b) + return offset1 != offset2 +} diff --git a/pkg/timer/runtime/cache_test.go b/pkg/timer/runtime/cache_test.go new file mode 100644 index 0000000000000..492e532bd389b --- /dev/null +++ b/pkg/timer/runtime/cache_test.go @@ -0,0 +1,477 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package runtime + +import ( + "fmt" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/robfig/cron/v3" + "github.com/stretchr/testify/require" +) + +func newTestTimer(id string, policyExpr string, watermark time.Time) *api.TimerRecord { + return &api.TimerRecord{ + ID: id, + TimerSpec: api.TimerSpec{ + Namespace: "n1", + Key: fmt.Sprintf("key-" + id), + SchedPolicyType: api.SchedEventInterval, + SchedPolicyExpr: policyExpr, + HookClass: "hook1", + Watermark: watermark, + Enable: true, + }, + Location: watermark.Location(), + EventStatus: api.SchedEventIdle, + Version: 1, + } +} + +func TestCacheUpdate(t *testing.T) { + now := time.Now().In(time.UTC) + nowFunc := func() time.Time { + return now + } + + cache := newTimersCache() + cache.nowFunc = nowFunc + + // update + t1 := newTestTimer("t1", "10m", now) + require.True(t, cache.updateTimer(t1)) + require.NotSame(t, t1, cache.items[t1.ID].timer) + checkSortedCache(t, cache, [][]any{{t1, now.Add(10 * time.Minute)}}) + require.Equal(t, 1, len(cache.items)) + + // dup update with same version + require.False(t, cache.updateTimer(t1.Clone())) + checkSortedCache(t, cache, [][]any{{t1, now.Add(10 * time.Minute)}}) + require.Equal(t, 1, len(cache.items)) + + // policy changed + t1.SchedPolicyType = api.SchedEventCron + t1.SchedPolicyExpr = "* 1 * * *" + t1.Version++ + require.True(t, cache.updateTimer(t1)) + require.NotSame(t, t1, cache.items[t1.ID].timer) + c, err := cron.ParseStandard(t1.SchedPolicyExpr) + require.NoError(t, err) + checkSortedCache(t, cache, [][]any{{t1, c.Next(now)}}) + require.Equal(t, 1, len(cache.items)) + + // update with same version but loc changed + t1.Location = time.FixedZone("name1", 2*60*60) + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{{t1, c.Next(now.In(t1.Location))}}) + require.Equal(t, 1, len(cache.items)) + + // invalid policy + t1.Location = now.Location() + t1.SchedPolicyType = api.SchedEventInterval + t1.SchedPolicyExpr = "invalid" + t1.Version++ + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) + require.Equal(t, 1, len(cache.items)) + + // manual set next try trigger time for invalid timer + cache.updateNextTryTriggerTime(t1.ID, now.Add(7*time.Second)) + checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) + require.Equal(t, 1, len(cache.items)) + + // not enable + t1.SchedPolicyExpr = "1m" + t1.Enable = false + t1.Version++ + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) + require.Equal(t, 1, len(cache.items)) + + // manual set next try trigger time but before nextEventTime + t1.Enable = true + t1.Version++ + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) + cache.updateNextTryTriggerTime(t1.ID, now.Add(time.Minute-time.Second)) + checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) + + // manual set next try trigger + cache.updateNextTryTriggerTime(t1.ID, now.Add(time.Minute+time.Second)) + checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute + time.Second)}}) + + // should not change procTriggering state + t1.Enable = true + t1.Version++ + cache.setTimerProcStatus(t1.ID, procTriggering, "event1") + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, nil) + require.Equal(t, 1, len(cache.items)) + require.Equal(t, procTriggering, cache.items[t1.ID].procStatus) + require.Equal(t, "event1", cache.items[t1.ID].triggerEventID) + + // test SchedEventTrigger but procIdle + t1.SchedPolicyExpr = "1m" + t1.EventStatus = api.SchedEventTrigger + t1.EventStart = now.Add(-10 * time.Second) + t1.EventID = "event1" + t1.Version++ + require.True(t, cache.updateTimer(t1)) + cache.setTimerProcStatus(t1.ID, procIdle, "event1") + checkSortedCache(t, cache, [][]any{{t1, now.Add(-10 * time.Second)}}) + require.Equal(t, 1, len(cache.items)) + require.Equal(t, 0, len(cache.waitCloseTimerIDs)) + + // should reset procWaitTriggerClose to procIdle + cache.setTimerProcStatus(t1.ID, procWaitTriggerClose, "event1") + checkSortedCache(t, cache, nil) + require.Equal(t, 1, len(cache.items)) + require.Equal(t, 1, len(cache.waitCloseTimerIDs)) + require.Contains(t, cache.waitCloseTimerIDs, t1.ID) + + t1.Version++ + require.True(t, cache.updateTimer(t1)) + require.Equal(t, 1, len(cache.items)) + require.Equal(t, 1, len(cache.waitCloseTimerIDs)) + require.Contains(t, cache.waitCloseTimerIDs, t1.ID) + require.Equal(t, procWaitTriggerClose, cache.items[t1.ID].procStatus) + + t1.EventStatus = api.SchedEventIdle + t1.EventID = "" + t1.Version++ + require.True(t, cache.updateTimer(t1)) + require.Equal(t, procIdle, cache.items[t1.ID].procStatus) + require.Equal(t, "", cache.items[t1.ID].triggerEventID) + require.Equal(t, 0, len(cache.waitCloseTimerIDs)) + require.NotContains(t, cache.waitCloseTimerIDs, t1.ID) + + t1.Version++ + t1.ManualRequest = api.ManualRequest{ + ManualRequestID: "req1", + ManualRequestTime: now, + ManualTimeout: time.Minute, + ManualProcessed: true, + } + require.True(t, cache.updateTimer(t1)) + require.Equal(t, procIdle, cache.items[t1.ID].procStatus) + require.Equal(t, "", cache.items[t1.ID].triggerEventID) + require.Equal(t, 0, len(cache.waitCloseTimerIDs)) + checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) + + t1.Version++ + t1.ManualRequest = api.ManualRequest{ + ManualRequestID: "req2", + ManualRequestTime: now, + ManualTimeout: time.Minute, + } + require.True(t, cache.updateTimer(t1)) + require.Equal(t, procIdle, cache.items[t1.ID].procStatus) + require.Equal(t, "", cache.items[t1.ID].triggerEventID) + require.Equal(t, 0, len(cache.waitCloseTimerIDs)) + checkSortedCache(t, cache, [][]any{{t1, now}}) +} + +func TestCacheSort(t *testing.T) { + now := time.Now().In(time.UTC) + nowFunc := func() time.Time { + return now + } + + cache := newTimersCache() + cache.nowFunc = nowFunc + + checkSortedCache(t, cache, nil) + + t1 := newTestTimer("t1", "10m", now) + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(10 * time.Minute)}, + }) + + t2 := newTestTimer("t2", "20m", now) + require.True(t, cache.updateTimer(t2)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(10 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + }) + + t3 := newTestTimer("t3", "5m", now) + require.True(t, cache.updateTimer(t3)) + checkSortedCache(t, cache, [][]any{ + {t3, now.Add(5 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + }) + + t4 := newTestTimer("t4", "3m", now) + require.True(t, cache.updateTimer(t4)) + checkSortedCache(t, cache, [][]any{ + {t4, now.Add(3 * time.Minute)}, + {t3, now.Add(5 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + }) + + // move left 1 + t3.SchedPolicyExpr = "1m" + t3.Version++ + require.True(t, cache.updateTimer(t3)) + checkSortedCache(t, cache, [][]any{ + {t3, now.Add(1 * time.Minute)}, + {t4, now.Add(3 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + }) + + // move left 2 + t2.SchedPolicyExpr = "2m" + t2.Version++ + require.True(t, cache.updateTimer(t2)) + checkSortedCache(t, cache, [][]any{ + {t3, now.Add(1 * time.Minute)}, + {t2, now.Add(2 * time.Minute)}, + {t4, now.Add(3 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + }) + + // move right 1 + t4.SchedPolicyExpr = "15m" + t4.Version++ + require.True(t, cache.updateTimer(t4)) + checkSortedCache(t, cache, [][]any{ + {t3, now.Add(1 * time.Minute)}, + {t2, now.Add(2 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t4, now.Add(15 * time.Minute)}, + }) + + // move right 2 + t3.SchedPolicyExpr = "12m" + t3.Version++ + require.True(t, cache.updateTimer(t3)) + checkSortedCache(t, cache, [][]any{ + {t2, now.Add(2 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t4, now.Add(15 * time.Minute)}, + }) + + // unchanged + t2.SchedPolicyExpr = "1m" + t2.Version++ + require.True(t, cache.updateTimer(t2)) + checkSortedCache(t, cache, [][]any{ + {t2, now.Add(1 * time.Minute)}, + {t1, now.Add(10 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t4, now.Add(15 * time.Minute)}, + }) + + t1.SchedPolicyExpr = "11m" + t1.Version++ + require.True(t, cache.updateTimer(t1)) + checkSortedCache(t, cache, [][]any{ + {t2, now.Add(1 * time.Minute)}, + {t1, now.Add(11 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t4, now.Add(15 * time.Minute)}, + }) + + t4.SchedPolicyExpr = "16m" + t4.Version++ + require.True(t, cache.updateTimer(t4)) + checkSortedCache(t, cache, [][]any{ + {t2, now.Add(1 * time.Minute)}, + {t1, now.Add(11 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t4, now.Add(16 * time.Minute)}, + }) + + // test updateNextTryTriggerTime + cache.updateNextTryTriggerTime(t2.ID, now.Add(20*time.Minute)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(11 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t4, now.Add(16 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + }) + + cache.updateNextTryTriggerTime(t2.ID, now.Add(14*time.Minute)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(11 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t2, now.Add(14 * time.Minute)}, + {t4, now.Add(16 * time.Minute)}, + }) + + cache.updateNextTryTriggerTime(t3.ID, now.Add(15*time.Minute)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(11 * time.Minute)}, + {t2, now.Add(14 * time.Minute)}, + {t3, now.Add(15 * time.Minute)}, + {t4, now.Add(16 * time.Minute)}, + }) + + // test version update should reset updateNextTryTriggerTime + t3.Version++ + require.True(t, cache.updateTimer(t3)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(11 * time.Minute)}, + {t3, now.Add(12 * time.Minute)}, + {t2, now.Add(14 * time.Minute)}, + {t4, now.Add(16 * time.Minute)}, + }) +} + +func TestFullUpdateCache(t *testing.T) { + now := time.Now().In(time.UTC) + cache := newTimersCache() + cache.nowFunc = func() time.Time { + return now + } + + t1 := newTestTimer("t1", "10m", now) + t2 := newTestTimer("t2", "20m", now) + t3 := newTestTimer("t3", "30m", now) + t4 := newTestTimer("t4", "40m", now) + + require.True(t, cache.updateTimer(t1)) + require.True(t, cache.updateTimer(t2)) + require.True(t, cache.updateTimer(t3)) + require.True(t, cache.updateTimer(t4)) + checkSortedCache(t, cache, [][]any{ + {t1, now.Add(10 * time.Minute)}, + {t2, now.Add(20 * time.Minute)}, + {t3, now.Add(30 * time.Minute)}, + {t4, now.Add(40 * time.Minute)}, + }) + + t1.SchedPolicyExpr = "15m" + t1.Version++ + t3.SchedPolicyExpr = "1m" + t3.Version++ + t5 := newTestTimer("t5", "25m", now) + cache.fullUpdateTimers([]*api.TimerRecord{t1, t3, t5}) + checkSortedCache(t, cache, [][]any{ + {t3, now.Add(1 * time.Minute)}, + {t1, now.Add(15 * time.Minute)}, + {t5, now.Add(25 * time.Minute)}, + }) + require.Equal(t, 3, len(cache.items)) +} + +func checkSortedCache(t *testing.T, cache *timersCache, sorted [][]any) { + i := 0 + cache.iterTryTriggerTimers(func(timer *api.TimerRecord, tryTriggerTime time.Time, nextEventTime *time.Time) bool { + expectedTimer := sorted[i][0].(*api.TimerRecord) + require.NotSame(t, expectedTimer, timer) + require.Equal(t, *expectedTimer, *timer) + item, ok := cache.items[timer.ID] + require.True(t, ok) + require.Equal(t, *expectedTimer, *item.timer) + + if timer.IsManualRequesting() { + require.Equal(t, tryTriggerTime, *nextEventTime) + } else { + if tm, ok, err := timer.NextEventTime(); err == nil { + if !timer.Enable { + require.True(t, tm.IsZero()) + require.False(t, ok) + } else { + require.True(t, ok) + require.NotNil(t, nextEventTime) + require.Equal(t, tm, *nextEventTime) + } + } else { + require.Nil(t, nextEventTime) + } + } + + require.Equal(t, sorted[i][1].(time.Time), tryTriggerTime) + i++ + return true + }) + require.Equal(t, len(sorted), i) +} + +func TestLocationChanged(t *testing.T) { + loc1, _ := time.LoadLocation("America/New_York") + loc2, _ := time.LoadLocation("America/Los_Angeles") + loc3, _ := time.LoadLocation("America/New_York") + loc4 := time.FixedZone("name1", 2*60*60) + loc5 := time.FixedZone("name2", 2*60*60) + loc6 := time.FixedZone("name1", 60*60) + + testCases := []struct { + a *time.Location + b *time.Location + changed bool + }{ + { + a: nil, + b: nil, + changed: false, + }, + { + a: loc1, + b: nil, + changed: true, + }, + { + a: nil, + b: loc1, + changed: true, + }, + { + a: loc1, + b: loc2, + changed: true, + }, + { + a: loc1, + b: loc3, + changed: false, + }, + { + a: loc4, + b: loc5, + changed: false, + }, + { + a: loc4, + b: loc6, + changed: true, + }, + } + + for i, tc := range testCases { + result := locationChanged(tc.a, tc.b) + a, b := "", "" + if tc.a != nil { + n, offset := timeutil.Zone(tc.a) + a = fmt.Sprintf("%s(%d)", n, offset) + } + + if tc.b != nil { + n, offset := timeutil.Zone(tc.b) + b = fmt.Sprintf("%s(%d)", n, offset) + } + + require.Equalf(t, tc.changed, result, "%d: compare %q and %q", i, a, b) + } +} diff --git a/pkg/timer/runtime/main_test.go b/pkg/timer/runtime/main_test.go new file mode 100644 index 0000000000000..4f98163999a89 --- /dev/null +++ b/pkg/timer/runtime/main_test.go @@ -0,0 +1,183 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package runtime + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/mock" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m) +} + +type mockHook struct { + mock.Mock + started chan struct{} + stopped chan struct{} +} + +type newHookFn struct { + mock.Mock +} + +func (n *newHookFn) OnFuncCall() *mock.Call { + return n.On("Func") +} + +func (n *newHookFn) Func() api.Hook { + args := n.Called() + if v := args.Get(0); v != nil { + return v.(api.Hook) + } + return nil +} + +func onlyOnceNewHook(hook api.Hook) func() api.Hook { + n := newHookFn{} + n.OnFuncCall().Return(hook).Once() + return n.Func +} + +func newMockHook() *mockHook { + return &mockHook{ + started: make(chan struct{}), + stopped: make(chan struct{}), + } +} + +func (h *mockHook) Start() { + h.Called() + close(h.started) +} + +func (h *mockHook) Stop() { + h.Called() + close(h.stopped) +} + +func (h *mockHook) OnPreSchedEvent(ctx context.Context, event api.TimerShedEvent) (api.PreSchedEventResult, error) { + args := h.Called(ctx, event) + return args.Get(0).(api.PreSchedEventResult), args.Error(1) +} + +func (h *mockHook) OnSchedEvent(ctx context.Context, event api.TimerShedEvent) error { + args := h.Called(ctx, event) + return args.Error(0) +} + +type mockStoreCore struct { + mock.Mock +} + +func newMockStore() (*mockStoreCore, *api.TimerStore) { + core := &mockStoreCore{} + return core, &api.TimerStore{TimerStoreCore: core} +} + +func (s *mockStoreCore) mock() *mock.Mock { + return &s.Mock +} + +func (s *mockStoreCore) Create(ctx context.Context, record *api.TimerRecord) (string, error) { + args := s.Called(ctx, record) + return args.String(0), args.Error(1) +} + +func (s *mockStoreCore) List(ctx context.Context, cond api.Cond) ([]*api.TimerRecord, error) { + args := s.Called(ctx, cond) + return args.Get(0).([]*api.TimerRecord), args.Error(1) +} + +func (s *mockStoreCore) Update(ctx context.Context, timerID string, update *api.TimerUpdate) error { + args := s.Called(ctx, timerID, update) + return args.Error(0) +} + +func (s *mockStoreCore) Delete(ctx context.Context, timerID string) (bool, error) { + args := s.Called(ctx, timerID) + return args.Bool(0), args.Error(1) +} + +func (s *mockStoreCore) WatchSupported() bool { + args := s.Called() + return args.Bool(0) +} + +func (s *mockStoreCore) Watch(ctx context.Context) api.WatchTimerChan { + args := s.Called(ctx) + return args.Get(0).(api.WatchTimerChan) +} + +func (s *mockStoreCore) Close() { +} + +func waitDone(obj any, timeout time.Duration) { + var ch <-chan struct{} + switch o := obj.(type) { + case chan struct{}: + ch = o + case <-chan struct{}: + ch = o + case *util.WaitGroupWrapper: + newCh := make(chan struct{}) + ch = newCh + + go func() { + o.Wait() + close(newCh) + }() + case *sync.WaitGroup: + newCh := make(chan struct{}) + ch = newCh + + go func() { + o.Wait() + close(newCh) + }() + default: + panic(fmt.Sprintf("unsupported type: %T", obj)) + } + + tm := time.NewTimer(timeout) + defer tm.Stop() + select { + case <-ch: + return + case <-tm.C: + panic("wait done timeout") + } +} + +func checkNotDone(ch <-chan struct{}, after time.Duration) { + if after != 0 { + time.Sleep(after) + } + select { + case <-ch: + panic("the channel is expected not done") + default: + } +} diff --git a/timer/runtime/runtime.go b/pkg/timer/runtime/runtime.go similarity index 98% rename from timer/runtime/runtime.go rename to pkg/timer/runtime/runtime.go index 74b6824fe532f..9901214864f19 100644 --- a/timer/runtime/runtime.go +++ b/pkg/timer/runtime/runtime.go @@ -22,10 +22,10 @@ import ( "time" "github.com/google/uuid" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/timer/metrics" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/timer/metrics" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" "golang.org/x/exp/maps" diff --git a/timer/runtime/runtime_test.go b/pkg/timer/runtime/runtime_test.go similarity index 99% rename from timer/runtime/runtime_test.go rename to pkg/timer/runtime/runtime_test.go index 3b292aea2725f..590208f7fefe2 100644 --- a/timer/runtime/runtime_test.go +++ b/pkg/timer/runtime/runtime_test.go @@ -24,8 +24,8 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/timer/api" - mockutil "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/timer/api" + mockutil "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/timer/runtime/worker.go b/pkg/timer/runtime/worker.go new file mode 100644 index 0000000000000..ca211b92d26df --- /dev/null +++ b/pkg/timer/runtime/worker.go @@ -0,0 +1,450 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package runtime + +import ( + "context" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/timer/metrics" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" +) + +type hookWorkerRetryLoopKeyType struct{} +type hookWorkerRetryRequestKeyType struct{} + +// keys for to set retry interval +// only used for test +var ( + hookWorkerRetryLoopKey = hookWorkerRetryLoopKeyType{} + hookWorkerRetryRequestKey = hookWorkerRetryRequestKeyType{} +) + +const ( + workerRecvChanCap = 128 + workerRespChanCap = 128 + workerEventDefaultRetryInterval = 10 * time.Second + chanBlockInterval = time.Minute +) + +type triggerEventRequest struct { + eventID string + timer *api.TimerRecord + store *api.TimerStore + resp chan<- *triggerEventResponse +} + +func (r *triggerEventRequest) DoneResponse() *triggerEventResponse { + return &triggerEventResponse{ + timerID: r.timer.ID, + eventID: r.eventID, + success: true, + } +} + +func (r *triggerEventRequest) RetryDefaultResponse() *triggerEventResponse { + return &triggerEventResponse{ + timerID: r.timer.ID, + eventID: r.eventID, + retryAfter: api.NewOptionalVal(workerEventDefaultRetryInterval), + success: false, + } +} + +func (r *triggerEventRequest) TimerMetaChangedResponse(t *api.TimerRecord) *triggerEventResponse { + return r.RetryDefaultResponse(). + WithNewTimerRecord(t). + WithRetryImmediately() +} + +type triggerEventResponse struct { + success bool + timerID string + eventID string + newTimerRecord api.OptionalVal[*api.TimerRecord] + retryAfter api.OptionalVal[time.Duration] +} + +func (r *triggerEventResponse) WithRetryImmediately() *triggerEventResponse { + r.retryAfter.Clear() + return r +} + +func (r *triggerEventResponse) WithRetryAfter(d time.Duration) *triggerEventResponse { + r.retryAfter.Set(d) + return r +} + +func (r *triggerEventResponse) WithNewTimerRecord(timer *api.TimerRecord) *triggerEventResponse { + r.newTimerRecord.Set(timer) + return r +} + +type timerEvent struct { + eventID string + record *api.TimerRecord +} + +func (e *timerEvent) EventID() string { + return e.eventID +} + +func (e *timerEvent) Timer() *api.TimerRecord { + return e.record +} + +type hookWorker struct { + ctx context.Context + groupID string + hookClass string + hookFn func() api.Hook + ch chan *triggerEventRequest + logger *zap.Logger + nowFunc func() time.Time + // metrics for worker + triggerRequestCounter prometheus.Counter + onPreSchedEventCounter prometheus.Counter + onPreSchedEventErrCounter prometheus.Counter + onPreSchedEventDelayCounter prometheus.Counter + onSchedEventCounter prometheus.Counter + onSchedEventErrCounter prometheus.Counter + // retryLoopWait/retryRequestWait indicates the wait time before restarting the loop after panic. + retryLoopWait time.Duration + retryRequestWait time.Duration +} + +func newHookWorker(ctx context.Context, wg *util.WaitGroupWrapper, groupID string, hookClass string, hookFn func() api.Hook, nowFunc func() time.Time) *hookWorker { + if nowFunc == nil { + nowFunc = time.Now + } + + w := &hookWorker{ + ctx: ctx, + groupID: groupID, + hookClass: hookClass, + hookFn: hookFn, + ch: make(chan *triggerEventRequest, workerRecvChanCap), + logger: logutil.BgLogger().With( + zap.String("groupID", groupID), + zap.String("hookClass", hookClass), + ), + nowFunc: nowFunc, + triggerRequestCounter: metrics.TimerHookWorkerCounter(hookClass, "trigger"), + onPreSchedEventCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent"), + onPreSchedEventErrCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent_error"), + onPreSchedEventDelayCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent_delay"), + onSchedEventCounter: metrics.TimerHookWorkerCounter(hookClass, "OnSchedEvent"), + onSchedEventErrCounter: metrics.TimerHookWorkerCounter(hookClass, "OnSchedEvent_error"), + retryLoopWait: 10 * time.Second, + retryRequestWait: 5 * time.Second, + } + + if v, ok := ctx.Value(hookWorkerRetryLoopKey).(time.Duration); ok { + w.retryLoopWait = v + } + + if v, ok := ctx.Value(hookWorkerRetryRequestKey).(time.Duration); ok { + w.retryRequestWait = v + } + + wg.Run(func() { + withRecoverUntil(w.ctx, w.loop) + }) + return w +} + +func (w *hookWorker) loop(totalPanic uint64) { + if totalPanic > 0 { + sleep(w.ctx, w.retryLoopWait) + w.logger.Info("timer hookWorker loop resumed from panic", + zap.Uint64("totalPanic", totalPanic), + zap.Duration("delay", w.retryLoopWait)) + } else { + w.logger.Info("timer hookWorker loop started") + } + defer w.logger.Info("timer hookWorker loop exited") + + var hook api.Hook + if w.hookFn != nil { + hook = w.hookFn() + } + + if hook != nil { + defer hook.Stop() + hook.Start() + } + + // TODO: we can have multiple `handleRequestLoop` goroutines running concurrently. + w.handleRequestLoop(hook) +} + +type unhandledRequest struct { + req *triggerEventRequest + retry int +} + +func (w *hookWorker) handleRequestLoop(hook api.Hook) { + var unhandled *unhandledRequest + withRecoverUntil(w.ctx, func(totalPanic uint64) { + wait := w.retryRequestWait + if totalPanic == 0 || (unhandled != nil && unhandled.retry == 0) { + // when retry a request, it will send a response to runtime without calling hook. + // So we can do the first retry immediately to assumption that it will succeed. + wait = 0 + } + + if totalPanic > 0 { + time.Sleep(wait) + w.logger.Info("handleRequestLoop resumed from panic", + zap.Uint64("totalPanic", totalPanic), + zap.Duration("delay", wait)) + } + + if unhandled != nil { + unhandled.retry++ + w.handleRequestOnce(hook, unhandled) + unhandled = nil + } + + for { + select { + case <-w.ctx.Done(): + return + case req := <-w.ch: + unhandled = &unhandledRequest{ + req: req, + } + w.handleRequestOnce(hook, unhandled) + unhandled = nil + } + } + }) +} + +func (w *hookWorker) handleRequestOnce(hook api.Hook, u *unhandledRequest) { + if u == nil || u.req == nil { + return + } + + if u.req.timer == nil { + w.logger.Warn("invalid triggerEventRequest, timer is nil") + return + } + + req := u.req + logger := w.logger.With( + zap.String("timerID", req.timer.ID), + zap.String("timerNamespace", req.timer.Namespace), + zap.String("timerKey", req.timer.Key), + zap.String("eventID", req.eventID), + zap.Int("requestRetry", u.retry), + ) + + if req.resp == nil { + logger.Warn("invalid triggerEventRequest, resp chan is nil") + return + } + + var resp *triggerEventResponse + if u.retry > 0 { + logger.Info("retry triggerEventRequest") + resp = req.RetryDefaultResponse() + } else { + resp = w.triggerEvent(hook, req, logger) + } + w.responseChan(req.resp, resp, logger) +} + +func (w *hookWorker) triggerEvent(hook api.Hook, req *triggerEventRequest, logger *zap.Logger) *triggerEventResponse { + w.triggerRequestCounter.Inc() + timer := req.timer + + if timer.IsManualRequesting() { + logger.Info("manual trigger request detected", + zap.String("requestID", timer.ManualRequestID), + zap.Time("requestTime", timer.ManualRequestTime), + zap.Duration("timeout", timer.ManualTimeout), + ) + timeout := timer.ManualRequestTime.Add(timer.ManualTimeout) + if w.nowFunc().After(timeout) { + logger.Warn( + "cancel manual trigger for timer is disabled for request timeout", + zap.String("requestID", timer.ManualRequestID), + zap.Bool("timerEnable", timer.Enable), + ) + + processed := timer.ManualRequest.SetProcessed("") + err := req.store.Update(w.ctx, timer.ID, &api.TimerUpdate{ + ManualRequest: api.NewOptionalVal(processed), + CheckVersion: api.NewOptionalVal(timer.Version), + }) + + if err == nil { + timer, err = req.store.GetByID(w.ctx, timer.ID) + } + + if err == nil || errors.ErrorEqual(err, api.ErrTimerNotExist) { + return req.TimerMetaChangedResponse(timer) + } + + logger.Error( + "error occurs when close manual request", + zap.Error(err), + zap.Duration("retryAfter", workerEventDefaultRetryInterval), + ) + return req.RetryDefaultResponse() + } + } + + if timer.EventStatus == api.SchedEventIdle { + var preResult api.PreSchedEventResult + if hook != nil { + logger.Debug("call OnPreSchedEvent") + w.onPreSchedEventCounter.Inc() + result, err := hook.OnPreSchedEvent(w.ctx, &timerEvent{ + eventID: req.eventID, + record: timer, + }) + + if err != nil { + logger.Error( + "error occurs when invoking hook.OnPreSchedEvent", + zap.Error(err), + zap.Duration("retryAfter", workerEventDefaultRetryInterval), + ) + w.onPreSchedEventErrCounter.Inc() + return req.RetryDefaultResponse() + } + + if result.Delay > 0 { + w.onPreSchedEventDelayCounter.Inc() + return req.RetryDefaultResponse().WithRetryAfter(result.Delay) + } + preResult = result + } + + update := buildEventUpdate(req, preResult, w.nowFunc) + if err := req.store.Update(w.ctx, timer.ID, update); err != nil { + if errors.ErrorEqual(err, api.ErrVersionNotMatch) { + logger.Info("cannot change timer to trigger state, timer version not match", + zap.Uint64("timerVersion", timer.Version), + ) + var newTimer *api.TimerRecord + newTimer, err = req.store.GetByID(w.ctx, timer.ID) + if err == nil { + return req.TimerMetaChangedResponse(newTimer) + } + } + + if errors.ErrorEqual(err, api.ErrTimerNotExist) { + logger.Info("cannot change timer to trigger state, timer deleted") + return req.TimerMetaChangedResponse(nil) + } + + logger.Error("error occurs to change timer to trigger state,", + zap.Error(err), + zap.Duration("retryAfter", workerEventDefaultRetryInterval), + ) + return req.RetryDefaultResponse() + } + } + + timer, err := req.store.GetByID(w.ctx, timer.ID) + if errors.ErrorEqual(err, api.ErrTimerNotExist) { + logger.Info("cannot trigger timer event, timer deleted") + return req.TimerMetaChangedResponse(timer) + } + + if err != nil { + logger.Error( + "error occurs when getting timer record to trigger timer event", + zap.Duration("retryAfter", workerEventDefaultRetryInterval), + ) + return req.RetryDefaultResponse() + } + + if timer.EventID != req.eventID { + logger.Info("cannot trigger timer event, timer event closed") + return req.TimerMetaChangedResponse(timer) + } + + if hook != nil { + logger.Debug("call OnSchedEvent") + w.onSchedEventCounter.Inc() + err = hook.OnSchedEvent(w.ctx, &timerEvent{ + eventID: req.eventID, + record: timer, + }) + + if err != nil { + w.onSchedEventErrCounter.Inc() + logger.Error( + "error occurs when invoking hook OnTimerEvent", + zap.Error(err), + zap.Duration("retryAfter", workerEventDefaultRetryInterval), + ) + return req.RetryDefaultResponse().WithNewTimerRecord(timer) + } + } + + return req.DoneResponse().WithNewTimerRecord(timer) +} + +func (w *hookWorker) responseChan(ch chan<- *triggerEventResponse, resp *triggerEventResponse, logger *zap.Logger) bool { + sendStart := time.Now() + ticker := time.NewTicker(chanBlockInterval) + defer ticker.Stop() + for { + select { + case <-w.ctx.Done(): + logger.Info("sending resp to chan aborted for context cancelled") + zap.Duration("totalBlock", time.Since(sendStart)) + return false + case ch <- resp: + return true + case <-ticker.C: + logger.Warn( + "sending resp to chan is blocked for a long time", + zap.Duration("totalBlock", time.Since(sendStart)), + ) + } + } +} + +func buildEventUpdate(req *triggerEventRequest, result api.PreSchedEventResult, nowFunc func() time.Time) *api.TimerUpdate { + var update api.TimerUpdate + update.EventStatus.Set(api.SchedEventTrigger) + update.EventID.Set(req.eventID) + update.EventStart.Set(nowFunc()) + update.EventData.Set(result.EventData) + update.CheckVersion.Set(req.timer.Version) + + eventExtra := api.EventExtra{ + EventWatermark: req.timer.Watermark, + } + + if manual := req.timer.ManualRequest; manual.IsManualRequesting() { + eventExtra.EventManualRequestID = manual.ManualRequestID + update.ManualRequest.Set(manual.SetProcessed(req.eventID)) + } + + update.EventExtra.Set(eventExtra) + return &update +} diff --git a/timer/runtime/worker_test.go b/pkg/timer/runtime/worker_test.go similarity index 99% rename from timer/runtime/worker_test.go rename to pkg/timer/runtime/worker_test.go index f9748d5fad913..0c2aef2079bd7 100644 --- a/timer/runtime/worker_test.go +++ b/pkg/timer/runtime/worker_test.go @@ -22,9 +22,9 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util" - mockutil "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util" + mockutil "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.uber.org/atomic" diff --git a/timer/store_intergartion_test.go b/pkg/timer/store_intergartion_test.go similarity index 99% rename from timer/store_intergartion_test.go rename to pkg/timer/store_intergartion_test.go index 146ce3ebefe8a..b68a6a2b847d3 100644 --- a/timer/store_intergartion_test.go +++ b/pkg/timer/store_intergartion_test.go @@ -23,12 +23,12 @@ import ( "github.com/google/uuid" "github.com/ngaut/pools" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/timer/runtime" - "github.com/pingcap/tidb/timer/tablestore" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/timer/runtime" + "github.com/pingcap/tidb/pkg/timer/tablestore" + "github.com/pingcap/tidb/pkg/util/timeutil" "github.com/stretchr/testify/require" "go.etcd.io/etcd/tests/v3/integration" ) diff --git a/pkg/timer/tablestore/BUILD.bazel b/pkg/timer/tablestore/BUILD.bazel new file mode 100644 index 0000000000000..9af2e0252410c --- /dev/null +++ b/pkg/timer/tablestore/BUILD.bazel @@ -0,0 +1,52 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tablestore", + srcs = [ + "notifier.go", + "sql.go", + "store.go", + ], + importpath = "github.com/pingcap/tidb/pkg/timer/tablestore", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/timer/api", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "//pkg/util/timeutil", + "@com_github_google_uuid//:uuid", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//util", + "@io_etcd_go_etcd_api_v3//mvccpb", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "tablestore_test", + timeout = "short", + srcs = ["sql_test.go"], + embed = [":tablestore"], + flaky = True, + race = "on", + shard_count = 8, + deps = [ + "//pkg/kv", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/timer/api", + "//pkg/util/sqlexec", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_stretchr_testify//mock", + "@com_github_stretchr_testify//require", + ], +) diff --git a/timer/tablestore/notifier.go b/pkg/timer/tablestore/notifier.go similarity index 98% rename from timer/tablestore/notifier.go rename to pkg/timer/tablestore/notifier.go index 2e9a4d98ed241..9fdd5f9df68e8 100644 --- a/timer/tablestore/notifier.go +++ b/pkg/timer/tablestore/notifier.go @@ -24,9 +24,9 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/logutil" "go.etcd.io/etcd/api/v3/mvccpb" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" diff --git a/pkg/timer/tablestore/sql.go b/pkg/timer/tablestore/sql.go new file mode 100644 index 0000000000000..681b20c317944 --- /dev/null +++ b/pkg/timer/tablestore/sql.go @@ -0,0 +1,484 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tablestore + +import ( + "encoding/json" + "fmt" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/timer/api" +) + +type timerExt struct { + Tags []string `json:"tags,omitempty"` + Manual *manualRequestObj `json:"manual,omitempty"` + Event *eventExtObj `json:"event,omitempty"` +} + +// CreateTimerTableSQL returns a SQL to create timer table +func CreateTimerTableSQL(dbName, tableName string) string { + return fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s ( + ID BIGINT(64) UNSIGNED NOT NULL AUTO_INCREMENT, + NAMESPACE VARCHAR(256) NOT NULL, + TIMER_KEY VARCHAR(256) NOT NULL, + TIMER_DATA BLOB, + TIMEZONE VARCHAR(64) NOT NULL, + SCHED_POLICY_TYPE VARCHAR(32) NOT NULL, + SCHED_POLICY_EXPR VARCHAR(256) NOT NULL, + HOOK_CLASS VARCHAR(64) NOT NULL, + WATERMARK TIMESTAMP DEFAULT NULL, + ENABLE TINYINT(2) NOT NULL, + TIMER_EXT JSON NOT NULL, + EVENT_STATUS VARCHAR(32) NOT NULL, + EVENT_ID VARCHAR(64) NOT NULL, + EVENT_DATA BLOB, + EVENT_START TIMESTAMP DEFAULT NULL, + SUMMARY_DATA BLOB, + CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + UPDATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + VERSION BIGINT(64) UNSIGNED NOT NULL, + PRIMARY KEY (ID), + UNIQUE KEY timer_key(NAMESPACE, TIMER_KEY), + KEY hook_class(HOOK_CLASS) + )`, indentString(dbName, tableName)) +} + +func indentString(dbName, tableName string) string { + return fmt.Sprintf("`%s`.`%s`", dbName, tableName) +} + +func buildInsertTimerSQL(dbName, tableName string, record *api.TimerRecord) (string, []any, error) { + var watermark, eventStart any + watermarkFormat, eventStartFormat := "%?", "%?" + if !record.Watermark.IsZero() { + watermark = record.Watermark.Unix() + watermarkFormat = "FROM_UNIXTIME(%?)" + } + + if !record.EventStart.IsZero() { + eventStart = record.EventStart.Unix() + eventStartFormat = "FROM_UNIXTIME(%?)" + } + + eventStatus := record.EventStatus + if eventStatus == "" { + eventStatus = api.SchedEventIdle + } + + ext := &timerExt{ + Tags: record.Tags, + Manual: newManualRequestObj(record.ManualRequest), + Event: newEventExtObj(record.EventExtra), + } + + extJSON, err := json.Marshal(ext) + if err != nil { + return "", nil, err + } + + sql := fmt.Sprintf("INSERT INTO %s ("+ + "NAMESPACE, "+ + "TIMER_KEY, "+ + "TIMER_DATA, "+ + "TIMEZONE, "+ + "SCHED_POLICY_TYPE, "+ + "SCHED_POLICY_EXPR, "+ + "HOOK_CLASS, "+ + "WATERMARK, "+ + "ENABLE, "+ + "TIMER_EXT, "+ + "EVENT_ID, "+ + "EVENT_STATUS, "+ + "EVENT_START, "+ + "EVENT_DATA, "+ + "SUMMARY_DATA, "+ + "VERSION) "+ + "VALUES (%%?, %%?, %%?, %%?, %%?, %%?, %%?, %s, %%?, JSON_MERGE_PATCH('{}', %%?), %%?, %%?, %s, %%?, %%?, 1)", + indentString(dbName, tableName), + watermarkFormat, + eventStartFormat, + ) + + return sql, []any{ + record.Namespace, + record.Key, + record.Data, + record.TimeZone, + string(record.SchedPolicyType), + record.SchedPolicyExpr, + record.HookClass, + watermark, + record.Enable, + json.RawMessage(extJSON), + record.EventID, + string(eventStatus), + eventStart, + record.EventData, + record.SummaryData, + }, nil +} + +func buildSelectTimerSQL(dbName, tableName string, cond api.Cond) (string, []any, error) { + criteria, args, err := buildCondCriteria(cond, make([]any, 0, 8)) + if err != nil { + return "", nil, err + } + + sql := fmt.Sprintf("SELECT "+ + "ID, "+ + "NAMESPACE, "+ + "TIMER_KEY, "+ + "TIMER_DATA, "+ + "TIMEZONE, "+ + "SCHED_POLICY_TYPE, "+ + "SCHED_POLICY_EXPR, "+ + "HOOK_CLASS, "+ + "WATERMARK, "+ + "ENABLE, "+ + "TIMER_EXT, "+ + "EVENT_STATUS, "+ + "EVENT_ID, "+ + "EVENT_DATA, "+ + "EVENT_START, "+ + "SUMMARY_DATA, "+ + "CREATE_TIME, "+ + "UPDATE_TIME, "+ + "VERSION "+ + "FROM %s WHERE %s", + indentString(dbName, tableName), + criteria, + ) + return sql, args, nil +} + +func buildCondCriteria(cond api.Cond, args []any) (criteria string, _ []any, err error) { + if cond == nil { + return "1", args, nil + } + + switch c := cond.(type) { + case *api.TimerCond: + criteria, args, err = buildTimerCondCriteria(c, args) + if err != nil { + return "", nil, err + } + return criteria, args, nil + case *api.Operator: + return buildOperatorCriteria(c, args) + default: + return "", nil, errors.Errorf("unsupported condition type: %T", cond) + } +} + +func buildTimerCondCriteria(cond *api.TimerCond, args []any) (string, []any, error) { + items := make([]string, 0, cap(args)-len(args)) + if val, ok := cond.ID.Get(); ok { + items = append(items, "ID = %?") + args = append(args, val) + } + + if val, ok := cond.Namespace.Get(); ok { + items = append(items, "NAMESPACE = %?") + args = append(args, val) + } + + if val, ok := cond.Key.Get(); ok { + if cond.KeyPrefix { + items = append(items, "TIMER_KEY LIKE %?") + args = append(args, val+"%") + } else { + items = append(items, "TIMER_KEY = %?") + args = append(args, val) + } + } + + if vals, ok := cond.Tags.Get(); ok && len(vals) > 0 { + bs, err := json.Marshal(vals) + if err != nil { + return "", nil, err + } + items = append(items, + "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL", + "JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", + ) + args = append(args, json.RawMessage(bs)) + } + + if len(items) == 0 { + return "1", args, nil + } + + return strings.Join(items, " AND "), args, nil +} + +func buildOperatorCriteria(op *api.Operator, args []any) (string, []any, error) { + if len(op.Children) == 0 { + return "", nil, errors.New("children should not be empty") + } + + var opStr string + switch op.Op { + case api.OperatorAnd: + opStr = "AND" + case api.OperatorOr: + opStr = "OR" + default: + return "", nil, errors.Errorf("unsupported operator: %v", op.Op) + } + + criteriaList := make([]string, 0, len(op.Children)) + for _, child := range op.Children { + var criteria string + var err error + criteria, args, err = buildCondCriteria(child, args) + if err != nil { + return "", nil, err + } + + if len(op.Children) > 1 && criteria != "1" && criteria != "0" { + criteria = fmt.Sprintf("(%s)", criteria) + } + + criteriaList = append(criteriaList, criteria) + } + + criteria := strings.Join(criteriaList, " "+opStr+" ") + if op.Not { + switch criteria { + case "0": + criteria = "1" + case "1": + criteria = "0" + default: + criteria = fmt.Sprintf("!(%s)", criteria) + } + } + return criteria, args, nil +} + +func buildUpdateTimerSQL(dbName, tblName string, timerID string, update *api.TimerUpdate) (string, []any, error) { + criteria, args, err := buildUpdateCriteria(update, make([]any, 0, 6)) + if err != nil { + return "", nil, err + } + + sql := fmt.Sprintf("UPDATE %s SET %s WHERE ID = %%?", indentString(dbName, tblName), criteria) + return sql, append(args, timerID), nil +} + +type manualRequestObj struct { + RequestID *string `json:"request_id"` + RequestTimeUnix *int64 `json:"request_time_unix"` + TimeoutSec *int64 `json:"timeout_sec"` + Processed *bool `json:"processed"` + EventID *string `json:"event_id"` +} + +func newManualRequestObj(manual api.ManualRequest) *manualRequestObj { + var empty api.ManualRequest + if manual == empty { + return nil + } + + obj := &manualRequestObj{} + if v := manual.ManualRequestID; v != "" { + obj.RequestID = &v + } + + if v := manual.ManualRequestTime; !v.IsZero() { + unix := v.Unix() + obj.RequestTimeUnix = &unix + } + + if v := manual.ManualTimeout; v != 0 { + sec := int64(v / time.Second) + obj.TimeoutSec = &sec + } + + if v := manual.ManualProcessed; v { + processed := true + obj.Processed = &processed + } + + if v := manual.ManualEventID; v != "" { + obj.EventID = &v + } + + return obj +} + +func (o *manualRequestObj) ToManualRequest() (r api.ManualRequest) { + if o == nil { + return + } + + if v := o.RequestID; v != nil { + r.ManualRequestID = *v + } + + if v := o.RequestTimeUnix; v != nil { + r.ManualRequestTime = time.Unix(*v, 0) + } + + if v := o.TimeoutSec; v != nil { + r.ManualTimeout = time.Duration(*v) * time.Second + } + + if v := o.Processed; v != nil { + r.ManualProcessed = *v + } + + if v := o.EventID; v != nil { + r.ManualEventID = *v + } + + return r +} + +type eventExtObj struct { + ManualRequestID *string `json:"manual_request_id"` + WatermarkUnix *int64 `json:"watermark_unix"` +} + +func newEventExtObj(e api.EventExtra) *eventExtObj { + var empty api.EventExtra + if e == empty { + return nil + } + + obj := &eventExtObj{} + if v := e.EventManualRequestID; v != "" { + obj.ManualRequestID = &v + } + + if v := e.EventWatermark; !v.IsZero() { + unix := v.Unix() + obj.WatermarkUnix = &unix + } + + return obj +} + +func (o *eventExtObj) ToEventExtra() (e api.EventExtra) { + if o == nil { + return + } + + if v := o.ManualRequestID; v != nil { + e.EventManualRequestID = *v + } + + if v := o.WatermarkUnix; v != nil { + e.EventWatermark = time.Unix(*o.WatermarkUnix, 0) + } + + return +} + +func buildUpdateCriteria(update *api.TimerUpdate, args []any) (string, []any, error) { + updateFields := make([]string, 0, cap(args)-len(args)) + if val, ok := update.Enable.Get(); ok { + updateFields = append(updateFields, "ENABLE = %?") + args = append(args, val) + } + + extFields := make(map[string]any) + if val, ok := update.Tags.Get(); ok { + if len(val) == 0 { + val = nil + } + extFields["tags"] = val + } + + if val, ok := update.ManualRequest.Get(); ok { + extFields["manual"] = newManualRequestObj(val) + } + + if val, ok := update.EventExtra.Get(); ok { + extFields["event"] = newEventExtObj(val) + } + + if val, ok := update.TimeZone.Get(); ok { + updateFields = append(updateFields, "TIMEZONE = %?") + args = append(args, val) + } + + if val, ok := update.SchedPolicyType.Get(); ok { + updateFields = append(updateFields, "SCHED_POLICY_TYPE = %?") + args = append(args, string(val)) + } + + if val, ok := update.SchedPolicyExpr.Get(); ok { + updateFields = append(updateFields, "SCHED_POLICY_EXPR = %?") + args = append(args, val) + } + + if val, ok := update.EventStatus.Get(); ok { + updateFields = append(updateFields, "EVENT_STATUS = %?") + args = append(args, string(val)) + } + + if val, ok := update.EventID.Get(); ok { + updateFields = append(updateFields, "EVENT_ID = %?") + args = append(args, val) + } + + if val, ok := update.EventData.Get(); ok { + updateFields = append(updateFields, "EVENT_DATA = %?") + args = append(args, val) + } + + if val, ok := update.EventStart.Get(); ok { + if val.IsZero() { + updateFields = append(updateFields, "EVENT_START = NULL") + } else { + updateFields = append(updateFields, "EVENT_START = FROM_UNIXTIME(%?)") + args = append(args, val.Unix()) + } + } + + if val, ok := update.Watermark.Get(); ok { + if val.IsZero() { + updateFields = append(updateFields, "WATERMARK = NULL") + } else { + updateFields = append(updateFields, "WATERMARK = FROM_UNIXTIME(%?)") + args = append(args, val.Unix()) + } + } + + if val, ok := update.SummaryData.Get(); ok { + updateFields = append(updateFields, "SUMMARY_DATA = %?") + args = append(args, val) + } + + if len(extFields) > 0 { + jsonBytes, err := json.Marshal(extFields) + if err != nil { + return "", nil, err + } + updateFields = append(updateFields, "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?)") + args = append(args, json.RawMessage(jsonBytes)) + } + + updateFields = append(updateFields, "VERSION = VERSION + 1") + return strings.Join(updateFields, ", "), args, nil +} + +func buildDeleteTimerSQL(dbName, tblName string, timerID string) (string, []any) { + return fmt.Sprintf("DELETE FROM %s WHERE ID = %%?", indentString(dbName, tblName)), []any{timerID} +} diff --git a/pkg/timer/tablestore/sql_test.go b/pkg/timer/tablestore/sql_test.go new file mode 100644 index 0000000000000..7e923c8272f86 --- /dev/null +++ b/pkg/timer/tablestore/sql_test.go @@ -0,0 +1,715 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tablestore + +import ( + "context" + "encoding/json" + "strings" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestBuildInsertTimerSQL(t *testing.T) { + now := time.Now() + sql1 := "INSERT INTO `db1`.`t1` (NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + + "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_ID, EVENT_STATUS, EVENT_START, EVENT_DATA, SUMMARY_DATA, VERSION) " + + "VALUES (%?, %?, %?, %?, %?, %?, %?, FROM_UNIXTIME(%?), %?, JSON_MERGE_PATCH('{}', %?), %?, %?, FROM_UNIXTIME(%?), %?, %?, 1)" + sql2 := "INSERT INTO `db1`.`t1` (NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + + "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_ID, EVENT_STATUS, EVENT_START, EVENT_DATA, SUMMARY_DATA, VERSION) " + + "VALUES (%?, %?, %?, %?, %?, %?, %?, %?, %?, JSON_MERGE_PATCH('{}', %?), %?, %?, %?, %?, %?, 1)" + + cases := []struct { + sql string + record *api.TimerRecord + args []any + }{ + { + sql: sql1, + record: &api.TimerRecord{ + TimerSpec: api.TimerSpec{ + Namespace: "n1", + Key: "k1", + Data: []byte("data1"), + TimeZone: "Asia/Shanghai", + SchedPolicyType: api.SchedEventInterval, + SchedPolicyExpr: "1h", + HookClass: "h1", + Watermark: now, + Enable: true, + Tags: []string{"l1", "l2"}, + }, + ManualRequest: api.ManualRequest{ + ManualRequestID: "req1", + ManualRequestTime: time.Unix(123, 0), + ManualTimeout: time.Minute, + ManualProcessed: true, + ManualEventID: "event1", + }, + EventExtra: api.EventExtra{ + EventManualRequestID: "req1", + EventWatermark: time.Unix(456, 0), + }, + EventID: "e1", + EventStatus: api.SchedEventTrigger, + EventStart: now.Add(time.Second), + EventData: []byte("event1"), + SummaryData: []byte("summary1"), + }, + args: []any{ + "n1", "k1", []byte("data1"), "Asia/Shanghai", "INTERVAL", "1h", "h1", now.Unix(), + true, json.RawMessage(`{"tags":["l1","l2"],` + + `"manual":{"request_id":"req1","request_time_unix":123,"timeout_sec":60,"processed":true,"event_id":"event1"},` + + `"event":{"manual_request_id":"req1","watermark_unix":456}}`), + "e1", "TRIGGER", now.Unix() + 1, []byte("event1"), []byte("summary1"), + }, + }, + { + sql: sql2, + record: &api.TimerRecord{ + TimerSpec: api.TimerSpec{ + Namespace: "n1", + Key: "k1", + SchedPolicyType: api.SchedEventInterval, + SchedPolicyExpr: "1h", + }, + }, + args: []any{ + "n1", "k1", []byte(nil), "", "INTERVAL", "1h", "", nil, + false, json.RawMessage("{}"), "", "IDLE", nil, []byte(nil), []byte(nil), + }, + }, + } + + for _, c := range cases { + require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) + sql, args, err := buildInsertTimerSQL("db1", "t1", c.record) + require.NoError(t, err) + require.Equal(t, c.sql, sql) + require.Equal(t, c.args, args) + } +} + +func TestBuildCondCriteria(t *testing.T) { + cases := []struct { + cond api.Cond + criteria string + args []any + }{ + { + cond: nil, + criteria: "1", + args: []any{}, + }, + { + cond: &api.TimerCond{}, + criteria: "1", + args: []any{}, + }, + { + cond: &api.TimerCond{ + ID: api.NewOptionalVal("1"), + }, + criteria: "ID = %?", + args: []any{"1"}, + }, + { + cond: &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + }, + criteria: "NAMESPACE = %?", + args: []any{"ns1"}, + }, + { + cond: &api.TimerCond{ + Key: api.NewOptionalVal("key1"), + }, + criteria: "TIMER_KEY = %?", + args: []any{"key1"}, + }, + { + cond: &api.TimerCond{ + Key: api.NewOptionalVal("key1"), + KeyPrefix: true, + }, + criteria: "TIMER_KEY LIKE %?", + args: []any{"key1%"}, + }, + { + cond: &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }, + criteria: "NAMESPACE = %? AND TIMER_KEY = %?", + args: []any{"ns1", "key1"}, + }, + { + cond: &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + KeyPrefix: true, + }, + criteria: "NAMESPACE = %? AND TIMER_KEY LIKE %?", + args: []any{"ns1", "key1%"}, + }, + { + cond: &api.TimerCond{ + Tags: api.NewOptionalVal([]string{}), + }, + criteria: "1", + args: []any{}, + }, + { + cond: &api.TimerCond{ + Tags: api.NewOptionalVal([]string{"l1"}), + }, + criteria: "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL AND JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", + args: []any{json.RawMessage(`["l1"]`)}, + }, + { + cond: &api.TimerCond{ + Tags: api.NewOptionalVal([]string{"l1", "l2"}), + }, + criteria: "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL AND JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", + args: []any{json.RawMessage(`["l1","l2"]`)}, + }, + { + cond: api.And( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "(NAMESPACE = %? AND TIMER_KEY = %?) AND (ID = %?)", + args: []any{"ns1", "key1", "2"}, + }, + { + cond: api.And( + &api.TimerCond{}, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "1 AND (ID = %?)", + args: []any{"2"}, + }, + { + cond: api.And( + api.Not(&api.TimerCond{}), + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "0 AND (ID = %?)", + args: []any{"2"}, + }, + { + cond: api.And( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + }, + &api.TimerCond{}, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "(NAMESPACE = %?) AND 1 AND (ID = %?)", + args: []any{"ns1", "2"}, + }, + { + cond: api.Not(api.And( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + )), + criteria: "!((NAMESPACE = %? AND TIMER_KEY = %?) AND (ID = %?))", + args: []any{"ns1", "key1", "2"}, + }, + { + cond: api.Or( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "(NAMESPACE = %? AND TIMER_KEY = %?) OR (ID = %?)", + args: []any{"ns1", "key1", "2"}, + }, + { + cond: api.Not(api.Or( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + )), + criteria: "!((NAMESPACE = %? AND TIMER_KEY = %?) OR (ID = %?))", + args: []any{"ns1", "key1", "2"}, + }, + { + cond: api.Or( + &api.TimerCond{}, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "1 OR (ID = %?)", + args: []any{"2"}, + }, + { + cond: api.Or( + &api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + }, + &api.TimerCond{}, + &api.TimerCond{ + ID: api.NewOptionalVal("2"), + }, + ), + criteria: "(NAMESPACE = %?) OR 1 OR (ID = %?)", + args: []any{"ns1", "2"}, + }, + { + cond: api.Not(&api.TimerCond{ + ID: api.NewOptionalVal("3"), + }), + criteria: "!(ID = %?)", + args: []any{"3"}, + }, + { + cond: api.Not(&api.TimerCond{}), + criteria: "0", + args: []any{}, + }, + { + cond: api.Not(api.Not(&api.TimerCond{})), + criteria: "1", + args: []any{}, + }, + { + cond: api.Not(&api.TimerCond{ + Namespace: api.NewOptionalVal("ns1"), + Key: api.NewOptionalVal("key1"), + }), + criteria: "!(NAMESPACE = %? AND TIMER_KEY = %?)", + args: []any{"ns1", "key1"}, + }, + } + + for _, c := range cases { + require.Equal(t, strings.Count(c.criteria, "%?"), len(c.args)) + args := make([]any, 0) + criteria, args, err := buildCondCriteria(c.cond, args) + require.NoError(t, err) + require.Equal(t, c.criteria, criteria) + require.Equal(t, c.args, args) + + args = []any{"a", "b"} + criteria, args, err = buildCondCriteria(c.cond, args) + require.NoError(t, err) + require.Equal(t, c.criteria, criteria) + require.Equal(t, append([]any{"a", "b"}, c.args...), args) + } +} + +func TestBuildSelectTimerSQL(t *testing.T) { + prefix := "SELECT " + + "ID, NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + + "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_STATUS, EVENT_ID, EVENT_DATA, EVENT_START, SUMMARY_DATA, " + + "CREATE_TIME, UPDATE_TIME, VERSION FROM `db1`.`t1`" + + cases := []struct { + cond api.Cond + sql string + args []any + }{ + { + cond: nil, + sql: prefix + " WHERE 1", + args: []any{}, + }, + { + cond: &api.TimerCond{ID: api.NewOptionalVal("2")}, + sql: prefix + " WHERE ID = %?", + args: []any{"2"}, + }, + { + cond: &api.TimerCond{Namespace: api.NewOptionalVal("ns1"), Key: api.NewOptionalVal("key1")}, + sql: prefix + " WHERE NAMESPACE = %? AND TIMER_KEY = %?", + args: []any{"ns1", "key1"}, + }, + { + cond: api.Or( + &api.TimerCond{ID: api.NewOptionalVal("3")}, + &api.TimerCond{Namespace: api.NewOptionalVal("ns1")}, + ), + sql: prefix + " WHERE (ID = %?) OR (NAMESPACE = %?)", + args: []any{"3", "ns1"}, + }, + } + + for _, c := range cases { + require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) + sql, args, err := buildSelectTimerSQL("db1", "t1", c.cond) + require.NoError(t, err) + require.Equal(t, c.sql, sql) + require.Equal(t, c.args, args) + } +} + +func TestBuildUpdateCriteria(t *testing.T) { + now := time.Now() + var zeroTime time.Time + cases := []struct { + update *api.TimerUpdate + criteria string + args []any + }{ + { + update: &api.TimerUpdate{}, + criteria: "VERSION = VERSION + 1", + args: []any{}, + }, + { + update: &api.TimerUpdate{ + Enable: api.NewOptionalVal(true), + }, + criteria: "ENABLE = %?, VERSION = VERSION + 1", + args: []any{true}, + }, + { + update: &api.TimerUpdate{ + Enable: api.NewOptionalVal(false), + Tags: api.NewOptionalVal([]string{"l1", "l2"}), + TimeZone: api.NewOptionalVal("Asia/Shanghai"), + SchedPolicyType: api.NewOptionalVal(api.SchedEventInterval), + SchedPolicyExpr: api.NewOptionalVal("1h"), + ManualRequest: api.NewOptionalVal(api.ManualRequest{ + ManualRequestID: "req1", + ManualRequestTime: time.Unix(123, 0), + ManualTimeout: time.Minute, + ManualProcessed: true, + ManualEventID: "event1", + }), + EventStatus: api.NewOptionalVal(api.SchedEventTrigger), + EventID: api.NewOptionalVal("event1"), + EventData: api.NewOptionalVal([]byte("data1")), + EventStart: api.NewOptionalVal(now), + EventExtra: api.NewOptionalVal(api.EventExtra{ + EventManualRequestID: "req2", + EventWatermark: time.Unix(456, 0), + }), + Watermark: api.NewOptionalVal(now.Add(time.Second)), + SummaryData: api.NewOptionalVal([]byte("summary")), + CheckEventID: api.NewOptionalVal("ee"), + CheckVersion: api.NewOptionalVal(uint64(1)), + }, + criteria: "ENABLE = %?, TIMEZONE = %?, SCHED_POLICY_TYPE = %?, SCHED_POLICY_EXPR = %?, EVENT_STATUS = %?, " + + "EVENT_ID = %?, EVENT_DATA = %?, EVENT_START = FROM_UNIXTIME(%?), " + + "WATERMARK = FROM_UNIXTIME(%?), SUMMARY_DATA = %?, " + + "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), " + + "VERSION = VERSION + 1", + args: []any{ + false, "Asia/Shanghai", "INTERVAL", "1h", "TRIGGER", "event1", []byte("data1"), now.Unix(), + now.Unix() + 1, []byte("summary"), + json.RawMessage(`{` + + `"event":{"manual_request_id":"req2","watermark_unix":456},` + + `"manual":{"request_id":"req1","request_time_unix":123,"timeout_sec":60,"processed":true,"event_id":"event1"},` + + `"tags":["l1","l2"]` + + `}`), + }, + }, + { + update: &api.TimerUpdate{ + EventExtra: api.NewOptionalVal(api.EventExtra{EventManualRequestID: "req1"}), + ManualRequest: api.NewOptionalVal(api.ManualRequest{ManualRequestID: "req2"}), + }, + criteria: "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), VERSION = VERSION + 1", + args: []any{json.RawMessage(`{` + + `"event":{"manual_request_id":"req1","watermark_unix":null},` + + `"manual":{"request_id":"req2","request_time_unix":null,"timeout_sec":null,"processed":null,"event_id":null}` + + `}`)}, + }, + { + update: &api.TimerUpdate{ + EventExtra: api.NewOptionalVal(api.EventExtra{EventWatermark: time.Unix(123, 0)}), + ManualRequest: api.NewOptionalVal(api.ManualRequest{ManualRequestTime: time.Unix(456, 0)}), + }, + criteria: "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), VERSION = VERSION + 1", + args: []any{json.RawMessage(`{` + + `"event":{"manual_request_id":null,"watermark_unix":123},` + + `"manual":{"request_id":null,"request_time_unix":456,"timeout_sec":null,"processed":null,"event_id":null}` + + `}`)}, + }, + { + update: &api.TimerUpdate{ + TimeZone: api.NewOptionalVal(""), + SchedPolicyExpr: api.NewOptionalVal(""), + EventID: api.NewOptionalVal(""), + EventData: api.NewOptionalVal([]byte(nil)), + EventStart: api.NewOptionalVal(zeroTime), + EventExtra: api.NewOptionalVal(api.EventExtra{}), + ManualRequest: api.NewOptionalVal(api.ManualRequest{}), + Watermark: api.NewOptionalVal(zeroTime), + SummaryData: api.NewOptionalVal([]byte(nil)), + Tags: api.NewOptionalVal([]string(nil)), + }, + criteria: "TIMEZONE = %?, SCHED_POLICY_EXPR = %?, EVENT_ID = %?, EVENT_DATA = %?, " + + "EVENT_START = NULL, WATERMARK = NULL, SUMMARY_DATA = %?, " + + "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), " + + "VERSION = VERSION + 1", + args: []any{"", "", "", []byte(nil), []byte(nil), json.RawMessage(`{"event":null,"manual":null,"tags":null}`)}, + }, + { + update: &api.TimerUpdate{ + CheckEventID: api.NewOptionalVal("ee"), + CheckVersion: api.NewOptionalVal(uint64(1)), + }, + criteria: "VERSION = VERSION + 1", + args: []any{}, + }, + } + + for _, c := range cases { + require.Equal(t, strings.Count(c.criteria, "%?"), len(c.args)) + criteria, args, err := buildUpdateCriteria(c.update, []any{}) + require.NoError(t, err) + require.Equal(t, c.criteria, criteria) + require.Equal(t, c.args, args) + + criteria, args, err = buildUpdateCriteria(c.update, []any{1, "2", "3"}) + require.NoError(t, err) + require.Equal(t, c.criteria, criteria) + require.Equal(t, append([]any{1, "2", "3"}, c.args...), args) + } +} + +func TestBuildUpdateTimerSQL(t *testing.T) { + timerID := "123" + cases := []struct { + update *api.TimerUpdate + sql string + args []any + }{ + { + update: &api.TimerUpdate{}, + sql: "UPDATE `db1`.`tbl1` SET VERSION = VERSION + 1 WHERE ID = %?", + args: []any{timerID}, + }, + { + update: &api.TimerUpdate{ + SchedPolicyType: api.NewOptionalVal(api.SchedEventInterval), + SchedPolicyExpr: api.NewOptionalVal("1h"), + }, + sql: "UPDATE `db1`.`tbl1` SET SCHED_POLICY_TYPE = %?, SCHED_POLICY_EXPR = %?, VERSION = VERSION + 1 WHERE ID = %?", + args: []any{"INTERVAL", "1h", timerID}, + }, + } + + for _, c := range cases { + require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) + sql, args, err := buildUpdateTimerSQL("db1", "tbl1", timerID, c.update) + require.NoError(t, err) + require.Equal(t, c.sql, sql) + require.Equal(t, c.args, args) + } +} + +func TestBuildDeleteTimerSQL(t *testing.T) { + sql, args := buildDeleteTimerSQL("db1", "tbl1", "123") + require.Equal(t, "DELETE FROM `db1`.`tbl1` WHERE ID = %?", sql) + require.Equal(t, []any{"123"}, args) +} + +type mockSessionPool struct { + mock.Mock +} + +func (p *mockSessionPool) Get() (resource pools.Resource, _ error) { + ret := p.Called() + if r := ret.Get(0); r != nil { + resource = r.(pools.Resource) + } + return resource, ret.Error(1) +} + +func (p *mockSessionPool) Put(r pools.Resource) { + p.Called(r) +} + +type mockSession struct { + mock.Mock + sessionctx.Context + sqlexec.SQLExecutor +} + +func (p *mockSession) ExecuteInternal(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, _ error) { + ret := p.Called(ctx, sql, args) + if r := ret.Get(0); r != nil { + rs = r.(sqlexec.RecordSet) + } + return rs, ret.Error(1) +} + +func (p *mockSession) GetSessionVars() *variable.SessionVars { + return p.Context.GetSessionVars() +} + +func (p *mockSession) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { + p.Context.SetDiskFullOpt(level) +} + +func (p *mockSession) Close() { + p.Called() +} + +var matchCtx = mock.MatchedBy(func(ctx context.Context) bool { + return kv.GetInternalSourceType(ctx) == kv.InternalTimer +}) + +func TestTakeSession(t *testing.T) { + pool := &mockSessionPool{} + core := tableTimerStoreCore{pool: pool} + + // Get returns error + pool.On("Get").Return(nil, errors.New("mockErr")).Once() + r, back, err := core.takeSession() + require.Nil(t, r) + require.Nil(t, back) + require.EqualError(t, err, "mockErr") + pool.AssertExpectations(t) + + // Get returns a session + se := &mockSession{} + pool.On("Get").Return(se, nil).Once() + r, back, err = core.takeSession() + require.Equal(t, r, se) + require.NotNil(t, back) + require.Nil(t, err) + pool.AssertExpectations(t) + se.AssertExpectations(t) + + // Put session failed + se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). + Return(nil, errors.New("mockErr")). + Once() + se.On("Close").Once() + back() + pool.AssertExpectations(t) + se.AssertExpectations(t) + + // Put session success + pool.On("Get").Return(se, nil).Once() + r, back, err = core.takeSession() + require.Equal(t, r, se) + require.NotNil(t, back) + require.Nil(t, err) + se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). + Return(nil, nil). + Once() + pool.On("Put", se).Once() + back() + pool.AssertExpectations(t) + se.AssertExpectations(t) +} + +func TestRunInTxn(t *testing.T) { + se := &mockSession{} + + // success + se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). + Return(nil, nil). + Once() + se.On("ExecuteInternal", matchCtx, mock.MatchedBy(func(sql string) bool { + return strings.HasPrefix(sql, "insert") + }), mock.Anything). + Return(nil, nil). + Once() + se.On("ExecuteInternal", matchCtx, "COMMIT", []interface{}(nil)). + Return(nil, nil). + Once() + require.Nil(t, runInTxn(context.Background(), se, func() error { + _, err := executeSQL(context.Background(), se, "insert into t value(?)", 1) + return err + })) + se.AssertExpectations(t) + + // start txn failed + se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). + Return(nil, errors.New("mockBeginErr")). + Once() + err := runInTxn(context.Background(), se, func() error { return nil }) + require.EqualError(t, err, "mockBeginErr") + se.AssertExpectations(t) + + // exec failed, rollback success + se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). + Return(nil, nil). + Once() + se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). + Return(nil, nil). + Once() + err = runInTxn(context.Background(), se, func() error { return errors.New("mockFuncErr") }) + require.EqualError(t, err, "mockFuncErr") + se.AssertExpectations(t) + + // commit failed + se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). + Return(nil, nil). + Once() + se.On("ExecuteInternal", matchCtx, "COMMIT", []interface{}(nil)). + Return(nil, errors.New("commitErr")). + Once() + se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). + Return(nil, nil). + Once() + err = runInTxn(context.Background(), se, func() error { return nil }) + require.EqualError(t, err, "commitErr") + se.AssertExpectations(t) + + // rollback failed + se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). + Return(nil, nil). + Once() + se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). + Return(nil, errors.New("rollbackErr")). + Once() + err = runInTxn(context.Background(), se, func() error { return errors.New("mockFuncErr") }) + require.EqualError(t, err, "mockFuncErr") + se.AssertExpectations(t) +} diff --git a/pkg/timer/tablestore/store.go b/pkg/timer/tablestore/store.go new file mode 100644 index 0000000000000..988c23feb0961 --- /dev/null +++ b/pkg/timer/tablestore/store.go @@ -0,0 +1,435 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tablestore + +import ( + "context" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/timeutil" + "github.com/tikv/client-go/v2/util" + clientv3 "go.etcd.io/etcd/client/v3" +) + +type sessionPool interface { + Get() (pools.Resource, error) + Put(pools.Resource) +} + +type tableTimerStoreCore struct { + pool sessionPool + dbName string + tblName string + etcd *clientv3.Client + notifier api.TimerWatchEventNotifier +} + +// NewTableTimerStore create a new timer store based on table +func NewTableTimerStore(clusterID uint64, pool sessionPool, dbName, tblName string, etcd *clientv3.Client) *api.TimerStore { + var notifier api.TimerWatchEventNotifier + if etcd != nil { + notifier = NewEtcdNotifier(clusterID, etcd) + } else { + notifier = api.NewMemTimerWatchEventNotifier() + } + + return &api.TimerStore{ + TimerStoreCore: &tableTimerStoreCore{ + pool: pool, + dbName: dbName, + tblName: tblName, + notifier: notifier, + }, + } +} + +func (s *tableTimerStoreCore) Create(ctx context.Context, record *api.TimerRecord) (string, error) { + if record == nil { + return "", errors.New("timer should not be nil") + } + + if record.ID != "" { + return "", errors.New("ID should not be specified when create record") + } + + if record.Version != 0 { + return "", errors.New("Version should not be specified when create record") + } + + if !record.CreateTime.IsZero() { + return "", errors.New("CreateTime should not be specified when create record") + } + + if err := record.Validate(); err != nil { + return "", err + } + + sctx, back, err := s.takeSession() + if err != nil { + return "", err + } + defer back() + + sql, args, err := buildInsertTimerSQL(s.dbName, s.tblName, record) + if err != nil { + return "", err + } + + _, err = executeSQL(ctx, sctx, sql, args...) + if err != nil { + return "", err + } + + rows, err := executeSQL(ctx, sctx, "select @@last_insert_id") + if err != nil { + return "", err + } + + timerID := strconv.FormatUint(rows[0].GetUint64(0), 10) + s.notifier.Notify(api.WatchTimerEventCreate, timerID) + return timerID, nil +} + +func (s *tableTimerStoreCore) List(ctx context.Context, cond api.Cond) ([]*api.TimerRecord, error) { + sctx, back, err := s.takeSession() + if err != nil { + return nil, err + } + defer back() + + if sessVars := sctx.GetSessionVars(); sessVars.GetEnableIndexMerge() { + // Enable index merge is used to make sure filtering timers with tags quickly. + // Currently, we are using multi-value index to index tags for timers which requires index merge enabled. + // see: https://docs.pingcap.com/tidb/dev/choose-index#use-a-multi-valued-index + sessVars.SetEnableIndexMerge(true) + defer sessVars.SetEnableIndexMerge(false) + } + + seTZ := sctx.GetSessionVars().Location() + sql, args, err := buildSelectTimerSQL(s.dbName, s.tblName, cond) + if err != nil { + return nil, err + } + + rows, err := executeSQL(ctx, sctx, sql, args...) + if err != nil { + return nil, err + } + + timers := make([]*api.TimerRecord, 0, len(rows)) + for _, row := range rows { + var timerData []byte + if !row.IsNull(3) { + timerData = row.GetBytes(3) + } + + var watermark time.Time + if !row.IsNull(8) { + watermark, err = row.GetTime(8).GoTime(seTZ) + if err != nil { + return nil, err + } + } + + var ext timerExt + if !row.IsNull(10) { + extJSON := row.GetJSON(10).String() + if err = json.Unmarshal([]byte(extJSON), &ext); err != nil { + return nil, err + } + } + + var eventData []byte + if !row.IsNull(13) { + eventData = row.GetBytes(13) + } + + var eventStart time.Time + if !row.IsNull(14) { + eventStart, err = row.GetTime(14).GoTime(seTZ) + if err != nil { + return nil, err + } + } + + var summaryData []byte + if !row.IsNull(15) { + summaryData = row.GetBytes(15) + } + + var createTime time.Time + if !row.IsNull(16) { + createTime, err = row.GetTime(16).GoTime(seTZ) + if err != nil { + return nil, err + } + } + + timer := &api.TimerRecord{ + ID: strconv.FormatUint(row.GetUint64(0), 10), + TimerSpec: api.TimerSpec{ + Namespace: row.GetString(1), + Key: row.GetString(2), + Tags: ext.Tags, + Data: timerData, + TimeZone: row.GetString(4), + SchedPolicyType: api.SchedPolicyType(row.GetString(5)), + SchedPolicyExpr: row.GetString(6), + HookClass: row.GetString(7), + Watermark: watermark, + Enable: row.GetInt64(9) != 0, + }, + ManualRequest: ext.Manual.ToManualRequest(), + EventStatus: api.SchedEventStatus(row.GetString(11)), + EventID: row.GetString(12), + EventData: eventData, + EventStart: eventStart, + EventExtra: ext.Event.ToEventExtra(), + SummaryData: summaryData, + CreateTime: createTime, + Version: row.GetUint64(18), + } + + tz := timer.TimeZone + // handling value "TIDB" is for compatibility of version 7.3.0 + if tz == "" || strings.EqualFold(tz, "TIDB") { + if tz, err = sctx.GetSessionVars().GetGlobalSystemVar(ctx, variable.TimeZone); err != nil { + return nil, err + } + } + + loc, err := timeutil.ParseTimeZone(tz) + if err == nil { + timer.Location = loc + } else { + timer.Location = timeutil.SystemLocation() + } + + timers = append(timers, timer) + } + return timers, nil +} + +func (s *tableTimerStoreCore) Update(ctx context.Context, timerID string, update *api.TimerUpdate) error { + sctx, back, err := s.takeSession() + if err != nil { + return err + } + defer back() + + err = runInTxn(ctx, sctx, func() error { + /* #nosec G202: SQL string concatenation */ + getCheckColsSQL := fmt.Sprintf( + "SELECT EVENT_ID, VERSION, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR FROM %s WHERE ID=%%?", + indentString(s.dbName, s.tblName), + ) + + rows, err := executeSQL(ctx, sctx, getCheckColsSQL, timerID) + if err != nil { + return err + } + + if len(rows) == 0 { + return api.ErrTimerNotExist + } + + err = checkUpdateConstraints( + update, + rows[0].GetString(0), + rows[0].GetUint64(1), + api.SchedPolicyType(rows[0].GetString(2)), + rows[0].GetString(3), + ) + + if err != nil { + return err + } + + updateSQL, args, err := buildUpdateTimerSQL(s.dbName, s.tblName, timerID, update) + if err != nil { + return err + } + + if _, err = executeSQL(ctx, sctx, updateSQL, args...); err != nil { + return err + } + + return nil + }) + + if err != nil { + return err + } + + s.notifier.Notify(api.WatchTimerEventUpdate, timerID) + return nil +} + +func (s *tableTimerStoreCore) Delete(ctx context.Context, timerID string) (bool, error) { + sctx, back, err := s.takeSession() + if err != nil { + return false, err + } + defer back() + + deleteSQL, args := buildDeleteTimerSQL(s.dbName, s.tblName, timerID) + _, err = executeSQL(ctx, sctx, deleteSQL, args...) + if err != nil { + return false, err + } + + rows, err := executeSQL(ctx, sctx, "SELECT ROW_COUNT()") + if err != nil { + return false, err + } + + exist := rows[0].GetInt64(0) > 0 + if exist { + s.notifier.Notify(api.WatchTimerEventDelete, timerID) + } + return exist, nil +} + +func (*tableTimerStoreCore) WatchSupported() bool { + return true +} + +func (s *tableTimerStoreCore) Watch(ctx context.Context) api.WatchTimerChan { + return s.notifier.Watch(ctx) +} + +func (s *tableTimerStoreCore) Close() { + s.notifier.Close() +} + +func (s *tableTimerStoreCore) takeSession() (sessionctx.Context, func(), error) { + r, err := s.pool.Get() + if err != nil { + return nil, nil, err + } + + sctx, ok := r.(sessionctx.Context) + if !ok { + s.pool.Put(r) + return nil, nil, errors.New("session is not the type sessionctx.Context") + } + + back := func() { + if _, err = executeSQL(context.Background(), sctx, "ROLLBACK"); err != nil { + // Though this branch is rarely to be called because "ROLLBACK" will always be successfully, we still need + // to handle it here to make sure the code is strong. + terror.Log(err) + // call `r.Close()` to make sure the resource is released to avoid memory leak + r.Close() + return + } + s.pool.Put(r) + } + + return sctx, back, nil +} + +func checkUpdateConstraints(update *api.TimerUpdate, eventID string, version uint64, policy api.SchedPolicyType, expr string) error { + if val, ok := update.CheckEventID.Get(); ok && eventID != val { + return api.ErrEventIDNotMatch + } + + if val, ok := update.CheckVersion.Get(); ok && version != val { + return api.ErrVersionNotMatch + } + + if val, ok := update.TimeZone.Get(); ok { + if err := api.ValidateTimeZone(val); err != nil { + return err + } + } + + checkPolicy := false + if val, ok := update.SchedPolicyType.Get(); ok { + checkPolicy = true + policy = val + } + + if val, ok := update.SchedPolicyExpr.Get(); ok { + checkPolicy = true + expr = val + } + + if checkPolicy { + if _, err := api.CreateSchedEventPolicy(policy, expr); err != nil { + return errors.Wrap(err, "schedule event configuration is not valid") + } + } + + return nil +} + +func executeSQL(ctx context.Context, sctx sessionctx.Context, sql string, args ...any) ([]chunk.Row, error) { + ctx = util.WithInternalSourceType(ctx, kv.InternalTimer) + sqlExec, ok := sctx.(sqlexec.SQLExecutor) + if !ok { + return nil, errors.New("session is not the type of SQLExecutor") + } + + rs, err := sqlExec.ExecuteInternal(ctx, sql, args...) + if err != nil { + return nil, err + } + + if rs == nil { + return nil, nil + } + + defer terror.Call(rs.Close) + return sqlexec.DrainRecordSet(ctx, rs, 1) +} + +func runInTxn(ctx context.Context, sctx sessionctx.Context, fn func() error) error { + if _, err := executeSQL(ctx, sctx, "BEGIN PESSIMISTIC"); err != nil { + return err + } + + success := false + defer func() { + if !success { + _, err := executeSQL(ctx, sctx, "ROLLBACK") + terror.Log(err) + } + }() + + if err := fn(); err != nil { + return err + } + + if _, err := executeSQL(ctx, sctx, "COMMIT"); err != nil { + return err + } + + success = true + return nil +} diff --git a/pkg/ttl/cache/BUILD.bazel b/pkg/ttl/cache/BUILD.bazel new file mode 100644 index 0000000000000..839c53f1822b2 --- /dev/null +++ b/pkg/ttl/cache/BUILD.bazel @@ -0,0 +1,71 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cache", + srcs = [ + "base.go", + "infoschema.go", + "table.go", + "task.go", + "ttlstatus.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ttl/cache", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/ttl/session", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "cache_test", + timeout = "short", + srcs = [ + "base_test.go", + "infoschema_test.go", + "main_test.go", + "split_test.go", + "table_test.go", + "task_test.go", + "ttlstatus_test.go", + ], + embed = [":cache"], + flaky = True, + shard_count = 13, + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/server", + "//pkg/session", + "//pkg/store/helper", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/ttl/session", + "//pkg/types", + "//pkg/util/codec", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_pd_client//:client", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/ttl/cache/base.go b/pkg/ttl/cache/base.go similarity index 100% rename from ttl/cache/base.go rename to pkg/ttl/cache/base.go diff --git a/ttl/cache/base_test.go b/pkg/ttl/cache/base_test.go similarity index 100% rename from ttl/cache/base_test.go rename to pkg/ttl/cache/base_test.go diff --git a/pkg/ttl/cache/infoschema.go b/pkg/ttl/cache/infoschema.go new file mode 100644 index 0000000000000..fb0fb6efec31b --- /dev/null +++ b/pkg/ttl/cache/infoschema.go @@ -0,0 +1,112 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache + +import ( + "time" + + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// InfoSchemaCache is the cache for InfoSchema, it builds a map from physical table id to physical table information +type InfoSchemaCache struct { + baseCache + + schemaVer int64 + Tables map[int64]*PhysicalTable +} + +// NewInfoSchemaCache creates the cache for info schema +func NewInfoSchemaCache(updateInterval time.Duration) *InfoSchemaCache { + return &InfoSchemaCache{ + baseCache: newBaseCache(updateInterval), + Tables: make(map[int64]*PhysicalTable), + } +} + +// Update updates the info schema cache +func (isc *InfoSchemaCache) Update(se session.Session) error { + is := se.GetDomainInfoSchema().(infoschema.InfoSchema) + + if isc.schemaVer == is.SchemaMetaVersion() { + return nil + } + + newTables := make(map[int64]*PhysicalTable, len(isc.Tables)) + for _, db := range is.AllSchemas() { + for _, tbl := range is.SchemaTables(db.Name) { + tblInfo := tbl.Meta() + if tblInfo.TTLInfo == nil || !tblInfo.TTLInfo.Enable || tblInfo.State != model.StatePublic { + continue + } + + logger := logutil.BgLogger(). + With(zap.String("schema", db.Name.L), + zap.Int64("tableID", tblInfo.ID), zap.String("tableName", tblInfo.Name.L)) + + if tblInfo.Partition == nil { + ttlTable, err := isc.newTable(db.Name, tblInfo, nil) + if err != nil { + logger.Warn("fail to build info schema cache", zap.Error(err)) + continue + } + newTables[tblInfo.ID] = ttlTable + continue + } + + for _, par := range tblInfo.Partition.Definitions { + par := par + ttlTable, err := isc.newTable(db.Name, tblInfo, &par) + if err != nil { + logger.Warn("fail to build info schema cache", + zap.Int64("partitionID", par.ID), + zap.String("partition", par.Name.L), zap.Error(err)) + continue + } + newTables[par.ID] = ttlTable + } + } + } + + isc.schemaVer = is.SchemaMetaVersion() + isc.Tables = newTables + isc.updateTime = time.Now() + return nil +} + +func (isc *InfoSchemaCache) newTable(schema model.CIStr, tblInfo *model.TableInfo, + par *model.PartitionDefinition) (*PhysicalTable, error) { + id := tblInfo.ID + if par != nil { + id = par.ID + } + + if isc.Tables != nil { + ttlTable, ok := isc.Tables[id] + if ok && ttlTable.TableInfo == tblInfo { + return ttlTable, nil + } + } + + partitionName := model.NewCIStr("") + if par != nil { + partitionName = par.Name + } + return NewPhysicalTable(schema, tblInfo, partitionName) +} diff --git a/pkg/ttl/cache/infoschema_test.go b/pkg/ttl/cache/infoschema_test.go new file mode 100644 index 0000000000000..185117e90b9b4 --- /dev/null +++ b/pkg/ttl/cache/infoschema_test.go @@ -0,0 +1,73 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache_test + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/stretchr/testify/assert" +) + +func TestInfoSchemaCache(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + sv := server.CreateMockServer(t, store) + sv.SetDomain(dom) + defer sv.Close() + + conn := server.CreateMockConn(t, sv) + sctx := conn.Context().Session + tk := testkit.NewTestKitWithSession(t, store, sctx) + se := session.NewSession(sctx, sctx, func(_ session.Session) {}) + + isc := cache.NewInfoSchemaCache(time.Hour) + + // test should update + assert.True(t, isc.ShouldUpdate()) + assert.NoError(t, isc.Update(se)) + assert.False(t, isc.ShouldUpdate()) + + // test new tables are synced + assert.Equal(t, 0, len(isc.Tables)) + tk.MustExec("create table test.t(created_at datetime) ttl = created_at + INTERVAL 5 YEAR") + assert.NoError(t, isc.Update(se)) + assert.Equal(t, 1, len(isc.Tables)) + for _, table := range isc.Tables { + assert.Equal(t, "t", table.TableInfo.Name.L) + } + + // test new partitioned table are synced + tk.MustExec("drop table test.t") + tk.MustExec(`create table test.t(created_at datetime) + ttl = created_at + INTERVAL 5 YEAR + partition by range (YEAR(created_at)) ( + partition p0 values less than (1991), + partition p1 values less than (2000) + ) + `) + assert.NoError(t, isc.Update(se)) + assert.Equal(t, 2, len(isc.Tables)) + partitions := []string{} + for id, table := range isc.Tables { + assert.Equal(t, "t", table.TableInfo.Name.L) + assert.Equal(t, id, table.PartitionDef.ID) + partitions = append(partitions, table.PartitionDef.Name.L) + } + assert.ElementsMatch(t, []string{"p0", "p1"}, partitions) +} diff --git a/pkg/ttl/cache/main_test.go b/pkg/ttl/cache/main_test.go new file mode 100644 index 0000000000000..15ec7eaf7fe2b --- /dev/null +++ b/pkg/ttl/cache/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ttl/cache/split_test.go b/pkg/ttl/cache/split_test.go new file mode 100644 index 0000000000000..6f7e944e7a4a4 --- /dev/null +++ b/pkg/ttl/cache/split_test.go @@ -0,0 +1,826 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache_test + +import ( + "context" + "fmt" + "math" + "sort" + "testing" + + "github.com/pingcap/kvproto/pkg/metapb" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/stretchr/testify/require" + "github.com/tikv/client-go/v2/tikv" + pd "github.com/tikv/pd/client" +) + +func newMockRegion(regionID uint64, startKey []byte, endKey []byte) *pd.Region { + leader := &metapb.Peer{ + Id: regionID, + StoreId: 1, + Role: metapb.PeerRole_Voter, + } + + return &pd.Region{ + Meta: &metapb.Region{ + Id: regionID, + StartKey: startKey, + EndKey: endKey, + Peers: []*metapb.Peer{leader}, + }, + Leader: leader, + } +} + +type mockPDClient struct { + t *testing.T + pd.Client + regions []*pd.Region + regionsSorted bool +} + +func (c *mockPDClient) ScanRegions(_ context.Context, key, endKey []byte, limit int) ([]*pd.Region, error) { + if len(c.regions) == 0 { + return []*pd.Region{newMockRegion(1, []byte{}, []byte{0xFF, 0xFF})}, nil + } + + if !c.regionsSorted { + sort.Slice(c.regions, func(i, j int) bool { + return kv.Key(c.regions[i].Meta.StartKey).Cmp(c.regions[j].Meta.StartKey) < 0 + }) + c.regionsSorted = true + } + + regions := []*pd.Region{newMockRegion(1, []byte{}, c.regions[0].Meta.StartKey)} + regions = append(regions, c.regions...) + regions = append(regions, newMockRegion(2, c.regions[len(c.regions)-1].Meta.EndKey, []byte{0xFF, 0xFF, 0xFF})) + + result := make([]*pd.Region, 0) + for _, r := range regions { + if kv.Key(r.Meta.StartKey).Cmp(endKey) >= 0 { + continue + } + + if kv.Key(r.Meta.EndKey).Cmp(key) <= 0 { + continue + } + + if len(result) >= limit { + break + } + + result = append(result, r) + } + return result, nil +} + +func (c *mockPDClient) GetStore(_ context.Context, storeID uint64) (*metapb.Store, error) { + return &metapb.Store{ + Id: storeID, + Address: fmt.Sprintf("127.0.0.%d", storeID), + }, nil +} + +type mockTiKVStore struct { + t *testing.T + helper.Storage + pdClient *mockPDClient + cache *tikv.RegionCache + nextRegionID uint64 +} + +func newMockTiKVStore(t *testing.T) *mockTiKVStore { + pdClient := &mockPDClient{t: t} + s := &mockTiKVStore{ + t: t, + pdClient: pdClient, + cache: tikv.NewRegionCache(pdClient), + nextRegionID: 1000, + } + s.refreshCache() + t.Cleanup(func() { + s.cache.Close() + }) + return s +} + +func (s *mockTiKVStore) addRegionBeginWithTablePrefix(tableID int64, handle kv.Handle) *mockTiKVStore { + start := tablecodec.GenTablePrefix(tableID) + end := tablecodec.EncodeRowKeyWithHandle(tableID, handle) + return s.addRegion(start, end) +} + +func (s *mockTiKVStore) addRegionEndWithTablePrefix(handle kv.Handle, tableID int64) *mockTiKVStore { + start := tablecodec.EncodeRowKeyWithHandle(tableID, handle) + end := tablecodec.GenTablePrefix(tableID + 1) + return s.addRegion(start, end) +} + +func (s *mockTiKVStore) addRegionWithTablePrefix(tableID int64, start kv.Handle, end kv.Handle) *mockTiKVStore { + startKey := tablecodec.EncodeRowKeyWithHandle(tableID, start) + endKey := tablecodec.EncodeRowKeyWithHandle(tableID, end) + return s.addRegion(startKey, endKey) +} + +func (s *mockTiKVStore) addRegion(key, endKey []byte) *mockTiKVStore { + require.True(s.t, kv.Key(endKey).Cmp(key) > 0) + if len(s.pdClient.regions) > 0 { + lastRegion := s.pdClient.regions[len(s.pdClient.regions)-1] + require.True(s.t, kv.Key(endKey).Cmp(lastRegion.Meta.EndKey) >= 0) + } + + regionID := s.nextRegionID + s.nextRegionID++ + leader := &metapb.Peer{ + Id: regionID, + StoreId: 1, + Role: metapb.PeerRole_Voter, + } + + s.pdClient.regions = append(s.pdClient.regions, &pd.Region{ + Meta: &metapb.Region{ + Id: regionID, + StartKey: key, + EndKey: endKey, + Peers: []*metapb.Peer{leader}, + }, + Leader: leader, + }) + + s.pdClient.regionsSorted = false + s.refreshCache() + return s +} + +func (s *mockTiKVStore) refreshCache() { + _, err := s.cache.LoadRegionsInKeyRange( + tikv.NewBackofferWithVars(context.Background(), 1000, nil), + []byte{}, + []byte{0xFF}, + ) + require.NoError(s.t, err) +} + +func (s *mockTiKVStore) batchAddIntHandleRegions(tblID int64, regionCnt, regionSize int, + offset int64) (end kv.IntHandle) { + for i := 0; i < regionCnt; i++ { + start := kv.IntHandle(offset + int64(i*regionSize)) + end = kv.IntHandle(start.IntValue() + int64(regionSize)) + s.addRegionWithTablePrefix(tblID, start, end) + } + return +} + +func (s *mockTiKVStore) clearRegions() { + s.pdClient.regions = nil + s.cache.Close() + s.cache = tikv.NewRegionCache(s.pdClient) + s.refreshCache() +} + +func (s *mockTiKVStore) GetRegionCache() *tikv.RegionCache { + return s.cache +} + +func bytesHandle(t *testing.T, data []byte) kv.Handle { + encoded, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(data)) + require.NoError(t, err) + h, err := kv.NewCommonHandle(encoded) + require.NoError(t, err) + return h +} + +func createTTLTable(t *testing.T, tk *testkit.TestKit, name string, option string) *cache.PhysicalTable { + if option == "" { + return createTTLTableWithSQL(t, tk, name, + fmt.Sprintf("create table test.%s(t timestamp) TTL = `t` + interval 1 day", name)) + } + + return createTTLTableWithSQL(t, tk, name, + fmt.Sprintf("create table test.%s(id %s primary key, t timestamp) TTL = `t` + interval 1 day", + name, option)) +} + +func create2PKTTLTable(t *testing.T, tk *testkit.TestKit, name string, option string) *cache.PhysicalTable { + return createTTLTableWithSQL(t, tk, name, + fmt.Sprintf( + "create table test.%s(id %s, id2 int, t timestamp, primary key(id, id2)) TTL = `t` + interval 1 day", + name, option)) +} + +func createTTLTableWithSQL(t *testing.T, tk *testkit.TestKit, name string, sql string) *cache.PhysicalTable { + tk.MustExec(sql) + is, ok := tk.Session().GetDomainInfoSchema().(infoschema.InfoSchema) + require.True(t, ok) + tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr(name)) + require.NoError(t, err) + ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tbl.Meta(), model.NewCIStr("")) + require.NoError(t, err) + return ttlTbl +} + +func checkRange(t *testing.T, r cache.ScanRange, start, end types.Datum) { + if start.IsNull() { + require.Nil(t, r.Start) + } else { + require.Equal(t, 1, len(r.Start)) + require.Equal(t, start.Kind(), r.Start[0].Kind()) + require.Equal(t, start.GetValue(), r.Start[0].GetValue()) + } + + if end.IsNull() { + require.Nil(t, r.End) + } else { + require.Equal(t, 1, len(r.End)) + require.Equal(t, end.Kind(), r.End[0].Kind()) + require.Equal(t, end.GetValue(), r.End[0].GetValue()) + } +} + +func TestSplitTTLScanRangesWithSignedInt(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tbls := []*cache.PhysicalTable{ + createTTLTable(t, tk, "t1", "tinyint"), + createTTLTable(t, tk, "t2", "smallint"), + createTTLTable(t, tk, "t3", "mediumint"), + createTTLTable(t, tk, "t4", "int"), + createTTLTable(t, tk, "t5", "bigint"), + createTTLTable(t, tk, "t6", ""), // no clustered + create2PKTTLTable(t, tk, "t7", "tinyint"), + } + + tikvStore := newMockTiKVStore(t) + for _, tbl := range tbls { + // test only one region + tikvStore.clearRegions() + ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test share regions with other table + tikvStore.clearRegions() + tikvStore.addRegion( + tablecodec.GenTablePrefix(tbl.ID-1), + tablecodec.GenTablePrefix(tbl.ID+1), + ) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test one table has multiple regions + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(0)) + end := tikvStore.batchAddIntHandleRegions(tbl.ID, 8, 100, 0) + tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 4, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.NewIntDatum(200)) + checkRange(t, ranges[1], types.NewIntDatum(200), types.NewIntDatum(500)) + checkRange(t, ranges[2], types.NewIntDatum(500), types.NewIntDatum(700)) + checkRange(t, ranges[3], types.NewIntDatum(700), types.Datum{}) + + // test one table has multiple regions and one table region across 0 + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-350)) + end = tikvStore.batchAddIntHandleRegions(tbl.ID, 8, 100, -350) + tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 5) + require.NoError(t, err) + require.Equal(t, 5, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.NewIntDatum(-250)) + checkRange(t, ranges[1], types.NewIntDatum(-250), types.NewIntDatum(-50)) + checkRange(t, ranges[2], types.NewIntDatum(-50), types.NewIntDatum(150)) + checkRange(t, ranges[3], types.NewIntDatum(150), types.NewIntDatum(350)) + checkRange(t, ranges[4], types.NewIntDatum(350), types.Datum{}) + } +} + +func TestSplitTTLScanRangesWithUnsignedInt(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tbls := []*cache.PhysicalTable{ + createTTLTable(t, tk, "t1", "tinyint unsigned"), + createTTLTable(t, tk, "t2", "smallint unsigned"), + createTTLTable(t, tk, "t3", "mediumint unsigned"), + createTTLTable(t, tk, "t4", "int unsigned"), + createTTLTable(t, tk, "t5", "bigint unsigned"), + create2PKTTLTable(t, tk, "t6", "tinyint unsigned"), + } + + tikvStore := newMockTiKVStore(t) + for _, tbl := range tbls { + // test only one region + tikvStore.clearRegions() + ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test share regions with other table + tikvStore.clearRegions() + tikvStore.addRegion( + tablecodec.GenTablePrefix(tbl.ID-1), + tablecodec.GenTablePrefix(tbl.ID+1), + ) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test one table has multiple regions: [MinInt64, a) [a, b) [b, 0) [0, c) [c, d) [d, MaxInt64] + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-200)) + end := tikvStore.batchAddIntHandleRegions(tbl.ID, 4, 100, -200) + tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 6) + require.NoError(t, err) + require.Equal(t, 6, len(ranges)) + checkRange(t, ranges[0], + types.NewUintDatum(uint64(math.MaxInt64)+1), types.NewUintDatum(uint64(math.MaxUint64)-199)) + checkRange(t, ranges[1], + types.NewUintDatum(uint64(math.MaxUint64)-199), types.NewUintDatum(uint64(math.MaxUint64)-99)) + checkRange(t, ranges[2], + types.NewUintDatum(uint64(math.MaxUint64)-99), types.Datum{}) + checkRange(t, ranges[3], + types.Datum{}, types.NewUintDatum(100)) + checkRange(t, ranges[4], + types.NewUintDatum(100), types.NewUintDatum(200)) + checkRange(t, ranges[5], + types.NewUintDatum(200), types.NewUintDatum(uint64(math.MaxInt64)+1)) + + // test one table has multiple regions: [MinInt64, a) [a, b) [b, c) [c, d) [d, MaxInt64], b < 0 < c + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-150)) + end = tikvStore.batchAddIntHandleRegions(tbl.ID, 3, 100, -150) + tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 5) + require.NoError(t, err) + require.Equal(t, 6, len(ranges)) + checkRange(t, ranges[0], + types.NewUintDatum(uint64(math.MaxInt64)+1), types.NewUintDatum(uint64(math.MaxUint64)-149)) + checkRange(t, ranges[1], + types.NewUintDatum(uint64(math.MaxUint64)-149), types.NewUintDatum(uint64(math.MaxUint64)-49)) + checkRange(t, ranges[2], + types.NewUintDatum(uint64(math.MaxUint64)-49), types.Datum{}) + checkRange(t, ranges[3], + types.Datum{}, types.NewUintDatum(50)) + checkRange(t, ranges[4], + types.NewUintDatum(50), types.NewUintDatum(150)) + checkRange(t, ranges[5], + types.NewUintDatum(150), types.NewUintDatum(uint64(math.MaxInt64)+1)) + } +} + +func TestSplitTTLScanRangesWithBytes(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tbls := []*cache.PhysicalTable{ + createTTLTable(t, tk, "t1", "binary(32)"), + createTTLTable(t, tk, "t2", "char(32) CHARACTER SET BINARY"), + createTTLTable(t, tk, "t3", "varchar(32) CHARACTER SET BINARY"), + createTTLTable(t, tk, "t4", "bit(32)"), + create2PKTTLTable(t, tk, "t5", "binary(32)"), + } + + tikvStore := newMockTiKVStore(t) + for _, tbl := range tbls { + // test only one region + tikvStore.clearRegions() + ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test share regions with other table + tikvStore.clearRegions() + tikvStore.addRegion( + tablecodec.GenTablePrefix(tbl.ID-1), + tablecodec.GenTablePrefix(tbl.ID+1), + ) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test one table has multiple regions + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3})) + tikvStore.addRegionWithTablePrefix( + tbl.ID, bytesHandle(t, []byte{1, 2, 3}), bytesHandle(t, []byte{1, 2, 3, 4})) + tikvStore.addRegionWithTablePrefix( + tbl.ID, bytesHandle(t, []byte{1, 2, 3, 4}), bytesHandle(t, []byte{1, 2, 3, 4, 5})) + tikvStore.addRegionWithTablePrefix( + tbl.ID, bytesHandle(t, []byte{1, 2, 3, 4, 5}), bytesHandle(t, []byte{1, 2, 4})) + tikvStore.addRegionWithTablePrefix( + tbl.ID, bytesHandle(t, []byte{1, 2, 4}), bytesHandle(t, []byte{1, 2, 5})) + tikvStore.addRegionEndWithTablePrefix(bytesHandle(t, []byte{1, 2, 5}), tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 4, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.NewBytesDatum([]byte{1, 2, 3, 4})) + checkRange(t, ranges[1], types.NewBytesDatum([]byte{1, 2, 3, 4}), types.NewBytesDatum([]byte{1, 2, 4})) + checkRange(t, ranges[2], types.NewBytesDatum([]byte{1, 2, 4}), types.NewBytesDatum([]byte{1, 2, 5})) + checkRange(t, ranges[3], types.NewBytesDatum([]byte{1, 2, 5}), types.Datum{}) + } +} + +func TestNoTTLSplitSupportTables(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + + tbls := []*cache.PhysicalTable{ + createTTLTable(t, tk, "t1", "char(32) CHARACTER SET UTF8MB4"), + createTTLTable(t, tk, "t2", "varchar(32) CHARACTER SET UTF8MB4"), + createTTLTable(t, tk, "t4", "decimal(32, 2)"), + create2PKTTLTable(t, tk, "t5", "char(32) CHARACTER SET UTF8MB4"), + } + + tikvStore := newMockTiKVStore(t) + for _, tbl := range tbls { + // test only one region + tikvStore.clearRegions() + ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test share regions with other table + tikvStore.clearRegions() + tikvStore.addRegion( + tablecodec.GenTablePrefix(tbl.ID-1), + tablecodec.GenTablePrefix(tbl.ID+1), + ) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + + // test one table has multiple regions + tikvStore.clearRegions() + tikvStore.addRegionBeginWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3})) + tikvStore.addRegionWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3}), bytesHandle(t, []byte{1, 2, 3, 4})) + tikvStore.addRegionEndWithTablePrefix(bytesHandle(t, []byte{1, 2, 3, 4}), tbl.ID) + ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 3) + require.NoError(t, err) + require.Equal(t, 1, len(ranges)) + checkRange(t, ranges[0], types.Datum{}, types.Datum{}) + } +} + +func TestGetNextBytesHandleDatum(t *testing.T) { + tblID := int64(7) + buildHandleBytes := func(data []byte) []byte { + handleBytes, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(data)) + require.NoError(t, err) + return handleBytes + } + + buildRowKey := func(handleBytes []byte) kv.Key { + return tablecodec.EncodeRowKey(tblID, handleBytes) + } + + buildBytesRowKey := func(data []byte) kv.Key { + return buildRowKey(buildHandleBytes(data)) + } + + binaryDataStartPos := len(tablecodec.GenTableRecordPrefix(tblID)) + 1 + cases := []struct { + key interface{} + result []byte + isNull bool + }{ + { + key: buildBytesRowKey([]byte{}), + result: []byte{}, + }, + { + key: buildBytesRowKey([]byte{1, 2, 3}), + result: []byte{1, 2, 3}, + }, + { + key: buildBytesRowKey([]byte{1, 2, 3, 0}), + result: []byte{1, 2, 3, 0}, + }, + { + key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}), + result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}), + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + { + key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0}, + }, + { + key: append(buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), 0), + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0}, + }, + { + key: append(buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), 1), + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0}, + }, + { + key: []byte{}, + result: []byte{}, + }, + { + key: tablecodec.GenTableRecordPrefix(tblID), + result: []byte{}, + }, + { + key: tablecodec.GenTableRecordPrefix(tblID - 1), + result: []byte{}, + }, + { + key: tablecodec.GenTablePrefix(tblID).PrefixNext(), + isNull: true, + }, + { + key: buildRowKey([]byte{0}), + result: []byte{}, + }, + { + key: buildRowKey([]byte{1}), + result: []byte{}, + }, + { + key: buildRowKey([]byte{2}), + isNull: true, + }, + { + // recordPrefix + bytesFlag + [0] + key: buildBytesRowKey([]byte{})[:binaryDataStartPos+1], + result: []byte{}, + }, + { + // recordPrefix + bytesFlag + [0, 0, 0, 0, 0, 0, 0, 0] + key: buildBytesRowKey([]byte{})[:binaryDataStartPos+8], + result: []byte{}, + }, + { + // recordPrefix + bytesFlag + [1] + key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+1], + result: []byte{1}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3] + key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+3], + result: []byte{1, 2, 3}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 0] + key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+4], + result: []byte{1, 2, 3}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 0, 0, 0, 0, 0, 247] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3}) + bs[len(bs)-1] = 247 + return bs + }, + result: []byte{1, 2, 3}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 0, 0, 0, 0, 0, 0] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3}) + bs[len(bs)-1] = 0 + return bs + }, + result: []byte{1, 2, 3}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 254, 9, 0, 0, 0, 0, 0, 0, 0, 248] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) + bs[len(bs)-10] = 254 + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 0, 254, 9, 0, 0, 0, 0, 0, 0, 0, 248] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 0, 9}) + bs[len(bs)-10] = 254 + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 0}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 0, 253, 9, 0, 0, 0, 0, 0, 0, 0, 248] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 0, 9}) + bs[len(bs)-10] = 253 + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 247] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) + bs[len(bs)-1] = 247 + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) + bs[len(bs)-1] = 0 + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) + bs = bs[:len(bs)-1] + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}) + bs = bs[:len(bs)-1] + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + { + // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 246] + key: func() []byte { + bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}) + bs = bs[:len(bs)-1] + return bs + }, + result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + }, + } + + for i, c := range cases { + var key kv.Key + switch k := c.key.(type) { + case kv.Key: + key = k + case []byte: + key = k + case func() []byte: + key = k() + case func() kv.Key: + key = k() + default: + require.FailNow(t, "%d", i) + } + + d := cache.GetNextBytesHandleDatum(key, tablecodec.GenTableRecordPrefix(tblID)) + if c.isNull { + require.True(t, d.IsNull(), i) + } else { + require.Equal(t, types.KindBytes, d.Kind(), i) + require.Equal(t, c.result, d.GetBytes(), i) + } + } +} +func TestGetNextIntHandle(t *testing.T) { + tblID := int64(7) + cases := []struct { + key interface{} + result int64 + isNull bool + }{ + { + key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(0)), + result: 0, + }, + { + key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(3)), + result: 3, + }, + { + key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MaxInt64)), + result: math.MaxInt64, + }, + { + key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MinInt64)), + result: math.MinInt64, + }, + { + key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(7)), 0), + result: 8, + }, + { + key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MaxInt64)), 0), + isNull: true, + }, + { + key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MinInt64)), 0), + result: math.MinInt64 + 1, + }, + { + key: []byte{}, + result: math.MinInt64, + }, + { + key: tablecodec.GenTableRecordPrefix(tblID), + result: math.MinInt64, + }, + { + key: tablecodec.GenTableRecordPrefix(tblID - 1), + result: math.MinInt64, + }, + { + key: tablecodec.GenTablePrefix(tblID).PrefixNext(), + isNull: true, + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{0}), + result: codec.DecodeCmpUintToInt(0), + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{0, 1, 2, 3}), + result: codec.DecodeCmpUintToInt(0x0001020300000000), + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{8, 1, 2, 3}), + result: codec.DecodeCmpUintToInt(0x0801020300000000), + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{0, 1, 2, 3, 4, 5, 6, 7, 0}), + result: codec.DecodeCmpUintToInt(0x0001020304050607) + 1, + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{8, 1, 2, 3, 4, 5, 6, 7, 0}), + result: codec.DecodeCmpUintToInt(0x0801020304050607) + 1, + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), + result: math.MaxInt64, + }, + { + key: tablecodec.EncodeRowKey(tblID, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}), + isNull: true, + }, + } + + for i, c := range cases { + var key kv.Key + switch k := c.key.(type) { + case kv.Key: + key = k + case []byte: + key = k + case func() []byte: + key = k() + case func() kv.Key: + key = k() + default: + require.FailNow(t, "%d", i) + } + + v := cache.GetNextIntHandle(key, tablecodec.GenTableRecordPrefix(tblID)) + if c.isNull { + require.Nil(t, v, i) + } else { + require.IsType(t, kv.IntHandle(0), v, i) + require.Equal(t, c.result, v.IntValue()) + } + } +} diff --git a/pkg/ttl/cache/table.go b/pkg/ttl/cache/table.go new file mode 100644 index 0000000000000..1e69faef9d34b --- /dev/null +++ b/pkg/ttl/cache/table.go @@ -0,0 +1,489 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache + +import ( + "context" + "encoding/binary" + "fmt" + "math" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/tikv/client-go/v2/tikv" +) + +func getTableKeyColumns(tbl *model.TableInfo) ([]*model.ColumnInfo, []*types.FieldType, error) { + if tbl.PKIsHandle { + for i, col := range tbl.Columns { + if mysql.HasPriKeyFlag(col.GetFlag()) { + return []*model.ColumnInfo{tbl.Columns[i]}, []*types.FieldType{&tbl.Columns[i].FieldType}, nil + } + } + return nil, nil, errors.Errorf("Cannot find primary key for table: %s", tbl.Name) + } + + if tbl.IsCommonHandle { + idxInfo := tables.FindPrimaryIndex(tbl) + columns := make([]*model.ColumnInfo, len(idxInfo.Columns)) + fieldTypes := make([]*types.FieldType, len(idxInfo.Columns)) + for i, idxCol := range idxInfo.Columns { + columns[i] = tbl.Columns[idxCol.Offset] + fieldTypes[i] = &tbl.Columns[idxCol.Offset].FieldType + } + return columns, fieldTypes, nil + } + + extraHandleColInfo := model.NewExtraHandleColInfo() + return []*model.ColumnInfo{extraHandleColInfo}, []*types.FieldType{&extraHandleColInfo.FieldType}, nil +} + +// ScanRange is the range to scan. The range is: [Start, End) +type ScanRange struct { + Start []types.Datum + End []types.Datum +} + +func newFullRange() ScanRange { + return ScanRange{} +} + +func newDatumRange(start types.Datum, end types.Datum) (r ScanRange) { + if !start.IsNull() { + r.Start = []types.Datum{start} + } + if !end.IsNull() { + r.End = []types.Datum{end} + } + return r +} + +func nullDatum() types.Datum { + d := types.Datum{} + d.SetNull() + return d +} + +// PhysicalTable is used to provide some information for a physical table in TTL job +type PhysicalTable struct { + // ID is the physical ID of the table + ID int64 + // Schema is the database name of the table + Schema model.CIStr + *model.TableInfo + // Partition is the partition name + Partition model.CIStr + // PartitionDef is the partition definition + PartitionDef *model.PartitionDefinition + // KeyColumns is the cluster index key columns for the table + KeyColumns []*model.ColumnInfo + // KeyColumnTypes is the types of the key columns + KeyColumnTypes []*types.FieldType + // TimeColum is the time column used for TTL + TimeColumn *model.ColumnInfo +} + +// NewBasePhysicalTable create a new PhysicalTable with specific timeColunm. +func NewBasePhysicalTable(schema model.CIStr, + tbl *model.TableInfo, + partition model.CIStr, + timeColumn *model.ColumnInfo, +) (*PhysicalTable, error) { + if tbl.State != model.StatePublic { + return nil, errors.Errorf("table '%s.%s' is not a public table", schema, tbl.Name) + } + + keyColumns, keyColumTypes, err := getTableKeyColumns(tbl) + if err != nil { + return nil, err + } + + var physicalID int64 + var partitionDef *model.PartitionDefinition + if tbl.Partition == nil { + if partition.L != "" { + return nil, errors.Errorf("table '%s.%s' is not a partitioned table", schema, tbl.Name) + } + physicalID = tbl.ID + } else { + if partition.L == "" { + return nil, errors.Errorf("partition name is required, table '%s.%s' is a partitioned table", schema, tbl.Name) + } + + for i := range tbl.Partition.Definitions { + def := &tbl.Partition.Definitions[i] + if def.Name.L == partition.L { + partitionDef = def + } + } + + if partitionDef == nil { + return nil, errors.Errorf("partition '%s' is not found in ttl table '%s.%s'", partition.O, schema, tbl.Name) + } + + physicalID = partitionDef.ID + } + + return &PhysicalTable{ + ID: physicalID, + Schema: schema, + TableInfo: tbl, + Partition: partition, + PartitionDef: partitionDef, + KeyColumns: keyColumns, + KeyColumnTypes: keyColumTypes, + TimeColumn: timeColumn, + }, nil +} + +// NewPhysicalTable create a new PhysicalTable +func NewPhysicalTable(schema model.CIStr, tbl *model.TableInfo, partition model.CIStr) (*PhysicalTable, error) { + ttlInfo := tbl.TTLInfo + if ttlInfo == nil { + return nil, errors.Errorf("table '%s.%s' is not a ttl table", schema, tbl.Name) + } + + timeColumn := tbl.FindPublicColumnByName(ttlInfo.ColumnName.L) + if timeColumn == nil { + return nil, errors.Errorf("time column '%s' is not public in ttl table '%s.%s'", ttlInfo.ColumnName, schema, tbl.Name) + } + + return NewBasePhysicalTable(schema, tbl, partition, timeColumn) +} + +// ValidateKeyPrefix validates a key prefix +func (t *PhysicalTable) ValidateKeyPrefix(key []types.Datum) error { + if len(key) > len(t.KeyColumns) { + return errors.Errorf("invalid key length: %d, expected %d", len(key), len(t.KeyColumns)) + } + return nil +} + +// EvalExpireTime returns the expired time +func (t *PhysicalTable) EvalExpireTime(ctx context.Context, se session.Session, + now time.Time) (expire time.Time, err error) { + tz := se.GetSessionVars().Location() + + expireExpr := t.TTLInfo.IntervalExprStr + unit := ast.TimeUnitType(t.TTLInfo.IntervalTimeUnit) + + var rows []chunk.Row + rows, err = se.ExecuteSQL( + ctx, + // FROM_UNIXTIME does not support negative value, so we use `FROM_UNIXTIME(0) + INTERVAL ` + // to present current time + fmt.Sprintf("SELECT FROM_UNIXTIME(0) + INTERVAL %d SECOND - INTERVAL %s %s", now.Unix(), expireExpr, unit.String()), + ) + + if err != nil { + return + } + + tm := rows[0].GetTime(0) + return tm.CoreTime().GoTime(tz) +} + +// SplitScanRanges split ranges for TTL scan +func (t *PhysicalTable) SplitScanRanges(ctx context.Context, store kv.Storage, splitCnt int) ([]ScanRange, error) { + if len(t.KeyColumns) < 1 || splitCnt <= 1 { + return []ScanRange{newFullRange()}, nil + } + + tikvStore, ok := store.(tikv.Storage) + if !ok { + return []ScanRange{newFullRange()}, nil + } + + ft := t.KeyColumns[0].FieldType + switch ft.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24: + return t.splitIntRanges(ctx, tikvStore, splitCnt) + case mysql.TypeBit: + return t.splitBinaryRanges(ctx, tikvStore, splitCnt) + case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar: + if mysql.HasBinaryFlag(ft.GetFlag()) { + return t.splitBinaryRanges(ctx, tikvStore, splitCnt) + } + } + return []ScanRange{newFullRange()}, nil +} + +func unsignedEdge(d types.Datum) types.Datum { + if d.IsNull() { + return types.NewUintDatum(uint64(math.MaxInt64 + 1)) + } + if d.GetInt64() == 0 { + return nullDatum() + } + return types.NewUintDatum(uint64(d.GetInt64())) +} + +func (t *PhysicalTable) splitIntRanges(ctx context.Context, store tikv.Storage, splitCnt int) ([]ScanRange, error) { + recordPrefix := tablecodec.GenTableRecordPrefix(t.ID) + startKey, endKey := tablecodec.GetTableHandleKeyRange(t.ID) + keyRanges, err := t.splitRawKeyRanges(ctx, store, startKey, endKey, splitCnt) + if err != nil { + return nil, err + } + + if len(keyRanges) <= 1 { + return []ScanRange{newFullRange()}, nil + } + + ft := t.KeyColumnTypes[0] + unsigned := mysql.HasUnsignedFlag(ft.GetFlag()) + scanRanges := make([]ScanRange, 0, len(keyRanges)+1) + curScanStart := nullDatum() + for i, keyRange := range keyRanges { + if i != 0 && curScanStart.IsNull() { + break + } + + curScanEnd := nullDatum() + if i < len(keyRanges)-1 { + if val := GetNextIntHandle(keyRange.EndKey, recordPrefix); val != nil { + curScanEnd = types.NewIntDatum(val.IntValue()) + } + } + + if !curScanStart.IsNull() && !curScanEnd.IsNull() && curScanStart.GetInt64() >= curScanEnd.GetInt64() { + continue + } + + if !unsigned { + // primary key is signed or range + scanRanges = append(scanRanges, newDatumRange(curScanStart, curScanEnd)) + } else if !curScanStart.IsNull() && curScanStart.GetInt64() >= 0 { + // primary key is unsigned and range is in the right half side + scanRanges = append(scanRanges, newDatumRange(unsignedEdge(curScanStart), unsignedEdge(curScanEnd))) + } else if !curScanEnd.IsNull() && curScanEnd.GetInt64() <= 0 { + // primary key is unsigned and range is in the left half side + scanRanges = append(scanRanges, newDatumRange(unsignedEdge(curScanStart), unsignedEdge(curScanEnd))) + } else { + // primary key is unsigned and the start > math.MaxInt64 && end < math.MaxInt64 + // we must split it to two ranges + scanRanges = append(scanRanges, + newDatumRange(unsignedEdge(curScanStart), nullDatum()), + newDatumRange(nullDatum(), unsignedEdge(curScanEnd)), + ) + } + curScanStart = curScanEnd + } + return scanRanges, nil +} + +func (t *PhysicalTable) splitBinaryRanges(ctx context.Context, store tikv.Storage, splitCnt int) ([]ScanRange, error) { + recordPrefix := tablecodec.GenTableRecordPrefix(t.ID) + startKey, endKey := recordPrefix, recordPrefix.PrefixNext() + keyRanges, err := t.splitRawKeyRanges(ctx, store, startKey, endKey, splitCnt) + if err != nil { + return nil, err + } + + if len(keyRanges) <= 1 { + return []ScanRange{newFullRange()}, nil + } + + scanRanges := make([]ScanRange, 0, len(keyRanges)) + curScanStart := nullDatum() + for i, keyRange := range keyRanges { + if i != 0 && curScanStart.IsNull() { + break + } + + curScanEnd := nullDatum() + if i != len(keyRanges)-1 { + curScanEnd = GetNextBytesHandleDatum(keyRange.EndKey, recordPrefix) + } + + if !curScanStart.IsNull() && !curScanEnd.IsNull() && kv.Key(curScanStart.GetBytes()).Cmp(curScanEnd.GetBytes()) >= 0 { + continue + } + + scanRanges = append(scanRanges, newDatumRange(curScanStart, curScanEnd)) + curScanStart = curScanEnd + } + return scanRanges, nil +} + +func (t *PhysicalTable) splitRawKeyRanges(ctx context.Context, store tikv.Storage, + startKey, endKey kv.Key, splitCnt int) ([]kv.KeyRange, error) { + regionCache := store.GetRegionCache() + regionIDs, err := regionCache.ListRegionIDsInKeyRange( + tikv.NewBackofferWithVars(ctx, 20000, nil), startKey, endKey) + if err != nil { + return nil, err + } + + regionsPerRange := len(regionIDs) / splitCnt + oversizeCnt := len(regionIDs) % splitCnt + ranges := make([]kv.KeyRange, 0, mathutil.Min(len(regionIDs), splitCnt)) + for len(regionIDs) > 0 { + startRegion, err := regionCache.LocateRegionByID(tikv.NewBackofferWithVars(ctx, 20000, nil), + regionIDs[0]) + if err != nil { + return nil, err + } + + endRegionIdx := regionsPerRange - 1 + if oversizeCnt > 0 { + endRegionIdx++ + } + + endRegion, err := regionCache.LocateRegionByID(tikv.NewBackofferWithVars(ctx, 20000, nil), + regionIDs[endRegionIdx]) + if err != nil { + return nil, err + } + + rangeStartKey := kv.Key(startRegion.StartKey) + if rangeStartKey.Cmp(startKey) < 0 { + rangeStartKey = startKey + } + + rangeEndKey := kv.Key(endRegion.EndKey) + if rangeEndKey.Cmp(endKey) > 0 { + rangeEndKey = endKey + } + + ranges = append(ranges, kv.KeyRange{StartKey: rangeStartKey, EndKey: rangeEndKey}) + oversizeCnt-- + regionIDs = regionIDs[endRegionIdx+1:] + } + return ranges, nil +} + +var emptyBytesHandleKey kv.Key + +func init() { + key, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(nil)) + terror.MustNil(err) + emptyBytesHandleKey = key +} + +// GetNextIntHandle is used for int handle tables. +// It returns the min handle whose encoded key is or after argument `key` +// If it cannot find a valid value, a null datum will be returned. +func GetNextIntHandle(key kv.Key, recordPrefix []byte) kv.Handle { + if key.Cmp(recordPrefix) > 0 && !key.HasPrefix(recordPrefix) { + return nil + } + + if key.Cmp(recordPrefix) <= 0 { + return kv.IntHandle(math.MinInt64) + } + + suffix := key[len(recordPrefix):] + encodedVal := suffix + if len(suffix) < 8 { + encodedVal = make([]byte, 8) + copy(encodedVal, suffix) + } + + findNext := false + if len(suffix) > 8 { + findNext = true + encodedVal = encodedVal[:8] + } + + u := codec.DecodeCmpUintToInt(binary.BigEndian.Uint64(encodedVal)) + if !findNext { + return kv.IntHandle(u) + } + + if u == math.MaxInt64 { + return nil + } + + return kv.IntHandle(u + 1) +} + +// GetNextBytesHandleDatum is used for a table with one binary or string column common handle. +// It returns the minValue whose encoded key is or after argument `key` +// If it cannot find a valid value, a null datum will be returned. +func GetNextBytesHandleDatum(key kv.Key, recordPrefix []byte) (d types.Datum) { + if key.Cmp(recordPrefix) > 0 && !key.HasPrefix(recordPrefix) { + d.SetNull() + return d + } + + if key.Cmp(recordPrefix) <= 0 { + d.SetBytes([]byte{}) + return d + } + + encodedVal := key[len(recordPrefix):] + if encodedVal[0] < emptyBytesHandleKey[0] { + d.SetBytes([]byte{}) + return d + } + + if encodedVal[0] > emptyBytesHandleKey[0] { + d.SetNull() + return d + } + + if remain, v, err := codec.DecodeOne(encodedVal); err == nil { + if len(remain) > 0 { + v.SetBytes(kv.Key(v.GetBytes()).Next()) + } + return v + } + + encodedVal = encodedVal[1:] + brokenGroupEndIdx := len(encodedVal) - 1 + brokenGroupEmptyBytes := len(encodedVal) % 9 + for i := 7; i+1 < len(encodedVal); i += 9 { + if emptyBytes := 255 - int(encodedVal[i+1]); emptyBytes != 0 || i+1 == len(encodedVal)-1 { + brokenGroupEndIdx = i + brokenGroupEmptyBytes = emptyBytes + break + } + } + + for i := 0; i < brokenGroupEmptyBytes; i++ { + if encodedVal[brokenGroupEndIdx] > 0 { + break + } + brokenGroupEndIdx-- + } + + if brokenGroupEndIdx < 0 { + d.SetBytes(nil) + return d + } + + val := make([]byte, 0, len(encodedVal)) + for i := 0; i <= brokenGroupEndIdx; i++ { + if i%9 == 8 { + continue + } + val = append(val, encodedVal[i]) + } + d.SetBytes(val) + return d +} diff --git a/pkg/ttl/cache/table_test.go b/pkg/ttl/cache/table_test.go new file mode 100644 index 0000000000000..ed21907fb2d96 --- /dev/null +++ b/pkg/ttl/cache/table_test.go @@ -0,0 +1,216 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cache_test + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/stretchr/testify/require" +) + +func TestNewTTLTable(t *testing.T) { + cases := []struct { + db string + tbl string + def string + timeCol string + keyCols []string + }{ + { + db: "test", + tbl: "t1", + def: "(a int)", + }, + { + db: "test", + tbl: "ttl1", + def: "(a int, t datetime) ttl = `t` + interval 2 hour", + timeCol: "t", + keyCols: []string{"_tidb_rowid"}, + }, + { + db: "test", + tbl: "ttl2", + def: "(id int primary key, t datetime) ttl = `t` + interval 3 hour", + timeCol: "t", + keyCols: []string{"id"}, + }, + { + db: "test", + tbl: "ttl3", + def: "(a int, b varchar(32), c binary(32), t datetime, primary key (a, b, c)) ttl = `t` + interval 1 month", + timeCol: "t", + keyCols: []string{"a", "b", "c"}, + }, + { + db: "test", + tbl: "ttl4", + def: "(id int primary key, t datetime) " + + "ttl = `t` + interval 1 day " + + "PARTITION BY RANGE (id) (" + + " PARTITION p0 VALUES LESS THAN (10)," + + " PARTITION p1 VALUES LESS THAN (100)," + + " PARTITION p2 VALUES LESS THAN (1000)," + + " PARTITION p3 VALUES LESS THAN MAXVALUE)", + timeCol: "t", + keyCols: []string{"id"}, + }, + { + db: "test", + tbl: "ttl5", + def: "(id int primary key nonclustered, t datetime) ttl = `t` + interval 3 hour", + timeCol: "t", + keyCols: []string{"_tidb_rowid"}, + }, + } + + store, do := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + + for _, c := range cases { + tk.MustExec("use " + c.db) + tk.MustExec("create table " + c.tbl + c.def) + } + + for _, c := range cases { + is := do.InfoSchema() + tbl, err := is.TableByName(model.NewCIStr(c.db), model.NewCIStr(c.tbl)) + require.NoError(t, err) + tblInfo := tbl.Meta() + var physicalTbls []*cache.PhysicalTable + if tblInfo.Partition == nil { + ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr(c.db), tblInfo, model.NewCIStr("")) + if c.timeCol == "" { + require.Error(t, err) + continue + } + require.NoError(t, err) + physicalTbls = append(physicalTbls, ttlTbl) + } else { + for _, partition := range tblInfo.Partition.Definitions { + ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr(c.db), tblInfo, partition.Name) + if c.timeCol == "" { + require.Error(t, err) + continue + } + require.NoError(t, err) + physicalTbls = append(physicalTbls, ttlTbl) + } + if c.timeCol == "" { + continue + } + } + + for i, ttlTbl := range physicalTbls { + require.Equal(t, c.db, ttlTbl.Schema.O) + require.Same(t, tblInfo, ttlTbl.TableInfo) + timeColumn := tblInfo.FindPublicColumnByName(c.timeCol) + require.NotNil(t, timeColumn) + require.Same(t, timeColumn, ttlTbl.TimeColumn) + + if tblInfo.Partition == nil { + require.Equal(t, ttlTbl.TableInfo.ID, ttlTbl.ID) + require.Equal(t, "", ttlTbl.Partition.L) + require.Nil(t, ttlTbl.PartitionDef) + } else { + def := tblInfo.Partition.Definitions[i] + require.Equal(t, def.ID, ttlTbl.ID) + require.Equal(t, def.Name.L, ttlTbl.Partition.L) + require.Equal(t, def, *(ttlTbl.PartitionDef)) + } + + require.Equal(t, len(c.keyCols), len(ttlTbl.KeyColumns)) + require.Equal(t, len(c.keyCols), len(ttlTbl.KeyColumnTypes)) + + for j, keyCol := range c.keyCols { + msg := fmt.Sprintf("%s, col: %s", c.tbl, keyCol) + var col *model.ColumnInfo + if keyCol == model.ExtraHandleName.L { + col = model.NewExtraHandleColInfo() + } else { + col = tblInfo.FindPublicColumnByName(keyCol) + } + colJ := ttlTbl.KeyColumns[j] + colFieldJ := ttlTbl.KeyColumnTypes[j] + + require.NotNil(t, col, msg) + require.Equal(t, col.ID, colJ.ID, msg) + require.Equal(t, col.Name.L, colJ.Name.L, msg) + require.Equal(t, col.FieldType, colJ.FieldType, msg) + require.Equal(t, col.FieldType, *colFieldJ, msg) + } + } + } +} + +func TestEvalTTLExpireTime(t *testing.T) { + store, do := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("create table test.t(a int, t datetime) ttl = `t` + interval 1 day") + tk.MustExec("create table test.t2(a int, t datetime) ttl = `t` + interval 3 month") + + tb, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + tblInfo := tb.Meta() + ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo, model.NewCIStr("")) + require.NoError(t, err) + + tb2, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) + require.NoError(t, err) + tblInfo2 := tb2.Meta() + ttlTbl2, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo2, model.NewCIStr("")) + require.NoError(t, err) + + se := session.NewSession(tk.Session(), tk.Session(), nil) + + now := time.UnixMilli(0) + tz1, err := time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + tz2, err := time.LoadLocation("Europe/Berlin") + require.NoError(t, err) + + se.GetSessionVars().TimeZone = tz1 + tm, err := ttlTbl.EvalExpireTime(context.TODO(), se, now) + require.NoError(t, err) + require.Equal(t, now.Add(-time.Hour*24).Unix(), tm.Unix()) + require.Equal(t, "1969-12-31 08:00:00", tm.Format(time.DateTime)) + require.Equal(t, tz1.String(), tm.Location().String()) + + se.GetSessionVars().TimeZone = tz2 + tm, err = ttlTbl.EvalExpireTime(context.TODO(), se, now) + require.NoError(t, err) + require.Equal(t, now.Add(-time.Hour*24).Unix(), tm.Unix()) + require.Equal(t, "1969-12-31 01:00:00", tm.Format(time.DateTime)) + require.Equal(t, tz2.String(), tm.Location().String()) + + se.GetSessionVars().TimeZone = tz1 + tm, err = ttlTbl2.EvalExpireTime(context.TODO(), se, now) + require.NoError(t, err) + require.Equal(t, "1969-10-01 08:00:00", tm.Format(time.DateTime)) + require.Equal(t, tz1.String(), tm.Location().String()) + + se.GetSessionVars().TimeZone = tz2 + tm, err = ttlTbl2.EvalExpireTime(context.TODO(), se, now) + require.NoError(t, err) + require.Equal(t, "1969-10-01 01:00:00", tm.Format(time.DateTime)) + require.Equal(t, tz2.String(), tm.Location().String()) +} diff --git a/pkg/ttl/cache/task.go b/pkg/ttl/cache/task.go new file mode 100644 index 0000000000000..c130f53b613af --- /dev/null +++ b/pkg/ttl/cache/task.go @@ -0,0 +1,198 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package cache + +import ( + "encoding/json" + "time" + + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" +) + +const selectFromTTLTask = `SELECT LOW_PRIORITY + job_id, + table_id, + scan_id, + scan_range_start, + scan_range_end, + expire_time, + owner_id, + owner_addr, + owner_hb_time, + status, + status_update_time, + state, + created_time FROM mysql.tidb_ttl_task` +const insertIntoTTLTask = `INSERT LOW_PRIORITY INTO mysql.tidb_ttl_task SET + job_id = %?, + table_id = %?, + scan_id = %?, + scan_range_start = %?, + scan_range_end = %?, + expire_time = %?, + created_time = %?` + +// SelectFromTTLTaskWithJobID returns an SQL statement to get all tasks of the specified job in mysql.tidb_ttl_task +func SelectFromTTLTaskWithJobID(jobID string) (string, []interface{}) { + return selectFromTTLTask + " WHERE job_id = %?", []interface{}{jobID} +} + +// SelectFromTTLTaskWithID returns an SQL statement to get all tasks of the specified job +// and scanID in mysql.tidb_ttl_task +func SelectFromTTLTaskWithID(jobID string, scanID int64) (string, []interface{}) { + return selectFromTTLTask + " WHERE job_id = %? AND scan_id = %?", []interface{}{jobID, scanID} +} + +// PeekWaitingTTLTask returns an SQL statement to get `limit` waiting ttl task +func PeekWaitingTTLTask(hbExpire time.Time) (string, []interface{}) { + return selectFromTTLTask + + " WHERE status = 'waiting' OR (owner_hb_time < %? AND status = 'running') ORDER BY created_time ASC", + []interface{}{hbExpire.Format(time.DateTime)} +} + +// InsertIntoTTLTask returns an SQL statement to insert a ttl task into mysql.tidb_ttl_task +func InsertIntoTTLTask(sctx sessionctx.Context, jobID string, tableID int64, scanID int, scanRangeStart []types.Datum, + scanRangeEnd []types.Datum, expireTime time.Time, createdTime time.Time) (string, []interface{}, error) { + rangeStart, err := codec.EncodeKey(sctx.GetSessionVars().StmtCtx, []byte{}, scanRangeStart...) + if err != nil { + return "", nil, err + } + rangeEnd, err := codec.EncodeKey(sctx.GetSessionVars().StmtCtx, []byte{}, scanRangeEnd...) + if err != nil { + return "", nil, err + } + return insertIntoTTLTask, []interface{}{jobID, tableID, int64(scanID), + rangeStart, rangeEnd, expireTime, createdTime}, nil +} + +// TaskStatus represents the current status of a task +type TaskStatus string + +const ( + // TaskStatusWaiting means the task hasn't started + TaskStatusWaiting TaskStatus = "waiting" + // TaskStatusRunning means this task is running + TaskStatusRunning = "running" + // TaskStatusFinished means this task has finished + TaskStatusFinished = "finished" +) + +// TTLTask is a row recorded in mysql.tidb_ttl_task +type TTLTask struct { + JobID string + TableID int64 + ScanID int64 + ScanRangeStart []types.Datum + ScanRangeEnd []types.Datum + ExpireTime time.Time + OwnerID string + OwnerAddr string + OwnerHBTime time.Time + Status TaskStatus + StatusUpdateTime time.Time + State *TTLTaskState + CreatedTime time.Time +} + +// TTLTaskState records the internal states of the ttl task +type TTLTaskState struct { + TotalRows uint64 `json:"total_rows"` + SuccessRows uint64 `json:"success_rows"` + ErrorRows uint64 `json:"error_rows"` + + ScanTaskErr string `json:"scan_task_err"` +} + +// RowToTTLTask converts a row into TTL task +func RowToTTLTask(sctx sessionctx.Context, row chunk.Row) (*TTLTask, error) { + var err error + timeZone := sctx.GetSessionVars().Location() + + task := &TTLTask{ + JobID: row.GetString(0), + TableID: row.GetInt64(1), + ScanID: row.GetInt64(2), + } + if !row.IsNull(3) { + scanRangeStartBuf := row.GetBytes(3) + // it's still posibble to be empty even this column is not NULL + if len(scanRangeStartBuf) > 0 { + task.ScanRangeStart, err = codec.Decode(scanRangeStartBuf, len(scanRangeStartBuf)) + if err != nil { + return nil, err + } + } + } + if !row.IsNull(4) { + scanRangeEndBuf := row.GetBytes(4) + // it's still posibble to be empty even this column is not NULL + if len(scanRangeEndBuf) > 0 { + task.ScanRangeEnd, err = codec.Decode(scanRangeEndBuf, len(scanRangeEndBuf)) + if err != nil { + return nil, err + } + } + } + + task.ExpireTime, err = row.GetTime(5).GoTime(timeZone) + if err != nil { + return nil, err + } + + if !row.IsNull(6) { + task.OwnerID = row.GetString(6) + } + if !row.IsNull(7) { + task.OwnerAddr = row.GetString(7) + } + if !row.IsNull(8) { + task.OwnerHBTime, err = row.GetTime(8).GoTime(timeZone) + if err != nil { + return nil, err + } + } + if !row.IsNull(9) { + status := row.GetString(9) + if len(status) == 0 { + status = "waiting" + } + task.Status = TaskStatus(status) + } + if !row.IsNull(10) { + task.StatusUpdateTime, err = row.GetTime(10).GoTime(timeZone) + if err != nil { + return nil, err + } + } + if !row.IsNull(11) { + stateStr := row.GetString(11) + state := &TTLTaskState{} + err = json.Unmarshal([]byte(stateStr), state) + if err != nil { + return nil, err + } + task.State = state + } + + task.CreatedTime, err = row.GetTime(12).GoTime(timeZone) + if err != nil { + return nil, err + } + + return task, nil +} diff --git a/ttl/cache/task_test.go b/pkg/ttl/cache/task_test.go similarity index 94% rename from ttl/cache/task_test.go rename to pkg/ttl/cache/task_test.go index a2081f85911b7..8edb41f004171 100644 --- a/ttl/cache/task_test.go +++ b/pkg/ttl/cache/task_test.go @@ -19,12 +19,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/require" ) diff --git a/ttl/cache/ttlstatus.go b/pkg/ttl/cache/ttlstatus.go similarity index 97% rename from ttl/cache/ttlstatus.go rename to pkg/ttl/cache/ttlstatus.go index b21a50a161f79..dcdc642bf2ae5 100644 --- a/ttl/cache/ttlstatus.go +++ b/pkg/ttl/cache/ttlstatus.go @@ -18,9 +18,9 @@ import ( "context" "time" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/chunk" ) // JobStatus represents the current status of a job diff --git a/ttl/cache/ttlstatus_test.go b/pkg/ttl/cache/ttlstatus_test.go similarity index 97% rename from ttl/cache/ttlstatus_test.go rename to pkg/ttl/cache/ttlstatus_test.go index bfcb7f797764a..8ed16f37c03c9 100644 --- a/ttl/cache/ttlstatus_test.go +++ b/pkg/ttl/cache/ttlstatus_test.go @@ -20,10 +20,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" + "github.com/pingcap/tidb/pkg/server" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" "github.com/stretchr/testify/assert" ) diff --git a/pkg/ttl/client/BUILD.bazel b/pkg/ttl/client/BUILD.bazel new file mode 100644 index 0000000000000..aba9359fdb9cf --- /dev/null +++ b/pkg/ttl/client/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "client", + srcs = [ + "command.go", + "notification.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ttl/client", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl/util", + "//pkg/util/logutil", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_errors//:errors", + "@io_etcd_go_etcd_client_v3//:client", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "client_test", + timeout = "short", + srcs = ["command_test.go"], + embed = [":client"], + flaky = True, + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_tests_v3//integration", + ], +) diff --git a/ttl/client/command.go b/pkg/ttl/client/command.go similarity index 99% rename from ttl/client/command.go rename to pkg/ttl/client/command.go index c6381d1a561b7..b84835825a660 100644 --- a/ttl/client/command.go +++ b/pkg/ttl/client/command.go @@ -23,7 +23,7 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" ) diff --git a/ttl/client/command_test.go b/pkg/ttl/client/command_test.go similarity index 100% rename from ttl/client/command_test.go rename to pkg/ttl/client/command_test.go diff --git a/ttl/client/notification.go b/pkg/ttl/client/notification.go similarity index 98% rename from ttl/client/notification.go rename to pkg/ttl/client/notification.go index 2778546426dc5..ecff7df05a08d 100644 --- a/ttl/client/notification.go +++ b/pkg/ttl/client/notification.go @@ -17,7 +17,7 @@ package client import ( "context" - "github.com/pingcap/tidb/ddl/util" + "github.com/pingcap/tidb/pkg/ddl/util" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/pkg/ttl/metrics/BUILD.bazel b/pkg/ttl/metrics/BUILD.bazel new file mode 100644 index 0000000000000..0f44fc67cd619 --- /dev/null +++ b/pkg/ttl/metrics/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/ttl/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) + +go_test( + name = "metrics_test", + timeout = "short", + srcs = ["metrics_test.go"], + embed = [":metrics"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/pkg/ttl/metrics/metrics.go b/pkg/ttl/metrics/metrics.go new file mode 100644 index 0000000000000..c308821a157f4 --- /dev/null +++ b/pkg/ttl/metrics/metrics.go @@ -0,0 +1,257 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package metrics + +import ( + "context" + "math" + "time" + + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// Phases to trace +var ( + PhaseIdle = "idle" + PhaseBeginTxn = "begin_txn" + PhaseCommitTxn = "commit_txn" + PhaseQuery = "query" + PhaseCheckTTL = "check_ttl" + PhaseWaitRetry = "wait_retry" + PhaseDispatch = "dispatch" + PhaseWaitToken = "wait_token" + PhaseOther = "other" +) + +// TTL metrics +var ( + SelectSuccessDuration prometheus.Observer + SelectErrorDuration prometheus.Observer + DeleteSuccessDuration prometheus.Observer + DeleteErrorDuration prometheus.Observer + + ScannedExpiredRows prometheus.Counter + DeleteSuccessExpiredRows prometheus.Counter + DeleteErrorExpiredRows prometheus.Counter + + RunningJobsCnt prometheus.Gauge + CancellingJobsCnt prometheus.Gauge + + ScanningTaskCnt prometheus.Gauge + DeletingTaskCnt prometheus.Gauge + + WaterMarkScheduleDelayNames = []struct { + Name string + Delay time.Duration + }{ + { + Name: "01 hour", + Delay: time.Hour, + }, + { + Name: "02 hour", + Delay: time.Hour, + }, + { + Name: "06 hour", + Delay: 6 * time.Hour, + }, + { + Name: "12 hour", + Delay: 12 * time.Hour, + }, + { + Name: "24 hour", + Delay: 24 * time.Hour, + }, + { + Name: "72 hour", + Delay: 72 * time.Hour, + }, + { + Name: "one week", + Delay: 72 * time.Hour, + }, + { + Name: "others", + Delay: math.MaxInt64, + }, + } +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init ttl metrics vars vars. +func InitMetricsVars() { + SelectSuccessDuration = metrics.TTLQueryDuration.With( + prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblOK}) + SelectErrorDuration = metrics.TTLQueryDuration.With( + prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblError}) + DeleteSuccessDuration = metrics.TTLQueryDuration.With( + prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblOK}) + DeleteErrorDuration = metrics.TTLQueryDuration.With( + prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblError}) + + ScannedExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( + prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblOK}) + DeleteSuccessExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( + prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblOK}) + DeleteErrorExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( + prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblError}) + + RunningJobsCnt = metrics.TTLJobStatus.With(prometheus.Labels{metrics.LblType: "running"}) + CancellingJobsCnt = metrics.TTLJobStatus.With(prometheus.Labels{metrics.LblType: "cancelling"}) + + ScanningTaskCnt = metrics.TTLTaskStatus.With(prometheus.Labels{metrics.LblType: "scanning"}) + DeletingTaskCnt = metrics.TTLTaskStatus.With(prometheus.Labels{metrics.LblType: "deleting"}) + + scanWorkerPhases = initWorkerPhases("scan_worker") + deleteWorkerPhases = initWorkerPhases("delete_worker") +} + +func initWorkerPhases(workerType string) map[string]prometheus.Counter { + return map[string]prometheus.Counter{ + PhaseIdle: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseIdle), + PhaseBeginTxn: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseBeginTxn), + PhaseCommitTxn: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseCommitTxn), + PhaseQuery: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseQuery), + PhaseWaitRetry: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseWaitRetry), + PhaseDispatch: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseDispatch), + PhaseCheckTTL: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseCheckTTL), + PhaseWaitToken: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseWaitToken), + PhaseOther: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseOther), + } +} + +var scanWorkerPhases map[string]prometheus.Counter +var deleteWorkerPhases map[string]prometheus.Counter + +// PhaseTracer is used to tracer the phases duration +type PhaseTracer struct { + getTime func() time.Time + recordDuration func(phase string, duration time.Duration) + + phase string + phaseTime time.Time +} + +// NewScanWorkerPhaseTracer returns a tracer for scan worker +func NewScanWorkerPhaseTracer() *PhaseTracer { + return newPhaseTracer(time.Now, func(status string, duration time.Duration) { + if counter, ok := scanWorkerPhases[status]; ok { + counter.Add(duration.Seconds()) + } + }) +} + +// NewDeleteWorkerPhaseTracer returns a tracer for delete worker +func NewDeleteWorkerPhaseTracer() *PhaseTracer { + return newPhaseTracer(time.Now, func(status string, duration time.Duration) { + if counter, ok := deleteWorkerPhases[status]; ok { + counter.Add(duration.Seconds()) + } + }) +} + +func newPhaseTracer(getTime func() time.Time, recordDuration func(status string, duration time.Duration)) *PhaseTracer { + return &PhaseTracer{ + getTime: getTime, + recordDuration: recordDuration, + phaseTime: getTime(), + } +} + +// Phase returns the current phase +func (t *PhaseTracer) Phase() string { + if t == nil { + return "" + } + return t.phase +} + +// EnterPhase enters into a new phase +func (t *PhaseTracer) EnterPhase(phase string) { + if t == nil { + return + } + + now := t.getTime() + if t.phase != "" { + t.recordDuration(t.phase, now.Sub(t.phaseTime)) + } + + t.phase = phase + t.phaseTime = now +} + +// EndPhase ends the current phase +func (t *PhaseTracer) EndPhase() { + if t == nil { + return + } + t.EnterPhase("") +} + +type ttlPhaseTraceKey struct{} + +// CtxWithPhaseTracer create a new context with tracer +func CtxWithPhaseTracer(ctx context.Context, tracer *PhaseTracer) context.Context { + return context.WithValue(ctx, ttlPhaseTraceKey{}, tracer) +} + +// PhaseTracerFromCtx returns a tracer from a given context +func PhaseTracerFromCtx(ctx context.Context) *PhaseTracer { + if tracer, ok := ctx.Value(ttlPhaseTraceKey{}).(*PhaseTracer); ok { + return tracer + } + return nil +} + +// DelayMetricsRecord is the delay metric record for a table +type DelayMetricsRecord struct { + TableID int64 + LastJobTime time.Time + AbsoluteDelay time.Duration + ScheduleRelativeDelay time.Duration +} + +func getWaterMarkScheduleDelayName(t time.Duration) string { + for _, l := range WaterMarkScheduleDelayNames { + if t <= l.Delay { + return l.Name + } + } + return WaterMarkScheduleDelayNames[len(WaterMarkScheduleDelayNames)-1].Name +} + +// UpdateDelayMetrics updates the metrics of TTL delay +func UpdateDelayMetrics(records map[int64]*DelayMetricsRecord) { + scheduleMetrics := make(map[string]float64, len(WaterMarkScheduleDelayNames)) + for _, l := range WaterMarkScheduleDelayNames { + scheduleMetrics[l.Name] = 0 + } + + for _, r := range records { + name := getWaterMarkScheduleDelayName(r.ScheduleRelativeDelay) + scheduleMetrics[name] = scheduleMetrics[name] + 1 + } + + for delay, v := range scheduleMetrics { + metrics.TTLWatermarkDelay.With(prometheus.Labels{metrics.LblType: "schedule", metrics.LblName: delay}).Set(v) + } +} diff --git a/ttl/metrics/metrics_test.go b/pkg/ttl/metrics/metrics_test.go similarity index 100% rename from ttl/metrics/metrics_test.go rename to pkg/ttl/metrics/metrics_test.go diff --git a/pkg/ttl/session/BUILD.bazel b/pkg/ttl/session/BUILD.bazel new file mode 100644 index 0000000000000..34988e64f3a9b --- /dev/null +++ b/pkg/ttl/session/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "session", + srcs = ["session.go"], + importpath = "github.com/pingcap/tidb/pkg/ttl/session", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/ttl/metrics", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "session_test", + timeout = "short", + srcs = [ + "main_test.go", + "session_test.go", + "sysvar_test.go", + ], + flaky = True, + shard_count = 6, + deps = [ + ":session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ttl/session/main_test.go b/pkg/ttl/session/main_test.go new file mode 100644 index 0000000000000..07cb8a84ef232 --- /dev/null +++ b/pkg/ttl/session/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package session_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ttl/session/session.go b/pkg/ttl/session/session.go new file mode 100644 index 0000000000000..297e2347e43fc --- /dev/null +++ b/pkg/ttl/session/session.go @@ -0,0 +1,184 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package session + +import ( + "context" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/sqlexec" +) + +// TxnMode represents using optimistic or pessimistic mode in the transaction +type TxnMode int + +const ( + // TxnModeOptimistic means using the optimistic transaction with "BEGIN OPTIMISTIC" + TxnModeOptimistic TxnMode = iota + // TxnModePessimistic means using the pessimistic transaction with "BEGIN PESSIMISTIC" + TxnModePessimistic +) + +// Session is used to execute queries for TTL case +type Session interface { + sessionctx.Context + // SessionInfoSchema returns information schema of current session + SessionInfoSchema() infoschema.InfoSchema + // ExecuteSQL executes the sql + ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) + // RunInTxn executes the specified function in a txn + RunInTxn(ctx context.Context, fn func() error, mode TxnMode) (err error) + // ResetWithGlobalTimeZone resets the session time zone to global time zone + ResetWithGlobalTimeZone(ctx context.Context) error + // Close closes the session + Close() + // Now returns the current time in location specified by session var + Now() time.Time +} + +type session struct { + sessionctx.Context + sqlExec sqlexec.SQLExecutor + closeFn func(Session) +} + +// NewSession creates a new Session +func NewSession(sctx sessionctx.Context, sqlExec sqlexec.SQLExecutor, closeFn func(Session)) Session { + return &session{ + Context: sctx, + sqlExec: sqlExec, + closeFn: closeFn, + } +} + +// SessionInfoSchema returns information schema of current session +func (s *session) SessionInfoSchema() infoschema.InfoSchema { + if s.Context == nil { + return nil + } + return sessiontxn.GetTxnManager(s.Context).GetTxnInfoSchema() +} + +// ExecuteSQL executes the sql +func (s *session) ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) { + if s.sqlExec == nil { + return nil, errors.New("session is closed") + } + + ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnTTL) + rs, err := s.sqlExec.ExecuteInternal(ctx, sql, args...) + if err != nil { + return nil, err + } + + if rs == nil { + return nil, nil + } + + defer func() { + terror.Log(rs.Close()) + }() + + return sqlexec.DrainRecordSet(ctx, rs, 8) +} + +// RunInTxn executes the specified function in a txn +func (s *session) RunInTxn(ctx context.Context, fn func() error, txnMode TxnMode) (err error) { + tracer := metrics.PhaseTracerFromCtx(ctx) + defer tracer.EnterPhase(tracer.Phase()) + + tracer.EnterPhase(metrics.PhaseBeginTxn) + sql := "BEGIN " + switch txnMode { + case TxnModeOptimistic: + sql += "OPTIMISTIC" + case TxnModePessimistic: + sql += "PESSIMISTIC" + default: + return errors.New("unknown transaction mode") + } + if _, err = s.ExecuteSQL(ctx, sql); err != nil { + return err + } + tracer.EnterPhase(metrics.PhaseOther) + + success := false + defer func() { + if !success { + _, rollbackErr := s.ExecuteSQL(ctx, "ROLLBACK") + terror.Log(rollbackErr) + } + }() + + if err = fn(); err != nil { + return err + } + + tracer.EnterPhase(metrics.PhaseCommitTxn) + if _, err = s.ExecuteSQL(ctx, "COMMIT"); err != nil { + return err + } + tracer.EnterPhase(metrics.PhaseOther) + + success = true + return err +} + +// ResetWithGlobalTimeZone resets the session time zone to global time zone +func (s *session) ResetWithGlobalTimeZone(ctx context.Context) error { + sessVar := s.GetSessionVars() + if sessVar.TimeZone != nil { + globalTZ, err := sessVar.GetGlobalSystemVar(ctx, variable.TimeZone) + if err != nil { + return err + } + + tz, err := sessVar.GetSessionOrGlobalSystemVar(ctx, variable.TimeZone) + if err != nil { + return err + } + + if globalTZ == tz { + return nil + } + } + + _, err := s.ExecuteSQL(ctx, "SET @@time_zone=@@global.time_zone") + return err +} + +// Close closes the session +func (s *session) Close() { + if s.closeFn != nil { + s.closeFn(s) + s.Context = nil + s.sqlExec = nil + s.closeFn = nil + } +} + +// Now returns the current time in the location of time_zone session var +func (s *session) Now() time.Time { + return time.Now().In(s.Context.GetSessionVars().Location()) +} diff --git a/pkg/ttl/session/session_test.go b/pkg/ttl/session/session_test.go new file mode 100644 index 0000000000000..0acd9fae836ca --- /dev/null +++ b/pkg/ttl/session/session_test.go @@ -0,0 +1,66 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package session_test + +import ( + "context" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/stretchr/testify/require" +) + +func TestSessionRunInTxn(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t(id int primary key, v int)") + se := session.NewSession(tk.Session(), tk.Session(), nil) + tk2 := testkit.NewTestKit(t, store) + tk2.MustExec("use test") + + require.NoError(t, se.RunInTxn(context.TODO(), func() error { + tk.MustExec("insert into t values (1, 10)") + return nil + }, session.TxnModeOptimistic)) + tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10")) + + err := se.RunInTxn(context.TODO(), func() error { + tk.MustExec("insert into t values (2, 20)") + return errors.New("mockErr") + }, session.TxnModeOptimistic) + require.EqualError(t, err, "mockErr") + tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10")) + + require.NoError(t, se.RunInTxn(context.TODO(), func() error { + tk.MustExec("insert into t values (3, 30)") + return nil + }, session.TxnModeOptimistic)) + tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10", "3 30")) +} + +func TestSessionResetTimeZone(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.time_zone='UTC'") + tk.MustExec("set @@time_zone='Asia/Shanghai'") + + se := session.NewSession(tk.Session(), tk.Session(), nil) + tk.MustQuery("select @@time_zone").Check(testkit.Rows("Asia/Shanghai")) + require.NoError(t, se.ResetWithGlobalTimeZone(context.TODO())) + tk.MustQuery("select @@time_zone").Check(testkit.Rows("UTC")) +} diff --git a/pkg/ttl/session/sysvar_test.go b/pkg/ttl/session/sysvar_test.go new file mode 100644 index 0000000000000..a234346acd277 --- /dev/null +++ b/pkg/ttl/session/sysvar_test.go @@ -0,0 +1,125 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package session_test + +import ( + "fmt" + "strconv" + "testing" + + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/stretchr/testify/require" +) + +func TestSysVarTTLJobEnable(t *testing.T) { + origEnableDDL := variable.EnableTTLJob.Load() + defer func() { + variable.EnableTTLJob.Store(origEnableDDL) + }() + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_ttl_job_enable=0") + require.False(t, variable.EnableTTLJob.Load()) + tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("0")) + tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("0")) + + tk.MustExec("set @@global.tidb_ttl_job_enable=1") + require.True(t, variable.EnableTTLJob.Load()) + tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("1")) + tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("1")) + + tk.MustExec("set @@global.tidb_ttl_job_enable=0") + require.False(t, variable.EnableTTLJob.Load()) + tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("0")) + tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("0")) +} + +func TestSysVarTTLScanBatchSize(t *testing.T) { + origScanBatchSize := variable.TTLScanBatchSize.Load() + defer func() { + variable.TTLScanBatchSize.Store(origScanBatchSize) + }() + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_ttl_scan_batch_size=789") + require.Equal(t, int64(789), variable.TTLScanBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows("789")) + tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows("789")) + + tk.MustExec("set @@global.tidb_ttl_scan_batch_size=0") + require.Equal(t, int64(1), variable.TTLScanBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows("1")) + tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows("1")) + + maxVal := int64(variable.DefTiDBTTLScanBatchMaxSize) + tk.MustExec(fmt.Sprintf("set @@global.tidb_ttl_scan_batch_size=%d", maxVal+1)) + require.Equal(t, maxVal, variable.TTLScanBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) + tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) +} + +func TestSysVarTTLScanDeleteBatchSize(t *testing.T) { + origScanBatchSize := variable.TTLScanBatchSize.Load() + defer func() { + variable.TTLScanBatchSize.Store(origScanBatchSize) + }() + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("set @@global.tidb_ttl_delete_batch_size=789") + require.Equal(t, int64(789), variable.TTLDeleteBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows("789")) + tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows("789")) + + tk.MustExec("set @@global.tidb_ttl_delete_batch_size=0") + require.Equal(t, int64(1), variable.TTLDeleteBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows("1")) + tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows("1")) + + maxVal := int64(variable.DefTiDBTTLDeleteBatchMaxSize) + tk.MustExec(fmt.Sprintf("set @@global.tidb_ttl_delete_batch_size=%d", maxVal+1)) + require.Equal(t, maxVal, variable.TTLDeleteBatchSize.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) + tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) +} + +func TestSysVarTTLScanDeleteLimit(t *testing.T) { + origDeleteLimit := variable.TTLDeleteRateLimit.Load() + defer func() { + variable.TTLDeleteRateLimit.Store(origDeleteLimit) + }() + + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) + + tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=100000") + require.Equal(t, int64(100000), variable.TTLDeleteRateLimit.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("100000")) + tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("100000")) + + tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=0") + require.Equal(t, int64(0), variable.TTLDeleteRateLimit.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) + tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) + + tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=-1") + require.Equal(t, int64(0), variable.TTLDeleteRateLimit.Load()) + tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) + tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) +} diff --git a/pkg/ttl/sqlbuilder/BUILD.bazel b/pkg/ttl/sqlbuilder/BUILD.bazel new file mode 100644 index 0000000000000..067fc11aeed2b --- /dev/null +++ b/pkg/ttl/sqlbuilder/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sqlbuilder", + srcs = ["sql.go"], + importpath = "github.com/pingcap/tidb/pkg/ttl/sqlbuilder", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/ttl/cache", + "//pkg/types", + "//pkg/util/sqlexec", + "@com_github_pkg_errors//:errors", + ], +) + +go_test( + name = "sqlbuilder_test", + timeout = "short", + srcs = [ + "main_test.go", + "sql_test.go", + ], + flaky = True, + shard_count = 5, + deps = [ + ":sqlbuilder", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/ttl/cache", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/sqlexec", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/ttl/sqlbuilder/main_test.go b/pkg/ttl/sqlbuilder/main_test.go new file mode 100644 index 0000000000000..a5bf3a3315e60 --- /dev/null +++ b/pkg/ttl/sqlbuilder/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package sqlbuilder_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/ttl/sqlbuilder/sql.go b/pkg/ttl/sqlbuilder/sql.go new file mode 100644 index 0000000000000..bf92dafab7cbf --- /dev/null +++ b/pkg/ttl/sqlbuilder/sql.go @@ -0,0 +1,482 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package sqlbuilder + +import ( + "encoding/hex" + "fmt" + "io" + "strconv" + "strings" + "time" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pkg/errors" +) + +func writeHex(in io.Writer, d types.Datum) error { + _, err := fmt.Fprintf(in, "x'%s'", hex.EncodeToString(d.GetBytes())) + return err +} + +func writeDatum(restoreCtx *format.RestoreCtx, d types.Datum, ft *types.FieldType) error { + switch ft.GetType() { + case mysql.TypeBit, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeTinyBlob: + return writeHex(restoreCtx.In, d) + case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeEnum, mysql.TypeSet: + if mysql.HasBinaryFlag(ft.GetFlag()) { + return writeHex(restoreCtx.In, d) + } + _, err := fmt.Fprintf(restoreCtx.In, "'%s'", sqlexec.EscapeString(d.GetString())) + return err + } + expr := ast.NewValueExpr(d.GetValue(), ft.GetCharset(), ft.GetCollate()) + return expr.Restore(restoreCtx) +} + +// FormatSQLDatum formats the datum to a value string in sql +func FormatSQLDatum(d types.Datum, ft *types.FieldType) (string, error) { + var sb strings.Builder + ctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) + if err := writeDatum(ctx, d, ft); err != nil { + return "", err + } + return sb.String(), nil +} + +type sqlBuilderState int + +const ( + writeBegin sqlBuilderState = iota + writeSelOrDel + writeWhere + writeOrderBy + writeLimit + writeDone +) + +// SQLBuilder is used to build SQLs for TTL +type SQLBuilder struct { + tbl *cache.PhysicalTable + sb strings.Builder + restoreCtx *format.RestoreCtx + state sqlBuilderState + + isReadOnly bool + hasWriteExpireCond bool +} + +// NewSQLBuilder creates a new TTLSQLBuilder +func NewSQLBuilder(tbl *cache.PhysicalTable) *SQLBuilder { + b := &SQLBuilder{tbl: tbl, state: writeBegin} + b.restoreCtx = format.NewRestoreCtx(format.DefaultRestoreFlags, &b.sb) + return b +} + +// Build builds the final sql +func (b *SQLBuilder) Build() (string, error) { + if b.state == writeBegin { + return "", errors.Errorf("invalid state: %v", b.state) + } + + if !b.isReadOnly && !b.hasWriteExpireCond { + // check whether the `timeRow < expire_time` condition has been written to make sure this SQL is safe. + return "", errors.New("expire condition not write") + } + + if b.state != writeDone { + b.state = writeDone + } + + return b.sb.String(), nil +} + +// WriteSelect writes a select statement to select key columns without any condition +func (b *SQLBuilder) WriteSelect() error { + if b.state != writeBegin { + return errors.Errorf("invalid state: %v", b.state) + } + b.restoreCtx.WritePlain("SELECT LOW_PRIORITY SQL_NO_CACHE ") + b.writeColNames(b.tbl.KeyColumns, false) + b.restoreCtx.WritePlain(" FROM ") + if err := b.writeTblName(); err != nil { + return err + } + if par := b.tbl.PartitionDef; par != nil { + b.restoreCtx.WritePlain(" PARTITION(") + b.restoreCtx.WriteName(par.Name.O) + b.restoreCtx.WritePlain(")") + } + b.state = writeSelOrDel + b.isReadOnly = true + return nil +} + +// WriteDelete writes a delete statement without any condition +func (b *SQLBuilder) WriteDelete() error { + if b.state != writeBegin { + return errors.Errorf("invalid state: %v", b.state) + } + b.restoreCtx.WritePlain("DELETE LOW_PRIORITY FROM ") + if err := b.writeTblName(); err != nil { + return err + } + if par := b.tbl.PartitionDef; par != nil { + b.restoreCtx.WritePlain(" PARTITION(") + b.restoreCtx.WriteName(par.Name.O) + b.restoreCtx.WritePlain(")") + } + b.state = writeSelOrDel + return nil +} + +// WriteCommonCondition writes a new condition +func (b *SQLBuilder) WriteCommonCondition(cols []*model.ColumnInfo, op string, dp []types.Datum) error { + switch b.state { + case writeSelOrDel: + b.restoreCtx.WritePlain(" WHERE ") + b.state = writeWhere + case writeWhere: + b.restoreCtx.WritePlain(" AND ") + default: + return errors.Errorf("invalid state: %v", b.state) + } + + b.writeColNames(cols, len(cols) > 1) + b.restoreCtx.WritePlain(" ") + b.restoreCtx.WritePlain(op) + b.restoreCtx.WritePlain(" ") + return b.writeDataPoint(cols, dp) +} + +// WriteExpireCondition writes a condition with the time column +func (b *SQLBuilder) WriteExpireCondition(expire time.Time) error { + switch b.state { + case writeSelOrDel: + b.restoreCtx.WritePlain(" WHERE ") + b.state = writeWhere + case writeWhere: + b.restoreCtx.WritePlain(" AND ") + default: + return errors.Errorf("invalid state: %v", b.state) + } + + b.writeColNames([]*model.ColumnInfo{b.tbl.TimeColumn}, false) + b.restoreCtx.WritePlain(" < ") + b.restoreCtx.WritePlain("FROM_UNIXTIME(") + b.restoreCtx.WritePlain(strconv.FormatInt(expire.Unix(), 10)) + b.restoreCtx.WritePlain(")") + b.hasWriteExpireCond = true + return nil +} + +// WriteInCondition writes an IN condition +func (b *SQLBuilder) WriteInCondition(cols []*model.ColumnInfo, dps ...[]types.Datum) error { + switch b.state { + case writeSelOrDel: + b.restoreCtx.WritePlain(" WHERE ") + b.state = writeWhere + case writeWhere: + b.restoreCtx.WritePlain(" AND ") + default: + return errors.Errorf("invalid state: %v", b.state) + } + + b.writeColNames(cols, len(cols) > 1) + b.restoreCtx.WritePlain(" IN ") + b.restoreCtx.WritePlain("(") + first := true + for _, v := range dps { + if first { + first = false + } else { + b.restoreCtx.WritePlain(", ") + } + if err := b.writeDataPoint(cols, v); err != nil { + return err + } + } + b.restoreCtx.WritePlain(")") + return nil +} + +// WriteOrderBy writes order by +func (b *SQLBuilder) WriteOrderBy(cols []*model.ColumnInfo, desc bool) error { + if b.state != writeSelOrDel && b.state != writeWhere { + return errors.Errorf("invalid state: %v", b.state) + } + b.state = writeOrderBy + b.restoreCtx.WritePlain(" ORDER BY ") + b.writeColNames(cols, false) + if desc { + b.restoreCtx.WritePlain(" DESC") + } else { + b.restoreCtx.WritePlain(" ASC") + } + return nil +} + +// WriteLimit writes the limit +func (b *SQLBuilder) WriteLimit(n int) error { + if b.state != writeSelOrDel && b.state != writeWhere && b.state != writeOrderBy { + return errors.Errorf("invalid state: %v", b.state) + } + b.state = writeLimit + b.restoreCtx.WritePlain(" LIMIT ") + b.restoreCtx.WritePlain(strconv.Itoa(n)) + return nil +} + +func (b *SQLBuilder) writeTblName() error { + tn := ast.TableName{Schema: b.tbl.Schema, Name: b.tbl.Name} + return tn.Restore(b.restoreCtx) +} + +func (b *SQLBuilder) writeColName(col *model.ColumnInfo) { + b.restoreCtx.WriteName(col.Name.O) +} + +func (b *SQLBuilder) writeColNames(cols []*model.ColumnInfo, writeBrackets bool) { + if writeBrackets { + b.restoreCtx.WritePlain("(") + } + + first := true + for _, col := range cols { + if first { + first = false + } else { + b.restoreCtx.WritePlain(", ") + } + b.writeColName(col) + } + + if writeBrackets { + b.restoreCtx.WritePlain(")") + } +} + +func (b *SQLBuilder) writeDataPoint(cols []*model.ColumnInfo, dp []types.Datum) error { + writeBrackets := len(cols) > 1 + if len(cols) != len(dp) { + return errors.Errorf("col count not match %d != %d", len(cols), len(dp)) + } + + if writeBrackets { + b.restoreCtx.WritePlain("(") + } + + first := true + for i, d := range dp { + if first { + first = false + } else { + b.restoreCtx.WritePlain(", ") + } + if err := writeDatum(b.restoreCtx, d, &cols[i].FieldType); err != nil { + return err + } + } + + if writeBrackets { + b.restoreCtx.WritePlain(")") + } + + return nil +} + +// ScanQueryGenerator generates SQLs for scan task +type ScanQueryGenerator struct { + tbl *cache.PhysicalTable + expire time.Time + keyRangeStart []types.Datum + keyRangeEnd []types.Datum + stack [][]types.Datum + limit int + firstBuild bool + exhausted bool +} + +// NewScanQueryGenerator creates a new ScanQueryGenerator +func NewScanQueryGenerator(tbl *cache.PhysicalTable, expire time.Time, + rangeStart, rangeEnd []types.Datum) (*ScanQueryGenerator, error) { + if err := tbl.ValidateKeyPrefix(rangeStart); err != nil { + return nil, err + } + + if err := tbl.ValidateKeyPrefix(rangeEnd); err != nil { + return nil, err + } + + return &ScanQueryGenerator{ + tbl: tbl, + expire: expire, + keyRangeStart: rangeStart, + keyRangeEnd: rangeEnd, + firstBuild: true, + }, nil +} + +// NextSQL creates next sql of the scan task +func (g *ScanQueryGenerator) NextSQL(continueFromResult [][]types.Datum, nextLimit int) (string, error) { + if g.exhausted { + return "", errors.New("generator is exhausted") + } + + if nextLimit <= 0 { + return "", errors.Errorf("invalid limit '%d'", nextLimit) + } + + defer func() { + g.firstBuild = false + }() + + if g.stack == nil { + g.stack = make([][]types.Datum, 0, len(g.tbl.KeyColumns)) + } + + if len(continueFromResult) >= g.limit { + var continueFromKey []types.Datum + if cnt := len(continueFromResult); cnt > 0 { + continueFromKey = continueFromResult[cnt-1] + } + if err := g.setStack(continueFromKey); err != nil { + return "", err + } + } else { + if l := len(g.stack); l > 0 { + g.stack = g.stack[:l-1] + } + if len(g.stack) == 0 { + g.exhausted = true + } + } + g.limit = nextLimit + return g.buildSQL() +} + +// IsExhausted returns whether the generator is exhausted +func (g *ScanQueryGenerator) IsExhausted() bool { + return g.exhausted +} + +func (g *ScanQueryGenerator) setStack(key []types.Datum) error { + if key == nil { + key = g.keyRangeStart + } + + if key == nil { + g.stack = g.stack[:0] + return nil + } + + if err := g.tbl.ValidateKeyPrefix(key); err != nil { + return err + } + + g.stack = g.stack[:len(key)] + for i := 0; i < len(key); i++ { + g.stack[i] = key[0 : i+1] + } + return nil +} + +func (g *ScanQueryGenerator) buildSQL() (string, error) { + if g.limit <= 0 { + return "", errors.Errorf("invalid limit '%d'", g.limit) + } + + if g.exhausted { + return "", nil + } + + b := NewSQLBuilder(g.tbl) + if err := b.WriteSelect(); err != nil { + return "", err + } + if len(g.stack) > 0 { + for i, d := range g.stack[len(g.stack)-1] { + col := []*model.ColumnInfo{g.tbl.KeyColumns[i]} + val := []types.Datum{d} + var err error + if i < len(g.stack)-1 { + err = b.WriteCommonCondition(col, "=", val) + } else if g.firstBuild { + // When `g.firstBuild == true`, that means we are querying rows after range start, because range is defined + // as [start, end), we should use ">=" to find the rows including start key. + err = b.WriteCommonCondition(col, ">=", val) + } else { + // Otherwise when `g.firstBuild != true`, that means we are continuing with the previous result, we should use + // ">" to exclude the previous row. + err = b.WriteCommonCondition(col, ">", val) + } + if err != nil { + return "", err + } + } + } + + if len(g.keyRangeEnd) > 0 { + if err := b.WriteCommonCondition(g.tbl.KeyColumns[0:len(g.keyRangeEnd)], "<", g.keyRangeEnd); err != nil { + return "", err + } + } + + if err := b.WriteExpireCondition(g.expire); err != nil { + return "", err + } + + if err := b.WriteOrderBy(g.tbl.KeyColumns, false); err != nil { + return "", err + } + + if err := b.WriteLimit(g.limit); err != nil { + return "", err + } + + return b.Build() +} + +// BuildDeleteSQL builds a delete SQL +func BuildDeleteSQL(tbl *cache.PhysicalTable, rows [][]types.Datum, expire time.Time) (string, error) { + if len(rows) == 0 { + return "", errors.New("Cannot build delete SQL with empty rows") + } + + b := NewSQLBuilder(tbl) + if err := b.WriteDelete(); err != nil { + return "", err + } + + if err := b.WriteInCondition(tbl.KeyColumns, rows...); err != nil { + return "", err + } + + if err := b.WriteExpireCondition(expire); err != nil { + return "", err + } + + if err := b.WriteLimit(len(rows)); err != nil { + return "", err + } + + return b.Build() +} diff --git a/pkg/ttl/sqlbuilder/sql_test.go b/pkg/ttl/sqlbuilder/sql_test.go new file mode 100644 index 0000000000000..967a9ea62c898 --- /dev/null +++ b/pkg/ttl/sqlbuilder/sql_test.go @@ -0,0 +1,949 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package sqlbuilder_test + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/sqlbuilder" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/stretchr/testify/require" +) + +func TestEscape(t *testing.T) { + tb := &cache.PhysicalTable{ + Schema: model.NewCIStr("testp;\"';123`456"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("tp\"';123`456"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("col1\"';123`456"), FieldType: *types.NewFieldType(mysql.TypeString)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time\"';123`456"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + PartitionDef: &model.PartitionDefinition{ + Name: model.NewCIStr("p1\"';123`456"), + }, + } + + buildSelect := func(d []types.Datum) string { + b := sqlbuilder.NewSQLBuilder(tb) + require.NoError(t, b.WriteSelect()) + require.NoError(t, b.WriteCommonCondition(tb.KeyColumns, ">", d)) + require.NoError(t, b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + s, err := b.Build() + require.NoError(t, err) + return s + } + + buildDelete := func(ds ...[]types.Datum) string { + b := sqlbuilder.NewSQLBuilder(tb) + require.NoError(t, b.WriteDelete()) + require.NoError(t, b.WriteInCondition(tb.KeyColumns, ds...)) + require.NoError(t, b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + s, err := b.Build() + require.NoError(t, err) + return s + } + + cases := []struct { + tp string + ds [][]types.Datum + sql string + }{ + { + tp: "select", + ds: [][]types.Datum{d("key1'\";123`456")}, + sql: "SELECT LOW_PRIORITY SQL_NO_CACHE `col1\"';123``456` FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` > 'key1\\'\\\";123`456' AND `time\"';123``456` < FROM_UNIXTIME(0)", + }, + { + tp: "delete", + ds: [][]types.Datum{d("key2'\";123`456")}, + sql: "DELETE LOW_PRIORITY FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` IN ('key2\\'\\\";123`456') AND `time\"';123``456` < FROM_UNIXTIME(0)", + }, + { + tp: "delete", + ds: [][]types.Datum{d("key3'\";123`456"), d("key4'`\"")}, + sql: "DELETE LOW_PRIORITY FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` IN ('key3\\'\\\";123`456', 'key4\\'`\\\"') AND `time\"';123``456` < FROM_UNIXTIME(0)", + }, + } + + for _, c := range cases { + switch c.tp { + case "select": + require.Equal(t, 1, len(c.ds)) + require.Equal(t, c.sql, buildSelect(c.ds[0])) + case "delete": + require.Equal(t, c.sql, buildDelete(c.ds...)) + default: + require.FailNow(t, "invalid tp: %s", c.tp) + } + + p := parser.New() + stmts, _, err := p.Parse(c.sql, "", "") + require.Equal(t, 1, len(stmts)) + require.NoError(t, err) + + var tbName *ast.TableName + var keyColumnName, timeColumnName string + var values []string + var timeFunc string + var timeTS int64 + switch c.tp { + case "select": + stmt, ok := stmts[0].(*ast.SelectStmt) + require.True(t, ok) + tbName = stmt.From.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) + and := stmt.Where.(*ast.BinaryOperationExpr) + cond1 := and.L.(*ast.BinaryOperationExpr) + keyColumnName = cond1.L.(*ast.ColumnNameExpr).Name.Name.O + values = []string{cond1.R.(ast.ValueExpr).GetValue().(string)} + cond2 := and.R.(*ast.BinaryOperationExpr) + timeColumnName = cond2.L.(*ast.ColumnNameExpr).Name.Name.O + timeFunc = cond2.R.(*ast.FuncCallExpr).FnName.L + timeTS = cond2.R.(*ast.FuncCallExpr).Args[0].(ast.ValueExpr).GetValue().(int64) + case "delete": + stmt, ok := stmts[0].(*ast.DeleteStmt) + require.True(t, ok) + tbName = stmt.TableRefs.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) + and := stmt.Where.(*ast.BinaryOperationExpr) + cond1 := and.L.(*ast.PatternInExpr) + keyColumnName = cond1.Expr.(*ast.ColumnNameExpr).Name.Name.O + require.Equal(t, len(c.ds), len(cond1.List)) + values = make([]string, 0, len(c.ds)) + for _, expr := range cond1.List { + values = append(values, expr.(ast.ValueExpr).GetValue().(string)) + } + cond2 := and.R.(*ast.BinaryOperationExpr) + timeColumnName = cond2.L.(*ast.ColumnNameExpr).Name.Name.O + timeFunc = cond2.R.(*ast.FuncCallExpr).FnName.L + timeTS = cond2.R.(*ast.FuncCallExpr).Args[0].(ast.ValueExpr).GetValue().(int64) + default: + require.FailNow(t, "invalid tp: %s", c.tp) + } + + require.Equal(t, tb.Schema.O, tbName.Schema.O) + require.Equal(t, tb.Name.O, tbName.Name.O) + require.Equal(t, 1, len(tbName.PartitionNames)) + require.Equal(t, tb.PartitionDef.Name.O, tbName.PartitionNames[0].O) + require.Equal(t, tb.KeyColumns[0].Name.O, keyColumnName) + require.Equal(t, tb.TimeColumn.Name.O, timeColumnName) + for i, row := range c.ds { + require.Equal(t, row[0].GetString(), values[i]) + } + require.Equal(t, "from_unixtime", timeFunc) + require.Equal(t, int64(0), timeTS) + } +} + +func TestFormatSQLDatum(t *testing.T) { + // invalid pk types contains the types that should not exist in primary keys of a TTL table. + // We do not need to check sqlbuilder.FormatSQLDatum for these types + invalidPKTypes := []struct { + types []string + err *terror.Error + }{ + { + types: []string{"json"}, + err: dbterror.ErrJSONUsedAsKey, + }, + { + types: []string{"blob"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"blob(8)"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"text"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"text(8)"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"int", "json"}, + err: dbterror.ErrJSONUsedAsKey, + }, + { + types: []string{"int", "blob"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"int", "text"}, + err: dbterror.ErrBlobKeyWithoutLength, + }, + { + types: []string{"float"}, + err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, + }, + { + types: []string{"double"}, + err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, + }, + { + types: []string{"int", "float"}, + err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, + }, + { + types: []string{"int", "double"}, + err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, + }, + } + + cases := []struct { + ft string + values []interface{} + hex bool + }{ + { + ft: "int", + values: []interface{}{1, 2, 3, -12}, + }, + { + ft: "decimal(5, 2)", + values: []interface{}{"0.3", "128.71", "-245.32"}, + }, + { + ft: "varchar(32) CHARACTER SET latin1", + values: []interface{}{ + "aa';delete from t where 1;", + string([]byte{0xf1, 0xf2}), + string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), + }, + }, + { + ft: "char(32) CHARACTER SET utf8mb4", + values: []interface{}{ + "demo", + "\n123", + "aa';delete from t where 1;", + "你好👋", + }, + }, + { + ft: "varchar(32) CHARACTER SET utf8mb4", + values: []interface{}{ + "demo", + "aa';delete from t where 1;", + "你好👋", + }, + }, + { + ft: "varchar(32) CHARACTER SET binary", + values: []interface{}{ + string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), + "你好👋", + "abcdef", + }, + hex: true, + }, + { + ft: "binary(8)", + values: []interface{}{ + string([]byte{0xf1, 0xf2}), + string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), + }, + hex: true, + }, + { + ft: "blob", + values: []interface{}{ + string([]byte{0xf1, 0xf2}), + string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), + }, + hex: true, + }, + { + ft: "bit(1)", + values: []interface{}{0, 1}, + hex: true, + }, + { + ft: "date", + values: []interface{}{"2022-01-02", "1900-12-31"}, + }, + { + ft: "time", + values: []interface{}{"00:00", "01:23", "13:51:22"}, + }, + { + ft: "datetime", + values: []interface{}{"2022-01-02 12:11:11", "2022-01-02"}, + }, + { + ft: "datetime(6)", + values: []interface{}{"2022-01-02 12:11:11.123456"}, + }, + { + ft: "timestamp", + values: []interface{}{"2022-01-02 12:11:11", "2022-01-02"}, + }, + { + ft: "timestamp(6)", + values: []interface{}{"2022-01-02 12:11:11.123456"}, + }, + { + ft: "enum('e1', 'e2', \"e3'\", 'e4\"', ';你好👋')", + values: []interface{}{"e1", "e2", "e3'", "e4\"", ";你好👋"}, + }, + { + ft: "set('e1', 'e2', \"e3'\", 'e4\"', ';你好👋')", + values: []interface{}{"", "e1", "e2", "e3'", "e4\"", ";你好👋"}, + }, + } + + store, do := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + + for _, c := range invalidPKTypes { + var sb strings.Builder + sb.WriteString("create table t(") + cols := make([]string, 0, len(invalidPKTypes)) + for i, tp := range c.types { + colName := fmt.Sprintf("pk%d", i) + cols = append(cols, colName) + sb.WriteString(colName) + sb.WriteString(" ") + sb.WriteString(tp) + sb.WriteString(", ") + } + sb.WriteString("t timestamp, ") + sb.WriteString("primary key (") + sb.WriteString(strings.Join(cols, ", ")) + sb.WriteString(")) TTL=`t` + INTERVAL 1 DAY") + tk.MustGetDBError(sb.String(), c.err) + } + + // create a table with n columns + var sb strings.Builder + sb.WriteString("CREATE TABLE t (id varchar(32) primary key") + for i, c := range cases { + _, err := fmt.Fprintf(&sb, ",\n col%d %s DEFAULT NULL", i, c.ft) + require.NoError(t, err) + } + sb.WriteString("\n);") + tk.MustExec(sb.String()) + + tbl, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) + require.NoError(t, err) + + for i, c := range cases { + for j, v := range c.values { + tk.MustExec(fmt.Sprintf("insert into t (id, col%d) values ('%d-%d', ?)", i, i, j), v) + } + } + + ctx := kv.WithInternalSourceType(context.TODO(), kv.InternalTxnOthers) + for i, c := range cases { + for j := range c.values { + rowID := fmt.Sprintf("%d-%d", i, j) + colName := fmt.Sprintf("col%d", i) + exec, ok := tk.Session().(sqlexec.SQLExecutor) + require.True(t, ok) + selectSQL := fmt.Sprintf("select %s from t where id='%s'", colName, rowID) + rs, err := exec.ExecuteInternal(ctx, selectSQL) + require.NoError(t, err, selectSQL) + rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) + require.NoError(t, err, selectSQL) + require.Equal(t, 1, len(rows), selectSQL) + col := tbl.Meta().FindPublicColumnByName(colName) + d := rows[0].GetDatum(0, &col.FieldType) + s, err := sqlbuilder.FormatSQLDatum(d, &col.FieldType) + require.NoError(t, err) + tk.MustQuery("select id from t where " + colName + "=" + s).Check(testkit.Rows(rowID)) + if c.hex { + require.True(t, strings.HasPrefix(s, "x'"), "ft: %s, got: %s", c.ft, s) + } + } + } +} + +func TestSQLBuilder(t *testing.T) { + must := func(err error) { + require.NoError(t, err) + } + + mustBuild := func(b *sqlbuilder.SQLBuilder, str string) { + s, err := b.Build() + require.NoError(t, err) + require.Equal(t, str, s) + } + + var b *sqlbuilder.SQLBuilder + + t1 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t1"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + t2 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test2"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t2"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, + {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + tp := &cache.PhysicalTable{ + Schema: model.NewCIStr("testp"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("tp"), + }, + KeyColumns: t1.KeyColumns, + TimeColumn: t1.TimeColumn, + PartitionDef: &model.PartitionDefinition{ + Name: model.NewCIStr("p1"), + }, + } + + // test build select queries + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1`") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1'") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) + must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("c3"))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1' AND `id` <= 'c3'") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + shLoc, err := time.LoadLocation("Asia/Shanghai") + require.NoError(t, err) + must(b.WriteExpireCondition(time.UnixMilli(0).In(shLoc))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0)") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) + must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("c3"))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1' AND `id` <= 'c3' AND `time` < FROM_UNIXTIME(0)") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteOrderBy(t1.KeyColumns, false)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` ASC") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteOrderBy(t1.KeyColumns, true)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` DESC") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteOrderBy(t1.KeyColumns, false)) + must(b.WriteLimit(128)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` ASC LIMIT 128") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t1.KeyColumns, ">", d("';``~?%\"\n"))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > '\\';``~?%\\\"\\n'") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1';'"))) + must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("a2\""))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + must(b.WriteOrderBy(t1.KeyColumns, false)) + must(b.WriteLimit(128)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1\\';\\'' AND `id` <= 'a2\\\"' AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 128") + + b = sqlbuilder.NewSQLBuilder(t2) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t2.KeyColumns, ">", d("x1", 20))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE (`a`, `b`) > ('x1', 20)") + + b = sqlbuilder.NewSQLBuilder(t2) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t2.KeyColumns, "<=", d("x2", 21))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + must(b.WriteOrderBy(t2.KeyColumns, false)) + must(b.WriteLimit(100)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE (`a`, `b`) <= ('x2', 21) AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b` ASC LIMIT 100") + + b = sqlbuilder.NewSQLBuilder(t2) + must(b.WriteSelect()) + must(b.WriteCommonCondition(t2.KeyColumns[0:1], "=", d("x3"))) + must(b.WriteCommonCondition(t2.KeyColumns[1:2], ">", d(31))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + must(b.WriteOrderBy(t2.KeyColumns, false)) + must(b.WriteLimit(100)) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE `a` = 'x3' AND `b` > 31 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b` ASC LIMIT 100") + + // test build delete queries + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + _, err = b.Build() + require.EqualError(t, err, "expire condition not write") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + must(b.WriteInCondition(t1.KeyColumns, d("a"))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN ('a') AND `time` < FROM_UNIXTIME(0)") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + must(b.WriteInCondition(t1.KeyColumns, d("a"), d("b"))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN ('a', 'b') AND `time` < FROM_UNIXTIME(0)") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + must(b.WriteInCondition(t2.KeyColumns, d("a", 1))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + must(b.WriteLimit(100)) + mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1)) AND `time` < FROM_UNIXTIME(0) LIMIT 100") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + must(b.WriteInCondition(t2.KeyColumns, d("a", 1), d("b", 2))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + must(b.WriteLimit(100)) + mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1), ('b', 2)) AND `time` < FROM_UNIXTIME(0) LIMIT 100") + + b = sqlbuilder.NewSQLBuilder(t1) + must(b.WriteDelete()) + must(b.WriteInCondition(t2.KeyColumns, d("a", 1), d("b", 2))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1), ('b', 2)) AND `time` < FROM_UNIXTIME(0)") + + // test select partition table + b = sqlbuilder.NewSQLBuilder(tp) + must(b.WriteSelect()) + must(b.WriteCommonCondition(tp.KeyColumns, ">", d("a1"))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `testp`.`tp` PARTITION(`p1`) WHERE `id` > 'a1' AND `time` < FROM_UNIXTIME(0)") + + b = sqlbuilder.NewSQLBuilder(tp) + must(b.WriteDelete()) + must(b.WriteInCondition(tp.KeyColumns, d("a"), d("b"))) + must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) + mustBuild(b, "DELETE LOW_PRIORITY FROM `testp`.`tp` PARTITION(`p1`) WHERE `id` IN ('a', 'b') AND `time` < FROM_UNIXTIME(0)") +} + +func TestScanQueryGenerator(t *testing.T) { + t1 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t1"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + t2 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test2"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t2"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, + {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, + {Name: model.NewCIStr("c"), FieldType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetFlag(mysql.BinaryFlag).Build()}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + result := func(last []types.Datum, n int) [][]types.Datum { + ds := make([][]types.Datum, n) + ds[n-1] = last + return ds + } + + cases := []struct { + tbl *cache.PhysicalTable + expire time.Time + rangeStart []types.Datum + rangeEnd []types.Datum + path [][]interface{} + }{ + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 3, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", + }, + { + nil, 5, "", + }, + }, + }, + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 3, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", + }, + { + [][]types.Datum{}, 5, "", + }, + }, + }, + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + rangeStart: d(1), + rangeEnd: d(100), + path: [][]interface{}{ + { + nil, 3, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` >= 1 AND `id` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", + }, + { + result(d(10), 3), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 10 AND `id` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 5", + }, + { + result(d(15), 4), 5, + "", + }, + }, + }, + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 3, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", + }, + { + result(d(2), 3), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 2 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 5", + }, + { + result(d(4), 5), 6, + "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 4 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 6", + }, + { + result(d(7), 5), 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + nil, 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + nil, 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + [][]types.Datum{}, 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0xf0}), 4), 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + rangeStart: d(1, "x", []byte{0xe}), + rangeEnd: d(100, "z", []byte{0xff}), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` >= x'0e' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x1a}), 5), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x20}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "y", []byte{0x0a}), 5), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'y' AND `c` > x'0a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "y", []byte{0x11}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'y' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "z", []byte{0x02}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(3, "a", []byte{0x01}), 5), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 3 AND `b` = 'a' AND `c` > x'01' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(3, "a", []byte{0x11}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 3 AND `b` > 'a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(3, "c", []byte{0x12}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 3 AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(5, "e", []byte{0xa1}), 4), 5, "", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + rangeStart: d(1), + rangeEnd: d(100), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` >= 1 AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x1a}), 5), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x20}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "y", []byte{0x0a}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + }, + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + rangeStart: d(1, "x"), + rangeEnd: d(100, "z"), + path: [][]interface{}{ + { + nil, 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` >= 'x' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x1a}), 5), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "x", []byte{0x20}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + { + result(d(1, "y", []byte{0x0a}), 4), 5, + "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", + }, + }, + }, + } + + for i, c := range cases { + g, err := sqlbuilder.NewScanQueryGenerator(c.tbl, c.expire, c.rangeStart, c.rangeEnd) + require.NoError(t, err, fmt.Sprintf("%d", i)) + for j, p := range c.path { + msg := fmt.Sprintf("%d-%d", i, j) + var result [][]types.Datum + require.Equal(t, 3, len(p), msg) + if arg := p[0]; arg != nil { + r, ok := arg.([][]types.Datum) + require.True(t, ok, msg) + result = r + } + limit, ok := p[1].(int) + require.True(t, ok, msg) + sql, ok := p[2].(string) + require.True(t, ok, msg) + s, err := g.NextSQL(result, limit) + require.NoError(t, err, msg) + require.Equal(t, sql, s, msg) + require.Equal(t, s == "", g.IsExhausted(), msg) + } + } +} + +func TestBuildDeleteSQL(t *testing.T) { + t1 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t1"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + t2 := &cache.PhysicalTable{ + Schema: model.NewCIStr("test2"), + TableInfo: &model.TableInfo{ + Name: model.NewCIStr("t2"), + }, + KeyColumns: []*model.ColumnInfo{ + {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, + {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, + }, + TimeColumn: &model.ColumnInfo{ + Name: model.NewCIStr("time"), + FieldType: *types.NewFieldType(mysql.TypeDatetime), + }, + } + + cases := []struct { + tbl *cache.PhysicalTable + expire time.Time + rows [][]types.Datum + sql string + }{ + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + rows: [][]types.Datum{d(1)}, + sql: "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN (1) AND `time` < FROM_UNIXTIME(0) LIMIT 1", + }, + { + tbl: t1, + expire: time.UnixMilli(0).In(time.UTC), + rows: [][]types.Datum{d(2), d(3), d(4)}, + sql: "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN (2, 3, 4) AND `time` < FROM_UNIXTIME(0) LIMIT 3", + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + rows: [][]types.Datum{d(1, "a")}, + sql: "DELETE LOW_PRIORITY FROM `test2`.`t2` WHERE (`a`, `b`) IN ((1, 'a')) AND `time` < FROM_UNIXTIME(0) LIMIT 1", + }, + { + tbl: t2, + expire: time.UnixMilli(0).In(time.UTC), + rows: [][]types.Datum{d(1, "a"), d(2, "b")}, + sql: "DELETE LOW_PRIORITY FROM `test2`.`t2` WHERE (`a`, `b`) IN ((1, 'a'), (2, 'b')) AND `time` < FROM_UNIXTIME(0) LIMIT 2", + }, + } + + for _, c := range cases { + sql, err := sqlbuilder.BuildDeleteSQL(c.tbl, c.rows, c.expire) + require.NoError(t, err) + require.Equal(t, c.sql, sql) + } +} + +func d(vs ...interface{}) []types.Datum { + datums := make([]types.Datum, len(vs)) + for i, v := range vs { + switch val := v.(type) { + case string: + datums[i] = types.NewStringDatum(val) + case int: + datums[i] = types.NewIntDatum(int64(val)) + case []byte: + datums[i] = types.NewBytesDatum(val) + default: + panic(fmt.Sprintf("invalid value type: %T, value: %v", v, v)) + } + } + return datums +} diff --git a/pkg/ttl/ttlworker/BUILD.bazel b/pkg/ttl/ttlworker/BUILD.bazel new file mode 100644 index 0000000000000..008710c69d727 --- /dev/null +++ b/pkg/ttl/ttlworker/BUILD.bazel @@ -0,0 +1,112 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ttlworker", + srcs = [ + "config.go", + "del.go", + "job.go", + "job_manager.go", + "scan.go", + "session.go", + "task_manager.go", + "timer.go", + "timer_sync.go", + "worker.go", + ], + importpath = "github.com/pingcap/tidb/pkg/ttl/ttlworker", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/store/driver/error", + "//pkg/timer/api", + "//pkg/timer/runtime", + "//pkg/timer/tablestore", + "//pkg/ttl/cache", + "//pkg/ttl/client", + "//pkg/ttl/metrics", + "//pkg/ttl/session", + "//pkg/ttl/sqlbuilder", + "//pkg/types", + "//pkg/util", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/sqlexec", + "//pkg/util/timeutil", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@io_etcd_go_etcd_client_v3//:client", + "@org_golang_x_exp//maps", + "@org_golang_x_time//rate", + "@org_uber_go_multierr//:multierr", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "ttlworker_test", + timeout = "moderate", + srcs = [ + "del_test.go", + "job_manager_integration_test.go", + "job_manager_test.go", + "scan_test.go", + "session_test.go", + "task_manager_integration_test.go", + "task_manager_test.go", + "timer_sync_test.go", + "timer_test.go", + ], + embed = [":ttlworker"], + flaky = True, + race = "on", + shard_count = 46, + deps = [ + "//pkg/domain", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/statistics/handle/autoanalyze", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/timer/api", + "//pkg/timer/tablestore", + "//pkg/ttl/cache", + "//pkg/ttl/client", + "//pkg/ttl/metrics", + "//pkg/ttl/session", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/logutil", + "//pkg/util/mock", + "@com_github_google_uuid//:uuid", + "@com_github_ngaut_pools//:pools", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_prometheus_client_model//go", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//mock", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//testutils", + "@com_github_tikv_client_go_v2//tikv", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_golang_x_time//rate", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) diff --git a/ttl/ttlworker/config.go b/pkg/ttl/ttlworker/config.go similarity index 100% rename from ttl/ttlworker/config.go rename to pkg/ttl/ttlworker/config.go diff --git a/ttl/ttlworker/del.go b/pkg/ttl/ttlworker/del.go similarity index 95% rename from ttl/ttlworker/del.go rename to pkg/ttl/ttlworker/del.go index a578f75adbd1e..c0f67b854beb3 100644 --- a/ttl/ttlworker/del.go +++ b/pkg/ttl/ttlworker/del.go @@ -22,13 +22,13 @@ import ( "sync/atomic" "time" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/ttl/sqlbuilder" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/ttl/sqlbuilder" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "golang.org/x/time/rate" ) diff --git a/ttl/ttlworker/del_test.go b/pkg/ttl/ttlworker/del_test.go similarity index 98% rename from ttl/ttlworker/del_test.go rename to pkg/ttl/ttlworker/del_test.go index 524b45c9c8806..d0f06792da90d 100644 --- a/ttl/ttlworker/del_test.go +++ b/pkg/ttl/ttlworker/del_test.go @@ -21,10 +21,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" "golang.org/x/time/rate" ) diff --git a/pkg/ttl/ttlworker/job.go b/pkg/ttl/ttlworker/job.go new file mode 100644 index 0000000000000..c937fa9037802 --- /dev/null +++ b/pkg/ttl/ttlworker/job.go @@ -0,0 +1,157 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ttlworker + +import ( + "context" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +const updateJobCurrentStatusTemplate = "UPDATE mysql.tidb_ttl_table_status SET current_job_status = %? WHERE table_id = %? AND current_job_status = %? AND current_job_id = %?" +const finishJobTemplate = `UPDATE mysql.tidb_ttl_table_status + SET last_job_id = current_job_id, + last_job_start_time = current_job_start_time, + last_job_finish_time = %?, + last_job_ttl_expire = current_job_ttl_expire, + last_job_summary = %?, + current_job_id = NULL, + current_job_owner_id = NULL, + current_job_owner_hb_time = NULL, + current_job_start_time = NULL, + current_job_ttl_expire = NULL, + current_job_state = NULL, + current_job_status = NULL, + current_job_status_update_time = NULL + WHERE table_id = %? AND current_job_id = %?` +const removeTaskForJobTemplate = "DELETE FROM mysql.tidb_ttl_task WHERE job_id = %?" +const createJobHistoryRowTemplate = `INSERT INTO + mysql.tidb_ttl_job_history ( + job_id, + table_id, + parent_table_id, + table_schema, + table_name, + partition_name, + create_time, + finish_time, + ttl_expire, + status + ) +VALUES + (%?, %?, %?, %?, %?, %?, %?, FROM_UNIXTIME(1), %?, %?)` +const finishJobHistoryTemplate = `UPDATE mysql.tidb_ttl_job_history + SET finish_time = %?, + summary_text = %?, + expired_rows = %?, + deleted_rows = %?, + error_delete_rows = %?, + status = %? + WHERE job_id = %?` + +func updateJobCurrentStatusSQL(tableID int64, oldStatus cache.JobStatus, newStatus cache.JobStatus, jobID string) (string, []interface{}) { + return updateJobCurrentStatusTemplate, []interface{}{string(newStatus), tableID, string(oldStatus), jobID} +} + +func finishJobSQL(tableID int64, finishTime time.Time, summary string, jobID string) (string, []interface{}) { + return finishJobTemplate, []interface{}{finishTime.Format(timeFormat), summary, tableID, jobID} +} + +func removeTaskForJob(jobID string) (string, []interface{}) { + return removeTaskForJobTemplate, []interface{}{jobID} +} + +func createJobHistorySQL(jobID string, tbl *cache.PhysicalTable, expire time.Time, now time.Time) (string, []interface{}) { + var partitionName interface{} + if tbl.Partition.O != "" { + partitionName = tbl.Partition.O + } + + return createJobHistoryRowTemplate, []interface{}{ + jobID, + tbl.ID, + tbl.TableInfo.ID, + tbl.Schema.O, + tbl.Name.O, + partitionName, + now.Format(timeFormat), + expire.Format(timeFormat), + string(cache.JobStatusRunning), + } +} + +func finishJobHistorySQL(jobID string, finishTime time.Time, summary *TTLSummary) (string, []interface{}) { + return finishJobHistoryTemplate, []interface{}{ + finishTime.Format(timeFormat), + summary.SummaryText, + summary.TotalRows, + summary.SuccessRows, + summary.ErrorRows, + string(cache.JobStatusFinished), + jobID, + } +} + +type ttlJob struct { + id string + ownerID string + + createTime time.Time + ttlExpireTime time.Time + + tbl *cache.PhysicalTable + + // status is the only field which should be protected by a mutex, as `Cancel` may be called at any time, and will + // change the status + statusMutex sync.Mutex + status cache.JobStatus +} + +// finish turns current job into last job, and update the error message and statistics summary +func (job *ttlJob) finish(se session.Session, now time.Time, summary *TTLSummary) { + // at this time, the job.ctx may have been canceled (to cancel this job) + // even when it's canceled, we'll need to update the states, so use another context + err := se.RunInTxn(context.TODO(), func() error { + sql, args := finishJobSQL(job.tbl.ID, now, summary.SummaryText, job.id) + _, err := se.ExecuteSQL(context.TODO(), sql, args...) + if err != nil { + return errors.Wrapf(err, "execute sql: %s", sql) + } + + sql, args = removeTaskForJob(job.id) + _, err = se.ExecuteSQL(context.TODO(), sql, args...) + if err != nil { + return errors.Wrapf(err, "execute sql: %s", sql) + } + + sql, args = finishJobHistorySQL(job.id, now, summary) + _, err = se.ExecuteSQL(context.TODO(), sql, args...) + if err != nil { + return errors.Wrapf(err, "execute sql: %s", sql) + } + + return nil + }, session.TxnModeOptimistic) + + if err != nil { + logutil.BgLogger().Error("fail to finish a ttl job", zap.Error(err), zap.Int64("tableID", job.tbl.ID), zap.String("jobID", job.id)) + } +} diff --git a/ttl/ttlworker/job_manager.go b/pkg/ttl/ttlworker/job_manager.go similarity index 98% rename from ttl/ttlworker/job_manager.go rename to pkg/ttl/ttlworker/job_manager.go index 2eff07f4b5ded..b01320f342f45 100644 --- a/ttl/ttlworker/job_manager.go +++ b/pkg/ttl/ttlworker/job_manager.go @@ -23,18 +23,18 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - timerapi "github.com/pingcap/tidb/timer/api" - ttltablestore "github.com/pingcap/tidb/timer/tablestore" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/client" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + ttltablestore "github.com/pingcap/tidb/pkg/timer/tablestore" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/client" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/multierr" "go.uber.org/zap" diff --git a/ttl/ttlworker/job_manager_integration_test.go b/pkg/ttl/ttlworker/job_manager_integration_test.go similarity index 93% rename from ttl/ttlworker/job_manager_integration_test.go rename to pkg/ttl/ttlworker/job_manager_integration_test.go index 33e6ac142a6e7..fc8162e6e5002 100644 --- a/ttl/ttlworker/job_manager_integration_test.go +++ b/pkg/ttl/ttlworker/job_manager_integration_test.go @@ -27,21 +27,21 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - dbsession "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/testkit" - timerapi "github.com/pingcap/tidb/timer/api" - timertable "github.com/pingcap/tidb/timer/tablestore" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/client" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/ttl/ttlworker" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + dbsession "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/statistics/handle/autoanalyze" + "github.com/pingcap/tidb/pkg/testkit" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + timertable "github.com/pingcap/tidb/pkg/timer/tablestore" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/client" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/ttl/ttlworker" + "github.com/pingcap/tidb/pkg/util/logutil" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" "go.uber.org/atomic" @@ -169,14 +169,14 @@ func TestFinishJob(t *testing.T) { } func TestTTLAutoAnalyze(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/update-info-schema-cache-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/update-info-schema-cache-interval") - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/update-status-table-cache-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/update-status-table-cache-interval") - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/resize-workers-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/resize-workers-interval") - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-info-schema-cache-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-info-schema-cache-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-status-table-cache-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-status-table-cache-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/resize-workers-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/resize-workers-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval") originAutoAnalyzeMinCnt := autoanalyze.AutoAnalyzeMinCnt autoanalyze.AutoAnalyzeMinCnt = 0 @@ -230,8 +230,8 @@ func TestTTLAutoAnalyze(t *testing.T) { } func TestTriggerTTLJob(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval") ctx, cancel := context.WithTimeout(context.TODO(), 5*time.Minute) defer cancel() @@ -280,8 +280,8 @@ func TestTriggerTTLJob(t *testing.T) { } func TestTTLDeleteWithTimeZoneChange(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/task-manager-loop-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/task-manager-loop-interval") store, do := testkit.CreateMockStoreAndDomain(t) timerStore := timertable.NewTableTimerStore(0, do.SysSessionPool(), "mysql", "tidb_timers", nil) @@ -357,12 +357,12 @@ func waitTTLJobFinished(t *testing.T, tk *testkit.TestKit, tableID int64, timerC } func TestTTLJobDisable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/update-info-schema-cache-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/update-info-schema-cache-interval") - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/update-status-table-cache-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/update-status-table-cache-interval") - failpoint.Enable("github.com/pingcap/tidb/ttl/ttlworker/resize-workers-interval", fmt.Sprintf("return(%d)", time.Second)) - defer failpoint.Disable("github.com/pingcap/tidb/ttl/ttlworker/resize-workers-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-info-schema-cache-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-info-schema-cache-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-status-table-cache-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/update-status-table-cache-interval") + failpoint.Enable("github.com/pingcap/tidb/pkg/ttl/ttlworker/resize-workers-interval", fmt.Sprintf("return(%d)", time.Second)) + defer failpoint.Disable("github.com/pingcap/tidb/pkg/ttl/ttlworker/resize-workers-interval") originAutoAnalyzeMinCnt := autoanalyze.AutoAnalyzeMinCnt autoanalyze.AutoAnalyzeMinCnt = 0 diff --git a/ttl/ttlworker/job_manager_test.go b/pkg/ttl/ttlworker/job_manager_test.go similarity index 98% rename from ttl/ttlworker/job_manager_test.go rename to pkg/ttl/ttlworker/job_manager_test.go index 42b3d528e731a..1fe7b48e476a3 100644 --- a/ttl/ttlworker/job_manager_test.go +++ b/pkg/ttl/ttlworker/job_manager_test.go @@ -21,13 +21,13 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - timerapi "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/ttl/ttlworker/scan.go b/pkg/ttl/ttlworker/scan.go similarity index 96% rename from ttl/ttlworker/scan.go rename to pkg/ttl/ttlworker/scan.go index 37855d0f2e238..6eb52dc431f4e 100644 --- a/ttl/ttlworker/scan.go +++ b/pkg/ttl/ttlworker/scan.go @@ -22,14 +22,14 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/sqlbuilder" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/sqlbuilder" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/ttl/ttlworker/scan_test.go b/pkg/ttl/ttlworker/scan_test.go similarity index 98% rename from ttl/ttlworker/scan_test.go rename to pkg/ttl/ttlworker/scan_test.go index 5b776fec21920..9c236bb836bdc 100644 --- a/ttl/ttlworker/scan_test.go +++ b/pkg/ttl/ttlworker/scan_test.go @@ -21,11 +21,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/pkg/ttl/ttlworker/session.go b/pkg/ttl/ttlworker/session.go new file mode 100644 index 0000000000000..b9085f4a1d668 --- /dev/null +++ b/pkg/ttl/ttlworker/session.go @@ -0,0 +1,248 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ttlworker + +import ( + "context" + "fmt" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// The following two functions are using `sqlexec.SQLExecutor` to represent session +// which is actually not correct. It's a work around for the cyclic dependency problem. +// It actually doesn't accept arbitrary SQLExecutor, but just `*session.session`, which means +// you cannot pass the `(ttl/session).Session` into it. +// Use `sqlexec.SQLExecutor` and `sessionctx.Session` or another other interface (including +// `interface{}`) here is the same, I just pick one small enough interface. +// Also, we cannot use the functions in `session/session.go` (to avoid cyclic dependency), so +// registering function here is really needed. + +// AttachStatsCollector attaches the stats collector for the session. +// this function is registered in BootstrapSession in /session/session.go +var AttachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { + return s +} + +// DetachStatsCollector removes the stats collector for the session +// this function is registered in BootstrapSession in /session/session.go +var DetachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { + return s +} + +type sessionPool interface { + Get() (pools.Resource, error) + Put(pools.Resource) +} + +func getSession(pool sessionPool) (session.Session, error) { + resource, err := pool.Get() + if err != nil { + return nil, err + } + + if se, ok := resource.(session.Session); ok { + // Only for test, in this case, the return session is mockSession + return se, nil + } + + sctx, ok := resource.(sessionctx.Context) + if !ok { + pool.Put(resource) + return nil, errors.Errorf("%T cannot be casted to sessionctx.Context", sctx) + } + + exec, ok := resource.(sqlexec.SQLExecutor) + if !ok { + pool.Put(resource) + return nil, errors.Errorf("%T cannot be casted to sqlexec.SQLExecutor", sctx) + } + + originalRetryLimit := sctx.GetSessionVars().RetryLimit + originalEnable1PC := sctx.GetSessionVars().Enable1PC + originalEnableAsyncCommit := sctx.GetSessionVars().EnableAsyncCommit + se := session.NewSession(sctx, exec, func(se session.Session) { + _, err = se.ExecuteSQL(context.Background(), fmt.Sprintf("set tidb_retry_limit=%d", originalRetryLimit)) + if err != nil { + logutil.BgLogger().Error("fail to reset tidb_retry_limit", zap.Int64("originalRetryLimit", originalRetryLimit), zap.Error(err)) + } + + if !originalEnable1PC { + _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_1pc=OFF") + terror.Log(err) + } + + if !originalEnableAsyncCommit { + _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_async_commit=OFF") + terror.Log(err) + } + + DetachStatsCollector(exec) + + pool.Put(resource) + }) + + exec = AttachStatsCollector(exec) + + // store and set the retry limit to 0 + _, err = se.ExecuteSQL(context.Background(), "set tidb_retry_limit=0") + if err != nil { + se.Close() + return nil, err + } + + // set enable 1pc to ON + _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_1pc=ON") + if err != nil { + se.Close() + return nil, err + } + + // set enable async commit to ON + _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_async_commit=ON") + if err != nil { + se.Close() + return nil, err + } + + // Force rollback the session to guarantee the session is not in any explicit transaction + if _, err = se.ExecuteSQL(context.Background(), "ROLLBACK"); err != nil { + se.Close() + return nil, err + } + + return se, nil +} + +func newTableSession(se session.Session, tbl *cache.PhysicalTable, expire time.Time) *ttlTableSession { + return &ttlTableSession{ + Session: se, + tbl: tbl, + expire: expire, + } +} + +type ttlTableSession struct { + session.Session + tbl *cache.PhysicalTable + expire time.Time +} + +func (s *ttlTableSession) ExecuteSQLWithCheck(ctx context.Context, sql string) ([]chunk.Row, bool, error) { + tracer := metrics.PhaseTracerFromCtx(ctx) + defer tracer.EnterPhase(tracer.Phase()) + + tracer.EnterPhase(metrics.PhaseOther) + if !variable.EnableTTLJob.Load() { + return nil, false, errors.New("global TTL job is disabled") + } + + if err := s.ResetWithGlobalTimeZone(ctx); err != nil { + return nil, false, err + } + + var result []chunk.Row + shouldRetry := true + err := s.RunInTxn(ctx, func() error { + tracer.EnterPhase(metrics.PhaseQuery) + defer tracer.EnterPhase(tracer.Phase()) + rows, err := s.ExecuteSQL(ctx, sql) + tracer.EnterPhase(metrics.PhaseCheckTTL) + // We must check the configuration after ExecuteSQL because of MDL and the meta the current transaction used + // can only be determined after executed one query. + if validateErr := validateTTLWork(ctx, s.Session, s.tbl, s.expire); validateErr != nil { + shouldRetry = false + return errors.Annotatef(validateErr, "table '%s.%s' meta changed, should abort current job", s.tbl.Schema, s.tbl.Name) + } + + if err != nil { + return err + } + + result = rows + return nil + }, session.TxnModeOptimistic) + + if err != nil { + return nil, shouldRetry, err + } + + return result, false, nil +} + +func validateTTLWork(ctx context.Context, s session.Session, tbl *cache.PhysicalTable, expire time.Time) error { + curTbl, err := s.SessionInfoSchema().TableByName(tbl.Schema, tbl.Name) + if err != nil { + return err + } + + newTblInfo := curTbl.Meta() + if tbl.TableInfo == newTblInfo { + return nil + } + + if tbl.TableInfo.ID != newTblInfo.ID { + return errors.New("table id changed") + } + + newTTLTbl, err := cache.NewPhysicalTable(tbl.Schema, newTblInfo, tbl.Partition) + if err != nil { + return err + } + + if newTTLTbl.ID != tbl.ID { + return errors.New("physical id changed") + } + + if tbl.Partition.L != "" { + if newTTLTbl.PartitionDef.Name.L != tbl.PartitionDef.Name.L { + return errors.New("partition name changed") + } + } + + if !newTTLTbl.TTLInfo.Enable { + return errors.New("table TTL disabled") + } + + if newTTLTbl.TimeColumn.Name.L != tbl.TimeColumn.Name.L { + return errors.New("time column name changed") + } + + if newTblInfo.TTLInfo.IntervalExprStr != tbl.TTLInfo.IntervalExprStr || + newTblInfo.TTLInfo.IntervalTimeUnit != tbl.TTLInfo.IntervalTimeUnit { + newExpireTime, err := newTTLTbl.EvalExpireTime(ctx, s, s.Now()) + if err != nil { + return err + } + + if newExpireTime.Before(expire) { + return errors.New("expire interval changed") + } + } + + return nil +} diff --git a/pkg/ttl/ttlworker/session_test.go b/pkg/ttl/ttlworker/session_test.go new file mode 100644 index 0000000000000..7f1d306a9530e --- /dev/null +++ b/pkg/ttl/ttlworker/session_test.go @@ -0,0 +1,358 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ttlworker + +import ( + "context" + "errors" + "strings" + "testing" + "time" + + "github.com/ngaut/pools" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/stretchr/testify/require" +) + +func newMockTTLTbl(t *testing.T, name string) *cache.PhysicalTable { + tblInfo := &model.TableInfo{ + Name: model.NewCIStr(name), + Columns: []*model.ColumnInfo{ + { + ID: 1, + Name: model.NewCIStr("time"), + Offset: 0, + FieldType: *types.NewFieldType(mysql.TypeDatetime), + State: model.StatePublic, + }, + }, + TTLInfo: &model.TTLInfo{ + ColumnName: model.NewCIStr("time"), + IntervalExprStr: "1", + IntervalTimeUnit: int(ast.TimeUnitSecond), + Enable: true, + JobInterval: "1h", + }, + State: model.StatePublic, + } + + tbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo, model.NewCIStr("")) + require.NoError(t, err) + return tbl +} + +func newMockInfoSchema(tbl ...*model.TableInfo) infoschema.InfoSchema { + return infoschema.MockInfoSchema(tbl) +} + +func newMockInfoSchemaWithVer(ver int64, tbl ...*model.TableInfo) infoschema.InfoSchema { + return infoschema.MockInfoSchemaWithSchemaVer(tbl, ver) +} + +type mockRows struct { + t *testing.T + fieldTypes []*types.FieldType + *chunk.Chunk +} + +func newMockRows(t *testing.T, fieldTypes ...*types.FieldType) *mockRows { + return &mockRows{ + t: t, + fieldTypes: fieldTypes, + Chunk: chunk.NewChunkWithCapacity(fieldTypes, 8), + } +} + +func (r *mockRows) Append(row ...interface{}) *mockRows { + require.Equal(r.t, len(r.fieldTypes), len(row)) + for i, ft := range r.fieldTypes { + tp := ft.GetType() + switch tp { + case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDatetime: + tm, ok := row[i].(time.Time) + require.True(r.t, ok) + r.AppendTime(i, types.NewTime(types.FromGoTime(tm), tp, types.DefaultFsp)) + case mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + val, ok := row[i].(int) + require.True(r.t, ok) + r.AppendInt64(i, int64(val)) + default: + require.FailNow(r.t, "unsupported tp %v", tp) + } + } + return r +} + +func (r *mockRows) Rows() []chunk.Row { + rows := make([]chunk.Row, r.NumRows()) + for i := 0; i < r.NumRows(); i++ { + rows[i] = r.GetRow(i) + } + return rows +} + +type mockSessionPool struct { + t *testing.T + se *mockSession + lastSession *mockSession +} + +func (p *mockSessionPool) Get() (pools.Resource, error) { + se := *(p.se) + p.lastSession = &se + return p.lastSession, nil +} + +func (p *mockSessionPool) Put(pools.Resource) {} + +func newMockSessionPool(t *testing.T, tbl ...*cache.PhysicalTable) *mockSessionPool { + return &mockSessionPool{ + se: newMockSession(t, tbl...), + } +} + +type mockSession struct { + t *testing.T + sessionctx.Context + sessionVars *variable.SessionVars + sessionInfoSchema infoschema.InfoSchema + executeSQL func(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) + rows []chunk.Row + execErr error + evalExpire time.Time + resetTimeZoneCalls int + closed bool + commitErr error +} + +func newMockSession(t *testing.T, tbl ...*cache.PhysicalTable) *mockSession { + tbls := make([]*model.TableInfo, len(tbl)) + for i, ttlTbl := range tbl { + tbls[i] = ttlTbl.TableInfo + } + sessVars := variable.NewSessionVars(nil) + sessVars.TimeZone = time.UTC + return &mockSession{ + t: t, + sessionInfoSchema: newMockInfoSchema(tbls...), + evalExpire: time.Now(), + sessionVars: sessVars, + } +} + +func (s *mockSession) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { + return s.sessionInfoSchema +} + +func (s *mockSession) SessionInfoSchema() infoschema.InfoSchema { + require.False(s.t, s.closed) + return s.sessionInfoSchema +} + +func (s *mockSession) GetSessionVars() *variable.SessionVars { + require.False(s.t, s.closed) + return s.sessionVars +} + +func (s *mockSession) ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) { + require.False(s.t, s.closed) + if strings.HasPrefix(strings.ToUpper(sql), "SELECT FROM_UNIXTIME") { + return newMockRows(s.t, types.NewFieldType(mysql.TypeTimestamp)).Append(s.evalExpire.In(s.GetSessionVars().TimeZone)).Rows(), nil + } + + if strings.HasPrefix(strings.ToUpper(sql), "SET ") { + return nil, nil + } + + if s.executeSQL != nil { + return s.executeSQL(ctx, sql, args...) + } + return s.rows, s.execErr +} + +func (s *mockSession) RunInTxn(_ context.Context, fn func() error, _ session.TxnMode) error { + require.False(s.t, s.closed) + if err := fn(); err != nil { + return err + } + return s.commitErr +} + +func (s *mockSession) ResetWithGlobalTimeZone(_ context.Context) (err error) { + require.False(s.t, s.closed) + s.resetTimeZoneCalls++ + return nil +} + +func (s *mockSession) Close() { + s.closed = true +} + +func (s *mockSession) Now() time.Time { + tz := s.sessionVars.TimeZone + if tz != nil { + tz = time.UTC + } + return time.Now().In(tz) +} + +func TestExecuteSQLWithCheck(t *testing.T) { + ctx := context.TODO() + tbl := newMockTTLTbl(t, "t1") + s := newMockSession(t, tbl) + s.execErr = errors.New("mockErr") + s.rows = newMockRows(t, types.NewFieldType(mysql.TypeInt24)).Append(12).Rows() + tblSe := newTableSession(s, tbl, time.UnixMilli(0).In(time.UTC)) + + rows, shouldRetry, err := tblSe.ExecuteSQLWithCheck(ctx, "select 1") + require.EqualError(t, err, "mockErr") + require.True(t, shouldRetry) + require.Nil(t, rows) + require.Equal(t, 1, s.resetTimeZoneCalls) + + s.sessionInfoSchema = newMockInfoSchema() + rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") + require.EqualError(t, err, "table 'test.t1' meta changed, should abort current job: [schema:1146]Table 'test.t1' doesn't exist") + require.False(t, shouldRetry) + require.Nil(t, rows) + require.Equal(t, 2, s.resetTimeZoneCalls) + + s.sessionInfoSchema = newMockInfoSchema(tbl.TableInfo) + s.execErr = nil + rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") + require.NoError(t, err) + require.False(t, shouldRetry) + require.Equal(t, 1, len(rows)) + require.Equal(t, int64(12), rows[0].GetInt64(0)) + require.Equal(t, 3, s.resetTimeZoneCalls) + + s.commitErr = errors.New("mockCommitErr") + rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") + require.EqualError(t, err, "mockCommitErr") + require.True(t, shouldRetry) + require.Nil(t, rows) + require.Equal(t, 4, s.resetTimeZoneCalls) +} + +func TestValidateTTLWork(t *testing.T) { + ctx := context.TODO() + tbl := newMockTTLTbl(t, "t1") + expire := time.UnixMilli(0).In(time.UTC) + + s := newMockSession(t, tbl) + s.execErr = errors.New("mockErr") + s.evalExpire = time.UnixMilli(0).In(time.UTC) + + // test table dropped + s.sessionInfoSchema = newMockInfoSchema() + err := validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "[schema:1146]Table 'test.t1' doesn't exist") + + // test TTL option removed + tbl2 := tbl.TableInfo.Clone() + tbl2.TTLInfo = nil + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "table 'test.t1' is not a ttl table") + + // test table state not public + tbl2 = tbl.TableInfo.Clone() + tbl2.State = model.StateDeleteOnly + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "table 'test.t1' is not a public table") + + // test table name changed + tbl2 = tbl.TableInfo.Clone() + tbl2.Name = model.NewCIStr("testcc") + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "[schema:1146]Table 'test.t1' doesn't exist") + + // test table id changed + tbl2 = tbl.TableInfo.Clone() + tbl2.ID = 123 + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "table id changed") + + // test time column name changed + tbl2 = tbl.TableInfo.Clone() + tbl2.Columns[0] = tbl2.Columns[0].Clone() + tbl2.Columns[0].Name = model.NewCIStr("time2") + tbl2.TTLInfo.ColumnName = model.NewCIStr("time2") + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "time column name changed") + + // test interval changed and expire time before previous + tbl2 = tbl.TableInfo.Clone() + tbl2.TTLInfo.IntervalExprStr = "10" + s.sessionInfoSchema = newMockInfoSchema(tbl2) + s.evalExpire = time.UnixMilli(-1) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "expire interval changed") + + tbl2 = tbl.TableInfo.Clone() + tbl2.TTLInfo.IntervalTimeUnit = int(ast.TimeUnitDay) + s.evalExpire = time.UnixMilli(-1) + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "expire interval changed") + + // test for safe meta change + tbl2 = tbl.TableInfo.Clone() + tbl2.Columns[0] = tbl2.Columns[0].Clone() + tbl2.Columns[0].ID += 10 + tbl2.Columns[0].FieldType = *types.NewFieldType(mysql.TypeDate) + tbl2.TTLInfo.IntervalExprStr = "100" + s.evalExpire = time.UnixMilli(1000) + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.NoError(t, err) + + // test table partition name changed + tp := tbl.TableInfo.Clone() + tp.Partition = &model.PartitionInfo{ + Definitions: []model.PartitionDefinition{ + {ID: 1023, Name: model.NewCIStr("p0")}, + }, + } + tbl, err = cache.NewPhysicalTable(model.NewCIStr("test"), tp, model.NewCIStr("p0")) + require.NoError(t, err) + tbl2 = tp.Clone() + tbl2.Partition = tp.Partition.Clone() + tbl2.Partition.Definitions[0].Name = model.NewCIStr("p1") + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "partition 'p0' is not found in ttl table 'test.t1'") + + // test table partition id changed + tbl2 = tp.Clone() + tbl2.Partition = tp.Partition.Clone() + tbl2.Partition.Definitions[0].ID += 100 + s.sessionInfoSchema = newMockInfoSchema(tbl2) + err = validateTTLWork(ctx, s, tbl, expire) + require.EqualError(t, err, "physical id changed") +} diff --git a/ttl/ttlworker/task_manager.go b/pkg/ttl/ttlworker/task_manager.go similarity index 98% rename from ttl/ttlworker/task_manager.go rename to pkg/ttl/ttlworker/task_manager.go index a5de011003512..7e6c58cb537f5 100644 --- a/ttl/ttlworker/task_manager.go +++ b/pkg/ttl/ttlworker/task_manager.go @@ -20,13 +20,13 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/tikvrpc" "go.uber.org/multierr" diff --git a/ttl/ttlworker/task_manager_integration_test.go b/pkg/ttl/ttlworker/task_manager_integration_test.go similarity index 97% rename from ttl/ttlworker/task_manager_integration_test.go rename to pkg/ttl/ttlworker/task_manager_integration_test.go index e66305806e079..dfa839ee13d70 100644 --- a/ttl/ttlworker/task_manager_integration_test.go +++ b/pkg/ttl/ttlworker/task_manager_integration_test.go @@ -21,15 +21,15 @@ import ( "testing" "time" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/ttlworker" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/ttl/ttlworker" + "github.com/pingcap/tidb/pkg/util/logutil" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/testutils" diff --git a/ttl/ttlworker/task_manager_test.go b/pkg/ttl/ttlworker/task_manager_test.go similarity index 96% rename from ttl/ttlworker/task_manager_test.go rename to pkg/ttl/ttlworker/task_manager_test.go index 57554332bf806..0792a5df404dc 100644 --- a/ttl/ttlworker/task_manager_test.go +++ b/pkg/ttl/ttlworker/task_manager_test.go @@ -19,10 +19,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" diff --git a/pkg/ttl/ttlworker/timer.go b/pkg/ttl/ttlworker/timer.go new file mode 100644 index 0000000000000..e4ca260b471fe --- /dev/null +++ b/pkg/ttl/ttlworker/timer.go @@ -0,0 +1,281 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package ttlworker + +import ( + "context" + "encoding/json" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + timerrt "github.com/pingcap/tidb/pkg/timer/runtime" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/timeutil" + "go.uber.org/zap" +) + +const ( + defaultCheckTTLJobInterval = 10 * time.Second +) + +type ttlTimerSummary struct { + LastJobRequestID string `json:"last_job_request_id,omitempty"` + LastJobSummary *TTLSummary `json:"last_job_summary,omitempty"` +} + +// TTLJobTrace contains some TTL job information to trace +type TTLJobTrace struct { + // RequestID is the request id when job submitted, we can use it to trace a job + RequestID string + // Finished indicates whether the job is finished + Finished bool + // Summary indicates the summary of the job + Summary *TTLSummary +} + +// TTLJobAdapter is used to submit TTL job and trace job status +type TTLJobAdapter interface { + // CanSubmitJob returns whether a new job can be created for the specified table + CanSubmitJob(tableID, physicalID int64) bool + // SubmitJob submits a new job + SubmitJob(ctx context.Context, tableID, physicalID int64, requestID string, watermark time.Time) (*TTLJobTrace, error) + // GetJob returns the job to trace + GetJob(ctx context.Context, tableID, physicalID int64, requestID string) (*TTLJobTrace, error) +} + +type ttlTimerHook struct { + adapter TTLJobAdapter + cli timerapi.TimerClient + ctx context.Context + cancel func() + wg sync.WaitGroup + nowFunc func() time.Time + checkTTLJobInterval time.Duration + // waitJobLoopCounter is only used for test + waitJobLoopCounter int64 +} + +func newTTLTimerHook(adapter TTLJobAdapter, cli timerapi.TimerClient) *ttlTimerHook { + ctx, cancel := context.WithCancel(context.Background()) + return &ttlTimerHook{ + adapter: adapter, + cli: cli, + ctx: ctx, + cancel: cancel, + nowFunc: time.Now, + checkTTLJobInterval: defaultCheckTTLJobInterval, + } +} + +func (t *ttlTimerHook) Start() {} + +func (t *ttlTimerHook) Stop() { + t.cancel() + t.wg.Wait() +} + +func (t *ttlTimerHook) OnPreSchedEvent(_ context.Context, event timerapi.TimerShedEvent) (r timerapi.PreSchedEventResult, err error) { + if !variable.EnableTTLJob.Load() { + r.Delay = time.Minute + return + } + + windowStart, windowEnd := variable.TTLJobScheduleWindowStartTime.Load(), variable.TTLJobScheduleWindowEndTime.Load() + if !timeutil.WithinDayTimePeriod(windowStart, windowEnd, t.nowFunc()) { + r.Delay = time.Minute + return + } + + timer := event.Timer() + var data TTLTimerData + if err = json.Unmarshal(event.Timer().Data, &data); err != nil { + logutil.BgLogger().Error("invalid TTL timer data", + zap.String("timerID", timer.ID), + zap.String("timerKey", timer.Key), + zap.ByteString("data", timer.Data), + ) + r.Delay = time.Minute + return + } + + if !t.adapter.CanSubmitJob(data.TableID, data.PhysicalID) { + r.Delay = time.Minute + return + } + + return +} + +func (t *ttlTimerHook) OnSchedEvent(ctx context.Context, event timerapi.TimerShedEvent) error { + timer := event.Timer() + eventID := event.EventID() + logger := logutil.BgLogger().With( + zap.String("key", timer.Key), + zap.String("eventID", eventID), + zap.Time("eventStart", timer.EventStart), + zap.Strings("tags", timer.Tags), + ) + + logger.Info("timer triggered to run TTL job", zap.String("manualRequest", timer.EventManualRequestID)) + if err := t.ctx.Err(); err != nil { + return err + } + + var data TTLTimerData + if err := json.Unmarshal(timer.Data, &data); err != nil { + logger.Error("invalid TTL timer data", zap.ByteString("data", timer.Data)) + return err + } + + job, err := t.adapter.GetJob(ctx, data.TableID, data.PhysicalID, eventID) + if err != nil { + return err + } + + if job == nil { + cancel := false + if !timer.Enable || !t.adapter.CanSubmitJob(data.TableID, data.PhysicalID) { + cancel = true + logger.Warn("cancel current TTL timer event because table's ttl is not enabled") + } + + if t.nowFunc().Sub(timer.EventStart) > 10*time.Minute { + cancel = true + logger.Warn("cancel current TTL timer event because job not submitted for a long time") + } + + if cancel { + return t.cli.CloseTimerEvent(ctx, timer.ID, eventID, timerapi.WithSetWatermark(timer.Watermark)) + } + + logger.Info("submit TTL job for current timer event") + if job, err = t.adapter.SubmitJob(ctx, data.TableID, data.PhysicalID, eventID, timer.EventStart); err != nil { + return err + } + } + + logger = logger.With(zap.String("jobRequestID", job.RequestID)) + logger.Info("start to wait TTL job") + t.wg.Add(1) + t.waitJobLoopCounter++ + go t.waitJobFinished(logger, &data, timer.ID, eventID, timer.EventStart) + return nil +} + +func (t *ttlTimerHook) waitJobFinished(logger *zap.Logger, data *TTLTimerData, timerID string, eventID string, eventStart time.Time) { + defer func() { + t.wg.Done() + logger.Info("stop to wait TTL job") + }() + + ticker := time.NewTicker(t.checkTTLJobInterval) + defer ticker.Stop() + + for { + select { + case <-t.ctx.Done(): + logger.Info("stop waiting TTL job because of context cancelled") + return + case <-ticker.C: + } + + timer, err := t.cli.GetTimerByID(t.ctx, timerID) + if err != nil { + if errors.ErrorEqual(timerapi.ErrTimerNotExist, err) { + logger.Warn("stop waiting TTL job because of timer is deleted") + return + } + + logger.Error("GetTimerByID failed", zap.Error(err)) + continue + } + + if timer.EventID != eventID { + logger.Warn("stop waiting TTL job because of current event id changed", zap.String("newEventID", timer.EventID)) + return + } + + job, err := t.adapter.GetJob(t.ctx, data.TableID, data.PhysicalID, eventID) + if err != nil { + logger.Error("GetJob error", zap.Error(err)) + continue + } + + if job != nil && !job.Finished { + continue + } + + timerSummary := &ttlTimerSummary{ + LastJobRequestID: eventID, + } + + if job != nil { + timerSummary.LastJobSummary = job.Summary + } else { + logger.Warn("job for current TTL timer event not found") + } + + logger.Info("TTL job is finished, close current timer event") + summaryData, err := json.Marshal(timerSummary) + if err != nil { + logger.Error("marshal summary error", zap.Error(err)) + continue + } + + if err = t.cli.CloseTimerEvent(t.ctx, timerID, eventID, timerapi.WithSetWatermark(eventStart), timerapi.WithSetSummaryData(summaryData)); err != nil { + logger.Error("CloseTimerEvent error", zap.Error(err)) + continue + } + + return + } +} + +type ttlTimerRuntime struct { + rt *timerrt.TimerGroupRuntime + store *timerapi.TimerStore + adapter TTLJobAdapter +} + +func newTTLTimerRuntime(store *timerapi.TimerStore, adapter TTLJobAdapter) *ttlTimerRuntime { + return &ttlTimerRuntime{ + store: store, + adapter: adapter, + } +} + +func (r *ttlTimerRuntime) Resume() { + if r.rt != nil { + return + } + + r.rt = timerrt.NewTimerRuntimeBuilder("ttl", r.store). + SetCond(&timerapi.TimerCond{Key: timerapi.NewOptionalVal(timerKeyPrefix), KeyPrefix: true}). + RegisterHookFactory(timerHookClass, func(hookClass string, cli timerapi.TimerClient) timerapi.Hook { + return newTTLTimerHook(r.adapter, cli) + }). + Build() + r.rt.Start() +} + +func (r *ttlTimerRuntime) Pause() { + if rt := r.rt; rt != nil { + r.rt = nil + rt.Stop() + } +} diff --git a/ttl/ttlworker/timer_sync.go b/pkg/ttl/ttlworker/timer_sync.go similarity index 97% rename from ttl/ttlworker/timer_sync.go rename to pkg/ttl/ttlworker/timer_sync.go index db2d1c1b7701e..fd3ebe606e44f 100644 --- a/ttl/ttlworker/timer_sync.go +++ b/pkg/ttl/ttlworker/timer_sync.go @@ -22,13 +22,13 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - timerapi "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/session" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "golang.org/x/exp/maps" ) diff --git a/ttl/ttlworker/timer_sync_test.go b/pkg/ttl/ttlworker/timer_sync_test.go similarity index 98% rename from ttl/ttlworker/timer_sync_test.go rename to pkg/ttl/ttlworker/timer_sync_test.go index c203a55d260c2..18f6cfd4c487b 100644 --- a/ttl/ttlworker/timer_sync_test.go +++ b/pkg/ttl/ttlworker/timer_sync_test.go @@ -23,15 +23,15 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - timerapi "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/timer/tablestore" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/ttlworker" - mockutil "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/timer/tablestore" + "github.com/pingcap/tidb/pkg/ttl/cache" + "github.com/pingcap/tidb/pkg/ttl/ttlworker" + mockutil "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/ttl/ttlworker/timer_test.go b/pkg/ttl/ttlworker/timer_test.go similarity index 99% rename from ttl/ttlworker/timer_test.go rename to pkg/ttl/ttlworker/timer_test.go index 741bda3a3be69..11b05c4b5393b 100644 --- a/ttl/ttlworker/timer_test.go +++ b/pkg/ttl/ttlworker/timer_test.go @@ -23,9 +23,9 @@ import ( "github.com/google/uuid" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/variable" - timerapi "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + timerapi "github.com/pingcap/tidb/pkg/timer/api" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/ttl/ttlworker/worker.go b/pkg/ttl/ttlworker/worker.go new file mode 100644 index 0000000000000..ec7ce604d72f5 --- /dev/null +++ b/pkg/ttl/ttlworker/worker.go @@ -0,0 +1,141 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package ttlworker + +import ( + "context" + "sync" + "time" + + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +type workerStatus int + +const ( + workerStatusCreated workerStatus = iota + workerStatusRunning + workerStatusStopping + workerStatusStopped +) + +type worker interface { + Start() + Stop() + Status() workerStatus + Error() error + Send() chan<- interface{} + WaitStopped(ctx context.Context, timeout time.Duration) error +} + +type baseWorker struct { + sync.Mutex + ctx context.Context + cancel func() + ch chan interface{} + loopFunc func() error + + err error + status workerStatus + wg util.WaitGroupWrapper +} + +func (w *baseWorker) init(loop func() error) { + w.ctx, w.cancel = context.WithCancel(context.Background()) + w.status = workerStatusCreated + w.loopFunc = loop + w.ch = make(chan interface{}) +} + +func (w *baseWorker) Start() { + w.Lock() + defer w.Unlock() + if w.status != workerStatusCreated { + return + } + + w.wg.Run(w.loop) + w.status = workerStatusRunning +} + +func (w *baseWorker) Stop() { + w.Lock() + defer w.Unlock() + switch w.status { + case workerStatusCreated: + w.cancel() + w.toStopped(nil) + case workerStatusRunning: + w.cancel() + w.status = workerStatusStopping + } +} + +func (w *baseWorker) Status() workerStatus { + w.Lock() + defer w.Unlock() + return w.status +} + +func (w *baseWorker) Error() error { + w.Lock() + defer w.Unlock() + return w.err +} + +func (w *baseWorker) WaitStopped(ctx context.Context, timeout time.Duration) error { + // consider the situation when the worker has stopped, but the context has also stopped. We should + // return without error + if w.Status() == workerStatusStopped { + return nil + } + + ctx, cancel := context.WithTimeout(ctx, timeout) + go func() { + w.wg.Wait() + cancel() + }() + + <-ctx.Done() + if w.Status() != workerStatusStopped { + return ctx.Err() + } + return nil +} + +func (w *baseWorker) Send() chan<- interface{} { + return w.ch +} + +func (w *baseWorker) loop() { + var err error + defer func() { + if r := recover(); r != nil { + logutil.BgLogger().Info("ttl worker panic", zap.Any("recover", r), zap.Stack("stack")) + } + w.Lock() + w.toStopped(err) + w.Unlock() + }() + err = w.loopFunc() +} + +func (w *baseWorker) toStopped(err error) { + w.status = workerStatusStopped + w.err = err + close(w.ch) +} diff --git a/pkg/types/BUILD.bazel b/pkg/types/BUILD.bazel new file mode 100644 index 0000000000000..1eae8bd68cc3f --- /dev/null +++ b/pkg/types/BUILD.bazel @@ -0,0 +1,116 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +package_group( + name = "types_friend", + packages = [ + "-//pkg/config/...", + "//...", + ], +) + +go_library( + name = "types", + srcs = [ + "binary_literal.go", + "compare.go", + "context.go", + "convert.go", + "core_time.go", + "datum.go", + "datum_eval.go", + "enum.go", + "errors.go", + "etc.go", + "eval_type.go", + "explain_format.go", + "field_name.go", + "field_type.go", + "field_type_builder.go", + "fsp.go", + "helper.go", + "json_binary.go", + "json_binary_functions.go", + "json_constants.go", + "json_path_expr.go", + "mydecimal.go", + "overflow.go", + "set.go", + "time.go", + ], + importpath = "github.com/pingcap/tidb/pkg/types", + visibility = [ + ":types_friend", + ], + deps = [ + "//pkg/errno", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/opcode", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/sessionctx/stmtctx", + "//pkg/types/context", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/parser", + "//pkg/util/size", + "//pkg/util/stringutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "types_test", + timeout = "short", + srcs = [ + "benchmark_test.go", + "binary_literal_test.go", + "compare_test.go", + "const_test.go", + "convert_test.go", + "core_time_test.go", + "datum_test.go", + "enum_test.go", + "errors_test.go", + "etc_test.go", + "export_test.go", + "field_type_test.go", + "format_test.go", + "fsp_test.go", + "helper_test.go", + "json_binary_functions_test.go", + "json_binary_test.go", + "json_path_expr_test.go", + "main_test.go", + "mydecimal_benchmark_test.go", + "mydecimal_test.go", + "overflow_test.go", + "set_test.go", + "time_test.go", + ], + embed = [":types"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/types/benchmark_test.go b/pkg/types/benchmark_test.go new file mode 100644 index 0000000000000..53455c8b247bf --- /dev/null +++ b/pkg/types/benchmark_test.go @@ -0,0 +1,60 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package types + +import ( + "math/rand" + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" +) + +func BenchmarkDefaultTypeForValue(b *testing.B) { + lenNums := 1000000 + numsFull := make([]uint64, lenNums) + nums64k := make([]uint64, lenNums) + nums512 := make([]uint64, lenNums) + + for i := range numsFull { + r := rand.Uint64() + numsFull[i] = r + nums64k[i] = r % 64000 + nums512[i] = r % 512 + } + + b.Run("LenOfUint64_input full range", func(b *testing.B) { + b.StartTimer() + var ft FieldType + for i := 0; i < b.N; i++ { + DefaultTypeForValue(numsFull[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) + } + }) + + b.Run("LenOfUint64_input 0 to 64K ", func(b *testing.B) { + b.StartTimer() + var ft FieldType + for i := 0; i < b.N; i++ { + DefaultTypeForValue(nums64k[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) + } + }) + + b.Run("LenOfUint64_input 0 to 512 ", func(b *testing.B) { + b.StartTimer() + var ft FieldType + for i := 0; i < b.N; i++ { + DefaultTypeForValue(nums512[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) + } + }) +} diff --git a/types/binary_literal.go b/pkg/types/binary_literal.go similarity index 99% rename from types/binary_literal.go rename to pkg/types/binary_literal.go index b84a1b8f04f8f..5b36b1a013ab1 100644 --- a/types/binary_literal.go +++ b/pkg/types/binary_literal.go @@ -24,7 +24,7 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" ) // BinaryLiteral is the internal type for storing bit / hex literal type. diff --git a/types/binary_literal_test.go b/pkg/types/binary_literal_test.go similarity index 99% rename from types/binary_literal_test.go rename to pkg/types/binary_literal_test.go index 29d3f8ee9f5c3..a404818ab6478 100644 --- a/types/binary_literal_test.go +++ b/pkg/types/binary_literal_test.go @@ -18,7 +18,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" "github.com/stretchr/testify/require" ) diff --git a/pkg/types/compare.go b/pkg/types/compare.go new file mode 100644 index 0000000000000..ec55f15e2fa22 --- /dev/null +++ b/pkg/types/compare.go @@ -0,0 +1,86 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "math" + + "github.com/pingcap/tidb/pkg/util/collate" +) + +// VecCompareUU returns []int64 comparing the []uint64 x to []uint64 y +func VecCompareUU(x, y []uint64, res []int64) { + n := len(x) + for i := 0; i < n; i++ { + if x[i] < y[i] { + res[i] = -1 + } else if x[i] == y[i] { + res[i] = 0 + } else { + res[i] = 1 + } + } +} + +// VecCompareII returns []int64 comparing the []int64 x to []int64 y +func VecCompareII(x, y, res []int64) { + n := len(x) + for i := 0; i < n; i++ { + if x[i] < y[i] { + res[i] = -1 + } else if x[i] == y[i] { + res[i] = 0 + } else { + res[i] = 1 + } + } +} + +// VecCompareUI returns []int64 comparing the []uint64 x to []int64y +func VecCompareUI(x []uint64, y, res []int64) { + n := len(x) + for i := 0; i < n; i++ { + if y[i] < 0 || x[i] > math.MaxInt64 { + res[i] = 1 + } else if int64(x[i]) < y[i] { + res[i] = -1 + } else if int64(x[i]) == y[i] { + res[i] = 0 + } else { + res[i] = 1 + } + } +} + +// VecCompareIU returns []int64 comparing the []int64 x to []uint64y +func VecCompareIU(x []int64, y []uint64, res []int64) { + n := len(x) + for i := 0; i < n; i++ { + if x[i] < 0 || y[i] > math.MaxInt64 { + res[i] = -1 + } else if x[i] < int64(y[i]) { + res[i] = -1 + } else if x[i] == int64(y[i]) { + res[i] = 0 + } else { + res[i] = 1 + } + } +} + +// CompareString returns an integer comparing the string x to y with the specified collation and length. +func CompareString(x, y, collation string) int { + return collate.GetCollator(collation).Compare(x, y) +} diff --git a/types/compare_test.go b/pkg/types/compare_test.go similarity index 98% rename from types/compare_test.go rename to pkg/types/compare_test.go index d4f212876ae33..368d8c4914e30 100644 --- a/types/compare_test.go +++ b/pkg/types/compare_test.go @@ -19,9 +19,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/types/const_test.go b/pkg/types/const_test.go similarity index 98% rename from types/const_test.go rename to pkg/types/const_test.go index 6bdd75b57e4b7..6ac4aff2f2f53 100644 --- a/types/const_test.go +++ b/pkg/types/const_test.go @@ -17,7 +17,7 @@ package types_test import ( "testing" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/pkg/types/context.go b/pkg/types/context.go new file mode 100644 index 0000000000000..854110c5cca90 --- /dev/null +++ b/pkg/types/context.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package types + +import "github.com/pingcap/tidb/pkg/types/context" + +// TODO: move a contents in `types/context/context.go` to this file after refactor finished. +// Because package `types` has a dependency on `sessionctx/stmtctx`, we need a separate package `type/context` to define +// context objects during refactor works. + +// Context is an alias of `context.Context` +type Context = context.Context + +// Flags is an alias of `Flags` +type Flags = context.Flags + +// StrictFlags is a flags with a fields unset and has the most strict behavior. +const StrictFlags = context.StrictFlags + +// NewContext creates a new `Context` +var NewContext = context.NewContext diff --git a/pkg/types/context/BUILD.bazel b/pkg/types/context/BUILD.bazel new file mode 100644 index 0000000000000..f000bb3415639 --- /dev/null +++ b/pkg/types/context/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "context", + srcs = ["context.go"], + importpath = "github.com/pingcap/tidb/pkg/types/context", + visibility = ["//visibility:public"], + deps = ["//pkg/util/intest"], +) + +go_test( + name = "context_test", + timeout = "short", + srcs = ["context_test.go"], + embed = [":context"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/pkg/types/context/context.go b/pkg/types/context/context.go new file mode 100644 index 0000000000000..ec7e17438ed48 --- /dev/null +++ b/pkg/types/context/context.go @@ -0,0 +1,166 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package context + +import ( + "time" + + "github.com/pingcap/tidb/pkg/util/intest" +) + +// StrictFlags is a flags with a fields unset and has the most strict behavior. +const StrictFlags Flags = 0 + +// Flags indicates how to handle the conversion of a value. +type Flags uint16 + +const ( + // FlagIgnoreTruncateErr indicates to ignore the truncate error. + // If this flag is set, `FlagTruncateAsWarning` will be ignored. + FlagIgnoreTruncateErr Flags = 1 << iota + // FlagTruncateAsWarning indicates to append the truncate error to warnings instead of returning it to user. + FlagTruncateAsWarning + // FlagClipNegativeToZero indicates to clip the value to zero when casting a negative value to an unsigned integer. + // When this flag is set and the clip happens, an overflow error occurs and how to handle it will be determined by flags + // `FlagIgnoreOverflowError` and `FlagOverflowAsWarning`. + FlagClipNegativeToZero + // FlagIgnoreOverflowError indicates to ignore the overflow error. + // If this flag is set, `FlagOverflowAsWarning` will be ignored. + FlagIgnoreOverflowError + // FlagOverflowAsWarning indicates to append the overflow error to warnings instead of returning it to user. + FlagOverflowAsWarning + // FlagIgnoreZeroDateErr indicates to ignore the zero-date error. + // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_date for details about the "zero-date" error. + // If this flag is set, `FlagZeroDateAsWarning` will be ignored. + FlagIgnoreZeroDateErr + // FlagZeroDateAsWarning indicates to append the zero-date error to warnings instead of returning it to user. + FlagZeroDateAsWarning + // FlagIgnoreZeroInDateErr indicates to ignore the zero-in-date error. + // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_in_date for details about the "zero-in-date" error. + FlagIgnoreZeroInDateErr + // FlagZeroInDateAsWarning indicates to append the zero-in-date error to warnings instead of returning it to user. + FlagZeroInDateAsWarning + // FlagIgnoreInvalidDateErr indicates to ignore the invalid-date error. + // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_allow_invalid_dates for details about the "invalid-date" error. + FlagIgnoreInvalidDateErr + // FlagInvalidDateAsWarning indicates to append the invalid-date error to warnings instead of returning it to user. + FlagInvalidDateAsWarning + // FlagSkipASCIICheck indicates to skip the ASCII check when converting the value to an ASCII string. + FlagSkipASCIICheck + // FlagSkipUTF8Check indicates to skip the UTF8 check when converting the value to an UTF8MB3 string. + FlagSkipUTF8Check + // FlagSkipUTF8MB4Check indicates to skip the UTF8MB4 check when converting the value to an UTF8 string. + FlagSkipUTF8MB4Check +) + +// SkipASCIICheck indicates whether the flag `FlagSkipASCIICheck` is set +func (f Flags) SkipASCIICheck() bool { + return f&FlagSkipASCIICheck != 0 +} + +// WithSkipSACIICheck returns a new flags with `FlagSkipASCIICheck` set/unset according to the skip parameter +func (f Flags) WithSkipSACIICheck(skip bool) Flags { + if skip { + return f | FlagSkipASCIICheck + } + return f &^ FlagSkipASCIICheck +} + +// SkipUTF8Check indicates whether the flag `FlagSkipUTF8Check` is set +func (f Flags) SkipUTF8Check() bool { + return f&FlagSkipUTF8Check != 0 +} + +// WithSkipUTF8Check returns a new flags with `FlagSkipUTF8Check` set/unset according to the skip parameter +func (f Flags) WithSkipUTF8Check(skip bool) Flags { + if skip { + return f | FlagSkipUTF8Check + } + return f &^ FlagSkipUTF8Check +} + +// SkipUTF8MB4Check indicates whether the flag `FlagSkipUTF8MB4Check` is set +func (f Flags) SkipUTF8MB4Check() bool { + return f&FlagSkipUTF8MB4Check != 0 +} + +// WithSkipUTF8MB4Check returns a new flags with `FlagSkipUTF8MB4Check` set/unset according to the skip parameter +func (f Flags) WithSkipUTF8MB4Check(skip bool) Flags { + if skip { + return f | FlagSkipUTF8MB4Check + } + return f &^ FlagSkipUTF8MB4Check +} + +// Context provides the information when converting between different types. +type Context struct { + flags Flags + loc *time.Location + appendWarningFn func(err error) +} + +// NewContext creates a new `Context` +func NewContext(flags Flags, loc *time.Location, appendWarningFn func(err error)) Context { + intest.Assert(loc != nil && appendWarningFn != nil) + return Context{ + flags: flags, + loc: loc, + appendWarningFn: appendWarningFn, + } +} + +// Flags returns the flags of the context +func (c *Context) Flags() Flags { + return c.flags +} + +// WithFlags returns a new context with the flags set to the given value +func (c *Context) WithFlags(f Flags) Context { + ctx := *c + ctx.flags = f + return ctx +} + +// WithLocation returns a new context with the given location +func (c *Context) WithLocation(loc *time.Location) Context { + intest.Assert(loc) + ctx := *c + ctx.loc = loc + return ctx +} + +// Location returns the location of the context +func (c *Context) Location() *time.Location { + intest.Assert(c.loc) + if c.loc == nil { + // c.loc should always not be nil, just make the code safe here. + return time.UTC + } + return c.loc +} + +// AppendWarning appends the error to warning. If the inner `appendWarningFn` is nil, do nothing. +func (c *Context) AppendWarning(err error) { + intest.Assert(c.appendWarningFn != nil) + if fn := c.appendWarningFn; fn != nil { + // appendWarningFn should always not be nil, check fn != nil here to just make code safe. + fn(err) + } +} + +// AppendWarningFunc returns the inner `appendWarningFn` +func (c *Context) AppendWarningFunc() func(err error) { + return c.appendWarningFn +} diff --git a/types/context/context_test.go b/pkg/types/context/context_test.go similarity index 100% rename from types/context/context_test.go rename to pkg/types/context/context_test.go diff --git a/pkg/types/convert.go b/pkg/types/convert.go new file mode 100644 index 0000000000000..874b6c788f392 --- /dev/null +++ b/pkg/types/convert.go @@ -0,0 +1,782 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package types + +import ( + "math" + "math/big" + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/hack" +) + +func truncateStr(str string, flen int) string { + if flen != UnspecifiedLength && len(str) > flen { + str = str[:flen] + } + return str +} + +// IntergerUnsignedUpperBound indicates the max uint64 values of different mysql types. +func IntergerUnsignedUpperBound(intType byte) uint64 { + switch intType { + case mysql.TypeTiny: + return math.MaxUint8 + case mysql.TypeShort: + return math.MaxUint16 + case mysql.TypeInt24: + return mysql.MaxUint24 + case mysql.TypeLong: + return math.MaxUint32 + case mysql.TypeLonglong: + return math.MaxUint64 + case mysql.TypeBit: + return math.MaxUint64 + case mysql.TypeEnum: + // enum can have at most 65535 distinct elements + // it would be better to use len(FieldType.GetElems()), but we only have a byte type here + return 65535 + case mysql.TypeSet: + return math.MaxUint64 + default: + panic("Input byte is not a mysql type") + } +} + +// IntergerSignedUpperBound indicates the max int64 values of different mysql types. +func IntergerSignedUpperBound(intType byte) int64 { + switch intType { + case mysql.TypeTiny: + return math.MaxInt8 + case mysql.TypeShort: + return math.MaxInt16 + case mysql.TypeInt24: + return mysql.MaxInt24 + case mysql.TypeLong: + return math.MaxInt32 + case mysql.TypeLonglong: + return math.MaxInt64 + case mysql.TypeEnum: + // enum can have at most 65535 distinct elements + // it would be better to use len(FieldType.GetElems()), but we only have a byte type here + return 65535 + default: + panic("Input byte is not a mysql int type") + } +} + +// IntergerSignedLowerBound indicates the min int64 values of different mysql types. +func IntergerSignedLowerBound(intType byte) int64 { + switch intType { + case mysql.TypeTiny: + return math.MinInt8 + case mysql.TypeShort: + return math.MinInt16 + case mysql.TypeInt24: + return mysql.MinInt24 + case mysql.TypeLong: + return math.MinInt32 + case mysql.TypeLonglong: + return math.MinInt64 + case mysql.TypeEnum: + return 0 + default: + panic("Input byte is not a mysql type") + } +} + +// ConvertFloatToInt converts a float64 value to a int value. +// `tp` is used in err msg, if there is overflow, this func will report err according to `tp` +func ConvertFloatToInt(fval float64, lowerBound, upperBound int64, tp byte) (int64, error) { + val := RoundFloat(fval) + if val < float64(lowerBound) { + return lowerBound, overflow(val, tp) + } + + if val >= float64(upperBound) { + if val == float64(upperBound) { + return upperBound, nil + } + return upperBound, overflow(val, tp) + } + return int64(val), nil +} + +// ConvertIntToInt converts an int value to another int value of different precision. +func ConvertIntToInt(val int64, lowerBound int64, upperBound int64, tp byte) (int64, error) { + if val < lowerBound { + return lowerBound, overflow(val, tp) + } + + if val > upperBound { + return upperBound, overflow(val, tp) + } + + return val, nil +} + +// ConvertUintToInt converts an uint value to an int value. +func ConvertUintToInt(val uint64, upperBound int64, tp byte) (int64, error) { + if val > uint64(upperBound) { + return upperBound, overflow(val, tp) + } + + return int64(val), nil +} + +// ConvertIntToUint converts an int value to an uint value. +func ConvertIntToUint(sc *stmtctx.StatementContext, val int64, upperBound uint64, tp byte) (uint64, error) { + if sc.ShouldClipToZero() && val < 0 { + return 0, overflow(val, tp) + } + + if uint64(val) > upperBound { + return upperBound, overflow(val, tp) + } + + return uint64(val), nil +} + +// ConvertUintToUint converts an uint value to another uint value of different precision. +func ConvertUintToUint(val uint64, upperBound uint64, tp byte) (uint64, error) { + if val > upperBound { + return upperBound, overflow(val, tp) + } + + return val, nil +} + +// ConvertFloatToUint converts a float value to an uint value. +func ConvertFloatToUint(sc *stmtctx.StatementContext, fval float64, upperBound uint64, tp byte) (uint64, error) { + val := RoundFloat(fval) + if val < 0 { + if sc.ShouldClipToZero() { + return 0, overflow(val, tp) + } + return uint64(int64(val)), overflow(val, tp) + } + + ret, acc := new(big.Float).SetFloat64(val).Uint64() + if acc == big.Below || ret > upperBound { + return upperBound, overflow(val, tp) + } + return ret, nil +} + +// convertScientificNotation converts a decimal string with scientific notation to a normal decimal string. +// 1E6 => 1000000, .12345E+5 => 12345 +func convertScientificNotation(str string) (string, error) { + // https://golang.org/ref/spec#Floating-point_literals + eIdx := -1 + point := -1 + for i := 0; i < len(str); i++ { + if str[i] == '.' { + point = i + } + if str[i] == 'e' || str[i] == 'E' { + eIdx = i + if point == -1 { + point = i + } + break + } + } + if eIdx == -1 { + return str, nil + } + exp, err := strconv.ParseInt(str[eIdx+1:], 10, 64) + if err != nil { + return "", errors.WithStack(err) + } + + f := str[:eIdx] + if exp == 0 { + return f, nil + } else if exp > 0 { // move point right + if point+int(exp) == len(f)-1 { // 123.456 >> 3 = 123456. = 123456 + return f[:point] + f[point+1:], nil + } else if point+int(exp) < len(f)-1 { // 123.456 >> 2 = 12345.6 + return f[:point] + f[point+1:point+1+int(exp)] + "." + f[point+1+int(exp):], nil + } + // 123.456 >> 5 = 12345600 + return f[:point] + f[point+1:] + strings.Repeat("0", point+int(exp)-len(f)+1), nil + } else { // move point left + exp = -exp + if int(exp) < point { // 123.456 << 2 = 1.23456 + return f[:point-int(exp)] + "." + f[point-int(exp):point] + f[point+1:], nil + } + // 123.456 << 5 = 0.00123456 + return "0." + strings.Repeat("0", int(exp)-point) + f[:point] + f[point+1:], nil + } +} + +func convertDecimalStrToUint(sc *stmtctx.StatementContext, str string, upperBound uint64, tp byte) (uint64, error) { + str, err := convertScientificNotation(str) + if err != nil { + return 0, err + } + + var intStr, fracStr string + p := strings.Index(str, ".") + if p == -1 { + intStr = str + } else { + intStr = str[:p] + fracStr = str[p+1:] + } + intStr = strings.TrimLeft(intStr, "0") + if intStr == "" { + intStr = "0" + } + if intStr[0] == '-' { + return 0, overflow(str, tp) + } + + var round uint64 + if fracStr != "" && fracStr[0] >= '5' { + round++ + } + + upperStr := strconv.FormatUint(upperBound-round, 10) + if len(intStr) > len(upperStr) || + (len(intStr) == len(upperStr) && intStr > upperStr) { + return upperBound, overflow(str, tp) + } + + val, err := strconv.ParseUint(intStr, 10, 64) + if err != nil { + return val, overflow(str, tp) + } + return val + round, nil +} + +// ConvertDecimalToUint converts a decimal to a uint by converting it to a string first to avoid float overflow (#10181). +func ConvertDecimalToUint(sc *stmtctx.StatementContext, d *MyDecimal, upperBound uint64, tp byte) (uint64, error) { + return convertDecimalStrToUint(sc, string(d.ToString()), upperBound, tp) +} + +// StrToInt converts a string to an integer at the best-effort. +func StrToInt(sc *stmtctx.StatementContext, str string, isFuncCast bool) (int64, error) { + str = strings.TrimSpace(str) + validPrefix, err := getValidIntPrefix(sc, str, isFuncCast) + iVal, err1 := strconv.ParseInt(validPrefix, 10, 64) + if err1 != nil { + return iVal, ErrOverflow.GenWithStackByArgs("BIGINT", validPrefix) + } + return iVal, errors.Trace(err) +} + +// StrToUint converts a string to an unsigned integer at the best-effort. +func StrToUint(sc *stmtctx.StatementContext, str string, isFuncCast bool) (uint64, error) { + str = strings.TrimSpace(str) + validPrefix, err := getValidIntPrefix(sc, str, isFuncCast) + uVal := uint64(0) + hasParseErr := false + + if validPrefix[0] == '-' { + // only `-000*` is valid to be converted into unsigned integer + for _, v := range validPrefix[1:] { + if v != '0' { + hasParseErr = true + break + } + } + } else { + if validPrefix[0] == '+' { + validPrefix = validPrefix[1:] + } + v, e := strconv.ParseUint(validPrefix, 10, 64) + uVal, hasParseErr = v, e != nil + } + + if hasParseErr { + return uVal, ErrOverflow.GenWithStackByArgs("BIGINT UNSIGNED", validPrefix) + } + return uVal, errors.Trace(err) +} + +// StrToDateTime converts str to MySQL DateTime. +func StrToDateTime(sc *stmtctx.StatementContext, str string, fsp int) (Time, error) { + return ParseTime(sc, str, mysql.TypeDatetime, fsp, nil) +} + +// StrToDuration converts str to Duration. It returns Duration in normal case, +// and returns Time when str is in datetime format. +// when isDuration is true, the d is returned, when it is false, the t is returned. +// See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html. +func StrToDuration(sc *stmtctx.StatementContext, str string, fsp int) (d Duration, t Time, isDuration bool, err error) { + str = strings.TrimSpace(str) + length := len(str) + if length > 0 && str[0] == '-' { + length-- + } + if n := strings.IndexByte(str, '.'); n >= 0 { + length = length - len(str[n:]) + } + // Timestamp format is 'YYYYMMDDHHMMSS' or 'YYMMDDHHMMSS', which length is 12. + // See #3923, it explains what we do here. + if length >= 12 { + t, err = StrToDateTime(sc, str, fsp) + if err == nil { + return d, t, false, nil + } + } + + d, _, err = ParseDuration(sc, str, fsp) + if ErrTruncatedWrongVal.Equal(err) { + err = sc.HandleTruncate(err) + } + return d, t, true, errors.Trace(err) +} + +// NumberToDuration converts number to Duration. +func NumberToDuration(number int64, fsp int) (Duration, error) { + if number > TimeMaxValue { + // Try to parse DATETIME. + if number >= 10000000000 { // '2001-00-00 00-00-00' + if t, err := ParseDatetimeFromNum(nil, number); err == nil { + dur, err1 := t.ConvertToDuration() + return dur, errors.Trace(err1) + } + } + dur := MaxMySQLDuration(fsp) + return dur, ErrOverflow.GenWithStackByArgs("Duration", strconv.Itoa(int(number))) + } else if number < -TimeMaxValue { + dur := MaxMySQLDuration(fsp) + dur.Duration = -dur.Duration + return dur, ErrOverflow.GenWithStackByArgs("Duration", strconv.Itoa(int(number))) + } + var neg bool + if neg = number < 0; neg { + number = -number + } + + if number/10000 > TimeMaxHour || number%100 >= 60 || (number/100)%100 >= 60 { + return ZeroDuration, errors.Trace(ErrTruncatedWrongVal.GenWithStackByArgs(TimeStr, strconv.FormatInt(number, 10))) + } + dur := NewDuration(int(number/10000), int((number/100)%100), int(number%100), 0, fsp) + if neg { + dur.Duration = -dur.Duration + } + return dur, nil +} + +// getValidIntPrefix gets prefix of the string which can be successfully parsed as int. +func getValidIntPrefix(sc *stmtctx.StatementContext, str string, isFuncCast bool) (string, error) { + if !isFuncCast { + floatPrefix, err := getValidFloatPrefix(sc, str, isFuncCast) + if err != nil { + return floatPrefix, errors.Trace(err) + } + return floatStrToIntStr(sc, floatPrefix, str) + } + + validLen := 0 + + for i := 0; i < len(str); i++ { + c := str[i] + if (c == '+' || c == '-') && i == 0 { + continue + } + + if c >= '0' && c <= '9' { + validLen = i + 1 + continue + } + + break + } + valid := str[:validLen] + if valid == "" { + valid = "0" + } + if validLen == 0 || validLen != len(str) { + return valid, errors.Trace(sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", str))) + } + return valid, nil +} + +// roundIntStr is to round a **valid int string** base on the number following dot. +func roundIntStr(numNextDot byte, intStr string) string { + if numNextDot < '5' { + return intStr + } + retStr := []byte(intStr) + idx := len(intStr) - 1 + for ; idx >= 1; idx-- { + if retStr[idx] != '9' { + retStr[idx]++ + break + } + retStr[idx] = '0' + } + if idx == 0 { + if intStr[0] == '9' { + retStr[0] = '1' + retStr = append(retStr, '0') + } else if isDigit(intStr[0]) { + retStr[0]++ + } else { + retStr[1] = '1' + retStr = append(retStr, '0') + } + } + return string(retStr) +} + +// floatStrToIntStr converts a valid float string into valid integer string which can be parsed by +// strconv.ParseInt, we can't parse float first then convert it to string because precision will +// be lost. For example, the string value "18446744073709551615" which is the max number of unsigned +// int will cause some precision to lose. intStr[0] may be a positive and negative sign like '+' or '-'. +// +// This func will find serious overflow such as the len of intStr > 20 (without prefix `+/-`) +// however, it will not check whether the intStr overflow BIGINT. +func floatStrToIntStr(sc *stmtctx.StatementContext, validFloat string, oriStr string) (intStr string, _ error) { + var dotIdx = -1 + var eIdx = -1 + for i := 0; i < len(validFloat); i++ { + switch validFloat[i] { + case '.': + dotIdx = i + case 'e', 'E': + eIdx = i + } + } + if eIdx == -1 { + if dotIdx == -1 { + return validFloat, nil + } + var digits []byte + if validFloat[0] == '-' || validFloat[0] == '+' { + dotIdx-- + digits = []byte(validFloat[1:]) + } else { + digits = []byte(validFloat) + } + if dotIdx == 0 { + intStr = "0" + } else { + intStr = string(digits)[:dotIdx] + } + if len(digits) > dotIdx+1 { + intStr = roundIntStr(digits[dotIdx+1], intStr) + } + if (len(intStr) > 1 || intStr[0] != '0') && validFloat[0] == '-' { + intStr = "-" + intStr + } + return intStr, nil + } + // intCnt and digits contain the prefix `+/-` if validFloat[0] is `+/-` + var intCnt int + digits := make([]byte, 0, len(validFloat)) + if dotIdx == -1 { + digits = append(digits, validFloat[:eIdx]...) + intCnt = len(digits) + } else { + digits = append(digits, validFloat[:dotIdx]...) + intCnt = len(digits) + digits = append(digits, validFloat[dotIdx+1:eIdx]...) + } + exp, err := strconv.Atoi(validFloat[eIdx+1:]) + if err != nil { + return validFloat, errors.Trace(err) + } + intCnt += exp + if exp >= 0 && (intCnt > 21 || intCnt < 0) { + // MaxInt64 has 19 decimal digits. + // MaxUint64 has 20 decimal digits. + // And the intCnt may contain the len of `+/-`, + // so I use 21 here as the early detection. + sc.AppendWarning(ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)) + return validFloat[:eIdx], nil + } + if intCnt <= 0 { + intStr = "0" + if intCnt == 0 && len(digits) > 0 && isDigit(digits[0]) { + intStr = roundIntStr(digits[0], intStr) + } + return intStr, nil + } + if intCnt == 1 && (digits[0] == '-' || digits[0] == '+') { + intStr = "0" + if len(digits) > 1 { + intStr = roundIntStr(digits[1], intStr) + } + if intStr[0] == '1' { + intStr = string(digits[:1]) + intStr + } + return intStr, nil + } + if intCnt <= len(digits) { + intStr = string(digits[:intCnt]) + if intCnt < len(digits) { + intStr = roundIntStr(digits[intCnt], intStr) + } + } else { + // convert scientific notation decimal number + extraZeroCount := intCnt - len(digits) + intStr = string(digits) + strings.Repeat("0", extraZeroCount) + } + return intStr, nil +} + +// StrToFloat converts a string to a float64 at the best-effort. +func StrToFloat(sc *stmtctx.StatementContext, str string, isFuncCast bool) (float64, error) { + str = strings.TrimSpace(str) + validStr, err := getValidFloatPrefix(sc, str, isFuncCast) + f, err1 := strconv.ParseFloat(validStr, 64) + if err1 != nil { + if err2, ok := err1.(*strconv.NumError); ok { + // value will truncate to MAX/MIN if out of range. + if err2.Err == strconv.ErrRange { + err1 = sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("DOUBLE", str)) + if math.IsInf(f, 1) { + f = math.MaxFloat64 + } else if math.IsInf(f, -1) { + f = -math.MaxFloat64 + } + } + } + return f, errors.Trace(err1) + } + return f, errors.Trace(err) +} + +// ConvertJSONToInt64 casts JSON into int64. +func ConvertJSONToInt64(sc *stmtctx.StatementContext, j BinaryJSON, unsigned bool) (int64, error) { + return ConvertJSONToInt(sc, j, unsigned, mysql.TypeLonglong) +} + +// ConvertJSONToInt casts JSON into int by type. +func ConvertJSONToInt(sc *stmtctx.StatementContext, j BinaryJSON, unsigned bool, tp byte) (int64, error) { + switch j.TypeCode { + case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: + return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", j.String())) + case JSONTypeCodeLiteral: + switch j.Value[0] { + case JSONLiteralFalse: + return 0, nil + case JSONLiteralNil: + return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", j.String())) + default: + return 1, nil + } + case JSONTypeCodeInt64: + i := j.GetInt64() + if unsigned { + uBound := IntergerUnsignedUpperBound(tp) + u, err := ConvertIntToUint(sc, i, uBound, tp) + return int64(u), sc.HandleOverflow(err, err) + } + + lBound := IntergerSignedLowerBound(tp) + uBound := IntergerSignedUpperBound(tp) + i, err := ConvertIntToInt(i, lBound, uBound, tp) + return i, sc.HandleOverflow(err, err) + case JSONTypeCodeUint64: + u := j.GetUint64() + if unsigned { + uBound := IntergerUnsignedUpperBound(tp) + u, err := ConvertUintToUint(u, uBound, tp) + return int64(u), sc.HandleOverflow(err, err) + } + + uBound := IntergerSignedUpperBound(tp) + i, err := ConvertUintToInt(u, uBound, tp) + return i, sc.HandleOverflow(err, err) + case JSONTypeCodeFloat64: + f := j.GetFloat64() + if !unsigned { + lBound := IntergerSignedLowerBound(tp) + uBound := IntergerSignedUpperBound(tp) + u, e := ConvertFloatToInt(f, lBound, uBound, tp) + return u, sc.HandleOverflow(e, e) + } + bound := IntergerUnsignedUpperBound(tp) + u, err := ConvertFloatToUint(sc, f, bound, tp) + return int64(u), sc.HandleOverflow(err, err) + case JSONTypeCodeString: + str := string(hack.String(j.GetString())) + if !unsigned { + r, e := StrToInt(sc, str, false) + return r, sc.HandleOverflow(e, e) + } + u, err := StrToUint(sc, str, false) + return int64(u), sc.HandleOverflow(err, err) + } + return 0, errors.New("Unknown type code in JSON") +} + +// ConvertJSONToFloat casts JSON into float64. +func ConvertJSONToFloat(sc *stmtctx.StatementContext, j BinaryJSON) (float64, error) { + switch j.TypeCode { + case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: + return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("FLOAT", j.String())) + case JSONTypeCodeLiteral: + switch j.Value[0] { + case JSONLiteralFalse: + return 0, nil + case JSONLiteralNil: + return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("FLOAT", j.String())) + default: + return 1, nil + } + case JSONTypeCodeInt64: + return float64(j.GetInt64()), nil + case JSONTypeCodeUint64: + return float64(j.GetUint64()), nil + case JSONTypeCodeFloat64: + return j.GetFloat64(), nil + case JSONTypeCodeString: + str := string(hack.String(j.GetString())) + return StrToFloat(sc, str, false) + } + return 0, errors.New("Unknown type code in JSON") +} + +// ConvertJSONToDecimal casts JSON into decimal. +func ConvertJSONToDecimal(sc *stmtctx.StatementContext, j BinaryJSON) (*MyDecimal, error) { + var err error = nil + res := new(MyDecimal) + switch j.TypeCode { + case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: + err = ErrTruncatedWrongVal.GenWithStackByArgs("DECIMAL", j.String()) + case JSONTypeCodeLiteral: + switch j.Value[0] { + case JSONLiteralFalse: + res = res.FromInt(0) + case JSONLiteralNil: + err = ErrTruncatedWrongVal.GenWithStackByArgs("DECIMAL", j.String()) + default: + res = res.FromInt(1) + } + case JSONTypeCodeInt64: + res = res.FromInt(j.GetInt64()) + case JSONTypeCodeUint64: + res = res.FromUint(j.GetUint64()) + case JSONTypeCodeFloat64: + err = res.FromFloat64(j.GetFloat64()) + case JSONTypeCodeString: + err = res.FromString(j.GetString()) + } + err = sc.HandleTruncate(err) + if err != nil { + return res, errors.Trace(err) + } + return res, errors.Trace(err) +} + +// getValidFloatPrefix gets prefix of string which can be successfully parsed as float. +func getValidFloatPrefix(sc *stmtctx.StatementContext, s string, isFuncCast bool) (valid string, err error) { + if isFuncCast && s == "" { + return "0", nil + } + + var ( + sawDot bool + sawDigit bool + validLen int + eIdx = -1 + ) + for i := 0; i < len(s); i++ { + c := s[i] + if c == '+' || c == '-' { + if i != 0 && i != eIdx+1 { // "1e+1" is valid. + break + } + } else if c == '.' { + if sawDot || eIdx > 0 { // "1.1." or "1e1.1" + break + } + sawDot = true + if sawDigit { // "123." is valid. + validLen = i + 1 + } + } else if c == 'e' || c == 'E' { + if !sawDigit { // "+.e" + break + } + if eIdx != -1 { // "1e5e" + break + } + eIdx = i + } else if c == '\u0000' { + s = s[:validLen] + break + } else if c < '0' || c > '9' { + break + } else { + sawDigit = true + validLen = i + 1 + } + } + valid = s[:validLen] + if valid == "" { + valid = "0" + } + if validLen == 0 || validLen != len(s) { + err = errors.Trace(sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("DOUBLE", s))) + } + return valid, err +} + +// ToString converts an interface to a string. +func ToString(value interface{}) (string, error) { + switch v := value.(type) { + case bool: + if v { + return "1", nil + } + return "0", nil + case int: + return strconv.FormatInt(int64(v), 10), nil + case int64: + return strconv.FormatInt(v, 10), nil + case uint64: + return strconv.FormatUint(v, 10), nil + case float32: + return strconv.FormatFloat(float64(v), 'f', -1, 32), nil + case float64: + return strconv.FormatFloat(v, 'f', -1, 64), nil + case string: + return v, nil + case []byte: + return string(v), nil + case Time: + return v.String(), nil + case Duration: + return v.String(), nil + case *MyDecimal: + return v.String(), nil + case BinaryLiteral: + return v.ToString(), nil + case Enum: + return v.String(), nil + case Set: + return v.String(), nil + case BinaryJSON: + return v.String(), nil + default: + return "", errors.Errorf("cannot convert %v(type %T) to string", value, value) + } +} diff --git a/types/convert_test.go b/pkg/types/convert_test.go similarity index 99% rename from types/convert_test.go rename to pkg/types/convert_test.go index 5ca795cb23ab5..b255e908a0f62 100644 --- a/types/convert_test.go +++ b/pkg/types/convert_test.go @@ -22,10 +22,10 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" "github.com/stretchr/testify/require" ) diff --git a/types/core_time.go b/pkg/types/core_time.go similarity index 100% rename from types/core_time.go rename to pkg/types/core_time.go diff --git a/types/core_time_test.go b/pkg/types/core_time_test.go similarity index 100% rename from types/core_time_test.go rename to pkg/types/core_time_test.go diff --git a/types/datum.go b/pkg/types/datum.go similarity index 99% rename from types/datum.go rename to pkg/types/datum.go index 35c072cc3c03d..034b07add7e53 100644 --- a/types/datum.go +++ b/pkg/types/datum.go @@ -28,14 +28,14 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/types/datum_eval.go b/pkg/types/datum_eval.go similarity index 95% rename from types/datum_eval.go rename to pkg/types/datum_eval.go index 126d13b2ed321..cb3c0a87148ed 100644 --- a/types/datum_eval.go +++ b/pkg/types/datum_eval.go @@ -16,8 +16,8 @@ package types import ( "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // ComputePlus computes the result of a+b. diff --git a/types/datum_test.go b/pkg/types/datum_test.go similarity index 99% rename from types/datum_test.go rename to pkg/types/datum_test.go index 41430d3943bf7..4b6a4e576207e 100644 --- a/types/datum_test.go +++ b/pkg/types/datum_test.go @@ -24,11 +24,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/types/enum.go b/pkg/types/enum.go similarity index 96% rename from types/enum.go rename to pkg/types/enum.go index 47e4684536412..5a973496bfd9b 100644 --- a/types/enum.go +++ b/pkg/types/enum.go @@ -19,8 +19,8 @@ import ( "strconv" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // Enum is for MySQL enum type. diff --git a/types/enum_test.go b/pkg/types/enum_test.go similarity index 98% rename from types/enum_test.go rename to pkg/types/enum_test.go index c7a349a10245d..4eb9abd744ca2 100644 --- a/types/enum_test.go +++ b/pkg/types/enum_test.go @@ -17,7 +17,7 @@ package types import ( "testing" - "github.com/pingcap/tidb/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/mysql" "github.com/stretchr/testify/require" ) diff --git a/pkg/types/errors.go b/pkg/types/errors.go new file mode 100644 index 0000000000000..8c7dffcf73b8b --- /dev/null +++ b/pkg/types/errors.go @@ -0,0 +1,97 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package types + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + parser_types "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// const strings for ErrWrongValue +const ( + DateTimeStr = "datetime" + DateStr = "date" + TimeStr = "time" + TimestampStr = "timestamp" +) + +var ( + // ErrInvalidDefault is returned when meet a invalid default value. + ErrInvalidDefault = parser_types.ErrInvalidDefault + // ErrDataTooLong is returned when converts a string value that is longer than field type length. + ErrDataTooLong = dbterror.ClassTypes.NewStd(mysql.ErrDataTooLong) + // ErrIllegalValueForType is returned when value of type is illegal. + ErrIllegalValueForType = dbterror.ClassTypes.NewStd(mysql.ErrIllegalValueForType) + // ErrTruncated is returned when data has been truncated during conversion. + ErrTruncated = dbterror.ClassTypes.NewStd(mysql.WarnDataTruncated) + // ErrOverflow is returned when data is out of range for a field type. + ErrOverflow = dbterror.ClassTypes.NewStd(mysql.ErrDataOutOfRange) + // ErrDivByZero is return when do division by 0. + ErrDivByZero = dbterror.ClassTypes.NewStd(mysql.ErrDivisionByZero) + // ErrTooBigDisplayWidth is return when display width out of range for column. + ErrTooBigDisplayWidth = dbterror.ClassTypes.NewStd(mysql.ErrTooBigDisplaywidth) + // ErrTooBigFieldLength is return when column length too big for column. + ErrTooBigFieldLength = dbterror.ClassTypes.NewStd(mysql.ErrTooBigFieldlength) + // ErrTooBigSet is returned when too many strings for column. + ErrTooBigSet = dbterror.ClassTypes.NewStd(mysql.ErrTooBigSet) + // ErrTooBigScale is returned when type DECIMAL/NUMERIC scale is bigger than mysql.MaxDecimalScale. + ErrTooBigScale = dbterror.ClassTypes.NewStd(mysql.ErrTooBigScale) + // ErrTooBigPrecision is returned when type DECIMAL/NUMERIC or DATETIME precision is bigger than mysql.MaxDecimalWidth or types.MaxFsp + ErrTooBigPrecision = dbterror.ClassTypes.NewStd(mysql.ErrTooBigPrecision) + // ErrBadNumber is return when parsing an invalid binary decimal number. + ErrBadNumber = dbterror.ClassTypes.NewStd(mysql.ErrBadNumber) + // ErrInvalidFieldSize is returned when the precision of a column is out of range. + ErrInvalidFieldSize = dbterror.ClassTypes.NewStd(mysql.ErrInvalidFieldSize) + // ErrMBiggerThanD is returned when precision less than the scale. + ErrMBiggerThanD = dbterror.ClassTypes.NewStd(mysql.ErrMBiggerThanD) + // ErrWarnDataOutOfRange is returned when the value in a numeric column that is outside the permissible range of the column data type. + // See https://dev.mysql.com/doc/refman/5.5/en/out-of-range-and-overflow.html for details + ErrWarnDataOutOfRange = dbterror.ClassTypes.NewStd(mysql.ErrWarnDataOutOfRange) + // ErrDuplicatedValueInType is returned when enum column has duplicated value. + ErrDuplicatedValueInType = dbterror.ClassTypes.NewStd(mysql.ErrDuplicatedValueInType) + // ErrDatetimeFunctionOverflow is returned when the calculation in datetime function cause overflow. + ErrDatetimeFunctionOverflow = dbterror.ClassTypes.NewStd(mysql.ErrDatetimeFunctionOverflow) + // ErrCastAsSignedOverflow is returned when positive out-of-range integer, and convert to it's negative complement. + ErrCastAsSignedOverflow = dbterror.ClassTypes.NewStd(mysql.ErrCastAsSignedOverflow) + // ErrCastNegIntAsUnsigned is returned when a negative integer be casted to an unsigned int. + ErrCastNegIntAsUnsigned = dbterror.ClassTypes.NewStd(mysql.ErrCastNegIntAsUnsigned) + // ErrInvalidYearFormat is returned when the input is not a valid year format. + ErrInvalidYearFormat = dbterror.ClassTypes.NewStd(mysql.ErrInvalidYearFormat) + // ErrInvalidYear is returned when the input value is not a valid year. + ErrInvalidYear = dbterror.ClassTypes.NewStd(mysql.ErrInvalidYear) + // ErrTruncatedWrongVal is returned when data has been truncated during conversion. + ErrTruncatedWrongVal = dbterror.ClassTypes.NewStd(mysql.ErrTruncatedWrongValue) + // ErrInvalidWeekModeFormat is returned when the week mode is wrong. + ErrInvalidWeekModeFormat = dbterror.ClassTypes.NewStd(mysql.ErrInvalidWeekModeFormat) + // ErrWrongFieldSpec is returned when the column specifier incorrect. + ErrWrongFieldSpec = dbterror.ClassTypes.NewStd(mysql.ErrWrongFieldSpec) + // ErrSyntax is returned when the syntax is not allowed. + ErrSyntax = dbterror.ClassTypes.NewStdErr(mysql.ErrParse, mysql.MySQLErrName[mysql.ErrSyntax]) + // ErrWrongValue is returned when the input value is in wrong format. + ErrWrongValue = dbterror.ClassTypes.NewStdErr(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrWrongValue]) + // ErrWrongValue2 is returned when the input value is in wrong format. + ErrWrongValue2 = dbterror.ClassTypes.NewStdErr(mysql.ErrWrongValue, mysql.MySQLErrName[mysql.ErrWrongValue]) + // ErrWrongValueForType is returned when the input value is in wrong format for function. + ErrWrongValueForType = dbterror.ClassTypes.NewStdErr(mysql.ErrWrongValueForType, mysql.MySQLErrName[mysql.ErrWrongValueForType]) + // ErrPartitionStatsMissing is returned when the partition-level stats is missing and the build global-level stats fails. + // Put this error here is to prevent `import cycle not allowed`. + ErrPartitionStatsMissing = dbterror.ClassTypes.NewStd(mysql.ErrPartitionStatsMissing) + // ErrPartitionColumnStatsMissing is returned when the partition-level column stats is missing and the build global-level stats fails. + // Put this error here is to prevent `import cycle not allowed`. + ErrPartitionColumnStatsMissing = dbterror.ClassTypes.NewStd(mysql.ErrPartitionColumnStatsMissing) + // ErrIncorrectDatetimeValue is returned when the input value is in wrong format for datetime. + ErrIncorrectDatetimeValue = dbterror.ClassTypes.NewStd(mysql.ErrIncorrectDatetimeValue) +) diff --git a/pkg/types/errors_test.go b/pkg/types/errors_test.go new file mode 100644 index 0000000000000..92cdc08b9656e --- /dev/null +++ b/pkg/types/errors_test.go @@ -0,0 +1,55 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package types + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/require" +) + +func TestError(t *testing.T) { + kvErrs := []*terror.Error{ + ErrInvalidDefault, + ErrDataTooLong, + ErrIllegalValueForType, + ErrTruncated, + ErrOverflow, + ErrDivByZero, + ErrTooBigDisplayWidth, + ErrTooBigFieldLength, + ErrTooBigSet, + ErrTooBigScale, + ErrTooBigPrecision, + ErrBadNumber, + ErrInvalidFieldSize, + ErrMBiggerThanD, + ErrWarnDataOutOfRange, + ErrDuplicatedValueInType, + ErrDatetimeFunctionOverflow, + ErrCastAsSignedOverflow, + ErrCastNegIntAsUnsigned, + ErrInvalidYearFormat, + ErrTruncatedWrongVal, + ErrInvalidWeekModeFormat, + ErrWrongValue, + } + + for _, err := range kvErrs { + code := terror.ToSQLError(err).Code + require.Equalf(t, code, uint16(err.Code()), "err: %v", err) + } +} diff --git a/pkg/types/etc.go b/pkg/types/etc.go new file mode 100644 index 0000000000000..4e177124c5d55 --- /dev/null +++ b/pkg/types/etc.go @@ -0,0 +1,208 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Copyright 2014 The ql Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSES/QL-LICENSE file. + +package types + +import ( + "io" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/opcode" + "github.com/pingcap/tidb/pkg/parser/terror" + ast "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/util/collate" +) + +// IsTypeBlob returns a boolean indicating whether the tp is a blob type. +var IsTypeBlob = ast.IsTypeBlob + +// IsTypeChar returns a boolean indicating +// whether the tp is the char type like a string type or a varchar type. +var IsTypeChar = ast.IsTypeChar + +// IsTypeVarchar returns a boolean indicating +// whether the tp is the varchar type like a varstring type or a varchar type. +func IsTypeVarchar(tp byte) bool { + return tp == mysql.TypeVarString || tp == mysql.TypeVarchar +} + +// IsTypeUnspecified returns a boolean indicating whether the tp is the Unspecified type. +func IsTypeUnspecified(tp byte) bool { + return tp == mysql.TypeUnspecified +} + +// IsTypePrefixable returns a boolean indicating +// whether an index on a column with the tp can be defined with a prefix. +func IsTypePrefixable(tp byte) bool { + return IsTypeBlob(tp) || IsTypeChar(tp) +} + +// IsTypeFractionable returns a boolean indicating +// whether the tp can has time fraction. +func IsTypeFractionable(tp byte) bool { + return tp == mysql.TypeDatetime || tp == mysql.TypeDuration || tp == mysql.TypeTimestamp +} + +// IsTypeTime returns a boolean indicating +// whether the tp is time type like datetime, date or timestamp. +func IsTypeTime(tp byte) bool { + return tp == mysql.TypeDatetime || tp == mysql.TypeDate || tp == mysql.TypeTimestamp +} + +// IsTypeFloat indicates whether the type is TypeFloat +func IsTypeFloat(tp byte) bool { + return tp == mysql.TypeFloat +} + +// IsTypeInteger returns a boolean indicating whether the tp is integer type. +func IsTypeInteger(tp byte) bool { + switch tp { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: + return true + } + return false +} + +// IsTypeNumeric returns a boolean indicating whether the tp is numeric type. +func IsTypeNumeric(tp byte) bool { + switch tp { + case mysql.TypeBit, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeNewDecimal, + mysql.TypeFloat, mysql.TypeDouble, mysql.TypeShort: + return true + } + return false +} + +// IsTypeBit returns a boolean indicating whether the tp is bit type. +func IsTypeBit(ft *FieldType) bool { + return ft.GetType() == mysql.TypeBit +} + +// IsTemporalWithDate returns a boolean indicating +// whether the tp is time type with date. +func IsTemporalWithDate(tp byte) bool { + return IsTypeTime(tp) +} + +// IsBinaryStr returns a boolean indicating +// whether the field type is a binary string type. +func IsBinaryStr(ft *FieldType) bool { + return ft.GetCollate() == charset.CollationBin && IsString(ft.GetType()) +} + +// IsNonBinaryStr returns a boolean indicating +// whether the field type is a non-binary string type. +func IsNonBinaryStr(ft *FieldType) bool { + if ft.GetCollate() != charset.CollationBin && IsString(ft.GetType()) { + return true + } + return false +} + +// NeedRestoredData returns if a type needs restored data. +// If the type is char and the collation is _bin, NeedRestoredData() returns false. +func NeedRestoredData(ft *FieldType) bool { + if collate.NewCollationEnabled() && + IsNonBinaryStr(ft) && + (!collate.IsBinCollation(ft.GetCollate()) || IsTypeVarchar(ft.GetType())) && + ft.GetCollate() != "utf8mb4_0900_bin" { + return true + } + return false +} + +// IsString returns a boolean indicating +// whether the field type is a string type. +func IsString(tp byte) bool { + return IsTypeChar(tp) || IsTypeBlob(tp) || IsTypeVarchar(tp) || IsTypeUnspecified(tp) +} + +// IsStringKind returns a boolean indicating whether the tp is a string type. +func IsStringKind(kind byte) bool { + return kind == KindString || kind == KindBytes +} + +var kind2Str = map[byte]string{ + KindNull: "null", + KindInt64: "bigint", + KindUint64: "unsigned bigint", + KindFloat32: "float", + KindFloat64: "double", + KindString: "char", + KindBytes: "bytes", + KindBinaryLiteral: "bit/hex literal", + KindMysqlDecimal: "decimal", + KindMysqlDuration: "time", + KindMysqlEnum: "enum", + KindMysqlBit: "bit", + KindMysqlSet: "set", + KindMysqlTime: "datetime", + KindInterface: "interface", + KindMinNotNull: "min_not_null", + KindMaxValue: "max_value", + KindRaw: "raw", + KindMysqlJSON: "json", +} + +// TypeStr converts tp to a string. +var TypeStr = ast.TypeStr + +// KindStr converts kind to a string. +func KindStr(kind byte) (r string) { + return kind2Str[kind] +} + +// TypeToStr converts a field to a string. +// It is used for converting Text to Blob, +// or converting Char to Binary. +// Args: +// +// tp: type enum +// cs: charset +var TypeToStr = ast.TypeToStr + +// EOFAsNil filtrates errors, +// If err is equal to io.EOF returns nil. +func EOFAsNil(err error) error { + if terror.ErrorEqual(err, io.EOF) { + return nil + } + return errors.Trace(err) +} + +// InvOp2 returns an invalid operation error. +func InvOp2(x, y interface{}, o opcode.Op) (interface{}, error) { + return nil, errors.Errorf("Invalid operation: %v %v %v (mismatched types %T and %T)", x, o, y, x, y) +} + +// overflow returns an overflowed error. +func overflow(v interface{}, tp byte) error { + return ErrOverflow.GenWithStack("constant %v overflows %s", v, TypeStr(tp)) +} + +// IsTypeTemporal checks if a type is a temporal type. +func IsTypeTemporal(tp byte) bool { + switch tp { + case mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeTimestamp, + mysql.TypeDate, mysql.TypeNewDate: + return true + } + return false +} diff --git a/pkg/types/etc_test.go b/pkg/types/etc_test.go new file mode 100644 index 0000000000000..8c1632c235348 --- /dev/null +++ b/pkg/types/etc_test.go @@ -0,0 +1,346 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "io" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/stretchr/testify/require" +) + +func testIsTypeBlob(t *testing.T, tp byte, expect bool) { + v := IsTypeBlob(tp) + require.Equal(t, expect, v) +} + +func testIsTypeChar(t *testing.T, tp byte, expect bool) { + v := IsTypeChar(tp) + require.Equal(t, expect, v) +} + +func TestIsType(t *testing.T) { + testIsTypeBlob(t, mysql.TypeTinyBlob, true) + testIsTypeBlob(t, mysql.TypeMediumBlob, true) + testIsTypeBlob(t, mysql.TypeBlob, true) + testIsTypeBlob(t, mysql.TypeLongBlob, true) + testIsTypeBlob(t, mysql.TypeInt24, false) + + testIsTypeChar(t, mysql.TypeString, true) + testIsTypeChar(t, mysql.TypeVarchar, true) + testIsTypeChar(t, mysql.TypeLong, false) +} + +func testTypeStr(t *testing.T, tp byte, expect string) { + v := TypeStr(tp) + require.Equal(t, expect, v) +} + +func testTypeToStr(t *testing.T, tp byte, charset string, expect string) { + v := TypeToStr(tp, charset) + require.Equal(t, expect, v) +} + +func TestTypeToStr(t *testing.T) { + testTypeStr(t, mysql.TypeYear, "year") + testTypeStr(t, 0xdd, "") + + testTypeToStr(t, mysql.TypeBlob, "utf8", "text") + testTypeToStr(t, mysql.TypeLongBlob, "utf8", "longtext") + testTypeToStr(t, mysql.TypeTinyBlob, "utf8", "tinytext") + testTypeToStr(t, mysql.TypeMediumBlob, "utf8", "mediumtext") + testTypeToStr(t, mysql.TypeVarchar, "binary", "varbinary") + testTypeToStr(t, mysql.TypeString, "binary", "binary") + testTypeToStr(t, mysql.TypeTiny, "binary", "tinyint") + testTypeToStr(t, mysql.TypeBlob, "binary", "blob") + testTypeToStr(t, mysql.TypeLongBlob, "binary", "longblob") + testTypeToStr(t, mysql.TypeTinyBlob, "binary", "tinyblob") + testTypeToStr(t, mysql.TypeMediumBlob, "binary", "mediumblob") + testTypeToStr(t, mysql.TypeVarchar, "utf8", "varchar") + testTypeToStr(t, mysql.TypeString, "utf8", "char") + testTypeToStr(t, mysql.TypeShort, "binary", "smallint") + testTypeToStr(t, mysql.TypeInt24, "binary", "mediumint") + testTypeToStr(t, mysql.TypeLong, "binary", "int") + testTypeToStr(t, mysql.TypeLonglong, "binary", "bigint") + testTypeToStr(t, mysql.TypeFloat, "binary", "float") + testTypeToStr(t, mysql.TypeDouble, "binary", "double") + testTypeToStr(t, mysql.TypeYear, "binary", "year") + testTypeToStr(t, mysql.TypeDuration, "binary", "time") + testTypeToStr(t, mysql.TypeDatetime, "binary", "datetime") + testTypeToStr(t, mysql.TypeDate, "binary", "date") + testTypeToStr(t, mysql.TypeTimestamp, "binary", "timestamp") + testTypeToStr(t, mysql.TypeNewDecimal, "binary", "decimal") + testTypeToStr(t, mysql.TypeUnspecified, "binary", "unspecified") + testTypeToStr(t, 0xdd, "binary", "") + testTypeToStr(t, mysql.TypeBit, "binary", "bit") + testTypeToStr(t, mysql.TypeEnum, "binary", "enum") + testTypeToStr(t, mysql.TypeSet, "binary", "set") +} + +func TestEOFAsNil(t *testing.T) { + err := EOFAsNil(io.EOF) + require.NoError(t, err) + err = EOFAsNil(errors.New("test")) + require.EqualError(t, err, "test") +} + +func TestMaxFloat(t *testing.T) { + tests := []struct { + flen int + decimal int + expect float64 + }{ + {3, 2, 9.99}, + {5, 2, 999.99}, + {10, 1, 999999999.9}, + {5, 5, 0.99999}, + } + + for _, test := range tests { + require.Equal(t, test.expect, GetMaxFloat(test.flen, test.decimal)) + } +} + +func TestRoundFloat(t *testing.T) { + tests := []struct { + input float64 + expect float64 + }{ + {2.5, 2}, + {1.5, 2}, + {0.5, 0}, + {0.49999999999999997, 0}, + {0, 0}, + {-0.49999999999999997, 0}, + {-0.5, 0}, + {-2.5, -2}, + {-1.5, -2}, + } + + for _, test := range tests { + require.Equal(t, test.expect, RoundFloat(test.input)) + } +} + +func TestRound(t *testing.T) { + tests := []struct { + input float64 + dec int + expect float64 + }{ + {-1.23, 0, -1}, + {-1.58, 0, -2}, + {1.58, 0, 2}, + {1.298, 1, 1.3}, + {1.298, 0, 1}, + {23.298, -1, 20}, + } + + for _, test := range tests { + require.Equal(t, test.expect, Round(test.input, test.dec)) + } +} + +func TestTruncateFloat(t *testing.T) { + tests := []struct { + input float64 + flen int + decimal int + expect float64 + err error + }{ + {100.114, 10, 2, 100.11, nil}, + {100.115, 10, 2, 100.12, nil}, + {100.1156, 10, 3, 100.116, nil}, + {100.1156, 3, 1, 99.9, ErrOverflow}, + {1.36, 10, 2, 1.36, nil}, + } + + for _, test := range tests { + f, err := TruncateFloat(test.input, test.flen, test.decimal) + require.Equal(t, test.expect, f) + require.Truef(t, terror.ErrorEqual(err, test.err), "err: %v", err) + } +} + +func TestIsTypeTemporal(t *testing.T) { + res := IsTypeTemporal(mysql.TypeDuration) + require.True(t, res) + res = IsTypeTemporal(mysql.TypeDatetime) + require.True(t, res) + res = IsTypeTemporal(mysql.TypeTimestamp) + require.True(t, res) + res = IsTypeTemporal(mysql.TypeDate) + require.True(t, res) + res = IsTypeTemporal(mysql.TypeNewDate) + require.True(t, res) + res = IsTypeTemporal('t') + require.False(t, res) +} + +func TestIsBinaryStr(t *testing.T) { + in := &FieldType{} + in.SetType(mysql.TypeBit) + in.SetFlag(mysql.UnsignedFlag) + in.SetFlen(1) + in.SetDecimal(0) + in.SetCharset(charset.CharsetUTF8) + in.SetCollate(charset.CollationUTF8) + + in.SetCollate(charset.CollationUTF8) + res := IsBinaryStr(in) + require.False(t, res) + + in.SetCollate(charset.CollationBin) + res = IsBinaryStr(in) + require.False(t, res) + + in.SetType(mysql.TypeBlob) + res = IsBinaryStr(in) + require.True(t, res) +} + +func TestIsNonBinaryStr(t *testing.T) { + in := NewFieldType(mysql.TypeBit) + in.SetFlag(mysql.UnsignedFlag) + in.SetFlen(1) + in.SetDecimal(0) + in.SetCharset(charset.CharsetUTF8) + in.SetCollate(charset.CollationUTF8) + + in.SetCollate(charset.CollationBin) + res := IsBinaryStr(in) + require.False(t, res) + + in.SetCollate(charset.CollationUTF8) + res = IsBinaryStr(in) + require.False(t, res) + + in.SetType(mysql.TypeBlob) + res = IsBinaryStr(in) + require.False(t, res) +} + +func TestIsTemporalWithDate(t *testing.T) { + res := IsTemporalWithDate(mysql.TypeDatetime) + require.True(t, res) + + res = IsTemporalWithDate(mysql.TypeDate) + require.True(t, res) + + res = IsTemporalWithDate(mysql.TypeTimestamp) + require.True(t, res) + + res = IsTemporalWithDate('t') + require.False(t, res) +} + +func TestIsTypePrefixable(t *testing.T) { + res := IsTypePrefixable('t') + require.False(t, res) + + res = IsTypePrefixable(mysql.TypeBlob) + require.True(t, res) +} + +func TestIsTypeFractionable(t *testing.T) { + res := IsTypeFractionable(mysql.TypeDatetime) + require.True(t, res) + + res = IsTypeFractionable(mysql.TypeDuration) + require.True(t, res) + + res = IsTypeFractionable(mysql.TypeTimestamp) + require.True(t, res) + + res = IsTypeFractionable('t') + require.False(t, res) +} + +func TestIsTypeNumeric(t *testing.T) { + res := IsTypeNumeric(mysql.TypeBit) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeTiny) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeInt24) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeLong) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeLonglong) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeNewDecimal) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeUnspecified) + require.False(t, res) + + res = IsTypeNumeric(mysql.TypeFloat) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeDouble) + require.True(t, res) + + res = IsTypeNumeric(mysql.TypeShort) + require.True(t, res) + + res = IsTypeNumeric('t') + require.False(t, res) +} + +func TestNeedRestoredData(t *testing.T) { + type testCase struct { + tp byte + charset string + collate string + result bool + } + cases := []testCase{ + {mysql.TypeString, "binary", "binary", false}, + {mysql.TypeVarString, "binary", "binary", false}, + {mysql.TypeString, "utf8mb4", "utf8mb4_bin", false}, + {mysql.TypeVarString, "utf8mb4", "utf8mb4_bin", true}, + {mysql.TypeString, "utf8mb4", "utf8mb4_general_ci", true}, + {mysql.TypeVarString, "utf8mb4", "utf8mb4_general_ci", true}, + {mysql.TypeString, "utf8mb4", "utf8mb4_unicode_ci", true}, + {mysql.TypeVarString, "utf8mb4", "utf8mb4_unicode_ci", true}, + {mysql.TypeString, "utf8mb4", "utf8mb4_0900_ai_ci", true}, + {mysql.TypeVarString, "utf8mb4", "utf8mb4_0900_ai_ci", true}, + {mysql.TypeString, "utf8mb4", "utf8mb4_0900_bin", false}, + {mysql.TypeVarString, "utf8mb4", "utf8mb4_0900_bin", false}, + {mysql.TypeString, "gbk", "gbk_bin", true}, + {mysql.TypeVarString, "gbk", "gbk_bin", true}, + {mysql.TypeString, "gbk", "gbk_chinese_ci", true}, + {mysql.TypeVarString, "gbk", "gbk_chinese_ci", true}, + } + + for _, c := range cases { + ft := NewFieldTypeBuilder(). + SetType(c.tp). + SetCharset(c.charset). + SetCollate(c.collate). + Build() + + require.Equal(t, c.result, NeedRestoredData(&ft), "NeedRestoredData of tp: %d charset: %s collate: %s should be %t", c.tp, c.charset, c.collate, c.result) + } +} diff --git a/types/eval_type.go b/pkg/types/eval_type.go similarity index 96% rename from types/eval_type.go rename to pkg/types/eval_type.go index 891dd537599b8..fd4cd93318723 100644 --- a/types/eval_type.go +++ b/pkg/types/eval_type.go @@ -14,7 +14,7 @@ package types -import ast "github.com/pingcap/tidb/parser/types" +import ast "github.com/pingcap/tidb/pkg/parser/types" // EvalType indicates the specified types that arguments and result of a built-in function should be. type EvalType = ast.EvalType diff --git a/types/explain_format.go b/pkg/types/explain_format.go similarity index 100% rename from types/explain_format.go rename to pkg/types/explain_format.go diff --git a/types/export_test.go b/pkg/types/export_test.go similarity index 100% rename from types/export_test.go rename to pkg/types/export_test.go diff --git a/types/field_name.go b/pkg/types/field_name.go similarity index 95% rename from types/field_name.go rename to pkg/types/field_name.go index 9c546fdc08e46..3ebf0f74fb616 100644 --- a/types/field_name.go +++ b/pkg/types/field_name.go @@ -17,9 +17,9 @@ package types import ( "strings" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/size" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/size" ) // FieldName records the names used for mysql protocol. diff --git a/pkg/types/field_type.go b/pkg/types/field_type.go new file mode 100644 index 0000000000000..6791d906ac25f --- /dev/null +++ b/pkg/types/field_type.go @@ -0,0 +1,1490 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "fmt" + "strconv" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + ast "github.com/pingcap/tidb/pkg/parser/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/mathutil" +) + +// UnspecifiedLength is unspecified length. +const UnspecifiedLength = -1 + +// ErrorLength is error length for blob or text. +const ErrorLength = 0 + +// FieldType records field type information. +type FieldType = ast.FieldType + +// NewFieldType returns a FieldType, +// with a type and other information about field type. +func NewFieldType(tp byte) *FieldType { + charset1, collate1 := DefaultCharsetForType(tp) + flen, decimal := minFlenAndDecimalForType(tp) + return NewFieldTypeBuilder(). + SetType(tp). + SetCharset(charset1). + SetCollate(collate1). + SetFlen(flen). + SetDecimal(decimal). + BuildP() +} + +// NewFieldTypeWithCollation returns a FieldType, +// with a type and other information about field type. +func NewFieldTypeWithCollation(tp byte, collation string, length int) *FieldType { + coll, _ := charset.GetCollationByName(collation) + return NewFieldTypeBuilder().SetType(tp).SetFlen(length).SetCharset(coll.CharsetName).SetCollate(collation).SetDecimal(UnspecifiedLength).BuildP() +} + +// AggFieldType aggregates field types for a multi-argument function like `IF`, `IFNULL`, `COALESCE` +// whose return type is determined by the arguments' FieldTypes. +// Aggregation is performed by MergeFieldType function. +func AggFieldType(tps []*FieldType) *FieldType { + var currType FieldType + isMixedSign := false + for i, t := range tps { + if i == 0 && currType.GetType() == mysql.TypeUnspecified { + currType = *t + continue + } + mtp := MergeFieldType(currType.GetType(), t.GetType()) + isMixedSign = isMixedSign || (mysql.HasUnsignedFlag(currType.GetFlag()) != mysql.HasUnsignedFlag(t.GetFlag())) + currType.SetType(mtp) + currType.SetFlag(mergeTypeFlag(currType.GetFlag(), t.GetFlag())) + } + // integral promotion when tps contains signed and unsigned + if isMixedSign && IsTypeInteger(currType.GetType()) { + bumpRange := false // indicate one of tps bump currType range + for _, t := range tps { + bumpRange = bumpRange || (mysql.HasUnsignedFlag(t.GetFlag()) && (t.GetType() == currType.GetType() || + t.GetType() == mysql.TypeBit)) + } + if bumpRange { + switch currType.GetType() { + case mysql.TypeTiny: + currType.SetType(mysql.TypeShort) + case mysql.TypeShort: + currType.SetType(mysql.TypeInt24) + case mysql.TypeInt24: + currType.SetType(mysql.TypeLong) + case mysql.TypeLong: + currType.SetType(mysql.TypeLonglong) + case mysql.TypeLonglong: + currType.SetType(mysql.TypeNewDecimal) + } + } + } + + if mysql.HasUnsignedFlag(currType.GetFlag()) && !isMixedSign { + currType.AddFlag(mysql.UnsignedFlag) + } + + return &currType +} + +// TryToFixFlenOfDatetime try to fix flen of Datetime for specific func or other field merge cases +func TryToFixFlenOfDatetime(resultTp *FieldType) { + if resultTp.GetType() == mysql.TypeDatetime { + resultTp.SetFlen(mysql.MaxDatetimeWidthNoFsp) + if resultTp.GetDecimal() > 0 { + resultTp.SetFlen(resultTp.GetFlen() + resultTp.GetDecimal() + 1) + } + } +} + +// AggregateEvalType aggregates arguments' EvalType of a multi-argument function. +func AggregateEvalType(fts []*FieldType, flag *uint) EvalType { + var ( + aggregatedEvalType = ETString + unsigned bool + gotFirst bool + gotBinString bool + ) + lft := fts[0] + for _, ft := range fts { + if ft.GetType() == mysql.TypeNull { + continue + } + et := ft.EvalType() + rft := ft + if (IsTypeBlob(ft.GetType()) || IsTypeVarchar(ft.GetType()) || IsTypeChar(ft.GetType())) && mysql.HasBinaryFlag(ft.GetFlag()) { + gotBinString = true + } + if !gotFirst { + gotFirst = true + aggregatedEvalType = et + unsigned = mysql.HasUnsignedFlag(ft.GetFlag()) + } else { + aggregatedEvalType = mergeEvalType(aggregatedEvalType, et, lft, rft, unsigned, mysql.HasUnsignedFlag(ft.GetFlag())) + unsigned = unsigned && mysql.HasUnsignedFlag(ft.GetFlag()) + } + lft = rft + } + SetTypeFlag(flag, mysql.UnsignedFlag, unsigned) + SetTypeFlag(flag, mysql.BinaryFlag, !aggregatedEvalType.IsStringKind() || gotBinString) + return aggregatedEvalType +} + +func mergeEvalType(lhs, rhs EvalType, lft, rft *FieldType, isLHSUnsigned, isRHSUnsigned bool) EvalType { + if lft.GetType() == mysql.TypeUnspecified || rft.GetType() == mysql.TypeUnspecified { + if lft.GetType() == rft.GetType() { + return ETString + } + if lft.GetType() == mysql.TypeUnspecified { + lhs = rhs + } else { + rhs = lhs + } + } + if lhs.IsStringKind() || rhs.IsStringKind() { + return ETString + } else if lhs == ETReal || rhs == ETReal { + return ETReal + } else if lhs == ETDecimal || rhs == ETDecimal || isLHSUnsigned != isRHSUnsigned { + return ETDecimal + } + return ETInt +} + +// SetTypeFlag turns the flagItem on or off. +func SetTypeFlag(flag *uint, flagItem uint, on bool) { + if on { + *flag |= flagItem + } else { + *flag &= ^flagItem + } +} + +// InferParamTypeFromDatum is used for plan cache to infer the type of a parameter from its datum. +func InferParamTypeFromDatum(d *Datum, tp *FieldType) { + InferParamTypeFromUnderlyingValue(d.GetValue(), tp) + if IsStringKind(d.k) { + // consider charset and collation here + c, err := collate.GetCollationByName(d.collation) + if err != nil || c == nil { + return // use default charset and collation + } + tp.SetCharset(c.CharsetName) + tp.SetCollate(d.collation) + } +} + +// InferParamTypeFromUnderlyingValue is used for plan cache to infer the type of a parameter from its underlying value. +func InferParamTypeFromUnderlyingValue(value interface{}, tp *FieldType) { + switch value.(type) { + case nil: + tp.SetType(mysql.TypeVarString) + tp.SetFlen(UnspecifiedLength) + tp.SetDecimal(UnspecifiedLength) + default: + DefaultTypeForValue(value, tp, mysql.DefaultCharset, mysql.DefaultCollationName) + if hasVariantFieldLength(tp) { + tp.SetFlen(UnspecifiedLength) + } + if tp.GetType() == mysql.TypeUnspecified { + tp.SetType(mysql.TypeVarString) + } + } +} + +func hasVariantFieldLength(tp *FieldType) bool { + switch tp.GetType() { + case mysql.TypeLonglong, mysql.TypeVarString, mysql.TypeDouble, mysql.TypeBlob, + mysql.TypeBit, mysql.TypeDuration, mysql.TypeEnum, mysql.TypeSet: + return true + } + return false +} + +// DefaultTypeForValue returns the default FieldType for the value. +func DefaultTypeForValue(value interface{}, tp *FieldType, char string, collate string) { + if value != nil { + tp.AddFlag(mysql.NotNullFlag) + } + switch x := value.(type) { + case nil: + tp.SetType(mysql.TypeNull) + tp.SetFlen(0) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + case bool: + tp.SetType(mysql.TypeLonglong) + tp.SetFlen(1) + tp.SetDecimal(0) + tp.AddFlag(mysql.IsBooleanFlag) + SetBinChsClnFlag(tp) + case int: + tp.SetType(mysql.TypeLonglong) + tp.SetFlen(mathutil.StrLenOfInt64Fast(int64(x))) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + case int64: + tp.SetType(mysql.TypeLonglong) + tp.SetFlen(mathutil.StrLenOfInt64Fast(x)) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + case uint64: + tp.SetType(mysql.TypeLonglong) + tp.AddFlag(mysql.UnsignedFlag) + tp.SetFlen(mathutil.StrLenOfUint64Fast(x)) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + case string: + tp.SetType(mysql.TypeVarString) + // TODO: tp.flen should be len(x) * 3 (max bytes length of CharsetUTF8) + tp.SetFlen(len(x)) + tp.SetDecimal(UnspecifiedLength) + tp.SetCharset(char) + tp.SetCollate(collate) + case float32: + tp.SetType(mysql.TypeFloat) + s := strconv.FormatFloat(float64(x), 'f', -1, 32) + tp.SetFlen(len(s)) + tp.SetDecimal(UnspecifiedLength) + SetBinChsClnFlag(tp) + case float64: + tp.SetType(mysql.TypeDouble) + s := strconv.FormatFloat(x, 'f', -1, 64) + tp.SetFlen(len(s)) + tp.SetDecimal(UnspecifiedLength) + SetBinChsClnFlag(tp) + case []byte: + tp.SetType(mysql.TypeBlob) + tp.SetFlen(len(x)) + tp.SetDecimal(UnspecifiedLength) + SetBinChsClnFlag(tp) + case BitLiteral: + tp.SetType(mysql.TypeVarString) + tp.SetFlen(len(x) * 3) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + case HexLiteral: + tp.SetType(mysql.TypeVarString) + tp.SetFlen(len(x) * 3) + tp.SetDecimal(0) + tp.AddFlag(mysql.UnsignedFlag) + SetBinChsClnFlag(tp) + case BinaryLiteral: + tp.SetType(mysql.TypeVarString) + tp.SetFlen(len(x)) + tp.SetDecimal(0) + SetBinChsClnFlag(tp) + tp.DelFlag(mysql.BinaryFlag) + tp.AddFlag(mysql.UnsignedFlag) + case Time: + tp.SetType(x.Type()) + switch x.Type() { + case mysql.TypeDate: + tp.SetFlen(mysql.MaxDateWidth) + tp.SetDecimal(UnspecifiedLength) + case mysql.TypeDatetime, mysql.TypeTimestamp: + tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) + if x.Fsp() > DefaultFsp { // consider point('.') and the fractional part. + tp.SetFlen(tp.GetFlen() + x.Fsp() + 1) + } + tp.SetDecimal(x.Fsp()) + } + SetBinChsClnFlag(tp) + case Duration: + tp.SetType(mysql.TypeDuration) + tp.SetFlen(len(x.String())) + if x.Fsp > DefaultFsp { // consider point('.') and the fractional part. + tp.SetFlen(x.Fsp + 1) + } + tp.SetDecimal(x.Fsp) + SetBinChsClnFlag(tp) + case *MyDecimal: + tp.SetType(mysql.TypeNewDecimal) + tp.SetFlenUnderLimit(len(x.ToString())) + tp.SetDecimalUnderLimit(int(x.digitsFrac)) + // Add the length for `.`. + tp.SetFlenUnderLimit(tp.GetFlen() + 1) + SetBinChsClnFlag(tp) + case Enum: + tp.SetType(mysql.TypeEnum) + tp.SetFlen(len(x.Name)) + tp.SetDecimal(UnspecifiedLength) + SetBinChsClnFlag(tp) + case Set: + tp.SetType(mysql.TypeSet) + tp.SetFlen(len(x.Name)) + tp.SetDecimal(UnspecifiedLength) + SetBinChsClnFlag(tp) + case BinaryJSON: + tp.SetType(mysql.TypeJSON) + tp.SetFlen(UnspecifiedLength) + tp.SetDecimal(0) + tp.SetCharset(charset.CharsetUTF8MB4) + tp.SetCollate(charset.CollationUTF8MB4) + default: + tp.SetType(mysql.TypeUnspecified) + tp.SetFlen(UnspecifiedLength) + tp.SetDecimal(UnspecifiedLength) + tp.SetCharset(charset.CharsetUTF8MB4) + tp.SetCollate(charset.CollationUTF8MB4) + } +} + +// minFlenAndDecimalForType returns the minimum flen/decimal that can hold all the data for `tp`. +func minFlenAndDecimalForType(tp byte) (int, int) { + switch tp { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: + return mysql.GetDefaultFieldLengthAndDecimal(tp) + default: + // todo support non-integer type + return UnspecifiedLength, UnspecifiedLength + } +} + +// DefaultCharsetForType returns the default charset/collation for mysql type. +func DefaultCharsetForType(tp byte) (defaultCharset string, defaultCollation string) { + switch tp { + case mysql.TypeVarString, mysql.TypeString, mysql.TypeVarchar: + // Default charset for string types is utf8mb4. + return mysql.DefaultCharset, mysql.DefaultCollationName + } + return charset.CharsetBin, charset.CollationBin +} + +// MergeFieldType merges two MySQL type to a new type. +// This is used in hybrid field type expression. +// For example "select case c when 1 then 2 when 2 then 'tidb' from t;" +// The result field type of the case expression is the merged type of the two when clause. +// See https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L1042 +func MergeFieldType(a byte, b byte) byte { + ia := getFieldTypeIndex(a) + ib := getFieldTypeIndex(b) + return fieldTypeMergeRules[ia][ib] +} + +// mergeTypeFlag merges two MySQL type flag to a new one +// currently only NotNullFlag and UnsignedFlag is checked +// todo more flag need to be checked +func mergeTypeFlag(a, b uint) uint { + return a & (b&mysql.NotNullFlag | ^mysql.NotNullFlag) & (b&mysql.UnsignedFlag | ^mysql.UnsignedFlag) +} + +func getFieldTypeIndex(tp byte) int { + itp := int(tp) + if itp < fieldTypeTearFrom { + return itp + } + return fieldTypeTearFrom + itp - fieldTypeTearTo - 1 +} + +const ( + fieldTypeTearFrom = int(mysql.TypeBit) + 1 + fieldTypeTearTo = int(mysql.TypeJSON) - 1 + fieldTypeNum = fieldTypeTearFrom + (255 - fieldTypeTearTo) +) + +// https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L248 +var fieldTypeMergeRules = [fieldTypeNum][fieldTypeNum]byte{ + /* mysql.TypeUnspecified -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeNewDecimal, + // mysql.TypeShort mysql.TypeLong + mysql.TypeNewDecimal, mysql.TypeNewDecimal, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeUnspecified, mysql.TypeUnspecified, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeTiny -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeTiny, + // mysql.TypeShort mysql.TypeLong + mysql.TypeShort, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeTiny, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeInt24, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeTiny, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeShort -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeShort, + // mysql.TypeShort mysql.TypeLong + mysql.TypeShort, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeShort, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeInt24, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeShort, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeLong -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeLong, + // mysql.TypeShort mysql.TypeLong + mysql.TypeLong, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeLong, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeLong, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeLong, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeFloat -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeDouble, mysql.TypeFloat, + // mysql.TypeShort mysql.TypeLong + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeFloat, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeFloat, mysql.TypeFloat, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeFloat, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeDouble, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeDouble, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeDouble -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeShort mysql.TypeLong + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeDouble, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeDouble, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeDouble, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeDouble, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeNull -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeTiny, + // mysql.TypeShort mysql.TypeLong + mysql.TypeShort, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeNull, mysql.TypeTimestamp, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeLonglong, + // mysql.TypeDate mysql.TypeTime + mysql.TypeDate, mysql.TypeDuration, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeYear, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeBit, + // mysql.TypeJSON + mysql.TypeJSON, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeEnum, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeSet, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeGeometry, + }, + /* mysql.TypeTimestamp -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeTimestamp, mysql.TypeTimestamp, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeDatetime, mysql.TypeDatetime, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeLonglong -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeLonglong, + // mysql.TypeShort mysql.TypeLong + mysql.TypeLonglong, mysql.TypeLonglong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeLonglong, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeLong, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeLonglong, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeInt24 -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeInt24, + // mysql.TypeShort mysql.TypeLong + mysql.TypeInt24, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeInt24, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeInt24, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeInt24, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeDate -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeDate, mysql.TypeDatetime, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeDate, mysql.TypeDatetime, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeTime -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeDuration, mysql.TypeDatetime, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeDatetime, mysql.TypeDuration, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeDatetime -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeDatetime, mysql.TypeDatetime, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeDatetime, mysql.TypeDatetime, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeYear -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeUnspecified, mysql.TypeTiny, + // mysql.TypeShort mysql.TypeLong + mysql.TypeShort, mysql.TypeLong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeFloat, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeYear, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeInt24, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeYear, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeLonglong, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeNewDate -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeNewDate, mysql.TypeDatetime, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeNewDate, mysql.TypeDatetime, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeDatetime, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeNewDate, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeVarchar -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeVarchar, mysql.TypeVarchar, + }, + /* mysql.TypeBit -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeLonglong, + // mysql.TypeShort mysql.TypeLong + mysql.TypeLonglong, mysql.TypeLonglong, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeBit, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLonglong, mysql.TypeLonglong, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeLonglong, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeBit, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeJSON -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeJSON, mysql.TypeVarchar, + // mysql.TypeLongLONG mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate MYSQL_TYPE_TIME + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime MYSQL_TYPE_YEAR + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeJSON, + // mysql.TypeNewDecimal MYSQL_TYPE_ENUM + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeLongBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeLongBlob, mysql.TypeVarchar, + // mysql.TypeString MYSQL_TYPE_GEOMETRY + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeNewDecimal -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeNewDecimal, mysql.TypeNewDecimal, + // mysql.TypeShort mysql.TypeLong + mysql.TypeNewDecimal, mysql.TypeNewDecimal, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeDouble, mysql.TypeDouble, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeNewDecimal, mysql.TypeNewDecimal, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeNewDecimal, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeNewDecimal, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeNewDecimal, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeEnum -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeEnum, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeSet -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeSet, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeVarchar, + }, + /* mysql.TypeTinyBlob -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeShort mysql.TypeLong + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeDate mysql.TypeTime + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeBit <16>-<244> + mysql.TypeTinyBlob, + // mysql.TypeJSON + mysql.TypeLongBlob, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeTinyBlob, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeTinyBlob, mysql.TypeTinyBlob, + }, + /* mysql.TypeMediumBlob -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeShort mysql.TypeLong + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeDate mysql.TypeTime + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeBit <16>-<244> + mysql.TypeMediumBlob, + // mysql.TypeJSON + mysql.TypeLongBlob, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeMediumBlob, mysql.TypeMediumBlob, + }, + /* mysql.TypeLongBlob -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeShort mysql.TypeLong + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeDate mysql.TypeTime + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeBit <16>-<244> + mysql.TypeLongBlob, + // mysql.TypeJSON + mysql.TypeLongBlob, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeLongBlob, mysql.TypeLongBlob, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeLongBlob, mysql.TypeLongBlob, + }, + /* mysql.TypeBlob -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeShort mysql.TypeLong + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeDate mysql.TypeTime + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeBit <16>-<244> + mysql.TypeBlob, + // mysql.TypeJSON + mysql.TypeLongBlob, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeBlob, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeBlob, mysql.TypeBlob, + }, + /* mysql.TypeVarString -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeVarchar, mysql.TypeVarchar, + }, + /* mysql.TypeString -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeString, mysql.TypeString, + // mysql.TypeShort mysql.TypeLong + mysql.TypeString, mysql.TypeString, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeString, mysql.TypeString, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeString, mysql.TypeString, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeString, mysql.TypeString, + // mysql.TypeDate mysql.TypeTime + mysql.TypeString, mysql.TypeString, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeString, mysql.TypeString, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeString, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeString, + // mysql.TypeJSON + mysql.TypeString, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeString, mysql.TypeString, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeString, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeString, + }, + /* mysql.TypeGeometry -> */ + { + // mysql.TypeUnspecified mysql.TypeTiny + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeShort mysql.TypeLong + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeFloat mysql.TypeDouble + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNull mysql.TypeTimestamp + mysql.TypeGeometry, mysql.TypeVarchar, + // mysql.TypeLonglong mysql.TypeInt24 + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDate mysql.TypeTime + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeDatetime mysql.TypeYear + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeNewDate mysql.TypeVarchar + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeBit <16>-<244> + mysql.TypeVarchar, + // mysql.TypeJSON + mysql.TypeVarchar, + // mysql.TypeNewDecimal mysql.TypeEnum + mysql.TypeVarchar, mysql.TypeVarchar, + // mysql.TypeSet mysql.TypeTinyBlob + mysql.TypeVarchar, mysql.TypeTinyBlob, + // mysql.TypeMediumBlob mysql.TypeLongBlob + mysql.TypeMediumBlob, mysql.TypeLongBlob, + // mysql.TypeBlob mysql.TypeVarString + mysql.TypeBlob, mysql.TypeVarchar, + // mysql.TypeString mysql.TypeGeometry + mysql.TypeString, mysql.TypeGeometry, + }, +} + +// SetBinChsClnFlag sets charset, collation as 'binary' and adds binaryFlag to FieldType. +func SetBinChsClnFlag(ft *FieldType) { + ft.SetCharset(charset.CharsetBin) + ft.SetCollate(charset.CollationBin) + ft.AddFlag(mysql.BinaryFlag) +} + +// VarStorageLen indicates this column is a variable length column. +const VarStorageLen = ast.VarStorageLen + +// CheckModifyTypeCompatible checks whether changes column type to another is compatible and can be changed. +// If types are compatible and can be directly changed, nil err will be returned; otherwise the types are incompatible. +// There are two cases when types incompatible: +// 1. returned canReorg == true: types can be changed by reorg +// 2. returned canReorg == false: type change not supported yet +func CheckModifyTypeCompatible(origin *FieldType, to *FieldType) (canReorg bool, err error) { + // Deal with the same type. + if origin.GetType() == to.GetType() { + if origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet { + typeVar := "set" + if origin.GetType() == mysql.TypeEnum { + typeVar = "enum" + } + if len(to.GetElems()) < len(origin.GetElems()) { + msg := fmt.Sprintf("the number of %s column's elements is less than the original: %d", typeVar, len(origin.GetElems())) + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) + } + for index, originElem := range origin.GetElems() { + toElem := to.GetElems()[index] + if originElem != toElem { + msg := fmt.Sprintf("cannot modify %s column value %s to %s", typeVar, originElem, toElem) + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) + } + } + } + + if origin.GetType() == mysql.TypeNewDecimal { + // Floating-point and fixed-point types also can be UNSIGNED. As with integer types, this attribute prevents + // negative values from being stored in the column. Unlike the integer types, the upper range of column values + // remains the same. + if to.GetFlen() != origin.GetFlen() || to.GetDecimal() != origin.GetDecimal() || mysql.HasUnsignedFlag(to.GetFlag()) != mysql.HasUnsignedFlag(origin.GetFlag()) { + msg := fmt.Sprintf("decimal change from decimal(%d, %d) to decimal(%d, %d)", origin.GetFlen(), origin.GetDecimal(), to.GetFlen(), to.GetDecimal()) + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) + } + } + + needReorg, reason := needReorgToChange(origin, to) + if !needReorg { + return false, nil + } + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(reason) + } + + // Deal with the different type. + if !checkTypeChangeSupported(origin, to) { + unsupportedMsg := fmt.Sprintf("change from original type %v to %v is currently unsupported yet", origin.CompactStr(), to.CompactStr()) + return false, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg) + } + + // Check if different type can directly convert and no need to reorg. + stringToString := IsString(origin.GetType()) && IsString(to.GetType()) + integerToInteger := mysql.IsIntegerType(origin.GetType()) && mysql.IsIntegerType(to.GetType()) + if stringToString || integerToInteger { + needReorg, reason := needReorgToChange(origin, to) + if !needReorg { + return false, nil + } + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(reason) + } + + notCompatibleMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) + return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(notCompatibleMsg) +} + +func needReorgToChange(origin *FieldType, to *FieldType) (needReorg bool, reasonMsg string) { + toFlen := to.GetFlen() + originFlen := origin.GetFlen() + if mysql.IsIntegerType(to.GetType()) && mysql.IsIntegerType(origin.GetType()) { + // For integers, we should ignore the potential display length represented by flen, using + // the default flen of the type. + originFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(origin.GetType()) + toFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(to.GetType()) + } + + if ConvertBetweenCharAndVarchar(origin.GetType(), to.GetType()) { + return true, "conversion between char and varchar string needs reorganization" + } + + if toFlen > 0 && toFlen != originFlen { + if toFlen < originFlen { + return true, fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen) + } + + // Due to the behavior of padding \x00 at binary type, we need to reorg when binary length changed + isBinaryType := func(tp *FieldType) bool { return tp.GetType() == mysql.TypeString && IsBinaryStr(tp) } + if isBinaryType(origin) && isBinaryType(to) { + return true, "can't change binary types of different length" + } + } + if to.GetDecimal() > 0 && to.GetDecimal() < origin.GetDecimal() { + return true, fmt.Sprintf("decimal %d is less than origin %d", to.GetDecimal(), origin.GetDecimal()) + } + if mysql.HasUnsignedFlag(origin.GetFlag()) != mysql.HasUnsignedFlag(to.GetFlag()) { + return true, "can't change unsigned integer to signed or vice versa" + } + return false, "" +} + +func checkTypeChangeSupported(origin *FieldType, to *FieldType) bool { + if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear || + IsString(origin.GetType()) || origin.GetType() == mysql.TypeJSON) && + to.GetType() == mysql.TypeBit { + // TODO: Currently date/datetime/timestamp/time/year/string/json data type cast to bit are not compatible with mysql, should fix here after compatible. + return false + } + + if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear || + origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble || origin.GetType() == mysql.TypeJSON || origin.GetType() == mysql.TypeBit) && + (to.GetType() == mysql.TypeEnum || to.GetType() == mysql.TypeSet) { + // TODO: Currently date/datetime/timestamp/time/year/decimal/float/double/json/bit cast to enum/set are not support yet, should fix here after supported. + return false + } + + if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit || + origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble) && + (IsTypeTime(to.GetType())) { + // TODO: Currently enum/set/bit/decimal/float/double cast to date/datetime/timestamp type are not support yet, should fix here after supported. + return false + } + + if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit) && + to.GetType() == mysql.TypeDuration { + // TODO: Currently enum/set/bit cast to time are not support yet, should fix here after supported. + return false + } + + return true +} + +// ConvertBetweenCharAndVarchar is that Column type conversion between varchar to char need reorganization because +// 1. varchar -> char: char type is stored with the padding removed. All the indexes need to be rewritten. +// 2. char -> varchar: the index value encoding of secondary index on clustered primary key tables is different. +// These secondary indexes need to be rewritten. +func ConvertBetweenCharAndVarchar(oldCol, newCol byte) bool { + return (IsTypeVarchar(oldCol) && newCol == mysql.TypeString) || + (oldCol == mysql.TypeString && IsTypeVarchar(newCol) && collate.NewCollationEnabled()) +} + +// IsVarcharTooBigFieldLength check if the varchar type column exceeds the maximum length limit. +func IsVarcharTooBigFieldLength(colDefTpFlen int, colDefName, setCharset string) error { + desc, err := charset.GetCharsetInfo(setCharset) + if err != nil { + return errors.Trace(err) + } + maxFlen := mysql.MaxFieldVarCharLength + maxFlen /= desc.Maxlen + if colDefTpFlen != UnspecifiedLength && colDefTpFlen > maxFlen { + return ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDefName, maxFlen) + } + return nil +} diff --git a/types/field_type_builder.go b/pkg/types/field_type_builder.go similarity index 100% rename from types/field_type_builder.go rename to pkg/types/field_type_builder.go diff --git a/pkg/types/field_type_test.go b/pkg/types/field_type_test.go new file mode 100644 index 0000000000000..b136b5862ce01 --- /dev/null +++ b/pkg/types/field_type_test.go @@ -0,0 +1,466 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/stretchr/testify/require" +) + +func TestFieldType(t *testing.T) { + ft := NewFieldType(mysql.TypeDuration) + require.Equal(t, UnspecifiedLength, ft.GetFlen()) + require.Equal(t, UnspecifiedLength, ft.GetDecimal()) + + ft.SetDecimal(5) + require.Equal(t, "time(5)", ft.String()) + + ft = NewFieldType(mysql.TypeLong) + ft.SetFlen(5) + ft.SetFlen(5) + ft.SetFlag(mysql.UnsignedFlag | mysql.ZerofillFlag) + require.Equal(t, "int(5) UNSIGNED ZEROFILL", ft.String()) + require.Equal(t, "int(5) unsigned", ft.InfoSchemaStr()) + + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(12) // Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "float(12,3)", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(12) // Default + ft.SetDecimal(-1) // Default + require.Equal(t, "float", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(5) // Not Default + ft.SetDecimal(-1) // Default + require.Equal(t, "float", ft.String()) + ft = NewFieldType(mysql.TypeFloat) + ft.SetFlen(7) // Not Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "float(7,3)", ft.String()) + + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(22) // Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "double(22,3)", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(22) // Default + ft.SetDecimal(-1) // Default + require.Equal(t, "double", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(5) // Not Default + ft.SetDecimal(-1) // Default + require.Equal(t, "double", ft.String()) + ft = NewFieldType(mysql.TypeDouble) + ft.SetFlen(7) // Not Default + ft.SetDecimal(3) // Not Default + require.Equal(t, "double(7,3)", ft.String()) + + ft = NewFieldType(mysql.TypeBlob) + ft.SetFlen(10) + ft.SetCharset("UTF8") + ft.SetCollate("UTF8_UNICODE_GI") + require.Equal(t, "text CHARACTER SET UTF8 COLLATE UTF8_UNICODE_GI", ft.String()) + + ft = NewFieldType(mysql.TypeVarchar) + ft.SetFlen(10) + ft.AddFlag(mysql.BinaryFlag) + require.Equal(t, "varchar(10) BINARY CHARACTER SET utf8mb4 COLLATE utf8mb4_bin", ft.String()) + + ft = NewFieldType(mysql.TypeString) + ft.SetCharset(charset.CollationBin) + ft.AddFlag(mysql.BinaryFlag) + require.Equal(t, "binary(1) COLLATE utf8mb4_bin", ft.String()) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a", "b"}) + require.Equal(t, "enum('a','b')", ft.String()) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"'a'", "'b'"}) + require.Equal(t, "enum('''a''','''b''')", ft.String()) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a\nb", "a\tb", "a\rb"}) + require.Equal(t, "enum('a\\nb','a\tb','a\\rb')", ft.String()) + + ft = NewFieldType(mysql.TypeEnum) + ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) + require.Equal(t, "enum('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a", "b"}) + require.Equal(t, "set('a','b')", ft.String()) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"'a'", "'b'"}) + require.Equal(t, "set('''a''','''b''')", ft.String()) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) + require.Equal(t, "set('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) + + ft = NewFieldType(mysql.TypeSet) + ft.SetElems([]string{"a'\nb", "a'b\tc"}) + require.Equal(t, "set('a''\\nb','a''b c')", ft.String()) + + ft = NewFieldType(mysql.TypeTimestamp) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "timestamp(2)", ft.String()) + ft = NewFieldType(mysql.TypeTimestamp) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "timestamp", ft.String()) + + ft = NewFieldType(mysql.TypeDatetime) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "datetime(2)", ft.String()) + ft = NewFieldType(mysql.TypeDatetime) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "datetime", ft.String()) + + ft = NewFieldType(mysql.TypeDate) + ft.SetFlen(8) + ft.SetDecimal(2) + require.Equal(t, "date", ft.String()) + ft = NewFieldType(mysql.TypeDate) + ft.SetFlen(8) + ft.SetDecimal(0) + require.Equal(t, "date", ft.String()) + + ft = NewFieldType(mysql.TypeYear) + ft.SetFlen(4) + ft.SetDecimal(0) + require.Equal(t, "year(4)", ft.String()) + ft = NewFieldType(mysql.TypeYear) + ft.SetFlen(2) + ft.SetDecimal(2) + require.Equal(t, "year(2)", ft.String()) // Note: Invalid year. +} + +func TestDefaultTypeForValue(t *testing.T) { + tests := []struct { + value interface{} + tp byte + flen int + decimal int + charset string + collation string + flag uint + }{ + {nil, mysql.TypeNull, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, + {1, mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {0, mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {432, mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {4321, mysql.TypeLonglong, 4, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {1234567, mysql.TypeLonglong, 7, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {12345678, mysql.TypeLonglong, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {12345678901234567, mysql.TypeLonglong, 17, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {-42, mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {uint64(1), mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {uint64(123), mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {uint64(1234), mysql.TypeLonglong, 4, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {uint64(1234567), mysql.TypeLonglong, 7, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {uint64(12345678), mysql.TypeLonglong, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {uint64(12345678901234567), mysql.TypeLonglong, 17, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {"abc", mysql.TypeVarString, 3, UnspecifiedLength, charset.CharsetUTF8MB4, charset.CollationUTF8MB4, mysql.NotNullFlag}, + {1.1, mysql.TypeDouble, 3, -1, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {[]byte("abc"), mysql.TypeBlob, 3, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {HexLiteral{}, mysql.TypeVarString, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, + {BitLiteral{}, mysql.TypeVarString, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {NewTime(ZeroCoreTime, mysql.TypeDatetime, DefaultFsp), mysql.TypeDatetime, 19, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {NewTime(FromDate(2017, 12, 12, 12, 59, 59, 0), mysql.TypeDatetime, 3), mysql.TypeDatetime, 23, 3, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {Duration{}, mysql.TypeDuration, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {&MyDecimal{}, mysql.TypeNewDecimal, 2, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {Enum{Name: "a", Value: 1}, mysql.TypeEnum, 1, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + {Set{Name: "a", Value: 1}, mysql.TypeSet, 1, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, + } + + for i, tt := range tests { + var ft FieldType + DefaultTypeForValue(tt.value, &ft, mysql.DefaultCharset, mysql.DefaultCollationName) + require.Equalf(t, tt.tp, ft.GetType(), "%v %v %v", i, ft.GetType(), tt.tp) + require.Equalf(t, tt.flen, ft.GetFlen(), "%v %v %v", i, ft.GetFlen(), tt.flen) + require.Equalf(t, tt.charset, ft.GetCharset(), "%v %v %v", i, ft.GetCharset(), tt.charset) + require.Equalf(t, tt.decimal, ft.GetDecimal(), "%v %v %v", i, ft.GetDecimal(), tt.decimal) + require.Equalf(t, tt.collation, ft.GetCollate(), "%v %v %v", i, ft.GetCollate(), tt.collation) + require.Equalf(t, tt.flag, ft.GetFlag(), "%v %v %v", i, ft.GetFlag(), tt.flag) + } +} + +func TestAggFieldType(t *testing.T) { + fts := []*FieldType{ + NewFieldType(mysql.TypeUnspecified), + NewFieldType(mysql.TypeTiny), + NewFieldType(mysql.TypeShort), + NewFieldType(mysql.TypeLong), + NewFieldType(mysql.TypeFloat), + NewFieldType(mysql.TypeDouble), + NewFieldType(mysql.TypeNull), + NewFieldType(mysql.TypeTimestamp), + NewFieldType(mysql.TypeLonglong), + NewFieldType(mysql.TypeInt24), + NewFieldType(mysql.TypeDate), + NewFieldType(mysql.TypeDuration), + NewFieldType(mysql.TypeDatetime), + NewFieldType(mysql.TypeYear), + NewFieldType(mysql.TypeNewDate), + NewFieldType(mysql.TypeVarchar), + NewFieldType(mysql.TypeBit), + NewFieldType(mysql.TypeJSON), + NewFieldType(mysql.TypeNewDecimal), + NewFieldType(mysql.TypeEnum), + NewFieldType(mysql.TypeSet), + NewFieldType(mysql.TypeTinyBlob), + NewFieldType(mysql.TypeMediumBlob), + NewFieldType(mysql.TypeLongBlob), + NewFieldType(mysql.TypeBlob), + NewFieldType(mysql.TypeVarString), + NewFieldType(mysql.TypeString), + NewFieldType(mysql.TypeGeometry), + } + + for i := range fts { + aggTp := AggFieldType(fts[i : i+1]) + require.Equal(t, fts[i].GetType(), aggTp.GetType()) + + aggTp = AggFieldType([]*FieldType{fts[i], fts[i]}) + switch fts[i].GetType() { + case mysql.TypeDate: + require.Equal(t, mysql.TypeDate, aggTp.GetType()) + case mysql.TypeJSON: + require.Equal(t, mysql.TypeJSON, aggTp.GetType()) + case mysql.TypeEnum, mysql.TypeSet, mysql.TypeVarString: + require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) + case mysql.TypeUnspecified: + require.Equal(t, mysql.TypeNewDecimal, aggTp.GetType()) + default: + require.Equal(t, fts[i].GetType(), aggTp.GetType()) + } + + aggTp = AggFieldType([]*FieldType{fts[i], NewFieldType(mysql.TypeLong)}) + switch fts[i].GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, + mysql.TypeYear, mysql.TypeInt24, mysql.TypeNull: + require.Equal(t, mysql.TypeLong, aggTp.GetType()) + case mysql.TypeLonglong: + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + case mysql.TypeFloat, mysql.TypeDouble: + require.Equal(t, mysql.TypeDouble, aggTp.GetType()) + case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDuration, + mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, + mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, + mysql.TypeVarString, mysql.TypeGeometry: + require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) + case mysql.TypeBit: + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + case mysql.TypeString: + require.Equal(t, mysql.TypeString, aggTp.GetType()) + case mysql.TypeUnspecified, mysql.TypeNewDecimal: + require.Equal(t, mysql.TypeNewDecimal, aggTp.GetType()) + case mysql.TypeTinyBlob: + require.Equal(t, mysql.TypeTinyBlob, aggTp.GetType()) + case mysql.TypeBlob: + require.Equal(t, mysql.TypeBlob, aggTp.GetType()) + case mysql.TypeMediumBlob: + require.Equal(t, mysql.TypeMediumBlob, aggTp.GetType()) + case mysql.TypeLongBlob: + require.Equal(t, mysql.TypeLongBlob, aggTp.GetType()) + } + + aggTp = AggFieldType([]*FieldType{fts[i], NewFieldType(mysql.TypeJSON)}) + switch fts[i].GetType() { + case mysql.TypeJSON, mysql.TypeNull: + require.Equal(t, mysql.TypeJSON, aggTp.GetType()) + case mysql.TypeLongBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeBlob: + require.Equal(t, mysql.TypeLongBlob, aggTp.GetType()) + case mysql.TypeString: + require.Equal(t, mysql.TypeString, aggTp.GetType()) + default: + require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) + } + } +} + +func TestAggFieldTypeForTypeFlag(t *testing.T) { + types := []*FieldType{ + NewFieldType(mysql.TypeLonglong), + NewFieldType(mysql.TypeLonglong), + } + + aggTp := AggFieldType(types) + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + + types[0].SetFlag(mysql.NotNullFlag) + aggTp = AggFieldType(types) + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + + types[0].SetFlag(0) + types[1].SetFlag(mysql.NotNullFlag) + aggTp = AggFieldType(types) + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + + types[0].SetFlag(mysql.NotNullFlag) + aggTp = AggFieldType(types) + require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) + require.Equal(t, mysql.NotNullFlag, aggTp.GetFlag()) +} + +func TestAggFieldTypeForIntegralPromotion(t *testing.T) { + fts := []*FieldType{ + NewFieldType(mysql.TypeTiny), + NewFieldType(mysql.TypeShort), + NewFieldType(mysql.TypeInt24), + NewFieldType(mysql.TypeLong), + NewFieldType(mysql.TypeLonglong), + NewFieldType(mysql.TypeNewDecimal), + } + + for i := 1; i < len(fts)-1; i++ { + tps := fts[i-1 : i+1] + + tps[0].SetFlag(0) + tps[1].SetFlag(0) + aggTp := AggFieldType(tps) + require.Equal(t, fts[i].GetType(), aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + + tps[0].SetFlag(mysql.UnsignedFlag) + aggTp = AggFieldType(tps) + require.Equal(t, fts[i].GetType(), aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + + tps[0].SetFlag(mysql.UnsignedFlag) + tps[1].SetFlag(mysql.UnsignedFlag) + aggTp = AggFieldType(tps) + require.Equal(t, fts[i].GetType(), aggTp.GetType()) + require.Equal(t, mysql.UnsignedFlag, aggTp.GetFlag()) + + tps[0].SetFlag(0) + tps[1].SetFlag(mysql.UnsignedFlag) + aggTp = AggFieldType(tps) + require.Equal(t, fts[i+1].GetType(), aggTp.GetType()) + require.Equal(t, uint(0), aggTp.GetFlag()) + } +} + +func TestAggregateEvalType(t *testing.T) { + fts := []*FieldType{ + NewFieldType(mysql.TypeUnspecified), + NewFieldType(mysql.TypeTiny), + NewFieldType(mysql.TypeShort), + NewFieldType(mysql.TypeLong), + NewFieldType(mysql.TypeFloat), + NewFieldType(mysql.TypeDouble), + NewFieldType(mysql.TypeNull), + NewFieldType(mysql.TypeTimestamp), + NewFieldType(mysql.TypeLonglong), + NewFieldType(mysql.TypeInt24), + NewFieldType(mysql.TypeDate), + NewFieldType(mysql.TypeDuration), + NewFieldType(mysql.TypeDatetime), + NewFieldType(mysql.TypeYear), + NewFieldType(mysql.TypeNewDate), + NewFieldType(mysql.TypeVarchar), + NewFieldType(mysql.TypeBit), + NewFieldType(mysql.TypeJSON), + NewFieldType(mysql.TypeNewDecimal), + NewFieldType(mysql.TypeEnum), + NewFieldType(mysql.TypeSet), + NewFieldType(mysql.TypeTinyBlob), + NewFieldType(mysql.TypeMediumBlob), + NewFieldType(mysql.TypeLongBlob), + NewFieldType(mysql.TypeBlob), + NewFieldType(mysql.TypeVarString), + NewFieldType(mysql.TypeString), + NewFieldType(mysql.TypeGeometry), + } + + for i := range fts { + var flag uint + aggregatedEvalType := AggregateEvalType(fts[i:i+1], &flag) + switch fts[i].GetType() { + case mysql.TypeUnspecified, mysql.TypeNull, mysql.TypeTimestamp, mysql.TypeDate, + mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, + mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, + mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob, + mysql.TypeVarString, mysql.TypeString, mysql.TypeGeometry: + require.True(t, aggregatedEvalType.IsStringKind()) + require.Equal(t, uint(0), flag) + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeBit, + mysql.TypeInt24, mysql.TypeYear: + require.Equal(t, ETInt, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeFloat, mysql.TypeDouble: + require.Equal(t, ETReal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeNewDecimal: + require.Equal(t, ETDecimal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + } + + flag = 0 + aggregatedEvalType = AggregateEvalType([]*FieldType{fts[i], fts[i]}, &flag) + switch fts[i].GetType() { + case mysql.TypeUnspecified, mysql.TypeNull, mysql.TypeTimestamp, mysql.TypeDate, + mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, + mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, + mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob, + mysql.TypeVarString, mysql.TypeString, mysql.TypeGeometry: + require.True(t, aggregatedEvalType.IsStringKind()) + require.Equal(t, uint(0), flag) + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeBit, + mysql.TypeInt24, mysql.TypeYear: + require.Equal(t, ETInt, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeFloat, mysql.TypeDouble: + require.Equal(t, ETReal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeNewDecimal: + require.Equal(t, ETDecimal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + } + flag = 0 + aggregatedEvalType = AggregateEvalType([]*FieldType{fts[i], NewFieldType(mysql.TypeLong)}, &flag) + switch fts[i].GetType() { + case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDuration, + mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, mysql.TypeJSON, + mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, mysql.TypeMediumBlob, + mysql.TypeLongBlob, mysql.TypeBlob, mysql.TypeVarString, + mysql.TypeString, mysql.TypeGeometry: + require.True(t, aggregatedEvalType.IsStringKind()) + require.Equal(t, uint(0), flag) + case mysql.TypeUnspecified, mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeNull, mysql.TypeBit, + mysql.TypeLonglong, mysql.TypeYear, mysql.TypeInt24: + require.Equal(t, ETInt, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeFloat, mysql.TypeDouble: + require.Equal(t, ETReal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + case mysql.TypeNewDecimal: + require.Equal(t, ETDecimal, aggregatedEvalType) + require.Equal(t, mysql.BinaryFlag, flag) + } + } +} diff --git a/pkg/types/format_test.go b/pkg/types/format_test.go new file mode 100644 index 0000000000000..b48cec6212acf --- /dev/null +++ b/pkg/types/format_test.go @@ -0,0 +1,199 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package types_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/stretchr/testify/require" +) + +func TestTimeFormatMethod(t *testing.T) { + sc := mock.NewContext().GetSessionVars().StmtCtx + sc.IgnoreZeroInDate = true + tblDate := []struct { + Input string + Format string + Expect string + }{ + { + "2010-01-07 23:12:34.12345", + `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, + `Jan January 01 1 7th 07 7 007 23 11 12 PM 11:12:34 PM 23:12:34 34 123450 01 01 01 01 Thu Thursday 4 2010 2010 2010 10 %`, + }, + { + "2012-12-21 23:12:34.123456", + `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, + `Dec December 12 12 21st 21 21 356 23 11 12 PM 11:12:34 PM 23:12:34 34 123456 51 51 51 51 Fri Friday 5 2012 2012 2012 12 %`, + }, + { + "0000-01-01 00:00:00.123456", + // Functions week() and yearweek() don't support multi mode, + // so the result of "%U %u %V %Y" is different from MySQL. + `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %v %Y %y %%`, + `Jan January 01 1 1st 01 1 001 0 12 00 AM 12:00:00 AM 00:00:00 00 123456 52 0000 00 %`, + }, + { + "2016-09-3 00:59:59.123456", + `abc%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y!123 %%xyz %z`, + `abcSep September 09 9 3rd 03 3 247 0 12 59 AM 12:59:59 AM 00:59:59 59 123456 35 35 35 35 Sat Saturday 6 2016 2016 2016 16!123 %xyz z`, + }, + { + "2012-10-01 00:00:00", + `%b %M %m %c %D %d %e %j %k %H %i %p %r %T %s %f %v %x %Y %y %%`, + `Oct October 10 10 1st 01 1 275 0 00 00 AM 12:00:00 AM 00:00:00 00 000000 40 2012 2012 12 %`, + }, + { + // For invalid date month or year = 0, MySQL behavior is confusing, %U (which format Week()) is 52, but Week() is 0. + // It's because in MySQL, Week() checks invalid date before processing, but DateFormat() don't. + // So there are some difference to MySQL here (%U %u %V %v), TiDB user should not rely on those corner case behavior. + // %W %w %a is not compatible in this case because Week() use GoTime() currently. + "0000-01-00 00:00:00.123456", + `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, + `Jan January 01 1 0th 00 0 000 0 12 00 AM 12:00:00 AM 00:00:00 00 123456 00 00 00 52 Fri Friday 5 4294967295 4294967295 0000 00 %`, + }, + } + for i, tt := range tblDate { + tm, err := types.ParseTime(sc, tt.Input, mysql.TypeDatetime, 6, nil) + require.NoErrorf(t, err, "Parse time fail: %s", tt.Input) + + str, err := tm.DateFormat(tt.Format) + require.NoErrorf(t, err, "time format fail: %d", i) + require.Equalf(t, tt.Expect, str, "no.%d \nobtain:%v \nexpect:%v\n", i, str, tt.Expect) + } +} + +func TestStrToDate(t *testing.T) { + sc := mock.NewContext().GetSessionVars().StmtCtx + sc.IgnoreZeroInDate = true + tests := []struct { + input string + format string + expect types.CoreTime + }{ + {`01,05,2013`, `%d,%m,%Y`, types.FromDate(2013, 5, 1, 0, 0, 0, 0)}, + {`5 12 2021`, `%m%d%Y`, types.FromDate(2021, 5, 12, 0, 0, 0, 0)}, + {`May 01, 2013`, `%M %d,%Y`, types.FromDate(2013, 5, 1, 0, 0, 0, 0)}, + {`a09:30:17`, `a%h:%i:%s`, types.FromDate(0, 0, 0, 9, 30, 17, 0)}, + {`09:30:17a`, `%h:%i:%s`, types.FromDate(0, 0, 0, 9, 30, 17, 0)}, + {`12:43:24`, `%h:%i:%s`, types.FromDate(0, 0, 0, 0, 43, 24, 0)}, + {`abc`, `abc`, types.ZeroCoreTime}, + {`09`, `%m`, types.FromDate(0, 9, 0, 0, 0, 0, 0)}, + {`09`, `%s`, types.FromDate(0, 0, 0, 0, 0, 9, 0)}, + {`12:43:24 AM`, `%r`, types.FromDate(0, 0, 0, 0, 43, 24, 0)}, + {`12:43:24 PM`, `%r`, types.FromDate(0, 0, 0, 12, 43, 24, 0)}, + {`11:43:24 PM`, `%r`, types.FromDate(0, 0, 0, 23, 43, 24, 0)}, + {`00:12:13`, `%T`, types.FromDate(0, 0, 0, 0, 12, 13, 0)}, + {`23:59:59`, `%T`, types.FromDate(0, 0, 0, 23, 59, 59, 0)}, + {`00/00/0000`, `%m/%d/%Y`, types.ZeroCoreTime}, + {`04/30/2004`, `%m/%d/%Y`, types.FromDate(2004, 4, 30, 0, 0, 0, 0)}, + {`15:35:00`, `%H:%i:%s`, types.FromDate(0, 0, 0, 15, 35, 0, 0)}, + {`Jul 17 33`, `%b %k %S`, types.FromDate(0, 7, 0, 17, 0, 33, 0)}, + {`2016-January:7 432101`, `%Y-%M:%l %f`, types.FromDate(2016, 1, 0, 7, 0, 0, 432101)}, + {`10:13 PM`, `%l:%i %p`, types.FromDate(0, 0, 0, 22, 13, 0, 0)}, + {`12:00:00 AM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 0, 0, 0, 0)}, + {`12:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, + {`12:00:00 PM`, `%I:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, + {`1:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 13, 0, 0, 0)}, + {`18/10/22`, `%y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, + {`8/10/22`, `%y/%m/%d`, types.FromDate(2008, 10, 22, 0, 0, 0, 0)}, + {`69/10/22`, `%y/%m/%d`, types.FromDate(2069, 10, 22, 0, 0, 0, 0)}, + {`70/10/22`, `%y/%m/%d`, types.FromDate(1970, 10, 22, 0, 0, 0, 0)}, + {`18/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, + {`2018/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, + {`8/10/22`, `%Y/%m/%d`, types.FromDate(2008, 10, 22, 0, 0, 0, 0)}, + {`69/10/22`, `%Y/%m/%d`, types.FromDate(2069, 10, 22, 0, 0, 0, 0)}, + {`70/10/22`, `%Y/%m/%d`, types.FromDate(1970, 10, 22, 0, 0, 0, 0)}, + {`18/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, + {`100/10/22`, `%Y/%m/%d`, types.FromDate(100, 10, 22, 0, 0, 0, 0)}, + {`09/10/1021`, `%d/%m/%y`, types.FromDate(2010, 10, 9, 0, 0, 0, 0)}, // '%y' only accept up to 2 digits for year + {`09/10/1021`, `%d/%m/%Y`, types.FromDate(1021, 10, 9, 0, 0, 0, 0)}, // '%Y' accept up to 4 digits for year + {`09/10/10`, `%d/%m/%Y`, types.FromDate(2010, 10, 9, 0, 0, 0, 0)}, // '%Y' will fix the year for only 2 digits + //'%b'/'%M' should be case insensitive + {"31/may/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 5, 31, 12, 34, 56, 123400)}, + {"30/april/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 30, 12, 34, 56, 0)}, + {"31/mAy/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 5, 31, 12, 34, 56, 123400)}, + {"30/apRil/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 30, 12, 34, 56, 0)}, + // '%r' + {" 04 :13:56 AM13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 4, 13, 56, 0)}, // + {"12: 13:56 AM 13/05/2019", "%r%d/%c/%Y", types.FromDate(2019, 5, 13, 0, 13, 56, 0)}, // + {"12:13 :56 pm 13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 13, 56, 0)}, // + {"12:3: 56pm 13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 3, 56, 0)}, // + {"11:13:56", "%r", types.FromDate(0, 0, 0, 11, 13, 56, 0)}, // EOF before parsing "AM"/"PM" + {"11:13", "%r", types.FromDate(0, 0, 0, 11, 13, 0, 0)}, // EOF after hh:mm + {"11:", "%r", types.FromDate(0, 0, 0, 11, 0, 0, 0)}, // EOF after hh: + {"11", "%r", types.FromDate(0, 0, 0, 11, 0, 0, 0)}, // EOF after hh: + {"12", "%r", types.FromDate(0, 0, 0, 0, 0, 0, 0)}, // EOF after hh:, and hh=12 -> 0 + // '%T' + {" 4 :13:56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 4, 13, 56, 0)}, + {"23: 13:56 13/05/2019", "%T%d/%c/%Y", types.FromDate(2019, 5, 13, 23, 13, 56, 0)}, + {"12:13 :56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 13, 56, 0)}, + {"19:3: 56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 19, 3, 56, 0)}, + {"21:13", "%T", types.FromDate(0, 0, 0, 21, 13, 0, 0)}, // EOF after hh:mm + {"21:", "%T", types.FromDate(0, 0, 0, 21, 0, 0, 0)}, // EOF after hh: + // More patterns than input string + {" 2/Jun", "%d/%b/%Y", types.FromDate(0, 6, 2, 0, 0, 0, 0)}, + {" liter", "lit era l", types.ZeroCoreTime}, + // Feb 29 in leap-year + {"29/Feb/2020 12:34:56.", "%d/%b/%Y %H:%i:%s.%f", types.FromDate(2020, 2, 29, 12, 34, 56, 0)}, + // When `AllowInvalidDate` is true, check only that the month is in the range from 1 to 12 and the day is in the range from 1 to 31 + {"31/April/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 31, 12, 34, 56, 0)}, // April 31th + {"29/Feb/2021 12:34:56.", "%d/%b/%Y %H:%i:%s.%f", types.FromDate(2021, 2, 29, 12, 34, 56, 0)}, // Feb 29 in non-leap-year + {"30/Feb/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 2, 30, 12, 34, 56, 123400)}, // Feb 30th + } + for i, tt := range tests { + sc.AllowInvalidDate = true + var time types.Time + require.Truef(t, time.StrToDate(sc, tt.input, tt.format), "no.%d failed input=%s format=%s", i, tt.input, tt.format) + require.Equalf(t, tt.expect, time.CoreTime(), "no.%d failed input=%s format=%s", i, tt.input, tt.format) + } + + errTests := []struct { + input string + format string + }{ + // invalid days when `AllowInvalidDate` is false + {`04/31/2004`, `%m/%d/%Y`}, // not exists in the real world + {"29/Feb/2021 12:34:56.", "%d/%b/%Y %H:%i:%s.%f"}, // Feb 29 in non-leap-year + + {`512 2021`, `%m%d %Y`}, // MySQL will try to parse '51' for '%m', fail + + {`a09:30:17`, `%h:%i:%s`}, // format mismatch + {`12:43:24 a`, `%r`}, // followed by incomplete 'AM'/'PM' + {`23:60:12`, `%T`}, // invalid minute + {`18`, `%l`}, + {`00:21:22 AM`, `%h:%i:%s %p`}, + {`100/10/22`, `%y/%m/%d`}, + {"2010-11-12 11 am", `%Y-%m-%d %H %p`}, + {"2010-11-12 13 am", `%Y-%m-%d %h %p`}, + {"2010-11-12 0 am", `%Y-%m-%d %h %p`}, + // MySQL accept `SEPTEMB` as `SEPTEMBER`, but we don't want this "feature" in TiDB + // unless we have to. + {"15 SEPTEMB 2001", "%d %M %Y"}, + // '%r' + {"13:13:56 AM13/5/2019", "%r"}, // hh = 13 with am is invalid + {"00:13:56 AM13/05/2019", "%r"}, // hh = 0 with am is invalid + {"00:13:56 pM13/05/2019", "%r"}, // hh = 0 with pm is invalid + {"11:13:56a", "%r"}, // EOF while parsing "AM"/"PM" + } + for i, tt := range errTests { + sc.AllowInvalidDate = false + var time types.Time + require.Falsef(t, time.StrToDate(sc, tt.input, tt.format), "no.%d failed input=%s format=%s", i, tt.input, tt.format) + } +} diff --git a/types/fsp.go b/pkg/types/fsp.go similarity index 100% rename from types/fsp.go rename to pkg/types/fsp.go diff --git a/types/fsp_test.go b/pkg/types/fsp_test.go similarity index 100% rename from types/fsp_test.go rename to pkg/types/fsp_test.go diff --git a/types/helper.go b/pkg/types/helper.go similarity index 100% rename from types/helper.go rename to pkg/types/helper.go diff --git a/types/helper_test.go b/pkg/types/helper_test.go similarity index 100% rename from types/helper_test.go rename to pkg/types/helper_test.go diff --git a/types/json_binary.go b/pkg/types/json_binary.go similarity index 99% rename from types/json_binary.go rename to pkg/types/json_binary.go index 43fd0a308b714..b9c81043b42b5 100644 --- a/types/json_binary.go +++ b/pkg/types/json_binary.go @@ -31,10 +31,10 @@ import ( "unicode/utf8" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/types/json_binary_functions.go b/pkg/types/json_binary_functions.go similarity index 99% rename from types/json_binary_functions.go rename to pkg/types/json_binary_functions.go index be579bb759588..0dbf067c40a4a 100644 --- a/types/json_binary_functions.go +++ b/pkg/types/json_binary_functions.go @@ -25,9 +25,9 @@ import ( "unicode/utf8" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // Type returns type of BinaryJSON as string. diff --git a/types/json_binary_functions_test.go b/pkg/types/json_binary_functions_test.go similarity index 100% rename from types/json_binary_functions_test.go rename to pkg/types/json_binary_functions_test.go diff --git a/types/json_binary_test.go b/pkg/types/json_binary_test.go similarity index 100% rename from types/json_binary_test.go rename to pkg/types/json_binary_test.go diff --git a/types/json_constants.go b/pkg/types/json_constants.go similarity index 98% rename from types/json_constants.go rename to pkg/types/json_constants.go index d7486d4025201..9c463815526f6 100644 --- a/types/json_constants.go +++ b/pkg/types/json_constants.go @@ -18,8 +18,8 @@ import ( "encoding/binary" "unicode/utf8" - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" + mysql "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" ) // JSONTypeCode indicates JSON type. diff --git a/types/json_path_expr.go b/pkg/types/json_path_expr.go similarity index 99% rename from types/json_path_expr.go rename to pkg/types/json_path_expr.go index e784155518da9..dbe3750fdc503 100644 --- a/types/json_path_expr.go +++ b/pkg/types/json_path_expr.go @@ -22,8 +22,8 @@ import ( "sync" "unicode" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" ) /* diff --git a/types/json_path_expr_test.go b/pkg/types/json_path_expr_test.go similarity index 100% rename from types/json_path_expr_test.go rename to pkg/types/json_path_expr_test.go diff --git a/pkg/types/main_test.go b/pkg/types/main_test.go new file mode 100644 index 0000000000000..e18894ac68b20 --- /dev/null +++ b/pkg/types/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package types + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/types/mydecimal.go b/pkg/types/mydecimal.go similarity index 99% rename from types/mydecimal.go rename to pkg/types/mydecimal.go index 37e724ab61370..baca38fef4443 100644 --- a/types/mydecimal.go +++ b/pkg/types/mydecimal.go @@ -22,9 +22,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/types/mydecimal_benchmark_test.go b/pkg/types/mydecimal_benchmark_test.go similarity index 100% rename from types/mydecimal_benchmark_test.go rename to pkg/types/mydecimal_benchmark_test.go diff --git a/types/mydecimal_test.go b/pkg/types/mydecimal_test.go similarity index 100% rename from types/mydecimal_test.go rename to pkg/types/mydecimal_test.go diff --git a/types/overflow.go b/pkg/types/overflow.go similarity index 100% rename from types/overflow.go rename to pkg/types/overflow.go diff --git a/types/overflow_test.go b/pkg/types/overflow_test.go similarity index 100% rename from types/overflow_test.go rename to pkg/types/overflow_test.go diff --git a/pkg/types/parser_driver/BUILD.bazel b/pkg/types/parser_driver/BUILD.bazel new file mode 100644 index 0000000000000..6fcf692efad44 --- /dev/null +++ b/pkg/types/parser_driver/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "parser_driver", + srcs = ["value_expr.go"], + importpath = "github.com/pingcap/tidb/pkg/types/parser_driver", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/hack", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "parser_driver_test", + timeout = "short", + srcs = [ + "main_test.go", + "value_expr_test.go", + ], + embed = [":parser_driver"], + flaky = True, + deps = [ + "//pkg/parser/format", + "//pkg/testkit/testsetup", + "//pkg/types", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/types/parser_driver/main_test.go b/pkg/types/parser_driver/main_test.go new file mode 100644 index 0000000000000..50aa866ac77f3 --- /dev/null +++ b/pkg/types/parser_driver/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package driver + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/types/parser_driver/value_expr.go b/pkg/types/parser_driver/value_expr.go similarity index 97% rename from types/parser_driver/value_expr.go rename to pkg/types/parser_driver/value_expr.go index b100f1302e3a6..1ff4cc6fca8f3 100644 --- a/types/parser_driver/value_expr.go +++ b/pkg/types/parser_driver/value_expr.go @@ -21,11 +21,11 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" ) // The purpose of driver package is to decompose the dependency of the parser and diff --git a/types/parser_driver/value_expr_test.go b/pkg/types/parser_driver/value_expr_test.go similarity index 97% rename from types/parser_driver/value_expr_test.go rename to pkg/types/parser_driver/value_expr_test.go index 501319f9286e3..456b9f2ef16f4 100644 --- a/types/parser_driver/value_expr_test.go +++ b/pkg/types/parser_driver/value_expr_test.go @@ -18,8 +18,8 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/types/set.go b/pkg/types/set.go new file mode 100644 index 0000000000000..ece1006353e1f --- /dev/null +++ b/pkg/types/set.go @@ -0,0 +1,132 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/stringutil" +) + +var zeroSet = Set{Name: "", Value: 0} + +// Set is for MySQL Set type. +type Set struct { + Name string + Value uint64 +} + +// String implements fmt.Stringer interface. +func (e Set) String() string { + return e.Name +} + +// ToNumber changes Set to float64 for numeric operation. +func (e Set) ToNumber() float64 { + return float64(e.Value) +} + +// Copy deep copy a Set. +func (e Set) Copy() Set { + return Set{ + Name: stringutil.Copy(e.Name), + Value: e.Value, + } +} + +// ParseSet creates a Set with name or value. +func ParseSet(elems []string, name string, collation string) (Set, error) { + if setName, err := ParseSetName(elems, name, collation); err == nil { + return setName, nil + } + // name doesn't exist, maybe an integer? + if num, err := strconv.ParseUint(name, 0, 64); err == nil { + return ParseSetValue(elems, num) + } + + return Set{}, errors.Errorf("item %s is not in Set %v", name, elems) +} + +// ParseSetName creates a Set with name. +func ParseSetName(elems []string, name string, collation string) (Set, error) { + if len(name) == 0 { + return zeroSet, nil + } + + ctor := collate.GetCollator(collation) + + seps := strings.Split(name, ",") + marked := make(map[string]struct{}, len(seps)) + for _, s := range seps { + marked[string(ctor.Key(s))] = struct{}{} + } + items := make([]string, 0, len(seps)) + + value := uint64(0) + for i, n := range elems { + key := string(ctor.Key(n)) + if _, ok := marked[key]; ok { + value |= 1 << uint64(i) + delete(marked, key) + items = append(items, n) + } + } + + if len(marked) == 0 { + return Set{Name: strings.Join(items, ","), Value: value}, nil + } + + return Set{}, errors.Errorf("item %s is not in Set %v", name, elems) +} + +var ( + setIndexValue []uint64 + setIndexInvertValue []uint64 +) + +func init() { + setIndexValue = make([]uint64, 64) + setIndexInvertValue = make([]uint64, 64) + + for i := 0; i < 64; i++ { + setIndexValue[i] = 1 << uint64(i) + setIndexInvertValue[i] = ^setIndexValue[i] + } +} + +// ParseSetValue creates a Set with special number. +func ParseSetValue(elems []string, number uint64) (Set, error) { + if number == 0 { + return zeroSet, nil + } + + value := number + var items []string + for i := 0; i < len(elems); i++ { + if number&setIndexValue[i] > 0 { + items = append(items, elems[i]) + number &= setIndexInvertValue[i] + } + } + + if number != 0 { + return Set{}, errors.Errorf("invalid number %d for Set %v", number, elems) + } + + return Set{Name: strings.Join(items, ","), Value: value}, nil +} diff --git a/pkg/types/set_test.go b/pkg/types/set_test.go new file mode 100644 index 0000000000000..7c5e7aa26004e --- /dev/null +++ b/pkg/types/set_test.go @@ -0,0 +1,104 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package types + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/stretchr/testify/require" +) + +func TestSet(t *testing.T) { + elems := []string{"a", "b", "c", "d"} + + t.Run("ParseSet", func(t *testing.T) { + tests := []struct { + Name string + ExpectedValue uint64 + ExpectedName string + }{ + {"a", 1, "a"}, + {"a,b,a", 3, "a,b"}, + {"b,a", 3, "a,b"}, + {"a,b,c,d", 15, "a,b,c,d"}, + {"d", 8, "d"}, + {"", 0, ""}, + {"0", 0, ""}, + } + + for _, collation := range []string{mysql.DefaultCollationName, "utf8_unicode_ci"} { + for _, test := range tests { + e, err := ParseSet(elems, test.Name, collation) + require.NoError(t, err) + require.Equal(t, float64(test.ExpectedValue), e.ToNumber()) + require.Equal(t, test.ExpectedName, e.String()) + } + } + }) + + t.Run("ParseSet_ci", func(t *testing.T) { + tests := []struct { + Name string + ExpectedValue uint64 + ExpectedName string + }{ + {"A ", 1, "a"}, + {"a,B,a", 3, "a,b"}, + } + + for _, test := range tests { + e, err := ParseSet(elems, test.Name, "utf8_general_ci") + require.NoError(t, err) + require.Equal(t, float64(test.ExpectedValue), e.ToNumber()) + require.Equal(t, test.ExpectedName, e.String()) + } + }) + + t.Run("ParseSetValue", func(t *testing.T) { + tests := []struct { + Number uint64 + ExpectedName string + }{ + {0, ""}, + {1, "a"}, + {3, "a,b"}, + {9, "a,d"}, + } + + for _, test := range tests { + e, err := ParseSetValue(elems, test.Number) + require.NoError(t, err) + require.Equal(t, float64(test.Number), e.ToNumber()) + require.Equal(t, test.ExpectedName, e.String()) + } + }) + + t.Run("ParseSet_err", func(t *testing.T) { + tests := []string{"a.e", "e.f"} + for _, test := range tests { + _, err := ParseSet(elems, test, mysql.DefaultCollationName) + require.Error(t, err) + } + }) + + t.Run("ParseSetValue_err", func(t *testing.T) { + tests := []uint64{100, 16, 64} + for _, test := range tests { + _, err := ParseSetValue(elems, test) + require.Error(t, err) + } + }) +} diff --git a/types/time.go b/pkg/types/time.go similarity index 99% rename from types/time.go rename to pkg/types/time.go index 957090306f227..27c76ef1d7492 100644 --- a/types/time.go +++ b/pkg/types/time.go @@ -27,14 +27,14 @@ import ( "unicode" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/parser" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/mathutil" + "github.com/pingcap/tidb/pkg/util/parser" "go.uber.org/zap" ) diff --git a/types/time_test.go b/pkg/types/time_test.go similarity index 99% rename from types/time_test.go rename to pkg/types/time_test.go index 11390f3d2a29b..67008aee63707 100644 --- a/types/time_test.go +++ b/pkg/types/time_test.go @@ -23,11 +23,11 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mock" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/BUILD.bazel b/pkg/util/BUILD.bazel new file mode 100644 index 0000000000000..2cc0b835f7e43 --- /dev/null +++ b/pkg/util/BUILD.bazel @@ -0,0 +1,90 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "util", + srcs = [ + "cpu_posix.go", + "cpu_windows.go", + "errors.go", + "etcd.go", + "gogc.go", + "id_generator.go", + "misc.go", + "prefix_helper.go", + "printer.go", + "processinfo.go", + "rlimit_other.go", + "rlimit_windows.go", + "security.go", + "urls.go", + "util.go", + "wait_group_wrapper.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/metrics", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/session/txninfo", + "//pkg/sessionctx/stmtctx", + "//pkg/util/collate", + "//pkg/util/disk", + "//pkg/util/execdetails", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/tls", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_log//:log", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//oracle", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//concurrency", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "util_test", + timeout = "short", + srcs = [ + "errors_test.go", + "main_test.go", + "misc_test.go", + "prefix_helper_test.go", + "security_test.go", + "urls_test.go", + "util_test.go", + "wait_group_wrapper_test.go", + ], + data = glob(["tls_test/**"]), + embed = [":util"], + flaky = True, + shard_count = 50, + deps = [ + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/store/mockstore", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/fastrand", + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/admin/BUILD.bazel b/pkg/util/admin/BUILD.bazel new file mode 100644 index 0000000000000..0343daad14376 --- /dev/null +++ b/pkg/util/admin/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "admin", + srcs = ["admin.go"], + importpath = "github.com/pingcap/tidb/pkg/util/admin", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx", + "//pkg/table", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "//pkg/util/logutil/consistency", + "//pkg/util/rowDecoder", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "admin_test", + timeout = "short", + srcs = [ + "admin_integration_test.go", + "main_test.go", + ], + embed = [":admin"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/admin/admin.go b/pkg/util/admin/admin.go new file mode 100644 index 0000000000000..1f99ddbd48d04 --- /dev/null +++ b/pkg/util/admin/admin.go @@ -0,0 +1,263 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package admin + +import ( + "context" + "math" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil/consistency" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "go.uber.org/zap" +) + +// RecordData is the record data composed of a handle and values. +type RecordData struct { + Handle kv.Handle + Values []types.Datum +} + +func getCount(exec sqlexec.RestrictedSQLExecutor, snapshot uint64, sql string, args ...interface{}) (int64, error) { + ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnAdmin) + rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionWithSnapshot(snapshot)}, sql, args...) + if err != nil { + return 0, errors.Trace(err) + } + if len(rows) != 1 { + return 0, errors.Errorf("can not get count, rows count = %d", len(rows)) + } + return rows[0].GetInt64(0), nil +} + +// Count greater Types +const ( + // TblCntGreater means that the number of table rows is more than the number of index rows. + TblCntGreater byte = 1 + // IdxCntGreater means that the number of index rows is more than the number of table rows. + IdxCntGreater byte = 2 +) + +// CheckIndicesCount compares indices count with table count. +// It returns the count greater type, the index offset and an error. +// It returns nil if the count from the index is equal to the count from the table columns, +// otherwise it returns an error and the corresponding index's offset. +func CheckIndicesCount(ctx sessionctx.Context, dbName, tableName string, indices []string) (byte, int, error) { + // Here we need check all indexes, includes invisible index + ctx.GetSessionVars().OptimizerUseInvisibleIndexes = true + defer func() { + ctx.GetSessionVars().OptimizerUseInvisibleIndexes = false + }() + + var snapshot uint64 + txn, err := ctx.Txn(false) + if err != nil { + return 0, 0, err + } + if txn.Valid() { + snapshot = txn.StartTS() + } + if ctx.GetSessionVars().SnapshotTS != 0 { + snapshot = ctx.GetSessionVars().SnapshotTS + } + + // Add `` for some names like `table name`. + exec := ctx.(sqlexec.RestrictedSQLExecutor) + tblCnt, err := getCount(exec, snapshot, "SELECT COUNT(*) FROM %n.%n USE INDEX()", dbName, tableName) + if err != nil { + return 0, 0, errors.Trace(err) + } + for i, idx := range indices { + idxCnt, err := getCount(exec, snapshot, "SELECT COUNT(*) FROM %n.%n USE INDEX(%n)", dbName, tableName, idx) + if err != nil { + return 0, i, errors.Trace(err) + } + logutil.Logger(context.Background()).Info("check indices count", + zap.String("table", tableName), zap.Int64("tblCnt", tblCnt), zap.Reflect("index", idx), zap.Int64("idxCnt", idxCnt)) + if tblCnt == idxCnt { + continue + } + + var ret byte + if tblCnt > idxCnt { + ret = TblCntGreater + } else if idxCnt > tblCnt { + ret = IdxCntGreater + } + return ret, i, ErrAdminCheckTable.GenWithStack("table count %d != index(%s) count %d", tblCnt, idx, idxCnt) + } + return 0, 0, nil +} + +// CheckRecordAndIndex is exported for testing. +func CheckRecordAndIndex(ctx context.Context, sessCtx sessionctx.Context, txn kv.Transaction, t table.Table, idx table.Index) error { + sc := sessCtx.GetSessionVars().StmtCtx + cols := make([]*table.Column, len(idx.Meta().Columns)) + for i, col := range idx.Meta().Columns { + cols[i] = t.Cols()[col.Offset] + } + + ir := func() *consistency.Reporter { + return &consistency.Reporter{ + HandleEncode: func(handle kv.Handle) kv.Key { + return tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) + }, + IndexEncode: func(idxRow *consistency.RecordData) kv.Key { + var matchingIdx table.Index + for _, v := range t.Indices() { + if strings.EqualFold(v.Meta().Name.String(), idx.Meta().Name.O) { + matchingIdx = v + break + } + } + if matchingIdx == nil { + return nil + } + k, _, err := matchingIdx.GenIndexKey(sessCtx.GetSessionVars().StmtCtx, idxRow.Values, idxRow.Handle, nil) + if err != nil { + return nil + } + return k + }, + Tbl: t.Meta(), + Idx: idx.Meta(), + Sctx: sessCtx, + } + } + + startKey := tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) + filterFunc := func(h1 kv.Handle, vals1 []types.Datum, cols []*table.Column) (bool, error) { + for i, val := range vals1 { + col := cols[i] + if val.IsNull() { + if mysql.HasNotNullFlag(col.GetFlag()) && col.ToInfo().GetOriginDefaultValue() == nil { + return false, errors.Errorf("Column %v define as not null, but can't find the value where handle is %v", col.Name, h1) + } + // NULL value is regarded as its default value. + colDefVal, err := table.GetColOriginDefaultValue(sessCtx, col.ToInfo()) + if err != nil { + return false, errors.Trace(err) + } + vals1[i] = colDefVal + } + } + isExist, h2, err := idx.Exist(sc, txn, vals1, h1) + if kv.ErrKeyExists.Equal(err) { + record1 := &consistency.RecordData{Handle: h1, Values: vals1} + record2 := &consistency.RecordData{Handle: h2, Values: vals1} + return false, ir().ReportAdminCheckInconsistent(ctx, h1, record2, record1) + } + if err != nil { + return false, errors.Trace(err) + } + if !isExist { + record := &consistency.RecordData{Handle: h1, Values: vals1} + return false, ir().ReportAdminCheckInconsistent(ctx, h1, nil, record) + } + + return true, nil + } + err := iterRecords(sessCtx, txn, t, startKey, cols, filterFunc) + if err != nil { + return errors.Trace(err) + } + + return nil +} + +func makeRowDecoder(t table.Table, sctx sessionctx.Context) (*decoder.RowDecoder, error) { + dbName := model.NewCIStr(sctx.GetSessionVars().CurrentDB) + exprCols, _, err := expression.ColumnInfos2ColumnsAndNames(sctx, dbName, t.Meta().Name, t.Meta().Cols(), t.Meta()) + if err != nil { + return nil, err + } + mockSchema := expression.NewSchema(exprCols...) + decodeColsMap := decoder.BuildFullDecodeColMap(t.Cols(), mockSchema) + + return decoder.NewRowDecoder(t, t.Cols(), decodeColsMap), nil +} + +func iterRecords(sessCtx sessionctx.Context, retriever kv.Retriever, t table.Table, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc) error { + prefix := t.RecordPrefix() + keyUpperBound := prefix.PrefixNext() + + it, err := retriever.Iter(startKey, keyUpperBound) + if err != nil { + return errors.Trace(err) + } + defer it.Close() + + if !it.Valid() { + return nil + } + + logutil.BgLogger().Debug("record", + zap.Stringer("startKey", startKey), + zap.Stringer("key", it.Key()), + zap.Binary("value", it.Value())) + rowDecoder, err := makeRowDecoder(t, sessCtx) + if err != nil { + return err + } + for it.Valid() && it.Key().HasPrefix(prefix) { + // first kv pair is row lock information. + // TODO: check valid lock + // get row handle + handle, err := tablecodec.DecodeRowKey(it.Key()) + if err != nil { + return errors.Trace(err) + } + + rowMap, err := rowDecoder.DecodeAndEvalRowWithMap(sessCtx, handle, it.Value(), sessCtx.GetSessionVars().Location(), nil) + if err != nil { + return errors.Trace(err) + } + data := make([]types.Datum, 0, len(cols)) + for _, col := range cols { + data = append(data, rowMap[col.ID]) + } + more, err := fn(handle, data, cols) + if !more || err != nil { + return errors.Trace(err) + } + + rk := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) + err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) + if err != nil { + return errors.Trace(err) + } + } + + return nil +} + +var ( + // ErrAdminCheckTable returns when the table records is inconsistent with the index values. + ErrAdminCheckTable = dbterror.ClassAdmin.NewStd(errno.ErrAdminCheckTable) +) diff --git a/util/admin/admin_integration_test.go b/pkg/util/admin/admin_integration_test.go similarity index 95% rename from util/admin/admin_integration_test.go rename to pkg/util/admin/admin_integration_test.go index 579fbe9fa9d1a..e259aa580cabc 100644 --- a/util/admin/admin_integration_test.go +++ b/pkg/util/admin/admin_integration_test.go @@ -17,8 +17,8 @@ package admin_test import ( "testing" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/admin/main_test.go b/pkg/util/admin/main_test.go new file mode 100644 index 0000000000000..98458e4d747fd --- /dev/null +++ b/pkg/util/admin/main_test.go @@ -0,0 +1,40 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package admin + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + config.UpdateGlobal(func(conf *config.Config) { + conf.TiKVClient.AsyncCommit.SafeWindow = 0 + conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 + }) + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/arena/BUILD.bazel b/pkg/util/arena/BUILD.bazel new file mode 100644 index 0000000000000..7ef3e758c10cb --- /dev/null +++ b/pkg/util/arena/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "arena", + srcs = ["arena.go"], + importpath = "github.com/pingcap/tidb/pkg/util/arena", + visibility = ["//visibility:public"], +) + +go_test( + name = "arena_test", + timeout = "short", + srcs = [ + "arena_test.go", + "main_test.go", + ], + embed = [":arena"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/arena/arena.go b/pkg/util/arena/arena.go similarity index 100% rename from util/arena/arena.go rename to pkg/util/arena/arena.go diff --git a/util/arena/arena_test.go b/pkg/util/arena/arena_test.go similarity index 100% rename from util/arena/arena_test.go rename to pkg/util/arena/arena_test.go diff --git a/pkg/util/arena/main_test.go b/pkg/util/arena/main_test.go new file mode 100644 index 0000000000000..1a6ed68534ada --- /dev/null +++ b/pkg/util/arena/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package arena + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/backoff/BUILD.bazel b/pkg/util/backoff/BUILD.bazel new file mode 100644 index 0000000000000..72f5594188034 --- /dev/null +++ b/pkg/util/backoff/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "backoff", + srcs = ["backoff.go"], + importpath = "github.com/pingcap/tidb/pkg/util/backoff", + visibility = ["//visibility:public"], +) + +go_test( + name = "backoff_test", + timeout = "short", + srcs = ["backoff_test.go"], + embed = [":backoff"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/backoff/backoff.go b/pkg/util/backoff/backoff.go similarity index 100% rename from util/backoff/backoff.go rename to pkg/util/backoff/backoff.go diff --git a/util/backoff/backoff_test.go b/pkg/util/backoff/backoff_test.go similarity index 100% rename from util/backoff/backoff_test.go rename to pkg/util/backoff/backoff_test.go diff --git a/pkg/util/benchdaily/BUILD.bazel b/pkg/util/benchdaily/BUILD.bazel new file mode 100644 index 0000000000000..242d81f40d931 --- /dev/null +++ b/pkg/util/benchdaily/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "benchdaily", + srcs = ["bench_daily.go"], + importpath = "github.com/pingcap/tidb/pkg/util/benchdaily", + visibility = ["//visibility:public"], +) + +go_test( + name = "benchdaily_test", + timeout = "short", + srcs = [ + "bench_daily_test.go", + "main_test.go", + ], + embed = [":benchdaily"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/benchdaily/bench_daily.go b/pkg/util/benchdaily/bench_daily.go similarity index 100% rename from util/benchdaily/bench_daily.go rename to pkg/util/benchdaily/bench_daily.go diff --git a/util/benchdaily/bench_daily_test.go b/pkg/util/benchdaily/bench_daily_test.go similarity index 100% rename from util/benchdaily/bench_daily_test.go rename to pkg/util/benchdaily/bench_daily_test.go diff --git a/pkg/util/benchdaily/main_test.go b/pkg/util/benchdaily/main_test.go new file mode 100644 index 0000000000000..e6915fd19d87e --- /dev/null +++ b/pkg/util/benchdaily/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package benchdaily + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/bitmap/BUILD.bazel b/pkg/util/bitmap/BUILD.bazel new file mode 100644 index 0000000000000..39f8942e7996e --- /dev/null +++ b/pkg/util/bitmap/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "bitmap", + srcs = ["concurrent.go"], + importpath = "github.com/pingcap/tidb/pkg/util/bitmap", + visibility = ["//visibility:public"], +) + +go_test( + name = "bitmap_test", + timeout = "short", + srcs = [ + "concurrent_test.go", + "main_test.go", + ], + embed = [":bitmap"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/bitmap/concurrent.go b/pkg/util/bitmap/concurrent.go similarity index 100% rename from util/bitmap/concurrent.go rename to pkg/util/bitmap/concurrent.go diff --git a/util/bitmap/concurrent_test.go b/pkg/util/bitmap/concurrent_test.go similarity index 100% rename from util/bitmap/concurrent_test.go rename to pkg/util/bitmap/concurrent_test.go diff --git a/pkg/util/bitmap/main_test.go b/pkg/util/bitmap/main_test.go new file mode 100644 index 0000000000000..2b41b309c4d69 --- /dev/null +++ b/pkg/util/bitmap/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package bitmap + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/breakpoint/BUILD.bazel b/pkg/util/breakpoint/BUILD.bazel new file mode 100644 index 0000000000000..351dbcf3d3186 --- /dev/null +++ b/pkg/util/breakpoint/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "breakpoint", + srcs = ["breakpoint.go"], + importpath = "github.com/pingcap/tidb/pkg/util/breakpoint", + visibility = ["//visibility:public"], + deps = [ + "//pkg/sessionctx", + "//pkg/util/stringutil", + "@com_github_pingcap_failpoint//:failpoint", + ], +) diff --git a/util/breakpoint/breakpoint.go b/pkg/util/breakpoint/breakpoint.go similarity index 92% rename from util/breakpoint/breakpoint.go rename to pkg/util/breakpoint/breakpoint.go index b94a90be02954..60c0e1f828799 100644 --- a/util/breakpoint/breakpoint.go +++ b/pkg/util/breakpoint/breakpoint.go @@ -16,8 +16,8 @@ package breakpoint import ( "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/stringutil" ) // NotifyBreakPointFuncKey is the key where break point notify function located diff --git a/pkg/util/cgroup/BUILD.bazel b/pkg/util/cgroup/BUILD.bazel new file mode 100644 index 0000000000000..299f0b3eee2f0 --- /dev/null +++ b/pkg/util/cgroup/BUILD.bazel @@ -0,0 +1,76 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cgroup", + srcs = [ + "cgroup.go", + "cgroup_cpu.go", + "cgroup_cpu_linux.go", + "cgroup_cpu_unsupport.go", + "cgroup_memory.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/cgroup", + visibility = ["//visibility:public"], + deps = [ + "@com_github_cockroachdb_errors//:errors", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ] + select({ + "@io_bazel_rules_go//go/platform:aix": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:android": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:illumos": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:ios": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:js": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "@com_github_pingcap_failpoint//:failpoint", + ], + "//conditions:default": [], + }), +) + +go_test( + name = "cgroup_test", + timeout = "short", + srcs = [ + "cgroup_cpu_test.go", + "cgroup_mock_test.go", + ], + embed = [":cgroup"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/cgroup/cgroup.go b/pkg/util/cgroup/cgroup.go similarity index 100% rename from util/cgroup/cgroup.go rename to pkg/util/cgroup/cgroup.go diff --git a/util/cgroup/cgroup_cpu.go b/pkg/util/cgroup/cgroup_cpu.go similarity index 100% rename from util/cgroup/cgroup_cpu.go rename to pkg/util/cgroup/cgroup_cpu.go diff --git a/util/cgroup/cgroup_cpu_linux.go b/pkg/util/cgroup/cgroup_cpu_linux.go similarity index 100% rename from util/cgroup/cgroup_cpu_linux.go rename to pkg/util/cgroup/cgroup_cpu_linux.go diff --git a/util/cgroup/cgroup_cpu_test.go b/pkg/util/cgroup/cgroup_cpu_test.go similarity index 100% rename from util/cgroup/cgroup_cpu_test.go rename to pkg/util/cgroup/cgroup_cpu_test.go diff --git a/util/cgroup/cgroup_cpu_unsupport.go b/pkg/util/cgroup/cgroup_cpu_unsupport.go similarity index 100% rename from util/cgroup/cgroup_cpu_unsupport.go rename to pkg/util/cgroup/cgroup_cpu_unsupport.go diff --git a/util/cgroup/cgroup_memory.go b/pkg/util/cgroup/cgroup_memory.go similarity index 100% rename from util/cgroup/cgroup_memory.go rename to pkg/util/cgroup/cgroup_memory.go diff --git a/util/cgroup/cgroup_mock_test.go b/pkg/util/cgroup/cgroup_mock_test.go similarity index 100% rename from util/cgroup/cgroup_mock_test.go rename to pkg/util/cgroup/cgroup_mock_test.go diff --git a/pkg/util/channel/BUILD.bazel b/pkg/util/channel/BUILD.bazel new file mode 100644 index 0000000000000..436d1ea1fb22b --- /dev/null +++ b/pkg/util/channel/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "channel", + srcs = ["channel.go"], + importpath = "github.com/pingcap/tidb/pkg/util/channel", + visibility = ["//visibility:public"], +) diff --git a/util/channel/channel.go b/pkg/util/channel/channel.go similarity index 100% rename from util/channel/channel.go rename to pkg/util/channel/channel.go diff --git a/pkg/util/checksum/BUILD.bazel b/pkg/util/checksum/BUILD.bazel new file mode 100644 index 0000000000000..ce53cd1e7cb08 --- /dev/null +++ b/pkg/util/checksum/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "checksum", + srcs = ["checksum.go"], + importpath = "github.com/pingcap/tidb/pkg/util/checksum", + visibility = ["//visibility:public"], + deps = ["//pkg/util/zeropool"], +) + +go_test( + name = "checksum_test", + timeout = "short", + srcs = [ + "checksum_test.go", + "main_test.go", + ], + embed = [":checksum"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/encrypt", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/checksum/checksum.go b/pkg/util/checksum/checksum.go new file mode 100644 index 0000000000000..c29bdea27294f --- /dev/null +++ b/pkg/util/checksum/checksum.go @@ -0,0 +1,183 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package checksum + +import ( + "encoding/binary" + "errors" + "hash/crc32" + "io" + + "github.com/pingcap/tidb/pkg/util/zeropool" +) + +const ( + // the size of whole checksum block + checksumBlockSize = 1024 + // the size of checksum field, we use CRC-32 algorithm to generate a 4 bytes checksum + checksumSize = 4 + // the size of the payload of a checksum block + checksumPayloadSize = checksumBlockSize - checksumSize +) + +var checksumReaderBufPool = zeropool.New[[]byte](func() []byte { + return make([]byte, checksumBlockSize) +}) + +// Writer implements an io.WriteCloser, it calculates and stores a CRC-32 checksum for the payload before +// writing to the underlying object. +// +// For example, a layout of the checksum block which payload is 2100 bytes is as follow: +// +// | -- 4B -- | -- 1020B -- || -- 4B -- | -- 1020B -- || -- 4B -- | -- 60B -- | +// | -- checksum -- | -- payload -- || -- checksum -- | -- payload -- || -- checksum -- | -- payload -- | +type Writer struct { + err error + w io.WriteCloser + buf []byte + payload []byte + payloadUsed int + flushedUserDataCnt int64 +} + +// NewWriter returns a new Writer which calculates and stores a CRC-32 checksum for the payload before +// writing to the underlying object. +func NewWriter(w io.WriteCloser) *Writer { + checksumWriter := &Writer{w: w} + checksumWriter.buf = make([]byte, checksumBlockSize) + checksumWriter.payload = checksumWriter.buf[checksumSize:] + checksumWriter.payloadUsed = 0 + return checksumWriter +} + +// AvailableSize returns how many bytes are unused in the buffer. +func (w *Writer) AvailableSize() int { return checksumPayloadSize - w.payloadUsed } + +// Write implements the io.Writer interface. +func (w *Writer) Write(p []byte) (n int, err error) { + for len(p) > w.AvailableSize() && w.err == nil { + copiedNum := copy(w.payload[w.payloadUsed:], p) + w.payloadUsed += copiedNum + err = w.Flush() + if err != nil { + return + } + n += copiedNum + p = p[copiedNum:] + } + if w.err != nil { + return n, w.err + } + copiedNum := copy(w.payload[w.payloadUsed:], p) + w.payloadUsed += copiedNum + n += copiedNum + return +} + +// Buffered returns the number of bytes that have been written into the current buffer. +func (w *Writer) Buffered() int { return w.payloadUsed } + +// Flush writes all the buffered data to the underlying object. +func (w *Writer) Flush() error { + if w.err != nil { + return w.err + } + if w.payloadUsed == 0 { + return nil + } + checksum := crc32.Checksum(w.payload[:w.payloadUsed], crc32.MakeTable(crc32.IEEE)) + binary.LittleEndian.PutUint32(w.buf, checksum) + n, err := w.w.Write(w.buf[:w.payloadUsed+checksumSize]) + if n < w.payloadUsed && err == nil { + err = io.ErrShortWrite + } + if err != nil { + w.err = err + return err + } + w.flushedUserDataCnt += int64(w.payloadUsed) + w.payloadUsed = 0 + return nil +} + +// GetCache returns the byte slice that holds the data not flushed to disk. +func (w *Writer) GetCache() []byte { + return w.payload[:w.payloadUsed] +} + +// GetCacheDataOffset return the user data offset in cache. +func (w *Writer) GetCacheDataOffset() int64 { + return w.flushedUserDataCnt +} + +// Close implements the io.Closer interface. +func (w *Writer) Close() (err error) { + err = w.Flush() + if err != nil { + return + } + return w.w.Close() +} + +// Reader implements an io.ReadAt, reading from the input source after verifying the checksum. +type Reader struct { + r io.ReaderAt +} + +// NewReader returns a new Reader which can read from the input source after verifying the checksum. +func NewReader(r io.ReaderAt) *Reader { + checksumReader := &Reader{r: r} + return checksumReader +} + +var errChecksumFail = errors.New("error checksum") + +// ReadAt implements the io.ReadAt interface. +func (r *Reader) ReadAt(p []byte, off int64) (nn int, err error) { + if len(p) == 0 { + return 0, nil + } + offsetInPayload := off % checksumPayloadSize + cursor := off / checksumPayloadSize * checksumBlockSize + + buf := checksumReaderBufPool.Get() + defer checksumReaderBufPool.Put(buf) + + var n int + for len(p) > 0 && err == nil { + n, err = r.r.ReadAt(buf, cursor) + if err != nil { + if n == 0 || err != io.EOF { + return nn, err + } + err = nil + // continue if n > 0 and r.err is io.EOF + } + if n < checksumSize { + return nn, errChecksumFail + } + cursor += int64(n) + originChecksum := binary.LittleEndian.Uint32(buf) + checksum := crc32.Checksum(buf[checksumSize:n], crc32.MakeTable(crc32.IEEE)) + if originChecksum != checksum { + return nn, errChecksumFail + } + n1 := copy(p, buf[checksumSize+offsetInPayload:n]) + nn += n1 + p = p[n1:] + offsetInPayload = 0 + } + return nn, err +} diff --git a/util/checksum/checksum_test.go b/pkg/util/checksum/checksum_test.go similarity index 99% rename from util/checksum/checksum_test.go rename to pkg/util/checksum/checksum_test.go index 9ffcbfd3195d1..7d7b9197b4665 100644 --- a/util/checksum/checksum_test.go +++ b/pkg/util/checksum/checksum_test.go @@ -20,7 +20,7 @@ import ( "strings" "testing" - encrypt2 "github.com/pingcap/tidb/util/encrypt" + encrypt2 "github.com/pingcap/tidb/pkg/util/encrypt" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/checksum/main_test.go b/pkg/util/checksum/main_test.go new file mode 100644 index 0000000000000..70535f9187fe1 --- /dev/null +++ b/pkg/util/checksum/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package checksum + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/chunk/BUILD.bazel b/pkg/util/chunk/BUILD.bazel new file mode 100644 index 0000000000000..ee4d5eee5d8ec --- /dev/null +++ b/pkg/util/chunk/BUILD.bazel @@ -0,0 +1,78 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "chunk", + srcs = [ + "alloc.go", + "chunk.go", + "chunk_util.go", + "codec.go", + "column.go", + "compare.go", + "disk.go", + "iterator.go", + "list.go", + "mutrow.go", + "pool.go", + "row.go", + "row_container.go", + "row_container_reader.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/chunk", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/types", + "//pkg/util/checksum", + "//pkg/util/disk", + "//pkg/util/encrypt", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_golang_x_sys//cpu", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "chunk_test", + timeout = "short", + srcs = [ + "alloc_test.go", + "chunk_test.go", + "chunk_util_test.go", + "codec_test.go", + "column_test.go", + "disk_test.go", + "iterator_test.go", + "list_test.go", + "main_test.go", + "mutrow_test.go", + "pool_test.go", + "row_container_test.go", + ], + embed = [":chunk"], + flaky = True, + race = "on", + shard_count = 50, + deps = [ + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/mathutil", + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/chunk/alloc.go b/pkg/util/chunk/alloc.go similarity index 98% rename from util/chunk/alloc.go rename to pkg/util/chunk/alloc.go index e9ebaf2e59efd..306ca6283897a 100644 --- a/util/chunk/alloc.go +++ b/pkg/util/chunk/alloc.go @@ -17,8 +17,8 @@ package chunk import ( "math" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // Allocator is an interface defined to reduce object allocation. diff --git a/util/chunk/alloc_test.go b/pkg/util/chunk/alloc_test.go similarity index 99% rename from util/chunk/alloc_test.go rename to pkg/util/chunk/alloc_test.go index 7c09d818d1222..b3838c96fca7d 100644 --- a/util/chunk/alloc_test.go +++ b/pkg/util/chunk/alloc_test.go @@ -17,8 +17,8 @@ package chunk import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/util/chunk/chunk.go b/pkg/util/chunk/chunk.go similarity index 99% rename from util/chunk/chunk.go rename to pkg/util/chunk/chunk.go index d04cafe70a5d9..db1539ed187af 100644 --- a/util/chunk/chunk.go +++ b/pkg/util/chunk/chunk.go @@ -18,8 +18,8 @@ import ( "unsafe" "github.com/pingcap/errors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" ) var msgErrSelNotNil = "The selection vector of Chunk is not nil. Please file a bug to the TiDB Team" diff --git a/util/chunk/chunk_test.go b/pkg/util/chunk/chunk_test.go similarity index 99% rename from util/chunk/chunk_test.go rename to pkg/util/chunk/chunk_test.go index d6550a8398842..2484f751b472a 100644 --- a/util/chunk/chunk_test.go +++ b/pkg/util/chunk/chunk_test.go @@ -23,10 +23,10 @@ import ( "time" "unsafe" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/require" ) diff --git a/util/chunk/chunk_util.go b/pkg/util/chunk/chunk_util.go similarity index 100% rename from util/chunk/chunk_util.go rename to pkg/util/chunk/chunk_util.go diff --git a/util/chunk/chunk_util_test.go b/pkg/util/chunk/chunk_util_test.go similarity index 99% rename from util/chunk/chunk_util_test.go rename to pkg/util/chunk/chunk_util_test.go index 368f27d08a107..32614ada18703 100644 --- a/util/chunk/chunk_util_test.go +++ b/pkg/util/chunk/chunk_util_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/chunk/codec.go b/pkg/util/chunk/codec.go new file mode 100644 index 0000000000000..3a64e48476e6f --- /dev/null +++ b/pkg/util/chunk/codec.go @@ -0,0 +1,354 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "encoding/binary" + "reflect" + "unsafe" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" +) + +// Codec is used to: +// 1. encode a Chunk to a byte slice. +// 2. decode a Chunk from a byte slice. +type Codec struct { + // colTypes is used to check whether a Column is fixed sized and what the + // fixed size for every element. + // NOTE: It's only used for decoding. + colTypes []*types.FieldType +} + +// NewCodec creates a new Codec object for encode or decode a Chunk. +func NewCodec(colTypes []*types.FieldType) *Codec { + return &Codec{colTypes} +} + +// Encode encodes a Chunk to a byte slice. +func (c *Codec) Encode(chk *Chunk) []byte { + buffer := make([]byte, 0, chk.MemoryUsage()) + for _, col := range chk.columns { + buffer = c.encodeColumn(buffer, col) + } + return buffer +} + +func (*Codec) encodeColumn(buffer []byte, col *Column) []byte { + var lenBuffer [4]byte + // encode length. + binary.LittleEndian.PutUint32(lenBuffer[:], uint32(col.length)) + buffer = append(buffer, lenBuffer[:4]...) + + // encode nullCount. + binary.LittleEndian.PutUint32(lenBuffer[:], uint32(col.nullCount())) + buffer = append(buffer, lenBuffer[:4]...) + + // encode nullBitmap. + if col.nullCount() > 0 { + numNullBitmapBytes := (col.length + 7) / 8 + buffer = append(buffer, col.nullBitmap[:numNullBitmapBytes]...) + } + + // encode offsets. + if !col.isFixed() { + numOffsetBytes := (col.length + 1) * 8 + offsetBytes := i64SliceToBytes(col.offsets) + buffer = append(buffer, offsetBytes[:numOffsetBytes]...) + } + + // encode data. + buffer = append(buffer, col.data...) + return buffer +} + +func i64SliceToBytes(i64s []int64) (b []byte) { + if len(i64s) == 0 { + return nil + } + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + hdr.Len = len(i64s) * 8 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&i64s[0])) + return b +} + +// Decode decodes a Chunk from a byte slice, return the remained unused bytes. +func (c *Codec) Decode(buffer []byte) (*Chunk, []byte) { + chk := &Chunk{} + for ordinal := 0; len(buffer) > 0; ordinal++ { + col := &Column{} + buffer = c.decodeColumn(buffer, col, ordinal) + chk.columns = append(chk.columns, col) + } + return chk, buffer +} + +// DecodeToChunk decodes a Chunk from a byte slice, return the remained unused bytes. +func (c *Codec) DecodeToChunk(buffer []byte, chk *Chunk) (remained []byte) { + for i := 0; i < len(chk.columns); i++ { + buffer = c.decodeColumn(buffer, chk.columns[i], i) + } + return buffer +} + +// decodeColumn decodes a Column from a byte slice, return the remained unused bytes. +func (c *Codec) decodeColumn(buffer []byte, col *Column, ordinal int) (remained []byte) { + // Todo(Shenghui Wu): Optimize all data is null. + // decode length. + col.length = int(binary.LittleEndian.Uint32(buffer)) + buffer = buffer[4:] + + // decode nullCount. + nullCount := int(binary.LittleEndian.Uint32(buffer)) + buffer = buffer[4:] + + // decode nullBitmap. + if nullCount > 0 { + numNullBitmapBytes := (col.length + 7) / 8 + col.nullBitmap = buffer[:numNullBitmapBytes:numNullBitmapBytes] + buffer = buffer[numNullBitmapBytes:] + } else { + c.setAllNotNull(col) + } + + // decode offsets. + numFixedBytes := getFixedLen(c.colTypes[ordinal]) + numDataBytes := int64(numFixedBytes * col.length) + if numFixedBytes == -1 { + numOffsetBytes := (col.length + 1) * 8 + col.offsets = bytesToI64Slice(buffer[:numOffsetBytes:numOffsetBytes]) + buffer = buffer[numOffsetBytes:] + numDataBytes = col.offsets[col.length] + } else if cap(col.elemBuf) < numFixedBytes { + col.elemBuf = make([]byte, numFixedBytes) + } + + // decode data. + col.data = buffer[:numDataBytes:numDataBytes] + // The column reference the data of the grpc response, the memory of the grpc message cannot be GCed if we reuse + // this column. Thus, we set `avoidReusing` to true. + col.avoidReusing = true + return buffer[numDataBytes:] +} + +var allNotNullBitmap [128]byte + +func (*Codec) setAllNotNull(col *Column) { + numNullBitmapBytes := (col.length + 7) / 8 + col.nullBitmap = col.nullBitmap[:0] + for i := 0; i < numNullBitmapBytes; { + numAppendBytes := mathutil.Min(numNullBitmapBytes-i, cap(allNotNullBitmap)) + col.nullBitmap = append(col.nullBitmap, allNotNullBitmap[:numAppendBytes]...) + i += numAppendBytes + } +} + +func bytesToI64Slice(b []byte) (i64s []int64) { + if len(b) == 0 { + return nil + } + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&i64s)) + hdr.Len = len(b) / 8 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&b[0])) + return i64s +} + +// varElemLen indicates this Column is a variable length Column. +const varElemLen = -1 + +func getFixedLen(colType *types.FieldType) int { + switch colType.GetType() { + case mysql.TypeFloat: + return 4 + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, + mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeYear, mysql.TypeDuration: + return 8 + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + return sizeTime + case mysql.TypeNewDecimal: + return types.MyDecimalStructSize + default: + return varElemLen + } +} + +// GetFixedLen get the memory size of a fixed-length type. +// if colType is not fixed-length, it returns varElemLen, aka -1. +func GetFixedLen(colType *types.FieldType) int { + return getFixedLen(colType) +} + +// EstimateTypeWidth estimates the average width of values of the type. +// This is used by the planner, which doesn't require absolutely correct results; +// it's OK (and expected) to guess if we don't know for sure. +// +// mostly study from https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/utils/cache/lsyscache.c#L2356 +func EstimateTypeWidth(colType *types.FieldType) int { + colLen := getFixedLen(colType) + // Easy if it's a fixed-width type + if colLen != varElemLen { + return colLen + } + + colLen = colType.GetFlen() + if colLen > 0 { + if colLen <= 32 { + return colLen + } + if colLen < 1000 { + return 32 + (colLen-32)/2 // assume 50% + } + /* + * Beyond 1000, assume we're looking at something like + * "varchar(10000)" where the limit isn't actually reached often, and + * use a fixed estimate. + */ + return 32 + (1000-32)/2 + } + // Oops, we have no idea ... wild guess time. + return 32 +} + +func init() { + for i := 0; i < 128; i++ { + allNotNullBitmap[i] = 0xFF + } +} + +// Decoder decodes the data returned from the coprocessor and stores the result in Chunk. +// How Decoder works: +// 1. Initialization phase: Decode a whole input byte slice to Decoder.intermChk(intermediate chunk) using Codec.Decode. +// intermChk is introduced to simplify the implementation of decode phase. This phase uses pointer operations with +// less CPU and memory cost. +// 2. Decode phase: +// 2.1 Set the number of rows to be decoded to a value that is a multiple of 8 and greater than +// `chk.RequiredRows() - chk.NumRows()`. This reduces the overhead of copying the srcCol.nullBitMap into +// destCol.nullBitMap. +// 2.2 Append srcCol.offsets to destCol.offsets when the elements is of var-length type. And further adjust the +// offsets according to descCol.offsets[destCol.length]-srcCol.offsets[0]. +// 2.3 Append srcCol.nullBitMap to destCol.nullBitMap. +// 3. Go to step 1 when the input byte slice is consumed. +type Decoder struct { + intermChk *Chunk + codec *Codec + remainedRows int +} + +// NewDecoder creates a new Decoder object for decode a Chunk. +func NewDecoder(chk *Chunk, colTypes []*types.FieldType) *Decoder { + return &Decoder{intermChk: chk, codec: NewCodec(colTypes), remainedRows: 0} +} + +// Decode decodes multiple rows of Decoder.intermChk and stores the result in chk. +func (c *Decoder) Decode(chk *Chunk) { + requiredRows := chk.RequiredRows() - chk.NumRows() + // Set the requiredRows to a multiple of 8. + requiredRows = (requiredRows + 7) >> 3 << 3 + if requiredRows > c.remainedRows { + requiredRows = c.remainedRows + } + for i := 0; i < chk.NumCols(); i++ { + c.decodeColumn(chk, i, requiredRows) + } + c.remainedRows -= requiredRows +} + +// Reset decodes data and store the result in Decoder.intermChk. This decode phase uses pointer operations with less +// CPU and memory costs. +func (c *Decoder) Reset(data []byte) { + c.codec.DecodeToChunk(data, c.intermChk) + c.remainedRows = c.intermChk.NumRows() +} + +// IsFinished indicates whether Decoder.intermChk has been dried up. +func (c *Decoder) IsFinished() bool { + return c.remainedRows == 0 +} + +// RemainedRows indicates Decoder.intermChk has remained rows. +func (c *Decoder) RemainedRows() int { + return c.remainedRows +} + +// ReuseIntermChk swaps `Decoder.intermChk` with `chk` directly when `Decoder.intermChk.NumRows()` is no less +// than `chk.requiredRows * factor` where `factor` is 0.8 now. This can avoid the overhead of appending the +// data from `Decoder.intermChk` to `chk`. Moreover, the column.offsets needs to be further adjusted +// according to column.offset[0]. +func (c *Decoder) ReuseIntermChk(chk *Chunk) { + for i, col := range c.intermChk.columns { + col.length = c.remainedRows + elemLen := getFixedLen(c.codec.colTypes[i]) + if elemLen == varElemLen { + // For var-length types, we need to adjust the offsets before reuse. + if deltaOffset := col.offsets[0]; deltaOffset != 0 { + for j := 0; j < len(col.offsets); j++ { + col.offsets[j] -= deltaOffset + } + } + } + } + chk.SwapColumns(c.intermChk) + c.remainedRows = 0 +} + +func (c *Decoder) decodeColumn(chk *Chunk, ordinal int, requiredRows int) { + elemLen := getFixedLen(c.codec.colTypes[ordinal]) + numDataBytes := int64(elemLen * requiredRows) + srcCol := c.intermChk.columns[ordinal] + destCol := chk.columns[ordinal] + + if elemLen == varElemLen { + // For var-length types, we need to adjust the offsets after appending to destCol. + numDataBytes = srcCol.offsets[requiredRows] - srcCol.offsets[0] + deltaOffset := destCol.offsets[destCol.length] - srcCol.offsets[0] + destCol.offsets = append(destCol.offsets, srcCol.offsets[1:requiredRows+1]...) + for i := destCol.length + 1; i <= destCol.length+requiredRows; i++ { + destCol.offsets[i] = destCol.offsets[i] + deltaOffset + } + srcCol.offsets = srcCol.offsets[requiredRows:] + } + + numNullBitmapBytes := (requiredRows + 7) >> 3 + if destCol.length%8 == 0 { + destCol.nullBitmap = append(destCol.nullBitmap, srcCol.nullBitmap[:numNullBitmapBytes]...) + } else { + destCol.appendMultiSameNullBitmap(false, requiredRows) + bitMapLen := len(destCol.nullBitmap) + // bitOffset indicates the number of valid bits in destCol.nullBitmap's last byte. + bitOffset := destCol.length % 8 + startIdx := (destCol.length - 1) >> 3 + for i := 0; i < numNullBitmapBytes; i++ { + destCol.nullBitmap[startIdx+i] |= srcCol.nullBitmap[i] << bitOffset + // The high order 8-bitOffset bits in `srcCol.nullBitmap[i]` should be appended to the low order of the next slot. + if startIdx+i+1 < bitMapLen { + destCol.nullBitmap[startIdx+i+1] |= srcCol.nullBitmap[i] >> (8 - bitOffset) + } + } + } + // Set all the redundant bits in the last slot of destCol.nullBitmap to 0. + numRedundantBits := uint(len(destCol.nullBitmap)*8 - destCol.length - requiredRows) + bitMask := byte(1<<(8-numRedundantBits)) - 1 + destCol.nullBitmap[len(destCol.nullBitmap)-1] &= bitMask + + srcCol.nullBitmap = srcCol.nullBitmap[numNullBitmapBytes:] + destCol.length += requiredRows + + destCol.data = append(destCol.data, srcCol.data[:numDataBytes]...) + srcCol.data = srcCol.data[numDataBytes:] +} diff --git a/pkg/util/chunk/codec_test.go b/pkg/util/chunk/codec_test.go new file mode 100644 index 0000000000000..904e8172f699e --- /dev/null +++ b/pkg/util/chunk/codec_test.go @@ -0,0 +1,191 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "fmt" + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestCodec(t *testing.T) { + numCols := 6 + numRows := 10 + + colTypes := make([]*types.FieldType, 0, numCols) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeNewDecimal)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeJSON)) + + oldChk := NewChunkWithCapacity(colTypes, numRows) + for i := 0; i < numRows; i++ { + str := fmt.Sprintf("%d.12345", i) + oldChk.AppendNull(0) + oldChk.AppendInt64(1, int64(i)) + oldChk.AppendString(2, str) + oldChk.AppendString(3, str) + oldChk.AppendMyDecimal(4, types.NewDecFromStringForTest(str)) + oldChk.AppendJSON(5, types.CreateBinaryJSON(str)) + } + + codec := NewCodec(colTypes) + buffer := codec.Encode(oldChk) + + newChk := NewChunkWithCapacity(colTypes, numRows) + remained := codec.DecodeToChunk(buffer, newChk) + + require.Empty(t, remained) + require.Equal(t, numCols, newChk.NumCols()) + require.Equal(t, numRows, newChk.NumRows()) + for i := 0; i < numRows; i++ { + row := newChk.GetRow(i) + str := fmt.Sprintf("%d.12345", i) + require.True(t, row.IsNull(0)) + require.False(t, row.IsNull(1)) + require.False(t, row.IsNull(2)) + require.False(t, row.IsNull(3)) + require.False(t, row.IsNull(4)) + require.False(t, row.IsNull(5)) + + require.Equal(t, int64(i), row.GetInt64(1)) + require.Equal(t, str, row.GetString(2)) + require.Equal(t, str, row.GetString(3)) + require.Equal(t, str, row.GetMyDecimal(4).String()) + require.Equal(t, str, string(row.GetJSON(5).GetString())) + } +} + +func TestEstimateTypeWidth(t *testing.T) { + var colType *types.FieldType + + colType = types.NewFieldType(mysql.TypeLonglong) + require.Equal(t, 8, EstimateTypeWidth(colType)) // fixed-witch type + + colType = types.NewFieldType(mysql.TypeString) + colType.SetFlen(31) + require.Equal(t, 31, EstimateTypeWidth(colType)) // colLen <= 32 + + colType = types.NewFieldType(mysql.TypeString) + colType.SetFlen(999) + require.Equal(t, 515, EstimateTypeWidth(colType)) // colLen < 1000 + + colType = types.NewFieldType(mysql.TypeString) + colType.SetFlen(2000) + require.Equal(t, 516, EstimateTypeWidth(colType)) // colLen < 1000 + + colType = types.NewFieldType(mysql.TypeString) + require.Equal(t, 32, EstimateTypeWidth(colType)) // value after guessing +} + +func BenchmarkEncodeChunk(b *testing.B) { + numCols := 4 + numRows := 1024 + + colTypes := make([]*types.FieldType, numCols) + for i := 0; i < numCols; i++ { + colTypes[i] = types.NewFieldType(mysql.TypeLonglong) + } + chk := NewChunkWithCapacity(colTypes, numRows) + + codec := &Codec{} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + codec.Encode(chk) + } +} + +func BenchmarkDecode(b *testing.B) { + numCols := 4 + numRows := 1024 + + colTypes := make([]*types.FieldType, numCols) + for i := 0; i < numCols; i++ { + colTypes[i] = types.NewFieldType(mysql.TypeLonglong) + } + chk := NewChunkWithCapacity(colTypes, numRows) + codec := &Codec{colTypes} + buffer := codec.Encode(chk) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + codec.Decode(buffer) + } +} + +func BenchmarkDecodeToChunk(b *testing.B) { + numCols := 4 + numRows := 1024 + + colTypes := make([]*types.FieldType, numCols) + chk := &Chunk{ + columns: make([]*Column, numCols), + } + for i := 0; i < numCols; i++ { + chk.columns[i] = &Column{ + length: numRows, + nullBitmap: make([]byte, numRows/8+1), + data: make([]byte, numRows*8), + elemBuf: make([]byte, 8), + } + colTypes[i] = types.NewFieldType(mysql.TypeLonglong) + } + codec := &Codec{colTypes} + buffer := codec.Encode(chk) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + codec.DecodeToChunk(buffer, chk) + } +} + +func BenchmarkDecodeToChunkWithVariableType(b *testing.B) { + numCols := 6 + numRows := 1024 + + colTypes := make([]*types.FieldType, 0, numCols) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeNewDecimal)) + colTypes = append(colTypes, types.NewFieldType(mysql.TypeJSON)) + + chk := NewChunkWithCapacity(colTypes, numRows) + for i := 0; i < numRows; i++ { + str := fmt.Sprintf("%d.12345", i) + chk.AppendNull(0) + chk.AppendInt64(1, int64(i)) + chk.AppendString(2, str) + chk.AppendString(3, str) + chk.AppendMyDecimal(4, types.NewDecFromStringForTest(str)) + chk.AppendJSON(5, types.CreateBinaryJSON(str)) + } + codec := &Codec{colTypes} + buffer := codec.Encode(chk) + + chk.Reset() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + codec.DecodeToChunk(buffer, chk) + } +} diff --git a/pkg/util/chunk/column.go b/pkg/util/chunk/column.go new file mode 100644 index 0000000000000..e83d939669690 --- /dev/null +++ b/pkg/util/chunk/column.go @@ -0,0 +1,764 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "fmt" + "math/bits" + "reflect" + "time" + "unsafe" + + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" +) + +// AppendDuration appends a duration value into this Column. +// Fsp is ignored. +func (c *Column) AppendDuration(dur types.Duration) { + c.AppendInt64(int64(dur.Duration)) +} + +// AppendMyDecimal appends a MyDecimal value into this Column. +func (c *Column) AppendMyDecimal(dec *types.MyDecimal) { + *(*types.MyDecimal)(unsafe.Pointer(&c.elemBuf[0])) = *dec + c.finishAppendFixed() +} + +func (c *Column) appendNameValue(name string, val uint64) { + var buf [8]byte + copy(buf[:], (*[8]byte)(unsafe.Pointer(&val))[:]) + c.data = append(c.data, buf[:]...) + c.data = append(c.data, name...) + c.finishAppendVar() +} + +// AppendJSON appends a BinaryJSON value into this Column. +func (c *Column) AppendJSON(j types.BinaryJSON) { + c.data = append(c.data, j.TypeCode) + c.data = append(c.data, j.Value...) + c.finishAppendVar() +} + +// AppendSet appends a Set value into this Column. +func (c *Column) AppendSet(set types.Set) { + c.appendNameValue(set.Name, set.Value) +} + +// Column stores one column of data in Apache Arrow format. +// See https://arrow.apache.org/docs/format/Columnar.html#format-columnar +type Column struct { + length int + nullBitmap []byte // bit 0 is null, 1 is not null + offsets []int64 // used for varLen column. Row i starts from data[offsets[i]] + data []byte + elemBuf []byte + + avoidReusing bool // avoid reusing the Column by allocator +} + +// ColumnAllocator defines an allocator for Column. +type ColumnAllocator interface { + NewColumn(ft *types.FieldType, count int) *Column +} + +// DefaultColumnAllocator is the default implementation of ColumnAllocator. +type DefaultColumnAllocator struct{} + +// NewColumn implements the ColumnAllocator interface. +func (DefaultColumnAllocator) NewColumn(ft *types.FieldType, capacity int) *Column { + return newColumn(getFixedLen(ft), capacity) +} + +// NewColumn creates a new column with the specific type and capacity. +func NewColumn(ft *types.FieldType, capacity int) *Column { + return newColumn(getFixedLen(ft), capacity) +} + +func newColumn(ts, capacity int) *Column { + var col *Column + if ts == varElemLen { + col = newVarLenColumn(capacity) + } else { + col = newFixedLenColumn(ts, capacity) + } + return col +} + +// newFixedLenColumn creates a fixed length Column with elemLen and initial data capacity. +func newFixedLenColumn(elemLen, capacity int) *Column { + return &Column{ + elemBuf: make([]byte, elemLen), + data: make([]byte, 0, capacity*elemLen), + nullBitmap: make([]byte, 0, (capacity+7)>>3), + } +} + +// newVarLenColumn creates a variable length Column with initial data capacity. +func newVarLenColumn(capacity int) *Column { + estimatedElemLen := 8 + // For varLenColumn (e.g. varchar), the accurate length of an element is unknown. + // Therefore, in the first executor.Next we use an experience value -- 8 (so it may make runtime.growslice) + + return &Column{ + offsets: make([]int64, 1, capacity+1), + data: make([]byte, 0, capacity*estimatedElemLen), + nullBitmap: make([]byte, 0, (capacity+7)>>3), + } +} + +func (c *Column) typeSize() int { + if len(c.elemBuf) > 0 { + return len(c.elemBuf) + } + return varElemLen +} + +func (c *Column) isFixed() bool { + return c.elemBuf != nil +} + +// Reset resets this Column according to the EvalType. +// Different from reset, Reset will reset the elemBuf. +func (c *Column) Reset(eType types.EvalType) { + switch eType { + case types.ETInt: + c.ResizeInt64(0, false) + case types.ETReal: + c.ResizeFloat64(0, false) + case types.ETDecimal: + c.ResizeDecimal(0, false) + case types.ETString: + c.ReserveString(0) + case types.ETDatetime, types.ETTimestamp: + c.ResizeTime(0, false) + case types.ETDuration: + c.ResizeGoDuration(0, false) + case types.ETJson: + c.ReserveJSON(0) + default: + panic(fmt.Sprintf("invalid EvalType %v", eType)) + } +} + +// reset resets the underlying data of this Column but doesn't modify its data type. +func (c *Column) reset() { + c.length = 0 + c.nullBitmap = c.nullBitmap[:0] + if len(c.offsets) > 0 { + // The first offset is always 0, it makes slicing the data easier, we need to keep it. + c.offsets = c.offsets[:1] + } + c.data = c.data[:0] +} + +// IsNull returns if this row is null. +func (c *Column) IsNull(rowIdx int) bool { + nullByte := c.nullBitmap[rowIdx/8] + return nullByte&(1<<(uint(rowIdx)&7)) == 0 +} + +// CopyConstruct copies this Column to dst. +// If dst is nil, it creates a new Column and returns it. +func (c *Column) CopyConstruct(dst *Column) *Column { + if dst != nil { + dst.length = c.length + dst.nullBitmap = append(dst.nullBitmap[:0], c.nullBitmap...) + dst.offsets = append(dst.offsets[:0], c.offsets...) + dst.data = append(dst.data[:0], c.data...) + dst.elemBuf = append(dst.elemBuf[:0], c.elemBuf...) + return dst + } + newCol := &Column{length: c.length} + newCol.nullBitmap = append(newCol.nullBitmap, c.nullBitmap...) + newCol.offsets = append(newCol.offsets, c.offsets...) + newCol.data = append(newCol.data, c.data...) + newCol.elemBuf = append(newCol.elemBuf, c.elemBuf...) + return newCol +} + +func (c *Column) appendNullBitmap(notNull bool) { + idx := c.length >> 3 + if idx >= len(c.nullBitmap) { + c.nullBitmap = append(c.nullBitmap, 0) + } + if notNull { + pos := uint(c.length) & 7 + c.nullBitmap[idx] |= byte(1 << pos) + } +} + +// appendMultiSameNullBitmap appends multiple same bit value to `nullBitMap`. +// notNull means not null. +// num means the number of bits that should be appended. +func (c *Column) appendMultiSameNullBitmap(notNull bool, num int) { + numNewBytes := ((c.length + num + 7) >> 3) - len(c.nullBitmap) + b := byte(0) + if notNull { + b = 0xff + } + for i := 0; i < numNewBytes; i++ { + c.nullBitmap = append(c.nullBitmap, b) + } + if !notNull { + return + } + // 1. Set all the remaining bits in the last slot of old c.numBitMap to 1. + numRemainingBits := uint(c.length % 8) + bitMask := byte(^((1 << numRemainingBits) - 1)) + c.nullBitmap[c.length/8] |= bitMask + // 2. Set all the redundant bits in the last slot of new c.numBitMap to 0. + numRedundantBits := uint(len(c.nullBitmap)*8 - c.length - num) + bitMask = byte(1<<(8-numRedundantBits)) - 1 + c.nullBitmap[len(c.nullBitmap)-1] &= bitMask +} + +// AppendNull appends a null value into this Column. +func (c *Column) AppendNull() { + c.appendNullBitmap(false) + if c.isFixed() { + c.data = append(c.data, c.elemBuf...) + } else { + c.offsets = append(c.offsets, c.offsets[c.length]) + } + c.length++ +} + +func (c *Column) finishAppendFixed() { + c.data = append(c.data, c.elemBuf...) + c.appendNullBitmap(true) + c.length++ +} + +// AppendInt64 appends an int64 value into this Column. +func (c *Column) AppendInt64(i int64) { + *(*int64)(unsafe.Pointer(&c.elemBuf[0])) = i + c.finishAppendFixed() +} + +// AppendUint64 appends a uint64 value into this Column. +func (c *Column) AppendUint64(u uint64) { + *(*uint64)(unsafe.Pointer(&c.elemBuf[0])) = u + c.finishAppendFixed() +} + +// AppendFloat32 appends a float32 value into this Column. +func (c *Column) AppendFloat32(f float32) { + *(*float32)(unsafe.Pointer(&c.elemBuf[0])) = f + c.finishAppendFixed() +} + +// AppendFloat64 appends a float64 value into this Column. +func (c *Column) AppendFloat64(f float64) { + *(*float64)(unsafe.Pointer(&c.elemBuf[0])) = f + c.finishAppendFixed() +} + +func (c *Column) finishAppendVar() { + c.appendNullBitmap(true) + c.offsets = append(c.offsets, int64(len(c.data))) + c.length++ +} + +// AppendString appends a string value into this Column. +func (c *Column) AppendString(str string) { + c.data = append(c.data, str...) + c.finishAppendVar() +} + +// AppendBytes appends a byte slice into this Column. +func (c *Column) AppendBytes(b []byte) { + c.data = append(c.data, b...) + c.finishAppendVar() +} + +// AppendTime appends a time value into this Column. +func (c *Column) AppendTime(t types.Time) { + *(*types.Time)(unsafe.Pointer(&c.elemBuf[0])) = t + c.finishAppendFixed() +} + +// AppendEnum appends a Enum value into this Column. +func (c *Column) AppendEnum(enum types.Enum) { + c.appendNameValue(enum.Name, enum.Value) +} + +const ( + sizeInt64 = int(unsafe.Sizeof(int64(0))) + sizeUint64 = int(unsafe.Sizeof(uint64(0))) + sizeFloat32 = int(unsafe.Sizeof(float32(0))) + sizeFloat64 = int(unsafe.Sizeof(float64(0))) + sizeMyDecimal = int(unsafe.Sizeof(types.MyDecimal{})) + sizeGoDuration = int(unsafe.Sizeof(time.Duration(0))) + sizeTime = int(unsafe.Sizeof(types.ZeroTime)) +) + +var ( + emptyBuf = make([]byte, 4*1024) +) + +// resize resizes the column so that it contains n elements, only valid for fixed-length types. +func (c *Column) resize(n, typeSize int, isNull bool) { + sizeData := n * typeSize + if cap(c.data) >= sizeData { + (*reflect.SliceHeader)(unsafe.Pointer(&c.data)).Len = sizeData + } else { + c.data = make([]byte, sizeData) + } + if !isNull { + for j := 0; j < sizeData; j += len(emptyBuf) { + copy(c.data[j:], emptyBuf) + } + } + + newNulls := false + sizeNulls := (n + 7) >> 3 + if cap(c.nullBitmap) >= sizeNulls { + (*reflect.SliceHeader)(unsafe.Pointer(&c.nullBitmap)).Len = sizeNulls + } else { + c.nullBitmap = make([]byte, sizeNulls) + newNulls = true + } + if !isNull || !newNulls { + var nullVal, lastByte byte + if !isNull { + nullVal = 0xFF + } + + // Fill the null bitmap + for i := range c.nullBitmap { + c.nullBitmap[i] = nullVal + } + // Revise the last byte if necessary, when it's not divided by 8. + if x := (n % 8); x != 0 { + if !isNull { + lastByte = byte((1 << x) - 1) + if len(c.nullBitmap) > 0 { + c.nullBitmap[len(c.nullBitmap)-1] = lastByte + } + } + } + } + + if cap(c.elemBuf) >= typeSize { + (*reflect.SliceHeader)(unsafe.Pointer(&c.elemBuf)).Len = typeSize + } else { + c.elemBuf = make([]byte, typeSize) + } + + c.length = n +} + +// reserve makes the column capacity be at least enough to contain n elements. +// this method is only valid for var-length types and estElemSize is the estimated size of this type. +func (c *Column) reserve(n, estElemSize int) { + sizeData := n * estElemSize + if cap(c.data) >= sizeData { + c.data = c.data[:0] + } else { + c.data = make([]byte, 0, sizeData) + } + + sizeNulls := (n + 7) >> 3 + if cap(c.nullBitmap) >= sizeNulls { + c.nullBitmap = c.nullBitmap[:0] + } else { + c.nullBitmap = make([]byte, 0, sizeNulls) + } + + sizeOffs := n + 1 + if cap(c.offsets) >= sizeOffs { + c.offsets = c.offsets[:1] + } else { + c.offsets = make([]int64, 1, sizeOffs) + } + + c.elemBuf = nil + c.length = 0 +} + +// SetNull sets the rowIdx to null. +func (c *Column) SetNull(rowIdx int, isNull bool) { + if isNull { + c.nullBitmap[rowIdx>>3] &= ^(1 << uint(rowIdx&7)) + } else { + c.nullBitmap[rowIdx>>3] |= 1 << uint(rowIdx&7) + } +} + +// SetNulls sets rows in [begin, end) to null. +func (c *Column) SetNulls(begin, end int, isNull bool) { + i := ((begin + 7) >> 3) << 3 + for ; begin < i && begin < end; begin++ { + c.SetNull(begin, isNull) + } + var v uint8 + if !isNull { + v = (1 << 8) - 1 + } + for ; begin+8 <= end; begin += 8 { + c.nullBitmap[begin>>3] = v + } + for ; begin < end; begin++ { + c.SetNull(begin, isNull) + } +} + +// nullCount returns the number of nulls in this Column. +func (c *Column) nullCount() int { + var cnt, i int + for ; i+8 <= c.length; i += 8 { + // 0 is null and 1 is not null + cnt += 8 - bits.OnesCount8(c.nullBitmap[i>>3]) + } + for ; i < c.length; i++ { + if c.IsNull(i) { + cnt++ + } + } + return cnt +} + +// ResizeInt64 resizes the column so that it contains n int64 elements. +func (c *Column) ResizeInt64(n int, isNull bool) { + c.resize(n, sizeInt64, isNull) +} + +// ResizeUint64 resizes the column so that it contains n uint64 elements. +func (c *Column) ResizeUint64(n int, isNull bool) { + c.resize(n, sizeUint64, isNull) +} + +// ResizeFloat32 resizes the column so that it contains n float32 elements. +func (c *Column) ResizeFloat32(n int, isNull bool) { + c.resize(n, sizeFloat32, isNull) +} + +// ResizeFloat64 resizes the column so that it contains n float64 elements. +func (c *Column) ResizeFloat64(n int, isNull bool) { + c.resize(n, sizeFloat64, isNull) +} + +// ResizeDecimal resizes the column so that it contains n decimal elements. +func (c *Column) ResizeDecimal(n int, isNull bool) { + c.resize(n, sizeMyDecimal, isNull) +} + +// ResizeGoDuration resizes the column so that it contains n duration elements. +func (c *Column) ResizeGoDuration(n int, isNull bool) { + c.resize(n, sizeGoDuration, isNull) +} + +// ResizeTime resizes the column so that it contains n Time elements. +func (c *Column) ResizeTime(n int, isNull bool) { + c.resize(n, sizeTime, isNull) +} + +// ReserveString changes the column capacity to store n string elements and set the length to zero. +func (c *Column) ReserveString(n int) { + c.reserve(n, 8) +} + +// ReserveBytes changes the column capacity to store n bytes elements and set the length to zero. +func (c *Column) ReserveBytes(n int) { + c.reserve(n, 8) +} + +// ReserveJSON changes the column capacity to store n JSON elements and set the length to zero. +func (c *Column) ReserveJSON(n int) { + c.reserve(n, 8) +} + +// ReserveSet changes the column capacity to store n set elements and set the length to zero. +func (c *Column) ReserveSet(n int) { + c.reserve(n, 8) +} + +// ReserveEnum changes the column capacity to store n enum elements and set the length to zero. +func (c *Column) ReserveEnum(n int) { + c.reserve(n, 8) +} + +func (c *Column) castSliceHeader(header *reflect.SliceHeader, typeSize int) { + header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&c.data)).Data + header.Len = c.length + header.Cap = cap(c.data) / typeSize +} + +// Int64s returns an int64 slice stored in this Column. +func (c *Column) Int64s() []int64 { + var res []int64 + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeInt64) + return res +} + +// Uint64s returns a uint64 slice stored in this Column. +func (c *Column) Uint64s() []uint64 { + var res []uint64 + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeUint64) + return res +} + +// Float32s returns a float32 slice stored in this Column. +func (c *Column) Float32s() []float32 { + var res []float32 + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeFloat32) + return res +} + +// Float64s returns a float64 slice stored in this Column. +func (c *Column) Float64s() []float64 { + var res []float64 + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeFloat64) + return res +} + +// GoDurations returns a Golang time.Duration slice stored in this Column. +// Different from the Row.GetDuration method, the argument Fsp is ignored, so the user should handle it outside. +func (c *Column) GoDurations() []time.Duration { + var res []time.Duration + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeGoDuration) + return res +} + +// Decimals returns a MyDecimal slice stored in this Column. +func (c *Column) Decimals() []types.MyDecimal { + var res []types.MyDecimal + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeMyDecimal) + return res +} + +// Times returns a Time slice stored in this Column. +func (c *Column) Times() []types.Time { + var res []types.Time + c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeTime) + return res +} + +// GetInt64 returns the int64 in the specific row. +func (c *Column) GetInt64(rowID int) int64 { + return *(*int64)(unsafe.Pointer(&c.data[rowID*8])) +} + +// GetUint64 returns the uint64 in the specific row. +func (c *Column) GetUint64(rowID int) uint64 { + return *(*uint64)(unsafe.Pointer(&c.data[rowID*8])) +} + +// GetFloat32 returns the float32 in the specific row. +func (c *Column) GetFloat32(rowID int) float32 { + return *(*float32)(unsafe.Pointer(&c.data[rowID*4])) +} + +// GetFloat64 returns the float64 in the specific row. +func (c *Column) GetFloat64(rowID int) float64 { + return *(*float64)(unsafe.Pointer(&c.data[rowID*8])) +} + +// GetDecimal returns the decimal in the specific row. +func (c *Column) GetDecimal(rowID int) *types.MyDecimal { + return (*types.MyDecimal)(unsafe.Pointer(&c.data[rowID*types.MyDecimalStructSize])) +} + +// GetString returns the string in the specific row. +func (c *Column) GetString(rowID int) string { + return string(hack.String(c.data[c.offsets[rowID]:c.offsets[rowID+1]])) +} + +// GetJSON returns the JSON in the specific row. +func (c *Column) GetJSON(rowID int) types.BinaryJSON { + start := c.offsets[rowID] + return types.BinaryJSON{TypeCode: c.data[start], Value: c.data[start+1 : c.offsets[rowID+1]]} +} + +// GetBytes returns the byte slice in the specific row. +func (c *Column) GetBytes(rowID int) []byte { + return c.data[c.offsets[rowID]:c.offsets[rowID+1]] +} + +// GetEnum returns the Enum in the specific row. +func (c *Column) GetEnum(rowID int) types.Enum { + name, val := c.getNameValue(rowID) + return types.Enum{Name: name, Value: val} +} + +// GetSet returns the Set in the specific row. +func (c *Column) GetSet(rowID int) types.Set { + name, val := c.getNameValue(rowID) + return types.Set{Name: name, Value: val} +} + +// GetTime returns the Time in the specific row. +func (c *Column) GetTime(rowID int) types.Time { + return *(*types.Time)(unsafe.Pointer(&c.data[rowID*sizeTime])) +} + +// GetDuration returns the Duration in the specific row. +func (c *Column) GetDuration(rowID int, fillFsp int) types.Duration { + dur := *(*int64)(unsafe.Pointer(&c.data[rowID*8])) + return types.Duration{Duration: time.Duration(dur), Fsp: fillFsp} +} + +func (c *Column) getNameValue(rowID int) (string, uint64) { + start, end := c.offsets[rowID], c.offsets[rowID+1] + if start == end { + return "", 0 + } + var val uint64 + copy((*[8]byte)(unsafe.Pointer(&val))[:], c.data[start:]) + return string(hack.String(c.data[start+8 : end])), val +} + +// GetRaw returns the underlying raw bytes in the specific row. +func (c *Column) GetRaw(rowID int) []byte { + var data []byte + if c.isFixed() { + elemLen := len(c.elemBuf) + data = c.data[rowID*elemLen : rowID*elemLen+elemLen] + } else { + data = c.data[c.offsets[rowID]:c.offsets[rowID+1]] + } + return data +} + +// SetRaw sets the raw bytes for the rowIdx-th element. +// NOTE: Two conditions must be satisfied before calling this function: +// 1. The column should be stored with variable-length elements. +// 2. The length of the new element should be exactly the same as the old one. +func (c *Column) SetRaw(rowID int, bs []byte) { + copy(c.data[c.offsets[rowID]:c.offsets[rowID+1]], bs) +} + +// reconstruct reconstructs this Column by removing all filtered rows in it according to sel. +func (c *Column) reconstruct(sel []int) { + if sel == nil { + return + } + if c.isFixed() { + elemLen := len(c.elemBuf) + for dst, src := range sel { + idx := dst >> 3 + pos := uint16(dst & 7) + if c.IsNull(src) { + c.nullBitmap[idx] &= ^byte(1 << pos) + } else { + copy(c.data[dst*elemLen:dst*elemLen+elemLen], c.data[src*elemLen:src*elemLen+elemLen]) + c.nullBitmap[idx] |= byte(1 << pos) + } + } + c.data = c.data[:len(sel)*elemLen] + } else { + tail := 0 + for dst, src := range sel { + idx := dst >> 3 + pos := uint(dst & 7) + if c.IsNull(src) { + c.nullBitmap[idx] &= ^byte(1 << pos) + c.offsets[dst+1] = int64(tail) + } else { + start, end := c.offsets[src], c.offsets[src+1] + copy(c.data[tail:], c.data[start:end]) + tail += int(end - start) + c.offsets[dst+1] = int64(tail) + c.nullBitmap[idx] |= byte(1 << pos) + } + } + c.data = c.data[:tail] + c.offsets = c.offsets[:len(sel)+1] + } + c.length = len(sel) + + // clean nullBitmap + c.nullBitmap = c.nullBitmap[:(len(sel)+7)>>3] + idx := len(sel) >> 3 + if idx < len(c.nullBitmap) { + pos := uint16(len(sel) & 7) + c.nullBitmap[idx] &= byte((1 << pos) - 1) + } +} + +// CopyReconstruct copies this Column to dst and removes unselected rows. +// If dst is nil, it creates a new Column and returns it. +func (c *Column) CopyReconstruct(sel []int, dst *Column) *Column { + if sel == nil { + return c.CopyConstruct(dst) + } + + selLength := len(sel) + if selLength == c.length { + // The variable 'ascend' is used to check if the sel array is in ascending order + ascend := true + for i := 1; i < selLength; i++ { + if sel[i] < sel[i-1] { + ascend = false + break + } + } + if ascend { + return c.CopyConstruct(dst) + } + } + + if dst == nil { + dst = newColumn(c.typeSize(), len(sel)) + } else { + dst.reset() + } + + if c.isFixed() { + elemLen := len(c.elemBuf) + dst.elemBuf = make([]byte, elemLen) + for _, i := range sel { + dst.appendNullBitmap(!c.IsNull(i)) + dst.data = append(dst.data, c.data[i*elemLen:i*elemLen+elemLen]...) + dst.length++ + } + } else { + dst.elemBuf = nil + if len(dst.offsets) == 0 { + dst.offsets = append(dst.offsets, 0) + } + for _, i := range sel { + dst.appendNullBitmap(!c.IsNull(i)) + start, end := c.offsets[i], c.offsets[i+1] + dst.data = append(dst.data, c.data[start:end]...) + dst.offsets = append(dst.offsets, int64(len(dst.data))) + dst.length++ + } + } + return dst +} + +// MergeNulls merges these columns' null bitmaps. +// For a row, if any column of it is null, the result is null. +// It works like: if col1.IsNull || col2.IsNull || col3.IsNull. +// The caller should ensure that all these columns have the same +// length, and data stored in the result column is fixed-length type. +func (c *Column) MergeNulls(cols ...*Column) { + if !c.isFixed() { + panic("result column should be fixed-length type") + } + for _, col := range cols { + if c.length != col.length { + panic(fmt.Sprintf("should ensure all columns have the same length, expect %v, but got %v", c.length, col.length)) + } + } + for _, col := range cols { + for i := range c.nullBitmap { + // bit 0 is null, 1 is not null, so do AND operations here. + c.nullBitmap[i] &= col.nullBitmap[i] + } + } +} diff --git a/pkg/util/chunk/column_test.go b/pkg/util/chunk/column_test.go new file mode 100644 index 0000000000000..0e1d3813637da --- /dev/null +++ b/pkg/util/chunk/column_test.go @@ -0,0 +1,995 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "fmt" + "math/rand" + "testing" + "time" + "unsafe" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestColumnCopy(t *testing.T) { + col := newFixedLenColumn(8, 10) + for i := 0; i < 10; i++ { + col.AppendInt64(int64(i)) + } + + c1 := col.CopyConstruct(nil) + require.Equal(t, col, c1) + + c2 := newFixedLenColumn(8, 10) + c2 = col.CopyConstruct(c2) + require.Equal(t, col, c2) +} + +func TestColumnCopyReconstructFixedLen(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + results := make([]int64, 0, 1024) + nulls := make([]bool, 0, 1024) + sel := make([]int, 0, 1024) + for i := 0; i < 1024; i++ { + if rand.Intn(10) < 6 { + sel = append(sel, i) + } + + if rand.Intn(10) < 2 { + col.AppendNull() + nulls = append(nulls, true) + results = append(results, 0) + continue + } + + v := rand.Int63() + col.AppendInt64(v) + results = append(results, v) + nulls = append(nulls, false) + } + + col = col.CopyReconstruct(sel, nil) + nullCnt := 0 + for n, i := range sel { + if nulls[i] { + nullCnt++ + require.True(t, col.IsNull(n)) + } else { + require.Equal(t, results[i], col.GetInt64(n)) + } + } + require.Equal(t, col.nullCount(), nullCnt) + require.Len(t, sel, col.length) + + for i := 0; i < 128; i++ { + if i%2 == 0 { + col.AppendNull() + } else { + col.AppendInt64(int64(i * i * i)) + } + } + + require.Len(t, sel, col.length-128) + require.Equal(t, nullCnt+128/2, col.nullCount()) + for i := 0; i < 128; i++ { + if i%2 == 0 { + require.True(t, col.IsNull(len(sel)+i)) + } else { + require.Equal(t, int64(i*i*i), col.GetInt64(len(sel)+i)) + require.False(t, col.IsNull(len(sel)+i)) + } + } +} + +func TestColumnCopyReconstructVarLen(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeVarString), 1024) + results := make([]string, 0, 1024) + nulls := make([]bool, 0, 1024) + sel := make([]int, 0, 1024) + for i := 0; i < 1024; i++ { + if rand.Intn(10) < 6 { + sel = append(sel, i) + } + + if rand.Intn(10) < 2 { + col.AppendNull() + nulls = append(nulls, true) + results = append(results, "") + continue + } + + v := fmt.Sprintf("%v", rand.Int63()) + col.AppendString(v) + results = append(results, v) + nulls = append(nulls, false) + } + + col = col.CopyReconstruct(sel, nil) + nullCnt := 0 + for n, i := range sel { + if nulls[i] { + nullCnt++ + require.True(t, col.IsNull(n)) + } else { + require.Equal(t, results[i], col.GetString(n)) + } + } + require.Equal(t, col.nullCount(), nullCnt) + require.Len(t, sel, col.length) + + for i := 0; i < 128; i++ { + if i%2 == 0 { + col.AppendNull() + } else { + col.AppendString(fmt.Sprintf("%v", i*i*i)) + } + } + + require.Len(t, sel, col.length-128) + require.Equal(t, nullCnt+128/2, col.nullCount()) + for i := 0; i < 128; i++ { + if i%2 == 0 { + require.True(t, col.IsNull(len(sel)+i)) + } else { + require.Equal(t, fmt.Sprintf("%v", i*i*i), col.GetString(len(sel)+i)) + require.False(t, col.IsNull(len(sel)+i)) + } + } +} + +func TestLargeStringColumnOffset(t *testing.T) { + numRows := 1 + col := newVarLenColumn(numRows) + // The max-length of a string field can be 6M, a typical batch size for Chunk is 1024, which is 1K. + // That is to say, the memory offset of a string column can be 6GB, which exceeds int32 + col.offsets[0] = 6 << 30 + require.Equal(t, int64(6<<30), col.offsets[0]) // test no overflow. +} + +func TestI64Column(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendInt64(int64(i)) + } + + i64s := col.Int64s() + for i := 0; i < 1024; i++ { + require.Equal(t, int64(i), i64s[i]) + i64s[i]++ + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, int64(i+1), row.GetInt64(0)) + require.Equal(t, int64(i+1), col.GetInt64(i)) + i++ + } +} + +func TestF64Column(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDouble)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendFloat64(float64(i)) + } + + f64s := col.Float64s() + for i := 0; i < 1024; i++ { + require.Equal(t, float64(i), f64s[i]) + f64s[i] /= 2 + } + + it := NewIterator4Chunk(chk) + var i int64 + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, float64(i)/2, row.GetFloat64(0)) + require.Equal(t, float64(i)/2, col.GetFloat64(int(i))) + i++ + } +} + +func TestF32Column(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeFloat)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendFloat32(float32(i)) + } + + f32s := col.Float32s() + for i := 0; i < 1024; i++ { + require.Equal(t, float32(i), f32s[i]) + f32s[i] /= 2 + } + + it := NewIterator4Chunk(chk) + var i int64 + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, float32(i)/2, row.GetFloat32(0)) + require.Equal(t, float32(i)/2, col.GetFloat32(int(i))) + i++ + } +} + +func TestDurationSliceColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendDuration(types.Duration{Duration: time.Duration(i)}) + } + + ds := col.GoDurations() + for i := 0; i < 1024; i++ { + require.Equal(t, time.Duration(i), ds[i]) + d := types.Duration{Duration: ds[i]} + d, _ = d.Add(d) + ds[i] = d.Duration + } + + it := NewIterator4Chunk(chk) + var i int64 + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, time.Duration(i)*2, row.GetDuration(0, 0).Duration) + require.Equal(t, time.Duration(i)*2, col.GetDuration(int(i), 0).Duration) + i++ + } +} + +func TestMyDecimal(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeNewDecimal)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + d := new(types.MyDecimal) + err := d.FromFloat64(float64(i) * 1.1) + require.NoError(t, err) + col.AppendMyDecimal(d) + } + + ds := col.Decimals() + for i := 0; i < 1024; i++ { + d := new(types.MyDecimal) + err := d.FromFloat64(float64(i) * 1.1) + require.NoError(t, err) + require.Zero(t, d.Compare(&ds[i])) + + types.DecimalAdd(&ds[i], d, &ds[i]) + require.NoError(t, err) + } + + it := NewIterator4Chunk(chk) + var i int64 + for row := it.Begin(); row != it.End(); row = it.Next() { + d := new(types.MyDecimal) + err := d.FromFloat64(float64(i) * 1.1 * 2) + require.NoError(t, err) + + delta := new(types.MyDecimal) + err = types.DecimalSub(d, row.GetMyDecimal(0), delta) + require.NoError(t, err) + + fDelta, err := delta.ToFloat64() + require.NoError(t, err) + require.InDelta(t, 0, fDelta, 0.0001) + + i++ + } +} + +func TestStringColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeVarString)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendString(fmt.Sprintf("%v", i*i)) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, fmt.Sprintf("%v", i*i), row.GetString(0)) + require.Equal(t, fmt.Sprintf("%v", i*i), col.GetString(i)) + i++ + } +} + +func TestSetColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeSet)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendSet(types.Set{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + s1 := col.GetSet(i) + s2 := row.GetSet(0) + require.Equal(t, s2.Name, s1.Name) + require.Equal(t, s2.Value, s1.Value) + require.Equal(t, fmt.Sprintf("%v", i), s1.Name) + require.Equal(t, uint64(i), s1.Value) + i++ + } +} + +func TestJSONColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeJSON)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + j := new(types.BinaryJSON) + err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"%v":%v}`, i, i))) + require.NoError(t, err) + col.AppendJSON(*j) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + j1 := col.GetJSON(i) + j2 := row.GetJSON(0) + require.Equal(t, j2.String(), j1.String()) + i++ + } +} + +func TestTimeColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDatetime)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendTime(types.CurrentTime(mysql.TypeDatetime)) + time.Sleep(time.Millisecond / 10) + } + + it := NewIterator4Chunk(chk) + ts := col.Times() + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + j1 := col.GetTime(i) + j2 := row.GetTime(0) + j3 := ts[i] + require.Zero(t, j1.Compare(j2)) + require.Zero(t, j1.Compare(j3)) + i++ + } +} + +func TestDurationColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + j1 := col.GetDuration(i, 0) + j2 := row.GetDuration(0, 0) + require.Zero(t, j1.Compare(j2)) + i++ + } +} + +func TestEnumColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeEnum)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendEnum(types.Enum{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + s1 := col.GetEnum(i) + s2 := row.GetEnum(0) + require.Equal(t, s2.Name, s1.Name) + require.Equal(t, s2.Value, s1.Value) + require.Equal(t, fmt.Sprintf("%v", i), s1.Name) + require.Equal(t, uint64(i), s1.Value) + i++ + } +} + +func TestNullsColumn(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + if i%2 == 0 { + col.AppendNull() + continue + } + col.AppendInt64(int64(i)) + } + + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + if i%2 == 0 { + require.True(t, row.IsNull(0)) + require.True(t, col.IsNull(i)) + } else { + require.Equal(t, int64(i), row.GetInt64(0)) + } + i++ + } +} + +func TestReconstructFixedLen(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + results := make([]int64, 0, 1024) + nulls := make([]bool, 0, 1024) + sel := make([]int, 0, 1024) + for i := 0; i < 1024; i++ { + if rand.Intn(10) < 6 { + sel = append(sel, i) + } + + if rand.Intn(10) < 2 { + col.AppendNull() + nulls = append(nulls, true) + results = append(results, 0) + continue + } + + v := rand.Int63() + col.AppendInt64(v) + results = append(results, v) + nulls = append(nulls, false) + } + + col.reconstruct(sel) + nullCnt := 0 + for n, i := range sel { + if nulls[i] { + nullCnt++ + require.True(t, col.IsNull(n)) + } else { + require.Equal(t, results[i], col.GetInt64(n)) + } + } + require.Equal(t, col.nullCount(), nullCnt) + require.Len(t, sel, col.length) + + for i := 0; i < 128; i++ { + if i%2 == 0 { + col.AppendNull() + } else { + col.AppendInt64(int64(i * i * i)) + } + } + + require.Len(t, sel, col.length-128) + require.Equal(t, nullCnt+128/2, col.nullCount()) + for i := 0; i < 128; i++ { + if i%2 == 0 { + require.True(t, col.IsNull(len(sel)+i)) + } else { + require.Equal(t, int64(i*i*i), col.GetInt64(len(sel)+i)) + require.False(t, col.IsNull(len(sel)+i)) + } + } +} + +func TestReconstructVarLen(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeVarString), 1024) + results := make([]string, 0, 1024) + nulls := make([]bool, 0, 1024) + sel := make([]int, 0, 1024) + for i := 0; i < 1024; i++ { + if rand.Intn(10) < 6 { + sel = append(sel, i) + } + + if rand.Intn(10) < 2 { + col.AppendNull() + nulls = append(nulls, true) + results = append(results, "") + continue + } + + v := fmt.Sprintf("%v", rand.Int63()) + col.AppendString(v) + results = append(results, v) + nulls = append(nulls, false) + } + + col.reconstruct(sel) + nullCnt := 0 + for n, i := range sel { + if nulls[i] { + nullCnt++ + require.True(t, col.IsNull(n)) + } else { + require.Equal(t, results[i], col.GetString(n)) + } + } + require.Equal(t, col.nullCount(), nullCnt) + require.Len(t, sel, col.length) + + for i := 0; i < 128; i++ { + if i%2 == 0 { + col.AppendNull() + } else { + col.AppendString(fmt.Sprintf("%v", i*i*i)) + } + } + + require.Len(t, sel, col.length-128) + require.Equal(t, nullCnt+128/2, col.nullCount()) + for i := 0; i < 128; i++ { + if i%2 == 0 { + require.True(t, col.IsNull(len(sel)+i)) + } else { + require.Equal(t, fmt.Sprintf("%v", i*i*i), col.GetString(len(sel)+i)) + require.False(t, col.IsNull(len(sel)+i)) + } + } +} + +func TestPreAllocInt64(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 128) + col.ResizeInt64(256, true) + i64s := col.Int64s() + require.Equal(t, 256, len(i64s)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendInt64(2333) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Int64s())) + require.Equal(t, int64(2333), col.Int64s()[256]) +} + +func TestPreAllocUint64(t *testing.T) { + tll := types.NewFieldType(mysql.TypeLonglong) + tll.AddFlag(mysql.UnsignedFlag) + col := NewColumn(tll, 128) + col.ResizeUint64(256, true) + u64s := col.Uint64s() + require.Equal(t, 256, len(u64s)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendUint64(2333) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Uint64s())) + require.Equal(t, uint64(2333), col.Uint64s()[256]) +} + +func TestPreAllocFloat32(t *testing.T) { + col := newFixedLenColumn(sizeFloat32, 128) + col.ResizeFloat32(256, true) + f32s := col.Float32s() + require.Equal(t, 256, len(f32s)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendFloat32(2333) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Float32s())) + require.Equal(t, float32(2333), col.Float32s()[256]) +} + +func TestPreAllocFloat64(t *testing.T) { + col := newFixedLenColumn(sizeFloat64, 128) + col.ResizeFloat64(256, true) + f64s := col.Float64s() + require.Equal(t, 256, len(f64s)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendFloat64(2333) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Float64s())) + require.Equal(t, float64(2333), col.Float64s()[256]) +} + +func TestPreAllocDecimal(t *testing.T) { + col := newFixedLenColumn(sizeMyDecimal, 128) + col.ResizeDecimal(256, true) + ds := col.Decimals() + require.Equal(t, 256, len(ds)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendMyDecimal(new(types.MyDecimal)) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Float64s())) +} + +func TestPreAllocTime(t *testing.T) { + col := newFixedLenColumn(sizeTime, 128) + col.ResizeTime(256, true) + ds := col.Times() + require.Equal(t, 256, len(ds)) + for i := 0; i < 256; i++ { + require.True(t, col.IsNull(i)) + } + col.AppendTime(types.ZeroDatetime) + require.False(t, col.IsNull(256)) + require.Equal(t, 257, len(col.Times())) +} + +func TestNull(t *testing.T) { + col := newFixedLenColumn(sizeFloat64, 32) + col.ResizeFloat64(1024, true) + require.Equal(t, 1024, col.nullCount()) + + notNulls := make(map[int]struct{}) + for i := 0; i < 512; i++ { + idx := rand.Intn(1024) + notNulls[idx] = struct{}{} + col.SetNull(idx, false) + } + + require.Equal(t, 1024-len(notNulls), col.nullCount()) + for idx := range notNulls { + require.False(t, col.IsNull(idx)) + } + + col.ResizeFloat64(8, true) + col.SetNulls(0, 8, true) + col.SetNull(7, false) + require.Equal(t, 7, col.nullCount()) + + col.ResizeFloat64(8, true) + col.SetNulls(0, 8, true) + require.Equal(t, 8, col.nullCount()) + + col.ResizeFloat64(9, true) + col.SetNulls(0, 9, true) + col.SetNull(8, false) + require.Equal(t, 8, col.nullCount()) +} + +func TestSetNulls(t *testing.T) { + col := newFixedLenColumn(sizeFloat64, 32) + col.ResizeFloat64(1024, true) + require.Equal(t, 1024, col.nullCount()) + + col.SetNulls(0, 1024, false) + require.Zero(t, col.nullCount()) + + nullMap := make(map[int]struct{}) + for i := 0; i < 100; i++ { + begin := rand.Intn(1024) + l := rand.Intn(37) + end := begin + l + if end > 1024 { + end = 1024 + } + for i := begin; i < end; i++ { + nullMap[i] = struct{}{} + } + col.SetNulls(begin, end, true) + + require.Len(t, nullMap, col.nullCount()) + for k := range nullMap { + require.True(t, col.IsNull(k)) + } + } +} + +func TestResizeReserve(t *testing.T) { + cI64s := newFixedLenColumn(sizeInt64, 0) + require.Zero(t, cI64s.length) + for i := 0; i < 100; i++ { + n := rand.Intn(1024) + cI64s.ResizeInt64(n, true) + require.Equal(t, n, cI64s.length) + require.Equal(t, n, len(cI64s.Int64s())) + } + cI64s.ResizeInt64(0, true) + require.Zero(t, cI64s.length) + require.Zero(t, len(cI64s.Int64s())) + + cStrs := newVarLenColumn(0) + for i := 0; i < 100; i++ { + n := rand.Intn(1024) + cStrs.ReserveString(n) + require.Zero(t, cStrs.length) + } + cStrs.ReserveString(0) + require.Zero(t, cStrs.length) +} + +func TestGetRaw(t *testing.T) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeFloat)}, 1024) + col := chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendFloat32(float32(i)) + } + it := NewIterator4Chunk(chk) + var i int + for row := it.Begin(); row != it.End(); row = it.Next() { + f := float32(i) + b := (*[unsafe.Sizeof(f)]byte)(unsafe.Pointer(&f))[:] + require.Equal(t, b, row.GetRaw(0)) + require.Equal(t, b, col.GetRaw(i)) + i++ + } + + chk = NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeVarString)}, 1024) + col = chk.Column(0) + for i := 0; i < 1024; i++ { + col.AppendString(fmt.Sprint(i)) + } + it = NewIterator4Chunk(chk) + i = 0 + for row := it.Begin(); row != it.End(); row = it.Next() { + require.Equal(t, []byte(fmt.Sprint(i)), row.GetRaw(0)) + require.Equal(t, []byte(fmt.Sprint(i)), col.GetRaw(i)) + i++ + } +} + +func TestResize(t *testing.T) { + col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + for i := 0; i < 1024; i++ { + col.AppendInt64(int64(i)) + } + col.ResizeInt64(1024, false) + for i := 0; i < 1024; i++ { + require.Equal(t, int64(0), col.Int64s()[i]) + } + + col = NewColumn(types.NewFieldType(mysql.TypeFloat), 1024) + for i := 0; i < 1024; i++ { + col.AppendFloat32(float32(i)) + } + col.ResizeFloat32(1024, false) + for i := 0; i < 1024; i++ { + require.Equal(t, float32(0), col.Float32s()[i]) + } + + col = NewColumn(types.NewFieldType(mysql.TypeDouble), 1024) + for i := 0; i < 1024; i++ { + col.AppendFloat64(float64(i)) + } + col.ResizeFloat64(1024, false) + for i := 0; i < 1024; i++ { + require.Equal(t, float64(0), col.Float64s()[i]) + } + + col = NewColumn(types.NewFieldType(mysql.TypeNewDecimal), 1024) + for i := 0; i < 1024; i++ { + col.AppendMyDecimal(new(types.MyDecimal).FromInt(int64(i))) + } + col.ResizeDecimal(1024, false) + for i := 0; i < 1024; i++ { + var d types.MyDecimal + require.Equal(t, d, col.Decimals()[i]) + } + + col = NewColumn(types.NewFieldType(mysql.TypeDuration), 1024) + for i := 0; i < 1024; i++ { + col.AppendDuration(types.Duration{Duration: time.Duration(i), Fsp: i}) + } + col.ResizeGoDuration(1024, false) + for i := 0; i < 1024; i++ { + require.Equal(t, time.Duration(0), col.GoDurations()[i]) + } + + col = NewColumn(types.NewFieldType(mysql.TypeDatetime), 1024) + for i := 0; i < 1024; i++ { + gt := types.FromDate(rand.Intn(2200), rand.Intn(10)+1, rand.Intn(20)+1, rand.Intn(12), rand.Intn(60), rand.Intn(60), rand.Intn(1000000)) + t := types.NewTime(gt, 0, 0) + col.AppendTime(t) + } + col.ResizeTime(1024, false) + for i := 0; i < 1024; i++ { + var time types.Time + require.Equal(t, time, col.Times()[i]) + } +} + +func BenchmarkDurationRow(b *testing.B) { + chk1 := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) + col1 := chk1.Column(0) + for i := 0; i < 1024; i++ { + col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) + } + chk2 := chk1.CopyConstruct() + result := chk1.CopyConstruct() + + b.ResetTimer() + for k := 0; k < b.N; k++ { + result.Reset() + it1 := NewIterator4Chunk(chk1) + it2 := NewIterator4Chunk(chk2) + for r1, r2 := it1.Begin(), it2.Begin(); r1 != it1.End() && r2 != it2.End(); r1, r2 = it1.Next(), it2.Next() { + d1 := r1.GetDuration(0, 0) + d2 := r2.GetDuration(0, 0) + r, err := d1.Add(d2) + if err != nil { + b.Fatal(err) + } + result.AppendDuration(0, r) + } + } +} + +func BenchmarkDurationVec(b *testing.B) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) + col1 := chk.Column(0) + for i := 0; i < 1024; i++ { + col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) + } + col2 := col1.CopyConstruct(nil) + result := col1.CopyConstruct(nil) + + ds1 := col1.GoDurations() + ds2 := col2.GoDurations() + rs := result.GoDurations() + + b.ResetTimer() + for k := 0; k < b.N; k++ { + result.ResizeGoDuration(1024, true) + for i := 0; i < 1024; i++ { + d1 := types.Duration{Duration: ds1[i]} + d2 := types.Duration{Duration: ds2[i]} + r, err := d1.Add(d2) + if err != nil { + b.Fatal(err) + } + rs[i] = r.Duration + } + } +} + +func BenchmarkTimeRow(b *testing.B) { + chk1 := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDate)}, 1024) + col1 := chk1.Column(0) + for i := 0; i < 1024; i++ { + col1.AppendTime(types.ZeroDate) + } + chk2 := chk1.CopyConstruct() + result := chk1.CopyConstruct() + + b.ResetTimer() + for k := 0; k < b.N; k++ { + result.Reset() + it1 := NewIterator4Chunk(chk1) + it2 := NewIterator4Chunk(chk2) + for r1, r2 := it1.Begin(), it2.Begin(); r1 != it1.End() && r2 != it2.End(); r1, r2 = it1.Next(), it2.Next() { + d1 := r1.GetTime(0) + d2 := r2.GetTime(0) + if r := d1.Compare(d2); r > 0 { + result.AppendTime(0, d1) + } else { + result.AppendTime(0, d2) + } + } + } +} + +func BenchmarkTimeVec(b *testing.B) { + chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDate)}, 1024) + col1 := chk.Column(0) + for i := 0; i < 1024; i++ { + col1.AppendTime(types.ZeroDate) + } + col2 := col1.CopyConstruct(nil) + result := col1.CopyConstruct(nil) + + ds1 := col1.Times() + ds2 := col2.Times() + rs := result.Times() + + b.ResetTimer() + for k := 0; k < b.N; k++ { + result.ResizeTime(1024, true) + for i := 0; i < 1024; i++ { + if r := ds1[i].Compare(ds2[i]); r > 0 { + rs[i] = ds1[i] + } else { + rs[i] = ds2[i] + } + } + } +} + +func genNullCols(n int) []*Column { + cols := make([]*Column, n) + for i := range cols { + cols[i] = NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) + cols[i].ResizeInt64(1024, false) + for j := 0; j < 1024; j++ { + if rand.Intn(10) < 5 { + cols[i].SetNull(j, true) + } + } + } + return cols +} + +func TestVectorizedNulls(t *testing.T) { + for i := 0; i < 256; i++ { + cols := genNullCols(4) + lCol, rCol := cols[0], cols[1] + vecResult, rowResult := cols[2], cols[3] + vecResult.SetNulls(0, 1024, false) + rowResult.SetNulls(0, 1024, false) + vecResult.MergeNulls(lCol, rCol) + for i := 0; i < 1024; i++ { + rowResult.SetNull(i, lCol.IsNull(i) || rCol.IsNull(i)) + } + + for i := 0; i < 1024; i++ { + require.Equal(t, vecResult.IsNull(i), rowResult.IsNull(i)) + } + } +} + +func TestResetColumn(t *testing.T) { + col0 := NewColumn(types.NewFieldType(mysql.TypeVarString), 0) + col1 := NewColumn(types.NewFieldType(mysql.TypeLonglong), 0) + + // using col0.reset() here will cause panic since it doesn't reset the elemBuf field which + // is used by MergeNulls. + col0.Reset(types.ETInt) + col0.MergeNulls(col1) + + col := NewColumn(types.NewFieldType(mysql.TypeDatetime), 0) + col.Reset(types.ETDuration) + col.AppendDuration(types.Duration{}) + // using col.reset() above will let this assertion fail since the length of initialized elemBuf + // is sizeTime. + require.Equal(t, sizeGoDuration, len(col.data)) +} + +func BenchmarkMergeNullsVectorized(b *testing.B) { + cols := genNullCols(3) + b.ResetTimer() + for i := 0; i < b.N; i++ { + cols[0].MergeNulls(cols[1:]...) + } +} + +func BenchmarkMergeNullsNonVectorized(b *testing.B) { + cols := genNullCols(3) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for i := 0; i < 1024; i++ { + cols[0].SetNull(i, cols[1].IsNull(i) || cols[2].IsNull(i)) + } + } +} + +func TestColumnResizeInt64(t *testing.T) { + var col = NewColumn(types.NewFieldType(mysql.TypeLonglong), 2) + col.AppendUint64(11) + col.AppendUint64(11) + + col.ResizeInt64(4, false) + require.Equal(t, col.nullBitmap, []byte{0b1111}) + col.AppendUint64(11) + require.Equal(t, col.nullBitmap, []byte{0b11111}) + col.AppendNull() + require.Equal(t, col.nullBitmap, []byte{0b011111}) + + col.ResizeUint64(11, false) + require.Equal(t, col.nullBitmap, []byte{0b11111111, 0b111}) + + col.ResizeUint64(7, true) + require.Equal(t, col.nullBitmap, []byte{0}) + + col.AppendUint64(32) + col.AppendUint64(32) + require.Equal(t, col.nullBitmap, []byte{0b10000000, 0b1}) +} diff --git a/pkg/util/chunk/compare.go b/pkg/util/chunk/compare.go new file mode 100644 index 0000000000000..b2f6f6bd0d0d4 --- /dev/null +++ b/pkg/util/chunk/compare.go @@ -0,0 +1,245 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "bytes" + "cmp" + "sort" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" +) + +// CompareFunc is a function to compare the two values in Row, the two columns must have the same type. +type CompareFunc = func(l Row, lCol int, r Row, rCol int) int + +// GetCompareFunc gets a compare function for the field type. +func GetCompareFunc(tp *types.FieldType) CompareFunc { + switch tp.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: + if mysql.HasUnsignedFlag(tp.GetFlag()) { + return cmpUint64 + } + return cmpInt64 + case mysql.TypeFloat: + return cmpFloat32 + case mysql.TypeDouble: + return cmpFloat64 + case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, + mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + return genCmpStringFunc(tp.GetCollate()) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + return cmpTime + case mysql.TypeDuration: + return cmpDuration + case mysql.TypeNewDecimal: + return cmpMyDecimal + case mysql.TypeSet, mysql.TypeEnum: + return cmpNameValue + case mysql.TypeBit: + return cmpBit + case mysql.TypeJSON: + return cmpJSON + } + return nil +} + +func cmpNull(lNull, rNull bool) int { + if lNull && rNull { + return 0 + } + if lNull { + return -1 + } + return 1 +} + +func cmpInt64(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + return cmp.Compare(l.GetInt64(lCol), r.GetInt64(rCol)) +} + +func cmpUint64(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + return cmp.Compare(l.GetUint64(lCol), r.GetUint64(rCol)) +} + +func genCmpStringFunc(collation string) func(l Row, lCol int, r Row, rCol int) int { + return func(l Row, lCol int, r Row, rCol int) int { + return cmpStringWithCollationInfo(l, lCol, r, rCol, collation) + } +} + +func cmpStringWithCollationInfo(l Row, lCol int, r Row, rCol int, collation string) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + return types.CompareString(l.GetString(lCol), r.GetString(rCol), collation) +} + +func cmpFloat32(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + return cmp.Compare(float64(l.GetFloat32(lCol)), float64(r.GetFloat32(rCol))) +} + +func cmpFloat64(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + return cmp.Compare(l.GetFloat64(lCol), r.GetFloat64(rCol)) +} + +func cmpMyDecimal(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + lDec, rDec := l.GetMyDecimal(lCol), r.GetMyDecimal(rCol) + return lDec.Compare(rDec) +} + +func cmpTime(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + lTime, rTime := l.GetTime(lCol), r.GetTime(rCol) + return lTime.Compare(rTime) +} + +func cmpDuration(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + lDur, rDur := l.GetDuration(lCol, 0).Duration, r.GetDuration(rCol, 0).Duration + return cmp.Compare(int64(lDur), int64(rDur)) +} + +func cmpNameValue(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + _, lVal := l.getNameValue(lCol) + _, rVal := r.getNameValue(rCol) + return cmp.Compare(lVal, rVal) +} + +func cmpBit(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + lBit := types.BinaryLiteral(l.GetBytes(lCol)) + rBit := types.BinaryLiteral(r.GetBytes(rCol)) + return lBit.Compare(rBit) +} + +func cmpJSON(l Row, lCol int, r Row, rCol int) int { + lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) + if lNull || rNull { + return cmpNull(lNull, rNull) + } + lJ, rJ := l.GetJSON(lCol), r.GetJSON(rCol) + return types.CompareBinaryJSON(lJ, rJ) +} + +// Compare compares the value with ad. +// We assume that the collation information of the column is the same with the datum. +func Compare(row Row, colIdx int, ad *types.Datum) int { + switch ad.Kind() { + case types.KindNull: + if row.IsNull(colIdx) { + return 0 + } + return 1 + case types.KindMinNotNull: + if row.IsNull(colIdx) { + return -1 + } + return 1 + case types.KindMaxValue: + return -1 + case types.KindInt64: + return cmp.Compare(row.GetInt64(colIdx), ad.GetInt64()) + case types.KindUint64: + return cmp.Compare(row.GetUint64(colIdx), ad.GetUint64()) + case types.KindFloat32: + return cmp.Compare(float64(row.GetFloat32(colIdx)), float64(ad.GetFloat32())) + case types.KindFloat64: + return cmp.Compare(row.GetFloat64(colIdx), ad.GetFloat64()) + case types.KindString: + return types.CompareString(row.GetString(colIdx), ad.GetString(), ad.Collation()) + case types.KindBytes, types.KindBinaryLiteral, types.KindMysqlBit: + return bytes.Compare(row.GetBytes(colIdx), ad.GetBytes()) + case types.KindMysqlDecimal: + l, r := row.GetMyDecimal(colIdx), ad.GetMysqlDecimal() + return l.Compare(r) + case types.KindMysqlDuration: + l, r := row.GetDuration(colIdx, 0).Duration, ad.GetMysqlDuration().Duration + return cmp.Compare(int64(l), int64(r)) + case types.KindMysqlEnum: + l, r := row.GetEnum(colIdx).Value, ad.GetMysqlEnum().Value + return cmp.Compare(l, r) + case types.KindMysqlSet: + l, r := row.GetSet(colIdx).Value, ad.GetMysqlSet().Value + return cmp.Compare(l, r) + case types.KindMysqlJSON: + l, r := row.GetJSON(colIdx), ad.GetMysqlJSON() + return types.CompareBinaryJSON(l, r) + case types.KindMysqlTime: + l, r := row.GetTime(colIdx), ad.GetMysqlTime() + return l.Compare(r) + default: + return 0 + } +} + +// LowerBound searches on the non-decreasing Column colIdx, +// returns the smallest index i such that the value at row i is not less than `d`. +func (c *Chunk) LowerBound(colIdx int, d *types.Datum) (index int, match bool) { + if Compare(c.GetRow(c.NumRows()-1), colIdx, d) < 0 { + return c.NumRows(), false + } + index = sort.Search(c.NumRows(), func(i int) bool { + cmp := Compare(c.GetRow(i), colIdx, d) + if cmp == 0 { + match = true + } + return cmp >= 0 + }) + return +} + +// UpperBound searches on the non-decreasing Column colIdx, +// returns the smallest index i such that the value at row i is larger than `d`. +func (c *Chunk) UpperBound(colIdx int, d *types.Datum) int { + return sort.Search(c.NumRows(), func(i int) bool { + return Compare(c.GetRow(i), colIdx, d) > 0 + }) +} diff --git a/util/chunk/disk.go b/pkg/util/chunk/disk.go similarity index 97% rename from util/chunk/disk.go rename to pkg/util/chunk/disk.go index 3c40e3861665b..fa36de5658a31 100644 --- a/util/chunk/disk.go +++ b/pkg/util/chunk/disk.go @@ -21,13 +21,13 @@ import ( "strconv" errors2 "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/checksum" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/encrypt" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/checksum" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/encrypt" + "github.com/pingcap/tidb/pkg/util/memory" ) // ListInDisk represents a slice of chunks storing in temporary disk. diff --git a/util/chunk/disk_test.go b/pkg/util/chunk/disk_test.go similarity index 98% rename from util/chunk/disk_test.go rename to pkg/util/chunk/disk_test.go index a7a115b318968..677a964b257b5 100644 --- a/util/chunk/disk_test.go +++ b/pkg/util/chunk/disk_test.go @@ -27,10 +27,10 @@ import ( "testing" errors2 "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/util/chunk/iterator.go b/pkg/util/chunk/iterator.go similarity index 100% rename from util/chunk/iterator.go rename to pkg/util/chunk/iterator.go diff --git a/util/chunk/iterator_test.go b/pkg/util/chunk/iterator_test.go similarity index 98% rename from util/chunk/iterator_test.go rename to pkg/util/chunk/iterator_test.go index 5537f9c325898..b764be28124ed 100644 --- a/util/chunk/iterator_test.go +++ b/pkg/util/chunk/iterator_test.go @@ -17,8 +17,8 @@ package chunk import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/chunk/list.go b/pkg/util/chunk/list.go new file mode 100644 index 0000000000000..53a52581e74cb --- /dev/null +++ b/pkg/util/chunk/list.go @@ -0,0 +1,179 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/memory" +) + +// List holds a slice of chunks, use to append rows with max chunk size properly handled. +type List struct { + fieldTypes []*types.FieldType + initChunkSize int + maxChunkSize int + length int + chunks []*Chunk + freelist []*Chunk + + memTracker *memory.Tracker // track memory usage. + consumedIdx int // chunk index in "chunks", has been consumed. +} + +// RowPtr is used to get a row from a list. +// It is only valid for the list that returns it. +type RowPtr struct { + ChkIdx uint32 + RowIdx uint32 +} + +// NewList creates a new List with field types, init chunk size and max chunk size. +func NewList(fieldTypes []*types.FieldType, initChunkSize, maxChunkSize int) *List { + l := &List{ + fieldTypes: fieldTypes, + initChunkSize: initChunkSize, + maxChunkSize: maxChunkSize, + memTracker: memory.NewTracker(memory.LabelForChunkList, -1), + consumedIdx: -1, + } + return l +} + +// GetMemTracker returns the memory tracker of this List. +func (l *List) GetMemTracker() *memory.Tracker { + return l.memTracker +} + +// Len returns the length of the List. +func (l *List) Len() int { + return l.length +} + +// NumChunks returns the number of chunks in the List. +func (l *List) NumChunks() int { + return len(l.chunks) +} + +// FieldTypes returns the fieldTypes of the list +func (l *List) FieldTypes() []*types.FieldType { + return l.fieldTypes +} + +// NumRowsOfChunk returns the number of rows of a chunk in the ListInDisk. +func (l *List) NumRowsOfChunk(chkID int) int { + return l.chunks[chkID].NumRows() +} + +// GetChunk gets the Chunk by ChkIdx. +func (l *List) GetChunk(chkIdx int) *Chunk { + return l.chunks[chkIdx] +} + +// AppendRow appends a row to the List, the row is copied to the List. +func (l *List) AppendRow(row Row) RowPtr { + chkIdx := len(l.chunks) - 1 + if chkIdx == -1 || l.chunks[chkIdx].NumRows() >= l.chunks[chkIdx].Capacity() || chkIdx == l.consumedIdx { + newChk := l.allocChunk() + l.chunks = append(l.chunks, newChk) + if chkIdx != l.consumedIdx { + l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage()) + l.consumedIdx = chkIdx + } + chkIdx++ + } + chk := l.chunks[chkIdx] + rowIdx := chk.NumRows() + chk.AppendRow(row) + l.length++ + return RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)} +} + +// Add adds a chunk to the List, the chunk may be modified later by the list. +// Caller must make sure the input chk is not empty and not used any more and has the same field types. +func (l *List) Add(chk *Chunk) { + // FixMe: we should avoid add a Chunk that chk.NumRows() > list.maxChunkSize. + if chk.NumRows() == 0 { + // TODO: return error here. + panic("chunk appended to List should have at least 1 row") + } + if chkIdx := len(l.chunks) - 1; l.consumedIdx != chkIdx { + l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage()) + l.consumedIdx = chkIdx + } + l.memTracker.Consume(chk.MemoryUsage()) + l.consumedIdx++ + l.chunks = append(l.chunks, chk) + l.length += chk.NumRows() +} + +func (l *List) allocChunk() (chk *Chunk) { + if len(l.freelist) > 0 { + lastIdx := len(l.freelist) - 1 + chk = l.freelist[lastIdx] + l.freelist = l.freelist[:lastIdx] + l.memTracker.Consume(-chk.MemoryUsage()) + chk.Reset() + return + } + if len(l.chunks) > 0 { + return Renew(l.chunks[len(l.chunks)-1], l.maxChunkSize) + } + return New(l.fieldTypes, l.initChunkSize, l.maxChunkSize) +} + +// GetRow gets a Row from the list by RowPtr. +func (l *List) GetRow(ptr RowPtr) Row { + chk := l.chunks[ptr.ChkIdx] + return chk.GetRow(int(ptr.RowIdx)) +} + +// Reset resets the List. +func (l *List) Reset() { + if lastIdx := len(l.chunks) - 1; lastIdx != l.consumedIdx { + l.memTracker.Consume(l.chunks[lastIdx].MemoryUsage()) + } + l.freelist = append(l.freelist, l.chunks...) + l.chunks = l.chunks[:0] + l.length = 0 + l.consumedIdx = -1 +} + +// Clear triggers GC for all the allocated chunks and reset the list +func (l *List) Clear() { + l.memTracker.Consume(-l.memTracker.BytesConsumed()) + l.freelist = nil + l.chunks = nil + l.length = 0 + l.consumedIdx = -1 +} + +// ListWalkFunc is used to walk the list. +// If error is returned, it will stop walking. +type ListWalkFunc = func(row Row) error + +// Walk iterate the list and call walkFunc for each row. +func (l *List) Walk(walkFunc ListWalkFunc) error { + for i := 0; i < len(l.chunks); i++ { + chk := l.chunks[i] + for j := 0; j < chk.NumRows(); j++ { + err := walkFunc(chk.GetRow(j)) + if err != nil { + return errors.Trace(err) + } + } + } + return nil +} diff --git a/util/chunk/list_test.go b/pkg/util/chunk/list_test.go similarity index 97% rename from util/chunk/list_test.go rename to pkg/util/chunk/list_test.go index 607b16c0d6ff5..df6c29fc3591b 100644 --- a/util/chunk/list_test.go +++ b/pkg/util/chunk/list_test.go @@ -20,9 +20,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/chunk/main_test.go b/pkg/util/chunk/main_test.go new file mode 100644 index 0000000000000..2aa418ca86456 --- /dev/null +++ b/pkg/util/chunk/main_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "os" + "testing" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + path, _ := os.MkdirTemp("", "tidb_enable_tmp_storage_on_oom") + config.UpdateGlobal(func(conf *config.Config) { + conf.TempStoragePath = path + }) + + goleak.VerifyTestMain(wrapper{ + M: m, + cleanUp: func() { os.RemoveAll(path) }, + }) +} + +// wrap *testing.M to do the clean up after m.Run() and before os.Exit() +type wrapper struct { + *testing.M + cleanUp func() +} + +func (m wrapper) Run() int { + defer m.cleanUp() + return m.M.Run() +} diff --git a/util/chunk/mutrow.go b/pkg/util/chunk/mutrow.go similarity index 98% rename from util/chunk/mutrow.go rename to pkg/util/chunk/mutrow.go index da274fa7931ad..275d7188aa10b 100644 --- a/util/chunk/mutrow.go +++ b/pkg/util/chunk/mutrow.go @@ -19,9 +19,9 @@ import ( "math" "unsafe" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" ) // MutRow represents a mutable Row. diff --git a/util/chunk/mutrow_test.go b/pkg/util/chunk/mutrow_test.go similarity index 97% rename from util/chunk/mutrow_test.go rename to pkg/util/chunk/mutrow_test.go index a69cfcd75fac3..82d0ce32adb82 100644 --- a/util/chunk/mutrow_test.go +++ b/pkg/util/chunk/mutrow_test.go @@ -18,10 +18,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/require" ) diff --git a/util/chunk/pool.go b/pkg/util/chunk/pool.go similarity index 98% rename from util/chunk/pool.go rename to pkg/util/chunk/pool.go index 4debe388157c2..f4fbcfbbcb07f 100644 --- a/util/chunk/pool.go +++ b/pkg/util/chunk/pool.go @@ -17,7 +17,7 @@ package chunk import ( "sync" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" ) // Pool is the Column pool. diff --git a/pkg/util/chunk/pool_test.go b/pkg/util/chunk/pool_test.go new file mode 100644 index 0000000000000..105e06ba3ac19 --- /dev/null +++ b/pkg/util/chunk/pool_test.go @@ -0,0 +1,110 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package chunk + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestNewPool(t *testing.T) { + pool := NewPool(1024) + require.Equal(t, 1024, pool.initCap) + require.NotNil(t, pool.varLenColPool) + require.NotNil(t, pool.fixLenColPool4) + require.NotNil(t, pool.fixLenColPool8) + require.NotNil(t, pool.fixLenColPool16) + require.NotNil(t, pool.fixLenColPool40) +} + +func TestPoolGetChunk(t *testing.T) { + initCap := 1024 + pool := NewPool(initCap) + + fieldTypes := []*types.FieldType{ + types.NewFieldType(mysql.TypeVarchar), + types.NewFieldType(mysql.TypeJSON), + types.NewFieldType(mysql.TypeFloat), + types.NewFieldType(mysql.TypeNewDecimal), + types.NewFieldType(mysql.TypeDouble), + types.NewFieldType(mysql.TypeLonglong), + // types.NewFieldType(mysql.TypeTimestamp), + // types.NewFieldType(mysql.TypeDatetime), + } + + chk := pool.GetChunk(fieldTypes) + require.NotNil(t, chk) + require.Len(t, fieldTypes, chk.NumCols()) + require.Nil(t, chk.columns[0].elemBuf) + require.Nil(t, chk.columns[1].elemBuf) + require.Equal(t, getFixedLen(fieldTypes[2]), len(chk.columns[2].elemBuf)) + require.Equal(t, getFixedLen(fieldTypes[3]), len(chk.columns[3].elemBuf)) + require.Equal(t, getFixedLen(fieldTypes[4]), len(chk.columns[4].elemBuf)) + require.Equal(t, getFixedLen(fieldTypes[5]), len(chk.columns[5].elemBuf)) + // require.Equal(t, getFixedLen(fieldTypes[6]), len(chk.columns[6].elemBuf)) + // require.Equal(t, getFixedLen(fieldTypes[7]), len(chk.columns[7].elemBuf)) + + require.Equal(t, initCap*getFixedLen(fieldTypes[2]), cap(chk.columns[2].data)) + require.Equal(t, initCap*getFixedLen(fieldTypes[3]), cap(chk.columns[3].data)) + require.Equal(t, initCap*getFixedLen(fieldTypes[4]), cap(chk.columns[4].data)) + require.Equal(t, initCap*getFixedLen(fieldTypes[5]), cap(chk.columns[5].data)) + // require.Equal(t, initCap*getFixedLen(fieldTypes[6]), cap(chk.columns[6].data)) + // require.Equal(t, initCap*getFixedLen(fieldTypes[7]), cap(chk.columns[7].data)) +} + +func TestPoolPutChunk(t *testing.T) { + initCap := 1024 + pool := NewPool(initCap) + + fieldTypes := []*types.FieldType{ + types.NewFieldType(mysql.TypeVarchar), + types.NewFieldType(mysql.TypeJSON), + types.NewFieldType(mysql.TypeFloat), + types.NewFieldType(mysql.TypeNewDecimal), + types.NewFieldType(mysql.TypeDouble), + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeTimestamp), + types.NewFieldType(mysql.TypeDatetime), + } + + chk := pool.GetChunk(fieldTypes) + pool.PutChunk(fieldTypes, chk) + require.Equal(t, 0, len(chk.columns)) +} + +func BenchmarkPoolChunkOperation(b *testing.B) { + pool := NewPool(1024) + + fieldTypes := []*types.FieldType{ + types.NewFieldType(mysql.TypeVarchar), + types.NewFieldType(mysql.TypeJSON), + types.NewFieldType(mysql.TypeFloat), + types.NewFieldType(mysql.TypeNewDecimal), + types.NewFieldType(mysql.TypeDouble), + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeTimestamp), + types.NewFieldType(mysql.TypeDatetime), + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + pool.PutChunk(fieldTypes, pool.GetChunk(fieldTypes)) + } + }) +} diff --git a/util/chunk/row.go b/pkg/util/chunk/row.go similarity index 98% rename from util/chunk/row.go rename to pkg/util/chunk/row.go index b2ee172f8a646..2b5e2a3376021 100644 --- a/util/chunk/row.go +++ b/pkg/util/chunk/row.go @@ -17,8 +17,8 @@ package chunk import ( "strconv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) // Row represents a row of data, can be used to access values. diff --git a/util/chunk/row_container.go b/pkg/util/chunk/row_container.go similarity index 99% rename from util/chunk/row_container.go rename to pkg/util/chunk/row_container.go index c6fc98dc57e74..97c268fccecfa 100644 --- a/util/chunk/row_container.go +++ b/pkg/util/chunk/row_container.go @@ -22,10 +22,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" "golang.org/x/sys/cpu" ) diff --git a/util/chunk/row_container_reader.go b/pkg/util/chunk/row_container_reader.go similarity index 98% rename from util/chunk/row_container_reader.go rename to pkg/util/chunk/row_container_reader.go index b96b20c6921c1..e0f3b0e314060 100644 --- a/util/chunk/row_container_reader.go +++ b/pkg/util/chunk/row_container_reader.go @@ -19,7 +19,7 @@ import ( "runtime" "sync" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" ) // RowContainerReader is a forward-only iterator for the row container. It provides an interface similar to other diff --git a/util/chunk/row_container_test.go b/pkg/util/chunk/row_container_test.go similarity index 96% rename from util/chunk/row_container_test.go rename to pkg/util/chunk/row_container_test.go index 381f174dfbb6c..1ef9c11684c88 100644 --- a/util/chunk/row_container_test.go +++ b/pkg/util/chunk/row_container_test.go @@ -22,10 +22,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) @@ -230,9 +230,9 @@ func TestSpillActionDeadLock(t *testing.T) { // Goroutine 2: ------------------> SpillDiskAction -> new Goroutine to spill -> ------------------ // new Goroutine created by 2: ---> rc.SpillToDisk (Lock) // In golang, RLock will be blocked after try to get Lock. So it will cause deadlock. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/chunk/testRowContainerDeadLock", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/chunk/testRowContainerDeadLock", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/chunk/testRowContainerDeadLock")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/chunk/testRowContainerDeadLock")) }() sz := 4 fields := []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)} @@ -457,9 +457,9 @@ func TestPanicWhenSpillToDisk(t *testing.T) { rc.actionSpill.WaitForTest() require.False(t, rc.AlreadySpilledSafeForTest()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/chunk/spillToDiskOutOfDiskQuota", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/chunk/spillToDiskOutOfDiskQuota", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/chunk/spillToDiskOutOfDiskQuota")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/chunk/spillToDiskOutOfDiskQuota")) }() require.NoError(t, rc.Add(chk)) rc.actionSpill.WaitForTest() @@ -493,9 +493,9 @@ func TestPanicDuringSortedRowContainerSpill(t *testing.T) { rc.actionSpill.WaitForTest() require.False(t, rc.AlreadySpilledSafeForTest()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/chunk/errorDuringSortRowContainer", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/chunk/errorDuringSortRowContainer", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/chunk/errorDuringSortRowContainer")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/chunk/errorDuringSortRowContainer")) }() err = rc.Add(chk) require.NoError(t, err) diff --git a/pkg/util/codec/BUILD.bazel b/pkg/util/codec/BUILD.bazel new file mode 100644 index 0000000000000..a9ebe3a5eb98a --- /dev/null +++ b/pkg/util/codec/BUILD.bazel @@ -0,0 +1,54 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "codec", + srcs = [ + "bytes.go", + "codec.go", + "decimal.go", + "float.go", + "number.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/codec", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/collate", + "//pkg/util/hack", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "codec_test", + timeout = "short", + srcs = [ + "bench_test.go", + "bytes_test.go", + "codec_test.go", + "collation_test.go", + "decimal_test.go", + "main_test.go", + ], + embed = [":codec"], + flaky = True, + deps = [ + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/collate", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/codec/bench_test.go b/pkg/util/codec/bench_test.go new file mode 100644 index 0000000000000..62cca52d5eb16 --- /dev/null +++ b/pkg/util/codec/bench_test.go @@ -0,0 +1,117 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package codec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/chunk" +) + +var valueCnt = 100 + +func composeEncodedData(size int) []byte { + values := make([]types.Datum, 0, size) + for i := 0; i < size; i++ { + values = append(values, types.NewDatum(i)) + } + bs, _ := EncodeValue(nil, nil, values...) + return bs +} + +func BenchmarkDecodeWithSize(b *testing.B) { + b.StopTimer() + bs := composeEncodedData(valueCnt) + b.StartTimer() + for i := 0; i < b.N; i++ { + _, err := Decode(bs, valueCnt) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkDecodeWithOutSize(b *testing.B) { + b.StopTimer() + bs := composeEncodedData(valueCnt) + b.StartTimer() + for i := 0; i < b.N; i++ { + _, err := Decode(bs, 1) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEncodeIntWithSize(b *testing.B) { + for i := 0; i < b.N; i++ { + data := make([]byte, 0, 8) + EncodeInt(data, 10) + } +} + +func BenchmarkEncodeIntWithOutSize(b *testing.B) { + for i := 0; i < b.N; i++ { + EncodeInt(nil, 10) + } +} + +func BenchmarkDecodeDecimal(b *testing.B) { + dec := &types.MyDecimal{} + err := dec.FromFloat64(1211.1211113) + if err != nil { + b.Fatal(err) + } + precision, frac := dec.PrecisionAndFrac() + raw, _ := EncodeDecimal([]byte{}, dec, precision, frac) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, _, _, _, err := DecodeDecimal(raw) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkDecodeOneToChunk(b *testing.B) { + str := new(types.Datum) + *str = types.NewStringDatum("a") + var raw []byte + raw = append(raw, bytesFlag) + raw = EncodeBytes(raw, str.GetBytes()) + intType := types.NewFieldType(mysql.TypeLonglong) + b.ResetTimer() + decoder := NewDecoder(chunk.New([]*types.FieldType{intType}, 32, 32), nil) + for i := 0; i < b.N; i++ { + _, err := decoder.DecodeOne(raw, 0, intType) + if err != nil { + b.Fatal(err) + } + } +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkDecodeWithSize, + BenchmarkDecodeWithOutSize, + BenchmarkEncodeIntWithSize, + BenchmarkEncodeIntWithOutSize, + BenchmarkDecodeDecimal, + BenchmarkDecodeOneToChunk, + ) +} diff --git a/util/codec/bytes.go b/pkg/util/codec/bytes.go similarity index 100% rename from util/codec/bytes.go rename to pkg/util/codec/bytes.go diff --git a/util/codec/bytes_test.go b/pkg/util/codec/bytes_test.go similarity index 100% rename from util/codec/bytes_test.go rename to pkg/util/codec/bytes_test.go diff --git a/pkg/util/codec/codec.go b/pkg/util/codec/codec.go new file mode 100644 index 0000000000000..1c9519280d8d4 --- /dev/null +++ b/pkg/util/codec/codec.go @@ -0,0 +1,1381 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package codec + +import ( + "bytes" + "encoding/binary" + "fmt" + "hash" + "io" + "time" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// First byte in the encoded value which specifies the encoding type. +const ( + NilFlag byte = 0 + bytesFlag byte = 1 + compactBytesFlag byte = 2 + intFlag byte = 3 + uintFlag byte = 4 + floatFlag byte = 5 + decimalFlag byte = 6 + durationFlag byte = 7 + varintFlag byte = 8 + uvarintFlag byte = 9 + jsonFlag byte = 10 + maxFlag byte = 250 +) + +const ( + sizeUint64 = unsafe.Sizeof(uint64(0)) + sizeFloat64 = unsafe.Sizeof(float64(0)) +) + +func preRealloc(b []byte, vals []types.Datum, comparable1 bool) []byte { + var size int + for i := range vals { + switch vals[i].Kind() { + case types.KindInt64, types.KindUint64, types.KindMysqlEnum, types.KindMysqlSet, types.KindMysqlBit, types.KindBinaryLiteral: + size += sizeInt(comparable1) + case types.KindString, types.KindBytes: + size += sizeBytes(vals[i].GetBytes(), comparable1) + case types.KindMysqlTime, types.KindMysqlDuration, types.KindFloat32, types.KindFloat64: + size += 9 + case types.KindNull, types.KindMinNotNull, types.KindMaxValue: + size++ + case types.KindMysqlJSON: + size += 2 + len(vals[i].GetBytes()) + case types.KindMysqlDecimal: + size += 1 + types.MyDecimalStructSize + default: + return b + } + } + return reallocBytes(b, size) +} + +// encode will encode a datum and append it to a byte slice. If comparable1 is true, the encoded bytes can be sorted as it's original order. +// If hash is true, the encoded bytes can be checked equal as it's original value. +func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparable1 bool) (_ []byte, err error) { + b = preRealloc(b, vals, comparable1) + for i, length := 0, len(vals); i < length; i++ { + switch vals[i].Kind() { + case types.KindInt64: + b = encodeSignedInt(b, vals[i].GetInt64(), comparable1) + case types.KindUint64: + b = encodeUnsignedInt(b, vals[i].GetUint64(), comparable1) + case types.KindFloat32, types.KindFloat64: + b = append(b, floatFlag) + b = EncodeFloat(b, vals[i].GetFloat64()) + case types.KindString: + b = encodeString(b, vals[i], comparable1) + case types.KindBytes: + b = encodeBytes(b, vals[i].GetBytes(), comparable1) + case types.KindMysqlTime: + b = append(b, uintFlag) + b, err = EncodeMySQLTime(sc, vals[i].GetMysqlTime(), mysql.TypeUnspecified, b) + if err != nil { + return b, err + } + case types.KindMysqlDuration: + // duration may have negative value, so we cannot use String to encode directly. + b = append(b, durationFlag) + b = EncodeInt(b, int64(vals[i].GetMysqlDuration().Duration)) + case types.KindMysqlDecimal: + b = append(b, decimalFlag) + b, err = EncodeDecimal(b, vals[i].GetMysqlDecimal(), vals[i].Length(), vals[i].Frac()) + if terror.ErrorEqual(err, types.ErrTruncated) { + err = sc.HandleTruncate(err) + } else if terror.ErrorEqual(err, types.ErrOverflow) { + err = sc.HandleOverflow(err, err) + } + case types.KindMysqlEnum: + b = encodeUnsignedInt(b, vals[i].GetMysqlEnum().Value, comparable1) + case types.KindMysqlSet: + b = encodeUnsignedInt(b, vals[i].GetMysqlSet().Value, comparable1) + case types.KindMysqlBit, types.KindBinaryLiteral: + // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. + var val uint64 + val, err = vals[i].GetBinaryLiteral().ToInt(sc) + terror.Log(errors.Trace(err)) + b = encodeUnsignedInt(b, val, comparable1) + case types.KindMysqlJSON: + b = append(b, jsonFlag) + j := vals[i].GetMysqlJSON() + b = append(b, j.TypeCode) + b = append(b, j.Value...) + case types.KindNull: + b = append(b, NilFlag) + case types.KindMinNotNull: + b = append(b, bytesFlag) + case types.KindMaxValue: + b = append(b, maxFlag) + default: + return b, errors.Errorf("unsupport encode type %d", vals[i].Kind()) + } + } + + return b, errors.Trace(err) +} + +// EstimateValueSize uses to estimate the value size of the encoded values. +func EstimateValueSize(sc *stmtctx.StatementContext, val types.Datum) (int, error) { + l := 0 + switch val.Kind() { + case types.KindInt64: + l = valueSizeOfSignedInt(val.GetInt64()) + case types.KindUint64: + l = valueSizeOfUnsignedInt(val.GetUint64()) + case types.KindFloat32, types.KindFloat64, types.KindMysqlTime, types.KindMysqlDuration: + l = 9 + case types.KindString, types.KindBytes: + l = valueSizeOfBytes(val.GetBytes()) + case types.KindMysqlDecimal: + var err error + l, err = valueSizeOfDecimal(val.GetMysqlDecimal(), val.Length(), val.Frac()) + if err != nil { + return 0, err + } + l = l + 1 + case types.KindMysqlEnum: + l = valueSizeOfUnsignedInt(val.GetMysqlEnum().Value) + case types.KindMysqlSet: + l = valueSizeOfUnsignedInt(val.GetMysqlSet().Value) + case types.KindMysqlBit, types.KindBinaryLiteral: + val, err := val.GetBinaryLiteral().ToInt(sc) + terror.Log(errors.Trace(err)) + l = valueSizeOfUnsignedInt(val) + case types.KindMysqlJSON: + l = 2 + len(val.GetMysqlJSON().Value) + case types.KindNull, types.KindMinNotNull, types.KindMaxValue: + l = 1 + default: + return l, errors.Errorf("unsupported encode type %d", val.Kind()) + } + return l, nil +} + +// EncodeMySQLTime encodes datum of `KindMysqlTime` to []byte. +func EncodeMySQLTime(sc *stmtctx.StatementContext, t types.Time, tp byte, b []byte) (_ []byte, err error) { + // Encoding timestamp need to consider timezone. If it's not in UTC, transform to UTC first. + // This is compatible with `PBToExpr > convertTime`, and coprocessor assumes the passed timestamp is in UTC as well. + if tp == mysql.TypeUnspecified { + tp = t.Type() + } + if tp == mysql.TypeTimestamp && sc.TimeZone() != time.UTC { + err = t.ConvertTimeZone(sc.TimeZone(), time.UTC) + if err != nil { + return nil, err + } + } + var v uint64 + v, err = t.ToPackedUint() + if err != nil { + return nil, err + } + b = EncodeUint(b, v) + return b, nil +} + +func encodeString(b []byte, val types.Datum, comparable1 bool) []byte { + if collate.NewCollationEnabled() && comparable1 { + return encodeBytes(b, collate.GetCollator(val.Collation()).Key(val.GetString()), true) + } + return encodeBytes(b, val.GetBytes(), comparable1) +} + +func encodeBytes(b []byte, v []byte, comparable1 bool) []byte { + if comparable1 { + b = append(b, bytesFlag) + b = EncodeBytes(b, v) + } else { + b = append(b, compactBytesFlag) + b = EncodeCompactBytes(b, v) + } + return b +} + +func valueSizeOfBytes(v []byte) int { + return valueSizeOfSignedInt(int64(len(v))) + len(v) +} + +func sizeBytes(v []byte, comparable1 bool) int { + if comparable1 { + reallocSize := (len(v)/encGroupSize + 1) * (encGroupSize + 1) + return 1 + reallocSize + } + reallocSize := binary.MaxVarintLen64 + len(v) + return 1 + reallocSize +} + +func encodeSignedInt(b []byte, v int64, comparable1 bool) []byte { + if comparable1 { + b = append(b, intFlag) + b = EncodeInt(b, v) + } else { + b = append(b, varintFlag) + b = EncodeVarint(b, v) + } + return b +} + +func valueSizeOfSignedInt(v int64) int { + if v < 0 { + v = 0 - v - 1 + } + // flag occupy 1 bit and at lease 1 bit. + size := 2 + v = v >> 6 + for v > 0 { + size++ + v = v >> 7 + } + return size +} + +func encodeUnsignedInt(b []byte, v uint64, comparable1 bool) []byte { + if comparable1 { + b = append(b, uintFlag) + b = EncodeUint(b, v) + } else { + b = append(b, uvarintFlag) + b = EncodeUvarint(b, v) + } + return b +} + +func valueSizeOfUnsignedInt(v uint64) int { + // flag occupy 1 bit and at lease 1 bit. + size := 2 + v = v >> 7 + for v > 0 { + size++ + v = v >> 7 + } + return size +} + +func sizeInt(comparable1 bool) int { + if comparable1 { + return 9 + } + return 1 + binary.MaxVarintLen64 +} + +// EncodeKey appends the encoded values to byte slice b, returns the appended +// slice. It guarantees the encoded value is in ascending order for comparison. +// For decimal type, datum must set datum's length and frac. +func EncodeKey(sc *stmtctx.StatementContext, b []byte, v ...types.Datum) ([]byte, error) { + return encode(sc, b, v, true) +} + +// EncodeValue appends the encoded values to byte slice b, returning the appended +// slice. It does not guarantee the order for comparison. +func EncodeValue(sc *stmtctx.StatementContext, b []byte, v ...types.Datum) ([]byte, error) { + return encode(sc, b, v, false) +} + +func encodeHashChunkRowIdx(sc *stmtctx.StatementContext, row chunk.Row, tp *types.FieldType, idx int) (flag byte, b []byte, err error) { + if row.IsNull(idx) { + flag = NilFlag + return + } + switch tp.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: + flag = uvarintFlag + if !mysql.HasUnsignedFlag(tp.GetFlag()) && row.GetInt64(idx) < 0 { + flag = varintFlag + } + b = row.GetRaw(idx) + case mysql.TypeFloat: + flag = floatFlag + f := float64(row.GetFloat32(idx)) + // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. + // It makes -0's hash val different from 0's. + if f == 0 { + f = 0 + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), unsafe.Sizeof(f)) + case mysql.TypeDouble: + flag = floatFlag + f := row.GetFloat64(idx) + // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. + // It makes -0's hash val different from 0's. + if f == 0 { + f = 0 + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), unsafe.Sizeof(f)) + case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + flag = compactBytesFlag + b = row.GetBytes(idx) + b = ConvertByCollation(b, tp) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + flag = uintFlag + t := row.GetTime(idx) + + var v uint64 + v, err = t.ToPackedUint() + if err != nil { + return + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), unsafe.Sizeof(v)) + case mysql.TypeDuration: + flag = durationFlag + // duration may have negative value, so we cannot use String to encode directly. + b = row.GetRaw(idx) + case mysql.TypeNewDecimal: + flag = decimalFlag + // If hash is true, we only consider the original value of this decimal and ignore it's precision. + dec := row.GetMyDecimal(idx) + b, err = dec.ToHashKey() + if err != nil { + return + } + case mysql.TypeEnum: + if mysql.HasEnumSetAsIntFlag(tp.GetFlag()) { + flag = uvarintFlag + v := row.GetEnum(idx).Value + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) + } else { + flag = compactBytesFlag + v := row.GetEnum(idx).Value + str := "" + if enum, err := types.ParseEnumValue(tp.GetElems(), v); err == nil { + // str will be empty string if v out of definition of enum. + str = enum.Name + } + b = ConvertByCollation(hack.Slice(str), tp) + } + case mysql.TypeSet: + flag = compactBytesFlag + s, err := types.ParseSetValue(tp.GetElems(), row.GetSet(idx).Value) + if err != nil { + return 0, nil, err + } + b = ConvertByCollation(hack.Slice(s.Name), tp) + case mysql.TypeBit: + // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. + flag = uvarintFlag + v, err1 := types.BinaryLiteral(row.GetBytes(idx)).ToInt(sc) + terror.Log(errors.Trace(err1)) + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), unsafe.Sizeof(v)) + case mysql.TypeJSON: + flag = jsonFlag + json := row.GetJSON(idx) + b = json.HashValue(b) + default: + return 0, nil, errors.Errorf("unsupport column type for encode %d", tp.GetType()) + } + return +} + +// HashChunkColumns writes the encoded value of each row's column, which of index `colIdx`, to h. +func HashChunkColumns(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk.Chunk, tp *types.FieldType, colIdx int, buf []byte, isNull []bool) (err error) { + return HashChunkSelected(sc, h, chk, tp, colIdx, buf, isNull, nil, false) +} + +// HashChunkSelected writes the encoded value of selected row's column, which of index `colIdx`, to h. +// sel indicates which rows are selected. If it is nil, all rows are selected. +func HashChunkSelected(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk.Chunk, tp *types.FieldType, colIdx int, buf []byte, + isNull, sel []bool, ignoreNull bool) (err error) { + var b []byte + column := chk.Column(colIdx) + rows := chk.NumRows() + switch tp.GetType() { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: + i64s := column.Int64s() + for i, v := range i64s { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = uvarintFlag + if !mysql.HasUnsignedFlag(tp.GetFlag()) && v < 0 { + buf[0] = varintFlag + } + b = column.GetRaw(i) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeFloat: + f32s := column.Float32s() + for i, f := range f32s { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = floatFlag + d := float64(f) + // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. + // It makes -0's hash val different from 0's. + if d == 0 { + d = 0 + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&d)), sizeFloat64) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeDouble: + f64s := column.Float64s() + for i := range f64s { + f := f64s[i] + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = floatFlag + // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. + // It makes -0's hash val different from 0's. + if f == 0 { + f = 0 + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), sizeFloat64) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = compactBytesFlag + b = column.GetBytes(i) + b = ConvertByCollation(b, tp) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + ts := column.Times() + for i, t := range ts { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = uintFlag + + var v uint64 + v, err = t.ToPackedUint() + if err != nil { + return + } + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeDuration: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = durationFlag + // duration may have negative value, so we cannot use String to encode directly. + b = column.GetRaw(i) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeNewDecimal: + ds := column.Decimals() + for i, d := range ds { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = decimalFlag + // If hash is true, we only consider the original value of this decimal and ignore it's precision. + b, err = d.ToHashKey() + if err != nil { + return + } + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeEnum: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else if mysql.HasEnumSetAsIntFlag(tp.GetFlag()) { + buf[0] = uvarintFlag + v := column.GetEnum(i).Value + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) + } else { + buf[0] = compactBytesFlag + v := column.GetEnum(i).Value + str := "" + if enum, err := types.ParseEnumValue(tp.GetElems(), v); err == nil { + // str will be empty string if v out of definition of enum. + str = enum.Name + } + b = ConvertByCollation(hack.Slice(str), tp) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeSet: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = compactBytesFlag + s, err := types.ParseSetValue(tp.GetElems(), column.GetSet(i).Value) + if err != nil { + return err + } + b = ConvertByCollation(hack.Slice(s.Name), tp) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeBit: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. + buf[0] = uvarintFlag + v, err1 := types.BinaryLiteral(column.GetBytes(i)).ToInt(sc) + terror.Log(errors.Trace(err1)) + b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) + } + + // As the golang doc described, `Hash.Write` never returns an error. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeJSON: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + if column.IsNull(i) { + buf[0], b = NilFlag, nil + isNull[i] = !ignoreNull + } else { + buf[0] = jsonFlag + json := column.GetJSON(i) + b = b[:0] + b = json.HashValue(b) + } + + // As the golang doc described, `Hash.Write` never returns an error.. + // See https://golang.org/pkg/hash/#Hash + _, _ = h[i].Write(buf) + _, _ = h[i].Write(b) + } + case mysql.TypeNull: + for i := 0; i < rows; i++ { + if sel != nil && !sel[i] { + continue + } + isNull[i] = !ignoreNull + buf[0] = NilFlag + _, _ = h[i].Write(buf) + } + default: + return errors.Errorf("unsupport column type for encode %d", tp.GetType()) + } + return +} + +// HashChunkRow writes the encoded values to w. +// If two rows are logically equal, it will generate the same bytes. +func HashChunkRow(sc *stmtctx.StatementContext, w io.Writer, row chunk.Row, allTypes []*types.FieldType, colIdx []int, buf []byte) (err error) { + var b []byte + for i, idx := range colIdx { + buf[0], b, err = encodeHashChunkRowIdx(sc, row, allTypes[i], idx) + if err != nil { + return errors.Trace(err) + } + _, err = w.Write(buf) + if err != nil { + return + } + _, err = w.Write(b) + if err != nil { + return + } + } + return err +} + +// EqualChunkRow returns a boolean reporting whether row1 and row2 +// with their types and column index are logically equal. +func EqualChunkRow(sc *stmtctx.StatementContext, + row1 chunk.Row, allTypes1 []*types.FieldType, colIdx1 []int, + row2 chunk.Row, allTypes2 []*types.FieldType, colIdx2 []int, +) (bool, error) { + if len(colIdx1) != len(colIdx2) { + return false, errors.Errorf("Internal error: Hash columns count mismatch, col1: %d, col2: %d", len(colIdx1), len(colIdx2)) + } + for i := range colIdx1 { + idx1, idx2 := colIdx1[i], colIdx2[i] + flag1, b1, err := encodeHashChunkRowIdx(sc, row1, allTypes1[i], idx1) + if err != nil { + return false, errors.Trace(err) + } + flag2, b2, err := encodeHashChunkRowIdx(sc, row2, allTypes2[i], idx2) + if err != nil { + return false, errors.Trace(err) + } + if !(flag1 == flag2 && bytes.Equal(b1, b2)) { + return false, nil + } + } + return true, nil +} + +// Decode decodes values from a byte slice generated with EncodeKey or EncodeValue +// before. +// size is the size of decoded datum slice. +func Decode(b []byte, size int) ([]types.Datum, error) { + if len(b) < 1 { + return nil, errors.New("invalid encoded key") + } + + var ( + err error + values = make([]types.Datum, 0, size) + ) + + for len(b) > 0 { + var d types.Datum + b, d, err = DecodeOne(b) + if err != nil { + return nil, errors.Trace(err) + } + + values = append(values, d) + } + + return values, nil +} + +// DecodeRange decodes the range values from a byte slice that generated by EncodeKey. +// It handles some special values like `MinNotNull` and `MaxValueDatum`. +// loc can be nil and only used in when the corresponding type is `mysql.TypeTimestamp`. +func DecodeRange(b []byte, size int, idxColumnTypes []byte, loc *time.Location) ([]types.Datum, []byte, error) { + if len(b) < 1 { + return nil, b, errors.New("invalid encoded key: length of key is zero") + } + + var ( + err error + values = make([]types.Datum, 0, size) + ) + + i := 0 + for len(b) > 1 { + var d types.Datum + if idxColumnTypes == nil { + b, d, err = DecodeOne(b) + } else { + if i >= len(idxColumnTypes) { + return values, b, errors.New("invalid length of index's columns") + } + if types.IsTypeTime(idxColumnTypes[i]) { + // handle datetime values specially since they are encoded to int and we'll get int values if using DecodeOne. + b, d, err = DecodeAsDateTime(b, idxColumnTypes[i], loc) + } else if types.IsTypeFloat(idxColumnTypes[i]) { + b, d, err = DecodeAsFloat32(b, idxColumnTypes[i]) + } else { + b, d, err = DecodeOne(b) + } + } + if err != nil { + return values, b, errors.Trace(err) + } + values = append(values, d) + i++ + } + + if len(b) == 1 { + switch b[0] { + case NilFlag: + values = append(values, types.Datum{}) + case bytesFlag: + values = append(values, types.MinNotNullDatum()) + // `maxFlag + 1` for PrefixNext + case maxFlag, maxFlag + 1: + values = append(values, types.MaxValueDatum()) + default: + return values, b, errors.Errorf("invalid encoded key flag %v", b[0]) + } + } + return values, nil, nil +} + +// DecodeOne decodes on datum from a byte slice generated with EncodeKey or EncodeValue. +func DecodeOne(b []byte) (remain []byte, d types.Datum, err error) { + if len(b) < 1 { + return nil, d, errors.New("invalid encoded key") + } + flag := b[0] + b = b[1:] + switch flag { + case intFlag: + var v int64 + b, v, err = DecodeInt(b) + d.SetInt64(v) + case uintFlag: + var v uint64 + b, v, err = DecodeUint(b) + d.SetUint64(v) + case varintFlag: + var v int64 + b, v, err = DecodeVarint(b) + d.SetInt64(v) + case uvarintFlag: + var v uint64 + b, v, err = DecodeUvarint(b) + d.SetUint64(v) + case floatFlag: + var v float64 + b, v, err = DecodeFloat(b) + d.SetFloat64(v) + case bytesFlag: + var v []byte + b, v, err = DecodeBytes(b, nil) + d.SetBytes(v) + case compactBytesFlag: + var v []byte + b, v, err = DecodeCompactBytes(b) + d.SetBytes(v) + case decimalFlag: + var ( + dec *types.MyDecimal + precision, frac int + ) + b, dec, precision, frac, err = DecodeDecimal(b) + if err == nil { + d.SetMysqlDecimal(dec) + d.SetLength(precision) + d.SetFrac(frac) + } + case durationFlag: + var r int64 + b, r, err = DecodeInt(b) + if err == nil { + // use max fsp, let outer to do round manually. + v := types.Duration{Duration: time.Duration(r), Fsp: types.MaxFsp} + d.SetMysqlDuration(v) + } + case jsonFlag: + var size int + size, err = types.PeekBytesAsJSON(b) + if err != nil { + return b, d, err + } + j := types.BinaryJSON{TypeCode: b[0], Value: b[1:size]} + d.SetMysqlJSON(j) + b = b[size:] + case NilFlag: + default: + return b, d, errors.Errorf("invalid encoded key flag %v", flag) + } + if err != nil { + return b, d, errors.Trace(err) + } + return b, d, nil +} + +// DecodeAsDateTime decodes on datum from []byte of `KindMysqlTime`. +func DecodeAsDateTime(b []byte, tp byte, loc *time.Location) (remain []byte, d types.Datum, err error) { + if len(b) < 1 { + return nil, d, errors.New("invalid encoded key") + } + flag := b[0] + b = b[1:] + var v uint64 + switch flag { + case uintFlag: + b, v, err = DecodeUint(b) + case uvarintFlag: + // Datetime can be encoded as Uvarint + b, v, err = DecodeUvarint(b) + + default: + return b, d, errors.Errorf("invalid encoded key flag %v", flag) + } + if err != nil { + return b, d, err + } + t := types.NewTime(types.ZeroCoreTime, tp, 0) + err = t.FromPackedUint(v) + if err != nil { + return b, d, errors.Trace(err) + } + if tp == mysql.TypeTimestamp && !t.IsZero() && loc != nil { + err = t.ConvertTimeZone(time.UTC, loc) + if err != nil { + return b, d, err + } + } + d.SetMysqlTime(t) + return b, d, nil +} + +// DecodeAsFloat32 decodes value for mysql.TypeFloat +func DecodeAsFloat32(b []byte, tp byte) (remain []byte, d types.Datum, err error) { + if len(b) < 1 || tp != mysql.TypeFloat { + return nil, d, errors.New("invalid encoded key") + } + flag := b[0] + b = b[1:] + if flag != floatFlag { + return b, d, errors.Errorf("invalid encoded key flag %v for DecodeAsFloat32", flag) + } + var v float64 + b, v, err = DecodeFloat(b) + if err != nil { + return nil, d, err + } + d.SetFloat32FromF64(v) + return b, d, nil +} + +// CutOne cuts the first encoded value from b. +// It will return the first encoded item and the remains as byte slice. +func CutOne(b []byte) (data []byte, remain []byte, err error) { + l, err := peek(b) + if err != nil { + return nil, nil, errors.Trace(err) + } + return b[:l], b[l:], nil +} + +// CutColumnID cuts the column ID from b. +// It will return the remains as byte slice and column ID +func CutColumnID(b []byte) (remain []byte, n int64, err error) { + if len(b) < 1 { + return nil, 0, errors.New("invalid encoded key") + } + // skip the flag + b = b[1:] + return DecodeVarint(b) +} + +// SetRawValues set raw datum values from a row data. +func SetRawValues(data []byte, values []types.Datum) error { + for i := 0; i < len(values); i++ { + l, err := peek(data) + if err != nil { + return errors.Trace(err) + } + values[i].SetRaw(data[:l:l]) + data = data[l:] + } + return nil +} + +// peek peeks the first encoded value from b and returns its length. +func peek(b []byte) (length int, err error) { + originLength := len(b) + if len(b) < 1 { + return 0, errors.New("invalid encoded key") + } + flag := b[0] + length++ + b = b[1:] + var l int + switch flag { + case NilFlag: + case intFlag, uintFlag, floatFlag, durationFlag: + // Those types are stored in 8 bytes. + l = 8 + case bytesFlag: + l, err = peekBytes(b) + case compactBytesFlag: + l, err = peekCompactBytes(b) + case decimalFlag: + l, err = types.DecimalPeak(b) + case varintFlag: + l, err = peekVarint(b) + case uvarintFlag: + l, err = peekUvarint(b) + case jsonFlag: + l, err = types.PeekBytesAsJSON(b) + default: + return 0, errors.Errorf("invalid encoded key flag %v", flag) + } + if err != nil { + return 0, errors.Trace(err) + } + length += l + if length <= 0 { + return 0, errors.New("invalid encoded key") + } else if length > originLength { + return 0, errors.Errorf("invalid encoded key, "+ + "expected length: %d, actual length: %d", length, originLength) + } + return +} + +func peekBytes(b []byte) (int, error) { + offset := 0 + for { + if len(b) < offset+encGroupSize+1 { + return 0, errors.New("insufficient bytes to decode value") + } + // The byte slice is encoded into many groups. + // For each group, there are 8 bytes for data and 1 byte for marker. + marker := b[offset+encGroupSize] + padCount := encMarker - marker + offset += encGroupSize + 1 + // When padCount is not zero, it means we get the end of the byte slice. + if padCount != 0 { + break + } + } + return offset, nil +} + +func peekCompactBytes(b []byte) (int, error) { + // Get length. + v, n := binary.Varint(b) + vi := int(v) + if n < 0 { + return 0, errors.New("value larger than 64 bits") + } else if n == 0 { + return 0, errors.New("insufficient bytes to decode value") + } + if len(b) < vi+n { + return 0, errors.Errorf("insufficient bytes to decode value, expected length: %v", n) + } + return n + vi, nil +} + +func peekVarint(b []byte) (int, error) { + _, n := binary.Varint(b) + if n < 0 { + return 0, errors.New("value larger than 64 bits") + } + return n, nil +} + +func peekUvarint(b []byte) (int, error) { + _, n := binary.Uvarint(b) + if n < 0 { + return 0, errors.New("value larger than 64 bits") + } + return n, nil +} + +// Decoder is used to decode value to chunk. +type Decoder struct { + chk *chunk.Chunk + timezone *time.Location + + // buf is only used for DecodeBytes to avoid the cost of makeslice. + buf []byte +} + +// NewDecoder creates a Decoder. +func NewDecoder(chk *chunk.Chunk, timezone *time.Location) *Decoder { + return &Decoder{ + chk: chk, + timezone: timezone, + } +} + +// DecodeOne decodes one value to chunk and returns the remained bytes. +func (decoder *Decoder) DecodeOne(b []byte, colIdx int, ft *types.FieldType) (remain []byte, err error) { + if len(b) < 1 { + return nil, errors.New("invalid encoded key") + } + chk := decoder.chk + flag := b[0] + b = b[1:] + switch flag { + case intFlag: + var v int64 + b, v, err = DecodeInt(b) + if err != nil { + return nil, errors.Trace(err) + } + appendIntToChunk(v, chk, colIdx, ft) + case uintFlag: + var v uint64 + b, v, err = DecodeUint(b) + if err != nil { + return nil, errors.Trace(err) + } + err = appendUintToChunk(v, chk, colIdx, ft, decoder.timezone) + case varintFlag: + var v int64 + b, v, err = DecodeVarint(b) + if err != nil { + return nil, errors.Trace(err) + } + appendIntToChunk(v, chk, colIdx, ft) + case uvarintFlag: + var v uint64 + b, v, err = DecodeUvarint(b) + if err != nil { + return nil, errors.Trace(err) + } + err = appendUintToChunk(v, chk, colIdx, ft, decoder.timezone) + case floatFlag: + var v float64 + b, v, err = DecodeFloat(b) + if err != nil { + return nil, errors.Trace(err) + } + appendFloatToChunk(v, chk, colIdx, ft) + case bytesFlag: + b, decoder.buf, err = DecodeBytes(b, decoder.buf) + if err != nil { + return nil, errors.Trace(err) + } + chk.AppendBytes(colIdx, decoder.buf) + case compactBytesFlag: + var v []byte + b, v, err = DecodeCompactBytes(b) + if err != nil { + return nil, errors.Trace(err) + } + chk.AppendBytes(colIdx, v) + case decimalFlag: + var dec *types.MyDecimal + var frac int + b, dec, _, frac, err = DecodeDecimal(b) + if err != nil { + return nil, errors.Trace(err) + } + if ft.GetDecimal() != types.UnspecifiedLength && frac > ft.GetDecimal() { + to := new(types.MyDecimal) + err := dec.Round(to, ft.GetDecimal(), types.ModeHalfUp) + if err != nil { + return nil, errors.Trace(err) + } + dec = to + } + chk.AppendMyDecimal(colIdx, dec) + case durationFlag: + var r int64 + b, r, err = DecodeInt(b) + if err != nil { + return nil, errors.Trace(err) + } + v := types.Duration{Duration: time.Duration(r), Fsp: ft.GetDecimal()} + chk.AppendDuration(colIdx, v) + case jsonFlag: + var size int + size, err = types.PeekBytesAsJSON(b) + if err != nil { + return nil, errors.Trace(err) + } + chk.AppendJSON(colIdx, types.BinaryJSON{TypeCode: b[0], Value: b[1:size]}) + b = b[size:] + case NilFlag: + chk.AppendNull(colIdx) + default: + return nil, errors.Errorf("invalid encoded key flag %v", flag) + } + if err != nil { + return nil, errors.Trace(err) + } + return b, nil +} + +func appendIntToChunk(val int64, chk *chunk.Chunk, colIdx int, ft *types.FieldType) { + switch ft.GetType() { + case mysql.TypeDuration: + v := types.Duration{Duration: time.Duration(val), Fsp: ft.GetDecimal()} + chk.AppendDuration(colIdx, v) + default: + chk.AppendInt64(colIdx, val) + } +} + +func appendUintToChunk(val uint64, chk *chunk.Chunk, colIdx int, ft *types.FieldType, loc *time.Location) error { + switch ft.GetType() { + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + t := types.NewTime(types.ZeroCoreTime, ft.GetType(), ft.GetDecimal()) + var err error + err = t.FromPackedUint(val) + if err != nil { + return errors.Trace(err) + } + if ft.GetType() == mysql.TypeTimestamp && !t.IsZero() { + err = t.ConvertTimeZone(time.UTC, loc) + if err != nil { + return errors.Trace(err) + } + } + chk.AppendTime(colIdx, t) + case mysql.TypeEnum: + // ignore error deliberately, to read empty enum value. + enum, err := types.ParseEnumValue(ft.GetElems(), val) + if err != nil { + enum = types.Enum{} + } + chk.AppendEnum(colIdx, enum) + case mysql.TypeSet: + set, err := types.ParseSetValue(ft.GetElems(), val) + if err != nil { + return errors.Trace(err) + } + chk.AppendSet(colIdx, set) + case mysql.TypeBit: + byteSize := (ft.GetFlen() + 7) >> 3 + chk.AppendBytes(colIdx, types.NewBinaryLiteralFromUint(val, byteSize)) + default: + chk.AppendUint64(colIdx, val) + } + return nil +} + +func appendFloatToChunk(val float64, chk *chunk.Chunk, colIdx int, ft *types.FieldType) { + if ft.GetType() == mysql.TypeFloat { + chk.AppendFloat32(colIdx, float32(val)) + } else { + chk.AppendFloat64(colIdx, val) + } +} + +// HashGroupKey encodes each row of this column and append encoded data into buf. +// Only use in the aggregate executor. +func HashGroupKey(sc *stmtctx.StatementContext, n int, col *chunk.Column, buf [][]byte, ft *types.FieldType) ([][]byte, error) { + var err error + switch ft.EvalType() { + case types.ETInt: + i64s := col.Int64s() + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = encodeSignedInt(buf[i], i64s[i], false) + } + } + case types.ETReal: + f64s := col.Float64s() + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = append(buf[i], floatFlag) + buf[i] = EncodeFloat(buf[i], f64s[i]) + } + } + case types.ETDecimal: + ds := col.Decimals() + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = append(buf[i], decimalFlag) + buf[i], err = EncodeDecimal(buf[i], &ds[i], ft.GetFlen(), ft.GetDecimal()) + if terror.ErrorEqual(err, types.ErrTruncated) { + err = sc.HandleTruncate(err) + } else if terror.ErrorEqual(err, types.ErrOverflow) { + err = sc.HandleOverflow(err, err) + } + if err != nil { + return nil, err + } + } + } + case types.ETDatetime, types.ETTimestamp: + ts := col.Times() + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = append(buf[i], uintFlag) + buf[i], err = EncodeMySQLTime(sc, ts[i], mysql.TypeUnspecified, buf[i]) + if err != nil { + return nil, err + } + } + } + case types.ETDuration: + ds := col.GoDurations() + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = append(buf[i], durationFlag) + buf[i] = EncodeInt(buf[i], int64(ds[i])) + } + } + case types.ETJson: + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = append(buf[i], jsonFlag) + buf[i] = col.GetJSON(i).HashValue(buf[i]) + } + } + case types.ETString: + for i := 0; i < n; i++ { + if col.IsNull(i) { + buf[i] = append(buf[i], NilFlag) + } else { + buf[i] = encodeBytes(buf[i], ConvertByCollation(col.GetBytes(i), ft), false) + } + } + default: + return nil, fmt.Errorf("invalid eval type %v", ft.EvalType()) + } + return buf, nil +} + +// ConvertByCollation converts these bytes according to its collation. +func ConvertByCollation(raw []byte, tp *types.FieldType) []byte { + collator := collate.GetCollator(tp.GetCollate()) + return collator.Key(string(hack.String(raw))) +} + +// ConvertByCollationStr converts this string according to its collation. +func ConvertByCollationStr(str string, tp *types.FieldType) string { + collator := collate.GetCollator(tp.GetCollate()) + return string(hack.String(collator.Key(str))) +} + +// HashCode encodes a Datum into a unique byte slice. +// It is mostly the same as EncodeValue, but it doesn't contain truncation or verification logic in order to make the encoding lossless. +func HashCode(b []byte, d types.Datum) []byte { + switch d.Kind() { + case types.KindInt64: + b = encodeSignedInt(b, d.GetInt64(), false) + case types.KindUint64: + b = encodeUnsignedInt(b, d.GetUint64(), false) + case types.KindFloat32, types.KindFloat64: + b = append(b, floatFlag) + b = EncodeFloat(b, d.GetFloat64()) + case types.KindString: + b = encodeString(b, d, false) + case types.KindBytes: + b = encodeBytes(b, d.GetBytes(), false) + case types.KindMysqlTime: + b = append(b, uintFlag) + t := d.GetMysqlTime().CoreTime() + b = encodeUnsignedInt(b, uint64(t), true) + case types.KindMysqlDuration: + // duration may have negative value, so we cannot use String to encode directly. + b = append(b, durationFlag) + b = EncodeInt(b, int64(d.GetMysqlDuration().Duration)) + case types.KindMysqlDecimal: + b = append(b, decimalFlag) + decStr := d.GetMysqlDecimal().ToString() + b = encodeBytes(b, decStr, false) + case types.KindMysqlEnum: + b = encodeUnsignedInt(b, d.GetMysqlEnum().Value, false) + case types.KindMysqlSet: + b = encodeUnsignedInt(b, d.GetMysqlSet().Value, false) + case types.KindMysqlBit, types.KindBinaryLiteral: + val := d.GetBinaryLiteral() + b = encodeBytes(b, val, false) + case types.KindMysqlJSON: + b = append(b, jsonFlag) + j := d.GetMysqlJSON() + b = append(b, j.TypeCode) + b = append(b, j.Value...) + case types.KindNull: + b = append(b, NilFlag) + case types.KindMinNotNull: + b = append(b, bytesFlag) + case types.KindMaxValue: + b = append(b, maxFlag) + default: + logutil.BgLogger().Warn("trying to calculate HashCode of an unexpected type of Datum", + zap.Uint8("Datum Kind", d.Kind()), + zap.Stack("stack")) + } + return b +} diff --git a/pkg/util/codec/codec_test.go b/pkg/util/codec/codec_test.go new file mode 100644 index 0000000000000..f4931c8529013 --- /dev/null +++ b/pkg/util/codec/codec_test.go @@ -0,0 +1,1300 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +package codec + +import ( + "bytes" + "fmt" + "hash" + "hash/crc32" + "hash/fnv" + "math" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/stretchr/testify/require" +) + +func TestCodecKey(t *testing.T) { + table := []struct { + Input []types.Datum + Expect []types.Datum + }{ + { + types.MakeDatums(int64(1)), + types.MakeDatums(int64(1)), + }, + + { + types.MakeDatums(float32(1), float64(3.15), []byte("123"), "123"), + types.MakeDatums(float64(1), float64(3.15), []byte("123"), []byte("123")), + }, + { + types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), + types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), + }, + + { + types.MakeDatums(true, false), + types.MakeDatums(int64(1), int64(0)), + }, + + { + types.MakeDatums(nil), + types.MakeDatums(nil), + }, + + { + types.MakeDatums(types.NewBinaryLiteralFromUint(100, -1), types.NewBinaryLiteralFromUint(100, 4)), + types.MakeDatums(uint64(100), uint64(100)), + }, + + { + types.MakeDatums(types.Enum{Name: "a", Value: 1}, types.Set{Name: "a", Value: 1}), + types.MakeDatums(uint64(1), uint64(1)), + }, + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for i, datums := range table { + comment := fmt.Sprintf("%d %v", i, datums) + b, err := EncodeKey(sc, nil, datums.Input...) + require.NoError(t, err, comment) + + args, err := Decode(b, 1) + require.NoError(t, err, comment) + require.Equal(t, datums.Expect, args, comment) + + b, err = EncodeValue(sc, nil, datums.Input...) + require.NoError(t, err, comment) + + size, err := estimateValuesSize(sc, datums.Input) + require.NoError(t, err, comment) + require.Len(t, b, size, comment) + + args, err = Decode(b, 1) + require.NoError(t, err, comment) + require.Equal(t, datums.Expect, args, comment) + } + + var raw types.Datum + raw.SetRaw([]byte("raw")) + _, err := EncodeKey(sc, nil, raw) + require.Error(t, err) +} + +func estimateValuesSize(sc *stmtctx.StatementContext, vals []types.Datum) (int, error) { + size := 0 + for _, val := range vals { + length, err := EstimateValueSize(sc, val) + if err != nil { + return 0, err + } + size += length + } + return size, nil +} + +func TestCodecKeyCompare(t *testing.T) { + table := []struct { + Left []types.Datum + Right []types.Datum + Expect int + }{ + { + types.MakeDatums(1), + types.MakeDatums(1), + 0, + }, + { + types.MakeDatums(-1), + types.MakeDatums(1), + -1, + }, + { + types.MakeDatums(3.15), + types.MakeDatums(3.12), + 1, + }, + { + types.MakeDatums("abc"), + types.MakeDatums("abcd"), + -1, + }, + { + types.MakeDatums("abcdefgh"), + types.MakeDatums("abcdefghi"), + -1, + }, + { + types.MakeDatums(1, "abc"), + types.MakeDatums(1, "abcd"), + -1, + }, + { + types.MakeDatums(1, "abc", "def"), + types.MakeDatums(1, "abcd", "af"), + -1, + }, + { + types.MakeDatums(3.12, "ebc", "def"), + types.MakeDatums(2.12, "abcd", "af"), + 1, + }, + { + types.MakeDatums([]byte{0x01, 0x00}, []byte{0xFF}), + types.MakeDatums([]byte{0x01, 0x00, 0xFF}), + -1, + }, + { + types.MakeDatums([]byte{0x01}, uint64(0xFFFFFFFFFFFFFFF)), + types.MakeDatums([]byte{0x01, 0x10}, 0), + -1, + }, + { + types.MakeDatums(0), + types.MakeDatums(nil), + 1, + }, + { + types.MakeDatums([]byte{0x00}), + types.MakeDatums(nil), + 1, + }, + { + types.MakeDatums(math.SmallestNonzeroFloat64), + types.MakeDatums(nil), + 1, + }, + { + types.MakeDatums(int64(math.MinInt64)), + types.MakeDatums(nil), + 1, + }, + { + types.MakeDatums(1, int64(math.MinInt64), nil), + types.MakeDatums(1, nil, uint64(math.MaxUint64)), + 1, + }, + { + types.MakeDatums(1, []byte{}, nil), + types.MakeDatums(1, nil, 123), + 1, + }, + { + types.MakeDatums(parseTime(t, "2011-11-11 00:00:00"), 1), + types.MakeDatums(parseTime(t, "2011-11-11 00:00:00"), 0), + 1, + }, + { + types.MakeDatums(parseDuration(t, "00:00:00"), 1), + types.MakeDatums(parseDuration(t, "00:00:01"), 0), + -1, + }, + { + []types.Datum{types.MinNotNullDatum()}, + []types.Datum{types.MaxValueDatum()}, + -1, + }, + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for _, datums := range table { + b1, err := EncodeKey(sc, nil, datums.Left...) + require.NoError(t, err) + + b2, err := EncodeKey(sc, nil, datums.Right...) + require.NoError(t, err) + + comparedRes := bytes.Compare(b1, b2) + require.Equalf(t, datums.Expect, comparedRes, "%v - %v - %v - %v - %v", datums.Left, datums.Right, b1, b2, datums.Expect) + } +} + +func TestNumberCodec(t *testing.T) { + tblInt64 := []int64{ + math.MinInt64, + math.MinInt32, + math.MinInt16, + math.MinInt8, + 0, + math.MaxInt8, + math.MaxInt16, + math.MaxInt32, + math.MaxInt64, + 1<<47 - 1, + -1 << 47, + 1<<23 - 1, + -1 << 23, + 1<<33 - 1, + -1 << 33, + 1<<55 - 1, + -1 << 55, + 1, + -1, + } + + for _, intNum := range tblInt64 { + b := EncodeInt(nil, intNum) + _, v, err := DecodeInt(b) + require.NoError(t, err) + require.Equal(t, intNum, v) + + b = EncodeIntDesc(nil, intNum) + _, v, err = DecodeIntDesc(b) + require.NoError(t, err) + require.Equal(t, intNum, v) + + b = EncodeVarint(nil, intNum) + _, v, err = DecodeVarint(b) + require.NoError(t, err) + require.Equal(t, intNum, v) + + b = EncodeComparableVarint(nil, intNum) + _, v, err = DecodeComparableVarint(b) + require.NoError(t, err) + require.Equal(t, intNum, v) + } + + tblUint64 := []uint64{ + 0, + math.MaxUint8, + math.MaxUint16, + math.MaxUint32, + math.MaxUint64, + 1<<24 - 1, + 1<<48 - 1, + 1<<56 - 1, + 1, + math.MaxInt16, + math.MaxInt8, + math.MaxInt32, + math.MaxInt64, + } + + for _, uintNum := range tblUint64 { + b := EncodeUint(nil, uintNum) + _, v, err := DecodeUint(b) + require.NoError(t, err) + require.Equal(t, uintNum, v) + + b = EncodeUintDesc(nil, uintNum) + _, v, err = DecodeUintDesc(b) + require.NoError(t, err) + require.Equal(t, uintNum, v) + + b = EncodeUvarint(nil, uintNum) + _, v, err = DecodeUvarint(b) + require.NoError(t, err) + require.Equal(t, uintNum, v) + + b = EncodeComparableUvarint(nil, uintNum) + _, v, err = DecodeComparableUvarint(b) + require.NoError(t, err) + require.Equal(t, uintNum, v) + } + + var b []byte + b = EncodeComparableVarint(b, -1) + b = EncodeComparableUvarint(b, 1) + b = EncodeComparableVarint(b, 2) + b, i, err := DecodeComparableVarint(b) + require.NoError(t, err) + require.Equal(t, int64(-1), i) + + b, u, err := DecodeComparableUvarint(b) + require.NoError(t, err) + require.Equal(t, uint64(1), u) + + _, i, err = DecodeComparableVarint(b) + require.NoError(t, err) + require.Equal(t, int64(2), i) +} + +func TestNumberOrder(t *testing.T) { + tblInt64 := []struct { + Arg1 int64 + Arg2 int64 + Ret int + }{ + {-1, 1, -1}, + {math.MaxInt64, math.MinInt64, 1}, + {math.MaxInt64, math.MaxInt32, 1}, + {math.MinInt32, math.MaxInt16, -1}, + {math.MinInt64, math.MaxInt8, -1}, + {0, math.MaxInt8, -1}, + {math.MinInt8, 0, -1}, + {math.MinInt16, math.MaxInt16, -1}, + {1, -1, 1}, + {1, 0, 1}, + {-1, 0, -1}, + {0, 0, 0}, + {math.MaxInt16, math.MaxInt16, 0}, + } + + for _, intNums := range tblInt64 { + b1 := EncodeInt(nil, intNums.Arg1) + b2 := EncodeInt(nil, intNums.Arg2) + require.Equal(t, intNums.Ret, bytes.Compare(b1, b2)) + + b1 = EncodeIntDesc(nil, intNums.Arg1) + b2 = EncodeIntDesc(nil, intNums.Arg2) + require.Equal(t, -intNums.Ret, bytes.Compare(b1, b2)) + + b1 = EncodeComparableVarint(nil, intNums.Arg1) + b2 = EncodeComparableVarint(nil, intNums.Arg2) + require.Equal(t, intNums.Ret, bytes.Compare(b1, b2)) + } + + tblUint64 := []struct { + Arg1 uint64 + Arg2 uint64 + Ret int + }{ + {0, 0, 0}, + {1, 0, 1}, + {0, 1, -1}, + {math.MaxInt8, math.MaxInt16, -1}, + {math.MaxUint32, math.MaxInt32, 1}, + {math.MaxUint8, math.MaxInt8, 1}, + {math.MaxUint16, math.MaxInt32, -1}, + {math.MaxUint64, math.MaxInt64, 1}, + {math.MaxInt64, math.MaxUint32, 1}, + {math.MaxUint64, 0, 1}, + {0, math.MaxUint64, -1}, + } + + for _, uintNums := range tblUint64 { + b1 := EncodeUint(nil, uintNums.Arg1) + b2 := EncodeUint(nil, uintNums.Arg2) + require.Equal(t, uintNums.Ret, bytes.Compare(b1, b2)) + + b1 = EncodeUintDesc(nil, uintNums.Arg1) + b2 = EncodeUintDesc(nil, uintNums.Arg2) + require.Equal(t, -uintNums.Ret, bytes.Compare(b1, b2)) + + b1 = EncodeComparableUvarint(nil, uintNums.Arg1) + b2 = EncodeComparableUvarint(nil, uintNums.Arg2) + require.Equal(t, uintNums.Ret, bytes.Compare(b1, b2)) + } +} + +func TestFloatCodec(t *testing.T) { + tblFloat := []float64{ + -1, + 0, + 1, + math.MaxFloat64, + math.MaxFloat32, + math.SmallestNonzeroFloat32, + math.SmallestNonzeroFloat64, + math.Inf(-1), + math.Inf(1), + } + + for _, floatNum := range tblFloat { + b := EncodeFloat(nil, floatNum) + _, v, err := DecodeFloat(b) + require.NoError(t, err) + require.Equal(t, floatNum, v) + + b = EncodeFloatDesc(nil, floatNum) + _, v, err = DecodeFloatDesc(b) + require.NoError(t, err) + require.Equal(t, floatNum, v) + } + + tblCmp := []struct { + Arg1 float64 + Arg2 float64 + Ret int + }{ + {1, -1, 1}, + {1, 0, 1}, + {0, -1, 1}, + {0, 0, 0}, + {math.MaxFloat64, 1, 1}, + {math.MaxFloat32, math.MaxFloat64, -1}, + {math.MaxFloat64, 0, 1}, + {math.MaxFloat64, math.SmallestNonzeroFloat64, 1}, + {math.Inf(-1), 0, -1}, + {math.Inf(1), 0, 1}, + {math.Inf(-1), math.Inf(1), -1}, + } + + for _, floatNums := range tblCmp { + b1 := EncodeFloat(nil, floatNums.Arg1) + b2 := EncodeFloat(nil, floatNums.Arg2) + + ret := bytes.Compare(b1, b2) + require.Equal(t, floatNums.Ret, ret) + + b1 = EncodeFloatDesc(nil, floatNums.Arg1) + b2 = EncodeFloatDesc(nil, floatNums.Arg2) + + ret = bytes.Compare(b1, b2) + require.Equal(t, -floatNums.Ret, ret) + } +} + +func TestBytes(t *testing.T) { + tblBytes := [][]byte{ + {}, + {0x00, 0x01}, + {0xff, 0xff}, + {0x01, 0x00}, + []byte("abc"), + []byte("hello world"), + } + + for _, bytesDatum := range tblBytes { + b := EncodeBytes(nil, bytesDatum) + _, v, err := DecodeBytes(b, nil) + require.NoError(t, err) + require.Equalf(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) + + b = EncodeBytesDesc(nil, bytesDatum) + _, v, err = DecodeBytesDesc(b, nil) + require.NoError(t, err) + require.Equalf(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) + + b = EncodeCompactBytes(nil, bytesDatum) + _, v, err = DecodeCompactBytes(b) + require.NoError(t, err) + require.Equal(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) + } + + tblCmp := []struct { + Arg1 []byte + Arg2 []byte + Ret int + }{ + {[]byte{}, []byte{0x00}, -1}, + {[]byte{0x00}, []byte{0x00}, 0}, + {[]byte{0xFF}, []byte{0x00}, 1}, + {[]byte{0xFF}, []byte{0xFF, 0x00}, -1}, + {[]byte("a"), []byte("b"), -1}, + {[]byte("a"), []byte{0x00}, 1}, + {[]byte{0x00}, []byte{0x01}, -1}, + {[]byte{0x00, 0x01}, []byte{0x00, 0x00}, 1}, + {[]byte{0x00, 0x00, 0x00}, []byte{0x00, 0x00}, 1}, + {[]byte{0x00, 0x00, 0x00}, []byte{0x00, 0x00}, 1}, + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, -1}, + {[]byte{0x01, 0x02, 0x03, 0x00}, []byte{0x01, 0x02, 0x03}, 1}, + {[]byte{0x01, 0x03, 0x03, 0x04}, []byte{0x01, 0x03, 0x03, 0x05}, -1}, + {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, -1}, + {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 1}, + {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 1}, + } + + for _, bytesData := range tblCmp { + b1 := EncodeBytes(nil, bytesData.Arg1) + b2 := EncodeBytes(nil, bytesData.Arg2) + + ret := bytes.Compare(b1, b2) + require.Equal(t, bytesData.Ret, ret) + + b1 = EncodeBytesDesc(nil, bytesData.Arg1) + b2 = EncodeBytesDesc(nil, bytesData.Arg2) + + ret = bytes.Compare(b1, b2) + require.Equal(t, -bytesData.Ret, ret) + } +} + +func parseTime(t *testing.T, s string) types.Time { + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + m, err := types.ParseTime(sc, s, mysql.TypeDatetime, types.DefaultFsp, nil) + require.NoError(t, err) + return m +} + +func parseDuration(t *testing.T, s string) types.Duration { + m, _, err := types.ParseDuration(nil, s, types.DefaultFsp) + require.NoError(t, err) + return m +} + +func TestTime(t *testing.T) { + tbl := []string{ + "2011-01-01 00:00:00", + "2011-01-01 00:00:00", + "0001-01-01 00:00:00", + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for _, timeDatum := range tbl { + m := types.NewDatum(parseTime(t, timeDatum)) + + b, err := EncodeKey(sc, nil, m) + require.NoError(t, err) + v, err := Decode(b, 1) + require.NoError(t, err) + + var rawTime types.Time + rawTime.SetType(mysql.TypeDatetime) + err = rawTime.FromPackedUint(v[0].GetUint64()) + require.NoError(t, err) + + require.Equal(t, m, types.NewDatum(rawTime)) + } + + tblCmp := []struct { + Arg1 string + Arg2 string + Ret int + }{ + {"2011-10-10 00:00:00", "2000-12-12 11:11:11", 1}, + {"2000-10-10 00:00:00", "2001-10-10 00:00:00", -1}, + {"2000-10-10 00:00:00", "2000-10-10 00:00:00", 0}, + } + + for _, timeData := range tblCmp { + m1 := types.NewDatum(parseTime(t, timeData.Arg1)) + m2 := types.NewDatum(parseTime(t, timeData.Arg2)) + + b1, err := EncodeKey(sc, nil, m1) + require.NoError(t, err) + b2, err := EncodeKey(sc, nil, m2) + require.NoError(t, err) + + ret := bytes.Compare(b1, b2) + require.Equal(t, timeData.Ret, ret) + } +} + +func TestDuration(t *testing.T) { + tbl := []string{ + "11:11:11", + "00:00:00", + "1 11:11:11", + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for _, duration := range tbl { + m := parseDuration(t, duration) + + b, err := EncodeKey(sc, nil, types.NewDatum(m)) + require.NoError(t, err) + v, err := Decode(b, 1) + require.NoError(t, err) + m.Fsp = types.MaxFsp + require.Equal(t, types.MakeDatums(m), v) + } + + tblCmp := []struct { + Arg1 string + Arg2 string + Ret int + }{ + {"20:00:00", "11:11:11", 1}, + {"00:00:00", "00:00:01", -1}, + {"00:00:00", "00:00:00", 0}, + } + + for _, durations := range tblCmp { + m1 := parseDuration(t, durations.Arg1) + m2 := parseDuration(t, durations.Arg2) + + b1, err := EncodeKey(sc, nil, types.NewDatum(m1)) + require.NoError(t, err) + b2, err := EncodeKey(sc, nil, types.NewDatum(m2)) + require.NoError(t, err) + + ret := bytes.Compare(b1, b2) + require.Equal(t, durations.Ret, ret) + } +} + +func TestDecimal(t *testing.T) { + tbl := []string{ + "1234.00", + "1234", + "12.34", + "12.340", + "0.1234", + "0.0", + "0", + "-0.0", + "-0.0000", + "-1234.00", + "-1234", + "-12.34", + "-12.340", + "-0.1234", + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for _, decimalNum := range tbl { + dec := new(types.MyDecimal) + err := dec.FromString([]byte(decimalNum)) + require.NoError(t, err) + + b, err := EncodeKey(sc, nil, types.NewDatum(dec)) + require.NoError(t, err) + v, err := Decode(b, 1) + require.NoError(t, err) + require.Len(t, v, 1) + + vv := v[0].GetMysqlDecimal() + require.Equal(t, 0, vv.Compare(dec)) + } + + tblCmp := []struct { + Arg1 interface{} + Arg2 interface{} + Ret int + }{ + // Test for float type decimal. + {"1234", "123400", -1}, + {"12340", "123400", -1}, + {"1234", "1234.5", -1}, + {"1234", "1234.0000", 0}, + {"1234", "12.34", 1}, + {"12.34", "12.35", -1}, + {"0.12", "0.1234", -1}, + {"0.1234", "12.3400", -1}, + {"0.1234", "0.1235", -1}, + {"0.123400", "12.34", -1}, + {"12.34000", "12.34", 0}, + {"0.01234", "0.01235", -1}, + {"0.1234", "0", 1}, + {"0.0000", "0", 0}, + {"0.0001", "0", 1}, + {"0.0001", "0.0000", 1}, + {"0", "-0.0000", 0}, + {"-0.0001", "0", -1}, + {"-0.1234", "0", -1}, + {"-0.1234", "-0.12", -1}, + {"-0.12", "-0.1234", 1}, + {"-0.12", "-0.1200", 0}, + {"-0.1234", "0.1234", -1}, + {"-1.234", "-12.34", 1}, + {"-0.1234", "-12.34", 1}, + {"-12.34", "1234", -1}, + {"-12.34", "-12.35", 1}, + {"-0.01234", "-0.01235", 1}, + {"-1234", "-123400", 1}, + {"-12340", "-123400", 1}, + + // Test for int type decimal. + {int64(-1), int64(1), -1}, + {int64(math.MaxInt64), int64(math.MinInt64), 1}, + {int64(math.MaxInt64), int64(math.MaxInt32), 1}, + {int64(math.MinInt32), int64(math.MaxInt16), -1}, + {int64(math.MinInt64), int64(math.MaxInt8), -1}, + {int64(0), int64(math.MaxInt8), -1}, + {int64(math.MinInt8), int64(0), -1}, + {int64(math.MinInt16), int64(math.MaxInt16), -1}, + {int64(1), int64(-1), 1}, + {int64(1), int64(0), 1}, + {int64(-1), int64(0), -1}, + {int64(0), int64(0), 0}, + {int64(math.MaxInt16), int64(math.MaxInt16), 0}, + + // Test for uint type decimal. + {uint64(0), uint64(0), 0}, + {uint64(1), uint64(0), 1}, + {uint64(0), uint64(1), -1}, + {uint64(math.MaxInt8), uint64(math.MaxInt16), -1}, + {uint64(math.MaxUint32), uint64(math.MaxInt32), 1}, + {uint64(math.MaxUint8), uint64(math.MaxInt8), 1}, + {uint64(math.MaxUint16), uint64(math.MaxInt32), -1}, + {uint64(math.MaxUint64), uint64(math.MaxInt64), 1}, + {uint64(math.MaxInt64), uint64(math.MaxUint32), 1}, + {uint64(math.MaxUint64), uint64(0), 1}, + {uint64(0), uint64(math.MaxUint64), -1}, + } + for _, decimalNums := range tblCmp { + d1 := types.NewDatum(decimalNums.Arg1) + dec1, err := d1.ToDecimal(sc) + require.NoError(t, err) + d1.SetMysqlDecimal(dec1) + + d2 := types.NewDatum(decimalNums.Arg2) + dec2, err := d2.ToDecimal(sc) + require.NoError(t, err) + d2.SetMysqlDecimal(dec2) + + d1.SetLength(30) + d1.SetFrac(6) + d2.SetLength(30) + d2.SetFrac(6) + + b1, err := EncodeKey(sc, nil, d1) + require.NoError(t, err) + b2, err := EncodeKey(sc, nil, d2) + require.NoError(t, err) + + ret := bytes.Compare(b1, b2) + require.Equalf(t, decimalNums.Ret, ret, "%v %x %x", decimalNums, b1, b2) + + b1, err = EncodeValue(sc, b1[:0], d1) + require.NoError(t, err) + size, err := EstimateValueSize(sc, d1) + require.NoError(t, err) + require.Len(t, b1, size) + } + + floats := []float64{-123.45, -123.40, -23.45, -1.43, -0.93, -0.4333, -0.068, + -0.0099, 0, 0.001, 0.0012, 0.12, 1.2, 1.23, 123.3, 2424.242424} + decs := make([][]byte, 0, len(floats)) + for i := range floats { + dec := types.NewDecFromFloatForTest(floats[i]) + var d types.Datum + d.SetLength(20) + d.SetFrac(6) + d.SetMysqlDecimal(dec) + b, err := EncodeDecimal(nil, d.GetMysqlDecimal(), d.Length(), d.Frac()) + require.NoError(t, err) + decs = append(decs, b) + size, err := EstimateValueSize(sc, d) + require.NoError(t, err) + // size - 1 because the flag occupy 1 bit. + require.Len(t, b, size-1) + } + for i := 0; i < len(decs)-1; i++ { + cmpRes := bytes.Compare(decs[i], decs[i+1]) + require.LessOrEqual(t, cmpRes, 0) + } + + d := types.NewDecFromStringForTest("-123.123456789") + _, err := EncodeDecimal(nil, d, 20, 5) + require.Truef(t, terror.ErrorEqual(err, types.ErrTruncated), "err %v", err) + + _, err = EncodeDecimal(nil, d, 12, 10) + require.Truef(t, terror.ErrorEqual(err, types.ErrOverflow), "err %v", err) + + sc.IgnoreTruncate.Store(true) + decimalDatum := types.NewDatum(d) + decimalDatum.SetLength(20) + decimalDatum.SetFrac(5) + _, err = EncodeValue(sc, nil, decimalDatum) + require.NoError(t, err) + + sc.OverflowAsWarning = true + decimalDatum.SetLength(12) + decimalDatum.SetFrac(10) + _, err = EncodeValue(sc, nil, decimalDatum) + require.NoError(t, err) +} + +func TestJSON(t *testing.T) { + tbl := []string{ + "1234.00", + `{"a": "b"}`, + } + + originalDatums := make([]types.Datum, 0, len(tbl)) + for _, jsonDatum := range tbl { + var d types.Datum + j, err := types.ParseBinaryJSONFromString(jsonDatum) + require.NoError(t, err) + d.SetMysqlJSON(j) + originalDatums = append(originalDatums, d) + } + + buf := make([]byte, 0, 4096) + buf, err := encode(nil, buf, originalDatums, false) + require.NoError(t, err) + + decodedDatums, err := Decode(buf, 2) + require.NoError(t, err) + + for i := range decodedDatums { + lhs := originalDatums[i].GetMysqlJSON().String() + rhs := decodedDatums[i].GetMysqlJSON().String() + require.Equal(t, lhs, rhs) + } +} + +func TestCut(t *testing.T) { + table := []struct { + Input []types.Datum + Expect []types.Datum + }{ + { + types.MakeDatums(int64(1)), + types.MakeDatums(int64(1)), + }, + + { + types.MakeDatums(float32(1), float64(3.15), []byte("123"), "123"), + types.MakeDatums(float64(1), float64(3.15), []byte("123"), []byte("123")), + }, + { + types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), + types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), + }, + + { + types.MakeDatums(true, false), + types.MakeDatums(int64(1), int64(0)), + }, + + { + types.MakeDatums(nil), + types.MakeDatums(nil), + }, + + { + types.MakeDatums(types.NewBinaryLiteralFromUint(100, -1), types.NewBinaryLiteralFromUint(100, 4)), + types.MakeDatums(uint64(100), uint64(100)), + }, + + { + types.MakeDatums(types.Enum{Name: "a", Value: 1}, types.Set{Name: "a", Value: 1}), + types.MakeDatums(uint64(1), uint64(1)), + }, + { + types.MakeDatums(float32(1), float64(3.15), []byte("123456789012345")), + types.MakeDatums(float64(1), float64(3.15), []byte("123456789012345")), + }, + { + types.MakeDatums(types.NewDecFromInt(0), types.NewDecFromFloatForTest(-1.3)), + types.MakeDatums(types.NewDecFromInt(0), types.NewDecFromFloatForTest(-1.3)), + }, + { + types.MakeDatums(types.CreateBinaryJSON("abc")), + types.MakeDatums(types.CreateBinaryJSON("abc")), + }, + { + types.MakeDatums(types.CreateBinaryJSON(types.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), + types.MakeDatums(types.CreateBinaryJSON(types.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), + }, + } + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + for i, datums := range table { + b, err := EncodeKey(sc, nil, datums.Input...) + require.NoErrorf(t, err, "%d %v", i, datums) + + var d []byte + for j, e := range datums.Expect { + d, b, err = CutOne(b) + require.NoError(t, err) + require.NotNil(t, d) + + ed, err1 := EncodeKey(sc, nil, e) + require.NoError(t, err1) + require.Equalf(t, ed, d, "%d:%d %#v", i, j, e) + } + require.Len(t, b, 0) + } + + for i, datums := range table { + b, err := EncodeValue(sc, nil, datums.Input...) + require.NoErrorf(t, err, "%d %v", i, datums) + + var d []byte + for j, e := range datums.Expect { + d, b, err = CutOne(b) + require.NoError(t, err) + require.NotNil(t, d) + + ed, err1 := EncodeValue(sc, nil, e) + require.NoError(t, err1) + require.Equalf(t, ed, d, "%d:%d %#v", i, j, e) + } + require.Len(t, b, 0) + } + + input := 42 + b, err := EncodeValue(sc, nil, types.NewDatum(input)) + require.NoError(t, err) + rem, n, err := CutColumnID(b) + require.NoError(t, err) + require.Len(t, rem, 0) + require.Equal(t, int64(input), n) +} + +func TestCutOneError(t *testing.T) { + var b []byte + _, _, err := CutOne(b) + require.Error(t, err) + require.EqualError(t, err, "invalid encoded key") + + b = []byte{4 /* codec.uintFlag */, 0, 0, 0} + _, _, err = CutOne(b) + require.Error(t, err) + require.Regexp(t, "^invalid encoded key", err.Error()) +} + +func TestSetRawValues(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + datums := types.MakeDatums(1, "abc", 1.1, []byte("def")) + rowData, err := EncodeValue(sc, nil, datums...) + require.NoError(t, err) + + values := make([]types.Datum, 4) + err = SetRawValues(rowData, values) + require.NoError(t, err) + + for i, rawVal := range values { + require.IsType(t, types.KindRaw, rawVal.Kind()) + encoded, encodedErr := EncodeValue(sc, nil, datums[i]) + require.NoError(t, encodedErr) + require.Equal(t, rawVal.GetBytes(), encoded) + } +} + +func TestDecodeOneToChunk(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + datums, tps := datumsForTest(sc) + rowCount := 3 + chk := chunkForTest(t, sc, datums, tps, rowCount) + for colIdx, tp := range tps { + for rowIdx := 0; rowIdx < rowCount; rowIdx++ { + got := chk.GetRow(rowIdx).GetDatum(colIdx, tp) + expect := datums[colIdx] + if got.IsNull() { + require.True(t, expect.IsNull()) + } else { + if got.Kind() != types.KindMysqlDecimal { + cmp, err := got.Compare(sc, &expect, collate.GetCollator(tp.GetCollate())) + require.NoError(t, err) + require.Equalf(t, 0, cmp, "expect: %v, got %v", expect, got) + } else { + require.Equal(t, expect.GetString(), got.GetString(), "expect: %v, got %v", expect, got) + } + } + } + } +} + +func TestHashGroup(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + tp := types.NewFieldType(mysql.TypeNewDecimal) + tps := []*types.FieldType{tp} + chk1 := chunk.New(tps, 3, 3) + chk1.Reset() + chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) + chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) + chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) + + buf1 := make([][]byte, 3) + tp1 := tp + tp1.SetFlen(20) + tp1.SetDecimal(5) + _, err := HashGroupKey(sc, 3, chk1.Column(0), buf1, tp1) + require.Error(t, err) + + tp2 := tp + tp2.SetFlen(12) + tp2.SetDecimal(10) + _, err = HashGroupKey(sc, 3, chk1.Column(0), buf1, tp2) + require.Error(t, err) +} + +func datumsForTest(_ *stmtctx.StatementContext) ([]types.Datum, []*types.FieldType) { + decType := types.NewFieldType(mysql.TypeNewDecimal) + decType.SetDecimal(2) + _tp1 := types.NewFieldType(mysql.TypeEnum) + _tp1.SetElems([]string{"a"}) + _tp2 := types.NewFieldType(mysql.TypeSet) + _tp2.SetElems([]string{"a"}) + _tp3 := types.NewFieldType(mysql.TypeSet) + _tp3.SetElems([]string{"a", "b", "c", "d", "e", "f"}) + _tp4 := types.NewFieldType(mysql.TypeBit) + _tp4.SetFlen(8) + table := []struct { + value interface{} + tp *types.FieldType + }{ + {nil, types.NewFieldType(mysql.TypeNull)}, + {nil, types.NewFieldType(mysql.TypeLonglong)}, + {nil, types.NewFieldType(mysql.TypeFloat)}, + {nil, types.NewFieldType(mysql.TypeDate)}, + {nil, types.NewFieldType(mysql.TypeDuration)}, + {nil, types.NewFieldType(mysql.TypeNewDecimal)}, + {nil, types.NewFieldType(mysql.TypeEnum)}, + {nil, types.NewFieldType(mysql.TypeSet)}, + {nil, types.NewFieldType(mysql.TypeBit)}, + {nil, types.NewFieldType(mysql.TypeJSON)}, + {nil, types.NewFieldType(mysql.TypeVarchar)}, + {nil, types.NewFieldType(mysql.TypeDouble)}, + {int64(1), types.NewFieldType(mysql.TypeTiny)}, + {int64(1), types.NewFieldType(mysql.TypeShort)}, + {int64(1), types.NewFieldType(mysql.TypeInt24)}, + {int64(1), types.NewFieldType(mysql.TypeLong)}, + {int64(-1), types.NewFieldType(mysql.TypeLong)}, + {int64(1), types.NewFieldType(mysql.TypeLonglong)}, + {uint64(1), types.NewFieldType(mysql.TypeLonglong)}, + {float32(1), types.NewFieldType(mysql.TypeFloat)}, + {float64(1), types.NewFieldType(mysql.TypeDouble)}, + {types.NewDecFromInt(1), types.NewFieldType(mysql.TypeNewDecimal)}, + {types.NewDecFromStringForTest("1.123"), decType}, + {"abc", types.NewFieldType(mysql.TypeString)}, + {"def", types.NewFieldType(mysql.TypeVarchar)}, + {"ghi", types.NewFieldType(mysql.TypeVarString)}, + {[]byte("abc"), types.NewFieldType(mysql.TypeBlob)}, + {[]byte("abc"), types.NewFieldType(mysql.TypeTinyBlob)}, + {[]byte("abc"), types.NewFieldType(mysql.TypeMediumBlob)}, + {[]byte("abc"), types.NewFieldType(mysql.TypeLongBlob)}, + {types.CurrentTime(mysql.TypeDatetime), types.NewFieldType(mysql.TypeDatetime)}, + {types.CurrentTime(mysql.TypeDate), types.NewFieldType(mysql.TypeDate)}, + {types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, types.DefaultFsp), types.NewFieldType(mysql.TypeTimestamp)}, + {types.Duration{Duration: time.Second, Fsp: 1}, types.NewFieldType(mysql.TypeDuration)}, + {types.Enum{Name: "a", Value: 1}, _tp1}, + {types.Set{Name: "a", Value: 1}, _tp2}, + {types.Set{Name: "f", Value: 32}, _tp3}, + {types.BinaryLiteral{100}, _tp4}, + {types.CreateBinaryJSON("abc"), types.NewFieldType(mysql.TypeJSON)}, + {int64(1), types.NewFieldType(mysql.TypeYear)}, + } + + datums := make([]types.Datum, 0, len(table)+2) + tps := make([]*types.FieldType, 0, len(table)+2) + for _, t := range table { + tps = append(tps, t.tp) + d := types.NewDatum(t.value) + datums = append(datums, d) + } + return datums, tps +} + +func chunkForTest(t *testing.T, sc *stmtctx.StatementContext, datums []types.Datum, tps []*types.FieldType, rowCount int) *chunk.Chunk { + decoder := NewDecoder(chunk.New(tps, 32, 32), sc.TimeZone()) + for rowIdx := 0; rowIdx < rowCount; rowIdx++ { + encoded, err := EncodeValue(sc, nil, datums...) + require.NoError(t, err) + decoder.buf = make([]byte, 0, len(encoded)) + for colIdx, tp := range tps { + encoded, err = decoder.DecodeOne(encoded, colIdx, tp) + require.NoError(t, err) + } + } + return decoder.chk +} + +func TestDecodeRange(t *testing.T) { + _, _, err := DecodeRange(nil, 0, nil, nil) + require.Error(t, err) + + datums := types.MakeDatums(1, "abc", 1.1, []byte("def")) + rowData, err := EncodeValue(nil, nil, datums...) + require.NoError(t, err) + + datums1, _, err := DecodeRange(rowData, len(datums), nil, nil) + require.NoError(t, err) + for i, datum := range datums1 { + cmp, err := datum.Compare(nil, &datums[i], collate.GetBinaryCollator()) + require.NoError(t, err) + require.Equal(t, 0, cmp) + } + + for _, b := range []byte{NilFlag, bytesFlag, maxFlag, maxFlag + 1} { + newData := append(rowData, b) + _, _, err := DecodeRange(newData, len(datums)+1, nil, nil) + require.NoError(t, err) + } +} + +func testHashChunkRowEqual(t *testing.T, a, b interface{}, equal bool) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + buf1 := make([]byte, 1) + buf2 := make([]byte, 1) + + tp1 := new(types.FieldType) + types.DefaultTypeForValue(a, tp1, mysql.DefaultCharset, mysql.DefaultCollationName) + chk1 := chunk.New([]*types.FieldType{tp1}, 1, 1) + d := types.Datum{} + d.SetValue(a, tp1) + chk1.AppendDatum(0, &d) + + tp2 := new(types.FieldType) + types.DefaultTypeForValue(b, tp2, mysql.DefaultCharset, mysql.DefaultCollationName) + chk2 := chunk.New([]*types.FieldType{tp2}, 1, 1) + d = types.Datum{} + d.SetValue(b, tp2) + chk2.AppendDatum(0, &d) + + h := crc32.NewIEEE() + err1 := HashChunkRow(sc, h, chk1.GetRow(0), []*types.FieldType{tp1}, []int{0}, buf1) + sum1 := h.Sum32() + h.Reset() + err2 := HashChunkRow(sc, h, chk2.GetRow(0), []*types.FieldType{tp2}, []int{0}, buf2) + sum2 := h.Sum32() + require.NoError(t, err1) + require.NoError(t, err2) + if equal { + require.Equal(t, sum2, sum1) + } else { + require.NotEqual(t, sum2, sum1) + } + e, err := EqualChunkRow(sc, + chk1.GetRow(0), []*types.FieldType{tp1}, []int{0}, + chk2.GetRow(0), []*types.FieldType{tp2}, []int{0}) + require.NoError(t, err) + if equal { + require.True(t, e) + } else { + require.False(t, e) + } +} + +func TestHashChunkRow(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + buf := make([]byte, 1) + datums, tps := datumsForTest(sc) + chk := chunkForTest(t, sc, datums, tps, 1) + + colIdx := make([]int, len(tps)) + for i := 0; i < len(tps); i++ { + colIdx[i] = i + } + h := crc32.NewIEEE() + err1 := HashChunkRow(sc, h, chk.GetRow(0), tps, colIdx, buf) + sum1 := h.Sum32() + h.Reset() + err2 := HashChunkRow(sc, h, chk.GetRow(0), tps, colIdx, buf) + sum2 := h.Sum32() + + require.NoError(t, err1) + require.NoError(t, err2) + require.Equal(t, sum2, sum1) + e, err := EqualChunkRow(sc, + chk.GetRow(0), tps, colIdx, + chk.GetRow(0), tps, colIdx) + require.NoError(t, err) + require.True(t, e) + + testHashChunkRowEqual(t, nil, nil, true) + testHashChunkRowEqual(t, uint64(1), int64(1), true) + testHashChunkRowEqual(t, uint64(18446744073709551615), int64(-1), false) + + dec1 := types.NewDecFromStringForTest("1.1") + dec2 := types.NewDecFromStringForTest("01.100") + testHashChunkRowEqual(t, dec1, dec2, true) + dec1 = types.NewDecFromStringForTest("1.1") + dec2 = types.NewDecFromStringForTest("01.200") + testHashChunkRowEqual(t, dec1, dec2, false) + + testHashChunkRowEqual(t, float32(1.0), float64(1.0), true) + testHashChunkRowEqual(t, float32(1.0), float64(1.1), false) + + testHashChunkRowEqual(t, "x", []byte("x"), true) + testHashChunkRowEqual(t, "x", []byte("y"), false) + + testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(1)), types.CreateBinaryJSON(float64(1.0)), true) + testHashChunkRowEqual(t, types.CreateBinaryJSON(uint64(math.MaxUint64)), types.CreateBinaryJSON(float64(math.MaxUint64)), false) + testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(math.MinInt64)), types.CreateBinaryJSON(float64(math.MinInt64)), true) +} + +func TestValueSizeOfSignedInt(t *testing.T) { + testCase := []int64{64, 8192, 1048576, 134217728, 17179869184, 2199023255552, 281474976710656, 36028797018963968, 4611686018427387904} + var b []byte + for _, v := range testCase { + b := encodeSignedInt(b[:0], v-10, false) + require.Equal(t, valueSizeOfSignedInt(v-10), len(b)) + + b = encodeSignedInt(b[:0], v, false) + require.Equal(t, valueSizeOfSignedInt(v), len(b)) + + b = encodeSignedInt(b[:0], v+10, false) + require.Equal(t, valueSizeOfSignedInt(v+10), len(b)) + + // Test for negative value. + b = encodeSignedInt(b[:0], 0-v, false) + require.Equal(t, valueSizeOfSignedInt(0-v), len(b)) + + b = encodeSignedInt(b[:0], 0-v+10, false) + require.Equal(t, valueSizeOfSignedInt(0-v+10), len(b)) + + b = encodeSignedInt(b[:0], 0-v-10, false) + require.Equal(t, valueSizeOfSignedInt(0-v-10), len(b)) + } +} + +func TestValueSizeOfUnsignedInt(t *testing.T) { + testCase := []uint64{128, 16384, 2097152, 268435456, 34359738368, 4398046511104, 562949953421312, 72057594037927936, 9223372036854775808} + var b []byte + for _, v := range testCase { + b := encodeUnsignedInt(b[:0], v-10, false) + require.Equal(t, valueSizeOfUnsignedInt(v-10), len(b)) + + b = encodeUnsignedInt(b[:0], v, false) + require.Equal(t, valueSizeOfUnsignedInt(v), len(b)) + + b = encodeUnsignedInt(b[:0], v+10, false) + require.Equal(t, valueSizeOfUnsignedInt(v+10), len(b)) + } +} + +func TestHashChunkColumns(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + buf := make([]byte, 1) + datums, tps := datumsForTest(sc) + chk := chunkForTest(t, sc, datums, tps, 4) + + colIdx := make([]int, len(tps)) + for i := 0; i < len(tps); i++ { + colIdx[i] = i + } + hasNull := []bool{false, false, false} + vecHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + rowHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + + sel := make([]bool, len(datums)) + for i := 0; i < 3; i++ { + sel[i] = true + } + + // Test hash value of the first 12 `Null` columns + for i := 0; i < 12; i++ { + require.True(t, chk.GetRow(0).IsNull(i)) + err1 := HashChunkSelected(sc, vecHash, chk, tps[i], i, buf, hasNull, sel, false) + err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps[i:i+1], colIdx[i:i+1], buf) + err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps[i:i+1], colIdx[i:i+1], buf) + err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps[i:i+1], colIdx[i:i+1], buf) + require.NoError(t, err1) + require.NoError(t, err2) + require.NoError(t, err3) + require.NoError(t, err4) + + require.True(t, hasNull[0]) + require.True(t, hasNull[1]) + require.True(t, hasNull[2]) + require.Equal(t, rowHash[0].Sum64(), vecHash[0].Sum64()) + require.Equal(t, rowHash[1].Sum64(), vecHash[1].Sum64()) + require.Equal(t, rowHash[2].Sum64(), vecHash[2].Sum64()) + } + + // Test hash value of every single column that is not `Null` + for i := 12; i < len(tps); i++ { + hasNull = []bool{false, false, false} + vecHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + rowHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + + require.False(t, chk.GetRow(0).IsNull(i)) + + err1 := HashChunkSelected(sc, vecHash, chk, tps[i], i, buf, hasNull, sel, false) + err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps[i:i+1], colIdx[i:i+1], buf) + err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps[i:i+1], colIdx[i:i+1], buf) + err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps[i:i+1], colIdx[i:i+1], buf) + + require.NoError(t, err1) + require.NoError(t, err2) + require.NoError(t, err3) + require.NoError(t, err4) + + require.False(t, hasNull[0]) + require.False(t, hasNull[1]) + require.False(t, hasNull[2]) + require.Equal(t, rowHash[0].Sum64(), vecHash[0].Sum64()) + require.Equal(t, rowHash[1].Sum64(), vecHash[1].Sum64()) + require.Equal(t, rowHash[2].Sum64(), vecHash[2].Sum64()) + } +} diff --git a/pkg/util/codec/collation_test.go b/pkg/util/codec/collation_test.go new file mode 100644 index 0000000000000..2ffa4f0ffca81 --- /dev/null +++ b/pkg/util/codec/collation_test.go @@ -0,0 +1,158 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package codec + +import ( + "hash" + "hash/crc32" + "hash/fnv" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/stretchr/testify/require" +) + +func prepareCollationData() (int, *chunk.Chunk, *chunk.Chunk) { + tp := types.NewFieldType(mysql.TypeString) + tps := []*types.FieldType{tp} + chk1 := chunk.New(tps, 3, 3) + chk2 := chunk.New(tps, 3, 3) + chk1.Reset() + chk2.Reset() + chk1.Column(0).AppendString("aaa") + chk2.Column(0).AppendString("AAA") + chk1.Column(0).AppendString("😜") + chk2.Column(0).AppendString("😃") + chk1.Column(0).AppendString("À") + chk2.Column(0).AppendString("A") + return 3, chk1, chk2 +} + +func TestHashGroupKeyCollation(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + tp := types.NewFieldType(mysql.TypeString) + n, chk1, chk2 := prepareCollationData() + + tp.SetCollate("utf8_general_ci") + buf1 := make([][]byte, n) + buf2 := make([][]byte, n) + buf1, err := HashGroupKey(sc, n, chk1.Column(0), buf1, tp) + require.NoError(t, err) + + buf2, err = HashGroupKey(sc, n, chk2.Column(0), buf2, tp) + require.NoError(t, err) + + for i := 0; i < n; i++ { + require.Equal(t, len(buf2[i]), len(buf1[i])) + for j := range buf1 { + require.Equal(t, buf2[i][j], buf1[i][j]) + } + } + + tp.SetCollate("utf8_unicode_ci") + buf1 = make([][]byte, n) + buf2 = make([][]byte, n) + buf1, err = HashGroupKey(sc, n, chk1.Column(0), buf1, tp) + require.NoError(t, err) + buf2, err = HashGroupKey(sc, n, chk2.Column(0), buf2, tp) + require.NoError(t, err) + + for i := 0; i < n; i++ { + require.Equal(t, len(buf2[i]), len(buf1[i])) + for j := range buf1 { + require.Equal(t, buf2[i][j], buf1[i][j]) + } + } +} + +func TestHashChunkRowCollation(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + tp := types.NewFieldType(mysql.TypeString) + tps := []*types.FieldType{tp} + n, chk1, chk2 := prepareCollationData() + cols := []int{0} + buf := make([]byte, 1) + + tp.SetCollate("binary") + for i := 0; i < n; i++ { + h1 := crc32.NewIEEE() + h2 := crc32.NewIEEE() + require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) + require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) + require.NotEqual(t, h2.Sum32(), h1.Sum32()) + h1.Reset() + h2.Reset() + } + + tp.SetCollate("utf8_general_ci") + for i := 0; i < n; i++ { + h1 := crc32.NewIEEE() + h2 := crc32.NewIEEE() + require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) + require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) + require.Equal(t, h2.Sum32(), h1.Sum32()) + h1.Reset() + h2.Reset() + } + + tp.SetCollate("utf8_unicode_ci") + for i := 0; i < n; i++ { + h1 := crc32.NewIEEE() + h2 := crc32.NewIEEE() + require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) + require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) + require.Equal(t, h2.Sum32(), h1.Sum32()) + h1.Reset() + h2.Reset() + } +} + +func TestHashChunkColumnsCollation(t *testing.T) { + sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) + tp := types.NewFieldType(mysql.TypeString) + n, chk1, chk2 := prepareCollationData() + buf := make([]byte, 1) + hasNull := []bool{false, false, false} + h1s := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + h2s := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} + + tp.SetCollate("binary") + require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) + require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) + + for i := 0; i < n; i++ { + require.NotEqual(t, h2s[i].Sum64(), h1s[i].Sum64()) + h1s[i].Reset() + h2s[i].Reset() + } + + tp.SetCollate("utf8_general_ci") + require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) + require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) + for i := 0; i < n; i++ { + require.Equal(t, h2s[i].Sum64(), h1s[i].Sum64()) + } + + tp.SetCollate("utf8_unicode_ci") + require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) + require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) + for i := 0; i < n; i++ { + require.Equal(t, h2s[i].Sum64(), h1s[i].Sum64()) + } +} diff --git a/util/codec/decimal.go b/pkg/util/codec/decimal.go similarity index 96% rename from util/codec/decimal.go rename to pkg/util/codec/decimal.go index 0454febac0a61..7b26feb86b2d8 100644 --- a/util/codec/decimal.go +++ b/pkg/util/codec/decimal.go @@ -17,8 +17,8 @@ package codec import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) // EncodeDecimal encodes a decimal into a byte slice which can be sorted lexicographically later. diff --git a/util/codec/decimal_test.go b/pkg/util/codec/decimal_test.go similarity index 98% rename from util/codec/decimal_test.go rename to pkg/util/codec/decimal_test.go index 77ec77ef92343..d57244b7a138e 100644 --- a/util/codec/decimal_test.go +++ b/pkg/util/codec/decimal_test.go @@ -17,7 +17,7 @@ package codec import ( "testing" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/util/codec/float.go b/pkg/util/codec/float.go similarity index 100% rename from util/codec/float.go rename to pkg/util/codec/float.go diff --git a/pkg/util/codec/main_test.go b/pkg/util/codec/main_test.go new file mode 100644 index 0000000000000..b052b268e77d1 --- /dev/null +++ b/pkg/util/codec/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package codec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/codec/number.go b/pkg/util/codec/number.go similarity index 100% rename from util/codec/number.go rename to pkg/util/codec/number.go diff --git a/pkg/util/collate/BUILD.bazel b/pkg/util/collate/BUILD.bazel new file mode 100644 index 0000000000000..567ddd458cdbf --- /dev/null +++ b/pkg/util/collate/BUILD.bazel @@ -0,0 +1,51 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "collate", + srcs = [ + "bin.go", + "charset.go", + "collate.go", + "gbk_bin.go", + "gbk_chinese_ci.go", + "gbk_chinese_ci_data.go", + "general_ci.go", + "pinyin_tidb_as_cs.go", + "unicode_0400_ci_generated.go", + "unicode_0400_ci_impl.go", + "unicode_0900_ai_ci_generated.go", + "unicode_0900_ai_ci_impl.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/collate", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/charset", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/util/collate/ucadata", + "//pkg/util/dbterror", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/stringutil", + "@com_github_pingcap_errors//:errors", + "@org_golang_x_text//encoding", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "collate_test", + timeout = "short", + srcs = [ + "collate_bench_test.go", + "collate_test.go", + "main_test.go", + ], + embed = [":collate"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/collate/bin.go b/pkg/util/collate/bin.go similarity index 97% rename from util/collate/bin.go rename to pkg/util/collate/bin.go index 2d8509c8e1a46..cbb448a2d4438 100644 --- a/util/collate/bin.go +++ b/pkg/util/collate/bin.go @@ -17,7 +17,7 @@ package collate import ( "strings" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/stringutil" ) type binCollator struct { diff --git a/pkg/util/collate/charset.go b/pkg/util/collate/charset.go new file mode 100644 index 0000000000000..63fa1cb3fa605 --- /dev/null +++ b/pkg/util/collate/charset.go @@ -0,0 +1,28 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package collate + +import "github.com/pingcap/tidb/pkg/parser/charset" + +// switchDefaultCollation switch the default collation for charset according to the new collation config. +func switchDefaultCollation(flag bool) { + if flag { + charset.CharacterSetInfos[charset.CharsetGBK].DefaultCollation = charset.CollationGBKChineseCI + } else { + charset.CharacterSetInfos[charset.CharsetGBK].DefaultCollation = charset.CollationGBKBin + } + charset.CharacterSetInfos[charset.CharsetGBK].Collations[charset.CollationGBKBin].IsDefault = !flag + charset.CharacterSetInfos[charset.CharsetGBK].Collations[charset.CollationGBKChineseCI].IsDefault = flag +} diff --git a/util/collate/collate.go b/pkg/util/collate/collate.go similarity index 98% rename from util/collate/collate.go rename to pkg/util/collate/collate.go index 2367cb8874240..442db4cbc67c4 100644 --- a/util/collate/collate.go +++ b/pkg/util/collate/collate.go @@ -21,11 +21,11 @@ import ( "sync/atomic" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/collate/collate_bench_test.go b/pkg/util/collate/collate_bench_test.go similarity index 100% rename from util/collate/collate_bench_test.go rename to pkg/util/collate/collate_bench_test.go diff --git a/util/collate/collate_test.go b/pkg/util/collate/collate_test.go similarity index 100% rename from util/collate/collate_test.go rename to pkg/util/collate/collate_test.go diff --git a/util/collate/gbk_bin.go b/pkg/util/collate/gbk_bin.go similarity index 98% rename from util/collate/gbk_bin.go rename to pkg/util/collate/gbk_bin.go index 807d7b8c1768e..29894e3222624 100644 --- a/util/collate/gbk_bin.go +++ b/pkg/util/collate/gbk_bin.go @@ -17,7 +17,7 @@ package collate import ( "bytes" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/hack" "golang.org/x/text/encoding" ) diff --git a/util/collate/gbk_chinese_ci.go b/pkg/util/collate/gbk_chinese_ci.go similarity index 97% rename from util/collate/gbk_chinese_ci.go rename to pkg/util/collate/gbk_chinese_ci.go index a81003cd9ef6a..970936835d81c 100644 --- a/util/collate/gbk_chinese_ci.go +++ b/pkg/util/collate/gbk_chinese_ci.go @@ -14,7 +14,7 @@ package collate -import "github.com/pingcap/tidb/util/stringutil" +import "github.com/pingcap/tidb/pkg/util/stringutil" type gbkChineseCICollator struct { } diff --git a/util/collate/gbk_chinese_ci_data.go b/pkg/util/collate/gbk_chinese_ci_data.go similarity index 100% rename from util/collate/gbk_chinese_ci_data.go rename to pkg/util/collate/gbk_chinese_ci_data.go diff --git a/util/collate/general_ci.go b/pkg/util/collate/general_ci.go similarity index 99% rename from util/collate/general_ci.go rename to pkg/util/collate/general_ci.go index 00f9c817819f6..1edcbf5d6eaf1 100644 --- a/util/collate/general_ci.go +++ b/pkg/util/collate/general_ci.go @@ -15,7 +15,7 @@ package collate import ( - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/stringutil" ) type generalCICollator struct { diff --git a/pkg/util/collate/main_test.go b/pkg/util/collate/main_test.go new file mode 100644 index 0000000000000..fa97d7d4464a2 --- /dev/null +++ b/pkg/util/collate/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package collate + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/collate/pinyin_tidb_as_cs.go b/pkg/util/collate/pinyin_tidb_as_cs.go similarity index 100% rename from util/collate/pinyin_tidb_as_cs.go rename to pkg/util/collate/pinyin_tidb_as_cs.go diff --git a/pkg/util/collate/ucadata/BUILD.bazel b/pkg/util/collate/ucadata/BUILD.bazel new file mode 100644 index 0000000000000..2f3fdb1a4331c --- /dev/null +++ b/pkg/util/collate/ucadata/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ucadata", + srcs = [ + "data.go", + "unicode_0900_ai_ci_data_generated.go", + "unicode_ci_data_generated.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/collate/ucadata", + visibility = ["//visibility:public"], +) + +go_test( + name = "ucadata_test", + timeout = "short", + srcs = [ + "unicode_0900_ai_ci_data_test.go", + "unicode_ci_data_original_test.go", + "unicode_ci_data_test.go", + ], + embed = [":ucadata"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/collate/ucadata/data.go b/pkg/util/collate/ucadata/data.go similarity index 100% rename from util/collate/ucadata/data.go rename to pkg/util/collate/ucadata/data.go diff --git a/pkg/util/collate/ucadata/generator/BUILD.bazel b/pkg/util/collate/ucadata/generator/BUILD.bazel new file mode 100644 index 0000000000000..619d15ffb9bde --- /dev/null +++ b/pkg/util/collate/ucadata/generator/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "generator_lib", + srcs = [ + "magic.go", + "main.go", + ], + embedsrcs = [ + "allkeys-4.0.0.txt", + "allkeys-9.0.0.txt", + "data.go.tpl", + ], + importpath = "github.com/pingcap/tidb/pkg/util/collate/ucadata/generator", + visibility = ["//visibility:private"], +) + +go_binary( + name = "generator", + embed = [":generator_lib"], + visibility = ["//visibility:public"], +) diff --git a/util/collate/ucadata/generator/allkeys-4.0.0.txt b/pkg/util/collate/ucadata/generator/allkeys-4.0.0.txt similarity index 100% rename from util/collate/ucadata/generator/allkeys-4.0.0.txt rename to pkg/util/collate/ucadata/generator/allkeys-4.0.0.txt diff --git a/util/collate/ucadata/generator/allkeys-9.0.0.txt b/pkg/util/collate/ucadata/generator/allkeys-9.0.0.txt similarity index 100% rename from util/collate/ucadata/generator/allkeys-9.0.0.txt rename to pkg/util/collate/ucadata/generator/allkeys-9.0.0.txt diff --git a/util/collate/ucadata/generator/data.go.tpl b/pkg/util/collate/ucadata/generator/data.go.tpl similarity index 100% rename from util/collate/ucadata/generator/data.go.tpl rename to pkg/util/collate/ucadata/generator/data.go.tpl diff --git a/util/collate/ucadata/generator/magic.go b/pkg/util/collate/ucadata/generator/magic.go similarity index 100% rename from util/collate/ucadata/generator/magic.go rename to pkg/util/collate/ucadata/generator/magic.go diff --git a/util/collate/ucadata/generator/main.go b/pkg/util/collate/ucadata/generator/main.go similarity index 100% rename from util/collate/ucadata/generator/main.go rename to pkg/util/collate/ucadata/generator/main.go diff --git a/util/collate/ucadata/unicode_0900_ai_ci_data_generated.go b/pkg/util/collate/ucadata/unicode_0900_ai_ci_data_generated.go similarity index 100% rename from util/collate/ucadata/unicode_0900_ai_ci_data_generated.go rename to pkg/util/collate/ucadata/unicode_0900_ai_ci_data_generated.go diff --git a/util/collate/ucadata/unicode_0900_ai_ci_data_test.go b/pkg/util/collate/ucadata/unicode_0900_ai_ci_data_test.go similarity index 100% rename from util/collate/ucadata/unicode_0900_ai_ci_data_test.go rename to pkg/util/collate/ucadata/unicode_0900_ai_ci_data_test.go diff --git a/util/collate/ucadata/unicode_ci_data_generated.go b/pkg/util/collate/ucadata/unicode_ci_data_generated.go similarity index 100% rename from util/collate/ucadata/unicode_ci_data_generated.go rename to pkg/util/collate/ucadata/unicode_ci_data_generated.go diff --git a/util/collate/ucadata/unicode_ci_data_original_test.go b/pkg/util/collate/ucadata/unicode_ci_data_original_test.go similarity index 100% rename from util/collate/ucadata/unicode_ci_data_original_test.go rename to pkg/util/collate/ucadata/unicode_ci_data_original_test.go diff --git a/util/collate/ucadata/unicode_ci_data_test.go b/pkg/util/collate/ucadata/unicode_ci_data_test.go similarity index 100% rename from util/collate/ucadata/unicode_ci_data_test.go rename to pkg/util/collate/ucadata/unicode_ci_data_test.go diff --git a/pkg/util/collate/ucaimpl/BUILD.bazel b/pkg/util/collate/ucaimpl/BUILD.bazel new file mode 100644 index 0000000000000..fc48a5930bef7 --- /dev/null +++ b/pkg/util/collate/ucaimpl/BUILD.bazel @@ -0,0 +1,15 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "ucaimpl_lib", + srcs = ["main.go"], + embedsrcs = ["unicode_ci.go.tpl"], + importpath = "github.com/pingcap/tidb/pkg/util/collate/ucaimpl", + visibility = ["//visibility:private"], +) + +go_binary( + name = "ucaimpl", + embed = [":ucaimpl_lib"], + visibility = ["//visibility:public"], +) diff --git a/util/collate/ucaimpl/main.go b/pkg/util/collate/ucaimpl/main.go similarity index 100% rename from util/collate/ucaimpl/main.go rename to pkg/util/collate/ucaimpl/main.go diff --git a/util/collate/ucaimpl/unicode_ci.go.tpl b/pkg/util/collate/ucaimpl/unicode_ci.go.tpl similarity index 100% rename from util/collate/ucaimpl/unicode_ci.go.tpl rename to pkg/util/collate/ucaimpl/unicode_ci.go.tpl diff --git a/util/collate/unicode_0400_ci_generated.go b/pkg/util/collate/unicode_0400_ci_generated.go similarity index 100% rename from util/collate/unicode_0400_ci_generated.go rename to pkg/util/collate/unicode_0400_ci_generated.go diff --git a/util/collate/unicode_0400_ci_impl.go b/pkg/util/collate/unicode_0400_ci_impl.go similarity index 95% rename from util/collate/unicode_0400_ci_impl.go rename to pkg/util/collate/unicode_0400_ci_impl.go index 7def2b4731bcb..731c095736e18 100644 --- a/util/collate/unicode_0400_ci_impl.go +++ b/pkg/util/collate/unicode_0400_ci_impl.go @@ -15,8 +15,8 @@ package collate import ( - "github.com/pingcap/tidb/util/collate/ucadata" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/collate/ucadata" + "github.com/pingcap/tidb/pkg/util/stringutil" ) const ( diff --git a/util/collate/unicode_0900_ai_ci_generated.go b/pkg/util/collate/unicode_0900_ai_ci_generated.go similarity index 100% rename from util/collate/unicode_0900_ai_ci_generated.go rename to pkg/util/collate/unicode_0900_ai_ci_generated.go diff --git a/util/collate/unicode_0900_ai_ci_impl.go b/pkg/util/collate/unicode_0900_ai_ci_impl.go similarity index 95% rename from util/collate/unicode_0900_ai_ci_impl.go rename to pkg/util/collate/unicode_0900_ai_ci_impl.go index 7f2b03a268077..4981b46430e87 100644 --- a/util/collate/unicode_0900_ai_ci_impl.go +++ b/pkg/util/collate/unicode_0900_ai_ci_impl.go @@ -15,8 +15,8 @@ package collate import ( - "github.com/pingcap/tidb/util/collate/ucadata" - "github.com/pingcap/tidb/util/stringutil" + "github.com/pingcap/tidb/pkg/util/collate/ucadata" + "github.com/pingcap/tidb/pkg/util/stringutil" ) //go:generate go run ./ucaimpl/main.go -- unicode_0900_ai_ci_generated.go diff --git a/pkg/util/column-mapping/BUILD.bazel b/pkg/util/column-mapping/BUILD.bazel new file mode 100644 index 0000000000000..7c93ae318fba6 --- /dev/null +++ b/pkg/util/column-mapping/BUILD.bazel @@ -0,0 +1,21 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "column-mapping", + srcs = ["column.go"], + importpath = "github.com/pingcap/tidb/pkg/util/column-mapping", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/table-rule-selector", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "column-mapping_test", + timeout = "short", + srcs = ["column_test.go"], + embed = [":column-mapping"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/column-mapping/README.md b/pkg/util/column-mapping/README.md similarity index 100% rename from util/column-mapping/README.md rename to pkg/util/column-mapping/README.md diff --git a/pkg/util/column-mapping/column.go b/pkg/util/column-mapping/column.go new file mode 100644 index 0000000000000..7acac1b925a1c --- /dev/null +++ b/pkg/util/column-mapping/column.go @@ -0,0 +1,539 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package column + +import ( + "fmt" + "strconv" + "strings" + "sync" + + "github.com/pingcap/errors" + selector "github.com/pingcap/tidb/pkg/util/table-rule-selector" +) + +var ( + // for partition ID, ref definition of partitionID + instanceIDBitSize = 4 + schemaIDBitSize = 7 + tableIDBitSize = 8 + maxOriginID int64 = 17592186044416 +) + +// SetPartitionRule sets bit size of schema ID and table ID +func SetPartitionRule(instanceIDSize, schemaIDSize, tableIDSize int) { + instanceIDBitSize = instanceIDSize + schemaIDBitSize = schemaIDSize + tableIDBitSize = tableIDSize + maxOriginID = 1 << uint(64-instanceIDSize-schemaIDSize-tableIDSize-1) +} + +// Expr indicates how to handle column mapping +type Expr string + +// poor Expr +const ( + AddPrefix Expr = "add prefix" + AddSuffix Expr = "add suffix" + PartitionID Expr = "partition id" +) + +// Exprs is some built-in expression for column mapping +// only supports some poor expressions now, +// we would unify tableInfo later and support more +var Exprs = map[Expr]func(*mappingInfo, []interface{}) ([]interface{}, error){ + AddPrefix: addPrefix, // arguments contains prefix + AddSuffix: addSuffix, // arguments contains suffix + // arguments contains [instance_id, prefix of schema, prefix of table] + // we would compute a ID like + // [1:1 bit][2:9 bits][3:10 bits][4:44 bits] int64 (using default bits length) + // # 1 useless, no reason + // # 2 schema ID (schema suffix) + // # 3 table ID (table suffix) + // # 4 origin ID (>= 0, <= 17592186044415) + // + // others: schema = arguments[1] or arguments[1] + arguments[3] + schema suffix + // table = arguments[2] or arguments[2] + arguments[3] + table suffix + // example: schema = schema_1 table = t_1 + // => arguments[1] = "schema", arguments[2] = "t", arguments[3] = "_" + // if arguments[1]/arguments[2] == "", it means we don't use schemaID/tableID to compute partition ID + // if length of arguments is < 4, arguments[3] is set to "" (empty string) + PartitionID: partitionID, +} + +// Rule is a rule to map column +// TODO: we will do it later, if we need to implement a real column mapping, we need table structure of source and target system +type Rule struct { + PatternSchema string `yaml:"schema-pattern" json:"schema-pattern" toml:"schema-pattern"` + PatternTable string `yaml:"table-pattern" json:"table-pattern" toml:"table-pattern"` + SourceColumn string `yaml:"source-column" json:"source-column" toml:"source-column"` // modify, add refer column, ignore + TargetColumn string `yaml:"target-column" json:"target-column" toml:"target-column"` // add column, modify + Expression Expr `yaml:"expression" json:"expression" toml:"expression"` + Arguments []string `yaml:"arguments" json:"arguments" toml:"arguments"` + CreateTableQuery string `yaml:"create-table-query" json:"create-table-query" toml:"create-table-query"` +} + +// ToLower covert schema/table parttern to lower case +func (r *Rule) ToLower() { + r.PatternSchema = strings.ToLower(r.PatternSchema) + r.PatternTable = strings.ToLower(r.PatternTable) +} + +// Valid checks validity of rule. +// add prefix/suffix: it should have target column and one argument +// partition id: it should have 3 to 4 arguments +func (r *Rule) Valid() error { + if _, ok := Exprs[r.Expression]; !ok { + return errors.NotFoundf("expression %s", r.Expression) + } + + if r.TargetColumn == "" { + return errors.NotValidf("rule need to be applied a target column") + } + + if r.Expression == AddPrefix || r.Expression == AddSuffix { + if len(r.Arguments) != 1 { + return errors.NotValidf("arguments %v for add prefix/suffix", r.Arguments) + } + } + + if r.Expression == PartitionID { + switch len(r.Arguments) { + case 3, 4: + return nil + default: + return errors.NotValidf("arguments %v for patition id", r.Arguments) + } + } + + return nil +} + +// Adjust normalizes the rule into an easier-to-process form, e.g. filling in +// optional arguments with the default values. +func (r *Rule) Adjust() { + if r.Expression == PartitionID && len(r.Arguments) == 3 { + r.Arguments = append(r.Arguments, "") + } +} + +// check source and target position +func (r *Rule) adjustColumnPosition(source, target int) (src int, targ int, err error) { + // if not found target, ignore it + if target == -1 { + return source, target, errors.NotFoundf("target column %s", r.TargetColumn) + } + + return source, target, nil +} + +type mappingInfo struct { + ignore bool + sourcePosition int + targetPosition int + rule *Rule + + instanceID int64 + schemaID int64 + tableID int64 +} + +// Mapping maps column to something by rules +type Mapping struct { + selector.Selector + + caseSensitive bool + + cache struct { + sync.RWMutex + infos map[string]*mappingInfo + } +} + +// NewMapping returns a column mapping +func NewMapping(caseSensitive bool, rules []*Rule) (*Mapping, error) { + m := &Mapping{ + Selector: selector.NewTrieSelector(), + caseSensitive: caseSensitive, + } + m.resetCache() + + for _, rule := range rules { + if err := m.AddRule(rule); err != nil { + return nil, errors.Annotatef(err, "initial rule %+v in mapping", rule) + } + } + + return m, nil +} + +func (m *Mapping) addOrUpdateRule(rule *Rule, isUpdate bool) error { + if m == nil || rule == nil { + return nil + } + + err := rule.Valid() + if err != nil { + return errors.Trace(err) + } + if !m.caseSensitive { + rule.ToLower() + } + rule.Adjust() + + m.resetCache() + if isUpdate { + err = m.Insert(rule.PatternSchema, rule.PatternTable, rule, selector.Replace) + } else { + err = m.Insert(rule.PatternSchema, rule.PatternTable, rule, selector.Insert) + } + if err != nil { + var method string + if isUpdate { + method = "update" + } else { + method = "add" + } + return errors.Annotatef(err, "%s rule %+v into mapping", method, rule) + } + + return nil +} + +// AddRule adds a rule into mapping +func (m *Mapping) AddRule(rule *Rule) error { + return m.addOrUpdateRule(rule, false) +} + +// UpdateRule updates mapping rule +func (m *Mapping) UpdateRule(rule *Rule) error { + return m.addOrUpdateRule(rule, true) +} + +// RemoveRule removes a rule from mapping +func (m *Mapping) RemoveRule(rule *Rule) error { + if m == nil || rule == nil { + return nil + } + if !m.caseSensitive { + rule.ToLower() + } + + m.resetCache() + err := m.Remove(rule.PatternSchema, rule.PatternTable) + if err != nil { + return errors.Annotatef(err, "remove rule %+v from mapping", rule) + } + + return nil +} + +// HandleRowValue handles row value +func (m *Mapping) HandleRowValue(schema, table string, columns []string, vals []interface{}) ([]interface{}, []int, error) { + if m == nil { + return vals, nil, nil + } + + schemaL, tableL := schema, table + if !m.caseSensitive { + schemaL, tableL = strings.ToLower(schema), strings.ToLower(table) + } + + info, err := m.queryColumnInfo(schemaL, tableL, columns) + if err != nil { + return nil, nil, errors.Trace(err) + } + if info.ignore { + return vals, nil, nil + } + + exp, ok := Exprs[info.rule.Expression] + if !ok { + return nil, nil, errors.NotFoundf("column mapping expression %s", info.rule.Expression) + } + + vals, err = exp(info, vals) + if err != nil { + return nil, nil, errors.Trace(err) + } + + return vals, []int{info.sourcePosition, info.targetPosition}, nil +} + +// HandleDDL handles ddl +func (m *Mapping) HandleDDL(schema, table string, columns []string, statement string) (string, []int, error) { + if m == nil { + return statement, nil, nil + } + + schemaL, tableL := schema, table + if !m.caseSensitive { + schemaL, tableL = strings.ToLower(schema), strings.ToLower(table) + } + + info, err := m.queryColumnInfo(schemaL, tableL, columns) + if err != nil { + return statement, nil, errors.Trace(err) + } + + if info.ignore { + return statement, nil, nil + } + + m.resetCache() + // only output erro now, wait fix it manually + return statement, nil, errors.Errorf("ddl %s @ column mapping rule %s/%s:%+v not implemented", statement, schema, table, info.rule) +} + +func (m *Mapping) queryColumnInfo(schema, table string, columns []string) (*mappingInfo, error) { + m.cache.RLock() + ci, ok := m.cache.infos[tableName(schema, table)] + m.cache.RUnlock() + if ok { + return ci, nil + } + + var info = &mappingInfo{ + ignore: true, + } + rules := m.Match(schema, table) + if len(rules) == 0 { + m.cache.Lock() + m.cache.infos[tableName(schema, table)] = info + m.cache.Unlock() + + return info, nil + } + + var ( + schemaRules []*Rule + tableRules = make([]*Rule, 0, 1) + ) + // classify rules into schema level rules and table level + // table level rules have highest priority + for i := range rules { + rule, ok := rules[i].(*Rule) + if !ok { + return nil, errors.NotValidf("column mapping rule %+v", rules[i]) + } + + if len(rule.PatternTable) == 0 { + schemaRules = append(schemaRules, rule) + } else { + tableRules = append(tableRules, rule) + } + } + + // only support one expression for one table now, refine it later + var rule *Rule + if len(table) == 0 || len(tableRules) == 0 { + if len(schemaRules) != 1 { + return nil, errors.NotSupportedf("`%s`.`%s` matches %d schema column mapping rules which should be one. It's", schema, table, len(schemaRules)) + } + + rule = schemaRules[0] + } else { + if len(tableRules) != 1 { + return nil, errors.NotSupportedf("`%s`.`%s` matches %d table column mapping rules which should be one. It's", schema, table, len(tableRules)) + } + + rule = tableRules[0] + } + if rule == nil { + m.cache.Lock() + m.cache.infos[tableName(schema, table)] = info + m.cache.Unlock() + + return info, nil + } + + // compute source and target column position + sourcePosition := findColumnPosition(columns, rule.SourceColumn) + targetPosition := findColumnPosition(columns, rule.TargetColumn) + + sourcePosition, targetPosition, err := rule.adjustColumnPosition(sourcePosition, targetPosition) + if err != nil { + return nil, errors.Trace(err) + } + + info = &mappingInfo{ + sourcePosition: sourcePosition, + targetPosition: targetPosition, + rule: rule, + } + + // if expr is partition ID, compute schema and table ID + if rule.Expression == PartitionID { + info.instanceID, info.schemaID, info.tableID, err = computePartitionID(schema, table, rule) + if err != nil { + return nil, errors.Trace(err) + } + } + + m.cache.Lock() + m.cache.infos[tableName(schema, table)] = info + m.cache.Unlock() + + return info, nil +} + +func (m *Mapping) resetCache() { + m.cache.Lock() + m.cache.infos = make(map[string]*mappingInfo) + m.cache.Unlock() +} + +func findColumnPosition(cols []string, col string) int { + for i := range cols { + if cols[i] == col { + return i + } + } + + return -1 +} + +func tableName(schema, table string) string { + return fmt.Sprintf("`%s`.`%s`", schema, table) +} + +func addPrefix(info *mappingInfo, vals []interface{}) ([]interface{}, error) { + prefix := info.rule.Arguments[0] + originStr, ok := vals[info.targetPosition].(string) + if !ok { + return nil, errors.NotValidf("column %d value is not string, but %v, which is", info.targetPosition, vals[info.targetPosition]) + } + + // fast to concatenated string + rawByte := make([]byte, 0, len(prefix)+len(originStr)) + rawByte = append(rawByte, prefix...) + rawByte = append(rawByte, originStr...) + + vals[info.targetPosition] = string(rawByte) + return vals, nil +} + +func addSuffix(info *mappingInfo, vals []interface{}) ([]interface{}, error) { + suffix := info.rule.Arguments[0] + originStr, ok := vals[info.targetPosition].(string) + if !ok { + return nil, errors.NotValidf("column %d value is not string, but %v, which is", info.targetPosition, vals[info.targetPosition]) + } + + rawByte := make([]byte, 0, len(suffix)+len(originStr)) + rawByte = append(rawByte, originStr...) + rawByte = append(rawByte, suffix...) + + vals[info.targetPosition] = string(rawByte) + return vals, nil +} + +func partitionID(info *mappingInfo, vals []interface{}) ([]interface{}, error) { + // only int64 now + var ( + originID int64 + err error + isChars bool + ) + + switch rawID := vals[info.targetPosition].(type) { + case int: + originID = int64(rawID) + case int8: + originID = int64(rawID) + case int32: + originID = int64(rawID) + case int64: + originID = rawID + case uint: + originID = int64(rawID) + case uint16: + originID = int64(rawID) + case uint32: + originID = int64(rawID) + case uint64: + originID = int64(rawID) + case string: + originID, err = strconv.ParseInt(rawID, 10, 64) + if err != nil { + return nil, errors.NotValidf("column %d value is not int, but %v, which is", info.targetPosition, vals[info.targetPosition]) + } + isChars = true + default: + return nil, errors.NotValidf("type %T(%v)", vals[info.targetPosition], vals[info.targetPosition]) + } + + if originID >= maxOriginID || originID < 0 { + return nil, errors.NotValidf("id must less than %d, greater than or equal to 0, but got %d, which is", maxOriginID, originID) + } + + originID = info.instanceID | info.schemaID | info.tableID | originID + if isChars { + vals[info.targetPosition] = strconv.FormatInt(originID, 10) + } else { + vals[info.targetPosition] = originID + } + + return vals, nil +} + +func computePartitionID(schema, table string, rule *Rule) (instanceID int64, schemaID int64, tableID int64, err error) { + shiftCnt := uint(63) + if instanceIDBitSize > 0 && len(rule.Arguments[0]) > 0 { + var instanceIDUnsign uint64 + shiftCnt = shiftCnt - uint(instanceIDBitSize) + instanceIDUnsign, err = strconv.ParseUint(rule.Arguments[0], 10, instanceIDBitSize) + if err != nil { + return + } + instanceID = int64(instanceIDUnsign << shiftCnt) + } + + sep := rule.Arguments[3] + + if schemaIDBitSize > 0 && len(rule.Arguments[1]) > 0 { + shiftCnt = shiftCnt - uint(schemaIDBitSize) + schemaID, err = computeID(schema, rule.Arguments[1], sep, schemaIDBitSize, shiftCnt) + if err != nil { + return + } + } + + if tableIDBitSize > 0 && len(rule.Arguments[2]) > 0 { + shiftCnt = shiftCnt - uint(tableIDBitSize) + tableID, err = computeID(table, rule.Arguments[2], sep, tableIDBitSize, shiftCnt) + } + + return +} + +func computeID(name string, prefix, sep string, bitSize int, shiftCount uint) (int64, error) { + if name == prefix { + return 0, nil + } + + prefix += sep + if len(prefix) >= len(name) || prefix != name[:len(prefix)] { + return 0, errors.NotValidf("%s is not the prefix of %s", prefix, name) + } + + idStr := name[len(prefix):] + id, err := strconv.ParseUint(idStr, 10, bitSize) + if err != nil { + return 0, errors.NotValidf("the suffix of %s can't be converted to int64", idStr) + } + + return int64(id << shiftCount), nil +} diff --git a/util/column-mapping/column_test.go b/pkg/util/column-mapping/column_test.go similarity index 100% rename from util/column-mapping/column_test.go rename to pkg/util/column-mapping/column_test.go diff --git a/pkg/util/compress/BUILD.bazel b/pkg/util/compress/BUILD.bazel new file mode 100644 index 0000000000000..f00125ae87b37 --- /dev/null +++ b/pkg/util/compress/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "compress", + srcs = ["gzip.go"], + importpath = "github.com/pingcap/tidb/pkg/util/compress", + visibility = ["//visibility:public"], + deps = ["@com_github_klauspost_compress//gzip"], +) diff --git a/util/compress/gzip.go b/pkg/util/compress/gzip.go similarity index 100% rename from util/compress/gzip.go rename to pkg/util/compress/gzip.go diff --git a/pkg/util/cpu/BUILD.bazel b/pkg/util/cpu/BUILD.bazel new file mode 100644 index 0000000000000..50332f6fbaeec --- /dev/null +++ b/pkg/util/cpu/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cpu", + srcs = ["cpu.go"], + importpath = "github.com/pingcap/tidb/pkg/util/cpu", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/util/cgroup", + "//pkg/util/mathutil", + "@com_github_cloudfoundry_gosigar//:gosigar", + "@com_github_pingcap_log//:log", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "cpu_test", + timeout = "short", + srcs = [ + "cpu_test.go", + "main_test.go", + ], + flaky = True, + race = "on", + shard_count = 2, + deps = [ + ":cpu", + "//pkg/resourcemanager/scheduler", + "//pkg/resourcemanager/util", + "//pkg/testkit/testsetup", + "//pkg/util/cgroup", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/cpu/cpu.go b/pkg/util/cpu/cpu.go new file mode 100644 index 0000000000000..4be66d05421a7 --- /dev/null +++ b/pkg/util/cpu/cpu.go @@ -0,0 +1,122 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cpu + +import ( + "os" + "sync" + "time" + + sigar "github.com/cloudfoundry/gosigar" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/cgroup" + "github.com/pingcap/tidb/pkg/util/mathutil" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +var cpuUsage atomic.Float64 + +// If your kernel is lower than linux 4.7, you cannot get the cpu usage in the container. +var unsupported atomic.Bool + +// GetCPUUsage returns the cpu usage of the current process. +func GetCPUUsage() (float64, bool) { + return cpuUsage.Load(), unsupported.Load() +} + +// Observer is used to observe the cpu usage of the current process. +type Observer struct { + utime int64 + stime int64 + now int64 + exit chan struct{} + cpu mathutil.ExponentialMovingAverage + wg sync.WaitGroup +} + +// NewCPUObserver returns a cpu observer. +func NewCPUObserver() *Observer { + return &Observer{ + exit: make(chan struct{}), + now: time.Now().UnixNano(), + cpu: *mathutil.NewExponentialMovingAverage(0.95, 10), + } +} + +// Start starts the cpu observer. +func (c *Observer) Start() { + _, err := cgroup.GetCgroupCPU() + if err != nil { + unsupported.Store(true) + log.Error("GetCgroupCPU", zap.Error(err)) + return + } + c.wg.Add(1) + go func() { + ticker := time.NewTicker(100 * time.Millisecond) + defer func() { + ticker.Stop() + c.wg.Done() + }() + for { + select { + case <-ticker.C: + curr := c.observe() + c.cpu.Add(curr) + cpuUsage.Store(c.cpu.Get()) + metrics.EMACPUUsageGauge.Set(c.cpu.Get()) + case <-c.exit: + return + } + } + }() +} + +// Stop stops the cpu observer. +func (c *Observer) Stop() { + close(c.exit) + c.wg.Wait() +} + +func (c *Observer) observe() float64 { + user, sys, err := getCPUTime() + if err != nil { + log.Error("getCPUTime", zap.Error(err)) + } + cgroupCPU, _ := cgroup.GetCgroupCPU() + cpuShare := cgroupCPU.CPUShares() + now := time.Now().UnixNano() + dur := float64(now - c.now) + utime := user * 1e6 + stime := sys * 1e6 + urate := float64(utime-c.utime) / dur + srate := float64(stime-c.stime) / dur + c.now = now + c.utime = utime + c.stime = stime + return (srate + urate) / cpuShare +} + +// getCPUTime returns the cumulative user/system time (in ms) since the process start. +func getCPUTime() (userTimeMillis, sysTimeMillis int64, err error) { + pid := os.Getpid() + cpuTime := sigar.ProcTime{} + if err := cpuTime.Get(pid); err != nil { + return 0, 0, err + } + return int64(cpuTime.User), int64(cpuTime.Sys), nil +} diff --git a/util/cpu/cpu_test.go b/pkg/util/cpu/cpu_test.go similarity index 86% rename from util/cpu/cpu_test.go rename to pkg/util/cpu/cpu_test.go index 706bb84707047..d520b367d4007 100644 --- a/util/cpu/cpu_test.go +++ b/pkg/util/cpu/cpu_test.go @@ -21,10 +21,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/resourcemanager/scheduler" - "github.com/pingcap/tidb/resourcemanager/util" - "github.com/pingcap/tidb/util/cgroup" - "github.com/pingcap/tidb/util/cpu" + "github.com/pingcap/tidb/pkg/resourcemanager/scheduler" + "github.com/pingcap/tidb/pkg/resourcemanager/util" + "github.com/pingcap/tidb/pkg/util/cgroup" + "github.com/pingcap/tidb/pkg/util/cpu" "github.com/stretchr/testify/require" ) @@ -65,9 +65,9 @@ func TestCPUValue(t *testing.T) { } func TestFailpointCPUValue(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/util/cgroup/GetCgroupCPUErr", "return(true)") + failpoint.Enable("github.com/pingcap/tidb/pkg/util/cgroup/GetCgroupCPUErr", "return(true)") defer func() { - failpoint.Disable("github.com/pingcap/tidb/util/cgroup/GetCgroupCPUErr") + failpoint.Disable("github.com/pingcap/tidb/pkg/util/cgroup/GetCgroupCPUErr") }() observer := cpu.NewCPUObserver() exit := make(chan struct{}) diff --git a/pkg/util/cpu/main_test.go b/pkg/util/cpu/main_test.go new file mode 100644 index 0000000000000..f0dadb7da4c73 --- /dev/null +++ b/pkg/util/cpu/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package cpu_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/cpu_posix.go b/pkg/util/cpu_posix.go similarity index 100% rename from util/cpu_posix.go rename to pkg/util/cpu_posix.go diff --git a/util/cpu_windows.go b/pkg/util/cpu_windows.go similarity index 100% rename from util/cpu_windows.go rename to pkg/util/cpu_windows.go diff --git a/pkg/util/cpuprofile/BUILD.bazel b/pkg/util/cpuprofile/BUILD.bazel new file mode 100644 index 0000000000000..b129a73dd6d9b --- /dev/null +++ b/pkg/util/cpuprofile/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cpuprofile", + srcs = [ + "cpuprofile.go", + "pprof_api.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/cpuprofile", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_google_pprof//profile", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "cpuprofile_test", + timeout = "short", + srcs = ["cpuprofile_test.go"], + embed = [":cpuprofile"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/cpuprofile/testutil", + "@com_github_google_pprof//profile", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/cpuprofile/cpuprofile.go b/pkg/util/cpuprofile/cpuprofile.go similarity index 97% rename from util/cpuprofile/cpuprofile.go rename to pkg/util/cpuprofile/cpuprofile.go index 8201b963702e2..9ecda5d677d56 100644 --- a/util/cpuprofile/cpuprofile.go +++ b/pkg/util/cpuprofile/cpuprofile.go @@ -22,9 +22,9 @@ import ( "sync" "time" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/cpuprofile/cpuprofile_test.go b/pkg/util/cpuprofile/cpuprofile_test.go similarity index 98% rename from util/cpuprofile/cpuprofile_test.go rename to pkg/util/cpuprofile/cpuprofile_test.go index b20428dcf21fd..f8d3cc0925f0f 100644 --- a/util/cpuprofile/cpuprofile_test.go +++ b/pkg/util/cpuprofile/cpuprofile_test.go @@ -26,8 +26,8 @@ import ( "time" "github.com/google/pprof/profile" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/cpuprofile/testutil" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/cpuprofile/testutil" "github.com/stretchr/testify/require" "go.uber.org/goleak" ) diff --git a/util/cpuprofile/pprof_api.go b/pkg/util/cpuprofile/pprof_api.go similarity index 98% rename from util/cpuprofile/pprof_api.go rename to pkg/util/cpuprofile/pprof_api.go index 25d6dab363982..24cc487d33b39 100644 --- a/util/cpuprofile/pprof_api.go +++ b/pkg/util/cpuprofile/pprof_api.go @@ -25,8 +25,8 @@ import ( "time" "github.com/google/pprof/profile" - goutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + goutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/util/cpuprofile/testutil/BUILD.bazel b/pkg/util/cpuprofile/testutil/BUILD.bazel new file mode 100644 index 0000000000000..b7a148358fb6b --- /dev/null +++ b/pkg/util/cpuprofile/testutil/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "testutil", + srcs = ["util.go"], + importpath = "github.com/pingcap/tidb/pkg/util/cpuprofile/testutil", + visibility = ["//visibility:public"], +) diff --git a/util/cpuprofile/testutil/util.go b/pkg/util/cpuprofile/testutil/util.go similarity index 100% rename from util/cpuprofile/testutil/util.go rename to pkg/util/cpuprofile/testutil/util.go diff --git a/pkg/util/cteutil/BUILD.bazel b/pkg/util/cteutil/BUILD.bazel new file mode 100644 index 0000000000000..eaeb81968a4ba --- /dev/null +++ b/pkg/util/cteutil/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "cteutil", + srcs = ["storage.go"], + importpath = "github.com/pingcap/tidb/pkg/util/cteutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/disk", + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "cteutil_test", + timeout = "short", + srcs = [ + "main_test.go", + "storage_test.go", + ], + embed = [":cteutil"], + flaky = True, + deps = [ + "//pkg/parser/mysql", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/chunk", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/cteutil/main_test.go b/pkg/util/cteutil/main_test.go new file mode 100644 index 0000000000000..6ad7525828b32 --- /dev/null +++ b/pkg/util/cteutil/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cteutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/cteutil/storage.go b/pkg/util/cteutil/storage.go new file mode 100644 index 0000000000000..a3f334794b2fc --- /dev/null +++ b/pkg/util/cteutil/storage.go @@ -0,0 +1,271 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package cteutil + +import ( + "sync" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/memory" +) + +var _ Storage = &StorageRC{} + +// Storage is a temporary storage to store the intermidate data of CTE. +// +// Common usage as follows: +// +// storage.Lock() +// if !storage.Done() { +// fill all data into storage +// } +// storage.UnLock() +// read data from storage +type Storage interface { + // If is first called, will open underlying storage. Otherwise will add ref count by one. + OpenAndRef() error + + // Minus ref count by one, if ref count is zero, close underlying storage. + DerefAndClose() (err error) + + // SwapData swaps data of two storage. + // Other metainfo is not touched, such ref count/done flag etc. + SwapData(other Storage) error + + // Reopen reset storage and related info. + // So the status of Storage is like a new created one. + Reopen() error + + // Add chunk into underlying storage. + // Should return directly if chk is empty. + Add(chk *chunk.Chunk) error + + // Get Chunk by index. + GetChunk(chkIdx int) (*chunk.Chunk, error) + + // Get row by RowPtr. + GetRow(ptr chunk.RowPtr) (chunk.Row, error) + + // NumChunks return chunk number of the underlying storage. + NumChunks() int + + // NumRows return row number of the underlying storage. + NumRows() int + + // Storage is not thread-safe. + // By using Lock(), users can achieve the purpose of ensuring thread safety. + Lock() + Unlock() + + // Usually, Storage is filled first, then user can read it. + // User can check whether Storage is filled first, if not, they can fill it. + Done() bool + SetDone() + + // Store error message, so we can return directly. + Error() error + SetError(err error) + + // Readers use iter information to determine + // whether they need to read data from the beginning. + SetIter(iter int) + GetIter() int + + GetMemTracker() *memory.Tracker + GetDiskTracker() *disk.Tracker + ActionSpill() *chunk.SpillDiskAction +} + +// StorageRC implements Storage interface using RowContainer. +type StorageRC struct { + err error + rc *chunk.RowContainer + tp []*types.FieldType + refCnt int + chkSize int + iter int + mu sync.Mutex + done bool +} + +// NewStorageRowContainer create a new StorageRC. +func NewStorageRowContainer(tp []*types.FieldType, chkSize int) *StorageRC { + return &StorageRC{tp: tp, chkSize: chkSize} +} + +// OpenAndRef impls Storage OpenAndRef interface. +func (s *StorageRC) OpenAndRef() (err error) { + if !s.valid() { + s.rc = chunk.NewRowContainer(s.tp, s.chkSize) + s.refCnt = 1 + s.iter = 0 + } else { + s.refCnt++ + } + return nil +} + +// DerefAndClose impls Storage DerefAndClose interface. +func (s *StorageRC) DerefAndClose() (err error) { + if !s.valid() { + return errors.New("Storage not opend yet") + } + s.refCnt-- + if s.refCnt < 0 { + return errors.New("Storage ref count is less than zero") + } else if s.refCnt == 0 { + s.refCnt = -1 + s.done = false + s.err = nil + s.iter = 0 + if err = s.rc.Close(); err != nil { + return err + } + s.rc = nil + } + return nil +} + +// SwapData impls Storage Swap interface. +func (s *StorageRC) SwapData(other Storage) (err error) { + otherRC, ok := other.(*StorageRC) + if !ok { + return errors.New("cannot swap if underlying storages are different") + } + s.tp, otherRC.tp = otherRC.tp, s.tp + s.chkSize, otherRC.chkSize = otherRC.chkSize, s.chkSize + + s.rc, otherRC.rc = otherRC.rc, s.rc + return nil +} + +// Reopen impls Storage Reopen interface. +func (s *StorageRC) Reopen() (err error) { + if err = s.rc.Close(); err != nil { + return err + } + s.iter = 0 + s.done = false + s.err = nil + // Create a new RowContainer. + // Because some meta infos in old RowContainer are not resetted. + // Such as memTracker/actionSpill etc. So we just use a new one. + s.rc = chunk.NewRowContainer(s.tp, s.chkSize) + return nil +} + +// Add impls Storage Add interface. +func (s *StorageRC) Add(chk *chunk.Chunk) (err error) { + if !s.valid() { + return errors.New("Storage is not valid") + } + if chk.NumRows() == 0 { + return nil + } + return s.rc.Add(chk) +} + +// GetChunk impls Storage GetChunk interface. +func (s *StorageRC) GetChunk(chkIdx int) (*chunk.Chunk, error) { + if !s.valid() { + return nil, errors.New("Storage is not valid") + } + return s.rc.GetChunk(chkIdx) +} + +// GetRow impls Storage GetRow interface. +func (s *StorageRC) GetRow(ptr chunk.RowPtr) (chunk.Row, error) { + if !s.valid() { + return chunk.Row{}, errors.New("Storage is not valid") + } + return s.rc.GetRow(ptr) +} + +// NumChunks impls Storage NumChunks interface. +func (s *StorageRC) NumChunks() int { + return s.rc.NumChunks() +} + +// NumRows impls Storage NumRows interface. +func (s *StorageRC) NumRows() int { + return s.rc.NumRow() +} + +// Lock impls Storage Lock interface. +func (s *StorageRC) Lock() { + s.mu.Lock() +} + +// Unlock impls Storage Unlock interface. +func (s *StorageRC) Unlock() { + s.mu.Unlock() +} + +// Done impls Storage Done interface. +func (s *StorageRC) Done() bool { + return s.done +} + +// SetDone impls Storage SetDone interface. +func (s *StorageRC) SetDone() { + s.done = true +} + +// Error impls Storage Error interface. +func (s *StorageRC) Error() error { + return s.err +} + +// SetError impls Storage SetError interface. +func (s *StorageRC) SetError(err error) { + s.err = err +} + +// SetIter impls Storage SetIter interface. +func (s *StorageRC) SetIter(iter int) { + s.iter = iter +} + +// GetIter impls Storage GetIter interface. +func (s *StorageRC) GetIter() int { + return s.iter +} + +// GetMemTracker impls Storage GetMemTracker interface. +func (s *StorageRC) GetMemTracker() *memory.Tracker { + return s.rc.GetMemTracker() +} + +// GetDiskTracker impls Storage GetDiskTracker interface. +func (s *StorageRC) GetDiskTracker() *memory.Tracker { + return s.rc.GetDiskTracker() +} + +// ActionSpill impls Storage ActionSpill interface. +func (s *StorageRC) ActionSpill() *chunk.SpillDiskAction { + return s.rc.ActionSpill() +} + +// ActionSpillForTest is for test. +func (s *StorageRC) ActionSpillForTest() *chunk.SpillDiskAction { + return s.rc.ActionSpillForTest() +} + +func (s *StorageRC) valid() bool { + return s.refCnt > 0 && s.rc != nil +} diff --git a/util/cteutil/storage_test.go b/pkg/util/cteutil/storage_test.go similarity index 98% rename from util/cteutil/storage_test.go rename to pkg/util/cteutil/storage_test.go index c64d77d229ef3..fab50d9b0cb45 100644 --- a/util/cteutil/storage_test.go +++ b/pkg/util/cteutil/storage_test.go @@ -18,9 +18,9 @@ import ( "strconv" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/dbterror/BUILD.bazel b/pkg/util/dbterror/BUILD.bazel new file mode 100644 index 0000000000000..188fe9af542e3 --- /dev/null +++ b/pkg/util/dbterror/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "dbterror", + srcs = [ + "ddl_terror.go", + "terror.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/dbterror", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/parser/mysql", + "//pkg/parser/terror", + ], +) + +go_test( + name = "dbterror_test", + timeout = "short", + srcs = [ + "main_test.go", + "terror_test.go", + ], + embed = [":dbterror"], + flaky = True, + deps = [ + "//pkg/errno", + "//pkg/testkit/testsetup", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/dbterror/ddl_terror.go b/pkg/util/dbterror/ddl_terror.go similarity index 99% rename from util/dbterror/ddl_terror.go rename to pkg/util/dbterror/ddl_terror.go index 80d239c86b408..de98d1b9912b1 100644 --- a/util/dbterror/ddl_terror.go +++ b/pkg/util/dbterror/ddl_terror.go @@ -17,8 +17,8 @@ package dbterror import ( "fmt" - mysql "github.com/pingcap/tidb/errno" - parser_mysql "github.com/pingcap/tidb/parser/mysql" + mysql "github.com/pingcap/tidb/pkg/errno" + parser_mysql "github.com/pingcap/tidb/pkg/parser/mysql" ) var ( diff --git a/pkg/util/dbterror/exeerrors/BUILD.bazel b/pkg/util/dbterror/exeerrors/BUILD.bazel new file mode 100644 index 0000000000000..cd9bd87a6d1dd --- /dev/null +++ b/pkg/util/dbterror/exeerrors/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "exeerrors", + srcs = ["errors.go"], + importpath = "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/parser/mysql", + "//pkg/util/dbterror", + ], +) diff --git a/pkg/util/dbterror/exeerrors/errors.go b/pkg/util/dbterror/exeerrors/errors.go new file mode 100644 index 0000000000000..9d3c602a2d781 --- /dev/null +++ b/pkg/util/dbterror/exeerrors/errors.go @@ -0,0 +1,102 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package exeerrors + +import ( + mysql "github.com/pingcap/tidb/pkg/errno" + parser_mysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// Error instances. +var ( + ErrGetStartTS = dbterror.ClassExecutor.NewStd(mysql.ErrGetStartTS) + ErrUnknownPlan = dbterror.ClassExecutor.NewStd(mysql.ErrUnknownPlan) + ErrPrepareMulti = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareMulti) + ErrPrepareDDL = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareDDL) + ErrResultIsEmpty = dbterror.ClassExecutor.NewStd(mysql.ErrResultIsEmpty) + ErrBuildExecutor = dbterror.ClassExecutor.NewStd(mysql.ErrBuildExecutor) + ErrBatchInsertFail = dbterror.ClassExecutor.NewStd(mysql.ErrBatchInsertFail) + ErrUnsupportedPs = dbterror.ClassExecutor.NewStd(mysql.ErrUnsupportedPs) + ErrSubqueryMoreThan1Row = dbterror.ClassExecutor.NewStd(mysql.ErrSubqueryNo1Row) + ErrIllegalGrantForTable = dbterror.ClassExecutor.NewStd(mysql.ErrIllegalGrantForTable) + ErrColumnsNotMatched = dbterror.ClassExecutor.NewStd(mysql.ErrColumnNotMatched) + + ErrCantCreateUserWithGrant = dbterror.ClassExecutor.NewStd(mysql.ErrCantCreateUserWithGrant) + ErrPasswordNoMatch = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordNoMatch) + ErrCannotUser = dbterror.ClassExecutor.NewStd(mysql.ErrCannotUser) + ErrGrantRole = dbterror.ClassExecutor.NewStd(mysql.ErrGrantRole) + ErrPasswordFormat = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordFormat) + ErrCantChangeTxCharacteristics = dbterror.ClassExecutor.NewStd(mysql.ErrCantChangeTxCharacteristics) + ErrPsManyParam = dbterror.ClassExecutor.NewStd(mysql.ErrPsManyParam) + ErrAdminCheckTable = dbterror.ClassExecutor.NewStd(mysql.ErrAdminCheckTable) + ErrDBaccessDenied = dbterror.ClassExecutor.NewStd(mysql.ErrDBaccessDenied) + ErrTableaccessDenied = dbterror.ClassExecutor.NewStd(mysql.ErrTableaccessDenied) + ErrBadDB = dbterror.ClassExecutor.NewStd(mysql.ErrBadDB) + ErrWrongObject = dbterror.ClassExecutor.NewStd(mysql.ErrWrongObject) + ErrWrongUsage = dbterror.ClassExecutor.NewStd(mysql.ErrWrongUsage) + ErrRoleNotGranted = dbterror.ClassPrivilege.NewStd(mysql.ErrRoleNotGranted) + ErrDeadlock = dbterror.ClassExecutor.NewStd(mysql.ErrLockDeadlock) + ErrQueryInterrupted = dbterror.ClassExecutor.NewStd(mysql.ErrQueryInterrupted) + ErrMaxExecTimeExceeded = dbterror.ClassExecutor.NewStd(mysql.ErrMaxExecTimeExceeded) + ErrResourceGroupQueryRunawayInterrupted = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupQueryRunawayInterrupted) + ErrResourceGroupQueryRunawayQuarantine = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupQueryRunawayQuarantine) + ErrDynamicPrivilegeNotRegistered = dbterror.ClassExecutor.NewStd(mysql.ErrDynamicPrivilegeNotRegistered) + ErrIllegalPrivilegeLevel = dbterror.ClassExecutor.NewStd(mysql.ErrIllegalPrivilegeLevel) + ErrInvalidSplitRegionRanges = dbterror.ClassExecutor.NewStd(mysql.ErrInvalidSplitRegionRanges) + ErrViewInvalid = dbterror.ClassExecutor.NewStd(mysql.ErrViewInvalid) + ErrInstanceScope = dbterror.ClassExecutor.NewStd(mysql.ErrInstanceScope) + ErrSettingNoopVariable = dbterror.ClassExecutor.NewStd(mysql.ErrSettingNoopVariable) + ErrLazyUniquenessCheckFailure = dbterror.ClassExecutor.NewStd(mysql.ErrLazyUniquenessCheckFailure) + + ErrBRIEBackupFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEBackupFailed) + ErrBRIERestoreFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIERestoreFailed) + ErrBRIEImportFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEImportFailed) + ErrBRIEExportFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEExportFailed) + ErrBRJobNotFound = dbterror.ClassExecutor.NewStd(mysql.ErrBRJobNotFound) + ErrCTEMaxRecursionDepth = dbterror.ClassExecutor.NewStd(mysql.ErrCTEMaxRecursionDepth) + ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) + ErrPluginIsNotLoaded = dbterror.ClassExecutor.NewStd(mysql.ErrPluginIsNotLoaded) + ErrSetPasswordAuthPlugin = dbterror.ClassExecutor.NewStd(mysql.ErrSetPasswordAuthPlugin) + ErrFuncNotEnabled = dbterror.ClassExecutor.NewStdErr(mysql.ErrNotSupportedYet, parser_mysql.Message("%-.32s is not supported. To enable this experimental feature, set '%-.32s' in the configuration file.", nil)) + ErrSavepointNotExists = dbterror.ClassExecutor.NewStd(mysql.ErrSpDoesNotExist) + ErrForeignKeyCascadeDepthExceeded = dbterror.ClassExecutor.NewStd(mysql.ErrForeignKeyCascadeDepthExceeded) + ErrPasswordExpireAnonymousUser = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordExpireAnonymousUser) + ErrMustChangePassword = dbterror.ClassExecutor.NewStd(mysql.ErrMustChangePassword) + + ErrWrongStringLength = dbterror.ClassDDL.NewStd(mysql.ErrWrongStringLength) + ErrUnsupportedFlashbackTmpTable = dbterror.ClassDDL.NewStdErr(mysql.ErrUnsupportedDDLOperation, parser_mysql.Message("Recover/flashback table is not supported on temporary tables", nil)) + ErrTruncateWrongInsertValue = dbterror.ClassTable.NewStdErr(mysql.ErrTruncatedWrongValue, parser_mysql.Message("Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %d", nil)) + ErrExistsInHistoryPassword = dbterror.ClassExecutor.NewStd(mysql.ErrExistsInHistoryPassword) + + ErrWarnTooFewRecords = dbterror.ClassExecutor.NewStd(mysql.ErrWarnTooFewRecords) + ErrWarnTooManyRecords = dbterror.ClassExecutor.NewStd(mysql.ErrWarnTooManyRecords) + ErrLoadDataFromServerDisk = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataFromServerDisk) + ErrLoadParquetFromLocal = dbterror.ClassExecutor.NewStd(mysql.ErrLoadParquetFromLocal) + ErrLoadDataEmptyPath = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataEmptyPath) + ErrLoadDataUnsupportedFormat = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataUnsupportedFormat) + ErrLoadDataInvalidURI = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataInvalidURI) + ErrLoadDataCantAccess = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataCantAccess) + ErrLoadDataCantRead = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataCantRead) + ErrLoadDataWrongFormatConfig = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataWrongFormatConfig) + ErrUnknownOption = dbterror.ClassExecutor.NewStd(mysql.ErrUnknownOption) + ErrInvalidOptionVal = dbterror.ClassExecutor.NewStd(mysql.ErrInvalidOptionVal) + ErrDuplicateOption = dbterror.ClassExecutor.NewStd(mysql.ErrDuplicateOption) + ErrLoadDataUnsupportedOption = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataUnsupportedOption) + ErrLoadDataJobNotFound = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataJobNotFound) + ErrLoadDataInvalidOperation = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataInvalidOperation) + ErrLoadDataLocalUnsupportedOption = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataLocalUnsupportedOption) + ErrLoadDataPreCheckFailed = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataPreCheckFailed) +) diff --git a/pkg/util/dbterror/main_test.go b/pkg/util/dbterror/main_test.go new file mode 100644 index 0000000000000..bc0d4b93b72c2 --- /dev/null +++ b/pkg/util/dbterror/main_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package dbterror + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/dbterror/terror.go b/pkg/util/dbterror/terror.go new file mode 100644 index 0000000000000..26b8d323336d8 --- /dev/null +++ b/pkg/util/dbterror/terror.go @@ -0,0 +1,57 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package dbterror + +import ( + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" +) + +// ErrClass represents a class of errors. +type ErrClass struct{ terror.ErrClass } + +// Error classes. +var ( + ClassAutoid = ErrClass{terror.ClassAutoid} + ClassDDL = ErrClass{terror.ClassDDL} + ClassDomain = ErrClass{terror.ClassDomain} + ClassExecutor = ErrClass{terror.ClassExecutor} + ClassExpression = ErrClass{terror.ClassExpression} + ClassAdmin = ErrClass{terror.ClassAdmin} + ClassKV = ErrClass{terror.ClassKV} + ClassMeta = ErrClass{terror.ClassMeta} + ClassOptimizer = ErrClass{terror.ClassOptimizer} + ClassPrivilege = ErrClass{terror.ClassPrivilege} + ClassSchema = ErrClass{terror.ClassSchema} + ClassServer = ErrClass{terror.ClassServer} + ClassStructure = ErrClass{terror.ClassStructure} + ClassVariable = ErrClass{terror.ClassVariable} + ClassXEval = ErrClass{terror.ClassXEval} + ClassTable = ErrClass{terror.ClassTable} + ClassTypes = ErrClass{terror.ClassTypes} + ClassJSON = ErrClass{terror.ClassJSON} + ClassTiKV = ErrClass{terror.ClassTiKV} + ClassSession = ErrClass{terror.ClassSession} + ClassPlugin = ErrClass{terror.ClassPlugin} + ClassUtil = ErrClass{terror.ClassUtil} +) + +// NewStd calls New using the standard message for the error code +// Attention: +// this method is not goroutine-safe and +// usually be used in global variable initializer +func (ec ErrClass) NewStd(code terror.ErrCode) *terror.Error { + return ec.NewStdErr(code, errno.MySQLErrName[uint16(code)]) +} diff --git a/util/dbterror/terror_test.go b/pkg/util/dbterror/terror_test.go similarity index 99% rename from util/dbterror/terror_test.go rename to pkg/util/dbterror/terror_test.go index 5ecfc2acaee58..af6e37b5c8225 100644 --- a/util/dbterror/terror_test.go +++ b/pkg/util/dbterror/terror_test.go @@ -19,7 +19,7 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" + "github.com/pingcap/tidb/pkg/errno" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/dbutil/BUILD.bazel b/pkg/util/dbutil/BUILD.bazel new file mode 100644 index 0000000000000..2accabbc687bf --- /dev/null +++ b/pkg/util/dbutil/BUILD.bazel @@ -0,0 +1,65 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "dbutil", + srcs = [ + "common.go", + "index.go", + "interface.go", + "query.go", + "retry.go", + "table.go", + "types.go", + "variable.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/dbutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/ddl", + "//pkg/errno", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util", + "//pkg/util/collate", + "//pkg/util/dbterror", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "dbutil_test", + timeout = "short", + srcs = [ + "common_test.go", + "index_test.go", + "retry_test.go", + "table_test.go", + "variable_test.go", + ], + embed = [":dbutil"], + flaky = True, + deps = [ + "//pkg/errno", + "//pkg/infoschema", + "//pkg/parser", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/schemacmp", + "@com_github_data_dog_go_sqlmock//:go-sqlmock", + "@com_github_go_sql_driver_mysql//:mysql", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/dbutil/README.md b/pkg/util/dbutil/README.md similarity index 100% rename from util/dbutil/README.md rename to pkg/util/dbutil/README.md diff --git a/pkg/util/dbutil/common.go b/pkg/util/dbutil/common.go new file mode 100644 index 0000000000000..d6291b30729fd --- /dev/null +++ b/pkg/util/dbutil/common.go @@ -0,0 +1,891 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "context" + "database/sql" + "encoding/json" + "fmt" + "net" + "os" + "strconv" + "strings" + "time" + + "github.com/go-sql-driver/mysql" + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + tmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/dbterror" + "go.uber.org/zap" +) + +const ( + // DefaultRetryTime is the default retry time to execute sql + DefaultRetryTime = 10 + + // DefaultTimeout is the default timeout for execute sql + DefaultTimeout time.Duration = 10 * time.Second + + // SlowLogThreshold defines the duration to log debug log of sql when exec time greater than + SlowLogThreshold = 200 * time.Millisecond + + // DefaultDeleteRowsNum is the default rows num for delete one time + DefaultDeleteRowsNum int64 = 100000 +) + +var ( + // ErrVersionNotFound means can't get the database's version + ErrVersionNotFound = errors.New("can't get the database's version") + + // ErrNoData means no data in table + ErrNoData = errors.New("no data found in table") +) + +// DBConfig is database configuration. +type DBConfig struct { + Host string `toml:"host" json:"host"` + User string `toml:"user" json:"user"` + Password string `toml:"password" json:"-"` + Schema string `toml:"schema" json:"schema"` + Snapshot string `toml:"snapshot" json:"snapshot"` + Port int `toml:"port" json:"port"` +} + +// String returns native format of database configuration +func (c *DBConfig) String() string { + cfg, err := json.Marshal(c) + if err != nil { + return "" + } + return string(cfg) +} + +// GetDBConfigFromEnv returns DBConfig from environment +func GetDBConfigFromEnv(schema string) DBConfig { + host := os.Getenv("MYSQL_HOST") + if host == "" { + host = "127.0.0.1" + } + port, _ := strconv.Atoi(os.Getenv("MYSQL_PORT")) + if port == 0 { + port = 3306 + } + user := os.Getenv("MYSQL_USER") + if user == "" { + user = "root" + } + pswd := os.Getenv("MYSQL_PSWD") + + return DBConfig{ + Host: host, + Port: port, + User: user, + Password: pswd, + Schema: schema, + } +} + +// OpenDB opens a mysql connection FD +func OpenDB(cfg DBConfig, vars map[string]string) (*sql.DB, error) { + driverCfg := mysql.NewConfig() + driverCfg.Params = make(map[string]string) + driverCfg.User = cfg.User + driverCfg.Passwd = cfg.Password + driverCfg.Net = "tcp" + driverCfg.Addr = net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port)) + driverCfg.Params["charset"] = "utf8mb4" + + if len(cfg.Snapshot) != 0 { + log.Info("create connection with snapshot", zap.String("snapshot", cfg.Snapshot)) + driverCfg.Params["tidb_snapshot"] = cfg.Snapshot + } + + for key, val := range vars { + // key='val'. add single quote for better compatibility. + driverCfg.Params[key] = fmt.Sprintf("'%s'", val) + } + + c, err := mysql.NewConnector(driverCfg) + if err != nil { + return nil, errors.Trace(err) + } + db := sql.OpenDB(c) + err = db.Ping() + return db, errors.Trace(err) +} + +// CloseDB closes the mysql fd +func CloseDB(db *sql.DB) error { + if db == nil { + return nil + } + + return errors.Trace(db.Close()) +} + +// GetCreateTableSQL returns the create table statement. +func GetCreateTableSQL(ctx context.Context, db QueryExecutor, schemaName string, tableName string) (string, error) { + /* + show create table example result: + mysql> SHOW CREATE TABLE `test`.`itest`; + +-------+--------------------------------------------------------------------+ + | Table | Create Table | + +-------+--------------------------------------------------------------------+ + | itest | CREATE TABLE `itest` ( + `id` int(11) DEFAULT NULL, + `name` varchar(24) DEFAULT NULL + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin | + +-------+--------------------------------------------------------------------+ + */ + query := fmt.Sprintf("SHOW CREATE TABLE %s", TableName(schemaName, tableName)) + + var tbl, createTable sql.NullString + err := db.QueryRowContext(ctx, query).Scan(&tbl, &createTable) + if err != nil { + return "", errors.Trace(err) + } + if !tbl.Valid || !createTable.Valid { + return "", errors.NotFoundf("table %s", tableName) + } + + return createTable.String, nil +} + +// GetRowCount returns row count of the table. +// if not specify where condition, return total row count of the table. +func GetRowCount(ctx context.Context, db QueryExecutor, schemaName string, tableName string, where string, args []interface{}) (int64, error) { + /* + select count example result: + mysql> SELECT count(1) cnt from `test`.`itest` where id > 0; + +------+ + | cnt | + +------+ + | 100 | + +------+ + */ + + query := fmt.Sprintf("SELECT COUNT(1) cnt FROM %s", TableName(schemaName, tableName)) + if len(where) > 0 { + query += fmt.Sprintf(" WHERE %s", where) + } + log.Debug("get row count", zap.String("sql", query), zap.Reflect("args", args)) + + var cnt sql.NullInt64 + err := db.QueryRowContext(ctx, query, args...).Scan(&cnt) + if err != nil { + return 0, errors.Trace(err) + } + if !cnt.Valid { + return 0, errors.NotFoundf("table `%s`.`%s`", schemaName, tableName) + } + + return cnt.Int64, nil +} + +// GetRandomValues returns some random value. Tips: limitArgs is the value in limitRange. +func GetRandomValues(ctx context.Context, db QueryExecutor, schemaName, table, column string, num int, limitRange string, limitArgs []interface{}, collation string) ([]string, error) { + /* + example: + mysql> SELECT `id` FROM (SELECT `id`, rand() rand_value FROM `test`.`test` WHERE `id` COLLATE "latin1_bin" > 0 AND `id` COLLATE "latin1_bin" < 100 ORDER BY rand_value LIMIT 5) rand_tmp ORDER BY `id` COLLATE "latin1_bin"; + +------+ + | id | + +------+ + | 1 | + | 2 | + | 3 | + +------+ + */ + + if limitRange == "" { + limitRange = "TRUE" + } + + if collation != "" { + collation = fmt.Sprintf(" COLLATE \"%s\"", collation) + } + + query := fmt.Sprintf("SELECT %[1]s FROM (SELECT %[1]s, rand() rand_value FROM %[2]s WHERE %[3]s ORDER BY rand_value LIMIT %[4]d)rand_tmp ORDER BY %[1]s%[5]s", + ColumnName(column), TableName(schemaName, table), limitRange, num, collation) + log.Debug("get random values", zap.String("sql", query), zap.Reflect("args", limitArgs)) + + rows, err := db.QueryContext(ctx, query, limitArgs...) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + randomValue := make([]string, 0, num) + for rows.Next() { + var value sql.NullString + err = rows.Scan(&value) + if err != nil { + return nil, errors.Trace(err) + } + if value.Valid { + randomValue = append(randomValue, value.String) + } + } + + return randomValue, errors.Trace(rows.Err()) +} + +// GetMinMaxValue return min and max value of given column by specified limitRange condition. +func GetMinMaxValue(ctx context.Context, db QueryExecutor, schema, table, column string, limitRange string, limitArgs []interface{}, collation string) (minStr string, maxStr string, err error) { + /* + example: + mysql> SELECT MIN(`id`) as MIN, MAX(`id`) as MAX FROM `test`.`testa` WHERE id > 0 AND id < 10; + +------+------+ + | MIN | MAX | + +------+------+ + | 1 | 2 | + +------+------+ + */ + + if limitRange == "" { + limitRange = "TRUE" + } + + if collation != "" { + collation = fmt.Sprintf(" COLLATE \"%s\"", collation) + } + + query := fmt.Sprintf("SELECT /*!40001 SQL_NO_CACHE */ MIN(%s%s) as MIN, MAX(%s%s) as MAX FROM %s WHERE %s", + ColumnName(column), collation, ColumnName(column), collation, TableName(schema, table), limitRange) + log.Debug("GetMinMaxValue", zap.String("sql", query), zap.Reflect("args", limitArgs)) + + var min, max sql.NullString + rows, err := db.QueryContext(ctx, query, limitArgs...) + if err != nil { + return "", "", errors.Trace(err) + } + defer rows.Close() + + for rows.Next() { + err = rows.Scan(&min, &max) + if err != nil { + return "", "", errors.Trace(err) + } + } + + if !min.Valid || !max.Valid { + // don't have any data + return "", "", ErrNoData + } + + return min.String, max.String, errors.Trace(rows.Err()) +} + +// GetTimeZoneOffset is to get offset of timezone. +func GetTimeZoneOffset(ctx context.Context, db QueryExecutor) (time.Duration, error) { + var timeStr string + err := db.QueryRowContext(ctx, "SELECT cast(TIMEDIFF(NOW(6), UTC_TIMESTAMP(6)) as time);").Scan(&timeStr) + if err != nil { + return 0, errors.Trace(err) + } + factor := time.Duration(1) + if timeStr[0] == '-' || timeStr[0] == '+' { + if timeStr[0] == '-' { + factor *= -1 + } + timeStr = timeStr[1:] + } + t, err := time.Parse(time.TimeOnly, timeStr) + if err != nil { + return 0, errors.Trace(err) + } + + if t.IsZero() { + return 0, nil + } + + hour, minute, second := t.Clock() + //nolint:durationcheck + return time.Duration(hour*3600+minute*60+second) * time.Second * factor, nil +} + +// FormatTimeZoneOffset is to format offset of timezone. +func FormatTimeZoneOffset(offset time.Duration) string { + prefix := "+" + if offset < 0 { + prefix = "-" + offset *= -1 + } + hours := offset / time.Hour + minutes := (offset % time.Hour) / time.Minute + + return fmt.Sprintf("%s%02d:%02d", prefix, hours, minutes) +} + +func queryTables(ctx context.Context, db QueryExecutor, q string) (tables []string, err error) { + log.Debug("query tables", zap.String("query", q)) + rows, err := db.QueryContext(ctx, q) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + tables = make([]string, 0, 8) + for rows.Next() { + var table, tType sql.NullString + err = rows.Scan(&table, &tType) + if err != nil { + return nil, errors.Trace(err) + } + + if !table.Valid || !tType.Valid { + continue + } + + tables = append(tables, table.String) + } + + return tables, errors.Trace(rows.Err()) +} + +// GetTables returns name of all tables in the specified schema +func GetTables(ctx context.Context, db QueryExecutor, schemaName string) (tables []string, err error) { + /* + show tables without view: https://dev.mysql.com/doc/refman/5.7/en/show-tables.html + + example: + mysql> show full tables in test where Table_Type != 'VIEW'; + +----------------+------------+ + | Tables_in_test | Table_type | + +----------------+------------+ + | NTEST | BASE TABLE | + +----------------+------------+ + */ + query := fmt.Sprintf("SHOW FULL TABLES IN `%s` WHERE Table_Type != 'VIEW';", escapeName(schemaName)) + return queryTables(ctx, db, query) +} + +// GetViews returns names of all views in the specified schema +func GetViews(ctx context.Context, db QueryExecutor, schemaName string) (tables []string, err error) { + query := fmt.Sprintf("SHOW FULL TABLES IN `%s` WHERE Table_Type = 'VIEW';", escapeName(schemaName)) + return queryTables(ctx, db, query) +} + +// GetSchemas returns name of all schemas +func GetSchemas(ctx context.Context, db QueryExecutor) ([]string, error) { + query := "SHOW DATABASES" + rows, err := db.QueryContext(ctx, query) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + // show an example. + /* + mysql> SHOW DATABASES; + +--------------------+ + | Database | + +--------------------+ + | information_schema | + | mysql | + | performance_schema | + | sys | + | test_db | + +--------------------+ + */ + schemas := make([]string, 0, 10) + for rows.Next() { + var schema string + err = rows.Scan(&schema) + if err != nil { + return nil, errors.Trace(err) + } + schemas = append(schemas, schema) + } + return schemas, errors.Trace(rows.Err()) +} + +// GetCRC32Checksum returns checksum code of some data by given condition +func GetCRC32Checksum(ctx context.Context, db QueryExecutor, schemaName, tableName string, tbInfo *model.TableInfo, limitRange string, args []interface{}) (int64, error) { + /* + calculate CRC32 checksum example: + mysql> SELECT BIT_XOR(CAST(CRC32(CONCAT_WS(',', id, name, age, CONCAT(ISNULL(id), ISNULL(name), ISNULL(age))))AS UNSIGNED)) AS checksum FROM test.test WHERE id > 0 AND id < 10; + +------------+ + | checksum | + +------------+ + | 1466098199 | + +------------+ + */ + columnNames := make([]string, 0, len(tbInfo.Columns)) + columnIsNull := make([]string, 0, len(tbInfo.Columns)) + for _, col := range tbInfo.Columns { + columnNames = append(columnNames, ColumnName(col.Name.O)) + columnIsNull = append(columnIsNull, fmt.Sprintf("ISNULL(%s)", ColumnName(col.Name.O))) + } + + query := fmt.Sprintf("SELECT BIT_XOR(CAST(CRC32(CONCAT_WS(',', %s, CONCAT(%s)))AS UNSIGNED)) AS checksum FROM %s WHERE %s;", + strings.Join(columnNames, ", "), strings.Join(columnIsNull, ", "), TableName(schemaName, tableName), limitRange) + log.Debug("checksum", zap.String("sql", query), zap.Reflect("args", args)) + + var checksum sql.NullInt64 + err := db.QueryRowContext(ctx, query, args...).Scan(&checksum) + if err != nil { + return -1, errors.Trace(err) + } + if !checksum.Valid { + // if don't have any data, the checksum will be `NULL` + log.Warn("get empty checksum", zap.String("sql", query), zap.Reflect("args", args)) + return 0, nil + } + + return checksum.Int64, nil +} + +// Bucket saves the bucket information from TiDB. +type Bucket struct { + LowerBound string + UpperBound string + Count int64 +} + +// GetBucketsInfo SHOW STATS_BUCKETS in TiDB. +func GetBucketsInfo(ctx context.Context, db QueryExecutor, schema, table string, tableInfo *model.TableInfo) (map[string][]Bucket, error) { + /* + example in tidb: + mysql> SHOW STATS_BUCKETS WHERE db_name= "test" AND table_name="testa"; + +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ + | Db_name | Table_name | Partition_name | Column_name | Is_index | Bucket_id | Count | Repeats | Lower_Bound | Upper_Bound | + +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ + | test | testa | | PRIMARY | 1 | 0 | 64 | 1 | 1846693550524203008 | 1846838686059069440 | + | test | testa | | PRIMARY | 1 | 1 | 128 | 1 | 1846840885082324992 | 1847056389361369088 | + +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ + */ + buckets := make(map[string][]Bucket) + query := "SHOW STATS_BUCKETS WHERE db_name= ? AND table_name= ?;" + log.Debug("GetBucketsInfo", zap.String("sql", query), zap.String("schema", schema), zap.String("table", table)) + + rows, err := db.QueryContext(ctx, query, schema, table) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + cols, err := rows.Columns() + if err != nil { + return nil, errors.Trace(err) + } + + for rows.Next() { + var dbName, tableName, partitionName, columnName, lowerBound, upperBound sql.NullString + var isIndex, bucketID, count, repeats, ndv sql.NullInt64 + + // add partiton_name in new version + switch len(cols) { + case 9: + err = rows.Scan(&dbName, &tableName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound) + case 10: + err = rows.Scan(&dbName, &tableName, &partitionName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound) + case 11: + err = rows.Scan(&dbName, &tableName, &partitionName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound, &ndv) + default: + return nil, errors.New("Unknown struct for buckets info") + } + if err != nil { + return nil, errors.Trace(err) + } + + if _, ok := buckets[columnName.String]; !ok { + buckets[columnName.String] = make([]Bucket, 0, 100) + } + buckets[columnName.String] = append(buckets[columnName.String], Bucket{ + Count: count.Int64, + LowerBound: lowerBound.String, + UpperBound: upperBound.String, + }) + } + + // when primary key is int type, the columnName will be column's name, not `PRIMARY`, check and transform here. + indices := FindAllIndex(tableInfo) + for _, index := range indices { + if index.Name.O != "PRIMARY" { + continue + } + _, ok := buckets[index.Name.O] + if !ok && len(index.Columns) == 1 { + if _, ok := buckets[index.Columns[0].Name.O]; !ok { + return nil, errors.NotFoundf("primary key on %s in buckets info", index.Columns[0].Name.O) + } + buckets[index.Name.O] = buckets[index.Columns[0].Name.O] + delete(buckets, index.Columns[0].Name.O) + } + } + + return buckets, errors.Trace(rows.Err()) +} + +// AnalyzeValuesFromBuckets analyze upperBound or lowerBound to string for each column. +// upperBound and lowerBound are looks like '(123, abc)' for multiple fields, or '123' for one field. +func AnalyzeValuesFromBuckets(valueString string, cols []*model.ColumnInfo) ([]string, error) { + // FIXME: maybe some values contains '(', ')' or ', ' + vStr := strings.Trim(valueString, "()") + values := strings.Split(vStr, ", ") + if len(values) != len(cols) { + return nil, errors.Errorf("analyze value %s failed", valueString) + } + + for i, col := range cols { + if IsTimeTypeAndNeedDecode(col.GetType()) { + // check if values[i] is already a time string + sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) + _, err := types.ParseTime(sc, values[i], col.GetType(), types.MinFsp, nil) + if err == nil { + continue + } + + value, err := DecodeTimeInBucket(values[i]) + if err != nil { + log.Error("analyze values from buckets", zap.String("column", col.Name.O), zap.String("value", values[i]), zap.Error(err)) + return nil, errors.Trace(err) + } + + values[i] = value + } + } + + return values, nil +} + +// DecodeTimeInBucket decodes Time from a packed uint64 value. +func DecodeTimeInBucket(packedStr string) (string, error) { + packed, err := strconv.ParseUint(packedStr, 10, 64) + if err != nil { + return "", err + } + + if packed == 0 { + return "", nil + } + + t := new(types.Time) + err = t.FromPackedUint(packed) + if err != nil { + return "", err + } + + return t.String(), nil +} + +// GetTidbLatestTSO returns tidb's current TSO. +func GetTidbLatestTSO(ctx context.Context, db QueryExecutor) (int64, error) { + /* + example in tidb: + mysql> SHOW MASTER STATUS; + +-------------+--------------------+--------------+------------------+-------------------+ + | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | + +-------------+--------------------+--------------+------------------+-------------------+ + | tidb-binlog | 400718757701615617 | | | | + +-------------+--------------------+--------------+------------------+-------------------+ + */ + rows, err := db.QueryContext(ctx, "SHOW MASTER STATUS") + if err != nil { + return 0, errors.Trace(err) + } + defer rows.Close() + + if rows.Next() { + fields, err1 := ScanRow(rows) + if err1 != nil { + return 0, errors.Trace(err1) + } + + ts, err1 := strconv.ParseInt(string(fields["Position"].Data), 10, 64) + if err1 != nil { + return 0, errors.Trace(err1) + } + return ts, nil + } + return 0, errors.New("get secondary cluster's ts failed") +} + +// GetDBVersion returns the database's version +func GetDBVersion(ctx context.Context, db QueryExecutor) (string, error) { + /* + example in TiDB: + mysql> select version(); + +--------------------------------------+ + | version() | + +--------------------------------------+ + | 5.7.10-TiDB-v2.1.0-beta-173-g7e48ab1 | + +--------------------------------------+ + + example in MySQL: + mysql> select version(); + +-----------+ + | version() | + +-----------+ + | 5.7.21 | + +-----------+ + */ + query := "SELECT version()" + result, err := db.QueryContext(ctx, query) //nolint:rowserrcheck + if err != nil { + return "", errors.Trace(err) + } + defer result.Close() + + var version sql.NullString + if result.Next() { + err := result.Scan(&version) + if err != nil { + return "", errors.Trace(err) + } + } + + if version.Valid { + return version.String, nil + } + + return "", ErrVersionNotFound +} + +// GetSessionVariable gets server's session variable, although argument is QueryExecutor, (session) system variables may be +// set through DSN +func GetSessionVariable(ctx context.Context, db QueryExecutor, variable string) (value string, err error) { + query := fmt.Sprintf("SHOW VARIABLES LIKE '%s'", variable) + rows, err := db.QueryContext(ctx, query) + + if err != nil { + return "", errors.Trace(err) + } + defer rows.Close() + + // Show an example. + /* + mysql> SHOW VARIABLES LIKE "binlog_format"; + +---------------+-------+ + | Variable_name | Value | + +---------------+-------+ + | binlog_format | ROW | + +---------------+-------+ + */ + + for rows.Next() { + if err = rows.Scan(&variable, &value); err != nil { + return "", errors.Trace(err) + } + } + + if err := rows.Err(); err != nil { + return "", errors.Trace(err) + } + + return value, nil +} + +// GetSQLMode returns sql_mode. +func GetSQLMode(ctx context.Context, db QueryExecutor) (tmysql.SQLMode, error) { + sqlMode, err := GetSessionVariable(ctx, db, "sql_mode") + if err != nil { + return tmysql.ModeNone, err + } + + mode, err := tmysql.GetSQLMode(sqlMode) + return mode, errors.Trace(err) +} + +// IsTiDB returns true if this database is tidb +func IsTiDB(ctx context.Context, db QueryExecutor) (bool, error) { + version, err := GetDBVersion(ctx, db) + if err != nil { + log.Error("get database's version failed", zap.Error(err)) + return false, errors.Trace(err) + } + + return strings.Contains(strings.ToLower(version), "tidb"), nil +} + +// TableName returns `schema`.`table` +func TableName(schema, table string) string { + return fmt.Sprintf("`%s`.`%s`", escapeName(schema), escapeName(table)) +} + +// ColumnName returns `column` +func ColumnName(column string) string { + return fmt.Sprintf("`%s`", escapeName(column)) +} + +func escapeName(name string) string { + return strings.ReplaceAll(name, "`", "``") +} + +// ReplacePlaceholder will use args to replace '?', used for log. +// tips: make sure the num of "?" is same with len(args) +func ReplacePlaceholder(str string, args []string) string { + /* + for example: + str is "a > ? AND a < ?", args is {'1', '2'}, + this function will return "a > '1' AND a < '2'" + */ + newStr := strings.ReplaceAll(str, "?", "'%s'") + return fmt.Sprintf(newStr, util.StringsToInterfaces(args)...) +} + +// ExecSQLWithRetry executes sql with retry +func ExecSQLWithRetry(ctx context.Context, db DBExecutor, sql string, args ...interface{}) (err error) { + for i := 0; i < DefaultRetryTime; i++ { + startTime := time.Now() + _, err = db.ExecContext(ctx, sql, args...) + takeDuration := time.Since(startTime) + if takeDuration > SlowLogThreshold { + log.Debug("exec sql slow", zap.String("sql", sql), zap.Reflect("args", args), zap.Duration("take", takeDuration)) + } + if err == nil { + return nil + } + + if ignoreError(err) { + log.Warn("ignore execute sql error", zap.Error(err)) + return nil + } + + if !IsRetryableError(err) { + return errors.Trace(err) + } + + log.Warn("exe sql failed, will try again", zap.String("sql", sql), zap.Reflect("args", args), zap.Error(err)) + + if i == DefaultRetryTime-1 { + break + } + + select { + case <-ctx.Done(): + return errors.Trace(ctx.Err()) + case <-time.After(10 * time.Millisecond): + } + } + + return errors.Trace(err) +} + +// ExecuteSQLs executes some sqls in one transaction +func ExecuteSQLs(ctx context.Context, db DBExecutor, sqls []string, args [][]interface{}) error { + txn, err := db.BeginTx(ctx, nil) + if err != nil { + log.Error("exec sqls begin", zap.Error(err)) + return errors.Trace(err) + } + + for i := range sqls { + startTime := time.Now() + + _, err = txn.ExecContext(ctx, sqls[i], args[i]...) + if err != nil { + log.Error("exec sql", zap.String("sql", sqls[i]), zap.Reflect("args", args[i]), zap.Error(err)) + rerr := txn.Rollback() + if rerr != nil { + log.Error("rollback", zap.Error(err)) + } + return errors.Trace(err) + } + + takeDuration := time.Since(startTime) + if takeDuration > SlowLogThreshold { + log.Debug("exec sql slow", zap.String("sql", sqls[i]), zap.Reflect("args", args[i]), zap.Duration("take", takeDuration)) + } + } + + err = txn.Commit() + if err != nil { + log.Error("exec sqls commit", zap.Error(err)) + return errors.Trace(err) + } + + return nil +} + +func ignoreError(err error) bool { + // TODO: now only ignore some ddl error, add some dml error later + return ignoreDDLError(err) +} + +func ignoreDDLError(err error) bool { + err = errors.Cause(err) + mysqlErr, ok := err.(*mysql.MySQLError) + if !ok { + return false + } + + errCode := errors.ErrCode(mysqlErr.Number) + switch errCode { + case infoschema.ErrDatabaseExists.Code(), infoschema.ErrDatabaseDropExists.Code(), + infoschema.ErrTableExists.Code(), infoschema.ErrTableDropExists.Code(), + infoschema.ErrColumnExists.Code(), infoschema.ErrIndexExists.Code(): + return true + case dbterror.ErrDupKeyName.Code(): + return true + default: + return false + } +} + +// DeleteRows delete rows in several times. Only can delete less than 300,000 one time in TiDB. +func DeleteRows(ctx context.Context, db DBExecutor, schemaName string, tableName string, where string, args []interface{}) error { + deleteSQL := fmt.Sprintf("DELETE FROM %s WHERE %s limit %d;", TableName(schemaName, tableName), where, DefaultDeleteRowsNum) + result, err := db.ExecContext(ctx, deleteSQL, args...) + if err != nil { + return errors.Trace(err) + } + + rows, err := result.RowsAffected() + if err != nil { + return errors.Trace(err) + } + + if rows < DefaultDeleteRowsNum { + return nil + } + + return DeleteRows(ctx, db, schemaName, tableName, where, args) +} + +// getParser gets parser according to sql mode +func getParser(sqlModeStr string) (*parser.Parser, error) { + if len(sqlModeStr) == 0 { + return parser.New(), nil + } + + sqlMode, err := tmysql.GetSQLMode(tmysql.FormatSQLModeStr(sqlModeStr)) + if err != nil { + return nil, errors.Annotatef(err, "invalid sql mode %s", sqlModeStr) + } + parser2 := parser.New() + parser2.SetSQLMode(sqlMode) + return parser2, nil +} + +// GetParserForDB discovers ANSI_QUOTES in db's session variables and returns a proper parser +func GetParserForDB(ctx context.Context, db QueryExecutor) (*parser.Parser, error) { + mode, err := GetSQLMode(ctx, db) + if err != nil { + return nil, err + } + + parser2 := parser.New() + parser2.SetSQLMode(mode) + return parser2, nil +} diff --git a/pkg/util/dbutil/common_test.go b/pkg/util/dbutil/common_test.go new file mode 100644 index 0000000000000..44290d34c8e04 --- /dev/null +++ b/pkg/util/dbutil/common_test.go @@ -0,0 +1,257 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "context" + "testing" + "time" + + sqlmock "github.com/DATA-DOG/go-sqlmock" + "github.com/go-sql-driver/mysql" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + pmysql "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/stretchr/testify/require" +) + +func TestReplacePlaceholder(t *testing.T) { + testCases := []struct { + originStr string + args []string + expectStr string + }{ + { + "a > ? AND a < ?", + []string{"1", "2"}, + "a > '1' AND a < '2'", + }, { + "a = ? AND b = ?", + []string{"1", "2"}, + "a = '1' AND b = '2'", + }, + } + + for _, testCase := range testCases { + str := ReplacePlaceholder(testCase.originStr, testCase.args) + require.Equal(t, testCase.expectStr, str) + } +} + +func TestTableName(t *testing.T) { + testCases := []struct { + schema string + table string + expectTableName string + }{ + { + "test", + "testa", + "`test`.`testa`", + }, + { + "test-1", + "test-a", + "`test-1`.`test-a`", + }, + { + "test", + "t`esta", + "`test`.`t``esta`", + }, + } + + for _, testCase := range testCases { + tableName := TableName(testCase.schema, testCase.table) + require.Equal(t, testCase.expectTableName, tableName) + } +} + +func TestColumnName(t *testing.T) { + testCases := []struct { + column string + expectColName string + }{ + { + "test", + "`test`", + }, + { + "test-1", + "`test-1`", + }, + { + "t`esta", + "`t``esta`", + }, + } + + for _, testCase := range testCases { + colName := ColumnName(testCase.column) + require.Equal(t, testCase.expectColName, colName) + } +} + +func newMysqlErr(number uint16, message string) *mysql.MySQLError { + return &mysql.MySQLError{ + Number: number, + Message: message, + } +} + +func TestIsIgnoreError(t *testing.T) { + cases := []struct { + err error + canIgnore bool + }{ + {newMysqlErr(uint16(infoschema.ErrDatabaseExists.Code()), "Can't create database, database exists"), true}, + {newMysqlErr(uint16(infoschema.ErrDatabaseDropExists.Code()), "Can't drop database, database doesn't exists"), true}, + {newMysqlErr(uint16(infoschema.ErrTableExists.Code()), "Can't create table, table exists"), true}, + {newMysqlErr(uint16(infoschema.ErrTableDropExists.Code()), "Can't drop table, table dosen't exists"), true}, + {newMysqlErr(uint16(infoschema.ErrColumnExists.Code()), "Duplicate column name"), true}, + {newMysqlErr(uint16(infoschema.ErrIndexExists.Code()), "Duplicate Index"), true}, + + {newMysqlErr(uint16(999), "fake error"), false}, + {errors.New("unknown error"), false}, + } + + for _, tt := range cases { + t.Logf("err %v, expected %v", tt.err, tt.canIgnore) + require.Equal(t, tt.canIgnore, ignoreError(tt.err)) + } +} + +func TestDeleteRows(t *testing.T) { + db, mock, err := sqlmock.New() + require.NoError(t, err) + + // delete twice + mock.ExpectExec("DELETE FROM").WillReturnResult(sqlmock.NewResult(0, DefaultDeleteRowsNum)) + mock.ExpectExec("DELETE FROM").WillReturnResult(sqlmock.NewResult(0, DefaultDeleteRowsNum-1)) + + err = DeleteRows(context.Background(), db, "test", "t", "", nil) + require.NoError(t, err) + + if err := mock.ExpectationsWereMet(); err != nil { + t.Errorf("there were unfulfilled expectations: %s", err) + } +} + +func TestGetParser(t *testing.T) { + testCases := []struct { + sqlModeStr string + hasErr bool + }{ + { + "", + false, + }, { + "ANSI_QUOTES", + false, + }, { + "ANSI_QUOTES,IGNORE_SPACE", + false, + }, { + "ANSI_QUOTES123", + true, + }, { + "ANSI_QUOTES,IGNORE_SPACE123", + true, + }, + } + + for _, testCase := range testCases { + parser, err := getParser(testCase.sqlModeStr) + if testCase.hasErr { + require.Error(t, err) + } else { + require.NoError(t, err) + require.NotNil(t, parser) + } + } +} + +func TestAnalyzeValuesFromBuckets(t *testing.T) { + cases := []struct { + value string + col *model.ColumnInfo + expect string + }{ + { + "2021-03-05 21:31:03", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDatetime).Build()}, + "2021-03-05 21:31:03", + }, + { + "2021-03-05 21:31:03", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeTimestamp).Build()}, + "2021-03-05 21:31:03", + }, + { + "2021-03-05", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDate).Build()}, + "2021-03-05", + }, + { + "1847956477067657216", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDatetime).Build()}, + "2020-01-01 10:00:00", + }, + { + "1847955927311843328", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeTimestamp).Build()}, + "2020-01-01 02:00:00", + }, + { + "1847955789872889856", + &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDate).Build()}, + "2020-01-01 00:00:00", + }, + } + for _, ca := range cases { + val, err := AnalyzeValuesFromBuckets(ca.value, []*model.ColumnInfo{ca.col}) + require.NoError(t, err) + require.Len(t, val, 1) + require.Equal(t, ca.expect, val[0]) + } +} + +func TestFormatTimeZoneOffset(t *testing.T) { + cases := map[string]time.Duration{ + "+00:00": 0, + "+01:00": time.Hour, + "-08:03": -1 * (8*time.Hour + 3*time.Minute), + "-12:59": -1 * (12*time.Hour + 59*time.Minute), + "+12:59": 12*time.Hour + 59*time.Minute, + } + + for k, v := range cases { + offset := FormatTimeZoneOffset(v) + require.Equal(t, offset, k) + } +} + +func TestGetTimeZoneOffset(t *testing.T) { + db, mock, err := sqlmock.New() + require.NoError(t, err) + + mock.ExpectQuery("SELECT cast\\(TIMEDIFF\\(NOW\\(6\\), UTC_TIMESTAMP\\(6\\)\\) as time\\);"). + WillReturnRows(mock.NewRows([]string{""}).AddRow("01:00:00")) + d, err := GetTimeZoneOffset(context.Background(), db) + require.NoError(t, err) + require.Equal(t, "1h0m0s", d.String()) +} diff --git a/pkg/util/dbutil/index.go b/pkg/util/dbutil/index.go new file mode 100644 index 0000000000000..afec600160b65 --- /dev/null +++ b/pkg/util/dbutil/index.go @@ -0,0 +1,209 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "context" + "fmt" + "sort" + "strconv" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/model" +) + +// IndexInfo contains information of table index. +type IndexInfo struct { + Table string + KeyName string + ColumnName string + SeqInIndex int + Cardinality int + NoneUnique bool +} + +// ShowIndex returns result of executing `show index` +func ShowIndex(ctx context.Context, db QueryExecutor, schemaName string, table string) ([]*IndexInfo, error) { + /* + show index example result: + mysql> show index from test; + +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ + | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | + +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ + | test | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | + | test | 0 | aid | 1 | aid | A | 0 | NULL | NULL | YES | BTREE | | | + +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ + */ + indices := make([]*IndexInfo, 0, 3) + query := fmt.Sprintf("SHOW INDEX FROM %s", TableName(schemaName, table)) + rows, err := db.QueryContext(ctx, query) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + for rows.Next() { + fields, err1 := ScanRow(rows) + if err1 != nil { + return nil, errors.Trace(err1) + } + seqInIndex, err1 := strconv.Atoi(string(fields["Seq_in_index"].Data)) + if err1 != nil { + return nil, errors.Trace(err1) + } + cardinality, err1 := strconv.Atoi(string(fields["Cardinality"].Data)) + if err1 != nil { + return nil, errors.Trace(err1) + } + index := &IndexInfo{ + Table: string(fields["Table"].Data), + NoneUnique: string(fields["Non_unique"].Data) == "1", + KeyName: string(fields["Key_name"].Data), + ColumnName: string(fields["Column_name"].Data), + SeqInIndex: seqInIndex, + Cardinality: cardinality, + } + indices = append(indices, index) + } + + return indices, nil +} + +// FindSuitableColumnWithIndex returns first column of a suitable index. +// The priority is +// * primary key +// * unique key +// * normal index which has max cardinality +func FindSuitableColumnWithIndex(ctx context.Context, db QueryExecutor, schemaName string, tableInfo *model.TableInfo) (*model.ColumnInfo, error) { + // find primary key + for _, index := range tableInfo.Indices { + if index.Primary { + return FindColumnByName(tableInfo.Columns, index.Columns[0].Name.O), nil + } + } + + // no primary key found, seek unique index + for _, index := range tableInfo.Indices { + if index.Unique { + return FindColumnByName(tableInfo.Columns, index.Columns[0].Name.O), nil + } + } + + // no unique index found, seek index with max cardinality + indices, err := ShowIndex(ctx, db, schemaName, tableInfo.Name.O) + if err != nil { + return nil, errors.Trace(err) + } + var c *model.ColumnInfo + var maxCardinality int + for _, indexInfo := range indices { + // just use the first column in the index, otherwise can't hit the index when select + if indexInfo.SeqInIndex != 1 { + continue + } + + if indexInfo.Cardinality > maxCardinality { + column := FindColumnByName(tableInfo.Columns, indexInfo.ColumnName) + if column == nil { + return nil, errors.NotFoundf("column %s in %s.%s", indexInfo.ColumnName, schemaName, tableInfo.Name.O) + } + maxCardinality = indexInfo.Cardinality + c = column + } + } + + return c, nil +} + +// FindAllIndex returns all index, order is pk, uk, and normal index. +func FindAllIndex(tableInfo *model.TableInfo) []*model.IndexInfo { + indices := make([]*model.IndexInfo, len(tableInfo.Indices)) + copy(indices, tableInfo.Indices) + sort.SliceStable(indices, func(i, j int) bool { + a := indices[i] + b := indices[j] + switch { + case b.Primary: + return false + case a.Primary: + return true + case b.Unique: + return false + case a.Unique: + return true + default: + return false + } + }) + return indices +} + +// FindAllColumnWithIndex returns columns with index, order is pk, uk and normal index. +func FindAllColumnWithIndex(tableInfo *model.TableInfo) []*model.ColumnInfo { + colsMap := make(map[string]interface{}) + cols := make([]*model.ColumnInfo, 0, 2) + + for _, index := range FindAllIndex(tableInfo) { + // index will be guaranteed to be visited in order PK -> UK -> IK + for _, indexCol := range index.Columns { + col := FindColumnByName(tableInfo.Columns, indexCol.Name.O) + if _, ok := colsMap[col.Name.O]; ok { + continue + } + colsMap[col.Name.O] = struct{}{} + cols = append(cols, col) + } + } + + return cols +} + +// SelectUniqueOrderKey returns some columns for order by condition. +func SelectUniqueOrderKey(tbInfo *model.TableInfo) ([]string, []*model.ColumnInfo) { + keys := make([]string, 0, 2) + keyCols := make([]*model.ColumnInfo, 0, 2) + + for _, index := range tbInfo.Indices { + if index.Primary { + keys = keys[:0] + keyCols = keyCols[:0] + for _, indexCol := range index.Columns { + keys = append(keys, indexCol.Name.O) + keyCols = append(keyCols, tbInfo.Columns[indexCol.Offset]) + } + break + } + if index.Unique { + keys = keys[:0] + keyCols = keyCols[:0] + for _, indexCol := range index.Columns { + keys = append(keys, indexCol.Name.O) + keyCols = append(keyCols, tbInfo.Columns[indexCol.Offset]) + } + } + } + + if len(keys) != 0 { + return keys, keyCols + } + + // no primary key or unique found, use all fields as order by key + for _, col := range tbInfo.Columns { + keys = append(keys, col.Name.O) + keyCols = append(keyCols, col) + } + + return keys, keyCols +} diff --git a/pkg/util/dbutil/index_test.go b/pkg/util/dbutil/index_test.go new file mode 100644 index 0000000000000..ea80b2d49098f --- /dev/null +++ b/pkg/util/dbutil/index_test.go @@ -0,0 +1,101 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/stretchr/testify/require" +) + +func TestIndex(t *testing.T) { + testCases := []struct { + sql string + indices []string + cols []string + }{ + { + ` + CREATE TABLE itest (a int(11) NOT NULL, + b double NOT NULL DEFAULT '2', + c varchar(10) NOT NULL, + d time DEFAULT NULL, + PRIMARY KEY (a, b), + UNIQUE KEY d(d)) + `, + []string{"PRIMARY", "d"}, + []string{"a", "b", "d"}, + }, { + ` + CREATE TABLE jtest ( + a int(11) NOT NULL, + b varchar(10) DEFAULT NULL, + c varchar(255) DEFAULT NULL, + KEY c(c), + UNIQUE KEY b(b, c), + PRIMARY KEY (a) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin + `, + []string{"PRIMARY", "b", "c"}, + []string{"a", "b", "c"}, + }, { + ` + CREATE TABLE mtest ( + a int(24), + KEY test (a)) + `, + []string{"test"}, + []string{"a"}, + }, + { + ` + CREATE TABLE mtest ( + a int(24), + b int(24), + KEY test1 (a), + KEY test2 (b)) + `, + []string{"test1", "test2"}, + []string{"a", "b"}, + }, + { + ` + CREATE TABLE mtest ( + a int(24), + b int(24), + UNIQUE KEY test1 (a), + UNIQUE KEY test2 (b)) + `, + []string{"test1", "test2"}, + []string{"a", "b"}, + }, + } + + for _, testCase := range testCases { + tableInfo, err := GetTableInfoBySQL(testCase.sql, parser.New()) + require.NoError(t, err) + + indices := FindAllIndex(tableInfo) + for i, index := range indices { + require.Equal(t, testCase.indices[i], index.Name.O) + } + + cols := FindAllColumnWithIndex(tableInfo) + for j, col := range cols { + require.Equal(t, testCase.cols[j], col.Name.O) + } + } +} diff --git a/util/dbutil/interface.go b/pkg/util/dbutil/interface.go similarity index 100% rename from util/dbutil/interface.go rename to pkg/util/dbutil/interface.go diff --git a/util/dbutil/query.go b/pkg/util/dbutil/query.go similarity index 100% rename from util/dbutil/query.go rename to pkg/util/dbutil/query.go diff --git a/util/dbutil/retry.go b/pkg/util/dbutil/retry.go similarity index 98% rename from util/dbutil/retry.go rename to pkg/util/dbutil/retry.go index 30409883c920f..d37e8f32fde0c 100644 --- a/util/dbutil/retry.go +++ b/pkg/util/dbutil/retry.go @@ -19,7 +19,7 @@ import ( "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" + "github.com/pingcap/tidb/pkg/errno" ) var ( diff --git a/util/dbutil/retry_test.go b/pkg/util/dbutil/retry_test.go similarity index 99% rename from util/dbutil/retry_test.go rename to pkg/util/dbutil/retry_test.go index 8aae23e6169a1..04228f9f6ade7 100644 --- a/util/dbutil/retry_test.go +++ b/pkg/util/dbutil/retry_test.go @@ -20,7 +20,7 @@ import ( "testing" "github.com/go-sql-driver/mysql" - "github.com/pingcap/tidb/errno" + "github.com/pingcap/tidb/pkg/errno" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/dbutil/table.go b/pkg/util/dbutil/table.go new file mode 100644 index 0000000000000..6b8c6a3edc4da --- /dev/null +++ b/pkg/util/dbutil/table.go @@ -0,0 +1,146 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "context" + "fmt" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + _ "github.com/pingcap/tidb/pkg/planner/core" // to setup expression.EvalAstExpr. See: https://github.com/pingcap/tidb/blob/a94cff903cd1e7f3b050db782da84273ef5592f4/planner/core/optimizer.go#L202 + "github.com/pingcap/tidb/pkg/types" + _ "github.com/pingcap/tidb/pkg/types/parser_driver" // for parser driver + "github.com/pingcap/tidb/pkg/util/collate" +) + +func init() { + collate.SetNewCollationEnabledForTest(false) +} + +// GetTableInfo returns table information. +func GetTableInfo(ctx context.Context, db QueryExecutor, schemaName string, tableName string) (*model.TableInfo, error) { + createTableSQL, err := GetCreateTableSQL(ctx, db, schemaName, tableName) + if err != nil { + return nil, errors.Trace(err) + } + + parser2, err := GetParserForDB(ctx, db) + if err != nil { + return nil, errors.Trace(err) + } + return GetTableInfoBySQL(createTableSQL, parser2) +} + +// GetTableInfoBySQL returns table information by given create table sql. +func GetTableInfoBySQL(createTableSQL string, parser2 *parser.Parser) (table *model.TableInfo, err error) { + stmt, err := parser2.ParseOneStmt(createTableSQL, "", "") + if err != nil { + return nil, errors.Trace(err) + } + + s, ok := stmt.(*ast.CreateTableStmt) + if ok { + table, err := ddl.BuildTableInfoFromAST(s) + if err != nil { + return nil, errors.Trace(err) + } + + // put primary key in indices + if table.PKIsHandle { + pkIndex := &model.IndexInfo{ + Name: model.NewCIStr("PRIMARY"), + Primary: true, + State: model.StatePublic, + Unique: true, + Tp: model.IndexTypeBtree, + Columns: []*model.IndexColumn{ + { + Name: table.GetPkName(), + Length: types.UnspecifiedLength, + }, + }, + } + + table.Indices = append(table.Indices, pkIndex) + } + + return table, nil + } + + return nil, errors.Errorf("get table info from sql %s failed", createTableSQL) +} + +// FindColumnByName finds column by name. +func FindColumnByName(cols []*model.ColumnInfo, name string) *model.ColumnInfo { + // column name don't distinguish capital and small letter + name = strings.ToLower(name) + for _, col := range cols { + if col.Name.L == name { + return col + } + } + + return nil +} + +// EqualTableInfo returns true if this two table info have same columns and indices +func EqualTableInfo(tableInfo1, tableInfo2 *model.TableInfo) (bool, string) { + // check columns + if len(tableInfo1.Columns) != len(tableInfo2.Columns) { + return false, fmt.Sprintf("column num not equal, one is %d another is %d", len(tableInfo1.Columns), len(tableInfo2.Columns)) + } + + for j, col := range tableInfo1.Columns { + if col.Name.O != tableInfo2.Columns[j].Name.O { + return false, fmt.Sprintf("column name not equal, one is %s another is %s", col.Name.O, tableInfo2.Columns[j].Name.O) + } + if col.GetType() != tableInfo2.Columns[j].GetType() { + return false, fmt.Sprintf("column %s's type not equal, one is %v another is %v", col.Name.O, col.GetType(), tableInfo2.Columns[j].GetType()) + } + } + + // check index + if len(tableInfo1.Indices) != len(tableInfo2.Indices) { + return false, fmt.Sprintf("index num not equal, one is %d another is %d", len(tableInfo1.Indices), len(tableInfo2.Indices)) + } + + index2Map := make(map[string]*model.IndexInfo) + for _, index := range tableInfo2.Indices { + index2Map[index.Name.O] = index + } + + for _, index1 := range tableInfo1.Indices { + index2, ok := index2Map[index1.Name.O] + if !ok { + return false, fmt.Sprintf("index %s not exists", index1.Name.O) + } + + if len(index1.Columns) != len(index2.Columns) { + return false, fmt.Sprintf("index %s's columns num not equal, one is %d another is %d", index1.Name.O, len(index1.Columns), len(index2.Columns)) + } + for j, col := range index1.Columns { + if col.Name.O != index2.Columns[j].Name.O { + return false, fmt.Sprintf("index %s's column not equal, one has %s another has %s", index1.Name.O, col.Name.O, index2.Columns[j].Name.O) + } + } + } + + return true, "" +} diff --git a/pkg/util/dbutil/table_test.go b/pkg/util/dbutil/table_test.go new file mode 100644 index 0000000000000..28e2ed6cb6f73 --- /dev/null +++ b/pkg/util/dbutil/table_test.go @@ -0,0 +1,165 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/schemacmp" + "github.com/stretchr/testify/require" +) + +type testCase struct { + sql string + columns []string + indexs []string + colLen [][]int + colName string + fineCol bool +} + +func TestTable(t *testing.T) { + testCases := []*testCase{ + { + ` + CREATE TABLE htest ( + a int(11) PRIMARY KEY + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin + `, + []string{"a"}, + []string{mysql.PrimaryKeyName}, + [][]int{{types.UnspecifiedLength}}, + "c", + false, + }, { + ` + CREATE TABLE itest (a int(11) NOT NULL, + b double NOT NULL DEFAULT '2', + c varchar(10) NOT NULL, + d time DEFAULT NULL, + PRIMARY KEY (a, b), + UNIQUE KEY d (d)) + `, + []string{"a", "b", "c", "d"}, + []string{mysql.PrimaryKeyName, "d"}, + [][]int{{types.UnspecifiedLength, types.UnspecifiedLength}, {types.UnspecifiedLength}}, + "a", + true, + }, { + ` + CREATE TABLE jtest ( + a int(11) NOT NULL, + b varchar(10) DEFAULT NULL, + c varchar(255) DEFAULT NULL, + PRIMARY KEY (a) + ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin + `, + []string{"a", "b", "c"}, + []string{mysql.PrimaryKeyName}, + [][]int{{types.UnspecifiedLength}}, + "c", + true, + }, { + ` + CREATE TABLE mtest ( + a int(24), + KEY test (a)) + `, + []string{"a"}, + []string{"test"}, + [][]int{{types.UnspecifiedLength}}, + "d", + false, + }, { + ` + CREATE TABLE ntest ( + a int(24) PRIMARY KEY CLUSTERED + ) + `, + []string{"a"}, + []string{mysql.PrimaryKeyName}, + [][]int{{types.UnspecifiedLength}}, + "d", + false, + }, { + ` + CREATE TABLE otest ( + a int(11) NOT NULL, + b varchar(10) DEFAULT NULL, + c varchar(255) DEFAULT NULL, + PRIMARY KEY (a) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci + `, + []string{"a", "b", "c"}, + []string{mysql.PrimaryKeyName}, + [][]int{{types.UnspecifiedLength}}, + "c", + true, + }, + } + + for _, testCase := range testCases { + tableInfo, err := GetTableInfoBySQL(testCase.sql, parser.New()) + require.NoError(t, err) + for i, column := range tableInfo.Columns { + require.Equal(t, column.Name.O, testCase.columns[i]) + } + + require.Len(t, tableInfo.Indices, len(testCase.indexs)) + for j, index := range tableInfo.Indices { + require.Equal(t, index.Name.O, testCase.indexs[j]) + for k, indexCol := range index.Columns { + require.Equal(t, testCase.colLen[j][k], indexCol.Length) + } + } + + col := FindColumnByName(tableInfo.Columns, testCase.colName) + require.Equal(t, col != nil, testCase.fineCol) + } +} + +func TestTableStructEqual(t *testing.T) { + createTableSQL1 := "CREATE TABLE `test`.`atest` (`id` int(24), `name` varchar(24), `birthday` datetime, `update_time` time, `money` decimal(20,2), primary key(`id`))" + tableInfo1, err := GetTableInfoBySQL(createTableSQL1, parser.New()) + require.NoError(t, err) + + createTableSQL2 := "CREATE TABLE `test`.`atest` (`id` int(24) NOT NULL, `name` varchar(24), `birthday` datetime, `update_time` time, `money` decimal(20,2), primary key(`id`))" + tableInfo2, err := GetTableInfoBySQL(createTableSQL2, parser.New()) + require.NoError(t, err) + + createTableSQL3 := `CREATE TABLE "test"."atest" ("id" int(24), "name" varchar(24), "birthday" datetime, "update_time" time, "money" decimal(20,2), unique key("id"))` + p := parser.New() + p.SetSQLMode(mysql.ModeANSIQuotes) + tableInfo3, err := GetTableInfoBySQL(createTableSQL3, p) + require.NoError(t, err) + + equal, _ := EqualTableInfo(tableInfo1, tableInfo2) + require.Equal(t, true, equal) + + equal, _ = EqualTableInfo(tableInfo1, tableInfo3) + require.Equal(t, false, equal) +} + +func TestSchemacmpEncode(t *testing.T) { + createTableSQL := "CREATE TABLE `test`.`atest` (`id` int(24), primary key(`id`))" + tableInfo, err := GetTableInfoBySQL(createTableSQL, parser.New()) + require.NoError(t, err) + + table := schemacmp.Encode(tableInfo) + require.Equal(t, "CREATE TABLE `tbl`(`id` INT(24) NOT NULL, PRIMARY KEY (`id`)) CHARSET UTF8MB4 COLLATE UTF8MB4_BIN", table.String()) +} diff --git a/pkg/util/dbutil/types.go b/pkg/util/dbutil/types.go new file mode 100644 index 0000000000000..e129a63faad28 --- /dev/null +++ b/pkg/util/dbutil/types.go @@ -0,0 +1,47 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "github.com/pingcap/tidb/pkg/parser/mysql" +) + +// IsNumberType returns true if tp is number type +func IsNumberType(tp byte) bool { + switch tp { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24, mysql.TypeYear: + return true + } + + return false +} + +// IsFloatType returns true if tp is float type +func IsFloatType(tp byte) bool { + switch tp { + case mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: + return true + } + + return false +} + +// IsTimeTypeAndNeedDecode returns true if tp is time type and encoded in tidb buckets. +func IsTimeTypeAndNeedDecode(tp byte) bool { + if tp == mysql.TypeDatetime || tp == mysql.TypeTimestamp || tp == mysql.TypeDate { + return true + } + return false +} diff --git a/pkg/util/dbutil/variable.go b/pkg/util/dbutil/variable.go new file mode 100644 index 0000000000000..ffe8947eeebb2 --- /dev/null +++ b/pkg/util/dbutil/variable.go @@ -0,0 +1,155 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package dbutil + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/auth" +) + +// ShowVersion queries variable 'version' and returns its value. +func ShowVersion(ctx context.Context, db QueryExecutor) (value string, err error) { + return ShowMySQLVariable(ctx, db, "version") +} + +// ShowLogBin queries variable 'log_bin' and returns its value. +func ShowLogBin(ctx context.Context, db QueryExecutor) (value string, err error) { + return ShowMySQLVariable(ctx, db, "log_bin") +} + +// ShowBinlogFormat queries variable 'binlog_format' and returns its value. +func ShowBinlogFormat(ctx context.Context, db QueryExecutor) (value string, err error) { + return ShowMySQLVariable(ctx, db, "binlog_format") +} + +// ShowBinlogRowImage queries variable 'binlog_row_image' and returns its values. +func ShowBinlogRowImage(ctx context.Context, db QueryExecutor) (value string, err error) { + return ShowMySQLVariable(ctx, db, "binlog_row_image") +} + +// ShowServerID queries variable 'server_id' and returns its value. +func ShowServerID(ctx context.Context, db QueryExecutor) (serverID uint64, err error) { + value, err := ShowMySQLVariable(ctx, db, "server_id") + if err != nil { + return 0, errors.Trace(err) + } + + serverID, err = strconv.ParseUint(value, 10, 64) + return serverID, errors.Annotatef(err, "parse server_id %s failed", value) +} + +// ShowMySQLVariable queries MySQL variable and returns its value. +func ShowMySQLVariable(ctx context.Context, db QueryExecutor, variable string) (value string, err error) { + query := fmt.Sprintf("SHOW GLOBAL VARIABLES LIKE '%s';", variable) + err = db.QueryRowContext(ctx, query).Scan(&variable, &value) + if err != nil { + return "", errors.Trace(err) + } + return value, nil +} + +// ShowGrants queries privileges for a mysql user. +// For mysql 8.0, if user has granted roles, ShowGrants also extract privilege from roles. +func ShowGrants(ctx context.Context, db QueryExecutor, user, host string) ([]string, error) { + if host == "" { + host = "%" + } + + var query string + if user == "" { + // for current user. + query = "SHOW GRANTS FOR CURRENT_USER" + } else { + query = fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", user, host) + } + + readGrantsFunc := func() ([]string, error) { + rows, err := db.QueryContext(ctx, query) + if err != nil { + return nil, errors.Trace(err) + } + defer rows.Close() + + grants := make([]string, 0, 8) + for rows.Next() { + var grant string + err = rows.Scan(&grant) + if err != nil { + return nil, errors.Trace(err) + } + + // TiDB parser does not support parse `IDENTIFIED BY PASSWORD `, + // but it may appear in some cases, ref: https://dev.mysql.com/doc/refman/5.6/en/show-grants.html. + // We do not need the password in grant statement, so we can replace it. + grant = strings.Replace(grant, "IDENTIFIED BY PASSWORD ", "IDENTIFIED BY PASSWORD 'secret'", 1) + + // support parse `IDENTIFIED BY PASSWORD WITH {GRANT OPTION | resource_option} ...` + grant = strings.Replace(grant, "IDENTIFIED BY PASSWORD WITH", "IDENTIFIED BY PASSWORD 'secret' WITH", 1) + + // support parse `IDENTIFIED BY PASSWORD` + if strings.HasSuffix(grant, "IDENTIFIED BY PASSWORD") { + grant = grant + " 'secret'" + } + + grants = append(grants, grant) + } + if err := rows.Err(); err != nil { + return nil, errors.Trace(err) + } + return grants, nil + } + + grants, err := readGrantsFunc() + if err != nil { + return nil, errors.Trace(err) + } + + // for mysql 8.0, we should collect granted roles + var roles []*auth.RoleIdentity + p := parser.New() + for _, grant := range grants { + node, err := p.ParseOneStmt(grant, "", "") + if err != nil { + return nil, err + } + if grantRoleStmt, ok := node.(*ast.GrantRoleStmt); ok { + roles = append(roles, grantRoleStmt.Roles...) + } + } + + if len(roles) == 0 { + return grants, nil + } + + var s strings.Builder + s.WriteString(query) + s.WriteString(" USING ") + for i, role := range roles { + if i > 0 { + s.WriteString(", ") + } + s.WriteString(role.String()) + } + query = s.String() + + return readGrantsFunc() +} diff --git a/util/dbutil/variable_test.go b/pkg/util/dbutil/variable_test.go similarity index 100% rename from util/dbutil/variable_test.go rename to pkg/util/dbutil/variable_test.go diff --git a/pkg/util/ddl-checker/BUILD.bazel b/pkg/util/ddl-checker/BUILD.bazel new file mode 100644 index 0000000000000..c8ee1ac53d0b2 --- /dev/null +++ b/pkg/util/ddl-checker/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ddl-checker", + srcs = [ + "ddl_syncer.go", + "executable_checker.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/ddl-checker", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/util/dbutil", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "ddl-checker_test", + timeout = "short", + srcs = ["executable_checker_test.go"], + embed = [":ddl-checker"], + flaky = True, + deps = [ + "//pkg/testkit", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/ddl-checker/ddl_syncer.go b/pkg/util/ddl-checker/ddl_syncer.go similarity index 97% rename from util/ddl-checker/ddl_syncer.go rename to pkg/util/ddl-checker/ddl_syncer.go index e6ee2c473eecc..f94a1451cdcdb 100644 --- a/util/ddl-checker/ddl_syncer.go +++ b/pkg/util/ddl-checker/ddl_syncer.go @@ -19,7 +19,7 @@ import ( "database/sql" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/dbutil" + "github.com/pingcap/tidb/pkg/util/dbutil" ) // DDLSyncer can sync the table structure from upstream(usually MySQL) to ExecutableChecker diff --git a/util/ddl-checker/executable_checker.go b/pkg/util/ddl-checker/executable_checker.go similarity index 95% rename from util/ddl-checker/executable_checker.go rename to pkg/util/ddl-checker/executable_checker.go index 7571cbfe3f524..a448c6b8fa787 100644 --- a/util/ddl-checker/executable_checker.go +++ b/pkg/util/ddl-checker/executable_checker.go @@ -20,11 +20,11 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/atomic" ) diff --git a/util/ddl-checker/executable_checker_test.go b/pkg/util/ddl-checker/executable_checker_test.go similarity index 99% rename from util/ddl-checker/executable_checker_test.go rename to pkg/util/ddl-checker/executable_checker_test.go index 943ef5908b7ea..430f9117ef570 100644 --- a/util/ddl-checker/executable_checker_test.go +++ b/pkg/util/ddl-checker/executable_checker_test.go @@ -18,7 +18,7 @@ import ( "container/list" "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/deadlockhistory/BUILD.bazel b/pkg/util/deadlockhistory/BUILD.bazel new file mode 100644 index 0000000000000..25a478a4526d9 --- /dev/null +++ b/pkg/util/deadlockhistory/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "deadlockhistory", + srcs = ["deadlock_history.go"], + importpath = "github.com/pingcap/tidb/pkg/util/deadlockhistory", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/logutil", + "//pkg/util/resourcegrouptag", + "@com_github_tikv_client_go_v2//error", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "deadlockhistory_test", + timeout = "short", + srcs = [ + "deadlock_history_test.go", + "main_test.go", + ], + embed = [":deadlockhistory"], + flaky = True, + deps = [ + "//pkg/parser", + "//pkg/parser/model", + "//pkg/testkit/testsetup", + "//pkg/types", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//assert", + "@com_github_tikv_client_go_v2//error", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/deadlockhistory/deadlock_history.go b/pkg/util/deadlockhistory/deadlock_history.go similarity index 98% rename from util/deadlockhistory/deadlock_history.go rename to pkg/util/deadlockhistory/deadlock_history.go index 6c5775a6de0cf..cf7e692f76a15 100644 --- a/util/deadlockhistory/deadlock_history.go +++ b/pkg/util/deadlockhistory/deadlock_history.go @@ -20,10 +20,10 @@ import ( "sync" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/resourcegrouptag" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/resourcegrouptag" tikverr "github.com/tikv/client-go/v2/error" "go.uber.org/zap" ) diff --git a/util/deadlockhistory/deadlock_history_test.go b/pkg/util/deadlockhistory/deadlock_history_test.go similarity index 98% rename from util/deadlockhistory/deadlock_history_test.go rename to pkg/util/deadlockhistory/deadlock_history_test.go index 94ed2e893a7d4..a8154b19e1ec9 100644 --- a/util/deadlockhistory/deadlock_history_test.go +++ b/pkg/util/deadlockhistory/deadlock_history_test.go @@ -20,9 +20,9 @@ import ( "github.com/pingcap/kvproto/pkg/deadlock" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/assert" tikverr "github.com/tikv/client-go/v2/error" diff --git a/pkg/util/deadlockhistory/main_test.go b/pkg/util/deadlockhistory/main_test.go new file mode 100644 index 0000000000000..1ee38c2ae7f03 --- /dev/null +++ b/pkg/util/deadlockhistory/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package deadlockhistory + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/disjointset/BUILD.bazel b/pkg/util/disjointset/BUILD.bazel new file mode 100644 index 0000000000000..941410ed9d54b --- /dev/null +++ b/pkg/util/disjointset/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "disjointset", + srcs = ["int_set.go"], + importpath = "github.com/pingcap/tidb/pkg/util/disjointset", + visibility = ["//visibility:public"], +) + +go_test( + name = "disjointset_test", + timeout = "short", + srcs = [ + "int_set_test.go", + "main_test.go", + ], + embed = [":disjointset"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/disjointset/int_set.go b/pkg/util/disjointset/int_set.go similarity index 100% rename from util/disjointset/int_set.go rename to pkg/util/disjointset/int_set.go diff --git a/util/disjointset/int_set_test.go b/pkg/util/disjointset/int_set_test.go similarity index 100% rename from util/disjointset/int_set_test.go rename to pkg/util/disjointset/int_set_test.go diff --git a/pkg/util/disjointset/main_test.go b/pkg/util/disjointset/main_test.go new file mode 100644 index 0000000000000..60272b24db0ba --- /dev/null +++ b/pkg/util/disjointset/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package disjointset + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/disk/BUILD.bazel b/pkg/util/disk/BUILD.bazel new file mode 100644 index 0000000000000..50185bc9af681 --- /dev/null +++ b/pkg/util/disk/BUILD.bazel @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "disk", + srcs = [ + "tempDir.go", + "tracker.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/disk", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/parser/terror", + "//pkg/util/memory", + "@com_github_danjacques_gofslock//fslock", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_golang_x_sync//singleflight", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "disk_test", + timeout = "short", + srcs = [ + "main_test.go", + "tempDir_test.go", + ], + embed = [":disk"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/disk/main_test.go b/pkg/util/disk/main_test.go new file mode 100644 index 0000000000000..e02420162d5ef --- /dev/null +++ b/pkg/util/disk/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package disk + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/disk/tempDir.go b/pkg/util/disk/tempDir.go similarity index 97% rename from util/disk/tempDir.go rename to pkg/util/disk/tempDir.go index 1a615ccd647b1..0289570655782 100644 --- a/util/disk/tempDir.go +++ b/pkg/util/disk/tempDir.go @@ -21,8 +21,8 @@ import ( "github.com/danjacques/gofslock/fslock" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/terror" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/terror" "go.uber.org/zap" "golang.org/x/sync/singleflight" ) diff --git a/util/disk/tempDir_test.go b/pkg/util/disk/tempDir_test.go similarity index 97% rename from util/disk/tempDir_test.go rename to pkg/util/disk/tempDir_test.go index 09e2a87c66709..b0fe1d0c14248 100644 --- a/util/disk/tempDir_test.go +++ b/pkg/util/disk/tempDir_test.go @@ -19,7 +19,7 @@ import ( "sync" "testing" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/disk/tracker.go b/pkg/util/disk/tracker.go new file mode 100644 index 0000000000000..26cb913770b90 --- /dev/null +++ b/pkg/util/disk/tracker.go @@ -0,0 +1,30 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package disk + +import ( + "github.com/pingcap/tidb/pkg/util/memory" +) + +// Tracker is used to track the disk usage during query execution. +type Tracker = memory.Tracker + +// NewTracker creates a disk tracker. +// 1. "label" is the label used in the usage string. +// 2. "bytesLimit <= 0" means no limit. +var NewTracker = memory.NewTracker + +// NewGlobalTrcaker creates a global disk tracker. +var NewGlobalTrcaker = memory.NewGlobalTracker diff --git a/pkg/util/distrole/BUILD.bazel b/pkg/util/distrole/BUILD.bazel new file mode 100644 index 0000000000000..3f3f8e208504a --- /dev/null +++ b/pkg/util/distrole/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "distrole", + srcs = ["role.go"], + importpath = "github.com/pingcap/tidb/pkg/util/distrole", + visibility = ["//visibility:public"], +) diff --git a/util/distrole/role.go b/pkg/util/distrole/role.go similarity index 100% rename from util/distrole/role.go rename to pkg/util/distrole/role.go diff --git a/pkg/util/disttask/BUILD.bazel b/pkg/util/disttask/BUILD.bazel new file mode 100644 index 0000000000000..dc6f7fb37ff53 --- /dev/null +++ b/pkg/util/disttask/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "disttask", + srcs = ["idservice.go"], + importpath = "github.com/pingcap/tidb/pkg/util/disttask", + visibility = ["//visibility:public"], + deps = ["//pkg/domain/infosync"], +) + +go_test( + name = "disttask_test", + timeout = "short", + srcs = ["idservice_test.go"], + embed = [":disttask"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/disttask/idservice.go b/pkg/util/disttask/idservice.go similarity index 97% rename from util/disttask/idservice.go rename to pkg/util/disttask/idservice.go index c41fb1820eae0..8f3d3d891f5fc 100644 --- a/util/disttask/idservice.go +++ b/pkg/util/disttask/idservice.go @@ -19,7 +19,7 @@ import ( "fmt" "net" - "github.com/pingcap/tidb/domain/infosync" + "github.com/pingcap/tidb/pkg/domain/infosync" ) // GenerateExecID used to generate IP:port as exec_id value diff --git a/util/disttask/idservice_test.go b/pkg/util/disttask/idservice_test.go similarity index 100% rename from util/disttask/idservice_test.go rename to pkg/util/disttask/idservice_test.go diff --git a/pkg/util/domainutil/BUILD.bazel b/pkg/util/domainutil/BUILD.bazel new file mode 100644 index 0000000000000..32ccbf9120e5d --- /dev/null +++ b/pkg/util/domainutil/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "domainutil", + srcs = ["repair_vars.go"], + importpath = "github.com/pingcap/tidb/pkg/util/domainutil", + visibility = ["//visibility:public"], + deps = ["//pkg/parser/model"], +) diff --git a/util/domainutil/repair_vars.go b/pkg/util/domainutil/repair_vars.go similarity index 99% rename from util/domainutil/repair_vars.go rename to pkg/util/domainutil/repair_vars.go index 59c3389a312a8..1d735ddfbbde3 100644 --- a/util/domainutil/repair_vars.go +++ b/pkg/util/domainutil/repair_vars.go @@ -18,7 +18,7 @@ import ( "strings" "sync" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" ) type repairInfo struct { diff --git a/pkg/util/encrypt/BUILD.bazel b/pkg/util/encrypt/BUILD.bazel new file mode 100644 index 0000000000000..cf848a5fbda83 --- /dev/null +++ b/pkg/util/encrypt/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "encrypt", + srcs = [ + "aes.go", + "aes_layer.go", + "crypt.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/encrypt", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "encrypt_test", + timeout = "short", + srcs = [ + "aes_layer_test.go", + "aes_test.go", + "crypt_test.go", + "main_test.go", + ], + embed = [":encrypt"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/checksum", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/encrypt/aes.go b/pkg/util/encrypt/aes.go similarity index 100% rename from util/encrypt/aes.go rename to pkg/util/encrypt/aes.go diff --git a/util/encrypt/aes_layer.go b/pkg/util/encrypt/aes_layer.go similarity index 100% rename from util/encrypt/aes_layer.go rename to pkg/util/encrypt/aes_layer.go diff --git a/util/encrypt/aes_layer_test.go b/pkg/util/encrypt/aes_layer_test.go similarity index 99% rename from util/encrypt/aes_layer_test.go rename to pkg/util/encrypt/aes_layer_test.go index f2f502adfb904..f839278ce4289 100644 --- a/util/encrypt/aes_layer_test.go +++ b/pkg/util/encrypt/aes_layer_test.go @@ -20,7 +20,7 @@ import ( "os" "testing" - "github.com/pingcap/tidb/util/checksum" + "github.com/pingcap/tidb/pkg/util/checksum" "github.com/stretchr/testify/require" ) diff --git a/util/encrypt/aes_test.go b/pkg/util/encrypt/aes_test.go similarity index 100% rename from util/encrypt/aes_test.go rename to pkg/util/encrypt/aes_test.go diff --git a/util/encrypt/crypt.go b/pkg/util/encrypt/crypt.go similarity index 100% rename from util/encrypt/crypt.go rename to pkg/util/encrypt/crypt.go diff --git a/util/encrypt/crypt_test.go b/pkg/util/encrypt/crypt_test.go similarity index 100% rename from util/encrypt/crypt_test.go rename to pkg/util/encrypt/crypt_test.go diff --git a/pkg/util/encrypt/main_test.go b/pkg/util/encrypt/main_test.go new file mode 100644 index 0000000000000..b791ab5bce537 --- /dev/null +++ b/pkg/util/encrypt/main_test.go @@ -0,0 +1,28 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package encrypt + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + goleak.VerifyTestMain(m) +} diff --git a/pkg/util/engine/BUILD.bazel b/pkg/util/engine/BUILD.bazel new file mode 100644 index 0000000000000..5afe79b899187 --- /dev/null +++ b/pkg/util/engine/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "engine", + srcs = ["engine.go"], + importpath = "github.com/pingcap/tidb/pkg/util/engine", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_kvproto//pkg/metapb"], +) diff --git a/util/engine/engine.go b/pkg/util/engine/engine.go similarity index 100% rename from util/engine/engine.go rename to pkg/util/engine/engine.go diff --git a/util/errors.go b/pkg/util/errors.go similarity index 100% rename from util/errors.go rename to pkg/util/errors.go diff --git a/pkg/util/errors_test.go b/pkg/util/errors_test.go new file mode 100644 index 0000000000000..d75e41855a6ef --- /dev/null +++ b/pkg/util/errors_test.go @@ -0,0 +1,36 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package util_test + +import ( + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestOriginError(t *testing.T) { + require.Nil(t, util.OriginError(nil)) + + err1 := errors.New("err1") + require.Equal(t, err1, util.OriginError(err1)) + + err2 := errors.Trace(err1) + require.Equal(t, err1, util.OriginError(err2)) + + err3 := errors.Trace(err2) + require.Equal(t, err1, util.OriginError(err3)) +} diff --git a/util/etcd.go b/pkg/util/etcd.go similarity index 95% rename from util/etcd.go rename to pkg/util/etcd.go index 8657241e4c178..bff00a8d66428 100644 --- a/util/etcd.go +++ b/pkg/util/etcd.go @@ -21,9 +21,9 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/logutil" clientv3 "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "go.uber.org/zap" diff --git a/pkg/util/etcd/BUILD.bazel b/pkg/util/etcd/BUILD.bazel new file mode 100644 index 0000000000000..d53b6d7758e23 --- /dev/null +++ b/pkg/util/etcd/BUILD.bazel @@ -0,0 +1,27 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "etcd", + srcs = ["etcd.go"], + importpath = "github.com/pingcap/tidb/pkg/util/etcd", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_client_v3//namespace", + ], +) + +go_test( + name = "etcd_test", + timeout = "short", + srcs = ["etcd_test.go"], + embed = [":etcd"], + flaky = True, + deps = [ + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@io_etcd_go_etcd_client_v3//:client", + "@io_etcd_go_etcd_tests_v3//integration", + ], +) diff --git a/util/etcd/etcd.go b/pkg/util/etcd/etcd.go similarity index 100% rename from util/etcd/etcd.go rename to pkg/util/etcd/etcd.go diff --git a/util/etcd/etcd_test.go b/pkg/util/etcd/etcd_test.go similarity index 100% rename from util/etcd/etcd_test.go rename to pkg/util/etcd/etcd_test.go diff --git a/pkg/util/execdetails/BUILD.bazel b/pkg/util/execdetails/BUILD.bazel new file mode 100644 index 0000000000000..210bd8e0cd590 --- /dev/null +++ b/pkg/util/execdetails/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "execdetails", + srcs = ["execdetails.go"], + importpath = "github.com/pingcap/tidb/pkg/util/execdetails", + visibility = ["//visibility:public"], + deps = [ + "@com_github_influxdata_tdigest//:tdigest", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "execdetails_test", + timeout = "short", + srcs = [ + "execdetails_test.go", + "main_test.go", + ], + embed = [":execdetails"], + flaky = True, + race = "on", + deps = [ + "//pkg/testkit/testsetup", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/execdetails/execdetails.go b/pkg/util/execdetails/execdetails.go similarity index 100% rename from util/execdetails/execdetails.go rename to pkg/util/execdetails/execdetails.go diff --git a/util/execdetails/execdetails_test.go b/pkg/util/execdetails/execdetails_test.go similarity index 100% rename from util/execdetails/execdetails_test.go rename to pkg/util/execdetails/execdetails_test.go diff --git a/pkg/util/execdetails/main_test.go b/pkg/util/execdetails/main_test.go new file mode 100644 index 0000000000000..401137ccd8c65 --- /dev/null +++ b/pkg/util/execdetails/main_test.go @@ -0,0 +1,31 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. +package execdetails + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/expensivequery/BUILD.bazel b/pkg/util/expensivequery/BUILD.bazel new file mode 100644 index 0000000000000..b3d8412e55421 --- /dev/null +++ b/pkg/util/expensivequery/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "expensivequery", + srcs = ["expensivequery.go"], + importpath = "github.com/pingcap/tidb/pkg/util/expensivequery", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/logutil", + "@com_github_pingcap_log//:log", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "expensivequery_test", + timeout = "short", + srcs = ["expensivequerey_test.go"], + embed = [":expensivequery"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/expensivequery/expensivequerey_test.go b/pkg/util/expensivequery/expensivequerey_test.go similarity index 95% rename from util/expensivequery/expensivequerey_test.go rename to pkg/util/expensivequery/expensivequerey_test.go index 7a7d6c8815a5d..3013355b111e2 100644 --- a/util/expensivequery/expensivequerey_test.go +++ b/pkg/util/expensivequery/expensivequerey_test.go @@ -17,7 +17,7 @@ package expensivequery import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/util/expensivequery/expensivequery.go b/pkg/util/expensivequery/expensivequery.go similarity index 96% rename from util/expensivequery/expensivequery.go rename to pkg/util/expensivequery/expensivequery.go index 9ce5cb81d747d..9a65b1f002ada 100644 --- a/util/expensivequery/expensivequery.go +++ b/pkg/util/expensivequery/expensivequery.go @@ -19,10 +19,10 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/pkg/util/extsort/BUILD.bazel b/pkg/util/extsort/BUILD.bazel new file mode 100644 index 0000000000000..8a16035729f05 --- /dev/null +++ b/pkg/util/extsort/BUILD.bazel @@ -0,0 +1,42 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "extsort", + srcs = [ + "disk_sorter.go", + "external_sorter.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/extsort", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/generic", + "//pkg/util/syncutil", + "@com_github_cockroachdb_pebble//:pebble", + "@com_github_cockroachdb_pebble//sstable", + "@com_github_cockroachdb_pebble//vfs", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@org_golang_x_sync//errgroup", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "extsort_test", + timeout = "short", + srcs = [ + "disk_sorter_test.go", + "external_sorter_test.go", + ], + embed = [":extsort"], + flaky = True, + deps = [ + "@com_github_cockroachdb_pebble//:pebble", + "@com_github_cockroachdb_pebble//sstable", + "@com_github_cockroachdb_pebble//vfs", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + "@org_golang_x_sync//errgroup", + "@org_uber_go_zap//:zap", + ], +) diff --git a/util/extsort/disk_sorter.go b/pkg/util/extsort/disk_sorter.go similarity index 99% rename from util/extsort/disk_sorter.go rename to pkg/util/extsort/disk_sorter.go index 7ea5c5f6454d3..87296bd67c505 100644 --- a/util/extsort/disk_sorter.go +++ b/pkg/util/extsort/disk_sorter.go @@ -35,8 +35,8 @@ import ( "github.com/cockroachdb/pebble/vfs" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/generic" - "github.com/pingcap/tidb/util/syncutil" + "github.com/pingcap/tidb/pkg/util/generic" + "github.com/pingcap/tidb/pkg/util/syncutil" "go.uber.org/zap" "golang.org/x/sync/errgroup" ) diff --git a/util/extsort/disk_sorter_test.go b/pkg/util/extsort/disk_sorter_test.go similarity index 100% rename from util/extsort/disk_sorter_test.go rename to pkg/util/extsort/disk_sorter_test.go diff --git a/util/extsort/external_sorter.go b/pkg/util/extsort/external_sorter.go similarity index 100% rename from util/extsort/external_sorter.go rename to pkg/util/extsort/external_sorter.go diff --git a/util/extsort/external_sorter_test.go b/pkg/util/extsort/external_sorter_test.go similarity index 100% rename from util/extsort/external_sorter_test.go rename to pkg/util/extsort/external_sorter_test.go diff --git a/pkg/util/fastrand/BUILD.bazel b/pkg/util/fastrand/BUILD.bazel new file mode 100644 index 0000000000000..84df5c88e852a --- /dev/null +++ b/pkg/util/fastrand/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "fastrand", + srcs = ["random.go"], + importpath = "github.com/pingcap/tidb/pkg/util/fastrand", + visibility = ["//visibility:public"], +) + +go_test( + name = "fastrand_test", + timeout = "short", + srcs = [ + "main_test.go", + "random_test.go", + ], + embed = [":fastrand"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/fastrand/main_test.go b/pkg/util/fastrand/main_test.go new file mode 100644 index 0000000000000..91dfb427f065b --- /dev/null +++ b/pkg/util/fastrand/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package fastrand + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/fastrand/random.go b/pkg/util/fastrand/random.go similarity index 100% rename from util/fastrand/random.go rename to pkg/util/fastrand/random.go diff --git a/util/fastrand/random_test.go b/pkg/util/fastrand/random_test.go similarity index 100% rename from util/fastrand/random_test.go rename to pkg/util/fastrand/random_test.go diff --git a/pkg/util/filter/BUILD.bazel b/pkg/util/filter/BUILD.bazel new file mode 100644 index 0000000000000..839902a4aa0a4 --- /dev/null +++ b/pkg/util/filter/BUILD.bazel @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "filter", + srcs = [ + "filter.go", + "schema.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/filter", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/table-filter", + "//pkg/util/table-rule-selector", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "filter_test", + timeout = "short", + srcs = [ + "filter_test.go", + "schema_test.go", + ], + embed = [":filter"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/filter/README.md b/pkg/util/filter/README.md similarity index 100% rename from util/filter/README.md rename to pkg/util/filter/README.md diff --git a/util/filter/filter.go b/pkg/util/filter/filter.go similarity index 98% rename from util/filter/filter.go rename to pkg/util/filter/filter.go index 2c96ae0e788bd..0091e9ca63a74 100644 --- a/util/filter/filter.go +++ b/pkg/util/filter/filter.go @@ -20,8 +20,8 @@ import ( "sync" "github.com/pingcap/errors" - tfilter "github.com/pingcap/tidb/util/table-filter" - selector "github.com/pingcap/tidb/util/table-rule-selector" + tfilter "github.com/pingcap/tidb/pkg/util/table-filter" + selector "github.com/pingcap/tidb/pkg/util/table-rule-selector" ) // ActionType is do or ignore something diff --git a/util/filter/filter_test.go b/pkg/util/filter/filter_test.go similarity index 100% rename from util/filter/filter_test.go rename to pkg/util/filter/filter_test.go diff --git a/util/filter/schema.go b/pkg/util/filter/schema.go similarity index 100% rename from util/filter/schema.go rename to pkg/util/filter/schema.go diff --git a/util/filter/schema_test.go b/pkg/util/filter/schema_test.go similarity index 100% rename from util/filter/schema_test.go rename to pkg/util/filter/schema_test.go diff --git a/pkg/util/format/BUILD.bazel b/pkg/util/format/BUILD.bazel new file mode 100644 index 0000000000000..952d4cc837592 --- /dev/null +++ b/pkg/util/format/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "format", + srcs = ["format.go"], + importpath = "github.com/pingcap/tidb/pkg/util/format", + visibility = ["//visibility:public"], +) + +go_test( + name = "format_test", + timeout = "short", + srcs = [ + "format_test.go", + "main_test.go", + ], + embed = [":format"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/format/format.go b/pkg/util/format/format.go similarity index 100% rename from util/format/format.go rename to pkg/util/format/format.go diff --git a/util/format/format_test.go b/pkg/util/format/format_test.go similarity index 100% rename from util/format/format_test.go rename to pkg/util/format/format_test.go diff --git a/pkg/util/format/main_test.go b/pkg/util/format/main_test.go new file mode 100644 index 0000000000000..760fc94afa085 --- /dev/null +++ b/pkg/util/format/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package format + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/gctuner/BUILD.bazel b/pkg/util/gctuner/BUILD.bazel new file mode 100644 index 0000000000000..0052b084a1c65 --- /dev/null +++ b/pkg/util/gctuner/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "gctuner", + srcs = [ + "finalizer.go", + "mem.go", + "memory_limit_tuner.go", + "tuner.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/gctuner", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util", + "//pkg/util/intest", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "gctuner_test", + timeout = "short", + srcs = [ + "finalizer_test.go", + "mem_test.go", + "memory_limit_tuner_test.go", + "tuner_test.go", + ], + embed = [":gctuner"], + flaky = True, + race = "on", + shard_count = 5, + deps = [ + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/gctuner/finalizer.go b/pkg/util/gctuner/finalizer.go similarity index 100% rename from util/gctuner/finalizer.go rename to pkg/util/gctuner/finalizer.go diff --git a/util/gctuner/finalizer_test.go b/pkg/util/gctuner/finalizer_test.go similarity index 100% rename from util/gctuner/finalizer_test.go rename to pkg/util/gctuner/finalizer_test.go diff --git a/util/gctuner/mem.go b/pkg/util/gctuner/mem.go similarity index 94% rename from util/gctuner/mem.go rename to pkg/util/gctuner/mem.go index 9fb85f1d801cb..e1a4fff960799 100644 --- a/util/gctuner/mem.go +++ b/pkg/util/gctuner/mem.go @@ -15,7 +15,7 @@ package gctuner import ( - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/memory" ) func readMemoryInuse() uint64 { diff --git a/util/gctuner/mem_test.go b/pkg/util/gctuner/mem_test.go similarity index 100% rename from util/gctuner/mem_test.go rename to pkg/util/gctuner/mem_test.go diff --git a/util/gctuner/memory_limit_tuner.go b/pkg/util/gctuner/memory_limit_tuner.go similarity index 97% rename from util/gctuner/memory_limit_tuner.go rename to pkg/util/gctuner/memory_limit_tuner.go index 204c569c0cd45..b226248bcd0a6 100644 --- a/util/gctuner/memory_limit_tuner.go +++ b/pkg/util/gctuner/memory_limit_tuner.go @@ -20,9 +20,9 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/intest" + "github.com/pingcap/tidb/pkg/util/memory" atomicutil "go.uber.org/atomic" ) diff --git a/util/gctuner/memory_limit_tuner_test.go b/pkg/util/gctuner/memory_limit_tuner_test.go similarity index 94% rename from util/gctuner/memory_limit_tuner_test.go rename to pkg/util/gctuner/memory_limit_tuner_test.go index fecd859f15cac..ab354530d852c 100644 --- a/util/gctuner/memory_limit_tuner_test.go +++ b/pkg/util/gctuner/memory_limit_tuner_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) @@ -45,9 +45,9 @@ func (a *mockAllocator) freeAll() { } func TestGlobalMemoryTuner(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/util/gctuner/testMemoryLimitTuner", "return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/gctuner/testMemoryLimitTuner")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/util/gctuner/testMemoryLimitTuner")) }() // Close GOGCTuner gogcTuner := EnableGOGCTuner.Load() diff --git a/util/gctuner/tuner.go b/pkg/util/gctuner/tuner.go similarity index 99% rename from util/gctuner/tuner.go rename to pkg/util/gctuner/tuner.go index 9b7e22f95d0fe..9324cda85f703 100644 --- a/util/gctuner/tuner.go +++ b/pkg/util/gctuner/tuner.go @@ -20,7 +20,7 @@ import ( "strconv" "sync/atomic" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" ) var ( diff --git a/util/gctuner/tuner_test.go b/pkg/util/gctuner/tuner_test.go similarity index 100% rename from util/gctuner/tuner_test.go rename to pkg/util/gctuner/tuner_test.go diff --git a/pkg/util/gcutil/BUILD.bazel b/pkg/util/gcutil/BUILD.bazel new file mode 100644 index 0000000000000..5a684a90c6ac3 --- /dev/null +++ b/pkg/util/gcutil/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "gcutil", + srcs = ["gcutil.go"], + importpath = "github.com/pingcap/tidb/pkg/util/gcutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/util/sqlexec", + "@com_github_pingcap_errors//:errors", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//util", + ], +) diff --git a/util/gcutil/gcutil.go b/pkg/util/gcutil/gcutil.go similarity index 93% rename from util/gcutil/gcutil.go rename to pkg/util/gcutil/gcutil.go index 9474cace52378..842a73708b065 100644 --- a/util/gcutil/gcutil.go +++ b/pkg/util/gcutil/gcutil.go @@ -18,11 +18,11 @@ import ( "context" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/sqlexec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/sqlexec" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/util" ) diff --git a/pkg/util/generatedexpr/BUILD.bazel b/pkg/util/generatedexpr/BUILD.bazel new file mode 100644 index 0000000000000..18a7323de356d --- /dev/null +++ b/pkg/util/generatedexpr/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "generatedexpr", + srcs = ["generated_expr.go"], + importpath = "github.com/pingcap/tidb/pkg/util/generatedexpr", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/model", + "//pkg/util", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "generatedexpr_test", + timeout = "short", + srcs = [ + "gen_expr_test.go", + "main_test.go", + ], + embed = [":generatedexpr"], + flaky = True, + deps = [ + "//pkg/parser/ast", + "//pkg/testkit/testsetup", + "//pkg/types/parser_driver", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/generatedexpr/gen_expr_test.go b/pkg/util/generatedexpr/gen_expr_test.go similarity index 90% rename from util/generatedexpr/gen_expr_test.go rename to pkg/util/generatedexpr/gen_expr_test.go index 6b1d39f2c2490..ea4cc590ca036 100644 --- a/util/generatedexpr/gen_expr_test.go +++ b/pkg/util/generatedexpr/gen_expr_test.go @@ -17,8 +17,8 @@ package generatedexpr import ( "testing" - "github.com/pingcap/tidb/parser/ast" - _ "github.com/pingcap/tidb/types/parser_driver" + "github.com/pingcap/tidb/pkg/parser/ast" + _ "github.com/pingcap/tidb/pkg/types/parser_driver" "github.com/stretchr/testify/require" ) diff --git a/util/generatedexpr/generated_expr.go b/pkg/util/generatedexpr/generated_expr.go similarity index 92% rename from util/generatedexpr/generated_expr.go rename to pkg/util/generatedexpr/generated_expr.go index aff83341cfd6d..cc7f8055c3dbe 100644 --- a/util/generatedexpr/generated_expr.go +++ b/pkg/util/generatedexpr/generated_expr.go @@ -18,11 +18,11 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util" ) // nameResolver is the visitor to resolve table name and column name. diff --git a/pkg/util/generatedexpr/main_test.go b/pkg/util/generatedexpr/main_test.go new file mode 100644 index 0000000000000..e9bc74444d9c2 --- /dev/null +++ b/pkg/util/generatedexpr/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package generatedexpr + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/generic/BUILD.bazel b/pkg/util/generic/BUILD.bazel new file mode 100644 index 0000000000000..64bb0f007f2a0 --- /dev/null +++ b/pkg/util/generic/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "generic", + srcs = ["sync_map.go"], + importpath = "github.com/pingcap/tidb/pkg/util/generic", + visibility = ["//visibility:public"], +) + +go_test( + name = "generic_test", + timeout = "short", + srcs = ["sync_map_test.go"], + flaky = True, + deps = [ + ":generic", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/generic/sync_map.go b/pkg/util/generic/sync_map.go similarity index 100% rename from util/generic/sync_map.go rename to pkg/util/generic/sync_map.go diff --git a/util/generic/sync_map_test.go b/pkg/util/generic/sync_map_test.go similarity index 97% rename from util/generic/sync_map_test.go rename to pkg/util/generic/sync_map_test.go index 6e0553a928664..433aacd2d42b4 100644 --- a/util/generic/sync_map_test.go +++ b/pkg/util/generic/sync_map_test.go @@ -18,7 +18,7 @@ import ( "sort" "testing" - "github.com/pingcap/tidb/util/generic" + "github.com/pingcap/tidb/pkg/util/generic" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/globalconn/BUILD.bazel b/pkg/util/globalconn/BUILD.bazel new file mode 100644 index 0000000000000..9cc7cdcc23865 --- /dev/null +++ b/pkg/util/globalconn/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "globalconn", + srcs = [ + "globalconn.go", + "pool.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/globalconn", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/logutil", + "@com_github_cznic_mathutil//:mathutil", + "@com_github_ngaut_sync2//:sync2", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "globalconn_test", + timeout = "short", + srcs = [ + "globalconn_test.go", + "pool_test.go", + ], + flaky = True, + deps = [ + ":globalconn", + "@com_github_cznic_mathutil//:mathutil", + "@com_github_stretchr_testify//assert", + ], +) diff --git a/util/globalconn/globalconn.go b/pkg/util/globalconn/globalconn.go similarity index 99% rename from util/globalconn/globalconn.go rename to pkg/util/globalconn/globalconn.go index 3089ac96127ec..0d9c2aaa9aa3c 100644 --- a/util/globalconn/globalconn.go +++ b/pkg/util/globalconn/globalconn.go @@ -21,7 +21,7 @@ import ( "strconv" "github.com/ngaut/sync2" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/globalconn/globalconn_test.go b/pkg/util/globalconn/globalconn_test.go similarity index 99% rename from util/globalconn/globalconn_test.go rename to pkg/util/globalconn/globalconn_test.go index ad29c2faf34d2..5d3ec59c8f9fd 100644 --- a/util/globalconn/globalconn_test.go +++ b/pkg/util/globalconn/globalconn_test.go @@ -20,7 +20,7 @@ import ( "runtime" "testing" - "github.com/pingcap/tidb/util/globalconn" + "github.com/pingcap/tidb/pkg/util/globalconn" "github.com/stretchr/testify/assert" ) diff --git a/util/globalconn/pool.go b/pkg/util/globalconn/pool.go similarity index 100% rename from util/globalconn/pool.go rename to pkg/util/globalconn/pool.go diff --git a/pkg/util/globalconn/pool_test.go b/pkg/util/globalconn/pool_test.go new file mode 100644 index 0000000000000..353d86804a79c --- /dev/null +++ b/pkg/util/globalconn/pool_test.go @@ -0,0 +1,500 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package globalconn_test + +import ( + "fmt" + "math" + "runtime" + "sync" + "sync/atomic" + "testing" + + "github.com/cznic/mathutil" + "github.com/pingcap/tidb/pkg/util/globalconn" + "github.com/stretchr/testify/assert" +) + +func TestAutoIncPool(t *testing.T) { + assert := assert.New(t) + + const SizeInBits uint32 = 8 + const Size uint64 = 1 << SizeInBits + const TryCnt = 4 + + var ( + pool globalconn.AutoIncPool + val uint64 + ok bool + i uint64 + ) + + pool.InitExt(Size, true, TryCnt) + assert.Equal(int(Size), pool.Cap()) + assert.Equal(0, pool.Len()) + + // get all. + for i = 1; i < Size; i++ { + val, ok = pool.Get() + assert.True(ok) + assert.Equal(i, val) + } + val, ok = pool.Get() + assert.True(ok) + assert.Equal(uint64(0), val) // wrap around to 0 + assert.Equal(int(Size), pool.Len()) + + _, ok = pool.Get() // exhausted. try TryCnt times, lastID is added to 0+TryCnt. + assert.False(ok) + + nextVal := uint64(TryCnt + 1) + pool.Put(nextVal) + val, ok = pool.Get() + assert.True(ok) + assert.Equal(nextVal, val) + + nextVal += TryCnt - 1 + pool.Put(nextVal) + val, ok = pool.Get() + assert.True(ok) + assert.Equal(nextVal, val) + + nextVal += TryCnt + 1 + pool.Put(nextVal) + _, ok = pool.Get() + assert.False(ok) +} + +func TestLockFreePoolBasic(t *testing.T) { + assert := assert.New(t) + + const SizeInBits uint32 = 8 + const Size uint64 = 1< 0 { + pool.InitForTest(headPos, fillCount) + } + + return &pool +} + +func prepareConcurrencyTest(pool globalconn.IDPool, producers int, consumers int, requests int, total *int64) (ready chan struct{}, done chan struct{}, wgProducer *sync.WaitGroup, wgConsumer *sync.WaitGroup) { + ready = make(chan struct{}) + done = make(chan struct{}) + + wgProducer = &sync.WaitGroup{} + if producers > 0 { + reqsPerProducer := (requests + producers - 1) / producers + wgProducer.Add(producers) + for p := 0; p < producers; p++ { + go func(p int) { + defer wgProducer.Done() + <-ready + + for i := p * reqsPerProducer; i < (p+1)*reqsPerProducer && i < requests; i++ { + for !pool.Put(uint64(i)) { + runtime.Gosched() + } + } + }(p) + } + } + + wgConsumer = &sync.WaitGroup{} + if consumers > 0 { + wgConsumer.Add(consumers) + for c := 0; c < consumers; c++ { + go func(c int) { + defer wgConsumer.Done() + <-ready + + var sum int64 + Loop: + for { + val, ok := pool.Get() + if ok { + sum += int64(val) + continue + } + select { + case <-done: + break Loop + default: + runtime.Gosched() + } + } + atomic.AddInt64(total, sum) + }(c) + } + } + + return ready, done, wgProducer, wgConsumer +} + +func doConcurrencyTest(ready chan struct{}, done chan struct{}, wgProducer *sync.WaitGroup, wgConsumer *sync.WaitGroup) { + // logutil.BgLogger().Info("Init", zap.Stringer("pool", q)) + close(ready) + wgProducer.Wait() + // logutil.BgLogger().Info("Snapshot on producing done", zap.Stringer("pool", q)) + close(done) + wgConsumer.Wait() + // logutil.BgLogger().Info("Finally", zap.Stringer("pool", q)) +} + +func expectedConcurrencyTestResult(poolSizeInBits uint32, fillCount uint32, producers int, consumers int, requests int) (expected int64) { + if producers > 0 && consumers > 0 { + expected += (int64(requests) - 1) * int64(requests) / 2 + } + if fillCount > 0 { + fillCount = mathutil.MinUint32(1<" + } + return fmt.Sprintf("Config(%+v)", *c) +} diff --git a/util/importer/data.go b/pkg/util/importer/data.go similarity index 100% rename from util/importer/data.go rename to pkg/util/importer/data.go diff --git a/util/importer/db.go b/pkg/util/importer/db.go similarity index 98% rename from util/importer/db.go rename to pkg/util/importer/db.go index 14a5bf46ecddd..9194da31ea842 100644 --- a/util/importer/db.go +++ b/pkg/util/importer/db.go @@ -23,8 +23,8 @@ import ( _ "github.com/go-sql-driver/mysql" // for mysql driver "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbutil" "go.uber.org/zap" ) diff --git a/util/importer/importer.go b/pkg/util/importer/importer.go similarity index 100% rename from util/importer/importer.go rename to pkg/util/importer/importer.go diff --git a/util/importer/job.go b/pkg/util/importer/job.go similarity index 100% rename from util/importer/job.go rename to pkg/util/importer/job.go diff --git a/pkg/util/importer/parser.go b/pkg/util/importer/parser.go new file mode 100644 index 0000000000000..48e58985e98b0 --- /dev/null +++ b/pkg/util/importer/parser.go @@ -0,0 +1,275 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package importer + +import ( + "fmt" + "strconv" + "strings" + + "github.com/pingcap/errors" + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/types" + _ "github.com/pingcap/tidb/pkg/types/parser_driver" // for parser driver + "github.com/pingcap/tidb/pkg/util/dbutil" + "go.uber.org/zap" +) + +type column struct { + data *datum + tp *types.FieldType + table *table + name string + comment string + min string + max string + set []string + idx int + step int64 +} + +func (col *column) String() string { + if col == nil { + return "" + } + + return fmt.Sprintf("[column]idx: %d, name: %s, tp: %v, min: %s, max: %s, step: %d, set: %v\n", + col.idx, col.name, col.tp, col.min, col.max, col.step, col.set) +} + +func (col *column) parseRule(kvs []string) { + if len(kvs) != 2 { + return + } + + key := strings.TrimSpace(kvs[0]) + value := strings.TrimSpace(kvs[1]) + if key == "range" { + fields := strings.Split(value, ",") + if len(fields) == 1 { + col.min = strings.TrimSpace(fields[0]) + } else if len(fields) == 2 { + col.min = strings.TrimSpace(fields[0]) + col.max = strings.TrimSpace(fields[1]) + } + } else if key == "step" { + var err error + col.step, err = strconv.ParseInt(value, 10, 64) + if err != nil { + log.Fatal("parseRule", zap.Error(err)) + } + } else if key == "set" { + fields := strings.Split(value, ",") + for _, field := range fields { + col.set = append(col.set, strings.TrimSpace(field)) + } + } +} + +// parse the data rules. +// rules like `a int unique comment '[[range=1,10;step=1]]'`, +// then we will get value from 1,2...10 +func (col *column) parseColumnComment() { + comment := strings.TrimSpace(col.comment) + start := strings.Index(comment, "[[") + end := strings.Index(comment, "]]") + var content string + if start < end { + content = comment[start+2 : end] + } + + fields := strings.Split(content, ";") + for _, field := range fields { + field = strings.TrimSpace(field) + kvs := strings.Split(field, "=") + col.parseRule(kvs) + } +} + +func (col *column) parseColumn(cd *ast.ColumnDef) { + col.name = cd.Name.Name.L + col.tp = cd.Tp + col.parseColumnOptions(cd.Options) + col.parseColumnComment() + col.table.columns = append(col.table.columns, col) +} + +func (col *column) parseColumnOptions(ops []*ast.ColumnOption) { + for _, op := range ops { + switch op.Tp { + case ast.ColumnOptionPrimaryKey, ast.ColumnOptionAutoIncrement, ast.ColumnOptionUniqKey: + col.table.uniqIndices[col.name] = col + case ast.ColumnOptionComment: + col.comment = op.Expr.(ast.ValueExpr).GetDatumString() + } + } +} + +type table struct { + indices map[string]*column + uniqIndices map[string]*column + unsignedCols map[string]*column + name string + columnList string + columns []*column +} + +func (t *table) printColumns() string { + ret := "" + for _, col := range t.columns { + ret += fmt.Sprintf("%v", col) + } + + return ret +} + +func (t *table) String() string { + if t == nil { + return "" + } + + ret := fmt.Sprintf("[table]name: %s\n", t.name) + ret += "[table]columns:\n" + ret += t.printColumns() + + ret += fmt.Sprintf("[table]column list: %s\n", t.columnList) + + ret += "[table]indices:\n" + for k, v := range t.indices { + ret += fmt.Sprintf("key->%s, value->%v", k, v) + } + + ret += "[table]unique indices:\n" + for k, v := range t.uniqIndices { + ret += fmt.Sprintf("key->%s, value->%v", k, v) + } + + return ret +} + +func newTable() *table { + return &table{ + indices: make(map[string]*column), + uniqIndices: make(map[string]*column), + unsignedCols: make(map[string]*column), + } +} + +func (*table) findCol(cols []*column, name string) *column { + for _, col := range cols { + if col.name == name { + return col + } + } + return nil +} + +func (t *table) parseTableConstraint(cons *ast.Constraint) { + switch cons.Tp { + case ast.ConstraintPrimaryKey, ast.ConstraintKey, ast.ConstraintUniq, + ast.ConstraintUniqKey, ast.ConstraintUniqIndex: + for _, indexCol := range cons.Keys { + name := indexCol.Column.Name.L + t.uniqIndices[name] = t.findCol(t.columns, name) + } + case ast.ConstraintIndex: + for _, indexCol := range cons.Keys { + name := indexCol.Column.Name.L + t.indices[name] = t.findCol(t.columns, name) + } + } +} + +func (t *table) buildColumnList() { + columns := make([]string, 0, len(t.columns)) + for _, column := range t.columns { + columns = append(columns, dbutil.ColumnName(column.name)) + } + + t.columnList = strings.Join(columns, ",") +} + +func parseTable(t *table, stmt *ast.CreateTableStmt) error { + t.name = stmt.Table.Name.L + t.columns = make([]*column, 0, len(stmt.Cols)) + + for i, col := range stmt.Cols { + column := &column{idx: i + 1, table: t, step: defaultStep, data: newDatum()} + column.parseColumn(col) + } + + for _, cons := range stmt.Constraints { + t.parseTableConstraint(cons) + } + + t.buildColumnList() + + return nil +} + +func parseTableSQL(table *table, sql string) error { + stmt, err := parser.New().ParseOneStmt(sql, "", "") + if err != nil { + return errors.Trace(err) + } + + switch node := stmt.(type) { + case *ast.CreateTableStmt: + err = parseTable(table, node) + default: + err = errors.Errorf("invalid statement - %v", stmt.Text()) + } + + return errors.Trace(err) +} + +func parseIndex(table *table, stmt *ast.CreateIndexStmt) error { + if table.name != stmt.Table.Name.L { + return errors.Errorf("mismatch table name for create index - %s : %s", table.name, stmt.Table.Name.L) + } + + for _, indexCol := range stmt.IndexPartSpecifications { + name := indexCol.Column.Name.L + if stmt.KeyType == ast.IndexKeyTypeUnique { + table.uniqIndices[name] = table.findCol(table.columns, name) + } else { + table.indices[name] = table.findCol(table.columns, name) + } + } + + return nil +} + +func parseIndexSQL(table *table, sql string) error { + if len(sql) == 0 { + return nil + } + + stmt, err := parser.New().ParseOneStmt(sql, "", "") + if err != nil { + return errors.Trace(err) + } + + switch node := stmt.(type) { + case *ast.CreateIndexStmt: + err = parseIndex(table, node) + default: + err = errors.Errorf("invalid statement - %v", stmt.Text()) + } + + return errors.Trace(err) +} diff --git a/util/importer/rand.go b/pkg/util/importer/rand.go similarity index 100% rename from util/importer/rand.go rename to pkg/util/importer/rand.go diff --git a/pkg/util/intest/BUILD.bazel b/pkg/util/intest/BUILD.bazel new file mode 100644 index 0000000000000..bab02008d0df9 --- /dev/null +++ b/pkg/util/intest/BUILD.bazel @@ -0,0 +1,23 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "intest", + srcs = [ + "assert.go", #keep + "common.go", + "intest.go", #keep + ], + importpath = "github.com/pingcap/tidb/pkg/util/intest", + visibility = ["//visibility:public"], +) + +go_test( + name = "intest_test", + timeout = "short", + srcs = ["assert_test.go"], + flaky = True, + deps = [ + ":intest", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/intest/assert.go b/pkg/util/intest/assert.go similarity index 100% rename from util/intest/assert.go rename to pkg/util/intest/assert.go diff --git a/util/intest/assert_test.go b/pkg/util/intest/assert_test.go similarity index 98% rename from util/intest/assert_test.go rename to pkg/util/intest/assert_test.go index 8097b9e685e97..c85fc8d5b12fd 100644 --- a/util/intest/assert_test.go +++ b/pkg/util/intest/assert_test.go @@ -17,7 +17,7 @@ package intest_test import ( "testing" - "github.com/pingcap/tidb/util/intest" + "github.com/pingcap/tidb/pkg/util/intest" "github.com/stretchr/testify/require" ) diff --git a/util/intest/common.go b/pkg/util/intest/common.go similarity index 100% rename from util/intest/common.go rename to pkg/util/intest/common.go diff --git a/util/intest/intest.go b/pkg/util/intest/intest.go similarity index 100% rename from util/intest/intest.go rename to pkg/util/intest/intest.go diff --git a/pkg/util/israce/BUILD.bazel b/pkg/util/israce/BUILD.bazel new file mode 100644 index 0000000000000..4f23ac476a54c --- /dev/null +++ b/pkg/util/israce/BUILD.bazel @@ -0,0 +1,11 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "israce", + srcs = [ + "israce.go", + "norace.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/israce", + visibility = ["//visibility:public"], +) diff --git a/util/israce/israce.go b/pkg/util/israce/israce.go similarity index 100% rename from util/israce/israce.go rename to pkg/util/israce/israce.go diff --git a/util/israce/norace.go b/pkg/util/israce/norace.go similarity index 100% rename from util/israce/norace.go rename to pkg/util/israce/norace.go diff --git a/pkg/util/keydecoder/BUILD.bazel b/pkg/util/keydecoder/BUILD.bazel new file mode 100644 index 0000000000000..bf8759d232273 --- /dev/null +++ b/pkg/util/keydecoder/BUILD.bazel @@ -0,0 +1,43 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "keydecoder", + srcs = ["keydecoder.go"], + importpath = "github.com/pingcap/tidb/pkg/util/keydecoder", + visibility = ["//visibility:public"], + deps = [ + "//pkg/infoschema", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/tablecodec", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "keydecoder_test", + timeout = "short", + srcs = [ + "keydecoder_test.go", + "main_test.go", + ], + embed = [":keydecoder"], + flaky = True, + deps = [ + "//pkg/infoschema", + "//pkg/parser/model", + "//pkg/planner/core", + "//pkg/sessionctx/stmtctx", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util/codec", + "@com_github_stretchr_testify//assert", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/keydecoder/keydecoder.go b/pkg/util/keydecoder/keydecoder.go similarity index 96% rename from util/keydecoder/keydecoder.go rename to pkg/util/keydecoder/keydecoder.go index 16baeefe4d1ac..fa2200405c3c8 100644 --- a/util/keydecoder/keydecoder.go +++ b/pkg/util/keydecoder/keydecoder.go @@ -18,11 +18,11 @@ import ( "fmt" "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/keydecoder/keydecoder_test.go b/pkg/util/keydecoder/keydecoder_test.go similarity index 95% rename from util/keydecoder/keydecoder_test.go rename to pkg/util/keydecoder/keydecoder_test.go index 3a545a7a9b23c..26b3c8519d089 100644 --- a/util/keydecoder/keydecoder_test.go +++ b/pkg/util/keydecoder/keydecoder_test.go @@ -17,15 +17,15 @@ package keydecoder import ( "testing" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - _ "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/parser/model" + _ "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" "github.com/stretchr/testify/assert" "go.opencensus.io/stats/view" ) diff --git a/pkg/util/keydecoder/main_test.go b/pkg/util/keydecoder/main_test.go new file mode 100644 index 0000000000000..4df44739f2dac --- /dev/null +++ b/pkg/util/keydecoder/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package keydecoder + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/kvcache/BUILD.bazel b/pkg/util/kvcache/BUILD.bazel new file mode 100644 index 0000000000000..9b878f5bbc8c0 --- /dev/null +++ b/pkg/util/kvcache/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "kvcache", + srcs = ["simple_lru.go"], + importpath = "github.com/pingcap/tidb/pkg/util/kvcache", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/memory", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "kvcache_test", + timeout = "short", + srcs = [ + "main_test.go", + "simple_lru_test.go", + ], + embed = [":kvcache"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/memory", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/kvcache/main_test.go b/pkg/util/kvcache/main_test.go new file mode 100644 index 0000000000000..b4c6c23422645 --- /dev/null +++ b/pkg/util/kvcache/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package kvcache + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/kvcache/simple_lru.go b/pkg/util/kvcache/simple_lru.go similarity index 97% rename from util/kvcache/simple_lru.go rename to pkg/util/kvcache/simple_lru.go index f3700c80ba6ca..7dc9c4f91fb12 100644 --- a/util/kvcache/simple_lru.go +++ b/pkg/util/kvcache/simple_lru.go @@ -18,7 +18,7 @@ import ( "container/list" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/memory" ) // Key is the interface that every key in LRU Cache should implement. @@ -43,7 +43,7 @@ var ( const ( // ProfileName is the function name in heap profile - ProfileName = "github.com/pingcap/tidb/util/kvcache.(*SimpleLRUCache).Put" + ProfileName = "github.com/pingcap/tidb/pkg/util/kvcache.(*SimpleLRUCache).Put" ) func init() { diff --git a/util/kvcache/simple_lru_test.go b/pkg/util/kvcache/simple_lru_test.go similarity index 99% rename from util/kvcache/simple_lru_test.go rename to pkg/util/kvcache/simple_lru_test.go index 009971a640e93..6dd9476482afb 100644 --- a/util/kvcache/simple_lru_test.go +++ b/pkg/util/kvcache/simple_lru_test.go @@ -19,7 +19,7 @@ import ( "reflect" "testing" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/localpool/BUILD.bazel b/pkg/util/localpool/BUILD.bazel new file mode 100644 index 0000000000000..9affa37f9b86b --- /dev/null +++ b/pkg/util/localpool/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "localpool", + srcs = [ + "localpool.go", + "localpool_norace.go", + "localpool_race.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/localpool", + visibility = ["//visibility:public"], +) + +go_test( + name = "localpool_test", + timeout = "short", + srcs = [ + "localpool_test.go", + "main_test.go", + ], + embed = [":localpool"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/localpool/localpool.go b/pkg/util/localpool/localpool.go similarity index 100% rename from util/localpool/localpool.go rename to pkg/util/localpool/localpool.go diff --git a/util/localpool/localpool_norace.go b/pkg/util/localpool/localpool_norace.go similarity index 100% rename from util/localpool/localpool_norace.go rename to pkg/util/localpool/localpool_norace.go diff --git a/util/localpool/localpool_race.go b/pkg/util/localpool/localpool_race.go similarity index 100% rename from util/localpool/localpool_race.go rename to pkg/util/localpool/localpool_race.go diff --git a/util/localpool/localpool_test.go b/pkg/util/localpool/localpool_test.go similarity index 97% rename from util/localpool/localpool_test.go rename to pkg/util/localpool/localpool_test.go index cd338e6f7964e..6fd87a4ec3ddf 100644 --- a/util/localpool/localpool_test.go +++ b/pkg/util/localpool/localpool_test.go @@ -21,7 +21,7 @@ import ( "runtime" "testing" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/localpool/main_test.go b/pkg/util/localpool/main_test.go new file mode 100644 index 0000000000000..58e280974d634 --- /dev/null +++ b/pkg/util/localpool/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package localpool + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/logutil/BUILD.bazel b/pkg/util/logutil/BUILD.bazel new file mode 100644 index 0000000000000..b6893b1563238 --- /dev/null +++ b/pkg/util/logutil/BUILD.bazel @@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "logutil", + srcs = [ + "hex.go", + "log.go", + "slow_query_logger.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/logutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/model", + "@com_github_golang_protobuf//proto", + "@com_github_grpc_ecosystem_go_grpc_middleware//logging/zap", + "@com_github_opentracing_opentracing_go//:opentracing-go", + "@com_github_opentracing_opentracing_go//log", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//buffer", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "logutil_test", + timeout = "short", + srcs = [ + "hex_test.go", + "log_test.go", + "main_test.go", + ], + embed = [":logutil"], + flaky = True, + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/testkit/testsetup", + "@com_github_google_uuid//:uuid", + "@com_github_pingcap_kvproto//pkg/metapb", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) diff --git a/pkg/util/logutil/consistency/BUILD.bazel b/pkg/util/logutil/consistency/BUILD.bazel new file mode 100644 index 0000000000000..94b90c118d775 --- /dev/null +++ b/pkg/util/logutil/consistency/BUILD.bazel @@ -0,0 +1,22 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "consistency", + srcs = ["reporter.go"], + importpath = "github.com/pingcap/tidb/pkg/util/logutil/consistency", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/store/helper", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/util/logutil/consistency/reporter.go b/pkg/util/logutil/consistency/reporter.go new file mode 100644 index 0000000000000..1ec71bec20fe7 --- /dev/null +++ b/pkg/util/logutil/consistency/reporter.go @@ -0,0 +1,294 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package consistency + +import ( + "context" + "encoding/hex" + "encoding/json" + "fmt" + "strconv" + "strings" + "time" + + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/store/helper" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/tikv/client-go/v2/tikv" + "go.uber.org/zap" +) + +var ( + // ErrAdminCheckInconsistent returns for data inconsistency for admin check. + ErrAdminCheckInconsistent = dbterror.ClassAdmin.NewStd(errno.ErrDataInconsistent) + // ErrLookupInconsistent returns for data inconsistency for index lookup. + ErrLookupInconsistent = dbterror.ClassExecutor.NewStd(errno.ErrDataInconsistentMismatchCount) + // ErrAdminCheckInconsistentWithColInfo returns for data inconsistency for admin check but with column info. + ErrAdminCheckInconsistentWithColInfo = dbterror.ClassExecutor.NewStd(errno.ErrDataInconsistentMismatchIndex) +) + +// GetMvccByKey gets the MVCC value by key, and returns a json string including decoded data +func GetMvccByKey(tikvStore helper.Storage, key kv.Key, decodeMvccFn func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{})) string { + if key == nil { + return "" + } + h := helper.NewHelper(tikvStore) + data, err := h.GetMvccByEncodedKey(key) + if err != nil { + return "" + } + regionID := getRegionIDByKey(tikvStore, key) + + decodeKey := strings.ToUpper(hex.EncodeToString(key)) + + resp := map[string]interface{}{ + "key": decodeKey, + "regionID": regionID, + "mvcc": data, + } + + if decodeMvccFn != nil { + decodeMvccFn(key, data, resp) + } + + rj, err := json.Marshal(resp) + if err != nil { + return "" + } + const maxMvccInfoLen = 5000 + s := string(rj) + if len(s) > maxMvccInfoLen { + s = s[:maxMvccInfoLen] + "[truncated]..." + } + + return s +} + +func getRegionIDByKey(tikvStore helper.Storage, encodedKey []byte) uint64 { + keyLocation, err := tikvStore.GetRegionCache().LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) + if err != nil { + return 0 + } + return keyLocation.Region.GetID() +} + +// Reporter is a helper to generate report. +type Reporter struct { + HandleEncode func(handle kv.Handle) kv.Key + IndexEncode func(idxRow *RecordData) kv.Key + Tbl *model.TableInfo + Idx *model.IndexInfo + Sctx sessionctx.Context +} + +// DecodeRowMvccData creates a closure that captures the tableInfo to be used a decode function in GetMvccByKey. +func DecodeRowMvccData(tableInfo *model.TableInfo) func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) { + return func(key kv.Key, respValue *kvrpcpb.MvccGetByKeyResponse, outMap map[string]interface{}) { + colMap := make(map[int64]*types.FieldType, 3) + for _, col := range tableInfo.Columns { + var fieldType = col.FieldType + colMap[col.ID] = &fieldType + } + + if respValue.Info != nil { + var err error + datas := make(map[string]map[string]string) + for _, w := range respValue.Info.Writes { + if len(w.ShortValue) > 0 { + datas[strconv.FormatUint(w.StartTs, 10)], err = decodeMvccRecordValue(w.ShortValue, colMap, tableInfo) + } + } + + for _, v := range respValue.Info.Values { + if len(v.Value) > 0 { + datas[strconv.FormatUint(v.StartTs, 10)], err = decodeMvccRecordValue(v.Value, colMap, tableInfo) + } + } + if len(datas) > 0 { + outMap["decoded"] = datas + if err != nil { + outMap["decode_error"] = err.Error() + } + } + } + } +} + +// DecodeIndexMvccData creates a closure that captures the indexInfo to be used a decode function in GetMvccByKey. +func DecodeIndexMvccData(indexInfo *model.IndexInfo) func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) { + return func(key kv.Key, respValue *kvrpcpb.MvccGetByKeyResponse, outMap map[string]interface{}) { + if respValue.Info != nil { + var ( + hd kv.Handle + err error + datas = make(map[string]map[string]string) + ) + for _, w := range respValue.Info.Writes { + if len(w.ShortValue) > 0 { + hd, err = tablecodec.DecodeIndexHandle(key, w.ShortValue, len(indexInfo.Columns)) + if err == nil { + datas[strconv.FormatUint(w.StartTs, 10)] = map[string]string{"handle": hd.String()} + } + } + } + for _, v := range respValue.Info.Values { + if len(v.Value) > 0 { + hd, err = tablecodec.DecodeIndexHandle(key, v.Value, len(indexInfo.Columns)) + if err == nil { + datas[strconv.FormatUint(v.StartTs, 10)] = map[string]string{"handle": hd.String()} + } + } + } + if len(datas) > 0 { + outMap["decoded"] = datas + if err != nil { + outMap["decode_error"] = err.Error() + } + } + } + } +} + +func decodeMvccRecordValue(bs []byte, colMap map[int64]*types.FieldType, tb *model.TableInfo) (map[string]string, error) { + rs, err := tablecodec.DecodeRowToDatumMap(bs, colMap, time.UTC) + record := make(map[string]string, len(tb.Columns)) + for _, col := range tb.Columns { + if c, ok := rs[col.ID]; ok { + data := "nil" + if !c.IsNull() { + data, err = c.ToString() + } + record[col.Name.O] = data + } + } + return record, err +} + +// ReportLookupInconsistent reports inconsistent when index rows is more than record rows. +func (r *Reporter) ReportLookupInconsistent(ctx context.Context, idxCnt, tblCnt int, missHd, fullHd []kv.Handle, missRowIdx []RecordData) error { + if r.Sctx.GetSessionVars().EnableRedactLog { + logutil.Logger(ctx).Error("indexLookup found data inconsistency", + zap.String("table_name", r.Tbl.Name.O), + zap.String("index_name", r.Idx.Name.O), + zap.Int("index_cnt", idxCnt), + zap.Int("table_cnt", tblCnt), + zap.Stack("stack")) + } else { + const maxFullHandleCnt = 50 + displayFullHdCnt := len(fullHd) + if displayFullHdCnt > maxFullHandleCnt { + displayFullHdCnt = maxFullHandleCnt + } + fs := []zap.Field{ + zap.String("table_name", r.Tbl.Name.O), + zap.String("index_name", r.Idx.Name.O), + zap.Int("index_cnt", idxCnt), zap.Int("table_cnt", tblCnt), + zap.String("missing_handles", fmt.Sprint(missHd)), + zap.String("total_handles", fmt.Sprint(fullHd[:displayFullHdCnt])), + } + store, ok := r.Sctx.GetStore().(helper.Storage) + if ok { + for i, hd := range missHd { + fs = append(fs, zap.String("row_mvcc_"+strconv.Itoa(i), GetMvccByKey(store, r.HandleEncode(hd), DecodeRowMvccData(r.Tbl)))) + } + for i := range missRowIdx { + fs = append(fs, zap.String("index_mvcc_"+strconv.Itoa(i), GetMvccByKey(store, r.IndexEncode(&missRowIdx[i]), DecodeIndexMvccData(r.Idx)))) + } + } + + logutil.Logger(ctx).Error("indexLookup found data inconsistency", fs...) + } + return ErrLookupInconsistent.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, idxCnt, tblCnt) +} + +// ReportAdminCheckInconsistentWithColInfo reports inconsistent when the value of index row is different from record row. +func (r *Reporter) ReportAdminCheckInconsistentWithColInfo(ctx context.Context, handle kv.Handle, colName string, idxDat, tblDat fmt.Stringer, err error, idxRow *RecordData) error { + if r.Sctx.GetSessionVars().EnableRedactLog { + logutil.Logger(ctx).Error("admin check found data inconsistency", + zap.String("table_name", r.Tbl.Name.O), + zap.String("index", r.Idx.Name.O), + zap.String("col", colName), + zap.Error(err), + zap.Stack("stack"), + ) + } else { + fs := []zap.Field{ + zap.String("table_name", r.Tbl.Name.O), + zap.String("index_name", r.Idx.Name.O), + zap.String("col", colName), + zap.Stringer("row_id", handle), + zap.Stringer("idxDatum", idxDat), + zap.Stringer("rowDatum", tblDat), + } + store, ok := r.Sctx.GetStore().(helper.Storage) + if ok { + fs = append(fs, zap.String("row_mvcc", GetMvccByKey(store, r.HandleEncode(handle), DecodeRowMvccData(r.Tbl)))) + fs = append(fs, zap.String("index_mvcc", GetMvccByKey(store, r.IndexEncode(idxRow), DecodeIndexMvccData(r.Idx)))) + } + fs = append(fs, zap.Error(err)) + fs = append(fs, zap.Stack("stack")) + logutil.Logger(ctx).Error("admin check found data inconsistency", fs...) + } + return ErrAdminCheckInconsistentWithColInfo.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, colName, fmt.Sprint(handle), fmt.Sprint(idxDat), fmt.Sprint(tblDat), err) +} + +// RecordData is the record data composed of a handle and values. +type RecordData struct { + Handle kv.Handle + Values []types.Datum +} + +func (r *RecordData) String() string { + if r == nil { + return "" + } + return fmt.Sprintf("handle: %s, values: %s", fmt.Sprint(r.Handle), fmt.Sprint(r.Values)) +} + +// ReportAdminCheckInconsistent reports inconsistent when single index row not found in record rows. +func (r *Reporter) ReportAdminCheckInconsistent(ctx context.Context, handle kv.Handle, idxRow, tblRow *RecordData) error { + if r.Sctx.GetSessionVars().EnableRedactLog { + logutil.Logger(ctx).Error("admin check found data inconsistency", + zap.String("table_name", r.Tbl.Name.O), + zap.String("index", r.Idx.Name.O), + zap.Stack("stack"), + ) + } else { + fs := []zap.Field{ + zap.String("table_name", r.Tbl.Name.O), + zap.String("index_name", r.Idx.Name.O), + zap.Stringer("row_id", handle), + zap.Stringer("index", idxRow), + zap.Stringer("row", tblRow), + } + store, ok := r.Sctx.GetStore().(helper.Storage) + if ok { + fs = append(fs, zap.String("row_mvcc", GetMvccByKey(store, r.HandleEncode(handle), DecodeRowMvccData(r.Tbl)))) + if idxRow != nil { + fs = append(fs, zap.String("index_mvcc", GetMvccByKey(store, r.IndexEncode(idxRow), DecodeIndexMvccData(r.Idx)))) + } + } + fs = append(fs, zap.Stack("stack")) + logutil.Logger(ctx).Error("admin check found data inconsistency", fs...) + } + return ErrAdminCheckInconsistent.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, fmt.Sprint(handle), fmt.Sprint(idxRow), fmt.Sprint(tblRow)) +} diff --git a/util/logutil/hex.go b/pkg/util/logutil/hex.go similarity index 100% rename from util/logutil/hex.go rename to pkg/util/logutil/hex.go diff --git a/util/logutil/hex_test.go b/pkg/util/logutil/hex_test.go similarity index 96% rename from util/logutil/hex_test.go rename to pkg/util/logutil/hex_test.go index 35d98310c1bcb..b4c82d51340a3 100644 --- a/util/logutil/hex_test.go +++ b/pkg/util/logutil/hex_test.go @@ -21,8 +21,8 @@ import ( "testing" "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" ) diff --git a/util/logutil/log.go b/pkg/util/logutil/log.go similarity index 99% rename from util/logutil/log.go rename to pkg/util/logutil/log.go index defec61644c87..8b5570b4471f7 100644 --- a/util/logutil/log.go +++ b/pkg/util/logutil/log.go @@ -27,7 +27,7 @@ import ( tlog "github.com/opentracing/opentracing-go/log" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/tikv/client-go/v2/tikv" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/util/logutil/log_test.go b/pkg/util/logutil/log_test.go similarity index 99% rename from util/logutil/log_test.go rename to pkg/util/logutil/log_test.go index d977e8d5e184d..d0c08d5554e0a 100644 --- a/util/logutil/log_test.go +++ b/pkg/util/logutil/log_test.go @@ -25,7 +25,7 @@ import ( "github.com/google/uuid" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/parser/model" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" diff --git a/pkg/util/logutil/main_test.go b/pkg/util/logutil/main_test.go new file mode 100644 index 0000000000000..5ab6d0991e04e --- /dev/null +++ b/pkg/util/logutil/main_test.go @@ -0,0 +1,51 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package logutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +const ( + // zapLogWithoutCheckKeyPattern is used to match the zap log format but do not check some specified key, such as the following log: + // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"]["str key"=val] ["int key"=123] + zapLogWithoutCheckKeyPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] (\[.*=.*\]).*\n` + // zapLogPatern is used to match the zap log format, such as the following log: + // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [conn=conn1] ["str key"=val] ["int key"=123] + zapLogWithConnIDPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[conn=.*\] (\[.*=.*\]).*\n` + // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [conn=conn1] [session_alias=alias] ["str key"=val] ["int key"=123] + zapLogWithTraceInfoPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[conn=.*\] \[session_alias=.*\] (\[.*=.*\]).*\n` + // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [ctxKey=ctxKey1] ["str key"=val] ["int key"=123] + zapLogWithKeyValPatternByCtx = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[ctxKey=.*\] (\[.*=.*\]).*\n` + // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [coreKey=coreKey1] ["str key"=val] ["int key"=123] + zapLogWithKeyValPatternByCore = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[coreKey=.*\] (\[.*=.*\]).*\n` +) + +var ( + PrettyPrint = prettyPrint +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/logutil/slow_query_logger.go b/pkg/util/logutil/slow_query_logger.go similarity index 100% rename from util/logutil/slow_query_logger.go rename to pkg/util/logutil/slow_query_logger.go diff --git a/pkg/util/main_test.go b/pkg/util/main_test.go new file mode 100644 index 0000000000000..0de0070ab2afa --- /dev/null +++ b/pkg/util/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package util_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/mathutil/BUILD.bazel b/pkg/util/mathutil/BUILD.bazel new file mode 100644 index 0000000000000..b5776dfe4d411 --- /dev/null +++ b/pkg/util/mathutil/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mathutil", + srcs = [ + "exponential_average.go", + "math.go", + "rand.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/mathutil", + visibility = ["//visibility:public"], + deps = ["@org_golang_x_exp//constraints"], +) + +go_test( + name = "mathutil_test", + timeout = "short", + srcs = [ + "exponential_average_test.go", + "main_test.go", + "math_test.go", + "rand_test.go", + ], + embed = [":mathutil"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/mathutil/exponential_average.go b/pkg/util/mathutil/exponential_average.go similarity index 100% rename from util/mathutil/exponential_average.go rename to pkg/util/mathutil/exponential_average.go diff --git a/util/mathutil/exponential_average_test.go b/pkg/util/mathutil/exponential_average_test.go similarity index 100% rename from util/mathutil/exponential_average_test.go rename to pkg/util/mathutil/exponential_average_test.go diff --git a/pkg/util/mathutil/main_test.go b/pkg/util/mathutil/main_test.go new file mode 100644 index 0000000000000..a87c5cc6aecb0 --- /dev/null +++ b/pkg/util/mathutil/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mathutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/mathutil/math.go b/pkg/util/mathutil/math.go similarity index 100% rename from util/mathutil/math.go rename to pkg/util/mathutil/math.go diff --git a/util/mathutil/math_test.go b/pkg/util/mathutil/math_test.go similarity index 100% rename from util/mathutil/math_test.go rename to pkg/util/mathutil/math_test.go diff --git a/util/mathutil/rand.go b/pkg/util/mathutil/rand.go similarity index 100% rename from util/mathutil/rand.go rename to pkg/util/mathutil/rand.go diff --git a/util/mathutil/rand_test.go b/pkg/util/mathutil/rand_test.go similarity index 100% rename from util/mathutil/rand_test.go rename to pkg/util/mathutil/rand_test.go diff --git a/pkg/util/memory/BUILD.bazel b/pkg/util/memory/BUILD.bazel new file mode 100644 index 0000000000000..0a1c6321a7a75 --- /dev/null +++ b/pkg/util/memory/BUILD.bazel @@ -0,0 +1,47 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "memory", + srcs = [ + "action.go", + "meminfo.go", + "memstats.go", + "tracker.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/memory", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/metrics", + "//pkg/parser/terror", + "//pkg/util/cgroup", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "//pkg/util/mathutil", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_sysutil//:sysutil", + "@com_github_shirou_gopsutil_v3//mem", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "memory_test", + timeout = "short", + srcs = [ + "bench_test.go", + "main_test.go", + "tracker_test.go", + ], + embed = [":memory"], + flaky = True, + deps = [ + "//pkg/errno", + "//pkg/parser/terror", + "//pkg/testkit/testsetup", + "//pkg/util/mathutil", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/memory/action.go b/pkg/util/memory/action.go similarity index 98% rename from util/memory/action.go rename to pkg/util/memory/action.go index 21a6fdc9f0ebf..39a37be2a56ef 100644 --- a/util/memory/action.go +++ b/pkg/util/memory/action.go @@ -19,9 +19,9 @@ import ( "sync" "sync/atomic" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/memory/bench_test.go b/pkg/util/memory/bench_test.go similarity index 100% rename from util/memory/bench_test.go rename to pkg/util/memory/bench_test.go diff --git a/pkg/util/memory/main_test.go b/pkg/util/memory/main_test.go new file mode 100644 index 0000000000000..5c77593cb6d00 --- /dev/null +++ b/pkg/util/memory/main_test.go @@ -0,0 +1,28 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package memory + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + goleak.VerifyTestMain(m) +} diff --git a/util/memory/meminfo.go b/pkg/util/memory/meminfo.go similarity index 97% rename from util/memory/meminfo.go rename to pkg/util/memory/meminfo.go index e1dccb2530fbc..8d0e25bcb759e 100644 --- a/util/memory/meminfo.go +++ b/pkg/util/memory/meminfo.go @@ -20,9 +20,9 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/sysutil" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/cgroup" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/cgroup" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/shirou/gopsutil/v3/mem" ) diff --git a/util/memory/memstats.go b/pkg/util/memory/memstats.go similarity index 100% rename from util/memory/memstats.go rename to pkg/util/memory/memstats.go diff --git a/pkg/util/memory/tracker.go b/pkg/util/memory/tracker.go new file mode 100644 index 0000000000000..a85c7185a2b0f --- /dev/null +++ b/pkg/util/memory/tracker.go @@ -0,0 +1,868 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package memory + +import ( + "bytes" + "fmt" + "runtime" + "slices" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/util/logutil" + atomicutil "go.uber.org/atomic" + "go.uber.org/zap" +) + +// TrackMemWhenExceeds is the threshold when memory usage needs to be tracked. +const TrackMemWhenExceeds = 104857600 // 100MB + +// Process global variables for memory limit. +var ( + ServerMemoryLimitOriginText = atomicutil.NewString("0") + ServerMemoryLimit = atomicutil.NewUint64(0) + ServerMemoryLimitSessMinSize = atomicutil.NewUint64(128 << 20) + + QueryForceDisk = atomicutil.NewInt64(0) + TriggerMemoryLimitGC = atomicutil.NewBool(false) + MemoryLimitGCLast = atomicutil.NewTime(time.Time{}) + MemoryLimitGCTotal = atomicutil.NewInt64(0) +) + +// Tracker is used to track the memory usage during query execution. +// It contains an optional limit and can be arranged into a tree structure +// such that the consumption tracked by a Tracker is also tracked by +// its ancestors. The main idea comes from Apache Impala: +// +// https://github.com/cloudera/Impala/blob/cdh5-trunk/be/src/runtime/mem-tracker.h +// +// By default, memory consumption is tracked via calls to "Consume()", either to +// the tracker itself or to one of its descendents. A typical sequence of calls +// for a single Tracker is: +// 1. tracker.SetLabel() / tracker.SetActionOnExceed() / tracker.AttachTo() +// 2. tracker.Consume() / tracker.ReplaceChild() / tracker.BytesConsumed() +// +// NOTE: We only protect concurrent access to "bytesConsumed" and "children", +// that is to say: +// 1. Only "BytesConsumed()", "Consume()" and "AttachTo()" are thread-safe. +// 2. Other operations of a Tracker tree is not thread-safe. +// +// We have two limits for the memory quota: soft limit and hard limit. +// If the soft limit is exceeded, we will trigger the action that alleviates the +// speed of memory growth. The soft limit is hard-coded as `0.8*hard limit`. +// The actions that could be triggered are: AggSpillDiskAction. +// +// If the hard limit is exceeded, we will trigger the action that immediately +// reduces memory usage. The hard limit is set by the system variable `tidb_mem_query_quota`. +// The actions that could be triggered are: SpillDiskAction, SortAndSpillDiskAction, rateLimitAction, +// PanicOnExceed, globalPanicOnExceed, LogOnExceed. +type Tracker struct { + bytesLimit atomic.Value + actionMuForHardLimit actionMu + actionMuForSoftLimit actionMu + mu struct { + // The children memory trackers. If the Tracker is the Global Tracker, like executor.GlobalDiskUsageTracker, + // we wouldn't maintain its children in order to avoiding mutex contention. + children map[int][]*Tracker + sync.Mutex + } + parMu struct { + parent *Tracker // The parent memory tracker. + sync.Mutex + } + label int // Label of this "Tracker". + // following fields are used with atomic operations, so make them 64-byte aligned. + bytesConsumed int64 // Consumed bytes. + bytesReleased int64 // Released bytes. + maxConsumed atomicutil.Int64 // max number of bytes consumed during execution. + SessionID atomicutil.Uint64 // SessionID indicates the sessionID the tracker is bound. + NeedKill atomic.Bool // NeedKill indicates whether this session need kill because OOM + NeedKillReceived sync.Once + IsRootTrackerOfSess bool // IsRootTrackerOfSess indicates whether this tracker is bound for session + isGlobal bool // isGlobal indicates whether this tracker is global tracker +} + +type actionMu struct { + actionOnExceed ActionOnExceed + sync.Mutex +} + +// EnableGCAwareMemoryTrack is used to turn on/off the GC-aware memory track +var EnableGCAwareMemoryTrack = atomicutil.NewBool(false) + +// https://golang.google.cn/pkg/runtime/#SetFinalizer +// It is not guaranteed that a finalizer will run if the size of *obj is zero bytes. +type finalizerRef struct { + byte //nolint:unused +} + +// softScale means the scale of the soft limit to the hard limit. +const softScale = 0.8 + +// bytesLimits holds limit config atomically. +type bytesLimits struct { + bytesHardLimit int64 // bytesHardLimit <= 0 means no limit, used for actionMuForHardLimit. + bytesSoftLimit int64 // bytesSoftLimit <= 0 means no limit, used for actionMuForSoftLimit. +} + +// MemUsageTop1Tracker record the use memory top1 session's tracker for kill. +var MemUsageTop1Tracker atomic.Pointer[Tracker] + +// InitTracker initializes a memory tracker. +// 1. "label" is the label used in the usage string. +// 2. "bytesLimit <= 0" means no limit. +// +// For the common tracker, isGlobal is default as false +func InitTracker(t *Tracker, label int, bytesLimit int64, action ActionOnExceed) { + t.mu.children = nil + t.actionMuForHardLimit.actionOnExceed = action + t.actionMuForSoftLimit.actionOnExceed = nil + t.parMu.parent = nil + + t.label = label + t.bytesLimit.Store(&bytesLimits{ + bytesHardLimit: bytesLimit, + bytesSoftLimit: int64(float64(bytesLimit) * softScale), + }) + t.maxConsumed.Store(0) + t.isGlobal = false +} + +// NewTracker creates a memory tracker. +// 1. "label" is the label used in the usage string. +// 2. "bytesLimit <= 0" means no limit. +// +// For the common tracker, isGlobal is default as false +func NewTracker(label int, bytesLimit int64) *Tracker { + t := &Tracker{ + label: label, + } + t.bytesLimit.Store(&bytesLimits{ + bytesHardLimit: bytesLimit, + bytesSoftLimit: int64(float64(bytesLimit) * softScale), + }) + t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} + t.isGlobal = false + return t +} + +// NewGlobalTracker creates a global tracker, its isGlobal is default as true +func NewGlobalTracker(label int, bytesLimit int64) *Tracker { + t := &Tracker{ + label: label, + } + t.bytesLimit.Store(&bytesLimits{ + bytesHardLimit: bytesLimit, + bytesSoftLimit: int64(float64(bytesLimit) * softScale), + }) + t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} + t.isGlobal = true + return t +} + +// CheckBytesLimit check whether the bytes limit of the tracker is equal to a value. +// Only used in test. +func (t *Tracker) CheckBytesLimit(val int64) bool { + return t.bytesLimit.Load().(*bytesLimits).bytesHardLimit == val +} + +// SetBytesLimit sets the bytes limit for this tracker. +// "bytesHardLimit <= 0" means no limit. +func (t *Tracker) SetBytesLimit(bytesLimit int64) { + t.bytesLimit.Store(&bytesLimits{ + bytesHardLimit: bytesLimit, + bytesSoftLimit: int64(float64(bytesLimit) * softScale), + }) +} + +// GetBytesLimit gets the bytes limit for this tracker. +// "bytesHardLimit <= 0" means no limit. +func (t *Tracker) GetBytesLimit() int64 { + return t.bytesLimit.Load().(*bytesLimits).bytesHardLimit +} + +// CheckExceed checks whether the consumed bytes is exceed for this tracker. +func (t *Tracker) CheckExceed() bool { + bytesHardLimit := t.bytesLimit.Load().(*bytesLimits).bytesHardLimit + return atomic.LoadInt64(&t.bytesConsumed) >= bytesHardLimit && bytesHardLimit > 0 +} + +// SetActionOnExceed sets the action when memory usage exceeds bytesHardLimit. +func (t *Tracker) SetActionOnExceed(a ActionOnExceed) { + t.actionMuForHardLimit.Lock() + t.actionMuForHardLimit.actionOnExceed = a + t.actionMuForHardLimit.Unlock() +} + +// FallbackOldAndSetNewAction sets the action when memory usage exceeds bytesHardLimit +// and set the original action as its fallback. +func (t *Tracker) FallbackOldAndSetNewAction(a ActionOnExceed) { + t.actionMuForHardLimit.Lock() + defer t.actionMuForHardLimit.Unlock() + t.actionMuForHardLimit.actionOnExceed = reArrangeFallback(a, t.actionMuForHardLimit.actionOnExceed) +} + +// FallbackOldAndSetNewActionForSoftLimit sets the action when memory usage exceeds bytesSoftLimit +// and set the original action as its fallback. +func (t *Tracker) FallbackOldAndSetNewActionForSoftLimit(a ActionOnExceed) { + t.actionMuForSoftLimit.Lock() + defer t.actionMuForSoftLimit.Unlock() + t.actionMuForSoftLimit.actionOnExceed = reArrangeFallback(a, t.actionMuForSoftLimit.actionOnExceed) +} + +// GetFallbackForTest get the oom action used by test. +func (t *Tracker) GetFallbackForTest(ignoreFinishedAction bool) ActionOnExceed { + t.actionMuForHardLimit.Lock() + defer t.actionMuForHardLimit.Unlock() + if t.actionMuForHardLimit.actionOnExceed != nil && t.actionMuForHardLimit.actionOnExceed.IsFinished() && ignoreFinishedAction { + t.actionMuForHardLimit.actionOnExceed = t.actionMuForHardLimit.actionOnExceed.GetFallback() + } + return t.actionMuForHardLimit.actionOnExceed +} + +// UnbindActions unbinds actionForHardLimit and actionForSoftLimit. +func (t *Tracker) UnbindActions() { + t.actionMuForSoftLimit.Lock() + defer t.actionMuForSoftLimit.Unlock() + t.actionMuForSoftLimit.actionOnExceed = nil + + t.actionMuForHardLimit.Lock() + defer t.actionMuForHardLimit.Unlock() + t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} +} + +// reArrangeFallback merge two action chains and rearrange them by priority in descending order. +func reArrangeFallback(a ActionOnExceed, b ActionOnExceed) ActionOnExceed { + if a == nil { + return b + } + if b == nil { + return a + } + if a.GetPriority() < b.GetPriority() { + a, b = b, a + } + a.SetFallback(reArrangeFallback(a.GetFallback(), b)) + return a +} + +// SetLabel sets the label of a Tracker. +func (t *Tracker) SetLabel(label int) { + parent := t.getParent() + t.Detach() + t.label = label + if parent != nil { + t.AttachTo(parent) + } +} + +// Label gets the label of a Tracker. +func (t *Tracker) Label() int { + return t.label +} + +// AttachTo attaches this memory tracker as a child to another Tracker. If it +// already has a parent, this function will remove it from the old parent. +// Its consumed memory usage is used to update all its ancestors. +func (t *Tracker) AttachTo(parent *Tracker) { + if parent.isGlobal { + t.AttachToGlobalTracker(parent) + return + } + oldParent := t.getParent() + if oldParent != nil { + oldParent.remove(t) + } + parent.mu.Lock() + if parent.mu.children == nil { + parent.mu.children = make(map[int][]*Tracker) + } + parent.mu.children[t.label] = append(parent.mu.children[t.label], t) + parent.mu.Unlock() + + t.setParent(parent) + parent.Consume(t.BytesConsumed()) +} + +// Detach de-attach the tracker child from its parent, then set its parent property as nil +func (t *Tracker) Detach() { + if t == nil { + return + } + parent := t.getParent() + if parent == nil { + return + } + if parent.isGlobal { + t.DetachFromGlobalTracker() + return + } + if parent.IsRootTrackerOfSess && t.label != LabelForMemDB { + parent.actionMuForHardLimit.Lock() + parent.actionMuForHardLimit.actionOnExceed = nil + parent.actionMuForHardLimit.Unlock() + + parent.actionMuForSoftLimit.Lock() + parent.actionMuForSoftLimit.actionOnExceed = nil + parent.actionMuForSoftLimit.Unlock() + parent.NeedKill.Store(false) + parent.NeedKillReceived = sync.Once{} + } + parent.remove(t) + t.mu.Lock() + defer t.mu.Unlock() + t.setParent(nil) +} + +func (t *Tracker) remove(oldChild *Tracker) { + found := false + label := oldChild.label + t.mu.Lock() + if t.mu.children != nil { + children := t.mu.children[label] + for i, child := range children { + if child == oldChild { + children = append(children[:i], children[i+1:]...) + if len(children) > 0 { + t.mu.children[label] = children + } else { + delete(t.mu.children, label) + } + found = true + break + } + } + } + t.mu.Unlock() + if found { + oldChild.setParent(nil) + t.Consume(-oldChild.BytesConsumed()) + } +} + +// ReplaceChild removes the old child specified in "oldChild" and add a new +// child specified in "newChild". old child's memory consumption will be +// removed and new child's memory consumption will be added. +func (t *Tracker) ReplaceChild(oldChild, newChild *Tracker) { + if newChild == nil { + t.remove(oldChild) + return + } + + if oldChild.label != newChild.label { + t.remove(oldChild) + newChild.AttachTo(t) + return + } + + newConsumed := newChild.BytesConsumed() + newChild.setParent(t) + + label := oldChild.label + t.mu.Lock() + if t.mu.children != nil { + children := t.mu.children[label] + for i, child := range children { + if child != oldChild { + continue + } + + newConsumed -= oldChild.BytesConsumed() + oldChild.setParent(nil) + children[i] = newChild + t.mu.children[label] = children + break + } + } + t.mu.Unlock() + + t.Consume(newConsumed) +} + +// Consume is used to consume a memory usage. "bytes" can be a negative value, +// which means this is a memory release operation. When memory usage of a tracker +// exceeds its bytesSoftLimit/bytesHardLimit, the tracker calls its action, so does each of its ancestors. +func (t *Tracker) Consume(bs int64) { + if bs == 0 { + return + } + var rootExceed, rootExceedForSoftLimit, sessionRootTracker *Tracker + for tracker := t; tracker != nil; tracker = tracker.getParent() { + if tracker.IsRootTrackerOfSess { + sessionRootTracker = tracker + } + bytesConsumed := atomic.AddInt64(&tracker.bytesConsumed, bs) + bytesReleased := atomic.LoadInt64(&tracker.bytesReleased) + limits := tracker.bytesLimit.Load().(*bytesLimits) + if bytesConsumed+bytesReleased >= limits.bytesHardLimit && limits.bytesHardLimit > 0 { + rootExceed = tracker + } + if bytesConsumed+bytesReleased >= limits.bytesSoftLimit && limits.bytesSoftLimit > 0 { + rootExceedForSoftLimit = tracker + } + + for { + maxNow := tracker.maxConsumed.Load() + consumed := atomic.LoadInt64(&tracker.bytesConsumed) + if consumed > maxNow && !tracker.maxConsumed.CompareAndSwap(maxNow, consumed) { + continue + } + if label, ok := MetricsTypes[tracker.label]; ok { + metrics.MemoryUsage.WithLabelValues(label[0], label[1]).Set(float64(consumed)) + } + break + } + } + + tryAction := func(mu *actionMu, tracker *Tracker) { + mu.Lock() + defer mu.Unlock() + for mu.actionOnExceed != nil && mu.actionOnExceed.IsFinished() { + mu.actionOnExceed = mu.actionOnExceed.GetFallback() + } + if mu.actionOnExceed != nil { + mu.actionOnExceed.Action(tracker) + } + } + + tryActionLastOne := func(mu *actionMu, tracker *Tracker) { + mu.Lock() + defer mu.Unlock() + if currentAction := mu.actionOnExceed; currentAction != nil { + for nextAction := currentAction.GetFallback(); nextAction != nil; { + currentAction = nextAction + nextAction = currentAction.GetFallback() + } + if action, ok := currentAction.(ActionCareInvoker); ok { + action.SetInvoker(Instance) + } + currentAction.Action(tracker) + } + } + + if bs > 0 && sessionRootTracker != nil { + // Kill the Top1 session + if sessionRootTracker.NeedKill.Load() { + sessionRootTracker.NeedKillReceived.Do( + func() { + logutil.BgLogger().Warn("global memory controller, NeedKill signal is received successfully", + zap.Uint64("conn", sessionRootTracker.SessionID.Load())) + }) + tryActionLastOne(&sessionRootTracker.actionMuForHardLimit, sessionRootTracker) + } + // Update the Top1 session + memUsage := sessionRootTracker.BytesConsumed() + limitSessMinSize := ServerMemoryLimitSessMinSize.Load() + if uint64(memUsage) >= limitSessMinSize { + oldTracker := MemUsageTop1Tracker.Load() + for oldTracker.LessThan(sessionRootTracker) { + if MemUsageTop1Tracker.CompareAndSwap(oldTracker, sessionRootTracker) { + break + } + oldTracker = MemUsageTop1Tracker.Load() + } + } + } + + if bs > 0 && rootExceed != nil { + tryAction(&rootExceed.actionMuForHardLimit, rootExceed) + } + + if bs > 0 && rootExceedForSoftLimit != nil { + tryAction(&rootExceedForSoftLimit.actionMuForSoftLimit, rootExceedForSoftLimit) + } +} + +// BufferedConsume is used to buffer memory usage and do late consume +// not thread-safe, should be called in one goroutine +func (t *Tracker) BufferedConsume(bufferedMemSize *int64, bytes int64) { + *bufferedMemSize += bytes + if *bufferedMemSize >= int64(TrackMemWhenExceeds) { + t.Consume(*bufferedMemSize) + *bufferedMemSize = int64(0) + } +} + +// Release is used to release memory tracked, track the released memory until GC triggered if needed +// If you want your track to be GC-aware, please use Release(bytes) instead of Consume(-bytes), and pass the memory size of the real object. +// Only Analyze is integrated with Release so far. +func (t *Tracker) Release(bytes int64) { + if bytes == 0 { + return + } + defer t.Consume(-bytes) + for tracker := t; tracker != nil; tracker = tracker.getParent() { + if tracker.shouldRecordRelease() { + // use fake ref instead of obj ref, otherwise obj will be reachable again and gc in next cycle + newRef := &finalizerRef{} + finalizer := func(tracker *Tracker) func(ref *finalizerRef) { + return func(ref *finalizerRef) { + tracker.release(bytes) // finalizer func is called async + } + } + runtime.SetFinalizer(newRef, finalizer(tracker)) + tracker.recordRelease(bytes) + return + } + } +} + +// BufferedRelease is used to buffer memory release and do late release +// not thread-safe, should be called in one goroutine +func (t *Tracker) BufferedRelease(bufferedMemSize *int64, bytes int64) { + *bufferedMemSize += bytes + if *bufferedMemSize >= int64(TrackMemWhenExceeds) { + t.Release(*bufferedMemSize) + *bufferedMemSize = int64(0) + } +} + +func (t *Tracker) shouldRecordRelease() bool { + return EnableGCAwareMemoryTrack.Load() && t.label == LabelForGlobalAnalyzeMemory +} + +func (t *Tracker) recordRelease(bytes int64) { + for tracker := t; tracker != nil; tracker = tracker.getParent() { + bytesReleased := atomic.AddInt64(&tracker.bytesReleased, bytes) + if label, ok := MetricsTypes[tracker.label]; ok { + metrics.MemoryUsage.WithLabelValues(label[0], label[2]).Set(float64(bytesReleased)) + } + } +} + +func (t *Tracker) release(bytes int64) { + for tracker := t; tracker != nil; tracker = tracker.getParent() { + bytesReleased := atomic.AddInt64(&tracker.bytesReleased, -bytes) + if label, ok := MetricsTypes[tracker.label]; ok { + metrics.MemoryUsage.WithLabelValues(label[0], label[2]).Set(float64(bytesReleased)) + } + } +} + +// BytesConsumed returns the consumed memory usage value in bytes. +func (t *Tracker) BytesConsumed() int64 { + return atomic.LoadInt64(&t.bytesConsumed) +} + +// BytesReleased returns the released memory value in bytes. +func (t *Tracker) BytesReleased() int64 { + return atomic.LoadInt64(&t.bytesReleased) +} + +// MaxConsumed returns max number of bytes consumed during execution. +// Note: Don't make this method return -1 for special meanings in the future. Because binary plan has used -1 to +// distinguish between "0 bytes" and "N/A". ref: binaryOpFromFlatOp() +func (t *Tracker) MaxConsumed() int64 { + return t.maxConsumed.Load() +} + +// ResetMaxConsumed should be invoked before executing a new statement in a session. +func (t *Tracker) ResetMaxConsumed() { + t.maxConsumed.Store(t.BytesConsumed()) +} + +// SearchTrackerWithoutLock searches the specific tracker under this tracker without lock. +func (t *Tracker) SearchTrackerWithoutLock(label int) *Tracker { + if t.label == label { + return t + } + children := t.mu.children[label] + if len(children) > 0 { + return children[0] + } + return nil +} + +// SearchTrackerConsumedMoreThanNBytes searches the specific tracker that consumes more than NBytes. +func (t *Tracker) SearchTrackerConsumedMoreThanNBytes(limit int64) (res []*Tracker) { + t.mu.Lock() + defer t.mu.Unlock() + for _, childSlice := range t.mu.children { + for _, tracker := range childSlice { + if tracker.BytesConsumed() > limit { + res = append(res, tracker) + } + } + } + return +} + +// String returns the string representation of this Tracker tree. +func (t *Tracker) String() string { + buffer := bytes.NewBufferString("\n") + t.toString("", buffer) + return buffer.String() +} + +func (t *Tracker) toString(indent string, buffer *bytes.Buffer) { + fmt.Fprintf(buffer, "%s\"%d\"{\n", indent, t.label) + bytesLimit := t.GetBytesLimit() + if bytesLimit > 0 { + fmt.Fprintf(buffer, "%s \"quota\": %s\n", indent, t.FormatBytes(bytesLimit)) + } + fmt.Fprintf(buffer, "%s \"consumed\": %s\n", indent, t.FormatBytes(t.BytesConsumed())) + + t.mu.Lock() + labels := make([]int, 0, len(t.mu.children)) + for label := range t.mu.children { + labels = append(labels, label) + } + slices.Sort(labels) + for _, label := range labels { + children := t.mu.children[label] + for _, child := range children { + child.toString(indent+" ", buffer) + } + } + t.mu.Unlock() + buffer.WriteString(indent + "}\n") +} + +// FormatBytes uses to format bytes, this function will prune precision before format bytes. +func (*Tracker) FormatBytes(numBytes int64) string { + return FormatBytes(numBytes) +} + +// LessThan indicates whether t byteConsumed is less than t2 byteConsumed. +func (t *Tracker) LessThan(t2 *Tracker) bool { + if t == nil { + return true + } + if t2 == nil { + return false + } + return t.BytesConsumed() < t2.BytesConsumed() +} + +// BytesToString converts the memory consumption to a readable string. +func BytesToString(numBytes int64) string { + gb := float64(numBytes) / float64(byteSizeGB) + if gb > 1 { + return fmt.Sprintf("%v GB", gb) + } + + mb := float64(numBytes) / float64(byteSizeMB) + if mb > 1 { + return fmt.Sprintf("%v MB", mb) + } + + kb := float64(numBytes) / float64(byteSizeKB) + if kb > 1 { + return fmt.Sprintf("%v KB", kb) + } + + return fmt.Sprintf("%v Bytes", numBytes) +} + +const ( + byteSizeGB = int64(1 << 30) + byteSizeMB = int64(1 << 20) + byteSizeKB = int64(1 << 10) + byteSizeBB = int64(1) +) + +// FormatBytes uses to format bytes, this function will prune precision before format bytes. +func FormatBytes(numBytes int64) string { + if numBytes <= byteSizeKB { + return BytesToString(numBytes) + } + unit, unitStr := getByteUnit(numBytes) + if unit == byteSizeBB { + return BytesToString(numBytes) + } + v := float64(numBytes) / float64(unit) + decimal := 1 + if numBytes%unit == 0 { + decimal = 0 + } else if v < 10 { + decimal = 2 + } + return fmt.Sprintf("%v %s", strconv.FormatFloat(v, 'f', decimal, 64), unitStr) +} + +func getByteUnit(b int64) (int64, string) { + if b > byteSizeGB { + return byteSizeGB, "GB" + } else if b > byteSizeMB { + return byteSizeMB, "MB" + } else if b > byteSizeKB { + return byteSizeKB, "KB" + } + return byteSizeBB, "Bytes" +} + +// AttachToGlobalTracker attach the tracker to the global tracker +// AttachToGlobalTracker should be called at the initialization for the session executor's tracker +func (t *Tracker) AttachToGlobalTracker(globalTracker *Tracker) { + if globalTracker == nil { + return + } + if !globalTracker.isGlobal { + panic("Attach to a non-GlobalTracker") + } + parent := t.getParent() + if parent != nil { + if parent.isGlobal { + parent.Consume(-t.BytesConsumed()) + } else { + parent.remove(t) + } + } + t.setParent(globalTracker) + globalTracker.Consume(t.BytesConsumed()) +} + +// DetachFromGlobalTracker detach itself from its parent +// Note that only the parent of this tracker is Global Tracker could call this function +// Otherwise it should use Detach +func (t *Tracker) DetachFromGlobalTracker() { + parent := t.getParent() + if parent == nil { + return + } + if !parent.isGlobal { + panic("Detach from a non-GlobalTracker") + } + parent.Consume(-t.BytesConsumed()) + t.setParent(nil) +} + +// ReplaceBytesUsed replace bytesConsume for the tracker +func (t *Tracker) ReplaceBytesUsed(bytes int64) { + t.Consume(bytes - t.BytesConsumed()) +} + +// Reset detach the tracker from the old parent and clear the old children. The label and byteLimit would not be reset. +func (t *Tracker) Reset() { + t.Detach() + t.ReplaceBytesUsed(0) + t.mu.children = nil +} + +func (t *Tracker) getParent() *Tracker { + t.parMu.Lock() + defer t.parMu.Unlock() + return t.parMu.parent +} + +func (t *Tracker) setParent(parent *Tracker) { + t.parMu.Lock() + defer t.parMu.Unlock() + t.parMu.parent = parent +} + +// CountAllChildrenMemUse return memory used tree for the tracker +func (t *Tracker) CountAllChildrenMemUse() map[string]int64 { + trackerMemUseMap := make(map[string]int64, 1024) + countChildMem(t, "", trackerMemUseMap) + return trackerMemUseMap +} + +// GetChildrenForTest returns children trackers +func (t *Tracker) GetChildrenForTest() []*Tracker { + t.mu.Lock() + defer t.mu.Unlock() + trackers := make([]*Tracker, 0) + for _, list := range t.mu.children { + trackers = append(trackers, list...) + } + return trackers +} + +func countChildMem(t *Tracker, familyTreeName string, trackerMemUseMap map[string]int64) { + if len(familyTreeName) > 0 { + familyTreeName += " <- " + } + familyTreeName += "[" + strconv.Itoa(t.Label()) + "]" + trackerMemUseMap[familyTreeName] += t.BytesConsumed() + t.mu.Lock() + defer t.mu.Unlock() + for _, sli := range t.mu.children { + for _, tracker := range sli { + countChildMem(tracker, familyTreeName, trackerMemUseMap) + } + } +} + +const ( + // LabelForSQLText represents the label of the SQL Text + LabelForSQLText int = -1 + // LabelForIndexWorker represents the label of the index worker + LabelForIndexWorker int = -2 + // LabelForInnerList represents the label of the inner list + LabelForInnerList int = -3 + // LabelForInnerTable represents the label of the inner table + LabelForInnerTable int = -4 + // LabelForOuterTable represents the label of the outer table + LabelForOuterTable int = -5 + // LabelForCoprocessor represents the label of the coprocessor + LabelForCoprocessor int = -6 + // LabelForChunkList represents the label of the chunk list + LabelForChunkList int = -7 + // LabelForGlobalSimpleLRUCache represents the label of the Global SimpleLRUCache + LabelForGlobalSimpleLRUCache int = -8 + // LabelForChunkListInDisk represents the label of the chunk list in disk + LabelForChunkListInDisk int = -9 + // LabelForRowContainer represents the label of the row container + LabelForRowContainer int = -10 + // LabelForGlobalStorage represents the label of the Global Storage + LabelForGlobalStorage int = -11 + // LabelForGlobalMemory represents the label of the Global Memory + LabelForGlobalMemory int = -12 + // LabelForBuildSideResult represents the label of the BuildSideResult + LabelForBuildSideResult int = -13 + // LabelForRowChunks represents the label of the row chunks + LabelForRowChunks int = -14 + // LabelForStatsCache represents the label of the stats cache + LabelForStatsCache int = -15 + // LabelForOuterList represents the label of the outer list + LabelForOuterList int = -16 + // LabelForApplyCache represents the label of the apply cache + LabelForApplyCache int = -17 + // LabelForSimpleTask represents the label of the simple task + LabelForSimpleTask int = -18 + // LabelForCTEStorage represents the label of CTE storage + LabelForCTEStorage int = -19 + // LabelForIndexJoinInnerWorker represents the label of IndexJoin InnerWorker + LabelForIndexJoinInnerWorker int = -20 + // LabelForIndexJoinOuterWorker represents the label of IndexJoin OuterWorker + LabelForIndexJoinOuterWorker int = -21 + // LabelForBindCache represents the label of the bind cache + LabelForBindCache int = -22 + // LabelForNonTransactionalDML represents the label of the non-transactional DML + LabelForNonTransactionalDML = -23 + // LabelForAnalyzeMemory represents the label of the memory of each analyze job + LabelForAnalyzeMemory int = -24 + // LabelForGlobalAnalyzeMemory represents the label of the global memory of all analyze jobs + LabelForGlobalAnalyzeMemory int = -25 + // LabelForPreparedPlanCache represents the label of the prepared plan cache memory usage + LabelForPreparedPlanCache int = -26 + // LabelForSession represents the label of a session. + LabelForSession int = -27 + // LabelForMemDB represents the label of the MemDB + LabelForMemDB int = -28 + // LabelForCursorFetch represents the label of the execution of cursor fetch + LabelForCursorFetch int = -29 +) + +// MetricsTypes is used to get label for metrics +// string[0] is LblModule, string[1] is heap-in-use type, string[2] is released type +var MetricsTypes = map[int][]string{ + LabelForGlobalAnalyzeMemory: {"analyze", "inuse", "released"}, +} diff --git a/util/memory/tracker_test.go b/pkg/util/memory/tracker_test.go similarity index 99% rename from util/memory/tracker_test.go rename to pkg/util/memory/tracker_test.go index cdfc4ff353435..ce6d3e3856031 100644 --- a/util/memory/tracker_test.go +++ b/pkg/util/memory/tracker_test.go @@ -25,9 +25,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/mathutil" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/memoryusagealarm/BUILD.bazel b/pkg/util/memoryusagealarm/BUILD.bazel new file mode 100644 index 0000000000000..7c78e8fcfb5dc --- /dev/null +++ b/pkg/util/memoryusagealarm/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "memoryusagealarm", + srcs = ["memoryusagealarm.go"], + importpath = "github.com/pingcap/tidb/pkg/util/memoryusagealarm", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/disk", + "//pkg/util/logutil", + "//pkg/util/memory", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "memoryusagealarm_test", + timeout = "short", + srcs = ["memoryusagealarm_test.go"], + embed = [":memoryusagealarm"], + flaky = True, + race = "on", + deps = [ + "//pkg/sessionctx/stmtctx", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/memory", + "@com_github_stretchr_testify//assert", + ], +) diff --git a/util/memoryusagealarm/memoryusagealarm.go b/pkg/util/memoryusagealarm/memoryusagealarm.go similarity index 98% rename from util/memoryusagealarm/memoryusagealarm.go rename to pkg/util/memoryusagealarm/memoryusagealarm.go index a7d196bd83a7c..3f26956a78ff9 100644 --- a/util/memoryusagealarm/memoryusagealarm.go +++ b/pkg/util/memoryusagealarm/memoryusagealarm.go @@ -25,12 +25,12 @@ import ( "sync/atomic" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) diff --git a/util/memoryusagealarm/memoryusagealarm_test.go b/pkg/util/memoryusagealarm/memoryusagealarm_test.go similarity index 98% rename from util/memoryusagealarm/memoryusagealarm_test.go rename to pkg/util/memoryusagealarm/memoryusagealarm_test.go index 5e6535d3c0c12..cff335e0a4fc0 100644 --- a/util/memoryusagealarm/memoryusagealarm_test.go +++ b/pkg/util/memoryusagealarm/memoryusagealarm_test.go @@ -18,10 +18,10 @@ import ( "testing" "time" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/stretchr/testify/assert" ) diff --git a/pkg/util/metricsutil/BUILD.bazel b/pkg/util/metricsutil/BUILD.bazel new file mode 100644 index 0000000000000..ddede42f32d8a --- /dev/null +++ b/pkg/util/metricsutil/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metricsutil", + srcs = ["common.go"], + importpath = "github.com/pingcap/tidb/pkg/util/metricsutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/domain/metrics", + "//pkg/executor/metrics", + "//pkg/infoschema/metrics", + "//pkg/keyspace", + "//pkg/metrics", + "//pkg/planner/core/metrics", + "//pkg/server/metrics", + "//pkg/session/metrics", + "//pkg/session/txninfo", + "//pkg/sessiontxn/isolation/metrics", + "//pkg/statistics/handle/metrics", + "//pkg/store", + "//pkg/store/copr/metrics", + "//pkg/store/mockstore/unistore/metrics", + "//pkg/ttl/metrics", + "//pkg/util", + "//pkg/util/topsql/reporter/metrics", + "@com_github_pingcap_kvproto//pkg/keyspacepb", + "@com_github_tikv_client_go_v2//config", + "@com_github_tikv_pd_client//:client", + ], +) diff --git a/pkg/util/metricsutil/common.go b/pkg/util/metricsutil/common.go new file mode 100644 index 0000000000000..8f653b93bf396 --- /dev/null +++ b/pkg/util/metricsutil/common.go @@ -0,0 +1,144 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package metricsutil + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/pingcap/kvproto/pkg/keyspacepb" + "github.com/pingcap/tidb/pkg/config" + domain_metrics "github.com/pingcap/tidb/pkg/domain/metrics" + executor_metrics "github.com/pingcap/tidb/pkg/executor/metrics" + infoschema_metrics "github.com/pingcap/tidb/pkg/infoschema/metrics" + "github.com/pingcap/tidb/pkg/keyspace" + "github.com/pingcap/tidb/pkg/metrics" + plannercore "github.com/pingcap/tidb/pkg/planner/core/metrics" + server_metrics "github.com/pingcap/tidb/pkg/server/metrics" + session_metrics "github.com/pingcap/tidb/pkg/session/metrics" + txninfo "github.com/pingcap/tidb/pkg/session/txninfo" + isolation_metrics "github.com/pingcap/tidb/pkg/sessiontxn/isolation/metrics" + statshandler_metrics "github.com/pingcap/tidb/pkg/statistics/handle/metrics" + kvstore "github.com/pingcap/tidb/pkg/store" + copr_metrics "github.com/pingcap/tidb/pkg/store/copr/metrics" + unimetrics "github.com/pingcap/tidb/pkg/store/mockstore/unistore/metrics" + ttlmetrics "github.com/pingcap/tidb/pkg/ttl/metrics" + "github.com/pingcap/tidb/pkg/util" + topsqlreporter_metrics "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics" + tikvconfig "github.com/tikv/client-go/v2/config" + pd "github.com/tikv/pd/client" +) + +// RegisterMetrics register metrics with const label 'keyspace_id' if keyspaceName set. +func RegisterMetrics() error { + cfg := config.GetGlobalConfig() + if keyspace.IsKeyspaceNameEmpty(cfg.KeyspaceName) || strings.ToLower(cfg.Store) != "tikv" { + return registerMetrics(nil) // register metrics without label 'keyspace_id'. + } + + pdAddrs, _, _, err := tikvconfig.ParsePath("tikv://" + cfg.Path) + if err != nil { + return err + } + + timeoutSec := time.Duration(cfg.PDClient.PDServerTimeout) * time.Second + pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{ + CAPath: cfg.Security.ClusterSSLCA, + CertPath: cfg.Security.ClusterSSLCert, + KeyPath: cfg.Security.ClusterSSLKey, + }, pd.WithCustomTimeoutOption(timeoutSec)) + if err != nil { + return err + } + defer pdCli.Close() + + keyspaceMeta, err := getKeyspaceMeta(pdCli, cfg.KeyspaceName) + if err != nil { + return err + } + + return registerMetrics(keyspaceMeta) +} + +// RegisterMetricsForBR register metrics with const label keyspace_id for BR. +func RegisterMetricsForBR(pdAddrs []string, keyspaceName string) error { + if keyspace.IsKeyspaceNameEmpty(keyspaceName) { + return registerMetrics(nil) // register metrics without label 'keyspace_id'. + } + + timeoutSec := 10 * time.Second + pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{}, + pd.WithCustomTimeoutOption(timeoutSec)) + if err != nil { + return err + } + defer pdCli.Close() + + keyspaceMeta, err := getKeyspaceMeta(pdCli, keyspaceName) + if err != nil { + return err + } + + return registerMetrics(keyspaceMeta) +} + +func registerMetrics(keyspaceMeta *keyspacepb.KeyspaceMeta) error { + if keyspaceMeta != nil { + metrics.SetConstLabels("keyspace_id", fmt.Sprint(keyspaceMeta.GetId())) + } + + metrics.InitMetrics() + metrics.RegisterMetrics() + + copr_metrics.InitMetricsVars() + domain_metrics.InitMetricsVars() + executor_metrics.InitMetricsVars() + infoschema_metrics.InitMetricsVars() + isolation_metrics.InitMetricsVars() + plannercore.InitMetricsVars() + server_metrics.InitMetricsVars() + session_metrics.InitMetricsVars() + statshandler_metrics.InitMetricsVars() + topsqlreporter_metrics.InitMetricsVars() + ttlmetrics.InitMetricsVars() + txninfo.InitMetricsVars() + + if config.GetGlobalConfig().Store == "unistore" { + unimetrics.RegisterMetrics() + } + return nil +} + +func getKeyspaceMeta(pdCli pd.Client, keyspaceName string) (*keyspacepb.KeyspaceMeta, error) { + // Load Keyspace meta with retry. + var keyspaceMeta *keyspacepb.KeyspaceMeta + err := util.RunWithRetry(util.DefaultMaxRetries, util.RetryInterval, func() (bool, error) { + var errInner error + keyspaceMeta, errInner = pdCli.LoadKeyspace(context.TODO(), keyspaceName) + // Retry when pd not bootstrapped or if keyspace not exists. + if kvstore.IsNotBootstrappedError(errInner) || kvstore.IsKeyspaceNotExistError(errInner) { + return true, errInner + } + // Do not retry when success or encountered unexpected error. + return false, errInner + }) + if err != nil { + return nil, err + } + + return keyspaceMeta, nil +} diff --git a/pkg/util/misc.go b/pkg/util/misc.go new file mode 100644 index 0000000000000..1b0fce47d2b64 --- /dev/null +++ b/pkg/util/misc.go @@ -0,0 +1,696 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package util + +import ( + "context" + "crypto" + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/metrics" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/logutil" + tlsutil "github.com/pingcap/tidb/pkg/util/tls" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +const ( + // DefaultMaxRetries indicates the max retry count. + DefaultMaxRetries = 30 + // RetryInterval indicates retry interval. + RetryInterval uint64 = 500 +) + +// RunWithRetry will run the f with backoff and retry. +// retryCnt: Max retry count +// backoff: When run f failed, it will sleep backoff * triedCount time.Millisecond. +// Function f should have two return value. The first one is an bool which indicate if the err if retryable. +// The second is if the f meet any error. +func RunWithRetry(retryCnt int, backoff uint64, f func() (bool, error)) (err error) { + for i := 1; i <= retryCnt; i++ { + var retryAble bool + retryAble, err = f() + if err == nil || !retryAble { + return errors.Trace(err) + } + sleepTime := time.Duration(backoff*uint64(i)) * time.Millisecond + time.Sleep(sleepTime) + } + return errors.Trace(err) +} + +// WithRecovery wraps goroutine startup call with force recovery. +// it will dump current goroutine stack into log if catch any recover result. +// +// exec: execute logic function. +// recoverFn: handler will be called after recover and before dump stack, passing `nil` means noop. +func WithRecovery(exec func(), recoverFn func(r interface{})) { + defer func() { + r := recover() + if recoverFn != nil { + recoverFn(r) + } + if r != nil { + logutil.BgLogger().Error("panic in the recoverable goroutine", + zap.Any("r", r), + zap.Stack("stack trace")) + } + }() + exec() +} + +// Recover includes operations such as recovering, clearing,and printing information. +// It will dump current goroutine stack into log if catch any recover result. +// +// metricsLabel: The label of PanicCounter metrics. +// funcInfo: Some information for the panic function. +// recoverFn: Handler will be called after recover and before dump stack, passing `nil` means noop. +// quit: If this value is true, the current program exits after recovery. +func Recover(metricsLabel, funcInfo string, recoverFn func(), quit bool) { + //nolint: revive + r := recover() + if r == nil { + return + } + + if recoverFn != nil { + recoverFn() + } + logutil.BgLogger().Error("panic in the recoverable goroutine", + zap.String("label", metricsLabel), + zap.String("funcInfo", funcInfo), + zap.Any("r", r), + zap.Stack("stack")) + metrics.PanicCounter.WithLabelValues(metricsLabel).Inc() + if quit { + // Wait for metrics to be pushed. + time.Sleep(time.Second * 15) + os.Exit(1) + } +} + +// HasCancelled checks whether context has be cancelled. +func HasCancelled(ctx context.Context) (cancel bool) { + select { + case <-ctx.Done(): + cancel = true + default: + } + return +} + +const ( + // syntaxErrorPrefix is the common prefix for SQL syntax error in TiDB. + syntaxErrorPrefix = "You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use" +) + +// SyntaxError converts parser error to TiDB's syntax error. +func SyntaxError(err error) error { + if err == nil { + return nil + } + logutil.BgLogger().Debug("syntax error", zap.Error(err)) + + // If the error is already a terror with stack, pass it through. + if errors.HasStack(err) { + cause := errors.Cause(err) + if _, ok := cause.(*terror.Error); ok { + return err + } + } + + return parser.ErrParse.GenWithStackByArgs(syntaxErrorPrefix, err.Error()) +} + +// SyntaxWarn converts parser warn to TiDB's syntax warn. +func SyntaxWarn(err error) error { + if err == nil { + return nil + } + logutil.BgLogger().Debug("syntax error", zap.Error(err)) + + // If the warn is already a terror with stack, pass it through. + if errors.HasStack(err) { + cause := errors.Cause(err) + if _, ok := cause.(*terror.Error); ok { + return err + } + } + + return parser.ErrParse.GenWithStackByArgs(syntaxErrorPrefix, err.Error()) +} + +var ( + // InformationSchemaName is the `INFORMATION_SCHEMA` database name. + InformationSchemaName = model.NewCIStr("INFORMATION_SCHEMA") + // PerformanceSchemaName is the `PERFORMANCE_SCHEMA` database name. + PerformanceSchemaName = model.NewCIStr("PERFORMANCE_SCHEMA") + // MetricSchemaName is the `METRICS_SCHEMA` database name. + MetricSchemaName = model.NewCIStr("METRICS_SCHEMA") + // ClusterTableInstanceColumnName is the `INSTANCE` column name of the cluster table. + ClusterTableInstanceColumnName = "INSTANCE" +) + +// IsMemOrSysDB uses to check whether dbLowerName is memory database or system database. +func IsMemOrSysDB(dbLowerName string) bool { + return IsMemDB(dbLowerName) || IsSysDB(dbLowerName) +} + +// IsMemDB checks whether dbLowerName is memory database. +func IsMemDB(dbLowerName string) bool { + switch dbLowerName { + case InformationSchemaName.L, + PerformanceSchemaName.L, + MetricSchemaName.L: + return true + } + return false +} + +// IsSysDB checks whether dbLowerName is system database. +func IsSysDB(dbLowerName string) bool { + return dbLowerName == mysql.SystemDB +} + +// IsSystemView is similar to IsMemOrSyDB, but does not include the mysql schema +func IsSystemView(dbLowerName string) bool { + switch dbLowerName { + case InformationSchemaName.L, + PerformanceSchemaName.L, + MetricSchemaName.L: + return true + } + return false +} + +// X509NameOnline prints pkix.Name into old X509_NAME_oneline format. +// https://www.openssl.org/docs/manmaster/man3/X509_NAME_oneline.html +func X509NameOnline(n pkix.Name) string { + s := make([]string, 0, len(n.Names)) + for _, name := range n.Names { + oid := name.Type.String() + // unlike MySQL, TiDB only support check pkixAttributeTypeNames fields + if n, exist := pkixAttributeTypeNames[oid]; exist { + s = append(s, n+"="+fmt.Sprint(name.Value)) + } + } + if len(s) == 0 { + return "" + } + return "/" + strings.Join(s, "/") +} + +const ( + // Country is type name for country. + Country = "C" + // Organization is type name for organization. + Organization = "O" + // OrganizationalUnit is type name for organizational unit. + OrganizationalUnit = "OU" + // Locality is type name for locality. + Locality = "L" + // Email is type name for email. + Email = "emailAddress" + // CommonName is type name for common name. + CommonName = "CN" + // Province is type name for province or state. + Province = "ST" +) + +// see go/src/crypto/x509/pkix/pkix.go:attributeTypeNames +var pkixAttributeTypeNames = map[string]string{ + "2.5.4.6": Country, + "2.5.4.10": Organization, + "2.5.4.11": OrganizationalUnit, + "2.5.4.3": CommonName, + "2.5.4.5": "SERIALNUMBER", + "2.5.4.7": Locality, + "2.5.4.8": Province, + "2.5.4.9": "STREET", + "2.5.4.17": "POSTALCODE", + "1.2.840.113549.1.9.1": Email, +} + +var pkixTypeNameAttributes = make(map[string]string) + +// MockPkixAttribute generates mock AttributeTypeAndValue. +// only used for test. +func MockPkixAttribute(name, value string) pkix.AttributeTypeAndValue { + n, exists := pkixTypeNameAttributes[name] + if !exists { + panic(fmt.Sprintf("unsupport mock type: %s", name)) + } + split := strings.Split(n, ".") + vs := make([]int, 0, len(split)) + for _, v := range split { + i, err := strconv.Atoi(v) + if err != nil { + panic(err) + } + vs = append(vs, i) + } + return pkix.AttributeTypeAndValue{ + Type: vs, + Value: value, + } +} + +// SANType is enum value for GlobalPrivValue.SANs keys. +type SANType string + +const ( + // URI indicates uri info in SAN. + URI = SANType("URI") + // DNS indicates dns info in SAN. + DNS = SANType("DNS") + // IP indicates ip info in SAN. + IP = SANType("IP") +) + +var supportSAN = map[SANType]struct{}{ + URI: {}, + DNS: {}, + IP: {}, +} + +// ParseAndCheckSAN parses and check SAN str. +func ParseAndCheckSAN(san string) (map[SANType][]string, error) { + sanMap := make(map[SANType][]string) + sans := strings.Split(san, ",") + for _, san := range sans { + kv := strings.SplitN(san, ":", 2) + if len(kv) != 2 { + return nil, errors.Errorf("invalid SAN value %s", san) + } + k, v := SANType(strings.ToUpper(strings.TrimSpace(kv[0]))), strings.TrimSpace(kv[1]) + if _, s := supportSAN[k]; !s { + return nil, errors.Errorf("unsupported SAN key %s, current only support %v", k, supportSAN) + } + sanMap[k] = append(sanMap[k], v) + } + return sanMap, nil +} + +// CheckSupportX509NameOneline parses and validate input str is X509_NAME_oneline format +// and precheck check-item is supported by TiDB +// https://www.openssl.org/docs/manmaster/man3/X509_NAME_oneline.html +func CheckSupportX509NameOneline(oneline string) (err error) { + entries := strings.Split(oneline, `/`) + for _, entry := range entries { + if len(entry) == 0 { + continue + } + kvs := strings.Split(entry, "=") + if len(kvs) != 2 { + err = errors.Errorf("invalid X509_NAME input: %s", oneline) + return + } + k := kvs[0] + if _, support := pkixTypeNameAttributes[k]; !support { + err = errors.Errorf("Unsupport check '%s' in current version TiDB", k) + return + } + } + return +} + +var tlsCipherString = map[uint16]string{ + tls.TLS_RSA_WITH_RC4_128_SHA: "RC4-SHA", + tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "DES-CBC3-SHA", + tls.TLS_RSA_WITH_AES_128_CBC_SHA: "AES128-SHA", + tls.TLS_RSA_WITH_AES_256_CBC_SHA: "AES256-SHA", + tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "AES128-SHA256", + tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "AES128-GCM-SHA256", + tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "AES256-GCM-SHA384", + tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "ECDHE-ECDSA-RC4-SHA", + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "ECDHE-ECDSA-AES128-SHA", + tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "ECDHE-ECDSA-AES256-SHA", + tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "ECDHE-RSA-RC4-SHA", + tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "ECDHE-RSA-DES-CBC3-SHA", + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "ECDHE-RSA-AES128-SHA", + tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "ECDHE-RSA-AES256-SHA", + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "ECDHE-ECDSA-AES128-SHA256", + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "ECDHE-RSA-AES128-SHA256", + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256", + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256", + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "ECDHE-RSA-AES256-GCM-SHA384", + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "ECDHE-ECDSA-AES256-GCM-SHA384", + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "ECDHE-RSA-CHACHA20-POLY1305", + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "ECDHE-ECDSA-CHACHA20-POLY1305", + // TLS 1.3 cipher suites, compatible with mysql using '_'. + tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256", + tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384", + tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", +} + +// SupportCipher maintains cipher supported by TiDB. +var SupportCipher = make(map[string]struct{}, len(tlsCipherString)) + +// TLSCipher2String convert tls num to string. +// Taken from https://testssl.sh/openssl-rfc.mapping.html . +func TLSCipher2String(n uint16) string { + s, ok := tlsCipherString[n] + if !ok { + return "" + } + return s +} + +// ColumnsToProto converts a slice of model.ColumnInfo to a slice of tipb.ColumnInfo. +func ColumnsToProto(columns []*model.ColumnInfo, pkIsHandle bool, forIndex bool) []*tipb.ColumnInfo { + cols := make([]*tipb.ColumnInfo, 0, len(columns)) + for _, c := range columns { + col := ColumnToProto(c, forIndex) + // TODO: Here `PkHandle`'s meaning is changed, we will change it to `IsHandle` when tikv's old select logic + // is abandoned. + if (pkIsHandle && mysql.HasPriKeyFlag(c.GetFlag())) || c.ID == model.ExtraHandleID { + col.PkHandle = true + } else { + col.PkHandle = false + } + cols = append(cols, col) + } + return cols +} + +// ColumnToProto converts model.ColumnInfo to tipb.ColumnInfo. +func ColumnToProto(c *model.ColumnInfo, forIndex bool) *tipb.ColumnInfo { + pc := &tipb.ColumnInfo{ + ColumnId: c.ID, + Collation: collate.RewriteNewCollationIDIfNeeded(int32(mysql.CollationNames[c.GetCollate()])), + ColumnLen: int32(c.GetFlen()), + Decimal: int32(c.GetDecimal()), + Flag: int32(c.GetFlag()), + Elems: c.GetElems(), + } + if forIndex { + // Use array type for read the multi-valued index. + pc.Tp = int32(c.FieldType.ArrayType().GetType()) + if c.FieldType.IsArray() { + // Use "binary" collation for read the multi-valued index. Most of the time, the `Collation` of this hidden + // column should already been set to "binary". However, in old versions, the collation is set to the default + // value. See https://github.com/pingcap/tidb/issues/46717 + pc.Collation = int32(mysql.CollationNames["binary"]) + } + } else { + pc.Tp = int32(c.GetType()) + } + return pc +} + +func init() { + for _, value := range tlsCipherString { + SupportCipher[value] = struct{}{} + } + for key, value := range pkixAttributeTypeNames { + pkixTypeNameAttributes[value] = key + } +} + +// GetSequenceByName could be used in expression package without import cycle problem. +var GetSequenceByName func(is interface{}, schema, sequence model.CIStr) (SequenceTable, error) + +// SequenceTable is implemented by tableCommon, +// and it is specialised in handling sequence operation. +// Otherwise calling table will cause import cycle problem. +type SequenceTable interface { + GetSequenceID() int64 + GetSequenceNextVal(ctx interface{}, dbName, seqName string) (int64, error) + SetSequenceVal(ctx interface{}, newVal int64, dbName, seqName string) (int64, bool, error) +} + +// LoadTLSCertificates loads CA/KEY/CERT for special paths. +func LoadTLSCertificates(ca, key, cert string, autoTLS bool, rsaKeySize int) (tlsConfig *tls.Config, autoReload bool, err error) { + autoReload = false + if len(cert) == 0 || len(key) == 0 { + if !autoTLS { + logutil.BgLogger().Warn("Automatic TLS Certificate creation is disabled", zap.Error(err)) + return + } + autoReload = true + tempStoragePath := config.GetGlobalConfig().TempStoragePath + cert = filepath.Join(tempStoragePath, "/cert.pem") + key = filepath.Join(tempStoragePath, "/key.pem") + err = createTLSCertificates(cert, key, rsaKeySize) + if err != nil { + logutil.BgLogger().Warn("TLS Certificate creation failed", zap.Error(err)) + return + } + } + + var tlsCert tls.Certificate + tlsCert, err = tls.LoadX509KeyPair(cert, key) + if err != nil { + logutil.BgLogger().Warn("load x509 failed", zap.Error(err)) + err = errors.Trace(err) + return + } + + requireTLS := tlsutil.RequireSecureTransport.Load() + + var minTLSVersion uint16 = tls.VersionTLS11 + switch tlsver := config.GetGlobalConfig().Security.MinTLSVersion; tlsver { + case "TLSv1.0": + minTLSVersion = tls.VersionTLS10 + case "TLSv1.1": + minTLSVersion = tls.VersionTLS11 + case "TLSv1.2": + minTLSVersion = tls.VersionTLS12 + case "TLSv1.3": + minTLSVersion = tls.VersionTLS13 + case "": + default: + logutil.BgLogger().Warn( + "Invalid TLS version, using default instead", + zap.String("tls-version", tlsver), + ) + } + if minTLSVersion < tls.VersionTLS12 { + logutil.BgLogger().Warn( + "Minimum TLS version allows pre-TLSv1.2 protocols, this is not recommended", + ) + } + + // Try loading CA cert. + clientAuthPolicy := tls.NoClientCert + if requireTLS { + clientAuthPolicy = tls.RequestClientCert + } + var certPool *x509.CertPool + if len(ca) > 0 { + var caCert []byte + caCert, err = os.ReadFile(ca) + if err != nil { + logutil.BgLogger().Warn("read file failed", zap.Error(err)) + err = errors.Trace(err) + return + } + certPool = x509.NewCertPool() + if certPool.AppendCertsFromPEM(caCert) { + if requireTLS { + clientAuthPolicy = tls.RequireAndVerifyClientCert + } else { + clientAuthPolicy = tls.VerifyClientCertIfGiven + } + } + } + + // This excludes ciphers listed in tls.InsecureCipherSuites() and can be used to filter out more + var cipherSuites []uint16 + var cipherNames []string + for _, sc := range tls.CipherSuites() { + switch sc.ID { + case tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: + logutil.BgLogger().Info("Disabling weak cipherSuite", zap.String("cipherSuite", sc.Name)) + default: + cipherNames = append(cipherNames, sc.Name) + cipherSuites = append(cipherSuites, sc.ID) + } + } + logutil.BgLogger().Info("Enabled ciphersuites", zap.Strings("cipherNames", cipherNames)) + + /* #nosec G402 */ + tlsConfig = &tls.Config{ + Certificates: []tls.Certificate{tlsCert}, + ClientCAs: certPool, + ClientAuth: clientAuthPolicy, + MinVersion: minTLSVersion, + CipherSuites: cipherSuites, + } + return +} + +var ( + internalClientInit sync.Once + internalHTTPClient *http.Client + internalHTTPSchema string +) + +// InternalHTTPClient is used by TiDB-Server to request other components. +func InternalHTTPClient() *http.Client { + internalClientInit.Do(initInternalClient) + return internalHTTPClient +} + +// InternalHTTPSchema specifies use http or https to request other components. +func InternalHTTPSchema() string { + internalClientInit.Do(initInternalClient) + return internalHTTPSchema +} + +func initInternalClient() { + clusterSecurity := config.GetGlobalConfig().Security.ClusterSecurity() + tlsCfg, err := clusterSecurity.ToTLSConfig() + if err != nil { + logutil.BgLogger().Fatal("could not load cluster ssl", zap.Error(err)) + } + if tlsCfg == nil { + internalHTTPSchema = "http" + internalHTTPClient = http.DefaultClient + return + } + internalHTTPSchema = "https" + internalHTTPClient = &http.Client{ + Transport: &http.Transport{TLSClientConfig: tlsCfg}, + } +} + +// ComposeURL adds HTTP schema if missing and concats address with path +func ComposeURL(address, path string) string { + if strings.HasPrefix(address, "http://") || strings.HasPrefix(address, "https://") { + return fmt.Sprintf("%s%s", address, path) + } + return fmt.Sprintf("%s://%s%s", InternalHTTPSchema(), address, path) +} + +// GetLocalIP will return a local IP(non-loopback, non 0.0.0.0), if there is one +func GetLocalIP() string { + addrs, err := net.InterfaceAddrs() + if err == nil { + for _, address := range addrs { + ipnet, ok := address.(*net.IPNet) + if ok && ipnet.IP.IsGlobalUnicast() { + return ipnet.IP.String() + } + } + } + return "" +} + +// CreateCertificates creates and writes a cert based on the params. +func CreateCertificates(certpath string, keypath string, rsaKeySize int, pubKeyAlgo x509.PublicKeyAlgorithm, + signAlgo x509.SignatureAlgorithm) error { + certValidity := 90 * 24 * time.Hour // 90 days + notBefore := time.Now() + notAfter := notBefore.Add(certValidity) + hostname, err := os.Hostname() + if err != nil { + return err + } + + template := x509.Certificate{ + Subject: pkix.Name{ + CommonName: "TiDB_Server_Auto_Generated_Server_Certificate", + }, + SerialNumber: big.NewInt(1), + NotBefore: notBefore, + NotAfter: notAfter, + DNSNames: []string{hostname}, + SignatureAlgorithm: signAlgo, + } + + var privKey crypto.Signer + switch pubKeyAlgo { + case x509.RSA: + privKey, err = rsa.GenerateKey(rand.Reader, rsaKeySize) + case x509.ECDSA: + privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + case x509.Ed25519: + _, privKey, err = ed25519.GenerateKey(rand.Reader) + default: + return errors.Errorf("unknown public key algorithm: %s", pubKeyAlgo.String()) + } + if err != nil { + return err + } + // DER: Distinguished Encoding Rules, this is the ASN.1 encoding rule of the certificate. + derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, privKey.Public(), privKey) + if err != nil { + return err + } + + certOut, err := os.Create(certpath) + if err != nil { + return err + } + if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { + return err + } + if err := certOut.Close(); err != nil { + return err + } + + keyOut, err := os.OpenFile(keypath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return err + } + + privBytes, err := x509.MarshalPKCS8PrivateKey(privKey) + if err != nil { + return err + } + + if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { + return err + } + + if err := keyOut.Close(); err != nil { + return err + } + + logutil.BgLogger().Info("TLS Certificates created", zap.String("cert", certpath), zap.String("key", keypath), + zap.Duration("validity", certValidity), zap.Int("rsaKeySize", rsaKeySize)) + return nil +} + +func createTLSCertificates(certpath string, keypath string, rsaKeySize int) error { + // use RSA and unspecified signature algorithm + return CreateCertificates(certpath, keypath, rsaKeySize, x509.RSA, x509.UnknownSignatureAlgorithm) +} diff --git a/pkg/util/misc_test.go b/pkg/util/misc_test.go new file mode 100644 index 0000000000000..25d883604c7cc --- /dev/null +++ b/pkg/util/misc_test.go @@ -0,0 +1,215 @@ +// Copyright 2016 PingCAP, Inc. +// +// 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. + +package util + +import ( + "bytes" + "crypto/x509/pkix" + "fmt" + "testing" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/fastrand" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/assert" +) + +func TestRunWithRetry(t *testing.T) { + t.Run("success", func(t *testing.T) { + cnt := 0 + err := RunWithRetry(3, 1, func() (bool, error) { + cnt++ + if cnt < 2 { + return true, errors.New("err") + } + return true, nil + }) + assert.Nil(t, err) + assert.Equal(t, 2, cnt) + }) + + t.Run("retry exceeds", func(t *testing.T) { + cnt := 0 + err := RunWithRetry(3, 1, func() (bool, error) { + cnt++ + if cnt < 4 { + return true, errors.New("err") + } + return true, nil + }) + assert.NotNil(t, err) + assert.Equal(t, 3, cnt) + }) + + t.Run("failed result", func(t *testing.T) { + cnt := 0 + err := RunWithRetry(3, 1, func() (bool, error) { + cnt++ + if cnt < 2 { + return false, errors.New("err") + } + return true, nil + }) + assert.NotNil(t, err) + assert.Equal(t, 1, cnt) + }) +} + +func TestX509NameParseMatch(t *testing.T) { + assert.Equal(t, "", X509NameOnline(pkix.Name{})) + + check := pkix.Name{ + Names: []pkix.AttributeTypeAndValue{ + MockPkixAttribute(Country, "SE"), + MockPkixAttribute(Province, "Stockholm2"), + MockPkixAttribute(Locality, "Stockholm"), + MockPkixAttribute(Organization, "MySQL demo client certificate"), + MockPkixAttribute(OrganizationalUnit, "testUnit"), + MockPkixAttribute(CommonName, "client"), + MockPkixAttribute(Email, "client@example.com"), + }, + } + result := "/C=SE/ST=Stockholm2/L=Stockholm/O=MySQL demo client certificate/OU=testUnit/CN=client/emailAddress=client@example.com" + assert.Equal(t, result, X509NameOnline(check)) +} + +func TestBasicFuncWithRecovery(t *testing.T) { + var recovery interface{} + WithRecovery(func() { + panic("test") + }, func(r interface{}) { + recovery = r + }) + assert.Equal(t, "test", recovery) +} + +func TestBasicFuncSyntaxError(t *testing.T) { + assert.Nil(t, SyntaxError(nil)) + assert.True(t, terror.ErrorEqual(SyntaxError(errors.New("test")), parser.ErrParse)) + assert.True(t, terror.ErrorEqual(SyntaxError(parser.ErrSyntax.GenWithStackByArgs()), parser.ErrSyntax)) +} + +func TestBasicFuncSyntaxWarn(t *testing.T) { + assert.Nil(t, SyntaxWarn(nil)) + assert.True(t, terror.ErrorEqual(SyntaxWarn(errors.New("test")), parser.ErrParse)) +} + +func TestBasicFuncProcessInfo(t *testing.T) { + sc := stmtctx.NewStmtCtx() + sc.MemTracker = memory.NewTracker(-1, -1) + pi := ProcessInfo{ + ID: 1, + User: "test", + Host: "www", + DB: "db", + Command: mysql.ComSleep, + Plan: nil, + Time: time.Now(), + State: 3, + Info: "test", + StmtCtx: sc, + } + row := pi.ToRowForShow(false) + row2 := pi.ToRowForShow(true) + assert.Equal(t, row2, row) + assert.Len(t, row, 8) + assert.Equal(t, pi.ID, row[0]) + assert.Equal(t, pi.User, row[1]) + assert.Equal(t, pi.Host, row[2]) + assert.Equal(t, pi.DB, row[3]) + assert.Equal(t, "Sleep", row[4]) + assert.Equal(t, uint64(0), row[5]) + assert.Equal(t, "in transaction; autocommit", row[6]) + assert.Equal(t, "test", row[7]) + + row3 := pi.ToRow(time.UTC) + assert.Equal(t, row, row3[:8]) + assert.Equal(t, int64(0), row3[9]) +} + +func TestBasicFuncRandomBuf(t *testing.T) { + buf := fastrand.Buf(5) + assert.Len(t, buf, 5) + assert.False(t, bytes.Contains(buf, []byte("$"))) + assert.False(t, bytes.Contains(buf, []byte{0})) +} + +func TestToPB(t *testing.T) { + column := &model.ColumnInfo{ + ID: 1, + Name: model.NewCIStr("c"), + Offset: 0, + DefaultValue: 0, + FieldType: *types.NewFieldType(0), + Hidden: true, + } + column.SetCollate("utf8mb4_general_ci") + + column2 := &model.ColumnInfo{ + ID: 1, + Name: model.NewCIStr("c"), + Offset: 0, + DefaultValue: 0, + FieldType: *types.NewFieldType(0), + Hidden: true, + } + column2.SetCollate("utf8mb4_bin") + + assert.Equal(t, "column_id:1 collation:-45 columnLen:-1 decimal:-1 ", ColumnToProto(column, false).String()) + assert.Equal(t, "column_id:1 collation:-45 columnLen:-1 decimal:-1 ", ColumnsToProto([]*model.ColumnInfo{column, column2}, false, false)[0].String()) +} + +func TestComposeURL(t *testing.T) { + // TODO Setup config for TLS and verify https protocol output + assert.Equal(t, ComposeURL("server.example.com", ""), "http://server.example.com") + assert.Equal(t, ComposeURL("httpserver.example.com", ""), "http://httpserver.example.com") + assert.Equal(t, ComposeURL("http://httpserver.example.com", "/"), "http://httpserver.example.com/") + assert.Equal(t, ComposeURL("https://httpserver.example.com", "/api/test"), "https://httpserver.example.com/api/test") + assert.Equal(t, ComposeURL("http://server.example.com", ""), "http://server.example.com") + assert.Equal(t, ComposeURL("https://server.example.com", ""), "https://server.example.com") +} + +func assertChannel[T any](t *testing.T, ch <-chan T, items ...T) { + for i, item := range items { + assert.Equal(t, <-ch, item, "the %d-th item doesn't match", i) + } + select { + case item, ok := <-ch: + assert.False(t, ok, "channel not closed: more item %v", item) + case <-time.After(50 * time.Microsecond): + t.Fatal("channel not closed: blocked") + } +} + +func TestChannelMap(t *testing.T) { + ch := make(chan int, 4) + ch <- 1 + ch <- 2 + ch <- 3 + + tableCh := ChanMap(ch, func(i int) string { + return fmt.Sprintf("table%d", i) + }) + close(ch) + + assertChannel(t, tableCh, "table1", "table2", "table3") +} diff --git a/pkg/util/mock/BUILD.bazel b/pkg/util/mock/BUILD.bazel new file mode 100644 index 0000000000000..75ac693889df1 --- /dev/null +++ b/pkg/util/mock/BUILD.bazel @@ -0,0 +1,57 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mock", + srcs = [ + "client.go", + "context.go", + "iter.go", + "metrics.go", + "store.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/mock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/extension", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/terror", + "//pkg/sessionctx", + "//pkg/sessionctx/sessionstates", + "//pkg/sessionctx/variable", + "//pkg/util", + "//pkg/util/disk", + "//pkg/util/memory", + "//pkg/util/sli", + "//pkg/util/sqlexec", + "//pkg/util/topsql/stmtstats", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/deadlock", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-binlog", + "@com_github_prometheus_client_golang//prometheus", + "@com_github_stretchr_testify//assert", + "@com_github_tikv_client_go_v2//oracle", + "@com_github_tikv_client_go_v2//tikv", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "mock_test", + timeout = "short", + srcs = [ + "iter_test.go", + "main_test.go", + "mock_test.go", + ], + embed = [":mock"], + flaky = True, + deps = [ + "//pkg/kv", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/mock/client.go b/pkg/util/mock/client.go new file mode 100644 index 0000000000000..579eeae7d02db --- /dev/null +++ b/pkg/util/mock/client.go @@ -0,0 +1,33 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "context" + + "github.com/pingcap/tidb/pkg/kv" +) + +// Client implement kv.Client interface, mocked from "CopClient" defined in +// "store/tikv/copprocessor.go". +type Client struct { + kv.RequestTypeSupportedChecker + MockResponse kv.Response +} + +// Send implement kv.Client interface. +func (c *Client) Send(_ context.Context, _ *kv.Request, _ interface{}, _ *kv.ClientSendOption) kv.Response { + return c.MockResponse +} diff --git a/pkg/util/mock/context.go b/pkg/util/mock/context.go new file mode 100644 index 0000000000000..56dc4ae1ad50d --- /dev/null +++ b/pkg/util/mock/context.go @@ -0,0 +1,490 @@ +// Copyright 2015 PingCAP, Inc. +// +// 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. + +// Package mock is just for test only. +package mock + +import ( + "context" + "fmt" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/kvproto/pkg/kvrpcpb" + "github.com/pingcap/tidb/pkg/extension" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/sessionstates" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/sli" + "github.com/pingcap/tidb/pkg/util/sqlexec" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "github.com/pingcap/tipb/go-binlog" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" +) + +var ( + _ sessionctx.Context = (*Context)(nil) + _ sqlexec.SQLExecutor = (*Context)(nil) +) + +// Context represents mocked sessionctx.Context. +type Context struct { + txn wrapTxn // mock global variable + Store kv.Storage // mock global variable + ctx context.Context + sm util.SessionManager + is sessionctx.InfoschemaMetaVersion + values map[fmt.Stringer]interface{} + sessionVars *variable.SessionVars + cancel context.CancelFunc + pcache sessionctx.PlanCache + level kvrpcpb.DiskFullOpt + inSandBoxMode bool +} + +type wrapTxn struct { + kv.Transaction + tsFuture oracle.Future +} + +func (txn *wrapTxn) validOrPending() bool { + return txn.tsFuture != nil || txn.Transaction.Valid() +} + +func (txn *wrapTxn) pending() bool { + return txn.Transaction == nil && txn.tsFuture != nil +} + +// Wait creates a new kvTransaction +func (txn *wrapTxn) Wait(_ context.Context, sctx sessionctx.Context) (kv.Transaction, error) { + if !txn.validOrPending() { + return txn, errors.AddStack(kv.ErrInvalidTxn) + } + if txn.pending() { + ts, err := txn.tsFuture.Wait() + if err != nil { + return nil, err + } + kvTxn, err := sctx.GetStore().Begin(tikv.WithStartTS(ts)) + if err != nil { + return nil, errors.Trace(err) + } + txn.Transaction = kvTxn + } + return txn, nil +} + +func (txn *wrapTxn) Valid() bool { + return txn.Transaction != nil && txn.Transaction.Valid() +} + +func (txn *wrapTxn) CacheTableInfo(id int64, info *model.TableInfo) { + if txn.Transaction == nil { + return + } + txn.Transaction.CacheTableInfo(id, info) +} + +func (txn *wrapTxn) GetTableInfo(id int64) *model.TableInfo { + if txn.Transaction == nil { + return nil + } + return txn.Transaction.GetTableInfo(id) +} + +// Execute implements sqlexec.SQLExecutor Execute interface. +func (*Context) Execute(_ context.Context, _ string) ([]sqlexec.RecordSet, error) { + return nil, errors.Errorf("Not Supported") +} + +// ExecuteStmt implements sqlexec.SQLExecutor ExecuteStmt interface. +func (*Context) ExecuteStmt(_ context.Context, _ ast.StmtNode) (sqlexec.RecordSet, error) { + return nil, errors.Errorf("Not Supported") +} + +// SetDiskFullOpt sets allowed options of current operation in each TiKV disk usage level. +func (c *Context) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { + c.level = level +} + +// ClearDiskFullOpt clears allowed options of current operation in each TiKV disk usage level. +func (c *Context) ClearDiskFullOpt() { + c.level = kvrpcpb.DiskFullOpt_NotAllowedOnFull +} + +// ExecuteInternal implements sqlexec.SQLExecutor ExecuteInternal interface. +func (*Context) ExecuteInternal(_ context.Context, _ string, _ ...interface{}) (sqlexec.RecordSet, error) { + return nil, errors.Errorf("Not Supported") +} + +// ShowProcess implements sessionctx.Context ShowProcess interface. +func (*Context) ShowProcess() *util.ProcessInfo { + return &util.ProcessInfo{} +} + +// IsDDLOwner checks whether this session is DDL owner. +func (*Context) IsDDLOwner() bool { + return true +} + +// SetValue implements sessionctx.Context SetValue interface. +func (c *Context) SetValue(key fmt.Stringer, value interface{}) { + c.values[key] = value +} + +// Value implements sessionctx.Context Value interface. +func (c *Context) Value(key fmt.Stringer) interface{} { + value := c.values[key] + return value +} + +// ClearValue implements sessionctx.Context ClearValue interface. +func (c *Context) ClearValue(key fmt.Stringer) { + delete(c.values, key) +} + +// HasDirtyContent implements sessionctx.Context ClearValue interface. +func (*Context) HasDirtyContent(_ int64) bool { + return false +} + +// GetSessionVars implements the sessionctx.Context GetSessionVars interface. +func (c *Context) GetSessionVars() *variable.SessionVars { + return c.sessionVars +} + +// Txn implements sessionctx.Context Txn interface. +func (c *Context) Txn(bool) (kv.Transaction, error) { + return &c.txn, nil +} + +// GetClient implements sessionctx.Context GetClient interface. +func (c *Context) GetClient() kv.Client { + if c.Store == nil { + return nil + } + return c.Store.GetClient() +} + +// GetMPPClient implements sessionctx.Context GetMPPClient interface. +func (c *Context) GetMPPClient() kv.MPPClient { + if c.Store == nil { + return nil + } + return c.Store.GetMPPClient() +} + +// GetInfoSchema implements sessionctx.Context GetInfoSchema interface. +func (c *Context) GetInfoSchema() sessionctx.InfoschemaMetaVersion { + vars := c.GetSessionVars() + if snap, ok := vars.SnapshotInfoschema.(sessionctx.InfoschemaMetaVersion); ok { + return snap + } + if vars.TxnCtx != nil && vars.InTxn() { + if is, ok := vars.TxnCtx.InfoSchema.(sessionctx.InfoschemaMetaVersion); ok { + return is + } + } + if c.is == nil { + c.is = MockInfoschema(nil) + } + return c.is +} + +// MockInfoschema only serves for test. +var MockInfoschema func(tbList []*model.TableInfo) sessionctx.InfoschemaMetaVersion + +// GetDomainInfoSchema returns the latest information schema in domain +func (c *Context) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { + if c.is == nil { + c.is = MockInfoschema(nil) + } + return c.is +} + +// GetBuiltinFunctionUsage implements sessionctx.Context GetBuiltinFunctionUsage interface. +func (*Context) GetBuiltinFunctionUsage() map[string]uint32 { + return make(map[string]uint32) +} + +// BuiltinFunctionUsageInc implements sessionctx.Context. +func (*Context) BuiltinFunctionUsageInc(_ string) {} + +// GetGlobalSysVar implements GlobalVarAccessor GetGlobalSysVar interface. +func (*Context) GetGlobalSysVar(_ sessionctx.Context, name string) (string, error) { + v := variable.GetSysVar(name) + if v == nil { + return "", variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + return v.Value, nil +} + +// SetGlobalSysVar implements GlobalVarAccessor SetGlobalSysVar interface. +func (*Context) SetGlobalSysVar(_ sessionctx.Context, name string, value string) error { + v := variable.GetSysVar(name) + if v == nil { + return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) + } + v.Value = value + return nil +} + +// GetSessionPlanCache implements the sessionctx.Context interface. +func (c *Context) GetSessionPlanCache() sessionctx.PlanCache { + return c.pcache +} + +// NewTxn implements the sessionctx.Context interface. +func (c *Context) NewTxn(context.Context) error { + if c.Store == nil { + return errors.New("store is not set") + } + if c.txn.Valid() { + err := c.txn.Commit(c.ctx) + if err != nil { + return errors.Trace(err) + } + } + + txn, err := c.Store.Begin() + if err != nil { + return errors.Trace(err) + } + c.txn.Transaction = txn + return nil +} + +// NewStaleTxnWithStartTS implements the sessionctx.Context interface. +func (c *Context) NewStaleTxnWithStartTS(ctx context.Context, _ uint64) error { + return c.NewTxn(ctx) +} + +// RefreshTxnCtx implements the sessionctx.Context interface. +func (c *Context) RefreshTxnCtx(ctx context.Context) error { + return errors.Trace(c.NewTxn(ctx)) +} + +// RollbackTxn indicates an expected call of RollbackTxn. +func (c *Context) RollbackTxn(_ context.Context) { + defer c.sessionVars.SetInTxn(false) + if c.txn.Valid() { + terror.Log(c.txn.Rollback()) + } +} + +// CommitTxn indicates an expected call of CommitTxn. +func (c *Context) CommitTxn(ctx context.Context) error { + defer c.sessionVars.SetInTxn(false) + c.txn.SetDiskFullOpt(c.level) + if c.txn.Valid() { + return c.txn.Commit(ctx) + } + return nil +} + +// GetStore gets the store of session. +func (c *Context) GetStore() kv.Storage { + return c.Store +} + +// GetSessionManager implements the sessionctx.Context interface. +func (c *Context) GetSessionManager() util.SessionManager { + return c.sm +} + +// SetSessionManager set the session manager. +func (c *Context) SetSessionManager(sm util.SessionManager) { + c.sm = sm +} + +// Cancel implements the Session interface. +func (c *Context) Cancel() { + c.cancel() +} + +// GoCtx returns standard sessionctx.Context that bind with current transaction. +func (c *Context) GoCtx() context.Context { + return c.ctx +} + +// UpdateColStatsUsage updates the column stats usage. +func (*Context) UpdateColStatsUsage(_ []model.TableItemID) {} + +// StoreIndexUsage strores the index usage information. +func (*Context) StoreIndexUsage(_ int64, _ int64, _ int64) {} + +// GetTxnWriteThroughputSLI implements the sessionctx.Context interface. +func (*Context) GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI { + return &sli.TxnWriteThroughputSLI{} +} + +// StmtCommit implements the sessionctx.Context interface. +func (*Context) StmtCommit(context.Context) {} + +// StmtRollback implements the sessionctx.Context interface. +func (*Context) StmtRollback(context.Context, bool) {} + +// StmtGetMutation implements the sessionctx.Context interface. +func (*Context) StmtGetMutation(_ int64) *binlog.TableMutation { + return nil +} + +// AddTableLock implements the sessionctx.Context interface. +func (*Context) AddTableLock(_ []model.TableLockTpInfo) { +} + +// ReleaseTableLocks implements the sessionctx.Context interface. +func (*Context) ReleaseTableLocks(_ []model.TableLockTpInfo) { +} + +// ReleaseTableLockByTableIDs implements the sessionctx.Context interface. +func (*Context) ReleaseTableLockByTableIDs(_ []int64) { +} + +// CheckTableLocked implements the sessionctx.Context interface. +func (*Context) CheckTableLocked(_ int64) (bool, model.TableLockType) { + return false, model.TableLockNone +} + +// GetAllTableLocks implements the sessionctx.Context interface. +func (*Context) GetAllTableLocks() []model.TableLockTpInfo { + return nil +} + +// ReleaseAllTableLocks implements the sessionctx.Context interface. +func (*Context) ReleaseAllTableLocks() { +} + +// HasLockedTables implements the sessionctx.Context interface. +func (*Context) HasLockedTables() bool { + return false +} + +// PrepareTSFuture implements the sessionctx.Context interface. +func (c *Context) PrepareTSFuture(_ context.Context, future oracle.Future, _ string) error { + c.txn.Transaction = nil + c.txn.tsFuture = future + return nil +} + +// GetPreparedTxnFuture returns the TxnFuture if it is prepared. +// It returns nil otherwise. +func (c *Context) GetPreparedTxnFuture() sessionctx.TxnFuture { + if !c.txn.validOrPending() { + return nil + } + return &c.txn +} + +// GetStmtStats implements the sessionctx.Context interface. +func (*Context) GetStmtStats() *stmtstats.StatementStats { + return nil +} + +// GetAdvisoryLock acquires an advisory lock +func (*Context) GetAdvisoryLock(_ string, _ int64) error { + return nil +} + +// IsUsedAdvisoryLock check if a lock name is in use +func (*Context) IsUsedAdvisoryLock(_ string) uint64 { + return 0 +} + +// ReleaseAdvisoryLock releases an advisory lock +func (*Context) ReleaseAdvisoryLock(_ string) bool { + return true +} + +// ReleaseAllAdvisoryLocks releases all advisory locks +func (*Context) ReleaseAllAdvisoryLocks() int { + return 0 +} + +// EncodeSessionStates implements sessionctx.Context EncodeSessionStates interface. +func (*Context) EncodeSessionStates(context.Context, sessionctx.Context, *sessionstates.SessionStates) error { + return errors.Errorf("Not Supported") +} + +// DecodeSessionStates implements sessionctx.Context DecodeSessionStates interface. +func (*Context) DecodeSessionStates(context.Context, sessionctx.Context, *sessionstates.SessionStates) error { + return errors.Errorf("Not Supported") +} + +// GetExtensions returns the `*extension.SessionExtensions` object +func (*Context) GetExtensions() *extension.SessionExtensions { + return nil +} + +// EnableSandBoxMode enable the sandbox mode. +func (c *Context) EnableSandBoxMode() { + c.inSandBoxMode = true +} + +// DisableSandBoxMode enable the sandbox mode. +func (c *Context) DisableSandBoxMode() { + c.inSandBoxMode = false +} + +// InSandBoxMode indicates that this Session is in sandbox mode +func (c *Context) InSandBoxMode() bool { + return c.inSandBoxMode +} + +// Close implements the sessionctx.Context interface. +func (*Context) Close() {} + +// NewContext creates a new mocked sessionctx.Context. +func NewContext() *Context { + ctx, cancel := context.WithCancel(context.Background()) + sctx := &Context{ + values: make(map[fmt.Stringer]interface{}), + ctx: ctx, + cancel: cancel, + } + vars := variable.NewSessionVars(sctx) + sctx.sessionVars = vars + vars.InitChunkSize = 2 + vars.MaxChunkSize = 32 + vars.StmtCtx.SetTimeZone(time.UTC) + vars.MemTracker.SetBytesLimit(-1) + vars.DiskTracker.SetBytesLimit(-1) + vars.StmtCtx.MemTracker, vars.StmtCtx.DiskTracker = memory.NewTracker(-1, -1), disk.NewTracker(-1, -1) + vars.MemTracker.AttachTo(vars.MemTracker) + vars.DiskTracker.AttachTo(vars.DiskTracker) + vars.GlobalVarsAccessor = variable.NewMockGlobalAccessor() + vars.EnablePaging = variable.DefTiDBEnablePaging + vars.MinPagingSize = variable.DefMinPagingSize + vars.CostModelVersion = variable.DefTiDBCostModelVer + vars.EnableChunkRPC = true + if err := sctx.GetSessionVars().SetSystemVar(variable.MaxAllowedPacket, "67108864"); err != nil { + panic(err) + } + if err := sctx.GetSessionVars().SetSystemVar(variable.CharacterSetConnection, "utf8mb4"); err != nil { + panic(err) + } + return sctx +} + +// HookKeyForTest is as alias, used by context.WithValue. +// golint forbits using string type as key in context.WithValue. +type HookKeyForTest string diff --git a/util/mock/iter.go b/pkg/util/mock/iter.go similarity index 98% rename from util/mock/iter.go rename to pkg/util/mock/iter.go index b62a7158aeb05..8807567b3ea40 100644 --- a/util/mock/iter.go +++ b/pkg/util/mock/iter.go @@ -18,7 +18,7 @@ import ( "testing" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/assert" ) diff --git a/util/mock/iter_test.go b/pkg/util/mock/iter_test.go similarity index 98% rename from util/mock/iter_test.go rename to pkg/util/mock/iter_test.go index 88c531f2119be..befaddf3b7e8c 100644 --- a/util/mock/iter_test.go +++ b/pkg/util/mock/iter_test.go @@ -16,7 +16,7 @@ package mock import ( "testing" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" "github.com/stretchr/testify/assert" ) diff --git a/pkg/util/mock/main_test.go b/pkg/util/mock/main_test.go new file mode 100644 index 0000000000000..0bea359a75d0e --- /dev/null +++ b/pkg/util/mock/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/mock/metrics.go b/pkg/util/mock/metrics.go similarity index 100% rename from util/mock/metrics.go rename to pkg/util/mock/metrics.go diff --git a/util/mock/mock_test.go b/pkg/util/mock/mock_test.go similarity index 100% rename from util/mock/mock_test.go rename to pkg/util/mock/mock_test.go diff --git a/pkg/util/mock/store.go b/pkg/util/mock/store.go new file mode 100644 index 0000000000000..5bc684f5767e2 --- /dev/null +++ b/pkg/util/mock/store.go @@ -0,0 +1,87 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "context" + + deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/tidb/pkg/kv" + "github.com/tikv/client-go/v2/oracle" + "github.com/tikv/client-go/v2/tikv" +) + +// Store implements kv.Storage interface. +type Store struct { + Client kv.Client +} + +// GetClient implements kv.Storage interface. +func (s *Store) GetClient() kv.Client { return s.Client } + +// GetMPPClient implements kv.Storage interface. +func (*Store) GetMPPClient() kv.MPPClient { return nil } + +// GetOracle implements kv.Storage interface. +func (*Store) GetOracle() oracle.Oracle { return nil } + +// Begin implements kv.Storage interface. +func (*Store) Begin(_ ...tikv.TxnOption) (kv.Transaction, error) { return nil, nil } + +// GetSnapshot implements kv.Storage interface. +func (*Store) GetSnapshot(_ kv.Version) kv.Snapshot { return nil } + +// Close implements kv.Storage interface. +func (*Store) Close() error { return nil } + +// UUID implements kv.Storage interface. +func (*Store) UUID() string { return "mock" } + +// CurrentVersion implements kv.Storage interface. +func (*Store) CurrentVersion(_ string) (kv.Version, error) { return kv.Version{}, nil } + +// SupportDeleteRange implements kv.Storage interface. +func (*Store) SupportDeleteRange() bool { return false } + +// Name implements kv.Storage interface. +func (*Store) Name() string { return "UtilMockStorage" } + +// Describe implements kv.Storage interface. +func (*Store) Describe() string { + return "UtilMockStorage is a mock Store implementation, only for unittests in util package" +} + +// GetMemCache implements kv.Storage interface +func (*Store) GetMemCache() kv.MemManager { + return nil +} + +// ShowStatus implements kv.Storage interface. +func (*Store) ShowStatus(_ context.Context, _ string) (interface{}, error) { return nil, nil } + +// GetMinSafeTS implements kv.Storage interface. +func (*Store) GetMinSafeTS(_ string) uint64 { + return 0 +} + +// GetLockWaits implements kv.Storage interface. +func (*Store) GetLockWaits() ([]*deadlockpb.WaitForEntry, error) { + return nil, nil +} + +// GetCodec implements kv.Storage interface. +func (*Store) GetCodec() tikv.Codec { + return nil +} diff --git a/pkg/util/mvmap/BUILD.bazel b/pkg/util/mvmap/BUILD.bazel new file mode 100644 index 0000000000000..83144cce78b90 --- /dev/null +++ b/pkg/util/mvmap/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "mvmap", + srcs = [ + "fnv.go", + "mvmap.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/mvmap", + visibility = ["//visibility:public"], + deps = ["//pkg/util/mathutil"], +) + +go_test( + name = "mvmap_test", + timeout = "short", + srcs = [ + "bench_test.go", + "main_test.go", + "mvmap_test.go", + ], + embed = [":mvmap"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/mvmap/bench_test.go b/pkg/util/mvmap/bench_test.go similarity index 100% rename from util/mvmap/bench_test.go rename to pkg/util/mvmap/bench_test.go diff --git a/util/mvmap/fnv.go b/pkg/util/mvmap/fnv.go similarity index 100% rename from util/mvmap/fnv.go rename to pkg/util/mvmap/fnv.go diff --git a/pkg/util/mvmap/main_test.go b/pkg/util/mvmap/main_test.go new file mode 100644 index 0000000000000..7ba67dea80350 --- /dev/null +++ b/pkg/util/mvmap/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mvmap + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/mvmap/mvmap.go b/pkg/util/mvmap/mvmap.go similarity index 99% rename from util/mvmap/mvmap.go rename to pkg/util/mvmap/mvmap.go index 06a2646e52089..00ec73dc9baaf 100644 --- a/util/mvmap/mvmap.go +++ b/pkg/util/mvmap/mvmap.go @@ -17,7 +17,7 @@ package mvmap import ( "bytes" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" ) type entry struct { diff --git a/util/mvmap/mvmap_test.go b/pkg/util/mvmap/mvmap_test.go similarity index 100% rename from util/mvmap/mvmap_test.go rename to pkg/util/mvmap/mvmap_test.go diff --git a/pkg/util/nocopy/BUILD.bazel b/pkg/util/nocopy/BUILD.bazel new file mode 100644 index 0000000000000..2302693640d29 --- /dev/null +++ b/pkg/util/nocopy/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "nocopy", + srcs = ["nocopy.go"], + importpath = "github.com/pingcap/tidb/pkg/util/nocopy", + visibility = ["//visibility:public"], +) diff --git a/util/nocopy/nocopy.go b/pkg/util/nocopy/nocopy.go similarity index 100% rename from util/nocopy/nocopy.go rename to pkg/util/nocopy/nocopy.go diff --git a/pkg/util/paging/BUILD.bazel b/pkg/util/paging/BUILD.bazel new file mode 100644 index 0000000000000..c5a5a4d9ba5b2 --- /dev/null +++ b/pkg/util/paging/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "paging", + srcs = ["paging.go"], + importpath = "github.com/pingcap/tidb/pkg/util/paging", + visibility = ["//visibility:public"], +) + +go_test( + name = "paging_test", + timeout = "short", + srcs = [ + "main_test.go", + "paging_test.go", + ], + embed = [":paging"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/paging/main_test.go b/pkg/util/paging/main_test.go new file mode 100644 index 0000000000000..51c55f873da42 --- /dev/null +++ b/pkg/util/paging/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package paging + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/paging/paging.go b/pkg/util/paging/paging.go similarity index 100% rename from util/paging/paging.go rename to pkg/util/paging/paging.go diff --git a/util/paging/paging_test.go b/pkg/util/paging/paging_test.go similarity index 100% rename from util/paging/paging_test.go rename to pkg/util/paging/paging_test.go diff --git a/pkg/util/parser/BUILD.bazel b/pkg/util/parser/BUILD.bazel new file mode 100644 index 0000000000000..0f26f841e3ad8 --- /dev/null +++ b/pkg/util/parser/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "parser", + srcs = [ + "ast.go", + "parser.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/parser", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/parser/format", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "parser_test", + timeout = "short", + srcs = [ + "ast_test.go", + "main_test.go", + "parser_test.go", + ], + flaky = True, + deps = [ + ":parser", + "//pkg/parser", + "//pkg/testkit/testsetup", + "//pkg/types/parser_driver", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/parser/ast.go b/pkg/util/parser/ast.go new file mode 100644 index 0000000000000..3c6495d650d1b --- /dev/null +++ b/pkg/util/parser/ast.go @@ -0,0 +1,140 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package parser + +import ( + "strings" + + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" +) + +// GetDefaultDB checks if all tables in the AST have explicit DBName. If not, return specified DBName. +func GetDefaultDB(sel ast.StmtNode, dbName string) string { + implicitDB := &implicitDatabase{} + sel.Accept(implicitDB) + if implicitDB.hasImplicit { + return dbName + } + return "" +} + +type implicitDatabase struct { + hasImplicit bool +} + +func (i *implicitDatabase) Enter(in ast.Node) (out ast.Node, skipChildren bool) { + switch x := in.(type) { + case *ast.TableName: + if x.Schema.L == "" { + i.hasImplicit = true + } + return in, true + default: + return in, i.hasImplicit + } +} + +func (*implicitDatabase) Leave(in ast.Node) (out ast.Node, ok bool) { + return in, true +} + +func findTablePos(s, t string) int { + l := 0 + for i := range s { + if s[i] == ' ' || s[i] == ',' { + if len(t) == i-l && strings.Compare(s[l:i], t) == 0 { + return l + } + l = i + 1 + } + } + if len(t) == len(s)-l && strings.Compare(s[l:], t) == 0 { + return l + } + return -1 +} + +// SimpleCases captures simple SQL statements and uses string replacement instead of `restore` to improve performance. +// See https://github.com/pingcap/tidb/issues/22398. +func SimpleCases(node ast.StmtNode, defaultDB, origin string) (s string, ok bool) { + if len(origin) == 0 { + return "", false + } + insert, ok := node.(*ast.InsertStmt) + if !ok { + return "", false + } + if insert.Select != nil || insert.Setlist || insert.OnDuplicate != nil || (insert.TableHints != nil && len(insert.TableHints) != 0) { + return "", false + } + join := insert.Table.TableRefs + if join.Tp != 0 || join.Right != nil { + return "", false + } + ts, ok := join.Left.(*ast.TableSource) + if !ok { + return "", false + } + tn, ok := ts.Source.(*ast.TableName) + if !ok { + return "", false + } + parenPos := strings.Index(origin, "(") + if parenPos == -1 { + return "", false + } + if strings.Contains(origin[:parenPos], ".") { + return origin, true + } + lower := strings.ToLower(origin[:parenPos]) + pos := findTablePos(lower, tn.Name.L) + if pos == -1 { + return "", false + } + var builder strings.Builder + builder.WriteString(origin[:pos]) + if tn.Schema.String() != "" { + builder.WriteString(tn.Schema.String()) + } else { + builder.WriteString(defaultDB) + } + builder.WriteString(".") + builder.WriteString(origin[pos:]) + return builder.String(), true +} + +// RestoreWithDefaultDB returns restore strings for StmtNode with defaultDB +// This function is customized for SQL bind usage. +func RestoreWithDefaultDB(node ast.StmtNode, defaultDB, origin string) string { + if s, ok := SimpleCases(node, defaultDB, origin); ok { + return s + } + var sb strings.Builder + // Three flags for restore with default DB: + // 1. RestoreStringSingleQuotes specifies to use single quotes to surround the string; + // 2. RestoreSpacesAroundBinaryOperation specifies to add space around binary operation; + // 3. RestoreStringWithoutCharset specifies to not print charset before string; + // 4. RestoreNameBackQuotes specifies to use back quotes to surround the name; + ctx := format.NewRestoreCtx(format.RestoreStringSingleQuotes|format.RestoreSpacesAroundBinaryOperation|format.RestoreStringWithoutCharset|format.RestoreNameBackQuotes, &sb) + ctx.DefaultDB = defaultDB + if err := node.Restore(ctx); err != nil { + logutil.BgLogger().Debug("restore SQL failed", zap.String("category", "sql-bind"), zap.Error(err)) + return "" + } + return sb.String() +} diff --git a/util/parser/ast_test.go b/pkg/util/parser/ast_test.go similarity index 91% rename from util/parser/ast_test.go rename to pkg/util/parser/ast_test.go index 6c10efc3020b0..52b4524901df1 100644 --- a/util/parser/ast_test.go +++ b/pkg/util/parser/ast_test.go @@ -17,9 +17,9 @@ package parser_test import ( "testing" - "github.com/pingcap/tidb/parser" - _ "github.com/pingcap/tidb/types/parser_driver" - utilparser "github.com/pingcap/tidb/util/parser" + "github.com/pingcap/tidb/pkg/parser" + _ "github.com/pingcap/tidb/pkg/types/parser_driver" + utilparser "github.com/pingcap/tidb/pkg/util/parser" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/parser/main_test.go b/pkg/util/parser/main_test.go new file mode 100644 index 0000000000000..dc54105b04d67 --- /dev/null +++ b/pkg/util/parser/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package parser_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/parser/parser.go b/pkg/util/parser/parser.go similarity index 100% rename from util/parser/parser.go rename to pkg/util/parser/parser.go diff --git a/pkg/util/parser/parser_test.go b/pkg/util/parser/parser_test.go new file mode 100644 index 0000000000000..6e03ad51c20f4 --- /dev/null +++ b/pkg/util/parser/parser_test.go @@ -0,0 +1,170 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package parser_test + +import ( + "testing" + + utilparser "github.com/pingcap/tidb/pkg/util/parser" + "github.com/stretchr/testify/require" +) + +func TestSpace(t *testing.T) { + okTable := []struct { + Times int + Input string + Expected string + }{ + {0, " 1", "1"}, + {0, "1", "1"}, + {1, " 1", "1"}, + {2, " 1", "1"}, + } + for _, test := range okTable { + rest, err := utilparser.Space(test.Input, test.Times) + require.NoError(t, err) + require.Equal(t, test.Expected, rest) + } + + errTable := []struct { + Times int + Input string + }{ + {1, "1"}, + {2, " 1"}, + } + + for _, test := range errTable { + rest, err := utilparser.Space(test.Input, test.Times) + + require.NotNil(t, err) + require.Equal(t, test.Input, rest) + } +} + +func TestDigit(t *testing.T) { + okTable := []struct { + Times int + Input string + ExpectedDigits string + ExpectedRest string + }{ + {0, "123abc", "123", "abc"}, + {1, "123abc", "123", "abc"}, + {2, "123 @)@)", "123", " @)@)"}, + {3, "456 121", "456", " 121"}, + } + + for _, test := range okTable { + digits, rest, err := utilparser.Digit(test.Input, test.Times) + + require.NoError(t, err) + require.Equal(t, test.ExpectedDigits, digits) + require.Equal(t, test.ExpectedRest, rest) + } + + errTable := []struct { + Times int + Input string + }{ + {1, "int"}, + {2, "1int"}, + {3, "12 int"}, + } + + for _, test := range errTable { + digits, rest, err := utilparser.Digit(test.Input, test.Times) + + require.NotNil(t, err) + require.Equal(t, "", digits) + require.Equal(t, test.Input, rest) + } +} + +func TestNumber(t *testing.T) { + okTable := []struct { + Input string + ExpectedNum int + ExpectedRest string + }{ + {"123abc", 123, "abc"}, + {"123abc", 123, "abc"}, + {"123 @)@)", 123, " @)@)"}, + {"456 121", 456, " 121"}, + } + for _, test := range okTable { + digits, rest, err := utilparser.Number(test.Input) + + require.NoError(t, err) + require.Equal(t, test.ExpectedNum, digits) + require.Equal(t, test.ExpectedRest, rest) + } + + errTable := []struct { + Input string + }{ + {"int"}, + {"abcint"}, + {"@)@)int"}, + } + + for _, test := range errTable { + digits, rest, err := utilparser.Number(test.Input) + + require.NotNil(t, err) + require.Equal(t, 0, digits) + require.Equal(t, test.Input, rest) + } +} + +func TestCharAndAnyChar(t *testing.T) { + okTable := []struct { + Char byte + Input string + Expected string + }{ + {'i', "int", "nt"}, + {'1', "1int", "int"}, + {'1', "12 int", "2 int"}, + } + + for _, test := range okTable { + rest, err := utilparser.Char(test.Input, test.Char) + + require.NoError(t, err) + require.Equal(t, test.Expected, rest) + + rest, err = utilparser.AnyChar(test.Input) + + require.NoError(t, err) + require.Equal(t, test.Expected, rest) + } + + errTable := []struct { + Char byte + Input string + }{ + {'i', "xint"}, + {'1', "x1int"}, + {'1', "x12 int"}, + } + + for _, test := range errTable { + rest, err := utilparser.Char(test.Input, test.Char) + + require.NotNil(t, err) + require.Equal(t, test.Input, rest) + } +} diff --git a/pkg/util/password-validation/BUILD.bazel b/pkg/util/password-validation/BUILD.bazel new file mode 100644 index 0000000000000..54658302913e2 --- /dev/null +++ b/pkg/util/password-validation/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "password-validation", + srcs = ["password_validation.go"], + importpath = "github.com/pingcap/tidb/pkg/util/password-validation", + visibility = ["//visibility:public"], + deps = [ + "//pkg/sessionctx/variable", + "//pkg/util/hack", + ], +) + +go_test( + name = "password-validation_test", + timeout = "short", + srcs = ["password_validation_test.go"], + embed = [":password-validation"], + flaky = True, + deps = [ + "//pkg/parser/auth", + "//pkg/sessionctx/variable", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/password-validation/password_validation.go b/pkg/util/password-validation/password_validation.go similarity index 98% rename from util/password-validation/password_validation.go rename to pkg/util/password-validation/password_validation.go index 53ef1bb052d7c..86798ad217e6b 100644 --- a/util/password-validation/password_validation.go +++ b/pkg/util/password-validation/password_validation.go @@ -21,8 +21,8 @@ import ( "strings" "unicode" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/hack" ) const maxPwdValidationLength int = 100 diff --git a/util/password-validation/password_validation_test.go b/pkg/util/password-validation/password_validation_test.go similarity index 98% rename from util/password-validation/password_validation_test.go rename to pkg/util/password-validation/password_validation_test.go index 8a851b2006203..aff7be452a195 100644 --- a/util/password-validation/password_validation_test.go +++ b/pkg/util/password-validation/password_validation_test.go @@ -18,8 +18,8 @@ import ( "context" "testing" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/pdapi/BUILD.bazel b/pkg/util/pdapi/BUILD.bazel new file mode 100644 index 0000000000000..64859603979a5 --- /dev/null +++ b/pkg/util/pdapi/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "pdapi", + srcs = ["const.go"], + importpath = "github.com/pingcap/tidb/pkg/util/pdapi", + visibility = ["//visibility:public"], +) diff --git a/util/pdapi/const.go b/pkg/util/pdapi/const.go similarity index 100% rename from util/pdapi/const.go rename to pkg/util/pdapi/const.go diff --git a/pkg/util/plancache/BUILD.bazel b/pkg/util/plancache/BUILD.bazel new file mode 100644 index 0000000000000..2d729c3ac374a --- /dev/null +++ b/pkg/util/plancache/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "plancache", + srcs = ["util.go"], + importpath = "github.com/pingcap/tidb/pkg/util/plancache", + visibility = ["//visibility:public"], + deps = ["//pkg/types"], +) diff --git a/pkg/util/plancache/util.go b/pkg/util/plancache/util.go new file mode 100644 index 0000000000000..f0a202573ad3e --- /dev/null +++ b/pkg/util/plancache/util.go @@ -0,0 +1,36 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package util + +import ( + "github.com/pingcap/tidb/pkg/types" +) + +// PlanCacheMatchOpts store some property used to fetch plan from plan cache +// The structure set here is to avoid import cycle +type PlanCacheMatchOpts struct { + // paramTypes stores all parameters' FieldType, some different parameters may share same plan + ParamTypes []*types.FieldType + // limitOffsetAndCount stores all the offset and key parameters extract from limit statement + // only used for cache and pick plan with parameters in limit + LimitOffsetAndCount []uint64 + // HasSubQuery indicate whether this query has sub query + HasSubQuery bool + // StatsVersionHash is the hash value of the statistics version + StatsVersionHash uint64 + + // Below are some variables that can affect the plan + ForeignKeyChecks bool +} diff --git a/pkg/util/plancodec/BUILD.bazel b/pkg/util/plancodec/BUILD.bazel new file mode 100644 index 0000000000000..dc5ca3ec5564b --- /dev/null +++ b/pkg/util/plancodec/BUILD.bazel @@ -0,0 +1,41 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "plancodec", + srcs = [ + "binary_plan_decode.go", + "codec.go", + "id.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/plancodec", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/memory", + "//pkg/util/texttree", + "@com_github_golang_snappy//:snappy", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_tipb//go-tipb", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "plancodec_test", + timeout = "short", + srcs = [ + "codec_test.go", + "id_test.go", + "main_test.go", + ], + embed = [":plancodec"], + flaky = True, + deps = [ + "//pkg/kv", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/plancodec/binary_plan_decode.go b/pkg/util/plancodec/binary_plan_decode.go similarity index 99% rename from util/plancodec/binary_plan_decode.go rename to pkg/util/plancodec/binary_plan_decode.go index 439b48cabc860..636fe32f27b96 100644 --- a/util/plancodec/binary_plan_decode.go +++ b/pkg/util/plancodec/binary_plan_decode.go @@ -18,8 +18,8 @@ import ( "strconv" "strings" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/pingcap/tidb/pkg/util/texttree" "github.com/pingcap/tipb/go-tipb" ) diff --git a/pkg/util/plancodec/codec.go b/pkg/util/plancodec/codec.go new file mode 100644 index 0000000000000..f493ee060537d --- /dev/null +++ b/pkg/util/plancodec/codec.go @@ -0,0 +1,448 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package plancodec + +import ( + "bytes" + "encoding/base64" + "math" + "strconv" + "strings" + "sync" + + "github.com/golang/snappy" + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/texttree" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +const ( + rootTaskType = "0" + copTaskType = "1" +) + +const ( + idSeparator = "_" + lineBreaker = '\n' + lineBreakerStr = "\n" + separator = '\t' + separatorStr = "\t" +) + +var ( + // PlanDiscardedEncoded indicates the discard plan because it is too long + PlanDiscardedEncoded = "[discard]" + planDiscardedDecoded = "(plan discarded because too long)" + // BinaryPlanDiscardedEncoded is a special binary plan that represents it's discarded because of too long. + BinaryPlanDiscardedEncoded = func() string { + binary := &tipb.ExplainData{DiscardedDueToTooLong: true} + proto, err := binary.Marshal() + if err != nil { + return "" + } + return Compress(proto) + }() +) + +var decoderPool = sync.Pool{ + New: func() interface{} { + return &planDecoder{} + }, +} + +// DecodePlan use to decode the string to plan tree. +func DecodePlan(planString string) (res string, err error) { + defer func() { + if r := recover(); r != nil { + logutil.BgLogger().Error("DecodePlan panic", zap.Stack("stack"), zap.Any("recover", r)) + err = errors.New("DecodePlan panicked") + } + }() + if len(planString) == 0 { + return "", nil + } + pd := decoderPool.Get().(*planDecoder) + defer decoderPool.Put(pd) + pd.buf.Reset() + pd.addHeader = true + return pd.decode(planString) +} + +// DecodeNormalizedPlan decodes the string to plan tree. +func DecodeNormalizedPlan(planString string) (string, error) { + if len(planString) == 0 { + return "", nil + } + pd := decoderPool.Get().(*planDecoder) + defer decoderPool.Put(pd) + pd.buf.Reset() + pd.addHeader = false + return pd.buildPlanTree(planString) +} + +type planDecoder struct { + buf bytes.Buffer + depths []int + indents [][]rune + planInfos []*planInfo + addHeader bool + cacheParentIdent map[int]int +} + +type planInfo struct { + depth int + fields []string +} + +func (pd *planDecoder) decode(planString string) (string, error) { + b, err := decompress(planString) + if err != nil { + if planString == PlanDiscardedEncoded { + return planDiscardedDecoded, nil + } + return "", err + } + return pd.buildPlanTree(string(hack.String(b))) +} + +func (pd *planDecoder) buildPlanTree(planString string) (string, error) { + nodes := strings.Split(planString, lineBreakerStr) + if len(pd.depths) < len(nodes) { + pd.depths = make([]int, 0, len(nodes)) + pd.planInfos = make([]*planInfo, 0, len(nodes)) + pd.indents = make([][]rune, 0, len(nodes)) + } + pd.depths = pd.depths[:0] + pd.planInfos = pd.planInfos[:0] + for _, node := range nodes { + p, err := decodePlanInfo(node) + if err != nil { + return "", err + } + if p == nil { + continue + } + pd.planInfos = append(pd.planInfos, p) + pd.depths = append(pd.depths, p.depth) + } + + if pd.addHeader { + pd.addPlanHeader() + } + + // Calculated indentation of plans. + pd.initPlanTreeIndents() + pd.cacheParentIdent = make(map[int]int) + for i := 1; i < len(pd.depths); i++ { + parentIndex := pd.findParentIndex(i) + pd.fillIndent(parentIndex, i) + } + + // Align the value of plan fields. + pd.alignFields() + + for i, p := range pd.planInfos { + if i > 0 { + pd.buf.WriteByte(lineBreaker) + } + // This is for alignment. + pd.buf.WriteByte(separator) + pd.buf.WriteString(string(pd.indents[i])) + for j := 0; j < len(p.fields); j++ { + if j > 0 { + pd.buf.WriteByte(separator) + } + pd.buf.WriteString(p.fields[j]) + } + } + return pd.buf.String(), nil +} + +func (pd *planDecoder) addPlanHeader() { + if len(pd.planInfos) == 0 { + return + } + header := &planInfo{ + depth: 0, + fields: []string{"id", "task", "estRows", "operator info", "actRows", "execution info", "memory", "disk"}, + } + if len(pd.planInfos[0].fields) < len(header.fields) { + // plan without runtime information. + header.fields = header.fields[:len(pd.planInfos[0].fields)] + } + planInfos := make([]*planInfo, 0, len(pd.planInfos)+1) + depths := make([]int, 0, len(pd.planInfos)+1) + planInfos = append(planInfos, header) + planInfos = append(planInfos, pd.planInfos...) + depths = append(depths, header.depth) + depths = append(depths, pd.depths...) + pd.planInfos = planInfos + pd.depths = depths +} + +func (pd *planDecoder) initPlanTreeIndents() { + pd.indents = pd.indents[:0] + for i := 0; i < len(pd.depths); i++ { + indent := make([]rune, 2*pd.depths[i]) + pd.indents = append(pd.indents, indent) + if len(indent) == 0 { + continue + } + for i := 0; i < len(indent)-2; i++ { + indent[i] = ' ' + } + indent[len(indent)-2] = texttree.TreeLastNode + indent[len(indent)-1] = texttree.TreeNodeIdentifier + } +} + +func (pd *planDecoder) findParentIndex(childIndex int) int { + pd.cacheParentIdent[pd.depths[childIndex]] = childIndex + parentDepth := pd.depths[childIndex] - 1 + if parentIdx, ok := pd.cacheParentIdent[parentDepth]; ok { + return parentIdx + } + for i := childIndex - 1; i > 0; i-- { + if pd.depths[i] == parentDepth { + pd.cacheParentIdent[pd.depths[i]] = i + return i + } + } + return 0 +} + +func (pd *planDecoder) fillIndent(parentIndex, childIndex int) { + depth := pd.depths[childIndex] + if depth == 0 { + return + } + idx := depth*2 - 2 + for i := childIndex - 1; i > parentIndex; i-- { + if pd.indents[i][idx] == texttree.TreeLastNode { + pd.indents[i][idx] = texttree.TreeMiddleNode + break + } + pd.indents[i][idx] = texttree.TreeBody + } +} + +func (pd *planDecoder) alignFields() { + if len(pd.planInfos) == 0 { + return + } + // Align fields length. Some plan may doesn't have runtime info, need append `` to align with other plan fields. + maxLen := -1 + for _, p := range pd.planInfos { + if len(p.fields) > maxLen { + maxLen = len(p.fields) + } + } + for _, p := range pd.planInfos { + for len(p.fields) < maxLen { + p.fields = append(p.fields, "") + } + } + + fieldsLen := len(pd.planInfos[0].fields) + // Last field no need to align. + fieldsLen-- + var buf []byte + for colIdx := 0; colIdx < fieldsLen; colIdx++ { + maxFieldLen := pd.getMaxFieldLength(colIdx) + for rowIdx, p := range pd.planInfos { + fillLen := maxFieldLen - pd.getPlanFieldLen(rowIdx, colIdx, p) + for len(buf) < fillLen { + buf = append(buf, ' ') + } + buf = buf[:fillLen] + p.fields[colIdx] += string(buf) + } + } +} + +func (pd *planDecoder) getMaxFieldLength(idx int) int { + maxLength := -1 + for rowIdx, p := range pd.planInfos { + l := pd.getPlanFieldLen(rowIdx, idx, p) + if l > maxLength { + maxLength = l + } + } + return maxLength +} + +func (pd *planDecoder) getPlanFieldLen(rowIdx, colIdx int, p *planInfo) int { + if colIdx == 0 { + return len(p.fields[0]) + len(pd.indents[rowIdx]) + } + return len(p.fields[colIdx]) +} + +func decodePlanInfo(str string) (*planInfo, error) { + values := strings.Split(str, separatorStr) + if len(values) < 2 { + return nil, nil + } + + p := &planInfo{ + fields: make([]string, 0, len(values)-1), + } + for i, v := range values { + switch i { + // depth + case 0: + depth, err := strconv.Atoi(v) + if err != nil { + return nil, errors.Errorf("decode plan: %v, depth: %v, error: %v", str, v, err) + } + p.depth = depth + // plan ID + case 1: + ids := strings.Split(v, idSeparator) + if len(ids) != 1 && len(ids) != 2 { + return nil, errors.Errorf("decode plan: %v error, invalid plan id: %v", str, v) + } + planID, err := strconv.Atoi(ids[0]) + if err != nil { + return nil, errors.Errorf("decode plan: %v, plan id: %v, error: %v", str, v, err) + } + if len(ids) == 1 { + p.fields = append(p.fields, PhysicalIDToTypeString(planID)) + } else { + p.fields = append(p.fields, PhysicalIDToTypeString(planID)+idSeparator+ids[1]) + } + // task type + case 2: + task, err := decodeTaskType(v) + if err != nil { + return nil, errors.Errorf("decode plan: %v, task type: %v, error: %v", str, v, err) + } + p.fields = append(p.fields, task) + default: + p.fields = append(p.fields, v) + } + } + return p, nil +} + +// EncodePlanNode is used to encode the plan to a string. +func EncodePlanNode(depth int, pid, planType string, rowCount float64, + taskTypeInfo, explainInfo, actRows, analyzeInfo, memoryInfo, diskInfo string, buf *bytes.Buffer) { + explainInfo = escapeString(explainInfo) + buf.WriteString(strconv.Itoa(depth)) + buf.WriteByte(separator) + buf.WriteString(encodeID(planType, pid)) + buf.WriteByte(separator) + buf.WriteString(taskTypeInfo) + buf.WriteByte(separator) + if math.Round(rowCount) == rowCount { + buf.WriteString(strconv.FormatFloat(rowCount, 'f', 0, 64)) + } else { + buf.WriteString(strconv.FormatFloat(rowCount, 'f', 2, 64)) + } + buf.WriteByte(separator) + buf.WriteString(explainInfo) + // Check whether has runtime info. + if len(actRows) > 0 || len(analyzeInfo) > 0 || len(memoryInfo) > 0 || len(diskInfo) > 0 { + buf.WriteByte(separator) + buf.WriteString(actRows) + buf.WriteByte(separator) + buf.WriteString(analyzeInfo) + buf.WriteByte(separator) + buf.WriteString(memoryInfo) + buf.WriteByte(separator) + buf.WriteString(diskInfo) + } + buf.WriteByte(lineBreaker) +} + +func escapeString(s string) string { + s = strings.ReplaceAll(s, string([]byte{separator}), "\\t") + return strings.ReplaceAll(s, string([]byte{lineBreaker}), "\\n") +} + +// NormalizePlanNode is used to normalize the plan to a string. +func NormalizePlanNode(depth int, planType string, taskTypeInfo string, explainInfo string, buf *bytes.Buffer) { + buf.WriteString(strconv.Itoa(depth)) + buf.WriteByte(separator) + planID := TypeStringToPhysicalID(planType) + buf.WriteString(strconv.Itoa(planID)) + buf.WriteByte(separator) + buf.WriteString(taskTypeInfo) + buf.WriteByte(separator) + buf.WriteString(explainInfo) + buf.WriteByte(lineBreaker) +} + +func encodeID(planType, id string) string { + planID := TypeStringToPhysicalID(planType) + return strconv.Itoa(planID) + idSeparator + id +} + +// EncodeTaskType is used to encode task type to a string. +func EncodeTaskType(isRoot bool, storeType kv.StoreType) string { + if isRoot { + return rootTaskType + } + return copTaskType + idSeparator + strconv.Itoa((int)(storeType)) +} + +// EncodeTaskTypeForNormalize is used to encode task type to a string. Only use for normalize plan. +func EncodeTaskTypeForNormalize(isRoot bool, storeType kv.StoreType) string { + if isRoot { + return rootTaskType + } else if storeType == kv.TiKV { + return copTaskType + } + return copTaskType + idSeparator + strconv.Itoa((int)(storeType)) +} + +func decodeTaskType(str string) (string, error) { + segs := strings.Split(str, idSeparator) + if segs[0] == rootTaskType { + return "root", nil + } + if len(segs) == 1 { // be compatible to `NormalizePlanNode`, which doesn't encode storeType in task field. + return "cop", nil + } + storeType, err := strconv.Atoi(segs[1]) + if err != nil { + return "", err + } + return "cop[" + ((kv.StoreType)(storeType)).Name() + "]", nil +} + +// Compress compresses the input with snappy then encodes it with base64. +func Compress(input []byte) string { + compressBytes := snappy.Encode(nil, input) + return base64.StdEncoding.EncodeToString(compressBytes) +} + +func decompress(str string) ([]byte, error) { + decodeBytes, err := base64.StdEncoding.DecodeString(str) + if err != nil { + return nil, err + } + + bs, err := snappy.Decode(nil, decodeBytes) + if err != nil { + return nil, err + } + return bs, nil +} diff --git a/pkg/util/plancodec/codec_test.go b/pkg/util/plancodec/codec_test.go new file mode 100644 index 0000000000000..d9ea8ea69918a --- /dev/null +++ b/pkg/util/plancodec/codec_test.go @@ -0,0 +1,57 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package plancodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/stretchr/testify/require" +) + +type encodeTaskTypeCase struct { + IsRoot bool + StoreType kv.StoreType + EncodedStr string + DecodedStr string +} + +func TestEncodeTaskType(t *testing.T) { + cases := []encodeTaskTypeCase{ + {true, kv.UnSpecified, "0", "root"}, + {false, kv.TiKV, "1_0", "cop[tikv]"}, + {false, kv.TiFlash, "1_1", "cop[tiflash]"}, + {false, kv.TiDB, "1_2", "cop[tidb]"}, + } + for _, cas := range cases { + require.Equal(t, cas.EncodedStr, EncodeTaskType(cas.IsRoot, cas.StoreType)) + str, err := decodeTaskType(cas.EncodedStr) + require.NoError(t, err) + require.Equal(t, cas.DecodedStr, str) + } + + str, err := decodeTaskType("1") + require.NoError(t, err) + require.Equal(t, "cop", str) + + _, err = decodeTaskType("1_x") + require.Error(t, err) +} + +func TestDecodeDiscardPlan(t *testing.T) { + plan, err := DecodePlan(PlanDiscardedEncoded) + require.NoError(t, err) + require.Equal(t, planDiscardedDecoded, plan) +} diff --git a/util/plancodec/id.go b/pkg/util/plancodec/id.go similarity index 100% rename from util/plancodec/id.go rename to pkg/util/plancodec/id.go diff --git a/util/plancodec/id_test.go b/pkg/util/plancodec/id_test.go similarity index 100% rename from util/plancodec/id_test.go rename to pkg/util/plancodec/id_test.go diff --git a/pkg/util/plancodec/main_test.go b/pkg/util/plancodec/main_test.go new file mode 100644 index 0000000000000..8dc97a2e08e21 --- /dev/null +++ b/pkg/util/plancodec/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package plancodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/prefix_helper.go b/pkg/util/prefix_helper.go similarity index 98% rename from util/prefix_helper.go rename to pkg/util/prefix_helper.go index 9d3fcdd476a64..2e9e3db5924b6 100644 --- a/util/prefix_helper.go +++ b/pkg/util/prefix_helper.go @@ -22,7 +22,7 @@ import ( "bytes" "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" + "github.com/pingcap/tidb/pkg/kv" ) // ScanMetaWithPrefix scans metadata with the prefix. diff --git a/util/prefix_helper_test.go b/pkg/util/prefix_helper_test.go similarity index 95% rename from util/prefix_helper_test.go rename to pkg/util/prefix_helper_test.go index 4f44d487b019c..f4c9c17bbf34f 100644 --- a/util/prefix_helper_test.go +++ b/pkg/util/prefix_helper_test.go @@ -19,10 +19,10 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/util/printer.go b/pkg/util/printer.go similarity index 100% rename from util/printer.go rename to pkg/util/printer.go diff --git a/pkg/util/printer/BUILD.bazel b/pkg/util/printer/BUILD.bazel new file mode 100644 index 0000000000000..b5d8f78a52cf5 --- /dev/null +++ b/pkg/util/printer/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "printer", + srcs = ["printer.go"], + importpath = "github.com/pingcap/tidb/pkg/util/printer", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/parser/mysql", + "//pkg/util/israce", + "//pkg/util/logutil", + "//pkg/util/versioninfo", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "printer_test", + timeout = "short", + srcs = [ + "main_test.go", + "printer_test.go", + ], + embed = [":printer"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/printer/main_test.go b/pkg/util/printer/main_test.go new file mode 100644 index 0000000000000..773220ce96303 --- /dev/null +++ b/pkg/util/printer/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package printer + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/printer/printer.go b/pkg/util/printer/printer.go similarity index 95% rename from util/printer/printer.go rename to pkg/util/printer/printer.go index e9bcc1e6852d0..495053475e4c2 100644 --- a/util/printer/printer.go +++ b/pkg/util/printer/printer.go @@ -21,11 +21,11 @@ import ( _ "runtime" // import link package _ "unsafe" // required by go:linkname - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/israce" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/versioninfo" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/israce" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/versioninfo" "go.uber.org/zap" ) diff --git a/util/printer/printer_test.go b/pkg/util/printer/printer_test.go similarity index 100% rename from util/printer/printer_test.go rename to pkg/util/printer/printer_test.go diff --git a/util/processinfo.go b/pkg/util/processinfo.go similarity index 95% rename from util/processinfo.go rename to pkg/util/processinfo.go index 964d8cadd7859..3107e095e0380 100644 --- a/util/processinfo.go +++ b/pkg/util/processinfo.go @@ -21,13 +21,13 @@ import ( "strings" "time" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/disk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/memory" "github.com/tikv/client-go/v2/oracle" ) diff --git a/pkg/util/profile/BUILD.bazel b/pkg/util/profile/BUILD.bazel new file mode 100644 index 0000000000000..6c1c440989851 --- /dev/null +++ b/pkg/util/profile/BUILD.bazel @@ -0,0 +1,45 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "profile", + srcs = [ + "flamegraph.go", + "profile.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/profile", + visibility = ["//visibility:public"], + deps = [ + "//pkg/types", + "//pkg/util/cpuprofile", + "//pkg/util/texttree", + "@com_github_google_pprof//profile", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "profile_test", + timeout = "short", + srcs = [ + "flamegraph_test.go", + "main_test.go", + "profile_test.go", + ], + data = glob(["testdata/**"]), + embed = [":profile"], + flaky = True, + deps = [ + "//pkg/domain", + "//pkg/kv", + "//pkg/session", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/profile/flamegraph.go b/pkg/util/profile/flamegraph.go similarity index 98% rename from util/profile/flamegraph.go rename to pkg/util/profile/flamegraph.go index ea6202d8996a6..d4de6c33d8d48 100644 --- a/util/profile/flamegraph.go +++ b/pkg/util/profile/flamegraph.go @@ -21,8 +21,8 @@ import ( "slices" "github.com/google/pprof/profile" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/texttree" ) type flamegraphNode struct { diff --git a/util/profile/flamegraph_test.go b/pkg/util/profile/flamegraph_test.go similarity index 98% rename from util/profile/flamegraph_test.go rename to pkg/util/profile/flamegraph_test.go index add383d199cab..02cb46f8859a5 100644 --- a/util/profile/flamegraph_test.go +++ b/pkg/util/profile/flamegraph_test.go @@ -19,8 +19,8 @@ import ( "os" "testing" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" diff --git a/pkg/util/profile/main_test.go b/pkg/util/profile/main_test.go new file mode 100644 index 0000000000000..bf6400442e635 --- /dev/null +++ b/pkg/util/profile/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package profile + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/profile/profile.go b/pkg/util/profile/profile.go similarity index 97% rename from util/profile/profile.go rename to pkg/util/profile/profile.go index 686b08c31499d..a9e347b235d57 100644 --- a/util/profile/profile.go +++ b/pkg/util/profile/profile.go @@ -24,9 +24,9 @@ import ( "github.com/google/pprof/profile" "github.com/pingcap/errors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/texttree" ) // CPUProfileInterval represents the duration of sampling CPU diff --git a/util/profile/profile_test.go b/pkg/util/profile/profile_test.go similarity index 87% rename from util/profile/profile_test.go rename to pkg/util/profile/profile_test.go index 3009f801401ae..6d8550f33e658 100644 --- a/util/profile/profile_test.go +++ b/pkg/util/profile/profile_test.go @@ -18,12 +18,12 @@ import ( "testing" "time" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/profile" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/profile" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/util/profile/testdata/test.pprof b/pkg/util/profile/testdata/test.pprof similarity index 100% rename from util/profile/testdata/test.pprof rename to pkg/util/profile/testdata/test.pprof diff --git a/pkg/util/promutil/BUILD.bazel b/pkg/util/promutil/BUILD.bazel new file mode 100644 index 0000000000000..f720104a1a1f4 --- /dev/null +++ b/pkg/util/promutil/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "promutil", + srcs = [ + "factory.go", + "registry.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/promutil", + visibility = ["//visibility:public"], + deps = ["@com_github_prometheus_client_golang//prometheus"], +) + +go_test( + name = "promutil_test", + timeout = "short", + srcs = ["registry_test.go"], + embed = [":promutil"], + flaky = True, + deps = [ + "@com_github_prometheus_client_golang//prometheus", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/promutil/factory.go b/pkg/util/promutil/factory.go similarity index 100% rename from util/promutil/factory.go rename to pkg/util/promutil/factory.go diff --git a/util/promutil/registry.go b/pkg/util/promutil/registry.go similarity index 100% rename from util/promutil/registry.go rename to pkg/util/promutil/registry.go diff --git a/util/promutil/registry_test.go b/pkg/util/promutil/registry_test.go similarity index 100% rename from util/promutil/registry_test.go rename to pkg/util/promutil/registry_test.go diff --git a/pkg/util/ranger/BUILD.bazel b/pkg/util/ranger/BUILD.bazel new file mode 100644 index 0000000000000..b0e45e7cfca1d --- /dev/null +++ b/pkg/util/ranger/BUILD.bazel @@ -0,0 +1,67 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "ranger", + srcs = [ + "checker.go", + "detacher.go", + "points.go", + "ranger.go", + "types.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/ranger", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/ast", + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/util/fixcontrol", + "//pkg/sessionctx", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/types/parser_driver", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "//pkg/util/dbterror", + "//pkg/util/mathutil", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "ranger_test", + timeout = "short", + srcs = [ + "bench_test.go", + "main_test.go", + "ranger_test.go", + "types_test.go", + ], + flaky = True, + shard_count = 26, + deps = [ + ":ranger", + "//pkg/config", + "//pkg/domain", + "//pkg/expression", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/collate", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/ranger/bench_test.go b/pkg/util/ranger/bench_test.go new file mode 100644 index 0000000000000..69deaab02ddbb --- /dev/null +++ b/pkg/util/ranger/bench_test.go @@ -0,0 +1,133 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package ranger_test + +import ( + "context" + "testing" + + "github.com/pingcap/tidb/pkg/expression" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/ranger" + "github.com/stretchr/testify/require" +) + +func BenchmarkDetachCondAndBuildRangeForIndex(b *testing.B) { + store := testkit.CreateMockStore(b) + testKit := testkit.NewTestKit(b, store) + testKit.MustExec("USE test") + testKit.MustExec("DROP TABLE IF EXISTS t") + testKit.MustExec(` +CREATE TABLE t ( + id bigint(20) NOT NULL, + flag tinyint(1) NOT NULL, + start_time bigint(20) NOT NULL, + end_time bigint(20) NOT NULL, + org_id bigint(20) NOT NULL, + work_type varchar(20) CHARACTER SET latin1 DEFAULT NULL, + KEY idx (work_type, flag, org_id, end_time) +)`) + const longInListQuery = ` +SELECT + * +FROM + test.t +WHERE + flag = false + AND work_type = 'ART' + AND start_time < 1437233819011 + AND org_id IN ( + 6914445279216,13370400597894,23573357446410,25887731612857,47914138043476,55587486395595,81718641401629,116900287316705,145561135354847,146149695520705,150737101640717, + 156461412195734,159282589442914,172009309011258,177247737846954,186431984199126,192702036012003,198028383844036,203822128217766,204361899227892,222307981616040, + 223676791310456,239710781952147,284504079630201,293577747630605,294033490212963,295616321229667,296595862564733,306758307660237,312453546107536,341725138386801, + 365564508529854,367823101074613,372136375994705,379986503803645,391811651805728,400703265358976,402101198418969,418909618067022,431707392441212,433127041410999, + 453871162499705,459423393488794,468411967568789,470295426508062,474431880357867,486212302560292,491505628336549,501258676986205,512885499269076,541562269064881, + 573472004072939,588733301257768,615894236361514,615983097695492,632401842501612,647142957465854,718424118160213,727429900786891,730535283550760,738198024914914, + 739163331366841,752647396960214,756654620675819,771528730062551,780002237988119,819197371932508,841300462416232,850412457085478,874244695288775,881570046324012, + 888197716219703,891639557310370,891974587948562,894056936863746,896673560435518,907536217395121,911688951561329,915825484067466,955508673412475,970655461426927, + 972378619997200,995940920336141,1001616070548723,1002328390838858,1008841511217759,1027157044666161,1048868628313964,1066571897813670,1077847212985966,1080168008312256, + 1096474319900512,1102806753403857,1117700255389200,1121170709282654,1160894105829436,1173396752247253,1174823990738043,1176756778119821,1183639496028232,1224377491431012, + 1240466389734677,1265076660322902,1269569491282720,1272581574031499,1294849094933502,1295099688484884,1298881176988511,1299992642087679,1307669929489952,1338074385647926, + 1342415156765380,1360218717881999,1377658957083475,1392922543432192,1407444098594050,1438256495838735,1445134088155147,1486929584894557,1494681055271250,1500940354882616, + 1530516421560327,1532590570974295,1544648947254643,1561923580960451,1563587565476473,1565067823935596,1573069534228327,1573167213450271,1573297960637648,1577324450386436, + 1595234199645128,1595320706602899,1595934401297767,1616085587597345,1652295812716667,1655495487920136,1663475672388133,1668352817492466,1681094348275341,1689623403182214, + 1701682724927093,1705012823832699,1710393138044599,1716535649128474,1733575964270463,1750190609804974,1754580077690816,1756061776687456,1758058273255859,1775158332937577, + 1786728287430927,1816461420376899,1828580334315536,1858008005313000,1878841219054602,1878932921719554,1904081347331116,1904820184794904,1913069596895373,1941380005857044, + 1954836070968071,1955618820347782,1983296066429676,1987819713385690,1998098943250021,2013403425656222,2026046763398088,2026621786756595,2042249014990205,2046741004470190, + 2054370107316514,2082578854015062,2087183461591329,2087192311688265,2095277921868705,2103249621417272,2105159282073449,2106385538583803,2115025577188264,2115892671475192, + 2144855844328126,2145526421460724,2155282047243675,2170620433275766,2189596848694026,2194468311513858,2213049505974092,2213713514793282,2213911591088204,2215904332099994, + 2220235232411590,2226449138693468,2281727415070895,2288760906462231,2292201263531973,2292800860074434,2305015986098173,2311091146951322,2345959699274993,2356192386220089, + 2358508761766515,2372253813770528,2372994254274287,2374279183118353,2382395547546489,2397593929551113,2414904392525386,2416320859150803,2432407185506251,2439133858011514, + 2456909988876966,2490057196713078,2491000908839300,2501843499019962,2539856169115632,2542691236689315,2554165079560216,2565016421484028,2579373020942520,2583310536030502, + 2601385761450563,2603243552830302,2609138752573551,2628285706286347,2631734058797888,2638342575912817,2640167419150776,2651402302096646,2652627728219962,2677347449018607, + 2680209147172480,2683108753662485,2697695717735514,2699481485241986,2706019556864874,2707225343321107,2707841703322085,2724535386459144,2751805187614069,2759827465036125, + 2761426575202406,2768997857008184,2780782950787373,2789872834409453,2801402100587551,2806075464399632,2811893029385689,2816433481597910,2817217705468440,2817327738406355, + 2834191662385443,2838661299874842,2841835162527294,2842790844846179,2873548708258327,2879581389559553,2881798195775557,2943715564248539,2951224334569493,2958397742216527, + 2962117760574537,2977264272542363,2982743447396379,3009349759215772,3010012117130847,3010096874529870,3010115852485268,3012535704934837,3019483201458596,3036314602733381, + 3036837746136599,3039514679368453,3052506678397436,3059152178913030,3061095276596365,3063233426258797,3085115838323303,3093308450248420,3101208987984055,3106059275305341, + 3107559394454149,3110050933644367,3138545183162445,3140678914029638,3142109724205254,3142527081183238,3160670144726081,3160829106050702,3172902656372574,3181996104936476, + 3185563316673259,3189751274857877,3211729550313903,3213599609783958,3213659878256809,3215918533522245,3225137047012900,3231023124763183,3237856608607317,3247879729114765, + 3253596244997212,3256221808530784,3259756319203329,3260467132021662,3270706364495298,3273179837457878,3281263986182695,3296686215236784,3315584830022847,3317781471030011, + 3338120669693067,3342373672540130,3348941534532661,3359715878023917,3383970565360566,3397868551281252,3428808017724584,3449238661063482,3450439330534765,3471286956110444, + 3472931370488350,3473088266701087,3474445823605231,3477506057124755,3477541077050002,3488929919781737,3502330373943603,3504844077112105,3508329172255490,3510690524473209, + 3512324709745663,3514007754921924,3558011694836438,3559330712604565,3562145253615132,3581148885399840,3591312468185522,3597256243542091,3618160287855458,3625686898155792, + 3650194338715713,3650610878210125,3668345904483321,3668494673430762,3677776463522008,3694233081600122,3709632134619598,3725419513943971,3730099951927002,3734961839633730, + 3788380331922768,3811246425255446,3815582365292463,3829203122180282,3847292141308076,3877507310017402,3882136043994493,3887872593644033,3891280433757250,3924114035682754, + 3930635798027692,3940634174809349,3955900287161214,3957844806020309,3973236219509940,3981294421878412,3984846206013660,3993802859865565,3996764828980278,4023461371356880, + 4030830455174294,4032434581680299,4041625011713530,4041957068079946,4058955264781991,4083680454731905,4095581705542542,4112420671677445,4121292361441010,4133226631387396, + 4139004365572538,4161250756754756,4163706760683594,4170471653379067,4171134267004311,4177796537235481,4180802682160942,4203191696595400,4209464578955045,4223422057959415, + 4237541104444937,4257691911774311,4260020795088571,4301574030764989,4301922471400280,4304478206038048,4314941265701364,4320330498423583,4339739390410992,4342413486284428, + 4347136230283432,4351145740656078,4358837874704787,4362622126951624,4364582223851552,4366497646764759,4385815379876479,4406431382404050,4409339407622327,4411432076559821, + 4449849757340102,4453892102562139,4466153465045159,4479272299804907,4486938493241801,4500505590495671,4502678993390350,4508608408879950,4515778758013390,4516708986545589, + 4534650929880461,4537213242644302,4537280160911816,4544757774741059,4544796825985803,4567299173366450,4586846032456054,4607174945068672,4613242210966642,4625035792839901, + 4634756593700865,4635123568273790,4659875963875224,4670322637424975,4683035442855866,4689033135620199,4714163851388857,4714214015125119,4744848633979578,4750937375207328, + 4776805289846989,4804031931975645,4837280540915990,4840957238353452,4868526553354967,4875063418864529,4909882543513230,4912268119820614,4919123728900332,4929754602909549, + 4941072993543698,4941174020949904,4948032640819331,4955057670206957,4989823480030237,4990195550280933,5011619499820208,5013143564325843,5016786248387969,5019292677276101, + 5045230878000223,5053158166772953,5058611677018883,5083565032770599,5100847523417394,5105223137691724,5116076462386020,5117104556161083,5137323839372187,5159591155175114, + 5174145884534911,5182009696238822,5207529188216676,5229363397364492,5231081768308950,5233170527517877,5238337785560206,5259149223152958,5259347545688076,5265284989590946, + 5288711160650011,5297955243309025,5300535720011230,5311590629866593,5313248950470856,5313960220028487,5321218075331795,5334031591400346,5340934779949776,5380276587818617, + 5380604989545853,5392427172834832,5419648071490001,5436430269421440,5438720576124743,5442272167466546,5443531545450195,5462404261617760,5484761325677647 + ) +` + sctx := testKit.Session().(sessionctx.Context) + stmts, err := session.Parse(sctx, longInListQuery) + require.NoError(b, err) + require.Len(b, stmts, 1) + ret := &plannercore.PreprocessorReturn{} + err = plannercore.Preprocess(context.Background(), sctx, stmts[0], plannercore.WithPreprocessorReturn(ret)) + require.NoError(b, err) + ctx := context.Background() + p, _, err := plannercore.BuildLogicalPlanForTest(ctx, sctx, stmts[0], ret.InfoSchema) + require.NoError(b, err) + selection := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) + tbl := selection.Children()[0].(*plannercore.DataSource).TableInfo() + require.NotNil(b, selection) + conds := make([]expression.Expression, len(selection.Conditions)) + for i, cond := range selection.Conditions { + conds[i] = expression.PushDownNot(sctx, cond) + } + cols, lengths := expression.IndexInfo2PrefixCols(tbl.Columns, selection.Schema().Columns, tbl.Indices[0]) + require.NotNil(b, cols) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err = ranger.DetachCondAndBuildRangeForIndex(sctx, conds, cols, lengths, 0) + require.NoError(b, err) + } + b.StopTimer() +} diff --git a/pkg/util/ranger/checker.go b/pkg/util/ranger/checker.go new file mode 100644 index 0000000000000..7d769a029b2fd --- /dev/null +++ b/pkg/util/ranger/checker.go @@ -0,0 +1,222 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package ranger + +import ( + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" +) + +// conditionChecker checks if this condition can be pushed to index planner. +type conditionChecker struct { + checkerCol *expression.Column + length int + optPrefixIndexSingleScan bool +} + +func (c *conditionChecker) isFullLengthColumn() bool { + return c.length == types.UnspecifiedLength || c.length == c.checkerCol.GetType().GetFlen() +} + +// check returns two values, isAccessCond and shouldReserve. +// isAccessCond indicates whether the condition can be used to build ranges. +// shouldReserve indicates whether the condition should be reserved in filter conditions. +func (c *conditionChecker) check(condition expression.Expression) (isAccessCond, shouldReserve bool) { + switch x := condition.(type) { + case *expression.ScalarFunction: + return c.checkScalarFunction(x) + case *expression.Column: + if x.RetType.EvalType() == types.ETString { + return false, true + } + return c.checkColumn(x) + case *expression.Constant: + return true, false + } + return false, true +} + +func (c *conditionChecker) checkScalarFunction(scalar *expression.ScalarFunction) (isAccessCond, shouldReserve bool) { + _, collation := scalar.CharsetAndCollation() + switch scalar.FuncName.L { + case ast.LogicOr, ast.LogicAnd: + isAccessCond0, shouldReserve0 := c.check(scalar.GetArgs()[0]) + isAccessCond1, shouldReserve1 := c.check(scalar.GetArgs()[1]) + if isAccessCond0 && isAccessCond1 { + return true, shouldReserve0 || shouldReserve1 + } + return false, true + case ast.EQ, ast.NE, ast.GE, ast.GT, ast.LE, ast.LT, ast.NullEQ: + if _, ok := scalar.GetArgs()[0].(*expression.Constant); ok { + if c.matchColumn(scalar.GetArgs()[1]) { + // Checks whether the scalar function is calculated use the collation compatible with the column. + if scalar.GetArgs()[1].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[1].GetType().GetCollate(), collation) { + return false, true + } + isFullLength := c.isFullLengthColumn() + if scalar.FuncName.L == ast.NE { + return isFullLength, !isFullLength + } + return true, !isFullLength + } + } + if _, ok := scalar.GetArgs()[1].(*expression.Constant); ok { + if c.matchColumn(scalar.GetArgs()[0]) { + // Checks whether the scalar function is calculated use the collation compatible with the column. + if scalar.GetArgs()[0].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { + return false, true + } + isFullLength := c.isFullLengthColumn() + if scalar.FuncName.L == ast.NE { + return isFullLength, !isFullLength + } + return true, !isFullLength + } + } + case ast.IsNull: + if c.matchColumn(scalar.GetArgs()[0]) { + var isNullReserve bool // We can know whether the column is null from prefix column of any length. + if !c.optPrefixIndexSingleScan { + isNullReserve = !c.isFullLengthColumn() + } + return true, isNullReserve + } + return false, true + case ast.IsTruthWithoutNull, ast.IsFalsity, ast.IsTruthWithNull: + if s, ok := scalar.GetArgs()[0].(*expression.Column); ok { + if s.RetType.EvalType() == types.ETString { + return false, true + } + } + return c.checkColumn(scalar.GetArgs()[0]) + case ast.UnaryNot: + // TODO: support "not like" convert to access conditions. + s, ok := scalar.GetArgs()[0].(*expression.ScalarFunction) + if !ok { + // "not column" or "not constant" can't lead to a range. + return false, true + } + if s.FuncName.L == ast.Like || s.FuncName.L == ast.NullEQ { + return false, true + } + return c.check(scalar.GetArgs()[0]) + case ast.In: + if !c.matchColumn(scalar.GetArgs()[0]) { + return false, true + } + if scalar.GetArgs()[0].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { + return false, true + } + for _, v := range scalar.GetArgs()[1:] { + if _, ok := v.(*expression.Constant); !ok { + return false, true + } + } + return true, !c.isFullLengthColumn() + case ast.Like: + return c.checkLikeFunc(scalar) + case ast.GetParam: + // TODO + return true, false + } + return false, true +} + +func (c *conditionChecker) checkLikeFunc(scalar *expression.ScalarFunction) (isAccessCond, shouldReserve bool) { + _, collation := scalar.CharsetAndCollation() + if collate.NewCollationEnabled() && !collate.IsBinCollation(collation) { + // The algorithm constructs the range in byte-level: for example, ab% is mapped to [ab, ac] by adding 1 to the last byte. + // However, this is incorrect for non-binary collation strings because the sort key order is not the same as byte order. + // For example, "`%" is mapped to the range [`, a](where ` is 0x60 and a is 0x61). + // Because the collation utf8_general_ci is case-insensitive, a and A have the same sort key. + // Finally, the range comes to be [`, A], which is actually an empty range. + // See https://github.com/pingcap/tidb/issues/31174 for more details. + // In short, when the column type is non-binary collation string, we cannot use `like` expressions to generate the range. + return false, true + } + if !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { + return false, true + } + if !c.matchColumn(scalar.GetArgs()[0]) { + return false, true + } + pattern, ok := scalar.GetArgs()[1].(*expression.Constant) + if !ok { + return false, true + } + if pattern.Value.IsNull() { + return false, true + } + patternStr, err := pattern.Value.ToString() + if err != nil { + return false, true + } + if len(patternStr) == 0 { + return true, !c.isFullLengthColumn() + } + escape := byte(scalar.GetArgs()[2].(*expression.Constant).Value.GetInt64()) + likeFuncReserve := !c.isFullLengthColumn() + for i := 0; i < len(patternStr); i++ { + if patternStr[i] == escape { + i++ + if i < len(patternStr)-1 { + continue + } + break + } + if i == 0 && (patternStr[i] == '%' || patternStr[i] == '_') { + return false, true + } + if patternStr[i] == '%' { + // We currently do not support using `enum like 'xxx%'` to build range + // see https://github.com/pingcap/tidb/issues/27130 for more details + if scalar.GetArgs()[0].GetType().GetType() == mysql.TypeEnum { + return false, true + } + if i != len(patternStr)-1 { + likeFuncReserve = true + } + break + } + if patternStr[i] == '_' { + // We currently do not support using `enum like 'xxx_'` to build range + // see https://github.com/pingcap/tidb/issues/27130 for more details + if scalar.GetArgs()[0].GetType().GetType() == mysql.TypeEnum { + return false, true + } + likeFuncReserve = true + break + } + } + return true, likeFuncReserve +} + +func (c *conditionChecker) matchColumn(expr expression.Expression) bool { + // Check if virtual expression column matched + if c.checkerCol != nil { + return c.checkerCol.EqualByExprAndID(nil, expr) + } + return false +} + +func (c *conditionChecker) checkColumn(expr expression.Expression) (isAccessCond, shouldReserve bool) { + if c.matchColumn(expr) { + return true, !c.isFullLengthColumn() + } + return false, true +} diff --git a/util/ranger/detacher.go b/pkg/util/ranger/detacher.go similarity index 99% rename from util/ranger/detacher.go rename to pkg/util/ranger/detacher.go index 30755158a8b67..954bbcee5254e 100644 --- a/util/ranger/detacher.go +++ b/pkg/util/ranger/detacher.go @@ -18,17 +18,17 @@ import ( "math" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util/fixcontrol" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/planner/util/fixcontrol" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mathutil" ) // detachColumnCNFConditions detaches the condition for calculating range from the other conditions. diff --git a/pkg/util/ranger/main_test.go b/pkg/util/ranger/main_test.go new file mode 100644 index 0000000000000..e4ab0093e2f8b --- /dev/null +++ b/pkg/util/ranger/main_test.go @@ -0,0 +1,47 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package ranger_test + +import ( + "flag" + "fmt" + "os" + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + flag.Parse() + + if exitCode := m.Run(); exitCode != 0 { + os.Exit(exitCode) + } + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + + if err := goleak.Find(opts...); err != nil { + _, _ = fmt.Fprintf(os.Stderr, "goleak: Errors on successful test run: %v\n", err) + os.Exit(1) + } +} diff --git a/util/ranger/points.go b/pkg/util/ranger/points.go similarity index 98% rename from util/ranger/points.go rename to pkg/util/ranger/points.go index 75c5bfa363c8c..31063894d533d 100644 --- a/util/ranger/points.go +++ b/pkg/util/ranger/points.go @@ -21,15 +21,15 @@ import ( "sort" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/dbterror" ) // Error instances. diff --git a/util/ranger/ranger.go b/pkg/util/ranger/ranger.go similarity index 98% rename from util/ranger/ranger.go rename to pkg/util/ranger/ranger.go index 9a628cf397632..10fa1cc70f6ae 100644 --- a/util/ranger/ranger.go +++ b/pkg/util/ranger/ranger.go @@ -22,19 +22,19 @@ import ( "unicode/utf8" "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + driver "github.com/pingcap/tidb/pkg/types/parser_driver" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" ) func validInterval(sctx sessionctx.Context, low, high *point) (bool, error) { diff --git a/util/ranger/ranger_test.go b/pkg/util/ranger/ranger_test.go similarity index 99% rename from util/ranger/ranger_test.go rename to pkg/util/ranger/ranger_test.go index 8fcce6849910d..71a4552ae491c 100644 --- a/util/ranger/ranger_test.go +++ b/pkg/util/ranger/ranger_test.go @@ -19,18 +19,18 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/ranger/types.go b/pkg/util/ranger/types.go new file mode 100644 index 0000000000000..c3c8cbaa9ad02 --- /dev/null +++ b/pkg/util/ranger/types.go @@ -0,0 +1,280 @@ +// Copyright 2017 PingCAP, Inc. +// +// 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. + +package ranger + +import ( + "fmt" + "math" + "strings" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" +) + +// MutableRanges represents a range may change after it is created. +// It's mainly designed for plan-cache, since some ranges in a cached plan have to be rebuild when reusing. +type MutableRanges interface { + // Range returns the underlying range values. + Range() Ranges + // Rebuild rebuilds the underlying ranges again. + Rebuild() error +} + +// Ranges implements the MutableRanges interface for range array. +type Ranges []*Range + +// Range returns the range array. +func (rs Ranges) Range() Ranges { + return rs +} + +// Rebuild rebuilds this range. +func (Ranges) Rebuild() error { + return nil +} + +// MemUsage gets the memory usage of ranges. +func (rs Ranges) MemUsage() (sum int64) { + for _, ran := range rs { + sum += ran.MemUsage() + } + return +} + +// Range represents a range generated in physical plan building phase. +type Range struct { + LowVal []types.Datum // Low value is exclusive. + HighVal []types.Datum // High value is exclusive. + Collators []collate.Collator + LowExclude bool + HighExclude bool +} + +// Width returns the width of this range. +func (ran *Range) Width() int { + return len(ran.LowVal) +} + +// Clone clones a Range. +func (ran *Range) Clone() *Range { + if ran == nil { + return nil + } + newRange := &Range{ + LowVal: make([]types.Datum, 0, len(ran.LowVal)), + HighVal: make([]types.Datum, 0, len(ran.HighVal)), + LowExclude: ran.LowExclude, + HighExclude: ran.HighExclude, + } + for i, length := 0, len(ran.LowVal); i < length; i++ { + newRange.LowVal = append(newRange.LowVal, ran.LowVal[i]) + } + for i, length := 0, len(ran.HighVal); i < length; i++ { + newRange.HighVal = append(newRange.HighVal, ran.HighVal[i]) + } + newRange.Collators = append(newRange.Collators, ran.Collators...) + return newRange +} + +// IsPoint returns if the range is a point. +func (ran *Range) IsPoint(sctx sessionctx.Context) bool { + return ran.isPoint(sctx.GetSessionVars().StmtCtx, sctx.GetSessionVars().RegardNULLAsPoint) +} + +func (ran *Range) isPoint(stmtCtx *stmtctx.StatementContext, regardNullAsPoint bool) bool { + if len(ran.LowVal) != len(ran.HighVal) { + return false + } + for i := range ran.LowVal { + a := ran.LowVal[i] + b := ran.HighVal[i] + if a.Kind() == types.KindMinNotNull || b.Kind() == types.KindMaxValue { + return false + } + cmp, err := a.Compare(stmtCtx, &b, ran.Collators[i]) + if err != nil { + return false + } + if cmp != 0 { + return false + } + + if a.IsNull() && b.IsNull() { // [NULL, NULL] + if !regardNullAsPoint { + return false + } + } + } + return !ran.LowExclude && !ran.HighExclude +} + +// IsPointNonNullable returns if the range is a point without NULL. +func (ran *Range) IsPointNonNullable(sctx sessionctx.Context) bool { + return ran.isPoint(sctx.GetSessionVars().StmtCtx, false) +} + +// IsPointNullable returns if the range is a point. +// TODO: unify the parameter type with IsPointNullable and IsPoint +func (ran *Range) IsPointNullable(sctx sessionctx.Context) bool { + return ran.isPoint(sctx.GetSessionVars().StmtCtx, true) +} + +// IsFullRange check if the range is full scan range +func (ran *Range) IsFullRange(unsignedIntHandle bool) bool { + if unsignedIntHandle { + if len(ran.LowVal) != 1 || len(ran.HighVal) != 1 { + return false + } + lowValRawString := formatDatum(ran.LowVal[0], true) + highValRawString := formatDatum(ran.HighVal[0], false) + return lowValRawString == "0" && highValRawString == "+inf" + } + if len(ran.LowVal) != len(ran.HighVal) { + return false + } + for i := range ran.LowVal { + lowValRawString := formatDatum(ran.LowVal[i], true) + highValRawString := formatDatum(ran.HighVal[i], false) + if ("-inf" != lowValRawString && "NULL" != lowValRawString) || + ("+inf" != highValRawString && "NULL" != highValRawString) || + ("NULL" == lowValRawString && "NULL" == highValRawString) { + return false + } + } + return true +} + +// HasFullRange checks if any range in the slice is a full range. +func HasFullRange(ranges []*Range, unsignedIntHandle bool) bool { + for _, ran := range ranges { + if ran.IsFullRange(unsignedIntHandle) { + return true + } + } + return false +} + +// String implements the Stringer interface. +func (ran *Range) String() string { + lowStrs := make([]string, 0, len(ran.LowVal)) + for _, d := range ran.LowVal { + lowStrs = append(lowStrs, formatDatum(d, true)) + } + highStrs := make([]string, 0, len(ran.LowVal)) + for _, d := range ran.HighVal { + highStrs = append(highStrs, formatDatum(d, false)) + } + l, r := "[", "]" + if ran.LowExclude { + l = "(" + } + if ran.HighExclude { + r = ")" + } + return l + strings.Join(lowStrs, " ") + "," + strings.Join(highStrs, " ") + r +} + +// Encode encodes the range to its encoded value. +func (ran *Range) Encode(sc *stmtctx.StatementContext, lowBuffer, highBuffer []byte) ([]byte, []byte, error) { + var err error + lowBuffer, err = codec.EncodeKey(sc, lowBuffer[:0], ran.LowVal...) + if err != nil { + return nil, nil, err + } + if ran.LowExclude { + lowBuffer = kv.Key(lowBuffer).PrefixNext() + } + highBuffer, err = codec.EncodeKey(sc, highBuffer[:0], ran.HighVal...) + if err != nil { + return nil, nil, err + } + if !ran.HighExclude { + highBuffer = kv.Key(highBuffer).PrefixNext() + } + return lowBuffer, highBuffer, nil +} + +// PrefixEqualLen tells you how long the prefix of the range is a point. +// e.g. If this range is (1 2 3, 1 2 +inf), then the return value is 2. +func (ran *Range) PrefixEqualLen(sc *stmtctx.StatementContext) (int, error) { + // Here, len(ran.LowVal) always equal to len(ran.HighVal) + for i := 0; i < len(ran.LowVal); i++ { + cmp, err := ran.LowVal[i].Compare(sc, &ran.HighVal[i], ran.Collators[i]) + if err != nil { + return 0, errors.Trace(err) + } + if cmp != 0 { + return i, nil + } + } + return len(ran.LowVal), nil +} + +// EmptyRangeSize is the size of empty range. +const EmptyRangeSize = int64(unsafe.Sizeof(Range{})) + +// MemUsage gets the memory usage of range. +func (ran *Range) MemUsage() (sum int64) { + // 16 is the size of Collator interface. + sum = EmptyRangeSize + int64(len(ran.Collators))*16 + for _, val := range ran.LowVal { + sum += val.MemUsage() + } + for _, val := range ran.HighVal { + sum += val.MemUsage() + } + // We ignore size of collator currently. + return sum +} + +func formatDatum(d types.Datum, isLeftSide bool) string { + switch d.Kind() { + case types.KindNull: + return "NULL" + case types.KindMinNotNull: + return "-inf" + case types.KindMaxValue: + return "+inf" + case types.KindInt64: + switch d.GetInt64() { + case math.MinInt64: + if isLeftSide { + return "-inf" + } + case math.MaxInt64: + if !isLeftSide { + return "+inf" + } + } + case types.KindUint64: + if d.GetUint64() == math.MaxUint64 && !isLeftSide { + return "+inf" + } + case types.KindBytes: + return fmt.Sprintf("0x%X", d.GetValue()) + case types.KindString: + return fmt.Sprintf("%q", d.GetValue()) + case types.KindMysqlEnum, types.KindMysqlSet, + types.KindMysqlJSON, types.KindBinaryLiteral, types.KindMysqlBit: + return fmt.Sprintf("\"%v\"", d.GetValue()) + } + return fmt.Sprintf("%v", d.GetValue()) +} diff --git a/util/ranger/types_test.go b/pkg/util/ranger/types_test.go similarity index 96% rename from util/ranger/types_test.go rename to pkg/util/ranger/types_test.go index d09c7e65b8b89..c49d817a9a481 100644 --- a/util/ranger/types_test.go +++ b/pkg/util/ranger/types_test.go @@ -18,11 +18,11 @@ import ( "math" "testing" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/ranger" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/regexpr-router/BUILD.bazel b/pkg/util/regexpr-router/BUILD.bazel new file mode 100644 index 0000000000000..bc20994534461 --- /dev/null +++ b/pkg/util/regexpr-router/BUILD.bazel @@ -0,0 +1,26 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "regexpr-router", + srcs = ["regexpr_router.go"], + importpath = "github.com/pingcap/tidb/pkg/util/regexpr-router", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/filter", + "//pkg/util/table-router", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "regexpr-router_test", + timeout = "short", + srcs = ["regexpr_router_test.go"], + embed = [":regexpr-router"], + flaky = True, + deps = [ + "//pkg/util/filter", + "//pkg/util/table-router", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/regexpr-router/regexpr_router.go b/pkg/util/regexpr-router/regexpr_router.go similarity index 98% rename from util/regexpr-router/regexpr_router.go rename to pkg/util/regexpr-router/regexpr_router.go index 1c571d7de4fcb..0c026a3fd2ee0 100644 --- a/util/regexpr-router/regexpr_router.go +++ b/pkg/util/regexpr-router/regexpr_router.go @@ -19,8 +19,8 @@ import ( "strings" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/filter" - router "github.com/pingcap/tidb/util/table-router" + "github.com/pingcap/tidb/pkg/util/filter" + router "github.com/pingcap/tidb/pkg/util/table-router" ) // FilterType is the type of filter diff --git a/util/regexpr-router/regexpr_router_test.go b/pkg/util/regexpr-router/regexpr_router_test.go similarity index 98% rename from util/regexpr-router/regexpr_router_test.go rename to pkg/util/regexpr-router/regexpr_router_test.go index f8ed5bddb2b92..905d3c3f66fff 100644 --- a/util/regexpr-router/regexpr_router_test.go +++ b/pkg/util/regexpr-router/regexpr_router_test.go @@ -18,8 +18,8 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/util/filter" - router "github.com/pingcap/tidb/util/table-router" + "github.com/pingcap/tidb/pkg/util/filter" + router "github.com/pingcap/tidb/pkg/util/table-router" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/replayer/BUILD.bazel b/pkg/util/replayer/BUILD.bazel new file mode 100644 index 0000000000000..362ec2f48e293 --- /dev/null +++ b/pkg/util/replayer/BUILD.bazel @@ -0,0 +1,13 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "replayer", + srcs = ["replayer.go"], + importpath = "github.com/pingcap/tidb/pkg/util/replayer", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + ], +) diff --git a/util/replayer/replayer.go b/pkg/util/replayer/replayer.go similarity index 98% rename from util/replayer/replayer.go rename to pkg/util/replayer/replayer.go index 489747f5f2a01..9711479fb6c41 100644 --- a/util/replayer/replayer.go +++ b/pkg/util/replayer/replayer.go @@ -24,7 +24,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" ) // PlanReplayerTaskKey indicates key of a plan replayer task diff --git a/pkg/util/resourcegrouptag/BUILD.bazel b/pkg/util/resourcegrouptag/BUILD.bazel new file mode 100644 index 0000000000000..2e8b455e531d9 --- /dev/null +++ b/pkg/util/resourcegrouptag/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "resourcegrouptag", + srcs = ["resource_group_tag.go"], + importpath = "github.com/pingcap/tidb/pkg/util/resourcegrouptag", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/tablecodec/rowindexcodec", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_tikv_client_go_v2//tikvrpc", + ], +) + +go_test( + name = "resourcegrouptag_test", + timeout = "short", + srcs = [ + "main_test.go", + "resource_group_tag_test.go", + ], + embed = [":resourcegrouptag"], + flaky = True, + deps = [ + "//pkg/parser", + "//pkg/testkit/testsetup", + "//pkg/util/hack", + "@com_github_pingcap_kvproto//pkg/coprocessor", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/resourcegrouptag/main_test.go b/pkg/util/resourcegrouptag/main_test.go new file mode 100644 index 0000000000000..ca0858b62b118 --- /dev/null +++ b/pkg/util/resourcegrouptag/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package resourcegrouptag + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/resourcegrouptag/resource_group_tag.go b/pkg/util/resourcegrouptag/resource_group_tag.go similarity index 97% rename from util/resourcegrouptag/resource_group_tag.go rename to pkg/util/resourcegrouptag/resource_group_tag.go index 1b70c6e207ea4..fba6a5fdfc66c 100644 --- a/util/resourcegrouptag/resource_group_tag.go +++ b/pkg/util/resourcegrouptag/resource_group_tag.go @@ -18,8 +18,8 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/tablecodec/rowindexcodec" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/tablecodec/rowindexcodec" "github.com/pingcap/tipb/go-tipb" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/util/resourcegrouptag/resource_group_tag_test.go b/pkg/util/resourcegrouptag/resource_group_tag_test.go similarity index 99% rename from util/resourcegrouptag/resource_group_tag_test.go rename to pkg/util/resourcegrouptag/resource_group_tag_test.go index 98d864829b189..8c154ae0de6d5 100644 --- a/util/resourcegrouptag/resource_group_tag_test.go +++ b/pkg/util/resourcegrouptag/resource_group_tag_test.go @@ -21,8 +21,8 @@ import ( "github.com/pingcap/kvproto/pkg/coprocessor" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/hack" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikvrpc" diff --git a/util/rlimit_other.go b/pkg/util/rlimit_other.go similarity index 96% rename from util/rlimit_other.go rename to pkg/util/rlimit_other.go index 0478f0eb0ea6a..4a6ef7e6d2713 100644 --- a/util/rlimit_other.go +++ b/pkg/util/rlimit_other.go @@ -19,7 +19,7 @@ import ( "fmt" "syscall" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/rlimit_windows.go b/pkg/util/rlimit_windows.go similarity index 100% rename from util/rlimit_windows.go rename to pkg/util/rlimit_windows.go diff --git a/pkg/util/rowDecoder/BUILD.bazel b/pkg/util/rowDecoder/BUILD.bazel new file mode 100644 index 0000000000000..b764cdda034b4 --- /dev/null +++ b/pkg/util/rowDecoder/BUILD.bazel @@ -0,0 +1,50 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "rowDecoder", + srcs = ["decoder.go"], + importpath = "github.com/pingcap/tidb/pkg/util/rowDecoder", + visibility = ["//visibility:public"], + deps = [ + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/table", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/rowcodec", + ], +) + +go_test( + name = "rowDecoder_test", + timeout = "short", + srcs = [ + "decoder_test.go", + "main_test.go", + ], + flaky = True, + deps = [ + ":rowDecoder", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner/core", + "//pkg/sessionctx/stmtctx", + "//pkg/table/tables", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/testkit/testutil", + "//pkg/types", + "//pkg/util/collate", + "//pkg/util/mock", + "//pkg/util/rowcodec", + "@com_github_stretchr_testify//require", + "@io_opencensus_go//stats/view", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/rowDecoder/decoder.go b/pkg/util/rowDecoder/decoder.go new file mode 100644 index 0000000000000..30cf792fbe20d --- /dev/null +++ b/pkg/util/rowDecoder/decoder.go @@ -0,0 +1,205 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package decoder + +import ( + "slices" + "time" + + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/rowcodec" +) + +// Column contains the info and generated expr of column. +type Column struct { + Col *table.Column + GenExpr expression.Expression +} + +// RowDecoder decodes a byte slice into datums and eval the generated column value. +type RowDecoder struct { + tbl table.Table + mutRow chunk.MutRow + colMap map[int64]Column + colTypes map[int64]*types.FieldType + defaultVals []types.Datum + cols []*table.Column + pkCols []int64 +} + +// NewRowDecoder returns a new RowDecoder. +func NewRowDecoder(tbl table.Table, cols []*table.Column, decodeColMap map[int64]Column) *RowDecoder { + tblInfo := tbl.Meta() + colFieldMap := make(map[int64]*types.FieldType, len(decodeColMap)) + for id, col := range decodeColMap { + colFieldMap[id] = &col.Col.ColumnInfo.FieldType + } + + tps := make([]*types.FieldType, len(cols)) + for _, col := range cols { + // Even for changing column in column type change, we target field type uniformly. + tps[col.Offset] = &col.FieldType + } + var pkCols []int64 + switch { + case tblInfo.IsCommonHandle: + pkCols = tables.TryGetCommonPkColumnIds(tbl.Meta()) + case tblInfo.PKIsHandle: + pkCols = []int64{tblInfo.GetPkColInfo().ID} + default: // support decoding _tidb_rowid. + pkCols = []int64{model.ExtraHandleID} + } + return &RowDecoder{ + tbl: tbl, + mutRow: chunk.MutRowFromTypes(tps), + colMap: decodeColMap, + colTypes: colFieldMap, + defaultVals: make([]types.Datum, len(cols)), + cols: cols, + pkCols: pkCols, + } +} + +// DecodeAndEvalRowWithMap decodes a byte slice into datums and evaluates the generated column value. +func (rd *RowDecoder) DecodeAndEvalRowWithMap(ctx sessionctx.Context, handle kv.Handle, b []byte, decodeLoc *time.Location, row map[int64]types.Datum) (map[int64]types.Datum, error) { + var err error + if rowcodec.IsNewFormat(b) { + row, err = tablecodec.DecodeRowWithMapNew(b, rd.colTypes, decodeLoc, row) + } else { + row, err = tablecodec.DecodeRowWithMap(b, rd.colTypes, decodeLoc, row) + } + if err != nil { + return nil, err + } + row, err = tablecodec.DecodeHandleToDatumMap(handle, rd.pkCols, rd.colTypes, decodeLoc, row) + if err != nil { + return nil, err + } + for _, dCol := range rd.colMap { + colInfo := dCol.Col.ColumnInfo + val, ok := row[colInfo.ID] + if ok || dCol.GenExpr != nil { + rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) + continue + } + if dCol.Col.ChangeStateInfo != nil { + val, _, err = tables.GetChangingColVal(ctx, rd.cols, dCol.Col, row, rd.defaultVals) + } else { + // Get the default value of the column in the generated column expression. + val, err = tables.GetColDefaultValue(ctx, dCol.Col, rd.defaultVals) + } + if err != nil { + return nil, err + } + rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) + } + return rd.EvalRemainedExprColumnMap(ctx, row) +} + +// BuildFullDecodeColMap builds a map that contains [columnID -> struct{*table.Column, expression.Expression}] from all columns. +func BuildFullDecodeColMap(cols []*table.Column, schema *expression.Schema) map[int64]Column { + decodeColMap := make(map[int64]Column, len(cols)) + for _, col := range cols { + decodeColMap[col.ID] = Column{ + Col: col, + GenExpr: schema.Columns[col.Offset].VirtualExpr, + } + } + return decodeColMap +} + +// CurrentRowWithDefaultVal returns current decoding row with default column values set properly. +// Please make sure calling DecodeAndEvalRowWithMap first. +func (rd *RowDecoder) CurrentRowWithDefaultVal() chunk.Row { + return rd.mutRow.ToRow() +} + +// DecodeTheExistedColumnMap is used by ddl column-type-change first column reorg stage. +// In the function, we only decode the existed column in the row and fill the default value. +// For changing column, we shouldn't cast it here, because we will do a unified cast operation latter. +// For generated column, we didn't cast it here too, because the eval process will depend on the changing column. +func (rd *RowDecoder) DecodeTheExistedColumnMap(ctx sessionctx.Context, handle kv.Handle, b []byte, decodeLoc *time.Location, row map[int64]types.Datum) (map[int64]types.Datum, error) { + var err error + if rowcodec.IsNewFormat(b) { + row, err = tablecodec.DecodeRowWithMapNew(b, rd.colTypes, decodeLoc, row) + } else { + row, err = tablecodec.DecodeRowWithMap(b, rd.colTypes, decodeLoc, row) + } + if err != nil { + return nil, err + } + row, err = tablecodec.DecodeHandleToDatumMap(handle, rd.pkCols, rd.colTypes, decodeLoc, row) + if err != nil { + return nil, err + } + for _, dCol := range rd.colMap { + colInfo := dCol.Col.ColumnInfo + val, ok := row[colInfo.ID] + if ok || dCol.GenExpr != nil || dCol.Col.ChangeStateInfo != nil { + rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) + continue + } + // Get the default value of the column in the generated column expression. + val, err = tables.GetColDefaultValue(ctx, dCol.Col, rd.defaultVals) + if err != nil { + return nil, err + } + // Fill the default value into map. + row[colInfo.ID] = val + rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) + } + // return the existed column map here. + return row, nil +} + +// EvalRemainedExprColumnMap is used by ddl column-type-change first column reorg stage. +// It is always called after DecodeTheExistedColumnMap to finish the generated column evaluation. +func (rd *RowDecoder) EvalRemainedExprColumnMap(ctx sessionctx.Context, row map[int64]types.Datum) (map[int64]types.Datum, error) { + keys := make([]int, 0, len(rd.colMap)) + ids := make(map[int]int, len(rd.colMap)) + for k, col := range rd.colMap { + keys = append(keys, col.Col.Offset) + ids[col.Col.Offset] = int(k) + } + slices.Sort(keys) + for _, id := range keys { + col := rd.colMap[int64(ids[id])] + if col.GenExpr == nil { + continue + } + // Eval the column value + val, err := col.GenExpr.Eval(rd.mutRow.ToRow()) + if err != nil { + return nil, err + } + val, err = table.CastValue(ctx, *val.Clone(), col.Col.ColumnInfo, false, true) + if err != nil { + return nil, err + } + + rd.mutRow.SetValue(col.Col.Offset, val.GetValue()) + row[int64(ids[id])] = val + } + // return the existed and evaluated column map here. + return row, nil +} diff --git a/util/rowDecoder/decoder_test.go b/pkg/util/rowDecoder/decoder_test.go similarity index 92% rename from util/rowDecoder/decoder_test.go rename to pkg/util/rowDecoder/decoder_test.go index 94eab0a17c0a8..fb77d027d4592 100644 --- a/util/rowDecoder/decoder_test.go +++ b/pkg/util/rowDecoder/decoder_test.go @@ -18,20 +18,20 @@ import ( "testing" "time" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - _ "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + _ "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit/testutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/mock" + decoder "github.com/pingcap/tidb/pkg/util/rowDecoder" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/pkg/util/rowDecoder/main_test.go b/pkg/util/rowDecoder/main_test.go new file mode 100644 index 0000000000000..0318dea55610b --- /dev/null +++ b/pkg/util/rowDecoder/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package decoder_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + testsetup.SetupForCommonTest() + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/rowcodec/BUILD.bazel b/pkg/util/rowcodec/BUILD.bazel new file mode 100644 index 0000000000000..42eb49b739d7d --- /dev/null +++ b/pkg/util/rowcodec/BUILD.bazel @@ -0,0 +1,52 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "rowcodec", + srcs = [ + "common.go", + "decoder.go", + "encoder.go", + "row.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/rowcodec", + visibility = ["//visibility:public"], + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/parser/types", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/codec", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "rowcodec_test", + timeout = "short", + srcs = [ + "bench_test.go", + "main_test.go", + "rowcodec_test.go", + ], + embed = [":rowcodec"], + flaky = True, + deps = [ + "//pkg/kv", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/tablecodec", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util/benchdaily", + "//pkg/util/chunk", + "//pkg/util/codec", + "//pkg/util/collate", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/rowcodec/bench_test.go b/pkg/util/rowcodec/bench_test.go new file mode 100644 index 0000000000000..a5bc6bc0bc228 --- /dev/null +++ b/pkg/util/rowcodec/bench_test.go @@ -0,0 +1,122 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package rowcodec_test + +import ( + "testing" + "time" + + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/benchdaily" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/rowcodec" +) + +func BenchmarkChecksum(b *testing.B) { + b.ReportAllocs() + datums := types.MakeDatums(1, "abc", 1.1) + tp1 := types.NewFieldType(mysql.TypeLong) + tp2 := types.NewFieldType(mysql.TypeVarchar) + tp3 := types.NewFieldType(mysql.TypeDouble) + cols := []rowcodec.ColData{ + {&model.ColumnInfo{ID: 1, FieldType: *tp1}, &datums[0]}, + {&model.ColumnInfo{ID: 2, FieldType: *tp2}, &datums[1]}, + {&model.ColumnInfo{ID: 3, FieldType: *tp3}, &datums[2]}, + } + row := rowcodec.RowData{Cols: cols} + for i := 0; i < b.N; i++ { + _, err := row.Checksum() + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEncode(b *testing.B) { + b.ReportAllocs() + oldRow := types.MakeDatums(1, "abc", 1.1) + var xb rowcodec.Encoder + var buf []byte + colIDs := []int64{1, 2, 3} + var err error + for i := 0; i < b.N; i++ { + buf, err = xb.Encode(nil, colIDs, oldRow, buf) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkEncodeFromOldRow(b *testing.B) { + b.ReportAllocs() + oldRow := types.MakeDatums(1, "abc", 1.1) + oldRowData, err := tablecodec.EncodeOldRow(stmtctx.NewStmtCtx(), oldRow, []int64{1, 2, 3}, nil, nil) + if err != nil { + b.Fatal(err) + } + var xb rowcodec.Encoder + var buf []byte + for i := 0; i < b.N; i++ { + buf, err = rowcodec.EncodeFromOldRow(&xb, nil, oldRowData, buf) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkDecode(b *testing.B) { + b.ReportAllocs() + oldRow := types.MakeDatums(1, "abc", 1.1) + colIDs := []int64{-1, 2, 3} + tps := []*types.FieldType{ + types.NewFieldType(mysql.TypeLonglong), + types.NewFieldType(mysql.TypeString), + types.NewFieldType(mysql.TypeDouble), + } + var xb rowcodec.Encoder + xRowData, err := xb.Encode(nil, colIDs, oldRow, nil) + if err != nil { + b.Fatal(err) + } + cols := make([]rowcodec.ColInfo, len(tps)) + for i, tp := range tps { + cols[i] = rowcodec.ColInfo{ + ID: colIDs[i], + Ft: tp, + } + } + decoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, time.Local) + chk := chunk.NewChunkWithCapacity(tps, 1) + for i := 0; i < b.N; i++ { + chk.Reset() + err = decoder.DecodeToChunk(xRowData, kv.IntHandle(1), chk) + if err != nil { + b.Fatal(err) + } + } +} + +func TestBenchDaily(t *testing.T) { + benchdaily.Run( + BenchmarkEncode, + BenchmarkDecode, + BenchmarkEncodeFromOldRow, + ) +} diff --git a/pkg/util/rowcodec/common.go b/pkg/util/rowcodec/common.go new file mode 100644 index 0000000000000..e8651a53b5966 --- /dev/null +++ b/pkg/util/rowcodec/common.go @@ -0,0 +1,356 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package rowcodec + +import ( + "encoding/binary" + "hash/crc32" + "math" + "reflect" + "unsafe" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" + data "github.com/pingcap/tidb/pkg/types" +) + +// CodecVer is the constant number that represent the new row format. +const CodecVer = 128 + +var ( + errInvalidCodecVer = errors.New("invalid codec version") + errInvalidChecksumVer = errors.New("invalid checksum version") + errInvalidChecksumTyp = errors.New("invalid type for checksum") +) + +// First byte in the encoded value which specifies the encoding type. +const ( + NilFlag byte = 0 + BytesFlag byte = 1 + CompactBytesFlag byte = 2 + IntFlag byte = 3 + UintFlag byte = 4 + FloatFlag byte = 5 + DecimalFlag byte = 6 + VarintFlag byte = 8 + VaruintFlag byte = 9 + JSONFlag byte = 10 +) + +func bytesToU32Slice(b []byte) []uint32 { + if len(b) == 0 { + return nil + } + var u32s []uint32 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u32s)) + hdr.Len = len(b) / 4 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&b[0])) + return u32s +} + +func bytes2U16Slice(b []byte) []uint16 { + if len(b) == 0 { + return nil + } + var u16s []uint16 + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16s)) + hdr.Len = len(b) / 2 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&b[0])) + return u16s +} + +func u16SliceToBytes(u16s []uint16) []byte { + if len(u16s) == 0 { + return nil + } + var b []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + hdr.Len = len(u16s) * 2 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&u16s[0])) + return b +} + +func u32SliceToBytes(u32s []uint32) []byte { + if len(u32s) == 0 { + return nil + } + var b []byte + hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) + hdr.Len = len(u32s) * 4 + hdr.Cap = hdr.Len + hdr.Data = uintptr(unsafe.Pointer(&u32s[0])) + return b +} + +func encodeInt(buf []byte, iVal int64) []byte { + var tmp [8]byte + if int64(int8(iVal)) == iVal { + buf = append(buf, byte(iVal)) + } else if int64(int16(iVal)) == iVal { + binary.LittleEndian.PutUint16(tmp[:], uint16(iVal)) + buf = append(buf, tmp[:2]...) + } else if int64(int32(iVal)) == iVal { + binary.LittleEndian.PutUint32(tmp[:], uint32(iVal)) + buf = append(buf, tmp[:4]...) + } else { + binary.LittleEndian.PutUint64(tmp[:], uint64(iVal)) + buf = append(buf, tmp[:8]...) + } + return buf +} + +func decodeInt(val []byte) int64 { + switch len(val) { + case 1: + return int64(int8(val[0])) + case 2: + return int64(int16(binary.LittleEndian.Uint16(val))) + case 4: + return int64(int32(binary.LittleEndian.Uint32(val))) + default: + return int64(binary.LittleEndian.Uint64(val)) + } +} + +func encodeUint(buf []byte, uVal uint64) []byte { + var tmp [8]byte + if uint64(uint8(uVal)) == uVal { + buf = append(buf, byte(uVal)) + } else if uint64(uint16(uVal)) == uVal { + binary.LittleEndian.PutUint16(tmp[:], uint16(uVal)) + buf = append(buf, tmp[:2]...) + } else if uint64(uint32(uVal)) == uVal { + binary.LittleEndian.PutUint32(tmp[:], uint32(uVal)) + buf = append(buf, tmp[:4]...) + } else { + binary.LittleEndian.PutUint64(tmp[:], uVal) + buf = append(buf, tmp[:8]...) + } + return buf +} + +func decodeUint(val []byte) uint64 { + switch len(val) { + case 1: + return uint64(val[0]) + case 2: + return uint64(binary.LittleEndian.Uint16(val)) + case 4: + return uint64(binary.LittleEndian.Uint32(val)) + default: + return binary.LittleEndian.Uint64(val) + } +} + +type largeNotNullSorter Encoder + +func (s *largeNotNullSorter) Less(i, j int) bool { + return s.colIDs32[i] < s.colIDs32[j] +} + +func (s *largeNotNullSorter) Len() int { + return int(s.numNotNullCols) +} + +func (s *largeNotNullSorter) Swap(i, j int) { + s.colIDs32[i], s.colIDs32[j] = s.colIDs32[j], s.colIDs32[i] + s.values[i], s.values[j] = s.values[j], s.values[i] +} + +type smallNotNullSorter Encoder + +func (s *smallNotNullSorter) Less(i, j int) bool { + return s.colIDs[i] < s.colIDs[j] +} + +func (s *smallNotNullSorter) Len() int { + return int(s.numNotNullCols) +} + +func (s *smallNotNullSorter) Swap(i, j int) { + s.colIDs[i], s.colIDs[j] = s.colIDs[j], s.colIDs[i] + s.values[i], s.values[j] = s.values[j], s.values[i] +} + +type smallNullSorter Encoder + +func (s *smallNullSorter) Less(i, j int) bool { + nullCols := s.colIDs[s.numNotNullCols:] + return nullCols[i] < nullCols[j] +} + +func (s *smallNullSorter) Len() int { + return int(s.numNullCols) +} + +func (s *smallNullSorter) Swap(i, j int) { + nullCols := s.colIDs[s.numNotNullCols:] + nullCols[i], nullCols[j] = nullCols[j], nullCols[i] +} + +type largeNullSorter Encoder + +func (s *largeNullSorter) Less(i, j int) bool { + nullCols := s.colIDs32[s.numNotNullCols:] + return nullCols[i] < nullCols[j] +} + +func (s *largeNullSorter) Len() int { + return int(s.numNullCols) +} + +func (s *largeNullSorter) Swap(i, j int) { + nullCols := s.colIDs32[s.numNotNullCols:] + nullCols[i], nullCols[j] = nullCols[j], nullCols[i] +} + +const ( + // Length of rowkey. + rowKeyLen = 19 + // Index of record flag 'r' in rowkey used by tidb-server. + // The rowkey format is t{8 bytes id}_r{8 bytes handle} + recordPrefixIdx = 10 +) + +// IsRowKey determine whether key is row key. +// this method will be used in unistore. +func IsRowKey(key []byte) bool { + return len(key) >= rowKeyLen && key[0] == 't' && key[recordPrefixIdx] == 'r' +} + +// IsNewFormat checks whether row data is in new-format. +func IsNewFormat(rowData []byte) bool { + return rowData[0] == CodecVer +} + +// FieldTypeFromModelColumn creates a types.FieldType from model.ColumnInfo. +// export for test case and CDC. +func FieldTypeFromModelColumn(col *model.ColumnInfo) *types.FieldType { + return col.FieldType.Clone() +} + +// ColData combines the column info as well as its datum. It's used to calculate checksum. +type ColData struct { + *model.ColumnInfo + Datum *data.Datum +} + +// Encode encodes the column datum into bytes for checksum. If buf provided, append encoded data to it. +func (c ColData) Encode(buf []byte) ([]byte, error) { + return appendDatumForChecksum(buf, c.Datum, c.GetType()) +} + +// RowData is a list of ColData for row checksum calculation. +type RowData struct { + // Cols is a list of ColData which is expected to be sorted by id before calling Encode/Checksum. + Cols []ColData + // Data stores the result of Encode. However, it mostly acts as a buffer for encoding columns on checksum + // calculation. + Data []byte +} + +// Len implements sort.Interface for RowData. +func (r RowData) Len() int { return len(r.Cols) } + +// Less implements sort.Interface for RowData. +func (r RowData) Less(i int, j int) bool { return r.Cols[i].ID < r.Cols[j].ID } + +// Swap implements sort.Interface for RowData. +func (r RowData) Swap(i int, j int) { r.Cols[i], r.Cols[j] = r.Cols[j], r.Cols[i] } + +// Encode encodes all columns into bytes (for test purpose). +func (r *RowData) Encode() ([]byte, error) { + var err error + if len(r.Data) > 0 { + r.Data = r.Data[:0] + } + for _, col := range r.Cols { + r.Data, err = col.Encode(r.Data) + if err != nil { + return nil, err + } + } + return r.Data, nil +} + +// Checksum calculates the checksum of columns. Callers should make sure columns are sorted by id. +func (r *RowData) Checksum() (checksum uint32, err error) { + for _, col := range r.Cols { + if len(r.Data) > 0 { + r.Data = r.Data[:0] + } + r.Data, err = col.Encode(r.Data) + if err != nil { + return 0, err + } + checksum = crc32.Update(checksum, crc32.IEEETable, r.Data) + } + return checksum, nil +} + +func appendDatumForChecksum(buf []byte, dat *data.Datum, typ byte) (out []byte, err error) { + defer func() { + if x := recover(); x != nil { + // catch panic when datum and type mismatch + err = errors.Annotatef(x.(error), "encode datum(%s) as %s for checksum", dat.String(), types.TypeStr(typ)) + } + }() + if dat.IsNull() { + return buf, nil + } + switch typ { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24, mysql.TypeYear: + out = binary.LittleEndian.AppendUint64(buf, dat.GetUint64()) + case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: + out = appendLengthValue(buf, dat.GetBytes()) + case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate, mysql.TypeNewDate: + out = appendLengthValue(buf, []byte(dat.GetMysqlTime().String())) + case mysql.TypeDuration: + out = appendLengthValue(buf, []byte(dat.GetMysqlDuration().String())) + case mysql.TypeFloat, mysql.TypeDouble: + v := dat.GetFloat64() + if math.IsInf(v, 0) || math.IsNaN(v) { + v = 0 // because ticdc has such a transform + } + out = binary.LittleEndian.AppendUint64(buf, math.Float64bits(v)) + case mysql.TypeNewDecimal: + out = appendLengthValue(buf, []byte(dat.GetMysqlDecimal().String())) + case mysql.TypeEnum: + out = binary.LittleEndian.AppendUint64(buf, dat.GetMysqlEnum().Value) + case mysql.TypeSet: + out = binary.LittleEndian.AppendUint64(buf, dat.GetMysqlSet().Value) + case mysql.TypeBit: + // ticdc transforms a bit value as the following way, no need to handle truncate error here. + v, _ := dat.GetBinaryLiteral().ToInt(nil) + out = binary.LittleEndian.AppendUint64(buf, v) + case mysql.TypeJSON: + out = appendLengthValue(buf, []byte(dat.GetMysqlJSON().String())) + case mysql.TypeNull, mysql.TypeGeometry: + out = buf + default: + return buf, errInvalidChecksumTyp + } + return +} + +func appendLengthValue(buf []byte, val []byte) []byte { + buf = binary.LittleEndian.AppendUint32(buf, uint32(len(val))) + return append(buf, val...) +} diff --git a/pkg/util/rowcodec/decoder.go b/pkg/util/rowcodec/decoder.go new file mode 100644 index 0000000000000..ee33bce2acb3a --- /dev/null +++ b/pkg/util/rowcodec/decoder.go @@ -0,0 +1,524 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. + +package rowcodec + +import ( + "encoding/binary" + "fmt" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" +) + +// decoder contains base util for decode row. +type decoder struct { + row + columns []ColInfo + handleColIDs []int64 + loc *time.Location +} + +// NewDecoder creates a decoder. +func NewDecoder(columns []ColInfo, handleColIDs []int64, loc *time.Location) *decoder { + return &decoder{ + columns: columns, + handleColIDs: handleColIDs, + loc: loc, + } +} + +// ColInfo is used as column meta info for row decoder. +type ColInfo struct { + ID int64 + IsPKHandle bool + VirtualGenCol bool + Ft *types.FieldType +} + +// DatumMapDecoder decodes the row to datum map. +type DatumMapDecoder struct { + decoder +} + +// NewDatumMapDecoder creates a DatumMapDecoder. +func NewDatumMapDecoder(columns []ColInfo, loc *time.Location) *DatumMapDecoder { + return &DatumMapDecoder{decoder{ + columns: columns, + loc: loc, + }} +} + +// DecodeToDatumMap decodes byte slices to datum map. +func (decoder *DatumMapDecoder) DecodeToDatumMap(rowData []byte, row map[int64]types.Datum) (map[int64]types.Datum, error) { + if row == nil { + row = make(map[int64]types.Datum, len(decoder.columns)) + } + err := decoder.fromBytes(rowData) + if err != nil { + return nil, err + } + for i := range decoder.columns { + col := &decoder.columns[i] + idx, isNil, notFound := decoder.row.findColID(col.ID) + if !notFound && !isNil { + colData := decoder.getData(idx) + d, err := decoder.decodeColDatum(col, colData) + if err != nil { + return nil, err + } + row[col.ID] = d + continue + } + + if isNil { + var d types.Datum + d.SetNull() + row[col.ID] = d + continue + } + } + return row, nil +} + +func (decoder *DatumMapDecoder) decodeColDatum(col *ColInfo, colData []byte) (types.Datum, error) { + var d types.Datum + switch col.Ft.GetType() { + case mysql.TypeLonglong, mysql.TypeLong, mysql.TypeInt24, mysql.TypeShort, mysql.TypeTiny: + if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { + d.SetUint64(decodeUint(colData)) + } else { + d.SetInt64(decodeInt(colData)) + } + case mysql.TypeYear: + d.SetInt64(decodeInt(colData)) + case mysql.TypeFloat: + _, fVal, err := codec.DecodeFloat(colData) + if err != nil { + return d, err + } + d.SetFloat32(float32(fVal)) + case mysql.TypeDouble: + _, fVal, err := codec.DecodeFloat(colData) + if err != nil { + return d, err + } + d.SetFloat64(fVal) + case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + d.SetString(string(colData), col.Ft.GetCollate()) + case mysql.TypeNewDecimal: + _, dec, precision, frac, err := codec.DecodeDecimal(colData) + if err != nil { + return d, err + } + d.SetMysqlDecimal(dec) + d.SetLength(precision) + d.SetFrac(frac) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + var t types.Time + t.SetType(col.Ft.GetType()) + t.SetFsp(col.Ft.GetDecimal()) + err := t.FromPackedUint(decodeUint(colData)) + if err != nil { + return d, err + } + if col.Ft.GetType() == mysql.TypeTimestamp && !t.IsZero() { + err = t.ConvertTimeZone(time.UTC, decoder.loc) + if err != nil { + return d, err + } + } + d.SetMysqlTime(t) + case mysql.TypeDuration: + var dur types.Duration + dur.Duration = time.Duration(decodeInt(colData)) + dur.Fsp = col.Ft.GetDecimal() + d.SetMysqlDuration(dur) + case mysql.TypeEnum: + // ignore error deliberately, to read empty enum value. + enum, err := types.ParseEnumValue(col.Ft.GetElems(), decodeUint(colData)) + if err != nil { + enum = types.Enum{} + } + d.SetMysqlEnum(enum, col.Ft.GetCollate()) + case mysql.TypeSet: + set, err := types.ParseSetValue(col.Ft.GetElems(), decodeUint(colData)) + if err != nil { + return d, err + } + d.SetMysqlSet(set, col.Ft.GetCollate()) + case mysql.TypeBit: + byteSize := (col.Ft.GetFlen() + 7) >> 3 + d.SetMysqlBit(types.NewBinaryLiteralFromUint(decodeUint(colData), byteSize)) + case mysql.TypeJSON: + var j types.BinaryJSON + j.TypeCode = colData[0] + j.Value = colData[1:] + d.SetMysqlJSON(j) + default: + return d, errors.Errorf("unknown type %d", col.Ft.GetType()) + } + return d, nil +} + +// ChunkDecoder decodes the row to chunk.Chunk. +type ChunkDecoder struct { + decoder + defDatum func(i int, chk *chunk.Chunk) error +} + +// NewChunkDecoder creates a NewChunkDecoder. +func NewChunkDecoder(columns []ColInfo, handleColIDs []int64, defDatum func(i int, chk *chunk.Chunk) error, loc *time.Location) *ChunkDecoder { + return &ChunkDecoder{ + decoder: decoder{ + columns: columns, + handleColIDs: handleColIDs, + loc: loc, + }, + defDatum: defDatum, + } +} + +// DecodeToChunk decodes a row to chunk. +func (decoder *ChunkDecoder) DecodeToChunk(rowData []byte, handle kv.Handle, chk *chunk.Chunk) error { + err := decoder.fromBytes(rowData) + if err != nil { + return err + } + + for colIdx := range decoder.columns { + col := &decoder.columns[colIdx] + // fill the virtual column value after row calculation + if col.VirtualGenCol { + chk.AppendNull(colIdx) + continue + } + if col.ID == model.ExtraRowChecksumID { + if v := decoder.row.getChecksumInfo(); len(v) > 0 { + chk.AppendString(colIdx, v) + } else { + chk.AppendNull(colIdx) + } + continue + } + + idx, isNil, notFound := decoder.row.findColID(col.ID) + if !notFound && !isNil { + colData := decoder.getData(idx) + err := decoder.decodeColToChunk(colIdx, col, colData, chk) + if err != nil { + return err + } + continue + } + + // Only try to decode handle when there is no corresponding column in the value. + // This is because the information in handle may be incomplete in some cases. + // For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle. + if decoder.tryAppendHandleColumn(colIdx, col, handle, chk) { + continue + } + + if isNil { + chk.AppendNull(colIdx) + continue + } + + if decoder.defDatum == nil { + chk.AppendNull(colIdx) + continue + } + + err := decoder.defDatum(colIdx, chk) + if err != nil { + return err + } + } + return nil +} + +func (decoder *ChunkDecoder) tryAppendHandleColumn(colIdx int, col *ColInfo, handle kv.Handle, chk *chunk.Chunk) bool { + if handle == nil { + return false + } + if handle.IsInt() && col.ID == decoder.handleColIDs[0] { + chk.AppendInt64(colIdx, handle.IntValue()) + return true + } + for i, id := range decoder.handleColIDs { + if col.ID == id { + if types.NeedRestoredData(col.Ft) { + return false + } + coder := codec.NewDecoder(chk, decoder.loc) + _, err := coder.DecodeOne(handle.EncodedCol(i), colIdx, col.Ft) + return err == nil + } + } + return false +} + +func (decoder *ChunkDecoder) decodeColToChunk(colIdx int, col *ColInfo, colData []byte, chk *chunk.Chunk) error { + switch col.Ft.GetType() { + case mysql.TypeLonglong, mysql.TypeLong, mysql.TypeInt24, mysql.TypeShort, mysql.TypeTiny: + if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { + chk.AppendUint64(colIdx, decodeUint(colData)) + } else { + chk.AppendInt64(colIdx, decodeInt(colData)) + } + case mysql.TypeYear: + chk.AppendInt64(colIdx, decodeInt(colData)) + case mysql.TypeFloat: + _, fVal, err := codec.DecodeFloat(colData) + if err != nil { + return err + } + chk.AppendFloat32(colIdx, float32(fVal)) + case mysql.TypeDouble: + _, fVal, err := codec.DecodeFloat(colData) + if err != nil { + return err + } + chk.AppendFloat64(colIdx, fVal) + case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeString, + mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: + chk.AppendBytes(colIdx, colData) + case mysql.TypeNewDecimal: + _, dec, _, frac, err := codec.DecodeDecimal(colData) + if err != nil { + return err + } + if col.Ft.GetDecimal() != types.UnspecifiedLength && frac > col.Ft.GetDecimal() { + to := new(types.MyDecimal) + err := dec.Round(to, col.Ft.GetDecimal(), types.ModeHalfUp) + if err != nil { + return errors.Trace(err) + } + dec = to + } + chk.AppendMyDecimal(colIdx, dec) + case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: + var t types.Time + t.SetType(col.Ft.GetType()) + t.SetFsp(col.Ft.GetDecimal()) + err := t.FromPackedUint(decodeUint(colData)) + if err != nil { + return err + } + if col.Ft.GetType() == mysql.TypeTimestamp && decoder.loc != nil && !t.IsZero() { + err = t.ConvertTimeZone(time.UTC, decoder.loc) + if err != nil { + return err + } + } + chk.AppendTime(colIdx, t) + case mysql.TypeDuration: + var dur types.Duration + dur.Duration = time.Duration(decodeInt(colData)) + dur.Fsp = col.Ft.GetDecimal() + chk.AppendDuration(colIdx, dur) + case mysql.TypeEnum: + // ignore error deliberately, to read empty enum value. + enum, err := types.ParseEnumValue(col.Ft.GetElems(), decodeUint(colData)) + if err != nil { + enum = types.Enum{} + } + chk.AppendEnum(colIdx, enum) + case mysql.TypeSet: + set, err := types.ParseSetValue(col.Ft.GetElems(), decodeUint(colData)) + if err != nil { + return err + } + chk.AppendSet(colIdx, set) + case mysql.TypeBit: + byteSize := (col.Ft.GetFlen() + 7) >> 3 + chk.AppendBytes(colIdx, types.NewBinaryLiteralFromUint(decodeUint(colData), byteSize)) + case mysql.TypeJSON: + var j types.BinaryJSON + j.TypeCode = colData[0] + j.Value = colData[1:] + chk.AppendJSON(colIdx, j) + default: + return errors.Errorf("unknown type %d", col.Ft.GetType()) + } + return nil +} + +// BytesDecoder decodes the row to old datums bytes. +type BytesDecoder struct { + decoder + defBytes func(i int) ([]byte, error) +} + +// NewByteDecoder creates a BytesDecoder. +// defBytes: provided default value bytes in old datum format(flag+colData). +func NewByteDecoder(columns []ColInfo, handleColIDs []int64, defBytes func(i int) ([]byte, error), loc *time.Location) *BytesDecoder { + return &BytesDecoder{ + decoder: decoder{ + columns: columns, + handleColIDs: handleColIDs, + loc: loc, + }, + defBytes: defBytes, + } +} + +func (decoder *BytesDecoder) decodeToBytesInternal(outputOffset map[int64]int, handle kv.Handle, value []byte, cacheBytes []byte) ([][]byte, error) { + var r row + err := r.fromBytes(value) + if err != nil { + return nil, err + } + values := make([][]byte, len(outputOffset)) + for i := range decoder.columns { + col := &decoder.columns[i] + tp := fieldType2Flag(col.Ft.GetType(), col.Ft.GetFlag()&mysql.UnsignedFlag == 0) + colID := col.ID + offset := outputOffset[colID] + idx, isNil, notFound := r.findColID(colID) + if !notFound && !isNil { + val := r.getData(idx) + values[offset] = decoder.encodeOldDatum(tp, val) + continue + } + + // Only try to decode handle when there is no corresponding column in the value. + // This is because the information in handle may be incomplete in some cases. + // For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle. + if decoder.tryDecodeHandle(values, offset, col, handle, cacheBytes) { + continue + } + + if isNil { + values[offset] = []byte{NilFlag} + continue + } + + if decoder.defBytes != nil { + defVal, err := decoder.defBytes(i) + if err != nil { + return nil, err + } + if len(defVal) > 0 { + values[offset] = defVal + continue + } + } + + values[offset] = []byte{NilFlag} + } + return values, nil +} + +func (decoder *BytesDecoder) tryDecodeHandle(values [][]byte, offset int, col *ColInfo, + handle kv.Handle, cacheBytes []byte) bool { + if handle == nil { + return false + } + if types.NeedRestoredData(col.Ft) { + return false + } + if col.IsPKHandle || col.ID == model.ExtraHandleID { + handleData := cacheBytes + if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { + handleData = append(handleData, UintFlag) + handleData = codec.EncodeUint(handleData, uint64(handle.IntValue())) + } else { + handleData = append(handleData, IntFlag) + handleData = codec.EncodeInt(handleData, handle.IntValue()) + } + values[offset] = handleData + return true + } + var handleData []byte + for i, hid := range decoder.handleColIDs { + if col.ID == hid { + handleData = append(handleData, handle.EncodedCol(i)...) + } + } + if len(handleData) > 0 { + values[offset] = handleData + return true + } + return false +} + +// DecodeToBytesNoHandle decodes raw byte slice to row data without handle. +func (decoder *BytesDecoder) DecodeToBytesNoHandle(outputOffset map[int64]int, value []byte) ([][]byte, error) { + return decoder.decodeToBytesInternal(outputOffset, nil, value, nil) +} + +// DecodeToBytes decodes raw byte slice to row data. +func (decoder *BytesDecoder) DecodeToBytes(outputOffset map[int64]int, handle kv.Handle, value []byte, cacheBytes []byte) ([][]byte, error) { + return decoder.decodeToBytesInternal(outputOffset, handle, value, cacheBytes) +} + +func (*BytesDecoder) encodeOldDatum(tp byte, val []byte) []byte { + buf := make([]byte, 0, 1+binary.MaxVarintLen64+len(val)) + switch tp { + case BytesFlag: + buf = append(buf, CompactBytesFlag) + buf = codec.EncodeCompactBytes(buf, val) + case IntFlag: + buf = append(buf, VarintFlag) + buf = codec.EncodeVarint(buf, decodeInt(val)) + case UintFlag: + buf = append(buf, VaruintFlag) + buf = codec.EncodeUvarint(buf, decodeUint(val)) + default: + buf = append(buf, tp) + buf = append(buf, val...) + } + return buf +} + +// fieldType2Flag transforms field type into kv type flag. +func fieldType2Flag(tp byte, signed bool) (flag byte) { + switch tp { + case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: + if signed { + flag = IntFlag + } else { + flag = UintFlag + } + case mysql.TypeFloat, mysql.TypeDouble: + flag = FloatFlag + case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, + mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: + flag = BytesFlag + case mysql.TypeDatetime, mysql.TypeDate, mysql.TypeTimestamp: + flag = UintFlag + case mysql.TypeDuration: + flag = IntFlag + case mysql.TypeNewDecimal: + flag = DecimalFlag + case mysql.TypeYear: + flag = IntFlag + case mysql.TypeEnum, mysql.TypeBit, mysql.TypeSet: + flag = UintFlag + case mysql.TypeJSON: + flag = JSONFlag + case mysql.TypeNull: + flag = NilFlag + default: + panic(fmt.Sprintf("unknown field type %d", tp)) + } + return +} diff --git a/util/rowcodec/encoder.go b/pkg/util/rowcodec/encoder.go similarity index 96% rename from util/rowcodec/encoder.go rename to pkg/util/rowcodec/encoder.go index 95ff35b13aa14..92fbd566c96cb 100644 --- a/util/rowcodec/encoder.go +++ b/pkg/util/rowcodec/encoder.go @@ -20,11 +20,11 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" ) // Encoder is used to encode a row. diff --git a/pkg/util/rowcodec/main_test.go b/pkg/util/rowcodec/main_test.go new file mode 100644 index 0000000000000..d6c88fe255ad7 --- /dev/null +++ b/pkg/util/rowcodec/main_test.go @@ -0,0 +1,64 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package rowcodec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} + +// EncodeFromOldRow encodes a row from an old-format row. +// this method will be used in test. +func EncodeFromOldRow(encoder *Encoder, sc *stmtctx.StatementContext, oldRow, buf []byte) ([]byte, error) { + if len(oldRow) > 0 && oldRow[0] == CodecVer { + return oldRow, nil + } + encoder.reset() + for len(oldRow) > 1 { + var d types.Datum + var err error + oldRow, d, err = codec.DecodeOne(oldRow) + if err != nil { + return nil, err + } + colID := d.GetInt64() + oldRow, d, err = codec.DecodeOne(oldRow) + if err != nil { + return nil, err + } + encoder.appendColVal(colID, &d) + } + numCols, notNullIdx := encoder.reformatCols() + err := encoder.encodeRowCols(sc, numCols, notNullIdx) + if err != nil { + return nil, err + } + return encoder.row.toBytes(buf[:0]), nil +} diff --git a/util/rowcodec/row.go b/pkg/util/rowcodec/row.go similarity index 100% rename from util/rowcodec/row.go rename to pkg/util/rowcodec/row.go diff --git a/util/rowcodec/rowcodec_test.go b/pkg/util/rowcodec/rowcodec_test.go similarity index 99% rename from util/rowcodec/rowcodec_test.go rename to pkg/util/rowcodec/rowcodec_test.go index 933895dc78ea3..64844fed25e78 100644 --- a/util/rowcodec/rowcodec_test.go +++ b/pkg/util/rowcodec/rowcodec_test.go @@ -23,16 +23,16 @@ import ( "testing" "time" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/collate" + "github.com/pingcap/tidb/pkg/util/rowcodec" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/schemacmp/BUILD.bazel b/pkg/util/schemacmp/BUILD.bazel new file mode 100644 index 0000000000000..1399538d41388 --- /dev/null +++ b/pkg/util/schemacmp/BUILD.bazel @@ -0,0 +1,46 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "schemacmp", + srcs = [ + "lattice.go", + "table.go", + "type.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/schemacmp", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/charset", + "//pkg/parser/format", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/types", + "//pkg/types", + ], +) + +go_test( + name = "schemacmp_test", + timeout = "short", + srcs = [ + "lattice_test.go", + "table_test.go", + "type_test.go", + ], + flaky = True, + deps = [ + ":schemacmp", + "//pkg/ddl", + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/planner", + "//pkg/sessionctx", + "//pkg/types", + "//pkg/util/mock", + "@com_github_pingcap_errors//:errors", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/schemacmp/lattice.go b/pkg/util/schemacmp/lattice.go similarity index 99% rename from util/schemacmp/lattice.go rename to pkg/util/schemacmp/lattice.go index f524a5a1c993a..f09130ba1c38e 100644 --- a/util/schemacmp/lattice.go +++ b/pkg/util/schemacmp/lattice.go @@ -17,8 +17,8 @@ package schemacmp import ( "fmt" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" ) // IncompatibleError is the error type for incompatible schema. @@ -301,12 +301,12 @@ func (a Byte) Join(other Lattice) (Lattice, error) { } // fieldTp is a mysql column field type implementing lattice. -// It is used for the column field type (`github.com/pingcap/tidb/parser/types.FieldType.Tp`). +// It is used for the column field type (`github.com/pingcap/tidb/pkg/parser/types.FieldType.Tp`). type fieldTp struct { value byte } -// FieldTp is used for the column field type (`github.com/pingcap/tidb/parser/types.FieldType.Tp`). +// FieldTp is used for the column field type (`github.com/pingcap/tidb/pkg/parser/types.FieldType.Tp`). func FieldTp(value byte) Lattice { return fieldTp{value: value} } diff --git a/util/schemacmp/lattice_test.go b/pkg/util/schemacmp/lattice_test.go similarity index 99% rename from util/schemacmp/lattice_test.go rename to pkg/util/schemacmp/lattice_test.go index fa0a1e546b6a7..1e58cbc961635 100644 --- a/util/schemacmp/lattice_test.go +++ b/pkg/util/schemacmp/lattice_test.go @@ -18,8 +18,8 @@ import ( "bytes" "testing" - "github.com/pingcap/tidb/parser/mysql" - . "github.com/pingcap/tidb/util/schemacmp" + "github.com/pingcap/tidb/pkg/parser/mysql" + . "github.com/pingcap/tidb/pkg/util/schemacmp" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/schemacmp/table.go b/pkg/util/schemacmp/table.go new file mode 100644 index 0000000000000..234c2c2f038e6 --- /dev/null +++ b/pkg/util/schemacmp/table.go @@ -0,0 +1,429 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schemacmp + +import ( + "cmp" + "slices" + "strings" + + "github.com/pingcap/tidb/pkg/parser/format" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/types" +) + +const ( + columnInfoTupleIndexDefaultValue = iota + columnInfoTupleIndexGeneratedExprString + columnInfoTupleIndexGeneratedStored + columnInfoTupleIndexFieldTypes +) + +// encodeColumnInfoToLattice collects the necessary information for comparing a column. +func encodeColumnInfoToLattice(ci *model.ColumnInfo) Tuple { + return Tuple{ + MaybeSingletonInterface(ci.DefaultValue), + Singleton(ci.GeneratedExprString), + Singleton(ci.GeneratedStored), + Type(&ci.FieldType), + } +} + +// restoreColumnInfoFromUnwrapped restores the text representation of a column. +func restoreColumnInfoFromUnwrapped(ctx *format.RestoreCtx, col []interface{}, colName string) { + typ := col[columnInfoTupleIndexFieldTypes].(*types.FieldType) + + ctx.WriteName(colName) + ctx.WritePlain(" ") + _ = typ.Restore(ctx) + if genExpr := col[columnInfoTupleIndexGeneratedExprString].(string); len(genExpr) != 0 { + ctx.WriteKeyWord(" GENERATED ALWAYS AS ") + ctx.WritePlainf("(%s)", genExpr) + } + if col[columnInfoTupleIndexGeneratedStored].(bool) { + ctx.WriteKeyWord(" STORED") + } + if mysql.HasNotNullFlag(typ.GetFlag()) { + ctx.WriteKeyWord(" NOT NULL") + } + if defVal := col[columnInfoTupleIndexDefaultValue]; defVal != nil { + ctx.WriteKeyWord(" DEFAULT ") + ctx.WritePlainf("%v", defVal) + } + if mysql.HasAutoIncrementFlag(typ.GetFlag()) { + ctx.WriteKeyWord(" AUTO_INCREMENT") + } +} + +const ( + indexInfoTupleIndexColumns = iota + indexInfoTupleIndexNotUnique + indexInfoTupleIndexNotPrimary + indexInfoTupleIndexType +) + +type indexColumn struct { + colName string + length int +} + +type indexColumnSlice []indexColumn + +// Equals implements Equality +func (a indexColumnSlice) Equals(other Equality) bool { + b, ok := other.(indexColumnSlice) + if !ok || len(a) != len(b) { + return false + } + for i, av := range a { + if av.colName != b[i].colName || av.length != b[i].length { + return false + } + } + return true +} + +func encodeIndexInfoToLattice(ii *model.IndexInfo) Tuple { + indexColumns := make(indexColumnSlice, 0, len(ii.Columns)) + for _, column := range ii.Columns { + indexColumns = append(indexColumns, indexColumn{colName: column.Name.L, length: column.Length}) + } + + return Tuple{ + EqualitySingleton(indexColumns), + Bool(!ii.Unique), + Bool(!ii.Primary), + Singleton(ii.Tp), + } +} +func encodeImplicitPrimaryKeyToLattice(ci *model.ColumnInfo) Tuple { + return Tuple{ + EqualitySingleton(indexColumnSlice{indexColumn{colName: ci.Name.L, length: types.UnspecifiedLength}}), + Bool(false), + Bool(false), + Singleton(model.IndexTypeBtree), + } +} + +func restoreIndexInfoFromUnwrapped(ctx *format.RestoreCtx, index []interface{}, keyName string) { + isPrimary := !index[indexInfoTupleIndexNotPrimary].(bool) + + switch { + case isPrimary: + ctx.WriteKeyWord("PRIMARY KEY") + case !index[indexInfoTupleIndexNotUnique].(bool): + ctx.WriteKeyWord("UNIQUE KEY ") + ctx.WriteName(keyName) + default: + ctx.WriteKeyWord("KEY ") + ctx.WriteName(keyName) + } + + if tp := index[indexInfoTupleIndexType].(model.IndexType); tp != model.IndexTypeBtree { + ctx.WriteKeyWord(" USING ") + ctx.WriteKeyWord(tp.String()) + } + + ctx.WritePlain(" (") + for i, column := range index[indexInfoTupleIndexColumns].(indexColumnSlice) { + if i != 0 { + ctx.WritePlain(", ") + } + ctx.WriteName(column.colName) + if column.length != types.UnspecifiedLength { + ctx.WritePlainf("(%d)", column.length) + } + } + ctx.WritePlain(")") +} + +type columnMap map[string]Tuple + +func (columnMap) New() LatticeMap { + return make(columnMap) +} + +func (a columnMap) Get(key string) Lattice { + val, ok := a[key] + if !ok { + return nil + } + return val +} + +func (a columnMap) Insert(key string, value Lattice) { + a[key] = value.(Tuple) +} + +func (a columnMap) ForEach(f func(key string, value Lattice) error) error { + for key, value := range a { + if err := f(key, value); err != nil { + return err + } + } + return nil +} + +func (columnMap) CompareWithNil(value Lattice) (int, error) { + if value.(Tuple)[columnInfoTupleIndexFieldTypes].(typ).hasDefault() { + return 1, nil + } + return 0, &IncompatibleError{Msg: "column with no default value cannot be missing"} +} + +func (columnMap) ShouldDeleteIncompatibleJoin() bool { + return false +} + +func (columnMap) JoinWithNil(value Lattice) (Lattice, error) { + col := append(make(Tuple, 0), value.(Tuple)...) + ty := col[columnInfoTupleIndexFieldTypes].(typ).clone() + if ty.setFlagForMissingColumn() && ty.isNotNull() { + col[columnInfoTupleIndexDefaultValue] = Maybe(Singleton(ty.getStandardDefaultValue())) + } + col[columnInfoTupleIndexFieldTypes] = ty + return col, nil +} + +type indexMap map[string]Tuple + +func (indexMap) New() LatticeMap { + return make(indexMap) +} + +func (a indexMap) Get(key string) Lattice { + val, ok := a[key] + if !ok { + return nil + } + return val +} + +func (a indexMap) Insert(key string, value Lattice) { + a[key] = value.(Tuple) +} + +func (a indexMap) ForEach(f func(key string, value Lattice) error) error { + for key, value := range a { + if err := f(key, value); err != nil { + return err + } + } + return nil +} + +func (indexMap) CompareWithNil(_ Lattice) (int, error) { + return -1, nil +} + +func (indexMap) ShouldDeleteIncompatibleJoin() bool { + return true +} + +func (indexMap) JoinWithNil(_ Lattice) (Lattice, error) { + return nil, nil +} + +const ( + tableInfoTupleIndexCharset = iota + tableInfoTupleIndexCollate + tableInfoTupleIndexColumns + tableInfoTupleIndexIndices + // nolint:unused, varcheck, deadcode + tableInfoTupleIndexAutoIncID + tableInfoTupleIndexShardRowIDBits + tableInfoTupleIndexAutoRandomBits + // nolint: unused, varcheck, deadcode + tableInfoTupleIndexPreSplitRegions + tableInfoTupleIndexCompression +) + +func encodeTableInfoToLattice(ti *model.TableInfo) Tuple { + // TODO: Handle VIEW and PARTITION and SEQUENCE + hasExplicitPrimaryKey := false + indices := make(indexMap) + for _, ii := range ti.Indices { + if ii.Primary { + hasExplicitPrimaryKey = true + } + indices[ii.Name.L] = encodeIndexInfoToLattice(ii) + } + columns := make(columnMap) + for _, ci := range ti.Columns { + columns[ci.Name.L] = encodeColumnInfoToLattice(ci) + if !hasExplicitPrimaryKey && (ci.GetFlag()&mysql.PriKeyFlag) != 0 { + indices["primary"] = encodeImplicitPrimaryKeyToLattice(ci) + } + } + + return Tuple{ + Singleton(ti.Charset), + Singleton(ti.Collate), + Map(columns), + Map(indices), + // TODO ForeignKeys? + Int64(ti.AutoIncID), + // TODO Relax these? + Singleton(ti.ShardRowIDBits), + Singleton(ti.AutoRandomBits), + Singleton(ti.PreSplitRegions), + MaybeSingletonString(ti.Compression), + } +} + +type kvPair struct { + value interface{} + key string +} + +func sortedMap(input map[string]interface{}) []kvPair { + res := make([]kvPair, 0, len(input)) + for key, value := range input { + res = append(res, kvPair{key: key, value: value}) + } + + slices.SortFunc(res, func(a, b kvPair) int { + return cmp.Compare(a.key, b.key) + }) + return res +} + +func restoreTableInfoFromUnwrapped(ctx *format.RestoreCtx, table []interface{}, tableName string) { + ctx.WriteKeyWord("CREATE TABLE ") + ctx.WriteName(tableName) + ctx.WritePlain("(") + + for i, pair := range sortedMap(table[tableInfoTupleIndexColumns].(map[string]interface{})) { + if i != 0 { + ctx.WritePlain(", ") + } + colName := pair.key + column := pair.value.([]interface{}) + restoreColumnInfoFromUnwrapped(ctx, column, colName) + } + + for _, pair := range sortedMap(table[tableInfoTupleIndexIndices].(map[string]interface{})) { + ctx.WritePlain(", ") + indexName := pair.key + index := pair.value.([]interface{}) + restoreIndexInfoFromUnwrapped(ctx, index, indexName) + } + + ctx.WritePlain(")") + if charset := table[tableInfoTupleIndexCharset].(string); charset != "" { + ctx.WriteKeyWord(" CHARSET ") + ctx.WriteKeyWord(charset) + } + if collate := table[tableInfoTupleIndexCollate].(string); collate != "" { + ctx.WriteKeyWord(" COLLATE ") + ctx.WriteKeyWord(collate) + } + if bits := table[tableInfoTupleIndexShardRowIDBits].(uint64); bits > 0 { + ctx.WriteKeyWord(" SHARD_ROW_ID_BITS ") + ctx.WritePlainf("%d", bits) + } + if bits := table[tableInfoTupleIndexAutoRandomBits].(uint64); bits > 0 { + ctx.WritePlain("/*") + ctx.WriteKeyWord(" AUTO_RANDOM_BITS ") + ctx.WritePlainf("%d */", bits) + } + if compression, ok := table[tableInfoTupleIndexCompression].(string); ok && len(compression) != 0 { + ctx.WriteKeyWord(" COMPRESSION ") + ctx.WriteString(compression) + } +} + +// Table is a table in the database. +type Table struct{ value Lattice } + +// Encode is used to encode a Table. +func Encode(ti *model.TableInfo) Table { + return Table{value: encodeTableInfoToLattice(ti)} +} + +// DecodeColumnFieldTypes is used to decode column field types from Lattice. +func DecodeColumnFieldTypes(t Table) map[string]*types.FieldType { + table := t.value.Unwrap().([]interface{}) + columnMaps := table[tableInfoTupleIndexColumns].(map[string]interface{}) + cols := make(map[string]*types.FieldType, len(columnMaps)) + for key, value := range columnMaps { + cols[key] = value.([]interface{})[columnInfoTupleIndexFieldTypes].(*types.FieldType) + } + return cols +} + +// Restore is for debug use only. +func (t Table) Restore(ctx *format.RestoreCtx, tableName string) { + restoreTableInfoFromUnwrapped(ctx, t.value.Unwrap().([]interface{}), tableName) +} + +// Compare is the implementation of Lattice interface. +func (t Table) Compare(other Table) (int, error) { + return t.value.Compare(other.value) +} + +// Join is a helper function to join two tables. +func (t Table) Join(other Table) (Table, error) { + res, err := t.value.Join(other.value) + if err != nil { + return Table{value: nil}, err + } + + // fix up the type's key flags. + // unfortunately we cannot count on the type's own flag joining + // because an index's joining rule is more complex than 3 bits. + columnKeyFlags := make(map[string]uint) + table := res.(Tuple) + for _, index := range table[tableInfoTupleIndexIndices].(latticeMap).LatticeMap.(indexMap) { + cols := index[indexInfoTupleIndexColumns].Unwrap().(indexColumnSlice) + if len(cols) == 0 { + continue + } + switch { + case !index[indexInfoTupleIndexNotPrimary].Unwrap().(bool): + for _, col := range cols { + columnKeyFlags[col.colName] |= mysql.PriKeyFlag + } + case !index[indexInfoTupleIndexNotUnique].Unwrap().(bool) && len(cols) == 1: + columnKeyFlags[cols[0].colName] |= mysql.UniqueKeyFlag + default: + // Only the first column can be set if index or unique index has multiple columns. + // See https://dev.mysql.com/doc/refman/5.7/en/show-columns.html. + columnKeyFlags[cols[0].colName] |= mysql.MultipleKeyFlag + } + } + columns := table[tableInfoTupleIndexColumns].(latticeMap).LatticeMap.(columnMap) + for name, column := range columns { + ty := column[columnInfoTupleIndexFieldTypes].(typ) + flag, ok := columnKeyFlags[name] + if !ok && ty.inAutoIncrement() { + return Table{value: nil}, &IncompatibleError{ + Msg: ErrMsgAtMapKey, + Args: []interface{}{name, &IncompatibleError{Msg: ErrMsgAutoTypeWithoutKey}}, + } + } + ty.setAntiKeyFlags(flag) + } + + return Table{value: table}, nil +} + +func (t Table) String() string { + var sb strings.Builder + ctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) + t.Restore(ctx, "tbl") + return sb.String() +} diff --git a/pkg/util/schemacmp/table_test.go b/pkg/util/schemacmp/table_test.go new file mode 100644 index 0000000000000..670c9600f3252 --- /dev/null +++ b/pkg/util/schemacmp/table_test.go @@ -0,0 +1,517 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schemacmp_test + +import ( + "strings" + "testing" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + _ "github.com/pingcap/tidb/pkg/planner" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/util/mock" + "github.com/pingcap/tidb/pkg/util/schemacmp" + "github.com/stretchr/testify/require" +) + +func toTableInfo(parser *parser.Parser, sctx sessionctx.Context, createTableStmt string) (*model.TableInfo, error) { + node, err := parser.ParseOneStmt(createTableStmt, "", "") + if err != nil { + return nil, err + } + createStmtNode, ok := node.(*ast.CreateTableStmt) + if !ok { + return nil, errors.New("not a create table statement") + } + return ddl.MockTableInfo(sctx, createStmtNode, 1) +} + +func checkDecodeFieldTypes(t *testing.T, info *model.TableInfo, tt schemacmp.Table) { + fieldTyps := schemacmp.DecodeColumnFieldTypes(tt) + require.Len(t, fieldTyps, len(info.Columns)) + for _, col := range info.Columns { + typ, ok := fieldTyps[col.Name.O] + require.True(t, ok) + require.Equal(t, *typ, col.FieldType) + } +} + +func TestJoinSchemas(t *testing.T) { + p := parser.New() + sctx := mock.NewContext() + testCases := []struct { + name string + a string + b string + cmp int + cmpErr string + join string + joinErr string + }{ + { + name: "DM_002/1", + a: "CREATE TABLE tb1 (col1 INT)", + b: "CREATE TABLE tb2 (col1 INT, new_col1 INT)", + cmp: -1, + join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", + }, + { + name: "DM_002/1/unordered", + a: "CREATE TABLE tb1 (col1 INT)", + b: "CREATE TABLE tb2 (new_col1 INT, col1 INT)", + cmp: -1, + join: "CREATE TABLE tb3 (new_col1 INT, col1 INT)", + }, + { + name: "DM_002/2", + a: "CREATE TABLE tb1 (col1 INT, new_col1 INT)", + b: "CREATE TABLE tb2 (col1 INT, new_col1 INT)", + cmp: 0, + join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", + }, + { + name: "DM_002/2/unordered", + a: "CREATE TABLE tb1 (col1 INT, new_col1 INT)", + b: "CREATE TABLE tb2 (new_col1 INT, col1 INT)", + cmp: 0, + join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", + }, + { + name: "DM_010", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmp: 1, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", + }, + { + name: "DM_011", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 FLOAT)", + cmpErr: `.*"new_col1".*incompatible mysql type.*`, + joinErr: `.*"new_col1".*incompatible mysql type.*`, + }, + { + name: "DM_014", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col2 INT)", + cmpErr: `.*combining contradicting orders.*`, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", + }, + { + name: "DM_031/VARCHAR", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 VARCHAR(10))", + cmpErr: `.*"new_col1".*incompatible mysql type.*`, + joinErr: `.*"new_col1".*incompatible mysql type.*`, + }, + { + name: "DM_031/TEXT", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 TEXT)", + cmpErr: `.*"new_col1".*incompatible mysql type.*`, + joinErr: `.*"new_col1".*incompatible mysql type.*`, + }, + { + name: "DM_031/JSON", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 JSON)", + cmpErr: `.*"new_col1".*incompatible mysql type.*`, + joinErr: `.*"new_col1".*incompatible mysql type.*`, + }, + { + name: "DM_033", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), c FLOAT NOT NULL)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmpErr: `.*"c": column with no default value cannot be missing`, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), c FLOAT NOT NULL DEFAULT 0)", + }, + { + name: "DM_034", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT UNIQUE AUTO_INCREMENT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmpErr: `.*combining contradicting orders.*`, + joinErr: `.*"new_col1".*auto type but not defined as a key`, + }, + { + name: "DM_035", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 INT, col2 INT)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col2 INT, col1 INT)", + cmp: 0, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 INT, col2 INT)", + }, + { + name: "DM_037", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 INT DEFAULT 0)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 INT DEFAULT -1)", + cmpErr: `.*"col1".*distinct singletons.*`, + joinErr: `.*"col1".*distinct singletons.*`, + }, + { + name: "DM_039/1", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmp: 1, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + }, + { + name: "DM_039/2", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + cmp: 0, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + }, + { + name: "DM_040", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_bin)", + cmpErr: `.*"col1".*distinct singletons.*`, + joinErr: `.*"col1".*distinct singletons.*`, + }, + { + name: "DM_041/1", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmp: 1, + join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + }, + { + name: "DM_041/2", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + cmp: 0, + join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + }, + { + name: "DM_042", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmp: 1, + join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", + }, + { + name: "DM_043", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 2))", + cmpErr: `.*"new_col1".*distinct singletons.*`, + joinErr: `.*"new_col1".*distinct singletons.*`, + }, + { + name: "DM_044", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) VIRTUAL)", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", + cmpErr: `.*"new_col1".*distinct singletons.*`, + joinErr: `.*"new_col1".*distinct singletons.*`, + }, + { + name: "DM_052", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (c BIGINT, b VARCHAR(10))", + cmpErr: `.*combining contradicting orders.*`, + join: `CREATE TABLE tb3 (a INT, b VARCHAR(10), c BIGINT)`, + }, + { + name: "DM_053", + a: "CREATE TABLE tb1 (c BIGINT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (c DOUBLE, b VARCHAR(10))", + cmpErr: `.*"c".*incompatible mysql type.*`, + joinErr: `.*"c".*incompatible mysql type.*`, + }, + { + name: "DM_055", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (a BIGINT, b VARCHAR(10))", + cmp: -1, + join: "CREATE TABLE tb2 (a BIGINT, b VARCHAR(10))", + }, + { + name: "DM_057", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (c INT DEFAULT 1, b VARCHAR(10))", + cmpErr: `.*combining contradicting orders.*`, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), c INT DEFAULT 1)", + }, + { + name: "DM_061", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10) CHARSET utf8)", + cmpErr: `.*"b".*distinct singletons.*`, + joinErr: `.*"b".*distinct singletons.*`, + }, + { + name: "DM_066", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (a INT DEFAULT 1, b VARCHAR(10))", + cmp: -1, + join: "CREATE TABLE tb3 (a INT DEFAULT 1, b VARCHAR(10))", + }, + // { // these table options are somehow ignored by the parser. + // name: "DM_074", + // a: "CREATE TABLE tbl1 (a INT, b VARCHAR(10)) CHARSET utf8 COLLATE utf8_bin", + // b: "CREATE TABLE tbl2 (a INT, b VARCHAR(10)) CHARSET utf8mb4 COLLATE utf8mb4_bin", + // joinErr: `.*distinct singletons.*`, + // }, + { + name: "DM_078", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", + b: "CREATE TABLE tb2 (a INT PRIMARY KEY, b VARCHAR(10))", + cmp: 1, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10))", + }, + { + name: "DM_080/1", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b), UNIQUE KEY idx_ab(a, b))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", + cmp: -1, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10))", + }, + { + name: "DM_080/2", + a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b), UNIQUE KEY idx_ab(a, b))", + b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b))", + cmp: -1, + join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b))", + }, + // { // index visibility is not visible in IndexInfo yet. + // name: "DM_086", + // a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY a(a) VISIBLE)", + // b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), UNIQUE KEY a(a) INVISIBLE)", + // joinErr: `.*distinct singletons.*`, + // }, + { + name: "Different index components", + a: "CREATE TABLE tbl1 (a INT, b INT, KEY i(a))", + b: "CREATE TABLE tbl2 (a INT, b INT, KEY i(b))", + cmpErr: `.*combining contradicting orders.*`, + join: "CREATE TABLE tbl3 (a INT, b INT)", + }, + { + name: "Different index order", + a: "CREATE TABLE tbl1 (a INT, b INT, KEY i(a, b))", + b: "CREATE TABLE tbl2 (a INT, b INT, KEY i(b, a))", + cmpErr: `.*combining contradicting orders.*`, + join: "CREATE TABLE tbl3 (a INT, b INT)", + }, + { + name: "Different index length", + a: "CREATE TABLE tbl1 (a TEXT, KEY i(a(14)))", + b: "CREATE TABLE tbl2 (a TEXT, KEY i(a(15)))", + cmpErr: `.*distinct singletons.*`, + join: "CREATE TABLE tbl3 (a TEXT)", + }, + { + name: "Cannot drop key tied to AUTO_INC column", + a: "CREATE TABLE tbl1(a INT AUTO_INCREMENT, b INT, KEY i(a))", + b: "CREATE TABLE tbl2(a INT AUTO_INCREMENT, b INT, KEY i(a, b))", + cmpErr: `.*distinct singletons.*`, + joinErr: `.*"a".*auto type but not defined as a key`, + }, + { + name: "not-null column with special types", + a: `CREATE TABLE tbl1( + a1 INT NOT NULL, + b1 DECIMAL NOT NULL, + c1 VARCHAR(20) NOT NULL, + d1 DATETIME(3) NOT NULL, + e1 ENUM('abc', 'def') NOT NULL + )`, + b: `CREATE TABLE tbl2( + a2 TIME NOT NULL, + b2 DATE NOT NULL, + c2 BINARY(50) NOT NULL, + d2 YEAR(4) NOT NULL, + e2 SET('abc', 'def') NOT NULL + )`, + cmpErr: `.*column with no default value cannot be missing`, + join: `CREATE TABLE tbl3( + a1 INT NOT NULL DEFAULT 0, + b1 DECIMAL NOT NULL DEFAULT 0, + c1 VARCHAR(20) NOT NULL DEFAULT '', + d1 DATETIME(3) NOT NULL DEFAULT '0000-00-00 00:00:00', + e1 ENUM('abc', 'def') NOT NULL DEFAULT 'abc', + a2 TIME NOT NULL DEFAULT '00:00:00', + b2 DATE NOT NULL DEFAULT '0000-00-00', + c2 BINARY(50) NOT NULL DEFAULT '', + d2 YEAR(4) NOT NULL DEFAULT '0000', + e2 SET('abc', 'def') NOT NULL DEFAULT '' + )`, + }, + { + name: "test case 2020-03-17", + a: `CREATE TABLE bar (id INT PRIMARY KEY)`, + b: `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`, + cmp: -1, + join: `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`, + }, + { + name: "test case 2020-03-17-alt", + a: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY)`, + b: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY, c1 INT)`, + cmp: -1, + join: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY, c1 INT)`, + }, + { + name: "test case 2020-03-17-alt-2", + a: `CREATE TABLE bar (id INT PRIMARY KEY)`, + b: `CREATE TABLE bar (id INT, c1 INT)`, + cmp: -1, + join: `CREATE TABLE bar (id INT, c1 INT)`, + }, + { + name: "test case 2020-03-17-alt-3", + a: `CREATE TABLE bar (id1 INT PRIMARY KEY, id2 INT)`, + b: `CREATE TABLE bar (id1 INT, id2 INT PRIMARY KEY)`, + cmpErr: `.*combining contradicting orders.*`, + join: `CREATE TABLE bar (id1 INT, id2 INT)`, + }, + { + name: "test case 2020-04-28-blob", + a: "CREATE TABLE tb1 (a BLOB, b VARCHAR(10))", + b: "CREATE TABLE tb2 (a LONGBLOB, b VARCHAR(10))", + cmp: -1, + join: "CREATE TABLE tb2 (a LONGBLOB, b VARCHAR(10))", + }, + { + name: "join equal single primary key", + a: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", + b: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", + cmp: 0, + join: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", + }, + { + name: "join equal composite primary key", + a: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", + b: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", + cmp: 0, + join: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", + }, + { + name: "join equal single index", + a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", + b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", + cmp: 0, + join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", + }, + { + name: "join equal unique index", + a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", + b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", + cmp: 0, + join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", + }, + { + name: "join equal composite index", + a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", + b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", + cmp: 0, + join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", + }, + { + name: "join equal composite unique index", + a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", + b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", + cmp: 0, + join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", + }, + } + + for _, tc := range testCases { + tia, err := toTableInfo(p, sctx, tc.a) + require.NoError(t, err) + tib, err := toTableInfo(p, sctx, tc.b) + require.NoError(t, err) + + a := schemacmp.Encode(tia) + b := schemacmp.Encode(tib) + checkDecodeFieldTypes(t, tia, a) + checkDecodeFieldTypes(t, tib, b) + var j schemacmp.Table + if len(tc.joinErr) == 0 { + tij, err := toTableInfo(p, sctx, tc.join) + require.NoError(t, err) + j = schemacmp.Encode(tij) + } + + cmp, err := a.Compare(b) + if len(tc.cmpErr) != 0 { + require.Regexp(t, tc.cmpErr, err) + } else { + require.NoError(t, err) + require.Equal(t, tc.cmp, cmp) + } + + cmp, err = b.Compare(a) + if len(tc.cmpErr) != 0 { + require.Regexp(t, tc.cmpErr, err) + } else { + require.NoError(t, err) + require.Equal(t, cmp, -tc.cmp) + } + + joined, err := a.Join(b) + if len(tc.joinErr) != 0 { + if err == nil { + t.Log("a = ", a) + t.Log("b = ", b) + t.Log("j = ", joined) + } + require.Regexp(t, tc.joinErr, err) + } else { + require.NoError(t, err) + require.Equal(t, joined, j) + } + + joined, err = b.Join(a) + if len(tc.joinErr) != 0 { + require.Regexp(t, tc.joinErr, err) + } else { + require.NoError(t, err) + require.Equal(t, joined, j) + cmp, err = joined.Compare(a) + + require.NoError(t, err) + require.GreaterOrEqual(t, cmp, 0) + + cmp, err = joined.Compare(b) + require.NoError(t, err) + require.GreaterOrEqual(t, cmp, 0) + } + } +} + +func TestTableString(t *testing.T) { + p := parser.New() + sctx := mock.NewContext() + ti, err := toTableInfo(p, sctx, "CREATE TABLE tb (a INT, b INT)") + require.NoError(t, err) + + charsets := []string{"", mysql.DefaultCharset} + collates := []string{"", mysql.DefaultCollationName} + for _, charset := range charsets { + for _, collate := range collates { + ti.Charset = charset + ti.Collate = collate + sql := strings.ToLower(schemacmp.Encode(ti).String()) + require.Equal(t, charset != "", strings.Contains(sql, "charset")) + require.Equal(t, collate != "", strings.Contains(sql, "collate")) + _, err := toTableInfo(p, sctx, sql) + require.NoError(t, err) + } + } +} diff --git a/pkg/util/schemacmp/type.go b/pkg/util/schemacmp/type.go new file mode 100644 index 0000000000000..de9a7df566f7a --- /dev/null +++ b/pkg/util/schemacmp/type.go @@ -0,0 +1,214 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schemacmp + +import ( + "math/bits" + "strings" + + "github.com/pingcap/tidb/pkg/parser/charset" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" +) + +const ( + flagMaskKeys = mysql.PriKeyFlag | mysql.UniqueKeyFlag | mysql.MultipleKeyFlag + flagMaskDefVal = mysql.AutoIncrementFlag | mysql.NoDefaultValueFlag + + notPartOfKeys = ^byte(0) +) + +// Please ensure this list is synchronized with the order of Tuple{} in encodeFieldTypeToLattice(). +const ( + fieldTypeTupleIndexTp = iota + fieldTypeTupleIndexFlen + fieldTypeTupleIndexDec + fieldTypeTupleIndexFlagSingleton + fieldTypeTupleIndexFlagNull + fieldTypeTupleIndexFlagAntiKeys + fieldTypeTupleIndexFlagDefVal + fieldTypeTupleIndexCharset + fieldTypeTupleIndexCollate + fieldTypeTupleIndexElems + + ErrMsgAutoTypeWithoutKey = "auto type but not defined as a key" +) + +func encodeAntiKeys(flag uint) byte { + // this ensure we get this order: + // 1. "not part of keys" (flag = 0) is the maximum + // 2. multiple keys (8) > unique key (4) > primary key (2). + return ^bits.Reverse8(byte(flag & flagMaskKeys)) +} + +func decodeAntiKeys(encoded byte) uint { + return uint(bits.Reverse8(^encoded)) +} + +// encodeTypeTpAsLattice +func encodeFieldTypeToLattice(ft *types.FieldType) Tuple { + var flen, dec Lattice + if ft.GetType() == mysql.TypeNewDecimal { + flen = Singleton(ft.GetFlen()) + dec = Singleton(ft.GetDecimal()) + } else { + flen = Int(ft.GetFlen()) + dec = Int(ft.GetDecimal()) + } + + var defVal Lattice + if mysql.HasAutoIncrementFlag(ft.GetFlag()) || !mysql.HasNoDefaultValueFlag(ft.GetFlag()) { + defVal = Maybe(Singleton(ft.GetFlag() & flagMaskDefVal)) + } else { + defVal = Maybe(nil) + } + + return Tuple{ + FieldTp(ft.GetType()), + flen, + dec, + + // TODO: recognize if the remaining flags can be merged or not. + Singleton(ft.GetFlag() &^ (flagMaskDefVal | mysql.NotNullFlag | flagMaskKeys)), + Bool(!mysql.HasNotNullFlag(ft.GetFlag())), + Byte(encodeAntiKeys(ft.GetFlag())), + defVal, + + Singleton(ft.GetCharset()), + Singleton(ft.GetCollate()), + StringList(ft.GetElems()), + } +} + +func decodeFieldTypeFromLattice(tup Tuple) *types.FieldType { + lst := tup.Unwrap().([]interface{}) + + flags := lst[fieldTypeTupleIndexFlagSingleton].(uint) + flags |= decodeAntiKeys(lst[fieldTypeTupleIndexFlagAntiKeys].(byte)) + if !lst[fieldTypeTupleIndexFlagNull].(bool) { + flags |= mysql.NotNullFlag + } + if x, ok := lst[fieldTypeTupleIndexFlagDefVal].(uint); ok { + flags |= x + } else { + flags |= mysql.NoDefaultValueFlag + } + + return types.NewFieldTypeBuilder().SetType(lst[fieldTypeTupleIndexTp].(byte)).SetFlen(lst[fieldTypeTupleIndexFlen].(int)).SetDecimal(lst[fieldTypeTupleIndexDec].(int)).SetFlag(flags).SetCharset(lst[fieldTypeTupleIndexCharset].(string)).SetCollate(lst[fieldTypeTupleIndexCollate].(string)).SetElems(lst[fieldTypeTupleIndexElems].([]string)).BuildP() +} + +type typ struct{ Tuple } + +// Type is to create type. +func Type(ft *types.FieldType) typ { + return typ{Tuple: encodeFieldTypeToLattice(ft)} +} + +func (a typ) hasDefault() bool { + return a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap() != nil +} + +// setFlagForMissingColumn adjusts the flags of the type for filling in a +// missing column. Returns whether the column had no default values. +// If the column is AUTO_INCREMENT, returns an incompatible error, because such +// column cannot be part of any keys in the joined table which is invalid. +func (a typ) setFlagForMissingColumn() (hadNoDefault bool) { + a.Tuple[fieldTypeTupleIndexFlagAntiKeys] = Byte(notPartOfKeys) + defVal, ok := a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) + if !ok || mysql.HasNoDefaultValueFlag(defVal) { + a.Tuple[fieldTypeTupleIndexFlagDefVal] = Maybe(Singleton(defVal &^ mysql.NoDefaultValueFlag)) + return true + } + return false +} + +func (a typ) isNotNull() bool { + return !a.Tuple[fieldTypeTupleIndexFlagNull].Unwrap().(bool) +} + +func (a typ) inAutoIncrement() bool { + defVal, ok := a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) + return ok && mysql.HasAutoIncrementFlag(defVal) +} + +func (a typ) setAntiKeyFlags(flag uint) { + a.Tuple[fieldTypeTupleIndexFlagAntiKeys] = Byte(encodeAntiKeys(flag)) +} + +func (a typ) getStandardDefaultValue() interface{} { + var tail string + if dec := a.Tuple[fieldTypeTupleIndexDec].Unwrap().(int); dec > 0 { + tail = "." + strings.Repeat("0", dec) + } + + switch a.Tuple[fieldTypeTupleIndexTp].Unwrap().(byte) { + case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: + return "0" + case mysql.TypeTimestamp, mysql.TypeDatetime: + return "0000-00-00 00:00:00" + tail + case mysql.TypeDate: + return "0000-00-00" + case mysql.TypeDuration: + return "00:00:00" + tail + case mysql.TypeYear: + return "0000" + case mysql.TypeJSON: + return "null" + case mysql.TypeEnum: + return a.Tuple[fieldTypeTupleIndexElems].(StringList)[0] + case mysql.TypeString: + // ref https://github.com/pingcap/tidb/blob/66948b2fd9bec8ea11644770a2fa746c7eba1a1f/ddl/ddl_api.go#L3916 + if a.Tuple[fieldTypeTupleIndexCollate].Unwrap().(string) == charset.CollationBin { + return string(make([]byte, a.Tuple[fieldTypeTupleIndexFlen].Unwrap().(int))) + } + return "" + default: + return "" + } +} + +func (a typ) clone() typ { + return typ{Tuple: append(make(Tuple, 0, len(a.Tuple)), a.Tuple...)} +} + +func (a typ) Unwrap() interface{} { + return decodeFieldTypeFromLattice(a.Tuple) +} + +func (a typ) Compare(other Lattice) (int, error) { + if b, ok := other.(typ); ok { + return a.Tuple.Compare(b.Tuple) + } + return 0, typeMismatchError(a, other) +} + +func (a typ) Join(other Lattice) (Lattice, error) { + if b, ok := other.(typ); ok { + genJoin, err := a.Tuple.Join(b.Tuple) + if err != nil { + return nil, err + } + join := genJoin.(Tuple) + + // Special check: we can't have an AUTO_INCREMENT column without being a KEY. + x, ok := join[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) + if ok && x&mysql.AutoIncrementFlag != 0 && join[fieldTypeTupleIndexFlagAntiKeys].Unwrap() == notPartOfKeys { + return nil, &IncompatibleError{Msg: ErrMsgAutoTypeWithoutKey} + } + + return typ{Tuple: join}, nil + } + return nil, typeMismatchError(a, other) +} diff --git a/util/schemacmp/type_test.go b/pkg/util/schemacmp/type_test.go similarity index 98% rename from util/schemacmp/type_test.go rename to pkg/util/schemacmp/type_test.go index 8345b2c7c8b57..0c9b87da507fb 100644 --- a/util/schemacmp/type_test.go +++ b/pkg/util/schemacmp/type_test.go @@ -17,9 +17,9 @@ package schemacmp_test import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - . "github.com/pingcap/tidb/util/schemacmp" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + . "github.com/pingcap/tidb/pkg/util/schemacmp" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/schemacmp/util.go b/pkg/util/schemacmp/util.go new file mode 100644 index 0000000000000..771a745cc3450 --- /dev/null +++ b/pkg/util/schemacmp/util.go @@ -0,0 +1,69 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package schemacmp + +import "github.com/pingcap/tidb/pkg/parser/mysql" + +// compareMySQLIntegerType compares two MySQL integer types, +// return -1 if a < b, 0 if a == b and 1 if a > b. +func compareMySQLIntegerType(a, b byte) int { + if a == b { + return 0 + } + + // TypeTiny(1) < TypeShort(2) < TypeInt24(9) < TypeLong(3) < TypeLonglong(8) + switch { + case a == mysql.TypeInt24: + if b <= mysql.TypeShort { + return 1 + } + return -1 + case b == mysql.TypeInt24: + if a <= mysql.TypeShort { + return -1 + } + return 1 + case a < b: + return -1 + default: + return 1 + } +} + +// compareMySQLBlobType compares two MySQL blob types, +// return -1 if a < b, 0 if a == b and 1 if a > b. +func compareMySQLBlobType(a, b byte) int { + if a == b { + return 0 + } + + // TypeTinyBlob(0xf9, 249) < TypeBlob(0xfc, 252) < TypeMediumBlob(0xfa, 250) < TypeLongBlob(0xfb, 251) + switch { + case a == mysql.TypeBlob: + if b == mysql.TypeTinyBlob { + return 1 + } + return -1 + case b == mysql.TypeBlob: + if a == mysql.TypeTinyBlob { + return -1 + } + return 1 + case a < b: + return -1 + default: + return 1 + } +} diff --git a/util/security.go b/pkg/util/security.go similarity index 100% rename from util/security.go rename to pkg/util/security.go diff --git a/util/security_test.go b/pkg/util/security_test.go similarity index 99% rename from util/security_test.go rename to pkg/util/security_test.go index 98473f1f123a6..e34d8420487b6 100644 --- a/util/security_test.go +++ b/pkg/util/security_test.go @@ -33,7 +33,7 @@ import ( "testing" "time" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/selection/BUILD.bazel b/pkg/util/selection/BUILD.bazel new file mode 100644 index 0000000000000..ffa31813e67d9 --- /dev/null +++ b/pkg/util/selection/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "selection", + srcs = ["selection.go"], + importpath = "github.com/pingcap/tidb/pkg/util/selection", + visibility = ["//visibility:public"], +) + +go_test( + name = "selection_test", + timeout = "short", + srcs = [ + "main_test.go", + "selection_test.go", + ], + embed = [":selection"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/selection/main_test.go b/pkg/util/selection/main_test.go new file mode 100644 index 0000000000000..c8b0050402311 --- /dev/null +++ b/pkg/util/selection/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package selection + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/selection/selection.go b/pkg/util/selection/selection.go similarity index 100% rename from util/selection/selection.go rename to pkg/util/selection/selection.go diff --git a/util/selection/selection_test.go b/pkg/util/selection/selection_test.go similarity index 100% rename from util/selection/selection_test.go rename to pkg/util/selection/selection_test.go diff --git a/pkg/util/sem/BUILD.bazel b/pkg/util/sem/BUILD.bazel new file mode 100644 index 0000000000000..7f9c79450f76a --- /dev/null +++ b/pkg/util/sem/BUILD.bazel @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sem", + srcs = ["sem.go"], + importpath = "github.com/pingcap/tidb/pkg/util/sem", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/util/logutil", + ], +) + +go_test( + name = "sem_test", + timeout = "short", + srcs = [ + "main_test.go", + "sem_test.go", + ], + embed = [":sem"], + flaky = True, + deps = [ + "//pkg/parser/mysql", + "//pkg/sessionctx/variable", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/sem/main_test.go b/pkg/util/sem/main_test.go new file mode 100644 index 0000000000000..5bb48dd2b821b --- /dev/null +++ b/pkg/util/sem/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package sem + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/sem/sem.go b/pkg/util/sem/sem.go similarity index 97% rename from util/sem/sem.go rename to pkg/util/sem/sem.go index 2724077d78286..ecc3e56bb1ece 100644 --- a/util/sem/sem.go +++ b/pkg/util/sem/sem.go @@ -19,9 +19,9 @@ import ( "strings" "sync/atomic" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/logutil" ) const ( diff --git a/util/sem/sem_test.go b/pkg/util/sem/sem_test.go similarity index 97% rename from util/sem/sem_test.go rename to pkg/util/sem/sem_test.go index d19c2af27adcb..2645cb79b21de 100644 --- a/util/sem/sem_test.go +++ b/pkg/util/sem/sem_test.go @@ -17,8 +17,8 @@ package sem import ( "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/variable" "github.com/stretchr/testify/assert" ) diff --git a/pkg/util/servermemorylimit/BUILD.bazel b/pkg/util/servermemorylimit/BUILD.bazel new file mode 100644 index 0000000000000..5c81b4743a315 --- /dev/null +++ b/pkg/util/servermemorylimit/BUILD.bazel @@ -0,0 +1,32 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "servermemorylimit", + srcs = ["servermemorylimit.go"], + importpath = "github.com/pingcap/tidb/pkg/util/servermemorylimit", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util", + "//pkg/util/logutil", + "//pkg/util/memory", + "@com_github_pingcap_failpoint//:failpoint", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "servermemorylimit_test", + timeout = "short", + srcs = ["servermemorylimit_test.go"], + embed = [":servermemorylimit"], + flaky = True, + race = "on", + deps = [ + "//pkg/types", + "//pkg/util", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/servermemorylimit/servermemorylimit.go b/pkg/util/servermemorylimit/servermemorylimit.go similarity index 97% rename from util/servermemorylimit/servermemorylimit.go rename to pkg/util/servermemorylimit/servermemorylimit.go index cee5acd7f16b3..8c5c12ff3700d 100644 --- a/util/servermemorylimit/servermemorylimit.go +++ b/pkg/util/servermemorylimit/servermemorylimit.go @@ -22,11 +22,11 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/memory" atomicutil "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/util/servermemorylimit/servermemorylimit_test.go b/pkg/util/servermemorylimit/servermemorylimit_test.go similarity index 96% rename from util/servermemorylimit/servermemorylimit_test.go rename to pkg/util/servermemorylimit/servermemorylimit_test.go index 35c96942d29b5..841af29702b7f 100644 --- a/util/servermemorylimit/servermemorylimit_test.go +++ b/pkg/util/servermemorylimit/servermemorylimit_test.go @@ -19,8 +19,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/set/BUILD.bazel b/pkg/util/set/BUILD.bazel new file mode 100644 index 0000000000000..443f3c384023a --- /dev/null +++ b/pkg/util/set/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "set", + srcs = [ + "float64_set.go", + "int_set.go", + "mem_aware_map.go", + "set_with_memory_usage.go", + "string_set.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/set", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/hack", + "//pkg/util/memory", + "@org_golang_x_exp//maps", + ], +) + +go_test( + name = "set_test", + timeout = "short", + srcs = [ + "float64_set_test.go", + "int_set_test.go", + "main_test.go", + "mem_aware_map_test.go", + "set_with_memory_usage_test.go", + "string_set_test.go", + ], + embed = [":set"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//assert", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/set/float64_set.go b/pkg/util/set/float64_set.go similarity index 100% rename from util/set/float64_set.go rename to pkg/util/set/float64_set.go diff --git a/util/set/float64_set_test.go b/pkg/util/set/float64_set_test.go similarity index 100% rename from util/set/float64_set_test.go rename to pkg/util/set/float64_set_test.go diff --git a/util/set/int_set.go b/pkg/util/set/int_set.go similarity index 100% rename from util/set/int_set.go rename to pkg/util/set/int_set.go diff --git a/util/set/int_set_test.go b/pkg/util/set/int_set_test.go similarity index 100% rename from util/set/int_set_test.go rename to pkg/util/set/int_set_test.go diff --git a/pkg/util/set/main_test.go b/pkg/util/set/main_test.go new file mode 100644 index 0000000000000..cb2ed37af7781 --- /dev/null +++ b/pkg/util/set/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package set + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/set/mem_aware_map.go b/pkg/util/set/mem_aware_map.go similarity index 98% rename from util/set/mem_aware_map.go rename to pkg/util/set/mem_aware_map.go index da1cb227af306..d2b8a564a783f 100644 --- a/util/set/mem_aware_map.go +++ b/pkg/util/set/mem_aware_map.go @@ -17,7 +17,7 @@ package set import ( "math" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/hack" ) // MemAwareMap is a map which is aware of its memory usage. It's adapted from SetWithMemoryUsage. diff --git a/util/set/mem_aware_map_test.go b/pkg/util/set/mem_aware_map_test.go similarity index 100% rename from util/set/mem_aware_map_test.go rename to pkg/util/set/mem_aware_map_test.go diff --git a/util/set/set_with_memory_usage.go b/pkg/util/set/set_with_memory_usage.go similarity index 97% rename from util/set/set_with_memory_usage.go rename to pkg/util/set/set_with_memory_usage.go index 513d9e3057d76..e59ffa9ffb634 100644 --- a/util/set/set_with_memory_usage.go +++ b/pkg/util/set/set_with_memory_usage.go @@ -15,8 +15,8 @@ package set import ( - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/memory" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/memory" ) // StringSetWithMemoryUsage is a string set with memory usage. diff --git a/util/set/set_with_memory_usage_test.go b/pkg/util/set/set_with_memory_usage_test.go similarity index 100% rename from util/set/set_with_memory_usage_test.go rename to pkg/util/set/set_with_memory_usage_test.go diff --git a/util/set/string_set.go b/pkg/util/set/string_set.go similarity index 100% rename from util/set/string_set.go rename to pkg/util/set/string_set.go diff --git a/util/set/string_set_test.go b/pkg/util/set/string_set_test.go similarity index 100% rename from util/set/string_set_test.go rename to pkg/util/set/string_set_test.go diff --git a/pkg/util/signal/BUILD.bazel b/pkg/util/signal/BUILD.bazel new file mode 100644 index 0000000000000..2275bd8333627 --- /dev/null +++ b/pkg/util/signal/BUILD.bazel @@ -0,0 +1,63 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "signal", + srcs = [ + "signal_posix.go", + "signal_wasm.go", + "signal_windows.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/signal", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:android": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:illumos": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:ios": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "@io_bazel_rules_go//go/platform:windows": [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], + "//conditions:default": [], + }), +) diff --git a/util/signal/signal_posix.go b/pkg/util/signal/signal_posix.go similarity index 97% rename from util/signal/signal_posix.go rename to pkg/util/signal/signal_posix.go index 8b416b93c543e..cb3680ce202c2 100644 --- a/util/signal/signal_posix.go +++ b/pkg/util/signal/signal_posix.go @@ -22,7 +22,7 @@ import ( "runtime" "syscall" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/signal/signal_wasm.go b/pkg/util/signal/signal_wasm.go similarity index 100% rename from util/signal/signal_wasm.go rename to pkg/util/signal/signal_wasm.go diff --git a/util/signal/signal_windows.go b/pkg/util/signal/signal_windows.go similarity index 96% rename from util/signal/signal_windows.go rename to pkg/util/signal/signal_windows.go index 420329f90081c..becf2fd8412e8 100644 --- a/util/signal/signal_windows.go +++ b/pkg/util/signal/signal_windows.go @@ -20,7 +20,7 @@ import ( "os/signal" "syscall" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/util/size/BUILD.bazel b/pkg/util/size/BUILD.bazel new file mode 100644 index 0000000000000..7fb2f6f6d0974 --- /dev/null +++ b/pkg/util/size/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "size", + srcs = ["size.go"], + importpath = "github.com/pingcap/tidb/pkg/util/size", + visibility = ["//visibility:public"], +) diff --git a/util/size/size.go b/pkg/util/size/size.go similarity index 100% rename from util/size/size.go rename to pkg/util/size/size.go diff --git a/pkg/util/skip/BUILD.bazel b/pkg/util/skip/BUILD.bazel new file mode 100644 index 0000000000000..571ba097074d9 --- /dev/null +++ b/pkg/util/skip/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "skip", + srcs = ["skip.go"], + importpath = "github.com/pingcap/tidb/pkg/util/skip", + visibility = ["//visibility:public"], +) diff --git a/util/skip/skip.go b/pkg/util/skip/skip.go similarity index 100% rename from util/skip/skip.go rename to pkg/util/skip/skip.go diff --git a/pkg/util/sli/BUILD.bazel b/pkg/util/sli/BUILD.bazel new file mode 100644 index 0000000000000..40218d823e406 --- /dev/null +++ b/pkg/util/sli/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "sli", + srcs = ["sli.go"], + importpath = "github.com/pingcap/tidb/pkg/util/sli", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_pingcap_failpoint//:failpoint", + ], +) diff --git a/util/sli/sli.go b/pkg/util/sli/sli.go similarity index 98% rename from util/sli/sli.go rename to pkg/util/sli/sli.go index 3332e06464e47..4acdee79e5e09 100644 --- a/util/sli/sli.go +++ b/pkg/util/sli/sli.go @@ -19,7 +19,7 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/metrics" + "github.com/pingcap/tidb/pkg/metrics" ) // TxnWriteThroughputSLI uses to report transaction write throughput metrics for SLI. diff --git a/pkg/util/slice/BUILD.bazel b/pkg/util/slice/BUILD.bazel new file mode 100644 index 0000000000000..946ff90ddc408 --- /dev/null +++ b/pkg/util/slice/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "slice", + srcs = ["slice.go"], + importpath = "github.com/pingcap/tidb/pkg/util/slice", + visibility = ["//visibility:public"], +) + +go_test( + name = "slice_test", + timeout = "short", + srcs = [ + "main_test.go", + "slice_test.go", + ], + embed = [":slice"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/slice/main_test.go b/pkg/util/slice/main_test.go new file mode 100644 index 0000000000000..e685892f7a16f --- /dev/null +++ b/pkg/util/slice/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package slice + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/slice/slice.go b/pkg/util/slice/slice.go similarity index 100% rename from util/slice/slice.go rename to pkg/util/slice/slice.go diff --git a/util/slice/slice_test.go b/pkg/util/slice/slice_test.go similarity index 100% rename from util/slice/slice_test.go rename to pkg/util/slice/slice_test.go diff --git a/pkg/util/sqlexec/BUILD.bazel b/pkg/util/sqlexec/BUILD.bazel new file mode 100644 index 0000000000000..613d6dc7758e6 --- /dev/null +++ b/pkg/util/sqlexec/BUILD.bazel @@ -0,0 +1,39 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "sqlexec", + srcs = [ + "restricted_sql_executor.go", + "simple_record_set.go", + "utils.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/sqlexec", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/parser/ast", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/hack", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_kvproto//pkg/kvrpcpb", + ], +) + +go_test( + name = "sqlexec_test", + timeout = "short", + srcs = [ + "main_test.go", + "utils_test.go", + ], + embed = [":sqlexec"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/sqlexec/main_test.go b/pkg/util/sqlexec/main_test.go new file mode 100644 index 0000000000000..15bfeb81c3c60 --- /dev/null +++ b/pkg/util/sqlexec/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package sqlexec + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/sqlexec/mock/BUILD.bazel b/pkg/util/sqlexec/mock/BUILD.bazel new file mode 100644 index 0000000000000..6803a1ba28cb7 --- /dev/null +++ b/pkg/util/sqlexec/mock/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mock", + srcs = ["restricted_sql_executor_mock.go"], + importpath = "github.com/pingcap/tidb/pkg/util/sqlexec/mock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/ast", + "//pkg/util/chunk", + "//pkg/util/sqlexec", + "@org_uber_go_mock//gomock", + ], +) diff --git a/util/sqlexec/mock/restricted_sql_executor_mock.go b/pkg/util/sqlexec/mock/restricted_sql_executor_mock.go similarity index 94% rename from util/sqlexec/mock/restricted_sql_executor_mock.go rename to pkg/util/sqlexec/mock/restricted_sql_executor_mock.go index 1ebf1079db430..49d5f3b05e14e 100644 --- a/util/sqlexec/mock/restricted_sql_executor_mock.go +++ b/pkg/util/sqlexec/mock/restricted_sql_executor_mock.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/pingcap/tidb/util/sqlexec (interfaces: RestrictedSQLExecutor) +// Source: github.com/pingcap/tidb/pkg/util/sqlexec (interfaces: RestrictedSQLExecutor) // Package mock is a generated GoMock package. package mock @@ -8,9 +8,9 @@ import ( context "context" reflect "reflect" - ast "github.com/pingcap/tidb/parser/ast" - chunk "github.com/pingcap/tidb/util/chunk" - sqlexec "github.com/pingcap/tidb/util/sqlexec" + ast "github.com/pingcap/tidb/pkg/parser/ast" + chunk "github.com/pingcap/tidb/pkg/util/chunk" + sqlexec "github.com/pingcap/tidb/pkg/util/sqlexec" gomock "go.uber.org/mock/gomock" ) diff --git a/util/sqlexec/restricted_sql_executor.go b/pkg/util/sqlexec/restricted_sql_executor.go similarity index 97% rename from util/sqlexec/restricted_sql_executor.go rename to pkg/util/sqlexec/restricted_sql_executor.go index 3b576d546ad5a..9401b20e63e36 100644 --- a/util/sqlexec/restricted_sql_executor.go +++ b/pkg/util/sqlexec/restricted_sql_executor.go @@ -18,11 +18,11 @@ import ( "context" "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/util/chunk" ) // RestrictedSQLExecutor is an interface provides executing restricted sql statement. diff --git a/util/sqlexec/simple_record_set.go b/pkg/util/sqlexec/simple_record_set.go similarity index 93% rename from util/sqlexec/simple_record_set.go rename to pkg/util/sqlexec/simple_record_set.go index 4cb0d1e8d8df8..58125a940db82 100644 --- a/util/sqlexec/simple_record_set.go +++ b/pkg/util/sqlexec/simple_record_set.go @@ -17,9 +17,9 @@ package sqlexec import ( "context" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" + "github.com/pingcap/tidb/pkg/parser/ast" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/chunk" ) // SimpleRecordSet is a simple implementation of RecordSet. All values are known when creating SimpleRecordSet. diff --git a/util/sqlexec/utils.go b/pkg/util/sqlexec/utils.go similarity index 99% rename from util/sqlexec/utils.go rename to pkg/util/sqlexec/utils.go index 3613c6f11f1bd..877da4b94af6f 100644 --- a/util/sqlexec/utils.go +++ b/pkg/util/sqlexec/utils.go @@ -23,7 +23,7 @@ import ( "time" "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/util/hack" ) func reserveBuffer(buf []byte, appendSize int) []byte { diff --git a/util/sqlexec/utils_test.go b/pkg/util/sqlexec/utils_test.go similarity index 100% rename from util/sqlexec/utils_test.go rename to pkg/util/sqlexec/utils_test.go diff --git a/pkg/util/stmtsummary/BUILD.bazel b/pkg/util/stmtsummary/BUILD.bazel new file mode 100644 index 0000000000000..ce2b1cb12bc07 --- /dev/null +++ b/pkg/util/stmtsummary/BUILD.bazel @@ -0,0 +1,59 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stmtsummary", + srcs = [ + "evicted.go", + "reader.go", + "statement_summary.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/stmtsummary", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util/chunk", + "//pkg/util/execdetails", + "//pkg/util/hack", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/plancodec", + "//pkg/util/set", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_tikv_client_go_v2//util", + "@org_golang_x_exp//maps", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "stmtsummary_test", + timeout = "short", + srcs = [ + "evicted_test.go", + "main_test.go", + "statement_summary_test.go", + ], + embed = [":stmtsummary"], + flaky = True, + shard_count = 24, + deps = [ + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/execdetails", + "//pkg/util/plancodec", + "@com_github_pingcap_log//:log", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/stmtsummary/evicted.go b/pkg/util/stmtsummary/evicted.go similarity index 99% rename from util/stmtsummary/evicted.go rename to pkg/util/stmtsummary/evicted.go index be216c7414bdd..580029dad12ce 100644 --- a/util/stmtsummary/evicted.go +++ b/pkg/util/stmtsummary/evicted.go @@ -20,8 +20,8 @@ import ( "sync" "time" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" ) // stmtSummaryByDigestEvicted contents digests evicted from stmtSummaryByDigestMap diff --git a/util/stmtsummary/evicted_test.go b/pkg/util/stmtsummary/evicted_test.go similarity index 99% rename from util/stmtsummary/evicted_test.go rename to pkg/util/stmtsummary/evicted_test.go index 112d42037525f..948ef249c81f0 100644 --- a/util/stmtsummary/evicted_test.go +++ b/pkg/util/stmtsummary/evicted_test.go @@ -23,8 +23,8 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/stmtsummary/main_test.go b/pkg/util/stmtsummary/main_test.go new file mode 100644 index 0000000000000..dd39f90bd3673 --- /dev/null +++ b/pkg/util/stmtsummary/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/stmtsummary/reader.go b/pkg/util/stmtsummary/reader.go new file mode 100644 index 0000000000000..4ecaf59bdd004 --- /dev/null +++ b/pkg/util/stmtsummary/reader.go @@ -0,0 +1,635 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "fmt" + "strings" + "time" + + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/set" + "go.uber.org/zap" +) + +// stmtSummaryReader uses to read the statement summaries data and convert to []datum row. +type stmtSummaryReader struct { + user *auth.UserIdentity + // If the user has the 'PROCESS' privilege, he can read all the statements. + hasProcessPriv bool + columns []*model.ColumnInfo + instanceAddr string + ssMap *stmtSummaryByDigestMap + columnValueFactories []columnValueFactory + checker *stmtSummaryChecker + tz *time.Location +} + +// NewStmtSummaryReader return a new statement summaries reader. +func NewStmtSummaryReader(user *auth.UserIdentity, hasProcessPriv bool, cols []*model.ColumnInfo, instanceAddr string, tz *time.Location) *stmtSummaryReader { + reader := &stmtSummaryReader{ + user: user, + hasProcessPriv: hasProcessPriv, + columns: cols, + instanceAddr: instanceAddr, + ssMap: StmtSummaryByDigestMap, + tz: tz, + } + // initialize column value factories. + reader.columnValueFactories = make([]columnValueFactory, len(reader.columns)) + for i, col := range reader.columns { + factory, ok := columnValueFactoryMap[col.Name.O] + if !ok { + panic(fmt.Sprintf("should never happen, should register new column %v into columnValueFactoryMap", col.Name.O)) + } + reader.columnValueFactories[i] = factory + } + return reader +} + +// GetStmtSummaryCurrentRows gets all current statement summaries rows. +func (ssr *stmtSummaryReader) GetStmtSummaryCurrentRows() [][]types.Datum { + ssMap := ssr.ssMap + ssMap.Lock() + values := ssMap.summaryMap.Values() + beginTime := ssMap.beginTimeForCurInterval + other := ssMap.other + ssMap.Unlock() + + rows := make([][]types.Datum, 0, len(values)) + for _, value := range values { + ssbd := value.(*stmtSummaryByDigest) + if ssr.checker != nil && !ssr.checker.isDigestValid(ssbd.digest) { + continue + } + record := ssr.getStmtByDigestRow(ssbd, beginTime) + if record != nil { + rows = append(rows, record) + } + } + if ssr.checker == nil { + if otherDatum := ssr.getStmtEvictedOtherRow(other); otherDatum != nil { + rows = append(rows, otherDatum) + } + } + return rows +} + +// GetStmtSummaryHistoryRows gets all history statement summaries rows. +func (ssr *stmtSummaryReader) GetStmtSummaryHistoryRows() [][]types.Datum { + ssMap := ssr.ssMap + ssMap.Lock() + values := ssMap.summaryMap.Values() + other := ssMap.other + ssMap.Unlock() + + historySize := ssMap.historySize() + rows := make([][]types.Datum, 0, len(values)*historySize) + for _, value := range values { + records := ssr.getStmtByDigestHistoryRow(value.(*stmtSummaryByDigest), historySize) + rows = append(rows, records...) + } + + if ssr.checker == nil { + otherDatum := ssr.getStmtEvictedOtherHistoryRow(other, historySize) + rows = append(rows, otherDatum...) + } + return rows +} + +func (ssr *stmtSummaryReader) SetChecker(checker *stmtSummaryChecker) { + ssr.checker = checker +} + +func (ssr *stmtSummaryReader) getStmtByDigestRow(ssbd *stmtSummaryByDigest, beginTimeForCurInterval int64) []types.Datum { + var ssElement *stmtSummaryByDigestElement + + ssbd.Lock() + if ssbd.initialized && ssbd.history.Len() > 0 { + ssElement = ssbd.history.Back().Value.(*stmtSummaryByDigestElement) + } + ssbd.Unlock() + + // `ssElement` is lazy expired, so expired elements could also be read. + // `beginTime` won't change since `ssElement` is created, so locking is not needed here. + if ssElement == nil || ssElement.beginTime < beginTimeForCurInterval { + return nil + } + return ssr.getStmtByDigestElementRow(ssElement, ssbd) +} + +func (ssr *stmtSummaryReader) getStmtByDigestElementRow(ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) []types.Datum { + ssElement.Lock() + defer ssElement.Unlock() + isAuthed := true + if ssr.user != nil && !ssr.hasProcessPriv { + _, isAuthed = ssElement.authUsers[ssr.user.Username] + } + if !isAuthed { + return nil + } + + datums := make([]types.Datum, len(ssr.columnValueFactories)) + for i, factory := range ssr.columnValueFactories { + datums[i] = types.NewDatum(factory(ssr, ssElement, ssbd)) + } + return datums +} + +func (ssr *stmtSummaryReader) getStmtByDigestHistoryRow(ssbd *stmtSummaryByDigest, historySize int) [][]types.Datum { + // Collect all history summaries to an array. + ssElements := ssbd.collectHistorySummaries(ssr.checker, historySize) + + rows := make([][]types.Datum, 0, len(ssElements)) + for _, ssElement := range ssElements { + record := ssr.getStmtByDigestElementRow(ssElement, ssbd) + if record != nil { + rows = append(rows, record) + } + } + return rows +} + +func (ssr *stmtSummaryReader) getStmtEvictedOtherRow(ssbde *stmtSummaryByDigestEvicted) []types.Datum { + var seElement *stmtSummaryByDigestEvictedElement + + ssbde.Lock() + if ssbde.history.Len() > 0 { + seElement = ssbde.history.Back().Value.(*stmtSummaryByDigestEvictedElement) + } + ssbde.Unlock() + + if seElement == nil { + return nil + } + + return ssr.getStmtByDigestElementRow(seElement.otherSummary, new(stmtSummaryByDigest)) +} + +func (ssr *stmtSummaryReader) getStmtEvictedOtherHistoryRow(ssbde *stmtSummaryByDigestEvicted, historySize int) [][]types.Datum { + // Collect all history summaries to an array. + ssbde.Lock() + seElements := ssbde.collectHistorySummaries(historySize) + ssbde.Unlock() + rows := make([][]types.Datum, 0, len(seElements)) + + ssbd := new(stmtSummaryByDigest) + for _, seElement := range seElements { + record := ssr.getStmtByDigestElementRow(seElement.otherSummary, ssbd) + if record != nil { + rows = append(rows, record) + } + } + return rows +} + +type stmtSummaryChecker struct { + digests set.StringSet +} + +// NewStmtSummaryChecker return a new statement summaries checker. +func NewStmtSummaryChecker(digests set.StringSet) *stmtSummaryChecker { + return &stmtSummaryChecker{ + digests: digests, + } +} + +func (ssc *stmtSummaryChecker) isDigestValid(digest string) bool { + return ssc.digests.Exist(digest) +} + +// Statements summary table column name. +const ( + ClusterTableInstanceColumnNameStr = "INSTANCE" + SummaryBeginTimeStr = "SUMMARY_BEGIN_TIME" + SummaryEndTimeStr = "SUMMARY_END_TIME" + StmtTypeStr = "STMT_TYPE" + SchemaNameStr = "SCHEMA_NAME" + DigestStr = "DIGEST" + DigestTextStr = "DIGEST_TEXT" + TableNamesStr = "TABLE_NAMES" + IndexNamesStr = "INDEX_NAMES" + SampleUserStr = "SAMPLE_USER" + ExecCountStr = "EXEC_COUNT" + SumErrorsStr = "SUM_ERRORS" + SumWarningsStr = "SUM_WARNINGS" + SumLatencyStr = "SUM_LATENCY" + MaxLatencyStr = "MAX_LATENCY" + MinLatencyStr = "MIN_LATENCY" + AvgLatencyStr = "AVG_LATENCY" + AvgParseLatencyStr = "AVG_PARSE_LATENCY" + MaxParseLatencyStr = "MAX_PARSE_LATENCY" + AvgCompileLatencyStr = "AVG_COMPILE_LATENCY" + MaxCompileLatencyStr = "MAX_COMPILE_LATENCY" + SumCopTaskNumStr = "SUM_COP_TASK_NUM" + MaxCopProcessTimeStr = "MAX_COP_PROCESS_TIME" + MaxCopProcessAddressStr = "MAX_COP_PROCESS_ADDRESS" + MaxCopWaitTimeStr = "MAX_COP_WAIT_TIME" // #nosec G101 + MaxCopWaitAddressStr = "MAX_COP_WAIT_ADDRESS" // #nosec G101 + AvgProcessTimeStr = "AVG_PROCESS_TIME" + MaxProcessTimeStr = "MAX_PROCESS_TIME" + AvgWaitTimeStr = "AVG_WAIT_TIME" + MaxWaitTimeStr = "MAX_WAIT_TIME" + AvgBackoffTimeStr = "AVG_BACKOFF_TIME" + MaxBackoffTimeStr = "MAX_BACKOFF_TIME" + AvgTotalKeysStr = "AVG_TOTAL_KEYS" + MaxTotalKeysStr = "MAX_TOTAL_KEYS" + AvgProcessedKeysStr = "AVG_PROCESSED_KEYS" + MaxProcessedKeysStr = "MAX_PROCESSED_KEYS" + AvgRocksdbDeleteSkippedCountStr = "AVG_ROCKSDB_DELETE_SKIPPED_COUNT" + MaxRocksdbDeleteSkippedCountStr = "MAX_ROCKSDB_DELETE_SKIPPED_COUNT" + AvgRocksdbKeySkippedCountStr = "AVG_ROCKSDB_KEY_SKIPPED_COUNT" + MaxRocksdbKeySkippedCountStr = "MAX_ROCKSDB_KEY_SKIPPED_COUNT" + AvgRocksdbBlockCacheHitCountStr = "AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT" + MaxRocksdbBlockCacheHitCountStr = "MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT" + AvgRocksdbBlockReadCountStr = "AVG_ROCKSDB_BLOCK_READ_COUNT" + MaxRocksdbBlockReadCountStr = "MAX_ROCKSDB_BLOCK_READ_COUNT" + AvgRocksdbBlockReadByteStr = "AVG_ROCKSDB_BLOCK_READ_BYTE" + MaxRocksdbBlockReadByteStr = "MAX_ROCKSDB_BLOCK_READ_BYTE" + AvgPrewriteTimeStr = "AVG_PREWRITE_TIME" + MaxPrewriteTimeStr = "MAX_PREWRITE_TIME" + AvgCommitTimeStr = "AVG_COMMIT_TIME" + MaxCommitTimeStr = "MAX_COMMIT_TIME" + AvgGetCommitTsTimeStr = "AVG_GET_COMMIT_TS_TIME" + MaxGetCommitTsTimeStr = "MAX_GET_COMMIT_TS_TIME" + AvgCommitBackoffTimeStr = "AVG_COMMIT_BACKOFF_TIME" + MaxCommitBackoffTimeStr = "MAX_COMMIT_BACKOFF_TIME" + AvgResolveLockTimeStr = "AVG_RESOLVE_LOCK_TIME" + MaxResolveLockTimeStr = "MAX_RESOLVE_LOCK_TIME" + AvgLocalLatchWaitTimeStr = "AVG_LOCAL_LATCH_WAIT_TIME" + MaxLocalLatchWaitTimeStr = "MAX_LOCAL_LATCH_WAIT_TIME" + AvgWriteKeysStr = "AVG_WRITE_KEYS" + MaxWriteKeysStr = "MAX_WRITE_KEYS" + AvgWriteSizeStr = "AVG_WRITE_SIZE" + MaxWriteSizeStr = "MAX_WRITE_SIZE" + AvgPrewriteRegionsStr = "AVG_PREWRITE_REGIONS" + MaxPrewriteRegionsStr = "MAX_PREWRITE_REGIONS" + AvgTxnRetryStr = "AVG_TXN_RETRY" + MaxTxnRetryStr = "MAX_TXN_RETRY" + SumExecRetryStr = "SUM_EXEC_RETRY" + SumExecRetryTimeStr = "SUM_EXEC_RETRY_TIME" + SumBackoffTimesStr = "SUM_BACKOFF_TIMES" + BackoffTypesStr = "BACKOFF_TYPES" + AvgMemStr = "AVG_MEM" + MaxMemStr = "MAX_MEM" + AvgDiskStr = "AVG_DISK" + MaxDiskStr = "MAX_DISK" + AvgKvTimeStr = "AVG_KV_TIME" + AvgPdTimeStr = "AVG_PD_TIME" + AvgBackoffTotalTimeStr = "AVG_BACKOFF_TOTAL_TIME" + AvgWriteSQLRespTimeStr = "AVG_WRITE_SQL_RESP_TIME" + MaxResultRowsStr = "MAX_RESULT_ROWS" + MinResultRowsStr = "MIN_RESULT_ROWS" + AvgResultRowsStr = "AVG_RESULT_ROWS" + PreparedStr = "PREPARED" + AvgAffectedRowsStr = "AVG_AFFECTED_ROWS" + FirstSeenStr = "FIRST_SEEN" + LastSeenStr = "LAST_SEEN" + PlanInCacheStr = "PLAN_IN_CACHE" + PlanCacheHitsStr = "PLAN_CACHE_HITS" + PlanInBindingStr = "PLAN_IN_BINDING" + QuerySampleTextStr = "QUERY_SAMPLE_TEXT" + PrevSampleTextStr = "PREV_SAMPLE_TEXT" + PlanDigestStr = "PLAN_DIGEST" + PlanStr = "PLAN" + BinaryPlan = "BINARY_PLAN" + Charset = "CHARSET" + Collation = "COLLATION" + PlanHint = "PLAN_HINT" +) + +type columnValueFactory func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} + +var columnValueFactoryMap = map[string]columnValueFactory{ + ClusterTableInstanceColumnNameStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return reader.instanceAddr + }, + SummaryBeginTimeStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + beginTime := time.Unix(ssElement.beginTime, 0) + if beginTime.Location() != reader.tz { + beginTime = beginTime.In(reader.tz) + } + return types.NewTime(types.FromGoTime(beginTime), mysql.TypeTimestamp, 0) + }, + SummaryEndTimeStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + endTime := time.Unix(ssElement.endTime, 0) + if endTime.Location() != reader.tz { + endTime = endTime.In(reader.tz) + } + return types.NewTime(types.FromGoTime(endTime), mysql.TypeTimestamp, 0) + }, + StmtTypeStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return ssbd.stmtType + }, + SchemaNameStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(ssbd.schemaName) + }, + DigestStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(ssbd.digest) + }, + DigestTextStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return ssbd.normalizedSQL + }, + TableNamesStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(ssbd.tableNames) + }, + IndexNamesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(strings.Join(ssElement.indexNames, ",")) + }, + SampleUserStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + sampleUser := "" + for key := range ssElement.authUsers { + sampleUser = key + break + } + return convertEmptyToNil(sampleUser) + }, + ExecCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.execCount + }, + SumErrorsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sumErrors + }, + SumWarningsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sumWarnings + }, + SumLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.sumLatency) + }, + MaxLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxLatency) + }, + MinLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.minLatency) + }, + AvgLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumLatency), ssElement.execCount) + }, + AvgParseLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumParseLatency), ssElement.execCount) + }, + MaxParseLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxParseLatency) + }, + AvgCompileLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumCompileLatency), ssElement.execCount) + }, + MaxCompileLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxCompileLatency) + }, + SumCopTaskNumStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sumNumCopTasks + }, + MaxCopProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxCopProcessTime) + }, + MaxCopProcessAddressStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(ssElement.maxCopProcessAddress) + }, + MaxCopWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxCopWaitTime) + }, + MaxCopWaitAddressStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return convertEmptyToNil(ssElement.maxCopWaitAddress) + }, + AvgProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumProcessTime), ssElement.execCount) + }, + MaxProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxProcessTime) + }, + AvgWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumWaitTime), ssElement.execCount) + }, + MaxWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxWaitTime) + }, + AvgBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumBackoffTime), ssElement.execCount) + }, + MaxBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxBackoffTime) + }, + AvgTotalKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumTotalKeys, ssElement.execCount) + }, + MaxTotalKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxTotalKeys + }, + AvgProcessedKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumProcessedKeys, ssElement.execCount) + }, + MaxProcessedKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxProcessedKeys + }, + AvgRocksdbDeleteSkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumRocksdbDeleteSkippedCount), ssElement.execCount) + }, + MaxRocksdbDeleteSkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxRocksdbDeleteSkippedCount + }, + AvgRocksdbKeySkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumRocksdbKeySkippedCount), ssElement.execCount) + }, + MaxRocksdbKeySkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxRocksdbKeySkippedCount + }, + AvgRocksdbBlockCacheHitCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumRocksdbBlockCacheHitCount), ssElement.execCount) + }, + MaxRocksdbBlockCacheHitCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxRocksdbBlockCacheHitCount + }, + AvgRocksdbBlockReadCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumRocksdbBlockReadCount), ssElement.execCount) + }, + MaxRocksdbBlockReadCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxRocksdbBlockReadCount + }, + AvgRocksdbBlockReadByteStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumRocksdbBlockReadByte), ssElement.execCount) + }, + MaxRocksdbBlockReadByteStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxRocksdbBlockReadByte + }, + AvgPrewriteTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumPrewriteTime), ssElement.commitCount) + }, + MaxPrewriteTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxPrewriteTime) + }, + AvgCommitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumCommitTime), ssElement.commitCount) + }, + MaxCommitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxCommitTime) + }, + AvgGetCommitTsTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumGetCommitTsTime), ssElement.commitCount) + }, + MaxGetCommitTsTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxGetCommitTsTime) + }, + AvgCommitBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumCommitBackoffTime, ssElement.commitCount) + }, + MaxCommitBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxCommitBackoffTime + }, + AvgResolveLockTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumResolveLockTime, ssElement.commitCount) + }, + MaxResolveLockTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxResolveLockTime + }, + AvgLocalLatchWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumLocalLatchTime), ssElement.commitCount) + }, + MaxLocalLatchWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.maxLocalLatchTime) + }, + AvgWriteKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgFloat(ssElement.sumWriteKeys, ssElement.commitCount) + }, + MaxWriteKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxWriteKeys + }, + AvgWriteSizeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgFloat(ssElement.sumWriteSize, ssElement.commitCount) + }, + MaxWriteSizeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxWriteSize + }, + AvgPrewriteRegionsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgFloat(ssElement.sumPrewriteRegionNum, ssElement.commitCount) + }, + MaxPrewriteRegionsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int(ssElement.maxPrewriteRegionNum) + }, + AvgTxnRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgFloat(ssElement.sumTxnRetry, ssElement.commitCount) + }, + MaxTxnRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxTxnRetry + }, + SumExecRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int(ssElement.execRetryCount) + }, + SumExecRetryTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return int64(ssElement.execRetryTime) + }, + SumBackoffTimesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sumBackoffTimes + }, + BackoffTypesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return formatBackoffTypes(ssElement.backoffTypes) + }, + AvgMemStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumMem, ssElement.execCount) + }, + MaxMemStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxMem + }, + AvgDiskStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumDisk, ssElement.execCount) + }, + MaxDiskStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxDisk + }, + AvgKvTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumKVTotal), ssElement.commitCount) + }, + AvgPdTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumPDTotal), ssElement.commitCount) + }, + AvgBackoffTotalTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumBackoffTotal), ssElement.commitCount) + }, + AvgWriteSQLRespTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(int64(ssElement.sumWriteSQLRespTotal), ssElement.commitCount) + }, + MaxResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.maxResultRows + }, + MinResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.minResultRows + }, + AvgResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgInt(ssElement.sumResultRows, ssElement.execCount) + }, + PreparedStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.prepared + }, + AvgAffectedRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount) + }, + FirstSeenStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + firstSeen := ssElement.firstSeen + if firstSeen.Location() != reader.tz { + firstSeen = firstSeen.In(reader.tz) + } + return types.NewTime(types.FromGoTime(firstSeen), mysql.TypeTimestamp, 0) + }, + LastSeenStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + lastSeen := ssElement.lastSeen + if lastSeen.Location() != reader.tz { + lastSeen = lastSeen.In(reader.tz) + } + return types.NewTime(types.FromGoTime(lastSeen), mysql.TypeTimestamp, 0) + }, + PlanInCacheStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.planInCache + }, + PlanCacheHitsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.planCacheHits + }, + PlanInBindingStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.planInBinding + }, + QuerySampleTextStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sampleSQL + }, + PrevSampleTextStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.prevSQL + }, + PlanDigestStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { + return ssbd.planDigest + }, + PlanStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + plan, err := plancodec.DecodePlan(ssElement.samplePlan) + if err != nil { + logutil.BgLogger().Error("decode plan in statement summary failed", zap.String("plan", ssElement.samplePlan), zap.String("query", ssElement.sampleSQL), zap.Error(err)) + plan = "" + } + return plan + }, + BinaryPlan: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.sampleBinaryPlan + }, + Charset: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.charset + }, + Collation: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.collation + }, + PlanHint: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { + return ssElement.planHint + }, +} diff --git a/util/stmtsummary/statement_summary.go b/pkg/util/stmtsummary/statement_summary.go similarity index 99% rename from util/stmtsummary/statement_summary.go rename to pkg/util/stmtsummary/statement_summary.go index b1e8ab8fec4b5..b778171356d6c 100644 --- a/util/stmtsummary/statement_summary.go +++ b/pkg/util/stmtsummary/statement_summary.go @@ -28,12 +28,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/chunk" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/plancodec" "github.com/tikv/client-go/v2/util" atomic2 "go.uber.org/atomic" "golang.org/x/exp/maps" diff --git a/util/stmtsummary/statement_summary_test.go b/pkg/util/stmtsummary/statement_summary_test.go similarity index 99% rename from util/stmtsummary/statement_summary_test.go rename to pkg/util/stmtsummary/statement_summary_test.go index a208ae92ce036..e9ab25166f6a3 100644 --- a/util/stmtsummary/statement_summary_test.go +++ b/pkg/util/stmtsummary/statement_summary_test.go @@ -22,14 +22,14 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/plancodec" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/types" + tidbutil "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/plancodec" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/util" ) diff --git a/pkg/util/stmtsummary/v2/BUILD.bazel b/pkg/util/stmtsummary/v2/BUILD.bazel new file mode 100644 index 0000000000000..c427b84498ec7 --- /dev/null +++ b/pkg/util/stmtsummary/v2/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stmtsummary", + srcs = [ + "column.go", + "logger.go", + "reader.go", + "record.go", + "stmtsummary.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/stmtsummary/v2", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/sessionctx/stmtctx", + "//pkg/types", + "//pkg/util", + "//pkg/util/execdetails", + "//pkg/util/hack", + "//pkg/util/kvcache", + "//pkg/util/logutil", + "//pkg/util/plancodec", + "//pkg/util/set", + "//pkg/util/stmtsummary", + "@com_github_pingcap_log//:log", + "@com_github_tikv_client_go_v2//util", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + "@org_uber_go_zap//buffer", + "@org_uber_go_zap//zapcore", + ], +) + +go_test( + name = "stmtsummary_test", + timeout = "short", + srcs = [ + "column_test.go", + "main_test.go", + "reader_test.go", + "record_test.go", + "stmtsummary_test.go", + ], + embed = [":stmtsummary"], + flaky = True, + shard_count = 13, + deps = [ + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/testkit/testsetup", + "//pkg/types", + "//pkg/util", + "//pkg/util/set", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/stmtsummary/v2/column.go b/pkg/util/stmtsummary/v2/column.go new file mode 100644 index 0000000000000..ec5ad55510402 --- /dev/null +++ b/pkg/util/stmtsummary/v2/column.go @@ -0,0 +1,522 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "bytes" + "cmp" + "fmt" + "slices" + "strings" + "time" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "go.uber.org/zap" +) + +// Statements summary table column name. +const ( + ClusterTableInstanceColumnNameStr = "INSTANCE" + SummaryBeginTimeStr = "SUMMARY_BEGIN_TIME" + SummaryEndTimeStr = "SUMMARY_END_TIME" + StmtTypeStr = "STMT_TYPE" + SchemaNameStr = "SCHEMA_NAME" + DigestStr = "DIGEST" + DigestTextStr = "DIGEST_TEXT" + TableNamesStr = "TABLE_NAMES" + IndexNamesStr = "INDEX_NAMES" + SampleUserStr = "SAMPLE_USER" + ExecCountStr = "EXEC_COUNT" + SumErrorsStr = "SUM_ERRORS" + SumWarningsStr = "SUM_WARNINGS" + SumLatencyStr = "SUM_LATENCY" + MaxLatencyStr = "MAX_LATENCY" + MinLatencyStr = "MIN_LATENCY" + AvgLatencyStr = "AVG_LATENCY" + AvgParseLatencyStr = "AVG_PARSE_LATENCY" + MaxParseLatencyStr = "MAX_PARSE_LATENCY" + AvgCompileLatencyStr = "AVG_COMPILE_LATENCY" + MaxCompileLatencyStr = "MAX_COMPILE_LATENCY" + SumCopTaskNumStr = "SUM_COP_TASK_NUM" + MaxCopProcessTimeStr = "MAX_COP_PROCESS_TIME" + MaxCopProcessAddressStr = "MAX_COP_PROCESS_ADDRESS" + MaxCopWaitTimeStr = "MAX_COP_WAIT_TIME" // #nosec G101 + MaxCopWaitAddressStr = "MAX_COP_WAIT_ADDRESS" // #nosec G101 + AvgProcessTimeStr = "AVG_PROCESS_TIME" + MaxProcessTimeStr = "MAX_PROCESS_TIME" + AvgWaitTimeStr = "AVG_WAIT_TIME" + MaxWaitTimeStr = "MAX_WAIT_TIME" + AvgBackoffTimeStr = "AVG_BACKOFF_TIME" + MaxBackoffTimeStr = "MAX_BACKOFF_TIME" + AvgTotalKeysStr = "AVG_TOTAL_KEYS" + MaxTotalKeysStr = "MAX_TOTAL_KEYS" + AvgProcessedKeysStr = "AVG_PROCESSED_KEYS" + MaxProcessedKeysStr = "MAX_PROCESSED_KEYS" + AvgRocksdbDeleteSkippedCountStr = "AVG_ROCKSDB_DELETE_SKIPPED_COUNT" + MaxRocksdbDeleteSkippedCountStr = "MAX_ROCKSDB_DELETE_SKIPPED_COUNT" + AvgRocksdbKeySkippedCountStr = "AVG_ROCKSDB_KEY_SKIPPED_COUNT" + MaxRocksdbKeySkippedCountStr = "MAX_ROCKSDB_KEY_SKIPPED_COUNT" + AvgRocksdbBlockCacheHitCountStr = "AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT" + MaxRocksdbBlockCacheHitCountStr = "MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT" + AvgRocksdbBlockReadCountStr = "AVG_ROCKSDB_BLOCK_READ_COUNT" + MaxRocksdbBlockReadCountStr = "MAX_ROCKSDB_BLOCK_READ_COUNT" + AvgRocksdbBlockReadByteStr = "AVG_ROCKSDB_BLOCK_READ_BYTE" + MaxRocksdbBlockReadByteStr = "MAX_ROCKSDB_BLOCK_READ_BYTE" + AvgPrewriteTimeStr = "AVG_PREWRITE_TIME" + MaxPrewriteTimeStr = "MAX_PREWRITE_TIME" + AvgCommitTimeStr = "AVG_COMMIT_TIME" + MaxCommitTimeStr = "MAX_COMMIT_TIME" + AvgGetCommitTsTimeStr = "AVG_GET_COMMIT_TS_TIME" + MaxGetCommitTsTimeStr = "MAX_GET_COMMIT_TS_TIME" + AvgCommitBackoffTimeStr = "AVG_COMMIT_BACKOFF_TIME" + MaxCommitBackoffTimeStr = "MAX_COMMIT_BACKOFF_TIME" + AvgResolveLockTimeStr = "AVG_RESOLVE_LOCK_TIME" + MaxResolveLockTimeStr = "MAX_RESOLVE_LOCK_TIME" + AvgLocalLatchWaitTimeStr = "AVG_LOCAL_LATCH_WAIT_TIME" + MaxLocalLatchWaitTimeStr = "MAX_LOCAL_LATCH_WAIT_TIME" + AvgWriteKeysStr = "AVG_WRITE_KEYS" + MaxWriteKeysStr = "MAX_WRITE_KEYS" + AvgWriteSizeStr = "AVG_WRITE_SIZE" + MaxWriteSizeStr = "MAX_WRITE_SIZE" + AvgPrewriteRegionsStr = "AVG_PREWRITE_REGIONS" + MaxPrewriteRegionsStr = "MAX_PREWRITE_REGIONS" + AvgTxnRetryStr = "AVG_TXN_RETRY" + MaxTxnRetryStr = "MAX_TXN_RETRY" + SumExecRetryStr = "SUM_EXEC_RETRY" + SumExecRetryTimeStr = "SUM_EXEC_RETRY_TIME" + SumBackoffTimesStr = "SUM_BACKOFF_TIMES" + BackoffTypesStr = "BACKOFF_TYPES" + AvgMemStr = "AVG_MEM" + MaxMemStr = "MAX_MEM" + AvgDiskStr = "AVG_DISK" + MaxDiskStr = "MAX_DISK" + AvgKvTimeStr = "AVG_KV_TIME" + AvgPdTimeStr = "AVG_PD_TIME" + AvgBackoffTotalTimeStr = "AVG_BACKOFF_TOTAL_TIME" + AvgWriteSQLRespTimeStr = "AVG_WRITE_SQL_RESP_TIME" + MaxResultRowsStr = "MAX_RESULT_ROWS" + MinResultRowsStr = "MIN_RESULT_ROWS" + AvgResultRowsStr = "AVG_RESULT_ROWS" + PreparedStr = "PREPARED" + AvgAffectedRowsStr = "AVG_AFFECTED_ROWS" + FirstSeenStr = "FIRST_SEEN" + LastSeenStr = "LAST_SEEN" + PlanInCacheStr = "PLAN_IN_CACHE" + PlanCacheHitsStr = "PLAN_CACHE_HITS" + PlanInBindingStr = "PLAN_IN_BINDING" + QuerySampleTextStr = "QUERY_SAMPLE_TEXT" + PrevSampleTextStr = "PREV_SAMPLE_TEXT" + PlanDigestStr = "PLAN_DIGEST" + PlanStr = "PLAN" + BinaryPlan = "BINARY_PLAN" + Charset = "CHARSET" + Collation = "COLLATION" + PlanHint = "PLAN_HINT" +) + +type columnInfo interface { + getInstanceAddr() string + getTimeLocation() *time.Location +} + +type columnFactory func(info columnInfo, record *StmtRecord) interface{} + +var columnFactoryMap = map[string]columnFactory{ + ClusterTableInstanceColumnNameStr: func(info columnInfo, record *StmtRecord) interface{} { + return info.getInstanceAddr() + }, + SummaryBeginTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + beginTime := time.Unix(record.Begin, 0) + if beginTime.Location() != info.getTimeLocation() { + beginTime = beginTime.In(info.getTimeLocation()) + } + return types.NewTime(types.FromGoTime(beginTime), mysql.TypeTimestamp, 0) + }, + SummaryEndTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + endTime := time.Unix(record.End, 0) + if endTime.Location() != info.getTimeLocation() { + endTime = endTime.In(info.getTimeLocation()) + } + return types.NewTime(types.FromGoTime(endTime), mysql.TypeTimestamp, 0) + }, + StmtTypeStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.StmtType + }, + SchemaNameStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(record.SchemaName) + }, + DigestStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(record.Digest) + }, + DigestTextStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.NormalizedSQL + }, + TableNamesStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(record.TableNames) + }, + IndexNamesStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(strings.Join(record.IndexNames, ",")) + }, + SampleUserStr: func(info columnInfo, record *StmtRecord) interface{} { + sampleUser := "" + for key := range record.AuthUsers { + sampleUser = key + break + } + return convertEmptyToNil(sampleUser) + }, + ExecCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.ExecCount + }, + SumErrorsStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.SumErrors + }, + SumWarningsStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.SumWarnings + }, + SumLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.SumLatency) + }, + MaxLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxLatency) + }, + MinLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MinLatency) + }, + AvgLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumLatency), record.ExecCount) + }, + AvgParseLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumParseLatency), record.ExecCount) + }, + MaxParseLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxParseLatency) + }, + AvgCompileLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumCompileLatency), record.ExecCount) + }, + MaxCompileLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxCompileLatency) + }, + SumCopTaskNumStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.SumNumCopTasks + }, + MaxCopProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxCopProcessTime) + }, + MaxCopProcessAddressStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(record.MaxCopProcessAddress) + }, + MaxCopWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxCopWaitTime) + }, + MaxCopWaitAddressStr: func(info columnInfo, record *StmtRecord) interface{} { + return convertEmptyToNil(record.MaxCopWaitAddress) + }, + AvgProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumProcessTime), record.ExecCount) + }, + MaxProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxProcessTime) + }, + AvgWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumWaitTime), record.ExecCount) + }, + MaxWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxWaitTime) + }, + AvgBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumBackoffTime), record.ExecCount) + }, + MaxBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxBackoffTime) + }, + AvgTotalKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumTotalKeys, record.ExecCount) + }, + MaxTotalKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxTotalKeys + }, + AvgProcessedKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumProcessedKeys, record.ExecCount) + }, + MaxProcessedKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxProcessedKeys + }, + AvgRocksdbDeleteSkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumRocksdbDeleteSkippedCount), record.ExecCount) + }, + MaxRocksdbDeleteSkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxRocksdbDeleteSkippedCount + }, + AvgRocksdbKeySkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumRocksdbKeySkippedCount), record.ExecCount) + }, + MaxRocksdbKeySkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxRocksdbKeySkippedCount + }, + AvgRocksdbBlockCacheHitCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumRocksdbBlockCacheHitCount), record.ExecCount) + }, + MaxRocksdbBlockCacheHitCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxRocksdbBlockCacheHitCount + }, + AvgRocksdbBlockReadCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumRocksdbBlockReadCount), record.ExecCount) + }, + MaxRocksdbBlockReadCountStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxRocksdbBlockReadCount + }, + AvgRocksdbBlockReadByteStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumRocksdbBlockReadByte), record.ExecCount) + }, + MaxRocksdbBlockReadByteStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxRocksdbBlockReadByte + }, + AvgPrewriteTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumPrewriteTime), record.CommitCount) + }, + MaxPrewriteTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxPrewriteTime) + }, + AvgCommitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumCommitTime), record.CommitCount) + }, + MaxCommitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxCommitTime) + }, + AvgGetCommitTsTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumGetCommitTsTime), record.CommitCount) + }, + MaxGetCommitTsTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxGetCommitTsTime) + }, + AvgCommitBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumCommitBackoffTime, record.CommitCount) + }, + MaxCommitBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxCommitBackoffTime + }, + AvgResolveLockTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumResolveLockTime, record.CommitCount) + }, + MaxResolveLockTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxResolveLockTime + }, + AvgLocalLatchWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumLocalLatchTime), record.CommitCount) + }, + MaxLocalLatchWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.MaxLocalLatchTime) + }, + AvgWriteKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgFloat(record.SumWriteKeys, record.CommitCount) + }, + MaxWriteKeysStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxWriteKeys + }, + AvgWriteSizeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgFloat(record.SumWriteSize, record.CommitCount) + }, + MaxWriteSizeStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxWriteSize + }, + AvgPrewriteRegionsStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgFloat(record.SumPrewriteRegionNum, record.CommitCount) + }, + MaxPrewriteRegionsStr: func(info columnInfo, record *StmtRecord) interface{} { + return int(record.MaxPrewriteRegionNum) + }, + AvgTxnRetryStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgFloat(record.SumTxnRetry, record.CommitCount) + }, + MaxTxnRetryStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxTxnRetry + }, + SumExecRetryStr: func(info columnInfo, record *StmtRecord) interface{} { + return int(record.ExecRetryCount) + }, + SumExecRetryTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return int64(record.ExecRetryTime) + }, + SumBackoffTimesStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.SumBackoffTimes + }, + BackoffTypesStr: func(info columnInfo, record *StmtRecord) interface{} { + return formatBackoffTypes(record.BackoffTypes) + }, + AvgMemStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumMem, record.ExecCount) + }, + MaxMemStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxMem + }, + AvgDiskStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumDisk, record.ExecCount) + }, + MaxDiskStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxDisk + }, + AvgKvTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumKVTotal), record.CommitCount) + }, + AvgPdTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumPDTotal), record.CommitCount) + }, + AvgBackoffTotalTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumBackoffTotal), record.CommitCount) + }, + AvgWriteSQLRespTimeStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(int64(record.SumWriteSQLRespTotal), record.CommitCount) + }, + MaxResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MaxResultRows + }, + MinResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.MinResultRows + }, + AvgResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgInt(record.SumResultRows, record.ExecCount) + }, + PreparedStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.Prepared + }, + AvgAffectedRowsStr: func(info columnInfo, record *StmtRecord) interface{} { + return avgFloat(int64(record.SumAffectedRows), record.ExecCount) + }, + FirstSeenStr: func(info columnInfo, record *StmtRecord) interface{} { + firstSeen := record.FirstSeen + if firstSeen.Location() != info.getTimeLocation() { + firstSeen = firstSeen.In(info.getTimeLocation()) + } + return types.NewTime(types.FromGoTime(firstSeen), mysql.TypeTimestamp, 0) + }, + LastSeenStr: func(info columnInfo, record *StmtRecord) interface{} { + lastSeen := record.LastSeen + if lastSeen.Location() != info.getTimeLocation() { + lastSeen = lastSeen.In(info.getTimeLocation()) + } + return types.NewTime(types.FromGoTime(lastSeen), mysql.TypeTimestamp, 0) + }, + PlanInCacheStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.PlanInCache + }, + PlanCacheHitsStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.PlanCacheHits + }, + PlanInBindingStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.PlanInBinding + }, + QuerySampleTextStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.SampleSQL + }, + PrevSampleTextStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.PrevSQL + }, + PlanDigestStr: func(info columnInfo, record *StmtRecord) interface{} { + return record.PlanDigest + }, + PlanStr: func(info columnInfo, record *StmtRecord) interface{} { + plan, err := plancodec.DecodePlan(record.SamplePlan) + if err != nil { + logutil.BgLogger().Error("decode plan in statement summary failed", + zap.String("plan", record.SamplePlan), + zap.String("query", record.SampleSQL), zap.Error(err)) + plan = "" + } + return plan + }, + BinaryPlan: func(info columnInfo, record *StmtRecord) interface{} { + return record.SampleBinaryPlan + }, + Charset: func(info columnInfo, record *StmtRecord) interface{} { + return record.Charset + }, + Collation: func(info columnInfo, record *StmtRecord) interface{} { + return record.Collation + }, + PlanHint: func(info columnInfo, record *StmtRecord) interface{} { + return record.PlanHint + }, +} + +func makeColumnFactories(columns []*model.ColumnInfo) []columnFactory { + columnFactories := make([]columnFactory, len(columns)) + for i, col := range columns { + factory, ok := columnFactoryMap[col.Name.O] + if !ok { + panic(fmt.Sprintf("should never happen, should register new column %v into columnValueFactoryMap", col.Name.O)) + } + columnFactories[i] = factory + } + return columnFactories +} + +// Format the backoffType map to a string or nil. +func formatBackoffTypes(backoffMap map[string]int) interface{} { + type backoffStat struct { + backoffType string + count int + } + + size := len(backoffMap) + if size == 0 { + return nil + } + + backoffArray := make([]backoffStat, 0, len(backoffMap)) + for backoffType, count := range backoffMap { + backoffArray = append(backoffArray, backoffStat{backoffType, count}) + } + slices.SortFunc(backoffArray, func(i, j backoffStat) int { + return cmp.Compare(j.count, i.count) + }) + + var buffer bytes.Buffer + for index, stat := range backoffArray { + if _, err := fmt.Fprintf(&buffer, "%v:%d", stat.backoffType, stat.count); err != nil { + return "FORMAT ERROR" + } + if index < len(backoffArray)-1 { + buffer.WriteString(",") + } + } + return buffer.String() +} + +func avgInt(sum int64, count int64) int64 { + if count > 0 { + return sum / count + } + return 0 +} + +func avgFloat(sum int64, count int64) float64 { + if count > 0 { + return float64(sum) / float64(count) + } + return 0 +} + +func convertEmptyToNil(str string) interface{} { + if str == "" { + return nil + } + return str +} diff --git a/pkg/util/stmtsummary/v2/column_test.go b/pkg/util/stmtsummary/v2/column_test.go new file mode 100644 index 0000000000000..9e7af9cc09805 --- /dev/null +++ b/pkg/util/stmtsummary/v2/column_test.go @@ -0,0 +1,82 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/stretchr/testify/require" +) + +func TestColumn(t *testing.T) { + columns := []*model.ColumnInfo{ + {Name: model.NewCIStr(ClusterTableInstanceColumnNameStr)}, + {Name: model.NewCIStr(StmtTypeStr)}, + {Name: model.NewCIStr(SchemaNameStr)}, + {Name: model.NewCIStr(DigestStr)}, + {Name: model.NewCIStr(DigestTextStr)}, + {Name: model.NewCIStr(TableNamesStr)}, + {Name: model.NewCIStr(IndexNamesStr)}, + {Name: model.NewCIStr(SampleUserStr)}, + {Name: model.NewCIStr(ExecCountStr)}, + {Name: model.NewCIStr(SumLatencyStr)}, + {Name: model.NewCIStr(MaxLatencyStr)}, + } + factories := makeColumnFactories(columns) + info := GenerateStmtExecInfo4Test("digest") + record := NewStmtRecord(info) + record.Add(info) + for n, f := range factories { + column := f(mockColumnInfo{}, record) + switch columns[n].Name.O { + case ClusterTableInstanceColumnNameStr: + require.Equal(t, "instance_addr", column) + case StmtTypeStr: + require.Equal(t, record.StmtType, column) + case SchemaNameStr: + require.Equal(t, record.SchemaName, column) + case DigestStr: + require.Equal(t, record.Digest, column) + case DigestTextStr: + require.Equal(t, record.NormalizedSQL, column) + case TableNamesStr: + require.Equal(t, record.TableNames, column) + case IndexNamesStr: + require.Equal(t, strings.Join(record.IndexNames, ","), column) + case SampleUserStr: + require.Equal(t, info.User, column) + case ExecCountStr: + require.Equal(t, int64(1), column) + case SumLatencyStr: + require.Equal(t, int64(record.SumLatency), column) + case MaxLatencyStr: + require.Equal(t, int64(record.MaxLatency), column) + } + } +} + +type mockColumnInfo struct{} + +func (mockColumnInfo) getInstanceAddr() string { + return "instance_addr" +} + +func (mockColumnInfo) getTimeLocation() *time.Location { + loc, _ := time.LoadLocation("Asia/Shanghai") + return loc +} diff --git a/util/stmtsummary/v2/logger.go b/pkg/util/stmtsummary/v2/logger.go similarity index 98% rename from util/stmtsummary/v2/logger.go rename to pkg/util/stmtsummary/v2/logger.go index 92f8277d0a111..87462375771c3 100644 --- a/util/stmtsummary/v2/logger.go +++ b/pkg/util/stmtsummary/v2/logger.go @@ -20,7 +20,7 @@ import ( "time" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" "go.uber.org/zap/buffer" "go.uber.org/zap/zapcore" diff --git a/pkg/util/stmtsummary/v2/main_test.go b/pkg/util/stmtsummary/v2/main_test.go new file mode 100644 index 0000000000000..f3ec7e160415a --- /dev/null +++ b/pkg/util/stmtsummary/v2/main_test.go @@ -0,0 +1,33 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/stmtsummary/v2/reader.go b/pkg/util/stmtsummary/v2/reader.go new file mode 100644 index 0000000000000..f5e7c9742a6c8 --- /dev/null +++ b/pkg/util/stmtsummary/v2/reader.go @@ -0,0 +1,862 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "bufio" + "cmp" + "context" + "encoding/json" + "io" + "math" + "os" + "path/filepath" + "slices" + "strings" + "sync" + "time" + + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/set" + "go.uber.org/zap" +) + +const ( + logFileTimeFormat = "2006-01-02T15-04-05.000" // depends on lumberjack.go#backupTimeFormat + maxLineSize = 1073741824 + + batchScanSize = 64 +) + +// StmtTimeRange is the time range type used in the stmtsummary package. +// [Begin, End) +type StmtTimeRange struct { + Begin int64 + End int64 +} + +// MemReader is used to read the current window's data maintained in memory by StmtSummary. +type MemReader struct { + s *StmtSummary + columns []*model.ColumnInfo + instanceAddr string + timeLocation *time.Location + + columnFactories []columnFactory + checker *stmtChecker +} + +// NewMemReader creates a MemReader from StmtSummary and other necessary parameters. +func NewMemReader(s *StmtSummary, + columns []*model.ColumnInfo, + instanceAddr string, + timeLocation *time.Location, + user *auth.UserIdentity, + hasProcessPriv bool, + digests set.StringSet, + timeRanges []*StmtTimeRange) *MemReader { + return &MemReader{ + s: s, + columns: columns, + instanceAddr: instanceAddr, + timeLocation: timeLocation, + columnFactories: makeColumnFactories(columns), + checker: &stmtChecker{ + user: user, + hasProcessPriv: hasProcessPriv, + digests: digests, + timeRanges: timeRanges, + }, + } +} + +// Rows returns rows converted from the current window's data maintained +// in memory by StmtSummary. All evicted data will be aggregated into a +// single row appended at the end. +func (r *MemReader) Rows() [][]types.Datum { + if r.s == nil { + return nil + } + end := timeNow().Unix() + r.s.windowLock.Lock() + w := r.s.window + if !r.checker.isTimeValid(w.begin.Unix(), end) { + r.s.windowLock.Unlock() + return nil + } + values := w.lru.Values() + evicted := w.evicted + r.s.windowLock.Unlock() + rows := make([][]types.Datum, 0, len(values)+1) + for _, v := range values { + record := v.(*lockedStmtRecord) + if !r.checker.isDigestValid(record.Digest) { + continue + } + func() { + record.Lock() + defer record.Unlock() + if !r.checker.hasPrivilege(record.AuthUsers) { + return + } + record.Begin = w.begin.Unix() + record.End = end + row := make([]types.Datum, len(r.columnFactories)) + for i, factory := range r.columnFactories { + row[i] = types.NewDatum(factory(r, record.StmtRecord)) + } + rows = append(rows, row) + }() + } + if r.checker.digests == nil { + func() { + evicted.Lock() + defer evicted.Unlock() + if evicted.other.ExecCount == 0 { + return + } + if !r.checker.hasPrivilege(evicted.other.AuthUsers) { + return + } + evicted.other.Begin = w.begin.Unix() + evicted.other.End = end + row := make([]types.Datum, len(r.columnFactories)) + for i, factory := range r.columnFactories { + row[i] = types.NewDatum(factory(r, evicted.other)) + } + rows = append(rows, row) + }() + } + return rows +} + +// getInstanceAddr implements columnInfo. +func (r *MemReader) getInstanceAddr() string { + return r.instanceAddr +} + +// getInstanceAddr implements columnInfo. +func (r *MemReader) getTimeLocation() *time.Location { + return r.timeLocation +} + +// HistoryReader is used to read data that has been persisted to files. +type HistoryReader struct { + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + + instanceAddr string + timeLocation *time.Location + + columnFactories []columnFactory + checker *stmtChecker + files *stmtFiles + + concurrent int + rowsCh <-chan [][]types.Datum + errCh <-chan error +} + +// NewHistoryReader creates a HisroryReader from StmtSummary and other +// necessary parameters. If timeRanges is present, only files within +// the time range will be read. +func NewHistoryReader( + ctx context.Context, + columns []*model.ColumnInfo, + instanceAddr string, + timeLocation *time.Location, + user *auth.UserIdentity, + hasProcessPriv bool, + digests set.StringSet, + timeRanges []*StmtTimeRange, + concurrent int, +) (*HistoryReader, error) { + files, err := newStmtFiles(ctx, timeRanges) + if err != nil { + return nil, err + } + + if concurrent < 2 { + concurrent = 2 + } + rowsCh := make(chan [][]types.Datum, concurrent) + errCh := make(chan error, concurrent) + + ctx, cancel := context.WithCancel(ctx) + r := &HistoryReader{ + ctx: ctx, + cancel: cancel, + + instanceAddr: instanceAddr, + timeLocation: timeLocation, + columnFactories: makeColumnFactories(columns), + checker: &stmtChecker{ + user: user, + hasProcessPriv: hasProcessPriv, + digests: digests, + timeRanges: timeRanges, + }, + files: files, + concurrent: concurrent, + rowsCh: rowsCh, + errCh: errCh, + } + + r.wg.Add(1) + go func() { + defer r.wg.Done() + r.scheduleTasks(rowsCh, errCh) + }() + return r, nil +} + +// Rows returns rows converted from records in files. Reading and parsing +// works asynchronously. If (nil, nil) is returned, it means that the +// reading has been completed. +func (r *HistoryReader) Rows() ([][]types.Datum, error) { + ctx := r.ctx + for { + select { + case err := <-r.errCh: + return nil, err + case rows, ok := <-r.rowsCh: + if !ok { + select { + case err := <-r.errCh: + return nil, err + default: + return nil, nil + } + } + if len(rows) == 0 { + continue + } + return rows, nil + case <-ctx.Done(): + return nil, ctx.Err() + } + } +} + +// Close ends reading and closes all files. +func (r *HistoryReader) Close() error { + r.files.close() + if r.cancel != nil { + r.cancel() + } + r.wg.Wait() + return nil +} + +// 4 roles to handle the read task in pipeline: +// +// ## Pipeline +// . +--------------+ +---------------+ +// == files => | scan workers | == lines => | parse workers | == rows => +// . filesCh +--------------+ linesCh +---------------+ rowsCh +// +// ## Roles +// +--------------+--------------+------------------------------------+ +// | ROLE | COUNT | DESCRIPTION | +// +--------------+--------------+------------------------------------+ +// | Scan Worker | concurrent/2 | Scan files (I/O) first, then help | +// | | | parse workers to parse lines (CPU) | +// +--------------+--------------+------------------------------------+ +// | Parse Worker | concurrent- | Parse lines (CPU) to rows | +// | | concurrent/2 | | +// +--------------+--------------+------------------------------------+ +// | Manager | 1 | Drive the whole process and notify | +// | | | scan workers to switch role | +// +--------------+--------------+------------------------------------+ +// | Monitor | 1 | Cover failures and notify workers | +// | | | to exit | +// +--------------+--------------+------------------------------------+ +func (r *HistoryReader) scheduleTasks( + rowsCh chan<- [][]types.Datum, + errCh chan<- error, +) { + if r.files == nil || len(r.files.files) == 0 { + close(rowsCh) + return + } + + ctx, cancel := context.WithCancel(r.ctx) + defer cancel() + + scanWorker := &stmtScanWorker{ + ctx: ctx, + batchSize: batchScanSize, + checker: r.checker, + } + parseWorker := &stmtParseWorker{ + ctx: ctx, + instanceAddr: r.instanceAddr, + timeLocation: r.timeLocation, + checker: r.checker, + columnFactories: r.columnFactories, + } + + concurrent := r.concurrent + filesCh := make(chan *os.File, concurrent) + linesCh := make(chan [][]byte, concurrent) + innerErrCh := make(chan error, concurrent) + + var scanWg sync.WaitGroup + scanWg.Add(concurrent / 2) + scanDone := scanWg.Done + waitScanAllDone := scanWg.Wait + + var parseWg sync.WaitGroup + parseWg.Add(concurrent) // finally all workers will become parse workers + parseDone := parseWg.Done + waitParseAllDone := parseWg.Wait + + // Half of workers are scheduled to scan files and then parse lines. + for i := 0; i < concurrent/2; i++ { + go func() { + scanWorker.run(filesCh, linesCh, innerErrCh) + scanDone() + + parseWorker.run(linesCh, rowsCh, innerErrCh) + parseDone() + }() + } + + // Remaining workers are scheduled to parse lines. + for i := concurrent / 2; i < concurrent; i++ { + go func() { + parseWorker.run(linesCh, rowsCh, innerErrCh) + parseDone() + }() + } + + // Manager drives the whole process + var mgrWg sync.WaitGroup + mgrWg.Add(1) + go func() { + defer mgrWg.Done() + + func() { + for _, file := range r.files.files { + select { + case filesCh <- file.file: + case <-ctx.Done(): + return + } + } + }() + // No scan tasks to be generating. Notify idle scan + // workers to become parse workers + close(filesCh) + + // No parse tasks to be generating once all scan + // tasks are done. Notify idle parse workers to exit + waitScanAllDone() + close(linesCh) + + // No rows to be generating once all parse tasks + // are done. Notify monitor to close rowsCh + waitParseAllDone() + cancel() + }() + + // Monitor to cover failures and notify workers to exit + select { + case err := <-innerErrCh: + select { + case errCh <- err: + default: + } + cancel() // notify workers to exit + case <-ctx.Done(): + // notified by manager or parent ctx is canceled + } + mgrWg.Wait() + close(rowsCh) // task done +} + +type stmtChecker struct { + user *auth.UserIdentity + hasProcessPriv bool // If the user has the 'PROCESS' privilege, he can read all statements. + digests set.StringSet + timeRanges []*StmtTimeRange +} + +func (c *stmtChecker) hasPrivilege(authUsers map[string]struct{}) bool { + authed := true + if c.user != nil && !c.hasProcessPriv { + if len(authUsers) == 0 { + return false + } + _, authed = authUsers[c.user.Username] + } + return authed +} + +func (c *stmtChecker) isDigestValid(digest string) bool { + if c.digests == nil { + return true + } + return c.digests.Exist(digest) +} + +func (c *stmtChecker) isTimeValid(begin, end int64) bool { + if len(c.timeRanges) == 0 { + return true + } + for _, tr := range c.timeRanges { + if timeRangeOverlap(begin, end, tr.Begin, tr.End) { + return true + } + } + return false +} + +func (c *stmtChecker) needStop(curBegin int64) bool { + if len(c.timeRanges) == 0 { + return false + } + stop := true + for _, tr := range c.timeRanges { + if tr.End == 0 || tr.End >= curBegin { + stop = false + } + } + return stop +} + +type stmtTinyRecord struct { + Begin int64 `json:"begin"` + End int64 `json:"end"` +} + +type stmtFile struct { + file *os.File + begin int64 + end int64 +} + +func openStmtFile(path string) (*stmtFile, error) { + file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm) + if err != nil { + return nil, err + } + begin, err := parseBeginTsAndReseek(file) + if err != nil { + if err != io.EOF { + _ = file.Close() + return nil, err + } + } + end, err := parseEndTs(file) + if err != nil { + _ = file.Close() + return nil, err + } + + return &stmtFile{ + file: file, + begin: begin, + end: end, + }, nil +} + +func parseBeginTsAndReseek(file *os.File) (int64, error) { + if _, err := file.Seek(0, io.SeekStart); err != nil { + return 0, err + } + + reader := bufio.NewReader(file) + var record stmtTinyRecord + for { // ignore invalid lines + line, err := readLine(reader) + if err != nil { + return 0, err + } + err = json.Unmarshal(line, &record) + if err == nil { + break + } + } + + if _, err := file.Seek(0, io.SeekStart); err != nil { + return 0, err + } + return record.Begin, nil +} + +func parseEndTs(file *os.File) (int64, error) { + // tidb-statements.log + filename := config.GetGlobalConfig().Instance.StmtSummaryFilename + // .log + ext := filepath.Ext(filename) + // tidb-statements + prefix := filename[:len(filename)-len(ext)] + + // tidb-statements-2022-12-27T16-21-20.245.log + filename = filepath.Base(file.Name()) + // .log + ext = filepath.Ext(file.Name()) + // tidb-statements-2022-12-27T16-21-20.245 + filename = filename[:len(filename)-len(ext)] + + if strings.HasPrefix(filename, prefix+"-") { + // 2022-12-27T16-21-20.245 + timeStr := strings.TrimPrefix(filename, prefix+"-") + end, err := time.ParseInLocation(logFileTimeFormat, timeStr, time.Local) + if err != nil { + return 0, err + } + return end.Unix(), nil + } + return 0, nil +} + +func (f *stmtFile) close() error { + if f.file != nil { + return f.file.Close() + } + return nil +} + +type stmtFiles struct { + files []*stmtFile +} + +func newStmtFiles(ctx context.Context, timeRanges []*StmtTimeRange) (*stmtFiles, error) { + filename := config.GetGlobalConfig().Instance.StmtSummaryFilename + ext := filepath.Ext(filename) + prefix := filename[:len(filename)-len(ext)] + var files []*stmtFile + walkFn := func(path string, info os.DirEntry) error { + if info.IsDir() { + return nil + } + if !strings.HasPrefix(path, prefix) { + return nil + } + if isCtxDone(ctx) { + return ctx.Err() + } + file, err := openStmtFile(path) + if err != nil { + logutil.BgLogger().Warn("failed to open or parse statements file", zap.Error(err), zap.String("path", path)) + return nil + } + if len(timeRanges) == 0 { + files = append(files, file) + return nil + } + for _, tr := range timeRanges { + if timeRangeOverlap(file.begin, file.end, tr.Begin, tr.End) { + files = append(files, file) + return nil + } + } + return nil + } + + dir := filepath.Dir(filename) + entries, err := os.ReadDir(dir) + if err != nil { + return nil, err + } + for _, entry := range entries { + if err := walkFn(filepath.Join(dir, entry.Name()), entry); err != nil { + for _, f := range files { + _ = f.close() + } + return nil, err + } + } + slices.SortFunc(files, func(i, j *stmtFile) int { + return cmp.Compare(i.begin, j.begin) + }) + return &stmtFiles{files: files}, nil +} + +func (f *stmtFiles) close() { + for _, f := range f.files { + _ = f.close() + } +} + +type stmtScanWorker struct { + ctx context.Context + batchSize int + checker *stmtChecker +} + +func (w *stmtScanWorker) run( + fileCh <-chan *os.File, + linesCh chan<- [][]byte, + errCh chan<- error, +) { + for { + select { + case file, ok := <-fileCh: + if !ok { + return + } + w.handleFile(file, linesCh, errCh) + case <-w.ctx.Done(): + return + } + } +} + +func (w *stmtScanWorker) handleFile( + file *os.File, + linesCh chan<- [][]byte, + errCh chan<- error, +) { + if file == nil { + return + } + + reader := bufio.NewReader(file) + for { + if isCtxDone(w.ctx) { + return + } + + lines, err := w.readlines(reader) + if err == io.EOF { + return + } + if err != nil { + w.putErr(err, errCh) + return + } + + w.putLines(lines, linesCh) + } +} + +func (w *stmtScanWorker) putErr( + err error, + errCh chan<- error, +) { + select { + case errCh <- err: + case <-w.ctx.Done(): + } +} + +func (w *stmtScanWorker) putLines( + lines [][]byte, + linesCh chan<- [][]byte, +) { + select { + case linesCh <- lines: + case <-w.ctx.Done(): + } +} + +func (w *stmtScanWorker) readlines(reader *bufio.Reader) ([][]byte, error) { + var firstLine []byte + var record *stmtTinyRecord + for { // ingore invalid lines + var err error + firstLine, err = readLine(reader) + if err != nil { + return nil, err + } + + record, err = w.parse(firstLine) + if err == nil { + break + } + } + + if w.needStop(record) { + // done because remaining lines in file + // are not in the time range + return nil, io.EOF + } + + lines := make([][]byte, 0, w.batchSize) + lines = append(lines, firstLine) + + newLines, err := readLines(reader, w.batchSize-1) + if err == io.EOF { + return lines, nil + } + if err != nil { + return nil, err + } + + lines = append(lines, newLines...) + return lines, nil +} + +func (*stmtScanWorker) parse(raw []byte) (*stmtTinyRecord, error) { + var record stmtTinyRecord + if err := json.Unmarshal(raw, &record); err != nil { + return nil, err + } + return &record, nil +} + +func (w *stmtScanWorker) needStop(record *stmtTinyRecord) bool { + return w.checker.needStop(record.Begin) +} + +type stmtParseWorker struct { + ctx context.Context + instanceAddr string + timeLocation *time.Location + checker *stmtChecker + columnFactories []columnFactory +} + +func (w *stmtParseWorker) run( + linesCh <-chan [][]byte, + rowsCh chan<- [][]types.Datum, + errCh chan<- error, +) { + for { + select { + case lines, ok := <-linesCh: + if !ok { + return + } + w.handleLines(lines, rowsCh, errCh) + case <-w.ctx.Done(): + return + } + } +} + +func (w *stmtParseWorker) handleLines( + lines [][]byte, + rowsCh chan<- [][]types.Datum, + _ chan<- error, +) { + if len(lines) == 0 { + return + } + + rows := make([][]types.Datum, 0, len(lines)) + for _, line := range lines { + record, err := w.parse(line) + if err != nil { + // ignore invalid lines + continue + } + + if w.needStop(record) { + break + } + + if !w.matchConds(record) { + continue + } + + row := w.buildRow(record) + rows = append(rows, row) + } + + if len(rows) > 0 { + w.putRows(rows, rowsCh) + } +} + +func (w *stmtParseWorker) putRows( + rows [][]types.Datum, + rowsCh chan<- [][]types.Datum, +) { + select { + case rowsCh <- rows: + case <-w.ctx.Done(): + } +} + +func (*stmtParseWorker) parse(raw []byte) (*StmtRecord, error) { + var record StmtRecord + if err := json.Unmarshal(raw, &record); err != nil { + return nil, err + } + return &record, nil +} + +func (w *stmtParseWorker) needStop(record *StmtRecord) bool { + return w.checker.needStop(record.Begin) +} + +func (w *stmtParseWorker) matchConds(record *StmtRecord) bool { + if !w.checker.isTimeValid(record.Begin, record.End) { + return false + } + if !w.checker.isDigestValid(record.Digest) { + return false + } + if !w.checker.hasPrivilege(record.AuthUsers) { + return false + } + return true +} + +func (w *stmtParseWorker) buildRow(record *StmtRecord) []types.Datum { + row := make([]types.Datum, len(w.columnFactories)) + for n, factory := range w.columnFactories { + row[n] = types.NewDatum(factory(w, record)) + } + return row +} + +// getInstanceAddr implements columnInfo. +func (w *stmtParseWorker) getInstanceAddr() string { + return w.instanceAddr +} + +// getInstanceAddr implements columnInfo. +func (w *stmtParseWorker) getTimeLocation() *time.Location { + return w.timeLocation +} + +func isCtxDone(ctx context.Context) bool { + select { + case <-ctx.Done(): + return true + default: + return false + } +} + +func readLine(reader *bufio.Reader) ([]byte, error) { + return util.ReadLine(reader, maxLineSize) +} + +func readLines(reader *bufio.Reader, count int) ([][]byte, error) { + return util.ReadLines(reader, count, maxLineSize) +} + +func timeRangeOverlap(aBegin, aEnd, bBegin, bEnd int64) bool { + if aEnd == 0 || aEnd < aBegin { + aEnd = math.MaxInt64 + } + if bEnd == 0 || bEnd < bBegin { + bEnd = math.MaxInt64 + } + // https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap + return aBegin <= bEnd && aEnd >= bBegin +} diff --git a/util/stmtsummary/v2/reader_test.go b/pkg/util/stmtsummary/v2/reader_test.go similarity index 98% rename from util/stmtsummary/v2/reader_test.go rename to pkg/util/stmtsummary/v2/reader_test.go index 3ac7f5f11af06..0439d431a8994 100644 --- a/util/stmtsummary/v2/reader_test.go +++ b/pkg/util/stmtsummary/v2/reader_test.go @@ -21,11 +21,11 @@ import ( "testing" "time" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) diff --git a/util/stmtsummary/v2/record.go b/pkg/util/stmtsummary/v2/record.go similarity index 99% rename from util/stmtsummary/v2/record.go rename to pkg/util/stmtsummary/v2/record.go index a0155babee45c..2cc85bac40fa7 100644 --- a/util/stmtsummary/v2/record.go +++ b/pkg/util/stmtsummary/v2/record.go @@ -23,10 +23,10 @@ import ( "sync/atomic" "time" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/stmtsummary" + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/execdetails" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/stmtsummary" "github.com/tikv/client-go/v2/util" ) diff --git a/util/stmtsummary/v2/record_test.go b/pkg/util/stmtsummary/v2/record_test.go similarity index 100% rename from util/stmtsummary/v2/record_test.go rename to pkg/util/stmtsummary/v2/record_test.go diff --git a/pkg/util/stmtsummary/v2/stmtsummary.go b/pkg/util/stmtsummary/v2/stmtsummary.go new file mode 100644 index 0000000000000..e7892c3e78535 --- /dev/null +++ b/pkg/util/stmtsummary/v2/stmtsummary.go @@ -0,0 +1,613 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package stmtsummary + +import ( + "context" + "errors" + "maps" + "math" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/log" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/kvcache" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/stmtsummary" + atomic2 "go.uber.org/atomic" + "go.uber.org/zap" +) + +const ( + defaultEnabled = true + defaultEnableInternalQuery = false + defaultMaxStmtCount = 3000 + defaultMaxSQLLength = 4096 + defaultRefreshInterval = 30 * 60 // 30 min + defaultRotateCheckInterval = 1 // s +) + +var ( + // GlobalStmtSummary is the global StmtSummary instance, we need + // to explicitly call Setup() to initialize it. It will then be + // referenced by SessionVars.StmtSummary for each session. + GlobalStmtSummary *StmtSummary + + timeNow = time.Now +) + +// Setup initializes the GlobalStmtSummary. +func Setup(cfg *Config) (err error) { + GlobalStmtSummary, err = NewStmtSummary(cfg) + return +} + +// Close closes the GlobalStmtSummary. +func Close() { + if GlobalStmtSummary != nil { + GlobalStmtSummary.Close() + } +} + +// Config is the static configuration of StmtSummary. It cannot be +// modified at runtime. +type Config struct { + Filename string + FileMaxSize int + FileMaxDays int + FileMaxBackups int +} + +// StmtSummary represents the complete statements summary statistics. +// It controls data rotation and persistence internally, and provides +// reading interface through MemReader and HistoryReader. +type StmtSummary struct { + ctx context.Context + cancel context.CancelFunc + + optEnabled *atomic2.Bool + optEnableInternalQuery *atomic2.Bool + optMaxStmtCount *atomic2.Uint32 + optMaxSQLLength *atomic2.Uint32 + optRefreshInterval *atomic2.Uint32 + + window *stmtWindow + windowLock sync.Mutex + storage stmtStorage + closeWg sync.WaitGroup + closed atomic.Bool +} + +// NewStmtSummary creates a new StmtSummary from Config. +func NewStmtSummary(cfg *Config) (*StmtSummary, error) { + if cfg.Filename == "" { + return nil, errors.New("stmtsummary: empty filename") + } + + ctx, cancel := context.WithCancel(context.Background()) + s := &StmtSummary{ + ctx: ctx, + cancel: cancel, + + // These options can be changed dynamically at runtime. + // The default values here are just placeholders, and the real values in + // sessionctx/variables/tidb_vars.go will overwrite them after TiDB starts. + optEnabled: atomic2.NewBool(defaultEnabled), + optEnableInternalQuery: atomic2.NewBool(defaultEnableInternalQuery), + optMaxStmtCount: atomic2.NewUint32(defaultMaxStmtCount), + optMaxSQLLength: atomic2.NewUint32(defaultMaxSQLLength), + optRefreshInterval: atomic2.NewUint32(defaultRefreshInterval), + window: newStmtWindow(timeNow(), uint(defaultMaxStmtCount)), + storage: newStmtLogStorage(&log.Config{ + File: log.FileLogConfig{ + Filename: cfg.Filename, + MaxSize: cfg.FileMaxSize, + MaxDays: cfg.FileMaxDays, + MaxBackups: cfg.FileMaxBackups, + }, + }), + } + + s.closeWg.Add(1) + go func() { + defer s.closeWg.Done() + s.rotateLoop() + }() + + return s, nil +} + +// NewStmtSummary4Test creates a new StmtSummary for testing purposes. +func NewStmtSummary4Test(maxStmtCount uint) *StmtSummary { + ctx, cancel := context.WithCancel(context.Background()) + + ss := &StmtSummary{ + ctx: ctx, + cancel: cancel, + + optEnabled: atomic2.NewBool(defaultEnabled), + optEnableInternalQuery: atomic2.NewBool(defaultEnableInternalQuery), + optMaxStmtCount: atomic2.NewUint32(defaultMaxStmtCount), + optMaxSQLLength: atomic2.NewUint32(defaultMaxSQLLength), + optRefreshInterval: atomic2.NewUint32(60 * 60 * 24 * 365), // 1 year + window: newStmtWindow(timeNow(), maxStmtCount), + storage: &mockStmtStorage{}, + } + + return ss +} + +// Enabled returns whether the StmtSummary is enabled. +func (s *StmtSummary) Enabled() bool { + return s.optEnabled.Load() +} + +// SetEnabled is used to enable or disable StmtSummary. If disabled, in-memory +// data will be cleared, (persisted data will still be remained). +func (s *StmtSummary) SetEnabled(v bool) error { + s.optEnabled.Store(v) + if !v { + s.Clear() + } + + return nil +} + +// EnableInternalQuery returns whether the StmtSummary counts internal queries. +func (s *StmtSummary) EnableInternalQuery() bool { + return s.optEnableInternalQuery.Load() +} + +// SetEnableInternalQuery is used to enable or disable StmtSummary's internal +// query statistics. If disabled, in-memory internal queries will be cleared, +// (persisted internal queries will still be remained). +func (s *StmtSummary) SetEnableInternalQuery(v bool) error { + s.optEnableInternalQuery.Store(v) + if !v { + s.ClearInternal() + } + + return nil +} + +// MaxStmtCount returns the maximum number of statements. +func (s *StmtSummary) MaxStmtCount() uint32 { + return s.optMaxStmtCount.Load() +} + +// SetMaxStmtCount is used to set the maximum number of statements. +// If the current number exceeds the maximum number, the excess will be evicted. +func (s *StmtSummary) SetMaxStmtCount(v uint32) error { + if v < 1 { + v = 1 + } + s.optMaxStmtCount.Store(v) + s.windowLock.Lock() + _ = s.window.lru.SetCapacity(uint(v)) + s.windowLock.Unlock() + + return nil +} + +// MaxSQLLength returns the maximum size of a single SQL statement. +func (s *StmtSummary) MaxSQLLength() uint32 { + return s.optMaxSQLLength.Load() +} + +// SetMaxSQLLength sets the maximum size of a single SQL statement. +func (s *StmtSummary) SetMaxSQLLength(v uint32) error { + s.optMaxSQLLength.Store(v) + + return nil +} + +// RefreshInterval returns the period (in seconds) at which the statistics +// window is refreshed (persisted). +func (s *StmtSummary) RefreshInterval() uint32 { + return s.optRefreshInterval.Load() +} + +// SetRefreshInterval sets the period (in seconds) for the statistics window +// to be refreshed (persisted). This may trigger a refresh (persistence) of +// the current statistics window early. +func (s *StmtSummary) SetRefreshInterval(v uint32) error { + if v < 1 { + v = 1 + } + s.optRefreshInterval.Store(v) + + return nil +} + +// Add adds a single stmtsummary.StmtExecInfo to the current statistics window +// of StmtSummary. Before adding, it will check whether the current window has +// expired, and if it has expired, the window will be persisted asynchronously +// and a new window will be created to replace the current one. +func (s *StmtSummary) Add(info *stmtsummary.StmtExecInfo) { + if s.closed.Load() { + return + } + + k := &stmtKey{ + schemaName: info.SchemaName, + digest: info.Digest, + prevDigest: info.PrevSQLDigest, + planDigest: info.PlanDigest, + } + k.Hash() // Calculate hash value in advance, to reduce the time holding the window lock. + + // Add info to the current statistics window. + s.windowLock.Lock() + var record *lockedStmtRecord + if v, ok := s.window.lru.Get(k); ok { + record = v.(*lockedStmtRecord) + } else { + record = &lockedStmtRecord{StmtRecord: NewStmtRecord(info)} + s.window.lru.Put(k, record) + } + s.windowLock.Unlock() + + record.Lock() + record.Add(info) + record.Unlock() +} + +// Evicted returns the number of statements evicted for the current +// time window. The returned type is one row consisting of three +// columns: [BEGIN_TIME, END_TIME, EVICTED_COUNT]. +func (s *StmtSummary) Evicted() []types.Datum { + s.windowLock.Lock() + count := int64(s.window.evicted.count()) + s.windowLock.Unlock() + if count == 0 { + return nil + } + begin := types.NewTime(types.FromGoTime(s.window.begin), mysql.TypeTimestamp, 0) + end := types.NewTime(types.FromGoTime(timeNow()), mysql.TypeTimestamp, 0) + return types.MakeDatums(begin, end, count) +} + +// Clear clears all data in the current window, and the data that +// has been persisted will not be cleared. +func (s *StmtSummary) Clear() { + s.windowLock.Lock() + defer s.windowLock.Unlock() + s.window.clear() +} + +// ClearInternal clears all internal queries of the current window, +// and the data that has been persisted will not be cleared. +func (s *StmtSummary) ClearInternal() { + s.windowLock.Lock() + defer s.windowLock.Unlock() + for _, k := range s.window.lru.Keys() { + v, _ := s.window.lru.Get(k) + if v.(*lockedStmtRecord).IsInternal { + s.window.lru.Delete(k) + } + } +} + +// Close closes the work of StmtSummary. +func (s *StmtSummary) Close() { + if s.cancel != nil { + s.cancel() + s.closeWg.Wait() + } + s.closed.Store(true) + s.flush() +} + +func (s *StmtSummary) flush() { + now := timeNow() + + s.windowLock.Lock() + window := s.window + s.window = newStmtWindow(now, uint(s.MaxStmtCount())) + s.windowLock.Unlock() + + if window.lru.Size() > 0 { + s.storage.persist(window, now) + } + err := s.storage.sync() + if err != nil { + logutil.BgLogger().Error("sync stmt summary failed", zap.Error(err)) + } +} + +// GetMoreThanCntBindableStmt is used to get bindable statements. +// Statements whose execution times exceed the threshold will be +// returned. Since the historical data has been persisted, we only +// refer to the statistics data of the current window in memory. +func (s *StmtSummary) GetMoreThanCntBindableStmt(cnt int64) []*stmtsummary.BindableStmt { + s.windowLock.Lock() + values := s.window.lru.Values() + s.windowLock.Unlock() + stmts := make([]*stmtsummary.BindableStmt, 0, len(values)) + for _, value := range values { + record := value.(*lockedStmtRecord) + func() { + record.Lock() + defer record.Unlock() + if record.StmtType == "Select" || + record.StmtType == "Delete" || + record.StmtType == "Update" || + record.StmtType == "Insert" || + record.StmtType == "Replace" { + if len(record.AuthUsers) > 0 && record.ExecCount > cnt { + stmt := &stmtsummary.BindableStmt{ + Schema: record.SchemaName, + Query: record.SampleSQL, + PlanHint: record.PlanHint, + Charset: record.Charset, + Collation: record.Collation, + Users: make(map[string]struct{}), + } + maps.Copy(stmt.Users, record.AuthUsers) + + // If it is SQL command prepare / execute, the ssElement.sampleSQL + // is `execute ...`, we should get the original select query. + // If it is binary protocol prepare / execute, ssbd.normalizedSQL + // should be same as ssElement.sampleSQL. + if record.Prepared { + stmt.Query = record.NormalizedSQL + } + stmts = append(stmts, stmt) + } + } + }() + } + return stmts +} + +func (s *StmtSummary) rotateLoop() { + tick := time.NewTicker(defaultRotateCheckInterval * time.Second) + defer tick.Stop() + + for { + select { + case <-s.ctx.Done(): + return + case <-tick.C: + now := timeNow() + s.windowLock.Lock() + // The current window has expired and needs to be refreshed and persisted. + if now.After(s.window.begin.Add(time.Duration(s.RefreshInterval()) * time.Second)) { + s.rotate(now) + } + s.windowLock.Unlock() + } + } +} + +func (s *StmtSummary) rotate(now time.Time) { + w := s.window + s.window = newStmtWindow(now, uint(s.MaxStmtCount())) + size := w.lru.Size() + if size > 0 { + // Persist window asynchronously. + s.closeWg.Add(1) + go func() { + defer s.closeWg.Done() + s.storage.persist(w, now) + }() + } +} + +// stmtWindow represents a single statistical window, which has a begin +// time and an end time. Data within a single window is eliminated +// according to the LRU strategy. All evicted data will be aggregated +// into stmtEvicted. +type stmtWindow struct { + begin time.Time + lru *kvcache.SimpleLRUCache // *stmtKey => *lockedStmtRecord + evicted *stmtEvicted +} + +func newStmtWindow(begin time.Time, capacity uint) *stmtWindow { + w := &stmtWindow{ + begin: begin, + lru: kvcache.NewSimpleLRUCache(capacity, 0, 0), + evicted: newStmtEvicted(), + } + w.lru.SetOnEvict(func(k kvcache.Key, v kvcache.Value) { + r := v.(*lockedStmtRecord) + r.Lock() + defer r.Unlock() + w.evicted.add(k.(*stmtKey), r.StmtRecord) + }) + return w +} + +func (w *stmtWindow) clear() { + w.lru.DeleteAll() + w.evicted = newStmtEvicted() +} + +type stmtStorage interface { + persist(w *stmtWindow, end time.Time) + sync() error +} + +// stmtKey defines key for stmtElement. +type stmtKey struct { + // Same statements may appear in different schema, but they refer to different tables. + schemaName string + digest string + // The digest of the previous statement. + prevDigest string + // The digest of the plan of this SQL. + planDigest string + // `hash` is the hash value of this object. + hash []byte +} + +// Hash implements SimpleLRUCache.Key. +// Only when current SQL is `commit` do we record `prevSQL`. Otherwise, `prevSQL` is empty. +// `prevSQL` is included in the key To distinguish different transactions. +func (k *stmtKey) Hash() []byte { + if len(k.hash) == 0 { + k.hash = make([]byte, 0, len(k.schemaName)+len(k.digest)+len(k.prevDigest)+len(k.planDigest)) + k.hash = append(k.hash, hack.Slice(k.digest)...) + k.hash = append(k.hash, hack.Slice(k.schemaName)...) + k.hash = append(k.hash, hack.Slice(k.prevDigest)...) + k.hash = append(k.hash, hack.Slice(k.planDigest)...) + } + return k.hash +} + +type stmtEvicted struct { + sync.Mutex + keys map[string]struct{} + other *StmtRecord +} + +func newStmtEvicted() *stmtEvicted { + return &stmtEvicted{ + keys: make(map[string]struct{}), + other: &StmtRecord{ + AuthUsers: make(map[string]struct{}), + MinLatency: time.Duration(math.MaxInt64), + BackoffTypes: make(map[string]int), + FirstSeen: time.Unix(math.MaxInt64, 0), + }, + } +} + +func (e *stmtEvicted) add(key *stmtKey, record *StmtRecord) { + if key == nil || record == nil { + return + } + e.Lock() + defer e.Unlock() + e.keys[string(key.Hash())] = struct{}{} + e.other.Merge(record) +} + +func (e *stmtEvicted) count() int { + e.Lock() + defer e.Unlock() + return len(e.keys) +} + +type lockedStmtRecord struct { + sync.Mutex + *StmtRecord +} + +type mockStmtStorage struct { + sync.Mutex + windows []*stmtWindow +} + +func (s *mockStmtStorage) persist(w *stmtWindow, _ time.Time) { + s.Lock() + s.windows = append(s.windows, w) + s.Unlock() +} + +func (*mockStmtStorage) sync() error { + return nil +} + +/* Public proxy functions between v1 and v2 */ + +// Add wraps GlobalStmtSummary.Add and stmtsummary.StmtSummaryByDigestMap.AddStatement. +func Add(stmtExecInfo *stmtsummary.StmtExecInfo) { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + GlobalStmtSummary.Add(stmtExecInfo) + } else { + stmtsummary.StmtSummaryByDigestMap.AddStatement(stmtExecInfo) + } +} + +// Enabled wraps GlobalStmtSummary.Enabled and stmtsummary.StmtSummaryByDigestMap.Enabled. +func Enabled() bool { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.Enabled() + } + return stmtsummary.StmtSummaryByDigestMap.Enabled() +} + +// EnabledInternal wraps GlobalStmtSummary.EnableInternalQuery and stmtsummary.StmtSummaryByDigestMap.EnabledInternal. +func EnabledInternal() bool { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.EnableInternalQuery() + } + return stmtsummary.StmtSummaryByDigestMap.EnabledInternal() +} + +// SetEnabled wraps GlobalStmtSummary.SetEnabled and stmtsummary.StmtSummaryByDigestMap.SetEnabled. +func SetEnabled(v bool) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.SetEnabled(v) + } + return stmtsummary.StmtSummaryByDigestMap.SetEnabled(v) +} + +// SetEnableInternalQuery wraps GlobalStmtSummary.SetEnableInternalQuery and +// stmtsummary.StmtSummaryByDigestMap.SetEnabledInternalQuery. +func SetEnableInternalQuery(v bool) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.SetEnableInternalQuery(v) + } + return stmtsummary.StmtSummaryByDigestMap.SetEnabledInternalQuery(v) +} + +// SetRefreshInterval wraps GlobalStmtSummary.SetRefreshInterval and stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval. +func SetRefreshInterval(v int64) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.SetRefreshInterval(uint32(v)) + } + return stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval(v) +} + +// SetHistorySize wraps stmtsummary.StmtSummaryByDigestMap.SetHistorySize. +func SetHistorySize(v int) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return nil // not support + } + return stmtsummary.StmtSummaryByDigestMap.SetHistorySize(v) +} + +// SetMaxStmtCount wraps GlobalStmtSummary.SetMaxStmtCount and stmtsummary.StmtSummaryByDigestMap.SetMaxStmtCount. +func SetMaxStmtCount(v int) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.SetMaxStmtCount(uint32(v)) + } + return stmtsummary.StmtSummaryByDigestMap.SetMaxStmtCount(uint(v)) +} + +// SetMaxSQLLength wraps GlobalStmtSummary.SetMaxSQLLength and stmtsummary.StmtSummaryByDigestMap.SetMaxSQLLength. +func SetMaxSQLLength(v int) error { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.SetMaxSQLLength(uint32(v)) + } + return stmtsummary.StmtSummaryByDigestMap.SetMaxSQLLength(v) +} + +// GetMoreThanCntBindableStmt wraps GlobalStmtSummary.GetMoreThanCntBindableStmt and +// stmtsummary.StmtSummaryByDigestMap.GetMoreThanCntBindableStmt. +func GetMoreThanCntBindableStmt(frequency int64) []*stmtsummary.BindableStmt { + if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { + return GlobalStmtSummary.GetMoreThanCntBindableStmt(frequency) + } + return stmtsummary.StmtSummaryByDigestMap.GetMoreThanCntBindableStmt(frequency) +} diff --git a/util/stmtsummary/v2/stmtsummary_test.go b/pkg/util/stmtsummary/v2/stmtsummary_test.go similarity index 100% rename from util/stmtsummary/v2/stmtsummary_test.go rename to pkg/util/stmtsummary/v2/stmtsummary_test.go diff --git a/pkg/util/stmtsummary/v2/tests/BUILD.bazel b/pkg/util/stmtsummary/v2/tests/BUILD.bazel new file mode 100644 index 0000000000000..b993a67dd1173 --- /dev/null +++ b/pkg/util/stmtsummary/v2/tests/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_test") + +go_test( + name = "tests_test", + timeout = "short", + srcs = [ + "main_test.go", + "table_test.go", + ], + flaky = True, + shard_count = 10, + deps = [ + "//pkg/config", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/planner/core", + "//pkg/session", + "//pkg/testkit", + "//pkg/testkit/testsetup", + "//pkg/util/stmtsummary/v2:stmtsummary", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/stmtsummary/v2/tests/main_test.go b/pkg/util/stmtsummary/v2/tests/main_test.go new file mode 100644 index 0000000000000..c5a825bfa4229 --- /dev/null +++ b/pkg/util/stmtsummary/v2/tests/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/stmtsummary/v2/tests/table_test.go b/pkg/util/stmtsummary/v2/tests/table_test.go new file mode 100644 index 0000000000000..53acaae838bc6 --- /dev/null +++ b/pkg/util/stmtsummary/v2/tests/table_test.go @@ -0,0 +1,570 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package tests + +import ( + "fmt" + "math" + "os" + "testing" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/auth" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + stmtsummaryv2 "github.com/pingcap/tidb/pkg/util/stmtsummary/v2" + "github.com/stretchr/testify/require" +) + +func TestStmtSummaryTable(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + tk.MustExec("set @@tidb_enable_collect_execution_info=0;") + tk.MustQuery("select column_comment from information_schema.columns " + + "where table_name='STATEMENTS_SUMMARY' and column_name='STMT_TYPE'", + ).Check(testkit.Rows("Statement type")) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + // Test INSERT + tk.MustExec("insert into t values(1, 'a')") + tk.MustExec("insert into t values(2, 'b')") + tk.MustExec("insert into t VALUES(3, 'c')") + tk.MustExec("/**/insert into t values(4, 'd')") + + sql := "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text " + + "from information_schema.statements_summary " + + "where digest_text like 'insert into `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) + + // Test point get. + tk.MustExec("drop table if exists p") + tk.MustExec("create table p(a int primary key, b int)") + for i := 1; i < 3; i++ { + tk.MustQuery("select b from p where a=1") + expectedResult := fmt.Sprintf("%d \tid \ttask\testRows\toperator info\n\tPoint_Get_1\troot\t1 \ttable:p, handle:1 %s", i, "test.p") + // Also make sure that the plan digest is not empty + sql = "select exec_count, plan, table_names from information_schema.statements_summary " + + "where digest_text like 'select `b` from `p`%' and plan_digest != ''" + tk.MustQuery(sql).Check(testkit.Rows(expectedResult)) + } + + // Point get another database. + tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'") + // Test for Encode plan cache. + p1 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() + require.Greater(t, len(p1), 0) + rows := tk.MustQuery("select tidb_decode_plan('" + p1 + "');").Rows() + require.Equal(t, 1, len(rows)) + require.Equal(t, 1, len(rows[0])) + require.Regexp(t, "\n.*Point_Get.*table.tidb, index.PRIMARY.VARIABLE_NAME", rows[0][0]) + + sql = "select table_names from information_schema.statements_summary " + + "where digest_text like 'select `variable_value`%' and `schema_name`='test'" + tk.MustQuery(sql).Check(testkit.Rows("mysql.tidb")) + + // Test `create database`. + tk.MustExec("create database if not exists test") + // Test for Encode plan cache. + p2 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() + require.Equal(t, "", p2) + tk.MustQuery(`select table_names + from information_schema.statements_summary + where digest_text like 'create database%' and schema_name='test'`, + ).Check(testkit.Rows("")) + + // Test SELECT. + const failpointName = "github.com/pingcap/tidb/pkg/planner/core/mockPlanRowCount" + require.NoError(t, failpoint.Enable(failpointName, "return(100)")) + defer func() { require.NoError(t, failpoint.Disable(failpointName)) }() + tk.MustQuery("select * from t where a=2") + + // sum_cop_task_num is always 0 if tidb_enable_collect_execution_info disabled + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t100 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) + + // select ... order by + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text + from information_schema.statements_summary + order by exec_count desc limit 1`, + ).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) + + // Test different plans with same digest. + require.NoError(t, failpoint.Enable(failpointName, "return(1000)")) + tk.MustQuery("select * from t where a=3") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows( + "Select test test.t t:k 2 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t100 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) + + // Disable it again. + tk.MustExec("set global tidb_enable_stmt_summary = false") + defer tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) + + // Create a new session to test + tk = newTestKitWithRoot(t, store) + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT + + // This statement shouldn't be summarized. + tk.MustQuery("select * from t where a=2") + + // The table should be cleared. + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, plan + from information_schema.statements_summary`, + ).Check(testkit.Rows()) + + tk.MustExec("SET GLOBAL tidb_enable_stmt_summary = on") + // It should work immediately. + tk.MustExec("begin") + tk.MustExec("insert into t values(1, 'a')") + tk.MustExec("commit") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text " + + "from information_schema.statements_summary " + + "where digest_text like 'insert into `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') ")) + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text + from information_schema.statements_summary + where digest_text='commit'`, + ).Check(testkit.Rows("Commit test 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a')")) + + tk.MustQuery("select * from t where a=2") + sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + + "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + + "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + + "from information_schema.statements_summary " + + "where digest_text like 'select * from `t`%'" + tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + + "\tIndexLookUp_10 \troot \t1000 \t\n" + + "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t1000 \ttable:t, keep order:false, stats:pseudo")) + + // Disable it in global scope. + tk.MustExec("set global tidb_enable_stmt_summary = false") + + // Create a new session to test. + tk = newTestKitWithRoot(t, store) + + // Statement summary is disabled. + tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, + max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, + max_prewrite_regions, avg_affected_rows, query_sample_text, plan + from information_schema.statements_summary`, + ).Check(testkit.Rows()) + + tk.MustExec("set global tidb_enable_stmt_summary = on") + tk.MustExec("set global tidb_stmt_summary_history_size = 24") +} + +func TestStmtSummaryTablePrivilege(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t") + + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + // Create a new user to test statements summary table privilege + tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("create user 'test_user'@'localhost'") + defer tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("grant select on test.t to 'test_user'@'localhost'") + tk.MustExec("select * from t where a=1") + result := tk.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + result = tk.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + + tk1 := newTestKit(t, store) + tk1.Session().Auth(&auth.UserIdentity{ + Username: "test_user", + Hostname: "localhost", + AuthUsername: "test_user", + AuthHostname: "localhost", + }, nil, nil, nil) + + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Ordinary users can not see others' records + require.Equal(t, 0, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 0, len(result.Rows())) + tk1.MustExec("select * from t where b=1") + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Ordinary users can see his own records + require.Equal(t, 1, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 1, len(result.Rows())) + + tk.MustExec("grant process on *.* to 'test_user'@'localhost'") + result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") + // Users with 'PROCESS' privileges can query all records. + require.Equal(t, 2, len(result.Rows())) + result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") + require.Equal(t, 2, len(result.Rows())) +} + +func TestCapturePrivilege(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t") + + tk.MustExec("drop table if exists t1") + tk.MustExec("create table t1(a int, b varchar(10), key k(a))") + defer tk.MustExec("drop table if exists t1") + + // Clear all statements. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + // Create a new user to test statements summary table privilege + tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("create user 'test_user'@'localhost'") + defer tk.MustExec("drop user if exists 'test_user'@'localhost'") + tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") + tk.MustExec("select * from t where a=1") + tk.MustExec("select * from t where a=1") + tk.MustExec("admin capture bindings") + rows := tk.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + + tk1 := newTestKit(t, store) + tk1.Session().Auth(&auth.UserIdentity{ + Username: "test_user", + Hostname: "localhost", + AuthUsername: "test_user", + AuthHostname: "localhost", + }, nil, nil, nil) + + rows = tk1.MustQuery("show global bindings").Rows() + // Ordinary users can not see others' records + require.Len(t, rows, 0) + tk1.MustExec("select * from t1 where b=1") + tk1.MustExec("select * from t1 where b=1") + tk1.MustExec("admin capture bindings") + rows = tk1.MustQuery("show global bindings").Rows() + require.Len(t, rows, 1) + + tk.MustExec("grant all on *.* to 'test_user'@'localhost'") + tk1.MustExec("admin capture bindings") + rows = tk1.MustQuery("show global bindings").Rows() + require.Len(t, rows, 2) +} + +func TestStmtSummaryErrorCount(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + // Clear summaries. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + tk.MustExec("use test") + tk.MustExec("drop table if exists stmt_summary_test") + tk.MustExec("create table stmt_summary_test(id int primary key)") + tk.MustExec("insert into stmt_summary_test values(1)") + _, err := tk.Exec("insert into stmt_summary_test values(1)") + require.Error(t, err) + + sql := "select exec_count, sum_errors, sum_warnings from information_schema.statements_summary where digest_text like \"insert into `stmt_summary_test`%\"" + tk.MustQuery(sql).Check(testkit.Rows("2 1 0")) + + tk.MustExec("insert ignore into stmt_summary_test values(1)") + sql = "select exec_count, sum_errors, sum_warnings from information_schema.statements_summary where digest_text like \"insert ignore into `stmt_summary_test`%\"" + tk.MustQuery(sql).Check(testkit.Rows("1 0 1")) +} + +func TestStmtSummaryPreparedStatements(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + // Clear summaries. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + + tk.MustExec("use test") + tk.MustExec("prepare stmt from 'select ?'") + tk.MustExec("set @number=1") + tk.MustExec("execute stmt using @number") + + tk.MustQuery(`select exec_count + from information_schema.statements_summary + where digest_text like "prepare%"`).Check(testkit.Rows()) + tk.MustQuery(`select exec_count + from information_schema.statements_summary + where digest_text like "select ?"`).Check(testkit.Rows("1")) +} + +func TestStmtSummarySensitiveQuery(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustExec("drop user if exists user_sensitive;") + tk.MustExec("create user user_sensitive identified by '123456789';") + tk.MustExec("alter user 'user_sensitive'@'%' identified by 'abcdefg';") + tk.MustExec("set password for 'user_sensitive'@'%' = 'xyzuvw';") + tk.MustQuery("select query_sample_text from `information_schema`.`STATEMENTS_SUMMARY` " + + "where query_sample_text like '%user_sensitive%' and " + + "(query_sample_text like 'set password%' or query_sample_text like 'create user%' or query_sample_text like 'alter user%') " + + "order by query_sample_text;"). + Check(testkit.Rows( + "alter user {user_sensitive@% password = ***}", + "create user {user_sensitive@% password = ***}", + "set password for user user_sensitive@%", + )) +} + +func TestStmtSummaryTableOther(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + tk.MustExec("set global tidb_enable_stmt_summary=0") + tk.MustExec("set global tidb_enable_stmt_summary=1") + // set stmt size to 1 + // first sql + tk.MustExec("set global tidb_stmt_summary_max_stmt_count=1") + defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count=100") + // second sql, evict first sql from stmt_summary + tk.MustExec("show databases;") + // third sql, evict second sql from stmt_summary + tk.MustQuery("SELECT DIGEST_TEXT, DIGEST FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY`;"). + Check(testkit.Rows( + // digest in cache + // "show databases ;" + "show databases 0e247706bf6e791fbf4af8c8e7658af5ffc45c63179871202d8f91551ee03161", + // digest evicted + " ", + )) + // forth sql, evict third sql from stmt_summary + tk.MustQuery("SELECT SCHEMA_NAME FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY`;"). + Check(testkit.Rows( + // digest in cache + "test", // select xx from yy; + // digest evicted + "", + )) +} + +func TestStmtSummaryHistoryTableOther(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + + tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 1") + defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 100") + + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + // first sql + tk.MustExec("set global tidb_stmt_summary_max_stmt_count=1") + // second sql, evict first sql from stmt_summary + tk.MustExec("show databases;") + // third sql, evict second sql from stmt_summary + tk.MustQuery("SELECT DIGEST_TEXT, DIGEST FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY_HISTORY`;"). + Check(testkit.Rows( + // digest in cache + // "show databases ;" + "show databases 0e247706bf6e791fbf4af8c8e7658af5ffc45c63179871202d8f91551ee03161", + // digest evicted + " ", + )) + // forth sql, evict third sql from stmt_summary + tk.MustQuery("SELECT SCHEMA_NAME FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY_HISTORY`;"). + Check(testkit.Rows( + // digest in cache + "test", // select xx from yy; + // digest evicted + "", + )) +} + +func TestPerformanceSchemaforNonPrepPlanCache(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tk := newTestKitWithRoot(t, store) + tk.MustExec(`use test`) + tk.MustExec(`create table t (a int, key(a))`) + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=1`) + + tk.MustExec(`select * from t where a=1`) + tk.MustExec(`select * from t where a=1`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + + tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + + "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( + "2 select * from `t` where `a` = ? 0 1 1 select * from t where a=1")) + + tk.MustExec(`select * from t where a=2`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + tk.MustExec(`select * from t where a=3`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) + + // exec_count 2->4, plan_cache_hits 1->3 + tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + + "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( + "4 select * from `t` where `a` = ? 0 1 3 select * from t where a=1")) + + tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) + tk.MustExec(`select * from t where a=2`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + tk.MustExec(`select * from t where a=3`) + tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) + + // exec_count 4->6, plan_cache_hits 3->3 + tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + + "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( + "6 select * from `t` where `a` = ? 0 0 3 select * from t where a=1")) +} + +func TestPerformanceSchemaforPlanCache(t *testing.T) { + setupStmtSummary() + defer closeStmtSummary() + + store := testkit.CreateMockStore(t) + tmp := testkit.NewTestKit(t, store) + tmp.MustExec("set tidb_enable_prepared_plan_cache=ON") + tk := newTestKitWithPlanCache(t, store) + + // Clear summaries. + tk.MustExec("set global tidb_enable_stmt_summary = 0") + tk.MustExec("set global tidb_enable_stmt_summary = 1") + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec("create table t(a int)") + tk.MustExec("prepare stmt from 'select * from t'") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check( + testkit.Rows("0 0")) + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustExec("execute stmt") + tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check( + testkit.Rows("3 1")) +} + +func newTestKit(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + return tk +} + +func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := newTestKit(t, store) + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + return tk +} + +func newTestKitWithPlanCache(t *testing.T, store kv.Storage) *testkit.TestKit { + tk := testkit.NewTestKit(t, store) + se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ + PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), + }) + require.NoError(t, err) + tk.SetSession(se) + tk.RefreshConnectionID() + require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) + return tk +} + +func setupStmtSummary() { + stmtsummaryv2.Setup(&stmtsummaryv2.Config{ + Filename: "tidb-statements.log", + }) + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = true + }) +} + +func closeStmtSummary() { + config.UpdateGlobal(func(conf *config.Config) { + conf.Instance.StmtSummaryEnablePersistent = false + }) + stmtsummaryv2.GlobalStmtSummary.Close() + stmtsummaryv2.GlobalStmtSummary = nil + _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) +} diff --git a/pkg/util/stringutil/BUILD.bazel b/pkg/util/stringutil/BUILD.bazel new file mode 100644 index 0000000000000..8330247d2766c --- /dev/null +++ b/pkg/util/stringutil/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stringutil", + srcs = ["string_util.go"], + importpath = "github.com/pingcap/tidb/pkg/util/stringutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/util/hack", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "stringutil_test", + timeout = "short", + srcs = [ + "main_test.go", + "string_util_test.go", + ], + embed = [":stringutil"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/stringutil/main_test.go b/pkg/util/stringutil/main_test.go new file mode 100644 index 0000000000000..fa2992c77d0e9 --- /dev/null +++ b/pkg/util/stringutil/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package stringutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/stringutil/string_util.go b/pkg/util/stringutil/string_util.go similarity index 99% rename from util/stringutil/string_util.go rename to pkg/util/stringutil/string_util.go index 72f67f44715ab..ce6072251a76d 100644 --- a/util/stringutil/string_util.go +++ b/pkg/util/stringutil/string_util.go @@ -22,8 +22,8 @@ import ( "unicode/utf8" "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/hack" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/hack" ) // ErrSyntax indicates that a value does not have the right syntax for the target type. diff --git a/util/stringutil/string_util_test.go b/pkg/util/stringutil/string_util_test.go similarity index 100% rename from util/stringutil/string_util_test.go rename to pkg/util/stringutil/string_util_test.go diff --git a/pkg/util/syncutil/BUILD.bazel b/pkg/util/syncutil/BUILD.bazel new file mode 100644 index 0000000000000..0d534a7a9118e --- /dev/null +++ b/pkg/util/syncutil/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "syncutil", + srcs = [ + "mutex_deadlock.go", #keep + "mutex_sync.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/syncutil", + visibility = ["//visibility:public"], + deps = ["@com_github_sasha_s_go_deadlock//:go-deadlock"], #keep +) diff --git a/util/syncutil/mutex_deadlock.go b/pkg/util/syncutil/mutex_deadlock.go similarity index 100% rename from util/syncutil/mutex_deadlock.go rename to pkg/util/syncutil/mutex_deadlock.go diff --git a/util/syncutil/mutex_sync.go b/pkg/util/syncutil/mutex_sync.go similarity index 100% rename from util/syncutil/mutex_sync.go rename to pkg/util/syncutil/mutex_sync.go diff --git a/pkg/util/sys/linux/BUILD.bazel b/pkg/util/sys/linux/BUILD.bazel new file mode 100644 index 0000000000000..7a448ba800e86 --- /dev/null +++ b/pkg/util/sys/linux/BUILD.bazel @@ -0,0 +1,70 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "linux", + srcs = [ + "sys_linux.go", + "sys_other.go", + "sys_windows.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/sys/linux", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:aix": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:android": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:darwin": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:dragonfly": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:freebsd": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:illumos": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:ios": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:js": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:linux": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:netbsd": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:openbsd": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:plan9": [ + "@org_golang_x_sys//unix", + ], + "@io_bazel_rules_go//go/platform:solaris": [ + "@org_golang_x_sys//unix", + ], + "//conditions:default": [], + }), +) + +go_test( + name = "linux_test", + timeout = "short", + srcs = [ + "main_test.go", + "sys_test.go", + ], + flaky = True, + deps = [ + ":linux", + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/sys/linux/main_test.go b/pkg/util/sys/linux/main_test.go new file mode 100644 index 0000000000000..c106fbc062e85 --- /dev/null +++ b/pkg/util/sys/linux/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package linux_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/sys/linux/sys_linux.go b/pkg/util/sys/linux/sys_linux.go similarity index 100% rename from util/sys/linux/sys_linux.go rename to pkg/util/sys/linux/sys_linux.go diff --git a/util/sys/linux/sys_other.go b/pkg/util/sys/linux/sys_other.go similarity index 100% rename from util/sys/linux/sys_other.go rename to pkg/util/sys/linux/sys_other.go diff --git a/pkg/util/sys/linux/sys_test.go b/pkg/util/sys/linux/sys_test.go new file mode 100644 index 0000000000000..2b492bc560ae0 --- /dev/null +++ b/pkg/util/sys/linux/sys_test.go @@ -0,0 +1,27 @@ +// Copyright 2019 PingCAP, Inc. +// +// 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. +package linux_test + +import ( + "testing" + + . "github.com/pingcap/tidb/pkg/util/sys/linux" + "github.com/stretchr/testify/require" +) + +func TestGetOSVersion(t *testing.T) { + osRelease, err := OSVersion() + require.NoError(t, err) + require.NotEmpty(t, osRelease) +} diff --git a/util/sys/linux/sys_windows.go b/pkg/util/sys/linux/sys_windows.go similarity index 100% rename from util/sys/linux/sys_windows.go rename to pkg/util/sys/linux/sys_windows.go diff --git a/pkg/util/sys/storage/BUILD.bazel b/pkg/util/sys/storage/BUILD.bazel new file mode 100644 index 0000000000000..2c977c407657d --- /dev/null +++ b/pkg/util/sys/storage/BUILD.bazel @@ -0,0 +1,34 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "storage", + srcs = [ + "sys_other.go", + "sys_posix.go", + "sys_windows.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/sys/storage", + visibility = ["//visibility:public"], + deps = select({ + "@io_bazel_rules_go//go/platform:windows": [ + "@org_golang_x_sys//windows", + ], + "//conditions:default": [], + }), +) + +go_test( + name = "storage_test", + timeout = "short", + srcs = [ + "main_test.go", + "sys_test.go", + ], + embed = [":storage"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/sys/storage/main_test.go b/pkg/util/sys/storage/main_test.go new file mode 100644 index 0000000000000..8961a999b8d33 --- /dev/null +++ b/pkg/util/sys/storage/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package storage + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/sys/storage/sys_other.go b/pkg/util/sys/storage/sys_other.go similarity index 100% rename from util/sys/storage/sys_other.go rename to pkg/util/sys/storage/sys_other.go diff --git a/util/sys/storage/sys_posix.go b/pkg/util/sys/storage/sys_posix.go similarity index 100% rename from util/sys/storage/sys_posix.go rename to pkg/util/sys/storage/sys_posix.go diff --git a/pkg/util/sys/storage/sys_test.go b/pkg/util/sys/storage/sys_test.go new file mode 100644 index 0000000000000..f83ec0018fff2 --- /dev/null +++ b/pkg/util/sys/storage/sys_test.go @@ -0,0 +1,30 @@ +// Copyright 2020 PingCAP, Inc. +// +// 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. + +package storage_test + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/util/sys/storage" + "github.com/stretchr/testify/require" +) + +func TestGetTargetDirectoryCapacity(t *testing.T) { + r, err := storage.GetTargetDirectoryCapacity(".") + require.NoError(t, err) + require.GreaterOrEqual(t, r, uint64(1), "couldn't get capacity") + + //TODO: check the value of r with `df` in linux +} diff --git a/util/sys/storage/sys_windows.go b/pkg/util/sys/storage/sys_windows.go similarity index 100% rename from util/sys/storage/sys_windows.go rename to pkg/util/sys/storage/sys_windows.go diff --git a/pkg/util/systimemon/BUILD.bazel b/pkg/util/systimemon/BUILD.bazel new file mode 100644 index 0000000000000..40eb53fa5bb9f --- /dev/null +++ b/pkg/util/systimemon/BUILD.bazel @@ -0,0 +1,29 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "systimemon", + srcs = ["systime_mon.go"], + importpath = "github.com/pingcap/tidb/pkg/util/systimemon", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/logutil", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "systimemon_test", + timeout = "short", + srcs = [ + "main_test.go", + "systime_mon_test.go", + ], + embed = [":systimemon"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/systimemon/main_test.go b/pkg/util/systimemon/main_test.go new file mode 100644 index 0000000000000..931b406f4b62a --- /dev/null +++ b/pkg/util/systimemon/main_test.go @@ -0,0 +1,34 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package systimemon + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("github.com/pingcap/tidb/pkg/util/systimemon.StartMonitor"), + } + + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/systimemon/systime_mon.go b/pkg/util/systimemon/systime_mon.go similarity index 96% rename from util/systimemon/systime_mon.go rename to pkg/util/systimemon/systime_mon.go index 26819a4159776..c9a1596798bde 100644 --- a/util/systimemon/systime_mon.go +++ b/pkg/util/systimemon/systime_mon.go @@ -17,7 +17,7 @@ package systimemon import ( "time" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/systimemon/systime_mon_test.go b/pkg/util/systimemon/systime_mon_test.go similarity index 100% rename from util/systimemon/systime_mon_test.go rename to pkg/util/systimemon/systime_mon_test.go diff --git a/pkg/util/table-filter/BUILD.bazel b/pkg/util/table-filter/BUILD.bazel new file mode 100644 index 0000000000000..12ba75d44b426 --- /dev/null +++ b/pkg/util/table-filter/BUILD.bazel @@ -0,0 +1,30 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "table-filter", + srcs = [ + "column_filter.go", + "compat.go", + "matchers.go", + "parser.go", + "table_filter.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/table-filter", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "table-filter_test", + timeout = "short", + srcs = [ + "column_filter_test.go", + "compat_test.go", + "table_filter_test.go", + ], + flaky = True, + deps = [ + ":table-filter", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/table-filter/README.md b/pkg/util/table-filter/README.md similarity index 100% rename from util/table-filter/README.md rename to pkg/util/table-filter/README.md diff --git a/util/table-filter/column_filter.go b/pkg/util/table-filter/column_filter.go similarity index 100% rename from util/table-filter/column_filter.go rename to pkg/util/table-filter/column_filter.go diff --git a/util/table-filter/column_filter_test.go b/pkg/util/table-filter/column_filter_test.go similarity index 99% rename from util/table-filter/column_filter_test.go rename to pkg/util/table-filter/column_filter_test.go index 4dba1812c14f3..2a44db8c876ad 100644 --- a/util/table-filter/column_filter_test.go +++ b/pkg/util/table-filter/column_filter_test.go @@ -19,7 +19,7 @@ import ( "path/filepath" "testing" - filter "github.com/pingcap/tidb/util/table-filter" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" ) diff --git a/util/table-filter/compat.go b/pkg/util/table-filter/compat.go similarity index 100% rename from util/table-filter/compat.go rename to pkg/util/table-filter/compat.go diff --git a/util/table-filter/compat_test.go b/pkg/util/table-filter/compat_test.go similarity index 99% rename from util/table-filter/compat_test.go rename to pkg/util/table-filter/compat_test.go index fc76a890305a9..695bb83c874b0 100644 --- a/util/table-filter/compat_test.go +++ b/pkg/util/table-filter/compat_test.go @@ -17,7 +17,7 @@ package filter_test import ( "testing" - filter "github.com/pingcap/tidb/util/table-filter" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" ) diff --git a/util/table-filter/matchers.go b/pkg/util/table-filter/matchers.go similarity index 100% rename from util/table-filter/matchers.go rename to pkg/util/table-filter/matchers.go diff --git a/util/table-filter/parser.go b/pkg/util/table-filter/parser.go similarity index 100% rename from util/table-filter/parser.go rename to pkg/util/table-filter/parser.go diff --git a/util/table-filter/table_filter.go b/pkg/util/table-filter/table_filter.go similarity index 100% rename from util/table-filter/table_filter.go rename to pkg/util/table-filter/table_filter.go diff --git a/util/table-filter/table_filter_test.go b/pkg/util/table-filter/table_filter_test.go similarity index 99% rename from util/table-filter/table_filter_test.go rename to pkg/util/table-filter/table_filter_test.go index 2928d498098ea..2120812615c2b 100644 --- a/util/table-filter/table_filter_test.go +++ b/pkg/util/table-filter/table_filter_test.go @@ -19,7 +19,7 @@ import ( "path/filepath" "testing" - filter "github.com/pingcap/tidb/util/table-filter" + filter "github.com/pingcap/tidb/pkg/util/table-filter" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/table-router/BUILD.bazel b/pkg/util/table-router/BUILD.bazel new file mode 100644 index 0000000000000..580f6c5593721 --- /dev/null +++ b/pkg/util/table-router/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "table-router", + srcs = ["router.go"], + importpath = "github.com/pingcap/tidb/pkg/util/table-router", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/table-rule-selector", + "@com_github_pingcap_errors//:errors", + ], +) + +go_test( + name = "table-router_test", + timeout = "short", + srcs = ["router_test.go"], + embed = [":table-router"], + flaky = True, + deps = [ + "//pkg/util/table-rule-selector", + "@com_github_stretchr_testify//require", + ], +) diff --git a/util/table-router/router.go b/pkg/util/table-router/router.go similarity index 99% rename from util/table-router/router.go rename to pkg/util/table-router/router.go index 9d88f784823c9..1fd927314d4ec 100644 --- a/util/table-router/router.go +++ b/pkg/util/table-router/router.go @@ -20,7 +20,7 @@ import ( "strings" "github.com/pingcap/errors" - selector "github.com/pingcap/tidb/util/table-rule-selector" + selector "github.com/pingcap/tidb/pkg/util/table-rule-selector" ) // TableRule is a rule to route schema/table to target schema/table diff --git a/util/table-router/router_test.go b/pkg/util/table-router/router_test.go similarity index 98% rename from util/table-router/router_test.go rename to pkg/util/table-router/router_test.go index f05db7734193d..dc3c295841ce6 100644 --- a/util/table-router/router_test.go +++ b/pkg/util/table-router/router_test.go @@ -17,7 +17,7 @@ package router import ( "testing" - selector "github.com/pingcap/tidb/util/table-rule-selector" + selector "github.com/pingcap/tidb/pkg/util/table-rule-selector" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/table-rule-selector/BUILD.bazel b/pkg/util/table-rule-selector/BUILD.bazel new file mode 100644 index 0000000000000..e486f315b612c --- /dev/null +++ b/pkg/util/table-rule-selector/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "table-rule-selector", + srcs = ["trie_selector.go"], + importpath = "github.com/pingcap/tidb/pkg/util/table-rule-selector", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "table-rule-selector_test", + timeout = "short", + srcs = ["selector_test.go"], + embed = [":table-rule-selector"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/table-rule-selector/selector_test.go b/pkg/util/table-rule-selector/selector_test.go similarity index 100% rename from util/table-rule-selector/selector_test.go rename to pkg/util/table-rule-selector/selector_test.go diff --git a/util/table-rule-selector/trie_selector.go b/pkg/util/table-rule-selector/trie_selector.go similarity index 100% rename from util/table-rule-selector/trie_selector.go rename to pkg/util/table-rule-selector/trie_selector.go diff --git a/pkg/util/tableutil/BUILD.bazel b/pkg/util/tableutil/BUILD.bazel new file mode 100644 index 0000000000000..bafed296d54ae --- /dev/null +++ b/pkg/util/tableutil/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tableutil", + srcs = ["tableutil.go"], + importpath = "github.com/pingcap/tidb/pkg/util/tableutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/meta/autoid", + "//pkg/parser/model", + ], +) diff --git a/util/tableutil/tableutil.go b/pkg/util/tableutil/tableutil.go similarity index 94% rename from util/tableutil/tableutil.go rename to pkg/util/tableutil/tableutil.go index 4d9165fa3dce0..5ef313c3705cd 100644 --- a/util/tableutil/tableutil.go +++ b/pkg/util/tableutil/tableutil.go @@ -15,8 +15,8 @@ package tableutil import ( - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" ) // TempTable is used to store transaction-specific or session-specific information for global / local temporary tables. diff --git a/pkg/util/texttree/BUILD.bazel b/pkg/util/texttree/BUILD.bazel new file mode 100644 index 0000000000000..72b9a3ec23d88 --- /dev/null +++ b/pkg/util/texttree/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "texttree", + srcs = ["texttree.go"], + importpath = "github.com/pingcap/tidb/pkg/util/texttree", + visibility = ["//visibility:public"], +) + +go_test( + name = "texttree_test", + timeout = "short", + srcs = [ + "main_test.go", + "texttree_test.go", + ], + embed = [":texttree"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/texttree/main_test.go b/pkg/util/texttree/main_test.go new file mode 100644 index 0000000000000..57a2d78b7347b --- /dev/null +++ b/pkg/util/texttree/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package texttree + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/texttree/texttree.go b/pkg/util/texttree/texttree.go similarity index 100% rename from util/texttree/texttree.go rename to pkg/util/texttree/texttree.go diff --git a/util/texttree/texttree_test.go b/pkg/util/texttree/texttree_test.go similarity index 96% rename from util/texttree/texttree_test.go rename to pkg/util/texttree/texttree_test.go index 5143cac90e2ed..019123142513f 100644 --- a/util/texttree/texttree_test.go +++ b/pkg/util/texttree/texttree_test.go @@ -17,7 +17,7 @@ package texttree_test import ( "testing" - "github.com/pingcap/tidb/util/texttree" + "github.com/pingcap/tidb/pkg/util/texttree" "github.com/stretchr/testify/require" ) diff --git a/pkg/util/tiflash/BUILD.bazel b/pkg/util/tiflash/BUILD.bazel new file mode 100644 index 0000000000000..065ed59873641 --- /dev/null +++ b/pkg/util/tiflash/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tiflash", + srcs = ["tiflash_replica_read.go"], + importpath = "github.com/pingcap/tidb/pkg/util/tiflash", + visibility = ["//visibility:public"], +) diff --git a/util/tiflash/tiflash_replica_read.go b/pkg/util/tiflash/tiflash_replica_read.go similarity index 100% rename from util/tiflash/tiflash_replica_read.go rename to pkg/util/tiflash/tiflash_replica_read.go diff --git a/pkg/util/tiflashcompute/BUILD.bazel b/pkg/util/tiflashcompute/BUILD.bazel new file mode 100644 index 0000000000000..2d84cba472216 --- /dev/null +++ b/pkg/util/tiflashcompute/BUILD.bazel @@ -0,0 +1,18 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tiflashcompute", + srcs = [ + "dispatch_policy.go", + "topo_fetcher.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/tiflashcompute", + visibility = ["//visibility:public"], + deps = [ + "//pkg/errno", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@com_github_pingcap_errors//:errors", + "@org_uber_go_zap//:zap", + ], +) diff --git a/util/tiflashcompute/dispatch_policy.go b/pkg/util/tiflashcompute/dispatch_policy.go similarity index 100% rename from util/tiflashcompute/dispatch_policy.go rename to pkg/util/tiflashcompute/dispatch_policy.go diff --git a/util/tiflashcompute/topo_fetcher.go b/pkg/util/tiflashcompute/topo_fetcher.go similarity index 99% rename from util/tiflashcompute/topo_fetcher.go rename to pkg/util/tiflashcompute/topo_fetcher.go index 9f6e89bfad995..3b15159aa2636 100644 --- a/util/tiflashcompute/topo_fetcher.go +++ b/pkg/util/tiflashcompute/topo_fetcher.go @@ -24,9 +24,9 @@ import ( "sync" "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/pkg/util/tikvutil/BUILD.bazel b/pkg/util/tikvutil/BUILD.bazel new file mode 100644 index 0000000000000..e8391be50a080 --- /dev/null +++ b/pkg/util/tikvutil/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tikvutil", + srcs = ["tikvutil.go"], + importpath = "github.com/pingcap/tidb/pkg/util/tikvutil", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_atomic//:atomic"], +) diff --git a/util/tikvutil/tikvutil.go b/pkg/util/tikvutil/tikvutil.go similarity index 100% rename from util/tikvutil/tikvutil.go rename to pkg/util/tikvutil/tikvutil.go diff --git a/pkg/util/timeutil/BUILD.bazel b/pkg/util/timeutil/BUILD.bazel new file mode 100644 index 0000000000000..a0f51bea94e03 --- /dev/null +++ b/pkg/util/timeutil/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "timeutil", + srcs = [ + "errors.go", + "time.go", + "time_zone.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/timeutil", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/mysql", + "//pkg/types", + "//pkg/util/dbterror", + "//pkg/util/logutil", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "timeutil_test", + timeout = "short", + srcs = [ + "main_test.go", + "time_test.go", + "time_zone_test.go", + ], + embed = [":timeutil"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/timeutil/errors.go b/pkg/util/timeutil/errors.go new file mode 100644 index 0000000000000..7d3f2a86f8a95 --- /dev/null +++ b/pkg/util/timeutil/errors.go @@ -0,0 +1,23 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package timeutil + +import ( + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/util/dbterror" +) + +// ErrUnknownTimeZone indicates timezone is unknown. +var ErrUnknownTimeZone = dbterror.ClassVariable.NewStd(mysql.ErrUnknownTimeZone) diff --git a/pkg/util/timeutil/main_test.go b/pkg/util/timeutil/main_test.go new file mode 100644 index 0000000000000..430895db6ddb1 --- /dev/null +++ b/pkg/util/timeutil/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package timeutil + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/timeutil/time.go b/pkg/util/timeutil/time.go similarity index 100% rename from util/timeutil/time.go rename to pkg/util/timeutil/time.go diff --git a/util/timeutil/time_test.go b/pkg/util/timeutil/time_test.go similarity index 100% rename from util/timeutil/time_test.go rename to pkg/util/timeutil/time_test.go diff --git a/util/timeutil/time_zone.go b/pkg/util/timeutil/time_zone.go similarity index 99% rename from util/timeutil/time_zone.go rename to pkg/util/timeutil/time_zone.go index 64f58a72077d9..173810695414c 100644 --- a/util/timeutil/time_zone.go +++ b/pkg/util/timeutil/time_zone.go @@ -23,8 +23,8 @@ import ( "syscall" "time" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/atomic" "go.uber.org/zap" ) diff --git a/util/timeutil/time_zone_test.go b/pkg/util/timeutil/time_zone_test.go similarity index 100% rename from util/timeutil/time_zone_test.go rename to pkg/util/timeutil/time_zone_test.go diff --git a/pkg/util/tls/BUILD.bazel b/pkg/util/tls/BUILD.bazel new file mode 100644 index 0000000000000..d1c9349663328 --- /dev/null +++ b/pkg/util/tls/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "tls", + srcs = ["tls.go"], + importpath = "github.com/pingcap/tidb/pkg/util/tls", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_atomic//:atomic"], +) diff --git a/util/tls/tls.go b/pkg/util/tls/tls.go similarity index 100% rename from util/tls/tls.go rename to pkg/util/tls/tls.go diff --git a/pkg/util/topsql/BUILD.bazel b/pkg/util/topsql/BUILD.bazel new file mode 100644 index 0000000000000..22e2f2ab9a268 --- /dev/null +++ b/pkg/util/topsql/BUILD.bazel @@ -0,0 +1,49 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "topsql", + srcs = ["topsql.go"], + importpath = "github.com/pingcap/tidb/pkg/util/topsql", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/util/logutil", + "//pkg/util/plancodec", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/reporter", + "//pkg/util/topsql/stmtstats", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "topsql_test", + timeout = "short", + srcs = [ + "main_test.go", + "topsql_test.go", + ], + embed = [":topsql"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/parser", + "//pkg/testkit/testsetup", + "//pkg/util", + "//pkg/util/cpuprofile", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/collector/mock", + "//pkg/util/topsql/reporter", + "//pkg/util/topsql/reporter/mock", + "//pkg/util/topsql/state", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//require", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//credentials/insecure", + "@org_golang_google_grpc//keepalive", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/topsql/collector/BUILD.bazel b/pkg/util/topsql/collector/BUILD.bazel new file mode 100644 index 0000000000000..18f182775d7ce --- /dev/null +++ b/pkg/util/topsql/collector/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "collector", + srcs = ["cpu.go"], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/collector", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util", + "//pkg/util/cpuprofile", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/topsql/state", + "@com_github_google_pprof//profile", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "collector_test", + timeout = "short", + srcs = ["main_test.go"], + embed = [":collector"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/cpuprofile", + "//pkg/util/cpuprofile/testutil", + "//pkg/util/topsql/state", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/topsql/collector/cpu.go b/pkg/util/topsql/collector/cpu.go new file mode 100644 index 0000000000000..c32f8ca45aa5d --- /dev/null +++ b/pkg/util/topsql/collector/cpu.go @@ -0,0 +1,266 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package collector + +import ( + "context" + "runtime/pprof" + "sync" + "time" + + "github.com/google/pprof/profile" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "go.uber.org/zap" +) + +const ( + labelSQLDigest = "sql_digest" + labelPlanDigest = "plan_digest" +) + +// Collector uses to collect SQL execution cpu time. +type Collector interface { + // Collect uses to collect the SQL execution cpu time. + Collect(stats []SQLCPUTimeRecord) +} + +// SQLCPUTimeRecord represents a single record of how much cpu time a sql plan consumes in one second. +// +// PlanDigest can be empty, because: +// 1. some sql statements has no plan, like `COMMIT` +// 2. when a sql statement is being compiled, there's no plan yet +type SQLCPUTimeRecord struct { + SQLDigest []byte + PlanDigest []byte + CPUTimeMs uint32 +} + +// SQLCPUCollector uses to consume cpu profile from globalCPUProfiler, then parse the SQL CPU usage from the cpu profile data. +// It is not thread-safe, should only be used in one goroutine. +type SQLCPUCollector struct { + ctx context.Context + collector Collector + cancel context.CancelFunc + wg sync.WaitGroup + started bool + registered bool +} + +// NewSQLCPUCollector create a SQLCPUCollector. +func NewSQLCPUCollector(c Collector) *SQLCPUCollector { + return &SQLCPUCollector{ + collector: c, + } +} + +// Start uses to start to run SQLCPUCollector. +// This will register a consumer into globalCPUProfiler, then SQLCPUCollector will receive cpu profile data per seconds. +// WARN: this function is not thread-safe. +func (sp *SQLCPUCollector) Start() { + if sp.started { + return + } + sp.started = true + sp.ctx, sp.cancel = context.WithCancel(context.Background()) + sp.wg.Add(1) + go sp.collectSQLCPULoop() + logutil.BgLogger().Info("sql cpu collector started") +} + +// Stop uses to stop the SQLCPUCollector. +// WARN: this function is not thread-safe. +func (sp *SQLCPUCollector) Stop() { + if !sp.started { + return + } + sp.started = false + if sp.cancel != nil { + sp.cancel() + } + + sp.wg.Wait() + logutil.BgLogger().Info("sql cpu collector stopped") +} + +var defCollectTickerInterval = time.Second + +func (sp *SQLCPUCollector) collectSQLCPULoop() { + profileConsumer := make(cpuprofile.ProfileConsumer, 1) + ticker := time.NewTicker(defCollectTickerInterval) + defer func() { + sp.wg.Done() + sp.doUnregister(profileConsumer) + ticker.Stop() + }() + defer util.Recover("top-sql", "startAnalyzeProfileWorker", nil, false) + + for { + if topsqlstate.TopSQLEnabled() { + sp.doRegister(profileConsumer) + } else { + sp.doUnregister(profileConsumer) + } + + select { + case <-sp.ctx.Done(): + return + case <-ticker.C: + case data := <-profileConsumer: + sp.handleProfileData(data) + } + } +} + +func (sp *SQLCPUCollector) handleProfileData(data *cpuprofile.ProfileData) { + if data.Error != nil { + return + } + + p, err := profile.ParseData(data.Data.Bytes()) + if err != nil { + logutil.BgLogger().Error("parse profile error", zap.Error(err)) + return + } + stats := sp.parseCPUProfileBySQLLabels(p) + sp.collector.Collect(stats) +} + +func (sp *SQLCPUCollector) doRegister(profileConsumer cpuprofile.ProfileConsumer) { + if sp.registered { + return + } + sp.registered = true + cpuprofile.Register(profileConsumer) +} + +func (sp *SQLCPUCollector) doUnregister(profileConsumer cpuprofile.ProfileConsumer) { + if !sp.registered { + return + } + sp.registered = false + cpuprofile.Unregister(profileConsumer) +} + +// parseCPUProfileBySQLLabels uses to aggregate the cpu-profile sample data by sql_digest and plan_digest labels, +// output the TopSQLCPUTimeRecord slice. Want to know more information about profile labels, see https://rakyll.org/profiler-labels/ +// The sql_digest label is been set by `SetSQLLabels` function after parse the SQL. +// The plan_digest label is been set by `SetSQLAndPlanLabels` function after build the SQL plan. +// Since `SQLCPUCollector` only care about the cpu time that consume by (sql_digest,plan_digest), the other sample data +// without those label will be ignore. +func (sp *SQLCPUCollector) parseCPUProfileBySQLLabels(p *profile.Profile) []SQLCPUTimeRecord { + sqlMap := make(map[string]*sqlStats) + idx := len(p.SampleType) - 1 + for _, s := range p.Sample { + digests, ok := s.Label[labelSQLDigest] + if !ok || len(digests) == 0 { + continue + } + for _, digest := range digests { + stmt, ok := sqlMap[digest] + if !ok { + stmt = &sqlStats{ + plans: make(map[string]int64), + total: 0, + } + sqlMap[digest] = stmt + } + stmt.total += s.Value[idx] + + plans := s.Label[labelPlanDigest] + for _, plan := range plans { + stmt.plans[plan] += s.Value[idx] + } + } + } + return sp.createSQLStats(sqlMap) +} + +func (*SQLCPUCollector) createSQLStats(sqlMap map[string]*sqlStats) []SQLCPUTimeRecord { + stats := make([]SQLCPUTimeRecord, 0, len(sqlMap)) + for sqlDigest, stmt := range sqlMap { + stmt.tune() + for planDigest, val := range stmt.plans { + stats = append(stats, SQLCPUTimeRecord{ + SQLDigest: []byte(sqlDigest), + PlanDigest: []byte(planDigest), + CPUTimeMs: uint32(time.Duration(val).Milliseconds()), + }) + } + } + return stats +} + +type sqlStats struct { + plans map[string]int64 + total int64 +} + +// tune use to adjust sql stats. Consider following situation: +// The `sqlStats` maybe: +// +// plans: { +// "table_scan": 200ms, // The cpu time of the sql that plan with `table_scan` is 200ms. +// "index_scan": 300ms, // The cpu time of the sql that plan with `index_scan` is 300ms. +// }, +// total: 600ms, // The total cpu time of the sql is 600ms. +// +// total_time - table_scan_time - index_scan_time = 100ms, and this 100ms means those sample data only contain the +// sql_digest label, doesn't contain the plan_digest label. This is cause by the `pprof profile` is base on sample, +// and the plan digest can only be set after optimizer generated execution plan. So the remain 100ms means the plan +// optimizer takes time to generated plan. +// After this tune function, the `sqlStats` become to: +// +// plans: { +// "" : 100ms, // 600 - 200 - 300 = 100ms, indicate the optimizer generated plan time cost. +// "table_scan": 200ms, +// "index_scan": 300ms, +// }, +// total: 600ms, +func (s *sqlStats) tune() { + if len(s.plans) == 0 { + s.plans[""] = s.total + return + } + if len(s.plans) == 1 { + for k := range s.plans { + s.plans[k] = s.total + return + } + } + planTotal := int64(0) + for _, v := range s.plans { + planTotal += v + } + optimize := s.total - planTotal + if optimize <= 0 { + return + } + s.plans[""] += optimize +} + +// CtxWithSQLDigest wrap the ctx with sql digest. +func CtxWithSQLDigest(ctx context.Context, sqlDigest []byte) context.Context { + return pprof.WithLabels(ctx, pprof.Labels(labelSQLDigest, string(hack.String(sqlDigest)))) +} + +// CtxWithSQLAndPlanDigest wrap the ctx with sql digest and plan digest. +func CtxWithSQLAndPlanDigest(ctx context.Context, sqlDigest, planDigest []byte) context.Context { + return pprof.WithLabels(ctx, pprof.Labels(labelSQLDigest, string(hack.String(sqlDigest)), + labelPlanDigest, string(hack.String(planDigest)))) +} diff --git a/pkg/util/topsql/collector/main_test.go b/pkg/util/topsql/collector/main_test.go new file mode 100644 index 0000000000000..dbbcf9a14ee90 --- /dev/null +++ b/pkg/util/topsql/collector/main_test.go @@ -0,0 +1,110 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package collector + +import ( + "context" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/cpuprofile/testutil" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} + +func TestPProfCPUProfile(t *testing.T) { + // short the interval to speed up the test. + interval := time.Millisecond * 400 + defCollectTickerInterval = interval + cpuprofile.DefProfileDuration = interval + + err := cpuprofile.StartCPUProfiler() + require.NoError(t, err) + defer cpuprofile.StopCPUProfiler() + + topsqlstate.EnableTopSQL() + mc := &mockCollector{ + dataCh: make(chan []SQLCPUTimeRecord, 10), + } + sqlCPUCollector := NewSQLCPUCollector(mc) + sqlCPUCollector.Start() + defer sqlCPUCollector.Stop() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + testutil.MockCPULoad(ctx, "sql", "sql_digest", "plan_digest") + + data := <-mc.dataCh + require.True(t, len(data) > 0) + require.Equal(t, []byte("sql_digest value"), data[0].SQLDigest) + + // Test disable then re-enable. + topsqlstate.DisableTopSQL() + time.Sleep(interval * 2) + dataChLen := len(mc.dataCh) + deltaLen := 0 + topsqlstate.EnableTopSQL() + for i := 0; i < 10; i++ { + t1 := time.Now() + data = <-mc.dataCh + require.True(t, time.Since(t1) < interval*4) + if len(data) > 0 { + deltaLen++ + if deltaLen > dataChLen { + // Here we can ensure that we receive new data after "re-enable". + break + } + } + } + require.True(t, len(data) > 0) + require.True(t, deltaLen > dataChLen) + require.Equal(t, []byte("sql_digest value"), data[0].SQLDigest) +} + +func TestSQLStatsTune(t *testing.T) { + s := &sqlStats{plans: map[string]int64{"plan-1": 80}, total: 100} + s.tune() + require.Equal(t, int64(100), s.total) + require.Equal(t, int64(100), s.plans["plan-1"]) + + s = &sqlStats{plans: map[string]int64{"plan-1": 30, "plan-2": 30}, total: 100} + s.tune() + require.Equal(t, int64(100), s.total) + require.Equal(t, int64(30), s.plans["plan-1"]) + require.Equal(t, int64(30), s.plans["plan-2"]) + require.Equal(t, int64(40), s.plans[""]) +} + +type mockCollector struct { + dataCh chan []SQLCPUTimeRecord +} + +// Collect implements the Collector interface. +func (c *mockCollector) Collect(records []SQLCPUTimeRecord) { + c.dataCh <- records +} diff --git a/pkg/util/topsql/collector/mock/BUILD.bazel b/pkg/util/topsql/collector/mock/BUILD.bazel new file mode 100644 index 0000000000000..b88f552ac39e6 --- /dev/null +++ b/pkg/util/topsql/collector/mock/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mock", + srcs = ["mock.go"], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/collector/mock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/stmtstats", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/util/topsql/collector/mock/mock.go b/pkg/util/topsql/collector/mock/mock.go new file mode 100644 index 0000000000000..b44495a5559aa --- /dev/null +++ b/pkg/util/topsql/collector/mock/mock.go @@ -0,0 +1,221 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "bytes" + "sync" + "time" + + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +// TopSQLCollector uses for testing. +type TopSQLCollector struct { + // sql_digest -> normalized SQL + sqlMap map[string]string + // plan_digest -> normalized plan + planMap map[string]string + // (sql + plan_digest) -> sql stats + sqlStatsMap map[string]*collector.SQLCPUTimeRecord + collectCnt atomic.Int64 + sync.Mutex +} + +// NewTopSQLCollector uses for testing. +func NewTopSQLCollector() *TopSQLCollector { + return &TopSQLCollector{ + sqlMap: make(map[string]string), + planMap: make(map[string]string), + sqlStatsMap: make(map[string]*collector.SQLCPUTimeRecord), + } +} + +// Start implements TopSQLReporter interface. +func (*TopSQLCollector) Start() {} + +// Collect uses for testing. +func (c *TopSQLCollector) Collect(stats []collector.SQLCPUTimeRecord) { + defer c.collectCnt.Inc() + if len(stats) == 0 { + return + } + c.Lock() + defer c.Unlock() + for _, stmt := range stats { + hash := c.hash(stmt) + stats, ok := c.sqlStatsMap[hash] + if !ok { + stats = &collector.SQLCPUTimeRecord{ + SQLDigest: stmt.SQLDigest, + PlanDigest: stmt.PlanDigest, + } + c.sqlStatsMap[hash] = stats + } + stats.CPUTimeMs += stmt.CPUTimeMs + logutil.BgLogger().Info("mock top sql collector collected sql", + zap.String("sql", c.sqlMap[string(stmt.SQLDigest)]), + zap.Bool("has-plan", len(c.planMap[string(stmt.PlanDigest)]) > 0)) + } +} + +// CollectStmtStatsMap implements stmtstats.Collector. +func (*TopSQLCollector) CollectStmtStatsMap(_ stmtstats.StatementStatsMap) {} + +// GetSQLStatsBySQLWithRetry uses for testing. +func (c *TopSQLCollector) GetSQLStatsBySQLWithRetry(sql string, planIsNotNull bool) []*collector.SQLCPUTimeRecord { + after := time.After(time.Second * 10) + for { + select { + case <-after: + return nil + default: + } + stats := c.GetSQLStatsBySQL(sql, planIsNotNull) + if len(stats) > 0 { + return stats + } + c.WaitCollectCnt(1) + } +} + +// GetSQLStatsBySQL uses for testing. +func (c *TopSQLCollector) GetSQLStatsBySQL(sql string, planIsNotNull bool) []*collector.SQLCPUTimeRecord { + stats := make([]*collector.SQLCPUTimeRecord, 0, 2) + sqlDigest := GenSQLDigest(sql) + c.Lock() + for _, stmt := range c.sqlStatsMap { + if bytes.Equal(stmt.SQLDigest, sqlDigest.Bytes()) { + if planIsNotNull { + plan := c.planMap[string(stmt.PlanDigest)] + if len(plan) > 0 { + stats = append(stats, stmt) + } + } else { + stats = append(stats, stmt) + } + } + } + c.Unlock() + return stats +} + +// GetSQLCPUTimeBySQL uses for testing. +func (c *TopSQLCollector) GetSQLCPUTimeBySQL(sql string) uint32 { + sqlDigest := GenSQLDigest(sql) + cpuTime := uint32(0) + c.Lock() + for _, stmt := range c.sqlStatsMap { + if bytes.Equal(stmt.SQLDigest, sqlDigest.Bytes()) { + cpuTime += stmt.CPUTimeMs + } + } + c.Unlock() + return cpuTime +} + +// GetSQL uses for testing. +func (c *TopSQLCollector) GetSQL(sqlDigest []byte) string { + c.Lock() + sql := c.sqlMap[string(sqlDigest)] + c.Unlock() + return sql +} + +// GetPlan uses for testing. +func (c *TopSQLCollector) GetPlan(planDigest []byte) string { + c.Lock() + plan := c.planMap[string(planDigest)] + c.Unlock() + return plan +} + +// RegisterSQL uses for testing. +func (c *TopSQLCollector) RegisterSQL(sqlDigest []byte, normalizedSQL string, _ bool) { + digestStr := string(hack.String(sqlDigest)) + c.Lock() + _, ok := c.sqlMap[digestStr] + if !ok { + c.sqlMap[digestStr] = normalizedSQL + } + c.Unlock() +} + +// RegisterPlan uses for testing. +func (c *TopSQLCollector) RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) { + if isLarge { + return + } + + digestStr := string(hack.String(planDigest)) + c.Lock() + _, ok := c.planMap[digestStr] + if !ok { + c.planMap[digestStr] = normalizedPlan + } + c.Unlock() +} + +// WaitCollectCnt uses for testing. +func (c *TopSQLCollector) WaitCollectCnt(count int64) { + timeout := time.After(time.Second * 10) + end := c.collectCnt.Load() + count + for { + // Wait for reporter to collect sql stats count >= expected count + if c.collectCnt.Load() >= end { + return + } + select { + case <-timeout: + return + default: + time.Sleep(time.Millisecond * 10) + } + } +} + +// Reset cleans all collected data. +func (c *TopSQLCollector) Reset() { + c.Lock() + defer c.Unlock() + c.sqlMap = make(map[string]string) + c.planMap = make(map[string]string) + c.sqlStatsMap = make(map[string]*collector.SQLCPUTimeRecord) + c.collectCnt.Store(0) +} + +// CollectCnt uses for testing. +func (c *TopSQLCollector) CollectCnt() int64 { + return c.collectCnt.Load() +} + +// Close implements the interface. +func (*TopSQLCollector) Close() {} + +func (*TopSQLCollector) hash(stat collector.SQLCPUTimeRecord) string { + return string(stat.SQLDigest) + string(stat.PlanDigest) +} + +// GenSQLDigest uses for testing. +func GenSQLDigest(sql string) *parser.Digest { + _, digest := parser.NormalizeDigest(sql) + return digest +} diff --git a/pkg/util/topsql/main_test.go b/pkg/util/topsql/main_test.go new file mode 100644 index 0000000000000..34d5699b9bb9a --- /dev/null +++ b/pkg/util/topsql/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package topsql + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/topsql/reporter/BUILD.bazel b/pkg/util/topsql/reporter/BUILD.bazel new file mode 100644 index 0000000000000..3c131898f0b57 --- /dev/null +++ b/pkg/util/topsql/reporter/BUILD.bazel @@ -0,0 +1,61 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "reporter", + srcs = [ + "datamodel.go", + "datasink.go", + "pubsub.go", + "reporter.go", + "single_target.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/reporter", + visibility = ["//visibility:public"], + deps = [ + "//pkg/config", + "//pkg/util", + "//pkg/util/hack", + "//pkg/util/logutil", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/reporter/metrics", + "//pkg/util/topsql/state", + "//pkg/util/topsql/stmtstats", + "@com_github_pingcap_errors//:errors", + "@com_github_pingcap_failpoint//:failpoint", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_wangjohn_quickselect//:quickselect", + "@org_golang_google_grpc//:grpc", + "@org_golang_google_grpc//backoff", + "@org_golang_google_grpc//credentials/insecure", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_zap//:zap", + ], +) + +go_test( + name = "reporter_test", + timeout = "short", + srcs = [ + "datamodel_test.go", + "datasink_test.go", + "main_test.go", + "pubsub_test.go", + "reporter_test.go", + "single_target_test.go", + ], + embed = [":reporter"], + flaky = True, + deps = [ + "//pkg/config", + "//pkg/testkit/testsetup", + "//pkg/util/topsql/collector", + "//pkg/util/topsql/reporter/mock", + "//pkg/util/topsql/state", + "//pkg/util/topsql/stmtstats", + "@com_github_pingcap_tipb//go-tipb", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@org_golang_google_grpc//metadata", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/topsql/reporter/datamodel.go b/pkg/util/topsql/reporter/datamodel.go similarity index 98% rename from util/topsql/reporter/datamodel.go rename to pkg/util/topsql/reporter/datamodel.go index 29980c81c4451..125ff6eb0f14d 100644 --- a/util/topsql/reporter/datamodel.go +++ b/pkg/util/topsql/reporter/datamodel.go @@ -20,12 +20,12 @@ import ( "sync" "sync/atomic" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/topsql/collector" - reporter_metrics "github.com/pingcap/tidb/util/topsql/reporter/metrics" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/util/hack" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + reporter_metrics "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" "github.com/pingcap/tipb/go-tipb" "github.com/wangjohn/quickselect" atomic2 "go.uber.org/atomic" diff --git a/util/topsql/reporter/datamodel_test.go b/pkg/util/topsql/reporter/datamodel_test.go similarity index 99% rename from util/topsql/reporter/datamodel_test.go rename to pkg/util/topsql/reporter/datamodel_test.go index c8643b1b518bd..e20efd31ba411 100644 --- a/util/topsql/reporter/datamodel_test.go +++ b/pkg/util/topsql/reporter/datamodel_test.go @@ -19,8 +19,8 @@ import ( "sort" "testing" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" ) diff --git a/util/topsql/reporter/datasink.go b/pkg/util/topsql/reporter/datasink.go similarity index 98% rename from util/topsql/reporter/datasink.go rename to pkg/util/topsql/reporter/datasink.go index 8d1e597b21430..b4ae7aeeb6f0d 100644 --- a/util/topsql/reporter/datasink.go +++ b/pkg/util/topsql/reporter/datasink.go @@ -20,7 +20,7 @@ import ( "time" "github.com/pingcap/errors" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/pingcap/tipb/go-tipb" ) diff --git a/util/topsql/reporter/datasink_test.go b/pkg/util/topsql/reporter/datasink_test.go similarity index 100% rename from util/topsql/reporter/datasink_test.go rename to pkg/util/topsql/reporter/datasink_test.go diff --git a/pkg/util/topsql/reporter/main_test.go b/pkg/util/topsql/reporter/main_test.go new file mode 100644 index 0000000000000..bee96b1b93f66 --- /dev/null +++ b/pkg/util/topsql/reporter/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package reporter + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/pkg/util/topsql/reporter/metrics/BUILD.bazel b/pkg/util/topsql/reporter/metrics/BUILD.bazel new file mode 100644 index 0000000000000..f02735fd05be2 --- /dev/null +++ b/pkg/util/topsql/reporter/metrics/BUILD.bazel @@ -0,0 +1,12 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "metrics", + srcs = ["metrics.go"], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics", + visibility = ["//visibility:public"], + deps = [ + "//pkg/metrics", + "@com_github_prometheus_client_golang//prometheus", + ], +) diff --git a/pkg/util/topsql/reporter/metrics/metrics.go b/pkg/util/topsql/reporter/metrics/metrics.go new file mode 100644 index 0000000000000..99d12524e285b --- /dev/null +++ b/pkg/util/topsql/reporter/metrics/metrics.go @@ -0,0 +1,64 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package reporter + +import ( + "github.com/pingcap/tidb/pkg/metrics" + "github.com/prometheus/client_golang/prometheus" +) + +// reporter metrics vars +var ( + IgnoreExceedSQLCounter prometheus.Counter + IgnoreExceedPlanCounter prometheus.Counter + IgnoreCollectChannelFullCounter prometheus.Counter + IgnoreCollectStmtChannelFullCounter prometheus.Counter + IgnoreReportChannelFullCounter prometheus.Counter + ReportAllDurationSuccHistogram prometheus.Observer + ReportAllDurationFailedHistogram prometheus.Observer + ReportRecordDurationSuccHistogram prometheus.Observer + ReportRecordDurationFailedHistogram prometheus.Observer + ReportSQLDurationSuccHistogram prometheus.Observer + ReportSQLDurationFailedHistogram prometheus.Observer + ReportPlanDurationSuccHistogram prometheus.Observer + ReportPlanDurationFailedHistogram prometheus.Observer + TopSQLReportRecordCounterHistogram prometheus.Observer + TopSQLReportSQLCountHistogram prometheus.Observer + TopSQLReportPlanCountHistogram prometheus.Observer +) + +func init() { + InitMetricsVars() +} + +// InitMetricsVars init topsql reporter metrics vars. +func InitMetricsVars() { + IgnoreExceedSQLCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_exceed_sql") + IgnoreExceedPlanCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_exceed_plan") + IgnoreCollectChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_collect_channel_full") + IgnoreCollectStmtChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_collect_stmt_channel_full") + IgnoreReportChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_report_channel_full") + ReportAllDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("all", metrics.LblOK) + ReportAllDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("all", metrics.LblError) + ReportRecordDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("record", metrics.LblOK) + ReportRecordDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("record", metrics.LblError) + ReportSQLDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("sql", metrics.LblOK) + ReportSQLDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("sql", metrics.LblError) + ReportPlanDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("plan", metrics.LblOK) + ReportPlanDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("plan", metrics.LblError) + TopSQLReportRecordCounterHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("record") + TopSQLReportSQLCountHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("sql") + TopSQLReportPlanCountHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("plan") +} diff --git a/pkg/util/topsql/reporter/mock/BUILD.bazel b/pkg/util/topsql/reporter/mock/BUILD.bazel new file mode 100644 index 0000000000000..ff06204953519 --- /dev/null +++ b/pkg/util/topsql/reporter/mock/BUILD.bazel @@ -0,0 +1,17 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "mock", + srcs = [ + "pubsub.go", + "server.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/reporter/mock", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/logutil", + "@com_github_pingcap_tipb//go-tipb", + "@org_golang_google_grpc//:grpc", + "@org_uber_go_zap//:zap", + ], +) diff --git a/pkg/util/topsql/reporter/mock/pubsub.go b/pkg/util/topsql/reporter/mock/pubsub.go new file mode 100644 index 0000000000000..9ab71dc168ca2 --- /dev/null +++ b/pkg/util/topsql/reporter/mock/pubsub.go @@ -0,0 +1,67 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "fmt" + "net" + + "github.com/pingcap/tidb/pkg/util/logutil" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +type mockPubSubServer struct { + listen net.Listener + grpcServer *grpc.Server + addr string +} + +// NewMockPubSubServer creates a mock publisher server. +func NewMockPubSubServer() (*mockPubSubServer, error) { + addr := "127.0.0.1:0" + lis, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + server := grpc.NewServer() + + return &mockPubSubServer{ + addr: fmt.Sprintf("127.0.0.1:%d", lis.Addr().(*net.TCPAddr).Port), + listen: lis, + grpcServer: server, + }, nil +} + +func (svr *mockPubSubServer) Serve() { + err := svr.grpcServer.Serve(svr.listen) + if err != nil { + logutil.BgLogger().Warn("mock pubsub server serve failed", zap.String("category", "top-sql"), zap.Error(err)) + } +} + +func (svr *mockPubSubServer) Server() *grpc.Server { + return svr.grpcServer +} + +func (svr *mockPubSubServer) Address() string { + return svr.addr +} + +func (svr *mockPubSubServer) Stop() { + if svr.grpcServer != nil { + svr.grpcServer.Stop() + } +} diff --git a/pkg/util/topsql/reporter/mock/server.go b/pkg/util/topsql/reporter/mock/server.go new file mode 100644 index 0000000000000..864932a2a1f5d --- /dev/null +++ b/pkg/util/topsql/reporter/mock/server.go @@ -0,0 +1,241 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package mock + +import ( + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" + "google.golang.org/grpc" +) + +type mockAgentServer struct { + hang struct { + beginTime atomic.Pointer[time.Time] + endTime atomic.Pointer[time.Time] + } + grpcServer *grpc.Server + sqlMetas map[string]tipb.SQLMeta + planMetas map[string]string + addr string + records [][]*tipb.TopSQLRecord + sync.Mutex +} + +// StartMockAgentServer starts the mock agent server. +func StartMockAgentServer() (*mockAgentServer, error) { + addr := "127.0.0.1:0" + lis, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + server := grpc.NewServer() + agentServer := &mockAgentServer{ + addr: fmt.Sprintf("127.0.0.1:%d", lis.Addr().(*net.TCPAddr).Port), + grpcServer: server, + sqlMetas: make(map[string]tipb.SQLMeta, 5000), + planMetas: make(map[string]string, 5000), + } + beginTime := time.Now() + endTime := time.Now() + agentServer.hang.beginTime.Store(&beginTime) + agentServer.hang.endTime.Store(&endTime) + tipb.RegisterTopSQLAgentServer(server, agentServer) + + go func() { + err := server.Serve(lis) + if err != nil { + logutil.BgLogger().Warn("mock agent server serve failed", zap.String("category", "top-sql"), zap.Error(err)) + } + }() + + return agentServer, nil +} + +func (svr *mockAgentServer) HangFromNow(duration time.Duration) { + now := time.Now() + svr.hang.beginTime.Store(&now) + endTime := now.Add(duration) + svr.hang.endTime.Store(&endTime) +} + +// mayHang will check the hanging period, and ensure to sleep through it +func (svr *mockAgentServer) mayHang() { + now := time.Now() + beginTime := svr.hang.beginTime.Load() + endTime := svr.hang.endTime.Load() + if now.Before(*endTime) && now.After(*beginTime) { + time.Sleep(endTime.Sub(now)) + } +} + +func (svr *mockAgentServer) ReportTopSQLRecords(stream tipb.TopSQLAgent_ReportTopSQLRecordsServer) error { + records := make([]*tipb.TopSQLRecord, 0, 10) + for { + svr.mayHang() + req, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + return err + } + records = append(records, req) + } + svr.Lock() + svr.records = append(svr.records, records) + svr.Unlock() + return stream.SendAndClose(&tipb.EmptyResponse{}) +} + +func (svr *mockAgentServer) ReportSQLMeta(stream tipb.TopSQLAgent_ReportSQLMetaServer) error { + for { + svr.mayHang() + req, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + return err + } + svr.Lock() + svr.sqlMetas[string(req.SqlDigest)] = *req + svr.Unlock() + } + return stream.SendAndClose(&tipb.EmptyResponse{}) +} + +func (svr *mockAgentServer) ReportPlanMeta(stream tipb.TopSQLAgent_ReportPlanMetaServer) error { + for { + svr.mayHang() + req, err := stream.Recv() + if err == io.EOF { + break + } else if err != nil { + return err + } + svr.Lock() + svr.planMetas[string(req.PlanDigest)] = req.NormalizedPlan + svr.Unlock() + } + return stream.SendAndClose(&tipb.EmptyResponse{}) +} + +func (svr *mockAgentServer) RecordsCnt() int { + svr.Lock() + defer svr.Unlock() + return len(svr.records) +} + +func (svr *mockAgentServer) SQLMetaCnt() int { + svr.Lock() + defer svr.Unlock() + return len(svr.sqlMetas) +} + +func (svr *mockAgentServer) WaitCollectCnt(old, cnt int, timeout time.Duration) { + start := time.Now() + for { + svr.Lock() + if len(svr.records)-old >= cnt { + svr.Unlock() + return + } + svr.Unlock() + if time.Since(start) > timeout { + return + } + time.Sleep(time.Millisecond) + } +} + +func (svr *mockAgentServer) WaitCollectCntOfSQLMeta(old, cnt int, timeout time.Duration) { + start := time.Now() + for { + svr.Lock() + if len(svr.sqlMetas)-old >= cnt { + svr.Unlock() + return + } + svr.Unlock() + if time.Since(start) > timeout { + return + } + time.Sleep(time.Millisecond) + } +} + +func (svr *mockAgentServer) GetSQLMetaByDigestBlocking(digest []byte, timeout time.Duration) (meta tipb.SQLMeta, exist bool) { + start := time.Now() + for { + svr.Lock() + sqlMeta, exist := svr.sqlMetas[string(digest)] + svr.Unlock() + if exist || time.Since(start) > timeout { + return sqlMeta, exist + } + time.Sleep(time.Millisecond) + } +} + +func (svr *mockAgentServer) GetPlanMetaByDigestBlocking(digest []byte, timeout time.Duration) (normalizedPlan string, exist bool) { + start := time.Now() + for { + svr.Lock() + normalizedPlan, exist = svr.planMetas[string(digest)] + svr.Unlock() + if exist || time.Since(start) > timeout { + return normalizedPlan, exist + } + time.Sleep(time.Millisecond) + } +} + +func (svr *mockAgentServer) GetLatestRecords() []*tipb.TopSQLRecord { + svr.Lock() + records := svr.records + svr.records = [][]*tipb.TopSQLRecord{} + svr.Unlock() + + if len(records) == 0 { + return nil + } + return records[len(records)-1] +} + +func (svr *mockAgentServer) GetTotalSQLMetas() []tipb.SQLMeta { + svr.Lock() + defer svr.Unlock() + metas := make([]tipb.SQLMeta, 0, len(svr.sqlMetas)) + for _, meta := range svr.sqlMetas { + metas = append(metas, meta) + } + return metas +} + +func (svr *mockAgentServer) Address() string { + return svr.addr +} + +func (svr *mockAgentServer) Stop() { + if svr.grpcServer != nil { + svr.grpcServer.Stop() + } +} diff --git a/pkg/util/topsql/reporter/pubsub.go b/pkg/util/topsql/reporter/pubsub.go new file mode 100644 index 0000000000000..0210df87d9212 --- /dev/null +++ b/pkg/util/topsql/reporter/pubsub.go @@ -0,0 +1,268 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package reporter + +import ( + "context" + "errors" + "time" + + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + reporter_metrics "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics" + "github.com/pingcap/tipb/go-tipb" + "go.uber.org/zap" +) + +// TopSQLPubSubService implements tipb.TopSQLPubSubServer. +// +// If a client subscribes to TopSQL records, the TopSQLPubSubService is responsible +// for registering an associated DataSink to the reporter. Then the DataSink sends +// data to the client periodically. +type TopSQLPubSubService struct { + dataSinkRegisterer DataSinkRegisterer +} + +// NewTopSQLPubSubService creates a new TopSQLPubSubService. +func NewTopSQLPubSubService(dataSinkRegisterer DataSinkRegisterer) *TopSQLPubSubService { + return &TopSQLPubSubService{dataSinkRegisterer: dataSinkRegisterer} +} + +var _ tipb.TopSQLPubSubServer = &TopSQLPubSubService{} + +// Subscribe registers dataSinks to the reporter and redirects data received from reporter +// to subscribers associated with those dataSinks. +func (ps *TopSQLPubSubService) Subscribe(_ *tipb.TopSQLSubRequest, stream tipb.TopSQLPubSub_SubscribeServer) error { + ds := newPubSubDataSink(stream, ps.dataSinkRegisterer) + if err := ps.dataSinkRegisterer.Register(ds); err != nil { + return err + } + return ds.run() +} + +type pubSubDataSink struct { + ctx context.Context + cancel context.CancelFunc + + stream tipb.TopSQLPubSub_SubscribeServer + sendTaskCh chan sendTask + + // for deregister + registerer DataSinkRegisterer +} + +func newPubSubDataSink(stream tipb.TopSQLPubSub_SubscribeServer, registerer DataSinkRegisterer) *pubSubDataSink { + ctx, cancel := context.WithCancel(stream.Context()) + + return &pubSubDataSink{ + ctx: ctx, + cancel: cancel, + + stream: stream, + sendTaskCh: make(chan sendTask, 1), + + registerer: registerer, + } +} + +var _ DataSink = &pubSubDataSink{} + +func (ds *pubSubDataSink) TrySend(data *ReportData, deadline time.Time) error { + select { + case ds.sendTaskCh <- sendTask{data: data, deadline: deadline}: + return nil + case <-ds.ctx.Done(): + return ds.ctx.Err() + default: + reporter_metrics.IgnoreReportChannelFullCounter.Inc() + return errors.New("the channel of pubsub dataSink is full") + } +} + +func (ds *pubSubDataSink) OnReporterClosing() { + ds.cancel() +} + +func (ds *pubSubDataSink) run() error { + defer func() { + ds.registerer.Deregister(ds) + ds.cancel() + }() + + for { + select { + case task := <-ds.sendTaskCh: + ctx, cancel := context.WithDeadline(ds.ctx, task.deadline) + var err error + + start := time.Now() + go util.WithRecovery(func() { + defer cancel() + err = ds.doSend(ctx, task.data) + + if err != nil { + reporter_metrics.ReportAllDurationFailedHistogram.Observe(time.Since(start).Seconds()) + } else { + reporter_metrics.ReportAllDurationSuccHistogram.Observe(time.Since(start).Seconds()) + } + }, nil) + + // When the deadline is exceeded, the closure inside `go util.WithRecovery` above may not notice that + // immediately because it can be blocked by `stream.Send`. + // In order to clean up resources as quickly as possible, we let that closure run in an individual goroutine, + // and wait for timeout here. + <-ctx.Done() + + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + logutil.BgLogger().Warn( + "[top-sql] pubsub datasink failed to send data to subscriber due to deadline exceeded", + zap.Time("deadline", task.deadline), + ) + return ctx.Err() + } + + if err != nil { + logutil.BgLogger().Warn( + "[top-sql] pubsub datasink failed to send data to subscriber", + zap.Error(err), + ) + return err + } + case <-ds.ctx.Done(): + return ds.ctx.Err() + } + } +} + +func (ds *pubSubDataSink) doSend(ctx context.Context, data *ReportData) error { + if err := ds.sendTopSQLRecords(ctx, data.DataRecords); err != nil { + return err + } + if err := ds.sendSQLMeta(ctx, data.SQLMetas); err != nil { + return err + } + return ds.sendPlanMeta(ctx, data.PlanMetas) +} + +func (ds *pubSubDataSink) sendTopSQLRecords(ctx context.Context, records []tipb.TopSQLRecord) (err error) { + if len(records) == 0 { + return + } + + start := time.Now() + sentCount := 0 + defer func() { + reporter_metrics.TopSQLReportRecordCounterHistogram.Observe(float64(sentCount)) + if err != nil { + reporter_metrics.ReportRecordDurationFailedHistogram.Observe(time.Since(start).Seconds()) + } else { + reporter_metrics.ReportRecordDurationSuccHistogram.Observe(time.Since(start).Seconds()) + } + }() + + topSQLRecord := &tipb.TopSQLSubResponse_Record{} + r := &tipb.TopSQLSubResponse{RespOneof: topSQLRecord} + + for i := range records { + topSQLRecord.Record = &records[i] + if err = ds.stream.Send(r); err != nil { + return + } + sentCount++ + + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + } + } + + return +} + +func (ds *pubSubDataSink) sendSQLMeta(ctx context.Context, sqlMetas []tipb.SQLMeta) (err error) { + if len(sqlMetas) == 0 { + return + } + + start := time.Now() + sentCount := 0 + defer func() { + reporter_metrics.TopSQLReportSQLCountHistogram.Observe(float64(sentCount)) + if err != nil { + reporter_metrics.ReportSQLDurationFailedHistogram.Observe(time.Since(start).Seconds()) + } else { + reporter_metrics.ReportSQLDurationSuccHistogram.Observe(time.Since(start).Seconds()) + } + }() + + sqlMeta := &tipb.TopSQLSubResponse_SqlMeta{} + r := &tipb.TopSQLSubResponse{RespOneof: sqlMeta} + + for i := range sqlMetas { + sqlMeta.SqlMeta = &sqlMetas[i] + if err = ds.stream.Send(r); err != nil { + return + } + sentCount++ + + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + } + } + + return +} + +func (ds *pubSubDataSink) sendPlanMeta(ctx context.Context, planMetas []tipb.PlanMeta) (err error) { + if len(planMetas) == 0 { + return + } + + start := time.Now() + sentCount := 0 + defer func() { + reporter_metrics.TopSQLReportPlanCountHistogram.Observe(float64(sentCount)) + if err != nil { + reporter_metrics.ReportPlanDurationFailedHistogram.Observe(time.Since(start).Seconds()) + } else { + reporter_metrics.ReportPlanDurationSuccHistogram.Observe(time.Since(start).Seconds()) + } + }() + + planMeta := &tipb.TopSQLSubResponse_PlanMeta{} + r := &tipb.TopSQLSubResponse{RespOneof: planMeta} + + for i := range planMetas { + planMeta.PlanMeta = &planMetas[i] + if err = ds.stream.Send(r); err != nil { + return + } + sentCount++ + + select { + case <-ctx.Done(): + err = ctx.Err() + return + default: + } + } + + return +} diff --git a/util/topsql/reporter/pubsub_test.go b/pkg/util/topsql/reporter/pubsub_test.go similarity index 100% rename from util/topsql/reporter/pubsub_test.go rename to pkg/util/topsql/reporter/pubsub_test.go diff --git a/pkg/util/topsql/reporter/reporter.go b/pkg/util/topsql/reporter/reporter.go new file mode 100644 index 0000000000000..ddb2fd4c81c82 --- /dev/null +++ b/pkg/util/topsql/reporter/reporter.go @@ -0,0 +1,333 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package reporter + +import ( + "context" + "time" + + "github.com/pingcap/failpoint" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + reporter_metrics "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" + "go.uber.org/zap" +) + +const ( + reportTimeout = 40 * time.Second + collectChanBufferSize = 2 +) + +var nowFunc = time.Now + +// TopSQLReporter collects Top SQL metrics. +type TopSQLReporter interface { + collector.Collector + stmtstats.Collector + + // Start uses to start the reporter. + Start() + + // RegisterSQL registers a normalizedSQL with SQLDigest. + // + // Note that the normalized SQL string can be of >1M long. + // This function should be thread-safe, which means concurrently calling it + // in several goroutines should be fine. It should also return immediately, + // and do any CPU-intensive job asynchronously. + RegisterSQL(sqlDigest []byte, normalizedSQL string, isInternal bool) + + // RegisterPlan like RegisterSQL, but for normalized plan strings. + // isLarge indicates the size of normalizedPlan is big. + RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) + + // Close uses to close and release the reporter resource. + Close() +} + +var _ TopSQLReporter = &RemoteTopSQLReporter{} +var _ DataSinkRegisterer = &RemoteTopSQLReporter{} + +// RemoteTopSQLReporter implements TopSQLReporter that sends data to a remote agent. +// This should be called periodically to collect TopSQL resource usage metrics. +type RemoteTopSQLReporter struct { + ctx context.Context + reportCollectedDataChan chan collectedData + cancel context.CancelFunc + sqlCPUCollector *collector.SQLCPUCollector + collectCPUTimeChan chan []collector.SQLCPUTimeRecord + collectStmtStatsChan chan stmtstats.StatementStatsMap + collecting *collecting + normalizedSQLMap *normalizedSQLMap + normalizedPlanMap *normalizedPlanMap + stmtStatsBuffer map[uint64]stmtstats.StatementStatsMap // timestamp => stmtstats.StatementStatsMap + // calling decodePlan this can take a while, so should not block critical paths. + decodePlan planBinaryDecodeFunc + // Instead of dropping large plans, we compress it into encoded format and report + compressPlan planBinaryCompressFunc + DefaultDataSinkRegisterer +} + +// NewRemoteTopSQLReporter creates a new RemoteTopSQLReporter. +// +// decodePlan is a decoding function which will be called asynchronously to decode the plan binary to string. +func NewRemoteTopSQLReporter(decodePlan planBinaryDecodeFunc, compressPlan planBinaryCompressFunc) *RemoteTopSQLReporter { + ctx, cancel := context.WithCancel(context.Background()) + tsr := &RemoteTopSQLReporter{ + DefaultDataSinkRegisterer: NewDefaultDataSinkRegisterer(ctx), + ctx: ctx, + cancel: cancel, + collectCPUTimeChan: make(chan []collector.SQLCPUTimeRecord, collectChanBufferSize), + collectStmtStatsChan: make(chan stmtstats.StatementStatsMap, collectChanBufferSize), + reportCollectedDataChan: make(chan collectedData, 1), + collecting: newCollecting(), + normalizedSQLMap: newNormalizedSQLMap(), + normalizedPlanMap: newNormalizedPlanMap(), + stmtStatsBuffer: map[uint64]stmtstats.StatementStatsMap{}, + decodePlan: decodePlan, + compressPlan: compressPlan, + } + tsr.sqlCPUCollector = collector.NewSQLCPUCollector(tsr) + return tsr +} + +// Start implements the TopSQLReporter interface. +func (tsr *RemoteTopSQLReporter) Start() { + tsr.sqlCPUCollector.Start() + go tsr.collectWorker() + go tsr.reportWorker() +} + +// Collect implements tracecpu.Collector. +// +// WARN: It will drop the DataRecords if the processing is not in time. +// This function is thread-safe and efficient. +func (tsr *RemoteTopSQLReporter) Collect(data []collector.SQLCPUTimeRecord) { + if len(data) == 0 { + return + } + select { + case tsr.collectCPUTimeChan <- data: + default: + // ignore if chan blocked + reporter_metrics.IgnoreCollectChannelFullCounter.Inc() + } +} + +// CollectStmtStatsMap implements stmtstats.Collector. +// +// WARN: It will drop the DataRecords if the processing is not in time. +// This function is thread-safe and efficient. +func (tsr *RemoteTopSQLReporter) CollectStmtStatsMap(data stmtstats.StatementStatsMap) { + if len(data) == 0 { + return + } + select { + case tsr.collectStmtStatsChan <- data: + default: + // ignore if chan blocked + reporter_metrics.IgnoreCollectStmtChannelFullCounter.Inc() + } +} + +// RegisterSQL implements TopSQLReporter. +// +// This function is thread-safe and efficient. +func (tsr *RemoteTopSQLReporter) RegisterSQL(sqlDigest []byte, normalizedSQL string, isInternal bool) { + tsr.normalizedSQLMap.register(sqlDigest, normalizedSQL, isInternal) +} + +// RegisterPlan implements TopSQLReporter. +// +// This function is thread-safe and efficient. +func (tsr *RemoteTopSQLReporter) RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) { + tsr.normalizedPlanMap.register(planDigest, normalizedPlan, isLarge) +} + +// Close implements TopSQLReporter. +func (tsr *RemoteTopSQLReporter) Close() { + tsr.cancel() + tsr.sqlCPUCollector.Stop() + tsr.onReporterClosing() +} + +// collectWorker consumes and collects data from tracecpu.Collector/stmtstats.Collector. +func (tsr *RemoteTopSQLReporter) collectWorker() { + defer util.Recover("top-sql", "collectWorker", nil, false) + + currentReportInterval := topsqlstate.GlobalState.ReportIntervalSeconds.Load() + reportTicker := time.NewTicker(time.Second * time.Duration(currentReportInterval)) + defer reportTicker.Stop() + for { + select { + case <-tsr.ctx.Done(): + return + case data := <-tsr.collectCPUTimeChan: + timestamp := uint64(nowFunc().Unix()) + tsr.processCPUTimeData(timestamp, data) + case data := <-tsr.collectStmtStatsChan: + timestamp := uint64(nowFunc().Unix()) + tsr.stmtStatsBuffer[timestamp] = data + case <-reportTicker.C: + tsr.processStmtStatsData() + tsr.takeDataAndSendToReportChan() + // Update `reportTicker` if report interval changed. + if newInterval := topsqlstate.GlobalState.ReportIntervalSeconds.Load(); newInterval != currentReportInterval { + currentReportInterval = newInterval + reportTicker.Reset(time.Second * time.Duration(currentReportInterval)) + } + } + } +} + +// processCPUTimeData collects top N cpuRecords of each round into tsr.collecting, and evict the +// data that is not in top N. All the evicted cpuRecords will be summary into the others. +func (tsr *RemoteTopSQLReporter) processCPUTimeData(timestamp uint64, data cpuRecords) { + defer util.Recover("top-sql", "processCPUTimeData", nil, false) + + // Get top N cpuRecords of each round cpuRecords. Collect the top N to tsr.collecting + // for each round. SQL meta will not be evicted, since the evicted SQL can be appeared + // on other components (TiKV) TopN DataRecords. + top, evicted := data.topN(int(topsqlstate.GlobalState.MaxStatementCount.Load())) + for _, r := range top { + tsr.collecting.getOrCreateRecord(r.SQLDigest, r.PlanDigest).appendCPUTime(timestamp, r.CPUTimeMs) + } + if len(evicted) == 0 { + return + } + totalEvictedCPUTime := uint32(0) + for _, e := range evicted { + totalEvictedCPUTime += e.CPUTimeMs + // Mark which digests are evicted under each timestamp. + // We will determine whether the corresponding CPUTime has been evicted + // when collecting stmtstats. If so, then we can ignore it directly. + tsr.collecting.markAsEvicted(timestamp, e.SQLDigest, e.PlanDigest) + } + tsr.collecting.appendOthersCPUTime(timestamp, totalEvictedCPUTime) +} + +// processStmtStatsData collects tsr.stmtStatsBuffer into tsr.collecting. +// All the evicted items will be summary into the others. +func (tsr *RemoteTopSQLReporter) processStmtStatsData() { + defer util.Recover("top-sql", "processStmtStatsData", nil, false) + + for timestamp, data := range tsr.stmtStatsBuffer { + for digest, item := range data { + sqlDigest, planDigest := []byte(digest.SQLDigest), []byte(digest.PlanDigest) + if tsr.collecting.hasEvicted(timestamp, sqlDigest, planDigest) { + // This timestamp+sql+plan has been evicted due to low CPUTime. + tsr.collecting.appendOthersStmtStatsItem(timestamp, *item) + continue + } + tsr.collecting.getOrCreateRecord(sqlDigest, planDigest).appendStmtStatsItem(timestamp, *item) + } + } + tsr.stmtStatsBuffer = map[uint64]stmtstats.StatementStatsMap{} +} + +// takeDataAndSendToReportChan takes records data and then send to the report channel for reporting. +func (tsr *RemoteTopSQLReporter) takeDataAndSendToReportChan() { + // Send to report channel. When channel is full, data will be dropped. + select { + case tsr.reportCollectedDataChan <- collectedData{ + collected: tsr.collecting.take(), + normalizedSQLMap: tsr.normalizedSQLMap.take(), + normalizedPlanMap: tsr.normalizedPlanMap.take(), + }: + default: + // ignore if chan blocked + reporter_metrics.IgnoreReportChannelFullCounter.Inc() + } +} + +// reportWorker sends data to the gRPC endpoint from the `reportCollectedDataChan` one by one. +func (tsr *RemoteTopSQLReporter) reportWorker() { + defer util.Recover("top-sql", "reportWorker", nil, false) + + for { + select { + case data := <-tsr.reportCollectedDataChan: + // When `reportCollectedDataChan` receives something, there could be ongoing + // `RegisterSQL` and `RegisterPlan` running, who writes to the data structure + // that `data` contains. So we wait for a little while to ensure that writes + // are finished. + time.Sleep(time.Millisecond * 100) + rs := data.collected.getReportRecords() + // Convert to protobuf data and do report. + tsr.doReport(&ReportData{ + DataRecords: rs.toProto(), + SQLMetas: data.normalizedSQLMap.toProto(), + PlanMetas: data.normalizedPlanMap.toProto(tsr.decodePlan, tsr.compressPlan), + }) + case <-tsr.ctx.Done(): + return + } + } +} + +// doReport sends ReportData to DataSinks. +func (tsr *RemoteTopSQLReporter) doReport(data *ReportData) { + defer util.Recover("top-sql", "doReport", nil, false) + + if !data.hasData() { + return + } + timeout := reportTimeout + failpoint.Inject("resetTimeoutForTest", func(val failpoint.Value) { + if val.(bool) { + interval := time.Duration(topsqlstate.GlobalState.ReportIntervalSeconds.Load()) * time.Second + if interval < timeout { + timeout = interval + } + } + }) + _ = tsr.trySend(data, time.Now().Add(timeout)) +} + +// trySend sends ReportData to all internal registered DataSinks. +func (tsr *RemoteTopSQLReporter) trySend(data *ReportData, deadline time.Time) error { + tsr.DefaultDataSinkRegisterer.Lock() + dataSinks := make([]DataSink, 0, len(tsr.dataSinks)) + for ds := range tsr.dataSinks { + dataSinks = append(dataSinks, ds) + } + tsr.DefaultDataSinkRegisterer.Unlock() + for _, ds := range dataSinks { + if err := ds.TrySend(data, deadline); err != nil { + logutil.BgLogger().Warn("failed to send data to datasink", zap.String("category", "top-sql"), zap.Error(err)) + } + } + return nil +} + +// onReporterClosing calls the OnReporterClosing method of all internally registered DataSinks. +func (tsr *RemoteTopSQLReporter) onReporterClosing() { + var m map[DataSink]struct{} + tsr.DefaultDataSinkRegisterer.Lock() + m, tsr.dataSinks = tsr.dataSinks, make(map[DataSink]struct{}) + tsr.DefaultDataSinkRegisterer.Unlock() + for d := range m { + d.OnReporterClosing() + } +} + +// collectedData is used for transmission in the channel. +type collectedData struct { + collected *collecting + normalizedSQLMap *normalizedSQLMap + normalizedPlanMap *normalizedPlanMap +} diff --git a/util/topsql/reporter/reporter_test.go b/pkg/util/topsql/reporter/reporter_test.go similarity index 98% rename from util/topsql/reporter/reporter_test.go rename to pkg/util/topsql/reporter/reporter_test.go index c57e4ba3ff906..19cd6b3fa91ee 100644 --- a/util/topsql/reporter/reporter_test.go +++ b/pkg/util/topsql/reporter/reporter_test.go @@ -21,9 +21,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/util/topsql/collector" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/util/topsql/reporter/single_target.go b/pkg/util/topsql/reporter/single_target.go similarity index 98% rename from util/topsql/reporter/single_target.go rename to pkg/util/topsql/reporter/single_target.go index 0c0d425adab9b..f3738f7868f6a 100644 --- a/util/topsql/reporter/single_target.go +++ b/pkg/util/topsql/reporter/single_target.go @@ -21,9 +21,9 @@ import ( "sync" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/util/logutil" - reporter_metrics "github.com/pingcap/tidb/util/topsql/reporter/metrics" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util/logutil" + reporter_metrics "github.com/pingcap/tidb/pkg/util/topsql/reporter/metrics" "github.com/pingcap/tipb/go-tipb" "go.uber.org/atomic" "go.uber.org/zap" diff --git a/util/topsql/reporter/single_target_test.go b/pkg/util/topsql/reporter/single_target_test.go similarity index 96% rename from util/topsql/reporter/single_target_test.go rename to pkg/util/topsql/reporter/single_target_test.go index f9f2010f6084e..1733ffed90420 100644 --- a/util/topsql/reporter/single_target_test.go +++ b/pkg/util/topsql/reporter/single_target_test.go @@ -18,8 +18,8 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/util/topsql/reporter/mock" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/util/topsql/reporter/mock" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/assert" ) diff --git a/pkg/util/topsql/state/BUILD.bazel b/pkg/util/topsql/state/BUILD.bazel new file mode 100644 index 0000000000000..81b1f64153380 --- /dev/null +++ b/pkg/util/topsql/state/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "state", + srcs = ["state.go"], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/state", + visibility = ["//visibility:public"], + deps = ["@org_uber_go_atomic//:atomic"], +) diff --git a/util/topsql/state/state.go b/pkg/util/topsql/state/state.go similarity index 100% rename from util/topsql/state/state.go rename to pkg/util/topsql/state/state.go diff --git a/pkg/util/topsql/stmtstats/BUILD.bazel b/pkg/util/topsql/stmtstats/BUILD.bazel new file mode 100644 index 0000000000000..2d88d2d937d9f --- /dev/null +++ b/pkg/util/topsql/stmtstats/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "stmtstats", + srcs = [ + "aggregator.go", + "kv_exec_count.go", + "stmtstats.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/topsql/stmtstats", + visibility = ["//visibility:public"], + deps = [ + "//pkg/util/topsql/state", + "@com_github_tikv_client_go_v2//tikvrpc", + "@com_github_tikv_client_go_v2//tikvrpc/interceptor", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "stmtstats_test", + timeout = "short", + srcs = [ + "aggregator_test.go", + "kv_exec_count_test.go", + "main_test.go", + "stmtstats_test.go", + ], + embed = [":stmtstats"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "//pkg/util/topsql/state", + "@com_github_stretchr_testify//assert", + "@com_github_stretchr_testify//require", + "@com_github_tikv_client_go_v2//tikvrpc", + "@org_uber_go_atomic//:atomic", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/util/topsql/stmtstats/aggregator.go b/pkg/util/topsql/stmtstats/aggregator.go similarity index 98% rename from util/topsql/stmtstats/aggregator.go rename to pkg/util/topsql/stmtstats/aggregator.go index 3ae57fc9f6a4d..f133fb5e65b91 100644 --- a/util/topsql/stmtstats/aggregator.go +++ b/pkg/util/topsql/stmtstats/aggregator.go @@ -19,7 +19,7 @@ import ( "sync" "time" - "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/state" "go.uber.org/atomic" ) diff --git a/util/topsql/stmtstats/aggregator_test.go b/pkg/util/topsql/stmtstats/aggregator_test.go similarity index 98% rename from util/topsql/stmtstats/aggregator_test.go rename to pkg/util/topsql/stmtstats/aggregator_test.go index 396f39022b778..c0f236b7c00b5 100644 --- a/util/topsql/stmtstats/aggregator_test.go +++ b/pkg/util/topsql/stmtstats/aggregator_test.go @@ -19,7 +19,7 @@ import ( "testing" "time" - "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/atomic" diff --git a/util/topsql/stmtstats/kv_exec_count.go b/pkg/util/topsql/stmtstats/kv_exec_count.go similarity index 97% rename from util/topsql/stmtstats/kv_exec_count.go rename to pkg/util/topsql/stmtstats/kv_exec_count.go index 8f8ec73ea04af..9c52f2bef7255 100644 --- a/util/topsql/stmtstats/kv_exec_count.go +++ b/pkg/util/topsql/stmtstats/kv_exec_count.go @@ -17,7 +17,7 @@ package stmtstats import ( "sync" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/tikv/client-go/v2/tikvrpc" "github.com/tikv/client-go/v2/tikvrpc/interceptor" ) diff --git a/util/topsql/stmtstats/kv_exec_count_test.go b/pkg/util/topsql/stmtstats/kv_exec_count_test.go similarity index 96% rename from util/topsql/stmtstats/kv_exec_count_test.go rename to pkg/util/topsql/stmtstats/kv_exec_count_test.go index caea903e29d98..1d42ee74093ed 100644 --- a/util/topsql/stmtstats/kv_exec_count_test.go +++ b/pkg/util/topsql/stmtstats/kv_exec_count_test.go @@ -17,7 +17,7 @@ package stmtstats import ( "testing" - "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/stretchr/testify/assert" "github.com/tikv/client-go/v2/tikvrpc" ) diff --git a/pkg/util/topsql/stmtstats/main_test.go b/pkg/util/topsql/stmtstats/main_test.go new file mode 100644 index 0000000000000..5c1c4384c3508 --- /dev/null +++ b/pkg/util/topsql/stmtstats/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package stmtstats + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/topsql/stmtstats/stmtstats.go b/pkg/util/topsql/stmtstats/stmtstats.go similarity index 100% rename from util/topsql/stmtstats/stmtstats.go rename to pkg/util/topsql/stmtstats/stmtstats.go diff --git a/util/topsql/stmtstats/stmtstats_test.go b/pkg/util/topsql/stmtstats/stmtstats_test.go similarity index 100% rename from util/topsql/stmtstats/stmtstats_test.go rename to pkg/util/topsql/stmtstats/stmtstats_test.go diff --git a/util/topsql/topsql.go b/pkg/util/topsql/topsql.go similarity index 95% rename from util/topsql/topsql.go rename to pkg/util/topsql/topsql.go index 97e10dd58e498..df9eb118f399a 100644 --- a/util/topsql/topsql.go +++ b/pkg/util/topsql/topsql.go @@ -21,12 +21,12 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/topsql/collector" - "github.com/pingcap/tidb/util/topsql/reporter" - "github.com/pingcap/tidb/util/topsql/stmtstats" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util/logutil" + "github.com/pingcap/tidb/pkg/util/plancodec" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + "github.com/pingcap/tidb/pkg/util/topsql/reporter" + "github.com/pingcap/tidb/pkg/util/topsql/stmtstats" "github.com/pingcap/tipb/go-tipb" "go.uber.org/zap" "google.golang.org/grpc" diff --git a/util/topsql/topsql_test.go b/pkg/util/topsql/topsql_test.go similarity index 95% rename from util/topsql/topsql_test.go rename to pkg/util/topsql/topsql_test.go index 1862b090a94de..82588b31268cf 100644 --- a/util/topsql/topsql_test.go +++ b/pkg/util/topsql/topsql_test.go @@ -19,16 +19,16 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/topsql" - "github.com/pingcap/tidb/util/topsql/collector" - "github.com/pingcap/tidb/util/topsql/collector/mock" - "github.com/pingcap/tidb/util/topsql/reporter" - mockServer "github.com/pingcap/tidb/util/topsql/reporter/mock" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/util" + "github.com/pingcap/tidb/pkg/util/cpuprofile" + "github.com/pingcap/tidb/pkg/util/topsql" + "github.com/pingcap/tidb/pkg/util/topsql/collector" + "github.com/pingcap/tidb/pkg/util/topsql/collector/mock" + "github.com/pingcap/tidb/pkg/util/topsql/reporter" + mockServer "github.com/pingcap/tidb/pkg/util/topsql/reporter/mock" + topsqlstate "github.com/pingcap/tidb/pkg/util/topsql/state" "github.com/pingcap/tipb/go-tipb" "github.com/stretchr/testify/require" "google.golang.org/grpc" diff --git a/pkg/util/tracing/BUILD.bazel b/pkg/util/tracing/BUILD.bazel new file mode 100644 index 0000000000000..88842d5e47532 --- /dev/null +++ b/pkg/util/tracing/BUILD.bazel @@ -0,0 +1,37 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "tracing", + srcs = [ + "opt_trace.go", + "util.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/tracing", + visibility = ["//visibility:public"], + deps = [ + "//pkg/parser/model", + "@com_github_opentracing_basictracer_go//:basictracer-go", + "@com_github_opentracing_opentracing_go//:opentracing-go", + ], +) + +go_test( + name = "tracing_test", + timeout = "short", + srcs = [ + "main_test.go", + "noop_bench_test.go", + "opt_trace_test.go", + "util_test.go", + ], + embed = [":tracing"], + flaky = True, + deps = [ + "//pkg/parser/model", + "//pkg/testkit/testsetup", + "@com_github_opentracing_basictracer_go//:basictracer-go", + "@com_github_opentracing_opentracing_go//:opentracing-go", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/tracing/main_test.go b/pkg/util/tracing/main_test.go new file mode 100644 index 0000000000000..0a11113f6469a --- /dev/null +++ b/pkg/util/tracing/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package tracing + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/tracing/noop_bench_test.go b/pkg/util/tracing/noop_bench_test.go similarity index 100% rename from util/tracing/noop_bench_test.go rename to pkg/util/tracing/noop_bench_test.go diff --git a/util/tracing/opt_trace.go b/pkg/util/tracing/opt_trace.go similarity index 100% rename from util/tracing/opt_trace.go rename to pkg/util/tracing/opt_trace.go diff --git a/util/tracing/opt_trace_test.go b/pkg/util/tracing/opt_trace_test.go similarity index 100% rename from util/tracing/opt_trace_test.go rename to pkg/util/tracing/opt_trace_test.go diff --git a/pkg/util/tracing/util.go b/pkg/util/tracing/util.go new file mode 100644 index 0000000000000..4c45b61b322f4 --- /dev/null +++ b/pkg/util/tracing/util.go @@ -0,0 +1,134 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package tracing + +import ( + "context" + "runtime/trace" + + "github.com/opentracing/basictracer-go" + "github.com/opentracing/opentracing-go" + "github.com/pingcap/tidb/pkg/parser/model" +) + +// TiDBTrace is set as Baggage on traces which are used for tidb tracing. +const TiDBTrace = "tr" + +type sqlTracingCtxKeyType struct{} + +var sqlTracingCtxKey = sqlTracingCtxKeyType{} + +// A CallbackRecorder immediately invokes itself on received trace spans. +type CallbackRecorder func(sp basictracer.RawSpan) + +// RecordSpan implements basictracer.SpanRecorder. +func (cr CallbackRecorder) RecordSpan(sp basictracer.RawSpan) { + cr(sp) +} + +// NewRecordedTrace returns a Span which records directly via the specified +// callback. +func NewRecordedTrace(opName string, callback func(sp basictracer.RawSpan)) opentracing.Span { + tr := basictracer.New(CallbackRecorder(callback)) + opentracing.SetGlobalTracer(tr) + sp := tr.StartSpan(opName) + sp.SetBaggageItem(TiDBTrace, "1") + return sp +} + +// noopSpan returns a Span which discards all operations. +func noopSpan() opentracing.Span { + return (opentracing.NoopTracer{}).StartSpan("DefaultSpan") +} + +// SpanFromContext returns the span obtained from the context or, if none is found, a new one started through tracer. +func SpanFromContext(ctx context.Context) (sp opentracing.Span) { + if sp = opentracing.SpanFromContext(ctx); sp == nil { + return noopSpan() + } + return sp +} + +// ChildSpanFromContxt return a non-nil span. If span can be got from ctx, then returned span is +// a child of such span. Otherwise, returned span is a noop span. +func ChildSpanFromContxt(ctx context.Context, opName string) (opentracing.Span, context.Context) { + if sp := opentracing.SpanFromContext(ctx); sp != nil { + if _, ok := sp.Tracer().(opentracing.NoopTracer); !ok { + child := opentracing.StartSpan(opName, opentracing.ChildOf(sp.Context())) + return child, opentracing.ContextWithSpan(ctx, child) + } + } + return noopSpan(), ctx +} + +// StartRegion provides better API, integrating both opentracing and runtime.trace facilities into one. +// Recommended usage is +// +// defer tracing.StartRegion(ctx, "myTracedRegion").End() +func StartRegion(ctx context.Context, regionType string) Region { + r := trace.StartRegion(ctx, regionType) + var span1 opentracing.Span + if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { + span1 = span.Tracer().StartSpan(regionType, opentracing.ChildOf(span.Context())) + } + return Region{ + Region: r, + Span: span1, + } +} + +// StartRegionEx returns Region together with the context. +// Recommended usage is +// +// r, ctx := tracing.StartRegionEx(ctx, "myTracedRegion") +// defer r.End() +func StartRegionEx(ctx context.Context, regionType string) (Region, context.Context) { + r := StartRegion(ctx, regionType) + if r.Span != nil { + ctx = opentracing.ContextWithSpan(ctx, r.Span) + } + return r, ctx +} + +// Region is a region of code whose execution time interval is traced. +type Region struct { + *trace.Region + opentracing.Span +} + +// End marks the end of the traced code region. +func (r Region) End() { + if r.Span != nil { + r.Span.Finish() + } + r.Region.End() +} + +// TraceInfoFromContext returns the `model.TraceInfo` in context +func TraceInfoFromContext(ctx context.Context) *model.TraceInfo { + val := ctx.Value(sqlTracingCtxKey) + if info, ok := val.(*model.TraceInfo); ok { + return info + } + return nil +} + +// ContextWithTraceInfo creates a new `model.TraceInfo` for context +func ContextWithTraceInfo(ctx context.Context, info *model.TraceInfo) context.Context { + if info == nil { + return ctx + } + return context.WithValue(ctx, sqlTracingCtxKey, info) +} diff --git a/pkg/util/tracing/util_test.go b/pkg/util/tracing/util_test.go new file mode 100644 index 0000000000000..a7f825de569ea --- /dev/null +++ b/pkg/util/tracing/util_test.go @@ -0,0 +1,152 @@ +// Copyright 2018 PingCAP, Inc. +// +// 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. + +package tracing_test + +import ( + "context" + "testing" + + "github.com/opentracing/basictracer-go" + "github.com/opentracing/opentracing-go" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/util/tracing" + "github.com/stretchr/testify/require" +) + +func TestSpanFromContext(t *testing.T) { + ctx := context.TODO() + noopSp := tracing.SpanFromContext(ctx) + // test noop span + _, ok := noopSp.Tracer().(opentracing.NoopTracer) + require.True(t, ok) + + // test tidb tracing + collectedSpan := make([]basictracer.RawSpan, 1) + sp := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { + collectedSpan = append(collectedSpan, sp) + }) + sp.Finish() + opentracing.ContextWithSpan(ctx, sp) + child := tracing.SpanFromContext(ctx) + child.Finish() + + // verify second span's operation is not nil, this way we can ensure + // callback logic works. + require.NotNil(t, collectedSpan[0].Operation) +} + +func TestChildSpanFromContext(t *testing.T) { + ctx := context.TODO() + noopSp, _ := tracing.ChildSpanFromContxt(ctx, "") + _, ok := noopSp.Tracer().(opentracing.NoopTracer) + require.True(t, ok) + + // test tidb tracing + collectedSpan := make([]basictracer.RawSpan, 1) + sp := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { + collectedSpan = append(collectedSpan, sp) + }) + sp.Finish() + opentracing.ContextWithSpan(ctx, sp) + child, _ := tracing.ChildSpanFromContxt(ctx, "test_child") + child.Finish() + + // verify second span's operation is not nil, this way we can ensure + // callback logic works. + require.NotNil(t, collectedSpan[1].Operation) +} + +func TestFollowFrom(t *testing.T) { + var collectedSpans []basictracer.RawSpan + // first start a root span + sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { + collectedSpans = append(collectedSpans, sp) + }) + sp2 := sp1.Tracer().StartSpan("follow_from", opentracing.FollowsFrom(sp1.Context())) + sp1.Finish() + sp2.Finish() + require.Equal(t, "follow_from", collectedSpans[1].Operation) + require.NotEqual(t, uint64(0), collectedSpans[1].ParentSpanID) + // only root span has 0 parent id + require.Equal(t, uint64(0), collectedSpans[0].ParentSpanID) +} + +func TestCreateSapnBeforeSetupGlobalTracer(t *testing.T) { + var collectedSpans []basictracer.RawSpan + sp := opentracing.StartSpan("before") + sp.Finish() + + // first start a root span + sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { + collectedSpans = append(collectedSpans, sp) + }) + sp1.Finish() + + // sp is a span started before we setup global tracer; hence such span will be + // droped. + require.Len(t, collectedSpans, 1) +} + +func TestTreeRelationship(t *testing.T) { + var collectedSpans []basictracer.RawSpan + ctx := context.TODO() + // first start a root span + sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { + collectedSpans = append(collectedSpans, sp) + }) + + // then store such root span into context + ctx = opentracing.ContextWithSpan(ctx, sp1) + + // create children span from context + sp2, ctx := tracing.ChildSpanFromContxt(ctx, "parent") + sp3, _ := tracing.ChildSpanFromContxt(ctx, "child") + + // notify span that we are about to reach end of journey. + sp1.Finish() + sp2.Finish() + sp3.Finish() + + // ensure length of collectedSpans is greather than 0 + require.Greater(t, len(collectedSpans), 0) + if len(collectedSpans) > 0 { + require.Equal(t, "test", collectedSpans[0].Operation) + require.Equal(t, "parent", collectedSpans[1].Operation) + require.Equal(t, "child", collectedSpans[2].Operation) + // check tree relationship + // sp1 is parent of sp2. And sp2 is parent of sp3. + require.Equal(t, collectedSpans[0].Context.SpanID, collectedSpans[1].ParentSpanID) + require.Equal(t, collectedSpans[1].Context.SpanID, collectedSpans[2].ParentSpanID) + } +} + +func TestTraceInfoFromContext(t *testing.T) { + ctx := context.Background() + // get info from a non-tracing context + require.Nil(t, tracing.TraceInfoFromContext(ctx)) + // ContextWithTraceInfo with a nil info will return the original context + require.Equal(t, ctx, tracing.ContextWithTraceInfo(ctx, nil)) + // create a context with trace info + ctx, cancel := context.WithCancel(context.WithValue(ctx, "val1", "a")) + ctx = tracing.ContextWithTraceInfo(ctx, &model.TraceInfo{ConnectionID: 12345, SessionAlias: "alias1"}) + // new context should have the same value as the original one + info := tracing.TraceInfoFromContext(ctx) + require.Equal(t, uint64(12345), info.ConnectionID) + require.Equal(t, "alias1", info.SessionAlias) + require.Equal(t, "a", ctx.Value("val1")) + require.NoError(t, ctx.Err()) + cancel() + require.Error(t, ctx.Err()) +} diff --git a/pkg/util/trxevents/BUILD.bazel b/pkg/util/trxevents/BUILD.bazel new file mode 100644 index 0000000000000..7351c23ab0d2b --- /dev/null +++ b/pkg/util/trxevents/BUILD.bazel @@ -0,0 +1,9 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "trxevents", + srcs = ["trx_events.go"], + importpath = "github.com/pingcap/tidb/pkg/util/trxevents", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_kvproto//pkg/kvrpcpb"], +) diff --git a/util/trxevents/trx_events.go b/pkg/util/trxevents/trx_events.go similarity index 100% rename from util/trxevents/trx_events.go rename to pkg/util/trxevents/trx_events.go diff --git a/util/urls.go b/pkg/util/urls.go similarity index 100% rename from util/urls.go rename to pkg/util/urls.go diff --git a/util/urls_test.go b/pkg/util/urls_test.go similarity index 100% rename from util/urls_test.go rename to pkg/util/urls_test.go diff --git a/pkg/util/util.go b/pkg/util/util.go new file mode 100644 index 0000000000000..eb2f9d5e584dd --- /dev/null +++ b/pkg/util/util.go @@ -0,0 +1,298 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package util + +import ( + "bufio" + "bytes" + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "strconv" + "strings" + "time" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb/pkg/parser" + "go.uber.org/atomic" + "go.uber.org/zap" +) + +// SliceToMap converts slice to map +// nolint:unused +func SliceToMap(slice []string) map[string]interface{} { + sMap := make(map[string]interface{}) + for _, str := range slice { + sMap[str] = struct{}{} + } + return sMap +} + +// StringsToInterfaces converts string slice to interface slice +func StringsToInterfaces(strs []string) []interface{} { + is := make([]interface{}, 0, len(strs)) + for _, str := range strs { + is = append(is, str) + } + + return is +} + +// GetJSON fetches a page and parses it as JSON. The parsed result will be +// stored into the `v`. The variable `v` must be a pointer to a type that can be +// unmarshalled from JSON. +// +// Example: +// +// client := &http.Client{} +// var resp struct { IP string } +// if err := util.GetJSON(client, "http://api.ipify.org/?format=json", &resp); err != nil { +// return errors.Trace(err) +// } +// fmt.Println(resp.IP) +// +// nolint:unused +func GetJSON(client *http.Client, url string, v interface{}) error { + resp, err := client.Get(url) + if err != nil { + return errors.Trace(err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, err := io.ReadAll(resp.Body) + if err != nil { + return errors.Trace(err) + } + return errors.Errorf("get %s http status code != 200, message %s", url, string(body)) + } + + return errors.Trace(json.NewDecoder(resp.Body).Decode(v)) +} + +// ChanMap creates a channel which applies the function over the input Channel. +// Hint of Resource Leakage: +// In golang, channel isn't an interface so we must create a goroutine for handling the inputs. +// Hence the input channel must be closed properly or this function may leak a goroutine. +func ChanMap[T, R any](c <-chan T, f func(T) R) <-chan R { + outCh := make(chan R) + go func() { + defer close(outCh) + for item := range c { + outCh <- f(item) + } + }() + return outCh +} + +// Str2Int64Map converts a string to a map[int64]struct{}. +func Str2Int64Map(str string) map[int64]struct{} { + strs := strings.Split(str, ",") + res := make(map[int64]struct{}, len(strs)) + for _, s := range strs { + id, _ := strconv.ParseInt(s, 10, 64) + res[id] = struct{}{} + } + return res +} + +// GenLogFields generate log fields. +func GenLogFields(costTime time.Duration, info *ProcessInfo, needTruncateSQL bool) []zap.Field { + if info.RefCountOfStmtCtx != nil && !info.RefCountOfStmtCtx.TryIncrease() { + return nil + } + defer info.RefCountOfStmtCtx.Decrease() + + logFields := make([]zap.Field, 0, 20) + logFields = append(logFields, zap.String("cost_time", strconv.FormatFloat(costTime.Seconds(), 'f', -1, 64)+"s")) + execDetail := info.StmtCtx.GetExecDetails() + logFields = append(logFields, execDetail.ToZapFields()...) + if copTaskInfo := info.StmtCtx.CopTasksDetails(); copTaskInfo != nil { + logFields = append(logFields, copTaskInfo.ToZapFields()...) + } + if statsInfo := info.StatsInfo(info.Plan); len(statsInfo) > 0 { + var buf strings.Builder + firstComma := false + vStr := "" + for k, v := range statsInfo { + if v == 0 { + vStr = "pseudo" + } else { + vStr = strconv.FormatUint(v, 10) + } + if firstComma { + buf.WriteString("," + k + ":" + vStr) + } else { + buf.WriteString(k + ":" + vStr) + firstComma = true + } + } + logFields = append(logFields, zap.String("stats", buf.String())) + } + if info.ID != 0 { + logFields = append(logFields, zap.Uint64("conn", info.ID)) + } + if len(info.User) > 0 { + logFields = append(logFields, zap.String("user", info.User)) + } + if len(info.DB) > 0 { + logFields = append(logFields, zap.String("database", info.DB)) + } + var tableIDs, indexNames string + if len(info.TableIDs) > 0 { + tableIDs = strings.ReplaceAll(fmt.Sprintf("%v", info.TableIDs), " ", ",") + logFields = append(logFields, zap.String("table_ids", tableIDs)) + } + if len(info.IndexNames) > 0 { + indexNames = strings.ReplaceAll(fmt.Sprintf("%v", info.IndexNames), " ", ",") + logFields = append(logFields, zap.String("index_names", indexNames)) + } + logFields = append(logFields, zap.Uint64("txn_start_ts", info.CurTxnStartTS)) + if memTracker := info.MemTracker; memTracker != nil { + logFields = append(logFields, zap.String("mem_max", fmt.Sprintf("%d Bytes (%v)", memTracker.MaxConsumed(), memTracker.FormatBytes(memTracker.MaxConsumed())))) + } + + const logSQLLen = 1024 * 8 + var sql string + if len(info.Info) > 0 { + sql = info.Info + if info.RedactSQL { + sql = parser.Normalize(sql) + } + } + if len(sql) > logSQLLen && needTruncateSQL { + sql = fmt.Sprintf("%s len(%d)", sql[:logSQLLen], len(sql)) + } + logFields = append(logFields, zap.String("sql", sql)) + logFields = append(logFields, zap.String("session_alias", info.SessionAlias)) + return logFields +} + +// PrintableASCII detects if b is a printable ASCII character. +// Ref to:http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm +func PrintableASCII(b byte) bool { + if b < 32 || b > 127 { + return false + } + + return true +} + +// FmtNonASCIIPrintableCharToHex turns non-printable-ASCII characters into Hex +func FmtNonASCIIPrintableCharToHex(str string) string { + var b bytes.Buffer + b.Grow(len(str) * 2) + for i := 0; i < len(str); i++ { + if PrintableASCII(str[i]) { + b.WriteByte(str[i]) + continue + } + + b.WriteString(`\x`) + // turns non-printable-ASCII character into hex-string + b.WriteString(fmt.Sprintf("%02X", str[i])) + } + return b.String() +} + +// TCPConnWithIOCounter is a wrapper of net.TCPConn with counter that accumulates +// the bytes this connection reads/writes. +type TCPConnWithIOCounter struct { + *net.TCPConn + c *atomic.Uint64 +} + +// NewTCPConnWithIOCounter creates a new TCPConnWithIOCounter. +func NewTCPConnWithIOCounter(conn *net.TCPConn, c *atomic.Uint64) net.Conn { + return &TCPConnWithIOCounter{ + TCPConn: conn, + c: c, + } +} + +func (t *TCPConnWithIOCounter) Read(b []byte) (n int, err error) { + n, err = t.TCPConn.Read(b) + t.c.Add(uint64(n)) + return n, err +} + +func (t *TCPConnWithIOCounter) Write(b []byte) (n int, err error) { + n, err = t.TCPConn.Write(b) + t.c.Add(uint64(n)) + return n, err +} + +// ReadLine tries to read a complete line from bufio.Reader. +// maxLineSize specifies the maximum size of a single line. +func ReadLine(reader *bufio.Reader, maxLineSize int) ([]byte, error) { + var resByte []byte + lineByte, isPrefix, err := reader.ReadLine() + if isPrefix { + // Need to read more data. + resByte = make([]byte, len(lineByte), len(lineByte)*2) + } else { + resByte = make([]byte, len(lineByte)) + } + // Use copy here to avoid shallow copy problem. + copy(resByte, lineByte) + if err != nil { + return resByte, err + } + var tempLine []byte + for isPrefix { + tempLine, isPrefix, err = reader.ReadLine() + resByte = append(resByte, tempLine...) // nozero + // Use maxLineSize to check the single line length. + if len(resByte) > maxLineSize { + return resByte, errors.Errorf("single line length exceeds limit: %v", maxLineSize) + } + if err != nil { + return resByte, err + } + } + return resByte, err +} + +// ReadLines tries to read lines from bufio.Reader. +// count specifies the number of lines. +// maxLineSize specifies the maximum size of a single line. +func ReadLines(reader *bufio.Reader, count int, maxLineSize int) ([][]byte, error) { + lines := make([][]byte, 0, count) + for i := 0; i < count; i++ { + line, err := ReadLine(reader, maxLineSize) + if err == io.EOF && len(lines) > 0 { + return lines, nil + } + if err != nil { + return nil, err + } + lines = append(lines, line) + } + return lines, nil +} + +// IsInCorrectIdentifierName checks if the identifier is incorrect. +// See https://dev.mysql.com/doc/refman/5.7/en/identifiers.html +func IsInCorrectIdentifierName(name string) bool { + if len(name) == 0 { + return true + } + if name[len(name)-1] == ' ' { + return true + } + return false +} diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go new file mode 100644 index 0000000000000..ccf83ab7c5b69 --- /dev/null +++ b/pkg/util/util_test.go @@ -0,0 +1,116 @@ +// Copyright 2022 PingCAP, Inc. +// +// 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. + +package util + +import ( + "bufio" + "io" + "strings" + "testing" + "time" + + "github.com/pingcap/tidb/pkg/sessionctx/stmtctx" + "github.com/pingcap/tidb/pkg/util/memory" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLogFormat(t *testing.T) { + mem := memory.NewTracker(-1, -1) + mem.Consume(1<<30 + 1<<29 + 1<<28 + 1<<27) + mockTooLongQuery := make([]byte, 1024*9) + + var refCount stmtctx.ReferenceCount = 0 + info := &ProcessInfo{ + ID: 233, + User: "PingCAP", + Host: "127.0.0.1", + DB: "Database", + Info: "select * from table where a > 1", + CurTxnStartTS: 23333, + StatsInfo: func(interface{}) map[string]uint64 { + return nil + }, + StmtCtx: stmtctx.NewStmtCtx(), + RefCountOfStmtCtx: &refCount, + MemTracker: mem, + RedactSQL: false, + SessionAlias: "alias123", + } + costTime := time.Second * 233 + logSQLTruncateLen := 1024 * 8 + logFields := GenLogFields(costTime, info, true) + + assert.Len(t, logFields, 8) + assert.Equal(t, "cost_time", logFields[0].Key) + assert.Equal(t, "233s", logFields[0].String) + assert.Equal(t, "conn", logFields[1].Key) + assert.Equal(t, int64(233), logFields[1].Integer) + assert.Equal(t, "user", logFields[2].Key) + assert.Equal(t, "PingCAP", logFields[2].String) + assert.Equal(t, "database", logFields[3].Key) + assert.Equal(t, "Database", logFields[3].String) + assert.Equal(t, "txn_start_ts", logFields[4].Key) + assert.Equal(t, int64(23333), logFields[4].Integer) + assert.Equal(t, "mem_max", logFields[5].Key) + assert.Equal(t, "2013265920 Bytes (1.88 GB)", logFields[5].String) + assert.Equal(t, "sql", logFields[6].Key) + assert.Equal(t, "select * from table where a > 1", logFields[6].String) + + logFields = GenLogFields(costTime, info, true) + assert.Equal(t, "select * from table where a > 1", logFields[6].String) + info.Info = string(mockTooLongQuery) + logFields = GenLogFields(costTime, info, true) + assert.Equal(t, len(logFields[6].String), logSQLTruncateLen+10) + logFields = GenLogFields(costTime, info, false) + assert.Equal(t, len(logFields[6].String), len(mockTooLongQuery)) + assert.Equal(t, logFields[7].String, "alias123") +} + +func TestReadLine(t *testing.T) { + reader := bufio.NewReader(strings.NewReader(`line1 +line2 +line3`)) + line, err := ReadLine(reader, 1024) + require.NoError(t, err) + require.Equal(t, "line1", string(line)) + line, err = ReadLine(reader, 1024) + require.NoError(t, err) + require.Equal(t, "line2", string(line)) + line, err = ReadLine(reader, 1024) + require.NoError(t, err) + require.Equal(t, "line3", string(line)) + line, err = ReadLine(reader, 1024) + require.Equal(t, io.EOF, err) + require.Len(t, line, 0) +} + +func TestIsInCorrectIdentifierName(t *testing.T) { + tests := []struct { + name string + input string + correct bool + }{ + {"Empty identifier", "", true}, + {"Ending space", "test ", true}, + {"Correct identifier", "test", false}, + {"Other correct Identifier", "aaa --\n\txyz", false}, + } + + for _, tc := range tests { + got := IsInCorrectIdentifierName(tc.input) + require.Equalf(t, tc.correct, got, "IsInCorrectIdentifierName(%v) != %v", tc.name, tc.correct) + } +} diff --git a/pkg/util/versioninfo/BUILD.bazel b/pkg/util/versioninfo/BUILD.bazel new file mode 100644 index 0000000000000..cb86627c6de28 --- /dev/null +++ b/pkg/util/versioninfo/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "versioninfo", + srcs = ["versioninfo.go"], + importpath = "github.com/pingcap/tidb/pkg/util/versioninfo", + visibility = ["//visibility:public"], +) diff --git a/util/versioninfo/versioninfo.go b/pkg/util/versioninfo/versioninfo.go similarity index 100% rename from util/versioninfo/versioninfo.go rename to pkg/util/versioninfo/versioninfo.go diff --git a/pkg/util/vitess/BUILD.bazel b/pkg/util/vitess/BUILD.bazel new file mode 100644 index 0000000000000..7874526ca1ada --- /dev/null +++ b/pkg/util/vitess/BUILD.bazel @@ -0,0 +1,25 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "vitess", + srcs = ["vitess_hash.go"], + importpath = "github.com/pingcap/tidb/pkg/util/vitess", + visibility = ["//visibility:public"], + deps = ["@com_github_pingcap_errors//:errors"], +) + +go_test( + name = "vitess_test", + timeout = "short", + srcs = [ + "main_test.go", + "vitess_hash_test.go", + ], + embed = [":vitess"], + flaky = True, + deps = [ + "//pkg/testkit/testsetup", + "@com_github_stretchr_testify//require", + "@org_uber_go_goleak//:goleak", + ], +) diff --git a/pkg/util/vitess/main_test.go b/pkg/util/vitess/main_test.go new file mode 100644 index 0000000000000..911d8b424df13 --- /dev/null +++ b/pkg/util/vitess/main_test.go @@ -0,0 +1,32 @@ +// Copyright 2021 PingCAP, Inc. +// +// 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. + +package vitess + +import ( + "testing" + + "github.com/pingcap/tidb/pkg/testkit/testsetup" + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + testsetup.SetupForCommonTest() + opts := []goleak.Option{ + goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), + goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), + goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), + } + goleak.VerifyTestMain(m, opts...) +} diff --git a/util/vitess/vitess_hash.go b/pkg/util/vitess/vitess_hash.go similarity index 100% rename from util/vitess/vitess_hash.go rename to pkg/util/vitess/vitess_hash.go diff --git a/util/vitess/vitess_hash_test.go b/pkg/util/vitess/vitess_hash_test.go similarity index 100% rename from util/vitess/vitess_hash_test.go rename to pkg/util/vitess/vitess_hash_test.go diff --git a/util/wait_group_wrapper.go b/pkg/util/wait_group_wrapper.go similarity index 99% rename from util/wait_group_wrapper.go rename to pkg/util/wait_group_wrapper.go index 21f808934f8d7..e1f44cb90e174 100644 --- a/util/wait_group_wrapper.go +++ b/pkg/util/wait_group_wrapper.go @@ -18,7 +18,7 @@ import ( "sync" "time" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "go.uber.org/zap" ) diff --git a/util/wait_group_wrapper_test.go b/pkg/util/wait_group_wrapper_test.go similarity index 100% rename from util/wait_group_wrapper_test.go rename to pkg/util/wait_group_wrapper_test.go diff --git a/pkg/util/watcher/BUILD.bazel b/pkg/util/watcher/BUILD.bazel new file mode 100644 index 0000000000000..11e467fb84ac8 --- /dev/null +++ b/pkg/util/watcher/BUILD.bazel @@ -0,0 +1,24 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "watcher", + srcs = [ + "event.go", + "watcher.go", + ], + importpath = "github.com/pingcap/tidb/pkg/util/watcher", + visibility = ["//visibility:public"], + deps = [ + "@com_github_pingcap_errors//:errors", + "@org_uber_go_atomic//:atomic", + ], +) + +go_test( + name = "watcher_test", + timeout = "short", + srcs = ["watcher_test.go"], + embed = [":watcher"], + flaky = True, + deps = ["@com_github_stretchr_testify//require"], +) diff --git a/util/watcher/event.go b/pkg/util/watcher/event.go similarity index 100% rename from util/watcher/event.go rename to pkg/util/watcher/event.go diff --git a/util/watcher/watcher.go b/pkg/util/watcher/watcher.go similarity index 100% rename from util/watcher/watcher.go rename to pkg/util/watcher/watcher.go diff --git a/util/watcher/watcher_test.go b/pkg/util/watcher/watcher_test.go similarity index 100% rename from util/watcher/watcher_test.go rename to pkg/util/watcher/watcher_test.go diff --git a/pkg/util/zeropool/BUILD.bazel b/pkg/util/zeropool/BUILD.bazel new file mode 100644 index 0000000000000..016c0c6f674ab --- /dev/null +++ b/pkg/util/zeropool/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "zeropool", + srcs = ["pool.go"], + importpath = "github.com/pingcap/tidb/pkg/util/zeropool", + visibility = ["//visibility:public"], +) + +go_test( + name = "zeropool_test", + timeout = "short", + srcs = ["pool_test.go"], + flaky = True, + deps = [ + ":zeropool", + "@com_github_stretchr_testify//require", + "@org_uber_go_atomic//:atomic", + ], +) diff --git a/util/zeropool/pool.go b/pkg/util/zeropool/pool.go similarity index 100% rename from util/zeropool/pool.go rename to pkg/util/zeropool/pool.go diff --git a/pkg/util/zeropool/pool_test.go b/pkg/util/zeropool/pool_test.go new file mode 100644 index 0000000000000..5a287dfae43c5 --- /dev/null +++ b/pkg/util/zeropool/pool_test.go @@ -0,0 +1,178 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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. + +package zeropool_test + +import ( + "math" + "sync" + "testing" + + "github.com/pingcap/tidb/pkg/util/zeropool" + "github.com/stretchr/testify/require" + "go.uber.org/atomic" +) + +func TestPool(t *testing.T) { + t.Run("provides correct values", func(t *testing.T) { + pool := zeropool.New(func() []byte { return make([]byte, 1024) }) + item1 := pool.Get() + require.Equal(t, 1024, len(item1)) + + item2 := pool.Get() + require.Equal(t, 1024, len(item2)) + + pool.Put(item1) + pool.Put(item2) + + item1 = pool.Get() + require.Equal(t, 1024, len(item1)) + + item2 = pool.Get() + require.Equal(t, 1024, len(item2)) + }) + + t.Run("is not racy", func(t *testing.T) { + pool := zeropool.New(func() []byte { return make([]byte, 1024) }) + + const iterations = 1e6 + const concurrency = math.MaxUint8 + var counter atomic.Int64 + + do := make(chan struct{}, 1e6) + for i := 0; i < iterations; i++ { + do <- struct{}{} + } + close(do) + + run := make(chan struct{}) + done := sync.WaitGroup{} + done.Add(concurrency) + for i := 0; i < concurrency; i++ { + go func(worker int) { + <-run + for range do { + item := pool.Get() + item[0] = byte(worker) + counter.Add(1) // Counts and also adds some delay to add raciness. + if item[0] != byte(worker) { + panic("wrong value") + } + pool.Put(item) + } + done.Done() + }(i) + } + close(run) + done.Wait() + t.Logf("Done %d iterations", counter.Load()) + }) + + t.Run("does not allocate", func(t *testing.T) { + pool := zeropool.New(func() []byte { return make([]byte, 1024) }) + // Warm up, this will alloate one slice. + slice := pool.Get() + pool.Put(slice) + + allocs := testing.AllocsPerRun(1000, func() { + slice := pool.Get() + pool.Put(slice) + }) + // Don't compare to 0, as when passing all the tests the GC could flush the pools during this test and we would allocate. + // Just check that it's less than 1 on average, which is mostly the same thing. + require.Less(t, allocs, 1., "Should not allocate.") + }) + + t.Run("zero value is valid", func(t *testing.T) { + var pool zeropool.Pool[[]byte] + slice := pool.Get() + pool.Put(slice) + + allocs := testing.AllocsPerRun(1000, func() { + slice := pool.Get() + pool.Put(slice) + }) + // Don't compare to 0, as when passing all the tests the GC could flush the pools during this test and we would allocate. + // Just check that it's less than 1 on average, which is mostly the same thing. + require.Less(t, allocs, 1., "Should not allocate.") + }) +} + +func BenchmarkZeropoolPool(b *testing.B) { + pool := zeropool.New(func() []byte { return make([]byte, 1024) }) + + // Warmup + item := pool.Get() + pool.Put(item) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + item := pool.Get() + pool.Put(item) + } +} + +// BenchmarkSyncPoolValue uses sync.Pool to store values, which makes an allocation on each Put call. +func BenchmarkSyncPoolValue(b *testing.B) { + pool := sync.Pool{New: func() any { + return make([]byte, 1024) + }} + + // Warmup + item := pool.Get().([]byte) + pool.Put(item) //nolint:staticcheck // This allocates. + + b.ResetTimer() + for i := 0; i < b.N; i++ { + item := pool.Get().([]byte) + pool.Put(item) //nolint:staticcheck // This allocates. + } +} + +// BenchmarkSyncPoolNewPointer uses sync.Pool to store pointers, but it calls Put with a new pointer every time. +func BenchmarkSyncPoolNewPointer(b *testing.B) { + pool := sync.Pool{New: func() any { + v := make([]byte, 1024) + return &v + }} + + // Warmup + item := pool.Get().(*[]byte) + pool.Put(item) //nolint:staticcheck // This allocates. + + b.ResetTimer() + for i := 0; i < b.N; i++ { + item := pool.Get().(*[]byte) + buf := *item + pool.Put(&buf) //nolint:staticcheck // New pointer. + } +} + +// BenchmarkSyncPoolPointer illustrates the optimal usage of sync.Pool, not always possible. +func BenchmarkSyncPoolPointer(b *testing.B) { + pool := sync.Pool{New: func() any { + v := make([]byte, 1024) + return &v + }} + + // Warmup + item := pool.Get().(*[]byte) + pool.Put(item) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + item := pool.Get().(*[]byte) + pool.Put(item) + } +} diff --git a/planner/BUILD.bazel b/planner/BUILD.bazel deleted file mode 100644 index efa663186301d..0000000000000 --- a/planner/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "planner", - srcs = ["optimize.go"], - importpath = "github.com/pingcap/tidb/planner", - visibility = ["//visibility:public"], - deps = [ - "//bindinfo", - "//domain", - "//infoschema", - "//kv", - "//metrics", - "//parser", - "//parser/ast", - "//planner/cascades", - "//planner/core", - "//planner/util/debugtrace", - "//privilege", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//types", - "//util/hint", - "//util/intest", - "//util/logutil", - "//util/parser", - "//util/topsql", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) diff --git a/planner/cardinality/BUILD.bazel b/planner/cardinality/BUILD.bazel deleted file mode 100644 index e5d3aad9515d5..0000000000000 --- a/planner/cardinality/BUILD.bazel +++ /dev/null @@ -1,93 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cardinality", - srcs = [ - "cross_estimation.go", - "join.go", - "ndv.go", - "pseudo.go", - "row_count_column.go", - "row_count_index.go", - "row_size.go", - "selectivity.go", - "trace.go", - ], - importpath = "github.com/pingcap/tidb/planner/cardinality", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/ast", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//planner/property", - "//planner/util", - "//planner/util/debugtrace", - "//sessionctx", - "//sessionctx/stmtctx", - "//statistics", - "//tablecodec", - "//types", - "//types/parser_driver", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/logutil", - "//util/mathutil", - "//util/ranger", - "//util/set", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_golang_x_exp//maps", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "cardinality_test", - timeout = "short", - srcs = [ - "main_test.go", - "row_count_test.go", - "row_size_test.go", - "selectivity_test.go", - "trace_test.go", - ], - data = glob(["testdata/**"]), - embed = [":cardinality"], - flaky = True, - shard_count = 25, - deps = [ - "//config", - "//domain", - "//executor", - "//expression", - "//infoschema", - "//kv", - "//parser", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//session", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//statistics", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/collate", - "//util/mock", - "//util/ranger", - "//util/tracing", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/cardinality/join.go b/planner/cardinality/join.go deleted file mode 100644 index cea0a4fca8a7c..0000000000000 --- a/planner/cardinality/join.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cardinality - -import ( - "math" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" -) - -// EstimateFullJoinRowCount estimates the row count of a full join. -func EstimateFullJoinRowCount(sctx sessionctx.Context, - isCartesian bool, - leftProfile, rightProfile *property.StatsInfo, - leftJoinKeys, rightJoinKeys []*expression.Column, - leftSchema, rightSchema *expression.Schema, - leftNAJoinKeys, rightNAJoinKeys []*expression.Column) float64 { - if isCartesian { - return leftProfile.RowCount * rightProfile.RowCount - } - var leftKeyNDV, rightKeyNDV float64 - var leftColCnt, rightColCnt int - if len(leftJoinKeys) > 0 || len(rightJoinKeys) > 0 { - leftKeyNDV, leftColCnt = EstimateColsNDVWithMatchedLen(leftJoinKeys, leftSchema, leftProfile) - rightKeyNDV, rightColCnt = EstimateColsNDVWithMatchedLen(rightJoinKeys, rightSchema, rightProfile) - } else { - leftKeyNDV, leftColCnt = EstimateColsNDVWithMatchedLen(leftNAJoinKeys, leftSchema, leftProfile) - rightKeyNDV, rightColCnt = EstimateColsNDVWithMatchedLen(rightNAJoinKeys, rightSchema, rightProfile) - } - count := leftProfile.RowCount * rightProfile.RowCount / max(leftKeyNDV, rightKeyNDV) - if sctx.GetSessionVars().TiDBOptJoinReorderThreshold <= 0 { - return count - } - // If we enable the DP choice, we multiple the 0.9 for each remained join key supposing that 0.9 is the correlation factor between them. - // This estimation logic is referred to Presto. - return count * math.Pow(0.9, float64(len(leftJoinKeys)-max(leftColCnt, rightColCnt))) -} diff --git a/planner/cardinality/main_test.go b/planner/cardinality/main_test.go deleted file mode 100644 index d4c80f050638e..0000000000000 --- a/planner/cardinality/main_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cardinality - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper, 3) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - if !flag.Parsed() { - flag.Parse() - } - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - testDataMap.LoadTestSuiteData("testdata", "cardinality_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetCardinalitySuiteData() testdata.TestData { - return testDataMap["cardinality_suite"] -} - -// MockStatsNode is only used for test. -func MockStatsNode(id int64, m int64, num int) *StatsNode { - return &StatsNode{ID: id, mask: m, numCols: num} -} diff --git a/planner/cardinality/trace.go b/planner/cardinality/trace.go deleted file mode 100644 index a729457e3d109..0000000000000 --- a/planner/cardinality/trace.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cardinality - -import ( - "bytes" - "encoding/json" - "errors" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/tracing" - "go.uber.org/zap" -) - -// ceTraceExpr appends an expression and related information into CE trace -func ceTraceExpr(sctx sessionctx.Context, tableID int64, tp string, expr expression.Expression, rowCount float64) { - exprStr, err := exprToString(expr) - if err != nil { - logutil.BgLogger().Debug("Failed to trace CE of an expression", zap.String("category", "OptimizerTrace"), - zap.Any("expression", expr)) - return - } - rec := tracing.CETraceRecord{ - TableID: tableID, - Type: tp, - Expr: exprStr, - RowCount: uint64(rowCount), - } - sc := sctx.GetSessionVars().StmtCtx - sc.OptimizerCETrace = append(sc.OptimizerCETrace, &rec) -} - -// exprToString prints an Expression into a string which can appear in a SQL. -// -// It might be too tricky because it makes use of TiDB allowing using internal function name in SQL. -// For example, you can write `eq`(a, 1), which is the same as a = 1. -// We should have implemented this by first implementing a method to turn an expression to an AST -// -// then call astNode.Restore(), like the Constant case here. But for convenience, we use this trick for now. -// -// It may be more appropriate to put this in expression package. But currently we only use it for CE trace, -// -// and it may not be general enough to handle all possible expressions. So we put it here for now. -func exprToString(e expression.Expression) (string, error) { - switch expr := e.(type) { - case *expression.ScalarFunction: - var buffer bytes.Buffer - buffer.WriteString("`" + expr.FuncName.L + "`(") - switch expr.FuncName.L { - case ast.Cast: - for _, arg := range expr.GetArgs() { - argStr, err := exprToString(arg) - if err != nil { - return "", err - } - buffer.WriteString(argStr) - buffer.WriteString(", ") - buffer.WriteString(expr.RetType.String()) - } - default: - for i, arg := range expr.GetArgs() { - argStr, err := exprToString(arg) - if err != nil { - return "", err - } - buffer.WriteString(argStr) - if i+1 != len(expr.GetArgs()) { - buffer.WriteString(", ") - } - } - } - buffer.WriteString(")") - return buffer.String(), nil - case *expression.Column: - return expr.String(), nil - case *expression.CorrelatedColumn: - return "", errors.New("tracing for correlated columns not supported now") - case *expression.Constant: - value, err := expr.Eval(chunk.Row{}) - if err != nil { - return "", err - } - valueExpr := driver.ValueExpr{Datum: value} - var buffer bytes.Buffer - restoreCtx := format.NewRestoreCtx(format.DefaultRestoreFlags, &buffer) - err = valueExpr.Restore(restoreCtx) - if err != nil { - return "", err - } - return buffer.String(), nil - } - return "", errors.New("unexpected type of Expression") -} - -/* - Below is debug trace for GetRowCountByXXX(). -*/ - -type getRowCountInput struct { - Ranges []string - ID int64 -} - -func debugTraceGetRowCountInput( - s sessionctx.Context, - id int64, - ranges ranger.Ranges, -) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - newCtx := &getRowCountInput{ - ID: id, - Ranges: make([]string, len(ranges)), - } - for i, r := range ranges { - newCtx.Ranges[i] = r.String() - } - root.AppendStepToCurrentContext(newCtx) -} - -// GetTblInfoForUsedStatsByPhysicalID get table name, partition name and TableInfo that will be used to record used stats. -var GetTblInfoForUsedStatsByPhysicalID func(sctx sessionctx.Context, id int64) (fullName string, tblInfo *model.TableInfo) - -// recordUsedItemStatsStatus only records un-FullLoad item load status during user query -func recordUsedItemStatsStatus(sctx sessionctx.Context, stats interface{}, tableID, id int64) { - // Sometimes we try to use stats on _tidb_rowid (id == -1), which must be empty, we ignore this case here. - if id <= 0 { - return - } - var isIndex, missing bool - var loadStatus *statistics.StatsLoadedStatus - switch x := stats.(type) { - case *statistics.Column: - isIndex = false - if x == nil { - missing = true - } else { - loadStatus = &x.StatsLoadedStatus - } - case *statistics.Index: - isIndex = true - if x == nil { - missing = true - } else { - loadStatus = &x.StatsLoadedStatus - } - } - - // no need to record - if !missing && loadStatus.IsFullLoad() { - return - } - - // need to record - statsRecord := sctx.GetSessionVars().StmtCtx.GetUsedStatsInfo(true) - if statsRecord[tableID] == nil { - name, tblInfo := GetTblInfoForUsedStatsByPhysicalID(sctx, tableID) - statsRecord[tableID] = &stmtctx.UsedStatsInfoForTable{ - Name: name, - TblInfo: tblInfo, - } - } - recordForTbl := statsRecord[tableID] - - var recordForColOrIdx map[int64]string - if isIndex { - if recordForTbl.IndexStatsLoadStatus == nil { - recordForTbl.IndexStatsLoadStatus = make(map[int64]string, 1) - } - recordForColOrIdx = recordForTbl.IndexStatsLoadStatus - } else { - if recordForTbl.ColumnStatsLoadStatus == nil { - recordForTbl.ColumnStatsLoadStatus = make(map[int64]string, 1) - } - recordForColOrIdx = recordForTbl.ColumnStatsLoadStatus - } - - if missing { - recordForColOrIdx[id] = "missing" - return - } - recordForColOrIdx[id] = loadStatus.StatusToString() -} - -// ceTraceRange appends a list of ranges and related information into CE trace -func ceTraceRange(sctx sessionctx.Context, tableID int64, colNames []string, ranges []*ranger.Range, tp string, rowCount uint64) { - sc := sctx.GetSessionVars().StmtCtx - allPoint := true - for _, ran := range ranges { - if !ran.IsPointNullable(sctx) { - allPoint = false - break - } - } - if allPoint { - tp = tp + "-Point" - } else { - tp = tp + "-Range" - } - expr, err := ranger.RangesToString(sc, ranges, colNames) - if err != nil { - logutil.BgLogger().Debug("Failed to trace CE of ranges", zap.String("category", "OptimizerTrace"), zap.Error(err)) - } - // We don't need to record meaningless expressions. - if expr == "" || expr == "true" || expr == "false" { - return - } - ceRecord := tracing.CETraceRecord{ - TableID: tableID, - Type: tp, - Expr: expr, - RowCount: rowCount, - } - sc.OptimizerCETrace = append(sc.OptimizerCETrace, &ceRecord) -} - -/* - Below is debug trace for the estimation for each single range inside GetRowCountByXXX(). -*/ - -type startEstimateRangeInfo struct { - Range string - LowValueEncoded []byte - HighValueEncoded []byte - CurrentRowCount float64 -} - -func debugTraceStartEstimateRange( - s sessionctx.Context, - r *ranger.Range, - lowBytes, highBytes []byte, - currentCount float64, -) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &startEstimateRangeInfo{ - CurrentRowCount: currentCount, - Range: r.String(), - LowValueEncoded: lowBytes, - HighValueEncoded: highBytes, - } - root.AppendStepWithNameToCurrentContext(traceInfo, "Start estimate range") -} - -type debugTraceAddRowCountType int8 - -const ( - debugTraceUnknownTypeAddRowCount debugTraceAddRowCountType = iota - debugTraceImpossible - debugTraceUniquePoint - debugTracePoint - debugTraceRange - debugTraceVer1SmallRange -) - -var addRowCountTypeToString = map[debugTraceAddRowCountType]string{ - debugTraceUnknownTypeAddRowCount: "Unknown", - debugTraceImpossible: "Impossible", - debugTraceUniquePoint: "Unique point", - debugTracePoint: "Point", - debugTraceRange: "Range", - debugTraceVer1SmallRange: "Small range in ver1 stats", -} - -func (d debugTraceAddRowCountType) MarshalJSON() ([]byte, error) { - return json.Marshal(addRowCountTypeToString[d]) -} - -type endEstimateRangeInfo struct { - RowCount float64 - Type debugTraceAddRowCountType -} - -func debugTraceEndEstimateRange( - s sessionctx.Context, - count float64, - addType debugTraceAddRowCountType, -) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &endEstimateRangeInfo{ - RowCount: count, - Type: addType, - } - root.AppendStepWithNameToCurrentContext(traceInfo, "End estimate range") -} diff --git a/planner/cardinality/trace_test.go b/planner/cardinality/trace_test.go deleted file mode 100644 index 44a44e5b1dcf0..0000000000000 --- a/planner/cardinality/trace_test.go +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cardinality_test - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/tracing" - "github.com/stretchr/testify/require" -) - -func TestTraceCE(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, d varchar(10), index idx(a, b))") - tk.MustExec(`insert into t values(1, 1, "aaa"), - (1, 1, "bbb"), - (1, 2, "ccc"), - (1, 2, "ddd"), - (2, 2, "aaa"), - (2, 3, "bbb")`) - tk.MustExec("analyze table t") - var ( - in []string - out []struct { - Expr string - Trace []*tracing.CETraceRecord - } - ) - traceSuiteData := cardinality.GetCardinalitySuiteData() - traceSuiteData.LoadTestCases(t, &in, &out) - - // Load needed statistics. - for _, tt := range in { - sql := "explain select * from t where " + tt - tk.MustExec(sql) - } - statsHandle := dom.StatsHandle() - err := statsHandle.LoadNeededHistograms() - require.NoError(t, err) - - sctx := tk.Session().(sessionctx.Context) - is := sctx.GetInfoSchema().(infoschema.InfoSchema) - p := parser.New() - for i, expr := range in { - stmtCtx := sctx.GetSessionVars().StmtCtx - sql := "explain select * from t where " + expr - stmtCtx.EnableOptimizerCETrace = true - stmtCtx.OptimizerCETrace = nil - stmt, err := p.ParseOneStmt(sql, "", "") - require.NoError(t, err) - _, _, err = plannercore.OptimizeAstNode(context.Background(), sctx, stmt, is) - require.NoError(t, err) - - traceResult := sctx.GetSessionVars().StmtCtx.OptimizerCETrace - // Ignore the TableID field because this field is unexported when marshalling to JSON. - for _, rec := range traceResult { - rec.TableID = 0 - } - - testdata.OnRecord(func() { - out[i].Expr = expr - out[i].Trace = traceResult - }) - // Assert using the result in the stmtCtx - require.ElementsMatch(t, traceResult, out[i].Trace) - - sql = "trace plan target='estimation' select * from t where " + expr - result := tk.MustQuery(sql) - require.Len(t, result.Rows(), 1) - resultStr := result.Rows()[0][0].(string) - var resultJSON []*tracing.CETraceRecord - err = json.Unmarshal([]byte(resultStr), &resultJSON) - require.NoError(t, err) - // Assert using the result of trace plan SQL - require.ElementsMatch(t, resultJSON, out[i].Trace) - } -} - -func TestTraceDebugSelectivity(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - statsHandle := dom.StatsHandle() - - // Make the result of v1 analyze result stable - // 1. make sure all rows are always collect as samples - originalSampleSize := executor.MaxRegionSampleSize - executor.MaxRegionSampleSize = 10000 - defer func() { - executor.MaxRegionSampleSize = originalSampleSize - }() - // 2. make the order of samples for building TopN stable - // (the earlier TopN entry will modify the CMSketch, therefore influence later TopN entry's row count, - // see (*SampleCollector).ExtractTopN() for details) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/statistics/StabilizeV1AnalyzeTopN", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/statistics/StabilizeV1AnalyzeTopN")) - }() - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index iab(a, b), index ib(b))") - require.NoError(t, statsHandle.HandleDDLEvent(<-statsHandle.DDLEventCh())) - - // Prepare the data. - - // For column a, from -1000 to 999, each value appears 1 time, - // but if it's dividable by 100, make this value appear 50 times. - // For column b, it's always a+500. - start := -1000 - for i := 0; i < 2000; i += 50 { - sql := "insert into t values " - // 50 rows as a batch - values := make([]string, 0, 50) - for j := 0; j < 50; j++ { - values = append(values, fmt.Sprintf("(%d,%d)", start+i+j, start+i+j+500)) - } - sql = sql + strings.Join(values, ",") - tk.MustExec(sql) - - if i%100 == 0 { - sql := "insert into t values " - topNValue := fmt.Sprintf("(%d,%d) ,", start+i, start+i+500) - sql = sql + strings.Repeat(topNValue, 49) - sql = sql[0 : len(sql)-1] - tk.MustExec(sql) - } - } - require.Nil(t, statsHandle.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t with 1 samplerate, 20 topn") - require.Nil(t, statsHandle.Update(dom.InfoSchema())) - // Add 100 modify count - sql := "insert into t values " - topNValue := fmt.Sprintf("(%d,%d) ,", 5000, 5000) - sql = sql + strings.Repeat(topNValue, 100) - sql = sql[0 : len(sql)-1] - tk.MustExec(sql) - require.Nil(t, statsHandle.DumpStatsDeltaToKV(true)) - require.Nil(t, statsHandle.Update(dom.InfoSchema())) - - var ( - in []string - out []struct { - ResultForV1 interface{} - ResultForV2 interface{} - } - ) - traceSuiteData := cardinality.GetCardinalitySuiteData() - traceSuiteData.LoadTestCases(t, &in, &out) - - // Trigger loading needed statistics. - for _, tt := range in { - sql := "explain " + tt - tk.MustExec(sql) - } - err := statsHandle.LoadNeededHistograms() - require.NoError(t, err) - - sctx := tk.Session().(sessionctx.Context) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tb.Meta() - statsTbl := statsHandle.GetTableStats(tblInfo) - stmtCtx := sctx.GetSessionVars().StmtCtx - stmtCtx.EnableOptimizerDebugTrace = true - - // Collect common information for the following tests. - p := parser.New() - dsSchemaCols := make([][]*expression.Column, 0, len(in)) - selConditions := make([][]expression.Expression, 0, len(in)) - tblInfos := make([]*model.TableInfo, 0, len(in)) - for _, sql := range in { - stmt, err := p.ParseOneStmt(sql, "", "") - require.NoError(t, err) - ret := &plannercore.PreprocessorReturn{} - err = plannercore.Preprocess(context.Background(), sctx, stmt, plannercore.WithPreprocessorReturn(ret)) - require.NoError(t, err) - p, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), sctx, stmt, ret.InfoSchema) - require.NoError(t, err) - - sel := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) - ds := sel.Children()[0].(*plannercore.DataSource) - - dsSchemaCols = append(dsSchemaCols, ds.Schema().Columns) - selConditions = append(selConditions, sel.Conditions) - tblInfos = append(tblInfos, ds.TableInfo()) - } - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - encoder.SetEscapeHTML(false) - - // Test using ver2 stats. - for i, sql := range in { - stmtCtx.OptimizerDebugTrace = nil - histColl := statsTbl.GenerateHistCollFromColumnInfo(tblInfos[i], dsSchemaCols[i]) - _, _, err = cardinality.Selectivity(sctx, histColl, selConditions[i], nil) - require.NoError(t, err, sql, "For ver2") - traceInfo := stmtCtx.OptimizerDebugTrace - buf.Reset() - require.NoError(t, encoder.Encode(traceInfo), sql, "For ver2") - var res interface{} - require.NoError(t, json.Unmarshal(buf.Bytes(), &res), sql, "For ver2") - testdata.OnRecord(func() { - out[i].ResultForV2 = res - }) - require.Equal(t, out[i].ResultForV2, res, sql, "For ver2") - } - - tk.MustExec("set tidb_analyze_version = 1") - tk.MustExec("analyze table t with 20 topn") - require.Nil(t, statsHandle.Update(dom.InfoSchema())) - statsTbl = statsHandle.GetTableStats(tblInfo) - - // Test using ver1 stats. - stmtCtx = sctx.GetSessionVars().StmtCtx - stmtCtx.EnableOptimizerDebugTrace = true - for i, sql := range in { - stmtCtx.OptimizerDebugTrace = nil - histColl := statsTbl.GenerateHistCollFromColumnInfo(tblInfos[i], dsSchemaCols[i]) - _, _, err = cardinality.Selectivity(sctx, histColl, selConditions[i], nil) - require.NoError(t, err, sql, "For ver1") - traceInfo := stmtCtx.OptimizerDebugTrace - buf.Reset() - require.NoError(t, encoder.Encode(traceInfo), sql, "For ver1") - var res interface{} - require.NoError(t, json.Unmarshal(buf.Bytes(), &res), sql, "For ver1") - testdata.OnRecord(func() { - out[i].ResultForV1 = res - }) - require.Equal(t, out[i].ResultForV1, res, sql, "For ver1") - } -} diff --git a/planner/cascades/BUILD.bazel b/planner/cascades/BUILD.bazel deleted file mode 100644 index e763f09636af5..0000000000000 --- a/planner/cascades/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cascades", - srcs = [ - "enforcer_rules.go", - "implementation_rules.go", - "optimize.go", - "stringer.go", - "transformation_rules.go", - ], - importpath = "github.com/pingcap/tidb/planner/cascades", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//expression/aggregation", - "//kv", - "//parser/ast", - "//parser/mysql", - "//planner/core", - "//planner/implementation", - "//planner/memo", - "//planner/property", - "//planner/util", - "//sessionctx", - "//types", - "//util/ranger", - "//util/set", - ], -) - -go_test( - name = "cascades_test", - timeout = "short", - srcs = [ - "enforcer_rules_test.go", - "integration_test.go", - "main_test.go", - "optimize_test.go", - "stringer_test.go", - "transformation_rules_test.go", - ], - data = glob(["testdata/**"]), - embed = [":cascades"], - flaky = True, - shard_count = 26, - deps = [ - "//domain", - "//expression", - "//infoschema", - "//parser", - "//parser/model", - "//planner/core", - "//planner/memo", - "//planner/property", - "//testkit", - "//testkit/testdata", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/cascades/integration_test.go b/planner/cascades/integration_test.go deleted file mode 100644 index a03fbb28f1821..0000000000000 --- a/planner/cascades/integration_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package cascades_test - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/planner/cascades" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" -) - -func TestCascadePlannerHashedPartTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists pt1") - tk.MustExec("create table pt1(a bigint, b bigint) partition by hash(a) partitions 4") - tk.MustExec(`insert into pt1 values(1,10)`) - tk.MustExec(`insert into pt1 values(2,20)`) - tk.MustExec(`insert into pt1 values(3,30)`) - tk.MustExec(`insert into pt1 values(4,40)`) - tk.MustExec(`insert into pt1 values(5,50)`) - - tk.MustExec("set @@tidb_enable_cascades_planner = 1") - var input []string - var output []struct { - SQL string - Plan []string - Result []string - } - integrationSuiteData := cascades.GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, sql := range input { - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + sql).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(sql).Rows()) - }) - tk.MustQuery("explain " + sql).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(sql).Check(testkit.Rows(output[i].Result...)) - } -} diff --git a/planner/cascades/main_test.go b/planner/cascades/main_test.go deleted file mode 100644 index c0e826a6fa134..0000000000000 --- a/planner/cascades/main_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cascades - -import ( - "flag" - "fmt" - "os" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper, 3) -var stringerSuiteData testdata.TestData -var transformationRulesSuiteData testdata.TestData - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - - testDataMap.LoadTestSuiteData("testdata", "stringer_suite") - testDataMap.LoadTestSuiteData("testdata", "transformation_rules_suite") - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - stringerSuiteData = testDataMap["stringer_suite"] - transformationRulesSuiteData = testDataMap["transformation_rules_suite"] - - if exitCode := m.Run(); exitCode != 0 { - os.Exit(exitCode) - } - - testDataMap.GenerateOutputIfNeeded() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - if err := goleak.Find(opts...); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "goleak: Errors on successful test run: %v\n", err) - os.Exit(1) - } -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} diff --git a/planner/cascades/optimize.go b/planner/cascades/optimize.go deleted file mode 100644 index 29be0272e011e..0000000000000 --- a/planner/cascades/optimize.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package cascades - -import ( - "container/list" - "math" - - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" -) - -// DefaultOptimizer is the optimizer which contains all of the default -// transformation and implementation rules. -var DefaultOptimizer = NewOptimizer() - -// Optimizer is the struct for cascades optimizer. -type Optimizer struct { - transformationRuleBatches []TransformationRuleBatch - implementationRuleMap map[memo.Operand][]ImplementationRule -} - -// NewOptimizer returns a cascades optimizer with default transformation -// rules and implementation rules. -func NewOptimizer() *Optimizer { - return &Optimizer{ - transformationRuleBatches: DefaultRuleBatches, - implementationRuleMap: defaultImplementationMap, - } -} - -// ResetTransformationRules resets the transformationRuleBatches of the optimizer, and returns the optimizer. -func (opt *Optimizer) ResetTransformationRules(ruleBatches ...TransformationRuleBatch) *Optimizer { - opt.transformationRuleBatches = ruleBatches - return opt -} - -// ResetImplementationRules resets the implementationRuleMap of the optimizer, and returns the optimizer. -func (opt *Optimizer) ResetImplementationRules(rules map[memo.Operand][]ImplementationRule) *Optimizer { - opt.implementationRuleMap = rules - return opt -} - -// GetImplementationRules gets all the candidate implementation rules of the optimizer -// for the logical plan node. -func (opt *Optimizer) GetImplementationRules(node plannercore.LogicalPlan) []ImplementationRule { - return opt.implementationRuleMap[memo.GetOperand(node)] -} - -// FindBestPlan is the optimization entrance of the cascades planner. The -// optimization is composed of 3 phases: preprocessing, exploration and implementation. -// -// ------------------------------------------------------------------------------ -// Phase 1: Preprocessing -// ------------------------------------------------------------------------------ -// -// The target of this phase is to preprocess the plan tree by some heuristic -// rules which should always be beneficial, for example Column Pruning. -// -// ------------------------------------------------------------------------------ -// Phase 2: Exploration -// ------------------------------------------------------------------------------ -// -// The target of this phase is to explore all the logically equivalent -// expressions by exploring all the equivalent group expressions of each group. -// -// At the very beginning, there is only one group expression in a Group. After -// applying some transformation rules on certain expressions of the Group, all -// the equivalent expressions are found and stored in the Group. This procedure -// can be regarded as searching for a weak connected component in a directed -// graph, where nodes are expressions and directed edges are the transformation -// rules. -// -// ------------------------------------------------------------------------------ -// Phase 3: Implementation -// ------------------------------------------------------------------------------ -// -// The target of this phase is to search the best physical plan for a Group -// which satisfies a certain required physical property. -// -// In this phase, we need to enumerate all the applicable implementation rules -// for each expression in each group under the required physical property. A -// memo structure is used for a group to reduce the repeated search on the same -// required physical property. -func (opt *Optimizer) FindBestPlan(sctx sessionctx.Context, logical plannercore.LogicalPlan) (p plannercore.PhysicalPlan, cost float64, err error) { - logical, err = opt.onPhasePreprocessing(sctx, logical) - if err != nil { - return nil, 0, err - } - rootGroup := memo.Convert2Group(logical) - err = opt.onPhaseExploration(sctx, rootGroup) - if err != nil { - return nil, 0, err - } - p, cost, err = opt.onPhaseImplementation(sctx, rootGroup) - if err != nil { - return nil, 0, err - } - err = p.ResolveIndices() - return p, cost, err -} - -func (*Optimizer) onPhasePreprocessing(_ sessionctx.Context, plan plannercore.LogicalPlan) (plannercore.LogicalPlan, error) { - err := plan.PruneColumns(plan.Schema().Columns, nil) - if err != nil { - return nil, err - } - return plan, nil -} - -func (opt *Optimizer) onPhaseExploration(_ sessionctx.Context, g *memo.Group) error { - for round, ruleBatch := range opt.transformationRuleBatches { - for !g.Explored(round) { - err := opt.exploreGroup(g, round, ruleBatch) - if err != nil { - return err - } - } - } - return nil -} - -func (opt *Optimizer) exploreGroup(g *memo.Group, round int, ruleBatch TransformationRuleBatch) error { - if g.Explored(round) { - return nil - } - g.SetExplored(round) - - for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { - curExpr := elem.Value.(*memo.GroupExpr) - if curExpr.Explored(round) { - continue - } - curExpr.SetExplored(round) - - // Explore child groups firstly. - for _, childGroup := range curExpr.Children { - for !childGroup.Explored(round) { - if err := opt.exploreGroup(childGroup, round, ruleBatch); err != nil { - return err - } - } - } - - eraseCur, err := opt.findMoreEquiv(g, elem, round, ruleBatch) - if err != nil { - return err - } - if eraseCur { - g.Delete(curExpr) - } - } - return nil -} - -// findMoreEquiv finds and applies the matched transformation rules. -func (*Optimizer) findMoreEquiv(g *memo.Group, elem *list.Element, round int, ruleBatch TransformationRuleBatch) (eraseCur bool, err error) { - expr := elem.Value.(*memo.GroupExpr) - operand := memo.GetOperand(expr.ExprNode) - for _, rule := range ruleBatch[operand] { - pattern := rule.GetPattern() - if !pattern.Operand.Match(operand) { - continue - } - // Create a binding of the current Group expression and the pattern of - // the transformation rule to enumerate all the possible expressions. - iter := memo.NewExprIterFromGroupElem(elem, pattern) - for ; iter != nil && iter.Matched(); iter.Next() { - if !rule.Match(iter) { - continue - } - - newExprs, eraseOld, eraseAll, err := rule.OnTransform(iter) - if err != nil { - return false, err - } - - if eraseAll { - g.DeleteAll() - for _, e := range newExprs { - g.Insert(e) - } - // If we delete all of the other GroupExprs, we can break the search. - g.SetExplored(round) - return false, nil - } - - eraseCur = eraseCur || eraseOld - for _, e := range newExprs { - if !g.Insert(e) { - continue - } - // If the new Group expression is successfully inserted into the - // current Group, mark the Group as unexplored to enable the exploration - // on the new Group expressions. - g.SetUnexplored(round) - } - } - } - return eraseCur, nil -} - -// fillGroupStats computes Stats property for each Group recursively. -func (opt *Optimizer) fillGroupStats(g *memo.Group) (err error) { - if g.Prop.Stats != nil { - return nil - } - // All GroupExpr in a Group should share same LogicalProperty, so just use - // first one to compute Stats property. - elem := g.Equivalents.Front() - expr := elem.Value.(*memo.GroupExpr) - childStats := make([]*property.StatsInfo, len(expr.Children)) - childSchema := make([]*expression.Schema, len(expr.Children)) - for i, childGroup := range expr.Children { - err = opt.fillGroupStats(childGroup) - if err != nil { - return err - } - childStats[i] = childGroup.Prop.Stats - childSchema[i] = childGroup.Prop.Schema - } - planNode := expr.ExprNode - g.Prop.Stats, err = planNode.DeriveStats(childStats, g.Prop.Schema, childSchema, nil) - return err -} - -// onPhaseImplementation starts implementation physical operators from given root Group. -func (opt *Optimizer) onPhaseImplementation(_ sessionctx.Context, g *memo.Group) (plannercore.PhysicalPlan, float64, error) { - prop := &property.PhysicalProperty{ - ExpectedCnt: math.MaxFloat64, - } - preparePossibleProperties(g, make(map[*memo.Group][][]*expression.Column)) - // TODO replace MaxFloat64 costLimit by variable from sctx, or other sources. - impl, err := opt.implGroup(g, prop, math.MaxFloat64) - if err != nil { - return nil, 0, err - } - if impl == nil { - return nil, 0, plannercore.ErrInternal.GenWithStackByArgs("Can't find a proper physical plan for this query") - } - return impl.GetPlan(), impl.GetCost(), nil -} - -// implGroup finds the best Implementation which satisfies the required -// physical property for a Group. The best Implementation should have the -// lowest cost among all the applicable Implementations. -// -// g: the Group to be implemented. -// reqPhysProp: the required physical property. -// costLimit: the maximum cost of all the Implementations. -func (opt *Optimizer) implGroup(g *memo.Group, reqPhysProp *property.PhysicalProperty, costLimit float64) (memo.Implementation, error) { - groupImpl := g.GetImpl(reqPhysProp) - if groupImpl != nil { - if groupImpl.GetCost() <= costLimit { - return groupImpl, nil - } - return nil, nil - } - // Handle implementation rules for each equivalent GroupExpr. - var childImpls []memo.Implementation - err := opt.fillGroupStats(g) - if err != nil { - return nil, err - } - outCount := math.Min(g.Prop.Stats.RowCount, reqPhysProp.ExpectedCnt) - for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { - curExpr := elem.Value.(*memo.GroupExpr) - impls, err := opt.implGroupExpr(curExpr, reqPhysProp) - if err != nil { - return nil, err - } - for _, impl := range impls { - childImpls = childImpls[:0] - for i, childGroup := range curExpr.Children { - childImpl, err := opt.implGroup(childGroup, impl.GetPlan().GetChildReqProps(i), impl.GetCostLimit(costLimit, childImpls...)) - if err != nil { - return nil, err - } - if childImpl == nil { - impl.SetCost(math.MaxFloat64) - break - } - childImpls = append(childImpls, childImpl) - } - if impl.GetCost() == math.MaxFloat64 { - continue - } - implCost := impl.CalcCost(outCount, childImpls...) - if implCost > costLimit { - continue - } - if groupImpl == nil || groupImpl.GetCost() > implCost { - groupImpl = impl.AttachChildren(childImpls...) - costLimit = implCost - } - } - } - // Handle enforcer rules for required physical property. - for _, rule := range GetEnforcerRules(g, reqPhysProp) { - newReqPhysProp := rule.NewProperty(reqPhysProp) - enforceCost := rule.GetEnforceCost(g) - childImpl, err := opt.implGroup(g, newReqPhysProp, costLimit-enforceCost) - if err != nil { - return nil, err - } - if childImpl == nil { - continue - } - impl := rule.OnEnforce(reqPhysProp, childImpl) - implCost := enforceCost + childImpl.GetCost() - impl.SetCost(implCost) - if groupImpl == nil || groupImpl.GetCost() > implCost { - groupImpl = impl - costLimit = implCost - } - } - if groupImpl == nil || groupImpl.GetCost() == math.MaxFloat64 { - return nil, nil - } - g.InsertImpl(reqPhysProp, groupImpl) - return groupImpl, nil -} - -func (opt *Optimizer) implGroupExpr(cur *memo.GroupExpr, reqPhysProp *property.PhysicalProperty) (impls []memo.Implementation, err error) { - for _, rule := range opt.GetImplementationRules(cur.ExprNode) { - if !rule.Match(cur, reqPhysProp) { - continue - } - curImpls, err := rule.OnImplement(cur, reqPhysProp) - if err != nil { - return nil, err - } - impls = append(impls, curImpls...) - } - return impls, nil -} - -// preparePossibleProperties recursively calls LogicalPlan PreparePossibleProperties -// interface. It will fulfill the the possible properties fields of LogicalAggregation -// and LogicalJoin. -func preparePossibleProperties(g *memo.Group, propertyMap map[*memo.Group][][]*expression.Column) [][]*expression.Column { - if prop, ok := propertyMap[g]; ok { - return prop - } - groupPropertyMap := make(map[string][]*expression.Column) - for elem := g.Equivalents.Front(); elem != nil; elem = elem.Next() { - expr := elem.Value.(*memo.GroupExpr) - childrenProperties := make([][][]*expression.Column, len(expr.Children)) - for i, child := range expr.Children { - childrenProperties[i] = preparePossibleProperties(child, propertyMap) - } - exprProperties := expr.ExprNode.PreparePossibleProperties(expr.Schema(), childrenProperties...) - for _, newPropCols := range exprProperties { - // Check if the prop has already been in `groupPropertyMap`. - newProp := property.PhysicalProperty{SortItems: property.SortItemsFromCols(newPropCols, true)} - key := newProp.HashCode() - if _, ok := groupPropertyMap[string(key)]; !ok { - groupPropertyMap[string(key)] = newPropCols - } - } - } - resultProps := make([][]*expression.Column, 0, len(groupPropertyMap)) - for _, prop := range groupPropertyMap { - resultProps = append(resultProps, prop) - } - propertyMap[g] = resultProps - return resultProps -} diff --git a/planner/cascades/optimize_test.go b/planner/cascades/optimize_test.go deleted file mode 100644 index 55727427e132e..0000000000000 --- a/planner/cascades/optimize_test.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package cascades - -import ( - "context" - "math" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/planner/property" - "github.com/stretchr/testify/require" -) - -func TestImplGroupZeroCost(t *testing.T) { - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - - stmt, err := p.ParseOneStmt("select t1.a, t2.a from t as t1 left join t as t2 on t1.a = t2.a where t1.a < 1.0", "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - rootGroup := memo.Convert2Group(logic) - prop := &property.PhysicalProperty{ - ExpectedCnt: math.MaxFloat64, - } - impl, err := NewOptimizer().implGroup(rootGroup, prop, 0.0) - require.NoError(t, err) - require.Nil(t, impl) -} - -func TestInitGroupSchema(t *testing.T) { - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - - stmt, err := p.ParseOneStmt("select a from t", "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - g := memo.Convert2Group(logic) - require.NotNil(t, g) - require.NotNil(t, g.Prop) - require.Equal(t, 1, g.Prop.Schema.Len()) - require.Nil(t, g.Prop.Stats) -} - -func TestFillGroupStats(t *testing.T) { - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - - stmt, err := p.ParseOneStmt("select * from t t1 join t t2 on t1.a = t2.a", "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - rootGroup := memo.Convert2Group(logic) - err = NewOptimizer().fillGroupStats(rootGroup) - require.NoError(t, err) - require.NotNil(t, rootGroup.Prop.Stats) -} - -func TestPreparePossibleProperties(t *testing.T) { - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - optimizer := NewOptimizer() - - optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ - memo.OperandDataSource: { - NewRuleEnumeratePaths(), - }, - }) - defer func() { - optimizer.ResetTransformationRules(DefaultRuleBatches...) - }() - - stmt, err := p.ParseOneStmt("select f, sum(a) from t group by f", "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - logic, err = optimizer.onPhasePreprocessing(ctx, logic) - require.NoError(t, err) - - // collect the target columns: f, a - ds, ok := logic.Children()[0].Children()[0].(*plannercore.DataSource) - require.True(t, ok) - - var columnF, columnA *expression.Column - for i, col := range ds.Columns { - if col.Name.L == "f" { - columnF = ds.Schema().Columns[i] - } else if col.Name.L == "a" { - columnA = ds.Schema().Columns[i] - } - } - require.NotNil(t, columnF) - require.NotNil(t, columnA) - - agg, ok := logic.Children()[0].(*plannercore.LogicalAggregation) - require.True(t, ok) - - group := memo.Convert2Group(agg) - require.NoError(t, optimizer.onPhaseExploration(ctx, group)) - - // The memo looks like this: - // Group#0 Schema:[Column#13,test.t.f] - // Aggregation_2 input:[Group#1], group by:test.t.f, funcs:sum(test.t.a), firstrow(test.t.f) - // Group#1 Schema:[test.t.a,test.t.f] - // TiKVSingleGather_5 input:[Group#2], table:t - // TiKVSingleGather_9 input:[Group#3], table:t, index:f_g - // TiKVSingleGather_7 input:[Group#4], table:t, index:f - // Group#2 Schema:[test.t.a,test.t.f] - // TableScan_4 table:t, pk col:test.t.a - // Group#3 Schema:[test.t.a,test.t.f] - // IndexScan_8 table:t, index:f, g - // Group#4 Schema:[test.t.a,test.t.f] - // IndexScan_6 table:t, index:f - propMap := make(map[*memo.Group][][]*expression.Column) - aggProp := preparePossibleProperties(group, propMap) - // We only have one prop for Group0 : f - require.Len(t, aggProp, 1) - require.True(t, aggProp[0][0].Equal(nil, columnF)) - - gatherGroup := group.Equivalents.Front().Value.(*memo.GroupExpr).Children[0] - gatherProp, ok := propMap[gatherGroup] - require.True(t, ok) - // We have 2 props for Group1: [f], [a] - require.Len(t, gatherProp, 2) - for _, prop := range gatherProp { - require.Len(t, prop, 1) - require.True(t, prop[0].Equal(nil, columnA) || prop[0].Equal(nil, columnF)) - } -} - -// fakeTransformation is used for TestAppliedRuleSet. -type fakeTransformation struct { - baseRule - appliedTimes int -} - -// OnTransform implements Transformation interface. -func (rule *fakeTransformation) OnTransform(old *memo.ExprIter) (newExprs []*memo.GroupExpr, eraseOld bool, eraseAll bool, err error) { - rule.appliedTimes++ - old.GetExpr().AddAppliedRule(rule) - return []*memo.GroupExpr{old.GetExpr()}, true, false, nil -} - -func TestAppliedRuleSet(t *testing.T) { - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - optimizer := NewOptimizer() - - rule := fakeTransformation{} - rule.pattern = memo.NewPattern(memo.OperandProjection, memo.EngineAll) - optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ - memo.OperandProjection: { - &rule, - }, - }) - defer func() { - optimizer.ResetTransformationRules(DefaultRuleBatches...) - }() - - stmt, err := p.ParseOneStmt("select 1", "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - group := memo.Convert2Group(logic) - require.NoError(t, optimizer.onPhaseExploration(ctx, group)) - require.Equal(t, 1, rule.appliedTimes) -} diff --git a/planner/cascades/stringer.go b/planner/cascades/stringer.go deleted file mode 100644 index 99638eaaebe36..0000000000000 --- a/planner/cascades/stringer.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package cascades - -import ( - "bytes" - "fmt" - "strings" - - "github.com/pingcap/tidb/planner/memo" -) - -// ToString stringifies a Group Tree. -func ToString(g *memo.Group) []string { - idMap := make(map[*memo.Group]int) - idMap[g] = 0 - return toString(g, idMap, map[*memo.Group]struct{}{}, []string{}) -} - -// toString recursively stringifies a Group Tree using a preorder traversal method. -func toString(g *memo.Group, idMap map[*memo.Group]int, visited map[*memo.Group]struct{}, strs []string) []string { - if _, exists := visited[g]; exists { - return strs - } - visited[g] = struct{}{} - // Add new Groups to idMap. - for item := g.Equivalents.Front(); item != nil; item = item.Next() { - expr := item.Value.(*memo.GroupExpr) - for _, childGroup := range expr.Children { - if _, exists := idMap[childGroup]; !exists { - idMap[childGroup] = len(idMap) - } - } - } - // Visit self first. - strs = append(strs, groupToString(g, idMap)...) - // Visit children then. - for item := g.Equivalents.Front(); item != nil; item = item.Next() { - expr := item.Value.(*memo.GroupExpr) - for _, childGroup := range expr.Children { - strs = toString(childGroup, idMap, visited, strs) - } - } - return strs -} - -// groupToString only stringifies a single Group. -// Format: -// Group#1 Column: [Column#1,Column#2,Column#13] Unique key: [] -// -// Selection_4 input:[Group#2], eq(Column#13, Column#2), gt(Column#1, 10) -// Projection_15 input:Group#3 Column#1, Column#2 -func groupToString(g *memo.Group, idMap map[*memo.Group]int) []string { - schema := g.Prop.Schema - colStrs := make([]string, 0, len(schema.Columns)) - for _, col := range schema.Columns { - colStrs = append(colStrs, col.String()) - } - - groupLine := bytes.NewBufferString("") - fmt.Fprintf(groupLine, "Group#%d Schema:[%s]", idMap[g], strings.Join(colStrs, ",")) - - if len(g.Prop.Schema.Keys) > 0 { - ukStrs := make([]string, 0, len(schema.Keys)) - for _, key := range schema.Keys { - ukColStrs := make([]string, 0, len(key)) - for _, col := range key { - ukColStrs = append(ukColStrs, col.String()) - } - ukStrs = append(ukStrs, strings.Join(ukColStrs, ",")) - } - fmt.Fprintf(groupLine, ", UniqueKey:[%s]", strings.Join(ukStrs, ",")) - } - - result := make([]string, 0, g.Equivalents.Len()+1) - result = append(result, groupLine.String()) - for item := g.Equivalents.Front(); item != nil; item = item.Next() { - expr := item.Value.(*memo.GroupExpr) - result = append(result, " "+groupExprToString(expr, idMap)) - } - return result -} - -// groupExprToString stringifies a groupExpr(or a LogicalPlan). -// Format: -// Selection_13 input:Group#2 gt(Column#1, Column#4) -func groupExprToString(expr *memo.GroupExpr, idMap map[*memo.Group]int) string { - buffer := bytes.NewBufferString(expr.ExprNode.ExplainID().String()) - if len(expr.Children) == 0 { - fmt.Fprintf(buffer, " %s", expr.ExprNode.ExplainInfo()) - } else { - fmt.Fprintf(buffer, " %s", getChildrenGroupID(expr, idMap)) - explainInfo := expr.ExprNode.ExplainInfo() - if len(explainInfo) != 0 { - fmt.Fprintf(buffer, ", %s", explainInfo) - } - } - return buffer.String() -} - -func getChildrenGroupID(expr *memo.GroupExpr, idMap map[*memo.Group]int) string { - children := make([]string, 0, len(expr.Children)) - for _, child := range expr.Children { - children = append(children, fmt.Sprintf("Group#%d", idMap[child])) - } - return "input:[" + strings.Join(children, ",") + "]" -} diff --git a/planner/cascades/stringer_test.go b/planner/cascades/stringer_test.go deleted file mode 100644 index 5fe587b890518..0000000000000 --- a/planner/cascades/stringer_test.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package cascades - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func TestGroupStringer(t *testing.T) { - optimizer := NewOptimizer() - optimizer.ResetTransformationRules(map[memo.Operand][]Transformation{ - memo.OperandSelection: { - NewRulePushSelDownTiKVSingleGather(), - NewRulePushSelDownTableScan(), - }, - memo.OperandDataSource: { - NewRuleEnumeratePaths(), - }, - }) - defer func() { - optimizer.ResetTransformationRules(DefaultRuleBatches...) - }() - - var input []string - var output []struct { - SQL string - Result []string - } - stringerSuiteData.LoadTestCases(t, &input, &output) - - p := parser.New() - ctx := plannercore.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - is := infoschema.MockInfoSchema([]*model.TableInfo{plannercore.MockSignedTable()}) - domain.GetDomain(ctx).MockInfoCacheAndLoadInfoSchema(is) - for i, sql := range input { - stmt, err := p.ParseOneStmt(sql, "", "") - require.NoError(t, err) - - plan, _, err := plannercore.BuildLogicalPlanForTest(context.Background(), ctx, stmt, is) - require.NoError(t, err) - - logic, ok := plan.(plannercore.LogicalPlan) - require.True(t, ok) - - logic, err = optimizer.onPhasePreprocessing(ctx, logic) - require.NoError(t, err) - - group := memo.Convert2Group(logic) - require.NoError(t, optimizer.onPhaseExploration(ctx, group)) - - group.BuildKeyInfo() - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Result = ToString(group) - }) - require.Equalf(t, output[i].Result, ToString(group), "case:%v, sql:%s", i, sql) - } -} diff --git a/planner/core/BUILD.bazel b/planner/core/BUILD.bazel deleted file mode 100644 index ef995ac6a95be..0000000000000 --- a/planner/core/BUILD.bazel +++ /dev/null @@ -1,289 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "core", - srcs = [ - "access_object.go", - "collect_column_stats_usage.go", - "common_plans.go", - "debugtrace.go", - "encode.go", - "errors.go", - "exhaust_physical_plans.go", - "explain.go", - "expression_rewriter.go", - "find_best_task.go", - "flat_plan.go", - "foreign_key.go", - "fragment.go", - "handle_cols.go", - "hashcode.go", - "hints.go", - "indexmerge_path.go", - "initialize.go", - "logical_plan_builder.go", - "logical_plans.go", - "memtable_predicate_extractor.go", - "mock.go", - "optimizer.go", - "partition_prune.go", - "pb_to_plan.go", - "physical_plans.go", - "plan.go", - "plan_cache.go", - "plan_cache_lru.go", - "plan_cache_param.go", - "plan_cache_utils.go", - "plan_cacheable_checker.go", - "plan_cost_detail.go", - "plan_cost_ver1.go", - "plan_cost_ver2.go", - "plan_stats.go", - "plan_to_pb.go", - "planbuilder.go", - "point_get_plan.go", - "preprocess.go", - "property_cols_prune.go", - "resolve_indices.go", - "rule_aggregation_elimination.go", - "rule_aggregation_push_down.go", - "rule_aggregation_skew_rewrite.go", - "rule_build_key_info.go", - "rule_column_pruning.go", - "rule_constant_propagation.go", - "rule_decorrelate.go", - "rule_derive_topn_from_window.go", - "rule_eliminate_projection.go", - "rule_generate_column_substitute.go", - "rule_inject_extra_projection.go", - "rule_join_elimination.go", - "rule_join_reorder.go", - "rule_join_reorder_dp.go", - "rule_join_reorder_greedy.go", - "rule_max_min_eliminate.go", - "rule_partition_processor.go", - "rule_predicate_push_down.go", - "rule_predicate_simplification.go", - "rule_push_down_sequence.go", - "rule_resolve_grouping_expand.go", - "rule_result_reorder.go", - "rule_semi_join_rewrite.go", - "rule_topn_push_down.go", - "runtime_filter.go", - "runtime_filter_generator.go", - "scalar_subq_expression.go", - "show_predicate_extractor.go", - "stats.go", - "stringer.go", - "task.go", - "telemetry.go", - "tiflash_selection_late_materialization.go", - "trace.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/planner/core", - visibility = ["//visibility:public"], - deps = [ - "//bindinfo", - "//br/pkg/storage", - "//config", - "//distsql", - "//domain", - "//errno", - "//expression", - "//expression/aggregation", - "//infoschema", - "//kv", - "//lock", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/types", - "//planner/cardinality", - "//planner/core/internal", - "//planner/core/internal/base", - "//planner/core/metrics", - "//planner/funcdep", - "//planner/property", - "//planner/util", - "//planner/util/debugtrace", - "//planner/util/fixcontrol", - "//privilege", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/staleread", - "//statistics", - "//table", - "//table/tables", - "//table/temptable", - "//tablecodec", - "//telemetry", - "//types", - "//types/parser_driver", - "//util", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/dbterror/exeerrors", - "//util/disjointset", - "//util/domainutil", - "//util/execdetails", - "//util/filter", - "//util/hack", - "//util/hint", - "//util/intest", - "//util/kvcache", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/mock", - "//util/paging", - "//util/parser", - "//util/plancache", - "//util/plancodec", - "//util/ranger", - "//util/rowcodec", - "//util/sem", - "//util/set", - "//util/size", - "//util/sqlexec", - "//util/stmtsummary", - "//util/stringutil", - "//util/syncutil", - "//util/texttree", - "//util/tiflashcompute", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/diagnosticspb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "core_test", - timeout = "short", - srcs = [ - "binary_plan_test.go", - "cbo_test.go", - "collect_column_stats_usage_test.go", - "common_plans_test.go", - "enforce_mpp_test.go", - "errors_test.go", - "exhaust_physical_plans_test.go", - "expression_rewriter_test.go", - "expression_test.go", - "find_best_task_test.go", - "fragment_test.go", - "indexmerge_intersection_test.go", - "indexmerge_path_test.go", - "indexmerge_test.go", - "integration_partition_test.go", - "integration_test.go", - "logical_plan_trace_test.go", - "logical_plans_test.go", - "main_test.go", - "memtable_predicate_extractor_test.go", - "optimizer_test.go", - "partition_pruning_test.go", - "physical_plan_test.go", - "physical_plan_trace_test.go", - "plan_cache_lru_test.go", - "plan_cache_param_test.go", - "plan_cache_test.go", - "plan_cacheable_checker_test.go", - "plan_cost_detail_test.go", - "plan_cost_ver1_test.go", - "plan_cost_ver2_test.go", - "plan_replayer_capture_test.go", - "plan_test.go", - "plan_to_pb_test.go", - "planbuilder_test.go", - "point_get_plan_test.go", - "preprocess_test.go", - "rule_generate_column_substitute_test.go", - "rule_join_reorder_dp_test.go", - "rule_join_reorder_test.go", - "runtime_filter_generator_test.go", - "stringer_test.go", - ], - data = glob(["testdata/**"]), - embed = [":core"], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//domain", - "//errno", - "//expression", - "//expression/aggregation", - "//infoschema", - "//kv", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner", - "//planner/core/internal", - "//planner/property", - "//planner/util", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn", - "//statistics", - "//table", - "//testkit", - "//testkit/ddlhelper", - "//testkit/external", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//types/parser_driver", - "//util", - "//util/benchdaily", - "//util/collate", - "//util/dbterror", - "//util/hack", - "//util/hint", - "//util/kvcache", - "//util/logutil", - "//util/mock", - "//util/plancache", - "//util/plancodec", - "//util/ranger", - "//util/set", - "//util/stmtsummary", - "//util/tracing", - "@com_github_golang_snappy//:snappy", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/binary_plan_test.go b/planner/core/binary_plan_test.go deleted file mode 100644 index 65548581451fb..0000000000000 --- a/planner/core/binary_plan_test.go +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "encoding/base64" - "fmt" - "io" - "os" - "strings" - "testing" - - "github.com/golang/snappy" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/stmtsummary" - "github.com/pingcap/tipb/go-tipb" - "github.com/stretchr/testify/require" -) - -func TestBinaryPlanSwitch(t *testing.T) { - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - - origin := tk.MustQuery("SELECT @@global.tidb_generate_binary_plan") - originStr := origin.Rows()[0][0].(string) - defer func() { - tk.MustExec("set @@global.tidb_generate_binary_plan = '" + originStr + "'") - }() - - tk.MustExec("use test") - // 1. assert binary plan is generated if the variable is turned on - tk.MustExec("set global tidb_generate_binary_plan = 1") - tk.MustQuery("select sleep(1)") - - result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query like "%select sleep(1)%" and query not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s := result[0] - b, err := base64.StdEncoding.DecodeString(s) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary := &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + - `where QUERY_SAMPLE_TEXT like "%select sleep(1)%" and QUERY_SAMPLE_TEXT not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s = result[0] - b, err = base64.StdEncoding.DecodeString(s) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary = &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - - // 2. assert binary plan is not generated if the variable is turned off - tk.MustExec("set global tidb_generate_binary_plan = 0") - tk.MustQuery("select 1 > sleep(1)") - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query like "%select 1 > sleep(1)%" and query not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s = result[0] - require.Empty(t, s) - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + - `where QUERY_SAMPLE_TEXT like "%select 1 > sleep(1)%" and QUERY_SAMPLE_TEXT not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s = result[0] - require.Empty(t, s) -} - -// TestTooLongBinaryPlan asserts that if the binary plan is larger than 1024*1024 bytes, it should be output to slow query but not to stmt summary. -func TestTooLongBinaryPlan(t *testing.T) { - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - - origin := tk.MustQuery("SELECT @@global.tidb_enable_stmt_summary") - originStr := origin.Rows()[0][0].(string) - defer func() { - tk.MustExec("set @@global.tidb_enable_stmt_summary = '" + originStr + "'") - }() - - // Trigger clear the stmt summary in memory to prevent this case from being affected by other cases. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - tk.MustExec("use test") - tk.MustExec("drop table if exists th") - tk.MustExec("set @@session.tidb_enable_table_partition = 1") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") - tk.MustQuery("select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i") - - result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query like "%th t1 join th t2 join th t3%" and query not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s := result[0] - require.Greater(t, len(s), stmtsummary.MaxEncodedPlanSizeInBytes) - b, err := base64.StdEncoding.DecodeString(s) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary := &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - require.False(t, binary.DiscardedDueToTooLong) - require.True(t, binary.WithRuntimeStats) - require.NotNil(t, binary.Main) - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + - `where QUERY_SAMPLE_TEXT like "%th t1 join th t2 join th t3%" and QUERY_SAMPLE_TEXT not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s = result[0] - b, err = base64.StdEncoding.DecodeString(s) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary = &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - require.True(t, binary.DiscardedDueToTooLong) - require.Nil(t, binary.Main) - require.Nil(t, binary.Ctes) -} - -// TestLongBinaryPlan asserts that if the binary plan is smaller than 1024*1024 bytes, it should be output to both slow query and stmt summary. -func TestLongBinaryPlan(t *testing.T) { - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - - tk.MustExec("use test") - - tk.MustExec("drop table if exists th") - tk.MustExec("set @@session.tidb_enable_table_partition = 1") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 1000;") - tk.MustQuery("select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i") - - result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query like "%th t1 join th t2 join th t3%" and query not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s1 := result[0] - // The binary plan in this test case is expected to be smaller than MaxEncodedPlanSizeInBytes. - // If the size of the binary plan changed and this case failed in the future, you can adjust the partition numbers in the CREATE TABLE statement above. - require.Less(t, len(s1), stmtsummary.MaxEncodedPlanSizeInBytes) - b, err := base64.StdEncoding.DecodeString(s1) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary := &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - require.False(t, binary.DiscardedDueToTooLong) - require.True(t, binary.WithRuntimeStats) - require.NotNil(t, binary.Main) - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + - `where QUERY_SAMPLE_TEXT like "%th t1 join th t2 join th t3%" and QUERY_SAMPLE_TEXT not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s2 := result[0] - require.Equal(t, s1, s2) -} - -func TestBinaryPlanOfPreparedStmt(t *testing.T) { - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int);") - tk.MustExec("insert into t value(30,30);") - tk.MustExec(`prepare stmt from "select sleep(1), b from t where a > ?"`) - tk.MustExec("set @a = 20") - tk.MustQuery("execute stmt using @a") - - result := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query like "%select sleep%" and query not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s1 := result[0] - b, err := base64.StdEncoding.DecodeString(s1) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary := &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - require.False(t, binary.DiscardedDueToTooLong) - require.True(t, binary.WithRuntimeStats) - require.NotNil(t, binary.Main) - - result = testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.statements_summary " + - `where QUERY_SAMPLE_TEXT like "%select sleep%" and QUERY_SAMPLE_TEXT not like "%like%" ` + - "limit 1;").Rows()) - require.Len(t, result, 1) - s2 := result[0] - require.Equal(t, s1, s2) -} - -// TestDecodeBinaryPlan asserts that the result of EXPLAIN ANALYZE FORMAT = 'verbose' is the same as tidb_decode_binary_plan(). -func TestDecodeBinaryPlan(t *testing.T) { - // Prepare the slow log - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - tk.MustExec("set tidb_slow_log_threshold=0;") - defer func() { - tk.MustExec("set tidb_slow_log_threshold=300;") - }() - tk.MustExec(`create table tp (a int, b int) partition by range(a) ( - partition p0 values less than (100), - partition p1 values less than (200), - partition p2 values less than (300), - partition p3 values less than maxvalue - )`) - tk.MustExec("insert into tp value(1,1), (10,10), (123,234), (213, 234);") - tk.MustExec("create table t(a int, b int, c int, index ia(a));") - tk.MustExec("insert into t value(1,1,1), (10,10,10), (123,234,345), (-213, -234, -234);") - cases := []string{ - "explain analyze format = 'verbose' select * from t", - "explain analyze format = 'verbose' select * from t where a > 10", - "explain analyze format = 'verbose' select /*+ inl_join(t1) */ * from t t1 join t t2 where t1.a = t2.a", - "explain analyze format = 'verbose' WITH RECURSIVE cte(n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", - "set @@tidb_partition_prune_mode='static'", - "explain analyze format = 'verbose' select * from tp", - "explain analyze format = 'verbose' select * from tp t1 join tp t2 on t1.b > t2.b", - "explain analyze format = 'verbose' select * from tp where a > 400", - "explain analyze format = 'verbose' select * from tp where a < 30", - "explain analyze format = 'verbose' select * from tp where a > 0", - "set @@tidb_partition_prune_mode='dynamic'", - "explain analyze format = 'verbose' select * from tp", - "explain analyze format = 'verbose' select * from tp t1 join tp t2 on t1.b > t2.b", - "explain analyze format = 'verbose' select * from tp where a > 400", - "explain analyze format = 'verbose' select * from tp where a < 30", - "explain analyze format = 'verbose' select * from tp where a > 0", - } - - for _, c := range cases { - if len(c) < 7 || c[:7] != "explain" { - tk.MustExec(c) - continue - } - comment := fmt.Sprintf("sql:%s", c) - - var res1, res2 []string - - explainResult := tk.MustQuery(c).Rows() - for _, row := range explainResult { - for _, val := range row { - str := val.(string) - str = strings.TrimSpace(str) - if len(str) > 0 { - res1 = append(res1, str) - } - } - } - - slowLogResult := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query = "` + c + `;" ` + - "order by time desc limit 1").Rows()) - require.Lenf(t, slowLogResult, 1, comment) - decoded := testdata.ConvertRowsToStrings(tk.MustQuery(`select tidb_decode_binary_plan('` + slowLogResult[0] + `')`).Rows())[0] - decodedRows := strings.Split(decoded, "\n") - // remove the first newline and the title row - decodedRows = decodedRows[2:] - for _, decodedRow := range decodedRows { - vals := strings.Split(decodedRow, "|") - for _, val := range vals { - val = strings.TrimSpace(val) - if len(val) > 0 { - res2 = append(res2, val) - } - } - } - - require.Equalf(t, res1, res2, comment) - } -} - -func TestInvalidDecodeBinaryPlan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - str1 := "some random bytes" - str2 := base64.StdEncoding.EncodeToString([]byte(str1)) - str3 := base64.StdEncoding.EncodeToString(snappy.Encode(nil, []byte(str1))) - - tk.MustQuery(`select tidb_decode_binary_plan('` + str1 + `')`).Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 illegal base64 data at input byte 4")) - tk.MustQuery(`select tidb_decode_binary_plan('` + str2 + `')`).Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 snappy: corrupt input")) - tk.MustQuery(`select tidb_decode_binary_plan('` + str3 + `')`).Check(testkit.Rows("")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 proto: illegal wireType 7")) -} - -func TestUnnecessaryBinaryPlanInSlowLog(t *testing.T) { - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - - origin := tk.MustQuery("SELECT @@global.tidb_slow_log_threshold") - originStr := origin.Rows()[0][0].(string) - defer func() { - tk.MustExec("set @@global.tidb_slow_log_threshold = '" + originStr + "'") - }() - - tk.MustExec("use test") - tk.MustExec("drop table if exists th") - tk.MustExec("set global tidb_slow_log_threshold = 1;") - tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 100;") - slowLogBytes, err := io.ReadAll(f) - require.NoError(t, err) - require.NotContains(t, string(slowLogBytes), `tidb_decode_binary_plan('')`) -} diff --git a/planner/core/casetest/BUILD.bazel b/planner/core/casetest/BUILD.bazel deleted file mode 100644 index 09f18249bbaca..0000000000000 --- a/planner/core/casetest/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "casetest_test", - timeout = "moderate", - srcs = [ - "integration_test.go", - "main_test.go", - "plan_test.go", - "stats_test.go", - "tiflash_selection_late_materialization_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 19, - deps = [ - "//domain", - "//parser", - "//parser/model", - "//planner/core", - "//planner/property", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util/hint", - "//util/plancodec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/binaryplan/BUILD.bazel b/planner/core/casetest/binaryplan/BUILD.bazel deleted file mode 100644 index 7d08c6c0b6b43..0000000000000 --- a/planner/core/casetest/binaryplan/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "binaryplan_test", - timeout = "short", - srcs = [ - "binary_plan_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - deps = [ - "//config", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util/logutil", - "@com_github_golang_snappy//:snappy", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/binaryplan/binary_plan_test.go b/planner/core/casetest/binaryplan/binary_plan_test.go deleted file mode 100644 index f830e104e3eed..0000000000000 --- a/planner/core/casetest/binaryplan/binary_plan_test.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package binaryplan - -import ( - "encoding/base64" - "fmt" - "os" - "regexp" - "testing" - - "github.com/golang/snappy" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tipb/go-tipb" - "github.com/stretchr/testify/require" -) - -func simplifyAndCheckBinaryOperator(t *testing.T, pb *tipb.ExplainOperator, withRuntimeStats bool) { - if withRuntimeStats { - if pb.TaskType == tipb.TaskType_root { - require.NotEmpty(t, pb.RootBasicExecInfo) - } else if pb.TaskType != tipb.TaskType_unknown { - require.NotEmpty(t, pb.CopExecInfo) - } - } - pb.RootBasicExecInfo = "" - pb.RootGroupExecInfo = nil - pb.CopExecInfo = "" - match, err := regexp.MatchString("((Table|Index).*Scan)|CTEFullScan|Point_Get", pb.Name) - if err == nil && match { - require.NotNil(t, pb.AccessObjects) - } - // AccessObject field is an interface and json.Unmarshall can't handle it, so we don't check it against the json output. - pb.AccessObjects = nil - // MemoryBytes and DiskBytes are not stable sometimes. - pb.MemoryBytes = 0 - pb.DiskBytes = 0 - if len(pb.Children) > 0 { - for _, op := range pb.Children { - if op != nil { - simplifyAndCheckBinaryOperator(t, op, withRuntimeStats) - } - } - } -} - -func simplifyAndCheckBinaryPlan(t *testing.T, pb *tipb.ExplainData) { - if pb.Main != nil { - simplifyAndCheckBinaryOperator(t, pb.Main, pb.WithRuntimeStats) - } - for _, cte := range pb.Ctes { - if cte != nil { - simplifyAndCheckBinaryOperator(t, cte, pb.WithRuntimeStats) - } - } -} -func TestBinaryPlanInExplainAndSlowLog(t *testing.T) { - // Prepare the slow log - originCfg := config.GetGlobalConfig() - newCfg := *originCfg - f, err := os.CreateTemp("", "tidb-slow-*.log") - require.NoError(t, err) - newCfg.Log.SlowQueryFile = f.Name() - config.StoreGlobalConfig(&newCfg) - defer func() { - config.StoreGlobalConfig(originCfg) - require.NoError(t, f.Close()) - require.NoError(t, os.Remove(newCfg.Log.SlowQueryFile)) - }() - require.NoError(t, logutil.InitLogger(newCfg.Log.ToLogConfig())) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - // If we don't set this, it will be false sometimes and the cost in the result will be different. - tk.MustExec("set @@tidb_enable_chunk_rpc=true") - tk.MustExec(fmt.Sprintf("set @@tidb_slow_query_file='%v'", f.Name())) - tk.MustExec("set tidb_slow_log_threshold=0;") - defer func() { - tk.MustExec("set tidb_slow_log_threshold=300;") - }() - - var input []string - var output []struct { - SQL string - BinaryPlan *tipb.ExplainData - } - planSuiteData := GetBinaryPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - if len(test) < 7 || test[:7] != "explain" { - tk.MustExec(test) - testdata.OnRecord(func() { - output[i].SQL = test - output[i].BinaryPlan = nil - }) - continue - } - result := testdata.ConvertRowsToStrings(tk.MustQuery(test).Rows()) - require.Equal(t, len(result), 1, comment) - s := result[0] - - // assert that the binary plan in the slow log is the same as the result in the EXPLAIN statement - slowLogResult := testdata.ConvertRowsToStrings(tk.MustQuery("select binary_plan from information_schema.slow_query " + - `where query = "` + test + `;" ` + - "order by time desc limit 1").Rows()) - require.Lenf(t, slowLogResult, 1, comment) - require.Equal(t, s, slowLogResult[0], comment) - - b, err := base64.StdEncoding.DecodeString(s) - require.NoError(t, err) - b, err = snappy.Decode(nil, b) - require.NoError(t, err) - binary := &tipb.ExplainData{} - err = binary.Unmarshal(b) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = test - output[i].BinaryPlan = binary - }) - simplifyAndCheckBinaryPlan(t, binary) - require.Equal(t, output[i].BinaryPlan, binary) - } -} diff --git a/planner/core/casetest/binaryplan/main_test.go b/planner/core/casetest/binaryplan/main_test.go deleted file mode 100644 index e912a86d27734..0000000000000 --- a/planner/core/casetest/binaryplan/main_test.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package binaryplan - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "binary_plan_suite") - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetBinaryPlanSuiteData() testdata.TestData { - return testDataMap["binary_plan_suite"] -} diff --git a/planner/core/casetest/cbotest/BUILD.bazel b/planner/core/casetest/cbotest/BUILD.bazel deleted file mode 100644 index 3e46013a5f364..0000000000000 --- a/planner/core/casetest/cbotest/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "cbotest_test", - timeout = "short", - srcs = [ - "cbo_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - race = "on", - shard_count = 17, - deps = [ - "//domain", - "//executor", - "//parser/model", - "//planner", - "//planner/core", - "//session", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/storage", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/cbotest/cbo_test.go b/planner/core/casetest/cbotest/cbo_test.go deleted file mode 100644 index 19c16a219d40d..0000000000000 --- a/planner/core/casetest/cbotest/cbo_test.go +++ /dev/null @@ -1,633 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cbotest - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func loadTableStats(fileName string, dom *domain.Domain) error { - statsPath := filepath.Join("testdata", fileName) - bytes, err := os.ReadFile(statsPath) - if err != nil { - return err - } - statsTbl := &storage.JSONTable{} - err = json.Unmarshal(bytes, statsTbl) - if err != nil { - return err - } - statsHandle := dom.StatsHandle() - err = statsHandle.LoadStatsFromJSON(context.Background(), dom.InfoSchema(), statsTbl, 0) - if err != nil { - return err - } - return nil -} - -// TestCBOWithoutAnalyze tests the plan with stats that only have count info. -func TestCBOWithoutAnalyze(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t1 (a int)") - testKit.MustExec("create table t2 (a int)") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - testKit.MustExec("insert into t1 values (1), (2), (3), (4), (5), (6)") - testKit.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(dom.InfoSchema())) - var input []string - var output []struct { - SQL string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, sql := range input { - plan := testKit.MustQuery(sql) - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - }) - plan.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestStraightJoin(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - h := dom.StatsHandle() - for _, tblName := range []string{"t1", "t2", "t3", "t4"} { - testKit.MustExec(fmt.Sprintf("create table %s (a int)", tblName)) - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - } - var input []string - var output [][]string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) - }) - testKit.MustQuery(tt).Check(testkit.Rows(output[i]...)) - } -} - -func TestTableDual(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - testKit := testkit.NewTestKit(t, store) - testKit.MustExec(`use test`) - h := dom.StatsHandle() - testKit.MustExec(`create table t(a int)`) - testKit.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(dom.InfoSchema())) - var input []string - var output []struct { - SQL string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, sql := range input { - plan := testKit.MustQuery(sql) - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - }) - plan.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestEstimation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - statistics.RatioOfPseudoEstimate.Store(10.0) - defer statistics.RatioOfPseudoEstimate.Store(0.7) - testKit.MustExec("use test") - testKit.MustExec("set tidb_cost_model_version=2") - testKit.MustExec("create table t (a int)") - testKit.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)") - testKit.MustExec("insert into t select * from t") - testKit.MustExec("insert into t select * from t") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec("analyze table t") - for i := 1; i <= 8; i++ { - testKit.MustExec("delete from t where a = ?", i) - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(dom.InfoSchema())) - var input []string - var output []struct { - SQL string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, sql := range input { - plan := testKit.MustQuery(sql) - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - }) - plan.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIndexRead(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("set tidb_cost_model_version=2") - testKit.MustExec("set @@session.tidb_executor_concurrency = 4;") - testKit.MustExec("set @@session.tidb_hash_join_concurrency = 5;") - testKit.MustExec("set @@session.tidb_distsql_scan_concurrency = 15;") - - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t, t1") - testKit.MustExec("create table t (a int primary key, b int, c varchar(200), d datetime DEFAULT CURRENT_TIMESTAMP, e int, ts timestamp DEFAULT CURRENT_TIMESTAMP)") - testKit.MustExec("create index b on t (b)") - testKit.MustExec("create index d on t (d)") - testKit.MustExec("create index e on t (e)") - testKit.MustExec("create index b_c on t (b,c)") - testKit.MustExec("create index ts on t (ts)") - testKit.MustExec("create table t1 (a int, b int, index idx(a), index idxx(b))") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - testKit.MustExec("set @@tidb_enable_chunk_rpc = on") - - // This stats is generated by following format: - // fill (a, b, c, e) as (i*100+j, i, i+j, i*100+j), i and j is dependent and range of this two are [0, 99]. - require.NoError(t, loadTableStats("analyzesSuiteTestIndexReadT.json", dom)) - for i := 1; i < 16; i++ { - testKit.MustExec(fmt.Sprintf("insert into t1 values(%v, %v)", i, i)) - } - testKit.MustExec("analyze table t1") - ctx := testKit.Session() - var input, output []string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - - for i, tt := range input { - stmts, err := session.Parse(ctx, tt) - require.NoError(t, err) - require.Len(t, stmts, 1) - stmt := stmts[0] - ret := &core.PreprocessorReturn{} - err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) - require.NoError(t, err) - planString := core.ToString(p) - testdata.OnRecord(func() { - output[i] = planString - }) - require.Equalf(t, output[i], planString, "case: %v", tt) - } -} - -func TestEmptyTable(t *testing.T) { - store := testkit.CreateMockStore(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set tidb_cost_model_version=2") - testKit.MustExec("drop table if exists t, t1") - testKit.MustExec("create table t (c1 int)") - testKit.MustExec("create table t1 (c1 int)") - testKit.MustExec("analyze table t, t1") - var input, output []string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - ctx := testKit.Session() - stmts, err := session.Parse(ctx, tt) - require.NoError(t, err) - require.Len(t, stmts, 1) - stmt := stmts[0] - ret := &core.PreprocessorReturn{} - err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) - require.NoError(t, err) - planString := core.ToString(p) - testdata.OnRecord(func() { - output[i] = planString - }) - require.Equalf(t, output[i], planString, "case: %v", tt) - } -} - -func TestAnalyze(t *testing.T) { - store := testkit.CreateMockStore(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t, t1, t2, t3") - testKit.MustExec("create table t (a int, b int)") - testKit.MustExec("create index a on t (a)") - testKit.MustExec("create index b on t (b)") - testKit.MustExec("insert into t (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") - testKit.MustExec("analyze table t") - - testKit.MustExec("create table t1 (a int, b int)") - testKit.MustExec("create index a on t1 (a)") - testKit.MustExec("create index b on t1 (b)") - testKit.MustExec("insert into t1 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") - - testKit.MustExec("create table t2 (a int, b int)") - testKit.MustExec("create index a on t2 (a)") - testKit.MustExec("create index b on t2 (b)") - testKit.MustExec("insert into t2 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") - testKit.MustExec("analyze table t2 index a") - - testKit.MustExec("create table t3 (a int, b int)") - testKit.MustExec("create index a on t3 (a)") - - testKit.MustExec("set @@tidb_partition_prune_mode = 'static';") - testKit.MustExec("create table t4 (a int, b int) partition by range (a) (partition p1 values less than (2), partition p2 values less than (3))") - testKit.MustExec("create index a on t4 (a)") - testKit.MustExec("create index b on t4 (b)") - testKit.MustExec("insert into t4 (a,b) values (1,1),(1,2),(1,3),(1,4),(2,5),(2,6),(2,7),(2,8)") - testKit.MustExec("analyze table t4") - - testKit.MustExec("create view v as select * from t") - _, err := testKit.Exec("analyze table v") - require.EqualError(t, err, "analyze view v is not supported now") - testKit.MustExec("drop view v") - - testKit.MustExec("create sequence seq") - _, err = testKit.Exec("analyze table seq") - require.EqualError(t, err, "analyze sequence seq is not supported now") - testKit.MustExec("drop sequence seq") - - var input, output []string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - - for i, tt := range input { - ctx := testKit.Session() - stmts, err := session.Parse(ctx, tt) - require.NoError(t, err) - require.Len(t, stmts, 1) - stmt := stmts[0] - err = executor.ResetContextOfStmt(ctx, stmt) - require.NoError(t, err) - ret := &core.PreprocessorReturn{} - err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) - require.NoError(t, err) - planString := core.ToString(p) - testdata.OnRecord(func() { - output[i] = planString - }) - require.Equalf(t, output[i], planString, "case: %v", tt) - } -} - -func TestOutdatedAnalyze(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (a int, b int, index idx(a))") - for i := 0; i < 10; i++ { - testKit.MustExec(fmt.Sprintf("insert into t values (%d,%d)", i, i)) - } - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec("analyze table t") - testKit.MustExec("insert into t select * from t") - testKit.MustExec("insert into t select * from t") - testKit.MustExec("insert into t select * from t") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(dom.InfoSchema())) - var input []struct { - SQL string - EnablePseudoForOutdatedStats bool - RatioOfPseudoEstimate float64 - } - var output []struct { - SQL string - EnablePseudoForOutdatedStats bool - RatioOfPseudoEstimate float64 - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testKit.Session().GetSessionVars().SetEnablePseudoForOutdatedStats(tt.EnablePseudoForOutdatedStats) - statistics.RatioOfPseudoEstimate.Store(tt.RatioOfPseudoEstimate) - plan := testKit.MustQuery(tt.SQL) - testdata.OnRecord(func() { - output[i].SQL = tt.SQL - output[i].EnablePseudoForOutdatedStats = tt.EnablePseudoForOutdatedStats - output[i].RatioOfPseudoEstimate = tt.RatioOfPseudoEstimate - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - }) - plan.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestNullCount(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t (a int, b int, index idx(a))") - testKit.MustExec("insert into t values (null, null), (null, null)") - testKit.MustExec("analyze table t") - var input []string - var output [][]string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i := 0; i < 2; i++ { - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(input[i]).Rows()) - }) - testKit.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) - } - h := dom.StatsHandle() - h.Clear() - require.NoError(t, h.Update(dom.InfoSchema())) - for i := 2; i < 4; i++ { - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(testKit.MustQuery(input[i]).Rows()) - }) - testKit.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) - } -} - -func TestCorrelatedEstimation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by - tk.MustExec("create table t(a int, b int, c int, index idx(c,b,a))") - tk.MustExec("insert into t values(1,1,1), (2,2,2), (3,3,3), (4,4,4), (5,5,5), (6,6,6), (7,7,7), (8,8,8), (9,9,9),(10,10,10)") - tk.MustExec("analyze table t") - var ( - input []string - output [][]string - ) - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - rs := tk.MustQuery(tt) - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(rs.Rows()) - }) - rs.Check(testkit.Rows(output[i]...)) - } -} - -func TestInconsistentEstimation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int, index ab(a,b), index ac(a,c))") - tk.MustExec("insert into t values (1,1,1), (1000,1000,1000)") - for i := 0; i < 10; i++ { - tk.MustExec("insert into t values (5,5,5), (10,10,10)") - } - tk.MustExec("set @@tidb_analyze_version=1") - tk.MustExec("analyze table t with 2 buckets") - // Force using the histogram to estimate. - tk.MustExec("update mysql.stats_histograms set stats_ver = 0") - dom.StatsHandle().Clear() - require.NoError(t, dom.StatsHandle().Update(dom.InfoSchema())) - var input []string - var output []struct { - SQL string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, sql := range input { - plan := tk.MustQuery(sql) - testdata.OnRecord(func() { - output[i].SQL = sql - output[i].Plan = testdata.ConvertRowsToStrings(plan.Rows()) - }) - plan.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIssue9562(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - var input [][]string - var output []struct { - SQL []string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, ts := range input { - for j, tt := range ts { - if j != len(ts)-1 { - tk.MustExec(tt) - } - testdata.OnRecord(func() { - output[i].SQL = ts - if j == len(ts)-1 { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - } - }) - if j == len(ts)-1 { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - } - } -} - -func TestLimitCrossEstimation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("set @@session.tidb_executor_concurrency = 4;") - tk.MustExec("set @@session.tidb_hash_join_concurrency = 5;") - tk.MustExec("set @@session.tidb_distsql_scan_concurrency = 15;") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b int not null, c int not null default 0, index idx_bc(b, c))") - var input [][]string - var output []struct { - SQL []string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, ts := range input { - for j, tt := range ts { - if j != len(ts)-1 { - tk.MustExec(tt) - } - testdata.OnRecord(func() { - output[i].SQL = ts - if j == len(ts)-1 { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - } - }) - if j == len(ts)-1 { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - } - } -} - -func TestLowSelIndexGreedySearch(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set tidb_cost_model_version=2") - testKit.MustExec(`set tidb_opt_limit_push_down_threshold=0`) - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t (a varchar(32) default null, b varchar(10) default null, c varchar(12) default null, d varchar(32) default null, e bigint(10) default null, key idx1 (d,a), key idx2 (a,c), key idx3 (c,b), key idx4 (e))") - require.NoError(t, loadTableStats("analyzeSuiteTestLowSelIndexGreedySearchT.json", dom)) - var input []string - var output []struct { - SQL string - Plan []string - } - // The test purposes are: - // - index `idx2` runs much faster than `idx4` experimentally; - // - estimated row count of IndexLookUp should be 0; - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) - }) - testKit.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestTiFlashCostModel(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t (a int, b int, c int, primary key(a))") - tk.MustExec("insert into t values(1,1,1), (2,2,2), (3,3,3)") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - var input, output [][]string - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, ts := range input { - for j, tt := range ts { - if j != len(ts)-1 { - tk.MustExec(tt) - } - testdata.OnRecord(func() { - if j == len(ts)-1 { - output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - } - }) - if j == len(ts)-1 { - tk.MustQuery(tt).Check(testkit.Rows(output[i]...)) - } - } - } -} - -func TestIndexEqualUnknown(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t, t1") - testKit.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly - testKit.MustExec("CREATE TABLE t(a bigint(20) NOT NULL, b bigint(20) NOT NULL, c bigint(20) NOT NULL, PRIMARY KEY (a,c,b), KEY (b))") - require.NoError(t, loadTableStats("analyzeSuiteTestIndexEqualUnknownT.json", dom)) - var input []string - var output []struct { - SQL string - Plan []string - } - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(testKit.MustQuery(tt).Rows()) - }) - testKit.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestLimitIndexEstimation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, key idx_a(a), key idx_b(b))") - tk.MustExec("set session tidb_enable_extended_stats = on") - // Values in column a are from 1 to 1000000, values in column b are from 1000000 to 1, - // these 2 columns are strictly correlated in reverse order. - require.NoError(t, loadTableStats("analyzeSuiteTestLimitIndexEstimationT.json", dom)) - var input []string - var output []struct { - SQL string - Plan []string - } - - analyzeSuiteData := GetAnalyzeSuiteData() - analyzeSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} diff --git a/planner/core/casetest/cbotest/main_test.go b/planner/core/casetest/cbotest/main_test.go deleted file mode 100644 index 9a94e137fc273..0000000000000 --- a/planner/core/casetest/cbotest/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cbotest - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "analyze_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetAnalyzeSuiteData() testdata.TestData { - return testDataMap["analyze_suite"] -} diff --git a/planner/core/casetest/dag/BUILD.bazel b/planner/core/casetest/dag/BUILD.bazel deleted file mode 100644 index bc426ad557ab2..0000000000000 --- a/planner/core/casetest/dag/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "dag_test", - timeout = "short", - srcs = [ - "dag_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 10, - deps = [ - "//domain", - "//infoschema", - "//parser", - "//parser/ast", - "//parser/model", - "//planner", - "//planner/core", - "//session", - "//sessiontxn", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util/hint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/dag/main_test.go b/planner/core/casetest/dag/main_test.go deleted file mode 100644 index 888e9a78d4523..0000000000000 --- a/planner/core/casetest/dag/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package dag - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetPlanSuiteData() testdata.TestData { - return testDataMap["plan_suite"] -} diff --git a/planner/core/casetest/enforcempp/BUILD.bazel b/planner/core/casetest/enforcempp/BUILD.bazel deleted file mode 100644 index 67171669ebcbb..0000000000000 --- a/planner/core/casetest/enforcempp/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "enforcempp_test", - timeout = "short", - srcs = [ - "enforce_mpp_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 12, - deps = [ - "//domain", - "//parser/model", - "//planner/core/internal", - "//sessionctx/stmtctx", - "//testkit", - "//testkit/external", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util/collate", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/enforcempp/enforce_mpp_test.go b/planner/core/casetest/enforcempp/enforce_mpp_test.go deleted file mode 100644 index 79caa4df798e5..0000000000000 --- a/planner/core/casetest/enforcempp/enforce_mpp_test.go +++ /dev/null @@ -1,727 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package enforcempp - -import ( - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/collate" - "github.com/stretchr/testify/require" -) - -func TestEnforceMPP(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test query - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("create index idx on t(a)") - tk.MustExec("CREATE TABLE `s` (\n `a` int(11) DEFAULT NULL,\n `b` int(11) DEFAULT NULL,\n `c` int(11) DEFAULT NULL,\n `d` int(11) DEFAULT NULL,\n UNIQUE KEY `a` (`a`),\n KEY `ii` (`a`,`b`)\n)") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - if tblInfo.Name.L == "s" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - filterWarnings := func(originalWarnings []stmtctx.SQLWarn) []stmtctx.SQLWarn { - warnings := make([]stmtctx.SQLWarn, 0, 4) - for _, warning := range originalWarnings { - // filter out warning about skyline pruning - if !strings.Contains(warning.Err.Error(), "remain after pruning paths for") { - warnings = append(warnings, warning) - } - } - return warnings - } - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(filterWarnings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - }) - require.Eventually(t, - func() bool { - res := tk.MustQuery(tt) - return res.Equal(testkit.Rows(output[i].Plan...)) - }, 1*time.Second, 100*time.Millisecond) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(filterWarnings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()))) - } -} - -// general cases. -func TestEnforceMPPWarning1(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test query - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int as (a+1), c enum('xx', 'yy'), d bit(1))") - tk.MustExec("create index idx on t(a)") - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") { - tk.MustExec(tt) - continue - } - if strings.HasPrefix(tt, "cmd: create-replica") { - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: false, - } - } - } - continue - } - if strings.HasPrefix(tt, "cmd: enable-replica") { - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// partition table. -func TestEnforceMPPWarning2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test query - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (a int, b char(20)) PARTITION BY HASH(a)") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// new collation. -func TestEnforceMPPWarning3(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test query - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t (a int, b char(20))") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - if strings.HasPrefix(tt, "cmd: enable-new-collation") { - collate.SetNewCollationEnabledForTest(true) - continue - } - if strings.HasPrefix(tt, "cmd: disable-new-collation") { - collate.SetNewCollationEnabledForTest(false) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } - collate.SetNewCollationEnabledForTest(true) -} - -// Test enforce mpp warning for joins -func TestEnforceMPPWarning4(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE t(a int primary key)") - tk.MustExec("drop table if exists s") - tk.MustExec("CREATE TABLE s(a int primary key)") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" || tblInfo.Name.L == "s" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// Test agg push down for MPP mode -func TestMPP2PhaseAggPushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists c") - tk.MustExec("drop table if exists o") - tk.MustExec("create table c(c_id bigint)") - tk.MustExec("create table o(o_id bigint, c_id bigint not null)") - - tk.MustExec("create table t (a int, b int)") - tk.MustExec("insert into t values (1, 1);") - tk.MustExec("insert into t values (1, 1);") - tk.MustExec("insert into t values (1, 1);") - tk.MustExec("insert into t values (1, 1);") - tk.MustExec("insert into t values (1, 1);") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "c" || tblInfo.Name.L == "o" || tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// Test skewed group distinct aggregate rewrite for MPP mode -func TestMPPSkewedGroupDistinctRewrite(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b bigint not null, c bigint, d date, e varchar(20))") - // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// Test 3 stage aggregation for single count distinct -func TestMPPSingleDistinct3Stage(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b bigint not null, c bigint, d date, e varchar(20) collate utf8mb4_general_ci)") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// todo: some post optimization after resolveIndices will inject another projection below agg, which change the column name used in higher operator, -// -// since it doesn't change the schema out (index ref is still the right), so by now it's fine. SEE case: EXPLAIN select count(distinct a), count(distinct b), sum(c) from t. -func TestMPPMultiDistinct3Stage(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test;") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, d int);") - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - tk.MustExec("set @@session.tidb_opt_enable_three_stage_multi_distinct_agg=1") - defer tk.MustExec("set @@session.tidb_opt_enable_three_stage_multi_distinct_agg=0") - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\";") - tk.MustExec("set @@session.tidb_enforce_mpp=1") - tk.MustExec("set @@session.tidb_allow_mpp=ON;") - // todo: current mock regionCache won't scale the regions among tiFlash nodes. The under layer still collect data from only one of the nodes. - tk.MustExec("split table t BETWEEN (0) AND (5000) REGIONS 5;") - tk.MustExec("insert into t values(1000, 1000, 1000, 1)") - tk.MustExec("insert into t values(1000, 1000, 1000, 1)") - tk.MustExec("insert into t values(2000, 2000, 2000, 1)") - tk.MustExec("insert into t values(2000, 2000, 2000, 1)") - tk.MustExec("insert into t values(3000, 3000, 3000, 1)") - tk.MustExec("insert into t values(3000, 3000, 3000, 1)") - tk.MustExec("insert into t values(4000, 4000, 4000, 1)") - tk.MustExec("insert into t values(4000, 4000, 4000, 1)") - tk.MustExec("insert into t values(5000, 5000, 5000, 1)") - tk.MustExec("insert into t values(5000, 5000, 5000, 1)") - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -// Test null-aware semi join push down for MPP mode -func TestMPPNullAwareSemiJoinPushDown(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists s") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("create table s(a int, b int, c int)") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustExec("alter table s set tiflash replica 1") - - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tb = external.GetTableByName(t, tk, "test", "s") - err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPSharedCTEScan(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - - // test table - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists s") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("create table s(a int, b int, c int)") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustExec("alter table s set tiflash replica 1") - - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tb = external.GetTableByName(t, tk, "test", "s") - err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - - tk.MustExec("set @@tidb_enforce_mpp='on'") - tk.MustExec("set @@tidb_opt_enable_mpp_shared_cte_execution='on'") - - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestRollupMPP(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("drop table if exists s") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("create table s(a int, b int, c int)") - tk.MustExec("CREATE TABLE `sales` (`year` int(11) DEFAULT NULL, `country` varchar(20) DEFAULT NULL, `product` varchar(32) DEFAULT NULL, `profit` int(11) DEFAULT NULL)") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustExec("alter table s set tiflash replica 1") - tk.MustExec("alter table sales set tiflash replica 1") - - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tb = external.GetTableByName(t, tk, "test", "s") - err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tb = external.GetTableByName(t, tk, "test", "sales") - err = domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - // error test - err = tk.ExecToErr("explain format = 'brief' SELECT country, product, SUM(profit) AS profit FROM sales GROUP BY country, country, product with rollup order by grouping(year);") - require.Equal(t, err.Error(), "[planner:3602]Argument #0 of GROUPING function is not in GROUP BY") - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - - tk.MustExec("set @@tidb_enforce_mpp='on'") - tk.Session().GetSessionVars().TiFlashFineGrainedShuffleStreamCount = -1 - - enforceMPPSuiteData := GetEnforceMPPSuiteData() - enforceMPPSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} diff --git a/planner/core/casetest/enforcempp/main_test.go b/planner/core/casetest/enforcempp/main_test.go deleted file mode 100644 index 1e9face0180e6..0000000000000 --- a/planner/core/casetest/enforcempp/main_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package enforcempp - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "enforce_mpp_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetEnforceMPPSuiteData() testdata.TestData { - return testDataMap["enforce_mpp_suite"] -} diff --git a/planner/core/casetest/flatplan/BUILD.bazel b/planner/core/casetest/flatplan/BUILD.bazel deleted file mode 100644 index ea05b2c483a99..0000000000000 --- a/planner/core/casetest/flatplan/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "flatplan_test", - timeout = "short", - srcs = [ - "flat_plan_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - deps = [ - "//config", - "//infoschema", - "//kv", - "//parser", - "//parser/model", - "//planner", - "//planner/core", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/flatplan/main_test.go b/planner/core/casetest/flatplan/main_test.go deleted file mode 100644 index eabbf5cb6706b..0000000000000 --- a/planner/core/casetest/flatplan/main_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package flatplan - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "flat_plan_suite") - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetFlatPlanSuiteData() testdata.TestData { - return testDataMap["flat_plan_suite"] -} diff --git a/planner/core/casetest/hint/BUILD.bazel b/planner/core/casetest/hint/BUILD.bazel deleted file mode 100644 index e9df04d8c85c8..0000000000000 --- a/planner/core/casetest/hint/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "hint_test", - timeout = "short", - srcs = [ - "hint_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 6, - deps = [ - "//config", - "//domain", - "//parser/model", - "//planner/core/internal", - "//sessionctx/variable", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/hint/main_test.go b/planner/core/casetest/hint/main_test.go deleted file mode 100644 index 926826235ede1..0000000000000 --- a/planner/core/casetest/hint/main_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package hint - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} diff --git a/planner/core/casetest/index/BUILD.bazel b/planner/core/casetest/index/BUILD.bazel deleted file mode 100644 index b6b88e1899b8b..0000000000000 --- a/planner/core/casetest/index/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "index_test", - timeout = "short", - srcs = [ - "index_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 12, - deps = [ - "//sessionctx/variable", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/index/index_test.go b/planner/core/casetest/index/index_test.go deleted file mode 100644 index 29233f93496d9..0000000000000 --- a/planner/core/casetest/index/index_test.go +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package index - -import ( - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestIndexJoinUniqueCompositeIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly - tk.MustExec("create table t1(a int not null, c int not null)") - tk.MustExec("create table t2(a int not null, b int not null, c int not null, primary key(a,b))") - tk.MustExec("insert into t1 values(1,1)") - tk.MustExec("insert into t2 values(1,1,1),(1,2,1)") - tk.MustExec("analyze table t1,t2") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIndexMerge(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, unique index(a), unique index(b), primary key(c))") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -// for issue #14822 and #38258 -func TestIndexJoinTableRange(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int, primary key (a), key idx_t1_b (b))") - tk.MustExec("create table t2(a int, b int, primary key (a), key idx_t1_b (b))") - tk.MustExec("create table t3(a int, b int, c int)") - tk.MustExec("create table t4(a int, b int, c int, primary key (a, b) clustered)") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIndexJoinInnerIndexNDV(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int not null, b int not null, c int not null)") - tk.MustExec("create table t2(a int not null, b int not null, c int not null, index idx1(a,b), index idx2(c))") - tk.MustExec("insert into t1 values(1,1,1),(1,1,1),(1,1,1)") - tk.MustExec("insert into t2 values(1,1,1),(1,1,2),(1,1,3)") - tk.MustExec("analyze table t1, t2") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIndexMergeSerial(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, unique key(a), unique key(b))") - tk.MustExec("insert into t value (1, 5), (2, 4), (3, 3), (4, 2), (5, 1)") - tk.MustExec("insert into t value (6, 0), (7, -1), (8, -2), (9, -3), (10, -4)") - tk.MustExec("analyze table t") - - var input []string - var output []struct { - SQL string - Plan []string - Warnings []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warnings = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warnings...)) - } -} - -func TestIndexJoinOnClusteredIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c))") - tk.MustExec(`insert into t values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13)`) - tk.MustExec("analyze table t") - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery("explain format = 'brief'" + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...)) - } -} - -func TestIndexMergeWithCorrelatedColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2;") - tk.MustExec("create table t1(c1 int, c2 int, c3 int, primary key(c1), key(c2));") - tk.MustExec("insert into t1 values(1, 1, 1);") - tk.MustExec("insert into t1 values(2, 2, 2);") - tk.MustExec("create table t2(c1 int, c2 int, c3 int);") - tk.MustExec("insert into t2 values(1, 1, 1);") - tk.MustExec("insert into t2 values(2, 2, 2);") - - tk.MustExec("drop table if exists tt1, tt2;") - tk.MustExec("create table tt1 (c_int int, c_str varchar(40), c_datetime datetime, c_decimal decimal(12, 6), primary key(c_int), key(c_int), key(c_str), unique key(c_decimal), key(c_datetime));") - tk.MustExec("create table tt2 like tt1 ;") - tk.MustExec(`insert into tt1 (c_int, c_str, c_datetime, c_decimal) values (6, 'sharp payne', '2020-06-07 10:40:39', 6.117000) , - (7, 'objective kare', '2020-02-05 18:47:26', 1.053000) , - (8, 'thirsty pasteur', '2020-01-02 13:06:56', 2.506000) , - (9, 'blissful wilbur', '2020-06-04 11:34:04', 9.144000) , - (10, 'reverent mclean', '2020-02-12 07:36:26', 7.751000) ;`) - tk.MustExec(`insert into tt2 (c_int, c_str, c_datetime, c_decimal) values (6, 'beautiful joliot', '2020-01-16 01:44:37', 5.627000) , - (7, 'hopeful blackburn', '2020-05-23 21:44:20', 7.890000) , - (8, 'ecstatic davinci', '2020-02-01 12:27:17', 5.648000) , - (9, 'hopeful lewin', '2020-05-05 05:58:25', 7.288000) , - (10, 'sharp jennings', '2020-01-28 04:35:03', 9.758000) ;`) - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format=brief " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery("explain format=brief " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Res...)) - } -} - -func TestIndexJoinRangeFallback(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int, c varchar(10), d varchar(10), index idx_a_b_c_d(a, b, c(2), d(2)))") - tk.MustExec("create table t2(e int, f int, g varchar(10), h varchar(10))") - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - setStmt := strings.HasPrefix(tt, "set") - testdata.OnRecord(func() { - output[i].SQL = tt - if !setStmt { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - } - }) - if setStmt { - tk.MustExec(tt) - } else { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } - } -} - -func TestNullConditionForPrefixIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`CREATE TABLE t1 ( - id char(1) DEFAULT NULL, - c1 varchar(255) DEFAULT NULL, - c2 text DEFAULT NULL, - KEY idx1 (c1), - KEY idx2 (c1,c2(5)) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin`) - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t2(a int, b varchar(10), index idx(b(5)))") - tk.MustExec("create table t3(a int, b varchar(10), c int, primary key (a, b(5)) clustered)") - tk.MustExec("set tidb_opt_prefix_index_single_scan = 1") - tk.MustExec("insert into t1 values ('a', '0xfff', '111111'), ('b', '0xfff', '22 '), ('c', '0xfff', ''), ('d', '0xfff', null)") - tk.MustExec("insert into t2 values (1, 'aaaaaa'), (2, 'bb '), (3, ''), (4, null)") - tk.MustExec("insert into t3 values (1, 'aaaaaa', 2), (1, 'bb ', 3), (1, '', 4)") - - var input []string - var output []struct { - SQL string - Plan []string - Result []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + tt).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain format='brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) - } - - // test plan cache - tk.MustExec(`set tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0") - tk.MustExec("prepare stmt from 'select count(1) from t1 where c1 = ? and c2 is not null'") - tk.MustExec("set @a = '0xfff'") - tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) - tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) - tk.MustQuery("execute stmt using @a").Check(testkit.Rows("3")) - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Check(testkit.Rows( - "StreamAgg_17 1.00 root funcs:count(Column#7)->Column#5", - "└─IndexReader_18 1.00 root index:StreamAgg_9", - " └─StreamAgg_9 1.00 cop[tikv] funcs:count(1)->Column#7", - " └─IndexRangeScan_16 99.90 cop[tikv] table:t1, index:idx2(c1, c2) range:[\"0xfff\" -inf,\"0xfff\" +inf], keep order:false, stats:pseudo")) -} - -func TestHeuristicIndexSelection(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int, c int, d int, e int, f int, g int, primary key (a), unique key c_d_e (c, d, e), unique key f (f), unique key f_g (f, g), key g (g))") - tk.MustExec("create table t2(a int, b int, c int, d int, unique index idx_a (a), unique index idx_b_c (b, c), unique index idx_b_c_a_d (b, c, a, d))") - tk.MustExec("create table t3(a bigint, b varchar(255), c bigint, primary key(a, b) clustered)") - tk.MustExec("create table t4(a bigint, b varchar(255), c bigint, primary key(a, b) nonclustered)") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - var input []string - var output []struct { - SQL string - Plan []string - Warnings []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'verbose' " + tt).Rows()) - output[i].Warnings = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'verbose' " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warnings...)) - } -} - -func TestLimitIndexLookUpKeepOrder(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b int, c int, d int, index idx(a,b,c));") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestAccessPathOnClusterIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int, b varchar(20), c decimal(40,10), d int, primary key(a,b), key(c))") - tk.MustExec(`insert into t1 values (1,"111",1.1,11), (2,"222",2.2,12), (3,"333",3.3,13)`) - tk.MustExec("analyze table t1") - - var input []string - var output []struct { - SQL string - Plan []string - Res []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + tt).Rows()) - output[i].Res = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Sort().Rows()) - }) - tk.MustQuery("explain format='brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Res...)) - } -} diff --git a/planner/core/casetest/index/main_test.go b/planner/core/casetest/index/main_test.go deleted file mode 100644 index 52c9296a142b8..0000000000000 --- a/planner/core/casetest/index/main_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package index - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} diff --git a/planner/core/casetest/integration_test.go b/planner/core/casetest/integration_test.go deleted file mode 100644 index 80b6b3d35d2d8..0000000000000 --- a/planner/core/casetest/integration_test.go +++ /dev/null @@ -1,441 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package casetest - -import ( - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func TestVerboseExplain(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`set tidb_opt_limit_push_down_threshold=0`) - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int, b int)") - tk.MustExec("create table t3(a int, b int, index c(b))") - tk.MustExec("insert into t1 values(1,2)") - tk.MustExec("insert into t1 values(3,4)") - tk.MustExec("insert into t1 values(5,6)") - tk.MustExec("insert into t2 values(1,2)") - tk.MustExec("insert into t2 values(3,4)") - tk.MustExec("insert into t2 values(5,6)") - tk.MustExec("insert into t3 values(1,2)") - tk.MustExec("insert into t3 values(3,4)") - tk.MustExec("insert into t3 values(5,6)") - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustExec("analyze table t3") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t1" || tblInfo.Name.L == "t2" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIsolationReadTiFlashNotChoosePointGet(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, primary key (a))") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") - var input []string - var output []struct { - SQL string - Result []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) - } -} - -func TestIsolationReadDoNotFilterSystemDB(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set @@tidb_isolation_read_engines = \"tiflash\"") - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestPartitionPruningForInExpr(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int(11) not null, b int) partition by range (a) (partition p0 values less than (4), partition p1 values less than(10), partition p2 values less than maxvalue);") - tk.MustExec("insert into t values (1, 1),(10, 10),(11, 11)") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestPartitionExplain(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - - tk.MustExec("set @@tidb_enable_index_merge = 1;") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) - }) - tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestMergeContinuousSelections(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists ts") - tk.MustExec("create table ts (col_char_64 char(64), col_varchar_64_not_null varchar(64) not null, col_varchar_key varchar(1), id int primary key, col_varchar_64 varchar(64),col_char_64_not_null char(64) not null);") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "ts" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec(" set @@tidb_allow_mpp=1;") - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIssue31240(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t31240(a int, b int);") - tk.MustExec("set @@tidb_allow_mpp = 0") - tk.MustExec("set tidb_cost_model_version=2") - // since allow-mpp is adjusted to false, there will be no physical plan if TiFlash cop is banned. - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31240", L: "t31240"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - tk.MustExec("drop table if exists t31240") -} - -func TestIssue32632(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("CREATE TABLE `partsupp` (" + - " `PS_PARTKEY` bigint(20) NOT NULL," + - "`PS_SUPPKEY` bigint(20) NOT NULL," + - "`PS_AVAILQTY` bigint(20) NOT NULL," + - "`PS_SUPPLYCOST` decimal(15,2) NOT NULL," + - "`PS_COMMENT` varchar(199) NOT NULL," + - "PRIMARY KEY (`PS_PARTKEY`,`PS_SUPPKEY`) /*T![clustered_index] NONCLUSTERED */)") - tk.MustExec("CREATE TABLE `supplier` (" + - "`S_SUPPKEY` bigint(20) NOT NULL," + - "`S_NAME` char(25) NOT NULL," + - "`S_ADDRESS` varchar(40) NOT NULL," + - "`S_NATIONKEY` bigint(20) NOT NULL," + - "`S_PHONE` char(15) NOT NULL," + - "`S_ACCTBAL` decimal(15,2) NOT NULL," + - "`S_COMMENT` varchar(101) NOT NULL," + - "PRIMARY KEY (`S_SUPPKEY`) /*T![clustered_index] CLUSTERED */)") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("set @@tidb_enforce_mpp = 1") - - tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "partsupp", L: "partsupp"}) - require.NoError(t, err) - tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "supplier", L: "supplier"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - statsTbl1 := h.GetTableStats(tbl1.Meta()) - statsTbl1.RealtimeCount = 800000 - statsTbl2 := h.GetTableStats(tbl2.Meta()) - statsTbl2.RealtimeCount = 10000 - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - tk.MustExec("drop table if exists partsupp") - tk.MustExec("drop table if exists supplier") -} - -func TestTiFlashPartitionTableScan(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@tidb_enforce_mpp = on") - tk.MustExec("set @@tidb_allow_batch_cop = 2") - tk.MustExec("drop table if exists rp_t;") - tk.MustExec("drop table if exists hp_t;") - tk.MustExec("create table rp_t(a int) partition by RANGE (a) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11), PARTITION p2 VALUES LESS THAN (16), PARTITION p3 VALUES LESS THAN (21));") - tk.MustExec("create table hp_t(a int) partition by hash(a) partitions 4;") - tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "rp_t", L: "rp_t"}) - require.NoError(t, err) - tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "hp_t", L: "hp_t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - tk.MustExec("drop table rp_t;") - tk.MustExec("drop table hp_t;") -} - -func TestTiFlashFineGrainedShuffle(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@tidb_enforce_mpp = on") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int, c2 int)") - - tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - var input []string - var output []struct { - SQL string - Plan []string - } - integrationSuiteData := GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestFixControl45132(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec(`create table t (a int, b int, key(a))`) - values := make([]string, 0, 101) - for i := 0; i < 100; i++ { - values = append(values, "(1, 1)") - } - values = append(values, "(2, 2)") // count(1) : count(2) == 100 : 1 - tk.MustExec(`insert into t values ` + strings.Join(values, ",")) - for i := 0; i < 7; i++ { - tk.MustExec(`insert into t select * from t`) - } - tk.MustExec(`analyze table t`) - // the cost model prefers to use TableScan instead of IndexLookup to avoid double requests. - tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) - - tk.MustExec(`set @@tidb_opt_fix_control = "45132:99"`) - tk.MustIndexLookup(`select * from t where a=2`) // index lookup - - tk.MustExec(`set @@tidb_opt_fix_control = "45132:500"`) - tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) - - tk.MustExec(`set @@tidb_opt_fix_control = "45132:0"`) - tk.MustHavePlan(`select * from t where a=2`, `TableFullScan`) -} - -func TestIssue41957(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`use test`) - tk.MustExec("CREATE TABLE `github_events` (\n `id` bigint(20) NOT NULL DEFAULT '0',\n `type` varchar(29) NOT NULL DEFAULT 'Event',\n `created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `repo_id` bigint(20) NOT NULL DEFAULT '0',\n `repo_name` varchar(140) NOT NULL DEFAULT '',\n `actor_id` bigint(20) NOT NULL DEFAULT '0',\n `actor_login` varchar(40) NOT NULL DEFAULT '',\n `language` varchar(26) NOT NULL DEFAULT '',\n `additions` bigint(20) NOT NULL DEFAULT '0',\n `deletions` bigint(20) NOT NULL DEFAULT '0',\n `action` varchar(11) NOT NULL DEFAULT '',\n `number` int(11) NOT NULL DEFAULT '0',\n `commit_id` varchar(40) NOT NULL DEFAULT '',\n `comment_id` bigint(20) NOT NULL DEFAULT '0',\n `org_login` varchar(40) NOT NULL DEFAULT '',\n `org_id` bigint(20) NOT NULL DEFAULT '0',\n `state` varchar(6) NOT NULL DEFAULT '',\n `closed_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `comments` int(11) NOT NULL DEFAULT '0',\n `pr_merged_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n `pr_merged` tinyint(1) NOT NULL DEFAULT '0',\n `pr_changed_files` int(11) NOT NULL DEFAULT '0',\n `pr_review_comments` int(11) NOT NULL DEFAULT '0',\n `pr_or_issue_id` bigint(20) NOT NULL DEFAULT '0',\n `event_day` date NOT NULL,\n `event_month` date NOT NULL,\n `event_year` int(11) NOT NULL,\n `push_size` int(11) NOT NULL DEFAULT '0',\n `push_distinct_size` int(11) NOT NULL DEFAULT '0',\n `creator_user_login` varchar(40) NOT NULL DEFAULT '',\n `creator_user_id` bigint(20) NOT NULL DEFAULT '0',\n `pr_or_issue_created_at` datetime NOT NULL DEFAULT '1970-01-01 00:00:00',\n KEY `index_github_events_on_id` (`id`),\n KEY `index_github_events_on_created_at` (`created_at`),\n KEY `index_github_events_on_repo_id_type_action_month_actor_login` (`repo_id`,`type`,`action`,`event_month`,`actor_login`),\n KEY `index_ge_on_repo_id_type_action_pr_merged_created_at_add_del` (`repo_id`,`type`,`action`,`pr_merged`,`created_at`,`additions`,`deletions`),\n KEY `index_ge_on_creator_id_type_action_merged_created_at_add_del` (`creator_user_id`,`type`,`action`,`pr_merged`,`created_at`,`additions`,`deletions`),\n KEY `index_ge_on_actor_id_type_action_created_at_repo_id_commits` (`actor_id`,`type`,`action`,`created_at`,`repo_id`,`push_distinct_size`),\n KEY `index_ge_on_repo_id_type_action_created_at_number_pdsize_psize` (`repo_id`,`type`,`action`,`created_at`,`number`,`push_distinct_size`,`push_size`),\n KEY `index_ge_on_repo_id_type_action_created_at_actor_login` (`repo_id`,`type`,`action`,`created_at`,`actor_login`),\n KEY `index_ge_on_repo_name_type` (`repo_name`,`type`),\n KEY `index_ge_on_actor_login_type` (`actor_login`,`type`),\n KEY `index_ge_on_org_login_type` (`org_login`,`type`),\n KEY `index_ge_on_language` (`language`),\n KEY `index_ge_on_org_id_type` (`org_id`,`type`),\n KEY `index_ge_on_actor_login_lower` ((lower(`actor_login`))),\n KEY `index_ge_on_repo_name_lower` ((lower(`repo_name`))),\n KEY `index_ge_on_language_lower` ((lower(`language`))),\n KEY `index_ge_on_type_action` (`type`,`action`) /*!80000 INVISIBLE */,\n KEY `index_ge_on_repo_id_type_created_at` (`repo_id`,`type`,`created_at`),\n KEY `index_ge_on_repo_id_created_at` (`repo_id`,`created_at`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\nPARTITION BY LIST COLUMNS(`type`)\n(PARTITION `push_event` VALUES IN ('PushEvent'),\n PARTITION `create_event` VALUES IN ('CreateEvent'),\n PARTITION `pull_request_event` VALUES IN ('PullRequestEvent'),\n PARTITION `watch_event` VALUES IN ('WatchEvent'),\n PARTITION `issue_comment_event` VALUES IN ('IssueCommentEvent'),\n PARTITION `issues_event` VALUES IN ('IssuesEvent'),\n PARTITION `delete_event` VALUES IN ('DeleteEvent'),\n PARTITION `fork_event` VALUES IN ('ForkEvent'),\n PARTITION `pull_request_review_comment_event` VALUES IN ('PullRequestReviewCommentEvent'),\n PARTITION `pull_request_review_event` VALUES IN ('PullRequestReviewEvent'),\n PARTITION `gollum_event` VALUES IN ('GollumEvent'),\n PARTITION `release_event` VALUES IN ('ReleaseEvent'),\n PARTITION `member_event` VALUES IN ('MemberEvent'),\n PARTITION `commit_comment_event` VALUES IN ('CommitCommentEvent'),\n PARTITION `public_event` VALUES IN ('PublicEvent'),\n PARTITION `gist_event` VALUES IN ('GistEvent'),\n PARTITION `follow_event` VALUES IN ('FollowEvent'),\n PARTITION `event` VALUES IN ('Event'),\n PARTITION `download_event` VALUES IN ('DownloadEvent'),\n PARTITION `team_add_event` VALUES IN ('TeamAddEvent'),\n PARTITION `fork_apply_event` VALUES IN ('ForkApplyEvent'))\n") - tk.MustQuery("SELECT\n repo_id, GROUP_CONCAT(\n DISTINCT actor_login\n ORDER BY cnt DESC\n SEPARATOR ','\n ) AS actor_logins\nFROM (\n SELECT\n ge.repo_id AS repo_id,\n ge.actor_login AS actor_login,\n COUNT(*) AS cnt\n FROM github_events ge\n WHERE\n type = 'PullRequestEvent' AND action = 'opened'\n AND (ge.created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY) AND ge.created_at <= NOW())\n GROUP BY ge.repo_id, ge.actor_login\n ORDER BY cnt DESC\n) sub\nGROUP BY repo_id").Check(testkit.Rows()) -} diff --git a/planner/core/casetest/main_test.go b/planner/core/casetest/main_test.go deleted file mode 100644 index a2d0a032e4b0f..0000000000000 --- a/planner/core/casetest/main_test.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package casetest - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_normalized_suite") - testDataMap.LoadTestSuiteData("testdata", "stats_suite") - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - testDataMap.LoadTestSuiteData("testdata", "json_plan_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetPlanNormalizedSuiteData() testdata.TestData { - return testDataMap["plan_normalized_suite"] -} - -func GetStatsSuiteData() testdata.TestData { - return testDataMap["stats_suite"] -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} - -func GetJSONPlanSuiteData() testdata.TestData { - return testDataMap["json_plan_suite"] -} diff --git a/planner/core/casetest/mpp/BUILD.bazel b/planner/core/casetest/mpp/BUILD.bazel deleted file mode 100644 index c10500d9573bd..0000000000000 --- a/planner/core/casetest/mpp/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "mpp_test", - timeout = "short", - srcs = [ - "main_test.go", - "mpp_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 20, - deps = [ - "//config", - "//domain", - "//parser/model", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/mpp/main_test.go b/planner/core/casetest/mpp/main_test.go deleted file mode 100644 index cf98ae28458b3..0000000000000 --- a/planner/core/casetest/mpp/main_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package mpp - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Performance.EnableStatsCacheMemQuota = true - }) - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} diff --git a/planner/core/casetest/partition/BUILD.bazel b/planner/core/casetest/partition/BUILD.bazel deleted file mode 100644 index a55c4b50ba071..0000000000000 --- a/planner/core/casetest/partition/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "partition_test", - timeout = "short", - srcs = [ - "integration_partition_test.go", - "main_test.go", - "partition_pruner_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 6, - deps = [ - "//config", - "//planner/core/internal", - "//sessionctx/variable", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/partition/integration_partition_test.go b/planner/core/casetest/partition/integration_partition_test.go deleted file mode 100644 index 9895bb1cf607b..0000000000000 --- a/planner/core/casetest/partition/integration_partition_test.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package partition - -import ( - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func TestListPartitionPruning(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database list_partition_pruning") - tk.MustExec("use list_partition_pruning") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - tk.MustExec(`create table tlist (a int) partition by list (a) ( - partition p0 values in (0, 1, 2), - partition p1 values in (3, 4, 5), - partition p2 values in (6, 7, 8), - partition p3 values in (9, 10, 11))`) - tk.MustExec(`create table tcollist (a int) partition by list columns(a) ( - partition p0 values in (0, 1, 2), - partition p1 values in (3, 4, 5), - partition p2 values in (6, 7, 8), - partition p3 values in (9, 10, 11))`) - tk.MustExec(`analyze table tlist`) - tk.MustExec(`analyze table tcollist`) - - var input []string - var output []struct { - SQL string - DynamicPlan []string - StaticPlan []string - } - integrationPartitionSuiteData := getIntegrationPartitionSuiteData() - integrationPartitionSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery(tt).Check(testkit.Rows(output[i].DynamicPlan...)) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustQuery(tt).Check(testkit.Rows(output[i].StaticPlan...)) - } -} - -func TestPartitionTableExplain(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`create table t (a int primary key, b int, key (b)) partition by hash(a) (partition P0, partition p1, partition P2)`) - tk.MustExec(`create table t2 (a int, b int)`) - tk.MustExec(`insert into t values (1,1),(2,2),(3,3)`) - tk.MustExec(`insert into t2 values (1,1),(2,2),(3,3)`) - tk.MustExec(`analyze table t, t2`) - - var input []string - var output []struct { - SQL string - DynamicPlan []string - StaticPlan []string - } - integrationPartitionSuiteData := getIntegrationPartitionSuiteData() - integrationPartitionSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery(tt).Check(testkit.Rows(output[i].DynamicPlan...)) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustQuery(tt).Check(testkit.Rows(output[i].StaticPlan...)) - } -} - -func TestBatchPointGetTablePartition(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table thash1(a int, b int, primary key(a,b) nonclustered) partition by hash(b) partitions 2") - tk.MustExec("insert into thash1 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table trange1(a int, b int, primary key(a,b) nonclustered) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)") - tk.MustExec("insert into trange1 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table tlist1(a int, b int, primary key(a,b) nonclustered) partition by list(b) (partition p0 values in (0, 1), partition p1 values in (2, 3))") - tk.MustExec("insert into tlist1 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table thash2(a int, b int, primary key(a,b)) partition by hash(b) partitions 2") - tk.MustExec("insert into thash2 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table trange2(a int, b int, primary key(a,b)) partition by range(b) (partition p0 values less than (2), partition p1 values less than maxvalue)") - tk.MustExec("insert into trange2 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table tlist2(a int, b int, primary key(a,b)) partition by list(b) (partition p0 values in (0, 1), partition p1 values in (2, 3))") - tk.MustExec("insert into tlist2 values(1,1),(1,2),(2,1),(2,2)") - - tk.MustExec("create table thash3(a int, b int, primary key(a)) partition by hash(a) partitions 2") - tk.MustExec("insert into thash3 values(1,0),(2,0),(3,0),(4,0)") - - tk.MustExec("create table trange3(a int, b int, primary key(a)) partition by range(a) (partition p0 values less than (3), partition p1 values less than maxvalue)") - tk.MustExec("insert into trange3 values(1,0),(2,0),(3,0),(4,0)") - - tk.MustExec("create table tlist3(a int, b int, primary key(a)) partition by list(a) (partition p0 values in (0, 1, 2), partition p1 values in (3, 4, 5))") - tk.MustExec("insert into tlist3 values(1,0),(2,0),(3,0),(4,0)") - - tk.MustExec("create table issue45889(a int) partition by list(a) (partition p0 values in (0, 1), partition p1 values in (2, 3))") - tk.MustExec("insert into issue45889 values (0),(0),(1),(1),(2),(2),(3),(3)") - - var input []string - var output []struct { - SQL string - DynamicPlan []string - StaticPlan []string - Result []string - } - - integrationPartitionSuiteData := getIntegrationPartitionSuiteData() - integrationPartitionSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - output[i].DynamicPlan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - dynamicQuery := tk.MustQuery(tt) - if !strings.Contains(tt, "order by") { - dynamicQuery = dynamicQuery.Sort() - } - dynamicRes := testdata.ConvertRowsToStrings(dynamicQuery.Rows()) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - output[i].StaticPlan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - staticQuery := tk.MustQuery(tt) - if !strings.Contains(tt, "order by") { - staticQuery = staticQuery.Sort() - } - staticRes := testdata.ConvertRowsToStrings(staticQuery.Rows()) - require.Equal(t, dynamicRes, staticRes) - output[i].Result = staticRes - }) - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].DynamicPlan...)) - if strings.Contains(tt, "order by") { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) - } else { - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) - } - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].StaticPlan...)) - if strings.Contains(tt, "order by") { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Result...)) - } else { - tk.MustQuery(tt).Sort().Check(testkit.Rows(output[i].Result...)) - } - } -} - -func TestBatchPointGetPartitionForAccessObject(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1(a int, b int, UNIQUE KEY (b)) PARTITION BY HASH(b) PARTITIONS 4") - tk.MustExec("insert into t1 values(1, 1), (2, 2), (3, 3), (4, 4)") - - tk.MustExec("CREATE TABLE t2 (id int primary key, name_id int) PARTITION BY LIST(id) (" + - "partition p0 values IN (1, 2), " + - "partition p1 values IN (3, 4), " + - "partition p3 values IN (5))") - tk.MustExec("insert into t2 values(1, 1), (2, 2), (3, 3), (4, 4)") - - tk.MustExec("CREATE TABLE t3 (id int primary key, name_id int) PARTITION BY LIST COLUMNS(id) (" + - "partition p0 values IN (1, 2), " + - "partition p1 values IN (3, 4), " + - "partition p3 values IN (5))") - tk.MustExec("insert into t3 values(1, 1), (2, 2), (3, 3), (4, 4)") - - tk.MustExec("CREATE TABLE t4 (id int, name_id int, unique key(id, name_id)) PARTITION BY LIST COLUMNS(id, name_id) (" + - "partition p0 values IN ((1, 1),(2, 2)), " + - "partition p1 values IN ((3, 3),(4, 4)), " + - "partition p3 values IN ((5, 5)))") - tk.MustExec("insert into t4 values(1, 1), (2, 2), (3, 3), (4, 4)") - - tk.MustExec("CREATE TABLE t5 (id int, name varchar(10), unique key(id, name)) PARTITION BY LIST COLUMNS(id, name) (" + - "partition p0 values IN ((1,'a'),(2,'b')), " + - "partition p1 values IN ((3,'c'),(4,'d')), " + - "partition p3 values IN ((5,'e')))") - tk.MustExec("insert into t5 values(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')") - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - - var input []string - var output []struct { - SQL string - Plan []string - } - - integrationPartitionSuiteData := getIntegrationPartitionSuiteData() - integrationPartitionSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - }) - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } -} diff --git a/planner/core/casetest/partition/main_test.go b/planner/core/casetest/partition/main_test.go deleted file mode 100644 index ac3fc704e11ac..0000000000000 --- a/planner/core/casetest/partition/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package partition - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "integration_partition_suite") - testDataMap.LoadTestSuiteData("testdata", "partition_pruner") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - conf.Performance.EnableStatsCacheMemQuota = true - }) - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func getIntegrationPartitionSuiteData() testdata.TestData { - return testDataMap["integration_partition_suite"] -} - -func getPartitionPrunerData() testdata.TestData { - return testDataMap["partition_pruner"] -} diff --git a/planner/core/casetest/physicalplantest/BUILD.bazel b/planner/core/casetest/physicalplantest/BUILD.bazel deleted file mode 100644 index b5d06e6752bfd..0000000000000 --- a/planner/core/casetest/physicalplantest/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "physicalplantest_test", - timeout = "short", - srcs = [ - "main_test.go", - "physical_plan_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - race = "on", - shard_count = 50, - deps = [ - "//config", - "//domain", - "//executor", - "//infoschema", - "//parser", - "//parser/ast", - "//parser/model", - "//planner", - "//planner/core", - "//planner/core/internal", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//testkit", - "//testkit/external", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//util/hint", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/physicalplantest/main_test.go b/planner/core/casetest/physicalplantest/main_test.go deleted file mode 100644 index 8fc12bd4df38a..0000000000000 --- a/planner/core/casetest/physicalplantest/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package physicalplantest - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetPlanSuiteData() testdata.TestData { - return testDataMap["plan_suite"] -} diff --git a/planner/core/casetest/physicalplantest/physical_plan_test.go b/planner/core/casetest/physicalplantest/physical_plan_test.go deleted file mode 100644 index 14fcbb046e7bb..0000000000000 --- a/planner/core/casetest/physicalplantest/physical_plan_test.go +++ /dev/null @@ -1,2243 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package physicalplantest - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/hint" - "github.com/stretchr/testify/require" -) - -func assertSameHints(t *testing.T, expected, actual []*ast.TableOptimizerHint) { - expectedStr := make([]string, 0, len(expected)) - actualStr := make([]string, 0, len(actual)) - for _, h := range expected { - expectedStr = append(expectedStr, hint.RestoreTableOptimizerHint(h)) - } - for _, h := range actual { - actualStr = append(actualStr, hint.RestoreTableOptimizerHint(h)) - } - require.ElementsMatch(t, expectedStr, actualStr) -} - -func doTestPushdownDistinct(t *testing.T, vars, input []string, output []struct { - SQL string - Plan []string - Result []string -}) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, index(c))") - tk.MustExec("insert into t values (1, 1, 1), (1, 1, 3), (1, 2, 3), (2, 1, 3), (1, 2, NULL);") - - tk.MustExec("drop table if exists pt") - tk.MustExec(`CREATE TABLE pt (a int, b int) PARTITION BY RANGE (a) ( - PARTITION p0 VALUES LESS THAN (2), - PARTITION p1 VALUES LESS THAN (100) - );`) - - tk.MustExec("drop table if exists tc;") - tk.MustExec("CREATE TABLE `tc`(`timestamp` timestamp NULL DEFAULT NULL, KEY `idx_timestamp` (`timestamp`)) PARTITION BY RANGE ( UNIX_TIMESTAMP(`timestamp`) ) (PARTITION `p2020072312` VALUES LESS THAN (1595480400),PARTITION `p2020072313` VALUES LESS THAN (1595484000));") - - tk.MustExec("drop table if exists ta") - tk.MustExec("create table ta(a int);") - tk.MustExec("insert into ta values(1), (1);") - tk.MustExec("drop table if exists tb") - tk.MustExec("create table tb(a int);") - tk.MustExec("insert into tb values(1), (1);") - - tk.MustExec("set session sql_mode=''") - tk.MustExec(fmt.Sprintf("set session %s=1", variable.TiDBHashAggPartialConcurrency)) - tk.MustExec(fmt.Sprintf("set session %s=1", variable.TiDBHashAggFinalConcurrency)) - - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - - for _, v := range vars { - tk.MustExec(v) - } - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestRefine(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - - var input []string - var output []struct { - SQL string - Best string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, tt := range input { - comment := fmt.Sprintf("input: %s", tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - sc := tk.Session().GetSessionVars().StmtCtx - sc.IgnoreTruncate.Store(false) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err, comment) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Best = core.ToString(p) - }) - require.Equal(t, output[i].Best, core.ToString(p), comment) - } -} - -func TestAggEliminator(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("set tidb_opt_limit_push_down_threshold=0") - tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by - var input []string - var output []struct { - SQL string - Best string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, tt := range input { - comment := fmt.Sprintf("input: %s", tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - sc := tk.Session().GetSessionVars().StmtCtx - sc.IgnoreTruncate.Store(false) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Best = core.ToString(p) - }) - require.Equal(t, output[i].Best, core.ToString(p), fmt.Sprintf("input: %s", tt)) - } -} - -func TestINMJHint(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int primary key, b int not null)") - tk.MustExec("create table t2(a int primary key, b int not null)") - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestEliminateMaxOneRow(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1;") - tk.MustExec("drop table if exists t2;") - tk.MustExec("drop table if exists t3;") - tk.MustExec("create table t1(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL, UNIQUE KEY idx_a (a))") - tk.MustExec("create table t2(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL)") - tk.MustExec("create table t3(a int(11) DEFAULT NULL, b int(11) DEFAULT NULL, c int(11) DEFAULT NULL, UNIQUE KEY idx_abc (a, b, c))") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) - } -} - -func TestIndexJoinUnionScan(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int primary key, b int, index idx(a))") - tk.MustExec("create table tt (a int primary key) partition by range (a) (partition p0 values less than (100), partition p1 values less than (200))") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - - var input [][]string - var output []struct { - SQL []string - Plan []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - tk.MustExec("begin") - for j, tt := range ts { - if j != len(ts)-1 { - tk.MustExec(tt) - } - testdata.OnRecord(func() { - output[i].SQL = ts - if j == len(ts)-1 { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - } - }) - if j == len(ts)-1 { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - } - tk.MustExec("rollback") - } -} - -func TestMergeJoinUnionScan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (c_int int, c_str varchar(40), primary key (c_int))") - tk.MustExec("create table t2 (c_int int, c_str varchar(40), primary key (c_int))") - tk.MustExec("insert into t1 (`c_int`, `c_str`) values (11, 'keen williamson'), (10, 'gracious hermann')") - tk.MustExec("insert into t2 (`c_int`, `c_str`) values (10, 'gracious hermann')") - - var input [][]string - var output []struct { - SQL []string - Plan []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - tk.MustExec("begin") - for j, tt := range ts { - if j != len(ts)-1 { - tk.MustExec(tt) - } - testdata.OnRecord(func() { - output[i].SQL = ts - if j == len(ts)-1 { - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - } - }) - if j == len(ts)-1 { - tk.MustQuery(tt).Check(testkit.Rows(output[i].Plan...)) - } - } - tk.MustExec("rollback") - } -} - -func TestSemiJoinToInner(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - - var input []string - var output []struct { - SQL string - Best string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, tt := range input { - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Best = core.ToString(p) - }) - require.Equal(t, output[i].Best, core.ToString(p)) - } -} - -func TestUnmatchedTableInHint(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - var input []string - var output []struct { - SQL string - Warning string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, test := range input { - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err) - _, _, err = planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - testdata.OnRecord(func() { - output[i].SQL = test - if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() - } - }) - if output[i].Warning == "" { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, 1) - require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) - require.Equal(t, output[i].Warning, warnings[0].Err.Error()) - } - } -} - -func TestIssue37520(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int primary key, b int);") - tk.MustExec("create table t2(a int, b int, index ia(a));") - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPHints(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustExec("set @@session.tidb_allow_mpp=ON") - tk.MustExec("create definer='root'@'localhost' view v as select a, sum(b) from t group by a, c;") - tk.MustExec("create definer='root'@'localhost' view v1 as select t1.a from t t1, t t2 where t1.a=t2.a;") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPHintsScope(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tk.MustExec("select /*+ MPP_1PHASE_AGG() */ a, sum(b) from t group by a, c") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The agg can not push down to the MPP side, the MPP_1PHASE_AGG() hint is invalid")) - tk.MustExec("select /*+ MPP_2PHASE_AGG() */ a, sum(b) from t group by a, c") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The agg can not push down to the MPP side, the MPP_2PHASE_AGG() hint is invalid")) - tk.MustExec("select /*+ shuffle_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The join can not push down to the MPP side, the shuffle_join() hint is invalid")) - tk.MustExec("select /*+ broadcast_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 The join can not push down to the MPP side, the broadcast_join() hint is invalid")) - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPBCJModel(t *testing.T) { - /* - if there are 3 mpp stores, planner won't choose broadcast join enven if `tidb_prefer_broadcast_join_by_exchange_data_size` is ON - broadcast exchange size: - Build: 2 * sizeof(Data) - Probe: 0 - exchange size: Build = 2 * sizeof(Data) - hash exchange size: - Build: sizeof(Data) * 2 / 3 - Probe: sizeof(Data) * 2 / 3 - exchange size: Build + Probe = 4/3 * sizeof(Data) - */ - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) - { - cnt, err := store.GetMPPClient().GetMPPStoreCount() - require.Equal(t, cnt, 3) - require.Nil(t, err) - } - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPPreferBCJ(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int)") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2 (b int)") - - tk.MustExec("insert into t1 values (1);") - tk.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8);") - - { - tk.MustExec("alter table t1 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t1") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - { - tk.MustExec("alter table t2 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t2") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - { - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } - } -} - -func TestMPPBCJModelOneTiFlash(t *testing.T) { - /* - if there are 1 mpp stores, planner should choose broadcast join if `tidb_prefer_broadcast_join_by_exchange_data_size` is ON - broadcast exchange size: - Build: 0 * sizeof(Data) - Probe: 0 - exchange size: Build = 0 * sizeof(Data) - hash exchange size: - Build: sizeof(Data) * 0 / 1 - Probe: sizeof(Data) * 0 / 1 - exchange size: Build + Probe = 0 * sizeof(Data) - */ - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(1)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - { - cnt, err := store.GetMPPClient().GetMPPStoreCount() - require.Equal(t, cnt, 1) - require.Nil(t, err) - } - { - tk.MustExecToErr("set @@session.tidb_prefer_broadcast_join_by_exchange_data_size=-1") - tk.MustExecToErr("set @@session.tidb_prefer_broadcast_join_by_exchange_data_size=2") - } - { - // no BCJ if `tidb_prefer_broadcast_join_by_exchange_data_size` is OFF - tk.MustExec("set @@session.tidb_broadcast_join_threshold_size=0") - tk.MustExec("set @@session.tidb_broadcast_join_threshold_count=0") - } - - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "UPDATE") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } -} - -func TestMPPRightSemiJoin(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int)") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2 (b int)") - - tk.MustExec("insert into t1 values (1);") - tk.MustExec("insert into t2 values (1), (2), (3), (4), (5), (6), (7), (8);") - - { - tk.MustExec("alter table t1 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t1") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - { - tk.MustExec("alter table t2 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t2") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - { - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } - } -} - -func TestMPPRightOuterJoin(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(3)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int, c int)") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2 (b int, d int)") - - tk.MustExec("insert into t1 values (1, 10), (2, 20), (3, 30), (4, 40), (5, 50);") - tk.MustExec("insert into t2 values (1, 12), (2, 18), (7, 66);") - - { - tk.MustExec("alter table t1 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t1") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - { - tk.MustExec("alter table t2 set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t2") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - } - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - { - var input []string - var output []struct { - SQL string - Plan []string - Warn []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - }) - if strings.HasPrefix(tt, "set") || strings.HasPrefix(tt, "insert") { - tk.MustExec(tt) - continue - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(tt).Rows()) - output[i].Warn = testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) - }) - res := tk.MustQuery(tt) - res.Check(testkit.Rows(output[i].Plan...)) - require.Equal(t, output[i].Warn, testdata.ConvertSQLWarnToStrings(tk.Session().GetSessionVars().StmtCtx.GetWarnings())) - } - } -} - -func TestHintScope(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`set @@tidb_opt_advanced_join_hint=0`) - - var input []string - var output []struct { - SQL string - Best string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(context.Background(), tk.Session(), stmt, is) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = test - output[i].Best = core.ToString(p) - }) - require.Equal(t, output[i].Best, core.ToString(p)) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - require.Len(t, warnings, 0, comment) - } -} - -func TestJoinHints(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - - var input []string - var output []struct { - SQL string - Best string - Warning string - Hints string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.Background() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - - testdata.OnRecord(func() { - output[i].SQL = test - output[i].Best = core.ToString(p) - if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() - } - output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) - }) - require.Equal(t, output[i].Best, core.ToString(p)) - if output[i].Warning == "" { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) - require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) - require.Equal(t, output[i].Warning, warnings[0].Err.Error()) - } - hints := core.GenHintsFromPhysicalPlan(p) - - // test the new genHints code - flat := core.FlattenPhysicalPlan(p, false) - newHints := core.GenHintsFromFlatPlan(flat) - assertSameHints(t, hints, newHints) - - require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) - } -} - -func TestAggregationHints(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - sessionVars := tk.Session().GetSessionVars() - sessionVars.SetHashAggFinalConcurrency(1) - sessionVars.SetHashAggPartialConcurrency(1) - - var input []struct { - SQL string - AggPushDown bool - } - var output []struct { - SQL string - Best string - Warning string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.Background() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, test := range input { - comment := fmt.Sprintf("case: %v sql: %v", i, test) - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - tk.Session().GetSessionVars().AllowAggPushDown = test.AggPushDown - - stmt, err := p.ParseOneStmt(test.SQL, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - - testdata.OnRecord(func() { - output[i].SQL = test.SQL - output[i].Best = core.ToString(p) - if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() - } - }) - require.Equal(t, output[i].Best, core.ToString(p), comment) - if output[i].Warning == "" { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) - require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) - require.Equal(t, output[i].Warning, warnings[0].Err.Error()) - } - } -} - -func TestSemiJoinRewriteHints(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t(a int, b int, c int)") - - sessionVars := tk.Session().GetSessionVars() - sessionVars.SetHashAggFinalConcurrency(1) - sessionVars.SetHashAggPartialConcurrency(1) - - var input []string - var output []struct { - SQL string - Plan []string - Warning string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.Background() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, test := range input { - comment := fmt.Sprintf("case: %v sql: %v", i, test) - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - - _, _, err = planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - - testdata.OnRecord(func() { - output[i].SQL = test - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief'" + test).Rows()) - if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() - } - }) - tk.MustQuery("explain format = 'brief'" + test).Check(testkit.Rows(output[i].Plan...)) - if output[i].Warning == "" { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) - require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) - require.Equal(t, output[i].Warning, warnings[0].Err.Error()) - } - } -} - -func TestAggToCopHint(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists ta") - tk.MustExec("create table ta(a int, b int, index(a))") - - var ( - input []string - output []struct { - SQL string - Best string - Warning string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - ctx := context.Background() - is := domain.GetDomain(tk.Session()).InfoSchema() - p := parser.New() - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - testdata.OnRecord(func() { - output[i].SQL = test - }) - require.Equal(t, output[i].SQL, test, comment) - - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err, comment) - planString := core.ToString(p) - testdata.OnRecord(func() { - output[i].Best = planString - }) - require.Equal(t, output[i].Best, planString, comment) - - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - testdata.OnRecord(func() { - if len(warnings) > 0 { - output[i].Warning = warnings[0].Err.Error() - } - }) - if output[i].Warning == "" { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, 1, fmt.Sprintf("%v", warnings)) - require.Equal(t, stmtctx.WarnLevelWarning, warnings[0].Level) - require.Equal(t, output[i].Warning, warnings[0].Err.Error()) - } - } -} - -func TestLimitToCopHint(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists tn") - tk.MustExec("create table tn(a int, b int, c int, d int, key (a, b, c, d))") - tk.MustExec(`set tidb_opt_limit_push_down_threshold=0`) - - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - - comment := fmt.Sprintf("case:%v sql:%s", i, ts) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - testdata.OnRecord(func() { - if len(warnings) > 0 { - output[i].Warning = make([]string, len(warnings)) - for j, warning := range warnings { - output[i].Warning[j] = warning.Err.Error() - } - } - }) - if len(output[i].Warning) == 0 { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, len(output[i].Warning), comment) - for j, warning := range warnings { - require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) - require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) - } - } - } -} - -func TestCTEMergeHint(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists tc") - tk.MustExec("drop table if exists te") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop table if exists t2") - tk.MustExec("drop table if exists t3") - tk.MustExec("drop table if exists t4") - tk.MustExec("drop view if exists v") - tk.MustExec("create table tc(a int)") - tk.MustExec("create table te(c int)") - tk.MustExec("create table t1(a int)") - tk.MustExec("create table t2(b int)") - tk.MustExec("create table t3(c int)") - tk.MustExec("create table t4(d int)") - tk.MustExec("insert into tc values (1), (5), (10), (15), (20), (30), (50);") - tk.MustExec("insert into te values (1), (5), (10), (25), (40), (60), (100);") - tk.MustExec("insert into t1 values (1), (5), (10), (25), (40), (60), (100);") - tk.MustExec("insert into t2 values (1), (5), (10), (25), (40), (60), (100);") - tk.MustExec("insert into t3 values (1), (5), (10), (25), (40), (60), (100);") - tk.MustExec("insert into t4 values (1), (5), (10), (25), (40), (60), (100);") - tk.MustExec("analyze table tc;") - tk.MustExec("analyze table te;") - tk.MustExec("analyze table t1;") - tk.MustExec("analyze table t2;") - tk.MustExec("analyze table t3;") - tk.MustExec("analyze table t4;") - tk.MustExec("create definer='root'@'localhost' view v as select * from tc") - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - - comment := fmt.Sprintf("case:%v sql:%s", i, ts) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - testdata.OnRecord(func() { - if len(warnings) > 0 { - output[i].Warning = make([]string, len(warnings)) - for j, warning := range warnings { - output[i].Warning[j] = warning.Err.Error() - } - } - }) - if len(output[i].Warning) == 0 { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, len(output[i].Warning), comment) - for j, warning := range warnings { - require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) - require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) - } - } - } -} - -func TestForceInlineCTE(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t;") - tk.MustExec("CREATE TABLE `t` (`a` int(11));") - tk.MustExec("insert into t values (1), (5), (10), (15), (20), (30), (50);") - - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - }) - if strings.HasPrefix(ts, "set") { - tk.MustExec(ts) - continue - } - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - - comment := fmt.Sprintf("case:%v sql:%s", i, ts) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - testdata.OnRecord(func() { - if len(warnings) > 0 { - output[i].Warning = make([]string, len(warnings)) - for j, warning := range warnings { - output[i].Warning[j] = warning.Err.Error() - } - } - }) - if len(output[i].Warning) == 0 { - require.Len(t, warnings, 0) - } else { - require.Len(t, warnings, len(output[i].Warning), comment) - for j, warning := range warnings { - require.Equal(t, stmtctx.WarnLevelWarning, warning.Level, comment) - require.Equal(t, output[i].Warning[j], warning.Err.Error(), comment) - } - } - } -} - -func TestSingleConsumerCTE(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("CREATE TABLE `t` (`a` int(11));") - tk.MustExec("insert into t values (1), (5), (10), (15), (20), (30), (50);") - - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - }) - if strings.HasPrefix(ts, "set") { - tk.MustExec(ts) - continue - } - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestPushdownDistinctEnableAggPushDownDisable(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - vars := []string{ - fmt.Sprintf("set @@session.%s = 1", variable.TiDBOptDistinctAggPushDown), - "set session tidb_opt_agg_push_down = 0", - "set tidb_cost_model_version=2", - } - doTestPushdownDistinct(t, vars, input, output) -} - -func TestGroupConcatOrderby(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists test;") - tk.MustExec("create table test(id int, name int)") - tk.MustExec("insert into test values(1, 10);") - tk.MustExec("insert into test values(1, 20);") - tk.MustExec("insert into test values(1, 30);") - tk.MustExec("insert into test values(2, 20);") - tk.MustExec("insert into test values(3, 200);") - tk.MustExec("insert into test values(3, 500);") - - tk.MustExec("drop table if exists ptest;") - tk.MustExec("CREATE TABLE ptest (id int,name int) PARTITION BY RANGE ( id ) " + - "(PARTITION `p0` VALUES LESS THAN (2), PARTITION `p1` VALUES LESS THAN (11))") - tk.MustExec("insert into ptest select * from test;") - tk.MustExec(fmt.Sprintf("set session tidb_opt_distinct_agg_push_down = %v", 1)) - tk.MustExec(fmt.Sprintf("set session tidb_opt_agg_push_down = %v", 1)) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) - } -} - -func TestIndexHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - var input []string - var output []struct { - SQL string - Best string - HasWarn bool - Hints string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.Background() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = test - output[i].Best = core.ToString(p) - output[i].HasWarn = len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) > 0 - output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) - }) - require.Equal(t, output[i].Best, core.ToString(p), comment) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - if output[i].HasWarn { - require.Len(t, warnings, 1, comment) - } else { - require.Len(t, warnings, 0, comment) - } - hints := core.GenHintsFromPhysicalPlan(p) - - // test the new genHints code - flat := core.FlattenPhysicalPlan(p, false) - newHints := core.GenHintsFromFlatPlan(flat) - assertSameHints(t, hints, newHints) - - require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) - } -} - -func TestIndexMergeHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - - var input []string - var output []struct { - SQL string - Best string - HasWarn bool - Hints string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.Background() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, test := range input { - comment := fmt.Sprintf("case:%v sql:%s", i, test) - tk.Session().GetSessionVars().StmtCtx.SetWarnings(nil) - stmt, err := p.ParseOneStmt(test, "", "") - require.NoError(t, err, comment) - sctx := tk.Session() - err = executor.ResetContextOfStmt(sctx, stmt) - require.NoError(t, err) - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = test - output[i].Best = core.ToString(p) - output[i].HasWarn = len(tk.Session().GetSessionVars().StmtCtx.GetWarnings()) > 0 - output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) - }) - require.Equal(t, output[i].Best, core.ToString(p), comment) - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - if output[i].HasWarn { - require.Len(t, warnings, 1, comment) - } else { - require.Len(t, warnings, 0, comment) - } - hints := core.GenHintsFromPhysicalPlan(p) - - // test the new genHints code - flat := core.FlattenPhysicalPlan(p, false) - newHints := core.GenHintsFromFlatPlan(flat) - assertSameHints(t, hints, newHints) - - require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) - } -} - -func TestQueryBlockHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - - var input []string - var output []struct { - SQL string - Plan string - Hints string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - ctx := context.TODO() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, tt := range input { - comment := fmt.Sprintf("case:%v sql: %s", i, tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err, comment) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = core.ToString(p) - output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) - }) - require.Equal(t, output[i].Plan, core.ToString(p), comment) - hints := core.GenHintsFromPhysicalPlan(p) - - // test the new genHints code - flat := core.FlattenPhysicalPlan(p, false) - newHints := core.GenHintsFromFlatPlan(flat) - assertSameHints(t, hints, newHints) - - require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) - } -} - -func TestInlineProjection(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`drop table if exists test.t1, test.t2;`) - tk.MustExec(`create table test.t1(a bigint, b bigint, index idx_a(a), index idx_b(b));`) - tk.MustExec(`create table test.t2(a bigint, b bigint, index idx_a(a), index idx_b(b));`) - - var input []string - var output []struct { - SQL string - Plan string - Hints string - } - is := domain.GetDomain(tk.Session()).InfoSchema() - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - ctx := context.Background() - p := parser.New() - - for i, tt := range input { - comment := fmt.Sprintf("case:%v sql: %s", i, tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err, comment) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = core.ToString(p) - output[i].Hints = hint.RestoreOptimizerHints(core.GenHintsFromPhysicalPlan(p)) - }) - require.Equal(t, output[i].Plan, core.ToString(p), comment) - hints := core.GenHintsFromPhysicalPlan(p) - - // test the new genHints code - flat := core.FlattenPhysicalPlan(p, false) - newHints := core.GenHintsFromFlatPlan(flat) - assertSameHints(t, hints, newHints) - - require.Equal(t, output[i].Hints, hint.RestoreOptimizerHints(hints), comment) - } -} - -func TestIndexJoinHint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`drop table if exists test.t1, test.t2, test.t;`) - tk.MustExec(`create table test.t1(a bigint, b bigint, index idx_a(a), index idx_b(b));`) - tk.MustExec(`create table test.t2(a bigint, b bigint, index idx_a(a), index idx_b(b));`) - tk.MustExec("CREATE TABLE `t` ( `a` bigint(20) NOT NULL, `b` tinyint(1) DEFAULT NULL, `c` datetime DEFAULT NULL, `d` int(10) unsigned DEFAULT NULL, `e` varchar(20) DEFAULT NULL, `f` double DEFAULT NULL, `g` decimal(30,5) DEFAULT NULL, `h` float DEFAULT NULL, `i` date DEFAULT NULL, `j` timestamp NULL DEFAULT NULL, PRIMARY KEY (`a`), UNIQUE KEY `b` (`b`), KEY `c` (`c`,`d`,`e`), KEY `f` (`f`), KEY `g` (`g`,`h`), KEY `g_2` (`g`), UNIQUE KEY `g_3` (`g`), KEY `i` (`i`) );") - - var input []string - var output []struct { - SQL string - Plan string - } - - is := domain.GetDomain(tk.Session()).InfoSchema() - p := parser.New() - ctx := context.Background() - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - comment := fmt.Sprintf("case:%v sql: %s", i, tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err, comment) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = core.ToString(p) - }) - require.Equal(t, output[i].Plan, core.ToString(p), comment) - } -} - -func TestNominalSort(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - var input []string - var output []struct { - SQL string - Plan []string - Result []string - } - tk.MustExec("create table t (a int, b int, index idx_a(a), index idx_b(b))") - tk.MustExec("insert into t values(1, 1)") - tk.MustExec("insert into t values(1, 2)") - tk.MustExec("insert into t values(2, 4)") - tk.MustExec("insert into t values(3, 5)") - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Check(testkit.Rows(output[i].Result...)) - } -} - -func TestHintFromDiffDatabase(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`drop table if exists test.t1`) - tk.MustExec(`create table test.t1(a bigint, index idx_a(a));`) - tk.MustExec(`create table test.t2(a bigint, index idx_a(a));`) - tk.MustExec("drop database if exists test2") - tk.MustExec("create database test2") - tk.MustExec("use test2") - - var input []string - var output []struct { - SQL string - Plan string - } - is := domain.GetDomain(tk.Session()).InfoSchema() - p := parser.New() - ctx := context.Background() - - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - comment := fmt.Sprintf("case:%v sql: %s", i, tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - p, _, err := planner.Optimize(ctx, tk.Session(), stmt, is) - require.NoError(t, err, comment) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = core.ToString(p) - }) - require.Equal(t, output[i].Plan, core.ToString(p), comment) - } -} - -func TestNthPlanHintWithExplain(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`drop table if exists test.tt`) - tk.MustExec(`create table test.tt (a int,b int, index(a), index(b));`) - tk.MustExec("insert into tt values (1, 1), (2, 2), (3, 4)") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - - var input []string - var output []struct { - SQL string - Plan []string - } - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } - - // This assertion makes sure a query with or without nth_plan() hint output exactly the same plan(including plan ID). - // The query below is the same as queries in the testdata except for nth_plan() hint. - // Currently, its output is the same as the second test case in the testdata, which is `output[1]`. If this doesn't - // hold in the future, you may need to modify this. - tk.MustQuery("explain format = 'brief' select * from test.tt where a=1 and b=1").Check(testkit.Rows(output[1].Plan...)) -} - -func TestEnumIndex(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(e enum('c','b','a',''), index idx(e))") - tk.MustExec("insert ignore into t values(0),(1),(2),(3),(4);") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestIssue27233(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `PK_S_MULTI_31` (\n `COL1` tinyint(45) NOT NULL,\n `COL2` tinyint(45) NOT NULL,\n PRIMARY KEY (`COL1`,`COL2`) /*T![clustered_index] NONCLUSTERED */\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustExec("insert into PK_S_MULTI_31 values(122,100),(124,-22),(124,34),(127,103);") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestSelectionPartialPushDown(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int as (a+1) virtual)") - tk.MustExec("create table t2(a int, b int as (a+1) virtual, c int, key idx_a(a))") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIssue28316(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestSkewDistinctAgg(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t` (`a` int(11), `b` int(11), `c` int(11), `d` date)") - tk.MustExec("insert into t (a,b,c,d) value(1,4,5,'2019-06-01')") - tk.MustExec("insert into t (a,b,c,d) value(2,null,1,'2019-07-01')") - tk.MustExec("insert into t (a,b,c,d) value(3,4,5,'2019-08-01')") - tk.MustExec("insert into t (a,b,c,d) value(3,6,2,'2019-09-01')") - tk.MustExec("insert into t (a,b,c,d) value(10,4,null,'2020-06-01')") - tk.MustExec("insert into t (a,b,c,d) value(20,null,1,'2020-07-01')") - tk.MustExec("insert into t (a,b,c,d) value(30,4,5,'2020-08-01')") - tk.MustExec("insert into t (a,b,c,d) value(30,6,5,'2020-09-01')") - tk.MustQuery("select date_format(d,'%Y') as df, sum(a), count(b), count(distinct c) " + - "from t group by date_format(d,'%Y') order by df;").Check( - testkit.Rows("2019 9 3 3", "2020 90 3 2")) - tk.MustExec("set @@tidb_opt_skew_distinct_agg=1") - tk.MustQuery("select date_format(d,'%Y') as df, sum(a), count(b), count(distinct c) " + - "from t group by date_format(d,'%Y') order by df;").Check( - testkit.Rows("2019 9 3 3", "2020 90 3 2")) - tk.MustQuery("select count(distinct b), sum(c) from t group by a order by 1,2;").Check( - testkit.Rows("0 1", "0 1", "1 ", "1 5", "2 7", "2 10")) - tk.MustQuery("select count(distinct b) from t group by date_format(d,'%Y') order by 1;").Check( - testkit.Rows("2", "2")) - tk.MustQuery("select count(a), count(distinct b), max(b) from t group by date_format(d,'%Y') order by 1,2,3;").Check( - testkit.Rows("4 2 6", "4 2 6")) - tk.MustQuery("select count(a), count(distinct b), max(b) from t group by date_format(d,'%Y'),c order by 1,2,3;").Check( - testkit.Rows("1 0 ", "1 0 ", "1 1 4", "1 1 6", "2 1 4", "2 2 6")) - tk.MustQuery("select avg(distinct b), count(a), sum(b) from t group by date_format(d,'%Y'),c order by 1,2,3;").Check( - testkit.Rows(" 1 ", " 1 ", "4.0000 1 4", "4.0000 2 8", "5.0000 2 10", "6.0000 1 6")) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief' " + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestHJBuildAndProbeHint(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1(a int primary key, b int not null)") - tk.MustExec("create table t2(a int primary key, b int not null)") - tk.MustExec("create table t3(a int primary key, b int not null)") - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - tk.MustExec("insert into t3 values(1,1),(2,1)") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} - -func TestHJBuildAndProbeHint4StaticPartitionTable(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - tk.MustExec("insert into t3 values(1,1),(2,1)") - tk.MustExec(`set @@tidb_partition_prune_mode="static"`) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestHJBuildAndProbeHint4DynamicPartitionTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - tk.MustExec("insert into t3 values(1,1),(2,1)") - tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - } -} - -func TestHJBuildAndProbeHint4TiFlash(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1(a int primary key, b int not null)") - tk.MustExec("create table t2(a int primary key, b int not null)") - tk.MustExec("create table t3(a int primary key, b int not null)") - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - tk.MustExec("insert into t3 values(1,1),(2,1)") - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tableName := tblInfo.Name.L - if tableName == "t1" || tableName == "t2" || tableName == "t3" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestMPPSinglePartitionType(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists employee") - tk.MustExec("create table employee(empid int, deptid int, salary decimal(10,2))") - tk.MustExec("set tidb_enforce_mpp=0") - - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "employee" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - }) - if strings.HasPrefix(ts, "set") { - tk.MustExec(ts) - continue - } - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format='brief'" + ts).Rows()) - }) - tk.MustQuery("explain format='brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestNoDecorrelateHint(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Result []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int primary key, b int)") - tk.MustExec("create table t3(a int, b int)") - tk.MustExec("insert into t1 values(1,1),(2,2)") - tk.MustExec("insert into t2 values(1,1),(2,1)") - tk.MustExec("insert into t3 values(1,1),(2,1)") - - tk.MustExec("create table ta(id int, code int, name varchar(20), index idx_ta_id(id),index idx_ta_name(name), index idx_ta_code(code))") - tk.MustExec("create table tb(id int, code int, name varchar(20), index idx_tb_id(id),index idx_tb_name(name))") - tk.MustExec("create table tc(id int, code int, name varchar(20), index idx_tc_id(id),index idx_tc_name(name))") - tk.MustExec("create table td(id int, code int, name varchar(20), index idx_tc_id(id),index idx_tc_name(name))") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Result = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Sort().Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery(ts).Sort().Check(testkit.Rows(output[i].Result...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} - -func TestCountStarForTikv(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("create table t (a int(11) not null, b varchar(10) not null, c date not null, d char(1) not null, e bigint not null, f datetime not null, g bool not null, h bool )") - tk.MustExec("create table t_pick_row_id (a char(20) not null)") - - // tikv - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestCountStarForTiFlash(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("create table t (a int(11) not null, b varchar(10) not null, c date not null, d char(1) not null, e bigint not null, f datetime not null, g bool not null, h bool )") - tk.MustExec("create table t_pick_row_id (a char(20) not null)") - - // tiflash - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tableName := tblInfo.Name.L - if tableName == "t" || tableName == "t_pick_row_id" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestHashAggPushdownToTiFlashCompute(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists tbl_15;") - tk.MustExec(`create table tbl_15 (col_89 text (473) collate utf8mb4_bin , - col_90 timestamp default '1976-04-03' , - col_91 tinyint unsigned not null , - col_92 tinyint , - col_93 double not null , - col_94 datetime not null default '1970-06-08' , - col_95 datetime default '2028-02-13' , - col_96 int unsigned not null default 2532480521 , - col_97 char (168) default '') partition by hash (col_91) partitions 4;`) - - tk.MustExec("drop table if exists tbl_16;") - tk.MustExec(`create table tbl_16 (col_98 text (246) not null , - col_99 decimal (30 ,19) , - col_100 mediumint unsigned , - col_101 text (410) collate utf8mb4_bin , - col_102 date not null , - col_103 timestamp not null default '2003-08-27' , - col_104 text (391) not null , - col_105 date default '2010-10-24' , - col_106 text (9) not null,primary key (col_100, col_98(5), col_103), - unique key idx_23 (col_100, col_106 (3), col_101 (3))) partition by hash (col_100) partitions 2;`) - - config.UpdateGlobal(func(conf *config.Config) { - conf.DisaggregatedTiFlash = true - }) - defer config.UpdateGlobal(func(conf *config.Config) { - conf.DisaggregatedTiFlash = false - }) - - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tableName := tblInfo.Name.L - if tableName == "tbl_15" || tableName == "tbl_16" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_partition_prune_mode = 'static';") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - } -} - -func TestIndexMergeOrderPushDown(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("create table t (a int, b int, c int, index idx(a, c), index idx2(b, c))") - tk.MustExec("create table tcommon (a int, b int, c int, primary key(a, c), index idx2(b, c))") - tk.MustExec("create table thash(a int, b int, c int, index idx_ac(a, c), index idx_bc(b, c)) PARTITION BY HASH (`a`) PARTITIONS 4") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + ts).Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} - -func TestIndexMergeSinkLimit(t *testing.T) { - var ( - input []string - output []struct { - SQL string - Plan []string - Warning []string - } - ) - planSuiteData := GetPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec(" CREATE TABLE `t2` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT NULL, `c` int(11) DEFAULT NULL, KEY `a` (`a`), KEY `b` (`b`)) ") - tk.MustExec("insert into t2 values(1,2,1),(2,1,1),(3,3,1)") - tk.MustExec("create table t(a int, j json, index kj((cast(j as signed array))))") - tk.MustExec("insert into t values(1, '[1,2,3]')") - - for i, ts := range input { - testdata.OnRecord(func() { - output[i].SQL = ts - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery(ts).Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery(ts).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} diff --git a/planner/core/casetest/plan_test.go b/planner/core/casetest/plan_test.go deleted file mode 100644 index a1e855bea225d..0000000000000 --- a/planner/core/casetest/plan_test.go +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package casetest - -import ( - "encoding/json" - "strings" - "testing" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/plancodec" - "github.com/stretchr/testify/require" -) - -func getPlanRows(planStr string) []string { - planStr = strings.Replace(planStr, "\t", " ", -1) - return strings.Split(planStr, "\n") -} - -func compareStringSlice(t *testing.T, ss1, ss2 []string) { - require.Equal(t, len(ss1), len(ss2)) - for i, s := range ss1 { - require.Equal(t, len(s), len(ss2[i])) - } -} - -func TestPreferRangeScan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // affect this ut: tidb_opt_prefer_range_scan - tk.MustExec("drop table if exists test;") - tk.MustExec("create table test(`id` int(10) NOT NULL AUTO_INCREMENT,`name` varchar(50) NOT NULL DEFAULT 'tidb',`age` int(11) NOT NULL,`addr` varchar(50) DEFAULT 'The ocean of stars',PRIMARY KEY (`id`),KEY `idx_age` (`age`))") - tk.MustExec("insert into test(age) values(5);") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("insert into test(name,age,addr) select name,age,addr from test;") - tk.MustExec("analyze table test;") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - var input []string - var output []struct { - SQL string - Plan []string - } - planNormalizedSuiteData := GetPlanNormalizedSuiteData() - planNormalizedSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - if i == 0 { - tk.MustExec("set session tidb_opt_prefer_range_scan=0") - } else if i == 1 { - tk.MustExec("set session tidb_opt_prefer_range_scan=1") - } - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(tt) - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(core.Plan) - require.True(t, ok) - normalized, digest := core.NormalizePlan(p) - - // test the new normalization code - flat := core.FlattenPhysicalPlan(p, false) - newNormalized, newDigest := core.NormalizeFlatPlan(flat) - require.Equal(t, normalized, newNormalized) - require.Equal(t, digest, newDigest) - - normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) - normalizedPlanRows := getPlanRows(normalizedPlan) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = normalizedPlanRows - }) - compareStringSlice(t, normalizedPlanRows, output[i].Plan) - } -} - -func TestNormalizedPlan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode='static';") - tk.MustExec("drop table if exists t1,t2,t3,t4") - tk.MustExec("create table t1 (a int key,b int,c int, index (b));") - tk.MustExec("create table t2 (a int key,b int,c int, index (b));") - tk.MustExec("create table t3 (a int key,b int) partition by hash(a) partitions 2;") - tk.MustExec("create table t4 (a int, b int, index(a)) partition by range(a) (partition p0 values less than (10),partition p1 values less than MAXVALUE);") - tk.MustExec("set @@global.tidb_enable_foreign_key=1") - tk.MustExec("set @@foreign_key_checks=1") - tk.MustExec("create table t5 (id int key, id2 int, id3 int, unique index idx2(id2), index idx3(id3));") - tk.MustExec("create table t6 (id int, id2 int, id3 int, index idx_id(id), index idx_id2(id2), " + - "foreign key fk_1 (id) references t5(id) ON UPDATE CASCADE ON DELETE CASCADE, " + - "foreign key fk_2 (id2) references t5(id2) ON UPDATE CASCADE, " + - "foreign key fk_3 (id3) references t5(id3) ON DELETE CASCADE);") - tk.MustExec("insert into t5 values (1,1,1), (2,2,2)") - var input []string - var output []struct { - SQL string - Plan []string - } - planNormalizedSuiteData := GetPlanNormalizedSuiteData() - planNormalizedSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(tt) - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(core.Plan) - require.True(t, ok) - normalized, digest := core.NormalizePlan(p) - - // test the new normalization code - flat := core.FlattenPhysicalPlan(p, false) - newNormalized, newDigest := core.NormalizeFlatPlan(flat) - require.Equal(t, normalized, newNormalized) - require.Equal(t, digest, newDigest) - // Test for GenHintsFromFlatPlan won't panic. - core.GenHintsFromFlatPlan(flat) - - normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) - normalizedPlanRows := getPlanRows(normalizedPlan) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = normalizedPlanRows - }) - compareStringSlice(t, normalizedPlanRows, output[i].Plan) - } -} - -func TestNormalizedPlanForDiffStore(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int, b int, c int, primary key(a))") - tk.MustExec("insert into t1 values(1,1,1), (2,2,2), (3,3,3)") - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - var input []string - var output []struct { - Digest string - Plan []string - } - planNormalizedSuiteData := GetPlanNormalizedSuiteData() - planNormalizedSuiteData.LoadTestCases(t, &input, &output) - lastDigest := "" - for i, tt := range input { - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(tt) - info := tk.Session().ShowProcess() - require.NotNil(t, info) - ep, ok := info.Plan.(*core.Explain) - require.True(t, ok) - normalized, digest := core.NormalizePlan(ep.TargetPlan) - - // test the new normalization code - flat := core.FlattenPhysicalPlan(ep.TargetPlan, false) - newNormalized, newPlanDigest := core.NormalizeFlatPlan(flat) - require.Equal(t, digest, newPlanDigest) - require.Equal(t, normalized, newNormalized) - - normalizedPlan, err := plancodec.DecodeNormalizedPlan(normalized) - normalizedPlanRows := getPlanRows(normalizedPlan) - require.NoError(t, err) - testdata.OnRecord(func() { - output[i].Digest = digest.String() - output[i].Plan = normalizedPlanRows - }) - compareStringSlice(t, normalizedPlanRows, output[i].Plan) - require.NotEqual(t, digest.String(), lastDigest) - lastDigest = digest.String() - } -} - -func TestJSONPlanInExplain(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(id int, key(id))") - tk.MustExec("create table t2(id int, key(id))") - - var input []string - var output []struct { - SQL string - JSONPlan []*core.ExplainInfoForEncode - } - planSuiteData := GetJSONPlanSuiteData() - planSuiteData.LoadTestCases(t, &input, &output) - - for i, test := range input { - resJSON := tk.MustQuery(test).Rows() - var res []*core.ExplainInfoForEncode - require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), &res)) - for j, expect := range output[i].JSONPlan { - require.Equal(t, expect.ID, res[j].ID) - require.Equal(t, expect.EstRows, res[j].EstRows) - require.Equal(t, expect.ActRows, res[j].ActRows) - require.Equal(t, expect.TaskType, res[j].TaskType) - require.Equal(t, expect.AccessObject, res[j].AccessObject) - require.Equal(t, expect.OperatorInfo, res[j].OperatorInfo) - } - } -} diff --git a/planner/core/casetest/planstats/BUILD.bazel b/planner/core/casetest/planstats/BUILD.bazel deleted file mode 100644 index 954d4734fe294..0000000000000 --- a/planner/core/casetest/planstats/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "planstats_test", - timeout = "short", - srcs = [ - "main_test.go", - "plan_stats_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 4, - deps = [ - "//config", - "//domain", - "//executor", - "//parser", - "//parser/model", - "//planner", - "//planner/core", - "//sessionctx", - "//sessionctx/stmtctx", - "//statistics", - "//statistics/handle", - "//table", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/planstats/main_test.go b/planner/core/casetest/planstats/main_test.go deleted file mode 100644 index d474b1eb0c058..0000000000000 --- a/planner/core/casetest/planstats/main_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package planstats_test - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_stats_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetPlanStatsData() testdata.TestData { - return testDataMap["plan_stats_suite"] -} diff --git a/planner/core/casetest/pushdown/BUILD.bazel b/planner/core/casetest/pushdown/BUILD.bazel deleted file mode 100644 index cdd18ad38ea0f..0000000000000 --- a/planner/core/casetest/pushdown/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "pushdown_test", - timeout = "short", - srcs = [ - "main_test.go", - "push_down_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 6, - deps = [ - "//domain", - "//parser/model", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/pushdown/main_test.go b/planner/core/casetest/pushdown/main_test.go deleted file mode 100644 index 3e9ec6eb95535..0000000000000 --- a/planner/core/casetest/pushdown/main_test.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package pushdown - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} diff --git a/planner/core/casetest/rule/BUILD.bazel b/planner/core/casetest/rule/BUILD.bazel deleted file mode 100644 index e609a7493e0f5..0000000000000 --- a/planner/core/casetest/rule/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "rule_test", - timeout = "short", - srcs = [ - "main_test.go", - "rule_derive_topn_from_window_test.go", - "rule_inject_extra_projection_test.go", - "rule_join_reorder_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 5, - deps = [ - "//domain", - "//expression", - "//expression/aggregation", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner/core/internal", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/mock", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/rule/main_test.go b/planner/core/casetest/rule/main_test.go deleted file mode 100644 index 627ac9b43b17c..0000000000000 --- a/planner/core/casetest/rule/main_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package rule - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "derive_topn_from_window") - testDataMap.LoadTestSuiteData("testdata", "join_reorder_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetDerivedTopNSuiteData() testdata.TestData { - return testDataMap["derive_topn_from_window"] -} - -func GetJoinReorderSuiteData() testdata.TestData { - return testDataMap["join_reorder_suite"] -} diff --git a/planner/core/casetest/rule/rule_join_reorder_test.go b/planner/core/casetest/rule/rule_join_reorder_test.go deleted file mode 100644 index 32f653ffccf07..0000000000000 --- a/planner/core/casetest/rule/rule_join_reorder_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package rule - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func runJoinReorderTestData(t *testing.T, tk *testkit.TestKit, name string) { - var input []string - var output []struct { - SQL string - Plan []string - Warning []string - } - joinReorderSuiteData := GetJoinReorderSuiteData() - joinReorderSuiteData.LoadTestCasesByName(name, t, &input, &output) - require.Equal(t, len(input), len(output)) - for i := range input { - testdata.OnRecord(func() { - output[i].SQL = input[i] - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + input[i]).Rows()) - output[i].Warning = testdata.ConvertRowsToStrings(tk.MustQuery("show warnings").Rows()) - }) - tk.MustQuery("explain format = 'brief' " + input[i]).Check(testkit.Rows(output[i].Plan...)) - tk.MustQuery("show warnings").Check(testkit.Rows(output[i].Warning...)) - } -} - -// test the global/session variable tidb_opt_enable_hash_join being set to no -func TestOptEnableHashJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_opt_enable_hash_join=off") - tk.MustExec("create table t1(a int, b int, key(a));") - tk.MustExec("create table t2(a int, b int, key(a));") - tk.MustExec("create table t3(a int, b int, key(a));") - tk.MustExec("create table t4(a int, b int, key(a));") - runJoinReorderTestData(t, tk, "TestOptEnableHashJoin") -} - -func TestJoinOrderHint4TiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec("create table t(a int, b int, key(a));") - tk.MustExec("create table t1(a int, b int, key(a));") - tk.MustExec("create table t2(a int, b int, key(a));") - tk.MustExec("create table t3(a int, b int, key(a));") - tk.MustExec("create table t4(a int, b int, key(a));") - tk.MustExec("create table t5(a int, b int, key(a));") - tk.MustExec("create table t6(a int, b int, key(a));") - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - tableName := tblInfo.Name.L - if tableName == "t" || tableName == "t1" || tableName == "t2" || tableName == "t3" || tableName == "t4" || tableName == "t5" || tableName == "t6" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - runJoinReorderTestData(t, tk, "TestJoinOrderHint4TiFlash") -} - -func TestJoinOrderHint4DynamicPartitionTable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec(`create table t(a int, b int) partition by hash(a) partitions 3`) - tk.MustExec(`create table t1(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t2(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t3(a int, b int) partition by hash(b) partitions 3`) - tk.MustExec(`create table t4(a int, b int) partition by hash(a) partitions 4`) - tk.MustExec(`create table t5(a int, b int) partition by hash(a) partitions 5`) - tk.MustExec(`create table t6(a int, b int) partition by hash(b) partitions 3`) - - tk.MustExec(`set @@tidb_partition_prune_mode="dynamic"`) - tk.MustExec("set @@tidb_enable_outer_join_reorder=true") - runJoinReorderTestData(t, tk, "TestJoinOrderHint4DynamicPartitionTable") -} diff --git a/planner/core/casetest/scalarsubquery/BUILD.bazel b/planner/core/casetest/scalarsubquery/BUILD.bazel deleted file mode 100644 index 2ae01097534c6..0000000000000 --- a/planner/core/casetest/scalarsubquery/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "scalarsubquery_test", - timeout = "short", - srcs = [ - "cases_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - deps = [ - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/scalarsubquery/main_test.go b/planner/core/casetest/scalarsubquery/main_test.go deleted file mode 100644 index c790cd0b2264f..0000000000000 --- a/planner/core/casetest/scalarsubquery/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package scalarsubquery - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetPlanSuiteData() testdata.TestData { - return testDataMap["plan_suite"] -} diff --git a/planner/core/casetest/stats_test.go b/planner/core/casetest/stats_test.go deleted file mode 100644 index 8e58dbfbe82eb..0000000000000 --- a/planner/core/casetest/stats_test.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package casetest - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/util/hint" - "github.com/stretchr/testify/require" -) - -func TestGroupNDVs(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int not null, b int not null, key(a,b))") - tk.MustExec("insert into t1 values(1,1),(1,2),(2,1),(2,2),(1,1)") - tk.MustExec("create table t2(a int not null, b int not null, key(a,b))") - tk.MustExec("insert into t2 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3),(1,1)") - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - - ctx := context.Background() - p := parser.New() - var input []string - var output []struct { - SQL string - AggInput string - JoinInput string - } - statsSuiteData := GetStatsSuiteData() - statsSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - comment := fmt.Sprintf("case:%v sql: %s", i, tt) - stmt, err := p.ParseOneStmt(tt, "", "") - require.NoError(t, err, comment) - ret := &core.PreprocessorReturn{} - err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(ret)) - require.NoError(t, err) - tk.Session().GetSessionVars().PlanColumnID.Store(0) - builder, _ := core.NewPlanBuilder().Init(tk.Session(), ret.InfoSchema, &hint.BlockHintProcessor{}) - p, err := builder.Build(ctx, stmt) - require.NoError(t, err, comment) - p, err = core.LogicalOptimizeTest(ctx, builder.GetOptFlag(), p.(core.LogicalPlan)) - require.NoError(t, err, comment) - lp := p.(core.LogicalPlan) - _, err = core.RecursiveDeriveStats4Test(lp) - require.NoError(t, err, comment) - var agg *core.LogicalAggregation - var join *core.LogicalJoin - stack := make([]core.LogicalPlan, 0, 2) - traversed := false - for !traversed { - switch v := lp.(type) { - case *core.LogicalAggregation: - agg = v - lp = lp.Children()[0] - case *core.LogicalJoin: - join = v - lp = v.Children()[0] - stack = append(stack, v.Children()[1]) - case *core.LogicalApply: - lp = lp.Children()[0] - stack = append(stack, v.Children()[1]) - case *core.LogicalUnionAll: - lp = lp.Children()[0] - for i := 1; i < len(v.Children()); i++ { - stack = append(stack, v.Children()[i]) - } - case *core.DataSource: - if len(stack) == 0 { - traversed = true - } else { - lp = stack[0] - stack = stack[1:] - } - default: - lp = lp.Children()[0] - } - } - aggInput := "" - joinInput := "" - if agg != nil { - s := core.GetStats4Test(agg.Children()[0]) - aggInput = property.ToString(s.GroupNDVs) - } - if join != nil { - l := core.GetStats4Test(join.Children()[0]) - r := core.GetStats4Test(join.Children()[1]) - joinInput = property.ToString(l.GroupNDVs) + ";" + property.ToString(r.GroupNDVs) - } - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].AggInput = aggInput - output[i].JoinInput = joinInput - }) - require.Equal(t, output[i].AggInput, aggInput, comment) - require.Equal(t, output[i].JoinInput, joinInput, comment) - } -} - -func TestNDVGroupCols(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int not null, b int not null, key(a,b))") - tk.MustExec("insert into t1 values(1,1),(1,2),(2,1),(2,2)") - tk.MustExec("create table t2(a int not null, b int not null, key(a,b))") - tk.MustExec("insert into t2 values(1,1),(1,2),(1,3),(2,1),(2,2),(2,3),(3,1),(3,2),(3,3)") - tk.MustExec("analyze table t1") - tk.MustExec("analyze table t2") - - // Default RPC encoding may cause statistics explain result differ and then the test unstable. - tk.MustExec("set @@tidb_enable_chunk_rpc = on") - - var input []string - var output []struct { - SQL string - Plan []string - } - statsSuiteData := GetStatsSuiteData() - statsSuiteData.LoadTestCases(t, &input, &output) - for i, tt := range input { - testdata.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = testdata.ConvertRowsToStrings(tk.MustQuery("explain format = 'brief' " + tt).Rows()) - }) - // The test point is the row count estimation for aggregations and joins. - tk.MustQuery("explain format = 'brief' " + tt).Check(testkit.Rows(output[i].Plan...)) - } -} diff --git a/planner/core/casetest/windows/BUILD.bazel b/planner/core/casetest/windows/BUILD.bazel deleted file mode 100644 index 54eece02b22cb..0000000000000 --- a/planner/core/casetest/windows/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "windows_test", - timeout = "short", - srcs = [ - "main_test.go", - "window_push_down_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - shard_count = 4, - deps = [ - "//domain", - "//planner/core/internal", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/casetest/windows/main_test.go b/planner/core/casetest/windows/main_test.go deleted file mode 100644 index 71e2336422a0e..0000000000000 --- a/planner/core/casetest/windows/main_test.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package windows - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "window_push_down_suite") - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func getWindowPushDownSuiteData() testdata.TestData { - return testDataMap["window_push_down_suite"] -} diff --git a/planner/core/cbo_test.go b/planner/core/cbo_test.go deleted file mode 100644 index cb8f46e32509e..0000000000000 --- a/planner/core/cbo_test.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "context" - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestExplainCostTrace(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - tk.MustExec("insert into t values (1)") - - tk.MustExec("set tidb_cost_model_version=2") - tk.MustQuery("explain format='cost_trace' select * from t").Check(testkit.Rows( - `TableReader_5 10000.00 177906.67 ((scan(10000*logrowsize(32)*tikv_scan_factor(40.7))) + (net(10000*rowsize(16)*tidb_kv_net_factor(3.96))))/15.00 root data:TableFullScan_4`, - `└─TableFullScan_4 10000.00 2035000.00 scan(10000*logrowsize(32)*tikv_scan_factor(40.7)) cop[tikv] table:t keep order:false, stats:pseudo`)) - tk.MustQuery("explain analyze format='cost_trace' select * from t").CheckAt([]int{0, 1, 2, 3, 4}, [][]interface{}{ - {"TableReader_5", "10000.00", "177906.67", "((scan(10000*logrowsize(32)*tikv_scan_factor(40.7))) + (net(10000*rowsize(16)*tidb_kv_net_factor(3.96))))/15.00", "1"}, - {"└─TableFullScan_4", "10000.00", "2035000.00", "scan(10000*logrowsize(32)*tikv_scan_factor(40.7))", "1"}, - }) - - tk.MustExec("set tidb_cost_model_version=1") - tk.MustQuery("explain format='cost_trace' select * from t").Check(testkit.Rows( - // cost trace on model ver1 is not supported - `TableReader_5 10000.00 34418.00 N/A root data:TableFullScan_4`, - `└─TableFullScan_4 10000.00 435000.00 N/A cop[tikv] table:t keep order:false, stats:pseudo`, - )) - tk.MustQuery("explain analyze format='cost_trace' select * from t").CheckAt([]int{0, 1, 2, 3, 4}, [][]interface{}{ - {"TableReader_5", "10000.00", "34418.00", "N/A", "1"}, - {"└─TableFullScan_4", "10000.00", "435000.00", "N/A", "1"}, - }) -} - -func TestExplainAnalyze(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set sql_mode='STRICT_TRANS_TABLES'") // disable only full group by - tk.MustExec("create table t1(a int, b int, c int, key idx(a, b))") - tk.MustExec("create table t2(a int, b int)") - tk.MustExec("insert into t1 values (1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)") - tk.MustExec("insert into t2 values (2, 22), (3, 33), (5, 55), (233, 2), (333, 3), (3434, 5)") - tk.MustExec("analyze table t1, t2") - rs := tk.MustQuery("explain analyze select t1.a, t1.b, sum(t1.c) from t1 join t2 on t1.a = t2.b where t1.a > 1") - require.Len(t, rs.Rows(), 10) - for _, row := range rs.Rows() { - require.Len(t, row, 9) - execInfo := row[5].(string) - require.Contains(t, execInfo, "time") - require.Contains(t, execInfo, "loops") - if strings.Contains(row[0].(string), "Reader") || strings.Contains(row[0].(string), "IndexLookUp") { - require.Contains(t, execInfo, "cop_task") - } - } -} - -func constructInsertSQL(i, n int) string { - sql := "insert into t (a,b,c,e)values " - for j := 0; j < n; j++ { - sql += fmt.Sprintf("(%d, %d, '%d', %d)", i*n+j, i, i+j, i*n+j) - if j != n-1 { - sql += ", " - } - } - return sql -} - -func BenchmarkOptimize(b *testing.B) { - store := testkit.CreateMockStore(b) - - testKit := testkit.NewTestKit(b, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t (a int primary key, b int, c varchar(200), d datetime DEFAULT CURRENT_TIMESTAMP, e int, ts timestamp DEFAULT CURRENT_TIMESTAMP)") - testKit.MustExec("create index b on t (b)") - testKit.MustExec("create index d on t (d)") - testKit.MustExec("create index e on t (e)") - testKit.MustExec("create index b_c on t (b,c)") - testKit.MustExec("create index ts on t (ts)") - for i := 0; i < 100; i++ { - testKit.MustExec(constructInsertSQL(i, 100)) - } - testKit.MustExec("analyze table t") - tests := []struct { - sql string - best string - }{ - { - sql: "select count(*) from t group by e", - best: "IndexReader(Index(t.e)[[NULL,+inf]])->StreamAgg", - }, - { - sql: "select count(*) from t where e <= 10 group by e", - best: "IndexReader(Index(t.e)[[-inf,10]])->StreamAgg", - }, - { - sql: "select count(*) from t where e <= 50", - best: "IndexReader(Index(t.e)[[-inf,50]]->HashAgg)->HashAgg", - }, - { - sql: "select count(*) from t where c > '1' group by b", - best: "IndexReader(Index(t.b_c)[[NULL,+inf]]->Sel([gt(test.t.c, 1)]))->StreamAgg", - }, - { - sql: "select count(*) from t where e = 1 group by b", - best: "IndexLookUp(Index(t.e)[[1,1]], Table(t)->HashAgg)->HashAgg", - }, - { - sql: "select count(*) from t where e > 1 group by b", - best: "TableReader(Table(t)->Sel([gt(test.t.e, 1)])->HashAgg)->HashAgg", - }, - { - sql: "select count(e) from t where t.b <= 20", - best: "IndexLookUp(Index(t.b)[[-inf,20]], Table(t)->HashAgg)->HashAgg", - }, - { - sql: "select count(e) from t where t.b <= 30", - best: "IndexLookUp(Index(t.b)[[-inf,30]], Table(t)->HashAgg)->HashAgg", - }, - { - sql: "select count(e) from t where t.b <= 40", - best: "IndexLookUp(Index(t.b)[[-inf,40]], Table(t)->HashAgg)->HashAgg", - }, - { - sql: "select count(e) from t where t.b <= 50", - best: "TableReader(Table(t)->Sel([le(test.t.b, 50)])->HashAgg)->HashAgg", - }, - { - sql: "select * from t where t.b <= 40", - best: "IndexLookUp(Index(t.b)[[-inf,40]], Table(t))", - }, - { - sql: "select * from t where t.b <= 50", - best: "TableReader(Table(t)->Sel([le(test.t.b, 50)]))", - }, - // test panic - { - sql: "select * from t where 1 and t.b <= 50", - best: "TableReader(Table(t)->Sel([le(test.t.b, 50)]))", - }, - { - sql: "select * from t where t.b <= 100 order by t.a limit 1", - best: "TableReader(Table(t)->Sel([le(test.t.b, 100)])->Limit)->Limit", - }, - { - sql: "select * from t where t.b <= 1 order by t.a limit 10", - best: "IndexLookUp(Index(t.b)[[-inf,1]]->TopN([test.t.a],0,10), Table(t))->TopN([test.t.a],0,10)", - }, - { - sql: "select * from t use index(b) where b = 1 order by a", - best: "IndexLookUp(Index(t.b)[[1,1]], Table(t))->Sort", - }, - // test datetime - { - sql: "select * from t where d < cast('1991-09-05' as datetime)", - best: "IndexLookUp(Index(t.d)[[-inf,1991-09-05 00:00:00)], Table(t))", - }, - // test timestamp - { - sql: "select * from t where ts < '1991-09-05'", - best: "IndexLookUp(Index(t.ts)[[-inf,1991-09-05 00:00:00)], Table(t))", - }, - } - for _, tt := range tests { - ctx := testKit.Session() - stmts, err := session.Parse(ctx, tt.sql) - require.NoError(b, err) - require.Len(b, stmts, 1) - stmt := stmts[0] - ret := &core.PreprocessorReturn{} - err = core.Preprocess(context.Background(), ctx, stmt, core.WithPreprocessorReturn(ret)) - require.NoError(b, err) - - b.Run(tt.sql, func(b *testing.B) { - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, err := planner.Optimize(context.TODO(), ctx, stmt, ret.InfoSchema) - require.NoError(b, err) - } - b.ReportAllocs() - }) - } -} - -func TestIssue9805(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec(` - create table t1 ( - id bigint primary key, - a bigint not null, - b varchar(100) not null, - c varchar(10) not null, - d bigint as (a % 30) not null, - key (d, b, c) - ) - `) - tk.MustExec(` - create table t2 ( - id varchar(50) primary key, - a varchar(100) unique, - b datetime, - c varchar(45), - d int not null unique auto_increment - ) - `) - // Test when both tables are empty, EXPLAIN ANALYZE for IndexLookUp would not panic. - tk.MustQuery("explain analyze select /*+ TIDB_INLJ(t2) */ t1.id, t2.a from t1 join t2 on t1.a = t2.d where t1.b = 't2' and t1.d = 4") -} diff --git a/planner/core/debugtrace.go b/planner/core/debugtrace.go deleted file mode 100644 index 609651b9c554d..0000000000000 --- a/planner/core/debugtrace.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package core - -import ( - "strconv" - "strings" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/util/hint" -) - -/* - Below is debug trace for the received command from the client. - It records the input to the optimizer at the very beginning of query optimization. -*/ - -type receivedCmdInfo struct { - Command string - ExecutedASTText string - ExecuteStmtInfo *executeInfo -} - -type executeInfo struct { - PreparedSQL string - BinaryParamsInfo []binaryParamInfo - UseCursor bool -} - -type binaryParamInfo struct { - Type string - Value string -} - -func (info *binaryParamInfo) MarshalJSON() ([]byte, error) { - type binaryParamInfoForMarshal binaryParamInfo - infoForMarshal := new(binaryParamInfoForMarshal) - quote := `"` - // We only need the escape functionality of strconv.Quote, the quoting is not needed, - // so we trim the \" prefix and suffix here. - infoForMarshal.Type = strings.TrimSuffix( - strings.TrimPrefix( - strconv.Quote(info.Type), - quote), - quote) - infoForMarshal.Value = strings.TrimSuffix( - strings.TrimPrefix( - strconv.Quote(info.Value), - quote), - quote) - return debugtrace.EncodeJSONCommon(infoForMarshal) -} - -// DebugTraceReceivedCommand records the received command from the client to the debug trace. -func DebugTraceReceivedCommand(s sessionctx.Context, cmd byte, stmtNode ast.StmtNode) { - sessionVars := s.GetSessionVars() - trace := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := new(receivedCmdInfo) - trace.AppendStepWithNameToCurrentContext(traceInfo, "Received Command") - traceInfo.Command = mysql.Command2Str[cmd] - traceInfo.ExecutedASTText = stmtNode.Text() - - // Collect information for execute stmt, and record it in executeInfo. - var binaryParams []expression.Expression - var planCacheStmt *PlanCacheStmt - if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { - if execStmt.PrepStmt != nil { - planCacheStmt, _ = execStmt.PrepStmt.(*PlanCacheStmt) - } - if execStmt.BinaryArgs != nil { - binaryParams, _ = execStmt.BinaryArgs.([]expression.Expression) - } - } - useCursor := mysql.HasCursorExistsFlag(sessionVars.Status) - // If none of them needs record, we don't need a executeInfo. - if binaryParams == nil && planCacheStmt == nil && !useCursor { - return - } - execInfo := &executeInfo{} - traceInfo.ExecuteStmtInfo = execInfo - execInfo.UseCursor = useCursor - if planCacheStmt != nil { - execInfo.PreparedSQL = planCacheStmt.StmtText - } - if len(binaryParams) > 0 { - execInfo.BinaryParamsInfo = make([]binaryParamInfo, len(binaryParams)) - for i, param := range binaryParams { - execInfo.BinaryParamsInfo[i].Type = param.GetType().String() - execInfo.BinaryParamsInfo[i].Value = param.String() - } - } -} - -/* - Below is debug trace for the hint that matches the current query. -*/ - -type bindingHint struct { - Hint *hint.HintsSet - trying bool -} - -func (b *bindingHint) MarshalJSON() ([]byte, error) { - tmp := make(map[string]string, 1) - hintStr, err := b.Hint.Restore() - if err != nil { - return debugtrace.EncodeJSONCommon(err) - } - if b.trying { - tmp["Trying Hint"] = hintStr - } else { - tmp["Best Hint"] = hintStr - } - return debugtrace.EncodeJSONCommon(tmp) -} - -// DebugTraceTryBinding records the hint that might be chosen to the debug trace. -func DebugTraceTryBinding(s sessionctx.Context, binding *hint.HintsSet) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &bindingHint{ - Hint: binding, - trying: true, - } - root.AppendStepToCurrentContext(traceInfo) -} - -// DebugTraceBestBinding records the chosen hint to the debug trace. -func DebugTraceBestBinding(s sessionctx.Context, binding *hint.HintsSet) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &bindingHint{ - Hint: binding, - trying: false, - } - root.AppendStepToCurrentContext(traceInfo) -} - -/* - Below is debug trace for getStatsTable(). - Part of the logic for collecting information is in statistics/debug_trace.go. -*/ - -type getStatsTblInfo struct { - TableName string - TblInfoID int64 - InputPhysicalID int64 - HandleIsNil bool - UsePartitionStats bool - CountIsZero bool - Uninitialized bool - Outdated bool - StatsTblInfo *statistics.StatsTblTraceInfo -} - -func debugTraceGetStatsTbl( - s sessionctx.Context, - tblInfo *model.TableInfo, - pid int64, - handleIsNil, - usePartitionStats, - countIsZero, - uninitialized, - outdated bool, - statsTbl *statistics.Table, -) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &getStatsTblInfo{ - TableName: tblInfo.Name.O, - TblInfoID: tblInfo.ID, - InputPhysicalID: pid, - HandleIsNil: handleIsNil, - UsePartitionStats: usePartitionStats, - CountIsZero: countIsZero, - Uninitialized: uninitialized, - Outdated: outdated, - StatsTblInfo: statistics.TraceStatsTbl(statsTbl), - } - failpoint.Inject("DebugTraceStableStatsTbl", func(val failpoint.Value) { - if val.(bool) { - stabilizeGetStatsTblInfo(traceInfo) - } - }) - root.AppendStepToCurrentContext(traceInfo) -} - -// Only for test. -func stabilizeGetStatsTblInfo(info *getStatsTblInfo) { - info.TblInfoID = 100 - info.InputPhysicalID = 100 - tbl := info.StatsTblInfo - if tbl == nil { - return - } - tbl.PhysicalID = 100 - tbl.Version = 440930000000000000 - for _, col := range tbl.Columns { - col.LastUpdateVersion = 440930000000000000 - } - for _, idx := range tbl.Indexes { - idx.LastUpdateVersion = 440930000000000000 - } -} - -/* - Below is debug trace for AccessPath. -*/ - -type accessPathForDebugTrace struct { - IndexName string `json:",omitempty"` - AccessConditions []string - IndexFilters []string - TableFilters []string - PartialPaths []accessPathForDebugTrace `json:",omitempty"` - CountAfterAccess float64 - CountAfterIndex float64 -} - -func convertAccessPathForDebugTrace(path *util.AccessPath, out *accessPathForDebugTrace) { - if path.Index != nil { - out.IndexName = path.Index.Name.O - } - out.AccessConditions = expression.ExprsToStringsForDisplay(path.AccessConds) - out.IndexFilters = expression.ExprsToStringsForDisplay(path.IndexFilters) - out.TableFilters = expression.ExprsToStringsForDisplay(path.TableFilters) - out.CountAfterAccess = path.CountAfterAccess - out.CountAfterIndex = path.CountAfterIndex - out.PartialPaths = make([]accessPathForDebugTrace, len(path.PartialIndexPaths)) - for i, partialPath := range path.PartialIndexPaths { - convertAccessPathForDebugTrace(partialPath, &out.PartialPaths[i]) - } -} - -func debugTraceAccessPaths(s sessionctx.Context, paths []*util.AccessPath) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := make([]accessPathForDebugTrace, len(paths)) - for i, partialPath := range paths { - convertAccessPathForDebugTrace(partialPath, &traceInfo[i]) - } - root.AppendStepWithNameToCurrentContext(traceInfo, "Access paths") -} diff --git a/planner/core/enforce_mpp_test.go b/planner/core/enforce_mpp_test.go deleted file mode 100644 index 91d9c612abca8..0000000000000 --- a/planner/core/enforce_mpp_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "fmt" - "strconv" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestRowSizeInMPP(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a varchar(10), b varchar(20), c varchar(256))") - tk.MustExec("insert into t values (space(10), space(20), space(256))") - tk.MustExec("analyze table t") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec(`set @@tidb_opt_tiflash_concurrency_factor=1`) - tk.MustExec(`set @@tidb_allow_mpp=1`) - var costs [3]float64 - for i, col := range []string{"a", "b", "c"} { - rs := tk.MustQuery(fmt.Sprintf(`explain format='verbose' select /*+ read_from_storage(tiflash[t]) */ %v from t`, col)).Rows() - cost, err := strconv.ParseFloat(rs[0][2].(string), 64) - require.NoError(t, err) - costs[i] = cost - } - require.True(t, costs[0] < costs[1] && costs[1] < costs[2]) // rowSize can affect the final cost -} diff --git a/planner/core/errors.go b/planner/core/errors.go deleted file mode 100644 index 75dd70616fba5..0000000000000 --- a/planner/core/errors.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package core - -import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -// error definitions. -var ( - ErrUnsupportedType = dbterror.ClassOptimizer.NewStd(mysql.ErrUnsupportedType) - ErrAnalyzeMissIndex = dbterror.ClassOptimizer.NewStd(mysql.ErrAnalyzeMissIndex) - ErrAnalyzeMissColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrAnalyzeMissColumn) - ErrWrongParamCount = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongParamCount) - ErrSchemaChanged = dbterror.ClassOptimizer.NewStd(mysql.ErrSchemaChanged) - ErrTablenameNotAllowedHere = dbterror.ClassOptimizer.NewStd(mysql.ErrTablenameNotAllowedHere) - ErrNotSupportedYet = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedYet) - ErrWrongUsage = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongUsage) - ErrUnknown = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknown) - ErrUnknownTable = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknownTable) - ErrNoSuchTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNoSuchTable) - ErrViewRecursive = dbterror.ClassOptimizer.NewStd(mysql.ErrViewRecursive) - ErrWrongArguments = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongArguments) - ErrWrongNumberOfColumnsInSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongNumberOfColumnsInSelect) - ErrBadGeneratedColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrBadGeneratedColumn) - ErrFieldNotInGroupBy = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldNotInGroupBy) - ErrAggregateOrderNonAggQuery = dbterror.ClassOptimizer.NewStd(mysql.ErrAggregateOrderNonAggQuery) - ErrFieldInOrderNotSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldInOrderNotSelect) - ErrAggregateInOrderNotSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrAggregateInOrderNotSelect) - ErrBadTable = dbterror.ClassOptimizer.NewStd(mysql.ErrBadTable) - ErrKeyDoesNotExist = dbterror.ClassOptimizer.NewStd(mysql.ErrKeyDoesNotExist) - ErrOperandColumns = dbterror.ClassOptimizer.NewStd(mysql.ErrOperandColumns) - ErrInvalidGroupFuncUse = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidGroupFuncUse) - ErrIllegalReference = dbterror.ClassOptimizer.NewStd(mysql.ErrIllegalReference) - ErrNoDB = dbterror.ClassOptimizer.NewStd(mysql.ErrNoDB) - ErrUnknownExplainFormat = dbterror.ClassOptimizer.NewStd(mysql.ErrUnknownExplainFormat) - ErrWrongGroupField = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongGroupField) - ErrDupFieldName = dbterror.ClassOptimizer.NewStd(mysql.ErrDupFieldName) - ErrNonUpdatableTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonUpdatableTable) - ErrMultiUpdateKeyConflict = dbterror.ClassOptimizer.NewStd(mysql.ErrMultiUpdateKeyConflict) - ErrInternal = dbterror.ClassOptimizer.NewStd(mysql.ErrInternal) - ErrNonUniqTable = dbterror.ClassOptimizer.NewStd(mysql.ErrNonuniqTable) - ErrWindowInvalidWindowFuncUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowInvalidWindowFuncUse) - ErrWindowInvalidWindowFuncAliasUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowInvalidWindowFuncAliasUse) - ErrWindowNoSuchWindow = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoSuchWindow) - ErrWindowCircularityInWindowGraph = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowCircularityInWindowGraph) - ErrWindowNoChildPartitioning = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoChildPartitioning) - ErrWindowNoInherentFrame = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoInherentFrame) - ErrWindowNoRedefineOrderBy = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowNoRedefineOrderBy) - ErrWindowDuplicateName = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowDuplicateName) - ErrPartitionClauseOnNonpartitioned = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionClauseOnNonpartitioned) - ErrWindowFrameStartIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameStartIllegal) - ErrWindowFrameEndIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameEndIllegal) - ErrWindowFrameIllegal = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFrameIllegal) - ErrWindowRangeFrameOrderType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameOrderType) - ErrWindowRangeFrameTemporalType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameTemporalType) - ErrWindowRangeFrameNumericType = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeFrameNumericType) - ErrWindowRangeBoundNotConstant = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRangeBoundNotConstant) - ErrWindowRowsIntervalUse = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowRowsIntervalUse) - ErrWindowFunctionIgnoresFrame = dbterror.ClassOptimizer.NewStd(mysql.ErrWindowFunctionIgnoresFrame) - ErrInvalidNumberOfArgs = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidNumberOfArgs) - ErrFieldInGroupingNotGroupBy = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldInGroupingNotGroupBy) - ErrUnsupportedOnGeneratedColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrUnsupportedOnGeneratedColumn) - ErrPrivilegeCheckFail = dbterror.ClassOptimizer.NewStd(mysql.ErrPrivilegeCheckFail) - ErrInvalidWildCard = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidWildCard) - ErrMixOfGroupFuncAndFields = dbterror.ClassOptimizer.NewStd(mysql.ErrMixOfGroupFuncAndFieldsIncompatible) - errTooBigPrecision = dbterror.ClassExpression.NewStd(mysql.ErrTooBigPrecision) - ErrDBaccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrDBaccessDenied) - ErrTableaccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrTableaccessDenied) - ErrSpecificAccessDenied = dbterror.ClassOptimizer.NewStd(mysql.ErrSpecificAccessDenied) - ErrViewNoExplain = dbterror.ClassOptimizer.NewStd(mysql.ErrViewNoExplain) - ErrWrongValueCountOnRow = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongValueCountOnRow) - ErrViewInvalid = dbterror.ClassOptimizer.NewStd(mysql.ErrViewInvalid) - ErrNoSuchThread = dbterror.ClassOptimizer.NewStd(mysql.ErrNoSuchThread) - ErrUnknownColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrBadField) - ErrCartesianProductUnsupported = dbterror.ClassOptimizer.NewStd(mysql.ErrCartesianProductUnsupported) - ErrStmtNotFound = dbterror.ClassOptimizer.NewStd(mysql.ErrPreparedStmtNotFound) - ErrAmbiguous = dbterror.ClassOptimizer.NewStd(mysql.ErrNonUniq) - ErrUnresolvedHintName = dbterror.ClassOptimizer.NewStd(mysql.ErrUnresolvedHintName) - ErrNotHintUpdatable = dbterror.ClassOptimizer.NewStd(mysql.ErrNotHintUpdatable) - ErrWarnConflictingHint = dbterror.ClassOptimizer.NewStd(mysql.ErrWarnConflictingHint) - ErrCTERecursiveRequiresUnion = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveRequiresUnion) - ErrCTERecursiveRequiresNonRecursiveFirst = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveRequiresNonRecursiveFirst) - ErrCTERecursiveForbidsAggregation = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveForbidsAggregation) - ErrCTERecursiveForbiddenJoinOrder = dbterror.ClassOptimizer.NewStd(mysql.ErrCTERecursiveForbiddenJoinOrder) - ErrInvalidRequiresSingleReference = dbterror.ClassOptimizer.NewStd(mysql.ErrInvalidRequiresSingleReference) - ErrSQLInReadOnlyMode = dbterror.ClassOptimizer.NewStd(mysql.ErrReadOnlyMode) - // Since we cannot know if user logged in with a password, use message of ErrAccessDeniedNoPassword instead - ErrAccessDenied = dbterror.ClassOptimizer.NewStdErr(mysql.ErrAccessDenied, mysql.MySQLErrName[mysql.ErrAccessDeniedNoPassword]) - ErrBadNull = dbterror.ClassOptimizer.NewStd(mysql.ErrBadNull) - ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) - ErrAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) - ErrOptOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnTemporaryTable) - ErrOptOnCacheTable = dbterror.ClassOptimizer.NewStd(mysql.ErrOptOnCacheTable) - ErrDropTableOnTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrDropTableOnTemporaryTable) - // ErrPartitionNoTemporary returns when partition at temporary mode - ErrPartitionNoTemporary = dbterror.ClassOptimizer.NewStd(mysql.ErrPartitionNoTemporary) - ErrViewSelectTemporaryTable = dbterror.ClassOptimizer.NewStd(mysql.ErrViewSelectTmptable) - ErrSubqueryMoreThan1Row = dbterror.ClassOptimizer.NewStd(mysql.ErrSubqueryNo1Row) - ErrKeyPart0 = dbterror.ClassOptimizer.NewStd(mysql.ErrKeyPart0) - ErrGettingNoopVariable = dbterror.ClassOptimizer.NewStd(mysql.ErrGettingNoopVariable) - - ErrPrepareMulti = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareMulti) - ErrUnsupportedPs = dbterror.ClassExecutor.NewStd(mysql.ErrUnsupportedPs) - ErrPsManyParam = dbterror.ClassExecutor.NewStd(mysql.ErrPsManyParam) - ErrPrepareDDL = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareDDL) - ErrRowIsReferenced2 = dbterror.ClassOptimizer.NewStd(mysql.ErrRowIsReferenced2) - ErrNoReferencedRow2 = dbterror.ClassOptimizer.NewStd(mysql.ErrNoReferencedRow2) -) diff --git a/planner/core/errors_test.go b/planner/core/errors_test.go deleted file mode 100644 index 0eb26d2ed2702..0000000000000 --- a/planner/core/errors_test.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package core - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/require" -) - -func TestError(t *testing.T) { - kvErrs := []*terror.Error{ - ErrUnsupportedType, - ErrAnalyzeMissIndex, - ErrAnalyzeMissColumn, - ErrWrongParamCount, - ErrSchemaChanged, - ErrTablenameNotAllowedHere, - ErrNotSupportedYet, - ErrWrongUsage, - ErrUnknownTable, - ErrWrongArguments, - ErrWrongNumberOfColumnsInSelect, - ErrBadGeneratedColumn, - ErrFieldNotInGroupBy, - ErrBadTable, - ErrKeyDoesNotExist, - ErrOperandColumns, - ErrInvalidGroupFuncUse, - ErrIllegalReference, - ErrNoDB, - ErrUnknownExplainFormat, - ErrWrongGroupField, - ErrDupFieldName, - ErrNonUpdatableTable, - ErrInternal, - ErrNonUniqTable, - ErrWindowInvalidWindowFuncUse, - ErrWindowInvalidWindowFuncAliasUse, - ErrWindowNoSuchWindow, - ErrWindowCircularityInWindowGraph, - ErrWindowNoChildPartitioning, - ErrWindowNoInherentFrame, - ErrWindowNoRedefineOrderBy, - ErrWindowDuplicateName, - ErrPartitionClauseOnNonpartitioned, - ErrWindowFrameStartIllegal, - ErrWindowFrameEndIllegal, - ErrWindowFrameIllegal, - ErrWindowRangeFrameOrderType, - ErrWindowRangeFrameTemporalType, - ErrWindowRangeFrameNumericType, - ErrWindowRangeBoundNotConstant, - ErrWindowRowsIntervalUse, - ErrWindowFunctionIgnoresFrame, - ErrUnsupportedOnGeneratedColumn, - ErrPrivilegeCheckFail, - ErrInvalidWildCard, - ErrMixOfGroupFuncAndFields, - ErrDBaccessDenied, - ErrTableaccessDenied, - ErrSpecificAccessDenied, - ErrViewNoExplain, - ErrWrongValueCountOnRow, - ErrViewInvalid, - ErrNoSuchThread, - ErrUnknownColumn, - ErrCartesianProductUnsupported, - ErrStmtNotFound, - ErrAmbiguous, - ErrKeyPart0, - } - for _, err := range kvErrs { - code := terror.ToSQLError(err).Code - require.Truef(t, code != mysql.ErrUnknown && code == uint16(err.Code()), "err: %v", err) - } -} diff --git a/planner/core/explain.go b/planner/core/explain.go deleted file mode 100644 index 54579bb7456dd..0000000000000 --- a/planner/core/explain.go +++ /dev/null @@ -1,1112 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core - -import ( - "bytes" - "fmt" - "strconv" - "strings" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -// ExplainInfo implements Plan interface. -func (p *PhysicalLock) ExplainInfo() string { - var str strings.Builder - str.WriteString(p.Lock.LockType.String()) - str.WriteString(" ") - str.WriteString(strconv.FormatUint(p.Lock.WaitSec, 10)) - return str.String() -} - -// ExplainID overrides the ExplainID in order to match different range. -func (p *PhysicalIndexScan) ExplainID() fmt.Stringer { - return stringutil.MemoizeStr(func() string { - if p.SCtx() != nil && p.SCtx().GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { - return p.TP() - } - return p.TP() + "_" + strconv.Itoa(p.ID()) - }) -} - -// TP overrides the TP in order to match different range. -func (p *PhysicalIndexScan) TP() string { - if p.isFullScan() { - return plancodec.TypeIndexFullScan - } - return plancodec.TypeIndexRangeScan -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexScan) ExplainInfo() string { - return p.AccessObject().String() + ", " + p.OperatorInfo(false) -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalIndexScan) ExplainNormalizedInfo() string { - return p.AccessObject().NormalizedString() + ", " + p.OperatorInfo(true) -} - -// OperatorInfo implements dataAccesser interface. -func (p *PhysicalIndexScan) OperatorInfo(normalized bool) string { - var buffer strings.Builder - if len(p.rangeInfo) > 0 { - if !normalized { - buffer.WriteString("range: decided by ") - buffer.WriteString(p.rangeInfo) - buffer.WriteString(", ") - } - } else if p.haveCorCol() { - if normalized { - buffer.WriteString("range: decided by ") - buffer.Write(expression.SortedExplainNormalizedExpressionList(p.AccessCondition)) - buffer.WriteString(", ") - } else { - buffer.WriteString("range: decided by [") - for i, expr := range p.AccessCondition { - if i != 0 { - buffer.WriteString(" ") - } - buffer.WriteString(expr.String()) - } - buffer.WriteString("], ") - } - } else if len(p.Ranges) > 0 { - if normalized { - buffer.WriteString("range:[?,?], ") - } else if !p.isFullScan() { - buffer.WriteString("range:") - for _, idxRange := range p.Ranges { - buffer.WriteString(idxRange.String()) - buffer.WriteString(", ") - } - } - } - buffer.WriteString("keep order:") - buffer.WriteString(strconv.FormatBool(p.KeepOrder)) - if p.Desc { - buffer.WriteString(", desc") - } - if !normalized { - if p.usedStatsInfo != nil { - str := p.usedStatsInfo.FormatForExplain() - if len(str) > 0 { - buffer.WriteString(", ") - buffer.WriteString(str) - } - } else if p.StatsInfo().StatsVersion == statistics.PseudoVersion { - // This branch is not needed in fact, we add this to prevent test result changes under planner/cascades/ - buffer.WriteString(", stats:pseudo") - } - } - return buffer.String() -} - -func (p *PhysicalIndexScan) haveCorCol() bool { - for _, cond := range p.AccessCondition { - if len(expression.ExtractCorColumns(cond)) > 0 { - return true - } - } - return false -} - -func (p *PhysicalIndexScan) isFullScan() bool { - if len(p.rangeInfo) > 0 || p.haveCorCol() { - return false - } - for _, ran := range p.Ranges { - if !ran.IsFullRange(false) { - return false - } - } - return true -} - -// ExplainID overrides the ExplainID in order to match different range. -func (p *PhysicalTableScan) ExplainID() fmt.Stringer { - return stringutil.MemoizeStr(func() string { - if p.SCtx() != nil && p.SCtx().GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { - return p.TP() - } - return p.TP() + "_" + strconv.Itoa(p.ID()) - }) -} - -// TP overrides the TP in order to match different range. -func (p *PhysicalTableScan) TP() string { - if p.isChildOfIndexLookUp { - return plancodec.TypeTableRowIDScan - } else if p.isFullScan() { - return plancodec.TypeTableFullScan - } - return plancodec.TypeTableRangeScan -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalTableScan) ExplainInfo() string { - return p.AccessObject().String() + ", " + p.OperatorInfo(false) -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalTableScan) ExplainNormalizedInfo() string { - return p.AccessObject().NormalizedString() + ", " + p.OperatorInfo(true) -} - -// OperatorInfo implements dataAccesser interface. -func (p *PhysicalTableScan) OperatorInfo(normalized bool) string { - var buffer strings.Builder - if len(p.rangeInfo) > 0 { - // TODO: deal with normalized case - buffer.WriteString("range: decided by ") - buffer.WriteString(p.rangeInfo) - buffer.WriteString(", ") - } else if p.haveCorCol() { - if normalized { - buffer.WriteString("range: decided by ") - buffer.Write(expression.SortedExplainNormalizedExpressionList(p.AccessCondition)) - buffer.WriteString(", ") - } else { - buffer.WriteString("range: decided by [") - for i, AccessCondition := range p.AccessCondition { - if i != 0 { - buffer.WriteString(" ") - } - buffer.WriteString(AccessCondition.String()) - } - buffer.WriteString("], ") - } - } else if len(p.Ranges) > 0 { - if normalized { - buffer.WriteString("range:[?,?], ") - } else if !p.isFullScan() { - buffer.WriteString("range:") - for _, idxRange := range p.Ranges { - buffer.WriteString(idxRange.String()) - buffer.WriteString(", ") - } - } - } - if p.SCtx().GetSessionVars().EnableLateMaterialization && len(p.filterCondition) > 0 && p.StoreType == kv.TiFlash { - buffer.WriteString("pushed down filter:") - if len(p.lateMaterializationFilterCondition) > 0 { - if normalized { - buffer.Write(expression.SortedExplainNormalizedExpressionList(p.lateMaterializationFilterCondition)) - } else { - buffer.Write(expression.SortedExplainExpressionList(p.lateMaterializationFilterCondition)) - } - } else { - buffer.WriteString("empty") - } - buffer.WriteString(", ") - } - buffer.WriteString("keep order:") - buffer.WriteString(strconv.FormatBool(p.KeepOrder)) - if p.Desc { - buffer.WriteString(", desc") - } - if !normalized { - if p.usedStatsInfo != nil { - str := p.usedStatsInfo.FormatForExplain() - if len(str) > 0 { - buffer.WriteString(", ") - buffer.WriteString(str) - } - } else if p.StatsInfo().StatsVersion == statistics.PseudoVersion { - // This branch is not needed in fact, we add this to prevent test result changes under planner/cascades/ - buffer.WriteString(", stats:pseudo") - } - } - if p.StoreType == kv.TiFlash && p.Table.GetPartitionInfo() != nil && p.IsMPPOrBatchCop && p.SCtx().GetSessionVars().StmtCtx.UseDynamicPartitionPrune() { - buffer.WriteString(", PartitionTableScan:true") - } - if len(p.runtimeFilterList) > 0 { - buffer.WriteString(", runtime filter:") - for i, runtimeFilter := range p.runtimeFilterList { - if i != 0 { - buffer.WriteString(", ") - } - buffer.WriteString(runtimeFilter.ExplainInfo(false)) - } - } - return buffer.String() -} - -func (p *PhysicalTableScan) haveCorCol() bool { - for _, cond := range p.AccessCondition { - if len(expression.ExtractCorColumns(cond)) > 0 { - return true - } - } - return false -} - -func (p *PhysicalTableScan) isFullScan() bool { - if len(p.rangeInfo) > 0 || p.haveCorCol() { - return false - } - var unsignedIntHandle bool - if p.Table.PKIsHandle { - if pkColInfo := p.Table.GetPkColInfo(); pkColInfo != nil { - unsignedIntHandle = mysql.HasUnsignedFlag(pkColInfo.GetFlag()) - } - } - for _, ran := range p.Ranges { - if !ran.IsFullRange(unsignedIntHandle) { - return false - } - } - return true -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalTableReader) ExplainInfo() string { - tablePlanInfo := "data:" + p.tablePlan.ExplainID().String() - - if p.ReadReqType == MPP { - return fmt.Sprintf("MppVersion: %d, %s", p.SCtx().GetSessionVars().ChooseMppVersion(), tablePlanInfo) - } - - return tablePlanInfo -} - -// ExplainNormalizedInfo implements Plan interface. -func (*PhysicalTableReader) ExplainNormalizedInfo() string { - return "" -} - -// OperatorInfo return other operator information to be explained. -func (p *PhysicalTableReader) OperatorInfo(_ bool) string { - return "data:" + p.tablePlan.ExplainID().String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexReader) ExplainInfo() string { - return "index:" + p.indexPlan.ExplainID().String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalIndexReader) ExplainNormalizedInfo() string { - return "index:" + p.indexPlan.TP() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexLookUpReader) ExplainInfo() string { - var str strings.Builder - // The children can be inferred by the relation symbol. - if p.PushedLimit != nil { - str.WriteString("limit embedded(offset:") - str.WriteString(strconv.FormatUint(p.PushedLimit.Offset, 10)) - str.WriteString(", count:") - str.WriteString(strconv.FormatUint(p.PushedLimit.Count, 10)) - str.WriteString(")") - } - return str.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexMergeReader) ExplainInfo() string { - var str strings.Builder - if p.IsIntersectionType { - str.WriteString("type: intersection") - } else { - str.WriteString("type: union") - } - if p.PushedLimit != nil { - str.WriteString(", limit embedded(offset:") - str.WriteString(strconv.FormatUint(p.PushedLimit.Offset, 10)) - str.WriteString(", count:") - str.WriteString(strconv.FormatUint(p.PushedLimit.Count, 10)) - str.WriteString(")") - } - return str.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalUnionScan) ExplainInfo() string { - return string(expression.SortedExplainExpressionList(p.Conditions)) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalSelection) ExplainInfo() string { - exprStr := string(expression.SortedExplainExpressionList(p.Conditions)) - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - exprStr += fmt.Sprintf(", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return exprStr -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalSelection) ExplainNormalizedInfo() string { - return string(expression.SortedExplainNormalizedExpressionList(p.Conditions)) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalProjection) ExplainInfo() string { - exprStr := expression.ExplainExpressionList(p.Exprs, p.schema) - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - exprStr += fmt.Sprintf(", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return exprStr -} - -func (p *PhysicalExpand) explainInfoV2() string { - sb := strings.Builder{} - for i, oneL := range p.LevelExprs { - if i == 0 { - sb.WriteString("level-projection:") - sb.WriteString("[") - sb.WriteString(expression.ExplainExpressionList(oneL, p.schema)) - sb.WriteString("]") - } else { - sb.WriteString(",[") - sb.WriteString(expression.ExplainExpressionList(oneL, p.schema)) - sb.WriteString("]") - } - } - sb.WriteString("; schema: [") - colStrs := make([]string, 0, len(p.schema.Columns)) - for _, col := range p.schema.Columns { - colStrs = append(colStrs, col.String()) - } - sb.WriteString(strings.Join(colStrs, ",")) - sb.WriteString("]") - return sb.String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalProjection) ExplainNormalizedInfo() string { - return string(expression.SortedExplainNormalizedExpressionList(p.Exprs)) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalTableDual) ExplainInfo() string { - var str strings.Builder - str.WriteString("rows:") - str.WriteString(strconv.Itoa(p.RowCount)) - return str.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalSort) ExplainInfo() string { - buffer := bytes.NewBufferString("") - buffer = explainByItems(buffer, p.ByItems) - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalLimit) ExplainInfo() string { - buffer := bytes.NewBufferString("") - if len(p.GetPartitionBy()) > 0 { - buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) - fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) - } else { - fmt.Fprintf(buffer, "offset:%v, count:%v", p.Offset, p.Count) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalExpand) ExplainInfo() string { - if len(p.LevelExprs) > 0 { - return p.explainInfoV2() - } - var str strings.Builder - str.WriteString("group set num:") - str.WriteString(strconv.FormatInt(int64(len(p.GroupingSets)), 10)) - str.WriteString(", groupingID:") - str.WriteString(p.GroupingIDCol.String()) - str.WriteString(", ") - str.WriteString(p.GroupingSets.String()) - return str.String() -} - -// ExplainInfo implements Plan interface. -func (p *basePhysicalAgg) ExplainInfo() string { - return p.explainInfo(false) -} - -func (p *basePhysicalAgg) explainInfo(normalized bool) string { - sortedExplainExpressionList := expression.SortedExplainExpressionList - if normalized { - sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList - } - - builder := &strings.Builder{} - if len(p.GroupByItems) > 0 { - builder.WriteString("group by:") - builder.Write(sortedExplainExpressionList(p.GroupByItems)) - builder.WriteString(", ") - } - for i := 0; i < len(p.AggFuncs); i++ { - builder.WriteString("funcs:") - var colName string - if normalized { - colName = p.schema.Columns[i].ExplainNormalizedInfo() - } else { - colName = p.schema.Columns[i].ExplainInfo() - } - builder.WriteString(aggregation.ExplainAggFunc(p.AggFuncs[i], normalized)) - builder.WriteString("->") - builder.WriteString(colName) - if i+1 < len(p.AggFuncs) { - builder.WriteString(", ") - } - } - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - fmt.Fprintf(builder, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return builder.String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *basePhysicalAgg) ExplainNormalizedInfo() string { - return p.explainInfo(true) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexJoin) ExplainInfo() string { - return p.explainInfo(false, false) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalIndexMergeJoin) ExplainInfo() string { - return p.explainInfo(false, true) -} - -func (p *PhysicalIndexJoin) explainInfo(normalized bool, isIndexMergeJoin bool) string { - sortedExplainExpressionList := expression.SortedExplainExpressionList - if normalized { - sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList - } - - buffer := bytes.NewBufferString(p.JoinType.String()) - buffer.WriteString(", inner:") - if normalized { - buffer.WriteString(p.Children()[p.InnerChildIdx].TP()) - } else { - buffer.WriteString(p.Children()[p.InnerChildIdx].ExplainID().String()) - } - if len(p.OuterJoinKeys) > 0 { - buffer.WriteString(", outer key:") - buffer.Write(expression.ExplainColumnList(p.OuterJoinKeys)) - } - if len(p.InnerJoinKeys) > 0 { - buffer.WriteString(", inner key:") - buffer.Write(expression.ExplainColumnList(p.InnerJoinKeys)) - } - - if len(p.OuterHashKeys) > 0 && !isIndexMergeJoin { - exprs := make([]expression.Expression, 0, len(p.OuterHashKeys)) - for i := range p.OuterHashKeys { - expr, err := expression.NewFunctionBase(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeLonglong), p.OuterHashKeys[i], p.InnerHashKeys[i]) - if err != nil { - logutil.BgLogger().Warn("fail to NewFunctionBase", zap.Error(err)) - } - exprs = append(exprs, expr) - } - buffer.WriteString(", equal cond:") - buffer.Write(sortedExplainExpressionList(exprs)) - } - if len(p.LeftConditions) > 0 { - buffer.WriteString(", left cond:") - buffer.Write(sortedExplainExpressionList(p.LeftConditions)) - } - if len(p.RightConditions) > 0 { - buffer.WriteString(", right cond:") - buffer.Write(sortedExplainExpressionList(p.RightConditions)) - } - if len(p.OtherConditions) > 0 { - buffer.WriteString(", other cond:") - buffer.Write(sortedExplainExpressionList(p.OtherConditions)) - } - return buffer.String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalIndexJoin) ExplainNormalizedInfo() string { - return p.explainInfo(true, false) -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalIndexMergeJoin) ExplainNormalizedInfo() string { - return p.explainInfo(true, true) -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalHashJoin) ExplainInfo() string { - return p.explainInfo(false) -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalHashJoin) ExplainNormalizedInfo() string { - return p.explainInfo(true) -} - -func (p *PhysicalHashJoin) explainInfo(normalized bool) string { - sortedExplainExpressionList := expression.SortedExplainExpressionList - if normalized { - sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList - } - - buffer := new(strings.Builder) - - if len(p.EqualConditions) == 0 { - if len(p.NAEqualConditions) == 0 { - buffer.WriteString("CARTESIAN ") - } else { - buffer.WriteString("Null-aware ") - } - } - - buffer.WriteString(p.JoinType.String()) - - if len(p.EqualConditions) > 0 { - if normalized { - buffer.WriteString(", equal:") - buffer.Write(expression.SortedExplainNormalizedScalarFuncList(p.EqualConditions)) - } else { - buffer.WriteString(", equal:[") - for i, EqualConditions := range p.EqualConditions { - if i != 0 { - buffer.WriteString(" ") - } - buffer.WriteString(EqualConditions.String()) - } - buffer.WriteString("]") - } - } - if len(p.NAEqualConditions) > 0 { - if normalized { - buffer.WriteString(", equal:") - buffer.Write(expression.SortedExplainNormalizedScalarFuncList(p.NAEqualConditions)) - } else { - buffer.WriteString(", equal:[") - for i, NAEqualCondition := range p.NAEqualConditions { - if i != 0 { - buffer.WriteString(" ") - } - buffer.WriteString(NAEqualCondition.String()) - } - buffer.WriteString("]") - } - } - if len(p.LeftConditions) > 0 { - if normalized { - buffer.WriteString(", left cond:") - buffer.Write(expression.SortedExplainNormalizedExpressionList(p.LeftConditions)) - } else { - buffer.WriteString(", left cond:[") - for i, LeftConditions := range p.LeftConditions { - if i != 0 { - buffer.WriteString(" ") - } - buffer.WriteString(LeftConditions.String()) - } - buffer.WriteString("]") - } - } - if len(p.RightConditions) > 0 { - buffer.WriteString(", right cond:") - buffer.Write(sortedExplainExpressionList(p.RightConditions)) - } - if len(p.OtherConditions) > 0 { - buffer.WriteString(", other cond:") - buffer.Write(sortedExplainExpressionList(p.OtherConditions)) - } - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - - // for runtime filter - if len(p.runtimeFilterList) > 0 { - buffer.WriteString(", runtime filter:") - for i, runtimeFilter := range p.runtimeFilterList { - if i != 0 { - buffer.WriteString(", ") - } - buffer.WriteString(runtimeFilter.ExplainInfo(true)) - } - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalMergeJoin) ExplainInfo() string { - return p.explainInfo(false) -} - -func (p *PhysicalMergeJoin) explainInfo(normalized bool) string { - sortedExplainExpressionList := expression.SortedExplainExpressionList - if normalized { - sortedExplainExpressionList = expression.SortedExplainNormalizedExpressionList - } - - buffer := bytes.NewBufferString(p.JoinType.String()) - if len(p.LeftJoinKeys) > 0 { - fmt.Fprintf(buffer, ", left key:%s", - expression.ExplainColumnList(p.LeftJoinKeys)) - } - if len(p.RightJoinKeys) > 0 { - fmt.Fprintf(buffer, ", right key:%s", - expression.ExplainColumnList(p.RightJoinKeys)) - } - if len(p.LeftConditions) > 0 { - if normalized { - fmt.Fprintf(buffer, ", left cond:%s", expression.SortedExplainNormalizedExpressionList(p.LeftConditions)) - } else { - fmt.Fprintf(buffer, ", left cond:%s", p.LeftConditions) - } - } - if len(p.RightConditions) > 0 { - fmt.Fprintf(buffer, ", right cond:%s", - sortedExplainExpressionList(p.RightConditions)) - } - if len(p.OtherConditions) > 0 { - fmt.Fprintf(buffer, ", other cond:%s", - sortedExplainExpressionList(p.OtherConditions)) - } - return buffer.String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalMergeJoin) ExplainNormalizedInfo() string { - return p.explainInfo(true) -} - -// explainPartitionBy: produce text for p.PartitionBy. Common for window functions and TopN. -func explainPartitionBy(buffer *bytes.Buffer, partitionBy []property.SortItem, normalized bool) *bytes.Buffer { - if len(partitionBy) > 0 { - buffer.WriteString("partition by ") - for i, item := range partitionBy { - if normalized { - fmt.Fprintf(buffer, "%s", item.Col.ExplainNormalizedInfo()) - } else { - fmt.Fprintf(buffer, "%s", item.Col.ExplainInfo()) - } - if i+1 < len(partitionBy) { - buffer.WriteString(", ") - } - } - } - return buffer -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalTopN) ExplainInfo() string { - buffer := bytes.NewBufferString("") - if len(p.GetPartitionBy()) > 0 { - buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) - buffer.WriteString(" ") - } - if len(p.ByItems) > 0 { - // Add order by text to separate partition by. Otherwise, do not add order by to - // avoid breaking existing TopN tests. - if len(p.GetPartitionBy()) > 0 { - buffer.WriteString("order by ") - } - buffer = explainByItems(buffer, p.ByItems) - } - fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) - return buffer.String() -} - -// ExplainNormalizedInfo implements Plan interface. -func (p *PhysicalTopN) ExplainNormalizedInfo() string { - buffer := bytes.NewBufferString("") - if len(p.GetPartitionBy()) > 0 { - buffer = explainPartitionBy(buffer, p.GetPartitionBy(), true) - buffer.WriteString(" ") - } - if len(p.ByItems) > 0 { - // Add order by text to separate partition by. Otherwise, do not add order by to - // avoid breaking existing TopN tests. - if len(p.GetPartitionBy()) > 0 { - buffer.WriteString("order by ") - } - buffer = explainNormalizedByItems(buffer, p.ByItems) - } - return buffer.String() -} - -func (*PhysicalWindow) formatFrameBound(buffer *bytes.Buffer, bound *FrameBound) { - if bound.Type == ast.CurrentRow { - buffer.WriteString("current row") - return - } - if bound.UnBounded { - buffer.WriteString("unbounded") - } else if len(bound.CalcFuncs) > 0 { - sf := bound.CalcFuncs[0].(*expression.ScalarFunction) - switch sf.FuncName.L { - case ast.DateAdd, ast.DateSub: - // For `interval '2:30' minute_second`. - fmt.Fprintf(buffer, "interval %s %s", sf.GetArgs()[1].ExplainInfo(), sf.GetArgs()[2].ExplainInfo()) - case ast.Plus, ast.Minus: - // For `1 preceding` of range frame. - fmt.Fprintf(buffer, "%s", sf.GetArgs()[1].ExplainInfo()) - } - } else { - fmt.Fprintf(buffer, "%d", bound.Num) - } - if bound.Type == ast.Preceding { - buffer.WriteString(" preceding") - } else { - buffer.WriteString(" following") - } -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalWindow) ExplainInfo() string { - buffer := bytes.NewBufferString("") - formatWindowFuncDescs(buffer, p.WindowFuncDescs, p.schema) - buffer.WriteString(" over(") - isFirst := true - buffer = explainPartitionBy(buffer, p.PartitionBy, false) - if len(p.PartitionBy) > 0 { - isFirst = false - } - if len(p.OrderBy) > 0 { - if !isFirst { - buffer.WriteString(" ") - } - buffer.WriteString("order by ") - for i, item := range p.OrderBy { - if item.Desc { - fmt.Fprintf(buffer, "%s desc", item.Col.ExplainInfo()) - } else { - fmt.Fprintf(buffer, "%s", item.Col.ExplainInfo()) - } - - if i+1 < len(p.OrderBy) { - buffer.WriteString(", ") - } - } - isFirst = false - } - if p.Frame != nil { - if !isFirst { - buffer.WriteString(" ") - } - if p.Frame.Type == ast.Rows { - buffer.WriteString("rows") - } else { - buffer.WriteString("range") - } - buffer.WriteString(" between ") - p.formatFrameBound(buffer, p.Frame.Start) - buffer.WriteString(" and ") - p.formatFrameBound(buffer, p.Frame.End) - } - buffer.WriteString(")") - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalShuffle) ExplainInfo() string { - explainIds := make([]fmt.Stringer, len(p.DataSources)) - for i := range p.DataSources { - explainIds[i] = p.DataSources[i].ExplainID() - } - - buffer := bytes.NewBufferString("") - fmt.Fprintf(buffer, "execution info: concurrency:%v, data sources:%v", p.Concurrency, explainIds) - return buffer.String() -} - -func formatWindowFuncDescs(buffer *bytes.Buffer, descs []*aggregation.WindowFuncDesc, schema *expression.Schema) *bytes.Buffer { - winFuncStartIdx := len(schema.Columns) - len(descs) - for i, desc := range descs { - if i != 0 { - buffer.WriteString(", ") - } - fmt.Fprintf(buffer, "%v->%v", desc, schema.Columns[winFuncStartIdx+i]) - } - return buffer -} - -// ExplainInfo implements Plan interface. -func (p *LogicalJoin) ExplainInfo() string { - buffer := bytes.NewBufferString(p.JoinType.String()) - if len(p.EqualConditions) > 0 { - fmt.Fprintf(buffer, ", equal:%v", p.EqualConditions) - } - if len(p.LeftConditions) > 0 { - fmt.Fprintf(buffer, ", left cond:%s", - expression.SortedExplainExpressionList(p.LeftConditions)) - } - if len(p.RightConditions) > 0 { - fmt.Fprintf(buffer, ", right cond:%s", - expression.SortedExplainExpressionList(p.RightConditions)) - } - if len(p.OtherConditions) > 0 { - fmt.Fprintf(buffer, ", other cond:%s", - expression.SortedExplainExpressionList(p.OtherConditions)) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalAggregation) ExplainInfo() string { - buffer := bytes.NewBufferString("") - if len(p.GroupByItems) > 0 { - fmt.Fprintf(buffer, "group by:%s, ", - expression.SortedExplainExpressionList(p.GroupByItems)) - } - if len(p.AggFuncs) > 0 { - buffer.WriteString("funcs:") - for i, agg := range p.AggFuncs { - buffer.WriteString(aggregation.ExplainAggFunc(agg, false)) - if i+1 < len(p.AggFuncs) { - buffer.WriteString(", ") - } - } - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalProjection) ExplainInfo() string { - return expression.ExplainExpressionList(p.Exprs, p.schema) -} - -// ExplainInfo implements Plan interface. -func (p *LogicalSelection) ExplainInfo() string { - return string(expression.SortedExplainExpressionList(p.Conditions)) -} - -// ExplainInfo implements Plan interface. -func (p *LogicalApply) ExplainInfo() string { - return p.LogicalJoin.ExplainInfo() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalTableDual) ExplainInfo() string { - var str strings.Builder - str.WriteString("rowcount:") - str.WriteString(strconv.Itoa(p.RowCount)) - return str.String() -} - -// ExplainInfo implements Plan interface. -func (ds *DataSource) ExplainInfo() string { - buffer := bytes.NewBufferString("") - tblName := ds.tableInfo.Name.O - if ds.TableAsName != nil && ds.TableAsName.O != "" { - tblName = ds.TableAsName.O - } - fmt.Fprintf(buffer, "table:%s", tblName) - if ds.isPartition { - if pi := ds.tableInfo.GetPartitionInfo(); pi != nil { - partitionName := pi.GetNameByID(ds.physicalTableID) - fmt.Fprintf(buffer, ", partition:%s", partitionName) - } - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalExchangeSender) ExplainInfo() string { - buffer := bytes.NewBufferString("ExchangeType: ") - switch p.ExchangeType { - case tipb.ExchangeType_PassThrough: - fmt.Fprintf(buffer, "PassThrough") - case tipb.ExchangeType_Broadcast: - fmt.Fprintf(buffer, "Broadcast") - case tipb.ExchangeType_Hash: - fmt.Fprintf(buffer, "HashPartition") - } - if p.CompressionMode != kv.ExchangeCompressionModeNONE { - fmt.Fprintf(buffer, ", Compression: %s", p.CompressionMode.Name()) - } - if p.ExchangeType == tipb.ExchangeType_Hash { - fmt.Fprintf(buffer, ", Hash Cols: %s", property.ExplainColumnList(p.HashCols)) - } - if len(p.Tasks) > 0 { - fmt.Fprintf(buffer, ", tasks: [") - for idx, task := range p.Tasks { - if idx != 0 { - fmt.Fprintf(buffer, ", ") - } - fmt.Fprintf(buffer, "%v", task.ID) - } - fmt.Fprintf(buffer, "]") - } - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - fmt.Fprintf(buffer, ", stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *PhysicalExchangeReceiver) ExplainInfo() (res string) { - if p.TiFlashFineGrainedShuffleStreamCount > 0 { - res = fmt.Sprintf("stream_count: %d", p.TiFlashFineGrainedShuffleStreamCount) - } - return res -} - -// ExplainInfo implements Plan interface. -func (p *LogicalUnionScan) ExplainInfo() string { - buffer := bytes.NewBufferString("") - fmt.Fprintf(buffer, "conds:%s", - expression.SortedExplainExpressionList(p.conditions)) - fmt.Fprintf(buffer, ", handle:%s", p.handleCols) - return buffer.String() -} - -func explainByItems(buffer *bytes.Buffer, byItems []*util.ByItems) *bytes.Buffer { - for i, item := range byItems { - if item.Desc { - fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainInfo()) - } else { - fmt.Fprintf(buffer, "%s", item.Expr.ExplainInfo()) - } - - if i+1 < len(byItems) { - buffer.WriteString(", ") - } - } - return buffer -} - -func explainNormalizedByItems(buffer *bytes.Buffer, byItems []*util.ByItems) *bytes.Buffer { - for i, item := range byItems { - if item.Desc { - fmt.Fprintf(buffer, "%s:desc", item.Expr.ExplainNormalizedInfo()) - } else { - fmt.Fprintf(buffer, "%s", item.Expr.ExplainNormalizedInfo()) - } - - if i+1 < len(byItems) { - buffer.WriteString(", ") - } - } - return buffer -} - -// ExplainInfo implements Plan interface. -func (p *LogicalSort) ExplainInfo() string { - buffer := bytes.NewBufferString("") - return explainByItems(buffer, p.ByItems).String() -} - -// ExplainInfo implements Plan interface. -func (lt *LogicalTopN) ExplainInfo() string { - buffer := bytes.NewBufferString("") - buffer = explainPartitionBy(buffer, lt.GetPartitionBy(), false) - if len(lt.GetPartitionBy()) > 0 && len(lt.ByItems) > 0 { - buffer.WriteString("order by ") - } - buffer = explainByItems(buffer, lt.ByItems) - fmt.Fprintf(buffer, ", offset:%v, count:%v", lt.Offset, lt.Count) - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalLimit) ExplainInfo() string { - buffer := bytes.NewBufferString("") - if len(p.GetPartitionBy()) > 0 { - buffer = explainPartitionBy(buffer, p.GetPartitionBy(), false) - fmt.Fprintf(buffer, ", offset:%v, count:%v", p.Offset, p.Count) - } else { - fmt.Fprintf(buffer, "offset:%v, count:%v", p.Offset, p.Count) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalTableScan) ExplainInfo() string { - buffer := bytes.NewBufferString(p.Source.ExplainInfo()) - if p.Source.handleCols != nil { - fmt.Fprintf(buffer, ", pk col:%s", p.Source.handleCols) - } - if len(p.AccessConds) > 0 { - fmt.Fprintf(buffer, ", cond:%v", p.AccessConds) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *LogicalIndexScan) ExplainInfo() string { - buffer := bytes.NewBufferString(p.Source.ExplainInfo()) - index := p.Index - if len(index.Columns) > 0 { - buffer.WriteString(", index:") - for i, idxCol := range index.Columns { - if tblCol := p.Source.tableInfo.Columns[idxCol.Offset]; tblCol.Hidden { - buffer.WriteString(tblCol.GeneratedExprString) - } else { - buffer.WriteString(idxCol.Name.O) - } - if i+1 < len(index.Columns) { - buffer.WriteString(", ") - } - } - } - if len(p.AccessConds) > 0 { - fmt.Fprintf(buffer, ", cond:%v", p.AccessConds) - } - return buffer.String() -} - -// ExplainInfo implements Plan interface. -func (p *TiKVSingleGather) ExplainInfo() string { - buffer := bytes.NewBufferString(p.Source.ExplainInfo()) - if p.IsIndexGather { - buffer.WriteString(", index:" + p.Index.Name.String()) - } - return buffer.String() -} - -// MetricTableTimeFormat is the time format for metric table explain and format. -const MetricTableTimeFormat = "2006-01-02 15:04:05.999" - -// ExplainInfo implements Plan interface. -func (p *PhysicalMemTable) ExplainInfo() string { - accessObject, operatorInfo := p.AccessObject().String(), p.OperatorInfo(false) - if len(operatorInfo) == 0 { - return accessObject - } - return accessObject + ", " + operatorInfo -} - -// OperatorInfo implements dataAccesser interface. -func (p *PhysicalMemTable) OperatorInfo(_ bool) string { - if p.Extractor != nil { - return p.Extractor.explainInfo(p) - } - return "" -} diff --git a/planner/core/expression_test.go b/planner/core/expression_test.go deleted file mode 100644 index 1057461e60b17..0000000000000 --- a/planner/core/expression_test.go +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package core - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func parseExpr(t *testing.T, expr string) ast.ExprNode { - p := parser.New() - st, err := p.ParseOneStmt("select "+expr, "", "") - require.NoError(t, err) - stmt := st.(*ast.SelectStmt) - return stmt.Fields.Fields[0].Expr -} - -type testCase struct { - exprStr string - resultStr string -} - -func runTests(t *testing.T, tests []testCase) { - ctx := MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - for _, tt := range tests { - expr := parseExpr(t, tt.exprStr) - val, err := evalAstExpr(ctx, expr) - require.NoError(t, err) - valStr := fmt.Sprintf("%v", val.GetValue()) - require.Equalf(t, tt.resultStr, valStr, "for %s", tt.exprStr) - } -} - -func TestBetween(t *testing.T) { - tests := []testCase{ - {exprStr: "1 between 2 and 3", resultStr: "0"}, - {exprStr: "1 not between 2 and 3", resultStr: "1"}, - {exprStr: "'2001-04-10 12:34:56' between cast('2001-01-01 01:01:01' as datetime) and '01-05-01'", resultStr: "1"}, - {exprStr: "20010410123456 between cast('2001-01-01 01:01:01' as datetime) and 010501", resultStr: "0"}, - {exprStr: "20010410123456 between cast('2001-01-01 01:01:01' as datetime) and 20010501123456", resultStr: "1"}, - } - runTests(t, tests) -} - -func TestCaseWhen(t *testing.T) { - tests := []testCase{ - { - exprStr: "case 1 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "str1", - }, - { - exprStr: "case 2 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "str2", - }, - { - exprStr: "case 3 when 1 then 'str1' when 2 then 'str2' end", - resultStr: "", - }, - { - exprStr: "case 4 when 1 then 'str1' when 2 then 'str2' else 'str3' end", - resultStr: "str3", - }, - } - runTests(t, tests) - - // When expression value changed, result set back to null. - valExpr := ast.NewValueExpr(1, "", "") - whenClause := &ast.WhenClause{Expr: ast.NewValueExpr(1, "", ""), Result: ast.NewValueExpr(1, "", "")} - caseExpr := &ast.CaseExpr{ - Value: valExpr, - WhenClauses: []*ast.WhenClause{whenClause}, - } - ctx := MockContext() - defer func() { - do := domain.GetDomain(ctx) - do.StatsHandle().Close() - }() - v, err := evalAstExpr(ctx, caseExpr) - require.NoError(t, err) - require.Equal(t, types.NewDatum(int64(1)), v) - valExpr.SetValue(4) - v, err = evalAstExpr(ctx, caseExpr) - require.NoError(t, err) - require.Equal(t, types.KindNull, v.Kind()) -} - -func TestCast(t *testing.T) { - f := types.NewFieldType(mysql.TypeLonglong) - - expr := &ast.FuncCastExpr{ - Expr: ast.NewValueExpr(1, "", ""), - Tp: f, - } - - ctx := MockContext() - defer func() { - do := domain.GetDomain(ctx) - do.StatsHandle().Close() - }() - ast.SetFlag(expr) - v, err := evalAstExpr(ctx, expr) - require.NoError(t, err) - require.Equal(t, types.NewDatum(int64(1)), v) - - f.AddFlag(mysql.UnsignedFlag) - v, err = evalAstExpr(ctx, expr) - require.NoError(t, err) - require.Equal(t, types.NewDatum(uint64(1)), v) - - f.SetType(mysql.TypeString) - f.SetCharset(charset.CharsetBin) - f.SetFlen(-1) - f.SetDecimal(-1) - v, err = evalAstExpr(ctx, expr) - require.NoError(t, err) - testutil.DatumEqual(t, types.NewDatum([]byte("1")), v) - - f.SetType(mysql.TypeString) - f.SetCharset(charset.CharsetUTF8) - f.SetFlen(-1) - f.SetDecimal(-1) - v, err = evalAstExpr(ctx, expr) - require.NoError(t, err) - testutil.DatumEqual(t, types.NewDatum([]byte("1")), v) - - expr.Expr = ast.NewValueExpr(nil, "", "") - v, err = evalAstExpr(ctx, expr) - require.NoError(t, err) - require.Equal(t, types.KindNull, v.Kind()) -} - -func TestPatternIn(t *testing.T) { - tests := []testCase{ - { - exprStr: "1 not in (1, 2, 3)", - resultStr: "0", - }, - { - exprStr: "1 in (1, 2, 3)", - resultStr: "1", - }, - { - exprStr: "1 in (2, 3)", - resultStr: "0", - }, - { - exprStr: "NULL in (2, 3)", - resultStr: "", - }, - { - exprStr: "NULL not in (2, 3)", - resultStr: "", - }, - { - exprStr: "NULL in (NULL, 3)", - resultStr: "", - }, - { - exprStr: "1 in (1, NULL)", - resultStr: "1", - }, - { - exprStr: "1 in (NULL, 1)", - resultStr: "1", - }, - { - exprStr: "2 in (1, NULL)", - resultStr: "", - }, - { - exprStr: "(-(23)++46/51*+51) in (+23)", - resultStr: "0", - }, - } - runTests(t, tests) -} - -func TestIsNull(t *testing.T) { - tests := []testCase{ - { - exprStr: "1 IS NULL", - resultStr: "0", - }, - { - exprStr: "1 IS NOT NULL", - resultStr: "1", - }, - { - exprStr: "NULL IS NULL", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT NULL", - resultStr: "0", - }, - } - runTests(t, tests) -} - -func TestCompareRow(t *testing.T) { - tests := []testCase{ - { - exprStr: "row(1,2,3)=row(1,2,3)", - resultStr: "1", - }, - { - exprStr: "row(1,2,3)=row(1+3,2,3)", - resultStr: "0", - }, - { - exprStr: "row(1,2,3)<>row(1,2,3)", - resultStr: "0", - }, - { - exprStr: "row(1,2,3)<>row(1+3,2,3)", - resultStr: "1", - }, - { - exprStr: "row(1+3,2,3)<>row(1+3,2,3)", - resultStr: "0", - }, - { - exprStr: "row(1,2,3)", - }, - { - exprStr: "row(1,2,3)=row(0,NULL,3)", - resultStr: "1", - }, - { - exprStr: "row(1,2,3)<=row(2,NULL,3)", - resultStr: "1", - }, - } - runTests(t, tests) -} - -func TestIsTruth(t *testing.T) { - tests := []testCase{ - { - exprStr: "1 IS TRUE", - resultStr: "1", - }, - { - exprStr: "2 IS TRUE", - resultStr: "1", - }, - { - exprStr: "0 IS TRUE", - resultStr: "0", - }, - { - exprStr: "NULL IS TRUE", - resultStr: "0", - }, - { - exprStr: "1 IS FALSE", - resultStr: "0", - }, - { - exprStr: "2 IS FALSE", - resultStr: "0", - }, - { - exprStr: "0 IS FALSE", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "1 IS NOT TRUE", - resultStr: "0", - }, - { - exprStr: "2 IS NOT TRUE", - resultStr: "0", - }, - { - exprStr: "0 IS NOT TRUE", - resultStr: "1", - }, - { - exprStr: "NULL IS NOT TRUE", - resultStr: "1", - }, - { - exprStr: "1 IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "2 IS NOT FALSE", - resultStr: "1", - }, - { - exprStr: "0 IS NOT FALSE", - resultStr: "0", - }, - { - exprStr: "NULL IS NOT FALSE", - resultStr: "1", - }, - } - runTests(t, tests) -} diff --git a/planner/core/foreign_key.go b/planner/core/foreign_key.go deleted file mode 100644 index be7a49dc31462..0000000000000 --- a/planner/core/foreign_key.go +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package core - -import ( - "fmt" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" -) - -// FKCheck indicates the foreign key constraint checker. -type FKCheck struct { - basePhysicalPlan - FK *model.FKInfo - ReferredFK *model.ReferredFKInfo - Tbl table.Table - Idx table.Index - Cols []model.CIStr - - IdxIsPrimaryKey bool - IdxIsExclusive bool - - CheckExist bool - FailedErr error -} - -// FKCascade indicates the foreign key constraint cascade behaviour. -type FKCascade struct { - basePhysicalPlan - Tp FKCascadeType - ReferredFK *model.ReferredFKInfo - ChildTable table.Table - FK *model.FKInfo - FKCols []*model.ColumnInfo - FKIdx *model.IndexInfo - // CascadePlans contains the child cascade plan. - // CascadePlans will be filled during execution, so only `explain analyze` statement result contains the cascade plan, - // `explain` statement result doesn't contain the cascade plan. - CascadePlans []Plan -} - -// FKCascadeType indicates in which (delete/update) statements. -type FKCascadeType int8 - -const ( - // FKCascadeOnDelete indicates in delete statement. - FKCascadeOnDelete FKCascadeType = 1 - // FKCascadeOnUpdate indicates in update statement. - FKCascadeOnUpdate FKCascadeType = 2 - - emptyFkCheckSize = int64(unsafe.Sizeof(FKCheck{})) - emptyFkCascadeSize = int64(unsafe.Sizeof(FKCascade{})) -) - -// AccessObject implements dataAccesser interface. -func (f *FKCheck) AccessObject() AccessObject { - if f.Idx == nil { - return OtherAccessObject(fmt.Sprintf("table:%s", f.Tbl.Meta().Name)) - } - return OtherAccessObject(fmt.Sprintf("table:%s, index:%s", f.Tbl.Meta().Name, f.Idx.Meta().Name)) -} - -// OperatorInfo implements dataAccesser interface. -func (f *FKCheck) OperatorInfo(bool) string { - if f.FK != nil { - return fmt.Sprintf("foreign_key:%s, check_exist", f.FK.Name) - } - if f.ReferredFK != nil { - return fmt.Sprintf("foreign_key:%s, check_not_exist", f.ReferredFK.ChildFKName) - } - return "" -} - -// ExplainInfo implement Plan interface. -func (f *FKCheck) ExplainInfo() string { - return f.AccessObject().String() + ", " + f.OperatorInfo(false) -} - -// MemoryUsage return the memory usage of FKCheck -func (f *FKCheck) MemoryUsage() (sum int64) { - if f == nil { - return - } - - sum = emptyFkCheckSize - for _, cis := range f.Cols { - sum += cis.MemoryUsage() - } - return -} - -// AccessObject implements dataAccesser interface. -func (f *FKCascade) AccessObject() AccessObject { - if f.FKIdx == nil { - return OtherAccessObject(fmt.Sprintf("table:%s", f.ChildTable.Meta().Name)) - } - return OtherAccessObject(fmt.Sprintf("table:%s, index:%s", f.ChildTable.Meta().Name, f.FKIdx.Name)) -} - -// OperatorInfo implements dataAccesser interface. -func (f *FKCascade) OperatorInfo(bool) string { - switch f.Tp { - case FKCascadeOnDelete: - return fmt.Sprintf("foreign_key:%s, on_delete:%s", f.FK.Name, model.ReferOptionType(f.FK.OnDelete).String()) - case FKCascadeOnUpdate: - return fmt.Sprintf("foreign_key:%s, on_update:%s", f.FK.Name, model.ReferOptionType(f.FK.OnUpdate).String()) - } - return "" -} - -// ExplainInfo implement Plan interface. -func (f *FKCascade) ExplainInfo() string { - return f.AccessObject().String() + ", " + f.OperatorInfo(false) -} - -// MemoryUsage return the memory usage of FKCascade -func (f *FKCascade) MemoryUsage() (sum int64) { - if f == nil { - return - } - sum = emptyFkCascadeSize - return -} - -func (p *Insert) buildOnInsertFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string) error { - if !ctx.GetSessionVars().ForeignKeyChecks { - return nil - } - tblInfo := p.Table.Meta() - fkChecks := make([]*FKCheck, 0, len(tblInfo.ForeignKeys)) - fkCascades := make([]*FKCascade, 0, len(tblInfo.ForeignKeys)) - updateCols := p.buildOnDuplicateUpdateColumns() - if len(updateCols) > 0 { - referredFKChecks, referredFKCascades, err := buildOnUpdateReferredFKTriggers(ctx, is, dbName, tblInfo, updateCols) - if err != nil { - return err - } - if len(referredFKChecks) > 0 { - fkChecks = append(fkChecks, referredFKChecks...) - } - if len(referredFKCascades) > 0 { - fkCascades = append(fkCascades, referredFKCascades...) - } - } else if p.IsReplace { - referredFKChecks, referredFKCascades, err := p.buildOnReplaceReferredFKTriggers(ctx, is, dbName, tblInfo) - if err != nil { - return err - } - if len(referredFKChecks) > 0 { - fkChecks = append(fkChecks, referredFKChecks...) - } - if len(referredFKCascades) > 0 { - fkCascades = append(fkCascades, referredFKCascades...) - } - } - for _, fk := range tblInfo.ForeignKeys { - if fk.Version < 1 { - continue - } - failedErr := ErrNoReferencedRow2.FastGenByArgs(fk.String(dbName, tblInfo.Name.L)) - fkCheck, err := buildFKCheckOnModifyChildTable(ctx, is, fk, failedErr) - if err != nil { - return err - } - if fkCheck != nil { - fkChecks = append(fkChecks, fkCheck) - } - } - p.FKChecks = fkChecks - p.FKCascades = fkCascades - return nil -} - -func (p *Insert) buildOnDuplicateUpdateColumns() map[string]struct{} { - m := make(map[string]struct{}) - for _, assign := range p.OnDuplicate { - m[assign.ColName.L] = struct{}{} - } - return m -} - -func (*Insert) buildOnReplaceReferredFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo) ([]*FKCheck, []*FKCascade, error) { - referredFKs := is.GetTableReferredForeignKeys(dbName, tblInfo.Name.L) - fkChecks := make([]*FKCheck, 0, len(referredFKs)) - fkCascades := make([]*FKCascade, 0, len(referredFKs)) - for _, referredFK := range referredFKs { - fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnDelete) - if err != nil { - return nil, nil, err - } - if fkCheck != nil { - fkChecks = append(fkChecks, fkCheck) - } - if fkCascade != nil { - fkCascades = append(fkCascades, fkCascade) - } - } - return fkChecks, fkCascades, nil -} - -func (updt *Update) buildOnUpdateFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, tblID2table map[int64]table.Table) error { - if !ctx.GetSessionVars().ForeignKeyChecks { - return nil - } - tblID2UpdateColumns := updt.buildTbl2UpdateColumns() - fkChecks := make(map[int64][]*FKCheck) - fkCascades := make(map[int64][]*FKCascade) - for tid, tbl := range tblID2table { - tblInfo := tbl.Meta() - dbInfo, exist := is.SchemaByTable(tblInfo) - if !exist { - // Normally, it should never happen. Just check here to avoid panic here. - return infoschema.ErrDatabaseNotExists - } - updateCols := tblID2UpdateColumns[tid] - if len(updateCols) == 0 { - continue - } - referredFKChecks, referredFKCascades, err := buildOnUpdateReferredFKTriggers(ctx, is, dbInfo.Name.L, tblInfo, updateCols) - if err != nil { - return err - } - if len(referredFKChecks) > 0 { - fkChecks[tid] = append(fkChecks[tid], referredFKChecks...) - } - if len(referredFKCascades) > 0 { - fkCascades[tid] = append(fkCascades[tid], referredFKCascades...) - } - childFKChecks, err := buildOnUpdateChildFKChecks(ctx, is, dbInfo.Name.L, tblInfo, updateCols) - if err != nil { - return err - } - if len(childFKChecks) > 0 { - fkChecks[tid] = append(fkChecks[tid], childFKChecks...) - } - } - updt.FKChecks = fkChecks - updt.FKCascades = fkCascades - return nil -} - -func (del *Delete) buildOnDeleteFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, tblID2table map[int64]table.Table) error { - if !ctx.GetSessionVars().ForeignKeyChecks { - return nil - } - fkChecks := make(map[int64][]*FKCheck) - fkCascades := make(map[int64][]*FKCascade) - for tid, tbl := range tblID2table { - tblInfo := tbl.Meta() - dbInfo, exist := is.SchemaByTable(tblInfo) - if !exist { - return infoschema.ErrDatabaseNotExists - } - referredFKs := is.GetTableReferredForeignKeys(dbInfo.Name.L, tblInfo.Name.L) - for _, referredFK := range referredFKs { - fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnDelete) - if err != nil { - return err - } - if fkCheck != nil { - fkChecks[tid] = append(fkChecks[tid], fkCheck) - } - if fkCascade != nil { - fkCascades[tid] = append(fkCascades[tid], fkCascade) - } - } - } - del.FKChecks = fkChecks - del.FKCascades = fkCascades - return nil -} - -func buildOnUpdateReferredFKTriggers(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo, updateCols map[string]struct{}) ([]*FKCheck, []*FKCascade, error) { - referredFKs := is.GetTableReferredForeignKeys(dbName, tblInfo.Name.L) - fkChecks := make([]*FKCheck, 0, len(referredFKs)) - fkCascades := make([]*FKCascade, 0, len(referredFKs)) - for _, referredFK := range referredFKs { - if !isMapContainAnyCols(updateCols, referredFK.Cols...) { - continue - } - fkCheck, fkCascade, err := buildOnDeleteOrUpdateFKTrigger(ctx, is, referredFK, FKCascadeOnUpdate) - if err != nil { - return nil, nil, err - } - if fkCheck != nil { - fkChecks = append(fkChecks, fkCheck) - } - if fkCascade != nil { - fkCascades = append(fkCascades, fkCascade) - } - } - return fkChecks, fkCascades, nil -} - -func buildOnUpdateChildFKChecks(ctx sessionctx.Context, is infoschema.InfoSchema, dbName string, tblInfo *model.TableInfo, updateCols map[string]struct{}) ([]*FKCheck, error) { - fkChecks := make([]*FKCheck, 0, len(tblInfo.ForeignKeys)) - for _, fk := range tblInfo.ForeignKeys { - if fk.Version < 1 { - continue - } - if !isMapContainAnyCols(updateCols, fk.Cols...) { - continue - } - failedErr := ErrNoReferencedRow2.FastGenByArgs(fk.String(dbName, tblInfo.Name.L)) - fkCheck, err := buildFKCheckOnModifyChildTable(ctx, is, fk, failedErr) - if err != nil { - return nil, err - } - if fkCheck != nil { - fkChecks = append(fkChecks, fkCheck) - } - } - return fkChecks, nil -} - -func (updt *Update) buildTbl2UpdateColumns() map[int64]map[string]struct{} { - colsInfo := GetUpdateColumnsInfo(updt.tblID2Table, updt.TblColPosInfos, len(updt.SelectPlan.Schema().Columns)) - tblID2UpdateColumns := make(map[int64]map[string]struct{}) - for _, assign := range updt.OrderedList { - col := colsInfo[assign.Col.Index] - for _, content := range updt.TblColPosInfos { - if assign.Col.Index >= content.Start && assign.Col.Index < content.End { - if _, ok := tblID2UpdateColumns[content.TblID]; !ok { - tblID2UpdateColumns[content.TblID] = make(map[string]struct{}) - } - tblID2UpdateColumns[content.TblID][col.Name.L] = struct{}{} - break - } - } - } - for tid, tbl := range updt.tblID2Table { - updateCols := tblID2UpdateColumns[tid] - if len(updateCols) == 0 { - continue - } - for _, col := range tbl.WritableCols() { - if !col.IsGenerated() || !col.GeneratedStored { - continue - } - for depCol := range col.Dependences { - if _, ok := updateCols[depCol]; ok { - tblID2UpdateColumns[tid][col.Name.L] = struct{}{} - } - } - } - } - return tblID2UpdateColumns -} - -func buildOnDeleteOrUpdateFKTrigger(ctx sessionctx.Context, is infoschema.InfoSchema, referredFK *model.ReferredFKInfo, tp FKCascadeType) (*FKCheck, *FKCascade, error) { - childTable, err := is.TableByName(referredFK.ChildSchema, referredFK.ChildTable) - if err != nil { - return nil, nil, nil - } - fk := model.FindFKInfoByName(childTable.Meta().ForeignKeys, referredFK.ChildFKName.L) - if fk == nil || fk.Version < 1 { - return nil, nil, nil - } - var fkReferOption model.ReferOptionType - if fk.State != model.StatePublic { - fkReferOption = model.ReferOptionRestrict - } else { - switch tp { - case FKCascadeOnDelete: - fkReferOption = model.ReferOptionType(fk.OnDelete) - case FKCascadeOnUpdate: - fkReferOption = model.ReferOptionType(fk.OnUpdate) - } - } - switch fkReferOption { - case model.ReferOptionCascade, model.ReferOptionSetNull: - fkCascade, err := buildFKCascade(ctx, tp, referredFK, childTable, fk) - return nil, fkCascade, err - default: - fkCheck, err := buildFKCheckForReferredFK(ctx, childTable, fk, referredFK) - return fkCheck, nil, err - } -} - -func isMapContainAnyCols(colsMap map[string]struct{}, cols ...model.CIStr) bool { - for _, col := range cols { - _, exist := colsMap[col.L] - if exist { - return true - } - } - return false -} - -func buildFKCheckOnModifyChildTable(ctx sessionctx.Context, is infoschema.InfoSchema, fk *model.FKInfo, failedErr error) (*FKCheck, error) { - referTable, err := is.TableByName(fk.RefSchema, fk.RefTable) - if err != nil { - return nil, nil - } - fkCheck, err := buildFKCheck(ctx, referTable, fk.RefCols, failedErr) - if err != nil { - return nil, err - } - fkCheck.CheckExist = true - fkCheck.FK = fk - return fkCheck, nil -} - -func buildFKCheckForReferredFK(ctx sessionctx.Context, childTable table.Table, fk *model.FKInfo, referredFK *model.ReferredFKInfo) (*FKCheck, error) { - failedErr := ErrRowIsReferenced2.GenWithStackByArgs(fk.String(referredFK.ChildSchema.L, referredFK.ChildTable.L)) - fkCheck, err := buildFKCheck(ctx, childTable, fk.Cols, failedErr) - if err != nil { - return nil, err - } - fkCheck.CheckExist = false - fkCheck.ReferredFK = referredFK - return fkCheck, nil -} - -func buildFKCheck(ctx sessionctx.Context, tbl table.Table, cols []model.CIStr, failedErr error) (*FKCheck, error) { - tblInfo := tbl.Meta() - if tblInfo.PKIsHandle && len(cols) == 1 { - refColInfo := model.FindColumnInfo(tblInfo.Columns, cols[0].L) - if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { - return FKCheck{ - Tbl: tbl, - IdxIsPrimaryKey: true, - IdxIsExclusive: true, - FailedErr: failedErr, - }.Init(ctx), nil - } - } - - referTbIdxInfo := model.FindIndexByColumns(tblInfo, tblInfo.Indices, cols...) - if referTbIdxInfo == nil { - return nil, failedErr - } - var tblIdx table.Index - for _, idx := range tbl.Indices() { - if idx.Meta().ID == referTbIdxInfo.ID { - tblIdx = idx - } - } - if tblIdx == nil { - return nil, failedErr - } - - return FKCheck{ - Tbl: tbl, - Idx: tblIdx, - IdxIsExclusive: len(cols) == len(referTbIdxInfo.Columns), - IdxIsPrimaryKey: referTbIdxInfo.Primary && tblInfo.IsCommonHandle, - FailedErr: failedErr, - }.Init(ctx), nil -} - -func buildFKCascade(ctx sessionctx.Context, tp FKCascadeType, referredFK *model.ReferredFKInfo, childTable table.Table, fk *model.FKInfo) (*FKCascade, error) { - cols := make([]*model.ColumnInfo, len(fk.Cols)) - childTableColumns := childTable.Meta().Columns - for i, c := range fk.Cols { - col := model.FindColumnInfo(childTableColumns, c.L) - if col == nil { - return nil, errors.Errorf("foreign key column %s is not found in table %s", c.L, childTable.Meta().Name) - } - cols[i] = col - } - fkCascade := FKCascade{ - Tp: tp, - ReferredFK: referredFK, - ChildTable: childTable, - FK: fk, - FKCols: cols, - }.Init(ctx) - if childTable.Meta().PKIsHandle && len(cols) == 1 { - refColInfo := model.FindColumnInfo(childTableColumns, cols[0].Name.L) - if refColInfo != nil && mysql.HasPriKeyFlag(refColInfo.GetFlag()) { - return fkCascade, nil - } - } - indexForFK := model.FindIndexByColumns(childTable.Meta(), childTable.Meta().Indices, fk.Cols...) - if indexForFK == nil { - return nil, errors.Errorf("Missing index for '%s' foreign key columns in the table '%s'", fk.Name, childTable.Meta().Name) - } - fkCascade.FKIdx = indexForFK - return fkCascade, nil -} diff --git a/planner/core/integration_partition_test.go b/planner/core/integration_partition_test.go deleted file mode 100644 index 3a2ec93520819..0000000000000 --- a/planner/core/integration_partition_test.go +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "bytes" - "fmt" - "math/rand" - "strconv" - "testing" - - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/stretchr/testify/require" -) - -func TestListPartitionOrderLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database list_partition_order_limit") - tk.MustExec("use list_partition_order_limit") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tnormal (a int, b int)`) - - vals := "" - for i := 0; i < 50; i++ { - if vals != "" { - vals += ", " - } - vals += fmt.Sprintf("(%v, %v)", i*2+rand.Intn(2), i*2+rand.Intn(2)) - } - tk.MustExec(`insert into tlist values ` + vals) - tk.MustExec(`insert into tcollist values ` + vals) - tk.MustExec(`insert into tnormal values ` + vals) - - for _, orderCol := range []string{"a", "b"} { - for _, limitNum := range []string{"1", "5", "20", "100"} { - randCond := fmt.Sprintf("where %v > %v", []string{"a", "b"}[rand.Intn(2)], rand.Intn(100)) - rs := tk.MustQuery(fmt.Sprintf(`select * from tnormal %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - rsDynamic := tk.MustQuery(fmt.Sprintf(`select * from tlist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - rsStatic := tk.MustQuery(fmt.Sprintf(`select * from tlist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - rsColDynamic := tk.MustQuery(fmt.Sprintf(`select * from tcollist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - rsColStatic := tk.MustQuery(fmt.Sprintf(`select * from tcollist %v order by %v limit %v`, randCond, orderCol, limitNum)).Sort() - - rs.Check(rsDynamic.Rows()) - rs.Check(rsStatic.Rows()) - rs.Check(rsColDynamic.Rows()) - rs.Check(rsColStatic.Rows()) - } - } -} - -func TestListPartitionAgg(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database list_partition_agg") - tk.MustExec("use list_partition_agg") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tnormal (a int, b int)`) - - vals := "" - for i := 0; i < 50; i++ { - if vals != "" { - vals += ", " - } - vals += fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100)) - } - tk.MustExec(`insert into tlist values ` + vals) - tk.MustExec(`insert into tcollist values ` + vals) - tk.MustExec(`insert into tnormal values ` + vals) - - for _, aggFunc := range []string{"min", "max", "sum", "count"} { - c1, c2 := "a", "b" - for i := 0; i < 2; i++ { - rs := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tnormal group by %v`, c1, aggFunc, c2, c1)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - rsDynamic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tlist group by %v`, c1, aggFunc, c2, c1)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - rsStatic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tlist group by %v`, c1, aggFunc, c2, c1)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - rsColDynamic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tcollist group by %v`, c1, aggFunc, c2, c1)).Sort() - - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - rsColStatic := tk.MustQuery(fmt.Sprintf(`select %v, %v(%v) from tcollist group by %v`, c1, aggFunc, c2, c1)).Sort() - - rs.Check(rsDynamic.Rows()) - rs.Check(rsStatic.Rows()) - rs.Check(rsColDynamic.Rows()) - rs.Check(rsColStatic.Rows()) - } - } -} - -func TestListPartitionPrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.SetSession(se) - tk.MustExec("create database list_partition_pri") - tk.MustExec("use list_partition_pri") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - tk.MustExec(`create table tlist (a int) partition by list (a) (partition p0 values in (0), partition p1 values in (1))`) - - tk.MustExec(`create user 'priv_test'@'%'`) - tk.MustExec(`grant select on list_partition_pri.tlist to 'priv_test'`) - - tk1 := testkit.NewTestKit(t, store) - se, err = session.CreateSession4Test(store) - require.NoError(t, err) - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "priv_test", Hostname: "%"}, nil, nil, nil)) - tk1.SetSession(se) - tk1.MustExec(`use list_partition_pri`) - err = tk1.ExecToErr(`alter table tlist truncate partition p0`) - require.Error(t, err) - require.Contains(t, err.Error(), "denied") - err = tk1.ExecToErr(`alter table tlist drop partition p0`) - require.Error(t, err) - require.Contains(t, err.Error(), "denied") - err = tk1.ExecToErr(`alter table tlist add partition (partition p2 values in (2))`) - require.Error(t, err) - require.Contains(t, err.Error(), "denied") - err = tk1.ExecToErr(`insert into tlist values (1)`) - require.Error(t, err) - require.Contains(t, err.Error(), "denied") -} - -func TestListPartitionView(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database list_partition_view") - tk.MustExec("use list_partition_view") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - - tk.MustExec(`create table tlist (a int, b int) partition by list (a) ( - partition p0 values in (0, 1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8, 9), - partition p2 values in (10, 11, 12, 13, 14))`) - tk.MustExec(`create definer='root'@'localhost' view vlist as select a*2 as a2, a+b as ab from tlist`) - tk.MustExec(`create table tnormal (a int, b int)`) - tk.MustExec(`create definer='root'@'localhost' view vnormal as select a*2 as a2, a+b as ab from tnormal`) - for i := 0; i < 10; i++ { - a, b := rand.Intn(15), rand.Intn(100) - tk.MustExec(fmt.Sprintf(`insert into tlist values (%v, %v)`, a, b)) - tk.MustExec(fmt.Sprintf(`insert into tnormal values (%v, %v)`, a, b)) - } - - r1 := tk.MustQuery(`select * from vlist`).Sort() - r2 := tk.MustQuery(`select * from vnormal`).Sort() - r1.Check(r2.Rows()) - - tk.MustExec(`create table tcollist (a int, b int) partition by list columns (a) ( - partition p0 values in (0, 1, 2, 3, 4), - partition p1 values in (5, 6, 7, 8, 9), - partition p2 values in (10, 11, 12, 13, 14))`) - tk.MustExec(`create definer='root'@'localhost' view vcollist as select a*2 as a2, a+b as ab from tcollist`) - tk.MustExec(`truncate tnormal`) - for i := 0; i < 10; i++ { - a, b := rand.Intn(15), rand.Intn(100) - tk.MustExec(fmt.Sprintf(`insert into tcollist values (%v, %v)`, a, b)) - tk.MustExec(fmt.Sprintf(`insert into tnormal values (%v, %v)`, a, b)) - } - - r1 = tk.MustQuery(`select * from vcollist`).Sort() - r2 = tk.MustQuery(`select * from vnormal`).Sort() - r1.Check(r2.Rows()) -} - -func TestListPartitionRandomTransaction(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database list_partition_random_tran") - tk.MustExec("use list_partition_random_tran") - tk.MustExec("drop table if exists tlist") - tk.MustExec(`set tidb_enable_list_partition = 1`) - - tk.MustExec(`create table tlist (a int, b int) partition by list(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tcollist (a int, b int) partition by list columns(a) (` + - ` partition p0 values in ` + genListPartition(0, 20) + - `, partition p1 values in ` + genListPartition(20, 40) + - `, partition p2 values in ` + genListPartition(40, 60) + - `, partition p3 values in ` + genListPartition(60, 80) + - `, partition p4 values in ` + genListPartition(80, 100) + `)`) - tk.MustExec(`create table tnormal (a int, b int)`) - - inTrans := false - for i := 0; i < 50; i++ { - switch rand.Intn(4) { - case 0: // begin - if inTrans { - continue - } - tk.MustExec(`begin`) - inTrans = true - case 1: // sel - cond := fmt.Sprintf("where a>=%v and a<=%v", rand.Intn(50), 50+rand.Intn(50)) - rnormal := tk.MustQuery(`select * from tnormal ` + cond).Sort().Rows() - tk.MustQuery(`select * from tlist ` + cond).Sort().Check(rnormal) - tk.MustQuery(`select * from tcollist ` + cond).Sort().Check(rnormal) - case 2: // insert - values := fmt.Sprintf("(%v, %v)", rand.Intn(100), rand.Intn(100)) - tk.MustExec(`insert into tnormal values ` + values) - tk.MustExec(`insert into tlist values ` + values) - tk.MustExec(`insert into tcollist values ` + values) - case 3: // commit or rollback - if !inTrans { - continue - } - tk.MustExec([]string{"commit", "rollback"}[rand.Intn(2)]) - inTrans = false - } - } -} - -func genListPartition(begin, end int) string { - buf := &bytes.Buffer{} - buf.WriteString("(") - for i := begin; i < end-1; i++ { - buf.WriteString(fmt.Sprintf("%v, ", i)) - } - buf.WriteString(fmt.Sprintf("%v)", end-1)) - return buf.String() -} - -func BenchmarkPartitionRangeColumns(b *testing.B) { - store := testkit.CreateMockStore(b) - - tk := testkit.NewTestKit(b, store) - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("create schema rcb") - tk.MustExec("use rcb") - tk.MustExec(`create table t (` + - `c1 int primary key clustered,` + - `c2 varchar(255))` + - ` partition by range columns (c1)` + - ` interval (10000) first partition less than (10000) last partition less than (5120000)`) - b.ResetTimer() - for i := 0; i < b.N; i++ { - val := strconv.FormatInt(int64(rand.Intn(5120000)), 10) - tk.MustExec("select * from t where c1 = " + val) - //tk.MustExec("insert ignore into t values (" + val + ",'" + val + "')") - } - b.StopTimer() -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkPartitionRangeColumns, - ) -} diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go deleted file mode 100644 index 9139c5aa8d6c1..0000000000000 --- a/planner/core/integration_test.go +++ /dev/null @@ -1,2570 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "bytes" - "fmt" - "regexp" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestNoneAccessPathsFoundByIsolationRead(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key)") - - tk.MustExec("select * from t") - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") - - // Don't filter mysql.SystemDB by isolation read. - tk.MustQuery("explain format = 'brief' select * from mysql.stats_meta").Check(testkit.Rows( - "TableReader 10000.00 root data:TableFullScan", - "└─TableFullScan 10000.00 cop[tikv] table:stats_meta keep order:false, stats:pseudo")) - - _, err := tk.Exec("select * from t") - require.EqualError(t, err, "[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tikv'. Please check tiflash replica.") - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash, tikv'") - tk.MustExec("select * from t") - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.IsolationRead.Engines = []string{"tiflash"} - }) - // Change instance config doesn't affect isolation read. - tk.MustExec("select * from t") -} - -func TestAggPushDownEngine(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b varchar(20))") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") - - tk.MustQuery("explain format = 'brief' select approx_count_distinct(a) from t").Check(testkit.Rows( - "StreamAgg 1.00 root funcs:approx_count_distinct(Column#5)->Column#3", - "└─TableReader 1.00 root data:StreamAgg", - " └─StreamAgg 1.00 batchCop[tiflash] funcs:approx_count_distinct(test.t.a)->Column#5", - " └─TableFullScan 10000.00 batchCop[tiflash] table:t keep order:false, stats:pseudo")) - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tikv'") - - tk.MustQuery("explain format = 'brief' select approx_count_distinct(a) from t").Check(testkit.Rows( - "HashAgg 1.00 root funcs:approx_count_distinct(test.t.a)->Column#3", - "└─TableReader 10000.00 root data:TableFullScan", - " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo")) -} - -func TestIssue15110(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists crm_rd_150m") - tk.MustExec(`CREATE TABLE crm_rd_150m ( - product varchar(256) DEFAULT NULL, - uks varchar(16) DEFAULT NULL, - brand varchar(256) DEFAULT NULL, - cin varchar(16) DEFAULT NULL, - created_date timestamp NULL DEFAULT NULL, - quantity int(11) DEFAULT NULL, - amount decimal(11,0) DEFAULT NULL, - pl_date timestamp NULL DEFAULT NULL, - customer_first_date timestamp NULL DEFAULT NULL, - recent_date timestamp NULL DEFAULT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;`) - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "crm_rd_150m" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec("set @@session.tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("explain format = 'brief' SELECT count(*) FROM crm_rd_150m dataset_48 WHERE (CASE WHEN (month(dataset_48.customer_first_date)) <= 30 THEN '新客' ELSE NULL END) IS NOT NULL;") -} - -func TestKeepOrderHintWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b int, index idx_a(a));") - - // create binding for order_index hint - tk.MustExec("select * from t1 where a<10 order by a limit 1;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select * from t1 where a<10 order by a limit 1 using select /*+ order_index(t1, idx_a) */ * from t1 where a<10 order by a limit 1;") - tk.MustExec("select * from t1 where a<10 order by a limit 1;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from `test` . `t1` where `a` < ? order by `a` limit ?") - require.Equal(t, res[0][1], "SELECT /*+ order_index(`t1` `idx_a`)*/ * FROM `test`.`t1` WHERE `a` < 10 ORDER BY `a` LIMIT 1") - - tk.MustExec("drop global binding for select * from t1 where a<10 order by a limit 1;") - tk.MustExec("select * from t1 where a<10 order by a limit 1;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) - - // create binding for no_order_index hint - tk.MustExec("create global binding for select * from t1 where a<10 order by a limit 1 using select /*+ no_order_index(t1, idx_a) */ * from t1 where a<10 order by a limit 1;") - tk.MustExec("select * from t1 where a<10 order by a limit 1;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from `test` . `t1` where `a` < ? order by `a` limit ?") - require.Equal(t, res[0][1], "SELECT /*+ no_order_index(`t1` `idx_a`)*/ * FROM `test`.`t1` WHERE `a` < 10 ORDER BY `a` LIMIT 1") - - tk.MustExec("drop global binding for select * from t1 where a<10 order by a limit 1;") - tk.MustExec("select * from t1 where a<10 order by a limit 1;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) -} - -func TestViewHintWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop view if exists v, v1") - tk.MustExec("drop table if exists t, t1, t2, t3") - tk.MustExec("create table t(a int, b int);") - tk.MustExec("create table t1(a int, b int);") - tk.MustExec("create table t2(a int, b int);") - tk.MustExec("create table t3(a int, b int)") - tk.MustExec("create definer='root'@'localhost' view v as select t.a, t.b from t join (select count(*) as a from t1 join t2 join t3 where t1.b=t2.b and t2.a = t3.a group by t2.a) tt on t.a = tt.a;") - tk.MustExec("create definer='root'@'localhost' view v1 as select t.a, t.b from t join (select count(*) as a from t1 join v on t1.b=v.b group by v.a) tt on t.a = tt.a;") - tk.MustExec("create definer='root'@'localhost' view v2 as select t.a, t.b from t join (select count(*) as a from t1 join v1 on t1.b=v1.b group by v1.a) tt on t.a = tt.a;") - - tk.MustExec("select * from v2") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select * from v2 using select /*+ qb_name(qb_v_2, v2.v1@sel_2 .v@sel_2 .@sel_2), merge_join(t1@qb_v_2), stream_agg(@qb_v_2), qb_name(qb_v_1, v2. v1@sel_2 .v@sel_2 .@sel_1), merge_join(t@qb_v_1) */ * from v2;") - tk.MustExec("select * from v2") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from `test` . `v2`") - require.Equal(t, res[0][1], "SELECT /*+ qb_name(`qb_v_2` , `v2`. `v1`@`sel_2`. `v`@`sel_2`. ``@`sel_2`) merge_join(`t1`@`qb_v_2`) stream_agg(@`qb_v_2`) qb_name(`qb_v_1` , `v2`. `v1`@`sel_2`. `v`@`sel_2`. ``@`sel_1`) merge_join(`t`@`qb_v_1`)*/ * FROM `test`.`v2`") - - tk.MustExec("drop global binding for select * from v2") - tk.MustExec("select * from v2") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) -} - -func TestPartitionPruningWithDateType(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a datetime) partition by range columns (a) (partition p1 values less than ('20000101'), partition p2 values less than ('2000-10-01'));") - tk.MustExec("insert into t values ('20000201'), ('19000101');") - - // cannot get the statistical information immediately - // tk.MustQuery(`SELECT PARTITION_NAME,TABLE_ROWS FROM INFORMATION_SCHEMA.PARTITIONS WHERE TABLE_NAME = 't';`).Check(testkit.Rows("p1 1", "p2 1")) - str := tk.MustQuery(`desc select * from t where a < '2000-01-01';`).Rows()[0][3].(string) - require.True(t, strings.Contains(str, "partition:p1")) -} - -func TestPartitionPruningForEQ(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a datetime, b int) partition by range(weekday(a)) (partition p0 values less than(10), partition p1 values less than (100))") - - is := tk.Session().GetInfoSchema().(infoschema.InfoSchema) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - pt := tbl.(table.PartitionedTable) - query, err := expression.ParseSimpleExprWithTableInfo(tk.Session(), "a = '2020-01-01 00:00:00'", tbl.Meta()) - require.NoError(t, err) - dbName := model.NewCIStr(tk.Session().GetSessionVars().CurrentDB) - columns, names, err := expression.ColumnInfos2ColumnsAndNames(tk.Session(), dbName, tbl.Meta().Name, tbl.Meta().Cols(), tbl.Meta()) - require.NoError(t, err) - // Even the partition is not monotonous, EQ condition should be prune! - // select * from t where a = '2020-01-01 00:00:00' - res, err := core.PartitionPruning(tk.Session(), pt, []expression.Expression{query}, nil, columns, names) - require.NoError(t, err) - require.Len(t, res, 1) - require.Equal(t, 0, res[0]) -} - -func TestErrNoDB(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create user test") - _, err := tk.Exec("grant select on test1111 to test@'%'") - require.Equal(t, core.ErrNoDB, errors.Cause(err)) - _, err = tk.Exec("grant select on * to test@'%'") - require.Equal(t, core.ErrNoDB, errors.Cause(err)) - _, err = tk.Exec("revoke select on * from test@'%'") - require.Equal(t, core.ErrNoDB, errors.Cause(err)) - tk.MustExec("use test") - tk.MustExec("create table test1111 (id int)") - tk.MustExec("grant select on test1111 to test@'%'") -} - -func TestNotReadOnlySQLOnTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b varchar(20))") - tk.MustExec(`set @@tidb_isolation_read_engines = "tiflash"`) - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - err := tk.ExecToErr("select * from t for update") - require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) - - err = tk.ExecToErr("insert into t select * from t") - require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) - - tk.MustExec("prepare stmt_insert from 'insert into t select * from t where t.a = ?'") - tk.MustExec("set @a=1") - err = tk.ExecToErr("execute stmt_insert using @a") - require.EqualError(t, err, `[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash', valid values can be 'tiflash, tikv'. Please check tiflash replica or check if the query is not readonly and sql mode is strict.`) -} - -func TestTimeToSecPushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a time(4))") - tk.MustExec("insert into t values('700:10:10.123456')") - tk.MustExec("insert into t values('20:20:20')") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "10000.00", "root", " MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "10000.00", "mpp[tiflash]", " ExchangeType: PassThrough"}, - {" └─Projection_4", "10000.00", "mpp[tiflash]", " time_to_sec(test.t.a)->Column#3"}, - {" └─TableFullScan_8", "10000.00", "mpp[tiflash]", "table:t", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select time_to_sec(a) from t;").Check(rows) -} - -func TestRightShiftPushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(2147483647, 32)") - tk.MustExec("insert into t values(12, 2)") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "rightshift(test.t.a, test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select a >> b from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestBitColumnPushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=1") - tk.MustExec("create table t1(a bit(8), b int)") - tk.MustExec("create table t2(a bit(8), b int)") - tk.MustExec("insert into t1 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)") - tk.MustExec("insert into t2 values ('1', 1), ('2', 2), ('3', 3), ('4', 4), ('1', 1), ('2', 2), ('3', 3), ('4', 4)") - sql := "select b from t1 where t1.b > (select min(t2.b) from t2 where t2.a < t1.a)" - tk.MustQuery(sql).Sort().Check(testkit.Rows("2", "2", "3", "3", "4", "4")) - rows := [][]interface{}{ - {"Projection_15", "root", "test.t1.b"}, - {"└─Apply_17", "root", "CARTESIAN inner join, other cond:gt(test.t1.b, Column#7)"}, - {" ├─TableReader_20(Build)", "root", "data:Selection_19"}, - {" │ └─Selection_19", "cop[tikv]", "not(isnull(test.t1.b))"}, - {" │ └─TableFullScan_18", "cop[tikv]", "keep order:false, stats:pseudo"}, - {" └─Selection_21(Probe)", "root", "not(isnull(Column#7))"}, - {" └─StreamAgg_23", "root", "funcs:min(test.t2.b)->Column#7"}, - {" └─TopN_24", "root", "test.t2.b, offset:0, count:1"}, - {" └─TableReader_32", "root", "data:TopN_31"}, - {" └─TopN_31", "cop[tikv]", "test.t2.b, offset:0, count:1"}, - {" └─Selection_30", "cop[tikv]", "lt(test.t2.a, test.t1.a), not(isnull(test.t2.b))"}, - {" └─TableFullScan_29", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - tk.MustExec("insert t1 values ('A', 1);") - sql = "select a from t1 where ascii(a)=65" - tk.MustQuery(sql).Check(testkit.Rows("A")) - rows = [][]interface{}{ - {"TableReader_7", "root", "data:Selection_6"}, - {"└─Selection_6", "cop[tikv]", "eq(ascii(cast(test.t1.a, var_string(1))), 65)"}, - {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = `eq(concat(cast(test.t1.a, var_string(1)), "A"), "AA")` - sql = "select a from t1 where concat(a, 'A')='AA'" - tk.MustQuery(sql).Check(testkit.Rows("A")) - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = `eq(cast(test.t1.a, binary(1)), "A")` - sql = "select a from t1 where binary a='A'" - tk.MustQuery(sql).Check(testkit.Rows("A")) - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = `eq(cast(test.t1.a, var_string(1)), "A")` - sql = "select a from t1 where cast(a as char)='A'" - tk.MustQuery(sql).Check(testkit.Rows("A")) - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - tk.MustExec("insert into mysql.expr_pushdown_blacklist values('bit', 'tikv','');") - tk.MustExec("admin reload expr_pushdown_blacklist;") - rows = [][]interface{}{ - {"Selection_5", "root", `eq(cast(test.t1.a, var_string(1)), "A")`}, - {"└─TableReader_7", "root", "data:TableFullScan_6"}, - {" └─TableFullScan_6", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - sql = "select a from t1 where cast(a as char)='A'" - tk.MustQuery(sql).Check(testkit.Rows("A")) - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name='bit'") - tk.MustExec("admin reload expr_pushdown_blacklist;") - sql = "select a from t1 where ascii(a)=65" - tk.MustQuery(sql).Check(testkit.Rows("A")) - rows = [][]interface{}{ - {"TableReader_7", "root", "data:Selection_6"}, - {"└─Selection_6", "cop[tikv]", "eq(ascii(cast(test.t1.a, var_string(1))), 65)"}, - {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery(fmt.Sprintf("explain analyze %s", sql)).CheckAt([]int{0, 3, 6}, rows) - - // test collation - tk.MustExec("update mysql.tidb set VARIABLE_VALUE='True' where VARIABLE_NAME='new_collation_enabled'") - tk.MustQuery("SELECT VARIABLE_VALUE FROM mysql.tidb WHERE VARIABLE_NAME='new_collation_enabled';").Check( - testkit.Rows("True")) - tk.MustExec("create table t3 (a bit(8));") - tk.MustExec("insert into t3 values (65)") - tk.MustExec("SET NAMES utf8mb4 COLLATE utf8mb4_bin") - tk.MustQuery("select a from t3 where cast(a as char) = 'a'").Check(testkit.Rows()) - tk.MustExec("SET NAMES utf8mb4 COLLATE utf8mb4_general_ci") - tk.MustQuery("select a from t3 where cast(a as char) = 'a'").Check(testkit.Rows("A")) -} - -func TestSysdatePushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id int signed, id2 int unsigned, c varchar(11), d datetime, b double, e bit(10))") - tk.MustExec("insert into t(id, id2, c, d) values (-1, 1, 'abc', '2021-12-12')") - rows := [][]interface{}{ - {"TableReader_7", "root", "data:Selection_6"}, - {"└─Selection_6", "cop[tikv]", "gt(test.t.d, sysdate())"}, - {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). - CheckAt([]int{0, 3, 6}, rows) - // assert sysdate isn't now after set global tidb_sysdate_is_now in the same session - tk.MustExec("set global tidb_sysdate_is_now='1'") - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). - CheckAt([]int{0, 3, 6}, rows) - - // assert sysdate is now after set global tidb_sysdate_is_now in the new session - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - now := time.Now() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/expression/injectNow", fmt.Sprintf(`return(%d)`, now.Unix()))) - rows[1][2] = fmt.Sprintf("gt(test.t.d, %v)", now.Format(time.DateTime)) - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). - CheckAt([]int{0, 3, 6}, rows) - failpoint.Disable("github.com/pingcap/tidb/expression/injectNow") - - // assert sysdate isn't now after set session tidb_sysdate_is_now false in the same session - tk.MustExec("set tidb_sysdate_is_now='0'") - rows[1][2] = "gt(test.t.d, sysdate())" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). - CheckAt([]int{0, 3, 6}, rows) -} - -func TestTimeScalarFunctionPushDownResult(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(col1 datetime, col2 datetime, y int(8), m int(8), d int(8)) CHARSET=utf8 COLLATE=utf8_general_ci;") - tk.MustExec("insert into t values ('2022-03-24 01:02:03.040506', '9999-12-31 23:59:59', 9999, 12, 31);") - testcases := []struct { - sql string - function string - }{ - { - sql: "select col1, hour(col1) from t where hour(col1)=hour('2022-03-24 01:02:03.040506');", - function: "hour", - }, - { - sql: "select col1, month(col1) from t where month(col1)=month('2022-03-24 01:02:03.040506');", - function: "month", - }, - { - sql: "select col1, minute(col1) from t where minute(col1)=minute('2022-03-24 01:02:03.040506');", - function: "minute", - }, - { - function: "second", - sql: "select col1, second(col1) from t where second(col1)=second('2022-03-24 01:02:03.040506');", - }, - { - function: "microsecond", - sql: "select col1, microsecond(col1) from t where microsecond(col1)=microsecond('2022-03-24 01:02:03.040506');", - }, - { - function: "dayName", - sql: "select col1, dayName(col1) from t where dayName(col1)=dayName('2022-03-24 01:02:03.040506');", - }, - { - function: "dayOfMonth", - sql: "select col1, dayOfMonth(col1) from t where dayOfMonth(col1)=dayOfMonth('2022-03-24 01:02:03.040506');", - }, - { - function: "dayOfWeek", - sql: "select col1, dayOfWeek(col1) from t where dayOfWeek(col1)=dayOfWeek('2022-03-24 01:02:03.040506');", - }, - { - function: "dayOfYear", - sql: "select col1, dayOfYear(col1) from t where dayOfYear(col1)=dayOfYear('2022-03-24 01:02:03.040506');", - }, - { - function: "Date", - sql: "select col1, Date(col1) from t where Date(col1)=Date('2022-03-24 01:02:03.040506');", - }, - { - function: "Week", - sql: "select col1, Week(col1) from t where Week(col1)=Week('2022-03-24 01:02:03.040506');", - }, - { - function: "time_to_sec", - sql: "select col1, time_to_sec (col1) from t where time_to_sec(col1)=time_to_sec('2022-03-24 01:02:03.040506');", - }, - { - function: "DateDiff", - sql: "select col1, DateDiff(col1, col2) from t where DateDiff(col1, col2)=DateDiff('2022-03-24 01:02:03.040506', '9999-12-31 23:59:59');", - }, - { - function: "MonthName", - sql: "select col1, MonthName(col1) from t where MonthName(col1)=MonthName('2022-03-24 01:02:03.040506');", - }, - { - function: "MakeDate", - sql: "select col1, MakeDate(9999, 31) from t where MakeDate(y, d)=MakeDate(9999, 31);", - }, - { - function: "MakeTime", - sql: "select col1, MakeTime(12, 12, 31) from t where MakeTime(m, m, d)=MakeTime(12, 12, 31);", - }, - } - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") - tk.MustExec("admin reload expr_pushdown_blacklist;") - for _, testcase := range testcases { - r1 := tk.MustQuery(testcase.sql).Rows() - tk.MustExec(fmt.Sprintf("insert into mysql.expr_pushdown_blacklist(name) values('%s');", testcase.function)) - tk.MustExec("admin reload expr_pushdown_blacklist;") - r2 := tk.MustQuery(testcase.sql).Rows() - require.EqualValues(t, r2, r1, testcase.sql) - } - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") - tk.MustExec("admin reload expr_pushdown_blacklist;") -} - -func TestNumberFunctionPushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int signed, b int unsigned,c double)") - tk.MustExec("insert into t values (-1,61,4.4)") - testcases := []struct { - sql string - function string - }{ - { - sql: "select a, mod(a,2) from t where mod(-1,2)=mod(a,2);", - function: "mod", - }, - { - sql: "select b, mod(b,2) from t where mod(61,2)=mod(b,2);", - function: "mod", - }, - { - sql: "select b,unhex(b) from t where unhex(61) = unhex(b)", - function: "unhex", - }, - { - sql: "select b, oct(b) from t where oct(61) = oct(b)", - function: "oct", - }, - { - sql: "select c, sin(c) from t where sin(4.4) = sin(c)", - function: "sin", - }, - { - sql: "select c, asin(c) from t where asin(4.4) = asin(c)", - function: "asin", - }, - { - sql: "select c, cos(c) from t where cos(4.4) = cos(c)", - function: "cos", - }, - { - sql: "select c, acos(c) from t where acos(4.4) = acos(c)", - function: "acos", - }, - { - sql: "select b,atan(b) from t where atan(61)=atan(b)", - function: "atan", - }, - { - sql: "select b, atan2(b, c) from t where atan2(61,4.4)=atan2(b,c)", - function: "atan2", - }, - { - sql: "select b,cot(b) from t where cot(61)=cot(b)", - function: "cot", - }, - { - sql: "select c from t where pi() < c", - function: "pi", - }, - } - for _, testcase := range testcases { - tk.MustExec("delete from mysql.expr_pushdown_blacklist where name != 'date_add'") - tk.MustExec("admin reload expr_pushdown_blacklist;") - r1 := tk.MustQuery(testcase.sql).Rows() - tk.MustExec(fmt.Sprintf("insert into mysql.expr_pushdown_blacklist(name) values('%s');", testcase.function)) - tk.MustExec("admin reload expr_pushdown_blacklist;") - r2 := tk.MustQuery(testcase.sql).Rows() - require.EqualValues(t, r2, r1, testcase.sql) - } -} - -func TestScalarFunctionPushDown(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id int signed, id2 int unsigned, c varchar(11), d datetime, b double, e bit(10))") - tk.MustExec("insert into t(id, id2, c, d) values (-1, 1, '{\"a\":1}', '2021-12-12')") - rows := [][]interface{}{ - {"TableReader_7", "root", "data:Selection_6"}, - {"└─Selection_6", "cop[tikv]", "right(test.t.c, 1)"}, - {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where right(c,1);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "mod(test.t.id, test.t.id)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id, id);"). - CheckAt([]int{0, 3, 6}, rows) - rows[1][2] = "mod(test.t.id, test.t.id2)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id, id2);"). - CheckAt([]int{0, 3, 6}, rows) - rows[1][2] = "mod(test.t.id2, test.t.id)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id2, id);"). - CheckAt([]int{0, 3, 6}, rows) - rows[1][2] = "mod(test.t.id2, test.t.id2)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where mod(id2, id2);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "sin(cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where sin(id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "asin(cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where asin(id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "cos(cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where cos(id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "acos(cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where acos(id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "atan(cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where atan(id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "atan2(cast(test.t.id, double BINARY), cast(test.t.id, double BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where atan2(id,id);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "hour(cast(test.t.d, time))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where hour(d);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "hour(cast(test.t.d, time))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where hour(d);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "minute(cast(test.t.d, time))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where minute(d);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "second(cast(test.t.d, time))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where second(d);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "month(test.t.d)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where month(d);"). - CheckAt([]int{0, 3, 6}, rows) - - //rows[1][2] = "dayname(test.t.d)" - //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where dayname(d);"). - // CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "dayofmonth(test.t.d)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where dayofmonth(d);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "from_days(test.t.id)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where from_days(id);"). - CheckAt([]int{0, 3, 6}, rows) - - //rows[1][2] = "last_day(test.t.d)" - //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where last_day(d);"). - // CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "gt(4, test.t.id)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where pi() > id;"). - CheckAt([]int{0, 3, 6}, rows) - - //rows[1][2] = "truncate(test.t.id, 0)" - //tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where truncate(id,0)"). - // CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "round(test.t.b)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where round(b)"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "date(test.t.d)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where date(d)"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "week(test.t.d)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where week(d)"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "datediff(test.t.d, test.t.d)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where datediff(d,d)"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "gt(test.t.d, sysdate())" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where d > sysdate()"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "ascii(cast(test.t.e, var_string(2)))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where ascii(e);"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "eq(json_valid(test.t.c), 1)" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where json_valid(c)=1;"). - CheckAt([]int{0, 3, 6}, rows) - - rows[1][2] = "json_contains(cast(test.t.c, json BINARY), cast(\"1\", json BINARY))" - tk.MustQuery("explain analyze select /*+read_from_storage(tikv[t])*/ * from t where json_contains(c, '1');"). - CheckAt([]int{0, 3, 6}, rows) -} - -func TestReverseUTF8PushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a varchar(256))") - tk.MustExec("insert into t values('pingcap')") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "reverse(test.t.a)->Column#3"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - - tk.MustQuery("explain select reverse(a) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestReversePushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a binary(32))") - tk.MustExec("insert into t values('pingcap')") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "reverse(test.t.a)->Column#3"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - - tk.MustQuery("explain select reverse(a) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestSpacePushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int)") - tk.MustExec("insert into t values(5)") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "space(test.t.a)->Column#3"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - - tk.MustQuery("explain select space(a) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestExplainAnalyzePointGet(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b varchar(20))") - tk.MustExec("insert into t values (1,1)") - - res := tk.MustQuery("explain analyze select * from t where a=1;") - checkExplain := func(rpc string) { - resBuff := bytes.NewBufferString("") - for _, row := range res.Rows() { - _, _ = fmt.Fprintf(resBuff, "%s\n", row) - } - explain := resBuff.String() - require.Containsf(t, explain, rpc+":{num_rpc:", "%s", explain) - require.Containsf(t, explain, "total_time:", "%s", explain) - } - checkExplain("Get") - res = tk.MustQuery("explain analyze select * from t where a in (1,2,3);") - checkExplain("BatchGet") -} - -func TestExplainAnalyzeDML(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(" create table t (a int, b int, unique index (a));") - tk.MustExec("insert into t values (1,1)") - - res := tk.MustQuery("explain analyze select * from t where a=1;") - checkExplain := func(rpc string) { - resBuff := bytes.NewBufferString("") - for _, row := range res.Rows() { - _, _ = fmt.Fprintf(resBuff, "%s\n", row) - } - explain := resBuff.String() - require.Containsf(t, explain, rpc+":{num_rpc:", "%s", explain) - require.Containsf(t, explain, "total_time:", "%s", explain) - } - checkExplain("Get") - res = tk.MustQuery("explain analyze insert ignore into t values (1,1),(2,2),(3,3),(4,4);") - checkExplain("BatchGet") -} - -func TestExplainAnalyzeDML2(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - cases := []struct { - prepare string - sql string - planRegexp string - }{ - // Test for alloc auto ID. - { - sql: "insert into t () values ()", - planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", - }, - // Test for rebase ID. - { - sql: "insert into t (a) values (99000000000)", - planRegexp: ".*prepare.*total.*, auto_id_allocator.*rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", - }, - // Test for alloc auto ID and rebase ID. - { - sql: "insert into t (a) values (null), (99000000000)", - planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*, insert.*", - }, - // Test for insert ignore. - { - sql: "insert ignore into t values (null,1), (2, 2), (99000000000, 3), (100000000000, 4)", - planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 2, Get.*num_rpc.*total_time.*commit_txn.*count: 3, prewrite.*get_commit_ts.*commit.*write_keys.*, check_insert.*", - }, - // Test for insert on duplicate. - { - sql: "insert into t values (null,null), (1,1),(2,2) on duplicate key update a = a + 100000000000", - planRegexp: ".*prepare.*total.*, auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*count: 2, prewrite.*get_commit_ts.*commit.*write_keys.*, check_insert.*", - }, - // Test for replace with alloc ID. - { - sql: "replace into t () values ()", - planRegexp: ".*auto_id_allocator.*alloc_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", - }, - // Test for replace with alloc ID and rebase ID. - { - sql: "replace into t (a) values (null), (99000000000)", - planRegexp: ".*auto_id_allocator.*alloc_cnt: 1, rebase_cnt: 1, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", - }, - // Test for update with rebase ID. - { - prepare: "insert into t values (1,1),(2,2)", - sql: "update t set a=a*100000000000", - planRegexp: ".*auto_id_allocator.*rebase_cnt: 2, Get.*num_rpc.*total_time.*commit_txn.*prewrite.*get_commit_ts.*commit.*write_keys.*", - }, - } - - for _, ca := range cases { - for i := 0; i < 3; i++ { - tk.MustExec("drop table if exists t") - switch i { - case 0: - tk.MustExec("create table t (a bigint auto_increment, b int, primary key (a));") - case 1: - tk.MustExec("create table t (a bigint unsigned auto_increment, b int, primary key (a));") - case 2: - if strings.Contains(ca.sql, "on duplicate key") { - continue - } - tk.MustExec("create table t (a bigint primary key auto_random(5), b int);") - tk.MustExec("set @@allow_auto_random_explicit_insert=1;") - default: - panic("should never happen") - } - if ca.prepare != "" { - tk.MustExec(ca.prepare) - } - res := tk.MustQuery("explain analyze " + ca.sql) - resBuff := bytes.NewBufferString("") - for _, row := range res.Rows() { - _, _ = fmt.Fprintf(resBuff, "%s\t", row) - } - explain := resBuff.String() - require.Regexpf(t, ca.planRegexp, explain, "idx: %v,sql: %v", i, ca.sql) - } - } - - // Test for table without auto id. - for _, ca := range cases { - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a bigint, b int);") - tk.MustExec("insert into t () values ()") - if ca.prepare != "" { - tk.MustExec(ca.prepare) - } - res := tk.MustQuery("explain analyze " + ca.sql) - resBuff := bytes.NewBufferString("") - for _, row := range res.Rows() { - _, _ = fmt.Fprintf(resBuff, "%s\t", row) - } - explain := resBuff.String() - require.NotContainsf(t, explain, "auto_id_allocator", "sql: %v, explain: %v", ca.sql, explain) - } -} - -func TestIssue20139(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int, c int) partition by range (id) (partition p0 values less than (4), partition p1 values less than (7))") - tk.MustExec("insert into t values(3, 3), (5, 5)") - plan := tk.MustQuery("explain format = 'brief' select * from t where c = 1 and id = c") - plan.Check(testkit.Rows( - "TableReader 0.01 root partition:p0 data:Selection", - "└─Selection 0.01 cop[tikv] eq(test.t.c, 1), eq(test.t.id, 1)", - " └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", - )) - tk.MustExec("drop table t") -} - -// Test for issue https://github.com/pingcap/tidb/issues/21607. -func TestConditionColPruneInPhysicalUnionScan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int, b int);") - tk.MustExec("begin;") - tk.MustExec("insert into t values (1, 2);") - tk.MustQuery("select count(*) from t where b = 1 and b in (3);"). - Check(testkit.Rows("0")) - - tk.MustExec("drop table t;") - tk.MustExec("create table t (a int, b int as (a + 1), c int as (b + 1));") - tk.MustExec("begin;") - tk.MustExec("insert into t (a) values (1);") - tk.MustQuery("select count(*) from t where b = 1 and b in (3);"). - Check(testkit.Rows("0")) - tk.MustQuery("select count(*) from t where c = 1 and c in (3);"). - Check(testkit.Rows("0")) -} - -func TestCreateViewIsolationRead(t *testing.T) { - store := testkit.CreateMockStore(t) - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk := testkit.NewTestKit(t, store) - tk.SetSession(se) - - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int, b int);") - tk.MustExec("set session tidb_isolation_read_engines='tiflash,tidb';") - // No error for CreateView. - tk.MustExec("create view v0 (a, avg_b) as select a, avg(b) from t group by a;") - tk.MustGetErrMsg("select * from v0;", "[planner:1815]Internal : No access path for table 't' is found with 'tidb_isolation_read_engines' = 'tiflash,tidb', valid values can be 'tikv'.") - tk.MustExec("set session tidb_isolation_read_engines='tikv,tiflash,tidb';") - tk.MustQuery("select * from v0;").Check(testkit.Rows()) -} - -func TestConflictReadFromStorage(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec(`create table t ( - a int, b int, c varchar(20), - primary key(a), key(b), key(c) - ) partition by range columns(a) ( - partition p0 values less than(6), - partition p1 values less than(11), - partition p2 values less than(16));`) - tk.MustExec(`insert into t values (1,1,"1"), (2,2,"2"), (8,8,"8"), (11,11,"11"), (15,15,"15")`) - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - tk.MustQuery(`explain select /*+ read_from_storage(tikv[t partition(p0)], tiflash[t partition(p1, p2)]) */ * from t`) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Storage hints are conflict, you can only specify one storage type of table test.t")) - tk.MustQuery(`explain select /*+ read_from_storage(tikv[t], tiflash[t]) */ * from t`) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1815 Storage hints are conflict, you can only specify one storage type of table test.t")) -} - -func TestSelectIgnoreTemporaryTableInView(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - tk.MustExec("create table t1 (a int, b int)") - tk.MustExec("create table t2 (c int, d int)") - tk.MustExec("create view v1 as select * from t1 order by a limit 5") - tk.MustExec("create view v2 as select * from ((select * from t1) union (select * from t2)) as tt order by a, b limit 5") - tk.MustExec("create view v3 as select * from v1 order by a limit 5") - tk.MustExec("create view v4 as select * from t1, t2 where t1.a = t2.c order by a, b limit 5") - tk.MustExec("create view v5 as select * from (select * from t1) as t1 order by a limit 5") - - tk.MustExec("insert into t1 values (1, 2), (3, 4)") - tk.MustExec("insert into t2 values (3, 5), (6, 7)") - - tk.MustExec("create temporary table t1 (a int, b int)") - tk.MustExec("create temporary table t2 (c int, d int)") - tk.MustQuery("select * from t1").Check(testkit.Rows()) - tk.MustQuery("select * from t2").Check(testkit.Rows()) - - tk.MustQuery("select * from v1").Check(testkit.Rows("1 2", "3 4")) - tk.MustQuery("select * from v2").Check(testkit.Rows("1 2", "3 4", "3 5", "6 7")) - tk.MustQuery("select * from v3").Check(testkit.Rows("1 2", "3 4")) - tk.MustQuery("select * from v4").Check(testkit.Rows("3 4 3 5")) - tk.MustQuery("select * from v5").Check(testkit.Rows("1 2", "3 4")) -} - -func TestIssue29503(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Status.RecordQPSbyDB = true - }) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t(a int);") - require.NoError(t, tk.ExecToErr("create binding for select 1 using select 1;")) - require.NoError(t, tk.ExecToErr("create binding for select a from t using select a from t;")) - res := tk.MustQuery("show session bindings;") - require.Len(t, res.Rows(), 2) -} - -func verifyTimestampOutOfRange(tk *testkit.TestKit) { - tk.MustQuery(`select * from t28424 where t != "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) - tk.MustQuery(`select * from t28424 where t < "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) - tk.MustQuery(`select * from t28424 where t <= "2038-1-19 3:14:08"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) - tk.MustQuery(`select * from t28424 where t >= "2038-1-19 3:14:08"`).Check(testkit.Rows()) - tk.MustQuery(`select * from t28424 where t > "2038-1-19 3:14:08"`).Check(testkit.Rows()) - tk.MustQuery(`select * from t28424 where t != "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) - tk.MustQuery(`select * from t28424 where t < "1970-1-1 0:0:0"`).Check(testkit.Rows()) - tk.MustQuery(`select * from t28424 where t <= "1970-1-1 0:0:0"`).Check(testkit.Rows()) - tk.MustQuery(`select * from t28424 where t >= "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) - tk.MustQuery(`select * from t28424 where t > "1970-1-1 0:0:0"`).Sort().Check(testkit.Rows("1970-01-01 00:00:01]\n[2038-01-19 03:14:07")) -} - -func TestIssue27949(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t27949") - tk.MustExec("create table t27949 (a int, b int, key(b))") - tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("IndexLookUp 10.00 root ", - "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t27949, index:b(b) range:[1,1], keep order:false, stats:pseudo", - "└─TableRowIDScan(Probe) 10.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) - tk.MustExec("create global binding for select * from t27949 where b=1 using select * from t27949 ignore index(b) where b=1") - tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("TableReader 10.00 root data:Selection", - "└─Selection 10.00 cop[tikv] eq(test.t27949.b, 1)", - " └─TableFullScan 10000.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) - tk.MustExec("set @@sql_select_limit=100") - tk.MustQuery("explain format = 'brief' select * from t27949 where b=1").Check(testkit.Rows("Limit 10.00 root offset:0, count:100", - "└─TableReader 10.00 root data:Limit", - " └─Limit 10.00 cop[tikv] offset:0, count:100", - " └─Selection 10.00 cop[tikv] eq(test.t27949.b, 1)", - " └─TableFullScan 10000.00 cop[tikv] table:t27949 keep order:false, stats:pseudo")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, index idx_a(a));") - tk.MustExec("create binding for select * from t using select * from t use index(idx_a);") - tk.MustExec("select * from t;") - tk.MustQuery("select @@last_plan_from_binding;").Check(testkit.Rows("1")) - tk.MustExec("prepare stmt from 'select * from t';") - tk.MustExec("execute stmt;") - tk.MustQuery("select @@last_plan_from_binding;").Check(testkit.Rows("1")) -} - -func TestIssue30804(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int, b int)") - // minimal reproduction of https://github.com/pingcap/tidb/issues/30804 - tk.MustExec("select avg(0) over w from t1 window w as (order by (select 1))") - // named window cannot be used in subquery - err := tk.ExecToErr("select avg(0) over w from t1 where b > (select sum(t2.a) over w from t2) window w as (partition by t1.b)") - require.True(t, core.ErrWindowNoSuchWindow.Equal(err)) - tk.MustExec("select avg(0) over w1 from t1 where b > (select sum(t2.a) over w2 from t2 window w2 as (partition by t2.b)) window w1 as (partition by t1.b)") -} - -func TestIssue31202(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t31202(a int primary key, b int);") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t31202", L: "t31202"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - tk.MustQuery("explain format = 'brief' select * from t31202;").Check(testkit.Rows( - "TableReader 10000.00 root MppVersion: 2, data:ExchangeSender", - "└─ExchangeSender 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TableFullScan 10000.00 mpp[tiflash] table:t31202 keep order:false, stats:pseudo")) - - tk.MustQuery("explain format = 'brief' select * from t31202 use index (primary);").Check(testkit.Rows( - "TableReader 10000.00 root data:TableFullScan", - "└─TableFullScan 10000.00 cop[tikv] table:t31202 keep order:false, stats:pseudo")) - tk.MustExec("drop table if exists t31202") -} - -func TestNaturalJoinUpdateSameTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create database natural_join_update") - defer tk.MustExec("drop database natural_join_update") - tk.MustExec("use natural_join_update") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustExec("update t1 as a natural join t1 b SET a.a = 2, b.b = 3") - tk.MustQuery("select * from t1").Sort().Check(testkit.Rows("2 3", "2 3")) - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (a int primary key, b int)") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (a int, b int) partition by hash (a) partitions 3") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustGetErrCode(`update t1 as a natural join t1 b SET a.a = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (A int, b int) partition by hash (b) partitions 3") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) - _, err := tk.Exec(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`) - require.Error(t, err) - require.Regexp(t, ".planner:1706.Primary key/partition key update is not allowed since the table is updated both as 'a' and 'B'.", err.Error()) - tk.MustExec("drop table t1") - tk.MustExec("create table t1 (A int, b int) partition by RANGE COLUMNS (b) (partition `pNeg` values less than (0),partition `pPos` values less than MAXVALUE)") - tk.MustExec("insert into t1 values (1,1),(2,2)") - tk.MustGetErrCode(`update t1 as a natural join t1 B SET a.A = 2, b.b = 3`, mysql.ErrMultiUpdateKeyConflict) - tk.MustExec("drop table t1") -} - -func TestAggPushToCopForCachedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec(`create table t32157( - process_code varchar(8) NOT NULL, - ctrl_class varchar(2) NOT NULL, - ctrl_type varchar(1) NOT NULL, - oper_no varchar(12) DEFAULT NULL, - modify_date datetime DEFAULT NULL, - d_c_flag varchar(2) NOT NULL, - PRIMARY KEY (process_code,ctrl_class,d_c_flag) NONCLUSTERED);`) - tk.MustExec("insert into t32157 values ('GDEP0071', '05', '1', '10000', '2016-06-29 00:00:00', 'C')") - tk.MustExec("insert into t32157 values ('GDEP0071', '05', '0', '0000', '2016-06-01 00:00:00', 'D')") - tk.MustExec("alter table t32157 cache") - - tk.MustQuery("explain format = 'brief' select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows( - "StreamAgg 1.00 root funcs:count(1)->Column#8]\n" + - "[└─UnionScan 10.00 root eq(test.t32157.process_code, \"GDEP0071\")]\n" + - "[ └─TableReader 10.00 root data:Selection]\n" + - "[ └─Selection 10.00 cop[tikv] eq(test.t32157.process_code, \"GDEP0071\")]\n" + - "[ └─TableFullScan 10000.00 cop[tikv] table:t32157 keep order:false, stats:pseudo")) - - require.Eventually(t, func() bool { - tk.MustQuery("select /*+AGG_TO_COP()*/ count(*) from t32157 ignore index(primary) where process_code = 'GDEP0071'").Check(testkit.Rows("2")) - return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache - }, 10*time.Second, 500*time.Millisecond) - - tk.MustExec("drop table if exists t31202") -} - -func TestTiFlashFineGrainedShuffleWithMaxTiFlashThreads(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@tidb_enforce_mpp = on") - tk.MustExec("drop table if exists t1;") - tk.MustExec("create table t1(c1 int, c2 int)") - tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - sql := "explain select row_number() over w1 from t1 window w1 as (partition by c1);" - - getStreamCountFromExplain := func(rows [][]interface{}) (res []uint64) { - re := regexp.MustCompile("stream_count: ([0-9]+)") - for _, row := range rows { - buf := bytes.NewBufferString("") - _, _ = fmt.Fprintf(buf, "%s\n", row) - if matched := re.FindStringSubmatch(buf.String()); matched != nil { - require.Equal(t, len(matched), 2) - c, err := strconv.ParseUint(matched[1], 10, 64) - require.NoError(t, err) - res = append(res, c) - } - } - return res - } - - // tiflash_fine_grained_shuffle_stream_count should be same with tidb_max_tiflash_threads. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") - tk.MustExec("set @@tidb_max_tiflash_threads = 10") - rows := tk.MustQuery(sql).Rows() - streamCount := getStreamCountFromExplain(rows) - // require.Equal(t, len(streamCount), 1) - require.Equal(t, uint64(10), streamCount[0]) - - // tiflash_fine_grained_shuffle_stream_count should be default value when tidb_max_tiflash_threads is -1. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") - tk.MustExec("set @@tidb_max_tiflash_threads = -1") - rows = tk.MustQuery(sql).Rows() - streamCount = getStreamCountFromExplain(rows) - // require.Equal(t, len(streamCount), 1) - require.Equal(t, uint64(variable.DefStreamCountWhenMaxThreadsNotSet), streamCount[0]) - - // tiflash_fine_grained_shuffle_stream_count should be default value when tidb_max_tiflash_threads is 0. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 0") - tk.MustExec("set @@tidb_max_tiflash_threads = 0") - rows = tk.MustQuery(sql).Rows() - streamCount = getStreamCountFromExplain(rows) - // require.Equal(t, len(streamCount), 1) - require.Equal(t, uint64(variable.DefStreamCountWhenMaxThreadsNotSet), streamCount[0]) - - // Disabled when tiflash_fine_grained_shuffle_stream_count is -1. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = -1") - tk.MustExec("set @@tidb_max_tiflash_threads = 10") - rows = tk.MustQuery(sql).Rows() - streamCount = getStreamCountFromExplain(rows) - require.Equal(t, len(streamCount), 0) - - // Test when tiflash_fine_grained_shuffle_stream_count is greater than 0. - tk.MustExec("set @@tiflash_fine_grained_shuffle_stream_count = 16") - tk.MustExec("set @@tidb_max_tiflash_threads = 10") - rows = tk.MustQuery(sql).Rows() - streamCount = getStreamCountFromExplain(rows) - // require.Equal(t, len(streamCount), 1) - require.Equal(t, uint64(16), streamCount[0]) -} - -func TestIssue33175(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table t (id bigint(45) unsigned not null, c varchar(20), primary key(id));") - tk.MustExec("insert into t values (9734095886065816707, 'a'), (10353107668348738101, 'b'), (0, 'c');") - tk.MustExec("begin") - tk.MustExec("insert into t values (33, 'd');") - tk.MustQuery("select max(id) from t;").Check(testkit.Rows("10353107668348738101")) - tk.MustExec("rollback") - - tk.MustExec("alter table t cache") - for { - tk.MustQuery("select max(id) from t;").Check(testkit.Rows("10353107668348738101")) - if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { - break - } - } - - // // With subquery, like the original issue case. - for { - tk.MustQuery("select * from t where id > (select max(id) from t where t.id > 0);").Check(testkit.Rows()) - if tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache { - break - } - } - - // Test order by desc / asc. - tk.MustQuery("select id from t order by id desc;").Check(testkit.Rows( - "10353107668348738101", - "9734095886065816707", - "0")) - - tk.MustQuery("select id from t order by id asc;").Check(testkit.Rows( - "0", - "9734095886065816707", - "10353107668348738101")) - - tk.MustExec("alter table t nocache") - tk.MustExec("drop table t") - - // Cover more code that use union scan - // TableReader/IndexReader/IndexLookup - for idx, q := range []string{ - "create temporary table t (id bigint unsigned, c int default null, index(id))", - "create temporary table t (id bigint unsigned primary key)", - } { - tk.MustExec(q) - tk.MustExec("insert into t(id) values (1), (3), (9734095886065816707), (9734095886065816708)") - tk.MustQuery("select min(id) from t").Check(testkit.Rows("1")) - tk.MustQuery("select max(id) from t").Check(testkit.Rows("9734095886065816708")) - tk.MustQuery("select id from t order by id asc").Check(testkit.Rows( - "1", "3", "9734095886065816707", "9734095886065816708")) - tk.MustQuery("select id from t order by id desc").Check(testkit.Rows( - "9734095886065816708", "9734095886065816707", "3", "1")) - if idx == 0 { - tk.MustQuery("select * from t order by id asc").Check(testkit.Rows( - "1 ", - "3 ", - "9734095886065816707 ", - "9734095886065816708 ")) - tk.MustQuery("select * from t order by id desc").Check(testkit.Rows( - "9734095886065816708 ", - "9734095886065816707 ", - "3 ", - "1 ")) - } - tk.MustExec("drop table t") - } - - // More and more test - tk.MustExec("create global temporary table `tmp1` (id bigint unsigned primary key) on commit delete rows;") - tk.MustExec("begin") - tk.MustExec("insert into tmp1 values (0),(1),(2),(65536),(9734095886065816707),(9734095886065816708);") - tk.MustQuery("select * from tmp1 where id <= 65534 or (id > 65535 and id < 9734095886065816700) or id >= 9734095886065816707 order by id desc;").Check(testkit.Rows( - "9734095886065816708", "9734095886065816707", "65536", "2", "1", "0")) - - tk.MustQuery("select * from tmp1 where id <= 65534 or (id > 65535 and id < 9734095886065816700) or id >= 9734095886065816707 order by id asc;").Check(testkit.Rows( - "0", "1", "2", "65536", "9734095886065816707", "9734095886065816708")) - - tk.MustExec("create global temporary table `tmp2` (id bigint primary key) on commit delete rows;") - tk.MustExec("begin") - tk.MustExec("insert into tmp2 values(-2),(-1),(0),(1),(2);") - tk.MustQuery("select * from tmp2 where id <= -1 or id > 0 order by id desc;").Check(testkit.Rows("2", "1", "-1", "-2")) - tk.MustQuery("select * from tmp2 where id <= -1 or id > 0 order by id asc;").Check(testkit.Rows("-2", "-1", "1", "2")) -} - -func TestIssue35083(t *testing.T) { - defer func() { - variable.SetSysVar(variable.TiDBOptProjectionPushDown, variable.BoolToOnOff(config.GetGlobalConfig().Performance.ProjectionPushDown)) - }() - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.ProjectionPushDown = true - }) - variable.SetSysVar(variable.TiDBOptProjectionPushDown, variable.BoolToOnOff(config.GetGlobalConfig().Performance.ProjectionPushDown)) - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a varchar(100), b int)") - tk.MustQuery("select @@tidb_opt_projection_push_down").Check(testkit.Rows("1")) - tk.MustQuery("explain format = 'brief' select cast(a as datetime) from t1").Check(testkit.Rows( - "TableReader 10000.00 root data:Projection", - "└─Projection 10000.00 cop[tikv] cast(test.t1.a, datetime BINARY)->Column#4", - " └─TableFullScan 10000.00 cop[tikv] table:t1 keep order:false, stats:pseudo")) -} - -func TestRepeatPushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(2147483647, 2)") - tk.MustExec("insert into t values(12, 2)") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "repeat(cast(test.t.a, var_string(20)), test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select repeat(a,b) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestIssue36194(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - // create virtual tiflash replica. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - tk.MustQuery("explain format = 'brief' select /*+ read_from_storage(tiflash[t]) */ * from t where a + 1 > 20 limit 100;;").Check(testkit.Rows( - "Limit 100.00 root offset:0, count:100", - "└─TableReader 100.00 root MppVersion: 2, data:ExchangeSender", - " └─ExchangeSender 100.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Limit 100.00 mpp[tiflash] offset:0, count:100", - " └─Selection 100.00 mpp[tiflash] gt(plus(test.t.a, 1), 20)", - " └─TableFullScan 125.00 mpp[tiflash] table:t pushed down filter:empty, keep order:false, stats:pseudo")) -} - -func TestGetFormatPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(location varchar(10));") - tk.MustExec("insert into t values('USA'), ('JIS'), ('ISO'), ('EUR'), ('INTERNAL')") - tk.MustExec("set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - tk.MustQuery("explain format = 'brief' select GET_FORMAT(DATE, location) from t;").Check(testkit.Rows( - "TableReader 10000.00 root MppVersion: 2, data:ExchangeSender", - "└─ExchangeSender 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Projection 10000.00 mpp[tiflash] get_format(DATE, test.t.location)->Column#3", - " └─TableFullScan 10000.00 mpp[tiflash] table:t keep order:false, stats:pseudo")) -} - -func TestAggWithJsonPushDownToTiFlash(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a json);") - tk.MustExec("insert into t values(null);") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // Create virtual tiflash replica info. - dom := domain.GetDomain(tk.Session()) - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"HashAgg_6", "root", "funcs:avg(Column#4)->Column#3"}, - {"└─Projection_19", "root", "cast(test.t.a, double BINARY)->Column#4"}, - {" └─TableReader_12", "root", "data:TableFullScan_11"}, - {" └─TableFullScan_11", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select avg(a) from t;").CheckAt([]int{0, 2, 4}, rows) - - rows = [][]interface{}{ - {"HashAgg_6", "root", "funcs:sum(Column#4)->Column#3"}, - {"└─Projection_19", "root", "cast(test.t.a, double BINARY)->Column#4"}, - {" └─TableReader_12", "root", "data:TableFullScan_11"}, - {" └─TableFullScan_11", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select sum(a) from t;").CheckAt([]int{0, 2, 4}, rows) - - rows = [][]interface{}{ - {"HashAgg_6", "root", "funcs:group_concat(Column#4 separator \",\")->Column#3"}, - {"└─Projection_13", "root", "cast(test.t.a, var_string(4294967295))->Column#4"}, - {" └─TableReader_10", "root", "data:TableFullScan_9"}, - {" └─TableFullScan_9", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select /*+ hash_agg() */ group_concat(a) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestLeftShiftPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(2147483647, 32)") - tk.MustExec("insert into t values(12, 2)") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "leftshift(test.t.a, test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select a << b from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestIssue36609(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("use test") - tk.MustExec("create table t1(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") - tk.MustExec("create table t2(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") - tk.MustExec("create table t3(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") - tk.MustExec("create table t4(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") - tk.MustExec("create table t5(a int, b int, c int, d int, index ia(a), index ib(b), index ic(c), index id(d))") - tk.MustQuery("select * from t3 straight_join t4 on t3.a = t4.b straight_join t2 on t3.d = t2.c straight_join t1 on t1.a = t2.b straight_join t5 on t4.c = t5.d where t2.b < 100 and t4.a = 10;") - tk.MustQuery("select * from information_schema.statements_summary;") -} - -func TestHexIntOrStrPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10));") - tk.MustExec("insert into t values(1, 'tiflash');") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "hex(test.t.a)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select hex(a) from t;").CheckAt([]int{0, 2, 4}, rows) - - rows = [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "hex(test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select hex(b) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestBinPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int);") - tk.MustExec("insert into t values(1);") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "bin(test.t.a)->Column#3"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select bin(a) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestEltPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(20))") - tk.MustExec("insert into t values(2147483647, '32')") - tk.MustExec("insert into t values(12, 'abc')") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "elt(test.t.a, test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select elt(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestRegexpInstrPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists test.t;") - tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), pos int, occur int, ret_op int, match_type varchar(30));") - tk.MustExec("insert into test.t values ('123', '12.', 1, 1, 0, ''), ('aBb', 'bb', 1, 1, 0, 'i'), ('ab\nabc', '^abc$', 1, 1, 0, 'm');") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "regexp_instr(test.t.expr, test.t.pattern, 1, 1, 0, test.t.match_type)->Column#8"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select regexp_instr(expr, pattern, 1, 1, 0, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestRegexpSubstrPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists test.t;") - tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), pos int, occur int, match_type varchar(30));") - tk.MustExec("insert into test.t values ('123', '12.', 1, 1, ''), ('aBb', 'bb', 1, 1, 'i'), ('ab\nabc', '^abc$', 1, 1, 'm');") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "regexp_substr(test.t.expr, test.t.pattern, 1, 1, test.t.match_type)->Column#7"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select regexp_substr(expr, pattern, 1, 1, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestRegexpReplacePushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists test.t;") - tk.MustExec("create table test.t (expr varchar(30), pattern varchar(30), repl varchar(30), pos int, occur int, match_type varchar(30));") - tk.MustExec("insert into test.t values ('123', '12.', '233', 1, 1, ''), ('aBb', 'bb', 'bc', 1, 1, 'i'), ('ab\nabc', '^abc$', 'd', 1, 1, 'm');") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "regexp_replace(test.t.expr, test.t.pattern, test.t.repl, 1, 1, test.t.match_type)->Column#8"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select regexp_replace(expr, pattern, repl, 1, 1, match_type) as res from test.t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestCastTimeAsDurationToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a date, b datetime(4))") - tk.MustExec("insert into t values('2021-10-26', '2021-10-26')") - tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11')") - tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.111111')") - tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.123456')") - tk.MustExec("insert into t values('2021-10-26', '2021-10-26 11:11:11.999999')") - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "cast(test.t.a, time BINARY)->Column#4, cast(test.t.b, time BINARY)->Column#5"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select cast(a as time), cast(b as time) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestUnhexPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(20));") - tk.MustExec("insert into t values(6162, '7469666C617368');") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1;") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "unhex(cast(test.t.a, var_string(20)))->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select unhex(a) from t;").CheckAt([]int{0, 2, 4}, rows) - - rows = [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "unhex(test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select unhex(b) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestLeastGretestStringPushDownToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a varchar(20), b varchar(20))") - tk.MustExec("insert into t values('123', '234')") - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "least(test.t.a, test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select least(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) - - rows = [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "greatest(test.t.a, test.t.b)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select greatest(a, b) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestTiFlashReadForWriteStmt(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1, 2)") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2(a int)") - tk.MustExec("set @@tidb_allow_mpp=1") - - // Default should be 1 - tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) - // Set ON - tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = ON") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) - // Set OFF - tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = OFF") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 tidb_enable_tiflash_read_for_write_stmt is always turned on. This variable has been deprecated and will be removed in the future releases")) - tk.MustQuery("select @@tidb_enable_tiflash_read_for_write_stmt").Check(testkit.Rows("1")) - - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - tbl2, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t2", L: "t2"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl2.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - checkRes := func(r [][]interface{}, pos int, expected string) { - check := false - for i := range r { - if r[i][pos] == expected { - check = true - break - } - } - require.Equal(t, check, true) - } - - check := func(query string) { - // If sql mode is strict, read does not push down to tiflash - tk.MustExec("set @@sql_mode = 'strict_trans_tables'") - tk.MustExec("set @@tidb_enforce_mpp=0") - rs := tk.MustQuery(query).Rows() - checkRes(rs, 2, "cop[tikv]") - tk.MustQuery("show warnings").Check(testkit.Rows()) - - // If sql mode is strict and tidb_enforce_mpp is on, read does not push down to tiflash - // and should return a warning. - tk.MustExec("set @@tidb_enforce_mpp=1") - rs = tk.MustQuery(query).Rows() - checkRes(rs, 2, "cop[tikv]") - rs = tk.MustQuery("show warnings").Rows() - checkRes(rs, 2, "MPP mode may be blocked because the query is not readonly and sql mode is strict.") - - // If sql mode is not strict, read should push down to tiflash - tk.MustExec("set @@sql_mode = ''") - rs = tk.MustQuery(query).Rows() - checkRes(rs, 2, "mpp[tiflash]") - tk.MustQuery("show warnings").Check(testkit.Rows()) - } - - // Insert into ... select - check("explain insert into t2 select a+b from t") - check("explain insert into t2 select t.a from t2 join t on t2.a = t.a") - - // Replace into ... select - check("explain replace into t2 select a+b from t") - - // CTE - check("explain update t set a=a+1 where b in (select a from t2 where t.a > t2.a)") -} - -func TestPointGetWithSelectLock(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, primary key(a, b));") - tk.MustExec("create table t1(c int unique, d int);") - tbl, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t", L: "t"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - tbl1, err := dom.InfoSchema().TableByName(model.CIStr{O: "test", L: "test"}, model.CIStr{O: "t1", L: "t1"}) - require.NoError(t, err) - // Set the hacked TiFlash replica for explain tests. - tbl1.Meta().TiFlashReplica = &model.TiFlashReplicaInfo{Count: 1, Available: true} - - sqls := []string{ - "explain select a, b from t where (a = 1 and b = 2) or (a =2 and b = 1) for update;", - "explain select a, b from t where a = 1 and b = 2 for update;", - "explain select c, d from t1 where c = 1 for update;", - "explain select c, d from t1 where c = 1 and d = 1 for update;", - "explain select c, d from t1 where (c = 1 or c = 2 )and d = 1 for update;", - "explain select c, d from t1 where c in (1,2,3,4) for update;", - } - tk.MustExec("set @@tidb_enable_tiflash_read_for_write_stmt = on;") - tk.MustExec("set @@sql_mode='';") - tk.MustExec("set @@tidb_isolation_read_engines='tidb,tiflash';") - tk.MustExec("begin;") - // assert point get / batch point get can't work with tiflash in interaction txn - for _, sql := range sqls { - err = tk.ExecToErr(sql) - require.Error(t, err) - } - // assert point get / batch point get can work with tikv in interaction txn - tk.MustExec("set @@tidb_isolation_read_engines='tidb,tikv,tiflash';") - for _, sql := range sqls { - tk.MustQuery(sql) - } - tk.MustExec("commit") - // assert point get / batch point get can work with tiflash in auto commit - tk.MustExec("set @@tidb_isolation_read_engines='tidb,tiflash';") - for _, sql := range sqls { - tk.MustQuery(sql) - } -} - -func TestPlanCacheForIndexRangeFallback(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec(`set @@tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0") // In this way `explain for connection id` doesn't display execution info. - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a varchar(10), b varchar(10), c varchar(10), index idx_a_b(a, b))") - tk.MustExec("set @@tidb_opt_range_max_size=1330") // 1330 is the memory usage of ["aa","aa"], ["bb","bb"], ["cc","cc"], ["dd","dd"], ["ee","ee"]. - rows := tk.MustQuery("explain format='brief' select * from t where a in ('aa', 'bb', 'cc', 'dd', 'ee')").Rows() - require.True(t, strings.Contains(rows[1][0].(string), "IndexRangeScan")) - require.True(t, strings.Contains(rows[1][4].(string), "range:[\"aa\",\"aa\"], [\"bb\",\"bb\"], [\"cc\",\"cc\"], [\"dd\",\"dd\"], [\"ee\",\"ee\"]")) - rows = tk.MustQuery("explain format='brief' select * from t where a in ('aaaaaaaaaa', 'bbbbbbbbbb', 'cccccccccc', 'dddddddddd', 'eeeeeeeeee')").Rows() - // 1330 is not enough for ["aaaaaaaaaa","aaaaaaaaaa"], ["bbbbbbbbbb","bbbbbbbbbb"], ["cccccccccc","cccccccccc"], ["dddddddddd","dddddddddd"], ["eeeeeeeeee","eeeeeeeeee"]. - // So it falls back to table full scan. - require.True(t, strings.Contains(rows[2][0].(string), "TableFullScan")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1330 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) - - // Test rebuilding ranges for the cached plan doesn't have memory limit. - tk.MustExec("prepare stmt1 from 'select * from t where a in (?, ?, ?, ?, ?)'") - tk.MustExec("set @a='aa', @b='bb', @c='cc', @d='dd', @e='ee'") - tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") - tk.MustQuery("show warnings").Check(testkit.Rows()) // Range fallback doesn't happen and the plan can be put into cache. - tk.MustExec("set @a='aaaaaaaaaa', @b='bbbbbbbbbb', @c='cccccccccc', @d='dddddddddd', @e='eeeeeeeeee'") - tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustExec("execute stmt1 using @a, @b, @c, @d, @e") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - // We don't limit range mem usage when rebuilding ranges for the cached plan. - // So ["aaaaaaaaaa","aaaaaaaaaa"], ["bbbbbbbbbb","bbbbbbbbbb"], ["cccccccccc","cccccccccc"], ["dddddddddd","dddddddddd"], ["eeeeeeeeee","eeeeeeeeee"] can still be built even if its mem usage exceeds 1330. - require.True(t, strings.Contains(rows[1][0].(string), "IndexRangeScan")) - require.True(t, strings.Contains(rows[1][4].(string), "range:[\"aaaaaaaaaa\",\"aaaaaaaaaa\"], [\"bbbbbbbbbb\",\"bbbbbbbbbb\"], [\"cccccccccc\",\"cccccccccc\"], [\"dddddddddd\",\"dddddddddd\"], [\"eeeeeeeeee\",\"eeeeeeeeee\"]")) - - // Test the plan with range fallback would not be put into cache. - tk.MustExec("prepare stmt2 from 'select * from t where a in (?, ?, ?, ?, ?) and b in (?, ?, ?, ?, ?)'") - tk.MustExec("set @a='aa', @b='bb', @c='cc', @d='dd', @e='ee', @f='ff', @g='gg', @h='hh', @i='ii', @j='jj'") - tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e, @f, @g, @h, @i, @j") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1330 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", - "Warning 1105 skip prepared plan-cache: in-list is too long")) - tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e, @f, @g, @h, @i, @j") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) -} - -func TestCorColRangeWithRangeMaxSize(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2, t3") - tk.MustExec("create table t1(a int)") - tk.MustExec("create table t2 (a int, b int, c int, index idx_a_b(a, b))") - tk.MustExec("create table t3(a int primary key)") - tk.MustExec("insert into t1 values (2), (4), (6)") - tk.MustExec("insert into t2 (a, b) values (1, 2), (3, 2), (5, 2)") - tk.MustExec("insert into t3 values (2), (4)") - tk.MustExec("insert into mysql.opt_rule_blacklist value(\"decorrelate\")") - tk.MustExec("admin reload opt_rule_blacklist") - defer func() { - tk.MustExec("delete from mysql.opt_rule_blacklist where name = \"decorrelate\"") - tk.MustExec("admin reload opt_rule_blacklist") - }() - - // Correlated column in index range. - tk.MustExec("set @@tidb_opt_range_max_size=1000") - rows := tk.MustQuery("explain format='brief' select * from t1 where exists (select * from t2 where t2.a in (1, 3, 5) and b >= 2 and t2.b = t1.a)").Rows() - // 1000 is not enough for [1 2,1 +inf], [3 2,3 +inf], [5 2,5 +inf]. So b >= 2 is not used to build ranges. - require.True(t, strings.Contains(rows[4][0].(string), "Selection")) - require.True(t, strings.Contains(rows[4][4].(string), "ge(test.t2.b, 2)")) - // 1000 is not enough for [1 ?,1 ?], [3 ?,3 ?], [5 ?,5 ?] but we don't restrict range mem usage when appending col = cor_col - // conditions to access conditions in SplitCorColAccessCondFromFilters. - require.True(t, strings.Contains(rows[5][0].(string), "IndexRangeScan")) - require.True(t, strings.Contains(rows[5][4].(string), "range: decided by [in(test.t2.a, 1, 3, 5) eq(test.t2.b, test.t1.a)]")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1000 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) - // We need to rebuild index ranges each time the value of correlated column test.t1.a changes. We don't restrict range - // mem usage when rebuilding index ranges, otherwise range fallback would happen when rebuilding index ranges, causing - // to wrong query results. - tk.MustQuery("select * from t1 where exists (select * from t2 where t2.a in (1, 3, 5) and b >= 2 and t2.b = t1.a)").Check(testkit.Rows("2")) - - // Correlated column in table range. - tk.MustExec("set @@tidb_opt_range_max_size=1") - rows = tk.MustQuery("explain format='brief' select * from t1 where exists (select * from t3 where t3.a = t1.a)").Rows() - // 1 is not enough for [?,?] but we don't restrict range mem usage when adding col = cor_col to access conditions. - require.True(t, strings.Contains(rows[4][0].(string), "TableRangeScan")) - require.True(t, strings.Contains(rows[4][4].(string), "range: decided by [eq(test.t3.a, test.t1.a)]")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - // We need to rebuild table ranges each time the value of correlated column test.t1.a changes. We don't restrict range - // mem usage when rebuilding table ranges, otherwise range fallback would happen when rebuilding table ranges, causing - // to wrong query results. - tk.MustQuery("select * from t1 where exists (select * from t3 where t3.a = t1.a)").Check(testkit.Rows("2", "4")) -} - -// TestExplainAnalyzeDMLCommit covers the issue #37373. -func TestExplainAnalyzeDMLCommit(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 int key, c2 int);") - tk.MustExec("insert into t values (1, 1)") - - err := failpoint.Enable("github.com/pingcap/tidb/session/mockSleepBeforeTxnCommit", "return(500)") - require.NoError(t, err) - defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/session/mockSleepBeforeTxnCommit") - }() - // The commit is paused by the failpoint, after the fix the explain statement - // execution should proceed after the commit finishes. - _, err = tk.Exec("explain analyze delete from t;") - require.NoError(t, err) - tk.MustQuery("select * from t").Check(testkit.Rows()) -} - -func TestPlanCacheForIndexJoinRangeFallback(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec(`set @@tidb_enable_prepared_plan_cache=1`) - tk.MustExec("set @@tidb_enable_collect_execution_info=0") - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2") - tk.MustExec("create table t1(a int, b varchar(10), c varchar(10), index idx_a_b(a, b))") - tk.MustExec("create table t2(d int)") - tk.MustExec("set @@tidb_opt_range_max_size=1275") - // 1275 is enough for [? a,? a], [? b,? b], [? c,? c] but is not enough for [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc]. - rows := tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('a', 'b', 'c')").Rows() - require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, a, b, c)]")) - tk.MustQuery("show warnings").Check(testkit.Rows()) - rows = tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('aaaaaa', 'bbbbbb', 'cccccc');").Rows() - require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d)]")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) - - tk.MustExec("prepare stmt1 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?)'") - tk.MustExec("set @a='a', @b='b', @c='c'") - tk.MustExec("execute stmt1 using @a, @b, @c") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec("set @a='aaaaaa', @b='bbbbbb', @c='cccccc'") - tk.MustExec("execute stmt1 using @a, @b, @c") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - tk.MustExec("execute stmt1 using @a, @b, @c") - tkProcess := tk.Session().ShowProcess() - ps := []*util.ProcessInfo{tkProcess} - tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) - rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() - // We don't limit range mem usage when rebuilding index join ranges for the cached plan. So [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc] can be built. - require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, aaaaaa, bbbbbb, cccccc)]")) - - // Test the plan with range fallback would not be put into cache. - tk.MustExec("prepare stmt2 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?, ?, ?)'") - tk.MustExec("set @a='a', @b='b', @c='c', @d='d', @e='e'") - tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", - "Warning 1105 skip prepared plan-cache: in-list is too long")) - tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) -} - -// https://github.com/pingcap/tidb/issues/38295. -// WARN: this test cannot be migrated to tests/integrationtest, because the error message of -// `SELECT t0.c1, t0.c2 FROM t0 GROUP BY MOD(t0.c0, DEFAULT(t0.c2));` is unstable. -func TestIssue38295(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("CREATE TABLE t0(c0 BLOB(298) , c1 BLOB(182) , c2 NUMERIC);") - tk.MustExec("CREATE VIEW v0(c0) AS SELECT t0.c1 FROM t0;") - tk.MustExec("INSERT INTO t0 VALUES (-1, 'a', '2046549365');") - tk.MustExec("CREATE INDEX i0 ON t0(c2);") - tk.MustGetErrCode("SELECT t0.c1, t0.c2 FROM t0 GROUP BY MOD(t0.c0, DEFAULT(t0.c2));", errno.ErrFieldNotInGroupBy) - tk.MustExec("UPDATE t0 SET c2=1413;") -} - -// https://github.com/pingcap/tidb/issues/41273 -func TestIssue41273(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`CREATE TABLE t ( - a set('nwbk','r5','1ad3u','van','ir1z','y','9m','f1','z','e6yd','wfev') NOT NULL DEFAULT 'ir1z,f1,e6yd', - b enum('soo2','4s4j','qi9om','8ue','i71o','qon','3','3feh','6o1i','5yebx','d') NOT NULL DEFAULT '8ue', - c varchar(66) DEFAULT '13mdezixgcn', - PRIMARY KEY (a,b) /*T![clustered_index] CLUSTERED */, - UNIQUE KEY ib(b), - KEY ia(a) - )ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin;`) - tk.MustExec("INSERT INTO t VALUES('ir1z,f1,e6yd','i71o','13mdezixgcn'),('ir1z,f1,e6yd','d','13mdezixgcn'),('nwbk','8ue','13mdezixgcn');") - expectedRes := []string{"ir1z,f1,e6yd d 13mdezixgcn", "ir1z,f1,e6yd i71o 13mdezixgcn", "nwbk 8ue 13mdezixgcn"} - tk.MustQuery("select * from t where a between 'e6yd' and 'z' or b <> '8ue';").Sort().Check(testkit.Rows(expectedRes...)) - tk.MustQuery("select /*+ use_index_merge(t) */ * from t where a between 'e6yd' and 'z' or b <> '8ue';").Sort().Check(testkit.Rows(expectedRes...)) - // For now tidb doesn't support push set type to TiKV, and column a is a set type, so we shouldn't generate a IndexMerge path. - require.False(t, tk.HasPlanForLastExecution("IndexMerge")) -} - -func TestIsIPv4ToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(v4 varchar(100), v6 varchar(100))") - tk.MustExec("insert into t values('123.123.123.123', 'F746:C349:48E3:22F2:81E0:0EA8:E7B6:8286')") - tk.MustExec("insert into t values('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000')") - tk.MustExec("insert into t values('127.0.0.1', '2001:0:2851:b9f0:6d:2326:9036:f37a')") - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "is_ipv4(test.t.v4)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select is_ipv4(v4) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestIsIPv6ToTiFlash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(v4 varchar(100), v6 varchar(100))") - tk.MustExec("insert into t values('123.123.123.123', 'F746:C349:48E3:22F2:81E0:0EA8:E7B6:8286')") - tk.MustExec("insert into t values('0.0.0.0', '0000:0000:0000:0000:0000:0000:0000:0000')") - tk.MustExec("insert into t values('127.0.0.1', '2001:0:2851:b9f0:6d:2326:9036:f37a')") - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := [][]interface{}{ - {"TableReader_10", "root", "MppVersion: 2, data:ExchangeSender_9"}, - {"└─ExchangeSender_9", "mpp[tiflash]", "ExchangeType: PassThrough"}, - {" └─Projection_4", "mpp[tiflash]", "is_ipv6(test.t.v6)->Column#4"}, - {" └─TableFullScan_8", "mpp[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select is_ipv6(v6) from t;").CheckAt([]int{0, 2, 4}, rows) -} - -// https://github.com/pingcap/tidb/issues/41355 -// The "virtual generated column" push down is not supported now. -// This test covers: TopN, Projection, Selection. -func TestVirtualExprPushDown(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("CREATE TABLE t (c1 int DEFAULT 0, c2 int GENERATED ALWAYS AS (abs(c1)) VIRTUAL);") - tk.MustExec("insert into t(c1) values(1), (-1), (2), (-2), (99), (-99);") - tk.MustExec("set @@tidb_isolation_read_engines = 'tikv'") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - // TopN to tikv. - rows := [][]interface{}{ - {"TopN_7", "root", "test.t.c2, offset:0, count:2"}, - {"└─TableReader_13", "root", "data:TableFullScan_12"}, - {" └─TableFullScan_12", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select * from t order by c2 limit 2;").CheckAt([]int{0, 2, 4}, rows) - - // Projection to tikv. - rows = [][]interface{}{ - {"Projection_3", "root", "plus(test.t.c1, test.t.c2)->Column#4"}, - {"└─TableReader_5", "root", "data:TableFullScan_4"}, - {" └─TableFullScan_4", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustExec("set session tidb_opt_projection_push_down='ON';") - tk.MustQuery("explain select c1 + c2 from t;").CheckAt([]int{0, 2, 4}, rows) - tk.MustExec("set session tidb_opt_projection_push_down='OFF';") - - // Selection to tikv. - rows = [][]interface{}{ - {"Selection_7", "root", "gt(test.t.c2, 1)"}, - {"└─TableReader_6", "root", "data:TableFullScan_5"}, - {" └─TableFullScan_5", "cop[tikv]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select * from t where c2 > 1;").CheckAt([]int{0, 2, 4}, rows) - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - // TopN to tiflash. - rows = [][]interface{}{ - {"TopN_7", "root", "test.t.c2, offset:0, count:2"}, - {"└─TableReader_15", "root", "data:TableFullScan_14"}, - {" └─TableFullScan_14", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select * from t order by c2 limit 2;").CheckAt([]int{0, 2, 4}, rows) - - // Projection to tiflash. - rows = [][]interface{}{ - {"Projection_3", "root", "plus(test.t.c1, test.t.c2)->Column#4"}, - {"└─TableReader_6", "root", "data:TableFullScan_5"}, - {" └─TableFullScan_5", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustExec("set session tidb_opt_projection_push_down='ON';") - tk.MustQuery("explain select c1 + c2 from t;").CheckAt([]int{0, 2, 4}, rows) - tk.MustExec("set session tidb_opt_projection_push_down='OFF';") - - // Selection to tiflash. - rows = [][]interface{}{ - {"Selection_8", "root", "gt(test.t.c2, 1)"}, - {"└─TableReader_7", "root", "data:TableFullScan_6"}, - {" └─TableFullScan_6", "cop[tiflash]", "keep order:false, stats:pseudo"}, - } - tk.MustQuery("explain select * from t where c2 > 1;").CheckAt([]int{0, 2, 4}, rows) -} - -func TestWindowRangeFramePushDownTiflash(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("drop table if exists test.first_range;") - tk.MustExec("create table test.first_range(p int not null, o int not null, v int not null, o_datetime datetime not null, o_time time not null);") - tk.MustExec("insert into test.first_range (p, o, v, o_datetime, o_time) values (0, 0, 0, '2023-9-20 11:17:10', '11:17:10');") - - tk.MustExec("drop table if exists test.first_range_d64;") - tk.MustExec("create table test.first_range_d64(p int not null, o decimal(17,1) not null, v int not null);") - tk.MustExec("insert into test.first_range_d64 (p, o, v) values (0, 0.1, 0), (1, 1.0, 1), (1, 2.1, 2), (1, 4.1, 4), (1, 8.1, 8), (2, 0.0, 0), (2, 3.1, 3), (2, 10.0, 10), (2, 13.1, 13), (2, 15.1, 15), (3, 1.1, 1), (3, 2.9, 3), (3, 5.1, 5), (3, 9.1, 9), (3, 15.0, 15), (3, 20.1, 20), (3, 31.1, 31);") - - tk.MustExec("set @@tidb_allow_mpp=1; set @@tidb_enforce_mpp=1") - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash'") - - // Create virtual tiflash replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "first_range" || tblInfo.Name.L == "first_range_d64" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - tk.MustExec(`set @@tidb_max_tiflash_threads=20`) - - tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 3 preceding and 0 following) as a from test.first_range;").Check(testkit.Rows( - "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", - "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o range between 3 preceding and 0 following), stream_count: 20", - " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o, stream_count: 20", - " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", - " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", - " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) - - tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 3 preceding and 2.9E0 following) as a from test.first_range;").Check(testkit.Rows( - "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", - "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o range between 3 preceding and 2.9 following), stream_count: 20", - " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o, stream_count: 20", - " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", - " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", - " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) - - tk.MustQuery("explain select *, first_value(v) over (partition by p order by o range between 2.3 preceding and 0 following) as a from test.first_range_d64;").Check(testkit.Rows( - "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", - "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range_d64.v)->Column#6 over(partition by test.first_range_d64.p order by test.first_range_d64.o range between 2.3 preceding and 0 following), stream_count: 20", - " └─Sort_13 10000.00 mpp[tiflash] test.first_range_d64.p, test.first_range_d64.o, stream_count: 20", - " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", - " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range_d64.p, collate: binary], stream_count: 20", - " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range_d64 keep order:false, stats:pseudo")) - - tk.MustQuery("explain select *, first_value(v) over (partition by p order by o_datetime range between interval 1 day preceding and interval 1 day following) as a from test.first_range;").Check(testkit.Rows( - "TableReader_23 10000.00 root MppVersion: 2, data:ExchangeSender_22", - "└─ExchangeSender_22 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─Window_21 10000.00 mpp[tiflash] first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o_datetime range between interval 1 \"DAY\" preceding and interval 1 \"DAY\" following), stream_count: 20", - " └─Sort_13 10000.00 mpp[tiflash] test.first_range.p, test.first_range.o_datetime, stream_count: 20", - " └─ExchangeReceiver_12 10000.00 mpp[tiflash] stream_count: 20", - " └─ExchangeSender_11 10000.00 mpp[tiflash] ExchangeType: HashPartition, Compression: FAST, Hash Cols: [name: test.first_range.p, collate: binary], stream_count: 20", - " └─TableFullScan_10 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) - - tk.MustQuery("explain select *, first_value(v) over (partition by p order by o_time range between interval 1 day preceding and interval 1 day following) as a from test.first_range;").Check(testkit.Rows( - "Shuffle_13 10000.00 root execution info: concurrency:5, data sources:[TableReader_11]", - "└─Window_8 10000.00 root first_value(test.first_range.v)->Column#8 over(partition by test.first_range.p order by test.first_range.o_time range between interval 1 \"DAY\" preceding and interval 1 \"DAY\" following)", - " └─Sort_12 10000.00 root test.first_range.p, test.first_range.o_time", - " └─TableReader_11 10000.00 root MppVersion: 2, data:ExchangeSender_10", - " └─ExchangeSender_10 10000.00 mpp[tiflash] ExchangeType: PassThrough", - " └─TableFullScan_9 10000.00 mpp[tiflash] table:first_range keep order:false, stats:pseudo")) -} - -func TestIssue46298(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists test.first_range;") - tk.MustExec("create table test.first_range(p int not null, o tinyint not null, v int not null);") - tk.MustExec("insert into test.first_range (p, o, v) values (0, 0, 0), (1, 1, 1), (1, 2, 2), (1, 4, 4), (1, 8, 8), (2, 0, 0), (2, 3, 3), (2, 10, 10), (2, 13, 13), (2, 15, 15), (3, 1, 1), (3, 3, 3), (3, 5, 5), (3, 9, 9), (3, 15, 15), (3, 20, 20), (3, 31, 31);") - tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") - tk.MustExec(`set @@tidb_enable_pipelined_window_function=0`) - tk.MustQuery("select *, first_value(v) over (partition by p order by o range between 3.1 preceding and 2.9 following) as a from test.first_range;") -} - -// https://github.com/pingcap/tidb/issues/41458 -func TestIssue41458(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("use test") - tk.MustExec(`create table t (a int, b int, c int, index ia(a));`) - tk.MustExec("select * from t t1 join t t2 on t1.b = t2.b join t t3 on t2.b=t3.b join t t4 on t3.b=t4.b where t3.a=1 and t2.a=2;") - rawRows := tk.MustQuery("select plan from information_schema.statements_summary where SCHEMA_NAME = 'test' and STMT_TYPE = 'Select';").Sort().Rows() - plan := rawRows[0][0].(string) - rows := strings.Split(plan, "\n") - rows = rows[1:] - expectedRes := []string{ - "Projection", - "└─HashJoin", - " ├─HashJoin", - " │ ├─HashJoin", - " │ │ ├─IndexLookUp", - " │ │ │ ├─IndexRangeScan", - " │ │ │ └─Selection", - " │ │ │ └─TableRowIDScan", - " │ │ └─IndexLookUp", - " │ │ ├─IndexRangeScan", - " │ │ └─Selection", - " │ │ └─TableRowIDScan", - " │ └─TableReader", - " │ └─Selection", - " │ └─TableFullScan", - " └─TableReader", - " └─Selection", - " └─TableFullScan", - } - for i, row := range rows { - fields := strings.Split(row, "\t") - fields = strings.Split(fields[1], "_") - op := fields[0] - require.Equalf(t, expectedRes[i], op, fmt.Sprintf("Mismatch at index %d.", i)) - } -} diff --git a/planner/core/internal/BUILD.bazel b/planner/core/internal/BUILD.bazel deleted file mode 100644 index b57b813b5f190..0000000000000 --- a/planner/core/internal/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = [ - "testkit.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/planner/core/internal", - visibility = ["//planner/core:__subpackages__"], - deps = [ - "//domain", - "//expression/aggregation", - "//parser/model", - "//sessionctx", - "//store/mockstore", - "//store/mockstore/unistore", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - ], -) diff --git a/planner/core/internal/base/BUILD.bazel b/planner/core/internal/base/BUILD.bazel deleted file mode 100644 index dcee218a0bae8..0000000000000 --- a/planner/core/internal/base/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "base", - srcs = ["plan.go"], - importpath = "github.com/pingcap/tidb/planner/core/internal/base", - visibility = ["//planner/core:__subpackages__"], - deps = [ - "//expression", - "//planner/property", - "//sessionctx", - "//types", - "//util/stringutil", - "//util/tracing", - ], -) diff --git a/planner/core/internal/base/plan.go b/planner/core/internal/base/plan.go deleted file mode 100644 index b644738b432c8..0000000000000 --- a/planner/core/internal/base/plan.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package base - -import ( - "fmt" - "strconv" - "unsafe" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tracing" -) - -// Plan Should be used as embedded struct in Plan implementations. -type Plan struct { - ctx sessionctx.Context - stats *property.StatsInfo - tp string - id int - blockOffset int -} - -// NewBasePlan creates a new base plan. -func NewBasePlan(ctx sessionctx.Context, tp string, offset int) Plan { - id := ctx.GetSessionVars().PlanID.Add(1) - return Plan{ - tp: tp, - id: int(id), - ctx: ctx, - blockOffset: offset, - } -} - -// SCtx is to get the sessionctx from the plan. -func (p *Plan) SCtx() sessionctx.Context { - return p.ctx -} - -// SetSCtx is to set the sessionctx for the plan. -func (p *Plan) SetSCtx(ctx sessionctx.Context) { - p.ctx = ctx -} - -// OutputNames returns the outputting names of each column. -func (*Plan) OutputNames() types.NameSlice { - return nil -} - -// SetOutputNames sets the outputting name by the given slice. -func (*Plan) SetOutputNames(_ types.NameSlice) {} - -// ReplaceExprColumns implements Plan interface. -func (*Plan) ReplaceExprColumns(_ map[string]*expression.Column) {} - -// ID is to get the id. -func (p *Plan) ID() int { - return p.id -} - -// SetID is to set id. -func (p *Plan) SetID(id int) { - p.id = id -} - -// StatsInfo is to get the stats info. -func (p *Plan) StatsInfo() *property.StatsInfo { - return p.stats -} - -// ExplainInfo is to get the explain information. -func (*Plan) ExplainInfo() string { - return "N/A" -} - -// ExplainID is to get the explain ID. -func (p *Plan) ExplainID() fmt.Stringer { - return stringutil.MemoizeStr(func() string { - if p.ctx != nil && p.ctx.GetSessionVars().StmtCtx.IgnoreExplainIDSuffix { - return p.tp - } - return p.tp + "_" + strconv.Itoa(p.id) - }) -} - -// TP is to get the tp. -func (p *Plan) TP() string { - return p.tp -} - -// SetTP is to set the tp. -func (p *Plan) SetTP(tp string) { - p.tp = tp -} - -// SelectBlockOffset is to get the select block offset. -func (p *Plan) SelectBlockOffset() int { - return p.blockOffset -} - -// SetStats sets the stats -func (p *Plan) SetStats(s *property.StatsInfo) { - p.stats = s -} - -// PlanSize is the size of BasePlan. -const PlanSize = int64(unsafe.Sizeof(Plan{})) - -// MemoryUsage return the memory usage of BasePlan -func (p *Plan) MemoryUsage() (sum int64) { - if p == nil { - return - } - - sum = PlanSize + int64(len(p.tp)) - return sum -} - -// BuildPlanTrace is to build the plan trace. -func (p *Plan) BuildPlanTrace() *tracing.PlanTrace { - planTrace := &tracing.PlanTrace{ID: p.ID(), TP: p.TP()} - return planTrace -} diff --git a/planner/core/internal/testkit.go b/planner/core/internal/testkit.go deleted file mode 100644 index 4faa81cc0a8cf..0000000000000 --- a/planner/core/internal/testkit.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "fmt" - "strings" - "testing" - - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" -) - -// SetTiFlashReplica is to set TiFlash replica -func SetTiFlashReplica(t *testing.T, dom *domain.Domain, dbName, tableName string) { - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr(dbName)) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == tableName { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } -} - -// WithMockTiFlash sets the mockStore to have N TiFlash stores (naming as tiflash0, tiflash1, ...). -func WithMockTiFlash(nodes int) mockstore.MockTiKVStoreOption { - return mockstore.WithMultipleOptions( - mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockCluster := c.(*unistore.Cluster) - _, _, region1 := mockstore.BootstrapWithSingleStore(c) - tiflashIdx := 0 - for tiflashIdx < nodes { - store2 := c.AllocID() - peer2 := c.AllocID() - addr2 := fmt.Sprintf("tiflash%d", tiflashIdx) - mockCluster.AddStore(store2, addr2, &metapb.StoreLabel{Key: "engine", Value: "tiflash"}) - mockCluster.AddPeer(region1, store2, peer2) - tiflashIdx++ - } - }), - mockstore.WithStoreType(mockstore.EmbedUnistore), - ) -} - -// GetFieldValue is to get field value. -func GetFieldValue(prefix, row string) string { - if idx := strings.Index(row, prefix); idx > 0 { - start := idx + len(prefix) - end := strings.Index(row[start:], " ") - if end > 0 { - value := row[start : start+end] - value = strings.Trim(value, ",") - return value - } - } - return "" -} diff --git a/planner/core/internal/util.go b/planner/core/internal/util.go deleted file mode 100644 index 8bc9c3b5b66e9..0000000000000 --- a/planner/core/internal/util.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/sessionctx" -) - -// WrapCastForAggFuncs wraps the args of an aggregate function with a cast function. -// If the mode is FinalMode or Partial2Mode, we do not need to wrap cast upon the args, -// since the types of the args are already the expected. -func WrapCastForAggFuncs(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc) { - for i := range aggFuncs { - if aggFuncs[i].Mode != aggregation.FinalMode && aggFuncs[i].Mode != aggregation.Partial2Mode { - aggFuncs[i].WrapCastForAggArgs(sctx) - } - } -} diff --git a/planner/core/issuetest/BUILD.bazel b/planner/core/issuetest/BUILD.bazel deleted file mode 100644 index d9b163636ef83..0000000000000 --- a/planner/core/issuetest/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "issuetest_test", - timeout = "short", - srcs = [ - "main_test.go", - "planner_issue_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - race = "on", - deps = [ - "//parser", - "//planner", - "//planner/core", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/issuetest/main_test.go b/planner/core/issuetest/main_test.go deleted file mode 100644 index a917b2293ed19..0000000000000 --- a/planner/core/issuetest/main_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package issuetest - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/planner/core/main_test.go b/planner/core/main_test.go deleted file mode 100644 index b1aff52659185..0000000000000 --- a/planner/core/main_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package core - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) -var planSuiteUnexportedData testdata.TestData -var indexMergeSuiteData testdata.TestData - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - testDataMap.LoadTestSuiteData("testdata", "plan_suite_unexported") - testDataMap.LoadTestSuiteData("testdata", "index_merge_suite") - testDataMap.LoadTestSuiteData("testdata", "runtime_filter_generator_suite") - - indexMergeSuiteData = testDataMap["index_merge_suite"] - planSuiteUnexportedData = testDataMap["plan_suite_unexported"] - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIndexMergeSuiteData() testdata.TestData { - return testDataMap["index_merge_suite"] -} - -func GetRuntimeFilterGeneratorData() testdata.TestData { - return testDataMap["runtime_filter_generator_suite"] -} diff --git a/planner/core/metrics/BUILD.bazel b/planner/core/metrics/BUILD.bazel deleted file mode 100644 index 86070d1901b66..0000000000000 --- a/planner/core/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/planner/core/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/planner/core/metrics/metrics.go b/planner/core/metrics/metrics.go deleted file mode 100644 index 0676129d59a86..0000000000000 --- a/planner/core/metrics/metrics.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// planner core metrics vars -var ( - PseudoEstimationNotAvailable prometheus.Counter - PseudoEstimationOutdate prometheus.Counter - preparedPlanCacheHitCounter prometheus.Counter - nonPreparedPlanCacheHitCounter prometheus.Counter - preparedPlanCacheMissCounter prometheus.Counter - nonPreparedPlanCacheMissCounter prometheus.Counter - nonPreparedPlanCacheUnsupportedCounter prometheus.Counter - sessionPlanCacheInstancePlanNumCounter prometheus.Gauge - sessionPlanCacheInstanceMemoryUsage prometheus.Gauge -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init planner core metrics vars. -func InitMetricsVars() { - PseudoEstimationNotAvailable = metrics.PseudoEstimation.WithLabelValues("nodata") - PseudoEstimationOutdate = metrics.PseudoEstimation.WithLabelValues("outdate") - // plan cache metrics - preparedPlanCacheHitCounter = metrics.PlanCacheCounter.WithLabelValues("prepared") - nonPreparedPlanCacheHitCounter = metrics.PlanCacheCounter.WithLabelValues("non-prepared") - preparedPlanCacheMissCounter = metrics.PlanCacheMissCounter.WithLabelValues("prepared") - nonPreparedPlanCacheMissCounter = metrics.PlanCacheMissCounter.WithLabelValues("non-prepared") - nonPreparedPlanCacheUnsupportedCounter = metrics.PlanCacheMissCounter.WithLabelValues("non-prepared-unsupported") - sessionPlanCacheInstancePlanNumCounter = metrics.PlanCacheInstancePlanNumCounter.WithLabelValues(" session-plan-cache") - sessionPlanCacheInstanceMemoryUsage = metrics.PlanCacheInstanceMemoryUsage.WithLabelValues(" session-plan-cache") -} - -// GetPlanCacheHitCounter get different plan cache hit counter -func GetPlanCacheHitCounter(isNonPrepared bool) prometheus.Counter { - if isNonPrepared { - return nonPreparedPlanCacheHitCounter - } - return preparedPlanCacheHitCounter -} - -// GetPlanCacheMissCounter get different plan cache miss counter -func GetPlanCacheMissCounter(isNonPrepared bool) prometheus.Counter { - if isNonPrepared { - return nonPreparedPlanCacheMissCounter - } - return preparedPlanCacheMissCounter -} - -// GetNonPrepPlanCacheUnsupportedCounter get non-prepared plan cache unsupported counter. -func GetNonPrepPlanCacheUnsupportedCounter() prometheus.Counter { - return nonPreparedPlanCacheUnsupportedCounter -} - -// GetPlanCacheInstanceNumCounter get different plan counter of plan cache -func GetPlanCacheInstanceNumCounter() prometheus.Gauge { - return sessionPlanCacheInstancePlanNumCounter -} - -// GetPlanCacheInstanceMemoryUsage get different plan memory usage counter of plan cache -func GetPlanCacheInstanceMemoryUsage() prometheus.Gauge { - return sessionPlanCacheInstanceMemoryUsage -} diff --git a/planner/core/mock.go b/planner/core/mock.go deleted file mode 100644 index 6d7dc112c3ec7..0000000000000 --- a/planner/core/mock.go +++ /dev/null @@ -1,611 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package core - -import ( - "fmt" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" -) - -func newLongType() types.FieldType { - return *(types.NewFieldType(mysql.TypeLong)) -} - -func newStringType() types.FieldType { - ft := types.NewFieldType(mysql.TypeVarchar) - charset, collate := types.DefaultCharsetForType(mysql.TypeVarchar) - ft.SetCharset(charset) - ft.SetCollate(collate) - return *ft -} - -func newDateType() types.FieldType { - ft := types.NewFieldType(mysql.TypeDate) - return *ft -} - -// MockSignedTable is only used for plan related tests. -func MockSignedTable() *model.TableInfo { - // column: a, b, c, d, e, c_str, d_str, e_str, f, g, h, i_date - // PK: a - // indices: c_d_e, e, f, g, f_g, c_d_e_str, e_d_c_str_prefix - indices := []*model.IndexInfo{ - { - Name: model.NewCIStr("c_d_e"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("c"), - Length: types.UnspecifiedLength, - Offset: 2, - }, - { - Name: model.NewCIStr("d"), - Length: types.UnspecifiedLength, - Offset: 3, - }, - { - Name: model.NewCIStr("e"), - Length: types.UnspecifiedLength, - Offset: 4, - }, - }, - State: model.StatePublic, - Unique: true, - }, - { - Name: model.NewCIStr("x"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("e"), - Length: types.UnspecifiedLength, - Offset: 4, - }, - }, - State: model.StateWriteOnly, - Unique: true, - }, - { - Name: model.NewCIStr("f"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("f"), - Length: types.UnspecifiedLength, - Offset: 8, - }, - }, - State: model.StatePublic, - Unique: true, - }, - { - Name: model.NewCIStr("g"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("g"), - Length: types.UnspecifiedLength, - Offset: 9, - }, - }, - State: model.StatePublic, - }, - { - Name: model.NewCIStr("f_g"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("f"), - Length: types.UnspecifiedLength, - Offset: 8, - }, - { - Name: model.NewCIStr("g"), - Length: types.UnspecifiedLength, - Offset: 9, - }, - }, - State: model.StatePublic, - Unique: true, - }, - { - Name: model.NewCIStr("c_d_e_str"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("c_str"), - Length: types.UnspecifiedLength, - Offset: 5, - }, - { - Name: model.NewCIStr("d_str"), - Length: types.UnspecifiedLength, - Offset: 6, - }, - { - Name: model.NewCIStr("e_str"), - Length: types.UnspecifiedLength, - Offset: 7, - }, - }, - State: model.StatePublic, - }, - { - Name: model.NewCIStr("e_d_c_str_prefix"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("e_str"), - Length: types.UnspecifiedLength, - Offset: 7, - }, - { - Name: model.NewCIStr("d_str"), - Length: types.UnspecifiedLength, - Offset: 6, - }, - { - Name: model.NewCIStr("c_str"), - Length: 10, - Offset: 5, - }, - }, - State: model.StatePublic, - }, - } - pkColumn := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 0, - Name: model.NewCIStr("a"), - FieldType: newLongType(), - ID: 1, - } - col0 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 1, - Name: model.NewCIStr("b"), - FieldType: newLongType(), - ID: 2, - } - col1 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 2, - Name: model.NewCIStr("c"), - FieldType: newLongType(), - ID: 3, - } - col2 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 3, - Name: model.NewCIStr("d"), - FieldType: newLongType(), - ID: 4, - } - col3 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 4, - Name: model.NewCIStr("e"), - FieldType: newLongType(), - ID: 5, - } - colStr1 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 5, - Name: model.NewCIStr("c_str"), - FieldType: newStringType(), - ID: 6, - } - colStr2 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 6, - Name: model.NewCIStr("d_str"), - FieldType: newStringType(), - ID: 7, - } - colStr3 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 7, - Name: model.NewCIStr("e_str"), - FieldType: newStringType(), - ID: 8, - } - col4 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 8, - Name: model.NewCIStr("f"), - FieldType: newLongType(), - ID: 9, - } - col5 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 9, - Name: model.NewCIStr("g"), - FieldType: newLongType(), - ID: 10, - } - col6 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 10, - Name: model.NewCIStr("h"), - FieldType: newLongType(), - ID: 11, - } - col7 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 11, - Name: model.NewCIStr("i_date"), - FieldType: newDateType(), - ID: 12, - } - pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag) - // Column 'b', 'c', 'd', 'f', 'g' is not null. - col0.SetFlag(mysql.NotNullFlag) - col1.SetFlag(mysql.NotNullFlag) - col2.SetFlag(mysql.NotNullFlag) - col4.SetFlag(mysql.NotNullFlag) - col5.SetFlag(mysql.NotNullFlag) - col6.SetFlag(mysql.NoDefaultValueFlag) - table := &model.TableInfo{ - Columns: []*model.ColumnInfo{pkColumn, col0, col1, col2, col3, colStr1, colStr2, colStr3, col4, col5, col6, col7}, - Indices: indices, - Name: model.NewCIStr("t"), - PKIsHandle: true, - } - return table -} - -// MockUnsignedTable is only used for plan related tests. -func MockUnsignedTable() *model.TableInfo { - // column: a, b, c - // PK: a - // indeices: b, b_c - indices := []*model.IndexInfo{ - { - Name: model.NewCIStr("b"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("b"), - Length: types.UnspecifiedLength, - Offset: 1, - }, - }, - State: model.StatePublic, - Unique: true, - }, - { - Name: model.NewCIStr("b_c"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("b"), - Length: types.UnspecifiedLength, - Offset: 1, - }, - { - Name: model.NewCIStr("c"), - Length: types.UnspecifiedLength, - Offset: 2, - }, - }, - State: model.StatePublic, - }, - } - pkColumn := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 0, - Name: model.NewCIStr("a"), - FieldType: newLongType(), - ID: 1, - } - col0 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 1, - Name: model.NewCIStr("b"), - FieldType: newLongType(), - ID: 2, - } - col1 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 2, - Name: model.NewCIStr("c"), - FieldType: newLongType(), - ID: 3, - } - pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag) - // Column 'b' is not null. - col0.SetFlag(mysql.NotNullFlag) - col1.SetFlag(mysql.UnsignedFlag) - table := &model.TableInfo{ - Columns: []*model.ColumnInfo{pkColumn, col0, col1}, - Indices: indices, - Name: model.NewCIStr("t2"), - PKIsHandle: true, - } - return table -} - -// MockNoPKTable is only used for plan related tests. -func MockNoPKTable() *model.TableInfo { - // column: a, b - col0 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 1, - Name: model.NewCIStr("a"), - FieldType: newLongType(), - ID: 2, - } - col1 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 2, - Name: model.NewCIStr("b"), - FieldType: newLongType(), - ID: 3, - } - // Column 'a', 'b' is not null. - col0.SetFlag(mysql.NotNullFlag) - col1.SetFlag(mysql.UnsignedFlag) - table := &model.TableInfo{ - Columns: []*model.ColumnInfo{col0, col1}, - Name: model.NewCIStr("t3"), - PKIsHandle: true, - } - return table -} - -// MockView is only used for plan related tests. -func MockView() *model.TableInfo { - selectStmt := "select b,c,d from t" - col0 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 0, - Name: model.NewCIStr("b"), - ID: 1, - } - col1 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 1, - Name: model.NewCIStr("c"), - ID: 2, - } - col2 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 2, - Name: model.NewCIStr("d"), - ID: 3, - } - view := &model.ViewInfo{SelectStmt: selectStmt, Security: model.SecurityDefiner, Definer: &auth.UserIdentity{Username: "root", Hostname: ""}, Cols: []model.CIStr{col0.Name, col1.Name, col2.Name}} - table := &model.TableInfo{ - Name: model.NewCIStr("v"), - Columns: []*model.ColumnInfo{col0, col1, col2}, - View: view, - } - return table -} - -// MockContext is only used for plan related tests. -func MockContext() sessionctx.Context { - ctx := mock.NewContext() - ctx.Store = &mock.Store{ - Client: &mock.Client{}, - } - initStatsCtx := mock.NewContext() - initStatsCtx.Store = &mock.Store{ - Client: &mock.Client{}, - } - ctx.GetSessionVars().CurrentDB = "test" - do := domain.NewMockDomain() - if err := do.CreateStatsHandle(ctx, initStatsCtx); err != nil { - panic(fmt.Sprintf("create mock context panic: %+v", err)) - } - domain.BindDomain(ctx, do) - return ctx -} - -// MockPartitionInfoSchema mocks an info schema for partition table. -func MockPartitionInfoSchema(definitions []model.PartitionDefinition) infoschema.InfoSchema { - tableInfo := MockSignedTable() - cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) - cols = append(cols, tableInfo.Columns...) - last := tableInfo.Columns[len(tableInfo.Columns)-1] - cols = append(cols, &model.ColumnInfo{ - State: model.StatePublic, - Offset: last.Offset + 1, - Name: model.NewCIStr("ptn"), - FieldType: newLongType(), - ID: last.ID + 1, - }) - partition := &model.PartitionInfo{ - Type: model.PartitionTypeRange, - Expr: "ptn", - Enable: true, - Definitions: definitions, - } - tableInfo.Columns = cols - tableInfo.Partition = partition - is := infoschema.MockInfoSchema([]*model.TableInfo{tableInfo}) - return is -} - -// MockRangePartitionTable mocks a range partition table for test -func MockRangePartitionTable() *model.TableInfo { - definitions := []model.PartitionDefinition{ - { - ID: 41, - Name: model.NewCIStr("p1"), - LessThan: []string{"16"}, - }, - { - ID: 42, - Name: model.NewCIStr("p2"), - LessThan: []string{"32"}, - }, - } - tableInfo := MockSignedTable() - tableInfo.Name = model.NewCIStr("pt1") - cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) - cols = append(cols, tableInfo.Columns...) - last := tableInfo.Columns[len(tableInfo.Columns)-1] - cols = append(cols, &model.ColumnInfo{ - State: model.StatePublic, - Offset: last.Offset + 1, - Name: model.NewCIStr("ptn"), - FieldType: newLongType(), - ID: last.ID + 1, - }) - partition := &model.PartitionInfo{ - Type: model.PartitionTypeRange, - Expr: "ptn", - Enable: true, - Definitions: definitions, - } - tableInfo.Columns = cols - tableInfo.Partition = partition - return tableInfo -} - -// MockHashPartitionTable mocks a hash partition table for test -func MockHashPartitionTable() *model.TableInfo { - definitions := []model.PartitionDefinition{ - { - ID: 51, - Name: model.NewCIStr("p1"), - }, - { - ID: 52, - Name: model.NewCIStr("p2"), - }, - } - tableInfo := MockSignedTable() - tableInfo.Name = model.NewCIStr("pt2") - cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) - cols = append(cols, tableInfo.Columns...) - last := tableInfo.Columns[len(tableInfo.Columns)-1] - cols = append(cols, &model.ColumnInfo{ - State: model.StatePublic, - Offset: last.Offset + 1, - Name: model.NewCIStr("ptn"), - FieldType: newLongType(), - ID: last.ID + 1, - }) - partition := &model.PartitionInfo{ - Type: model.PartitionTypeHash, - Expr: "ptn", - Enable: true, - Definitions: definitions, - Num: 2, - } - tableInfo.Columns = cols - tableInfo.Partition = partition - return tableInfo -} - -// MockListPartitionTable mocks a list partition table for test -func MockListPartitionTable() *model.TableInfo { - definitions := []model.PartitionDefinition{ - { - ID: 61, - Name: model.NewCIStr("p1"), - InValues: [][]string{ - { - "1", - }, - }, - }, - { - ID: 62, - Name: model.NewCIStr("p2"), - InValues: [][]string{ - { - "2", - }, - }, - }, - } - tableInfo := MockSignedTable() - tableInfo.Name = model.NewCIStr("pt3") - cols := make([]*model.ColumnInfo, 0, len(tableInfo.Columns)) - cols = append(cols, tableInfo.Columns...) - last := tableInfo.Columns[len(tableInfo.Columns)-1] - cols = append(cols, &model.ColumnInfo{ - State: model.StatePublic, - Offset: last.Offset + 1, - Name: model.NewCIStr("ptn"), - FieldType: newLongType(), - ID: last.ID + 1, - }) - partition := &model.PartitionInfo{ - Type: model.PartitionTypeList, - Expr: "ptn", - Enable: true, - Definitions: definitions, - Num: 2, - } - tableInfo.Columns = cols - tableInfo.Partition = partition - return tableInfo -} - -// MockStateNoneColumnTable is only used for plan related tests. -func MockStateNoneColumnTable() *model.TableInfo { - // column: a, b - // PK: a - // indeices: b - indices := []*model.IndexInfo{ - { - Name: model.NewCIStr("b"), - Columns: []*model.IndexColumn{ - { - Name: model.NewCIStr("b"), - Length: types.UnspecifiedLength, - Offset: 1, - }, - }, - State: model.StatePublic, - Unique: true, - }, - } - pkColumn := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 0, - Name: model.NewCIStr("a"), - FieldType: newLongType(), - ID: 1, - } - col0 := &model.ColumnInfo{ - State: model.StatePublic, - Offset: 1, - Name: model.NewCIStr("b"), - FieldType: newLongType(), - ID: 2, - } - col1 := &model.ColumnInfo{ - State: model.StateNone, - Offset: 2, - Name: model.NewCIStr("c"), - FieldType: newLongType(), - ID: 3, - } - pkColumn.SetFlag(mysql.PriKeyFlag | mysql.NotNullFlag | mysql.UnsignedFlag) - col0.SetFlag(mysql.NotNullFlag) - col1.SetFlag(mysql.UnsignedFlag) - table := &model.TableInfo{ - Columns: []*model.ColumnInfo{pkColumn, col0, col1}, - Indices: indices, - Name: model.NewCIStr("T_StateNoneColumn"), - PKIsHandle: true, - } - return table -} diff --git a/planner/core/physical_plan_test.go b/planner/core/physical_plan_test.go deleted file mode 100644 index 8763704f7dc2d..0000000000000 --- a/planner/core/physical_plan_test.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "context" - "fmt" - "math" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/core/internal" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/stretchr/testify/require" -) - -func TestAnalyzeBuildSucc(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tests := []struct { - sql string - succ bool - statsVer int - }{ - { - sql: "analyze table t with 0.1 samplerate", - succ: true, - statsVer: 2, - }, - { - sql: "analyze table t with 0.1 samplerate", - succ: false, - statsVer: 1, - }, - { - sql: "analyze table t with 10 samplerate", - succ: false, - statsVer: 2, - }, - { - sql: "analyze table t with 0.1 samplerate, 100000 samples", - succ: false, - statsVer: 2, - }, - { - sql: "analyze table t with 0.1 samplerate, 100000 samples", - succ: false, - statsVer: 1, - }, - } - - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, tt := range tests { - comment := fmt.Sprintf("The %v-th test failed", i) - tk.MustExec(fmt.Sprintf("set @@tidb_analyze_version=%v", tt.statsVer)) - - stmt, err := p.ParseOneStmt(tt.sql, "", "") - if tt.succ { - require.NoError(t, err, comment) - } else if err != nil { - continue - } - err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) - require.NoError(t, err) - _, _, err = planner.Optimize(context.Background(), tk.Session(), stmt, is) - if tt.succ { - require.NoError(t, err, comment) - } else { - require.Error(t, err, comment) - } - } -} - -func TestAnalyzeSetRate(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - tests := []struct { - sql string - rate float64 - }{ - { - sql: "analyze table t", - rate: -1, - }, - { - sql: "analyze table t with 0.1 samplerate", - rate: 0.1, - }, - { - sql: "analyze table t with 10000 samples", - rate: -1, - }, - } - - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - for i, tt := range tests { - comment := fmt.Sprintf("The %v-th test failed", i) - stmt, err := p.ParseOneStmt(tt.sql, "", "") - require.NoError(t, err, comment) - - err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) - require.NoError(t, err, comment) - p, _, err := planner.Optimize(context.Background(), tk.Session(), stmt, is) - require.NoError(t, err, comment) - ana := p.(*core.Analyze) - require.Equal(t, tt.rate, math.Float64frombits(ana.Opts[ast.AnalyzeOptSampleRate])) - } -} - -type overrideStore struct{ kv.Storage } - -func (store overrideStore) GetClient() kv.Client { - cli := store.Storage.GetClient() - return overrideClient{cli} -} - -type overrideClient struct{ kv.Client } - -func (cli overrideClient) IsRequestTypeSupported(_, _ int64) bool { - return false -} - -func TestRequestTypeSupportedOff(t *testing.T) { - store := testkit.CreateMockStore(t) - se, err := session.CreateSession4Test(overrideStore{store}) - require.NoError(t, err) - _, err = se.Execute(context.Background(), "use test") - require.NoError(t, err) - - sql := "select * from t where a in (1, 10, 20)" - expect := "TableReader(Table(t))->Sel([in(test.t.a, 1, 10, 20)])" - - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - stmt, err := parser.New().ParseOneStmt(sql, "", "") - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), se, stmt, is) - require.NoError(t, err) - require.Equal(t, expect, core.ToString(p), fmt.Sprintf("sql: %s", sql)) -} - -func TestDoSubQuery(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tests := []struct { - sql string - best string - }{ - { - sql: "do 1 in (select a from t)", - best: "LeftHashJoin{Dual->PointGet(Handle(t.a)1)}->Projection", - }, - } - - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for _, tt := range tests { - comment := fmt.Sprintf("for %s", tt.sql) - stmt, err := p.ParseOneStmt(tt.sql, "", "") - require.NoError(t, err, comment) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err) - require.Equal(t, tt.best, core.ToString(p), comment) - } -} - -func TestIndexLookupCartesianJoin(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - stmt, err := parser.New().ParseOneStmt("select /*+ TIDB_INLJ(t1, t2) */ * from t t1 join t t2", "", "") - require.NoError(t, err) - - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err) - require.Equal(t, "LeftHashJoin{TableReader(Table(t))->TableReader(Table(t))}", core.ToString(p)) - - warnings := tk.Session().GetSessionVars().StmtCtx.GetWarnings() - lastWarn := warnings[len(warnings)-1] - err = core.ErrInternal.GenWithStack("TIDB_INLJ hint is inapplicable without column equal ON condition") - require.True(t, terror.ErrorEqual(err, lastWarn.Err)) -} - -func TestMPPHintsWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int, b int, c int)") - tk.MustExec("alter table t set tiflash replica 1") - tk.MustExec("set @@session.tidb_allow_mpp=ON") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tk.MustExec("explain select a, sum(b) from t group by a, c") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select a, sum(b) from t group by a, c using select /*+ read_from_storage(tiflash[t]), MPP_1PHASE_AGG() */ a, sum(b) from t group by a, c;") - tk.MustExec("explain select a, sum(b) from t group by a, c") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select `a` , sum ( `b` ) from `test` . `t` group by `a` , `c`") - require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t`]) MPP_1PHASE_AGG()*/ `a`,sum(`b`) FROM `test`.`t` GROUP BY `a`,`c`") - tk.MustExec("create global binding for select a, sum(b) from t group by a, c using select /*+ read_from_storage(tiflash[t]), MPP_2PHASE_AGG() */ a, sum(b) from t group by a, c;") - tk.MustExec("explain select a, sum(b) from t group by a, c") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select `a` , sum ( `b` ) from `test` . `t` group by `a` , `c`") - require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t`]) MPP_2PHASE_AGG()*/ `a`,sum(`b`) FROM `test`.`t` GROUP BY `a`,`c`") - tk.MustExec("drop global binding for select a, sum(b) from t group by a, c;") - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) - - tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select * from t t1, t t2 where t1.a=t2.a using select /*+ read_from_storage(tiflash[t1, t2]), shuffle_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a") - tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` ) join `test` . `t` as `t2` where `t1` . `a` = `t2` . `a`") - require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t1`, `t2`]) shuffle_join(`t1`, `t2`)*/ * FROM (`test`.`t` AS `t1`) JOIN `test`.`t` AS `t2` WHERE `t1`.`a` = `t2`.`a`") - tk.MustExec("create global binding for select * from t t1, t t2 where t1.a=t2.a using select /*+ read_from_storage(tiflash[t1, t2]), broadcast_join(t1, t2) */ * from t t1, t t2 where t1.a=t2.a;") - tk.MustExec("explain select * from t t1, t t2 where t1.a=t2.a") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` ) join `test` . `t` as `t2` where `t1` . `a` = `t2` . `a`") - require.Equal(t, res[0][1], "SELECT /*+ read_from_storage(tiflash[`t1`, `t2`]) broadcast_join(`t1`, `t2`)*/ * FROM (`test`.`t` AS `t1`) JOIN `test`.`t` AS `t2` WHERE `t1`.`a` = `t2`.`a`") - tk.MustExec("drop global binding for select * from t t1, t t2 where t1.a=t2.a;") - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) -} - -func TestJoinHintCompatibilityWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t, internal.WithMockTiFlash(2)) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec("create global binding for select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b using select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t` as `t1` join `test` . `t` as `t2` ) join `test` . `t` as `t3` where `t1` . `a` = `t2` . `a` and `t2` . `b` = `t3` . `b`") - require.Equal(t, res[0][1], "SELECT /*+ leading(`t2`) hash_join(`t2`)*/ * FROM (`test`.`t` AS `t1` JOIN `test`.`t` AS `t2`) JOIN `test`.`t` AS `t3` WHERE `t1`.`a` = `t2`.`a` AND `t2`.`b` = `t3`.`b`") - tk.MustExec("select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec("drop global binding for select * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) -} - -func TestJoinHintCompatibilityWithVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table t (a int, b int, c int, index idx_a(a), index idx_b(b))") - tb := external.GetTableByName(t, tk, "test", "t") - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tk.MustExec("set @@session.tidb_opt_advanced_join_hint=0") - tk.MustExec("select /*+ leading(t2), hash_join(t2) */ * from t t1 join t t2 join t t3 where t1.a = t2.a and t2.b = t3.b;") - res := tk.MustQuery("show warnings").Rows() - require.Equal(t, len(res) > 0, true) -} - -func TestHintAlias(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tests := []struct { - sql1 string - sql2 string - }{ - { - sql1: "select /*+ TIDB_SMJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_INLJ(t3) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - sql2: "select /*+ MERGE_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ INL_JOIN(t3) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - }, - { - sql1: "select /*+ TIDB_HJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_SMJ(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - sql2: "select /*+ HASH_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ MERGE_JOIN(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - }, - { - sql1: "select /*+ TIDB_INLJ(t1) */ t1.a, t1.b from t t1, (select /*+ TIDB_HJ(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - sql2: "select /*+ INL_JOIN(t1) */ t1.a, t1.b from t t1, (select /*+ HASH_JOIN(t2) */ t2.a from t t2, t t3 where t2.a = t3.c) s where t1.a=s.a", - }, - } - ctx := context.TODO() - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for i, tt := range tests { - comment := fmt.Sprintf("case:%v sql1:%s sql2:%s", i, tt.sql1, tt.sql2) - stmt1, err := p.ParseOneStmt(tt.sql1, "", "") - require.NoError(t, err, comment) - stmt2, err := p.ParseOneStmt(tt.sql2, "", "") - require.NoError(t, err, comment) - - p1, _, err := planner.Optimize(ctx, tk.Session(), stmt1, is) - require.NoError(t, err) - p2, _, err := planner.Optimize(ctx, tk.Session(), stmt2, is) - require.NoError(t, err) - - require.Equal(t, core.ToString(p2), core.ToString(p1)) - } -} - -func TestDAGPlanBuilderSplitAvg(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tests := []struct { - sql string - plan string - }{ - { - sql: "select avg(a),avg(b),avg(c) from t", - plan: "TableReader(Table(t)->HashAgg)->HashAgg", - }, - { - sql: "select /*+ HASH_AGG() */ avg(a),avg(b),avg(c) from t", - plan: "TableReader(Table(t)->HashAgg)->HashAgg", - }, - } - - p := parser.New() - is := infoschema.MockInfoSchema([]*model.TableInfo{core.MockSignedTable(), core.MockUnsignedTable()}) - - for _, tt := range tests { - comment := fmt.Sprintf("for %s", tt.sql) - stmt, err := p.ParseOneStmt(tt.sql, "", "") - require.NoError(t, err, comment) - - err = core.Preprocess(context.Background(), tk.Session(), stmt, core.WithPreprocessorReturn(&core.PreprocessorReturn{InfoSchema: is})) - require.NoError(t, err) - p, _, err := planner.Optimize(context.TODO(), tk.Session(), stmt, is) - require.NoError(t, err, comment) - - require.Equal(t, tt.plan, core.ToString(p), comment) - root, ok := p.(core.PhysicalPlan) - if !ok { - continue - } - testDAGPlanBuilderSplitAvg(t, root) - } -} - -func testDAGPlanBuilderSplitAvg(t *testing.T, root core.PhysicalPlan) { - if p, ok := root.(*core.PhysicalTableReader); ok { - if p.TablePlans != nil { - baseAgg := p.TablePlans[len(p.TablePlans)-1] - if agg, ok := baseAgg.(*core.PhysicalHashAgg); ok { - for i, aggfunc := range agg.AggFuncs { - require.Equal(t, aggfunc.RetTp, agg.Schema().Columns[i].RetType) - } - } - if agg, ok := baseAgg.(*core.PhysicalStreamAgg); ok { - for i, aggfunc := range agg.AggFuncs { - require.Equal(t, aggfunc.RetTp, agg.Schema().Columns[i].RetType) - } - } - } - } - - childs := root.Children() - if childs == nil { - return - } - for _, son := range childs { - testDAGPlanBuilderSplitAvg(t, son) - } -} - -func TestHJBuildAndProbeHintWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec("create table t(a int, b int, key(a));") - tk.MustExec("create table t1(a int, b int, key(a));") - tk.MustExec("create table t2(a int, b int, key(a));") - tk.MustExec("create table t3(a int, b int, key(a));") - - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ hash_join_build(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ hash_join_build(t1)*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") - - tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ hash_join_probe(t1) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ hash_join_probe(t1)*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") - - tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) -} - -func TestPhysicalPlanMemoryTrace(t *testing.T) { - // PhysicalSort - ls := core.PhysicalSort{} - size := ls.MemoryUsage() - ls.ByItems = append(ls.ByItems, &util.ByItems{}) - require.Greater(t, ls.MemoryUsage(), size) - - // PhysicalProperty - pp := property.PhysicalProperty{} - size = pp.MemoryUsage() - pp.MPPPartitionCols = append(pp.MPPPartitionCols, &property.MPPPartitionColumn{}) - require.Greater(t, pp.MemoryUsage(), size) -} diff --git a/planner/core/plan.go b/planner/core/plan.go deleted file mode 100644 index da472ded3f259..0000000000000 --- a/planner/core/plan.go +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package core - -import ( - "fmt" - "math" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/core/internal/base" - fd "github.com/pingcap/tidb/planner/funcdep" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tipb/go-tipb" -) - -// Plan is the description of an execution flow. -// It is created from ast.Node first, then optimized by the optimizer, -// finally used by the executor to create a Cursor which executes the statement. -type Plan interface { - // Get the schema. - Schema() *expression.Schema - - // Get the ID. - ID() int - - // TP get the plan type. - TP() string - - // Get the ID in explain statement - ExplainID() fmt.Stringer - - // ExplainInfo returns operator information to be explained. - ExplainInfo() string - - // ReplaceExprColumns replace all the column reference in the plan's expression node. - ReplaceExprColumns(replace map[string]*expression.Column) - - SCtx() sessionctx.Context - - // StatsInfo will return the property.StatsInfo for this plan. - StatsInfo() *property.StatsInfo - - // OutputNames returns the outputting names of each column. - OutputNames() types.NameSlice - - // SetOutputNames sets the outputting name by the given slice. - SetOutputNames(names types.NameSlice) - - SelectBlockOffset() int - - BuildPlanTrace() *tracing.PlanTrace -} - -func enforceProperty(p *property.PhysicalProperty, tsk task, ctx sessionctx.Context) task { - if p.TaskTp == property.MppTaskType { - mpp, ok := tsk.(*mppTask) - if !ok || mpp.invalid() { - return invalidTask - } - if !p.IsSortItemAllForPartition() { - ctx.GetSessionVars().RaiseWarningWhenMPPEnforced("MPP mode may be blocked because operator `Sort` is not supported now.") - return invalidTask - } - tsk = mpp.enforceExchanger(p) - } - if p.IsSortItemEmpty() || tsk.plan() == nil { - return tsk - } - if p.TaskTp != property.MppTaskType { - tsk = tsk.convertToRootTask(ctx) - } - sortReqProp := &property.PhysicalProperty{TaskTp: property.RootTaskType, SortItems: p.SortItems, ExpectedCnt: math.MaxFloat64} - sort := PhysicalSort{ - ByItems: make([]*util.ByItems, 0, len(p.SortItems)), - IsPartialSort: p.IsSortItemAllForPartition(), - }.Init(ctx, tsk.plan().StatsInfo(), tsk.plan().SelectBlockOffset(), sortReqProp) - for _, col := range p.SortItems { - sort.ByItems = append(sort.ByItems, &util.ByItems{Expr: col.Col, Desc: col.Desc}) - } - return sort.attach2Task(tsk) -} - -// optimizeByShuffle insert `PhysicalShuffle` to optimize performance by running in a parallel manner. -func optimizeByShuffle(tsk task, ctx sessionctx.Context) task { - if tsk.plan() == nil { - return tsk - } - - switch p := tsk.plan().(type) { - case *PhysicalWindow: - if shuffle := optimizeByShuffle4Window(p, ctx); shuffle != nil { - return shuffle.attach2Task(tsk) - } - case *PhysicalMergeJoin: - if shuffle := optimizeByShuffle4MergeJoin(p, ctx); shuffle != nil { - return shuffle.attach2Task(tsk) - } - case *PhysicalStreamAgg: - if shuffle := optimizeByShuffle4StreamAgg(p, ctx); shuffle != nil { - return shuffle.attach2Task(tsk) - } - } - return tsk -} - -func optimizeByShuffle4Window(pp *PhysicalWindow, ctx sessionctx.Context) *PhysicalShuffle { - concurrency := ctx.GetSessionVars().WindowConcurrency() - if concurrency <= 1 { - return nil - } - - sort, ok := pp.Children()[0].(*PhysicalSort) - if !ok { - // Multi-thread executing on SORTED data source is not effective enough by current implementation. - // TODO: Implement a better one. - return nil - } - tail, dataSource := sort, sort.Children()[0] - - partitionBy := make([]*expression.Column, 0, len(pp.PartitionBy)) - for _, item := range pp.PartitionBy { - partitionBy = append(partitionBy, item.Col) - } - ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(partitionBy, dataSource.Schema(), dataSource.StatsInfo()) - if ndv <= 1 { - return nil - } - concurrency = mathutil.Min(concurrency, int(ndv)) - - byItems := make([]expression.Expression, 0, len(pp.PartitionBy)) - for _, item := range pp.PartitionBy { - byItems = append(byItems, item.Col) - } - reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} - shuffle := PhysicalShuffle{ - Concurrency: concurrency, - Tails: []PhysicalPlan{tail}, - DataSources: []PhysicalPlan{dataSource}, - SplitterType: PartitionHashSplitterType, - ByItemArrays: [][]expression.Expression{byItems}, - }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) - return shuffle -} - -func optimizeByShuffle4StreamAgg(pp *PhysicalStreamAgg, ctx sessionctx.Context) *PhysicalShuffle { - concurrency := ctx.GetSessionVars().StreamAggConcurrency() - if concurrency <= 1 { - return nil - } - - sort, ok := pp.Children()[0].(*PhysicalSort) - if !ok { - // Multi-thread executing on SORTED data source is not effective enough by current implementation. - // TODO: Implement a better one. - return nil - } - tail, dataSource := sort, sort.Children()[0] - - partitionBy := make([]*expression.Column, 0, len(pp.GroupByItems)) - for _, item := range pp.GroupByItems { - if col, ok := item.(*expression.Column); ok { - partitionBy = append(partitionBy, col) - } - } - ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(partitionBy, dataSource.Schema(), dataSource.StatsInfo()) - if ndv <= 1 { - return nil - } - concurrency = mathutil.Min(concurrency, int(ndv)) - - reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} - shuffle := PhysicalShuffle{ - Concurrency: concurrency, - Tails: []PhysicalPlan{tail}, - DataSources: []PhysicalPlan{dataSource}, - SplitterType: PartitionHashSplitterType, - ByItemArrays: [][]expression.Expression{util.CloneExprs(pp.GroupByItems)}, - }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) - return shuffle -} - -func optimizeByShuffle4MergeJoin(pp *PhysicalMergeJoin, ctx sessionctx.Context) *PhysicalShuffle { - concurrency := ctx.GetSessionVars().MergeJoinConcurrency() - if concurrency <= 1 { - return nil - } - - children := pp.Children() - dataSources := make([]PhysicalPlan, len(children)) - tails := make([]PhysicalPlan, len(children)) - - for i := range children { - sort, ok := children[i].(*PhysicalSort) - if !ok { - // Multi-thread executing on SORTED data source is not effective enough by current implementation. - // TODO: Implement a better one. - return nil - } - tails[i], dataSources[i] = sort, sort.Children()[0] - } - - leftByItemArray := make([]expression.Expression, 0, len(pp.LeftJoinKeys)) - for _, col := range pp.LeftJoinKeys { - leftByItemArray = append(leftByItemArray, col.Clone()) - } - rightByItemArray := make([]expression.Expression, 0, len(pp.RightJoinKeys)) - for _, col := range pp.RightJoinKeys { - rightByItemArray = append(rightByItemArray, col.Clone()) - } - reqProp := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} - shuffle := PhysicalShuffle{ - Concurrency: concurrency, - Tails: tails, - DataSources: dataSources, - SplitterType: PartitionHashSplitterType, - ByItemArrays: [][]expression.Expression{leftByItemArray, rightByItemArray}, - }.Init(ctx, pp.StatsInfo(), pp.SelectBlockOffset(), reqProp) - return shuffle -} - -// LogicalPlan is a tree of logical operators. -// We can do a lot of logical optimizations to it, like predicate pushdown and column pruning. -type LogicalPlan interface { - Plan - - // HashCode encodes a LogicalPlan to fast compare whether a LogicalPlan equals to another. - // We use a strict encode method here which ensures there is no conflict. - HashCode() []byte - - // PredicatePushDown pushes down the predicates in the where/on/having clauses as deeply as possible. - // It will accept a predicate that is an expression slice, and return the expressions that can't be pushed. - // Because it might change the root if the having clause exists, we need to return a plan that represents a new root. - PredicatePushDown([]expression.Expression, *logicalOptimizeOp) ([]expression.Expression, LogicalPlan) - - // PruneColumns prunes the unused columns. - PruneColumns([]*expression.Column, *logicalOptimizeOp) error - - // findBestTask converts the logical plan to the physical plan. It's a new interface. - // It is called recursively from the parent to the children to create the result physical plan. - // Some logical plans will convert the children to the physical plans in different ways, and return the one - // With the lowest cost and how many plans are found in this function. - // planCounter is a counter for planner to force a plan. - // If planCounter > 0, the clock_th plan generated in this function will be returned. - // If planCounter = 0, the plan generated in this function will not be considered. - // If planCounter = -1, then we will not force plan. - findBestTask(prop *property.PhysicalProperty, planCounter *PlanCounterTp, op *physicalOptimizeOp) (task, int64, error) - - // BuildKeyInfo will collect the information of unique keys into schema. - // Because this method is also used in cascades planner, we cannot use - // things like `p.schema` or `p.children` inside it. We should use the `selfSchema` - // and `childSchema` instead. - BuildKeyInfo(selfSchema *expression.Schema, childSchema []*expression.Schema) - - // pushDownTopN will push down the topN or limit operator during logical optimization. - pushDownTopN(topN *LogicalTopN, opt *logicalOptimizeOp) LogicalPlan - - // deriveTopN derives an implicit TopN from a filter on row_number window function.. - deriveTopN(opt *logicalOptimizeOp) LogicalPlan - - // predicateSimplification consolidates different predcicates on a column and its equivalence classes. - predicateSimplification(opt *logicalOptimizeOp) LogicalPlan - - // constantPropagation generate new constant predicate according to column equivalence relation - constantPropagation(parentPlan LogicalPlan, currentChildIdx int, opt *logicalOptimizeOp) (newRoot LogicalPlan) - - // pullUpConstantPredicates recursive find constant predicate, used for the constant propagation rule - pullUpConstantPredicates() []expression.Expression - - // recursiveDeriveStats derives statistic info between plans. - recursiveDeriveStats(colGroups [][]*expression.Column) (*property.StatsInfo, error) - - // DeriveStats derives statistic info for current plan node given child stats. - // We need selfSchema, childSchema here because it makes this method can be used in - // cascades planner, where LogicalPlan might not record its children or schema. - DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) - - // ExtractColGroups extracts column groups from child operator whose DNVs are required by the current operator. - // For example, if current operator is LogicalAggregation of `Group By a, b`, we indicate the child operators to maintain - // and propagate the NDV info of column group (a, b), to improve the row count estimation of current LogicalAggregation. - // The parameter colGroups are column groups required by upper operators, besides from the column groups derived from - // current operator, we should pass down parent colGroups to child operator as many as possible. - ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column - - // PreparePossibleProperties is only used for join and aggregation. Like group by a,b,c, all permutation of (a,b,c) is - // valid, but the ordered indices in leaf plan is limited. So we can get all possible order properties by a pre-walking. - PreparePossibleProperties(schema *expression.Schema, childrenProperties ...[][]*expression.Column) [][]*expression.Column - - // exhaustPhysicalPlans generates all possible plans that can match the required property. - // It will return: - // 1. All possible plans that can match the required property. - // 2. Whether the SQL hint can work. Return true if there is no hint. - exhaustPhysicalPlans(*property.PhysicalProperty) (physicalPlans []PhysicalPlan, hintCanWork bool, err error) - - // ExtractCorrelatedCols extracts correlated columns inside the LogicalPlan. - ExtractCorrelatedCols() []*expression.CorrelatedColumn - - // MaxOneRow means whether this operator only returns max one row. - MaxOneRow() bool - - // Get all the children. - Children() []LogicalPlan - - // SetChildren sets the children for the plan. - SetChildren(...LogicalPlan) - - // SetChild sets the ith child for the plan. - SetChild(i int, child LogicalPlan) - - // rollBackTaskMap roll back all taskMap's logs after TimeStamp TS. - rollBackTaskMap(TS uint64) - - // canPushToCop check if we might push this plan to a specific store. - canPushToCop(store kv.StoreType) bool - - // ExtractFD derive the FDSet from the tree bottom up. - ExtractFD() *fd.FDSet -} - -// PhysicalPlan is a tree of the physical operators. -type PhysicalPlan interface { - Plan - - // getPlanCostVer1 calculates the cost of the plan if it has not been calculated yet and returns the cost on model ver1. - getPlanCostVer1(taskType property.TaskType, option *PlanCostOption) (float64, error) - - // getPlanCostVer2 calculates the cost of the plan if it has not been calculated yet and returns the cost on model ver2. - getPlanCostVer2(taskType property.TaskType, option *PlanCostOption) (costVer2, error) - - // attach2Task makes the current physical plan as the father of task's physicalPlan and updates the cost of - // current task. If the child's task is cop task, some operator may close this task and return a new rootTask. - attach2Task(...task) task - - // ToPB converts physical plan to tipb executor. - ToPB(ctx sessionctx.Context, storeType kv.StoreType) (*tipb.Executor, error) - - // GetChildReqProps gets the required property by child index. - GetChildReqProps(idx int) *property.PhysicalProperty - - // StatsCount returns the count of property.StatsInfo for this plan. - StatsCount() float64 - - // ExtractCorrelatedCols extracts correlated columns inside the PhysicalPlan. - ExtractCorrelatedCols() []*expression.CorrelatedColumn - - // Children get all the children. - Children() []PhysicalPlan - - // SetChildren sets the children for the plan. - SetChildren(...PhysicalPlan) - - // SetChild sets the ith child for the plan. - SetChild(i int, child PhysicalPlan) - - // ResolveIndices resolves the indices for columns. After doing this, the columns can evaluate the rows by their indices. - ResolveIndices() error - - // StatsInfo returns the StatsInfo of the plan. - StatsInfo() *property.StatsInfo - - // SetStats sets basePlan.stats inside the basePhysicalPlan. - SetStats(s *property.StatsInfo) - - // ExplainNormalizedInfo returns operator normalized information for generating digest. - ExplainNormalizedInfo() string - - // Clone clones this physical plan. - Clone() (PhysicalPlan, error) - - // appendChildCandidate append child physicalPlan into tracer in order to track each child physicalPlan which can't - // be tracked during findBestTask or enumeratePhysicalPlans4Task - appendChildCandidate(op *physicalOptimizeOp) - - // MemoryUsage return the memory usage of PhysicalPlan - MemoryUsage() int64 - - // Below three methods help to handle the inconsistency between row count in the StatsInfo and the recorded - // actual row count. - // For the children in the inner side (probe side) of Index Join and Apply, the row count in the StatsInfo - // means the estimated row count for a single "probe", but the recorded actual row count is the total row - // count for all "probes". - // To handle this inconsistency without breaking anything else, we added a field `probeParents` of - // type `[]PhysicalPlan` into all PhysicalPlan to record all operators that are (indirect or direct) parents - // of this PhysicalPlan and will cause this inconsistency. - // Using this information, we can convert the row count between the "single probe" row count and "all probes" - // row count freely. - - // setProbeParents sets the above stated `probeParents` field. - setProbeParents([]PhysicalPlan) - // getEstRowCountForDisplay uses the "single probe" row count in StatsInfo and the probeParents to calculate - // the "all probe" row count. - // All places that display the row count for a PhysicalPlan are expected to use this method. - getEstRowCountForDisplay() float64 - // getEstRowCountForDisplay uses the runtime stats and the probeParents to calculate the actual "probe" count. - getActualProbeCnt(*execdetails.RuntimeStatsColl) int64 -} - -// NewDefaultPlanCostOption returns PlanCostOption -func NewDefaultPlanCostOption() *PlanCostOption { - return &PlanCostOption{} -} - -// PlanCostOption indicates option during GetPlanCost -type PlanCostOption struct { - CostFlag uint64 - tracer *physicalOptimizeOp -} - -// WithCostFlag set costflag -func (op *PlanCostOption) WithCostFlag(flag uint64) *PlanCostOption { - if op == nil { - return nil - } - op.CostFlag = flag - return op -} - -// WithOptimizeTracer set tracer -func (op *PlanCostOption) WithOptimizeTracer(v *physicalOptimizeOp) *PlanCostOption { - if op == nil { - return nil - } - op.tracer = v - if v != nil && v.tracer != nil { - op.CostFlag |= CostFlagTrace - } - return op -} - -type baseLogicalPlan struct { - base.Plan - - taskMap map[string]task - // taskMapBak forms a backlog stack of taskMap, used to roll back the taskMap. - taskMapBak []string - // taskMapBakTS stores the timestamps of logs. - taskMapBakTS []uint64 - self LogicalPlan - maxOneRow bool - children []LogicalPlan - // fdSet is a set of functional dependencies(FDs) which powers many optimizations, - // including eliminating unnecessary DISTINCT operators, simplifying ORDER BY columns, - // removing Max1Row operators, and mapping semi-joins to inner-joins. - // for now, it's hard to maintain in individual operator, build it from bottom up when using. - fdSet *fd.FDSet -} - -// ExtractFD return the children[0]'s fdSet if there are no adding/removing fd in this logic plan. -func (p *baseLogicalPlan) ExtractFD() *fd.FDSet { - if p.fdSet != nil { - return p.fdSet - } - fds := &fd.FDSet{HashCodeToUniqueID: make(map[string]int)} - for _, ch := range p.children { - fds.AddFrom(ch.ExtractFD()) - } - return fds -} - -func (p *baseLogicalPlan) MaxOneRow() bool { - return p.maxOneRow -} - -// ExplainInfo implements Plan interface. -func (*baseLogicalPlan) ExplainInfo() string { - return "" -} - -func getEstimatedProbeCntFromProbeParents(probeParents []PhysicalPlan) float64 { - res := float64(1) - for _, pp := range probeParents { - switch pp.(type) { - case *PhysicalApply, *PhysicalIndexHashJoin, *PhysicalIndexMergeJoin, *PhysicalIndexJoin: - if join, ok := pp.(interface{ getInnerChildIdx() int }); ok { - outer := pp.Children()[1-join.getInnerChildIdx()] - res *= outer.StatsInfo().RowCount - } - } - } - return res -} - -func getActualProbeCntFromProbeParents(pps []PhysicalPlan, statsColl *execdetails.RuntimeStatsColl) int64 { - res := int64(1) - for _, pp := range pps { - switch pp.(type) { - case *PhysicalApply, *PhysicalIndexHashJoin, *PhysicalIndexMergeJoin, *PhysicalIndexJoin: - if join, ok := pp.(interface{ getInnerChildIdx() int }); ok { - outerChildID := pp.Children()[1-join.getInnerChildIdx()].ID() - actRows := int64(1) - if statsColl.ExistsRootStats(outerChildID) { - actRows = statsColl.GetRootStats(outerChildID).GetActRows() - } - if statsColl.ExistsCopStats(outerChildID) { - actRows = statsColl.GetCopStats(outerChildID).GetActRows() - } - // TODO: For PhysicalApply, we need to consider cache hit ratio in JoinRuntimeStats and use actRows/(1-ratio) here. - res *= actRows - } - } - } - return res -} - -type basePhysicalPlan struct { - base.Plan - - childrenReqProps []*property.PhysicalProperty - self PhysicalPlan - children []PhysicalPlan - - // used by the new cost interface - planCostInit bool - planCost float64 - planCostVer2 costVer2 - - // probeParents records the IndexJoins and Applys with this operator in their inner children. - // Please see comments in PhysicalPlan for details. - probeParents []PhysicalPlan - - // Only for MPP. If TiFlashFineGrainedShuffleStreamCount > 0: - // 1. For ExchangeSender, means its output will be partitioned by hash key. - // 2. For ExchangeReceiver/Window/Sort, means its input is already partitioned. - TiFlashFineGrainedShuffleStreamCount uint64 -} - -func (p *basePhysicalPlan) cloneWithSelf(newSelf PhysicalPlan) (*basePhysicalPlan, error) { - base := &basePhysicalPlan{ - Plan: p.Plan, - self: newSelf, - TiFlashFineGrainedShuffleStreamCount: p.TiFlashFineGrainedShuffleStreamCount, - probeParents: p.probeParents, - } - for _, child := range p.children { - cloned, err := child.Clone() - if err != nil { - return nil, err - } - base.children = append(base.children, cloned) - } - for _, prop := range p.childrenReqProps { - if prop == nil { - continue - } - base.childrenReqProps = append(base.childrenReqProps, prop.CloneEssentialFields()) - } - return base, nil -} - -// Clone implements PhysicalPlan interface. -func (p *basePhysicalPlan) Clone() (PhysicalPlan, error) { - return nil, errors.Errorf("%T doesn't support cloning", p.self) -} - -// ExplainInfo implements Plan interface. -func (*basePhysicalPlan) ExplainInfo() string { - return "" -} - -// ExplainNormalizedInfo implements PhysicalPlan interface. -func (*basePhysicalPlan) ExplainNormalizedInfo() string { - return "" -} - -func (p *basePhysicalPlan) GetChildReqProps(idx int) *property.PhysicalProperty { - return p.childrenReqProps[idx] -} - -// ExtractCorrelatedCols implements PhysicalPlan interface. -func (*basePhysicalPlan) ExtractCorrelatedCols() []*expression.CorrelatedColumn { - return nil -} - -// MemoryUsage return the memory usage of basePhysicalPlan -func (p *basePhysicalPlan) MemoryUsage() (sum int64) { - if p == nil { - return - } - - sum = p.Plan.MemoryUsage() + size.SizeOfSlice + int64(cap(p.childrenReqProps))*size.SizeOfPointer + - size.SizeOfSlice + int64(cap(p.children)+1)*size.SizeOfInterface + size.SizeOfFloat64 + - size.SizeOfUint64 + size.SizeOfBool - - for _, prop := range p.childrenReqProps { - sum += prop.MemoryUsage() - } - for _, plan := range p.children { - sum += plan.MemoryUsage() - } - return -} - -func (p *basePhysicalPlan) getEstRowCountForDisplay() float64 { - if p == nil { - return 0 - } - return p.StatsInfo().RowCount * getEstimatedProbeCntFromProbeParents(p.probeParents) -} - -func (p *basePhysicalPlan) getActualProbeCnt(statsColl *execdetails.RuntimeStatsColl) int64 { - if p == nil { - return 1 - } - return getActualProbeCntFromProbeParents(p.probeParents, statsColl) -} - -func (p *basePhysicalPlan) setProbeParents(probeParents []PhysicalPlan) { - p.probeParents = probeParents -} - -// GetLogicalTS4TaskMap get the logical TimeStamp now to help rollback the TaskMap changes after that. -func (p *baseLogicalPlan) GetLogicalTS4TaskMap() uint64 { - p.SCtx().GetSessionVars().StmtCtx.TaskMapBakTS++ - return p.SCtx().GetSessionVars().StmtCtx.TaskMapBakTS -} - -func (p *baseLogicalPlan) rollBackTaskMap(ts uint64) { - if !p.SCtx().GetSessionVars().StmtCtx.StmtHints.TaskMapNeedBackUp() { - return - } - if len(p.taskMapBak) > 0 { - // Rollback all the logs with TimeStamp TS. - n := len(p.taskMapBak) - for i := 0; i < n; i++ { - cur := p.taskMapBak[i] - if p.taskMapBakTS[i] < ts { - continue - } - - // Remove the i_th log. - p.taskMapBak = append(p.taskMapBak[:i], p.taskMapBak[i+1:]...) - p.taskMapBakTS = append(p.taskMapBakTS[:i], p.taskMapBakTS[i+1:]...) - i-- - n-- - - // Roll back taskMap. - p.taskMap[cur] = nil - } - } - for _, child := range p.children { - child.rollBackTaskMap(ts) - } -} - -func (p *baseLogicalPlan) getTask(prop *property.PhysicalProperty) task { - key := prop.HashCode() - return p.taskMap[string(key)] -} - -func (p *baseLogicalPlan) storeTask(prop *property.PhysicalProperty, task task) { - key := prop.HashCode() - if p.SCtx().GetSessionVars().StmtCtx.StmtHints.TaskMapNeedBackUp() { - // Empty string for useless change. - ts := p.GetLogicalTS4TaskMap() - p.taskMapBakTS = append(p.taskMapBakTS, ts) - p.taskMapBak = append(p.taskMapBak, string(key)) - } - p.taskMap[string(key)] = task -} - -// HasMaxOneRow returns if the LogicalPlan will output at most one row. -func HasMaxOneRow(p LogicalPlan, childMaxOneRow []bool) bool { - if len(childMaxOneRow) == 0 { - // The reason why we use this check is that, this function - // is used both in planner/core and planner/cascades. - // In cascades planner, LogicalPlan may have no `children`. - return false - } - switch x := p.(type) { - case *LogicalLock, *LogicalLimit, *LogicalSort, *LogicalSelection, - *LogicalApply, *LogicalProjection, *LogicalWindow, *LogicalAggregation: - return childMaxOneRow[0] - case *LogicalMaxOneRow: - return true - case *LogicalJoin: - switch x.JoinType { - case SemiJoin, AntiSemiJoin, LeftOuterSemiJoin, AntiLeftOuterSemiJoin: - return childMaxOneRow[0] - default: - return childMaxOneRow[0] && childMaxOneRow[1] - } - } - return false -} - -// BuildKeyInfo implements LogicalPlan BuildKeyInfo interface. -func (p *baseLogicalPlan) BuildKeyInfo(_ *expression.Schema, _ []*expression.Schema) { - childMaxOneRow := make([]bool, len(p.children)) - for i := range p.children { - childMaxOneRow[i] = p.children[i].MaxOneRow() - } - p.maxOneRow = HasMaxOneRow(p.self, childMaxOneRow) -} - -// BuildKeyInfo implements LogicalPlan BuildKeyInfo interface. -func (p *logicalSchemaProducer) BuildKeyInfo(selfSchema *expression.Schema, childSchema []*expression.Schema) { - selfSchema.Keys = nil - p.baseLogicalPlan.BuildKeyInfo(selfSchema, childSchema) - - // default implementation for plans has only one child: proprgate child keys - // multi-children plans are likely to have particular implementation. - if len(childSchema) == 1 { - for _, key := range childSchema[0].Keys { - indices := selfSchema.ColumnsIndices(key) - if indices == nil { - continue - } - newKey := make([]*expression.Column, 0, len(key)) - for _, i := range indices { - newKey = append(newKey, selfSchema.Columns[i]) - } - selfSchema.Keys = append(selfSchema.Keys, newKey) - } - } -} - -func newBaseLogicalPlan(ctx sessionctx.Context, tp string, self LogicalPlan, offset int) baseLogicalPlan { - return baseLogicalPlan{ - taskMap: make(map[string]task), - taskMapBak: make([]string, 0, 10), - taskMapBakTS: make([]uint64, 0, 10), - Plan: base.NewBasePlan(ctx, tp, offset), - self: self, - } -} - -func newBasePhysicalPlan(ctx sessionctx.Context, tp string, self PhysicalPlan, offset int) basePhysicalPlan { - return basePhysicalPlan{ - Plan: base.NewBasePlan(ctx, tp, offset), - self: self, - } -} - -func (*baseLogicalPlan) ExtractCorrelatedCols() []*expression.CorrelatedColumn { - return nil -} - -// PruneColumns implements LogicalPlan interface. -func (p *baseLogicalPlan) PruneColumns(parentUsedCols []*expression.Column, opt *logicalOptimizeOp) error { - if len(p.children) == 0 { - return nil - } - return p.children[0].PruneColumns(parentUsedCols, opt) -} - -// Schema implements Plan Schema interface. -func (p *baseLogicalPlan) Schema() *expression.Schema { - return p.children[0].Schema() -} - -func (p *baseLogicalPlan) OutputNames() types.NameSlice { - return p.children[0].OutputNames() -} - -func (p *baseLogicalPlan) SetOutputNames(names types.NameSlice) { - p.children[0].SetOutputNames(names) -} - -// Schema implements Plan Schema interface. -func (p *basePhysicalPlan) Schema() *expression.Schema { - return p.children[0].Schema() -} - -// Children implements LogicalPlan Children interface. -func (p *baseLogicalPlan) Children() []LogicalPlan { - return p.children -} - -// Children implements PhysicalPlan Children interface. -func (p *basePhysicalPlan) Children() []PhysicalPlan { - return p.children -} - -// SetChildren implements LogicalPlan SetChildren interface. -func (p *baseLogicalPlan) SetChildren(children ...LogicalPlan) { - p.children = children -} - -// SetChildren implements PhysicalPlan SetChildren interface. -func (p *basePhysicalPlan) SetChildren(children ...PhysicalPlan) { - p.children = children -} - -// SetChild implements LogicalPlan SetChild interface. -func (p *baseLogicalPlan) SetChild(i int, child LogicalPlan) { - p.children[i] = child -} - -// SetChild implements PhysicalPlan SetChild interface. -func (p *basePhysicalPlan) SetChild(i int, child PhysicalPlan) { - p.children[i] = child -} - -// BuildPlanTrace implements Plan -func (p *basePhysicalPlan) BuildPlanTrace() *tracing.PlanTrace { - tp := "" - info := "" - if p.self != nil { - tp = p.self.TP() - info = p.self.ExplainInfo() - } - - planTrace := &tracing.PlanTrace{ID: p.ID(), TP: tp, ExplainInfo: info} - for _, child := range p.Children() { - planTrace.Children = append(planTrace.Children, child.BuildPlanTrace()) - } - return planTrace -} - -// BuildPlanTrace implements Plan -func (p *baseLogicalPlan) BuildPlanTrace() *tracing.PlanTrace { - planTrace := &tracing.PlanTrace{ID: p.ID(), TP: p.TP(), ExplainInfo: p.self.ExplainInfo()} - for _, child := range p.Children() { - planTrace.Children = append(planTrace.Children, child.BuildPlanTrace()) - } - return planTrace -} - -func (p *basePhysicalPlan) appendChildCandidate(op *physicalOptimizeOp) { - if len(p.Children()) < 1 { - return - } - childrenID := make([]int, 0) - for _, child := range p.Children() { - childCandidate := &tracing.CandidatePlanTrace{ - PlanTrace: &tracing.PlanTrace{TP: child.TP(), ID: child.ID(), - ExplainInfo: child.ExplainInfo()}, - } - op.tracer.AppendCandidate(childCandidate) - child.appendChildCandidate(op) - childrenID = append(childrenID, child.ID()) - } - op.tracer.Candidates[p.ID()].PlanTrace.AppendChildrenID(childrenID...) -} diff --git a/planner/core/plan_test.go b/planner/core/plan_test.go deleted file mode 100644 index 4d46e50caa38f..0000000000000 --- a/planner/core/plan_test.go +++ /dev/null @@ -1,763 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "bytes" - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/plancodec" - "github.com/stretchr/testify/require" -) - -func TestEncodeDecodePlan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1,t2") - tk.MustExec("create table t1 (a int key,b int,c int, index (b));") - tk.MustExec("create table tp (a int ,b int,c int) partition by hash(b) partitions 5;") - tk.MustExec("set tidb_enable_collect_execution_info=1;") - tk.MustExec("set tidb_partition_prune_mode='static';") - - tk.Session().GetSessionVars().PlanID.Store(0) - getPlanTree := func() (str1, str2 string) { - info := tk.Session().ShowProcess() - require.NotNil(t, info) - p, ok := info.Plan.(core.Plan) - require.True(t, ok) - encodeStr := core.EncodePlan(p) - planTree, err := plancodec.DecodePlan(encodeStr) - require.NoError(t, err) - - // test the new encoding method - flat := core.FlattenPhysicalPlan(p, true) - newEncodeStr := core.EncodeFlatPlan(flat) - newPlanTree, err := plancodec.DecodePlan(newEncodeStr) - require.NoError(t, err) - - return planTree, newPlanTree - } - tk.MustExec("select max(a) from t1 where a>0;") - planTree, newplanTree := getPlanTree() - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("prepare stmt from \"select max(a) from t1 where a > ?\";") - tk.MustExec("set @a = 1;") - tk.MustExec("execute stmt using @a;") - planTree, newplanTree = getPlanTree() - require.Empty(t, planTree) - require.Empty(t, newplanTree) - - tk.MustExec("insert into t1 values (1,1,1), (2,2,2);") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Insert") - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "Insert") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("update t1 set b = 3 where c = 1;") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Update") - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "Update") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("delete from t1 where b = 3;") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Delete") - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "Delete") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("with cte(a) as (select 1) select * from cte") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Projection_7") - require.Contains(t, planTree, "1->Column#3") - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "Projection_7") - require.Contains(t, newplanTree, "1->Column#3") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("with cte(a) as (select 2) select * from cte") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Projection_7") - require.Contains(t, planTree, "2->Column#3") - require.Contains(t, planTree, "time") - require.Contains(t, planTree, "loops") - require.Contains(t, newplanTree, "Projection_7") - require.Contains(t, newplanTree, "2->Column#3") - require.Contains(t, newplanTree, "time") - require.Contains(t, newplanTree, "loops") - - tk.MustExec("select * from tp") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "PartitionUnion") - require.Contains(t, newplanTree, "PartitionUnion") - - tk.MustExec("select row_number() over (partition by c) from t1;") - planTree, newplanTree = getPlanTree() - require.Contains(t, planTree, "Shuffle") - require.Contains(t, planTree, "ShuffleReceiver") - require.Contains(t, newplanTree, "Shuffle") - require.Contains(t, newplanTree, "ShuffleReceiver") -} - -func TestNormalizedDigest(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1,t2,t3,t4, bmsql_order_line, bmsql_district,bmsql_stock") - tk.MustExec("create table t1 (a int key,b int,c int, index (b));") - tk.MustExec("create table t2 (a int key,b int,c int, index (b));") - tk.MustExec("create table t3 (a int, b int, index(a)) partition by range(a) (partition p0 values less than (10),partition p1 values less than MAXVALUE);") - tk.MustExec("create table t4 (a int key,b int) partition by hash(a) partitions 2;") - tk.MustExec(`CREATE TABLE bmsql_order_line ( - ol_w_id int(11) NOT NULL, - ol_d_id int(11) NOT NULL, - ol_o_id int(11) NOT NULL, - ol_number int(11) NOT NULL, - ol_i_id int(11) NOT NULL, - ol_delivery_d timestamp NULL DEFAULT NULL, - ol_amount decimal(6,2) DEFAULT NULL, - ol_supply_w_id int(11) DEFAULT NULL, - ol_quantity int(11) DEFAULT NULL, - ol_dist_info char(24) DEFAULT NULL, - PRIMARY KEY ( ol_w_id , ol_d_id , ol_o_id , ol_number ) NONCLUSTERED - );`) - tk.MustExec(`CREATE TABLE bmsql_district ( - d_w_id int(11) NOT NULL, - d_id int(11) NOT NULL, - d_ytd decimal(12,2) DEFAULT NULL, - d_tax decimal(4,4) DEFAULT NULL, - d_next_o_id int(11) DEFAULT NULL, - d_name varchar(10) DEFAULT NULL, - d_street_1 varchar(20) DEFAULT NULL, - d_street_2 varchar(20) DEFAULT NULL, - d_city varchar(20) DEFAULT NULL, - d_state char(2) DEFAULT NULL, - d_zip char(9) DEFAULT NULL, - PRIMARY KEY ( d_w_id , d_id ) NONCLUSTERED - );`) - tk.MustExec(`CREATE TABLE bmsql_stock ( - s_w_id int(11) NOT NULL, - s_i_id int(11) NOT NULL, - s_quantity int(11) DEFAULT NULL, - s_ytd int(11) DEFAULT NULL, - s_order_cnt int(11) DEFAULT NULL, - s_remote_cnt int(11) DEFAULT NULL, - s_data varchar(50) DEFAULT NULL, - s_dist_01 char(24) DEFAULT NULL, - s_dist_02 char(24) DEFAULT NULL, - s_dist_03 char(24) DEFAULT NULL, - s_dist_04 char(24) DEFAULT NULL, - s_dist_05 char(24) DEFAULT NULL, - s_dist_06 char(24) DEFAULT NULL, - s_dist_07 char(24) DEFAULT NULL, - s_dist_08 char(24) DEFAULT NULL, - s_dist_09 char(24) DEFAULT NULL, - s_dist_10 char(24) DEFAULT NULL, - PRIMARY KEY ( s_w_id , s_i_id ) NONCLUSTERED - );`) - - err := failpoint.Enable("github.com/pingcap/tidb/planner/mockRandomPlanID", "return(true)") - require.NoError(t, err) - defer func() { - err = failpoint.Disable("github.com/pingcap/tidb/planner/mockRandomPlanID") - require.NoError(t, err) - }() - - normalizedDigestCases := []struct { - sql1 string - sql2 string - isSame bool - }{ - { - sql1: "select * from t1;", - sql2: "select * from t2;", - isSame: false, - }, - { // test for tableReader and tableScan. - sql1: "select * from t1 where a<1", - sql2: "select * from t1 where a<2", - isSame: true, - }, - { - sql1: "select * from t1 where a<1", - sql2: "select * from t1 where a=2", - isSame: false, - }, - { // test for point get. - sql1: "select * from t1 where a=3", - sql2: "select * from t1 where a=2", - isSame: true, - }, - { // test for indexLookUp. - sql1: "select * from t1 use index(b) where b=3", - sql2: "select * from t1 use index(b) where b=1", - isSame: true, - }, - { // test for indexReader. - sql1: "select a+1,b+2 from t1 use index(b) where b=3", - sql2: "select a+2,b+3 from t1 use index(b) where b=2", - isSame: true, - }, - { // test for merge join. - sql1: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", - sql2: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>2;", - isSame: true, - }, - { // test for indexLookUpJoin. - sql1: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", - sql2: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", - isSame: true, - }, - { // test for hashJoin. - sql1: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", - sql2: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", - isSame: true, - }, - { // test for diff join. - sql1: "SELECT /*+ TIDB_HJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", - sql2: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", - isSame: false, - }, - { // test for diff join. - sql1: "SELECT /*+ TIDB_INLJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>1;", - sql2: "SELECT /*+ TIDB_SMJ(t1, t2) */ * from t1, t2 where t1.a = t2.a and t1.c>3;", - isSame: false, - }, - { // test for apply. - sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", - sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >0)", - isSame: true, - }, - { // test for apply. - sql1: "select * from t1 where t1.b > 0 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null and t2.c >1)", - sql2: "select * from t1 where t1.b > 1 and t1.a in (select sum(t2.b) from t2 where t2.a=t1.a and t2.b is not null)", - isSame: false, - }, - { // test for topN. - sql1: "SELECT * from t1 where a!=1 order by c limit 1", - sql2: "SELECT * from t1 where a!=2 order by c limit 2", - isSame: true, - }, - { // test for union - sql1: "select count(1) as num,a from t1 where a=1 group by a union select count(1) as num,a from t1 where a=3 group by a;", - sql2: "select count(1) as num,a from t1 where a=2 group by a union select count(1) as num,a from t1 where a=4 group by a;", - isSame: true, - }, - { // test for tablescan partition - sql1: "select * from t3 where a=5", - sql2: "select * from t3 where a=15", - isSame: true, - }, - { // test for point get partition - sql1: "select * from t4 where a=4", - sql2: "select * from t4 where a=30", - isSame: true, - }, - { - sql1: `SELECT COUNT(*) AS low_stock - FROM - ( - SELECT * - FROM bmsql_stock - WHERE s_w_id = 1 - AND s_quantity < 2 - AND s_i_id IN ( SELECT /*+ TIDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 20 AND ol_o_id < d_next_o_id WHERE d_w_id = 1 AND d_id = 2 ) - ) AS L;`, - sql2: `SELECT COUNT(*) AS low_stock - FROM - ( - SELECT * - FROM bmsql_stock - WHERE s_w_id = 5 - AND s_quantity < 6 - AND s_i_id IN ( SELECT /*+ TIDB_INLJ(bmsql_order_line) */ ol_i_id FROM bmsql_district JOIN bmsql_order_line ON ol_w_id = d_w_id AND ol_d_id = d_id AND ol_o_id >= d_next_o_id - 70 AND ol_o_id < d_next_o_id WHERE d_w_id = 5 AND d_id = 6 ) - ) AS L;`, - isSame: true, - }, - } - for _, testCase := range normalizedDigestCases { - testNormalizeDigest(tk, t, testCase.sql1, testCase.sql2, testCase.isSame) - } -} - -func testNormalizeDigest(tk *testkit.TestKit, t *testing.T, sql1, sql2 string, isSame bool) { - tk.MustQuery(sql1) - info := tk.Session().ShowProcess() - require.NotNil(t, info) - physicalPlan, ok := info.Plan.(core.PhysicalPlan) - require.True(t, ok) - normalized1, digest1 := core.NormalizePlan(physicalPlan) - - // test the new normalization code - flat := core.FlattenPhysicalPlan(physicalPlan, false) - newNormalized, newPlanDigest := core.NormalizeFlatPlan(flat) - require.Equal(t, digest1, newPlanDigest) - require.Equal(t, normalized1, newNormalized) - - tk.MustQuery(sql2) - info = tk.Session().ShowProcess() - require.NotNil(t, info) - physicalPlan, ok = info.Plan.(core.PhysicalPlan) - require.True(t, ok) - normalized2, digest2 := core.NormalizePlan(physicalPlan) - - // test the new normalization code - flat = core.FlattenPhysicalPlan(physicalPlan, false) - newNormalized, newPlanDigest = core.NormalizeFlatPlan(flat) - require.Equal(t, digest2, newPlanDigest) - require.Equal(t, normalized2, newNormalized) - - comment := fmt.Sprintf("sql1: %v, sql2: %v\n%v !=\n%v\n", sql1, sql2, normalized1, normalized2) - if isSame { - require.Equal(t, normalized1, normalized2, comment) - require.Equal(t, digest1.String(), digest2.String(), comment) - } else { - require.NotEqual(t, normalized1, normalized2, comment) - require.NotEqual(t, digest1.String(), digest2.String(), comment) - } -} - -func TestExplainFormatHintRecoverableForTiFlashReplica(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - // Create virtual `tiflash` replica info. - is := dom.InfoSchema() - db, exists := is.SchemaByName(model.NewCIStr("test")) - require.True(t, exists) - for _, tblInfo := range db.Tables { - if tblInfo.Name.L == "t" { - tblInfo.TiFlashReplica = &model.TiFlashReplicaInfo{ - Count: 1, - Available: true, - } - } - } - - rows := tk.MustQuery("explain select * from t").Rows() - require.Equal(t, rows[len(rows)-1][2], "mpp[tiflash]") - - rows = tk.MustQuery("explain format='hint' select * from t").Rows() - require.Equal(t, rows[0][0], "read_from_storage(@`sel_1` tiflash[`test`.`t`])") - - hints := tk.MustQuery("explain format='hint' select * from t;").Rows()[0][0] - rows = tk.MustQuery(fmt.Sprintf("explain select /*+ %s */ * from t", hints)).Rows() - require.Equal(t, rows[len(rows)-1][2], "mpp[tiflash]") -} - -func BenchmarkDecodePlan(b *testing.B) { - store := testkit.CreateMockStore(b) - tk := testkit.NewTestKit(b, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a varchar(10) key,b int);") - tk.MustExec("set @@tidb_slow_log_threshold=200000") - - // generate SQL - buf := bytes.NewBuffer(make([]byte, 0, 1024*1024*4)) - for i := 0; i < 50000; i++ { - if i > 0 { - buf.WriteString(" union ") - } - buf.WriteString(fmt.Sprintf("select count(1) as num,a from t where a='%v' group by a", i)) - } - query := buf.String() - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(query) - info := tk.Session().ShowProcess() - require.NotNil(b, info) - p, ok := info.Plan.(core.PhysicalPlan) - require.True(b, ok) - // TODO: optimize the encode plan performance when encode plan with runtimeStats - tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil - encodedPlanStr := core.EncodePlan(p) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := plancodec.DecodePlan(encodedPlanStr) - require.NoError(b, err) - } -} - -func BenchmarkEncodePlan(b *testing.B) { - store := testkit.CreateMockStore(b) - tk := testkit.NewTestKit(b, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists th") - tk.MustExec("set @@session.tidb_enable_table_partition = 1") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") - tk.MustExec("set @@tidb_slow_log_threshold=200000") - - query := "select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i" - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(query) - info := tk.Session().ShowProcess() - require.NotNil(b, info) - p, ok := info.Plan.(core.PhysicalPlan) - require.True(b, ok) - tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil - b.ResetTimer() - for i := 0; i < b.N; i++ { - core.EncodePlan(p) - } -} - -func BenchmarkEncodeFlatPlan(b *testing.B) { - store := testkit.CreateMockStore(b) - tk := testkit.NewTestKit(b, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists th") - tk.MustExec("set @@session.tidb_enable_table_partition = 1") - tk.MustExec(`set @@tidb_partition_prune_mode='` + string(variable.Static) + `'`) - tk.MustExec("create table th (i int, a int,b int, c int, index (a)) partition by hash (a) partitions 8192;") - tk.MustExec("set @@tidb_slow_log_threshold=200000") - - query := "select count(*) from th t1 join th t2 join th t3 join th t4 join th t5 join th t6 where t1.i=t2.a and t1.i=t3.i and t3.i=t4.i and t4.i=t5.i and t5.i=t6.i" - tk.Session().GetSessionVars().PlanID.Store(0) - tk.MustExec(query) - info := tk.Session().ShowProcess() - require.NotNil(b, info) - p, ok := info.Plan.(core.PhysicalPlan) - require.True(b, ok) - tk.Session().GetSessionVars().StmtCtx.RuntimeStatsColl = nil - b.ResetTimer() - for i := 0; i < b.N; i++ { - flat := core.FlattenPhysicalPlan(p, false) - core.EncodeFlatPlan(flat) - } -} - -func TestIssue35090(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists p, t;") - tk.MustExec("create table p (id int, c int, key i_id(id), key i_c(c));") - tk.MustExec("create table t (id int);") - tk.MustExec("insert into p values (3,3), (4,4), (6,6), (9,9);") - tk.MustExec("insert into t values (4), (9);") - tk.MustExec("select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id;") - rows := [][]interface{}{ - {"IndexJoin"}, - {"├─TableReader(Build)"}, - {"│ └─Selection"}, - {"│ └─TableFullScan"}, - {"└─IndexLookUp(Probe)"}, - {" ├─Selection(Build)"}, - {" │ └─IndexRangeScan"}, - {" └─TableRowIDScan(Probe)"}, - } - tk.MustQuery("explain analyze format='brief' select /*+ INL_JOIN(p) */ * from p, t where p.id = t.id;").CheckAt([]int{0}, rows) -} - -func TestCopPaging(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("drop table if exists t") - tk.MustExec("set session tidb_enable_paging = 1") - tk.MustExec("create table t(id int, c1 int, c2 int, primary key (id), key i(c1))") - defer tk.MustExec("drop table t") - for i := 0; i < 1024; i++ { - tk.MustExec("insert into t values(?, ?, ?)", i, i, i) - } - tk.MustExec("analyze table t") - - // limit 960 should go paging - for i := 0; i < 10; i++ { - tk.MustQuery("explain format='brief' select * from t force index(i) where id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 960").Check(testkit.Rows( - "Limit 4.00 root offset:0, count:960", - "└─IndexLookUp 4.00 root ", - " ├─Selection(Build) 1024.00 cop[tikv] le(test.t.id, 1024)", - " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", - " └─Selection(Probe) 4.00 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", - " └─TableRowIDScan 1024.00 cop[tikv] table:t keep order:false")) - } - - // selection between limit and indexlookup, limit 960 should also go paging - for i := 0; i < 10; i++ { - tk.MustQuery("explain format='brief' select * from t force index(i) where mod(id, 2) > 0 and id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 960").Check(testkit.Rows( - "Limit 3.20 root offset:0, count:960", - "└─IndexLookUp 3.20 root ", - " ├─Selection(Build) 819.20 cop[tikv] gt(mod(test.t.id, 2), 0), le(test.t.id, 1024)", - " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", - " └─Selection(Probe) 3.20 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", - " └─TableRowIDScan 819.20 cop[tikv] table:t keep order:false")) - } - - // limit 961 exceeds the threshold, it should not go paging - for i := 0; i < 10; i++ { - tk.MustQuery("explain format='brief' select * from t force index(i) where id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 961").Check(testkit.Rows( - "Limit 4.00 root offset:0, count:961", - "└─IndexLookUp 4.00 root ", - " ├─Selection(Build) 1024.00 cop[tikv] le(test.t.id, 1024)", - " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", - " └─Selection(Probe) 4.00 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", - " └─TableRowIDScan 1024.00 cop[tikv] table:t keep order:false")) - } - - // selection between limit and indexlookup, limit 961 should not go paging too - for i := 0; i < 10; i++ { - tk.MustQuery("explain format='brief' select * from t force index(i) where mod(id, 2) > 0 and id <= 1024 and c1 >= 0 and c1 <= 1024 and c2 in (2, 4, 6, 8) order by c1 limit 961").Check(testkit.Rows( - "Limit 3.20 root offset:0, count:961", - "└─IndexLookUp 3.20 root ", - " ├─Selection(Build) 819.20 cop[tikv] gt(mod(test.t.id, 2), 0), le(test.t.id, 1024)", - " │ └─IndexRangeScan 1024.00 cop[tikv] table:t, index:i(c1) range:[0,1024], keep order:true", - " └─Selection(Probe) 3.20 cop[tikv] in(test.t.c2, 2, 4, 6, 8)", - " └─TableRowIDScan 819.20 cop[tikv] table:t keep order:false")) - } -} - -func TestBuildFinalModeAggregation(t *testing.T) { - aggSchemaBuilder := func(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc) *expression.Schema { - schema := expression.NewSchema(make([]*expression.Column, 0, len(aggFuncs))...) - for _, agg := range aggFuncs { - newCol := &expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: agg.RetTp, - } - schema.Append(newCol) - } - return schema - } - isFinalAggMode := func(mode aggregation.AggFunctionMode) bool { - return mode == aggregation.FinalMode || mode == aggregation.CompleteMode - } - checkResult := func(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc, groubyItems []expression.Expression) { - for partialIsCop := 0; partialIsCop < 2; partialIsCop++ { - for isMPPTask := 0; isMPPTask < 2; isMPPTask++ { - partial, final, _ := core.BuildFinalModeAggregation(sctx, &core.AggInfo{ - AggFuncs: aggFuncs, - GroupByItems: groubyItems, - Schema: aggSchemaBuilder(sctx, aggFuncs), - }, partialIsCop == 0, isMPPTask == 0) - if partial != nil { - for _, aggFunc := range partial.AggFuncs { - if partialIsCop == 0 { - require.True(t, !isFinalAggMode(aggFunc.Mode)) - } else { - require.True(t, isFinalAggMode(aggFunc.Mode)) - } - } - } - if final != nil { - for _, aggFunc := range final.AggFuncs { - require.True(t, isFinalAggMode(aggFunc.Mode)) - } - } - } - } - } - - ctx := core.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - aggCol := &expression.Column{ - Index: 0, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - gbyCol := &expression.Column{ - Index: 1, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - orderCol := &expression.Column{ - Index: 2, - RetType: types.NewFieldType(mysql.TypeLonglong), - } - - emptyGroupByItems := make([]expression.Expression, 0, 1) - groupByItems := make([]expression.Expression, 0, 1) - groupByItems = append(groupByItems, gbyCol) - - orderByItems := make([]*util.ByItems, 0, 1) - orderByItems = append(orderByItems, &util.ByItems{ - Expr: orderCol, - Desc: true, - }) - - aggFuncs := make([]*aggregation.AggFuncDesc, 0, 5) - desc, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncMax, []expression.Expression{aggCol}, false) - require.NoError(t, err) - aggFuncs = append(aggFuncs, desc) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncFirstRow, []expression.Expression{aggCol}, false) - require.NoError(t, err) - aggFuncs = append(aggFuncs, desc) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncCount, []expression.Expression{aggCol}, false) - require.NoError(t, err) - aggFuncs = append(aggFuncs, desc) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncSum, []expression.Expression{aggCol}, false) - require.NoError(t, err) - aggFuncs = append(aggFuncs, desc) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{aggCol}, false) - require.NoError(t, err) - aggFuncs = append(aggFuncs, desc) - - aggFuncsWithDistinct := make([]*aggregation.AggFuncDesc, 0, 2) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncAvg, []expression.Expression{aggCol}, true) - require.NoError(t, err) - aggFuncsWithDistinct = append(aggFuncsWithDistinct, desc) - desc, err = aggregation.NewAggFuncDesc(ctx, ast.AggFuncCount, []expression.Expression{aggCol}, true) - require.NoError(t, err) - aggFuncsWithDistinct = append(aggFuncsWithDistinct, desc) - - groupConcatAggFuncs := make([]*aggregation.AggFuncDesc, 0, 4) - groupConcatWithoutDistinctWithoutOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, false) - require.NoError(t, err) - groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithoutDistinctWithoutOrderBy) - groupConcatWithoutDistinctWithOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, false) - require.NoError(t, err) - groupConcatWithoutDistinctWithOrderBy.OrderByItems = orderByItems - groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithoutDistinctWithOrderBy) - groupConcatWithDistinctWithoutOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, true) - require.NoError(t, err) - groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithDistinctWithoutOrderBy) - groupConcatWithDistinctWithOrderBy, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncGroupConcat, []expression.Expression{aggCol, aggCol}, true) - require.NoError(t, err) - groupConcatWithDistinctWithOrderBy.OrderByItems = orderByItems - groupConcatAggFuncs = append(groupConcatAggFuncs, groupConcatWithDistinctWithOrderBy) - - // case 1 agg without distinct - checkResult(ctx, aggFuncs, emptyGroupByItems) - checkResult(ctx, aggFuncs, groupByItems) - - // case 2 agg with distinct - checkResult(ctx, aggFuncsWithDistinct, emptyGroupByItems) - checkResult(ctx, aggFuncsWithDistinct, groupByItems) - - // case 3 mixed with distinct and without distinct - mixedAggFuncs := make([]*aggregation.AggFuncDesc, 0, 10) - mixedAggFuncs = append(mixedAggFuncs, aggFuncs...) - mixedAggFuncs = append(mixedAggFuncs, aggFuncsWithDistinct...) - checkResult(ctx, mixedAggFuncs, emptyGroupByItems) - checkResult(ctx, mixedAggFuncs, groupByItems) - - // case 4 group concat - for _, groupConcatAggFunc := range groupConcatAggFuncs { - checkResult(ctx, []*aggregation.AggFuncDesc{groupConcatAggFunc}, emptyGroupByItems) - checkResult(ctx, []*aggregation.AggFuncDesc{groupConcatAggFunc}, groupByItems) - } - checkResult(ctx, groupConcatAggFuncs, emptyGroupByItems) - checkResult(ctx, groupConcatAggFuncs, groupByItems) - - // case 5 mixed group concat and other agg funcs - for _, groupConcatAggFunc := range groupConcatAggFuncs { - funcs := make([]*aggregation.AggFuncDesc, 0, 10) - funcs = append(funcs, groupConcatAggFunc) - funcs = append(funcs, aggFuncs...) - checkResult(ctx, funcs, emptyGroupByItems) - checkResult(ctx, funcs, groupByItems) - funcs = append(funcs, aggFuncsWithDistinct...) - checkResult(ctx, funcs, emptyGroupByItems) - checkResult(ctx, funcs, groupByItems) - } - mixedAggFuncs = append(mixedAggFuncs, groupConcatAggFuncs...) - checkResult(ctx, mixedAggFuncs, emptyGroupByItems) - checkResult(ctx, mixedAggFuncs, groupByItems) -} - -func TestIssue40857(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("CREATE TABLE t (c1 mediumint(9) DEFAULT '-4747160',c2 year(4) NOT NULL DEFAULT '2075',c3 double DEFAULT '1.1559030660251948',c4 enum('wbv4','eli','d8ym','m3gsx','lz7td','o','d1k7l','y1x','xcxq','bj','n7') DEFAULT 'xcxq',c5 int(11) DEFAULT '255080866',c6 tinyint(1) DEFAULT '1',PRIMARY KEY (c2),KEY `c4d86d54-091c-4307-957b-b164c9652b7f` (c6,c4) );") - tk.MustExec("insert into t values (-4747160, 2075, 722.5719203870632, 'xcxq', 1576824797, 1);") - tk.MustExec("select /*+ stream_agg() */ bit_or(t.c5) as r0 from t where t.c3 in (select c6 from t where not(t.c6 <> 1) and not(t.c3 in(9263.749352636818))) group by t.c1;") - require.Empty(t, tk.Session().LastMessage()) -} - -func TestCloneFineGrainedShuffleStreamCount(t *testing.T) { - window := &core.PhysicalWindow{} - newPlan, err := window.Clone() - require.NoError(t, err) - newWindow, ok := newPlan.(*core.PhysicalWindow) - require.Equal(t, ok, true) - require.Equal(t, window.TiFlashFineGrainedShuffleStreamCount, newWindow.TiFlashFineGrainedShuffleStreamCount) - - window.TiFlashFineGrainedShuffleStreamCount = 8 - newPlan, err = window.Clone() - require.NoError(t, err) - newWindow, ok = newPlan.(*core.PhysicalWindow) - require.Equal(t, ok, true) - require.Equal(t, window.TiFlashFineGrainedShuffleStreamCount, newWindow.TiFlashFineGrainedShuffleStreamCount) - - sort := &core.PhysicalSort{} - newPlan, err = sort.Clone() - require.NoError(t, err) - newSort, ok := newPlan.(*core.PhysicalSort) - require.Equal(t, ok, true) - require.Equal(t, sort.TiFlashFineGrainedShuffleStreamCount, newSort.TiFlashFineGrainedShuffleStreamCount) - - sort.TiFlashFineGrainedShuffleStreamCount = 8 - newPlan, err = sort.Clone() - require.NoError(t, err) - newSort, ok = newPlan.(*core.PhysicalSort) - require.Equal(t, ok, true) - require.Equal(t, sort.TiFlashFineGrainedShuffleStreamCount, newSort.TiFlashFineGrainedShuffleStreamCount) -} - -func TestIssue40535(t *testing.T) { - store := testkit.CreateMockStore(t) - var cfg kv.InjectionConfig - tk := testkit.NewTestKit(t, kv.NewInjectedStore(store, &cfg)) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t1; drop table if exists t2;") - tk.MustExec("CREATE TABLE `t1`(`c1` bigint(20) NOT NULL DEFAULT '-2312745469307452950', `c2` datetime DEFAULT '5316-02-03 06:54:49', `c3` tinyblob DEFAULT NULL, PRIMARY KEY (`c1`) /*T![clustered_index] CLUSTERED */) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;") - tk.MustExec("CREATE TABLE `t2`(`c1` set('kn8pu','7et','vekx6','v3','liwrh','q14','1met','nnd5i','5o0','8cz','l') DEFAULT '7et,vekx6,liwrh,q14,1met', `c2` float DEFAULT '1.683167', KEY `k1` (`c2`,`c1`), KEY `k2` (`c2`)) ENGINE=InnoDB DEFAULT CHARSET=gbk COLLATE=gbk_chinese_ci;") - tk.MustExec("(select /*+ agg_to_cop()*/ locate(t1.c3, t1.c3) as r0, t1.c3 as r1 from t1 where not( IsNull(t1.c1)) order by r0,r1) union all (select concat_ws(',', t2.c2, t2.c1) as r0, t2.c1 as r1 from t2 order by r0, r1) order by 1 limit 273;") - require.Empty(t, tk.Session().LastMessage()) -} - -func TestExplainValuesStatement(t *testing.T) { - store, _ := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - tk.MustMatchErrMsg("EXPLAIN FORMAT = TRADITIONAL ((VALUES ROW ()) ORDER BY 1)", ".*Unknown table ''.*") -} diff --git a/planner/core/rule_join_reorder_test.go b/planner/core/rule_join_reorder_test.go deleted file mode 100644 index 7448c8d57918b..0000000000000 --- a/planner/core/rule_join_reorder_test.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestJoinOrderHintWithBinding(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t, t1, t2, t3;") - tk.MustExec("create table t(a int, b int, key(a));") - tk.MustExec("create table t1(a int, b int, key(a));") - tk.MustExec("create table t2(a int, b int, key(a));") - tk.MustExec("create table t3(a int, b int, key(a));") - - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ straight_join() */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res := tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`", "SELECT /*+ straight_join()*/ * FROM (`test`.`t1` JOIN `test`.`t2` ON `t1`.`a` = `t2`.`a`) JOIN `test`.`t3` ON `t2`.`b` = `t3`.`b`") - - tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) - - tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b using select /*+ leading(t3) */ * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) join `test` . `t3` on `t2` . `b` = `t3` . `b`") - - tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") - - // test for outer join - tk.MustExec("select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("0")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, len(res), 0) - - tk.MustExec("create global binding for select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b using select /*+ leading(t2) */ * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") - tk.MustExec("select * from t1 join t2 on t1.a=t2.a left join t3 on t2.b=t3.b") - tk.MustQuery("select @@last_plan_from_binding").Check(testkit.Rows("1")) - res = tk.MustQuery("show global bindings").Rows() - require.Equal(t, res[0][0], "select * from ( `test` . `t1` join `test` . `t2` on `t1` . `a` = `t2` . `a` ) left join `test` . `t3` on `t2` . `b` = `t3` . `b`") - - tk.MustExec("drop global binding for select * from t1 join t2 on t1.a=t2.a join t3 on t2.b=t3.b") -} diff --git a/planner/core/stats.go b/planner/core/stats.go deleted file mode 100644 index 4312f181c7a33..0000000000000 --- a/planner/core/stats.go +++ /dev/null @@ -1,1042 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core - -import ( - "context" - "fmt" - "math" - "slices" - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/ranger" - "go.uber.org/zap" -) - -func (p *basePhysicalPlan) StatsCount() float64 { - return p.StatsInfo().RowCount -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalTableDual) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - profile := &property.StatsInfo{ - RowCount: float64(p.RowCount), - ColNDVs: make(map[int64]float64, selfSchema.Len()), - } - for _, col := range selfSchema.Columns { - profile.ColNDVs[col.UniqueID] = float64(p.RowCount) - } - p.SetStats(profile) - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalMemTable) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - statsTable := statistics.PseudoTable(p.TableInfo, false) - stats := &property.StatsInfo{ - RowCount: float64(statsTable.RealtimeCount), - ColNDVs: make(map[int64]float64, len(p.TableInfo.Columns)), - HistColl: statsTable.GenerateHistCollFromColumnInfo(p.TableInfo, p.schema.Columns), - StatsVersion: statistics.PseudoVersion, - } - for _, col := range selfSchema.Columns { - stats.ColNDVs[col.UniqueID] = float64(statsTable.RealtimeCount) - } - p.SetStats(stats) - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalShow) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - // A fake count, just to avoid panic now. - p.SetStats(getFakeStats(selfSchema)) - return p.StatsInfo(), nil -} - -func getFakeStats(schema *expression.Schema) *property.StatsInfo { - profile := &property.StatsInfo{ - RowCount: 1, - ColNDVs: make(map[int64]float64, schema.Len()), - } - for _, col := range schema.Columns { - profile.ColNDVs[col.UniqueID] = 1 - } - return profile -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalShowDDLJobs) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - // A fake count, just to avoid panic now. - p.SetStats(getFakeStats(selfSchema)) - return p.StatsInfo(), nil -} - -// RecursiveDeriveStats4Test is a exporter just for test. -func RecursiveDeriveStats4Test(p LogicalPlan) (*property.StatsInfo, error) { - return p.recursiveDeriveStats(nil) -} - -// GetStats4Test is a exporter just for test. -func GetStats4Test(p LogicalPlan) *property.StatsInfo { - return p.StatsInfo() -} - -func (p *baseLogicalPlan) recursiveDeriveStats(colGroups [][]*expression.Column) (*property.StatsInfo, error) { - childStats := make([]*property.StatsInfo, len(p.children)) - childSchema := make([]*expression.Schema, len(p.children)) - cumColGroups := p.self.ExtractColGroups(colGroups) - for i, child := range p.children { - childProfile, err := child.recursiveDeriveStats(cumColGroups) - if err != nil { - return nil, err - } - childStats[i] = childProfile - childSchema[i] = child.Schema() - } - return p.self.DeriveStats(childStats, p.self.Schema(), childSchema, colGroups) -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (*baseLogicalPlan) ExtractColGroups(_ [][]*expression.Column) [][]*expression.Column { - return nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *baseLogicalPlan) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if len(childStats) == 1 { - p.SetStats(childStats[0]) - return p.StatsInfo(), nil - } - if len(childStats) > 1 { - err := ErrInternal.GenWithStack("LogicalPlans with more than one child should implement their own DeriveStats().") - return nil, err - } - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - profile := &property.StatsInfo{ - RowCount: float64(1), - ColNDVs: make(map[int64]float64, selfSchema.Len()), - } - for _, col := range selfSchema.Columns { - profile.ColNDVs[col.UniqueID] = 1 - } - p.SetStats(profile) - return profile, nil -} - -func (ds *DataSource) getGroupNDVs(colGroups [][]*expression.Column) []property.GroupNDV { - if colGroups == nil { - return nil - } - tbl := ds.tableStats.HistColl - ndvs := make([]property.GroupNDV, 0, len(colGroups)) - for idxID, idx := range tbl.Indices { - colsLen := len(tbl.Idx2ColumnIDs[idxID]) - // tbl.Idx2ColumnIDs may only contain the prefix of index columns. - // But it may exceeds the total index since the index would contain the handle column if it's not a unique index. - // We append the handle at fillIndexPath. - if colsLen < len(idx.Info.Columns) { - continue - } else if colsLen > len(idx.Info.Columns) { - colsLen-- - } - idxCols := make([]int64, colsLen) - copy(idxCols, tbl.Idx2ColumnIDs[idxID]) - slices.Sort(idxCols) - for _, g := range colGroups { - // We only want those exact matches. - if len(g) != colsLen { - continue - } - match := true - for i, col := range g { - // Both slices are sorted according to UniqueID. - if col.UniqueID != idxCols[i] { - match = false - break - } - } - if match { - ndv := property.GroupNDV{ - Cols: idxCols, - NDV: float64(idx.NDV), - } - ndvs = append(ndvs, ndv) - break - } - } - } - return ndvs -} - -func init() { - // To handle cycle import, we have to define this function here. - cardinality.GetTblInfoForUsedStatsByPhysicalID = getTblInfoForUsedStatsByPhysicalID -} - -// getTblInfoForUsedStatsByPhysicalID get table name, partition name and TableInfo that will be used to record used stats. -func getTblInfoForUsedStatsByPhysicalID(sctx sessionctx.Context, id int64) (fullName string, tblInfo *model.TableInfo) { - fullName = "tableID " + strconv.FormatInt(id, 10) - - is := domain.GetDomain(sctx).InfoSchema() - var tbl table.Table - var partDef *model.PartitionDefinition - - tbl, partDef = infoschema.FindTableByTblOrPartID(is, id) - if tbl == nil || tbl.Meta() == nil { - return - } - tblInfo = tbl.Meta() - fullName = tblInfo.Name.O - if partDef != nil { - fullName += " " + partDef.Name.O - } else if pi := tblInfo.GetPartitionInfo(); pi != nil && len(pi.Definitions) > 0 { - fullName += " global" - } - return -} - -func (ds *DataSource) initStats(colGroups [][]*expression.Column) { - if ds.tableStats != nil { - // Reload GroupNDVs since colGroups may have changed. - ds.tableStats.GroupNDVs = ds.getGroupNDVs(colGroups) - return - } - if ds.statisticTable == nil { - ds.statisticTable = getStatsTable(ds.SCtx(), ds.tableInfo, ds.physicalTableID) - } - tableStats := &property.StatsInfo{ - RowCount: float64(ds.statisticTable.RealtimeCount), - ColNDVs: make(map[int64]float64, ds.schema.Len()), - HistColl: ds.statisticTable.GenerateHistCollFromColumnInfo(ds.tableInfo, ds.TblCols), - StatsVersion: ds.statisticTable.Version, - } - if ds.statisticTable.Pseudo { - tableStats.StatsVersion = statistics.PseudoVersion - } - - statsRecord := ds.SCtx().GetSessionVars().StmtCtx.GetUsedStatsInfo(true) - name, tblInfo := getTblInfoForUsedStatsByPhysicalID(ds.SCtx(), ds.physicalTableID) - statsRecord[ds.physicalTableID] = &stmtctx.UsedStatsInfoForTable{ - Name: name, - TblInfo: tblInfo, - Version: tableStats.StatsVersion, - RealtimeCount: tableStats.HistColl.RealtimeCount, - ModifyCount: tableStats.HistColl.ModifyCount, - } - - for _, col := range ds.schema.Columns { - tableStats.ColNDVs[col.UniqueID] = cardinality.EstimateColumnNDV(ds.statisticTable, col.ID) - } - ds.tableStats = tableStats - ds.tableStats.GroupNDVs = ds.getGroupNDVs(colGroups) - ds.TblColHists = ds.statisticTable.ID2UniqueID(ds.TblCols) -} - -func (ds *DataSource) deriveStatsByFilter(conds expression.CNFExprs, filledPaths []*util.AccessPath) *property.StatsInfo { - if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(ds.SCtx()) - defer debugtrace.LeaveContextCommon(ds.SCtx()) - } - selectivity, _, err := cardinality.Selectivity(ds.SCtx(), ds.tableStats.HistColl, conds, filledPaths) - if err != nil { - logutil.BgLogger().Debug("something wrong happened, use the default selectivity", zap.Error(err)) - selectivity = SelectionFactor - } - // TODO: remove NewHistCollBySelectivity later on. - // if ds.SCtx().GetSessionVars().OptimizerSelectivityLevel >= 1 { - // Only '0' is suggested, see https://docs.pingcap.com/zh/tidb/stable/system-variables#tidb_optimizer_selectivity_level. - // stats.HistColl = stats.HistColl.NewHistCollBySelectivity(ds.SCtx(), nodes) - // } - return ds.tableStats.Scale(selectivity) -} - -// We bind logic of derivePathStats and tryHeuristics together. When some path matches the heuristic rule, we don't need -// to derive stats of subsequent paths. In this way we can save unnecessary computation of derivePathStats. -func (ds *DataSource) derivePathStatsAndTryHeuristics() error { - if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(ds.SCtx()) - defer debugtrace.LeaveContextCommon(ds.SCtx()) - } - uniqueIdxsWithDoubleScan := make([]*util.AccessPath, 0, len(ds.possibleAccessPaths)) - singleScanIdxs := make([]*util.AccessPath, 0, len(ds.possibleAccessPaths)) - var ( - selected, uniqueBest, refinedBest *util.AccessPath - isRefinedPath bool - ) - for _, path := range ds.possibleAccessPaths { - if path.IsTablePath() { - err := ds.deriveTablePathStats(path, ds.pushedDownConds, false) - if err != nil { - return err - } - path.IsSingleScan = true - } else { - ds.deriveIndexPathStats(path, ds.pushedDownConds, false) - path.IsSingleScan = ds.isSingleScan(path.FullIdxCols, path.FullIdxColLens) - } - // Try some heuristic rules to select access path. - if len(path.Ranges) == 0 { - selected = path - break - } - if path.OnlyPointRange(ds.SCtx()) { - if path.IsTablePath() || path.Index.Unique { - if path.IsSingleScan { - selected = path - break - } - uniqueIdxsWithDoubleScan = append(uniqueIdxsWithDoubleScan, path) - } - } else if path.IsSingleScan { - singleScanIdxs = append(singleScanIdxs, path) - } - } - if selected == nil && len(uniqueIdxsWithDoubleScan) > 0 { - uniqueIdxAccessCols := make([]util.Col2Len, 0, len(uniqueIdxsWithDoubleScan)) - for _, uniqueIdx := range uniqueIdxsWithDoubleScan { - uniqueIdxAccessCols = append(uniqueIdxAccessCols, uniqueIdx.GetCol2LenFromAccessConds()) - // Find the unique index with the minimal number of ranges as `uniqueBest`. - if uniqueBest == nil || len(uniqueIdx.Ranges) < len(uniqueBest.Ranges) { - uniqueBest = uniqueIdx - } - } - // `uniqueBest` may not always be the best. - // ``` - // create table t(a int, b int, c int, unique index idx_b(b), index idx_b_c(b, c)); - // select b, c from t where b = 5 and c > 10; - // ``` - // In the case, `uniqueBest` is `idx_b`. However, `idx_b_c` is better than `idx_b`. - // Hence, for each index in `singleScanIdxs`, we check whether it is better than some index in `uniqueIdxsWithDoubleScan`. - // If yes, the index is a refined one. We find the refined index with the minimal number of ranges as `refineBest`. - for _, singleScanIdx := range singleScanIdxs { - col2Len := singleScanIdx.GetCol2LenFromAccessConds() - for _, uniqueIdxCol2Len := range uniqueIdxAccessCols { - accessResult, comparable1 := util.CompareCol2Len(col2Len, uniqueIdxCol2Len) - if comparable1 && accessResult == 1 { - if refinedBest == nil || len(singleScanIdx.Ranges) < len(refinedBest.Ranges) { - refinedBest = singleScanIdx - } - } - } - } - // `refineBest` may not always be better than `uniqueBest`. - // ``` - // create table t(a int, b int, c int, d int, unique index idx_a(a), unique index idx_b_c(b, c), unique index idx_b_c_a_d(b, c, a, d)); - // select a, b, c from t where a = 1 and b = 2 and c in (1, 2, 3, 4, 5); - // ``` - // In the case, `refinedBest` is `idx_b_c_a_d` and `uniqueBest` is `a`. `idx_b_c_a_d` needs to access five points while `idx_a` - // only needs one point access and one table access. - // Hence we should compare `len(refinedBest.Ranges)` and `2*len(uniqueBest.Ranges)` to select the better one. - if refinedBest != nil && (uniqueBest == nil || len(refinedBest.Ranges) < 2*len(uniqueBest.Ranges)) { - selected = refinedBest - isRefinedPath = true - } else { - selected = uniqueBest - } - } - // heuristic rule pruning other path should consider hint prefer. - // If no hints and some path matches a heuristic rule, just remove other possible paths. - if selected != nil { - // if user wanna tiFlash read, while current heuristic choose a TiKV path. so we shouldn't prune other paths. - keep := ds.preferStoreType&preferTiFlash != 0 && selected.StoreType != kv.TiFlash - if keep { - return nil - } - ds.possibleAccessPaths[0] = selected - ds.possibleAccessPaths = ds.possibleAccessPaths[:1] - var tableName string - if ds.TableAsName.O == "" { - tableName = ds.tableInfo.Name.O - } else { - tableName = ds.TableAsName.O - } - var sb strings.Builder - if selected.IsTablePath() { - // TODO: primary key / handle / real name? - fmt.Fprintf(&sb, "handle of %s is selected since the path only has point ranges", tableName) - } else { - if selected.Index.Unique { - sb.WriteString("unique ") - } - sb.WriteString(fmt.Sprintf("index %s of %s is selected since the path", selected.Index.Name.O, tableName)) - if isRefinedPath { - sb.WriteString(" only fetches limited number of rows") - } else { - sb.WriteString(" only has point ranges") - } - if selected.IsSingleScan { - sb.WriteString(" with single scan") - } else { - sb.WriteString(" with double scan") - } - } - if ds.SCtx().GetSessionVars().StmtCtx.InVerboseExplain { - ds.SCtx().GetSessionVars().StmtCtx.AppendNote(errors.New(sb.String())) - } else { - ds.SCtx().GetSessionVars().StmtCtx.AppendExtraNote(errors.New(sb.String())) - } - } - return nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (ds *DataSource) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - if ds.StatsInfo() != nil && len(colGroups) == 0 { - return ds.StatsInfo(), nil - } - ds.initStats(colGroups) - if ds.StatsInfo() != nil { - // Just reload the GroupNDVs. - selectivity := ds.StatsInfo().RowCount / ds.tableStats.RowCount - ds.SetStats(ds.tableStats.Scale(selectivity)) - return ds.StatsInfo(), nil - } - if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(ds.SCtx()) - defer debugtrace.LeaveContextCommon(ds.SCtx()) - } - // two preprocess here. - // 1: PushDownNot here can convert query 'not (a != 1)' to 'a = 1'. - // 2: EliminateNoPrecisionCast here can convert query 'cast(c as bigint) = 1' to 'c = 1' to leverage access range. - for i, expr := range ds.pushedDownConds { - ds.pushedDownConds[i] = expression.PushDownNot(ds.SCtx(), expr) - ds.pushedDownConds[i] = expression.EliminateNoPrecisionLossCast(ds.SCtx(), ds.pushedDownConds[i]) - } - for _, path := range ds.possibleAccessPaths { - if path.IsTablePath() { - continue - } - err := ds.fillIndexPath(path, ds.pushedDownConds) - if err != nil { - return nil, err - } - } - // TODO: Can we move ds.deriveStatsByFilter after pruning by heuristics? In this way some computation can be avoided - // when ds.possibleAccessPaths are pruned. - ds.SetStats(ds.deriveStatsByFilter(ds.pushedDownConds, ds.possibleAccessPaths)) - err := ds.derivePathStatsAndTryHeuristics() - if err != nil { - return nil, err - } - - if err := ds.generateIndexMergePath(); err != nil { - return nil, err - } - - if ds.SCtx().GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugTraceAccessPaths(ds.SCtx(), ds.possibleAccessPaths) - } - ds.accessPathMinSelectivity = getMinSelectivityFromPaths(ds.possibleAccessPaths, float64(ds.TblColHists.RealtimeCount)) - - return ds.StatsInfo(), nil -} - -func getMinSelectivityFromPaths(paths []*util.AccessPath, totalRowCount float64) float64 { - minSelectivity := 1.0 - if totalRowCount <= 0 { - return minSelectivity - } - for _, path := range paths { - // For table path and index merge path, AccessPath.CountAfterIndex is not set and meaningless, - // but we still consider their AccessPath.CountAfterAccess. - if path.IsTablePath() || path.PartialIndexPaths != nil { - minSelectivity = mathutil.Min(minSelectivity, path.CountAfterAccess/totalRowCount) - continue - } - minSelectivity = mathutil.Min(minSelectivity, path.CountAfterIndex/totalRowCount) - } - return minSelectivity -} - -// DeriveStats implements LogicalPlan DeriveStats interface. -func (ts *LogicalTableScan) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (_ *property.StatsInfo, err error) { - ts.Source.initStats(nil) - // PushDownNot here can convert query 'not (a != 1)' to 'a = 1'. - for i, expr := range ts.AccessConds { - // TODO The expressions may be shared by TableScan and several IndexScans, there would be redundant - // `PushDownNot` function call in multiple `DeriveStats` then. - ts.AccessConds[i] = expression.PushDownNot(ts.SCtx(), expr) - } - ts.SetStats(ts.Source.deriveStatsByFilter(ts.AccessConds, nil)) - // ts.Handle could be nil if PK is Handle, and PK column has been pruned. - // TODO: support clustered index. - if ts.HandleCols != nil { - // TODO: restrict mem usage of table ranges. - ts.Ranges, _, _, err = ranger.BuildTableRange(ts.AccessConds, ts.SCtx(), ts.HandleCols.GetCol(0).RetType, 0) - } else { - isUnsigned := false - if ts.Source.tableInfo.PKIsHandle { - if pkColInfo := ts.Source.tableInfo.GetPkColInfo(); pkColInfo != nil { - isUnsigned = mysql.HasUnsignedFlag(pkColInfo.GetFlag()) - } - } - ts.Ranges = ranger.FullIntRange(isUnsigned) - } - if err != nil { - return nil, err - } - return ts.StatsInfo(), nil -} - -// DeriveStats implements LogicalPlan DeriveStats interface. -func (is *LogicalIndexScan) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - is.Source.initStats(nil) - for i, expr := range is.AccessConds { - is.AccessConds[i] = expression.PushDownNot(is.SCtx(), expr) - } - is.SetStats(is.Source.deriveStatsByFilter(is.AccessConds, nil)) - if len(is.AccessConds) == 0 { - is.Ranges = ranger.FullRange() - } - is.IdxCols, is.IdxColLens = expression.IndexInfo2PrefixCols(is.Columns, selfSchema.Columns, is.Index) - is.FullIdxCols, is.FullIdxColLens = expression.IndexInfo2Cols(is.Columns, selfSchema.Columns, is.Index) - if !is.Index.Unique && !is.Index.Primary && len(is.Index.Columns) == len(is.IdxCols) { - handleCol := is.getPKIsHandleCol(selfSchema) - if handleCol != nil && !mysql.HasUnsignedFlag(handleCol.RetType.GetFlag()) { - is.IdxCols = append(is.IdxCols, handleCol) - is.IdxColLens = append(is.IdxColLens, types.UnspecifiedLength) - } - } - return is.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalSelection) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - p.SetStats(childStats[0].Scale(SelectionFactor)) - p.StatsInfo().GroupNDVs = nil - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalUnionAll) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - p.SetStats(&property.StatsInfo{ - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - for _, childProfile := range childStats { - p.StatsInfo().RowCount += childProfile.RowCount - for _, col := range selfSchema.Columns { - p.StatsInfo().ColNDVs[col.UniqueID] += childProfile.ColNDVs[col.UniqueID] - } - } - return p.StatsInfo(), nil -} - -func deriveLimitStats(childProfile *property.StatsInfo, limitCount float64) *property.StatsInfo { - stats := &property.StatsInfo{ - RowCount: math.Min(limitCount, childProfile.RowCount), - ColNDVs: make(map[int64]float64, len(childProfile.ColNDVs)), - } - for id, c := range childProfile.ColNDVs { - stats.ColNDVs[id] = math.Min(c, stats.RowCount) - } - return stats -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalLimit) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - p.SetStats(deriveLimitStats(childStats[0], float64(p.Count))) - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (lt *LogicalTopN) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if lt.StatsInfo() != nil { - return lt.StatsInfo(), nil - } - lt.SetStats(deriveLimitStats(childStats[0], float64(lt.Count))) - return lt.StatsInfo(), nil -} - -func (p *LogicalProjection) getGroupNDVs(colGroups [][]*expression.Column, childProfile *property.StatsInfo, selfSchema *expression.Schema) []property.GroupNDV { - if len(colGroups) == 0 || len(childProfile.GroupNDVs) == 0 { - return nil - } - exprCol2ProjCol := make(map[int64]int64) - for i, expr := range p.Exprs { - exprCol, ok := expr.(*expression.Column) - if !ok { - continue - } - exprCol2ProjCol[exprCol.UniqueID] = selfSchema.Columns[i].UniqueID - } - ndvs := make([]property.GroupNDV, 0, len(childProfile.GroupNDVs)) - for _, childGroupNDV := range childProfile.GroupNDVs { - projCols := make([]int64, len(childGroupNDV.Cols)) - for i, col := range childGroupNDV.Cols { - projCol, ok := exprCol2ProjCol[col] - if !ok { - projCols = nil - break - } - projCols[i] = projCol - } - if projCols == nil { - continue - } - slices.Sort(projCols) - groupNDV := property.GroupNDV{ - Cols: projCols, - NDV: childGroupNDV.NDV, - } - ndvs = append(ndvs, groupNDV) - } - return ndvs -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalProjection) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - childProfile := childStats[0] - if p.StatsInfo() != nil { - // Reload GroupNDVs since colGroups may have changed. - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childProfile, selfSchema) - return p.StatsInfo(), nil - } - p.SetStats(&property.StatsInfo{ - RowCount: childProfile.RowCount, - ColNDVs: make(map[int64]float64, len(p.Exprs)), - }) - for i, expr := range p.Exprs { - cols := expression.ExtractColumns(expr) - p.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID], _ = cardinality.EstimateColsNDVWithMatchedLen(cols, childSchema[0], childProfile) - } - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childProfile, selfSchema) - return p.StatsInfo(), nil -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (p *LogicalProjection) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { - if len(colGroups) == 0 { - return nil - } - extColGroups, _ := p.Schema().ExtractColGroups(colGroups) - if len(extColGroups) == 0 { - return nil - } - extracted := make([][]*expression.Column, 0, len(extColGroups)) - for _, cols := range extColGroups { - exprs := make([]*expression.Column, len(cols)) - allCols := true - for i, offset := range cols { - col, ok := p.Exprs[offset].(*expression.Column) - // TODO: for functional dependent projections like `col1 + 1` -> `col2`, we can maintain GroupNDVs actually. - if !ok { - allCols = false - break - } - exprs[i] = col - } - if allCols { - extracted = append(extracted, expression.SortColumns(exprs)) - } - } - return extracted -} - -func (*LogicalAggregation) getGroupNDVs(colGroups [][]*expression.Column, childProfile *property.StatsInfo, gbyCols []*expression.Column) []property.GroupNDV { - if len(colGroups) == 0 { - return nil - } - // Check if the child profile provides GroupNDV for the GROUP BY columns. - // Note that gbyCols may not be the exact GROUP BY columns, e.g, GROUP BY a+b, - // but we have no other approaches for the NDV estimation of these cases - // except for using the independent assumption, unless we can use stats of expression index. - groupNDV := childProfile.GetGroupNDV4Cols(gbyCols) - if groupNDV == nil { - return nil - } - return []property.GroupNDV{*groupNDV} -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (la *LogicalAggregation) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - childProfile := childStats[0] - gbyCols := make([]*expression.Column, 0, len(la.GroupByItems)) - for _, gbyExpr := range la.GroupByItems { - cols := expression.ExtractColumns(gbyExpr) - gbyCols = append(gbyCols, cols...) - } - if la.StatsInfo() != nil { - // Reload GroupNDVs since colGroups may have changed. - la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childProfile, gbyCols) - return la.StatsInfo(), nil - } - ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(gbyCols, childSchema[0], childProfile) - la.SetStats(&property.StatsInfo{ - RowCount: ndv, - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - // We cannot estimate the ColNDVs for every output, so we use a conservative strategy. - for _, col := range selfSchema.Columns { - la.StatsInfo().ColNDVs[col.UniqueID] = ndv - } - la.inputCount = childProfile.RowCount - la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childProfile, gbyCols) - return la.StatsInfo(), nil -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (la *LogicalAggregation) ExtractColGroups(_ [][]*expression.Column) [][]*expression.Column { - // Parent colGroups would be dicarded, because aggregation would make NDV of colGroups - // which does not match GroupByItems invalid. - // Note that gbyCols may not be the exact GROUP BY columns, e.g, GROUP BY a+b, - // but we have no other approaches for the NDV estimation of these cases - // except for using the independent assumption, unless we can use stats of expression index. - gbyCols := make([]*expression.Column, 0, len(la.GroupByItems)) - for _, gbyExpr := range la.GroupByItems { - cols := expression.ExtractColumns(gbyExpr) - gbyCols = append(gbyCols, cols...) - } - if len(gbyCols) > 1 { - return [][]*expression.Column{expression.SortColumns(gbyCols)} - } - return nil -} - -func (p *LogicalJoin) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { - outerIdx := int(-1) - if p.JoinType == LeftOuterJoin || p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { - outerIdx = 0 - } else if p.JoinType == RightOuterJoin { - outerIdx = 1 - } - if outerIdx >= 0 && len(colGroups) > 0 { - return childStats[outerIdx].GroupNDVs - } - return nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -// If the type of join is SemiJoin, the selectivity of it will be same as selection's. -// If the type of join is LeftOuterSemiJoin, it will not add or remove any row. The last column is a boolean value, whose NDV should be two. -// If the type of join is inner/outer join, the output of join(s, t) should be N(s) * N(t) / (V(s.key) * V(t.key)) * Min(s.key, t.key). -// N(s) stands for the number of rows in relation s. V(s.key) means the NDV of join key in s. -// This is a quite simple strategy: We assume every bucket of relation which will participate join has the same number of rows, and apply cross join for -// every matched bucket. -func (p *LogicalJoin) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - // Reload GroupNDVs since colGroups may have changed. - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) - return p.StatsInfo(), nil - } - leftProfile, rightProfile := childStats[0], childStats[1] - leftJoinKeys, rightJoinKeys, _, _ := p.GetJoinKeys() - p.equalCondOutCnt = cardinality.EstimateFullJoinRowCount(p.SCtx(), - 0 == len(p.EqualConditions), - leftProfile, rightProfile, - leftJoinKeys, rightJoinKeys, - childSchema[0], childSchema[1], - nil, nil) - if p.JoinType == SemiJoin || p.JoinType == AntiSemiJoin { - p.SetStats(&property.StatsInfo{ - RowCount: leftProfile.RowCount * SelectionFactor, - ColNDVs: make(map[int64]float64, len(leftProfile.ColNDVs)), - }) - for id, c := range leftProfile.ColNDVs { - p.StatsInfo().ColNDVs[id] = c * SelectionFactor - } - return p.StatsInfo(), nil - } - if p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { - p.SetStats(&property.StatsInfo{ - RowCount: leftProfile.RowCount, - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - for id, c := range leftProfile.ColNDVs { - p.StatsInfo().ColNDVs[id] = c - } - p.StatsInfo().ColNDVs[selfSchema.Columns[selfSchema.Len()-1].UniqueID] = 2.0 - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) - return p.StatsInfo(), nil - } - count := p.equalCondOutCnt - if p.JoinType == LeftOuterJoin { - count = math.Max(count, leftProfile.RowCount) - } else if p.JoinType == RightOuterJoin { - count = math.Max(count, rightProfile.RowCount) - } - colNDVs := make(map[int64]float64, selfSchema.Len()) - for id, c := range leftProfile.ColNDVs { - colNDVs[id] = math.Min(c, count) - } - for id, c := range rightProfile.ColNDVs { - colNDVs[id] = math.Min(c, count) - } - p.SetStats(&property.StatsInfo{ - RowCount: count, - ColNDVs: colNDVs, - }) - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) - return p.StatsInfo(), nil -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (p *LogicalJoin) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { - leftJoinKeys, rightJoinKeys, _, _ := p.GetJoinKeys() - extracted := make([][]*expression.Column, 0, 2+len(colGroups)) - if len(leftJoinKeys) > 1 && (p.JoinType == InnerJoin || p.JoinType == LeftOuterJoin || p.JoinType == RightOuterJoin) { - extracted = append(extracted, expression.SortColumns(leftJoinKeys), expression.SortColumns(rightJoinKeys)) - } - var outerSchema *expression.Schema - if p.JoinType == LeftOuterJoin || p.JoinType == LeftOuterSemiJoin || p.JoinType == AntiLeftOuterSemiJoin { - outerSchema = p.Children()[0].Schema() - } else if p.JoinType == RightOuterJoin { - outerSchema = p.Children()[1].Schema() - } - if len(colGroups) == 0 || outerSchema == nil { - return extracted - } - _, offsets := outerSchema.ExtractColGroups(colGroups) - if len(offsets) == 0 { - return extracted - } - for _, offset := range offsets { - extracted = append(extracted, colGroups[offset]) - } - return extracted -} - -func (la *LogicalApply) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { - if len(colGroups) > 0 && (la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin || la.JoinType == LeftOuterJoin) { - return childStats[0].GroupNDVs - } - return nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (la *LogicalApply) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, childSchema []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - if la.StatsInfo() != nil { - // Reload GroupNDVs since colGroups may have changed. - la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childStats) - return la.StatsInfo(), nil - } - leftProfile := childStats[0] - la.SetStats(&property.StatsInfo{ - RowCount: leftProfile.RowCount, - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - for id, c := range leftProfile.ColNDVs { - la.StatsInfo().ColNDVs[id] = c - } - if la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin { - la.StatsInfo().ColNDVs[selfSchema.Columns[selfSchema.Len()-1].UniqueID] = 2.0 - } else { - for i := childSchema[0].Len(); i < selfSchema.Len(); i++ { - la.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID] = leftProfile.RowCount - } - } - la.StatsInfo().GroupNDVs = la.getGroupNDVs(colGroups, childStats) - return la.StatsInfo(), nil -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (la *LogicalApply) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { - var outerSchema *expression.Schema - // Apply doesn't have RightOuterJoin. - if la.JoinType == LeftOuterJoin || la.JoinType == LeftOuterSemiJoin || la.JoinType == AntiLeftOuterSemiJoin { - outerSchema = la.Children()[0].Schema() - } - if len(colGroups) == 0 || outerSchema == nil { - return nil - } - _, offsets := outerSchema.ExtractColGroups(colGroups) - if len(offsets) == 0 { - return nil - } - extracted := make([][]*expression.Column, len(offsets)) - for i, offset := range offsets { - extracted[i] = colGroups[offset] - } - return extracted -} - -// Exists and MaxOneRow produce at most one row, so we set the RowCount of stats one. -func getSingletonStats(schema *expression.Schema) *property.StatsInfo { - ret := &property.StatsInfo{ - RowCount: 1.0, - ColNDVs: make(map[int64]float64, schema.Len()), - } - for _, col := range schema.Columns { - ret.ColNDVs[col.UniqueID] = 1 - } - return ret -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalMaxOneRow) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - p.SetStats(getSingletonStats(selfSchema)) - return p.StatsInfo(), nil -} - -func (*LogicalWindow) getGroupNDVs(colGroups [][]*expression.Column, childStats []*property.StatsInfo) []property.GroupNDV { - if len(colGroups) > 0 { - return childStats[0].GroupNDVs - } - return nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalWindow) DeriveStats(childStats []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, colGroups [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - // Reload GroupNDVs since colGroups may have changed. - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) - return p.StatsInfo(), nil - } - childProfile := childStats[0] - p.SetStats(&property.StatsInfo{ - RowCount: childProfile.RowCount, - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - childLen := selfSchema.Len() - len(p.WindowFuncDescs) - for i := 0; i < childLen; i++ { - id := selfSchema.Columns[i].UniqueID - p.StatsInfo().ColNDVs[id] = childProfile.ColNDVs[id] - } - for i := childLen; i < selfSchema.Len(); i++ { - p.StatsInfo().ColNDVs[selfSchema.Columns[i].UniqueID] = childProfile.RowCount - } - p.StatsInfo().GroupNDVs = p.getGroupNDVs(colGroups, childStats) - return p.StatsInfo(), nil -} - -// ExtractColGroups implements LogicalPlan ExtractColGroups interface. -func (p *LogicalWindow) ExtractColGroups(colGroups [][]*expression.Column) [][]*expression.Column { - if len(colGroups) == 0 { - return nil - } - childSchema := p.Children()[0].Schema() - _, offsets := childSchema.ExtractColGroups(colGroups) - if len(offsets) == 0 { - return nil - } - extracted := make([][]*expression.Column, len(offsets)) - for i, offset := range offsets { - extracted[i] = colGroups[offset] - } - return extracted -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalCTE) DeriveStats(_ []*property.StatsInfo, selfSchema *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - - var err error - if p.cte.seedPartPhysicalPlan == nil { - // Build push-downed predicates. - if len(p.cte.pushDownPredicates) > 0 { - newCond := expression.ComposeDNFCondition(p.SCtx(), p.cte.pushDownPredicates...) - newSel := LogicalSelection{Conditions: []expression.Expression{newCond}}.Init(p.SCtx(), p.cte.seedPartLogicalPlan.SelectBlockOffset()) - newSel.SetChildren(p.cte.seedPartLogicalPlan) - p.cte.seedPartLogicalPlan = newSel - p.cte.optFlag |= flagPredicatePushDown - } - p.cte.seedPartLogicalPlan, p.cte.seedPartPhysicalPlan, _, err = DoOptimizeAndLogicAsRet(context.TODO(), p.SCtx(), p.cte.optFlag, p.cte.seedPartLogicalPlan) - if err != nil { - return nil, err - } - } - if p.onlyUsedAsStorage { - p.SetChildren(p.cte.seedPartLogicalPlan) - } - resStat := p.cte.seedPartPhysicalPlan.StatsInfo() - // Changing the pointer so that seedStat in LogicalCTETable can get the new stat. - *p.seedStat = *resStat - p.SetStats(&property.StatsInfo{ - RowCount: resStat.RowCount, - ColNDVs: make(map[int64]float64, selfSchema.Len()), - }) - for i, col := range selfSchema.Columns { - p.StatsInfo().ColNDVs[col.UniqueID] += resStat.ColNDVs[p.cte.seedPartLogicalPlan.Schema().Columns[i].UniqueID] - } - if p.cte.recursivePartLogicalPlan != nil { - if p.cte.recursivePartPhysicalPlan == nil { - p.cte.recursivePartPhysicalPlan, _, err = DoOptimize(context.TODO(), p.SCtx(), p.cte.optFlag, p.cte.recursivePartLogicalPlan) - if err != nil { - return nil, err - } - } - recurStat := p.cte.recursivePartLogicalPlan.StatsInfo() - for i, col := range selfSchema.Columns { - p.StatsInfo().ColNDVs[col.UniqueID] += recurStat.ColNDVs[p.cte.recursivePartLogicalPlan.Schema().Columns[i].UniqueID] - } - if p.cte.IsDistinct { - p.StatsInfo().RowCount, _ = cardinality.EstimateColsNDVWithMatchedLen(p.schema.Columns, p.schema, p.StatsInfo()) - } else { - p.StatsInfo().RowCount += recurStat.RowCount - } - } - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalCTETable) DeriveStats(_ []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - if p.StatsInfo() != nil { - return p.StatsInfo(), nil - } - p.SetStats(p.seedStat) - return p.StatsInfo(), nil -} - -// DeriveStats implement LogicalPlan DeriveStats interface. -func (p *LogicalSequence) DeriveStats(childStats []*property.StatsInfo, _ *expression.Schema, _ []*expression.Schema, _ [][]*expression.Column) (*property.StatsInfo, error) { - p.SetStats(childStats[len(childStats)-1]) - return p.StatsInfo(), nil -} diff --git a/planner/core/stringer.go b/planner/core/stringer.go deleted file mode 100644 index de045c5d0b0af..0000000000000 --- a/planner/core/stringer.go +++ /dev/null @@ -1,378 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package core - -import ( - "bytes" - "fmt" - "strings" - - "github.com/pingcap/tidb/util/plancodec" -) - -// ToString explains a Plan, returns description string. -func ToString(p Plan) string { - strs, _ := toString(p, []string{}, []int{}) - return strings.Join(strs, "->") -} - -// FDToString explains fd transfer over a Plan, returns description string. -func FDToString(p LogicalPlan) string { - strs, _ := fdToString(p, []string{}, []int{}) - for i, j := 0, len(strs)-1; i < j; i, j = i+1, j-1 { - strs[i], strs[j] = strs[j], strs[i] - } - return strings.Join(strs, " >>> ") -} - -func needIncludeChildrenString(plan Plan) bool { - switch x := plan.(type) { - case *LogicalUnionAll, *PhysicalUnionAll, *LogicalPartitionUnionAll: - // after https://github.com/pingcap/tidb/pull/25218, the union may contain less than 2 children, - // but we still wants to include its child plan's information when calling `toString` on union. - return true - case LogicalPlan: - return len(x.Children()) > 1 - case PhysicalPlan: - return len(x.Children()) > 1 - default: - return false - } -} - -func fdToString(in LogicalPlan, strs []string, idxs []int) ([]string, []int) { - switch x := in.(type) { - case *LogicalProjection: - strs = append(strs, "{"+x.fdSet.String()+"}") - for _, child := range x.Children() { - strs, idxs = fdToString(child, strs, idxs) - } - case *LogicalAggregation: - strs = append(strs, "{"+x.fdSet.String()+"}") - for _, child := range x.Children() { - strs, idxs = fdToString(child, strs, idxs) - } - case *DataSource: - strs = append(strs, "{"+x.fdSet.String()+"}") - case *LogicalApply: - strs = append(strs, "{"+x.fdSet.String()+"}") - case *LogicalJoin: - strs = append(strs, "{"+x.fdSet.String()+"}") - default: - } - return strs, idxs -} - -func toString(in Plan, strs []string, idxs []int) ([]string, []int) { - switch x := in.(type) { - case LogicalPlan: - if needIncludeChildrenString(in) { - idxs = append(idxs, len(strs)) - } - - for _, c := range x.Children() { - strs, idxs = toString(c, strs, idxs) - } - case *PhysicalExchangeReceiver: // do nothing - case PhysicalPlan: - if needIncludeChildrenString(in) { - idxs = append(idxs, len(strs)) - } - - for _, c := range x.Children() { - strs, idxs = toString(c, strs, idxs) - } - } - - var str string - switch x := in.(type) { - case *CheckTable: - str = "CheckTable" - case *PhysicalIndexScan: - str = fmt.Sprintf("Index(%s.%s)%v", x.Table.Name.L, x.Index.Name.L, x.Ranges) - case *PhysicalTableScan: - str = fmt.Sprintf("Table(%s)", x.Table.Name.L) - case *PhysicalHashJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - if x.InnerChildIdx == 0 { - str = "RightHashJoin{" + strings.Join(children, "->") + "}" - } else { - str = "LeftHashJoin{" + strings.Join(children, "->") + "}" - } - for _, eq := range x.EqualConditions { - l := eq.GetArgs()[0].String() - r := eq.GetArgs()[1].String() - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *PhysicalMergeJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - id := "MergeJoin" - switch x.JoinType { - case SemiJoin: - id = "MergeSemiJoin" - case AntiSemiJoin: - id = "MergeAntiSemiJoin" - case LeftOuterSemiJoin: - id = "MergeLeftOuterSemiJoin" - case AntiLeftOuterSemiJoin: - id = "MergeAntiLeftOuterSemiJoin" - case LeftOuterJoin: - id = "MergeLeftOuterJoin" - case RightOuterJoin: - id = "MergeRightOuterJoin" - case InnerJoin: - id = "MergeInnerJoin" - } - str = id + "{" + strings.Join(children, "->") + "}" - for i := range x.LeftJoinKeys { - l := x.LeftJoinKeys[i].String() - r := x.RightJoinKeys[i].String() - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *LogicalApply, *PhysicalApply: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - str = "Apply{" + strings.Join(children, "->") + "}" - case *LogicalMaxOneRow, *PhysicalMaxOneRow: - str = "MaxOneRow" - case *LogicalLimit, *PhysicalLimit: - str = "Limit" - case *PhysicalLock, *LogicalLock: - str = "Lock" - case *ShowDDL: - str = "ShowDDL" - case *LogicalShow: - str = "Show" - if pl := in.(*LogicalShow); pl.Extractor != nil { - str = str + "(" + pl.Extractor.explainInfo() + ")" - } - case *PhysicalShow: - str = "Show" - if pl := in.(*PhysicalShow); pl.Extractor != nil { - str = str + "(" + pl.Extractor.explainInfo() + ")" - } - case *LogicalShowDDLJobs, *PhysicalShowDDLJobs: - str = "ShowDDLJobs" - case *LogicalSort, *PhysicalSort: - str = "Sort" - case *LogicalJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - str = "Join{" + strings.Join(children, "->") + "}" - idxs = idxs[:last] - for _, eq := range x.EqualConditions { - l := eq.GetArgs()[0].String() - r := eq.GetArgs()[1].String() - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *LogicalUnionAll, *PhysicalUnionAll, *LogicalPartitionUnionAll: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - name := "UnionAll" - if x.TP() == plancodec.TypePartitionUnion { - name = "PartitionUnionAll" - } - str = name + "{" + strings.Join(children, "->") + "}" - idxs = idxs[:last] - case *LogicalSequence: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - name := "Sequence" - str = name + "{" + strings.Join(children, ",") + "}" - idxs = idxs[:last] - case *DataSource: - if x.isPartition { - str = fmt.Sprintf("Partition(%d)", x.physicalTableID) - } else { - if x.TableAsName != nil && x.TableAsName.L != "" { - str = fmt.Sprintf("DataScan(%s)", x.TableAsName) - } else { - str = fmt.Sprintf("DataScan(%s)", x.tableInfo.Name) - } - } - case *LogicalSelection: - str = fmt.Sprintf("Sel(%s)", x.Conditions) - case *PhysicalSelection: - str = fmt.Sprintf("Sel(%s)", x.Conditions) - case *LogicalProjection, *PhysicalProjection: - str = "Projection" - case *LogicalTopN: - str = fmt.Sprintf("TopN(%v,%d,%d)", x.ByItems, x.Offset, x.Count) - case *PhysicalTopN: - str = fmt.Sprintf("TopN(%v,%d,%d)", x.ByItems, x.Offset, x.Count) - case *LogicalTableDual, *PhysicalTableDual: - str = "Dual" - case *PhysicalHashAgg: - str = "HashAgg" - case *PhysicalStreamAgg: - str = "StreamAgg" - case *LogicalAggregation: - str = "Aggr(" - for i, aggFunc := range x.AggFuncs { - str += aggFunc.String() - if i != len(x.AggFuncs)-1 { - str += "," - } - } - str += ")" - case *PhysicalTableReader: - str = fmt.Sprintf("TableReader(%s)", ToString(x.tablePlan)) - case *PhysicalIndexReader: - str = fmt.Sprintf("IndexReader(%s)", ToString(x.indexPlan)) - case *PhysicalIndexLookUpReader: - str = fmt.Sprintf("IndexLookUp(%s, %s)", ToString(x.indexPlan), ToString(x.tablePlan)) - case *PhysicalIndexMergeReader: - str = "IndexMergeReader(PartialPlans->[" - for i, paritalPlan := range x.partialPlans { - if i > 0 { - str += ", " - } - str += ToString(paritalPlan) - } - str += "], TablePlan->" + ToString(x.tablePlan) + ")" - case *PhysicalUnionScan: - str = fmt.Sprintf("UnionScan(%s)", x.Conditions) - case *PhysicalIndexJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - str = "IndexJoin{" + strings.Join(children, "->") + "}" - for i := range x.OuterJoinKeys { - l := x.OuterJoinKeys[i] - r := x.InnerJoinKeys[i] - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *PhysicalIndexMergeJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - str = "IndexMergeJoin{" + strings.Join(children, "->") + "}" - for i := range x.OuterJoinKeys { - l := x.OuterJoinKeys[i] - r := x.InnerJoinKeys[i] - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *PhysicalIndexHashJoin: - last := len(idxs) - 1 - idx := idxs[last] - children := strs[idx:] - strs = strs[:idx] - idxs = idxs[:last] - str = "IndexHashJoin{" + strings.Join(children, "->") + "}" - for i := range x.OuterJoinKeys { - l := x.OuterJoinKeys[i] - r := x.InnerJoinKeys[i] - str += fmt.Sprintf("(%s,%s)", l, r) - } - case *Analyze: - str = "Analyze{" - var children []string - for _, idx := range x.IdxTasks { - children = append(children, fmt.Sprintf("Index(%s)", idx.IndexInfo.Name.O)) - } - for _, col := range x.ColTasks { - var colNames []string - if col.HandleCols != nil { - colNames = append(colNames, col.HandleCols.String()) - } - for _, c := range col.ColsInfo { - colNames = append(colNames, c.Name.O) - } - children = append(children, fmt.Sprintf("Table(%s)", strings.Join(colNames, ", "))) - } - str = str + strings.Join(children, ",") + "}" - case *Update: - str = fmt.Sprintf("%s->Update", ToString(x.SelectPlan)) - case *Delete: - str = fmt.Sprintf("%s->Delete", ToString(x.SelectPlan)) - case *Insert: - str = "Insert" - if x.SelectPlan != nil { - str = fmt.Sprintf("%s->Insert", ToString(x.SelectPlan)) - } - case *LogicalWindow: - buffer := bytes.NewBufferString("") - formatWindowFuncDescs(buffer, x.WindowFuncDescs, x.schema) - str = fmt.Sprintf("Window(%s)", buffer.String()) - case *PhysicalWindow: - str = fmt.Sprintf("Window(%s)", x.ExplainInfo()) - case *PhysicalShuffle: - str = fmt.Sprintf("Partition(%s)", x.ExplainInfo()) - case *PhysicalShuffleReceiverStub: - str = fmt.Sprintf("PartitionReceiverStub(%s)", x.ExplainInfo()) - case *PointGetPlan: - str = "PointGet(" - if x.IndexInfo != nil { - str += fmt.Sprintf("Index(%s.%s)%v)", x.TblInfo.Name.L, x.IndexInfo.Name.L, x.IndexValues) - } else { - str += fmt.Sprintf("Handle(%s.%s)%v)", x.TblInfo.Name.L, x.TblInfo.GetPkName().L, x.Handle) - } - case *BatchPointGetPlan: - str = "BatchPointGet(" - if x.IndexInfo != nil { - str += fmt.Sprintf("Index(%s.%s)%v)", x.TblInfo.Name.L, x.IndexInfo.Name.L, x.IndexValues) - } else { - str += fmt.Sprintf("Handle(%s.%s)%v)", x.TblInfo.Name.L, x.TblInfo.GetPkName().L, x.Handles) - } - case *PhysicalExchangeReceiver: - str = "Recv(" - for _, task := range x.Tasks { - str += fmt.Sprintf("%d, ", task.ID) - } - str += ")" - case *PhysicalExchangeSender: - str = "Send(" - for _, task := range x.TargetTasks { - str += fmt.Sprintf("%d, ", task.ID) - } - for _, tasks := range x.TargetCTEReaderTasks { - str += "(" - for _, task := range tasks { - str += fmt.Sprintf("%d, ", task.ID) - } - str += ")" - } - str += ")" - case *PhysicalCTE: - str = "CTEReader(" - str += fmt.Sprintf("%v", x.CTE.IDForStorage) - str += ")" - default: - str = fmt.Sprintf("%T", in) - } - strs = append(strs, str) - return strs, idxs -} diff --git a/planner/core/stringer_test.go b/planner/core/stringer_test.go deleted file mode 100644 index 2dd305e4ab85b..0000000000000 --- a/planner/core/stringer_test.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package core_test - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/hint" - "github.com/stretchr/testify/require" -) - -func TestPlanStringer(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int, index idx(a))") - tests := []struct { - sql string - plan string - }{ - { - sql: "show columns from t like 'a'", - plan: "Show(field:[a])", - }, - { - sql: "show columns from t like 'a%'", - plan: "Show(field_pattern:[a%])", - }, - { - sql: "show columns from t where field = 'a'", - plan: "Show->Sel([eq(Column#13, a)])->Projection", - }, - { - sql: "desc t", - plan: "Show", - }, - { - sql: "desc t a", - plan: "Show(field:[a])", - }, - { - sql: "show tables in test like 't'", - plan: "Show(table:[t])", - }, - { - sql: "show tables in test like 'T'", - plan: "Show(table:[t])", - }, - { - sql: "show tables in test like 't%'", - plan: "Show(table_pattern:[t%])", - }, - { - sql: "show tables in test like '%T%'", - plan: "Show(table_pattern:[%t%])", - }, - { - sql: "show databases like 't'", - plan: "Show(database:[t])", - }, - { - sql: "show databases like 'T'", - plan: "Show(database:[t])", - }, - { - sql: "show databases like 't%'", - plan: "Show(database_pattern:[t%])", - }, - { - sql: "show databases like '%T%'", - plan: "Show(database_pattern:[%t%])", - }, - { - sql: "show table status in test like 'T%'", - plan: "Show(table_pattern:[t%])", - }, - { - sql: "show table status in test like '%T%'", - plan: "Show(table_pattern:[%t%])", - }, - { - sql: "show collation like 't'", - plan: "Show(collation:[t])", - }, - { - sql: "show collation like 'T'", - plan: "Show(collation:[t])", - }, - { - sql: "show collation like 't%'", - plan: "Show(collation_pattern:[t%])", - }, - { - sql: "show collation like '%T%'", - plan: "Show(collation_pattern:[%t%])", - }, - } - parser := parser.New() - for _, tt := range tests { - stmt, err := parser.ParseOneStmt(tt.sql, "", "") - require.NoError(t, err, "for %s", tt.sql) - ret := &core.PreprocessorReturn{} - builder, _ := core.NewPlanBuilder().Init(tk.Session(), ret.InfoSchema, &hint.BlockHintProcessor{}) - p, err := builder.Build(context.TODO(), stmt) - require.NoError(t, err, "for %s", tt.sql) - p, err = core.LogicalOptimize(context.TODO(), builder.GetOptFlag(), p.(core.LogicalPlan)) - require.NoError(t, err, "for %s", tt.sql) - require.Equal(t, tt.plan, core.ToString(p)) - } -} diff --git a/planner/core/task.go b/planner/core/task.go deleted file mode 100644 index a32567ce4f758..0000000000000 --- a/planner/core/task.go +++ /dev/null @@ -1,2747 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core - -import ( - "math" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/planner/core/internal/base" - "github.com/pingcap/tidb/planner/property" - "github.com/pingcap/tidb/planner/util" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/paging" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/size" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -var ( - _ task = &copTask{} - _ task = &rootTask{} - _ task = &mppTask{} -) - -// task is a new version of `PhysicalPlanInfo`. It stores cost information for a task. -// A task may be CopTask, RootTask, MPPTaskMeta or a ParallelTask. -type task interface { - count() float64 - copy() task - plan() PhysicalPlan - invalid() bool - convertToRootTask(ctx sessionctx.Context) *rootTask - MemoryUsage() int64 -} - -// copTask is a task that runs in a distributed kv store. -// TODO: In future, we should split copTask to indexTask and tableTask. -type copTask struct { - indexPlan PhysicalPlan - tablePlan PhysicalPlan - // indexPlanFinished means we have finished index plan. - indexPlanFinished bool - // keepOrder indicates if the plan scans data by order. - keepOrder bool - // needExtraProj means an extra prune is needed because - // in double read / index merge cases, they may output one more column for handle(row id). - needExtraProj bool - // originSchema is the target schema to be projected to when needExtraProj is true. - originSchema *expression.Schema - - extraHandleCol *expression.Column - commonHandleCols []*expression.Column - // tblColHists stores the original stats of DataSource, it is used to get - // average row width when computing network cost. - tblColHists *statistics.HistColl - // tblCols stores the original columns of DataSource before being pruned, it - // is used to compute average row width when computing scan cost. - tblCols []*expression.Column - - idxMergePartPlans []PhysicalPlan - idxMergeIsIntersection bool - idxMergeAccessMVIndex bool - - // rootTaskConds stores select conditions containing virtual columns. - // These conditions can't push to TiKV, so we have to add a selection for rootTask - rootTaskConds []expression.Expression - - // For table partition. - partitionInfo PartitionInfo - - // expectCnt is the expected row count of upper task, 0 for unlimited. - // It's used for deciding whether using paging distsql. - expectCnt uint64 -} - -func (t *copTask) invalid() bool { - return t.tablePlan == nil && t.indexPlan == nil -} - -func (t *rootTask) invalid() bool { - return t.p == nil -} - -func (t *copTask) count() float64 { - if t.indexPlanFinished { - return t.tablePlan.StatsInfo().RowCount - } - return t.indexPlan.StatsInfo().RowCount -} - -func (t *copTask) copy() task { - nt := *t - return &nt -} - -func (t *copTask) plan() PhysicalPlan { - if t.indexPlanFinished { - return t.tablePlan - } - return t.indexPlan -} - -func attachPlan2Task(p PhysicalPlan, t task) task { - switch v := t.(type) { - case *copTask: - if v.indexPlanFinished { - p.SetChildren(v.tablePlan) - v.tablePlan = p - } else { - p.SetChildren(v.indexPlan) - v.indexPlan = p - } - case *rootTask: - p.SetChildren(v.p) - v.p = p - case *mppTask: - p.SetChildren(v.p) - v.p = p - } - return t -} - -// finishIndexPlan means we no longer add plan to index plan, and compute the network cost for it. -func (t *copTask) finishIndexPlan() { - if t.indexPlanFinished { - return - } - t.indexPlanFinished = true - // index merge case is specially handled for now. - // We need a elegant way to solve the stats of index merge in this case. - if t.tablePlan != nil && t.indexPlan != nil { - ts := t.tablePlan.(*PhysicalTableScan) - originStats := ts.StatsInfo() - ts.SetStats(t.indexPlan.StatsInfo()) - if originStats != nil { - // keep the original stats version - ts.StatsInfo().StatsVersion = originStats.StatsVersion - } - } -} - -func (t *copTask) getStoreType() kv.StoreType { - if t.tablePlan == nil { - return kv.TiKV - } - tp := t.tablePlan - for len(tp.Children()) > 0 { - if len(tp.Children()) > 1 { - return kv.TiFlash - } - tp = tp.Children()[0] - } - if ts, ok := tp.(*PhysicalTableScan); ok { - return ts.StoreType - } - return kv.TiKV -} - -// MemoryUsage return the memory usage of copTask -func (t *copTask) MemoryUsage() (sum int64) { - if t == nil { - return - } - - sum = size.SizeOfInterface*(2+int64(cap(t.idxMergePartPlans)+cap(t.rootTaskConds))) + size.SizeOfBool*3 + size.SizeOfUint64 + - size.SizeOfPointer*(3+int64(cap(t.commonHandleCols)+cap(t.tblCols))) + size.SizeOfSlice*4 + t.partitionInfo.MemoryUsage() - if t.indexPlan != nil { - sum += t.indexPlan.MemoryUsage() - } - if t.tablePlan != nil { - sum += t.tablePlan.MemoryUsage() - } - if t.originSchema != nil { - sum += t.originSchema.MemoryUsage() - } - if t.extraHandleCol != nil { - sum += t.extraHandleCol.MemoryUsage() - } - - for _, col := range t.commonHandleCols { - sum += col.MemoryUsage() - } - for _, col := range t.tblCols { - sum += col.MemoryUsage() - } - for _, p := range t.idxMergePartPlans { - sum += p.MemoryUsage() - } - for _, expr := range t.rootTaskConds { - sum += expr.MemoryUsage() - } - return -} - -func (p *basePhysicalPlan) attach2Task(tasks ...task) task { - t := tasks[0].convertToRootTask(p.SCtx()) - return attachPlan2Task(p.self, t) -} - -func (p *PhysicalUnionScan) attach2Task(tasks ...task) task { - // We need to pull the projection under unionScan upon unionScan. - // Since the projection only prunes columns, it's ok the put it upon unionScan. - if sel, ok := tasks[0].plan().(*PhysicalSelection); ok { - if pj, ok := sel.children[0].(*PhysicalProjection); ok { - // Convert unionScan->selection->projection to projection->unionScan->selection. - sel.SetChildren(pj.children...) - p.SetChildren(sel) - p.SetStats(tasks[0].plan().StatsInfo()) - rt, _ := tasks[0].(*rootTask) - rt.p = p - pj.SetChildren(p) - return pj.attach2Task(tasks...) - } - } - if pj, ok := tasks[0].plan().(*PhysicalProjection); ok { - // Convert unionScan->projection to projection->unionScan, because unionScan can't handle projection as its children. - p.SetChildren(pj.children...) - p.SetStats(tasks[0].plan().StatsInfo()) - rt, _ := tasks[0].(*rootTask) - rt.p = pj.children[0] - pj.SetChildren(p) - return pj.attach2Task(p.basePhysicalPlan.attach2Task(tasks...)) - } - p.SetStats(tasks[0].plan().StatsInfo()) - return p.basePhysicalPlan.attach2Task(tasks...) -} - -func (p *PhysicalApply) attach2Task(tasks ...task) task { - lTask := tasks[0].convertToRootTask(p.SCtx()) - rTask := tasks[1].convertToRootTask(p.SCtx()) - p.SetChildren(lTask.plan(), rTask.plan()) - p.schema = BuildPhysicalJoinSchema(p.JoinType, p) - t := &rootTask{ - p: p, - } - return t -} - -func (p *PhysicalIndexMergeJoin) attach2Task(tasks ...task) task { - innerTask := p.innerTask - outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) - if p.InnerChildIdx == 1 { - p.SetChildren(outerTask.plan(), innerTask.plan()) - } else { - p.SetChildren(innerTask.plan(), outerTask.plan()) - } - t := &rootTask{ - p: p, - } - return t -} - -func (p *PhysicalIndexHashJoin) attach2Task(tasks ...task) task { - innerTask := p.innerTask - outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) - if p.InnerChildIdx == 1 { - p.SetChildren(outerTask.plan(), innerTask.plan()) - } else { - p.SetChildren(innerTask.plan(), outerTask.plan()) - } - t := &rootTask{ - p: p, - } - return t -} - -func (p *PhysicalIndexJoin) attach2Task(tasks ...task) task { - innerTask := p.innerTask - outerTask := tasks[1-p.InnerChildIdx].convertToRootTask(p.SCtx()) - if p.InnerChildIdx == 1 { - p.SetChildren(outerTask.plan(), innerTask.plan()) - } else { - p.SetChildren(innerTask.plan(), outerTask.plan()) - } - t := &rootTask{ - p: p, - } - return t -} - -// RowSize for cost model ver2 is simplified, always use this function to calculate row size. -func getAvgRowSize(stats *property.StatsInfo, cols []*expression.Column) (size float64) { - if stats.HistColl != nil { - size = cardinality.GetAvgRowSizeListInDisk(stats.HistColl, cols) - } else { - // Estimate using just the type info. - for _, col := range cols { - size += float64(chunk.EstimateTypeWidth(col.GetType())) - } - } - return -} - -func (p *PhysicalHashJoin) attach2Task(tasks ...task) task { - if p.storeTp == kv.TiFlash { - return p.attach2TaskForTiFlash(tasks...) - } - lTask := tasks[0].convertToRootTask(p.SCtx()) - rTask := tasks[1].convertToRootTask(p.SCtx()) - p.SetChildren(lTask.plan(), rTask.plan()) - task := &rootTask{ - p: p, - } - return task -} - -// TiDB only require that the types fall into the same catalog but TiFlash require the type to be exactly the same, so -// need to check if the conversion is a must -func needConvert(tp *types.FieldType, rtp *types.FieldType) bool { - // all the string type are mapped to the same type in TiFlash, so - // do not need convert for string types - if types.IsString(tp.GetType()) && types.IsString(rtp.GetType()) { - return false - } - if tp.GetType() != rtp.GetType() { - return true - } - if tp.GetType() != mysql.TypeNewDecimal { - return false - } - if tp.GetDecimal() != rtp.GetDecimal() { - return true - } - // for decimal type, TiFlash have 4 different impl based on the required precision - if tp.GetFlen() >= 0 && tp.GetFlen() <= 9 && rtp.GetFlen() >= 0 && rtp.GetFlen() <= 9 { - return false - } - if tp.GetFlen() > 9 && tp.GetFlen() <= 18 && rtp.GetFlen() > 9 && rtp.GetFlen() <= 18 { - return false - } - if tp.GetFlen() > 18 && tp.GetFlen() <= 38 && rtp.GetFlen() > 18 && rtp.GetFlen() <= 38 { - return false - } - if tp.GetFlen() > 38 && tp.GetFlen() <= 65 && rtp.GetFlen() > 38 && rtp.GetFlen() <= 65 { - return false - } - return true -} - -func negotiateCommonType(lType, rType *types.FieldType) (*types.FieldType, bool, bool) { - commonType := types.AggFieldType([]*types.FieldType{lType, rType}) - if commonType.GetType() == mysql.TypeNewDecimal { - lExtend := 0 - rExtend := 0 - cDec := rType.GetDecimal() - if lType.GetDecimal() < rType.GetDecimal() { - lExtend = rType.GetDecimal() - lType.GetDecimal() - } else if lType.GetDecimal() > rType.GetDecimal() { - rExtend = lType.GetDecimal() - rType.GetDecimal() - cDec = lType.GetDecimal() - } - lLen, rLen := lType.GetFlen()+lExtend, rType.GetFlen()+rExtend - cLen := mathutil.Max(lLen, rLen) - commonType.SetDecimalUnderLimit(cDec) - commonType.SetFlenUnderLimit(cLen) - } else if needConvert(lType, commonType) || needConvert(rType, commonType) { - if mysql.IsIntegerType(commonType.GetType()) { - // If the target type is int, both TiFlash and Mysql only support cast to Int64 - // so we need to promote the type to Int64 - commonType.SetType(mysql.TypeLonglong) - commonType.SetFlen(mysql.MaxIntWidth) - } - } - return commonType, needConvert(lType, commonType), needConvert(rType, commonType) -} - -func getProj(ctx sessionctx.Context, p PhysicalPlan) *PhysicalProjection { - proj := PhysicalProjection{ - Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)), - }.Init(ctx, p.StatsInfo(), p.SelectBlockOffset()) - for _, col := range p.Schema().Columns { - proj.Exprs = append(proj.Exprs, col) - } - proj.SetSchema(p.Schema().Clone()) - proj.SetChildren(p) - return proj -} - -func appendExpr(p *PhysicalProjection, expr expression.Expression) *expression.Column { - p.Exprs = append(p.Exprs, expr) - - col := &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: expr.GetType(), - } - col.SetCoercibility(expr.Coercibility()) - p.schema.Append(col) - return col -} - -// TiFlash join require that partition key has exactly the same type, while TiDB only guarantee the partition key is the same catalog, -// so if the partition key type is not exactly the same, we need add a projection below the join or exchanger if exists. -func (p *PhysicalHashJoin) convertPartitionKeysIfNeed(lTask, rTask *mppTask) (*mppTask, *mppTask) { - lp := lTask.p - if _, ok := lp.(*PhysicalExchangeReceiver); ok { - lp = lp.Children()[0].Children()[0] - } - rp := rTask.p - if _, ok := rp.(*PhysicalExchangeReceiver); ok { - rp = rp.Children()[0].Children()[0] - } - // to mark if any partition key needs to convert - lMask := make([]bool, len(lTask.hashCols)) - rMask := make([]bool, len(rTask.hashCols)) - cTypes := make([]*types.FieldType, len(lTask.hashCols)) - lChanged := false - rChanged := false - for i := range lTask.hashCols { - lKey := lTask.hashCols[i] - rKey := rTask.hashCols[i] - cType, lConvert, rConvert := negotiateCommonType(lKey.Col.RetType, rKey.Col.RetType) - if lConvert { - lMask[i] = true - cTypes[i] = cType - lChanged = true - } - if rConvert { - rMask[i] = true - cTypes[i] = cType - rChanged = true - } - } - if !lChanged && !rChanged { - return lTask, rTask - } - var lProj, rProj *PhysicalProjection - if lChanged { - lProj = getProj(p.SCtx(), lp) - lp = lProj - } - if rChanged { - rProj = getProj(p.SCtx(), rp) - rp = rProj - } - - lPartKeys := make([]*property.MPPPartitionColumn, 0, len(rTask.hashCols)) - rPartKeys := make([]*property.MPPPartitionColumn, 0, len(lTask.hashCols)) - for i := range lTask.hashCols { - lKey := lTask.hashCols[i] - rKey := rTask.hashCols[i] - if lMask[i] { - cType := cTypes[i].Clone() - cType.SetFlag(lKey.Col.RetType.GetFlag()) - lCast := expression.BuildCastFunction(p.SCtx(), lKey.Col, cType) - lKey = &property.MPPPartitionColumn{Col: appendExpr(lProj, lCast), CollateID: lKey.CollateID} - } - if rMask[i] { - cType := cTypes[i].Clone() - cType.SetFlag(rKey.Col.RetType.GetFlag()) - rCast := expression.BuildCastFunction(p.SCtx(), rKey.Col, cType) - rKey = &property.MPPPartitionColumn{Col: appendExpr(rProj, rCast), CollateID: rKey.CollateID} - } - lPartKeys = append(lPartKeys, lKey) - rPartKeys = append(rPartKeys, rKey) - } - // if left or right child changes, we need to add enforcer. - if lChanged { - nlTask := lTask.copy().(*mppTask) - nlTask.p = lProj - nlTask = nlTask.enforceExchanger(&property.PhysicalProperty{ - TaskTp: property.MppTaskType, - MPPPartitionTp: property.HashType, - MPPPartitionCols: lPartKeys, - }) - lTask = nlTask - } - if rChanged { - nrTask := rTask.copy().(*mppTask) - nrTask.p = rProj - nrTask = nrTask.enforceExchanger(&property.PhysicalProperty{ - TaskTp: property.MppTaskType, - MPPPartitionTp: property.HashType, - MPPPartitionCols: rPartKeys, - }) - rTask = nrTask - } - return lTask, rTask -} - -func (p *PhysicalHashJoin) attach2TaskForMpp(tasks ...task) task { - lTask, lok := tasks[0].(*mppTask) - rTask, rok := tasks[1].(*mppTask) - if !lok || !rok { - return invalidTask - } - if p.mppShuffleJoin { - // protection check is case of some bugs - if len(lTask.hashCols) != len(rTask.hashCols) || len(lTask.hashCols) == 0 { - return invalidTask - } - lTask, rTask = p.convertPartitionKeysIfNeed(lTask, rTask) - } - p.SetChildren(lTask.plan(), rTask.plan()) - p.schema = BuildPhysicalJoinSchema(p.JoinType, p) - - // outer task is the task that will pass its MPPPartitionType to the join result - // for broadcast inner join, it should be the non-broadcast side, since broadcast side is always the build side, so - // just use the probe side is ok. - // for hash inner join, both side is ok, by default, we use the probe side - // for outer join, it should always be the outer side of the join - // for semi join, it should be the left side(the same as left out join) - outerTaskIndex := 1 - p.InnerChildIdx - if p.JoinType != InnerJoin { - if p.JoinType == RightOuterJoin { - outerTaskIndex = 1 - } else { - outerTaskIndex = 0 - } - } - // can not use the task from tasks because it maybe updated. - outerTask := lTask - if outerTaskIndex == 1 { - outerTask = rTask - } - task := &mppTask{ - p: p, - partTp: outerTask.partTp, - hashCols: outerTask.hashCols, - } - return task -} - -func (p *PhysicalHashJoin) attach2TaskForTiFlash(tasks ...task) task { - lTask, lok := tasks[0].(*copTask) - rTask, rok := tasks[1].(*copTask) - if !lok || !rok { - return p.attach2TaskForMpp(tasks...) - } - p.SetChildren(lTask.plan(), rTask.plan()) - p.schema = BuildPhysicalJoinSchema(p.JoinType, p) - if !lTask.indexPlanFinished { - lTask.finishIndexPlan() - } - if !rTask.indexPlanFinished { - rTask.finishIndexPlan() - } - - task := &copTask{ - tblColHists: rTask.tblColHists, - indexPlanFinished: true, - tablePlan: p, - } - return task -} - -func (p *PhysicalMergeJoin) attach2Task(tasks ...task) task { - lTask := tasks[0].convertToRootTask(p.SCtx()) - rTask := tasks[1].convertToRootTask(p.SCtx()) - p.SetChildren(lTask.plan(), rTask.plan()) - t := &rootTask{ - p: p, - } - return t -} - -func buildIndexLookUpTask(ctx sessionctx.Context, t *copTask) *rootTask { - newTask := &rootTask{} - p := PhysicalIndexLookUpReader{ - tablePlan: t.tablePlan, - indexPlan: t.indexPlan, - ExtraHandleCol: t.extraHandleCol, - CommonHandleCols: t.commonHandleCols, - expectedCnt: t.expectCnt, - keepOrder: t.keepOrder, - }.Init(ctx, t.tablePlan.SelectBlockOffset()) - p.PartitionInfo = t.partitionInfo - setTableScanToTableRowIDScan(p.tablePlan) - p.SetStats(t.tablePlan.StatsInfo()) - // Do not inject the extra Projection even if t.needExtraProj is set, or the schema between the phase-1 agg and - // the final agg would be broken. Please reference comments for the similar logic in - // (*copTask).convertToRootTaskImpl() for the PhysicalTableReader case. - // We need to refactor these logics. - aggPushedDown := false - switch p.tablePlan.(type) { - case *PhysicalHashAgg, *PhysicalStreamAgg: - aggPushedDown = true - } - - if t.needExtraProj && !aggPushedDown { - schema := t.originSchema - proj := PhysicalProjection{Exprs: expression.Column2Exprs(schema.Columns)}.Init(ctx, p.StatsInfo(), t.tablePlan.SelectBlockOffset(), nil) - proj.SetSchema(schema) - proj.SetChildren(p) - newTask.p = proj - } else { - newTask.p = p - } - return newTask -} - -func extractRows(p PhysicalPlan) float64 { - f := float64(0) - for _, c := range p.Children() { - if len(c.Children()) != 0 { - f += extractRows(c) - } else { - f += c.StatsInfo().RowCount - } - } - return f -} - -// calcPagingCost calculates the cost for paging processing which may increase the seekCnt and reduce scanned rows. -func calcPagingCost(ctx sessionctx.Context, indexPlan PhysicalPlan, expectCnt uint64) float64 { - sessVars := ctx.GetSessionVars() - indexRows := indexPlan.StatsCount() - sourceRows := extractRows(indexPlan) - // with paging, the scanned rows is always less than or equal to source rows. - if uint64(sourceRows) < expectCnt { - expectCnt = uint64(sourceRows) - } - seekCnt := paging.CalculateSeekCnt(expectCnt) - indexSelectivity := float64(1) - if sourceRows > indexRows { - indexSelectivity = indexRows / sourceRows - } - pagingCst := seekCnt*sessVars.GetSeekFactor(nil) + float64(expectCnt)*sessVars.GetCPUFactor() - pagingCst *= indexSelectivity - - // we want the diff between idxCst and pagingCst here, - // however, the idxCst does not contain seekFactor, so a seekFactor needs to be removed - return math.Max(pagingCst-sessVars.GetSeekFactor(nil), 0) -} - -func (t *rootTask) convertToRootTask(_ sessionctx.Context) *rootTask { - return t.copy().(*rootTask) -} - -func (t *copTask) convertToRootTask(ctx sessionctx.Context) *rootTask { - // copy one to avoid changing itself. - return t.copy().(*copTask).convertToRootTaskImpl(ctx) -} - -func (t *copTask) convertToRootTaskImpl(ctx sessionctx.Context) *rootTask { - // copTasks are run in parallel, to make the estimated cost closer to execution time, we amortize - // the cost to cop iterator workers. According to `CopClient::Send`, the concurrency - // is Min(DistSQLScanConcurrency, numRegionsInvolvedInScan), since we cannot infer - // the number of regions involved, we simply use DistSQLScanConcurrency. - t.finishIndexPlan() - // Network cost of transferring rows of table scan to TiDB. - if t.tablePlan != nil { - tp := t.tablePlan - for len(tp.Children()) > 0 { - if len(tp.Children()) == 1 { - tp = tp.Children()[0] - } else { - join := tp.(*PhysicalHashJoin) - tp = join.children[1-join.InnerChildIdx] - } - } - ts := tp.(*PhysicalTableScan) - prevColumnLen := len(ts.Columns) - prevSchema := ts.schema.Clone() - ts.Columns = ExpandVirtualColumn(ts.Columns, ts.schema, ts.Table.Columns) - if !t.needExtraProj && len(ts.Columns) > prevColumnLen { - // Add an projection to make sure not to output extract columns. - t.needExtraProj = true - t.originSchema = prevSchema - } - } - newTask := &rootTask{} - if t.idxMergePartPlans != nil { - p := PhysicalIndexMergeReader{ - partialPlans: t.idxMergePartPlans, - tablePlan: t.tablePlan, - IsIntersectionType: t.idxMergeIsIntersection, - AccessMVIndex: t.idxMergeAccessMVIndex, - KeepOrder: t.keepOrder, - }.Init(ctx, t.idxMergePartPlans[0].SelectBlockOffset()) - p.PartitionInfo = t.partitionInfo - setTableScanToTableRowIDScan(p.tablePlan) - newTask.p = p - t.handleRootTaskConds(ctx, newTask) - if t.needExtraProj { - schema := t.originSchema - proj := PhysicalProjection{Exprs: expression.Column2Exprs(schema.Columns)}.Init(ctx, p.StatsInfo(), t.idxMergePartPlans[0].SelectBlockOffset(), nil) - proj.SetSchema(schema) - proj.SetChildren(p) - newTask.p = proj - } - return newTask - } - if t.indexPlan != nil && t.tablePlan != nil { - newTask = buildIndexLookUpTask(ctx, t) - } else if t.indexPlan != nil { - p := PhysicalIndexReader{indexPlan: t.indexPlan}.Init(ctx, t.indexPlan.SelectBlockOffset()) - p.PartitionInfo = t.partitionInfo - p.SetStats(t.indexPlan.StatsInfo()) - newTask.p = p - } else { - tp := t.tablePlan - for len(tp.Children()) > 0 { - if len(tp.Children()) == 1 { - tp = tp.Children()[0] - } else { - join := tp.(*PhysicalHashJoin) - tp = join.children[1-join.InnerChildIdx] - } - } - ts := tp.(*PhysicalTableScan) - p := PhysicalTableReader{ - tablePlan: t.tablePlan, - StoreType: ts.StoreType, - IsCommonHandle: ts.Table.IsCommonHandle, - }.Init(ctx, t.tablePlan.SelectBlockOffset()) - p.PartitionInfo = t.partitionInfo - p.SetStats(t.tablePlan.StatsInfo()) - - // If agg was pushed down in attach2Task(), the partial agg was placed on the top of tablePlan, the final agg was - // placed above the PhysicalTableReader, and the schema should have been set correctly for them, the schema of - // partial agg contains the columns needed by the final agg. - // If we add the projection here, the projection will be between the final agg and the partial agg, then the - // schema will be broken, the final agg will fail to find needed columns in ResolveIndices(). - // Besides, the agg would only be pushed down if it doesn't contain virtual columns, so virtual column should not be affected. - aggPushedDown := false - switch p.tablePlan.(type) { - case *PhysicalHashAgg, *PhysicalStreamAgg: - aggPushedDown = true - } - - if t.needExtraProj && !aggPushedDown { - proj := PhysicalProjection{Exprs: expression.Column2Exprs(t.originSchema.Columns)}.Init(ts.SCtx(), ts.StatsInfo(), ts.SelectBlockOffset(), nil) - proj.SetSchema(t.originSchema) - proj.SetChildren(p) - newTask.p = proj - } else { - newTask.p = p - } - } - - t.handleRootTaskConds(ctx, newTask) - return newTask -} - -func (t *copTask) handleRootTaskConds(ctx sessionctx.Context, newTask *rootTask) { - if len(t.rootTaskConds) > 0 { - selectivity, _, err := cardinality.Selectivity(ctx, t.tblColHists, t.rootTaskConds, nil) - if err != nil { - logutil.BgLogger().Debug("calculate selectivity failed, use selection factor", zap.Error(err)) - selectivity = SelectionFactor - } - sel := PhysicalSelection{Conditions: t.rootTaskConds}.Init(ctx, newTask.p.StatsInfo().Scale(selectivity), newTask.p.SelectBlockOffset()) - sel.fromDataSource = true - sel.SetChildren(newTask.p) - newTask.p = sel - } -} - -// setTableScanToTableRowIDScan is to update the isChildOfIndexLookUp attribute of PhysicalTableScan child -func setTableScanToTableRowIDScan(p PhysicalPlan) { - if ts, ok := p.(*PhysicalTableScan); ok { - ts.SetIsChildOfIndexLookUp(true) - } else { - for _, child := range p.Children() { - setTableScanToTableRowIDScan(child) - } - } -} - -// rootTask is the final sink node of a plan graph. It should be a single goroutine on tidb. -type rootTask struct { - p PhysicalPlan - isEmpty bool // isEmpty indicates if this task contains a dual table and returns empty data. - // TODO: The flag 'isEmpty' is only checked by Projection and UnionAll. We should support more cases in the future. -} - -func (t *rootTask) copy() task { - return &rootTask{ - p: t.p, - } -} - -func (t *rootTask) count() float64 { - return t.p.StatsInfo().RowCount -} - -func (t *rootTask) plan() PhysicalPlan { - return t.p -} - -// MemoryUsage return the memory usage of rootTask -func (t *rootTask) MemoryUsage() (sum int64) { - if t == nil { - return - } - - sum = size.SizeOfInterface + size.SizeOfBool - if t.p != nil { - sum += t.p.MemoryUsage() - } - return sum -} - -// attach2Task attach limit to different cases. -// For Normal Index Lookup -// 1: attach the limit to table side or index side of normal index lookup cop task. (normal case, old code, no more -// explanation here) -// -// For Index Merge: -// 2: attach the limit to **table** side for index merge intersection case, cause intersection will invalidate the -// fetched limit+offset rows from each partial index plan, you can not decide how many you want in advance for partial -// index path, actually. After we sink limit to table side, we still need an upper root limit to control the real limit -// count admission. -// -// 3: attach the limit to **index** side for index merge union case, because each index plan will output the fetched -// limit+offset (* N path) rows, you still need an embedded pushedLimit inside index merge reader to cut it down. -// -// 4: attach the limit to the TOP of root index merge operator if there is some root condition exists for index merge -// intersection/union case. -func (p *PhysicalLimit) attach2Task(tasks ...task) task { - t := tasks[0].copy() - newPartitionBy := make([]property.SortItem, 0, len(p.GetPartitionBy())) - for _, expr := range p.GetPartitionBy() { - newPartitionBy = append(newPartitionBy, expr.Clone()) - } - - sunk := false - if cop, ok := t.(*copTask); ok { - suspendLimitAboveTablePlan := func() { - newCount := p.Offset + p.Count - childProfile := cop.tablePlan.StatsInfo() - // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. - stats := deriveLimitStats(childProfile, float64(newCount)) - pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) - pushedDownLimit.SetChildren(cop.tablePlan) - cop.tablePlan = pushedDownLimit - // Don't use clone() so that Limit and its children share the same schema. Otherwise, the virtual generated column may not be resolved right. - pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) - t = cop.convertToRootTask(p.SCtx()) - } - if len(cop.idxMergePartPlans) == 0 { - // For double read which requires order being kept, the limit cannot be pushed down to the table side, - // because handles would be reordered before being sent to table scan. - if (!cop.keepOrder || !cop.indexPlanFinished || cop.indexPlan == nil) && len(cop.rootTaskConds) == 0 { - // When limit is pushed down, we should remove its offset. - newCount := p.Offset + p.Count - childProfile := cop.plan().StatsInfo() - // Strictly speaking, for the row count of stats, we should multiply newCount with "regionNum", - // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. - stats := deriveLimitStats(childProfile, float64(newCount)) - pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) - cop = attachPlan2Task(pushedDownLimit, cop).(*copTask) - // Don't use clone() so that Limit and its children share the same schema. Otherwise the virtual generated column may not be resolved right. - pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) - } - t = cop.convertToRootTask(p.SCtx()) - sunk = p.sinkIntoIndexLookUp(t) - } else if !cop.idxMergeIsIntersection { - // We only support push part of the order prop down to index merge build case. - if len(cop.rootTaskConds) == 0 { - if cop.indexPlanFinished { - // when the index plan is finished, sink the limit to the index merge table side. - suspendLimitAboveTablePlan() - } else { - // cop.indexPlanFinished = false indicates the table side is a pure table-scan, sink the limit to the index merge index side. - newCount := p.Offset + p.Count - limitChildren := make([]PhysicalPlan, 0, len(cop.idxMergePartPlans)) - for _, partialScan := range cop.idxMergePartPlans { - childProfile := partialScan.StatsInfo() - stats := deriveLimitStats(childProfile, float64(newCount)) - pushedDownLimit := PhysicalLimit{PartitionBy: newPartitionBy, Count: newCount}.Init(p.SCtx(), stats, p.SelectBlockOffset()) - pushedDownLimit.SetChildren(partialScan) - pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) - limitChildren = append(limitChildren, pushedDownLimit) - } - cop.idxMergePartPlans = limitChildren - t = cop.convertToRootTask(p.SCtx()) - sunk = p.sinkIntoIndexMerge(t) - } - } else { - // when there are some root conditions, just sink the limit upon the index merge reader. - t = cop.convertToRootTask(p.SCtx()) - sunk = p.sinkIntoIndexMerge(t) - } - } else if cop.idxMergeIsIntersection { - // In the index merge with intersection case, only the limit can be pushed down to the index merge table side. - // Note Difference: - // IndexMerge.PushedLimit is applied before table scan fetching, limiting the indexPartialPlan rows returned (it maybe ordered if orderBy items not empty) - // TableProbeSide sink limit is applied on the top of table plan, which will quickly shut down the both fetch-back and read-back process. - if len(cop.rootTaskConds) == 0 { - if cop.indexPlanFinished { - // indicates the table side is not a pure table-scan, so we could only append the limit upon the table plan. - suspendLimitAboveTablePlan() - } else { - t = cop.convertToRootTask(p.SCtx()) - sunk = p.sinkIntoIndexMerge(t) - } - } else { - // otherwise, suspend the limit out of index merge reader. - t = cop.convertToRootTask(p.SCtx()) - sunk = p.sinkIntoIndexMerge(t) - } - } else { - // Whatever the remained case is, we directly convert to it to root task. - t = cop.convertToRootTask(p.SCtx()) - } - } else if mpp, ok := t.(*mppTask); ok { - newCount := p.Offset + p.Count - childProfile := mpp.plan().StatsInfo() - stats := deriveLimitStats(childProfile, float64(newCount)) - pushedDownLimit := PhysicalLimit{Count: newCount, PartitionBy: newPartitionBy}.Init(p.SCtx(), stats, p.SelectBlockOffset()) - mpp = attachPlan2Task(pushedDownLimit, mpp).(*mppTask) - pushedDownLimit.SetSchema(pushedDownLimit.children[0].Schema()) - t = mpp.convertToRootTask(p.SCtx()) - } - if sunk { - return t - } - // Skip limit with partition on the root. This is a derived topN and window function - // will take care of the filter. - if len(p.GetPartitionBy()) > 0 { - return t - } - return attachPlan2Task(p, t) -} - -func (p *PhysicalLimit) sinkIntoIndexLookUp(t task) bool { - root := t.(*rootTask) - reader, isDoubleRead := root.p.(*PhysicalIndexLookUpReader) - proj, isProj := root.p.(*PhysicalProjection) - if !isDoubleRead && !isProj { - return false - } - if isProj { - reader, isDoubleRead = proj.Children()[0].(*PhysicalIndexLookUpReader) - if !isDoubleRead { - return false - } - } - - // We can sink Limit into IndexLookUpReader only if tablePlan contains no Selection. - ts, isTableScan := reader.tablePlan.(*PhysicalTableScan) - if !isTableScan { - return false - } - - // If this happens, some Projection Operator must be inlined into this Limit. (issues/14428) - // For example, if the original plan is `IndexLookUp(col1, col2) -> Limit(col1, col2) -> Project(col1)`, - // then after inlining the Project, it will be `IndexLookUp(col1, col2) -> Limit(col1)` here. - // If the Limit is sunk into the IndexLookUp, the IndexLookUp's schema needs to be updated as well, - // So we add an extra projection to solve the problem. - if p.Schema().Len() != reader.Schema().Len() { - extraProj := PhysicalProjection{ - Exprs: expression.Column2Exprs(p.schema.Columns), - }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), nil) - extraProj.SetSchema(p.schema) - // If the root.p is already a Projection. We left the optimization for the later Projection Elimination. - extraProj.SetChildren(root.p) - root.p = extraProj - } - - reader.PushedLimit = &PushedDownLimit{ - Offset: p.Offset, - Count: p.Count, - } - originStats := ts.StatsInfo() - ts.SetStats(p.StatsInfo()) - if originStats != nil { - // keep the original stats version - ts.StatsInfo().StatsVersion = originStats.StatsVersion - } - reader.SetStats(p.StatsInfo()) - if isProj { - proj.SetStats(p.StatsInfo()) - } - return true -} - -func (p *PhysicalLimit) sinkIntoIndexMerge(t task) bool { - root := t.(*rootTask) - imReader, isIm := root.p.(*PhysicalIndexMergeReader) - proj, isProj := root.p.(*PhysicalProjection) - if !isIm && !isProj { - return false - } - if isProj { - imReader, isIm = proj.Children()[0].(*PhysicalIndexMergeReader) - if !isIm { - return false - } - } - ts, ok := imReader.tablePlan.(*PhysicalTableScan) - if !ok { - return false - } - imReader.PushedLimit = &PushedDownLimit{ - Count: p.Count, - Offset: p.Offset, - } - // since ts.statsInfo.rowcount may dramatically smaller than limit.statsInfo. - // like limit: rowcount=1 - // ts: rowcount=0.0025 - originStats := ts.StatsInfo() - if originStats != nil { - // keep the original stats version - ts.StatsInfo().StatsVersion = originStats.StatsVersion - if originStats.RowCount < p.StatsInfo().RowCount { - ts.StatsInfo().RowCount = originStats.RowCount - } - } - needProj := p.schema.Len() != root.p.Schema().Len() - if !needProj { - for i := 0; i < p.schema.Len(); i++ { - if !p.schema.Columns[i].Equal(nil, root.p.Schema().Columns[i]) { - needProj = true - break - } - } - } - if needProj { - extraProj := PhysicalProjection{ - Exprs: expression.Column2Exprs(p.schema.Columns), - }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), nil) - extraProj.SetSchema(p.schema) - // If the root.p is already a Projection. We left the optimization for the later Projection Elimination. - extraProj.SetChildren(root.p) - root.p = extraProj - } - return true -} - -func (p *PhysicalSort) attach2Task(tasks ...task) task { - t := tasks[0].copy() - t = attachPlan2Task(p, t) - return t -} - -func (p *NominalSort) attach2Task(tasks ...task) task { - if p.OnlyColumn { - return tasks[0] - } - t := tasks[0].copy() - t = attachPlan2Task(p, t) - return t -} - -func (p *PhysicalTopN) getPushedDownTopN(childPlan PhysicalPlan) *PhysicalTopN { - newByItems := make([]*util.ByItems, 0, len(p.ByItems)) - for _, expr := range p.ByItems { - newByItems = append(newByItems, expr.Clone()) - } - newPartitionBy := make([]property.SortItem, 0, len(p.GetPartitionBy())) - for _, expr := range p.GetPartitionBy() { - newPartitionBy = append(newPartitionBy, expr.Clone()) - } - newCount := p.Offset + p.Count - childProfile := childPlan.StatsInfo() - // Strictly speaking, for the row count of pushed down TopN, we should multiply newCount with "regionNum", - // but "regionNum" is unknown since the copTask can be a double read, so we ignore it now. - stats := deriveLimitStats(childProfile, float64(newCount)) - topN := PhysicalTopN{ - ByItems: newByItems, - PartitionBy: newPartitionBy, - Count: newCount, - }.Init(p.SCtx(), stats, p.SelectBlockOffset(), p.GetChildReqProps(0)) - topN.SetChildren(childPlan) - return topN -} - -// canPushToIndexPlan checks if this TopN can be pushed to the index side of copTask. -// It can be pushed to the index side when all columns used by ByItems are available from the index side and there's no prefix index column. -func (*PhysicalTopN) canPushToIndexPlan(indexPlan PhysicalPlan, byItemCols []*expression.Column) bool { - // If we call canPushToIndexPlan and there's no index plan, we should go into the index merge case. - // Index merge case is specially handled for now. So we directly return false here. - // So we directly return false. - if indexPlan == nil { - return false - } - schema := indexPlan.Schema() - for _, col := range byItemCols { - pos := schema.ColumnIndex(col) - if pos == -1 { - return false - } - if schema.Columns[pos].IsPrefix { - return false - } - } - return true -} - -// canExpressionConvertedToPB checks whether each of the the expression in TopN can be converted to pb. -func (p *PhysicalTopN) canExpressionConvertedToPB(storeTp kv.StoreType) bool { - exprs := make([]expression.Expression, 0, len(p.ByItems)) - for _, item := range p.ByItems { - exprs = append(exprs, item.Expr) - } - return expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, exprs, p.SCtx().GetClient(), storeTp) -} - -// containVirtualColumn checks whether TopN.ByItems contains virtual generated columns. -func (p *PhysicalTopN) containVirtualColumn(tCols []*expression.Column) bool { - tColSet := make(map[int64]struct{}, len(tCols)) - for _, tCol := range tCols { - if tCol.ID > 0 && tCol.VirtualExpr != nil { - tColSet[tCol.ID] = struct{}{} - } - } - for _, by := range p.ByItems { - cols := expression.ExtractColumns(by.Expr) - for _, col := range cols { - if _, ok := tColSet[col.ID]; ok { - // A column with ID > 0 indicates that the column can be resolved by data source. - return true - } - } - } - return false -} - -// canPushDownToTiKV checks whether this topN can be pushed down to TiKV. -func (p *PhysicalTopN) canPushDownToTiKV(copTask *copTask) bool { - if !p.canExpressionConvertedToPB(kv.TiKV) { - return false - } - if len(copTask.rootTaskConds) != 0 { - return false - } - if !copTask.indexPlanFinished && len(copTask.idxMergePartPlans) > 0 { - for _, partialPlan := range copTask.idxMergePartPlans { - if p.containVirtualColumn(partialPlan.Schema().Columns) { - return false - } - } - } else if p.containVirtualColumn(copTask.plan().Schema().Columns) { - return false - } - return true -} - -// canPushDownToTiFlash checks whether this topN can be pushed down to TiFlash. -func (p *PhysicalTopN) canPushDownToTiFlash(mppTask *mppTask) bool { - if !p.canExpressionConvertedToPB(kv.TiFlash) { - return false - } - if p.containVirtualColumn(mppTask.plan().Schema().Columns) { - return false - } - return true -} - -func (p *PhysicalTopN) attach2Task(tasks ...task) task { - t := tasks[0].copy() - cols := make([]*expression.Column, 0, len(p.ByItems)) - for _, item := range p.ByItems { - cols = append(cols, expression.ExtractColumns(item.Expr)...) - } - needPushDown := len(cols) > 0 - if copTask, ok := t.(*copTask); ok && needPushDown && p.canPushDownToTiKV(copTask) && len(copTask.rootTaskConds) == 0 { - // If all columns in topN are from index plan, we push it to index plan, otherwise we finish the index plan and - // push it to table plan. - var pushedDownTopN *PhysicalTopN - if !copTask.indexPlanFinished && p.canPushToIndexPlan(copTask.indexPlan, cols) { - pushedDownTopN = p.getPushedDownTopN(copTask.indexPlan) - copTask.indexPlan = pushedDownTopN - } else { - // It works for both normal index scan and index merge scan. - copTask.finishIndexPlan() - pushedDownTopN = p.getPushedDownTopN(copTask.tablePlan) - copTask.tablePlan = pushedDownTopN - } - } else if mppTask, ok := t.(*mppTask); ok && needPushDown && p.canPushDownToTiFlash(mppTask) { - pushedDownTopN := p.getPushedDownTopN(mppTask.p) - mppTask.p = pushedDownTopN - } - rootTask := t.convertToRootTask(p.SCtx()) - // Skip TopN with partition on the root. This is a derived topN and window function - // will take care of the filter. - if len(p.GetPartitionBy()) > 0 { - return t - } - return attachPlan2Task(p, rootTask) -} - -func (p *PhysicalExpand) attach2Task(tasks ...task) task { - t := tasks[0].copy() - // current expand can only be run in MPP TiFlash mode. - if mpp, ok := t.(*mppTask); ok { - p.SetChildren(mpp.p) - mpp.p = p - return mpp - } - return invalidTask -} - -func (p *PhysicalProjection) attach2Task(tasks ...task) task { - t := tasks[0].copy() - if cop, ok := t.(*copTask); ok { - if (len(cop.rootTaskConds) == 0 && len(cop.idxMergePartPlans) == 0) && expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, p.Exprs, p.SCtx().GetClient(), cop.getStoreType()) { - copTask := attachPlan2Task(p, cop) - return copTask - } - } else if mpp, ok := t.(*mppTask); ok { - if expression.CanExprsPushDown(p.SCtx().GetSessionVars().StmtCtx, p.Exprs, p.SCtx().GetClient(), kv.TiFlash) { - p.SetChildren(mpp.p) - mpp.p = p - return mpp - } - } - t = t.convertToRootTask(p.SCtx()) - t = attachPlan2Task(p, t) - if root, ok := tasks[0].(*rootTask); ok && root.isEmpty { - t.(*rootTask).isEmpty = true - } - return t -} - -func (p *PhysicalUnionAll) attach2MppTasks(tasks ...task) task { - t := &mppTask{p: p} - childPlans := make([]PhysicalPlan, 0, len(tasks)) - for _, tk := range tasks { - if mpp, ok := tk.(*mppTask); ok && !tk.invalid() { - childPlans = append(childPlans, mpp.plan()) - } else if root, ok := tk.(*rootTask); ok && root.isEmpty { - continue - } else { - return invalidTask - } - } - if len(childPlans) == 0 { - return invalidTask - } - p.SetChildren(childPlans...) - return t -} - -func (p *PhysicalUnionAll) attach2Task(tasks ...task) task { - for _, t := range tasks { - if _, ok := t.(*mppTask); ok { - if p.TP() == plancodec.TypePartitionUnion { - // In attach2MppTasks(), will attach PhysicalUnion to mppTask directly. - // But PartitionUnion cannot pushdown to tiflash, so here disable PartitionUnion pushdown to tiflash explicitly. - // For now, return invalidTask immediately, we can refine this by letting childTask of PartitionUnion convert to rootTask. - return invalidTask - } - return p.attach2MppTasks(tasks...) - } - } - t := &rootTask{p: p} - childPlans := make([]PhysicalPlan, 0, len(tasks)) - for _, task := range tasks { - task = task.convertToRootTask(p.SCtx()) - childPlans = append(childPlans, task.plan()) - } - p.SetChildren(childPlans...) - return t -} - -func (sel *PhysicalSelection) attach2Task(tasks ...task) task { - if mppTask, _ := tasks[0].(*mppTask); mppTask != nil { // always push to mpp task. - sc := sel.SCtx().GetSessionVars().StmtCtx - if expression.CanExprsPushDown(sc, sel.Conditions, sel.SCtx().GetClient(), kv.TiFlash) { - return attachPlan2Task(sel, mppTask.copy()) - } - } - t := tasks[0].convertToRootTask(sel.SCtx()) - return attachPlan2Task(sel, t) -} - -// CheckAggCanPushCop checks whether the aggFuncs and groupByItems can -// be pushed down to coprocessor. -func CheckAggCanPushCop(sctx sessionctx.Context, aggFuncs []*aggregation.AggFuncDesc, groupByItems []expression.Expression, storeType kv.StoreType) bool { - sc := sctx.GetSessionVars().StmtCtx - client := sctx.GetClient() - ret := true - reason := "" - for _, aggFunc := range aggFuncs { - // if the aggFunc contain VirtualColumn or CorrelatedColumn, it can not be pushed down. - if expression.ContainVirtualColumn(aggFunc.Args) || expression.ContainCorrelatedColumn(aggFunc.Args) { - reason = "expressions of AggFunc `" + aggFunc.Name + "` contain virtual column or correlated column, which is not supported now" - ret = false - break - } - if !aggregation.CheckAggPushDown(aggFunc, storeType) { - reason = "AggFunc `" + aggFunc.Name + "` is not supported now" - ret = false - break - } - if !expression.CanExprsPushDownWithExtraInfo(sc, aggFunc.Args, client, storeType, aggFunc.Name == ast.AggFuncSum) { - reason = "arguments of AggFunc `" + aggFunc.Name + "` contains unsupported exprs" - ret = false - break - } - orderBySize := len(aggFunc.OrderByItems) - if orderBySize > 0 { - exprs := make([]expression.Expression, 0, orderBySize) - for _, item := range aggFunc.OrderByItems { - exprs = append(exprs, item.Expr) - } - if !expression.CanExprsPushDownWithExtraInfo(sc, exprs, client, storeType, false) { - reason = "arguments of AggFunc `" + aggFunc.Name + "` contains unsupported exprs in order-by clause" - ret = false - break - } - } - pb, _ := aggregation.AggFuncToPBExpr(sctx, client, aggFunc, storeType) - if pb == nil { - reason = "AggFunc `" + aggFunc.Name + "` can not be converted to pb expr" - ret = false - break - } - } - if ret && expression.ContainVirtualColumn(groupByItems) { - reason = "groupByItems contain virtual columns, which is not supported now" - ret = false - } - if ret && !expression.CanExprsPushDown(sc, groupByItems, client, storeType) { - reason = "groupByItems contain unsupported exprs" - ret = false - } - - if !ret { - storageName := storeType.Name() - if storeType == kv.UnSpecified { - storageName = "storage layer" - } - warnErr := errors.New("Aggregation can not be pushed to " + storageName + " because " + reason) - if sc.InExplainStmt { - sc.AppendWarning(warnErr) - } else { - sc.AppendExtraWarning(warnErr) - } - } - return ret -} - -// AggInfo stores the information of an Aggregation. -type AggInfo struct { - AggFuncs []*aggregation.AggFuncDesc - GroupByItems []expression.Expression - Schema *expression.Schema -} - -// BuildFinalModeAggregation splits either LogicalAggregation or PhysicalAggregation to finalAgg and partial1Agg, -// returns the information of partial and final agg. -// partialIsCop means whether partial agg is a cop task. When partialIsCop is false, -// we do not set the AggMode for partialAgg cause it may be split further when -// building the aggregate executor(e.g. buildHashAgg will split the AggDesc further for parallel executing). -// firstRowFuncMap is a map between partial first_row to final first_row, will be used in RemoveUnnecessaryFirstRow -func BuildFinalModeAggregation( - sctx sessionctx.Context, original *AggInfo, partialIsCop bool, isMPPTask bool) (partial, final *AggInfo, firstRowFuncMap map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc) { - firstRowFuncMap = make(map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc, len(original.AggFuncs)) - partial = &AggInfo{ - AggFuncs: make([]*aggregation.AggFuncDesc, 0, len(original.AggFuncs)), - GroupByItems: original.GroupByItems, - Schema: expression.NewSchema(), - } - partialCursor := 0 - final = &AggInfo{ - AggFuncs: make([]*aggregation.AggFuncDesc, len(original.AggFuncs)), - GroupByItems: make([]expression.Expression, 0, len(original.GroupByItems)), - Schema: original.Schema, - } - - partialGbySchema := expression.NewSchema() - // add group by columns - for _, gbyExpr := range partial.GroupByItems { - var gbyCol *expression.Column - if col, ok := gbyExpr.(*expression.Column); ok { - gbyCol = col - } else { - gbyCol = &expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: gbyExpr.GetType(), - } - } - partialGbySchema.Append(gbyCol) - final.GroupByItems = append(final.GroupByItems, gbyCol) - } - - // TODO: Refactor the way of constructing aggregation functions. - // This for loop is ugly, but I do not find a proper way to reconstruct - // it right away. - - // group_concat is special when pushing down, it cannot take the two phase execution if no distinct but with orderBy, and other cases are also different: - // for example: group_concat([distinct] expr0, expr1[, order by expr2] separator ‘,’) - // no distinct, no orderBy: can two phase - // [final agg] group_concat(col#1,’,’) - // [part agg] group_concat(expr0, expr1,’,’) -> col#1 - // no distinct, orderBy: only one phase - // distinct, no orderBy: can two phase - // [final agg] group_concat(distinct col#0, col#1,’,’) - // [part agg] group by expr0 ->col#0, expr1 -> col#1 - // distinct, orderBy: can two phase - // [final agg] group_concat(distinct col#0, col#1, order by col#2,’,’) - // [part agg] group by expr0 ->col#0, expr1 -> col#1; agg function: firstrow(expr2)-> col#2 - - for i, aggFunc := range original.AggFuncs { - finalAggFunc := &aggregation.AggFuncDesc{HasDistinct: false} - finalAggFunc.Name = aggFunc.Name - finalAggFunc.OrderByItems = aggFunc.OrderByItems - args := make([]expression.Expression, 0, len(aggFunc.Args)) - if aggFunc.HasDistinct { - /* - eg: SELECT COUNT(DISTINCT a), SUM(b) FROM t GROUP BY c - - change from - [root] group by: c, funcs:count(distinct a), funcs:sum(b) - to - [root] group by: c, funcs:count(distinct a), funcs:sum(b) - [cop]: group by: c, a - */ - // onlyAddFirstRow means if the distinctArg does not occur in group by items, - // it should be replaced with a firstrow() agg function, needed for the order by items of group_concat() - getDistinctExpr := func(distinctArg expression.Expression, onlyAddFirstRow bool) (ret expression.Expression) { - // 1. add all args to partial.GroupByItems - foundInGroupBy := false - for j, gbyExpr := range partial.GroupByItems { - if gbyExpr.Equal(sctx, distinctArg) && gbyExpr.GetType().Equal(distinctArg.GetType()) { - // if the two expressions exactly the same in terms of data types and collation, then can avoid it. - foundInGroupBy = true - ret = partialGbySchema.Columns[j] - break - } - } - if !foundInGroupBy { - var gbyCol *expression.Column - if col, ok := distinctArg.(*expression.Column); ok { - gbyCol = col - } else { - gbyCol = &expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: distinctArg.GetType(), - } - } - // 2. add group by items if needed - if !onlyAddFirstRow { - partial.GroupByItems = append(partial.GroupByItems, distinctArg) - partialGbySchema.Append(gbyCol) - ret = gbyCol - } - // 3. add firstrow() if needed - if !partialIsCop || onlyAddFirstRow { - // if partial is a cop task, firstrow function is redundant since group by items are outputted - // by group by schema, and final functions use group by schema as their arguments. - // if partial agg is not cop, we must append firstrow function & schema, to output the group by - // items. - // maybe we can unify them sometime. - // only add firstrow for order by items of group_concat() - firstRow, err := aggregation.NewAggFuncDesc(sctx, ast.AggFuncFirstRow, []expression.Expression{distinctArg}, false) - if err != nil { - panic("NewAggFuncDesc FirstRow meets error: " + err.Error()) - } - partial.AggFuncs = append(partial.AggFuncs, firstRow) - newCol, _ := gbyCol.Clone().(*expression.Column) - newCol.RetType = firstRow.RetTp - partial.Schema.Append(newCol) - if onlyAddFirstRow { - ret = newCol - } - partialCursor++ - } - } - return ret - } - - for j, distinctArg := range aggFunc.Args { - // the last arg of ast.AggFuncGroupConcat is the separator, so just put it into the final agg - if aggFunc.Name == ast.AggFuncGroupConcat && j+1 == len(aggFunc.Args) { - args = append(args, distinctArg) - continue - } - args = append(args, getDistinctExpr(distinctArg, false)) - } - - byItems := make([]*util.ByItems, 0, len(aggFunc.OrderByItems)) - for _, byItem := range aggFunc.OrderByItems { - byItems = append(byItems, &util.ByItems{Expr: getDistinctExpr(byItem.Expr, true), Desc: byItem.Desc}) - } - - if aggFunc.HasDistinct && isMPPTask && aggFunc.GroupingID > 0 { - // keep the groupingID as it was, otherwise the new split final aggregate's ganna lost its groupingID info. - finalAggFunc.GroupingID = aggFunc.GroupingID - } - - finalAggFunc.OrderByItems = byItems - finalAggFunc.HasDistinct = aggFunc.HasDistinct - // In logical optimize phase, the Agg->PartitionUnion->TableReader may become - // Agg1->PartitionUnion->Agg2->TableReader, and the Agg2 is a partial aggregation. - // So in the push down here, we need to add a new if-condition check: - // If the original agg mode is partial already, the finalAggFunc's mode become Partial2. - if aggFunc.Mode == aggregation.CompleteMode { - finalAggFunc.Mode = aggregation.CompleteMode - } else if aggFunc.Mode == aggregation.Partial1Mode || aggFunc.Mode == aggregation.Partial2Mode { - finalAggFunc.Mode = aggregation.Partial2Mode - } - } else { - if aggFunc.Name == ast.AggFuncGroupConcat && len(aggFunc.OrderByItems) > 0 { - // group_concat can only run in one phase if it has order by items but without distinct property - partial = nil - final = original - return - } - if aggregation.NeedCount(finalAggFunc.Name) { - // only Avg and Count need count - if isMPPTask && finalAggFunc.Name == ast.AggFuncCount { - // For MPP Task, the final count() is changed to sum(). - // Note: MPP mode does not run avg() directly, instead, avg() -> sum()/(case when count() = 0 then 1 else count() end), - // so we do not process it here. - finalAggFunc.Name = ast.AggFuncSum - } else { - // avg branch - ft := types.NewFieldType(mysql.TypeLonglong) - ft.SetFlen(21) - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - partial.Schema.Append(&expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: ft, - }) - args = append(args, partial.Schema.Columns[partialCursor]) - partialCursor++ - } - } - if finalAggFunc.Name == ast.AggFuncApproxCountDistinct { - ft := types.NewFieldType(mysql.TypeString) - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - ft.AddFlag(mysql.NotNullFlag) - partial.Schema.Append(&expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: ft, - }) - args = append(args, partial.Schema.Columns[partialCursor]) - partialCursor++ - } - if aggregation.NeedValue(finalAggFunc.Name) { - partial.Schema.Append(&expression.Column{ - UniqueID: sctx.GetSessionVars().AllocPlanColumnID(), - RetType: original.Schema.Columns[i].GetType(), - }) - args = append(args, partial.Schema.Columns[partialCursor]) - partialCursor++ - } - if aggFunc.Name == ast.AggFuncAvg { - cntAgg := aggFunc.Clone() - cntAgg.Name = ast.AggFuncCount - err := cntAgg.TypeInfer(sctx) - if err != nil { // must not happen - partial = nil - final = original - return - } - partial.Schema.Columns[partialCursor-2].RetType = cntAgg.RetTp - // we must call deep clone in this case, to avoid sharing the arguments. - sumAgg := aggFunc.Clone() - sumAgg.Name = ast.AggFuncSum - sumAgg.TypeInfer4AvgSum(sumAgg.RetTp) - partial.Schema.Columns[partialCursor-1].RetType = sumAgg.RetTp - partial.AggFuncs = append(partial.AggFuncs, cntAgg, sumAgg) - } else if aggFunc.Name == ast.AggFuncApproxCountDistinct || aggFunc.Name == ast.AggFuncGroupConcat { - newAggFunc := aggFunc.Clone() - newAggFunc.Name = aggFunc.Name - newAggFunc.RetTp = partial.Schema.Columns[partialCursor-1].GetType() - partial.AggFuncs = append(partial.AggFuncs, newAggFunc) - if aggFunc.Name == ast.AggFuncGroupConcat { - // append the last separator arg - args = append(args, aggFunc.Args[len(aggFunc.Args)-1]) - } - } else { - // other agg desc just split into two parts - partialFuncDesc := aggFunc.Clone() - partial.AggFuncs = append(partial.AggFuncs, partialFuncDesc) - if aggFunc.Name == ast.AggFuncFirstRow { - firstRowFuncMap[partialFuncDesc] = finalAggFunc - } - } - - // In logical optimize phase, the Agg->PartitionUnion->TableReader may become - // Agg1->PartitionUnion->Agg2->TableReader, and the Agg2 is a partial aggregation. - // So in the push down here, we need to add a new if-condition check: - // If the original agg mode is partial already, the finalAggFunc's mode become Partial2. - if aggFunc.Mode == aggregation.CompleteMode { - finalAggFunc.Mode = aggregation.FinalMode - } else if aggFunc.Mode == aggregation.Partial1Mode || aggFunc.Mode == aggregation.Partial2Mode { - finalAggFunc.Mode = aggregation.Partial2Mode - } - } - - finalAggFunc.Args = args - finalAggFunc.RetTp = aggFunc.RetTp - final.AggFuncs[i] = finalAggFunc - } - partial.Schema.Append(partialGbySchema.Columns...) - if partialIsCop { - for _, f := range partial.AggFuncs { - f.Mode = aggregation.Partial1Mode - } - } - return -} - -// convertAvgForMPP converts avg(arg) to sum(arg)/(case when count(arg)=0 then 1 else count(arg) end), in detail: -// 1.rewrite avg() in the final aggregation to count() and sum(), and reconstruct its schema. -// 2.replace avg() with sum(arg)/(case when count(arg)=0 then 1 else count(arg) end) and reuse the original schema of the final aggregation. -// If there is no avg, nothing is changed and return nil. -func (p *basePhysicalAgg) convertAvgForMPP() *PhysicalProjection { - newSchema := expression.NewSchema() - newSchema.Keys = p.schema.Keys - newSchema.UniqueKeys = p.schema.UniqueKeys - newAggFuncs := make([]*aggregation.AggFuncDesc, 0, 2*len(p.AggFuncs)) - exprs := make([]expression.Expression, 0, 2*len(p.schema.Columns)) - // add agg functions schema - for i, aggFunc := range p.AggFuncs { - if aggFunc.Name == ast.AggFuncAvg { - // inset a count(column) - avgCount := aggFunc.Clone() - avgCount.Name = ast.AggFuncCount - err := avgCount.TypeInfer(p.SCtx()) - if err != nil { // must not happen - return nil - } - newAggFuncs = append(newAggFuncs, avgCount) - avgCountCol := &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: avgCount.RetTp, - } - newSchema.Append(avgCountCol) - // insert a sum(column) - avgSum := aggFunc.Clone() - avgSum.Name = ast.AggFuncSum - avgSum.TypeInfer4AvgSum(avgSum.RetTp) - newAggFuncs = append(newAggFuncs, avgSum) - avgSumCol := &expression.Column{ - UniqueID: p.schema.Columns[i].UniqueID, - RetType: avgSum.RetTp, - } - newSchema.Append(avgSumCol) - // avgSumCol/(case when avgCountCol=0 then 1 else avgCountCol end) - eq := expression.NewFunctionInternal(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeTiny), avgCountCol, expression.NewZero()) - caseWhen := expression.NewFunctionInternal(p.SCtx(), ast.Case, avgCountCol.RetType, eq, expression.NewOne(), avgCountCol) - divide := expression.NewFunctionInternal(p.SCtx(), ast.Div, avgSumCol.RetType, avgSumCol, caseWhen) - divide.(*expression.ScalarFunction).RetType = p.schema.Columns[i].RetType - exprs = append(exprs, divide) - } else { - // other non-avg agg use the old schema as it did. - newAggFuncs = append(newAggFuncs, aggFunc) - newSchema.Append(p.schema.Columns[i]) - exprs = append(exprs, p.schema.Columns[i]) - } - } - // no avgs - // for final agg, always add project due to in-compatibility between TiDB and TiFlash - if len(p.schema.Columns) == len(newSchema.Columns) && !p.IsFinalAgg() { - return nil - } - // add remaining columns to exprs - for i := len(p.AggFuncs); i < len(p.schema.Columns); i++ { - exprs = append(exprs, p.schema.Columns[i]) - } - proj := PhysicalProjection{ - Exprs: exprs, - CalculateNoDelay: false, - AvoidColumnEvaluator: false, - }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), p.GetChildReqProps(0).CloneEssentialFields()) - proj.SetSchema(p.schema) - - p.AggFuncs = newAggFuncs - p.schema = newSchema - - return proj -} - -func (p *basePhysicalAgg) newPartialAggregate(copTaskType kv.StoreType, isMPPTask bool) (partial, final PhysicalPlan) { - // Check if this aggregation can push down. - if !CheckAggCanPushCop(p.SCtx(), p.AggFuncs, p.GroupByItems, copTaskType) { - return nil, p.self - } - partialPref, finalPref, firstRowFuncMap := BuildFinalModeAggregation(p.SCtx(), &AggInfo{ - AggFuncs: p.AggFuncs, - GroupByItems: p.GroupByItems, - Schema: p.Schema().Clone(), - }, true, isMPPTask) - if partialPref == nil { - return nil, p.self - } - if p.TP() == plancodec.TypeStreamAgg && len(partialPref.GroupByItems) != len(finalPref.GroupByItems) { - return nil, p.self - } - // Remove unnecessary FirstRow. - partialPref.AggFuncs = RemoveUnnecessaryFirstRow(p.SCtx(), - finalPref.GroupByItems, partialPref.AggFuncs, partialPref.GroupByItems, partialPref.Schema, firstRowFuncMap) - if copTaskType == kv.TiDB { - // For partial agg of TiDB cop task, since TiDB coprocessor reuse the TiDB executor, - // and TiDB aggregation executor won't output the group by value, - // so we need add `firstrow` aggregation function to output the group by value. - aggFuncs, err := genFirstRowAggForGroupBy(p.SCtx(), partialPref.GroupByItems) - if err != nil { - return nil, p.self - } - partialPref.AggFuncs = append(partialPref.AggFuncs, aggFuncs...) - } - p.AggFuncs = partialPref.AggFuncs - p.GroupByItems = partialPref.GroupByItems - p.schema = partialPref.Schema - partialAgg := p.self - // Create physical "final" aggregation. - prop := &property.PhysicalProperty{ExpectedCnt: math.MaxFloat64} - if p.TP() == plancodec.TypeStreamAgg { - finalAgg := basePhysicalAgg{ - AggFuncs: finalPref.AggFuncs, - GroupByItems: finalPref.GroupByItems, - MppRunMode: p.MppRunMode, - }.initForStream(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), prop) - finalAgg.schema = finalPref.Schema - return partialAgg, finalAgg - } - - finalAgg := basePhysicalAgg{ - AggFuncs: finalPref.AggFuncs, - GroupByItems: finalPref.GroupByItems, - MppRunMode: p.MppRunMode, - }.initForHash(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset(), prop) - finalAgg.schema = finalPref.Schema - // partialAgg and finalAgg use the same ref of stats - return partialAgg, finalAgg -} - -func (p *basePhysicalAgg) scale3StageForDistinctAgg() (bool, expression.GroupingSets) { - if p.canUse3Stage4SingleDistinctAgg() { - return true, nil - } - return p.canUse3Stage4MultiDistinctAgg() -} - -// canUse3Stage4MultiDistinctAgg returns true if this agg can use 3 stage for multi distinct aggregation -func (p *basePhysicalAgg) canUse3Stage4MultiDistinctAgg() (can bool, gss expression.GroupingSets) { - if !p.SCtx().GetSessionVars().Enable3StageDistinctAgg || !p.SCtx().GetSessionVars().Enable3StageMultiDistinctAgg || len(p.GroupByItems) > 0 { - return false, nil - } - defer func() { - // some clean work. - if !can { - for _, fun := range p.AggFuncs { - fun.GroupingID = 0 - } - } - }() - // groupingSets is alias of []GroupingSet, the below equal to = make([]GroupingSet, 0, 2) - groupingSets := make(expression.GroupingSets, 0, 2) - for _, fun := range p.AggFuncs { - if fun.HasDistinct { - if fun.Name != ast.AggFuncCount { - // now only for multi count(distinct x) - return false, nil - } - for _, arg := range fun.Args { - // bail out when args are not simple column, see GitHub issue #35417 - if _, ok := arg.(*expression.Column); !ok { - return false, nil - } - } - // here it's a valid count distinct agg with normal column args, collecting its distinct expr. - groupingSets = append(groupingSets, expression.GroupingSet{fun.Args}) - // groupingID now is the offset of target grouping in GroupingSets. - // todo: it may be changed after grouping set merge in the future. - fun.GroupingID = len(groupingSets) - } else if len(fun.Args) > 1 { - return false, nil - } - // banned group_concat(x order by y) - if len(fun.OrderByItems) > 0 || fun.Mode != aggregation.CompleteMode { - return false, nil - } - } - compressed := groupingSets.Merge() - if len(compressed) != len(groupingSets) { - p.SCtx().GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("Some grouping sets should be merged")) - // todo arenatlx: some grouping set should be merged which is not supported by now temporarily. - return false, nil - } - if groupingSets.NeedCloneColumn() { - // todo: column clone haven't implemented. - return false, nil - } - if len(groupingSets) > 1 { - // fill the grouping ID for normal agg. - for _, fun := range p.AggFuncs { - if fun.GroupingID == 0 { - // the grouping ID hasn't set. find the targeting grouping set. - groupingSetOffset := groupingSets.TargetOne(fun.Args) - if groupingSetOffset == -1 { - // todo: if we couldn't find a existed current valid group layout, we need to copy the column out from being filled with null value. - p.SCtx().GetSessionVars().StmtCtx.AppendWarning(errors.Errorf("couldn't find a proper group set for normal agg")) - return false, nil - } - // starting with 1 - fun.GroupingID = groupingSetOffset + 1 - } - } - return true, groupingSets - } - return false, nil -} - -// canUse3Stage4SingleDistinctAgg returns true if this agg can use 3 stage for distinct aggregation -func (p *basePhysicalAgg) canUse3Stage4SingleDistinctAgg() bool { - num := 0 - if !p.SCtx().GetSessionVars().Enable3StageDistinctAgg || len(p.GroupByItems) > 0 { - return false - } - for _, fun := range p.AggFuncs { - if fun.HasDistinct { - num++ - if num > 1 || fun.Name != ast.AggFuncCount { - return false - } - for _, arg := range fun.Args { - // bail out when args are not simple column, see GitHub issue #35417 - if _, ok := arg.(*expression.Column); !ok { - return false - } - } - } else if len(fun.Args) > 1 { - return false - } - - if len(fun.OrderByItems) > 0 || fun.Mode != aggregation.CompleteMode { - return false - } - } - return num == 1 -} - -func genFirstRowAggForGroupBy(ctx sessionctx.Context, groupByItems []expression.Expression) ([]*aggregation.AggFuncDesc, error) { - aggFuncs := make([]*aggregation.AggFuncDesc, 0, len(groupByItems)) - for _, groupBy := range groupByItems { - agg, err := aggregation.NewAggFuncDesc(ctx, ast.AggFuncFirstRow, []expression.Expression{groupBy}, false) - if err != nil { - return nil, err - } - aggFuncs = append(aggFuncs, agg) - } - return aggFuncs, nil -} - -// RemoveUnnecessaryFirstRow removes unnecessary FirstRow of the aggregation. This function can be -// used for both LogicalAggregation and PhysicalAggregation. -// When the select column is same with the group by key, the column can be removed and gets value from the group by key. -// e.g -// select a, count(b) from t group by a; -// The schema is [firstrow(a), count(b), a]. The column firstrow(a) is unnecessary. -// Can optimize the schema to [count(b), a] , and change the index to get value. -func RemoveUnnecessaryFirstRow( - sctx sessionctx.Context, - finalGbyItems []expression.Expression, - partialAggFuncs []*aggregation.AggFuncDesc, - partialGbyItems []expression.Expression, - partialSchema *expression.Schema, - firstRowFuncMap map[*aggregation.AggFuncDesc]*aggregation.AggFuncDesc) []*aggregation.AggFuncDesc { - partialCursor := 0 - newAggFuncs := make([]*aggregation.AggFuncDesc, 0, len(partialAggFuncs)) - for _, aggFunc := range partialAggFuncs { - if aggFunc.Name == ast.AggFuncFirstRow { - canOptimize := false - for j, gbyExpr := range partialGbyItems { - if j >= len(finalGbyItems) { - // after distinct push, len(partialGbyItems) may larger than len(finalGbyItems) - // for example, - // select /*+ HASH_AGG() */ a, count(distinct a) from t; - // will generate to, - // HashAgg root funcs:count(distinct a), funcs:firstrow(a)" - // HashAgg cop group by:a, funcs:firstrow(a)->Column#6" - // the firstrow in root task can not be removed. - break - } - if gbyExpr.Equal(sctx, aggFunc.Args[0]) { - canOptimize = true - firstRowFuncMap[aggFunc].Args[0] = finalGbyItems[j] - break - } - } - if canOptimize { - partialSchema.Columns = append(partialSchema.Columns[:partialCursor], partialSchema.Columns[partialCursor+1:]...) - continue - } - } - partialCursor += computePartialCursorOffset(aggFunc.Name) - newAggFuncs = append(newAggFuncs, aggFunc) - } - return newAggFuncs -} - -func computePartialCursorOffset(name string) int { - offset := 0 - if aggregation.NeedCount(name) { - offset++ - } - if aggregation.NeedValue(name) { - offset++ - } - if name == ast.AggFuncApproxCountDistinct { - offset++ - } - return offset -} - -func (p *PhysicalStreamAgg) attach2Task(tasks ...task) task { - t := tasks[0].copy() - if cop, ok := t.(*copTask); ok { - // We should not push agg down across - // 1. double read, since the data of second read is ordered by handle instead of index. The `extraHandleCol` is added - // if the double read needs to keep order. So we just use it to decided - // whether the following plan is double read with order reserved. - // 2. the case that there's filters should be calculated on TiDB side. - // 3. the case of index merge - if (cop.indexPlan != nil && cop.tablePlan != nil && cop.keepOrder) || len(cop.rootTaskConds) > 0 || len(cop.idxMergePartPlans) > 0 { - t = cop.convertToRootTask(p.SCtx()) - attachPlan2Task(p, t) - } else { - storeType := cop.getStoreType() - // TiFlash doesn't support Stream Aggregation - if storeType == kv.TiFlash && len(p.GroupByItems) > 0 { - return invalidTask - } - partialAgg, finalAgg := p.newPartialAggregate(storeType, false) - if partialAgg != nil { - if cop.tablePlan != nil { - cop.finishIndexPlan() - partialAgg.SetChildren(cop.tablePlan) - cop.tablePlan = partialAgg - // If needExtraProj is true, a projection will be created above the PhysicalIndexLookUpReader to make sure - // the schema is the same as the original DataSource schema. - // However, we pushed down the agg here, the partial agg was placed on the top of tablePlan, and the final - // agg will be placed above the PhysicalIndexLookUpReader, and the schema will be set correctly for them. - // If we add the projection again, the projection will be between the PhysicalIndexLookUpReader and - // the partial agg, and the schema will be broken. - cop.needExtraProj = false - } else { - partialAgg.SetChildren(cop.indexPlan) - cop.indexPlan = partialAgg - } - } - t = cop.convertToRootTask(p.SCtx()) - attachPlan2Task(finalAgg, t) - } - } else if mpp, ok := t.(*mppTask); ok { - t = mpp.convertToRootTask(p.SCtx()) - attachPlan2Task(p, t) - } else { - attachPlan2Task(p, t) - } - return t -} - -// cpuCostDivisor computes the concurrency to which we would amortize CPU cost -// for hash aggregation. -func (p *PhysicalHashAgg) cpuCostDivisor(hasDistinct bool) (divisor, con float64) { - if hasDistinct { - return 0, 0 - } - sessionVars := p.SCtx().GetSessionVars() - finalCon, partialCon := sessionVars.HashAggFinalConcurrency(), sessionVars.HashAggPartialConcurrency() - // According to `ValidateSetSystemVar`, `finalCon` and `partialCon` cannot be less than or equal to 0. - if finalCon == 1 && partialCon == 1 { - return 0, 0 - } - // It is tricky to decide which concurrency we should use to amortize CPU cost. Since cost of hash - // aggregation is tend to be under-estimated as explained in `attach2Task`, we choose the smaller - // concurrecy to make some compensation. - return math.Min(float64(finalCon), float64(partialCon)), float64(finalCon + partialCon) -} - -func (p *PhysicalHashAgg) attach2TaskForMpp1Phase(mpp *mppTask) task { - // 1-phase agg: when the partition columns can be satisfied, where the plan does not need to enforce Exchange - // only push down the original agg - proj := p.convertAvgForMPP() - attachPlan2Task(p.self, mpp) - if proj != nil { - attachPlan2Task(proj, mpp) - } - return mpp -} - -// scaleStats4GroupingSets scale the derived stats because the lower source has been expanded. -// -// parent OP <- logicalAgg <- children OP (derived stats) -// | -// v -// parent OP <- physicalAgg <- children OP (stats used) -// | -// +----------+----------+----------+ -// Final Mid Partial Expand -// -// physical agg stats is reasonable from the whole, because expand operator is designed to facilitate -// the Mid and Partial Agg, which means when leaving the Final, its output rowcount could be exactly -// the same as what it derived(estimated) before entering physical optimization phase. -// -// From the cost model correctness, for these inserted sub-agg and even expand operator, we should -// recompute the stats for them particularly. -// -// for example: grouping sets {},{}, group by items {a,b,c,groupingID} -// after expand: -// -// a, b, c, groupingID -// ... null c 1 ---+ -// ... null c 1 +------- replica group 1 -// ... null c 1 ---+ -// null ... c 2 ---+ -// null ... c 2 +------- replica group 2 -// null ... c 2 ---+ -// -// since null value is seen the same when grouping data (groupingID in one replica is always the same): -// - so the num of group in replica 1 is equal to NDV(a,c) -// - so the num of group in replica 2 is equal to NDV(b,c) -// -// in a summary, the total num of group of all replica is equal to = Σ:NDV(each-grouping-set-cols, normal-group-cols) -func (p *PhysicalHashAgg) scaleStats4GroupingSets(groupingSets expression.GroupingSets, groupingIDCol *expression.Column, - childSchema *expression.Schema, childStats *property.StatsInfo) { - idSets := groupingSets.AllSetsColIDs() - normalGbyCols := make([]*expression.Column, 0, len(p.GroupByItems)) - for _, gbyExpr := range p.GroupByItems { - cols := expression.ExtractColumns(gbyExpr) - for _, col := range cols { - if !idSets.Has(int(col.UniqueID)) && col.UniqueID != groupingIDCol.UniqueID { - normalGbyCols = append(normalGbyCols, col) - } - } - } - sumNDV := float64(0) - for _, groupingSet := range groupingSets { - // for every grouping set, pick its cols out, and combine with normal group cols to get the ndv. - groupingSetCols := groupingSet.ExtractCols() - groupingSetCols = append(groupingSetCols, normalGbyCols...) - ndv, _ := cardinality.EstimateColsNDVWithMatchedLen(groupingSetCols, childSchema, childStats) - sumNDV += ndv - } - // After group operator, all same rows are grouped into one row, that means all - // change the sub-agg's stats - if p.StatsInfo() != nil { - // equivalence to a new cloned one. (cause finalAgg and partialAgg may share a same copy of stats) - cpStats := p.StatsInfo().Scale(1) - cpStats.RowCount = sumNDV - // We cannot estimate the ColNDVs for every output, so we use a conservative strategy. - for k := range cpStats.ColNDVs { - cpStats.ColNDVs[k] = sumNDV - } - // for old groupNDV, if it's containing one more grouping set cols, just plus the NDV where the col is excluded. - // for example: old grouping NDV(b,c), where b is in grouping sets {},{}. so when countering the new NDV: - // cases: - // new grouping NDV(b,c) := old NDV(b,c) + NDV(null, c) = old NDV(b,c) + DNV(c). - // new grouping NDV(a,b,c) := old NDV(a,b,c) + NDV(null,b,c) + NDV(a,null,c) = old NDV(a,b,c) + NDV(b,c) + NDV(a,c) - allGroupingSetsIDs := groupingSets.AllSetsColIDs() - for _, oneGNDV := range cpStats.GroupNDVs { - newGNDV := oneGNDV.NDV - intersectionIDs := make([]int64, 0, len(oneGNDV.Cols)) - for i, id := range oneGNDV.Cols { - if allGroupingSetsIDs.Has(int(id)) { - // when meet an id in grouping sets, skip it (cause its null) and append the rest ids to count the incrementNDV. - beforeLen := len(intersectionIDs) - intersectionIDs = append(intersectionIDs, oneGNDV.Cols[i:]...) - incrementNDV, _ := cardinality.EstimateColsDNVWithMatchedLenFromUniqueIDs(intersectionIDs, childSchema, childStats) - newGNDV += incrementNDV - // restore the before intersectionIDs slice. - intersectionIDs = intersectionIDs[:beforeLen] - } - // insert ids one by one. - intersectionIDs = append(intersectionIDs, id) - } - oneGNDV.NDV = newGNDV - } - p.SetStats(cpStats) - } -} - -// adjust3StagePhaseAgg generate 3 stage aggregation for single/multi count distinct if applicable. -// -// select count(distinct a), count(b) from foo -// -// will generate plan: -// -// HashAgg sum(#1), sum(#2) -> final agg -// +- Exchange Passthrough -// +- HashAgg count(distinct a) #1, sum(#3) #2 -> middle agg -// +- Exchange HashPartition by a -// +- HashAgg count(b) #3, group by a -> partial agg -// +- TableScan foo -// -// select count(distinct a), count(distinct b), count(c) from foo -// -// will generate plan: -// -// HashAgg sum(#1), sum(#2), sum(#3) -> final agg -// +- Exchange Passthrough -// +- HashAgg count(distinct a) #1, count(distinct b) #2, sum(#4) #3 -> middle agg -// +- Exchange HashPartition by a,b,groupingID -// +- HashAgg count(c) #4, group by a,b,groupingID -> partial agg -// +- Expand {}, {} -> expand -// +- TableScan foo -func (p *PhysicalHashAgg) adjust3StagePhaseAgg(partialAgg, finalAgg PhysicalPlan, canUse3StageAgg bool, - groupingSets expression.GroupingSets, mpp *mppTask) (final, mid, part, proj4Part PhysicalPlan, _ error) { - if !(partialAgg != nil && canUse3StageAgg) { - // quick path: return the original finalAgg and partiAgg. - return finalAgg, nil, partialAgg, nil, nil - } - if len(groupingSets) == 0 { - // single distinct agg mode. - clonedAgg, err := finalAgg.Clone() - if err != nil { - return nil, nil, nil, nil, err - } - - // step1: adjust middle agg. - middleHashAgg := clonedAgg.(*PhysicalHashAgg) - distinctPos := 0 - middleSchema := expression.NewSchema() - schemaMap := make(map[int64]*expression.Column, len(middleHashAgg.AggFuncs)) - for i, fun := range middleHashAgg.AggFuncs { - col := &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: fun.RetTp, - } - if fun.HasDistinct { - distinctPos = i - fun.Mode = aggregation.Partial1Mode - } else { - fun.Mode = aggregation.Partial2Mode - originalCol := fun.Args[0].(*expression.Column) - // mapping the current partial output column with the agg origin arg column. (final agg arg should use this one) - schemaMap[originalCol.UniqueID] = col - } - middleSchema.Append(col) - } - middleHashAgg.schema = middleSchema - - // step2: adjust final agg. - finalHashAgg := finalAgg.(*PhysicalHashAgg) - finalAggDescs := make([]*aggregation.AggFuncDesc, 0, len(finalHashAgg.AggFuncs)) - for i, fun := range finalHashAgg.AggFuncs { - newArgs := make([]expression.Expression, 0, 1) - if distinctPos == i { - // change count(distinct) to sum() - fun.Name = ast.AggFuncSum - fun.HasDistinct = false - newArgs = append(newArgs, middleSchema.Columns[i]) - } else { - for _, arg := range fun.Args { - newCol, err := arg.RemapColumn(schemaMap) - if err != nil { - return nil, nil, nil, nil, err - } - newArgs = append(newArgs, newCol) - } - } - fun.Mode = aggregation.FinalMode - fun.Args = newArgs - finalAggDescs = append(finalAggDescs, fun) - } - finalHashAgg.AggFuncs = finalAggDescs - // partialAgg is im-mutated from args. - return finalHashAgg, middleHashAgg, partialAgg, nil, nil - } - // multi distinct agg mode, having grouping sets. - // set the default expression to constant 1 for the convenience to choose default group set data. - var groupingIDCol expression.Expression - // enforce Expand operator above the children. - // physical plan is enumerated without children from itself, use mpp subtree instead p.children. - // scale(len(groupingSets)) will change the NDV, while Expand doesn't change the NDV and groupNDV. - stats := mpp.p.StatsInfo().Scale(float64(1)) - stats.RowCount = stats.RowCount * float64(len(groupingSets)) - physicalExpand := PhysicalExpand{ - GroupingSets: groupingSets, - }.Init(p.SCtx(), stats, mpp.p.SelectBlockOffset()) - // generate a new column as groupingID to identify which this row is targeting for. - tp := types.NewFieldType(mysql.TypeLonglong) - tp.SetFlag(mysql.UnsignedFlag | mysql.NotNullFlag) - groupingIDCol = &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: tp, - } - // append the physical expand op with groupingID column. - physicalExpand.SetSchema(mpp.p.Schema().Clone()) - physicalExpand.schema.Append(groupingIDCol.(*expression.Column)) - physicalExpand.GroupingIDCol = groupingIDCol.(*expression.Column) - // attach PhysicalExpand to mpp - attachPlan2Task(physicalExpand, mpp) - - // having group sets - clonedAgg, err := finalAgg.Clone() - if err != nil { - return nil, nil, nil, nil, err - } - cloneHashAgg := clonedAgg.(*PhysicalHashAgg) - // Clone(), it will share same base-plan elements from the finalAgg, including id,tp,stats. Make a new one here. - cloneHashAgg.Plan = base.NewBasePlan(cloneHashAgg.SCtx(), cloneHashAgg.TP(), cloneHashAgg.SelectBlockOffset()) - cloneHashAgg.SetStats(finalAgg.StatsInfo()) // reuse the final agg stats here. - - // step1: adjust partial agg, for normal agg here, adjust it to target for specified group data. - // Since we may substitute the first arg of normal agg with case-when expression here, append a - // customized proj here rather than depending on postOptimize to insert a blunt one for us. - // - // proj4Partial output all the base col from lower op + caseWhen proj cols. - proj4Partial := new(PhysicalProjection).Init(p.SCtx(), mpp.p.StatsInfo(), mpp.p.SelectBlockOffset()) - for _, col := range mpp.p.Schema().Columns { - proj4Partial.Exprs = append(proj4Partial.Exprs, col) - } - proj4Partial.SetSchema(mpp.p.Schema().Clone()) - - partialHashAgg := partialAgg.(*PhysicalHashAgg) - partialHashAgg.GroupByItems = append(partialHashAgg.GroupByItems, groupingIDCol) - partialHashAgg.schema.Append(groupingIDCol.(*expression.Column)) - // it will create a new stats for partial agg. - partialHashAgg.scaleStats4GroupingSets(groupingSets, groupingIDCol.(*expression.Column), proj4Partial.Schema(), proj4Partial.StatsInfo()) - for _, fun := range partialHashAgg.AggFuncs { - if !fun.HasDistinct { - // for normal agg phase1, we should also modify them to target for specified group data. - // Expr = (case when groupingID = targeted_groupingID then arg else null end) - eqExpr := expression.NewFunctionInternal(p.SCtx(), ast.EQ, types.NewFieldType(mysql.TypeTiny), groupingIDCol, expression.NewUInt64Const(fun.GroupingID)) - caseWhen := expression.NewFunctionInternal(p.SCtx(), ast.Case, fun.Args[0].GetType(), eqExpr, fun.Args[0], expression.NewNull()) - caseWhenProjCol := &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: fun.Args[0].GetType(), - } - proj4Partial.Exprs = append(proj4Partial.Exprs, caseWhen) - proj4Partial.Schema().Append(caseWhenProjCol) - fun.Args[0] = caseWhenProjCol - } - } - - // step2: adjust middle agg - // middleHashAgg shared the same stats with the final agg does. - middleHashAgg := cloneHashAgg - middleSchema := expression.NewSchema() - schemaMap := make(map[int64]*expression.Column, len(middleHashAgg.AggFuncs)) - for _, fun := range middleHashAgg.AggFuncs { - col := &expression.Column{ - UniqueID: p.SCtx().GetSessionVars().AllocPlanColumnID(), - RetType: fun.RetTp, - } - if fun.HasDistinct { - // let count distinct agg aggregate on whole-scope data rather using case-when expr to target on specified group. (agg null strict attribute) - fun.Mode = aggregation.Partial1Mode - } else { - fun.Mode = aggregation.Partial2Mode - originalCol := fun.Args[0].(*expression.Column) - // record the origin column unique id down before change it to be case when expr. - // mapping the current partial output column with the agg origin arg column. (final agg arg should use this one) - schemaMap[originalCol.UniqueID] = col - } - middleSchema.Append(col) - } - middleHashAgg.schema = middleSchema - - // step3: adjust final agg - finalHashAgg := finalAgg.(*PhysicalHashAgg) - finalAggDescs := make([]*aggregation.AggFuncDesc, 0, len(finalHashAgg.AggFuncs)) - for i, fun := range finalHashAgg.AggFuncs { - newArgs := make([]expression.Expression, 0, 1) - if fun.HasDistinct { - // change count(distinct) agg to sum() - fun.Name = ast.AggFuncSum - fun.HasDistinct = false - // count(distinct a,b) -> become a single partial result col. - newArgs = append(newArgs, middleSchema.Columns[i]) - } else { - // remap final normal agg args to be output schema of middle normal agg. - for _, arg := range fun.Args { - newCol, err := arg.RemapColumn(schemaMap) - if err != nil { - return nil, nil, nil, nil, err - } - newArgs = append(newArgs, newCol) - } - } - fun.Mode = aggregation.FinalMode - fun.Args = newArgs - fun.GroupingID = 0 - finalAggDescs = append(finalAggDescs, fun) - } - finalHashAgg.AggFuncs = finalAggDescs - return finalHashAgg, middleHashAgg, partialHashAgg, proj4Partial, nil -} - -func (p *PhysicalHashAgg) attach2TaskForMpp(tasks ...task) task { - t := tasks[0].copy() - mpp, ok := t.(*mppTask) - if !ok { - return invalidTask - } - switch p.MppRunMode { - case Mpp1Phase: - // 1-phase agg: when the partition columns can be satisfied, where the plan does not need to enforce Exchange - // only push down the original agg - proj := p.convertAvgForMPP() - attachPlan2Task(p, mpp) - if proj != nil { - attachPlan2Task(proj, mpp) - } - return mpp - case Mpp2Phase: - // TODO: when partition property is matched by sub-plan, we actually needn't do extra an exchange and final agg. - proj := p.convertAvgForMPP() - partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, true) - if partialAgg == nil { - return invalidTask - } - attachPlan2Task(partialAgg, mpp) - partitionCols := p.MppPartitionCols - if len(partitionCols) == 0 { - items := finalAgg.(*PhysicalHashAgg).GroupByItems - partitionCols = make([]*property.MPPPartitionColumn, 0, len(items)) - for _, expr := range items { - col, ok := expr.(*expression.Column) - if !ok { - return invalidTask - } - partitionCols = append(partitionCols, &property.MPPPartitionColumn{ - Col: col, - CollateID: property.GetCollateIDByNameForPartition(col.GetType().GetCollate()), - }) - } - } - prop := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.HashType, MPPPartitionCols: partitionCols} - newMpp := mpp.enforceExchangerImpl(prop) - if newMpp.invalid() { - return newMpp - } - attachPlan2Task(finalAgg, newMpp) - // TODO: how to set 2-phase cost? - if proj != nil { - attachPlan2Task(proj, newMpp) - } - return newMpp - case MppTiDB: - partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, false) - if partialAgg != nil { - attachPlan2Task(partialAgg, mpp) - } - t = mpp.convertToRootTask(p.SCtx()) - attachPlan2Task(finalAgg, t) - return t - case MppScalar: - prop := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.SinglePartitionType} - if !mpp.needEnforceExchanger(prop) { - // On the one hand: when the low layer already satisfied the single partition layout, just do the all agg computation in the single node. - return p.attach2TaskForMpp1Phase(mpp) - } - // On the other hand: try to split the mppScalar agg into multi phases agg **down** to multi nodes since data already distributed across nodes. - // we have to check it before the content of p has been modified - canUse3StageAgg, groupingSets := p.scale3StageForDistinctAgg() - proj := p.convertAvgForMPP() - partialAgg, finalAgg := p.newPartialAggregate(kv.TiFlash, true) - if finalAgg == nil { - return invalidTask - } - - final, middle, partial, proj4Partial, err := p.adjust3StagePhaseAgg(partialAgg, finalAgg, canUse3StageAgg, groupingSets, mpp) - if err != nil { - return invalidTask - } - - // partial agg proj would be null if one scalar agg cannot run in two-phase mode - if proj4Partial != nil { - attachPlan2Task(proj4Partial, mpp) - } - - // partial agg would be null if one scalar agg cannot run in two-phase mode - if partial != nil { - attachPlan2Task(partial, mpp) - } - - if middle != nil && canUse3StageAgg { - items := partial.(*PhysicalHashAgg).GroupByItems - partitionCols := make([]*property.MPPPartitionColumn, 0, len(items)) - for _, expr := range items { - col, ok := expr.(*expression.Column) - if !ok { - continue - } - partitionCols = append(partitionCols, &property.MPPPartitionColumn{ - Col: col, - CollateID: property.GetCollateIDByNameForPartition(col.GetType().GetCollate()), - }) - } - - exProp := &property.PhysicalProperty{TaskTp: property.MppTaskType, ExpectedCnt: math.MaxFloat64, MPPPartitionTp: property.HashType, MPPPartitionCols: partitionCols} - newMpp := mpp.enforceExchanger(exProp) - attachPlan2Task(middle, newMpp) - mpp = newMpp - } - - // prop here still be the first generated single-partition requirement. - newMpp := mpp.enforceExchanger(prop) - attachPlan2Task(final, newMpp) - if proj == nil { - proj = PhysicalProjection{ - Exprs: make([]expression.Expression, 0, len(p.Schema().Columns)), - }.Init(p.SCtx(), p.StatsInfo(), p.SelectBlockOffset()) - for _, col := range p.Schema().Columns { - proj.Exprs = append(proj.Exprs, col) - } - proj.SetSchema(p.schema) - } - attachPlan2Task(proj, newMpp) - return newMpp - default: - return invalidTask - } -} - -func (p *PhysicalHashAgg) attach2Task(tasks ...task) task { - t := tasks[0].copy() - final := p - if cop, ok := t.(*copTask); ok { - if len(cop.rootTaskConds) == 0 && len(cop.idxMergePartPlans) == 0 { - copTaskType := cop.getStoreType() - partialAgg, finalAgg := p.newPartialAggregate(copTaskType, false) - if finalAgg != nil { - final = finalAgg.(*PhysicalHashAgg) - } - if partialAgg != nil { - if cop.tablePlan != nil { - cop.finishIndexPlan() - partialAgg.SetChildren(cop.tablePlan) - cop.tablePlan = partialAgg - // If needExtraProj is true, a projection will be created above the PhysicalIndexLookUpReader to make sure - // the schema is the same as the original DataSource schema. - // However, we pushed down the agg here, the partial agg was placed on the top of tablePlan, and the final - // agg will be placed above the PhysicalIndexLookUpReader, and the schema will be set correctly for them. - // If we add the projection again, the projection will be between the PhysicalIndexLookUpReader and - // the partial agg, and the schema will be broken. - cop.needExtraProj = false - } else { - partialAgg.SetChildren(cop.indexPlan) - cop.indexPlan = partialAgg - } - } - // In `newPartialAggregate`, we are using stats of final aggregation as stats - // of `partialAgg`, so the network cost of transferring result rows of `partialAgg` - // to TiDB is normally under-estimated for hash aggregation, since the group-by - // column may be independent of the column used for region distribution, so a closer - // estimation of network cost for hash aggregation may multiply the number of - // regions involved in the `partialAgg`, which is unknown however. - t = cop.convertToRootTask(p.SCtx()) - attachPlan2Task(finalAgg, t) - } else { - t = cop.convertToRootTask(p.SCtx()) - attachPlan2Task(p, t) - } - } else if _, ok := t.(*mppTask); ok { - return final.attach2TaskForMpp(tasks...) - } else { - attachPlan2Task(p, t) - } - return t -} - -func (p *PhysicalWindow) attach2TaskForMPP(mpp *mppTask) task { - // FIXME: currently, tiflash's join has different schema with TiDB, - // so we have to rebuild the schema of join and operators which may inherit schema from join. - // for window, we take the sub-plan's schema, and the schema generated by windowDescs. - columns := p.Schema().Clone().Columns[len(p.Schema().Columns)-len(p.WindowFuncDescs):] - p.schema = expression.MergeSchema(mpp.plan().Schema(), expression.NewSchema(columns...)) - - failpoint.Inject("CheckMPPWindowSchemaLength", func() { - if len(p.Schema().Columns) != len(mpp.plan().Schema().Columns)+len(p.WindowFuncDescs) { - panic("mpp physical window has incorrect schema length") - } - }) - - return attachPlan2Task(p, mpp) -} - -func (p *PhysicalWindow) attach2Task(tasks ...task) task { - if mpp, ok := tasks[0].copy().(*mppTask); ok && p.storeTp == kv.TiFlash { - return p.attach2TaskForMPP(mpp) - } - t := tasks[0].convertToRootTask(p.SCtx()) - return attachPlan2Task(p.self, t) -} - -func (p *PhysicalCTEStorage) attach2Task(tasks ...task) task { - t := tasks[0].copy() - if mpp, ok := t.(*mppTask); ok { - p.SetChildren(t.plan()) - return &mppTask{ - p: p, - partTp: mpp.partTp, - hashCols: mpp.hashCols, - tblColHists: mpp.tblColHists, - } - } - t.convertToRootTask(p.SCtx()) - p.SetChildren(t.plan()) - return &rootTask{ - p: p, - } -} - -func (p *PhysicalSequence) attach2Task(tasks ...task) task { - for _, t := range tasks { - _, isMpp := t.(*mppTask) - if !isMpp { - return tasks[len(tasks)-1] - } - } - - lastTask := tasks[len(tasks)-1].(*mppTask) - - children := make([]PhysicalPlan, 0, len(tasks)) - for _, t := range tasks { - children = append(children, t.plan()) - } - - p.SetChildren(children...) - - mppTask := &mppTask{ - p: p, - partTp: lastTask.partTp, - hashCols: lastTask.hashCols, - tblColHists: lastTask.tblColHists, - } - return mppTask -} - -// mppTask can not : -// 1. keep order -// 2. support double read -// 3. consider virtual columns. -// 4. TODO: partition prune after close -type mppTask struct { - p PhysicalPlan - - partTp property.MPPPartitionType - hashCols []*property.MPPPartitionColumn - - // rootTaskConds record filters of TableScan that cannot be pushed down to TiFlash. - - // For logical plan like: HashAgg -> Selection -> TableScan, if filters in Selection cannot be pushed to TiFlash. - // Planner will generate physical plan like: PhysicalHashAgg -> PhysicalSelection -> TableReader -> PhysicalTableScan(cop tiflash) - // Because planner will make mppTask invalid directly then use copTask directly. - - // But in DisaggregatedTiFlash mode, cop and batchCop protocol is disabled, so we have to consider this situation for mppTask. - // When generating PhysicalTableScan, if prop.TaskTp is RootTaskType, mppTask will be converted to rootTask, - // and filters in rootTaskConds will be added in a Selection which will be executed in TiDB. - // So physical plan be like: PhysicalHashAgg -> PhysicalSelection -> TableReader -> ExchangeSender -> PhysicalTableScan(mpp tiflash) - rootTaskConds []expression.Expression - tblColHists *statistics.HistColl -} - -func (t *mppTask) count() float64 { - return t.p.StatsInfo().RowCount -} - -func (t *mppTask) copy() task { - nt := *t - return &nt -} - -func (t *mppTask) plan() PhysicalPlan { - return t.p -} - -func (t *mppTask) invalid() bool { - return t.p == nil -} - -func (t *mppTask) convertToRootTask(ctx sessionctx.Context) *rootTask { - return t.copy().(*mppTask).convertToRootTaskImpl(ctx) -} - -// MemoryUsage return the memory usage of mppTask -func (t *mppTask) MemoryUsage() (sum int64) { - if t == nil { - return - } - - sum = size.SizeOfInterface + size.SizeOfInt + size.SizeOfSlice + int64(cap(t.hashCols))*size.SizeOfPointer - if t.p != nil { - sum += t.p.MemoryUsage() - } - return -} - -func collectPartitionInfosFromMPPPlan(p *PhysicalTableReader, mppPlan PhysicalPlan) { - switch x := mppPlan.(type) { - case *PhysicalTableScan: - p.PartitionInfos = append(p.PartitionInfos, tableScanAndPartitionInfo{x, x.PartitionInfo}) - default: - for _, ch := range mppPlan.Children() { - collectPartitionInfosFromMPPPlan(p, ch) - } - } -} - -func collectRowSizeFromMPPPlan(mppPlan PhysicalPlan) (rowSize float64) { - if mppPlan != nil && mppPlan.StatsInfo() != nil && mppPlan.StatsInfo().HistColl != nil { - return cardinality.GetAvgRowSize(mppPlan.SCtx(), mppPlan.StatsInfo().HistColl, mppPlan.Schema().Columns, false, false) - } - return 1 // use 1 as lower-bound for safety -} - -func accumulateNetSeekCost4MPP(p PhysicalPlan) (cost float64) { - if ts, ok := p.(*PhysicalTableScan); ok { - return float64(len(ts.Ranges)) * float64(len(ts.Columns)) * ts.SCtx().GetSessionVars().GetSeekFactor(ts.Table) - } - for _, c := range p.Children() { - cost += accumulateNetSeekCost4MPP(c) - } - return -} - -func tryExpandVirtualColumn(p PhysicalPlan) { - if ts, ok := p.(*PhysicalTableScan); ok { - ts.Columns = ExpandVirtualColumn(ts.Columns, ts.schema, ts.Table.Columns) - return - } - for _, child := range p.Children() { - tryExpandVirtualColumn(child) - } -} - -func (t *mppTask) convertToRootTaskImpl(ctx sessionctx.Context) *rootTask { - // In disaggregated-tiflash mode, need to consider generated column. - tryExpandVirtualColumn(t.p) - sender := PhysicalExchangeSender{ - ExchangeType: tipb.ExchangeType_PassThrough, - }.Init(ctx, t.p.StatsInfo()) - sender.SetChildren(t.p) - - p := PhysicalTableReader{ - tablePlan: sender, - StoreType: kv.TiFlash, - }.Init(ctx, t.p.SelectBlockOffset()) - p.SetStats(t.p.StatsInfo()) - collectPartitionInfosFromMPPPlan(p, t.p) - rt := &rootTask{ - p: p, - } - - if len(t.rootTaskConds) > 0 { - // Some Filter cannot be pushed down to TiFlash, need to add Selection in rootTask, - // so this Selection will be executed in TiDB. - _, isTableScan := t.p.(*PhysicalTableScan) - _, isSelection := t.p.(*PhysicalSelection) - if isSelection { - _, isTableScan = t.p.Children()[0].(*PhysicalTableScan) - } - if !isTableScan { - // Need to make sure oriTaskPlan is TableScan, because rootTaskConds is part of TableScan.FilterCondition. - // It's only for TableScan. This is ensured by converting mppTask to rootTask just after TableScan is built, - // so no other operators are added into this mppTask. - logutil.BgLogger().Error("expect Selection or TableScan for mppTask.p", zap.String("mppTask.p", t.p.TP())) - return invalidTask - } - selectivity, _, err := cardinality.Selectivity(ctx, t.tblColHists, t.rootTaskConds, nil) - if err != nil { - logutil.BgLogger().Debug("calculate selectivity failed, use selection factor", zap.Error(err)) - selectivity = SelectionFactor - } - sel := PhysicalSelection{Conditions: t.rootTaskConds}.Init(ctx, rt.p.StatsInfo().Scale(selectivity), rt.p.SelectBlockOffset()) - sel.fromDataSource = true - sel.SetChildren(rt.p) - rt.p = sel - } - return rt -} - -func (t *mppTask) needEnforceExchanger(prop *property.PhysicalProperty) bool { - switch prop.MPPPartitionTp { - case property.AnyType: - return false - case property.BroadcastType: - return true - case property.SinglePartitionType: - return t.partTp != property.SinglePartitionType - default: - if t.partTp != property.HashType { - return true - } - // TODO: consider equalivant class - // TODO: `prop.IsSubsetOf` is enough, instead of equal. - // for example, if already partitioned by hash(B,C), then same (A,B,C) must distribute on a same node. - if len(prop.MPPPartitionCols) != len(t.hashCols) { - return true - } - for i, col := range prop.MPPPartitionCols { - if !col.Equal(t.hashCols[i]) { - return true - } - } - return false - } -} - -func (t *mppTask) enforceExchanger(prop *property.PhysicalProperty) *mppTask { - if !t.needEnforceExchanger(prop) { - return t - } - return t.copy().(*mppTask).enforceExchangerImpl(prop) -} - -func (t *mppTask) enforceExchangerImpl(prop *property.PhysicalProperty) *mppTask { - if collate.NewCollationEnabled() && !t.p.SCtx().GetSessionVars().HashExchangeWithNewCollation && prop.MPPPartitionTp == property.HashType { - for _, col := range prop.MPPPartitionCols { - if types.IsString(col.Col.RetType.GetType()) { - t.p.SCtx().GetSessionVars().RaiseWarningWhenMPPEnforced("MPP mode may be blocked because when `new_collation_enabled` is true, HashJoin or HashAgg with string key is not supported now.") - return &mppTask{} - } - } - } - ctx := t.p.SCtx() - sender := PhysicalExchangeSender{ - ExchangeType: prop.MPPPartitionTp.ToExchangeType(), - HashCols: prop.MPPPartitionCols, - }.Init(ctx, t.p.StatsInfo()) - - if ctx.GetSessionVars().ChooseMppVersion() >= kv.MppVersionV1 { - sender.CompressionMode = ctx.GetSessionVars().ChooseMppExchangeCompressionMode() - } - - sender.SetChildren(t.p) - receiver := PhysicalExchangeReceiver{}.Init(ctx, t.p.StatsInfo()) - receiver.SetChildren(sender) - return &mppTask{ - p: receiver, - partTp: prop.MPPPartitionTp, - hashCols: prop.MPPPartitionCols, - } -} diff --git a/planner/core/telemetry.go b/planner/core/telemetry.go deleted file mode 100644 index bf1ff2af43bc7..0000000000000 --- a/planner/core/telemetry.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package core - -import ( - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/plancodec" -) - -// IsTiFlashContained returns whether the plan contains TiFlash related executors. -func IsTiFlashContained(plan Plan) (tiFlashPushDown, tiFlashExchangePushDown bool) { - if plan == nil { - return - } - var tiflashProcess func(p Plan) - tiflashProcess = func(p Plan) { - if exp, isExplain := p.(*Explain); isExplain { - p = exp.TargetPlan - if p == nil { - return - } - } - pp, isPhysical := p.(PhysicalPlan) - if !isPhysical { - return - } - if tableReader, ok := pp.(*PhysicalTableReader); ok { - tiFlashPushDown = tableReader.StoreType == kv.TiFlash - if tiFlashPushDown && tableReader.GetTablePlan().TP() == plancodec.TypeExchangeSender { - tiFlashExchangePushDown = true - } - return - } - for _, child := range pp.Children() { - tiflashProcess(child) - if tiFlashPushDown { - return - } - } - } - tiflashProcess(plan) - return -} diff --git a/planner/core/tests/prepare/BUILD.bazel b/planner/core/tests/prepare/BUILD.bazel deleted file mode 100644 index 9ac01f7d0dc90..0000000000000 --- a/planner/core/tests/prepare/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "prepare_test", - timeout = "short", - srcs = [ - "main_test.go", - "prepare_test.go", - ], - flaky = True, - shard_count = 21, - deps = [ - "//errno", - "//executor", - "//expression", - "//infoschema", - "//kv", - "//metrics", - "//parser", - "//parser/auth", - "//planner/core", - "//session", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//util/hint", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/tests/prepare/issue/BUILD.bazel b/planner/core/tests/prepare/issue/BUILD.bazel deleted file mode 100644 index 968601e0a311a..0000000000000 --- a/planner/core/tests/prepare/issue/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "issue_test", - timeout = "short", - srcs = [ - "issue_test.go", - "main_test.go", - ], - flaky = True, - deps = [ - "//parser/auth", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/core/tests/prepare/issue/main_test.go b/planner/core/tests/prepare/issue/main_test.go deleted file mode 100644 index beb509faa6971..0000000000000 --- a/planner/core/tests/prepare/issue/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package issue - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/planner/core/tests/prepare/main_test.go b/planner/core/tests/prepare/main_test.go deleted file mode 100644 index 33a6798f6259a..0000000000000 --- a/planner/core/tests/prepare/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package prepare - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - flag.Parse() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/planner/core/trace.go b/planner/core/trace.go deleted file mode 100644 index 759adcf6bb67c..0000000000000 --- a/planner/core/trace.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package core - -import ( - "github.com/pingcap/tidb/parser/ast" -) - -// Trace represents a trace plan. -type Trace struct { - baseSchemaProducer - - StmtNode ast.StmtNode - Format string - - // OptimizerTrace indicates `trace plan target = 'xxx' ` case - OptimizerTrace bool - OptimizerTraceTarget string -} diff --git a/planner/core/util.go b/planner/core/util.go deleted file mode 100644 index d456fe10374bf..0000000000000 --- a/planner/core/util.go +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package core - -import ( - "fmt" - "slices" - "sort" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/core/internal/base" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/set" - "github.com/pingcap/tidb/util/size" -) - -// AggregateFuncExtractor visits Expr tree. -// It collects AggregateFuncExpr from AST Node. -type AggregateFuncExtractor struct { - // skipAggMap stores correlated aggregate functions which have been built in outer query, - // so extractor in sub-query will skip these aggregate functions. - skipAggMap map[*ast.AggregateFuncExpr]*expression.CorrelatedColumn - // AggFuncs is the collected AggregateFuncExprs. - AggFuncs []*ast.AggregateFuncExpr -} - -// Enter implements Visitor interface. -func (*AggregateFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { - switch n.(type) { - case *ast.SelectStmt, *ast.SetOprStmt: - return n, true - } - return n, false -} - -// Leave implements Visitor interface. -func (a *AggregateFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { - //nolint: revive - switch v := n.(type) { - case *ast.AggregateFuncExpr: - if _, ok := a.skipAggMap[v]; !ok { - a.AggFuncs = append(a.AggFuncs, v) - } - } - return n, true -} - -// WindowFuncExtractor visits Expr tree. -// It converts ColunmNameExpr to WindowFuncExpr and collects WindowFuncExpr. -type WindowFuncExtractor struct { - // WindowFuncs is the collected WindowFuncExprs. - windowFuncs []*ast.WindowFuncExpr -} - -// Enter implements Visitor interface. -func (*WindowFuncExtractor) Enter(n ast.Node) (ast.Node, bool) { - switch n.(type) { - case *ast.SelectStmt, *ast.SetOprStmt: - return n, true - } - return n, false -} - -// Leave implements Visitor interface. -func (a *WindowFuncExtractor) Leave(n ast.Node) (ast.Node, bool) { - //nolint: revive - switch v := n.(type) { - case *ast.WindowFuncExpr: - a.windowFuncs = append(a.windowFuncs, v) - } - return n, true -} - -// logicalSchemaProducer stores the schema for the logical plans who can produce schema directly. -type logicalSchemaProducer struct { - schema *expression.Schema - names types.NameSlice - baseLogicalPlan -} - -// Schema implements the Plan.Schema interface. -func (s *logicalSchemaProducer) Schema() *expression.Schema { - if s.schema == nil { - if len(s.Children()) == 1 { - // default implementation for plans has only one child: proprgate child schema. - // multi-children plans are likely to have particular implementation. - s.schema = s.Children()[0].Schema().Clone() - } else { - s.schema = expression.NewSchema() - } - } - return s.schema -} - -func (s *logicalSchemaProducer) OutputNames() types.NameSlice { - if s.names == nil && len(s.Children()) == 1 { - // default implementation for plans has only one child: proprgate child `OutputNames`. - // multi-children plans are likely to have particular implementation. - s.names = s.Children()[0].OutputNames() - } - return s.names -} - -func (s *logicalSchemaProducer) SetOutputNames(names types.NameSlice) { - s.names = names -} - -// SetSchema implements the Plan.SetSchema interface. -func (s *logicalSchemaProducer) SetSchema(schema *expression.Schema) { - s.schema = schema -} - -func (s *logicalSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { - s.schema = schema - s.names = names -} - -// inlineProjection prunes unneeded columns inline a executor. -func (s *logicalSchemaProducer) inlineProjection(parentUsedCols []*expression.Column, opt *logicalOptimizeOp) { - prunedColumns := make([]*expression.Column, 0) - used := expression.GetUsedList(parentUsedCols, s.Schema()) - for i := len(used) - 1; i >= 0; i-- { - if !used[i] { - prunedColumns = append(prunedColumns, s.Schema().Columns[i]) - s.schema.Columns = append(s.Schema().Columns[:i], s.Schema().Columns[i+1:]...) - } - } - appendColumnPruneTraceStep(s.self, prunedColumns, opt) -} - -// physicalSchemaProducer stores the schema for the physical plans who can produce schema directly. -type physicalSchemaProducer struct { - schema *expression.Schema - basePhysicalPlan -} - -func (s *physicalSchemaProducer) cloneWithSelf(newSelf PhysicalPlan) (*physicalSchemaProducer, error) { - base, err := s.basePhysicalPlan.cloneWithSelf(newSelf) - if err != nil { - return nil, err - } - return &physicalSchemaProducer{ - basePhysicalPlan: *base, - schema: s.Schema().Clone(), - }, nil -} - -// Schema implements the Plan.Schema interface. -func (s *physicalSchemaProducer) Schema() *expression.Schema { - if s.schema == nil { - if len(s.Children()) == 1 { - // default implementation for plans has only one child: proprgate child schema. - // multi-children plans are likely to have particular implementation. - s.schema = s.Children()[0].Schema().Clone() - } else { - s.schema = expression.NewSchema() - } - } - return s.schema -} - -// SetSchema implements the Plan.SetSchema interface. -func (s *physicalSchemaProducer) SetSchema(schema *expression.Schema) { - s.schema = schema -} - -// MemoryUsage return the memory usage of physicalSchemaProducer -func (s *physicalSchemaProducer) MemoryUsage() (sum int64) { - if s == nil { - return - } - - sum = s.basePhysicalPlan.MemoryUsage() + size.SizeOfPointer - return -} - -// baseSchemaProducer stores the schema for the base plans who can produce schema directly. -type baseSchemaProducer struct { - schema *expression.Schema - names types.NameSlice - base.Plan -} - -// OutputNames returns the outputting names of each column. -func (s *baseSchemaProducer) OutputNames() types.NameSlice { - return s.names -} - -func (s *baseSchemaProducer) SetOutputNames(names types.NameSlice) { - s.names = names -} - -// Schema implements the Plan.Schema interface. -func (s *baseSchemaProducer) Schema() *expression.Schema { - if s.schema == nil { - s.schema = expression.NewSchema() - } - return s.schema -} - -// SetSchema implements the Plan.SetSchema interface. -func (s *baseSchemaProducer) SetSchema(schema *expression.Schema) { - s.schema = schema -} - -func (s *baseSchemaProducer) setSchemaAndNames(schema *expression.Schema, names types.NameSlice) { - s.schema = schema - s.names = names -} - -// MemoryUsage return the memory usage of baseSchemaProducer -func (s *baseSchemaProducer) MemoryUsage() (sum int64) { - if s == nil { - return - } - - sum = size.SizeOfPointer + size.SizeOfSlice + int64(cap(s.names))*size.SizeOfPointer + s.Plan.MemoryUsage() - if s.schema != nil { - sum += s.schema.MemoryUsage() - } - for _, name := range s.names { - sum += name.MemoryUsage() - } - return -} - -// Schema implements the Plan.Schema interface. -func (p *LogicalMaxOneRow) Schema() *expression.Schema { - s := p.Children()[0].Schema().Clone() - resetNotNullFlag(s, 0, s.Len()) - return s -} - -func buildLogicalJoinSchema(joinType JoinType, join LogicalPlan) *expression.Schema { - leftSchema := join.Children()[0].Schema() - switch joinType { - case SemiJoin, AntiSemiJoin: - return leftSchema.Clone() - case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: - newSchema := leftSchema.Clone() - newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) - return newSchema - } - newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema()) - if joinType == LeftOuterJoin { - resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len()) - } else if joinType == RightOuterJoin { - resetNotNullFlag(newSchema, 0, leftSchema.Len()) - } - return newSchema -} - -// BuildPhysicalJoinSchema builds the schema of PhysicalJoin from it's children's schema. -func BuildPhysicalJoinSchema(joinType JoinType, join PhysicalPlan) *expression.Schema { - leftSchema := join.Children()[0].Schema() - switch joinType { - case SemiJoin, AntiSemiJoin: - return leftSchema.Clone() - case LeftOuterSemiJoin, AntiLeftOuterSemiJoin: - newSchema := leftSchema.Clone() - newSchema.Append(join.Schema().Columns[join.Schema().Len()-1]) - return newSchema - } - newSchema := expression.MergeSchema(leftSchema, join.Children()[1].Schema()) - if joinType == LeftOuterJoin { - resetNotNullFlag(newSchema, leftSchema.Len(), newSchema.Len()) - } else if joinType == RightOuterJoin { - resetNotNullFlag(newSchema, 0, leftSchema.Len()) - } - return newSchema -} - -// GetStatsInfoFromFlatPlan gets the statistics info from a FlatPhysicalPlan. -func GetStatsInfoFromFlatPlan(flat *FlatPhysicalPlan) map[string]uint64 { - res := make(map[string]uint64) - for _, op := range flat.Main { - switch p := op.Origin.(type) { - case *PhysicalIndexScan: - if _, ok := res[p.Table.Name.O]; p.StatsInfo() != nil && !ok { - res[p.Table.Name.O] = p.StatsInfo().StatsVersion - } - case *PhysicalTableScan: - if _, ok := res[p.Table.Name.O]; p.StatsInfo() != nil && !ok { - res[p.Table.Name.O] = p.StatsInfo().StatsVersion - } - } - } - return res -} - -// GetStatsInfo gets the statistics info from a physical plan tree. -// Deprecated: FlattenPhysicalPlan() + GetStatsInfoFromFlatPlan() is preferred. -func GetStatsInfo(i interface{}) map[string]uint64 { - if i == nil { - // it's a workaround for https://github.com/pingcap/tidb/issues/17419 - // To entirely fix this, uncomment the assertion in TestPreparedIssue17419 - return nil - } - p := i.(Plan) - var physicalPlan PhysicalPlan - switch x := p.(type) { - case *Insert: - physicalPlan = x.SelectPlan - case *Update: - physicalPlan = x.SelectPlan - case *Delete: - physicalPlan = x.SelectPlan - case PhysicalPlan: - physicalPlan = x - } - - if physicalPlan == nil { - return nil - } - - statsInfos := make(map[string]uint64) - statsInfos = CollectPlanStatsVersion(physicalPlan, statsInfos) - return statsInfos -} - -// extractStringFromStringSet helps extract string info from set.StringSet. -func extractStringFromStringSet(set set.StringSet) string { - if len(set) < 1 { - return "" - } - l := make([]string, 0, len(set)) - for k := range set { - l = append(l, fmt.Sprintf(`"%s"`, k)) - } - slices.Sort(l) - return strings.Join(l, ",") -} - -// extractStringFromStringSlice helps extract string info from []string. -func extractStringFromStringSlice(ss []string) string { - if len(ss) < 1 { - return "" - } - slices.Sort(ss) - return strings.Join(ss, ",") -} - -// extractStringFromUint64Slice helps extract string info from uint64 slice. -func extractStringFromUint64Slice(slice []uint64) string { - if len(slice) < 1 { - return "" - } - l := make([]string, 0, len(slice)) - for _, k := range slice { - l = append(l, fmt.Sprintf(`%d`, k)) - } - slices.Sort(l) - return strings.Join(l, ",") -} - -// extractStringFromBoolSlice helps extract string info from bool slice. -func extractStringFromBoolSlice(slice []bool) string { - if len(slice) < 1 { - return "" - } - l := make([]string, 0, len(slice)) - for _, k := range slice { - l = append(l, fmt.Sprintf(`%t`, k)) - } - slices.Sort(l) - return strings.Join(l, ",") -} - -func tableHasDirtyContent(ctx sessionctx.Context, tableInfo *model.TableInfo) bool { - pi := tableInfo.GetPartitionInfo() - if pi == nil { - return ctx.HasDirtyContent(tableInfo.ID) - } - // Currently, we add UnionScan on every partition even though only one partition's data is changed. - // This is limited by current implementation of Partition Prune. It'll be updated once we modify that part. - for _, partition := range pi.Definitions { - if ctx.HasDirtyContent(partition.ID) { - return true - } - } - return false -} - -func clonePhysicalPlan(plans []PhysicalPlan) ([]PhysicalPlan, error) { - cloned := make([]PhysicalPlan, 0, len(plans)) - for _, p := range plans { - c, err := p.Clone() - if err != nil { - return nil, err - } - cloned = append(cloned, c) - } - return cloned, nil -} - -// GetPhysID returns the physical table ID. -func GetPhysID(tblInfo *model.TableInfo, partitionExpr *tables.PartitionExpr, colPos int, d types.Datum) (int64, error) { - pi := tblInfo.GetPartitionInfo() - if pi == nil { - return tblInfo.ID, nil - } - - if partitionExpr == nil { - return tblInfo.ID, nil - } - - switch pi.Type { - case model.PartitionTypeHash: - intVal := d.GetInt64() - partIdx := mathutil.Abs(intVal % int64(pi.Num)) - return pi.Definitions[partIdx].ID, nil - case model.PartitionTypeKey: - if partitionExpr.ForKeyPruning == nil || - len(pi.Columns) > 1 { - return 0, errors.Errorf("unsupported partition type in BatchGet") - } - newKeyPartExpr := tables.ForKeyPruning{ - KeyPartCols: []*expression.Column{{ - Index: colPos, - UniqueID: partitionExpr.KeyPartCols[0].UniqueID, - }}, - } - partIdx, err := newKeyPartExpr.LocateKeyPartition(pi.Num, []types.Datum{d}) - if err != nil { - return 0, errors.Errorf("unsupported partition type in BatchGet") - } - return pi.Definitions[partIdx].ID, nil - case model.PartitionTypeRange: - // we've check the type assertions in func TryFastPlan - col, ok := partitionExpr.Expr.(*expression.Column) - if !ok { - return 0, errors.Errorf("unsupported partition type in BatchGet") - } - unsigned := mysql.HasUnsignedFlag(col.GetType().GetFlag()) - ranges := partitionExpr.ForRangePruning - length := len(ranges.LessThan) - intVal := d.GetInt64() - partIdx := sort.Search(length, func(i int) bool { - return ranges.Compare(i, intVal, unsigned) > 0 - }) - if partIdx >= 0 && partIdx < length { - return pi.Definitions[partIdx].ID, nil - } - case model.PartitionTypeList: - isNull := false // we've guaranteed this in the build process of either TryFastPlan or buildBatchPointGet - intVal := d.GetInt64() - partIdx := partitionExpr.ForListPruning.LocatePartition(intVal, isNull) - if partIdx >= 0 { - return pi.Definitions[partIdx].ID, nil - } - } - - return 0, errors.Errorf("dual partition") -} diff --git a/planner/funcdep/BUILD.bazel b/planner/funcdep/BUILD.bazel deleted file mode 100644 index 078a14f3272b5..0000000000000 --- a/planner/funcdep/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "funcdep", - srcs = [ - "doc.go", - "fast_int_set.go", - "fd_graph.go", - ], - importpath = "github.com/pingcap/tidb/planner/funcdep", - visibility = ["//visibility:public"], - deps = [ - "//util/logutil", - "@org_golang_x_tools//container/intsets", - ], -) - -go_test( - name = "funcdep_test", - timeout = "short", - srcs = [ - "extract_fd_test.go", - "fast_int_set_bench_test.go", - "fast_int_set_test.go", - "fd_graph_test.go", - "main_test.go", - ], - embed = [":funcdep"], - flaky = True, - shard_count = 15, - deps = [ - "//domain", - "//infoschema", - "//parser", - "//planner/core", - "//sessionctx", - "//sessiontxn", - "//testkit", - "//testkit/testsetup", - "//util/hint", - "@com_github_stretchr_testify//require", - "@org_golang_x_exp//maps", - "@org_golang_x_tools//container/intsets", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/funcdep/main_test.go b/planner/funcdep/main_test.go deleted file mode 100644 index 0e5b0a59c8f46..0000000000000 --- a/planner/funcdep/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package funcdep - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/planner/implementation/BUILD.bazel b/planner/implementation/BUILD.bazel deleted file mode 100644 index da204b5b15c60..0000000000000 --- a/planner/implementation/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "implementation", - srcs = [ - "base.go", - "datasource.go", - "join.go", - "simple_plans.go", - "sort.go", - ], - importpath = "github.com/pingcap/tidb/planner/implementation", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/model", - "//planner/cardinality", - "//planner/core", - "//planner/memo", - "//statistics", - ], -) - -go_test( - name = "implementation_test", - timeout = "short", - srcs = [ - "base_test.go", - "main_test.go", - ], - embed = [":implementation"], - flaky = True, - deps = [ - "//domain", - "//planner/core", - "//planner/memo", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/implementation/base.go b/planner/implementation/base.go deleted file mode 100644 index 24b74a2d4a73e..0000000000000 --- a/planner/implementation/base.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package implementation - -import ( - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" -) - -type baseImpl struct { - cost float64 - plan plannercore.PhysicalPlan -} - -func (impl *baseImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { - impl.cost = 0 - for _, child := range children { - impl.cost += child.GetCost() - } - return impl.cost -} - -func (impl *baseImpl) SetCost(cost float64) { - impl.cost = cost -} - -func (impl *baseImpl) GetCost() float64 { - return impl.cost -} - -func (impl *baseImpl) GetPlan() plannercore.PhysicalPlan { - return impl.plan -} - -func (impl *baseImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { - childrenPlan := make([]plannercore.PhysicalPlan, len(children)) - for i, child := range children { - childrenPlan[i] = child.GetPlan() - } - impl.plan.SetChildren(childrenPlan...) - return impl -} - -func (*baseImpl) ScaleCostLimit(costLimit float64) float64 { - return costLimit -} - -func (*baseImpl) GetCostLimit(costLimit float64, children ...memo.Implementation) float64 { - childrenCost := 0.0 - for _, child := range children { - childrenCost += child.GetCost() - } - return costLimit - childrenCost -} diff --git a/planner/implementation/base_test.go b/planner/implementation/base_test.go deleted file mode 100644 index 265ba730f8936..0000000000000 --- a/planner/implementation/base_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package implementation - -import ( - "testing" - - "github.com/pingcap/tidb/domain" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" -) - -func TestBaseImplementation(t *testing.T) { - defer view.Stop() - sctx := plannercore.MockContext() - defer func() { - domain.GetDomain(sctx).StatsHandle().Close() - }() - p := plannercore.PhysicalLimit{}.Init(sctx, nil, 0, nil) - impl := &baseImpl{plan: p} - require.Equal(t, p, impl.GetPlan()) - - cost := impl.CalcCost(10, []memo.Implementation{}...) - require.Equal(t, 0.0, cost) - require.Equal(t, 0.0, impl.GetCost()) - - impl.SetCost(6.0) - require.Equal(t, 6.0, impl.GetCost()) -} diff --git a/planner/implementation/join.go b/planner/implementation/join.go deleted file mode 100644 index 3c514a26d8a47..0000000000000 --- a/planner/implementation/join.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package implementation - -import ( - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" -) - -// HashJoinImpl is the implementation for PhysicalHashJoin. -type HashJoinImpl struct { - baseImpl -} - -// CalcCost implements Implementation CalcCost interface. -func (impl *HashJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { - hashJoin := impl.plan.(*plannercore.PhysicalHashJoin) - // The children here are only used to calculate the cost. - hashJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) - selfCost := hashJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), false, 0, nil) - impl.cost = selfCost + children[0].GetCost() + children[1].GetCost() - return impl.cost -} - -// AttachChildren implements Implementation AttachChildren interface. -func (impl *HashJoinImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { - hashJoin := impl.plan.(*plannercore.PhysicalHashJoin) - hashJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) - return impl -} - -// NewHashJoinImpl creates a new HashJoinImpl. -func NewHashJoinImpl(hashJoin *plannercore.PhysicalHashJoin) *HashJoinImpl { - return &HashJoinImpl{baseImpl{plan: hashJoin}} -} - -// MergeJoinImpl is the implementation for PhysicalMergeJoin. -type MergeJoinImpl struct { - baseImpl -} - -// CalcCost implements Implementation CalcCost interface. -func (impl *MergeJoinImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { - mergeJoin := impl.plan.(*plannercore.PhysicalMergeJoin) - // The children here are only used to calculate the cost. - mergeJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) - selfCost := mergeJoin.GetCost(children[0].GetPlan().StatsCount(), children[1].GetPlan().StatsCount(), 0) - impl.cost = selfCost + children[0].GetCost() + children[1].GetCost() - return impl.cost -} - -// AttachChildren implements Implementation AttachChildren interface. -func (impl *MergeJoinImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { - mergeJoin := impl.plan.(*plannercore.PhysicalMergeJoin) - mergeJoin.SetChildren(children[0].GetPlan(), children[1].GetPlan()) - return impl -} - -// NewMergeJoinImpl creates a new MergeJoinImpl. -func NewMergeJoinImpl(mergeJoin *plannercore.PhysicalMergeJoin) *MergeJoinImpl { - return &MergeJoinImpl{baseImpl{plan: mergeJoin}} -} diff --git a/planner/implementation/main_test.go b/planner/implementation/main_test.go deleted file mode 100644 index 14e9aa0fff203..0000000000000 --- a/planner/implementation/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package implementation - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/planner/implementation/sort.go b/planner/implementation/sort.go deleted file mode 100644 index 2738ba51b0789..0000000000000 --- a/planner/implementation/sort.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package implementation - -import ( - "math" - - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/memo" -) - -// SortImpl implementation of PhysicalSort. -type SortImpl struct { - baseImpl -} - -// NewSortImpl creates a new sort Implementation. -func NewSortImpl(sort *plannercore.PhysicalSort) *SortImpl { - return &SortImpl{baseImpl{plan: sort}} -} - -// CalcCost calculates the cost of the sort Implementation. -func (impl *SortImpl) CalcCost(_ float64, children ...memo.Implementation) float64 { - cnt := math.Min(children[0].GetPlan().StatsInfo().RowCount, impl.plan.GetChildReqProps(0).ExpectedCnt) - sort := impl.plan.(*plannercore.PhysicalSort) - impl.cost = sort.GetCost(cnt, children[0].GetPlan().Schema()) + children[0].GetCost() - return impl.cost -} - -// AttachChildren implements Implementation AttachChildren interface. -func (impl *SortImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { - sort := impl.plan.(*plannercore.PhysicalSort) - sort.SetChildren(children[0].GetPlan()) - // When the Sort orderByItems contain ScalarFunction, we need - // to inject two Projections below and above the Sort. - impl.plan = plannercore.InjectProjBelowSort(sort, sort.ByItems) - return impl -} - -// NominalSortImpl is the implementation of NominalSort. -type NominalSortImpl struct { - baseImpl -} - -// AttachChildren implements Implementation AttachChildren interface. -func (*NominalSortImpl) AttachChildren(children ...memo.Implementation) memo.Implementation { - return children[0] -} - -// NewNominalSortImpl creates a new NominalSort Implementation. -func NewNominalSortImpl(sort *plannercore.NominalSort) *NominalSortImpl { - return &NominalSortImpl{baseImpl{plan: sort}} -} diff --git a/planner/memo/BUILD.bazel b/planner/memo/BUILD.bazel deleted file mode 100644 index 273981a6a68b8..0000000000000 --- a/planner/memo/BUILD.bazel +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "memo", - srcs = [ - "expr_iterator.go", - "group.go", - "group_expr.go", - "implementation.go", - "pattern.go", - ], - importpath = "github.com/pingcap/tidb/planner/memo", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//planner/core", - "//planner/property", - ], -) - -go_test( - name = "memo_test", - timeout = "short", - srcs = [ - "expr_iterator_test.go", - "group_expr_test.go", - "group_test.go", - "main_test.go", - "pattern_test.go", - ], - embed = [":memo"], - flaky = True, - shard_count = 22, - deps = [ - "//domain", - "//expression", - "//infoschema", - "//parser", - "//parser/model", - "//planner/core", - "//planner/property", - "//sessionctx/variable", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/memo/group.go b/planner/memo/group.go deleted file mode 100644 index a1d2f7c907085..0000000000000 --- a/planner/memo/group.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package memo - -import ( - "container/list" - "fmt" - - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/planner/property" -) - -// EngineType is determined by whether it's above or below `Gather`s. -// Plan will choose the different engine to be implemented/executed on according to its EngineType. -// Different engine may support different operators with different cost, so we should design -// different transformation and implementation rules for each engine. -type EngineType uint - -const ( - // EngineTiDB stands for groups which is above `Gather`s and will be executed in TiDB layer. - EngineTiDB EngineType = 1 << iota - // EngineTiKV stands for groups which is below `Gather`s and will be executed in TiKV layer. - EngineTiKV - // EngineTiFlash stands for groups which is below `Gather`s and will be executed in TiFlash layer. - EngineTiFlash -) - -// EngineTypeSet is the bit set of EngineTypes. -type EngineTypeSet uint - -const ( - // EngineTiDBOnly is the EngineTypeSet for EngineTiDB only. - EngineTiDBOnly = EngineTypeSet(EngineTiDB) - // EngineTiKVOnly is the EngineTypeSet for EngineTiKV only. - EngineTiKVOnly = EngineTypeSet(EngineTiKV) - // EngineTiFlashOnly is the EngineTypeSet for EngineTiFlash only. - EngineTiFlashOnly = EngineTypeSet(EngineTiFlash) - // EngineTiKVOrTiFlash is the EngineTypeSet for (EngineTiKV | EngineTiFlash). - EngineTiKVOrTiFlash = EngineTypeSet(EngineTiKV | EngineTiFlash) - // EngineAll is the EngineTypeSet for all of the EngineTypes. - EngineAll = EngineTypeSet(EngineTiDB | EngineTiKV | EngineTiFlash) -) - -// Contains checks whether the EngineTypeSet contains the EngineType. -func (e EngineTypeSet) Contains(tp EngineType) bool { - return uint(e)&uint(tp) != 0 -} - -// String implements fmt.Stringer interface. -func (e EngineType) String() string { - switch e { - case EngineTiDB: - return "EngineTiDB" - case EngineTiKV: - return "EngineTiKV" - case EngineTiFlash: - return "EngineTiFlash" - } - return "UnknownEngineType" -} - -// ExploreMark is uses to mark whether a Group or GroupExpr has -// been fully explored by a transformation rule batch. -type ExploreMark int - -// SetExplored sets the roundth bit. -func (m *ExploreMark) SetExplored(round int) { - *m |= 1 << round -} - -// SetUnexplored unsets the roundth bit. -func (m *ExploreMark) SetUnexplored(round int) { - *m &= ^(1 << round) -} - -// Explored returns whether the roundth bit has been set. -func (m *ExploreMark) Explored(round int) bool { - return *m&(1< 0 { - sctx.GetSessionVars().StmtCtx.SetSkipPlanCache(errors.Errorf("SET_VAR is used in the SQL")) - } - - txnManger := sessiontxn.GetTxnManager(sctx) - if _, isolationReadContainTiKV := sessVars.IsolationReadEngines[kv.TiKV]; isolationReadContainTiKV { - var fp core.Plan - if fpv, ok := sctx.Value(core.PointPlanKey).(core.PointPlanVal); ok { - // point plan is already tried in a multi-statement query. - fp = fpv.Plan - } else { - fp = core.TryFastPlan(sctx, node) - } - if fp != nil { - return fp, fp.OutputNames(), nil - } - } - if err := txnManger.AdviseWarmup(); err != nil { - return nil, nil, err - } - - enableUseBinding := sessVars.UsePlanBaselines - stmtNode, isStmtNode := node.(ast.StmtNode) - bindRecord, scope, match := matchSQLBinding(sctx, stmtNode) - useBinding := enableUseBinding && isStmtNode && match - if sessVars.StmtCtx.EnableOptimizerDebugTrace { - failpoint.Inject("SetBindingTimeToZero", func(val failpoint.Value) { - if val.(bool) && bindRecord != nil { - bindRecord = bindRecord.Copy() - for i := range bindRecord.Bindings { - bindRecord.Bindings[i].CreateTime = types.ZeroTime - bindRecord.Bindings[i].UpdateTime = types.ZeroTime - } - } - }) - debugtrace.RecordAnyValuesWithNames(sctx, - "Used binding", useBinding, - "Enable binding", enableUseBinding, - "IsStmtNode", isStmtNode, - "Matched", match, - "Scope", scope, - "Matched bindings", bindRecord, - ) - } - if isStmtNode { - // add the extra Limit after matching the bind record - stmtNode = core.TryAddExtraLimit(sctx, stmtNode) - node = stmtNode - } - - // try to get Plan from the NonPrepared Plan Cache - if sctx.GetSessionVars().EnableNonPreparedPlanCache && - isStmtNode && - !useBinding { // TODO: support binding - cachedPlan, names, ok, err := getPlanFromNonPreparedPlanCache(ctx, sctx, stmtNode, is) - if err != nil { - return nil, nil, err - } - if ok { - return cachedPlan, names, nil - } - } - - var ( - names types.NameSlice - bestPlan, bestPlanFromBind core.Plan - chosenBinding bindinfo.Binding - err error - ) - if useBinding { - minCost := math.MaxFloat64 - var bindStmtHints stmtctx.StmtHints - originHints := hint.CollectHint(stmtNode) - // bindRecord must be not nil when coming here, try to find the best binding. - for _, binding := range bindRecord.Bindings { - if !binding.IsBindingEnabled() { - continue - } - if sessVars.StmtCtx.EnableOptimizerDebugTrace { - core.DebugTraceTryBinding(sctx, binding.Hint) - } - metrics.BindUsageCounter.WithLabelValues(scope).Inc() - hint.BindHint(stmtNode, binding.Hint) - curStmtHints, _, curWarns := handleStmtHints(binding.Hint.GetFirstTableHints()) - sessVars.StmtCtx.StmtHints = curStmtHints - // update session var by hint /set_var/ - for name, val := range sessVars.StmtCtx.StmtHints.SetVars { - oldV, err := sessVars.SetSystemVarWithOldValAsRet(name, val) - if err != nil { - sessVars.StmtCtx.AppendWarning(err) - } - sessVars.StmtCtx.AddSetVarHintRestore(name, oldV) - } - plan, curNames, cost, err := optimize(ctx, sctx, node, is) - if err != nil { - binding.Status = bindinfo.Invalid - handleInvalidBindRecord(ctx, sctx, scope, bindinfo.BindRecord{ - OriginalSQL: bindRecord.OriginalSQL, - Db: bindRecord.Db, - Bindings: []bindinfo.Binding{binding}, - }) - continue - } - if cost < minCost { - bindStmtHints, warns, minCost, names, bestPlanFromBind, chosenBinding = curStmtHints, curWarns, cost, curNames, plan, binding - } - } - if bestPlanFromBind == nil { - sessVars.StmtCtx.AppendWarning(errors.New("no plan generated from bindings")) - } else { - bestPlan = bestPlanFromBind - sessVars.StmtCtx.StmtHints = bindStmtHints - for _, warn := range warns { - sessVars.StmtCtx.AppendWarning(warn) - } - sessVars.StmtCtx.BindSQL = chosenBinding.BindSQL - sessVars.FoundInBinding = true - if sessVars.StmtCtx.InVerboseExplain { - sessVars.StmtCtx.AppendNote(errors.Errorf("Using the bindSQL: %v", chosenBinding.BindSQL)) - } else { - sessVars.StmtCtx.AppendExtraNote(errors.Errorf("Using the bindSQL: %v", chosenBinding.BindSQL)) - } - if len(tableHints) > 0 { - sessVars.StmtCtx.AppendWarning(errors.Errorf("The system ignores the hints in the current query and uses the hints specified in the bindSQL: %v", chosenBinding.BindSQL)) - } - } - // Restore the hint to avoid changing the stmt node. - hint.BindHint(stmtNode, originHints) - } - - if sessVars.StmtCtx.EnableOptimizerDebugTrace && bestPlanFromBind != nil { - core.DebugTraceBestBinding(sctx, chosenBinding.Hint) - } - // No plan found from the bindings, or the bindings are ignored. - if bestPlan == nil { - sessVars.StmtCtx.StmtHints = originStmtHints - bestPlan, names, _, err = optimize(ctx, sctx, node, is) - if err != nil { - return nil, nil, err - } - } - - // Add a baseline evolution task if: - // 1. the returned plan is from bindings; - // 2. the query is a select statement; - // 3. the original binding contains no read_from_storage hint; - // 4. the plan when ignoring bindings contains no tiflash hint; - // 5. the pending verified binding has not been added already; - savedStmtHints := sessVars.StmtCtx.StmtHints - defer func() { - sessVars.StmtCtx.StmtHints = savedStmtHints - }() - if sessVars.EvolvePlanBaselines && bestPlanFromBind != nil && - sessVars.SelectLimit == math.MaxUint64 { // do not evolve this query if sql_select_limit is enabled - // Check bestPlanFromBind firstly to avoid nil stmtNode. - if _, ok := stmtNode.(*ast.SelectStmt); ok && !bindRecord.Bindings[0].Hint.ContainTableHint(core.HintReadFromStorage) { - sessVars.StmtCtx.StmtHints = originStmtHints - defPlan, _, _, err := optimize(ctx, sctx, node, is) - if err != nil { - // Ignore this evolution task. - return bestPlan, names, nil - } - defPlanHints := core.GenHintsFromPhysicalPlan(defPlan) - for _, hint := range defPlanHints { - if hint.HintName.String() == core.HintReadFromStorage { - return bestPlan, names, nil - } - } - // The hints generated from the plan do not contain the statement hints of the query, add them back. - for _, off := range originStmtHintsOffs { - defPlanHints = append(defPlanHints, tableHints[off]) - } - defPlanHintsStr := hint.RestoreOptimizerHints(defPlanHints) - binding := bindRecord.FindBinding(defPlanHintsStr) - if binding == nil { - handleEvolveTasks(ctx, sctx, bindRecord, stmtNode, defPlanHintsStr) - } - } - } - - return bestPlan, names, nil -} - -// OptimizeForForeignKeyCascade does optimization and creates a Plan for foreign key cascade. -// The node must be prepared first. -// Compare to Optimize, OptimizeForForeignKeyCascade only build plan by StmtNode, -// doesn't consider plan cache and plan binding, also doesn't do privilege check. -func OptimizeForForeignKeyCascade(ctx context.Context, sctx sessionctx.Context, node ast.StmtNode, is infoschema.InfoSchema) (core.Plan, error) { - builder := planBuilderPool.Get().(*core.PlanBuilder) - defer planBuilderPool.Put(builder.ResetForReuse()) - hintProcessor := &hint.BlockHintProcessor{Ctx: sctx} - builder.Init(sctx, is, hintProcessor) - p, err := builder.Build(ctx, node) - if err != nil { - return nil, err - } - if err := core.CheckTableLock(sctx, is, builder.GetVisitInfo()); err != nil { - return nil, err - } - return p, nil -} - -func allowInReadOnlyMode(sctx sessionctx.Context, node ast.Node) (bool, error) { - pm := privilege.GetPrivilegeManager(sctx) - if pm == nil { - return true, nil - } - roles := sctx.GetSessionVars().ActiveRoles - // allow replication thread - // NOTE: it is required, whether SEM is enabled or not, only user with explicit RESTRICTED_REPLICA_WRITER_ADMIN granted can ignore the restriction, so we need to surpass the case that if SEM is not enabled, SUPER will has all privileges - if pm.HasExplicitlyGrantedDynamicPrivilege(roles, "RESTRICTED_REPLICA_WRITER_ADMIN", false) { - return true, nil - } - - switch node.(type) { - // allow change variables (otherwise can't unset read-only mode) - case *ast.SetStmt, - // allow analyze table - *ast.AnalyzeTableStmt, - *ast.UseStmt, - *ast.ShowStmt, - *ast.CreateBindingStmt, - *ast.DropBindingStmt, - *ast.PrepareStmt, - *ast.BeginStmt, - *ast.RollbackStmt: - return true, nil - case *ast.CommitStmt: - txn, err := sctx.Txn(true) - if err != nil { - return false, err - } - if !txn.IsReadOnly() { - return false, txn.Rollback() - } - return true, nil - } - - vars := sctx.GetSessionVars() - return IsReadOnly(node, vars), nil -} - -var planBuilderPool = sync.Pool{ - New: func() interface{} { - return core.NewPlanBuilder() - }, -} - -// optimizeCnt is a global variable only used for test. -var optimizeCnt int - -func optimize(ctx context.Context, sctx sessionctx.Context, node ast.Node, is infoschema.InfoSchema) (core.Plan, types.NameSlice, float64, error) { - failpoint.Inject("checkOptimizeCountOne", func(val failpoint.Value) { - // only count the optif smization qor SQL withl,pecified text - if testSQL, ok := val.(string); ok && testSQL == node.OriginalText() { - optimizeCnt++ - if optimizeCnt > 1 { - failpoint.Return(nil, nil, 0, errors.New("gofail wrong optimizerCnt error")) - } - } - }) - failpoint.Inject("mockHighLoadForOptimize", func() { - sqlPrefixes := []string{"select"} - topsql.MockHighCPULoad(sctx.GetSessionVars().StmtCtx.OriginalSQL, sqlPrefixes, 10) - }) - sessVars := sctx.GetSessionVars() - if sessVars.StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(sctx) - defer debugtrace.LeaveContextCommon(sctx) - } - - // build logical plan - hintProcessor := &hint.BlockHintProcessor{Ctx: sctx} - node.Accept(hintProcessor) - defer hintProcessor.HandleUnusedViewHints() - builder := planBuilderPool.Get().(*core.PlanBuilder) - defer planBuilderPool.Put(builder.ResetForReuse()) - builder.Init(sctx, is, hintProcessor) - p, err := buildLogicalPlan(ctx, sctx, node, builder) - if err != nil { - return nil, nil, 0, err - } - - activeRoles := sessVars.ActiveRoles - // Check privilege. Maybe it's better to move this to the Preprocess, but - // we need the table information to check privilege, which is collected - // into the visitInfo in the logical plan builder. - if pm := privilege.GetPrivilegeManager(sctx); pm != nil { - visitInfo := core.VisitInfo4PrivCheck(is, node, builder.GetVisitInfo()) - if err := core.CheckPrivilege(activeRoles, pm, visitInfo); err != nil { - return nil, nil, 0, err - } - } - - if err := core.CheckTableLock(sctx, is, builder.GetVisitInfo()); err != nil { - return nil, nil, 0, err - } - - names := p.OutputNames() - - // Handle the non-logical plan statement. - logic, isLogicalPlan := p.(core.LogicalPlan) - if !isLogicalPlan { - return p, names, 0, nil - } - - // Handle the logical plan statement, use cascades planner if enabled. - if sessVars.GetEnableCascadesPlanner() { - finalPlan, cost, err := cascades.DefaultOptimizer.FindBestPlan(sctx, logic) - return finalPlan, names, cost, err - } - - beginOpt := time.Now() - finalPlan, cost, err := core.DoOptimize(ctx, sctx, builder.GetOptFlag(), logic) - // TODO: capture plan replayer here if it matches sql and plan digest - - sessVars.DurationOptimization = time.Since(beginOpt) - return finalPlan, names, cost, err -} - -// OptimizeExecStmt to handle the "execute" statement -func OptimizeExecStmt(ctx context.Context, sctx sessionctx.Context, - execAst *ast.ExecuteStmt, is infoschema.InfoSchema) (core.Plan, types.NameSlice, error) { - builder := planBuilderPool.Get().(*core.PlanBuilder) - defer planBuilderPool.Put(builder.ResetForReuse()) - builder.Init(sctx, is, nil) - - p, err := buildLogicalPlan(ctx, sctx, execAst, builder) - if err != nil { - return nil, nil, err - } - exec, ok := p.(*core.Execute) - if !ok { - return nil, nil, errors.Errorf("invalid result plan type, should be Execute") - } - plan, names, err := core.GetPlanFromSessionPlanCache(ctx, sctx, false, is, exec.PrepStmt, exec.Params) - if err != nil { - return nil, nil, err - } - exec.Plan = plan - exec.SetOutputNames(names) - exec.Stmt = exec.PrepStmt.PreparedAst.Stmt - return exec, names, nil -} - -func buildLogicalPlan(ctx context.Context, sctx sessionctx.Context, node ast.Node, builder *core.PlanBuilder) (core.Plan, error) { - sctx.GetSessionVars().PlanID.Store(0) - sctx.GetSessionVars().PlanColumnID.Store(0) - sctx.GetSessionVars().MapScalarSubQ = nil - sctx.GetSessionVars().MapHashCode2UniqueID4ExtendedCol = nil - - failpoint.Inject("mockRandomPlanID", func() { - sctx.GetSessionVars().PlanID.Store(rand.Int31n(1000)) // nolint:gosec - }) - - // reset fields about rewrite - sctx.GetSessionVars().RewritePhaseInfo.Reset() - beginRewrite := time.Now() - p, err := builder.Build(ctx, node) - if err != nil { - return nil, err - } - sctx.GetSessionVars().RewritePhaseInfo.DurationRewrite = time.Since(beginRewrite) - if exec, ok := p.(*core.Execute); ok && exec.PrepStmt != nil { - sctx.GetSessionVars().StmtCtx.Tables = core.GetDBTableInfo(exec.PrepStmt.VisitInfos) - } else { - sctx.GetSessionVars().StmtCtx.Tables = core.GetDBTableInfo(builder.GetVisitInfo()) - } - return p, nil -} - -// ExtractSelectAndNormalizeDigest extract the select statement and normalize it. -func ExtractSelectAndNormalizeDigest(stmtNode ast.StmtNode, specifiledDB string, forBinding bool) (ast.StmtNode, string, string, error) { - switch x := stmtNode.(type) { - case *ast.ExplainStmt: - // This function is only used to find bind record. - // For some SQLs, such as `explain select * from t`, they will be entered here many times, - // but some of them do not want to obtain bind record. - // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. - // For these cases, we need return "" as normalize SQL and hash. - if len(x.Text()) == 0 { - return x.Stmt, "", "", nil - } - switch x.Stmt.(type) { - case *ast.SelectStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: - var normalizeSQL string - if forBinding { - // Apply additional binding rules if enabled - normalizeSQL = parser.NormalizeForBinding(utilparser.RestoreWithDefaultDB(x.Stmt, specifiledDB, x.Text())) - } else { - normalizeSQL = parser.Normalize(utilparser.RestoreWithDefaultDB(x.Stmt, specifiledDB, x.Text())) - } - normalizeSQL = core.EraseLastSemicolonInSQL(normalizeSQL) - hash := parser.DigestNormalized(normalizeSQL) - return x.Stmt, normalizeSQL, hash.String(), nil - case *ast.SetOprStmt: - core.EraseLastSemicolon(x) - var normalizeExplainSQL string - var explainSQL string - if specifiledDB != "" { - explainSQL = utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text()) - } else { - explainSQL = x.Text() - } - - if forBinding { - // Apply additional binding rules - normalizeExplainSQL = parser.NormalizeForBinding(explainSQL) - } else { - normalizeExplainSQL = parser.Normalize(x.Text()) - } - - idx := strings.Index(normalizeExplainSQL, "select") - parenthesesIdx := strings.Index(normalizeExplainSQL, "(") - if parenthesesIdx != -1 && parenthesesIdx < idx { - idx = parenthesesIdx - } - // If the SQL is `EXPLAIN ((VALUES ROW ()) ORDER BY 1);`, the idx will be -1. - if idx == -1 { - hash := parser.DigestNormalized(normalizeExplainSQL) - return x.Stmt, normalizeExplainSQL, hash.String(), nil - } - normalizeSQL := normalizeExplainSQL[idx:] - hash := parser.DigestNormalized(normalizeSQL) - return x.Stmt, normalizeSQL, hash.String(), nil - } - case *ast.SelectStmt, *ast.SetOprStmt, *ast.DeleteStmt, *ast.UpdateStmt, *ast.InsertStmt: - core.EraseLastSemicolon(x) - // This function is only used to find bind record. - // For some SQLs, such as `explain select * from t`, they will be entered here many times, - // but some of them do not want to obtain bind record. - // The difference between them is whether len(x.Text()) is empty. They cannot be distinguished by stmt.restore. - // For these cases, we need return "" as normalize SQL and hash. - if len(x.Text()) == 0 { - return x, "", "", nil - } - - var normalizedSQL string - var hash *parser.Digest - if forBinding { - // Apply additional binding rules - normalizedSQL, hash = parser.NormalizeDigestForBinding(utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text())) - } else { - normalizedSQL, hash = parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(x, specifiledDB, x.Text())) - } - - return x, normalizedSQL, hash.String(), nil - } - return nil, "", "", nil -} - -func getBindRecord(ctx sessionctx.Context, stmt ast.StmtNode) (*bindinfo.BindRecord, string, error) { - // When the domain is initializing, the bind will be nil. - if ctx.Value(bindinfo.SessionBindInfoKeyType) == nil { - return nil, "", nil - } - stmtNode, normalizedSQL, hash, err := ExtractSelectAndNormalizeDigest(stmt, ctx.GetSessionVars().CurrentDB, true) - if err != nil || stmtNode == nil { - return nil, "", err - } - sessionHandle := ctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) - bindRecord := sessionHandle.GetBindRecord(hash, normalizedSQL, "") - if bindRecord != nil { - if bindRecord.HasEnabledBinding() { - return bindRecord, metrics.ScopeSession, nil - } - return nil, "", nil - } - globalHandle := domain.GetDomain(ctx).BindHandle() - if globalHandle == nil { - return nil, "", nil - } - bindRecord = globalHandle.GetBindRecord(hash, normalizedSQL, "") - return bindRecord, metrics.ScopeGlobal, nil -} - -func handleInvalidBindRecord(ctx context.Context, sctx sessionctx.Context, level string, bindRecord bindinfo.BindRecord) { - sessionHandle := sctx.Value(bindinfo.SessionBindInfoKeyType).(*bindinfo.SessionHandle) - err := sessionHandle.DropBindRecord(bindRecord.OriginalSQL, bindRecord.Db, &bindRecord.Bindings[0]) - if err != nil { - logutil.Logger(ctx).Info("drop session bindings failed") - } - if level == metrics.ScopeSession { - return - } - - globalHandle := domain.GetDomain(sctx).BindHandle() - globalHandle.AddDropInvalidBindTask(&bindRecord) -} - -func handleEvolveTasks(ctx context.Context, sctx sessionctx.Context, br *bindinfo.BindRecord, stmtNode ast.StmtNode, planHint string) { - bindSQL := bindinfo.GenerateBindSQL(ctx, stmtNode, planHint, false, br.Db) - if bindSQL == "" { - return - } - charset, collation := sctx.GetSessionVars().GetCharsetInfo() - _, sqlDigestWithDB := parser.NormalizeDigest(utilparser.RestoreWithDefaultDB(stmtNode, br.Db, br.OriginalSQL)) - binding := bindinfo.Binding{ - BindSQL: bindSQL, - Status: bindinfo.PendingVerify, - Charset: charset, - Collation: collation, - Source: bindinfo.Evolve, - SQLDigest: sqlDigestWithDB.String(), - } - globalHandle := domain.GetDomain(sctx).BindHandle() - globalHandle.AddEvolvePlanTask(br.OriginalSQL, br.Db, binding) -} - -func handleStmtHints(hints []*ast.TableOptimizerHint) (stmtHints stmtctx.StmtHints, offs []int, warns []error) { - if len(hints) == 0 { - return - } - hintOffs := make(map[string]int, len(hints)) - var forceNthPlan *ast.TableOptimizerHint - var memoryQuotaHintCnt, useToJAHintCnt, useCascadesHintCnt, noIndexMergeHintCnt, readReplicaHintCnt, maxExecutionTimeCnt, forceNthPlanCnt, straightJoinHintCnt, resourceGroupHintCnt int - setVars := make(map[string]string) - setVarsOffs := make([]int, 0, len(hints)) - for i, hint := range hints { - switch hint.HintName.L { - case "memory_quota": - hintOffs[hint.HintName.L] = i - memoryQuotaHintCnt++ - case "resource_group": - hintOffs[hint.HintName.L] = i - resourceGroupHintCnt++ - case "use_toja": - hintOffs[hint.HintName.L] = i - useToJAHintCnt++ - case "use_cascades": - hintOffs[hint.HintName.L] = i - useCascadesHintCnt++ - case "no_index_merge": - hintOffs[hint.HintName.L] = i - noIndexMergeHintCnt++ - case "read_consistent_replica": - hintOffs[hint.HintName.L] = i - readReplicaHintCnt++ - case "max_execution_time": - hintOffs[hint.HintName.L] = i - maxExecutionTimeCnt++ - case "nth_plan": - forceNthPlanCnt++ - forceNthPlan = hint - case "straight_join": - hintOffs[hint.HintName.L] = i - straightJoinHintCnt++ - case "set_var": - setVarHint := hint.HintData.(ast.HintSetVar) - - // Not all session variables are permitted for use with SET_VAR - sysVar := variable.GetSysVar(setVarHint.VarName) - if sysVar == nil { - warns = append(warns, core.ErrUnresolvedHintName.GenWithStackByArgs(setVarHint.VarName, hint.HintName.String())) - continue - } - if !sysVar.IsHintUpdatableVerfied { - warns = append(warns, core.ErrNotHintUpdatable.GenWithStackByArgs(setVarHint.VarName)) - } - // If several hints with the same variable name appear in the same statement, the first one is applied and the others are ignored with a warning - if _, ok := setVars[setVarHint.VarName]; ok { - msg := fmt.Sprintf("%s(%s=%s)", hint.HintName.String(), setVarHint.VarName, setVarHint.Value) - warns = append(warns, core.ErrWarnConflictingHint.GenWithStackByArgs(msg)) - continue - } - setVars[setVarHint.VarName] = setVarHint.Value - setVarsOffs = append(setVarsOffs, i) - } - } - stmtHints.OriginalTableHints = hints - stmtHints.SetVars = setVars - - // Handle MEMORY_QUOTA - if memoryQuotaHintCnt != 0 { - memoryQuotaHint := hints[hintOffs["memory_quota"]] - if memoryQuotaHintCnt > 1 { - warn := errors.Errorf("MEMORY_QUOTA() is defined more than once, only the last definition takes effect: MEMORY_QUOTA(%v)", memoryQuotaHint.HintData.(int64)) - warns = append(warns, warn) - } - // Executor use MemoryQuota <= 0 to indicate no memory limit, here use < 0 to handle hint syntax error. - if memoryQuota := memoryQuotaHint.HintData.(int64); memoryQuota < 0 { - delete(hintOffs, "memory_quota") - warn := errors.New("The use of MEMORY_QUOTA hint is invalid, valid usage: MEMORY_QUOTA(10 MB) or MEMORY_QUOTA(10 GB)") - warns = append(warns, warn) - } else { - stmtHints.HasMemQuotaHint = true - stmtHints.MemQuotaQuery = memoryQuota - if memoryQuota == 0 { - warn := errors.New("Setting the MEMORY_QUOTA to 0 means no memory limit") - warns = append(warns, warn) - } - } - } - // Handle USE_TOJA - if useToJAHintCnt != 0 { - useToJAHint := hints[hintOffs["use_toja"]] - if useToJAHintCnt > 1 { - warn := errors.Errorf("USE_TOJA() is defined more than once, only the last definition takes effect: USE_TOJA(%v)", useToJAHint.HintData.(bool)) - warns = append(warns, warn) - } - stmtHints.HasAllowInSubqToJoinAndAggHint = true - stmtHints.AllowInSubqToJoinAndAgg = useToJAHint.HintData.(bool) - } - // Handle USE_CASCADES - if useCascadesHintCnt != 0 { - useCascadesHint := hints[hintOffs["use_cascades"]] - if useCascadesHintCnt > 1 { - warn := errors.Errorf("USE_CASCADES() is defined more than once, only the last definition takes effect: USE_CASCADES(%v)", useCascadesHint.HintData.(bool)) - warns = append(warns, warn) - } - stmtHints.HasEnableCascadesPlannerHint = true - stmtHints.EnableCascadesPlanner = useCascadesHint.HintData.(bool) - } - // Handle NO_INDEX_MERGE - if noIndexMergeHintCnt != 0 { - if noIndexMergeHintCnt > 1 { - warn := errors.New("NO_INDEX_MERGE() is defined more than once, only the last definition takes effect") - warns = append(warns, warn) - } - stmtHints.NoIndexMergeHint = true - } - // Handle straight_join - if straightJoinHintCnt != 0 { - if straightJoinHintCnt > 1 { - warn := errors.New("STRAIGHT_JOIN() is defined more than once, only the last definition takes effect") - warns = append(warns, warn) - } - stmtHints.StraightJoinOrder = true - } - // Handle READ_CONSISTENT_REPLICA - if readReplicaHintCnt != 0 { - if readReplicaHintCnt > 1 { - warn := errors.New("READ_CONSISTENT_REPLICA() is defined more than once, only the last definition takes effect") - warns = append(warns, warn) - } - stmtHints.HasReplicaReadHint = true - stmtHints.ReplicaRead = byte(kv.ReplicaReadFollower) - } - // Handle MAX_EXECUTION_TIME - if maxExecutionTimeCnt != 0 { - maxExecutionTime := hints[hintOffs["max_execution_time"]] - if maxExecutionTimeCnt > 1 { - warn := errors.Errorf("MAX_EXECUTION_TIME() is defined more than once, only the last definition takes effect: MAX_EXECUTION_TIME(%v)", maxExecutionTime.HintData.(uint64)) - warns = append(warns, warn) - } - stmtHints.HasMaxExecutionTime = true - stmtHints.MaxExecutionTime = maxExecutionTime.HintData.(uint64) - } - // Handle RESOURCE_GROUP - if resourceGroupHintCnt != 0 { - resourceGroup := hints[hintOffs["resource_group"]] - if resourceGroupHintCnt > 1 { - warn := errors.Errorf("RESOURCE_GROUP() is defined more than once, only the last definition takes effect: RESOURCE_GROUP(%v)", resourceGroup.HintData.(string)) - warns = append(warns, warn) - } - stmtHints.HasResourceGroup = true - stmtHints.ResourceGroup = resourceGroup.HintData.(string) - } - // Handle NTH_PLAN - if forceNthPlanCnt != 0 { - if forceNthPlanCnt > 1 { - warn := errors.Errorf("NTH_PLAN() is defined more than once, only the last definition takes effect: NTH_PLAN(%v)", forceNthPlan.HintData.(int64)) - warns = append(warns, warn) - } - stmtHints.ForceNthPlan = forceNthPlan.HintData.(int64) - if stmtHints.ForceNthPlan < 1 { - stmtHints.ForceNthPlan = -1 - warn := errors.Errorf("the hintdata for NTH_PLAN() is too small, hint ignored") - warns = append(warns, warn) - } - } else { - stmtHints.ForceNthPlan = -1 - } - for _, off := range hintOffs { - offs = append(offs, off) - } - offs = append(offs, setVarsOffs...) - // let hint is always ordered, it is convenient to human compare and test. - sort.Ints(offs) - return -} - -func init() { - core.OptimizeAstNode = Optimize - core.IsReadOnly = IsReadOnly - core.ExtractSelectAndNormalizeDigest = ExtractSelectAndNormalizeDigest -} diff --git a/planner/property/BUILD.bazel b/planner/property/BUILD.bazel deleted file mode 100644 index 49c793997331e..0000000000000 --- a/planner/property/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "property", - srcs = [ - "logical_property.go", - "physical_property.go", - "stats_info.go", - "task_type.go", - ], - importpath = "github.com/pingcap/tidb/planner/property", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//sessionctx/stmtctx", - "//statistics", - "//util/codec", - "//util/collate", - "//util/size", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-tipb", - ], -) diff --git a/planner/util/BUILD.bazel b/planner/util/BUILD.bazel deleted file mode 100644 index 32807d3579a45..0000000000000 --- a/planner/util/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "util", - srcs = [ - "byitem.go", - "misc.go", - "path.go", - ], - importpath = "github.com/pingcap/tidb/planner/util", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//types", - "//util/collate", - "//util/ranger", - "//util/size", - ], -) - -go_test( - name = "util_test", - timeout = "short", - srcs = [ - "main_test.go", - "path_test.go", - ], - embed = [":util"], - flaky = True, - deps = [ - "//domain", - "//parser/model", - "//planner/core", - "//testkit/testsetup", - "//types", - "//util/collate", - "//util/ranger", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/util/debugtrace/BUILD.bazel b/planner/util/debugtrace/BUILD.bazel deleted file mode 100644 index 3eb198744dd01..0000000000000 --- a/planner/util/debugtrace/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "debugtrace", - srcs = ["base.go"], - importpath = "github.com/pingcap/tidb/planner/util/debugtrace", - visibility = ["//visibility:public"], - deps = ["//sessionctx"], -) diff --git a/planner/util/debugtrace/base.go b/planner/util/debugtrace/base.go deleted file mode 100644 index 4ce0b1b4c8fe1..0000000000000 --- a/planner/util/debugtrace/base.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package debugtrace - -import ( - "bytes" - "encoding/json" - "runtime" - - "github.com/pingcap/tidb/sessionctx" -) - -// OptimizerDebugTraceRoot is for recording the optimizer debug trace. -// Each debug information, which is a "step" and can be any type, is placed -// in a "context" (baseDebugTraceContext) as an interface{}. -// Overall, the debug trace is a tree-like hierarchical structure of baseDebugTraceContext. -// This structure can reflect the function call hierarchy of each step during optimization. -// In the end, the entire recorded baseDebugTraceContext will be marshalled to JSON as the result. -// -// EnterContextCommon and LeaveContextCommon can be used to maintain the context easily. -// Usually you just need to add the code below at the beginning of a function: -// -// if StmtCtx.EnableOptimizerDebugTrace { -// EnterContextCommon(ds.ctx) -// defer LeaveContextCommon(ds.ctx) -// } -// -// To record debug information, AppendStepToCurrentContext and AppendStepWithNameToCurrentContext -// are provided as low-level methods. -// RecordAnyValuesWithNames handles some common logic for better usability, so it should -// be the most commonly used function for recording simple information. -// If the tracing logic is more complicated or need extra MarshalJSON logic, you should implement -// separate logic like in planner/core/debug_trace.go and statistics/debug_trace.go -type OptimizerDebugTraceRoot struct { - traceCtx baseDebugTraceContext - // currentCtx indicates in which baseDebugTraceContext we should record the debug information. - currentCtx *baseDebugTraceContext -} - -// MarshalJSON overrides the default MarshalJSON behavior and marshals the unexported traceCtx. -func (root *OptimizerDebugTraceRoot) MarshalJSON() ([]byte, error) { - return EncodeJSONCommon(root.traceCtx.steps) -} - -// baseDebugTraceContext is the core of the debug trace. -// The steps field can be used to record any information, or point to another baseDebugTraceContext. -type baseDebugTraceContext struct { - name string - steps []interface{} - parentCtx *baseDebugTraceContext -} - -func (c *baseDebugTraceContext) MarshalJSON() ([]byte, error) { - var tmp, content interface{} - if len(c.steps) > 1 { - content = c.steps - } else if len(c.steps) == 1 { - content = c.steps[0] - } - if len(c.name) > 0 { - tmp = map[string]interface{}{ - c.name: content, - } - } else { - tmp = content - } - return EncodeJSONCommon(tmp) -} - -// AppendStepToCurrentContext records debug information to the current context of the debug trace. -func (root *OptimizerDebugTraceRoot) AppendStepToCurrentContext(step interface{}) { - root.currentCtx.steps = append(root.currentCtx.steps, step) -} - -// AppendStepWithNameToCurrentContext records debug information and a name to the current context of the debug trace. -func (root *OptimizerDebugTraceRoot) AppendStepWithNameToCurrentContext(step interface{}, name string) { - tmp := map[string]interface{}{ - name: step, - } - root.currentCtx.steps = append(root.currentCtx.steps, tmp) -} - -// GetOrInitDebugTraceRoot returns the debug trace root. -// If it's not initialized, it will initialize it first. -func GetOrInitDebugTraceRoot(sctx sessionctx.Context) *OptimizerDebugTraceRoot { - stmtCtx := sctx.GetSessionVars().StmtCtx - res, ok := stmtCtx.OptimizerDebugTrace.(*OptimizerDebugTraceRoot) - if !ok || res == nil { - trace := &OptimizerDebugTraceRoot{} - trace.currentCtx = &trace.traceCtx - // Though it's not needed in theory, we set the parent of the top level context to itself for safety. - trace.traceCtx.parentCtx = &trace.traceCtx - stmtCtx.OptimizerDebugTrace = trace - } - return stmtCtx.OptimizerDebugTrace.(*OptimizerDebugTraceRoot) -} - -// EncodeJSONCommon contains some common logic for the debug trace, -// like disabling EscapeHTML and recording error. -func EncodeJSONCommon(input interface{}) ([]byte, error) { - var buf bytes.Buffer - encoder := json.NewEncoder(&buf) - // If we do not set this to false, ">", "<", "&"... will be escaped to "\u003c","\u003e", "\u0026"... - encoder.SetEscapeHTML(false) - err := encoder.Encode(input) - if err != nil { - err = encoder.Encode(err) - } - return buf.Bytes(), err -} - -// EnterContextCommon records the function name of the caller, -// then creates and enter a new context for this debug trace structure. -func EnterContextCommon(sctx sessionctx.Context) { - root := GetOrInitDebugTraceRoot(sctx) - funcName := "Fail to get function name." - pc, _, _, ok := runtime.Caller(1) - if ok { - funcName = runtime.FuncForPC(pc).Name() - } - newCtx := &baseDebugTraceContext{ - name: funcName, - parentCtx: root.currentCtx, - } - root.currentCtx.steps = append(root.currentCtx.steps, newCtx) - root.currentCtx = newCtx -} - -// LeaveContextCommon makes the debug trace goes to its parent context. -func LeaveContextCommon(sctx sessionctx.Context) { - root := GetOrInitDebugTraceRoot(sctx) - root.currentCtx = root.currentCtx.parentCtx -} - -// RecordAnyValuesWithNames is a general debug trace logic for recording some values of any type with a name. -// The vals arguments should be a slice like ["name1", value1, "name2", value2]. -// The names must be string, the values can be any type. -func RecordAnyValuesWithNames( - s sessionctx.Context, - vals ...interface{}, -) { - root := GetOrInitDebugTraceRoot(s) - tmp := make(map[string]interface{}, len(vals)/2) - for i := 0; i < len(vals); i += 2 { - str, _ := vals[i].(string) - val := vals[i+1] - tmp[str] = val - } - root.AppendStepToCurrentContext(tmp) -} diff --git a/planner/util/fixcontrol/BUILD.bazel b/planner/util/fixcontrol/BUILD.bazel deleted file mode 100644 index 588308c0b119d..0000000000000 --- a/planner/util/fixcontrol/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "fixcontrol", - srcs = [ - "get.go", - "set.go", - ], - importpath = "github.com/pingcap/tidb/planner/util/fixcontrol", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "fixcontrol_test", - timeout = "short", - srcs = [ - "fixcontrol_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - deps = [ - ":fixcontrol", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_golang_x_exp//maps", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/planner/util/fixcontrol/main_test.go b/planner/util/fixcontrol/main_test.go deleted file mode 100644 index c15e9ff1ba8f5..0000000000000 --- a/planner/util/fixcontrol/main_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package fixcontrol_test - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - - testDataMap.LoadTestSuiteData("testdata", "fix_control_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/planner/util/main_test.go b/planner/util/main_test.go deleted file mode 100644 index 59738797650c6..0000000000000 --- a/planner/util/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package util - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/planner/util/misc.go b/planner/util/misc.go deleted file mode 100644 index bd67cbbe17b5c..0000000000000 --- a/planner/util/misc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package util - -import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/ranger" -) - -// CloneExprs uses Expression.Clone to clone a slice of Expression. -func CloneExprs(exprs []expression.Expression) []expression.Expression { - cloned := make([]expression.Expression, 0, len(exprs)) - for _, e := range exprs { - cloned = append(cloned, e.Clone()) - } - return cloned -} - -// CloneCols uses (*Column).Clone to clone a slice of *Column. -func CloneCols(cols []*expression.Column) []*expression.Column { - cloned := make([]*expression.Column, 0, len(cols)) - for _, c := range cols { - if c == nil { - cloned = append(cloned, nil) - continue - } - cloned = append(cloned, c.Clone().(*expression.Column)) - } - return cloned -} - -// CloneColInfos uses (*ColumnInfo).Clone to clone a slice of *ColumnInfo. -func CloneColInfos(cols []*model.ColumnInfo) []*model.ColumnInfo { - cloned := make([]*model.ColumnInfo, 0, len(cols)) - for _, c := range cols { - cloned = append(cloned, c.Clone()) - } - return cloned -} - -// CloneRanges uses (*Range).Clone to clone a slice of *Range. -func CloneRanges(ranges []*ranger.Range) []*ranger.Range { - cloned := make([]*ranger.Range, 0, len(ranges)) - for _, r := range ranges { - cloned = append(cloned, r.Clone()) - } - return cloned -} diff --git a/plugin/BUILD.bazel b/plugin/BUILD.bazel deleted file mode 100644 index e7fc2d9c08795..0000000000000 --- a/plugin/BUILD.bazel +++ /dev/null @@ -1,54 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "plugin", - srcs = [ - "audit.go", - "const.go", - "errors.go", - "helper.go", - "plugin.go", - "spi.go", - ], - importpath = "github.com/pingcap/tidb/plugin", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//errno", - "//sessionctx/variable", - "//util", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "plugin_test", - timeout = "short", - srcs = [ - "const_test.go", - "helper_test.go", - "integration_test.go", - "main_test.go", - "plugin_test.go", - "spi_test.go", - ], - embed = [":plugin"], - flaky = True, - shard_count = 11, - deps = [ - "//kv", - "//parser/mysql", - "//server", - "//session", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/plugin/conn_ip_example/BUILD.bazel b/plugin/conn_ip_example/BUILD.bazel deleted file mode 100644 index 4b128e57de662..0000000000000 --- a/plugin/conn_ip_example/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") - -go_library( - name = "conn_ip_example_lib", - srcs = ["conn_ip_example.go"], - importpath = "github.com/pingcap/tidb/plugin/conn_ip_example", - visibility = ["//visibility:private"], - deps = [ - "//plugin", - "//sessionctx/variable", - ], -) - -go_test( - name = "conn_ip_example_test", - timeout = "short", - srcs = [ - "conn_ip_example_test.go", - "main_test.go", - ], - embed = [":conn_ip_example_lib"], - flaky = True, - deps = [ - "//plugin", - "//sessionctx/variable", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/plugin/conn_ip_example/main_test.go b/plugin/conn_ip_example/main_test.go deleted file mode 100644 index 71350c1da3692..0000000000000 --- a/plugin/conn_ip_example/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package main - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/plugin/errors.go b/plugin/errors.go deleted file mode 100644 index 7b7fe758dd430..0000000000000 --- a/plugin/errors.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package plugin - -import ( - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -var ( - errInvalidPluginID = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginID) - errInvalidPluginManifest = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginManifest) - errInvalidPluginName = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginName) - errInvalidPluginVersion = dbterror.ClassPlugin.NewStd(errno.ErrInvalidPluginVersion) - errDuplicatePlugin = dbterror.ClassPlugin.NewStd(errno.ErrDuplicatePlugin) - errRequireVersionCheckFail = dbterror.ClassPlugin.NewStd(errno.ErrRequireVersionCheckFail) -) diff --git a/plugin/integration_test.go b/plugin/integration_test.go deleted file mode 100644 index b7becd0f3abcf..0000000000000 --- a/plugin/integration_test.go +++ /dev/null @@ -1,786 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package plugin_test - -import ( - "context" - "fmt" - "strconv" - "strings" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -// Audit tests cannot run in parallel. -func TestAuditLogNormal(t *testing.T) { - store := testkit.CreateMockStore(t) - sv := server.CreateMockServer(t, store) - defer sv.Close() - conn := server.CreateMockConn(t, sv) - defer conn.Close() - session.DisableStats4Test() - session.SetSchemaLease(0) - - type normalTest struct { - sql string - text string - rows uint64 - stmtType string - dbs string - tables string - cmd string - event plugin.GeneralEvent - resCnt int - } - - tests := []normalTest{ - { - sql: "CREATE DATABASE mynewdatabase", - stmtType: "CreateDatabase", - dbs: "mynewdatabase", - }, - { - sql: "CREATE TABLE t1 (a INT NOT NULL)", - stmtType: "CreateTable", - dbs: "test", - tables: "t1", - }, - { - sql: "CREATE TABLE t2 LIKE t1", - stmtType: "CreateTable", - dbs: "test,test", - tables: "t2,t1", - }, - { - sql: "CREATE INDEX a ON t1 (a)", - stmtType: "CreateIndex", - dbs: "test", - tables: "t1", - }, - { - sql: "CREATE SEQUENCE seq", - stmtType: "other", - dbs: "test", - tables: "seq", - }, - { - sql: " create temporary table t3 (a int)", - stmtType: "CreateTable", - dbs: "test", - tables: "t3", - }, - { - sql: "create global temporary table t4 (a int) on commit delete rows", - stmtType: "CreateTable", - dbs: "test", - tables: "t4", - }, - { - sql: "CREATE VIEW v1 AS SELECT * FROM t1 WHERE a> 2", - stmtType: "CreateView", - dbs: "test,test", - tables: "t1,v1", - }, - { - sql: "USE test", - stmtType: "Use", - }, - { - sql: "DROP DATABASE mynewdatabase", - stmtType: "DropDatabase", - dbs: "mynewdatabase", - }, - { - sql: "SHOW CREATE SEQUENCE seq", - stmtType: "Show", - dbs: "test", - tables: "seq", - }, - { - sql: "DROP SEQUENCE seq", - stmtType: "other", - dbs: "test", - tables: "seq", - }, - { - sql: "DROP TABLE t4", - stmtType: "DropTable", - dbs: "test", - tables: "t4", - }, - { - sql: "DROP VIEW v1", - stmtType: "DropView", - dbs: "test", - tables: "v1", - }, - { - sql: "ALTER TABLE t1 ADD COLUMN c1 INT NOT NULL", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 MODIFY c1 BIGINT", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 ADD INDEX (c1)", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 ALTER INDEX c1 INVISIBLE", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 RENAME INDEX c1 TO c2", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 DROP INDEX c2", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 CHANGE c1 c2 INT", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "ALTER TABLE t1 DROP COLUMN c2", - stmtType: "AlterTable", - dbs: "test", - tables: "t1", - }, - { - sql: "CREATE SESSION BINDING FOR SELECT * FROM t1 WHERE a = 123 USING SELECT * FROM t1 IGNORE INDEX (a) WHERE a = 123", - stmtType: "CreateBinding", - }, - { - sql: "DROP SESSION BINDING FOR SELECT * FROM t1 WHERE a = 123", - stmtType: "DropBinding", - }, - // { - // sql: "LOAD STATS '/tmp/stats.json'", - // stmtType: "other", - // }, - // { - // sql: "DROP STATS t", - // stmtType: "other", - // }, - { - sql: "RENAME TABLE t2 TO t5", - stmtType: "other", - dbs: "test,test", - tables: "t2,t5", - }, - { - sql: "TRUNCATE t1", - stmtType: "TruncateTable", - dbs: "test", - tables: "t1", - }, - // { - // sql: "FLASHBACK TABLE t TO t1", - // stmtType: "other", - // dbs: "test", - // tables: "t1", - // }, - // { - // sql: "RECOVER TABLE t1", - // stmtType: "other", - // dbs: "test", - // tables: "t1,t2", - // }, - { - sql: "ALTER DATABASE test DEFAULT CHARACTER SET = utf8mb4", - stmtType: "other", - dbs: "test", - }, - { - sql: "ADMIN RELOAD opt_rule_blacklist", - stmtType: "other", - }, - // { - // sql: "ADMIN PLUGINS ENABLE audit_test", - // stmtType: "other", - // }, - { - sql: "ADMIN FLUSH bindings", - stmtType: "other", - }, - // { - // sql: "ADMIN REPAIR TABLE t1 CREATE TABLE (id int)", - // stmtType: "other", - // dbs: "test", - // tables: "t1", - // }, - { - sql: "ADMIN SHOW SLOW RECENT 10", - stmtType: "other", - }, - { - sql: "ADMIN SHOW DDL JOBS", - stmtType: "other", - }, - // { - // sql: "ADMIN CANCEL DDL JOBS 1", - // stmtType: "other", - // }, - { - sql: "ADMIN CHECKSUM TABLE t1", - stmtType: "other", - // dbs: "test", - // tables: "t1", - }, - { - sql: "ADMIN CHECK TABLE t1", - stmtType: "other", - // dbs: "test", - // tables: "t1", - }, - { - sql: "ADMIN CHECK INDEX t1 a", - stmtType: "other", - // dbs: "test", - // tables: "t1", - }, - { - sql: "CREATE USER 'newuser' IDENTIFIED BY 'newuserpassword'", - stmtType: "CreateUser", - }, - { - sql: "ALTER USER 'newuser' IDENTIFIED BY 'newnewpassword'", - stmtType: "other", - }, - { - sql: "CREATE ROLE analyticsteam", - stmtType: "CreateUser", - }, - { - sql: "GRANT SELECT ON test.* TO analyticsteam", - stmtType: "Grant", - dbs: "test", - }, - { - sql: "GRANT analyticsteam TO 'newuser'", - stmtType: "other", - }, - { - sql: "SET DEFAULT ROLE analyticsteam TO newuser;", - stmtType: "other", - }, - { - sql: "REVOKE SELECT ON test.* FROM 'analyticsteam'", - stmtType: "Revoke", - dbs: "test", - }, - { - sql: "DROP ROLE analyticsteam", - stmtType: "other", - }, - { - sql: "FLUSH PRIVILEGES", - stmtType: "other", - }, - { - sql: "SET PASSWORD FOR 'newuser' = 'test'", - stmtType: "Set", - }, - // { - // sql: "SET ROLE ALL", - // stmtType: "other", - // }, - { - sql: "DROP USER 'newuser'", - stmtType: "other", - }, - { - sql: "analyze table t1", - stmtType: "AnalyzeTable", - dbs: "test", - tables: "t1", - }, - { - sql: "SPLIT TABLE t1 BETWEEN (0) AND (1000000000) REGIONS 16", - stmtType: "other", - // dbs: "test", - // tables: "t1", - }, - // { - // sql: "BACKUP DATABASE `test` TO '.'", - // stmtType: "other", - // dbs: "test", - // }, - // { - // sql: "RESTORE DATABASE * FROM '.'", - // stmtType: "other", - // }, - // { - // sql: "CHANGE DRAINER TO NODE_STATE ='paused' FOR NODE_ID 'drainer1'", - // stmtType: "other", - // }, - // { - // sql: "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID 'pump1'", - // stmtType: "other", - // }, - { - sql: "BEGIN", - stmtType: "Begin", - }, - { - sql: "ROLLBACK", - stmtType: "Rollback", - }, - { - sql: "START TRANSACTION", - stmtType: "Begin", - }, - { - sql: "COMMIT", - stmtType: "Commit", - }, - // { - // sql: "SHOW DRAINER STATUS", - // stmtType: "Show", - // }, - // { - // sql: "SHOW PUMP STATUS", - // stmtType: "Show", - // }, - // { - // sql: "SHOW GRANTS", - // stmtType: "Show", - // }, - { - sql: "SHOW PROCESSLIST", - stmtType: "Show", - }, - // { - // sql: "SHOW BACKUPS", - // stmtType: "Show", - // }, - // { - // sql: "SHOW RESTORES", - // stmtType: "Show", - // }, - { - sql: "show analyze status", - stmtType: "Show", - }, - { - sql: "SHOW SESSION BINDINGS", - stmtType: "Show", - }, - { - sql: "SHOW BUILTINS", - stmtType: "Show", - }, - { - sql: "SHOW CHARACTER SET", - stmtType: "Show", - }, - { - sql: "SHOW COLLATION", - stmtType: "Show", - }, - { - sql: "show columns from t1", - stmtType: "Show", - }, - { - sql: "show fields from t1", - stmtType: "Show", - }, - // { - // sql: "SHOW CONFIG", - // stmtType: "Show", - // }, - { - sql: "SHOW CREATE TABLE t1", - stmtType: "Show", - dbs: "test", - tables: "t1", - }, - { - sql: "SHOW CREATE USER 'root'", - stmtType: "Show", - }, - { - sql: "SHOW DATABASES", - stmtType: "Show", - }, - { - sql: "SHOW ENGINES", - stmtType: "Show", - }, - { - sql: "SHOW ERRORS", - stmtType: "Show", - }, - { - sql: "SHOW INDEXES FROM t1", - stmtType: "Show", - }, - { - sql: "SHOW MASTER STATUS", - stmtType: "Show", - }, - { - sql: "SHOW PLUGINS", - stmtType: "Show", - }, - { - sql: "show privileges", - stmtType: "Show", - }, - { - sql: "SHOW PROFILES", - stmtType: "Show", - }, - // { - // sql: "SHOW PUMP STATUS", - // stmtType: "Show", - // }, - { - sql: "SHOW SCHEMAS", - stmtType: "Show", - }, - { - sql: "SHOW STATS_HEALTHY", - stmtType: "Show", - dbs: "mysql", - }, - { - sql: "show stats_histograms", - stmtType: "Show", - dbs: "mysql", - tables: "stats_histograms", - }, - { - sql: "show stats_meta", - stmtType: "Show", - dbs: "mysql", - tables: "stats_meta", - }, - { - sql: "show status", - stmtType: "Show", - }, - { - sql: "show table t1 next_row_id", - stmtType: "Show", - dbs: "test", - tables: "t1", - }, - { - sql: "show table t1 regions", - stmtType: "Show", - }, - { - sql: "SHOW TABLE STATUS LIKE 't1'", - stmtType: "Show", - resCnt: 3, // Start + SHOW TABLE + Internal SELECT .. FROM IS.TABLES in current session - }, - { - sql: "SHOW TABLES", - stmtType: "Show", - }, - { - sql: "SHOW VARIABLES", - stmtType: "Show", - }, - { - sql: "SHOW WARNINGS", - stmtType: "Show", - }, - { - sql: "SET @number = 5", - stmtType: "Set", - }, - { - sql: "SET NAMES utf8", - stmtType: "Set", - }, - { - sql: "SET CHARACTER SET utf8mb4", - stmtType: "Set", - }, - { - sql: "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED", - stmtType: "Set", - }, - { - sql: "SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER'", - stmtType: "Set", - }, - { - sql: "PREPARE mystmt FROM 'SELECT ? as num FROM DUAL'", - stmtType: "Prepare", - }, - { - sql: "EXECUTE mystmt USING @number", - text: "SELECT ? as num FROM DUAL", - stmtType: "Select", - }, - { - sql: "DEALLOCATE PREPARE mystmt", - stmtType: "Deallocate", - }, - { - sql: "INSERT INTO t1 VALUES (1), (2)", - stmtType: "Insert", - dbs: "test", - tables: "t1", - rows: 2, - }, - { - sql: "DELETE FROM t1 WHERE a = 2", - stmtType: "Delete", - dbs: "test", - tables: "t1", - rows: 1, - }, - { - sql: "REPLACE INTO t1 VALUES(3)", - stmtType: "Replace", - dbs: "test", - tables: "t1", - rows: 1, - }, - { - sql: "UPDATE t1 SET a=5 WHERE a=1", - stmtType: "Update", - dbs: "test", - tables: "t1", - rows: 1, - }, - { - sql: "DO 1", - stmtType: "other", - }, - // { - // sql: "LOAD DATA LOCAL INFILE 'data.csv' INTO TABLE t1 FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES (id)", - // stmtType: "LoadData", - // dbs: "test", - // tables: "t1", - // }, - { - sql: "SELECT * FROM t1", - stmtType: "Select", - dbs: "test", - tables: "t1", - }, - { - sql: "SELECT 1", - stmtType: "Select", - }, - { - sql: "TABLE t1", - stmtType: "Select", - dbs: "test", - tables: "t1", - }, - { - sql: "EXPLAIN ANALYZE SELECT * FROM t1 WHERE a = 1", - stmtType: "ExplainAnalyzeSQL", - // dbs: "test", - // tables: "t1", - }, - { - sql: "EXPLAIN SELECT * FROM t1", - stmtType: "ExplainSQL", - // dbs: "test", - // tables: "t1", - }, - { - sql: "EXPLAIN SELECT * FROM t1 WHERE a = 1", - stmtType: "ExplainSQL", - // dbs: "test", - // tables: "t1", - }, - { - sql: "DESC SELECT * FROM t1 WHERE a = 1", - stmtType: "ExplainSQL", - // dbs: "test", - // tables: "t1", - }, - { - sql: "DESCRIBE SELECT * FROM t1 WHERE a = 1", - stmtType: "ExplainSQL", - // dbs: "test", - // tables: "t1", - }, - { - sql: "trace format='row' select * from t1", - stmtType: "Trace", - // dbs: "test", - // tables: "t1", - }, - { - sql: "flush status", - stmtType: "other", - }, - { - sql: "FLUSH TABLES", - stmtType: "other", - }, - // { - // sql: "KILL TIDB 2", - // stmtType: "other", - // }, - // { - // sql: "SHUTDOWN", - // stmtType: "Shutdow", - // }, - // { - // sql: "ALTER INSTANCE RELOAD TLS", - // stmtType: "other", - // }, - } - - testResults := make([]normalTest, 0) - dbNames := make([]string, 0) - tableNames := make([]string, 0) - onGeneralEvent := func(ctx context.Context, sctx *variable.SessionVars, event plugin.GeneralEvent, cmd string) { - dbNames = dbNames[:0] - tableNames = tableNames[:0] - for _, value := range sctx.StmtCtx.Tables { - dbNames = append(dbNames, value.DB) - tableNames = append(tableNames, value.Table) - } - audit := normalTest{ - text: sctx.StmtCtx.OriginalSQL, - rows: sctx.StmtCtx.AffectedRows(), - stmtType: sctx.StmtCtx.StmtType, - dbs: strings.Join(dbNames, ","), - tables: strings.Join(tableNames, ","), - cmd: cmd, - event: event, - } - testResults = append(testResults, audit) - } - loadPlugin(t, onGeneralEvent) - defer plugin.Shutdown(context.Background()) - - require.NoError(t, conn.HandleQuery(context.Background(), "use test")) - for _, test := range tests { - testResults = testResults[:0] - errMsg := fmt.Sprintf("statement: %s", test.sql) - query := append([]byte{mysql.ComQuery}, []byte(test.sql)...) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - err := conn.Dispatch(ctx, query) - require.NoError(t, err, errMsg) - resultCount := test.resCnt - if resultCount == 0 { - resultCount = 2 - } - require.Equal(t, resultCount, len(testResults), errMsg) - - result := testResults[0] - require.Equal(t, "Query", result.cmd, errMsg) - require.Equal(t, plugin.Starting, result.event, errMsg) - - result = testResults[resultCount-1] - require.Equal(t, "Query", result.cmd, errMsg) - if test.text == "" { - require.Equal(t, test.sql, result.text, errMsg) - } else { - require.Equal(t, test.text, result.text, errMsg) - } - require.Equal(t, test.rows, result.rows, errMsg) - require.Equal(t, test.stmtType, result.stmtType, errMsg) - require.Equal(t, test.dbs, result.dbs, errMsg) - require.Equal(t, test.tables, result.tables, errMsg) - require.Equal(t, "Query", result.cmd, errMsg) - require.Equal(t, plugin.Completed, result.event, errMsg) - for i := 1; i < resultCount-1; i++ { - result = testResults[i] - require.Equal(t, "Query", result.cmd, errMsg) - require.Equal(t, plugin.Completed, result.event, errMsg) - } - } -} - -func loadPlugin(t *testing.T, onGeneralEvent func(context.Context, *variable.SessionVars, plugin.GeneralEvent, string)) { - ctx := context.Background() - pluginName := "audit_test" - pluginVersion := uint16(1) - pluginSign := pluginName + "-" + strconv.Itoa(int(pluginVersion)) - - cfg := plugin.Config{ - Plugins: []string{pluginSign}, - PluginDir: "", - EnvVersion: map[string]uint16{"go": 1112}, - } - - validate := func(ctx context.Context, manifest *plugin.Manifest) error { - return nil - } - onInit := func(ctx context.Context, manifest *plugin.Manifest) error { - return nil - } - onShutdown := func(ctx context.Context, manifest *plugin.Manifest) error { - return nil - } - onConnectionEvent := func(ctx context.Context, event plugin.ConnectionEvent, info *variable.ConnectionInfo) error { - return nil - } - - // setup load test hook. - loadOne := func(p *plugin.Plugin, dir string, pluginID plugin.ID) (manifest func() *plugin.Manifest, err error) { - return func() *plugin.Manifest { - m := &plugin.AuditManifest{ - Manifest: plugin.Manifest{ - Kind: plugin.Audit, - Name: pluginName, - Version: pluginVersion, - OnInit: onInit, - OnShutdown: onShutdown, - Validate: validate, - }, - OnGeneralEvent: onGeneralEvent, - OnConnectionEvent: onConnectionEvent, - } - return plugin.ExportManifest(m) - }, nil - } - plugin.SetTestHook(loadOne) - - // trigger load. - err := plugin.Load(ctx, cfg) - require.NoErrorf(t, err, "load plugin [%s] fail, error [%s]\n", pluginSign, err) - - err = plugin.Init(ctx, cfg) - require.NoErrorf(t, err, "init plugin [%s] fail, error [%s]\n", pluginSign, err) -} diff --git a/plugin/main_test.go b/plugin/main_test.go deleted file mode 100644 index dd1ac858afbd3..0000000000000 --- a/plugin/main_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package plugin - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("time.Sleep"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/privilege/BUILD.bazel b/privilege/BUILD.bazel deleted file mode 100644 index 23e0682a08622..0000000000000 --- a/privilege/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "privilege", - srcs = ["privilege.go"], - importpath = "github.com/pingcap/tidb/privilege", - visibility = ["//visibility:public"], - deps = [ - "//parser/auth", - "//parser/mysql", - "//privilege/conn", - "//sessionctx", - "//sessionctx/variable", - "//types", - ], -) diff --git a/privilege/conn/BUILD.bazel b/privilege/conn/BUILD.bazel deleted file mode 100644 index 109898cb68c12..0000000000000 --- a/privilege/conn/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "conn", - srcs = ["conn.go"], - importpath = "github.com/pingcap/tidb/privilege/conn", - visibility = ["//visibility:public"], -) diff --git a/privilege/privileges/BUILD.bazel b/privilege/privileges/BUILD.bazel deleted file mode 100644 index cb6b7d75d3650..0000000000000 --- a/privilege/privileges/BUILD.bazel +++ /dev/null @@ -1,88 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "privileges", - srcs = [ - "cache.go", - "errors.go", - "privileges.go", - "tidb_auth_token.go", - ], - importpath = "github.com/pingcap/tidb/privilege/privileges", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//extension", - "//infoschema", - "//kv", - "//parser/ast", - "//parser/auth", - "//parser/mysql", - "//parser/terror", - "//privilege", - "//privilege/conn", - "//privilege/privileges/ldap", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/variable", - "//types", - "//util", - "//util/chunk", - "//util/dbterror", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/sem", - "//util/sqlexec", - "//util/stringutil", - "@com_github_lestrrat_go_jwx_v2//jwk", - "@com_github_lestrrat_go_jwx_v2//jws", - "@com_github_lestrrat_go_jwx_v2//jwt", - "@com_github_lestrrat_go_jwx_v2//jwt/openid", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "privileges_test", - timeout = "short", - srcs = [ - "cache_test.go", - "main_test.go", - "privileges_test.go", - "tidb_auth_token_test.go", - ], - embed = [":privileges"], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//errno", - "//kv", - "//parser/auth", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//privilege", - "//session", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//testkit/testutil", - "//util", - "//util/dbterror/exeerrors", - "//util/hack", - "//util/sem", - "//util/sqlexec", - "@com_github_lestrrat_go_jwx_v2//jwa", - "@com_github_lestrrat_go_jwx_v2//jwk", - "@com_github_lestrrat_go_jwx_v2//jws", - "@com_github_lestrrat_go_jwx_v2//jwt", - "@com_github_lestrrat_go_jwx_v2//jwt/openid", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/privilege/privileges/cache.go b/privilege/privileges/cache.go deleted file mode 100644 index eb89926ad7eb3..0000000000000 --- a/privilege/privileges/cache.go +++ /dev/null @@ -1,1659 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package privileges - -import ( - "bytes" - "cmp" - "context" - "encoding/json" - "fmt" - "net" - "slices" - "strings" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/stringutil" - "go.uber.org/zap" -) - -var ( - userTablePrivilegeMask = computePrivMask(mysql.AllGlobalPrivs) - dbTablePrivilegeMask = computePrivMask(mysql.AllDBPrivs) - tablePrivMask = computePrivMask(mysql.AllTablePrivs) -) - -const globalDBVisible = mysql.CreatePriv | mysql.SelectPriv | mysql.InsertPriv | mysql.UpdatePriv | mysql.DeletePriv | mysql.ShowDBPriv | mysql.DropPriv | mysql.AlterPriv | mysql.IndexPriv | mysql.CreateViewPriv | mysql.ShowViewPriv | mysql.GrantPriv | mysql.TriggerPriv | mysql.ReferencesPriv | mysql.ExecutePriv - -const ( - sqlLoadRoleGraph = "SELECT HIGH_PRIORITY FROM_USER, FROM_HOST, TO_USER, TO_HOST FROM mysql.role_edges" - sqlLoadGlobalPrivTable = "SELECT HIGH_PRIORITY Host,User,Priv FROM mysql.global_priv" - sqlLoadDBTable = "SELECT HIGH_PRIORITY Host,DB,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Index_priv,References_priv,Lock_tables_priv,Create_tmp_table_priv,Event_priv,Create_routine_priv,Alter_routine_priv,Alter_priv,Execute_priv,Create_view_priv,Show_view_priv,Trigger_priv FROM mysql.db ORDER BY host, db, user" - sqlLoadTablePrivTable = "SELECT HIGH_PRIORITY Host,DB,User,Table_name,Grantor,Timestamp,Table_priv,Column_priv FROM mysql.tables_priv" - sqlLoadColumnsPrivTable = "SELECT HIGH_PRIORITY Host,DB,User,Table_name,Column_name,Timestamp,Column_priv FROM mysql.columns_priv" - sqlLoadDefaultRoles = "SELECT HIGH_PRIORITY HOST, USER, DEFAULT_ROLE_HOST, DEFAULT_ROLE_USER FROM mysql.default_roles" - // list of privileges from mysql.Priv2UserCol - sqlLoadUserTable = `SELECT HIGH_PRIORITY Host,User,authentication_string, - Create_priv, Select_priv, Insert_priv, Update_priv, Delete_priv, Show_db_priv, Super_priv, - Create_user_priv,Create_tablespace_priv,Trigger_priv,Drop_priv,Process_priv,Grant_priv, - References_priv,Alter_priv,Execute_priv,Index_priv,Create_view_priv,Show_view_priv, - Create_role_priv,Drop_role_priv,Create_tmp_table_priv,Lock_tables_priv,Create_routine_priv, - Alter_routine_priv,Event_priv,Shutdown_priv,Reload_priv,File_priv,Config_priv,Repl_client_priv,Repl_slave_priv, - Account_locked,Plugin,Token_issuer,User_attributes,password_expired,password_last_changed,password_lifetime FROM mysql.user` - sqlLoadGlobalGrantsTable = `SELECT HIGH_PRIORITY Host,User,Priv,With_Grant_Option FROM mysql.global_grants` -) - -func computePrivMask(privs []mysql.PrivilegeType) mysql.PrivilegeType { - var mask mysql.PrivilegeType - for _, p := range privs { - mask |= p - } - return mask -} - -// baseRecord is used to represent a base record in privilege cache, -// it only store Host and User field, and it should be nested in other record type. -type baseRecord struct { - Host string // max length 60, primary key - User string // max length 32, primary key - - // patChars is compiled from Host, cached for pattern match performance. - patChars []byte - patTypes []byte - - // IPv4 with netmask, cached for host match performance. - hostIPNet *net.IPNet -} - -// MetadataInfo is the User_attributes->>"$.metadata". -type MetadataInfo struct { - Email string -} - -// UserAttributesInfo is the 'User_attributes' in privilege cache. -type UserAttributesInfo struct { - MetadataInfo - PasswordLocking -} - -// UserRecord is used to represent a user record in privilege cache. -type UserRecord struct { - baseRecord - UserAttributesInfo - - AuthenticationString string - Privileges mysql.PrivilegeType - AccountLocked bool // A role record when this field is true - AuthPlugin string - AuthTokenIssuer string - PasswordExpired bool - PasswordLastChanged time.Time - PasswordLifeTime int64 - ResourceGroup string -} - -// NewUserRecord return a UserRecord, only use for unit test. -func NewUserRecord(host, user string) UserRecord { - return UserRecord{ - baseRecord: baseRecord{ - Host: host, - User: user, - }, - } -} - -type globalPrivRecord struct { - baseRecord - - Priv GlobalPrivValue - Broken bool -} - -type dynamicPrivRecord struct { - baseRecord - - PrivilegeName string - GrantOption bool -} - -// SSLType is enum value for GlobalPrivValue.SSLType. -// the value is compatible with MySQL storage json value. -type SSLType int - -const ( - // SslTypeNotSpecified indicates . - SslTypeNotSpecified SSLType = iota - 1 - // SslTypeNone indicates not require use ssl. - SslTypeNone - // SslTypeAny indicates require use ssl but not validate cert. - SslTypeAny - // SslTypeX509 indicates require use ssl and validate cert. - SslTypeX509 - // SslTypeSpecified indicates require use ssl and validate cert's subject or issuer. - SslTypeSpecified -) - -// GlobalPrivValue is store json format for priv column in mysql.global_priv. -type GlobalPrivValue struct { - SSLType SSLType `json:"ssl_type,omitempty"` - SSLCipher string `json:"ssl_cipher,omitempty"` - X509Issuer string `json:"x509_issuer,omitempty"` - X509Subject string `json:"x509_subject,omitempty"` - SAN string `json:"san,omitempty"` - SANs map[util.SANType][]string `json:"-"` -} - -// RequireStr returns describe string after `REQUIRE` clause. -func (g *GlobalPrivValue) RequireStr() string { - require := "NONE" - switch g.SSLType { - case SslTypeAny: - require = "SSL" - case SslTypeX509: - require = "X509" - case SslTypeSpecified: - var s []string - if len(g.SSLCipher) > 0 { - s = append(s, "CIPHER") - s = append(s, "'"+g.SSLCipher+"'") - } - if len(g.X509Issuer) > 0 { - s = append(s, "ISSUER") - s = append(s, "'"+g.X509Issuer+"'") - } - if len(g.X509Subject) > 0 { - s = append(s, "SUBJECT") - s = append(s, "'"+g.X509Subject+"'") - } - if len(g.SAN) > 0 { - s = append(s, "SAN") - s = append(s, "'"+g.SAN+"'") - } - if len(s) > 0 { - require = strings.Join(s, " ") - } - } - return require -} - -type dbRecord struct { - baseRecord - - DB string - Privileges mysql.PrivilegeType - - dbPatChars []byte - dbPatTypes []byte -} - -type tablesPrivRecord struct { - baseRecord - - DB string - TableName string - Grantor string - Timestamp time.Time - TablePriv mysql.PrivilegeType - ColumnPriv mysql.PrivilegeType -} - -type columnsPrivRecord struct { - baseRecord - - DB string - TableName string - ColumnName string - Timestamp time.Time - ColumnPriv mysql.PrivilegeType -} - -// defaultRoleRecord is used to cache mysql.default_roles -type defaultRoleRecord struct { - baseRecord - - DefaultRoleUser string - DefaultRoleHost string -} - -// roleGraphEdgesTable is used to cache relationship between and role. -type roleGraphEdgesTable struct { - roleList map[string]*auth.RoleIdentity -} - -// Find method is used to find role from table -func (g roleGraphEdgesTable) Find(user, host string) bool { - if host == "" { - host = "%" - } - key := user + "@" + host - if g.roleList == nil { - return false - } - _, ok := g.roleList[key] - return ok -} - -// MySQLPrivilege is the in-memory cache of mysql privilege tables. -type MySQLPrivilege struct { - // In MySQL, a user identity consists of a user + host. - // Either portion of user or host can contain wildcards, - // requiring the privileges system to use a list-like - // structure instead of a hash. - - // TiDB contains a sensible behavior difference from MySQL, - // which is that usernames can not contain wildcards. - // This means that DB-records are organized in both a - // slice (p.DB) and a Map (p.DBMap). - - // This helps in the case that there are a number of users with - // non-full privileges (i.e. user.db entries). - User []UserRecord - UserMap map[string][]UserRecord // Accelerate User searching - Global map[string][]globalPrivRecord - Dynamic map[string][]dynamicPrivRecord - DB []dbRecord - DBMap map[string][]dbRecord // Accelerate DB searching - TablesPriv []tablesPrivRecord - TablesPrivMap map[string][]tablesPrivRecord // Accelerate TablesPriv searching - ColumnsPriv []columnsPrivRecord - DefaultRoles []defaultRoleRecord - RoleGraph map[string]roleGraphEdgesTable -} - -// FindAllUserEffectiveRoles is used to find all effective roles grant to this user. -// This method will filter out the roles that are not granted to the user but are still in activeRoles -func (p *MySQLPrivilege) FindAllUserEffectiveRoles(user, host string, activeRoles []*auth.RoleIdentity) []*auth.RoleIdentity { - grantedActiveRoles := make([]*auth.RoleIdentity, 0, len(activeRoles)) - for _, role := range activeRoles { - if p.FindRole(user, host, role) { - grantedActiveRoles = append(grantedActiveRoles, role) - } - } - return p.FindAllRole(grantedActiveRoles) -} - -// FindAllRole is used to find all roles grant to this user. -func (p *MySQLPrivilege) FindAllRole(activeRoles []*auth.RoleIdentity) []*auth.RoleIdentity { - queue, head := make([]*auth.RoleIdentity, 0, len(activeRoles)), 0 - queue = append(queue, activeRoles...) - // Using breadth first search to find all roles grant to this user. - visited, ret := make(map[string]bool), make([]*auth.RoleIdentity, 0) - for head < len(queue) { - role := queue[head] - if _, ok := visited[role.String()]; !ok { - visited[role.String()] = true - ret = append(ret, role) - key := role.Username + "@" + role.Hostname - if edgeTable, ok := p.RoleGraph[key]; ok { - for _, v := range edgeTable.roleList { - if _, ok := visited[v.String()]; !ok { - queue = append(queue, v) - } - } - } - } - head++ - } - return ret -} - -// FindRole is used to detect whether there is edges between users and roles. -func (p *MySQLPrivilege) FindRole(user string, host string, role *auth.RoleIdentity) bool { - rec := p.matchUser(user, host) - r := p.matchUser(role.Username, role.Hostname) - if rec != nil && r != nil { - key := rec.User + "@" + rec.Host - return p.RoleGraph[key].Find(role.Username, role.Hostname) - } - return false -} - -// LoadAll loads the tables from database to memory. -func (p *MySQLPrivilege) LoadAll(ctx sessionctx.Context) error { - err := p.LoadUserTable(ctx) - if err != nil { - logutil.BgLogger().Warn("load mysql.user fail", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.user") - } - - err = p.LoadGlobalPrivTable(ctx) - if err != nil { - return errors.Trace(err) - } - - err = p.LoadGlobalGrantsTable(ctx) - if err != nil { - return errors.Trace(err) - } - - err = p.LoadDBTable(ctx) - if err != nil { - if !noSuchTable(err) { - logutil.BgLogger().Warn("load mysql.db fail", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.db") - } - logutil.BgLogger().Warn("mysql.db maybe missing") - } - - err = p.LoadTablesPrivTable(ctx) - if err != nil { - if !noSuchTable(err) { - logutil.BgLogger().Warn("load mysql.tables_priv fail", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.tables_priv") - } - logutil.BgLogger().Warn("mysql.tables_priv missing") - } - - err = p.LoadDefaultRoles(ctx) - if err != nil { - if !noSuchTable(err) { - logutil.BgLogger().Warn("load mysql.roles", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.roles") - } - logutil.BgLogger().Warn("mysql.default_roles missing") - } - - err = p.LoadColumnsPrivTable(ctx) - if err != nil { - if !noSuchTable(err) { - logutil.BgLogger().Warn("load mysql.columns_priv", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.columns_priv") - } - logutil.BgLogger().Warn("mysql.columns_priv missing") - } - - err = p.LoadRoleGraph(ctx) - if err != nil { - if !noSuchTable(err) { - logutil.BgLogger().Warn("load mysql.role_edges", zap.Error(err)) - return errLoadPrivilege.FastGen("mysql.role_edges") - } - logutil.BgLogger().Warn("mysql.role_edges missing") - } - return nil -} - -func noSuchTable(err error) bool { - e1 := errors.Cause(err) - if e2, ok := e1.(*terror.Error); ok { - if terror.ErrCode(e2.Code()) == terror.ErrCode(mysql.ErrNoSuchTable) { - return true - } - } - return false -} - -// LoadRoleGraph loads the mysql.role_edges table from database. -func (p *MySQLPrivilege) LoadRoleGraph(ctx sessionctx.Context) error { - p.RoleGraph = make(map[string]roleGraphEdgesTable) - err := p.loadTable(ctx, sqlLoadRoleGraph, p.decodeRoleEdgesTable) - if err != nil { - return errors.Trace(err) - } - return nil -} - -// LoadUserTable loads the mysql.user table from database. -func (p *MySQLPrivilege) LoadUserTable(ctx sessionctx.Context) error { - err := p.loadTable(ctx, sqlLoadUserTable, p.decodeUserTableRow) - if err != nil { - return errors.Trace(err) - } - // See https://dev.mysql.com/doc/refman/8.0/en/connection-access.html - // When multiple matches are possible, the server must determine which of them to use. It resolves this issue as follows: - // 1. Whenever the server reads the user table into memory, it sorts the rows. - // 2. When a client attempts to connect, the server looks through the rows in sorted order. - // 3. The server uses the first row that matches the client host name and user name. - // The server uses sorting rules that order rows with the most-specific Host values first. - p.SortUserTable() - p.buildUserMap() - return nil -} - -func (p *MySQLPrivilege) buildUserMap() { - userMap := make(map[string][]UserRecord, len(p.User)) - for _, record := range p.User { - userMap[record.User] = append(userMap[record.User], record) - } - p.UserMap = userMap -} - -func compareBaseRecord(x, y *baseRecord) int { - // Compare two item by user's host first. - c1 := compareHost(x.Host, y.Host) - if c1 != 0 { - return c1 - } - // Then, compare item by user's name value. - return cmp.Compare(x.User, y.User) -} - -func compareUserRecord(x, y UserRecord) int { - return compareBaseRecord(&x.baseRecord, &y.baseRecord) -} - -// compareHost compares two host string using some special rules, return value 1, 0, -1 means > = <. -// TODO: Check how MySQL do it exactly, instead of guess its rules. -func compareHost(x, y string) int { - // The more-specific, the smaller it is. - // The pattern '%' means “any host” and is least specific. - if y == `%` { - if x == `%` { - return 0 - } - return -1 - } - - // The empty string '' also means “any host” but sorts after '%'. - if y == "" { - if x == "" { - return 0 - } - return -1 - } - - // One of them end with `%`. - xEnd := strings.HasSuffix(x, `%`) - yEnd := strings.HasSuffix(y, `%`) - if xEnd || yEnd { - switch { - case !xEnd && yEnd: - return -1 - case xEnd && !yEnd: - return 1 - case xEnd && yEnd: - // 192.168.199.% smaller than 192.168.% - // A not very accurate comparison, compare them by length. - if len(x) > len(y) { - return -1 - } - } - return 0 - } - - // For other case, the order is nondeterministic. - switch x < y { - case true: - return -1 - case false: - return 1 - } - return 0 -} - -// SortUserTable sorts p.User in the MySQLPrivilege struct. -func (p MySQLPrivilege) SortUserTable() { - slices.SortFunc(p.User, compareUserRecord) -} - -// LoadGlobalPrivTable loads the mysql.global_priv table from database. -func (p *MySQLPrivilege) LoadGlobalPrivTable(ctx sessionctx.Context) error { - return p.loadTable(ctx, sqlLoadGlobalPrivTable, p.decodeGlobalPrivTableRow) -} - -// LoadGlobalGrantsTable loads the mysql.global_priv table from database. -func (p *MySQLPrivilege) LoadGlobalGrantsTable(ctx sessionctx.Context) error { - return p.loadTable(ctx, sqlLoadGlobalGrantsTable, p.decodeGlobalGrantsTableRow) -} - -// LoadDBTable loads the mysql.db table from database. -func (p *MySQLPrivilege) LoadDBTable(ctx sessionctx.Context) error { - err := p.loadTable(ctx, sqlLoadDBTable, p.decodeDBTableRow) - if err != nil { - return err - } - p.buildDBMap() - return nil -} - -func compareDBRecord(x, y dbRecord) int { - return compareBaseRecord(&x.baseRecord, &y.baseRecord) -} - -func (p *MySQLPrivilege) buildDBMap() { - dbMap := make(map[string][]dbRecord, len(p.DB)) - for _, record := range p.DB { - dbMap[record.User] = append(dbMap[record.User], record) - } - - // Sort the records to make the matching rule work. - for _, records := range dbMap { - slices.SortFunc(records, compareDBRecord) - } - p.DBMap = dbMap -} - -// LoadTablesPrivTable loads the mysql.tables_priv table from database. -func (p *MySQLPrivilege) LoadTablesPrivTable(ctx sessionctx.Context) error { - err := p.loadTable(ctx, sqlLoadTablePrivTable, p.decodeTablesPrivTableRow) - if err != nil { - return err - } - p.buildTablesPrivMap() - return nil -} - -func (p *MySQLPrivilege) buildTablesPrivMap() { - tablesPrivMap := make(map[string][]tablesPrivRecord, len(p.TablesPriv)) - for _, record := range p.TablesPriv { - tablesPrivMap[record.User] = append(tablesPrivMap[record.User], record) - } - p.TablesPrivMap = tablesPrivMap -} - -// LoadColumnsPrivTable loads the mysql.columns_priv table from database. -func (p *MySQLPrivilege) LoadColumnsPrivTable(ctx sessionctx.Context) error { - return p.loadTable(ctx, sqlLoadColumnsPrivTable, p.decodeColumnsPrivTableRow) -} - -// LoadDefaultRoles loads the mysql.columns_priv table from database. -func (p *MySQLPrivilege) LoadDefaultRoles(ctx sessionctx.Context) error { - return p.loadTable(ctx, sqlLoadDefaultRoles, p.decodeDefaultRoleTableRow) -} - -func (p *MySQLPrivilege) loadTable(sctx sessionctx.Context, sql string, - decodeTableRow func(chunk.Row, []*ast.ResultField) error) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - rs, err := sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rs.Close) - fs := rs.Fields() - req := rs.NewChunk(nil) - for { - err = rs.Next(context.TODO(), req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - return nil - } - it := chunk.NewIterator4Chunk(req) - for row := it.Begin(); row != it.End(); row = it.Next() { - err = decodeTableRow(row, fs) - if err != nil { - return errors.Trace(err) - } - } - // NOTE: decodeTableRow decodes data from a chunk Row, that is a shallow copy. - // The result will reference memory in the chunk, so the chunk must not be reused - // here, otherwise some werid bug will happen! - req = chunk.Renew(req, sctx.GetSessionVars().MaxChunkSize) - } -} - -// parseHostIPNet parses an IPv4 address and its subnet mask (e.g. `127.0.0.0/255.255.255.0`), -// return the `IPNet` struct which represent the IP range info (e.g. `127.0.0.1 ~ 127.0.0.255`). -// `IPNet` is used to check if a giving IP (e.g. `127.0.0.1`) is in its IP range by call `IPNet.Contains(ip)`. -func parseHostIPNet(s string) *net.IPNet { - i := strings.IndexByte(s, '/') - if i < 0 { - return nil - } - hostIP := net.ParseIP(s[:i]).To4() - if hostIP == nil { - return nil - } - maskIP := net.ParseIP(s[i+1:]).To4() - if maskIP == nil { - return nil - } - mask := net.IPv4Mask(maskIP[0], maskIP[1], maskIP[2], maskIP[3]) - // We must ensure that: & == - // e.g. `127.0.0.1/255.0.0.0` is an illegal string, - // because `127.0.0.1` & `255.0.0.0` == `127.0.0.0`, but != `127.0.0.1` - // see https://dev.mysql.com/doc/refman/5.7/en/account-names.html - if !hostIP.Equal(hostIP.Mask(mask)) { - return nil - } - return &net.IPNet{ - IP: hostIP, - Mask: mask, - } -} - -func (record *baseRecord) assignUserOrHost(row chunk.Row, i int, f *ast.ResultField) { - switch f.ColumnAsName.L { - case "user": - record.User = row.GetString(i) - case "host": - record.Host = row.GetString(i) - record.patChars, record.patTypes = stringutil.CompilePatternBytes(record.Host, '\\') - record.hostIPNet = parseHostIPNet(record.Host) - } -} - -func (p *MySQLPrivilege) decodeUserTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value UserRecord - for i, f := range fs { - switch { - case f.ColumnAsName.L == "authentication_string": - value.AuthenticationString = row.GetString(i) - case f.ColumnAsName.L == "account_locked": - if row.GetEnum(i).String() == "Y" { - value.AccountLocked = true - } - case f.ColumnAsName.L == "plugin": - if row.GetString(i) != "" { - value.AuthPlugin = row.GetString(i) - } else { - value.AuthPlugin = mysql.AuthNativePassword - } - case f.ColumnAsName.L == "token_issuer": - value.AuthTokenIssuer = row.GetString(i) - case f.ColumnAsName.L == "user_attributes": - if row.IsNull(i) { - continue - } - bj := row.GetJSON(i) - pathExpr, err := types.ParseJSONPathExpr("$.metadata.email") - if err != nil { - return err - } - if emailBJ, found := bj.Extract([]types.JSONPathExpression{pathExpr}); found { - email, err := emailBJ.Unquote() - if err != nil { - return err - } - value.Email = email - } - pathExpr, err = types.ParseJSONPathExpr("$.resource_group") - if err != nil { - return err - } - if resourceGroup, found := bj.Extract([]types.JSONPathExpression{pathExpr}); found { - resourceGroup, err := resourceGroup.Unquote() - if err != nil { - return err - } - value.ResourceGroup = resourceGroup - } - passwordLocking := PasswordLocking{} - if err := passwordLocking.ParseJSON(bj); err != nil { - return err - } - value.FailedLoginAttempts = passwordLocking.FailedLoginAttempts - value.PasswordLockTimeDays = passwordLocking.PasswordLockTimeDays - value.FailedLoginCount = passwordLocking.FailedLoginCount - value.AutoLockedLastChanged = passwordLocking.AutoLockedLastChanged - value.AutoAccountLocked = passwordLocking.AutoAccountLocked - case f.ColumnAsName.L == "password_expired": - if row.GetEnum(i).String() == "Y" { - value.PasswordExpired = true - } - case f.ColumnAsName.L == "password_last_changed": - t := row.GetTime(i) - gotime, err := t.GoTime(time.Local) - if err != nil { - return err - } - value.PasswordLastChanged = gotime - case f.ColumnAsName.L == "password_lifetime": - if row.IsNull(i) { - value.PasswordLifeTime = -1 - continue - } - value.PasswordLifeTime = row.GetInt64(i) - case f.Column.GetType() == mysql.TypeEnum: - if row.GetEnum(i).String() != "Y" { - continue - } - priv, ok := mysql.Col2PrivType[f.ColumnAsName.O] - if !ok { - return errInvalidPrivilegeType.GenWithStack(f.ColumnAsName.O) - } - value.Privileges |= priv - default: - value.assignUserOrHost(row, i, f) - } - } - p.User = append(p.User, value) - return nil -} - -func (p *MySQLPrivilege) decodeGlobalPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value globalPrivRecord - for i, f := range fs { - if f.ColumnAsName.L == "priv" { - privData := row.GetString(i) - if len(privData) > 0 { - var privValue GlobalPrivValue - err := json.Unmarshal(hack.Slice(privData), &privValue) - if err != nil { - logutil.BgLogger().Error("one user global priv data is broken, forbidden login until data be fixed", - zap.String("user", value.User), zap.String("host", value.Host)) - value.Broken = true - } else { - value.Priv.SSLType = privValue.SSLType - value.Priv.SSLCipher = privValue.SSLCipher - value.Priv.X509Issuer = privValue.X509Issuer - value.Priv.X509Subject = privValue.X509Subject - value.Priv.SAN = privValue.SAN - if len(value.Priv.SAN) > 0 { - value.Priv.SANs, err = util.ParseAndCheckSAN(value.Priv.SAN) - if err != nil { - value.Broken = true - } - } - } - } - } else { - value.assignUserOrHost(row, i, f) - } - } - if p.Global == nil { - p.Global = make(map[string][]globalPrivRecord) - } - p.Global[value.User] = append(p.Global[value.User], value) - return nil -} - -func (p *MySQLPrivilege) decodeGlobalGrantsTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value dynamicPrivRecord - for i, f := range fs { - switch f.ColumnAsName.L { - case "priv": - value.PrivilegeName = strings.ToUpper(row.GetString(i)) - case "with_grant_option": - value.GrantOption = row.GetEnum(i).String() == "Y" - default: - value.assignUserOrHost(row, i, f) - } - } - if p.Dynamic == nil { - p.Dynamic = make(map[string][]dynamicPrivRecord) - } - p.Dynamic[value.User] = append(p.Dynamic[value.User], value) - return nil -} - -func (p *MySQLPrivilege) decodeDBTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value dbRecord - for i, f := range fs { - switch { - case f.ColumnAsName.L == "db": - value.DB = row.GetString(i) - value.dbPatChars, value.dbPatTypes = stringutil.CompilePatternBytes(strings.ToUpper(value.DB), '\\') - case f.Column.GetType() == mysql.TypeEnum: - if row.GetEnum(i).String() != "Y" { - continue - } - priv, ok := mysql.Col2PrivType[f.ColumnAsName.O] - if !ok { - return errInvalidPrivilegeType.GenWithStack("Unknown Privilege Type!") - } - value.Privileges |= priv - default: - value.assignUserOrHost(row, i, f) - } - } - p.DB = append(p.DB, value) - return nil -} - -func (p *MySQLPrivilege) decodeTablesPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value tablesPrivRecord - for i, f := range fs { - switch f.ColumnAsName.L { - case "db": - value.DB = row.GetString(i) - case "table_name": - value.TableName = row.GetString(i) - case "table_priv": - value.TablePriv = decodeSetToPrivilege(row.GetSet(i)) - case "column_priv": - value.ColumnPriv = decodeSetToPrivilege(row.GetSet(i)) - default: - value.assignUserOrHost(row, i, f) - } - } - p.TablesPriv = append(p.TablesPriv, value) - return nil -} - -func (p *MySQLPrivilege) decodeRoleEdgesTable(row chunk.Row, fs []*ast.ResultField) error { - var fromUser, fromHost, toHost, toUser string - for i, f := range fs { - switch f.ColumnAsName.L { - case "from_host": - fromHost = row.GetString(i) - case "from_user": - fromUser = row.GetString(i) - case "to_host": - toHost = row.GetString(i) - case "to_user": - toUser = row.GetString(i) - } - } - fromKey := fromUser + "@" + fromHost - toKey := toUser + "@" + toHost - roleGraph, ok := p.RoleGraph[toKey] - if !ok { - roleGraph = roleGraphEdgesTable{roleList: make(map[string]*auth.RoleIdentity)} - p.RoleGraph[toKey] = roleGraph - } - roleGraph.roleList[fromKey] = &auth.RoleIdentity{Username: fromUser, Hostname: fromHost} - return nil -} - -func (p *MySQLPrivilege) decodeDefaultRoleTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value defaultRoleRecord - for i, f := range fs { - switch f.ColumnAsName.L { - case "default_role_host": - value.DefaultRoleHost = row.GetString(i) - case "default_role_user": - value.DefaultRoleUser = row.GetString(i) - default: - value.assignUserOrHost(row, i, f) - } - } - p.DefaultRoles = append(p.DefaultRoles, value) - return nil -} - -func (p *MySQLPrivilege) decodeColumnsPrivTableRow(row chunk.Row, fs []*ast.ResultField) error { - var value columnsPrivRecord - for i, f := range fs { - switch f.ColumnAsName.L { - case "db": - value.DB = row.GetString(i) - case "table_name": - value.TableName = row.GetString(i) - case "column_name": - value.ColumnName = row.GetString(i) - case "timestamp": - var err error - value.Timestamp, err = row.GetTime(i).GoTime(time.Local) - if err != nil { - return errors.Trace(err) - } - case "column_priv": - value.ColumnPriv = decodeSetToPrivilege(row.GetSet(i)) - default: - value.assignUserOrHost(row, i, f) - } - } - p.ColumnsPriv = append(p.ColumnsPriv, value) - return nil -} - -func decodeSetToPrivilege(s types.Set) mysql.PrivilegeType { - var ret mysql.PrivilegeType - if s.Name == "" { - return ret - } - for _, str := range strings.Split(s.Name, ",") { - priv, ok := mysql.SetStr2Priv[str] - if !ok { - logutil.BgLogger().Warn("unsupported privilege", zap.String("type", str)) - continue - } - ret |= priv - } - return ret -} - -// hostMatch checks if giving IP is in IP range of hostname. -// In MySQL, the hostname of user can be set to `/` -// e.g. `127.0.0.0/255.255.255.0` represent IP range from `127.0.0.1` to `127.0.0.255`, -// only IP addresses that satisfy this condition range can be login with this user. -// See https://dev.mysql.com/doc/refman/5.7/en/account-names.html -func (record *baseRecord) hostMatch(s string) bool { - if record.hostIPNet == nil { - if record.Host == "localhost" && net.ParseIP(s).IsLoopback() { - return true - } - return false - } - ip := net.ParseIP(s).To4() - if ip == nil { - return false - } - return record.hostIPNet.Contains(ip) -} - -func (record *baseRecord) match(user, host string) bool { - return record.User == user && (patternMatch(host, record.patChars, record.patTypes) || - record.hostMatch(host)) -} - -func (record *baseRecord) fullyMatch(user, host string) bool { - return record.User == user && record.Host == host -} - -func (record *dbRecord) match(user, host, db string) bool { - return record.baseRecord.match(user, host) && - patternMatch(strings.ToUpper(db), record.dbPatChars, record.dbPatTypes) -} - -func (record *tablesPrivRecord) match(user, host, db, table string) bool { - return record.baseRecord.match(user, host) && - strings.EqualFold(record.DB, db) && - strings.EqualFold(record.TableName, table) -} - -func (record *columnsPrivRecord) match(user, host, db, table, col string) bool { - return record.baseRecord.match(user, host) && - strings.EqualFold(record.DB, db) && - strings.EqualFold(record.TableName, table) && - strings.EqualFold(record.ColumnName, col) -} - -// patternMatch matches "%" the same way as ".*" in regular expression, for example, -// "10.0.%" would match "10.0.1" "10.0.1.118" ... -func patternMatch(str string, patChars, patTypes []byte) bool { - return stringutil.DoMatchBytes(str, patChars, patTypes) -} - -// matchIdentity finds an identity to match a user + host -// using the correct rules according to MySQL. -func (p *MySQLPrivilege) matchIdentity(user, host string, skipNameResolve bool) *UserRecord { - for i := 0; i < len(p.User); i++ { - record := &p.User[i] - if record.match(user, host) { - return record - } - } - - // If skip-name resolve is not enabled, and the host is not localhost - // we can fallback and try to resolve with all addrs that match. - // TODO: this is imported from previous code in session.Auth(), and can be improved in future. - if !skipNameResolve && host != variable.DefHostname { - addrs, err := net.LookupAddr(host) - if err != nil { - logutil.BgLogger().Warn( - "net.LookupAddr returned an error during auth check", - zap.String("host", host), - zap.Error(err), - ) - return nil - } - for _, addr := range addrs { - for i := 0; i < len(p.User); i++ { - record := &p.User[i] - if record.match(user, addr) { - return record - } - } - } - } - return nil -} - -// matchResoureGroup finds an identity to match resource group. -func (p *MySQLPrivilege) matchResoureGroup(resourceGroupName string) *UserRecord { - for i := 0; i < len(p.User); i++ { - record := &p.User[i] - if record.ResourceGroup == resourceGroupName { - return record - } - } - return nil -} - -// connectionVerification verifies the username + hostname according to exact -// match from the mysql.user privilege table. call matchIdentity() first if you -// do not have an exact match yet. -func (p *MySQLPrivilege) connectionVerification(user, host string) *UserRecord { - records, exists := p.UserMap[user] - if exists { - for i := 0; i < len(records); i++ { - record := &records[i] - if record.Host == host { // exact match - return record - } - } - } - return nil -} - -func (p *MySQLPrivilege) matchGlobalPriv(user, host string) *globalPrivRecord { - uGlobal, exists := p.Global[user] - if !exists { - return nil - } - for i := 0; i < len(uGlobal); i++ { - record := &uGlobal[i] - if record.match(user, host) { - return record - } - } - return nil -} - -func (p *MySQLPrivilege) matchUser(user, host string) *UserRecord { - records, exists := p.UserMap[user] - if exists { - for i := 0; i < len(records); i++ { - record := &records[i] - if record.match(user, host) { - return record - } - } - } - return nil -} - -func (p *MySQLPrivilege) matchDB(user, host, db string) *dbRecord { - records, exists := p.DBMap[user] - if exists { - for i := 0; i < len(records); i++ { - record := &records[i] - if record.match(user, host, db) { - return record - } - } - } - return nil -} - -func (p *MySQLPrivilege) matchTables(user, host, db, table string) *tablesPrivRecord { - records, exists := p.TablesPrivMap[user] - if exists { - for i := 0; i < len(records); i++ { - record := &records[i] - if record.match(user, host, db, table) { - return record - } - } - } - return nil -} - -func (p *MySQLPrivilege) matchColumns(user, host, db, table, column string) *columnsPrivRecord { - for i := 0; i < len(p.ColumnsPriv); i++ { - record := &p.ColumnsPriv[i] - if record.match(user, host, db, table, column) { - return record - } - } - return nil -} - -// HasExplicitlyGrantedDynamicPrivilege checks if a user has a DYNAMIC privilege -// without accepting SUPER privilege as a fallback. -func (p *MySQLPrivilege) HasExplicitlyGrantedDynamicPrivilege(activeRoles []*auth.RoleIdentity, user, host, privName string, withGrant bool) bool { - privName = strings.ToUpper(privName) - roleList := p.FindAllUserEffectiveRoles(user, host, activeRoles) - roleList = append(roleList, &auth.RoleIdentity{Username: user, Hostname: host}) - // Loop through each of the roles and return on first match - // If grantable is required, ensure the record has the GrantOption set. - for _, r := range roleList { - u := r.Username - h := r.Hostname - for _, record := range p.Dynamic[u] { - if record.match(u, h) { - if withGrant && !record.GrantOption { - continue - } - if record.PrivilegeName == privName { - return true - } - } - } - } - return false -} - -// RequestDynamicVerification checks all roles for a specific DYNAMIC privilege. -func (p *MySQLPrivilege) RequestDynamicVerification(activeRoles []*auth.RoleIdentity, user, host, privName string, withGrant bool) bool { - privName = strings.ToUpper(privName) - if p.HasExplicitlyGrantedDynamicPrivilege(activeRoles, user, host, privName, withGrant) { - return true - } - // If SEM is enabled, and the privilege is of type restricted, do not fall through - // To using SUPER as a replacement privilege. - if sem.IsEnabled() && sem.IsRestrictedPrivilege(privName) { - return false - } - // For compatibility reasons, the SUPER privilege also has all DYNAMIC privileges granted to it (dynamic privs are a super replacement) - // This may be changed in future, but will require a bootstrap task to assign all dynamic privileges - // to users with SUPER, otherwise tasks such as BACKUP and ROLE_ADMIN will start to fail. - // The visitInfo system will also need modification to support OR conditions. - if withGrant && !p.RequestVerification(activeRoles, user, host, "", "", "", mysql.GrantPriv) { - return false - } - return p.RequestVerification(activeRoles, user, host, "", "", "", mysql.SuperPriv) -} - -// RequestVerification checks whether the user have sufficient privileges to do the operation. -func (p *MySQLPrivilege) RequestVerification(activeRoles []*auth.RoleIdentity, user, host, db, table, column string, priv mysql.PrivilegeType) bool { - if priv == mysql.UsagePriv { - return true - } - - roleList := p.FindAllUserEffectiveRoles(user, host, activeRoles) - roleList = append(roleList, &auth.RoleIdentity{Username: user, Hostname: host}) - - var userPriv, dbPriv, tablePriv, columnPriv mysql.PrivilegeType - for _, r := range roleList { - userRecord := p.matchUser(r.Username, r.Hostname) - if userRecord != nil { - userPriv |= userRecord.Privileges - } - } - if userPriv&priv > 0 { - return true - } - - for _, r := range roleList { - dbRecord := p.matchDB(r.Username, r.Hostname, db) - if dbRecord != nil { - dbPriv |= dbRecord.Privileges - } - } - if dbPriv&priv > 0 { - return true - } - - for _, r := range roleList { - tableRecord := p.matchTables(r.Username, r.Hostname, db, table) - if tableRecord != nil { - tablePriv |= tableRecord.TablePriv - if column != "" { - columnPriv |= tableRecord.ColumnPriv - } - } - } - if tablePriv&priv > 0 || columnPriv&priv > 0 { - return true - } - - columnPriv = 0 - for _, r := range roleList { - columnRecord := p.matchColumns(r.Username, r.Hostname, db, table, column) - if columnRecord != nil { - columnPriv |= columnRecord.ColumnPriv - } - } - if columnPriv&priv > 0 { - return true - } - - return priv == 0 -} - -// DBIsVisible checks whether the user can see the db. -func (p *MySQLPrivilege) DBIsVisible(user, host, db string) bool { - if record := p.matchUser(user, host); record != nil { - if record.Privileges&globalDBVisible > 0 { - return true - } - // For metrics_schema, `PROCESS` can also work. - if record.Privileges&mysql.ProcessPriv > 0 && strings.EqualFold(db, util.MetricSchemaName.O) { - return true - } - } - - // INFORMATION_SCHEMA is visible to all users. - if strings.EqualFold(db, "INFORMATION_SCHEMA") { - return true - } - - if record := p.matchDB(user, host, db); record != nil { - if record.Privileges > 0 { - return true - } - } - - for _, record := range p.TablesPriv { - if record.baseRecord.match(user, host) && - strings.EqualFold(record.DB, db) { - if record.TablePriv != 0 || record.ColumnPriv != 0 { - return true - } - } - } - - for _, record := range p.ColumnsPriv { - if record.baseRecord.match(user, host) && - strings.EqualFold(record.DB, db) { - if record.ColumnPriv != 0 { - return true - } - } - } - - return false -} - -func (p *MySQLPrivilege) showGrants(ctx sessionctx.Context, user, host string, roles []*auth.RoleIdentity) []string { - var gs []string //nolint: prealloc - var sortFromIdx int - var hasGlobalGrant = false - // Some privileges may granted from role inheritance. - // We should find these inheritance relationship. - allRoles := p.FindAllUserEffectiveRoles(user, host, roles) - // Show global grants. - var currentPriv mysql.PrivilegeType - var userExists = false - // Check whether user exists. - if userList, ok := p.UserMap[user]; ok { - for _, record := range userList { - if record.fullyMatch(user, host) { - userExists = true - break - } - } - if !userExists { - return gs - } - } - var g string - for _, record := range p.User { - if record.fullyMatch(user, host) { - hasGlobalGrant = true - currentPriv |= record.Privileges - } else { - for _, r := range allRoles { - if record.baseRecord.match(r.Username, r.Hostname) { - hasGlobalGrant = true - currentPriv |= record.Privileges - } - } - } - } - g = userPrivToString(currentPriv) - if len(g) > 0 { - var s string - if (currentPriv & mysql.GrantPriv) > 0 { - s = fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s' WITH GRANT OPTION`, g, user, host) - } else { - s = fmt.Sprintf(`GRANT %s ON *.* TO '%s'@'%s'`, g, user, host) - } - gs = append(gs, s) - } - - // This is a mysql convention. - if len(gs) == 0 && hasGlobalGrant { - var s string - if (currentPriv & mysql.GrantPriv) > 0 { - s = fmt.Sprintf("GRANT USAGE ON *.* TO '%s'@'%s' WITH GRANT OPTION", user, host) - } else { - s = fmt.Sprintf("GRANT USAGE ON *.* TO '%s'@'%s'", user, host) - } - gs = append(gs, s) - } - - // Show db scope grants. - sortFromIdx = len(gs) - dbPrivTable := make(map[string]mysql.PrivilegeType) - for _, record := range p.DB { - if record.fullyMatch(user, host) { - dbPrivTable[record.DB] |= record.Privileges - } else { - for _, r := range allRoles { - if record.baseRecord.match(r.Username, r.Hostname) { - dbPrivTable[record.DB] |= record.Privileges - } - } - } - } - - sqlMode := ctx.GetSessionVars().SQLMode - for dbName, priv := range dbPrivTable { - dbName = stringutil.Escape(dbName, sqlMode) - g := dbPrivToString(priv) - if len(g) > 0 { - var s string - if (priv & mysql.GrantPriv) > 0 { - s = fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s' WITH GRANT OPTION`, g, dbName, user, host) - } else { - s = fmt.Sprintf(`GRANT %s ON %s.* TO '%s'@'%s'`, g, dbName, user, host) - } - gs = append(gs, s) - } else if len(g) == 0 && (priv&mysql.GrantPriv) > 0 { - // We have GRANT OPTION on the db, but no privilege granted. - // Wo we need to print a special USAGE line. - s := fmt.Sprintf(`GRANT USAGE ON %s.* TO '%s'@'%s' WITH GRANT OPTION`, dbName, user, host) - gs = append(gs, s) - } - } - slices.Sort(gs[sortFromIdx:]) - - // Show table scope grants. - sortFromIdx = len(gs) - tablePrivTable := make(map[string]mysql.PrivilegeType) - for _, record := range p.TablesPriv { - recordKey := stringutil.Escape(record.DB, sqlMode) + "." + stringutil.Escape(record.TableName, sqlMode) - if user == record.User && host == record.Host { - tablePrivTable[recordKey] |= record.TablePriv - } else { - for _, r := range allRoles { - if record.baseRecord.match(r.Username, r.Hostname) { - tablePrivTable[recordKey] |= record.TablePriv - } - } - } - } - for k, priv := range tablePrivTable { - g := tablePrivToString(priv) - if len(g) > 0 { - var s string - if (priv & mysql.GrantPriv) > 0 { - s = fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s' WITH GRANT OPTION`, g, k, user, host) - } else { - s = fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s'`, g, k, user, host) - } - gs = append(gs, s) - } else if len(g) == 0 && (priv&mysql.GrantPriv) > 0 { - // We have GRANT OPTION on the table, but no privilege granted. - // Wo we need to print a special USAGE line. - s := fmt.Sprintf(`GRANT USAGE ON %s TO '%s'@'%s' WITH GRANT OPTION`, k, user, host) - gs = append(gs, s) - } - } - slices.Sort(gs[sortFromIdx:]) - - // Show column scope grants, column and table are combined. - // A map of "DB.Table" => Priv(col1, col2 ...) - sortFromIdx = len(gs) - columnPrivTable := make(map[string]privOnColumns) - for i := range p.ColumnsPriv { - record := p.ColumnsPriv[i] - if !collectColumnGrant(&record, user, host, columnPrivTable, sqlMode) { - for _, r := range allRoles { - collectColumnGrant(&record, r.Username, r.Hostname, columnPrivTable, sqlMode) - } - } - } - for k, v := range columnPrivTable { - privCols := privOnColumnsToString(v) - s := fmt.Sprintf(`GRANT %s ON %s TO '%s'@'%s'`, privCols, k, user, host) - gs = append(gs, s) - } - slices.Sort(gs[sortFromIdx:]) - - // Show role grants. - graphKey := user + "@" + host - edgeTable, ok := p.RoleGraph[graphKey] - g = "" - if ok { - sortedRes := make([]string, 0, 10) - for k := range edgeTable.roleList { - role := strings.Split(k, "@") - roleName, roleHost := role[0], role[1] - tmp := fmt.Sprintf("'%s'@'%s'", roleName, roleHost) - sortedRes = append(sortedRes, tmp) - } - slices.Sort(sortedRes) - for i, r := range sortedRes { - g += r - if i != len(sortedRes)-1 { - g += ", " - } - } - s := fmt.Sprintf(`GRANT %s TO '%s'@'%s'`, g, user, host) - gs = append(gs, s) - } - - // If the SHOW GRANTS is for the current user, there might be activeRoles (allRoles) - // The convention is to merge the Dynamic privileges assigned to the user with - // inherited dynamic privileges from those roles - dynamicPrivsMap := make(map[string]bool) // privName, grantable - for _, record := range p.Dynamic[user] { - if record.fullyMatch(user, host) { - dynamicPrivsMap[record.PrivilegeName] = record.GrantOption - } - } - for _, r := range allRoles { - for _, record := range p.Dynamic[r.Username] { - if record.fullyMatch(r.Username, r.Hostname) { - // If the record already exists in the map and it's grantable - // skip doing anything, because we might inherit a non-grantable permission - // from a role, and don't want to clobber the existing privilege. - if grantable, ok := dynamicPrivsMap[record.PrivilegeName]; ok && grantable { - continue - } - dynamicPrivsMap[record.PrivilegeName] = record.GrantOption - } - } - } - - // Convert the map to a slice so it can be sorted to be deterministic and joined - var dynamicPrivs, grantableDynamicPrivs []string - for privName, grantable := range dynamicPrivsMap { - if grantable { - grantableDynamicPrivs = append(grantableDynamicPrivs, privName) - } else { - dynamicPrivs = append(dynamicPrivs, privName) - } - } - - // Merge the DYNAMIC privs into a line for non-grantable and then grantable. - if len(dynamicPrivs) > 0 { - slices.Sort(dynamicPrivs) - s := fmt.Sprintf("GRANT %s ON *.* TO '%s'@'%s'", strings.Join(dynamicPrivs, ","), user, host) - gs = append(gs, s) - } - if len(grantableDynamicPrivs) > 0 { - slices.Sort(grantableDynamicPrivs) - s := fmt.Sprintf("GRANT %s ON *.* TO '%s'@'%s' WITH GRANT OPTION", strings.Join(grantableDynamicPrivs, ","), user, host) - gs = append(gs, s) - } - return gs -} - -type columnStr = string -type columnStrs = []columnStr -type privOnColumns = map[mysql.PrivilegeType]columnStrs - -func privOnColumnsToString(p privOnColumns) string { - var buf bytes.Buffer - idx := 0 - for _, priv := range mysql.AllColumnPrivs { - v, ok := p[priv] - if !ok || len(v) == 0 { - continue - } - - if idx > 0 { - buf.WriteString(", ") - } - privStr := PrivToString(priv, mysql.AllColumnPrivs, mysql.Priv2Str) - fmt.Fprintf(&buf, "%s(", privStr) - for i, col := range v { - if i > 0 { - fmt.Fprintf(&buf, ", ") - } - buf.WriteString(col) - } - buf.WriteString(")") - idx++ - } - return buf.String() -} - -func collectColumnGrant(record *columnsPrivRecord, user, host string, columnPrivTable map[string]privOnColumns, sqlMode mysql.SQLMode) bool { - if record.baseRecord.match(user, host) { - recordKey := stringutil.Escape(record.DB, sqlMode) + "." + stringutil.Escape(record.TableName, sqlMode) - - privColumns, ok := columnPrivTable[recordKey] - if !ok { - privColumns = make(map[mysql.PrivilegeType]columnStrs) - } - - for _, priv := range mysql.AllColumnPrivs { - if priv&record.ColumnPriv > 0 { - old := privColumns[priv] - privColumns[priv] = append(old, record.ColumnName) - columnPrivTable[recordKey] = privColumns - } - } - return true - } - return false -} - -func userPrivToString(privs mysql.PrivilegeType) string { - if (privs & ^mysql.GrantPriv) == userTablePrivilegeMask { - return mysql.AllPrivilegeLiteral - } - return PrivToString(privs, mysql.AllGlobalPrivs, mysql.Priv2Str) -} - -func dbPrivToString(privs mysql.PrivilegeType) string { - if (privs & ^mysql.GrantPriv) == dbTablePrivilegeMask { - return mysql.AllPrivilegeLiteral - } - return PrivToString(privs, mysql.AllDBPrivs, mysql.Priv2SetStr) -} - -func tablePrivToString(privs mysql.PrivilegeType) string { - if (privs & ^mysql.GrantPriv) == tablePrivMask { - return mysql.AllPrivilegeLiteral - } - return PrivToString(privs, mysql.AllTablePrivs, mysql.Priv2Str) -} - -// PrivToString converts the privileges to string. -func PrivToString(priv mysql.PrivilegeType, allPrivs []mysql.PrivilegeType, allPrivNames map[mysql.PrivilegeType]string) string { - pstrs := make([]string, 0, 20) - for _, p := range allPrivs { - if priv&p == 0 { - continue - } - s := strings.ToUpper(allPrivNames[p]) - pstrs = append(pstrs, s) - } - return strings.Join(pstrs, ",") -} - -// UserPrivilegesTable provide data for INFORMATION_SCHEMA.USERS_PRIVILEGES table. -func (p *MySQLPrivilege) UserPrivilegesTable(activeRoles []*auth.RoleIdentity, user, host string) [][]types.Datum { - // Seeing all users requires SELECT ON * FROM mysql.* - // The SUPER privilege (or any other dynamic privilege) doesn't help here. - // This is verified against MySQL. - showOtherUsers := p.RequestVerification(activeRoles, user, host, mysql.SystemDB, "", "", mysql.SelectPriv) - var rows [][]types.Datum - for _, u := range p.User { - if showOtherUsers || u.match(user, host) { - rows = appendUserPrivilegesTableRow(rows, u) - } - } - for _, dynamicPrivs := range p.Dynamic { - for _, dynamicPriv := range dynamicPrivs { - if showOtherUsers || dynamicPriv.match(user, host) { - rows = appendDynamicPrivRecord(rows, dynamicPriv) - } - } - } - return rows -} - -func appendDynamicPrivRecord(rows [][]types.Datum, user dynamicPrivRecord) [][]types.Datum { - isGrantable := "NO" - if user.GrantOption { - isGrantable = "YES" - } - grantee := fmt.Sprintf("'%s'@'%s'", user.User, user.Host) - record := types.MakeDatums(grantee, "def", user.PrivilegeName, isGrantable) - return append(rows, record) -} - -func appendUserPrivilegesTableRow(rows [][]types.Datum, user UserRecord) [][]types.Datum { - var isGrantable string - if user.Privileges&mysql.GrantPriv > 0 { - isGrantable = "YES" - } else { - isGrantable = "NO" - } - grantee := fmt.Sprintf("'%s'@'%s'", user.User, user.Host) - if user.Privileges <= 1 { - // The "USAGE" row only appears if the user has no non-DYNAMIC privileges. - // This behavior was observed in MySQL. - record := types.MakeDatums(grantee, "def", "USAGE", "NO") - return append(rows, record) - } - for _, priv := range mysql.AllGlobalPrivs { - if user.Privileges&priv > 0 { - privilegeType := strings.ToUpper(mysql.Priv2Str[priv]) - // +---------------------------+---------------+-------------------------+--------------+ - // | GRANTEE | TABLE_CATALOG | PRIVILEGE_TYPE | IS_GRANTABLE | - // +---------------------------+---------------+-------------------------+--------------+ - // | 'root'@'localhost' | def | SELECT | YES | - record := types.MakeDatums(grantee, "def", privilegeType, isGrantable) - rows = append(rows, record) - } - } - return rows -} - -func (p *MySQLPrivilege) getDefaultRoles(user, host string) []*auth.RoleIdentity { - ret := make([]*auth.RoleIdentity, 0) - for _, r := range p.DefaultRoles { - if r.match(user, host) { - ret = append(ret, &auth.RoleIdentity{Username: r.DefaultRoleUser, Hostname: r.DefaultRoleHost}) - } - } - return ret -} - -func (p *MySQLPrivilege) getAllRoles(user, host string) []*auth.RoleIdentity { - key := user + "@" + host - edgeTable, ok := p.RoleGraph[key] - ret := make([]*auth.RoleIdentity, 0, len(edgeTable.roleList)) - if ok { - for _, r := range edgeTable.roleList { - ret = append(ret, r) - } - } - return ret -} - -// Handle wraps MySQLPrivilege providing thread safe access. -type Handle struct { - priv atomic.Value -} - -// NewHandle returns a Handle. -func NewHandle() *Handle { - return &Handle{} -} - -// Get the MySQLPrivilege for read. -func (h *Handle) Get() *MySQLPrivilege { - return h.priv.Load().(*MySQLPrivilege) -} - -// Update loads all the privilege info from kv storage. -func (h *Handle) Update(ctx sessionctx.Context) error { - var priv MySQLPrivilege - err := priv.LoadAll(ctx) - if err != nil { - return err - } - - h.priv.Store(&priv) - return nil -} diff --git a/privilege/privileges/cache_test.go b/privilege/privileges/cache_test.go deleted file mode 100644 index 0feefaafa6bff..0000000000000 --- a/privilege/privileges/cache_test.go +++ /dev/null @@ -1,574 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package privileges_test - -import ( - "fmt" - "testing" - "time" - - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestLoadUserTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table user;") - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadUserTable(tk.Session())) - require.Len(t, p.User, 0) - - // Host | User | authentication_string | Select_priv | Insert_priv | Update_priv | Delete_priv | Create_priv | Drop_priv | Process_priv | Grant_priv | References_priv | Alter_priv | Show_db_priv | Super_priv | Execute_priv | Index_priv | Create_user_priv | Trigger_priv - tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Select_priv) VALUES ("%", "root", "", "Y")`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Insert_priv) VALUES ("%", "root1", "admin", "Y")`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Update_priv, Show_db_priv, References_priv) VALUES ("%", "root11", "", "Y", "Y", "Y")`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, authentication_string, Create_user_priv, Index_priv, Execute_priv, Create_view_priv, Show_view_priv, Show_db_priv, Super_priv, Trigger_priv) VALUES ("%", "root111", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, user_attributes, token_issuer) VALUES ("%", "root1111", "{\"metadata\": {\"email\": \"user@pingcap.com\"}}", "")`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, password_expired, password_last_changed, password_lifetime) VALUES ("%", "root2", "Y", "2022-10-10 12:00:00", 3)`) - tk.MustExec(`INSERT INTO mysql.user (Host, User, password_expired, password_last_changed) VALUES ("%", "root3", "N", "2022-10-10 12:00:00")`) - - p = privileges.MySQLPrivilege{} - require.NoError(t, p.LoadUserTable(tk.Session())) - require.Len(t, p.User, len(p.UserMap)) - - user := p.User - require.Equal(t, "root", user[0].User) - require.Equal(t, mysql.SelectPriv, user[0].Privileges) - require.Equal(t, mysql.InsertPriv, user[1].Privileges) - require.Equal(t, mysql.UpdatePriv|mysql.ShowDBPriv|mysql.ReferencesPriv, user[2].Privileges) - require.Equal(t, mysql.CreateUserPriv|mysql.IndexPriv|mysql.ExecutePriv|mysql.CreateViewPriv|mysql.ShowViewPriv|mysql.ShowDBPriv|mysql.SuperPriv|mysql.TriggerPriv, user[3].Privileges) - require.Equal(t, "user@pingcap.com", user[4].Email) - require.Equal(t, "", user[4].AuthTokenIssuer) - require.Equal(t, true, user[5].PasswordExpired) - require.Equal(t, time.Date(2022, 10, 10, 12, 0, 0, 0, time.Local), user[5].PasswordLastChanged) - require.Equal(t, int64(3), user[5].PasswordLifeTime) - require.Equal(t, false, user[6].PasswordExpired) - require.Equal(t, time.Date(2022, 10, 10, 12, 0, 0, 0, time.Local), user[6].PasswordLastChanged) - require.Equal(t, int64(-1), user[6].PasswordLifeTime) -} - -func TestLoadGlobalPrivTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table global_priv") - - tk.MustExec(`INSERT INTO mysql.global_priv VALUES ("%", "tu", "{\"access\":0,\"plugin\":\"mysql_native_password\",\"ssl_type\":3, - \"ssl_cipher\":\"cipher\",\"x509_subject\":\"\C=ZH1\", \"x509_issuer\":\"\C=ZH2\", \"san\":\"\IP:127.0.0.1, IP:1.1.1.1, DNS:pingcap.com, URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1\", \"password_last_changed\":1}")`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadGlobalPrivTable(tk.Session())) - require.Equal(t, `%`, p.Global["tu"][0].Host) - require.Equal(t, `tu`, p.Global["tu"][0].User) - require.Equal(t, privileges.SslTypeSpecified, p.Global["tu"][0].Priv.SSLType) - require.Equal(t, "C=ZH2", p.Global["tu"][0].Priv.X509Issuer) - require.Equal(t, "C=ZH1", p.Global["tu"][0].Priv.X509Subject) - require.Equal(t, "IP:127.0.0.1, IP:1.1.1.1, DNS:pingcap.com, URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1", p.Global["tu"][0].Priv.SAN) - require.Len(t, p.Global["tu"][0].Priv.SANs[util.IP], 2) - require.Equal(t, "pingcap.com", p.Global["tu"][0].Priv.SANs[util.DNS][0]) - require.Equal(t, "spiffe://mesh.pingcap.com/ns/timesh/sa/me1", p.Global["tu"][0].Priv.SANs[util.URI][0]) -} - -func TestLoadDBTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table db;") - - tk.MustExec(`INSERT INTO mysql.db (Host, DB, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv) VALUES ("%", "information_schema", "root", "Y", "Y", "Y", "Y", "Y")`) - tk.MustExec(`INSERT INTO mysql.db (Host, DB, User, Drop_priv, Grant_priv, Index_priv, Alter_priv, Create_view_priv, Show_view_priv, Execute_priv) VALUES ("%", "mysql", "root1", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadDBTable(tk.Session())) - require.Len(t, p.DB, len(p.DBMap)) - - require.Equal(t, mysql.SelectPriv|mysql.InsertPriv|mysql.UpdatePriv|mysql.DeletePriv|mysql.CreatePriv, p.DB[0].Privileges) - require.Equal(t, mysql.DropPriv|mysql.GrantPriv|mysql.IndexPriv|mysql.AlterPriv|mysql.CreateViewPriv|mysql.ShowViewPriv|mysql.ExecutePriv, p.DB[1].Privileges) -} - -func TestLoadTablesPrivTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table tables_priv") - - tk.MustExec(`INSERT INTO mysql.tables_priv VALUES ("%", "db", "user", "table", "grantor", "2017-01-04 16:33:42.235831", "Grant,Index,Alter", "Insert,Update")`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadTablesPrivTable(tk.Session())) - require.Len(t, p.TablesPriv, len(p.TablesPrivMap)) - - require.Equal(t, `%`, p.TablesPriv[0].Host) - require.Equal(t, "db", p.TablesPriv[0].DB) - require.Equal(t, "user", p.TablesPriv[0].User) - require.Equal(t, "table", p.TablesPriv[0].TableName) - require.Equal(t, mysql.GrantPriv|mysql.IndexPriv|mysql.AlterPriv, p.TablesPriv[0].TablePriv) - require.Equal(t, mysql.InsertPriv|mysql.UpdatePriv, p.TablesPriv[0].ColumnPriv) -} - -func TestLoadColumnsPrivTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table columns_priv") - - tk.MustExec(`INSERT INTO mysql.columns_priv VALUES ("%", "db", "user", "table", "column", "2017-01-04 16:33:42.235831", "Insert,Update")`) - tk.MustExec(`INSERT INTO mysql.columns_priv VALUES ("127.0.0.1", "db", "user", "table", "column", "2017-01-04 16:33:42.235831", "Select")`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadColumnsPrivTable(tk.Session())) - require.Equal(t, `%`, p.ColumnsPriv[0].Host) - require.Equal(t, "db", p.ColumnsPriv[0].DB) - require.Equal(t, "user", p.ColumnsPriv[0].User) - require.Equal(t, "table", p.ColumnsPriv[0].TableName) - require.Equal(t, "column", p.ColumnsPriv[0].ColumnName) - require.Equal(t, mysql.InsertPriv|mysql.UpdatePriv, p.ColumnsPriv[0].ColumnPriv) - require.Equal(t, mysql.SelectPriv, p.ColumnsPriv[1].ColumnPriv) -} - -func TestLoadDefaultRoleTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table default_roles") - - tk.MustExec(`INSERT INTO mysql.default_roles VALUES ("%", "test_default_roles", "localhost", "r_1")`) - tk.MustExec(`INSERT INTO mysql.default_roles VALUES ("%", "test_default_roles", "localhost", "r_2")`) - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadDefaultRoles(tk.Session())) - require.Equal(t, `%`, p.DefaultRoles[0].Host) - require.Equal(t, "test_default_roles", p.DefaultRoles[0].User) - require.Equal(t, "localhost", p.DefaultRoles[0].DefaultRoleHost) - require.Equal(t, "r_1", p.DefaultRoles[0].DefaultRoleUser) - require.Equal(t, "localhost", p.DefaultRoles[1].DefaultRoleHost) -} - -func TestPatternMatch(t *testing.T) { - store := createStoreAndPrepareDB(t) - - activeRoles := make([]*auth.RoleIdentity, 0) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("USE MYSQL;") - tk.MustExec("TRUNCATE TABLE mysql.user") - tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("10.0.%", "root", "Y", "Y")`) - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadUserTable(tk.Session())) - require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1", "test", "", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1.118", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "114.114.114.114", "test", "", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "root", "114.114.114.114", "test", "", "", mysql.PrivilegeType(0))) - require.True(t, p.RequestVerification(activeRoles, "root", "10.0.1.118", "test", "", "", mysql.ShutdownPriv)) - - tk.MustExec("TRUNCATE TABLE mysql.user") - tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("", "root", "Y", "N")`) - p = privileges.MySQLPrivilege{} - require.NoError(t, p.LoadUserTable(tk.Session())) - require.True(t, p.RequestVerification(activeRoles, "root", "", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "notnull", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "", "test", "", "", mysql.ShutdownPriv)) - - // Pattern match for DB. - tk.MustExec("TRUNCATE TABLE mysql.user") - tk.MustExec("TRUNCATE TABLE mysql.db") - tk.MustExec(`INSERT INTO mysql.db (user,host,db,select_priv) values ('genius', '%', 'te%', 'Y')`) - require.NoError(t, p.LoadDBTable(tk.Session())) - require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "test", "", "", mysql.SelectPriv)) -} - -func TestHostMatch(t *testing.T) { - store := createStoreAndPrepareDB(t) - - activeRoles := make([]*auth.RoleIdentity, 0) - - tk := testkit.NewTestKit(t, store) - // Host name can be IPv4 address + netmask. - tk.MustExec("USE MYSQL;") - tk.MustExec("TRUNCATE TABLE mysql.user") - tk.MustExec(`INSERT INTO mysql.user (HOST, USER, authentication_string, Select_priv, Shutdown_priv) VALUES ("172.0.0.0/255.0.0.0", "root", "", "Y", "Y")`) - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadUserTable(tk.Session())) - require.True(t, p.RequestVerification(activeRoles, "root", "172.0.0.1", "test", "", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "root", "172.1.1.1", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "198.0.0.1", "test", "", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "root", "198.0.0.1", "test", "", "", mysql.PrivilegeType(0))) - require.True(t, p.RequestVerification(activeRoles, "root", "172.0.0.1", "test", "", "", mysql.ShutdownPriv)) - tk.MustExec(`TRUNCATE TABLE mysql.user`) - - // Invalid host name, the user can be created, but cannot login. - cases := []string{ - "127.0.0.0/24", - "127.0.0.1/255.0.0.0", - "127.0.0.0/255.0.0", - "127.0.0.0/255.0.0.0.0", - "127%/255.0.0.0", - "127.0.0.0/%", - "127.0.0.%/%", - "127%/%", - } - for _, IPMask := range cases { - sql := fmt.Sprintf(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("%s", "root", "Y", "Y")`, IPMask) - tk.MustExec(sql) - p = privileges.MySQLPrivilege{} - require.NoError(t, p.LoadUserTable(tk.Session())) - require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.1", "test", "", "", mysql.SelectPriv), fmt.Sprintf("test case: %s", IPMask)) - require.False(t, p.RequestVerification(activeRoles, "root", "127.0.0.0", "test", "", "", mysql.SelectPriv), fmt.Sprintf("test case: %s", IPMask)) - require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.ShutdownPriv), fmt.Sprintf("test case: %s", IPMask)) - } - - // Netmask notation cannot be used for IPv6 addresses. - tk.MustExec(`INSERT INTO mysql.user (HOST, USER, Select_priv, Shutdown_priv) VALUES ("2001:db8::/ffff:ffff::", "root", "Y", "Y")`) - p = privileges.MySQLPrivilege{} - require.NoError(t, p.LoadUserTable(tk.Session())) - require.False(t, p.RequestVerification(activeRoles, "root", "2001:db8::1234", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "2001:db8::", "test", "", "", mysql.SelectPriv)) - require.False(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.ShutdownPriv)) -} - -func TestCaseInsensitive(t *testing.T) { - store := createStoreAndPrepareDB(t) - - activeRoles := make([]*auth.RoleIdentity, 0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE TCTrain;") - tk.MustExec("CREATE TABLE TCTrain.TCTrainOrder (id int);") - tk.MustExec("TRUNCATE TABLE mysql.user") - tk.MustExec(`INSERT INTO mysql.db VALUES ("127.0.0.1", "TCTrain", "genius", "Y", "Y", "Y", "Y", "Y", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N", "N")`) - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadDBTable(tk.Session())) - // DB and Table names are case-insensitive in MySQL. - require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "TCTrain", "TCTrainOrder", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "TCTRAIN", "TCTRAINORDER", "", mysql.SelectPriv)) - require.True(t, p.RequestVerification(activeRoles, "genius", "127.0.0.1", "tctrain", "tctrainorder", "", mysql.SelectPriv)) -} - -func TestLoadRoleGraph(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use mysql;") - tk.MustExec("truncate table user;") - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadDBTable(tk.Session())) - require.Len(t, p.User, 0) - - tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_1", "%", "user2")`) - tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_2", "%", "root")`) - tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_3", "%", "user1")`) - tk.MustExec(`INSERT INTO mysql.role_edges (FROM_HOST, FROM_USER, TO_HOST, TO_USER) VALUES ("%", "r_4", "%", "root")`) - - p = privileges.MySQLPrivilege{} - require.NoError(t, p.LoadRoleGraph(tk.Session())) - graph := p.RoleGraph - require.True(t, graph["root@%"].Find("r_2", "%")) - require.True(t, graph["root@%"].Find("r_4", "%")) - require.True(t, graph["user2@%"].Find("r_1", "%")) - require.True(t, graph["user1@%"].Find("r_3", "%")) - _, ok := graph["illedal"] - require.False(t, ok) - require.False(t, graph["root@%"].Find("r_1", "%")) -} - -func TestRoleGraphBFS(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE ROLE r_1, r_2, r_3, r_4, r_5, r_6;`) - tk.MustExec(`GRANT r_2 TO r_1;`) - tk.MustExec(`GRANT r_3 TO r_2;`) - tk.MustExec(`GRANT r_4 TO r_3;`) - tk.MustExec(`GRANT r_1 TO r_4;`) - tk.MustExec(`GRANT r_5 TO r_3, r_6;`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadRoleGraph(tk.Session())) - - activeRoles := make([]*auth.RoleIdentity, 0) - ret := p.FindAllRole(activeRoles) - require.Len(t, ret, 0) - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_1", Hostname: "%"}) - ret = p.FindAllRole(activeRoles) - require.Len(t, ret, 5) - - activeRoles = make([]*auth.RoleIdentity, 0) - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_6", Hostname: "%"}) - ret = p.FindAllRole(activeRoles) - require.Len(t, ret, 2) - - activeRoles = make([]*auth.RoleIdentity, 0) - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_3", Hostname: "%"}) - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "r_6", Hostname: "%"}) - ret = p.FindAllRole(activeRoles) - require.Len(t, ret, 6) -} - -func TestFindAllUserEffectiveRoles(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER u1`) - tk.MustExec(`CREATE ROLE r_1, r_2, r_3, r_4;`) - tk.MustExec(`GRANT r_3 to r_1`) - tk.MustExec(`GRANT r_4 to r_2`) - tk.MustExec(`GRANT r_1 to u1`) - tk.MustExec(`GRANT r_2 to u1`) - - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadAll(tk.Session())) - ret := p.FindAllUserEffectiveRoles("u1", "%", []*auth.RoleIdentity{ - {Username: "r_1", Hostname: "%"}, - {Username: "r_2", Hostname: "%"}, - }) - require.Equal(t, 4, len(ret)) - require.Equal(t, "r_1", ret[0].Username) - require.Equal(t, "r_2", ret[1].Username) - require.Equal(t, "r_3", ret[2].Username) - require.Equal(t, "r_4", ret[3].Username) - - tk.MustExec(`REVOKE r_2 from u1`) - require.NoError(t, p.LoadAll(tk.Session())) - ret = p.FindAllUserEffectiveRoles("u1", "%", []*auth.RoleIdentity{ - {Username: "r_1", Hostname: "%"}, - {Username: "r_2", Hostname: "%"}, - }) - require.Equal(t, 2, len(ret)) - require.Equal(t, "r_1", ret[0].Username) - require.Equal(t, "r_3", ret[1].Username) -} - -func TestAbnormalMySQLTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - - // Simulate the case mysql.user is synchronized from MySQL. - tk.MustExec("DROP TABLE mysql.user;") - tk.MustExec("USE mysql;") - tk.MustExec(`CREATE TABLE user ( - Host char(60) COLLATE utf8_bin NOT NULL DEFAULT '', - User char(16) COLLATE utf8_bin NOT NULL DEFAULT '', - Password char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', - Select_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Insert_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Update_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Delete_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Drop_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Reload_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Shutdown_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Process_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - File_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Config_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Grant_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - References_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Index_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Alter_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Show_db_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Super_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_tmp_table_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Lock_tables_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Execute_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Repl_slave_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Repl_client_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Show_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Alter_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_user_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Event_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Trigger_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_tablespace_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - Create_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Drop_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Account_locked ENUM('N','Y') NOT NULL DEFAULT 'N', - ssl_type enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '', - ssl_cipher blob NOT NULL, - x509_issuer blob NOT NULL, - x509_subject blob NOT NULL, - max_questions int(11) unsigned NOT NULL DEFAULT '0', - max_updates int(11) unsigned NOT NULL DEFAULT '0', - max_connections int(11) unsigned NOT NULL DEFAULT '0', - max_user_connections int(11) unsigned NOT NULL DEFAULT '0', - plugin char(64) COLLATE utf8_bin DEFAULT 'mysql_native_password', - authentication_string text COLLATE utf8_bin, - token_issuer varchar(255), - user_attributes json, - password_expired ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', - password_last_changed TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), - password_lifetime SMALLINT UNSIGNED, - PRIMARY KEY (Host,User) -) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges';`) - tk.MustExec(`INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'mysql_native_password','', '', 'null', 'N', current_timestamp(), null); -`) - var p privileges.MySQLPrivilege - require.NoError(t, p.LoadUserTable(tk.Session())) - activeRoles := make([]*auth.RoleIdentity, 0) - // MySQL mysql.user table schema is not identical to TiDB, check it doesn't break privilege. - require.True(t, p.RequestVerification(activeRoles, "root", "localhost", "test", "", "", mysql.SelectPriv)) - - // Absent of those tables doesn't cause error. - tk.MustExec("DROP TABLE mysql.db;") - tk.MustExec("DROP TABLE mysql.tables_priv;") - tk.MustExec("DROP TABLE mysql.columns_priv;") - require.NoError(t, p.LoadAll(tk.Session())) -} - -func TestSortUserTable(t *testing.T) { - var p privileges.MySQLPrivilege - p.User = []privileges.UserRecord{ - privileges.NewUserRecord(`%`, "root"), - privileges.NewUserRecord(`%`, "jeffrey"), - privileges.NewUserRecord("localhost", "root"), - privileges.NewUserRecord("localhost", ""), - } - p.SortUserTable() - result := []privileges.UserRecord{ - privileges.NewUserRecord("localhost", "root"), - privileges.NewUserRecord("localhost", ""), - privileges.NewUserRecord(`%`, "jeffrey"), - privileges.NewUserRecord(`%`, "root"), - } - checkUserRecord(t, p.User, result) - - p.User = []privileges.UserRecord{ - privileges.NewUserRecord(`%`, "jeffrey"), - privileges.NewUserRecord("h1.example.net", ""), - } - p.SortUserTable() - result = []privileges.UserRecord{ - privileges.NewUserRecord("h1.example.net", ""), - privileges.NewUserRecord(`%`, "jeffrey"), - } - checkUserRecord(t, p.User, result) - - p.User = []privileges.UserRecord{ - privileges.NewUserRecord(`192.168.%`, "xxx"), - privileges.NewUserRecord(`192.168.199.%`, "xxx"), - } - p.SortUserTable() - result = []privileges.UserRecord{ - privileges.NewUserRecord(`192.168.199.%`, "xxx"), - privileges.NewUserRecord(`192.168.%`, "xxx"), - } - checkUserRecord(t, p.User, result) -} - -func TestGlobalPrivValueRequireStr(t *testing.T) { - var ( - none = privileges.GlobalPrivValue{SSLType: privileges.SslTypeNone} - tls = privileges.GlobalPrivValue{SSLType: privileges.SslTypeAny} - x509 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeX509} - spec = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, SSLCipher: "c1", X509Subject: "s1", X509Issuer: "i1"} - spec2 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, X509Subject: "s1", X509Issuer: "i1"} - spec3 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified, X509Issuer: "i1"} - spec4 = privileges.GlobalPrivValue{SSLType: privileges.SslTypeSpecified} - ) - require.Equal(t, "NONE", none.RequireStr()) - require.Equal(t, "SSL", tls.RequireStr()) - require.Equal(t, "X509", x509.RequireStr()) - require.Equal(t, "CIPHER 'c1' ISSUER 'i1' SUBJECT 's1'", spec.RequireStr()) - require.Equal(t, "ISSUER 'i1' SUBJECT 's1'", spec2.RequireStr()) - require.Equal(t, "ISSUER 'i1'", spec3.RequireStr()) - require.Equal(t, "NONE", spec4.RequireStr()) -} - -func checkUserRecord(t *testing.T, x, y []privileges.UserRecord) { - require.Equal(t, len(x), len(y)) - for i := 0; i < len(x); i++ { - require.Equal(t, x[i].User, y[i].User) - require.Equal(t, x[i].Host, y[i].Host) - } -} - -func TestDBIsVisible(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database visdb") - p := privileges.MySQLPrivilege{} - require.NoError(t, p.LoadAll(tk.Session())) - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_role_priv, Super_priv) VALUES ("%", "testvisdb", "Y", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible := p.DBIsVisible("testvisdb", "%", "visdb") - require.False(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Select_priv) VALUES ("%", "testvisdb2", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb2", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_priv) VALUES ("%", "testvisdb3", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb3", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Insert_priv) VALUES ("%", "testvisdb4", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb4", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Update_priv) VALUES ("%", "testvisdb5", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb5", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Create_view_priv) VALUES ("%", "testvisdb6", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb6", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Trigger_priv) VALUES ("%", "testvisdb7", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb7", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, References_priv) VALUES ("%", "testvisdb8", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb8", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") - - tk.MustExec(`INSERT INTO mysql.user (Host, User, Execute_priv) VALUES ("%", "testvisdb9", "Y")`) - require.NoError(t, p.LoadUserTable(tk.Session())) - isVisible = p.DBIsVisible("testvisdb9", "%", "visdb") - require.True(t, isVisible) - tk.MustExec("TRUNCATE TABLE mysql.user") -} diff --git a/privilege/privileges/errors.go b/privilege/privileges/errors.go deleted file mode 100644 index 8ec5d9401e3c9..0000000000000 --- a/privilege/privileges/errors.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package privileges - -import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -// error definitions. -var ( - errInvalidPrivilegeType = dbterror.ClassPrivilege.NewStd(mysql.ErrInvalidPrivilegeType) - ErrNonexistingGrant = dbterror.ClassPrivilege.NewStd(mysql.ErrNonexistingGrant) - errLoadPrivilege = dbterror.ClassPrivilege.NewStd(mysql.ErrLoadPrivilege) - ErrAccessDenied = dbterror.ClassPrivilege.NewStd(mysql.ErrAccessDenied) - errAccountHasBeenLocked = dbterror.ClassPrivilege.NewStd(mysql.ErrAccountHasBeenLocked) - ErUserAccessDeniedForUserAccountBlockedByPasswordLock = dbterror.ClassPrivilege.NewStd(mysql.ErUserAccessDeniedForUserAccountBlockedByPasswordLock) - ErrMustChangePasswordLogin = dbterror.ClassPrivilege.NewStd(mysql.ErrMustChangePasswordLogin) -) diff --git a/privilege/privileges/ldap/BUILD.bazel b/privilege/privileges/ldap/BUILD.bazel deleted file mode 100644 index 4f168771f8ec1..0000000000000 --- a/privilege/privileges/ldap/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ldap", - srcs = [ - "const.go", - "ldap_common.go", - "sasl.go", - "simple.go", - ], - importpath = "github.com/pingcap/tidb/privilege/privileges/ldap", - visibility = ["//visibility:public"], - deps = [ - "//privilege/conn", - "@com_github_go_ldap_ldap_v3//:ldap", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "ldap_test", - timeout = "short", - srcs = ["ldap_common_test.go"], - embed = [":ldap"], - embedsrcs = [ - "test/ca.crt", - "test/ldap.crt", - "test/ldap.key", - ], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/privilege/privileges/main_test.go b/privilege/privileges/main_test.go deleted file mode 100644 index 47bce6f139bb3..0000000000000 --- a/privilege/privileges/main_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package privileges_test - -import ( - "testing" - - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/privilege/privileges.(*JWKSImpl).LoadJWKS4AuthToken.func1"), - } - testsetup.SetupForCommonTest() - - session.SetSchemaLease(0) - session.DisableStats4Test() - - goleak.VerifyTestMain(m, opts...) -} diff --git a/privilege/privileges/privileges_test.go b/privilege/privileges/privileges_test.go deleted file mode 100644 index dfc33a1a17811..0000000000000 --- a/privilege/privileges/privileges_test.go +++ /dev/null @@ -1,2117 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package privileges_test - -import ( - "bytes" - "context" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/json" - "fmt" - "net/url" - "os" - "path/filepath" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" -) - -func TestCheckDBPrivilege(t *testing.T) { - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'testcheck'@'localhost';`) - rootTk.MustExec(`CREATE USER 'testcheck_tmp'@'localhost';`) - - tk := testkit.NewTestKit(t, store) - activeRoles := make([]*auth.RoleIdentity, 0) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "testcheck", Hostname: "localhost"}, nil, nil, nil)) - pc := privilege.GetPrivilegeManager(tk.Session()) - require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) - - rootTk.MustExec(`GRANT SELECT ON *.* TO 'testcheck'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) - require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) - - rootTk.MustExec(`GRANT Update ON test.* TO 'testcheck'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) - - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "testcheck", Hostname: "localhost"}) - rootTk.MustExec(`GRANT 'testcheck'@'localhost' TO 'testcheck_tmp'@'localhost';`) - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "testcheck_tmp", Hostname: "localhost"}, nil, nil, nil)) - pc = privilege.GetPrivilegeManager(tk2.Session()) - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) -} - -func TestCheckTablePrivilege(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'test1'@'localhost';`) - rootTk.MustExec(`CREATE USER 'test1_tmp'@'localhost';`) - - tk := testkit.NewTestKit(t, store) - activeRoles := make([]*auth.RoleIdentity, 0) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test1", Hostname: "localhost"}, nil, nil, nil)) - pc := privilege.GetPrivilegeManager(tk.Session()) - require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) - - rootTk.MustExec(`GRANT SELECT ON *.* TO 'test1'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) - require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) - - rootTk.MustExec(`GRANT Update ON test.* TO 'test1'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) - require.False(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) - - activeRoles = append(activeRoles, &auth.RoleIdentity{Username: "test1", Hostname: "localhost"}) - tk2 := testkit.NewTestKit(t, store) - rootTk.MustExec(`GRANT 'test1'@'localhost' TO 'test1_tmp'@'localhost';`) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "test1_tmp", Hostname: "localhost"}, nil, nil, nil)) - pc2 := privilege.GetPrivilegeManager(tk2.Session()) - require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.SelectPriv)) - require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.UpdatePriv)) - require.False(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) - - rootTk.MustExec(`GRANT Index ON test.test TO 'test1'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) - require.True(t, pc2.RequestVerification(activeRoles, "test", "test", "", mysql.IndexPriv)) -} - -func TestCheckViewPrivilege(t *testing.T) { - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec("use test") - rootTk.MustExec(`CREATE USER 'vuser'@'localhost';`) - rootTk.MustExec(`CREATE VIEW v AS SELECT * FROM test;`) - - tk := testkit.NewTestKit(t, store) - activeRoles := make([]*auth.RoleIdentity, 0) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "vuser", Hostname: "localhost"}, nil, nil, nil)) - pc := privilege.GetPrivilegeManager(tk.Session()) - require.False(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) - - rootTk.MustExec(`GRANT SELECT ON test.v TO 'vuser'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) - require.False(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.ShowViewPriv)) - - rootTk.MustExec(`GRANT SHOW VIEW ON test.v TO 'vuser'@'localhost';`) - require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.SelectPriv)) - require.True(t, pc.RequestVerification(activeRoles, "test", "v", "", mysql.ShowViewPriv)) -} - -func TestCheckPrivilegeWithRoles(t *testing.T) { - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'test_role'@'localhost';`) - rootTk.MustExec(`CREATE ROLE r_1, r_2, r_3;`) - rootTk.MustExec(`GRANT r_1, r_2, r_3 TO 'test_role'@'localhost';`) - - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_role", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`SET ROLE r_1, r_2;`) - rootTk.MustExec(`SET DEFAULT ROLE r_1 TO 'test_role'@'localhost';`) - // test bogus role for current user. - err := tk.ExecToErr(`SET DEFAULT ROLE roledoesnotexist TO 'test_role'@'localhost';`) - require.True(t, terror.ErrorEqual(err, exeerrors.ErrRoleNotGranted)) - - rootTk.MustExec(`GRANT SELECT ON test.* TO r_1;`) - pc := privilege.GetPrivilegeManager(tk.Session()) - activeRoles := tk.Session().GetSessionVars().ActiveRoles - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.SelectPriv)) - require.False(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) - rootTk.MustExec(`GRANT UPDATE ON test.* TO r_2;`) - require.True(t, pc.RequestVerification(activeRoles, "test", "", "", mysql.UpdatePriv)) - - tk.MustExec(`SET ROLE NONE;`) - require.Equal(t, 0, len(tk.Session().GetSessionVars().ActiveRoles)) - tk.MustExec(`SET ROLE DEFAULT;`) - require.Equal(t, 1, len(tk.Session().GetSessionVars().ActiveRoles)) - tk.MustExec(`SET ROLE ALL;`) - require.Equal(t, 3, len(tk.Session().GetSessionVars().ActiveRoles)) - tk.MustExec(`SET ROLE ALL EXCEPT r_1, r_2;`) - require.Equal(t, 1, len(tk.Session().GetSessionVars().ActiveRoles)) -} - -// TestErrorMessage checks that the identity in error messages matches the mysql.user table one. -// MySQL is inconsistent in its error messages, as some match the loginHost and others the -// identity from mysql.user. In TiDB we now use the identity from mysql.user in error messages -// for consistency. -func TestErrorMessage(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER wildcard`) - rootTk.MustExec(`CREATE USER specifichost@192.168.1.1`) - rootTk.MustExec(`GRANT SELECT on test.* TO wildcard`) - rootTk.MustExec(`GRANT SELECT on test.* TO specifichost@192.168.1.1`) - - wildTk := testkit.NewTestKit(t, store) - - // The session.Auth() func will populate the AuthUsername and AuthHostname fields. - // We don't have to explicitly specify them. - require.NoError(t, wildTk.Session().Auth(&auth.UserIdentity{Username: "wildcard", Hostname: "192.168.1.1"}, nil, nil, nil)) - require.EqualError(t, wildTk.ExecToErr("use mysql;"), "[executor:1044]Access denied for user 'wildcard'@'%' to database 'mysql'") - - specificTk := testkit.NewTestKit(t, store) - require.NoError(t, specificTk.Session().Auth(&auth.UserIdentity{Username: "specifichost", Hostname: "192.168.1.1"}, nil, nil, nil)) - require.EqualError(t, specificTk.ExecToErr("use mysql;"), "[executor:1044]Access denied for user 'specifichost'@'192.168.1.1' to database 'mysql'") -} - -func TestDropTablePrivileges(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - ctx, _ := tk.Session().(sessionctx.Context) - tk.MustExec(`CREATE TABLE todrop(c int);`) - // ctx.GetSessionVars().User = "root@localhost" - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`CREATE USER 'drop'@'localhost';`) - tk.MustExec(`GRANT Select ON test.todrop TO 'drop'@'localhost';`) - - // ctx.GetSessionVars().User = "drop@localhost" - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "drop", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`SELECT * FROM todrop;`) - require.Error(t, tk.ExecToErr("DROP TABLE todrop;")) - - tk = testkit.NewTestKit(t, store) - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "root", Hostname: "localhost"} - tk.MustExec(`GRANT Drop ON test.todrop TO 'drop'@'localhost';`) - - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - ctx.GetSessionVars().User = &auth.UserIdentity{Username: "drop", Hostname: "localhost"} - tk.MustExec(`DROP TABLE todrop;`) -} - -func TestAlterUserStmt(t *testing.T) { - store := createStoreAndPrepareDB(t) - tk := testkit.NewTestKit(t, store) - - // high privileged user setting password for other user (passes) - tk.MustExec("CREATE USER superuser2, nobodyuser2, nobodyuser3, nobodyuser4, nobodyuser5, semuser1, semuser2, semuser3, semuser4") - tk.MustExec("GRANT ALL ON *.* TO superuser2") - tk.MustExec("GRANT CREATE USER ON *.* TO nobodyuser2") - tk.MustExec("GRANT SYSTEM_USER ON *.* TO nobodyuser4") - tk.MustExec("GRANT UPDATE ON mysql.user TO nobodyuser5, semuser1") - tk.MustExec("GRANT RESTRICTED_TABLES_ADMIN ON *.* TO semuser1") - tk.MustExec("GRANT RESTRICTED_USER_ADMIN ON *.* TO semuser1, semuser2, semuser3") - tk.MustExec("GRANT SYSTEM_USER ON *.* to semuser3") // user is both restricted + has SYSTEM_USER (or super) - - sem.Enable() - defer sem.Disable() - - // When SEM is enabled, even though we have UPDATE privilege on mysql.user, it explicitly - // denies writeable privileges to system schemas unless RESTRICTED_TABLES_ADMIN is granted. - // so the previous method of granting to the table instead of CREATE USER will fail now. - // This is intentional because SEM plugs directly into the privilege manager to DENY - // any request for UpdatePriv on mysql.user even if the privilege exists in the internal mysql.user table. - - // UpdatePriv on mysql.user - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "nobodyuser5", Hostname: "localhost"}, nil, nil, nil)) - err := tk.ExecToErr("ALTER USER 'nobodyuser2' IDENTIFIED BY 'newpassword'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") - - // actual CreateUserPriv - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "nobodyuser2", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("ALTER USER 'nobodyuser2' IDENTIFIED BY ''") - tk.MustExec("ALTER USER 'nobodyuser3' IDENTIFIED BY ''") - - // UpdatePriv on mysql.user but also has RESTRICTED_TABLES_ADMIN - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "semuser1", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("ALTER USER 'nobodyuser2' IDENTIFIED BY ''") - tk.MustExec("ALTER USER 'nobodyuser3' IDENTIFIED BY ''") - - // As it has (RESTRICTED_TABLES_ADMIN + UpdatePriv on mysql.user) + RESTRICTED_USER_ADMIN it can modify other restricted_user_admins like semuser2 - // and it can modify semuser3 because RESTRICTED_USER_ADMIN does not also need SYSTEM_USER - tk.MustExec("ALTER USER 'semuser1' IDENTIFIED BY ''") - tk.MustExec("ALTER USER 'semuser2' IDENTIFIED BY ''") - tk.MustExec("ALTER USER 'semuser3' IDENTIFIED BY ''") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "superuser2", Hostname: "localhost"}, nil, nil, nil)) - err = tk.ExecToErr("ALTER USER 'semuser1' IDENTIFIED BY 'newpassword'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_USER_ADMIN privilege(s) for this operation") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "semuser4", Hostname: "localhost"}, nil, nil, nil)) - // has restricted_user_admin but not CREATE USER or (update on mysql.user + RESTRICTED_TABLES_ADMIN) - tk.MustExec("ALTER USER 'semuser4' IDENTIFIED BY ''") // can modify self - err = tk.ExecToErr("ALTER USER 'nobodyuser3' IDENTIFIED BY 'newpassword'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") - err = tk.ExecToErr("ALTER USER 'semuser1' IDENTIFIED BY 'newpassword'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") - err = tk.ExecToErr("ALTER USER 'semuser3' IDENTIFIED BY 'newpassword'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation") -} - -func TestShowViewPriv(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`DROP VIEW IF EXISTS test.v`) - tk.MustExec(`CREATE VIEW test.v AS SELECT 1`) - tk.MustExec("CREATE USER vnobody, vshowview, vselect, vshowandselect") - tk.MustExec("GRANT SHOW VIEW ON test.v TO vshowview") - tk.MustExec("GRANT SELECT ON test.v TO vselect") - tk.MustExec("GRANT SHOW VIEW, SELECT ON test.v TO vshowandselect") - - tests := []struct { - userName string - showViewErr string - showTableErr string - explainErr string - explainRes string - descErr string - descRes string - tablesNum string - columnsNum string - }{ - {"vnobody", - "[planner:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", - "[planner:1142]SHOW VIEW command denied to user 'vnobody'@'%' for table 'v'", - "[executor:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", - "", - "[executor:1142]SELECT command denied to user 'vnobody'@'%' for table 'v'", - "", - "0", - "0", - }, - {"vshowview", - "[planner:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", - "", - "[executor:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", - "", - "[executor:1142]SELECT command denied to user 'vshowview'@'%' for table 'v'", - "", - "1", - "0", - }, - {"vselect", - "[planner:1142]SHOW VIEW command denied to user 'vselect'@'%' for table 'v'", - "[planner:1142]SHOW VIEW command denied to user 'vselect'@'%' for table 'v'", - "", - "1 bigint(1) NO ", - "", - "1 bigint(1) NO ", - "1", - "1", - }, - {"vshowandselect", - "", - "", - "", - "1 bigint(1) NO ", - "", - "1 bigint(1) NO ", - "1", - "1", - }, - } - - for _, test := range tests { - tk.Session().Auth(&auth.UserIdentity{Username: test.userName, Hostname: "localhost"}, nil, nil, nil) - err := tk.ExecToErr("SHOW CREATE VIEW test.v") - if test.showViewErr != "" { - require.EqualError(t, err, test.showViewErr, test) - } else { - require.NoError(t, err, test) - } - err = tk.ExecToErr("SHOW CREATE TABLE test.v") - if test.showTableErr != "" { - require.EqualError(t, err, test.showTableErr, test) - } else { - require.NoError(t, err, test) - } - if test.explainErr != "" { - err = tk.QueryToErr("explain test.v") - require.EqualError(t, err, test.explainErr, test) - } else { - tk.MustQuery("explain test.v").Check(testkit.Rows(test.explainRes)) - } - if test.descErr != "" { - err = tk.QueryToErr("explain test.v") - require.EqualError(t, err, test.descErr, test) - } else { - tk.MustQuery("desc test.v").Check(testkit.Rows(test.descRes)) - } - tk.MustQuery("select count(*) from information_schema.tables where table_schema='test' and table_name='v'").Check(testkit.Rows(test.tablesNum)) - tk.MustQuery("select count(*) from information_schema.columns where table_schema='test' and table_name='v'").Check(testkit.Rows(test.columnsNum)) - } -} - -func TestCheckCertBasedAuth(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER 'r1'@'localhost';`) - tk.MustExec(`CREATE USER 'r2'@'localhost' require none;`) - tk.MustExec(`CREATE USER 'r3'@'localhost' require ssl;`) - tk.MustExec(`CREATE USER 'r4'@'localhost' require x509;`) - tk.MustExec(`CREATE USER 'r5'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' - subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1' cipher 'TLS_AES_128_GCM_SHA256'`) - tk.MustExec(`CREATE USER 'r6'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' - subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) - tk.MustExec(`CREATE USER 'r7_issuer_only'@'localhost' require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin'`) - tk.MustExec(`CREATE USER 'r8_subject_only'@'localhost' require subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) - tk.MustExec(`CREATE USER 'r9_subject_disorder'@'localhost' require subject '/ST=Beijing/C=ZH/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) - tk.MustExec(`CREATE USER 'r10_issuer_disorder'@'localhost' require issuer '/ST=California/C=US/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin'`) - tk.MustExec(`CREATE USER 'r11_cipher_only'@'localhost' require cipher 'TLS_AES_256_GCM_SHA384'`) - tk.MustExec(`CREATE USER 'r12_old_tidb_user'@'localhost'`) - tk.MustExec("DELETE FROM mysql.global_priv WHERE `user` = 'r12_old_tidb_user' and `host` = 'localhost'") - tk.MustExec(`CREATE USER 'r13_broken_user'@'localhost'require issuer '/C=US/ST=California/L=San Francisco/O=PingCAP/OU=TiDB/CN=TiDB admin' - subject '/C=ZH/ST=Beijing/L=Haidian/O=PingCAP.Inc/OU=TiDB/CN=tester1'`) - tk.MustExec("UPDATE mysql.global_priv set priv = 'abc' where `user` = 'r13_broken_user' and `host` = 'localhost'") - tk.MustExec(`CREATE USER 'r14_san_only_pass'@'localhost' require san 'URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me1'`) - tk.MustExec(`CREATE USER 'r15_san_only_fail'@'localhost' require san 'URI:spiffe://mesh.pingcap.com/ns/timesh/sa/me2'`) - - defer func() { - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("drop user 'r1'@'localhost'") - tk.MustExec("drop user 'r2'@'localhost'") - tk.MustExec("drop user 'r3'@'localhost'") - tk.MustExec("drop user 'r4'@'localhost'") - tk.MustExec("drop user 'r5'@'localhost'") - tk.MustExec("drop user 'r6'@'localhost'") - tk.MustExec("drop user 'r7_issuer_only'@'localhost'") - tk.MustExec("drop user 'r8_subject_only'@'localhost'") - tk.MustExec("drop user 'r9_subject_disorder'@'localhost'") - tk.MustExec("drop user 'r10_issuer_disorder'@'localhost'") - tk.MustExec("drop user 'r11_cipher_only'@'localhost'") - tk.MustExec("drop user 'r12_old_tidb_user'@'localhost'") - tk.MustExec("drop user 'r13_broken_user'@'localhost'") - tk.MustExec("drop user 'r14_san_only_pass'@'localhost'") - tk.MustExec("drop user 'r15_san_only_fail'@'localhost'") - }() - - // test without ssl or ca - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - - // test use ssl without ca - tk.Session().GetSessionVars().TLSConnectionState = &tls.ConnectionState{VerifiedChains: nil} - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - - // test use ssl with signed but info wrong ca. - tk.Session().GetSessionVars().TLSConnectionState = &tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{{{}}}} - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - - // test a all pass case - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "US"), - util.MockPkixAttribute(util.Province, "California"), - util.MockPkixAttribute(util.Locality, "San Francisco"), - util.MockPkixAttribute(util.Organization, "PingCAP"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "TiDB admin"), - }, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "ZH"), - util.MockPkixAttribute(util.Province, "Beijing"), - util.MockPkixAttribute(util.Locality, "Haidian"), - util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "tester1"), - }, - }, - tls.TLS_AES_128_GCM_SHA256, func(cert *x509.Certificate) { - var url url.URL - err := url.UnmarshalBinary([]byte("spiffe://mesh.pingcap.com/ns/timesh/sa/me1")) - require.NoError(t, err) - cert.URIs = append(cert.URIs, &url) - }) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r4", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r14_san_only_pass", Hostname: "localhost"}, nil, nil, nil)) - - // test require but give nothing - tk.Session().GetSessionVars().TLSConnectionState = nil - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - - // test mismatch cipher - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "US"), - util.MockPkixAttribute(util.Province, "California"), - util.MockPkixAttribute(util.Locality, "San Francisco"), - util.MockPkixAttribute(util.Organization, "PingCAP"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "TiDB admin"), - }, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "ZH"), - util.MockPkixAttribute(util.Province, "Beijing"), - util.MockPkixAttribute(util.Locality, "Haidian"), - util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "tester1"), - }, - }, - tls.TLS_AES_256_GCM_SHA384) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r5", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r6", Hostname: "localhost"}, nil, nil, nil)) // not require cipher - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r11_cipher_only", Hostname: "localhost"}, nil, nil, nil)) - - // test only subject or only issuer - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "US"), - util.MockPkixAttribute(util.Province, "California"), - util.MockPkixAttribute(util.Locality, "San Francisco"), - util.MockPkixAttribute(util.Organization, "PingCAP"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "TiDB admin"), - }, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "AZ"), - util.MockPkixAttribute(util.Province, "Beijing"), - util.MockPkixAttribute(util.Locality, "Shijingshang"), - util.MockPkixAttribute(util.Organization, "CAPPing.Inc"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "tester2"), - }, - }, - tls.TLS_AES_128_GCM_SHA256) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r7_issuer_only", Hostname: "localhost"}, nil, nil, nil)) - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "AU"), - util.MockPkixAttribute(util.Province, "California"), - util.MockPkixAttribute(util.Locality, "San Francisco"), - util.MockPkixAttribute(util.Organization, "PingCAP"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "TiDB admin2"), - }, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "ZH"), - util.MockPkixAttribute(util.Province, "Beijing"), - util.MockPkixAttribute(util.Locality, "Haidian"), - util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "tester1"), - }, - }, - tls.TLS_AES_128_GCM_SHA256) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r8_subject_only", Hostname: "localhost"}, nil, nil, nil)) - - // test disorder issuer or subject - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{}, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "ZH"), - util.MockPkixAttribute(util.Province, "Beijing"), - util.MockPkixAttribute(util.Locality, "Haidian"), - util.MockPkixAttribute(util.Organization, "PingCAP.Inc"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "tester1"), - }, - }, - tls.TLS_AES_128_GCM_SHA256) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r9_subject_disorder", Hostname: "localhost"}, nil, nil, nil)) - tk.Session().GetSessionVars().TLSConnectionState = connectionState( - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - util.MockPkixAttribute(util.Country, "US"), - util.MockPkixAttribute(util.Province, "California"), - util.MockPkixAttribute(util.Locality, "San Francisco"), - util.MockPkixAttribute(util.Organization, "PingCAP"), - util.MockPkixAttribute(util.OrganizationalUnit, "TiDB"), - util.MockPkixAttribute(util.CommonName, "TiDB admin"), - }, - }, - pkix.Name{ - Names: []pkix.AttributeTypeAndValue{}, - }, - tls.TLS_AES_128_GCM_SHA256) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r10_issuer_disorder", Hostname: "localhost"}, nil, nil, nil)) - - // test mismatch san - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r15_san_only_fail", Hostname: "localhost"}, nil, nil, nil)) - - // test old data and broken data - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "r12_old_tidb_user", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r13_broken_user", Hostname: "localhost"}, nil, nil, nil)) -} - -func connectionState(issuer, subject pkix.Name, cipher uint16, opt ...func(c *x509.Certificate)) *tls.ConnectionState { - cert := &x509.Certificate{Issuer: issuer, Subject: subject} - for _, o := range opt { - o(cert) - } - return &tls.ConnectionState{ - VerifiedChains: [][]*x509.Certificate{{cert}}, - CipherSuite: cipher, - } -} - -func TestCheckAuthenticate(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER 'u1'@'localhost';`) - tk.MustExec(`CREATE USER 'u2'@'localhost' identified by 'abc';`) - tk.MustExec(`CREATE USER 'u3@example.com'@'localhost';`) - tk.MustExec(`CREATE USER u4@localhost;`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) - salt := []byte{85, 92, 45, 22, 58, 79, 107, 6, 122, 125, 58, 80, 12, 90, 103, 32, 90, 10, 74, 82} - authentication := []byte{24, 180, 183, 225, 166, 6, 81, 102, 70, 248, 199, 143, 91, 204, 169, 9, 161, 171, 203, 33} - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, authentication, salt, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u3@example.com", Hostname: "localhost"}, nil, nil, nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u4", Hostname: "localhost"}, nil, nil, nil)) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("drop user 'u1'@'localhost'") - tk1.MustExec("drop user 'u2'@'localhost'") - tk1.MustExec("drop user 'u3@example.com'@'localhost'") - tk1.MustExec("drop user u4@localhost") - - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u2", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u3@example.com", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "u4", Hostname: "localhost"}, nil, nil, nil)) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("create role 'r1'@'localhost'") - tk2.MustExec("create role 'r2'@'localhost'") - tk2.MustExec("create role 'r3@example.com'@'localhost'") - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r1", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r2", Hostname: "localhost"}, nil, nil, nil)) - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "r3@example.com", Hostname: "localhost"}, nil, nil, nil)) - - tk1.MustExec("drop user 'r1'@'localhost'") - tk1.MustExec("drop user 'r2'@'localhost'") - tk1.MustExec("drop user 'r3@example.com'@'localhost'") -} - -func TestUseDB(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - // high privileged user - tk.MustExec("CREATE USER 'usesuper'") - tk.MustExec("CREATE USER 'usenobody'") - tk.MustExec("GRANT ALL ON *.* TO 'usesuper'") - // without grant option - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) - require.Error(t, tk.ExecToErr("GRANT SELECT ON mysql.* TO 'usenobody'")) - // with grant option - tk = testkit.NewTestKit(t, store) - // high privileged user - tk.MustExec("GRANT ALL ON *.* TO 'usesuper' WITH GRANT OPTION") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("use mysql") - // low privileged user - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil, nil)) - err := tk.ExecToErr("use mysql") - require.Error(t, err) - - // try again after privilege granted - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("GRANT SELECT ON mysql.* TO 'usenobody'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usenobody", Hostname: "localhost", AuthUsername: "usenobody", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("use mysql") - - // test `use db` for role. - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "usesuper", Hostname: "localhost", AuthUsername: "usesuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec(`CREATE DATABASE app_db`) - tk.MustExec(`CREATE ROLE 'app_developer'`) - tk.MustExec(`GRANT ALL ON app_db.* TO 'app_developer'`) - tk.MustExec(`CREATE USER 'dev'@'localhost'`) - tk.MustExec(`GRANT 'app_developer' TO 'dev'@'localhost'`) - tk.MustExec(`SET DEFAULT ROLE 'app_developer' TO 'dev'@'localhost'`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dev", Hostname: "localhost", AuthUsername: "dev", AuthHostname: "localhost"}, nil, nil, nil)) - tk.MustExec("use app_db") - err = tk.ExecToErr("use mysql") - require.Error(t, err) -} - -func TestConfigPrivilege(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`DROP USER IF EXISTS tcd1`) - tk.MustExec(`CREATE USER tcd1`) - tk.MustExec(`GRANT ALL ON *.* to tcd1`) - tk.MustExec(`DROP USER IF EXISTS tcd2`) - tk.MustExec(`CREATE USER tcd2`) - tk.MustExec(`GRANT ALL ON *.* to tcd2`) - tk.MustExec(`REVOKE CONFIG ON *.* FROM tcd2`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tcd1", Hostname: "localhost", AuthHostname: "tcd1", AuthUsername: "%"}, nil, nil, nil)) - tk.MustExec(`SHOW CONFIG`) - tk.MustExec(`SET CONFIG TIKV testkey="testval"`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tcd2", Hostname: "localhost", AuthHostname: "tcd2", AuthUsername: "%"}, nil, nil, nil)) - err := tk.ExecToErr(`SHOW CONFIG`) - require.Error(t, err) - require.Regexp(t, "you need \\(at least one of\\) the CONFIG privilege\\(s\\) for this operation$", err.Error()) - err = tk.ExecToErr(`SET CONFIG TIKV testkey="testval"`) - require.Error(t, err) - require.Regexp(t, "you need \\(at least one of\\) the CONFIG privilege\\(s\\) for this operation$", err.Error()) - tk.MustExec(`DROP USER tcd1, tcd2`) -} - -func TestShowCreateTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER tsct1, tsct2`) - tk.MustExec(`GRANT select ON mysql.* to tsct2`) - - // should fail - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tsct1", Hostname: "localhost", AuthUsername: "tsct1", AuthHostname: "%"}, nil, nil, nil)) - err := tk.ExecToErr(`SHOW CREATE TABLE mysql.user`) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - - // should pass - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "tsct2", Hostname: "localhost", AuthUsername: "tsct2", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec(`SHOW CREATE TABLE mysql.user`) -} - -func TestAnalyzeTable(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - // high privileged user - tk.MustExec("CREATE USER 'asuper'") - tk.MustExec("CREATE USER 'anobody'") - tk.MustExec("GRANT ALL ON *.* TO 'asuper' WITH GRANT OPTION") - tk.MustExec("CREATE DATABASE atest") - tk.MustExec("use atest") - tk.MustExec("CREATE TABLE t1 (a int)") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("analyze table mysql.user") - // low privileged user - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) - err := tk.ExecToErr("analyze table t1") - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - require.EqualError(t, err, "[planner:1142]INSERT command denied to user 'anobody'@'%' for table 't1'") - - err = tk.ExecToErr("select * from t1") - require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'anobody'@'%' for table 't1'") - - // try again after SELECT privilege granted - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("GRANT SELECT ON atest.* TO 'anobody'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) - err = tk.ExecToErr("analyze table t1") - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - require.EqualError(t, err, "[planner:1142]INSERT command denied to user 'anobody'@'%' for table 't1'") - // Add INSERT privilege and it should work. - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "asuper", Hostname: "localhost", AuthUsername: "asuper", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("GRANT INSERT ON atest.* TO 'anobody'") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "anobody", Hostname: "localhost", AuthUsername: "anobody", AuthHostname: "%"}, nil, nil, nil)) - tk.MustExec("analyze table t1") -} - -func TestSystemSchema(t *testing.T) { - store := createStoreAndPrepareDB(t) - - // This test tests no privilege check for INFORMATION_SCHEMA database. - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER 'u1'@'localhost';`) - tk.MustExec(`GRANT SELECT ON *.* TO 'u1'@'localhost';`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`select * from information_schema.tables`) - tk.MustExec(`select * from information_schema.key_column_usage`) - err := tk.ExecToErr("create table information_schema.t(a int)") - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "denied to user")) - err = tk.ExecToErr("drop table information_schema.tables") - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "denied to user")) - err = tk.ExecToErr("update information_schema.tables set table_name = 'tst' where table_name = 'mysql'") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) - - // Test metric_schema. - tk.MustExec(`select * from metrics_schema.tidb_query_duration`) - err = tk.ExecToErr("drop table metrics_schema.tidb_query_duration") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - err = tk.ExecToErr("update metrics_schema.tidb_query_duration set instance = 'tst'") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) - err = tk.ExecToErr("delete from metrics_schema.tidb_query_duration") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - err = tk.ExecToErr("create table metric_schema.t(a int)") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - - tk.MustGetErrCode("create table metrics_schema.t (id int);", errno.ErrTableaccessDenied) - tk.MustGetErrCode("create table performance_schema.t (id int);", errno.ErrTableaccessDenied) -} - -func TestPerformanceSchema(t *testing.T) { - store := createStoreAndPrepareDB(t) - - // This test tests no privilege check for INFORMATION_SCHEMA database. - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER 'u1'@'localhost';`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) - err := tk.ExecToErr("select * from performance_schema.events_statements_summary_by_digest where schema_name = 'tst'") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`GRANT SELECT ON *.* TO 'u1'@'localhost';`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("select * from performance_schema.events_statements_summary_by_digest where schema_name = 'tst'") - tk.MustExec(`select * from performance_schema.events_statements_summary_by_digest`) - err = tk.ExecToErr("drop table performance_schema.events_statements_summary_by_digest") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - err = tk.ExecToErr("update performance_schema.events_statements_summary_by_digest set schema_name = 'tst'") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) - err = tk.ExecToErr("delete from performance_schema.events_statements_summary_by_digest") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - err = tk.ExecToErr("create table performance_schema.t(a int)") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) -} - -func TestMetricsSchema(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER nobody, msprocess, msselect") - tk.MustExec("GRANT Process ON *.* TO msprocess") - tk.MustExec("GRANT SELECT ON metrics_schema.* TO msselect") - - tests := []struct { - stmt string - user string - checkErr func(err error) - }{ - { - "SHOW CREATE DATABASE metrics_schema", - "nobody", - func(err error) { - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, exeerrors.ErrDBaccessDenied)) - }, - }, - { - "SHOW CREATE DATABASE metrics_schema", - "msprocess", - func(err error) { - require.NoError(t, err) - }, - }, - { - "SHOW CREATE DATABASE metrics_schema", - "msselect", - func(err error) { - require.NoError(t, err) - }, - }, - { - "SELECT * FROM metrics_schema.up", - "nobody", - func(err error) { - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - }, - }, - { - "SELECT * FROM metrics_schema.up", - "msprocess", - func(err error) { - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "pd unavailable")) - }, - }, - { - "SELECT * FROM metrics_schema.up", - "msselect", - func(err error) { - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "pd unavailable")) - }, - }, - { - "SELECT * FROM information_schema.metrics_summary", - "nobody", - func(err error) { - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrSpecificAccessDenied)) - }, - }, - { - "SELECT * FROM information_schema.metrics_summary", - "msprocess", - func(err error) { - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "pd unavailable")) - }, - }, - { - "SELECT * FROM information_schema.metrics_summary_by_label", - "nobody", - func(err error) { - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrSpecificAccessDenied)) - }, - }, - { - "SELECT * FROM information_schema.metrics_summary_by_label", - "msprocess", - func(err error) { - require.Error(t, err) - require.True(t, strings.Contains(err.Error(), "pd unavailable")) - }, - }, - } - - for _, test := range tests { - tk.Session().Auth(&auth.UserIdentity{ - Username: test.user, - Hostname: "localhost", - }, nil, nil, nil) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - rs, err := tk.Session().ExecuteInternal(ctx, test.stmt) - if err == nil { - _, err = session.GetRows4Test(context.Background(), tk.Session(), rs) - } - if rs != nil { - require.NoError(t, rs.Close()) - } - test.checkErr(err) - } -} - -func TestAdminCommand(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`CREATE USER 'test_admin'@'localhost';`) - tk.MustExec(`CREATE TABLE t(a int)`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_admin", Hostname: "localhost"}, nil, nil, nil)) - err := tk.ExecToErr("ADMIN SHOW DDL JOBS") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) - err = tk.ExecToErr("ADMIN CHECK TABLE t") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrPrivilegeCheckFail)) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("ADMIN SHOW DDL JOBS") -} - -func TestLoadDataPrivilege(t *testing.T) { - // Create file. - path := "/tmp/load_data_priv.csv" - fp, err := os.Create(path) - require.NoError(t, err) - require.NotNil(t, fp) - defer func() { - require.NoError(t, fp.Close()) - require.NoError(t, os.Remove(path)) - }() - _, err = fp.WriteString("1\n") - require.NoError(t, err) - - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`CREATE USER 'test_load'@'localhost';`) - tk.MustExec(`CREATE TABLE t_load(a int)`) - - tk.MustExec(`GRANT SELECT on *.* to 'test_load'@'localhost'`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) - err = tk.ExecToErr("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' INTO TABLE t_load") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`GRANT INSERT on *.* to 'test_load'@'localhost'`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' INTO TABLE t_load") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - tk.MustExec(`GRANT INSERT on *.* to 'test_load'@'localhost'`) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_load", Hostname: "localhost"}, nil, nil, nil)) - err = tk.ExecToErr("LOAD DATA LOCAL INFILE '/tmp/load_data_priv.csv' REPLACE INTO TABLE t_load") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, core.ErrTableaccessDenied)) -} - -func TestAuthHost(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - tk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'test_auth_host'@'%';`) - rootTk.MustExec(`GRANT ALL ON *.* TO 'test_auth_host'@'%' WITH GRANT OPTION;`) - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_auth_host", Hostname: "192.168.0.10"}, nil, nil, nil)) - tk.MustExec("CREATE USER 'test_auth_host'@'192.168.%';") - tk.MustExec("GRANT SELECT ON *.* TO 'test_auth_host'@'192.168.%';") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "test_auth_host", Hostname: "192.168.0.10"}, nil, nil, nil)) - err := tk.ExecToErr("create user test_auth_host_a") - require.Error(t, err) - - rootTk.MustExec("DROP USER 'test_auth_host'@'192.168.%';") - rootTk.MustExec("DROP USER 'test_auth_host'@'%';") -} - -func TestDefaultRoles(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'testdefault'@'localhost';`) - rootTk.MustExec(`CREATE ROLE 'testdefault_r1'@'localhost', 'testdefault_r2'@'localhost';`) - rootTk.MustExec(`GRANT 'testdefault_r1'@'localhost', 'testdefault_r2'@'localhost' TO 'testdefault'@'localhost';`) - - tk := testkit.NewTestKit(t, store) - pc := privilege.GetPrivilegeManager(tk.Session()) - - ret := pc.GetDefaultRoles("testdefault", "localhost") - require.Len(t, ret, 0) - - rootTk.MustExec(`SET DEFAULT ROLE ALL TO 'testdefault'@'localhost';`) - ret = pc.GetDefaultRoles("testdefault", "localhost") - require.Len(t, ret, 2) - - rootTk.MustExec(`SET DEFAULT ROLE NONE TO 'testdefault'@'localhost';`) - ret = pc.GetDefaultRoles("testdefault", "localhost") - require.Len(t, ret, 0) -} - -func TestUserTableConsistency(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewAsyncTestKit(t, store) - ctx := tk.OpenSession(context.Background(), "test") - tk.MustExec(ctx, "create user superadmin") - tk.MustExec(ctx, "grant all privileges on *.* to 'superadmin'") - - // GrantPriv is not in AllGlobalPrivs any more, see pingcap/parser#581 - require.Equal(t, len(mysql.Priv2UserCol), len(mysql.AllGlobalPrivs)+1) - - var buf bytes.Buffer - var res bytes.Buffer - buf.WriteString("select ") - i := 0 - for _, priv := range mysql.AllGlobalPrivs { - if i != 0 { - buf.WriteString(", ") - res.WriteString(" ") - } - buf.WriteString(mysql.Priv2UserCol[priv]) - res.WriteString("Y") - i++ - } - buf.WriteString(" from mysql.user where user = 'superadmin'") - tk.MustQuery(ctx, buf.String()).Check(testkit.Rows(res.String())) -} - -func TestDynamicPrivs(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec("CREATE USER notsuper") - rootTk.MustExec("CREATE USER otheruser") - rootTk.MustExec("CREATE ROLE anyrolename") - - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "notsuper", Hostname: "%"}, nil, nil, nil)) - - // test SYSTEM_VARIABLES_ADMIN - err := tk.ExecToErr("SET GLOBAL wait_timeout = 86400") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") - rootTk.MustExec("GRANT SYSTEM_VARIABLES_admin ON *.* TO notsuper") - tk.MustExec("SET GLOBAL wait_timeout = 86400") - - // test ROLE_ADMIN - err = tk.ExecToErr("GRANT anyrolename TO otheruser") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or ROLE_ADMIN privilege(s) for this operation") - rootTk.MustExec("GRANT ROLE_ADMIN ON *.* TO notsuper") - tk.MustExec("GRANT anyrolename TO otheruser") - - // revoke SYSTEM_VARIABLES_ADMIN, confirm it is dropped - rootTk.MustExec("REVOKE SYSTEM_VARIABLES_AdmIn ON *.* FROM notsuper") - err = tk.ExecToErr("SET GLOBAL wait_timeout = 86000") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") - - // grant super, confirm that it is also a substitute for SYSTEM_VARIABLES_ADMIN - rootTk.MustExec("GRANT SUPER ON *.* TO notsuper") - tk.MustExec("SET GLOBAL wait_timeout = 86400") - - // revoke SUPER, assign SYSTEM_VARIABLES_ADMIN to anyrolename. - // confirm that a dynamic privilege can be inherited from a role. - rootTk.MustExec("REVOKE SUPER ON *.* FROM notsuper") - rootTk.MustExec("GRANT SYSTEM_VARIABLES_AdmIn ON *.* TO anyrolename") - rootTk.MustExec("GRANT anyrolename TO notsuper") - - // It's not a default role, this should initially fail: - err = tk.ExecToErr("SET GLOBAL wait_timeout = 86400") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the SUPER or SYSTEM_VARIABLES_ADMIN privilege(s) for this operation") - tk.MustExec("SET ROLE anyrolename") - tk.MustExec("SET GLOBAL wait_timeout = 87000") -} - -func TestDynamicGrantOption(t *testing.T) { - store := createStoreAndPrepareDB(t) - - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec("CREATE USER varuser1") - rootTk.MustExec("CREATE USER varuser2") - rootTk.MustExec("CREATE USER varuser3") - - rootTk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser1") - rootTk.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser2 WITH GRANT OPTION") - - tk1 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "varuser1", Hostname: "%"}, nil, nil, nil)) - err := tk1.ExecToErr("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser3") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the GRANT OPTION privilege(s) for this operation") - - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "varuser2", Hostname: "%"}, nil, nil, nil)) - tk2.MustExec("GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO varuser3") -} - -func TestSecurityEnhancedModeRestrictedTables(t *testing.T) { - store := createStoreAndPrepareDB(t) - - // This provides an integration test of the tests in util/security/security_test.go - cloudAdminTK := testkit.NewTestKit(t, store) - cloudAdminTK.MustExec("CREATE USER cloudadmin") - cloudAdminTK.MustExec("GRANT RESTRICTED_TABLES_ADMIN, SELECT ON *.* to cloudadmin") - cloudAdminTK.MustExec("GRANT CREATE ON mysql.* to cloudadmin") - cloudAdminTK.MustExec("CREATE USER uroot") - cloudAdminTK.MustExec("GRANT ALL ON *.* to uroot WITH GRANT OPTION") // A "MySQL" all powerful user. - require.NoError(t, cloudAdminTK.Session().Auth(&auth.UserIdentity{Username: "cloudadmin", Hostname: "%"}, nil, nil, nil)) - urootTk := testkit.NewTestKit(t, store) - require.NoError(t, urootTk.Session().Auth(&auth.UserIdentity{Username: "uroot", Hostname: "%"}, nil, nil, nil)) - - sem.Enable() - defer sem.Disable() - - err := urootTk.ExecToErr("use metrics_schema") - require.EqualError(t, err, "[executor:1044]Access denied for user 'uroot'@'%' to database 'metrics_schema'") - - err = urootTk.ExecToErr("SELECT * FROM metrics_schema.uptime") - require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'uroot'@'%' for table 'uptime'") - - err = urootTk.ExecToErr("CREATE TABLE mysql.abcd (a int)") - require.EqualError(t, err, "[planner:1142]CREATE command denied to user 'uroot'@'%' for table 'abcd'") - - cloudAdminTK.MustExec("USE metrics_schema") - cloudAdminTK.MustExec("SELECT * FROM metrics_schema.uptime") - cloudAdminTK.MustExec("CREATE TABLE mysql.abcd (a int)") -} - -func TestSecurityEnhancedModeInfoschema(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("CREATE USER uroot1, uroot2, uroot3") - tk.MustExec("GRANT SUPER ON *.* to uroot1 WITH GRANT OPTION") // super not process - tk.MustExec("GRANT SUPER, PROCESS, RESTRICTED_TABLES_ADMIN ON *.* to uroot2 WITH GRANT OPTION") - tk.Session().Auth(&auth.UserIdentity{ - Username: "uroot1", - Hostname: "localhost", - }, nil, nil, nil) - - sem.Enable() - defer sem.Disable() - - // Even though we have super, we still can't read protected information from tidb_servers_info, cluster_* tables - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.tidb_servers_info WHERE ip IS NOT NULL`).Check(testkit.Rows("0")) - err := tk.QueryToErr(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NOT NULL`) - require.Error(t, err) - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the PROCESS privilege(s) for this operation") - - // That is unless we have the RESTRICTED_TABLES_ADMIN privilege - tk.Session().Auth(&auth.UserIdentity{ - Username: "uroot2", - Hostname: "localhost", - }, nil, nil, nil) - - // flip from is NOT NULL etc - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.tidb_servers_info WHERE ip IS NULL`).Check(testkit.Rows("0")) - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.cluster_info WHERE status_address IS NULL`).Check(testkit.Rows("0")) -} - -func TestSecurityEnhancedLocalBackupRestore(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER backuprestore") - tk.MustExec("GRANT BACKUP_ADMIN,RESTORE_ADMIN ON *.* to backuprestore") - tk.Session().Auth(&auth.UserIdentity{ - Username: "backuprestore", - Hostname: "localhost", - }, nil, nil, nil) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - // Prior to SEM nolocal has permission, the error should be because backup requires tikv - _, err := tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'Local:///tmp/test';") - require.EqualError(t, err, "BACKUP requires tikv store, not unistore") - - _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'LOCAl:///tmp/test';") - require.EqualError(t, err, "RESTORE requires tikv store, not unistore") - - sem.Enable() - defer sem.Disable() - - // With SEM enabled nolocal does not have permission, but yeslocal does. - _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'local:///tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") - - _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'file:///tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") - - _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO '/tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") - - _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'LOCAl:///tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'local storage' is not supported when security enhanced mode is enabled") - - _, err = tk.Session().ExecuteInternal(ctx, "BACKUP DATABASE * TO 'hdfs:///tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'hdfs storage' is not supported when security enhanced mode is enabled") - - _, err = tk.Session().ExecuteInternal(ctx, "RESTORE DATABASE * FROM 'HDFS:///tmp/test';") - require.EqualError(t, err, "[planner:8132]Feature 'hdfs storage' is not supported when security enhanced mode is enabled") -} - -func TestSecurityEnhancedModeSysVars(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER svroot1, svroot2") - tk.MustExec("GRANT SUPER ON *.* to svroot1 WITH GRANT OPTION") - tk.MustExec("GRANT SELECT ON performance_schema.* to svroot1") - tk.MustExec("GRANT SUPER, RESTRICTED_VARIABLES_ADMIN ON *.* to svroot2") - tk.MustExec("GRANT SELECT ON performance_schema.* to svroot2") - - sem.Enable() - defer sem.Disable() - - // svroot1 has SUPER but in SEM will be restricted - tk.Session().Auth(&auth.UserIdentity{ - Username: "svroot1", - Hostname: "localhost", - AuthUsername: "uroot", - AuthHostname: "%", - }, nil, nil, nil) - - tk.MustQuery(`SHOW VARIABLES LIKE 'tidb_force_priority'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_force_priority'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_force_priority'`).Check(testkit.Rows()) - tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_enable_telemetry'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows()) - tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_time_series_count'`).Check(testkit.Rows()) - tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows()) - - _, err := tk.Exec("SET @@global.tidb_force_priority = 'NO_PRIORITY'") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - _, err = tk.Exec("SET GLOBAL tidb_enable_telemetry = OFF") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - _, err = tk.Exec("SET GLOBAL tidb_top_sql_max_time_series_count = 100") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - - _, err = tk.Exec("SELECT @@global.tidb_force_priority") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - _, err = tk.Exec("SELECT @@global.tidb_enable_telemetry") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - _, err = tk.Exec("SELECT @@global.tidb_top_sql_max_time_series_count") - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_VARIABLES_ADMIN privilege(s) for this operation") - - tk.Session().Auth(&auth.UserIdentity{ - Username: "svroot2", - Hostname: "localhost", - AuthUsername: "uroot", - AuthHostname: "%", - }, nil, nil, nil) - - tk.MustQuery(`SHOW VARIABLES LIKE 'tidb_force_priority'`).Check(testkit.Rows("tidb_force_priority NO_PRIORITY")) - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.variables_info WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows("1")) - tk.MustQuery(`SELECT COUNT(*) FROM performance_schema.session_variables WHERE variable_name = 'tidb_top_sql_max_meta_count'`).Check(testkit.Rows("1")) - tk.MustQuery(`SHOW GLOBAL VARIABLES LIKE 'tidb_enable_telemetry'`).Check(testkit.Rows("tidb_enable_telemetry OFF")) - tk.MustQuery(`SELECT COUNT(*) FROM information_schema.variables_info WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows("1")) - tk.MustQuery(`SELECT COUNT(*) FROM performance_schema.session_variables WHERE variable_name = 'tidb_enable_telemetry'`).Check(testkit.Rows("1")) - - // should not actually make any change. - tk.MustExec("SET @@global.tidb_force_priority = 'NO_PRIORITY'") - tk.MustExec("SET GLOBAL tidb_enable_telemetry = ON") - - tk.MustQuery(`SELECT @@global.tidb_force_priority`).Check(testkit.Rows("NO_PRIORITY")) - tk.MustQuery(`SELECT @@global.tidb_enable_telemetry`).Check(testkit.Rows("1")) - - tk.MustQuery(`SELECT @@hostname`).Check(testkit.Rows(variable.DefHostname)) - sem.Disable() - if hostname, err := os.Hostname(); err == nil { - tk.MustQuery(`SELECT @@hostname`).Check(testkit.Rows(hostname)) - } -} - -// TestViewDefiner tests that default roles are correctly applied in the algorithm definer -// See: https://github.com/pingcap/tidb/issues/24414 -func TestViewDefiner(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE issue24414") - tk.MustExec("USE issue24414") - tk.MustExec(`create table table1( - col1 int, - col2 int, - col3 int - )`) - tk.MustExec(`insert into table1 values (1,1,1),(2,2,2)`) - tk.MustExec(`CREATE ROLE 'ACL-mobius-admin'`) - tk.MustExec(`CREATE USER 'mobius-admin'`) - tk.MustExec(`CREATE USER 'mobius-admin-no-role'`) - tk.MustExec(`GRANT Select,Insert,Update,Delete,Create,Drop,Alter,Index,Create View,Show View ON issue24414.* TO 'ACL-mobius-admin'@'%'`) - tk.MustExec(`GRANT Select,Insert,Update,Delete,Create,Drop,Alter,Index,Create View,Show View ON issue24414.* TO 'mobius-admin-no-role'@'%'`) - tk.MustExec(`GRANT 'ACL-mobius-admin'@'%' to 'mobius-admin'@'%'`) - tk.MustExec(`SET DEFAULT ROLE ALL TO 'mobius-admin'`) - // create tables - tk.MustExec(`CREATE ALGORITHM = UNDEFINED DEFINER = 'mobius-admin'@'127.0.0.1' SQL SECURITY DEFINER VIEW test_view (col1 , col2 , col3) AS SELECT * from table1`) - tk.MustExec(`CREATE ALGORITHM = UNDEFINED DEFINER = 'mobius-admin-no-role'@'127.0.0.1' SQL SECURITY DEFINER VIEW test_view2 (col1 , col2 , col3) AS SELECT * from table1`) - - // all examples should work - tk.MustExec("select * from test_view") - tk.MustExec("select * from test_view2") -} - -func TestSecurityEnhancedModeRestrictedUsers(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER ruroot1, ruroot2, ruroot3") - tk.MustExec("CREATE ROLE notimportant") - tk.MustExec("GRANT SUPER, CREATE USER ON *.* to ruroot1 WITH GRANT OPTION") - tk.MustExec("GRANT SUPER, RESTRICTED_USER_ADMIN, CREATE USER ON *.* to ruroot2 WITH GRANT OPTION") - tk.MustExec("GRANT RESTRICTED_USER_ADMIN ON *.* to ruroot3") - tk.MustExec("GRANT notimportant TO ruroot2, ruroot3") - - sem.Enable() - defer sem.Disable() - - stmts := []string{ - "SET PASSWORD for ruroot3 = 'newpassword'", - "REVOKE notimportant FROM ruroot3", - "REVOKE SUPER ON *.* FROM ruroot3", - "DROP USER ruroot3", - } - - // ruroot1 has SUPER but in SEM will be restricted - tk.Session().Auth(&auth.UserIdentity{ - Username: "ruroot1", - Hostname: "localhost", - AuthUsername: "uroot", - AuthHostname: "%", - }, nil, nil, nil) - - for _, stmt := range stmts { - _, err := tk.Exec(stmt) - require.EqualError(t, err, "[planner:1227]Access denied; you need (at least one of) the RESTRICTED_USER_ADMIN privilege(s) for this operation") - } - - // Switch to ruroot2, it should be permitted - tk.Session().Auth(&auth.UserIdentity{ - Username: "ruroot2", - Hostname: "localhost", - AuthUsername: "uroot", - AuthHostname: "%", - }, nil, nil, nil) - - for _, stmt := range stmts { - tk.MustExec(stmt) - } -} - -func TestDynamicPrivsRegistration(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - pm := privilege.GetPrivilegeManager(tk.Session()) - - count := len(privileges.GetDynamicPrivileges()) - - require.False(t, pm.IsDynamicPrivilege("ACDC_ADMIN")) - require.Nil(t, privileges.RegisterDynamicPrivilege("ACDC_ADMIN")) - require.True(t, pm.IsDynamicPrivilege("ACDC_ADMIN")) - require.Len(t, privileges.GetDynamicPrivileges(), count+1) - - require.False(t, pm.IsDynamicPrivilege("iAmdynamIC")) - require.Nil(t, privileges.RegisterDynamicPrivilege("IAMdynamic")) - require.True(t, pm.IsDynamicPrivilege("IAMdyNAMIC")) - require.Len(t, privileges.GetDynamicPrivileges(), count+2) - - require.Equal(t, "privilege name is longer than 32 characters", privileges.RegisterDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS").Error()) - require.False(t, pm.IsDynamicPrivilege("THIS_PRIVILEGE_NAME_IS_TOO_LONG_THE_MAX_IS_32_CHARS")) - - tk = testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER privassigntest") - - // Check that all privileges registered are assignable to users, - // including the recently registered ACDC_ADMIN - for _, priv := range privileges.GetDynamicPrivileges() { - sqlGrant, err := sqlexec.EscapeSQL("GRANT %n ON *.* TO privassigntest", priv) - require.NoError(t, err) - tk.MustExec(sqlGrant) - } - // Check that all privileges registered are revokable - for _, priv := range privileges.GetDynamicPrivileges() { - sqlGrant, err := sqlexec.EscapeSQL("REVOKE %n ON *.* FROM privassigntest", priv) - require.NoError(t, err) - tk.MustExec(sqlGrant) - } -} - -func TestInfoSchemaUserPrivileges(t *testing.T) { - // Being able to read all privileges from information_schema.user_privileges requires a very specific set of permissions. - // SUPER user is not sufficient. It was observed in MySQL to require SELECT on mysql.* - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE USER isnobody, isroot, isselectonmysqluser, isselectonmysql") - tk.MustExec("GRANT SUPER ON *.* TO isroot") - tk.MustExec("GRANT SELECT ON mysql.user TO isselectonmysqluser") - tk.MustExec("GRANT SELECT ON mysql.* TO isselectonmysql") - - // First as Nobody - tk.Session().Auth(&auth.UserIdentity{ - Username: "isnobody", - Hostname: "localhost", - }, nil, nil, nil) - - // I can see myself, but I can not see other users - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO")) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows()) - - // Basically the same result as as isselectonmysqluser - tk.Session().Auth(&auth.UserIdentity{ - Username: "isselectonmysqluser", - Hostname: "localhost", - }, nil, nil, nil) - - // Now as isselectonmysqluser - // Tests discovered issue that SELECT on mysql.user is not sufficient. It must be on mysql.* - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO")) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysql'@'%'"`).Check(testkit.Rows()) - - // Now as root - tk.Session().Auth(&auth.UserIdentity{ - Username: "isroot", - Hostname: "localhost", - }, nil, nil, nil) - - // I can see myself, but I can not see other users - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows()) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def SUPER NO")) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows()) - - // Now as isselectonmysqluser - tk.Session().Auth(&auth.UserIdentity{ - Username: "isselectonmysql", - Hostname: "localhost", - }, nil, nil, nil) - - // Now as isselectonmysqluser - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isnobody'@'%'"`).Check(testkit.Rows("'isnobody'@'%' def USAGE NO")) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isroot'@'%'"`).Check(testkit.Rows("'isroot'@'%' def SUPER NO")) - tk.MustQuery(`SELECT * FROM information_schema.user_privileges WHERE grantee = "'isselectonmysqluser'@'%'"`).Check(testkit.Rows("'isselectonmysqluser'@'%' def USAGE NO")) -} - -// Issues https://github.com/pingcap/tidb/issues/25972 and https://github.com/pingcap/tidb/issues/26451 -func TestGrantOptionAndRevoke(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("DROP USER IF EXISTS u1, u2, u3, ruser") - tk.MustExec("CREATE USER u1, u2, u3, ruser") - tk.MustExec("GRANT ALL ON *.* TO ruser WITH GRANT OPTION") - tk.MustExec("GRANT SELECT ON *.* TO u1 WITH GRANT OPTION") - tk.MustExec("GRANT UPDATE, DELETE on db.* TO u1") - - tk.Session().Auth(&auth.UserIdentity{ - Username: "ruser", - Hostname: "localhost", - }, nil, nil, nil) - - tk.MustQuery(`SHOW GRANTS FOR u1`).Check(testkit.Rows("GRANT SELECT ON *.* TO 'u1'@'%' WITH GRANT OPTION", "GRANT UPDATE,DELETE ON `db`.* TO 'u1'@'%'")) - - tk.MustExec("GRANT SELECT ON d1.* to u2") - tk.MustExec("GRANT SELECT ON d2.* to u2 WITH GRANT OPTION") - tk.MustExec("GRANT SELECT ON d3.* to u2") - tk.MustExec("GRANT SELECT ON d4.* to u2") - tk.MustExec("GRANT SELECT ON d5.* to u2") - tk.MustQuery(`SHOW GRANTS FOR u2;`).Sort().Check(testkit.Rows( - "GRANT SELECT ON `d1`.* TO 'u2'@'%'", - "GRANT SELECT ON `d2`.* TO 'u2'@'%' WITH GRANT OPTION", - "GRANT SELECT ON `d3`.* TO 'u2'@'%'", - "GRANT SELECT ON `d4`.* TO 'u2'@'%'", - "GRANT SELECT ON `d5`.* TO 'u2'@'%'", - "GRANT USAGE ON *.* TO 'u2'@'%'", - )) - - tk.MustExec("grant all on hchwang.* to u3 with grant option") - tk.MustQuery(`SHOW GRANTS FOR u3;`).Check(testkit.Rows("GRANT USAGE ON *.* TO 'u3'@'%'", "GRANT ALL PRIVILEGES ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION")) - tk.MustExec("revoke all on hchwang.* from u3") - tk.MustQuery(`SHOW GRANTS FOR u3;`).Check(testkit.Rows("GRANT USAGE ON *.* TO 'u3'@'%'", "GRANT USAGE ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION")) - - // Same again but with column privileges. - - tk.MustExec("DROP TABLE IF EXISTS test.testgrant") - tk.MustExec("CREATE TABLE test.testgrant (a int)") - tk.MustExec("grant all on test.testgrant to u3 with grant option") - tk.MustExec("revoke all on test.testgrant from u3") - tk.MustQuery(`SHOW GRANTS FOR u3`).Sort().Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'u3'@'%'", - "GRANT USAGE ON `hchwang`.* TO 'u3'@'%' WITH GRANT OPTION", - "GRANT USAGE ON `test`.`testgrant` TO 'u3'@'%' WITH GRANT OPTION", - )) -} - -func createStoreAndPrepareDB(t *testing.T) kv.Storage { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database if not exists test") - tk.MustExec("create database if not exists test1") - tk.MustExec("use test") - tk.MustExec(`CREATE TABLE test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));`) - tk.MustExec(fmt.Sprintf("create database if not exists %s;", mysql.SystemDB)) - tk.MustExec(session.CreateUserTable) - tk.MustExec(session.CreateDBPrivTable) - tk.MustExec(session.CreateTablePrivTable) - tk.MustExec(session.CreateColumnPrivTable) - return store -} - -func TestDashboardClientDynamicPriv(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE ROLE dc_r1") - tk.MustExec("CREATE USER dc_u1") - tk.MustExec("GRANT dc_r1 TO dc_u1") - tk.MustExec("SET DEFAULT ROLE dc_r1 TO dc_u1") - - tk1 := testkit.NewTestKit(t, store) - tk1.Session().Auth(&auth.UserIdentity{ - Username: "dc_u1", - Hostname: "localhost", - }, nil, nil, nil) - tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'dc_u1'@'%'", - "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", - )) - tk.MustExec("GRANT DASHBOARD_CLIENT ON *.* TO dc_r1") - tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'dc_u1'@'%'", - "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", - "GRANT DASHBOARD_CLIENT ON *.* TO 'dc_u1'@'%'", - )) - tk.MustExec("REVOKE DASHBOARD_CLIENT ON *.* FROM dc_r1") - tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'dc_u1'@'%'", - "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", - )) - tk.MustExec("GRANT DASHBOARD_CLIENT ON *.* TO dc_u1") - tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'dc_u1'@'%'", - "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", - "GRANT DASHBOARD_CLIENT ON *.* TO 'dc_u1'@'%'", - )) - tk.MustExec("REVOKE DASHBOARD_CLIENT ON *.* FROM dc_u1") - tk1.MustQuery("SHOW GRANTS FOR CURRENT_USER()").Check(testkit.Rows( - "GRANT USAGE ON *.* TO 'dc_u1'@'%'", - "GRANT 'dc_r1'@'%' TO 'dc_u1'@'%'", - )) -} - -func TestGrantCreateTmpTables(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE create_tmp_table_db") - tk.MustExec("USE create_tmp_table_db") - tk.MustExec("CREATE USER u1") - tk.MustExec("CREATE TABLE create_tmp_table_table (a int)") - tk.MustExec("GRANT CREATE TEMPORARY TABLES on create_tmp_table_db.* to u1") - tk.MustExec("GRANT CREATE TEMPORARY TABLES on *.* to u1") - tk.MustGetErrCode("GRANT CREATE TEMPORARY TABLES on create_tmp_table_db.tmp to u1", mysql.ErrIllegalGrantForTable) - // Must set a session user to avoid null pointer dereference - tk.Session().Auth(&auth.UserIdentity{ - Username: "root", - Hostname: "localhost", - }, nil, nil, nil) - tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( - `GRANT CREATE TEMPORARY TABLES ON *.* TO 'u1'@'%'`, - "GRANT CREATE TEMPORARY TABLES ON `create_tmp_table_db`.* TO 'u1'@'%'")) - tk.MustExec("DROP USER u1") - tk.MustExec("DROP DATABASE create_tmp_table_db") -} - -func TestCreateTmpTablesPriv(t *testing.T) { - store := createStoreAndPrepareDB(t) - - createStmt := "CREATE TEMPORARY TABLE test.tmp(id int)" - dropStmt := "DROP TEMPORARY TABLE IF EXISTS test.tmp" - - tk := testkit.NewTestKit(t, store) - tk.MustExec(dropStmt) - tk.MustExec("CREATE TABLE test.t(id int primary key)") - tk.MustExec("CREATE SEQUENCE test.tmp") - tk.MustExec("CREATE USER vcreate, vcreate_tmp, vcreate_tmp_all") - tk.MustExec("GRANT CREATE, USAGE ON test.* TO vcreate") - tk.MustExec("GRANT CREATE TEMPORARY TABLES, USAGE ON test.* TO vcreate_tmp") - tk.MustExec("GRANT CREATE TEMPORARY TABLES, USAGE ON *.* TO vcreate_tmp_all") - - tk.Session().Auth(&auth.UserIdentity{Username: "vcreate", Hostname: "localhost"}, nil, nil, nil) - err := tk.ExecToErr(createStmt) - require.EqualError(t, err, "[planner:1044]Access denied for user 'vcreate'@'%' to database 'test'") - tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp", Hostname: "localhost"}, nil, nil, nil) - tk.MustExec(createStmt) - tk.MustExec(dropStmt) - tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp_all", Hostname: "localhost"}, nil, nil, nil) - // TODO: issue #29280 to be fixed. - //err = tk.ExecToErr(createStmt) - //require.EqualError(t, err, "[planner:1044]Access denied for user 'vcreate_tmp_all'@'%' to database 'test'") - - tests := []struct { - sql string - errcode int - }{ - { - sql: "create temporary table tmp(id int primary key)", - }, - { - sql: "insert into tmp value(1)", - }, - { - sql: "insert into tmp value(1) on duplicate key update id=1", - }, - { - sql: "replace tmp values(1)", - }, - { - sql: "insert into tmp select * from t", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "update tmp set id=1 where id=1", - }, - { - sql: "update tmp t1, t t2 set t1.id=t2.id where t1.id=t2.id", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "delete from tmp where id=1", - }, - { - sql: "delete t1 from tmp t1 join t t2 where t1.id=t2.id", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "select * from tmp where id=1", - }, - { - sql: "select * from tmp where id in (1,2)", - }, - { - sql: "select * from tmp", - }, - { - sql: "select * from tmp join t where tmp.id=t.id", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "(select * from tmp) union (select * from t)", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "create temporary table tmp1 like t", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "create table tmp(id int primary key)", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "create table t(id int primary key)", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "analyze table tmp", - }, - { - sql: "analyze table tmp, t", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "show create table tmp", - }, - // TODO: issue #29281 to be fixed. - //{ - // sql: "show create table t", - // errcode: mysql.ErrTableaccessDenied, - //}, - { - sql: "drop sequence tmp", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "alter table tmp add column c1 char(10)", - errcode: errno.ErrUnsupportedDDLOperation, - }, - { - sql: "truncate table tmp", - }, - { - sql: "drop temporary table t", - errcode: mysql.ErrBadTable, - }, - { - sql: "drop table t", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "drop table t, tmp", - errcode: mysql.ErrTableaccessDenied, - }, - { - sql: "drop temporary table tmp", - }, - } - - tk.Session().Auth(&auth.UserIdentity{Username: "vcreate_tmp", Hostname: "localhost"}, nil, nil, nil) - tk.MustExec("use test") - tk.MustExec(dropStmt) - for _, test := range tests { - if test.errcode == 0 { - tk.MustExec(test.sql) - } else { - tk.MustGetErrCode(test.sql, test.errcode) - } - } - for i, test := range tests { - preparedStmt := fmt.Sprintf("prepare stmt%d from '%s'", i, test.sql) - executeStmt := fmt.Sprintf("execute stmt%d", i) - if test.errcode == 0 { - tk.MustExec(preparedStmt) - tk.MustExec(executeStmt) - } else { - _, err := tk.Exec(preparedStmt) - if err != nil { - tk.MustGetErrCode(preparedStmt, test.errcode) - } else { - tk.MustGetErrCode(executeStmt, test.errcode) - } - } - } -} - -func TestGrantEvent(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("CREATE DATABASE event_db") - tk.MustExec("USE event_db") - tk.MustExec("CREATE USER u1") - tk.MustExec("CREATE TABLE event_table (a int)") - tk.MustExec("GRANT EVENT on event_db.* to u1") - tk.MustExec("GRANT EVENT on *.* to u1") - // Must set a session user to avoid null pointer dereferencing - tk.Session().Auth(&auth.UserIdentity{ - Username: "root", - Hostname: "localhost", - }, nil, nil, nil) - tk.MustQuery("SHOW GRANTS FOR u1").Check(testkit.Rows( - `GRANT EVENT ON *.* TO 'u1'@'%'`, - "GRANT EVENT ON `event_db`.* TO 'u1'@'%'")) - tk.MustExec("DROP USER u1") - tk.MustExec("DROP DATABASE event_db") -} - -func TestSkipGrantTable(t *testing.T) { - save := config.GetGlobalConfig() - config.UpdateGlobal(func(c *config.Config) { c.Security.SkipGrantTable = true }) - defer config.StoreGlobalConfig(save) - - store := createStoreAndPrepareDB(t) - - // Issue 29317 - tk := testkit.NewTestKit(t, store) - tk.MustExec(`CREATE USER 'test1'@'%';`) - tk.MustExec(`GRANT BACKUP_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTORE_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RELOAD ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SHUTDOWN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTRICTED_CONNECTION_ADMIN, CONNECTION_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTRICTED_USER_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT PROCESS ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SHUTDOWN ON *.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SELECT, INSERT, UPDATE, DELETE ON mysql.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SELECT ON information_schema.* TO 'test1'@'%';`) - tk.MustExec(`GRANT SELECT ON performance_schema.* TO 'test1'@'%';`) - tk.MustExec(`GRANT ALL PRIVILEGES ON *.* TO root;`) - tk.MustExec(`revoke SHUTDOWN on *.* from root;`) - tk.MustExec(`revoke CONFIG on *.* from root;`) - - tk.MustExec(`CREATE USER 'test2'@'%' IDENTIFIED BY '12345';`) - tk.MustExec(`GRANT PROCESS, CONFIG ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT SHOW DATABASES ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT DASHBOARD_CLIENT ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'test2'@'%';`) - tk.MustExec(`GRANT RESTRICTED_USER_ADMIN ON *.* TO 'test2'@'%';`) -} - -func TestIssue29823(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create user u1") - tk.MustExec("create role r1") - tk.MustExec("create table t1 (c1 int)") - tk.MustExec("grant select on t1 to r1") - tk.MustExec("grant r1 to u1") - - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "u1", Hostname: "%"}, nil, nil, nil)) - tk2.MustExec("set role all") - tk2.MustQuery("select current_role()").Check(testkit.Rows("`r1`@`%`")) - tk2.MustQuery("select * from test.t1").Check(testkit.Rows()) - tk2.MustQuery("show databases like 'test'").Check(testkit.Rows("test")) - tk2.MustQuery("show tables from test").Check(testkit.Rows("t1")) - - tk.MustExec("revoke r1 from u1") - tk2.MustQuery("select current_role()").Check(testkit.Rows("`r1`@`%`")) - err := tk2.ExecToErr("select * from test.t1") - require.EqualError(t, err, "[planner:1142]SELECT command denied to user 'u1'@'%' for table 't1'") - tk2.MustQuery("show databases like 'test'").Check(testkit.Rows()) - err = tk2.QueryToErr("show tables from test") - require.EqualError(t, err, "[executor:1044]Access denied for user 'u1'@'%' to database 'test'") -} - -func TestIssue37488(t *testing.T) { - store := createStoreAndPrepareDB(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("CREATE USER dba_test@'%';") - tk.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO 'dba_test'@'%';") - tk.MustExec("CREATE USER dba_test@'192.168.%';") - tk.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP,ALTER ON test.* TO 'dba_test'@'192.168.%';") - - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "dba_test", Hostname: "192.168.13.15"}, nil, nil, nil)) - tk.MustQuery("select current_user()").Check(testkit.Rows("dba_test@192.168.%")) - tk.MustExec("DROP TABLE IF EXISTS a;") // succ -} - -func TestCheckPasswordExpired(t *testing.T) { - sessionVars := variable.NewSessionVars(nil) - sessionVars.GlobalVarsAccessor = variable.NewMockGlobalAccessor4Tests() - record := privileges.NewUserRecord("%", "root") - userPrivilege := privileges.NewUserPrivileges(privileges.NewHandle(), nil) - - record.PasswordExpired = true - _, err := userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") - - record.PasswordExpired = false - err = sessionVars.GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.DefaultPasswordLifetime, "2") - require.NoError(t, err) - // use default_password_lifetime - record.PasswordLifeTime = -1 - record.PasswordLastChanged = time.Now().AddDate(0, 0, -2) - time.Sleep(time.Second) - _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") - record.PasswordLastChanged = time.Now().AddDate(0, 0, -1) - _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.NoError(t, err) - - // never expire - record.PasswordLifeTime = 0 - record.PasswordLastChanged = time.Now().AddDate(0, 0, -10) - _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.NoError(t, err) - - // expire with the specified time - record.PasswordLifeTime = 3 - record.PasswordLastChanged = time.Now().AddDate(0, 0, -3) - time.Sleep(time.Second) - _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.ErrorContains(t, err, "Your password has expired. To log in you must change it using a client that supports expired passwords") - record.PasswordLastChanged = time.Now().AddDate(0, 0, -2) - _, err = userPrivilege.CheckPasswordExpired(sessionVars, &record) - require.NoError(t, err) -} - -func TestPasswordExpireWithoutSandBoxMode(t *testing.T) { - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) - - // PASSWORD EXPIRE - user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost"} - tk := testkit.NewTestKit(t, store) - err := tk.Session().Auth(user, nil, nil, nil) - require.ErrorContains(t, err, "Your password has expired") - - // PASSWORD EXPIRE NEVER - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' IDENTIFIED BY '' PASSWORD EXPIRE NEVER`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - - // PASSWORD EXPIRE INTERVAL N DAY - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE INTERVAL 2 DAY`) - rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 1 DAY) where user='testuser'`) - rootTk.MustExec(`FLUSH PRIVILEGES`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 2 DAY) where user='testuser'`) - rootTk.MustExec(`FLUSH PRIVILEGES`) - time.Sleep(2 * time.Second) - err = tk.Session().Auth(user, nil, nil, nil) - require.ErrorContains(t, err, "Your password has expired") - - // PASSWORD EXPIRE DEFAULT - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE DEFAULT`) - rootTk.MustExec(`SET GLOBAL default_password_lifetime = 2`) - err = tk.Session().Auth(user, nil, nil, nil) - require.ErrorContains(t, err, "Your password has expired") - rootTk.MustExec(`SET GLOBAL default_password_lifetime = 3`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) -} - -func TestPasswordExpireWithSandBoxMode(t *testing.T) { - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) - variable.IsSandBoxModeEnabled.Store(true) - - // PASSWORD EXPIRE - user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost"} - tk := testkit.NewTestKit(t, store) - err := tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.True(t, tk.Session().InSandBoxMode()) - tk.Session().DisableSandBoxMode() - - // PASSWORD EXPIRE NEVER - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' IDENTIFIED BY '' PASSWORD EXPIRE NEVER`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.False(t, tk.Session().InSandBoxMode()) - - // PASSWORD EXPIRE INTERVAL N DAY - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE INTERVAL 2 DAY`) - rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 1 DAY) where user='testuser'`) - rootTk.MustExec(`FLUSH PRIVILEGES`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.False(t, tk.Session().InSandBoxMode()) - rootTk.MustExec(`UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 2 DAY) where user='testuser'`) - rootTk.MustExec(`FLUSH PRIVILEGES`) - time.Sleep(2 * time.Second) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.True(t, tk.Session().InSandBoxMode()) - tk.Session().DisableSandBoxMode() - - // PASSWORD EXPIRE DEFAULT - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' PASSWORD EXPIRE DEFAULT`) - rootTk.MustExec(`SET GLOBAL default_password_lifetime = 2`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.True(t, tk.Session().InSandBoxMode()) - tk.Session().DisableSandBoxMode() - rootTk.MustExec(`SET GLOBAL default_password_lifetime = 3`) - err = tk.Session().Auth(user, nil, nil, nil) - require.NoError(t, err) - require.False(t, tk.Session().InSandBoxMode()) -} - -func TestVerificationInfoWithSessionTokenPlugin(t *testing.T) { - // prepare signing certs - tempDir := t.TempDir() - certPath := filepath.Join(tempDir, "test1_cert.pem") - keyPath := filepath.Join(tempDir, "test1_key.pem") - err := util.CreateCertificates(certPath, keyPath, 4096, x509.RSA, x509.UnknownSignatureAlgorithm) - require.NoError(t, err) - sessionstates.SetKeyPath(keyPath) - sessionstates.SetCertPath(certPath) - - // prepare user - store := createStoreAndPrepareDB(t) - rootTk := testkit.NewTestKit(t, store) - rootTk.MustExec(`CREATE USER 'testuser'@'localhost' PASSWORD EXPIRE`) - // prepare session token - token, err := sessionstates.CreateSessionToken("testuser") - require.NoError(t, err) - tokenBytes, err := json.Marshal(token) - require.NoError(t, err) - - // Test password expiration without sandbox. - user := &auth.UserIdentity{Username: "testuser", Hostname: "localhost", AuthPlugin: mysql.AuthTiDBSessionToken} - tk := testkit.NewTestKit(t, store) - err = tk.Session().Auth(user, tokenBytes, nil, nil) - require.NoError(t, err) - require.False(t, tk.Session().InSandBoxMode()) - - // Test password expiration with sandbox. - variable.IsSandBoxModeEnabled.Store(true) - err = tk.Session().Auth(user, tokenBytes, nil, nil) - require.NoError(t, err) - require.False(t, tk.Session().InSandBoxMode()) - - // Enable resource group. - variable.EnableResourceControl.Store(true) - err = tk.Session().Auth(user, tokenBytes, nil, nil) - require.NoError(t, err) - require.Equal(t, "default", tk.Session().GetSessionVars().ResourceGroupName) - - // Non-default resource group. - rootTk.MustExec("CREATE RESOURCE GROUP rg1 RU_PER_SEC = 999") - rootTk.MustExec(`ALTER USER 'testuser'@'localhost' RESOURCE GROUP rg1`) - err = tk.Session().Auth(user, tokenBytes, nil, nil) - require.NoError(t, err) - require.Equal(t, "rg1", tk.Session().GetSessionVars().ResourceGroupName) - - // Wrong token - err = tk.Session().Auth(user, nil, nil, nil) - require.ErrorContains(t, err, "Access denied") -} - -func TestNilHandleInConnectionVerification(t *testing.T) { - config.GetGlobalConfig().Security.SkipGrantTable = true - privileges.SkipWithGrant = true - defer func() { - config.GetGlobalConfig().Security.SkipGrantTable = false - privileges.SkipWithGrant = false - }() - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, nil, nil)) -} - -func testShowGrantsSQLMode(t *testing.T, tk *testkit.TestKit, expected []string) { - pc := privilege.GetPrivilegeManager(tk.Session()) - gs, err := pc.ShowGrants(tk.Session(), &auth.UserIdentity{Username: "show_sql_mode", Hostname: "localhost"}, nil) - require.NoError(t, err) - require.Len(t, gs, 2) - require.True(t, testutil.CompareUnorderedStringSlice(gs, expected), fmt.Sprintf("gs: %v, expected: %v", gs, expected)) -} - -func TestShowGrantsSQLMode(t *testing.T) { - store := createStoreAndPrepareDB(t) - - tk := testkit.NewTestKit(t, store) - ctx, _ := tk.Session().(sessionctx.Context) - tk.MustExec(`CREATE USER 'show_sql_mode'@'localhost' identified by '123';`) - tk.MustExec(`GRANT Select ON test.* TO 'show_sql_mode'@'localhost';`) - - testShowGrantsSQLMode(t, tk, []string{ - "GRANT USAGE ON *.* TO 'show_sql_mode'@'localhost'", - "GRANT SELECT ON `test`.* TO 'show_sql_mode'@'localhost'", - }) - - ctx.GetSessionVars().SQLMode = mysql.SetSQLMode(ctx.GetSessionVars().SQLMode, mysql.ModeANSIQuotes) - testShowGrantsSQLMode(t, tk, []string{ - "GRANT USAGE ON *.* TO 'show_sql_mode'@'localhost'", - "GRANT SELECT ON \"test\".* TO 'show_sql_mode'@'localhost'", - }) -} diff --git a/resourcemanager/BUILD.bazel b/resourcemanager/BUILD.bazel deleted file mode 100644 index f2b8cc6e00361..0000000000000 --- a/resourcemanager/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "resourcemanager", - srcs = [ - "rm.go", - "schedule.go", - ], - importpath = "github.com/pingcap/tidb/resourcemanager", - visibility = ["//visibility:public"], - deps = [ - "//resourcemanager/scheduler", - "//resourcemanager/util", - "//util", - "//util/cpu", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "resourcemanager_test", - timeout = "short", - srcs = ["schedule_test.go"], - embed = [":resourcemanager"], - flaky = True, - deps = [ - "//resourcemanager/scheduler", - "//resourcemanager/util", - "@com_github_stretchr_testify//require", - ], -) diff --git a/resourcemanager/pool/BUILD.bazel b/resourcemanager/pool/BUILD.bazel deleted file mode 100644 index 2b4fecc6e0542..0000000000000 --- a/resourcemanager/pool/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "pool", - srcs = ["basepool.go"], - importpath = "github.com/pingcap/tidb/resourcemanager/pool", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_atomic//:atomic"], -) diff --git a/resourcemanager/pool/spool/BUILD.bazel b/resourcemanager/pool/spool/BUILD.bazel deleted file mode 100644 index 6079538cbcc91..0000000000000 --- a/resourcemanager/pool/spool/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "spool", - srcs = [ - "option.go", - "spool.go", - ], - importpath = "github.com/pingcap/tidb/resourcemanager/pool/spool", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//resourcemanager", - "//resourcemanager/pool", - "//resourcemanager/poolmanager", - "//resourcemanager/util", - "//util/logutil", - "//util/mathutil", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_sasha_s_go_deadlock//:go-deadlock", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "spool_test", - timeout = "short", - srcs = [ - "main_test.go", - "spool_test.go", - ], - embed = [":spool"], - flaky = True, - race = "on", - shard_count = 6, - deps = [ - "//resourcemanager/pool", - "//resourcemanager/util", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/resourcemanager/pool/spool/main_test.go b/resourcemanager/pool/spool/main_test.go deleted file mode 100644 index 05949577a7f08..0000000000000 --- a/resourcemanager/pool/spool/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package spool - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/resourcemanager/pool/workerpool/BUILD.bazel b/resourcemanager/pool/workerpool/BUILD.bazel deleted file mode 100644 index 619e7eef69423..0000000000000 --- a/resourcemanager/pool/workerpool/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "workerpool", - srcs = ["workerpool.go"], - importpath = "github.com/pingcap/tidb/resourcemanager/pool/workerpool", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//resourcemanager/util", - "//util", - "//util/syncutil", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "workerpool_test", - timeout = "short", - srcs = [ - "main_test.go", - "workpool_test.go", - ], - embed = [":workerpool"], - flaky = True, - race = "on", - shard_count = 4, - deps = [ - "//resourcemanager/util", - "//testkit/testsetup", - "//util/logutil", - "@com_github_stretchr_testify//require", - "@org_golang_x_sync//errgroup", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/resourcemanager/pool/workerpool/main_test.go b/resourcemanager/pool/workerpool/main_test.go deleted file mode 100644 index 10bc1c57288ac..0000000000000 --- a/resourcemanager/pool/workerpool/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package workerpool - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/resourcemanager/poolmanager/BUILD.bazel b/resourcemanager/poolmanager/BUILD.bazel deleted file mode 100644 index b7977a1fb2f0c..0000000000000 --- a/resourcemanager/poolmanager/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "poolmanager", - srcs = [ - "task_manager.go", - "task_manager_iterator.go", - "task_manager_scheduler.go", - ], - importpath = "github.com/pingcap/tidb/resourcemanager/poolmanager", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_atomic//:atomic"], -) diff --git a/resourcemanager/scheduler/BUILD.bazel b/resourcemanager/scheduler/BUILD.bazel deleted file mode 100644 index 39bd88f030372..0000000000000 --- a/resourcemanager/scheduler/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "scheduler", - srcs = [ - "cpu_scheduler.go", - "scheduler.go", - ], - importpath = "github.com/pingcap/tidb/resourcemanager/scheduler", - visibility = ["//visibility:public"], - deps = [ - "//resourcemanager/util", - "//util/cpu", - ], -) diff --git a/resourcemanager/scheduler/scheduler.go b/resourcemanager/scheduler/scheduler.go deleted file mode 100644 index 521536a741dee..0000000000000 --- a/resourcemanager/scheduler/scheduler.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package scheduler - -import ( - "github.com/pingcap/tidb/resourcemanager/util" -) - -// Command is the command for scheduler -type Command int - -const ( - // Downclock is to reduce the number of concurrency. - Downclock Command = iota - // Hold is to hold the number of concurrency. - Hold - // Overclock is to increase the number of concurrency. - Overclock -) - -// Scheduler is a scheduler interface -type Scheduler interface { - Tune(component util.Component, p util.GoroutinePool) Command -} diff --git a/resourcemanager/util/BUILD.bazel b/resourcemanager/util/BUILD.bazel deleted file mode 100644 index ed91648321472..0000000000000 --- a/resourcemanager/util/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "util", - srcs = [ - "mock_gpool.go", - "shard_pool_map.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/resourcemanager/util", - visibility = ["//visibility:public"], - deps = [ - "//util/intest", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "util_test", - timeout = "short", - srcs = ["shard_pool_map_test.go"], - embed = [":util"], - flaky = True, - deps = [ - "//util/intest", - "@com_github_stretchr_testify//require", - ], -) diff --git a/server/BUILD.bazel b/server/BUILD.bazel deleted file mode 100644 index db313470a622a..0000000000000 --- a/server/BUILD.bazel +++ /dev/null @@ -1,187 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "server", - srcs = [ - "conn.go", - "conn_stmt.go", - "driver.go", - "driver_tidb.go", - "extension.go", - "extract.go", - "http_handler.go", - "http_status.go", - "mock_conn.go", - "rpc_server.go", - "server.go", - "stat.go", - "tokenlimiter.go", - ], - importpath = "github.com/pingcap/tidb/server", - visibility = ["//visibility:public"], - deps = [ - "//autoid_service", - "//config", - "//domain", - "//domain/infosync", - "//errno", - "//executor", - "//executor/mppcoordmanager", - "//expression", - "//extension", - "//infoschema", - "//kv", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//plugin", - "//privilege", - "//privilege/conn", - "//privilege/privileges", - "//privilege/privileges/ldap", - "//server/err", - "//server/handler", - "//server/handler/extactorhandler", - "//server/handler/optimizor", - "//server/handler/tikvhandler", - "//server/handler/ttlhandler", - "//server/internal", - "//server/internal/column", - "//server/internal/dump", - "//server/internal/handshake", - "//server/internal/parse", - "//server/internal/resultset", - "//server/internal/util", - "//server/metrics", - "//session", - "//session/txninfo", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//statistics/handle", - "//store", - "//store/driver/error", - "//store/helper", - "//tablecodec", - "//types", - "//util", - "//util/arena", - "//util/chunk", - "//util/cpuprofile", - "//util/dbterror/exeerrors", - "//util/execdetails", - "//util/fastrand", - "//util/hack", - "//util/intest", - "//util/logutil", - "//util/memory", - "//util/printer", - "//util/sqlexec", - "//util/sys/linux", - "//util/timeutil", - "//util/tls", - "//util/topsql", - "//util/topsql/state", - "//util/topsql/stmtstats", - "//util/tracing", - "//util/versioninfo", - "@com_github_blacktear23_go_proxyprotocol//:go-proxyprotocol", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_fn//:fn", - "@com_github_pingcap_kvproto//pkg/autoid", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/diagnosticspb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_kvproto//pkg/tikvpb", - "@com_github_pingcap_sysutil//:sysutil", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_golang//prometheus/promhttp", - "@com_github_soheilhy_cmux//:cmux", - "@com_github_stretchr_testify//require", - "@com_github_tiancaiamao_appdash//traceapp", - "@com_github_tikv_client_go_v2//util", - "@com_sourcegraph_sourcegraph_appdash_data//:appdash-data", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//channelz/service", - "@org_golang_google_grpc//keepalive", - "@org_golang_google_grpc//peer", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "server_test", - timeout = "short", - srcs = [ - "conn_stmt_test.go", - "conn_test.go", - "driver_tidb_test.go", - "main_test.go", - "mock_conn_test.go", - "server_test.go", - "stat_test.go", - "tidb_library_test.go", - "tidb_test.go", - ], - data = glob(["testdata/**"]), - embed = [":server"], - flaky = True, - shard_count = 48, - deps = [ - "//config", - "//domain", - "//domain/infosync", - "//extension", - "//keyspace", - "//kv", - "//metrics", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//server/internal", - "//server/internal/column", - "//server/internal/handshake", - "//server/internal/parse", - "//server/internal/testutil", - "//server/internal/util", - "//session", - "//sessionctx/variable", - "//sessiontxn", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit", - "//testkit/external", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util", - "//util/arena", - "//util/chunk", - "//util/dbterror/exeerrors", - "//util/replayer", - "//util/syncutil", - "//util/topsql/state", - "@com_github_docker_go_units//:go-units", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/server/err/BUILD.bazel b/server/err/BUILD.bazel deleted file mode 100644 index 02e7a65dfc48d..0000000000000 --- a/server/err/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "err", - srcs = ["error.go"], - importpath = "github.com/pingcap/tidb/server/err", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//util/dbterror", - ], -) diff --git a/server/err/error.go b/server/err/error.go deleted file mode 100644 index 2132ecd9b4f88..0000000000000 --- a/server/err/error.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package err - -import ( - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -var ( - // ErrInvalidType is returned when the type of a value is not expected. - ErrInvalidType = dbterror.ClassServer.NewStd(errno.ErrInvalidType) - // ErrInvalidSequence is returned when the sequence of method invocations is not correct. - ErrInvalidSequence = dbterror.ClassServer.NewStd(errno.ErrInvalidSequence) - // ErrNotAllowedCommand is returned when the used command is not in the expected list. - ErrNotAllowedCommand = dbterror.ClassServer.NewStd(errno.ErrNotAllowedCommand) - // ErrAccessDenied is returned when the user does not have sufficient privileges. - ErrAccessDenied = dbterror.ClassServer.NewStd(errno.ErrAccessDenied) - // ErrAccessDeniedNoPassword is returned when trying to use an anonymous user without password is not allowed. - ErrAccessDeniedNoPassword = dbterror.ClassServer.NewStd(errno.ErrAccessDeniedNoPassword) - // ErrConCount is returned when too many connections are established by the user. - ErrConCount = dbterror.ClassServer.NewStd(errno.ErrConCount) - // ErrSecureTransportRequired is returned when the user tries to connect without SSL. - ErrSecureTransportRequired = dbterror.ClassServer.NewStd(errno.ErrSecureTransportRequired) - // ErrMultiStatementDisabled is returned when the user tries to send multiple statements in one statement. - ErrMultiStatementDisabled = dbterror.ClassServer.NewStd(errno.ErrMultiStatementDisabled) - // ErrNewAbortingConnection is returned when the user tries to connect with an aborting connection. - ErrNewAbortingConnection = dbterror.ClassServer.NewStd(errno.ErrNewAbortingConnection) - // ErrNotSupportedAuthMode is returned when the user uses an unsupported authentication method. - ErrNotSupportedAuthMode = dbterror.ClassServer.NewStd(errno.ErrNotSupportedAuthMode) - // ErrNetPacketTooLarge is returned when the user sends a packet too large. - ErrNetPacketTooLarge = dbterror.ClassServer.NewStd(errno.ErrNetPacketTooLarge) - // ErrMustChangePassword is returned when the user must change the password. - ErrMustChangePassword = dbterror.ClassServer.NewStd(errno.ErrMustChangePassword) -) diff --git a/server/extension.go b/server/extension.go deleted file mode 100644 index 367946c6d7bdb..0000000000000 --- a/server/extension.go +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package server - -import ( - "fmt" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" -) - -func (cc *clientConn) onExtensionConnEvent(tp extension.ConnEventTp, err error) { - if cc.extensions == nil { - return - } - - var connInfo *variable.ConnectionInfo - var activeRoles []*auth.RoleIdentity - var sessionAlias string - if ctx := cc.getCtx(); ctx != nil { - sessVars := ctx.GetSessionVars() - connInfo = sessVars.ConnectionInfo - sessionAlias = sessVars.SessionAlias - activeRoles = sessVars.ActiveRoles - } - - if connInfo == nil { - connInfo = cc.connectInfo() - } - - info := &extension.ConnEventInfo{ - ConnectionInfo: connInfo, - SessionAlias: sessionAlias, - ActiveRoles: activeRoles, - Error: err, - } - - cc.extensions.OnConnectionEvent(tp, info) -} - -func (cc *clientConn) onExtensionStmtEnd(node interface{}, stmtCtxValid bool, err error, args ...expression.Expression) { - if !cc.extensions.HasStmtEventListeners() { - return - } - - ctx := cc.getCtx() - if ctx == nil { - return - } - - tp := extension.StmtSuccess - if err != nil { - tp = extension.StmtError - } - - sessVars := ctx.GetSessionVars() - info := &stmtEventInfo{ - sessVars: sessVars, - err: err, - } - - switch stmt := node.(type) { - case *ast.ExecuteStmt: - info.executeStmt = stmt - info.stmtNode = stmt - case PreparedStatement: - info.executeStmtID = uint32(stmt.ID()) - prepared, _ := sessVars.GetPreparedStmtByID(info.executeStmtID) - info.executeStmt = &ast.ExecuteStmt{ - PrepStmt: prepared, - BinaryArgs: args, - } - info.stmtNode = info.executeStmt - case ast.StmtNode: - info.stmtNode = stmt - } - - if stmtCtxValid { - info.sc = sessVars.StmtCtx - } else { - info.sc = stmtctx.NewStmtCtx() - } - cc.extensions.OnStmtEvent(tp, info) -} - -// onSQLParseFailed will be called when sql parse failed -func (cc *clientConn) onExtensionSQLParseFailed(sql string, err error) { - if !cc.extensions.HasStmtEventListeners() { - return - } - - cc.extensions.OnStmtEvent(extension.StmtError, &stmtEventInfo{ - sessVars: cc.getCtx().GetSessionVars(), - err: err, - failedParseText: sql, - }) -} - -func (cc *clientConn) onExtensionBinaryExecuteEnd(prep PreparedStatement, args []expression.Expression, stmtCtxValid bool, err error) { - cc.onExtensionStmtEnd(prep, stmtCtxValid, err, args...) -} - -type stmtEventInfo struct { - sessVars *variable.SessionVars - sc *stmtctx.StatementContext - stmtNode ast.StmtNode - // execute info - executeStmtID uint32 - executeStmt *ast.ExecuteStmt - executePreparedCached bool - executePreparedCache *core.PlanCacheStmt - // error will only be valid when the stmt is failed - err error - // failedParseText will only present on parse failed - failedParseText string -} - -func (e *stmtEventInfo) ConnectionInfo() *variable.ConnectionInfo { - return e.sessVars.ConnectionInfo -} - -func (e *stmtEventInfo) SessionAlias() string { - return e.sessVars.SessionAlias -} - -func (e *stmtEventInfo) StmtNode() ast.StmtNode { - return e.stmtNode -} - -func (e *stmtEventInfo) ExecuteStmtNode() *ast.ExecuteStmt { - return e.executeStmt -} - -func (e *stmtEventInfo) ExecutePreparedStmt() ast.StmtNode { - if cache := e.ensureExecutePreparedCache(); cache != nil { - return cache.PreparedAst.Stmt - } - return nil -} - -func (e *stmtEventInfo) PreparedParams() []types.Datum { - return e.sessVars.PlanCacheParams.AllParamValues() -} - -func (e *stmtEventInfo) OriginalText() string { - if sql := e.ensureStmtContextOriginalSQL(); sql != "" { - return sql - } - - if e.executeStmtID != 0 { - return binaryExecuteStmtText(e.executeStmtID) - } - - return e.failedParseText -} - -func (e *stmtEventInfo) SQLDigest() (normalized string, digest *parser.Digest) { - if sql := e.ensureStmtContextOriginalSQL(); sql != "" { - return e.sc.SQLDigest() - } - - if e.executeStmtID != 0 { - return binaryExecuteStmtText(e.executeStmtID), nil - } - - return e.failedParseText, nil -} - -func (e *stmtEventInfo) User() *auth.UserIdentity { - return e.sessVars.User -} - -func (e *stmtEventInfo) ActiveRoles() []*auth.RoleIdentity { - return e.sessVars.ActiveRoles -} - -func (e *stmtEventInfo) CurrentDB() string { - return e.sessVars.CurrentDB -} - -func (e *stmtEventInfo) AffectedRows() uint64 { - if e.sc == nil || e.err != nil { - return 0 - } - return e.sc.AffectedRows() -} - -func (e *stmtEventInfo) RelatedTables() []stmtctx.TableEntry { - if e.sc == nil { - return nil - } - return e.sc.Tables -} - -func (e *stmtEventInfo) GetError() error { - return e.err -} - -func (e *stmtEventInfo) ensureExecutePreparedCache() *core.PlanCacheStmt { - if e.executeStmt == nil { - return nil - } - - if !e.executePreparedCached { - e.executePreparedCache, _ = core.GetPreparedStmt(e.executeStmt, e.sessVars) - e.executePreparedCached = true - } - - return e.executePreparedCache -} - -func (e *stmtEventInfo) ensureStmtContextOriginalSQL() string { - if e.sc == nil { - return "" - } - - if sql := e.sc.OriginalSQL; sql != "" { - return sql - } - - if planCache := e.ensureExecutePreparedCache(); planCache != nil { - e.sc.OriginalSQL = planCache.PreparedAst.Stmt.Text() - e.sc.InitSQLDigest(planCache.NormalizedSQL, planCache.SQLDigest) - } - - if e.sc.OriginalSQL == "" && e.executeStmtID == 0 && e.stmtNode != nil { - e.sc.OriginalSQL = e.stmtNode.Text() - } - - return e.sc.OriginalSQL -} - -func binaryExecuteStmtText(id uint32) string { - return fmt.Sprintf("BINARY EXECUTE (ID %d)", id) -} diff --git a/server/extract.go b/server/extract.go deleted file mode 100644 index c459a66ad1938..0000000000000 --- a/server/extract.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package server - -import "github.com/pingcap/tidb/server/handler/extactorhandler" - -// newExtractServeHandler returns extractTaskServeHandler -func (s *Server) newExtractServeHandler() *extactorhandler.ExtractTaskServeHandler { - esh := &extactorhandler.ExtractTaskServeHandler{} - if s.dom != nil { - esh = extactorhandler.NewExtractTaskServeHandler(s.dom.GetExtractHandle()) - } - return esh -} diff --git a/server/handler/BUILD.bazel b/server/handler/BUILD.bazel deleted file mode 100644 index d0c22ad70c3b0..0000000000000 --- a/server/handler/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "handler", - srcs = [ - "tikv_handler.go", - "upgrade_handler.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/server/handler", - visibility = ["//visibility:public"], - deps = [ - "//domain/infosync", - "//infoschema", - "//kv", - "//parser/model", - "//parser/terror", - "//session", - "//sessionctx/stmtctx", - "//store/driver/error", - "//store/helper", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util/codec", - "//util/logutil", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) diff --git a/server/handler/extactorhandler/BUILD.bazel b/server/handler/extactorhandler/BUILD.bazel deleted file mode 100644 index e6245e4790520..0000000000000 --- a/server/handler/extactorhandler/BUILD.bazel +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "extactorhandler", - srcs = ["extactor.go"], - importpath = "github.com/pingcap/tidb/server/handler/extactorhandler", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//server/handler", - "//types", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "extactorhandler_test", - timeout = "short", - srcs = [ - "extract_test.go", - "main_test.go", - ], - flaky = True, - deps = [ - ":extactorhandler", - "//config", - "//metrics", - "//server", - "//server/internal/testserverclient", - "//server/internal/testutil", - "//server/internal/util", - "//session", - "//store/mockstore/unistore", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/stmtsummary/v2:stmtsummary", - "//util/topsql/state", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/server/handler/extactorhandler/extract_test.go b/server/handler/extactorhandler/extract_test.go deleted file mode 100644 index bdef56ef2805a..0000000000000 --- a/server/handler/extactorhandler/extract_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package extactorhandler_test - -import ( - "database/sql" - "fmt" - "net/http" - "net/url" - "os" - "testing" - "time" - - "github.com/gorilla/mux" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - server2 "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/handler/extactorhandler" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - "github.com/stretchr/testify/require" -) - -func TestExtractHandler(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - - driver := server2.NewTiDBDriver(store) - client := testserverclient.NewTestServerClient() - cfg := util.NewTestConfig() - cfg.Port = client.Port - cfg.Status.StatusPort = client.StatusPort - cfg.Status.ReportStatus = true - - server, err := server2.NewServer(cfg, driver) - require.NoError(t, err) - defer server.Close() - - dom, err := session.GetDomain(store) - require.NoError(t, err) - server.SetDomain(dom) - - client.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - client.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - client.WaitUntilServerOnline() - startTime := time.Now() - time.Sleep(time.Second) - prepareData4ExtractPlanTask(t, client) - time.Sleep(time.Second) - endTime := time.Now() - eh := &extactorhandler.ExtractTaskServeHandler{ExtractHandler: dom.GetExtractHandle()} - router := mux.NewRouter() - router.Handle("/extract_task/dump", eh) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/extractTaskServeHandler", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/extractTaskServeHandler")) - }() - resp0, err := client.FetchStatus(fmt.Sprintf("/extract_task/dump?type=plan&begin=%s&end=%s", - url.QueryEscape(startTime.Format(types.TimeFormat)), url.QueryEscape(endTime.Format(types.TimeFormat)))) - require.NoError(t, err) - defer func() { - require.NoError(t, resp0.Body.Close()) - }() - require.Equal(t, resp0.StatusCode, http.StatusOK) - resp0, err = client.FetchStatus("/extract_task/dump?type=plan") - require.NoError(t, err) - defer func() { - require.NoError(t, resp0.Body.Close()) - }() - require.Equal(t, resp0.StatusCode, http.StatusOK) -} - -func prepareData4ExtractPlanTask(t *testing.T, client *testserverclient.TestServerClient) { - db, err := sql.Open("mysql", client.GetDSN()) - require.NoError(t, err, "Error connecting") - defer func() { - err := db.Close() - require.NoError(t, err) - }() - tk := testkit.NewDBTestKit(t, db) - tk.MustExec("use test") - tk.MustExec("create table t(id int)") - tk.MustExec("select * from t") -} - -func setupStmtSummary() { - stmtsummaryv2.Setup(&stmtsummaryv2.Config{ - Filename: "tidb-statements.log", - }) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = true - }) -} - -func closeStmtSummary() { - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = false - }) - stmtsummaryv2.GlobalStmtSummary.Close() - stmtsummaryv2.GlobalStmtSummary = nil - _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) -} diff --git a/server/handler/extactorhandler/main_test.go b/server/handler/extactorhandler/main_test.go deleted file mode 100644 index 3af5e01f1dfdb..0000000000000 --- a/server/handler/extactorhandler/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package extactorhandler_test - -import ( - "fmt" - "os" - "reflect" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testsetup" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - server.RunInGoTest = true - testsetup.SetupForCommonTest() - topsqlstate.EnableTopSQL() - unistore.CheckResourceTagForTopSQLInGoTest = true - - // AsyncCommit will make DDL wait 2.5s before changing to the next state. - // Set schema lease to avoid it from making CI slow. - session.SetSchemaLease(0) - - tikv.EnableFailpoints() - - metrics.RegisterMetrics() - - // sanity check: the global config should not be changed by other pkg init function. - // see also https://github.com/pingcap/tidb/issues/22162 - defaultConfig := config.NewConfig() - globalConfig := config.GetGlobalConfig() - if !reflect.DeepEqual(defaultConfig, globalConfig) { - _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") - _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("time.Sleep"), - goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/server.NewServer.func1"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/server/handler/optimizor/BUILD.bazel b/server/handler/optimizor/BUILD.bazel deleted file mode 100644 index 3b9a59077f586..0000000000000 --- a/server/handler/optimizor/BUILD.bazel +++ /dev/null @@ -1,74 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "optimizor", - srcs = [ - "optimize_trace.go", - "plan_replayer.go", - "statistics_handler.go", - ], - importpath = "github.com/pingcap/tidb/server/handler/optimizor", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//domain/infosync", - "//infoschema", - "//parser/model", - "//parser/mysql", - "//server/handler", - "//session", - "//sessionctx/variable", - "//statistics/handle", - "//statistics/handle/storage", - "//table", - "//types", - "//util", - "//util/logutil", - "//util/replayer", - "@com_github_burntsushi_toml//:toml", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "optimizor_test", - timeout = "short", - srcs = [ - "main_test.go", - "optimize_trace_test.go", - "plan_replayer_test.go", - "statistics_handler_test.go", - ], - flaky = True, - shard_count = 4, - deps = [ - ":optimizor", - "//config", - "//domain", - "//kv", - "//metrics", - "//parser/model", - "//server", - "//server/internal/testserverclient", - "//server/internal/testutil", - "//server/internal/util", - "//session", - "//statistics/handle/globalstats", - "//statistics/handle/storage", - "//store/mockstore/unistore", - "//testkit", - "//testkit/testsetup", - "//util/topsql/state", - "@com_github_burntsushi_toml//:toml", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/server/handler/optimizor/main_test.go b/server/handler/optimizor/main_test.go deleted file mode 100644 index b1e601b012b83..0000000000000 --- a/server/handler/optimizor/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package optimizor_test - -import ( - "fmt" - "os" - "reflect" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testsetup" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - server.RunInGoTest = true - testsetup.SetupForCommonTest() - topsqlstate.EnableTopSQL() - unistore.CheckResourceTagForTopSQLInGoTest = true - - // AsyncCommit will make DDL wait 2.5s before changing to the next state. - // Set schema lease to avoid it from making CI slow. - session.SetSchemaLease(0) - - tikv.EnableFailpoints() - - metrics.RegisterMetrics() - - // sanity check: the global config should not be changed by other pkg init function. - // see also https://github.com/pingcap/tidb/issues/22162 - defaultConfig := config.NewConfig() - globalConfig := config.GetGlobalConfig() - if !reflect.DeepEqual(defaultConfig, globalConfig) { - _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") - _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("time.Sleep"), - goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/server.NewServer.func1"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/server/handler/optimizor/plan_replayer.go b/server/handler/optimizor/plan_replayer.go deleted file mode 100644 index c643e0916f3c7..0000000000000 --- a/server/handler/optimizor/plan_replayer.go +++ /dev/null @@ -1,375 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package optimizor - -import ( - "archive/zip" - "bytes" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/BurntSushi/toml" - "github.com/gorilla/mux" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/replayer" - "go.uber.org/zap" -) - -// PlanReplayerHandler is the handler for dumping plan replayer file. -type PlanReplayerHandler struct { - is infoschema.InfoSchema - statsHandle *handle.Handle - infoGetter *infosync.InfoSyncer - address string - statusPort uint -} - -// NewPlanReplayerHandler creates a new PlanReplayerHandler. -func NewPlanReplayerHandler(is infoschema.InfoSchema, statsHandle *handle.Handle, infoGetter *infosync.InfoSyncer, address string, statusPort uint) *PlanReplayerHandler { - return &PlanReplayerHandler{ - is: is, - statsHandle: statsHandle, - infoGetter: infoGetter, - address: address, - statusPort: statusPort, - } -} - -// ServeHTTP handles request of dumping plan replayer file. -func (prh PlanReplayerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - params := mux.Vars(req) - name := params[handler.FileName] - handler := downloadFileHandler{ - filePath: filepath.Join(replayer.GetPlanReplayerDirName(), name), - fileName: name, - infoGetter: prh.infoGetter, - address: prh.address, - statusPort: prh.statusPort, - urlPath: fmt.Sprintf("plan_replayer/dump/%s", name), - downloadedFilename: "plan_replayer", - scheme: util.InternalHTTPSchema(), - statsHandle: prh.statsHandle, - is: prh.is, - } - handleDownloadFile(handler, w, req) -} - -func handleDownloadFile(dfHandler downloadFileHandler, w http.ResponseWriter, req *http.Request) { - params := mux.Vars(req) - name := params[handler.FileName] - path := dfHandler.filePath - isForwarded := len(req.URL.Query().Get("forward")) > 0 - localAddr := net.JoinHostPort(dfHandler.address, strconv.Itoa(int(dfHandler.statusPort))) - exist, err := isExists(path) - if err != nil { - handler.WriteError(w, err) - return - } - if exist { - //nolint: gosec - file, err := os.Open(path) - if err != nil { - handler.WriteError(w, err) - return - } - content, err := io.ReadAll(file) - if err != nil { - handler.WriteError(w, err) - return - } - err = file.Close() - if err != nil { - handler.WriteError(w, err) - return - } - if dfHandler.downloadedFilename == "plan_replayer" { - content, err = handlePlanReplayerCaptureFile(content, path, dfHandler) - if err != nil { - handler.WriteError(w, err) - return - } - } - _, err = w.Write(content) - if err != nil { - handler.WriteError(w, err) - return - } - w.Header().Set("Content-Type", "application/zip") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", dfHandler.downloadedFilename)) - logutil.BgLogger().Info("return dump file successfully", zap.String("filename", name), - zap.String("address", localAddr), zap.Bool("forwarded", isForwarded)) - return - } - // handler.infoGetter will be nil only in unit test - // or we couldn't find file for forward request, return 404 - if dfHandler.infoGetter == nil || isForwarded { - logutil.BgLogger().Info("failed to find dump file", zap.String("filename", name), - zap.String("address", localAddr), zap.Bool("forwarded", isForwarded)) - w.WriteHeader(http.StatusNotFound) - return - } - // If we didn't find file in origin request, try to broadcast the request to all remote tidb-servers - topos, err := dfHandler.infoGetter.GetAllTiDBTopology(req.Context()) - if err != nil { - handler.WriteError(w, err) - return - } - client := util.InternalHTTPClient() - // transfer each remote tidb-server and try to find dump file - for _, topo := range topos { - if topo.IP == dfHandler.address && topo.StatusPort == dfHandler.statusPort { - continue - } - remoteAddr := net.JoinHostPort(topo.IP, strconv.Itoa(int(topo.StatusPort))) - url := fmt.Sprintf("%s://%s/%s?forward=true", dfHandler.scheme, remoteAddr, dfHandler.urlPath) - resp, err := client.Get(url) - if err != nil { - logutil.BgLogger().Error("forward request failed", - zap.String("remote-addr", remoteAddr), zap.Error(err)) - continue - } - if resp.StatusCode != http.StatusOK { - logutil.BgLogger().Info("can't find file in remote server", zap.String("filename", name), - zap.String("remote-addr", remoteAddr), zap.Int("status-code", resp.StatusCode)) - continue - } - content, err := io.ReadAll(resp.Body) - if err != nil { - handler.WriteError(w, err) - return - } - err = resp.Body.Close() - if err != nil { - handler.WriteError(w, err) - return - } - _, err = w.Write(content) - if err != nil { - handler.WriteError(w, err) - return - } - // find dump file in one remote tidb-server, return file directly - w.Header().Set("Content-Type", "application/zip") - w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.zip\"", dfHandler.downloadedFilename)) - logutil.BgLogger().Info("return dump file successfully in remote server", - zap.String("filename", name), zap.String("remote-addr", remoteAddr)) - return - } - // we can't find dump file in any tidb-server, return 404 directly - logutil.BgLogger().Info("can't find dump file in any remote server", zap.String("filename", name)) - w.WriteHeader(http.StatusNotFound) - _, err = fmt.Fprintf(w, "can't find dump file %s in any remote server", name) - handler.WriteError(w, err) -} - -type downloadFileHandler struct { - scheme string - filePath string - fileName string - infoGetter *infosync.InfoSyncer - address string - statusPort uint - urlPath string - downloadedFilename string - - statsHandle *handle.Handle - is infoschema.InfoSchema -} - -func isExists(path string) (bool, error) { - _, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return false, nil - } - return false, err - } - return true, nil -} - -func handlePlanReplayerCaptureFile(content []byte, path string, handler downloadFileHandler) ([]byte, error) { - if !strings.HasPrefix(handler.filePath, "capture_replayer") { - return content, nil - } - b := bytes.NewReader(content) - zr, err := zip.NewReader(b, int64(len(content))) - if err != nil { - return nil, err - } - startTS, err := loadSQLMetaFile(zr) - if err != nil { - return nil, err - } - if startTS == 0 { - return content, nil - } - tbls, err := loadSchemaMeta(zr, handler.is) - if err != nil { - return nil, err - } - for _, tbl := range tbls { - jsonStats, _, err := handler.statsHandle.DumpHistoricalStatsBySnapshot(tbl.dbName, tbl.info, startTS) - if err != nil { - return nil, err - } - tbl.jsonStats = jsonStats - } - newPath, err := dumpJSONStatsIntoZip(tbls, content, path) - if err != nil { - return nil, err - } - //nolint: gosec - file, err := os.Open(newPath) - if err != nil { - return nil, err - } - content, err = io.ReadAll(file) - if err != nil { - return nil, err - } - err = file.Close() - if err != nil { - return nil, err - } - return content, nil -} - -func loadSQLMetaFile(z *zip.Reader) (uint64, error) { - for _, zipFile := range z.File { - if zipFile.Name == domain.PlanReplayerSQLMetaFile { - varMap := make(map[string]string) - v, err := zipFile.Open() - if err != nil { - return 0, errors.AddStack(err) - } - //nolint: errcheck,all_revive,revive - defer v.Close() - _, err = toml.NewDecoder(v).Decode(&varMap) - if err != nil { - return 0, errors.AddStack(err) - } - startTS, err := strconv.ParseUint(varMap[domain.PlanReplayerSQLMetaStartTS], 10, 64) - if err != nil { - return 0, err - } - return startTS, nil - } - } - return 0, nil -} - -func loadSchemaMeta(z *zip.Reader, is infoschema.InfoSchema) (map[int64]*tblInfo, error) { - r := make(map[int64]*tblInfo, 0) - for _, zipFile := range z.File { - if zipFile.Name == fmt.Sprintf("schema/%v", domain.PlanReplayerSchemaMetaFile) { - v, err := zipFile.Open() - if err != nil { - return nil, errors.AddStack(err) - } - //nolint: errcheck,all_revive,revive - defer v.Close() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(v) - if err != nil { - return nil, errors.AddStack(err) - } - rows := strings.Split(buf.String(), "\n") - for _, row := range rows { - s := strings.Split(row, ";") - databaseName := s[0] - tableName := s[1] - t, err := is.TableByName(model.NewCIStr(databaseName), model.NewCIStr(tableName)) - if err != nil { - return nil, err - } - r[t.Meta().ID] = &tblInfo{ - info: t.Meta(), - dbName: databaseName, - tblName: tableName, - } - } - break - } - } - return r, nil -} - -func dumpJSONStatsIntoZip(tbls map[int64]*tblInfo, content []byte, path string) (string, error) { - zr, err := zip.NewReader(bytes.NewReader(content), int64(len(content))) - if err != nil { - return "", err - } - newPath := strings.Replace(path, "capture_replayer", "copy_capture_replayer", 1) - zf, err := os.Create(newPath) - if err != nil { - return "", err - } - zw := zip.NewWriter(zf) - for _, f := range zr.File { - err = zw.Copy(f) - if err != nil { - logutil.BgLogger().Error("copy plan replayer zip file failed", zap.Error(err)) - return "", err - } - } - for _, tbl := range tbls { - w, err := zw.Create(fmt.Sprintf("stats/%v.%v.json", tbl.dbName, tbl.tblName)) - if err != nil { - return "", err - } - data, err := json.Marshal(tbl.jsonStats) - if err != nil { - return "", err - } - _, err = w.Write(data) - if err != nil { - return "", err - } - } - err = zw.Close() - if err != nil { - logutil.BgLogger().Error("Closing file failed", zap.Error(err)) - return "", err - } - err = zf.Close() - if err != nil { - logutil.BgLogger().Error("Closing file failed", zap.Error(err)) - return "", err - } - return newPath, nil -} - -type tblInfo struct { - info *model.TableInfo - jsonStats *storage.JSONTable - dbName string - tblName string -} diff --git a/server/handler/optimizor/plan_replayer_test.go b/server/handler/optimizor/plan_replayer_test.go deleted file mode 100644 index 52930cfa6c062..0000000000000 --- a/server/handler/optimizor/plan_replayer_test.go +++ /dev/null @@ -1,452 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package optimizor_test - -import ( - "archive/zip" - "bytes" - "database/sql" - "encoding/json" - "fmt" - "io" - "os" - "path/filepath" - "slices" - "strconv" - "strings" - "testing" - "time" - - "github.com/BurntSushi/toml" - "github.com/go-sql-driver/mysql" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" -) - -var expectedFilesInReplayer = []string{ - "config.toml", - "debug_trace/debug_trace0.json", - "explain.txt", - "global_bindings.sql", - "meta.txt", - "schema/planreplayer.t.schema.txt", - "schema/schema_meta.txt", - "session_bindings.sql", - "sql/sql0.sql", - "sql_meta.toml", - "stats/planreplayer.t.json", - "statsMem/planreplayer.t.txt", - "table_tiflash_replica.txt", - "variables.toml", -} - -var expectedFilesInReplayerForCapture = []string{ - "config.toml", - "debug_trace/debug_trace0.json", - "explain/sql.txt", - "global_bindings.sql", - "meta.txt", - "schema/planreplayer.t.schema.txt", - "schema/schema_meta.txt", - "session_bindings.sql", - "sql/sql0.sql", - "sql_meta.toml", - "stats/planreplayer.t.json", - "statsMem/planreplayer.t.txt", - "table_tiflash_replica.txt", - "variables.toml", -} - -func prepareServerAndClientForTest(t *testing.T, store kv.Storage, dom *domain.Domain) (srv *server.Server, client *testserverclient.TestServerClient) { - driver := server.NewTiDBDriver(store) - client = testserverclient.NewTestServerClient() - - cfg := util.NewTestConfig() - cfg.Port = client.Port - cfg.Status.StatusPort = client.StatusPort - cfg.Status.ReportStatus = true - - srv, err := server.NewServer(cfg, driver) - srv.SetDomain(dom) - require.NoError(t, err) - go func() { - err := srv.Run() - require.NoError(t, err) - }() - - client.Port = testutil.GetPortFromTCPAddr(srv.ListenAddr()) - client.StatusPort = testutil.GetPortFromTCPAddr(srv.StatusListenerAddr()) - client.WaitUntilServerOnline() - return -} - -func TestDumpPlanReplayerAPI(t *testing.T) { - store := testkit.CreateMockStore(t) - dom, err := session.GetDomain(store) - require.NoError(t, err) - // 1. setup and prepare plan replayer files by manual command and capture - server, client := prepareServerAndClientForTest(t, store, dom) - defer server.Close() - - filename, fileNameFromCapture := prepareData4PlanReplayer(t, client, dom) - - // 2. check the contents of the plan replayer zip files. - - var filesInReplayer []string - collectFileNameAndAssertFileSize := func(f *zip.File) { - // collect file name - filesInReplayer = append(filesInReplayer, f.Name) - // except for {global,session}_bindings.sql and table_tiflash_replica.txt, the file should not be empty - if !strings.Contains(f.Name, "table_tiflash_replica.txt") && - !strings.Contains(f.Name, "bindings.sql") { - require.NotZero(t, f.UncompressedSize64, f.Name) - } - } - - // 2-1. check the plan replayer file from manual command - resp0, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", filename)) - require.NoError(t, err) - defer func() { - require.NoError(t, resp0.Body.Close()) - }() - body, err := io.ReadAll(resp0.Body) - require.NoError(t, err) - forEachFileInZipBytes(t, body, collectFileNameAndAssertFileSize) - slices.Sort(filesInReplayer) - require.Equal(t, expectedFilesInReplayer, filesInReplayer) - - // 2-2. check the plan replayer file from capture - resp1, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", fileNameFromCapture)) - require.NoError(t, err) - defer func() { - require.NoError(t, resp1.Body.Close()) - }() - body, err = io.ReadAll(resp1.Body) - require.NoError(t, err) - filesInReplayer = filesInReplayer[:0] - forEachFileInZipBytes(t, body, collectFileNameAndAssertFileSize) - slices.Sort(filesInReplayer) - require.Equal(t, expectedFilesInReplayerForCapture, filesInReplayer) - - // 3. check plan replayer load - - // 3-1. write the plan replayer file from manual command to a file - path := "/tmp/plan_replayer.zip" - fp, err := os.Create(path) - require.NoError(t, err) - require.NotNil(t, fp) - defer func() { - require.NoError(t, fp.Close()) - require.NoError(t, os.Remove(path)) - }() - - _, err = io.Copy(fp, bytes.NewReader(body)) - require.NoError(t, err) - require.NoError(t, fp.Sync()) - - // 3-2. connect to tidb and use PLAN REPLAYER LOAD to load this file - db, err := sql.Open("mysql", client.GetDSN(func(config *mysql.Config) { - config.AllowAllFiles = true - })) - require.NoError(t, err, "Error connecting") - defer func() { - err := db.Close() - require.NoError(t, err) - }() - tk := testkit.NewDBTestKit(t, db) - - tk.MustExec("use planReplayer") - tk.MustExec("drop table planReplayer.t") - tk.MustExec(`plan replayer load "/tmp/plan_replayer.zip"`) - - // 3-3. assert that the count and modify count in the stats is as expected - rows := tk.MustQuery("show stats_meta") - require.True(t, rows.Next(), "unexpected data") - var dbName, tableName string - var modifyCount, count int64 - var other interface{} - err = rows.Scan(&dbName, &tableName, &other, &other, &modifyCount, &count) - require.NoError(t, err) - require.Equal(t, "planReplayer", dbName) - require.Equal(t, "t", tableName) - require.Equal(t, int64(4), modifyCount) - require.Equal(t, int64(8), count) -} - -// prepareData4PlanReplayer trigger tidb to dump 2 plan replayer files, -// one by manual command, the other by capture, and return the filenames. -func prepareData4PlanReplayer(t *testing.T, client *testserverclient.TestServerClient, dom *domain.Domain) (string, string) { - h := dom.StatsHandle() - replayerHandle := dom.GetPlanReplayerHandle() - db, err := sql.Open("mysql", client.GetDSN()) - require.NoError(t, err, "Error connecting") - defer func() { - err := db.Close() - require.NoError(t, err) - }() - tk := testkit.NewDBTestKit(t, db) - - tk.MustExec("create database planReplayer") - tk.MustExec("use planReplayer") - tk.MustExec("create table t(a int)") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - tk.MustExec("insert into t values(1), (2), (3), (4)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t") - tk.MustExec("insert into t values(5), (6), (7), (8)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - rows := tk.MustQuery("plan replayer dump explain select * from t") - require.True(t, rows.Next(), "unexpected data") - var filename string - require.NoError(t, rows.Scan(&filename)) - require.NoError(t, rows.Close()) - rows = tk.MustQuery("select @@tidb_last_plan_replayer_token") - require.True(t, rows.Next(), "unexpected data") - var filename2 string - require.NoError(t, rows.Scan(&filename2)) - require.NoError(t, rows.Close()) - require.Equal(t, filename, filename2) - - tk.MustExec("plan replayer capture 'e5796985ccafe2f71126ed6c0ac939ffa015a8c0744a24b7aee6d587103fd2f7' '*'") - tk.MustQuery("select * from t") - task := replayerHandle.DrainTask() - require.NotNil(t, task) - worker := replayerHandle.GetWorker() - require.True(t, worker.HandleTask(task)) - rows = tk.MustQuery("select token from mysql.plan_replayer_status where length(sql_digest) > 0") - require.True(t, rows.Next(), "unexpected data") - var filename3 string - require.NoError(t, rows.Scan(&filename3)) - require.NoError(t, rows.Close()) - - return filename, filename3 -} - -func forEachFileInZipBytes(t *testing.T, b []byte, fn func(file *zip.File)) { - br := bytes.NewReader(b) - z, err := zip.NewReader(br, int64(len(b))) - require.NoError(t, err) - for _, f := range z.File { - fn(f) - } -} - -func fetchZipFromPlanReplayerAPI(t *testing.T, client *testserverclient.TestServerClient, filename string) *zip.Reader { - resp0, err := client.FetchStatus(filepath.Join("/plan_replayer/dump/", filename)) - require.NoError(t, err) - defer func() { - require.NoError(t, resp0.Body.Close()) - }() - body, err := io.ReadAll(resp0.Body) - require.NoError(t, err) - b := bytes.NewReader(body) - z, err := zip.NewReader(b, int64(len(body))) - require.NoError(t, err) - return z -} - -func getInfoFromPlanReplayerZip( - t *testing.T, - z *zip.Reader, -) ( - jsonTbls []*storage.JSONTable, - metas []map[string]string, - errMsgs []string, -) { - for _, zipFile := range z.File { - if strings.HasPrefix(zipFile.Name, "stats/") { - jsonTbl := &storage.JSONTable{} - r, err := zipFile.Open() - require.NoError(t, err) - //nolint: all_revive - defer func() { - require.NoError(t, r.Close()) - }() - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(r) - require.NoError(t, err) - err = json.Unmarshal(buf.Bytes(), jsonTbl) - require.NoError(t, err) - - jsonTbls = append(jsonTbls, jsonTbl) - } else if zipFile.Name == "sql_meta.toml" { - meta := make(map[string]string) - r, err := zipFile.Open() - require.NoError(t, err) - //nolint: all_revive - defer func() { - require.NoError(t, r.Close()) - }() - _, err = toml.NewDecoder(r).Decode(&meta) - require.NoError(t, err) - - metas = append(metas, meta) - } else if zipFile.Name == "errors.txt" { - r, err := zipFile.Open() - require.NoError(t, err) - //nolint: all_revive - defer func() { - require.NoError(t, r.Close()) - }() - content, err := io.ReadAll(r) - require.NoError(t, err) - errMsgs = strings.Split(string(content), "\n") - } - } - return -} - -func TestDumpPlanReplayerAPIWithHistoryStats(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/sendHistoricalStats", "return(true)")) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/sendHistoricalStats")) - }() - store := testkit.CreateMockStore(t) - dom, err := session.GetDomain(store) - require.NoError(t, err) - server, client := prepareServerAndClientForTest(t, store, dom) - defer server.Close() - statsHandle := dom.StatsHandle() - hsWorker := dom.GetHistoricalStatsWorker() - - // 1. prepare test data - - // time1, ts1: before everything starts - tk := testkit.NewTestKit(t, store) - time1 := time.Now() - ts1 := oracle.GoTimeToTS(time1) - - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int, index ia(a))") - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - - // 1-1. first insert and first analyze, trigger first dump history stats - tk.MustExec("insert into t value(1,1,1), (2,2,2), (3,3,3)") - tk.MustExec("analyze table t with 1 samplerate") - tblID := hsWorker.GetOneHistoricalStatsTable() - err = hsWorker.DumpHistoricalStats(tblID, statsHandle) - require.NoError(t, err) - - // time2, stats1: after first analyze - time2 := time.Now() - ts2 := oracle.GoTimeToTS(time2) - stats1, err := statsHandle.DumpStatsToJSON("test", tblInfo, nil, true) - require.NoError(t, err) - - // 1-2. second insert and second analyze, trigger second dump history stats - tk.MustExec("insert into t value(4,4,4), (5,5,5), (6,6,6)") - tk.MustExec("analyze table t with 1 samplerate") - tblID = hsWorker.GetOneHistoricalStatsTable() - err = hsWorker.DumpHistoricalStats(tblID, statsHandle) - require.NoError(t, err) - - // time3, stats2: after second analyze - time3 := time.Now() - ts3 := oracle.GoTimeToTS(time3) - stats2, err := statsHandle.DumpStatsToJSON("test", tblInfo, nil, true) - require.NoError(t, err) - - // 2. get the plan replayer and assert - - template := "plan replayer dump with stats as of timestamp '%s' explain %s" - query := "select * from t where a > 1" - - // 2-1. specify time1 to get the plan replayer - filename1 := tk.MustQuery( - fmt.Sprintf(template, strconv.FormatUint(ts1, 10), query), - ).Rows()[0][0].(string) - zip1 := fetchZipFromPlanReplayerAPI(t, client, filename1) - jsonTbls1, metas1, errMsg1 := getInfoFromPlanReplayerZip(t, zip1) - - // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above - require.Len(t, metas1, 1) - require.Contains(t, metas1[0], "historicalStatsTS") - tsInReplayerMeta1, err := strconv.ParseUint(metas1[0]["historicalStatsTS"], 10, 64) - require.NoError(t, err) - require.Equal(t, ts1, tsInReplayerMeta1) - - // the result is the same as stats2, and IsHistoricalStats is false. - require.Len(t, jsonTbls1, 1) - require.False(t, jsonTbls1[0].IsHistoricalStats) - require.Equal(t, jsonTbls1[0], stats2) - - // because we failed to get historical stats, there's an error message. - require.Equal(t, []string{"Historical stats for test.t are unavailable, fallback to latest stats", ""}, errMsg1) - - // 2-2. specify time2 to get the plan replayer - filename2 := tk.MustQuery( - fmt.Sprintf(template, time2.Format("2006-01-02 15:04:05.000000"), query), - ).Rows()[0][0].(string) - zip2 := fetchZipFromPlanReplayerAPI(t, client, filename2) - jsonTbls2, metas2, errMsg2 := getInfoFromPlanReplayerZip(t, zip2) - - // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above - require.Len(t, metas2, 1) - require.Contains(t, metas2[0], "historicalStatsTS") - tsInReplayerMeta2, err := strconv.ParseUint(metas2[0]["historicalStatsTS"], 10, 64) - require.NoError(t, err) - require.Equal(t, ts2, tsInReplayerMeta2) - - // the result is the same as stats1, and IsHistoricalStats is true. - require.Len(t, jsonTbls2, 1) - require.True(t, jsonTbls2[0].IsHistoricalStats) - jsonTbls2[0].IsHistoricalStats = false - require.Equal(t, jsonTbls2[0], stats1) - - // succeeded to get historical stats, there should be no error message. - require.Empty(t, errMsg2) - - // 2-3. specify time3 to get the plan replayer - filename3 := tk.MustQuery( - fmt.Sprintf(template, time3.Format("2006-01-02T15:04:05.000000Z07:00"), query), - ).Rows()[0][0].(string) - zip3 := fetchZipFromPlanReplayerAPI(t, client, filename3) - jsonTbls3, metas3, errMsg3 := getInfoFromPlanReplayerZip(t, zip3) - - // the TS is recorded in the plan replayer, and it's the same as the TS we calculated above - require.Len(t, metas3, 1) - require.Contains(t, metas3[0], "historicalStatsTS") - tsInReplayerMeta3, err := strconv.ParseUint(metas3[0]["historicalStatsTS"], 10, 64) - require.NoError(t, err) - require.Equal(t, ts3, tsInReplayerMeta3) - - // the result is the same as stats2, and IsHistoricalStats is true. - require.Len(t, jsonTbls3, 1) - require.True(t, jsonTbls3[0].IsHistoricalStats) - jsonTbls3[0].IsHistoricalStats = false - require.Equal(t, jsonTbls3[0], stats2) - - // succeeded to get historical stats, there should be no error message. - require.Empty(t, errMsg3) - - // 3. remove the plan replayer files generated during the test - gcHandler := dom.GetDumpFileGCChecker() - gcHandler.GCDumpFiles(0, 0) -} diff --git a/server/handler/tests/BUILD.bazel b/server/handler/tests/BUILD.bazel deleted file mode 100644 index 2f1eef4ff97d8..0000000000000 --- a/server/handler/tests/BUILD.bazel +++ /dev/null @@ -1,60 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tests_test", - timeout = "short", - srcs = [ - "http_handler_serial_test.go", - "http_handler_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 36, - deps = [ - "//config", - "//ddl", - "//ddl/util", - "//domain", - "//domain/infosync", - "//infoschema", - "//kv", - "//meta", - "//metrics", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//server", - "//server/handler", - "//server/handler/optimizor", - "//server/handler/tikvhandler", - "//server/internal/testserverclient", - "//server/internal/testutil", - "//server/internal/util", - "//session", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//store/helper", - "//store/mockstore", - "//store/mockstore/unistore", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/deadlockhistory", - "//util/rowcodec", - "//util/topsql/state", - "//util/versioninfo", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/server/handler/tests/main_test.go b/server/handler/tests/main_test.go deleted file mode 100644 index 57fa214b4df97..0000000000000 --- a/server/handler/tests/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "fmt" - "os" - "reflect" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testsetup" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - server.RunInGoTest = true - testsetup.SetupForCommonTest() - topsqlstate.EnableTopSQL() - unistore.CheckResourceTagForTopSQLInGoTest = true - - // AsyncCommit will make DDL wait 2.5s before changing to the next state. - // Set schema lease to avoid it from making CI slow. - session.SetSchemaLease(0) - - tikv.EnableFailpoints() - - metrics.RegisterMetrics() - - // sanity check: the global config should not be changed by other pkg init function. - // see also https://github.com/pingcap/tidb/issues/22162 - defaultConfig := config.NewConfig() - globalConfig := config.GetGlobalConfig() - if !reflect.DeepEqual(defaultConfig, globalConfig) { - _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") - _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("time.Sleep"), - goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/server.NewServer.func1"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/server/handler/tikv_handler.go b/server/handler/tikv_handler.go deleted file mode 100644 index 0839c901d0a9d..0000000000000 --- a/server/handler/tikv_handler.go +++ /dev/null @@ -1,278 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package handler - -import ( - "context" - "encoding/hex" - "fmt" - "net/url" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/stmtctx" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/tikv/client-go/v2/tikv" -) - -// TikvHandlerTool is a tool to handle TiKV data. -type TikvHandlerTool struct { - helper.Helper -} - -// NewTikvHandlerTool creates a new TikvHandlerTool. -func NewTikvHandlerTool(store helper.Storage) *TikvHandlerTool { - return &TikvHandlerTool{Helper: *helper.NewHelper(store)} -} - -type mvccKV struct { - Key string `json:"key"` - RegionID uint64 `json:"region_id"` - Value *kvrpcpb.MvccGetByKeyResponse `json:"value"` -} - -// GetRegionIDByKey gets the region id by the key. -func (t *TikvHandlerTool) GetRegionIDByKey(encodedKey []byte) (uint64, error) { - keyLocation, err := t.RegionCache.LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) - if err != nil { - return 0, derr.ToTiDBErr(err) - } - return keyLocation.Region.GetID(), nil -} - -// GetHandle gets the handle of the record. -func (t *TikvHandlerTool) GetHandle(tb table.PhysicalTable, params map[string]string, values url.Values) (kv.Handle, error) { - var handle kv.Handle - if intHandleStr, ok := params[Handle]; ok { - if tb.Meta().IsCommonHandle { - return nil, errors.BadRequestf("For clustered index tables, please use query strings to specify the column values.") - } - intHandle, err := strconv.ParseInt(intHandleStr, 0, 64) - if err != nil { - return nil, errors.Trace(err) - } - handle = kv.IntHandle(intHandle) - } else { - tblInfo := tb.Meta() - pkIdx := tables.FindPrimaryIndex(tblInfo) - if pkIdx == nil || !tblInfo.IsCommonHandle { - return nil, errors.BadRequestf("Clustered common handle not found.") - } - cols := tblInfo.Cols() - pkCols := make([]*model.ColumnInfo, 0, len(pkIdx.Columns)) - for _, idxCol := range pkIdx.Columns { - pkCols = append(pkCols, cols[idxCol.Offset]) - } - sc := stmtctx.NewStmtCtx() - sc.SetTimeZone(time.UTC) - pkDts, err := t.formValue2DatumRow(sc, values, pkCols) - if err != nil { - return nil, errors.Trace(err) - } - tablecodec.TruncateIndexValues(tblInfo, pkIdx, pkDts) - var handleBytes []byte - handleBytes, err = codec.EncodeKey(sc, nil, pkDts...) - if err != nil { - return nil, errors.Trace(err) - } - handle, err = kv.NewCommonHandle(handleBytes) - if err != nil { - return nil, errors.Trace(err) - } - } - return handle, nil -} - -// GetMvccByIdxValue gets the mvcc by the index value. -func (t *TikvHandlerTool) GetMvccByIdxValue(idx table.Index, values url.Values, idxCols []*model.ColumnInfo, handle kv.Handle) ([]*helper.MvccKV, error) { - // HTTP request is not a database session, set timezone to UTC directly here. - // See https://github.com/pingcap/tidb/blob/master/docs/tidb_http_api.md for more details. - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - idxRow, err := t.formValue2DatumRow(sc, values, idxCols) - if err != nil { - return nil, errors.Trace(err) - } - encodedKey, _, err := idx.GenIndexKey(sc, idxRow, handle, nil) - if err != nil { - return nil, errors.Trace(err) - } - data, err := t.GetMvccByEncodedKey(encodedKey) - if err != nil { - return nil, err - } - regionID, err := t.GetRegionIDByKey(encodedKey) - if err != nil { - return nil, err - } - idxData := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), RegionID: regionID, Value: data} - tablecodec.IndexKey2TempIndexKey(encodedKey) - data, err = t.GetMvccByEncodedKey(encodedKey) - if err != nil { - return nil, err - } - regionID, err = t.GetRegionIDByKey(encodedKey) - if err != nil { - return nil, err - } - tempIdxData := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), RegionID: regionID, Value: data} - return append([]*helper.MvccKV{}, idxData, tempIdxData), err -} - -// formValue2DatumRow converts URL query string to a Datum Row. -func (*TikvHandlerTool) formValue2DatumRow(sc *stmtctx.StatementContext, values url.Values, idxCols []*model.ColumnInfo) ([]types.Datum, error) { - data := make([]types.Datum, len(idxCols)) - for i, col := range idxCols { - colName := col.Name.String() - vals, ok := values[colName] - if !ok { - return nil, errors.BadRequestf("Missing value for index column %s.", colName) - } - - switch len(vals) { - case 0: - data[i].SetNull() - case 1: - bDatum := types.NewStringDatum(vals[0]) - cDatum, err := bDatum.ConvertTo(sc, &col.FieldType) - if err != nil { - return nil, errors.Trace(err) - } - data[i] = cDatum - default: - return nil, errors.BadRequestf("Invalid query form for column '%s', it's values are %v."+ - " Column value should be unique for one index record.", colName, vals) - } - } - return data, nil -} - -// GetTableID gets the table ID by the database name and table name. -func (t *TikvHandlerTool) GetTableID(dbName, tableName string) (int64, error) { - tbl, err := t.GetTable(dbName, tableName) - if err != nil { - return 0, errors.Trace(err) - } - return tbl.GetPhysicalID(), nil -} - -// GetTable gets the table by the database name and table name. -func (t *TikvHandlerTool) GetTable(dbName, tableName string) (table.PhysicalTable, error) { - schema, err := t.Schema() - if err != nil { - return nil, errors.Trace(err) - } - tableName, partitionName := ExtractTableAndPartitionName(tableName) - tableVal, err := schema.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName)) - if err != nil { - return nil, errors.Trace(err) - } - return t.GetPartition(tableVal, partitionName) -} - -// GetPartition gets the partition by the table and partition name. -func (*TikvHandlerTool) GetPartition(tableVal table.Table, partitionName string) (table.PhysicalTable, error) { - if pt, ok := tableVal.(table.PartitionedTable); ok { - if partitionName == "" { - return tableVal.(table.PhysicalTable), errors.New("work on partitioned table, please specify the table name like this: table(partition)") - } - tblInfo := pt.Meta() - pid, err := tables.FindPartitionByName(tblInfo, partitionName) - if err != nil { - return nil, errors.Trace(err) - } - return pt.GetPartition(pid), nil - } - if partitionName != "" { - return nil, fmt.Errorf("%s is not a partitionted table", tableVal.Meta().Name) - } - return tableVal.(table.PhysicalTable), nil -} - -// Schema gets the schema. -func (t *TikvHandlerTool) Schema() (infoschema.InfoSchema, error) { - dom, err := session.GetDomain(t.Store) - if err != nil { - return nil, err - } - return dom.InfoSchema(), nil -} - -// HandleMvccGetByHex handles the request of getting mvcc by hex encoded key. -func (t *TikvHandlerTool) HandleMvccGetByHex(params map[string]string) (*mvccKV, error) { - encodedKey, err := hex.DecodeString(params[HexKey]) - if err != nil { - return nil, errors.Trace(err) - } - data, err := t.GetMvccByEncodedKey(encodedKey) - if err != nil { - return nil, errors.Trace(err) - } - regionID, err := t.GetRegionIDByKey(encodedKey) - if err != nil { - return nil, err - } - return &mvccKV{Key: strings.ToUpper(params[HexKey]), Value: data, RegionID: regionID}, nil -} - -// RegionMeta contains a region's peer detail -type RegionMeta struct { - ID uint64 `json:"region_id"` - Leader *metapb.Peer `json:"leader"` - Peers []*metapb.Peer `json:"peers"` - RegionEpoch *metapb.RegionEpoch `json:"region_epoch"` -} - -// GetRegionsMeta gets regions meta by regionIDs -func (t *TikvHandlerTool) GetRegionsMeta(regionIDs []uint64) ([]RegionMeta, error) { - regions := make([]RegionMeta, len(regionIDs)) - for i, regionID := range regionIDs { - region, err := t.RegionCache.PDClient().GetRegionByID(context.TODO(), regionID) - if err != nil { - return nil, errors.Trace(err) - } - - failpoint.Inject("errGetRegionByIDEmpty", func(val failpoint.Value) { - if val.(bool) { - region.Meta = nil - } - }) - - if region.Meta == nil { - return nil, errors.Errorf("region not found for regionID %q", regionID) - } - regions[i] = RegionMeta{ - ID: regionID, - Leader: region.Leader, - Peers: region.Meta.Peers, - RegionEpoch: region.Meta.RegionEpoch, - } - } - return regions, nil -} diff --git a/server/handler/tikvhandler/BUILD.bazel b/server/handler/tikvhandler/BUILD.bazel deleted file mode 100644 index a77d44f07899f..0000000000000 --- a/server/handler/tikvhandler/BUILD.bazel +++ /dev/null @@ -1,44 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tikvhandler", - srcs = ["tikv_handler.go"], - importpath = "github.com/pingcap/tidb/server/handler/tikvhandler", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl", - "//domain", - "//domain/infosync", - "//executor", - "//infoschema", - "//kv", - "//meta", - "//parser/model", - "//parser/terror", - "//server/handler", - "//session", - "//session/txninfo", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/variable", - "//store/gcworker", - "//store/helper", - "//table", - "//tablecodec", - "//types", - "//util", - "//util/codec", - "//util/deadlockhistory", - "//util/gcutil", - "//util/hack", - "//util/logutil", - "//util/pdapi", - "//util/sqlexec", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) diff --git a/server/handler/tikvhandler/tikv_handler.go b/server/handler/tikvhandler/tikv_handler.go deleted file mode 100644 index 4ac4dc4a68a68..0000000000000 --- a/server/handler/tikvhandler/tikv_handler.go +++ /dev/null @@ -1,2023 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package tikvhandler - -import ( - "bytes" - "context" - "encoding/base64" - "encoding/hex" - "encoding/json" - "fmt" - "math" - "net/http" - "net/url" - "runtime" - "strconv" - "strings" - "sync/atomic" - "time" - - "github.com/gorilla/mux" - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/deadlockhistory" - "github.com/pingcap/tidb/util/gcutil" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/pdapi" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" -) - -func writeError(w http.ResponseWriter, err error) { - w.WriteHeader(http.StatusBadRequest) - _, err = w.Write([]byte(err.Error())) - terror.Log(errors.Trace(err)) -} - -func writeData(w http.ResponseWriter, data interface{}) { - js, err := json.MarshalIndent(data, "", " ") - if err != nil { - writeError(w, err) - return - } - // write response - w.Header().Set(handler.HeaderContentType, handler.ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, err = w.Write(js) - terror.Log(errors.Trace(err)) -} - -// SettingsHandler is the handler for list tidb server settings. -type SettingsHandler struct { - *handler.TikvHandlerTool -} - -// NewSettingsHandler creates a new SettingsHandler. -func NewSettingsHandler(tool *handler.TikvHandlerTool) *SettingsHandler { - return &SettingsHandler{tool} -} - -// BinlogRecover is used to recover binlog service. -// When config binlog IgnoreError, binlog service will stop after meeting the first error. -// It can be recovered using HTTP API. -type BinlogRecover struct{} - -// SchemaHandler is the handler for list database or table schemas. -type SchemaHandler struct { - *handler.TikvHandlerTool -} - -// NewSchemaHandler creates a new SchemaHandler. -func NewSchemaHandler(tool *handler.TikvHandlerTool) *SchemaHandler { - return &SchemaHandler{tool} -} - -// SchemaStorageHandler is the handler for list database or table schemas. -type SchemaStorageHandler struct { - *handler.TikvHandlerTool -} - -// NewSchemaStorageHandler creates a new SchemaStorageHandler. -func NewSchemaStorageHandler(tool *handler.TikvHandlerTool) *SchemaStorageHandler { - return &SchemaStorageHandler{tool} -} - -// DBTableHandler is the handler for list table's regions. -type DBTableHandler struct { - *handler.TikvHandlerTool -} - -// NewDBTableHandler creates a new DBTableHandler. -func NewDBTableHandler(tool *handler.TikvHandlerTool) *DBTableHandler { - return &DBTableHandler{tool} -} - -// FlashReplicaHandler is the handler for flash replica. -type FlashReplicaHandler struct { - *handler.TikvHandlerTool -} - -// NewFlashReplicaHandler creates a new FlashReplicaHandler. -func NewFlashReplicaHandler(tool *handler.TikvHandlerTool) *FlashReplicaHandler { - return &FlashReplicaHandler{tool} -} - -// RegionHandler is the common field for http handler. It contains -// some common functions for all handlers. -type RegionHandler struct { - *handler.TikvHandlerTool -} - -// NewRegionHandler creates a new RegionHandler. -func NewRegionHandler(tool *handler.TikvHandlerTool) *RegionHandler { - return &RegionHandler{tool} -} - -// TableHandler is the handler for list table's regions. -type TableHandler struct { - *handler.TikvHandlerTool - op string -} - -// NewTableHandler creates a new TableHandler. -func NewTableHandler(tool *handler.TikvHandlerTool, op string) *TableHandler { - return &TableHandler{tool, op} -} - -// DDLHistoryJobHandler is the handler for list job history. -type DDLHistoryJobHandler struct { - *handler.TikvHandlerTool -} - -// NewDDLHistoryJobHandler creates a new DDLHistoryJobHandler. -func NewDDLHistoryJobHandler(tool *handler.TikvHandlerTool) *DDLHistoryJobHandler { - return &DDLHistoryJobHandler{tool} -} - -// DDLResignOwnerHandler is the handler for resigning ddl owner. -type DDLResignOwnerHandler struct { - store kv.Storage -} - -// NewDDLResignOwnerHandler creates a new DDLResignOwnerHandler. -func NewDDLResignOwnerHandler(store kv.Storage) *DDLResignOwnerHandler { - return &DDLResignOwnerHandler{store} -} - -// ServerInfoHandler is the handler for getting statistics. -type ServerInfoHandler struct { - *handler.TikvHandlerTool -} - -// NewServerInfoHandler creates a new ServerInfoHandler. -func NewServerInfoHandler(tool *handler.TikvHandlerTool) *ServerInfoHandler { - return &ServerInfoHandler{tool} -} - -// AllServerInfoHandler is the handler for getting all servers information. -type AllServerInfoHandler struct { - *handler.TikvHandlerTool -} - -// NewAllServerInfoHandler creates a new AllServerInfoHandler. -func NewAllServerInfoHandler(tool *handler.TikvHandlerTool) *AllServerInfoHandler { - return &AllServerInfoHandler{tool} -} - -// ProfileHandler is the handler for getting profile. -type ProfileHandler struct { - *handler.TikvHandlerTool -} - -// NewProfileHandler creates a new ProfileHandler. -func NewProfileHandler(tool *handler.TikvHandlerTool) *ProfileHandler { - return &ProfileHandler{tool} -} - -// DDLHookHandler is the handler for use pre-defined ddl callback. -// It's convenient to provide some APIs for integration tests. -type DDLHookHandler struct { - store kv.Storage -} - -// NewDDLHookHandler creates a new DDLHookHandler. -func NewDDLHookHandler(store kv.Storage) *DDLHookHandler { - return &DDLHookHandler{store} -} - -// ValueHandler is the handler for get value. -type ValueHandler struct { -} - -// LabelHandler is the handler for set labels -type LabelHandler struct{} - -const ( - // OpTableRegions is the operation for getting regions of a table. - OpTableRegions = "regions" - // OpTableRanges is the operation for getting ranges of a table. - OpTableRanges = "ranges" - // OpTableDiskUsage is the operation for getting disk usage of a table. - OpTableDiskUsage = "disk-usage" - // OpTableScatter is the operation for scattering a table. - OpTableScatter = "scatter-table" - // OpStopTableScatter is the operation for stopping scattering a table. - OpStopTableScatter = "stop-scatter-table" -) - -// MvccTxnHandler is the handler for txn debugger. -type MvccTxnHandler struct { - *handler.TikvHandlerTool - op string -} - -// NewMvccTxnHandler creates a new MvccTxnHandler. -func NewMvccTxnHandler(tool *handler.TikvHandlerTool, op string) *MvccTxnHandler { - return &MvccTxnHandler{tool, op} -} - -const ( - // OpMvccGetByHex is the operation for getting mvcc value by hex format. - OpMvccGetByHex = "hex" - // OpMvccGetByKey is the operation for getting mvcc value by key. - OpMvccGetByKey = "key" - // OpMvccGetByIdx is the operation for getting mvcc value by idx. - OpMvccGetByIdx = "idx" - // OpMvccGetByTxn is the operation for getting mvcc value by txn. - OpMvccGetByTxn = "txn" -) - -// ServeHTTP handles request of list a database or table's schemas. -func (ValueHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // parse params - params := mux.Vars(req) - - colID, err := strconv.ParseInt(params[handler.ColumnID], 0, 64) - if err != nil { - writeError(w, err) - return - } - colTp, err := strconv.ParseInt(params[handler.ColumnTp], 0, 64) - if err != nil { - writeError(w, err) - return - } - colFlag, err := strconv.ParseUint(params[handler.ColumnFlag], 0, 64) - if err != nil { - writeError(w, err) - return - } - colLen, err := strconv.ParseInt(params[handler.ColumnLen], 0, 64) - if err != nil { - writeError(w, err) - return - } - - // Get the unchanged binary. - if req.URL == nil { - err = errors.BadRequestf("Invalid URL") - writeError(w, err) - return - } - values := make(url.Values) - - err = parseQuery(req.URL.RawQuery, values, false) - if err != nil { - writeError(w, err) - return - } - if len(values[handler.RowBin]) != 1 { - err = errors.BadRequestf("Invalid Query:%v", values[handler.RowBin]) - writeError(w, err) - return - } - bin := values[handler.RowBin][0] - valData, err := base64.StdEncoding.DecodeString(bin) - if err != nil { - writeError(w, err) - return - } - // Construct field type. - defaultDecimal := 6 - ft := types.NewFieldTypeBuilder().SetType(byte(colTp)).SetFlag(uint(colFlag)).SetFlen(int(colLen)).SetDecimal(defaultDecimal).BuildP() - // Decode a column. - m := make(map[int64]*types.FieldType, 1) - m[colID] = ft - loc := time.UTC - vals, err := tablecodec.DecodeRowToDatumMap(valData, m, loc) - if err != nil { - writeError(w, err) - return - } - - v := vals[colID] - val, err := v.ToString() - if err != nil { - writeError(w, err) - return - } - writeData(w, val) -} - -// TableRegions is the response data for list table's regions. -// It contains regions list for record and indices. -type TableRegions struct { - TableName string `json:"name"` - TableID int64 `json:"id"` - RecordRegions []handler.RegionMeta `json:"record_regions"` - Indices []IndexRegions `json:"indices"` -} - -// RangeDetail contains detail information about a particular range -type RangeDetail struct { - StartKey []byte `json:"start_key"` - EndKey []byte `json:"end_key"` - StartKeyHex string `json:"start_key_hex"` - EndKeyHex string `json:"end_key_hex"` -} - -func createRangeDetail(start, end []byte) RangeDetail { - return RangeDetail{ - StartKey: start, - EndKey: end, - StartKeyHex: hex.EncodeToString(start), - EndKeyHex: hex.EncodeToString(end), - } -} - -// TableRanges is the response data for list table's ranges. -// It contains ranges list for record and indices as well as the whole table. -type TableRanges struct { - TableName string `json:"name"` - TableID int64 `json:"id"` - Range RangeDetail `json:"table"` - Record RangeDetail `json:"record"` - Index RangeDetail `json:"index"` - Indices map[string]RangeDetail `json:"indices,omitempty"` -} - -// IndexRegions is the region info for one index. -type IndexRegions struct { - Name string `json:"name"` - ID int64 `json:"id"` - Regions []handler.RegionMeta `json:"regions"` -} - -// RegionDetail is the response data for get region by ID -// it includes indices and records detail in current region. -type RegionDetail struct { - RangeDetail `json:",inline"` - RegionID uint64 `json:"region_id"` - Frames []*helper.FrameItem `json:"frames"` -} - -// addTableInRange insert a table into RegionDetail -// with index's id or record in the range if r. -func (rt *RegionDetail) addTableInRange(dbName string, curTable *model.TableInfo, r *helper.RegionFrameRange) { - tName := curTable.Name.String() - tID := curTable.ID - pi := curTable.GetPartitionInfo() - isCommonHandle := curTable.IsCommonHandle - for _, index := range curTable.Indices { - if index.Primary && isCommonHandle { - continue - } - if pi != nil { - for _, def := range pi.Definitions { - if f := r.GetIndexFrame(def.ID, index.ID, dbName, fmt.Sprintf("%s(%s)", tName, def.Name.O), index.Name.String()); f != nil { - rt.Frames = append(rt.Frames, f) - } - } - } else if f := r.GetIndexFrame(tID, index.ID, dbName, tName, index.Name.String()); f != nil { - rt.Frames = append(rt.Frames, f) - } - } - - if pi != nil { - for _, def := range pi.Definitions { - if f := r.GetRecordFrame(def.ID, dbName, fmt.Sprintf("%s(%s)", tName, def.Name.O), isCommonHandle); f != nil { - rt.Frames = append(rt.Frames, f) - } - } - } else if f := r.GetRecordFrame(tID, dbName, tName, isCommonHandle); f != nil { - rt.Frames = append(rt.Frames, f) - } -} - -// FrameItem includes a index's or record's meta data with table's info. -type FrameItem struct { - DBName string `json:"db_name"` - TableName string `json:"table_name"` - TableID int64 `json:"table_id"` - IsRecord bool `json:"is_record"` - RecordID int64 `json:"record_id,omitempty"` - IndexName string `json:"index_name,omitempty"` - IndexID int64 `json:"index_id,omitempty"` - IndexValues []string `json:"index_values,omitempty"` -} - -// ServeHTTP handles request of list tidb server settings. -func (h SettingsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method == "POST" { - err := req.ParseForm() - if err != nil { - writeError(w, err) - return - } - if levelStr := req.Form.Get("log_level"); levelStr != "" { - err1 := logutil.SetLevel(levelStr) - if err1 != nil { - writeError(w, err1) - return - } - - config.GetGlobalConfig().Log.Level = levelStr - } - if generalLog := req.Form.Get("tidb_general_log"); generalLog != "" { - switch generalLog { - case "0": - variable.ProcessGeneralLog.Store(false) - case "1": - variable.ProcessGeneralLog.Store(true) - default: - writeError(w, errors.New("illegal argument")) - return - } - } - if asyncCommit := req.Form.Get("tidb_enable_async_commit"); asyncCommit != "" { - s, err := session.CreateSession(h.Store) - if err != nil { - writeError(w, err) - return - } - defer s.Close() - - switch asyncCommit { - case "0": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableAsyncCommit, variable.Off) - case "1": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableAsyncCommit, variable.On) - default: - writeError(w, errors.New("illegal argument")) - return - } - if err != nil { - writeError(w, err) - return - } - } - if onePC := req.Form.Get("tidb_enable_1pc"); onePC != "" { - s, err := session.CreateSession(h.Store) - if err != nil { - writeError(w, err) - return - } - defer s.Close() - - switch onePC { - case "0": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnable1PC, variable.Off) - case "1": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnable1PC, variable.On) - default: - writeError(w, errors.New("illegal argument")) - return - } - if err != nil { - writeError(w, err) - return - } - } - if ddlSlowThreshold := req.Form.Get("ddl_slow_threshold"); ddlSlowThreshold != "" { - threshold, err1 := strconv.Atoi(ddlSlowThreshold) - if err1 != nil { - writeError(w, err1) - return - } - if threshold > 0 { - atomic.StoreUint32(&variable.DDLSlowOprThreshold, uint32(threshold)) - } - } - if checkMb4ValueInUtf8 := req.Form.Get("check_mb4_value_in_utf8"); checkMb4ValueInUtf8 != "" { - switch checkMb4ValueInUtf8 { - case "0": - config.GetGlobalConfig().Instance.CheckMb4ValueInUTF8.Store(false) - case "1": - config.GetGlobalConfig().Instance.CheckMb4ValueInUTF8.Store(true) - default: - writeError(w, errors.New("illegal argument")) - return - } - } - if deadlockHistoryCapacity := req.Form.Get("deadlock_history_capacity"); deadlockHistoryCapacity != "" { - capacity, err := strconv.Atoi(deadlockHistoryCapacity) - if err != nil { - writeError(w, errors.New("illegal argument")) - return - } else if capacity < 0 || capacity > 10000 { - writeError(w, errors.New("deadlock_history_capacity out of range, should be in 0 to 10000")) - return - } - cfg := config.GetGlobalConfig() - cfg.PessimisticTxn.DeadlockHistoryCapacity = uint(capacity) - config.StoreGlobalConfig(cfg) - deadlockhistory.GlobalDeadlockHistory.Resize(uint(capacity)) - } - if deadlockCollectRetryable := req.Form.Get("deadlock_history_collect_retryable"); deadlockCollectRetryable != "" { - collectRetryable, err := strconv.ParseBool(deadlockCollectRetryable) - if err != nil { - writeError(w, errors.New("illegal argument")) - return - } - cfg := config.GetGlobalConfig() - cfg.PessimisticTxn.DeadlockHistoryCollectRetryable = collectRetryable - config.StoreGlobalConfig(cfg) - } - if mutationChecker := req.Form.Get("tidb_enable_mutation_checker"); mutationChecker != "" { - s, err := session.CreateSession(h.Store) - if err != nil { - writeError(w, err) - return - } - defer s.Close() - - switch mutationChecker { - case "0": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableMutationChecker, variable.Off) - case "1": - err = s.GetSessionVars().GlobalVarsAccessor.SetGlobalSysVar(context.Background(), variable.TiDBEnableMutationChecker, variable.On) - default: - writeError(w, errors.New("illegal argument")) - return - } - if err != nil { - writeError(w, err) - return - } - } - if transactionSummaryCapacity := req.Form.Get("transaction_summary_capacity"); transactionSummaryCapacity != "" { - capacity, err := strconv.Atoi(transactionSummaryCapacity) - if err != nil { - writeError(w, errors.New("illegal argument")) - return - } else if capacity < 0 || capacity > 5000 { - writeError(w, errors.New("transaction_summary_capacity out of range, should be in 0 to 5000")) - return - } - cfg := config.GetGlobalConfig() - cfg.TrxSummary.TransactionSummaryCapacity = uint(capacity) - config.StoreGlobalConfig(cfg) - txninfo.Recorder.ResizeSummaries(uint(capacity)) - } - if transactionIDDigestMinDuration := req.Form.Get("transaction_id_digest_min_duration"); transactionIDDigestMinDuration != "" { - duration, err := strconv.Atoi(transactionIDDigestMinDuration) - if err != nil { - writeError(w, errors.New("illegal argument")) - return - } else if duration < 0 || duration > 2147483647 { - writeError(w, errors.New("transaction_id_digest_min_duration out of range, should be in 0 to 2147483647")) - return - } - cfg := config.GetGlobalConfig() - cfg.TrxSummary.TransactionIDDigestMinDuration = uint(duration) - config.StoreGlobalConfig(cfg) - txninfo.Recorder.SetMinDuration(time.Duration(duration) * time.Millisecond) - } - } else { - writeData(w, config.GetGlobalConfig()) - } -} - -// ServeHTTP recovers binlog service. -func (BinlogRecover) ServeHTTP(w http.ResponseWriter, req *http.Request) { - op := req.FormValue(handler.Operation) - switch op { - case "reset": - binloginfo.ResetSkippedCommitterCounter() - case "nowait": - err := binloginfo.DisableSkipBinlogFlag() - if err != nil { - writeError(w, err) - return - } - case "status": - default: - sec, err := strconv.ParseInt(req.FormValue(handler.Seconds), 10, 64) - if sec <= 0 || err != nil { - sec = 1800 - } - err = binloginfo.DisableSkipBinlogFlag() - if err != nil { - writeError(w, err) - return - } - timeout := time.Duration(sec) * time.Second - err = binloginfo.WaitBinlogRecover(timeout) - if err != nil { - writeError(w, err) - return - } - } - writeData(w, binloginfo.GetBinlogStatus()) -} - -// TableFlashReplicaInfo is the replica information of a table. -type TableFlashReplicaInfo struct { - // Modifying the field name needs to negotiate with TiFlash colleague. - ID int64 `json:"id"` - ReplicaCount uint64 `json:"replica_count"` - LocationLabels []string `json:"location_labels"` - Available bool `json:"available"` - HighPriority bool `json:"high_priority"` -} - -// ServeHTTP implements the HTTPHandler interface. -func (h FlashReplicaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method == http.MethodPost { - h.handleStatusReport(w, req) - return - } - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - replicaInfos := make([]*TableFlashReplicaInfo, 0) - allDBs := schema.AllSchemas() - for _, db := range allDBs { - tbls := schema.SchemaTables(db.Name) - for _, tbl := range tbls { - replicaInfos = h.getTiFlashReplicaInfo(tbl.Meta(), replicaInfos) - } - } - dropedOrTruncateReplicaInfos, err := h.getDropOrTruncateTableTiflash(schema) - if err != nil { - writeError(w, err) - return - } - replicaInfos = append(replicaInfos, dropedOrTruncateReplicaInfos...) - writeData(w, replicaInfos) -} - -func (FlashReplicaHandler) getTiFlashReplicaInfo(tblInfo *model.TableInfo, replicaInfos []*TableFlashReplicaInfo) []*TableFlashReplicaInfo { - if tblInfo.TiFlashReplica == nil { - return replicaInfos - } - if pi := tblInfo.GetPartitionInfo(); pi != nil { - for _, p := range pi.Definitions { - replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ - ID: p.ID, - ReplicaCount: tblInfo.TiFlashReplica.Count, - LocationLabels: tblInfo.TiFlashReplica.LocationLabels, - Available: tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID), - }) - } - for _, p := range pi.AddingDefinitions { - replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ - ID: p.ID, - ReplicaCount: tblInfo.TiFlashReplica.Count, - LocationLabels: tblInfo.TiFlashReplica.LocationLabels, - Available: tblInfo.TiFlashReplica.IsPartitionAvailable(p.ID), - HighPriority: true, - }) - } - return replicaInfos - } - replicaInfos = append(replicaInfos, &TableFlashReplicaInfo{ - ID: tblInfo.ID, - ReplicaCount: tblInfo.TiFlashReplica.Count, - LocationLabels: tblInfo.TiFlashReplica.LocationLabels, - Available: tblInfo.TiFlashReplica.Available, - }) - return replicaInfos -} - -func (h FlashReplicaHandler) getDropOrTruncateTableTiflash(currentSchema infoschema.InfoSchema) ([]*TableFlashReplicaInfo, error) { - s, err := session.CreateSession(h.Store) - if err != nil { - return nil, errors.Trace(err) - } - defer s.Close() - - store := domain.GetDomain(s).Store() - txn, err := store.Begin() - if err != nil { - return nil, errors.Trace(err) - } - gcSafePoint, err := gcutil.GetGCSafePoint(s) - if err != nil { - return nil, err - } - replicaInfos := make([]*TableFlashReplicaInfo, 0) - uniqueIDMap := make(map[int64]struct{}) - handleJobAndTableInfo := func(job *model.Job, tblInfo *model.TableInfo) (bool, error) { - // Avoid duplicate table ID info. - if _, ok := currentSchema.TableByID(tblInfo.ID); ok { - return false, nil - } - if _, ok := uniqueIDMap[tblInfo.ID]; ok { - return false, nil - } - uniqueIDMap[tblInfo.ID] = struct{}{} - replicaInfos = h.getTiFlashReplicaInfo(tblInfo, replicaInfos) - return false, nil - } - dom := domain.GetDomain(s) - fn := func(jobs []*model.Job) (bool, error) { - return executor.GetDropOrTruncateTableInfoFromJobs(jobs, gcSafePoint, dom, handleJobAndTableInfo) - } - err = ddl.IterAllDDLJobs(s, txn, fn) - if err != nil { - if terror.ErrorEqual(variable.ErrSnapshotTooOld, err) { - // The err indicate that current ddl job and remain DDL jobs was been deleted by GC, - // just ignore the error and return directly. - return replicaInfos, nil - } - return nil, err - } - return replicaInfos, nil -} - -type tableFlashReplicaStatus struct { - // Modifying the field name needs to negotiate with TiFlash colleague. - ID int64 `json:"id"` - // RegionCount is the number of regions that need sync. - RegionCount uint64 `json:"region_count"` - // FlashRegionCount is the number of regions that already sync completed. - FlashRegionCount uint64 `json:"flash_region_count"` -} - -// checkTableFlashReplicaAvailable uses to check the available status of table flash replica. -func (tf *tableFlashReplicaStatus) checkTableFlashReplicaAvailable() bool { - return tf.FlashRegionCount == tf.RegionCount -} - -func (h FlashReplicaHandler) handleStatusReport(w http.ResponseWriter, req *http.Request) { - var status tableFlashReplicaStatus - err := json.NewDecoder(req.Body).Decode(&status) - if err != nil { - writeError(w, err) - return - } - do, err := session.GetDomain(h.Store) - if err != nil { - writeError(w, err) - return - } - s, err := session.CreateSession(h.Store) - if err != nil { - writeError(w, err) - return - } - defer s.Close() - - available := status.checkTableFlashReplicaAvailable() - err = do.DDL().UpdateTableReplicaInfo(s, status.ID, available) - if err != nil { - writeError(w, err) - } - if available { - var tableInfo model.TableInfo - tableInfo.ID = status.ID - err = infosync.DeleteTiFlashTableSyncProgress(&tableInfo) - } else { - progress := float64(status.FlashRegionCount) / float64(status.RegionCount) - err = infosync.UpdateTiFlashProgressCache(status.ID, progress) - } - if err != nil { - writeError(w, err) - } - - logutil.BgLogger().Info("handle flash replica report", zap.Int64("table ID", status.ID), zap.Uint64("region count", - status.RegionCount), - zap.Uint64("flash region count", status.FlashRegionCount), - zap.Error(err)) -} - -// SchemaTableStorage is the schema table storage info. -type SchemaTableStorage struct { - TableSchema string `json:"table_schema"` - TableName string `json:"table_name"` - TableRows int64 `json:"table_rows"` - AvgRowLength int64 `json:"avg_row_length"` - DataLength int64 `json:"data_length"` - MaxDataLength int64 `json:"max_data_length"` - IndexLength int64 `json:"index_length"` - DataFree int64 `json:"data_free"` -} - -func getSchemaTablesStorageInfo(h *SchemaStorageHandler, schema *model.CIStr, table *model.CIStr) (messages []*SchemaTableStorage, err error) { - var s session.Session - if s, err = session.CreateSession(h.Store); err != nil { - return - } - defer s.Close() - - sctx := s.(sessionctx.Context) - condition := make([]string, 0) - params := make([]interface{}, 0) - - if schema != nil { - condition = append(condition, `TABLE_SCHEMA = %?`) - params = append(params, schema.O) - } - if table != nil { - condition = append(condition, `TABLE_NAME = %?`) - params = append(params, table.O) - } - - sql := `select TABLE_SCHEMA,TABLE_NAME,TABLE_ROWS,AVG_ROW_LENGTH,DATA_LENGTH,MAX_DATA_LENGTH,INDEX_LENGTH,DATA_FREE from INFORMATION_SCHEMA.TABLES` - if len(condition) > 0 { - //nolint: gosec - sql += ` WHERE ` + strings.Join(condition, ` AND `) - } - var results sqlexec.RecordSet - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - if results, err = sctx.(sqlexec.SQLExecutor).ExecuteInternal(ctx, sql, params...); err != nil { - logutil.BgLogger().Error(`ExecuteInternal`, zap.Error(err)) - } else if results != nil { - messages = make([]*SchemaTableStorage, 0) - defer terror.Call(results.Close) - for { - req := results.NewChunk(nil) - if err = results.Next(ctx, req); err != nil { - break - } - - if req.NumRows() == 0 { - break - } - - for i := 0; i < req.NumRows(); i++ { - messages = append(messages, &SchemaTableStorage{ - TableSchema: req.GetRow(i).GetString(0), - TableName: req.GetRow(i).GetString(1), - TableRows: req.GetRow(i).GetInt64(2), - AvgRowLength: req.GetRow(i).GetInt64(3), - DataLength: req.GetRow(i).GetInt64(4), - MaxDataLength: req.GetRow(i).GetInt64(5), - IndexLength: req.GetRow(i).GetInt64(6), - DataFree: req.GetRow(i).GetInt64(7), - }) - } - } - } - return -} - -// ServeHTTP handles request of list a database or table's schemas. -func (h SchemaStorageHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - - // parse params - params := mux.Vars(req) - - var ( - dbName *model.CIStr - tableName *model.CIStr - isSingle bool - ) - - if reqDbName, ok := params[handler.DBName]; ok { - cDBName := model.NewCIStr(reqDbName) - // all table schemas in a specified database - schemaInfo, exists := schema.SchemaByName(cDBName) - if !exists { - writeError(w, infoschema.ErrDatabaseNotExists.GenWithStackByArgs(reqDbName)) - return - } - dbName = &schemaInfo.Name - - if reqTableName, ok := params[handler.TableName]; ok { - // table schema of a specified table name - cTableName := model.NewCIStr(reqTableName) - data, e := schema.TableByName(cDBName, cTableName) - if e != nil { - writeError(w, e) - return - } - tableName = &data.Meta().Name - isSingle = true - } - } - - if results, e := getSchemaTablesStorageInfo(&h, dbName, tableName); e != nil { - writeError(w, e) - } else { - if isSingle { - writeData(w, results[0]) - } else { - writeData(w, results) - } - } -} - -// WriteDBTablesData writes all the table data in a database. The format is the marshal result of []*model.TableInfo, you can -// unmarshal it to []*model.TableInfo. In this function, we manually construct the marshal result so that the memory -// can be deallocated quickly. -// For every table in the input, we marshal them. The result such as {tb1} {tb2} {tb3}. -// Then we add some bytes to make it become [{tb1}, {tb2}, {tb3}], so we can unmarshal it to []*model.TableInfo. -// Note: It would return StatusOK even if errors occur. But if errors occur, there must be some bugs. -func WriteDBTablesData(w http.ResponseWriter, tbs []table.Table) { - if len(tbs) == 0 { - writeData(w, []*model.TableInfo{}) - return - } - w.Header().Set(handler.HeaderContentType, handler.ContentTypeJSON) - // We assume that marshal is always OK. - w.WriteHeader(http.StatusOK) - _, err := w.Write(hack.Slice("[\n")) - if err != nil { - terror.Log(errors.Trace(err)) - return - } - init := false - for _, tb := range tbs { - if init { - _, err = w.Write(hack.Slice(",\n")) - if err != nil { - terror.Log(errors.Trace(err)) - return - } - } else { - init = true - } - js, err := json.MarshalIndent(tb.Meta(), "", " ") - if err != nil { - terror.Log(errors.Trace(err)) - return - } - _, err = w.Write(js) - if err != nil { - terror.Log(errors.Trace(err)) - return - } - } - _, err = w.Write(hack.Slice("\n]")) - terror.Log(errors.Trace(err)) -} - -// ServeHTTP handles request of list a database or table's schemas. -func (h SchemaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - - // parse params - params := mux.Vars(req) - - if dbName, ok := params[handler.DBName]; ok { - cDBName := model.NewCIStr(dbName) - if tableName, ok := params[handler.TableName]; ok { - // table schema of a specified table name - cTableName := model.NewCIStr(tableName) - data, err := schema.TableByName(cDBName, cTableName) - if err != nil { - writeError(w, err) - return - } - writeData(w, data.Meta()) - return - } - // all table schemas in a specified database - if schema.SchemaExists(cDBName) { - tbs := schema.SchemaTables(cDBName) - WriteDBTablesData(w, tbs) - return - } - writeError(w, infoschema.ErrDatabaseNotExists.GenWithStackByArgs(dbName)) - return - } - - if tableID := req.FormValue(handler.TableIDQuery); len(tableID) > 0 { - // table schema of a specified tableID - tid, err := strconv.Atoi(tableID) - if err != nil { - writeError(w, err) - return - } - if tid < 0 { - writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) - return - } - if data, ok := schema.TableByID(int64(tid)); ok { - writeData(w, data.Meta()) - return - } - // The tid maybe a partition ID of the partition-table. - tbl, _, _ := schema.FindTableByPartitionID(int64(tid)) - if tbl == nil { - writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) - return - } - writeData(w, tbl.Meta()) - return - } - - // all databases' schemas - writeData(w, schema.AllSchemas()) -} - -// ServeHTTP handles table related requests, such as table's region information, disk usage. -func (h *TableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // parse params - params := mux.Vars(req) - dbName := params[handler.DBName] - tableName := params[handler.TableName] - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - - tableName, partitionName := handler.ExtractTableAndPartitionName(tableName) - tableVal, err := schema.TableByName(model.NewCIStr(dbName), model.NewCIStr(tableName)) - if err != nil { - writeError(w, err) - return - } - switch h.op { - case OpTableRegions: - h.handleRegionRequest(tableVal, w) - case OpTableRanges: - h.handleRangeRequest(tableVal, w) - case OpTableDiskUsage: - h.handleDiskUsageRequest(tableVal, w) - case OpTableScatter: - // supports partition table, only get one physical table, prevent too many scatter schedulers. - ptbl, err := h.GetPartition(tableVal, partitionName) - if err != nil { - writeError(w, err) - return - } - h.handleScatterTableRequest(ptbl, w) - case OpStopTableScatter: - ptbl, err := h.GetPartition(tableVal, partitionName) - if err != nil { - writeError(w, err) - return - } - h.handleStopScatterTableRequest(ptbl, w) - default: - writeError(w, errors.New("method not found")) - } -} - -// ServeHTTP handles request of ddl jobs history. -func (h DDLHistoryJobHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - var jobID, limitID int - var err error - if jobValue := req.FormValue(handler.JobID); len(jobValue) > 0 { - jobID, err = strconv.Atoi(jobValue) - if err != nil { - writeError(w, err) - return - } - if jobID < 1 { - writeError(w, errors.New("ddl history start_job_id must be greater than 0")) - return - } - } - if limitValue := req.FormValue(handler.Limit); len(limitValue) > 0 { - limitID, err = strconv.Atoi(limitValue) - if err != nil { - writeError(w, err) - return - } - if limitID < 1 { - writeError(w, errors.New("ddl history limit must be greater than 0")) - return - } - } - - jobs, err := h.getHistoryDDL(jobID, limitID) - if err != nil { - writeError(w, err) - return - } - writeData(w, jobs) -} - -func (h DDLHistoryJobHandler) getHistoryDDL(jobID, limit int) (jobs []*model.Job, err error) { - txn, err := h.Store.Begin() - if err != nil { - return nil, errors.Trace(err) - } - txnMeta := meta.NewMeta(txn) - - if jobID == 0 && limit == 0 { - jobs, err = ddl.GetAllHistoryDDLJobs(txnMeta) - } else { - jobs, err = ddl.ScanHistoryDDLJobs(txnMeta, int64(jobID), limit) - } - if err != nil { - return nil, errors.Trace(err) - } - return jobs, nil -} - -func (h DDLResignOwnerHandler) resignDDLOwner() error { - dom, err := session.GetDomain(h.store) - if err != nil { - return errors.Trace(err) - } - - ownerMgr := dom.DDL().OwnerManager() - err = ownerMgr.ResignOwner(context.Background()) - if err != nil { - return errors.Trace(err) - } - return nil -} - -// ServeHTTP handles request of resigning ddl owner. -func (h DDLResignOwnerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - writeError(w, errors.Errorf("This api only support POST method")) - return - } - - err := h.resignDDLOwner() - if err != nil { - log.Error("failed to resign DDL owner", zap.Error(err)) - writeError(w, err) - return - } - - writeData(w, "success!") -} - -func (h *TableHandler) getPDAddr() ([]string, error) { - etcd, ok := h.Store.(kv.EtcdBackend) - if !ok { - return nil, errors.New("not implemented") - } - pdAddrs, err := etcd.EtcdAddrs() - if err != nil { - return nil, err - } - if len(pdAddrs) == 0 { - return nil, errors.New("pd unavailable") - } - return pdAddrs, nil -} - -func (h *TableHandler) addScatterSchedule(startKey, endKey []byte, name string) error { - pdAddrs, err := h.getPDAddr() - if err != nil { - return err - } - input := map[string]string{ - "name": "scatter-range", - "start_key": url.QueryEscape(string(startKey)), - "end_key": url.QueryEscape(string(endKey)), - "range_name": name, - } - v, err := json.Marshal(input) - if err != nil { - return err - } - scheduleURL := fmt.Sprintf("%s://%s/pd/api/v1/schedulers", util.InternalHTTPSchema(), pdAddrs[0]) - resp, err := util.InternalHTTPClient().Post(scheduleURL, "application/json", bytes.NewBuffer(v)) - if err != nil { - return err - } - if err := resp.Body.Close(); err != nil { - log.Error("failed to close response body", zap.Error(err)) - } - return nil -} - -func (h *TableHandler) deleteScatterSchedule(name string) error { - pdAddrs, err := h.getPDAddr() - if err != nil { - return err - } - scheduleURL := fmt.Sprintf("%s://%s/pd/api/v1/schedulers/scatter-range-%s", util.InternalHTTPSchema(), pdAddrs[0], name) - req, err := http.NewRequest(http.MethodDelete, scheduleURL, nil) - if err != nil { - return err - } - resp, err := util.InternalHTTPClient().Do(req) - if err != nil { - return err - } - if err := resp.Body.Close(); err != nil { - log.Error("failed to close response body", zap.Error(err)) - } - return nil -} - -func (h *TableHandler) handleScatterTableRequest(tbl table.PhysicalTable, w http.ResponseWriter) { - // for record - tableID := tbl.GetPhysicalID() - startKey, endKey := tablecodec.GetTableHandleKeyRange(tableID) - startKey = codec.EncodeBytes([]byte{}, startKey) - endKey = codec.EncodeBytes([]byte{}, endKey) - tableName := fmt.Sprintf("%s-%d", tbl.Meta().Name.String(), tableID) - err := h.addScatterSchedule(startKey, endKey, tableName) - if err != nil { - writeError(w, errors.Annotate(err, "scatter record error")) - return - } - // for indices - for _, index := range tbl.Indices() { - indexID := index.Meta().ID - indexName := index.Meta().Name.String() - startKey, endKey := tablecodec.GetTableIndexKeyRange(tableID, indexID) - startKey = codec.EncodeBytes([]byte{}, startKey) - endKey = codec.EncodeBytes([]byte{}, endKey) - name := tableName + "-" + indexName - err := h.addScatterSchedule(startKey, endKey, name) - if err != nil { - writeError(w, errors.Annotatef(err, "scatter index(%s) error", name)) - return - } - } - writeData(w, "success!") -} - -func (h *TableHandler) handleStopScatterTableRequest(tbl table.PhysicalTable, w http.ResponseWriter) { - // for record - tableName := fmt.Sprintf("%s-%d", tbl.Meta().Name.String(), tbl.GetPhysicalID()) - err := h.deleteScatterSchedule(tableName) - if err != nil { - writeError(w, errors.Annotate(err, "stop scatter record error")) - return - } - // for indices - for _, index := range tbl.Indices() { - indexName := index.Meta().Name.String() - name := tableName + "-" + indexName - err := h.deleteScatterSchedule(name) - if err != nil { - writeError(w, errors.Annotatef(err, "delete scatter index(%s) error", name)) - return - } - } - writeData(w, "success!") -} - -func (h *TableHandler) handleRegionRequest(tbl table.Table, w http.ResponseWriter) { - pi := tbl.Meta().GetPartitionInfo() - if pi != nil { - // Partitioned table. - var data []*TableRegions - for _, def := range pi.Definitions { - tableRegions, err := h.getRegionsByID(tbl, def.ID, def.Name.O) - if err != nil { - writeError(w, err) - return - } - - data = append(data, tableRegions) - } - writeData(w, data) - return - } - - meta := tbl.Meta() - tableRegions, err := h.getRegionsByID(tbl, meta.ID, meta.Name.O) - if err != nil { - writeError(w, err) - return - } - - writeData(w, tableRegions) -} - -func createTableRanges(tblID int64, tblName string, indices []*model.IndexInfo) *TableRanges { - indexPrefix := tablecodec.GenTableIndexPrefix(tblID) - recordPrefix := tablecodec.GenTableRecordPrefix(tblID) - tableEnd := tablecodec.EncodeTablePrefix(tblID + 1) - ranges := &TableRanges{ - TableName: tblName, - TableID: tblID, - Range: createRangeDetail(tablecodec.EncodeTablePrefix(tblID), tableEnd), - Record: createRangeDetail(recordPrefix, tableEnd), - Index: createRangeDetail(indexPrefix, recordPrefix), - } - if len(indices) != 0 { - indexRanges := make(map[string]RangeDetail) - for _, index := range indices { - start := tablecodec.EncodeTableIndexPrefix(tblID, index.ID) - end := tablecodec.EncodeTableIndexPrefix(tblID, index.ID+1) - indexRanges[index.Name.String()] = createRangeDetail(start, end) - } - ranges.Indices = indexRanges - } - return ranges -} - -func (*TableHandler) handleRangeRequest(tbl table.Table, w http.ResponseWriter) { - meta := tbl.Meta() - pi := meta.GetPartitionInfo() - if pi != nil { - // Partitioned table. - var data []*TableRanges - for _, def := range pi.Definitions { - data = append(data, createTableRanges(def.ID, def.Name.String(), meta.Indices)) - } - writeData(w, data) - return - } - - writeData(w, createTableRanges(meta.ID, meta.Name.String(), meta.Indices)) -} - -func (h *TableHandler) getRegionsByID(tbl table.Table, id int64, name string) (*TableRegions, error) { - // for record - startKey, endKey := tablecodec.GetTableHandleKeyRange(id) - ctx := context.Background() - pdCli := h.RegionCache.PDClient() - regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) - if err != nil { - return nil, err - } - - recordRegions := make([]handler.RegionMeta, 0, len(regions)) - for _, region := range regions { - meta := handler.RegionMeta{ - ID: region.Meta.Id, - Leader: region.Leader, - Peers: region.Meta.Peers, - RegionEpoch: region.Meta.RegionEpoch, - } - recordRegions = append(recordRegions, meta) - } - - // for indices - indices := make([]IndexRegions, len(tbl.Indices())) - for i, index := range tbl.Indices() { - indexID := index.Meta().ID - indices[i].Name = index.Meta().Name.String() - indices[i].ID = indexID - startKey, endKey := tablecodec.GetTableIndexKeyRange(id, indexID) - regions, err := pdCli.ScanRegions(ctx, startKey, endKey, -1) - if err != nil { - return nil, err - } - indexRegions := make([]handler.RegionMeta, 0, len(regions)) - for _, region := range regions { - meta := handler.RegionMeta{ - ID: region.Meta.Id, - Leader: region.Leader, - Peers: region.Meta.Peers, - RegionEpoch: region.Meta.RegionEpoch, - } - indexRegions = append(indexRegions, meta) - } - indices[i].Regions = indexRegions - } - - return &TableRegions{ - TableName: name, - TableID: id, - Indices: indices, - RecordRegions: recordRegions, - }, nil -} - -func (h *TableHandler) handleDiskUsageRequest(tbl table.Table, w http.ResponseWriter) { - tableID := tbl.Meta().ID - var stats helper.PDRegionStats - err := h.GetPDRegionStats(tableID, &stats, false) - if err != nil { - writeError(w, err) - return - } - writeData(w, stats.StorageSize) -} - -// ServeHTTP handles request of get region by ID. -func (h RegionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - // parse and check params - params := mux.Vars(req) - if _, ok := params[handler.RegionID]; !ok { - router := mux.CurrentRoute(req).GetName() - if router == "RegionsMeta" { - startKey := []byte{'m'} - endKey := []byte{'n'} - - recordRegionIDs, err := h.RegionCache.ListRegionIDsInKeyRange(tikv.NewBackofferWithVars(context.Background(), 500, nil), startKey, endKey) - if err != nil { - writeError(w, err) - return - } - - recordRegions, err := h.GetRegionsMeta(recordRegionIDs) - if err != nil { - writeError(w, err) - return - } - writeData(w, recordRegions) - return - } - if router == "RegionHot" { - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - hotRead, err := h.ScrapeHotInfo(pdapi.HotRead, schema.AllSchemas()) - if err != nil { - writeError(w, err) - return - } - hotWrite, err := h.ScrapeHotInfo(pdapi.HotWrite, schema.AllSchemas()) - if err != nil { - writeError(w, err) - return - } - writeData(w, map[string]interface{}{ - "write": hotWrite, - "read": hotRead, - }) - return - } - return - } - - regionIDInt, err := strconv.ParseInt(params[handler.RegionID], 0, 64) - if err != nil { - writeError(w, err) - return - } - regionID := uint64(regionIDInt) - - // locate region - region, err := h.RegionCache.LocateRegionByID(tikv.NewBackofferWithVars(context.Background(), 500, nil), regionID) - if err != nil { - writeError(w, err) - return - } - - frameRange, err := helper.NewRegionFrameRange(region) - if err != nil { - writeError(w, err) - return - } - - // create RegionDetail from RegionFrameRange - regionDetail := &RegionDetail{ - RegionID: regionID, - RangeDetail: createRangeDetail(region.StartKey, region.EndKey), - } - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - // Since we need a database's name for each frame, and a table's database name can not - // get from table's ID directly. Above all, here do dot process like - // `for id in [frameRange.firstTableID,frameRange.endTableID]` - // on [frameRange.firstTableID,frameRange.endTableID] is small enough. - for _, db := range schema.AllSchemas() { - if util.IsMemDB(db.Name.L) { - continue - } - for _, tableVal := range db.Tables { - regionDetail.addTableInRange(db.Name.String(), tableVal, frameRange) - } - } - writeData(w, regionDetail) -} - -// parseQuery is used to parse query string in URL with shouldUnescape, due to golang http package can not distinguish -// query like "?a=" and "?a". We rewrite it to separate these two queries. e.g. -// "?a=" which means that a is an empty string ""; -// "?a" which means that a is null. -// If shouldUnescape is true, we use QueryUnescape to handle keys and values that will be put in m. -// If shouldUnescape is false, we don't use QueryUnescap to handle. -func parseQuery(query string, m url.Values, shouldUnescape bool) error { - var err error - for query != "" { - key := query - if i := strings.IndexAny(key, "&;"); i >= 0 { - key, query = key[:i], key[i+1:] - } else { - query = "" - } - if key == "" { - continue - } - if i := strings.Index(key, "="); i >= 0 { - value := "" - key, value = key[:i], key[i+1:] - if shouldUnescape { - key, err = url.QueryUnescape(key) - if err != nil { - return errors.Trace(err) - } - value, err = url.QueryUnescape(value) - if err != nil { - return errors.Trace(err) - } - } - m[key] = append(m[key], value) - } else { - if shouldUnescape { - key, err = url.QueryUnescape(key) - if err != nil { - return errors.Trace(err) - } - } - if _, ok := m[key]; !ok { - m[key] = nil - } - } - } - return errors.Trace(err) -} - -// ServeHTTP handles request of list a table's regions. -func (h MvccTxnHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - var data interface{} - params := mux.Vars(req) - var err error - switch h.op { - case OpMvccGetByHex: - data, err = h.HandleMvccGetByHex(params) - case OpMvccGetByIdx, OpMvccGetByKey: - if req.URL == nil { - err = errors.BadRequestf("Invalid URL") - break - } - values := make(url.Values) - err = parseQuery(req.URL.RawQuery, values, true) - if err == nil { - if h.op == OpMvccGetByIdx { - data, err = h.handleMvccGetByIdx(params, values) - } else { - data, err = h.handleMvccGetByKey(params, values) - } - } - case OpMvccGetByTxn: - data, err = h.handleMvccGetByTxn(params) - default: - err = errors.NotSupportedf("Operation not supported.") - } - if err != nil { - writeError(w, err) - } else { - writeData(w, data) - } -} - -// handleMvccGetByIdx gets MVCC info by an index key. -func (h MvccTxnHandler) handleMvccGetByIdx(params map[string]string, values url.Values) (interface{}, error) { - dbName := params[handler.DBName] - tableName := params[handler.TableName] - - t, err := h.GetTable(dbName, tableName) - if err != nil { - return nil, errors.Trace(err) - } - handle, err := h.GetHandle(t, params, values) - if err != nil { - return nil, errors.Trace(err) - } - - var idxCols []*model.ColumnInfo - var idx table.Index - for _, v := range t.Indices() { - if strings.EqualFold(v.Meta().Name.String(), params[handler.IndexName]) { - for _, c := range v.Meta().Columns { - idxCols = append(idxCols, t.Meta().Columns[c.Offset]) - } - idx = v - break - } - } - if idx == nil { - return nil, errors.NotFoundf("Index %s not found!", params[handler.IndexName]) - } - return h.GetMvccByIdxValue(idx, values, idxCols, handle) -} - -func (h MvccTxnHandler) handleMvccGetByKey(params map[string]string, values url.Values) (interface{}, error) { - dbName := params[handler.DBName] - tableName := params[handler.TableName] - tb, err := h.GetTable(dbName, tableName) - if err != nil { - return nil, errors.Trace(err) - } - handle, err := h.GetHandle(tb, params, values) - if err != nil { - return nil, err - } - - encodedKey := tablecodec.EncodeRecordKey(tb.RecordPrefix(), handle) - data, err := h.GetMvccByEncodedKey(encodedKey) - if err != nil { - return nil, err - } - regionID, err := h.GetRegionIDByKey(encodedKey) - if err != nil { - return nil, err - } - resp := &helper.MvccKV{Key: strings.ToUpper(hex.EncodeToString(encodedKey)), Value: data, RegionID: regionID} - if len(values.Get("decode")) == 0 { - return resp, nil - } - colMap := make(map[int64]*types.FieldType, 3) - for _, col := range tb.Meta().Columns { - colMap[col.ID] = &(col.FieldType) - } - - respValue := resp.Value - var result interface{} = resp - if respValue.Info != nil { - datas := make(map[string]map[string]string) - for _, w := range respValue.Info.Writes { - if len(w.ShortValue) > 0 { - datas[strconv.FormatUint(w.StartTs, 10)], err = h.decodeMvccData(w.ShortValue, colMap, tb.Meta()) - } - } - - for _, v := range respValue.Info.Values { - if len(v.Value) > 0 { - datas[strconv.FormatUint(v.StartTs, 10)], err = h.decodeMvccData(v.Value, colMap, tb.Meta()) - } - } - - if len(datas) > 0 { - re := map[string]interface{}{ - "key": resp.Key, - "info": respValue.Info, - "data": datas, - } - if err != nil { - re["decode_error"] = err.Error() - } - result = re - } - } - - return result, nil -} - -func (MvccTxnHandler) decodeMvccData(bs []byte, colMap map[int64]*types.FieldType, tb *model.TableInfo) (map[string]string, error) { - rs, err := tablecodec.DecodeRowToDatumMap(bs, colMap, time.UTC) - record := make(map[string]string, len(tb.Columns)) - for _, col := range tb.Columns { - if c, ok := rs[col.ID]; ok { - data := "nil" - if !c.IsNull() { - data, err = c.ToString() - } - record[col.Name.O] = data - } - } - return record, err -} - -func (h *MvccTxnHandler) handleMvccGetByTxn(params map[string]string) (interface{}, error) { - startTS, err := strconv.ParseInt(params[handler.StartTS], 0, 64) - if err != nil { - return nil, errors.Trace(err) - } - tableID, err := h.GetTableID(params[handler.DBName], params[handler.TableName]) - if err != nil { - return nil, errors.Trace(err) - } - startKey := tablecodec.EncodeTablePrefix(tableID) - endKey := tablecodec.EncodeRowKeyWithHandle(tableID, kv.IntHandle(math.MaxInt64)) - return h.GetMvccByStartTs(uint64(startTS), startKey, endKey) -} - -// ServerInfo is used to report the servers info when do http request. -type ServerInfo struct { - IsOwner bool `json:"is_owner"` - MaxProcs int `json:"max_procs"` - GOGC int `json:"gogc"` - *infosync.ServerInfo -} - -// ServeHTTP handles request of ddl server info. -func (h ServerInfoHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { - do, err := session.GetDomain(h.Store) - if err != nil { - writeError(w, errors.New("create session error")) - log.Error("failed to get session domain", zap.Error(err)) - return - } - info := ServerInfo{} - info.ServerInfo, err = infosync.GetServerInfo() - if err != nil { - writeError(w, err) - log.Error("failed to get server info", zap.Error(err)) - return - } - info.IsOwner = do.DDL().OwnerManager().IsOwner() - info.MaxProcs = runtime.GOMAXPROCS(0) - info.GOGC = util.GetGOGC() - writeData(w, info) -} - -// ClusterServerInfo is used to report cluster servers info when do http request. -type ClusterServerInfo struct { - ServersNum int `json:"servers_num,omitempty"` - OwnerID string `json:"owner_id"` - IsAllServerVersionConsistent bool `json:"is_all_server_version_consistent,omitempty"` - AllServersDiffVersions []infosync.ServerVersionInfo `json:"all_servers_diff_versions,omitempty"` - AllServersInfo map[string]*infosync.ServerInfo `json:"all_servers_info,omitempty"` -} - -// ServeHTTP handles request of all ddl servers info. -func (h AllServerInfoHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) { - do, err := session.GetDomain(h.Store) - if err != nil { - writeError(w, errors.New("create session error")) - log.Error("failed to get session domain", zap.Error(err)) - return - } - ctx := context.Background() - allServersInfo, err := infosync.GetAllServerInfo(ctx) - if err != nil { - writeError(w, errors.New("ddl server information not found")) - log.Error("failed to get all server info", zap.Error(err)) - return - } - ctx, cancel := context.WithTimeout(ctx, 3*time.Second) - ownerID, err := do.DDL().OwnerManager().GetOwnerID(ctx) - cancel() - if err != nil { - writeError(w, errors.New("ddl server information not found")) - log.Error("failed to get owner id", zap.Error(err)) - return - } - allVersionsMap := map[infosync.ServerVersionInfo]struct{}{} - allVersions := make([]infosync.ServerVersionInfo, 0, len(allServersInfo)) - for _, v := range allServersInfo { - if _, ok := allVersionsMap[v.ServerVersionInfo]; ok { - continue - } - allVersionsMap[v.ServerVersionInfo] = struct{}{} - allVersions = append(allVersions, v.ServerVersionInfo) - } - clusterInfo := ClusterServerInfo{ - ServersNum: len(allServersInfo), - OwnerID: ownerID, - // len(allVersions) = 1 indicates there has only 1 tidb version in cluster, so all server versions are consistent. - IsAllServerVersionConsistent: len(allVersions) == 1, - AllServersInfo: allServersInfo, - } - // if IsAllServerVersionConsistent is false, return the all tidb servers version. - if !clusterInfo.IsAllServerVersionConsistent { - clusterInfo.AllServersDiffVersions = allVersions - } - writeData(w, clusterInfo) -} - -// DBTableInfo is used to report the database, table information and the current schema version. -type DBTableInfo struct { - DBInfo *model.DBInfo `json:"db_info"` - TableInfo *model.TableInfo `json:"table_info"` - SchemaVersion int64 `json:"schema_version"` -} - -// ServeHTTP handles request of database information and table information by tableID. -func (h DBTableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - params := mux.Vars(req) - tableID := params[handler.TableID] - physicalID, err := strconv.Atoi(tableID) - if err != nil { - writeError(w, errors.Errorf("Wrong tableID: %v", tableID)) - return - } - - schema, err := h.Schema() - if err != nil { - writeError(w, err) - return - } - - dbTblInfo := DBTableInfo{ - SchemaVersion: schema.SchemaMetaVersion(), - } - tbl, ok := schema.TableByID(int64(physicalID)) - if ok { - dbTblInfo.TableInfo = tbl.Meta() - dbInfo, ok := schema.SchemaByTable(dbTblInfo.TableInfo) - if !ok { - logutil.BgLogger().Error("can not find the database of the table", zap.Int64("table id", dbTblInfo.TableInfo.ID), zap.String("table name", dbTblInfo.TableInfo.Name.L)) - writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) - return - } - dbTblInfo.DBInfo = dbInfo - writeData(w, dbTblInfo) - return - } - // The physicalID maybe a partition ID of the partition-table. - tbl, dbInfo, _ := schema.FindTableByPartitionID(int64(physicalID)) - if tbl == nil { - writeError(w, infoschema.ErrTableNotExists.GenWithStack("Table which ID = %s does not exist.", tableID)) - return - } - dbTblInfo.TableInfo = tbl.Meta() - dbTblInfo.DBInfo = dbInfo - writeData(w, dbTblInfo) -} - -// ServeHTTP handles request of TiDB metric profile. -func (h ProfileHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - sctx, err := session.CreateSession(h.Store) - if err != nil { - writeError(w, err) - return - } - defer sctx.Close() - - var start, end time.Time - if req.FormValue("end") != "" { - end, err = time.ParseInLocation(time.RFC3339, req.FormValue("end"), sctx.GetSessionVars().Location()) - if err != nil { - writeError(w, err) - return - } - } else { - end = time.Now() - } - if req.FormValue("start") != "" { - start, err = time.ParseInLocation(time.RFC3339, req.FormValue("start"), sctx.GetSessionVars().Location()) - if err != nil { - writeError(w, err) - return - } - } else { - start = end.Add(-time.Minute * 10) - } - valueTp := req.FormValue("type") - pb, err := executor.NewProfileBuilder(sctx, start, end, valueTp) - if err != nil { - writeError(w, err) - return - } - err = pb.Collect() - if err != nil { - writeError(w, err) - return - } - _, err = w.Write(pb.Build()) - terror.Log(errors.Trace(err)) -} - -// TestHandler is the handler for tests. It's convenient to provide some APIs for integration tests. -type TestHandler struct { - *handler.TikvHandlerTool - gcIsRunning uint32 -} - -// NewTestHandler creates a new TestHandler. -func NewTestHandler(tool *handler.TikvHandlerTool, gcIsRunning uint32) *TestHandler { - return &TestHandler{ - TikvHandlerTool: tool, - gcIsRunning: gcIsRunning, - } -} - -// ServeHTTP handles test related requests. -func (h *TestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - params := mux.Vars(req) - mod := strings.ToLower(params["mod"]) - op := strings.ToLower(params["op"]) - - switch mod { - case "gc": - h.handleGC(op, w, req) - default: - writeError(w, errors.NotSupportedf("module(%s)", mod)) - } -} - -// Supported operations: -// - resolvelock?safepoint={uint64}&physical={bool}: -// - safepoint: resolve all locks whose timestamp is less than the safepoint. -// - physical: whether it uses physical(green GC) mode to scan locks. Default is true. -func (h *TestHandler) handleGC(op string, w http.ResponseWriter, req *http.Request) { - if !atomic.CompareAndSwapUint32(&h.gcIsRunning, 0, 1) { - writeError(w, errors.New("GC is running")) - return - } - defer atomic.StoreUint32(&h.gcIsRunning, 0) - - switch op { - case "resolvelock": - h.handleGCResolveLocks(w, req) - default: - writeError(w, errors.NotSupportedf("operation(%s)", op)) - } -} - -func (h *TestHandler) handleGCResolveLocks(w http.ResponseWriter, req *http.Request) { - s := req.FormValue("safepoint") - safePoint, err := strconv.ParseUint(s, 10, 64) - if err != nil { - writeError(w, errors.Errorf("parse safePoint(%s) failed", s)) - return - } - usePhysical := true - s = req.FormValue("physical") - if s != "" { - usePhysical, err = strconv.ParseBool(s) - if err != nil { - writeError(w, errors.Errorf("parse physical(%s) failed", s)) - return - } - } - - ctx := req.Context() - logutil.Logger(ctx).Info("start resolving locks", zap.Uint64("safePoint", safePoint), zap.Bool("physical", usePhysical)) - physicalUsed, err := gcworker.RunResolveLocks(ctx, h.Store, h.RegionCache.PDClient(), safePoint, "testGCWorker", 3, usePhysical) - if err != nil { - writeError(w, errors.Annotate(err, "resolveLocks failed")) - } else { - writeData(w, map[string]interface{}{ - "physicalUsed": physicalUsed, - }) - } -} - -// ServeHTTP handles request of resigning ddl owner. -func (h DDLHookHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - writeError(w, errors.Errorf("This api only support POST method")) - return - } - - dom, err := session.GetDomain(h.store) - if err != nil { - log.Error("failed to get session domain", zap.Error(err)) - writeError(w, err) - } - - newCallbackFunc, err := ddl.GetCustomizedHook(req.FormValue("ddl_hook")) - if err != nil { - log.Error("failed to get customized hook", zap.Error(err)) - writeError(w, err) - } - callback := newCallbackFunc(dom) - - dom.DDL().SetHook(callback) - writeData(w, "success!") - - ctx := req.Context() - logutil.Logger(ctx).Info("change ddl hook success", zap.String("to_ddl_hook", req.FormValue("ddl_hook"))) -} - -// ServeHTTP handles request of set server labels. -func (LabelHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - writeError(w, errors.Errorf("This api only support POST method")) - return - } - - labels := make(map[string]string) - err := json.NewDecoder(req.Body).Decode(&labels) - if err != nil { - writeError(w, err) - return - } - - if len(labels) > 0 { - cfg := *config.GetGlobalConfig() - // Be careful of data race. The key & value of cfg.Labels must not be changed. - if cfg.Labels != nil { - for k, v := range cfg.Labels { - if _, found := labels[k]; !found { - labels[k] = v - } - } - } - cfg.Labels = labels - config.StoreGlobalConfig(&cfg) - logutil.BgLogger().Info("update server labels", zap.Any("labels", cfg.Labels)) - } - - writeData(w, config.GetGlobalConfig().Labels) -} diff --git a/server/handler/ttlhandler/BUILD.bazel b/server/handler/ttlhandler/BUILD.bazel deleted file mode 100644 index d7ccfbfeae123..0000000000000 --- a/server/handler/ttlhandler/BUILD.bazel +++ /dev/null @@ -1,19 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "ttlhandler", - srcs = ["ttl.go"], - importpath = "github.com/pingcap/tidb/server/handler/ttlhandler", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//server/handler", - "//session", - "//ttl/client", - "//util/logutil", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) diff --git a/server/handler/ttlhandler/ttl.go b/server/handler/ttlhandler/ttl.go deleted file mode 100644 index daf8e7bf058f7..0000000000000 --- a/server/handler/ttlhandler/ttl.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package ttlhandler - -import ( - "net/http" - "strings" - - "github.com/gorilla/mux" - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/server/handler" - "github.com/pingcap/tidb/session" - ttlcient "github.com/pingcap/tidb/ttl/client" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// TTLJobTriggerHandler is used to trigger a TTL job manually -type TTLJobTriggerHandler struct { - store kv.Storage -} - -// NewTTLJobTriggerHandler returns a new TTLJobTriggerHandler -func NewTTLJobTriggerHandler(store kv.Storage) *TTLJobTriggerHandler { - return &TTLJobTriggerHandler{store: store} -} - -// ServeHTTP handles request of triger a ttl job -func (h TTLJobTriggerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if req.Method != http.MethodPost { - handler.WriteError(w, errors.Errorf("This api only support POST method")) - return - } - - params := mux.Vars(req) - dbName := strings.ToLower(params["db"]) - tableName := strings.ToLower(params["table"]) - - ctx := req.Context() - dom, err := session.GetDomain(h.store) - if err != nil { - log.Error("failed to get session domain", zap.Error(err)) - handler.WriteError(w, err) - return - } - - cli := dom.TTLJobManager().GetCommandCli() - resp, err := ttlcient.TriggerNewTTLJob(ctx, cli, dbName, tableName) - if err != nil { - log.Error("failed to trigger new TTL job", zap.Error(err)) - handler.WriteError(w, err) - return - } - handler.WriteData(w, resp) - logutil.Logger(ctx).Info("trigger TTL job manually successfully", - zap.String("dbName", dbName), - zap.String("tableName", tableName), - zap.Any("response", resp)) -} diff --git a/server/handler/util.go b/server/handler/util.go deleted file mode 100644 index f7091fb4d6964..0000000000000 --- a/server/handler/util.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package handler - -import ( - "encoding/json" - "net/http" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" -) - -//revive:disable -const ( - DBName = "db" - HexKey = "hexKey" - IndexName = "index" - Handle = "handle" - RegionID = "regionID" - StartTS = "startTS" - TableName = "table" - TableID = "tableID" - ColumnID = "colID" - ColumnTp = "colTp" - ColumnFlag = "colFlag" - ColumnLen = "colLen" - RowBin = "rowBin" - Snapshot = "snapshot" - FileName = "filename" - DumpPartitionStats = "dumpPartitionStats" - Begin = "begin" - End = "end" -) - -// For extract task handler -const ( - Type = "type" - IsDump = "isDump" - - // For extract plan task handler - IsSkipStats = "isSkipStats" - IsHistoryView = "isHistoryView" -) - -// For query string -const ( - TableIDQuery = "table_id" - Limit = "limit" - JobID = "start_job_id" - Operation = "op" - Seconds = "seconds" -) - -const ( - HeaderContentType = "Content-Type" - ContentTypeJSON = "application/json" -) - -//revive:enable - -// WriteError writes error to response. -func WriteError(w http.ResponseWriter, err error) { - w.WriteHeader(http.StatusBadRequest) - _, err = w.Write([]byte(err.Error())) - terror.Log(errors.Trace(err)) -} - -// WriteData writes data to response. -func WriteData(w http.ResponseWriter, data interface{}) { - js, err := json.MarshalIndent(data, "", " ") - if err != nil { - WriteError(w, err) - return - } - // write response - w.Header().Set(HeaderContentType, ContentTypeJSON) - w.WriteHeader(http.StatusOK) - _, err = w.Write(js) - terror.Log(errors.Trace(err)) -} - -// ExtractTableAndPartitionName extracts table name and partition name from this "table(partition)": -func ExtractTableAndPartitionName(str string) (table, partition string) { - // extract table name and partition name from this "table(partition)": - // A sane person would not let the the table name or partition name contain '('. - start := strings.IndexByte(str, '(') - if start == -1 { - return str, "" - } - end := strings.IndexByte(str, ')') - if end == -1 { - return str, "" - } - return str[:start], str[start+1 : end] -} diff --git a/server/internal/BUILD.bazel b/server/internal/BUILD.bazel deleted file mode 100644 index 739ec0868a779..0000000000000 --- a/server/internal/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "internal", - srcs = ["packetio.go"], - importpath = "github.com/pingcap/tidb/server/internal", - visibility = ["//server:__subpackages__"], - deps = [ - "//parser/mysql", - "//parser/terror", - "//server/err", - "//server/internal/util", - "//server/metrics", - "//sessionctx/variable", - "@com_github_klauspost_compress//zstd", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "internal_test", - timeout = "short", - srcs = ["packetio_test.go"], - embed = [":internal"], - flaky = True, - shard_count = 4, - deps = [ - "//parser/mysql", - "//server/internal/testutil", - "//server/internal/util", - "@com_github_klauspost_compress//zstd", - "@com_github_stretchr_testify//require", - ], -) diff --git a/server/internal/column/BUILD.bazel b/server/internal/column/BUILD.bazel deleted file mode 100644 index 37920bfab35d2..0000000000000 --- a/server/internal/column/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "column", - srcs = [ - "column.go", - "convert.go", - "result_encoder.go", - ], - importpath = "github.com/pingcap/tidb/server/internal/column", - visibility = ["//server:__subpackages__"], - deps = [ - "//parser/ast", - "//parser/charset", - "//parser/mysql", - "//server/err", - "//server/internal/dump", - "//server/internal/util", - "//types", - "//util/chunk", - "//util/hack", - "//util/logutil", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "column_test", - timeout = "short", - srcs = [ - "column_test.go", - "result_encoder_test.go", - ], - embed = [":column"], - flaky = True, - shard_count = 5, - deps = [ - "//parser/charset", - "//parser/mysql", - "//server/internal/util", - "//types", - "//util/chunk", - "//util/mock", - "@com_github_stretchr_testify//require", - ], -) diff --git a/server/internal/column/column.go b/server/internal/column/column.go deleted file mode 100644 index ad0d49b46ac23..0000000000000 --- a/server/internal/column/column.go +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package column - -import ( - "fmt" - "math" - "strconv" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/err" - "github.com/pingcap/tidb/server/internal/dump" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" -) - -const maxColumnNameSize = 256 - -// Info contains information of a column -type Info struct { - DefaultValue any - Schema string - Table string - OrgTable string - Name string - OrgName string - ColumnLength uint32 - Charset uint16 - Flag uint16 - Decimal uint8 - Type uint8 -} - -// Dump dumps Info to bytes. -func (column *Info) Dump(buffer []byte, d *ResultEncoder) []byte { - return column.dump(buffer, d, false) -} - -// DumpWithDefault dumps Info to bytes, including column defaults. This is used for ComFieldList responses. -func (column *Info) DumpWithDefault(buffer []byte, d *ResultEncoder) []byte { - return column.dump(buffer, d, true) -} - -func (column *Info) dump(buffer []byte, d *ResultEncoder, withDefault bool) []byte { - if d == nil { - d = NewResultEncoder(charset.CharsetUTF8MB4) - } - nameDump, orgnameDump := []byte(column.Name), []byte(column.OrgName) - if len(nameDump) > maxColumnNameSize { - nameDump = nameDump[0:maxColumnNameSize] - } - if len(orgnameDump) > maxColumnNameSize { - orgnameDump = orgnameDump[0:maxColumnNameSize] - } - buffer = dump.LengthEncodedString(buffer, []byte("def")) - buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.Schema))) - buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.Table))) - buffer = dump.LengthEncodedString(buffer, d.EncodeMeta([]byte(column.OrgTable))) - buffer = dump.LengthEncodedString(buffer, d.EncodeMeta(nameDump)) - buffer = dump.LengthEncodedString(buffer, d.EncodeMeta(orgnameDump)) - - buffer = append(buffer, 0x0c) - buffer = dump.Uint16(buffer, d.ColumnTypeInfoCharsetID(column)) - buffer = dump.Uint32(buffer, column.ColumnLength) - buffer = append(buffer, dumpType(column.Type)) - buffer = dump.Uint16(buffer, DumpFlag(column.Type, column.Flag)) - buffer = append(buffer, column.Decimal) - buffer = append(buffer, 0, 0) - - if withDefault { - switch column.DefaultValue { - case "CURRENT_TIMESTAMP", "CURRENT_DATE", nil: - buffer = append(buffer, 251) // NULL - default: - defaultValStr := fmt.Sprintf("%v", column.DefaultValue) - buffer = dump.LengthEncodedString(buffer, []byte(defaultValStr)) - } - } - - return buffer -} - -// DumpFlag dumps flag of a column. -func DumpFlag(tp byte, flag uint16) uint16 { - switch tp { - case mysql.TypeSet: - return flag | uint16(mysql.SetFlag) - case mysql.TypeEnum: - return flag | uint16(mysql.EnumFlag) - default: - return flag - } -} - -func dumpType(tp byte) byte { - switch tp { - case mysql.TypeSet, mysql.TypeEnum: - return mysql.TypeString - default: - return tp - } -} - -// DumpTextRow dumps a row to bytes. -func DumpTextRow(buffer []byte, columns []*Info, row chunk.Row, d *ResultEncoder) ([]byte, error) { - if d == nil { - d = NewResultEncoder(charset.CharsetUTF8MB4) - } - tmp := make([]byte, 0, 20) - for i, col := range columns { - if row.IsNull(i) { - buffer = append(buffer, 0xfb) - continue - } - switch col.Type { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong: - tmp = strconv.AppendInt(tmp[:0], row.GetInt64(i), 10) - buffer = dump.LengthEncodedString(buffer, tmp) - case mysql.TypeYear: - year := row.GetInt64(i) - tmp = tmp[:0] - if year == 0 { - tmp = append(tmp, '0', '0', '0', '0') - } else { - tmp = strconv.AppendInt(tmp, year, 10) - } - buffer = dump.LengthEncodedString(buffer, tmp) - case mysql.TypeLonglong: - if mysql.HasUnsignedFlag(uint(columns[i].Flag)) { - tmp = strconv.AppendUint(tmp[:0], row.GetUint64(i), 10) - } else { - tmp = strconv.AppendInt(tmp[:0], row.GetInt64(i), 10) - } - buffer = dump.LengthEncodedString(buffer, tmp) - case mysql.TypeFloat: - prec := -1 - if columns[i].Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec && col.Table == "" { - prec = int(col.Decimal) - } - tmp = util.AppendFormatFloat(tmp[:0], float64(row.GetFloat32(i)), prec, 32) - buffer = dump.LengthEncodedString(buffer, tmp) - case mysql.TypeDouble: - prec := types.UnspecifiedLength - if col.Decimal > 0 && int(col.Decimal) != mysql.NotFixedDec && col.Table == "" { - prec = int(col.Decimal) - } - tmp = util.AppendFormatFloat(tmp[:0], row.GetFloat64(i), prec, 64) - buffer = dump.LengthEncodedString(buffer, tmp) - case mysql.TypeNewDecimal: - buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetMyDecimal(i).String())) - case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBit, - mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: - d.UpdateDataEncoding(col.Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(row.GetBytes(i))) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetTime(i).String())) - case mysql.TypeDuration: - dur := row.GetDuration(i, int(col.Decimal)) - buffer = dump.LengthEncodedString(buffer, hack.Slice(dur.String())) - case mysql.TypeEnum: - d.UpdateDataEncoding(col.Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetEnum(i).String()))) - case mysql.TypeSet: - d.UpdateDataEncoding(col.Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetSet(i).String()))) - case mysql.TypeJSON: - // The collation of JSON type is always binary. - // To compatible with MySQL, here we treat it as utf-8. - d.UpdateDataEncoding(mysql.DefaultCollationID) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetJSON(i).String()))) - default: - return nil, err.ErrInvalidType.GenWithStack("invalid type %v", columns[i].Type) - } - } - return buffer, nil -} - -// DumpBinaryRow dumps a row to bytes. -func DumpBinaryRow(buffer []byte, columns []*Info, row chunk.Row, d *ResultEncoder) ([]byte, error) { - if d == nil { - d = NewResultEncoder(charset.CharsetUTF8MB4) - } - buffer = append(buffer, mysql.OKHeader) - nullBitmapOff := len(buffer) - numBytes4Null := (len(columns) + 7 + 2) / 8 - for i := 0; i < numBytes4Null; i++ { - buffer = append(buffer, 0) - } - for i := range columns { - if row.IsNull(i) { - bytePos := (i + 2) / 8 - bitPos := byte((i + 2) % 8) - buffer[nullBitmapOff+bytePos] |= 1 << bitPos - continue - } - switch columns[i].Type { - case mysql.TypeTiny: - buffer = append(buffer, byte(row.GetInt64(i))) - case mysql.TypeShort, mysql.TypeYear: - buffer = dump.Uint16(buffer, uint16(row.GetInt64(i))) - case mysql.TypeInt24, mysql.TypeLong: - buffer = dump.Uint32(buffer, uint32(row.GetInt64(i))) - case mysql.TypeLonglong: - buffer = dump.Uint64(buffer, row.GetUint64(i)) - case mysql.TypeFloat: - buffer = dump.Uint32(buffer, math.Float32bits(row.GetFloat32(i))) - case mysql.TypeDouble: - buffer = dump.Uint64(buffer, math.Float64bits(row.GetFloat64(i))) - case mysql.TypeNewDecimal: - buffer = dump.LengthEncodedString(buffer, hack.Slice(row.GetMyDecimal(i).String())) - case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBit, - mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: - d.UpdateDataEncoding(columns[i].Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(row.GetBytes(i))) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - buffer = dump.BinaryDateTime(buffer, row.GetTime(i)) - case mysql.TypeDuration: - buffer = append(buffer, dump.BinaryTime(row.GetDuration(i, 0).Duration)...) - case mysql.TypeEnum: - d.UpdateDataEncoding(columns[i].Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetEnum(i).String()))) - case mysql.TypeSet: - d.UpdateDataEncoding(columns[i].Charset) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetSet(i).String()))) - case mysql.TypeJSON: - // The collation of JSON type is always binary. - // To compatible with MySQL, here we treat it as utf-8. - d.UpdateDataEncoding(mysql.DefaultCollationID) - buffer = dump.LengthEncodedString(buffer, d.EncodeData(hack.Slice(row.GetJSON(i).String()))) - default: - return nil, err.ErrInvalidType.GenWithStack("invalid type %v", columns[i].Type) - } - } - return buffer, nil -} diff --git a/server/internal/column/column_test.go b/server/internal/column/column_test.go deleted file mode 100644 index b918b88f140ef..0000000000000 --- a/server/internal/column/column_test.go +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package column - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestDumpColumn(t *testing.T) { - info := Info{ - Schema: "testSchema", - Table: "testTable", - OrgTable: "testOrgTable", - Name: "testName", - OrgName: "testOrgName", - ColumnLength: 1, - Charset: 106, - Flag: 0, - Decimal: 1, - Type: 14, - DefaultValue: []byte{5, 2}, - } - r := info.Dump(nil, nil) - exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x8, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0} - require.Equal(t, exp, r) - - require.Equal(t, uint16(mysql.SetFlag), DumpFlag(mysql.TypeSet, 0)) - require.Equal(t, uint16(mysql.EnumFlag), DumpFlag(mysql.TypeEnum, 0)) - require.Equal(t, uint16(0), DumpFlag(mysql.TypeString, 0)) - - require.Equal(t, mysql.TypeString, dumpType(mysql.TypeSet)) - require.Equal(t, mysql.TypeString, dumpType(mysql.TypeEnum)) - require.Equal(t, mysql.TypeBit, dumpType(mysql.TypeBit)) -} - -func TestDumpColumnWithDefault(t *testing.T) { - info := Info{ - Schema: "testSchema", - Table: "testTable", - OrgTable: "testOrgTable", - Name: "testName", - OrgName: "testOrgName", - ColumnLength: 1, - Charset: 106, - Flag: 0, - Decimal: 1, - Type: 14, - DefaultValue: "test", - } - r := info.DumpWithDefault(nil, nil) - exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x8, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0, 0x4, 0x74, 0x65, 0x73, 0x74} - require.Equal(t, exp, r) - - require.Equal(t, uint16(mysql.SetFlag), DumpFlag(mysql.TypeSet, 0)) - require.Equal(t, uint16(mysql.EnumFlag), DumpFlag(mysql.TypeEnum, 0)) - require.Equal(t, uint16(0), DumpFlag(mysql.TypeString, 0)) - - require.Equal(t, mysql.TypeString, dumpType(mysql.TypeSet)) - require.Equal(t, mysql.TypeString, dumpType(mysql.TypeEnum)) - require.Equal(t, mysql.TypeBit, dumpType(mysql.TypeBit)) -} - -func TestColumnNameLimit(t *testing.T) { - aLongName := make([]byte, 0, 300) - for i := 0; i < 300; i++ { - aLongName = append(aLongName, 'a') - } - info := Info{ - Schema: "testSchema", - Table: "testTable", - OrgTable: "testOrgTable", - Name: string(aLongName), - OrgName: "testOrgName", - ColumnLength: 1, - Charset: 106, - Flag: 0, - Decimal: 1, - Type: 14, - DefaultValue: []byte{5, 2}, - } - r := info.Dump(nil, nil) - exp := []byte{0x3, 0x64, 0x65, 0x66, 0xa, 0x74, 0x65, 0x73, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x9, 0x74, 0x65, 0x73, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xc, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x54, 0x61, 0x62, 0x6c, 0x65, 0xfc, 0x0, 0x1, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0xb, 0x74, 0x65, 0x73, 0x74, 0x4f, 0x72, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0xc, 0x6a, 0x0, 0x1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x1, 0x0, 0x0} - require.Equal(t, exp, r) -} - -func TestDumpTextValue(t *testing.T) { - columns := []*Info{{ - Type: mysql.TypeLonglong, - Decimal: mysql.NotFixedDec, - }} - - dp := NewResultEncoder(charset.CharsetUTF8MB4) - null := types.NewIntDatum(0) - null.SetNull() - bs, err := DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{null}).ToRow(), dp) - require.NoError(t, err) - _, isNull, _, err := util.ParseLengthEncodedBytes(bs) - require.NoError(t, err) - require.True(t, isNull) - - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewIntDatum(10)}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "10", mustDecodeStr(t, bs)) - - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewUintDatum(11)}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "11", mustDecodeStr(t, bs)) - - columns[0].Flag |= uint16(mysql.UnsignedFlag) - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewUintDatum(11)}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "11", mustDecodeStr(t, bs)) - - columns[0].Type = mysql.TypeFloat - columns[0].Decimal = 1 - f32 := types.NewFloat32Datum(1.2) - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f32}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "1.2", mustDecodeStr(t, bs)) - - columns[0].Decimal = 2 - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f32}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "1.20", mustDecodeStr(t, bs)) - - f64 := types.NewFloat64Datum(2.2) - columns[0].Type = mysql.TypeDouble - columns[0].Decimal = 1 - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f64}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "2.2", mustDecodeStr(t, bs)) - - columns[0].Decimal = 2 - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{f64}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "2.20", mustDecodeStr(t, bs)) - - columns[0].Type = mysql.TypeBlob - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewBytesDatum([]byte("foo"))}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "foo", mustDecodeStr(t, bs)) - - columns[0].Type = mysql.TypeVarchar - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{types.NewStringDatum("bar")}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "bar", mustDecodeStr(t, bs)) - - dp = NewResultEncoder("gbk") - columns[0].Type = mysql.TypeVarchar - dt := []types.Datum{types.NewStringDatum("一")} - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums(dt).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, []byte{0xd2, 0xbb}, []byte(mustDecodeStr(t, bs))) - - columns[0].Charset = uint16(mysql.CharsetNameToID("gbk")) - dp = NewResultEncoder("binary") - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums(dt).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, []byte{0xd2, 0xbb}, []byte(mustDecodeStr(t, bs))) - - var d types.Datum - - sc := mock.NewContext().GetSessionVars().StmtCtx - sc.IgnoreZeroInDate = true - losAngelesTz, err := time.LoadLocation("America/Los_Angeles") - require.NoError(t, err) - sc.SetTimeZone(losAngelesTz) - - time, err := types.ParseTime(sc, "2017-01-05 23:59:59.575601", mysql.TypeDatetime, 0, nil) - require.NoError(t, err) - d.SetMysqlTime(time) - columns[0].Type = mysql.TypeDatetime - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "2017-01-06 00:00:00", mustDecodeStr(t, bs)) - - duration, _, err := types.ParseDuration(sc, "11:30:45", 0) - require.NoError(t, err) - d.SetMysqlDuration(duration) - columns[0].Type = mysql.TypeDuration - columns[0].Decimal = 0 - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "11:30:45", mustDecodeStr(t, bs)) - - d.SetMysqlDecimal(types.NewDecFromStringForTest("1.23")) - columns[0].Type = mysql.TypeNewDecimal - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{d}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "1.23", mustDecodeStr(t, bs)) - - year := types.NewIntDatum(0) - columns[0].Type = mysql.TypeYear - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{year}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "0000", mustDecodeStr(t, bs)) - - year.SetInt64(1984) - columns[0].Type = mysql.TypeYear - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{year}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "1984", mustDecodeStr(t, bs)) - - enum := types.NewMysqlEnumDatum(types.Enum{Name: "ename", Value: 0}) - columns[0].Type = mysql.TypeEnum - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{enum}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "ename", mustDecodeStr(t, bs)) - - set := types.Datum{} - set.SetMysqlSet(types.Set{Name: "sname", Value: 0}, mysql.DefaultCollationName) - columns[0].Type = mysql.TypeSet - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{set}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, "sname", mustDecodeStr(t, bs)) - - js := types.Datum{} - binaryJSON, err := types.ParseBinaryJSONFromString(`{"a": 1, "b": 2}`) - require.NoError(t, err) - js.SetMysqlJSON(binaryJSON) - columns[0].Type = mysql.TypeJSON - bs, err = DumpTextRow(nil, columns, chunk.MutRowFromDatums([]types.Datum{js}).ToRow(), dp) - require.NoError(t, err) - require.Equal(t, `{"a": 1, "b": 2}`, mustDecodeStr(t, bs)) -} - -func mustDecodeStr(t *testing.T, b []byte) string { - str, _, _, err := util.ParseLengthEncodedBytes(b) - require.NoError(t, err) - return string(str) -} diff --git a/server/internal/column/convert.go b/server/internal/column/convert.go deleted file mode 100644 index 296b14b4b7576..0000000000000 --- a/server/internal/column/convert.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package column - -import ( - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" -) - -// ConvertColumnInfo converts `*ast.ResultField` to `*Info` -func ConvertColumnInfo(fld *ast.ResultField) (ci *Info) { - ci = &Info{ - Name: fld.ColumnAsName.O, - OrgName: fld.Column.Name.O, - Table: fld.TableAsName.O, - Schema: fld.DBName.O, - Flag: uint16(fld.Column.GetFlag()), - Charset: uint16(mysql.CharsetNameToID(fld.Column.GetCharset())), - Type: fld.Column.GetType(), - DefaultValue: fld.Column.GetDefaultValue(), - } - - if fld.EmptyOrgName { - ci.OrgName = "" - } - if fld.Table != nil { - ci.OrgTable = fld.Table.Name.O - } - if fld.Column.GetFlen() != types.UnspecifiedLength { - ci.ColumnLength = uint32(fld.Column.GetFlen()) - } - if fld.Column.GetType() == mysql.TypeNewDecimal { - // Consider the negative sign. - ci.ColumnLength++ - if fld.Column.GetDecimal() > types.DefaultFsp { - // Consider the decimal point. - ci.ColumnLength++ - } - } else if types.IsString(fld.Column.GetType()) || - fld.Column.GetType() == mysql.TypeEnum || fld.Column.GetType() == mysql.TypeSet { // issue #18870 - // Fix issue #4540. - // The flen is a hint, not a precise value, so most client will not use the value. - // But we found in rare MySQL client, like Navicat for MySQL(version before 12) will truncate - // the `show create table` result. To fix this case, we must use a large enough flen to prevent - // the truncation, in MySQL, it will multiply bytes length by a multiple based on character set. - // For examples: - // * latin, the multiple is 1 - // * gb2312, the multiple is 2 - // * Utf-8, the multiple is 3 - // * utf8mb4, the multiple is 4 - // We used to check non-string types to avoid the truncation problem in some MySQL - // client such as Navicat. Now we only allow string type enter this branch. - charsetDesc, err := charset.GetCharsetInfo(fld.Column.GetCharset()) - if err != nil { - ci.ColumnLength *= 4 - } else { - ci.ColumnLength *= uint32(charsetDesc.Maxlen) - } - } - - if fld.Column.GetDecimal() == types.UnspecifiedLength { - if fld.Column.GetType() == mysql.TypeDuration { - ci.Decimal = uint8(types.DefaultFsp) - } else { - ci.Decimal = mysql.NotFixedDec - } - } else { - ci.Decimal = uint8(fld.Column.GetDecimal()) - } - - // Keep things compatible for old clients. - // Refer to mysql-server/sql/protocol.cc send_result_set_metadata() - if ci.Type == mysql.TypeVarchar { - ci.Type = mysql.TypeVarString - } - return -} diff --git a/server/internal/dump/BUILD.bazel b/server/internal/dump/BUILD.bazel deleted file mode 100644 index 8fa53ba805945..0000000000000 --- a/server/internal/dump/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "dump", - srcs = ["dump.go"], - importpath = "github.com/pingcap/tidb/server/internal/dump", - visibility = ["//server:__subpackages__"], - deps = [ - "//parser/mysql", - "//types", - ], -) - -go_test( - name = "dump_test", - timeout = "short", - srcs = ["dump_test.go"], - embed = [":dump"], - flaky = True, - shard_count = 3, - deps = [ - "//sessionctx/stmtctx", - "//types", - "@com_github_stretchr_testify//require", - ], -) diff --git a/server/internal/dump/dump.go b/server/internal/dump/dump.go deleted file mode 100644 index 1a94c973feffa..0000000000000 --- a/server/internal/dump/dump.go +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -// The MIT License (MIT) -// -// Copyright (c) 2014 wandoulabs -// Copyright (c) 2014 siddontang -// -// 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. - -package dump - -import ( - "encoding/binary" - "time" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" -) - -// LengthEncodedString dumps a string as length encoded byte slice. -func LengthEncodedString(buffer []byte, bytes []byte) []byte { - buffer = LengthEncodedInt(buffer, uint64(len(bytes))) - buffer = append(buffer, bytes...) - return buffer -} - -// LengthEncodedInt dumps an integer as length encoded byte slice. -func LengthEncodedInt(buffer []byte, n uint64) []byte { - switch { - case n <= 250: - return append(buffer, byte(n)) - - case n <= 0xffff: - return append(buffer, 0xfc, byte(n), byte(n>>8)) - - case n <= 0xffffff: - return append(buffer, 0xfd, byte(n), byte(n>>8), byte(n>>16)) - - case n <= 0xffffffffffffffff: - return append(buffer, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24), - byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56)) - } - - return buffer -} - -// Uint16 dumps an uint16 as byte slice. -func Uint16(buffer []byte, n uint16) []byte { - buffer = append(buffer, byte(n)) - buffer = append(buffer, byte(n>>8)) - return buffer -} - -// Uint32 dumps an uint32 as byte slice. -func Uint32(buffer []byte, n uint32) []byte { - buffer = append(buffer, byte(n)) - buffer = append(buffer, byte(n>>8)) - buffer = append(buffer, byte(n>>16)) - buffer = append(buffer, byte(n>>24)) - return buffer -} - -// Uint64 dumps an uint64 as byte slice. -func Uint64(buffer []byte, n uint64) []byte { - buffer = append(buffer, byte(n)) - buffer = append(buffer, byte(n>>8)) - buffer = append(buffer, byte(n>>16)) - buffer = append(buffer, byte(n>>24)) - buffer = append(buffer, byte(n>>32)) - buffer = append(buffer, byte(n>>40)) - buffer = append(buffer, byte(n>>48)) - buffer = append(buffer, byte(n>>56)) - return buffer -} - -// BinaryTime dumps a time as binary byte slice. -func BinaryTime(dur time.Duration) (data []byte) { - if dur == 0 { - return []byte{0} - } - data = make([]byte, 13) - data[0] = 12 - if dur < 0 { - data[1] = 1 - dur = -dur - } - days := dur / (24 * time.Hour) - dur -= days * 24 * time.Hour //nolint:durationcheck - data[2] = byte(days) - hours := dur / time.Hour - dur -= hours * time.Hour //nolint:durationcheck - data[6] = byte(hours) - minutes := dur / time.Minute - dur -= minutes * time.Minute //nolint:durationcheck - data[7] = byte(minutes) - seconds := dur / time.Second - dur -= seconds * time.Second //nolint:durationcheck - data[8] = byte(seconds) - if dur == 0 { - data[0] = 8 - return data[:9] - } - binary.LittleEndian.PutUint32(data[9:13], uint32(dur/time.Microsecond)) - return -} - -// BinaryDateTime dumps a datetime as binary byte slice. -func BinaryDateTime(data []byte, t types.Time) []byte { - year, mon, day := t.Year(), t.Month(), t.Day() - switch t.Type() { - case mysql.TypeTimestamp, mysql.TypeDatetime: - if t.IsZero() { - // All zero. - data = append(data, 0) - } else if t.Microsecond() != 0 { - // Has micro seconds. - data = append(data, 11) - data = Uint16(data, uint16(year)) - data = append(data, byte(mon), byte(day), byte(t.Hour()), byte(t.Minute()), byte(t.Second())) - data = Uint32(data, uint32(t.Microsecond())) - } else if t.Hour() != 0 || t.Minute() != 0 || t.Second() != 0 { - // Has HH:MM:SS - data = append(data, 7) - data = Uint16(data, uint16(year)) - data = append(data, byte(mon), byte(day), byte(t.Hour()), byte(t.Minute()), byte(t.Second())) - } else { - // Only YY:MM:DD - data = append(data, 4) - data = Uint16(data, uint16(year)) - data = append(data, byte(mon), byte(day)) - } - case mysql.TypeDate: - if t.IsZero() { - data = append(data, 0) - } else { - data = append(data, 4) - data = Uint16(data, uint16(year)) // year - data = append(data, byte(mon), byte(day)) - } - } - return data -} diff --git a/server/internal/dump/dump_test.go b/server/internal/dump/dump_test.go deleted file mode 100644 index 37d1e4e6909aa..0000000000000 --- a/server/internal/dump/dump_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package dump - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestDumpBinaryTime(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - parsedTime, err := types.ParseTimestamp(sc, "0000-00-00 00:00:00.000000") - require.NoError(t, err) - d := BinaryDateTime(nil, parsedTime) - require.Equal(t, []byte{0}, d) - - parsedTime, err = types.ParseTimestamp(stmtctx.NewStmtCtxWithTimeZone(time.Local), "1991-05-01 01:01:01.100001") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - // 199 & 7 composed to uint16 1991 (litter-endian) - // 160 & 134 & 1 & 0 composed to uint32 1000001 (litter-endian) - require.Equal(t, []byte{11, 199, 7, 5, 1, 1, 1, 1, 161, 134, 1, 0}, d) - - parsedTime, err = types.ParseDatetime(sc, "0000-00-00 00:00:00.000000") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - require.Equal(t, []byte{0}, d) - - parsedTime, err = types.ParseDatetime(sc, "1993-07-13 01:01:01.000000") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - // 201 & 7 composed to uint16 1993 (litter-endian) - require.Equal(t, []byte{7, 201, 7, 7, 13, 1, 1, 1}, d) - - parsedTime, err = types.ParseDate(sc, "0000-00-00") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - require.Equal(t, []byte{0}, d) - parsedTime, err = types.ParseDate(sc, "1992-06-01") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - // 200 & 7 composed to uint16 1992 (litter-endian) - require.Equal(t, []byte{4, 200, 7, 6, 1}, d) - - parsedTime, err = types.ParseDate(sc, "0000-00-00") - require.NoError(t, err) - d = BinaryDateTime(nil, parsedTime) - require.Equal(t, []byte{0}, d) - - myDuration, _, err := types.ParseDuration(sc, "0000-00-00 00:00:00.000000", 6) - require.NoError(t, err) - d = BinaryTime(myDuration.Duration) - require.Equal(t, []byte{0}, d) - - d = BinaryTime(0) - require.Equal(t, []byte{0}, d) - - d = BinaryTime(-1) - require.Equal(t, []byte{12, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, d) - - d = BinaryTime(time.Nanosecond + 86400*1000*time.Microsecond) - require.Equal(t, []byte{12, 0, 0, 0, 0, 0, 0, 1, 26, 128, 26, 6, 0}, d) -} - -func TestDumpLengthEncodedInt(t *testing.T) { - testCases := []struct { - num uint64 - buffer []byte - }{ - { - uint64(0), - []byte{0x00}, - }, - { - uint64(513), - []byte{'\xfc', '\x01', '\x02'}, - }, - { - uint64(197121), - []byte{'\xfd', '\x01', '\x02', '\x03'}, - }, - { - uint64(578437695752307201), - []byte{'\xfe', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08'}, - }, - } - for _, tc := range testCases { - b := LengthEncodedInt(nil, tc.num) - require.Equal(t, tc.buffer, b) - } -} - -func TestDumpUint(t *testing.T) { - testCases := []uint64{ - 0, - 1, - 1<<64 - 1, - } - parseUint64 := func(b []byte) uint64 { - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | - uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | - uint64(b[6])<<48 | uint64(b[7])<<56 - } - for _, tc := range testCases { - b := Uint64(nil, tc) - require.Len(t, b, 8) - require.Equal(t, tc, parseUint64(b)) - } -} diff --git a/server/internal/handshake/BUILD.bazel b/server/internal/handshake/BUILD.bazel deleted file mode 100644 index 88ad097f22e15..0000000000000 --- a/server/internal/handshake/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "handshake", - srcs = ["handshake.go"], - importpath = "github.com/pingcap/tidb/server/internal/handshake", - visibility = ["//server:__subpackages__"], - deps = ["@com_github_klauspost_compress//zstd"], -) diff --git a/server/internal/parse/BUILD.bazel b/server/internal/parse/BUILD.bazel deleted file mode 100644 index b4cefaa8c2724..0000000000000 --- a/server/internal/parse/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "parse", - srcs = ["parse.go"], - importpath = "github.com/pingcap/tidb/server/internal/parse", - visibility = ["//server:__subpackages__"], - deps = [ - "//errno", - "//expression", - "//parser/charset", - "//parser/mysql", - "//server/internal/handshake", - "//server/internal/util", - "//sessionctx/stmtctx", - "//types", - "//util/dbterror", - "//util/hack", - "//util/logutil", - "@com_github_klauspost_compress//zstd", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "parse_test", - timeout = "short", - srcs = [ - "handshake_test.go", - "parse_test.go", - ], - embed = [":parse"], - flaky = True, - shard_count = 4, - deps = [ - "//expression", - "//parser/mysql", - "//parser/terror", - "//server/internal/handshake", - "//server/internal/util", - "//sessionctx/stmtctx", - "//types", - "@com_github_stretchr_testify//require", - ], -) diff --git a/server/internal/resultset/BUILD.bazel b/server/internal/resultset/BUILD.bazel deleted file mode 100644 index cbce362df87f0..0000000000000 --- a/server/internal/resultset/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "resultset", - srcs = [ - "cursor.go", - "resultset.go", - ], - importpath = "github.com/pingcap/tidb/server/internal/resultset", - visibility = ["//server:__subpackages__"], - deps = [ - "//planner/core", - "//server/internal/column", - "//types", - "//util/chunk", - "//util/sqlexec", - ], -) diff --git a/server/internal/testserverclient/BUILD.bazel b/server/internal/testserverclient/BUILD.bazel deleted file mode 100644 index ee478603c645a..0000000000000 --- a/server/internal/testserverclient/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testserverclient", - srcs = ["server_client.go"], - importpath = "github.com/pingcap/tidb/server/internal/testserverclient", - visibility = ["//server:__subpackages__"], - deps = [ - "//errno", - "//kv", - "//parser/mysql", - "//server", - "//testkit", - "//testkit/testenv", - "//util/versioninfo", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@org_uber_go_zap//:zap", - ], -) diff --git a/server/internal/testutil/BUILD.bazel b/server/internal/testutil/BUILD.bazel deleted file mode 100644 index e9095453c9064..0000000000000 --- a/server/internal/testutil/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testutil", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/server/internal/testutil", - visibility = ["//server:__subpackages__"], -) diff --git a/server/internal/util/BUILD.bazel b/server/internal/util/BUILD.bazel deleted file mode 100644 index 72372fd79eeb1..0000000000000 --- a/server/internal/util/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "util", - srcs = [ - "buffered_read_conn.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/server/internal/util", - visibility = ["//server:__subpackages__"], - deps = [ - "//config", - "//parser/charset", - ], -) - -go_test( - name = "util_test", - timeout = "short", - srcs = ["util_test.go"], - embed = [":util"], - flaky = True, - shard_count = 4, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/server/internal/util/util.go b/server/internal/util/util.go deleted file mode 100644 index 37d75d54a6dbc..0000000000000 --- a/server/internal/util/util.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this file, -// You can obtain one at http://mozilla.org/MPL/2.0/. - -// The MIT License (MIT) -// -// Copyright (c) 2014 wandoulabs -// Copyright (c) 2014 siddontang -// -// 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. - -package util - -import ( - "bytes" - "io" - "math" - "net/http" - "strconv" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/charset" -) - -// ParseNullTermString parses a null terminated string. -func ParseNullTermString(b []byte) (str []byte, remain []byte) { - off := bytes.IndexByte(b, 0) - if off == -1 { - return nil, b - } - return b[:off], b[off+1:] -} - -// ParseLengthEncodedInt parses a length encoded integer. -func ParseLengthEncodedInt(b []byte) (num uint64, isNull bool, n int) { - switch b[0] { - // 251: NULL - case 0xfb: - n = 1 - isNull = true - return - - // 252: value of following 2 - case 0xfc: - num = uint64(b[1]) | uint64(b[2])<<8 - n = 3 - return - - // 253: value of following 3 - case 0xfd: - num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 - n = 4 - return - - // 254: value of following 8 - case 0xfe: - num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | - uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | - uint64(b[7])<<48 | uint64(b[8])<<56 - n = 9 - return - } - - // https://dev.mysql.com/doc/internals/en/integer.html#length-encoded-integer: If the first byte of a packet is a length-encoded integer and its byte value is 0xfe, you must check the length of the packet to verify that it has enough space for a 8-byte integer. - // TODO: 0xff is undefined - - // 0-250: value of first byte - num = uint64(b[0]) - n = 1 - return -} - -// ParseLengthEncodedBytes parses a length encoded byte slice. -func ParseLengthEncodedBytes(b []byte) ([]byte, bool, int, error) { - // Get length - num, isNull, n := ParseLengthEncodedInt(b) - if num < 1 { - return nil, isNull, n, nil - } - - n += int(num) - - // Check data length - if len(b) >= n { - return b[n-int(num) : n], false, n, nil - } - - return nil, false, n, io.EOF -} - -// InputDecoder is used to decode input. -type InputDecoder struct { - encoding charset.Encoding -} - -// NewInputDecoder creates a new InputDecoder. -func NewInputDecoder(chs string) *InputDecoder { - return &InputDecoder{ - encoding: charset.FindEncodingTakeUTF8AsNoop(chs), - } -} - -// DecodeInput decodes input. -func (i *InputDecoder) DecodeInput(src []byte) []byte { - result, err := i.encoding.Transform(nil, src, charset.OpDecode) - if err != nil { - return src - } - return result -} - -// LengthEncodedIntSize returns the size of length encoded integer. -func LengthEncodedIntSize(n uint64) int { - switch { - case n <= 250: - return 1 - - case n <= 0xffff: - return 3 - - case n <= 0xffffff: - return 4 - } - - return 9 -} - -const ( - expFormatBig = 1e15 - expFormatSmall = 1e-15 - defaultMySQLPrec = 5 -) - -// AppendFormatFloat appends a float64 to dst in MySQL format. -func AppendFormatFloat(in []byte, fVal float64, prec, bitSize int) []byte { - absVal := math.Abs(fVal) - if absVal > math.MaxFloat64 || math.IsNaN(absVal) { - return []byte{'0'} - } - isEFormat := false - if bitSize == 32 { - isEFormat = float32(absVal) >= expFormatBig || (float32(absVal) != 0 && float32(absVal) < expFormatSmall) - } else { - isEFormat = absVal >= expFormatBig || (absVal != 0 && absVal < expFormatSmall) - } - var out []byte - if isEFormat { - if bitSize == 32 { - prec = defaultMySQLPrec - } - out = strconv.AppendFloat(in, fVal, 'e', prec, bitSize) - valStr := out[len(in):] - // remove the '+' from the string for compatibility. - plusPos := bytes.IndexByte(valStr, '+') - if plusPos > 0 { - plusPosInOut := len(in) + plusPos - out = append(out[:plusPosInOut], out[plusPosInOut+1:]...) - } - // remove extra '0' - ePos := bytes.IndexByte(valStr, 'e') - pointPos := bytes.IndexByte(valStr, '.') - ePosInOut := len(in) + ePos - pointPosInOut := len(in) + pointPos - validPos := ePosInOut - for i := ePosInOut - 1; i >= pointPosInOut; i-- { - if !(out[i] == '0' || out[i] == '.') { - break - } - validPos = i - } - out = append(out[:validPos], out[ePosInOut:]...) - } else { - out = strconv.AppendFloat(in, fVal, 'f', prec, bitSize) - } - return out -} - -// CorsHandler adds Cors Header if `cors` config is set. -type CorsHandler struct { - handler http.Handler - cfg *config.Config -} - -// NewCorsHandler creates a new CorsHandler. -func NewCorsHandler(handler http.Handler, cfg *config.Config) http.Handler { - return CorsHandler{handler: handler, cfg: cfg} -} - -// ServeHTTP implements http.Handler interface. -func (h CorsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if h.cfg.Cors != "" { - w.Header().Set("Access-Control-Allow-Origin", h.cfg.Cors) - w.Header().Set("Access-Control-Allow-Methods", "GET") - } - h.handler.ServeHTTP(w, req) -} - -// NewTestConfig creates a new config for test. -func NewTestConfig() *config.Config { - cfg := config.NewConfig() - cfg.Host = "127.0.0.1" - cfg.Status.StatusHost = "127.0.0.1" - cfg.Security.AutoTLS = false - cfg.Socket = "" - return cfg -} diff --git a/server/main_test.go b/server/main_test.go deleted file mode 100644 index 240c32270c586..0000000000000 --- a/server/main_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package server - -import ( - "fmt" - "os" - "reflect" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper, 1) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - RunInGoTest = true // flag for NewServer to known it is running in test environment - // Enable TopSQL for all test, and check the resource tag for each RPC request. - // This is used to detect which codes are not tracked by TopSQL. - topsqlstate.EnableTopSQL() - unistore.CheckResourceTagForTopSQLInGoTest = true - - // AsyncCommit will make DDL wait 2.5s before changing to the next state. - // Set schema lease to avoid it from making CI slow. - session.SetSchemaLease(0) - - tikv.EnableFailpoints() - - metrics.RegisterMetrics() - - // sanity check: the global config should not be changed by other pkg init function. - // see also https://github.com/pingcap/tidb/issues/22162 - defaultConfig := config.NewConfig() - globalConfig := config.GetGlobalConfig() - if !reflect.DeepEqual(defaultConfig, globalConfig) { - _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") - _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) - } - testDataMap.LoadTestSuiteData("testdata", "optimizer_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("time.Sleep"), - goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/server.NewServer.func1"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/server/metrics/BUILD.bazel b/server/metrics/BUILD.bazel deleted file mode 100644 index 7a3636ec5449a..0000000000000 --- a/server/metrics/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/server/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//parser/mysql", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/server/metrics/metrics.go b/server/metrics/metrics.go deleted file mode 100644 index 74f327b4ca28e..0000000000000 --- a/server/metrics/metrics.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/mysql" - "github.com/prometheus/client_golang/prometheus" -) - -// server metrics vars -var ( - QueryTotalCountOk []prometheus.Counter - QueryTotalCountErr []prometheus.Counter - - DisconnectNormal prometheus.Counter - DisconnectByClientWithError prometheus.Counter - DisconnectErrorUndetermined prometheus.Counter - - ConnIdleDurationHistogramNotInTxn prometheus.Observer - ConnIdleDurationHistogramInTxn prometheus.Observer - - AffectedRowsCounterInsert prometheus.Counter - AffectedRowsCounterUpdate prometheus.Counter - AffectedRowsCounterDelete prometheus.Counter - AffectedRowsCounterReplace prometheus.Counter - - ReadPacketBytes prometheus.Counter - WritePacketBytes prometheus.Counter -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init server metrics vars. -func InitMetricsVars() { - QueryTotalCountOk = []prometheus.Counter{ - mysql.ComSleep: metrics.QueryTotalCounter.WithLabelValues("Sleep", "OK"), - mysql.ComQuit: metrics.QueryTotalCounter.WithLabelValues("Quit", "OK"), - mysql.ComInitDB: metrics.QueryTotalCounter.WithLabelValues("InitDB", "OK"), - mysql.ComQuery: metrics.QueryTotalCounter.WithLabelValues("Query", "OK"), - mysql.ComPing: metrics.QueryTotalCounter.WithLabelValues("Ping", "OK"), - mysql.ComFieldList: metrics.QueryTotalCounter.WithLabelValues("FieldList", "OK"), - mysql.ComStmtPrepare: metrics.QueryTotalCounter.WithLabelValues("StmtPrepare", "OK"), - mysql.ComStmtExecute: metrics.QueryTotalCounter.WithLabelValues("StmtExecute", "OK"), - mysql.ComStmtFetch: metrics.QueryTotalCounter.WithLabelValues("StmtFetch", "OK"), - mysql.ComStmtClose: metrics.QueryTotalCounter.WithLabelValues("StmtClose", "OK"), - mysql.ComStmtSendLongData: metrics.QueryTotalCounter.WithLabelValues("StmtSendLongData", "OK"), - mysql.ComStmtReset: metrics.QueryTotalCounter.WithLabelValues("StmtReset", "OK"), - mysql.ComSetOption: metrics.QueryTotalCounter.WithLabelValues("SetOption", "OK"), - } - QueryTotalCountErr = []prometheus.Counter{ - mysql.ComSleep: metrics.QueryTotalCounter.WithLabelValues("Sleep", "Error"), - mysql.ComQuit: metrics.QueryTotalCounter.WithLabelValues("Quit", "Error"), - mysql.ComInitDB: metrics.QueryTotalCounter.WithLabelValues("InitDB", "Error"), - mysql.ComQuery: metrics.QueryTotalCounter.WithLabelValues("Query", "Error"), - mysql.ComPing: metrics.QueryTotalCounter.WithLabelValues("Ping", "Error"), - mysql.ComFieldList: metrics.QueryTotalCounter.WithLabelValues("FieldList", "Error"), - mysql.ComStmtPrepare: metrics.QueryTotalCounter.WithLabelValues("StmtPrepare", "Error"), - mysql.ComStmtExecute: metrics.QueryTotalCounter.WithLabelValues("StmtExecute", "Error"), - mysql.ComStmtFetch: metrics.QueryTotalCounter.WithLabelValues("StmtFetch", "Error"), - mysql.ComStmtClose: metrics.QueryTotalCounter.WithLabelValues("StmtClose", "Error"), - mysql.ComStmtSendLongData: metrics.QueryTotalCounter.WithLabelValues("StmtSendLongData", "Error"), - mysql.ComStmtReset: metrics.QueryTotalCounter.WithLabelValues("StmtReset", "Error"), - mysql.ComSetOption: metrics.QueryTotalCounter.WithLabelValues("SetOption", "Error"), - } - - DisconnectNormal = metrics.DisconnectionCounter.WithLabelValues(metrics.LblOK) - DisconnectByClientWithError = metrics.DisconnectionCounter.WithLabelValues(metrics.LblError) - DisconnectErrorUndetermined = metrics.DisconnectionCounter.WithLabelValues("undetermined") - - ConnIdleDurationHistogramNotInTxn = metrics.ConnIdleDurationHistogram.WithLabelValues("0") - ConnIdleDurationHistogramInTxn = metrics.ConnIdleDurationHistogram.WithLabelValues("1") - - AffectedRowsCounterInsert = metrics.AffectedRowsCounter.WithLabelValues("Insert") - AffectedRowsCounterUpdate = metrics.AffectedRowsCounter.WithLabelValues("Update") - AffectedRowsCounterDelete = metrics.AffectedRowsCounter.WithLabelValues("Delete") - AffectedRowsCounterReplace = metrics.AffectedRowsCounter.WithLabelValues("Replace") - - ReadPacketBytes = metrics.PacketIOCounter.WithLabelValues("read") - WritePacketBytes = metrics.PacketIOCounter.WithLabelValues("write") -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 2d85a3717f7ec..0000000000000 --- a/server/server.go +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// The MIT License (MIT) -// -// Copyright (c) 2014 wandoulabs -// Copyright (c) 2014 siddontang -// -// 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. - -package server - -import ( - "context" - "crypto/tls" - "fmt" - "io" - "net" - "net/http" //nolint:goimports - _ "net/http/pprof" // #nosec G108 for pprof - "os" - "os/user" - "reflect" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/blacktear23/go-proxyprotocol" - "github.com/pingcap/errors" - autoid "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege/privileges" - servererr "github.com/pingcap/tidb/server/err" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/fastrand" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sys/linux" - "github.com/pingcap/tidb/util/timeutil" - uatomic "go.uber.org/atomic" - "go.uber.org/zap" - "google.golang.org/grpc" -) - -var ( - serverPID int - osUser string - osVersion string - // RunInGoTest represents whether we are run code in test. - RunInGoTest bool -) - -func init() { - serverPID = os.Getpid() - currentUser, err := user.Current() - if err != nil { - osUser = "" - } else { - osUser = currentUser.Name - } - osVersion, err = linux.OSVersion() - if err != nil { - osVersion = "" - } -} - -// DefaultCapability is the capability of the server when it is created using the default configuration. -// When server is configured with SSL, the server will have extra capabilities compared to DefaultCapability. -const defaultCapability = mysql.ClientLongPassword | mysql.ClientLongFlag | - mysql.ClientConnectWithDB | mysql.ClientProtocol41 | - mysql.ClientTransactions | mysql.ClientSecureConnection | mysql.ClientFoundRows | - mysql.ClientMultiStatements | mysql.ClientMultiResults | mysql.ClientLocalFiles | - mysql.ClientConnectAtts | mysql.ClientPluginAuth | mysql.ClientInteractive | - mysql.ClientDeprecateEOF | mysql.ClientCompress | mysql.ClientZstdCompressionAlgorithm - -// Server is the MySQL protocol server -type Server struct { - cfg *config.Config - tlsConfig unsafe.Pointer // *tls.Config - driver IDriver - listener net.Listener - socket net.Listener - concurrentLimiter *TokenLimiter - - rwlock sync.RWMutex - clients map[uint64]*clientConn - - capability uint32 - dom *domain.Domain - - statusAddr string - statusListener net.Listener - statusServer *http.Server - grpcServer *grpc.Server - inShutdownMode *uatomic.Bool - health *uatomic.Bool - - sessionMapMutex sync.Mutex - internalSessions map[interface{}]struct{} - autoIDService *autoid.Service - authTokenCancelFunc context.CancelFunc - wg sync.WaitGroup - printMDLLogTime time.Time -} - -// NewTestServer creates a new Server for test. -func NewTestServer(cfg *config.Config) *Server { - return &Server{ - cfg: cfg, - } -} - -// Socket returns the server's socket file. -func (s *Server) Socket() net.Listener { - return s.socket -} - -// Listener returns the server's listener. -func (s *Server) Listener() net.Listener { - return s.listener -} - -// ListenAddr returns the server's listener's network address. -func (s *Server) ListenAddr() net.Addr { - return s.listener.Addr() -} - -// StatusListenerAddr returns the server's status listener's network address. -func (s *Server) StatusListenerAddr() net.Addr { - return s.statusListener.Addr() -} - -// BitwiseXorCapability gets the capability of the server. -func (s *Server) BitwiseXorCapability(capability uint32) { - s.capability ^= capability -} - -// BitwiseOrAssignCapability adds the capability to the server. -func (s *Server) BitwiseOrAssignCapability(capability uint32) { - s.capability |= capability -} - -// GetStatusServerAddr gets statusServer address for MppCoordinatorManager usage -func (s *Server) GetStatusServerAddr() (on bool, addr string) { - if !s.cfg.Status.ReportStatus { - return false, "" - } - if strings.Contains(s.statusAddr, config.DefStatusHost) { - if len(s.cfg.AdvertiseAddress) != 0 { - return true, strings.ReplaceAll(s.statusAddr, config.DefStatusHost, s.cfg.AdvertiseAddress) - } - return false, "" - } - return true, s.statusAddr -} - -// ConnectionCount gets current connection count. -func (s *Server) ConnectionCount() int { - s.rwlock.RLock() - cnt := len(s.clients) - s.rwlock.RUnlock() - return cnt -} - -func (s *Server) getToken() *Token { - start := time.Now() - tok := s.concurrentLimiter.Get() - metrics.TokenGauge.Inc() - // Note that data smaller than one microsecond is ignored, because that case can be viewed as non-block. - metrics.GetTokenDurationHistogram.Observe(float64(time.Since(start).Nanoseconds() / 1e3)) - return tok -} - -func (s *Server) releaseToken(token *Token) { - s.concurrentLimiter.Put(token) - metrics.TokenGauge.Dec() -} - -// SetDomain use to set the server domain. -func (s *Server) SetDomain(dom *domain.Domain) { - s.dom = dom -} - -// newConn creates a new *clientConn from a net.Conn. -// It allocates a connection ID and random salt data for authentication. -func (s *Server) newConn(conn net.Conn) *clientConn { - cc := newClientConn(s) - if tcpConn, ok := conn.(*net.TCPConn); ok { - if err := tcpConn.SetKeepAlive(s.cfg.Performance.TCPKeepAlive); err != nil { - logutil.BgLogger().Error("failed to set tcp keep alive option", zap.Error(err)) - } - if err := tcpConn.SetNoDelay(s.cfg.Performance.TCPNoDelay); err != nil { - logutil.BgLogger().Error("failed to set tcp no delay option", zap.Error(err)) - } - } - cc.setConn(conn) - cc.salt = fastrand.Buf(20) - return cc -} - -// NewServer creates a new Server. -func NewServer(cfg *config.Config, driver IDriver) (*Server, error) { - s := &Server{ - cfg: cfg, - driver: driver, - concurrentLimiter: NewTokenLimiter(cfg.TokenLimit), - clients: make(map[uint64]*clientConn), - internalSessions: make(map[interface{}]struct{}, 100), - health: uatomic.NewBool(true), - inShutdownMode: uatomic.NewBool(false), - printMDLLogTime: time.Now(), - } - s.capability = defaultCapability - setTxnScope() - setSystemTimeZoneVariable() - - tlsConfig, autoReload, err := util.LoadTLSCertificates( - s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert, - s.cfg.Security.AutoTLS, s.cfg.Security.RSAKeySize) - - // LoadTLSCertificates will auto generate certificates if autoTLS is enabled. - // It only returns an error if certificates are specified and invalid. - // In which case, we should halt server startup as a misconfiguration could - // lead to a connection downgrade. - if err != nil { - return nil, errors.Trace(err) - } - - // Automatically reload auto-generated certificates. - // The certificates are re-created every 30 days and are valid for 90 days. - if autoReload { - go func() { - for range time.Tick(time.Hour * 24 * 30) { // 30 days - logutil.BgLogger().Info("Rotating automatically created TLS Certificates") - tlsConfig, _, err = util.LoadTLSCertificates( - s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert, - s.cfg.Security.AutoTLS, s.cfg.Security.RSAKeySize) - if err != nil { - logutil.BgLogger().Warn("TLS Certificate rotation failed", zap.Error(err)) - } - atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(tlsConfig)) - } - }() - } - - if tlsConfig != nil { - setSSLVariable(s.cfg.Security.SSLCA, s.cfg.Security.SSLKey, s.cfg.Security.SSLCert) - atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(tlsConfig)) - logutil.BgLogger().Info("mysql protocol server secure connection is enabled", - zap.Bool("client verification enabled", len(variable.GetSysVar("ssl_ca").Value) > 0)) - } - if s.tlsConfig != nil { - s.capability |= mysql.ClientSSL - } - - if s.cfg.Host != "" && (s.cfg.Port != 0 || RunInGoTest) { - addr := net.JoinHostPort(s.cfg.Host, strconv.Itoa(int(s.cfg.Port))) - tcpProto := "tcp" - if s.cfg.EnableTCP4Only { - tcpProto = "tcp4" - } - if s.listener, err = net.Listen(tcpProto, addr); err != nil { - return nil, errors.Trace(err) - } - logutil.BgLogger().Info("server is running MySQL protocol", zap.String("addr", addr)) - if RunInGoTest && s.cfg.Port == 0 { - s.cfg.Port = uint(s.listener.Addr().(*net.TCPAddr).Port) - } - } - - if s.cfg.Socket != "" { - if err := cleanupStaleSocket(s.cfg.Socket); err != nil { - return nil, errors.Trace(err) - } - - if s.socket, err = net.Listen("unix", s.cfg.Socket); err != nil { - return nil, errors.Trace(err) - } - logutil.BgLogger().Info("server is running MySQL protocol", zap.String("socket", s.cfg.Socket)) - } - - if s.socket == nil && s.listener == nil { - err = errors.New("Server not configured to listen on either -socket or -host and -port") - return nil, errors.Trace(err) - } - - if s.cfg.ProxyProtocol.Networks != "" { - proxyTarget := s.listener - if proxyTarget == nil { - proxyTarget = s.socket - } - ppListener, err := proxyprotocol.NewLazyListener(proxyTarget, s.cfg.ProxyProtocol.Networks, - int(s.cfg.ProxyProtocol.HeaderTimeout), s.cfg.ProxyProtocol.Fallbackable) - if err != nil { - logutil.BgLogger().Error("ProxyProtocol networks parameter invalid") - return nil, errors.Trace(err) - } - if s.listener != nil { - s.listener = ppListener - logutil.BgLogger().Info("server is running MySQL protocol (through PROXY protocol)", zap.String("host", s.cfg.Host)) - } else { - s.socket = ppListener - logutil.BgLogger().Info("server is running MySQL protocol (through PROXY protocol)", zap.String("socket", s.cfg.Socket)) - } - } - - if s.cfg.Status.ReportStatus { - if err = s.listenStatusHTTPServer(); err != nil { - return nil, errors.Trace(err) - } - } - - // Automatically reload JWKS for tidb_auth_token. - if len(s.cfg.Security.AuthTokenJWKS) > 0 { - var ( - timeInterval time.Duration - err error - ctx context.Context - ) - if timeInterval, err = time.ParseDuration(s.cfg.Security.AuthTokenRefreshInterval); err != nil { - logutil.BgLogger().Error("Fail to parse security.auth-token-refresh-interval. Use default value", - zap.String("security.auth-token-refresh-interval", s.cfg.Security.AuthTokenRefreshInterval)) - timeInterval = config.DefAuthTokenRefreshInterval - } - ctx, s.authTokenCancelFunc = context.WithCancel(context.Background()) - if err = privileges.GlobalJWKS.LoadJWKS4AuthToken(ctx, &s.wg, s.cfg.Security.AuthTokenJWKS, timeInterval); err != nil { - logutil.BgLogger().Error("Fail to load JWKS from the path", zap.String("jwks", s.cfg.Security.AuthTokenJWKS)) - } - } - - variable.RegisterStatistics(s) - - return s, nil -} - -func cleanupStaleSocket(socket string) error { - sockStat, err := os.Stat(socket) - if err != nil { - return nil - } - - if sockStat.Mode().Type() != os.ModeSocket { - return fmt.Errorf( - "the specified socket file %s is a %s instead of a socket file", - socket, sockStat.Mode().String()) - } - - if _, err = net.Dial("unix", socket); err == nil { - return fmt.Errorf("unix socket %s exists and is functional, not removing it", socket) - } - - if err2 := os.Remove(socket); err2 != nil { - return fmt.Errorf("failed to cleanup stale Unix socket file %s: %w", socket, err) - } - - return nil -} - -func setSSLVariable(ca, key, cert string) { - variable.SetSysVar("have_openssl", "YES") - variable.SetSysVar("have_ssl", "YES") - variable.SetSysVar("ssl_cert", cert) - variable.SetSysVar("ssl_key", key) - variable.SetSysVar("ssl_ca", ca) -} - -func setTxnScope() { - variable.SetSysVar(variable.TiDBTxnScope, func() string { - if !variable.EnableLocalTxn.Load() { - return kv.GlobalTxnScope - } - if txnScope := config.GetTxnScopeFromConfig(); txnScope == kv.GlobalTxnScope { - return kv.GlobalTxnScope - } - return kv.LocalTxnScope - }()) -} - -// Export config-related metrics -func (s *Server) reportConfig() { - metrics.ConfigStatus.WithLabelValues("token-limit").Set(float64(s.cfg.TokenLimit)) - metrics.ConfigStatus.WithLabelValues("max_connections").Set(float64(s.cfg.Instance.MaxConnections)) -} - -// Run runs the server. -func (s *Server) Run() error { - metrics.ServerEventCounter.WithLabelValues(metrics.EventStart).Inc() - s.reportConfig() - - // Start HTTP API to report tidb info such as TPS. - if s.cfg.Status.ReportStatus { - s.startStatusHTTP() - } - // If error should be reported and exit the server it can be sent on this - // channel. Otherwise, end with sending a nil error to signal "done" - errChan := make(chan error, 2) - go s.startNetworkListener(s.listener, false, errChan) - go s.startNetworkListener(s.socket, true, errChan) - err := <-errChan - if err != nil { - return err - } - return <-errChan -} - -func (s *Server) startNetworkListener(listener net.Listener, isUnixSocket bool, errChan chan error) { - if listener == nil { - errChan <- nil - return - } - for { - conn, err := listener.Accept() - if err != nil { - if opErr, ok := err.(*net.OpError); ok { - if opErr.Err.Error() == "use of closed network connection" { - if s.inShutdownMode.Load() { - errChan <- nil - } else { - errChan <- err - } - return - } - } - - // If we got PROXY protocol error, we should continue to accept. - if proxyprotocol.IsProxyProtocolError(err) { - logutil.BgLogger().Error("PROXY protocol failed", zap.Error(err)) - continue - } - - logutil.BgLogger().Error("accept failed", zap.Error(err)) - errChan <- err - return - } - - logutil.BgLogger().Debug("accept new connection success") - - clientConn := s.newConn(conn) - if isUnixSocket { - var ( - uc *net.UnixConn - ok bool - ) - if clientConn.ppEnabled { - // Using reflect to get Raw Conn object from proxy protocol wrapper connection object - ppv := reflect.ValueOf(conn) - vconn := ppv.Elem().FieldByName("Conn") - rconn := vconn.Interface() - uc, ok = rconn.(*net.UnixConn) - } else { - uc, ok = conn.(*net.UnixConn) - } - if !ok { - logutil.BgLogger().Error("Expected UNIX socket, but got something else") - return - } - - clientConn.isUnixSocket = true - clientConn.peerHost = "localhost" - clientConn.socketCredUID, err = linux.GetSockUID(*uc) - if err != nil { - logutil.BgLogger().Error("Failed to get UNIX socket peer credentials", zap.Error(err)) - return - } - } - - err = nil - if !clientConn.ppEnabled { - // Check audit plugins when ProxyProtocol not enabled - err = s.checkAuditPlugin(clientConn) - } - if err != nil { - continue - } - - if s.dom != nil && s.dom.IsLostConnectionToPD() { - logutil.BgLogger().Warn("reject connection due to lost connection to PD") - terror.Log(clientConn.Close()) - continue - } - - go s.onConn(clientConn) - } -} - -func (*Server) checkAuditPlugin(clientConn *clientConn) error { - return plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { - authPlugin := plugin.DeclareAuditManifest(p.Manifest) - if authPlugin.OnConnectionEvent == nil { - return nil - } - host, _, err := clientConn.PeerHost("", false) - if err != nil { - logutil.BgLogger().Error("get peer host failed", zap.Error(err)) - terror.Log(clientConn.Close()) - return errors.Trace(err) - } - if err = authPlugin.OnConnectionEvent(context.Background(), plugin.PreAuth, - &variable.ConnectionInfo{Host: host}); err != nil { - logutil.BgLogger().Info("do connection event failed", zap.Error(err)) - terror.Log(clientConn.Close()) - return errors.Trace(err) - } - return nil - }) -} - -func (s *Server) startShutdown() { - logutil.BgLogger().Info("setting tidb-server to report unhealthy (shutting-down)") - s.health.Store(false) - // give the load balancer a chance to receive a few unhealthy health reports - // before acquiring the s.rwlock and blocking connections. - waitTime := time.Duration(s.cfg.GracefulWaitBeforeShutdown) * time.Second - if waitTime > 0 { - logutil.BgLogger().Info("waiting for stray connections before starting shutdown process", zap.Duration("waitTime", waitTime)) - time.Sleep(waitTime) - } -} - -func (s *Server) closeListener() { - if s.listener != nil { - err := s.listener.Close() - terror.Log(errors.Trace(err)) - s.listener = nil - } - if s.socket != nil { - err := s.socket.Close() - terror.Log(errors.Trace(err)) - s.socket = nil - } - if s.statusServer != nil { - err := s.statusServer.Close() - terror.Log(errors.Trace(err)) - s.statusServer = nil - } - if s.grpcServer != nil { - s.grpcServer.Stop() - s.grpcServer = nil - } - if s.autoIDService != nil { - s.autoIDService.Close() - } - if s.authTokenCancelFunc != nil { - s.authTokenCancelFunc() - } - s.wg.Wait() - metrics.ServerEventCounter.WithLabelValues(metrics.EventClose).Inc() -} - -// Close closes the server. -func (s *Server) Close() { - s.startShutdown() - s.rwlock.Lock() // // prevent new connections - defer s.rwlock.Unlock() - s.inShutdownMode.Store(true) - s.closeListener() -} - -func (s *Server) registerConn(conn *clientConn) bool { - s.rwlock.Lock() - defer s.rwlock.Unlock() - connections := len(s.clients) - - logger := logutil.BgLogger() - if s.inShutdownMode.Load() { - logger.Info("close connection directly when shutting down") - terror.Log(closeConn(conn, connections)) - return false - } - s.clients[conn.connectionID] = conn - connections = len(s.clients) - metrics.ConnGauge.Set(float64(connections)) - return true -} - -// onConn runs in its own goroutine, handles queries from this connection. -func (s *Server) onConn(conn *clientConn) { - // init the connInfo - _, _, err := conn.PeerHost("", false) - if err != nil { - logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). - Error("get peer host failed", zap.Error(err)) - terror.Log(conn.Close()) - return - } - - extensions, err := extension.GetExtensions() - if err != nil { - logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). - Error("error in get extensions", zap.Error(err)) - terror.Log(conn.Close()) - return - } - - if sessExtensions := extensions.NewSessionExtensions(); sessExtensions != nil { - conn.extensions = sessExtensions - conn.onExtensionConnEvent(extension.ConnConnected, nil) - defer func() { - conn.onExtensionConnEvent(extension.ConnDisconnected, nil) - }() - } - - ctx := logutil.WithConnID(context.Background(), conn.connectionID) - - if err := conn.handshake(ctx); err != nil { - conn.onExtensionConnEvent(extension.ConnHandshakeRejected, err) - if plugin.IsEnable(plugin.Audit) && conn.getCtx() != nil { - conn.getCtx().GetSessionVars().ConnectionInfo = conn.connectInfo() - err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { - authPlugin := plugin.DeclareAuditManifest(p.Manifest) - if authPlugin.OnConnectionEvent != nil { - pluginCtx := context.WithValue(context.Background(), plugin.RejectReasonCtxValue{}, err.Error()) - return authPlugin.OnConnectionEvent(pluginCtx, plugin.Reject, conn.ctx.GetSessionVars().ConnectionInfo) - } - return nil - }) - terror.Log(err) - } - switch errors.Cause(err) { - case io.EOF: - // `EOF` means the connection is closed normally, we do not treat it as a noticeable error and log it in 'DEBUG' level. - logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). - Debug("EOF", zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) - case servererr.ErrConCount: - if err := conn.writeError(ctx, err); err != nil { - logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). - Warn("error in writing errConCount", zap.Error(err), - zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) - } - default: - metrics.HandShakeErrorCounter.Inc() - logutil.BgLogger().With(zap.Uint64("conn", conn.connectionID)). - Warn("Server.onConn handshake", zap.Error(err), - zap.String("remote addr", conn.bufReadConn.RemoteAddr().String())) - } - terror.Log(conn.Close()) - return - } - - logutil.Logger(ctx).Debug("new connection", zap.String("remoteAddr", conn.bufReadConn.RemoteAddr().String())) - - defer func() { - terror.Log(conn.Close()) - logutil.Logger(ctx).Debug("connection closed") - }() - - if !s.registerConn(conn) { - return - } - - sessionVars := conn.ctx.GetSessionVars() - sessionVars.ConnectionInfo = conn.connectInfo() - conn.onExtensionConnEvent(extension.ConnHandshakeAccepted, nil) - err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { - authPlugin := plugin.DeclareAuditManifest(p.Manifest) - if authPlugin.OnConnectionEvent != nil { - return authPlugin.OnConnectionEvent(context.Background(), plugin.Connected, sessionVars.ConnectionInfo) - } - return nil - }) - if err != nil { - return - } - - connectedTime := time.Now() - conn.Run(ctx) - - err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { - authPlugin := plugin.DeclareAuditManifest(p.Manifest) - if authPlugin.OnConnectionEvent != nil { - sessionVars.ConnectionInfo.Duration = float64(time.Since(connectedTime)) / float64(time.Millisecond) - err := authPlugin.OnConnectionEvent(context.Background(), plugin.Disconnect, sessionVars.ConnectionInfo) - if err != nil { - logutil.BgLogger().Warn("do connection event failed", zap.String("plugin", authPlugin.Name), zap.Error(err)) - } - } - return nil - }) - if err != nil { - return - } -} - -func (cc *clientConn) connectInfo() *variable.ConnectionInfo { - connType := variable.ConnTypeSocket - sslVersion := "" - if cc.isUnixSocket { - connType = variable.ConnTypeUnixSocket - } else if cc.tlsConn != nil { - connType = variable.ConnTypeTLS - sslVersionNum := cc.tlsConn.ConnectionState().Version - switch sslVersionNum { - case tls.VersionTLS10: - sslVersion = "TLSv1.0" - case tls.VersionTLS11: - sslVersion = "TLSv1.1" - case tls.VersionTLS12: - sslVersion = "TLSv1.2" - case tls.VersionTLS13: - sslVersion = "TLSv1.3" - default: - sslVersion = fmt.Sprintf("Unknown TLS version: %d", sslVersionNum) - } - } - connInfo := &variable.ConnectionInfo{ - ConnectionID: cc.connectionID, - ConnectionType: connType, - Host: cc.peerHost, - ClientIP: cc.peerHost, - ClientPort: cc.peerPort, - ServerID: 1, - ServerIP: cc.serverHost, - ServerPort: int(cc.server.cfg.Port), - User: cc.user, - ServerOSLoginUser: osUser, - OSVersion: osVersion, - ServerVersion: mysql.TiDBReleaseVersion, - SSLVersion: sslVersion, - PID: serverPID, - DB: cc.dbname, - AuthMethod: cc.authPlugin, - Attributes: cc.attrs, - } - return connInfo -} - -func (s *Server) checkConnectionCount() error { - // When the value of Instance.MaxConnections is 0, the number of connections is unlimited. - if int(s.cfg.Instance.MaxConnections) == 0 { - return nil - } - - s.rwlock.RLock() - conns := len(s.clients) - s.rwlock.RUnlock() - - if conns >= int(s.cfg.Instance.MaxConnections) { - logutil.BgLogger().Error("too many connections", - zap.Uint32("max connections", s.cfg.Instance.MaxConnections), zap.Error(servererr.ErrConCount)) - return servererr.ErrConCount - } - return nil -} - -// ShowProcessList implements the SessionManager interface. -func (s *Server) ShowProcessList() map[uint64]*util.ProcessInfo { - rs := make(map[uint64]*util.ProcessInfo) - for connID, pi := range s.getUserProcessList() { - rs[connID] = pi - } - if s.dom != nil { - for connID, pi := range s.dom.SysProcTracker().GetSysProcessList() { - rs[connID] = pi - } - } - return rs -} - -func (s *Server) getUserProcessList() map[uint64]*util.ProcessInfo { - s.rwlock.RLock() - defer s.rwlock.RUnlock() - rs := make(map[uint64]*util.ProcessInfo) - for _, client := range s.clients { - if pi := client.ctx.ShowProcess(); pi != nil { - rs[pi.ID] = pi - } - } - return rs -} - -// ShowTxnList shows all txn info for displaying in `TIDB_TRX` -func (s *Server) ShowTxnList() []*txninfo.TxnInfo { - s.rwlock.RLock() - defer s.rwlock.RUnlock() - rs := make([]*txninfo.TxnInfo, 0, len(s.clients)) - for _, client := range s.clients { - if client.ctx.Session != nil { - info := client.ctx.Session.TxnInfo() - if info != nil { - rs = append(rs, info) - } - } - } - return rs -} - -// GetProcessInfo implements the SessionManager interface. -func (s *Server) GetProcessInfo(id uint64) (*util.ProcessInfo, bool) { - s.rwlock.RLock() - conn, ok := s.clients[id] - s.rwlock.RUnlock() - if !ok { - if s.dom != nil { - if pinfo, ok2 := s.dom.SysProcTracker().GetSysProcessList()[id]; ok2 { - return pinfo, true - } - } - return &util.ProcessInfo{}, false - } - return conn.ctx.ShowProcess(), ok -} - -// GetConAttrs returns the connection attributes -func (s *Server) GetConAttrs(user *auth.UserIdentity) map[uint64]map[string]string { - s.rwlock.RLock() - defer s.rwlock.RUnlock() - rs := make(map[uint64]map[string]string) - for _, client := range s.clients { - if user != nil { - if user.Username != client.user { - continue - } - if user.Hostname != client.peerHost { - continue - } - } - if pi := client.ctx.ShowProcess(); pi != nil { - rs[pi.ID] = client.attrs - } - } - return rs -} - -// Kill implements the SessionManager interface. -func (s *Server) Kill(connectionID uint64, query bool, maxExecutionTime bool) { - logutil.BgLogger().Info("kill", zap.Uint64("conn", connectionID), zap.Bool("query", query)) - metrics.ServerEventCounter.WithLabelValues(metrics.EventKill).Inc() - - s.rwlock.RLock() - defer s.rwlock.RUnlock() - conn, ok := s.clients[connectionID] - if !ok && s.dom != nil { - s.dom.SysProcTracker().KillSysProcess(connectionID) - return - } - - if !query { - // Mark the client connection status as WaitShutdown, when clientConn.Run detect - // this, it will end the dispatch loop and exit. - conn.setStatus(connStatusWaitShutdown) - } - killQuery(conn, maxExecutionTime) -} - -// UpdateTLSConfig implements the SessionManager interface. -func (s *Server) UpdateTLSConfig(cfg *tls.Config) { - atomic.StorePointer(&s.tlsConfig, unsafe.Pointer(cfg)) -} - -// GetTLSConfig implements the SessionManager interface. -func (s *Server) GetTLSConfig() *tls.Config { - return (*tls.Config)(atomic.LoadPointer(&s.tlsConfig)) -} - -func killQuery(conn *clientConn, maxExecutionTime bool) { - sessVars := conn.ctx.GetSessionVars() - if maxExecutionTime { - atomic.StoreUint32(&sessVars.Killed, 2) - } else { - atomic.StoreUint32(&sessVars.Killed, 1) - } - conn.mu.RLock() - cancelFunc := conn.mu.cancelFunc - conn.mu.RUnlock() - - if cancelFunc != nil { - cancelFunc() - } - if conn.bufReadConn != nil { - if err := conn.bufReadConn.SetReadDeadline(time.Now()); err != nil { - logutil.BgLogger().Warn("error setting read deadline for kill.", zap.Error(err)) - } - } -} - -// KillSysProcesses kill sys processes such as auto analyze. -func (s *Server) KillSysProcesses() { - if s.dom == nil { - return - } - sysProcTracker := s.dom.SysProcTracker() - for connID := range sysProcTracker.GetSysProcessList() { - sysProcTracker.KillSysProcess(connID) - } -} - -// KillAllConnections implements the SessionManager interface. -// KillAllConnections kills all connections. -func (s *Server) KillAllConnections() { - logutil.BgLogger().Info("kill all connections.", zap.String("category", "server")) - - s.rwlock.RLock() - defer s.rwlock.RUnlock() - for _, conn := range s.clients { - conn.setStatus(connStatusShutdown) - if err := conn.closeWithoutLock(); err != nil { - terror.Log(err) - } - killQuery(conn, false) - } - - s.KillSysProcesses() -} - -// DrainClients drain all connections in drainWait. -// After drainWait duration, we kill all connections still not quit explicitly and wait for cancelWait. -func (s *Server) DrainClients(drainWait time.Duration, cancelWait time.Duration) { - logger := logutil.BgLogger() - logger.Info("start drain clients") - - conns := make(map[uint64]*clientConn) - - s.rwlock.Lock() - for k, v := range s.clients { - conns[k] = v - } - s.rwlock.Unlock() - - allDone := make(chan struct{}) - quitWaitingForConns := make(chan struct{}) - defer close(quitWaitingForConns) - go func() { - defer close(allDone) - for _, conn := range conns { - if !conn.getCtx().GetSessionVars().InTxn() { - continue - } - select { - case <-conn.quit: - case <-quitWaitingForConns: - return - } - } - }() - - select { - case <-allDone: - logger.Info("all sessions quit in drain wait time") - case <-time.After(drainWait): - logger.Info("timeout waiting all sessions quit") - } - - s.KillAllConnections() - - select { - case <-allDone: - case <-time.After(cancelWait): - logger.Warn("some sessions do not quit in cancel wait time") - } -} - -// ServerID implements SessionManager interface. -func (s *Server) ServerID() uint64 { - return s.dom.ServerID() -} - -// GetAutoAnalyzeProcID implements SessionManager interface. -func (s *Server) GetAutoAnalyzeProcID() uint64 { - return s.dom.GetAutoAnalyzeProcID() -} - -// StoreInternalSession implements SessionManager interface. -// @param addr The address of a session.session struct variable -func (s *Server) StoreInternalSession(se interface{}) { - s.sessionMapMutex.Lock() - s.internalSessions[se] = struct{}{} - s.sessionMapMutex.Unlock() -} - -// DeleteInternalSession implements SessionManager interface. -// @param addr The address of a session.session struct variable -func (s *Server) DeleteInternalSession(se interface{}) { - s.sessionMapMutex.Lock() - delete(s.internalSessions, se) - s.sessionMapMutex.Unlock() -} - -// GetInternalSessionStartTSList implements SessionManager interface. -func (s *Server) GetInternalSessionStartTSList() []uint64 { - s.sessionMapMutex.Lock() - defer s.sessionMapMutex.Unlock() - tsList := make([]uint64, 0, len(s.internalSessions)) - analyzeProcID := s.GetAutoAnalyzeProcID() - for se := range s.internalSessions { - if ts, processInfoID := session.GetStartTSFromSession(se); ts != 0 { - if processInfoID == analyzeProcID { - continue - } - tsList = append(tsList, ts) - } - } - return tsList -} - -// InternalSessionExists is used for test -func (s *Server) InternalSessionExists(se interface{}) bool { - s.sessionMapMutex.Lock() - _, ok := s.internalSessions[se] - s.sessionMapMutex.Unlock() - return ok -} - -// setSysTimeZoneOnce is used for parallel run tests. When several servers are running, -// only the first will actually do setSystemTimeZoneVariable, thus we can avoid data race. -var setSysTimeZoneOnce = &sync.Once{} - -func setSystemTimeZoneVariable() { - setSysTimeZoneOnce.Do(func() { - tz, err := timeutil.GetSystemTZ() - if err != nil { - logutil.BgLogger().Error( - "Error getting SystemTZ, use default value instead", - zap.Error(err), - zap.String("default system_time_zone", variable.GetSysVar("system_time_zone").Value)) - return - } - variable.SetSysVar("system_time_zone", tz) - }) -} - -// CheckOldRunningTxn implements SessionManager interface. -func (s *Server) CheckOldRunningTxn(job2ver map[int64]int64, job2ids map[int64]string) { - s.rwlock.RLock() - defer s.rwlock.RUnlock() - - printLog := false - if time.Since(s.printMDLLogTime) > 10*time.Second { - printLog = true - s.printMDLLogTime = time.Now() - } - for _, client := range s.clients { - if client.ctx.Session != nil { - session.RemoveLockDDLJobs(client.ctx.Session, job2ver, job2ids, printLog) - } - } -} - -// KillNonFlashbackClusterConn implements SessionManager interface. -func (s *Server) KillNonFlashbackClusterConn() { - s.rwlock.RLock() - connIDs := make([]uint64, 0, len(s.clients)) - for _, client := range s.clients { - if client.ctx.Session != nil { - processInfo := client.ctx.Session.ShowProcess() - ddl, ok := processInfo.StmtCtx.GetPlan().(*core.DDL) - if !ok { - connIDs = append(connIDs, client.connectionID) - continue - } - _, ok = ddl.Statement.(*ast.FlashBackToTimestampStmt) - if !ok { - connIDs = append(connIDs, client.connectionID) - continue - } - } - } - s.rwlock.RUnlock() - for _, id := range connIDs { - s.Kill(id, false, false) - } -} diff --git a/server/stat.go b/server/stat.go deleted file mode 100644 index 335d58cc95778..0000000000000 --- a/server/stat.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package server - -import ( - "crypto/x509" - "time" - - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/server/handler/tikvhandler" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -var ( - serverNotAfter = "Ssl_server_not_after" - serverNotBefore = "Ssl_server_not_before" - upTime = "Uptime" -) - -var defaultStatus = map[string]*variable.StatusVal{ - serverNotAfter: {Scope: variable.ScopeGlobal | variable.ScopeSession, Value: ""}, - serverNotBefore: {Scope: variable.ScopeGlobal | variable.ScopeSession, Value: ""}, - upTime: {Scope: variable.ScopeGlobal, Value: 0}, -} - -// GetScope gets the Status variables scope. -func (*Server) GetScope(_ string) variable.ScopeFlag { - return variable.DefaultStatusVarScopeFlag -} - -// Stats returns the server statistics. -func (s *Server) Stats(_ *variable.SessionVars) (map[string]interface{}, error) { - m := make(map[string]interface{}, len(defaultStatus)) - - for name, v := range defaultStatus { - m[name] = v.Value - } - - tlsConfig := s.GetTLSConfig() - if tlsConfig != nil { - if len(tlsConfig.Certificates) == 1 { - pc, err := x509.ParseCertificate(tlsConfig.Certificates[0].Certificate[0]) - if err != nil { - logutil.BgLogger().Error("Failed to parse TLS certficates to get server status", zap.Error(err)) - } else { - m[serverNotAfter] = pc.NotAfter.Format("Jan _2 15:04:05 2006 MST") - m[serverNotBefore] = pc.NotBefore.Format("Jan _2 15:04:05 2006 MST") - } - } - } - - var err error - info := tikvhandler.ServerInfo{} - info.ServerInfo, err = infosync.GetServerInfo() - if err != nil { - logutil.BgLogger().Error("Failed to get ServerInfo for uptime status", zap.Error(err)) - } else { - m[upTime] = int64(time.Since(time.Unix(info.ServerInfo.StartTimestamp, 0)).Seconds()) - } - - return m, nil -} diff --git a/server/stat_test.go b/server/stat_test.go deleted file mode 100644 index d146f31a50ea1..0000000000000 --- a/server/stat_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package server - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/stretchr/testify/require" -) - -func TestUptime(t *testing.T) { - var err error - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/infosync/mockServerInfo", "return(true)")) - defer func() { - err := failpoint.Disable("github.com/pingcap/tidb/domain/infosync/mockServerInfo") - require.NoError(t, err) - }() - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - - dom, err := session.BootstrapSession(store) - defer func() { - dom.Close() - err := store.Close() - require.NoError(t, err) - }() - require.NoError(t, err) - - _, err = infosync.GlobalInfoSyncerInit(context.Background(), dom.DDL().GetID(), dom.ServerID, dom.GetEtcdClient(), dom.GetEtcdClient(), dom.GetPDClient(), keyspace.CodecV1, true) - require.NoError(t, err) - - tidbdrv := NewTiDBDriver(store) - cfg := util.NewTestConfig() - cfg.Socket = "" - cfg.Port = 0 - cfg.Status.StatusPort = 0 - server, err := NewServer(cfg, tidbdrv) - require.NoError(t, err) - - stats, err := server.Stats(nil) - require.NoError(t, err) - require.GreaterOrEqual(t, stats[upTime].(int64), int64(time.Since(time.Unix(1282967700, 0)).Seconds())) -} diff --git a/server/tests/BUILD.bazel b/server/tests/BUILD.bazel deleted file mode 100644 index 501ffb60f9439..0000000000000 --- a/server/tests/BUILD.bazel +++ /dev/null @@ -1,54 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tests_test", - timeout = "short", - srcs = [ - "main_test.go", - "tidb_serial_test.go", - "tidb_test.go", - ], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//ddl/util", - "//domain", - "//extension", - "//kv", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/mysql", - "//server", - "//server/internal/column", - "//server/internal/resultset", - "//server/internal/testserverclient", - "//server/internal/testutil", - "//server/internal/util", - "//session", - "//sessionctx/variable", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit", - "//testkit/testsetup", - "//util", - "//util/cpuprofile", - "//util/plancodec", - "//util/resourcegrouptag", - "//util/topsql", - "//util/topsql/collector", - "//util/topsql/collector/mock", - "//util/topsql/state", - "//util/topsql/stmtstats", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/server/tests/main_test.go b/server/tests/main_test.go deleted file mode 100644 index c0ed53c762f68..0000000000000 --- a/server/tests/main_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "fmt" - "os" - "reflect" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testsetup" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - server.RunInGoTest = true - testsetup.SetupForCommonTest() - topsqlstate.EnableTopSQL() - unistore.CheckResourceTagForTopSQLInGoTest = true - - // AsyncCommit will make DDL wait 2.5s before changing to the next state. - // Set schema lease to avoid it from making CI slow. - session.SetSchemaLease(0) - - tikv.EnableFailpoints() - - metrics.RegisterMetrics() - - // sanity check: the global config should not be changed by other pkg init function. - // see also https://github.com/pingcap/tidb/issues/22162 - defaultConfig := config.NewConfig() - globalConfig := config.GetGlobalConfig() - if !reflect.DeepEqual(defaultConfig, globalConfig) { - _, _ = fmt.Fprintf(os.Stderr, "server: the global config has been changed.\n") - _, _ = fmt.Fprintf(os.Stderr, "default: %#v\nglobal: %#v", defaultConfig, globalConfig) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("time.Sleep"), - goleak.IgnoreTopFunction("database/sql.(*Tx).awaitDone"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).readLoop"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/server.NewServer.func1"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/server/tests/tidb_test.go b/server/tests/tidb_test.go deleted file mode 100644 index 984c58b4c152d..0000000000000 --- a/server/tests/tidb_test.go +++ /dev/null @@ -1,3124 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "context" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "database/sql" - "encoding/binary" - "encoding/pem" - "fmt" - "io" - "math/big" - "net" - "net/http" - "os" - "path/filepath" - "strings" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/go-sql-driver/mysql" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - tmysql "github.com/pingcap/tidb/parser/mysql" - server2 "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/server/internal/column" - "github.com/pingcap/tidb/server/internal/resultset" - "github.com/pingcap/tidb/server/internal/testserverclient" - "github.com/pingcap/tidb/server/internal/testutil" - util2 "github.com/pingcap/tidb/server/internal/util" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/resourcegrouptag" - "github.com/pingcap/tidb/util/topsql" - "github.com/pingcap/tidb/util/topsql/collector" - mockTopSQLTraceCPU "github.com/pingcap/tidb/util/topsql/collector/mock" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikvrpc" - "go.opencensus.io/stats/view" -) - -type tidbTestSuite struct { - *testserverclient.TestServerClient - tidbdrv *server2.TiDBDriver - server *server2.Server - domain *domain.Domain - store kv.Storage -} - -func createTidbTestSuite(t *testing.T) *tidbTestSuite { - cfg := util2.NewTestConfig() - cfg.Port = 0 - cfg.Status.ReportStatus = true - cfg.Status.StatusPort = 0 - cfg.Status.RecordDBLabel = true - cfg.Performance.TCPKeepAlive = true - return createTidbTestSuiteWithCfg(t, cfg) -} - -func createTidbTestSuiteWithCfg(t *testing.T, cfg *config.Config) *tidbTestSuite { - ts := &tidbTestSuite{TestServerClient: testserverclient.NewTestServerClient()} - - // setup tidbTestSuite - var err error - ts.store, err = mockstore.NewMockStore() - session.DisableStats4Test() - require.NoError(t, err) - ts.domain, err = session.BootstrapSession(ts.store) - require.NoError(t, err) - ts.tidbdrv = server2.NewTiDBDriver(ts.store) - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - ts.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - ts.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) - ts.server = server - ts.server.SetDomain(ts.domain) - ts.domain.InfoSyncer().SetSessionManager(ts.server) - go func() { - err := ts.server.Run() - require.NoError(t, err) - }() - ts.WaitUntilServerOnline() - - t.Cleanup(func() { - if ts.domain != nil { - ts.domain.Close() - } - if ts.server != nil { - ts.server.Close() - } - if ts.store != nil { - require.NoError(t, ts.store.Close()) - } - view.Stop() - }) - return ts -} - -type tidbTestTopSQLSuite struct { - *tidbTestSuite -} - -func createTidbTestTopSQLSuite(t *testing.T) *tidbTestTopSQLSuite { - base := createTidbTestSuite(t) - - ts := &tidbTestTopSQLSuite{base} - - // Initialize global variable for top-sql test. - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - err := db.Close() - require.NoError(t, err) - }() - - dbt := testkit.NewDBTestKit(t, db) - topsqlstate.GlobalState.PrecisionSeconds.Store(1) - topsqlstate.GlobalState.ReportIntervalSeconds.Store(2) - dbt.MustExec("set @@global.tidb_top_sql_max_time_series_count=5;") - - require.NoError(t, cpuprofile.StartCPUProfiler()) - t.Cleanup(func() { - cpuprofile.StopCPUProfiler() - topsqlstate.GlobalState.PrecisionSeconds.Store(topsqlstate.DefTiDBTopSQLPrecisionSeconds) - topsqlstate.GlobalState.ReportIntervalSeconds.Store(topsqlstate.DefTiDBTopSQLReportIntervalSeconds) - view.Stop() - }) - return ts -} - -func TestRegression(t *testing.T) { - ts := createTidbTestSuite(t) - if testserverclient.Regression { - ts.RunTestRegression(t, nil, "Regression") - } -} - -func TestUint64(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestPrepareResultFieldType(t) -} - -func TestSpecialType(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestSpecialType(t) -} - -func TestPreparedString(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestPreparedString(t) -} - -func TestPreparedTimestamp(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestPreparedTimestamp(t) -} - -func TestConcurrentUpdate(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestConcurrentUpdate(t) -} - -func TestErrorCode(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestErrorCode(t) -} - -func TestAuth(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestAuth(t) - ts.RunTestIssue3682(t) - ts.RunTestAccountLock(t) -} - -func TestIssues(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestIssue3662(t) - ts.RunTestIssue3680(t) - ts.RunTestIssue22646(t) -} - -func TestDBNameEscape(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestDBNameEscape(t) -} - -func TestResultFieldTableIsNull(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestResultFieldTableIsNull(t) -} - -func TestStatusAPI(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestStatusAPI(t) -} - -func TestStatusPort(t *testing.T) { - ts := createTidbTestSuite(t) - - cfg := util2.NewTestConfig() - cfg.Port = 0 - cfg.Status.ReportStatus = true - cfg.Status.StatusPort = ts.StatusPort - cfg.Performance.TCPKeepAlive = true - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.Error(t, err) - require.Nil(t, server) -} - -func TestStatusAPIWithTLS(t *testing.T) { - ts := createTidbTestSuite(t) - - dir := t.TempDir() - - fileName := func(file string) string { - return filepath.Join(dir, file) - } - - caCert, caKey, err := generateCert(0, "TiDB CA 2", nil, nil, fileName("ca-key-2.pem"), fileName("ca-cert-2.pem")) - require.NoError(t, err) - _, _, err = generateCert(1, "tidb-server-2", caCert, caKey, fileName("server-key-2.pem"), fileName("server-cert-2.pem")) - require.NoError(t, err) - - cli := testserverclient.NewTestServerClient() - cli.StatusScheme = "https" - cfg := util2.NewTestConfig() - cfg.Port = cli.Port - cfg.Status.StatusPort = cli.StatusPort - cfg.Security.ClusterSSLCA = fileName("ca-cert-2.pem") - cfg.Security.ClusterSSLCert = fileName("server-cert-2.pem") - cfg.Security.ClusterSSLKey = fileName("server-key-2.pem") - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - - // https connection should work. - ts.RunTestStatusAPI(t) - - // but plain http connection should fail. - cli.StatusScheme = "http" - //nolint:bodyclose - _, err = cli.FetchStatus("/status") - require.Error(t, err) - - server.Close() -} - -func TestStatusAPIWithTLSCNCheck(t *testing.T) { - ts := createTidbTestSuite(t) - - dir := t.TempDir() - - caPath := filepath.Join(dir, "ca-cert-cn.pem") - serverKeyPath := filepath.Join(dir, "server-key-cn.pem") - serverCertPath := filepath.Join(dir, "server-cert-cn.pem") - client1KeyPath := filepath.Join(dir, "client-key-cn-check-a.pem") - client1CertPath := filepath.Join(dir, "client-cert-cn-check-a.pem") - client2KeyPath := filepath.Join(dir, "client-key-cn-check-b.pem") - client2CertPath := filepath.Join(dir, "client-cert-cn-check-b.pem") - - caCert, caKey, err := generateCert(0, "TiDB CA CN CHECK", nil, nil, filepath.Join(dir, "ca-key-cn.pem"), caPath) - require.NoError(t, err) - _, _, err = generateCert(1, "tidb-server-cn-check", caCert, caKey, serverKeyPath, serverCertPath) - require.NoError(t, err) - _, _, err = generateCert(2, "tidb-client-cn-check-a", caCert, caKey, client1KeyPath, client1CertPath, func(c *x509.Certificate) { - c.Subject.CommonName = "tidb-client-1" - }) - require.NoError(t, err) - _, _, err = generateCert(3, "tidb-client-cn-check-b", caCert, caKey, client2KeyPath, client2CertPath, func(c *x509.Certificate) { - c.Subject.CommonName = "tidb-client-2" - }) - require.NoError(t, err) - - cli := testserverclient.NewTestServerClient() - cli.StatusScheme = "https" - cfg := util2.NewTestConfig() - cfg.Port = cli.Port - cfg.Status.StatusPort = cli.StatusPort - cfg.Security.ClusterSSLCA = caPath - cfg.Security.ClusterSSLCert = serverCertPath - cfg.Security.ClusterSSLKey = serverKeyPath - cfg.Security.ClusterVerifyCN = []string{"tidb-client-2"} - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - defer server.Close() - time.Sleep(time.Millisecond * 100) - - hc := newTLSHttpClient(t, caPath, - client1CertPath, - client1KeyPath, - ) - //nolint:bodyclose - _, err = hc.Get(cli.StatusURL("/status")) - require.Error(t, err) - - hc = newTLSHttpClient(t, caPath, - client2CertPath, - client2KeyPath, - ) - resp, err := hc.Get(cli.StatusURL("/status")) - require.NoError(t, err) - require.Nil(t, resp.Body.Close()) -} - -func newTLSHttpClient(t *testing.T, caFile, certFile, keyFile string) *http.Client { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - require.NoError(t, err) - caCert, err := os.ReadFile(caFile) - require.NoError(t, err) - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caCertPool, - InsecureSkipVerify: true, - } - tlsConfig.BuildNameToCertificate() - return &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}} -} - -func TestMultiStatements(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunFailedTestMultiStatements(t) - ts.RunTestMultiStatements(t) -} - -func TestSocketForwarding(t *testing.T) { - tempDir := t.TempDir() - socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK - - ts := createTidbTestSuite(t) - - cli := testserverclient.NewTestServerClient() - cfg := util2.NewTestConfig() - cfg.Socket = socketFile - cfg.Port = cli.Port - os.Remove(cfg.Socket) - cfg.Status.ReportStatus = false - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - defer server.Close() - - cli.RunTestRegression(t, func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.Addr = socketFile - config.DBName = "test" - config.Params = map[string]string{"sql_mode": "'STRICT_ALL_TABLES'"} - }, "SocketRegression") -} - -func TestSocket(t *testing.T) { - tempDir := t.TempDir() - socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK - - cfg := util2.NewTestConfig() - cfg.Socket = socketFile - cfg.Port = 0 - os.Remove(cfg.Socket) - cfg.Host = "" - cfg.Status.ReportStatus = false - - ts := createTidbTestSuite(t) - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - defer server.Close() - - confFunc := func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.Addr = socketFile - config.DBName = "test" - config.Params = map[string]string{"sql_mode": "STRICT_ALL_TABLES"} - } - // a fake server client, config is override, just used to run tests - cli := testserverclient.NewTestServerClient() - cli.WaitUntilCustomServerCanConnect(confFunc) - cli.RunTestRegression(t, confFunc, "SocketRegression") -} - -func TestSocketAndIp(t *testing.T) { - tempDir := t.TempDir() - socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK - - cli := testserverclient.NewTestServerClient() - cfg := util2.NewTestConfig() - cfg.Socket = socketFile - cfg.Port = cli.Port - cfg.Status.ReportStatus = false - - ts := createTidbTestSuite(t) - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - cli.WaitUntilServerCanConnect() - defer server.Close() - - // Test with Socket connection + Setup user1@% for all host access - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - defer func() { - cli.RunTests(t, func(config *mysql.Config) { - config.User = "root" - }, - func(dbt *testkit.DBTestKit) { - dbt.MustExec("DROP USER IF EXISTS 'user1'@'%'") - dbt.MustExec("DROP USER IF EXISTS 'user1'@'localhost'") - dbt.MustExec("DROP USER IF EXISTS 'user1'@'127.0.0.1'") - }) - }() - cli.RunTests(t, func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.Addr = socketFile - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "root@localhost") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - dbt.MustQuery("CREATE USER user1@'%'") - dbt.MustQuery("GRANT SELECT ON test.* TO user1@'%'") - }) - // Test with Network interface connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "user1@127.0.0.1") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") - rows = dbt.MustQuery("select host from information_schema.processlist where user = 'user1'") - records := cli.Rows(t, rows) - require.Contains(t, records[0], ":", "Missing : in is.processlist") - }) - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") - }) - - // Setup user1@127.0.0.1 for loop back network interface access - cli.RunTests(t, func(config *mysql.Config) { - config.User = "root" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "root@127.0.0.1") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - dbt.MustQuery("CREATE USER user1@127.0.0.1") - dbt.MustQuery("GRANT SELECT,INSERT ON test.* TO user1@'127.0.0.1'") - }) - // Test with Network interface connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "user1@127.0.0.1") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'127.0.0.1'\nGRANT SELECT,INSERT ON `test`.* TO 'user1'@'127.0.0.1'") - }) - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") - }) - - // Setup user1@localhost for socket (and if MySQL compatible; loop back network interface access) - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "root" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "root@localhost") - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - dbt.MustExec("CREATE USER user1@localhost") - dbt.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO user1@localhost") - }) - // Test with Network interface connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "user1@127.0.0.1") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'127.0.0.1'\nGRANT SELECT,INSERT ON `test`.* TO 'user1'@'127.0.0.1'") - require.NoError(t, rows.Close()) - }) - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'localhost'\nGRANT SELECT,INSERT,UPDATE,DELETE ON `test`.* TO 'user1'@'localhost'") - require.NoError(t, rows.Close()) - }) -} - -// TestOnlySocket for server configuration without network interface for mysql clients -func TestOnlySocket(t *testing.T) { - tempDir := t.TempDir() - socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK - - cli := testserverclient.NewTestServerClient() - cfg := util2.NewTestConfig() - cfg.Socket = socketFile - cfg.Host = "" // No network interface listening for mysql traffic - cfg.Status.ReportStatus = false - - ts := createTidbTestSuite(t) - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - defer server.Close() - require.Nil(t, server.Listener()) - require.NotNil(t, server.Socket()) - - // Test with Socket connection + Setup user1@% for all host access - defer func() { - cli.RunTests(t, func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.Addr = socketFile - }, - func(dbt *testkit.DBTestKit) { - dbt.MustExec("DROP USER IF EXISTS 'user1'@'%'") - dbt.MustExec("DROP USER IF EXISTS 'user1'@'localhost'") - dbt.MustExec("DROP USER IF EXISTS 'user1'@'127.0.0.1'") - }) - }() - cli.RunTests(t, func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.Addr = socketFile - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "root@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - require.NoError(t, rows.Close()) - dbt.MustExec("CREATE USER user1@'%'") - dbt.MustExec("GRANT SELECT ON test.* TO user1@'%'") - }) - // Test with Network interface connection with all hosts, should fail since server not configured - db, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { - config.User = "root" - config.DBName = "test" - })) - require.NoErrorf(t, err, "Open failed") - err = db.Ping() - require.Errorf(t, err, "Connect succeeded when not configured!?!") - db.Close() - db, err = sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { - config.User = "user1" - config.DBName = "test" - })) - require.NoErrorf(t, err, "Open failed") - err = db.Ping() - require.Errorf(t, err, "Connect succeeded when not configured!?!") - db.Close() - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") - require.NoError(t, rows.Close()) - }) - - // Setup user1@127.0.0.1 for loop back network interface access - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "root" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report user1@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "root@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - require.NoError(t, rows.Close()) - dbt.MustExec("CREATE USER user1@127.0.0.1") - dbt.MustExec("GRANT SELECT,INSERT ON test.* TO user1@'127.0.0.1'") - }) - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'%'\nGRANT SELECT ON `test`.* TO 'user1'@'%'") - require.NoError(t, rows.Close()) - }) - - // Setup user1@localhost for socket (and if MySQL compatible; loop back network interface access) - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "root" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "root@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - require.NoError(t, rows.Close()) - dbt.MustExec("CREATE USER user1@localhost") - dbt.MustExec("GRANT SELECT,INSERT,UPDATE,DELETE ON test.* TO user1@localhost") - }) - // Test with unix domain socket file connection with all hosts - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "user1" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "user1@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'user1'@'localhost'\nGRANT SELECT,INSERT,UPDATE,DELETE ON `test`.* TO 'user1'@'localhost'") - require.NoError(t, rows.Close()) - }) -} - -// generateCert generates a private key and a certificate in PEM format based on parameters. -// If parentCert and parentCertKey is specified, the new certificate will be signed by the parentCert. -// Otherwise, the new certificate will be self-signed and is a CA. -func generateCert(sn int, commonName string, parentCert *x509.Certificate, parentCertKey *rsa.PrivateKey, outKeyFile string, outCertFile string, opts ...func(c *x509.Certificate)) (*x509.Certificate, *rsa.PrivateKey, error) { - privateKey, err := rsa.GenerateKey(rand.Reader, 528) - if err != nil { - return nil, nil, errors.Trace(err) - } - notBefore := time.Now().Add(-10 * time.Minute).UTC() - notAfter := notBefore.Add(1 * time.Hour).UTC() - - template := x509.Certificate{ - SerialNumber: big.NewInt(int64(sn)), - Subject: pkix.Name{CommonName: commonName, Names: []pkix.AttributeTypeAndValue{util.MockPkixAttribute(util.CommonName, commonName)}}, - DNSNames: []string{commonName}, - NotBefore: notBefore, - NotAfter: notAfter, - KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - BasicConstraintsValid: true, - } - for _, opt := range opts { - opt(&template) - } - - var parent *x509.Certificate - var priv *rsa.PrivateKey - - if parentCert == nil || parentCertKey == nil { - template.IsCA = true - template.KeyUsage |= x509.KeyUsageCertSign - parent = &template - priv = privateKey - } else { - parent = parentCert - priv = parentCertKey - } - - derBytes, err := x509.CreateCertificate(rand.Reader, &template, parent, &privateKey.PublicKey, priv) - if err != nil { - return nil, nil, errors.Trace(err) - } - - cert, err := x509.ParseCertificate(derBytes) - if err != nil { - return nil, nil, errors.Trace(err) - } - - certOut, err := os.Create(outCertFile) - if err != nil { - return nil, nil, errors.Trace(err) - } - err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) - if err != nil { - return nil, nil, errors.Trace(err) - } - err = certOut.Close() - if err != nil { - return nil, nil, errors.Trace(err) - } - - keyOut, err := os.OpenFile(outKeyFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return nil, nil, errors.Trace(err) - } - err = pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) - if err != nil { - return nil, nil, errors.Trace(err) - } - err = keyOut.Close() - if err != nil { - return nil, nil, errors.Trace(err) - } - - return cert, privateKey, nil -} - -// registerTLSConfig registers a mysql client TLS config. -// See https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig for details. -func registerTLSConfig(configName string, caCertPath string, clientCertPath string, clientKeyPath string, serverName string, verifyServer bool) error { - rootCertPool := x509.NewCertPool() - data, err := os.ReadFile(caCertPath) - if err != nil { - return err - } - if ok := rootCertPool.AppendCertsFromPEM(data); !ok { - return errors.New("Failed to append PEM") - } - clientCert := make([]tls.Certificate, 0, 1) - certs, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath) - if err != nil { - return err - } - clientCert = append(clientCert, certs) - tlsConfig := &tls.Config{ - RootCAs: rootCertPool, - Certificates: clientCert, - ServerName: serverName, - InsecureSkipVerify: !verifyServer, - } - return mysql.RegisterTLSConfig(configName, tlsConfig) -} - -func TestSystemTimeZone(t *testing.T) { - ts := createTidbTestSuite(t) - - tk := testkit.NewTestKit(t, ts.store) - cfg := util2.NewTestConfig() - cfg.Port, cfg.Status.StatusPort = 0, 0 - cfg.Status.ReportStatus = false - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - defer server.Close() - - tz1 := tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'").Rows() - tk.MustQuery("select @@system_time_zone").Check(tz1) -} - -func TestInternalSessionTxnStartTS(t *testing.T) { - ts := createTidbTestSuite(t) - - se, err := session.CreateSession4Test(ts.store) - require.NoError(t, err) - - _, err = se.Execute(context.Background(), "set global tidb_enable_metadata_lock=0") - require.NoError(t, err) - - count := 10 - stmts := make([]ast.StmtNode, count) - for i := 0; i < count; i++ { - stmt, err := session.ParseWithParams4Test(context.Background(), se, "select * from mysql.user limit 1") - require.NoError(t, err) - stmts[i] = stmt - } - // Test an issue that sysSessionPool doesn't call session's Close, cause - // asyncGetTSWorker goroutine leak. - var wg util.WaitGroupWrapper - for i := 0; i < count; i++ { - s := stmts[i] - wg.Run(func() { - _, _, err := session.ExecRestrictedStmt4Test(context.Background(), se, s) - require.NoError(t, err) - }) - } - - wg.Wait() -} - -func TestClientWithCollation(t *testing.T) { - ts := createTidbTestSuite(t) - - ts.RunTestClientWithCollation(t) -} - -func TestCreateTableFlen(t *testing.T) { - ts := createTidbTestSuite(t) - - // issue #4540 - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - _, err = Execute(context.Background(), qctx, "use test;") - require.NoError(t, err) - - ctx := context.Background() - testSQL := "CREATE TABLE `t1` (" + - "`a` char(36) NOT NULL," + - "`b` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + - "`c` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + - "`d` varchar(50) DEFAULT ''," + - "`e` char(36) NOT NULL DEFAULT ''," + - "`f` char(36) NOT NULL DEFAULT ''," + - "`g` char(1) NOT NULL DEFAULT 'N'," + - "`h` varchar(100) NOT NULL," + - "`i` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP," + - "`j` varchar(10) DEFAULT ''," + - "`k` varchar(10) DEFAULT ''," + - "`l` varchar(20) DEFAULT ''," + - "`m` varchar(20) DEFAULT ''," + - "`n` varchar(30) DEFAULT ''," + - "`o` varchar(100) DEFAULT ''," + - "`p` varchar(50) DEFAULT ''," + - "`q` varchar(50) DEFAULT ''," + - "`r` varchar(100) DEFAULT ''," + - "`s` varchar(20) DEFAULT ''," + - "`t` varchar(50) DEFAULT ''," + - "`u` varchar(100) DEFAULT ''," + - "`v` varchar(50) DEFAULT ''," + - "`w` varchar(300) NOT NULL," + - "`x` varchar(250) DEFAULT ''," + - "`y` decimal(20)," + - "`z` decimal(20, 4)," + - "PRIMARY KEY (`a`)" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" - _, err = Execute(ctx, qctx, testSQL) - require.NoError(t, err) - rs, err := Execute(ctx, qctx, "show create table t1") - require.NoError(t, err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - cols := rs.Columns() - require.NoError(t, err) - require.Len(t, cols, 2) - require.Equal(t, 5*tmysql.MaxBytesOfCharacter, int(cols[0].ColumnLength)) - require.Equal(t, len(req.GetRow(0).GetString(1))*tmysql.MaxBytesOfCharacter, int(cols[1].ColumnLength)) - - // for issue#5246 - rs, err = Execute(ctx, qctx, "select y, z from t1") - require.NoError(t, err) - cols = rs.Columns() - require.Len(t, cols, 2) - require.Equal(t, 21, int(cols[0].ColumnLength)) - require.Equal(t, 22, int(cols[1].ColumnLength)) - rs.Close() -} - -func Execute(ctx context.Context, qc *server2.TiDBContext, sql string) (resultset.ResultSet, error) { - stmts, err := qc.Parse(ctx, sql) - if err != nil { - return nil, err - } - if len(stmts) != 1 { - panic("wrong input for Execute: " + sql) - } - return qc.ExecuteStmt(ctx, stmts[0]) -} - -func TestShowTablesFlen(t *testing.T) { - ts := createTidbTestSuite(t) - - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - ctx := context.Background() - _, err = Execute(ctx, qctx, "use test;") - require.NoError(t, err) - - testSQL := "create table abcdefghijklmnopqrstuvwxyz (i int)" - _, err = Execute(ctx, qctx, testSQL) - require.NoError(t, err) - rs, err := Execute(ctx, qctx, "show tables") - require.NoError(t, err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - require.NoError(t, err) - cols := rs.Columns() - require.NoError(t, err) - require.Len(t, cols, 1) - require.Equal(t, 26*tmysql.MaxBytesOfCharacter, int(cols[0].ColumnLength)) -} - -func checkColNames(t *testing.T, columns []*column.Info, names ...string) { - for i, name := range names { - require.Equal(t, name, columns[i].Name) - require.Equal(t, name, columns[i].OrgName) - } -} - -func TestFieldList(t *testing.T) { - ts := createTidbTestSuite(t) - - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - _, err = Execute(context.Background(), qctx, "use test;") - require.NoError(t, err) - - ctx := context.Background() - testSQL := `create table t ( - c_bit bit(10), - c_int_d int, - c_bigint_d bigint, - c_float_d float, - c_double_d double, - c_decimal decimal(6, 3), - c_datetime datetime(2), - c_time time(3), - c_date date, - c_timestamp timestamp(4) DEFAULT CURRENT_TIMESTAMP(4), - c_char char(20), - c_varchar varchar(20), - c_text_d text, - c_binary binary(20), - c_blob_d blob, - c_set set('a', 'b', 'c'), - c_enum enum('a', 'b', 'c'), - c_json JSON, - c_year year - )` - _, err = Execute(ctx, qctx, testSQL) - require.NoError(t, err) - colInfos, err := qctx.FieldList("t") - require.NoError(t, err) - require.Len(t, colInfos, 19) - - checkColNames(t, colInfos, "c_bit", "c_int_d", "c_bigint_d", "c_float_d", - "c_double_d", "c_decimal", "c_datetime", "c_time", "c_date", "c_timestamp", - "c_char", "c_varchar", "c_text_d", "c_binary", "c_blob_d", "c_set", "c_enum", - "c_json", "c_year") - - for _, cols := range colInfos { - require.Equal(t, "test", cols.Schema) - } - - for _, cols := range colInfos { - require.Equal(t, "t", cols.Table) - } - - for i, col := range colInfos { - switch i { - case 10, 11, 12, 15, 16: - // c_char char(20), c_varchar varchar(20), c_text_d text, - // c_set set('a', 'b', 'c'), c_enum enum('a', 'b', 'c') - require.Equalf(t, uint16(tmysql.CharsetNameToID(tmysql.DefaultCharset)), col.Charset, "index %d", i) - continue - } - - require.Equalf(t, uint16(tmysql.CharsetNameToID("binary")), col.Charset, "index %d", i) - } - - // c_decimal decimal(6, 3) - require.Equal(t, uint8(3), colInfos[5].Decimal) - - // for issue#10513 - tooLongColumnAsName := "COALESCE(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,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,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,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,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,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,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,0,0,0,0)" - columnAsName := tooLongColumnAsName[:tmysql.MaxAliasIdentifierLen] - - rs, err := Execute(ctx, qctx, "select "+tooLongColumnAsName) - require.NoError(t, err) - cols := rs.Columns() - require.Equal(t, "", cols[0].OrgName) - require.Equal(t, columnAsName, cols[0].Name) - rs.Close() - - rs, err = Execute(ctx, qctx, "select c_bit as '"+tooLongColumnAsName+"' from t") - require.NoError(t, err) - cols = rs.Columns() - require.Equal(t, "c_bit", cols[0].OrgName) - require.Equal(t, columnAsName, cols[0].Name) - rs.Close() -} - -func TestClientErrors(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestInfoschemaClientErrors(t) -} - -func TestInitConnect(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestInitConnect(t) -} - -func TestSumAvg(t *testing.T) { - ts := createTidbTestSuite(t) - ts.RunTestSumAvg(t) -} - -func TestNullFlag(t *testing.T) { - ts := createTidbTestSuite(t) - - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - - ctx := context.Background() - { - // issue #9689 - rs, err := Execute(ctx, qctx, "select 1") - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.NotNullFlag | tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } - - { - // issue #19025 - rs, err := Execute(ctx, qctx, "select convert('{}', JSON)") - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } - - { - // issue #18488 - _, err := Execute(ctx, qctx, "use test") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "CREATE TABLE `test` (`iD` bigint(20) NOT NULL, `INT_TEST` int(11) DEFAULT NULL);") - require.NoError(t, err) - rs, err := Execute(ctx, qctx, `SELECT id + int_test as res FROM test GROUP BY res ORDER BY res;`) - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } - - { - rs, err := Execute(ctx, qctx, "select if(1, null, 1) ;") - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } - { - rs, err := Execute(ctx, qctx, "select CASE 1 WHEN 2 THEN 1 END ;") - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } - { - rs, err := Execute(ctx, qctx, "select NULL;") - require.NoError(t, err) - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.BinaryFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) - rs.Close() - } -} - -func TestNO_DEFAULT_VALUEFlag(t *testing.T) { - ts := createTidbTestSuite(t) - - // issue #21465 - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - - ctx := context.Background() - _, err = Execute(ctx, qctx, "use test") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "drop table if exists t") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "create table t(c1 int key, c2 int);") - require.NoError(t, err) - rs, err := Execute(ctx, qctx, "select c1 from t;") - require.NoError(t, err) - defer rs.Close() - cols := rs.Columns() - require.Len(t, cols, 1) - expectFlag := uint16(tmysql.NotNullFlag | tmysql.PriKeyFlag | tmysql.NoDefaultValueFlag) - require.Equal(t, expectFlag, column.DumpFlag(cols[0].Type, cols[0].Flag)) -} - -func TestGracefulShutdown(t *testing.T) { - ts := createTidbTestSuite(t) - - cli := testserverclient.NewTestServerClient() - cfg := util2.NewTestConfig() - cfg.GracefulWaitBeforeShutdown = 2 // wait before shutdown - cfg.Port = 0 - cfg.Status.StatusPort = 0 - cfg.Status.ReportStatus = true - cfg.Performance.TCPKeepAlive = true - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - require.NotNil(t, server) - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - cli.StatusPort = testutil.GetPortFromTCPAddr(server.StatusListenerAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - - resp, err := cli.FetchStatus("/status") // server is up - require.NoError(t, err) - require.Nil(t, resp.Body.Close()) - - go server.Close() - time.Sleep(time.Millisecond * 500) - - resp, _ = cli.FetchStatus("/status") // should return 5xx code - require.Equal(t, 500, resp.StatusCode) - require.Nil(t, resp.Body.Close()) - - time.Sleep(time.Second * 2) - - //nolint:bodyclose - _, err = cli.FetchStatus("/status") // Status is gone - require.Error(t, err) - require.Regexp(t, "connect: connection refused$", err.Error()) -} - -func TestPessimisticInsertSelectForUpdate(t *testing.T) { - ts := createTidbTestSuite(t) - - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - defer qctx.Close() - ctx := context.Background() - _, err = Execute(ctx, qctx, "use test;") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "drop table if exists t1, t2") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "create table t1 (id int)") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "create table t2 (id int)") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "insert into t1 select 1") - require.NoError(t, err) - _, err = Execute(ctx, qctx, "begin pessimistic") - require.NoError(t, err) - rs, err := Execute(ctx, qctx, "INSERT INTO t2 (id) select id from t1 where id = 1 for update") - require.NoError(t, err) - require.Nil(t, rs) // should be no delay -} - -func TestTopSQLCatchRunningSQL(t *testing.T) { - ts := createTidbTestTopSQLSuite(t) - - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - require.NoError(t, db.Close()) - }() - - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("drop database if exists topsql") - dbt.MustExec("create database topsql") - dbt.MustExec("use topsql;") - dbt.MustExec("create table t (a int, b int);") - - for i := 0; i < 5000; i++ { - dbt.MustExec(fmt.Sprintf("insert into t values (%v, %v)", i, i)) - } - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachPlan", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachPlan")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop")) - }() - - mc := mockTopSQLTraceCPU.NewTopSQLCollector() - topsql.SetupTopSQLForTest(mc) - sqlCPUCollector := collector.NewSQLCPUCollector(mc) - sqlCPUCollector.Start() - defer sqlCPUCollector.Stop() - - query := "select count(*) from t as t0 join t as t1 on t0.a != t1.a;" - needEnableTopSQL := int64(0) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) - defer cancel() - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - for { - select { - case <-ctx.Done(): - return - default: - } - if atomic.LoadInt64(&needEnableTopSQL) == 1 { - time.Sleep(2 * time.Millisecond) - topsqlstate.EnableTopSQL() - atomic.StoreInt64(&needEnableTopSQL, 0) - } - time.Sleep(time.Millisecond) - } - }() - execFn := func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - atomic.StoreInt64(&needEnableTopSQL, 1) - mustQuery(t, dbt, query) - topsqlstate.DisableTopSQL() - } - check := func() { - require.NoError(t, ctx.Err()) - stats := mc.GetSQLStatsBySQLWithRetry(query, true) - require.Greaterf(t, len(stats), 0, query) - } - ts.testCase(t, mc, execFn, check) - cancel() - wg.Wait() -} - -func TestTopSQLCPUProfile(t *testing.T) { - ts := createTidbTestTopSQLSuite(t) - - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - require.NoError(t, db.Close()) - }() - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachSQL", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachPlan", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachSQL")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/util/topsql/mockHighLoadForEachPlan")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop")) - }() - - topsqlstate.EnableTopSQL() - defer topsqlstate.DisableTopSQL() - - mc := mockTopSQLTraceCPU.NewTopSQLCollector() - topsql.SetupTopSQLForTest(mc) - sqlCPUCollector := collector.NewSQLCPUCollector(mc) - sqlCPUCollector.Start() - defer sqlCPUCollector.Stop() - - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("drop database if exists topsql") - dbt.MustExec("create database topsql") - dbt.MustExec("use topsql;") - dbt.MustExec("create table t (a int auto_increment, b int, unique index idx(a));") - dbt.MustExec("create table t1 (a int auto_increment, b int, unique index idx(a));") - dbt.MustExec("create table t2 (a int auto_increment, b int, unique index idx(a));") - dbt.MustExec("set @@global.tidb_txn_mode = 'pessimistic'") - - checkFn := func(sql, planRegexp string) { - stats := mc.GetSQLStatsBySQLWithRetry(sql, len(planRegexp) > 0) - // since 1 sql may has many plan, check `len(stats) > 0` instead of `len(stats) == 1`. - require.Greaterf(t, len(stats), 0, "sql: "+sql) - - for _, s := range stats { - sqlStr := mc.GetSQL(s.SQLDigest) - encodedPlan := mc.GetPlan(s.PlanDigest) - // Normalize the user SQL before check. - normalizedSQL := parser.Normalize(sql) - require.Equalf(t, normalizedSQL, sqlStr, "sql: %v", sql) - // decode plan before check. - normalizedPlan, err := plancodec.DecodeNormalizedPlan(encodedPlan) - require.NoError(t, err) - // remove '\n' '\t' before do regexp match. - normalizedPlan = strings.Replace(normalizedPlan, "\n", " ", -1) - normalizedPlan = strings.Replace(normalizedPlan, "\t", " ", -1) - require.Regexpf(t, planRegexp, normalizedPlan, "sql: %v", sql) - } - } - - // Test case 1: DML query: insert/update/replace/delete/select - cases1 := []struct { - sql string - planRegexp string - }{ - {sql: "insert into t () values (),(),(),(),(),(),();", planRegexp: ""}, - {sql: "insert into t (b) values (1),(1),(1),(1),(1),(1),(1),(1);", planRegexp: ""}, - {sql: "update t set b=a where b is null limit 1;", planRegexp: ".*Limit.*TableReader.*"}, - {sql: "delete from t where b = a limit 2;", planRegexp: ".*Limit.*TableReader.*"}, - {sql: "replace into t (b) values (1),(1),(1),(1),(1),(1),(1),(1);", planRegexp: ""}, - {sql: "select * from t use index(idx) where a<10;", planRegexp: ".*IndexLookUp.*"}, - {sql: "select * from t ignore index(idx) where a>1000000000;", planRegexp: ".*TableReader.*"}, - {sql: "select /*+ HASH_JOIN(t1, t2) */ * from t t1 join t t2 on t1.a=t2.a where t1.b is not null;", planRegexp: ".*HashJoin.*"}, - {sql: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t t1 join t t2 on t2.a=t1.a where t1.b is not null;", planRegexp: ".*IndexHashJoin.*"}, - {sql: "select * from t where a=1;", planRegexp: ".*Point_Get.*"}, - {sql: "select * from t where a in (1,2,3,4)", planRegexp: ".*Batch_Point_Get.*"}, - } - execFn := func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases1 { - sqlStr := ca.sql - if strings.HasPrefix(sqlStr, "select") { - mustQuery(t, dbt, sqlStr) - } else { - dbt.MustExec(sqlStr) - } - } - } - check := func() { - for _, ca := range cases1 { - checkFn(ca.sql, ca.planRegexp) - } - } - ts.testCase(t, mc, execFn, check) - - // Test case 2: prepare/execute sql - cases2 := []struct { - prepare string - args []interface{} - planRegexp string - }{ - {prepare: "insert into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, - {prepare: "replace into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, - {prepare: "update t1 set b=a where b is null limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, - {prepare: "delete from t1 where b = a limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, - {prepare: "replace into t1 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, - {prepare: "select * from t1 use index(idx) where a?;", args: []interface{}{1000000000}, planRegexp: ".*TableReader.*"}, - {prepare: "select /*+ HASH_JOIN(t1, t2) */ * from t1 t1 join t1 t2 on t1.a=t2.a where t1.b is not null;", args: nil, planRegexp: ".*HashJoin.*"}, - {prepare: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t1 t1 join t1 t2 on t2.a=t1.a where t1.b is not null;", args: nil, planRegexp: ".*IndexHashJoin.*"}, - {prepare: "select * from t1 where a=?;", args: []interface{}{1}, planRegexp: ".*Point_Get.*"}, - {prepare: "select * from t1 where a in (?,?,?,?)", args: []interface{}{1, 2, 3, 4}, planRegexp: ".*Batch_Point_Get.*"}, - } - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases2 { - prepare, args := ca.prepare, ca.args - stmt := dbt.MustPrepare(prepare) - if strings.HasPrefix(prepare, "select") { - rows, err := stmt.Query(args...) - require.NoError(t, err) - for rows.Next() { - } - require.NoError(t, rows.Close()) - } else { - _, err = stmt.Exec(args...) - require.NoError(t, err) - } - } - } - check = func() { - for _, ca := range cases2 { - checkFn(ca.prepare, ca.planRegexp) - } - } - ts.testCase(t, mc, execFn, check) - - // Test case 3: prepare, execute stmt using @val... - cases3 := []struct { - prepare string - args []interface{} - planRegexp string - }{ - {prepare: "insert into t2 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, - {prepare: "update t2 set b=a where b is null limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, - {prepare: "delete from t2 where b = a limit ?;", args: []interface{}{1}, planRegexp: ".*Limit.*TableReader.*"}, - {prepare: "replace into t2 (b) values (?);", args: []interface{}{1}, planRegexp: ""}, - {prepare: "select * from t2 use index(idx) where a?;", args: []interface{}{1000000000}, planRegexp: ".*TableReader.*"}, - {prepare: "select /*+ HASH_JOIN(t1, t2) */ * from t2 t1 join t2 t2 on t1.a=t2.a where t1.b is not null;", args: nil, planRegexp: ".*HashJoin.*"}, - {prepare: "select /*+ INL_HASH_JOIN(t1, t2) */ * from t2 t1 join t2 t2 on t2.a=t1.a where t1.b is not null;", args: nil, planRegexp: ".*IndexHashJoin.*"}, - {prepare: "select * from t2 where a=?;", args: []interface{}{1}, planRegexp: ".*Point_Get.*"}, - {prepare: "select * from t2 where a in (?,?,?,?)", args: []interface{}{1, 2, 3, 4}, planRegexp: ".*Batch_Point_Get.*"}, - } - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases3 { - prepare, args := ca.prepare, ca.args - dbt.MustExec(fmt.Sprintf("prepare stmt from '%v'", prepare)) - - var params []string - for i := range args { - param := 'a' + i - dbt.MustExec(fmt.Sprintf("set @%c=%v", param, args[i])) - params = append(params, fmt.Sprintf("@%c", param)) - } - - sqlStr := "execute stmt" - if len(params) > 0 { - sqlStr += " using " - sqlStr += strings.Join(params, ",") - } - if strings.HasPrefix(prepare, "select") { - mustQuery(t, dbt, sqlStr) - } else { - dbt.MustExec(sqlStr) - } - } - } - check = func() { - for _, ca := range cases3 { - checkFn(ca.prepare, ca.planRegexp) - } - } - ts.testCase(t, mc, execFn, check) - - // Test case for other statements - cases4 := []struct { - sql string - plan string - isQuery bool - }{ - {"begin", "", false}, - {"insert into t () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),()", "", false}, - {"commit", "", false}, - {"analyze table t", "", false}, - {"explain analyze select sum(a+b) from t", ".*TableReader.*", true}, - {"trace select sum(b*a), sum(a+b) from t", "", true}, - {"set global tidb_stmt_summary_history_size=5;", "", false}, - } - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases4 { - if ca.isQuery { - mustQuery(t, dbt, ca.sql) - } else { - dbt.MustExec(ca.sql) - } - } - } - check = func() { - for _, ca := range cases4 { - checkFn(ca.sql, ca.plan) - } - // check for internal SQL. - checkFn("replace into mysql.global_variables (variable_name,variable_value) values ('tidb_stmt_summary_history_size', '5')", "") - } - ts.testCase(t, mc, execFn, check) - - // Test case for multi-statement. - cases5 := []string{ - "delete from t limit 1;", - "update t set b=1 where b is null limit 1;", - "select sum(a+b*2) from t;", - } - multiStatement5 := strings.Join(cases5, "") - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("SET tidb_multi_statement_mode='ON'") - dbt.MustExec(multiStatement5) - } - check = func() { - for _, sqlStr := range cases5 { - checkFn(sqlStr, ".*TableReader.*") - } - } - ts.testCase(t, mc, execFn, check) - - // Test case for multi-statement, but first statements execute failed - cases6 := []string{ - "delete from t_not_exist;", - "update t set a=1 where a is null limit 1;", - } - multiStatement6 := strings.Join(cases6, "") - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("SET tidb_multi_statement_mode='ON'") - _, err := db.Exec(multiStatement6) - require.NotNil(t, err) - require.Equal(t, "Error 1146: Table 'topsql.t_not_exist' doesn't exist", err.Error()) - } - check = func() { - for i := 1; i < len(cases6); i++ { - sqlStr := cases6[i] - stats := mc.GetSQLStatsBySQL(sqlStr, false) - require.Equal(t, 0, len(stats), sqlStr) - } - } - ts.testCase(t, mc, execFn, check) - - // Test case for multi-statement, the first statements execute success but the second statement execute failed. - cases7 := []string{ - "update t set a=1 where a <0 limit 1;", - "delete from t_not_exist;", - } - multiStatement7 := strings.Join(cases7, "") - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("SET tidb_multi_statement_mode='ON'") - _, err = db.Exec(multiStatement7) - require.NotNil(t, err) - require.Equal(t, "Error 1146 (42S02): Table 'topsql.t_not_exist' doesn't exist", err.Error()) - } - check = func() { - checkFn(cases7[0], "") // the first statement execute success, should have topsql data. - } - ts.testCase(t, mc, execFn, check) - - // Test case for statement with wrong syntax. - wrongSyntaxSQL := "select * froms t" - execFn = func(db *sql.DB) { - _, err = db.Exec(wrongSyntaxSQL) - require.NotNil(t, err) - require.Regexp(t, "Error 1064: You have an error in your SQL syntax...", err.Error()) - } - check = func() { - stats := mc.GetSQLStatsBySQL(wrongSyntaxSQL, false) - require.Equal(t, 0, len(stats), wrongSyntaxSQL) - } - ts.testCase(t, mc, execFn, check) - - // Test case for high cost of plan optimize. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/mockHighLoadForOptimize", "return")) - selectSQL := "select sum(a+b), count(distinct b) from t where a+b >0" - updateSQL := "update t set a=a+100 where a > 10000000" - selectInPlanSQL := "select * from t where exists (select 1 from t1 where t1.a = 1);" - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - mustQuery(t, dbt, selectSQL) - dbt.MustExec(updateSQL) - mustQuery(t, dbt, selectInPlanSQL) - } - check = func() { - checkFn(selectSQL, "") - checkFn(updateSQL, "") - selectCPUTime := mc.GetSQLCPUTimeBySQL(selectSQL) - updateCPUTime := mc.GetSQLCPUTimeBySQL(updateSQL) - require.Less(t, updateCPUTime, selectCPUTime) - checkFn(selectInPlanSQL, "") - } - ts.testCase(t, mc, execFn, check) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/mockHighLoadForOptimize")) - - // Test case for DDL execute failed but should still have CPU data. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockHighLoadForAddIndex", "return")) - dbt.MustExec(fmt.Sprintf("insert into t values (%v,%v), (%v, %v);", 2000, 1, 2001, 1)) - addIndexStr := "alter table t add unique index idx_b (b)" - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("alter table t drop index if exists idx_b") - _, err := db.Exec(addIndexStr) - require.NotNil(t, err) - require.Equal(t, "Error 1062 (23000): Duplicate entry '1' for key 't.idx_b'", err.Error()) - } - check = func() { - checkFn(addIndexStr, "") - } - ts.testCase(t, mc, execFn, check) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockHighLoadForAddIndex")) - - // Test case for execute failed cause by storage error. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/handleTaskOnceError", `return(true)`)) - execFailedQuery := "select * from t where a*b < 1000" - execFn = func(db *sql.DB) { - _, err = db.Query(execFailedQuery) - require.NotNil(t, err) - require.Equal(t, "Error 1105 (HY000): mock handleTaskOnce error", err.Error()) - } - check = func() { - checkFn(execFailedQuery, "") - } - ts.testCase(t, mc, execFn, check) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/handleTaskOnceError")) -} - -func (ts *tidbTestTopSQLSuite) testCase(t *testing.T, mc *mockTopSQLTraceCPU.TopSQLCollector, execFn func(db *sql.DB), checkFn func()) { - var wg sync.WaitGroup - ctx, cancel := context.WithCancel(context.Background()) - wg.Add(1) - go func() { - defer wg.Done() - ts.loopExec(ctx, t, execFn) - }() - - checkFn() - cancel() - wg.Wait() - mc.Reset() -} - -func mustQuery(t *testing.T, dbt *testkit.DBTestKit, query string) { - rows := dbt.MustQuery(query) - for rows.Next() { - } - err := rows.Close() - require.NoError(t, err) -} - -type mockCollector struct { - f func(data stmtstats.StatementStatsMap) -} - -func newMockCollector(f func(data stmtstats.StatementStatsMap)) stmtstats.Collector { - return &mockCollector{f: f} -} - -func (c *mockCollector) CollectStmtStatsMap(data stmtstats.StatementStatsMap) { - c.f(data) -} - -func waitCollected(ch chan struct{}) { - select { - case <-ch: - case <-time.After(time.Second * 3): - } -} - -func TestTopSQLStatementStats(t *testing.T) { - ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) - - const ExecCountPerSQL = 2 - // Test for CRUD. - cases1 := []string{ - "insert into t values (%d, sleep(0.1))", - "update t set a = %[1]d + 1000 where a = %[1]d and sleep(0.1);", - "select a from t where b = %d and sleep(0.1);", - "select a from t where a = %d and sleep(0.1);", // test for point-get - "delete from t where a = %d and sleep(0.1);", - "insert into t values (%d, sleep(0.1)) on duplicate key update b = b+1", - } - var wg sync.WaitGroup - sqlDigests := map[stmtstats.BinaryDigest]string{} - for i, ca := range cases1 { - sqlStr := fmt.Sprintf(ca, i) - _, digest := parser.NormalizeDigest(sqlStr) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = sqlStr - } - wg.Add(1) - go func() { - defer wg.Done() - for _, ca := range cases1 { - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use stmtstats;") - for n := 0; n < ExecCountPerSQL; n++ { - sqlStr := fmt.Sprintf(ca, n) - if strings.HasPrefix(strings.ToLower(sqlStr), "select") { - mustQuery(t, dbt, sqlStr) - } else { - dbt.MustExec(sqlStr) - } - } - err = db.Close() - require.NoError(t, err) - } - }() - - // Test for prepare stmt/execute stmt - cases2 := []struct { - prepare string - execStmt string - setSQLsGen func(idx int) []string - execSQL string - }{ - { - prepare: "prepare stmt from 'insert into t2 values (?, sleep(?))';", - execStmt: "insert into t2 values (1, sleep(0.1))", - setSQLsGen: func(idx int) []string { - return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - prepare: "prepare stmt from 'update t2 set a = a + 1000 where a = ? and sleep(?);';", - execStmt: "update t2 set a = a + 1000 where a = 1 and sleep(0.1);", - setSQLsGen: func(idx int) []string { - return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - // test for point-get - prepare: "prepare stmt from 'select a, sleep(?) from t2 where a = ?';", - execStmt: "select a, sleep(?) from t2 where a = ?", - setSQLsGen: func(idx int) []string { - return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - prepare: "prepare stmt from 'select a, sleep(?) from t2 where b = ?';", - execStmt: "select a, sleep(?) from t2 where b = ?", - setSQLsGen: func(idx int) []string { - return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - prepare: "prepare stmt from 'delete from t2 where sleep(?) and a = ?';", - execStmt: "delete from t2 where sleep(0.1) and a = 1", - setSQLsGen: func(idx int) []string { - return []string{"set @a=0.1", fmt.Sprintf("set @b=%v", idx)} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - prepare: "prepare stmt from 'insert into t2 values (?, sleep(?)) on duplicate key update b = b+1';", - execStmt: "insert into t2 values (1, sleep(0.1)) on duplicate key update b = b+1", - setSQLsGen: func(idx int) []string { - return []string{fmt.Sprintf("set @a=%v", idx), "set @b=0.1"} - }, - execSQL: "execute stmt using @a, @b;", - }, - { - prepare: "prepare stmt from 'set global tidb_enable_top_sql = (? = sleep(?))';", - execStmt: "set global tidb_enable_top_sql = (0 = sleep(0.1))", - setSQLsGen: func(idx int) []string { - return []string{"set @a=0", "set @b=0.1"} - }, - execSQL: "execute stmt using @a, @b;", - }, - } - for _, ca := range cases2 { - _, digest := parser.NormalizeDigest(ca.execStmt) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.execStmt - } - wg.Add(1) - go func() { - defer wg.Done() - for _, ca := range cases2 { - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use stmtstats;") - // prepare stmt - dbt.MustExec(ca.prepare) - for n := 0; n < ExecCountPerSQL; n++ { - setSQLs := ca.setSQLsGen(n) - for _, setSQL := range setSQLs { - dbt.MustExec(setSQL) - } - if strings.HasPrefix(strings.ToLower(ca.execStmt), "select") { - mustQuery(t, dbt, ca.execSQL) - } else { - dbt.MustExec(ca.execSQL) - } - } - err = db.Close() - require.NoError(t, err) - } - }() - - // Test for prepare by db client prepare/exec interface. - cases3 := []struct { - prepare string - execStmt string - argsGen func(idx int) []interface{} - }{ - { - prepare: "insert into t3 values (?, sleep(?))", - argsGen: func(idx int) []interface{} { - return []interface{}{idx, 0.1} - }, - }, - { - prepare: "update t3 set a = a + 1000 where a = ? and sleep(?)", - argsGen: func(idx int) []interface{} { - return []interface{}{idx, 0.1} - }, - }, - { - // test for point-get - prepare: "select a, sleep(?) from t3 where a = ?", - argsGen: func(idx int) []interface{} { - return []interface{}{0.1, idx} - }, - }, - { - prepare: "select a, sleep(?) from t3 where b = ?", - argsGen: func(idx int) []interface{} { - return []interface{}{0.1, idx} - }, - }, - { - prepare: "delete from t3 where sleep(?) and a = ?", - argsGen: func(idx int) []interface{} { - return []interface{}{0.1, idx} - }, - }, - { - prepare: "insert into t3 values (?, sleep(?)) on duplicate key update b = b+1", - argsGen: func(idx int) []interface{} { - return []interface{}{idx, 0.1} - }, - }, - { - prepare: "set global tidb_enable_1pc = (? = sleep(?))", - argsGen: func(idx int) []interface{} { - return []interface{}{0, 0.1} - }, - }, - } - for _, ca := range cases3 { - _, digest := parser.NormalizeDigest(ca.prepare) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.prepare - } - wg.Add(1) - go func() { - defer wg.Done() - for _, ca := range cases3 { - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use stmtstats;") - // prepare stmt - stmt, err := db.Prepare(ca.prepare) - require.NoError(t, err) - for n := 0; n < ExecCountPerSQL; n++ { - args := ca.argsGen(n) - if strings.HasPrefix(strings.ToLower(ca.prepare), "select") { - row, err := stmt.Query(args...) - require.NoError(t, err) - err = row.Close() - require.NoError(t, err) - } else { - _, err := stmt.Exec(args...) - require.NoError(t, err) - } - } - err = db.Close() - require.NoError(t, err) - } - }() - - wg.Wait() - // Wait for collect. - waitCollected(collectedNotifyCh) - - found := 0 - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - found++ - require.Equal(t, uint64(ExecCountPerSQL), item.ExecCount, sqlStr) - require.Equal(t, uint64(ExecCountPerSQL), item.DurationCount, sqlStr) - require.True(t, item.SumDurationNs > uint64(time.Millisecond*100*ExecCountPerSQL), sqlStr) - require.True(t, item.SumDurationNs < uint64(time.Millisecond*300*ExecCountPerSQL), sqlStr) - if strings.HasPrefix(sqlStr, "set global") { - // set global statement use internal SQL to change global variable, so itself doesn't have KV request. - continue - } - var kvSum uint64 - for _, kvCount := range item.KvStatsItem.KvExecCount { - kvSum += kvCount - } - require.Equal(t, uint64(ExecCountPerSQL), kvSum) - tagChecker.checkExist(t, digest.SQLDigest, sqlStr) - } - } - require.Equal(t, len(sqlDigests), found) - require.Equal(t, 20, found) -} - -type resourceTagChecker struct { - sync.Mutex - sqlDigest2Reqs map[stmtstats.BinaryDigest]map[tikvrpc.CmdType]struct{} -} - -func (c *resourceTagChecker) checkExist(t *testing.T, digest stmtstats.BinaryDigest, sqlStr string) { - if strings.HasPrefix(sqlStr, "set global") { - // `set global` statement will use another internal sql to execute, so `set global` statement won't - // send RPC request. - return - } - if strings.HasPrefix(sqlStr, "trace") { - // `trace` statement will use another internal sql to execute, so remove the `trace` prefix before check. - _, sqlDigest := parser.NormalizeDigest(strings.TrimPrefix(sqlStr, "trace")) - digest = stmtstats.BinaryDigest(sqlDigest.Bytes()) - } - - c.Lock() - defer c.Unlock() - _, ok := c.sqlDigest2Reqs[digest] - require.True(t, ok, sqlStr) -} - -func (c *resourceTagChecker) checkReqExist(t *testing.T, digest stmtstats.BinaryDigest, sqlStr string, reqs ...tikvrpc.CmdType) { - if len(reqs) == 0 { - return - } - c.Lock() - defer c.Unlock() - reqMap, ok := c.sqlDigest2Reqs[digest] - require.True(t, ok, sqlStr) - for _, req := range reqs { - _, ok := reqMap[req] - require.True(t, ok, fmt.Sprintf("sql: %v, expect: %v, got: %v", sqlStr, reqs, reqMap)) - } -} - -func setupForTestTopSQLStatementStats(t *testing.T) (*tidbTestSuite, stmtstats.StatementStatsMap, *resourceTagChecker, chan struct{}) { - // Prepare stmt stats. - stmtstats.SetupAggregator() - - // Register stmt stats collector. - var mu sync.Mutex - collectedNotifyCh := make(chan struct{}) - total := stmtstats.StatementStatsMap{} - mockCollector := newMockCollector(func(data stmtstats.StatementStatsMap) { - mu.Lock() - defer mu.Unlock() - total.Merge(data) - select { - case collectedNotifyCh <- struct{}{}: - default: - } - }) - stmtstats.RegisterCollector(mockCollector) - - ts := createTidbTestSuite(t) - - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - err := db.Close() - require.NoError(t, err) - }() - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop", `return(true)`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCClientSendHook", `return(true)`)) - - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("drop database if exists stmtstats") - dbt.MustExec("create database stmtstats") - dbt.MustExec("use stmtstats;") - dbt.MustExec("create table t (a int, b int, unique index idx(a));") - dbt.MustExec("create table t2 (a int, b int, unique index idx(a));") - dbt.MustExec("create table t3 (a int, b int, unique index idx(a));") - - // Enable TopSQL - topsqlstate.EnableTopSQL() - config.UpdateGlobal(func(conf *config.Config) { - conf.TopSQL.ReceiverAddress = "mock-agent" - }) - - tagChecker := &resourceTagChecker{ - sqlDigest2Reqs: make(map[stmtstats.BinaryDigest]map[tikvrpc.CmdType]struct{}), - } - unistoreRPCClientSendHook := func(req *tikvrpc.Request) { - tag := req.GetResourceGroupTag() - if len(tag) == 0 || ddlutil.IsInternalResourceGroupTaggerForTopSQL(tag) { - // Ignore for internal background request. - return - } - sqlDigest, err := resourcegrouptag.DecodeResourceGroupTag(tag) - require.NoError(t, err) - tagChecker.Lock() - defer tagChecker.Unlock() - - reqMap, ok := tagChecker.sqlDigest2Reqs[stmtstats.BinaryDigest(sqlDigest)] - if !ok { - reqMap = make(map[tikvrpc.CmdType]struct{}) - } - reqMap[req.Type] = struct{}{} - tagChecker.sqlDigest2Reqs[stmtstats.BinaryDigest(sqlDigest)] = reqMap - } - unistore.UnistoreRPCClientSendHook.Store(&unistoreRPCClientSendHook) - - t.Cleanup(func() { - stmtstats.UnregisterCollector(mockCollector) - err = failpoint.Disable("github.com/pingcap/tidb/domain/skipLoadSysVarCacheLoop") - require.NoError(t, err) - err = failpoint.Disable("github.com/pingcap/tidb/store/mockstore/unistore/unistoreRPCClientSendHook") - require.NoError(t, err) - stmtstats.CloseAggregator() - view.Stop() - }) - - return ts, total, tagChecker, collectedNotifyCh -} - -func TestTopSQLStatementStats2(t *testing.T) { - ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) - - const ExecCountPerSQL = 3 - sqlDigests := map[stmtstats.BinaryDigest]string{} - - // Test case for other statements - cases4 := []struct { - sql string - plan string - isQuery bool - }{ - {"insert into t () values (),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),(),()", "", false}, - {"analyze table t", "", false}, - {"explain analyze select sum(a+b) from t", ".*TableReader.*", true}, - {"trace select sum(b*a), sum(a+b) from t", "", true}, - {"set global tidb_stmt_summary_history_size=5;", "", false}, - {"select * from stmtstats.t where exists (select 1 from stmtstats.t2 where t2.a = 1);", ".*TableReader.*", true}, - } - executeCaseFn := func(execFn func(db *sql.DB)) { - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use stmtstats;") - require.NoError(t, err) - - for n := 0; n < ExecCountPerSQL; n++ { - execFn(db) - } - err = db.Close() - require.NoError(t, err) - } - execFn := func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases4 { - if ca.isQuery { - mustQuery(t, dbt, ca.sql) - } else { - dbt.MustExec(ca.sql) - } - } - } - for _, ca := range cases4 { - _, digest := parser.NormalizeDigest(ca.sql) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.sql - } - executeCaseFn(execFn) - - // Test case for multi-statement. - cases5 := []string{ - "delete from t limit 1;", - "update t set b=1 where b is null limit 1;", - "select sum(a+b*2) from t;", - } - multiStatement5 := strings.Join(cases5, "") - // Test case for multi-statement, but first statements execute failed - cases6 := []string{ - "delete from t6_not_exist;", - "update t set a=1 where a is null limit 1;", - } - multiStatement6 := strings.Join(cases6, "") - // Test case for multi-statement, the first statements execute success but the second statement execute failed. - cases7 := []string{ - "update t set a=1 where a <0 limit 1;", - "delete from t7_not_exist;", - } - // Test case for DDL. - cases8 := []string{ - "create table if not exists t10 (a int, b int)", - "alter table t drop index if exists idx_b", - "alter table t add index idx_b (b)", - } - multiStatement7 := strings.Join(cases7, "") - execFn = func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("SET tidb_multi_statement_mode='ON'") - dbt.MustExec(multiStatement5) - - _, err := db.Exec(multiStatement6) - require.NotNil(t, err) - require.Equal(t, "Error 1146 (42S02): Table 'stmtstats.t6_not_exist' doesn't exist", err.Error()) - - _, err = db.Exec(multiStatement7) - require.NotNil(t, err) - require.Equal(t, "Error 1146 (42S02): Table 'stmtstats.t7_not_exist' doesn't exist", err.Error()) - - for _, ca := range cases8 { - dbt.MustExec(ca) - } - } - executeCaseFn(execFn) - sqlStrs := append([]string{}, cases5...) - sqlStrs = append(sqlStrs, cases7[0]) - sqlStrs = append(sqlStrs, cases8...) - for _, sqlStr := range sqlStrs { - _, digest := parser.NormalizeDigest(sqlStr) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = sqlStr - } - - // Wait for collect. - waitCollected(collectedNotifyCh) - - foundMap := map[stmtstats.BinaryDigest]string{} - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - require.Equal(t, uint64(ExecCountPerSQL), item.ExecCount, sqlStr) - require.True(t, item.SumDurationNs > 1, sqlStr) - foundMap[digest.SQLDigest] = sqlStr - tagChecker.checkExist(t, digest.SQLDigest, sqlStr) - // The special check uses to test the issue #33202. - if strings.Contains(strings.ToLower(sqlStr), "add index") { - tagChecker.checkReqExist(t, digest.SQLDigest, sqlStr, tikvrpc.CmdScan) - } - } - } - require.Equal(t, len(sqlDigests), len(foundMap), fmt.Sprintf("%v !=\n %v", sqlDigests, foundMap)) -} - -func TestTopSQLStatementStats3(t *testing.T) { - ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) - - err := failpoint.Enable("github.com/pingcap/tidb/executor/mockSleepInTableReaderNext", "return(2000)") - require.NoError(t, err) - defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/executor/mockSleepInTableReaderNext") - }() - - cases := []string{ - "select count(a+b) from stmtstats.t", - "select * from stmtstats.t where b is null", - "update stmtstats.t set b = 1 limit 10", - "delete from stmtstats.t limit 1", - } - var wg sync.WaitGroup - sqlDigests := map[stmtstats.BinaryDigest]string{} - for _, ca := range cases { - wg.Add(1) - go func(sqlStr string) { - defer wg.Done() - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - require.NoError(t, err) - if strings.HasPrefix(sqlStr, "select") { - mustQuery(t, dbt, sqlStr) - } else { - dbt.MustExec(sqlStr) - } - err = db.Close() - require.NoError(t, err) - }(ca) - _, digest := parser.NormalizeDigest(ca) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca - } - // Wait for collect. - waitCollected(collectedNotifyCh) - - foundMap := map[stmtstats.BinaryDigest]string{} - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - // since the SQL doesn't execute finish, the ExecCount should be recorded, - // but the DurationCount and SumDurationNs should be 0. - require.Equal(t, uint64(1), item.ExecCount, sqlStr) - require.Equal(t, uint64(0), item.DurationCount, sqlStr) - require.Equal(t, uint64(0), item.SumDurationNs, sqlStr) - foundMap[digest.SQLDigest] = sqlStr - } - } - - // wait sql execute finish. - wg.Wait() - // Wait for collect. - waitCollected(collectedNotifyCh) - - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - require.Equal(t, uint64(1), item.ExecCount, sqlStr) - require.Equal(t, uint64(1), item.DurationCount, sqlStr) - require.Less(t, uint64(0), item.SumDurationNs, sqlStr) - foundMap[digest.SQLDigest] = sqlStr - tagChecker.checkExist(t, digest.SQLDigest, sqlStr) - } - } -} - -func TestTopSQLStatementStats4(t *testing.T) { - ts, total, tagChecker, collectedNotifyCh := setupForTestTopSQLStatementStats(t) - - err := failpoint.Enable("github.com/pingcap/tidb/executor/mockSleepInTableReaderNext", "return(2000)") - require.NoError(t, err) - defer func() { - _ = failpoint.Disable("github.com/pingcap/tidb/executor/mockSleepInTableReaderNext") - }() - - cases := []struct { - prepare string - sql string - args []interface{} - }{ - {prepare: "select count(a+b) from stmtstats.t", sql: "select count(a+b) from stmtstats.t"}, - {prepare: "select * from stmtstats.t where b is null", sql: "select * from stmtstats.t where b is null"}, - {prepare: "update stmtstats.t set b = ? limit ?", sql: "update stmtstats.t set b = 1 limit 10", args: []interface{}{1, 10}}, - {prepare: "delete from stmtstats.t limit ?", sql: "delete from stmtstats.t limit 1", args: []interface{}{1}}, - } - var wg sync.WaitGroup - sqlDigests := map[stmtstats.BinaryDigest]string{} - for _, ca := range cases { - wg.Add(1) - go func(prepare string, args []interface{}) { - defer wg.Done() - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - stmt, err := db.Prepare(prepare) - require.NoError(t, err) - if strings.HasPrefix(prepare, "select") { - rows, err := stmt.Query(args...) - require.NoError(t, err) - for rows.Next() { - } - err = rows.Close() - require.NoError(t, err) - } else { - _, err := stmt.Exec(args...) - require.NoError(t, err) - } - err = db.Close() - require.NoError(t, err) - }(ca.prepare, ca.args) - _, digest := parser.NormalizeDigest(ca.sql) - sqlDigests[stmtstats.BinaryDigest(digest.Bytes())] = ca.sql - } - // Wait for collect. - waitCollected(collectedNotifyCh) - - foundMap := map[stmtstats.BinaryDigest]string{} - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - // since the SQL doesn't execute finish, the ExecCount should be recorded, - // but the DurationCount and SumDurationNs should be 0. - require.Equal(t, uint64(1), item.ExecCount, sqlStr) - require.Equal(t, uint64(0), item.DurationCount, sqlStr) - require.Equal(t, uint64(0), item.SumDurationNs, sqlStr) - foundMap[digest.SQLDigest] = sqlStr - } - } - - // wait sql execute finish. - wg.Wait() - // Wait for collect. - waitCollected(collectedNotifyCh) - - for digest, item := range total { - if sqlStr, ok := sqlDigests[digest.SQLDigest]; ok { - require.Equal(t, uint64(1), item.ExecCount, sqlStr) - require.Equal(t, uint64(1), item.DurationCount, sqlStr) - require.Less(t, uint64(0), item.SumDurationNs, sqlStr) - foundMap[digest.SQLDigest] = sqlStr - tagChecker.checkExist(t, digest.SQLDigest, sqlStr) - } - } -} - -func TestTopSQLResourceTag(t *testing.T) { - ts, _, tagChecker, _ := setupForTestTopSQLStatementStats(t) - defer func() { - topsqlstate.DisableTopSQL() - }() - - loadDataFile, err := os.CreateTemp("", "load_data_test0.csv") - require.NoError(t, err) - defer func() { - path := loadDataFile.Name() - err = loadDataFile.Close() - require.NoError(t, err) - err = os.Remove(path) - require.NoError(t, err) - }() - _, err = loadDataFile.WriteString( - "31 31\n" + - "32 32\n" + - "33 33\n") - require.NoError(t, err) - - // Test case for other statements - cases := []struct { - sql string - isQuery bool - reqs []tikvrpc.CmdType - }{ - // Test for curd. - {"insert into t values (1,1), (3,3)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"insert into t values (1,2) on duplicate key update a = 2", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, - {"update t set b=b+1 where a=3", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdGet}}, - {"update t set b=b+1 where a>1", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdCop}}, - {"delete from t where a=3", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdGet}}, - {"delete from t where a>1", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdCop}}, - {"insert ignore into t values (2,2), (3,3)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, - {"select * from t where a in (1,2,3,4)", true, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, - {"select * from t where a = 1", true, []tikvrpc.CmdType{tikvrpc.CmdGet}}, - {"select * from t where b > 0", true, []tikvrpc.CmdType{tikvrpc.CmdCop}}, - {"replace into t values (2,2), (4,4)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, - - // Test for DDL - {"create database test_db0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"create table test_db0.test_t0 (a int, b int, index idx(a))", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"create table test_db0.test_t1 (a int, b int, index idx(a))", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"alter table test_db0.test_t0 add column c int", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"drop table test_db0.test_t0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"drop database test_db0", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - {"alter table t modify column b double", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdScan, tikvrpc.CmdCop}}, - {"alter table t add index idx2 (b,a)", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdScan, tikvrpc.CmdCop}}, - {"alter table t drop index idx2", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - - // Test for transaction - {"begin", false, nil}, - {"insert into t2 values (10,10), (11,11)", false, nil}, - {"insert ignore into t2 values (20,20), (21,21)", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, - {"commit", false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit}}, - - // Test for other statements. - {"set @@global.tidb_enable_1pc = 1", false, nil}, - {fmt.Sprintf("load data local infile %q into table t2", loadDataFile.Name()), false, []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, - {"admin check table t", false, nil}, - {"admin check index t idx", false, nil}, - {"admin recover index t idx", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, - {"admin cleanup index t idx", false, []tikvrpc.CmdType{tikvrpc.CmdBatchGet}}, - } - - internalCases := []struct { - sql string - reqs []tikvrpc.CmdType - }{ - {"replace into mysql.global_variables (variable_name,variable_value) values ('tidb_enable_1pc', '1')", []tikvrpc.CmdType{tikvrpc.CmdPrewrite, tikvrpc.CmdCommit, tikvrpc.CmdBatchGet}}, - {"select /*+ read_from_storage(tikv[`stmtstats`.`t`]) */ bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index() where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, - {"select bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index(`idx`) where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, - {"select /*+ read_from_storage(tikv[`stmtstats`.`t`]) */ bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index() where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, - {"select bit_xor(crc32(md5(concat_ws(0x2, `_tidb_rowid`, `a`)))), ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024), count(*) from `stmtstats`.`t` use index(`idx`) where 0 = 0 group by ((cast(crc32(md5(concat_ws(0x2, `_tidb_rowid`))) as signed) - 0) div 1 % 1024)", []tikvrpc.CmdType{tikvrpc.CmdCop}}, - } - executeCaseFn := func(execFn func(db *sql.DB)) { - dsn := ts.GetDSN(func(config *mysql.Config) { - config.AllowAllFiles = true - config.Params["sql_mode"] = "''" - }) - db, err := sql.Open("mysql", dsn) - require.NoError(t, err) - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use stmtstats;") - require.NoError(t, err) - - execFn(db) - err = db.Close() - require.NoError(t, err) - } - execFn := func(db *sql.DB) { - dbt := testkit.NewDBTestKit(t, db) - for _, ca := range cases { - if ca.isQuery { - mustQuery(t, dbt, ca.sql) - } else { - dbt.MustExec(ca.sql) - } - } - } - executeCaseFn(execFn) - - for _, ca := range cases { - _, digest := parser.NormalizeDigest(ca.sql) - tagChecker.checkReqExist(t, stmtstats.BinaryDigest(digest.Bytes()), ca.sql, ca.reqs...) - } - for _, ca := range internalCases { - _, digest := parser.NormalizeDigest(ca.sql) - tagChecker.checkReqExist(t, stmtstats.BinaryDigest(digest.Bytes()), ca.sql, ca.reqs...) - } -} - -func (ts *tidbTestTopSQLSuite) loopExec(ctx context.Context, t *testing.T, fn func(db *sql.DB)) { - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err, "Error connecting") - defer func() { - err := db.Close() - require.NoError(t, err) - }() - dbt := testkit.NewDBTestKit(t, db) - dbt.MustExec("use topsql;") - for { - select { - case <-ctx.Done(): - return - default: - } - fn(db) - } -} - -func TestLocalhostClientMapping(t *testing.T) { - tempDir := t.TempDir() - socketFile := tempDir + "/tidbtest.sock" // Unix Socket does not work on Windows, so '/' should be OK - - cli := testserverclient.NewTestServerClient() - cfg := util2.NewTestConfig() - cfg.Socket = socketFile - cfg.Port = cli.Port - cfg.Status.ReportStatus = false - - ts := createTidbTestSuite(t) - - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - go func() { - err := server.Run() - require.NoError(t, err) - }() - defer server.Close() - cli.WaitUntilServerCanConnect() - - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - // Create a db connection for root - db, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { - config.User = "root" - config.Net = "unix" - config.DBName = "test" - config.Addr = socketFile - })) - require.NoErrorf(t, err, "Open failed") - err = db.Ping() - require.NoErrorf(t, err, "Ping failed") - defer db.Close() - dbt := testkit.NewDBTestKit(t, db) - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "root@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION") - require.NoError(t, rows.Close()) - - dbt.MustExec("CREATE USER 'localhostuser'@'localhost'") - dbt.MustExec("CREATE USER 'localhostuser'@'%'") - defer func() { - dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'%'") - dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'localhost'") - dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'127.0.0.1'") - }() - - dbt.MustExec("GRANT SELECT ON test.* TO 'localhostuser'@'%'") - dbt.MustExec("GRANT SELECT,UPDATE ON test.* TO 'localhostuser'@'localhost'") - - // Test with loopback interface - Should get access to localhostuser@localhost! - cli.RunTests(t, func(config *mysql.Config) { - config.User = "localhostuser" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - // NOTICE: this is not compatible with MySQL! (MySQL would report localhostuser@localhost also for 127.0.0.1) - cli.CheckRows(t, rows, "localhostuser@127.0.0.1") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'localhostuser'@'localhost'\nGRANT SELECT,UPDATE ON `test`.* TO 'localhostuser'@'localhost'") - require.NoError(t, rows.Close()) - }) - - dbt.MustExec("DROP USER IF EXISTS 'localhostuser'@'localhost'") - dbt.MustExec("CREATE USER 'localhostuser'@'127.0.0.1'") - dbt.MustExec("GRANT SELECT,UPDATE ON test.* TO 'localhostuser'@'127.0.0.1'") - // Test with unix domain socket file connection - Should get access to '%' - cli.RunTests(t, func(config *mysql.Config) { - config.Net = "unix" - config.Addr = socketFile - config.User = "localhostuser" - config.DBName = "test" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("select user()") - cli.CheckRows(t, rows, "localhostuser@localhost") - require.NoError(t, rows.Close()) - rows = dbt.MustQuery("show grants") - cli.CheckRows(t, rows, "GRANT USAGE ON *.* TO 'localhostuser'@'%'\nGRANT SELECT ON `test`.* TO 'localhostuser'@'%'") - require.NoError(t, rows.Close()) - }) - - // Test if only localhost exists - dbt.MustExec("DROP USER 'localhostuser'@'%'") - dbSocket, err := sql.Open("mysql", cli.GetDSN(func(config *mysql.Config) { - config.User = "localhostuser" - config.Net = "unix" - config.DBName = "test" - config.Addr = socketFile - })) - require.NoErrorf(t, err, "Open failed") - defer dbSocket.Close() - err = dbSocket.Ping() - require.Errorf(t, err, "Connection successful without matching host for unix domain socket!") -} - -func TestRcReadCheckTS(t *testing.T) { - ts := createTidbTestSuite(t) - - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - err := db.Close() - require.NoError(t, err) - }() - - db2, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - err := db2.Close() - require.NoError(t, err) - }() - tk2 := testkit.NewDBTestKit(t, db2) - tk2.MustExec("set @@tidb_enable_async_commit = 0") - tk2.MustExec("set @@tidb_enable_1pc = 0") - - cli := testserverclient.NewTestServerClient() - - tk := testkit.NewDBTestKit(t, db) - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(c1 int key, c2 int)") - tk.MustExec("insert into t1 values(1, 10), (2, 20), (3, 30)") - - tk.MustExec(`set tidb_rc_read_check_ts = 'on';`) - tk.MustExec(`set tx_isolation = 'READ-COMMITTED';`) - tk.MustExec("begin pessimistic") - // Test point get retry. - rows := tk.MustQuery("select * from t1 where c1 = 1") - cli.CheckRows(t, rows, "1 10") - tk2.MustExec("update t1 set c2 = c2 + 1") - rows = tk.MustQuery("select * from t1 where c1 = 1") - cli.CheckRows(t, rows, "1 11") - // Test batch point get retry. - rows = tk.MustQuery("select * from t1 where c1 in (1, 3)") - cli.CheckRows(t, rows, "1 11", "3 31") - tk2.MustExec("update t1 set c2 = c2 + 1") - rows = tk.MustQuery("select * from t1 where c1 in (1, 3)") - cli.CheckRows(t, rows, "1 12", "3 32") - // Test scan retry. - rows = tk.MustQuery("select * from t1") - cli.CheckRows(t, rows, "1 12", "2 22", "3 32") - tk2.MustExec("update t1 set c2 = c2 + 1") - rows = tk.MustQuery("select * from t1") - cli.CheckRows(t, rows, "1 13", "2 23", "3 33") - // Test reverse scan retry. - rows = tk.MustQuery("select * from t1 order by c1 desc") - cli.CheckRows(t, rows, "3 33", "2 23", "1 13") - tk2.MustExec("update t1 set c2 = c2 + 1") - rows = tk.MustQuery("select * from t1 order by c1 desc") - cli.CheckRows(t, rows, "3 34", "2 24", "1 14") - - // Test retry caused by ongoing prewrite lock. - // As the `defaultLockTTL` is 3s and it's difficult to change it here, the lock - // test is implemented in the uft test cases. -} - -type connEventLogs struct { - sync.Mutex - types []extension.ConnEventTp - infos []extension.ConnEventInfo -} - -func (l *connEventLogs) add(tp extension.ConnEventTp, info *extension.ConnEventInfo) { - l.Lock() - defer l.Unlock() - l.types = append(l.types, tp) - l.infos = append(l.infos, *info) -} - -func (l *connEventLogs) reset() { - l.Lock() - defer l.Unlock() - l.types = l.types[:0] - l.infos = l.infos[:0] -} - -func (l *connEventLogs) check(fn func()) { - l.Lock() - defer l.Unlock() - fn() -} - -func (l *connEventLogs) waitEvent(tp extension.ConnEventTp) error { - totalSleep := 0 - for { - l.Lock() - if l.types[len(l.types)-1] == tp { - l.Unlock() - return nil - } - l.Unlock() - if totalSleep >= 10000 { - break - } - time.Sleep(time.Millisecond * 100) - totalSleep += 100 - } - return errors.New("timeout") -} - -func TestExtensionConnEvent(t *testing.T) { - defer extension.Reset() - extension.Reset() - - logs := &connEventLogs{} - require.NoError(t, extension.Register("test", extension.WithSessionHandlerFactory(func() *extension.SessionHandler { - return &extension.SessionHandler{ - OnConnectionEvent: logs.add, - } - }))) - require.NoError(t, extension.Setup()) - - ts := createTidbTestSuite(t) - // createTidbTestSuite create an inner connection, so wait the previous connection closed - require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) - - // test for login success - logs.reset() - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - require.NoError(t, db.Close()) - }() - - conn, err := db.Conn(context.Background()) - require.NoError(t, err) - defer func() { - _ = conn.Close() - }() - - var expectedConn2 variable.ConnectionInfo - require.NoError(t, logs.waitEvent(extension.ConnHandshakeAccepted)) - logs.check(func() { - require.Equal(t, []extension.ConnEventTp{ - extension.ConnConnected, - extension.ConnHandshakeAccepted, - }, logs.types) - conn1 := logs.infos[0] - require.Equal(t, "127.0.0.1", conn1.ClientIP) - require.Equal(t, "127.0.0.1", conn1.ServerIP) - require.Empty(t, conn1.User) - require.Empty(t, conn1.DB) - require.Equal(t, int(ts.Port), conn1.ServerPort) - require.NotEqual(t, conn1.ServerPort, conn1.ClientPort) - require.NotEmpty(t, conn1.ConnectionID) - require.Nil(t, conn1.ActiveRoles) - require.NoError(t, conn1.Error) - require.Empty(t, conn1.SessionAlias) - - expectedConn2 = *(conn1.ConnectionInfo) - expectedConn2.User = "root" - expectedConn2.DB = "test" - require.Equal(t, []*auth.RoleIdentity{}, logs.infos[1].ActiveRoles) - require.Nil(t, logs.infos[1].Error) - require.Equal(t, expectedConn2, *(logs.infos[1].ConnectionInfo)) - require.Empty(t, logs.infos[1].SessionAlias) - }) - - _, err = conn.ExecContext(context.TODO(), "create role r1@'%'") - require.NoError(t, err) - _, err = conn.ExecContext(context.TODO(), "grant r1 TO root") - require.NoError(t, err) - _, err = conn.ExecContext(context.TODO(), "set role all") - require.NoError(t, err) - _, err = conn.ExecContext(context.TODO(), "set @@tidb_session_alias='alias123'") - require.NoError(t, err) - - require.NoError(t, conn.Close()) - require.NoError(t, db.Close()) - require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) - logs.check(func() { - require.Equal(t, 3, len(logs.infos)) - require.Equal(t, 1, len(logs.infos[2].ActiveRoles)) - require.Equal(t, auth.RoleIdentity{ - Username: "r1", - Hostname: "%", - }, *logs.infos[2].ActiveRoles[0]) - require.Nil(t, logs.infos[2].Error) - require.Equal(t, expectedConn2, *(logs.infos[2].ConnectionInfo)) - require.Equal(t, "alias123", logs.infos[2].SessionAlias) - }) - - // test for login failed - logs.reset() - cfg := mysql.NewConfig() - cfg.User = "noexist" - cfg.Net = "tcp" - cfg.Addr = fmt.Sprintf("127.0.0.1:%d", ts.Port) - cfg.DBName = "test" - - db, err = sql.Open("mysql", cfg.FormatDSN()) - require.NoError(t, err) - defer func() { - require.NoError(t, db.Close()) - }() - - _, err = db.Conn(context.Background()) - require.Error(t, err) - require.NoError(t, logs.waitEvent(extension.ConnDisconnected)) - logs.check(func() { - require.Equal(t, []extension.ConnEventTp{ - extension.ConnConnected, - extension.ConnHandshakeRejected, - extension.ConnDisconnected, - }, logs.types) - conn1 := logs.infos[0] - require.Equal(t, "127.0.0.1", conn1.ClientIP) - require.Equal(t, "127.0.0.1", conn1.ServerIP) - require.Empty(t, conn1.User) - require.Empty(t, conn1.DB) - require.Equal(t, int(ts.Port), conn1.ServerPort) - require.NotEqual(t, conn1.ServerPort, conn1.ClientPort) - require.NotEmpty(t, conn1.ConnectionID) - require.Nil(t, conn1.ActiveRoles) - require.NoError(t, conn1.Error) - require.Empty(t, conn1.SessionAlias) - - expectedConn2 = *(conn1.ConnectionInfo) - expectedConn2.User = "noexist" - expectedConn2.DB = "test" - require.Equal(t, []*auth.RoleIdentity{}, logs.infos[1].ActiveRoles) - require.EqualError(t, logs.infos[1].Error, "[server:1045]Access denied for user 'noexist'@'127.0.0.1' (using password: NO)") - require.Equal(t, expectedConn2, *(logs.infos[1].ConnectionInfo)) - require.Empty(t, logs.infos[2].SessionAlias) - }) -} - -func TestSandBoxMode(t *testing.T) { - ts := createTidbTestSuite(t) - qctx, err := ts.tidbdrv.OpenCtx(uint64(0), 0, uint8(tmysql.DefaultCollationID), "test", nil, nil) - require.NoError(t, err) - _, err = Execute(context.Background(), qctx, "create user testuser;") - require.NoError(t, err) - qctx.Session.GetSessionVars().User = &auth.UserIdentity{Username: "testuser", AuthUsername: "testuser", AuthHostname: "%"} - - alterPwdStmts := []string{ - "set password = '1234';", - "alter user testuser identified by '1234';", - "alter user current_user() identified by '1234';", - } - - for _, alterPwdStmt := range alterPwdStmts { - require.False(t, qctx.Session.InSandBoxMode()) - _, err = Execute(context.Background(), qctx, "select 1;") - require.NoError(t, err) - - qctx.Session.EnableSandBoxMode() - require.True(t, qctx.Session.InSandBoxMode()) - _, err = Execute(context.Background(), qctx, "select 1;") - require.Error(t, err) - _, err = Execute(context.Background(), qctx, "alter user testuser identified with 'mysql_native_password';") - require.Error(t, err) - _, err = Execute(context.Background(), qctx, alterPwdStmt) - require.NoError(t, err) - _, err = Execute(context.Background(), qctx, "select 1;") - require.NoError(t, err) - } -} - -// See: https://github.com/pingcap/tidb/issues/40979 -// Reusing memory of `chunk.Chunk` may cause some systems variable's memory value to be modified unexpectedly. -func TestChunkReuseCorruptSysVarString(t *testing.T) { - ts := createTidbTestSuite(t) - - db, err := sql.Open("mysql", ts.GetDSN()) - require.NoError(t, err) - defer func() { - require.NoError(t, db.Close()) - }() - - conn, err := db.Conn(context.Background()) - require.NoError(t, err) - defer func() { - require.NoError(t, conn.Close()) - }() - - rs, err := conn.QueryContext(context.Background(), "show tables in test") - ts.Rows(t, rs) - require.NoError(t, err) - - _, err = conn.ExecContext(context.Background(), "set @@time_zone=(select 'Asia/Shanghai')") - require.NoError(t, err) - - rs, err = conn.QueryContext(context.Background(), "select TIDB_TABLE_ID from information_schema.tables where TABLE_SCHEMA='aaaa'") - ts.Rows(t, rs) - require.NoError(t, err) - - rs, err = conn.QueryContext(context.Background(), "select @@time_zone") - require.NoError(t, err) - defer func() { - require.NoError(t, rs.Close()) - }() - - rows := ts.Rows(t, rs) - require.Equal(t, 1, len(rows)) - require.Equal(t, "Asia/Shanghai", rows[0]) -} - -type mockProxyProtocolProxy struct { - frontend string - backend string - clientAddr string - backendIsSock bool - ln net.Listener - run atomic.Bool -} - -func newMockProxyProtocolProxy(frontend, backend, clientAddr string, backendIsSock bool) *mockProxyProtocolProxy { - return &mockProxyProtocolProxy{ - frontend: frontend, - backend: backend, - clientAddr: clientAddr, - backendIsSock: backendIsSock, - ln: nil, - } -} - -func (p *mockProxyProtocolProxy) ListenAddr() net.Addr { - return p.ln.Addr() -} - -func (p *mockProxyProtocolProxy) Run() (err error) { - p.run.Store(true) - p.ln, err = net.Listen("tcp", p.frontend) - if err != nil { - return err - } - for p.run.Load() { - conn, err := p.ln.Accept() - if err != nil { - break - } - go p.onConn(conn) - } - return nil -} - -func (p *mockProxyProtocolProxy) Close() error { - p.run.Store(false) - if p.ln != nil { - return p.ln.Close() - } - return nil -} - -func (p *mockProxyProtocolProxy) connectToBackend() (net.Conn, error) { - if p.backendIsSock { - return net.Dial("unix", p.backend) - } - return net.Dial("tcp", p.backend) -} - -func (p *mockProxyProtocolProxy) onConn(conn net.Conn) { - bconn, err := p.connectToBackend() - if err != nil { - conn.Close() - fmt.Println(err) - } - defer bconn.Close() - ppHeader := p.generateProxyProtocolHeaderV2("tcp4", p.clientAddr, p.frontend) - bconn.Write(ppHeader) - p.proxyPipe(conn, bconn) -} - -func (p *mockProxyProtocolProxy) proxyPipe(p1, p2 io.ReadWriteCloser) { - defer p1.Close() - defer p2.Close() - - // start proxy - p1die := make(chan struct{}) - go func() { io.Copy(p1, p2); close(p1die) }() - - p2die := make(chan struct{}) - go func() { io.Copy(p2, p1); close(p2die) }() - - // wait for proxy termination - select { - case <-p1die: - case <-p2die: - } -} - -func (p *mockProxyProtocolProxy) generateProxyProtocolHeaderV2(network, srcAddr, dstAddr string) []byte { - var ( - proxyProtocolV2Sig = []byte{0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A} - v2CmdPos = 12 - v2FamlyPos = 13 - ) - saddr, _ := net.ResolveTCPAddr(network, srcAddr) - daddr, _ := net.ResolveTCPAddr(network, dstAddr) - buffer := make([]byte, 1024) - copy(buffer, proxyProtocolV2Sig) - // Command - buffer[v2CmdPos] = 0x21 - // Famly - if network == "tcp4" { - buffer[v2FamlyPos] = 0x11 - binary.BigEndian.PutUint16(buffer[14:14+2], 12) - copy(buffer[16:16+4], []byte(saddr.IP.To4())) - copy(buffer[20:20+4], []byte(daddr.IP.To4())) - binary.BigEndian.PutUint16(buffer[24:24+2], uint16(saddr.Port)) - binary.BigEndian.PutUint16(buffer[26:26+2], uint16(saddr.Port)) - return buffer[0:28] - } else if network == "tcp6" { - buffer[v2FamlyPos] = 0x21 - binary.BigEndian.PutUint16(buffer[14:14+2], 36) - copy(buffer[16:16+16], []byte(saddr.IP.To16())) - copy(buffer[32:32+16], []byte(daddr.IP.To16())) - binary.BigEndian.PutUint16(buffer[48:48+2], uint16(saddr.Port)) - binary.BigEndian.PutUint16(buffer[50:50+2], uint16(saddr.Port)) - return buffer[0:52] - } - return buffer -} - -func TestProxyProtocolWithIpFallbackable(t *testing.T) { - cfg := util2.NewTestConfig() - cfg.Port = 4999 - cfg.Status.ReportStatus = false - // Setup proxy protocol config - cfg.ProxyProtocol.Networks = "*" - cfg.ProxyProtocol.Fallbackable = true - - ts := createTidbTestSuite(t) - - // Prepare Server - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 100) - defer func() { - server.Close() - }() - - require.NotNil(t, server.Listener()) - require.Nil(t, server.Socket()) - - // Prepare Proxy - ppProxy := newMockProxyProtocolProxy("127.0.0.1:5000", "127.0.0.1:4999", "192.168.1.2:60055", false) - go func() { - ppProxy.Run() - }() - time.Sleep(time.Millisecond * 100) - defer func() { - ppProxy.Close() - }() - - cli := testserverclient.NewTestServerClient() - cli.Port = testutil.GetPortFromTCPAddr(ppProxy.ListenAddr()) - cli.WaitUntilServerCanConnect() - - cli.RunTests(t, - func(config *mysql.Config) { - config.User = "root" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("SHOW PROCESSLIST;") - records := cli.Rows(t, rows) - require.Contains(t, records[0], "192.168.1.2:60055") - }, - ) - - cli2 := testserverclient.NewTestServerClient() - cli2.Port = 4999 - cli2.RunTests(t, - func(config *mysql.Config) { - config.User = "root" - }, - func(dbt *testkit.DBTestKit) { - rows := dbt.MustQuery("SHOW PROCESSLIST;") - records := cli.Rows(t, rows) - require.Contains(t, records[0], "127.0.0.1:") - }, - ) -} - -func TestProxyProtocolWithIpNoFallbackable(t *testing.T) { - cfg := util2.NewTestConfig() - cfg.Port = 0 - cfg.Status.ReportStatus = false - // Setup proxy protocol config - cfg.ProxyProtocol.Networks = "*" - cfg.ProxyProtocol.Fallbackable = false - - ts := createTidbTestSuite(t) - - // Prepare Server - server, err := server2.NewServer(cfg, ts.tidbdrv) - require.NoError(t, err) - server.SetDomain(ts.domain) - go func() { - err := server.Run() - require.NoError(t, err) - }() - time.Sleep(time.Millisecond * 1000) - defer func() { - server.Close() - }() - - require.NotNil(t, server.Listener()) - require.Nil(t, server.Socket()) - - cli := testserverclient.NewTestServerClient() - cli.Port = testutil.GetPortFromTCPAddr(server.ListenAddr()) - dsn := cli.GetDSN(func(config *mysql.Config) { - config.User = "root" - config.DBName = "test" - }) - db, err := sql.Open("mysql", dsn) - require.Nil(t, err) - err = db.Ping() - require.NotNil(t, err) - db.Close() -} diff --git a/server/tidb_test.go b/server/tidb_test.go deleted file mode 100644 index 958cfbf7567b6..0000000000000 --- a/server/tidb_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package server - -import ( - "bufio" - "bytes" - "context" - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/server/internal" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/arena" - "github.com/pingcap/tidb/util/chunk" - "github.com/stretchr/testify/require" -) - -func TestRcReadCheckTSConflict(t *testing.T) { - store := testkit.CreateMockStore(t) - - cc := &clientConn{ - alloc: arena.NewAllocator(1024), - chunkAlloc: chunk.NewAllocator(), - pkt: internal.NewPacketIOForTest(bufio.NewWriter(bytes.NewBuffer(nil))), - } - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_rc_read_check_ts = ON") - tk.RefreshSession() - cc.SetCtx(&TiDBContext{Session: tk.Session(), stmts: make(map[int]*TiDBStatement)}) - - tk.MustExec("use test") - tk.MustExec("create table t(a int not null primary key, b int not null)") - dml := "insert into t values" - for i := 0; i < 50; i++ { - dml += fmt.Sprintf("(%v, 0)", i) - if i != 49 { - dml += "," - } - } - tk.MustExec(dml) - tk.MustQuery("select count(*) from t").Check(testkit.Rows("50")) - require.Equal(t, "ON", tk.MustQuery("show variables like 'tidb_rc_read_check_ts'").Rows()[0][1]) - - ctx := context.Background() - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/server/fetchNextErr", "return(\"secondNextAndRetConflict\")")) - err := cc.handleQuery(ctx, "select * from t limit 20") - require.NoError(t, err) - - err = cc.handleQuery(ctx, "select * from t t1 join t t2") - require.Equal(t, kv.ErrWriteConflict, err) - - tk.MustExec("set session tidb_max_chunk_size = 4096") - require.Equal(t, "4096", tk.MustQuery("show variables like 'tidb_max_chunk_size'").Rows()[0][1]) - err = cc.handleQuery(ctx, "select * from t t1 join t t2") - require.NoError(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/server/fetchNextErr")) - - tk.MustExec("drop table t") -} - -func TestRcReadCheckTSConflictExtra(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/sessiontxn/isolation/CallOnStmtRetry", "return")) - defer func() { - defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/sessiontxn/isolation/CallOnStmtRetry")) - }() - store := testkit.CreateMockStore(t) - - ctx := context.Background() - cc := &clientConn{ - alloc: arena.NewAllocator(1024), - chunkAlloc: chunk.NewAllocator(), - pkt: internal.NewPacketIOForTest(bufio.NewWriter(bytes.NewBuffer(nil))), - } - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_rc_read_check_ts = ON") - - se := tk.Session() - cc.SetCtx(&TiDBContext{Session: se, stmts: make(map[int]*TiDBStatement)}) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(id1 int, id2 int, id3 int, PRIMARY KEY(id1), UNIQUE KEY udx_id2 (id2))") - tk.MustExec("insert into t1 values (1, 1, 1)") - tk.MustExec("insert into t1 values (10, 10, 10)") - require.Equal(t, "ON", tk.MustQuery("show variables like 'tidb_rc_read_check_ts'").Rows()[0][1]) - - tk.MustExec("set transaction_isolation = 'READ-COMMITTED'") - tk2.MustExec("set transaction_isolation = 'READ-COMMITTED'") - - // Execute in text protocol - se.SetValue(sessiontxn.CallOnStmtRetryCount, 0) - tk.MustExec("begin pessimistic") - tk2.MustExec("update t1 set id3 = id3 + 1 where id1 = 1") - err := cc.handleQuery(ctx, "select * from t1 where id1 = 1") - require.NoError(t, err) - tk.MustExec("commit") - count, ok := se.Value(sessiontxn.CallOnStmtRetryCount).(int) - require.Equal(t, true, ok) - require.Equal(t, 1, count) - - // Execute in prepare binary protocol - se.SetValue(sessiontxn.CallOnStmtRetryCount, 0) - tk.MustExec("begin pessimistic") - tk2.MustExec("update t1 set id3 = id3 + 1 where id1 = 1") - require.NoError(t, cc.HandleStmtPrepare(ctx, "select * from t1 where id1 = 1")) - require.NoError(t, cc.handleStmtExecute(ctx, []byte{0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0})) - tk.MustExec("commit") - count, ok = se.Value(sessiontxn.CallOnStmtRetryCount).(int) - require.Equal(t, true, ok) - require.Equal(t, 1, count) - - tk.MustExec("drop table t1") -} diff --git a/session/BUILD.bazel b/session/BUILD.bazel deleted file mode 100644 index 81832113248e8..0000000000000 --- a/session/BUILD.bazel +++ /dev/null @@ -1,168 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "session", - srcs = [ - "advisory_locks.go", - "bootstrap.go", - "mock_bootstrap.go", - "nontransactional.go", - "session.go", - "sync_upgrade.go", - "testutil.go", #keep - "tidb.go", - "txn.go", - "txnmanager.go", - ], - importpath = "github.com/pingcap/tidb/session", - visibility = ["//visibility:public"], - deps = [ - "//bindinfo", - "//config", - "//ddl", - "//ddl/placement", - "//ddl/schematracker", - "//ddl/syncer", - "//domain", - "//domain/infosync", - "//errno", - "//executor", - "//expression", - "//extension", - "//extension/extensionimpl", - "//infoschema", - "//kv", - "//meta", - "//metrics", - "//owner", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//planner", - "//planner/core", - "//plugin", - "//privilege", - "//privilege/conn", - "//privilege/privileges", - "//session/metrics", - "//session/txninfo", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/isolation", - "//sessiontxn/staleread", - "//statistics/handle/usage", - "//store/driver/error", - "//store/driver/txn", - "//store/helper", - "//store/mockstore", - "//table", - "//table/tables", - "//table/temptable", - "//tablecodec", - "//telemetry", - "//testkit/testenv", - "//timer/tablestore", - "//ttl/ttlworker", - "//types", - "//types/parser_driver", - "//util", - "//util/chunk", - "//util/collate", - "//util/dbterror", - "//util/dbterror/exeerrors", - "//util/execdetails", - "//util/intest", - "//util/kvcache", - "//util/logutil", - "//util/logutil/consistency", - "//util/mathutil", - "//util/memory", - "//util/parser", - "//util/sem", - "//util/sli", - "//util/sqlexec", - "//util/syncutil", - "//util/tableutil", - "//util/timeutil", - "//util/topsql", - "//util/topsql/state", - "//util/topsql/stmtstats", - "//util/tracing", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "session_test", - timeout = "moderate", - srcs = [ - "bench_test.go", - "bootstrap_test.go", - "index_usage_sync_lease_test.go", - "main_test.go", - "tidb_test.go", - ], - data = glob(["testdata/**"]), - embed = [":session"], - flaky = True, - race = "on", - shard_count = 50, - deps = [ - "//autoid_service", - "//bindinfo", - "//config", - "//domain", - "//executor", - "//expression", - "//kv", - "//meta", - "//parser/ast", - "//parser/auth", - "//planner/core", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//store/mockstore", - "//tablecodec", - "//telemetry", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util", - "//util/benchdaily", - "//util/chunk", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_atomic//:atomic", #keep - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) diff --git a/session/bench_test.go b/session/bench_test.go deleted file mode 100644 index b726869117781..0000000000000 --- a/session/bench_test.go +++ /dev/null @@ -1,1945 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "fmt" - "math/rand" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/log" - _ "github.com/pingcap/tidb/autoid_service" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" -) - -var smallCount = 100 -var bigCount = 10000 - -func prepareBenchSession() (Session, *domain.Domain, kv.Storage) { - config.UpdateGlobal(func(cfg *config.Config) { - cfg.Instance.EnableSlowLog.Store(false) - }) - - store, err := mockstore.NewMockStore() - if err != nil { - logutil.BgLogger().Fatal(err.Error()) - } - domain, err := BootstrapSession(store) - if err != nil { - logutil.BgLogger().Fatal(err.Error()) - } - log.SetLevel(zapcore.ErrorLevel) - se, err := CreateSession4Test(store) - if err != nil { - logutil.BgLogger().Fatal(err.Error()) - } - mustExecute(se, "use test") - return se, domain, store -} - -func prepareBenchData(se Session, colType string, valueFormat string, valueCount int) { - mustExecute(se, "drop table if exists t") - mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s, index idx (col))", colType)) - mustExecute(se, "begin") - for i := 0; i < valueCount; i++ { - mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") - } - mustExecute(se, "commit") -} - -func prepareNonclusteredBenchData(se Session, colType string, valueFormat string, valueCount int) { - mustExecute(se, "drop table if exists t") - mustExecute(se, fmt.Sprintf("create table t (pk int primary key /*T![clustered_index] NONCLUSTERED */ auto_increment, col %s, index idx (col))", colType)) - mustExecute(se, "begin") - for i := 0; i < valueCount; i++ { - mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") - } - mustExecute(se, "commit") -} - -func prepareSortBenchData(se Session, colType string, valueFormat string, valueCount int) { - mustExecute(se, "drop table if exists t") - mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s)", colType)) - mustExecute(se, "begin") - r := rand.New(rand.NewSource(time.Now().UnixNano())) - for i := 0; i < valueCount; i++ { - if i%1000 == 0 { - mustExecute(se, "commit") - mustExecute(se, "begin") - } - mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, r.Intn(valueCount))+")") - } - mustExecute(se, "commit") -} - -func prepareJoinBenchData(se Session, colType string, valueFormat string, valueCount int) { - mustExecute(se, "drop table if exists t") - mustExecute(se, fmt.Sprintf("create table t (pk int primary key auto_increment, col %s)", colType)) - mustExecute(se, "begin") - for i := 0; i < valueCount; i++ { - mustExecute(se, "insert t (col) values ("+fmt.Sprintf(valueFormat, i)+")") - } - mustExecute(se, "commit") -} - -func readResult(ctx context.Context, rs sqlexec.RecordSet, count int) { - req := rs.NewChunk(nil) - for count > 0 { - err := rs.Next(ctx, req) - if err != nil { - logutil.Logger(ctx).Fatal("read result failed", zap.Error(err)) - } - if req.NumRows() == 0 { - logutil.Logger(ctx).Fatal(strconv.Itoa(count)) - } - count -= req.NumRows() - } - rs.Close() -} - -func hasPlan(ctx context.Context, b *testing.B, se Session, plan string) { - find := false - rs, err := se.Execute(ctx, "explain select * from t where col = 'hello 64'") - if err != nil { - b.Fatal(err) - } - rows, err := ResultSetToStringSlice(ctx, se, rs[0]) - if err != nil { - b.Fatal(err) - } - for i := range rows { - if strings.Contains(rows[i][0], plan) { - find = true - } - } - if !find { - b.Fatal(fmt.Printf("plan not contain `%s`", plan)) - } -} - -func BenchmarkBasic(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select 1") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkTableScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "int", "%v", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], smallCount) - } - b.StopTimer() -} - -func BenchmarkExplainTableScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "int", "%v", 0) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "explain select * from t") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkTableLookup(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "int", "%d", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where pk = 64") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkExplainTableLookup(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "int", "%d", 0) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "explain select * from t where pk = 64") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkStringIndexScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "varchar(255)", "'hello %d'", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col > 'hello'") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], smallCount) - } - b.StopTimer() -} - -func BenchmarkExplainStringIndexScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "varchar(255)", "'hello %d'", 0) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "explain select * from t where col > 'hello'") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkPointGet(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, "create table t (pk int primary key)") - mustExecute(se, "insert t values (61),(62),(63),(64)") - b.ResetTimer() - alloc := chunk.NewAllocator() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where pk = 64") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkBatchPointGet(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, "create table t (pk int primary key)") - mustExecute(se, "insert t values (61),(62),(63),(64)") - alloc := chunk.NewAllocator() - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where pk in (61, 64, 67)") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkPreparedPointGet(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, "create table t (pk int primary key)") - mustExecute(se, "insert t values (61),(62),(63),(64)") - - stmtID, _, _, err := se.PrepareStmt("select * from t where pk = ?") - if err != nil { - b.Fatal(err) - } - - params := expression.Args2Expressions4Test(64) - alloc := chunk.NewAllocator() - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.ExecutePreparedStmt(ctx, stmtID, params) - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs, alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkStringIndexLookup(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareNonclusteredBenchData(se, "varchar(255)", "'hello %d'", smallCount) - hasPlan(ctx, b, se, "IndexLookUp") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col = 'hello 64'") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkIntegerIndexScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "int", "%v", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col >= 0") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], smallCount) - } - b.StopTimer() -} - -func BenchmarkIntegerIndexLookup(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareNonclusteredBenchData(se, "int", "%v", smallCount) - hasPlan(ctx, b, se, "IndexLookUp") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col = 64") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkDecimalIndexScan(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareBenchData(se, "decimal(32,6)", "%v.1234", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col >= 0") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], smallCount) - } - b.StopTimer() -} - -func BenchmarkDecimalIndexLookup(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareNonclusteredBenchData(se, "decimal(32,6)", "%v.1234", smallCount) - hasPlan(ctx, b, se, "IndexLookUp") - - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where col = 64.1234") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkInsertWithIndex(b *testing.B) { - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, `set @@tidb_enable_mutation_checker = 0`) - mustExecute(se, "drop table if exists t") - mustExecute(se, "create table t (pk int primary key, col int, index idx (col))") - b.ResetTimer() - for i := 0; i < b.N; i++ { - mustExecute(se, fmt.Sprintf("insert t values (%d, %d)", i, i)) - } - b.StopTimer() -} - -func BenchmarkInsertNoIndex(b *testing.B) { - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, "drop table if exists t") - mustExecute(se, "create table t (pk int primary key, col int)") - b.ResetTimer() - for i := 0; i < b.N; i++ { - mustExecute(se, fmt.Sprintf("insert t values (%d, %d)", i, i)) - } - b.StopTimer() -} - -func BenchmarkSort(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareSortBenchData(se, "int", "%v", bigCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t order by col limit 50") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 50) - } - b.StopTimer() -} - -func BenchmarkSort2(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareSortBenchData(se, "int", "%v", 1000000) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t order by col") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1000000) - } - b.StopTimer() -} - -func BenchmarkJoin(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareJoinBenchData(se, "int", "%v", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t a join t b on a.col = b.col") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], smallCount) - } - b.StopTimer() -} - -func BenchmarkJoinLimit(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - prepareJoinBenchData(se, "int", "%v", smallCount) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t a join t b on a.col = b.col limit 1") - if err != nil { - b.Fatal(err) - } - readResult(ctx, rs[0], 1) - } - b.StopTimer() -} - -func BenchmarkPartitionPruning(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - - mustExecute(se, `create table t (id int, dt datetime) -partition by range (to_days(dt)) ( -partition p0 values less than (737515), -partition p1 values less than (737516), -partition p2 values less than (737517), -partition p3 values less than (737518), -partition p4 values less than (737519), -partition p5 values less than (737520), -partition p6 values less than (737521), -partition p7 values less than (737522), -partition p8 values less than (737523), -partition p9 values less than (737524), -partition p10 values less than (737525), -partition p11 values less than (737526), -partition p12 values less than (737527), -partition p13 values less than (737528), -partition p14 values less than (737529), -partition p15 values less than (737530), -partition p16 values less than (737531), -partition p17 values less than (737532), -partition p18 values less than (737533), -partition p19 values less than (737534), -partition p20 values less than (737535), -partition p21 values less than (737536), -partition p22 values less than (737537), -partition p23 values less than (737538), -partition p24 values less than (737539), -partition p25 values less than (737540), -partition p26 values less than (737541), -partition p27 values less than (737542), -partition p28 values less than (737543), -partition p29 values less than (737544), -partition p30 values less than (737545), -partition p31 values less than (737546), -partition p32 values less than (737547), -partition p33 values less than (737548), -partition p34 values less than (737549), -partition p35 values less than (737550), -partition p36 values less than (737551), -partition p37 values less than (737552), -partition p38 values less than (737553), -partition p39 values less than (737554), -partition p40 values less than (737555), -partition p41 values less than (737556), -partition p42 values less than (737557), -partition p43 values less than (737558), -partition p44 values less than (737559), -partition p45 values less than (737560), -partition p46 values less than (737561), -partition p47 values less than (737562), -partition p48 values less than (737563), -partition p49 values less than (737564), -partition p50 values less than (737565), -partition p51 values less than (737566), -partition p52 values less than (737567), -partition p53 values less than (737568), -partition p54 values less than (737569), -partition p55 values less than (737570), -partition p56 values less than (737571), -partition p57 values less than (737572), -partition p58 values less than (737573), -partition p59 values less than (737574), -partition p60 values less than (737575), -partition p61 values less than (737576), -partition p62 values less than (737577), -partition p63 values less than (737578), -partition p64 values less than (737579), -partition p65 values less than (737580), -partition p66 values less than (737581), -partition p67 values less than (737582), -partition p68 values less than (737583), -partition p69 values less than (737584), -partition p70 values less than (737585), -partition p71 values less than (737586), -partition p72 values less than (737587), -partition p73 values less than (737588), -partition p74 values less than (737589), -partition p75 values less than (737590), -partition p76 values less than (737591), -partition p77 values less than (737592), -partition p78 values less than (737593), -partition p79 values less than (737594), -partition p80 values less than (737595), -partition p81 values less than (737596), -partition p82 values less than (737597), -partition p83 values less than (737598), -partition p84 values less than (737599), -partition p85 values less than (737600), -partition p86 values less than (737601), -partition p87 values less than (737602), -partition p88 values less than (737603), -partition p89 values less than (737604), -partition p90 values less than (737605), -partition p91 values less than (737606), -partition p92 values less than (737607), -partition p93 values less than (737608), -partition p94 values less than (737609), -partition p95 values less than (737610), -partition p96 values less than (737611), -partition p97 values less than (737612), -partition p98 values less than (737613), -partition p99 values less than (737614), -partition p100 values less than (737615), -partition p101 values less than (737616), -partition p102 values less than (737617), -partition p103 values less than (737618), -partition p104 values less than (737619), -partition p105 values less than (737620), -partition p106 values less than (737621), -partition p107 values less than (737622), -partition p108 values less than (737623), -partition p109 values less than (737624), -partition p110 values less than (737625), -partition p111 values less than (737626), -partition p112 values less than (737627), -partition p113 values less than (737628), -partition p114 values less than (737629), -partition p115 values less than (737630), -partition p116 values less than (737631), -partition p117 values less than (737632), -partition p118 values less than (737633), -partition p119 values less than (737634), -partition p120 values less than (737635), -partition p121 values less than (737636), -partition p122 values less than (737637), -partition p123 values less than (737638), -partition p124 values less than (737639), -partition p125 values less than (737640), -partition p126 values less than (737641), -partition p127 values less than (737642), -partition p128 values less than (737643), -partition p129 values less than (737644), -partition p130 values less than (737645), -partition p131 values less than (737646), -partition p132 values less than (737647), -partition p133 values less than (737648), -partition p134 values less than (737649), -partition p135 values less than (737650), -partition p136 values less than (737651), -partition p137 values less than (737652), -partition p138 values less than (737653), -partition p139 values less than (737654), -partition p140 values less than (737655), -partition p141 values less than (737656), -partition p142 values less than (737657), -partition p143 values less than (737658), -partition p144 values less than (737659), -partition p145 values less than (737660), -partition p146 values less than (737661), -partition p147 values less than (737662), -partition p148 values less than (737663), -partition p149 values less than (737664), -partition p150 values less than (737665), -partition p151 values less than (737666), -partition p152 values less than (737667), -partition p153 values less than (737668), -partition p154 values less than (737669), -partition p155 values less than (737670), -partition p156 values less than (737671), -partition p157 values less than (737672), -partition p158 values less than (737673), -partition p159 values less than (737674), -partition p160 values less than (737675), -partition p161 values less than (737676), -partition p162 values less than (737677), -partition p163 values less than (737678), -partition p164 values less than (737679), -partition p165 values less than (737680), -partition p166 values less than (737681), -partition p167 values less than (737682), -partition p168 values less than (737683), -partition p169 values less than (737684), -partition p170 values less than (737685), -partition p171 values less than (737686), -partition p172 values less than (737687), -partition p173 values less than (737688), -partition p174 values less than (737689), -partition p175 values less than (737690), -partition p176 values less than (737691), -partition p177 values less than (737692), -partition p178 values less than (737693), -partition p179 values less than (737694), -partition p180 values less than (737695), -partition p181 values less than (737696), -partition p182 values less than (737697), -partition p183 values less than (737698), -partition p184 values less than (737699), -partition p185 values less than (737700), -partition p186 values less than (737701), -partition p187 values less than (737702), -partition p188 values less than (737703), -partition p189 values less than (737704), -partition p190 values less than (737705), -partition p191 values less than (737706), -partition p192 values less than (737707), -partition p193 values less than (737708), -partition p194 values less than (737709), -partition p195 values less than (737710), -partition p196 values less than (737711), -partition p197 values less than (737712), -partition p198 values less than (737713), -partition p199 values less than (737714), -partition p200 values less than (737715), -partition p201 values less than (737716), -partition p202 values less than (737717), -partition p203 values less than (737718), -partition p204 values less than (737719), -partition p205 values less than (737720), -partition p206 values less than (737721), -partition p207 values less than (737722), -partition p208 values less than (737723), -partition p209 values less than (737724), -partition p210 values less than (737725), -partition p211 values less than (737726), -partition p212 values less than (737727), -partition p213 values less than (737728), -partition p214 values less than (737729), -partition p215 values less than (737730), -partition p216 values less than (737731), -partition p217 values less than (737732), -partition p218 values less than (737733), -partition p219 values less than (737734), -partition p220 values less than (737735), -partition p221 values less than (737736), -partition p222 values less than (737737), -partition p223 values less than (737738), -partition p224 values less than (737739), -partition p225 values less than (737740), -partition p226 values less than (737741), -partition p227 values less than (737742), -partition p228 values less than (737743), -partition p229 values less than (737744), -partition p230 values less than (737745), -partition p231 values less than (737746), -partition p232 values less than (737747), -partition p233 values less than (737748), -partition p234 values less than (737749), -partition p235 values less than (737750), -partition p236 values less than (737751), -partition p237 values less than (737752), -partition p238 values less than (737753), -partition p239 values less than (737754), -partition p240 values less than (737755), -partition p241 values less than (737756), -partition p242 values less than (737757), -partition p243 values less than (737758), -partition p244 values less than (737759), -partition p245 values less than (737760), -partition p246 values less than (737761), -partition p247 values less than (737762), -partition p248 values less than (737763), -partition p249 values less than (737764), -partition p250 values less than (737765), -partition p251 values less than (737766), -partition p252 values less than (737767), -partition p253 values less than (737768), -partition p254 values less than (737769), -partition p255 values less than (737770), -partition p256 values less than (737771), -partition p257 values less than (737772), -partition p258 values less than (737773), -partition p259 values less than (737774), -partition p260 values less than (737775), -partition p261 values less than (737776), -partition p262 values less than (737777), -partition p263 values less than (737778), -partition p264 values less than (737779), -partition p265 values less than (737780), -partition p266 values less than (737781), -partition p267 values less than (737782), -partition p268 values less than (737783), -partition p269 values less than (737784), -partition p270 values less than (737785), -partition p271 values less than (737786), -partition p272 values less than (737787), -partition p273 values less than (737788), -partition p274 values less than (737789), -partition p275 values less than (737790), -partition p276 values less than (737791), -partition p277 values less than (737792), -partition p278 values less than (737793), -partition p279 values less than (737794), -partition p280 values less than (737795), -partition p281 values less than (737796), -partition p282 values less than (737797), -partition p283 values less than (737798), -partition p284 values less than (737799), -partition p285 values less than (737800), -partition p286 values less than (737801), -partition p287 values less than (737802), -partition p288 values less than (737803), -partition p289 values less than (737804), -partition p290 values less than (737805), -partition p291 values less than (737806), -partition p292 values less than (737807), -partition p293 values less than (737808), -partition p294 values less than (737809), -partition p295 values less than (737810), -partition p296 values less than (737811), -partition p297 values less than (737812), -partition p298 values less than (737813), -partition p299 values less than (737814), -partition p300 values less than (737815), -partition p301 values less than (737816), -partition p302 values less than (737817), -partition p303 values less than (737818), -partition p304 values less than (737819), -partition p305 values less than (737820), -partition p306 values less than (737821), -partition p307 values less than (737822), -partition p308 values less than (737823), -partition p309 values less than (737824), -partition p310 values less than (737825), -partition p311 values less than (737826), -partition p312 values less than (737827), -partition p313 values less than (737828), -partition p314 values less than (737829), -partition p315 values less than (737830), -partition p316 values less than (737831), -partition p317 values less than (737832), -partition p318 values less than (737833), -partition p319 values less than (737834), -partition p320 values less than (737835), -partition p321 values less than (737836), -partition p322 values less than (737837), -partition p323 values less than (737838), -partition p324 values less than (737839), -partition p325 values less than (737840), -partition p326 values less than (737841), -partition p327 values less than (737842), -partition p328 values less than (737843), -partition p329 values less than (737844), -partition p330 values less than (737845), -partition p331 values less than (737846), -partition p332 values less than (737847), -partition p333 values less than (737848), -partition p334 values less than (737849), -partition p335 values less than (737850), -partition p336 values less than (737851), -partition p337 values less than (737852), -partition p338 values less than (737853), -partition p339 values less than (737854), -partition p340 values less than (737855), -partition p341 values less than (737856), -partition p342 values less than (737857), -partition p343 values less than (737858), -partition p344 values less than (737859), -partition p345 values less than (737860), -partition p346 values less than (737861), -partition p347 values less than (737862), -partition p348 values less than (737863), -partition p349 values less than (737864), -partition p350 values less than (737865), -partition p351 values less than (737866), -partition p352 values less than (737867), -partition p353 values less than (737868), -partition p354 values less than (737869), -partition p355 values less than (737870), -partition p356 values less than (737871), -partition p357 values less than (737872), -partition p358 values less than (737873), -partition p359 values less than (737874), -partition p360 values less than (737875), -partition p361 values less than (737876), -partition p362 values less than (737877), -partition p363 values less than (737878), -partition p364 values less than (737879), -partition p365 values less than (737880), -partition p366 values less than (737881), -partition p367 values less than (737882), -partition p368 values less than (737883), -partition p369 values less than (737884), -partition p370 values less than (737885), -partition p371 values less than (737886), -partition p372 values less than (737887), -partition p373 values less than (737888), -partition p374 values less than (737889), -partition p375 values less than (737890), -partition p376 values less than (737891), -partition p377 values less than (737892), -partition p378 values less than (737893), -partition p379 values less than (737894), -partition p380 values less than (737895), -partition p381 values less than (737896), -partition p382 values less than (737897), -partition p383 values less than (737898), -partition p384 values less than (737899), -partition p385 values less than (737900), -partition p386 values less than (737901), -partition p387 values less than (737902), -partition p388 values less than (737903), -partition p389 values less than (737904), -partition p390 values less than (737905), -partition p391 values less than (737906), -partition p392 values less than (737907), -partition p393 values less than (737908), -partition p394 values less than (737909), -partition p395 values less than (737910), -partition p396 values less than (737911), -partition p397 values less than (737912), -partition p398 values less than (737913), -partition p399 values less than (737914), -partition p400 values less than (737915), -partition p401 values less than (737916), -partition p402 values less than (737917), -partition p403 values less than (737918), -partition p404 values less than (737919), -partition p405 values less than (737920), -partition p406 values less than (737921), -partition p407 values less than (737922), -partition p408 values less than (737923), -partition p409 values less than (737924), -partition p410 values less than (737925), -partition p411 values less than (737926), -partition p412 values less than (737927), -partition p413 values less than (737928), -partition p414 values less than (737929), -partition p415 values less than (737930), -partition p416 values less than (737931), -partition p417 values less than (737932), -partition p418 values less than (737933), -partition p419 values less than (737934), -partition p420 values less than (737935), -partition p421 values less than (737936), -partition p422 values less than (737937), -partition p423 values less than (737938), -partition p424 values less than (737939), -partition p425 values less than (737940), -partition p426 values less than (737941), -partition p427 values less than (737942), -partition p428 values less than (737943), -partition p429 values less than (737944), -partition p430 values less than (737945), -partition p431 values less than (737946), -partition p432 values less than (737947), -partition p433 values less than (737948), -partition p434 values less than (737949), -partition p435 values less than (737950), -partition p436 values less than (737951), -partition p437 values less than (737952), -partition p438 values less than (737953), -partition p439 values less than (737954), -partition p440 values less than (737955), -partition p441 values less than (737956), -partition p442 values less than (737957), -partition p443 values less than (737958), -partition p444 values less than (737959), -partition p445 values less than (737960), -partition p446 values less than (737961), -partition p447 values less than (737962), -partition p448 values less than (737963), -partition p449 values less than (737964), -partition p450 values less than (737965), -partition p451 values less than (737966), -partition p452 values less than (737967), -partition p453 values less than (737968), -partition p454 values less than (737969), -partition p455 values less than (737970), -partition p456 values less than (737971), -partition p457 values less than (737972), -partition p458 values less than (737973), -partition p459 values less than (737974), -partition p460 values less than (737975), -partition p461 values less than (737976), -partition p462 values less than (737977), -partition p463 values less than (737978), -partition p464 values less than (737979), -partition p465 values less than (737980), -partition p466 values less than (737981), -partition p467 values less than (737982), -partition p468 values less than (737983), -partition p469 values less than (737984), -partition p470 values less than (737985), -partition p471 values less than (737986), -partition p472 values less than (737987), -partition p473 values less than (737988), -partition p474 values less than (737989), -partition p475 values less than (737990), -partition p476 values less than (737991), -partition p477 values less than (737992), -partition p478 values less than (737993), -partition p479 values less than (737994), -partition p480 values less than (737995), -partition p481 values less than (737996), -partition p482 values less than (737997), -partition p483 values less than (737998), -partition p484 values less than (737999), -partition p485 values less than (738000), -partition p486 values less than (738001), -partition p487 values less than (738002), -partition p488 values less than (738003), -partition p489 values less than (738004), -partition p490 values less than (738005), -partition p491 values less than (738006), -partition p492 values less than (738007), -partition p493 values less than (738008), -partition p494 values less than (738009), -partition p495 values less than (738010), -partition p496 values less than (738011), -partition p497 values less than (738012), -partition p498 values less than (738013), -partition p499 values less than (738014), -partition p500 values less than (738015), -partition p501 values less than (738016), -partition p502 values less than (738017), -partition p503 values less than (738018), -partition p504 values less than (738019), -partition p505 values less than (738020), -partition p506 values less than (738021), -partition p507 values less than (738022), -partition p508 values less than (738023), -partition p509 values less than (738024), -partition p510 values less than (738025), -partition p511 values less than (738026), -partition p512 values less than (738027), -partition p513 values less than (738028), -partition p514 values less than (738029), -partition p515 values less than (738030), -partition p516 values less than (738031), -partition p517 values less than (738032), -partition p518 values less than (738033), -partition p519 values less than (738034), -partition p520 values less than (738035), -partition p521 values less than (738036), -partition p522 values less than (738037), -partition p523 values less than (738038), -partition p524 values less than (738039), -partition p525 values less than (738040), -partition p526 values less than (738041), -partition p527 values less than (738042), -partition p528 values less than (738043), -partition p529 values less than (738044), -partition p530 values less than (738045), -partition p531 values less than (738046), -partition p532 values less than (738047), -partition p533 values less than (738048), -partition p534 values less than (738049), -partition p535 values less than (738050), -partition p536 values less than (738051), -partition p537 values less than (738052), -partition p538 values less than (738053), -partition p539 values less than (738054), -partition p540 values less than (738055), -partition p541 values less than (738056), -partition p542 values less than (738057), -partition p543 values less than (738058), -partition p544 values less than (738059), -partition p545 values less than (738060), -partition p546 values less than (738061), -partition p547 values less than (738062), -partition p548 values less than (738063), -partition p549 values less than (738064), -partition p550 values less than (738065), -partition p551 values less than (738066), -partition p552 values less than (738067), -partition p553 values less than (738068), -partition p554 values less than (738069), -partition p555 values less than (738070), -partition p556 values less than (738071), -partition p557 values less than (738072), -partition p558 values less than (738073), -partition p559 values less than (738074), -partition p560 values less than (738075), -partition p561 values less than (738076), -partition p562 values less than (738077), -partition p563 values less than (738078), -partition p564 values less than (738079), -partition p565 values less than (738080), -partition p566 values less than (738081), -partition p567 values less than (738082), -partition p568 values less than (738083), -partition p569 values less than (738084), -partition p570 values less than (738085), -partition p571 values less than (738086), -partition p572 values less than (738087), -partition p573 values less than (738088), -partition p574 values less than (738089), -partition p575 values less than (738090), -partition p576 values less than (738091), -partition p577 values less than (738092), -partition p578 values less than (738093), -partition p579 values less than (738094), -partition p580 values less than (738095), -partition p581 values less than (738096), -partition p582 values less than (738097), -partition p583 values less than (738098), -partition p584 values less than (738099), -partition p585 values less than (738100), -partition p586 values less than (738101), -partition p587 values less than (738102), -partition p588 values less than (738103), -partition p589 values less than (738104), -partition p590 values less than (738105), -partition p591 values less than (738106), -partition p592 values less than (738107), -partition p593 values less than (738108), -partition p594 values less than (738109), -partition p595 values less than (738110), -partition p596 values less than (738111), -partition p597 values less than (738112), -partition p598 values less than (738113), -partition p599 values less than (738114), -partition p600 values less than (738115), -partition p601 values less than (738116), -partition p602 values less than (738117), -partition p603 values less than (738118), -partition p604 values less than (738119), -partition p605 values less than (738120), -partition p606 values less than (738121), -partition p607 values less than (738122), -partition p608 values less than (738123), -partition p609 values less than (738124), -partition p610 values less than (738125), -partition p611 values less than (738126), -partition p612 values less than (738127), -partition p613 values less than (738128), -partition p614 values less than (738129), -partition p615 values less than (738130), -partition p616 values less than (738131), -partition p617 values less than (738132), -partition p618 values less than (738133), -partition p619 values less than (738134), -partition p620 values less than (738135), -partition p621 values less than (738136), -partition p622 values less than (738137), -partition p623 values less than (738138), -partition p624 values less than (738139), -partition p625 values less than (738140), -partition p626 values less than (738141), -partition p627 values less than (738142), -partition p628 values less than (738143), -partition p629 values less than (738144), -partition p630 values less than (738145), -partition p631 values less than (738146), -partition p632 values less than (738147), -partition p633 values less than (738148), -partition p634 values less than (738149), -partition p635 values less than (738150), -partition p636 values less than (738151), -partition p637 values less than (738152), -partition p638 values less than (738153), -partition p639 values less than (738154), -partition p640 values less than (738155), -partition p641 values less than (738156), -partition p642 values less than (738157), -partition p643 values less than (738158), -partition p644 values less than (738159), -partition p645 values less than (738160), -partition p646 values less than (738161), -partition p647 values less than (738162), -partition p648 values less than (738163), -partition p649 values less than (738164), -partition p650 values less than (738165), -partition p651 values less than (738166), -partition p652 values less than (738167), -partition p653 values less than (738168), -partition p654 values less than (738169), -partition p655 values less than (738170), -partition p656 values less than (738171), -partition p657 values less than (738172), -partition p658 values less than (738173), -partition p659 values less than (738174), -partition p660 values less than (738175), -partition p661 values less than (738176), -partition p662 values less than (738177), -partition p663 values less than (738178), -partition p664 values less than (738179), -partition p665 values less than (738180), -partition p666 values less than (738181), -partition p667 values less than (738182), -partition p668 values less than (738183), -partition p669 values less than (738184), -partition p670 values less than (738185), -partition p671 values less than (738186), -partition p672 values less than (738187), -partition p673 values less than (738188), -partition p674 values less than (738189), -partition p675 values less than (738190), -partition p676 values less than (738191), -partition p677 values less than (738192), -partition p678 values less than (738193), -partition p679 values less than (738194), -partition p680 values less than (738195), -partition p681 values less than (738196), -partition p682 values less than (738197), -partition p683 values less than (738198), -partition p684 values less than (738199), -partition p685 values less than (738200), -partition p686 values less than (738201), -partition p687 values less than (738202), -partition p688 values less than (738203), -partition p689 values less than (738204), -partition p690 values less than (738205), -partition p691 values less than (738206), -partition p692 values less than (738207), -partition p693 values less than (738208), -partition p694 values less than (738209), -partition p695 values less than (738210), -partition p696 values less than (738211), -partition p697 values less than (738212), -partition p698 values less than (738213), -partition p699 values less than (738214), -partition p700 values less than (738215), -partition p701 values less than (738216), -partition p702 values less than (738217), -partition p703 values less than (738218), -partition p704 values less than (738219), -partition p705 values less than (738220), -partition p706 values less than (738221), -partition p707 values less than (738222), -partition p708 values less than (738223), -partition p709 values less than (738224), -partition p710 values less than (738225), -partition p711 values less than (738226), -partition p712 values less than (738227), -partition p713 values less than (738228), -partition p714 values less than (738229), -partition p715 values less than (738230), -partition p716 values less than (738231), -partition p717 values less than (738232), -partition p718 values less than (738233), -partition p719 values less than (738234), -partition p720 values less than (738235), -partition p721 values less than (738236), -partition p722 values less than (738237), -partition p723 values less than (738238), -partition p724 values less than (738239), -partition p725 values less than (738240), -partition p726 values less than (738241), -partition p727 values less than (738242), -partition p728 values less than (738243), -partition p729 values less than (738244), -partition p730 values less than (738245), -partition p731 values less than (738246), -partition p732 values less than (738247), -partition p733 values less than (738248), -partition p734 values less than (738249), -partition p735 values less than (738250), -partition p736 values less than (738251), -partition p737 values less than (738252), -partition p738 values less than (738253), -partition p739 values less than (738254), -partition p740 values less than (738255), -partition p741 values less than (738256), -partition p742 values less than (738257), -partition p743 values less than (738258), -partition p744 values less than (738259), -partition p745 values less than (738260), -partition p746 values less than (738261), -partition p747 values less than (738262), -partition p748 values less than (738263), -partition p749 values less than (738264), -partition p750 values less than (738265), -partition p751 values less than (738266), -partition p752 values less than (738267), -partition p753 values less than (738268), -partition p754 values less than (738269), -partition p755 values less than (738270), -partition p756 values less than (738271), -partition p757 values less than (738272), -partition p758 values less than (738273), -partition p759 values less than (738274), -partition p760 values less than (738275), -partition p761 values less than (738276), -partition p762 values less than (738277), -partition p763 values less than (738278), -partition p764 values less than (738279), -partition p765 values less than (738280), -partition p766 values less than (738281), -partition p767 values less than (738282), -partition p768 values less than (738283), -partition p769 values less than (738284), -partition p770 values less than (738285), -partition p771 values less than (738286), -partition p772 values less than (738287), -partition p773 values less than (738288), -partition p774 values less than (738289), -partition p775 values less than (738290), -partition p776 values less than (738291), -partition p777 values less than (738292), -partition p778 values less than (738293), -partition p779 values less than (738294), -partition p780 values less than (738295), -partition p781 values less than (738296), -partition p782 values less than (738297), -partition p783 values less than (738298), -partition p784 values less than (738299), -partition p785 values less than (738300), -partition p786 values less than (738301), -partition p787 values less than (738302), -partition p788 values less than (738303), -partition p789 values less than (738304), -partition p790 values less than (738305), -partition p791 values less than (738306), -partition p792 values less than (738307), -partition p793 values less than (738308), -partition p794 values less than (738309), -partition p795 values less than (738310), -partition p796 values less than (738311), -partition p797 values less than (738312), -partition p798 values less than (738313), -partition p799 values less than (738314), -partition p800 values less than (738315), -partition p801 values less than (738316), -partition p802 values less than (738317), -partition p803 values less than (738318), -partition p804 values less than (738319), -partition p805 values less than (738320), -partition p806 values less than (738321), -partition p807 values less than (738322), -partition p808 values less than (738323), -partition p809 values less than (738324), -partition p810 values less than (738325), -partition p811 values less than (738326), -partition p812 values less than (738327), -partition p813 values less than (738328), -partition p814 values less than (738329), -partition p815 values less than (738330), -partition p816 values less than (738331), -partition p817 values less than (738332), -partition p818 values less than (738333), -partition p819 values less than (738334), -partition p820 values less than (738335), -partition p821 values less than (738336), -partition p822 values less than (738337), -partition p823 values less than (738338), -partition p824 values less than (738339), -partition p825 values less than (738340), -partition p826 values less than (738341), -partition p827 values less than (738342), -partition p828 values less than (738343), -partition p829 values less than (738344), -partition p830 values less than (738345), -partition p831 values less than (738346), -partition p832 values less than (738347), -partition p833 values less than (738348), -partition p834 values less than (738349), -partition p835 values less than (738350), -partition p836 values less than (738351), -partition p837 values less than (738352), -partition p838 values less than (738353), -partition p839 values less than (738354), -partition p840 values less than (738355), -partition p841 values less than (738356), -partition p842 values less than (738357), -partition p843 values less than (738358), -partition p844 values less than (738359), -partition p845 values less than (738360), -partition p846 values less than (738361), -partition p847 values less than (738362), -partition p848 values less than (738363), -partition p849 values less than (738364), -partition p850 values less than (738365), -partition p851 values less than (738366), -partition p852 values less than (738367), -partition p853 values less than (738368), -partition p854 values less than (738369), -partition p855 values less than (738370), -partition p856 values less than (738371), -partition p857 values less than (738372), -partition p858 values less than (738373), -partition p859 values less than (738374), -partition p860 values less than (738375), -partition p861 values less than (738376), -partition p862 values less than (738377), -partition p863 values less than (738378), -partition p864 values less than (738379), -partition p865 values less than (738380), -partition p866 values less than (738381), -partition p867 values less than (738382), -partition p868 values less than (738383), -partition p869 values less than (738384), -partition p870 values less than (738385), -partition p871 values less than (738386), -partition p872 values less than (738387), -partition p873 values less than (738388), -partition p874 values less than (738389), -partition p875 values less than (738390), -partition p876 values less than (738391), -partition p877 values less than (738392), -partition p878 values less than (738393), -partition p879 values less than (738394), -partition p880 values less than (738395), -partition p881 values less than (738396), -partition p882 values less than (738397), -partition p883 values less than (738398), -partition p884 values less than (738399), -partition p885 values less than (738400), -partition p886 values less than (738401), -partition p887 values less than (738402), -partition p888 values less than (738403), -partition p889 values less than (738404), -partition p890 values less than (738405), -partition p891 values less than (738406), -partition p892 values less than (738407), -partition p893 values less than (738408), -partition p894 values less than (738409), -partition p895 values less than (738410), -partition p896 values less than (738411), -partition p897 values less than (738412), -partition p898 values less than (738413), -partition p899 values less than (738414), -partition p900 values less than (738415), -partition p901 values less than (738416), -partition p902 values less than (738417), -partition p903 values less than (738418), -partition p904 values less than (738419), -partition p905 values less than (738420), -partition p906 values less than (738421), -partition p907 values less than (738422), -partition p908 values less than (738423), -partition p909 values less than (738424), -partition p910 values less than (738425), -partition p911 values less than (738426), -partition p912 values less than (738427), -partition p913 values less than (738428), -partition p914 values less than (738429), -partition p915 values less than (738430), -partition p916 values less than (738431), -partition p917 values less than (738432), -partition p918 values less than (738433), -partition p919 values less than (738434), -partition p920 values less than (738435), -partition p921 values less than (738436), -partition p922 values less than (738437), -partition p923 values less than (738438), -partition p924 values less than (738439), -partition p925 values less than (738440), -partition p926 values less than (738441), -partition p927 values less than (738442), -partition p928 values less than (738443), -partition p929 values less than (738444), -partition p930 values less than (738445), -partition p931 values less than (738446), -partition p932 values less than (738447), -partition p933 values less than (738448), -partition p934 values less than (738449), -partition p935 values less than (738450), -partition p936 values less than (738451), -partition p937 values less than (738452), -partition p938 values less than (738453), -partition p939 values less than (738454), -partition p940 values less than (738455), -partition p941 values less than (738456), -partition p942 values less than (738457), -partition p943 values less than (738458), -partition p944 values less than (738459), -partition p945 values less than (738460), -partition p946 values less than (738461), -partition p947 values less than (738462), -partition p948 values less than (738463), -partition p949 values less than (738464), -partition p950 values less than (738465), -partition p951 values less than (738466), -partition p952 values less than (738467), -partition p953 values less than (738468), -partition p954 values less than (738469), -partition p955 values less than (738470), -partition p956 values less than (738471), -partition p957 values less than (738472), -partition p958 values less than (738473), -partition p959 values less than (738474), -partition p960 values less than (738475), -partition p961 values less than (738476), -partition p962 values less than (738477), -partition p963 values less than (738478), -partition p964 values less than (738479), -partition p965 values less than (738480), -partition p966 values less than (738481), -partition p967 values less than (738482), -partition p968 values less than (738483), -partition p969 values less than (738484), -partition p970 values less than (738485), -partition p971 values less than (738486), -partition p972 values less than (738487), -partition p973 values less than (738488), -partition p974 values less than (738489), -partition p975 values less than (738490), -partition p976 values less than (738491), -partition p977 values less than (738492), -partition p978 values less than (738493), -partition p979 values less than (738494), -partition p980 values less than (738495), -partition p981 values less than (738496), -partition p982 values less than (738497), -partition p983 values less than (738498), -partition p984 values less than (738499), -partition p985 values less than (738500), -partition p986 values less than (738501), -partition p987 values less than (738502), -partition p988 values less than (738503), -partition p989 values less than (738504), -partition p990 values less than (738505), -partition p991 values less than (738506), -partition p992 values less than (738507), -partition p993 values less than (738508), -partition p994 values less than (738509), -partition p995 values less than (738510), -partition p996 values less than (738511), -partition p997 values less than (738512), -partition p998 values less than (738513), -partition p999 values less than (738514), -partition p1000 values less than (738515), -partition p1001 values less than (738516), -partition p1002 values less than (738517), -partition p1003 values less than (738518), -partition p1004 values less than (738519), -partition p1005 values less than (738520), -partition p1006 values less than (738521), -partition p1007 values less than (738522), -partition p1008 values less than (738523), -partition p1009 values less than (738524), -partition p1010 values less than (738525), -partition p1011 values less than (738526), -partition p1012 values less than (738527), -partition p1013 values less than (738528), -partition p1014 values less than (738529), -partition p1015 values less than (738530), -partition p1016 values less than (738531), -partition p1017 values less than (738532), -partition p1018 values less than (738533), -partition p1019 values less than (738534), -partition p1020 values less than (738535), -partition p1021 values less than (738536), -partition p1022 values less than (738537), -partition p1023 values less than (738538) -)`) - - _, err := se.Execute(ctx, "analyze table t") - if err != nil { - b.Fatal(err) - } - alloc := chunk.NewAllocator() - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where dt > to_days('2019-04-01 21:00:00') and dt < to_days('2019-04-07 23:59:59')") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkRangeColumnPartitionPruning(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - - var build strings.Builder - build.WriteString(`create table t (id int, dt date) partition by range columns (dt) (`) - start := time.Date(2020, 5, 15, 0, 0, 0, 0, time.UTC) - for i := 0; i < 1023; i++ { - start = start.Add(24 * time.Hour) - fmt.Fprintf(&build, "partition p%d values less than ('%s'),\n", i, start.Format(time.DateOnly)) - } - build.WriteString("partition p1023 values less than maxvalue)") - mustExecute(se, build.String()) - alloc := chunk.NewAllocator() - _, err := se.Execute(ctx, "analyze table t") - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where dt > '2020-05-01' and dt < '2020-06-07'") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkHashPartitionPruningPointSelect(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - - alloc := chunk.NewAllocator() - mustExecute(se, `create table t (id int, dt datetime) partition by hash(id) partitions 1024;`) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where id = 2330") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkHashPartitionPruningMultiSelect(b *testing.B) { - ctx := context.Background() - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - - alloc := chunk.NewAllocator() - mustExecute(se, `create table t (id int, dt datetime) partition by hash(id) partitions 1024;`) - b.ResetTimer() - for i := 0; i < b.N; i++ { - rs, err := se.Execute(ctx, "select * from t where id = 2330") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - rs, err = se.Execute(ctx, "select * from t where id = 1233 or id = 1512") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - rs, err = se.Execute(ctx, "select * from t where id in (117, 1233, 15678)") - if err != nil { - b.Fatal(err) - } - _, err = drainRecordSet(ctx, se.(*session), rs[0], alloc) - if err != nil { - b.Fatal(err) - } - alloc.Reset() - } - b.StopTimer() -} - -func BenchmarkInsertIntoSelect(b *testing.B) { - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, `set @@tidb_enable_mutation_checker = 0`) - mustExecute(se, `set @@tmp_table_size = 1000000000`) - mustExecute(se, `create global temporary table tmp (id int, dt varchar(512)) on commit delete rows`) - mustExecute(se, `create table src (id int, dt varchar(512))`) - for i := 0; i < 100; i++ { - mustExecute(se, "begin") - for lines := 0; lines < 100; lines++ { - mustExecute(se, "insert into src values (42, repeat('x', 512)), (66, repeat('x', 512))") - } - mustExecute(se, "commit") - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - mustExecute(se, "insert into tmp select * from src") - } - b.StopTimer() -} - -func BenchmarkCompileStmt(b *testing.B) { - // See issue https://github.com/pingcap/tidb/issues/27633 - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - - mustExecute(se, `CREATE TABLE item1 ( - a varchar(200) DEFAULT NULL, - b varchar(480) DEFAULT NULL, - c varchar(200) DEFAULT NULL, - d varchar(200) DEFAULT NULL, - e varchar(200) DEFAULT NULL, - f varchar(200) DEFAULT NULL, - g varchar(3999) DEFAULT NULL, - h bigint(38) DEFAULT NULL, - i varchar(80) DEFAULT NULL, - j bigint(38) DEFAULT NULL, - k varchar(480) DEFAULT NULL, - l varchar(480) DEFAULT NULL, - m decimal(18,4) DEFAULT NULL, - n decimal(18,4) DEFAULT NULL, - o decimal(22,8) DEFAULT NULL, - p varchar(8) DEFAULT NULL, - q decimal(18,4) DEFAULT NULL, - r decimal(18,4) DEFAULT NULL, - s varchar(40) DEFAULT NULL, - t decimal(18,4) DEFAULT NULL, - u decimal(18,4) DEFAULT NULL, - v decimal(18,4) DEFAULT NULL, - w decimal(18,5) DEFAULT NULL, - x decimal(12,8) DEFAULT NULL, - y varchar(40) DEFAULT NULL, - z decimal(12,8) DEFAULT NULL, - a1 decimal(18,4) DEFAULT NULL, - b1 decimal(18,4) DEFAULT NULL, - c1 decimal(18,4) DEFAULT NULL, - d1 decimal(18,4) DEFAULT NULL, - e1 decimal(12,8) DEFAULT NULL, - f1 varchar(40) DEFAULT NULL, - g1 decimal(18,4) DEFAULT NULL, - h1 decimal(18,4) DEFAULT NULL, - i1 decimal(18,4) DEFAULT NULL, - j1 decimal(18,4) DEFAULT NULL, - k1 varchar(40) DEFAULT NULL, - l1 decimal(14,8) DEFAULT NULL, - m1 bigint(38) DEFAULT NULL, - n1 varchar(8) DEFAULT NULL, - o1 varchar(40) DEFAULT NULL, - p1 decimal(12,8) DEFAULT NULL, - q1 varchar(480) DEFAULT NULL, - r1 varchar(480) DEFAULT NULL, - s1 decimal(12,8) DEFAULT NULL, - t1 decimal(14,10) DEFAULT NULL, - u1 decimal(18,4) DEFAULT NULL, - v1 decimal(18,4) DEFAULT NULL, - w1 varchar(8) DEFAULT NULL, - x1 decimal(18,4) DEFAULT NULL, - y1 datetime DEFAULT NULL, - z1 datetime DEFAULT NULL, - a2 decimal(18,4) DEFAULT NULL, - b2 decimal(18,4) DEFAULT NULL, - c2 decimal(18,4) DEFAULT NULL, - d2 decimal(18,4) DEFAULT NULL, - e2 decimal(12,8) DEFAULT NULL, - f2 varchar(40) DEFAULT NULL, - g2 decimal(18,4) DEFAULT NULL, - h2 decimal(18,4) DEFAULT NULL, - i2 decimal(18,4) DEFAULT NULL, - j2 decimal(18,4) DEFAULT NULL, - k2 varchar(40) DEFAULT NULL, - l2 decimal(14,8) DEFAULT NULL, - m2 bigint(38) DEFAULT NULL, - n2 varchar(8) DEFAULT NULL, - o2 varchar(40) DEFAULT NULL, - p2 decimal(12,8) DEFAULT NULL, - q2 varchar(480) DEFAULT NULL, - r2 varchar(480) DEFAULT NULL, - s2 decimal(12,8) DEFAULT NULL, - t2 decimal(14,10) DEFAULT NULL, - u2 decimal(18,4) DEFAULT NULL, - v2 decimal(18,4) DEFAULT NULL, - w2 varchar(8) DEFAULT NULL, - x2 decimal(18,4) DEFAULT NULL, - y2 datetime DEFAULT NULL, - z2 datetime DEFAULT NULL)`) - - mustExecute(se, `CREATE TABLE item2 like item1`) - - stmtID, _, _, err := se.PrepareStmt("insert into item2 select * from item1 where a1 = ?") - if err != nil { - b.Fatal(err) - } - prepStmt, err := se.GetSessionVars().GetPreparedStmtByID(stmtID) - if err != nil { - b.Fatal(err) - } - - args := expression.Args2Expressions4Test(3401544) - - b.ResetTimer() - stmtExec := &ast.ExecuteStmt{PrepStmt: prepStmt, BinaryArgs: args} - compiler := executor.Compiler{Ctx: se} - for i := 0; i < b.N; i++ { - _, err := compiler.Compile(context.Background(), stmtExec) - if err != nil { - b.Fatal(err) - } - } - b.StopTimer() -} - -func BenchmarkAutoIncrement(b *testing.B) { - se, do, st := prepareBenchSession() - defer func() { - se.Close() - do.Close() - st.Close() - }() - mustExecute(se, "create table auto_inc (id int unsigned key nonclustered auto_increment) shard_row_id_bits=4 auto_id_cache 1;") - mustExecute(se, "set @@tidb_enable_mutation_checker = false") - b.ResetTimer() - for i := 0; i < b.N; i++ { - mustExecute(se, "insert into auto_inc values ()") - } - b.StopTimer() -} - -// TestBenchDaily collects the daily benchmark test result and generates a json output file. -// The format of the json output is described by the BenchOutput. -// Used by this command in the Makefile -// -// make bench-daily TO=xxx.json -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkPreparedPointGet, - BenchmarkPointGet, - BenchmarkBatchPointGet, - BenchmarkBasic, - BenchmarkTableScan, - BenchmarkTableLookup, - BenchmarkExplainTableLookup, - BenchmarkStringIndexScan, - BenchmarkExplainStringIndexScan, - BenchmarkStringIndexLookup, - BenchmarkIntegerIndexScan, - BenchmarkIntegerIndexLookup, - BenchmarkDecimalIndexScan, - BenchmarkDecimalIndexLookup, - BenchmarkInsertWithIndex, - BenchmarkInsertNoIndex, - BenchmarkSort, - BenchmarkJoin, - BenchmarkJoinLimit, - BenchmarkPartitionPruning, - BenchmarkRangeColumnPartitionPruning, - BenchmarkHashPartitionPruningPointSelect, - BenchmarkHashPartitionPruningMultiSelect, - BenchmarkInsertIntoSelect, - BenchmarkCompileStmt, - BenchmarkAutoIncrement, - ) -} diff --git a/session/bootstrap.go b/session/bootstrap.go deleted file mode 100644 index 0cbc79ec583bb..0000000000000 --- a/session/bootstrap.go +++ /dev/null @@ -1,3152 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package session - -import ( - "context" - "encoding/hex" - "fmt" - "os" - osuser "os/user" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table/tables" - timertable "github.com/pingcap/tidb/timer/tablestore" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/logutil" - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/timeutil" - "go.etcd.io/etcd/client/v3/concurrency" - "go.uber.org/zap" -) - -const ( - // CreateUserTable is the SQL statement creates User table in system db. - // WARNING: There are some limitations on altering the schema of mysql.user table. - // Adding columns that are nullable or have default values is permitted. - // But operations like dropping or renaming columns may break the compatibility with BR. - // REFERENCE ISSUE: https://github.com/pingcap/tidb/issues/38785 - CreateUserTable = `CREATE TABLE IF NOT EXISTS mysql.user ( - Host CHAR(255), - User CHAR(32), - authentication_string TEXT, - plugin CHAR(64), - Select_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Insert_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Update_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Delete_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Drop_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Process_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Grant_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - References_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Alter_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Show_db_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Super_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Execute_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Index_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_user_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Repl_slave_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Repl_client_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Drop_role_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Account_locked ENUM('N','Y') NOT NULL DEFAULT 'N', - Shutdown_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Reload_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - FILE_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Config_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_Tablespace_Priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Password_reuse_history smallint unsigned DEFAULT NULL, - Password_reuse_time smallint unsigned DEFAULT NULL, - User_attributes json, - Token_issuer VARCHAR(255), - Password_expired ENUM('N','Y') NOT NULL DEFAULT 'N', - Password_last_changed TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), - Password_lifetime SMALLINT UNSIGNED DEFAULT NULL, - PRIMARY KEY (Host, User));` - // CreateGlobalPrivTable is the SQL statement creates Global scope privilege table in system db. - CreateGlobalPrivTable = "CREATE TABLE IF NOT EXISTS mysql.global_priv (" + - "Host CHAR(255) NOT NULL DEFAULT ''," + - "User CHAR(80) NOT NULL DEFAULT ''," + - "Priv LONGTEXT NOT NULL DEFAULT ''," + - "PRIMARY KEY (Host, User)" + - ")" - - // For `mysql.db`, `mysql.tables_priv` and `mysql.columns_priv` table, we have a slight different - // schema definition with MySQL: columns `DB`/`Table_name`/`Column_name` are defined with case-insensitive - // collation(in MySQL, they are case-sensitive). - - // The reason behind this is that when writing those records, MySQL always converts those names into lower case - // while TiDB does not do so in early implementations, which makes some 'GRANT'/'REVOKE' operations case-sensitive. - - // In order to fix this, we decide to explicitly set case-insensitive collation for the related columns here, to - // make sure: - // * The 'GRANT'/'REVOKE' could be case-insensitive for new clusters(compatible with MySQL). - // * Keep all behaviors unchanged for upgraded cluster. - - // CreateDBPrivTable is the SQL statement creates DB scope privilege table in system db. - CreateDBPrivTable = `CREATE TABLE IF NOT EXISTS mysql.db ( - Host CHAR(255), - DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - User CHAR(32), - Select_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Insert_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Update_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Delete_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Drop_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Grant_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - References_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Index_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Alter_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Execute_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', - PRIMARY KEY (Host, DB, User));` - // CreateTablePrivTable is the SQL statement creates table scope privilege table in system db. - CreateTablePrivTable = `CREATE TABLE IF NOT EXISTS mysql.tables_priv ( - Host CHAR(255), - DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - User CHAR(32), - Table_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - Grantor CHAR(77), - Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - Table_priv SET('Select','Insert','Update','Delete','Create','Drop','Grant','Index','Alter','Create View','Show View','Trigger','References'), - Column_priv SET('Select','Insert','Update','References'), - PRIMARY KEY (Host, DB, User, Table_name));` - // CreateColumnPrivTable is the SQL statement creates column scope privilege table in system db. - CreateColumnPrivTable = `CREATE TABLE IF NOT EXISTS mysql.columns_priv( - Host CHAR(255), - DB CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - User CHAR(32), - Table_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - Column_name CHAR(64) CHARSET utf8mb4 COLLATE utf8mb4_general_ci, - Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - Column_priv SET('Select','Insert','Update','References'), - PRIMARY KEY (Host, DB, User, Table_name, Column_name));` - // CreateGlobalVariablesTable is the SQL statement creates global variable table in system db. - // TODO: MySQL puts GLOBAL_VARIABLES table in INFORMATION_SCHEMA db. - // INFORMATION_SCHEMA is a virtual db in TiDB. So we put this table in system db. - // Maybe we will put it back to INFORMATION_SCHEMA. - CreateGlobalVariablesTable = `CREATE TABLE IF NOT EXISTS mysql.GLOBAL_VARIABLES( - VARIABLE_NAME VARCHAR(64) NOT NULL PRIMARY KEY, - VARIABLE_VALUE VARCHAR(1024) DEFAULT NULL);` - // CreateTiDBTable is the SQL statement creates a table in system db. - // This table is a key-value struct contains some information used by TiDB. - // Currently we only put bootstrapped in it which indicates if the system is already bootstrapped. - CreateTiDBTable = `CREATE TABLE IF NOT EXISTS mysql.tidb( - VARIABLE_NAME VARCHAR(64) NOT NULL PRIMARY KEY, - VARIABLE_VALUE VARCHAR(1024) DEFAULT NULL, - COMMENT VARCHAR(1024));` - - // CreateHelpTopic is the SQL statement creates help_topic table in system db. - // See: https://dev.mysql.com/doc/refman/5.5/en/system-database.html#system-database-help-tables - CreateHelpTopic = `CREATE TABLE IF NOT EXISTS mysql.help_topic ( - help_topic_id INT(10) UNSIGNED NOT NULL, - name CHAR(64) NOT NULL, - help_category_id SMALLINT(5) UNSIGNED NOT NULL, - description TEXT NOT NULL, - example TEXT NOT NULL, - url TEXT NOT NULL, - PRIMARY KEY (help_topic_id) clustered, - UNIQUE KEY name (name) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help topics';` - - // CreateStatsMetaTable stores the meta of table statistics. - CreateStatsMetaTable = `CREATE TABLE IF NOT EXISTS mysql.stats_meta ( - version BIGINT(64) UNSIGNED NOT NULL, - table_id BIGINT(64) NOT NULL, - modify_count BIGINT(64) NOT NULL DEFAULT 0, - count BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, - snapshot BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, - INDEX idx_ver(version), - UNIQUE INDEX tbl(table_id) - );` - - // CreateStatsColsTable stores the statistics of table columns. - CreateStatsColsTable = `CREATE TABLE IF NOT EXISTS mysql.stats_histograms ( - table_id BIGINT(64) NOT NULL, - is_index TINYINT(2) NOT NULL, - hist_id BIGINT(64) NOT NULL, - distinct_count BIGINT(64) NOT NULL, - null_count BIGINT(64) NOT NULL DEFAULT 0, - tot_col_size BIGINT(64) NOT NULL DEFAULT 0, - modify_count BIGINT(64) NOT NULL DEFAULT 0, - version BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, - cm_sketch BLOB(6291456), - stats_ver BIGINT(64) NOT NULL DEFAULT 0, - flag BIGINT(64) NOT NULL DEFAULT 0, - correlation DOUBLE NOT NULL DEFAULT 0, - last_analyze_pos LONGBLOB DEFAULT NULL, - UNIQUE INDEX tbl(table_id, is_index, hist_id) - );` - - // CreateStatsBucketsTable stores the histogram info for every table columns. - CreateStatsBucketsTable = `CREATE TABLE IF NOT EXISTS mysql.stats_buckets ( - table_id BIGINT(64) NOT NULL, - is_index TINYINT(2) NOT NULL, - hist_id BIGINT(64) NOT NULL, - bucket_id BIGINT(64) NOT NULL, - count BIGINT(64) NOT NULL, - repeats BIGINT(64) NOT NULL, - upper_bound LONGBLOB NOT NULL, - lower_bound LONGBLOB , - ndv BIGINT NOT NULL DEFAULT 0, - UNIQUE INDEX tbl(table_id, is_index, hist_id, bucket_id) - );` - - // CreateGCDeleteRangeTable stores schemas which can be deleted by DeleteRange. - CreateGCDeleteRangeTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range ( - job_id BIGINT NOT NULL COMMENT "the DDL job ID", - element_id BIGINT NOT NULL COMMENT "the schema element ID", - start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", - end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", - ts BIGINT NOT NULL COMMENT "timestamp in uint64", - UNIQUE KEY delete_range_index (job_id, element_id) - );` - - // CreateGCDeleteRangeDoneTable stores schemas which are already deleted by DeleteRange. - CreateGCDeleteRangeDoneTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range_done ( - job_id BIGINT NOT NULL COMMENT "the DDL job ID", - element_id BIGINT NOT NULL COMMENT "the schema element ID", - start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", - end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", - ts BIGINT NOT NULL COMMENT "timestamp in uint64", - UNIQUE KEY delete_range_done_index (job_id, element_id) - );` - - // CreateStatsFeedbackTable stores the feedback info which is used to update stats. - // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. - CreateStatsFeedbackTable = `CREATE TABLE IF NOT EXISTS mysql.stats_feedback ( - table_id BIGINT(64) NOT NULL, - is_index TINYINT(2) NOT NULL, - hist_id BIGINT(64) NOT NULL, - feedback BLOB NOT NULL, - INDEX hist(table_id, is_index, hist_id) - );` - - // CreateBindInfoTable stores the sql bind info which is used to update globalBindCache. - CreateBindInfoTable = `CREATE TABLE IF NOT EXISTS mysql.bind_info ( - original_sql TEXT NOT NULL, - bind_sql TEXT NOT NULL, - default_db TEXT NOT NULL, - status TEXT NOT NULL, - create_time TIMESTAMP(3) NOT NULL, - update_time TIMESTAMP(3) NOT NULL, - charset TEXT NOT NULL, - collation TEXT NOT NULL, - source VARCHAR(10) NOT NULL DEFAULT 'unknown', - sql_digest varchar(64), - plan_digest varchar(64), - INDEX sql_index(original_sql(700),default_db(68)) COMMENT "accelerate the speed when add global binding query", - INDEX time_index(update_time) COMMENT "accelerate the speed when querying with last update time" - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` - - // CreateRoleEdgesTable stores the role and user relationship information. - CreateRoleEdgesTable = `CREATE TABLE IF NOT EXISTS mysql.role_edges ( - FROM_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', - FROM_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', - TO_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', - TO_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', - WITH_ADMIN_OPTION ENUM('N','Y') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N', - PRIMARY KEY (FROM_HOST,FROM_USER,TO_HOST,TO_USER) - );` - - // CreateDefaultRolesTable stores the active roles for a user. - CreateDefaultRolesTable = `CREATE TABLE IF NOT EXISTS mysql.default_roles ( - HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '', - USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', - DEFAULT_ROLE_HOST CHAR(60) COLLATE utf8_bin NOT NULL DEFAULT '%', - DEFAULT_ROLE_USER CHAR(32) COLLATE utf8_bin NOT NULL DEFAULT '', - PRIMARY KEY (HOST,USER,DEFAULT_ROLE_HOST,DEFAULT_ROLE_USER) - )` - - // CreateStatsTopNTable stores topn data of a cmsketch with top n. - CreateStatsTopNTable = `CREATE TABLE IF NOT EXISTS mysql.stats_top_n ( - table_id BIGINT(64) NOT NULL, - is_index TINYINT(2) NOT NULL, - hist_id BIGINT(64) NOT NULL, - value LONGBLOB, - count BIGINT(64) UNSIGNED NOT NULL, - INDEX tbl(table_id, is_index, hist_id) - );` - - // CreateStatsFMSketchTable stores FMSketch data of a column histogram. - CreateStatsFMSketchTable = `CREATE TABLE IF NOT EXISTS mysql.stats_fm_sketch ( - table_id BIGINT(64) NOT NULL, - is_index TINYINT(2) NOT NULL, - hist_id BIGINT(64) NOT NULL, - value LONGBLOB, - INDEX tbl(table_id, is_index, hist_id) - );` - - // CreateExprPushdownBlacklist stores the expressions which are not allowed to be pushed down. - CreateExprPushdownBlacklist = `CREATE TABLE IF NOT EXISTS mysql.expr_pushdown_blacklist ( - name CHAR(100) NOT NULL, - store_type CHAR(100) NOT NULL DEFAULT 'tikv,tiflash,tidb', - reason VARCHAR(200) - );` - - // CreateOptRuleBlacklist stores the list of disabled optimizing operations. - CreateOptRuleBlacklist = `CREATE TABLE IF NOT EXISTS mysql.opt_rule_blacklist ( - name CHAR(100) NOT NULL - );` - - // CreateStatsExtended stores the registered extended statistics. - CreateStatsExtended = `CREATE TABLE IF NOT EXISTS mysql.stats_extended ( - name varchar(32) NOT NULL, - type tinyint(4) NOT NULL, - table_id bigint(64) NOT NULL, - column_ids varchar(32) NOT NULL, - stats blob DEFAULT NULL, - version bigint(64) unsigned NOT NULL, - status tinyint(4) NOT NULL, - PRIMARY KEY(name, table_id), - KEY idx_1 (table_id, status, version), - KEY idx_2 (status, version) - );` - - // CreateSchemaIndexUsageTable stores the index usage information. - CreateSchemaIndexUsageTable = `CREATE TABLE IF NOT EXISTS mysql.schema_index_usage ( - TABLE_ID bigint(64), - INDEX_ID bigint(21), - QUERY_COUNT bigint(64), - ROWS_SELECTED bigint(64), - LAST_USED_AT timestamp, - PRIMARY KEY(TABLE_ID, INDEX_ID) - );` - // CreateGlobalGrantsTable stores dynamic privs - CreateGlobalGrantsTable = `CREATE TABLE IF NOT EXISTS mysql.global_grants ( - USER char(32) NOT NULL DEFAULT '', - HOST char(255) NOT NULL DEFAULT '', - PRIV char(32) NOT NULL DEFAULT '', - WITH_GRANT_OPTION enum('N','Y') NOT NULL DEFAULT 'N', - PRIMARY KEY (USER,HOST,PRIV) - );` - // CreateCapturePlanBaselinesBlacklist stores the baseline capture filter rules. - CreateCapturePlanBaselinesBlacklist = `CREATE TABLE IF NOT EXISTS mysql.capture_plan_baselines_blacklist ( - id bigint(64) auto_increment, - filter_type varchar(32) NOT NULL COMMENT "type of the filter, only db, table and frequency supported now", - filter_value varchar(32) NOT NULL, - key idx(filter_type), - primary key(id) - );` - // CreateColumnStatsUsageTable stores the column stats usage information. - CreateColumnStatsUsageTable = `CREATE TABLE IF NOT EXISTS mysql.column_stats_usage ( - table_id BIGINT(64) NOT NULL, - column_id BIGINT(64) NOT NULL, - last_used_at TIMESTAMP, - last_analyzed_at TIMESTAMP, - PRIMARY KEY (table_id, column_id) CLUSTERED - );` - // CreateTableCacheMetaTable stores the cached table meta lock information. - CreateTableCacheMetaTable = `CREATE TABLE IF NOT EXISTS mysql.table_cache_meta ( - tid bigint(11) NOT NULL DEFAULT 0, - lock_type enum('NONE','READ', 'INTEND', 'WRITE') NOT NULL DEFAULT 'NONE', - lease bigint(20) NOT NULL DEFAULT 0, - oldReadLease bigint(20) NOT NULL DEFAULT 0, - PRIMARY KEY (tid) - );` - // CreateAnalyzeOptionsTable stores the analyze options used by analyze and auto analyze. - CreateAnalyzeOptionsTable = `CREATE TABLE IF NOT EXISTS mysql.analyze_options ( - table_id BIGINT(64) NOT NULL, - sample_num BIGINT(64) NOT NULL DEFAULT 0, - sample_rate DOUBLE NOT NULL DEFAULT -1, - buckets BIGINT(64) NOT NULL DEFAULT 0, - topn BIGINT(64) NOT NULL DEFAULT -1, - column_choice enum('DEFAULT','ALL','PREDICATE','LIST') NOT NULL DEFAULT 'DEFAULT', - column_ids TEXT(19372), - PRIMARY KEY (table_id) CLUSTERED - );` - // CreateStatsHistory stores the historical stats. - CreateStatsHistory = `CREATE TABLE IF NOT EXISTS mysql.stats_history ( - table_id bigint(64) NOT NULL, - stats_data longblob NOT NULL, - seq_no bigint(64) NOT NULL comment 'sequence number of the gzipped data slice', - version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN', - create_time datetime(6) NOT NULL, - UNIQUE KEY table_version_seq (table_id, version, seq_no), - KEY table_create_time (table_id, create_time, seq_no), - KEY idx_create_time (create_time) - );` - // CreateStatsMetaHistory stores the historical meta stats. - CreateStatsMetaHistory = `CREATE TABLE IF NOT EXISTS mysql.stats_meta_history ( - table_id bigint(64) NOT NULL, - modify_count bigint(64) NOT NULL, - count bigint(64) NOT NULL, - version bigint(64) NOT NULL comment 'stats version which corresponding to stats:version in EXPLAIN', - source varchar(40) NOT NULL, - create_time datetime(6) NOT NULL, - UNIQUE KEY table_version (table_id, version), - KEY table_create_time (table_id, create_time), - KEY idx_create_time (create_time) - );` - // CreateAnalyzeJobs stores the analyze jobs. - CreateAnalyzeJobs = `CREATE TABLE IF NOT EXISTS mysql.analyze_jobs ( - id BIGINT(64) UNSIGNED NOT NULL AUTO_INCREMENT, - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - table_schema CHAR(64) NOT NULL DEFAULT '', - table_name CHAR(64) NOT NULL DEFAULT '', - partition_name CHAR(64) NOT NULL DEFAULT '', - job_info TEXT NOT NULL, - processed_rows BIGINT(64) UNSIGNED NOT NULL DEFAULT 0, - start_time TIMESTAMP, - end_time TIMESTAMP, - state ENUM('pending', 'running', 'finished', 'failed') NOT NULL, - fail_reason TEXT, - instance VARCHAR(512) NOT NULL comment 'address of the TiDB instance executing the analyze job', - process_id BIGINT(64) UNSIGNED comment 'ID of the process executing the analyze job', - PRIMARY KEY (id), - KEY (update_time) - );` - // CreateAdvisoryLocks stores the advisory locks (get_lock, release_lock). - CreateAdvisoryLocks = `CREATE TABLE IF NOT EXISTS mysql.advisory_locks ( - lock_name VARCHAR(64) NOT NULL PRIMARY KEY - );` - // CreateMDLView is a view about metadata locks. - CreateMDLView = `CREATE OR REPLACE VIEW mysql.tidb_mdl_view as ( - SELECT job_id, - db_name, - table_name, - query, - session_id, - txnstart, - tidb_decode_sql_digests(all_sql_digests, 4096) AS SQL_DIGESTS - FROM information_schema.ddl_jobs, - information_schema.cluster_tidb_trx, - information_schema.cluster_processlist - WHERE (ddl_jobs.state != 'synced' and ddl_jobs.state != 'cancelled') - AND Find_in_set(ddl_jobs.table_id, cluster_tidb_trx.related_table_ids) - AND cluster_tidb_trx.session_id = cluster_processlist.id - );` - - // CreatePlanReplayerStatusTable is a table about plan replayer status - CreatePlanReplayerStatusTable = `CREATE TABLE IF NOT EXISTS mysql.plan_replayer_status ( - sql_digest VARCHAR(128), - plan_digest VARCHAR(128), - origin_sql TEXT, - token VARCHAR(128), - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - fail_reason TEXT, - instance VARCHAR(512) NOT NULL comment 'address of the TiDB instance executing the plan replayer job');` - - // CreatePlanReplayerTaskTable is a table about plan replayer capture task - CreatePlanReplayerTaskTable = `CREATE TABLE IF NOT EXISTS mysql.plan_replayer_task ( - sql_digest VARCHAR(128) NOT NULL, - plan_digest VARCHAR(128) NOT NULL, - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (sql_digest,plan_digest));` - - // CreateStatsTableLocked stores the locked tables - CreateStatsTableLocked = `CREATE TABLE IF NOT EXISTS mysql.stats_table_locked( - table_id bigint(64) NOT NULL, - modify_count bigint(64) NOT NULL DEFAULT 0, - count bigint(64) NOT NULL DEFAULT 0, - version bigint(64) UNSIGNED NOT NULL DEFAULT 0, - PRIMARY KEY (table_id));` - - // CreatePasswordHistory is a table save history passwd. - CreatePasswordHistory = `CREATE TABLE IF NOT EXISTS mysql.password_history ( - Host char(255) NOT NULL DEFAULT '', - User char(32) NOT NULL DEFAULT '', - Password_timestamp timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - Password text, - PRIMARY KEY (Host,User,Password_timestamp ) - ) COMMENT='Password history for user accounts' ` - - // CreateTTLTableStatus is a table about TTL job schedule - CreateTTLTableStatus = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_table_status ( - table_id bigint(64) PRIMARY KEY, - parent_table_id bigint(64), - table_statistics text DEFAULT NULL, - last_job_id varchar(64) DEFAULT NULL, - last_job_start_time timestamp NULL DEFAULT NULL, - last_job_finish_time timestamp NULL DEFAULT NULL, - last_job_ttl_expire timestamp NULL DEFAULT NULL, - last_job_summary text DEFAULT NULL, - current_job_id varchar(64) DEFAULT NULL, - current_job_owner_id varchar(64) DEFAULT NULL, - current_job_owner_addr varchar(256) DEFAULT NULL, - current_job_owner_hb_time timestamp, - current_job_start_time timestamp NULL DEFAULT NULL, - current_job_ttl_expire timestamp NULL DEFAULT NULL, - current_job_state text DEFAULT NULL, - current_job_status varchar(64) DEFAULT NULL, - current_job_status_update_time timestamp NULL DEFAULT NULL);` - - // CreateTTLTask is a table about parallel ttl tasks - CreateTTLTask = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_task ( - job_id varchar(64) NOT NULL, - table_id bigint(64) NOT NULL, - scan_id int NOT NULL, - scan_range_start BLOB, - scan_range_end BLOB, - expire_time timestamp NOT NULL, - owner_id varchar(64) DEFAULT NULL, - owner_addr varchar(64) DEFAULT NULL, - owner_hb_time timestamp DEFAULT NULL, - status varchar(64) DEFAULT 'waiting', - status_update_time timestamp NULL DEFAULT NULL, - state text, - created_time timestamp NOT NULL, - primary key(job_id, scan_id), - key(created_time));` - - // CreateTTLJobHistory is a table that stores ttl job's history - CreateTTLJobHistory = `CREATE TABLE IF NOT EXISTS mysql.tidb_ttl_job_history ( - job_id varchar(64) PRIMARY KEY, - table_id bigint(64) NOT NULL, - parent_table_id bigint(64) NOT NULL, - table_schema varchar(64) NOT NULL, - table_name varchar(64) NOT NULL, - partition_name varchar(64) DEFAULT NULL, - create_time timestamp NOT NULL, - finish_time timestamp NOT NULL, - ttl_expire timestamp NOT NULL, - summary_text text, - expired_rows bigint(64) DEFAULT NULL, - deleted_rows bigint(64) DEFAULT NULL, - error_delete_rows bigint(64) DEFAULT NULL, - status varchar(64) NOT NULL, - key(table_schema, table_name, create_time), - key(parent_table_id, create_time), - key(create_time) - );` - - // CreateGlobalTask is a table about global task. - CreateGlobalTask = `CREATE TABLE IF NOT EXISTS mysql.tidb_global_task ( - id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - task_key VARCHAR(256) NOT NULL, - type VARCHAR(256) NOT NULL, - dispatcher_id VARCHAR(256), - state VARCHAR(64) NOT NULL, - start_time TIMESTAMP, - state_update_time TIMESTAMP, - meta LONGBLOB, - concurrency INT(11), - step INT(11), - error BLOB, - key(state), - UNIQUE KEY task_key(task_key) - );` - - // CreateGlobalTaskHistory is a table about history global task. - CreateGlobalTaskHistory = `CREATE TABLE IF NOT EXISTS mysql.tidb_global_task_history ( - id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - task_key VARCHAR(256) NOT NULL, - type VARCHAR(256) NOT NULL, - dispatcher_id VARCHAR(256), - state VARCHAR(64) NOT NULL, - start_time TIMESTAMP, - state_update_time TIMESTAMP, - meta LONGBLOB, - concurrency INT(11), - step INT(11), - error BLOB, - key(state), - UNIQUE KEY task_key(task_key) - );` - - // CreateDistFrameworkMeta create a system table that distributed task framework use to store meta information - CreateDistFrameworkMeta = `CREATE TABLE IF NOT EXISTS mysql.dist_framework_meta ( - host VARCHAR(100) NOT NULL PRIMARY KEY, - role VARCHAR(64), - keyspace_id bigint(8) NOT NULL DEFAULT -1);` - - // CreateLoadDataJobs is a table that LOAD DATA uses - CreateLoadDataJobs = `CREATE TABLE IF NOT EXISTS mysql.load_data_jobs ( - job_id bigint(64) NOT NULL AUTO_INCREMENT, - expected_status ENUM('running', 'paused', 'canceled') NOT NULL DEFAULT 'running', - create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - start_time TIMESTAMP(6) NULL DEFAULT NULL, - update_time TIMESTAMP(6) NULL DEFAULT NULL, - end_time TIMESTAMP(6) NULL DEFAULT NULL, - data_source TEXT NOT NULL, - table_schema VARCHAR(64) NOT NULL, - table_name VARCHAR(64) NOT NULL, - import_mode VARCHAR(64) NOT NULL, - create_user VARCHAR(32) NOT NULL, - progress TEXT DEFAULT NULL, - result_message TEXT DEFAULT NULL, - error_message TEXT DEFAULT NULL, - PRIMARY KEY (job_id), - KEY (create_time), - KEY (create_user));` - - // CreateRunawayTable stores the query which is identified as runaway or quarantined because of in watch list. - CreateRunawayTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_queries ( - resource_group_name varchar(32) not null, - time TIMESTAMP NOT NULL, - match_type varchar(12) NOT NULL, - action varchar(12) NOT NULL, - original_sql TEXT NOT NULL, - plan_digest TEXT NOT NULL, - tidb_server varchar(512), - INDEX plan_index(plan_digest(64)) COMMENT "accelerate the speed when select runaway query", - INDEX time_index(time) COMMENT "accelerate the speed when querying with active watch" - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` - - // CreateRunawayWatchTable stores the condition which is used to check whether query should be quarantined. - CreateRunawayWatchTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_watch ( - id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - resource_group_name varchar(32) not null, - start_time datetime(6) NOT NULL, - end_time datetime(6), - watch bigint(10) NOT NULL, - watch_text TEXT NOT NULL, - source varchar(512) NOT NULL, - action bigint(10), - INDEX sql_index(resource_group_name,watch_text(700)) COMMENT "accelerate the speed when select quarantined query", - INDEX time_index(end_time) COMMENT "accelerate the speed when querying with active watch" - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` - - // CreateDoneRunawayWatchTable stores the condition which is used to check whether query should be quarantined. - CreateDoneRunawayWatchTable = `CREATE TABLE IF NOT EXISTS mysql.tidb_runaway_watch_done ( - id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, - record_id BIGINT(20) not null, - resource_group_name varchar(32) not null, - start_time datetime(6) NOT NULL, - end_time datetime(6), - watch bigint(10) NOT NULL, - watch_text TEXT NOT NULL, - source varchar(512) NOT NULL, - action bigint(10), - done_time TIMESTAMP(6) NOT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;` - - // CreateImportJobs is a table that IMPORT INTO uses. - CreateImportJobs = `CREATE TABLE IF NOT EXISTS mysql.tidb_import_jobs ( - id bigint(64) NOT NULL AUTO_INCREMENT, - create_time TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), - start_time TIMESTAMP(6) NULL DEFAULT NULL, - update_time TIMESTAMP(6) NULL DEFAULT NULL, - end_time TIMESTAMP(6) NULL DEFAULT NULL, - table_schema VARCHAR(64) NOT NULL, - table_name VARCHAR(64) NOT NULL, - table_id bigint(64) NOT NULL, - created_by VARCHAR(300) NOT NULL, - parameters text NOT NULL, - source_file_size bigint(64) NOT NULL, - status VARCHAR(64) NOT NULL, - step VARCHAR(64) NOT NULL, - summary text DEFAULT NULL, - error_message TEXT DEFAULT NULL, - PRIMARY KEY (id), - KEY (created_by), - KEY (status));` -) - -// CreateTimers is a table to store all timers for tidb -var CreateTimers = timertable.CreateTimerTableSQL("mysql", "tidb_timers") - -// bootstrap initiates system DB for a store. -func bootstrap(s Session) { - startTime := time.Now() - err := InitMDLVariableForBootstrap(s.GetStore()) - if err != nil { - logutil.BgLogger().Fatal("init metadata lock error", - zap.Error(err)) - } - dom := domain.GetDomain(s) - for { - b, err := checkBootstrapped(s) - if err != nil { - logutil.BgLogger().Fatal("check bootstrap error", - zap.Error(err)) - } - // For rolling upgrade, we can't do upgrade only in the owner. - if b { - upgrade(s) - logutil.BgLogger().Info("upgrade successful in bootstrap", - zap.Duration("take time", time.Since(startTime))) - return - } - // To reduce conflict when multiple TiDB-server start at the same time. - // Actually only one server need to do the bootstrap. So we chose DDL owner to do this. - if dom.DDL().OwnerManager().IsOwner() { - doDDLWorks(s) - doDMLWorks(s) - runBootstrapSQLFile = true - logutil.BgLogger().Info("bootstrap successful", - zap.Duration("take time", time.Since(startTime))) - return - } - time.Sleep(200 * time.Millisecond) - } -} - -const ( - // varTrue is the true value in mysql.TiDB table for boolean columns. - varTrue = "True" - // varFalse is the false value in mysql.TiDB table for boolean columns. - varFalse = "False" - // The variable name in mysql.TiDB table. - // It is used for checking if the store is bootstrapped by any TiDB server. - // If the value is `True`, the store is already bootstrapped by a TiDB server. - bootstrappedVar = "bootstrapped" - // The variable name in mysql.TiDB table. - // It is used for getting the version of the TiDB server which bootstrapped the store. - tidbServerVersionVar = "tidb_server_version" - // The variable name in mysql.tidb table and it will be used when we want to know - // system timezone. - tidbSystemTZ = "system_tz" - // The variable name in mysql.tidb table and it will indicate if the new collations are enabled in the TiDB cluster. - tidbNewCollationEnabled = "new_collation_enabled" - // The variable name in mysql.tidb table and it records the default value of - // mem-quota-query when upgrade from v3.0.x to v4.0.9+. - tidbDefMemoryQuotaQuery = "default_memory_quota_query" - // The variable name in mysql.tidb table and it records the default value of - // oom-action when upgrade from v3.0.x to v4.0.11+. - tidbDefOOMAction = "default_oom_action" - // Const for TiDB server version 2. - version2 = 2 - version3 = 3 - version4 = 4 - version5 = 5 - version6 = 6 - version7 = 7 - version8 = 8 - version9 = 9 - version10 = 10 - version11 = 11 - version12 = 12 - version13 = 13 - version14 = 14 - version15 = 15 - version16 = 16 - version17 = 17 - version18 = 18 - version19 = 19 - version20 = 20 - version21 = 21 - version22 = 22 - version23 = 23 - version24 = 24 - version25 = 25 - version26 = 26 - version27 = 27 - version28 = 28 - // version29 is not needed. - version30 = 30 - version31 = 31 - version32 = 32 - version33 = 33 - version34 = 34 - version35 = 35 - version36 = 36 - version37 = 37 - version38 = 38 - // version39 will be redone in version46 so it's skipped here. - // version40 is the version that introduce new collation in TiDB, - // see https://github.com/pingcap/tidb/pull/14574 for more details. - version40 = 40 - version41 = 41 - // version42 add storeType and reason column in expr_pushdown_blacklist - version42 = 42 - // version43 updates global variables related to statement summary. - version43 = 43 - // version44 delete tidb_isolation_read_engines from mysql.global_variables to avoid unexpected behavior after upgrade. - version44 = 44 - // version45 introduces CONFIG_PRIV for SET CONFIG statements. - version45 = 45 - // version46 fix a bug in v3.1.1. - version46 = 46 - // version47 add Source to bindings to indicate the way binding created. - version47 = 47 - // version48 reset all deprecated concurrency related system-variables if they were all default value. - // version49 introduces mysql.stats_extended table. - // Both version48 and version49 will be redone in version55 and version56 so they're skipped here. - // version50 add mysql.schema_index_usage table. - version50 = 50 - // version51 introduces CreateTablespacePriv to mysql.user. - // version51 will be redone in version63 so it's skipped here. - // version52 change mysql.stats_histograms cm_sketch column from blob to blob(6291456) - version52 = 52 - // version53 introduce Global variable tidb_enable_strict_double_type_check - version53 = 53 - // version54 writes a variable `mem_quota_query` to mysql.tidb if it's a cluster upgraded from v3.0.x to v4.0.9+. - version54 = 54 - // version55 fixes the bug that upgradeToVer48 would be missed when upgrading from v4.0 to a new version - version55 = 55 - // version56 fixes the bug that upgradeToVer49 would be missed when upgrading from v4.0 to a new version - version56 = 56 - // version57 fixes the bug of concurrent create / drop binding - version57 = 57 - // version58 add `Repl_client_priv` and `Repl_slave_priv` to `mysql.user` - // version58 will be redone in version64 so it's skipped here. - // version59 add writes a variable `oom-action` to mysql.tidb if it's a cluster upgraded from v3.0.x to v4.0.11+. - version59 = 59 - // version60 redesigns `mysql.stats_extended` - version60 = 60 - // version61 will be redone in version67 - // version62 add column ndv for mysql.stats_buckets. - version62 = 62 - // version63 fixes the bug that upgradeToVer51 would be missed when upgrading from v4.0 to a new version - version63 = 63 - // version64 is redone upgradeToVer58 after upgradeToVer63, this is to preserve the order of the columns in mysql.user - version64 = 64 - // version65 add mysql.stats_fm_sketch table. - version65 = 65 - // version66 enables the feature `track_aggregate_memory_usage` by default. - version66 = 66 - // version67 restore all SQL bindings. - version67 = 67 - // version68 update the global variable 'tidb_enable_clustered_index' from 'off' to 'int_only'. - version68 = 68 - // version69 adds mysql.global_grants for DYNAMIC privileges - version69 = 69 - // version70 adds mysql.user.plugin to allow multiple authentication plugins - version70 = 70 - // version71 forces tidb_multi_statement_mode=OFF when tidb_multi_statement_mode=WARN - // This affects upgrades from v4.0 where the default was WARN. - version71 = 71 - // version72 adds snapshot column for mysql.stats_meta - version72 = 72 - // version73 adds mysql.capture_plan_baselines_blacklist table - version73 = 73 - // version74 changes global variable `tidb_stmt_summary_max_stmt_count` value from 200 to 3000. - version74 = 74 - // version75 update mysql.*.host from char(60) to char(255) - version75 = 75 - // version76 update mysql.columns_priv from SET('Select','Insert','Update') to SET('Select','Insert','Update','References') - version76 = 76 - // version77 adds mysql.column_stats_usage table - version77 = 77 - // version78 updates mysql.stats_buckets.lower_bound, mysql.stats_buckets.upper_bound and mysql.stats_histograms.last_analyze_pos from BLOB to LONGBLOB. - version78 = 78 - // version79 adds the mysql.table_cache_meta table - version79 = 79 - // version80 fixes the issue https://github.com/pingcap/tidb/issues/25422. - // If the TiDB upgrading from the 4.x to a newer version, we keep the tidb_analyze_version to 1. - version80 = 80 - // version81 insert "tidb_enable_index_merge|off" to mysql.GLOBAL_VARIABLES if there is no tidb_enable_index_merge. - // This will only happens when we upgrade a cluster before 4.0.0 to 4.0.0+. - version81 = 81 - // version82 adds the mysql.analyze_options table - version82 = 82 - // version83 adds the tables mysql.stats_history - version83 = 83 - // version84 adds the tables mysql.stats_meta_history - version84 = 84 - // version85 updates bindings with status 'using' in mysql.bind_info table to 'enabled' status - version85 = 85 - // version86 update mysql.tables_priv from SET('Select','Insert','Update') to SET('Select','Insert','Update','References'). - version86 = 86 - // version87 adds the mysql.analyze_jobs table - version87 = 87 - // version88 fixes the issue https://github.com/pingcap/tidb/issues/33650. - version88 = 88 - // version89 adds the tables mysql.advisory_locks - version89 = 89 - // version90 converts enable-batch-dml, mem-quota-query, query-log-max-len, committer-concurrency, run-auto-analyze, and oom-action to a sysvar - version90 = 90 - // version91 converts prepared-plan-cache to sysvars - version91 = 91 - // version92 for concurrent ddl. - version92 = 92 - // version93 converts oom-use-tmp-storage to a sysvar - version93 = 93 - version94 = 94 - // version95 add a column `User_attributes` to `mysql.user` - version95 = 95 - // version97 sets tidb_opt_range_max_size to 0 when a cluster upgrades from some version lower than v6.4.0 to v6.4.0+. - // It promises the compatibility of building ranges behavior. - version97 = 97 - // version98 add a column `Token_issuer` to `mysql.user` - version98 = 98 - version99 = 99 - // version100 converts server-memory-quota to a sysvar - version100 = 100 - // version101 add mysql.plan_replayer_status table - version101 = 101 - // version102 add mysql.plan_replayer_task table - version102 = 102 - // version103 adds the tables mysql.stats_table_locked - version103 = 103 - // version104 add `sql_digest` and `plan_digest` to `bind_info` - version104 = 104 - // version105 insert "tidb_cost_model_version|1" to mysql.GLOBAL_VARIABLES if there is no tidb_cost_model_version. - // This will only happens when we upgrade a cluster before 6.0. - version105 = 105 - // version106 add mysql.password_history, and Password_reuse_history, Password_reuse_time into mysql.user. - version106 = 106 - // version107 add columns related to password expiration into mysql.user - version107 = 107 - // version108 adds the table tidb_ttl_table_status - version108 = 108 - // version109 sets tidb_enable_gc_aware_memory_track to off when a cluster upgrades from some version lower than v6.5.0. - version109 = 109 - // ... - // [version110, version129] is the version range reserved for patches of 6.5.x - // ... - // version110 sets tidb_stats_load_pseudo_timeout to ON when a cluster upgrades from some version lower than v6.5.0. - version110 = 110 - // version130 add column source to mysql.stats_meta_history - version130 = 130 - // version131 adds the table tidb_ttl_task and tidb_ttl_job_history - version131 = 131 - // version132 modifies the view tidb_mdl_view - version132 = 132 - // version133 sets tidb_server_memory_limit to "80%" - version133 = 133 - // version134 modifies the following global variables default value: - // - foreign_key_checks: off -> on - // - tidb_enable_foreign_key: off -> on - // - tidb_store_batch_size: 0 -> 4 - version134 = 134 - // version135 sets tidb_opt_advanced_join_hint to off when a cluster upgrades from some version lower than v7.0. - version135 = 135 - // version136 prepare the tables for the distributed task. - version136 = 136 - // version137 introduces some reserved resource groups - version137 = 137 - // version 138 set tidb_enable_null_aware_anti_join to true - version138 = 138 - // version 139 creates mysql.load_data_jobs table for LOAD DATA statement - version139 = 139 - // version 140 add column task_key to mysql.tidb_global_task - version140 = 140 - // version 141 - // set the value of `tidb_session_plan_cache_size` to "tidb_prepared_plan_cache_size" if there is no `tidb_session_plan_cache_size`. - // update tidb_load_based_replica_read_threshold from 0 to 4 - // This will only happens when we upgrade a cluster before 7.1. - version141 = 141 - // version 142 insert "tidb_enable_non_prepared_plan_cache|0" to mysql.GLOBAL_VARIABLES if there is no tidb_enable_non_prepared_plan_cache. - // This will only happens when we upgrade a cluster before 6.5. - version142 = 142 - // version 143 add column `error` to `mysql.tidb_global_task` and `mysql.tidb_background_subtask` - version143 = 143 - // version 144 turn off `tidb_plan_cache_invalidation_on_fresh_stats`, which is introduced in 7.1-rc, - // if it's upgraded from an existing old version cluster. - version144 = 144 - // version 145 to only add a version make we know when we support upgrade state. - version145 = 145 - // version 146 add index for mysql.stats_meta_history and mysql.stats_history. - version146 = 146 - // ... - // [version147, version166] is the version range reserved for patches of 7.1.x - // ... - // version 167 add column `step` to `mysql.tidb_background_subtask` - version167 = 167 - version168 = 168 - // version 169 - // create table `mysql.tidb_runaway_quarantined_watch` and table `mysql.tidb_runaway_queries` - // to save runaway query records and persist runaway watch at 7.2 version. - // but due to ver171 recreate `mysql.tidb_runaway_watch`, - // no need to create table `mysql.tidb_runaway_quarantined_watch`, so delete it. - version169 = 169 - version170 = 170 - // version 171 - // keep the tidb_server length same as instance in other tables. - version171 = 171 - // version 172 - // create table `mysql.tidb_runaway_watch` and table `mysql.tidb_runaway_watch_done` - // to persist runaway watch and deletion of runaway watch at 7.3. - version172 = 172 - // version 173 add column `summary` to `mysql.tidb_background_subtask`. - version173 = 173 - // version 174 - // add column `step`, `error`; delete unique key; and add key idx_state_update_time - // to `mysql.tidb_background_subtask_history`. - version174 = 174 - - // version 175 - // update normalized bindings of `in (?)` to `in (...)` to solve #44298. - version175 = 175 - - // version 176 - // add `mysql.tidb_global_task_history`. - version176 = 176 -) - -// currentBootstrapVersion is defined as a variable, so we can modify its value for testing. -// please make sure this is the largest version -var currentBootstrapVersion int64 = version176 - -// DDL owner key's expired time is ManagerSessionTTL seconds, we should wait the time and give more time to have a chance to finish it. -var internalSQLTimeout = owner.ManagerSessionTTL + 15 - -// whether to run the sql file in bootstrap. -var runBootstrapSQLFile = false - -// DisableRunBootstrapSQLFileInTest only used for test -func DisableRunBootstrapSQLFileInTest() { - if intest.InTest { - runBootstrapSQLFile = false - } -} - -var ( - bootstrapVersion = []func(Session, int64){ - upgradeToVer2, - upgradeToVer3, - upgradeToVer4, - upgradeToVer5, - upgradeToVer6, - upgradeToVer7, - upgradeToVer8, - upgradeToVer9, - upgradeToVer10, - upgradeToVer11, - upgradeToVer12, - upgradeToVer13, - upgradeToVer14, - upgradeToVer15, - upgradeToVer16, - upgradeToVer17, - upgradeToVer18, - upgradeToVer19, - upgradeToVer20, - upgradeToVer21, - upgradeToVer22, - upgradeToVer23, - upgradeToVer24, - upgradeToVer25, - upgradeToVer26, - upgradeToVer27, - upgradeToVer28, - upgradeToVer29, - upgradeToVer30, - upgradeToVer31, - upgradeToVer32, - upgradeToVer33, - upgradeToVer34, - upgradeToVer35, - upgradeToVer36, - upgradeToVer37, - upgradeToVer38, - // We will redo upgradeToVer39 in upgradeToVer46, - // so upgradeToVer39 is skipped here. - upgradeToVer40, - upgradeToVer41, - upgradeToVer42, - upgradeToVer43, - upgradeToVer44, - upgradeToVer45, - upgradeToVer46, - upgradeToVer47, - // We will redo upgradeToVer48 and upgradeToVer49 in upgradeToVer55 and upgradeToVer56, - // so upgradeToVer48 and upgradeToVer49 is skipped here. - upgradeToVer50, - // We will redo upgradeToVer51 in upgradeToVer63, it is skipped here. - upgradeToVer52, - upgradeToVer53, - upgradeToVer54, - upgradeToVer55, - upgradeToVer56, - upgradeToVer57, - // We will redo upgradeToVer58 in upgradeToVer64, it is skipped here. - upgradeToVer59, - upgradeToVer60, - // We will redo upgradeToVer61 in upgradeToVer67, it is skipped here. - upgradeToVer62, - upgradeToVer63, - upgradeToVer64, - upgradeToVer65, - upgradeToVer66, - upgradeToVer67, - upgradeToVer68, - upgradeToVer69, - upgradeToVer70, - upgradeToVer71, - upgradeToVer72, - upgradeToVer73, - upgradeToVer74, - upgradeToVer75, - upgradeToVer76, - upgradeToVer77, - upgradeToVer78, - upgradeToVer79, - upgradeToVer80, - upgradeToVer81, - upgradeToVer82, - upgradeToVer83, - upgradeToVer84, - upgradeToVer85, - upgradeToVer86, - upgradeToVer87, - upgradeToVer88, - upgradeToVer89, - upgradeToVer90, - upgradeToVer91, - upgradeToVer93, - upgradeToVer94, - upgradeToVer95, - // We will redo upgradeToVer96 in upgradeToVer100, it is skipped here. - upgradeToVer97, - upgradeToVer98, - upgradeToVer100, - upgradeToVer101, - upgradeToVer102, - upgradeToVer103, - upgradeToVer104, - upgradeToVer105, - upgradeToVer106, - upgradeToVer107, - upgradeToVer108, - upgradeToVer109, - upgradeToVer110, - upgradeToVer130, - upgradeToVer131, - upgradeToVer132, - upgradeToVer133, - upgradeToVer134, - upgradeToVer135, - upgradeToVer136, - upgradeToVer137, - upgradeToVer138, - upgradeToVer139, - upgradeToVer140, - upgradeToVer141, - upgradeToVer142, - upgradeToVer143, - upgradeToVer144, - // We will only use Ver145 to differentiate versions, so it is skipped here. - upgradeToVer146, - upgradeToVer167, - upgradeToVer168, - upgradeToVer169, - upgradeToVer170, - upgradeToVer171, - upgradeToVer172, - upgradeToVer173, - upgradeToVer174, - upgradeToVer175, - upgradeToVer176, - } -) - -func checkBootstrapped(s Session) (bool, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - // Check if system db exists. - _, err := s.ExecuteInternal(ctx, "USE %n", mysql.SystemDB) - if err != nil && infoschema.ErrDatabaseNotExists.NotEqual(err) { - logutil.BgLogger().Fatal("check bootstrap error", - zap.Error(err)) - } - // Check bootstrapped variable value in TiDB table. - sVal, _, err := getTiDBVar(s, bootstrappedVar) - if err != nil { - if infoschema.ErrTableNotExists.Equal(err) { - return false, nil - } - return false, errors.Trace(err) - } - isBootstrapped := sVal == varTrue - if isBootstrapped { - // Make sure that doesn't affect the following operations. - if err = s.CommitTxn(ctx); err != nil { - return false, errors.Trace(err) - } - } - return isBootstrapped, nil -} - -// getTiDBVar gets variable value from mysql.tidb table. -// Those variables are used by TiDB server. -func getTiDBVar(s Session, name string) (sVal string, isNull bool, e error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, `SELECT HIGH_PRIORITY VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME= %?`, - mysql.SystemDB, - mysql.TiDBTable, - name, - ) - if err != nil { - return "", true, errors.Trace(err) - } - if rs == nil { - return "", true, errors.New("Wrong number of Recordset") - } - defer terror.Call(rs.Close) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - if err != nil || req.NumRows() == 0 { - return "", true, errors.Trace(err) - } - row := req.GetRow(0) - if row.IsNull(0) { - return "", true, nil - } - return row.GetString(0), false, nil -} - -var ( - // SupportUpgradeHTTPOpVer is exported for testing. - // The minimum version of the upgrade by paused user DDL can be notified through the HTTP API. - SupportUpgradeHTTPOpVer int64 = version174 -) - -// upgrade function will do some upgrade works, when the system is bootstrapped by low version TiDB server -// For example, add new system variables into mysql.global_variables table. -func upgrade(s Session) { - ver, err := getBootstrapVersion(s) - terror.MustNil(err) - if ver >= currentBootstrapVersion { - // It is already bootstrapped/upgraded by a higher version TiDB server. - return - } - printClusterState(s, ver) - - // Only upgrade from under version92 and this TiDB is not owner set. - // The owner in older tidb does not support concurrent DDL, we should add the internal DDL to job queue. - if ver < version92 { - useConcurrentDDL, err := checkOwnerVersion(context.Background(), domain.GetDomain(s)) - if err != nil { - logutil.BgLogger().Fatal("[upgrade] upgrade failed", zap.Error(err)) - } - if !useConcurrentDDL { - // Use another variable DDLForce2Queue but not EnableConcurrentDDL since in upgrade it may set global variable, the initial step will - // overwrite variable EnableConcurrentDDL. - variable.DDLForce2Queue.Store(true) - } - } - // Do upgrade works then update bootstrap version. - isNull, err := InitMDLVariableForUpgrade(s.GetStore()) - if err != nil { - logutil.BgLogger().Fatal("[upgrade] init metadata lock failed", zap.Error(err)) - } - - if isNull { - upgradeToVer99Before(s) - } - - // It is only used in test. - addMockBootstrapVersionForTest(s) - for _, upgrade := range bootstrapVersion { - upgrade(s, ver) - } - if isNull { - upgradeToVer99After(s) - } - - variable.DDLForce2Queue.Store(false) - updateBootstrapVer(s) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - _, err = s.ExecuteInternal(ctx, "COMMIT") - - if err != nil { - sleepTime := 1 * time.Second - logutil.BgLogger().Info("update bootstrap ver failed", - zap.Error(err), zap.Duration("sleeping time", sleepTime)) - time.Sleep(sleepTime) - // Check if TiDB is already upgraded. - v, err1 := getBootstrapVersion(s) - if err1 != nil { - logutil.BgLogger().Fatal("upgrade failed", zap.Error(err1)) - } - if v >= currentBootstrapVersion { - // It is already bootstrapped/upgraded by a higher version TiDB server. - return - } - logutil.BgLogger().Fatal("[upgrade] upgrade failed", - zap.Int64("from", ver), - zap.Int64("to", currentBootstrapVersion), - zap.Error(err)) - } -} - -// checkOwnerVersion is used to wait the DDL owner to be elected in the cluster and check it is the same version as this TiDB. -func checkOwnerVersion(ctx context.Context, dom *domain.Domain) (bool, error) { - ticker := time.NewTicker(100 * time.Millisecond) - defer ticker.Stop() - logutil.BgLogger().Info("Waiting for the DDL owner to be elected in the cluster") - for { - select { - case <-ctx.Done(): - return false, ctx.Err() - case <-ticker.C: - ownerID, err := dom.DDL().OwnerManager().GetOwnerID(ctx) - if err == concurrency.ErrElectionNoLeader { - continue - } - info, err := infosync.GetAllServerInfo(ctx) - if err != nil { - return false, err - } - if s, ok := info[ownerID]; ok { - return s.Version == mysql.ServerVersion, nil - } - } - } -} - -// upgradeToVer2 updates to version 2. -func upgradeToVer2(s Session, ver int64) { - if ver >= version2 { - return - } - // Version 2 add two system variable for DistSQL concurrency controlling. - // Insert distsql related system variable. - distSQLVars := []string{variable.TiDBDistSQLScanConcurrency} - values := make([]string, 0, len(distSQLVars)) - for _, v := range distSQLVars { - value := fmt.Sprintf(`("%s", "%s")`, v, variable.GetSysVar(v).Value) - values = append(values, value) - } - sql := fmt.Sprintf("INSERT HIGH_PRIORITY IGNORE INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, - strings.Join(values, ", ")) - mustExecute(s, sql) -} - -// upgradeToVer3 updates to version 3. -func upgradeToVer3(s Session, ver int64) { - if ver >= version3 { - return - } - // Version 3 fix tx_read_only variable value. - mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n SET variable_value = '0' WHERE variable_name = 'tx_read_only';", mysql.SystemDB, mysql.GlobalVariablesTable) -} - -// upgradeToVer4 updates to version 4. -func upgradeToVer4(s Session, ver int64) { - if ver >= version4 { - return - } - mustExecute(s, CreateStatsMetaTable) -} - -func upgradeToVer5(s Session, ver int64) { - if ver >= version5 { - return - } - mustExecute(s, CreateStatsColsTable) - mustExecute(s, CreateStatsBucketsTable) -} - -func upgradeToVer6(s Session, ver int64) { - if ver >= version6 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Super_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_db_priv`", infoschema.ErrColumnExists) - // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Super_priv='Y'") -} - -func upgradeToVer7(s Session, ver int64) { - if ver >= version7 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Process_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Drop_priv`", infoschema.ErrColumnExists) - // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Process_priv='Y'") -} - -func upgradeToVer8(s Session, ver int64) { - if ver >= version8 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - // This is a dummy upgrade, it checks whether upgradeToVer7 success, if not, do it again. - if _, err := s.ExecuteInternal(ctx, "SELECT HIGH_PRIORITY `Process_priv` FROM mysql.user LIMIT 0"); err == nil { - return - } - upgradeToVer7(s, ver) -} - -func upgradeToVer9(s Session, ver int64) { - if ver >= version9 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Trigger_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", infoschema.ErrColumnExists) - // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Trigger_priv='Y'") -} - -func doReentrantDDL(s Session, sql string, ignorableErrs ...error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(internalSQLTimeout)*time.Second) - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) - _, err := s.ExecuteInternal(ctx, sql) - defer cancel() - for _, ignorableErr := range ignorableErrs { - if terror.ErrorEqual(err, ignorableErr) { - return - } - } - if err != nil { - logutil.BgLogger().Fatal("doReentrantDDL error", zap.Error(err)) - } -} - -func upgradeToVer10(s Session, ver int64) { - if ver >= version10 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets CHANGE COLUMN `value` `upper_bound` BLOB NOT NULL", infoschema.ErrColumnNotExists, infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets ADD COLUMN `lower_bound` BLOB", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `null_count` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN distinct_ratio", dbterror.ErrCantDropFieldOrKey) - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN use_count_to_estimate", dbterror.ErrCantDropFieldOrKey) -} - -func upgradeToVer11(s Session, ver int64) { - if ver >= version11 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `References_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET References_priv='Y'") -} - -func upgradeToVer12(s Session, ver int64) { - if ver >= version12 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - _, err := s.ExecuteInternal(ctx, "BEGIN") - terror.MustNil(err) - sql := "SELECT HIGH_PRIORITY user, host, password FROM mysql.user WHERE password != ''" - rs, err := s.ExecuteInternal(ctx, sql) - if terror.ErrorEqual(err, core.ErrUnknownColumn) { - sql := "SELECT HIGH_PRIORITY user, host, authentication_string FROM mysql.user WHERE authentication_string != ''" - rs, err = s.ExecuteInternal(ctx, sql) - } - terror.MustNil(err) - sqls := make([]string, 0, 1) - defer terror.Call(rs.Close) - req := rs.NewChunk(nil) - it := chunk.NewIterator4Chunk(req) - err = rs.Next(ctx, req) - for err == nil && req.NumRows() != 0 { - for row := it.Begin(); row != it.End(); row = it.Next() { - user := row.GetString(0) - host := row.GetString(1) - pass := row.GetString(2) - var newPass string - newPass, err = oldPasswordUpgrade(pass) - terror.MustNil(err) - updateSQL := fmt.Sprintf(`UPDATE HIGH_PRIORITY mysql.user SET password = "%s" WHERE user="%s" AND host="%s"`, newPass, user, host) - sqls = append(sqls, updateSQL) - } - err = rs.Next(ctx, req) - } - terror.MustNil(err) - - for _, sql := range sqls { - mustExecute(s, sql) - } - - sql = fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES ("%s", "%d", "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%d"`, - mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, version12, version12) - mustExecute(s, sql) - - mustExecute(s, "COMMIT") -} - -func upgradeToVer13(s Session, ver int64) { - if ver >= version13 { - return - } - sqls := []string{ - "ALTER TABLE mysql.user ADD COLUMN `Create_tmp_table_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Super_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Lock_tables_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Create_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Show_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Create_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Alter_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", - "ALTER TABLE mysql.user ADD COLUMN `Event_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", - } - for _, sql := range sqls { - doReentrantDDL(s, sql, infoschema.ErrColumnExists) - } - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tmp_table_priv='Y',Lock_tables_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Event_priv='Y' WHERE Super_priv='Y'") - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_view_priv='Y',Show_view_priv='Y' WHERE Create_priv='Y'") -} - -func upgradeToVer14(s Session, ver int64) { - if ver >= version14 { - return - } - sqls := []string{ - "ALTER TABLE mysql.db ADD COLUMN `References_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Create_tmp_table_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Alter_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Lock_tables_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Create_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Lock_tables_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Show_view_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Create_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Alter_routine_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Event_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", - "ALTER TABLE mysql.db ADD COLUMN `Trigger_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Event_priv`", - } - for _, sql := range sqls { - doReentrantDDL(s, sql, infoschema.ErrColumnExists) - } -} - -func upgradeToVer15(s Session, ver int64) { - if ver >= version15 { - return - } - doReentrantDDL(s, CreateGCDeleteRangeTable) -} - -func upgradeToVer16(s Session, ver int64) { - if ver >= version16 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `cm_sketch` BLOB", infoschema.ErrColumnExists) -} - -func upgradeToVer17(s Session, ver int64) { - if ver >= version17 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user MODIFY User CHAR(32)") -} - -func upgradeToVer18(s Session, ver int64) { - if ver >= version18 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `tot_col_size` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) -} - -func upgradeToVer19(s Session, ver int64) { - if ver >= version19 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.db MODIFY User CHAR(32)") - doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY User CHAR(32)") - doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY User CHAR(32)") -} - -func upgradeToVer20(s Session, ver int64) { - if ver >= version20 { - return - } - // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. - doReentrantDDL(s, CreateStatsFeedbackTable) -} - -func upgradeToVer21(s Session, ver int64) { - if ver >= version21 { - return - } - mustExecute(s, CreateGCDeleteRangeDoneTable) - - doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX job_id", dbterror.ErrCantDropFieldOrKey) - doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range ADD UNIQUE INDEX delete_range_index (job_id, element_id)", dbterror.ErrDupKeyName) - doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX element_id", dbterror.ErrCantDropFieldOrKey) -} - -func upgradeToVer22(s Session, ver int64) { - if ver >= version22 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `stats_ver` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) -} - -func upgradeToVer23(s Session, ver int64) { - if ver >= version23 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `flag` BIGINT(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) -} - -// writeSystemTZ writes system timezone info into mysql.tidb -func writeSystemTZ(s Session) { - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, "TiDB Global System Timezone.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE= %?`, - mysql.SystemDB, - mysql.TiDBTable, - tidbSystemTZ, - timeutil.InferSystemTZ(), - timeutil.InferSystemTZ(), - ) -} - -// upgradeToVer24 initializes `System` timezone according to docs/design/2018-09-10-adding-tz-env.md -func upgradeToVer24(s Session, ver int64) { - if ver >= version24 { - return - } - writeSystemTZ(s) -} - -// upgradeToVer25 updates tidb_max_chunk_size to new low bound value 32 if previous value is small than 32. -func upgradeToVer25(s Session, ver int64) { - if ver >= version25 { - return - } - sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %[1]s.%[2]s SET VARIABLE_VALUE = '%[4]d' WHERE VARIABLE_NAME = '%[3]s' AND VARIABLE_VALUE < %[4]d", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBMaxChunkSize, variable.DefInitChunkSize) - mustExecute(s, sql) -} - -func upgradeToVer26(s Session, ver int64) { - if ver >= version26 { - return - } - mustExecute(s, CreateRoleEdgesTable) - mustExecute(s, CreateDefaultRolesTable) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Create_role_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Drop_role_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Account_locked` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - // user with Create_user_Priv privilege should have Create_view_priv and Show_view_priv after upgrade to v3.0 - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_role_priv='Y',Drop_role_priv='Y' WHERE Create_user_priv='Y'") - // user with Create_Priv privilege should have Create_view_priv and Show_view_priv after upgrade to v3.0 - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_view_priv='Y',Show_view_priv='Y' WHERE Create_priv='Y'") -} - -func upgradeToVer27(s Session, ver int64) { - if ver >= version27 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `correlation` DOUBLE NOT NULL DEFAULT 0", infoschema.ErrColumnExists) -} - -func upgradeToVer28(s Session, ver int64) { - if ver >= version28 { - return - } - doReentrantDDL(s, CreateBindInfoTable) -} - -func upgradeToVer29(s Session, ver int64) { - // upgradeToVer29 only need to be run when the current version is 28. - if ver != version28 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.bind_info CHANGE create_time create_time TIMESTAMP(3)") - doReentrantDDL(s, "ALTER TABLE mysql.bind_info CHANGE update_time update_time TIMESTAMP(3)") - doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD INDEX sql_index (original_sql(1024),default_db(1024))", dbterror.ErrDupKeyName) -} - -func upgradeToVer30(s Session, ver int64) { - if ver >= version30 { - return - } - mustExecute(s, CreateStatsTopNTable) -} - -func upgradeToVer31(s Session, ver int64) { - if ver >= version31 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `last_analyze_pos` BLOB DEFAULT NULL", infoschema.ErrColumnExists) -} - -func upgradeToVer32(s Session, ver int64) { - if ver >= version32 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY table_priv SET('Select','Insert','Update','Delete','Create','Drop','Grant', 'Index', 'Alter', 'Create View', 'Show View', 'Trigger', 'References')") -} - -func upgradeToVer33(s Session, ver int64) { - if ver >= version33 { - return - } - doReentrantDDL(s, CreateExprPushdownBlacklist) -} - -func upgradeToVer34(s Session, ver int64) { - if ver >= version34 { - return - } - doReentrantDDL(s, CreateOptRuleBlacklist) -} - -func upgradeToVer35(s Session, ver int64) { - if ver >= version35 { - return - } - sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %s.%s SET VARIABLE_NAME = '%s' WHERE VARIABLE_NAME = 'tidb_back_off_weight'", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBBackOffWeight) - mustExecute(s, sql) -} - -func upgradeToVer36(s Session, ver int64) { - if ver >= version36 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Shutdown_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - // A root user will have those privileges after upgrading. - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Shutdown_priv='Y' WHERE Super_priv='Y'") - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tmp_table_priv='Y',Lock_tables_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Event_priv='Y' WHERE Super_priv='Y'") -} - -func upgradeToVer37(s Session, ver int64) { - if ver >= version37 { - return - } - // when upgrade from old tidb and no 'tidb_enable_window_function' in GLOBAL_VARIABLES, init it with 0. - sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%d')", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableWindowFunction, 0) - mustExecute(s, sql) -} - -func upgradeToVer38(s Session, ver int64) { - if ver >= version38 { - return - } - doReentrantDDL(s, CreateGlobalPrivTable) -} - -func writeNewCollationParameter(s Session, flag bool) { - comment := "If the new collations are enabled. Do not edit it." - b := varFalse - if flag { - b = varTrue - } - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, - mysql.SystemDB, mysql.TiDBTable, tidbNewCollationEnabled, b, comment, b, - ) -} - -func upgradeToVer40(s Session, ver int64) { - if ver >= version40 { - return - } - // There is no way to enable new collation for an existing TiDB cluster. - writeNewCollationParameter(s, false) -} - -func upgradeToVer41(s Session, ver int64) { - if ver >= version41 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `password` `authentication_string` TEXT", infoschema.ErrColumnExists, infoschema.ErrColumnNotExists) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `password` TEXT as (`authentication_string`)", infoschema.ErrColumnExists) -} - -// writeDefaultExprPushDownBlacklist writes default expr pushdown blacklist into mysql.expr_pushdown_blacklist -func writeDefaultExprPushDownBlacklist(s Session) { - mustExecute(s, "INSERT HIGH_PRIORITY INTO mysql.expr_pushdown_blacklist VALUES"+ - "('date_add','tiflash', 'DST(daylight saving time) does not take effect in TiFlash date_add')") -} - -func upgradeToVer42(s Session, ver int64) { - if ver >= version42 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.expr_pushdown_blacklist ADD COLUMN `store_type` CHAR(100) NOT NULL DEFAULT 'tikv,tiflash,tidb'", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.expr_pushdown_blacklist ADD COLUMN `reason` VARCHAR(200)", infoschema.ErrColumnExists) - writeDefaultExprPushDownBlacklist(s) -} - -// Convert statement summary global variables to non-empty values. -func writeStmtSummaryVars(s Session) { - sql := "UPDATE %n.%n SET variable_value= %? WHERE variable_name= %? AND variable_value=''" - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, variable.BoolToOnOff(variable.DefTiDBEnableStmtSummary), variable.TiDBEnableStmtSummary) - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, variable.BoolToOnOff(variable.DefTiDBStmtSummaryInternalQuery), variable.TiDBStmtSummaryInternalQuery) - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.Itoa(variable.DefTiDBStmtSummaryRefreshInterval), variable.TiDBStmtSummaryRefreshInterval) - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.Itoa(variable.DefTiDBStmtSummaryHistorySize), variable.TiDBStmtSummaryHistorySize) - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.FormatUint(uint64(variable.DefTiDBStmtSummaryMaxStmtCount), 10), variable.TiDBStmtSummaryMaxStmtCount) - mustExecute(s, sql, mysql.SystemDB, mysql.GlobalVariablesTable, strconv.FormatUint(uint64(variable.DefTiDBStmtSummaryMaxSQLLength), 10), variable.TiDBStmtSummaryMaxSQLLength) -} - -func upgradeToVer43(s Session, ver int64) { - if ver >= version43 { - return - } - writeStmtSummaryVars(s) -} - -func upgradeToVer44(s Session, ver int64) { - if ver >= version44 { - return - } - mustExecute(s, "DELETE FROM mysql.global_variables where variable_name = \"tidb_isolation_read_engines\"") -} - -func upgradeToVer45(s Session, ver int64) { - if ver >= version45 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Config_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Config_priv='Y' WHERE Super_priv='Y'") -} - -// In v3.1.1, we wrongly replace the context of upgradeToVer39 with upgradeToVer44. If we upgrade from v3.1.1 to a newer version, -// upgradeToVer39 will be missed. So we redo upgradeToVer39 here to make sure the upgrading from v3.1.1 succeed. -func upgradeToVer46(s Session, ver int64) { - if ver >= version46 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Reload_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `File_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Reload_priv='Y' WHERE Super_priv='Y'") - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET File_priv='Y' WHERE Super_priv='Y'") -} - -func upgradeToVer47(s Session, ver int64) { - if ver >= version47 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN `source` varchar(10) NOT NULL default 'unknown'", infoschema.ErrColumnExists) -} - -func upgradeToVer50(s Session, ver int64) { - if ver >= version50 { - return - } - doReentrantDDL(s, CreateSchemaIndexUsageTable) -} - -func upgradeToVer52(s Session, ver int64) { - if ver >= version52 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms MODIFY cm_sketch BLOB(6291456)") -} - -func upgradeToVer53(s Session, ver int64) { - if ver >= version53 { - return - } - // when upgrade from old tidb and no `tidb_enable_strict_double_type_check` in GLOBAL_VARIABLES, init it with 1` - sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%d')", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableStrictDoubleTypeCheck, 0) - mustExecute(s, sql) -} - -func upgradeToVer54(s Session, ver int64) { - if ver >= version54 { - return - } - // The mem-query-quota default value is 32GB by default in v3.0, and 1GB by - // default in v4.0. - // If a cluster is upgraded from v3.0.x (bootstrapVer <= version38) to - // v4.0.9+, we'll write the default value to mysql.tidb. Thus we can get the - // default value of mem-quota-query, and promise the compatibility even if - // the tidb-server restarts. - // If it's a newly deployed cluster, we do not need to write the value into - // mysql.tidb, since no compatibility problem will happen. - - // This bootstrap task becomes obsolete in TiDB 5.0+, because it appears that the - // default value of mem-quota-query changes back to 1GB. In TiDB 6.1+ mem-quota-query - // is no longer a config option, but instead a system variable (tidb_mem_quota_query). - - if ver <= version38 { - writeMemoryQuotaQuery(s) - } -} - -// When cherry-pick upgradeToVer52 to v4.0, we wrongly name it upgradeToVer48. -// If we upgrade from v4.0 to a newer version, the real upgradeToVer48 will be missed. -// So we redo upgradeToVer48 here to make sure the upgrading from v4.0 succeeds. -func upgradeToVer55(s Session, ver int64) { - if ver >= version55 { - return - } - defValues := map[string]string{ - variable.TiDBIndexLookupConcurrency: "4", - variable.TiDBIndexLookupJoinConcurrency: "4", - variable.TiDBHashAggFinalConcurrency: "4", - variable.TiDBHashAggPartialConcurrency: "4", - variable.TiDBWindowConcurrency: "4", - variable.TiDBProjectionConcurrency: "4", - variable.TiDBHashJoinConcurrency: "5", - } - names := make([]string, 0, len(defValues)) - for n := range defValues { - names = append(names, n) - } - - selectSQL := "select HIGH_PRIORITY * from mysql.global_variables where variable_name in ('" + strings.Join(names, quoteCommaQuote) + "')" - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, selectSQL) - terror.MustNil(err) - defer terror.Call(rs.Close) - req := rs.NewChunk(nil) - it := chunk.NewIterator4Chunk(req) - err = rs.Next(ctx, req) - for err == nil && req.NumRows() != 0 { - for row := it.Begin(); row != it.End(); row = it.Next() { - n := strings.ToLower(row.GetString(0)) - v := row.GetString(1) - if defValue, ok := defValues[n]; !ok || defValue != v { - return - } - } - err = rs.Next(ctx, req) - } - terror.MustNil(err) - - mustExecute(s, "BEGIN") - v := strconv.Itoa(variable.ConcurrencyUnset) - sql := fmt.Sprintf("UPDATE %s.%s SET variable_value='%%s' WHERE variable_name='%%s'", mysql.SystemDB, mysql.GlobalVariablesTable) - for _, name := range names { - mustExecute(s, fmt.Sprintf(sql, v, name)) - } - mustExecute(s, "COMMIT") -} - -// When cherry-pick upgradeToVer54 to v4.0, we wrongly name it upgradeToVer49. -// If we upgrade from v4.0 to a newer version, the real upgradeToVer49 will be missed. -// So we redo upgradeToVer49 here to make sure the upgrading from v4.0 succeeds. -func upgradeToVer56(s Session, ver int64) { - if ver >= version56 { - return - } - doReentrantDDL(s, CreateStatsExtended) -} - -func upgradeToVer57(s Session, ver int64) { - if ver >= version57 { - return - } - insertBuiltinBindInfoRow(s) -} - -func initBindInfoTable(s Session) { - mustExecute(s, CreateBindInfoTable) - insertBuiltinBindInfoRow(s) -} - -func insertBuiltinBindInfoRow(s Session) { - mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.bind_info(original_sql, bind_sql, default_db, status, create_time, update_time, charset, collation, source) - VALUES (%?, %?, "mysql", %?, "0000-00-00 00:00:00", "0000-00-00 00:00:00", "", "", %?)`, - bindinfo.BuiltinPseudoSQL4BindLock, bindinfo.BuiltinPseudoSQL4BindLock, bindinfo.Builtin, bindinfo.Builtin, - ) -} - -func upgradeToVer59(s Session, ver int64) { - if ver >= version59 { - return - } - // The oom-action default value is log by default in v3.0, and cancel by - // default in v4.0.11+. - // If a cluster is upgraded from v3.0.x (bootstrapVer <= version59) to - // v4.0.11+, we'll write the default value to mysql.tidb. Thus we can get - // the default value of oom-action, and promise the compatibility even if - // the tidb-server restarts. - // If it's a newly deployed cluster, we do not need to write the value into - // mysql.tidb, since no compatibility problem will happen. - writeOOMAction(s) -} - -func upgradeToVer60(s Session, ver int64) { - if ver >= version60 { - return - } - mustExecute(s, "DROP TABLE IF EXISTS mysql.stats_extended") - doReentrantDDL(s, CreateStatsExtended) -} - -type bindInfo struct { - bindSQL string - status string - createTime types.Time - charset string - collation string - source string -} - -func upgradeToVer67(s Session, ver int64) { - if ver >= version67 { - return - } - bindMap := make(map[string]bindInfo) - h := &bindinfo.BindHandle{} - var err error - mustExecute(s, "BEGIN PESSIMISTIC") - - defer func() { - if err != nil { - mustExecute(s, "ROLLBACK") - return - } - - mustExecute(s, "COMMIT") - }() - mustExecute(s, h.LockBindInfoSQL()) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - var rs sqlexec.RecordSet - rs, err = s.ExecuteInternal(ctx, - `SELECT bind_sql, default_db, status, create_time, charset, collation, source - FROM mysql.bind_info - WHERE source != 'builtin' - ORDER BY update_time DESC`) - if err != nil { - logutil.BgLogger().Fatal("upgradeToVer67 error", zap.Error(err)) - } - req := rs.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - p := parser.New() - now := types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, 3) - for { - err = rs.Next(context.TODO(), req) - if err != nil { - logutil.BgLogger().Fatal("upgradeToVer67 error", zap.Error(err)) - } - if req.NumRows() == 0 { - break - } - updateBindInfo(iter, p, bindMap) - } - terror.Call(rs.Close) - - mustExecute(s, "DELETE FROM mysql.bind_info where source != 'builtin'") - for original, bind := range bindMap { - mustExecute(s, fmt.Sprintf("INSERT INTO mysql.bind_info VALUES(%s, %s, '', %s, %s, %s, %s, %s, %s)", - expression.Quote(original), - expression.Quote(bind.bindSQL), - expression.Quote(bind.status), - expression.Quote(bind.createTime.String()), - expression.Quote(now.String()), - expression.Quote(bind.charset), - expression.Quote(bind.collation), - expression.Quote(bind.source), - )) - } -} - -func updateBindInfo(iter *chunk.Iterator4Chunk, p *parser.Parser, bindMap map[string]bindInfo) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - bind := row.GetString(0) - db := row.GetString(1) - status := row.GetString(2) - - if status != bindinfo.Enabled && status != bindinfo.Using && status != bindinfo.Builtin { - continue - } - - charset := row.GetString(4) - collation := row.GetString(5) - stmt, err := p.ParseOneStmt(bind, charset, collation) - if err != nil { - logutil.BgLogger().Fatal("updateBindInfo error", zap.Error(err)) - } - originWithDB := parser.Normalize(utilparser.RestoreWithDefaultDB(stmt, db, bind)) - if _, ok := bindMap[originWithDB]; ok { - // The results are sorted in descending order of time. - // And in the following cases, duplicate originWithDB may occur - // originalText |bindText |DB - // `select * from t` |`select /*+ use_index(t, idx) */ * from t` |`test` - // `select * from test.t` |`select /*+ use_index(t, idx) */ * from test.t`|`` - // Therefore, if repeated, we can skip to keep the latest binding. - continue - } - bindMap[originWithDB] = bindInfo{ - bindSQL: utilparser.RestoreWithDefaultDB(stmt, db, bind), - status: status, - createTime: row.GetTime(3), - charset: charset, - collation: collation, - source: row.GetString(6), - } - } -} - -func writeMemoryQuotaQuery(s Session) { - comment := "memory_quota_query is 32GB by default in v3.0.x, 1GB by default in v4.0.x+" - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, - mysql.SystemDB, mysql.TiDBTable, tidbDefMemoryQuotaQuery, 32<<30, comment, 32<<30, - ) -} - -func upgradeToVer62(s Session, ver int64) { - if ver >= version62 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets ADD COLUMN `ndv` bigint not null default 0", infoschema.ErrColumnExists) -} - -func upgradeToVer63(s Session, ver int64) { - if ver >= version63 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Create_tablespace_priv` ENUM('N','Y') DEFAULT 'N'", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tablespace_priv='Y' where Super_priv='Y'") -} - -func upgradeToVer64(s Session, ver int64) { - if ver >= version64 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Repl_slave_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Repl_client_priv` ENUM('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Repl_slave_priv`", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Repl_slave_priv='Y',Repl_client_priv='Y' where Super_priv='Y'") -} - -func upgradeToVer65(s Session, ver int64) { - if ver >= version65 { - return - } - doReentrantDDL(s, CreateStatsFMSketchTable) -} - -func upgradeToVer66(s Session, ver int64) { - if ver >= version66 { - return - } - mustExecute(s, "set @@global.tidb_track_aggregate_memory_usage = 1") -} - -func upgradeToVer68(s Session, ver int64) { - if ver >= version68 { - return - } - mustExecute(s, "DELETE FROM mysql.global_variables where VARIABLE_NAME = 'tidb_enable_clustered_index' and VARIABLE_VALUE = 'OFF'") -} - -func upgradeToVer69(s Session, ver int64) { - if ver >= version69 { - return - } - doReentrantDDL(s, CreateGlobalGrantsTable) -} - -func upgradeToVer70(s Session, ver int64) { - if ver >= version70 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN plugin CHAR(64) AFTER authentication_string", infoschema.ErrColumnExists) - mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET plugin='mysql_native_password'") -} - -func upgradeToVer71(s Session, ver int64) { - if ver >= version71 { - return - } - mustExecute(s, "UPDATE mysql.global_variables SET VARIABLE_VALUE='OFF' WHERE VARIABLE_NAME = 'tidb_multi_statement_mode' AND VARIABLE_VALUE = 'WARN'") -} - -func upgradeToVer72(s Session, ver int64) { - if ver >= version72 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_meta ADD COLUMN snapshot BIGINT(64) UNSIGNED NOT NULL DEFAULT 0", infoschema.ErrColumnExists) -} - -func upgradeToVer73(s Session, ver int64) { - if ver >= version73 { - return - } - doReentrantDDL(s, CreateCapturePlanBaselinesBlacklist) -} - -func upgradeToVer74(s Session, ver int64) { - if ver >= version74 { - return - } - // The old default value of `tidb_stmt_summary_max_stmt_count` is 200, we want to enlarge this to the new default value when TiDB upgrade. - mustExecute(s, fmt.Sprintf("UPDATE mysql.global_variables SET VARIABLE_VALUE='%[1]v' WHERE VARIABLE_NAME = 'tidb_stmt_summary_max_stmt_count' AND CAST(VARIABLE_VALUE AS SIGNED) = 200", variable.DefTiDBStmtSummaryMaxStmtCount)) -} - -func upgradeToVer75(s Session, ver int64) { - if ver >= version75 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user MODIFY COLUMN Host CHAR(255)") - doReentrantDDL(s, "ALTER TABLE mysql.global_priv MODIFY COLUMN Host CHAR(255)") - doReentrantDDL(s, "ALTER TABLE mysql.db MODIFY COLUMN Host CHAR(255)") - doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY COLUMN Host CHAR(255)") - doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY COLUMN Host CHAR(255)") -} - -func upgradeToVer76(s Session, ver int64) { - if ver >= version76 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY COLUMN Column_priv SET('Select','Insert','Update','References')") -} - -func upgradeToVer77(s Session, ver int64) { - if ver >= version77 { - return - } - doReentrantDDL(s, CreateColumnStatsUsageTable) -} - -func upgradeToVer78(s Session, ver int64) { - if ver >= version78 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets MODIFY upper_bound LONGBLOB NOT NULL") - doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets MODIFY lower_bound LONGBLOB") - doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms MODIFY last_analyze_pos LONGBLOB DEFAULT NULL") -} - -func upgradeToVer79(s Session, ver int64) { - if ver >= version79 { - return - } - doReentrantDDL(s, CreateTableCacheMetaTable) -} - -func upgradeToVer80(s Session, ver int64) { - if ver >= version80 { - return - } - // Check if tidb_analyze_version exists in mysql.GLOBAL_VARIABLES. - // If not, insert "tidb_analyze_version | 1" since this is the old behavior before we introduce this variable. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBAnalyzeVersion) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBAnalyzeVersion, 1) -} - -// For users that upgrade TiDB from a pre-4.0 version, we want to disable index merge by default. -// This helps minimize query plan regressions. -func upgradeToVer81(s Session, ver int64) { - if ver >= version81 { - return - } - // Check if tidb_enable_index_merge exists in mysql.GLOBAL_VARIABLES. - // If not, insert "tidb_enable_index_merge | off". - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableIndexMerge) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableIndexMerge, variable.Off) -} - -func upgradeToVer82(s Session, ver int64) { - if ver >= version82 { - return - } - doReentrantDDL(s, CreateAnalyzeOptionsTable) -} - -func upgradeToVer83(s Session, ver int64) { - if ver >= version83 { - return - } - doReentrantDDL(s, CreateStatsHistory) -} - -func upgradeToVer84(s Session, ver int64) { - if ver >= version84 { - return - } - doReentrantDDL(s, CreateStatsMetaHistory) -} - -func upgradeToVer85(s Session, ver int64) { - if ver >= version85 { - return - } - mustExecute(s, fmt.Sprintf("UPDATE HIGH_PRIORITY mysql.bind_info SET status= '%s' WHERE status = '%s'", bindinfo.Enabled, bindinfo.Using)) -} - -func upgradeToVer86(s Session, ver int64) { - if ver >= version86 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY COLUMN Column_priv SET('Select','Insert','Update','References')") -} - -func upgradeToVer87(s Session, ver int64) { - if ver >= version87 { - return - } - doReentrantDDL(s, CreateAnalyzeJobs) -} - -func upgradeToVer88(s Session, ver int64) { - if ver >= version88 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `Repl_slave_priv` `Repl_slave_priv` ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER `Execute_priv`") - doReentrantDDL(s, "ALTER TABLE mysql.user CHANGE `Repl_client_priv` `Repl_client_priv` ENUM('N','Y') NOT NULL DEFAULT 'N' AFTER `Repl_slave_priv`") -} - -func upgradeToVer89(s Session, ver int64) { - if ver >= version89 { - return - } - doReentrantDDL(s, CreateAdvisoryLocks) -} - -// importConfigOption is a one-time import. -// It is intended to be used to convert a config option to a sysvar. -// It reads the config value from the tidb-server executing the bootstrap -// (not guaranteed to be the same on all servers), and writes a message -// to the error log. The message is important since the behavior is weird -// (changes to the config file will no longer take effect past this point). -func importConfigOption(s Session, configName, svName, valStr string) { - message := fmt.Sprintf("%s is now configured by the system variable %s. One-time importing the value specified in tidb.toml file", configName, svName) - logutil.BgLogger().Warn(message, zap.String("value", valStr)) - // We use insert ignore, since if its a duplicate we don't want to overwrite any user-set values. - sql := fmt.Sprintf("INSERT IGNORE INTO %s.%s (`VARIABLE_NAME`, `VARIABLE_VALUE`) VALUES ('%s', '%s')", - mysql.SystemDB, mysql.GlobalVariablesTable, svName, valStr) - mustExecute(s, sql) -} - -func upgradeToVer90(s Session, ver int64) { - if ver >= version90 { - return - } - valStr := variable.BoolToOnOff(config.GetGlobalConfig().EnableBatchDML) - importConfigOption(s, "enable-batch-dml", variable.TiDBEnableBatchDML, valStr) - valStr = fmt.Sprint(config.GetGlobalConfig().MemQuotaQuery) - importConfigOption(s, "mem-quota-query", variable.TiDBMemQuotaQuery, valStr) - valStr = fmt.Sprint(config.GetGlobalConfig().Log.QueryLogMaxLen) - importConfigOption(s, "query-log-max-len", variable.TiDBQueryLogMaxLen, valStr) - valStr = fmt.Sprint(config.GetGlobalConfig().Performance.CommitterConcurrency) - importConfigOption(s, "committer-concurrency", variable.TiDBCommitterConcurrency, valStr) - valStr = variable.BoolToOnOff(config.GetGlobalConfig().Performance.RunAutoAnalyze) - importConfigOption(s, "run-auto-analyze", variable.TiDBEnableAutoAnalyze, valStr) - valStr = config.GetGlobalConfig().OOMAction - importConfigOption(s, "oom-action", variable.TiDBMemOOMAction, valStr) -} - -func upgradeToVer91(s Session, ver int64) { - if ver >= version91 { - return - } - valStr := variable.BoolToOnOff(config.GetGlobalConfig().PreparedPlanCache.Enabled) - importConfigOption(s, "prepared-plan-cache.enable", variable.TiDBEnablePrepPlanCache, valStr) - - valStr = strconv.Itoa(int(config.GetGlobalConfig().PreparedPlanCache.Capacity)) - importConfigOption(s, "prepared-plan-cache.capacity", variable.TiDBPrepPlanCacheSize, valStr) - - valStr = strconv.FormatFloat(config.GetGlobalConfig().PreparedPlanCache.MemoryGuardRatio, 'f', -1, 64) - importConfigOption(s, "prepared-plan-cache.memory-guard-ratio", variable.TiDBPrepPlanCacheMemoryGuardRatio, valStr) -} - -func upgradeToVer93(s Session, ver int64) { - if ver >= version93 { - return - } - valStr := variable.BoolToOnOff(config.GetGlobalConfig().OOMUseTmpStorage) - importConfigOption(s, "oom-use-tmp-storage", variable.TiDBEnableTmpStorageOnOOM, valStr) -} - -func upgradeToVer94(s Session, ver int64) { - if ver >= version94 { - return - } - mustExecute(s, CreateMDLView) -} - -func upgradeToVer95(s Session, ver int64) { - if ver >= version95 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `User_attributes` JSON") -} - -func upgradeToVer97(s Session, ver int64) { - if ver >= version97 { - return - } - // Check if tidb_opt_range_max_size exists in mysql.GLOBAL_VARIABLES. - // If not, insert "tidb_opt_range_max_size | 0" since this is the old behavior before we introduce this variable. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptRangeMaxSize) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptRangeMaxSize, 0) -} - -func upgradeToVer98(s Session, ver int64) { - if ver >= version98 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Token_issuer` varchar(255)") -} - -func upgradeToVer99Before(s Session) { - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableMDL, 0) -} - -func upgradeToVer99After(s Session) { - sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %[1]s.%[2]s SET VARIABLE_VALUE = %[4]d WHERE VARIABLE_NAME = '%[3]s'", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableMDL, 1) - mustExecute(s, sql) - err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), s.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - return t.SetMetadataLock(true) - }) - terror.MustNil(err) -} - -func upgradeToVer100(s Session, ver int64) { - if ver >= version100 { - return - } - valStr := strconv.Itoa(int(config.GetGlobalConfig().Performance.ServerMemoryQuota)) - importConfigOption(s, "performance.server-memory-quota", variable.TiDBServerMemoryLimit, valStr) -} - -func upgradeToVer101(s Session, ver int64) { - if ver >= version101 { - return - } - doReentrantDDL(s, CreatePlanReplayerStatusTable) -} - -func upgradeToVer102(s Session, ver int64) { - if ver >= version102 { - return - } - doReentrantDDL(s, CreatePlanReplayerTaskTable) -} - -func upgradeToVer103(s Session, ver int64) { - if ver >= version103 { - return - } - doReentrantDDL(s, CreateStatsTableLocked) -} - -func upgradeToVer104(s Session, ver int64) { - if ver >= version104 { - return - } - - doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN IF NOT EXISTS `sql_digest` varchar(64)") - doReentrantDDL(s, "ALTER TABLE mysql.bind_info ADD COLUMN IF NOT EXISTS `plan_digest` varchar(64)") -} - -// For users that upgrade TiDB from a pre-6.0 version, we want to disable tidb cost model2 by default to keep plans unchanged. -func upgradeToVer105(s Session, ver int64) { - if ver >= version105 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBCostModelVersion) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBCostModelVersion, "1") -} - -func upgradeToVer106(s Session, ver int64) { - if ver >= version106 { - return - } - doReentrantDDL(s, CreatePasswordHistory) - doReentrantDDL(s, "Alter table mysql.user add COLUMN IF NOT EXISTS `Password_reuse_history` smallint unsigned DEFAULT NULL AFTER `Create_Tablespace_Priv` ") - doReentrantDDL(s, "Alter table mysql.user add COLUMN IF NOT EXISTS `Password_reuse_time` smallint unsigned DEFAULT NULL AFTER `Password_reuse_history`") -} - -func upgradeToVer107(s Session, ver int64) { - if ver >= version107 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_expired` ENUM('N','Y') NOT NULL DEFAULT 'N'") - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_last_changed` TIMESTAMP DEFAULT CURRENT_TIMESTAMP()") - doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN IF NOT EXISTS `Password_lifetime` SMALLINT UNSIGNED DEFAULT NULL") -} - -func upgradeToVer108(s Session, ver int64) { - if ver >= version108 { - return - } - doReentrantDDL(s, CreateTTLTableStatus) -} - -// For users that upgrade TiDB from a 6.2-6.4 version, we want to disable tidb gc_aware_memory_track by default. -func upgradeToVer109(s Session, ver int64) { - if ver >= version109 { - return - } - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableGCAwareMemoryTrack, 0) -} - -// For users that upgrade TiDB from a 5.4-6.4 version, we want to enable tidb tidb_stats_load_pseudo_timeout by default. -func upgradeToVer110(s Session, ver int64) { - if ver >= version110 { - return - } - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBStatsLoadPseudoTimeout, 1) -} - -func upgradeToVer130(s Session, ver int64) { - if ver >= version130 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_meta_history ADD COLUMN IF NOT EXISTS `source` varchar(40) NOT NULL after `version`;") -} - -func upgradeToVer131(s Session, ver int64) { - if ver >= version131 { - return - } - doReentrantDDL(s, CreateTTLTask) - doReentrantDDL(s, CreateTTLJobHistory) -} - -func upgradeToVer132(s Session, ver int64) { - if ver >= version132 { - return - } - doReentrantDDL(s, CreateMDLView) -} - -func upgradeToVer133(s Session, ver int64) { - if ver >= version133 { - return - } - mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n set VARIABLE_VALUE = %? where VARIABLE_NAME = %? and VARIABLE_VALUE = %?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.DefTiDBServerMemoryLimit, variable.TiDBServerMemoryLimit, "0") -} - -func upgradeToVer134(s Session, ver int64) { - if ver >= version134 { - return - } - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.ForeignKeyChecks, variable.On) - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableForeignKey, variable.On) - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableHistoricalStats, variable.On) - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnablePlanReplayerCapture, variable.On) - mustExecute(s, "UPDATE HIGH_PRIORITY %n.%n SET VARIABLE_VALUE = %? WHERE VARIABLE_NAME = %? AND VARIABLE_VALUE = %?;", mysql.SystemDB, mysql.GlobalVariablesTable, "4", variable.TiDBStoreBatchSize, "0") -} - -// For users that upgrade TiDB from a pre-7.0 version, we want to set tidb_opt_advanced_join_hint to off by default to keep plans unchanged. -func upgradeToVer135(s Session, ver int64) { - if ver >= version135 { - return - } - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptAdvancedJoinHint) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptAdvancedJoinHint, false) -} - -func upgradeToVer136(s Session, ver int64) { - if ver >= version136 { - return - } - mustExecute(s, CreateGlobalTask) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask DROP INDEX namespace", dbterror.ErrCantDropFieldOrKey) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD INDEX idx_task_key(task_key)", dbterror.ErrDupKeyName) -} - -func upgradeToVer137(_ Session, _ int64) { - // NOOP, we don't depend on ddl to init the default group due to backward compatible issue. -} - -// For users that upgrade TiDB from a version below 7.0, we want to enable tidb tidb_enable_null_aware_anti_join by default. -func upgradeToVer138(s Session, ver int64) { - if ver >= version138 { - return - } - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBOptimizerEnableNAAJ, variable.On) -} - -func upgradeToVer139(s Session, ver int64) { - if ver >= version139 { - return - } - mustExecute(s, CreateLoadDataJobs) -} - -func upgradeToVer140(s Session, ver int64) { - if ver >= version140 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD COLUMN `task_key` VARCHAR(256) NOT NULL AFTER `id`", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD UNIQUE KEY task_key(task_key)", dbterror.ErrDupKeyName) -} - -// upgradeToVer141 sets the value of `tidb_session_plan_cache_size` as `tidb_prepared_plan_cache_size` for compatibility, -// and update tidb_load_based_replica_read_threshold from 0 to 4. -func upgradeToVer141(s Session, ver int64) { - if ver >= version141 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPrepPlanCacheSize) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - if err != nil || req.NumRows() == 0 { - return - } - row := req.GetRow(0) - if row.IsNull(0) { - return - } - val := row.GetString(0) - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBSessionPlanCacheSize, val) - mustExecute(s, "REPLACE HIGH_PRIORITY INTO %n.%n VALUES (%?, %?);", mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBLoadBasedReplicaReadThreshold, variable.DefTiDBLoadBasedReplicaReadThreshold.String()) -} - -func upgradeToVer142(s Session, ver int64) { - if ver >= version142 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableNonPreparedPlanCache) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBEnableNonPreparedPlanCache, variable.Off) -} - -func upgradeToVer143(s Session, ver int64) { - if ver >= version143 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tidb_global_task ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) -} - -func upgradeToVer144(s Session, ver int64) { - if ver >= version144 { - return - } - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?;", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPlanCacheInvalidationOnFreshStats) - terror.MustNil(err) - req := rs.NewChunk(nil) - err = rs.Next(ctx, req) - terror.MustNil(err) - if req.NumRows() != 0 { - return - } - - mustExecute(s, "INSERT HIGH_PRIORITY IGNORE INTO %n.%n VALUES (%?, %?);", - mysql.SystemDB, mysql.GlobalVariablesTable, variable.TiDBPlanCacheInvalidationOnFreshStats, variable.Off) -} - -func upgradeToVer146(s Session, ver int64) { - if ver >= version146 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.stats_meta_history ADD INDEX idx_create_time (create_time)", dbterror.ErrDupKeyName) - doReentrantDDL(s, "ALTER TABLE mysql.stats_history ADD INDEX idx_create_time (create_time)", dbterror.ErrDupKeyName) -} - -func upgradeToVer167(s Session, ver int64) { - if ver >= version167 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `step` INT AFTER `id`", infoschema.ErrColumnExists) -} - -func upgradeToVer168(s Session, ver int64) { - if ver >= version168 { - return - } - mustExecute(s, CreateImportJobs) -} - -func upgradeToVer169(s Session, ver int64) { - if ver >= version169 { - return - } - mustExecute(s, CreateRunawayTable) -} - -func upgradeToVer170(s Session, ver int64) { - if ver >= version170 { - return - } - mustExecute(s, CreateTimers) -} - -func upgradeToVer171(s Session, ver int64) { - if ver >= version171 { - return - } - mustExecute(s, "ALTER TABLE mysql.tidb_runaway_queries CHANGE COLUMN `tidb_server` `tidb_server` varchar(512)") -} - -func upgradeToVer172(s Session, ver int64) { - if ver >= version172 { - return - } - mustExecute(s, "DROP TABLE IF EXISTS mysql.tidb_runaway_quarantined_watch") - mustExecute(s, CreateRunawayWatchTable) - mustExecute(s, CreateDoneRunawayWatchTable) -} - -func upgradeToVer173(s Session, ver int64) { - if ver >= version173 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask ADD COLUMN `summary` JSON", infoschema.ErrColumnExists) -} - -func upgradeToVer174(s Session, ver int64) { - if ver >= version174 { - return - } - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD COLUMN `step` INT AFTER `id`", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD COLUMN `error` BLOB", infoschema.ErrColumnExists) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history DROP INDEX `namespace`", dbterror.ErrCantDropFieldOrKey) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD INDEX `idx_task_key`(`task_key`)", dbterror.ErrDupKeyName) - doReentrantDDL(s, "ALTER TABLE mysql.tidb_background_subtask_history ADD INDEX `idx_state_update_time`(`state_update_time`)", dbterror.ErrDupKeyName) -} - -// upgradeToVer175 updates normalized bindings of `in (?)` to `in (...)` to solve -// the issue #44298 that bindings for `in (?)` can't work for `in (?, ?, ?)`. -// After this update, multiple bindings may have the same `original_sql`, but it's OK, and -// for safety, don't remove duplicated bindings when upgrading. -func upgradeToVer175(s Session, ver int64) { - if ver >= version175 { - return - } - - var err error - mustExecute(s, "BEGIN PESSIMISTIC") - defer func() { - if err != nil { - mustExecute(s, "ROLLBACK") - return - } - mustExecute(s, "COMMIT") - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - rs, err := s.ExecuteInternal(ctx, "SELECT original_sql, bind_sql FROM mysql.bind_info WHERE source != 'builtin'") - if err != nil { - logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) - return - } - req := rs.NewChunk(nil) - for { - err = rs.Next(ctx, req) - if err != nil { - logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) - return - } - if req.NumRows() == 0 { - break - } - for i := 0; i < req.NumRows(); i++ { - originalNormalizedSQL, bindSQL := req.GetRow(i).GetString(0), req.GetRow(i).GetString(1) - newNormalizedSQL := parser.NormalizeForBinding(bindSQL) - // update `in (?)` to `in (...)` - if originalNormalizedSQL == newNormalizedSQL { - continue // no need to update - } - mustExecute(s, fmt.Sprintf("UPDATE mysql.bind_info SET original_sql='%s' WHERE original_sql='%s'", newNormalizedSQL, originalNormalizedSQL)) - } - req.Reset() - } - if err := rs.Close(); err != nil { - logutil.BgLogger().Fatal("upgradeToVer175 error", zap.Error(err)) - } -} - -func upgradeToVer176(s Session, ver int64) { - if ver >= version176 { - return - } - mustExecute(s, CreateGlobalTaskHistory) -} - -func writeOOMAction(s Session) { - comment := "oom-action is `log` by default in v3.0.x, `cancel` by default in v4.0.11+" - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, %?) ON DUPLICATE KEY UPDATE VARIABLE_VALUE= %?`, - mysql.SystemDB, mysql.TiDBTable, tidbDefOOMAction, variable.OOMActionLog, comment, variable.OOMActionLog, - ) -} - -// updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. -func updateBootstrapVer(s Session) { - // Update bootstrap version. - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES (%?, %?, "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, - mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion, currentBootstrapVersion, - ) -} - -// getBootstrapVersion gets bootstrap version from mysql.tidb table; -func getBootstrapVersion(s Session) (int64, error) { - sVal, isNull, err := getTiDBVar(s, tidbServerVersionVar) - if err != nil { - return 0, errors.Trace(err) - } - if isNull { - return 0, nil - } - return strconv.ParseInt(sVal, 10, 64) -} - -// doDDLWorks executes DDL statements in bootstrap stage. -func doDDLWorks(s Session) { - // Create a test database. - mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") - // Create system db. - mustExecute(s, "CREATE DATABASE IF NOT EXISTS %n", mysql.SystemDB) - // Create user table. - mustExecute(s, CreateUserTable) - // Create password history. - mustExecute(s, CreatePasswordHistory) - // Create privilege tables. - mustExecute(s, CreateGlobalPrivTable) - mustExecute(s, CreateDBPrivTable) - mustExecute(s, CreateTablePrivTable) - mustExecute(s, CreateColumnPrivTable) - // Create global system variable table. - mustExecute(s, CreateGlobalVariablesTable) - // Create TiDB table. - mustExecute(s, CreateTiDBTable) - // Create help table. - mustExecute(s, CreateHelpTopic) - // Create stats_meta table. - mustExecute(s, CreateStatsMetaTable) - // Create stats_columns table. - mustExecute(s, CreateStatsColsTable) - // Create stats_buckets table. - mustExecute(s, CreateStatsBucketsTable) - // Create gc_delete_range table. - mustExecute(s, CreateGCDeleteRangeTable) - // Create gc_delete_range_done table. - mustExecute(s, CreateGCDeleteRangeDoneTable) - // Create stats_feedback table. - // NOTE: Feedback is deprecated, but we still need to create this table for compatibility. - mustExecute(s, CreateStatsFeedbackTable) - // Create role_edges table. - mustExecute(s, CreateRoleEdgesTable) - // Create default_roles table. - mustExecute(s, CreateDefaultRolesTable) - // Create bind_info table. - initBindInfoTable(s) - // Create stats_topn_store table. - mustExecute(s, CreateStatsTopNTable) - // Create expr_pushdown_blacklist table. - mustExecute(s, CreateExprPushdownBlacklist) - // Create opt_rule_blacklist table. - mustExecute(s, CreateOptRuleBlacklist) - // Create stats_extended table. - mustExecute(s, CreateStatsExtended) - // Create schema_index_usage. - mustExecute(s, CreateSchemaIndexUsageTable) - // Create stats_fm_sketch table. - mustExecute(s, CreateStatsFMSketchTable) - // Create global_grants - mustExecute(s, CreateGlobalGrantsTable) - // Create capture_plan_baselines_blacklist - mustExecute(s, CreateCapturePlanBaselinesBlacklist) - // Create column_stats_usage table - mustExecute(s, CreateColumnStatsUsageTable) - // Create table_cache_meta table. - mustExecute(s, CreateTableCacheMetaTable) - // Create analyze_options table. - mustExecute(s, CreateAnalyzeOptionsTable) - // Create stats_history table. - mustExecute(s, CreateStatsHistory) - // Create stats_meta_history table. - mustExecute(s, CreateStatsMetaHistory) - // Create analyze_jobs table. - mustExecute(s, CreateAnalyzeJobs) - // Create advisory_locks table. - mustExecute(s, CreateAdvisoryLocks) - // Create mdl view. - mustExecute(s, CreateMDLView) - // Create plan_replayer_status table - mustExecute(s, CreatePlanReplayerStatusTable) - // Create plan_replayer_task table - mustExecute(s, CreatePlanReplayerTaskTable) - // Create stats_meta_table_locked table - mustExecute(s, CreateStatsTableLocked) - // Create tidb_ttl_table_status table - mustExecute(s, CreateTTLTableStatus) - // Create tidb_ttl_task table - mustExecute(s, CreateTTLTask) - // Create tidb_ttl_job_history table - mustExecute(s, CreateTTLJobHistory) - // Create tidb_global_task table - mustExecute(s, CreateGlobalTask) - // Create tidb_global_task_history table - mustExecute(s, CreateGlobalTaskHistory) - // Create load_data_jobs - mustExecute(s, CreateLoadDataJobs) - // Create tidb_import_jobs - mustExecute(s, CreateImportJobs) - // create runaway_watch - mustExecute(s, CreateRunawayWatchTable) - // create runaway_queries - mustExecute(s, CreateRunawayTable) - // create tidb_timers - mustExecute(s, CreateTimers) - // create runaway_watch done - mustExecute(s, CreateDoneRunawayWatchTable) - // create dist_framework_meta - mustExecute(s, CreateDistFrameworkMeta) -} - -// doBootstrapSQLFile executes SQL commands in a file as the last stage of bootstrap. -// It is useful for setting the initial value of GLOBAL variables. -func doBootstrapSQLFile(s Session) error { - sqlFile := config.GetGlobalConfig().InitializeSQLFile - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - if sqlFile == "" { - return nil - } - logutil.BgLogger().Info("executing -initialize-sql-file", zap.String("file", sqlFile)) - b, err := os.ReadFile(sqlFile) //nolint:gosec - if err != nil { - if intest.InTest { - return err - } - logutil.BgLogger().Fatal("unable to read InitializeSQLFile", zap.Error(err)) - } - stmts, err := s.Parse(ctx, string(b)) - if err != nil { - if intest.InTest { - return err - } - logutil.BgLogger().Fatal("unable to parse InitializeSQLFile", zap.Error(err)) - } - for _, stmt := range stmts { - rs, err := s.ExecuteStmt(ctx, stmt) - if err != nil { - logutil.BgLogger().Warn("InitializeSQLFile error", zap.Error(err)) - } - if rs != nil { - // I don't believe we need to drain the result-set in bootstrap mode - // but if required we can do this here in future. - if err := rs.Close(); err != nil { - logutil.BgLogger().Fatal("unable to close result", zap.Error(err)) - } - } - } - return nil -} - -// doDMLWorks executes DML statements in bootstrap stage. -// All the statements run in a single transaction. -func doDMLWorks(s Session) { - mustExecute(s, "BEGIN") - if config.GetGlobalConfig().Security.SecureBootstrap { - // If secure bootstrap is enabled, we create a root@localhost account which can login with auth_socket. - // i.e. mysql -S /tmp/tidb.sock -uroot - // The auth_socket plugin will validate that the user matches $USER. - u, err := osuser.Current() - if err != nil { - logutil.BgLogger().Fatal("failed to read current user. unable to secure bootstrap.", zap.Error(err)) - } - mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user (Host,User,authentication_string,plugin,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Process_priv,Grant_priv,References_priv,Alter_priv,Show_db_priv, - Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Index_priv,Create_user_priv,Event_priv,Repl_slave_priv,Repl_client_priv,Trigger_priv,Create_role_priv,Drop_role_priv,Account_locked, - Shutdown_priv,Reload_priv,FILE_priv,Config_priv,Create_Tablespace_Priv,User_attributes,Token_issuer) VALUES - ("localhost", "root", %?, "auth_socket", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", null, "")`, u.Username) - } else { - mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user (Host,User,authentication_string,plugin,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Process_priv,Grant_priv,References_priv,Alter_priv,Show_db_priv, - Super_priv,Create_tmp_table_priv,Lock_tables_priv,Execute_priv,Create_view_priv,Show_view_priv,Create_routine_priv,Alter_routine_priv,Index_priv,Create_user_priv,Event_priv,Repl_slave_priv,Repl_client_priv,Trigger_priv,Create_role_priv,Drop_role_priv,Account_locked, - Shutdown_priv,Reload_priv,FILE_priv,Config_priv,Create_Tablespace_Priv,User_attributes,Token_issuer) VALUES - ("%", "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", null, "")`) - } - - // For GLOBAL scoped system variables, insert the initial value - // into the mysql.global_variables table. This is only run on initial - // bootstrap, and in some cases we will use a different default value - // for new installs versus existing installs. - - values := make([]string, 0, len(variable.GetSysVars())) - for k, v := range variable.GetSysVars() { - if !v.HasGlobalScope() { - continue - } - vVal := v.Value - switch v.Name { - case variable.TiDBTxnMode: - if config.GetGlobalConfig().Store == "tikv" || config.GetGlobalConfig().Store == "unistore" { - vVal = "pessimistic" - } - case variable.TiDBEnableAsyncCommit, variable.TiDBEnable1PC: - if config.GetGlobalConfig().Store == "tikv" { - vVal = variable.On - } - case variable.TiDBMemOOMAction: - if intest.InTest { - vVal = variable.OOMActionLog - } - case variable.TiDBEnableAutoAnalyze: - if intest.InTest { - vVal = variable.Off - } - // For the following sysvars, we change the default - // FOR NEW INSTALLS ONLY. In most cases you don't want to do this. - // It is better to change the value in the Sysvar struct, so that - // all installs will have the same value. - case variable.TiDBRowFormatVersion: - vVal = strconv.Itoa(variable.DefTiDBRowFormatV2) - case variable.TiDBTxnAssertionLevel: - vVal = variable.AssertionFastStr - case variable.TiDBEnableMutationChecker: - vVal = variable.On - case variable.TiDBPessimisticTransactionFairLocking: - vVal = variable.On - } - - // sanitize k and vVal - value := fmt.Sprintf(`("%s", "%s")`, sqlexec.EscapeString(k), sqlexec.EscapeString(vVal)) - values = append(values, value) - } - sql := fmt.Sprintf("INSERT HIGH_PRIORITY INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, - strings.Join(values, ", ")) - mustExecute(s, sql) - - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES(%?, %?, "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE=%?`, - mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, varTrue, varTrue, - ) - - mustExecute(s, `INSERT HIGH_PRIORITY INTO %n.%n VALUES(%?, %?, "Bootstrap version. Do not delete.")`, - mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion, - ) - writeSystemTZ(s) - - writeNewCollationParameter(s, config.GetGlobalConfig().NewCollationsEnabledOnFirstBootstrap) - - writeStmtSummaryVars(s) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - _, err := s.ExecuteInternal(ctx, "COMMIT") - if err != nil { - sleepTime := 1 * time.Second - logutil.BgLogger().Info("doDMLWorks failed", zap.Error(err), zap.Duration("sleeping time", sleepTime)) - time.Sleep(sleepTime) - // Check if TiDB is already bootstrapped. - b, err1 := checkBootstrapped(s) - if err1 != nil { - logutil.BgLogger().Fatal("doDMLWorks failed", zap.Error(err1)) - } - if b { - return - } - logutil.BgLogger().Fatal("doDMLWorks failed", zap.Error(err)) - } -} - -func mustExecute(s Session, sql string, args ...interface{}) { - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(internalSQLTimeout)*time.Second) - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnBootstrap) - _, err := s.ExecuteInternal(ctx, sql, args...) - defer cancel() - if err != nil { - logutil.BgLogger().Fatal("mustExecute error", zap.Error(err), zap.Stack("stack")) - } -} - -// oldPasswordUpgrade upgrade password to MySQL compatible format -func oldPasswordUpgrade(pass string) (string, error) { - hash1, err := hex.DecodeString(pass) - if err != nil { - return "", errors.Trace(err) - } - - hash2 := auth.Sha1Hash(hash1) - newpass := fmt.Sprintf("*%X", hash2) - return newpass, nil -} - -// rebuildAllPartitionValueMapAndSorted rebuilds all value map and sorted info for list column partitions with InfoSchema. -func rebuildAllPartitionValueMapAndSorted(s *session) { - type partitionExpr interface { - PartitionExpr() *tables.PartitionExpr - } - - p := parser.New() - is := s.GetInfoSchema().(infoschema.InfoSchema) - for _, dbInfo := range is.AllSchemas() { - for _, t := range is.SchemaTables(dbInfo.Name) { - pi := t.Meta().GetPartitionInfo() - if pi == nil || pi.Type != model.PartitionTypeList { - continue - } - - pe := t.(partitionExpr).PartitionExpr() - for _, cp := range pe.ColPrunes { - if err := cp.RebuildPartitionValueMapAndSorted(p, pi.Definitions); err != nil { - logutil.BgLogger().Warn("build list column partition value map and sorted failed") - break - } - } - } - } -} diff --git a/session/bootstrap_test.go b/session/bootstrap_test.go deleted file mode 100644 index a368f4273d626..0000000000000 --- a/session/bootstrap_test.go +++ /dev/null @@ -1,2089 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "fmt" - "sort" - "testing" - "time" - - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/telemetry" - "github.com/stretchr/testify/require" -) - -// This test file have many problem. -// 1. Please use testkit to create dom, session and store. -// 2. Don't use CreateStoreAndBootstrap and BootstrapSession together. It will cause data race. -// Please do not add any test here. You can add test case at the bootstrap_update_test.go. After All problem fixed, -// We will overwrite this file by update_test.go. -func TestBootstrap(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - MustExec(t, se, "set global tidb_txn_mode=''") - MustExec(t, se, "use mysql") - r := MustExecToRecodeSet(t, se, "select * from user") - require.NotNil(t, r) - - ctx := context.Background() - req := r.NewChunk(nil) - err := r.Next(ctx, req) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - - rows := statistics.RowToDatums(req.GetRow(0), r.Fields()) - match(t, rows, `%`, "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", nil, nil, nil, "", "N", time.Now(), nil) - r.Close() - - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "anyhost"}, []byte(""), []byte(""), nil)) - - MustExec(t, se, "use test") - - // Check privilege tables. - MustExec(t, se, "SELECT * from mysql.global_priv") - MustExec(t, se, "SELECT * from mysql.db") - MustExec(t, se, "SELECT * from mysql.tables_priv") - MustExec(t, se, "SELECT * from mysql.columns_priv") - MustExec(t, se, "SELECT * from mysql.global_grants") - - // Check privilege tables. - r = MustExecToRecodeSet(t, se, "SELECT COUNT(*) from mysql.global_variables") - require.NotNil(t, r) - - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, globalVarsCount(), req.GetRow(0).GetInt64(0)) - require.NoError(t, r.Close()) - - // Check a storage operations are default autocommit after the second start. - MustExec(t, se, "USE test") - MustExec(t, se, "drop table if exists t") - MustExec(t, se, "create table t (id int)") - unsetStoreBootstrapped(store.UUID()) - se.Close() - - se, err = CreateSession4Test(store) - require.NoError(t, err) - MustExec(t, se, "USE test") - MustExec(t, se, "insert t values (?)", 3) - - se, err = CreateSession4Test(store) - require.NoError(t, err) - MustExec(t, se, "USE test") - r = MustExecToRecodeSet(t, se, "select * from t") - require.NotNil(t, r) - - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - rows = statistics.RowToDatums(req.GetRow(0), r.Fields()) - match(t, rows, 3) - MustExec(t, se, "drop table if exists t") - se.Close() - - // Try to do bootstrap dml jobs on an already bootstrapped TiDB system will not cause fatal. - // For https://github.com/pingcap/tidb/issues/1096 - se, err = CreateSession4Test(store) - require.NoError(t, err) - doDMLWorks(se) - r = MustExecToRecodeSet(t, se, "select * from mysql.expr_pushdown_blacklist where name = 'date_add'") - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) - se.Close() -} - -func globalVarsCount() int64 { - var count int64 - for _, v := range variable.GetSysVars() { - if v.HasGlobalScope() { - count++ - } - } - return count -} - -// testBootstrapWithError : -// When a session failed in bootstrap process (for example, the session is killed after doDDLWorks()). -// We should make sure that the following session could finish the bootstrap process. -func TestBootstrapWithError(t *testing.T) { - ctx := context.Background() - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - // bootstrap - { - se := &session{ - store: store, - sessionVars: variable.NewSessionVars(nil), - } - globalVarsAccessor := variable.NewMockGlobalAccessor4Tests() - se.GetSessionVars().GlobalVarsAccessor = globalVarsAccessor - se.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) - se.txn.init() - se.mu.values = make(map[fmt.Stringer]interface{}) - se.SetValue(sessionctx.Initing, true) - err := InitDDLJobTables(store, meta.BaseDDLTableVersion) - require.NoError(t, err) - err = InitMDLTable(store) - require.NoError(t, err) - err = InitDDLJobTables(store, meta.BackfillTableVersion) - require.NoError(t, err) - dom, err := domap.Get(store) - require.NoError(t, err) - domain.BindDomain(se, dom) - b, err := checkBootstrapped(se) - require.False(t, b) - require.NoError(t, err) - doDDLWorks(se) - } - - dom, err := domap.Get(store) - require.NoError(t, err) - dom.Close() - - dom1, err := BootstrapSession(store) - require.NoError(t, err) - defer dom1.Close() - - se := CreateSessionAndSetID(t, store) - MustExec(t, se, "USE mysql") - r := MustExecToRecodeSet(t, se, `select * from user`) - req := r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - - row := req.GetRow(0) - rows := statistics.RowToDatums(row, r.Fields()) - match(t, rows, `%`, "root", "", "mysql_native_password", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "N", "Y", "Y", "Y", "Y", "Y", nil, nil, nil, "", "N", time.Now(), nil) - require.NoError(t, r.Close()) - - MustExec(t, se, "USE test") - // Check privilege tables. - MustExec(t, se, "SELECT * from mysql.global_priv") - MustExec(t, se, "SELECT * from mysql.db") - MustExec(t, se, "SELECT * from mysql.tables_priv") - MustExec(t, se, "SELECT * from mysql.columns_priv") - // Check role tables. - MustExec(t, se, "SELECT * from mysql.role_edges") - MustExec(t, se, "SELECT * from mysql.default_roles") - // Check global variables. - r = MustExecToRecodeSet(t, se, "SELECT COUNT(*) from mysql.global_variables") - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - v := req.GetRow(0) - require.Equal(t, globalVarsCount(), v.GetInt64(0)) - require.NoError(t, r.Close()) - - r = MustExecToRecodeSet(t, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="bootstrapped"`) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - row = req.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, []byte("True"), row.GetBytes(0)) - require.NoError(t, r.Close()) - - MustExec(t, se, "SELECT * from mysql.tidb_background_subtask") - MustExec(t, se, "SELECT * from mysql.tidb_background_subtask_history") - - // Check tidb_ttl_table_status table - MustExec(t, se, "SELECT * from mysql.tidb_ttl_table_status") -} - -func TestDDLTableCreateBackfillTable(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - se := CreateSessionAndSetID(t, store) - - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - ver, err := m.CheckDDLTableVersion() - require.NoError(t, err) - require.GreaterOrEqual(t, ver, meta.BackfillTableVersion) - - // downgrade `mDDLTableVersion` - m.SetDDLTables(meta.MDLTableVersion) - MustExec(t, se, "drop table mysql.tidb_background_subtask") - MustExec(t, se, "drop table mysql.tidb_background_subtask_history") - err = txn.Commit(context.Background()) - require.NoError(t, err) - - // to upgrade session for create ddl related tables - dom.Close() - dom, err = BootstrapSession(store) - require.NoError(t, err) - - se = CreateSessionAndSetID(t, store) - MustExec(t, se, "select * from mysql.tidb_background_subtask") - MustExec(t, se, "select * from mysql.tidb_background_subtask_history") - dom.Close() -} - -// TestUpgrade tests upgrading -func TestUpgrade(t *testing.T) { - ctx := context.Background() - - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - se := CreateSessionAndSetID(t, store) - - MustExec(t, se, "USE mysql") - - // bootstrap with currentBootstrapVersion - r := MustExecToRecodeSet(t, se, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) - req := r.NewChunk(nil) - err := r.Next(ctx, req) - row := req.GetRow(0) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - require.Equal(t, 1, row.Len()) - require.Equal(t, []byte(fmt.Sprintf("%d", currentBootstrapVersion)), row.GetBytes(0)) - require.NoError(t, r.Close()) - - se1 := CreateSessionAndSetID(t, store) - ver, err := getBootstrapVersion(se1) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // Do something to downgrade the store. - // downgrade meta bootstrap version - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(1)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, se1, `delete from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) - MustExec(t, se1, fmt.Sprintf(`delete from mysql.global_variables where VARIABLE_NAME="%s"`, variable.TiDBDistSQLScanConcurrency)) - MustExec(t, se1, `commit`) - unsetStoreBootstrapped(store.UUID()) - // Make sure the version is downgraded. - r = MustExecToRecodeSet(t, se1, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) - require.NoError(t, r.Close()) - - ver, err = getBootstrapVersion(se1) - require.NoError(t, err) - require.Equal(t, int64(0), ver) - dom.Close() - // Create a new session then upgrade() will run automatically. - dom, err = BootstrapSession(store) - require.NoError(t, err) - - se2 := CreateSessionAndSetID(t, store) - r = MustExecToRecodeSet(t, se2, `SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - row = req.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, []byte(fmt.Sprintf("%d", currentBootstrapVersion)), row.GetBytes(0)) - require.NoError(t, r.Close()) - - ver, err = getBootstrapVersion(se2) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // Verify that 'new_collation_enabled' is false. - r = MustExecToRecodeSet(t, se2, fmt.Sprintf(`SELECT VARIABLE_VALUE from mysql.TiDB where VARIABLE_NAME='%s'`, tidbNewCollationEnabled)) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - require.Equal(t, "False", req.GetRow(0).GetString(0)) - require.NoError(t, r.Close()) - dom.Close() -} - -func TestIssue17979_1(t *testing.T) { - ctx := context.Background() - - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - // test issue 20900, upgrade from v3.0 to v4.0.11+ - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(58)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, "update mysql.tidb set variable_value='58' where variable_name='tidb_server_version'") - MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_oom_action'") - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(58), ver) - dom.Close() - domV4, err := BootstrapSession(store) - require.NoError(t, err) - seV4 := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seV4) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - r := MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_oom_action'") - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, variable.OOMActionLog, req.GetRow(0).GetString(0)) - domV4.Close() -} - -func TestIssue17979_2(t *testing.T) { - ctx := context.Background() - - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // test issue 20900, upgrade from v4.0.11 to v4.0.11 - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(59)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, "update mysql.tidb set variable_value=59 where variable_name='tidb_server_version'") - MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_iim_action'") - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(59), ver) - dom.Close() - domV4, err := BootstrapSession(store) - require.NoError(t, err) - defer domV4.Close() - seV4 := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seV4) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - r := MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_oom_action'") - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, 0, req.NumRows()) -} - -// TestIssue20900_2 tests that a user can upgrade from TiDB 2.1 to latest, -// and their configuration remains similar. This helps protect against the -// case that a user had a 32G query memory limit in 2.1, but it is now a 1G limit -// in TiDB 4.0+. I tested this process, and it does correctly upgrade from 2.1 -> 4.0, -// but from 4.0 -> 5.0, the new default is picked up. - -func TestIssue20900_2(t *testing.T) { - ctx := context.Background() - - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // test issue 20900, upgrade from v4.0.8 to v4.0.9+ - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(52)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, "update mysql.tidb set variable_value=52 where variable_name='tidb_server_version'") - MustExec(t, seV3, "delete from mysql.tidb where variable_name='default_memory_quota_query'") - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(52), ver) - dom.Close() - domV4, err := BootstrapSession(store) - require.NoError(t, err) - seV4 := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seV4) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - r := MustExecToRecodeSet(t, seV4, "select @@tidb_mem_quota_query") - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, "1073741824", req.GetRow(0).GetString(0)) - require.Equal(t, int64(1073741824), seV4.GetSessionVars().MemQuotaQuery) - r = MustExecToRecodeSet(t, seV4, "select variable_value from mysql.tidb where variable_name='default_memory_quota_query'") - req = r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, 0, req.NumRows()) - domV4.Close() -} - -func TestANSISQLMode(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - se := CreateSessionAndSetID(t, store) - - MustExec(t, se, "USE mysql") - MustExec(t, se, `set @@global.sql_mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,ANSI"`) - MustExec(t, se, `delete from mysql.TiDB where VARIABLE_NAME="tidb_server_version"`) - unsetStoreBootstrapped(store.UUID()) - se.Close() - - // Do some clean up, BootstrapSession will not create a new domain otherwise. - dom.Close() - domap.Delete(store) - - // Set ANSI sql_mode and bootstrap again, to cover a bugfix. - // Once we have a SQL like that: - // select variable_value from mysql.tidb where variable_name = "system_tz" - // it fails to execute in the ANSI sql_mode, and makes TiDB cluster fail to bootstrap. - dom1, err := BootstrapSession(store) - require.NoError(t, err) - defer dom1.Close() - se = CreateSessionAndSetID(t, store) - MustExec(t, se, "select @@global.sql_mode") - se.Close() -} - -func TestOldPasswordUpgrade(t *testing.T) { - pwd := "abc" - oldpwd := fmt.Sprintf("%X", auth.Sha1Hash([]byte(pwd))) - newpwd, err := oldPasswordUpgrade(oldpwd) - require.NoError(t, err) - require.Equal(t, "*0D3CED9BEC10A777AEC23CCC353A8C08A633045E", newpwd) -} - -func TestBootstrapInitExpensiveQueryHandle(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - se, err := createSession(store) - require.NoError(t, err) - dom := domain.GetDomain(se) - require.NotNil(t, dom) - defer dom.Close() - require.NotNil(t, dom.ExpensiveQueryHandle()) -} - -func TestStmtSummary(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - r := MustExecToRecodeSet(t, se, "select variable_value from mysql.global_variables where variable_name='tidb_enable_stmt_summary'") - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - row := req.GetRow(0) - require.Equal(t, []byte("ON"), row.GetBytes(0)) - require.NoError(t, r.Close()) -} - -type bindTestStruct struct { - originText string - bindText string - db string - originWithDB string - bindWithDB string - deleteText string -} - -func TestUpdateBindInfo(t *testing.T) { - bindCases := []bindTestStruct{ - { - originText: "select * from t where a > ?", - bindText: "select /*+ use_index(t, idxb) */ * from t where a > 1", - db: "test", - originWithDB: "select * from `test` . `t` where `a` > ?", - bindWithDB: "SELECT /*+ use_index(`t` `idxb`)*/ * FROM `test`.`t` WHERE `a` > 1", - deleteText: "select * from test.t where a > 1", - }, - { - originText: "select count ( ? ), max ( a ) from t group by b", - bindText: "select /*+ use_index(t, idx) */ count(1), max(a) from t group by b", - db: "test", - originWithDB: "select count ( ? ) , max ( `a` ) from `test` . `t` group by `b`", - bindWithDB: "SELECT /*+ use_index(`t` `idx`)*/ count(1),max(`a`) FROM `test`.`t` GROUP BY `b`", - deleteText: "select count(1), max(a) from test.t group by b", - }, - { - originText: "select * from `test` . `t` where `a` = (_charset) ?", - bindText: "SELECT * FROM test.t WHERE a = _utf8\\'ab\\'", - db: "test", - originWithDB: "select * from `test` . `t` where `a` = ?", - bindWithDB: "SELECT * FROM `test`.`t` WHERE `a` = 'ab'", - deleteText: "select * from test.t where a = 'c'", - }, - } - - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") - MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") - for _, bindCase := range bindCases { - sql := fmt.Sprintf("insert into mysql.bind_info values('%s', '%s', '%s', 'enabled', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')", - bindCase.originText, - bindCase.bindText, - bindCase.db, - ) - MustExec(t, se, sql) - - upgradeToVer67(se, version66) - r := MustExecToRecodeSet(t, se, `select original_sql, bind_sql, default_db, status from mysql.bind_info where source != 'builtin'`) - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - row := req.GetRow(0) - require.Equal(t, bindCase.originWithDB, row.GetString(0)) - require.Equal(t, bindCase.bindWithDB, row.GetString(1)) - require.Equal(t, "", row.GetString(2)) - require.Equal(t, bindinfo.Enabled, row.GetString(3)) - require.NoError(t, r.Close()) - sql = fmt.Sprintf("drop global binding for %s", bindCase.deleteText) - MustExec(t, se, sql) - r = MustExecToRecodeSet(t, se, `select original_sql, bind_sql, status from mysql.bind_info where source != 'builtin'`) - require.NoError(t, r.Next(ctx, req)) - row = req.GetRow(0) - require.Equal(t, bindCase.originWithDB, row.GetString(0)) - require.Equal(t, bindCase.bindWithDB, row.GetString(1)) - require.Equal(t, "deleted", row.GetString(2)) - require.NoError(t, r.Close()) - sql = fmt.Sprintf("delete from mysql.bind_info where original_sql = '%s'", bindCase.originWithDB) - MustExec(t, se, sql) - } -} - -func TestUpdateDuplicateBindInfo(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") - MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") - - MustExec(t, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'enabled', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - // The latest one. - MustExec(t, se, `insert into mysql.bind_info values('select * from test . t', 'select /*+ use_index(t, idx_b)*/ * from test.t', 'test', 'enabled', '2021-01-04 14:50:58.257', '2021-01-09 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - - MustExec(t, se, `insert into mysql.bind_info values('select * from t where a < ?', 'select * from t use index(idx) where a < 1', 'test', 'deleted', '2021-06-04 17:04:43.333', '2021-06-04 17:04:43.335', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from t where a < ?', 'select * from t ignore index(idx) where a < 1', 'test', 'enabled', '2021-06-04 17:04:43.335', '2021-06-04 17:04:43.335', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from test . t where a <= ?', 'select * from test.t use index(idx) where a <= 1', '', 'deleted', '2021-06-04 17:04:43.345', '2021-06-04 17:04:45.334', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from test . t where a <= ?', 'select * from test.t ignore index(idx) where a <= 1', '', 'enabled', '2021-06-04 17:04:45.334', '2021-06-04 17:04:45.334', 'utf8', 'utf8_general_ci', 'manual')`) - - upgradeToVer67(se, version66) - - r := MustExecToRecodeSet(t, se, `select original_sql, bind_sql, default_db, status, create_time from mysql.bind_info where source != 'builtin' order by create_time`) - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, 3, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, "select * from `test` . `t`", row.GetString(0)) - require.Equal(t, "SELECT /*+ use_index(`t` `idx_b`)*/ * FROM `test`.`t`", row.GetString(1)) - require.Equal(t, "", row.GetString(2)) - require.Equal(t, bindinfo.Enabled, row.GetString(3)) - require.Equal(t, "2021-01-04 14:50:58.257", row.GetTime(4).String()) - row = req.GetRow(1) - require.Equal(t, "select * from `test` . `t` where `a` < ?", row.GetString(0)) - require.Equal(t, "SELECT * FROM `test`.`t` IGNORE INDEX (`idx`) WHERE `a` < 1", row.GetString(1)) - require.Equal(t, "", row.GetString(2)) - require.Equal(t, bindinfo.Enabled, row.GetString(3)) - require.Equal(t, "2021-06-04 17:04:43.335", row.GetTime(4).String()) - row = req.GetRow(2) - require.Equal(t, "select * from `test` . `t` where `a` <= ?", row.GetString(0)) - require.Equal(t, "SELECT * FROM `test`.`t` IGNORE INDEX (`idx`) WHERE `a` <= 1", row.GetString(1)) - require.Equal(t, "", row.GetString(2)) - require.Equal(t, bindinfo.Enabled, row.GetString(3)) - require.Equal(t, "2021-06-04 17:04:45.334", row.GetTime(4).String()) - - require.NoError(t, r.Close()) - MustExec(t, se, "delete from mysql.bind_info where original_sql = 'select * from test . t'") -} - -func TestUpgradeClusteredIndexDefaultValue(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - seV67 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(67)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV67, "update mysql.tidb set variable_value='67' where variable_name='tidb_server_version'") - MustExec(t, seV67, "UPDATE mysql.global_variables SET VARIABLE_VALUE = 'OFF' where VARIABLE_NAME = 'tidb_enable_clustered_index'") - require.Equal(t, uint64(1), seV67.GetSessionVars().StmtCtx.AffectedRows()) - MustExec(t, seV67, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV67) - require.NoError(t, err) - require.Equal(t, int64(67), ver) - dom.Close() - - domV68, err := BootstrapSession(store) - require.NoError(t, err) - seV68 := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seV68) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - r := MustExecToRecodeSet(t, seV68, `select @@global.tidb_enable_clustered_index, @@session.tidb_enable_clustered_index`) - req := r.NewChunk(nil) - require.NoError(t, r.Next(context.Background(), req)) - require.Equal(t, 1, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, "ON", row.GetString(0)) - require.Equal(t, "ON", row.GetString(1)) - domV68.Close() -} - -func TestForIssue23387(t *testing.T) { - // For issue https://github.com/pingcap/tidb/issues/23387 - saveCurrentBootstrapVersion := currentBootstrapVersion - currentBootstrapVersion = version57 - - // Bootstrap to an old version, create a user. - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { require.NoError(t, store.Close()) }() - dom, err := BootstrapSession(store) - require.NoError(t, err) - - se := CreateSessionAndSetID(t, store) - se.Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, []byte("012345678901234567890"), nil) - MustExec(t, se, "create user quatest") - dom.Close() - // Upgrade to a newer version, check the user's privilege. - currentBootstrapVersion = saveCurrentBootstrapVersion - dom, err = BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - - se = CreateSessionAndSetID(t, store) - se.Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, nil, []byte("012345678901234567890"), nil) - rs, err := exec(se, "show grants for quatest") - require.NoError(t, err) - rows, err := ResultSetToStringSlice(context.Background(), se, rs) - require.NoError(t, err) - require.Len(t, rows, 1) - require.Equal(t, "GRANT USAGE ON *.* TO 'quatest'@'%'", rows[0][0]) -} - -func TestReferencesPrivilegeOnColumn(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - defer func() { - MustExec(t, se, "drop user if exists issue28531") - MustExec(t, se, "drop table if exists t1") - }() - - MustExec(t, se, "create user if not exists issue28531") - MustExec(t, se, "use test") - MustExec(t, se, "drop table if exists t1") - MustExec(t, se, "create table t1 (a int)") - MustExec(t, se, "GRANT select (a), update (a),insert(a), references(a) on t1 to issue28531") -} - -func TestAnalyzeVersionUpgradeFrom300To500(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // Upgrade from 3.0.0 to 5.1+ or above. - ver300 := 33 - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver300)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) - MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBAnalyzeVersion)) - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(ver300), ver) - - // We are now in 3.0.0, check tidb_analyze_version should not exist. - res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBAnalyzeVersion)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - dom.Close() - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in version no lower than 5.x, tidb_enable_index_merge should be 1. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_analyze_version") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, "1", row.GetString(0)) -} - -func TestIndexMergeInNewCluster(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - // Indicates we are in a new cluster. - require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) - dom, err := BootstrapSession(store) - require.NoError(t, err) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - // In a new created cluster(above 5.4+), tidb_enable_index_merge is 1 by default. - MustExec(t, se, "use test;") - r := MustExecToRecodeSet(t, se, "select @@tidb_enable_index_merge;") - require.NotNil(t, r) - - ctx := context.Background() - chk := r.NewChunk(nil) - err = r.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, int64(1), row.GetInt64(0)) -} - -func TestIndexMergeUpgradeFrom300To540(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // Upgrade from 3.0.0 to 5.4+. - ver300 := 33 - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver300)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) - MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(ver300), ver) - - // We are now in 3.0.0, check tidb_enable_index_merge shoudle not exist. - res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - dom.Close() - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 5.x, tidb_enable_index_merge should be off. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_enable_index_merge") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, int64(0), row.GetInt64(0)) -} - -func TestIndexMergeUpgradeFrom400To540(t *testing.T) { - for i := 0; i < 2; i++ { - func() { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 4.0.0 to 5.4+. - ver400 := 46 - seV4 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver400)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV4, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver400)) - MustExec(t, seV4, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", variable.Off, variable.TiDBEnableIndexMerge)) - MustExec(t, seV4, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV4) - require.NoError(t, err) - require.Equal(t, int64(ver400), ver) - - // We are now in 4.0.0, tidb_enable_index_merge is off. - res := MustExecToRecodeSet(t, seV4, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableIndexMerge)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, variable.Off, row.GetString(1)) - - if i == 0 { - // For the first time, We set tidb_enable_index_merge as on. - // And after upgrade to 5.x, tidb_enable_index_merge should remains to be on. - // For the second it should be off. - MustExec(t, seV4, "set global tidb_enable_index_merge = on") - } - dom.Close() - // Upgrade to 5.x. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 5.x, tidb_enable_index_merge should be on because we enable it in 4.0.0. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_enable_index_merge") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 1, row.Len()) - if i == 0 { - require.Equal(t, int64(1), row.GetInt64(0)) - } else { - require.Equal(t, int64(0), row.GetInt64(0)) - } - }() - } -} - -func TestUpgradeToVer85(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - MustExec(t, se, "alter table mysql.bind_info drop column if exists plan_digest") - MustExec(t, se, "alter table mysql.bind_info drop column if exists sql_digest") - - MustExec(t, se, `insert into mysql.bind_info values('select * from t', 'select /*+ use_index(t, idx_a)*/ * from t', 'test', 'using', '2021-01-04 14:50:58.257', '2021-01-04 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from t1', 'select /*+ use_index(t1, idx_a)*/ * from t1', 'test', 'enabled', '2021-01-05 14:50:58.257', '2021-01-05 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from t2', 'select /*+ use_index(t2, idx_a)*/ * from t2', 'test', 'disabled', '2021-01-06 14:50:58.257', '2021-01-06 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from t3', 'select /*+ use_index(t3, idx_a)*/ * from t3', 'test', 'deleted', '2021-01-07 14:50:58.257', '2021-01-07 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - MustExec(t, se, `insert into mysql.bind_info values('select * from t4', 'select /*+ use_index(t4, idx_a)*/ * from t4', 'test', 'invalid', '2021-01-08 14:50:58.257', '2021-01-08 14:50:58.257', 'utf8', 'utf8_general_ci', 'manual')`) - upgradeToVer85(se, version84) - - r := MustExecToRecodeSet(t, se, `select count(*) from mysql.bind_info where status = 'enabled'`) - req := r.NewChunk(nil) - require.NoError(t, r.Next(ctx, req)) - require.Equal(t, 1, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, int64(2), row.GetInt64(0)) - - require.NoError(t, r.Close()) - MustExec(t, se, "delete from mysql.bind_info where default_db = 'test'") -} - -func TestTiDBEnablePagingVariable(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - se := CreateSessionAndSetID(t, store) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - - for _, sql := range []string{ - "select @@global.tidb_enable_paging", - "select @@session.tidb_enable_paging", - } { - r := MustExecToRecodeSet(t, se, sql) - require.NotNil(t, r) - - req := r.NewChunk(nil) - err := r.Next(context.Background(), req) - require.NoError(t, err) - require.NotEqual(t, 0, req.NumRows()) - - rows := statistics.RowToDatums(req.GetRow(0), r.Fields()) - if variable.DefTiDBEnablePaging { - match(t, rows, "1") - } else { - match(t, rows, "0") - } - r.Close() - } -} - -func TestTiDBOptRangeMaxSizeWhenUpgrading(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // Upgrade from v6.3.0 to v6.4.0+. - ver94 := 94 - seV630 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver94)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV630, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver94)) - MustExec(t, seV630, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptRangeMaxSize)) - MustExec(t, seV630, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV630) - require.NoError(t, err) - require.Equal(t, int64(ver94), ver) - - // We are now in 6.3.0, check tidb_opt_range_max_size should not exist. - res := MustExecToRecodeSet(t, seV630, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptRangeMaxSize)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - dom.Close() - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in version no lower than v6.4.0, tidb_opt_range_max_size should be 0. - res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_opt_range_max_size") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, "0", row.GetString(0)) - - res = MustExecToRecodeSet(t, seCurVer, "select @@global.tidb_opt_range_max_size") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, "0", row.GetString(0)) -} - -func TestTiDBOptAdvancedJoinHintWhenUpgrading(t *testing.T) { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // Upgrade from v6.6.0 to v7.0.0+. - ver134 := 134 - seV660 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver134)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV660, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver134)) - MustExec(t, seV660, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptAdvancedJoinHint)) - MustExec(t, seV660, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV660) - require.NoError(t, err) - require.Equal(t, int64(ver134), ver) - - // We are now in 6.6.0, check tidb_opt_advanced_join_hint should not exist. - res := MustExecToRecodeSet(t, seV660, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBOptAdvancedJoinHint)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - dom.Close() - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in version no lower than v7.0.0, tidb_opt_advanced_join_hint should be false. - res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_opt_advanced_join_hint;") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, int64(0), row.GetInt64(0)) - - res = MustExecToRecodeSet(t, seCurVer, "select @@global.tidb_opt_advanced_join_hint;") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, int64(0), row.GetInt64(0)) -} - -func TestTiDBOptAdvancedJoinHintInNewCluster(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - // Indicates we are in a new cluster. - require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) - dom, err := BootstrapSession(store) - require.NoError(t, err) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - // In a new created cluster(above 7.0+), tidb_opt_advanced_join_hint is true by default. - MustExec(t, se, "use test;") - r := MustExecToRecodeSet(t, se, "select @@tidb_opt_advanced_join_hint;") - require.NotNil(t, r) - - ctx := context.Background() - chk := r.NewChunk(nil) - err = r.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, int64(1), row.GetInt64(0)) -} - -func TestTiDBCostModelInNewCluster(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - // Indicates we are in a new cluster. - require.Equal(t, int64(notBootstrapped), getStoreBootstrapVersion(store)) - dom, err := BootstrapSession(store) - require.NoError(t, err) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - se := CreateSessionAndSetID(t, store) - - // In a new created cluster(above 6.5+), tidb_cost_model_version is 2 by default. - MustExec(t, se, "use test;") - r := MustExecToRecodeSet(t, se, "select @@tidb_cost_model_version;") - require.NotNil(t, r) - - ctx := context.Background() - chk := r.NewChunk(nil) - err = r.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, "2", row.GetString(0)) -} - -func TestTiDBCostModelUpgradeFrom300To650(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // Upgrade from 3.0.0 to 6.5+. - ver300 := 33 - seV3 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver300)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV3, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver300)) - MustExec(t, seV3, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) - MustExec(t, seV3, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV3) - require.NoError(t, err) - require.Equal(t, int64(ver300), ver) - - // We are now in 3.0.0, check TiDBCostModelVersion should not exist. - res := MustExecToRecodeSet(t, seV3, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5+, TiDBCostModelVersion should be 1. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_cost_model_version") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 1, row.Len()) - require.Equal(t, "1", row.GetString(0)) -} - -func TestTiDBCostModelUpgradeFrom610To650(t *testing.T) { - for i := 0; i < 2; i++ { - func() { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.1 to 6.5+. - ver61 := 91 - seV61 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver61)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV61, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver61)) - MustExec(t, seV61, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "1", variable.TiDBCostModelVersion)) - MustExec(t, seV61, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV61) - require.NoError(t, err) - require.Equal(t, int64(ver61), ver) - - // We are now in 6.1, tidb_cost_model_version is 1. - res := MustExecToRecodeSet(t, seV61, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBCostModelVersion)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "1", row.GetString(1)) - res.Close() - - if i == 0 { - // For the first time, We set tidb_cost_model_version to 2. - // And after upgrade to 6.5, tidb_cost_model_version should be 2. - // For the second it should be 1. - MustExec(t, seV61, "set global tidb_cost_model_version = 2") - } - dom.Close() - // Upgrade to 6.5. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_cost_model_version") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 1, row.Len()) - if i == 0 { - require.Equal(t, "2", row.GetString(0)) - } else { - require.Equal(t, "1", row.GetString(0)) - } - res.Close() - }() - } -} - -func TestTiDBGCAwareUpgradeFrom630To650(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.3 to 6.5+. - ver63 := version93 - seV63 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver63)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV63, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver63)) - MustExec(t, seV63, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "1", variable.TiDBEnableGCAwareMemoryTrack)) - MustExec(t, seV63, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV63) - require.NoError(t, err) - require.Equal(t, int64(ver63), ver) - - // We are now in 6.3, tidb_enable_gc_aware_memory_track is ON. - res := MustExecToRecodeSet(t, seV63, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableGCAwareMemoryTrack)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "1", row.GetString(1)) - - // Upgrade to 6.5. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5. - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableGCAwareMemoryTrack)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "0", row.GetString(1)) -} - -func TestTiDBServerMemoryLimitUpgradeTo651_1(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.5.0 to 6.5.1+. - ver132 := version132 - seV132 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver132)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV132, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver132)) - MustExec(t, seV132, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBServerMemoryLimit)) - MustExec(t, seV132, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV132) - require.NoError(t, err) - require.Equal(t, int64(ver132), ver) - - // We are now in 6.5.0, tidb_server_memory_limit is 0. - res := MustExecToRecodeSet(t, seV132, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "0", row.GetString(1)) - - // Upgrade to 6.5.1+. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5.1+. - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, variable.DefTiDBServerMemoryLimit, row.GetString(1)) -} - -func TestTiDBServerMemoryLimitUpgradeTo651_2(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.5.0 to 6.5.1+. - ver132 := version132 - seV132 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver132)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV132, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver132)) - MustExec(t, seV132, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "70%", variable.TiDBServerMemoryLimit)) - MustExec(t, seV132, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV132) - require.NoError(t, err) - require.Equal(t, int64(ver132), ver) - - // We are now in 6.5.0, tidb_server_memory_limit is "70%". - res := MustExecToRecodeSet(t, seV132, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "70%", row.GetString(1)) - - // Upgrade to 6.5.1+. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5.1+. - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBServerMemoryLimit)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "70%", row.GetString(1)) -} - -func TestTiDBGlobalVariablesDefaultValueUpgradeFrom630To660(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.3.0 to 6.6.0. - ver630 := version93 - seV630 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver630)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV630, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver630)) - MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnableForeignKey)) - MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.ForeignKeyChecks)) - MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnableHistoricalStats)) - MustExec(t, seV630, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "OFF", variable.TiDBEnablePlanReplayerCapture)) - MustExec(t, seV630, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV630) - require.NoError(t, err) - require.Equal(t, int64(ver630), ver) - - // We are now in 6.3.0. - upgradeVars := []string{variable.TiDBEnableForeignKey, variable.ForeignKeyChecks, variable.TiDBEnableHistoricalStats, variable.TiDBEnablePlanReplayerCapture} - varsValueList := []string{"OFF", "OFF", "OFF", "OFF"} - for i := range upgradeVars { - res := MustExecToRecodeSet(t, seV630, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", upgradeVars[i])) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, varsValueList[i], row.GetString(1)) - } - - // Upgrade to 6.6.0. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seV660 := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seV660) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.6.0. - varsValueList = []string{"ON", "ON", "ON", "ON"} - for i := range upgradeVars { - res := MustExecToRecodeSet(t, seV660, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", upgradeVars[i])) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, varsValueList[i], row.GetString(1)) - } -} - -func TestTiDBStoreBatchSizeUpgradeFrom650To660(t *testing.T) { - for i := 0; i < 2; i++ { - func() { - ctx := context.Background() - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.5 to 6.6. - ver65 := version132 - seV65 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver65)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV65, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver65)) - MustExec(t, seV65, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBStoreBatchSize)) - MustExec(t, seV65, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV65) - require.NoError(t, err) - require.Equal(t, int64(ver65), ver) - - // We are now in 6.5, tidb_store_batch_size is 0. - res := MustExecToRecodeSet(t, seV65, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStoreBatchSize)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "0", row.GetString(1)) - res.Close() - - if i == 0 { - // For the first time, We set tidb_store_batch_size to 1. - // And after upgrade to 6.6, tidb_store_batch_size should be 1. - // For the second it should be the latest default value. - MustExec(t, seV65, "set global tidb_store_batch_size = 1") - } - dom.Close() - // Upgrade to 6.6. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.6. - res = MustExecToRecodeSet(t, seCurVer, "select @@tidb_store_batch_size") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 1, row.Len()) - if i == 0 { - require.Equal(t, "1", row.GetString(0)) - } else { - require.Equal(t, "4", row.GetString(0)) - } - res.Close() - }() - } -} - -func TestTiDBUpgradeToVer136(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - - ver135 := version135 - seV135 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver135)) - require.NoError(t, err) - MustExec(t, seV135, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver135)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV135) - require.NoError(t, err) - require.Equal(t, int64(ver135), ver) - - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(seV135) - require.NoError(t, err) - require.Less(t, int64(ver135), ver) - dom.Close() -} - -func TestTiDBUpgradeToVer140(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - - ver139 := version139 - resetTo139 := func(s Session) { - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver139)) - require.NoError(t, err) - MustExec(t, s, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver139)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(s) - require.NoError(t, err) - require.Equal(t, int64(ver139), ver) - } - - // drop column task_key and then upgrade - s := CreateSessionAndSetID(t, store) - MustExec(t, s, "alter table mysql.tidb_global_task drop column task_key") - resetTo139(s) - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err := getBootstrapVersion(s) - require.NoError(t, err) - require.Less(t, int64(ver139), ver) - dom.Close() - - // upgrade with column task_key exists - s = CreateSessionAndSetID(t, store) - resetTo139(s) - dom, err = BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(s) - require.NoError(t, err) - require.Less(t, int64(ver139), ver) - dom.Close() -} - -func TestTiDBNonPrepPlanCacheUpgradeFrom540To700(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // bootstrap to 5.4 - ver54 := version82 - seV54 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver54)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV54, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver54)) - MustExec(t, seV54, fmt.Sprintf("delete from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) - MustExec(t, seV54, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV54) - require.NoError(t, err) - require.Equal(t, int64(ver54), ver) - - // We are now in 5.4, check TiDBCostModelVersion should not exist. - res := MustExecToRecodeSet(t, seV54, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 0, chk.NumRows()) - - // Upgrade to 7.0 - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 7.0 - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBEnableNonPreparedPlanCache)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "OFF", row.GetString(1)) // tidb_enable_non_prepared_plan_cache = off - - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBNonPreparedPlanCacheSize)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "100", row.GetString(1)) // tidb_non_prepared_plan_cache_size = 100 -} - -func TestTiDBStatsLoadPseudoTimeoutUpgradeFrom610To650(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 6.1 to 6.5+. - ver61 := version91 - seV61 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver61)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV61, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver61)) - MustExec(t, seV61, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBStatsLoadPseudoTimeout)) - MustExec(t, seV61, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV61) - require.NoError(t, err) - require.Equal(t, int64(ver61), ver) - - // We are now in 6.1, tidb_stats_load_pseudo_timeout is OFF. - res := MustExecToRecodeSet(t, seV61, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStatsLoadPseudoTimeout)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "0", row.GetString(1)) - - // Upgrade to 6.5. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 6.5. - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBStatsLoadPseudoTimeout)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "1", row.GetString(1)) -} - -func TestTiDBTiDBOptTiDBOptimizerEnableNAAJWhenUpgradingToVer138(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - ver137 := version137 - seV137 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver137)) - require.NoError(t, err) - MustExec(t, seV137, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver137)) - MustExec(t, seV137, "update mysql.GLOBAL_VARIABLES set variable_value='OFF' where variable_name='tidb_enable_null_aware_anti_join'") - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV137) - require.NoError(t, err) - require.Equal(t, int64(ver137), ver) - - res := MustExecToRecodeSet(t, seV137, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_enable_null_aware_anti_join'") - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "OFF", row.GetString(1)) - - // Upgrade to version 138. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - res = MustExecToRecodeSet(t, seCurVer, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_enable_null_aware_anti_join'") - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "ON", row.GetString(1)) -} - -func TestTiDBUpgradeToVer143(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - - ver142 := version142 - seV142 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver142)) - require.NoError(t, err) - MustExec(t, seV142, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver142)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV142) - require.NoError(t, err) - require.Equal(t, int64(ver142), ver) - - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(seV142) - require.NoError(t, err) - require.Less(t, int64(ver142), ver) - dom.Close() -} - -func TestTiDBLoadBasedReplicaReadThresholdUpgradingToVer141(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // upgrade from 7.0 to 7.1. - ver70 := version139 - seV70 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver70)) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) - MustExec(t, seV70, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver70)) - MustExec(t, seV70, fmt.Sprintf("update mysql.GLOBAL_VARIABLES set variable_value='%s' where variable_name='%s'", "0", variable.TiDBLoadBasedReplicaReadThreshold)) - MustExec(t, seV70, "commit") - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV70) - require.NoError(t, err) - require.Equal(t, int64(ver70), ver) - - // We are now in 7.0, tidb_load_based_replica_read_threshold is 0. - res := MustExecToRecodeSet(t, seV70, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBLoadBasedReplicaReadThreshold)) - chk := res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "0", row.GetString(1)) - - // Upgrade to 7.1. - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err = getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // We are now in 7.1. - res = MustExecToRecodeSet(t, seCurVer, fmt.Sprintf("select * from mysql.GLOBAL_VARIABLES where variable_name='%s'", variable.TiDBLoadBasedReplicaReadThreshold)) - chk = res.NewChunk(nil) - err = res.Next(ctx, chk) - require.NoError(t, err) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, 2, row.Len()) - require.Equal(t, "1s", row.GetString(1)) -} - -func TestTiDBPlanCacheInvalidationOnFreshStatsWhenUpgradingToVer144(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // bootstrap as version143 - ver143 := version143 - seV143 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver143)) - require.NoError(t, err) - MustExec(t, seV143, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver143)) - // simulate a real ver143 where `tidb_plan_cache_invalidation_on_fresh_stats` doesn't exist yet - MustExec(t, seV143, "delete from mysql.GLOBAL_VARIABLES where variable_name='tidb_plan_cache_invalidation_on_fresh_stats'") - err = txn.Commit(context.Background()) - require.NoError(t, err) - unsetStoreBootstrapped(store.UUID()) - - // upgrade to ver144 - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err := getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // the value in the table is set to OFF automatically - res := MustExecToRecodeSet(t, seCurVer, "select * from mysql.GLOBAL_VARIABLES where variable_name='tidb_plan_cache_invalidation_on_fresh_stats'") - chk := res.NewChunk(nil) - require.NoError(t, res.Next(ctx, chk)) - require.Equal(t, 1, chk.NumRows()) - row := chk.GetRow(0) - require.Equal(t, "OFF", row.GetString(1)) - - // the session and global variable is also OFF - res = MustExecToRecodeSet(t, seCurVer, "select @@session.tidb_plan_cache_invalidation_on_fresh_stats, @@global.tidb_plan_cache_invalidation_on_fresh_stats") - chk = res.NewChunk(nil) - require.NoError(t, res.Next(ctx, chk)) - require.Equal(t, 1, chk.NumRows()) - row = chk.GetRow(0) - require.Equal(t, int64(0), row.GetInt64(0)) - require.Equal(t, int64(0), row.GetInt64(1)) -} - -func TestTiDBUpgradeToVer145(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - - ver144 := version144 - seV144 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver144)) - require.NoError(t, err) - MustExec(t, seV144, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver144)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV144) - require.NoError(t, err) - require.Equal(t, int64(ver144), ver) - - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(seV144) - require.NoError(t, err) - require.Less(t, int64(ver144), ver) - dom.Close() -} - -func TestTiDBUpgradeToVer170(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - ver169 := version169 - seV169 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver169)) - require.NoError(t, err) - MustExec(t, seV169, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver169)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV169) - require.NoError(t, err) - require.Equal(t, int64(ver169), ver) - - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(seV169) - require.NoError(t, err) - require.Less(t, int64(ver169), ver) - dom.Close() -} - -func TestTiDBBindingInListToVer175(t *testing.T) { - ctx := context.Background() - store, _ := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - - // bootstrap as version174 - ver174 := version174 - seV174 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver174)) - require.NoError(t, err) - MustExec(t, seV174, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver174)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - unsetStoreBootstrapped(store.UUID()) - - // create some bindings at version174 - MustExec(t, seV174, "use test") - MustExec(t, seV174, "create table t (a int, b int, c int, key(c))") - MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ... )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1,2,3)', 'test', 'enabled', '2023-09-13 14:41:38.319', '2023-09-13 14:41:35.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") - MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ? )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1)', 'test', 'enabled', '2023-09-13 14:41:38.319', '2023-09-13 14:41:36.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") - MustExec(t, seV174, "insert into mysql.bind_info values ('select * from `test` . `t` where `a` in ( ? ) and `b` in ( ... )', 'SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3)', 'test', 'enabled', '2023-09-13 14:41:37.319', '2023-09-13 14:41:38.319', 'utf8', 'utf8_general_ci', 'manual', '', '')") - - showBindings := func(s Session) (records []string) { - MustExec(t, s, "admin reload bindings") - res := MustExecToRecodeSet(t, s, "show global bindings") - chk := res.NewChunk(nil) - for { - require.NoError(t, res.Next(ctx, chk)) - if chk.NumRows() == 0 { - break - } - for i := 0; i < chk.NumRows(); i++ { - originalSQL := chk.GetRow(i).GetString(0) - bindSQL := chk.GetRow(i).GetString(1) - records = append(records, fmt.Sprintf("%s:%s", bindSQL, originalSQL)) - } - } - require.NoError(t, res.Close()) - sort.Strings(records) - return - } - bindings := showBindings(seV174) - // on ver174, `in (1)` and `in (1,2,3)` have different normalized results: `in (?)` and `in (...)` - require.Equal(t, []string{"SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3):select * from `test` . `t` where `a` in ( ? ) and `b` in ( ... )", - "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1):select * from `test` . `t` where `a` in ( ? )", - "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1,2,3):select * from `test` . `t` where `a` in ( ... )"}, bindings) - - // upgrade to ver175 - domCurVer, err := BootstrapSession(store) - require.NoError(t, err) - defer domCurVer.Close() - seCurVer := CreateSessionAndSetID(t, store) - ver, err := getBootstrapVersion(seCurVer) - require.NoError(t, err) - require.Equal(t, currentBootstrapVersion, ver) - - // `in (?)` becomes to `in ( ... )` - bindings = showBindings(seCurVer) - require.Equal(t, []string{"SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1) AND `b` IN (1,2,3):select * from `test` . `t` where `a` in ( ... ) and `b` in ( ... )", - "SELECT /*+ use_index(`t` `c`)*/ * FROM `test`.`t` WHERE `a` IN (1):select * from `test` . `t` where `a` in ( ... )"}, bindings) - - planFromBinding := func(s Session, q string) { - MustExec(t, s, q) - res := MustExecToRecodeSet(t, s, "select @@last_plan_from_binding") - chk := res.NewChunk(nil) - require.NoError(t, res.Next(ctx, chk)) - require.Equal(t, int64(1), chk.GetRow(0).GetInt64(0)) - require.NoError(t, res.Close()) - } - planFromBinding(seCurVer, "select * from test.t where a in (1)") - planFromBinding(seCurVer, "select * from test.t where a in (1,2,3)") - planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7)") - planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7) and b in(1)") - planFromBinding(seCurVer, "select * from test.t where a in (1,2,3,4,5,6,7) and b in(1,2,3,4)") - planFromBinding(seCurVer, "select * from test.t where a in (7) and b in(1,2,3,4)") -} - -func TestTiDBUpgradeToVer176(t *testing.T) { - store, _ := CreateStoreAndBootstrap(t) - defer func() { - require.NoError(t, store.Close()) - }() - ver175 := version175 - seV175 := CreateSessionAndSetID(t, store) - txn, err := store.Begin() - require.NoError(t, err) - m := meta.NewMeta(txn) - err = m.FinishBootstrap(int64(ver175)) - require.NoError(t, err) - MustExec(t, seV175, fmt.Sprintf("update mysql.tidb set variable_value=%d where variable_name='tidb_server_version'", ver175)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - unsetStoreBootstrapped(store.UUID()) - ver, err := getBootstrapVersion(seV175) - require.NoError(t, err) - require.Equal(t, int64(ver175), ver) - - dom, err := BootstrapSession(store) - require.NoError(t, err) - ver, err = getBootstrapVersion(seV175) - require.NoError(t, err) - require.Less(t, int64(ver175), ver) - dom.Close() -} diff --git a/session/bootstraptest/BUILD.bazel b/session/bootstraptest/BUILD.bazel deleted file mode 100644 index 33f5223c50cfc..0000000000000 --- a/session/bootstraptest/BUILD.bazel +++ /dev/null @@ -1,36 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "bootstraptest_test", - timeout = "short", - srcs = [ - "bootstrap_upgrade_test.go", #keep - "main_test.go", - ], - flaky = True, - shard_count = 11, - deps = [ - "//config", - "//ddl", - "//ddl/util/callback", - "//kv", - "//meta", - "//parser/model", - "//parser/terror", - "//server/handler", - "//session", #keep - "//sessionctx", - "//testkit", #keep - "//testkit/testmain", - "//testkit/testsetup", - "//util", - "//util/chunk", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", #keep - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/bootstraptest/main_test.go b/session/bootstraptest/main_test.go deleted file mode 100644 index df405ec826bda..0000000000000 --- a/session/bootstraptest/main_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package bootstraptest - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlDeleteWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/clusteredindextest/BUILD.bazel b/session/clusteredindextest/BUILD.bazel deleted file mode 100644 index f62bd8974198a..0000000000000 --- a/session/clusteredindextest/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "clusteredindextest_test", - timeout = "short", - srcs = [ - "clustered_index_test.go", - "main_test.go", - ], - data = glob(["testdata/**"]), - flaky = True, - race = "on", - shard_count = 3, - deps = [ - "//config", - "//kv", - "//session", - "//sessionctx/variable", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/clusteredindextest/main_test.go b/session/clusteredindextest/main_test.go deleted file mode 100644 index cc0fb222e05f9..0000000000000 --- a/session/clusteredindextest/main_test.go +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package clusteredindextest - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - - session.SetSchemaLease(20 * time.Millisecond) - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/main_test.go b/session/main_test.go deleted file mode 100644 index 2be6935026b81..0000000000000 --- a/session/main_test.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package session - -import ( - "flag" - "fmt" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - - SetSchemaLease(20 * time.Millisecond) - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func match(t *testing.T, row []types.Datum, expected ...interface{}) { - require.Len(t, row, len(expected)) - for i := range row { - if _, ok := expected[i].(time.Time); ok { - // Since password_last_changed is set to default current_timestamp, we pass this check. - continue - } - got := fmt.Sprintf("%v", row[i].GetValue()) - need := fmt.Sprintf("%v", expected[i]) - require.Equal(t, need, got, i) - } -} diff --git a/session/metrics/BUILD.bazel b/session/metrics/BUILD.bazel deleted file mode 100644 index 9dc63b40d72ec..0000000000000 --- a/session/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/session/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/session/metrics/metrics.go b/session/metrics/metrics.go deleted file mode 100644 index 2c6310790c7c4..0000000000000 --- a/session/metrics/metrics.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// session metrics vars -var ( - NonTransactionalDeleteCount prometheus.Counter - NonTransactionalInsertCount prometheus.Counter - NonTransactionalUpdateCount prometheus.Counter - - StatementPerTransactionPessimisticOKInternal prometheus.Observer - StatementPerTransactionPessimisticOKGeneral prometheus.Observer - StatementPerTransactionPessimisticErrorInternal prometheus.Observer - StatementPerTransactionPessimisticErrorGeneral prometheus.Observer - StatementPerTransactionOptimisticOKInternal prometheus.Observer - StatementPerTransactionOptimisticOKGeneral prometheus.Observer - StatementPerTransactionOptimisticErrorInternal prometheus.Observer - StatementPerTransactionOptimisticErrorGeneral prometheus.Observer - TransactionDurationPessimisticCommitInternal prometheus.Observer - TransactionDurationPessimisticCommitGeneral prometheus.Observer - TransactionDurationPessimisticAbortInternal prometheus.Observer - TransactionDurationPessimisticAbortGeneral prometheus.Observer - TransactionDurationOptimisticCommitInternal prometheus.Observer - TransactionDurationOptimisticCommitGeneral prometheus.Observer - TransactionDurationOptimisticAbortInternal prometheus.Observer - TransactionDurationOptimisticAbortGeneral prometheus.Observer - TransactionRetryInternal prometheus.Observer - TransactionRetryGeneral prometheus.Observer - - SessionExecuteCompileDurationInternal prometheus.Observer - SessionExecuteCompileDurationGeneral prometheus.Observer - SessionExecuteParseDurationInternal prometheus.Observer - SessionExecuteParseDurationGeneral prometheus.Observer - - TelemetryCTEUsageRecurCTE prometheus.Counter - TelemetryCTEUsageNonRecurCTE prometheus.Counter - TelemetryCTEUsageNotCTE prometheus.Counter - TelemetryMultiSchemaChangeUsage prometheus.Counter - TelemetryFlashbackClusterUsage prometheus.Counter - - TelemetryTablePartitionUsage prometheus.Counter - TelemetryTablePartitionListUsage prometheus.Counter - TelemetryTablePartitionRangeUsage prometheus.Counter - TelemetryTablePartitionHashUsage prometheus.Counter - TelemetryTablePartitionRangeColumnsUsage prometheus.Counter - TelemetryTablePartitionRangeColumnsGt1Usage prometheus.Counter - TelemetryTablePartitionRangeColumnsGt2Usage prometheus.Counter - TelemetryTablePartitionRangeColumnsGt3Usage prometheus.Counter - TelemetryTablePartitionListColumnsUsage prometheus.Counter - TelemetryTablePartitionMaxPartitionsUsage prometheus.Counter - TelemetryTablePartitionCreateIntervalUsage prometheus.Counter - TelemetryTablePartitionAddIntervalUsage prometheus.Counter - TelemetryTablePartitionDropIntervalUsage prometheus.Counter - TelemetryExchangePartitionUsage prometheus.Counter - TelemetryTableCompactPartitionUsage prometheus.Counter - TelemetryReorganizePartitionUsage prometheus.Counter - - TelemetryLockUserUsage prometheus.Counter - TelemetryUnlockUserUsage prometheus.Counter - TelemetryCreateOrAlterUserUsage prometheus.Counter - - TelemetryIndexMerge prometheus.Counter - TelemetryStoreBatchedUsage prometheus.Counter -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init session metrics vars. -func InitMetricsVars() { - NonTransactionalDeleteCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "delete"}) - NonTransactionalInsertCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "insert"}) - NonTransactionalUpdateCount = metrics.NonTransactionalDMLCount.With(prometheus.Labels{metrics.LblType: "update"}) - - StatementPerTransactionPessimisticOKInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblOK, metrics.LblInternal) - StatementPerTransactionPessimisticOKGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblOK, metrics.LblGeneral) - StatementPerTransactionPessimisticErrorInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblError, metrics.LblInternal) - StatementPerTransactionPessimisticErrorGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblPessimistic, metrics.LblError, metrics.LblGeneral) - StatementPerTransactionOptimisticOKInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblOK, metrics.LblInternal) - StatementPerTransactionOptimisticOKGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblOK, metrics.LblGeneral) - StatementPerTransactionOptimisticErrorInternal = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblError, metrics.LblInternal) - StatementPerTransactionOptimisticErrorGeneral = metrics.StatementPerTransaction.WithLabelValues(metrics.LblOptimistic, metrics.LblError, metrics.LblGeneral) - TransactionDurationPessimisticCommitInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblCommit, metrics.LblInternal) - TransactionDurationPessimisticCommitGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblCommit, metrics.LblGeneral) - TransactionDurationPessimisticAbortInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblAbort, metrics.LblInternal) - TransactionDurationPessimisticAbortGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblPessimistic, metrics.LblAbort, metrics.LblGeneral) - TransactionDurationOptimisticCommitInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblCommit, metrics.LblInternal) - TransactionDurationOptimisticCommitGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblCommit, metrics.LblGeneral) - TransactionDurationOptimisticAbortInternal = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblAbort, metrics.LblInternal) - TransactionDurationOptimisticAbortGeneral = metrics.TransactionDuration.WithLabelValues(metrics.LblOptimistic, metrics.LblAbort, metrics.LblGeneral) - TransactionRetryInternal = metrics.SessionRetry.WithLabelValues(metrics.LblInternal) - TransactionRetryGeneral = metrics.SessionRetry.WithLabelValues(metrics.LblGeneral) - - SessionExecuteCompileDurationInternal = metrics.SessionExecuteCompileDuration.WithLabelValues(metrics.LblInternal) - SessionExecuteCompileDurationGeneral = metrics.SessionExecuteCompileDuration.WithLabelValues(metrics.LblGeneral) - SessionExecuteParseDurationInternal = metrics.SessionExecuteParseDuration.WithLabelValues(metrics.LblInternal) - SessionExecuteParseDurationGeneral = metrics.SessionExecuteParseDuration.WithLabelValues(metrics.LblGeneral) - - TelemetryCTEUsageRecurCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("recurCTE") - TelemetryCTEUsageNonRecurCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("nonRecurCTE") - TelemetryCTEUsageNotCTE = metrics.TelemetrySQLCTECnt.WithLabelValues("notCTE") - TelemetryMultiSchemaChangeUsage = metrics.TelemetryMultiSchemaChangeCnt - TelemetryFlashbackClusterUsage = metrics.TelemetryFlashbackClusterCnt - - TelemetryTablePartitionUsage = metrics.TelemetryTablePartitionCnt - TelemetryTablePartitionListUsage = metrics.TelemetryTablePartitionListCnt - TelemetryTablePartitionRangeUsage = metrics.TelemetryTablePartitionRangeCnt - TelemetryTablePartitionHashUsage = metrics.TelemetryTablePartitionHashCnt - TelemetryTablePartitionRangeColumnsUsage = metrics.TelemetryTablePartitionRangeColumnsCnt - TelemetryTablePartitionRangeColumnsGt1Usage = metrics.TelemetryTablePartitionRangeColumnsGt1Cnt - TelemetryTablePartitionRangeColumnsGt2Usage = metrics.TelemetryTablePartitionRangeColumnsGt2Cnt - TelemetryTablePartitionRangeColumnsGt3Usage = metrics.TelemetryTablePartitionRangeColumnsGt3Cnt - TelemetryTablePartitionListColumnsUsage = metrics.TelemetryTablePartitionListColumnsCnt - TelemetryTablePartitionMaxPartitionsUsage = metrics.TelemetryTablePartitionMaxPartitionsCnt - TelemetryTablePartitionCreateIntervalUsage = metrics.TelemetryTablePartitionCreateIntervalPartitionsCnt - TelemetryTablePartitionAddIntervalUsage = metrics.TelemetryTablePartitionAddIntervalPartitionsCnt - TelemetryTablePartitionDropIntervalUsage = metrics.TelemetryTablePartitionDropIntervalPartitionsCnt - TelemetryExchangePartitionUsage = metrics.TelemetryExchangePartitionCnt - TelemetryTableCompactPartitionUsage = metrics.TelemetryCompactPartitionCnt - TelemetryReorganizePartitionUsage = metrics.TelemetryReorganizePartitionCnt - - TelemetryLockUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("lockUser") - TelemetryUnlockUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("unlockUser") - TelemetryCreateOrAlterUserUsage = metrics.TelemetryAccountLockCnt.WithLabelValues("createOrAlterUser") - - TelemetryIndexMerge = metrics.TelemetryIndexMergeUsage - TelemetryStoreBatchedUsage = metrics.TelemetryStoreBatchedQueryCnt -} diff --git a/session/nontransactionaltest/BUILD.bazel b/session/nontransactionaltest/BUILD.bazel deleted file mode 100644 index 506ff14e46ca6..0000000000000 --- a/session/nontransactionaltest/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "nontransactionaltest_test", - timeout = "short", - srcs = [ - "main_test.go", - "nontransactional_test.go", - ], - flaky = True, - shard_count = 3, - deps = [ - "//config", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/nontransactionaltest/main_test.go b/session/nontransactionaltest/main_test.go deleted file mode 100644 index f88e4cb8a3303..0000000000000 --- a/session/nontransactionaltest/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package nontransactionaltest - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/resourcegrouptest/BUILD.bazel b/session/resourcegrouptest/BUILD.bazel deleted file mode 100644 index e1abfcc3eda1a..0000000000000 --- a/session/resourcegrouptest/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "resourcegrouptest_test", - timeout = "short", - srcs = ["resource_group_test.go"], - flaky = True, - deps = [ - "//testkit", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - ], -) diff --git a/session/resourcegrouptest/resource_group_test.go b/session/resourcegrouptest/resource_group_test.go deleted file mode 100644 index cd31663228e45..0000000000000 --- a/session/resourcegrouptest/resource_group_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package resourcegrouptest - -import ( - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestResourceGroupHintInTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("create resource group rg1 ru_per_sec=1000") - tk.MustExec("create resource group rg2 ru_per_sec=1000") - tk.MustExec("use test;") - tk.MustExec("create table t (id int primary key, val int)") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/TxnResouceGroupChecker", `return("default")`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/kv/TxnResouceGroupChecker")) - }() - tk.MustExec("insert into t values (1, 1);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/TxnResouceGroupChecker", `return("rg1")`)) - tk.MustExec("insert /*+ RESOURCE_GROUP(rg1) */ into t values (2, 2);") - tk.MustExec("BEGIN;") - // for pessimistic lock the resource group should be rg1 - tk.MustExec("insert /*+ RESOURCE_GROUP(rg1) */ into t values (3, 3);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/TxnResouceGroupChecker", `return("rg2")`)) - // for final prewrite/commit the resource group should be rg2 - tk.MustExec("update /*+ RESOURCE_GROUP(rg2) */ t set val = val + 1 where id = 3;") - tk.MustExec("COMMIT;") - - tk.MustExec("SET @@autocommit=1;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/kv/TxnResouceGroupChecker", `return("default")`)) - tk.MustExec("insert /*+ RESOURCE_GROUP(not_exist_group) */ into t values (4, 4);") - - tk.MustExec("BEGIN;") - // for pessimistic lock the resource group should be rg1 - tk.MustExec("insert /*+ RESOURCE_GROUP(unknown_1) */ into t values (5, 5);") - // for final prewrite/commit the resource group should be rg2 - tk.MustExec("update /*+ RESOURCE_GROUP(unknown_2) */ t set val = val + 1 where id = 5;") - tk.MustExec("COMMIT;") -} diff --git a/session/schematest/BUILD.bazel b/session/schematest/BUILD.bazel deleted file mode 100644 index a35607a0c0950..0000000000000 --- a/session/schematest/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "schematest_test", - timeout = "short", - srcs = [ - "main_test.go", - "schema_test.go", - ], - flaky = True, - shard_count = 14, - deps = [ - "//config", - "//domain", - "//kv", - "//parser/model", - "//parser/terror", - "//planner/core", - "//server", - "//session", - "//sessionctx/variable", - "//store/mockstore", - "//tablecodec", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/schematest/main_test.go b/session/schematest/main_test.go deleted file mode 100644 index 99ef8e45fa848..0000000000000 --- a/session/schematest/main_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package schematest - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/txnkv/transaction.keepAlive"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/schematest/schema_test.go b/session/schematest/schema_test.go deleted file mode 100644 index fb661cb19baae..0000000000000 --- a/session/schematest/schema_test.go +++ /dev/null @@ -1,681 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schematest - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" -) - -func createMockStoreForSchemaTest(t *testing.T, opts ...mockstore.MockTiKVStoreOption) kv.Storage { - store, err := mockstore.NewMockStore(opts...) - require.NoError(t, err) - session.DisableStats4Test() - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - - dom.SetStatsUpdating(true) - - sv := server.CreateMockServer(t, store) - sv.SetDomain(dom) - dom.InfoSyncer().SetSessionManager(sv) - - t.Cleanup(func() { - dom.Close() - require.NoError(t, store.Close()) - }) - return store -} - -func TestPrepareStmtCommitWhenSchemaChanged(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk1.MustExec("set global tidb_enable_metadata_lock=0") - tk2.MustExec("use test") - - tk1.MustExec("create table t (a int, b int)") - tk2.MustExec("prepare stmt from 'insert into t values (?, ?)'") - tk2.MustExec("set @a = 1") - - // Commit find unrelated schema change. - tk2.MustExec("begin") - tk1.MustExec("create table t1 (id int)") - tk2.MustExec("execute stmt using @a, @a") - tk2.MustExec("commit") - - tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk2.MustExec("begin") - tk1.MustExec("alter table t drop column b") - tk2.MustExec("execute stmt using @a, @a") - err := tk2.ExecToErr("commit") - require.True(t, terror.ErrorEqual(err, plannercore.ErrWrongValueCountOnRow), fmt.Sprintf("err %v", err)) -} - -func TestCommitWhenSchemaChanged(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("create table t (a int, b int)") - - tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk2.MustExec("begin") - tk2.MustExec("insert into t values (1, 1)") - - tk1.MustExec("alter table t drop column b") - - // When tk2 commit, it will find schema already changed. - tk2.MustExec("insert into t values (4, 4)") - err := tk2.ExecToErr("commit") - require.True(t, terror.ErrorEqual(err, plannercore.ErrWrongValueCountOnRow), fmt.Sprintf("err %v", err)) -} - -func TestRetrySchemaChangeForEmptyChange(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("create table t (i int)") - tk1.MustExec("create table t1 (i int)") - tk1.MustExec("begin") - tk2.MustExec("alter table t add j int") - tk1.MustExec("select * from t for update") - tk1.MustExec("update t set i = -i") - tk1.MustExec("delete from t") - tk1.MustExec("insert into t1 values (1)") - tk1.MustExec("commit") -} - -func TestRetrySchemaChange(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("create table t (a int primary key, b int)") - tk1.MustExec("insert into t values (1, 1)") - - tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk2.MustExec("begin") - tk2.MustExec("update t set b = 5 where a = 1") - - tk1.MustExec("alter table t add index b_i (b)") - - run := false - hook := func() { - if !run { - tk1.MustExec("update t set b = 3 where a = 1") - run = true - } - } - - // In order to cover a bug that statement history is not updated during retry. - // See https://github.com/pingcap/tidb/pull/5202 - // Step1: when tk2 commit, it find schema changed and retry(). - // Step2: during retry, hook() is called, tk1 update primary key. - // Step3: tk2 continue commit in retry() meet a retryable error(write conflict), retry again. - // Step4: tk2 retry() success, if it use the stale statement, data and index will inconsistent. - fpName := "github.com/pingcap/tidb/session/preCommitHook" - require.NoError(t, failpoint.Enable(fpName, "return")) - defer func() { require.NoError(t, failpoint.Disable(fpName)) }() - - ctx := context.WithValue(context.Background(), "__preCommitHook", hook) - require.NoError(t, tk2.Session().CommitTxn(ctx)) - tk1.MustQuery("select * from t where t.b = 5").Check(testkit.Rows("1 5")) -} - -func TestRetryMissingUnionScan(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("create table t (a int primary key, b int unique, c int)") - tk1.MustExec("insert into t values (1, 1, 1)") - - tk2.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk2.MustExec("begin") - tk2.MustExec("update t set b = 1, c = 2 where b = 2") - tk2.MustExec("update t set b = 1, c = 2 where a = 1") - - // Create a conflict to reproduces the bug that the second update statement in retry - // has a dirty table but doesn't use UnionScan. - tk1.MustExec("update t set b = 2 where a = 1") - - tk2.MustExec("commit") -} - -func TestTableReaderChunk(t *testing.T) { - // Since normally a single region mock tikv only returns one partial result we need to manually split the - // table to test multiple chunks. - var cluster testutils.Cluster - store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithSingleStore(c) - cluster = c - })) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table chk (a int)") - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) - } - tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("chk")) - require.NoError(t, err) - tableStart := tablecodec.GenTableRecordPrefix(tbl.Meta().ID) - cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 10) - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - tk.MustExec("set tidb_init_chunk_size = 2") - defer func() { - tk.MustExec(fmt.Sprintf("set tidb_init_chunk_size = %d", variable.DefInitChunkSize)) - }() - rs, err := tk.Exec("select * from chk") - require.NoError(t, err) - defer func() { require.NoError(t, rs.Close()) }() - - req := rs.NewChunk(nil) - var count int - var numChunks int - for { - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - numRows := req.NumRows() - if numRows == 0 { - break - } - for i := 0; i < numRows; i++ { - require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) - count++ - } - numChunks++ - } - require.Equal(t, 100, count) - // FIXME: revert this result to new group value after distsql can handle initChunkSize. - require.Equal(t, 1, numChunks) -} - -func TestInsertExecChunk(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table test1(a int)") - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert test1 values (%d)", i)) - } - tk.MustExec("create table test2(a int)") - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - tk.MustExec("insert into test2(a) select a from test1;") - - rs, err := tk.Exec("select * from test2") - require.NoError(t, err) - defer func() { require.NoError(t, rs.Close()) }() - var idx int - for { - req := rs.NewChunk(nil) - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - if req.NumRows() == 0 { - break - } - - for rowIdx := 0; rowIdx < req.NumRows(); rowIdx++ { - row := req.GetRow(rowIdx) - require.Equal(t, int64(idx), row.GetInt64(0)) - idx++ - } - } - require.Equal(t, 100, idx) -} - -func TestUpdateExecChunk(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table chk(a int)") - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) - } - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("update chk set a = a + 100 where a = %d", i)) - } - - rs, err := tk.Exec("select * from chk") - require.NoError(t, err) - defer func() { require.NoError(t, rs.Close()) }() - var idx int - for { - req := rs.NewChunk(nil) - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - if req.NumRows() == 0 { - break - } - - for rowIdx := 0; rowIdx < req.NumRows(); rowIdx++ { - row := req.GetRow(rowIdx) - require.Equal(t, int64(idx+100), row.GetInt64(0)) - idx++ - } - } - - require.Equal(t, 100, idx) -} - -func TestDeleteExecChunk(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table chk(a int)") - - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert chk values (%d)", i)) - } - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - - for i := 0; i < 99; i++ { - tk.MustExec(fmt.Sprintf("delete from chk where a = %d", i)) - } - - rs, err := tk.Exec("select * from chk") - require.NoError(t, err) - defer func() { require.NoError(t, rs.Close()) }() - - req := rs.NewChunk(nil) - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - - row := req.GetRow(0) - require.Equal(t, int64(99), row.GetInt64(0)) -} - -func TestDeleteMultiTableExecChunk(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table chk1(a int)") - tk.MustExec("create table chk2(a int)") - - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert chk1 values (%d)", i)) - } - - for i := 0; i < 50; i++ { - tk.MustExec(fmt.Sprintf("insert chk2 values (%d)", i)) - } - - tk.Session().GetSessionVars().SetDistSQLScanConcurrency(1) - - tk.MustExec("delete chk1, chk2 from chk1 inner join chk2 where chk1.a = chk2.a") - - rs, err := tk.Exec("select * from chk1") - require.NoError(t, err) - - var idx int - for { - req := rs.NewChunk(nil) - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - - if req.NumRows() == 0 { - break - } - - for i := 0; i < req.NumRows(); i++ { - row := req.GetRow(i) - require.Equal(t, int64(idx+50), row.GetInt64(0)) - idx++ - } - } - require.Equal(t, 50, idx) - require.NoError(t, rs.Close()) - - rs, err = tk.Exec("select * from chk2") - require.NoError(t, err) - - req := rs.NewChunk(nil) - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) - require.NoError(t, rs.Close()) -} - -func TestIndexLookUpReaderChunk(t *testing.T) { - // Since normally a single region mock tikv only returns one partial result we need to manually split the - // table to test multiple chunks. - var cluster testutils.Cluster - store := testkit.CreateMockStore(t, mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithSingleStore(c) - cluster = c - })) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists chk") - tk.MustExec("create table chk (k int unique, c int)") - for i := 0; i < 100; i++ { - tk.MustExec(fmt.Sprintf("insert chk values (%d, %d)", i, i)) - } - tbl, err := domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("chk")) - require.NoError(t, err) - indexStart := tablecodec.EncodeTableIndexPrefix(tbl.Meta().ID, tbl.Indices()[0].Meta().ID) - cluster.SplitKeys(indexStart, indexStart.PrefixNext(), 10) - - tk.Session().GetSessionVars().IndexLookupSize = 10 - rs, err := tk.Exec("select * from chk order by k") - require.NoError(t, err) - req := rs.NewChunk(nil) - var count int - for { - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - numRows := req.NumRows() - if numRows == 0 { - break - } - for i := 0; i < numRows; i++ { - require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) - require.Equal(t, int64(count), req.GetRow(i).GetInt64(1)) - count++ - } - } - require.Equal(t, 100, count) - require.NoError(t, rs.Close()) - - rs, err = tk.Exec("select k from chk where c < 90 order by k") - require.NoError(t, err) - req = rs.NewChunk(nil) - count = 0 - for { - err = rs.Next(context.TODO(), req) - require.NoError(t, err) - numRows := req.NumRows() - if numRows == 0 { - break - } - for i := 0; i < numRows; i++ { - require.Equal(t, int64(count), req.GetRow(i).GetInt64(0)) - count++ - } - } - require.Equal(t, 90, count) - require.NoError(t, rs.Close()) -} - -func TestTxnSize(t *testing.T) { - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists txn_size") - tk.MustExec("create table txn_size (k int , v varchar(64))") - tk.MustExec("begin") - tk.MustExec("insert txn_size values (1, 'dfaasdfsdf')") - tk.MustExec("insert txn_size values (2, 'dsdfaasdfsdf')") - tk.MustExec("insert txn_size values (3, 'abcdefghijkl')") - txn, err := tk.Session().Txn(false) - require.NoError(t, err) - require.Greater(t, txn.Size(), 0) -} - -func TestValidationRecursion(t *testing.T) { - // We have to expect that validation functions will call GlobalVarsAccessor.GetGlobalSysVar(). - // This tests for a regression where GetGlobalSysVar() can not safely call the validation - // function because it might cause infinite recursion. - // See: https://github.com/pingcap/tidb/issues/30255 - sv := variable.SysVar{Scope: variable.ScopeGlobal, Name: "mynewsysvar", Value: "test", Validation: func(vars *variable.SessionVars, normalizedValue string, originalValue string, scope variable.ScopeFlag) (string, error) { - return vars.GlobalVarsAccessor.GetGlobalSysVar("mynewsysvar") - }} - variable.RegisterSysVar(&sv) - - store := createMockStoreForSchemaTest(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - val, err := sv.Validate(tk.Session().GetSessionVars(), "test2", variable.ScopeGlobal) - require.NoError(t, err) - require.Equal(t, "test", val) -} - -func TestGlobalAndLocalTxn(t *testing.T) { - // Because the PD config of check_dev_2 test is not compatible with local/global txn yet, - // so we will skip this test for now. - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_enable_local_txn = on;") - tk.MustExec("drop table if exists t1") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - tk.MustExec("create placement policy p1 leader_constraints='[+zone=dc-1]'") - tk.MustExec("create placement policy p2 leader_constraints='[+zone=dc-2]'") - tk.MustExec(`create table t1 (c int) -PARTITION BY RANGE (c) ( - PARTITION p0 VALUES LESS THAN (100) placement policy p1, - PARTITION p1 VALUES LESS THAN (200) placement policy p2 -);`) - defer func() { - tk.MustExec("drop table if exists t1") - tk.MustExec("drop placement policy if exists p1") - tk.MustExec("drop placement policy if exists p2") - }() - - // set txn_scope to global - tk.MustExec(fmt.Sprintf("set @@session.txn_scope = '%s';", kv.GlobalTxnScope)) - result := tk.MustQuery("select @@txn_scope;") - result.Check(testkit.Rows(kv.GlobalTxnScope)) - - // test global txn auto commit - tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with global scope - result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope - require.Equal(t, 1, len(result.Rows())) - - // begin and commit with global txn scope - tk.MustExec("begin") - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().TxnCtx.TxnScope) - require.True(t, txn.Valid()) - tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with global scope - result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope - require.Equal(t, 2, len(result.Rows())) - require.True(t, txn.Valid()) - tk.MustExec("commit") - result = tk.MustQuery("select * from t1") - require.Equal(t, 2, len(result.Rows())) - - // begin and rollback with global txn scope - tk.MustExec("begin") - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().TxnCtx.TxnScope) - require.True(t, txn.Valid()) - tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with global scope - result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope - require.Equal(t, 3, len(result.Rows())) - require.True(t, txn.Valid()) - tk.MustExec("rollback") - result = tk.MustQuery("select * from t1") - require.Equal(t, 2, len(result.Rows())) - - timeBeforeWriting := time.Now() - tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with global scope - result = tk.MustQuery("select * from t1") // read dc-1 and dc-2 with global scope - require.Equal(t, 3, len(result.Rows())) - - failpoint.Enable("tikvclient/injectTxnScope", `return("dc-1")`) - defer failpoint.Disable("tikvclient/injectTxnScope") - // set txn_scope to local - tk.MustExec("set @@session.txn_scope = 'local';") - result = tk.MustQuery("select @@txn_scope;") - result.Check(testkit.Rows("local")) - - // test local txn auto commit - tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope - result = tk.MustQuery("select * from t1 where c = 1") // point get dc-1 with dc-1 scope - require.Equal(t, 3, len(result.Rows())) - result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope - require.Equal(t, 3, len(result.Rows())) - - // begin and commit with dc-1 txn scope - tk.MustExec("begin") - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - require.True(t, txn.Valid()) - tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope - result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope - require.Equal(t, 4, len(result.Rows())) - require.True(t, txn.Valid()) - tk.MustExec("commit") - result = tk.MustQuery("select * from t1 where c < 100") - require.Equal(t, 4, len(result.Rows())) - - // begin and rollback with dc-1 txn scope - tk.MustExec("begin") - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - require.True(t, txn.Valid()) - tk.MustExec("insert into t1 (c) values (1)") // write dc-1 with dc-1 scope - result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope - require.Equal(t, 5, len(result.Rows())) - require.True(t, txn.Valid()) - tk.MustExec("rollback") - result = tk.MustQuery("select * from t1 where c < 100") - require.Equal(t, 4, len(result.Rows())) - - // test wrong scope local txn auto commit - _, err = tk.Exec("insert into t1 (c) values (101)") // write dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*out of txn_scope.*", err) - err = tk.ExecToErr("select * from t1 where c = 101") // point get dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - tk.MustExec("begin") - err = tk.ExecToErr("select * from t1 where c = 101") // point get dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - tk.MustExec("commit") - - // begin and commit reading & writing the data in dc-2 with dc-1 txn scope - tk.MustExec("begin") - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - require.True(t, txn.Valid()) - tk.MustExec("insert into t1 (c) values (101)") // write dc-2 with dc-1 scope - err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - tk.MustExec("insert into t1 (c) values (99)") // write dc-1 with dc-1 scope - result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope - require.Equal(t, 5, len(result.Rows())) - require.True(t, txn.Valid()) - _, err = tk.Exec("commit") - require.Error(t, err) - require.Regexp(t, ".*out of txn_scope.*", err) - // Won't read the value 99 because the previous commit failed - result = tk.MustQuery("select * from t1 where c < 100") // read dc-1 with dc-1 scope - require.Equal(t, 4, len(result.Rows())) - - // Stale Read will ignore the cross-dc txn scope. - require.Equal(t, "dc-1", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - result = tk.MustQuery("select @@txn_scope;") - result.Check(testkit.Rows("local")) - err = tk.ExecToErr("select * from t1 where c > 100") // read dc-2 with dc-1 scope - require.Error(t, err) - require.Regexp(t, ".*can not be read by.*", err) - // Read dc-2 with Stale Read (in dc-1 scope) - timestamp := timeBeforeWriting.Format(time.RFC3339Nano) - // TODO: check the result of Stale Read when we figure out how to make the time precision more accurate. - tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c = 101", timestamp)) - tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c > 100", timestamp)) - tk.MustExec(fmt.Sprintf("START TRANSACTION READ ONLY AS OF TIMESTAMP '%s'", timestamp)) - tk.MustExec("select * from t1 where c = 101") - tk.MustExec("select * from t1 where c > 100") - tk.MustExec("commit") - tk.MustExec("set @@tidb_replica_read='closest-replicas'") - tk.MustExec(fmt.Sprintf("select * from t1 AS OF TIMESTAMP '%s' where c > 100", timestamp)) - tk.MustExec(fmt.Sprintf("START TRANSACTION READ ONLY AS OF TIMESTAMP '%s'", timestamp)) - tk.MustExec("select * from t1 where c = 101") - tk.MustExec("select * from t1 where c > 100") - tk.MustExec("commit") - - tk.MustExec("set global tidb_enable_local_txn = off;") -} diff --git a/session/session.go b/session/session.go deleted file mode 100644 index 957bdf2cb0672..0000000000000 --- a/session/session.go +++ /dev/null @@ -1,4434 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package session - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/hex" - "encoding/json" - stderrs "errors" - "fmt" - "math" - "math/rand" - "runtime/pprof" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/bindinfo" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/extension/extensionimpl" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/owner" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/plugin" - "github.com/pingcap/tidb/privilege" - "github.com/pingcap/tidb/privilege/conn" - "github.com/pingcap/tidb/privilege/privileges" - session_metrics "github.com/pingcap/tidb/session/metrics" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics/handle/usage" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/driver/txn" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/telemetry" - "github.com/pingcap/tidb/ttl/ttlworker" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/logutil/consistency" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sem" - "github.com/pingcap/tidb/util/sli" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/syncutil" - "github.com/pingcap/tidb/util/tableutil" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tidb/util/topsql" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tipb/go-binlog" - tikverr "github.com/tikv/client-go/v2/error" - tikvstore "github.com/tikv/client-go/v2/kv" - "github.com/tikv/client-go/v2/oracle" - tikvutil "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -// Session context, it is consistent with the lifecycle of a client connection. -type Session interface { - sessionctx.Context - Status() uint16 // Flag of current status, such as autocommit. - LastInsertID() uint64 // LastInsertID is the last inserted auto_increment ID. - LastMessage() string // LastMessage is the info message that may be generated by last command - AffectedRows() uint64 // Affected rows by latest executed stmt. - // Execute is deprecated, and only used by plugins. Use ExecuteStmt() instead. - Execute(context.Context, string) ([]sqlexec.RecordSet, error) // Execute a sql statement. - // ExecuteStmt executes a parsed statement. - ExecuteStmt(context.Context, ast.StmtNode) (sqlexec.RecordSet, error) - // Parse is deprecated, use ParseWithParams() instead. - Parse(ctx context.Context, sql string) ([]ast.StmtNode, error) - // ExecuteInternal is a helper around ParseWithParams() and ExecuteStmt(). It is not allowed to execute multiple statements. - ExecuteInternal(context.Context, string, ...interface{}) (sqlexec.RecordSet, error) - String() string // String is used to debug. - CommitTxn(context.Context) error - RollbackTxn(context.Context) - // PrepareStmt executes prepare statement in binary protocol. - PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) - // ExecutePreparedStmt executes a prepared statement. - // Deprecated: please use ExecuteStmt, this function is left for testing only. - // TODO: remove ExecutePreparedStmt. - ExecutePreparedStmt(ctx context.Context, stmtID uint32, param []expression.Expression) (sqlexec.RecordSet, error) - DropPreparedStmt(stmtID uint32) error - // SetSessionStatesHandler sets SessionStatesHandler for type stateType. - SetSessionStatesHandler(stateType sessionstates.SessionStateType, handler sessionctx.SessionStatesHandler) - SetClientCapability(uint32) // Set client capability flags. - SetConnectionID(uint64) - SetCommandValue(byte) - SetProcessInfo(string, time.Time, byte, uint64) - SetTLSState(*tls.ConnectionState) - SetCollation(coID int) error - SetSessionManager(util.SessionManager) - Close() - Auth(user *auth.UserIdentity, auth, salt []byte, authConn conn.AuthConn) error - AuthWithoutVerification(user *auth.UserIdentity) bool - AuthPluginForUser(user *auth.UserIdentity) (string, error) - MatchIdentity(username, remoteHost string) (*auth.UserIdentity, error) - // Return the information of the txn current running - TxnInfo() *txninfo.TxnInfo - // PrepareTxnCtx is exported for test. - PrepareTxnCtx(context.Context) error - // FieldList returns fields list of a table. - FieldList(tableName string) (fields []*ast.ResultField, err error) - SetPort(port string) - - // set cur session operations allowed when tikv disk full happens. - SetDiskFullOpt(level kvrpcpb.DiskFullOpt) - GetDiskFullOpt() kvrpcpb.DiskFullOpt - ClearDiskFullOpt() - - // SetExtensions sets the `*extension.SessionExtensions` object - SetExtensions(extensions *extension.SessionExtensions) -} - -func init() { - executor.CreateSession = func(ctx sessionctx.Context) (sessionctx.Context, error) { - return CreateSession(ctx.GetStore()) - } - executor.CloseSession = func(ctx sessionctx.Context) { - if se, ok := ctx.(Session); ok { - se.Close() - } - } -} - -var _ Session = (*session)(nil) - -type stmtRecord struct { - st sqlexec.Statement - stmtCtx *stmtctx.StatementContext -} - -// StmtHistory holds all histories of statements in a txn. -type StmtHistory struct { - history []*stmtRecord -} - -// Add appends a stmt to history list. -func (h *StmtHistory) Add(st sqlexec.Statement, stmtCtx *stmtctx.StatementContext) { - s := &stmtRecord{ - st: st, - stmtCtx: stmtCtx, - } - h.history = append(h.history, s) -} - -// Count returns the count of the history. -func (h *StmtHistory) Count() int { - return len(h.history) -} - -type session struct { - // processInfo is used by ShowProcess(), and should be modified atomically. - processInfo atomic.Value - txn LazyTxn - - mu struct { - sync.RWMutex - values map[fmt.Stringer]interface{} - } - - currentCtx context.Context // only use for runtime.trace, Please NEVER use it. - currentPlan plannercore.Plan - - store kv.Storage - - sessionPlanCache sessionctx.PlanCache - - sessionVars *variable.SessionVars - sessionManager util.SessionManager - - statsCollector *usage.SessionStatsItem - // ddlOwnerManager is used in `select tidb_is_ddl_owner()` statement; - ddlOwnerManager owner.Manager - // lockedTables use to record the table locks hold by the session. - lockedTables map[int64]model.TableLockTpInfo - - // client shared coprocessor client per session - client kv.Client - - mppClient kv.MPPClient - - // indexUsageCollector collects index usage information. - idxUsageCollector *usage.SessionIndexUsageCollector - - functionUsageMu struct { - syncutil.RWMutex - builtinFunctionUsage telemetry.BuiltinFunctionsUsage - } - // allowed when tikv disk full happened. - diskFullOpt kvrpcpb.DiskFullOpt - - // StmtStats is used to count various indicators of each SQL in this session - // at each point in time. These data will be periodically taken away by the - // background goroutine. The background goroutine will continue to aggregate - // all the local data in each session, and finally report them to the remote - // regularly. - stmtStats *stmtstats.StatementStats - - // Used to encode and decode each type of session states. - sessionStatesHandlers map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler - - // Contains a list of sessions used to collect advisory locks. - advisoryLocks map[string]*advisoryLock - - extensions *extension.SessionExtensions - - sandBoxMode bool -} - -var parserPool = &sync.Pool{New: func() interface{} { return parser.New() }} - -// AddTableLock adds table lock to the session lock map. -func (s *session) AddTableLock(locks []model.TableLockTpInfo) { - for _, l := range locks { - // read only lock is session unrelated, skip it when adding lock to session. - if l.Tp != model.TableLockReadOnly { - s.lockedTables[l.TableID] = l - } - } -} - -// ReleaseTableLocks releases table lock in the session lock map. -func (s *session) ReleaseTableLocks(locks []model.TableLockTpInfo) { - for _, l := range locks { - delete(s.lockedTables, l.TableID) - } -} - -// ReleaseTableLockByTableIDs releases table lock in the session lock map by table ID. -func (s *session) ReleaseTableLockByTableIDs(tableIDs []int64) { - for _, tblID := range tableIDs { - delete(s.lockedTables, tblID) - } -} - -// CheckTableLocked checks the table lock. -func (s *session) CheckTableLocked(tblID int64) (bool, model.TableLockType) { - lt, ok := s.lockedTables[tblID] - if !ok { - return false, model.TableLockNone - } - return true, lt.Tp -} - -// GetAllTableLocks gets all table locks table id and db id hold by the session. -func (s *session) GetAllTableLocks() []model.TableLockTpInfo { - lockTpInfo := make([]model.TableLockTpInfo, 0, len(s.lockedTables)) - for _, tl := range s.lockedTables { - lockTpInfo = append(lockTpInfo, tl) - } - return lockTpInfo -} - -// HasLockedTables uses to check whether this session locked any tables. -// If so, the session can only visit the table which locked by self. -func (s *session) HasLockedTables() bool { - b := len(s.lockedTables) > 0 - return b -} - -// ReleaseAllTableLocks releases all table locks hold by the session. -func (s *session) ReleaseAllTableLocks() { - s.lockedTables = make(map[int64]model.TableLockTpInfo) -} - -// IsDDLOwner checks whether this session is DDL owner. -func (s *session) IsDDLOwner() bool { - return s.ddlOwnerManager.IsOwner() -} - -func (s *session) cleanRetryInfo() { - if s.sessionVars.RetryInfo.Retrying { - return - } - - retryInfo := s.sessionVars.RetryInfo - defer retryInfo.Clean() - if len(retryInfo.DroppedPreparedStmtIDs) == 0 { - return - } - - planCacheEnabled := s.GetSessionVars().EnablePreparedPlanCache - var cacheKey kvcache.Key - var err error - var preparedAst *ast.Prepared - var stmtText, stmtDB string - if planCacheEnabled { - firstStmtID := retryInfo.DroppedPreparedStmtIDs[0] - if preparedPointer, ok := s.sessionVars.PreparedStmts[firstStmtID]; ok { - preparedObj, ok := preparedPointer.(*plannercore.PlanCacheStmt) - if ok { - preparedAst = preparedObj.PreparedAst - stmtText, stmtDB = preparedObj.StmtText, preparedObj.StmtDB - bindSQL, _ := plannercore.GetBindSQL4PlanCache(s, preparedObj) - cacheKey, err = plannercore.NewPlanCacheKey(s.sessionVars, stmtText, stmtDB, preparedAst.SchemaVersion, - 0, bindSQL, expression.ExprPushDownBlackListReloadTimeStamp.Load()) - if err != nil { - logutil.Logger(s.currentCtx).Warn("clean cached plan failed", zap.Error(err)) - return - } - } - } - } - for i, stmtID := range retryInfo.DroppedPreparedStmtIDs { - if planCacheEnabled { - if i > 0 && preparedAst != nil { - plannercore.SetPstmtIDSchemaVersion(cacheKey, stmtText, preparedAst.SchemaVersion, s.sessionVars.IsolationReadEngines) - } - if !s.sessionVars.IgnorePreparedCacheCloseStmt { // keep the plan in cache - s.GetSessionPlanCache().Delete(cacheKey) - } - } - s.sessionVars.RemovePreparedStmt(stmtID) - } -} - -func (s *session) Status() uint16 { - return s.sessionVars.Status -} - -func (s *session) LastInsertID() uint64 { - if s.sessionVars.StmtCtx.LastInsertID > 0 { - return s.sessionVars.StmtCtx.LastInsertID - } - return s.sessionVars.StmtCtx.InsertID -} - -func (s *session) LastMessage() string { - return s.sessionVars.StmtCtx.GetMessage() -} - -func (s *session) AffectedRows() uint64 { - return s.sessionVars.StmtCtx.AffectedRows() -} - -func (s *session) SetClientCapability(capability uint32) { - s.sessionVars.ClientCapability = capability -} - -func (s *session) SetConnectionID(connectionID uint64) { - s.sessionVars.ConnectionID = connectionID -} - -func (s *session) SetTLSState(tlsState *tls.ConnectionState) { - // If user is not connected via TLS, then tlsState == nil. - if tlsState != nil { - s.sessionVars.TLSConnectionState = tlsState - } -} - -func (s *session) SetCommandValue(command byte) { - atomic.StoreUint32(&s.sessionVars.CommandValue, uint32(command)) -} - -func (s *session) SetCollation(coID int) error { - cs, co, err := charset.GetCharsetInfoByID(coID) - if err != nil { - return err - } - // If new collations are enabled, switch to the default - // collation if this one is not supported. - co = collate.SubstituteMissingCollationToDefault(co) - for _, v := range variable.SetNamesVariables { - terror.Log(s.sessionVars.SetSystemVarWithoutValidation(v, cs)) - } - return s.sessionVars.SetSystemVarWithoutValidation(variable.CollationConnection, co) -} - -func (s *session) GetSessionPlanCache() sessionctx.PlanCache { - // use the prepared plan cache - if !s.GetSessionVars().EnablePreparedPlanCache && !s.GetSessionVars().EnableNonPreparedPlanCache { - return nil - } - if s.sessionPlanCache == nil { // lazy construction - s.sessionPlanCache = plannercore.NewLRUPlanCache(uint(s.GetSessionVars().SessionPlanCacheSize), - variable.PreparedPlanCacheMemoryGuardRatio.Load(), plannercore.PreparedPlanCacheMaxMemory.Load(), s, false) - } - return s.sessionPlanCache -} - -func (s *session) SetSessionManager(sm util.SessionManager) { - s.sessionManager = sm -} - -func (s *session) GetSessionManager() util.SessionManager { - return s.sessionManager -} - -func (s *session) UpdateColStatsUsage(predicateColumns []model.TableItemID) { - if s.statsCollector == nil { - return - } - t := time.Now() - colMap := make(map[model.TableItemID]time.Time, len(predicateColumns)) - for _, col := range predicateColumns { - if col.IsIndex { - continue - } - colMap[col] = t - } - s.statsCollector.UpdateColStatsUsage(colMap) -} - -// StoreIndexUsage stores index usage information in idxUsageCollector. -func (s *session) StoreIndexUsage(tblID int64, idxID int64, rowsSelected int64) { - if s.idxUsageCollector == nil { - return - } - s.idxUsageCollector.Update(tblID, idxID, &usage.IndexUsageInformation{QueryCount: 1, RowsSelected: rowsSelected}) -} - -// FieldList returns fields list of a table. -func (s *session) FieldList(tableName string) ([]*ast.ResultField, error) { - is := s.GetInfoSchema().(infoschema.InfoSchema) - dbName := model.NewCIStr(s.GetSessionVars().CurrentDB) - tName := model.NewCIStr(tableName) - pm := privilege.GetPrivilegeManager(s) - if pm != nil && s.sessionVars.User != nil { - if !pm.RequestVerification(s.sessionVars.ActiveRoles, dbName.O, tName.O, "", mysql.AllPrivMask) { - user := s.sessionVars.User - u := user.Username - h := user.Hostname - if len(user.AuthUsername) > 0 && len(user.AuthHostname) > 0 { - u = user.AuthUsername - h = user.AuthHostname - } - return nil, plannercore.ErrTableaccessDenied.GenWithStackByArgs("SELECT", u, h, tableName) - } - } - table, err := is.TableByName(dbName, tName) - if err != nil { - return nil, err - } - - cols := table.Cols() - fields := make([]*ast.ResultField, 0, len(cols)) - for _, col := range table.Cols() { - rf := &ast.ResultField{ - ColumnAsName: col.Name, - TableAsName: tName, - DBName: dbName, - Table: table.Meta(), - Column: col.ColumnInfo, - } - fields = append(fields, rf) - } - return fields, nil -} - -// TxnInfo returns a pointer to a *copy* of the internal TxnInfo, thus is *read only* -func (s *session) TxnInfo() *txninfo.TxnInfo { - s.txn.mu.RLock() - // Copy on read to get a snapshot, this API shouldn't be frequently called. - txnInfo := s.txn.mu.TxnInfo - s.txn.mu.RUnlock() - - if txnInfo.StartTS == 0 { - return nil - } - - processInfo := s.ShowProcess() - if processInfo == nil { - return nil - } - txnInfo.ConnectionID = processInfo.ID - txnInfo.Username = processInfo.User - txnInfo.CurrentDB = processInfo.DB - txnInfo.RelatedTableIDs = make(map[int64]struct{}) - s.GetSessionVars().GetRelatedTableForMDL().Range(func(key, value interface{}) bool { - txnInfo.RelatedTableIDs[key.(int64)] = struct{}{} - return true - }) - - return &txnInfo -} - -func (s *session) doCommit(ctx context.Context) error { - if !s.txn.Valid() { - return nil - } - - // to avoid session set overlap the txn set. - if s.GetDiskFullOpt() != kvrpcpb.DiskFullOpt_NotAllowedOnFull { - s.txn.SetDiskFullOpt(s.GetDiskFullOpt()) - } - - defer func() { - s.txn.changeToInvalid() - s.sessionVars.SetInTxn(false) - s.ClearDiskFullOpt() - }() - // check if the transaction is read-only - if s.txn.IsReadOnly() { - return nil - } - // check if the cluster is read-only - if !s.sessionVars.InRestrictedSQL && variable.RestrictedReadOnly.Load() || variable.VarTiDBSuperReadOnly.Load() { - // It is not internal SQL, and the cluster has one of RestrictedReadOnly or SuperReadOnly - // We need to privilege check again: a privilege check occurred during planning, but we need - // to prevent the case that a long running auto-commit statement is now trying to commit. - pm := privilege.GetPrivilegeManager(s) - roles := s.sessionVars.ActiveRoles - if pm != nil && !pm.HasExplicitlyGrantedDynamicPrivilege(roles, "RESTRICTED_REPLICA_WRITER_ADMIN", false) { - s.RollbackTxn(ctx) - return plannercore.ErrSQLInReadOnlyMode - } - } - err := s.checkPlacementPolicyBeforeCommit() - if err != nil { - return err - } - // mockCommitError and mockGetTSErrorInRetry use to test PR #8743. - failpoint.Inject("mockCommitError", func(val failpoint.Value) { - if val.(bool) { - if _, err := failpoint.Eval("tikvclient/mockCommitErrorOpt"); err == nil { - failpoint.Return(kv.ErrTxnRetryable) - } - } - }) - - if s.sessionVars.BinlogClient != nil { - prewriteValue := binloginfo.GetPrewriteValue(s, false) - if prewriteValue != nil { - prewriteData, err := prewriteValue.Marshal() - if err != nil { - return errors.Trace(err) - } - info := &binloginfo.BinlogInfo{ - Data: &binlog.Binlog{ - Tp: binlog.BinlogType_Prewrite, - PrewriteValue: prewriteData, - }, - Client: s.sessionVars.BinlogClient, - } - s.txn.SetOption(kv.BinlogInfo, info) - } - } - - sessVars := s.GetSessionVars() - // Get the related table or partition IDs. - relatedPhysicalTables := sessVars.TxnCtx.TableDeltaMap - // Get accessed temporary tables in the transaction. - temporaryTables := sessVars.TxnCtx.TemporaryTables - physicalTableIDs := make([]int64, 0, len(relatedPhysicalTables)) - for id := range relatedPhysicalTables { - // Schema change on global temporary tables doesn't affect transactions. - if _, ok := temporaryTables[id]; ok { - continue - } - physicalTableIDs = append(physicalTableIDs, id) - } - needCheckSchema := true - // Set this option for 2 phase commit to validate schema lease. - if s.GetSessionVars().TxnCtx != nil { - needCheckSchema = !s.GetSessionVars().TxnCtx.EnableMDL - } - s.txn.SetOption(kv.SchemaChecker, domain.NewSchemaChecker(domain.GetDomain(s), s.GetInfoSchema().SchemaMetaVersion(), physicalTableIDs, needCheckSchema)) - s.txn.SetOption(kv.InfoSchema, s.sessionVars.TxnCtx.InfoSchema) - s.txn.SetOption(kv.CommitHook, func(info string, _ error) { s.sessionVars.LastTxnInfo = info }) - s.txn.SetOption(kv.EnableAsyncCommit, sessVars.EnableAsyncCommit) - s.txn.SetOption(kv.Enable1PC, sessVars.Enable1PC) - s.txn.SetOption(kv.ResourceGroupTagger, sessVars.StmtCtx.GetResourceGroupTagger()) - s.txn.SetOption(kv.ExplicitRequestSourceType, sessVars.ExplicitRequestSourceType) - if sessVars.StmtCtx.KvExecCounter != nil { - // Bind an interceptor for client-go to count the number of SQL executions of each TiKV. - s.txn.SetOption(kv.RPCInterceptor, sessVars.StmtCtx.KvExecCounter.RPCInterceptor()) - } - // priority of the sysvar is lower than `start transaction with causal consistency only` - if val := s.txn.GetOption(kv.GuaranteeLinearizability); val == nil || val.(bool) { - // We needn't ask the TiKV client to guarantee linearizability for auto-commit transactions - // because the property is naturally holds: - // We guarantee the commitTS of any transaction must not exceed the next timestamp from the TSO. - // An auto-commit transaction fetches its startTS from the TSO so its commitTS > its startTS > the commitTS - // of any previously committed transactions. - s.txn.SetOption(kv.GuaranteeLinearizability, - sessVars.TxnCtx.IsExplicit && sessVars.GuaranteeLinearizability) - } - if tables := sessVars.TxnCtx.TemporaryTables; len(tables) > 0 { - s.txn.SetOption(kv.KVFilter, temporaryTableKVFilter(tables)) - } - - var txnSource uint64 - if val := s.txn.GetOption(kv.TxnSource); val != nil { - txnSource, _ = val.(uint64) - } - // If the transaction is started by CDC, we need to set the CDCWriteSource option. - if sessVars.CDCWriteSource != 0 { - err := kv.SetCDCWriteSource(&txnSource, sessVars.CDCWriteSource) - if err != nil { - return errors.Trace(err) - } - - s.txn.SetOption(kv.TxnSource, txnSource) - } - - if tables := sessVars.TxnCtx.CachedTables; len(tables) > 0 { - c := cachedTableRenewLease{tables: tables} - now := time.Now() - err := c.start(ctx) - defer c.stop(ctx) - sessVars.StmtCtx.WaitLockLeaseTime += time.Since(now) - if err != nil { - return errors.Trace(err) - } - s.txn.SetOption(kv.CommitTSUpperBoundCheck, c.commitTSCheck) - } - - err = s.commitTxnWithTemporaryData(tikvutil.SetSessionID(ctx, sessVars.ConnectionID), &s.txn) - if err != nil { - err = s.handleAssertionFailure(ctx, err) - } - return err -} - -type cachedTableRenewLease struct { - tables map[int64]interface{} - lease []uint64 // Lease for each visited cached tables. - exit chan struct{} -} - -func (c *cachedTableRenewLease) start(ctx context.Context) error { - c.exit = make(chan struct{}) - c.lease = make([]uint64, len(c.tables)) - wg := make(chan error, len(c.tables)) - ith := 0 - for _, raw := range c.tables { - tbl := raw.(table.CachedTable) - go tbl.WriteLockAndKeepAlive(ctx, c.exit, &c.lease[ith], wg) - ith++ - } - - // Wait for all LockForWrite() return, this function can return. - var err error - for ; ith > 0; ith-- { - tmp := <-wg - if tmp != nil { - err = tmp - } - } - return err -} - -func (c *cachedTableRenewLease) stop(_ context.Context) { - close(c.exit) -} - -func (c *cachedTableRenewLease) commitTSCheck(commitTS uint64) bool { - for i := 0; i < len(c.lease); i++ { - lease := atomic.LoadUint64(&c.lease[i]) - if commitTS >= lease { - // Txn fails to commit because the write lease is expired. - return false - } - } - return true -} - -// handleAssertionFailure extracts the possible underlying assertionFailed error, -// gets the corresponding MVCC history and logs it. -// If it's not an assertion failure, returns the original error. -func (s *session) handleAssertionFailure(ctx context.Context, err error) error { - var assertionFailure *tikverr.ErrAssertionFailed - if !stderrs.As(err, &assertionFailure) { - return err - } - key := assertionFailure.Key - newErr := kv.ErrAssertionFailed.GenWithStackByArgs( - hex.EncodeToString(key), assertionFailure.Assertion.String(), assertionFailure.StartTs, - assertionFailure.ExistingStartTs, assertionFailure.ExistingCommitTs, - ) - - if s.GetSessionVars().EnableRedactLog { - return newErr - } - - var decodeFunc func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) - // if it's a record key or an index key, decode it - if infoSchema, ok := s.sessionVars.TxnCtx.InfoSchema.(infoschema.InfoSchema); ok && - infoSchema != nil && (tablecodec.IsRecordKey(key) || tablecodec.IsIndexKey(key)) { - tableOrPartitionID := tablecodec.DecodeTableID(key) - tbl, ok := infoSchema.TableByID(tableOrPartitionID) - if !ok { - tbl, _, _ = infoSchema.FindTableByPartitionID(tableOrPartitionID) - } - if tbl == nil { - logutil.Logger(ctx).Warn("cannot find table by id", zap.Int64("tableID", tableOrPartitionID), zap.String("key", hex.EncodeToString(key))) - return newErr - } - - if tablecodec.IsRecordKey(key) { - decodeFunc = consistency.DecodeRowMvccData(tbl.Meta()) - } else { - tableInfo := tbl.Meta() - _, indexID, _, e := tablecodec.DecodeIndexKey(key) - if e != nil { - logutil.Logger(ctx).Error("assertion failed but cannot decode index key", zap.Error(e)) - return newErr - } - var indexInfo *model.IndexInfo - for _, idx := range tableInfo.Indices { - if idx.ID == indexID { - indexInfo = idx - break - } - } - if indexInfo == nil { - return newErr - } - decodeFunc = consistency.DecodeIndexMvccData(indexInfo) - } - } - if store, ok := s.store.(helper.Storage); ok { - content := consistency.GetMvccByKey(store, key, decodeFunc) - logutil.Logger(ctx).Error("assertion failed", zap.String("message", newErr.Error()), zap.String("mvcc history", content)) - } - return newErr -} - -func (s *session) commitTxnWithTemporaryData(ctx context.Context, txn kv.Transaction) error { - sessVars := s.sessionVars - txnTempTables := sessVars.TxnCtx.TemporaryTables - if len(txnTempTables) == 0 { - failpoint.Inject("mockSleepBeforeTxnCommit", func(v failpoint.Value) { - ms := v.(int) - time.Sleep(time.Millisecond * time.Duration(ms)) - }) - return txn.Commit(ctx) - } - - sessionData := sessVars.TemporaryTableData - var ( - stage kv.StagingHandle - localTempTables *infoschema.SessionTables - ) - - if sessVars.LocalTemporaryTables != nil { - localTempTables = sessVars.LocalTemporaryTables.(*infoschema.SessionTables) - } else { - localTempTables = new(infoschema.SessionTables) - } - - defer func() { - // stage != kv.InvalidStagingHandle means error occurs, we need to cleanup sessionData - if stage != kv.InvalidStagingHandle { - sessionData.Cleanup(stage) - } - }() - - for tblID, tbl := range txnTempTables { - if !tbl.GetModified() { - continue - } - - if tbl.GetMeta().TempTableType != model.TempTableLocal { - continue - } - if _, ok := localTempTables.TableByID(tblID); !ok { - continue - } - - if stage == kv.InvalidStagingHandle { - stage = sessionData.Staging() - } - - tblPrefix := tablecodec.EncodeTablePrefix(tblID) - endKey := tablecodec.EncodeTablePrefix(tblID + 1) - - txnMemBuffer := s.txn.GetMemBuffer() - iter, err := txnMemBuffer.Iter(tblPrefix, endKey) - if err != nil { - return err - } - - for iter.Valid() { - key := iter.Key() - if !bytes.HasPrefix(key, tblPrefix) { - break - } - - value := iter.Value() - if len(value) == 0 { - err = sessionData.DeleteTableKey(tblID, key) - } else { - err = sessionData.SetTableKey(tblID, key, iter.Value()) - } - - if err != nil { - return err - } - - err = iter.Next() - if err != nil { - return err - } - } - } - - err := txn.Commit(ctx) - if err != nil { - return err - } - - if stage != kv.InvalidStagingHandle { - sessionData.Release(stage) - stage = kv.InvalidStagingHandle - } - - return nil -} - -type temporaryTableKVFilter map[int64]tableutil.TempTable - -func (m temporaryTableKVFilter) IsUnnecessaryKeyValue(key, value []byte, flags tikvstore.KeyFlags) (bool, error) { - tid := tablecodec.DecodeTableID(key) - if _, ok := m[tid]; ok { - return true, nil - } - - // This is the default filter for all tables. - defaultFilter := txn.TiDBKVFilter{} - return defaultFilter.IsUnnecessaryKeyValue(key, value, flags) -} - -// errIsNoisy is used to filter DUPLCATE KEY errors. -// These can observed by users in INFORMATION_SCHEMA.CLIENT_ERRORS_SUMMARY_GLOBAL instead. -// -// The rationale for filtering these errors is because they are "client generated errors". i.e. -// of the errors defined in kv/error.go, these look to be clearly related to a client-inflicted issue, -// and the server is only responsible for handling the error correctly. It does not need to log. -func errIsNoisy(err error) bool { - if kv.ErrKeyExists.Equal(err) { - return true - } - if storeerr.ErrLockAcquireFailAndNoWaitSet.Equal(err) { - return true - } - return false -} - -func (s *session) doCommitWithRetry(ctx context.Context) error { - defer func() { - s.GetSessionVars().SetTxnIsolationLevelOneShotStateForNextTxn() - s.txn.changeToInvalid() - s.cleanRetryInfo() - sessiontxn.GetTxnManager(s).OnTxnEnd() - }() - if !s.txn.Valid() { - // If the transaction is invalid, maybe it has already been rolled back by the client. - return nil - } - isInternalTxn := false - if internal := s.txn.GetOption(kv.RequestSourceInternal); internal != nil && internal.(bool) { - isInternalTxn = true - } - var err error - txnSize := s.txn.Size() - isPessimistic := s.txn.IsPessimistic() - r, ctx := tracing.StartRegionEx(ctx, "session.doCommitWithRetry") - defer r.End() - - err = s.doCommit(ctx) - if err != nil { - // polish the Write Conflict error message - newErr := s.tryReplaceWriteConflictError(err) - if newErr != nil { - err = newErr - } - - commitRetryLimit := s.sessionVars.RetryLimit - if !s.sessionVars.TxnCtx.CouldRetry { - commitRetryLimit = 0 - } - // Don't retry in BatchInsert mode. As a counter-example, insert into t1 select * from t2, - // BatchInsert already commit the first batch 1000 rows, then it commit 1000-2000 and retry the statement, - // Finally t1 will have more data than t2, with no errors return to user! - if s.isTxnRetryableError(err) && !s.sessionVars.BatchInsert && commitRetryLimit > 0 && !isPessimistic { - logutil.Logger(ctx).Warn("sql", - zap.String("label", s.GetSQLLabel()), - zap.Error(err), - zap.String("txn", s.txn.GoString())) - // Transactions will retry 2 ~ commitRetryLimit times. - // We make larger transactions retry less times to prevent cluster resource outage. - txnSizeRate := float64(txnSize) / float64(kv.TxnTotalSizeLimit.Load()) - maxRetryCount := commitRetryLimit - int64(float64(commitRetryLimit-1)*txnSizeRate) - err = s.retry(ctx, uint(maxRetryCount)) - } else if !errIsNoisy(err) { - logutil.Logger(ctx).Warn("can not retry txn", - zap.String("label", s.GetSQLLabel()), - zap.Error(err), - zap.Bool("IsBatchInsert", s.sessionVars.BatchInsert), - zap.Bool("IsPessimistic", isPessimistic), - zap.Bool("InRestrictedSQL", s.sessionVars.InRestrictedSQL), - zap.Int64("tidb_retry_limit", s.sessionVars.RetryLimit), - zap.Bool("tidb_disable_txn_auto_retry", s.sessionVars.DisableTxnAutoRetry)) - } - } - counter := s.sessionVars.TxnCtx.StatementCount - duration := time.Since(s.GetSessionVars().TxnCtx.CreateTime).Seconds() - s.recordOnTransactionExecution(err, counter, duration, isInternalTxn) - - if err != nil { - if !errIsNoisy(err) { - logutil.Logger(ctx).Warn("commit failed", - zap.String("finished txn", s.txn.GoString()), - zap.Error(err)) - } - return err - } - s.updateStatsDeltaToCollector() - return nil -} - -// adds more information about the table in the error message -// precondition: oldErr is a 9007:WriteConflict Error -func (s *session) tryReplaceWriteConflictError(oldErr error) (newErr error) { - if !kv.ErrWriteConflict.Equal(oldErr) { - return nil - } - if errors.RedactLogEnabled.Load() { - return nil - } - originErr := errors.Cause(oldErr) - inErr, _ := originErr.(*errors.Error) - args := inErr.Args() - is := sessiontxn.GetTxnManager(s).GetTxnInfoSchema() - if is == nil { - return nil - } - newKeyTableField, ok := addTableNameInTableIDField(args[3], is) - if ok { - args[3] = newKeyTableField - } - newPrimaryKeyTableField, ok := addTableNameInTableIDField(args[5], is) - if ok { - args[5] = newPrimaryKeyTableField - } - return kv.ErrWriteConflict.FastGenByArgs(args...) -} - -// precondition: is != nil -func addTableNameInTableIDField(tableIDField interface{}, is infoschema.InfoSchema) (enhancedMsg string, done bool) { - keyTableID, ok := tableIDField.(string) - if !ok { - return "", false - } - stringsInTableIDField := strings.Split(keyTableID, "=") - if len(stringsInTableIDField) == 0 { - return "", false - } - tableIDStr := stringsInTableIDField[len(stringsInTableIDField)-1] - tableID, err := strconv.ParseInt(tableIDStr, 10, 64) - if err != nil { - return "", false - } - var tableName string - tbl, ok := is.TableByID(tableID) - if !ok { - tableName = "unknown" - } else { - dbInfo, ok := is.SchemaByTable(tbl.Meta()) - if !ok { - tableName = "unknown." + tbl.Meta().Name.String() - } else { - tableName = dbInfo.Name.String() + "." + tbl.Meta().Name.String() - } - } - enhancedMsg = keyTableID + ", tableName=" + tableName - return enhancedMsg, true -} - -func (s *session) updateStatsDeltaToCollector() { - mapper := s.GetSessionVars().TxnCtx.TableDeltaMap - if s.statsCollector != nil && mapper != nil { - for _, item := range mapper { - if item.TableID > 0 { - s.statsCollector.Update(item.TableID, item.Delta, item.Count, &item.ColSize) - } - } - } -} - -func (s *session) CommitTxn(ctx context.Context) error { - r, ctx := tracing.StartRegionEx(ctx, "session.CommitTxn") - defer r.End() - - var commitDetail *tikvutil.CommitDetails - ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) - err := s.doCommitWithRetry(ctx) - if commitDetail != nil { - s.sessionVars.StmtCtx.MergeExecDetails(nil, commitDetail) - } - - // record the TTLInsertRows in the metric - metrics.TTLInsertRowsCount.Add(float64(s.sessionVars.TxnCtx.InsertTTLRowsCount)) - - failpoint.Inject("keepHistory", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(err) - } - }) - s.sessionVars.TxnCtx.Cleanup() - s.sessionVars.CleanupTxnReadTSIfUsed() - return err -} - -func (s *session) RollbackTxn(ctx context.Context) { - r, ctx := tracing.StartRegionEx(ctx, "session.RollbackTxn") - defer r.End() - - if s.txn.Valid() { - terror.Log(s.txn.Rollback()) - } - if ctx.Value(inCloseSession{}) == nil { - s.cleanRetryInfo() - } - s.txn.changeToInvalid() - s.sessionVars.TxnCtx.Cleanup() - s.sessionVars.CleanupTxnReadTSIfUsed() - s.sessionVars.SetInTxn(false) - sessiontxn.GetTxnManager(s).OnTxnEnd() -} - -func (s *session) GetClient() kv.Client { - return s.client -} - -func (s *session) GetMPPClient() kv.MPPClient { - return s.mppClient -} - -func (s *session) String() string { - // TODO: how to print binded context in values appropriately? - sessVars := s.sessionVars - data := map[string]interface{}{ - "id": sessVars.ConnectionID, - "user": sessVars.User, - "currDBName": sessVars.CurrentDB, - "status": sessVars.Status, - "strictMode": sessVars.StrictSQLMode, - } - if s.txn.Valid() { - // if txn is committed or rolled back, txn is nil. - data["txn"] = s.txn.String() - } - if sessVars.SnapshotTS != 0 { - data["snapshotTS"] = sessVars.SnapshotTS - } - if sessVars.StmtCtx.LastInsertID > 0 { - data["lastInsertID"] = sessVars.StmtCtx.LastInsertID - } - if len(sessVars.PreparedStmts) > 0 { - data["preparedStmtCount"] = len(sessVars.PreparedStmts) - } - b, err := json.MarshalIndent(data, "", " ") - terror.Log(errors.Trace(err)) - return string(b) -} - -const sqlLogMaxLen = 1024 - -// SchemaChangedWithoutRetry is used for testing. -var SchemaChangedWithoutRetry uint32 - -func (s *session) GetSQLLabel() string { - if s.sessionVars.InRestrictedSQL { - return metrics.LblInternal - } - return metrics.LblGeneral -} - -func (s *session) isInternal() bool { - return s.sessionVars.InRestrictedSQL -} - -func (*session) isTxnRetryableError(err error) bool { - if atomic.LoadUint32(&SchemaChangedWithoutRetry) == 1 { - return kv.IsTxnRetryableError(err) - } - return kv.IsTxnRetryableError(err) || domain.ErrInfoSchemaChanged.Equal(err) -} - -func (s *session) checkTxnAborted(stmt sqlexec.Statement) error { - var err error - if atomic.LoadUint32(&s.GetSessionVars().TxnCtx.LockExpire) == 0 { - return nil - } - err = kv.ErrLockExpire - // If the transaction is aborted, the following statements do not need to execute, except `commit` and `rollback`, - // because they are used to finish the aborted transaction. - if _, ok := stmt.(*executor.ExecStmt).StmtNode.(*ast.CommitStmt); ok { - return nil - } - if _, ok := stmt.(*executor.ExecStmt).StmtNode.(*ast.RollbackStmt); ok { - return nil - } - return err -} - -func (s *session) retry(ctx context.Context, maxCnt uint) (err error) { - var retryCnt uint - defer func() { - s.sessionVars.RetryInfo.Retrying = false - // retryCnt only increments on retryable error, so +1 here. - if s.sessionVars.InRestrictedSQL { - session_metrics.TransactionRetryInternal.Observe(float64(retryCnt + 1)) - } else { - session_metrics.TransactionRetryGeneral.Observe(float64(retryCnt + 1)) - } - s.sessionVars.SetInTxn(false) - if err != nil { - s.RollbackTxn(ctx) - } - s.txn.changeToInvalid() - }() - - connID := s.sessionVars.ConnectionID - s.sessionVars.RetryInfo.Retrying = true - if atomic.LoadUint32(&s.sessionVars.TxnCtx.ForUpdate) == 1 { - err = ErrForUpdateCantRetry.GenWithStackByArgs(connID) - return err - } - - nh := GetHistory(s) - var schemaVersion int64 - sessVars := s.GetSessionVars() - orgStartTS := sessVars.TxnCtx.StartTS - label := s.GetSQLLabel() - for { - if err = s.PrepareTxnCtx(ctx); err != nil { - return err - } - s.sessionVars.RetryInfo.ResetOffset() - for i, sr := range nh.history { - st := sr.st - s.sessionVars.StmtCtx = sr.stmtCtx - s.sessionVars.StmtCtx.ResetForRetry() - s.sessionVars.PlanCacheParams.Reset() - schemaVersion, err = st.RebuildPlan(ctx) - if err != nil { - return err - } - - if retryCnt == 0 { - // We do not have to log the query every time. - // We print the queries at the first try only. - sql := sqlForLog(st.GetTextToLog(false)) - if !sessVars.EnableRedactLog { - sql += sessVars.PlanCacheParams.String() - } - logutil.Logger(ctx).Warn("retrying", - zap.Int64("schemaVersion", schemaVersion), - zap.Uint("retryCnt", retryCnt), - zap.Int("queryNum", i), - zap.String("sql", sql)) - } else { - logutil.Logger(ctx).Warn("retrying", - zap.Int64("schemaVersion", schemaVersion), - zap.Uint("retryCnt", retryCnt), - zap.Int("queryNum", i)) - } - _, digest := s.sessionVars.StmtCtx.SQLDigest() - s.txn.onStmtStart(digest.String()) - if err = sessiontxn.GetTxnManager(s).OnStmtStart(ctx, st.GetStmtNode()); err == nil { - _, err = st.Exec(ctx) - } - s.txn.onStmtEnd() - if err != nil { - s.StmtRollback(ctx, false) - break - } - s.StmtCommit(ctx) - } - logutil.Logger(ctx).Warn("transaction association", - zap.Uint64("retrying txnStartTS", s.GetSessionVars().TxnCtx.StartTS), - zap.Uint64("original txnStartTS", orgStartTS)) - failpoint.Inject("preCommitHook", func() { - hook, ok := ctx.Value("__preCommitHook").(func()) - if ok { - hook() - } - }) - if err == nil { - err = s.doCommit(ctx) - if err == nil { - break - } - } - if !s.isTxnRetryableError(err) { - logutil.Logger(ctx).Warn("sql", - zap.String("label", label), - zap.Stringer("session", s), - zap.Error(err)) - metrics.SessionRetryErrorCounter.WithLabelValues(label, metrics.LblUnretryable).Inc() - return err - } - retryCnt++ - if retryCnt >= maxCnt { - logutil.Logger(ctx).Warn("sql", - zap.String("label", label), - zap.Uint("retry reached max count", retryCnt)) - metrics.SessionRetryErrorCounter.WithLabelValues(label, metrics.LblReachMax).Inc() - return err - } - logutil.Logger(ctx).Warn("sql", - zap.String("label", label), - zap.Error(err), - zap.String("txn", s.txn.GoString())) - kv.BackOff(retryCnt) - s.txn.changeToInvalid() - s.sessionVars.SetInTxn(false) - } - return err -} - -func sqlForLog(sql string) string { - if len(sql) > sqlLogMaxLen { - sql = sql[:sqlLogMaxLen] + fmt.Sprintf("(len:%d)", len(sql)) - } - return executor.QueryReplacer.Replace(sql) -} - -type sessionPool interface { - Get() (pools.Resource, error) - Put(pools.Resource) -} - -func (s *session) sysSessionPool() sessionPool { - return domain.GetDomain(s).SysSessionPool() -} - -func createSessionFunc(store kv.Storage) pools.Factory { - return func() (pools.Resource, error) { - se, err := createSession(store) - if err != nil { - return nil, err - } - err = se.sessionVars.SetSystemVar(variable.AutoCommit, "1") - if err != nil { - return nil, err - } - err = se.sessionVars.SetSystemVar(variable.MaxExecutionTime, "0") - if err != nil { - return nil, errors.Trace(err) - } - err = se.sessionVars.SetSystemVar(variable.MaxAllowedPacket, strconv.FormatUint(variable.DefMaxAllowedPacket, 10)) - if err != nil { - return nil, errors.Trace(err) - } - err = se.sessionVars.SetSystemVar(variable.TiDBEnableWindowFunction, variable.BoolToOnOff(variable.DefEnableWindowFunction)) - if err != nil { - return nil, errors.Trace(err) - } - err = se.sessionVars.SetSystemVar(variable.TiDBConstraintCheckInPlacePessimistic, variable.On) - if err != nil { - return nil, errors.Trace(err) - } - se.sessionVars.CommonGlobalLoaded = true - se.sessionVars.InRestrictedSQL = true - // Internal session uses default format to prevent memory leak problem. - se.sessionVars.EnableChunkRPC = false - return se, nil - } -} - -func createSessionWithDomainFunc(store kv.Storage) func(*domain.Domain) (pools.Resource, error) { - return func(dom *domain.Domain) (pools.Resource, error) { - se, err := CreateSessionWithDomain(store, dom) - if err != nil { - return nil, err - } - err = se.sessionVars.SetSystemVar(variable.AutoCommit, "1") - if err != nil { - return nil, err - } - err = se.sessionVars.SetSystemVar(variable.MaxExecutionTime, "0") - if err != nil { - return nil, errors.Trace(err) - } - err = se.sessionVars.SetSystemVar(variable.MaxAllowedPacket, strconv.FormatUint(variable.DefMaxAllowedPacket, 10)) - if err != nil { - return nil, errors.Trace(err) - } - err = se.sessionVars.SetSystemVar(variable.TiDBConstraintCheckInPlacePessimistic, variable.On) - if err != nil { - return nil, errors.Trace(err) - } - se.sessionVars.CommonGlobalLoaded = true - se.sessionVars.InRestrictedSQL = true - // Internal session uses default format to prevent memory leak problem. - se.sessionVars.EnableChunkRPC = false - return se, nil - } -} - -func drainRecordSet(ctx context.Context, se *session, rs sqlexec.RecordSet, alloc chunk.Allocator) ([]chunk.Row, error) { - var rows []chunk.Row - var req *chunk.Chunk - req = rs.NewChunk(alloc) - for { - err := rs.Next(ctx, req) - if err != nil || req.NumRows() == 0 { - return rows, err - } - iter := chunk.NewIterator4Chunk(req) - for r := iter.Begin(); r != iter.End(); r = iter.Next() { - rows = append(rows, r) - } - req = chunk.Renew(req, se.sessionVars.MaxChunkSize) - } -} - -// getTableValue executes restricted sql and the result is one column. -// It returns a string value. -func (s *session) getTableValue(ctx context.Context, tblName string, varName string) (string, error) { - if ctx.Value(kv.RequestSourceKey) == nil { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnSysVar) - } - rows, fields, err := s.ExecRestrictedSQL(ctx, nil, "SELECT VARIABLE_VALUE FROM %n.%n WHERE VARIABLE_NAME=%?", mysql.SystemDB, tblName, varName) - if err != nil { - return "", err - } - if len(rows) == 0 { - return "", errResultIsEmpty - } - d := rows[0].GetDatum(0, &fields[0].Column.FieldType) - value, err := d.ToString() - if err != nil { - return "", err - } - return value, nil -} - -// replaceGlobalVariablesTableValue executes restricted sql updates the variable value -// It will then notify the etcd channel that the value has changed. -func (s *session) replaceGlobalVariablesTableValue(ctx context.Context, varName, val string, updateLocal bool) error { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnSysVar) - _, _, err := s.ExecRestrictedSQL(ctx, nil, `REPLACE INTO %n.%n (variable_name, variable_value) VALUES (%?, %?)`, mysql.SystemDB, mysql.GlobalVariablesTable, varName, val) - if err != nil { - return err - } - domain.GetDomain(s).NotifyUpdateSysVarCache(updateLocal) - return err -} - -// GetGlobalSysVar implements GlobalVarAccessor.GetGlobalSysVar interface. -func (s *session) GetGlobalSysVar(name string) (string, error) { - if s.Value(sessionctx.Initing) != nil { - // When running bootstrap or upgrade, we should not access global storage. - return "", nil - } - - sv := variable.GetSysVar(name) - if sv == nil { - // It might be a recently unregistered sysvar. We should return unknown - // since GetSysVar is the canonical version, but we can update the cache - // so the next request doesn't attempt to load this. - logutil.BgLogger().Info("sysvar does not exist. sysvar cache may be stale", zap.String("name", name)) - return "", variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - - sysVar, err := domain.GetDomain(s).GetGlobalVar(name) - if err != nil { - // The sysvar exists, but there is no cache entry yet. - // This might be because the sysvar was only recently registered. - // In which case it is safe to return the default, but we can also - // update the cache for the future. - logutil.BgLogger().Info("sysvar not in cache yet. sysvar cache may be stale", zap.String("name", name)) - sysVar, err = s.getTableValue(context.TODO(), mysql.GlobalVariablesTable, name) - if err != nil { - return sv.Value, nil - } - } - // It might have been written from an earlier TiDB version, so we should do type validation - // See https://github.com/pingcap/tidb/issues/30255 for why we don't do full validation. - // If validation fails, we should return the default value: - // See: https://github.com/pingcap/tidb/pull/31566 - sysVar, err = sv.ValidateFromType(s.GetSessionVars(), sysVar, variable.ScopeGlobal) - if err != nil { - return sv.Value, nil - } - return sysVar, nil -} - -// SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface. -// it is called (but skipped) when setting instance scope -func (s *session) SetGlobalSysVar(ctx context.Context, name string, value string) (err error) { - sv := variable.GetSysVar(name) - if sv == nil { - return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - if value, err = sv.Validate(s.sessionVars, value, variable.ScopeGlobal); err != nil { - return err - } - if err = sv.SetGlobalFromHook(ctx, s.sessionVars, value, false); err != nil { - return err - } - if sv.HasInstanceScope() { // skip for INSTANCE scope - return nil - } - if sv.GlobalConfigName != "" { - domain.GetDomain(s).NotifyGlobalConfigChange(sv.GlobalConfigName, variable.OnOffToTrueFalse(value)) - } - return s.replaceGlobalVariablesTableValue(context.TODO(), sv.Name, value, true) -} - -// SetGlobalSysVarOnly updates the sysvar, but does not call the validation function or update aliases. -// This is helpful to prevent duplicate warnings being appended from aliases, or recursion. -// updateLocal indicates whether to rebuild the local SysVar Cache. This is helpful to prevent recursion. -func (s *session) SetGlobalSysVarOnly(ctx context.Context, name string, value string, updateLocal bool) (err error) { - sv := variable.GetSysVar(name) - if sv == nil { - return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - if err = sv.SetGlobalFromHook(ctx, s.sessionVars, value, true); err != nil { - return err - } - if sv.HasInstanceScope() { // skip for INSTANCE scope - return nil - } - return s.replaceGlobalVariablesTableValue(ctx, sv.Name, value, updateLocal) -} - -// SetTiDBTableValue implements GlobalVarAccessor.SetTiDBTableValue interface. -func (s *session) SetTiDBTableValue(name, value, comment string) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnSysVar) - _, _, err := s.ExecRestrictedSQL(ctx, nil, `REPLACE INTO mysql.tidb (variable_name, variable_value, comment) VALUES (%?, %?, %?)`, name, value, comment) - return err -} - -// GetTiDBTableValue implements GlobalVarAccessor.GetTiDBTableValue interface. -func (s *session) GetTiDBTableValue(name string) (string, error) { - return s.getTableValue(context.TODO(), mysql.TiDBTable, name) -} - -var _ sqlexec.SQLParser = &session{} - -func (s *session) ParseSQL(ctx context.Context, sql string, params ...parser.ParseParam) ([]ast.StmtNode, []error, error) { - defer tracing.StartRegion(ctx, "ParseSQL").End() - - p := parserPool.Get().(*parser.Parser) - defer parserPool.Put(p) - - sqlMode := s.sessionVars.SQLMode - if s.isInternal() { - sqlMode = mysql.DelSQLMode(sqlMode, mysql.ModeNoBackslashEscapes) - } - p.SetSQLMode(sqlMode) - p.SetParserConfig(s.sessionVars.BuildParserConfig()) - tmp, warn, err := p.ParseSQL(sql, params...) - // The []ast.StmtNode is referenced by the parser, to reuse the parser, make a copy of the result. - res := make([]ast.StmtNode, len(tmp)) - copy(res, tmp) - return res, warn, err -} - -func (s *session) SetProcessInfo(sql string, t time.Time, command byte, maxExecutionTime uint64) { - // If command == mysql.ComSleep, it means the SQL execution is finished. The processinfo is reset to SLEEP. - // If the SQL finished and the session is not in transaction, the current start timestamp need to reset to 0. - // Otherwise, it should be set to the transaction start timestamp. - // Why not reset the transaction start timestamp to 0 when transaction committed? - // Because the select statement and other statements need this timestamp to read data, - // after the transaction is committed. e.g. SHOW MASTER STATUS; - var curTxnStartTS uint64 - var curTxnCreateTime time.Time - if command != mysql.ComSleep || s.GetSessionVars().InTxn() { - curTxnStartTS = s.sessionVars.TxnCtx.StartTS - curTxnCreateTime = s.sessionVars.TxnCtx.CreateTime - } - // Set curTxnStartTS to SnapshotTS directly when the session is trying to historic read. - // It will avoid the session meet GC lifetime too short error. - if s.GetSessionVars().SnapshotTS != 0 { - curTxnStartTS = s.GetSessionVars().SnapshotTS - } - p := s.currentPlan - if explain, ok := p.(*plannercore.Explain); ok && explain.Analyze && explain.TargetPlan != nil { - p = explain.TargetPlan - } - - pi := util.ProcessInfo{ - ID: s.sessionVars.ConnectionID, - Port: s.sessionVars.Port, - DB: s.sessionVars.CurrentDB, - Command: command, - Plan: p, - PlanExplainRows: plannercore.GetExplainRowsForPlan(p), - RuntimeStatsColl: s.sessionVars.StmtCtx.RuntimeStatsColl, - Time: t, - State: s.Status(), - Info: sql, - CurTxnStartTS: curTxnStartTS, - CurTxnCreateTime: curTxnCreateTime, - StmtCtx: s.sessionVars.StmtCtx, - RefCountOfStmtCtx: &s.sessionVars.RefCountOfStmtCtx, - MemTracker: s.sessionVars.MemTracker, - DiskTracker: s.sessionVars.DiskTracker, - StatsInfo: plannercore.GetStatsInfo, - OOMAlarmVariablesInfo: s.getOomAlarmVariablesInfo(), - TableIDs: s.sessionVars.StmtCtx.TableIDs, - IndexNames: s.sessionVars.StmtCtx.IndexNames, - MaxExecutionTime: maxExecutionTime, - RedactSQL: s.sessionVars.EnableRedactLog, - ResourceGroupName: s.sessionVars.ResourceGroupName, - SessionAlias: s.sessionVars.SessionAlias, - } - oldPi := s.ShowProcess() - if p == nil { - // Store the last valid plan when the current plan is nil. - // This is for `explain for connection` statement has the ability to query the last valid plan. - if oldPi != nil && oldPi.Plan != nil && len(oldPi.PlanExplainRows) > 0 { - pi.Plan = oldPi.Plan - pi.PlanExplainRows = oldPi.PlanExplainRows - pi.RuntimeStatsColl = oldPi.RuntimeStatsColl - } - } - // We set process info before building plan, so we extended execution time. - if oldPi != nil && oldPi.Info == pi.Info && oldPi.Command == pi.Command { - pi.Time = oldPi.Time - } - if oldPi != nil && oldPi.CurTxnStartTS != 0 && oldPi.CurTxnStartTS == pi.CurTxnStartTS { - // Keep the last expensive txn log time, avoid print too many expensive txn logs. - pi.ExpensiveTxnLogTime = oldPi.ExpensiveTxnLogTime - } - _, digest := s.sessionVars.StmtCtx.SQLDigest() - pi.Digest = digest.String() - // DO NOT reset the currentPlan to nil until this query finishes execution, otherwise reentrant calls - // of SetProcessInfo would override Plan and PlanExplainRows to nil. - if command == mysql.ComSleep { - s.currentPlan = nil - } - if s.sessionVars.User != nil { - pi.User = s.sessionVars.User.Username - pi.Host = s.sessionVars.User.Hostname - } - s.processInfo.Store(&pi) -} - -// UpdateProcessInfo updates the session's process info for the running statement. -func (s *session) UpdateProcessInfo() { - pi := s.ShowProcess() - if pi == nil || pi.CurTxnStartTS != 0 { - return - } - // Update the current transaction start timestamp. - pi.CurTxnStartTS = s.sessionVars.TxnCtx.StartTS - pi.CurTxnCreateTime = s.sessionVars.TxnCtx.CreateTime -} - -func (s *session) getOomAlarmVariablesInfo() util.OOMAlarmVariablesInfo { - return util.OOMAlarmVariablesInfo{ - SessionAnalyzeVersion: s.sessionVars.AnalyzeVersion, - SessionEnabledRateLimitAction: s.sessionVars.EnabledRateLimitAction, - SessionMemQuotaQuery: s.sessionVars.MemQuotaQuery, - } -} - -func (s *session) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { - s.diskFullOpt = level -} - -func (s *session) GetDiskFullOpt() kvrpcpb.DiskFullOpt { - return s.diskFullOpt -} - -func (s *session) ClearDiskFullOpt() { - s.diskFullOpt = kvrpcpb.DiskFullOpt_NotAllowedOnFull -} - -func (s *session) ExecuteInternal(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, err error) { - origin := s.sessionVars.InRestrictedSQL - s.sessionVars.InRestrictedSQL = true - defer func() { - s.sessionVars.InRestrictedSQL = origin - // Restore the goroutine label by using the original ctx after execution is finished. - pprof.SetGoroutineLabels(ctx) - }() - - r, ctx := tracing.StartRegionEx(ctx, "session.ExecuteInternal") - defer r.End() - logutil.Eventf(ctx, "execute: %s", sql) - - stmtNode, err := s.ParseWithParams(ctx, sql, args...) - if err != nil { - return nil, err - } - - rs, err = s.ExecuteStmt(ctx, stmtNode) - if err != nil { - s.sessionVars.StmtCtx.AppendError(err) - } - if rs == nil { - return nil, err - } - - return rs, err -} - -// Execute is deprecated, we can remove it as soon as plugins are migrated. -func (s *session) Execute(ctx context.Context, sql string) (recordSets []sqlexec.RecordSet, err error) { - r, ctx := tracing.StartRegionEx(ctx, "session.Execute") - defer r.End() - logutil.Eventf(ctx, "execute: %s", sql) - - stmtNodes, err := s.Parse(ctx, sql) - if err != nil { - return nil, err - } - if len(stmtNodes) != 1 { - return nil, errors.New("Execute() API doesn't support multiple statements any more") - } - - rs, err := s.ExecuteStmt(ctx, stmtNodes[0]) - if err != nil { - s.sessionVars.StmtCtx.AppendError(err) - } - if rs == nil { - return nil, err - } - return []sqlexec.RecordSet{rs}, err -} - -// Parse parses a query string to raw ast.StmtNode. -func (s *session) Parse(ctx context.Context, sql string) ([]ast.StmtNode, error) { - logutil.Logger(ctx).Debug("parse", zap.String("sql", sql)) - parseStartTime := time.Now() - stmts, warns, err := s.ParseSQL(ctx, sql, s.sessionVars.GetParseParams()...) - if err != nil { - s.rollbackOnError(ctx) - err = util.SyntaxError(err) - - // Only print log message when this SQL is from the user. - // Mute the warning for internal SQLs. - if !s.sessionVars.InRestrictedSQL { - if s.sessionVars.EnableRedactLog { - logutil.Logger(ctx).Debug("parse SQL failed", zap.Error(err), zap.String("SQL", sql)) - } else { - logutil.Logger(ctx).Warn("parse SQL failed", zap.Error(err), zap.String("SQL", sql)) - } - s.sessionVars.StmtCtx.AppendError(err) - } - return nil, err - } - - durParse := time.Since(parseStartTime) - s.GetSessionVars().DurationParse = durParse - isInternal := s.isInternal() - if isInternal { - session_metrics.SessionExecuteParseDurationInternal.Observe(durParse.Seconds()) - } else { - session_metrics.SessionExecuteParseDurationGeneral.Observe(durParse.Seconds()) - } - for _, warn := range warns { - s.sessionVars.StmtCtx.AppendWarning(util.SyntaxWarn(warn)) - } - return stmts, nil -} - -// ParseWithParams parses a query string, with arguments, to raw ast.StmtNode. -// Note that it will not do escaping if no variable arguments are passed. -func (s *session) ParseWithParams(ctx context.Context, sql string, args ...interface{}) (ast.StmtNode, error) { - var err error - if len(args) > 0 { - sql, err = sqlexec.EscapeSQL(sql, args...) - if err != nil { - return nil, err - } - } - - internal := s.isInternal() - - var stmts []ast.StmtNode - var warns []error - parseStartTime := time.Now() - if internal { - // Do no respect the settings from clients, if it is for internal usage. - // Charsets from clients may give chance injections. - // Refer to https://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string/12118602. - stmts, warns, err = s.ParseSQL(ctx, sql) - } else { - stmts, warns, err = s.ParseSQL(ctx, sql, s.sessionVars.GetParseParams()...) - } - if len(stmts) != 1 && err == nil { - err = errors.New("run multiple statements internally is not supported") - } - if err != nil { - s.rollbackOnError(ctx) - logSQL := sql[:mathutil.Min(500, len(sql))] - if s.sessionVars.EnableRedactLog { - logutil.Logger(ctx).Debug("parse SQL failed", zap.Error(err), zap.String("SQL", logSQL)) - } else { - logutil.Logger(ctx).Warn("parse SQL failed", zap.Error(err), zap.String("SQL", logSQL)) - } - return nil, util.SyntaxError(err) - } - durParse := time.Since(parseStartTime) - if internal { - session_metrics.SessionExecuteParseDurationInternal.Observe(durParse.Seconds()) - } else { - session_metrics.SessionExecuteParseDurationGeneral.Observe(durParse.Seconds()) - } - for _, warn := range warns { - s.sessionVars.StmtCtx.AppendWarning(util.SyntaxWarn(warn)) - } - if topsqlstate.TopSQLEnabled() { - normalized, digest := parser.NormalizeDigest(sql) - if digest != nil { - // Reset the goroutine label when internal sql execute finish. - // Specifically reset in ExecRestrictedStmt function. - s.sessionVars.StmtCtx.IsSQLRegistered.Store(true) - topsql.AttachAndRegisterSQLInfo(ctx, normalized, digest, s.sessionVars.InRestrictedSQL) - } - } - return stmts[0], nil -} - -// GetAdvisoryLock acquires an advisory lock of lockName. -// Note that a lock can be acquired multiple times by the same session, -// in which case we increment a reference count. -// Each lock needs to be held in a unique session because -// we need to be able to ROLLBACK in any arbitrary order -// in order to release the locks. -func (s *session) GetAdvisoryLock(lockName string, timeout int64) error { - if lock, ok := s.advisoryLocks[lockName]; ok { - lock.IncrReferences() - return nil - } - sess, err := createSession(s.store) - if err != nil { - return err - } - infosync.StoreInternalSession(sess) - lock := &advisoryLock{session: sess, ctx: context.TODO(), owner: s.ShowProcess().ID} - err = lock.GetLock(lockName, timeout) - if err != nil { - return err - } - s.advisoryLocks[lockName] = lock - return nil -} - -// IsUsedAdvisoryLock checks if a lockName is already in use -func (s *session) IsUsedAdvisoryLock(lockName string) uint64 { - // Same session - if lock, ok := s.advisoryLocks[lockName]; ok { - return lock.owner - } - - // Check for transaction on advisory_locks table - sess, err := createSession(s.store) - if err != nil { - return 0 - } - lock := &advisoryLock{session: sess, ctx: context.TODO(), owner: s.ShowProcess().ID} - err = lock.IsUsedLock(lockName) - if err != nil { - // TODO: Return actual owner pid - // TODO: Check for mysql.ErrLockWaitTimeout and DeadLock - return 1 - } - return 0 -} - -// ReleaseAdvisoryLock releases an advisory locks held by the session. -// It returns FALSE if no lock by this name was held (by this session), -// and TRUE if a lock was held and "released". -// Note that the lock is not actually released if there are multiple -// references to the same lockName by the session, instead the reference -// count is decremented. -func (s *session) ReleaseAdvisoryLock(lockName string) (released bool) { - if lock, ok := s.advisoryLocks[lockName]; ok { - lock.DecrReferences() - if lock.ReferenceCount() <= 0 { - lock.Close() - delete(s.advisoryLocks, lockName) - infosync.DeleteInternalSession(lock.session) - } - return true - } - return false -} - -// ReleaseAllAdvisoryLocks releases all advisory locks held by the session -// and returns a count of the locks that were released. -// The count is based on unique locks held, so multiple references -// to the same lock do not need to be accounted for. -func (s *session) ReleaseAllAdvisoryLocks() int { - var count int - for lockName, lock := range s.advisoryLocks { - lock.Close() - count += lock.ReferenceCount() - delete(s.advisoryLocks, lockName) - infosync.DeleteInternalSession(lock.session) - } - return count -} - -// GetExtensions returns the `*extension.SessionExtensions` object -func (s *session) GetExtensions() *extension.SessionExtensions { - return s.extensions -} - -// SetExtensions sets the `*extension.SessionExtensions` object -func (s *session) SetExtensions(extensions *extension.SessionExtensions) { - s.extensions = extensions -} - -// InSandBoxMode indicates that this session is in sandbox mode -func (s *session) InSandBoxMode() bool { - return s.sandBoxMode -} - -// EnableSandBoxMode enable the sandbox mode. -func (s *session) EnableSandBoxMode() { - s.sandBoxMode = true -} - -// DisableSandBoxMode enable the sandbox mode. -func (s *session) DisableSandBoxMode() { - s.sandBoxMode = false -} - -// ParseWithParams4Test wrapper (s *session) ParseWithParams for test -func ParseWithParams4Test(ctx context.Context, s Session, - sql string, args ...interface{}) (ast.StmtNode, error) { - return s.(*session).ParseWithParams(ctx, sql, args) -} - -var _ sqlexec.RestrictedSQLExecutor = &session{} -var _ sqlexec.SQLExecutor = &session{} - -// ExecRestrictedStmt implements RestrictedSQLExecutor interface. -func (s *session) ExecRestrictedStmt(ctx context.Context, stmtNode ast.StmtNode, opts ...sqlexec.OptionFuncAlias) ( - []chunk.Row, []*ast.ResultField, error) { - defer pprof.SetGoroutineLabels(ctx) - execOption := sqlexec.GetExecOption(opts) - var se *session - var clean func() - var err error - if execOption.UseCurSession { - se, clean, err = s.useCurrentSession(execOption) - } else { - se, clean, err = s.getInternalSession(execOption) - } - if err != nil { - return nil, nil, err - } - defer clean() - - startTime := time.Now() - metrics.SessionRestrictedSQLCounter.Inc() - ctx = context.WithValue(ctx, execdetails.StmtExecDetailKey, &execdetails.StmtExecDetails{}) - ctx = context.WithValue(ctx, tikvutil.ExecDetailsKey, &tikvutil.ExecDetails{}) - rs, err := se.ExecuteStmt(ctx, stmtNode) - if err != nil { - se.sessionVars.StmtCtx.AppendError(err) - } - if rs == nil { - return nil, nil, err - } - defer func() { - if closeErr := rs.Close(); closeErr != nil { - err = closeErr - } - }() - var rows []chunk.Row - rows, err = drainRecordSet(ctx, se, rs, nil) - if err != nil { - return nil, nil, err - } - - vars := se.GetSessionVars() - for _, dbName := range GetDBNames(vars) { - metrics.QueryDurationHistogram.WithLabelValues(metrics.LblInternal, dbName, vars.ResourceGroupName).Observe(time.Since(startTime).Seconds()) - } - return rows, rs.Fields(), err -} - -// ExecRestrictedStmt4Test wrapper `(s *session) ExecRestrictedStmt` for test. -func ExecRestrictedStmt4Test(ctx context.Context, s Session, - stmtNode ast.StmtNode, opts ...sqlexec.OptionFuncAlias) ( - []chunk.Row, []*ast.ResultField, error) { - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnOthers) - return s.(*session).ExecRestrictedStmt(ctx, stmtNode, opts...) -} - -// only set and clean session with execOption -func (s *session) useCurrentSession(execOption sqlexec.ExecOption) (*session, func(), error) { - var err error - orgSnapshotInfoSchema, orgSnapshotTS := s.sessionVars.SnapshotInfoschema, s.sessionVars.SnapshotTS - if execOption.SnapshotTS != 0 { - if err = s.sessionVars.SetSystemVar(variable.TiDBSnapshot, strconv.FormatUint(execOption.SnapshotTS, 10)); err != nil { - return nil, nil, err - } - s.sessionVars.SnapshotInfoschema, err = getSnapshotInfoSchema(s, execOption.SnapshotTS) - if err != nil { - return nil, nil, err - } - } - prevStatsVer := s.sessionVars.AnalyzeVersion - if execOption.AnalyzeVer != 0 { - s.sessionVars.AnalyzeVersion = execOption.AnalyzeVer - } - prevAnalyzeSnapshot := s.sessionVars.EnableAnalyzeSnapshot - if execOption.AnalyzeSnapshot != nil { - s.sessionVars.EnableAnalyzeSnapshot = *execOption.AnalyzeSnapshot - } - prePruneMode := s.sessionVars.PartitionPruneMode.Load() - if len(execOption.PartitionPruneMode) > 0 { - s.sessionVars.PartitionPruneMode.Store(execOption.PartitionPruneMode) - } - prevSQL := s.sessionVars.StmtCtx.OriginalSQL - prevStmtType := s.sessionVars.StmtCtx.StmtType - prevTables := s.sessionVars.StmtCtx.Tables - return s, func() { - s.sessionVars.AnalyzeVersion = prevStatsVer - s.sessionVars.EnableAnalyzeSnapshot = prevAnalyzeSnapshot - if err := s.sessionVars.SetSystemVar(variable.TiDBSnapshot, ""); err != nil { - logutil.BgLogger().Error("set tidbSnapshot error", zap.Error(err)) - } - s.sessionVars.SnapshotInfoschema = orgSnapshotInfoSchema - s.sessionVars.SnapshotTS = orgSnapshotTS - s.sessionVars.PartitionPruneMode.Store(prePruneMode) - s.sessionVars.StmtCtx.OriginalSQL = prevSQL - s.sessionVars.StmtCtx.StmtType = prevStmtType - s.sessionVars.StmtCtx.Tables = prevTables - s.sessionVars.MemTracker.Detach() - }, nil -} - -func (s *session) getInternalSession(execOption sqlexec.ExecOption) (*session, func(), error) { - tmp, err := s.sysSessionPool().Get() - if err != nil { - return nil, nil, errors.Trace(err) - } - se := tmp.(*session) - - // The special session will share the `InspectionTableCache` with current session - // if the current session in inspection mode. - if cache := s.sessionVars.InspectionTableCache; cache != nil { - se.sessionVars.InspectionTableCache = cache - } - if ok := s.sessionVars.OptimizerUseInvisibleIndexes; ok { - se.sessionVars.OptimizerUseInvisibleIndexes = true - } - - if execOption.SnapshotTS != 0 { - if err := se.sessionVars.SetSystemVar(variable.TiDBSnapshot, strconv.FormatUint(execOption.SnapshotTS, 10)); err != nil { - return nil, nil, err - } - se.sessionVars.SnapshotInfoschema, err = getSnapshotInfoSchema(s, execOption.SnapshotTS) - if err != nil { - return nil, nil, err - } - } - - prevStatsVer := se.sessionVars.AnalyzeVersion - if execOption.AnalyzeVer != 0 { - se.sessionVars.AnalyzeVersion = execOption.AnalyzeVer - } - - prevAnalyzeSnapshot := se.sessionVars.EnableAnalyzeSnapshot - if execOption.AnalyzeSnapshot != nil { - se.sessionVars.EnableAnalyzeSnapshot = *execOption.AnalyzeSnapshot - } - - prePruneMode := se.sessionVars.PartitionPruneMode.Load() - if len(execOption.PartitionPruneMode) > 0 { - se.sessionVars.PartitionPruneMode.Store(execOption.PartitionPruneMode) - } - - return se, func() { - se.sessionVars.AnalyzeVersion = prevStatsVer - se.sessionVars.EnableAnalyzeSnapshot = prevAnalyzeSnapshot - if err := se.sessionVars.SetSystemVar(variable.TiDBSnapshot, ""); err != nil { - logutil.BgLogger().Error("set tidbSnapshot error", zap.Error(err)) - } - se.sessionVars.SnapshotInfoschema = nil - se.sessionVars.SnapshotTS = 0 - if !execOption.IgnoreWarning { - if se != nil && se.GetSessionVars().StmtCtx.WarningCount() > 0 { - warnings := se.GetSessionVars().StmtCtx.GetWarnings() - s.GetSessionVars().StmtCtx.AppendWarnings(warnings) - } - } - se.sessionVars.PartitionPruneMode.Store(prePruneMode) - se.sessionVars.OptimizerUseInvisibleIndexes = false - se.sessionVars.InspectionTableCache = nil - se.sessionVars.MemTracker.Detach() - s.sysSessionPool().Put(tmp) - }, nil -} - -func (s *session) withRestrictedSQLExecutor(ctx context.Context, opts []sqlexec.OptionFuncAlias, fn func(context.Context, *session) ([]chunk.Row, []*ast.ResultField, error)) ([]chunk.Row, []*ast.ResultField, error) { - execOption := sqlexec.GetExecOption(opts) - var se *session - var clean func() - var err error - if execOption.UseCurSession { - se, clean, err = s.useCurrentSession(execOption) - } else { - se, clean, err = s.getInternalSession(execOption) - } - if err != nil { - return nil, nil, errors.Trace(err) - } - defer clean() - if execOption.TrackSysProcID > 0 { - err = execOption.TrackSysProc(execOption.TrackSysProcID, se) - if err != nil { - return nil, nil, errors.Trace(err) - } - // unTrack should be called before clean (return sys session) - defer execOption.UnTrackSysProc(execOption.TrackSysProcID) - } - return fn(ctx, se) -} - -func (s *session) ExecRestrictedSQL(ctx context.Context, opts []sqlexec.OptionFuncAlias, sql string, params ...interface{}) ([]chunk.Row, []*ast.ResultField, error) { - return s.withRestrictedSQLExecutor(ctx, opts, func(ctx context.Context, se *session) ([]chunk.Row, []*ast.ResultField, error) { - stmt, err := se.ParseWithParams(ctx, sql, params...) - if err != nil { - return nil, nil, errors.Trace(err) - } - defer pprof.SetGoroutineLabels(ctx) - startTime := time.Now() - metrics.SessionRestrictedSQLCounter.Inc() - ctx = context.WithValue(ctx, execdetails.StmtExecDetailKey, &execdetails.StmtExecDetails{}) - ctx = context.WithValue(ctx, tikvutil.ExecDetailsKey, &tikvutil.ExecDetails{}) - rs, err := se.ExecuteInternalStmt(ctx, stmt) - if err != nil { - se.sessionVars.StmtCtx.AppendError(err) - } - if rs == nil { - return nil, nil, err - } - defer func() { - if closeErr := rs.Close(); closeErr != nil { - err = closeErr - } - }() - var rows []chunk.Row - rows, err = drainRecordSet(ctx, se, rs, nil) - if err != nil { - return nil, nil, err - } - - vars := se.GetSessionVars() - for _, dbName := range GetDBNames(vars) { - metrics.QueryDurationHistogram.WithLabelValues(metrics.LblInternal, dbName, vars.ResourceGroupName).Observe(time.Since(startTime).Seconds()) - } - return rows, rs.Fields(), err - }) -} - -// ExecuteInternalStmt execute internal stmt -func (s *session) ExecuteInternalStmt(ctx context.Context, stmtNode ast.StmtNode) (sqlexec.RecordSet, error) { - origin := s.sessionVars.InRestrictedSQL - s.sessionVars.InRestrictedSQL = true - defer func() { - s.sessionVars.InRestrictedSQL = origin - // Restore the goroutine label by using the original ctx after execution is finished. - pprof.SetGoroutineLabels(ctx) - }() - return s.ExecuteStmt(ctx, stmtNode) -} - -func (s *session) ExecuteStmt(ctx context.Context, stmtNode ast.StmtNode) (sqlexec.RecordSet, error) { - r, ctx := tracing.StartRegionEx(ctx, "session.ExecuteStmt") - defer r.End() - - if err := s.PrepareTxnCtx(ctx); err != nil { - return nil, err - } - if err := s.loadCommonGlobalVariablesIfNeeded(); err != nil { - return nil, err - } - - sessVars := s.sessionVars - sessVars.StartTime = time.Now() - - // Some executions are done in compile stage, so we reset them before compile. - if err := executor.ResetContextOfStmt(s, stmtNode); err != nil { - return nil, err - } - normalizedSQL, digest := s.sessionVars.StmtCtx.SQLDigest() - cmdByte := byte(atomic.LoadUint32(&s.GetSessionVars().CommandValue)) - if topsqlstate.TopSQLEnabled() { - s.sessionVars.StmtCtx.IsSQLRegistered.Store(true) - ctx = topsql.AttachAndRegisterSQLInfo(ctx, normalizedSQL, digest, s.sessionVars.InRestrictedSQL) - } - if sessVars.InPlanReplayer { - sessVars.StmtCtx.EnableOptimizerDebugTrace = true - } else if dom := domain.GetDomain(s); dom != nil && !sessVars.InRestrictedSQL { - // This is the earliest place we can get the SQL digest for this execution. - // If we find this digest is registered for PLAN REPLAYER CAPTURE, we need to enable optimizer debug trace no matter - // the plan digest will be matched or not. - if planReplayerHandle := dom.GetPlanReplayerHandle(); planReplayerHandle != nil { - tasks := planReplayerHandle.GetTasks() - for _, task := range tasks { - if task.SQLDigest == digest.String() { - sessVars.StmtCtx.EnableOptimizerDebugTrace = true - } - } - } - } - if sessVars.StmtCtx.EnableOptimizerDebugTrace { - plannercore.DebugTraceReceivedCommand(s, cmdByte, stmtNode) - } - - if err := s.validateStatementInTxn(stmtNode); err != nil { - return nil, err - } - - if err := s.validateStatementReadOnlyInStaleness(stmtNode); err != nil { - return nil, err - } - - // Uncorrelated subqueries will execute once when building plan, so we reset process info before building plan. - s.currentPlan = nil // reset current plan - s.SetProcessInfo(stmtNode.Text(), time.Now(), cmdByte, 0) - s.txn.onStmtStart(digest.String()) - defer sessiontxn.GetTxnManager(s).OnStmtEnd() - defer s.txn.onStmtEnd() - - if err := s.onTxnManagerStmtStartOrRetry(ctx, stmtNode); err != nil { - return nil, err - } - - failpoint.Inject("mockStmtSlow", func(val failpoint.Value) { - if strings.Contains(stmtNode.Text(), "/* sleep */") { - v, _ := val.(int) - time.Sleep(time.Duration(v) * time.Millisecond) - } - }) - - var stmtLabel string - if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { - prepareStmt, err := plannercore.GetPreparedStmt(execStmt, s.sessionVars) - if err == nil && prepareStmt.PreparedAst != nil { - stmtLabel = ast.GetStmtLabel(prepareStmt.PreparedAst.Stmt) - } - } - if stmtLabel == "" { - stmtLabel = ast.GetStmtLabel(stmtNode) - } - s.setRequestSource(ctx, stmtLabel, stmtNode) - - // Backup the original resource group name since sql hint might change it during optimization - originalResourceGroup := s.GetSessionVars().ResourceGroupName - - // Transform abstract syntax tree to a physical plan(stored in executor.ExecStmt). - compiler := executor.Compiler{Ctx: s} - stmt, err := compiler.Compile(ctx, stmtNode) - // session resource-group might be changed by query hint, ensure restore it back when - // the execution finished. - if sessVars.ResourceGroupName != originalResourceGroup { - // if target resource group doesn't exist, fallback to the origin resource group. - if _, ok := domain.GetDomain(s).InfoSchema().ResourceGroupByName(model.NewCIStr(sessVars.ResourceGroupName)); !ok { - logutil.Logger(ctx).Warn("Unknown resource group from hint", zap.String("name", sessVars.ResourceGroupName)) - sessVars.ResourceGroupName = originalResourceGroup - // if we are in a txn, should also reset the txn resource group. - if txn, err := s.Txn(false); err == nil && txn != nil && txn.Valid() { - kv.SetTxnResourceGroup(txn, originalResourceGroup) - } - } else { - defer func() { - // Restore the resource group for the session - sessVars.ResourceGroupName = originalResourceGroup - }() - } - } - if err != nil { - s.rollbackOnError(ctx) - - // Only print log message when this SQL is from the user. - // Mute the warning for internal SQLs. - if !s.sessionVars.InRestrictedSQL { - if !variable.ErrUnknownSystemVar.Equal(err) { - sql := stmtNode.Text() - if s.sessionVars.EnableRedactLog { - sql = parser.Normalize(sql) - } - logutil.Logger(ctx).Warn("compile SQL failed", zap.Error(err), - zap.String("SQL", sql)) - } - } - return nil, err - } - - durCompile := time.Since(s.sessionVars.StartTime) - s.GetSessionVars().DurationCompile = durCompile - if s.isInternal() { - session_metrics.SessionExecuteCompileDurationInternal.Observe(durCompile.Seconds()) - } else { - session_metrics.SessionExecuteCompileDurationGeneral.Observe(durCompile.Seconds()) - } - s.currentPlan = stmt.Plan - if execStmt, ok := stmtNode.(*ast.ExecuteStmt); ok { - if execStmt.Name == "" { - // for exec-stmt on bin-protocol, ignore the plan detail in `show process` to gain performance benefits. - s.currentPlan = nil - } - } - - // Execute the physical plan. - logStmt(stmt, s) - - var recordSet sqlexec.RecordSet - if stmt.PsStmt != nil { // point plan short path - recordSet, err = stmt.PointGet(ctx) - s.txn.changeToInvalid() - } else { - recordSet, err = runStmt(ctx, s, stmt) - } - - // Observe the resource group query total counter if the resource control is enabled and the - // current session is attached with a resource group. - resourceGroupName := s.GetSessionVars().ResourceGroupName - if len(resourceGroupName) > 0 { - metrics.ResourceGroupQueryTotalCounter.WithLabelValues(resourceGroupName).Inc() - } - - if err != nil { - if !errIsNoisy(err) { - logutil.Logger(ctx).Warn("run statement failed", - zap.Int64("schemaVersion", s.GetInfoSchema().SchemaMetaVersion()), - zap.Error(err), - zap.String("session", s.String())) - } - return recordSet, err - } - if !s.isInternal() && config.GetGlobalConfig().EnableTelemetry { - telemetry.CurrentExecuteCount.Inc() - tiFlashPushDown, tiFlashExchangePushDown := plannercore.IsTiFlashContained(stmt.Plan) - if tiFlashPushDown { - telemetry.CurrentTiFlashPushDownCount.Inc() - } - if tiFlashExchangePushDown { - telemetry.CurrentTiFlashExchangePushDownCount.Inc() - } - } - return recordSet, nil -} - -func (s *session) onTxnManagerStmtStartOrRetry(ctx context.Context, node ast.StmtNode) error { - if s.sessionVars.RetryInfo.Retrying { - return sessiontxn.GetTxnManager(s).OnStmtRetry(ctx) - } - return sessiontxn.GetTxnManager(s).OnStmtStart(ctx, node) -} - -func (s *session) validateStatementInTxn(stmtNode ast.StmtNode) error { - vars := s.GetSessionVars() - if _, ok := stmtNode.(*ast.ImportIntoStmt); ok && vars.InTxn() { - return errors.New("cannot run IMPORT INTO in explicit transaction") - } - return nil -} - -func (s *session) validateStatementReadOnlyInStaleness(stmtNode ast.StmtNode) error { - vars := s.GetSessionVars() - if !vars.TxnCtx.IsStaleness && vars.TxnReadTS.PeakTxnReadTS() == 0 && !vars.EnableExternalTSRead || vars.InRestrictedSQL { - return nil - } - errMsg := "only support read-only statement during read-only staleness transactions" - node := stmtNode.(ast.Node) - switch v := node.(type) { - case *ast.SplitRegionStmt: - return nil - case *ast.SelectStmt: - // select lock statement needs start a transaction which will be conflict to stale read, - // we forbid select lock statement in stale read for now. - if v.LockInfo != nil { - return errors.New("select lock hasn't been supported in stale read yet") - } - if !planner.IsReadOnly(stmtNode, vars) { - return errors.New(errMsg) - } - return nil - case *ast.ExplainStmt, *ast.DoStmt, *ast.ShowStmt, *ast.SetOprStmt, *ast.ExecuteStmt, *ast.SetOprSelectList: - if !planner.IsReadOnly(stmtNode, vars) { - return errors.New(errMsg) - } - return nil - default: - } - // covered DeleteStmt/InsertStmt/UpdateStmt/CallStmt/LoadDataStmt - if _, ok := stmtNode.(ast.DMLNode); ok { - return errors.New(errMsg) - } - return nil -} - -// fileTransInConnKeys contains the keys of queries that will be handled by handleFileTransInConn. -var fileTransInConnKeys = []fmt.Stringer{ - executor.LoadDataVarKey, - executor.LoadStatsVarKey, - executor.IndexAdviseVarKey, - executor.PlanReplayerLoadVarKey, -} - -func (s *session) hasFileTransInConn() bool { - s.mu.RLock() - defer s.mu.RUnlock() - - for _, k := range fileTransInConnKeys { - v := s.mu.values[k] - if v != nil { - return true - } - } - return false -} - -// runStmt executes the sqlexec.Statement and commit or rollback the current transaction. -func runStmt(ctx context.Context, se *session, s sqlexec.Statement) (rs sqlexec.RecordSet, err error) { - failpoint.Inject("assertTxnManagerInRunStmt", func() { - sessiontxn.RecordAssert(se, "assertTxnManagerInRunStmt", true) - if stmt, ok := s.(*executor.ExecStmt); ok { - sessiontxn.AssertTxnManagerInfoSchema(se, stmt.InfoSchema) - } - }) - - r, ctx := tracing.StartRegionEx(ctx, "session.runStmt") - defer r.End() - if r.Span != nil { - r.Span.LogKV("sql", s.OriginText()) - } - - se.SetValue(sessionctx.QueryString, s.OriginText()) - if _, ok := s.(*executor.ExecStmt).StmtNode.(ast.DDLNode); ok { - se.SetValue(sessionctx.LastExecuteDDL, true) - } else { - se.ClearValue(sessionctx.LastExecuteDDL) - } - - sessVars := se.sessionVars - - // Record diagnostic information for DML statements - if stmt, ok := s.(*executor.ExecStmt).StmtNode.(ast.DMLNode); ok { - // Keep the previous queryInfo for `show session_states` because the statement needs to encode it. - if showStmt, ok := stmt.(*ast.ShowStmt); !ok || showStmt.Tp != ast.ShowSessionStates { - defer func() { - sessVars.LastQueryInfo = sessionstates.QueryInfo{ - TxnScope: sessVars.CheckAndGetTxnScope(), - StartTS: sessVars.TxnCtx.StartTS, - ForUpdateTS: sessVars.TxnCtx.GetForUpdateTS(), - } - if err != nil { - sessVars.LastQueryInfo.ErrMsg = err.Error() - } - }() - } - } - - // Save origTxnCtx here to avoid it reset in the transaction retry. - origTxnCtx := sessVars.TxnCtx - err = se.checkTxnAborted(s) - if err != nil { - return nil, err - } - - rs, err = s.Exec(ctx) - se.updateTelemetryMetric(s.(*executor.ExecStmt)) - sessVars.TxnCtx.StatementCount++ - if rs != nil { - if se.GetSessionVars().StmtCtx.IsExplainAnalyzeDML { - if !sessVars.InTxn() { - se.StmtCommit(ctx) - if err := se.CommitTxn(ctx); err != nil { - return nil, err - } - } - } - return &execStmtResult{ - RecordSet: rs, - sql: s, - se: se, - }, err - } - - err = finishStmt(ctx, se, err, s) - if se.hasFileTransInConn() { - // The query will be handled later in handleFileTransInConn, - // then should call the ExecStmt.FinishExecuteStmt to finish this statement. - se.SetValue(ExecStmtVarKey, s.(*executor.ExecStmt)) - } else { - // If it is not a select statement or special query, we record its slow log here, - // then it could include the transaction commit time. - s.(*executor.ExecStmt).FinishExecuteStmt(origTxnCtx.StartTS, err, false) - } - return nil, err -} - -// ExecStmtVarKeyType is a dummy type to avoid naming collision in context. -type ExecStmtVarKeyType int - -// String defines a Stringer function for debugging and pretty printing. -func (ExecStmtVarKeyType) String() string { - return "exec_stmt_var_key" -} - -// ExecStmtVarKey is a variable key for ExecStmt. -const ExecStmtVarKey ExecStmtVarKeyType = 0 - -// execStmtResult is the return value of ExecuteStmt and it implements the sqlexec.RecordSet interface. -// Why we need a struct to wrap a RecordSet and provide another RecordSet? -// This is because there are so many session state related things that definitely not belongs to the original -// RecordSet, so this struct exists and RecordSet.Close() is overrided handle that. -type execStmtResult struct { - sqlexec.RecordSet - se *session - sql sqlexec.Statement -} - -func (rs *execStmtResult) Close() error { - se := rs.se - if err := rs.RecordSet.Close(); err != nil { - return finishStmt(context.Background(), se, err, rs.sql) - } - return finishStmt(context.Background(), se, nil, rs.sql) -} - -// rollbackOnError makes sure the next statement starts a new transaction with the latest InfoSchema. -func (s *session) rollbackOnError(ctx context.Context) { - if !s.sessionVars.InTxn() { - s.RollbackTxn(ctx) - } -} - -// PrepareStmt is used for executing prepare statement in binary protocol -func (s *session) PrepareStmt(sql string) (stmtID uint32, paramCount int, fields []*ast.ResultField, err error) { - defer func() { - if s.sessionVars.StmtCtx != nil { - s.sessionVars.StmtCtx.DetachMemDiskTracker() - } - }() - if s.sessionVars.TxnCtx.InfoSchema == nil { - // We don't need to create a transaction for prepare statement, just get information schema will do. - s.sessionVars.TxnCtx.InfoSchema = domain.GetDomain(s).InfoSchema() - } - err = s.loadCommonGlobalVariablesIfNeeded() - if err != nil { - return - } - - ctx := context.Background() - // NewPrepareExec may need startTS to build the executor, for example prepare statement has subquery in int. - // So we have to call PrepareTxnCtx here. - if err = s.PrepareTxnCtx(ctx); err != nil { - return - } - - prepareStmt := &ast.PrepareStmt{SQLText: sql} - if err = s.onTxnManagerStmtStartOrRetry(ctx, prepareStmt); err != nil { - return - } - - if err = sessiontxn.GetTxnManager(s).AdviseWarmup(); err != nil { - return - } - prepareExec := executor.NewPrepareExec(s, sql) - err = prepareExec.Next(ctx, nil) - // Rollback even if err is nil. - s.rollbackOnError(ctx) - - if err != nil { - return - } - return prepareExec.ID, prepareExec.ParamCount, prepareExec.Fields, nil -} - -// ExecutePreparedStmt executes a prepared statement. -func (s *session) ExecutePreparedStmt(ctx context.Context, stmtID uint32, params []expression.Expression) (sqlexec.RecordSet, error) { - prepStmt, err := s.sessionVars.GetPreparedStmtByID(stmtID) - if err != nil { - err = plannercore.ErrStmtNotFound - logutil.Logger(ctx).Error("prepared statement not found", zap.Uint32("stmtID", stmtID)) - return nil, err - } - stmt, ok := prepStmt.(*plannercore.PlanCacheStmt) - if !ok { - return nil, errors.Errorf("invalid PlanCacheStmt type") - } - execStmt := &ast.ExecuteStmt{ - BinaryArgs: params, - PrepStmt: stmt, - } - return s.ExecuteStmt(ctx, execStmt) -} - -func (s *session) DropPreparedStmt(stmtID uint32) error { - vars := s.sessionVars - if _, ok := vars.PreparedStmts[stmtID]; !ok { - return plannercore.ErrStmtNotFound - } - vars.RetryInfo.DroppedPreparedStmtIDs = append(vars.RetryInfo.DroppedPreparedStmtIDs, stmtID) - return nil -} - -func (s *session) Txn(active bool) (kv.Transaction, error) { - if !active { - return &s.txn, nil - } - _, err := sessiontxn.GetTxnManager(s).ActivateTxn() - s.SetMemoryFootprintChangeHook() - return &s.txn, err -} - -func (s *session) SetValue(key fmt.Stringer, value interface{}) { - s.mu.Lock() - s.mu.values[key] = value - s.mu.Unlock() -} - -func (s *session) Value(key fmt.Stringer) interface{} { - s.mu.RLock() - value := s.mu.values[key] - s.mu.RUnlock() - return value -} - -func (s *session) ClearValue(key fmt.Stringer) { - s.mu.Lock() - delete(s.mu.values, key) - s.mu.Unlock() -} - -type inCloseSession struct{} - -// Close function does some clean work when session end. -// Close should release the table locks which hold by the session. -func (s *session) Close() { - // TODO: do clean table locks when session exited without execute Close. - // TODO: do clean table locks when tidb-server was `kill -9`. - if s.HasLockedTables() && config.TableLockEnabled() { - if ds := config.TableLockDelayClean(); ds > 0 { - time.Sleep(time.Duration(ds) * time.Millisecond) - } - lockedTables := s.GetAllTableLocks() - err := domain.GetDomain(s).DDL().UnlockTables(s, lockedTables) - if err != nil { - logutil.BgLogger().Error("release table lock failed", zap.Uint64("conn", s.sessionVars.ConnectionID)) - } - } - s.ReleaseAllAdvisoryLocks() - if s.statsCollector != nil { - s.statsCollector.Delete() - } - if s.idxUsageCollector != nil { - s.idxUsageCollector.Delete() - } - telemetry.GlobalBuiltinFunctionsUsage.Collect(s.GetBuiltinFunctionUsage()) - bindValue := s.Value(bindinfo.SessionBindInfoKeyType) - if bindValue != nil { - bindValue.(*bindinfo.SessionHandle).Close() - } - ctx := context.WithValue(context.TODO(), inCloseSession{}, struct{}{}) - s.RollbackTxn(ctx) - if s.sessionVars != nil { - s.sessionVars.WithdrawAllPreparedStmt() - } - if s.stmtStats != nil { - s.stmtStats.SetFinished() - } - s.ClearDiskFullOpt() - if s.sessionPlanCache != nil { - s.sessionPlanCache.Close() - } -} - -// GetSessionVars implements the context.Context interface. -func (s *session) GetSessionVars() *variable.SessionVars { - return s.sessionVars -} - -func (s *session) AuthPluginForUser(user *auth.UserIdentity) (string, error) { - pm := privilege.GetPrivilegeManager(s) - authplugin, err := pm.GetAuthPluginForConnection(user.Username, user.Hostname) - if err != nil { - return "", err - } - return authplugin, nil -} - -// Auth validates a user using an authentication string and salt. -// If the password fails, it will keep trying other users until exhausted. -// This means it can not be refactored to use MatchIdentity yet. -func (s *session) Auth(user *auth.UserIdentity, authentication, salt []byte, authConn conn.AuthConn) error { - hasPassword := "YES" - if len(authentication) == 0 { - hasPassword = "NO" - } - pm := privilege.GetPrivilegeManager(s) - authUser, err := s.MatchIdentity(user.Username, user.Hostname) - if err != nil { - return privileges.ErrAccessDenied.FastGenByArgs(user.Username, user.Hostname, hasPassword) - } - // Check whether continuous login failure is enabled to lock the account. - // If enabled, determine whether to unlock the account and notify TiDB to update the cache. - enableAutoLock := pm.IsAccountAutoLockEnabled(authUser.Username, authUser.Hostname) - if enableAutoLock { - err = failedLoginTrackingBegin(s) - if err != nil { - return err - } - lockStatusChanged, err := verifyAccountAutoLock(s, authUser.Username, authUser.Hostname) - if err != nil { - rollbackErr := failedLoginTrackingRollback(s) - if rollbackErr != nil { - return rollbackErr - } - return err - } - err = failedLoginTrackingCommit(s) - if err != nil { - rollbackErr := failedLoginTrackingRollback(s) - if rollbackErr != nil { - return rollbackErr - } - return err - } - if lockStatusChanged { - // Notification auto unlock. - err = domain.GetDomain(s).NotifyUpdatePrivilege() - if err != nil { - return err - } - } - } - - info, err := pm.ConnectionVerification(user, authUser.Username, authUser.Hostname, authentication, salt, s.sessionVars, authConn) - if err != nil { - if info.FailedDueToWrongPassword { - // when user enables the account locking function for consecutive login failures, - // the system updates the login failure count and determines whether to lock the account when authentication fails. - if enableAutoLock { - err := failedLoginTrackingBegin(s) - if err != nil { - return err - } - lockStatusChanged, passwordLocking, trackingErr := authFailedTracking(s, authUser.Username, authUser.Hostname) - if trackingErr != nil { - if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { - return rollBackErr - } - return trackingErr - } - if err := failedLoginTrackingCommit(s); err != nil { - if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { - return rollBackErr - } - return err - } - if lockStatusChanged { - // Notification auto lock. - err := autolockAction(s, passwordLocking, authUser.Username, authUser.Hostname) - if err != nil { - return err - } - } - } - } - return err - } - - if variable.EnableResourceControl.Load() && info.ResourceGroupName != "" { - s.sessionVars.ResourceGroupName = strings.ToLower(info.ResourceGroupName) - } - - if info.InSandBoxMode { - // Enter sandbox mode, only execute statement for resetting password. - s.EnableSandBoxMode() - } - if enableAutoLock { - err := failedLoginTrackingBegin(s) - if err != nil { - return err - } - // The password is correct. If the account is not locked, the number of login failure statistics will be cleared. - err = authSuccessClearCount(s, authUser.Username, authUser.Hostname) - if err != nil { - if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { - return rollBackErr - } - return err - } - err = failedLoginTrackingCommit(s) - if err != nil { - if rollBackErr := failedLoginTrackingRollback(s); rollBackErr != nil { - return rollBackErr - } - return err - } - } - pm.AuthSuccess(authUser.Username, authUser.Hostname) - user.AuthUsername = authUser.Username - user.AuthHostname = authUser.Hostname - s.sessionVars.User = user - s.sessionVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) - return nil -} - -func authSuccessClearCount(s *session, user string, host string) error { - // Obtain accurate lock status and failure count information. - passwordLocking, err := getFailedLoginUserAttributes(s, user, host) - if err != nil { - return err - } - // If the account is locked, it may be caused by the untimely update of the cache, - // directly report the account lock. - if passwordLocking.AutoAccountLocked { - if passwordLocking.PasswordLockTimeDays == -1 { - return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, - "unlimited", "unlimited") - } - - lds := strconv.FormatInt(passwordLocking.PasswordLockTimeDays, 10) - return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, lds, lds) - } - if passwordLocking.FailedLoginCount != 0 { - // If the number of account login failures is not zero, it will be updated to 0. - passwordLockingJSON := privileges.BuildSuccessPasswordLockingJSON(passwordLocking.FailedLoginAttempts, - passwordLocking.PasswordLockTimeDays) - if passwordLockingJSON != "" { - if err := s.passwordLocking(user, host, passwordLockingJSON); err != nil { - return err - } - } - } - return nil -} - -func verifyAccountAutoLock(s *session, user, host string) (bool, error) { - pm := privilege.GetPrivilegeManager(s) - // Use the cache to determine whether to unlock the account. - // If the account needs to be unlocked, read the database information to determine whether - // the account needs to be unlocked. Otherwise, an error message is displayed. - lockStatusInMemory, err := pm.VerifyAccountAutoLockInMemory(user, host) - if err != nil { - return false, err - } - // If the lock status in the cache is Unlock, the automatic unlock is skipped. - // If memory synchronization is slow and there is a lock in the database, it will be processed upon successful login. - if !lockStatusInMemory { - return false, nil - } - lockStatusChanged := false - var plJSON string - // After checking the cache, obtain the latest data from the database and determine - // whether to automatically unlock the database to prevent repeated unlock errors. - pl, err := getFailedLoginUserAttributes(s, user, host) - if err != nil { - return false, err - } - if pl.AutoAccountLocked { - // If it is locked, need to check whether it can be automatically unlocked. - lockTimeDay := pl.PasswordLockTimeDays - if lockTimeDay == -1 { - return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, "unlimited", "unlimited") - } - lastChanged := pl.AutoLockedLastChanged - d := time.Now().Unix() - lastChanged - if d <= lockTimeDay*24*60*60 { - lds := strconv.FormatInt(lockTimeDay, 10) - rds := strconv.FormatInt(int64(math.Ceil(float64(lockTimeDay)-float64(d)/(24*60*60))), 10) - return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, lds, rds) - } - // Generate unlock json string. - plJSON = privileges.BuildPasswordLockingJSON(pl.FailedLoginAttempts, - pl.PasswordLockTimeDays, "N", 0, time.Now().Format(time.UnixDate)) - } - if plJSON != "" { - lockStatusChanged = true - if err = s.passwordLocking(user, host, plJSON); err != nil { - return false, err - } - } - return lockStatusChanged, nil -} - -func authFailedTracking(s *session, user string, host string) (bool, *privileges.PasswordLocking, error) { - // Obtain the number of consecutive password login failures. - passwordLocking, err := getFailedLoginUserAttributes(s, user, host) - if err != nil { - return false, nil, err - } - // Consecutive wrong password login failure times +1, - // If the lock condition is satisfied, the lock status is updated and the update cache is notified. - lockStatusChanged, err := userAutoAccountLocked(s, user, host, passwordLocking) - if err != nil { - return false, nil, err - } - return lockStatusChanged, passwordLocking, nil -} - -func autolockAction(s *session, passwordLocking *privileges.PasswordLocking, user, host string) error { - // Don't want to update the cache frequently, and only trigger the update cache when the lock status is updated. - err := domain.GetDomain(s).NotifyUpdatePrivilege() - if err != nil { - return err - } - // The number of failed login attempts reaches FAILED_LOGIN_ATTEMPTS. - // An error message is displayed indicating permission denial and account lock. - if passwordLocking.PasswordLockTimeDays == -1 { - return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, - "unlimited", "unlimited") - } - lds := strconv.FormatInt(passwordLocking.PasswordLockTimeDays, 10) - return privileges.GenerateAccountAutoLockErr(passwordLocking.FailedLoginAttempts, user, host, lds, lds) -} - -func (s *session) passwordLocking(user string, host string, newAttributesStr string) error { - sql := new(strings.Builder) - sqlexec.MustFormatSQL(sql, "UPDATE %n.%n SET ", mysql.SystemDB, mysql.UserTable) - sqlexec.MustFormatSQL(sql, "user_attributes=json_merge_patch(coalesce(user_attributes, '{}'), %?)", newAttributesStr) - sqlexec.MustFormatSQL(sql, " WHERE Host=%? and User=%?;", host, user) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - _, err := s.ExecuteInternal(ctx, sql.String()) - return err -} - -func failedLoginTrackingBegin(s *session) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - _, err := s.ExecuteInternal(ctx, "BEGIN PESSIMISTIC") - return err -} - -func failedLoginTrackingCommit(s *session) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - _, err := s.ExecuteInternal(ctx, "COMMIT") - if err != nil { - _, rollBackErr := s.ExecuteInternal(ctx, "ROLLBACK") - if rollBackErr != nil { - return rollBackErr - } - } - return err -} - -func failedLoginTrackingRollback(s *session) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - _, err := s.ExecuteInternal(ctx, "ROLLBACK") - return err -} - -// getFailedLoginUserAttributes queries the exact number of consecutive password login failures (concurrency is not allowed). -func getFailedLoginUserAttributes(s *session, user string, host string) (*privileges.PasswordLocking, error) { - passwordLocking := &privileges.PasswordLocking{} - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnPrivilege) - rs, err := s.ExecuteInternal(ctx, `SELECT user_attributes from mysql.user WHERE USER = %? AND HOST = %? for update`, user, host) - if err != nil { - return passwordLocking, err - } - defer func() { - if closeErr := rs.Close(); closeErr != nil { - err = closeErr - } - }() - req := rs.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - err = rs.Next(ctx, req) - if err != nil { - return passwordLocking, err - } - if req.NumRows() == 0 { - return passwordLocking, fmt.Errorf("user_attributes by `%s`@`%s` not found", user, host) - } - row := iter.Begin() - if !row.IsNull(0) { - passwordLockingJSON := row.GetJSON(0) - return passwordLocking, passwordLocking.ParseJSON(passwordLockingJSON) - } - return passwordLocking, fmt.Errorf("user_attributes by `%s`@`%s` not found", user, host) -} - -func userAutoAccountLocked(s *session, user string, host string, pl *privileges.PasswordLocking) (bool, error) { - // Indicates whether the user needs to update the lock status change. - lockStatusChanged := false - // The number of consecutive login failures is stored in the database. - // If the current login fails, one is added to the number of consecutive login failures - // stored in the database to determine whether the user needs to be locked and the number of update failures. - failedLoginCount := pl.FailedLoginCount + 1 - // If the cache is not updated, but it is already locked, it will report that the account is locked. - if pl.AutoAccountLocked { - if pl.PasswordLockTimeDays == -1 { - return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, - "unlimited", "unlimited") - } - lds := strconv.FormatInt(pl.PasswordLockTimeDays, 10) - return false, privileges.GenerateAccountAutoLockErr(pl.FailedLoginAttempts, user, host, lds, lds) - } - - autoAccountLocked := "N" - autoLockedLastChanged := "" - if pl.FailedLoginAttempts == 0 || pl.PasswordLockTimeDays == 0 { - return false, nil - } - - if failedLoginCount >= pl.FailedLoginAttempts { - autoLockedLastChanged = time.Now().Format(time.UnixDate) - autoAccountLocked = "Y" - lockStatusChanged = true - } - - newAttributesStr := privileges.BuildPasswordLockingJSON(pl.FailedLoginAttempts, - pl.PasswordLockTimeDays, autoAccountLocked, failedLoginCount, autoLockedLastChanged) - if newAttributesStr != "" { - return lockStatusChanged, s.passwordLocking(user, host, newAttributesStr) - } - return lockStatusChanged, nil -} - -// MatchIdentity finds the matching username + password in the MySQL privilege tables -// for a username + hostname, since MySQL can have wildcards. -func (s *session) MatchIdentity(username, remoteHost string) (*auth.UserIdentity, error) { - pm := privilege.GetPrivilegeManager(s) - var success bool - var skipNameResolve bool - var user = &auth.UserIdentity{} - varVal, err := s.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.SkipNameResolve) - if err == nil && variable.TiDBOptOn(varVal) { - skipNameResolve = true - } - user.Username, user.Hostname, success = pm.MatchIdentity(username, remoteHost, skipNameResolve) - if success { - return user, nil - } - // This error will not be returned to the user, access denied will be instead - return nil, fmt.Errorf("could not find matching user in MatchIdentity: %s, %s", username, remoteHost) -} - -// AuthWithoutVerification is required by the ResetConnection RPC -func (s *session) AuthWithoutVerification(user *auth.UserIdentity) bool { - pm := privilege.GetPrivilegeManager(s) - authUser, err := s.MatchIdentity(user.Username, user.Hostname) - if err != nil { - return false - } - if pm.GetAuthWithoutVerification(authUser.Username, authUser.Hostname) { - user.AuthUsername = authUser.Username - user.AuthHostname = authUser.Hostname - s.sessionVars.User = user - s.sessionVars.ActiveRoles = pm.GetDefaultRoles(user.AuthUsername, user.AuthHostname) - return true - } - return false -} - -// SetSessionStatesHandler implements the Session.SetSessionStatesHandler interface. -func (s *session) SetSessionStatesHandler(stateType sessionstates.SessionStateType, handler sessionctx.SessionStatesHandler) { - s.sessionStatesHandlers[stateType] = handler -} - -// CreateSession4Test creates a new session environment for test. -func CreateSession4Test(store kv.Storage) (Session, error) { - se, err := CreateSession4TestWithOpt(store, nil) - if err == nil { - // Cover both chunk rpc encoding and default encoding. - // nolint:gosec - if rand.Intn(2) == 0 { - se.GetSessionVars().EnableChunkRPC = false - } else { - se.GetSessionVars().EnableChunkRPC = true - } - } - return se, err -} - -// Opt describes the option for creating session -type Opt struct { - PreparedPlanCache sessionctx.PlanCache -} - -// CreateSession4TestWithOpt creates a new session environment for test. -func CreateSession4TestWithOpt(store kv.Storage, opt *Opt) (Session, error) { - s, err := CreateSessionWithOpt(store, opt) - if err == nil { - // initialize session variables for test. - s.GetSessionVars().InitChunkSize = 2 - s.GetSessionVars().MaxChunkSize = 32 - s.GetSessionVars().MinPagingSize = variable.DefMinPagingSize - s.GetSessionVars().EnablePaging = variable.DefTiDBEnablePaging - err = s.GetSessionVars().SetSystemVarWithoutValidation(variable.CharacterSetConnection, "utf8mb4") - } - return s, err -} - -// CreateSession creates a new session environment. -func CreateSession(store kv.Storage) (Session, error) { - return CreateSessionWithOpt(store, nil) -} - -// CreateSessionWithOpt creates a new session environment with option. -// Use default option if opt is nil. -func CreateSessionWithOpt(store kv.Storage, opt *Opt) (Session, error) { - s, err := createSessionWithOpt(store, opt) - if err != nil { - return nil, err - } - - // Add auth here. - do, err := domap.Get(store) - if err != nil { - return nil, err - } - extensions, err := extension.GetExtensions() - if err != nil { - return nil, err - } - pm := privileges.NewUserPrivileges(do.PrivilegeHandle(), extensions) - privilege.BindPrivilegeManager(s, pm) - - // Add stats collector, and it will be freed by background stats worker - // which periodically updates stats using the collected data. - if do.StatsHandle() != nil && do.StatsUpdating() { - s.statsCollector = do.StatsHandle().NewSessionStatsItem().(*usage.SessionStatsItem) - if GetIndexUsageSyncLease() > 0 { - s.idxUsageCollector = do.StatsHandle().NewSessionIndexUsageCollector().(*usage.SessionIndexUsageCollector) - } - } - - return s, nil -} - -// loadCollationParameter loads collation parameter from mysql.tidb -func loadCollationParameter(ctx context.Context, se *session) (bool, error) { - para, err := se.getTableValue(ctx, mysql.TiDBTable, tidbNewCollationEnabled) - if err != nil { - return false, err - } - if para == varTrue { - return true, nil - } else if para == varFalse { - return false, nil - } - logutil.BgLogger().Warn( - "Unexpected value of 'new_collation_enabled' in 'mysql.tidb', use 'False' instead", - zap.String("value", para)) - return false, nil -} - -type tableBasicInfo struct { - SQL string - id int64 -} - -var ( - errResultIsEmpty = dbterror.ClassExecutor.NewStd(errno.ErrResultIsEmpty) - // DDLJobTables is a list of tables definitions used in concurrent DDL. - DDLJobTables = []tableBasicInfo{ - {ddl.JobTableSQL, ddl.JobTableID}, - {ddl.ReorgTableSQL, ddl.ReorgTableID}, - {ddl.HistoryTableSQL, ddl.HistoryTableID}, - } - // BackfillTables is a list of tables definitions used in dist reorg DDL. - BackfillTables = []tableBasicInfo{ - {ddl.BackgroundSubtaskTableSQL, ddl.BackgroundSubtaskTableID}, - {ddl.BackgroundSubtaskHistoryTableSQL, ddl.BackgroundSubtaskHistoryTableID}, - } - mdlTable = "create table mysql.tidb_mdl_info(job_id BIGINT NOT NULL PRIMARY KEY, version BIGINT NOT NULL, table_ids text(65535));" -) - -func splitAndScatterTable(store kv.Storage, tableIDs []int64) { - if s, ok := store.(kv.SplittableStore); ok && atomic.LoadUint32(&ddl.EnableSplitTableRegion) == 1 { - ctxWithTimeout, cancel := context.WithTimeout(context.Background(), variable.DefWaitSplitRegionTimeout*time.Second) - var regionIDs []uint64 - for _, id := range tableIDs { - regionIDs = append(regionIDs, ddl.SplitRecordRegion(ctxWithTimeout, s, id, id, variable.DefTiDBScatterRegion)) - } - if variable.DefTiDBScatterRegion { - ddl.WaitScatterRegionFinish(ctxWithTimeout, s, regionIDs...) - } - cancel() - } -} - -// InitDDLJobTables is to create tidb_ddl_job, tidb_ddl_reorg and tidb_ddl_history, or tidb_background_subtask and tidb_background_subtask_history. -func InitDDLJobTables(store kv.Storage, targetVer meta.DDLTableVersion) error { - targetTables := DDLJobTables - if targetVer == meta.BackfillTableVersion { - targetTables = BackfillTables - } - return kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - tableVer, err := t.CheckDDLTableVersion() - if err != nil || tableVer >= targetVer { - return errors.Trace(err) - } - dbID, err := t.CreateMySQLDatabaseIfNotExists() - if err != nil { - return err - } - if err = createAndSplitTables(store, t, dbID, targetTables); err != nil { - return err - } - return t.SetDDLTables(targetVer) - }) -} - -func createAndSplitTables(store kv.Storage, t *meta.Meta, dbID int64, tables []tableBasicInfo) error { - tableIDs := make([]int64, 0, len(tables)) - for _, tbl := range tables { - tableIDs = append(tableIDs, tbl.id) - } - splitAndScatterTable(store, tableIDs) - p := parser.New() - for _, tbl := range tables { - stmt, err := p.ParseOneStmt(tbl.SQL, "", "") - if err != nil { - return errors.Trace(err) - } - tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) - if err != nil { - return errors.Trace(err) - } - tblInfo.State = model.StatePublic - tblInfo.ID = tbl.id - tblInfo.UpdateTS = t.StartTS - err = t.CreateTableOrView(dbID, tblInfo) - if err != nil { - return errors.Trace(err) - } - } - return nil -} - -// InitMDLTable is to create tidb_mdl_info, which is used for metadata lock. -func InitMDLTable(store kv.Storage) error { - return kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - ver, err := t.CheckDDLTableVersion() - if err != nil || ver >= meta.MDLTableVersion { - return errors.Trace(err) - } - dbID, err := t.CreateMySQLDatabaseIfNotExists() - if err != nil { - return err - } - splitAndScatterTable(store, []int64{ddl.MDLTableID}) - p := parser.New() - stmt, err := p.ParseOneStmt(mdlTable, "", "") - if err != nil { - return errors.Trace(err) - } - tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) - if err != nil { - return errors.Trace(err) - } - tblInfo.State = model.StatePublic - tblInfo.ID = ddl.MDLTableID - tblInfo.UpdateTS = t.StartTS - err = t.CreateTableOrView(dbID, tblInfo) - if err != nil { - return errors.Trace(err) - } - - return t.SetDDLTables(meta.MDLTableVersion) - }) -} - -// InitMDLVariableForBootstrap initializes the metadata lock variable. -func InitMDLVariableForBootstrap(store kv.Storage) error { - err := kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - return t.SetMetadataLock(true) - }) - if err != nil { - return err - } - variable.EnableMDL.Store(true) - return nil -} - -// InitMDLVariableForUpgrade initializes the metadata lock variable. -func InitMDLVariableForUpgrade(store kv.Storage) (bool, error) { - isNull := false - enable := false - var err error - err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - enable, isNull, err = t.GetMetadataLock() - if err != nil { - return err - } - return nil - }) - if isNull || !enable { - variable.EnableMDL.Store(false) - } else { - variable.EnableMDL.Store(true) - } - return isNull, err -} - -// InitMDLVariable initializes the metadata lock variable. -func InitMDLVariable(store kv.Storage) error { - isNull := false - enable := false - var err error - err = kv.RunInNewTxn(kv.WithInternalSourceType(context.Background(), kv.InternalTxnDDL), store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - enable, isNull, err = t.GetMetadataLock() - if err != nil { - return err - } - if isNull { - // Workaround for version: nightly-2022-11-07 to nightly-2022-11-17. - enable = true - logutil.BgLogger().Warn("metadata lock is null") - err = t.SetMetadataLock(true) - if err != nil { - return err - } - } - return nil - }) - variable.EnableMDL.Store(enable) - return err -} - -// BootstrapSession bootstrap session and domain. -func BootstrapSession(store kv.Storage) (*domain.Domain, error) { - return bootstrapSessionImpl(store, createSessions) -} - -// BootstrapSession4DistExecution bootstrap session and dom for Distributed execution test, only for unit testing. -func BootstrapSession4DistExecution(store kv.Storage) (*domain.Domain, error) { - return bootstrapSessionImpl(store, createSessions4DistExecution) -} - -func bootstrapSessionImpl(store kv.Storage, createSessionsImpl func(store kv.Storage, cnt int) ([]*session, error)) (*domain.Domain, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - cfg := config.GetGlobalConfig() - if len(cfg.Instance.PluginLoad) > 0 { - err := plugin.Load(context.Background(), plugin.Config{ - Plugins: strings.Split(cfg.Instance.PluginLoad, ","), - PluginDir: cfg.Instance.PluginDir, - }) - if err != nil { - return nil, err - } - } - err := InitDDLJobTables(store, meta.BaseDDLTableVersion) - if err != nil { - return nil, err - } - err = InitMDLTable(store) - if err != nil { - return nil, err - } - err = InitDDLJobTables(store, meta.BackfillTableVersion) - if err != nil { - return nil, err - } - ver := getStoreBootstrapVersion(store) - if ver == notBootstrapped { - runInBootstrapSession(store, bootstrap) - } else if ver < currentBootstrapVersion { - runInBootstrapSession(store, upgrade) - } else { - err = InitMDLVariable(store) - if err != nil { - return nil, err - } - } - - analyzeConcurrencyQuota := int(config.GetGlobalConfig().Performance.AnalyzePartitionConcurrencyQuota) - concurrency := int(config.GetGlobalConfig().Performance.StatsLoadConcurrency) - ses, err := createSessionsImpl(store, 10) - if err != nil { - return nil, err - } - ses[0].GetSessionVars().InRestrictedSQL = true - - // get system tz from mysql.tidb - tz, err := ses[0].getTableValue(ctx, mysql.TiDBTable, tidbSystemTZ) - if err != nil { - return nil, err - } - timeutil.SetSystemTZ(tz) - - // get the flag from `mysql`.`tidb` which indicating if new collations are enabled. - newCollationEnabled, err := loadCollationParameter(ctx, ses[0]) - if err != nil { - return nil, err - } - collate.SetNewCollationEnabledForTest(newCollationEnabled) - // To deal with the location partition failure caused by inconsistent NewCollationEnabled values(see issue #32416). - rebuildAllPartitionValueMapAndSorted(ses[0]) - - dom := domain.GetDomain(ses[0]) - - // We should make the load bind-info loop before other loops which has internal SQL. - // Because the internal SQL may access the global bind-info handler. As the result, the data race occurs here as the - // LoadBindInfoLoop inits global bind-info handler. - err = dom.LoadBindInfoLoop(ses[1], ses[2]) - if err != nil { - return nil, err - } - - if !config.GetGlobalConfig().Security.SkipGrantTable { - err = dom.LoadPrivilegeLoop(ses[3]) - if err != nil { - return nil, err - } - } - - // Rebuild sysvar cache in a loop - err = dom.LoadSysVarCacheLoop(ses[4]) - if err != nil { - return nil, err - } - - if config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler { - // Invalid client-go tiflash_compute store cache if necessary. - err = dom.WatchTiFlashComputeNodeChange() - if err != nil { - return nil, err - } - } - - if err = extensionimpl.Bootstrap(context.Background(), dom); err != nil { - return nil, err - } - - if len(cfg.Instance.PluginLoad) > 0 { - err := plugin.Init(context.Background(), plugin.Config{EtcdClient: dom.GetEtcdClient()}) - if err != nil { - return nil, err - } - } - - err = executor.LoadExprPushdownBlacklist(ses[5]) - if err != nil { - return nil, err - } - err = executor.LoadOptRuleBlacklist(ctx, ses[5]) - if err != nil { - return nil, err - } - - if dom.GetEtcdClient() != nil { - // We only want telemetry data in production-like clusters. When TiDB is deployed over other engines, - // for example, unistore engine (used for local tests), we just skip it. Its etcd client is nil. - if config.GetGlobalConfig().EnableTelemetry { - // There is no way to turn telemetry on with global variable `tidb_enable_telemetry` - // when it is disabled in config. See IsTelemetryEnabled function in telemetry/telemetry.go - go func() { - dom.TelemetryReportLoop(ses[5]) - dom.TelemetryRotateSubWindowLoop(ses[5]) - }() - } - } - - planReplayerWorkerCnt := config.GetGlobalConfig().Performance.PlanReplayerDumpWorkerConcurrency - planReplayerWorkersSctx := make([]sessionctx.Context, planReplayerWorkerCnt) - pworkerSes, err := createSessions(store, int(planReplayerWorkerCnt)) - if err != nil { - return nil, err - } - for i := 0; i < int(planReplayerWorkerCnt); i++ { - planReplayerWorkersSctx[i] = pworkerSes[i] - } - // setup plan replayer handle - dom.SetupPlanReplayerHandle(ses[6], planReplayerWorkersSctx) - dom.StartPlanReplayerHandle() - // setup dumpFileGcChecker - dom.SetupDumpFileGCChecker(ses[7]) - dom.DumpFileGcCheckerLoop() - // setup historical stats worker - dom.SetupHistoricalStatsWorker(ses[8]) - dom.StartHistoricalStatsWorker() - failToLoadOrParseSQLFile := false // only used for unit test - if runBootstrapSQLFile { - pm := &privileges.UserPrivileges{ - Handle: dom.PrivilegeHandle(), - } - privilege.BindPrivilegeManager(ses[9], pm) - if err := doBootstrapSQLFile(ses[9]); err != nil && intest.InTest { - failToLoadOrParseSQLFile = true - } - } - // A sub context for update table stats, and other contexts for concurrent stats loading. - cnt := 1 + concurrency - syncStatsCtxs, err := createSessions(store, cnt) - if err != nil { - return nil, err - } - subCtxs := make([]sessionctx.Context, cnt) - for i := 0; i < cnt; i++ { - subCtxs[i] = sessionctx.Context(syncStatsCtxs[i]) - } - - // setup extract Handle - extractWorkers := 1 - sctxs, err := createSessions(store, extractWorkers) - if err != nil { - return nil, err - } - extractWorkerSctxs := make([]sessionctx.Context, 0) - for _, sctx := range sctxs { - extractWorkerSctxs = append(extractWorkerSctxs, sctx) - } - dom.SetupExtractHandle(extractWorkerSctxs) - - // setup init stats loader - initStatsCtx, err := createSession(store) - if err != nil { - return nil, err - } - if err = dom.LoadAndUpdateStatsLoop(subCtxs, initStatsCtx); err != nil { - return nil, err - } - - // start TTL job manager after setup stats collector - // because TTL could modify a lot of columns, and need to trigger auto analyze - ttlworker.AttachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { - if s, ok := s.(*session); ok { - return attachStatsCollector(s, dom) - } - return s - } - ttlworker.DetachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { - if s, ok := s.(*session); ok { - return detachStatsCollector(s) - } - return s - } - dom.StartTTLJobManager() - - analyzeCtxs, err := createSessions(store, analyzeConcurrencyQuota) - if err != nil { - return nil, err - } - subCtxs2 := make([]sessionctx.Context, analyzeConcurrencyQuota) - for i := 0; i < analyzeConcurrencyQuota; i++ { - subCtxs2[i] = analyzeCtxs[i] - } - dom.SetupAnalyzeExec(subCtxs2) - dom.LoadSigningCertLoop(cfg.Security.SessionTokenSigningCert, cfg.Security.SessionTokenSigningKey) - - if raw, ok := store.(kv.EtcdBackend); ok { - err = raw.StartGCWorker() - if err != nil { - return nil, err - } - } - - // This only happens in testing, since the failure of loading or parsing sql file - // would panic the bootstrapping. - if intest.InTest && failToLoadOrParseSQLFile { - dom.Close() - return nil, errors.New("Fail to load or parse sql file") - } - err = dom.InitDistTaskLoop(ctx) - if err != nil { - return nil, err - } - return dom, err -} - -// GetDomain gets the associated domain for store. -func GetDomain(store kv.Storage) (*domain.Domain, error) { - return domap.Get(store) -} - -// runInBootstrapSession create a special session for bootstrap to run. -// If no bootstrap and storage is remote, we must use a little lease time to -// bootstrap quickly, after bootstrapped, we will reset the lease time. -// TODO: Using a bootstrap tool for doing this may be better later. -func runInBootstrapSession(store kv.Storage, bootstrap func(Session)) { - s, err := createSession(store) - if err != nil { - // Bootstrap fail will cause program exit. - logutil.BgLogger().Fatal("createSession error", zap.Error(err)) - } - // For the bootstrap SQLs, the following variables should be compatible with old TiDB versions. - s.sessionVars.EnableClusteredIndex = variable.ClusteredIndexDefModeIntOnly - - s.SetValue(sessionctx.Initing, true) - bootstrap(s) - finishBootstrap(store) - s.ClearValue(sessionctx.Initing) - - dom := domain.GetDomain(s) - dom.Close() - if intest.InTest { - infosync.MockGlobalServerInfoManagerEntry.Close() - } - domap.Delete(store) -} - -func createSessions(store kv.Storage, cnt int) ([]*session, error) { - return createSessionsImpl(store, cnt, createSession) -} - -func createSessions4DistExecution(store kv.Storage, cnt int) ([]*session, error) { - domap.Delete(store) - - return createSessionsImpl(store, cnt, createSession4DistExecution) -} - -func createSessionsImpl(store kv.Storage, cnt int, createSessionImpl func(kv.Storage) (*session, error)) ([]*session, error) { - // Then we can create new dom - ses := make([]*session, cnt) - for i := 0; i < cnt; i++ { - se, err := createSessionImpl(store) - if err != nil { - return nil, err - } - ses[i] = se - } - - return ses, nil -} - -// createSession creates a new session. -// Please note that such a session is not tracked by the internal session list. -// This means the min ts reporter is not aware of it and may report a wrong min start ts. -// In most cases you should use a session pool in domain instead. -func createSession(store kv.Storage) (*session, error) { - return createSessionWithOpt(store, nil) -} - -func createSession4DistExecution(store kv.Storage) (*session, error) { - return createSessionWithOpt(store, nil) -} - -func createSessionWithOpt(store kv.Storage, opt *Opt) (*session, error) { - dom, err := domap.Get(store) - if err != nil { - return nil, err - } - s := &session{ - store: store, - ddlOwnerManager: dom.DDL().OwnerManager(), - client: store.GetClient(), - mppClient: store.GetMPPClient(), - stmtStats: stmtstats.CreateStatementStats(), - sessionStatesHandlers: make(map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler), - } - s.sessionVars = variable.NewSessionVars(s) - - s.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) - if opt != nil && opt.PreparedPlanCache != nil { - s.sessionPlanCache = opt.PreparedPlanCache - } - s.mu.values = make(map[fmt.Stringer]interface{}) - s.lockedTables = make(map[int64]model.TableLockTpInfo) - s.advisoryLocks = make(map[string]*advisoryLock) - - domain.BindDomain(s, dom) - // session implements variable.GlobalVarAccessor. Bind it to ctx. - s.sessionVars.GlobalVarsAccessor = s - s.sessionVars.BinlogClient = binloginfo.GetPumpsClient() - s.txn.init() - - sessionBindHandle := bindinfo.NewSessionBindHandle() - s.SetValue(bindinfo.SessionBindInfoKeyType, sessionBindHandle) - s.SetSessionStatesHandler(sessionstates.StateBinding, sessionBindHandle) - return s, nil -} - -// attachStatsCollector attaches the stats collector in the dom for the session -func attachStatsCollector(s *session, dom *domain.Domain) *session { - if dom.StatsHandle() != nil && dom.StatsUpdating() { - if s.statsCollector == nil { - s.statsCollector = dom.StatsHandle().NewSessionStatsItem().(*usage.SessionStatsItem) - } - if s.idxUsageCollector == nil && GetIndexUsageSyncLease() > 0 { - s.idxUsageCollector = dom.StatsHandle().NewSessionIndexUsageCollector().(*usage.SessionIndexUsageCollector) - } - } - - return s -} - -// detachStatsCollector removes the stats collector in the session -func detachStatsCollector(s *session) *session { - if s.statsCollector != nil { - s.statsCollector.Delete() - s.statsCollector = nil - } - if s.idxUsageCollector != nil { - s.idxUsageCollector.Delete() - s.idxUsageCollector = nil - } - return s -} - -// CreateSessionWithDomain creates a new Session and binds it with a Domain. -// We need this because when we start DDL in Domain, the DDL need a session -// to change some system tables. But at that time, we have been already in -// a lock context, which cause we can't call createSession directly. -func CreateSessionWithDomain(store kv.Storage, dom *domain.Domain) (*session, error) { - s := &session{ - store: store, - sessionVars: variable.NewSessionVars(nil), - client: store.GetClient(), - mppClient: store.GetMPPClient(), - stmtStats: stmtstats.CreateStatementStats(), - sessionStatesHandlers: make(map[sessionstates.SessionStateType]sessionctx.SessionStatesHandler), - } - s.functionUsageMu.builtinFunctionUsage = make(telemetry.BuiltinFunctionsUsage) - s.mu.values = make(map[fmt.Stringer]interface{}) - s.lockedTables = make(map[int64]model.TableLockTpInfo) - domain.BindDomain(s, dom) - // session implements variable.GlobalVarAccessor. Bind it to ctx. - s.sessionVars.GlobalVarsAccessor = s - s.txn.init() - return s, nil -} - -const ( - notBootstrapped = 0 -) - -func getStoreBootstrapVersion(store kv.Storage) int64 { - storeBootstrappedLock.Lock() - defer storeBootstrappedLock.Unlock() - // check in memory - _, ok := storeBootstrapped[store.UUID()] - if ok { - return currentBootstrapVersion - } - - var ver int64 - // check in kv store - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - err := kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - var err error - t := meta.NewMeta(txn) - ver, err = t.GetBootstrapVersion() - return err - }) - if err != nil { - logutil.BgLogger().Fatal("check bootstrapped failed", - zap.Error(err)) - } - - if ver > notBootstrapped { - // here mean memory is not ok, but other server has already finished it - storeBootstrapped[store.UUID()] = true - } - - modifyBootstrapVersionForTest(ver) - return ver -} - -func finishBootstrap(store kv.Storage) { - setStoreBootstrapped(store.UUID()) - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnBootstrap) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - t := meta.NewMeta(txn) - err := t.FinishBootstrap(currentBootstrapVersion) - return err - }) - if err != nil { - logutil.BgLogger().Fatal("finish bootstrap failed", - zap.Error(err)) - } -} - -const quoteCommaQuote = "', '" - -// loadCommonGlobalVariablesIfNeeded loads and applies commonly used global variables for the session. -func (s *session) loadCommonGlobalVariablesIfNeeded() error { - vars := s.sessionVars - if vars.CommonGlobalLoaded { - return nil - } - if s.Value(sessionctx.Initing) != nil { - // When running bootstrap or upgrade, we should not access global storage. - return nil - } - - vars.CommonGlobalLoaded = true - - // Deep copy sessionvar cache - sessionCache, err := domain.GetDomain(s).GetSessionCache() - if err != nil { - return err - } - for varName, varVal := range sessionCache { - if _, ok := vars.GetSystemVar(varName); !ok { - err = vars.SetSystemVarWithRelaxedValidation(varName, varVal) - if err != nil { - if variable.ErrUnknownSystemVar.Equal(err) { - continue // sessionCache is stale; sysvar has likely been unregistered - } - return err - } - } - } - // when client set Capability Flags CLIENT_INTERACTIVE, init wait_timeout with interactive_timeout - if vars.ClientCapability&mysql.ClientInteractive > 0 { - if varVal, ok := vars.GetSystemVar(variable.InteractiveTimeout); ok { - if err := vars.SetSystemVar(variable.WaitTimeout, varVal); err != nil { - return err - } - } - } - return nil -} - -// PrepareTxnCtx begins a transaction, and creates a new transaction context. -// It is called before we execute a sql query. -func (s *session) PrepareTxnCtx(ctx context.Context) error { - s.currentCtx = ctx - if s.txn.validOrPending() { - return nil - } - - txnMode := ast.Optimistic - if !s.sessionVars.IsAutocommit() || config.GetGlobalConfig().PessimisticTxn.PessimisticAutoCommit.Load() { - if s.sessionVars.TxnMode == ast.Pessimistic { - txnMode = ast.Pessimistic - } - } - - if s.sessionVars.RetryInfo.Retrying { - txnMode = ast.Pessimistic - } - - return sessiontxn.GetTxnManager(s).EnterNewTxn(ctx, &sessiontxn.EnterNewTxnRequest{ - Type: sessiontxn.EnterNewTxnBeforeStmt, - TxnMode: txnMode, - }) -} - -// PrepareTSFuture uses to try to get ts future. -func (s *session) PrepareTSFuture(ctx context.Context, future oracle.Future, scope string) error { - if s.txn.Valid() { - return errors.New("cannot prepare ts future when txn is valid") - } - - failpoint.Inject("assertTSONotRequest", func() { - if _, ok := future.(sessiontxn.ConstantFuture); !ok && !s.isInternal() { - panic("tso shouldn't be requested") - } - }) - - failpoint.InjectContext(ctx, "mockGetTSFail", func() { - future = txnFailFuture{} - }) - - s.txn.changeToPending(&txnFuture{ - future: future, - store: s.store, - txnScope: scope, - }) - return nil -} - -// GetPreparedTxnFuture returns the TxnFuture if it is valid or pending. -// It returns nil otherwise. -func (s *session) GetPreparedTxnFuture() sessionctx.TxnFuture { - if !s.txn.validOrPending() { - return nil - } - return &s.txn -} - -// RefreshTxnCtx implements context.RefreshTxnCtx interface. -func (s *session) RefreshTxnCtx(ctx context.Context) error { - var commitDetail *tikvutil.CommitDetails - ctx = context.WithValue(ctx, tikvutil.CommitDetailCtxKey, &commitDetail) - err := s.doCommit(ctx) - if commitDetail != nil { - s.GetSessionVars().StmtCtx.MergeExecDetails(nil, commitDetail) - } - if err != nil { - return err - } - - s.updateStatsDeltaToCollector() - - return sessiontxn.NewTxn(ctx, s) -} - -// GetStore gets the store of session. -func (s *session) GetStore() kv.Storage { - return s.store -} - -func (s *session) ShowProcess() *util.ProcessInfo { - var pi *util.ProcessInfo - tmp := s.processInfo.Load() - if tmp != nil { - pi = tmp.(*util.ProcessInfo) - } - return pi -} - -// GetStartTSFromSession returns the startTS in the session `se` -func GetStartTSFromSession(se interface{}) (startTS, processInfoID uint64) { - tmp, ok := se.(*session) - if !ok { - logutil.BgLogger().Error("GetStartTSFromSession failed, can't transform to session struct") - return 0, 0 - } - txnInfo := tmp.TxnInfo() - if txnInfo != nil { - startTS = txnInfo.StartTS - processInfoID = txnInfo.ConnectionID - } - - logutil.BgLogger().Debug( - "GetStartTSFromSession getting startTS of internal session", - zap.Uint64("startTS", startTS), zap.Time("start time", oracle.GetTimeFromTS(startTS))) - - return startTS, processInfoID -} - -// logStmt logs some crucial SQL including: CREATE USER/GRANT PRIVILEGE/CHANGE PASSWORD/DDL etc and normal SQL -// if variable.ProcessGeneralLog is set. -func logStmt(execStmt *executor.ExecStmt, s *session) { - vars := s.GetSessionVars() - isCrucial := false - switch stmt := execStmt.StmtNode.(type) { - case *ast.DropIndexStmt: - isCrucial = true - if stmt.IsHypo { - isCrucial = false - } - case *ast.CreateIndexStmt: - isCrucial = true - if stmt.IndexOption != nil && stmt.IndexOption.Tp == model.IndexTypeHypo { - isCrucial = false - } - case *ast.CreateUserStmt, *ast.DropUserStmt, *ast.AlterUserStmt, *ast.SetPwdStmt, *ast.GrantStmt, - *ast.RevokeStmt, *ast.AlterTableStmt, *ast.CreateDatabaseStmt, *ast.CreateTableStmt, - *ast.DropDatabaseStmt, *ast.DropTableStmt, *ast.RenameTableStmt, *ast.TruncateTableStmt, - *ast.RenameUserStmt: - isCrucial = true - } - - if isCrucial { - user := vars.User - schemaVersion := s.GetInfoSchema().SchemaMetaVersion() - if ss, ok := execStmt.StmtNode.(ast.SensitiveStmtNode); ok { - logutil.BgLogger().Info("CRUCIAL OPERATION", - zap.Uint64("conn", vars.ConnectionID), - zap.Int64("schemaVersion", schemaVersion), - zap.String("secure text", ss.SecureText()), - zap.Stringer("user", user)) - } else { - logutil.BgLogger().Info("CRUCIAL OPERATION", - zap.Uint64("conn", vars.ConnectionID), - zap.Int64("schemaVersion", schemaVersion), - zap.String("cur_db", vars.CurrentDB), - zap.String("sql", execStmt.StmtNode.Text()), - zap.Stringer("user", user)) - } - } else { - logGeneralQuery(execStmt, s, false) - } -} - -func logGeneralQuery(execStmt *executor.ExecStmt, s *session, isPrepared bool) { - vars := s.GetSessionVars() - if variable.ProcessGeneralLog.Load() && !vars.InRestrictedSQL { - var query string - if isPrepared { - query = execStmt.OriginText() - } else { - query = execStmt.GetTextToLog(false) - } - - query = executor.QueryReplacer.Replace(query) - if !vars.EnableRedactLog { - query += vars.PlanCacheParams.String() - } - logutil.BgLogger().Info("GENERAL_LOG", - zap.Uint64("conn", vars.ConnectionID), - zap.String("session_alias", vars.SessionAlias), - zap.String("user", vars.User.LoginString()), - zap.Int64("schemaVersion", s.GetInfoSchema().SchemaMetaVersion()), - zap.Uint64("txnStartTS", vars.TxnCtx.StartTS), - zap.Uint64("forUpdateTS", vars.TxnCtx.GetForUpdateTS()), - zap.Bool("isReadConsistency", vars.IsIsolation(ast.ReadCommitted)), - zap.String("currentDB", vars.CurrentDB), - zap.Bool("isPessimistic", vars.TxnCtx.IsPessimistic), - zap.String("sessionTxnMode", vars.GetReadableTxnMode()), - zap.String("sql", query)) - } -} - -func (s *session) recordOnTransactionExecution(err error, counter int, duration float64, isInternal bool) { - if s.sessionVars.TxnCtx.IsPessimistic { - if err != nil { - if isInternal { - session_metrics.TransactionDurationPessimisticAbortInternal.Observe(duration) - session_metrics.StatementPerTransactionPessimisticErrorInternal.Observe(float64(counter)) - } else { - session_metrics.TransactionDurationPessimisticAbortGeneral.Observe(duration) - session_metrics.StatementPerTransactionPessimisticErrorGeneral.Observe(float64(counter)) - } - } else { - if isInternal { - session_metrics.TransactionDurationPessimisticCommitInternal.Observe(duration) - session_metrics.StatementPerTransactionPessimisticOKInternal.Observe(float64(counter)) - } else { - session_metrics.TransactionDurationPessimisticCommitGeneral.Observe(duration) - session_metrics.StatementPerTransactionPessimisticOKGeneral.Observe(float64(counter)) - } - } - } else { - if err != nil { - if isInternal { - session_metrics.TransactionDurationOptimisticAbortInternal.Observe(duration) - session_metrics.StatementPerTransactionOptimisticErrorInternal.Observe(float64(counter)) - } else { - session_metrics.TransactionDurationOptimisticAbortGeneral.Observe(duration) - session_metrics.StatementPerTransactionOptimisticErrorGeneral.Observe(float64(counter)) - } - } else { - if isInternal { - session_metrics.TransactionDurationOptimisticCommitInternal.Observe(duration) - session_metrics.StatementPerTransactionOptimisticOKInternal.Observe(float64(counter)) - } else { - session_metrics.TransactionDurationOptimisticCommitGeneral.Observe(duration) - session_metrics.StatementPerTransactionOptimisticOKGeneral.Observe(float64(counter)) - } - } - } -} - -func (s *session) checkPlacementPolicyBeforeCommit() error { - var err error - // Get the txnScope of the transaction we're going to commit. - txnScope := s.GetSessionVars().TxnCtx.TxnScope - if txnScope == "" { - txnScope = kv.GlobalTxnScope - } - if txnScope != kv.GlobalTxnScope { - is := s.GetInfoSchema().(infoschema.InfoSchema) - deltaMap := s.GetSessionVars().TxnCtx.TableDeltaMap - for physicalTableID := range deltaMap { - var tableName string - var partitionName string - tblInfo, _, partInfo := is.FindTableByPartitionID(physicalTableID) - if tblInfo != nil && partInfo != nil { - tableName = tblInfo.Meta().Name.String() - partitionName = partInfo.Name.String() - } else { - tblInfo, _ := is.TableByID(physicalTableID) - tableName = tblInfo.Meta().Name.String() - } - bundle, ok := is.PlacementBundleByPhysicalTableID(physicalTableID) - if !ok { - errMsg := fmt.Sprintf("table %v doesn't have placement policies with txn_scope %v", - tableName, txnScope) - if len(partitionName) > 0 { - errMsg = fmt.Sprintf("table %v's partition %v doesn't have placement policies with txn_scope %v", - tableName, partitionName, txnScope) - } - err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) - break - } - dcLocation, ok := bundle.GetLeaderDC(placement.DCLabelKey) - if !ok { - errMsg := fmt.Sprintf("table %v's leader placement policy is not defined", tableName) - if len(partitionName) > 0 { - errMsg = fmt.Sprintf("table %v's partition %v's leader placement policy is not defined", tableName, partitionName) - } - err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) - break - } - if dcLocation != txnScope { - errMsg := fmt.Sprintf("table %v's leader location %v is out of txn_scope %v", tableName, dcLocation, txnScope) - if len(partitionName) > 0 { - errMsg = fmt.Sprintf("table %v's partition %v's leader location %v is out of txn_scope %v", - tableName, partitionName, dcLocation, txnScope) - } - err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs(errMsg) - break - } - // FIXME: currently we assume the physicalTableID is the partition ID. In future, we should consider the situation - // if the physicalTableID belongs to a Table. - partitionID := physicalTableID - tbl, _, partitionDefInfo := is.FindTableByPartitionID(partitionID) - if tbl != nil { - tblInfo := tbl.Meta() - state := tblInfo.Partition.GetStateByID(partitionID) - if state == model.StateGlobalTxnOnly { - err = dbterror.ErrInvalidPlacementPolicyCheck.GenWithStackByArgs( - fmt.Sprintf("partition %s of table %s can not be written by local transactions when its placement policy is being altered", - tblInfo.Name, partitionDefInfo.Name)) - break - } - } - } - } - return err -} - -func (s *session) SetPort(port string) { - s.sessionVars.Port = port -} - -// GetTxnWriteThroughputSLI implements the Context interface. -func (s *session) GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI { - return &s.txn.writeSLI -} - -// GetInfoSchema returns snapshotInfoSchema if snapshot schema is set. -// Transaction infoschema is returned if inside an explicit txn. -// Otherwise the latest infoschema is returned. -func (s *session) GetInfoSchema() sessionctx.InfoschemaMetaVersion { - vars := s.GetSessionVars() - var is infoschema.InfoSchema - if snap, ok := vars.SnapshotInfoschema.(infoschema.InfoSchema); ok { - logutil.BgLogger().Info("use snapshot schema", zap.Uint64("conn", vars.ConnectionID), zap.Int64("schemaVersion", snap.SchemaMetaVersion())) - is = snap - } else { - vars.TxnCtxMu.Lock() - if vars.TxnCtx != nil { - if tmp, ok := vars.TxnCtx.InfoSchema.(infoschema.InfoSchema); ok { - is = tmp - } - } - vars.TxnCtxMu.Unlock() - } - - if is == nil { - is = domain.GetDomain(s).InfoSchema() - } - - // Override the infoschema if the session has temporary table. - return temptable.AttachLocalTemporaryTableInfoSchema(s, is) -} - -func (s *session) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { - is := domain.GetDomain(s).InfoSchema() - extIs := &infoschema.SessionExtendedInfoSchema{InfoSchema: is} - return temptable.AttachLocalTemporaryTableInfoSchema(s, extIs) -} - -func getSnapshotInfoSchema(s sessionctx.Context, snapshotTS uint64) (infoschema.InfoSchema, error) { - is, err := domain.GetDomain(s).GetSnapshotInfoSchema(snapshotTS) - if err != nil { - return nil, err - } - // Set snapshot does not affect the witness of the local temporary table. - // The session always see the latest temporary tables. - return temptable.AttachLocalTemporaryTableInfoSchema(s, is), nil -} - -func (s *session) updateTelemetryMetric(es *executor.ExecStmt) { - if es.Ti == nil { - return - } - if s.isInternal() { - return - } - - ti := es.Ti - if ti.UseRecursive { - session_metrics.TelemetryCTEUsageRecurCTE.Inc() - } else if ti.UseNonRecursive { - session_metrics.TelemetryCTEUsageNonRecurCTE.Inc() - } else { - session_metrics.TelemetryCTEUsageNotCTE.Inc() - } - - if ti.UseIndexMerge { - session_metrics.TelemetryIndexMerge.Inc() - } - - if ti.UseMultiSchemaChange { - session_metrics.TelemetryMultiSchemaChangeUsage.Inc() - } - - if ti.UseFlashbackToCluster { - session_metrics.TelemetryFlashbackClusterUsage.Inc() - } - - if ti.UseExchangePartition { - session_metrics.TelemetryExchangePartitionUsage.Inc() - } - - if ti.PartitionTelemetry != nil { - if ti.PartitionTelemetry.UseTablePartition { - session_metrics.TelemetryTablePartitionUsage.Inc() - session_metrics.TelemetryTablePartitionMaxPartitionsUsage.Add(float64(ti.PartitionTelemetry.TablePartitionMaxPartitionsNum)) - } - if ti.PartitionTelemetry.UseTablePartitionList { - session_metrics.TelemetryTablePartitionListUsage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionRange { - session_metrics.TelemetryTablePartitionRangeUsage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionHash { - session_metrics.TelemetryTablePartitionHashUsage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionRangeColumns { - session_metrics.TelemetryTablePartitionRangeColumnsUsage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt1 { - session_metrics.TelemetryTablePartitionRangeColumnsGt1Usage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt2 { - session_metrics.TelemetryTablePartitionRangeColumnsGt2Usage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionRangeColumnsGt3 { - session_metrics.TelemetryTablePartitionRangeColumnsGt3Usage.Inc() - } - if ti.PartitionTelemetry.UseTablePartitionListColumns { - session_metrics.TelemetryTablePartitionListColumnsUsage.Inc() - } - if ti.PartitionTelemetry.UseCreateIntervalPartition { - session_metrics.TelemetryTablePartitionCreateIntervalUsage.Inc() - } - if ti.PartitionTelemetry.UseAddIntervalPartition { - session_metrics.TelemetryTablePartitionAddIntervalUsage.Inc() - } - if ti.PartitionTelemetry.UseDropIntervalPartition { - session_metrics.TelemetryTablePartitionDropIntervalUsage.Inc() - } - if ti.PartitionTelemetry.UseCompactTablePartition { - session_metrics.TelemetryTableCompactPartitionUsage.Inc() - } - if ti.PartitionTelemetry.UseReorganizePartition { - session_metrics.TelemetryReorganizePartitionUsage.Inc() - } - } - - if ti.AccountLockTelemetry != nil { - session_metrics.TelemetryLockUserUsage.Add(float64(ti.AccountLockTelemetry.LockUser)) - session_metrics.TelemetryUnlockUserUsage.Add(float64(ti.AccountLockTelemetry.UnlockUser)) - session_metrics.TelemetryCreateOrAlterUserUsage.Add(float64(ti.AccountLockTelemetry.CreateOrAlterUser)) - } - - if ti.UseTableLookUp.Load() && s.sessionVars.StoreBatchSize > 0 { - session_metrics.TelemetryStoreBatchedUsage.Inc() - } -} - -// GetBuiltinFunctionUsage returns the replica of counting of builtin function usage -func (s *session) GetBuiltinFunctionUsage() map[string]uint32 { - replica := make(map[string]uint32) - s.functionUsageMu.RLock() - defer s.functionUsageMu.RUnlock() - for key, value := range s.functionUsageMu.builtinFunctionUsage { - replica[key] = value - } - return replica -} - -// BuiltinFunctionUsageInc increase the counting of the builtin function usage -func (s *session) BuiltinFunctionUsageInc(scalarFuncSigName string) { - s.functionUsageMu.Lock() - defer s.functionUsageMu.Unlock() - s.functionUsageMu.builtinFunctionUsage.Inc(scalarFuncSigName) -} - -func (s *session) GetStmtStats() *stmtstats.StatementStats { - return s.stmtStats -} - -// SetMemoryFootprintChangeHook sets the hook that is called when the memdb changes its size. -// Call this after s.txn becomes valid, since TxnInfo is initialized when the txn becomes valid. -func (s *session) SetMemoryFootprintChangeHook() { - if config.GetGlobalConfig().Performance.TxnTotalSizeLimit != config.DefTxnTotalSizeLimit { - // if the user manually specifies the config, don't involve the new memory tracker mechanism, let the old config - // work as before. - return - } - hook := func(mem uint64) { - if s.sessionVars.MemDBFootprint == nil { - tracker := memory.NewTracker(memory.LabelForMemDB, -1) - tracker.AttachTo(s.sessionVars.MemTracker) - s.sessionVars.MemDBFootprint = tracker - } - s.sessionVars.MemDBFootprint.ReplaceBytesUsed(int64(mem)) - } - s.txn.SetMemoryFootprintChangeHook(hook) -} - -// EncodeSessionStates implements SessionStatesHandler.EncodeSessionStates interface. -func (s *session) EncodeSessionStates(ctx context.Context, - _ sessionctx.Context, sessionStates *sessionstates.SessionStates) error { - // Transaction status is hard to encode, so we do not support it. - s.txn.mu.Lock() - valid := s.txn.Valid() - s.txn.mu.Unlock() - if valid { - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has an active transaction") - } - // Data in local temporary tables is hard to encode, so we do not support it. - // Check temporary tables here to avoid circle dependency. - if s.sessionVars.LocalTemporaryTables != nil { - localTempTables := s.sessionVars.LocalTemporaryTables.(*infoschema.SessionTables) - if localTempTables.Count() > 0 { - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has local temporary tables") - } - } - // The advisory locks will be released when the session is closed. - if len(s.advisoryLocks) > 0 { - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has advisory locks") - } - // The TableInfo stores session ID and server ID, so the session cannot be migrated. - if len(s.lockedTables) > 0 { - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session has locked tables") - } - // It's insecure to migrate sandBoxMode because users can fake it. - if s.InSandBoxMode() { - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs("session is in sandbox mode") - } - - if err := s.sessionVars.EncodeSessionStates(ctx, sessionStates); err != nil { - return err - } - - hasRestrictVarPriv := false - checker := privilege.GetPrivilegeManager(s) - if checker == nil || checker.RequestDynamicVerification(s.sessionVars.ActiveRoles, "RESTRICTED_VARIABLES_ADMIN", false) { - hasRestrictVarPriv = true - } - // Encode session variables. We put it here instead of SessionVars to avoid cycle import. - sessionStates.SystemVars = make(map[string]string) - for _, sv := range variable.GetSysVars() { - switch { - case sv.HasNoneScope(), !sv.HasSessionScope(): - // Hidden attribute is deprecated. - // None-scoped variables cannot be modified. - // Noop variables should also be migrated even if they are noop. - continue - case sv.ReadOnly: - // Skip read-only variables here. We encode them into SessionStates manually. - continue - } - // Get all session variables because the default values may change between versions. - val, keep, err := s.sessionVars.GetSessionStatesSystemVar(sv.Name) - switch { - case err != nil: - return err - case !keep: - continue - case !hasRestrictVarPriv && sem.IsEnabled() && sem.IsInvisibleSysVar(sv.Name): - // If the variable has a global scope, it should be the same with the global one. - // Otherwise, it should be the same with the default value. - defaultVal := sv.Value - if sv.HasGlobalScope() { - // If the session value is the same with the global one, skip it. - if defaultVal, err = sv.GetGlobalFromHook(ctx, s.sessionVars); err != nil { - return err - } - } - if val != defaultVal { - // Case 1: the RESTRICTED_VARIABLES_ADMIN is revoked after setting the session variable. - // Case 2: the global variable is updated after the session is created. - // In any case, the variable can't be set in the new session, so give up. - return sessionstates.ErrCannotMigrateSession.GenWithStackByArgs(fmt.Sprintf("session has set invisible variable '%s'", sv.Name)) - } - default: - sessionStates.SystemVars[sv.Name] = val - } - } - - // Encode prepared statements and sql bindings. - for _, handler := range s.sessionStatesHandlers { - if err := handler.EncodeSessionStates(ctx, s, sessionStates); err != nil { - return err - } - } - return nil -} - -// DecodeSessionStates implements SessionStatesHandler.DecodeSessionStates interface. -func (s *session) DecodeSessionStates(ctx context.Context, - _ sessionctx.Context, sessionStates *sessionstates.SessionStates) error { - // Decode prepared statements and sql bindings. - for _, handler := range s.sessionStatesHandlers { - if err := handler.DecodeSessionStates(ctx, s, sessionStates); err != nil { - return err - } - } - - // Decode session variables. - names := variable.OrderByDependency(sessionStates.SystemVars) - // Some variables must be set before others, e.g. tidb_enable_noop_functions should be before noop variables. - for _, name := range names { - val := sessionStates.SystemVars[name] - // Experimental system variables may change scope, data types, or even be removed. - // We just ignore the errors and continue. - if err := s.sessionVars.SetSystemVar(name, val); err != nil { - logutil.Logger(ctx).Warn("set session variable during decoding session states error", - zap.String("name", name), zap.String("value", val), zap.Error(err)) - } - } - - // Decoding session vars / prepared statements may override stmt ctx, such as warnings, - // so we decode stmt ctx at last. - return s.sessionVars.DecodeSessionStates(ctx, sessionStates) -} - -func (s *session) setRequestSource(ctx context.Context, stmtLabel string, stmtNode ast.StmtNode) { - if !s.isInternal() { - if txn, _ := s.Txn(false); txn != nil && txn.Valid() { - txn.SetOption(kv.RequestSourceType, stmtLabel) - } - s.sessionVars.RequestSourceType = stmtLabel - return - } - if source := ctx.Value(kv.RequestSourceKey); source != nil { - requestSource := source.(kv.RequestSource) - if requestSource.RequestSourceType != "" { - s.sessionVars.RequestSourceType = requestSource.RequestSourceType - return - } - } - // panic in test mode in case there are requests without source in the future. - // log warnings in production mode. - if intest.InTest { - panic("unexpected no source type context, if you see this error, " + - "the `RequestSourceTypeKey` is missing in your context") - } else { - logutil.Logger(ctx).Warn("unexpected no source type context, if you see this warning, "+ - "the `RequestSourceTypeKey` is missing in the context", - zap.Bool("internal", s.isInternal()), - zap.String("sql", stmtNode.Text())) - } -} - -// RemoveLockDDLJobs removes the DDL jobs which doesn't get the metadata lock from job2ver. -func RemoveLockDDLJobs(s Session, job2ver map[int64]int64, job2ids map[int64]string, printLog bool) { - sv := s.GetSessionVars() - if sv.InRestrictedSQL { - return - } - sv.TxnCtxMu.Lock() - defer sv.TxnCtxMu.Unlock() - if sv.TxnCtx == nil { - return - } - sv.GetRelatedTableForMDL().Range(func(tblID, value any) bool { - for jobID, ver := range job2ver { - ids := util.Str2Int64Map(job2ids[jobID]) - if _, ok := ids[tblID.(int64)]; ok && value.(int64) < ver { - delete(job2ver, jobID) - elapsedTime := time.Since(oracle.GetTimeFromTS(sv.TxnCtx.StartTS)) - if elapsedTime > time.Minute && printLog { - logutil.BgLogger().Info("old running transaction block DDL", zap.Int64("table ID", tblID.(int64)), zap.Int64("jobID", jobID), zap.Uint64("connection ID", sv.ConnectionID), zap.Duration("elapsed time", elapsedTime)) - } else { - logutil.BgLogger().Debug("old running transaction block DDL", zap.Int64("table ID", tblID.(int64)), zap.Int64("jobID", jobID), zap.Uint64("connection ID", sv.ConnectionID), zap.Duration("elapsed time", elapsedTime)) - } - } - } - return true - }) -} - -// GetDBNames gets the sql layer database names from the session. -func GetDBNames(seVar *variable.SessionVars) []string { - dbNames := make(map[string]struct{}) - if seVar == nil || !config.GetGlobalConfig().Status.RecordDBLabel { - return []string{""} - } - if seVar.StmtCtx != nil { - for _, t := range seVar.StmtCtx.Tables { - dbNames[t.DB] = struct{}{} - } - } - if len(dbNames) == 0 { - dbNames[seVar.CurrentDB] = struct{}{} - } - ns := make([]string, 0, len(dbNames)) - for n := range dbNames { - ns = append(ns, n) - } - return ns -} diff --git a/session/temporarytabletest/BUILD.bazel b/session/temporarytabletest/BUILD.bazel deleted file mode 100644 index d42341e7d02ad..0000000000000 --- a/session/temporarytabletest/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "temporarytabletest_test", - timeout = "short", - srcs = [ - "main_test.go", - "temporary_table_test.go", - ], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//config", - "//domain", - "//kv", - "//parser/terror", - "//session", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/temporarytabletest/main_test.go b/session/temporarytabletest/main_test.go deleted file mode 100644 index 09bc6ca6301ce..0000000000000 --- a/session/temporarytabletest/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package temporarytabletest - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/temporarytabletest/temporary_table_test.go b/session/temporarytabletest/temporary_table_test.go deleted file mode 100644 index 1833fc0abe404..0000000000000 --- a/session/temporarytabletest/temporary_table_test.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package temporarytabletest - -import ( - "fmt" - "sort" - "strconv" - "strings" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestLocalTemporaryTableUpdate(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create temporary table tmp1 (id int primary key, u int unique, v int)") - - idList := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} - insertRecords := func(idList []int) { - for _, id := range idList { - tk.MustExec("insert into tmp1 values (?, ?, ?)", id, id+100, id+1000) - } - } - - checkNoChange := func() { - expect := make([]string, 0) - for _, id := range idList { - expect = append(expect, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) - } - tk.MustQuery("select * from tmp1").Check(testkit.Rows(expect...)) - } - - checkUpdatesAndDeletes := func(updates []string, deletes []int) { - modifyMap := make(map[int]string) - for _, m := range updates { - parts := strings.Split(strings.TrimSpace(m), " ") - require.NotZero(t, len(parts)) - id, err := strconv.Atoi(parts[0]) - require.NoError(t, err) - modifyMap[id] = m - } - - for _, d := range deletes { - modifyMap[d] = "" - } - - expect := make([]string, 0) - for _, id := range idList { - modify, exist := modifyMap[id] - if !exist { - expect = append(expect, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) - continue - } - - if modify != "" { - expect = append(expect, modify) - } - - delete(modifyMap, id) - } - - otherIds := make([]int, 0) - for id := range modifyMap { - otherIds = append(otherIds, id) - } - - sort.Ints(otherIds) - for _, id := range otherIds { - modify, exist := modifyMap[id] - require.True(t, exist) - expect = append(expect, modify) - } - - tk.MustQuery("select * from tmp1").Check(testkit.Rows(expect...)) - } - - type checkSuccess struct { - update []string - delete []int - } - - type checkError struct { - err error - } - - cases := []struct { - sql string - checkResult interface{} - additionalCheck func(error) - }{ - // update with point get for primary key - {"update tmp1 set v=999 where id=1", checkSuccess{[]string{"1 101 999"}, nil}, nil}, - {"update tmp1 set id=12 where id=1", checkSuccess{[]string{"12 101 1001"}, []int{1}}, nil}, - {"update tmp1 set id=1 where id=1", checkSuccess{nil, nil}, nil}, - {"update tmp1 set u=101 where id=1", checkSuccess{nil, nil}, nil}, - {"update tmp1 set v=999 where id=100", checkSuccess{nil, nil}, nil}, - {"update tmp1 set u=102 where id=100", checkSuccess{nil, nil}, nil}, - {"update tmp1 set u=21 where id=1", checkSuccess{[]string{"1 21 1001"}, nil}, func(_ error) { - // check index deleted - tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u=101").Check(testkit.Rows()) - tk.MustQuery("show warnings").Check(testkit.Rows()) - }}, - {"update tmp1 set id=2 where id=1", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set u=102 where id=1", checkError{kv.ErrKeyExists}, nil}, - // update with batch point get for primary key - {"update tmp1 set v=v+1000 where id in (1, 3, 5)", checkSuccess{[]string{"1 101 2001", "3 103 2003", "5 105 2005"}, nil}, nil}, - {"update tmp1 set u=u+1 where id in (9, 100)", checkSuccess{[]string{"9 110 1009"}, nil}, nil}, - {"update tmp1 set u=101 where id in (100, 101)", checkSuccess{nil, nil}, nil}, - {"update tmp1 set id=id+1 where id in (8, 9)", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set u=u+1 where id in (8, 9)", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set id=id+20 where id in (1, 3, 5)", checkSuccess{[]string{"21 101 1001", "23 103 1003", "25 105 1005"}, []int{1, 3, 5}}, nil}, - {"update tmp1 set u=u+100 where id in (1, 3, 5)", checkSuccess{[]string{"1 201 1001", "3 203 1003", "5 205 1005"}, nil}, func(_ error) { - // check index deleted - tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u in (101, 103, 105)").Check(testkit.Rows()) - tk.MustQuery("show warnings").Check(testkit.Rows()) - }}, - // update with point get for unique key - {"update tmp1 set v=888 where u=101", checkSuccess{[]string{"1 101 888"}, nil}, nil}, - {"update tmp1 set id=21 where u=101", checkSuccess{[]string{"21 101 1001"}, []int{1}}, nil}, - {"update tmp1 set v=888 where u=201", checkSuccess{nil, nil}, nil}, - {"update tmp1 set u=201 where u=101", checkSuccess{[]string{"1 201 1001"}, nil}, nil}, - {"update tmp1 set id=2 where u=101", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set u=102 where u=101", checkError{kv.ErrKeyExists}, nil}, - // update with batch point get for unique key - {"update tmp1 set v=v+1000 where u in (101, 103)", checkSuccess{[]string{"1 101 2001", "3 103 2003"}, nil}, nil}, - {"update tmp1 set v=v+1000 where u in (201, 203)", checkSuccess{nil, nil}, nil}, - {"update tmp1 set v=v+1000 where u in (101, 110)", checkSuccess{[]string{"1 101 2001"}, nil}, nil}, - {"update tmp1 set id=id+1 where u in (108, 109)", checkError{kv.ErrKeyExists}, nil}, - // update with table scan and index scan - {"update tmp1 set v=v+1000 where id<3", checkSuccess{[]string{"1 101 2001", "2 102 2002"}, nil}, nil}, - {"update /*+ use_index(tmp1, u) */ tmp1 set v=v+1000 where u>107", checkSuccess{[]string{"8 108 2008", "9 109 2009"}, nil}, nil}, - {"update tmp1 set v=v+1000 where v>=1007 or v<=1002", checkSuccess{[]string{"1 101 2001", "2 102 2002", "7 107 2007", "8 108 2008", "9 109 2009"}, nil}, nil}, - {"update tmp1 set v=v+1000 where id>=10", checkSuccess{nil, nil}, nil}, - {"update tmp1 set id=id+1 where id>7", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set id=id+1 where id>8", checkSuccess{[]string{"10 109 1009"}, []int{9}}, nil}, - {"update tmp1 set u=u+1 where u>107", checkError{kv.ErrKeyExists}, nil}, - {"update tmp1 set u=u+1 where u>108", checkSuccess{[]string{"9 110 1009"}, nil}, nil}, - {"update /*+ use_index(tmp1, u) */ tmp1 set v=v+1000 where u>108 or u<102", checkSuccess{[]string{"1 101 2001", "9 109 2009"}, nil}, nil}, - } - - executeSQL := func(sql string, checkResult interface{}, additionalCheck func(error)) (err error) { - switch check := checkResult.(type) { - case checkSuccess: - tk.MustExec(sql) - tk.MustQuery("show warnings").Check(testkit.Rows()) - checkUpdatesAndDeletes(check.update, check.delete) - case checkError: - err = tk.ExecToErr(sql) - require.Error(t, err) - expectedErr, _ := check.err.(*terror.Error) - require.True(t, expectedErr.Equal(err)) - checkNoChange() - default: - t.Fail() - } - - if additionalCheck != nil { - additionalCheck(err) - } - return - } - - for _, sqlCase := range cases { - // update records in txn and records are inserted in txn - tk.MustExec("begin") - insertRecords(idList) - _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) - tk.MustExec("rollback") - tk.MustQuery("select * from tmp1").Check(testkit.Rows()) - - // update records out of txn - insertRecords(idList) - _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) - tk.MustExec("delete from tmp1") - - // update records in txn and rollback - insertRecords(idList) - tk.MustExec("begin") - _ = executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) - tk.MustExec("rollback") - // rollback left records unmodified - checkNoChange() - - // update records in txn and commit - tk.MustExec("begin") - err := executeSQL(sqlCase.sql, sqlCase.checkResult, sqlCase.additionalCheck) - tk.MustExec("commit") - if err != nil { - checkNoChange() - } else { - r, _ := sqlCase.checkResult.(checkSuccess) - checkUpdatesAndDeletes(r.update, r.delete) - } - if sqlCase.additionalCheck != nil { - sqlCase.additionalCheck(err) - } - tk.MustExec("delete from tmp1") - tk.MustQuery("select * from tmp1").Check(testkit.Rows()) - } -} - -func TestRetryGlobalTemporaryTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists normal_table") - tk.MustExec("create table normal_table(a int primary key, b int)") - defer tk.MustExec("drop table if exists normal_table") - tk.MustExec("drop table if exists temp_table") - tk.MustExec("create global temporary table temp_table(a int primary key, b int) on commit delete rows") - defer tk.MustExec("drop table if exists temp_table") - - // insert select - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - tk.MustExec("insert normal_table value(100, 100)") - tk.MustExec("set @@autocommit = 0") - // used to make conflicts - tk.MustExec("update normal_table set b=b+1 where a=100") - tk.MustExec("insert temp_table value(1, 1)") - tk.MustExec("insert normal_table select * from temp_table") - require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) - - // try to conflict with tk - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("update normal_table set b=b+1 where a=100") - - // It will retry internally. - tk.MustExec("commit") - tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 1", "100 102")) - tk.MustQuery("select a, b from temp_table order by a").Check(testkit.Rows()) - - // update multi-tables - tk.MustExec("update normal_table set b=b+1 where a=100") - tk.MustExec("insert temp_table value(1, 2)") - // before update: normal_table=(1 1) (100 102), temp_table=(1 2) - tk.MustExec("update normal_table, temp_table set normal_table.b=temp_table.b where normal_table.a=temp_table.a") - require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) - - // try to conflict with tk - tk1.MustExec("update normal_table set b=b+1 where a=100") - - // It will retry internally. - tk.MustExec("commit") - tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 104")) -} - -func TestRetryLocalTemporaryTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists normal_table") - tk.MustExec("create table normal_table(a int primary key, b int)") - defer tk.MustExec("drop table if exists normal_table") - tk.MustExec("drop table if exists temp_table") - tk.MustExec("create temporary table l_temp_table(a int primary key, b int)") - defer tk.MustExec("drop table if exists l_temp_table") - - // insert select - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - tk.MustExec("insert normal_table value(100, 100)") - tk.MustExec("set @@autocommit = 0") - // used to make conflicts - tk.MustExec("update normal_table set b=b+1 where a=100") - tk.MustExec("insert l_temp_table value(1, 2)") - tk.MustExec("insert normal_table select * from l_temp_table") - require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) - - // try to conflict with tk - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("update normal_table set b=b+1 where a=100") - - // It will retry internally. - tk.MustExec("commit") - tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 102")) - tk.MustQuery("select a, b from l_temp_table order by a").Check(testkit.Rows("1 2")) - - // update multi-tables - tk.MustExec("update normal_table set b=b+1 where a=100") - tk.MustExec("insert l_temp_table value(3, 4)") - // before update: normal_table=(1 1) (100 102), temp_table=(1 2) - tk.MustExec("update normal_table, l_temp_table set normal_table.b=l_temp_table.b where normal_table.a=l_temp_table.a") - require.Equal(t, 3, session.GetHistory(tk.Session()).Count()) - - // try to conflict with tk - tk1.MustExec("update normal_table set b=b+1 where a=100") - - // It will retry internally. - tk.MustExec("commit") - tk.MustQuery("select a, b from normal_table order by a").Check(testkit.Rows("1 2", "100 104")) -} - -func TestLocalTemporaryTableDelete(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create temporary table tmp1 (id int primary key, u int unique, v int)") - - insertRecords := func(idList []int) { - for _, id := range idList { - tk.MustExec("insert into tmp1 values (?, ?, ?)", id, id+100, id+1000) - } - } - - checkAllExistRecords := func(idList []int) { - sort.Ints(idList) - expectedResult := make([]string, 0, len(idList)) - expectedIndexResult := make([]string, 0, len(idList)) - for _, id := range idList { - expectedResult = append(expectedResult, fmt.Sprintf("%d %d %d", id, id+100, id+1000)) - expectedIndexResult = append(expectedIndexResult, fmt.Sprintf("%d", id+100)) - } - tk.MustQuery("select * from tmp1 order by id").Check(testkit.Rows(expectedResult...)) - - // check index deleted - tk.MustQuery("select /*+ use_index(tmp1, u) */ u from tmp1 order by u").Check(testkit.Rows(expectedIndexResult...)) - tk.MustQuery("show warnings").Check(testkit.Rows()) - } - - assertDelete := func(sql string, deleted []int) { - idList := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} - - deletedMap := make(map[int]bool) - for _, id := range deleted { - deletedMap[id] = true - } - - keepList := make([]int, 0) - for _, id := range idList { - if _, exist := deletedMap[id]; !exist { - keepList = append(keepList, id) - } - } - - // delete records in txn and records are inserted in txn - tk.MustExec("begin") - insertRecords(idList) - tk.MustExec(sql) - tk.MustQuery("show warnings").Check(testkit.Rows()) - checkAllExistRecords(keepList) - tk.MustExec("rollback") - checkAllExistRecords([]int{}) - - // delete records out of txn - insertRecords(idList) - tk.MustExec(sql) - checkAllExistRecords(keepList) - - // delete records in txn - insertRecords(deleted) - tk.MustExec("begin") - tk.MustExec(sql) - checkAllExistRecords(keepList) - - // test rollback - tk.MustExec("rollback") - checkAllExistRecords(idList) - - // test commit - tk.MustExec("begin") - tk.MustExec(sql) - tk.MustExec("commit") - checkAllExistRecords(keepList) - - tk.MustExec("delete from tmp1") - checkAllExistRecords([]int{}) - } - - assertDelete("delete from tmp1 where id=1", []int{1}) - assertDelete("delete from tmp1 where id in (1, 3, 5)", []int{1, 3, 5}) - assertDelete("delete from tmp1 where u=102", []int{2}) - assertDelete("delete from tmp1 where u in (103, 107, 108)", []int{3, 7, 8}) - assertDelete("delete from tmp1 where id=10", []int{}) - assertDelete("delete from tmp1 where id in (10, 12)", []int{}) - assertDelete("delete from tmp1 where u=110", []int{}) - assertDelete("delete from tmp1 where u in (111, 112)", []int{}) - assertDelete("delete from tmp1 where id in (1, 11, 5)", []int{1, 5}) - assertDelete("delete from tmp1 where u in (102, 121, 106)", []int{2, 6}) - assertDelete("delete from tmp1 where id<3", []int{1, 2}) - assertDelete("delete from tmp1 where u>107", []int{8, 9}) - assertDelete("delete /*+ use_index(tmp1, u) */ from tmp1 where u>105 and u<107", []int{6}) - assertDelete("delete from tmp1 where v>=1006 or v<=1002", []int{1, 2, 6, 7, 8, 9}) -} - -func TestSchemaCheckerTempTable(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) - - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk1.MustExec("set global tidb_enable_metadata_lock=0") - tk2.MustExec("use test") - - // create table - tk1.MustExec(`drop table if exists normal_table`) - tk1.MustExec(`create table normal_table (id int, c int);`) - defer tk1.MustExec(`drop table if exists normal_table`) - tk1.MustExec(`drop table if exists temp_table`) - tk1.MustExec(`create global temporary table temp_table (id int primary key, c int) on commit delete rows;`) - defer tk1.MustExec(`drop table if exists temp_table`) - - // The schema version is out of date in the first transaction, and the SQL can't be retried. - atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 1) - defer func() { - atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 0) - }() - - // It's fine to change the schema of temporary tables. - tk1.MustExec(`begin;`) - tk2.MustExec(`alter table temp_table modify column c tinyint;`) - tk1.MustExec(`insert into temp_table values(3, 3);`) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c int;`) - tk1.MustQuery(`select * from temp_table for update;`).Check(testkit.Rows()) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c smallint;`) - tk1.MustExec(`insert into temp_table values(3, 4);`) - tk1.MustQuery(`select * from temp_table for update;`).Check(testkit.Rows("3 4")) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c bigint;`) - tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows()) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c smallint;`) - tk1.MustExec("insert into temp_table values (1, 2), (2, 3), (4, 5)") - tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows("1 2")) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c int;`) - tk1.MustQuery(`select * from temp_table where id=1 for update;`).Check(testkit.Rows()) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c bigint;`) - tk1.MustQuery(`select * from temp_table where id in (1, 2, 3) for update;`).Check(testkit.Rows()) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c int;`) - tk1.MustExec("insert into temp_table values (1, 2), (2, 3), (4, 5)") - tk1.MustQuery(`select * from temp_table where id in (1, 2, 3) for update;`).Check(testkit.Rows("1 2", "2 3")) - tk1.MustExec(`commit;`) - - tk1.MustExec("insert into normal_table values(1, 2)") - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table temp_table modify column c int;`) - tk1.MustExec(`insert into temp_table values(1, 5);`) - tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows("1 5 1 2")) - tk1.MustExec(`commit;`) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table normal_table modify column c bigint;`) - tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows()) - tk1.MustExec(`commit;`) - - // Truncate will modify table ID. - tk1.MustExec(`begin;`) - tk2.MustExec(`truncate table temp_table;`) - tk1.MustExec(`insert into temp_table values(3, 3);`) - tk1.MustExec(`commit;`) - - // It reports error when also changing the schema of a normal table. - tk1.MustExec(`begin;`) - tk2.MustExec(`alter table normal_table modify column c bigint;`) - tk1.MustExec(`insert into temp_table values(3, 3);`) - tk1.MustExec(`insert into normal_table values(3, 3);`) - err := tk1.ExecToErr(`commit;`) - require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) - - tk1.MustExec("begin pessimistic") - tk2.MustExec(`alter table normal_table modify column c int;`) - tk1.MustExec(`insert into temp_table values(1, 6);`) - tk1.MustQuery(`select * from temp_table, normal_table where temp_table.id = normal_table.id for update;`).Check(testkit.Rows("1 6 1 2")) - err = tk1.ExecToErr(`commit;`) - require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) -} diff --git a/session/test/BUILD.bazel b/session/test/BUILD.bazel deleted file mode 100644 index 304022ea06fb5..0000000000000 --- a/session/test/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "test_test", - timeout = "short", - srcs = [ - "main_test.go", - "session_test.go", - ], - flaky = True, - shard_count = 26, - deps = [ - "//config", - "//domain", - "//expression", - "//kv", - "//parser/ast", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//session", - "//store/mockstore", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util", - "//util/sqlexec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/common/BUILD.bazel b/session/test/common/BUILD.bazel deleted file mode 100644 index 6fe0b9fc426a5..0000000000000 --- a/session/test/common/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "common_test", - timeout = "short", - srcs = [ - "common_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 8, - deps = [ - "//config", - "//expression", - "//kv", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//table/tables", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/common/common_test.go b/session/test/common/common_test.go deleted file mode 100644 index 707feadaad94e..0000000000000 --- a/session/test/common/common_test.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package common - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestMiscs(t *testing.T) { - store := testkit.CreateMockStore(t) - - // TestString - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("select 1") - // here to check the panic bug in String() when txn is nil after committed. - t.Log(tk.Session().String()) - - // TestLastExecuteDDLFlag - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(id int)") - require.NotNil(t, tk.Session().Value(sessionctx.LastExecuteDDL)) - tk.MustExec("insert into t1 values (1)") - require.Nil(t, tk.Session().Value(sessionctx.LastExecuteDDL)) - - // TestSession - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("ROLLBACK;") - tk.Session().Close() -} - -func TestPrepare(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id TEXT)") - tk.MustExec(`INSERT INTO t VALUES ("id");`) - id, ps, _, err := tk.Session().PrepareStmt("select id+? from t") - ctx := context.Background() - require.NoError(t, err) - require.Equal(t, uint32(1), id) - require.Equal(t, 1, ps) - tk.MustExec(`set @a=1`) - rs, err := tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test("1")) - require.NoError(t, err) - require.NoError(t, rs.Close()) - err = tk.Session().DropPreparedStmt(id) - require.NoError(t, err) - - tk.MustExec("prepare stmt from 'select 1+?'") - tk.MustExec("set @v1=100") - tk.MustQuery("execute stmt using @v1").Check(testkit.Rows("101")) - - tk.MustExec("set @v2=200") - tk.MustQuery("execute stmt using @v2").Check(testkit.Rows("201")) - - tk.MustExec("set @v3=300") - tk.MustQuery("execute stmt using @v3").Check(testkit.Rows("301")) - tk.MustExec("deallocate prepare stmt") - - // Execute prepared statements for more than one time. - tk.MustExec("create table multiexec (a int, b int)") - tk.MustExec("insert multiexec values (1, 1), (2, 2)") - id, _, _, err = tk.Session().PrepareStmt("select a from multiexec where b = ? order by b") - require.NoError(t, err) - rs, err = tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - require.NoError(t, rs.Close()) - rs, err = tk.Session().ExecutePreparedStmt(ctx, id, expression.Args2Expressions4Test(2)) - require.NoError(t, err) - require.NoError(t, rs.Close()) -} - -func TestIndexColumnLength(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (c1 int, c2 blob);") - tk.MustExec("create index idx_c1 on t(c1);") - tk.MustExec("create index idx_c2 on t(c2(6));") - - is := dom.InfoSchema() - tab, err2 := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err2) - - idxC1Cols := tables.FindIndexByColName(tab, "c1").Meta().Columns - require.Equal(t, types.UnspecifiedLength, idxC1Cols[0].Length) - - idxC2Cols := tables.FindIndexByColName(tab, "c2").Meta().Columns - require.Equal(t, 6, idxC2Cols[0].Length) -} - -// test for https://github.com/pingcap/tidb/pull/461 -func TestUnique(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - tk.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk1.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk.MustExec(`CREATE TABLE test ( id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, val int UNIQUE, PRIMARY KEY (id)); `) - tk.MustExec("begin;") - tk.MustExec("insert into test(id, val) values(1, 1);") - tk1.MustExec("begin;") - tk1.MustExec("insert into test(id, val) values(2, 2);") - tk2.MustExec("begin;") - tk2.MustExec("insert into test(id, val) values(1, 2);") - tk2.MustExec("commit;") - _, err := tk.Exec("commit") - require.Error(t, err) - // Check error type and error message - require.True(t, terror.ErrorEqual(err, kv.ErrKeyExists), fmt.Sprintf("err %v", err)) - require.Equal(t, "previous statement: insert into test(id, val) values(1, 1);: [kv:1062]Duplicate entry '1' for key 'test.PRIMARY'", err.Error()) - - _, err = tk1.Exec("commit") - require.Error(t, err) - require.True(t, terror.ErrorEqual(err, kv.ErrKeyExists), fmt.Sprintf("err %v", err)) - require.Equal(t, "previous statement: insert into test(id, val) values(2, 2);: [kv:1062]Duplicate entry '2' for key 'test.val'", err.Error()) - - // Test for https://github.com/pingcap/tidb/issues/463 - tk.MustExec("drop table test;") - tk.MustExec(`CREATE TABLE test ( - id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, - val int UNIQUE, - PRIMARY KEY (id) - );`) - tk.MustExec("insert into test(id, val) values(1, 1);") - _, err = tk.Exec("insert into test(id, val) values(2, 1);") - require.Error(t, err) - tk.MustExec("insert into test(id, val) values(2, 2);") - - tk.MustExec("begin;") - tk.MustExec("insert into test(id, val) values(3, 3);") - _, err = tk.Exec("insert into test(id, val) values(4, 3);") - require.Error(t, err) - tk.MustExec("insert into test(id, val) values(4, 4);") - tk.MustExec("commit;") - - tk1.MustExec("begin;") - tk1.MustExec("insert into test(id, val) values(5, 6);") - tk.MustExec("begin;") - tk.MustExec("insert into test(id, val) values(20, 6);") - tk.MustExec("commit;") - _, _ = tk1.Exec("commit") - tk1.MustExec("insert into test(id, val) values(5, 5);") - - tk.MustExec("drop table test;") - tk.MustExec(`CREATE TABLE test ( - id int(11) UNSIGNED NOT NULL AUTO_INCREMENT, - val1 int UNIQUE, - val2 int UNIQUE, - PRIMARY KEY (id) - );`) - tk.MustExec("insert into test(id, val1, val2) values(1, 1, 1);") - tk.MustExec("insert into test(id, val1, val2) values(2, 2, 2);") - _, _ = tk.Exec("update test set val1 = 3, val2 = 2 where id = 1;") - tk.MustExec("insert into test(id, val1, val2) values(3, 3, 3);") -} - -func TestTableInfoMeta(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - checkResult := func(affectedRows uint64, insertID uint64) { - gotRows := tk.Session().AffectedRows() - require.Equal(t, affectedRows, gotRows) - - gotID := tk.Session().LastInsertID() - require.Equal(t, insertID, gotID) - } - - // create table - tk.MustExec("CREATE TABLE tbl_test(id INT NOT NULL DEFAULT 1, name varchar(255), PRIMARY KEY(id));") - - // insert data - tk.MustExec(`INSERT INTO tbl_test VALUES (1, "hello");`) - checkResult(1, 0) - - tk.MustExec(`INSERT INTO tbl_test VALUES (2, "hello");`) - checkResult(1, 0) - - tk.MustExec(`UPDATE tbl_test SET name = "abc" where id = 2;`) - checkResult(1, 0) - - tk.MustExec(`DELETE from tbl_test where id = 2;`) - checkResult(1, 0) - - // select data - tk.MustQuery("select * from tbl_test").Check(testkit.Rows("1 hello")) -} - -func TestLastMessage(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id TEXT)") - - // Insert - tk.MustExec(`INSERT INTO t VALUES ("a");`) - tk.CheckLastMessage("") - tk.MustExec(`INSERT INTO t VALUES ("b"), ("c");`) - tk.CheckLastMessage("Records: 2 Duplicates: 0 Warnings: 0") - - // Update - tk.MustExec(`UPDATE t set id = 'c' where id = 'a';`) - require.Equal(t, uint64(1), tk.Session().AffectedRows()) - tk.CheckLastMessage("Rows matched: 1 Changed: 1 Warnings: 0") - tk.MustExec(`UPDATE t set id = 'a' where id = 'a';`) - require.Equal(t, uint64(0), tk.Session().AffectedRows()) - tk.CheckLastMessage("Rows matched: 0 Changed: 0 Warnings: 0") - - // Replace - tk.MustExec(`drop table if exists t, t1; - create table t (c1 int PRIMARY KEY, c2 int); - create table t1 (a1 int, a2 int);`) - tk.MustExec(`INSERT INTO t VALUES (1,1)`) - tk.MustExec(`REPLACE INTO t VALUES (2,2)`) - tk.CheckLastMessage("") - tk.MustExec(`INSERT INTO t1 VALUES (1,10), (3,30);`) - tk.CheckLastMessage("Records: 2 Duplicates: 0 Warnings: 0") - tk.MustExec(`REPLACE INTO t SELECT * from t1`) - tk.CheckLastMessage("Records: 2 Duplicates: 1 Warnings: 0") - - // Check insert with CLIENT_FOUND_ROWS is set - tk.Session().SetClientCapability(mysql.ClientFoundRows) - tk.MustExec(`drop table if exists t, t1; - create table t (c1 int PRIMARY KEY, c2 int); - create table t1 (a1 int, a2 int);`) - tk.MustExec(`INSERT INTO t1 VALUES (1, 10), (2, 2), (3, 30);`) - tk.MustExec(`INSERT INTO t1 VALUES (1, 10), (2, 20), (3, 30);`) - tk.MustExec(`INSERT INTO t SELECT * FROM t1 ON DUPLICATE KEY UPDATE c2=a2;`) - tk.CheckLastMessage("Records: 6 Duplicates: 3 Warnings: 0") -} - -func TestQueryString(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table mutil1 (a int);create table multi2 (a int)") - queryStr := tk.Session().Value(sessionctx.QueryString) - require.Equal(t, "create table multi2 (a int)", queryStr) - - // Test execution of DDL through the "ExecutePreparedStmt" interface. - tk.MustExec("use test") - tk.MustExec("CREATE TABLE t (id bigint PRIMARY KEY, age int)") - tk.MustExec("show create table t") - id, _, _, err := tk.Session().PrepareStmt("CREATE TABLE t2(id bigint PRIMARY KEY, age int)") - require.NoError(t, err) - _, err = tk.Session().ExecutePreparedStmt(context.Background(), id, expression.Args2Expressions4Test()) - require.NoError(t, err) - qs := tk.Session().Value(sessionctx.QueryString) - require.Equal(t, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)", qs.(string)) - - // Test execution of DDL through the "Execute" interface. - tk.MustExec("use test") - tk.MustExec("drop table t2") - tk.MustExec("prepare stmt from 'CREATE TABLE t2(id bigint PRIMARY KEY, age int)'") - tk.MustExec("execute stmt") - qs = tk.Session().Value(sessionctx.QueryString) - require.Equal(t, "CREATE TABLE t2(id bigint PRIMARY KEY, age int)", qs.(string)) -} - -func TestAffectedRows(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id TEXT)") - tk.MustExec(`INSERT INTO t VALUES ("a");`) - require.Equal(t, 1, int(tk.Session().AffectedRows())) - tk.MustExec(`INSERT INTO t VALUES ("b");`) - require.Equal(t, 1, int(tk.Session().AffectedRows())) - tk.MustExec(`UPDATE t set id = 'c' where id = 'a';`) - require.Equal(t, 1, int(tk.Session().AffectedRows())) - tk.MustExec(`UPDATE t set id = 'a' where id = 'a';`) - require.Equal(t, 0, int(tk.Session().AffectedRows())) - tk.MustQuery(`SELECT * from t`).Check(testkit.Rows("c", "b")) - require.Equal(t, 0, int(tk.Session().AffectedRows())) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int, data int)") - tk.MustExec(`INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`) - tk.MustExec(`UPDATE t set id = 1 where data = 0;`) - require.Equal(t, 1, int(tk.Session().AffectedRows())) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int, c1 timestamp);") - tk.MustExec(`insert t(id) values(1);`) - tk.MustExec(`UPDATE t set id = 1 where id = 1;`) - require.Equal(t, 0, int(tk.Session().AffectedRows())) - - // With ON DUPLICATE KEY UPDATE, the affected-rows value per row is 1 if the row is inserted as a new row, - // 2 if an existing row is updated, and 0 if an existing row is set to its current values. - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (c1 int PRIMARY KEY, c2 int);") - tk.MustExec(`insert t values(1, 1);`) - tk.MustExec(`insert into t values (1, 1) on duplicate key update c2=2;`) - require.Equal(t, 2, int(tk.Session().AffectedRows())) - tk.MustExec(`insert into t values (1, 1) on duplicate key update c2=2;`) - require.Equal(t, 0, int(tk.Session().AffectedRows())) - tk.MustExec("drop table if exists test") - createSQL := `CREATE TABLE test ( - id VARCHAR(36) PRIMARY KEY NOT NULL, - factor INTEGER NOT NULL DEFAULT 2);` - tk.MustExec(createSQL) - insertSQL := `INSERT INTO test(id) VALUES('id') ON DUPLICATE KEY UPDATE factor=factor+3;` - tk.MustExec(insertSQL) - require.Equal(t, 1, int(tk.Session().AffectedRows())) - tk.MustExec(insertSQL) - require.Equal(t, 2, int(tk.Session().AffectedRows())) - tk.MustExec(insertSQL) - require.Equal(t, 2, int(tk.Session().AffectedRows())) - - tk.Session().SetClientCapability(mysql.ClientFoundRows) - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int, data int)") - tk.MustExec(`INSERT INTO t VALUES (1, 0), (0, 0), (1, 1);`) - tk.MustExec(`UPDATE t set id = 1 where data = 0;`) - require.Equal(t, 2, int(tk.Session().AffectedRows())) -} diff --git a/session/test/common/main_test.go b/session/test/common/main_test.go deleted file mode 100644 index 66ebe4a7b1454..0000000000000 --- a/session/test/common/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package common - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/main_test.go b/session/test/main_test.go deleted file mode 100644 index 0e1beb994546d..0000000000000 --- a/session/test/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package test - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/meta/BUILD.bazel b/session/test/meta/BUILD.bazel deleted file mode 100644 index 07f48e5e07730..0000000000000 --- a/session/test/meta/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "meta_test", - timeout = "short", - srcs = [ - "main_test.go", - "session_test.go", - ], - flaky = True, - shard_count = 4, - deps = [ - "//config", - "//ddl", - "//metrics", - "//session", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/meta/main_test.go b/session/test/meta/main_test.go deleted file mode 100644 index aaac91bf25853..0000000000000 --- a/session/test/meta/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package meta - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/meta/session_test.go b/session/test/meta/session_test.go deleted file mode 100644 index 752cffbf338e7..0000000000000 --- a/session/test/meta/session_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package meta_test - -import ( - "fmt" - "reflect" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/prometheus/client_golang/prometheus" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" -) - -func TestInitMetaTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - for _, sql := range session.DDLJobTables { - tk.MustExec(sql.SQL) - } - - for _, sql := range session.BackfillTables { - tk.MustExec(sql.SQL) - } - - tbls := map[string]struct{}{ - "tidb_ddl_job": {}, - "tidb_ddl_reorg": {}, - "tidb_ddl_history": {}, - "tidb_background_subtask": {}, - "tidb_background_subtask_history": {}, - } - - for tbl := range tbls { - metaInMySQL := external.GetTableByName(t, tk, "mysql", tbl).Meta().Clone() - metaInTest := external.GetTableByName(t, tk, "test", tbl).Meta().Clone() - - require.Greater(t, metaInMySQL.ID, int64(0)) - require.Greater(t, metaInMySQL.UpdateTS, uint64(0)) - - metaInTest.ID = metaInMySQL.ID - metaInMySQL.UpdateTS = metaInTest.UpdateTS - require.True(t, reflect.DeepEqual(metaInMySQL, metaInTest)) - } -} - -func TestMetaTableRegion(t *testing.T) { - enableSplitTableRegionVal := atomic.LoadUint32(&ddl.EnableSplitTableRegion) - atomic.StoreUint32(&ddl.EnableSplitTableRegion, 1) - defer atomic.StoreUint32(&ddl.EnableSplitTableRegion, enableSplitTableRegionVal) - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - - ddlReorgTableRegionID := tk.MustQuery("show table mysql.tidb_ddl_reorg regions").Rows()[0][0] - ddlReorgTableRegionStartKey := tk.MustQuery("show table mysql.tidb_ddl_reorg regions").Rows()[0][1] - require.Equal(t, ddlReorgTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.ReorgTableID)) - - ddlJobTableRegionID := tk.MustQuery("show table mysql.tidb_ddl_job regions").Rows()[0][0] - ddlJobTableRegionStartKey := tk.MustQuery("show table mysql.tidb_ddl_job regions").Rows()[0][1] - require.Equal(t, ddlJobTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.JobTableID)) - - require.NotEqual(t, ddlJobTableRegionID, ddlReorgTableRegionID) - - ddlBackfillTableRegionID := tk.MustQuery("show table mysql.tidb_background_subtask regions").Rows()[0][0] - ddlBackfillTableRegionStartKey := tk.MustQuery("show table mysql.tidb_background_subtask regions").Rows()[0][1] - require.Equal(t, ddlBackfillTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.BackgroundSubtaskTableID)) - ddlBackfillHistoryTableRegionID := tk.MustQuery("show table mysql.tidb_background_subtask_history regions").Rows()[0][0] - ddlBackfillHistoryTableRegionStartKey := tk.MustQuery("show table mysql.tidb_background_subtask_history regions").Rows()[0][1] - require.Equal(t, ddlBackfillHistoryTableRegionStartKey, fmt.Sprintf("%s_%d_", tablecodec.TablePrefix(), ddl.BackgroundSubtaskHistoryTableID)) - - require.NotEqual(t, ddlBackfillTableRegionID, ddlBackfillHistoryTableRegionID) -} - -func MustReadCounter(t *testing.T, m prometheus.Counter) float64 { - pb := &dto.Metric{} - require.NoError(t, m.Write(pb)) - return pb.GetCounter().GetValue() -} - -func TestRecordTTLRows(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec("use test") - tk.MustExec("create table t(created_at datetime) TTL = created_at + INTERVAL 1 DAY") - // simple insert should be recorded - tk.MustExec("insert into t values (NOW())") - require.Equal(t, 1.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) - - // insert in a explicit transaction should be recorded - tk.MustExec("begin") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("commit") - require.Equal(t, 2.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) - - // insert multiple rows should be the same - tk.MustExec("begin") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("commit") - require.Equal(t, 4.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) - - // rollback will remove all recorded TTL rows - tk.MustExec("begin") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("rollback") - require.Equal(t, 6.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) - - // savepoint will save the recorded TTL rows - tk.MustExec("begin") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("savepoint insert1") - tk.MustExec("insert into t values (NOW())") - tk.MustExec("rollback to insert1") - tk.MustExec("commit") - require.Equal(t, 7.0, MustReadCounter(t, metrics.TTLInsertRowsCount)) -} - -func TestInformationSchemaCreateTime(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (c int)") - tk.MustExec(`set @@time_zone = 'Asia/Shanghai'`) - ret := tk.MustQuery("select create_time from information_schema.tables where table_name='t';") - // Make sure t1 is greater than t. - time.Sleep(time.Second) - tk.MustExec("alter table t modify c int default 11") - ret1 := tk.MustQuery("select create_time from information_schema.tables where table_name='t';") - ret2 := tk.MustQuery("show table status like 't'") - require.Equal(t, ret2.Rows()[0][11].(string), ret1.Rows()[0][0].(string)) - typ1, err := types.ParseDatetime(nil, ret.Rows()[0][0].(string)) - require.NoError(t, err) - typ2, err := types.ParseDatetime(nil, ret1.Rows()[0][0].(string)) - require.NoError(t, err) - r := typ2.Compare(typ1) - require.Equal(t, 1, r) - // Check that time_zone changes makes the create_time different - tk.MustExec(`set @@time_zone = 'Europe/Amsterdam'`) - ret = tk.MustQuery(`select create_time from information_schema.tables where table_name='t'`) - ret2 = tk.MustQuery(`show table status like 't'`) - require.Equal(t, ret2.Rows()[0][11].(string), ret.Rows()[0][0].(string)) - typ3, err := types.ParseDatetime(nil, ret.Rows()[0][0].(string)) - require.NoError(t, err) - // Asia/Shanghai 2022-02-17 17:40:05 > Europe/Amsterdam 2022-02-17 10:40:05 - r = typ2.Compare(typ3) - require.Equal(t, 1, r) -} diff --git a/session/test/privileges/BUILD.bazel b/session/test/privileges/BUILD.bazel deleted file mode 100644 index 697f9f6ebffaf..0000000000000 --- a/session/test/privileges/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "privileges_test", - timeout = "short", - srcs = [ - "main_test.go", - "privileges_test.go", - ], - flaky = True, - shard_count = 5, - deps = [ - "//config", - "//parser/auth", - "//privilege/privileges", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/privileges/main_test.go b/session/test/privileges/main_test.go deleted file mode 100644 index 44ced8161ff15..0000000000000 --- a/session/test/privileges/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package privileges - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/privileges/privileges_test.go b/session/test/privileges/privileges_test.go deleted file mode 100644 index b9d424e7c4c0b..0000000000000 --- a/session/test/privileges/privileges_test.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package privileges - -import ( - "testing" - - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/privilege/privileges" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestSkipWithGrant(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - save2 := privileges.SkipWithGrant - - privileges.SkipWithGrant = false - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "user_not_exist"}, []byte("yyy"), []byte("zzz"), nil)) - - privileges.SkipWithGrant = true - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: `%`}, []byte("yyy"), []byte("zzz"), nil)) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: `%`}, []byte(""), []byte(""), nil)) - tk.MustExec("use test") - tk.MustExec("create table t (id int)") - tk.MustExec("create role r_1") - tk.MustExec("grant r_1 to root") - tk.MustExec("set role all") - tk.MustExec("show grants for root") - privileges.SkipWithGrant = save2 -} -func TestGrantViewRelated(t *testing.T) { - store := testkit.CreateMockStore(t) - - tkRoot := testkit.NewTestKit(t, store) - tkUser := testkit.NewTestKit(t, store) - tkRoot.MustExec("use test") - tkUser.MustExec("use test") - - tkRoot.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost", CurrentUser: true, AuthUsername: "root", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - - tkRoot.MustExec("create table if not exists t (a int)") - tkRoot.MustExec("create view v_version29 as select * from t") - tkRoot.MustExec("create user 'u_version29'@'%'") - tkRoot.MustExec("grant select on t to u_version29@'%'") - - tkUser.Session().Auth(&auth.UserIdentity{Username: "u_version29", Hostname: "localhost", CurrentUser: true, AuthUsername: "u_version29", AuthHostname: "%"}, nil, []byte("012345678901234567890"), nil) - - tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) - require.Error(t, tkUser.ExecToErr("select * from test.v_version29;")) - tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) - require.Error(t, tkUser.ExecToErr("create view v_version29_c as select * from t;")) - - tkRoot.MustExec(`grant show view, select on v_version29 to 'u_version29'@'%'`) - tkRoot.MustQuery("select table_priv from mysql.tables_priv where host='%' and db='test' and user='u_version29' and table_name='v_version29'").Check(testkit.Rows("Select,Show View")) - - tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) - tkUser.MustQuery("show create view v_version29;") - require.Error(t, tkUser.ExecToErr("create view v_version29_c as select * from v_version29;")) - - tkRoot.MustExec("create view v_version29_c as select * from v_version29;") - tkRoot.MustExec(`grant create view on v_version29_c to 'u_version29'@'%'`) // Can't grant privilege on a non-exist table/view. - tkRoot.MustQuery("select table_priv from mysql.tables_priv where host='%' and db='test' and user='u_version29' and table_name='v_version29_c'").Check(testkit.Rows("Create View")) - tkRoot.MustExec("drop view v_version29_c") - - tkRoot.MustExec(`grant select on v_version29 to 'u_version29'@'%'`) - tkUser.MustQuery("select current_user();").Check(testkit.Rows("u_version29@%")) - tkUser.MustExec("create view v_version29_c as select * from v_version29;") -} - -func TestUpdatePrivilege(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1, t2;") - tk.MustExec("create table t1 (id int);") - tk.MustExec("create table t2 (id int);") - tk.MustExec("insert into t1 values (1);") - tk.MustExec("insert into t2 values (2);") - tk.MustExec("create user xxx;") - tk.MustExec("grant all on test.t1 to xxx;") - tk.MustExec("grant select on test.t2 to xxx;") - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: "localhost"}, []byte(""), []byte(""), nil)) - - tk1.MustMatchErrMsg("update t2 set id = 666 where id = 1;", "privilege check.*") - - // Cover a bug that t1 and t2 both require update privilege. - // In fact, the privlege check for t1 should be update, and for t2 should be select. - tk1.MustExec("update t1,t2 set t1.id = t2.id;") - - // Fix issue 8911 - tk.MustExec("create database weperk") - tk.MustExec("use weperk") - tk.MustExec("create table tb_wehub_server (id int, active_count int, used_count int)") - tk.MustExec("create user 'weperk'") - tk.MustExec("grant all privileges on weperk.* to 'weperk'@'%'") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "weperk", Hostname: "%"}, []byte(""), []byte(""), nil)) - tk1.MustExec("use weperk") - tk1.MustExec("update tb_wehub_server a set a.active_count=a.active_count+1,a.used_count=a.used_count+1 where id=1") - - tk.MustExec("create database service") - tk.MustExec("create database report") - tk.MustExec(`CREATE TABLE service.t1 ( - id int(11) DEFAULT NULL, - a bigint(20) NOT NULL, - b text DEFAULT NULL, - PRIMARY KEY (a) -)`) - tk.MustExec(`CREATE TABLE report.t2 ( - a bigint(20) DEFAULT NULL, - c bigint(20) NOT NULL -)`) - tk.MustExec("grant all privileges on service.* to weperk") - tk.MustExec("grant all privileges on report.* to weperk") - tk1.Session().GetSessionVars().CurrentDB = "" - tk1.MustExec(`update service.t1 s, -report.t2 t -set s.a = t.a -WHERE -s.a = t.a -and t.c >= 1 and t.c <= 10000 -and s.b !='xx';`) - - // Fix issue 10028 - tk.MustExec("create database ap") - tk.MustExec("create database tp") - tk.MustExec("grant all privileges on ap.* to xxx") - tk.MustExec("grant select on tp.* to xxx") - tk.MustExec("create table tp.record( id int,name varchar(128),age int)") - tk.MustExec("insert into tp.record (id,name,age) values (1,'john',18),(2,'lary',19),(3,'lily',18)") - tk.MustExec("create table ap.record( id int,name varchar(128),age int)") - tk.MustExec("insert into ap.record(id) values(1)") - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "xxx", Hostname: "localhost"}, []byte(""), []byte(""), nil)) - tk1.MustExec("update ap.record t inner join tp.record tt on t.id=tt.id set t.name=tt.name") -} - -func TestDBUserNameLength(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table if not exists t (a int)") - // Test username length can be longer than 16. - tk.MustExec(`CREATE USER 'abcddfjakldfjaldddds'@'%' identified by ''`) - tk.MustExec(`grant all privileges on test.* to 'abcddfjakldfjaldddds'@'%'`) - tk.MustExec(`grant all privileges on test.t to 'abcddfjakldfjaldddds'@'%'`) -} - -func TestSessionAuth(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - require.Error(t, tk.Session().Auth(&auth.UserIdentity{Username: "Any not exist username with zero password!", Hostname: "anyhost"}, []byte(""), []byte(""), nil)) -} diff --git a/session/test/session_test.go b/session/test/session_test.go deleted file mode 100644 index b2f46604127f2..0000000000000 --- a/session/test/session_test.go +++ /dev/null @@ -1,1046 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package test - -import ( - "context" - "fmt" - "net" - "os" - "strings" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testutil" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikvrpc" - "github.com/tikv/client-go/v2/tikvrpc/interceptor" -) - -func TestSchemaCheckerSQL(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_enable_metadata_lock=0") - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk1 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1.MustExec("use test") - - // create table - tk.MustExec(`create table t (id int, c int);`) - tk.MustExec(`create table t1 (id int, c int);`) - // insert data - tk.MustExec(`insert into t values(1, 1);`) - - // The schema version is out of date in the first transaction, but the SQL can be retried. - tk.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk.MustExec(`begin;`) - tk1.MustExec(`alter table t add index idx(c);`) - tk.MustExec(`insert into t values(2, 2);`) - tk.MustExec(`commit;`) - - // The schema version is out of date in the first transaction, and the SQL can't be retried. - atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 1) - defer func() { - atomic.StoreUint32(&session.SchemaChangedWithoutRetry, 0) - }() - tk.MustExec(`begin;`) - tk1.MustExec(`alter table t modify column c bigint;`) - tk.MustExec(`insert into t values(3, 3);`) - err := tk.ExecToErr(`commit;`) - require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) - - // But the transaction related table IDs aren't in the updated table IDs. - tk.MustExec(`begin;`) - tk1.MustExec(`alter table t add index idx2(c);`) - tk.MustExec(`insert into t1 values(4, 4);`) - tk.MustExec(`commit;`) - - // Test for "select for update". - tk.MustExec(`begin;`) - tk1.MustExec(`alter table t add index idx3(c);`) - tk.MustQuery(`select * from t for update`) - require.Error(t, tk.ExecToErr(`commit;`)) - - // Repeated tests for partitioned table - tk.MustExec(`create table pt (id int, c int) partition by hash (id) partitions 3`) - tk.MustExec(`insert into pt values(1, 1);`) - // The schema version is out of date in the first transaction, and the SQL can't be retried. - tk.MustExec(`begin;`) - tk1.MustExec(`alter table pt modify column c bigint;`) - tk.MustExec(`insert into pt values(3, 3);`) - err = tk.ExecToErr(`commit;`) - require.True(t, terror.ErrorEqual(err, domain.ErrInfoSchemaChanged), fmt.Sprintf("err %v", err)) - - // But the transaction related table IDs aren't in the updated table IDs. - tk.MustExec(`begin;`) - tk1.MustExec(`alter table pt add index idx2(c);`) - tk.MustExec(`insert into t1 values(4, 4);`) - tk.MustExec(`commit;`) - - // Test for "select for update". - tk.MustExec(`begin;`) - tk1.MustExec(`alter table pt add index idx3(c);`) - tk.MustQuery(`select * from pt for update`) - require.Error(t, tk.ExecToErr(`commit;`)) - - // Test for "select for update". - tk.MustExec(`begin;`) - tk1.MustExec(`alter table pt add index idx4(c);`) - tk.MustQuery(`select * from pt partition (p1) for update`) - require.Error(t, tk.ExecToErr(`commit;`)) -} - -func TestLoadSchemaFailed(t *testing.T) { - originalRetryTime := domain.SchemaOutOfDateRetryTimes.Load() - originalRetryInterval := domain.SchemaOutOfDateRetryInterval.Load() - domain.SchemaOutOfDateRetryTimes.Store(3) - domain.SchemaOutOfDateRetryInterval.Store(20 * time.Millisecond) - defer func() { - domain.SchemaOutOfDateRetryTimes.Store(originalRetryTime) - domain.SchemaOutOfDateRetryInterval.Store(originalRetryInterval) - }() - - store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) - - tk := testkit.NewTestKit(t, store) - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk.MustExec("create table t (a int);") - tk.MustExec("create table t1 (a int);") - tk.MustExec("create table t2 (a int);") - - tk1.MustExec("begin") - tk2.MustExec("begin") - - // Make sure loading information schema is failed and server is invalid. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed", `return(true)`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed")) }() - require.Error(t, domain.GetDomain(tk.Session()).Reload()) - - lease := domain.GetDomain(tk.Session()).DDL().GetLease() - time.Sleep(lease * 2) - - // Make sure executing insert statement is failed when server is invalid. - require.Error(t, tk.ExecToErr("insert t values (100);")) - - tk1.MustExec("insert t1 values (100);") - tk2.MustExec("insert t2 values (100);") - - require.Error(t, tk1.ExecToErr("commit")) - - ver, err := store.CurrentVersion(kv.GlobalTxnScope) - require.NoError(t, err) - require.NotNil(t, ver) - - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/domain/ErrorMockReloadFailed")) - time.Sleep(lease * 2) - - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a int);") - tk.MustExec("insert t values (100);") - // Make sure insert to table t2 transaction executes. - tk2.MustExec("commit") -} - -func TestWriteOnMultipleCachedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists ct1, ct2") - tk.MustExec("create table ct1 (id int, c int)") - tk.MustExec("create table ct2 (id int, c int)") - tk.MustExec("alter table ct1 cache") - tk.MustExec("alter table ct2 cache") - tk.MustQuery("select * from ct1").Check(testkit.Rows()) - tk.MustQuery("select * from ct2").Check(testkit.Rows()) - - lastReadFromCache := func(tk *testkit.TestKit) bool { - return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache - } - - cached := false - for i := 0; i < 50; i++ { - tk.MustQuery("select * from ct1") - if lastReadFromCache(tk) { - cached = true - break - } - time.Sleep(100 * time.Millisecond) - } - require.True(t, cached) - - tk.MustExec("begin") - tk.MustExec("insert into ct1 values (3, 4)") - tk.MustExec("insert into ct2 values (5, 6)") - tk.MustExec("commit") - - tk.MustQuery("select * from ct1").Check(testkit.Rows("3 4")) - tk.MustQuery("select * from ct2").Check(testkit.Rows("5 6")) - - // cleanup - tk.MustExec("alter table ct1 nocache") - tk.MustExec("alter table ct2 nocache") -} - -func TestFixSetTiDBSnapshotTS(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - safePointName := "tikv_gc_safe_point" - safePointValue := "20160102-15:04:05 -0700" - safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" - updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') - ON DUPLICATE KEY - UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) - tk.MustExec(updateSafePoint) - tk.MustExec("create database t123") - time.Sleep(time.Second) - ts := time.Now().Format("2006-1-2 15:04:05") - time.Sleep(time.Second) - tk.MustExec("drop database t123") - tk.MustMatchErrMsg("use t123", ".*Unknown database.*") - tk.MustExec(fmt.Sprintf("set @@tidb_snapshot='%s'", ts)) - tk.MustExec("use t123") - // update any session variable and assert whether infoschema is changed - tk.MustExec("SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER';") - tk.MustExec("use t123") -} - -func TestPrepareZero(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(v timestamp)") - tk.MustExec("prepare s1 from 'insert into t (v) values (?)'") - tk.MustExec("set @v1='0'") - require.Error(t, tk.ExecToErr("execute s1 using @v1")) - tk.MustExec("set @v2='" + types.ZeroDatetimeStr + "'") - tk.MustExec("set @orig_sql_mode=@@sql_mode; set @@sql_mode='';") - tk.MustExec("execute s1 using @v2") - tk.MustQuery("select v from t").Check(testkit.Rows("0000-00-00 00:00:00")) - tk.MustExec("set @@sql_mode=@orig_sql_mode;") -} - -func TestPrimaryKeyAutoIncrement(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, name varchar(255) UNIQUE NOT NULL, status int)") - tk.MustExec("insert t (name) values (?)", "abc") - id := tk.Session().LastInsertID() - require.NotZero(t, id) - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustQuery("select * from t").Check(testkit.Rows(fmt.Sprintf("%d abc ", id))) - - tk.MustExec("update t set name = 'abc', status = 1 where id = ?", id) - tk1.MustQuery("select * from t").Check(testkit.Rows(fmt.Sprintf("%d abc 1", id))) - - // Check for pass bool param to tidb prepared statement - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id tinyint)") - tk.MustExec("insert t values (?)", true) - tk.MustQuery("select * from t").Check(testkit.Rows("1")) -} - -func TestParseWithParams(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - se := tk.Session() - exec := se.(sqlexec.RestrictedSQLExecutor) - - // test compatibility with ExcuteInternal - _, err := exec.ParseWithParams(context.TODO(), "SELECT 4") - require.NoError(t, err) - - // test charset attack - stmt, err := exec.ParseWithParams(context.TODO(), "SELECT * FROM test WHERE name = %? LIMIT 1", "\xbf\x27 OR 1=1 /*") - require.NoError(t, err) - - var sb strings.Builder - ctx := format.NewRestoreCtx(format.RestoreStringDoubleQuotes, &sb) - err = stmt.Restore(ctx) - require.NoError(t, err) - require.Equal(t, "SELECT * FROM test WHERE name=_utf8mb4\"\xbf' OR 1=1 /*\" LIMIT 1", sb.String()) - - // test invalid sql - _, err = exec.ParseWithParams(context.TODO(), "SELECT") - require.Regexp(t, ".*You have an error in your SQL syntax.*", err) - - // test invalid arguments to escape - _, err = exec.ParseWithParams(context.TODO(), "SELECT %?, %?", 3) - require.Regexp(t, "missing arguments.*", err) - - // test noescape - stmt, err = exec.ParseWithParams(context.TODO(), "SELECT 3") - require.NoError(t, err) - - sb.Reset() - ctx = format.NewRestoreCtx(0, &sb) - err = stmt.Restore(ctx) - require.NoError(t, err) - require.Equal(t, "SELECT 3", sb.String()) -} - -func TestDoDDLJobQuit(t *testing.T) { - // This is required since mock tikv does not support paging. - failpoint.Enable("github.com/pingcap/tidb/store/copr/DisablePaging", `return`) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/DisablePaging")) - }() - - // test https://github.com/pingcap/tidb/issues/18714, imitate DM's use environment - // use isolated store, because in below failpoint we will cancel its context - store, err := mockstore.NewMockStore(mockstore.WithStoreType(mockstore.MockTiKV)) - require.NoError(t, err) - defer func() { require.NoError(t, store.Close()) }() - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - se, err := session.CreateSession(store) - require.NoError(t, err) - defer se.Close() - - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/ddl/storeCloseInLoop", `return`)) - defer func() { require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/storeCloseInLoop")) }() - - // this DDL call will enter deadloop before this fix - err = dom.DDL().CreateSchema(se, &ast.CreateDatabaseStmt{Name: model.NewCIStr("testschema")}) - require.Equal(t, "context canceled", err.Error()) -} - -func TestProcessInfoIssue22068(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int)") - var wg util.WaitGroupWrapper - wg.Run(func() { - tk.MustQuery("select 1 from t where a = (select sleep(5));").Check(testkit.Rows()) - }) - time.Sleep(2 * time.Second) - pi := tk.Session().ShowProcess() - require.NotNil(t, pi) - require.Equal(t, "select 1 from t where a = (select sleep(5));", pi.Info) - require.Nil(t, pi.Plan) - wg.Wait() -} - -func TestPerStmtTaskID(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table task_id (v int)") - - tk.MustExec("begin") - tk.MustExec("select * from task_id where v > 10") - taskID1 := tk.Session().GetSessionVars().StmtCtx.TaskID - tk.MustExec("select * from task_id where v < 5") - taskID2 := tk.Session().GetSessionVars().StmtCtx.TaskID - tk.MustExec("commit") - - require.NotEqual(t, taskID1, taskID2) -} - -func TestStmtHints(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - // Test MEMORY_QUOTA hint - tk.MustExec("select /*+ MEMORY_QUOTA(1 MB) */ 1;") - val := int64(1) * 1024 * 1024 - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - tk.MustExec("select /*+ MEMORY_QUOTA(1 GB) */ 1;") - val = int64(1) * 1024 * 1024 * 1024 - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - tk.MustExec("select /*+ MEMORY_QUOTA(1 GB), MEMORY_QUOTA(1 MB) */ 1;") - val = int64(1) * 1024 * 1024 - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - tk.MustExec("select /*+ MEMORY_QUOTA(0 GB) */ 1;") - val = int64(0) - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "Setting the MEMORY_QUOTA to 0 means no memory limit") - - tk.MustExec("use test") - tk.MustExec("create table t1(a int);") - tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 (a) values (1);") - val = int64(1) * 1024 * 1024 - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - - tk.MustExec("insert /*+ MEMORY_QUOTA(1 MB) */ into t1 select /*+ MEMORY_QUOTA(3 MB) */ * from t1;") - val = int64(1) * 1024 * 1024 - require.True(t, tk.Session().GetSessionVars().MemTracker.CheckBytesLimit(val)) - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "[util:3126]Hint MEMORY_QUOTA(`3145728`) is ignored as conflicting/duplicated.") - - // Test NO_INDEX_MERGE hint - tk.Session().GetSessionVars().SetEnableIndexMerge(true) - tk.MustExec("select /*+ NO_INDEX_MERGE() */ 1;") - require.True(t, tk.Session().GetSessionVars().StmtCtx.NoIndexMergeHint) - tk.MustExec("select /*+ NO_INDEX_MERGE(), NO_INDEX_MERGE() */ 1;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.True(t, tk.Session().GetSessionVars().GetEnableIndexMerge()) - - // Test STRAIGHT_JOIN hint - tk.MustExec("select /*+ straight_join() */ 1;") - require.True(t, tk.Session().GetSessionVars().StmtCtx.StraightJoinOrder) - tk.MustExec("select /*+ straight_join(), straight_join() */ 1;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - - // Test USE_TOJA hint - tk.Session().GetSessionVars().SetAllowInSubqToJoinAndAgg(true) - tk.MustExec("select /*+ USE_TOJA(false) */ 1;") - require.False(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) - tk.Session().GetSessionVars().SetAllowInSubqToJoinAndAgg(false) - tk.MustExec("select /*+ USE_TOJA(true) */ 1;") - require.True(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) - tk.MustExec("select /*+ USE_TOJA(false), USE_TOJA(true) */ 1;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.True(t, tk.Session().GetSessionVars().GetAllowInSubqToJoinAndAgg()) - - // Test USE_CASCADES hint - tk.Session().GetSessionVars().SetEnableCascadesPlanner(true) - tk.MustExec("select /*+ USE_CASCADES(false) */ 1;") - require.False(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) - tk.Session().GetSessionVars().SetEnableCascadesPlanner(false) - tk.MustExec("select /*+ USE_CASCADES(true) */ 1;") - require.True(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) - tk.MustExec("select /*+ USE_CASCADES(false), USE_CASCADES(true) */ 1;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "USE_CASCADES() is defined more than once, only the last definition takes effect: USE_CASCADES(true)") - require.True(t, tk.Session().GetSessionVars().GetEnableCascadesPlanner()) - - // Test READ_CONSISTENT_REPLICA hint - tk.Session().GetSessionVars().SetReplicaRead(kv.ReplicaReadLeader) - tk.MustExec("select /*+ READ_CONSISTENT_REPLICA() */ 1;") - require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) - tk.MustExec("select /*+ READ_CONSISTENT_REPLICA(), READ_CONSISTENT_REPLICA() */ 1;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) -} - -func TestRollbackOnCompileError(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - tk.MustExec("insert t values (1)") - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustQuery("select * from t").Check(testkit.Rows("1")) - - tk.MustExec("rename table t to t2") - var meetErr bool - for i := 0; i < 100; i++ { - _, err := tk2.Exec("insert t values (1)") - if err != nil { - meetErr = true - break - } - } - require.True(t, meetErr) - - tk.MustExec("rename table t2 to t") - var recoverErr bool - for i := 0; i < 100; i++ { - _, err := tk2.Exec("insert t values (1)") - if err == nil { - recoverErr = true - break - } - } - require.True(t, recoverErr) -} - -func TestResultField(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (id int);") - - tk.MustExec(`INSERT INTO t VALUES (1);`) - tk.MustExec(`INSERT INTO t VALUES (2);`) - r, err := tk.Exec(`SELECT count(*) from t;`) - require.NoError(t, err) - defer r.Close() - fields := r.Fields() - require.NoError(t, err) - require.Len(t, fields, 1) - field := fields[0].Column - require.Equal(t, mysql.TypeLonglong, field.GetType()) - require.Equal(t, 21, field.GetFlen()) -} - -// Testcase for https://github.com/pingcap/tidb/issues/325 -func TestResultType(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - rs, err := tk.Exec(`select cast(null as char(30))`) - require.NoError(t, err) - req := rs.NewChunk(nil) - err = rs.Next(context.Background(), req) - require.NoError(t, err) - require.True(t, req.GetRow(0).IsNull(0)) - require.Equal(t, mysql.TypeVarString, rs.Fields()[0].Column.FieldType.GetType()) -} - -func TestFieldText(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - tests := []struct { - sql string - field string - }{ - {"select distinct(a) from t", "a"}, - {"select (1)", "1"}, - {"select (1+1)", "(1+1)"}, - {"select a from t", "a"}, - {"select ((a+1)) from t", "((a+1))"}, - {"select 1 /*!32301 +1 */;", "1 +1 "}, - {"select /*!32301 1 +1 */;", "1 +1 "}, - {"/*!32301 select 1 +1 */;", "1 +1 "}, - {"select 1 + /*!32301 1 +1 */;", "1 + 1 +1 "}, - {"select 1 /*!32301 + 1, 1 */;", "1 + 1"}, - {"select /*!32301 1, 1 +1 */;", "1"}, - {"select /*!32301 1 + 1, */ +1;", "1 + 1"}, - } - for _, tt := range tests { - result, err := tk.Exec(tt.sql) - require.NoError(t, err) - require.Equal(t, tt.field, result.Fields()[0].ColumnAsName.O) - result.Close() - } -} - -// TestRowLock . See http://dev.mysql.com/doc/refman/5.7/en/commit.html. -func TestRowLock(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - tk.MustExec("drop table if exists t") - txn, err := tk.Session().Txn(true) - require.True(t, kv.ErrInvalidTxn.Equal(err)) - require.False(t, txn.Valid()) - tk.MustExec("create table t (c1 int, c2 int, c3 int)") - tk.MustExec("insert t values (11, 2, 3)") - tk.MustExec("insert t values (12, 2, 3)") - tk.MustExec("insert t values (13, 2, 3)") - - tk1.MustExec("set @@tidb_disable_txn_auto_retry = 0") - tk1.MustExec("begin") - tk1.MustExec("update t set c2=21 where c1=11") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=211 where c1=11") - tk2.MustExec("commit") - - // tk1 will retry and the final value is 21 - tk1.MustExec("commit") - - // Check the result is correct - tk.MustQuery("select c2 from t where c1=11").Check(testkit.Rows("21")) - - tk1.MustExec("begin") - tk1.MustExec("update t set c2=21 where c1=11") - - tk2.MustExec("begin") - tk2.MustExec("update t set c2=22 where c1=12") - tk2.MustExec("commit") - - tk1.MustExec("commit") -} - -func TestMatchIdentity(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("CREATE USER `useridentity`@`%`") - tk.MustExec("CREATE USER `useridentity`@`localhost`") - tk.MustExec("CREATE USER `useridentity`@`192.168.1.1`") - tk.MustExec("CREATE USER `useridentity`@`example.com`") - - // The MySQL matching rule is most specific to least specific. - // So if I log in from 192.168.1.1 I should match that entry always. - identity, err := tk.Session().MatchIdentity("useridentity", "192.168.1.1") - require.NoError(t, err) - require.Equal(t, "useridentity", identity.Username) - require.Equal(t, "192.168.1.1", identity.Hostname) - - // If I log in from localhost, I should match localhost - identity, err = tk.Session().MatchIdentity("useridentity", "localhost") - require.NoError(t, err) - require.Equal(t, "useridentity", identity.Username) - require.Equal(t, "localhost", identity.Hostname) - - // If I log in from 192.168.1.2 I should match wildcard. - identity, err = tk.Session().MatchIdentity("useridentity", "192.168.1.2") - require.NoError(t, err) - require.Equal(t, "useridentity", identity.Username) - require.Equal(t, "%", identity.Hostname) - - identity, err = tk.Session().MatchIdentity("useridentity", "127.0.0.1") - require.NoError(t, err) - require.Equal(t, "useridentity", identity.Username) - require.Equal(t, "localhost", identity.Hostname) - - // This uses the lookup of example.com to get an IP address. - // We then login with that IP address, but expect it to match the example.com - // entry in the privileges table (by reverse lookup). - ips, err := net.LookupHost("example.com") - require.NoError(t, err) - identity, err = tk.Session().MatchIdentity("useridentity", ips[0]) - require.NoError(t, err) - require.Equal(t, "useridentity", identity.Username) - // FIXME: we *should* match example.com instead - // as long as skip-name-resolve is not set (DEFAULT) - require.Equal(t, "%", identity.Hostname) -} - -func TestBinaryReadOnly(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (i int key)") - id, _, _, err := tk.Session().PrepareStmt("select i from t where i = ?") - require.NoError(t, err) - id2, _, _, err := tk.Session().PrepareStmt("insert into t values (?)") - require.NoError(t, err) - tk.MustExec("set autocommit = 0") - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - _, err = tk.Session().ExecutePreparedStmt(context.Background(), id, expression.Args2Expressions4Test(1)) - require.NoError(t, err) - require.Equal(t, 0, session.GetHistory(tk.Session()).Count()) - tk.MustExec("insert into t values (1)") - require.Equal(t, 1, session.GetHistory(tk.Session()).Count()) - _, err = tk.Session().ExecutePreparedStmt(context.Background(), id2, expression.Args2Expressions4Test(2)) - require.NoError(t, err) - require.Equal(t, 2, session.GetHistory(tk.Session()).Count()) - tk.MustExec("commit") -} - -func TestHandleAssertionFailureForPartitionedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - se := tk.Session() - se.SetConnectionID(1) - tk.MustExec("use test") - tk.MustExec("create table t (a int, b int, c int, primary key(a, b)) partition by range (a) (partition p0 values less than (10), partition p1 values less than (20))") - failpoint.Enable("github.com/pingcap/tidb/table/tables/addRecordForceAssertExist", "return") - defer failpoint.Disable("github.com/pingcap/tidb/table/tables/addRecordForceAssertExist") - - ctx, hook := testutil.WithLogHook(context.TODO(), t, "table") - _, err := tk.ExecWithContext(ctx, "insert into t values (1, 1, 1)") - require.ErrorContains(t, err, "assertion") - hook.CheckLogCount(t, 0) -} - -func TestRandomBinary(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - allBytes := [][]byte{ - {4, 0, 0, 0, 0, 0, 0, 4, '2'}, - {4, 0, 0, 0, 0, 0, 0, 4, '.'}, - {4, 0, 0, 0, 0, 0, 0, 4, '*'}, - {4, 0, 0, 0, 0, 0, 0, 4, '('}, - {4, 0, 0, 0, 0, 0, 0, 4, '\''}, - {4, 0, 0, 0, 0, 0, 0, 4, '!'}, - {4, 0, 0, 0, 0, 0, 0, 4, 29}, - {4, 0, 0, 0, 0, 0, 0, 4, 28}, - {4, 0, 0, 0, 0, 0, 0, 4, 23}, - {4, 0, 0, 0, 0, 0, 0, 4, 16}, - } - sql := "insert into mysql.stats_top_n (table_id, is_index, hist_id, value, count) values " - var val string - for i, bytes := range allBytes { - if i == 0 { - val += sqlexec.MustEscapeSQL("(874, 0, 1, %?, 3)", bytes) - } else { - val += sqlexec.MustEscapeSQL(",(874, 0, 1, %?, 3)", bytes) - } - } - sql += val - tk.MustExec("set sql_mode = 'NO_BACKSLASH_ESCAPES';") - _, err := tk.Session().ExecuteInternal(ctx, sql) - require.NoError(t, err) -} - -func TestSQLModeOp(t *testing.T) { - s := mysql.ModeNoBackslashEscapes | mysql.ModeOnlyFullGroupBy - d := mysql.DelSQLMode(s, mysql.ModeANSIQuotes) - require.Equal(t, s, d) - - d = mysql.DelSQLMode(s, mysql.ModeNoBackslashEscapes) - require.Equal(t, mysql.ModeOnlyFullGroupBy, d) - - s = mysql.ModeNoBackslashEscapes | mysql.ModeOnlyFullGroupBy - a := mysql.SetSQLMode(s, mysql.ModeOnlyFullGroupBy) - require.Equal(t, s, a) - - a = mysql.SetSQLMode(s, mysql.ModeAllowInvalidDates) - require.Equal(t, mysql.ModeNoBackslashEscapes|mysql.ModeOnlyFullGroupBy|mysql.ModeAllowInvalidDates, a) -} - -func TestRequestSource(t *testing.T) { - store := testkit.CreateMockStore(t, mockstore.WithStoreType(mockstore.MockTiKV)) - tk := testkit.NewTestKit(t, store) - withCheckInterceptor := func(source string) interceptor.RPCInterceptor { - return interceptor.NewRPCInterceptor("kv-request-source-verify", func(next interceptor.RPCInterceptorFunc) interceptor.RPCInterceptorFunc { - return func(target string, req *tikvrpc.Request) (*tikvrpc.Response, error) { - requestSource := "" - readType := "" - switch r := req.Req.(type) { - case *kvrpcpb.PrewriteRequest: - requestSource = r.GetContext().GetRequestSource() - case *kvrpcpb.CommitRequest: - requestSource = r.GetContext().GetRequestSource() - case *coprocessor.Request: - readType = "leader_" // read request will be attached with read type - requestSource = r.GetContext().GetRequestSource() - case *kvrpcpb.GetRequest: - readType = "leader_" // read request will be attached with read type - requestSource = r.GetContext().GetRequestSource() - case *kvrpcpb.BatchGetRequest: - readType = "leader_" // read request will be attached with read type - requestSource = r.GetContext().GetRequestSource() - } - require.Equal(t, readType+source, requestSource) - return next(target, req) - } - }) - } - ctx := context.Background() - tk.MustExecWithContext(ctx, "use test") - tk.MustExecWithContext(ctx, "create table t(a int primary key, b int)") - tk.MustExecWithContext(ctx, "set @@tidb_request_source_type = 'lightning'") - tk.MustQueryWithContext(ctx, "select @@tidb_request_source_type").Check(testkit.Rows("lightning")) - insertCtx := interceptor.WithRPCInterceptor(context.Background(), withCheckInterceptor("external_Insert_lightning")) - tk.MustExecWithContext(insertCtx, "insert into t values(1, 1)") - selectCtx := interceptor.WithRPCInterceptor(context.Background(), withCheckInterceptor("external_Select_lightning")) - tk.MustExecWithContext(selectCtx, "select count(*) from t;") - tk.MustQueryWithContext(selectCtx, "select b from t where a = 1;") - tk.MustQueryWithContext(selectCtx, "select b from t where a in (1, 2, 3);") -} - -func TestEmptyInitSQLFile(t *testing.T) { - // A non-existent sql file would stop the bootstrap of the tidb cluster - store, err := mockstore.NewMockStore() - require.NoError(t, err) - config.GetGlobalConfig().InitializeSQLFile = "non-existent.sql" - defer func() { - require.NoError(t, store.Close()) - config.GetGlobalConfig().InitializeSQLFile = "" - }() - - dom, err := session.BootstrapSession(store) - require.Nil(t, dom) - require.Error(t, err) -} - -func TestInitSystemVariable(t *testing.T) { - // We create an initialize-sql-file and then bootstrap the server with it. - // The observed behavior should be that tidb_enable_noop_variables is now - // disabled, and the feature works as expected. - initializeSQLFile, err := os.CreateTemp("", "init.sql") - require.NoError(t, err) - defer func() { - path := initializeSQLFile.Name() - err = initializeSQLFile.Close() - require.NoError(t, err) - err = os.Remove(path) - require.NoError(t, err) - }() - // Implicitly test multi-line init files - _, err = initializeSQLFile.WriteString( - "CREATE DATABASE initsqlfiletest;\n" + - "SET GLOBAL tidb_enable_noop_variables = OFF;\n") - require.NoError(t, err) - - // Create a mock store - // Set the config parameter for initialize sql file - store, err := mockstore.NewMockStore() - require.NoError(t, err) - config.GetGlobalConfig().InitializeSQLFile = initializeSQLFile.Name() - defer func() { - require.NoError(t, store.Close()) - config.GetGlobalConfig().InitializeSQLFile = "" - }() - - // Bootstrap with the InitializeSQLFile config option - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - se := session.CreateSessionAndSetID(t, store) - ctx := context.Background() - r := session.MustExecToRecodeSet(t, se, `SHOW VARIABLES LIKE 'query_cache_type'`) - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) // not shown in noopvariables mode - require.NoError(t, r.Close()) - - r = session.MustExecToRecodeSet(t, se, `SHOW VARIABLES LIKE 'tidb_enable_noop_variables'`) - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, []byte("OFF"), row.GetBytes(1)) - require.NoError(t, r.Close()) -} - -func TestInitUsers(t *testing.T) { - // Two sql files are set to 'initialize-sql-file' one after another, - // and only the first one is executed. - var err error - sqlFiles := make([]*os.File, 2) - for i, name := range []string{"1.sql", "2.sql"} { - sqlFiles[i], err = os.CreateTemp("", name) - require.NoError(t, err) - } - defer func() { - for _, sqlFile := range sqlFiles { - path := sqlFile.Name() - err = sqlFile.Close() - require.NoError(t, err) - err = os.Remove(path) - require.NoError(t, err) - } - }() - _, err = sqlFiles[0].WriteString(` -CREATE USER cloud_admin; -GRANT BACKUP_ADMIN, RESTORE_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT DASHBOARD_CLIENT on *.* TO 'cloud_admin'@'%'; -GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_VARIABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_STATUS_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_CONNECTION_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_USER_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_TABLES_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT RESTRICTED_REPLICA_WRITER_ADMIN ON *.* TO 'cloud_admin'@'%'; -GRANT CREATE USER ON *.* TO 'cloud_admin'@'%'; -GRANT RELOAD ON *.* TO 'cloud_admin'@'%'; -GRANT PROCESS ON *.* TO 'cloud_admin'@'%'; -GRANT SELECT, INSERT, UPDATE, DELETE ON mysql.* TO 'cloud_admin'@'%'; -GRANT SELECT ON information_schema.* TO 'cloud_admin'@'%'; -GRANT SELECT ON performance_schema.* TO 'cloud_admin'@'%'; -GRANT SHOW DATABASES on *.* TO 'cloud_admin'@'%'; -GRANT REFERENCES ON *.* TO 'cloud_admin'@'%'; -GRANT SELECT ON *.* TO 'cloud_admin'@'%'; -GRANT INDEX ON *.* TO 'cloud_admin'@'%'; -GRANT INSERT ON *.* TO 'cloud_admin'@'%'; -GRANT UPDATE ON *.* TO 'cloud_admin'@'%'; -GRANT DELETE ON *.* TO 'cloud_admin'@'%'; -GRANT CREATE ON *.* TO 'cloud_admin'@'%'; -GRANT DROP ON *.* TO 'cloud_admin'@'%'; -GRANT ALTER ON *.* TO 'cloud_admin'@'%'; -GRANT CREATE VIEW ON *.* TO 'cloud_admin'@'%'; -GRANT SHUTDOWN, CONFIG ON *.* TO 'cloud_admin'@'%'; -REVOKE SHUTDOWN, CONFIG ON *.* FROM root; - -DROP USER root; -`) - require.NoError(t, err) - _, err = sqlFiles[1].WriteString("drop user cloud_admin;") - require.NoError(t, err) - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - config.GetGlobalConfig().InitializeSQLFile = sqlFiles[0].Name() - defer func() { - require.NoError(t, store.Close()) - config.GetGlobalConfig().InitializeSQLFile = "" - }() - - // Bootstrap with the first sql file - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - se := session.CreateSessionAndSetID(t, store) - ctx := context.Background() - // 'cloud_admin' has been created successfully - r := session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'cloud_admin'`) - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, "cloud_admin", row.GetString(0)) - require.NoError(t, r.Close()) - // 'root' has been deleted successfully - r = session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'root'`) - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) - require.NoError(t, r.Close()) - dom.Close() - - session.DisableRunBootstrapSQLFileInTest() - - // Bootstrap with the second sql file, which would not been executed. - config.GetGlobalConfig().InitializeSQLFile = sqlFiles[1].Name() - dom, err = session.BootstrapSession(store) - require.NoError(t, err) - se = session.CreateSessionAndSetID(t, store) - r = session.MustExecToRecodeSet(t, se, `select user from mysql.user where user = 'cloud_admin'`) - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - row = req.GetRow(0) - require.Equal(t, "cloud_admin", row.GetString(0)) - require.NoError(t, r.Close()) - dom.Close() -} - -func TestErrorHappenWhileInit(t *testing.T) { - // 1. parser error in sql file (1.sql) makes the bootstrap panic - // 2. other errors in sql file (2.sql) will be ignored - var err error - sqlFiles := make([]*os.File, 2) - for i, name := range []string{"1.sql", "2.sql"} { - sqlFiles[i], err = os.CreateTemp("", name) - require.NoError(t, err) - } - defer func() { - for _, sqlFile := range sqlFiles { - path := sqlFile.Name() - err = sqlFile.Close() - require.NoError(t, err) - err = os.Remove(path) - require.NoError(t, err) - } - }() - _, err = sqlFiles[0].WriteString("create table test.t (c in);") - require.NoError(t, err) - _, err = sqlFiles[1].WriteString(` -create table test.t (c int); -insert into test.t values ("abc"); -- invalid statement -`) - require.NoError(t, err) - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - config.GetGlobalConfig().InitializeSQLFile = sqlFiles[0].Name() - defer func() { - config.GetGlobalConfig().InitializeSQLFile = "" - }() - - // Bootstrap with the first sql file - dom, err := session.BootstrapSession(store) - require.Nil(t, dom) - require.Error(t, err) - require.NoError(t, store.Close()) - - session.DisableRunBootstrapSQLFileInTest() - - // Bootstrap with the second sql file, which would not been executed. - store, err = mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - config.GetGlobalConfig().InitializeSQLFile = sqlFiles[1].Name() - dom, err = session.BootstrapSession(store) - require.NoError(t, err) - se := session.CreateSessionAndSetID(t, store) - ctx := context.Background() - _ = session.MustExecToRecodeSet(t, se, `use test;`) - require.NoError(t, err) - // Table t has been created. - r := session.MustExecToRecodeSet(t, se, `show tables;`) - require.NoError(t, err) - req := r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 1, req.NumRows()) - row := req.GetRow(0) - require.Equal(t, "t", row.GetString(0)) - require.NoError(t, r.Close()) - // But data is failed to inserted since the error - r = session.MustExecToRecodeSet(t, se, `select * from test.t`) - require.NoError(t, err) - req = r.NewChunk(nil) - err = r.Next(ctx, req) - require.NoError(t, err) - require.Equal(t, 0, req.NumRows()) - require.NoError(t, r.Close()) - dom.Close() -} diff --git a/session/test/txn/BUILD.bazel b/session/test/txn/BUILD.bazel deleted file mode 100644 index 2f9930a6004c3..0000000000000 --- a/session/test/txn/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "txn_test", - timeout = "short", - srcs = [ - "main_test.go", - "txn_test.go", - ], - flaky = True, - race = "on", - shard_count = 11, - deps = [ - "//config", - "//kv", - "//parser/auth", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/txn/main_test.go b/session/test/txn/main_test.go deleted file mode 100644 index 808990b2f97bc..0000000000000 --- a/session/test/txn/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package txn - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/txn/txn_test.go b/session/test/txn/txn_test.go deleted file mode 100644 index 54311384a37a9..0000000000000 --- a/session/test/txn/txn_test.go +++ /dev/null @@ -1,621 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package txn - -import ( - "context" - "fmt" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -// TestAutocommit . See https://dev.mysql.com/doc/internals/en/status-flags.html -func TestAutocommit(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("drop table if exists t;") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("insert t values ()") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("begin") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("insert t values ()") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("drop table if exists t") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - tk.MustExec("set autocommit=0") - require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) - tk.MustExec("insert t values ()") - require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) - tk.MustExec("commit") - require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) - tk.MustExec("drop table if exists t") - require.Equal(t, 0, int(tk.Session().Status()&mysql.ServerStatusAutocommit)) - tk.MustExec("set autocommit='On'") - require.Greater(t, int(tk.Session().Status()&mysql.ServerStatusAutocommit), 0) - - // When autocommit is 0, transaction start ts should be the first *valid* - // statement, rather than *any* statement. - tk.MustExec("create table t (id int key)") - tk.MustExec("set @@autocommit = 0") - tk.MustExec("rollback") - tk.MustExec("set @@autocommit = 0") - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("insert into t select 1") - //nolint:all_revive,revive - tk.MustQuery("select * from t").Check(testkit.Rows("1")) - tk.MustExec("delete from t") - - // When the transaction is rolled back, the global set statement would succeed. - tk.MustExec("set @@global.autocommit = 0") - tk.MustExec("begin") - tk.MustExec("insert into t values (1)") - tk.MustExec("set @@global.autocommit = 1") - tk.MustExec("rollback") - tk.MustQuery("select count(*) from t where id = 1").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.autocommit").Check(testkit.Rows("1")) - - // When the transaction is committed because of switching mode, the session set statement shold succeed. - tk.MustExec("set autocommit = 0") - tk.MustExec("begin") - tk.MustExec("insert into t values (1)") - tk.MustExec("set autocommit = 1") - tk.MustExec("rollback") - tk.MustQuery("select count(*) from t where id = 1").Check(testkit.Rows("1")) - tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) - - tk.MustExec("set autocommit = 0") - tk.MustExec("insert into t values (2)") - tk.MustExec("set autocommit = 1") - tk.MustExec("rollback") - tk.MustQuery("select count(*) from t where id = 2").Check(testkit.Rows("1")) - tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) - - // Set should not take effect if the mode is not changed. - tk.MustExec("set autocommit = 0") - tk.MustExec("begin") - tk.MustExec("insert into t values (3)") - tk.MustExec("set autocommit = 0") - tk.MustExec("rollback") - tk.MustQuery("select count(*) from t where id = 3").Check(testkit.Rows("0")) - tk.MustQuery("select @@autocommit").Check(testkit.Rows("0")) - - tk.MustExec("set autocommit = 1") - tk.MustExec("begin") - tk.MustExec("insert into t values (4)") - tk.MustExec("set autocommit = 1") - tk.MustExec("rollback") - tk.MustQuery("select count(*) from t where id = 4").Check(testkit.Rows("0")) - tk.MustQuery("select @@autocommit").Check(testkit.Rows("1")) -} - -// TestTxnLazyInitialize tests that when autocommit = 0, not all statement starts -// a new transaction. -func TestTxnLazyInitialize(t *testing.T) { - testTxnLazyInitialize(t, false) - testTxnLazyInitialize(t, true) -} - -func testTxnLazyInitialize(t *testing.T, isPessimistic bool) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id int)") - if isPessimistic { - tk.MustExec("set tidb_txn_mode = 'pessimistic'") - } - - tk.MustExec("set @@autocommit = 0") - _, err := tk.Session().Txn(true) - require.True(t, kv.ErrInvalidTxn.Equal(err)) - txn, err := tk.Session().Txn(false) - require.NoError(t, err) - require.False(t, txn.Valid()) - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - - // Those statements should not start a new transaction automatically. - tk.MustQuery("select 1") - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - - tk.MustExec("set @@tidb_general_log = 0") - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - - tk.MustQuery("explain select * from t") - tk.MustQuery("select @@tidb_current_ts").Check(testkit.Rows("0")) - - // Begin statement should start a new transaction. - tk.MustExec("begin") - txn, err = tk.Session().Txn(false) - require.NoError(t, err) - require.True(t, txn.Valid()) - tk.MustExec("rollback") - - tk.MustExec("select * from t") - txn, err = tk.Session().Txn(false) - require.NoError(t, err) - require.True(t, txn.Valid()) - tk.MustExec("rollback") - - tk.MustExec("insert into t values (1)") - txn, err = tk.Session().Txn(false) - require.NoError(t, err) - require.True(t, txn.Valid()) - tk.MustExec("rollback") -} - -func TestDisableTxnAutoRetry(t *testing.T) { - store := testkit.CreateMockStoreWithSchemaLease(t, 1*time.Second) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - - tk1.MustExec("use test") - tk2.MustExec("use test") - - tk1.MustExec("create table no_retry (id int)") - tk1.MustExec("insert into no_retry values (1)") - tk1.MustExec("set @@tidb_disable_txn_auto_retry = 1") - - tk1.MustExec("begin") - tk1.MustExec("update no_retry set id = 2") - - tk2.MustExec("begin") - tk2.MustExec("update no_retry set id = 3") - tk2.MustExec("commit") - - // No auto retry because tidb_disable_txn_auto_retry is set to 1. - _, err := tk1.Session().Execute(context.Background(), "commit") - require.Error(t, err) - - // session 1 starts a transaction early. - // execute a select statement to clear retry history. - tk1.MustExec("select 1") - err = tk1.Session().PrepareTxnCtx(context.Background()) - require.NoError(t, err) - // session 2 update the value. - tk2.MustExec("update no_retry set id = 4") - // AutoCommit update will retry, so it would not fail. - tk1.MustExec("update no_retry set id = 5") - - // RestrictedSQL should retry. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - tk1.Session().ExecuteInternal(ctx, "begin") - - tk2.MustExec("update no_retry set id = 6") - - tk1.Session().ExecuteInternal(ctx, "update no_retry set id = 7") - tk1.Session().ExecuteInternal(ctx, "commit") - - // test for disable transaction local latch - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.TxnLocalLatches.Enabled = false - }) - tk1.MustExec("begin") - tk1.MustExec("update no_retry set id = 9") - - tk2.MustExec("update no_retry set id = 8") - - _, err = tk1.Session().Execute(context.Background(), "commit") - require.Error(t, err) - require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) - require.Contains(t, err.Error(), kv.TxnRetryableMark) - tk1.MustExec("rollback") - - config.UpdateGlobal(func(conf *config.Config) { - conf.TxnLocalLatches.Enabled = true - }) - tk1.MustExec("begin") - tk2.MustExec("alter table no_retry add index idx(id)") - tk2.MustQuery("select * from no_retry").Check(testkit.Rows("8")) - tk1.MustExec("update no_retry set id = 10") - _, err = tk1.Session().Execute(context.Background(), "commit") - require.Error(t, err) - - // set autocommit to begin and commit - tk1.MustExec("set autocommit = 0") - tk1.MustQuery("select * from no_retry").Check(testkit.Rows("8")) - tk2.MustExec("update no_retry set id = 11") - tk1.MustExec("update no_retry set id = 12") - _, err = tk1.Session().Execute(context.Background(), "set autocommit = 1") - require.Error(t, err) - require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) - require.Contains(t, err.Error(), kv.TxnRetryableMark) - tk1.MustExec("rollback") - tk2.MustQuery("select * from no_retry").Check(testkit.Rows("11")) - - tk1.MustExec("set autocommit = 0") - tk1.MustQuery("select * from no_retry").Check(testkit.Rows("11")) - tk2.MustExec("update no_retry set id = 13") - tk1.MustExec("update no_retry set id = 14") - _, err = tk1.Session().Execute(context.Background(), "commit") - require.Error(t, err) - require.True(t, kv.ErrWriteConflict.Equal(err), fmt.Sprintf("err %v", err)) - require.Contains(t, err.Error(), kv.TxnRetryableMark) - tk1.MustExec("rollback") - tk2.MustQuery("select * from no_retry").Check(testkit.Rows("13")) -} - -// The Read-only flags are checked in the planning stage of queries, -// but this test checks we check them again at commit time. -// The main use case for this is a long-running auto-commit statement. -func TestAutoCommitRespectsReadOnly(t *testing.T) { - store := testkit.CreateMockStore(t) - var wg sync.WaitGroup - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - require.NoError(t, tk1.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - require.NoError(t, tk2.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - - tk1.MustExec("create table test.auto_commit_test (a int)") - wg.Add(1) - go func() { - err := tk1.ExecToErr("INSERT INTO test.auto_commit_test VALUES (SLEEP(1))") - require.True(t, terror.ErrorEqual(err, plannercore.ErrSQLInReadOnlyMode), fmt.Sprintf("err %v", err)) - wg.Done() - }() - tk2.MustExec("SET GLOBAL tidb_restricted_read_only = 1") - err := tk2.ExecToErr("INSERT INTO test.auto_commit_test VALUES (0)") // should also be an error - require.True(t, terror.ErrorEqual(err, plannercore.ErrSQLInReadOnlyMode), fmt.Sprintf("err %v", err)) - // Reset and check with the privilege to ignore the readonly flag and continue to insert. - wg.Wait() - tk1.MustExec("SET GLOBAL tidb_restricted_read_only = 0") - tk1.MustExec("SET GLOBAL tidb_super_read_only = 0") - tk1.MustExec("GRANT RESTRICTED_REPLICA_WRITER_ADMIN on *.* to 'root'") - - wg.Add(1) - go func() { - tk1.MustExec("INSERT INTO test.auto_commit_test VALUES (SLEEP(1))") - wg.Done() - }() - tk2.MustExec("SET GLOBAL tidb_restricted_read_only = 1") - tk2.MustExec("INSERT INTO test.auto_commit_test VALUES (0)") - - // wait for go routines - wg.Wait() - tk1.MustExec("SET GLOBAL tidb_restricted_read_only = 0") - tk1.MustExec("SET GLOBAL tidb_super_read_only = 0") -} - -func TestRetryForCurrentTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table history (a int)") - tk.MustExec("insert history values (1)") - - // Firstly, enable retry. - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - tk.MustExec("begin") - tk.MustExec("update history set a = 2") - // Disable retry now. - tk.MustExec("set tidb_disable_txn_auto_retry = 1") - - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("update history set a = 3") - - tk.MustExec("commit") - tk.MustQuery("select * from history").Check(testkit.Rows("2")) -} - -func TestBatchCommit(t *testing.T) { - store := testkit.CreateMockStore(t) - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set tidb_batch_commit = 1") - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - tk.MustExec("create table t (id int)") - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.StmtCountLimit = 3 - }) - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk.MustExec("SET SESSION autocommit = 1") - tk.MustExec("begin") - tk.MustExec("insert into t values (1)") - tk1.MustQuery("select * from t").Check(testkit.Rows()) - tk.MustExec("insert into t values (2)") - tk1.MustQuery("select * from t").Check(testkit.Rows()) - tk.MustExec("rollback") - tk1.MustQuery("select * from t").Check(testkit.Rows()) - - // The above rollback will not make the session in transaction. - tk.MustExec("insert into t values (1)") - tk1.MustQuery("select * from t").Check(testkit.Rows("1")) - tk.MustExec("delete from t") - - tk.MustExec("begin") - tk.MustExec("insert into t values (5)") - tk1.MustQuery("select * from t").Check(testkit.Rows()) - tk.MustExec("insert into t values (6)") - tk1.MustQuery("select * from t").Check(testkit.Rows()) - tk.MustExec("insert into t values (7)") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) - - // The session is still in transaction. - tk.MustExec("insert into t values (8)") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) - tk.MustExec("insert into t values (9)") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) - tk.MustExec("insert into t values (10)") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7")) - tk.MustExec("commit") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7", "8", "9", "10")) - - // The above commit will not make the session in transaction. - tk.MustExec("insert into t values (11)") - tk1.MustQuery("select * from t").Check(testkit.Rows("5", "6", "7", "8", "9", "10", "11")) - - tk.MustExec("delete from t") - tk.MustExec("SET SESSION autocommit = 0") - tk.MustExec("insert into t values (1)") - tk.MustExec("insert into t values (2)") - tk.MustExec("insert into t values (3)") - tk.MustExec("rollback") - tk1.MustExec("insert into t values (4)") - tk1.MustExec("insert into t values (5)") - tk.MustQuery("select * from t").Check(testkit.Rows("4", "5")) -} - -func TestTxnRetryErrMsg(t *testing.T) { - store := testkit.CreateMockStore(t) - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("create table no_retry (id int)") - tk1.MustExec("insert into no_retry values (1)") - tk1.MustExec("begin") - tk2.MustExec("use test") - tk2.MustExec("update no_retry set id = id + 1") - tk1.MustExec("update no_retry set id = id + 1") - require.NoError(t, failpoint.Enable("tikvclient/mockRetryableErrorResp", `return(true)`)) - _, err := tk1.Session().Execute(context.Background(), "commit") - require.NoError(t, failpoint.Disable("tikvclient/mockRetryableErrorResp")) - require.Error(t, err) - require.True(t, kv.ErrTxnRetryable.Equal(err), "error: %s", err) - require.True(t, strings.Contains(err.Error(), "mock retryable error"), "error: %s", err) - require.True(t, strings.Contains(err.Error(), kv.TxnRetryableMark), "error: %s", err) -} - -func TestSetTxnScope(t *testing.T) { - // Check the default value of @@tidb_enable_local_txn and @@txn_scope whitout configuring the zone label. - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Check the default value of @@tidb_enable_local_txn and @@txn_scope with configuring the zone label. - require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) - - // @@tidb_enable_local_txn is off without configuring the zone label. - tk = testkit.NewTestKit(t, store) - tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to local. - err := tk.ExecToErr("set @@txn_scope = 'local';") - require.Error(t, err) - require.Regexp(t, `.*txn_scope can not be set to local when tidb_enable_local_txn is off.*`, err) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to global. - tk.MustExec("set @@txn_scope = 'global';") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - - // @@tidb_enable_local_txn is off with configuring the zone label. - require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustQuery("select @@global.tidb_enable_local_txn;").Check(testkit.Rows("0")) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to local. - err = tk.ExecToErr("set @@txn_scope = 'local';") - require.Error(t, err) - require.Regexp(t, `.*txn_scope can not be set to local when tidb_enable_local_txn is off.*`, err) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to global. - tk.MustExec("set @@txn_scope = 'global';") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) - - // @@tidb_enable_local_txn is on without configuring the zone label. - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_enable_local_txn = on;") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to local. - err = tk.ExecToErr("set @@txn_scope = 'local';") - require.Error(t, err) - require.Regexp(t, `.*txn_scope can not be set to local when zone label is empty or "global".*`, err) - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to global. - tk.MustExec("set @@txn_scope = 'global';") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - - // @@tidb_enable_local_txn is on with configuring the zone label. - require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", `return("bj")`)) - tk = testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_enable_local_txn = on;") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.LocalTxnScope)) - require.Equal(t, "bj", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to global. - tk.MustExec("set @@txn_scope = 'global';") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.GlobalTxnScope)) - require.Equal(t, kv.GlobalTxnScope, tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Set @@txn_scope to local. - tk.MustExec("set @@txn_scope = 'local';") - tk.MustQuery("select @@txn_scope;").Check(testkit.Rows(kv.LocalTxnScope)) - require.Equal(t, "bj", tk.Session().GetSessionVars().CheckAndGetTxnScope()) - // Try to set @@txn_scope to an invalid value. - err = tk.ExecToErr("set @@txn_scope='foo'") - require.Error(t, err) - require.Regexp(t, `.*txn_scope value should be global or local.*`, err) - require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) -} - -func TestErrorRollback(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_rollback") - tk.MustExec("create table t_rollback (c1 int, c2 int, primary key(c1))") - tk.MustExec("insert into t_rollback values (0, 0)") - - var wg sync.WaitGroup - cnt := 4 - wg.Add(cnt) - num := 20 - - for i := 0; i < cnt; i++ { - go func() { - defer wg.Done() - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_retry_limit = 100") - for j := 0; j < num; j++ { - _, _ = tk.Exec("insert into t_rollback values (1, 1)") - tk.MustExec("update t_rollback set c2 = c2 + 1 where c1 = 0") - } - }() - } - - wg.Wait() - tk.MustQuery("select c2 from t_rollback where c1 = 0").Check(testkit.Rows(fmt.Sprint(cnt * num))) -} - -// TestInTrans . See https://dev.mysql.com/doc/internals/en/status-flags.html -func TestInTrans(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") - tk.MustExec("insert t values ()") - tk.MustExec("begin") - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.True(t, txn.Valid()) - tk.MustExec("insert t values ()") - require.True(t, txn.Valid()) - tk.MustExec("drop table if exists t;") - require.False(t, txn.Valid()) - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") - require.False(t, txn.Valid()) - tk.MustExec("insert t values ()") - require.False(t, txn.Valid()) - tk.MustExec("commit") - tk.MustExec("insert t values ()") - - tk.MustExec("set autocommit=0") - tk.MustExec("begin") - require.True(t, txn.Valid()) - tk.MustExec("insert t values ()") - require.True(t, txn.Valid()) - tk.MustExec("commit") - require.False(t, txn.Valid()) - tk.MustExec("insert t values ()") - require.True(t, txn.Valid()) - tk.MustExec("commit") - require.False(t, txn.Valid()) - - tk.MustExec("set autocommit=1") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (id BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL)") - tk.MustExec("begin") - require.True(t, txn.Valid()) - tk.MustExec("insert t values ()") - require.True(t, txn.Valid()) - tk.MustExec("rollback") - require.False(t, txn.Valid()) -} - -func TestCommitRetryCount(t *testing.T) { - store := testkit.CreateMockStore(t) - - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - tk1.MustExec("create table no_retry (id int)") - tk1.MustExec("insert into no_retry values (1)") - tk1.MustExec("set @@tidb_retry_limit = 0") - - tk1.MustExec("begin") - tk1.MustExec("update no_retry set id = 2") - - tk2.MustExec("begin") - tk2.MustExec("update no_retry set id = 3") - tk2.MustExec("commit") - - // No auto retry because retry limit is set to 0. - require.Error(t, tk1.ExecToErr("commit")) -} diff --git a/session/test/variable/BUILD.bazel b/session/test/variable/BUILD.bazel deleted file mode 100644 index 7afdc8edb1b0a..0000000000000 --- a/session/test/variable/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "variable_test", - timeout = "short", - srcs = [ - "main_test.go", - "variable_test.go", - ], - flaky = True, - shard_count = 10, - deps = [ - "//config", - "//kv", - "//session", - "//sessionctx/variable", - "//store/copr", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/variable/main_test.go b/session/test/variable/main_test.go deleted file mode 100644 index d145cebcaaf8c..0000000000000 --- a/session/test/variable/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/test/variable/variable_test.go b/session/test/variable/variable_test.go deleted file mode 100644 index bca5803a93ab2..0000000000000 --- a/session/test/variable/variable_test.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/require" -) - -func TestForbidSettingBothTSVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - // For mock tikv, safe point is not initialized, we manually insert it for snapshot to use. - safePointName := "tikv_gc_safe_point" - safePointValue := "20060102-15:04:05 -0700" - safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)" - updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s') - ON DUPLICATE KEY - UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment) - tk.MustExec(updateSafePoint) - - // Set tidb_snapshot and assert tidb_read_staleness - tk.MustExec("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'") - tk.MustGetErrMsg("set @@tidb_read_staleness='-5'", "tidb_snapshot should be clear before setting tidb_read_staleness") - tk.MustExec("set @@tidb_snapshot = ''") - tk.MustExec("set @@tidb_read_staleness='-5'") - - // Set tidb_read_staleness and assert tidb_snapshot - tk.MustExec("set @@tidb_read_staleness='-5'") - tk.MustGetErrMsg("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'", "tidb_read_staleness should be clear before setting tidb_snapshot") - tk.MustExec("set @@tidb_read_staleness = ''") - tk.MustExec("set @@tidb_snapshot = '2007-01-01 15:04:05.999999'") -} - -func TestCoprocessorOOMAction(t *testing.T) { - // Assert Coprocessor OOMAction - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_enable_rate_limit_action=true") - tk.MustExec("create database testoom") - tk.MustExec("use testoom") - tk.MustExec(`set @@tidb_wait_split_region_finish=1`) - // create table for non keep-order case - tk.MustExec("drop table if exists t5") - tk.MustExec("create table t5(id int)") - tk.MustQuery(`split table t5 between (0) and (10000) regions 10`).Check(testkit.Rows("9 1")) - // create table for keep-order case - tk.MustExec("drop table if exists t6") - tk.MustExec("create table t6(id int, index(id))") - tk.MustQuery(`split table t6 between (0) and (10000) regions 10`).Check(testkit.Rows("10 1")) - tk.MustQuery("split table t6 INDEX id between (0) and (10000) regions 10;").Check(testkit.Rows("10 1")) - count := 10 - for i := 0; i < count; i++ { - tk.MustExec(fmt.Sprintf("insert into t5 (id) values (%v)", i)) - tk.MustExec(fmt.Sprintf("insert into t6 (id) values (%v)", i)) - } - - testcases := []struct { - name string - sql string - }{ - { - name: "keep Order", - sql: "select id from t6 order by id", - }, - { - name: "non keep Order", - sql: "select id from t5", - }, - } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/testRateLimitActionMockConsumeAndAssert", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/testRateLimitActionMockConsumeAndAssert")) - }() - - enableOOM := func(tk *testkit.TestKit, name, sql string) { - t.Logf("enable OOM, testcase: %v", name) - // larger than 4 copResponse, smaller than 5 copResponse - quota := 5*copr.MockResponseSizeForTest - 100 - defer tk.MustExec("SET GLOBAL tidb_mem_oom_action = DEFAULT") - tk.MustExec("SET GLOBAL tidb_mem_oom_action='CANCEL'") - tk.MustExec("use testoom") - tk.MustExec("set @@tidb_enable_rate_limit_action=1") - tk.MustExec("set @@tidb_distsql_scan_concurrency = 10") - tk.MustExec(fmt.Sprintf("set @@tidb_mem_quota_query=%v;", quota)) - var expect []string - for i := 0; i < count; i++ { - expect = append(expect, fmt.Sprintf("%v", i)) - } - tk.MustQuery(sql).Sort().Check(testkit.Rows(expect...)) - // assert oom action worked by max consumed > memory quota - require.Greater(t, tk.Session().GetSessionVars().StmtCtx.MemTracker.MaxConsumed(), int64(quota)) - } - - disableOOM := func(tk *testkit.TestKit, name, sql string) { - t.Logf("disable OOM, testcase: %v", name) - quota := 5*copr.MockResponseSizeForTest - 100 - tk.MustExec("use testoom") - tk.MustExec("set @@tidb_enable_rate_limit_action=0") - tk.MustExec("set @@tidb_distsql_scan_concurrency = 10") - tk.MustExec(fmt.Sprintf("set @@tidb_mem_quota_query=%v;", quota)) - err := tk.QueryToErr(sql) - require.Error(t, err) - require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) - } - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/copr/testRateLimitActionMockWaitMax", `return(true)`)) - // assert oom action and switch - for _, testcase := range testcases { - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(se) - enableOOM(tk, testcase.name, testcase.sql) - tk.MustExec("set @@tidb_enable_rate_limit_action = 0") - disableOOM(tk, testcase.name, testcase.sql) - tk.MustExec("set @@tidb_enable_rate_limit_action = 1") - enableOOM(tk, testcase.name, testcase.sql) - se.Close() - } - globaltk := testkit.NewTestKit(t, store) - globaltk.MustExec("use testoom") - globaltk.MustExec("set global tidb_enable_rate_limit_action= 0") - for _, testcase := range testcases { - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(se) - disableOOM(tk, testcase.name, testcase.sql) - se.Close() - } - globaltk.MustExec("set global tidb_enable_rate_limit_action= 1") - for _, testcase := range testcases { - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(se) - enableOOM(tk, testcase.name, testcase.sql) - se.Close() - } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/testRateLimitActionMockWaitMax")) - - // assert oom fallback - for _, testcase := range testcases { - t.Log(testcase.name) - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - tk.SetSession(se) - tk.MustExec("use testoom") - tk.MustExec("set tidb_distsql_scan_concurrency = 1") - tk.MustExec("set @@tidb_mem_quota_query=1;") - err = tk.QueryToErr(testcase.sql) - require.Error(t, err) - require.Regexp(t, memory.PanicMemoryExceedWarnMsg+memory.WarnMsgSuffixForSingleQuery, err) - se.Close() - } -} - -func TestStatementCountLimit(t *testing.T) { - store := testkit.CreateMockStore(t) - setTxnTk := testkit.NewTestKit(t, store) - setTxnTk.MustExec("set global tidb_txn_mode=''") - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table stmt_count_limit (id int)") - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.StmtCountLimit = 3 - }) - tk.MustExec("set tidb_disable_txn_auto_retry = 0") - tk.MustExec("begin") - tk.MustExec("insert into stmt_count_limit values (1)") - tk.MustExec("insert into stmt_count_limit values (2)") - _, err := tk.Exec("insert into stmt_count_limit values (3)") - require.Error(t, err) - - // begin is counted into history but this one is not. - tk.MustExec("SET SESSION autocommit = false") - tk.MustExec("insert into stmt_count_limit values (1)") - tk.MustExec("insert into stmt_count_limit values (2)") - tk.MustExec("insert into stmt_count_limit values (3)") - _, err = tk.Exec("insert into stmt_count_limit values (4)") - require.Error(t, err) -} - -func TestCorrectScopeError(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeNone, Name: "sv_none", Value: "acdc"}) - variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeGlobal, Name: "sv_global", Value: "acdc"}) - variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeSession, Name: "sv_session", Value: "acdc"}) - variable.RegisterSysVar(&variable.SysVar{Scope: variable.ScopeGlobal | variable.ScopeSession, Name: "sv_both", Value: "acdc"}) - - // check set behavior - - // none - _, err := tk.Exec("SET sv_none='acdc'") - require.Equal(t, "[variable:1238]Variable 'sv_none' is a read only variable", err.Error()) - _, err = tk.Exec("SET GLOBAL sv_none='acdc'") - require.Equal(t, "[variable:1238]Variable 'sv_none' is a read only variable", err.Error()) - - // global - tk.MustExec("SET GLOBAL sv_global='acdc'") - _, err = tk.Exec("SET sv_global='acdc'") - require.Equal(t, "[variable:1229]Variable 'sv_global' is a GLOBAL variable and should be set with SET GLOBAL", err.Error()) - - // session - _, err = tk.Exec("SET GLOBAL sv_session='acdc'") - require.Equal(t, "[variable:1228]Variable 'sv_session' is a SESSION variable and can't be used with SET GLOBAL", err.Error()) - tk.MustExec("SET sv_session='acdc'") - - // both - tk.MustExec("SET GLOBAL sv_both='acdc'") - tk.MustExec("SET sv_both='acdc'") - - // unregister - variable.UnregisterSysVar("sv_none") - variable.UnregisterSysVar("sv_global") - variable.UnregisterSysVar("sv_session") - variable.UnregisterSysVar("sv_both") -} - -func TestReadDMLBatchSize(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set global tidb_dml_batch_size=1000") - se, err := session.CreateSession(store) - require.NoError(t, err) - - // `select 1` to load the global variables. - _, _ = se.Execute(context.TODO(), "select 1") - require.Equal(t, 1000, se.GetSessionVars().DMLBatchSize) -} - -func TestSetEnableRateLimitAction(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("set @@tidb_enable_rate_limit_action=true") - // assert default value - result := tk.MustQuery("select @@tidb_enable_rate_limit_action;") - result.Check(testkit.Rows("1")) - tk.MustExec("use test") - tk.MustExec("create table tmp123(id int)") - rs, err := tk.Exec("select * from tmp123;") - require.NoError(t, err) - haveRateLimitAction := false - action := tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false) - for ; action != nil; action = action.GetFallback() { - if action.GetPriority() == memory.DefRateLimitPriority { - haveRateLimitAction = true - break - } - } - require.True(t, haveRateLimitAction) - err = rs.Close() - require.NoError(t, err) - - // assert set sys variable - tk.MustExec("set global tidb_enable_rate_limit_action= '0';") - tk.Session().Close() - - tk.RefreshSession() - result = tk.MustQuery("select @@tidb_enable_rate_limit_action;") - result.Check(testkit.Rows("0")) - - haveRateLimitAction = false - action = tk.Session().GetSessionVars().MemTracker.GetFallbackForTest(false) - for ; action != nil; action = action.GetFallback() { - if action.GetPriority() == memory.DefRateLimitPriority { - haveRateLimitAction = true - break - } - } - require.False(t, haveRateLimitAction) -} - -func TestMaxExecutionTime(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("use test") - tk.MustExec("create table MaxExecTime( id int,name varchar(128),age int);") - tk.MustExec("begin") - tk.MustExec("insert into MaxExecTime (id,name,age) values (1,'john',18),(2,'lary',19),(3,'lily',18);") - - tk.MustQuery("select /*+ MAX_EXECUTION_TIME(1000) MAX_EXECUTION_TIME(500) */ * FROM MaxExecTime;") - require.Len(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings(), 1) - require.EqualError(t, tk.Session().GetSessionVars().StmtCtx.GetWarnings()[0].Err, "MAX_EXECUTION_TIME() is defined more than once, only the last definition takes effect: MAX_EXECUTION_TIME(500)") - require.True(t, tk.Session().GetSessionVars().StmtCtx.HasMaxExecutionTime) - require.Equal(t, uint64(500), tk.Session().GetSessionVars().StmtCtx.MaxExecutionTime) - - tk.MustQuery("select @@MAX_EXECUTION_TIME;").Check(testkit.Rows("0")) - tk.MustQuery("select @@global.MAX_EXECUTION_TIME;").Check(testkit.Rows("0")) - tk.MustQuery("select /*+ MAX_EXECUTION_TIME(1000) */ * FROM MaxExecTime;") - - tk.MustExec("set @@global.MAX_EXECUTION_TIME = 300;") - tk.MustQuery("select * FROM MaxExecTime;") - - tk.MustExec("set @@MAX_EXECUTION_TIME = 150;") - tk.MustQuery("select * FROM MaxExecTime;") - - tk.MustQuery("select @@global.MAX_EXECUTION_TIME;").Check(testkit.Rows("300")) - tk.MustQuery("select @@MAX_EXECUTION_TIME;").Check(testkit.Rows("150")) - - tk.MustExec("set @@global.MAX_EXECUTION_TIME = 0;") - tk.MustExec("set @@MAX_EXECUTION_TIME = 0;") - tk.MustExec("commit") - tk.MustExec("drop table if exists MaxExecTime;") -} - -func TestReplicaRead(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - require.Equal(t, kv.ReplicaReadLeader, tk.Session().GetSessionVars().GetReplicaRead()) - tk.MustExec("set @@tidb_replica_read = 'follower';") - require.Equal(t, kv.ReplicaReadFollower, tk.Session().GetSessionVars().GetReplicaRead()) - tk.MustExec("set @@tidb_replica_read = 'leader';") - require.Equal(t, kv.ReplicaReadLeader, tk.Session().GetSessionVars().GetReplicaRead()) -} - -func TestIsolationRead(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - require.Len(t, tk.Session().GetSessionVars().GetIsolationReadEngines(), 3) - tk.MustExec("set @@tidb_isolation_read_engines = 'tiflash';") - engines := tk.Session().GetSessionVars().GetIsolationReadEngines() - require.Len(t, engines, 1) - _, hasTiFlash := engines[kv.TiFlash] - _, hasTiKV := engines[kv.TiKV] - require.True(t, hasTiFlash) - require.False(t, hasTiKV) -} - -func TestIndexMergeRuntimeStats(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_enable_index_merge = 1") - tk.MustExec("create table t1(id int primary key, a int, b int, c int, d int)") - tk.MustExec("create index t1a on t1(a)") - tk.MustExec("create index t1b on t1(b)") - tk.MustExec("insert into t1 values(1,1,1,1,1),(2,2,2,2,2),(3,3,3,3,3),(4,4,4,4,4),(5,5,5,5,5)") - rows := tk.MustQuery("explain analyze select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4;").Rows() - require.Len(t, rows, 4) - explain := fmt.Sprintf("%v", rows[0]) - pattern := ".*time:.*loops:.*index_task:{fetch_handle:.*, merge:.*}.*table_task:{num.*concurrency.*fetch_row.*wait_time.*}.*" - require.Regexp(t, pattern, explain) - tableRangeExplain := fmt.Sprintf("%v", rows[1]) - indexExplain := fmt.Sprintf("%v", rows[2]) - tableExplain := fmt.Sprintf("%v", rows[3]) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableRangeExplain) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", indexExplain) - require.Regexp(t, ".*time:.*loops:.*cop_task:.*", tableExplain) - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustQuery("select /*+ use_index_merge(t1, primary, t1a) */ * from t1 where id < 2 or a > 4 order by a").Check(testkit.Rows("1 1 1 1 1", "5 5 5 5 5")) -} diff --git a/session/test/vars/BUILD.bazel b/session/test/vars/BUILD.bazel deleted file mode 100644 index 08aa55a99a57d..0000000000000 --- a/session/test/vars/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "vars_test", - timeout = "short", - srcs = [ - "main_test.go", - "vars_test.go", - ], - flaky = True, - shard_count = 7, - deps = [ - "//config", - "//domain", - "//kv", - "//parser/terror", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//txnkv/transaction", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/session/test/vars/main_test.go b/session/test/vars/main_test.go deleted file mode 100644 index af1d80ac01470..0000000000000 --- a/session/test/vars/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package vars - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/session/testutil.go b/session/testutil.go deleted file mode 100644 index 592ce9b45ba5a..0000000000000 --- a/session/testutil.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit/testenv" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" - atomicutil "go.uber.org/atomic" -) - -var ( - // GetBootstrapVersion is used in test - GetBootstrapVersion = getBootstrapVersion - // CurrentBootstrapVersion is used in test - CurrentBootstrapVersion = currentBootstrapVersion - // UnsetStoreBootstrapped is used in test - UnsetStoreBootstrapped = unsetStoreBootstrapped -) - -// CreateStoreAndBootstrap creates a mock store and bootstrap it. -func CreateStoreAndBootstrap(t *testing.T) (kv.Storage, *domain.Domain) { - testenv.SetGOMAXPROCSForTest() - store, err := mockstore.NewMockStore() - require.NoError(t, err) - dom, err := BootstrapSession(store) - require.NoError(t, err) - return store, dom -} - -var sessionKitIDGenerator atomicutil.Uint64 - -// CreateSessionAndSetID creates a session and set connection ID. -func CreateSessionAndSetID(t *testing.T, store kv.Storage) Session { - se, err := CreateSession4Test(store) - se.SetConnectionID(sessionKitIDGenerator.Inc()) - require.NoError(t, err) - return se -} - -// MustExec executes a sql statement and asserts no error occurs. -func MustExec(t *testing.T, se Session, sql string, args ...interface{}) { - rs, err := exec(se, sql, args...) - require.NoError(t, err) - if rs != nil { - require.NoError(t, rs.Close()) - } -} - -// MustExecToRecodeSet executes a sql statement and asserts no error occurs. -func MustExecToRecodeSet(t *testing.T, se Session, sql string, args ...interface{}) sqlexec.RecordSet { - rs, err := exec(se, sql, args...) - require.NoError(t, err) - return rs -} - -func exec(se Session, sql string, args ...interface{}) (sqlexec.RecordSet, error) { - ctx := context.Background() - if len(args) == 0 { - rs, err := se.Execute(ctx, sql) - if err == nil && len(rs) > 0 { - return rs[0], nil - } - return nil, err - } - stmtID, _, _, err := se.PrepareStmt(sql) - if err != nil { - return nil, err - } - params := expression.Args2Expressions4Test(args...) - rs, err := se.ExecutePreparedStmt(ctx, stmtID, params) - if err != nil { - return nil, err - } - return rs, nil -} diff --git a/session/tidb_test.go b/session/tidb_test.go deleted file mode 100644 index aed1826b8e8d1..0000000000000 --- a/session/tidb_test.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestDomapHandleNil(t *testing.T) { - // this is required for enterprise plugins - // ref: https://github.com/pingcap/tidb/issues/37319 - require.NotPanics(t, func() { - _, _ = domap.Get(nil) - }) -} - -func TestSysSessionPoolGoroutineLeak(t *testing.T) { - store, dom := CreateStoreAndBootstrap(t) - defer func() { require.NoError(t, store.Close()) }() - defer dom.Close() - - se, err := createSession(store) - require.NoError(t, err) - - count := 200 - stmts := make([]ast.StmtNode, count) - for i := 0; i < count; i++ { - stmt, err := se.ParseWithParams(context.Background(), "select * from mysql.user limit 1") - require.NoError(t, err) - stmts[i] = stmt - } - // Test an issue that sysSessionPool doesn't call session's Close, cause - // asyncGetTSWorker goroutine leak. - var wg util.WaitGroupWrapper - for i := 0; i < count; i++ { - s := stmts[i] - wg.Run(func() { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - _, _, err := se.ExecRestrictedStmt(ctx, s) - require.NoError(t, err) - }) - } - wg.Wait() -} - -func TestParseErrorWarn(t *testing.T) { - ctx := core.MockContext() - defer func() { - domain.GetDomain(ctx).StatsHandle().Close() - }() - nodes, err := Parse(ctx, "select /*+ adf */ 1") - require.NoError(t, err) - require.Len(t, nodes, 1) - require.Len(t, ctx.GetSessionVars().StmtCtx.GetWarnings(), 1) - - _, err = Parse(ctx, "select") - require.Error(t, err) -} - -func TestKeysNeedLock(t *testing.T) { - rowKey := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) - uniqueIndexKey := tablecodec.EncodeIndexSeekKey(1, 1, []byte{1}) - nonUniqueIndexKey := tablecodec.EncodeIndexSeekKey(1, 2, []byte{1}) - uniqueValue := make([]byte, 8) - uniqueUntouched := append(uniqueValue, '1') - nonUniqueVal := []byte{'0'} - nonUniqueUntouched := []byte{'1'} - var deleteVal []byte - rowVal := []byte{'a', 'b', 'c'} - tests := []struct { - key []byte - val []byte - need bool - }{ - {rowKey, rowVal, true}, - {rowKey, deleteVal, true}, - {nonUniqueIndexKey, nonUniqueVal, false}, - {nonUniqueIndexKey, nonUniqueUntouched, false}, - {uniqueIndexKey, uniqueValue, true}, - {uniqueIndexKey, uniqueUntouched, false}, - {uniqueIndexKey, deleteVal, false}, - } - - for _, test := range tests { - need := keyNeedToLock(test.key, test.val, 0) - require.Equal(t, test.need, need) - - flag := kv.KeyFlags(1) - need = keyNeedToLock(test.key, test.val, flag) - require.True(t, flag.HasPresumeKeyNotExists()) - require.True(t, need) - } -} diff --git a/session/txn.go b/session/txn.go deleted file mode 100644 index bf5d2894fda02..0000000000000 --- a/session/txn.go +++ /dev/null @@ -1,749 +0,0 @@ -// Copyright 2018 PingCAP, Inc. - -// 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. - -package session - -import ( - "bytes" - "context" - "fmt" - "runtime/trace" - "strings" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sli" - "github.com/pingcap/tidb/util/syncutil" - "github.com/pingcap/tipb/go-binlog" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" -) - -// LazyTxn wraps kv.Transaction to provide a new kv.Transaction. -// 1. It holds all statement related modification in the buffer before flush to the txn, -// so if execute statement meets error, the txn won't be made dirty. -// 2. It's a lazy transaction, that means it's a txnFuture before StartTS() is really need. -type LazyTxn struct { - // States of a LazyTxn should be one of the followings: - // Invalid: kv.Transaction == nil && txnFuture == nil - // Pending: kv.Transaction == nil && txnFuture != nil - // Valid: kv.Transaction != nil && txnFuture == nil - kv.Transaction - txnFuture *txnFuture - - initCnt int - stagingHandle kv.StagingHandle - mutations map[int64]*binlog.TableMutation - writeSLI sli.TxnWriteThroughputSLI - - enterFairLockingOnValid bool - - // TxnInfo is added for the lock view feature, the data is frequent modified but - // rarely read (just in query select * from information_schema.tidb_trx). - // The data in this session would be query by other sessions, so Mutex is necessary. - // Since read is rare, the reader can copy-on-read to get a data snapshot. - mu struct { - syncutil.RWMutex - txninfo.TxnInfo - } - - // mark the txn enables lazy uniqueness check in pessimistic transactions. - lazyUniquenessCheckEnabled bool -} - -// GetTableInfo returns the cached index name. -func (txn *LazyTxn) GetTableInfo(id int64) *model.TableInfo { - return txn.Transaction.GetTableInfo(id) -} - -// CacheTableInfo caches the index name. -func (txn *LazyTxn) CacheTableInfo(id int64, info *model.TableInfo) { - txn.Transaction.CacheTableInfo(id, info) -} - -func (txn *LazyTxn) init() { - txn.mutations = make(map[int64]*binlog.TableMutation) - txn.mu.Lock() - defer txn.mu.Unlock() - txn.mu.TxnInfo = txninfo.TxnInfo{} -} - -// call this under lock! -func (txn *LazyTxn) updateState(state txninfo.TxnRunningState) { - if txn.mu.TxnInfo.State != state { - lastState := txn.mu.TxnInfo.State - lastStateChangeTime := txn.mu.TxnInfo.LastStateChangeTime - txn.mu.TxnInfo.State = state - txn.mu.TxnInfo.LastStateChangeTime = time.Now() - if !lastStateChangeTime.IsZero() { - hasLockLbl := !txn.mu.TxnInfo.BlockStartTime.IsZero() - txninfo.TxnDurationHistogram(lastState, hasLockLbl).Observe(time.Since(lastStateChangeTime).Seconds()) - } - txninfo.TxnStatusEnteringCounter(state).Inc() - } -} - -func (txn *LazyTxn) initStmtBuf() { - if txn.Transaction == nil { - return - } - buf := txn.Transaction.GetMemBuffer() - txn.initCnt = buf.Len() - txn.stagingHandle = buf.Staging() -} - -// countHint is estimated count of mutations. -func (txn *LazyTxn) countHint() int { - if txn.stagingHandle == kv.InvalidStagingHandle { - return 0 - } - return txn.Transaction.GetMemBuffer().Len() - txn.initCnt -} - -func (txn *LazyTxn) flushStmtBuf() { - if txn.stagingHandle == kv.InvalidStagingHandle { - return - } - buf := txn.Transaction.GetMemBuffer() - - if txn.lazyUniquenessCheckEnabled { - keysNeedSetPersistentPNE := kv.FindKeysInStage(buf, txn.stagingHandle, func(k kv.Key, flags kv.KeyFlags, v []byte) bool { - return flags.HasPresumeKeyNotExists() - }) - for _, key := range keysNeedSetPersistentPNE { - buf.UpdateFlags(key, kv.SetPreviousPresumeKeyNotExists) - } - } - - buf.Release(txn.stagingHandle) - txn.initCnt = buf.Len() -} - -func (txn *LazyTxn) cleanupStmtBuf() { - if txn.stagingHandle == kv.InvalidStagingHandle { - return - } - buf := txn.Transaction.GetMemBuffer() - buf.Cleanup(txn.stagingHandle) - txn.initCnt = buf.Len() - - txn.mu.Lock() - defer txn.mu.Unlock() - txn.mu.TxnInfo.EntriesCount = uint64(txn.Transaction.Len()) -} - -// resetTxnInfo resets the transaction info. -// Note: call it under lock! -func (txn *LazyTxn) resetTxnInfo( - startTS uint64, - state txninfo.TxnRunningState, - entriesCount uint64, - currentSQLDigest string, - allSQLDigests []string, -) { - if !txn.mu.LastStateChangeTime.IsZero() { - lastState := txn.mu.State - hasLockLbl := !txn.mu.BlockStartTime.IsZero() - txninfo.TxnDurationHistogram(lastState, hasLockLbl).Observe(time.Since(txn.mu.TxnInfo.LastStateChangeTime).Seconds()) - } - if txn.mu.TxnInfo.StartTS != 0 { - txninfo.Recorder.OnTrxEnd(&txn.mu.TxnInfo) - } - txn.mu.TxnInfo = txninfo.TxnInfo{} - txn.mu.TxnInfo.StartTS = startTS - txn.mu.TxnInfo.State = state - txninfo.TxnStatusEnteringCounter(state).Inc() - txn.mu.TxnInfo.LastStateChangeTime = time.Now() - txn.mu.TxnInfo.EntriesCount = entriesCount - - txn.mu.TxnInfo.CurrentSQLDigest = currentSQLDigest - txn.mu.TxnInfo.AllSQLDigests = allSQLDigests -} - -// Size implements the MemBuffer interface. -func (txn *LazyTxn) Size() int { - if txn.Transaction == nil { - return 0 - } - return txn.Transaction.Size() -} - -// Mem implements the MemBuffer interface. -func (txn *LazyTxn) Mem() uint64 { - if txn.Transaction == nil { - return 0 - } - return txn.Transaction.Mem() -} - -// SetMemoryFootprintChangeHook sets the hook to be called when the memory footprint of this transaction changes. -func (txn *LazyTxn) SetMemoryFootprintChangeHook(hook func(uint64)) { - if txn.Transaction == nil { - return - } - txn.Transaction.SetMemoryFootprintChangeHook(hook) -} - -// Valid implements the kv.Transaction interface. -func (txn *LazyTxn) Valid() bool { - return txn.Transaction != nil && txn.Transaction.Valid() -} - -func (txn *LazyTxn) pending() bool { - return txn.Transaction == nil && txn.txnFuture != nil -} - -func (txn *LazyTxn) validOrPending() bool { - return txn.txnFuture != nil || txn.Valid() -} - -func (txn *LazyTxn) String() string { - if txn.Transaction != nil { - return txn.Transaction.String() - } - if txn.txnFuture != nil { - res := "txnFuture" - if txn.enterFairLockingOnValid { - res += " (pending fair locking)" - } - return res - } - return "invalid transaction" -} - -// GoString implements the "%#v" format for fmt.Printf. -func (txn *LazyTxn) GoString() string { - var s strings.Builder - s.WriteString("Txn{") - if txn.pending() { - s.WriteString("state=pending") - } else if txn.Valid() { - s.WriteString("state=valid") - fmt.Fprintf(&s, ", txnStartTS=%d", txn.Transaction.StartTS()) - if len(txn.mutations) > 0 { - fmt.Fprintf(&s, ", len(mutations)=%d, %#v", len(txn.mutations), txn.mutations) - } - } else { - s.WriteString("state=invalid") - } - - s.WriteString("}") - return s.String() -} - -// GetOption implements the GetOption -func (txn *LazyTxn) GetOption(opt int) interface{} { - if txn.Transaction == nil { - if opt == kv.TxnScope { - return "" - } - return nil - } - return txn.Transaction.GetOption(opt) -} - -func (txn *LazyTxn) changeToPending(future *txnFuture) { - txn.Transaction = nil - txn.txnFuture = future -} - -func (txn *LazyTxn) changePendingToValid(ctx context.Context, sctx sessionctx.Context) error { - if txn.txnFuture == nil { - return errors.New("transaction future is not set") - } - - future := txn.txnFuture - txn.txnFuture = nil - - defer trace.StartRegion(ctx, "WaitTsoFuture").End() - t, err := future.wait() - if err != nil { - txn.Transaction = nil - return err - } - txn.Transaction = t - txn.initStmtBuf() - - if txn.enterFairLockingOnValid { - txn.enterFairLockingOnValid = false - err = txn.Transaction.StartFairLocking() - if err != nil { - return err - } - } - - // The txnInfo may already recorded the first statement (usually "begin") when it's pending, so keep them. - txn.mu.Lock() - defer txn.mu.Unlock() - txn.resetTxnInfo( - t.StartTS(), - txninfo.TxnIdle, - uint64(txn.Transaction.Len()), - txn.mu.TxnInfo.CurrentSQLDigest, - txn.mu.TxnInfo.AllSQLDigests) - - // set resource group name for kv request such as lock pessimistic keys. - kv.SetTxnResourceGroup(txn, sctx.GetSessionVars().ResourceGroupName) - - return nil -} - -func (txn *LazyTxn) changeToInvalid() { - if txn.stagingHandle != kv.InvalidStagingHandle { - txn.Transaction.GetMemBuffer().Cleanup(txn.stagingHandle) - } - txn.stagingHandle = kv.InvalidStagingHandle - txn.Transaction = nil - txn.txnFuture = nil - - txn.enterFairLockingOnValid = false - - txn.mu.Lock() - lastState := txn.mu.TxnInfo.State - lastStateChangeTime := txn.mu.TxnInfo.LastStateChangeTime - hasLock := !txn.mu.TxnInfo.BlockStartTime.IsZero() - if txn.mu.TxnInfo.StartTS != 0 { - txninfo.Recorder.OnTrxEnd(&txn.mu.TxnInfo) - } - txn.mu.TxnInfo = txninfo.TxnInfo{} - txn.mu.Unlock() - if !lastStateChangeTime.IsZero() { - txninfo.TxnDurationHistogram(lastState, hasLock).Observe(time.Since(lastStateChangeTime).Seconds()) - } -} - -func (txn *LazyTxn) onStmtStart(currentSQLDigest string) { - if len(currentSQLDigest) == 0 { - return - } - - txn.mu.Lock() - defer txn.mu.Unlock() - txn.updateState(txninfo.TxnRunning) - txn.mu.TxnInfo.CurrentSQLDigest = currentSQLDigest - // Keeps at most 50 history sqls to avoid consuming too much memory. - const maxTransactionStmtHistory int = 50 - if len(txn.mu.TxnInfo.AllSQLDigests) < maxTransactionStmtHistory { - txn.mu.TxnInfo.AllSQLDigests = append(txn.mu.TxnInfo.AllSQLDigests, currentSQLDigest) - } -} - -func (txn *LazyTxn) onStmtEnd() { - txn.mu.Lock() - defer txn.mu.Unlock() - txn.mu.TxnInfo.CurrentSQLDigest = "" - txn.updateState(txninfo.TxnIdle) -} - -var hasMockAutoIncIDRetry = int64(0) - -func enableMockAutoIncIDRetry() { - atomic.StoreInt64(&hasMockAutoIncIDRetry, 1) -} - -func mockAutoIncIDRetry() bool { - return atomic.LoadInt64(&hasMockAutoIncIDRetry) == 1 -} - -var mockAutoRandIDRetryCount = int64(0) - -func needMockAutoRandIDRetry() bool { - return atomic.LoadInt64(&mockAutoRandIDRetryCount) > 0 -} - -func decreaseMockAutoRandIDRetryCount() { - atomic.AddInt64(&mockAutoRandIDRetryCount, -1) -} - -// ResetMockAutoRandIDRetryCount set the number of occurrences of -// `kv.ErrTxnRetryable` when calling TxnState.Commit(). -func ResetMockAutoRandIDRetryCount(failTimes int64) { - atomic.StoreInt64(&mockAutoRandIDRetryCount, failTimes) -} - -// Commit overrides the Transaction interface. -func (txn *LazyTxn) Commit(ctx context.Context) error { - defer txn.reset() - if len(txn.mutations) != 0 || txn.countHint() != 0 { - logutil.BgLogger().Error("the code should never run here", - zap.String("TxnState", txn.GoString()), - zap.Int("staging handler", int(txn.stagingHandle)), - zap.Int("mutations", txn.countHint()), - zap.Stack("something must be wrong")) - return errors.Trace(kv.ErrInvalidTxn) - } - - txn.mu.Lock() - txn.updateState(txninfo.TxnCommitting) - txn.mu.Unlock() - - failpoint.Inject("mockSlowCommit", func(_ failpoint.Value) {}) - - // mockCommitError8942 is used for PR #8942. - failpoint.Inject("mockCommitError8942", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(kv.ErrTxnRetryable) - } - }) - - // mockCommitRetryForAutoIncID is used to mock an commit retry for adjustAutoIncrementDatum. - failpoint.Inject("mockCommitRetryForAutoIncID", func(val failpoint.Value) { - if val.(bool) && !mockAutoIncIDRetry() { - enableMockAutoIncIDRetry() - failpoint.Return(kv.ErrTxnRetryable) - } - }) - - failpoint.Inject("mockCommitRetryForAutoRandID", func(val failpoint.Value) { - if val.(bool) && needMockAutoRandIDRetry() { - decreaseMockAutoRandIDRetryCount() - failpoint.Return(kv.ErrTxnRetryable) - } - }) - - return txn.Transaction.Commit(ctx) -} - -// Rollback overrides the Transaction interface. -func (txn *LazyTxn) Rollback() error { - defer txn.reset() - txn.mu.Lock() - txn.updateState(txninfo.TxnRollingBack) - txn.mu.Unlock() - // mockSlowRollback is used to mock a rollback which takes a long time - failpoint.Inject("mockSlowRollback", func(_ failpoint.Value) {}) - return txn.Transaction.Rollback() -} - -// RollbackMemDBToCheckpoint overrides the Transaction interface. -func (txn *LazyTxn) RollbackMemDBToCheckpoint(savepoint *tikv.MemDBCheckpoint) { - txn.flushStmtBuf() - txn.Transaction.RollbackMemDBToCheckpoint(savepoint) - txn.cleanup() -} - -// LockKeys wraps the inner transaction's `LockKeys` to record the status -func (txn *LazyTxn) LockKeys(ctx context.Context, lockCtx *kv.LockCtx, keys ...kv.Key) error { - return txn.LockKeysFunc(ctx, lockCtx, nil, keys...) -} - -// LockKeysFunc Wrap the inner transaction's `LockKeys` to record the status -func (txn *LazyTxn) LockKeysFunc(ctx context.Context, lockCtx *kv.LockCtx, fn func(), keys ...kv.Key) error { - failpoint.Inject("beforeLockKeys", func() {}) - t := time.Now() - - var originState txninfo.TxnRunningState - txn.mu.Lock() - originState = txn.mu.TxnInfo.State - txn.updateState(txninfo.TxnLockAcquiring) - txn.mu.TxnInfo.BlockStartTime.Valid = true - txn.mu.TxnInfo.BlockStartTime.Time = t - txn.mu.Unlock() - lockFunc := func() { - if fn != nil { - fn() - } - txn.mu.Lock() - defer txn.mu.Unlock() - txn.updateState(originState) - txn.mu.TxnInfo.BlockStartTime.Valid = false - txn.mu.TxnInfo.EntriesCount = uint64(txn.Transaction.Len()) - } - return txn.Transaction.LockKeysFunc(ctx, lockCtx, lockFunc, keys...) -} - -// StartFairLocking wraps the inner transaction to support using fair locking with lazy initialization. -func (txn *LazyTxn) StartFairLocking() error { - if txn.Valid() { - return txn.Transaction.StartFairLocking() - } else if !txn.pending() { - err := errors.New("trying to start fair locking on a transaction in invalid state") - logutil.BgLogger().Error("unexpected error when starting fair locking", zap.Error(err), zap.Stringer("txn", txn)) - return err - } - txn.enterFairLockingOnValid = true - return nil -} - -// RetryFairLocking wraps the inner transaction to support using fair locking with lazy initialization. -func (txn *LazyTxn) RetryFairLocking(ctx context.Context) error { - if txn.Valid() { - return txn.Transaction.RetryFairLocking(ctx) - } else if !txn.pending() { - err := errors.New("trying to retry fair locking on a transaction in invalid state") - logutil.BgLogger().Error("unexpected error when retrying fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) - return err - } - return nil -} - -// CancelFairLocking wraps the inner transaction to support using fair locking with lazy initialization. -func (txn *LazyTxn) CancelFairLocking(ctx context.Context) error { - if txn.Valid() { - return txn.Transaction.CancelFairLocking(ctx) - } else if !txn.pending() { - err := errors.New("trying to cancel fair locking on a transaction in invalid state") - logutil.BgLogger().Error("unexpected error when cancelling fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) - return err - } - if !txn.enterFairLockingOnValid { - err := errors.New("trying to cancel fair locking when it's not started") - logutil.BgLogger().Error("unexpected error when cancelling fair locking", zap.Error(err), zap.Stringer("txnStartTS", txn)) - return err - } - txn.enterFairLockingOnValid = false - return nil -} - -// DoneFairLocking wraps the inner transaction to support using fair locking with lazy initialization. -func (txn *LazyTxn) DoneFairLocking(ctx context.Context) error { - if txn.Valid() { - return txn.Transaction.DoneFairLocking(ctx) - } - if !txn.pending() { - err := errors.New("trying to cancel fair locking on a transaction in invalid state") - logutil.BgLogger().Error("unexpected error when finishing fair locking") - return err - } - if !txn.enterFairLockingOnValid { - err := errors.New("trying to finish fair locking when it's not started") - logutil.BgLogger().Error("unexpected error when finishing fair locking") - return err - } - txn.enterFairLockingOnValid = false - return nil -} - -// IsInFairLockingMode wraps the inner transaction to support using fair locking with lazy initialization. -func (txn *LazyTxn) IsInFairLockingMode() bool { - if txn.Valid() { - return txn.Transaction.IsInFairLockingMode() - } else if txn.pending() { - return txn.enterFairLockingOnValid - } else { - return false - } -} - -func (txn *LazyTxn) reset() { - txn.cleanup() - txn.changeToInvalid() -} - -func (txn *LazyTxn) cleanup() { - txn.cleanupStmtBuf() - txn.initStmtBuf() - for key := range txn.mutations { - delete(txn.mutations, key) - } -} - -// KeysNeedToLock returns the keys need to be locked. -func (txn *LazyTxn) KeysNeedToLock() ([]kv.Key, error) { - if txn.stagingHandle == kv.InvalidStagingHandle { - return nil, nil - } - keys := make([]kv.Key, 0, txn.countHint()) - buf := txn.Transaction.GetMemBuffer() - buf.InspectStage(txn.stagingHandle, func(k kv.Key, flags kv.KeyFlags, v []byte) { - if !keyNeedToLock(k, v, flags) { - return - } - keys = append(keys, k) - }) - - return keys, nil -} - -// Wait converts pending txn to valid -func (txn *LazyTxn) Wait(ctx context.Context, sctx sessionctx.Context) (kv.Transaction, error) { - if !txn.validOrPending() { - return txn, errors.AddStack(kv.ErrInvalidTxn) - } - if txn.pending() { - defer func(begin time.Time) { - sctx.GetSessionVars().DurationWaitTS = time.Since(begin) - }(time.Now()) - - // Transaction is lazy initialized. - // PrepareTxnCtx is called to get a tso future, makes s.txn a pending txn, - // If Txn() is called later, wait for the future to get a valid txn. - if err := txn.changePendingToValid(ctx, sctx); err != nil { - logutil.BgLogger().Error("active transaction fail", - zap.Error(err)) - txn.cleanup() - sctx.GetSessionVars().TxnCtx.StartTS = 0 - return txn, err - } - txn.lazyUniquenessCheckEnabled = !sctx.GetSessionVars().ConstraintCheckInPlacePessimistic - } - return txn, nil -} - -func keyNeedToLock(k, v []byte, flags kv.KeyFlags) bool { - isTableKey := bytes.HasPrefix(k, tablecodec.TablePrefix()) - if !isTableKey { - // meta key always need to lock. - return true - } - - // a pessimistic locking is skipped, perform the conflict check and - // constraint check (more accurately, PresumeKeyNotExist) in prewrite (or later pessimistic locking) - if flags.HasNeedConstraintCheckInPrewrite() { - return false - } - - if flags.HasPresumeKeyNotExists() { - return true - } - - // lock row key, primary key and unique index for delete operation, - if len(v) == 0 { - return flags.HasNeedLocked() || tablecodec.IsRecordKey(k) - } - - if tablecodec.IsUntouchedIndexKValue(k, v) { - return false - } - - if !tablecodec.IsIndexKey(k) { - return true - } - - if tablecodec.IsTempIndexKey(k) { - tmpVal, err := tablecodec.DecodeTempIndexValue(v) - if err != nil { - logutil.BgLogger().Warn("decode temp index value failed", zap.Error(err)) - return false - } - current := tmpVal.Current() - return current.Handle != nil || tablecodec.IndexKVIsUnique(current.Value) - } - - return tablecodec.IndexKVIsUnique(v) -} - -func getBinlogMutation(ctx sessionctx.Context, tableID int64) *binlog.TableMutation { - bin := binloginfo.GetPrewriteValue(ctx, true) - for i := range bin.Mutations { - if bin.Mutations[i].TableId == tableID { - return &bin.Mutations[i] - } - } - idx := len(bin.Mutations) - bin.Mutations = append(bin.Mutations, binlog.TableMutation{TableId: tableID}) - return &bin.Mutations[idx] -} - -func mergeToMutation(m1, m2 *binlog.TableMutation) { - m1.InsertedRows = append(m1.InsertedRows, m2.InsertedRows...) - m1.UpdatedRows = append(m1.UpdatedRows, m2.UpdatedRows...) - m1.DeletedIds = append(m1.DeletedIds, m2.DeletedIds...) - m1.DeletedPks = append(m1.DeletedPks, m2.DeletedPks...) - m1.DeletedRows = append(m1.DeletedRows, m2.DeletedRows...) - m1.Sequence = append(m1.Sequence, m2.Sequence...) -} - -type txnFailFuture struct{} - -func (txnFailFuture) Wait() (uint64, error) { - return 0, errors.New("mock get timestamp fail") -} - -// txnFuture is a promise, which promises to return a txn in future. -type txnFuture struct { - future oracle.Future - store kv.Storage - txnScope string -} - -func (tf *txnFuture) wait() (kv.Transaction, error) { - startTS, err := tf.future.Wait() - failpoint.Inject("txnFutureWait", func() {}) - if err == nil { - return tf.store.Begin(tikv.WithTxnScope(tf.txnScope), tikv.WithStartTS(startTS)) - } else if config.GetGlobalConfig().Store == "unistore" { - return nil, err - } - - logutil.BgLogger().Warn("wait tso failed", zap.Error(err)) - // It would retry get timestamp. - return tf.store.Begin(tikv.WithTxnScope(tf.txnScope)) -} - -// HasDirtyContent checks whether there's dirty update on the given table. -// Put this function here is to avoid cycle import. -func (s *session) HasDirtyContent(tid int64) bool { - if s.txn.Transaction == nil { - return false - } - seekKey := tablecodec.EncodeTablePrefix(tid) - it, err := s.txn.GetMemBuffer().Iter(seekKey, nil) - terror.Log(err) - return it.Valid() && bytes.HasPrefix(it.Key(), seekKey) -} - -// StmtCommit implements the sessionctx.Context interface. -func (s *session) StmtCommit(ctx context.Context) { - defer func() { - s.txn.cleanup() - }() - - txnManager := sessiontxn.GetTxnManager(s) - err := txnManager.OnStmtCommit(ctx) - if err != nil { - logutil.Logger(ctx).Error("txnManager failed to handle OnStmtCommit", zap.Error(err)) - } - - st := &s.txn - st.flushStmtBuf() - - // Need to flush binlog. - for tableID, delta := range st.mutations { - mutation := getBinlogMutation(s, tableID) - mergeToMutation(mutation, delta) - } -} - -// StmtRollback implements the sessionctx.Context interface. -func (s *session) StmtRollback(ctx context.Context, isForPessimisticRetry bool) { - txnManager := sessiontxn.GetTxnManager(s) - err := txnManager.OnStmtRollback(ctx, isForPessimisticRetry) - if err != nil { - logutil.Logger(ctx).Error("txnManager failed to handle OnStmtRollback", zap.Error(err)) - } - s.txn.cleanup() -} - -// StmtGetMutation implements the sessionctx.Context interface. -func (s *session) StmtGetMutation(tableID int64) *binlog.TableMutation { - st := &s.txn - if _, ok := st.mutations[tableID]; !ok { - st.mutations[tableID] = &binlog.TableMutation{TableId: tableID} - } - return st.mutations[tableID] -} diff --git a/session/txninfo/BUILD.bazel b/session/txninfo/BUILD.bazel deleted file mode 100644 index 3e7e0864cd436..0000000000000 --- a/session/txninfo/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "txninfo", - srcs = [ - "summary.go", - "txn_info.go", - ], - importpath = "github.com/pingcap/tidb/session/txninfo", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//parser/mysql", - "//types", - "//util/logutil", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_zap//:zap", - ], -) diff --git a/session/txninfo/summary.go b/session/txninfo/summary.go deleted file mode 100644 index 30058c1d87e88..0000000000000 --- a/session/txninfo/summary.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2021 PingCAP, Inc. - -// 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. - -package txninfo - -import ( - "container/list" - "encoding/json" - "fmt" - "hash/fnv" - "sync" - "time" - - "github.com/pingcap/tidb/types" - "github.com/tikv/client-go/v2/oracle" -) - -func digest(digests []string) uint64 { - // We use FNV-1a hash to generate the 64bit digest - // since 64bit digest use less memory and FNV-1a is faster than most of other hash algorithms - // You can refer to https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed - hash := fnv.New64a() - for _, digest := range digests { - hash.Write([]byte(digest)) - } - return hash.Sum64() -} - -type trxSummaryEntry struct { - trxDigest uint64 - digests []string -} - -type trxSummaries struct { - capacity uint - - // lru cache for digest -> trxSummaryEntry - elements map[uint64]*list.Element - cache *list.List -} - -func newTrxSummaries(capacity uint) trxSummaries { - return trxSummaries{ - capacity: capacity, - cache: list.New(), - elements: make(map[uint64]*list.Element), - } -} - -func (s *trxSummaries) onTrxEnd(digests []string) { - key := digest(digests) - element, exists := s.elements[key] - if exists { - s.cache.MoveToFront(element) - return - } - e := trxSummaryEntry{ - trxDigest: key, - digests: digests, - } - s.elements[key] = s.cache.PushFront(e) - if uint(s.cache.Len()) > s.capacity { - last := s.cache.Back() - delete(s.elements, last.Value.(trxSummaryEntry).trxDigest) - s.cache.Remove(last) - } -} - -func (s *trxSummaries) dumpTrxSummary() [][]types.Datum { - var result [][]types.Datum - for element := s.cache.Front(); element != nil; element = element.Next() { - sqls := element.Value.(trxSummaryEntry).digests - // for consistency with other digests in TiDB, we calculate sum256 here to generate varchar(64) digest - digest := fmt.Sprintf("%x", element.Value.(trxSummaryEntry).trxDigest) - - res, err := json.Marshal(sqls) - if err != nil { - panic(err) - } - - result = append(result, []types.Datum{ - types.NewDatum(digest), - types.NewDatum(string(res)), - }) - } - return result -} - -func (s *trxSummaries) resize(capacity uint) { - s.capacity = capacity - for uint(s.cache.Len()) > s.capacity { - last := s.cache.Back() - delete(s.elements, last.Value.(trxSummaryEntry).trxDigest) - s.cache.Remove(last) - } -} - -// TrxHistoryRecorder is a history recorder for transaction. -type TrxHistoryRecorder struct { - mu sync.Mutex - minDuration time.Duration - summaries trxSummaries -} - -// DumpTrxSummary dumps the transaction summary to Datum for displaying in `TRX_SUMMARY` table. -func (recorder *TrxHistoryRecorder) DumpTrxSummary() [][]types.Datum { - recorder.mu.Lock() - defer recorder.mu.Unlock() - return recorder.summaries.dumpTrxSummary() -} - -// OnTrxEnd should be called when a transaction ends, ie. leaves `TIDB_TRX` table. -func (recorder *TrxHistoryRecorder) OnTrxEnd(info *TxnInfo) { - now := time.Now() - startTime := time.UnixMilli(oracle.ExtractPhysical(info.StartTS)) - recorder.mu.Lock() - defer recorder.mu.Unlock() - if now.Sub(startTime) < recorder.minDuration { - return - } - recorder.summaries.onTrxEnd(info.AllSQLDigests) -} - -func newTrxHistoryRecorder(summariesCap uint) TrxHistoryRecorder { - return TrxHistoryRecorder{ - summaries: newTrxSummaries(summariesCap), - minDuration: 1 * time.Second, - } -} - -// Clean clears the history recorder. For test only. -func (recorder *TrxHistoryRecorder) Clean() { - recorder.summaries.cache = list.New() -} - -// SetMinDuration sets the minimum duration for a transaction to be recorded. -func (recorder *TrxHistoryRecorder) SetMinDuration(d time.Duration) { - recorder.mu.Lock() - defer recorder.mu.Unlock() - recorder.minDuration = d -} - -// ResizeSummaries resizes the summaries capacity. -func (recorder *TrxHistoryRecorder) ResizeSummaries(capacity uint) { - recorder.mu.Lock() - defer recorder.mu.Unlock() - recorder.summaries.resize(capacity) -} - -// Recorder is the recorder instance. -var Recorder = newTrxHistoryRecorder(0) diff --git a/sessionctx/BUILD.bazel b/sessionctx/BUILD.bazel deleted file mode 100644 index c5c04c32b0e77..0000000000000 --- a/sessionctx/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sessionctx", - srcs = ["context.go"], - importpath = "github.com/pingcap/tidb/sessionctx", - visibility = ["//visibility:public"], - deps = [ - "//extension", - "//kv", - "//metrics", - "//parser/model", - "//sessionctx/sessionstates", - "//sessionctx/variable", - "//util", - "//util/kvcache", - "//util/plancache", - "//util/sli", - "//util/topsql/stmtstats", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_tikv_client_go_v2//oracle", - ], -) - -go_test( - name = "sessionctx_test", - timeout = "short", - srcs = [ - "context_test.go", - "main_test.go", - ], - embed = [":sessionctx"], - flaky = True, - race = "on", - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessionctx/binloginfo/BUILD.bazel b/sessionctx/binloginfo/BUILD.bazel deleted file mode 100644 index 51e43939b3bff..0000000000000 --- a/sessionctx/binloginfo/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "binloginfo", - srcs = ["binloginfo.go"], - importpath = "github.com/pingcap/tidb/sessionctx/binloginfo", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//kv", - "//metrics", - "//parser", - "//parser/format", - "//parser/terror", - "//sessionctx", - "//tidb-binlog/node", - "//tidb-binlog/pump_client", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_tipb//go-binlog", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "binloginfo_test", - timeout = "short", - srcs = [ - "binloginfo_test.go", - "main_test.go", - ], - embed = [":binloginfo"], - flaky = True, - shard_count = 11, - deps = [ - "//autoid_service", - "//ddl", - "//domain", - "//kv", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//table/tables", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//tidb-binlog/pump_client", - "//types", - "//util/codec", - "//util/collate", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_stretchr_testify//require", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessionctx/binloginfo/main_test.go b/sessionctx/binloginfo/main_test.go deleted file mode 100644 index 647225858fae7..0000000000000 --- a/sessionctx/binloginfo/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package binloginfo - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/sessionctx/context.go b/sessionctx/context.go deleted file mode 100644 index 9151aba7fdab6..0000000000000 --- a/sessionctx/context.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package sessionctx - -import ( - "context" - "fmt" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/kvcache" - utilpc "github.com/pingcap/tidb/util/plancache" - "github.com/pingcap/tidb/util/sli" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "github.com/pingcap/tipb/go-binlog" - "github.com/tikv/client-go/v2/oracle" -) - -// InfoschemaMetaVersion is a workaround. Due to circular dependency, -// can not return the complete interface. But SchemaMetaVersion is widely used for logging. -// So we give a convenience for that. -// FIXME: remove this interface -type InfoschemaMetaVersion interface { - SchemaMetaVersion() int64 -} - -// SessionStatesHandler is an interface for encoding and decoding session states. -type SessionStatesHandler interface { - // EncodeSessionStates encodes session states into a JSON. - EncodeSessionStates(context.Context, Context, *sessionstates.SessionStates) error - // DecodeSessionStates decodes a map into session states. - DecodeSessionStates(context.Context, Context, *sessionstates.SessionStates) error -} - -// PlanCache is an interface for prepare and non-prepared plan cache -type PlanCache interface { - Get(key kvcache.Key, opts *utilpc.PlanCacheMatchOpts) (value kvcache.Value, ok bool) - Put(key kvcache.Key, value kvcache.Value, opts *utilpc.PlanCacheMatchOpts) - Delete(key kvcache.Key) - DeleteAll() - Size() int - SetCapacity(capacity uint) error - Close() -} - -// Context is an interface for transaction and executive args environment. -type Context interface { - SessionStatesHandler - // SetDiskFullOpt set the disk full opt when tikv disk full happened. - SetDiskFullOpt(level kvrpcpb.DiskFullOpt) - // RollbackTxn rolls back the current transaction. - RollbackTxn(ctx context.Context) - // CommitTxn commits the current transaction. - CommitTxn(ctx context.Context) error - // Txn returns the current transaction which is created before executing a statement. - // The returned kv.Transaction is not nil, but it maybe pending or invalid. - // If the active parameter is true, call this function will wait for the pending txn - // to become valid. - Txn(active bool) (kv.Transaction, error) - - // GetClient gets a kv.Client. - GetClient() kv.Client - - // GetMPPClient gets a kv.MPPClient. - GetMPPClient() kv.MPPClient - - // SetValue saves a value associated with this context for key. - SetValue(key fmt.Stringer, value interface{}) - - // Value returns the value associated with this context for key. - Value(key fmt.Stringer) interface{} - - // ClearValue clears the value associated with this context for key. - ClearValue(key fmt.Stringer) - - // Deprecated: the semantics of session.GetInfoSchema() is ambiguous - // If you want to get the infoschema of the current transaction in SQL layer, use sessiontxn.GetTxnManager(ctx).GetTxnInfoSchema() - // If you want to get the latest infoschema use `GetDomainInfoSchema` - GetInfoSchema() InfoschemaMetaVersion - - // GetDomainInfoSchema returns the latest information schema in domain - // Different with `domain.InfoSchema()`, the information schema returned by this method - // includes the temporary table definitions stored in session - GetDomainInfoSchema() InfoschemaMetaVersion - - GetSessionVars() *variable.SessionVars - - GetSessionManager() util.SessionManager - - // RefreshTxnCtx commits old transaction without retry, - // and creates a new transaction. - // now just for load data and batch insert. - RefreshTxnCtx(context.Context) error - - // GetStore returns the store of session. - GetStore() kv.Storage - - // GetSessionPlanCache returns the session-level cache of the physical plan. - GetSessionPlanCache() PlanCache - - // UpdateColStatsUsage updates the column stats usage. - UpdateColStatsUsage(predicateColumns []model.TableItemID) - - // HasDirtyContent checks whether there's dirty update on the given table. - HasDirtyContent(tid int64) bool - - // StmtCommit flush all changes by the statement to the underlying transaction. - StmtCommit(ctx context.Context) - // StmtRollback provides statement level rollback. The parameter `forPessimisticRetry` should be true iff it's used - // for auto-retrying execution of DMLs in pessimistic transactions. - StmtRollback(ctx context.Context, isForPessimisticRetry bool) - // StmtGetMutation gets the binlog mutation for current statement. - StmtGetMutation(int64) *binlog.TableMutation - // IsDDLOwner checks whether this session is DDL owner. - IsDDLOwner() bool - // AddTableLock adds table lock to the session lock map. - AddTableLock([]model.TableLockTpInfo) - // ReleaseTableLocks releases table locks in the session lock map. - ReleaseTableLocks(locks []model.TableLockTpInfo) - // ReleaseTableLockByTableIDs releases table locks in the session lock map by table IDs. - ReleaseTableLockByTableIDs(tableIDs []int64) - // CheckTableLocked checks the table lock. - CheckTableLocked(tblID int64) (bool, model.TableLockType) - // GetAllTableLocks gets all table locks table id and db id hold by the session. - GetAllTableLocks() []model.TableLockTpInfo - // ReleaseAllTableLocks releases all table locks hold by the session. - ReleaseAllTableLocks() - // HasLockedTables uses to check whether this session locked any tables. - HasLockedTables() bool - // PrepareTSFuture uses to prepare timestamp by future. - PrepareTSFuture(ctx context.Context, future oracle.Future, scope string) error - // GetPreparedTxnFuture returns the TxnFuture if it is valid or pending. - // It returns nil otherwise. - GetPreparedTxnFuture() TxnFuture - // StoreIndexUsage stores the index usage information. - StoreIndexUsage(tblID int64, idxID int64, rowsSelected int64) - // GetTxnWriteThroughputSLI returns the TxnWriteThroughputSLI. - GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI - // GetBuiltinFunctionUsage returns the BuiltinFunctionUsage of current Context, which is not thread safe. - // Use primitive map type to prevent circular import. Should convert it to telemetry.BuiltinFunctionUsage before using. - GetBuiltinFunctionUsage() map[string]uint32 - // BuiltinFunctionUsageInc increase the counting of each builtin function usage - // Notice that this is a thread safe function - BuiltinFunctionUsageInc(scalarFuncSigName string) - // GetStmtStats returns stmtstats.StatementStats owned by implementation. - GetStmtStats() *stmtstats.StatementStats - // ShowProcess returns ProcessInfo running in current Context - ShowProcess() *util.ProcessInfo - // GetAdvisoryLock acquires an advisory lock (aka GET_LOCK()). - GetAdvisoryLock(string, int64) error - // IsUsedAdvisoryLock checks for existing locks (aka IS_USED_LOCK()). - IsUsedAdvisoryLock(string) uint64 - // ReleaseAdvisoryLock releases an advisory lock (aka RELEASE_LOCK()). - ReleaseAdvisoryLock(string) bool - // ReleaseAllAdvisoryLocks releases all advisory locks that this session holds. - ReleaseAllAdvisoryLocks() int - // GetExtensions returns the `*extension.SessionExtensions` object - GetExtensions() *extension.SessionExtensions - // InSandBoxMode indicates that this Session is in sandbox mode - // Ref about sandbox mode: https://dev.mysql.com/doc/refman/8.0/en/expired-password-handling.html - InSandBoxMode() bool - // EnableSandBoxMode enable the sandbox mode of this Session - EnableSandBoxMode() - // DisableSandBoxMode enable the sandbox mode of this Session - DisableSandBoxMode() -} - -// TxnFuture is an interface where implementations have a kv.Transaction field and after -// calling Wait of the TxnFuture, the kv.Transaction will become valid. -type TxnFuture interface { - // Wait converts pending txn to valid - Wait(ctx context.Context, sctx Context) (kv.Transaction, error) -} - -type basicCtxType int - -func (t basicCtxType) String() string { - switch t { - case QueryString: - return "query_string" - case Initing: - return "initing" - case LastExecuteDDL: - return "last_execute_ddl" - } - return "unknown" -} - -// Context keys. -const ( - // QueryString is the key for original query string. - QueryString basicCtxType = 1 - // Initing is the key for indicating if the server is running bootstrap or upgrade job. - Initing basicCtxType = 2 - // LastExecuteDDL is the key for whether the session execute a ddl command last time. - LastExecuteDDL basicCtxType = 3 -) - -// ValidateSnapshotReadTS strictly validates that readTS does not exceed the PD timestamp -func ValidateSnapshotReadTS(ctx context.Context, sctx Context, readTS uint64) error { - latestTS, err := sctx.GetStore().GetOracle().GetLowResolutionTimestamp(ctx, &oracle.Option{TxnScope: oracle.GlobalTxnScope}) - // If we fail to get latestTS or the readTS exceeds it, get a timestamp from PD to double check - if err != nil || readTS > latestTS { - metrics.ValidateReadTSFromPDCount.Inc() - currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) - if err != nil { - return errors.Errorf("fail to validate read timestamp: %v", err) - } - if readTS > currentVer.Ver { - return errors.Errorf("cannot set read timestamp to a future time") - } - } - return nil -} - -// How far future from now ValidateStaleReadTS allows at most -const allowedTimeFromNow = 100 * time.Millisecond - -// ValidateStaleReadTS validates that readTS does not exceed the current time not strictly. -func ValidateStaleReadTS(ctx context.Context, sctx Context, readTS uint64) error { - currentTS, err := sctx.GetSessionVars().StmtCtx.GetStaleTSO() - if currentTS == 0 || err != nil { - currentTS, err = sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) - } - // If we fail to calculate currentTS from local time, fallback to get a timestamp from PD - if err != nil { - metrics.ValidateReadTSFromPDCount.Inc() - currentVer, err := sctx.GetStore().CurrentVersion(oracle.GlobalTxnScope) - if err != nil { - return errors.Errorf("fail to validate read timestamp: %v", err) - } - currentTS = currentVer.Ver - } - if oracle.GetTimeFromTS(readTS).After(oracle.GetTimeFromTS(currentTS).Add(allowedTimeFromNow)) { - return errors.Errorf("cannot set read timestamp to a future time") - } - return nil -} - -// SysProcTracker is used to track background sys processes -type SysProcTracker interface { - Track(id uint64, proc Context) error - UnTrack(id uint64) - GetSysProcessList() map[uint64]*util.ProcessInfo - KillSysProcess(id uint64) -} diff --git a/sessionctx/main_test.go b/sessionctx/main_test.go deleted file mode 100644 index 034773325c656..0000000000000 --- a/sessionctx/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package sessionctx - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/sessionctx/sessionstates/BUILD.bazel b/sessionctx/sessionstates/BUILD.bazel deleted file mode 100644 index 901bb54c94dff..0000000000000 --- a/sessionctx/sessionstates/BUILD.bazel +++ /dev/null @@ -1,51 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sessionstates", - srcs = [ - "session_states.go", - "session_token.go", - ], - importpath = "github.com/pingcap/tidb/sessionctx/sessionstates", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//parser/model", - "//parser/types", - "//sessionctx/stmtctx", - "//types", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "sessionstates_test", - timeout = "short", - srcs = [ - "session_states_test.go", - "session_token_test.go", - ], - embed = [":sessionstates"], - flaky = True, - shard_count = 15, - deps = [ - "//config", - "//errno", - "//expression", - "//parser/auth", - "//parser/mysql", - "//parser/terror", - "//server", - "//sessionctx/variable", - "//testkit", - "//util", - "//util/sem", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - ], -) diff --git a/sessionctx/stmtctx/BUILD.bazel b/sessionctx/stmtctx/BUILD.bazel deleted file mode 100644 index e718ddc260834..0000000000000 --- a/sessionctx/stmtctx/BUILD.bazel +++ /dev/null @@ -1,58 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "stmtctx", - srcs = ["stmtctx.go"], - importpath = "github.com/pingcap/tidb/sessionctx/stmtctx", - visibility = ["//visibility:public"], - deps = [ - "//domain/resourcegroup", - "//errno", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//types/context", - "//util/disk", - "//util/execdetails", - "//util/intest", - "//util/memory", - "//util/nocopy", - "//util/resourcegrouptag", - "//util/topsql/stmtstats", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//util", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "stmtctx_test", - timeout = "short", - srcs = [ - "main_test.go", - "stmtctx_test.go", - ], - embed = [":stmtctx"], - flaky = True, - shard_count = 10, - deps = [ - "//kv", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "//types", - "//types/context", - "//util/execdetails", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessionctx/stmtctx/main_test.go b/sessionctx/stmtctx/main_test.go deleted file mode 100644 index e5442883b97d3..0000000000000 --- a/sessionctx/stmtctx/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package stmtctx - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/sessionctx/variable/BUILD.bazel b/sessionctx/variable/BUILD.bazel deleted file mode 100644 index 680cb87a43f72..0000000000000 --- a/sessionctx/variable/BUILD.bazel +++ /dev/null @@ -1,125 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "variable", - srcs = [ - "error.go", - "mock_globalaccessor.go", - "noop.go", - "removed.go", - "sequence_state.go", - "session.go", - "setvar_affect.go", - "statusvar.go", - "sysvar.go", - "tidb_vars.go", - "variable.go", - "varsutil.go", - ], - importpath = "github.com/pingcap/tidb/sessionctx/variable", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//domain/resourcegroup", - "//errno", - "//keyspace", - "//kv", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//planner/util/fixcontrol", - "//privilege/privileges/ldap", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//sessionctx/variable/featuretag/disttask", - "//tidb-binlog/pump_client", - "//types", - "//types/parser_driver", - "//util", - "//util/chunk", - "//util/collate", - "//util/dbterror", - "//util/disk", - "//util/distrole", - "//util/execdetails", - "//util/gctuner", - "//util/kvcache", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/paging", - "//util/replayer", - "//util/rowcodec", - "//util/size", - "//util/stmtsummary/v2:stmtsummary", - "//util/stringutil", - "//util/tableutil", - "//util/tiflash", - "//util/tiflashcompute", - "//util/tikvutil", - "//util/timeutil", - "//util/tls", - "//util/topsql/state", - "//util/versioninfo", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//util", - "@com_github_twmb_murmur3//:murmur3", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "variable_test", - timeout = "short", - srcs = [ - "main_test.go", - "mock_globalaccessor_test.go", - "removed_test.go", - "session_test.go", - "statusvar_test.go", - "sysvar_test.go", - "variable_test.go", - "varsutil_test.go", - ], - embed = [":variable"], - flaky = True, - shard_count = 50, - deps = [ - "//config", - "//kv", - "//parser", - "//parser/auth", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//sessionctx/sessionstates", - "//sessionctx/stmtctx", - "//testkit", - "//testkit/testsetup", - "//types", - "//util", - "//util/chunk", - "//util/execdetails", - "//util/gctuner", - "//util/memory", - "//util/mock", - "//util/timeutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessionctx/variable/error.go b/sessionctx/variable/error.go deleted file mode 100644 index 8544132543ae9..0000000000000 --- a/sessionctx/variable/error.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package variable - -import ( - mysql "github.com/pingcap/tidb/errno" - pmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbterror" -) - -// Error instances. -var ( - errWarnDeprecatedSyntax = dbterror.ClassVariable.NewStd(mysql.ErrWarnDeprecatedSyntax) - ErrSnapshotTooOld = dbterror.ClassVariable.NewStd(mysql.ErrSnapshotTooOld) - ErrUnsupportedValueForVar = dbterror.ClassVariable.NewStd(mysql.ErrUnsupportedValueForVar) - ErrUnknownSystemVar = dbterror.ClassVariable.NewStd(mysql.ErrUnknownSystemVariable) - ErrIncorrectScope = dbterror.ClassVariable.NewStd(mysql.ErrIncorrectGlobalLocalVar) - ErrUnknownTimeZone = dbterror.ClassVariable.NewStd(mysql.ErrUnknownTimeZone) - ErrReadOnly = dbterror.ClassVariable.NewStd(mysql.ErrVariableIsReadonly) - ErrWrongValueForVar = dbterror.ClassVariable.NewStd(mysql.ErrWrongValueForVar) - ErrWrongTypeForVar = dbterror.ClassVariable.NewStd(mysql.ErrWrongTypeForVar) - ErrTruncatedWrongValue = dbterror.ClassVariable.NewStd(mysql.ErrTruncatedWrongValue) - ErrMaxPreparedStmtCountReached = dbterror.ClassVariable.NewStd(mysql.ErrMaxPreparedStmtCountReached) - ErrUnsupportedIsolationLevel = dbterror.ClassVariable.NewStd(mysql.ErrUnsupportedIsolationLevel) - errUnknownSystemVariable = dbterror.ClassVariable.NewStd(mysql.ErrUnknownSystemVariable) - errGlobalVariable = dbterror.ClassVariable.NewStd(mysql.ErrGlobalVariable) - errLocalVariable = dbterror.ClassVariable.NewStd(mysql.ErrLocalVariable) - errValueNotSupportedWhen = dbterror.ClassVariable.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("%s = OFF is not supported when %s = ON", nil)) - ErrStmtNotFound = dbterror.ClassOptimizer.NewStd(mysql.ErrPreparedStmtNotFound) - ErrNotValidPassword = dbterror.ClassExecutor.NewStd(mysql.ErrNotValidPassword) - // ErrFunctionsNoopImpl is an error to say the behavior is protected by the tidb_enable_noop_functions sysvar. - // This is copied from expression.ErrFunctionsNoopImpl to prevent circular dependencies. - // It needs to be public for tests. - ErrFunctionsNoopImpl = dbterror.ClassVariable.NewStdErr(mysql.ErrNotSupportedYet, pmysql.Message("function %s has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", nil)) - ErrVariableNoLongerSupported = dbterror.ClassVariable.NewStd(mysql.ErrVariableNoLongerSupported) - ErrInvalidDefaultUTF8MB4Collation = dbterror.ClassVariable.NewStd(mysql.ErrInvalidDefaultUTF8MB4Collation) - ErrWarnDeprecatedSyntaxNoReplacement = dbterror.ClassVariable.NewStdErr(mysql.ErrWarnDeprecatedSyntaxNoReplacement, pmysql.Message("Updating '%s' is deprecated. It will be made read-only in a future release.", nil)) - ErrWarnDeprecatedSyntaxSimpleMsg = dbterror.ClassVariable.NewStdErr(mysql.ErrWarnDeprecatedSyntaxNoReplacement, pmysql.Message("%s is deprecated and will be removed in a future release.", nil)) -) diff --git a/sessionctx/variable/featuretag/disttask/BUILD.bazel b/sessionctx/variable/featuretag/disttask/BUILD.bazel deleted file mode 100644 index 2916b2548398b..0000000000000 --- a/sessionctx/variable/featuretag/disttask/BUILD.bazel +++ /dev/null @@ -1,11 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "disttask", - srcs = [ - "default.go", - "non_default.go", #keep - ], - importpath = "github.com/pingcap/tidb/sessionctx/variable/featuretag/disttask", - visibility = ["//visibility:public"], -) diff --git a/sessionctx/variable/main_test.go b/sessionctx/variable/main_test.go deleted file mode 100644 index f382b6d861395..0000000000000 --- a/sessionctx/variable/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/sessionctx/variable/session.go b/sessionctx/variable/session.go deleted file mode 100644 index 43f7d1787fc3a..0000000000000 --- a/sessionctx/variable/session.go +++ /dev/null @@ -1,3659 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "bytes" - "context" - "crypto/tls" - "encoding/binary" - "encoding/json" - "fmt" - "math" - "math/rand" - "net" - "slices" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - ptypes "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - pumpcli "github.com/pingcap/tidb/tidb-binlog/pump_client" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/replayer" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tableutil" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/tiflashcompute" - "github.com/pingcap/tidb/util/timeutil" - tikvstore "github.com/tikv/client-go/v2/kv" - "github.com/tikv/client-go/v2/tikv" - "github.com/twmb/murmur3" - atomic2 "go.uber.org/atomic" - "golang.org/x/exp/maps" -) - -var ( - // PreparedStmtCount is exported for test. - PreparedStmtCount int64 - // enableAdaptiveReplicaRead indicates whether closest adaptive replica read - // can be enabled. We forces disable replica read when tidb server in missing - // in regions that contains tikv server to avoid read traffic skew. - enableAdaptiveReplicaRead uint32 = 1 -) - -// ConnStatusShutdown indicates that the connection status is closed by server. -// This code is put here because of package imports, and this value is the original server.connStatusShutdown. -const ConnStatusShutdown int32 = 2 - -// SetEnableAdaptiveReplicaRead set `enableAdaptiveReplicaRead` with given value. -// return true if the value is changed. -func SetEnableAdaptiveReplicaRead(enabled bool) bool { - value := uint32(0) - if enabled { - value = 1 - } - return atomic.SwapUint32(&enableAdaptiveReplicaRead, value) != value -} - -// IsAdaptiveReplicaReadEnabled returns whether adaptive closest replica read can be enabled. -func IsAdaptiveReplicaReadEnabled() bool { - return atomic.LoadUint32(&enableAdaptiveReplicaRead) > 0 -} - -// RetryInfo saves retry information. -type RetryInfo struct { - Retrying bool - DroppedPreparedStmtIDs []uint32 - autoIncrementIDs retryInfoAutoIDs - autoRandomIDs retryInfoAutoIDs - LastRcReadTS uint64 -} - -// ReuseChunkPool save Alloc object -type ReuseChunkPool struct { - mu sync.Mutex - Alloc chunk.Allocator -} - -// Clean does some clean work. -func (r *RetryInfo) Clean() { - r.autoIncrementIDs.clean() - r.autoRandomIDs.clean() - - if len(r.DroppedPreparedStmtIDs) > 0 { - r.DroppedPreparedStmtIDs = r.DroppedPreparedStmtIDs[:0] - } -} - -// ResetOffset resets the current retry offset. -func (r *RetryInfo) ResetOffset() { - r.autoIncrementIDs.resetOffset() - r.autoRandomIDs.resetOffset() -} - -// AddAutoIncrementID adds id to autoIncrementIDs. -func (r *RetryInfo) AddAutoIncrementID(id int64) { - r.autoIncrementIDs.autoIDs = append(r.autoIncrementIDs.autoIDs, id) -} - -// GetCurrAutoIncrementID gets current autoIncrementID. -func (r *RetryInfo) GetCurrAutoIncrementID() (int64, bool) { - return r.autoIncrementIDs.getCurrent() -} - -// AddAutoRandomID adds id to autoRandomIDs. -func (r *RetryInfo) AddAutoRandomID(id int64) { - r.autoRandomIDs.autoIDs = append(r.autoRandomIDs.autoIDs, id) -} - -// GetCurrAutoRandomID gets current AutoRandomID. -func (r *RetryInfo) GetCurrAutoRandomID() (int64, bool) { - return r.autoRandomIDs.getCurrent() -} - -type retryInfoAutoIDs struct { - currentOffset int - autoIDs []int64 -} - -func (r *retryInfoAutoIDs) resetOffset() { - r.currentOffset = 0 -} - -func (r *retryInfoAutoIDs) clean() { - r.currentOffset = 0 - if len(r.autoIDs) > 0 { - r.autoIDs = r.autoIDs[:0] - } -} - -func (r *retryInfoAutoIDs) getCurrent() (int64, bool) { - if r.currentOffset >= len(r.autoIDs) { - return 0, false - } - id := r.autoIDs[r.currentOffset] - r.currentOffset++ - return id, true -} - -// TransactionContext is used to store variables that has transaction scope. -type TransactionContext struct { - TxnCtxNoNeedToRestore - TxnCtxNeedToRestore -} - -// TxnCtxNeedToRestore stores transaction variables which need to be restored when rolling back to a savepoint. -type TxnCtxNeedToRestore struct { - // TableDeltaMap is used in the schema validator for DDL changes in one table not to block others. - // It's also used in the statistics updating. - // Note: for the partitioned table, it stores all the partition IDs. - TableDeltaMap map[int64]TableDelta - - // pessimisticLockCache is the cache for pessimistic locked keys, - // The value never changes during the transaction. - pessimisticLockCache map[string][]byte - - // CachedTables is not nil if the transaction write on cached table. - CachedTables map[int64]interface{} - - // InsertTTLRowsCount counts how many rows are inserted in this statement - InsertTTLRowsCount int -} - -// TxnCtxNoNeedToRestore stores transaction variables which do not need to restored when rolling back to a savepoint. -type TxnCtxNoNeedToRestore struct { - forUpdateTS uint64 - Binlog interface{} - InfoSchema interface{} - History interface{} - StartTS uint64 - - // ShardStep indicates the max size of continuous rowid shard in one transaction. - ShardStep int - shardRemain int - currentShard int64 - - // unchangedKeys is used to store the unchanged keys that needs to lock for pessimistic transaction. - unchangedKeys map[string]struct{} - - PessimisticCacheHit int - - // CreateTime For metrics. - CreateTime time.Time - StatementCount int - CouldRetry bool - IsPessimistic bool - // IsStaleness indicates whether the txn is read only staleness txn. - IsStaleness bool - // IsExplicit indicates whether the txn is an interactive txn, which is typically started with a BEGIN - // or START TRANSACTION statement, or by setting autocommit to 0. - IsExplicit bool - Isolation string - LockExpire uint32 - ForUpdate uint32 - // TxnScope indicates the value of txn_scope - TxnScope string - - // Savepoints contains all definitions of the savepoint of a transaction at runtime, the order of the SavepointRecord is the same with the SAVEPOINT statements. - // It is used for a lookup when running `ROLLBACK TO` statement. - Savepoints []SavepointRecord - - // TableDeltaMap lock to prevent potential data race - tdmLock sync.Mutex - - // TemporaryTables is used to store transaction-specific information for global temporary tables. - // It can also be stored in sessionCtx with local temporary tables, but it's easier to clean this data after transaction ends. - TemporaryTables map[int64]tableutil.TempTable - // EnableMDL indicates whether to enable the MDL lock for the transaction. - EnableMDL bool - // relatedTableForMDL records the `lock` table for metadata lock. It maps from int64 to int64(version). - relatedTableForMDL *sync.Map - - // FairLockingUsed marking whether at least one of the statements in the transaction was executed in - // fair locking mode. - FairLockingUsed bool - // FairLockingEffective marking whether at least one of the statements in the transaction was executed in - // fair locking mode, and it takes effect (which is determined according to whether lock-with-conflict - // has occurred during execution of any statement). - FairLockingEffective bool - - // CurrentStmtPessimisticLockCache is the cache for pessimistic locked keys in the current statement. - // It is merged into `pessimisticLockCache` after a statement finishes. - // Read results cannot be directly written into pessimisticLockCache because failed statement need to rollback - // its pessimistic locks. - CurrentStmtPessimisticLockCache map[string][]byte -} - -// SavepointRecord indicates a transaction's savepoint record. -type SavepointRecord struct { - // name is the name of the savepoint - Name string - // MemDBCheckpoint is the transaction's memdb checkpoint. - MemDBCheckpoint *tikv.MemDBCheckpoint - // TxnCtxSavepoint is the savepoint of TransactionContext - TxnCtxSavepoint TxnCtxNeedToRestore -} - -// GetCurrentShard returns the shard for the next `count` IDs. -func (s *SessionVars) GetCurrentShard(count int) int64 { - tc := s.TxnCtx - if s.shardRand == nil { - s.shardRand = rand.New(rand.NewSource(int64(tc.StartTS))) // #nosec G404 - } - if tc.shardRemain <= 0 { - tc.updateShard(s.shardRand) - tc.shardRemain = tc.ShardStep - } - tc.shardRemain -= count - return tc.currentShard -} - -func (tc *TransactionContext) updateShard(shardRand *rand.Rand) { - var buf [8]byte - binary.LittleEndian.PutUint64(buf[:], shardRand.Uint64()) - tc.currentShard = int64(murmur3.Sum32(buf[:])) -} - -// AddUnchangedKeyForLock adds an unchanged key for pessimistic lock. -func (tc *TransactionContext) AddUnchangedKeyForLock(key []byte) { - if tc.unchangedKeys == nil { - tc.unchangedKeys = map[string]struct{}{} - } - tc.unchangedKeys[string(key)] = struct{}{} -} - -// CollectUnchangedKeysForLock collects unchanged keys for pessimistic lock. -func (tc *TransactionContext) CollectUnchangedKeysForLock(buf []kv.Key) []kv.Key { - for key := range tc.unchangedKeys { - buf = append(buf, kv.Key(key)) - } - tc.unchangedKeys = nil - return buf -} - -// UpdateDeltaForTable updates the delta info for some table. -func (tc *TransactionContext) UpdateDeltaForTable(physicalTableID int64, delta int64, count int64, colSize map[int64]int64) { - tc.tdmLock.Lock() - defer tc.tdmLock.Unlock() - if tc.TableDeltaMap == nil { - tc.TableDeltaMap = make(map[int64]TableDelta) - } - item := tc.TableDeltaMap[physicalTableID] - if item.ColSize == nil && colSize != nil { - item.ColSize = make(map[int64]int64, len(colSize)) - } - item.Delta += delta - item.Count += count - item.TableID = physicalTableID - for key, val := range colSize { - item.ColSize[key] += val - } - tc.TableDeltaMap[physicalTableID] = item -} - -// GetKeyInPessimisticLockCache gets a key in pessimistic lock cache. -func (tc *TransactionContext) GetKeyInPessimisticLockCache(key kv.Key) (val []byte, ok bool) { - if tc.pessimisticLockCache == nil && tc.CurrentStmtPessimisticLockCache == nil { - return nil, false - } - if tc.CurrentStmtPessimisticLockCache != nil { - val, ok = tc.CurrentStmtPessimisticLockCache[string(key)] - if ok { - tc.PessimisticCacheHit++ - return - } - } - if tc.pessimisticLockCache != nil { - val, ok = tc.pessimisticLockCache[string(key)] - if ok { - tc.PessimisticCacheHit++ - } - } - return -} - -// SetPessimisticLockCache sets a key value pair in pessimistic lock cache. -// The value is buffered in the statement cache until the current statement finishes. -func (tc *TransactionContext) SetPessimisticLockCache(key kv.Key, val []byte) { - if tc.CurrentStmtPessimisticLockCache == nil { - tc.CurrentStmtPessimisticLockCache = make(map[string][]byte) - } - tc.CurrentStmtPessimisticLockCache[string(key)] = val -} - -// Cleanup clears up transaction info that no longer use. -func (tc *TransactionContext) Cleanup() { - // tc.InfoSchema = nil; we cannot do it now, because some operation like handleFieldList depend on this. - tc.Binlog = nil - tc.History = nil - tc.tdmLock.Lock() - tc.TableDeltaMap = nil - tc.relatedTableForMDL = nil - tc.tdmLock.Unlock() - tc.pessimisticLockCache = nil - tc.CurrentStmtPessimisticLockCache = nil - tc.IsStaleness = false - tc.Savepoints = nil - tc.EnableMDL = false -} - -// ClearDelta clears the delta map. -func (tc *TransactionContext) ClearDelta() { - tc.tdmLock.Lock() - tc.TableDeltaMap = nil - tc.tdmLock.Unlock() -} - -// GetForUpdateTS returns the ts for update. -func (tc *TransactionContext) GetForUpdateTS() uint64 { - if tc.forUpdateTS > tc.StartTS { - return tc.forUpdateTS - } - return tc.StartTS -} - -// SetForUpdateTS sets the ts for update. -func (tc *TransactionContext) SetForUpdateTS(forUpdateTS uint64) { - if forUpdateTS > tc.forUpdateTS { - tc.forUpdateTS = forUpdateTS - } -} - -// GetCurrentSavepoint gets TransactionContext's savepoint. -func (tc *TransactionContext) GetCurrentSavepoint() TxnCtxNeedToRestore { - tableDeltaMap := make(map[int64]TableDelta, len(tc.TableDeltaMap)) - for k, v := range tc.TableDeltaMap { - tableDeltaMap[k] = v.Clone() - } - pessimisticLockCache := make(map[string][]byte, len(tc.pessimisticLockCache)) - maps.Copy(pessimisticLockCache, tc.pessimisticLockCache) - CurrentStmtPessimisticLockCache := make(map[string][]byte, len(tc.CurrentStmtPessimisticLockCache)) - maps.Copy(CurrentStmtPessimisticLockCache, tc.CurrentStmtPessimisticLockCache) - cachedTables := make(map[int64]interface{}, len(tc.CachedTables)) - maps.Copy(cachedTables, tc.CachedTables) - return TxnCtxNeedToRestore{ - TableDeltaMap: tableDeltaMap, - pessimisticLockCache: pessimisticLockCache, - CachedTables: cachedTables, - InsertTTLRowsCount: tc.InsertTTLRowsCount, - } -} - -// RestoreBySavepoint restores TransactionContext to the specify savepoint. -func (tc *TransactionContext) RestoreBySavepoint(savepoint TxnCtxNeedToRestore) { - tc.TableDeltaMap = savepoint.TableDeltaMap - tc.pessimisticLockCache = savepoint.pessimisticLockCache - tc.CachedTables = savepoint.CachedTables - tc.InsertTTLRowsCount = savepoint.InsertTTLRowsCount -} - -// AddSavepoint adds a new savepoint. -func (tc *TransactionContext) AddSavepoint(name string, memdbCheckpoint *tikv.MemDBCheckpoint) { - name = strings.ToLower(name) - tc.DeleteSavepoint(name) - - record := SavepointRecord{ - Name: name, - MemDBCheckpoint: memdbCheckpoint, - TxnCtxSavepoint: tc.GetCurrentSavepoint(), - } - tc.Savepoints = append(tc.Savepoints, record) -} - -// DeleteSavepoint deletes the savepoint, return false indicate the savepoint name doesn't exists. -func (tc *TransactionContext) DeleteSavepoint(name string) bool { - name = strings.ToLower(name) - for i, sp := range tc.Savepoints { - if sp.Name == name { - tc.Savepoints = append(tc.Savepoints[:i], tc.Savepoints[i+1:]...) - return true - } - } - return false -} - -// ReleaseSavepoint deletes the named savepoint and the later savepoints, return false indicate the named savepoint doesn't exists. -func (tc *TransactionContext) ReleaseSavepoint(name string) bool { - name = strings.ToLower(name) - for i, sp := range tc.Savepoints { - if sp.Name == name { - tc.Savepoints = append(tc.Savepoints[:i]) - return true - } - } - return false -} - -// RollbackToSavepoint rollbacks to the specified savepoint by name. -func (tc *TransactionContext) RollbackToSavepoint(name string) *SavepointRecord { - name = strings.ToLower(name) - for idx, sp := range tc.Savepoints { - if name == sp.Name { - tc.RestoreBySavepoint(sp.TxnCtxSavepoint) - tc.Savepoints = tc.Savepoints[:idx+1] - return &tc.Savepoints[idx] - } - } - return nil -} - -// FlushStmtPessimisticLockCache merges the current statement pessimistic lock cache into transaction pessimistic lock -// cache. The caller may need to clear the stmt cache itself. -func (tc *TransactionContext) FlushStmtPessimisticLockCache() { - if tc.CurrentStmtPessimisticLockCache == nil { - return - } - if tc.pessimisticLockCache == nil { - tc.pessimisticLockCache = make(map[string][]byte) - } - for key, val := range tc.CurrentStmtPessimisticLockCache { - tc.pessimisticLockCache[key] = val - } - tc.CurrentStmtPessimisticLockCache = nil -} - -// WriteStmtBufs can be used by insert/replace/delete/update statement. -// TODO: use a common memory pool to replace this. -type WriteStmtBufs struct { - // RowValBuf is used by tablecodec.EncodeRow, to reduce runtime.growslice. - RowValBuf []byte - // AddRowValues use to store temp insert rows value, to reduce memory allocations when importing data. - AddRowValues []types.Datum - - // IndexValsBuf is used by index.FetchValues - IndexValsBuf []types.Datum - // IndexKeyBuf is used by index.GenIndexKey - IndexKeyBuf []byte -} - -func (ib *WriteStmtBufs) clean() { - ib.RowValBuf = nil - ib.AddRowValues = nil - ib.IndexValsBuf = nil - ib.IndexKeyBuf = nil -} - -// TableSnapshot represents a data snapshot of the table contained in `information_schema`. -type TableSnapshot struct { - Rows [][]types.Datum - Err error -} - -type txnIsolationLevelOneShotState uint - -// RewritePhaseInfo records some information about the rewrite phase -type RewritePhaseInfo struct { - // DurationRewrite is the duration of rewriting the SQL. - DurationRewrite time.Duration - - // DurationPreprocessSubQuery is the duration of pre-processing sub-queries. - DurationPreprocessSubQuery time.Duration - - // PreprocessSubQueries is the number of pre-processed sub-queries. - PreprocessSubQueries int -} - -// Reset resets all fields in RewritePhaseInfo. -func (r *RewritePhaseInfo) Reset() { - r.DurationRewrite = 0 - r.DurationPreprocessSubQuery = 0 - r.PreprocessSubQueries = 0 -} - -// TemporaryTableData is a interface to maintain temporary data in session -type TemporaryTableData interface { - kv.Retriever - // Staging create a new staging buffer inside the MemBuffer. - // Subsequent writes will be temporarily stored in this new staging buffer. - // When you think all modifications looks good, you can call `Release` to public all of them to the upper level buffer. - Staging() kv.StagingHandle - // Release publish all modifications in the latest staging buffer to upper level. - Release(kv.StagingHandle) - // Cleanup cleanups the resources referenced by the StagingHandle. - // If the changes are not published by `Release`, they will be discarded. - Cleanup(kv.StagingHandle) - // GetTableSize get the size of a table - GetTableSize(tblID int64) int64 - // DeleteTableKey removes the entry for key k from table - DeleteTableKey(tblID int64, k kv.Key) error - // SetTableKey sets the entry for k from table - SetTableKey(tblID int64, k kv.Key, val []byte) error -} - -// temporaryTableData is used for store temporary table data in session -type temporaryTableData struct { - kv.MemBuffer - tblSize map[int64]int64 -} - -// NewTemporaryTableData creates a new TemporaryTableData -func NewTemporaryTableData(memBuffer kv.MemBuffer) TemporaryTableData { - return &temporaryTableData{ - MemBuffer: memBuffer, - tblSize: make(map[int64]int64), - } -} - -// GetTableSize get the size of a table -func (d *temporaryTableData) GetTableSize(tblID int64) int64 { - if tblSize, ok := d.tblSize[tblID]; ok { - return tblSize - } - return 0 -} - -// DeleteTableKey removes the entry for key k from table -func (d *temporaryTableData) DeleteTableKey(tblID int64, k kv.Key) error { - bufferSize := d.MemBuffer.Size() - defer d.updateTblSize(tblID, bufferSize) - - return d.MemBuffer.Delete(k) -} - -// SetTableKey sets the entry for k from table -func (d *temporaryTableData) SetTableKey(tblID int64, k kv.Key, val []byte) error { - bufferSize := d.MemBuffer.Size() - defer d.updateTblSize(tblID, bufferSize) - - return d.MemBuffer.Set(k, val) -} - -func (d *temporaryTableData) updateTblSize(tblID int64, beforeSize int) { - delta := int64(d.MemBuffer.Size() - beforeSize) - d.tblSize[tblID] = d.GetTableSize(tblID) + delta -} - -const ( - // oneShotDef means default, that is tx_isolation_one_shot not set. - oneShotDef txnIsolationLevelOneShotState = iota - // oneShotSet means it's set in current transaction. - oneShotSet - // onsShotUse means it should be used in current transaction. - oneShotUse -) - -// ReadConsistencyLevel is the level of read consistency. -type ReadConsistencyLevel string - -const ( - // ReadConsistencyStrict means read by strict consistency, default value. - ReadConsistencyStrict ReadConsistencyLevel = "strict" - // ReadConsistencyWeak means read can be weak consistency. - ReadConsistencyWeak ReadConsistencyLevel = "weak" -) - -// IsWeak returns true only if it's a weak-consistency read. -func (r ReadConsistencyLevel) IsWeak() bool { - return r == ReadConsistencyWeak -} - -func validateReadConsistencyLevel(val string) error { - switch v := ReadConsistencyLevel(strings.ToLower(val)); v { - case ReadConsistencyStrict, ReadConsistencyWeak: - return nil - default: - return ErrWrongTypeForVar.GenWithStackByArgs(TiDBReadConsistency) - } -} - -// SetUserVarVal set user defined variables' value -func (s *SessionVars) SetUserVarVal(name string, dt types.Datum) { - s.userVars.lock.Lock() - defer s.userVars.lock.Unlock() - s.userVars.values[name] = dt -} - -// GetUserVarVal get user defined variables' value -func (s *SessionVars) GetUserVarVal(name string) (types.Datum, bool) { - s.userVars.lock.RLock() - defer s.userVars.lock.RUnlock() - dt, ok := s.userVars.values[name] - return dt, ok -} - -// SetUserVarType set user defined variables' type -func (s *SessionVars) SetUserVarType(name string, ft *types.FieldType) { - s.userVars.lock.Lock() - defer s.userVars.lock.Unlock() - s.userVars.types[name] = ft -} - -// GetUserVarType get user defined variables' type -func (s *SessionVars) GetUserVarType(name string) (*types.FieldType, bool) { - s.userVars.lock.RLock() - defer s.userVars.lock.RUnlock() - ft, ok := s.userVars.types[name] - return ft, ok -} - -// HookContext contains the necessary variables for executing set/get hook -type HookContext interface { - GetStore() kv.Storage -} - -// SessionVars is to handle user-defined or global variables in the current session. -type SessionVars struct { - Concurrency - MemQuota - BatchSize - // DMLBatchSize indicates the number of rows batch-committed for a statement. - // It will be used when using LOAD DATA or BatchInsert or BatchDelete is on. - DMLBatchSize int - RetryLimit int64 - DisableTxnAutoRetry bool - userVars struct { - // lock is for user defined variables. values and types is read/write protected. - lock sync.RWMutex - // values stores the Datum for user variables - values map[string]types.Datum - // types stores the FieldType for user variables, it cannot be inferred from values when values have not been set yet. - types map[string]*types.FieldType - } - // systems variables, don't modify it directly, use GetSystemVar/SetSystemVar method. - systems map[string]string - // stmtVars variables are temporarily set by SET_VAR hint - // It only take effect for the duration of a single statement - stmtVars map[string]string - // SysWarningCount is the system variable "warning_count", because it is on the hot path, so we extract it from the systems - SysWarningCount int - // SysErrorCount is the system variable "error_count", because it is on the hot path, so we extract it from the systems - SysErrorCount uint16 - // nonPreparedPlanCacheStmts stores PlanCacheStmts for non-prepared plan cache. - nonPreparedPlanCacheStmts *kvcache.SimpleLRUCache - // PreparedStmts stores prepared statement. - PreparedStmts map[uint32]interface{} - PreparedStmtNameToID map[string]uint32 - // preparedStmtID is id of prepared statement. - preparedStmtID uint32 - // Parameter values for plan cache. - PlanCacheParams *PlanCacheParamList - LastUpdateTime4PC types.Time - - // ActiveRoles stores active roles for current user - ActiveRoles []*auth.RoleIdentity - - RetryInfo *RetryInfo - // TxnCtx Should be reset on transaction finished. - TxnCtx *TransactionContext - // TxnCtxMu is used to protect TxnCtx. - TxnCtxMu sync.Mutex - - // TxnManager is used to manage txn context in session - TxnManager interface{} - - // KVVars is the variables for KV storage. - KVVars *tikvstore.Variables - - // txnIsolationLevelOneShot is used to implements "set transaction isolation level ..." - txnIsolationLevelOneShot struct { - state txnIsolationLevelOneShotState - value string - } - - // Status stands for the session status. e.g. in transaction or not, auto commit is on or off, and so on. - Status uint16 - - // ClientCapability is client's capability. - ClientCapability uint32 - - // TLSConnectionState is the TLS connection state (nil if not using TLS). - TLSConnectionState *tls.ConnectionState - - // ConnectionID is the connection id of the current session. - ConnectionID uint64 - - // PlanID is the unique id of logical and physical plan. - PlanID atomic.Int32 - - // PlanColumnID is the unique id for column when building plan. - PlanColumnID atomic.Int64 - - // MapScalarSubQ maps the scalar sub queries from its ID to its struct. - MapScalarSubQ []interface{} - - // MapHashCode2UniqueID4ExtendedCol map the expr's hash code to specified unique ID. - MapHashCode2UniqueID4ExtendedCol map[string]int - - // User is the user identity with which the session login. - User *auth.UserIdentity - - // Port is the port of the connected socket - Port string - - // CurrentDB is the default database of this session. - CurrentDB string - - // CurrentDBChanged indicates if the CurrentDB has been updated, and if it is we should print it into - // the slow log to make it be compatible with MySQL, https://github.com/pingcap/tidb/issues/17846. - CurrentDBChanged bool - - // StrictSQLMode indicates if the session is in strict mode. - StrictSQLMode bool - - // CommonGlobalLoaded indicates if common global variable has been loaded for this session. - CommonGlobalLoaded bool - - // InRestrictedSQL indicates if the session is handling restricted SQL execution. - InRestrictedSQL bool - - // SnapshotTS is used for reading history data. For simplicity, SnapshotTS only supports distsql request. - SnapshotTS uint64 - - // TxnReadTS is used for staleness transaction, it provides next staleness transaction startTS. - TxnReadTS *TxnReadTS - - // SnapshotInfoschema is used with SnapshotTS, when the schema version at snapshotTS less than current schema - // version, we load an old version schema for query. - SnapshotInfoschema interface{} - - // BinlogClient is used to write binlog. - BinlogClient *pumpcli.PumpsClient - - // GlobalVarsAccessor is used to set and get global variables. - GlobalVarsAccessor GlobalVarAccessor - - // LastFoundRows is the number of found rows of last query statement - LastFoundRows uint64 - - // StmtCtx holds variables for current executing statement. - StmtCtx *stmtctx.StatementContext - - // RefCountOfStmtCtx indicates the reference count of StmtCtx. When the - // StmtCtx is accessed by other sessions, e.g. oom-alarm-handler/expensive-query-handler, add one first. - // Note: this variable should be accessed and updated by atomic operations. - RefCountOfStmtCtx stmtctx.ReferenceCount - - // AllowAggPushDown can be set to false to forbid aggregation push down. - AllowAggPushDown bool - - // AllowDeriveTopN is used to enable/disable derived TopN optimization. - AllowDeriveTopN bool - - // AllowCartesianBCJ means allow broadcast CARTESIAN join, 0 means not allow, 1 means allow broadcast CARTESIAN join - // but the table size should under the broadcast threshold, 2 means allow broadcast CARTESIAN join even if the table - // size exceeds the broadcast threshold - AllowCartesianBCJ int - - // MPPOuterJoinFixedBuildSide means in MPP plan, always use right(left) table as build side for left(right) out join - MPPOuterJoinFixedBuildSide bool - - // AllowDistinctAggPushDown can be set true to allow agg with distinct push down to tikv/tiflash. - AllowDistinctAggPushDown bool - - // EnableSkewDistinctAgg can be set true to allow skew distinct aggregate rewrite - EnableSkewDistinctAgg bool - - // Enable3StageDistinctAgg indicates whether to allow 3 stage distinct aggregate - Enable3StageDistinctAgg bool - - // Enable3StageMultiDistinctAgg indicates whether to allow 3 stage multi distinct aggregate - Enable3StageMultiDistinctAgg bool - - ExplainNonEvaledSubQuery bool - - // MultiStatementMode permits incorrect client library usage. Not recommended to be turned on. - MultiStatementMode int - - // InMultiStmts indicates whether the statement is a multi-statement like `update t set a=1; update t set b=2;`. - InMultiStmts bool - - // AllowWriteRowID variable is currently not recommended to be turned on. - AllowWriteRowID bool - - // AllowBatchCop means if we should send batch coprocessor to TiFlash. Default value is 1, means to use batch cop in case of aggregation and join. - // Value set to 2 means to force to send batch cop for any query. Value set to 0 means never use batch cop. - AllowBatchCop int - - // allowMPPExecution means if we should use mpp way to execute query. - // Default value is `true`, means to be determined by the optimizer. - // Value set to `false` means never use mpp. - allowMPPExecution bool - - // allowTiFlashCop means if we must use mpp way to execute query. - // Default value is `false`, means to be determined by the optimizer. - // Value set to `true` means we may fall back to TiFlash cop if possible. - allowTiFlashCop bool - - // HashExchangeWithNewCollation means if we support hash exchange when new collation is enabled. - // Default value is `true`, means support hash exchange when new collation is enabled. - // Value set to `false` means not use hash exchange when new collation is enabled. - HashExchangeWithNewCollation bool - - // enforceMPPExecution means if we should enforce mpp way to execute query. - // Default value is `false`, means to be determined by variable `allowMPPExecution`. - // Value set to `true` means enforce use mpp. - // Note if you want to set `enforceMPPExecution` to `true`, you must set `allowMPPExecution` to `true` first. - enforceMPPExecution bool - - // TiFlashMaxThreads is the maximum number of threads to execute the request which is pushed down to tiflash. - // Default value is -1, means it will not be pushed down to tiflash. - // If the value is bigger than -1, it will be pushed down to tiflash and used to create db context in tiflash. - TiFlashMaxThreads int64 - - // TiFlashMaxBytesBeforeExternalJoin is the maximum bytes used by a TiFlash join before spill to disk - // Default value is -1, means it will not be pushed down to TiFlash - // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means - // not limit and spill will never happen - TiFlashMaxBytesBeforeExternalJoin int64 - - // TiFlashMaxBytesBeforeExternalGroupBy is the maximum bytes used by a TiFlash hash aggregation before spill to disk - // Default value is -1, means it will not be pushed down to TiFlash - // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means - // not limit and spill will never happen - TiFlashMaxBytesBeforeExternalGroupBy int64 - - // TiFlashMaxBytesBeforeExternalSort is the maximum bytes used by a TiFlash sort/TopN before spill to disk - // Default value is -1, means it will not be pushed down to TiFlash - // If the value is bigger than -1, it will be pushed down to TiFlash, and if the value is 0, it means - // not limit and spill will never happen - TiFlashMaxBytesBeforeExternalSort int64 - - // TiFlash max query memory per node, -1 and 0 means no limit, and the default value is 0 - // If TiFlashMaxQueryMemoryPerNode > 0 && TiFlashQuerySpillRatio > 0, it will trigger auto spill in TiFlash side, and when auto spill - // is triggered, per executor's memory usage threshold set by TiFlashMaxBytesBeforeExternalJoin/TiFlashMaxBytesBeforeExternalGroupBy/TiFlashMaxBytesBeforeExternalSort will be ignored. - TiFlashMaxQueryMemoryPerNode int64 - - // TiFlashQuerySpillRatio is the percentage threshold to trigger auto spill in TiFlash if TiFlashMaxQueryMemoryPerNode is set - TiFlashQuerySpillRatio float64 - - // TiDBAllowAutoRandExplicitInsert indicates whether explicit insertion on auto_random column is allowed. - AllowAutoRandExplicitInsert bool - - // BroadcastJoinThresholdSize is used to limit the size of smaller table. - // It's unit is bytes, if the size of small table is larger than it, we will not use bcj. - BroadcastJoinThresholdSize int64 - - // BroadcastJoinThresholdCount is used to limit the total count of smaller table. - // If we can't estimate the size of one side of join child, we will check if its row number exceeds this limitation. - BroadcastJoinThresholdCount int64 - - // PreferBCJByExchangeDataSize indicates the method used to choose mpp broadcast join - // false: choose mpp broadcast join by `BroadcastJoinThresholdSize` and `BroadcastJoinThresholdCount` - // true: compare data exchange size of join and choose the smallest one - PreferBCJByExchangeDataSize bool - - // LimitPushDownThreshold determines if push Limit or TopN down to TiKV forcibly. - LimitPushDownThreshold int64 - - // CorrelationThreshold is the guard to enable row count estimation using column order correlation. - CorrelationThreshold float64 - - // EnableCorrelationAdjustment is used to indicate if correlation adjustment is enabled. - EnableCorrelationAdjustment bool - - // CorrelationExpFactor is used to control the heuristic approach of row count estimation when CorrelationThreshold is not met. - CorrelationExpFactor int - - // cpuFactor is the CPU cost of processing one expression for one row. - cpuFactor float64 - // copCPUFactor is the CPU cost of processing one expression for one row in coprocessor. - copCPUFactor float64 - // networkFactor is the network cost of transferring 1 byte data. - networkFactor float64 - // ScanFactor is the IO cost of scanning 1 byte data on TiKV and TiFlash. - scanFactor float64 - // descScanFactor is the IO cost of scanning 1 byte data on TiKV and TiFlash in desc order. - descScanFactor float64 - // seekFactor is the IO cost of seeking the start value of a range in TiKV or TiFlash. - seekFactor float64 - // memoryFactor is the memory cost of storing one tuple. - memoryFactor float64 - // diskFactor is the IO cost of reading/writing one byte to temporary disk. - diskFactor float64 - // concurrencyFactor is the CPU cost of additional one goroutine. - concurrencyFactor float64 - - // enableForceInlineCTE is used to enable/disable force inline CTE. - enableForceInlineCTE bool - - // CopTiFlashConcurrencyFactor is the concurrency number of computation in tiflash coprocessor. - CopTiFlashConcurrencyFactor float64 - - // CurrInsertValues is used to record current ValuesExpr's values. - // See http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values - CurrInsertValues chunk.Row - - // In https://github.com/pingcap/tidb/issues/14164, we can see that MySQL can enter the column that is not in the insert's SELECT's output. - // We store the extra columns in this variable. - CurrInsertBatchExtraCols [][]types.Datum - - // Per-connection time zones. Each client that connects has its own time zone setting, given by the session time_zone variable. - // See https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html - TimeZone *time.Location - - SQLMode mysql.SQLMode - - // AutoIncrementIncrement and AutoIncrementOffset indicates the autoID's start value and increment. - AutoIncrementIncrement int - - AutoIncrementOffset int - - /* TiDB system variables */ - - // SkipASCIICheck check on input value. - SkipASCIICheck bool - - // SkipUTF8Check check on input value. - SkipUTF8Check bool - - // DefaultCollationForUTF8MB4 indicates the default collation of UTF8MB4. - DefaultCollationForUTF8MB4 string - - // BatchInsert indicates if we should split insert data into multiple batches. - BatchInsert bool - - // BatchDelete indicates if we should split delete data into multiple batches. - BatchDelete bool - - // BatchCommit indicates if we should split the transaction into multiple batches. - BatchCommit bool - - // IDAllocator is provided by kvEncoder, if it is provided, we will use it to alloc auto id instead of using - // Table.alloc. - IDAllocator autoid.Allocator - - // OptimizerSelectivityLevel defines the level of the selectivity estimation in plan. - OptimizerSelectivityLevel int - - // OptimizerEnableNewOnlyFullGroupByCheck enables the new only_full_group_by check which is implemented by maintaining functional dependency. - OptimizerEnableNewOnlyFullGroupByCheck bool - - // EnableOuterJoinWithJoinReorder enables TiDB to involve the outer join into the join reorder. - EnableOuterJoinReorder bool - - // OptimizerEnableNAAJ enables TiDB to use null-aware anti join. - OptimizerEnableNAAJ bool - - // EnableTablePartition enables table partition feature. - EnableTablePartition string - - // EnableListTablePartition enables list table partition feature. - EnableListTablePartition bool - - // EnableCascadesPlanner enables the cascades planner. - EnableCascadesPlanner bool - - // EnableWindowFunction enables the window function. - EnableWindowFunction bool - - // EnablePipelinedWindowExec enables executing window functions in a pipelined manner. - EnablePipelinedWindowExec bool - - // AllowProjectionPushDown enables pushdown projection on TiKV. - AllowProjectionPushDown bool - - // EnableStrictDoubleTypeCheck enables table field double type check. - EnableStrictDoubleTypeCheck bool - - // EnableVectorizedExpression enables the vectorized expression evaluation. - EnableVectorizedExpression bool - - // DDLReorgPriority is the operation priority of adding indices. - DDLReorgPriority int - - // EnableAutoIncrementInGenerated is used to control whether to allow auto incremented columns in generated columns. - EnableAutoIncrementInGenerated bool - - // EnablePointGetCache is used to cache value for point get for read only scenario. - EnablePointGetCache bool - - // PlacementMode the placement mode we use - // strict: Check placement settings strictly in ddl operations - // ignore: Ignore all placement settings in ddl operations - PlacementMode string - - // WaitSplitRegionFinish defines the split region behaviour is sync or async. - WaitSplitRegionFinish bool - - // WaitSplitRegionTimeout defines the split region timeout. - WaitSplitRegionTimeout uint64 - - // EnableChunkRPC indicates whether the coprocessor request can use chunk API. - EnableChunkRPC bool - - writeStmtBufs WriteStmtBufs - - // ConstraintCheckInPlace indicates whether to check the constraint when the SQL executing. - ConstraintCheckInPlace bool - - // CommandValue indicates which command current session is doing. - CommandValue uint32 - - // TiDBOptJoinReorderThreshold defines the minimal number of join nodes - // to use the greedy join reorder algorithm. - TiDBOptJoinReorderThreshold int - - // SlowQueryFile indicates which slow query log file for SLOW_QUERY table to parse. - SlowQueryFile string - - // EnableFastAnalyze indicates whether to take fast analyze. - EnableFastAnalyze bool - - // TxnMode indicates should be pessimistic or optimistic. - TxnMode string - - // LowResolutionTSO is used for reading data with low resolution TSO which is updated once every two seconds. - LowResolutionTSO bool - - // MaxExecutionTime is the timeout for select statement, in milliseconds. - // If the value is 0, timeouts are not enabled. - // See https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_execution_time - MaxExecutionTime uint64 - - // TiKVClientReadTimeout is the timeout for readonly kv request in milliseconds, 0 means using default value - // See https://github.com/pingcap/tidb/blob/7105505a78fc886c33258caa5813baf197b15247/docs/design/2023-06-30-configurable-kv-timeout.md?plain=1#L14-L15 - TiKVClientReadTimeout uint64 - - // Killed is a flag to indicate that this query is killed. - Killed uint32 - - // ConnectionStatus indicates current connection status. - ConnectionStatus int32 - - // ConnectionInfo indicates current connection info used by current session. - ConnectionInfo *ConnectionInfo - - // NoopFuncsMode allows OFF/ON/WARN values as 0/1/2. - NoopFuncsMode int - - // StartTime is the start time of the last query. - StartTime time.Time - - // DurationParse is the duration of parsing SQL string to AST of the last query. - DurationParse time.Duration - - // DurationCompile is the duration of compiling AST to execution plan of the last query. - DurationCompile time.Duration - - // RewritePhaseInfo records all information about the rewriting phase. - RewritePhaseInfo - - // DurationOptimization is the duration of optimizing a query. - DurationOptimization time.Duration - - // DurationWaitTS is the duration of waiting for a snapshot TS - DurationWaitTS time.Duration - - // PrevStmt is used to store the previous executed statement in the current session. - PrevStmt fmt.Stringer - - // prevStmtDigest is used to store the digest of the previous statement in the current session. - prevStmtDigest string - - // AllowRemoveAutoInc indicates whether a user can drop the auto_increment column attribute or not. - AllowRemoveAutoInc bool - - // UsePlanBaselines indicates whether we will use plan baselines to adjust plan. - UsePlanBaselines bool - - // EvolvePlanBaselines indicates whether we will evolve the plan baselines. - EvolvePlanBaselines bool - - // EnableExtendedStats indicates whether we enable the extended statistics feature. - EnableExtendedStats bool - - // Unexported fields should be accessed and set through interfaces like GetReplicaRead() and SetReplicaRead(). - - // allowInSubqToJoinAndAgg can be set to false to forbid rewriting the semi join to inner join with agg. - allowInSubqToJoinAndAgg bool - - // preferRangeScan allows optimizer to always prefer range scan over table scan. - preferRangeScan bool - - // EnableIndexMerge enables the generation of IndexMergePath. - enableIndexMerge bool - - // replicaRead is used for reading data from replicas, only follower is supported at this time. - replicaRead kv.ReplicaReadType - // ReplicaClosestReadThreshold is the minimum response body size that a cop request should be sent to the closest replica. - // this variable only take effect when `tidb_follower_read` = 'closest-adaptive' - ReplicaClosestReadThreshold int64 - - // IsolationReadEngines is used to isolation read, tidb only read from the stores whose engine type is in the engines. - IsolationReadEngines map[kv.StoreType]struct{} - - mppVersion kv.MppVersion - - mppExchangeCompressionMode kv.ExchangeCompressionMode - - PlannerSelectBlockAsName atomic.Pointer[[]ast.HintTable] - - // LockWaitTimeout is the duration waiting for pessimistic lock in milliseconds - LockWaitTimeout int64 - - // MetricSchemaStep indicates the step when query metric schema. - MetricSchemaStep int64 - - // CDCWriteSource indicates the following data is written by TiCDC if it is not 0. - CDCWriteSource uint64 - - // MetricSchemaRangeDuration indicates the step when query metric schema. - MetricSchemaRangeDuration int64 - - // Some data of cluster-level memory tables will be retrieved many times in different inspection rules, - // and the cost of retrieving some data is expensive. We use the `TableSnapshot` to cache those data - // and obtain them lazily, and provide a consistent view of inspection tables for each inspection rules. - // All cached snapshots will be released at the end of retrieving - InspectionTableCache map[string]TableSnapshot - - // RowEncoder is reused in session for encode row data. - RowEncoder rowcodec.Encoder - - // SequenceState cache all sequence's latest value accessed by lastval() builtins. It's a session scoped - // variable, and all public methods of SequenceState are currently-safe. - SequenceState *SequenceState - - // WindowingUseHighPrecision determines whether to compute window operations without loss of precision. - // see https://dev.mysql.com/doc/refman/8.0/en/window-function-optimization.html for more details. - WindowingUseHighPrecision bool - - // FoundInPlanCache indicates whether this statement was found in plan cache. - FoundInPlanCache bool - // PrevFoundInPlanCache indicates whether the last statement was found in plan cache. - PrevFoundInPlanCache bool - - // FoundInBinding indicates whether the execution plan is matched with the hints in the binding. - FoundInBinding bool - // PrevFoundInBinding indicates whether the last execution plan is matched with the hints in the binding. - PrevFoundInBinding bool - - // OptimizerUseInvisibleIndexes indicates whether optimizer can use invisible index - OptimizerUseInvisibleIndexes bool - - // SelectLimit limits the max counts of select statement's output - SelectLimit uint64 - - // EnableClusteredIndex indicates whether to enable clustered index when creating a new table. - EnableClusteredIndex ClusteredIndexDefMode - - // PresumeKeyNotExists indicates lazy existence checking is enabled. - PresumeKeyNotExists bool - - // EnableParallelApply indicates that thether to use parallel apply. - EnableParallelApply bool - - // EnableRedactLog indicates that whether redact log. - EnableRedactLog bool - - // ShardAllocateStep indicates the max size of continuous rowid shard in one transaction. - ShardAllocateStep int64 - - // LastTxnInfo keeps track the info of last committed transaction. - LastTxnInfo string - - // LastQueryInfo keeps track the info of last query. - LastQueryInfo sessionstates.QueryInfo - - // LastDDLInfo keeps track the info of last DDL. - LastDDLInfo sessionstates.LastDDLInfo - - // PartitionPruneMode indicates how and when to prune partitions. - PartitionPruneMode atomic2.String - - // TxnScope indicates the scope of the transactions. It should be `global` or equal to the value of key `zone` in config.Labels. - TxnScope kv.TxnScopeVar - - // EnabledRateLimitAction indicates whether enabled ratelimit action during coprocessor - EnabledRateLimitAction bool - - // EnableAsyncCommit indicates whether to enable the async commit feature. - EnableAsyncCommit bool - - // Enable1PC indicates whether to enable the one-phase commit feature. - Enable1PC bool - - // GuaranteeLinearizability indicates whether to guarantee linearizability - GuaranteeLinearizability bool - - // AnalyzeVersion indicates how TiDB collect and use analyzed statistics. - AnalyzeVersion int - - // DisableHashJoin indicates whether to disable hash join. - DisableHashJoin bool - - // EnableHistoricalStats indicates whether to enable historical statistics. - EnableHistoricalStats bool - - // EnableIndexMergeJoin indicates whether to enable index merge join. - EnableIndexMergeJoin bool - - // TrackAggregateMemoryUsage indicates whether to track the memory usage of aggregate function. - TrackAggregateMemoryUsage bool - - // TiDBEnableExchangePartition indicates whether to enable exchange partition - TiDBEnableExchangePartition bool - - // AllowFallbackToTiKV indicates the engine types whose unavailability triggers fallback to TiKV. - // Now we only support TiFlash. - AllowFallbackToTiKV map[kv.StoreType]struct{} - - // CTEMaxRecursionDepth indicates The common table expression (CTE) maximum recursion depth. - // see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_cte_max_recursion_depth - CTEMaxRecursionDepth int - - // The temporary table size threshold, which is different from MySQL. See https://github.com/pingcap/tidb/issues/28691. - TMPTableSize int64 - - // EnableStableResultMode if stabilize query results. - EnableStableResultMode bool - - // EnablePseudoForOutdatedStats if using pseudo for outdated stats - EnablePseudoForOutdatedStats bool - - // RegardNULLAsPoint if regard NULL as Point - RegardNULLAsPoint bool - - // LocalTemporaryTables is *infoschema.LocalTemporaryTables, use interface to avoid circle dependency. - // It's nil if there is no local temporary table. - LocalTemporaryTables interface{} - - // TemporaryTableData stores committed kv values for temporary table for current session. - TemporaryTableData TemporaryTableData - - // MPPStoreFailTTL indicates the duration that protect TiDB from sending task to a new recovered TiFlash. - MPPStoreFailTTL string - - // ReadStaleness indicates the staleness duration for the following query - ReadStaleness time.Duration - - // cachedStmtCtx is used to optimze the object allocation. - cachedStmtCtx [2]stmtctx.StatementContext - - // Rng stores the rand_seed1 and rand_seed2 for Rand() function - Rng *mathutil.MysqlRng - - // EnablePaging indicates whether enable paging in coprocessor requests. - EnablePaging bool - - // EnableLegacyInstanceScope says if SET SESSION can be used to set an instance - // scope variable. The default is TRUE. - EnableLegacyInstanceScope bool - - // ReadConsistency indicates the read consistency requirement. - ReadConsistency ReadConsistencyLevel - - // StatsLoadSyncWait indicates how long to wait for stats load before timeout. - StatsLoadSyncWait int64 - - // SysdateIsNow indicates whether Sysdate is an alias of Now function - SysdateIsNow bool - // EnableMutationChecker indicates whether to check data consistency for mutations - EnableMutationChecker bool - // AssertionLevel controls how strict the assertions on data mutations should be. - AssertionLevel AssertionLevel - // IgnorePreparedCacheCloseStmt controls if ignore the close-stmt command for prepared statement. - IgnorePreparedCacheCloseStmt bool - // EnableNewCostInterface is a internal switch to indicates whether to use the new cost calculation interface. - EnableNewCostInterface bool - // CostModelVersion is a internal switch to indicates the Cost Model Version. - CostModelVersion int - // IndexJoinDoubleReadPenaltyCostRate indicates whether to add some penalty cost to IndexJoin and how much of it. - IndexJoinDoubleReadPenaltyCostRate float64 - - // BatchPendingTiFlashCount shows the threshold of pending TiFlash tables when batch adding. - BatchPendingTiFlashCount int - // RcWriteCheckTS indicates whether some special write statements don't get latest tso from PD at RC - RcWriteCheckTS bool - // RemoveOrderbyInSubquery indicates whether to remove ORDER BY in subquery. - RemoveOrderbyInSubquery bool - // NonTransactionalIgnoreError indicates whether to ignore error in non-transactional statements. - // When set to false, returns immediately when it meets the first error. - NonTransactionalIgnoreError bool - - // MaxAllowedPacket indicates the maximum size of a packet for the MySQL protocol. - MaxAllowedPacket uint64 - - // TiFlash related optimization, only for MPP. - TiFlashFineGrainedShuffleStreamCount int64 - TiFlashFineGrainedShuffleBatchSize uint64 - - // RequestSourceType is the type of inner request. - RequestSourceType string - // ExplicitRequestSourceType is the type of origin external request. - ExplicitRequestSourceType string - - // MemoryDebugModeMinHeapInUse indicated the minimum heapInUse threshold that triggers the memoryDebugMode. - MemoryDebugModeMinHeapInUse int64 - // MemoryDebugModeAlarmRatio indicated the allowable bias ratio of memory tracking accuracy check. - // When `(memory trakced by tidb) * (1+MemoryDebugModeAlarmRatio) < actual heapInUse`, an alarm log will be recorded. - MemoryDebugModeAlarmRatio int64 - - // EnableAnalyzeSnapshot indicates whether to read data on snapshot when collecting statistics. - // When it is false, ANALYZE reads the latest data. - // When it is true, ANALYZE reads data on the snapshot at the beginning of ANALYZE. - EnableAnalyzeSnapshot bool - - // DefaultStrMatchSelectivity adjust the estimation strategy for string matching expressions that can't be estimated by building into range. - // when > 0: it's the selectivity for the expression. - // when = 0: try to use TopN to evaluate the like expression to estimate the selectivity. - DefaultStrMatchSelectivity float64 - - // TiFlashFastScan indicates whether use fast scan in TiFlash - TiFlashFastScan bool - - // PrimaryKeyRequired indicates if sql_require_primary_key sysvar is set - PrimaryKeyRequired bool - - // EnablePreparedPlanCache indicates whether to enable prepared plan cache. - EnablePreparedPlanCache bool - - // PreparedPlanCacheSize controls the size of prepared plan cache. - PreparedPlanCacheSize uint64 - - // PreparedPlanCacheMonitor indicates whether to enable prepared plan cache monitor. - EnablePreparedPlanCacheMemoryMonitor bool - - // EnablePlanCacheForParamLimit controls whether the prepare statement with parameterized limit can be cached - EnablePlanCacheForParamLimit bool - - // EnablePlanCacheForSubquery controls whether the prepare statement with sub query can be cached - EnablePlanCacheForSubquery bool - - // EnableNonPreparedPlanCache indicates whether to enable non-prepared plan cache. - EnableNonPreparedPlanCache bool - - // EnableNonPreparedPlanCacheForDML indicates whether to enable non-prepared plan cache for DML statements. - EnableNonPreparedPlanCacheForDML bool - - // PlanCacheInvalidationOnFreshStats controls if plan cache will be invalidated automatically when - // related stats are analyzed after the plan cache is generated. - PlanCacheInvalidationOnFreshStats bool - - // NonPreparedPlanCacheSize controls the size of non-prepared plan cache. - NonPreparedPlanCacheSize uint64 - - // PlanCacheMaxPlanSize controls the maximum size of a plan that can be cached. - PlanCacheMaxPlanSize uint64 - - // SessionPlanCacheSize controls the size of session plan cache. - SessionPlanCacheSize uint64 - - // ConstraintCheckInPlacePessimistic controls whether to skip the locking of some keys in pessimistic transactions. - // Postpone the conflict check and constraint check to prewrite or later pessimistic locking requests. - ConstraintCheckInPlacePessimistic bool - - // EnableTiFlashReadForWriteStmt indicates whether to enable TiFlash to read for write statements. - EnableTiFlashReadForWriteStmt bool - - // EnableUnsafeSubstitute indicates whether to enable generate column takes unsafe substitute. - EnableUnsafeSubstitute bool - - // ForeignKeyChecks indicates whether to enable foreign key constraint check. - ForeignKeyChecks bool - - // RangeMaxSize is the max memory limit for ranges. When the optimizer estimates that the memory usage of complete - // ranges would exceed the limit, it chooses less accurate ranges such as full range. 0 indicates that there is no - // memory limit for ranges. - RangeMaxSize int64 - - // LastPlanReplayerToken indicates the last plan replayer token - LastPlanReplayerToken string - - // InPlanReplayer means we are now executing a statement for a PLAN REPLAYER SQL. - // Note that PLAN REPLAYER CAPTURE is not included here. - InPlanReplayer bool - - // AnalyzePartitionConcurrency indicates concurrency for partitions in Analyze - AnalyzePartitionConcurrency int - // AnalyzePartitionMergeConcurrency indicates concurrency for merging partition stats - AnalyzePartitionMergeConcurrency int - - // EnableExternalTSRead indicates whether to enable read through external ts - EnableExternalTSRead bool - - HookContext - - // MemTracker indicates the memory tracker of current session. - MemTracker *memory.Tracker - // MemDBDBFootprint tracks the memory footprint of memdb, and is attached to `MemTracker` - MemDBFootprint *memory.Tracker - DiskTracker *memory.Tracker - - // OptPrefixIndexSingleScan indicates whether to do some optimizations to avoid double scan for prefix index. - // When set to true, `col is (not) null`(`col` is index prefix column) is regarded as index filter rather than table filter. - OptPrefixIndexSingleScan bool - - // ChunkPool Several chunks and columns are cached - ChunkPool ReuseChunkPool - // EnableReuseCheck indicates request chunk whether use chunk alloc - EnableReuseCheck bool - - // EnableAdvancedJoinHint indicates whether the join method hint is compatible with join order hint. - EnableAdvancedJoinHint bool - - // preuseChunkAlloc indicates whether pre statement use chunk alloc - // like select @@last_sql_use_alloc - preUseChunkAlloc bool - - // EnablePlanReplayerCapture indicates whether enabled plan replayer capture - EnablePlanReplayerCapture bool - - // EnablePlanReplayedContinuesCapture indicates whether enabled plan replayer continues capture - EnablePlanReplayedContinuesCapture bool - - // PlanReplayerFinishedTaskKey used to record the finished plan replayer task key in order not to record the - // duplicate task in plan replayer continues capture - PlanReplayerFinishedTaskKey map[replayer.PlanReplayerTaskKey]struct{} - - // StoreBatchSize indicates the batch size limit of store batch, set this field to 0 to disable store batch. - StoreBatchSize int - - // shardRand is used by TxnCtx, for the GetCurrentShard() method. - shardRand *rand.Rand - - // Resource group name - ResourceGroupName string - - // PessimisticTransactionFairLocking controls whether fair locking for pessimistic transaction - // is enabled. - PessimisticTransactionFairLocking bool - - // EnableINLJoinInnerMultiPattern indicates whether enable multi pattern for index join inner side - // For now it is not public to user - EnableINLJoinInnerMultiPattern bool - - // Enable late materialization: push down some selection condition to tablescan. - EnableLateMaterialization bool - - // EnableRowLevelChecksum indicates whether row level checksum is enabled. - EnableRowLevelChecksum bool - - // TiFlashComputeDispatchPolicy indicates how to dipatch task to tiflash_compute nodes. - // Only for disaggregated-tiflash mode. - TiFlashComputeDispatchPolicy tiflashcompute.DispatchPolicy - - // SlowTxnThreshold is the threshold of slow transaction logs - SlowTxnThreshold uint64 - - // LoadBasedReplicaReadThreshold is the threshold for the estimated wait duration of a store. - // If exceeding the threshold, try other stores using replica read. - LoadBasedReplicaReadThreshold time.Duration - - // OptOrderingIdxSelThresh is the threshold for optimizer to consider the ordering index. - // If there exists an index whose estimated selectivity is smaller than this threshold, the optimizer won't - // use the ExpectedCnt to adjust the estimated row count for index scan. - OptOrderingIdxSelThresh float64 - - // EnableMPPSharedCTEExecution indicates whether we enable the shared CTE execution strategy on MPP side. - EnableMPPSharedCTEExecution bool - - // OptimizerFixControl control some details of the optimizer behavior through the tidb_opt_fix_control variable. - OptimizerFixControl map[uint64]string - - // FastCheckTable is used to control whether fast check table is enabled. - FastCheckTable bool - - // HypoIndexes are for the Index Advisor. - HypoIndexes map[string]map[string]map[string]*model.IndexInfo // dbName -> tblName -> idxName -> idxInfo - - // TiFlashReplicaRead indicates the policy of TiFlash node selection when the query needs the TiFlash engine. - TiFlashReplicaRead tiflash.ReplicaRead - - // HypoTiFlashReplicas are for the Index Advisor. - HypoTiFlashReplicas map[string]map[string]struct{} // dbName -> tblName -> whether to have replicas - - // Runtime Filter Group - // Runtime filter type: only support IN or MIN_MAX now. - // Runtime filter type can take multiple values at the same time. - runtimeFilterTypes []RuntimeFilterType - // Runtime filter mode: only support OFF, LOCAL now - runtimeFilterMode RuntimeFilterMode - - // Whether to lock duplicate keys in INSERT IGNORE and REPLACE statements, - // or unchanged unique keys in UPDATE statements, see PR #42210 and #42713 - LockUnchangedKeys bool - - // AnalyzeSkipColumnTypes indicates the column types whose statistics would not be collected when executing the ANALYZE command. - AnalyzeSkipColumnTypes map[string]struct{} - - // SkipMissingPartitionStats controls how to handle missing partition stats when merging partition stats to global stats. - // When set to true, skip missing partition stats and continue to merge other partition stats to global stats. - // When set to false, give up merging partition stats to global stats. - SkipMissingPartitionStats bool - - // SessionAlias is the identifier of the session - SessionAlias string - - // OptObjective indicates whether the optimizer should be more stable, predictable or more aggressive. - // For now, the possible values and corresponding behaviors are: - // OptObjectiveModerate: The default value. The optimizer considers the real-time stats (real-time row count, modify count). - // OptObjectiveDeterminate: The optimizer doesn't consider the real-time stats. - OptObjective string -} - -// GetOptimizerFixControlMap returns the specified value of the optimizer fix control. -func (s *SessionVars) GetOptimizerFixControlMap() map[uint64]string { - return s.OptimizerFixControl -} - -// planReplayerSessionFinishedTaskKeyLen is used to control the max size for the finished plan replayer task key in session -// in order to control the used memory -const planReplayerSessionFinishedTaskKeyLen = 128 - -// AddPlanReplayerFinishedTaskKey record finished task key in session -func (s *SessionVars) AddPlanReplayerFinishedTaskKey(key replayer.PlanReplayerTaskKey) { - if len(s.PlanReplayerFinishedTaskKey) >= planReplayerSessionFinishedTaskKeyLen { - s.initializePlanReplayerFinishedTaskKey() - } - s.PlanReplayerFinishedTaskKey[key] = struct{}{} -} - -func (s *SessionVars) initializePlanReplayerFinishedTaskKey() { - s.PlanReplayerFinishedTaskKey = make(map[replayer.PlanReplayerTaskKey]struct{}, planReplayerSessionFinishedTaskKeyLen) -} - -// CheckPlanReplayerFinishedTaskKey check whether the key exists -func (s *SessionVars) CheckPlanReplayerFinishedTaskKey(key replayer.PlanReplayerTaskKey) bool { - if s.PlanReplayerFinishedTaskKey == nil { - s.initializePlanReplayerFinishedTaskKey() - return false - } - _, ok := s.PlanReplayerFinishedTaskKey[key] - return ok -} - -// IsPlanReplayerCaptureEnabled indicates whether capture or continues capture enabled -func (s *SessionVars) IsPlanReplayerCaptureEnabled() bool { - return s.EnablePlanReplayerCapture || s.EnablePlanReplayedContinuesCapture -} - -// GetNewChunkWithCapacity Attempt to request memory from the chunk pool -// thread safety -func (s *SessionVars) GetNewChunkWithCapacity(fields []*types.FieldType, capacity int, maxCachesize int, pool chunk.Allocator) *chunk.Chunk { - if pool == nil { - return chunk.New(fields, capacity, maxCachesize) - } - s.ChunkPool.mu.Lock() - defer s.ChunkPool.mu.Unlock() - if pool.CheckReuseAllocSize() && (!s.GetUseChunkAlloc()) { - s.StmtCtx.SetUseChunkAlloc() - } - chk := pool.Alloc(fields, capacity, maxCachesize) - return chk -} - -// ExchangeChunkStatus give the status to preUseChunkAlloc -func (s *SessionVars) ExchangeChunkStatus() { - s.preUseChunkAlloc = s.GetUseChunkAlloc() -} - -// GetUseChunkAlloc return useChunkAlloc status -func (s *SessionVars) GetUseChunkAlloc() bool { - return s.StmtCtx.GetUseChunkAllocStatus() -} - -// SetAlloc Attempt to set the buffer pool address -func (s *SessionVars) SetAlloc(alloc chunk.Allocator) { - if !s.EnableReuseCheck { - return - } - s.ChunkPool.Alloc = alloc -} - -// IsAllocValid check if chunk reuse is enable or ChunkPool is inused. -func (s *SessionVars) IsAllocValid() bool { - if !s.EnableReuseCheck { - return false - } - s.ChunkPool.mu.Lock() - defer s.ChunkPool.mu.Unlock() - return s.ChunkPool.Alloc != nil -} - -// ClearAlloc indicates stop reuse chunk -func (s *SessionVars) ClearAlloc(alloc *chunk.Allocator, b bool) { - if !b { - s.ChunkPool.Alloc = nil - return - } - - // If an error is reported, re-apply for alloc - // Prevent the goroutine left before, affecting the execution of the next sql - // issuse 38918 - s.ChunkPool.mu.Lock() - s.ChunkPool.Alloc = nil - s.ChunkPool.mu.Unlock() - *alloc = chunk.NewAllocator() -} - -// GetPreparedStmtByName returns the prepared statement specified by stmtName. -func (s *SessionVars) GetPreparedStmtByName(stmtName string) (interface{}, error) { - stmtID, ok := s.PreparedStmtNameToID[stmtName] - if !ok { - return nil, ErrStmtNotFound - } - return s.GetPreparedStmtByID(stmtID) -} - -// GetPreparedStmtByID returns the prepared statement specified by stmtID. -func (s *SessionVars) GetPreparedStmtByID(stmtID uint32) (interface{}, error) { - stmt, ok := s.PreparedStmts[stmtID] - if !ok { - return nil, ErrStmtNotFound - } - return stmt, nil -} - -// InitStatementContext initializes a StatementContext, the object is reused to reduce allocation. -func (s *SessionVars) InitStatementContext() *stmtctx.StatementContext { - sc := &s.cachedStmtCtx[0] - if sc == s.StmtCtx { - sc = &s.cachedStmtCtx[1] - } - if s.RefCountOfStmtCtx.TryFreeze() { - sc.Reset() - s.RefCountOfStmtCtx.UnFreeze() - } else { - sc = stmtctx.NewStmtCtx() - } - return sc -} - -// IsMPPAllowed returns whether mpp execution is allowed. -func (s *SessionVars) IsMPPAllowed() bool { - return s.allowMPPExecution -} - -// IsTiFlashCopBanned returns whether cop execution is allowed. -func (s *SessionVars) IsTiFlashCopBanned() bool { - return !s.allowTiFlashCop -} - -// IsMPPEnforced returns whether mpp execution is enforced. -func (s *SessionVars) IsMPPEnforced() bool { - return s.allowMPPExecution && s.enforceMPPExecution -} - -// ChooseMppVersion indicates the mpp-version used to build mpp plan, if mpp-version is unspecified, use the latest version. -func (s *SessionVars) ChooseMppVersion() kv.MppVersion { - if s.mppVersion == kv.MppVersionUnspecified { - return kv.GetNewestMppVersion() - } - return s.mppVersion -} - -// ChooseMppExchangeCompressionMode indicates the data compression method in mpp exchange operator -func (s *SessionVars) ChooseMppExchangeCompressionMode() kv.ExchangeCompressionMode { - if s.mppExchangeCompressionMode == kv.ExchangeCompressionModeUnspecified { - // If unspecified, use recommended mode - return kv.RecommendedExchangeCompressionMode - } - return s.mppExchangeCompressionMode -} - -// RaiseWarningWhenMPPEnforced will raise a warning when mpp mode is enforced and executing explain statement. -// TODO: Confirm whether this function will be inlined and -// omit the overhead of string construction when calling with false condition. -func (s *SessionVars) RaiseWarningWhenMPPEnforced(warning string) { - if !s.IsMPPEnforced() { - return - } - if s.StmtCtx.InExplainStmt { - s.StmtCtx.AppendWarning(errors.New(warning)) - } else { - s.StmtCtx.AppendExtraWarning(errors.New(warning)) - } -} - -// CheckAndGetTxnScope will return the transaction scope we should use in the current session. -func (s *SessionVars) CheckAndGetTxnScope() string { - if s.InRestrictedSQL || !EnableLocalTxn.Load() { - return kv.GlobalTxnScope - } - if s.TxnScope.GetVarValue() == kv.LocalTxnScope { - return s.TxnScope.GetTxnScope() - } - return kv.GlobalTxnScope -} - -// IsDynamicPartitionPruneEnabled indicates whether dynamic partition prune enabled -// Note that: IsDynamicPartitionPruneEnabled only indicates whether dynamic partition prune mode is enabled according to -// session variable, it isn't guaranteed to be used during query due to other conditions checking. -func (s *SessionVars) IsDynamicPartitionPruneEnabled() bool { - return PartitionPruneMode(s.PartitionPruneMode.Load()) == Dynamic -} - -// IsRowLevelChecksumEnabled indicates whether row level checksum is enabled for current session, that is -// tidb_enable_row_level_checksum is on and tidb_row_format_version is 2 and it's not a internal session. -func (s *SessionVars) IsRowLevelChecksumEnabled() bool { - return s.EnableRowLevelChecksum && s.RowEncoder.Enable && !s.InRestrictedSQL -} - -// BuildParserConfig generate parser.ParserConfig for initial parser -func (s *SessionVars) BuildParserConfig() parser.ParserConfig { - return parser.ParserConfig{ - EnableWindowFunction: s.EnableWindowFunction, - EnableStrictDoubleTypeCheck: s.EnableStrictDoubleTypeCheck, - SkipPositionRecording: true, - } -} - -// AllocNewPlanID alloc new ID -func (s *SessionVars) AllocNewPlanID() int { - return int(s.PlanID.Add(1)) -} - -const ( - // PlacementModeStrict indicates all placement operations should be checked strictly in ddl - PlacementModeStrict string = "STRICT" - // PlacementModeIgnore indicates ignore all placement operations in ddl - PlacementModeIgnore string = "IGNORE" -) - -// PartitionPruneMode presents the prune mode used. -type PartitionPruneMode string - -const ( - // Static indicates only prune at plan phase. - Static PartitionPruneMode = "static" - // Dynamic indicates only prune at execute phase. - Dynamic PartitionPruneMode = "dynamic" - - // Don't use out-of-date mode. - - // StaticOnly is out-of-date. - StaticOnly PartitionPruneMode = "static-only" - // DynamicOnly is out-of-date. - DynamicOnly PartitionPruneMode = "dynamic-only" - // StaticButPrepareDynamic is out-of-date. - StaticButPrepareDynamic PartitionPruneMode = "static-collect-dynamic" -) - -// Valid indicate PruneMode is validated. -func (p PartitionPruneMode) Valid() bool { - switch p { - case Static, Dynamic, StaticOnly, DynamicOnly: - return true - default: - return false - } -} - -// Update updates out-of-date PruneMode. -func (p PartitionPruneMode) Update() PartitionPruneMode { - switch p { - case StaticOnly, StaticButPrepareDynamic: - return Static - case DynamicOnly: - return Dynamic - default: - return p - } -} - -// PlanCacheParamList stores the parameters for plan cache. -// Use attached methods to access or modify parameter values instead of accessing them directly. -type PlanCacheParamList struct { - paramValues []types.Datum - forNonPrepCache bool -} - -// NewPlanCacheParamList creates a new PlanCacheParams. -func NewPlanCacheParamList() *PlanCacheParamList { - p := &PlanCacheParamList{paramValues: make([]types.Datum, 0, 8)} - p.Reset() - return p -} - -// Reset resets the PlanCacheParams. -func (p *PlanCacheParamList) Reset() { - p.paramValues = p.paramValues[:0] - p.forNonPrepCache = false -} - -// String implements the fmt.Stringer interface. -func (p *PlanCacheParamList) String() string { - if p == nil || len(p.paramValues) == 0 || - p.forNonPrepCache { // hide non-prep parameter values by default - return "" - } - return " [arguments: " + types.DatumsToStrNoErr(p.paramValues) + "]" -} - -// Append appends a parameter value to the PlanCacheParams. -func (p *PlanCacheParamList) Append(vs ...types.Datum) { - p.paramValues = append(p.paramValues, vs...) -} - -// SetForNonPrepCache sets the flag forNonPrepCache. -func (p *PlanCacheParamList) SetForNonPrepCache(flag bool) { - p.forNonPrepCache = flag -} - -// GetParamValue returns the value of the parameter at the specified index. -func (p *PlanCacheParamList) GetParamValue(idx int) types.Datum { - return p.paramValues[idx] -} - -// AllParamValues returns all parameter values. -func (p *PlanCacheParamList) AllParamValues() []types.Datum { - return p.paramValues -} - -// ConnectionInfo presents the connection information, which is mainly used by audit logs. -type ConnectionInfo struct { - ConnectionID uint64 - ConnectionType string - Host string - ClientIP string - ClientPort string - ServerID int - ServerIP string - ServerPort int - Duration float64 - User string - ServerOSLoginUser string - OSVersion string - ClientVersion string - ServerVersion string - SSLVersion string - PID int - DB string - AuthMethod string - Attributes map[string]string -} - -const ( - // ConnTypeSocket indicates socket without TLS. - ConnTypeSocket string = "TCP" - // ConnTypeUnixSocket indicates Unix Socket. - ConnTypeUnixSocket string = "UnixSocket" - // ConnTypeTLS indicates socket with TLS. - ConnTypeTLS string = "SSL/TLS" -) - -// IsSecureTransport checks whether the connection is secure. -func (connInfo *ConnectionInfo) IsSecureTransport() bool { - switch connInfo.ConnectionType { - case ConnTypeUnixSocket, ConnTypeTLS: - return true - } - return false -} - -// NewSessionVars creates a session vars object. -func NewSessionVars(hctx HookContext) *SessionVars { - vars := &SessionVars{ - userVars: struct { - lock sync.RWMutex - values map[string]types.Datum - types map[string]*types.FieldType - }{ - values: make(map[string]types.Datum), - types: make(map[string]*types.FieldType), - }, - systems: make(map[string]string), - stmtVars: make(map[string]string), - PreparedStmts: make(map[uint32]interface{}), - PreparedStmtNameToID: make(map[string]uint32), - PlanCacheParams: NewPlanCacheParamList(), - TxnCtx: &TransactionContext{}, - RetryInfo: &RetryInfo{}, - ActiveRoles: make([]*auth.RoleIdentity, 0, 10), - StrictSQLMode: true, - AutoIncrementIncrement: DefAutoIncrementIncrement, - AutoIncrementOffset: DefAutoIncrementOffset, - Status: mysql.ServerStatusAutocommit, - StmtCtx: stmtctx.NewStmtCtx(), - AllowAggPushDown: false, - AllowCartesianBCJ: DefOptCartesianBCJ, - MPPOuterJoinFixedBuildSide: DefOptMPPOuterJoinFixedBuildSide, - BroadcastJoinThresholdSize: DefBroadcastJoinThresholdSize, - BroadcastJoinThresholdCount: DefBroadcastJoinThresholdSize, - OptimizerSelectivityLevel: DefTiDBOptimizerSelectivityLevel, - EnableOuterJoinReorder: DefTiDBEnableOuterJoinReorder, - RetryLimit: DefTiDBRetryLimit, - DisableTxnAutoRetry: DefTiDBDisableTxnAutoRetry, - DDLReorgPriority: kv.PriorityLow, - allowInSubqToJoinAndAgg: DefOptInSubqToJoinAndAgg, - preferRangeScan: DefOptPreferRangeScan, - EnableCorrelationAdjustment: DefOptEnableCorrelationAdjustment, - LimitPushDownThreshold: DefOptLimitPushDownThreshold, - CorrelationThreshold: DefOptCorrelationThreshold, - CorrelationExpFactor: DefOptCorrelationExpFactor, - cpuFactor: DefOptCPUFactor, - copCPUFactor: DefOptCopCPUFactor, - CopTiFlashConcurrencyFactor: DefOptTiFlashConcurrencyFactor, - networkFactor: DefOptNetworkFactor, - scanFactor: DefOptScanFactor, - descScanFactor: DefOptDescScanFactor, - seekFactor: DefOptSeekFactor, - memoryFactor: DefOptMemoryFactor, - diskFactor: DefOptDiskFactor, - concurrencyFactor: DefOptConcurrencyFactor, - enableForceInlineCTE: DefOptForceInlineCTE, - EnableVectorizedExpression: DefEnableVectorizedExpression, - CommandValue: uint32(mysql.ComSleep), - TiDBOptJoinReorderThreshold: DefTiDBOptJoinReorderThreshold, - SlowQueryFile: config.GetGlobalConfig().Log.SlowQueryFile, - WaitSplitRegionFinish: DefTiDBWaitSplitRegionFinish, - WaitSplitRegionTimeout: DefWaitSplitRegionTimeout, - enableIndexMerge: DefTiDBEnableIndexMerge, - NoopFuncsMode: TiDBOptOnOffWarn(DefTiDBEnableNoopFuncs), - replicaRead: kv.ReplicaReadLeader, - AllowRemoveAutoInc: DefTiDBAllowRemoveAutoInc, - UsePlanBaselines: DefTiDBUsePlanBaselines, - EvolvePlanBaselines: DefTiDBEvolvePlanBaselines, - EnableExtendedStats: false, - IsolationReadEngines: make(map[kv.StoreType]struct{}), - LockWaitTimeout: DefInnodbLockWaitTimeout * 1000, - MetricSchemaStep: DefTiDBMetricSchemaStep, - MetricSchemaRangeDuration: DefTiDBMetricSchemaRangeDuration, - SequenceState: NewSequenceState(), - WindowingUseHighPrecision: true, - PrevFoundInPlanCache: DefTiDBFoundInPlanCache, - FoundInPlanCache: DefTiDBFoundInPlanCache, - PrevFoundInBinding: DefTiDBFoundInBinding, - FoundInBinding: DefTiDBFoundInBinding, - SelectLimit: math.MaxUint64, - AllowAutoRandExplicitInsert: DefTiDBAllowAutoRandExplicitInsert, - EnableClusteredIndex: DefTiDBEnableClusteredIndex, - EnableParallelApply: DefTiDBEnableParallelApply, - ShardAllocateStep: DefTiDBShardAllocateStep, - PartitionPruneMode: *atomic2.NewString(DefTiDBPartitionPruneMode), - TxnScope: kv.NewDefaultTxnScopeVar(), - EnabledRateLimitAction: DefTiDBEnableRateLimitAction, - EnableAsyncCommit: DefTiDBEnableAsyncCommit, - Enable1PC: DefTiDBEnable1PC, - GuaranteeLinearizability: DefTiDBGuaranteeLinearizability, - AnalyzeVersion: DefTiDBAnalyzeVersion, - EnableIndexMergeJoin: DefTiDBEnableIndexMergeJoin, - AllowFallbackToTiKV: make(map[kv.StoreType]struct{}), - CTEMaxRecursionDepth: DefCTEMaxRecursionDepth, - TMPTableSize: DefTiDBTmpTableMaxSize, - MPPStoreFailTTL: DefTiDBMPPStoreFailTTL, - Rng: mathutil.NewWithTime(), - StatsLoadSyncWait: StatsLoadSyncWait.Load(), - EnableLegacyInstanceScope: DefEnableLegacyInstanceScope, - RemoveOrderbyInSubquery: DefTiDBRemoveOrderbyInSubquery, - EnableSkewDistinctAgg: DefTiDBSkewDistinctAgg, - Enable3StageDistinctAgg: DefTiDB3StageDistinctAgg, - MaxAllowedPacket: DefMaxAllowedPacket, - TiFlashFastScan: DefTiFlashFastScan, - EnableTiFlashReadForWriteStmt: true, - ForeignKeyChecks: DefTiDBForeignKeyChecks, - HookContext: hctx, - EnableReuseCheck: DefTiDBEnableReusechunk, - preUseChunkAlloc: DefTiDBUseAlloc, - ChunkPool: ReuseChunkPool{Alloc: nil}, - mppExchangeCompressionMode: DefaultExchangeCompressionMode, - mppVersion: kv.MppVersionUnspecified, - EnableLateMaterialization: DefTiDBOptEnableLateMaterialization, - TiFlashComputeDispatchPolicy: tiflashcompute.DispatchPolicyConsistentHash, - ResourceGroupName: resourcegroup.DefaultResourceGroupName, - DefaultCollationForUTF8MB4: mysql.DefaultCollationName, - } - vars.KVVars = tikvstore.NewVariables(&vars.Killed) - vars.Concurrency = Concurrency{ - indexLookupConcurrency: DefIndexLookupConcurrency, - indexSerialScanConcurrency: DefIndexSerialScanConcurrency, - indexLookupJoinConcurrency: DefIndexLookupJoinConcurrency, - hashJoinConcurrency: DefTiDBHashJoinConcurrency, - projectionConcurrency: DefTiDBProjectionConcurrency, - distSQLScanConcurrency: DefDistSQLScanConcurrency, - hashAggPartialConcurrency: DefTiDBHashAggPartialConcurrency, - hashAggFinalConcurrency: DefTiDBHashAggFinalConcurrency, - windowConcurrency: DefTiDBWindowConcurrency, - mergeJoinConcurrency: DefTiDBMergeJoinConcurrency, - streamAggConcurrency: DefTiDBStreamAggConcurrency, - indexMergeIntersectionConcurrency: DefTiDBIndexMergeIntersectionConcurrency, - ExecutorConcurrency: DefExecutorConcurrency, - } - vars.MemQuota = MemQuota{ - MemQuotaQuery: DefTiDBMemQuotaQuery, - MemQuotaApplyCache: DefTiDBMemQuotaApplyCache, - } - vars.BatchSize = BatchSize{ - IndexJoinBatchSize: DefIndexJoinBatchSize, - IndexLookupSize: DefIndexLookupSize, - InitChunkSize: DefInitChunkSize, - MaxChunkSize: DefMaxChunkSize, - MinPagingSize: DefMinPagingSize, - MaxPagingSize: DefMaxPagingSize, - } - vars.DMLBatchSize = DefDMLBatchSize - vars.AllowBatchCop = DefTiDBAllowBatchCop - vars.allowMPPExecution = DefTiDBAllowMPPExecution - vars.HashExchangeWithNewCollation = DefTiDBHashExchangeWithNewCollation - vars.enforceMPPExecution = DefTiDBEnforceMPPExecution - vars.TiFlashMaxThreads = DefTiFlashMaxThreads - vars.TiFlashMaxBytesBeforeExternalJoin = DefTiFlashMaxBytesBeforeExternalJoin - vars.TiFlashMaxBytesBeforeExternalGroupBy = DefTiFlashMaxBytesBeforeExternalGroupBy - vars.TiFlashMaxBytesBeforeExternalSort = DefTiFlashMaxBytesBeforeExternalSort - vars.TiFlashMaxQueryMemoryPerNode = DefTiFlashMemQuotaQueryPerNode - vars.TiFlashQuerySpillRatio = DefTiFlashQuerySpillRatio - vars.MPPStoreFailTTL = DefTiDBMPPStoreFailTTL - vars.DiskTracker = disk.NewTracker(memory.LabelForSession, -1) - vars.MemTracker = memory.NewTracker(memory.LabelForSession, vars.MemQuotaQuery) - vars.MemTracker.IsRootTrackerOfSess = true - - for _, engine := range config.GetGlobalConfig().IsolationRead.Engines { - switch engine { - case kv.TiFlash.Name(): - vars.IsolationReadEngines[kv.TiFlash] = struct{}{} - case kv.TiKV.Name(): - vars.IsolationReadEngines[kv.TiKV] = struct{}{} - case kv.TiDB.Name(): - vars.IsolationReadEngines[kv.TiDB] = struct{}{} - } - } - if !EnableLocalTxn.Load() { - vars.TxnScope = kv.NewGlobalTxnScopeVar() - } - if EnableRowLevelChecksum.Load() { - vars.EnableRowLevelChecksum = true - } - vars.systems[CharacterSetConnection], vars.systems[CollationConnection] = charset.GetDefaultCharsetAndCollate() - return vars -} - -// GetAllowInSubqToJoinAndAgg get AllowInSubqToJoinAndAgg from sql hints and SessionVars.allowInSubqToJoinAndAgg. -func (s *SessionVars) GetAllowInSubqToJoinAndAgg() bool { - if s.StmtCtx.HasAllowInSubqToJoinAndAggHint { - return s.StmtCtx.AllowInSubqToJoinAndAgg - } - return s.allowInSubqToJoinAndAgg -} - -// SetAllowInSubqToJoinAndAgg set SessionVars.allowInSubqToJoinAndAgg. -func (s *SessionVars) SetAllowInSubqToJoinAndAgg(val bool) { - s.allowInSubqToJoinAndAgg = val -} - -// GetAllowPreferRangeScan get preferRangeScan from SessionVars.preferRangeScan. -func (s *SessionVars) GetAllowPreferRangeScan() bool { - return s.preferRangeScan -} - -// SetAllowPreferRangeScan set SessionVars.preferRangeScan. -func (s *SessionVars) SetAllowPreferRangeScan(val bool) { - s.preferRangeScan = val -} - -// GetEnableCascadesPlanner get EnableCascadesPlanner from sql hints and SessionVars.EnableCascadesPlanner. -func (s *SessionVars) GetEnableCascadesPlanner() bool { - if s.StmtCtx.HasEnableCascadesPlannerHint { - return s.StmtCtx.EnableCascadesPlanner - } - return s.EnableCascadesPlanner -} - -// SetEnableCascadesPlanner set SessionVars.EnableCascadesPlanner. -func (s *SessionVars) SetEnableCascadesPlanner(val bool) { - s.EnableCascadesPlanner = val -} - -// GetEnableIndexMerge get EnableIndexMerge from SessionVars.enableIndexMerge. -func (s *SessionVars) GetEnableIndexMerge() bool { - return s.enableIndexMerge -} - -// SetEnableIndexMerge set SessionVars.enableIndexMerge. -func (s *SessionVars) SetEnableIndexMerge(val bool) { - s.enableIndexMerge = val -} - -// GetEnablePseudoForOutdatedStats get EnablePseudoForOutdatedStats from SessionVars.EnablePseudoForOutdatedStats. -func (s *SessionVars) GetEnablePseudoForOutdatedStats() bool { - return s.EnablePseudoForOutdatedStats -} - -// SetEnablePseudoForOutdatedStats set SessionVars.EnablePseudoForOutdatedStats. -func (s *SessionVars) SetEnablePseudoForOutdatedStats(val bool) { - s.EnablePseudoForOutdatedStats = val -} - -// GetReplicaRead get ReplicaRead from sql hints and SessionVars.replicaRead. -func (s *SessionVars) GetReplicaRead() kv.ReplicaReadType { - if s.StmtCtx.HasReplicaReadHint { - return kv.ReplicaReadType(s.StmtCtx.ReplicaRead) - } - // if closest-adaptive is unavailable, fallback to leader read - if s.replicaRead == kv.ReplicaReadClosestAdaptive && !IsAdaptiveReplicaReadEnabled() { - return kv.ReplicaReadLeader - } - return s.replicaRead -} - -// SetReplicaRead set SessionVars.replicaRead. -func (s *SessionVars) SetReplicaRead(val kv.ReplicaReadType) { - s.replicaRead = val -} - -// IsReplicaReadClosestAdaptive returns whether adaptive closest replica can be enabled. -func (s *SessionVars) IsReplicaReadClosestAdaptive() bool { - return s.replicaRead == kv.ReplicaReadClosestAdaptive && IsAdaptiveReplicaReadEnabled() -} - -// GetWriteStmtBufs get pointer of SessionVars.writeStmtBufs. -func (s *SessionVars) GetWriteStmtBufs() *WriteStmtBufs { - return &s.writeStmtBufs -} - -// GetSplitRegionTimeout gets split region timeout. -func (s *SessionVars) GetSplitRegionTimeout() time.Duration { - return time.Duration(s.WaitSplitRegionTimeout) * time.Second -} - -// GetIsolationReadEngines gets isolation read engines. -func (s *SessionVars) GetIsolationReadEngines() map[kv.StoreType]struct{} { - return s.IsolationReadEngines -} - -// CleanBuffers cleans the temporary bufs -func (s *SessionVars) CleanBuffers() { - s.GetWriteStmtBufs().clean() -} - -// AllocPlanColumnID allocates column id for plan. -func (s *SessionVars) AllocPlanColumnID() int64 { - return s.PlanColumnID.Add(1) -} - -// RegisterScalarSubQ register a scalar sub query into the map. This will be used for EXPLAIN. -func (s *SessionVars) RegisterScalarSubQ(scalarSubQ interface{}) { - s.MapScalarSubQ = append(s.MapScalarSubQ, scalarSubQ) -} - -// GetCharsetInfo gets charset and collation for current context. -// What character set should the server translate a statement to after receiving it? -// For this, the server uses the character_set_connection and collation_connection system variables. -// It converts statements sent by the client from character_set_client to character_set_connection -// (except for string literals that have an introducer such as _latin1 or _utf8). -// collation_connection is important for comparisons of literal strings. -// For comparisons of strings with column values, collation_connection does not matter because columns -// have their own collation, which has a higher collation precedence. -// See https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html -func (s *SessionVars) GetCharsetInfo() (charset, collation string) { - charset = s.systems[CharacterSetConnection] - collation = s.systems[CollationConnection] - return -} - -// GetParseParams gets the parse parameters from session variables. -func (s *SessionVars) GetParseParams() []parser.ParseParam { - chs, coll := s.GetCharsetInfo() - cli, err := s.GetSessionOrGlobalSystemVar(context.Background(), CharacterSetClient) - if err != nil { - cli = "" - } - return []parser.ParseParam{ - parser.CharsetConnection(chs), - parser.CollationConnection(coll), - parser.CharsetClient(cli), - } -} - -// SetStringUserVar set the value and collation for user defined variable. -func (s *SessionVars) SetStringUserVar(name string, strVal string, collation string) { - name = strings.ToLower(name) - if len(collation) > 0 { - s.SetUserVarVal(name, types.NewCollationStringDatum(stringutil.Copy(strVal), collation)) - } else { - _, collation = s.GetCharsetInfo() - s.SetUserVarVal(name, types.NewCollationStringDatum(stringutil.Copy(strVal), collation)) - } -} - -// UnsetUserVar unset an user defined variable by name. -func (s *SessionVars) UnsetUserVar(varName string) { - varName = strings.ToLower(varName) - s.userVars.lock.Lock() - defer s.userVars.lock.Unlock() - delete(s.userVars.values, varName) - delete(s.userVars.types, varName) -} - -// SetLastInsertID saves the last insert id to the session context. -// TODO: we may store the result for last_insert_id sys var later. -func (s *SessionVars) SetLastInsertID(insertID uint64) { - s.StmtCtx.LastInsertID = insertID -} - -// SetStatusFlag sets the session server status variable. -// If on is true sets the flag in session status, -// otherwise removes the flag. -func (s *SessionVars) SetStatusFlag(flag uint16, on bool) { - if on { - s.Status |= flag - return - } - s.Status &= ^flag -} - -// GetStatusFlag gets the session server status variable, returns true if it is on. -func (s *SessionVars) GetStatusFlag(flag uint16) bool { - return s.Status&flag > 0 -} - -// SetInTxn sets whether the session is in transaction. -// It also updates the IsExplicit flag in TxnCtx if val is true. -func (s *SessionVars) SetInTxn(val bool) { - s.SetStatusFlag(mysql.ServerStatusInTrans, val) - if val { - s.TxnCtx.IsExplicit = val - } -} - -// InTxn returns if the session is in transaction. -func (s *SessionVars) InTxn() bool { - return s.GetStatusFlag(mysql.ServerStatusInTrans) -} - -// IsAutocommit returns if the session is set to autocommit. -func (s *SessionVars) IsAutocommit() bool { - return s.GetStatusFlag(mysql.ServerStatusAutocommit) -} - -// IsIsolation if true it means the transaction is at that isolation level. -func (s *SessionVars) IsIsolation(isolation string) bool { - if s.TxnCtx.Isolation != "" { - return s.TxnCtx.Isolation == isolation - } - if s.txnIsolationLevelOneShot.state == oneShotUse { - s.TxnCtx.Isolation = s.txnIsolationLevelOneShot.value - } - if s.TxnCtx.Isolation == "" { - s.TxnCtx.Isolation, _ = s.GetSystemVar(TxnIsolation) - } - return s.TxnCtx.Isolation == isolation -} - -// IsolationLevelForNewTxn returns the isolation level if we want to enter a new transaction -func (s *SessionVars) IsolationLevelForNewTxn() (isolation string) { - if s.InTxn() { - if s.txnIsolationLevelOneShot.state == oneShotSet { - isolation = s.txnIsolationLevelOneShot.value - } - } else { - if s.txnIsolationLevelOneShot.state == oneShotUse { - isolation = s.txnIsolationLevelOneShot.value - } - } - - if isolation == "" { - isolation, _ = s.GetSystemVar(TxnIsolation) - } - - return -} - -// SetTxnIsolationLevelOneShotStateForNextTxn sets the txnIsolationLevelOneShot.state for next transaction. -func (s *SessionVars) SetTxnIsolationLevelOneShotStateForNextTxn() { - if isoLevelOneShot := &s.txnIsolationLevelOneShot; isoLevelOneShot.state != oneShotDef { - switch isoLevelOneShot.state { - case oneShotSet: - isoLevelOneShot.state = oneShotUse - case oneShotUse: - isoLevelOneShot.state = oneShotDef - isoLevelOneShot.value = "" - } - } -} - -// IsPessimisticReadConsistency if true it means the statement is in an read consistency pessimistic transaction. -func (s *SessionVars) IsPessimisticReadConsistency() bool { - return s.TxnCtx.IsPessimistic && s.IsIsolation(ast.ReadCommitted) -} - -// GetNextPreparedStmtID generates and returns the next session scope prepared statement id. -func (s *SessionVars) GetNextPreparedStmtID() uint32 { - s.preparedStmtID++ - return s.preparedStmtID -} - -// SetNextPreparedStmtID sets the next prepared statement id. It's only used in restoring session states. -func (s *SessionVars) SetNextPreparedStmtID(preparedStmtID uint32) { - s.preparedStmtID = preparedStmtID -} - -// Location returns the value of time_zone session variable. If it is nil, then return time.Local. -func (s *SessionVars) Location() *time.Location { - loc := s.TimeZone - if loc == nil { - loc = timeutil.SystemLocation() - } - return loc -} - -// GetSystemVar gets the string value of a system variable. -func (s *SessionVars) GetSystemVar(name string) (string, bool) { - if name == WarningCount { - return strconv.Itoa(s.SysWarningCount), true - } else if name == ErrorCount { - return strconv.Itoa(int(s.SysErrorCount)), true - } - if val, ok := s.stmtVars[name]; ok { - return val, ok - } - val, ok := s.systems[name] - return val, ok -} - -func (s *SessionVars) setDDLReorgPriority(val string) { - val = strings.ToLower(val) - switch val { - case "priority_low": - s.DDLReorgPriority = kv.PriorityLow - case "priority_normal": - s.DDLReorgPriority = kv.PriorityNormal - case "priority_high": - s.DDLReorgPriority = kv.PriorityHigh - default: - s.DDLReorgPriority = kv.PriorityLow - } -} - -type planCacheStmtKey string - -func (k planCacheStmtKey) Hash() []byte { - return []byte(k) -} - -// AddNonPreparedPlanCacheStmt adds this PlanCacheStmt into non-preapred plan-cache stmt cache -func (s *SessionVars) AddNonPreparedPlanCacheStmt(sql string, stmt interface{}) { - if s.nonPreparedPlanCacheStmts == nil { - s.nonPreparedPlanCacheStmts = kvcache.NewSimpleLRUCache(uint(s.SessionPlanCacheSize), 0, 0) - } - s.nonPreparedPlanCacheStmts.Put(planCacheStmtKey(sql), stmt) -} - -// GetNonPreparedPlanCacheStmt gets the PlanCacheStmt. -func (s *SessionVars) GetNonPreparedPlanCacheStmt(sql string) interface{} { - if s.nonPreparedPlanCacheStmts == nil { - return nil - } - stmt, _ := s.nonPreparedPlanCacheStmts.Get(planCacheStmtKey(sql)) - return stmt -} - -// AddPreparedStmt adds prepareStmt to current session and count in global. -func (s *SessionVars) AddPreparedStmt(stmtID uint32, stmt interface{}) error { - if _, exists := s.PreparedStmts[stmtID]; !exists { - maxPreparedStmtCount := MaxPreparedStmtCountValue.Load() - newPreparedStmtCount := atomic.AddInt64(&PreparedStmtCount, 1) - if maxPreparedStmtCount >= 0 && newPreparedStmtCount > maxPreparedStmtCount { - atomic.AddInt64(&PreparedStmtCount, -1) - return ErrMaxPreparedStmtCountReached.GenWithStackByArgs(maxPreparedStmtCount) - } - metrics.PreparedStmtGauge.Set(float64(newPreparedStmtCount)) - } - s.PreparedStmts[stmtID] = stmt - return nil -} - -// RemovePreparedStmt removes preparedStmt from current session and decrease count in global. -func (s *SessionVars) RemovePreparedStmt(stmtID uint32) { - _, exists := s.PreparedStmts[stmtID] - if !exists { - return - } - delete(s.PreparedStmts, stmtID) - afterMinus := atomic.AddInt64(&PreparedStmtCount, -1) - metrics.PreparedStmtGauge.Set(float64(afterMinus)) -} - -// WithdrawAllPreparedStmt remove all preparedStmt in current session and decrease count in global. -func (s *SessionVars) WithdrawAllPreparedStmt() { - psCount := len(s.PreparedStmts) - if psCount == 0 { - return - } - afterMinus := atomic.AddInt64(&PreparedStmtCount, -int64(psCount)) - metrics.PreparedStmtGauge.Set(float64(afterMinus)) -} - -// SetStmtVar sets the value of a system variable temporarily -func (s *SessionVars) setStmtVar(name string, val string) error { - s.stmtVars[name] = val - return nil -} - -// ClearStmtVars clear temporarily system variables. -func (s *SessionVars) ClearStmtVars() { - s.stmtVars = make(map[string]string) -} - -// GetSessionOrGlobalSystemVar gets a system variable. -// If it is a session only variable, use the default value defined in code. -// Returns error if there is no such variable. -func (s *SessionVars) GetSessionOrGlobalSystemVar(ctx context.Context, name string) (string, error) { - sv := GetSysVar(name) - if sv == nil { - return "", ErrUnknownSystemVar.GenWithStackByArgs(name) - } - if sv.HasNoneScope() { - return sv.Value, nil - } - if sv.HasSessionScope() { - // Populate the value to s.systems if it is not there already. - // in future should be already loaded on session init - if sv.GetSession != nil { - // shortcut to the getter, we won't use the value - return sv.GetSessionFromHook(s) - } - if _, ok := s.systems[sv.Name]; !ok { - if sv.HasGlobalScope() { - if val, err := s.GlobalVarsAccessor.GetGlobalSysVar(sv.Name); err == nil { - s.systems[sv.Name] = val - } - } else { - s.systems[sv.Name] = sv.Value // no global scope, use default - } - } - return sv.GetSessionFromHook(s) - } - return sv.GetGlobalFromHook(ctx, s) -} - -// GetSessionStatesSystemVar gets the session variable value for session states. -// It's only used for encoding session states when migrating a session. -// The returned boolean indicates whether to keep this value in the session states. -func (s *SessionVars) GetSessionStatesSystemVar(name string) (string, bool, error) { - sv := GetSysVar(name) - if sv == nil { - return "", false, ErrUnknownSystemVar.GenWithStackByArgs(name) - } - // Call GetStateValue first if it exists. Otherwise, call GetSession. - if sv.GetStateValue != nil { - return sv.GetStateValue(s) - } - if sv.GetSession != nil { - val, err := sv.GetSessionFromHook(s) - return val, err == nil, err - } - // Only get the cached value. No need to check the global or default value. - if val, ok := s.systems[sv.Name]; ok { - return val, true, nil - } - return "", false, nil -} - -// GetGlobalSystemVar gets a global system variable. -func (s *SessionVars) GetGlobalSystemVar(ctx context.Context, name string) (string, error) { - sv := GetSysVar(name) - if sv == nil { - return "", ErrUnknownSystemVar.GenWithStackByArgs(name) - } - return sv.GetGlobalFromHook(ctx, s) -} - -// SetSystemVar sets the value of a system variable for session scope. -// Values are automatically normalized (i.e. oN / on / 1 => ON) -// and the validation function is run. To set with less validation, see -// SetSystemVarWithRelaxedValidation. -func (s *SessionVars) SetSystemVar(name string, val string) error { - sv := GetSysVar(name) - if sv == nil { - return ErrUnknownSystemVar.GenWithStackByArgs(name) - } - val, err := sv.Validate(s, val, ScopeSession) - if err != nil { - return err - } - return sv.SetSessionFromHook(s, val) -} - -// SetSystemVarWithOldValAsRet is wrapper of SetSystemVar. Return the old value for later use. -func (s *SessionVars) SetSystemVarWithOldValAsRet(name string, val string) (string, error) { - sv := GetSysVar(name) - if sv == nil { - return "", ErrUnknownSystemVar.GenWithStackByArgs(name) - } - val, err := sv.Validate(s, val, ScopeSession) - if err != nil { - return "", err - } - // The map s.systems[sv.Name] is lazy initialized. If we directly read it, we might read empty result. - // Since this code path is not a hot path, we directly call GetSessionOrGlobalSystemVar to get the value safely. - oldV, err := s.GetSessionOrGlobalSystemVar(context.Background(), sv.Name) - if err != nil { - return "", err - } - return oldV, sv.SetSessionFromHook(s, val) -} - -// SetSystemVarWithoutValidation sets the value of a system variable for session scope. -// Deprecated: Values are NOT normalized or Validated. -func (s *SessionVars) SetSystemVarWithoutValidation(name string, val string) error { - sv := GetSysVar(name) - if sv == nil { - return ErrUnknownSystemVar.GenWithStackByArgs(name) - } - return sv.SetSessionFromHook(s, val) -} - -// SetSystemVarWithRelaxedValidation sets the value of a system variable for session scope. -// Validation functions are called, but scope validation is skipped. -// Errors are not expected to be returned because this could cause upgrade issues. -func (s *SessionVars) SetSystemVarWithRelaxedValidation(name string, val string) error { - sv := GetSysVar(name) - if sv == nil { - return ErrUnknownSystemVar.GenWithStackByArgs(name) - } - val = sv.ValidateWithRelaxedValidation(s, val, ScopeSession) - return sv.SetSessionFromHook(s, val) -} - -// GetReadableTxnMode returns the session variable TxnMode but rewrites it to "OPTIMISTIC" when it's empty. -func (s *SessionVars) GetReadableTxnMode() string { - txnMode := s.TxnMode - if txnMode == "" { - txnMode = ast.Optimistic - } - return txnMode -} - -// SetPrevStmtDigest sets the digest of the previous statement. -func (s *SessionVars) SetPrevStmtDigest(prevStmtDigest string) { - s.prevStmtDigest = prevStmtDigest -} - -// GetPrevStmtDigest returns the digest of the previous statement. -func (s *SessionVars) GetPrevStmtDigest() string { - // Because `prevStmt` may be truncated, so it's senseless to normalize it. - // Even if `prevStmtDigest` is empty but `prevStmt` is not, just return it anyway. - return s.prevStmtDigest -} - -// LazyCheckKeyNotExists returns if we can lazy check key not exists. -func (s *SessionVars) LazyCheckKeyNotExists() bool { - return s.PresumeKeyNotExists || (s.TxnCtx != nil && s.TxnCtx.IsPessimistic && !s.StmtCtx.DupKeyAsWarning) -} - -// GetTemporaryTable returns a TempTable by tableInfo. -func (s *SessionVars) GetTemporaryTable(tblInfo *model.TableInfo) tableutil.TempTable { - if tblInfo.TempTableType != model.TempTableNone { - if s.TxnCtx.TemporaryTables == nil { - s.TxnCtx.TemporaryTables = make(map[int64]tableutil.TempTable) - } - tempTables := s.TxnCtx.TemporaryTables - tempTable, ok := tempTables[tblInfo.ID] - if !ok { - tempTable = tableutil.TempTableFromMeta(tblInfo) - tempTables[tblInfo.ID] = tempTable - } - return tempTable - } - - return nil -} - -// EncodeSessionStates saves session states into SessionStates. -func (s *SessionVars) EncodeSessionStates(_ context.Context, sessionStates *sessionstates.SessionStates) (err error) { - // Encode user-defined variables. - s.userVars.lock.RLock() - sessionStates.UserVars = make(map[string]*types.Datum, len(s.userVars.values)) - sessionStates.UserVarTypes = make(map[string]*ptypes.FieldType, len(s.userVars.types)) - for name, userVar := range s.userVars.values { - sessionStates.UserVars[name] = userVar.Clone() - } - for name, userVarType := range s.userVars.types { - sessionStates.UserVarTypes[name] = userVarType.Clone() - } - s.userVars.lock.RUnlock() - - // Encode other session contexts. - sessionStates.PreparedStmtID = s.preparedStmtID - sessionStates.Status = s.Status - sessionStates.CurrentDB = s.CurrentDB - sessionStates.LastTxnInfo = s.LastTxnInfo - if s.LastQueryInfo.StartTS != 0 { - sessionStates.LastQueryInfo = &s.LastQueryInfo - } - if s.LastDDLInfo.SeqNum != 0 { - sessionStates.LastDDLInfo = &s.LastDDLInfo - } - sessionStates.LastFoundRows = s.LastFoundRows - sessionStates.SequenceLatestValues = s.SequenceState.GetAllStates() - sessionStates.FoundInPlanCache = s.PrevFoundInPlanCache - sessionStates.FoundInBinding = s.PrevFoundInBinding - sessionStates.ResourceGroupName = s.ResourceGroupName - sessionStates.HypoIndexes = s.HypoIndexes - sessionStates.HypoTiFlashReplicas = s.HypoTiFlashReplicas - - // Encode StatementContext. We encode it here to avoid circle dependency. - sessionStates.LastAffectedRows = s.StmtCtx.PrevAffectedRows - sessionStates.LastInsertID = s.StmtCtx.PrevLastInsertID - sessionStates.Warnings = s.StmtCtx.GetWarnings() - return -} - -// DecodeSessionStates restores session states from SessionStates. -func (s *SessionVars) DecodeSessionStates(_ context.Context, sessionStates *sessionstates.SessionStates) (err error) { - // Decode user-defined variables. - for name, userVar := range sessionStates.UserVars { - s.SetUserVarVal(name, *userVar.Clone()) - } - for name, userVarType := range sessionStates.UserVarTypes { - s.SetUserVarType(name, userVarType.Clone()) - } - - // Decode other session contexts. - s.preparedStmtID = sessionStates.PreparedStmtID - s.Status = sessionStates.Status - s.CurrentDB = sessionStates.CurrentDB - s.LastTxnInfo = sessionStates.LastTxnInfo - if sessionStates.LastQueryInfo != nil { - s.LastQueryInfo = *sessionStates.LastQueryInfo - } - if sessionStates.LastDDLInfo != nil { - s.LastDDLInfo = *sessionStates.LastDDLInfo - } - s.LastFoundRows = sessionStates.LastFoundRows - s.SequenceState.SetAllStates(sessionStates.SequenceLatestValues) - s.FoundInPlanCache = sessionStates.FoundInPlanCache - s.FoundInBinding = sessionStates.FoundInBinding - s.ResourceGroupName = sessionStates.ResourceGroupName - s.HypoIndexes = sessionStates.HypoIndexes - s.HypoTiFlashReplicas = sessionStates.HypoTiFlashReplicas - - // Decode StatementContext. - s.StmtCtx.SetAffectedRows(uint64(sessionStates.LastAffectedRows)) - s.StmtCtx.PrevLastInsertID = sessionStates.LastInsertID - s.StmtCtx.SetWarnings(sessionStates.Warnings) - return -} - -// TableDelta stands for the changed count for one table or partition. -type TableDelta struct { - Delta int64 - Count int64 - ColSize map[int64]int64 - InitTime time.Time // InitTime is the time that this delta is generated. - TableID int64 -} - -// Clone returns a cloned TableDelta. -func (td TableDelta) Clone() TableDelta { - colSize := make(map[int64]int64, len(td.ColSize)) - maps.Copy(colSize, td.ColSize) - return TableDelta{ - Delta: td.Delta, - Count: td.Count, - ColSize: colSize, - InitTime: td.InitTime, - TableID: td.TableID, - } -} - -// ConcurrencyUnset means the value the of the concurrency related variable is unset. -const ConcurrencyUnset = -1 - -// Concurrency defines concurrency values. -type Concurrency struct { - // indexLookupConcurrency is the number of concurrent index lookup worker. - // indexLookupConcurrency is deprecated, use ExecutorConcurrency instead. - indexLookupConcurrency int - - // indexLookupJoinConcurrency is the number of concurrent index lookup join inner worker. - // indexLookupJoinConcurrency is deprecated, use ExecutorConcurrency instead. - indexLookupJoinConcurrency int - - // distSQLScanConcurrency is the number of concurrent dist SQL scan worker. - distSQLScanConcurrency int - - // hashJoinConcurrency is the number of concurrent hash join outer worker. - // hashJoinConcurrency is deprecated, use ExecutorConcurrency instead. - hashJoinConcurrency int - - // projectionConcurrency is the number of concurrent projection worker. - // projectionConcurrency is deprecated, use ExecutorConcurrency instead. - projectionConcurrency int - - // hashAggPartialConcurrency is the number of concurrent hash aggregation partial worker. - // hashAggPartialConcurrency is deprecated, use ExecutorConcurrency instead. - hashAggPartialConcurrency int - - // hashAggFinalConcurrency is the number of concurrent hash aggregation final worker. - // hashAggFinalConcurrency is deprecated, use ExecutorConcurrency instead. - hashAggFinalConcurrency int - - // windowConcurrency is the number of concurrent window worker. - // windowConcurrency is deprecated, use ExecutorConcurrency instead. - windowConcurrency int - - // mergeJoinConcurrency is the number of concurrent merge join worker - mergeJoinConcurrency int - - // streamAggConcurrency is the number of concurrent stream aggregation worker. - // streamAggConcurrency is deprecated, use ExecutorConcurrency instead. - streamAggConcurrency int - - // indexMergeIntersectionConcurrency is the number of indexMergeProcessWorker - // Only meaningful for dynamic pruned partition table. - indexMergeIntersectionConcurrency int - - // indexSerialScanConcurrency is the number of concurrent index serial scan worker. - indexSerialScanConcurrency int - - // ExecutorConcurrency is the number of concurrent worker for all executors. - ExecutorConcurrency int - - // SourceAddr is the source address of request. Available in coprocessor ONLY. - SourceAddr net.TCPAddr -} - -// SetIndexLookupConcurrency set the number of concurrent index lookup worker. -func (c *Concurrency) SetIndexLookupConcurrency(n int) { - c.indexLookupConcurrency = n -} - -// SetIndexLookupJoinConcurrency set the number of concurrent index lookup join inner worker. -func (c *Concurrency) SetIndexLookupJoinConcurrency(n int) { - c.indexLookupJoinConcurrency = n -} - -// SetDistSQLScanConcurrency set the number of concurrent dist SQL scan worker. -func (c *Concurrency) SetDistSQLScanConcurrency(n int) { - c.distSQLScanConcurrency = n -} - -// SetHashJoinConcurrency set the number of concurrent hash join outer worker. -func (c *Concurrency) SetHashJoinConcurrency(n int) { - c.hashJoinConcurrency = n -} - -// SetProjectionConcurrency set the number of concurrent projection worker. -func (c *Concurrency) SetProjectionConcurrency(n int) { - c.projectionConcurrency = n -} - -// SetHashAggPartialConcurrency set the number of concurrent hash aggregation partial worker. -func (c *Concurrency) SetHashAggPartialConcurrency(n int) { - c.hashAggPartialConcurrency = n -} - -// SetHashAggFinalConcurrency set the number of concurrent hash aggregation final worker. -func (c *Concurrency) SetHashAggFinalConcurrency(n int) { - c.hashAggFinalConcurrency = n -} - -// SetWindowConcurrency set the number of concurrent window worker. -func (c *Concurrency) SetWindowConcurrency(n int) { - c.windowConcurrency = n -} - -// SetMergeJoinConcurrency set the number of concurrent merge join worker. -func (c *Concurrency) SetMergeJoinConcurrency(n int) { - c.mergeJoinConcurrency = n -} - -// SetStreamAggConcurrency set the number of concurrent stream aggregation worker. -func (c *Concurrency) SetStreamAggConcurrency(n int) { - c.streamAggConcurrency = n -} - -// SetIndexMergeIntersectionConcurrency set the number of concurrent intersection process worker. -func (c *Concurrency) SetIndexMergeIntersectionConcurrency(n int) { - c.indexMergeIntersectionConcurrency = n -} - -// SetIndexSerialScanConcurrency set the number of concurrent index serial scan worker. -func (c *Concurrency) SetIndexSerialScanConcurrency(n int) { - c.indexSerialScanConcurrency = n -} - -// IndexLookupConcurrency return the number of concurrent index lookup worker. -func (c *Concurrency) IndexLookupConcurrency() int { - if c.indexLookupConcurrency != ConcurrencyUnset { - return c.indexLookupConcurrency - } - return c.ExecutorConcurrency -} - -// IndexLookupJoinConcurrency return the number of concurrent index lookup join inner worker. -func (c *Concurrency) IndexLookupJoinConcurrency() int { - if c.indexLookupJoinConcurrency != ConcurrencyUnset { - return c.indexLookupJoinConcurrency - } - return c.ExecutorConcurrency -} - -// DistSQLScanConcurrency return the number of concurrent dist SQL scan worker. -func (c *Concurrency) DistSQLScanConcurrency() int { - return c.distSQLScanConcurrency -} - -// HashJoinConcurrency return the number of concurrent hash join outer worker. -func (c *Concurrency) HashJoinConcurrency() int { - if c.hashJoinConcurrency != ConcurrencyUnset { - return c.hashJoinConcurrency - } - return c.ExecutorConcurrency -} - -// ProjectionConcurrency return the number of concurrent projection worker. -func (c *Concurrency) ProjectionConcurrency() int { - if c.projectionConcurrency != ConcurrencyUnset { - return c.projectionConcurrency - } - return c.ExecutorConcurrency -} - -// HashAggPartialConcurrency return the number of concurrent hash aggregation partial worker. -func (c *Concurrency) HashAggPartialConcurrency() int { - if c.hashAggPartialConcurrency != ConcurrencyUnset { - return c.hashAggPartialConcurrency - } - return c.ExecutorConcurrency -} - -// HashAggFinalConcurrency return the number of concurrent hash aggregation final worker. -func (c *Concurrency) HashAggFinalConcurrency() int { - if c.hashAggFinalConcurrency != ConcurrencyUnset { - return c.hashAggFinalConcurrency - } - return c.ExecutorConcurrency -} - -// WindowConcurrency return the number of concurrent window worker. -func (c *Concurrency) WindowConcurrency() int { - if c.windowConcurrency != ConcurrencyUnset { - return c.windowConcurrency - } - return c.ExecutorConcurrency -} - -// MergeJoinConcurrency return the number of concurrent merge join worker. -func (c *Concurrency) MergeJoinConcurrency() int { - if c.mergeJoinConcurrency != ConcurrencyUnset { - return c.mergeJoinConcurrency - } - return c.ExecutorConcurrency -} - -// StreamAggConcurrency return the number of concurrent stream aggregation worker. -func (c *Concurrency) StreamAggConcurrency() int { - if c.streamAggConcurrency != ConcurrencyUnset { - return c.streamAggConcurrency - } - return c.ExecutorConcurrency -} - -// IndexMergeIntersectionConcurrency return the number of concurrent process worker. -func (c *Concurrency) IndexMergeIntersectionConcurrency() int { - if c.indexMergeIntersectionConcurrency != ConcurrencyUnset { - return c.indexMergeIntersectionConcurrency - } - return c.ExecutorConcurrency -} - -// IndexSerialScanConcurrency return the number of concurrent index serial scan worker. -// This option is not sync with ExecutorConcurrency since it's used by Analyze table. -func (c *Concurrency) IndexSerialScanConcurrency() int { - return c.indexSerialScanConcurrency -} - -// UnionConcurrency return the num of concurrent union worker. -func (c *Concurrency) UnionConcurrency() int { - return c.ExecutorConcurrency -} - -// MemQuota defines memory quota values. -type MemQuota struct { - // MemQuotaQuery defines the memory quota for a query. - MemQuotaQuery int64 - // MemQuotaApplyCache defines the memory capacity for apply cache. - MemQuotaApplyCache int64 -} - -// BatchSize defines batch size values. -type BatchSize struct { - // IndexJoinBatchSize is the batch size of a index lookup join. - IndexJoinBatchSize int - - // IndexLookupSize is the number of handles for an index lookup task in index double read executor. - IndexLookupSize int - - // InitChunkSize defines init row count of a Chunk during query execution. - InitChunkSize int - - // MaxChunkSize defines max row count of a Chunk during query execution. - MaxChunkSize int - - // MinPagingSize defines the min size used by the coprocessor paging protocol. - MinPagingSize int - - // MinPagingSize defines the max size used by the coprocessor paging protocol. - MaxPagingSize int -} - -const ( - // SlowLogRowPrefixStr is slow log row prefix. - SlowLogRowPrefixStr = "# " - // SlowLogSpaceMarkStr is slow log space mark. - SlowLogSpaceMarkStr = ": " - // SlowLogSQLSuffixStr is slow log suffix. - SlowLogSQLSuffixStr = ";" - // SlowLogTimeStr is slow log field name. - SlowLogTimeStr = "Time" - // SlowLogStartPrefixStr is slow log start row prefix. - SlowLogStartPrefixStr = SlowLogRowPrefixStr + SlowLogTimeStr + SlowLogSpaceMarkStr - // SlowLogTxnStartTSStr is slow log field name. - SlowLogTxnStartTSStr = "Txn_start_ts" - // SlowLogKeyspaceName is slow log field name. - SlowLogKeyspaceName = "Keyspace_name" - // SlowLogKeyspaceID is slow log field name. - SlowLogKeyspaceID = "Keyspace_ID" - // SlowLogUserAndHostStr is the user and host field name, which is compatible with MySQL. - SlowLogUserAndHostStr = "User@Host" - // SlowLogUserStr is slow log field name. - SlowLogUserStr = "User" - // SlowLogHostStr only for slow_query table usage. - SlowLogHostStr = "Host" - // SlowLogConnIDStr is slow log field name. - SlowLogConnIDStr = "Conn_ID" - // SlowLogSessAliasStr is the session alias set by user - SlowLogSessAliasStr = "Session_alias" - // SlowLogQueryTimeStr is slow log field name. - SlowLogQueryTimeStr = "Query_time" - // SlowLogParseTimeStr is the parse sql time. - SlowLogParseTimeStr = "Parse_time" - // SlowLogCompileTimeStr is the compile plan time. - SlowLogCompileTimeStr = "Compile_time" - // SlowLogRewriteTimeStr is the rewrite time. - SlowLogRewriteTimeStr = "Rewrite_time" - // SlowLogOptimizeTimeStr is the optimization time. - SlowLogOptimizeTimeStr = "Optimize_time" - // SlowLogWaitTSTimeStr is the time of waiting TS. - SlowLogWaitTSTimeStr = "Wait_TS" - // SlowLogPreprocSubQueriesStr is the number of pre-processed sub-queries. - SlowLogPreprocSubQueriesStr = "Preproc_subqueries" - // SlowLogPreProcSubQueryTimeStr is the total time of pre-processing sub-queries. - SlowLogPreProcSubQueryTimeStr = "Preproc_subqueries_time" - // SlowLogDBStr is slow log field name. - SlowLogDBStr = "DB" - // SlowLogIsInternalStr is slow log field name. - SlowLogIsInternalStr = "Is_internal" - // SlowLogIndexNamesStr is slow log field name. - SlowLogIndexNamesStr = "Index_names" - // SlowLogDigestStr is slow log field name. - SlowLogDigestStr = "Digest" - // SlowLogQuerySQLStr is slow log field name. - SlowLogQuerySQLStr = "Query" // use for slow log table, slow log will not print this field name but print sql directly. - // SlowLogStatsInfoStr is plan stats info. - SlowLogStatsInfoStr = "Stats" - // SlowLogNumCopTasksStr is the number of cop-tasks. - SlowLogNumCopTasksStr = "Num_cop_tasks" - // SlowLogCopProcAvg is the average process time of all cop-tasks. - SlowLogCopProcAvg = "Cop_proc_avg" - // SlowLogCopProcP90 is the p90 process time of all cop-tasks. - SlowLogCopProcP90 = "Cop_proc_p90" - // SlowLogCopProcMax is the max process time of all cop-tasks. - SlowLogCopProcMax = "Cop_proc_max" - // SlowLogCopProcAddr is the address of TiKV where the cop-task which cost max process time run. - SlowLogCopProcAddr = "Cop_proc_addr" - // SlowLogCopWaitAvg is the average wait time of all cop-tasks. - SlowLogCopWaitAvg = "Cop_wait_avg" // #nosec G101 - // SlowLogCopWaitP90 is the p90 wait time of all cop-tasks. - SlowLogCopWaitP90 = "Cop_wait_p90" // #nosec G101 - // SlowLogCopWaitMax is the max wait time of all cop-tasks. - SlowLogCopWaitMax = "Cop_wait_max" - // SlowLogCopWaitAddr is the address of TiKV where the cop-task which cost wait process time run. - SlowLogCopWaitAddr = "Cop_wait_addr" // #nosec G101 - // SlowLogCopBackoffPrefix contains backoff information. - SlowLogCopBackoffPrefix = "Cop_backoff_" - // SlowLogMemMax is the max number bytes of memory used in this statement. - SlowLogMemMax = "Mem_max" - // SlowLogDiskMax is the nax number bytes of disk used in this statement. - SlowLogDiskMax = "Disk_max" - // SlowLogPrepared is used to indicate whether this sql execute in prepare. - SlowLogPrepared = "Prepared" - // SlowLogPlanFromCache is used to indicate whether this plan is from plan cache. - SlowLogPlanFromCache = "Plan_from_cache" - // SlowLogPlanFromBinding is used to indicate whether this plan is matched with the hints in the binding. - SlowLogPlanFromBinding = "Plan_from_binding" - // SlowLogHasMoreResults is used to indicate whether this sql has more following results. - SlowLogHasMoreResults = "Has_more_results" - // SlowLogSucc is used to indicate whether this sql execute successfully. - SlowLogSucc = "Succ" - // SlowLogPrevStmt is used to show the previous executed statement. - SlowLogPrevStmt = "Prev_stmt" - // SlowLogPlan is used to record the query plan. - SlowLogPlan = "Plan" - // SlowLogPlanDigest is used to record the query plan digest. - SlowLogPlanDigest = "Plan_digest" - // SlowLogBinaryPlan is used to record the binary plan. - SlowLogBinaryPlan = "Binary_plan" - // SlowLogPlanPrefix is the prefix of the plan value. - SlowLogPlanPrefix = ast.TiDBDecodePlan + "('" - // SlowLogBinaryPlanPrefix is the prefix of the binary plan value. - SlowLogBinaryPlanPrefix = ast.TiDBDecodeBinaryPlan + "('" - // SlowLogPlanSuffix is the suffix of the plan value. - SlowLogPlanSuffix = "')" - // SlowLogPrevStmtPrefix is the prefix of Prev_stmt in slow log file. - SlowLogPrevStmtPrefix = SlowLogPrevStmt + SlowLogSpaceMarkStr - // SlowLogKVTotal is the total time waiting for kv. - SlowLogKVTotal = "KV_total" - // SlowLogPDTotal is the total time waiting for pd. - SlowLogPDTotal = "PD_total" - // SlowLogBackoffTotal is the total time doing backoff. - SlowLogBackoffTotal = "Backoff_total" - // SlowLogWriteSQLRespTotal is the total time used to write response to client. - SlowLogWriteSQLRespTotal = "Write_sql_response_total" - // SlowLogExecRetryCount is the execution retry count. - SlowLogExecRetryCount = "Exec_retry_count" - // SlowLogExecRetryTime is the execution retry time. - SlowLogExecRetryTime = "Exec_retry_time" - // SlowLogBackoffDetail is the detail of backoff. - SlowLogBackoffDetail = "Backoff_Detail" - // SlowLogResultRows is the row count of the SQL result. - SlowLogResultRows = "Result_rows" - // SlowLogWarnings is the warnings generated during executing the statement. - // Note that some extra warnings would also be printed through slow log. - SlowLogWarnings = "Warnings" - // SlowLogIsExplicitTxn is used to indicate whether this sql execute in explicit transaction or not. - SlowLogIsExplicitTxn = "IsExplicitTxn" - // SlowLogIsWriteCacheTable is used to indicate whether writing to the cache table need to wait for the read lock to expire. - SlowLogIsWriteCacheTable = "IsWriteCacheTable" - // SlowLogIsSyncStatsFailed is used to indicate whether any failure happen during sync stats - SlowLogIsSyncStatsFailed = "IsSyncStatsFailed" -) - -// GenerateBinaryPlan decides whether we should record binary plan in slow log and stmt summary. -// It's controlled by the global variable `tidb_generate_binary_plan`. -var GenerateBinaryPlan atomic2.Bool - -// JSONSQLWarnForSlowLog helps to print the SQLWarn through the slow log in JSON format. -type JSONSQLWarnForSlowLog struct { - Level string - Message string - // IsExtra means this SQL Warn is expected to be recorded only under some conditions (like in EXPLAIN) and should - // haven't been recorded as a warning now, but we recorded it anyway to help diagnostics. - IsExtra bool `json:",omitempty"` -} - -// SlowQueryLogItems is a collection of items that should be included in the -// slow query log. -type SlowQueryLogItems struct { - TxnTS uint64 - KeyspaceName string - KeyspaceID uint32 - SQL string - Digest string - TimeTotal time.Duration - TimeParse time.Duration - TimeCompile time.Duration - TimeOptimize time.Duration - TimeWaitTS time.Duration - IndexNames string - CopTasks *stmtctx.CopTasksDetails - ExecDetail execdetails.ExecDetails - MemMax int64 - DiskMax int64 - Succ bool - Prepared bool - PlanFromCache bool - PlanFromBinding bool - HasMoreResults bool - PrevStmt string - Plan string - PlanDigest string - BinaryPlan string - RewriteInfo RewritePhaseInfo - KVTotal time.Duration - PDTotal time.Duration - BackoffTotal time.Duration - WriteSQLRespTotal time.Duration - ExecRetryCount uint - ExecRetryTime time.Duration - ResultRows int64 - IsExplicitTxn bool - IsWriteCacheTable bool - UsedStats map[int64]*stmtctx.UsedStatsInfoForTable - IsSyncStatsFailed bool - Warnings []JSONSQLWarnForSlowLog -} - -// SlowLogFormat uses for formatting slow log. -// The slow log output is like below: -// # Time: 2019-04-28T15:24:04.309074+08:00 -// # Txn_start_ts: 406315658548871171 -// # Keyspace_name: keyspace_a -// # Keyspace_ID: 1 -// # User@Host: root[root] @ localhost [127.0.0.1] -// # Conn_ID: 6 -// # Query_time: 4.895492 -// # Process_time: 0.161 Request_count: 1 Total_keys: 100001 Processed_keys: 100000 -// # DB: test -// # Index_names: [t1.idx1,t2.idx2] -// # Is_internal: false -// # Digest: 42a1c8aae6f133e934d4bf0147491709a8812ea05ff8819ec522780fe657b772 -// # Stats: t1:1,t2:2 -// # Num_cop_tasks: 10 -// # Cop_process: Avg_time: 1s P90_time: 2s Max_time: 3s Max_addr: 10.6.131.78 -// # Cop_wait: Avg_time: 10ms P90_time: 20ms Max_time: 30ms Max_Addr: 10.6.131.79 -// # Memory_max: 4096 -// # Disk_max: 65535 -// # Succ: true -// # Prev_stmt: begin; -// select * from t_slim; -func (s *SessionVars) SlowLogFormat(logItems *SlowQueryLogItems) string { - var buf bytes.Buffer - - writeSlowLogItem(&buf, SlowLogTxnStartTSStr, strconv.FormatUint(logItems.TxnTS, 10)) - if logItems.KeyspaceName != "" { - writeSlowLogItem(&buf, SlowLogKeyspaceName, logItems.KeyspaceName) - writeSlowLogItem(&buf, SlowLogKeyspaceID, fmt.Sprintf("%d", logItems.KeyspaceID)) - } - - if s.User != nil { - hostAddress := s.User.Hostname - if s.ConnectionInfo != nil { - hostAddress = s.ConnectionInfo.ClientIP - } - writeSlowLogItem(&buf, SlowLogUserAndHostStr, fmt.Sprintf("%s[%s] @ %s [%s]", s.User.Username, s.User.Username, s.User.Hostname, hostAddress)) - } - if s.ConnectionID != 0 { - writeSlowLogItem(&buf, SlowLogConnIDStr, strconv.FormatUint(s.ConnectionID, 10)) - } - if s.SessionAlias != "" { - writeSlowLogItem(&buf, SlowLogSessAliasStr, s.SessionAlias) - } - if logItems.ExecRetryCount > 0 { - buf.WriteString(SlowLogRowPrefixStr) - buf.WriteString(SlowLogExecRetryTime) - buf.WriteString(SlowLogSpaceMarkStr) - buf.WriteString(strconv.FormatFloat(logItems.ExecRetryTime.Seconds(), 'f', -1, 64)) - buf.WriteString(" ") - buf.WriteString(SlowLogExecRetryCount) - buf.WriteString(SlowLogSpaceMarkStr) - buf.WriteString(strconv.Itoa(int(logItems.ExecRetryCount))) - buf.WriteString("\n") - } - writeSlowLogItem(&buf, SlowLogQueryTimeStr, strconv.FormatFloat(logItems.TimeTotal.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogParseTimeStr, strconv.FormatFloat(logItems.TimeParse.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogCompileTimeStr, strconv.FormatFloat(logItems.TimeCompile.Seconds(), 'f', -1, 64)) - - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v", SlowLogRewriteTimeStr, - SlowLogSpaceMarkStr, strconv.FormatFloat(logItems.RewriteInfo.DurationRewrite.Seconds(), 'f', -1, 64))) - if logItems.RewriteInfo.PreprocessSubQueries > 0 { - buf.WriteString(fmt.Sprintf(" %v%v%v %v%v%v", SlowLogPreprocSubQueriesStr, SlowLogSpaceMarkStr, logItems.RewriteInfo.PreprocessSubQueries, - SlowLogPreProcSubQueryTimeStr, SlowLogSpaceMarkStr, strconv.FormatFloat(logItems.RewriteInfo.DurationPreprocessSubQuery.Seconds(), 'f', -1, 64))) - } - buf.WriteString("\n") - - writeSlowLogItem(&buf, SlowLogOptimizeTimeStr, strconv.FormatFloat(logItems.TimeOptimize.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogWaitTSTimeStr, strconv.FormatFloat(logItems.TimeWaitTS.Seconds(), 'f', -1, 64)) - - if execDetailStr := logItems.ExecDetail.String(); len(execDetailStr) > 0 { - buf.WriteString(SlowLogRowPrefixStr + execDetailStr + "\n") - } - - if len(s.CurrentDB) > 0 { - writeSlowLogItem(&buf, SlowLogDBStr, strings.ToLower(s.CurrentDB)) - } - if len(logItems.IndexNames) > 0 { - writeSlowLogItem(&buf, SlowLogIndexNamesStr, logItems.IndexNames) - } - - writeSlowLogItem(&buf, SlowLogIsInternalStr, strconv.FormatBool(s.InRestrictedSQL)) - if len(logItems.Digest) > 0 { - writeSlowLogItem(&buf, SlowLogDigestStr, logItems.Digest) - } - if len(logItems.UsedStats) > 0 { - buf.WriteString(SlowLogRowPrefixStr + SlowLogStatsInfoStr + SlowLogSpaceMarkStr) - firstComma := false - keys := maps.Keys(logItems.UsedStats) - slices.Sort(keys) - for _, id := range keys { - usedStatsForTbl := logItems.UsedStats[id] - if usedStatsForTbl == nil { - continue - } - if firstComma { - buf.WriteString(",") - } - usedStatsForTbl.WriteToSlowLog(&buf) - firstComma = true - } - - buf.WriteString("\n") - } - if logItems.CopTasks != nil { - writeSlowLogItem(&buf, SlowLogNumCopTasksStr, strconv.FormatInt(int64(logItems.CopTasks.NumCopTasks), 10)) - if logItems.CopTasks.NumCopTasks > 0 { - // make the result stable - backoffs := make([]string, 0, 3) - for backoff := range logItems.CopTasks.TotBackoffTimes { - backoffs = append(backoffs, backoff) - } - slices.Sort(backoffs) - - if logItems.CopTasks.NumCopTasks == 1 { - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v", - SlowLogCopProcAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgProcessTime.Seconds(), - SlowLogCopProcAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessAddress) + "\n") - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v", - SlowLogCopWaitAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgWaitTime.Seconds(), - SlowLogCopWaitAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitAddress) + "\n") - for _, backoff := range backoffs { - backoffPrefix := SlowLogCopBackoffPrefix + backoff + "_" - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v\n", - backoffPrefix+"total_times", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTimes[backoff], - backoffPrefix+"total_time", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTime[backoff].Seconds(), - )) - } - } else { - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v", - SlowLogCopProcAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgProcessTime.Seconds(), - SlowLogCopProcP90, SlowLogSpaceMarkStr, logItems.CopTasks.P90ProcessTime.Seconds(), - SlowLogCopProcMax, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessTime.Seconds(), - SlowLogCopProcAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxProcessAddress) + "\n") - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v", - SlowLogCopWaitAvg, SlowLogSpaceMarkStr, logItems.CopTasks.AvgWaitTime.Seconds(), - SlowLogCopWaitP90, SlowLogSpaceMarkStr, logItems.CopTasks.P90WaitTime.Seconds(), - SlowLogCopWaitMax, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitTime.Seconds(), - SlowLogCopWaitAddr, SlowLogSpaceMarkStr, logItems.CopTasks.MaxWaitAddress) + "\n") - for _, backoff := range backoffs { - backoffPrefix := SlowLogCopBackoffPrefix + backoff + "_" - buf.WriteString(SlowLogRowPrefixStr + fmt.Sprintf("%v%v%v %v%v%v %v%v%v %v%v%v %v%v%v %v%v%v\n", - backoffPrefix+"total_times", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTimes[backoff], - backoffPrefix+"total_time", SlowLogSpaceMarkStr, logItems.CopTasks.TotBackoffTime[backoff].Seconds(), - backoffPrefix+"max_time", SlowLogSpaceMarkStr, logItems.CopTasks.MaxBackoffTime[backoff].Seconds(), - backoffPrefix+"max_addr", SlowLogSpaceMarkStr, logItems.CopTasks.MaxBackoffAddress[backoff], - backoffPrefix+"avg_time", SlowLogSpaceMarkStr, logItems.CopTasks.AvgBackoffTime[backoff].Seconds(), - backoffPrefix+"p90_time", SlowLogSpaceMarkStr, logItems.CopTasks.P90BackoffTime[backoff].Seconds(), - )) - } - } - } - } - if logItems.MemMax > 0 { - writeSlowLogItem(&buf, SlowLogMemMax, strconv.FormatInt(logItems.MemMax, 10)) - } - if logItems.DiskMax > 0 { - writeSlowLogItem(&buf, SlowLogDiskMax, strconv.FormatInt(logItems.DiskMax, 10)) - } - - writeSlowLogItem(&buf, SlowLogPrepared, strconv.FormatBool(logItems.Prepared)) - writeSlowLogItem(&buf, SlowLogPlanFromCache, strconv.FormatBool(logItems.PlanFromCache)) - writeSlowLogItem(&buf, SlowLogPlanFromBinding, strconv.FormatBool(logItems.PlanFromBinding)) - writeSlowLogItem(&buf, SlowLogHasMoreResults, strconv.FormatBool(logItems.HasMoreResults)) - writeSlowLogItem(&buf, SlowLogKVTotal, strconv.FormatFloat(logItems.KVTotal.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogPDTotal, strconv.FormatFloat(logItems.PDTotal.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogBackoffTotal, strconv.FormatFloat(logItems.BackoffTotal.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogWriteSQLRespTotal, strconv.FormatFloat(logItems.WriteSQLRespTotal.Seconds(), 'f', -1, 64)) - writeSlowLogItem(&buf, SlowLogResultRows, strconv.FormatInt(logItems.ResultRows, 10)) - if len(logItems.Warnings) > 0 { - buf.WriteString(SlowLogRowPrefixStr + SlowLogWarnings + SlowLogSpaceMarkStr) - jsonEncoder := json.NewEncoder(&buf) - jsonEncoder.SetEscapeHTML(false) - // Note that the Encode() will append a '\n' so we don't need to add another. - err := jsonEncoder.Encode(logItems.Warnings) - if err != nil { - buf.WriteString(err.Error()) - } - } - writeSlowLogItem(&buf, SlowLogSucc, strconv.FormatBool(logItems.Succ)) - writeSlowLogItem(&buf, SlowLogIsExplicitTxn, strconv.FormatBool(logItems.IsExplicitTxn)) - writeSlowLogItem(&buf, SlowLogIsSyncStatsFailed, strconv.FormatBool(logItems.IsSyncStatsFailed)) - if s.StmtCtx.WaitLockLeaseTime > 0 { - writeSlowLogItem(&buf, SlowLogIsWriteCacheTable, strconv.FormatBool(logItems.IsWriteCacheTable)) - } - if len(logItems.Plan) != 0 { - writeSlowLogItem(&buf, SlowLogPlan, logItems.Plan) - } - if len(logItems.PlanDigest) != 0 { - writeSlowLogItem(&buf, SlowLogPlanDigest, logItems.PlanDigest) - } - if len(logItems.BinaryPlan) != 0 { - writeSlowLogItem(&buf, SlowLogBinaryPlan, logItems.BinaryPlan) - } - if logItems.PrevStmt != "" { - writeSlowLogItem(&buf, SlowLogPrevStmt, logItems.PrevStmt) - } - - if s.CurrentDBChanged { - buf.WriteString(fmt.Sprintf("use %s;\n", strings.ToLower(s.CurrentDB))) - s.CurrentDBChanged = false - } - - buf.WriteString(logItems.SQL) - if len(logItems.SQL) == 0 || logItems.SQL[len(logItems.SQL)-1] != ';' { - buf.WriteString(";") - } - - return buf.String() -} - -// writeSlowLogItem writes a slow log item in the form of: "# ${key}:${value}" -func writeSlowLogItem(buf *bytes.Buffer, key, value string) { - buf.WriteString(SlowLogRowPrefixStr + key + SlowLogSpaceMarkStr + value + "\n") -} - -// TxnReadTS indicates the value and used situation for tx_read_ts -type TxnReadTS struct { - readTS uint64 - used bool -} - -// NewTxnReadTS creates TxnReadTS -func NewTxnReadTS(ts uint64) *TxnReadTS { - return &TxnReadTS{ - readTS: ts, - used: false, - } -} - -// UseTxnReadTS returns readTS, and mark used as true -func (t *TxnReadTS) UseTxnReadTS() uint64 { - if t == nil { - return 0 - } - t.used = true - return t.readTS -} - -// SetTxnReadTS update readTS, and refresh used -func (t *TxnReadTS) SetTxnReadTS(ts uint64) { - if t == nil { - return - } - t.used = false - t.readTS = ts -} - -// PeakTxnReadTS returns readTS -func (t *TxnReadTS) PeakTxnReadTS() uint64 { - if t == nil { - return 0 - } - return t.readTS -} - -// CleanupTxnReadTSIfUsed cleans txnReadTS if used -func (s *SessionVars) CleanupTxnReadTSIfUsed() { - if s.TxnReadTS == nil { - return - } - if s.TxnReadTS.used && s.TxnReadTS.readTS > 0 { - s.TxnReadTS = NewTxnReadTS(0) - s.SnapshotInfoschema = nil - } -} - -// GetCPUFactor returns the session variable cpuFactor -func (s *SessionVars) GetCPUFactor() float64 { - return s.cpuFactor -} - -// GetCopCPUFactor returns the session variable copCPUFactor -func (s *SessionVars) GetCopCPUFactor() float64 { - return s.copCPUFactor -} - -// GetMemoryFactor returns the session variable memoryFactor -func (s *SessionVars) GetMemoryFactor() float64 { - return s.memoryFactor -} - -// GetDiskFactor returns the session variable diskFactor -func (s *SessionVars) GetDiskFactor() float64 { - return s.diskFactor -} - -// GetConcurrencyFactor returns the session variable concurrencyFactor -func (s *SessionVars) GetConcurrencyFactor() float64 { - return s.concurrencyFactor -} - -// GetNetworkFactor returns the session variable networkFactor -// returns 0 when tbl is a temporary table. -func (s *SessionVars) GetNetworkFactor(tbl *model.TableInfo) float64 { - if tbl != nil { - if tbl.TempTableType != model.TempTableNone { - return 0 - } - } - return s.networkFactor -} - -// GetScanFactor returns the session variable scanFactor -// returns 0 when tbl is a temporary table. -func (s *SessionVars) GetScanFactor(tbl *model.TableInfo) float64 { - if tbl != nil { - if tbl.TempTableType != model.TempTableNone { - return 0 - } - } - return s.scanFactor -} - -// GetDescScanFactor returns the session variable descScanFactor -// returns 0 when tbl is a temporary table. -func (s *SessionVars) GetDescScanFactor(tbl *model.TableInfo) float64 { - if tbl != nil { - if tbl.TempTableType != model.TempTableNone { - return 0 - } - } - return s.descScanFactor -} - -// GetSeekFactor returns the session variable seekFactor -// returns 0 when tbl is a temporary table. -func (s *SessionVars) GetSeekFactor(tbl *model.TableInfo) float64 { - if tbl != nil { - if tbl.TempTableType != model.TempTableNone { - return 0 - } - } - return s.seekFactor -} - -// EnableEvalTopNEstimationForStrMatch means if we need to evaluate expression with TopN to improve estimation. -// Currently, it's only for string matching functions (like and regexp). -func (s *SessionVars) EnableEvalTopNEstimationForStrMatch() bool { - return s.DefaultStrMatchSelectivity == 0 -} - -// GetStrMatchDefaultSelectivity means the default selectivity for like and regexp. -// Note: 0 is a special value, which means the default selectivity is 0.1 and TopN assisted estimation is enabled. -func (s *SessionVars) GetStrMatchDefaultSelectivity() float64 { - if s.DefaultStrMatchSelectivity == 0 { - return 0.1 - } - return s.DefaultStrMatchSelectivity -} - -// GetNegateStrMatchDefaultSelectivity means the default selectivity for not like and not regexp. -// Note: -// -// 0 is a special value, which means the default selectivity is 0.9 and TopN assisted estimation is enabled. -// 0.8 (the default value) is also a special value. For backward compatibility, when the variable is set to 0.8, we -// keep the default selectivity of like/regexp and not like/regexp all 0.8. -func (s *SessionVars) GetNegateStrMatchDefaultSelectivity() float64 { - if s.DefaultStrMatchSelectivity == DefTiDBDefaultStrMatchSelectivity { - return DefTiDBDefaultStrMatchSelectivity - } - return 1 - s.GetStrMatchDefaultSelectivity() -} - -// GetRelatedTableForMDL gets the related table for metadata lock. -func (s *SessionVars) GetRelatedTableForMDL() *sync.Map { - s.TxnCtx.tdmLock.Lock() - defer s.TxnCtx.tdmLock.Unlock() - if s.TxnCtx.relatedTableForMDL == nil { - s.TxnCtx.relatedTableForMDL = new(sync.Map) - } - return s.TxnCtx.relatedTableForMDL -} - -// EnableForceInlineCTE returns the session variable enableForceInlineCTE -func (s *SessionVars) EnableForceInlineCTE() bool { - return s.enableForceInlineCTE -} - -// IsRuntimeFilterEnabled return runtime filter mode whether OFF -func (s *SessionVars) IsRuntimeFilterEnabled() bool { - return s.runtimeFilterMode != RFOff -} - -// GetRuntimeFilterTypes return the session variable runtimeFilterTypes -func (s *SessionVars) GetRuntimeFilterTypes() []RuntimeFilterType { - return s.runtimeFilterTypes -} - -// GetRuntimeFilterMode return the session variable runtimeFilterMode -func (s *SessionVars) GetRuntimeFilterMode() RuntimeFilterMode { - return s.runtimeFilterMode -} - -// GetTiKVClientReadTimeout returns readonly kv request timeout, prefer query hint over session variable -func (s *SessionVars) GetTiKVClientReadTimeout() uint64 { - return s.TiKVClientReadTimeout -} - -// RuntimeFilterType type of runtime filter "IN" -type RuntimeFilterType int64 - -// In type of runtime filter, like "t.k1 in (?)" -// MinMax type of runtime filter, like "t.k1 < ? and t.k1 > ?" -const ( - In RuntimeFilterType = iota - MinMax - // todo BloomFilter, bf/in -) - -// String convert Runtime Filter Type to String name -func (rfType RuntimeFilterType) String() string { - switch rfType { - case In: - return "IN" - case MinMax: - return "MIN_MAX" - default: - return "" - } -} - -// RuntimeFilterTypeStringToType convert RuntimeFilterTypeNameString to RuntimeFilterType -// If name is legal, it will return Runtime Filter Type and true -// Else, it will return -1 and false -// The second param means the convert is ok or not. Ture is ok, false means it is illegal name -// At present, we only support two names: "IN" and "MIN_MAX" -func RuntimeFilterTypeStringToType(name string) (RuntimeFilterType, bool) { - switch name { - case "IN": - return In, true - case "MIN_MAX": - return MinMax, true - default: - return -1, false - } -} - -// ToRuntimeFilterType convert session var value to RuntimeFilterType list -// If sessionVarValue is legal, it will return RuntimeFilterType list and true -// The second param means the convert is ok or not. Ture is ok, false means it is illegal value -// The legal value should be comma-separated, eg: "IN,MIN_MAX" -func ToRuntimeFilterType(sessionVarValue string) ([]RuntimeFilterType, bool) { - typeNameList := strings.Split(sessionVarValue, ",") - rfTypeMap := make(map[RuntimeFilterType]bool) - for _, typeName := range typeNameList { - rfType, ok := RuntimeFilterTypeStringToType(strings.ToUpper(typeName)) - if !ok { - return nil, ok - } - rfTypeMap[rfType] = true - } - rfTypeList := make([]RuntimeFilterType, 0, len(rfTypeMap)) - for rfType := range rfTypeMap { - rfTypeList = append(rfTypeList, rfType) - } - return rfTypeList, true -} - -// RuntimeFilterMode the mode of runtime filter "OFF", "LOCAL" -type RuntimeFilterMode int64 - -// RFOff disable runtime filter -// RFLocal enable local runtime filter -// RFGlobal enable local and global runtime filter -const ( - RFOff RuntimeFilterMode = iota + 1 - RFLocal - RFGlobal -) - -// String convert Runtime Filter Mode to String name -func (rfMode RuntimeFilterMode) String() string { - switch rfMode { - case RFOff: - return "OFF" - case RFLocal: - return "LOCAL" - case RFGlobal: - return "GLOBAL" - default: - return "" - } -} - -// RuntimeFilterModeStringToMode convert RuntimeFilterModeString to RuntimeFilterMode -// If name is legal, it will return Runtime Filter Mode and true -// Else, it will return -1 and false -// The second param means the convert is ok or not. Ture is ok, false means it is illegal name -// At present, we only support one name: "OFF", "LOCAL" -func RuntimeFilterModeStringToMode(name string) (RuntimeFilterMode, bool) { - switch name { - case "OFF": - return RFOff, true - case "LOCAL": - return RFLocal, true - default: - return -1, false - } -} - -const ( - // OptObjectiveModerate is a possible value and the default value for TiDBOptObjective. - // Please see comments of SessionVars.OptObjective for details. - OptObjectiveModerate string = "moderate" - // OptObjectiveDeterminate is a possible value for TiDBOptObjective. - OptObjectiveDeterminate = "determinate" -) - -// GetOptObjective return the session variable "tidb_opt_objective". -// Please see comments of SessionVars.OptObjective for details. -func (s *SessionVars) GetOptObjective() string { - return s.OptObjective -} diff --git a/sessionctx/variable/session_test.go b/sessionctx/variable/session_test.go deleted file mode 100644 index 0c431c41058f2..0000000000000 --- a/sessionctx/variable/session_test.go +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package variable_test - -import ( - "context" - "strconv" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/mysql" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - util2 "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/util" -) - -func TestSetSystemVariable(t *testing.T) { - v := variable.NewSessionVars(nil) - v.GlobalVarsAccessor = variable.NewMockGlobalAccessor4Tests() - v.TimeZone = time.UTC - mtx := new(sync.Mutex) - - testCases := []struct { - key string - value string - err bool - }{ - {variable.TxnIsolation, "SERIALIZABLE", true}, - {variable.TimeZone, "xyz", true}, - {variable.TiDBOptAggPushDown, "1", false}, - {variable.TiDBOptDeriveTopN, "1", false}, - {variable.TiDBOptDistinctAggPushDown, "1", false}, - {variable.TiDBMemQuotaQuery, "1024", false}, - {variable.TiDBMemQuotaApplyCache, "1024", false}, - {variable.TiDBEnableStmtSummary, "1", true}, // now global only - {variable.TiDBEnableRowLevelChecksum, "1", true}, - } - - for _, tc := range testCases { - // copy iterator variable into a new variable, see issue #27779 - tc := tc - t.Run(tc.key, func(t *testing.T) { - mtx.Lock() - err := v.SetSystemVar(tc.key, tc.value) - mtx.Unlock() - if tc.err { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestSession(t *testing.T) { - ctx := mock.NewContext() - - ss := ctx.GetSessionVars().StmtCtx - require.NotNil(t, ss) - - // For AffectedRows - ss.AddAffectedRows(1) - require.Equal(t, uint64(1), ss.AffectedRows()) - ss.AddAffectedRows(1) - require.Equal(t, uint64(2), ss.AffectedRows()) - - // For RecordRows - ss.AddRecordRows(1) - require.Equal(t, uint64(1), ss.RecordRows()) - ss.AddRecordRows(1) - require.Equal(t, uint64(2), ss.RecordRows()) - - // For FoundRows - ss.AddFoundRows(1) - require.Equal(t, uint64(1), ss.FoundRows()) - ss.AddFoundRows(1) - require.Equal(t, uint64(2), ss.FoundRows()) - - // For UpdatedRows - ss.AddUpdatedRows(1) - require.Equal(t, uint64(1), ss.UpdatedRows()) - ss.AddUpdatedRows(1) - require.Equal(t, uint64(2), ss.UpdatedRows()) - - // For TouchedRows - ss.AddTouchedRows(1) - require.Equal(t, uint64(1), ss.TouchedRows()) - ss.AddTouchedRows(1) - require.Equal(t, uint64(2), ss.TouchedRows()) - - // For CopiedRows - ss.AddCopiedRows(1) - require.Equal(t, uint64(1), ss.CopiedRows()) - ss.AddCopiedRows(1) - require.Equal(t, uint64(2), ss.CopiedRows()) - - // For last insert id - ctx.GetSessionVars().SetLastInsertID(1) - require.Equal(t, uint64(1), ctx.GetSessionVars().StmtCtx.LastInsertID) - - ss.ResetForRetry() - require.Equal(t, uint64(0), ss.AffectedRows()) - require.Equal(t, uint64(0), ss.FoundRows()) - require.Equal(t, uint64(0), ss.UpdatedRows()) - require.Equal(t, uint64(0), ss.RecordRows()) - require.Equal(t, uint64(0), ss.TouchedRows()) - require.Equal(t, uint64(0), ss.CopiedRows()) - require.Equal(t, uint16(0), ss.WarningCount()) -} - -func TestAllocMPPID(t *testing.T) { - ctx := mock.NewContext() - require.Equal(t, int64(1), plannercore.AllocMPPTaskID(ctx)) - require.Equal(t, int64(2), plannercore.AllocMPPTaskID(ctx)) - require.Equal(t, int64(3), plannercore.AllocMPPTaskID(ctx)) -} - -func TestSlowLogFormat(t *testing.T) { - ctx := mock.NewContext() - - seVar := ctx.GetSessionVars() - require.NotNil(t, seVar) - - seVar.User = &auth.UserIdentity{Username: "root", Hostname: "192.168.0.1"} - seVar.ConnectionInfo = &variable.ConnectionInfo{ClientIP: "192.168.0.1"} - seVar.ConnectionID = 1 - seVar.SessionAlias = "aliasabc" - // the out put of the loged CurrentDB should be 'test', should be to lower cased. - seVar.CurrentDB = "TeST" - seVar.InRestrictedSQL = true - seVar.StmtCtx.WaitLockLeaseTime = 1 - txnTS := uint64(406649736972468225) - costTime := time.Second - execDetail := execdetails.ExecDetails{ - BackoffTime: time.Millisecond, - RequestCount: 2, - ScanDetail: &util.ScanDetail{ - ProcessedKeys: 20001, - TotalKeys: 10000, - }, - DetailsNeedP90: execdetails.DetailsNeedP90{ - TimeDetail: util.TimeDetail{ - ProcessTime: time.Second * time.Duration(2), - WaitTime: time.Minute, - }, - }, - } - usedStats1 := &stmtctx.UsedStatsInfoForTable{ - Name: "t1", - TblInfo: nil, - Version: 123, - RealtimeCount: 1000, - ModifyCount: 0, - ColumnStatsLoadStatus: map[int64]string{2: "allEvicted", 3: "onlyCmsEvicted"}, - IndexStatsLoadStatus: map[int64]string{1: "allLoaded", 2: "allLoaded"}, - } - usedStats2 := &stmtctx.UsedStatsInfoForTable{ - Name: "t2", - TblInfo: nil, - Version: 0, - RealtimeCount: 10000, - ModifyCount: 0, - ColumnStatsLoadStatus: map[int64]string{2: "unInitialized"}, - } - - copTasks := &stmtctx.CopTasksDetails{ - NumCopTasks: 10, - AvgProcessTime: time.Second, - P90ProcessTime: time.Second * 2, - MaxProcessAddress: "10.6.131.78", - MaxProcessTime: time.Second * 3, - AvgWaitTime: time.Millisecond * 10, - P90WaitTime: time.Millisecond * 20, - MaxWaitTime: time.Millisecond * 30, - MaxWaitAddress: "10.6.131.79", - MaxBackoffTime: make(map[string]time.Duration), - AvgBackoffTime: make(map[string]time.Duration), - P90BackoffTime: make(map[string]time.Duration), - TotBackoffTime: make(map[string]time.Duration), - TotBackoffTimes: make(map[string]int), - MaxBackoffAddress: make(map[string]string), - } - - backoffs := []string{"rpcTiKV", "rpcPD", "regionMiss"} - for _, backoff := range backoffs { - copTasks.MaxBackoffTime[backoff] = time.Millisecond * 200 - copTasks.MaxBackoffAddress[backoff] = "127.0.0.1" - copTasks.AvgBackoffTime[backoff] = time.Millisecond * 200 - copTasks.P90BackoffTime[backoff] = time.Millisecond * 200 - copTasks.TotBackoffTime[backoff] = time.Millisecond * 200 - copTasks.TotBackoffTimes[backoff] = 200 - } - - var memMax int64 = 2333 - var diskMax int64 = 6666 - resultFields := `# Txn_start_ts: 406649736972468225 -# Keyspace_name: keyspace_a -# Keyspace_ID: 1 -# User@Host: root[root] @ 192.168.0.1 [192.168.0.1] -# Conn_ID: 1 -# Session_alias: aliasabc -# Exec_retry_time: 5.1 Exec_retry_count: 3 -# Query_time: 1 -# Parse_time: 0.00000001 -# Compile_time: 0.00000001 -# Rewrite_time: 0.000000003 Preproc_subqueries: 2 Preproc_subqueries_time: 0.000000002 -# Optimize_time: 0.00000001 -# Wait_TS: 0.000000003 -# Process_time: 2 Wait_time: 60 Backoff_time: 0.001 Request_count: 2 Process_keys: 20001 Total_keys: 10000 -# DB: test -# Index_names: [t1:a,t2:b] -# Is_internal: true -# Digest: e5796985ccafe2f71126ed6c0ac939ffa015a8c0744a24b7aee6d587103fd2f7 -# Stats: t1:123[1000;0][ID 1:allLoaded,ID 2:allLoaded][ID 2:allEvicted,ID 3:onlyCmsEvicted],t2:pseudo[10000;0] -# Num_cop_tasks: 10 -# Cop_proc_avg: 1 Cop_proc_p90: 2 Cop_proc_max: 3 Cop_proc_addr: 10.6.131.78 -# Cop_wait_avg: 0.01 Cop_wait_p90: 0.02 Cop_wait_max: 0.03 Cop_wait_addr: 10.6.131.79 -# Cop_backoff_regionMiss_total_times: 200 Cop_backoff_regionMiss_total_time: 0.2 Cop_backoff_regionMiss_max_time: 0.2 Cop_backoff_regionMiss_max_addr: 127.0.0.1 Cop_backoff_regionMiss_avg_time: 0.2 Cop_backoff_regionMiss_p90_time: 0.2 -# Cop_backoff_rpcPD_total_times: 200 Cop_backoff_rpcPD_total_time: 0.2 Cop_backoff_rpcPD_max_time: 0.2 Cop_backoff_rpcPD_max_addr: 127.0.0.1 Cop_backoff_rpcPD_avg_time: 0.2 Cop_backoff_rpcPD_p90_time: 0.2 -# Cop_backoff_rpcTiKV_total_times: 200 Cop_backoff_rpcTiKV_total_time: 0.2 Cop_backoff_rpcTiKV_max_time: 0.2 Cop_backoff_rpcTiKV_max_addr: 127.0.0.1 Cop_backoff_rpcTiKV_avg_time: 0.2 Cop_backoff_rpcTiKV_p90_time: 0.2 -# Mem_max: 2333 -# Disk_max: 6666 -# Prepared: true -# Plan_from_cache: true -# Plan_from_binding: true -# Has_more_results: true -# KV_total: 10 -# PD_total: 11 -# Backoff_total: 12 -# Write_sql_response_total: 1 -# Result_rows: 12345 -# Succ: true -# IsExplicitTxn: true -# IsSyncStatsFailed: false -# IsWriteCacheTable: true` - sql := "select * from t;" - _, digest := parser.NormalizeDigest(sql) - logItems := &variable.SlowQueryLogItems{ - TxnTS: txnTS, - KeyspaceName: "keyspace_a", - KeyspaceID: 1, - SQL: sql, - Digest: digest.String(), - TimeTotal: costTime, - TimeParse: time.Duration(10), - TimeCompile: time.Duration(10), - TimeOptimize: time.Duration(10), - TimeWaitTS: time.Duration(3), - IndexNames: "[t1:a,t2:b]", - CopTasks: copTasks, - ExecDetail: execDetail, - MemMax: memMax, - DiskMax: diskMax, - Prepared: true, - PlanFromCache: true, - PlanFromBinding: true, - HasMoreResults: true, - KVTotal: 10 * time.Second, - PDTotal: 11 * time.Second, - BackoffTotal: 12 * time.Second, - WriteSQLRespTotal: 1 * time.Second, - ResultRows: 12345, - Succ: true, - RewriteInfo: variable.RewritePhaseInfo{ - DurationRewrite: 3, - DurationPreprocessSubQuery: 2, - PreprocessSubQueries: 2, - }, - ExecRetryCount: 3, - ExecRetryTime: 5*time.Second + time.Millisecond*100, - IsExplicitTxn: true, - IsWriteCacheTable: true, - UsedStats: map[int64]*stmtctx.UsedStatsInfoForTable{1: usedStats1, 2: usedStats2}, - } - logString := seVar.SlowLogFormat(logItems) - require.Equal(t, resultFields+"\n"+sql, logString) - - seVar.CurrentDBChanged = true - logString = seVar.SlowLogFormat(logItems) - require.Equal(t, resultFields+"\n"+"use test;\n"+sql, logString) - require.False(t, seVar.CurrentDBChanged) -} - -func TestIsolationRead(t *testing.T) { - defer config.RestoreFunc()() - config.UpdateGlobal(func(conf *config.Config) { - conf.IsolationRead.Engines = []string{"tiflash", "tidb"} - }) - sessVars := variable.NewSessionVars(nil) - _, ok := sessVars.IsolationReadEngines[kv.TiDB] - require.True(t, ok) - _, ok = sessVars.IsolationReadEngines[kv.TiKV] - require.False(t, ok) - _, ok = sessVars.IsolationReadEngines[kv.TiFlash] - require.True(t, ok) -} - -func TestTableDeltaClone(t *testing.T) { - td0 := variable.TableDelta{ - Delta: 1, - Count: 2, - ColSize: map[int64]int64{1: 1, 2: 2}, - InitTime: time.Now(), - TableID: 5, - } - td1 := td0.Clone() - require.Equal(t, td0, td1) - td0.ColSize[3] = 3 - require.NotEqual(t, td0, td1) - - td2 := td0.Clone() - require.Equal(t, td0, td2) - td0.InitTime = td0.InitTime.Add(time.Second) - require.NotEqual(t, td0, td2) -} - -func TestTransactionContextSavepoint(t *testing.T) { - tc := &variable.TransactionContext{ - TxnCtxNeedToRestore: variable.TxnCtxNeedToRestore{ - TableDeltaMap: map[int64]variable.TableDelta{ - 1: { - Delta: 1, - Count: 2, - ColSize: map[int64]int64{1: 1}, - InitTime: time.Now(), - TableID: 5, - }, - }, - }, - } - tc.SetPessimisticLockCache([]byte{'a'}, []byte{'a'}) - tc.FlushStmtPessimisticLockCache() - - tc.AddSavepoint("S1", nil) - require.Equal(t, 1, len(tc.Savepoints)) - require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) - require.Equal(t, "s1", tc.Savepoints[0].Name) - - succ := tc.DeleteSavepoint("s2") - require.False(t, succ) - require.Equal(t, 1, len(tc.Savepoints)) - - tc.TableDeltaMap[1].ColSize[2] = 2 - tc.TableDeltaMap[2] = variable.TableDelta{ - Delta: 6, - Count: 7, - ColSize: map[int64]int64{8: 8}, - InitTime: time.Now(), - TableID: 9, - } - tc.SetPessimisticLockCache([]byte{'b'}, []byte{'b'}) - tc.FlushStmtPessimisticLockCache() - - tc.AddSavepoint("S2", nil) - require.Equal(t, 2, len(tc.Savepoints)) - require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) - require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap[1].ColSize)) - require.Equal(t, "s1", tc.Savepoints[0].Name) - require.Equal(t, 2, len(tc.Savepoints[1].TxnCtxSavepoint.TableDeltaMap)) - require.Equal(t, "s2", tc.Savepoints[1].Name) - - tc.TableDeltaMap[3] = variable.TableDelta{ - Delta: 10, - Count: 11, - ColSize: map[int64]int64{12: 12}, - InitTime: time.Now(), - TableID: 13, - } - tc.SetPessimisticLockCache([]byte{'c'}, []byte{'c'}) - tc.FlushStmtPessimisticLockCache() - - tc.AddSavepoint("s2", nil) - require.Equal(t, 2, len(tc.Savepoints)) - require.Equal(t, 3, len(tc.Savepoints[1].TxnCtxSavepoint.TableDeltaMap)) - require.Equal(t, "s2", tc.Savepoints[1].Name) - - tc.RollbackToSavepoint("s1") - require.Equal(t, 1, len(tc.Savepoints)) - require.Equal(t, 1, len(tc.Savepoints[0].TxnCtxSavepoint.TableDeltaMap)) - require.Equal(t, "s1", tc.Savepoints[0].Name) - val, ok := tc.GetKeyInPessimisticLockCache([]byte{'a'}) - require.True(t, ok) - require.Equal(t, []byte{'a'}, val) - val, ok = tc.GetKeyInPessimisticLockCache([]byte{'b'}) - require.False(t, ok) - require.Nil(t, val) - - succ = tc.DeleteSavepoint("s1") - require.True(t, succ) - require.Equal(t, 0, len(tc.Savepoints)) -} - -func TestNonPreparedPlanCacheStmt(t *testing.T) { - sessVars := variable.NewSessionVars(nil) - sessVars.SessionPlanCacheSize = 100 - sql1 := "select * from t where a>?" - sql2 := "select * from t where a 0 { - // Can use percentage format when TiDB can obtain physical memory - // Test Percentage Format - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1%") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - if total/100 > uint64(512<<20) { - require.Equal(t, memory.ServerMemoryLimit.Load(), total/100) - require.Equal(t, "1%", val) - } else { - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) - require.Equal(t, "512MB", val) - } - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "0%") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "100%") - require.Error(t, err) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "75%") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, "75%", val) - require.Equal(t, memory.ServerMemoryLimit.Load(), total/100*75) - } - // Test can't obtain physical memory - require.Nil(t, failpoint.Enable("github.com/pingcap/tidb/util/memory/GetMemTotalError", `return(true)`)) - require.Error(t, mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "75%")) - require.Nil(t, failpoint.Disable("github.com/pingcap/tidb/util/memory/GetMemTotalError")) - - // Test byteSize format - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1234") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) - require.Equal(t, "512MB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "1234567890123") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(1234567890123)) - require.Equal(t, "1234567890123", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "10KB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) - require.Equal(t, "512MB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "12345678KB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(12345678<<10)) - require.Equal(t, "12345678KB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "10MB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(512<<20)) - require.Equal(t, "512MB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "700MB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(700<<20)) - require.Equal(t, "700MB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "20GB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(20<<30)) - require.Equal(t, "20GB", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "2TB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimit) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimit.Load(), uint64(2<<40)) - require.Equal(t, "2TB", val) - - // Test error - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "123aaa123") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "700MBaa") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimit, "a700MB") - require.Error(t, err) -} - -func TestTiDBServerMemoryLimitSessMinSize(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - var ( - err error - val string - ) - - serverMemroyLimitSessMinSize := GetSysVar(TiDBServerMemoryLimitSessMinSize) - // Check default value - require.Equal(t, serverMemroyLimitSessMinSize.Value, strconv.FormatInt(DefTiDBServerMemoryLimitSessMinSize, 10)) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "123456") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(123456)) - require.Equal(t, "123456", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "100") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(128)) - require.Equal(t, "128", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitSessMinSize, "123MB") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitSessMinSize) - require.NoError(t, err) - require.Equal(t, memory.ServerMemoryLimitSessMinSize.Load(), uint64(123<<20)) - require.Equal(t, "128974848", val) -} - -func TestTiDBServerMemoryLimitGCTrigger(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - var ( - err error - val string - ) - - serverMemroyLimitGCTrigger := GetSysVar(TiDBServerMemoryLimitGCTrigger) - // Check default value - require.Equal(t, serverMemroyLimitGCTrigger.Value, strconv.FormatFloat(DefTiDBServerMemoryLimitGCTrigger, 'f', -1, 64)) - defer func() { - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, strconv.FormatFloat(DefTiDBServerMemoryLimitGCTrigger, 'f', -1, 64)) - require.NoError(t, err) - }() - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "0.8") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitGCTrigger) - require.NoError(t, err) - require.Equal(t, gctuner.GlobalMemoryLimitTuner.GetPercentage(), 0.8) - require.Equal(t, "0.8", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "90%") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBServerMemoryLimitGCTrigger) - require.NoError(t, err) - require.Equal(t, gctuner.GlobalMemoryLimitTuner.GetPercentage(), 0.9) - require.Equal(t, "0.9", val) - - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "100%") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "101%") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "99%") - require.NoError(t, err) - - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerThreshold, "0.4") - require.NoError(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "49%") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBServerMemoryLimitGCTrigger, "51%") - require.NoError(t, err) - - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMaxValue, "50") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "200") - require.NoError(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "1000") - require.Error(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMinValue, "100") - require.NoError(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBGOGCTunerMaxValue, "200") - require.NoError(t, err) -} - -func TestSetAggPushDownGlobally(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - val, err := mock.GetGlobalSysVar(TiDBOptAggPushDown) - require.NoError(t, err) - require.Equal(t, "OFF", val) - err = mock.SetGlobalSysVar(context.Background(), TiDBOptAggPushDown, "ON") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBOptAggPushDown) - require.NoError(t, err) - require.Equal(t, "ON", val) -} - -func TestSetDeriveTopNGlobally(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - val, err := mock.GetGlobalSysVar(TiDBOptDeriveTopN) - require.NoError(t, err) - require.Equal(t, "OFF", val) - err = mock.SetGlobalSysVar(context.Background(), TiDBOptDeriveTopN, "ON") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBOptDeriveTopN) - require.NoError(t, err) - require.Equal(t, "ON", val) -} - -func TestSetJobScheduleWindow(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - // default value - val, err := mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) - require.NoError(t, err) - require.Equal(t, "00:00 +0000", val) - - // set and get variable in UTC - vars.TimeZone = time.UTC - err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) - require.NoError(t, err) - require.Equal(t, "16:11 +0000", val) - - // set variable in UTC, get it in Asia/Shanghai - vars.TimeZone = time.UTC - err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") - require.NoError(t, err) - vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) - require.NoError(t, err) - require.Equal(t, "16:11 +0000", val) - - // set variable in Asia/Shanghai, get it it UTC - vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiDBTTLJobScheduleWindowStartTime, "16:11") - require.NoError(t, err) - vars.TimeZone = time.UTC - val, err = mock.GetGlobalSysVar(TiDBTTLJobScheduleWindowStartTime) - require.NoError(t, err) - require.Equal(t, "16:11 +0800", val) -} - -func TestTiDBEnableResourceControl(t *testing.T) { - // setup the hooks for test - // NOTE: the default system variable is true but the switch is false - // It is initialized at the first call of `rebuildSysVarCache` - enable := false - EnableGlobalResourceControlFunc = func() { enable = true } - DisableGlobalResourceControlFunc = func() { enable = false } - setGlobalResourceControlFunc := func(enable bool) { - if enable { - EnableGlobalResourceControlFunc() - } else { - DisableGlobalResourceControlFunc() - } - } - SetGlobalResourceControl.Store(&setGlobalResourceControlFunc) - - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - resourceControlEnabled := GetSysVar(TiDBEnableResourceControl) - - // Default true - require.Equal(t, resourceControlEnabled.Value, On) - require.Equal(t, enable, false) - - // Set to On(init at start) - err := mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, On) - require.NoError(t, err) - val, err1 := mock.GetGlobalSysVar(TiDBEnableResourceControl) - require.NoError(t, err1) - require.Equal(t, On, val) - require.Equal(t, enable, true) - - // Set to Off - err = mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, Off) - require.NoError(t, err) - val, err1 = mock.GetGlobalSysVar(TiDBEnableResourceControl) - require.NoError(t, err1) - require.Equal(t, Off, val) - require.Equal(t, enable, false) - - // Set to On again - err = mock.SetGlobalSysVar(context.Background(), TiDBEnableResourceControl, On) - require.NoError(t, err) - val, err1 = mock.GetGlobalSysVar(TiDBEnableResourceControl) - require.NoError(t, err1) - require.Equal(t, On, val) - require.Equal(t, enable, true) -} - -func TestTiDBEnableRowLevelChecksum(t *testing.T) { - ctx := context.Background() - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - - // default to false - val, err := mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) - require.NoError(t, err) - require.Equal(t, Off, val) - - // enable - err = mock.SetGlobalSysVar(ctx, TiDBEnableRowLevelChecksum, On) - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) - require.NoError(t, err) - require.Equal(t, On, val) - - // disable - err = mock.SetGlobalSysVar(ctx, TiDBEnableRowLevelChecksum, Off) - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiDBEnableRowLevelChecksum) - require.NoError(t, err) - require.Equal(t, Off, val) -} - -func TestTiDBTiFlashReplicaRead(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - tidbTiFlashReplicaRead := GetSysVar(TiFlashReplicaRead) - // Check default value - require.Equal(t, DefTiFlashReplicaRead, tidbTiFlashReplicaRead.Value) - - err := mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "all_replicas") - require.NoError(t, err) - val, err := mock.GetGlobalSysVar(TiFlashReplicaRead) - require.NoError(t, err) - require.Equal(t, "all_replicas", val) - - err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "closest_adaptive") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) - require.NoError(t, err) - require.Equal(t, "closest_adaptive", val) - - err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "closest_replicas") - require.NoError(t, err) - val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) - require.NoError(t, err) - require.Equal(t, "closest_replicas", val) - - err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, DefTiFlashReplicaRead) - require.NoError(t, err) - err = mock.SetGlobalSysVar(context.Background(), TiFlashReplicaRead, "random") - require.Error(t, err) - val, err = mock.GetGlobalSysVar(TiFlashReplicaRead) - require.NoError(t, err) - require.Equal(t, DefTiFlashReplicaRead, val) -} - -func TestSetTiDBCloudStorageURI(t *testing.T) { - vars := NewSessionVars(nil) - mock := NewMockGlobalAccessor4Tests() - mock.SessionVars = vars - vars.GlobalVarsAccessor = mock - cloudStorageURI := GetSysVar(TiDBCloudStorageURI) - require.Len(t, CloudStorageURI.Load(), 0) - defer func() { - CloudStorageURI.Store("") - }() - - // Default empty - require.Len(t, cloudStorageURI.Value, 0) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - // Set to noop - noopURI := "noop://blackhole?access-key=hello&secret-access-key=world" - err := mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, noopURI) - require.NoError(t, err) - val, err1 := mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) - require.NoError(t, err1) - require.Equal(t, noopURI, val) - require.Equal(t, noopURI, CloudStorageURI.Load()) - - // Set to s3, should fail - err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, "s3://blackhole") - require.ErrorContains(t, err, "bucket blackhole") - - s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(200) - })) - defer s.Close() - - // Set to s3, should return uri without variable - s3URI := "s3://tiflow-test/?access-key=testid&secret-access-key=testkey8&session-token=testtoken&endpoint=" + s.URL - err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, s3URI) - require.NoError(t, err) - val, err1 = mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) - require.NoError(t, err1) - require.True(t, strings.HasPrefix(val, "s3://tiflow-test/")) - require.Contains(t, val, "access-key=xxxxxx") - require.Contains(t, val, "secret-access-key=xxxxxx") - require.Contains(t, val, "session-token=xxxxxx") - require.Equal(t, s3URI, CloudStorageURI.Load()) - - // Set to empty, should return no error - err = mock.SetGlobalSysVar(ctx, TiDBCloudStorageURI, "") - require.NoError(t, err) - val, err1 = mock.SessionVars.GetSessionOrGlobalSystemVar(ctx, TiDBCloudStorageURI) - require.NoError(t, err1) - require.Len(t, val, 0) - cancel() -} diff --git a/sessionctx/variable/variable.go b/sessionctx/variable/variable.go deleted file mode 100644 index 8daad1c971df1..0000000000000 --- a/sessionctx/variable/variable.go +++ /dev/null @@ -1,664 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "context" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "golang.org/x/exp/maps" -) - -// ScopeFlag is for system variable whether can be changed in global/session dynamically or not. -type ScopeFlag uint8 - -// TypeFlag is the SysVar type, which doesn't exactly match MySQL types. -type TypeFlag byte - -const ( - // ScopeNone means the system variable can not be changed dynamically. - ScopeNone ScopeFlag = 0 - // ScopeGlobal means the system variable can be changed globally. - ScopeGlobal ScopeFlag = 1 << 0 - // ScopeSession means the system variable can only be changed in current session. - ScopeSession ScopeFlag = 1 << 1 - // ScopeInstance means it is similar to global but doesn't propagate to other TiDB servers. - ScopeInstance ScopeFlag = 1 << 2 - - // TypeStr is the default - TypeStr TypeFlag = iota - // TypeBool for boolean - TypeBool - // TypeInt for integer - TypeInt - // TypeEnum for Enum - TypeEnum - // TypeFloat for Double - TypeFloat - // TypeUnsigned for Unsigned integer - TypeUnsigned - // TypeTime for time of day (a TiDB extension) - TypeTime - // TypeDuration for a golang duration (a TiDB extension) - TypeDuration - - // On is the canonical string for ON - On = "ON" - // Off is the canonical string for OFF - Off = "OFF" - // Warn means return warnings - Warn = "WARN" - // IntOnly means enable for int type - IntOnly = "INT_ONLY" - - // AssertionStrictStr is a choice of variable TiDBTxnAssertionLevel that means full assertions should be performed, - // even if the performance might be slowed down. - AssertionStrictStr = "STRICT" - // AssertionFastStr is a choice of variable TiDBTxnAssertionLevel that means assertions that doesn't affect - // performance should be performed. - AssertionFastStr = "FAST" - // AssertionOffStr is a choice of variable TiDBTxnAssertionLevel that means no assertion should be performed. - AssertionOffStr = "OFF" - // OOMActionCancel constants represents the valid action configurations for OOMAction "CANCEL". - OOMActionCancel = "CANCEL" - // OOMActionLog constants represents the valid action configurations for OOMAction "LOG". - OOMActionLog = "LOG" -) - -// Global config name list. -const ( - GlobalConfigEnableTopSQL = "enable_resource_metering" - GlobalConfigSourceID = "source_id" -) - -func (s ScopeFlag) String() string { - var scopes []string - if s == ScopeNone { - return "NONE" - } - if s&ScopeSession != 0 { - scopes = append(scopes, "SESSION") - } - if s&ScopeGlobal != 0 { - scopes = append(scopes, "GLOBAL") - } - if s&ScopeInstance != 0 { - scopes = append(scopes, "INSTANCE") - } - return strings.Join(scopes, ",") -} - -// SysVar is for system variable. -// All the fields of SysVar should be READ ONLY after created. -type SysVar struct { - // Scope is for whether can be changed or not - Scope ScopeFlag - // Name is the variable name. - Name string - // Value is the variable value. - Value string - // Type is the MySQL type (optional) - Type TypeFlag - // MinValue will automatically be validated when specified (optional) - MinValue int64 - // MaxValue will automatically be validated when specified (optional) - MaxValue uint64 - // AutoConvertNegativeBool applies to boolean types (optional) - AutoConvertNegativeBool bool - // ReadOnly applies to all types - ReadOnly bool - // PossibleValues applies to ENUM type - PossibleValues []string - // AllowEmpty is a special TiDB behavior which means "read value from config" (do not use) - AllowEmpty bool - // AllowEmptyAll is a special behavior that only applies to TiDBCapturePlanBaseline, TiDBTxnMode (do not use) - AllowEmptyAll bool - // AllowAutoValue means that the special value "-1" is permitted, even when outside of range. - AllowAutoValue bool - // Validation is a callback after the type validation has been performed, but before the Set function - Validation func(*SessionVars, string, string, ScopeFlag) (string, error) - // SetSession is called after validation but before updating systems[]. It also doubles as an Init function - // and will be called on all variables in builtinGlobalVariable, regardless of their scope. - SetSession func(*SessionVars, string) error - // SetGlobal is called after validation - SetGlobal func(context.Context, *SessionVars, string) error - // IsHintUpdatableVerfied indicate whether we've confirmed that SET_VAR() hint is worked for this hint. - IsHintUpdatableVerfied bool - // Deprecated: Hidden previously meant that the variable still responds to SET but doesn't show up in SHOW VARIABLES - // However, this feature is no longer used. All variables are visble. - Hidden bool - // Aliases is a list of sysvars that should also be updated when this sysvar is updated. - // Updating aliases calls the SET function of the aliases, but does not update their aliases (preventing SET recursion) - Aliases []string - // GetSession is a getter function for session scope. - // It can be used by instance-scoped variables to overwrite the previously expected value. - GetSession func(*SessionVars) (string, error) - // GetGlobal is a getter function for global scope. - GetGlobal func(context.Context, *SessionVars) (string, error) - // GetStateValue gets the value for session states, which is used for migrating sessions. - // We need a function to override GetSession sometimes, because GetSession may not return the real value. - GetStateValue func(*SessionVars) (string, bool, error) - // Depended indicates whether other variables depend on this one. That is, if this one is not correctly set, - // another variable cannot be set either. - // This flag is used to decide the order to replay session variables. - Depended bool - // skipInit defines if the sysvar should be loaded into the session on init. - // This is only important to set for sysvars that include session scope, - // since global scoped sysvars are not-applicable. - skipInit bool - // IsNoop defines if the sysvar is a noop included for MySQL compatibility - IsNoop bool - // GlobalConfigName is the global config name of this global variable. - // If the global variable has the global config name, - // it should store the global config into PD(etcd) too when set global variable. - GlobalConfigName string - // RequireDynamicPrivileges is a function to return a dynamic privilege list to check the set sysvar privilege - RequireDynamicPrivileges func(isGlobal bool, sem bool) []string -} - -// GetGlobalFromHook calls the GetSession func if it exists. -func (sv *SysVar) GetGlobalFromHook(ctx context.Context, s *SessionVars) (string, error) { - // Call the Getter if there is one defined. - if sv.GetGlobal != nil { - val, err := sv.GetGlobal(ctx, s) - if err != nil { - return val, err - } - // Ensure that the results from the getter are validated - // Since some are read directly from tables. - return sv.ValidateWithRelaxedValidation(s, val, ScopeGlobal), nil - } - if sv.HasNoneScope() { - return sv.Value, nil - } - return s.GlobalVarsAccessor.GetGlobalSysVar(sv.Name) -} - -// GetSessionFromHook calls the GetSession func if it exists. -func (sv *SysVar) GetSessionFromHook(s *SessionVars) (string, error) { - if sv.HasNoneScope() { - return sv.Value, nil - } - // Call the Getter if there is one defined. - if sv.GetSession != nil { - val, err := sv.GetSession(s) - if err != nil { - return val, err - } - // Ensure that the results from the getter are validated - // Since some are read directly from tables. - return sv.ValidateWithRelaxedValidation(s, val, ScopeSession), nil - } - var ( - ok bool - val string - ) - if val, ok = s.stmtVars[sv.Name]; ok { - return val, nil - } - if val, ok = s.systems[sv.Name]; !ok { - return val, errors.New("sysvar has not yet loaded") - } - return val, nil -} - -// SetSessionFromHook calls the SetSession func if it exists. -func (sv *SysVar) SetSessionFromHook(s *SessionVars, val string) error { - if sv.SetSession != nil { - if err := sv.SetSession(s, val); err != nil { - return err - } - } - s.systems[sv.Name] = val - - // Call the Set function on all the aliases for this sysVar - // Skipping the validation function, and not calling aliases of - // aliases. By skipping the validation function it means that things - // like duplicate warnings should not appear. - - if sv.Aliases != nil { - for _, aliasName := range sv.Aliases { - aliasSv := GetSysVar(aliasName) - if aliasSv.SetSession != nil { - if err := aliasSv.SetSession(s, val); err != nil { - return err - } - } - s.systems[aliasSv.Name] = val - } - } - return nil -} - -// SetGlobalFromHook calls the SetGlobal func if it exists. -func (sv *SysVar) SetGlobalFromHook(ctx context.Context, s *SessionVars, val string, skipAliases bool) error { - if sv.SetGlobal != nil { - return sv.SetGlobal(ctx, s, val) - } - - // Call the SetGlobalSysVarOnly function on all the aliases for this sysVar - // which skips the validation function and when SetGlobalFromHook is called again - // it will be with skipAliases=true. This helps break recursion because - // most aliases are reciprocal. - - if !skipAliases && sv.Aliases != nil { - for _, aliasName := range sv.Aliases { - if err := s.GlobalVarsAccessor.SetGlobalSysVarOnly(ctx, aliasName, val, true); err != nil { - return err - } - } - } - return nil -} - -// HasNoneScope returns true if the scope for the sysVar is None. -func (sv *SysVar) HasNoneScope() bool { - return sv.Scope == ScopeNone -} - -// HasSessionScope returns true if the scope for the sysVar includes session. -func (sv *SysVar) HasSessionScope() bool { - return sv.Scope&ScopeSession != 0 -} - -// HasGlobalScope returns true if the scope for the sysVar includes global. -func (sv *SysVar) HasGlobalScope() bool { - return sv.Scope&ScopeGlobal != 0 -} - -// HasInstanceScope returns true if the scope for the sysVar includes instance -func (sv *SysVar) HasInstanceScope() bool { - return sv.Scope&ScopeInstance != 0 -} - -// Validate checks if system variable satisfies specific restriction. -func (sv *SysVar) Validate(vars *SessionVars, value string, scope ScopeFlag) (string, error) { - // Check that the scope is correct first. - if err := sv.validateScope(scope); err != nil { - return value, err - } - // Normalize the value and apply validation based on type. - // i.e. TypeBool converts 1/on/ON to ON. - normalizedValue, err := sv.ValidateFromType(vars, value, scope) - if err != nil { - return normalizedValue, err - } - // If type validation was successful, call the (optional) validation function - if sv.Validation != nil { - return sv.Validation(vars, normalizedValue, value, scope) - } - return normalizedValue, nil -} - -// ValidateFromType provides automatic validation based on the SysVar's type -func (sv *SysVar) ValidateFromType(vars *SessionVars, value string, scope ScopeFlag) (string, error) { - // Some sysvars in TiDB have a special behavior where the empty string means - // "use the config file value". This needs to be cleaned up once the behavior - // for instance variables is determined. - if value == "" && ((sv.AllowEmpty && scope == ScopeSession) || sv.AllowEmptyAll) { - return value, nil - } - // Provide validation using the SysVar struct - switch sv.Type { - case TypeUnsigned: - return sv.checkUInt64SystemVar(value, vars) - case TypeInt: - return sv.checkInt64SystemVar(value, vars) - case TypeBool: - return sv.checkBoolSystemVar(value, vars) - case TypeFloat: - return sv.checkFloatSystemVar(value, vars) - case TypeEnum: - return sv.checkEnumSystemVar(value, vars) - case TypeTime: - return sv.checkTimeSystemVar(value, vars) - case TypeDuration: - return sv.checkDurationSystemVar(value, vars) - } - return value, nil // typeString -} - -func (sv *SysVar) validateScope(scope ScopeFlag) error { - if sv.ReadOnly || sv.Scope == ScopeNone { - return ErrIncorrectScope.FastGenByArgs(sv.Name, "read only") - } - if scope == ScopeGlobal && !(sv.HasGlobalScope() || sv.HasInstanceScope()) { - return errLocalVariable.FastGenByArgs(sv.Name) - } - if scope == ScopeSession && !sv.HasSessionScope() { - return errGlobalVariable.FastGenByArgs(sv.Name) - } - return nil -} - -// ValidateWithRelaxedValidation normalizes values but can not return errors. -// Normalization+validation needs to be applied when reading values because older versions of TiDB -// may be less sophisticated in normalizing values. But errors should be caught and handled, -// because otherwise there will be upgrade issues. -func (sv *SysVar) ValidateWithRelaxedValidation(vars *SessionVars, value string, scope ScopeFlag) string { - warns := vars.StmtCtx.GetWarnings() - defer func() { - vars.StmtCtx.SetWarnings(warns) // RelaxedValidation = trim warnings too. - }() - normalizedValue, err := sv.ValidateFromType(vars, value, scope) - if err != nil { - return normalizedValue - } - if sv.Validation != nil { - normalizedValue, err = sv.Validation(vars, normalizedValue, value, scope) - if err != nil { - return normalizedValue - } - } - return normalizedValue -} - -const ( - localDayTimeFormat = "15:04" - // FullDayTimeFormat is the full format of analyze start time and end time. - FullDayTimeFormat = "15:04 -0700" -) - -func (sv *SysVar) checkTimeSystemVar(value string, vars *SessionVars) (string, error) { - var t time.Time - var err error - if len(value) <= len(localDayTimeFormat) { - t, err = time.ParseInLocation(localDayTimeFormat, value, vars.Location()) - } else { - t, err = time.ParseInLocation(FullDayTimeFormat, value, vars.Location()) - } - if err != nil { - return "", err - } - // Add a modern date to it, as the timezone shift can differ across the history - // For example, the Asia/Shanghai refers to +08:05 before 1900 - now := time.Now() - t = time.Date(now.Year(), now.Month(), now.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), t.Location()) - return t.Format(FullDayTimeFormat), nil -} - -func (sv *SysVar) checkDurationSystemVar(value string, vars *SessionVars) (string, error) { - d, err := time.ParseDuration(value) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - // Check for min/max violations - if int64(d) < sv.MinValue { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return time.Duration(sv.MinValue).String(), nil - } - if uint64(d) > sv.MaxValue { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return time.Duration(sv.MaxValue).String(), nil - } - // return a string representation of the duration - return d.String(), nil -} - -func (sv *SysVar) checkUInt64SystemVar(value string, vars *SessionVars) (string, error) { - if sv.AllowAutoValue && value == "-1" { - return value, nil - } - if len(value) == 0 { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - if value[0] == '-' { - _, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatInt(sv.MinValue, 10), nil - } - val, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - if val < uint64(sv.MinValue) { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatInt(sv.MinValue, 10), nil - } - if val > sv.MaxValue { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatUint(sv.MaxValue, 10), nil - } - return value, nil -} - -func (sv *SysVar) checkInt64SystemVar(value string, vars *SessionVars) (string, error) { - if sv.AllowAutoValue && value == "-1" { - return value, nil - } - val, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - if val < sv.MinValue { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatInt(sv.MinValue, 10), nil - } - if val > int64(sv.MaxValue) { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatUint(sv.MaxValue, 10), nil - } - return value, nil -} - -func (sv *SysVar) checkEnumSystemVar(value string, vars *SessionVars) (string, error) { - // The value could be either a string or the ordinal position in the PossibleValues. - // This allows for the behavior 0 = OFF, 1 = ON, 2 = DEMAND etc. - var iStr string - for i, v := range sv.PossibleValues { - iStr = strconv.Itoa(i) - if strings.EqualFold(value, v) || strings.EqualFold(value, iStr) { - return v, nil - } - } - return value, ErrWrongValueForVar.GenWithStackByArgs(sv.Name, value) -} - -func (sv *SysVar) checkFloatSystemVar(value string, vars *SessionVars) (string, error) { - if len(value) == 0 { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - val, err := strconv.ParseFloat(value, 64) - if err != nil { - return value, ErrWrongTypeForVar.GenWithStackByArgs(sv.Name) - } - if val < float64(sv.MinValue) { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatInt(sv.MinValue, 10), nil - } - if val > float64(sv.MaxValue) { - vars.StmtCtx.AppendWarning(ErrTruncatedWrongValue.GenWithStackByArgs(sv.Name, value)) - return strconv.FormatUint(sv.MaxValue, 10), nil - } - return value, nil -} - -func (sv *SysVar) checkBoolSystemVar(value string, vars *SessionVars) (string, error) { - if strings.EqualFold(value, "ON") { - return On, nil - } else if strings.EqualFold(value, "OFF") { - return Off, nil - } - val, err := strconv.ParseInt(value, 10, 64) - if err == nil { - // There are two types of conversion rules for integer values. - // The default only allows 0 || 1, but a subset of values convert any - // negative integer to 1. - if !sv.AutoConvertNegativeBool { - if val == 0 { - return Off, nil - } else if val == 1 { - return On, nil - } - } else { - if val == 1 || val < 0 { - return On, nil - } else if val == 0 { - return Off, nil - } - } - } - return value, ErrWrongValueForVar.GenWithStackByArgs(sv.Name, value) -} - -// GetNativeValType attempts to convert the val to the approx MySQL non-string type -// TODO: only return 3 types now, support others like DOUBLE, TIME later -func (sv *SysVar) GetNativeValType(val string) (types.Datum, byte, uint) { - switch sv.Type { - case TypeUnsigned: - u, err := strconv.ParseUint(val, 10, 64) - if err != nil { - u = 0 - } - return types.NewUintDatum(u), mysql.TypeLonglong, mysql.UnsignedFlag | mysql.BinaryFlag - case TypeBool: - optVal := int64(0) // OFF - if TiDBOptOn(val) { - optVal = 1 - } - return types.NewIntDatum(optVal), mysql.TypeLonglong, mysql.BinaryFlag - } - return types.NewStringDatum(val), mysql.TypeVarString, 0 -} - -// SkipInit returns true if when a new session is created we should "skip" copying -// an initial value to it (and call the SetSession func if it exists) -func (sv *SysVar) SkipInit() bool { - if sv.skipInit || sv.IsNoop { - return true - } - return !sv.HasSessionScope() -} - -// SkipSysvarCache returns true if the sysvar should not re-execute on peers -// This doesn't make sense for the GC variables because they are based in tikv -// tables. We'd effectively be reading and writing to the same table, which -// could be in an unsafe manner. In future these variables might be converted -// to not use a different table internally, but to do that we need to first -// fix upgrade/downgrade so we know that older servers won't be in the cluster -// which update only these values. -func (sv *SysVar) SkipSysvarCache() bool { - switch sv.Name { - case TiDBGCEnable, TiDBGCRunInterval, TiDBGCLifetime, - TiDBGCConcurrency, TiDBGCScanLockMode, TiDBExternalTS: - return true - } - return false -} - -var sysVars map[string]*SysVar -var sysVarsLock sync.RWMutex - -// RegisterSysVar adds a sysvar to the SysVars list -func RegisterSysVar(sv *SysVar) { - name := strings.ToLower(sv.Name) - sysVarsLock.Lock() - sysVars[name] = sv - sysVarsLock.Unlock() -} - -// UnregisterSysVar removes a sysvar from the SysVars list -// currently only used in tests. -func UnregisterSysVar(name string) { - name = strings.ToLower(name) - sysVarsLock.Lock() - delete(sysVars, name) - sysVarsLock.Unlock() -} - -// GetSysVar returns sys var info for name as key. -func GetSysVar(name string) *SysVar { - name = strings.ToLower(name) - sysVarsLock.RLock() - defer sysVarsLock.RUnlock() - - return sysVars[name] -} - -// SetSysVar sets a sysvar. In fact, SysVar is immutable. -// SetSysVar is implemented by register a new SysVar with the same name again. -// This will not propagate to the cluster, so it should only be -// used for instance scoped AUTO variables such as system_time_zone. -func SetSysVar(name string, value string) { - old := GetSysVar(name) - tmp := *old - tmp.Value = value - RegisterSysVar(&tmp) -} - -// GetSysVars deep copies the sysVars list under a RWLock -func GetSysVars() map[string]*SysVar { - sysVarsLock.RLock() - defer sysVarsLock.RUnlock() - m := make(map[string]*SysVar, len(sysVars)) - for name, sv := range sysVars { - tmp := *sv - m[name] = &tmp - } - return m -} - -// OrderByDependency orders the vars by dependency. The depended sys vars are in the front. -// Unknown sys vars are treated as not depended. -func OrderByDependency(names map[string]string) []string { - depended, notDepended := make([]string, 0, len(names)), make([]string, 0, len(names)) - sysVarsLock.RLock() - defer sysVarsLock.RUnlock() - for name := range names { - if sv, ok := sysVars[name]; ok && sv.Depended { - depended = append(depended, name) - } else { - notDepended = append(notDepended, name) - } - } - return append(depended, notDepended...) -} - -func init() { - sysVars = make(map[string]*SysVar) - setHintUpdatable(defaultSysVars) - // Destroy the map after init. - maps.Clear(isHintUpdatableVerified) - for _, v := range defaultSysVars { - RegisterSysVar(v) - } - for _, v := range noopSysVars { - v.IsNoop = true - RegisterSysVar(v) - } -} - -// GlobalVarAccessor is the interface for accessing global scope system and status variables. -type GlobalVarAccessor interface { - // GetGlobalSysVar gets the global system variable value for name. - GetGlobalSysVar(name string) (string, error) - // SetGlobalSysVar sets the global system variable name to value. - SetGlobalSysVar(ctx context.Context, name string, value string) error - // SetGlobalSysVarOnly sets the global system variable without calling the validation function or updating aliases. - SetGlobalSysVarOnly(ctx context.Context, name string, value string, updateLocal bool) error - // GetTiDBTableValue gets a value from mysql.tidb for the key 'name' - GetTiDBTableValue(name string) (string, error) - // SetTiDBTableValue sets a value+comment for the mysql.tidb key 'name' - SetTiDBTableValue(name, value, comment string) error -} diff --git a/sessionctx/variable/variable_test.go b/sessionctx/variable/variable_test.go deleted file mode 100644 index 9ed0c76a13622..0000000000000 --- a/sessionctx/variable/variable_test.go +++ /dev/null @@ -1,720 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package variable - -import ( - "context" - "encoding/json" - "fmt" - "runtime" - "slices" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestSysVar(t *testing.T) { - f := GetSysVar("autocommit") - require.NotNil(t, f) - - f = GetSysVar("wrong-var-name") - require.Nil(t, f) - - f = GetSysVar("explicit_defaults_for_timestamp") - require.NotNil(t, f) - require.Equal(t, "ON", f.Value) - - f = GetSysVar("port") - require.NotNil(t, f) - require.Equal(t, "4000", f.Value) - - f = GetSysVar("tidb_low_resolution_tso") - require.Equal(t, "OFF", f.Value) - - f = GetSysVar("tidb_replica_read") - require.Equal(t, "leader", f.Value) - - f = GetSysVar("tidb_enable_table_partition") - require.Equal(t, "ON", f.Value) - - f = GetSysVar("version_compile_os") - require.Equal(t, runtime.GOOS, f.Value) - - f = GetSysVar("version_compile_machine") - require.Equal(t, runtime.GOARCH, f.Value) - - // default enable vectorized_expression - f = GetSysVar("tidb_enable_vectorized_expression") - require.Equal(t, "ON", f.Value) -} - -func TestError(t *testing.T) { - kvErrs := []*terror.Error{ - ErrUnsupportedValueForVar, - ErrUnknownSystemVar, - ErrIncorrectScope, - ErrUnknownTimeZone, - ErrReadOnly, - ErrWrongValueForVar, - ErrWrongTypeForVar, - ErrTruncatedWrongValue, - ErrMaxPreparedStmtCountReached, - ErrUnsupportedIsolationLevel, - } - for _, err := range kvErrs { - require.True(t, terror.ToSQLError(err).Code != mysql.ErrUnknown) - } -} - -func TestRegistrationOfNewSysVar(t *testing.T) { - count := len(GetSysVars()) - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeBool, SetSession: func(s *SessionVars, val string) error { - return fmt.Errorf("set should fail") - }} - - RegisterSysVar(&sv) - require.Len(t, GetSysVars(), count+1) - - sysVar := GetSysVar("mynewsysvar") - require.NotNil(t, sysVar) - - vars := NewSessionVars(nil) - - // It is a boolean, try to set it to a bogus value - _, err := sysVar.Validate(vars, "ABCD", ScopeSession) - require.Error(t, err) - - // Boolean oN or 1 converts to canonical ON or OFF - normalizedVal, err := sysVar.Validate(vars, "oN", ScopeSession) - require.Equal(t, "ON", normalizedVal) - require.NoError(t, err) - normalizedVal, err = sysVar.Validate(vars, "0", ScopeSession) - require.Equal(t, "OFF", normalizedVal) - require.NoError(t, err) - - err = sysVar.SetSessionFromHook(vars, "OFF") // default is on - require.Equal(t, "set should fail", err.Error()) - - // Test unregistration restores previous count - UnregisterSysVar("mynewsysvar") - require.Equal(t, len(GetSysVars()), count) -} - -func TestIntValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeInt, MinValue: 10, MaxValue: 300, AllowAutoValue: true} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "oN", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - val, err := sv.Validate(vars, "301", ScopeSession) - require.NoError(t, err) - require.Equal(t, "300", val) - - val, err = sv.Validate(vars, "5", ScopeSession) - require.NoError(t, err) - require.Equal(t, "10", val) - - val, err = sv.Validate(vars, "300", ScopeSession) - require.NoError(t, err) - require.Equal(t, "300", val) - - // out of range but permitted due to auto value - val, err = sv.Validate(vars, "-1", ScopeSession) - require.NoError(t, err) - require.Equal(t, "-1", val) -} - -func TestUintValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeUnsigned, MinValue: 10, MaxValue: 300, AllowAutoValue: true} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "oN", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - _, err = sv.Validate(vars, "", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - val, err := sv.Validate(vars, "301", ScopeSession) - require.NoError(t, err) - require.Equal(t, "300", val) - - val, err = sv.Validate(vars, "-301", ScopeSession) - require.NoError(t, err) - require.Equal(t, "10", val) - - _, err = sv.Validate(vars, "-ERR", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - val, err = sv.Validate(vars, "5", ScopeSession) - require.NoError(t, err) - require.Equal(t, "10", val) - - val, err = sv.Validate(vars, "300", ScopeSession) - require.NoError(t, err) - require.Equal(t, "300", val) - - // out of range but permitted due to auto value - val, err = sv.Validate(vars, "-1", ScopeSession) - require.NoError(t, err) - require.Equal(t, "-1", val) -} - -func TestEnumValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "randomstring", ScopeSession) - require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of 'randomstring'", err.Error()) - - val, err := sv.Validate(vars, "oFf", ScopeSession) - require.NoError(t, err) - require.Equal(t, "OFF", val) - - val, err = sv.Validate(vars, "On", ScopeSession) - require.NoError(t, err) - require.Equal(t, "ON", val) - - val, err = sv.Validate(vars, "auto", ScopeSession) - require.NoError(t, err) - require.Equal(t, "AUTO", val) - - // Also settable by numeric offset. - val, err = sv.Validate(vars, "2", ScopeSession) - require.NoError(t, err) - require.Equal(t, "AUTO", val) -} - -func TestDurationValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "10m0s", Type: TypeDuration, MinValue: int64(time.Second), MaxValue: uint64(time.Hour)} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "1hr", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - val, err := sv.Validate(vars, "1ms", ScopeSession) - require.NoError(t, err) - require.Equal(t, "1s", val) // truncates to min - - val, err = sv.Validate(vars, "2h10m", ScopeSession) - require.NoError(t, err) - require.Equal(t, "1h0m0s", val) // truncates to max -} - -func TestFloatValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "10m0s", Type: TypeFloat, MinValue: 2, MaxValue: 7} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "stringval", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - _, err = sv.Validate(vars, "", ScopeSession) - require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error()) - - val, err := sv.Validate(vars, "1.1", ScopeSession) - require.NoError(t, err) - require.Equal(t, "2", val) // truncates to min - - val, err = sv.Validate(vars, "22", ScopeSession) - require.NoError(t, err) - require.Equal(t, "7", val) // truncates to max -} - -func TestBoolValidation(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool} - vars := NewSessionVars(nil) - - _, err := sv.Validate(vars, "0.000", ScopeSession) - require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of '0.000'", err.Error()) - _, err = sv.Validate(vars, "1.000", ScopeSession) - require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of '1.000'", err.Error()) - val, err := sv.Validate(vars, "0", ScopeSession) - require.NoError(t, err) - require.Equal(t, Off, val) - val, err = sv.Validate(vars, "1", ScopeSession) - require.NoError(t, err) - require.Equal(t, On, val) - val, err = sv.Validate(vars, "OFF", ScopeSession) - require.NoError(t, err) - require.Equal(t, Off, val) - val, err = sv.Validate(vars, "ON", ScopeSession) - require.NoError(t, err) - require.Equal(t, On, val) - val, err = sv.Validate(vars, "off", ScopeSession) - require.NoError(t, err) - require.Equal(t, Off, val) - val, err = sv.Validate(vars, "on", ScopeSession) - require.NoError(t, err) - require.Equal(t, On, val) - - // test AutoConvertNegativeBool - sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool, AutoConvertNegativeBool: true} - val, err = sv.Validate(vars, "-1", ScopeSession) - require.NoError(t, err) - require.Equal(t, On, val) - val, err = sv.Validate(vars, "1", ScopeSession) - require.NoError(t, err) - require.Equal(t, On, val) - val, err = sv.Validate(vars, "0", ScopeSession) - require.NoError(t, err) - require.Equal(t, Off, val) -} - -func TestTimeValidation(t *testing.T) { - sv := SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: "23:59 +0000", Type: TypeTime} - vars := NewSessionVars(nil) - - val, err := sv.Validate(vars, "23:59 +0000", ScopeSession) - require.NoError(t, err) - require.Equal(t, "23:59 +0000", val) - - val, err = sv.Validate(vars, "3:00 +0000", ScopeSession) - require.NoError(t, err) - require.Equal(t, "03:00 +0000", val) - - _, err = sv.Validate(vars, "0.000", ScopeSession) - require.Error(t, err) -} - -func TestGetNativeValType(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeBool} - - nativeVal, nativeType, flag := sv.GetNativeValType("ON") - require.Equal(t, mysql.TypeLonglong, nativeType) - require.Equal(t, mysql.BinaryFlag, flag) - require.Equal(t, types.NewIntDatum(1), nativeVal) - - nativeVal, nativeType, flag = sv.GetNativeValType("OFF") - require.Equal(t, mysql.TypeLonglong, nativeType) - require.Equal(t, mysql.BinaryFlag, flag) - require.Equal(t, types.NewIntDatum(0), nativeVal) - - nativeVal, nativeType, flag = sv.GetNativeValType("bogus") - require.Equal(t, mysql.TypeLonglong, nativeType) - require.Equal(t, mysql.BinaryFlag, flag) - require.Equal(t, types.NewIntDatum(0), nativeVal) - - sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: Off, Type: TypeUnsigned} - nativeVal, nativeType, flag = sv.GetNativeValType("1234") - require.Equal(t, mysql.TypeLonglong, nativeType) - require.Equal(t, mysql.UnsignedFlag|mysql.BinaryFlag, flag) - require.Equal(t, types.NewUintDatum(1234), nativeVal) - nativeVal, nativeType, flag = sv.GetNativeValType("bogus") - require.Equal(t, mysql.TypeLonglong, nativeType) - require.Equal(t, mysql.UnsignedFlag|mysql.BinaryFlag, flag) - require.Equal(t, types.NewUintDatum(0), nativeVal) // converts to zero - - sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "abc"} - nativeVal, nativeType, flag = sv.GetNativeValType("1234") - require.Equal(t, mysql.TypeVarString, nativeType) - require.Equal(t, uint(0), flag) - require.Equal(t, types.NewStringDatum("1234"), nativeVal) -} - -func TestSynonyms(t *testing.T) { - sysVar := GetSysVar(TxnIsolation) - require.NotNil(t, sysVar) - - vars := NewSessionVars(nil) - - // It does not permit SERIALIZABLE by default. - _, err := sysVar.Validate(vars, "SERIALIZABLE", ScopeSession) - require.Error(t, err) - require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error()) - - // Enable Skip isolation check - require.Nil(t, GetSysVar(TiDBSkipIsolationLevelCheck).SetSessionFromHook(vars, "ON")) - - // Serializable is now permitted. - _, err = sysVar.Validate(vars, "SERIALIZABLE", ScopeSession) - require.NoError(t, err) - - // Currently TiDB returns a warning because of SERIALIZABLE, but in future - // it may also return a warning because TxnIsolation is deprecated. - - warn := vars.StmtCtx.GetWarnings()[0].Err - require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", warn.Error()) - - require.Nil(t, sysVar.SetSessionFromHook(vars, "SERIALIZABLE")) - - // When we set TxnIsolation, it also updates TransactionIsolation. - require.Equal(t, "SERIALIZABLE", vars.systems[TxnIsolation]) - require.Equal(t, vars.systems[TxnIsolation], vars.systems[TransactionIsolation]) -} - -func TestDeprecation(t *testing.T) { - sysVar := GetSysVar(TiDBIndexLookupConcurrency) - require.NotNil(t, sysVar) - - vars := NewSessionVars(nil) - - _, err := sysVar.Validate(vars, "123", ScopeSession) - require.NoError(t, err) - - // There was no error but there is a deprecation warning. - warn := vars.StmtCtx.GetWarnings()[0].Err - require.Equal(t, "[variable:1287]'tidb_index_lookup_concurrency' is deprecated and will be removed in a future release. Please use tidb_executor_concurrency instead", warn.Error()) -} - -func TestScope(t *testing.T) { - sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.True(t, sv.HasSessionScope()) - require.True(t, sv.HasGlobalScope()) - require.False(t, sv.HasInstanceScope()) - require.False(t, sv.HasNoneScope()) - - sv = SysVar{Scope: ScopeGlobal, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.False(t, sv.HasSessionScope()) - require.True(t, sv.HasGlobalScope()) - require.False(t, sv.HasInstanceScope()) - require.False(t, sv.HasNoneScope()) - - sv = SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.True(t, sv.HasSessionScope()) - require.False(t, sv.HasGlobalScope()) - require.False(t, sv.HasInstanceScope()) - require.False(t, sv.HasNoneScope()) - - sv = SysVar{Scope: ScopeNone, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.False(t, sv.HasSessionScope()) - require.False(t, sv.HasGlobalScope()) - require.False(t, sv.HasInstanceScope()) - require.True(t, sv.HasNoneScope()) - - sv = SysVar{Scope: ScopeInstance, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.False(t, sv.HasSessionScope()) - require.False(t, sv.HasGlobalScope()) - require.True(t, sv.HasInstanceScope()) - require.False(t, sv.HasNoneScope()) - - sv = SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}} - require.Error(t, sv.validateScope(ScopeGlobal)) -} - -func TestBuiltInCase(t *testing.T) { - // All Sysvars should have lower case names. - // This tests builtins. - for name := range GetSysVars() { - require.Equal(t, strings.ToLower(name), name) - } -} - -// TestIsNoop is used by the documentation to auto-generate docs for real sysvars. -func TestIsNoop(t *testing.T) { - sv := GetSysVar(TiDBMultiStatementMode) - require.False(t, sv.IsNoop) - - sv = GetSysVar(InnodbLockWaitTimeout) - require.False(t, sv.IsNoop) - - sv = GetSysVar(InnodbFastShutdown) - require.True(t, sv.IsNoop) - - sv = GetSysVar(ReadOnly) - require.True(t, sv.IsNoop) -} - -// TestDefaultValuesAreSettable that sysvars defaults are logically valid. i.e. -// the default itself must validate without error provided the scope and read-only is correct. -// The default values should also be normalized for consistency. -func TestDefaultValuesAreSettable(t *testing.T) { - vars := NewSessionVars(nil) - vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests() - for _, sv := range GetSysVars() { - if sv.HasSessionScope() && !sv.ReadOnly { - val, err := sv.Validate(vars, sv.Value, ScopeSession) - require.NoError(t, err) - require.Equal(t, val, sv.Value) - } - - if sv.HasGlobalScope() && !sv.ReadOnly { - val, err := sv.Validate(vars, sv.Value, ScopeGlobal) - require.NoError(t, err) - require.Equal(t, val, sv.Value) - } - } -} - -func TestLimitBetweenVariable(t *testing.T) { - require.Less(t, DefTiDBGOGCTunerThreshold+0.05, DefTiDBServerMemoryLimitGCTrigger) -} - -// TestSysVarNameIsLowerCase tests that no new sysvars are added with uppercase characters. -// In MySQL variables are always lowercase, and can be set in a case-insensitive way. -func TestSysVarNameIsLowerCase(t *testing.T) { - for _, sv := range GetSysVars() { - require.Equal(t, strings.ToLower(sv.Name), sv.Name, "sysvar name contains uppercase characters") - } -} - -// TestSettersandGetters tests that sysvars are logically correct with getter and setter functions. -// i.e. it doesn't make sense to have a SetSession function on a variable that is only globally scoped. -func TestSettersandGetters(t *testing.T) { - for _, sv := range GetSysVars() { - if !sv.HasSessionScope() { - require.Nil(t, sv.SetSession) - require.Nil(t, sv.GetSession) - } - if !sv.HasGlobalScope() && !sv.HasInstanceScope() { - require.Nil(t, sv.SetGlobal) - if sv.Name == Timestamp { - // The Timestamp sysvar will have GetGlobal func even though it does not have global scope. - // It's GetGlobal func will only be called when "set timestamp = default". - continue - } - require.Nil(t, sv.GetGlobal) - } - } -} - -// TestSkipInitIsUsed ensures that no new variables are added with skipInit: true. -// This feature is deprecated, and if you need to run code to differentiate between init and "SET" (rare), -// you can instead check if s.StmtCtx.StmtType == "Set". -// The reason it is deprecated is that the behavior is typically wrong: -// it means session settings won't inherit from global and don't apply until you first set -// them in each session. This is a very weird behavior. -// See: https://github.com/pingcap/tidb/issues/35051 -func TestSkipInitIsUsed(t *testing.T) { - for _, sv := range GetSysVars() { - if sv.skipInit { - // skipInit only ever applied to session scope, so if anyone is setting it on - // a variable without session, that doesn't make sense. - require.True(t, sv.HasSessionScope(), fmt.Sprintf("skipInit has no effect on a variable without session scope: %s", sv.Name)) - // Since SetSession is the "init function" there is no init function to skip. - require.NotNil(t, sv.SetSession, fmt.Sprintf("skipInit has no effect on variables without an init (setsession) func: %s", sv.Name)) - // Skipinit has no use on noop funcs, since noop funcs always skipinit. - require.False(t, sv.IsNoop, fmt.Sprintf("skipInit has no effect on noop variables: %s", sv.Name)) - - // Test for variables that have a default of "0" or "OFF" - // If it is session-only scoped there is likely no bug now. - // If it is also global-scoped, then there is a bug as soon as the global changes. - if !(sv.Name == RandSeed1 || sv.Name == RandSeed2) { - // The bug is because the tests might not realize the SetSession func was not called on init, - // because it would initialize some session field to the empty value anyway. - require.NotEqual(t, "0", sv.Value, fmt.Sprintf("default value is zero: %s", sv.Name)) - require.NotEqual(t, "OFF", sv.Value, fmt.Sprintf("default value is OFF: %s", sv.Name)) - } - - // Many of these variables might allow skipInit to be removed, - // they need to be checked first. The purpose of this test is to make - // sure we don't introduce any new variables with skipInit, which seems - // to be a problem. - switch sv.Name { - case TiDBTxnScope, - TiDBSnapshot, - TiDBEnableChunkRPC, - TxnIsolationOneShot, - TiDBDDLReorgPriority, - TiDBSlowQueryFile, - TiDBWaitSplitRegionFinish, - TiDBWaitSplitRegionTimeout, - TiDBMetricSchemaStep, - TiDBMetricSchemaRangeDuration, - RandSeed1, - RandSeed2, - CollationDatabase, - CollationConnection, - CharsetDatabase, - CharacterSetConnection, - CharacterSetServer, - TiDBOptTiFlashConcurrencyFactor, - TiDBOptSeekFactor: - continue - } - require.Equal(t, false, sv.skipInit, fmt.Sprintf("skipInit should not be set on new system variables. variable %s is in violation", sv.Name)) - } - } -} - -func TestScopeToString(t *testing.T) { - require.Equal(t, "GLOBAL", ScopeGlobal.String()) - require.Equal(t, "SESSION", ScopeSession.String()) - require.Equal(t, "INSTANCE", ScopeInstance.String()) - require.Equal(t, "NONE", ScopeNone.String()) - tmp := ScopeSession + ScopeGlobal - require.Equal(t, "SESSION,GLOBAL", tmp.String()) - // this is not currently possible, but might be in future. - // *but* global + instance is not possible. these are mutually exclusive by design. - tmp = ScopeSession + ScopeInstance - require.Equal(t, "SESSION,INSTANCE", tmp.String()) -} - -func TestValidateWithRelaxedValidation(t *testing.T) { - sv := GetSysVar(SecureAuth) - vars := NewSessionVars(nil) - val := sv.ValidateWithRelaxedValidation(vars, "1", ScopeGlobal) - require.Equal(t, "ON", val) - - // Relaxed validation catches the error and squashes it. - // The incorrect value is returned as-is. - // I am not sure this is the correct behavior, we might need to - // change it to return the default instead in future. - sv = GetSysVar(DefaultAuthPlugin) - val = sv.ValidateWithRelaxedValidation(vars, "RandomText", ScopeGlobal) - require.Equal(t, "RandomText", val) - - // Validation func fails, the error is also caught and squashed. - // The incorrect value is returned as-is. - sv = GetSysVar(InitConnect) - val = sv.ValidateWithRelaxedValidation(vars, "RandomText - should be valid SQL", ScopeGlobal) - require.Equal(t, "RandomText - should be valid SQL", val) -} - -func TestInstanceConfigHasMatchingSysvar(t *testing.T) { - // This tests that each item in [instance] has a sysvar of the same name. - // The whole point of moving items to [instance] is to unify the name between - // config and sysvars. See: docs/design/2021-12-08-instance-scope.md#introduction - cfg, err := config.GetJSONConfig() - require.NoError(t, err) - var v interface{} - json.Unmarshal([]byte(cfg), &v) - data := v.(map[string]interface{}) - for k, v := range data { - if k != "instance" { - continue - } - instanceSection := v.(map[string]interface{}) - for instanceName := range instanceSection { - // Need to check there is a sysvar named instanceName. - sv := GetSysVar(instanceName) - require.NotNil(t, sv, fmt.Sprintf("config option: instance.%v requires a matching sysvar of the same name", instanceName)) - } - } -} - -func TestInstanceScope(t *testing.T) { - // Instance scope used to be settable via "SET SESSION", which is weird to any MySQL user. - // It is now settable via SET GLOBAL, but to work correctly a sysvar can only ever - // be INSTANCE scoped or GLOBAL scoped, never *both* at the same time (at least for now). - // Otherwise the semantics are confusing to users for how precedence applies. - - for _, sv := range GetSysVars() { - require.False(t, sv.HasGlobalScope() && sv.HasInstanceScope(), "sysvar %s has both instance and global scope", sv.Name) - if sv.HasInstanceScope() { - require.Nil(t, sv.GetSession) - require.Nil(t, sv.SetSession) - } - } - - count := len(GetSysVars()) - sv := SysVar{Scope: ScopeInstance, Name: "newinstancesysvar", Value: On, Type: TypeBool, - SetGlobal: func(_ context.Context, s *SessionVars, val string) error { - return fmt.Errorf("set should fail") - }, - GetGlobal: func(_ context.Context, s *SessionVars) (string, error) { - return "", fmt.Errorf("get should fail") - }, - } - - RegisterSysVar(&sv) - require.Len(t, GetSysVars(), count+1) - - sysVar := GetSysVar("newinstancesysvar") - require.NotNil(t, sysVar) - - vars := NewSessionVars(nil) - - // It is a boolean, try to set it to a bogus value - _, err := sysVar.Validate(vars, "ABCD", ScopeInstance) - require.Error(t, err) - - // Boolean oN or 1 converts to canonical ON or OFF - normalizedVal, err := sysVar.Validate(vars, "oN", ScopeInstance) - require.Equal(t, "ON", normalizedVal) - require.NoError(t, err) - normalizedVal, err = sysVar.Validate(vars, "0", ScopeInstance) - require.Equal(t, "OFF", normalizedVal) - require.NoError(t, err) - - err = sysVar.SetGlobalFromHook(context.Background(), vars, "OFF", true) // default is on - require.Equal(t, "set should fail", err.Error()) - - // Test unregistration restores previous count - UnregisterSysVar("newinstancesysvar") - require.Equal(t, len(GetSysVars()), count) -} - -func TestSetSysVar(t *testing.T) { - vars := NewSessionVars(nil) - vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests() - originalVal := GetSysVar(SystemTimeZone).Value - SetSysVar(SystemTimeZone, "America/New_York") - require.Equal(t, "America/New_York", GetSysVar(SystemTimeZone).Value) - // Test alternative Get - val, err := GetSysVar(SystemTimeZone).GetGlobalFromHook(context.Background(), vars) - require.Nil(t, err) - require.Equal(t, "America/New_York", val) - SetSysVar(SystemTimeZone, originalVal) // restore - require.Equal(t, originalVal, GetSysVar(SystemTimeZone).Value) -} - -func TestSkipSysvarCache(t *testing.T) { - require.True(t, GetSysVar(TiDBGCEnable).SkipSysvarCache()) - require.True(t, GetSysVar(TiDBGCRunInterval).SkipSysvarCache()) - require.True(t, GetSysVar(TiDBGCLifetime).SkipSysvarCache()) - require.True(t, GetSysVar(TiDBGCConcurrency).SkipSysvarCache()) - require.True(t, GetSysVar(TiDBGCScanLockMode).SkipSysvarCache()) - require.False(t, GetSysVar(TiDBEnableAsyncCommit).SkipSysvarCache()) -} - -func TestTimeValidationWithTimezone(t *testing.T) { - sv := SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: "23:59 +0000", Type: TypeTime} - vars := NewSessionVars(nil) - - // In timezone UTC - vars.TimeZone = time.UTC - val, err := sv.Validate(vars, "23:59", ScopeSession) - require.NoError(t, err) - require.Equal(t, "23:59 +0000", val) - - // In timezone Asia/Shanghai - vars.TimeZone, err = time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - val, err = sv.Validate(vars, "23:59", ScopeSession) - require.NoError(t, err) - require.Equal(t, "23:59 +0800", val) -} - -func TestOrderByDependency(t *testing.T) { - // Some other exceptions: - // - tidb_snapshot and tidb_read_staleness can not be set at the same time. It doesn't affect dependency. - vars := map[string]string{ - "unknown": "1", - TxReadOnly: "1", - SQLAutoIsNull: "1", - TiDBEnableNoopFuncs: "1", - TiDBEnforceMPPExecution: "1", - TiDBAllowMPPExecution: "1", - TiDBTxnScope: kv.LocalTxnScope, - TiDBEnableLocalTxn: "1", - TiDBEnablePlanReplayerContinuousCapture: "1", - TiDBEnableHistoricalStats: "1", - } - names := OrderByDependency(vars) - require.Greater(t, slices.Index(names, TxReadOnly), slices.Index(names, TiDBEnableNoopFuncs)) - require.Greater(t, slices.Index(names, SQLAutoIsNull), slices.Index(names, TiDBEnableNoopFuncs)) - require.Greater(t, slices.Index(names, TiDBEnforceMPPExecution), slices.Index(names, TiDBAllowMPPExecution)) - // Depended variables below are global variables, so actually it doesn't matter. - require.Greater(t, slices.Index(names, TiDBTxnScope), slices.Index(names, TiDBEnableLocalTxn)) - require.Greater(t, slices.Index(names, TiDBEnablePlanReplayerContinuousCapture), slices.Index(names, TiDBEnableHistoricalStats)) - require.Contains(t, names, "unknown") -} diff --git a/sessiontxn/BUILD.bazel b/sessiontxn/BUILD.bazel deleted file mode 100644 index 4a2e57f093c37..0000000000000 --- a/sessiontxn/BUILD.bazel +++ /dev/null @@ -1,55 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sessiontxn", - srcs = [ - "failpoint.go", - "future.go", - "interface.go", - ], - importpath = "github.com/pingcap/tidb/sessiontxn", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//parser/ast", - "//sessionctx", - "//util/stringutil", - ], -) - -go_test( - name = "sessiontxn_test", - timeout = "short", - srcs = [ - "txn_context_test.go", - "txn_manager_test.go", - "txn_rc_tso_optimize_test.go", - ], - flaky = True, - shard_count = 25, - deps = [ - ":sessiontxn", - "//domain", - "//errno", - "//expression", - "//infoschema", - "//kv", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//sessiontxn/internal", - "//sessiontxn/isolation", - "//sessiontxn/staleread", - "//table/temptable", - "//tablecodec", - "//testkit", - "//testkit/testfork", - "//testkit/testsetup", - "//tests/realtikvtest", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessiontxn/failpoint.go b/sessiontxn/failpoint.go deleted file mode 100644 index 25c13a3bc80e2..0000000000000 --- a/sessiontxn/failpoint.go +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package sessiontxn - -import ( - "fmt" - "time" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/stringutil" -) - -// AssertRecordsKey is used to save failPoint invoke records -// Only for test -var AssertRecordsKey stringutil.StringerStr = "assertTxnManagerRecords" - -// AssertTxnInfoSchemaKey is used to set the expected infoschema that should be check in failPoint -// Only for test -var AssertTxnInfoSchemaKey stringutil.StringerStr = "assertTxnInfoSchemaKey" - -// AssertTxnInfoSchemaAfterRetryKey is used to set the expected infoschema that should be check in failPoint after retry -// Only for test -var AssertTxnInfoSchemaAfterRetryKey stringutil.StringerStr = "assertTxnInfoSchemaAfterRetryKey" - -// BreakPointBeforeExecutorFirstRun is the key for the stop point where session stops before executor's first run -// Only for test -var BreakPointBeforeExecutorFirstRun = "beforeExecutorFirstRun" - -// BreakPointOnStmtRetryAfterLockError s the key for the stop point where session stops after OnStmtRetry when lock error happens -// Only for test -var BreakPointOnStmtRetryAfterLockError = "lockErrorAndThenOnStmtRetryCalled" - -// TsoRequestCount is the key for recording tso request counts in some places -var TsoRequestCount stringutil.StringerStr = "tsoRequestCount" - -// TsoWaitCount doesn't include begin and commit -var TsoWaitCount stringutil.StringerStr = "tsoWaitCount" - -// TsoUseConstantCount is the key for constant tso counter -var TsoUseConstantCount stringutil.StringerStr = "tsoUseConstantCount" - -// CallOnStmtRetryCount is the key for recording calling OnStmtRetry at RC isolation level -var CallOnStmtRetryCount stringutil.StringerStr = "callOnStmtRetryCount" - -// AssertLockErr is used to record the lock errors we encountered -// Only for test -var AssertLockErr stringutil.StringerStr = "assertLockError" - -// RecordAssert is used only for test -func RecordAssert(sctx sessionctx.Context, name string, value interface{}) { - records, ok := sctx.Value(AssertRecordsKey).(map[string]interface{}) - if !ok { - records = make(map[string]interface{}) - sctx.SetValue(AssertRecordsKey, records) - } - records[name] = value -} - -// AssertTxnManagerInfoSchema is used only for test -func AssertTxnManagerInfoSchema(sctx sessionctx.Context, is interface{}) { - assertVersion := func(expected interface{}) { - if expected == nil { - return - } - - expectVer := expected.(infoschema.InfoSchema).SchemaMetaVersion() - gotVer := GetTxnManager(sctx).GetTxnInfoSchema().SchemaMetaVersion() - if gotVer != expectVer { - panic(fmt.Sprintf("Txn schema version not match, expect:%d, got:%d", expectVer, gotVer)) - } - } - - if localTables := sctx.GetSessionVars().LocalTemporaryTables; localTables != nil { - got, ok := GetTxnManager(sctx).GetTxnInfoSchema().(*infoschema.SessionExtendedInfoSchema) - if !ok { - panic("Expected to be a SessionExtendedInfoSchema") - } - - if got.LocalTemporaryTables != localTables { - panic("Local tables should be the same with the one in session") - } - } - - assertVersion(is) - assertVersion(sctx.Value(AssertTxnInfoSchemaKey)) -} - -// AssertTxnManagerReadTS is used only for test -func AssertTxnManagerReadTS(sctx sessionctx.Context, expected uint64) { - actual, err := GetTxnManager(sctx).GetStmtReadTS() - if err != nil { - panic(err) - } - - if actual != expected { - panic(fmt.Sprintf("Txn read ts not match, expect:%d, got:%d", expected, actual)) - } -} - -// AddAssertEntranceForLockError is used only for test -func AddAssertEntranceForLockError(sctx sessionctx.Context, name string) { - records, ok := sctx.Value(AssertLockErr).(map[string]int) - if !ok { - records = make(map[string]int) - sctx.SetValue(AssertLockErr, records) - } - if v, ok := records[name]; ok { - records[name] = v + 1 - } else { - records[name] = 1 - } -} - -// TsoRequestCountInc is used only for test -// When it is called, there is a tso cmd request. -func TsoRequestCountInc(sctx sessionctx.Context) { - count, ok := sctx.Value(TsoRequestCount).(uint64) - if !ok { - count = 0 - } - count++ - sctx.SetValue(TsoRequestCount, count) -} - -// TsoWaitCountInc is used only for test -// When it is called, there is a waiting tso operation -func TsoWaitCountInc(sctx sessionctx.Context) { - count, ok := sctx.Value(TsoWaitCount).(uint64) - if !ok { - count = 0 - } - count++ - sctx.SetValue(TsoWaitCount, count) -} - -// TsoUseConstantCountInc is used to test constant tso count -func TsoUseConstantCountInc(sctx sessionctx.Context) { - count, ok := sctx.Value(TsoUseConstantCount).(uint64) - if !ok { - count = 0 - } - count++ - sctx.SetValue(TsoUseConstantCount, count) -} - -// OnStmtRetryCountInc is used only for test. -// When it is called, there is calling `(p *PessimisticRCTxnContextProvider) OnStmtRetry`. -func OnStmtRetryCountInc(sctx sessionctx.Context) { - count, ok := sctx.Value(CallOnStmtRetryCount).(int) - if !ok { - count = 0 - } - count++ - sctx.SetValue(CallOnStmtRetryCount, count) -} - -// ExecTestHook is used only for test. It consumes hookKey in session wait do what it gets from it. -func ExecTestHook(sctx sessionctx.Context, hookKey fmt.Stringer) { - c := sctx.Value(hookKey) - if ch, ok := c.(chan func()); ok { - select { - case fn := <-ch: - fn() - case <-time.After(time.Second * 10): - panic("timeout waiting for chan") - } - } -} diff --git a/sessiontxn/interface.go b/sessiontxn/interface.go deleted file mode 100644 index 6969895bbd5a7..0000000000000 --- a/sessiontxn/interface.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package sessiontxn - -import ( - "context" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" -) - -// EnterNewTxnType is the type to enter a new txn -type EnterNewTxnType int - -const ( - // EnterNewTxnDefault means to enter a new txn. Its behavior is more straight-forward - // just starting a new txn right now without any scenario assumptions. - EnterNewTxnDefault EnterNewTxnType = iota - // EnterNewTxnWithBeginStmt indicates to enter a new txn when execute 'BEGIN' or 'START TRANSACTION' - EnterNewTxnWithBeginStmt - // EnterNewTxnBeforeStmt indicates to enter a new txn before each statement when the txn is not present - // If `EnterNewTxnBeforeStmt` is used, the new txn will always act as the 'lazy' mode. That means the inner transaction - // is only activated when needed to reduce unnecessary overhead. - EnterNewTxnBeforeStmt - // EnterNewTxnWithReplaceProvider indicates to replace the current provider. Now only stale read are using this - EnterNewTxnWithReplaceProvider -) - -// EnterNewTxnRequest is the request when entering a new transaction -type EnterNewTxnRequest struct { - // Type is the type for new entering a new txn - Type EnterNewTxnType - // provider is the context provider - Provider TxnContextProvider - // txnMode is the transaction mode for the new txn. It has 3 values: `ast.Pessimistic` ,`ast.Optimistic` or empty/ - // When the value is empty, it means the value will be determined from sys vars. - TxnMode string - // causalConsistencyOnly means whether enable causal consistency for transactions, default is false - CausalConsistencyOnly bool - // staleReadTS indicates the read ts for the stale read transaction. - // The default value is zero which means not a stale read transaction. - StaleReadTS uint64 -} - -// StmtErrorHandlePoint is where the error is being handled -type StmtErrorHandlePoint int - -const ( - // StmtErrAfterQuery means we are handling an error after the query failed - StmtErrAfterQuery StmtErrorHandlePoint = iota - // StmtErrAfterPessimisticLock means we are handling an error after pessimistic lock failed. - StmtErrAfterPessimisticLock -) - -// StmtErrorAction is the next action advice when an error occurs when executing a statement -type StmtErrorAction int - -const ( - // StmtActionError means the error should be returned directly without any retry - StmtActionError StmtErrorAction = iota - // StmtActionRetryReady means the error is caused by this component, and it is ready for retry. - StmtActionRetryReady - // StmtActionNoIdea means the error is not caused by this component, and whether retry or not should be determined by other components. - // If the user do not know whether to retry or not, it is advised to return the original error. - StmtActionNoIdea -) - -// ErrorAction returns StmtActionError with specified error -func ErrorAction(err error) (StmtErrorAction, error) { - return StmtActionError, err -} - -// RetryReady returns StmtActionRetryReady, nil -func RetryReady() (StmtErrorAction, error) { - return StmtActionRetryReady, nil -} - -// NoIdea returns StmtActionNoIdea, nil -func NoIdea() (StmtErrorAction, error) { - return StmtActionNoIdea, nil -} - -// TxnAdvisable providers a collection of optimizations within transaction -type TxnAdvisable interface { - // AdviseWarmup provides warmup for inner state - AdviseWarmup() error - // AdviseOptimizeWithPlan providers optimization according to the plan - AdviseOptimizeWithPlan(plan interface{}) error -} - -// OptimizeWithPlanAndThenWarmUp first do `AdviseOptimizeWithPlan` to optimize the txn with plan -// and then do `AdviseWarmup` to do some tso fetch if necessary -func OptimizeWithPlanAndThenWarmUp(sctx sessionctx.Context, plan interface{}) error { - txnManager := GetTxnManager(sctx) - if err := txnManager.AdviseOptimizeWithPlan(plan); err != nil { - return err - } - return txnManager.AdviseWarmup() -} - -// TxnContextProvider provides txn context -type TxnContextProvider interface { - TxnAdvisable - // GetTxnInfoSchema returns the information schema used by txn - GetTxnInfoSchema() infoschema.InfoSchema - // GetTxnScope returns the current txn scope - GetTxnScope() string - // GetReadReplicaScope returns the read replica scope - GetReadReplicaScope() string - // GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) - GetStmtReadTS() (uint64, error) - // GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update - GetStmtForUpdateTS() (uint64, error) - // GetSnapshotWithStmtReadTS gets snapshot with read ts - GetSnapshotWithStmtReadTS() (kv.Snapshot, error) - // GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts - GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) - - // OnInitialize is the hook that should be called when enter a new txn with this provider - OnInitialize(ctx context.Context, enterNewTxnType EnterNewTxnType) error - // OnStmtStart is the hook that should be called when a new statement started - OnStmtStart(ctx context.Context, node ast.StmtNode) error - // OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or - // a pessimistic select-for-update statement. - OnPessimisticStmtStart(ctx context.Context) error - // OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or - // select-for-update statement. - OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error - // OnStmtErrorForNextAction is the hook that should be called when a new statement get an error - OnStmtErrorForNextAction(ctx context.Context, point StmtErrorHandlePoint, err error) (StmtErrorAction, error) - // OnStmtRetry is the hook that should be called when a statement is retried internally. - OnStmtRetry(ctx context.Context) error - // OnStmtCommit is the hook that should be called when a statement is executed successfully. - OnStmtCommit(ctx context.Context) error - // OnStmtRollback is the hook that should be called when a statement fails to execute. - OnStmtRollback(ctx context.Context, isForPessimisticRetry bool) error - // OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. - OnLocalTemporaryTableCreated() - // ActivateTxn activates the transaction. - ActivateTxn() (kv.Transaction, error) -} - -// TxnManager is an interface providing txn context management in session -type TxnManager interface { - TxnAdvisable - // GetTxnInfoSchema returns the information schema used by txn - // If the session is not in any transaction, for example: between two autocommit statements, - // this method will return the latest information schema in session that is same with `sessionctx.GetDomainInfoSchema()` - GetTxnInfoSchema() infoschema.InfoSchema - // GetTxnScope returns the current txn scope - GetTxnScope() string - // GetReadReplicaScope returns the read replica scope - GetReadReplicaScope() string - // GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) - // Calling this method will activate the txn implicitly if current read is not stale/historical read - GetStmtReadTS() (uint64, error) - // GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update - // Calling this method will activate the txn implicitly if current read is not stale/historical read - GetStmtForUpdateTS() (uint64, error) - // GetContextProvider returns the current TxnContextProvider - GetContextProvider() TxnContextProvider - // GetSnapshotWithStmtReadTS gets snapshot with read ts - GetSnapshotWithStmtReadTS() (kv.Snapshot, error) - // GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts - GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) - - // EnterNewTxn enters a new transaction. - EnterNewTxn(ctx context.Context, req *EnterNewTxnRequest) error - // OnTxnEnd is the hook that should be called after transaction commit or rollback - OnTxnEnd() - // OnStmtStart is the hook that should be called when a new statement started - OnStmtStart(ctx context.Context, node ast.StmtNode) error - // OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or - // a pessimistic select-for-update statement. - OnPessimisticStmtStart(ctx context.Context) error - // OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or - // select-for-update statement. - OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error - // OnStmtErrorForNextAction is the hook that should be called when a new statement get an error - // This method is not required to be called for every error in the statement, - // it is only required to be called for some errors handled in some specified points given by the parameter `point`. - // When the return error is not nil the return action is 'StmtActionError' and vice versa. - OnStmtErrorForNextAction(ctx context.Context, point StmtErrorHandlePoint, err error) (StmtErrorAction, error) - // OnStmtRetry is the hook that should be called when a statement retry - OnStmtRetry(ctx context.Context) error - // OnStmtCommit is the hook that should be called when a statement is executed successfully. - OnStmtCommit(ctx context.Context) error - // OnStmtRollback is the hook that should be called when a statement fails to execute. - OnStmtRollback(ctx context.Context, isForPessimisticRetry bool) error - // OnStmtEnd is called when a statement ends, together with txn.onStmtEnd() - OnStmtEnd() - // OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. - OnLocalTemporaryTableCreated() - // ActivateTxn activates the transaction. - ActivateTxn() (kv.Transaction, error) - // GetCurrentStmt returns the current statement node - GetCurrentStmt() ast.StmtNode -} - -// NewTxn starts a new optimistic and active txn, it can be used for the below scenes: -// 1. Commit the current transaction and do some work in a new transaction for some specific operations, for example: DDL -// 2. Some background job need to do something in a transaction. -// In other scenes like 'BEGIN', 'START TRANSACTION' or prepare transaction in a new statement, -// you should use `TxnManager`.`EnterNewTxn` and pass the relevant to it. -func NewTxn(ctx context.Context, sctx sessionctx.Context) error { - return GetTxnManager(sctx).EnterNewTxn(ctx, &EnterNewTxnRequest{ - Type: EnterNewTxnDefault, - TxnMode: ast.Optimistic, - }) -} - -// NewTxnInStmt is like `NewTxn` but it will call `OnStmtStart` after it. -// It should be used when a statement already started. -func NewTxnInStmt(ctx context.Context, sctx sessionctx.Context) error { - if err := NewTxn(ctx, sctx); err != nil { - return err - } - txnManager := GetTxnManager(sctx) - return txnManager.OnStmtStart(ctx, txnManager.GetCurrentStmt()) -} - -// GetTxnManager returns the TxnManager object from session context -var GetTxnManager func(sctx sessionctx.Context) TxnManager diff --git a/sessiontxn/internal/BUILD.bazel b/sessiontxn/internal/BUILD.bazel deleted file mode 100644 index 7f86bd3739573..0000000000000 --- a/sessiontxn/internal/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = ["txn.go"], - importpath = "github.com/pingcap/tidb/sessiontxn/internal", - visibility = ["//sessiontxn:__subpackages__"], - deps = [ - "//kv", - "//sessionctx", - "//sessionctx/variable", - "//util/logutil", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@org_uber_go_zap//:zap", - ], -) diff --git a/sessiontxn/internal/txn.go b/sessiontxn/internal/txn.go deleted file mode 100644 index c284d7b3e7fe4..0000000000000 --- a/sessiontxn/internal/txn.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "context" - - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// SetTxnAssertionLevel sets assertion level of a transactin. Note that assertion level should be set only once just -// after creating a new transaction. -func SetTxnAssertionLevel(txn kv.Transaction, assertionLevel variable.AssertionLevel) { - switch assertionLevel { - case variable.AssertionLevelOff: - txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Off) - case variable.AssertionLevelFast: - txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Fast) - case variable.AssertionLevelStrict: - txn.SetOption(kv.AssertionLevel, kvrpcpb.AssertionLevel_Strict) - } -} - -// CommitBeforeEnterNewTxn is called before entering a new transaction. It checks whether the old -// txn is valid in which case we should commit it first. -func CommitBeforeEnterNewTxn(ctx context.Context, sctx sessionctx.Context) error { - txn, err := sctx.Txn(false) - if err != nil { - return err - } - if txn.Valid() { - txnStartTS := txn.StartTS() - txnScope := sctx.GetSessionVars().TxnCtx.TxnScope - err = sctx.CommitTxn(ctx) - if err != nil { - return err - } - logutil.Logger(ctx).Info("Try to create a new txn inside a transaction auto commit", - zap.Int64("schemaVersion", sctx.GetInfoSchema().SchemaMetaVersion()), - zap.Uint64("txnStartTS", txnStartTS), - zap.String("txnScope", txnScope)) - } - return nil -} - -// GetSnapshotWithTS returns a snapshot with ts. -func GetSnapshotWithTS(s sessionctx.Context, ts uint64, interceptor kv.SnapshotInterceptor) kv.Snapshot { - snap := s.GetStore().GetSnapshot(kv.Version{Ver: ts}) - if interceptor != nil { - snap.SetOption(kv.SnapInterceptor, interceptor) - } - if s.GetSessionVars().InRestrictedSQL { - snap.SetOption(kv.RequestSourceInternal, true) - } - if tp := s.GetSessionVars().RequestSourceType; tp != "" { - snap.SetOption(kv.RequestSourceType, tp) - } - if tp := s.GetSessionVars().ExplicitRequestSourceType; tp != "" { - snap.SetOption(kv.ExplicitRequestSourceType, tp) - } - if s.GetSessionVars().LoadBasedReplicaReadThreshold > 0 { - snap.SetOption(kv.LoadBasedReplicaReadThreshold, s.GetSessionVars().LoadBasedReplicaReadThreshold) - } - return snap -} diff --git a/sessiontxn/isolation/BUILD.bazel b/sessiontxn/isolation/BUILD.bazel deleted file mode 100644 index 491318a67d728..0000000000000 --- a/sessiontxn/isolation/BUILD.bazel +++ /dev/null @@ -1,76 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "isolation", - srcs = [ - "base.go", - "optimistic.go", - "readcommitted.go", - "repeatable_read.go", - "serializable.go", - ], - importpath = "github.com/pingcap/tidb/sessiontxn/isolation", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//infoschema", - "//kv", - "//parser/ast", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/internal", - "//sessiontxn/isolation/metrics", - "//sessiontxn/staleread", - "//table/temptable", - "//util/logutil", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "isolation_test", - timeout = "short", - srcs = [ - "main_test.go", - "optimistic_test.go", - "readcommitted_test.go", - "repeatable_read_test.go", - "serializable_test.go", - ], - flaky = True, - shard_count = 29, - deps = [ - ":isolation", - "//config", - "//executor", - "//expression", - "//infoschema", - "//kv", - "//parser", - "//parser/ast", - "//planner", - "//session", - "//sessionctx", - "//sessiontxn", - "//testkit", - "//testkit/testfork", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessiontxn/isolation/base.go b/sessiontxn/isolation/base.go deleted file mode 100644 index 3fca66f52f123..0000000000000 --- a/sessiontxn/isolation/base.go +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package isolation - -import ( - "context" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/sessiontxn/internal" - "github.com/pingcap/tidb/sessiontxn/staleread" - "github.com/pingcap/tidb/table/temptable" - "github.com/pingcap/tidb/util/tracing" - "github.com/tikv/client-go/v2/oracle" -) - -// baseTxnContextProvider is a base class for the transaction context providers that implement `TxnContextProvider` in different isolation. -// It provides some common functions below: -// - Provides a default `OnInitialize` method to initialize its inner state. -// - Provides some methods like `activateTxn` and `prepareTxn` to manage the inner transaction. -// - Provides default methods `GetTxnInfoSchema`, `GetStmtReadTS` and `GetStmtForUpdateTS` and return the snapshot information schema or ts when `tidb_snapshot` is set. -// - Provides other default methods like `Advise`, `OnStmtStart`, `OnStmtRetry` and `OnStmtErrorForNextAction` -// -// The subclass can set some inner property of `baseTxnContextProvider` when it is constructed. -// For example, `getStmtReadTSFunc` and `getStmtForUpdateTSFunc` should be set, and they will be called when `GetStmtReadTS` -// or `GetStmtForUpdate` to get the timestamp that should be used by the corresponding isolation level. -type baseTxnContextProvider struct { - // States that should be initialized when baseTxnContextProvider is created and should not be changed after that - sctx sessionctx.Context - causalConsistencyOnly bool - onInitializeTxnCtx func(*variable.TransactionContext) - onTxnActiveFunc func(kv.Transaction, sessiontxn.EnterNewTxnType) - getStmtReadTSFunc func() (uint64, error) - getStmtForUpdateTSFunc func() (uint64, error) - - // Runtime states - ctx context.Context - infoSchema infoschema.InfoSchema - txn kv.Transaction - isTxnPrepared bool - enterNewTxnType sessiontxn.EnterNewTxnType - // constStartTS is only used by point get max ts optimization currently. - // When constStartTS != 0, we use constStartTS directly without fetching it from tso. - // To save the cpu cycles `PrepareTSFuture` will also not be called when warmup (postpone to activate txn). - constStartTS uint64 -} - -// OnInitialize is the hook that should be called when enter a new txn with this provider -func (p *baseTxnContextProvider) OnInitialize(ctx context.Context, tp sessiontxn.EnterNewTxnType) (err error) { - if p.getStmtReadTSFunc == nil || p.getStmtForUpdateTSFunc == nil { - return errors.New("ts functions should not be nil") - } - - p.ctx = ctx - sessVars := p.sctx.GetSessionVars() - activeNow := true - switch tp { - case sessiontxn.EnterNewTxnDefault: - // As we will enter a new txn, we need to commit the old txn if it's still valid. - // There are two main steps here to enter a new txn: - // 1. prepareTxnWithOracleTS - // 2. ActivateTxn - if err := internal.CommitBeforeEnterNewTxn(p.ctx, p.sctx); err != nil { - return err - } - if err := p.prepareTxnWithOracleTS(); err != nil { - return err - } - case sessiontxn.EnterNewTxnWithBeginStmt: - if !canReuseTxnWhenExplicitBegin(p.sctx) { - // As we will enter a new txn, we need to commit the old txn if it's still valid. - // There are two main steps here to enter a new txn: - // 1. prepareTxnWithOracleTS - // 2. ActivateTxn - if err := internal.CommitBeforeEnterNewTxn(p.ctx, p.sctx); err != nil { - return err - } - if err := p.prepareTxnWithOracleTS(); err != nil { - return err - } - } - sessVars.SetInTxn(true) - case sessiontxn.EnterNewTxnBeforeStmt: - activeNow = false - default: - return errors.Errorf("Unsupported type: %v", tp) - } - - p.enterNewTxnType = tp - p.infoSchema = p.sctx.GetDomainInfoSchema().(infoschema.InfoSchema) - txnCtx := &variable.TransactionContext{ - TxnCtxNoNeedToRestore: variable.TxnCtxNoNeedToRestore{ - CreateTime: time.Now(), - InfoSchema: p.infoSchema, - ShardStep: int(sessVars.ShardAllocateStep), - TxnScope: sessVars.CheckAndGetTxnScope(), - }, - } - if p.onInitializeTxnCtx != nil { - p.onInitializeTxnCtx(txnCtx) - } - sessVars.TxnCtxMu.Lock() - sessVars.TxnCtx = txnCtx - sessVars.TxnCtxMu.Unlock() - if variable.EnableMDL.Load() { - sessVars.TxnCtx.EnableMDL = true - } - - txn, err := p.sctx.Txn(false) - if err != nil { - return err - } - p.isTxnPrepared = txn.Valid() || p.sctx.GetPreparedTxnFuture() != nil - if activeNow { - _, err = p.ActivateTxn() - } - - return err -} - -// GetTxnInfoSchema returns the information schema used by txn -func (p *baseTxnContextProvider) GetTxnInfoSchema() infoschema.InfoSchema { - if is := p.sctx.GetSessionVars().SnapshotInfoschema; is != nil { - return is.(infoschema.InfoSchema) - } - if _, ok := p.infoSchema.(*infoschema.SessionExtendedInfoSchema); !ok { - p.infoSchema = &infoschema.SessionExtendedInfoSchema{ - InfoSchema: p.infoSchema, - } - p.sctx.GetSessionVars().TxnCtx.InfoSchema = p.infoSchema - } - return p.infoSchema -} - -// GetTxnScope returns the current txn scope -func (p *baseTxnContextProvider) GetTxnScope() string { - return p.sctx.GetSessionVars().TxnCtx.TxnScope -} - -// GetReadReplicaScope returns the read replica scope -func (p *baseTxnContextProvider) GetReadReplicaScope() string { - if txnScope := p.GetTxnScope(); txnScope != kv.GlobalTxnScope && txnScope != "" { - // In local txn, we should use txnScope as the readReplicaScope - return txnScope - } - - if p.sctx.GetSessionVars().GetReplicaRead().IsClosestRead() { - // If closest read is set, we should use the scope where instance located. - return config.GetTxnScopeFromConfig() - } - - // When it is not local txn or closet read, we should use global scope - return kv.GlobalReplicaScope -} - -// GetStmtReadTS returns the read timestamp used by select statement (not for select ... for update) -func (p *baseTxnContextProvider) GetStmtReadTS() (uint64, error) { - if _, err := p.ActivateTxn(); err != nil { - return 0, err - } - - if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { - return snapshotTS, nil - } - return p.getStmtReadTSFunc() -} - -// GetStmtForUpdateTS returns the read timestamp used by update/insert/delete or select ... for update -func (p *baseTxnContextProvider) GetStmtForUpdateTS() (uint64, error) { - if _, err := p.ActivateTxn(); err != nil { - return 0, err - } - - if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { - return snapshotTS, nil - } - return p.getStmtForUpdateTSFunc() -} - -// OnStmtStart is the hook that should be called when a new statement started -func (p *baseTxnContextProvider) OnStmtStart(ctx context.Context, _ ast.StmtNode) error { - p.ctx = ctx - return nil -} - -// OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or -// a pessimistic select-for-update statements. -func (p *baseTxnContextProvider) OnPessimisticStmtStart(_ context.Context) error { - return nil -} - -// OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or -// select-for-update statement. -func (p *baseTxnContextProvider) OnPessimisticStmtEnd(_ context.Context, _ bool) error { - return nil -} - -// OnStmtRetry is the hook that should be called when a statement is retried internally. -func (p *baseTxnContextProvider) OnStmtRetry(ctx context.Context) error { - p.ctx = ctx - p.sctx.GetSessionVars().TxnCtx.CurrentStmtPessimisticLockCache = nil - return nil -} - -// OnStmtCommit is the hook that should be called when a statement is executed successfully. -func (p *baseTxnContextProvider) OnStmtCommit(_ context.Context) error { - return nil -} - -// OnStmtRollback is the hook that should be called when a statement fails to execute. -func (p *baseTxnContextProvider) OnStmtRollback(_ context.Context, _ bool) error { - return nil -} - -// OnLocalTemporaryTableCreated is the hook that should be called when a local temporary table created. -func (p *baseTxnContextProvider) OnLocalTemporaryTableCreated() { - p.infoSchema = temptable.AttachLocalTemporaryTableInfoSchema(p.sctx, p.infoSchema) - p.sctx.GetSessionVars().TxnCtx.InfoSchema = p.infoSchema - if p.txn != nil && p.txn.Valid() { - if interceptor := temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema); interceptor != nil { - p.txn.SetOption(kv.SnapInterceptor, interceptor) - } - } -} - -// OnStmtErrorForNextAction is the hook that should be called when a new statement get an error -func (p *baseTxnContextProvider) OnStmtErrorForNextAction(ctx context.Context, point sessiontxn.StmtErrorHandlePoint, err error) (sessiontxn.StmtErrorAction, error) { - switch point { - case sessiontxn.StmtErrAfterPessimisticLock: - // for pessimistic lock error, return the error by default - return sessiontxn.ErrorAction(err) - default: - return sessiontxn.NoIdea() - } -} - -func (p *baseTxnContextProvider) getTxnStartTS() (uint64, error) { - txn, err := p.ActivateTxn() - if err != nil { - return 0, err - } - return txn.StartTS(), nil -} - -// ActivateTxn activates the transaction and set the relevant context variables. -func (p *baseTxnContextProvider) ActivateTxn() (kv.Transaction, error) { - if p.txn != nil { - return p.txn, nil - } - - if err := p.prepareTxn(); err != nil { - return nil, err - } - - if p.constStartTS != 0 { - if err := p.replaceTxnTsFuture(sessiontxn.ConstantFuture(p.constStartTS)); err != nil { - return nil, err - } - } - - txnFuture := p.sctx.GetPreparedTxnFuture() - txn, err := txnFuture.Wait(p.ctx, p.sctx) - if err != nil { - return nil, err - } - - sessVars := p.sctx.GetSessionVars() - sessVars.TxnCtxMu.Lock() - sessVars.TxnCtx.StartTS = txn.StartTS() - sessVars.TxnCtxMu.Unlock() - if sessVars.MemDBFootprint != nil { - sessVars.MemDBFootprint.Detach() - } - sessVars.MemDBFootprint = nil - - if p.enterNewTxnType == sessiontxn.EnterNewTxnBeforeStmt && !sessVars.IsAutocommit() && sessVars.SnapshotTS == 0 { - sessVars.SetInTxn(true) - } - - txn.SetVars(sessVars.KVVars) - - readReplicaType := sessVars.GetReplicaRead() - if readReplicaType.IsFollowerRead() { - txn.SetOption(kv.ReplicaRead, readReplicaType) - } - - if interceptor := temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema); interceptor != nil { - txn.SetOption(kv.SnapInterceptor, interceptor) - } - - if sessVars.StmtCtx.WeakConsistency { - txn.SetOption(kv.IsolationLevel, kv.RC) - } - - internal.SetTxnAssertionLevel(txn, sessVars.AssertionLevel) - - if p.causalConsistencyOnly { - txn.SetOption(kv.GuaranteeLinearizability, false) - } - - if p.onTxnActiveFunc != nil { - p.onTxnActiveFunc(txn, p.enterNewTxnType) - } - - if p.sctx.GetSessionVars().InRestrictedSQL { - txn.SetOption(kv.RequestSourceInternal, true) - } - - if tp := p.sctx.GetSessionVars().RequestSourceType; tp != "" { - txn.SetOption(kv.RequestSourceType, tp) - } - - if sessVars.LoadBasedReplicaReadThreshold > 0 { - txn.SetOption(kv.LoadBasedReplicaReadThreshold, sessVars.LoadBasedReplicaReadThreshold) - } - - p.txn = txn - return txn, nil -} - -// prepareTxn prepares txn with an oracle ts future. If the snapshotTS is set, -// the txn is prepared with it. -func (p *baseTxnContextProvider) prepareTxn() error { - if p.isTxnPrepared { - return nil - } - - if snapshotTS := p.sctx.GetSessionVars().SnapshotTS; snapshotTS != 0 { - return p.replaceTxnTsFuture(sessiontxn.ConstantFuture(snapshotTS)) - } - - future := newOracleFuture(p.ctx, p.sctx, p.sctx.GetSessionVars().TxnCtx.TxnScope) - return p.replaceTxnTsFuture(future) -} - -// prepareTxnWithOracleTS -// The difference between prepareTxnWithOracleTS and prepareTxn is that prepareTxnWithOracleTS -// does not consider snapshotTS -func (p *baseTxnContextProvider) prepareTxnWithOracleTS() error { - if p.isTxnPrepared { - return nil - } - - future := newOracleFuture(p.ctx, p.sctx, p.sctx.GetSessionVars().TxnCtx.TxnScope) - return p.replaceTxnTsFuture(future) -} - -func (p *baseTxnContextProvider) forcePrepareConstStartTS(ts uint64) error { - if p.txn != nil { - return errors.New("cannot force prepare const start ts because txn is active") - } - p.constStartTS = ts - p.isTxnPrepared = true - return nil -} - -func (p *baseTxnContextProvider) replaceTxnTsFuture(future oracle.Future) error { - txn, err := p.sctx.Txn(false) - if err != nil { - return err - } - - if txn.Valid() { - return nil - } - - txnScope := p.sctx.GetSessionVars().TxnCtx.TxnScope - if err = p.sctx.PrepareTSFuture(p.ctx, future, txnScope); err != nil { - return err - } - - p.isTxnPrepared = true - return nil -} - -func (p *baseTxnContextProvider) isTidbSnapshotEnabled() bool { - return p.sctx.GetSessionVars().SnapshotTS != 0 -} - -// isBeginStmtWithStaleRead indicates whether the current statement is `BeginStmt` type with stale read -// Because stale read will use `staleread.StalenessTxnContextProvider` for query, so if `staleread.IsStmtStaleness()` -// returns true in other providers, it means the current statement is `BeginStmt` with stale read -func (p *baseTxnContextProvider) isBeginStmtWithStaleRead() bool { - return staleread.IsStmtStaleness(p.sctx) -} - -// AdviseWarmup provides warmup for inner state -func (p *baseTxnContextProvider) AdviseWarmup() error { - if p.isTxnPrepared || p.isBeginStmtWithStaleRead() { - // When executing `START TRANSACTION READ ONLY AS OF ...` no need to warmUp - return nil - } - return p.prepareTxn() -} - -// AdviseOptimizeWithPlan providers optimization according to the plan -func (p *baseTxnContextProvider) AdviseOptimizeWithPlan(_ interface{}) error { - return nil -} - -// GetSnapshotWithStmtReadTS gets snapshot with read ts -func (p *baseTxnContextProvider) GetSnapshotWithStmtReadTS() (kv.Snapshot, error) { - ts, err := p.GetStmtReadTS() - if err != nil { - return nil, err - } - - return p.getSnapshotByTS(ts) -} - -// GetSnapshotWithStmtForUpdateTS gets snapshot with for update ts -func (p *baseTxnContextProvider) GetSnapshotWithStmtForUpdateTS() (kv.Snapshot, error) { - ts, err := p.GetStmtForUpdateTS() - if err != nil { - return nil, err - } - - return p.getSnapshotByTS(ts) -} - -// getSnapshotByTS get snapshot from store according to the snapshotTS and set the transaction related -// options before return -func (p *baseTxnContextProvider) getSnapshotByTS(snapshotTS uint64) (kv.Snapshot, error) { - txn, err := p.sctx.Txn(false) - if err != nil { - return nil, err - } - - txnCtx := p.sctx.GetSessionVars().TxnCtx - if txn.Valid() && txnCtx.StartTS == txnCtx.GetForUpdateTS() && txnCtx.StartTS == snapshotTS { - return txn.GetSnapshot(), nil - } - - sessVars := p.sctx.GetSessionVars() - snapshot := internal.GetSnapshotWithTS( - p.sctx, - snapshotTS, - temptable.SessionSnapshotInterceptor(p.sctx, p.infoSchema), - ) - - replicaReadType := sessVars.GetReplicaRead() - if replicaReadType.IsFollowerRead() && - !sessVars.StmtCtx.RCCheckTS && - !sessVars.RcWriteCheckTS { - snapshot.SetOption(kv.ReplicaRead, replicaReadType) - } - - return snapshot, nil -} - -// canReuseTxnWhenExplicitBegin returns whether we should reuse the txn when starting a transaction explicitly -func canReuseTxnWhenExplicitBegin(sctx sessionctx.Context) bool { - sessVars := sctx.GetSessionVars() - txnCtx := sessVars.TxnCtx - // If BEGIN is the first statement in TxnCtx, we can reuse the existing transaction, without the - // need to call NewTxn, which commits the existing transaction and begins a new one. - // If the last un-committed/un-rollback transaction is a time-bounded read-only transaction, we should - // always create a new transaction. - // If the variable `tidb_snapshot` is set, we should always create a new transaction because the current txn may be - // initialized with snapshot ts. - return txnCtx.History == nil && !txnCtx.IsStaleness && sessVars.SnapshotTS == 0 -} - -// newOracleFuture creates new future according to the scope and the session context -func newOracleFuture(ctx context.Context, sctx sessionctx.Context, scope string) oracle.Future { - r, ctx := tracing.StartRegionEx(ctx, "isolation.newOracleFuture") - defer r.End() - - failpoint.Inject("requestTsoFromPD", func() { - sessiontxn.TsoRequestCountInc(sctx) - }) - - oracleStore := sctx.GetStore().GetOracle() - option := &oracle.Option{TxnScope: scope} - - if sctx.GetSessionVars().LowResolutionTSO { - return oracleStore.GetLowResolutionTimestampAsync(ctx, option) - } - return oracleStore.GetTimestampAsync(ctx, option) -} - -// funcFuture implements oracle.Future -type funcFuture func() (uint64, error) - -// Wait returns a ts got from the func -func (f funcFuture) Wait() (uint64, error) { - return f() -} - -// basePessimisticTxnContextProvider extends baseTxnContextProvider with some functionalities that are commonly used in -// pessimistic transactions. -type basePessimisticTxnContextProvider struct { - baseTxnContextProvider -} - -// OnPessimisticStmtStart is the hook that should be called when starts handling a pessimistic DML or -// a pessimistic select-for-update statements. -func (p *basePessimisticTxnContextProvider) OnPessimisticStmtStart(ctx context.Context) error { - if err := p.baseTxnContextProvider.OnPessimisticStmtStart(ctx); err != nil { - return err - } - if p.sctx.GetSessionVars().PessimisticTransactionFairLocking && - p.txn != nil && - p.sctx.GetSessionVars().ConnectionID != 0 && - !p.sctx.GetSessionVars().InRestrictedSQL { - if err := p.txn.StartFairLocking(); err != nil { - return err - } - } - return nil -} - -// OnPessimisticStmtEnd is the hook that should be called when finishes handling a pessimistic DML or -// select-for-update statement. -func (p *basePessimisticTxnContextProvider) OnPessimisticStmtEnd(ctx context.Context, isSuccessful bool) error { - if err := p.baseTxnContextProvider.OnPessimisticStmtEnd(ctx, isSuccessful); err != nil { - return err - } - if p.txn != nil && p.txn.IsInFairLockingMode() { - if isSuccessful { - if err := p.txn.DoneFairLocking(ctx); err != nil { - return err - } - } else { - if err := p.txn.CancelFairLocking(ctx); err != nil { - return err - } - } - } - - if isSuccessful { - p.sctx.GetSessionVars().TxnCtx.FlushStmtPessimisticLockCache() - } else { - p.sctx.GetSessionVars().TxnCtx.CurrentStmtPessimisticLockCache = nil - } - return nil -} - -func (p *basePessimisticTxnContextProvider) retryFairLockingIfNeeded(ctx context.Context) error { - if p.txn != nil && p.txn.IsInFairLockingMode() { - if err := p.txn.RetryFairLocking(ctx); err != nil { - return err - } - } - return nil -} - -func (p *basePessimisticTxnContextProvider) cancelFairLockingIfNeeded(ctx context.Context) error { - if p.txn != nil && p.txn.IsInFairLockingMode() { - if err := p.txn.CancelFairLocking(ctx); err != nil { - return err - } - } - return nil -} diff --git a/sessiontxn/isolation/main_test.go b/sessiontxn/isolation/main_test.go deleted file mode 100644 index 89f2d0c0e01b4..0000000000000 --- a/sessiontxn/isolation/main_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package isolation_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testfork" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/client.(*mockClient).WatchCommand.func1"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*JobManager).jobLoop"), - } - goleak.VerifyTestMain(m, opts...) -} - -func getOracleTS(t testing.TB, sctx sessionctx.Context) uint64 { - ts, err := sctx.GetStore().GetOracle().GetTimestamp(context.TODO(), &oracle.Option{TxnScope: oracle.GlobalTxnScope}) - require.NoError(t, err) - return ts -} - -type txnAssert[T sessiontxn.TxnContextProvider] struct { - sctx sessionctx.Context - isolation string - minStartTime time.Time - active bool - inTxn bool - minStartTS uint64 - startTS uint64 - causalConsistencyOnly bool - couldRetry bool -} - -func (a *txnAssert[T]) Check(t testing.TB) { - provider := sessiontxn.GetTxnManager(a.sctx).GetContextProvider() - sessVars := a.sctx.GetSessionVars() - txnCtx := sessVars.TxnCtx - - if sessVars.SnapshotInfoschema == nil { - require.Same(t, provider.GetTxnInfoSchema(), txnCtx.InfoSchema) - } else { - require.Equal(t, sessVars.SnapshotInfoschema.(infoschema.InfoSchema).SchemaMetaVersion(), provider.GetTxnInfoSchema().SchemaMetaVersion()) - } - require.Equal(t, a.isolation, txnCtx.Isolation) - require.Equal(t, a.isolation != "", txnCtx.IsPessimistic) - require.Equal(t, sessVars.CheckAndGetTxnScope(), txnCtx.TxnScope) - require.Equal(t, sessVars.ShardAllocateStep, int64(txnCtx.ShardStep)) - require.False(t, txnCtx.IsStaleness) - require.GreaterOrEqual(t, txnCtx.CreateTime.UnixNano(), a.minStartTime.UnixNano()) - require.Equal(t, a.inTxn, sessVars.InTxn()) - require.Equal(t, a.inTxn, txnCtx.IsExplicit) - require.Equal(t, a.couldRetry, txnCtx.CouldRetry) - require.Equal(t, assertTxnScope, txnCtx.TxnScope) - require.Equal(t, assertTxnScope, provider.GetTxnScope()) - require.Equal(t, assertReplicaReadScope, provider.GetReadReplicaScope()) - - txn, err := a.sctx.Txn(false) - require.NoError(t, err) - require.Equal(t, a.active, txn.Valid()) - if !a.active { - require.False(t, a.inTxn) - require.Zero(t, a.startTS) - require.Zero(t, txnCtx.StartTS) - } else { - require.True(t, a.minStartTS != 0 || a.startTS != 0) - require.Greater(t, txnCtx.StartTS, a.minStartTS) - if a.startTS != 0 { - require.Equal(t, a.startTS, txnCtx.StartTS) - } - require.Equal(t, txnCtx.StartTS, txn.StartTS()) - require.Same(t, sessVars.KVVars, txn.GetVars()) - require.Equal(t, txnCtx.TxnScope, txn.GetOption(kv.TxnScope)) - require.Equal(t, a.causalConsistencyOnly, !txn.GetOption(kv.GuaranteeLinearizability).(bool)) - require.Equal(t, txnCtx.IsPessimistic, txn.IsPessimistic()) - } - // The next line is testing the provider has the type T, if not, the cast will panic - _ = provider.(T) -} - -func activeSnapshotTxnAssert(sctx sessionctx.Context, ts uint64, isolation string) *txnAssert[sessiontxn.TxnContextProvider] { - return &txnAssert[sessiontxn.TxnContextProvider]{ - sctx: sctx, - minStartTime: time.Now(), - startTS: ts, - active: true, - inTxn: false, - isolation: isolation, - } -} - -func (a *txnAssert[T]) CheckAndGetProvider(t testing.TB) T { - a.Check(t) - return sessiontxn.GetTxnManager(a.sctx).GetContextProvider().(T) -} - -var assertTxnScope = kv.GlobalTxnScope -var assertReplicaReadScope = kv.GlobalReplicaScope - -func forkScopeSettings(t *testfork.T, store kv.Storage) func() { - tk := testkit.NewTestKit(t, store) - failPointEnabled := false - clearFunc := func() { - assertTxnScope = kv.GlobalTxnScope - assertReplicaReadScope = kv.GlobalReplicaScope - tk.MustExec("set @@global.tidb_replica_read='leader'") - tk.MustExec("set @@global.tidb_enable_local_txn=0") - if failPointEnabled { - require.NoError(t, failpoint.Disable("tikvclient/injectTxnScope")) - } - } - - clearFunc() - success := false - defer func() { - if !success { - clearFunc() - } - }() - - zone := testfork.PickEnum(t, "", "bj") - if zone != "" { - require.NoError(t, failpoint.Enable("tikvclient/injectTxnScope", fmt.Sprintf(`return("%v")`, zone))) - failPointEnabled = true - if testfork.PickEnum(t, "", "enableLocalTxn") != "" { - tk.MustExec("set @@global.tidb_enable_local_txn=1") - assertTxnScope = zone - assertReplicaReadScope = zone - } - } - - if testfork.PickEnum(t, "", "closetRead") != "" { - tk.MustExec("set @@global.tidb_replica_read='closest-replicas'") - if zone != "" { - assertReplicaReadScope = zone - } - } - - success = true - return clearFunc -} diff --git a/sessiontxn/isolation/metrics/BUILD.bazel b/sessiontxn/isolation/metrics/BUILD.bazel deleted file mode 100644 index fcb4bcf68b783..0000000000000 --- a/sessiontxn/isolation/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/sessiontxn/isolation/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/sessiontxn/isolation/metrics/metrics.go b/sessiontxn/isolation/metrics/metrics.go deleted file mode 100644 index 8c95bdf8461f1..0000000000000 --- a/sessiontxn/isolation/metrics/metrics.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package isolation - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// isolation metrics vars -var ( - RcReadCheckTSWriteConfilictCounter prometheus.Counter - RcWriteCheckTSWriteConfilictCounter prometheus.Counter -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init isolation metrics vars. -func InitMetricsVars() { - RcReadCheckTSWriteConfilictCounter = metrics.RCCheckTSWriteConfilictCounter.WithLabelValues(metrics.LblRCReadCheckTS) - RcWriteCheckTSWriteConfilictCounter = metrics.RCCheckTSWriteConfilictCounter.WithLabelValues(metrics.LblRCWriteCheckTS) -} diff --git a/sessiontxn/staleread/BUILD.bazel b/sessiontxn/staleread/BUILD.bazel deleted file mode 100644 index 0fa1d89159ed3..0000000000000 --- a/sessiontxn/staleread/BUILD.bazel +++ /dev/null @@ -1,68 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "staleread", - srcs = [ - "errors.go", - "failpoint.go", - "processor.go", - "provider.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/sessiontxn/staleread", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//domain", - "//errno", - "//expression", - "//infoschema", - "//kv", - "//parser/ast", - "//parser/mysql", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//sessiontxn/internal", - "//table/temptable", - "//types", - "//util/dbterror", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//oracle", - ], -) - -go_test( - name = "staleread_test", - timeout = "short", - srcs = [ - "externalts_test.go", - "main_test.go", - "processor_test.go", - "provider_test.go", - ], - flaky = True, - shard_count = 6, - deps = [ - ":staleread", - "//domain", - "//infoschema", - "//kv", - "//parser", - "//parser/ast", - "//parser/auth", - "//sessionctx", - "//sessiontxn", - "//table/temptable", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/sessiontxn/staleread/errors.go b/sessiontxn/staleread/errors.go deleted file mode 100644 index 1d89fa632ccd6..0000000000000 --- a/sessiontxn/staleread/errors.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package staleread - -import ( - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/util/dbterror" -) - -var ( - errAsOf = dbterror.ClassOptimizer.NewStd(mysql.ErrAsOf) -) diff --git a/sessiontxn/staleread/failpoint.go b/sessiontxn/staleread/failpoint.go deleted file mode 100644 index 1e2d184453f7f..0000000000000 --- a/sessiontxn/staleread/failpoint.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package staleread - -import ( - "fmt" - - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" -) - -// AssertStmtStaleness is used only for test -func AssertStmtStaleness(sctx sessionctx.Context, expected bool) { - actual := IsStmtStaleness(sctx) - if actual != expected { - panic(fmt.Sprintf("stmtctx isStaleness wrong, expected:%v, got:%v", expected, actual)) - } - - if expected { - provider := sessiontxn.GetTxnManager(sctx).GetContextProvider() - if _, ok := provider.(*StalenessTxnContextProvider); !ok { - panic(fmt.Sprintf("stale read should be StalenessTxnContextProvider but current provider is: %T", provider)) - } - } -} diff --git a/sessiontxn/staleread/main_test.go b/sessiontxn/staleread/main_test.go deleted file mode 100644 index 4e66cfbfc12e2..0000000000000 --- a/sessiontxn/staleread/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. -package staleread_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/sessiontxn/staleread/util.go b/sessiontxn/staleread/util.go deleted file mode 100644 index 814861ffddcce..0000000000000 --- a/sessiontxn/staleread/util.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package staleread - -import ( - "context" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/types" - "github.com/tikv/client-go/v2/oracle" -) - -// CalculateAsOfTsExpr calculates the TsExpr of AsOfClause to get a StartTS. -func CalculateAsOfTsExpr(ctx context.Context, sctx sessionctx.Context, tsExpr ast.ExprNode) (uint64, error) { - sctx.GetSessionVars().StmtCtx.SetStaleTSOProvider(func() (uint64, error) { - failpoint.Inject("mockStaleReadTSO", func(val failpoint.Value) (uint64, error) { - return uint64(val.(int)), nil - }) - // this function accepts a context, but we don't need it when there is a valid cached ts. - // in most cases, the stale read ts can be calculated from `cached ts + time since cache - staleness`, - // this can be more accurate than `time.Now() - staleness`, because TiDB's local time can drift. - return sctx.GetStore().GetOracle().GetStaleTimestamp(ctx, oracle.GlobalTxnScope, 0) - }) - tsVal, err := expression.EvalAstExpr(sctx, tsExpr) - if err != nil { - return 0, err - } - - if tsVal.IsNull() { - return 0, errAsOf.FastGenWithCause("as of timestamp cannot be NULL") - } - - toTypeTimestamp := types.NewFieldType(mysql.TypeTimestamp) - // We need at least the millionsecond here, so set fsp to 3. - toTypeTimestamp.SetDecimal(3) - tsTimestamp, err := tsVal.ConvertTo(sctx.GetSessionVars().StmtCtx, toTypeTimestamp) - if err != nil { - return 0, err - } - tsTime, err := tsTimestamp.GetMysqlTime().GoTime(sctx.GetSessionVars().Location()) - if err != nil { - return 0, err - } - return oracle.GoTimeToTS(tsTime), nil -} - -// CalculateTsWithReadStaleness calculates the TsExpr for readStaleness duration -func CalculateTsWithReadStaleness(sctx sessionctx.Context, readStaleness time.Duration) (uint64, error) { - nowVal, err := expression.GetStmtTimestamp(sctx) - if err != nil { - return 0, err - } - tsVal := nowVal.Add(readStaleness) - minTsVal := expression.GetMinSafeTime(sctx) - return oracle.GoTimeToTS(expression.CalAppropriateTime(tsVal, nowVal, minTsVal)), nil -} - -// IsStmtStaleness indicates whether the current statement is staleness or not -func IsStmtStaleness(sctx sessionctx.Context) bool { - return sctx.GetSessionVars().StmtCtx.IsStaleness -} - -// GetExternalTimestamp returns the external timestamp in cache, or get and store it in cache -func GetExternalTimestamp(ctx context.Context, sctx sessionctx.Context) (uint64, error) { - // Try to get from the stmt cache to make sure this function is deterministic. - stmtCtx := sctx.GetSessionVars().StmtCtx - externalTimestamp, err := stmtCtx.GetOrEvaluateStmtCache(stmtctx.StmtExternalTSCacheKey, func() (interface{}, error) { - return variable.GetExternalTimestamp(ctx) - }) - - if err != nil { - return 0, errAsOf.FastGenWithCause(err.Error()) - } - return externalTimestamp.(uint64), nil -} diff --git a/statistics/BUILD.bazel b/statistics/BUILD.bazel deleted file mode 100644 index 283ecf2f9e181..0000000000000 --- a/statistics/BUILD.bazel +++ /dev/null @@ -1,102 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "statistics", - srcs = [ - "analyze.go", - "analyze_jobs.go", - "builder.go", - "cmsketch.go", - "cmsketch_util.go", - "column.go", - "debugtrace.go", - "estimate.go", - "fmsketch.go", - "histogram.go", - "index.go", - "row_sampler.go", - "sample.go", - "scalar.go", - "table.go", - ], - importpath = "github.com/pingcap/tidb/statistics", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/util/debugtrace", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//tablecodec", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/fastrand", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/ranger", - "//util/sqlexec", - "@com_github_dolthub_swiss//:swiss", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_twmb_murmur3//:murmur3", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "statistics_test", - timeout = "short", - srcs = [ - "cmsketch_test.go", - "fmsketch_test.go", - "histogram_bench_test.go", - "histogram_test.go", - "integration_test.go", - "main_test.go", - "sample_test.go", - "scalar_test.go", - "statistics_test.go", - ], - data = glob(["testdata/**"]), - embed = [":statistics"], - flaky = True, - shard_count = 34, - deps = [ - "//config", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//sessionctx/stmtctx", - "//statistics/handle/autoanalyze", - "//testkit", - "//testkit/testdata", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/mock", - "//util/ranger", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/builder.go b/statistics/builder.go deleted file mode 100644 index 1c439369f8d3e..0000000000000 --- a/statistics/builder.go +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "bytes" - "math" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/memory" -) - -// SortedBuilder is used to build histograms for PK and index. -type SortedBuilder struct { - sc *stmtctx.StatementContext - hist *Histogram - numBuckets int64 - valuesPerBucket int64 - lastNumber int64 - bucketIdx int64 - Count int64 - needBucketNDV bool -} - -// NewSortedBuilder creates a new SortedBuilder. -func NewSortedBuilder(sc *stmtctx.StatementContext, numBuckets, id int64, tp *types.FieldType, statsVer int) *SortedBuilder { - return &SortedBuilder{ - sc: sc, - numBuckets: numBuckets, - valuesPerBucket: 1, - hist: NewHistogram(id, 0, 0, 0, tp, int(numBuckets), 0), - needBucketNDV: statsVer >= Version2, - } -} - -// Hist returns the histogram built by SortedBuilder. -func (b *SortedBuilder) Hist() *Histogram { - return b.hist -} - -// Iterate updates the histogram incrementally. -func (b *SortedBuilder) Iterate(data types.Datum) error { - b.Count++ - appendBucket := b.hist.AppendBucket - if b.needBucketNDV { - appendBucket = func(lower, upper *types.Datum, count, repeat int64) { - b.hist.AppendBucketWithNDV(lower, upper, count, repeat, 1) - } - } - if b.Count == 1 { - appendBucket(&data, &data, 1, 1) - b.hist.NDV = 1 - return nil - } - cmp, err := b.hist.GetUpper(int(b.bucketIdx)).Compare(b.sc, &data, collate.GetBinaryCollator()) - if err != nil { - return errors.Trace(err) - } - if cmp == 0 { - // The new item has the same value as current bucket value, to ensure that - // a same value only stored in a single bucket, we do not increase bucketIdx even if it exceeds - // valuesPerBucket. - b.hist.Buckets[b.bucketIdx].Count++ - b.hist.Buckets[b.bucketIdx].Repeat++ - } else if b.hist.Buckets[b.bucketIdx].Count+1-b.lastNumber <= b.valuesPerBucket { - // The bucket still have room to store a new item, update the bucket. - b.hist.updateLastBucket(&data, b.hist.Buckets[b.bucketIdx].Count+1, 1, b.needBucketNDV) - b.hist.NDV++ - } else { - // All buckets are full, we should merge buckets. - if b.bucketIdx+1 == b.numBuckets { - b.hist.mergeBuckets(int(b.bucketIdx)) - b.valuesPerBucket *= 2 - b.bucketIdx = b.bucketIdx / 2 - if b.bucketIdx == 0 { - b.lastNumber = 0 - } else { - b.lastNumber = b.hist.Buckets[b.bucketIdx-1].Count - } - } - // We may merge buckets, so we should check it again. - if b.hist.Buckets[b.bucketIdx].Count+1-b.lastNumber <= b.valuesPerBucket { - b.hist.updateLastBucket(&data, b.hist.Buckets[b.bucketIdx].Count+1, 1, b.needBucketNDV) - } else { - b.lastNumber = b.hist.Buckets[b.bucketIdx].Count - b.bucketIdx++ - appendBucket(&data, &data, b.lastNumber+1, 1) - } - b.hist.NDV++ - } - return nil -} - -// BuildColumnHist build a histogram for a column. -// numBuckets: number of buckets for the histogram. -// id: the id of the table. -// collector: the collector of samples. -// tp: the FieldType for the column. -// count: represents the row count for the column. -// ndv: represents the number of distinct values for the column. -// nullCount: represents the number of null values for the column. -func BuildColumnHist(ctx sessionctx.Context, numBuckets, id int64, collector *SampleCollector, tp *types.FieldType, count int64, ndv int64, nullCount int64) (*Histogram, error) { - if ndv > count { - ndv = count - } - if count == 0 || len(collector.Samples) == 0 { - return NewHistogram(id, ndv, nullCount, 0, tp, 0, collector.TotalSize), nil - } - sc := ctx.GetSessionVars().StmtCtx - samples := collector.Samples - samples, err := SortSampleItems(sc, samples) - if err != nil { - return nil, err - } - hg := NewHistogram(id, ndv, nullCount, 0, tp, int(numBuckets), collector.TotalSize) - - corrXYSum, err := buildHist(sc, hg, samples, count, ndv, numBuckets, nil) - if err != nil { - return nil, err - } - hg.Correlation = calcCorrelation(int64(len(samples)), corrXYSum) - return hg, nil -} - -// buildHist builds histogram from samples and other information. -// It stores the built histogram in hg and return corrXYSum used for calculating the correlation. -func buildHist(sc *stmtctx.StatementContext, hg *Histogram, samples []*SampleItem, count, ndv, numBuckets int64, memTracker *memory.Tracker) (corrXYSum float64, err error) { - sampleNum := int64(len(samples)) - // As we use samples to build the histogram, the bucket number and repeat should multiply a factor. - sampleFactor := float64(count) / float64(sampleNum) - ndvFactor := float64(count) / float64(ndv) - if ndvFactor > sampleFactor { - ndvFactor = sampleFactor - } - // Since bucket count is increased by sampleFactor, so the actual max values per bucket is - // floor(valuesPerBucket/sampleFactor)*sampleFactor, which may less than valuesPerBucket, - // thus we need to add a sampleFactor to avoid building too many buckets. - valuesPerBucket := float64(count)/float64(numBuckets) + sampleFactor - - bucketIdx := 0 - var lastCount int64 - corrXYSum = float64(0) - hg.AppendBucket(&samples[0].Value, &samples[0].Value, int64(sampleFactor), int64(ndvFactor)) - bufferedMemSize := int64(0) - bufferedReleaseSize := int64(0) - defer func() { - if memTracker != nil { - memTracker.Consume(bufferedMemSize) - memTracker.Release(bufferedReleaseSize) - } - }() - var upper = new(types.Datum) - for i := int64(1); i < sampleNum; i++ { - corrXYSum += float64(i) * float64(samples[i].Ordinal) - hg.UpperToDatum(bucketIdx, upper) - if memTracker != nil { - // tmp memory usage - deltaSize := upper.MemUsage() - memTracker.BufferedConsume(&bufferedMemSize, deltaSize) - memTracker.BufferedRelease(&bufferedReleaseSize, deltaSize) - } - cmp, err := upper.Compare(sc, &samples[i].Value, collate.GetBinaryCollator()) - if err != nil { - return 0, errors.Trace(err) - } - totalCount := float64(i+1) * sampleFactor - if cmp == 0 { - // The new item has the same value as current bucket value, to ensure that - // a same value only stored in a single bucket, we do not increase bucketIdx even if it exceeds - // valuesPerBucket. - hg.Buckets[bucketIdx].Count = int64(totalCount) - if hg.Buckets[bucketIdx].Repeat == int64(ndvFactor) { - hg.Buckets[bucketIdx].Repeat = int64(2 * sampleFactor) - } else { - hg.Buckets[bucketIdx].Repeat += int64(sampleFactor) - } - } else if totalCount-float64(lastCount) <= valuesPerBucket { - // The bucket still have room to store a new item, update the bucket. - hg.updateLastBucket(&samples[i].Value, int64(totalCount), int64(ndvFactor), false) - } else { - lastCount = hg.Buckets[bucketIdx].Count - // The bucket is full, store the item in the next bucket. - bucketIdx++ - hg.AppendBucket(&samples[i].Value, &samples[i].Value, int64(totalCount), int64(ndvFactor)) - } - } - return corrXYSum, nil -} - -// calcCorrelation computes column order correlation with the handle. -func calcCorrelation(sampleNum int64, corrXYSum float64) float64 { - if sampleNum == 1 { - return 1 - } - // X means the ordinal of the item in original sequence, Y means the ordinal of the item in the - // sorted sequence, we know that X and Y value sets are both: - // 0, 1, ..., sampleNum-1 - // we can simply compute sum(X) = sum(Y) = - // (sampleNum-1)*sampleNum / 2 - // and sum(X^2) = sum(Y^2) = - // (sampleNum-1)*sampleNum*(2*sampleNum-1) / 6 - // We use "Pearson correlation coefficient" to compute the order correlation of columns, - // the formula is based on https://en.wikipedia.org/wiki/Pearson_correlation_coefficient. - // Note that (itemsCount*corrX2Sum - corrXSum*corrXSum) would never be zero when sampleNum is larger than 1. - itemsCount := float64(sampleNum) - corrXSum := (itemsCount - 1) * itemsCount / 2.0 - corrX2Sum := (itemsCount - 1) * itemsCount * (2*itemsCount - 1) / 6.0 - return (itemsCount*corrXYSum - corrXSum*corrXSum) / (itemsCount*corrX2Sum - corrXSum*corrXSum) -} - -// BuildColumn builds histogram from samples for column. -func BuildColumn(ctx sessionctx.Context, numBuckets, id int64, collector *SampleCollector, tp *types.FieldType) (*Histogram, error) { - return BuildColumnHist(ctx, numBuckets, id, collector, tp, collector.Count, collector.FMSketch.NDV(), collector.NullCount) -} - -// BuildHistAndTopN build a histogram and TopN for a column or an index from samples. -func BuildHistAndTopN( - ctx sessionctx.Context, - numBuckets, numTopN int, - id int64, - collector *SampleCollector, - tp *types.FieldType, - isColumn bool, - memTracker *memory.Tracker, -) (*Histogram, *TopN, error) { - bufferedMemSize := int64(0) - bufferedReleaseSize := int64(0) - defer func() { - if memTracker != nil { - memTracker.Consume(bufferedMemSize) - memTracker.Release(bufferedReleaseSize) - } - }() - var getComparedBytes func(datum types.Datum) ([]byte, error) - if isColumn { - getComparedBytes = func(datum types.Datum) ([]byte, error) { - encoded, err := codec.EncodeKey(ctx.GetSessionVars().StmtCtx, nil, datum) - if memTracker != nil { - // tmp memory usage - deltaSize := int64(cap(encoded)) - memTracker.BufferedConsume(&bufferedMemSize, deltaSize) - memTracker.BufferedRelease(&bufferedReleaseSize, deltaSize) - } - return encoded, err - } - } else { - getComparedBytes = func(datum types.Datum) ([]byte, error) { - return datum.GetBytes(), nil - } - } - count := collector.Count - ndv := collector.FMSketch.NDV() - nullCount := collector.NullCount - if ndv > count { - ndv = count - } - if count == 0 || len(collector.Samples) == 0 || ndv == 0 { - return NewHistogram(id, ndv, nullCount, 0, tp, 0, collector.TotalSize), nil, nil - } - sc := ctx.GetSessionVars().StmtCtx - samples := collector.Samples - samples, err := SortSampleItems(sc, samples) - if err != nil { - return nil, nil, err - } - hg := NewHistogram(id, ndv, nullCount, 0, tp, numBuckets, collector.TotalSize) - - sampleNum := int64(len(samples)) - // As we use samples to build the histogram, the bucket number and repeat should multiply a factor. - sampleFactor := float64(count) / float64(len(samples)) - - // Step1: collect topn from samples - - // the topNList is always sorted by count from more to less - topNList := make([]TopNMeta, 0, numTopN) - cur, err := getComparedBytes(samples[0].Value) - if err != nil { - return nil, nil, errors.Trace(err) - } - curCnt := float64(0) - var corrXYSum float64 - - // Iterate through the samples - for i := int64(0); i < sampleNum; i++ { - if isColumn { - corrXYSum += float64(i) * float64(samples[i].Ordinal) - } - - sampleBytes, err := getComparedBytes(samples[i].Value) - if err != nil { - return nil, nil, errors.Trace(err) - } - // case 1, this value is equal to the last one: current count++ - if bytes.Equal(cur, sampleBytes) { - curCnt++ - continue - } - // case 2, meet a different value: counting for the "current" is complete - // case 2-1, now topn is empty: append the "current" count directly - if len(topNList) == 0 { - topNList = append(topNList, TopNMeta{Encoded: cur, Count: uint64(curCnt)}) - cur, curCnt = sampleBytes, 1 - continue - } - // case 2-2, now topn is full, and the "current" count is less than the least count in the topn: no need to insert the "current" - if len(topNList) >= numTopN && uint64(curCnt) <= topNList[len(topNList)-1].Count { - cur, curCnt = sampleBytes, 1 - continue - } - // case 2-3, now topn is not full, or the "current" count is larger than the least count in the topn: need to find a slot to insert the "current" - j := len(topNList) - for ; j > 0; j-- { - if uint64(curCnt) < topNList[j-1].Count { - break - } - } - topNList = append(topNList, TopNMeta{}) - copy(topNList[j+1:], topNList[j:]) - topNList[j] = TopNMeta{Encoded: cur, Count: uint64(curCnt)} - if len(topNList) > numTopN { - topNList = topNList[:numTopN] - } - cur, curCnt = sampleBytes, 1 - } - - // Calc the correlation of the column between the handle column. - if isColumn { - hg.Correlation = calcCorrelation(sampleNum, corrXYSum) - } - - // Handle the counting for the last value. Basically equal to the case 2 above. - // now topn is empty: append the "current" count directly - if len(topNList) == 0 { - topNList = append(topNList, TopNMeta{Encoded: cur, Count: uint64(curCnt)}) - } else if len(topNList) < numTopN || uint64(curCnt) > topNList[len(topNList)-1].Count { - // now topn is not full, or the "current" count is larger than the least count in the topn: need to find a slot to insert the "current" - j := len(topNList) - for ; j > 0; j-- { - if uint64(curCnt) < topNList[j-1].Count { - break - } - } - topNList = append(topNList, TopNMeta{}) - copy(topNList[j+1:], topNList[j:]) - topNList[j] = TopNMeta{Encoded: cur, Count: uint64(curCnt)} - if len(topNList) > numTopN { - topNList = topNList[:numTopN] - } - } - - topNList = pruneTopNItem(topNList, ndv, nullCount, sampleNum, count) - - // Step2: exclude topn from samples - for i := int64(0); i < int64(len(samples)); i++ { - sampleBytes, err := getComparedBytes(samples[i].Value) - if err != nil { - return nil, nil, errors.Trace(err) - } - for j := 0; j < len(topNList); j++ { - if bytes.Equal(sampleBytes, topNList[j].Encoded) { - // find the same value in topn: need to skip over this value in samples - copy(samples[i:], samples[uint64(i)+topNList[j].Count:]) - samples = samples[:uint64(len(samples))-topNList[j].Count] - i-- - continue - } - } - } - - for i := 0; i < len(topNList); i++ { - topNList[i].Count *= uint64(sampleFactor) - } - topn := &TopN{TopN: topNList} - - if uint64(count) <= topn.TotalCount() || int(hg.NDV) <= len(topn.TopN) { - // TopN includes all sample data - return hg, topn, nil - } - - // Step3: build histogram with the rest samples - if len(samples) > 0 { - _, err = buildHist(sc, hg, samples, count-int64(topn.TotalCount()), ndv-int64(len(topn.TopN)), int64(numBuckets), memTracker) - if err != nil { - return nil, nil, err - } - } - - return hg, topn, nil -} - -// pruneTopNItem tries to prune the least common values in the top-n list if it is not significantly more common than the values not in the list. -// -// We assume that the ones not in the top-n list's selectivity is 1/remained_ndv which is the internal implementation of EqualRowCount -func pruneTopNItem(topns []TopNMeta, ndv, nullCount, sampleRows, totalRows int64) []TopNMeta { - // If the sampleRows holds all rows, or NDV of samples equals to actual NDV, we just return the TopN directly. - if sampleRows == totalRows || totalRows <= 1 || int64(len(topns)) >= ndv { - return topns - } - // Sum the occurrence except the least common one from the top-n list. To check whether the lest common one is worth - // storing later. - sumCount := uint64(0) - for i := 0; i < len(topns)-1; i++ { - sumCount += topns[i].Count - } - topNNum := len(topns) - for topNNum > 0 { - // Selectivity for the ones not in the top-n list. - // (1 - things in top-n list - null) / remained ndv. - selectivity := 1.0 - float64(sumCount)/float64(sampleRows) - float64(nullCount)/float64(totalRows) - if selectivity < 0.0 { - selectivity = 0 - } - if selectivity > 1 { - selectivity = 1 - } - otherNDV := float64(ndv) - (float64(topNNum) - 1) - if otherNDV > 1 { - selectivity /= otherNDV - } - totalRowsN := float64(totalRows) - n := float64(sampleRows) - k := totalRowsN * float64(topns[topNNum-1].Count) / n - // Since we are sampling without replacement. The distribution would be a hypergeometric distribution. - // Thus the variance is the following formula. - variance := n * k * (totalRowsN - k) * (totalRowsN - n) / (totalRowsN * totalRowsN * (totalRowsN - 1)) - stddev := math.Sqrt(variance) - // We choose the bound that plus two stddev of the sample frequency, plus an additional 0.5 for the continuity correction. - // Note: - // The mean + 2 * stddev is known as Wald confidence interval, plus 0.5 would be continuity-corrected Wald interval - if float64(topns[topNNum-1].Count) > selectivity*n+2*stddev+0.5 { - // Estimated selectivity of this item in the TopN is significantly higher than values not in TopN. - // So this value, and all other values in the TopN (selectivity of which is higher than this value) are - // worth being remained in the TopN list, and we stop pruning now. - break - } - // Current one is not worth storing, remove it and subtract it from sumCount, go to next one. - topNNum-- - if topNNum == 0 { - break - } - sumCount -= topns[topNNum-1].Count - } - return topns[:topNNum] -} diff --git a/statistics/column.go b/statistics/column.go deleted file mode 100644 index cd4025546a6e9..0000000000000 --- a/statistics/column.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "strconv" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// Column represents a column histogram. -type Column struct { - LastAnalyzePos types.Datum - CMSketch *CMSketch - TopN *TopN - FMSketch *FMSketch - Info *model.ColumnInfo - Histogram - - // StatsLoadedStatus indicates the status of column statistics - StatsLoadedStatus - // PhysicalID is the physical table id, - // or it could possibly be -1, which means "stats not available". - // The -1 case could happen in a pseudo stats table, and in this case, this stats should not trigger stats loading. - PhysicalID int64 - Flag int64 - StatsVer int64 // StatsVer is the version of the current stats, used to maintain compatibility - - IsHandle bool -} - -// Copy copies the column. -func (c *Column) Copy() *Column { - if c == nil { - return nil - } - nc := &Column{ - PhysicalID: c.PhysicalID, - Flag: c.Flag, - StatsVer: c.StatsVer, - IsHandle: c.IsHandle, - } - c.LastAnalyzePos.Copy(&nc.LastAnalyzePos) - if c.CMSketch != nil { - nc.CMSketch = c.CMSketch.Copy() - } - if c.TopN != nil { - nc.TopN = c.TopN.Copy() - } - if c.FMSketch != nil { - nc.FMSketch = c.FMSketch.Copy() - } - if c.Info != nil { - nc.Info = c.Info.Clone() - } - nc.Histogram = *c.Histogram.Copy() - nc.StatsLoadedStatus = c.StatsLoadedStatus.Copy() - return nc -} - -func (c *Column) String() string { - return c.Histogram.ToString(0) -} - -// TotalRowCount returns the total count of this column. -func (c *Column) TotalRowCount() float64 { - if c.StatsVer >= Version2 { - return c.Histogram.TotalRowCount() + float64(c.TopN.TotalCount()) - } - return c.Histogram.TotalRowCount() -} - -// NotNullCount returns the count of this column which is not null. -func (c *Column) NotNullCount() float64 { - if c.StatsVer >= Version2 { - return c.Histogram.NotNullCount() + float64(c.TopN.TotalCount()) - } - return c.Histogram.NotNullCount() -} - -// GetIncreaseFactor get the increase factor to adjust the final estimated count when the table is modified. -func (c *Column) GetIncreaseFactor(realtimeRowCount int64) float64 { - columnCount := c.TotalRowCount() - if columnCount == 0 { - // avoid dividing by 0 - return 1.0 - } - return float64(realtimeRowCount) / columnCount -} - -// MemoryUsage returns the total memory usage of Histogram, CMSketch, FMSketch in Column. -// We ignore the size of other metadata in Column -func (c *Column) MemoryUsage() CacheItemMemoryUsage { - var sum int64 - columnMemUsage := &ColumnMemUsage{ - ColumnID: c.Info.ID, - } - histogramMemUsage := c.Histogram.MemoryUsage() - columnMemUsage.HistogramMemUsage = histogramMemUsage - sum = histogramMemUsage - if c.CMSketch != nil { - cmSketchMemUsage := c.CMSketch.MemoryUsage() - columnMemUsage.CMSketchMemUsage = cmSketchMemUsage - sum += cmSketchMemUsage - } - if c.TopN != nil { - topnMemUsage := c.TopN.MemoryUsage() - columnMemUsage.TopNMemUsage = topnMemUsage - sum += topnMemUsage - } - if c.FMSketch != nil { - fmSketchMemUsage := c.FMSketch.MemoryUsage() - columnMemUsage.FMSketchMemUsage = fmSketchMemUsage - sum += fmSketchMemUsage - } - columnMemUsage.TotalMemUsage = sum - return columnMemUsage -} - -// HistogramNeededItems stores the columns/indices whose Histograms need to be loaded from physical kv layer. -// Currently, we only load index/pk's Histogram from kv automatically. Columns' are loaded by needs. -var HistogramNeededItems = neededStatsMap{items: map[model.TableItemID]struct{}{}} - -// IsInvalid checks if this column is invalid. -// If this column has histogram but not loaded yet, -// then we mark it as need histogram. -func (c *Column) IsInvalid( - sctx sessionctx.Context, - collPseudo bool, -) (res bool) { - var totalCount float64 - var ndv int64 - var inValidForCollPseudo, essentialLoaded bool - if sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(sctx) - defer func() { - debugtrace.RecordAnyValuesWithNames(sctx, - "IsInvalid", res, - "InValidForCollPseudo", inValidForCollPseudo, - "TotalCount", totalCount, - "NDV", ndv, - "EssentialLoaded", essentialLoaded, - ) - debugtrace.LeaveContextCommon(sctx) - }() - } - if sctx != nil { - stmtctx := sctx.GetSessionVars().StmtCtx - if (!c.IsStatsInitialized() || c.IsLoadNeeded()) && stmtctx != nil { - if stmtctx.StatsLoad.Timeout > 0 { - logutil.BgLogger().Warn("Hist for column should already be loaded as sync but not found.", - zap.String(strconv.FormatInt(c.Info.ID, 10), c.Info.Name.O)) - } - // In some tests, the c.Info is not set, so we add this check here. - // When we are using stats from PseudoTable(), the table ID will possibly be -1. - // In this case, we don't trigger stats loading. - if c.Info != nil && c.PhysicalID > 0 { - HistogramNeededItems.insert(model.TableItemID{TableID: c.PhysicalID, ID: c.Info.ID, IsIndex: false}) - } - } - } - if collPseudo { - inValidForCollPseudo = true - return true - } - // In some cases, some statistics in column would be evicted - // For example: the cmsketch of the column might be evicted while the histogram and the topn are still exists - // In this case, we will think this column as valid due to we can still use the rest of the statistics to do optimize. - totalCount = c.TotalRowCount() - essentialLoaded = c.IsEssentialStatsLoaded() - ndv = c.Histogram.NDV - return totalCount == 0 || (!essentialLoaded && ndv > 0) -} - -// ItemID implements TableCacheItem -func (c *Column) ItemID() int64 { - return c.Info.ID -} - -// DropUnnecessaryData drops the unnecessary data for the column. -func (c *Column) DropUnnecessaryData() { - if c.StatsVer < Version2 { - c.CMSketch = nil - } - c.TopN = nil - c.Histogram.Bounds = chunk.NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeBlob)}, 0) - c.Histogram.Buckets = make([]Bucket, 0) - c.Histogram.Scalars = make([]scalar, 0) - c.evictedStatus = AllEvicted -} - -// IsAllEvicted indicates whether all stats evicted -func (c *Column) IsAllEvicted() bool { - return c.statsInitialized && c.evictedStatus >= AllEvicted -} - -// GetEvictedStatus indicates the evicted status -func (c *Column) GetEvictedStatus() int { - return c.evictedStatus -} - -// IsStatsInitialized indicates whether stats is initialized -func (c *Column) IsStatsInitialized() bool { - return c.statsInitialized -} - -// GetStatsVer indicates the stats version -func (c *Column) GetStatsVer() int64 { - return c.StatsVer -} - -// IsCMSExist indicates whether CMSketch exists -func (c *Column) IsCMSExist() bool { - return c.CMSketch != nil -} - -// StatusToString gets the string info of StatsLoadedStatus -func (s StatsLoadedStatus) StatusToString() string { - if !s.statsInitialized { - return "unInitialized" - } - switch s.evictedStatus { - case AllLoaded: - return "allLoaded" - case AllEvicted: - return "allEvicted" - } - return "unknown" -} - -// IsAnalyzed indicates whether the column is analyzed. -// The set of IsAnalyzed columns is a subset of the set of StatsAvailable columns. -func (c *Column) IsAnalyzed() bool { - return c.GetStatsVer() != Version0 -} - -// StatsAvailable indicates whether the column stats are collected. -// Note: -// 1. The function merely talks about whether the stats are collected, regardless of the stats loaded status. -// 2. The function is used to decide StatsLoadedStatus.statsInitialized when reading the column stats from storage. -// 3. There are two cases that StatsAvailable is true: -// a. IsAnalyzed is true. -// b. The column is newly-added/modified and its stats are generated according to the default value. -func (c *Column) StatsAvailable() bool { - // Typically, when the column is analyzed, StatsVer is set to Version1/Version2, so we check IsAnalyzed(). - // However, when we add/modify a column, its stats are generated according to the default value without setting - // StatsVer, so we check NDV > 0 || NullCount > 0 for the case. - return c.IsAnalyzed() || c.NDV > 0 || c.NullCount > 0 -} diff --git a/statistics/debugtrace.go b/statistics/debugtrace.go deleted file mode 100644 index e5d617a0c453b..0000000000000 --- a/statistics/debugtrace.go +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "slices" - - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "golang.org/x/exp/maps" -) - -/* - Below is debug trace for statistics.Table and types related to fields in statistics.Table. -*/ - -// StatsTblTraceInfo is simplified from Table and used for debug trace. -type StatsTblTraceInfo struct { - Columns []*statsTblColOrIdxInfo - Indexes []*statsTblColOrIdxInfo - PhysicalID int64 - Version uint64 - Count int64 - ModifyCount int64 -} - -type statsTblColOrIdxInfo struct { - CMSketchInfo *cmSketchInfo - Name string - LoadingStatus string - - ID int64 - NDV int64 - NullCount int64 - LastUpdateVersion uint64 - TotColSize int64 - Correlation float64 - StatsVer int64 - - HistogramSize int - TopNSize int -} - -type cmSketchInfo struct { - Depth int32 - Width int32 - Count uint64 - DefaultValue uint64 -} - -func traceCMSketchInfo(cms *CMSketch) *cmSketchInfo { - if cms == nil { - return nil - } - return &cmSketchInfo{ - Depth: cms.depth, - Width: cms.width, - Count: cms.count, - DefaultValue: cms.defaultValue, - } -} - -func traceColStats(colStats *Column, id int64, out *statsTblColOrIdxInfo) { - if colStats == nil { - return - } - out.ID = id - if colStats.Info != nil { - out.Name = colStats.Info.Name.O - } - out.NDV = colStats.NDV - out.NullCount = colStats.NullCount - out.LastUpdateVersion = colStats.LastUpdateVersion - out.TotColSize = colStats.TotColSize - out.Correlation = colStats.Correlation - out.StatsVer = colStats.StatsVer - out.LoadingStatus = colStats.StatsLoadedStatus.StatusToString() - if colStats.Histogram.Buckets == nil { - out.HistogramSize = -1 - } else { - out.HistogramSize = len(colStats.Histogram.Buckets) - } - if colStats.TopN == nil { - out.TopNSize = -1 - } else { - out.TopNSize = len(colStats.TopN.TopN) - } - out.CMSketchInfo = traceCMSketchInfo(colStats.CMSketch) -} - -func traceIdxStats(idxStats *Index, id int64, out *statsTblColOrIdxInfo) { - if idxStats == nil { - return - } - out.ID = id - if idxStats.Info != nil { - out.Name = idxStats.Info.Name.O - } - out.NDV = idxStats.NDV - out.NullCount = idxStats.NullCount - out.LastUpdateVersion = idxStats.LastUpdateVersion - out.TotColSize = idxStats.TotColSize - out.Correlation = idxStats.Correlation - out.StatsVer = idxStats.StatsVer - out.LoadingStatus = idxStats.StatsLoadedStatus.StatusToString() - if idxStats.Histogram.Buckets == nil { - out.HistogramSize = -1 - } else { - out.HistogramSize = len(idxStats.Histogram.Buckets) - } - if idxStats.TopN == nil { - out.TopNSize = -1 - } else { - out.TopNSize = len(idxStats.TopN.TopN) - } - out.CMSketchInfo = traceCMSketchInfo(idxStats.CMSketch) -} - -// TraceStatsTbl converts a Table to StatsTblTraceInfo, which is suitable for the debug trace. -func TraceStatsTbl(statsTbl *Table) *StatsTblTraceInfo { - if statsTbl == nil { - return nil - } - // Collect table level information - colNum := len(statsTbl.Columns) - idxNum := len(statsTbl.Indices) - traceInfo := &StatsTblTraceInfo{ - PhysicalID: statsTbl.PhysicalID, - Version: statsTbl.Version, - Count: statsTbl.RealtimeCount, - ModifyCount: statsTbl.ModifyCount, - Columns: make([]*statsTblColOrIdxInfo, colNum), - Indexes: make([]*statsTblColOrIdxInfo, idxNum), - } - - // Collect information for each Column - colTraces := make([]statsTblColOrIdxInfo, colNum) - colIDs := maps.Keys(statsTbl.Columns) - slices.Sort(colIDs) - for i, id := range colIDs { - colStatsTrace := &colTraces[i] - traceInfo.Columns[i] = colStatsTrace - colStats := statsTbl.Columns[id] - traceColStats(colStats, id, colStatsTrace) - } - - // Collect information for each Index - idxTraces := make([]statsTblColOrIdxInfo, idxNum) - idxIDs := maps.Keys(statsTbl.Indices) - slices.Sort(idxIDs) - for i, id := range idxIDs { - idxStatsTrace := &idxTraces[i] - traceInfo.Indexes[i] = idxStatsTrace - idxStats := statsTbl.Indices[id] - traceIdxStats(idxStats, id, idxStatsTrace) - } - return traceInfo -} - -/* - Below is debug trace for (*Histogram).locateBucket(). -*/ - -type locateBucketInfo struct { - Value string - BucketIdx int - Exceed bool - InBucket bool - MatchLastValue bool -} - -func debugTraceLocateBucket( - s sessionctx.Context, - value *types.Datum, - exceed bool, - bucketIdx int, - inBucket bool, - matchLastValue bool, -) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := &locateBucketInfo{ - Value: value.String(), - Exceed: exceed, - BucketIdx: bucketIdx, - InBucket: inBucket, - MatchLastValue: matchLastValue, - } - root.AppendStepWithNameToCurrentContext(traceInfo, "Locate value in buckets") -} - -/* - Below is debug trace for used buckets in the histograms during estimation. -*/ - -type bucketInfo struct { - Index int - Count int64 - Repeat int64 -} - -// DebugTraceBuckets is used to trace the buckets used in the histogram. -func DebugTraceBuckets(s sessionctx.Context, hg *Histogram, bucketIdxs []int) { - root := debugtrace.GetOrInitDebugTraceRoot(s) - buckets := make([]bucketInfo, len(bucketIdxs)) - for i := range buckets { - idx := bucketIdxs[i] - buckets[i].Index = idx - if idx >= len(hg.Buckets) || idx < 0 { - buckets[i].Repeat = -1 - buckets[i].Count = -1 - continue - } - bkt := hg.Buckets[idx] - buckets[i].Repeat = bkt.Repeat - buckets[i].Count = bkt.Count - } - root.AppendStepWithNameToCurrentContext(buckets, "Related Buckets in Histogram") -} - -/* - Below is debug trace for used TopN range during estimation. -*/ - -type topNRangeInfo struct { - FirstEncoded []byte - LastEncoded []byte - Count []uint64 - FirstIdx int - LastIdx int -} - -func debugTraceTopNRange(s sessionctx.Context, t *TopN, startIdx, endIdx int) { - if endIdx <= startIdx { - return - } - root := debugtrace.GetOrInitDebugTraceRoot(s) - traceInfo := new(topNRangeInfo) - traceInfo.FirstIdx = startIdx - traceInfo.LastIdx = endIdx - 1 - if startIdx < len(t.TopN) { - traceInfo.FirstEncoded = t.TopN[startIdx].Encoded - } - if endIdx-1 < len(t.TopN) { - traceInfo.LastEncoded = t.TopN[endIdx-1].Encoded - } - cnts := make([]uint64, endIdx-startIdx) - for topNIdx, i := startIdx, 0; topNIdx < endIdx; { - cnts[i] = t.TopN[topNIdx].Count - topNIdx++ - i++ - } - traceInfo.Count = cnts - root.AppendStepWithNameToCurrentContext(traceInfo, "Related TopN Range") -} diff --git a/statistics/handle/BUILD.bazel b/statistics/handle/BUILD.bazel deleted file mode 100644 index 6ac1103d130c0..0000000000000 --- a/statistics/handle/BUILD.bazel +++ /dev/null @@ -1,90 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "handle", - srcs = [ - "bootstrap.go", - "ddl.go", - "dump.go", - "handle.go", - "handle_hist.go", - "update.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl/util", - "//infoschema", - "//kv", - "//metrics", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//statistics", - "//statistics/handle/autoanalyze", - "//statistics/handle/cache", - "//statistics/handle/extstats", - "//statistics/handle/globalstats", - "//statistics/handle/history", - "//statistics/handle/lockstats", - "//statistics/handle/metrics", - "//statistics/handle/storage", - "//statistics/handle/usage", - "//statistics/handle/util", - "//types", - "//util", - "//util/chunk", - "//util/intest", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tiancaiamao_gp//:gp", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "handle_test", - timeout = "short", - srcs = [ - "ddl_test.go", - "dump_test.go", - "gc_test.go", - "handle_hist_test.go", - "main_test.go", - ], - embed = [":handle"], - flaky = True, - race = "on", - shard_count = 27, - deps = [ - "//config", - "//domain", - "//parser/model", - "//planner/cardinality", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/globalstats", - "//statistics/handle/internal", - "//statistics/handle/storage", - "//testkit", - "//testkit/testsetup", - "//types", - "//util", - "//util/mathutil", - "//util/mock", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/autoanalyze/BUILD.bazel b/statistics/handle/autoanalyze/BUILD.bazel deleted file mode 100644 index 147d59635838e..0000000000000 --- a/statistics/handle/autoanalyze/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "autoanalyze", - srcs = ["autoanalyze.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/autoanalyze", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//metrics", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/util", - "//types", - "//util", - "//util/chunk", - "//util/logutil", - "//util/sqlexec", - "//util/timeutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "autoanalyze_test", - timeout = "short", - srcs = ["autoanalyze_test.go"], - flaky = True, - shard_count = 6, - deps = [ - ":autoanalyze", - "//parser/model", - "//statistics", - "//testkit", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - ], -) diff --git a/statistics/handle/bootstrap.go b/statistics/handle/bootstrap.go deleted file mode 100644 index 3101b77a1d499..0000000000000 --- a/statistics/handle/bootstrap.go +++ /dev/null @@ -1,557 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "context" - "strconv" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -func (h *Handle) initStatsMeta4Chunk(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - physicalID := row.GetInt64(1) - // The table is read-only. Please do not modify it. - table, ok := h.TableInfoByID(is, physicalID) - if !ok { - logutil.BgLogger().Debug("unknown physical ID in stats meta table, maybe it has been dropped", zap.Int64("ID", physicalID)) - continue - } - tableInfo := table.Meta() - newHistColl := statistics.HistColl{ - PhysicalID: physicalID, - HavePhysicalID: true, - RealtimeCount: row.GetInt64(3), - ModifyCount: row.GetInt64(2), - Columns: make(map[int64]*statistics.Column, len(tableInfo.Columns)), - Indices: make(map[int64]*statistics.Index, len(tableInfo.Indices)), - } - tbl := &statistics.Table{ - HistColl: newHistColl, - Version: row.GetUint64(0), - Name: getFullTableName(is, tableInfo), - } - cache.Put(physicalID, tbl) // put this table again since it is updated - } -} - -func (h *Handle) initStatsMeta(is infoschema.InfoSchema) (util.StatsCache, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY version, table_id, modify_count, count from mysql.stats_meta" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return nil, errors.Trace(err) - } - defer terror.Call(rc.Close) - tables, err := cache.NewStatsCacheImpl() - if err != nil { - return nil, err - } - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return nil, errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsMeta4Chunk(is, tables, iter) - } - return tables, nil -} - -func (h *Handle) initStatsHistograms4ChunkLite(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - tblID := row.GetInt64(0) - table, ok := cache.Get(tblID) - if !ok { - continue - } - isIndex := row.GetInt64(1) - id := row.GetInt64(2) - ndv := row.GetInt64(3) - version := row.GetUint64(4) - nullCount := row.GetInt64(5) - statsVer := row.GetInt64(7) - flag := row.GetInt64(9) - lastAnalyzePos := row.GetDatum(10, types.NewFieldType(mysql.TypeBlob)) - tbl, _ := h.TableInfoByID(is, table.PhysicalID) - if isIndex > 0 { - var idxInfo *model.IndexInfo - for _, idx := range tbl.Meta().Indices { - if idx.ID == id { - idxInfo = idx - break - } - } - if idxInfo == nil { - continue - } - hist := statistics.NewHistogram(id, ndv, nullCount, version, types.NewFieldType(mysql.TypeBlob), 0, 0) - index := &statistics.Index{ - Histogram: *hist, - Info: idxInfo, - StatsVer: statsVer, - Flag: flag, - PhysicalID: tblID, - } - lastAnalyzePos.Copy(&index.LastAnalyzePos) - if index.IsAnalyzed() { - index.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() - } - table.Indices[hist.ID] = index - } else { - var colInfo *model.ColumnInfo - for _, col := range tbl.Meta().Columns { - if col.ID == id { - colInfo = col - break - } - } - if colInfo == nil { - continue - } - hist := statistics.NewHistogram(id, ndv, nullCount, version, &colInfo.FieldType, 0, row.GetInt64(6)) - hist.Correlation = row.GetFloat64(8) - col := &statistics.Column{ - Histogram: *hist, - PhysicalID: tblID, - Info: colInfo, - IsHandle: tbl.Meta().PKIsHandle && mysql.HasPriKeyFlag(colInfo.GetFlag()), - Flag: flag, - StatsVer: statsVer, - } - lastAnalyzePos.Copy(&col.LastAnalyzePos) - if col.StatsAvailable() { - col.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() - } - table.Columns[hist.ID] = col - } - cache.Put(tblID, table) // put this table again since it is updated - } -} - -func (h *Handle) initStatsHistograms4Chunk(is infoschema.InfoSchema, cache util.StatsCache, iter *chunk.Iterator4Chunk) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - tblID, statsVer := row.GetInt64(0), row.GetInt64(8) - table, ok := cache.Get(tblID) - table = table.Copy() - if !ok { - continue - } - id, ndv, nullCount, version, totColSize := row.GetInt64(2), row.GetInt64(3), row.GetInt64(5), row.GetUint64(4), row.GetInt64(7) - lastAnalyzePos := row.GetDatum(11, types.NewFieldType(mysql.TypeBlob)) - tbl, _ := h.TableInfoByID(is, table.PhysicalID) - if row.GetInt64(1) > 0 { - var idxInfo *model.IndexInfo - for _, idx := range tbl.Meta().Indices { - if idx.ID == id { - idxInfo = idx - break - } - } - if idxInfo == nil { - continue - } - cms, topN, err := statistics.DecodeCMSketchAndTopN(row.GetBytes(6), nil) - if err != nil { - cms = nil - terror.Log(errors.Trace(err)) - } - hist := statistics.NewHistogram(id, ndv, nullCount, version, types.NewFieldType(mysql.TypeBlob), chunk.InitialCapacity, 0) - index := &statistics.Index{ - Histogram: *hist, - CMSketch: cms, - TopN: topN, - Info: idxInfo, - StatsVer: statsVer, - Flag: row.GetInt64(10), - PhysicalID: tblID, - } - if statsVer != statistics.Version0 { - index.StatsLoadedStatus = statistics.NewStatsFullLoadStatus() - } - lastAnalyzePos.Copy(&index.LastAnalyzePos) - table.Indices[hist.ID] = index - } else { - var colInfo *model.ColumnInfo - for _, col := range tbl.Meta().Columns { - if col.ID == id { - colInfo = col - break - } - } - if colInfo == nil { - continue - } - hist := statistics.NewHistogram(id, ndv, nullCount, version, &colInfo.FieldType, 0, totColSize) - hist.Correlation = row.GetFloat64(9) - col := &statistics.Column{ - Histogram: *hist, - PhysicalID: table.PhysicalID, - Info: colInfo, - IsHandle: tbl.Meta().PKIsHandle && mysql.HasPriKeyFlag(colInfo.GetFlag()), - Flag: row.GetInt64(10), - StatsVer: statsVer, - } - lastAnalyzePos.Copy(&col.LastAnalyzePos) - table.Columns[hist.ID] = col - } - cache.Put(tblID, table) // put this table again since it is updated - } -} - -func (h *Handle) initStatsHistogramsLite(is infoschema.InfoSchema, cache util.StatsCache) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY table_id, is_index, hist_id, distinct_count, version, null_count, tot_col_size, stats_ver, correlation, flag, last_analyze_pos from mysql.stats_histograms" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rc.Close) - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsHistograms4ChunkLite(is, cache, iter) - } - return nil -} - -func (h *Handle) initStatsHistograms(is infoschema.InfoSchema, cache util.StatsCache) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY table_id, is_index, hist_id, distinct_count, version, null_count, cm_sketch, tot_col_size, stats_ver, correlation, flag, last_analyze_pos from mysql.stats_histograms" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rc.Close) - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsHistograms4Chunk(is, cache, iter) - } - return nil -} - -func (*Handle) initStatsTopN4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { - affectedIndexes := make(map[*statistics.Index]struct{}) - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - table, ok := cache.Get(row.GetInt64(0)) - if !ok { - continue - } - table = table.Copy() - idx, ok := table.Indices[row.GetInt64(1)] - if !ok || (idx.CMSketch == nil && idx.StatsVer <= statistics.Version1) { - continue - } - if idx.TopN == nil { - idx.TopN = statistics.NewTopN(32) - } - affectedIndexes[idx] = struct{}{} - data := make([]byte, len(row.GetBytes(2))) - copy(data, row.GetBytes(2)) - idx.TopN.AppendTopN(data, row.GetUint64(3)) - cache.Put(table.PhysicalID, table) // put this table again since it is updated - } - for idx := range affectedIndexes { - idx.TopN.Sort() - } -} - -func (h *Handle) initStatsTopN(cache util.StatsCache) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY table_id, hist_id, value, count from mysql.stats_top_n where is_index = 1" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rc.Close) - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsTopN4Chunk(cache, iter) - } - return nil -} - -func (*Handle) initStatsFMSketch4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - table, ok := cache.Get(row.GetInt64(0)) - if !ok { - continue - } - fms, err := statistics.DecodeFMSketch(row.GetBytes(3)) - if err != nil { - fms = nil - terror.Log(errors.Trace(err)) - } - - isIndex := row.GetInt64(1) - id := row.GetInt64(2) - if isIndex == 1 { - if idxStats, ok := table.Indices[id]; ok { - idxStats.FMSketch = fms - } - } else { - if colStats, ok := table.Columns[id]; ok { - colStats.FMSketch = fms - } - } - cache.Put(table.PhysicalID, table) // put this table again since it is updated - } -} - -func (h *Handle) initStatsFMSketch(cache util.StatsCache) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY table_id, is_index, hist_id, value from mysql.stats_fm_sketch" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rc.Close) - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsFMSketch4Chunk(cache, iter) - } - return nil -} - -func (*Handle) initStatsBuckets4Chunk(cache util.StatsCache, iter *chunk.Iterator4Chunk) { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - tableID, isIndex, histID := row.GetInt64(0), row.GetInt64(1), row.GetInt64(2) - table, ok := cache.Get(tableID) - if !ok { - continue - } - table = table.Copy() - var lower, upper types.Datum - var hist *statistics.Histogram - if isIndex > 0 { - index, ok := table.Indices[histID] - if !ok { - continue - } - hist = &index.Histogram - lower, upper = types.NewBytesDatum(row.GetBytes(5)), types.NewBytesDatum(row.GetBytes(6)) - } else { - column, ok := table.Columns[histID] - if !ok { - continue - } - if !mysql.HasPriKeyFlag(column.Info.GetFlag()) { - continue - } - hist = &column.Histogram - d := types.NewBytesDatum(row.GetBytes(5)) - // Setting TimeZone to time.UTC aligns with HistogramFromStorage and can fix #41938. However, #41985 still exist. - // TODO: do the correct time zone conversion for timestamp-type columns' upper/lower bounds. - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - sc.AllowInvalidDate = true - sc.IgnoreZeroInDate = true - var err error - lower, err = d.ConvertTo(sc, &column.Info.FieldType) - if err != nil { - logutil.BgLogger().Debug("decode bucket lower bound failed", zap.Error(err)) - delete(table.Columns, histID) - continue - } - d = types.NewBytesDatum(row.GetBytes(6)) - upper, err = d.ConvertTo(sc, &column.Info.FieldType) - if err != nil { - logutil.BgLogger().Debug("decode bucket upper bound failed", zap.Error(err)) - delete(table.Columns, histID) - continue - } - } - hist.AppendBucketWithNDV(&lower, &upper, row.GetInt64(3), row.GetInt64(4), row.GetInt64(7)) - cache.Put(tableID, table) // put this table again since it is updated - } -} - -func (h *Handle) initStatsBuckets(cache util.StatsCache) error { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) - sql := "select HIGH_PRIORITY table_id, is_index, hist_id, count, repeats, lower_bound, upper_bound, ndv from mysql.stats_buckets order by table_id, is_index, hist_id, bucket_id" - rc, err := util.Exec(h.initStatsCtx, sql) - if err != nil { - return errors.Trace(err) - } - defer terror.Call(rc.Close) - req := rc.NewChunk(nil) - iter := chunk.NewIterator4Chunk(req) - for { - err := rc.Next(ctx, req) - if err != nil { - return errors.Trace(err) - } - if req.NumRows() == 0 { - break - } - h.initStatsBuckets4Chunk(cache, iter) - } - tables := cache.Values() - for _, table := range tables { - for _, idx := range table.Indices { - for i := 1; i < idx.Len(); i++ { - idx.Buckets[i].Count += idx.Buckets[i-1].Count - } - idx.PreCalculateScalar() - } - for _, col := range table.Columns { - for i := 1; i < col.Len(); i++ { - col.Buckets[i].Count += col.Buckets[i-1].Count - } - col.PreCalculateScalar() - } - cache.Put(table.PhysicalID, table) // put this table again since it is updated - } - return nil -} - -// InitStatsLite initiates the stats cache. The function is liter and faster than InitStats. -// Column/index stats are not loaded, i.e., we only load scalars such as NDV, NullCount, Correlation and don't load CMSketch/Histogram/TopN. -func (h *Handle) InitStatsLite(is infoschema.InfoSchema) (err error) { - defer func() { - _, err1 := util.Exec(h.initStatsCtx, "commit") - if err == nil && err1 != nil { - err = err1 - } - }() - _, err = util.Exec(h.initStatsCtx, "begin") - if err != nil { - return err - } - cache, err := h.initStatsMeta(is) - if err != nil { - return errors.Trace(err) - } - err = h.initStatsHistogramsLite(is, cache) - if err != nil { - return errors.Trace(err) - } - h.Replace(cache) - return nil -} - -// InitStats initiates the stats cache. -// Index/PK stats are fully loaded. -// Column stats are not loaded, i.e., we only load scalars such as NDV, NullCount, Correlation and don't load CMSketch/Histogram/TopN. -func (h *Handle) InitStats(is infoschema.InfoSchema) (err error) { - loadFMSketch := config.GetGlobalConfig().Performance.EnableLoadFMSketch - defer func() { - _, err1 := util.Exec(h.initStatsCtx, "commit") - if err == nil && err1 != nil { - err = err1 - } - }() - _, err = util.Exec(h.initStatsCtx, "begin") - if err != nil { - return err - } - cache, err := h.initStatsMeta(is) - if err != nil { - return errors.Trace(err) - } - err = h.initStatsHistograms(is, cache) - if err != nil { - return errors.Trace(err) - } - err = h.initStatsTopN(cache) - if err != nil { - return err - } - if loadFMSketch { - err = h.initStatsFMSketch(cache) - if err != nil { - return err - } - } - err = h.initStatsBuckets(cache) - if err != nil { - return errors.Trace(err) - } - // Set columns' stats status. - for _, table := range cache.Values() { - for _, col := range table.Columns { - if col.StatsAvailable() { - if mysql.HasPriKeyFlag(col.Info.GetFlag()) { - col.StatsLoadedStatus = statistics.NewStatsFullLoadStatus() - } else { - col.StatsLoadedStatus = statistics.NewStatsAllEvictedStatus() - } - } - } - } - h.Replace(cache) - return nil -} - -func getFullTableName(is infoschema.InfoSchema, tblInfo *model.TableInfo) string { - for _, schema := range is.AllSchemas() { - if t, err := is.TableByName(schema.Name, tblInfo.Name); err == nil { - if t.Meta().ID == tblInfo.ID { - return schema.Name.O + "." + tblInfo.Name.O - } - } - } - return strconv.FormatInt(tblInfo.ID, 10) -} diff --git a/statistics/handle/cache/BUILD.bazel b/statistics/handle/cache/BUILD.bazel deleted file mode 100644 index 6161d70904ed4..0000000000000 --- a/statistics/handle/cache/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cache", - srcs = [ - "statscache.go", - "statscacheinner.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/cache", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/cache/internal", - "//statistics/handle/cache/internal/lfu", - "//statistics/handle/cache/internal/mapcache", - "//statistics/handle/cache/internal/metrics", - "//statistics/handle/util", - "//types", - "//util/chunk", - "//util/logutil", - "//util/syncutil", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "cache_test", - timeout = "short", - srcs = ["bench_test.go"], - embed = [":cache"], - flaky = True, - deps = [ - "//config", - "//statistics", - "//statistics/handle/cache/internal/testutil", - "//statistics/handle/util", - "//util/benchdaily", - ], -) diff --git a/statistics/handle/cache/bench_test.go b/statistics/handle/cache/bench_test.go deleted file mode 100644 index d885df191a027..0000000000000 --- a/statistics/handle/cache/bench_test.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cache - -import ( - "math/rand" - "sync" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache/internal/testutil" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/benchdaily" -) - -func benchCopyAndUpdate(b *testing.B, c util.StatsCache) { - var wg sync.WaitGroup - b.ResetTimer() - for i := 0; i < b.N; i++ { - wg.Add(1) - go func() { - defer wg.Done() - t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) - t1.PhysicalID = rand.Int63() - c.UpdateStatsCache([]*statistics.Table{t1}, nil) - }() - } - wg.Wait() - b.StopTimer() -} - -func benchPutGet(b *testing.B, c util.StatsCache) { - var wg sync.WaitGroup - b.ResetTimer() - for i := 0; i < b.N; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) - t1.PhysicalID = rand.Int63() - c.UpdateStatsCache([]*statistics.Table{t1}, nil) - }(i) - } - for i := 0; i < b.N; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - c.Get(int64(i)) - }(i) - } - wg.Wait() - b.StopTimer() -} - -func benchGet(b *testing.B, c util.StatsCache) { - var w sync.WaitGroup - for i := 0; i < b.N; i++ { - w.Add(1) - go func(i int) { - defer w.Done() - t1 := testutil.NewMockStatisticsTable(1, 1, true, false, false) - t1.PhysicalID = rand.Int63() - c.UpdateStatsCache([]*statistics.Table{t1}, nil) - }(i) - } - w.Wait() - b.ResetTimer() - var wg sync.WaitGroup - for i := 0; i < b.N; i++ { - wg.Add(1) - go func(i int) { - defer wg.Done() - c.Get(int64(i)) - }(i) - } - wg.Wait() - b.StopTimer() -} - -func BenchmarkStatsCacheLFUCopyAndUpdate(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchCopyAndUpdate(b, cache) -} - -func BenchmarkStatsCacheMapCacheCopyAndUpdate(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = false - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchCopyAndUpdate(b, cache) -} - -func BenchmarkLFUCachePutGet(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchPutGet(b, cache) -} - -func BenchmarkMapCachePutGet(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = false - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchPutGet(b, cache) -} - -func BenchmarkLFUCacheGet(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchGet(b, cache) -} - -func BenchmarkMapCacheGet(b *testing.B) { - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = false - }) - cache, err := NewStatsCacheImpl() - if err != nil { - b.Fail() - } - benchGet(b, cache) -} - -func TestBenchDaily(*testing.T) { - benchdaily.Run( - BenchmarkStatsCacheLFUCopyAndUpdate, - BenchmarkStatsCacheMapCacheCopyAndUpdate, - BenchmarkLFUCachePutGet, - BenchmarkMapCachePutGet, - BenchmarkLFUCacheGet, - BenchmarkMapCacheGet, - ) -} diff --git a/statistics/handle/cache/internal/BUILD.bazel b/statistics/handle/cache/internal/BUILD.bazel deleted file mode 100644 index 7cd17f787dcf1..0000000000000 --- a/statistics/handle/cache/internal/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "cache", - srcs = ["inner.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/internal/cache", - visibility = ["//statistics/handle:__subpackages__"], - deps = ["//statistics"], -) - -go_library( - name = "internal", - srcs = ["inner.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/cache/internal", - visibility = ["//statistics/handle/cache:__subpackages__"], - deps = ["//statistics"], -) diff --git a/statistics/handle/cache/internal/lfu/BUILD.bazel b/statistics/handle/cache/internal/lfu/BUILD.bazel deleted file mode 100644 index 9da662997f3ca..0000000000000 --- a/statistics/handle/cache/internal/lfu/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "lfu", - srcs = [ - "key_set.go", - "key_set_shard.go", - "lfu_cache.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/cache/internal/lfu", - visibility = ["//statistics/handle/cache:__subpackages__"], - deps = [ - "//statistics", - "//statistics/handle/cache/internal", - "//statistics/handle/cache/internal/metrics", - "//util/intest", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "@com_github_dgraph_io_ristretto//:ristretto", - "@org_golang_x_exp//maps", - "@org_golang_x_exp//rand", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "lfu_test", - timeout = "short", - srcs = ["lfu_cache_test.go"], - embed = [":lfu"], - flaky = True, - race = "on", - shard_count = 9, - deps = [ - "//statistics", - "//statistics/handle/cache/internal/testutil", - "@com_github_stretchr_testify//require", - ], -) diff --git a/statistics/handle/cache/internal/mapcache/BUILD.bazel b/statistics/handle/cache/internal/mapcache/BUILD.bazel deleted file mode 100644 index 00d2fc65e3421..0000000000000 --- a/statistics/handle/cache/internal/mapcache/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mapcache", - srcs = ["map_cache.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/cache/internal/mapcache", - visibility = ["//statistics/handle:__subpackages__"], - deps = [ - "//statistics", - "//statistics/handle/cache/internal", - ], -) diff --git a/statistics/handle/cache/internal/metrics/BUILD.bazel b/statistics/handle/cache/internal/metrics/BUILD.bazel deleted file mode 100644 index 3d075ec6fd05a..0000000000000 --- a/statistics/handle/cache/internal/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/cache/internal/metrics", - visibility = ["//statistics/handle/cache:__subpackages__"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/statistics/handle/cache/internal/metrics/metrics.go b/statistics/handle/cache/internal/metrics/metrics.go deleted file mode 100644 index 9ecd1c6333f63..0000000000000 --- a/statistics/handle/cache/internal/metrics/metrics.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -var ( - // MissCounter is the counter of missing cache. - MissCounter prometheus.Counter - // HitCounter is the counter of hitting cache. - HitCounter prometheus.Counter - // UpdateCounter is the counter of updating cache. - UpdateCounter prometheus.Counter - // DelCounter is the counter of deleting cache. - DelCounter prometheus.Counter - // EvictCounter is the counter of evicting cache. - EvictCounter prometheus.Counter - // RejectCounter is the counter of reject cache. - RejectCounter prometheus.Counter - // CostGauge is the gauge of cost time. - CostGauge prometheus.Gauge - // CapacityGauge is the gauge of capacity. - CapacityGauge prometheus.Gauge -) - -func init() { - initMetricsVars() -} - -// initMetricsVars init copr metrics vars. -func initMetricsVars() { - metrics.StatsCacheCounter = metrics.NewCounterVec( - prometheus.CounterOpts{ - Namespace: "tidb", - Subsystem: "statistics", - Name: "stats_cache_op", - Help: "Counter for statsCache operation", - }, []string{metrics.LblType}) - metrics.StatsCacheGauge = metrics.NewGaugeVec(prometheus.GaugeOpts{ - Namespace: "tidb", - Subsystem: "statistics", - Name: "stats_cache_val", - Help: "gauge of stats cache value", - }, []string{metrics.LblType}) - MissCounter = metrics.StatsCacheCounter.WithLabelValues("miss") - HitCounter = metrics.StatsCacheCounter.WithLabelValues("hit") - UpdateCounter = metrics.StatsCacheCounter.WithLabelValues("update") - DelCounter = metrics.StatsCacheCounter.WithLabelValues("del") - EvictCounter = metrics.StatsCacheCounter.WithLabelValues("evict") - RejectCounter = metrics.StatsCacheCounter.WithLabelValues("reject") - CostGauge = metrics.StatsCacheGauge.WithLabelValues("track") - CapacityGauge = metrics.StatsCacheGauge.WithLabelValues("capacity") -} diff --git a/statistics/handle/cache/internal/testutil/BUILD.bazel b/statistics/handle/cache/internal/testutil/BUILD.bazel deleted file mode 100644 index 161e074e54ac6..0000000000000 --- a/statistics/handle/cache/internal/testutil/BUILD.bazel +++ /dev/null @@ -1,14 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testutil", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/cache/internal/testutil", - visibility = ["//statistics/handle/cache:__subpackages__"], - deps = [ - "//parser/model", - "//parser/mysql", - "//statistics", - "//types", - ], -) diff --git a/statistics/handle/cache/internal/testutil/testutil.go b/statistics/handle/cache/internal/testutil/testutil.go deleted file mode 100644 index 674b8d61da863..0000000000000 --- a/statistics/handle/cache/internal/testutil/testutil.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package testutil - -import ( - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/types" -) - -// NewMockStatisticsTable creates a mock statistics table with given columns and indices. -// each column and index consumes 4 bytes memory -func NewMockStatisticsTable(columns int, indices int, withCMS, withTopN, withHist bool) *statistics.Table { - t := &statistics.Table{} - t.Columns = make(map[int64]*statistics.Column) - t.Indices = make(map[int64]*statistics.Index) - for i := 1; i <= columns; i++ { - t.Columns[int64(i)] = &statistics.Column{ - Info: &model.ColumnInfo{ID: int64(i)}, - StatsLoadedStatus: statistics.NewStatsFullLoadStatus(), - } - if withCMS { - t.Columns[int64(i)].CMSketch = statistics.NewCMSketch(1, 1) - } - if withTopN { - t.Columns[int64(i)].TopN = statistics.NewTopN(1) - t.Columns[int64(i)].TopN.AppendTopN([]byte{}, 1) - } - if withHist { - t.Columns[int64(i)].Histogram = *statistics.NewHistogram(0, 10, 0, 0, types.NewFieldType(mysql.TypeBlob), 1, 0) - } - } - for i := 1; i <= indices; i++ { - t.Indices[int64(i)] = &statistics.Index{ - Info: &model.IndexInfo{ID: int64(i)}, - StatsLoadedStatus: statistics.NewStatsFullLoadStatus(), - } - if withCMS { - t.Indices[int64(i)].CMSketch = statistics.NewCMSketch(1, 1) - } - if withTopN { - t.Indices[int64(i)].TopN = statistics.NewTopN(1) - t.Indices[int64(i)].TopN.AppendTopN([]byte{}, 1) - } - if withHist { - t.Indices[int64(i)].Histogram = *statistics.NewHistogram(0, 10, 0, 0, types.NewFieldType(mysql.TypeBlob), 1, 0) - } - } - return t -} - -// MockTableAppendColumn appends a column to the table. -func MockTableAppendColumn(t *statistics.Table) { - index := int64(len(t.Columns) + 1) - t.Columns[index] = &statistics.Column{ - Info: &model.ColumnInfo{ID: index}, - CMSketch: statistics.NewCMSketch(1, 1), - } -} - -// MockTableAppendIndex appends an index to the table. -func MockTableAppendIndex(t *statistics.Table) { - index := int64(len(t.Indices) + 1) - t.Indices[index] = &statistics.Index{ - Info: &model.IndexInfo{ID: index}, - CMSketch: statistics.NewCMSketch(1, 1), - } -} - -// MockTableRemoveColumn removes the last column of the table. -func MockTableRemoveColumn(t *statistics.Table) { - delete(t.Columns, int64(len(t.Columns))) -} - -// MockTableRemoveIndex removes the last index of the table. -func MockTableRemoveIndex(t *statistics.Table) { - delete(t.Indices, int64(len(t.Indices))) -} diff --git a/statistics/handle/ddl.go b/statistics/handle/ddl.go deleted file mode 100644 index 7110bf23d31d9..0000000000000 --- a/statistics/handle/ddl.go +++ /dev/null @@ -1,393 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "context" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/statistics/handle/storage" - statsutil "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// HandleDDLEvent begins to process a ddl task. -func (h *Handle) HandleDDLEvent(t *util.Event) error { - switch t.Tp { - case model.ActionCreateTable, model.ActionTruncateTable: - ids, err := h.getInitStateTableIDs(t.TableInfo) - if err != nil { - return err - } - for _, id := range ids { - if err := h.insertTableStats2KV(t.TableInfo, id); err != nil { - return err - } - } - case model.ActionDropTable: - ids, err := h.getInitStateTableIDs(t.TableInfo) - if err != nil { - return err - } - for _, id := range ids { - if err := h.resetTableStats2KVForDrop(id); err != nil { - return err - } - } - case model.ActionAddColumn, model.ActionModifyColumn: - ids, err := h.getInitStateTableIDs(t.TableInfo) - if err != nil { - return err - } - for _, id := range ids { - if err := h.insertColStats2KV(id, t.ColumnInfos); err != nil { - return err - } - } - case model.ActionAddTablePartition, model.ActionTruncateTablePartition: - for _, def := range t.PartInfo.Definitions { - if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { - return err - } - } - case model.ActionDropTablePartition: - pruneMode, err := h.GetCurrentPruneMode() - if err != nil { - return err - } - if variable.PartitionPruneMode(pruneMode) == variable.Dynamic && t.PartInfo != nil { - if err := h.updateGlobalStats(t.TableInfo); err != nil { - return err - } - } - for _, def := range t.PartInfo.Definitions { - if err := h.resetTableStats2KVForDrop(def.ID); err != nil { - return err - } - } - case model.ActionReorganizePartition: - for _, def := range t.PartInfo.Definitions { - // TODO: Should we trigger analyze instead of adding 0s? - if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { - return err - } - // Do not update global stats, since the data have not changed! - } - case model.ActionAlterTablePartitioning: - // Add partitioning - for _, def := range t.PartInfo.Definitions { - // TODO: Should we trigger analyze instead of adding 0s? - if err := h.insertTableStats2KV(t.TableInfo, def.ID); err != nil { - return err - } - } - fallthrough - case model.ActionRemovePartitioning: - // Change id for global stats, since the data has not changed! - // Note that t.TableInfo is the current (new) table info - // and t.PartInfo.NewTableID is actually the old table ID! - // (see onReorganizePartition) - return h.changeGlobalStatsID(t.PartInfo.NewTableID, t.TableInfo.ID) - case model.ActionFlashbackCluster: - return h.updateStatsVersion() - } - return nil -} - -// analyzeOptionDefault saves the default values of NumBuckets and NumTopN. -// These values will be used in dynamic mode when we drop table partition and then need to merge global-stats. -// These values originally came from the analyzeOptionDefault structure in the planner/core/planbuilder.go file. -var analyzeOptionDefault = map[ast.AnalyzeOptionType]uint64{ - ast.AnalyzeOptNumBuckets: 256, - ast.AnalyzeOptNumTopN: 20, -} - -// updateStatsVersion will set statistics version to the newest TS, -// then tidb-server will reload automatic. -func (h *Handle) updateStatsVersion() error { - return h.callWithSCtx(func(sctx sessionctx.Context) error { - return storage.UpdateStatsVersion(sctx) - }, statsutil.FlagWrapTxn) -} - -// updateGlobalStats will trigger the merge of global-stats when we drop table partition -func (h *Handle) updateGlobalStats(tblInfo *model.TableInfo) error { - // We need to merge the partition-level stats to global-stats when we drop table partition in dynamic mode. - return h.callWithSCtx(func(sctx sessionctx.Context) error { - tableID := tblInfo.ID - is := sessiontxn.GetTxnManager(sctx).GetTxnInfoSchema() - globalStats, err := h.TableStatsFromStorage(tblInfo, tableID, true, 0) - if err != nil { - return err - } - // If we do not currently have global-stats, no new global-stats will be generated. - if globalStats == nil { - return nil - } - opts := make(map[ast.AnalyzeOptionType]uint64, len(analyzeOptionDefault)) - for key, val := range analyzeOptionDefault { - opts[key] = val - } - // Use current global-stats related information to construct the opts for `MergePartitionStats2GlobalStats` function. - globalColStatsTopNNum, globalColStatsBucketNum := 0, 0 - for colID := range globalStats.Columns { - globalColStatsTopN := globalStats.Columns[colID].TopN - if globalColStatsTopN != nil && len(globalColStatsTopN.TopN) > globalColStatsTopNNum { - globalColStatsTopNNum = len(globalColStatsTopN.TopN) - } - globalColStats := globalStats.Columns[colID] - if globalColStats != nil && len(globalColStats.Buckets) > globalColStatsBucketNum { - globalColStatsBucketNum = len(globalColStats.Buckets) - } - } - if globalColStatsTopNNum != 0 { - opts[ast.AnalyzeOptNumTopN] = uint64(globalColStatsTopNNum) - } - if globalColStatsBucketNum != 0 { - opts[ast.AnalyzeOptNumBuckets] = uint64(globalColStatsBucketNum) - } - // Generate the new column global-stats - newColGlobalStats, err := h.mergePartitionStats2GlobalStats(opts, is, tblInfo, false, nil, nil) - if err != nil { - return err - } - if len(newColGlobalStats.MissingPartitionStats) > 0 { - logutil.BgLogger().Warn("missing partition stats when merging global stats", zap.String("table", tblInfo.Name.L), - zap.String("item", "columns"), zap.Strings("missing", newColGlobalStats.MissingPartitionStats)) - } - for i := 0; i < newColGlobalStats.Num; i++ { - hg, cms, topN := newColGlobalStats.Hg[i], newColGlobalStats.Cms[i], newColGlobalStats.TopN[i] - if hg == nil { - // All partitions have no stats so global stats are not created. - continue - } - // fms for global stats doesn't need to dump to kv. - err = h.SaveStatsToStorage(tableID, newColGlobalStats.Count, newColGlobalStats.ModifyCount, - 0, hg, cms, topN, 2, 1, false, StatsMetaHistorySourceSchemaChange) - if err != nil { - return err - } - } - - // Generate the new index global-stats - globalIdxStatsTopNNum, globalIdxStatsBucketNum := 0, 0 - for _, idx := range tblInfo.Indices { - globalIdxStatsTopN := globalStats.Indices[idx.ID].TopN - if globalIdxStatsTopN != nil && len(globalIdxStatsTopN.TopN) > globalIdxStatsTopNNum { - globalIdxStatsTopNNum = len(globalIdxStatsTopN.TopN) - } - globalIdxStats := globalStats.Indices[idx.ID] - if globalIdxStats != nil && len(globalIdxStats.Buckets) > globalIdxStatsBucketNum { - globalIdxStatsBucketNum = len(globalIdxStats.Buckets) - } - if globalIdxStatsTopNNum != 0 { - opts[ast.AnalyzeOptNumTopN] = uint64(globalIdxStatsTopNNum) - } - if globalIdxStatsBucketNum != 0 { - opts[ast.AnalyzeOptNumBuckets] = uint64(globalIdxStatsBucketNum) - } - newIndexGlobalStats, err := h.mergePartitionStats2GlobalStats(opts, is, tblInfo, true, []int64{idx.ID}, nil) - if err != nil { - return err - } - if len(newIndexGlobalStats.MissingPartitionStats) > 0 { - logutil.BgLogger().Warn("missing partition stats when merging global stats", zap.String("table", tblInfo.Name.L), - zap.String("item", "index "+idx.Name.L), zap.Strings("missing", newIndexGlobalStats.MissingPartitionStats)) - } - for i := 0; i < newIndexGlobalStats.Num; i++ { - hg, cms, topN := newIndexGlobalStats.Hg[i], newIndexGlobalStats.Cms[i], newIndexGlobalStats.TopN[i] - if hg == nil { - // All partitions have no stats so global stats are not created. - continue - } - // fms for global stats doesn't need to dump to kv. - err = h.SaveStatsToStorage(tableID, newIndexGlobalStats.Count, newIndexGlobalStats.ModifyCount, 1, hg, cms, topN, 2, 1, false, StatsMetaHistorySourceSchemaChange) - if err != nil { - return err - } - } - } - return nil - }) -} - -func (h *Handle) changeGlobalStatsID(from, to int64) (err error) { - return h.callWithSCtx(func(sctx sessionctx.Context) error { - for _, table := range []string{"stats_meta", "stats_top_n", "stats_fm_sketch", "stats_buckets", "stats_histograms", "column_stats_usage"} { - _, err = statsutil.Exec(sctx, "update mysql."+table+" set table_id = %? where table_id = %?", to, from) - if err != nil { - return err - } - } - return nil - }, statsutil.FlagWrapTxn) -} - -func (h *Handle) getInitStateTableIDs(tblInfo *model.TableInfo) (ids []int64, err error) { - pi := tblInfo.GetPartitionInfo() - if pi == nil { - return []int64{tblInfo.ID}, nil - } - ids = make([]int64, 0, len(pi.Definitions)+1) - for _, def := range pi.Definitions { - ids = append(ids, def.ID) - } - pruneMode, err := h.GetCurrentPruneMode() - if err != nil { - return nil, err - } - if variable.PartitionPruneMode(pruneMode) == variable.Dynamic { - ids = append(ids, tblInfo.ID) - } - return ids, nil -} - -// DDLEventCh returns ddl events channel in handle. -func (h *Handle) DDLEventCh() chan *util.Event { - return h.ddlEventCh -} - -// insertTableStats2KV inserts a record standing for a new table to stats_meta and inserts some records standing for the -// new columns and indices which belong to this table. -func (h *Handle) insertTableStats2KV(info *model.TableInfo, physicalID int64) (err error) { - statsVer := uint64(0) - defer func() { - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) - } - }() - - return h.callWithSCtx(func(sctx sessionctx.Context) error { - startTS, err := statsutil.GetStartTS(sctx) - if err != nil { - return errors.Trace(err) - } - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id) values(%?, %?)", startTS, physicalID); err != nil { - return err - } - statsVer = startTS - for _, col := range info.Columns { - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, version) values(%?, 0, %?, 0, %?)", physicalID, col.ID, startTS); err != nil { - return err - } - } - for _, idx := range info.Indices { - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, version) values(%?, 1, %?, 0, %?)", physicalID, idx.ID, startTS); err != nil { - return err - } - } - return nil - }, statsutil.FlagWrapTxn) -} - -// resetTableStats2KV resets the count to 0. -func (h *Handle) resetTableStats2KVForDrop(physicalID int64) (err error) { - statsVer := uint64(0) - defer func() { - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) - } - }() - - return h.callWithSCtx(func(sctx sessionctx.Context) error { - startTS, err := statsutil.GetStartTS(sctx) - if err != nil { - return errors.Trace(err) - } - if _, err := statsutil.Exec(sctx, "update mysql.stats_meta set version=%? where table_id =%?", startTS, physicalID); err != nil { - return err - } - return nil - }, statsutil.FlagWrapTxn) -} - -// insertColStats2KV insert a record to stats_histograms with distinct_count 1 and insert a bucket to stats_buckets with default value. -// This operation also updates version. -func (h *Handle) insertColStats2KV(physicalID int64, colInfos []*model.ColumnInfo) (err error) { - statsVer := uint64(0) - defer func() { - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(physicalID, statsVer, StatsMetaHistorySourceSchemaChange) - } - }() - - return h.callWithSCtx(func(sctx sessionctx.Context) error { - startTS, err := statsutil.GetStartTS(sctx) - if err != nil { - return errors.Trace(err) - } - - // First of all, we update the version. - _, err = statsutil.Exec(sctx, "update mysql.stats_meta set version = %? where table_id = %?", startTS, physicalID) - if err != nil { - return err - } - statsVer = startTS - // If we didn't update anything by last SQL, it means the stats of this table does not exist. - if sctx.GetSessionVars().StmtCtx.AffectedRows() > 0 { - // By this step we can get the count of this table, then we can sure the count and repeats of bucket. - var rs sqlexec.RecordSet - rs, err = statsutil.Exec(sctx, "select count from mysql.stats_meta where table_id = %?", physicalID) - if err != nil { - return err - } - defer terror.Call(rs.Close) - req := rs.NewChunk(nil) - err = rs.Next(context.Background(), req) - if err != nil { - return err - } - count := req.GetRow(0).GetInt64(0) - for _, colInfo := range colInfos { - value := types.NewDatum(colInfo.GetOriginDefaultValue()) - value, err = value.ConvertTo(sctx.GetSessionVars().StmtCtx, &colInfo.FieldType) - if err != nil { - return err - } - if value.IsNull() { - // If the adding column has default value null, all the existing rows have null value on the newly added column. - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (version, table_id, is_index, hist_id, distinct_count, null_count) values (%?, %?, 0, %?, 0, %?)", startTS, physicalID, colInfo.ID, count); err != nil { - return err - } - } else { - // If this stats exists, we insert histogram meta first, the distinct_count will always be one. - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_histograms (version, table_id, is_index, hist_id, distinct_count, tot_col_size) values (%?, %?, 0, %?, 1, %?)", startTS, physicalID, colInfo.ID, int64(len(value.GetBytes()))*count); err != nil { - return err - } - value, err = value.ConvertTo(sctx.GetSessionVars().StmtCtx, types.NewFieldType(mysql.TypeBlob)) - if err != nil { - return err - } - // There must be only one bucket for this new column and the value is the default value. - if _, err := statsutil.Exec(sctx, "insert into mysql.stats_buckets (table_id, is_index, hist_id, bucket_id, repeats, count, lower_bound, upper_bound) values (%?, 0, %?, 0, %?, %?, %?, %?)", physicalID, colInfo.ID, count, count, value.GetBytes(), value.GetBytes()); err != nil { - return err - } - } - } - } - return nil - }, statsutil.FlagWrapTxn) -} diff --git a/statistics/handle/ddl_test.go b/statistics/handle/ddl_test.go deleted file mode 100644 index 5e4d53e2860e5..0000000000000 --- a/statistics/handle/ddl_test.go +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handle_test - -import ( - "testing" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestDDLAfterLoad(t *testing.T) { - store, do := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - testKit.MustExec("analyze table t") - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - recordCount := 1000 - for i := 0; i < recordCount; i++ { - testKit.MustExec("insert into t values (?, ?)", i, i+1) - } - testKit.MustExec("analyze table t") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - // add column - testKit.MustExec("alter table t add column c10 int") - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - - sctx := mock.NewContext() - count := cardinality.ColumnGreaterRowCount(sctx, statsTbl, types.NewDatum(recordCount+1), tableInfo.Columns[0].ID) - require.Equal(t, 0.0, count) - count = cardinality.ColumnGreaterRowCount(sctx, statsTbl, types.NewDatum(recordCount+1), tableInfo.Columns[2].ID) - require.Equal(t, 333, int(count)) -} - -func TestDDLTable(t *testing.T) { - store, do := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := do.StatsHandle() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - statsTbl := h.GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - testKit.MustExec("create table t1 (c1 int, c2 int, index idx(c1))") - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo = tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - statsTbl = h.GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - testKit.MustExec("truncate table t1") - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo = tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - statsTbl = h.GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) -} - -func TestDDLHistogram(t *testing.T) { - store, do := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - h := do.StatsHandle() - - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - <-h.DDLEventCh() - testKit.MustExec("insert into t values(1,2),(3,4)") - testKit.MustExec("analyze table t") - - testKit.MustExec("alter table t add column c_null int") - err := h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is := do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - require.True(t, statsTbl.Columns[tableInfo.Columns[2].ID].IsStatsInitialized()) - require.Equal(t, int64(2), statsTbl.Columns[tableInfo.Columns[2].ID].NullCount) - require.Equal(t, int64(0), statsTbl.Columns[tableInfo.Columns[2].ID].Histogram.NDV) - - testKit.MustExec("alter table t add column c3 int NOT NULL") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - require.True(t, statsTbl.Columns[tableInfo.Columns[3].ID].IsStatsInitialized()) - sctx := mock.NewContext() - count, err := cardinality.ColumnEqualRowCount(sctx, statsTbl, types.NewIntDatum(0), tableInfo.Columns[3].ID) - require.NoError(t, err) - require.Equal(t, float64(2), count) - count, err = cardinality.ColumnEqualRowCount(sctx, statsTbl, types.NewIntDatum(1), tableInfo.Columns[3].ID) - require.NoError(t, err) - require.Equal(t, float64(0), count) - - testKit.MustExec("alter table t add column c4 datetime NOT NULL default CURRENT_TIMESTAMP") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - // If we don't use original default value, we will get a pseudo table. - require.False(t, statsTbl.Pseudo) - - testKit.MustExec("alter table t add column c5 varchar(15) DEFAULT '123'") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - require.True(t, statsTbl.Columns[tableInfo.Columns[5].ID].IsStatsInitialized()) - require.Equal(t, 3.0, cardinality.AvgColSize(statsTbl.Columns[tableInfo.Columns[5].ID], statsTbl.RealtimeCount, false)) - - testKit.MustExec("alter table t add column c6 varchar(15) DEFAULT '123', add column c7 varchar(15) DEFAULT '123'") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - testKit.MustExec("create index i on t(c2, c1)") - testKit.MustExec("analyze table t") - rs := testKit.MustQuery("select count(*) from mysql.stats_histograms where table_id = ? and hist_id = 1 and is_index =1", tableInfo.ID) - rs.Check(testkit.Rows("1")) - rs = testKit.MustQuery("select count(*) from mysql.stats_buckets where table_id = ? and hist_id = 1 and is_index = 1", tableInfo.ID) - rs.Check(testkit.Rows("0")) - rs = testKit.MustQuery("select count(*) from mysql.stats_top_n where table_id = ? and hist_id = 1 and is_index = 1", tableInfo.ID) - rs.Check(testkit.Rows("2")) -} - -func TestDDLPartition(t *testing.T) { - store, do := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - for i, pruneMode := range []string{"static", "dynamic"} { - testKit.MustExec("set @@tidb_partition_prune_mode=`" + pruneMode + "`") - testKit.MustExec("set global tidb_partition_prune_mode=`" + pruneMode + "`") - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - h := do.StatsHandle() - if i == 1 { - err := h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - } - createTable := `CREATE TABLE t (a int, b int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21) -)` - testKit.MustExec(createTable) - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - pi := tableInfo.GetPartitionInfo() - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.False(t, statsTbl.Pseudo, "for %v", pruneMode) - } - - testKit.MustExec("insert into t values (1,2),(6,2),(11,2),(16,2)") - testKit.MustExec("analyze table t") - testKit.MustExec("alter table t add column c varchar(15) DEFAULT '123'") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - require.Nil(t, h.Update(is)) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - pi = tableInfo.GetPartitionInfo() - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.False(t, statsTbl.Pseudo) - require.Equal(t, 3.0, cardinality.AvgColSize(statsTbl.Columns[tableInfo.Columns[2].ID], statsTbl.RealtimeCount, false)) - } - - addPartition := "alter table t add partition (partition p4 values less than (26))" - testKit.MustExec(addPartition) - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - pi = tableInfo.GetPartitionInfo() - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.False(t, statsTbl.Pseudo) - } - - truncatePartition := "alter table t truncate partition p4" - testKit.MustExec(truncatePartition) - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - pi = tableInfo.GetPartitionInfo() - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.False(t, statsTbl.Pseudo) - } - - reorganizePartition := "alter table t reorganize partition p0,p1 into (partition p0 values less than (11))" - testKit.MustExec(reorganizePartition) - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - pi = tableInfo.GetPartitionInfo() - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.False(t, statsTbl.Pseudo) - } - } -} diff --git a/statistics/handle/dump.go b/statistics/handle/dump.go deleted file mode 100644 index 33efe5c1fff8d..0000000000000 --- a/statistics/handle/dump.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "context" - "fmt" - "runtime" - "sync" - "sync/atomic" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics/handle/globalstats" - handle_metrics "github.com/pingcap/tidb/statistics/handle/metrics" - "github.com/pingcap/tidb/statistics/handle/storage" - utilstats "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/sqlexec" -) - -// TestLoadStatsErr is only for test. -type TestLoadStatsErr struct{} - -// DumpStatsToJSON dumps statistic to json. -func (h *Handle) DumpStatsToJSON(dbName string, tableInfo *model.TableInfo, - historyStatsExec sqlexec.RestrictedSQLExecutor, dumpPartitionStats bool) (*storage.JSONTable, error) { - var snapshot uint64 - if historyStatsExec != nil { - sctx := historyStatsExec.(sessionctx.Context) - snapshot = sctx.GetSessionVars().SnapshotTS - } - return h.DumpStatsToJSONBySnapshot(dbName, tableInfo, snapshot, dumpPartitionStats) -} - -// DumpHistoricalStatsBySnapshot dumped json tables from mysql.stats_meta_history and mysql.stats_history. -// As implemented in getTableHistoricalStatsToJSONWithFallback, if historical stats are nonexistent, it will fall back -// to the latest stats, and these table names (and partition names) will be returned in fallbackTbls. -func (h *Handle) DumpHistoricalStatsBySnapshot( - dbName string, - tableInfo *model.TableInfo, - snapshot uint64, -) ( - jt *storage.JSONTable, - fallbackTbls []string, - err error, -) { - historicalStatsEnabled, err := h.CheckHistoricalStatsEnable() - if err != nil { - return nil, nil, errors.Errorf("check %v failed: %v", variable.TiDBEnableHistoricalStats, err) - } - if !historicalStatsEnabled { - return nil, nil, errors.Errorf("%v should be enabled", variable.TiDBEnableHistoricalStats) - } - - defer func() { - if err == nil { - handle_metrics.DumpHistoricalStatsSuccessCounter.Inc() - } else { - handle_metrics.DumpHistoricalStatsFailedCounter.Inc() - } - }() - pi := tableInfo.GetPartitionInfo() - if pi == nil { - jt, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, tableInfo.ID, snapshot) - if fallback { - fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s", dbName, tableInfo.Name.O)) - } - return jt, fallbackTbls, err - } - jsonTbl := &storage.JSONTable{ - DatabaseName: dbName, - TableName: tableInfo.Name.L, - Partitions: make(map[string]*storage.JSONTable, len(pi.Definitions)), - } - for _, def := range pi.Definitions { - tbl, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, def.ID, snapshot) - if err != nil { - return nil, nil, errors.Trace(err) - } - if fallback { - fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s %s", dbName, tableInfo.Name.O, def.Name.O)) - } - jsonTbl.Partitions[def.Name.L] = tbl - } - tbl, fallback, err := h.getTableHistoricalStatsToJSONWithFallback(dbName, tableInfo, tableInfo.ID, snapshot) - if err != nil { - return nil, nil, err - } - if fallback { - fallbackTbls = append(fallbackTbls, fmt.Sprintf("%s.%s global", dbName, tableInfo.Name.O)) - } - // dump its global-stats if existed - if tbl != nil { - jsonTbl.Partitions[globalstats.TiDBGlobalStats] = tbl - } - return jsonTbl, fallbackTbls, nil -} - -// DumpStatsToJSONBySnapshot dumps statistic to json. -func (h *Handle) DumpStatsToJSONBySnapshot(dbName string, tableInfo *model.TableInfo, snapshot uint64, dumpPartitionStats bool) (*storage.JSONTable, error) { - pruneMode, err := h.GetCurrentPruneMode() - if err != nil { - return nil, err - } - isDynamicMode := variable.PartitionPruneMode(pruneMode) == variable.Dynamic - pi := tableInfo.GetPartitionInfo() - if pi == nil { - return h.tableStatsToJSON(dbName, tableInfo, tableInfo.ID, snapshot) - } - jsonTbl := &storage.JSONTable{ - DatabaseName: dbName, - TableName: tableInfo.Name.L, - Partitions: make(map[string]*storage.JSONTable, len(pi.Definitions)), - } - // dump partition stats only if in static mode or enable dumpPartitionStats flag in dynamic mode - if !isDynamicMode || dumpPartitionStats { - for _, def := range pi.Definitions { - tbl, err := h.tableStatsToJSON(dbName, tableInfo, def.ID, snapshot) - if err != nil { - return nil, errors.Trace(err) - } - if tbl == nil { - continue - } - jsonTbl.Partitions[def.Name.L] = tbl - } - } - // dump its global-stats if existed - tbl, err := h.tableStatsToJSON(dbName, tableInfo, tableInfo.ID, snapshot) - if err != nil { - return nil, errors.Trace(err) - } - if tbl != nil { - jsonTbl.Partitions[globalstats.TiDBGlobalStats] = tbl - } - return jsonTbl, nil -} - -// getTableHistoricalStatsToJSONWithFallback try to get table historical stats, if not exist, directly fallback to the -// latest stats, and the second return value would be true. -func (h *Handle) getTableHistoricalStatsToJSONWithFallback( - dbName string, - tableInfo *model.TableInfo, - physicalID int64, - snapshot uint64, -) ( - *storage.JSONTable, - bool, - error, -) { - jt, exist, err := h.tableHistoricalStatsToJSON(physicalID, snapshot) - if err != nil { - return nil, false, err - } - if !exist { - jt, err = h.tableStatsToJSON(dbName, tableInfo, physicalID, 0) - fallback := true - if snapshot == 0 { - fallback = false - } - return jt, fallback, err - } - return jt, false, nil -} - -func (h *Handle) tableHistoricalStatsToJSON(physicalID int64, snapshot uint64) (jt *storage.JSONTable, exist bool, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - jt, exist, err = storage.TableHistoricalStatsToJSON(sctx, physicalID, snapshot) - return err - }, utilstats.FlagWrapTxn) - return -} - -func (h *Handle) tableStatsToJSON(dbName string, tableInfo *model.TableInfo, physicalID int64, snapshot uint64) (*storage.JSONTable, error) { - tbl, err := h.TableStatsFromStorage(tableInfo, physicalID, true, snapshot) - if err != nil || tbl == nil { - return nil, err - } - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - tbl.Version, tbl.ModifyCount, tbl.RealtimeCount, err = storage.StatsMetaByTableIDFromStorage(sctx, physicalID, snapshot) - return err - }) - if err != nil { - return nil, err - } - jsonTbl, err := storage.GenJSONTableFromStats(dbName, tableInfo, tbl) - if err != nil { - return nil, err - } - return jsonTbl, nil -} - -// LoadStatsFromJSON will load statistic from JSONTable, and save it to the storage. -// In final, it will also udpate the stats cache. -func (h *Handle) LoadStatsFromJSON(ctx context.Context, is infoschema.InfoSchema, - jsonTbl *storage.JSONTable, concurrencyForPartition uint8) error { - if err := h.LoadStatsFromJSONNoUpdate(ctx, is, jsonTbl, concurrencyForPartition); err != nil { - return errors.Trace(err) - } - return errors.Trace(h.Update(is)) -} - -// LoadStatsFromJSONNoUpdate will load statistic from JSONTable, and save it to the storage. -func (h *Handle) LoadStatsFromJSONNoUpdate(ctx context.Context, is infoschema.InfoSchema, - jsonTbl *storage.JSONTable, concurrencyForPartition uint8) error { - nCPU := uint8(runtime.GOMAXPROCS(0)) - if concurrencyForPartition == 0 { - concurrencyForPartition = nCPU / 2 // default - } - if concurrencyForPartition > nCPU { - concurrencyForPartition = nCPU // for safety - } - - table, err := is.TableByName(model.NewCIStr(jsonTbl.DatabaseName), model.NewCIStr(jsonTbl.TableName)) - if err != nil { - return errors.Trace(err) - } - tableInfo := table.Meta() - pi := tableInfo.GetPartitionInfo() - if pi == nil || jsonTbl.Partitions == nil { - err := h.loadStatsFromJSON(tableInfo, tableInfo.ID, jsonTbl) - if err != nil { - return errors.Trace(err) - } - } else { - // load partition statistics concurrently - taskCh := make(chan model.PartitionDefinition, len(pi.Definitions)) - for _, def := range pi.Definitions { - taskCh <- def - } - close(taskCh) - var wg sync.WaitGroup - e := new(atomic.Pointer[error]) - for i := 0; i < int(concurrencyForPartition); i++ { - wg.Add(1) - h.gpool.Go(func() { - defer func() { - if r := recover(); r != nil { - err := fmt.Errorf("%v", r) - e.CompareAndSwap(nil, &err) - } - wg.Done() - }() - - for def := range taskCh { - tbl := jsonTbl.Partitions[def.Name.L] - if tbl == nil { - continue - } - - loadFunc := h.loadStatsFromJSON - if intest.InTest && ctx.Value(TestLoadStatsErr{}) != nil { - loadFunc = ctx.Value(TestLoadStatsErr{}).(func(*model.TableInfo, int64, *storage.JSONTable) error) - } - - err := loadFunc(tableInfo, def.ID, tbl) - if err != nil { - e.CompareAndSwap(nil, &err) - return - } - if e.Load() != nil { - return - } - } - }) - } - wg.Wait() - if e.Load() != nil { - return *e.Load() - } - - // load global-stats if existed - if globalStats, ok := jsonTbl.Partitions[globalstats.TiDBGlobalStats]; ok { - if err := h.loadStatsFromJSON(tableInfo, tableInfo.ID, globalStats); err != nil { - return errors.Trace(err) - } - } - } - return nil -} - -func (h *Handle) loadStatsFromJSON(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { - tbl, err := storage.TableStatsFromJSON(tableInfo, physicalID, jsonTbl) - if err != nil { - return errors.Trace(err) - } - - for _, col := range tbl.Columns { - // loadStatsFromJSON doesn't support partition table now. - // The table level count and modify_count would be overridden by the SaveMetaToStorage below, so we don't need - // to care about them here. - err = h.SaveStatsToStorage(tbl.PhysicalID, tbl.RealtimeCount, 0, 0, &col.Histogram, col.CMSketch, col.TopN, int(col.GetStatsVer()), 1, false, StatsMetaHistorySourceLoadStats) - if err != nil { - return errors.Trace(err) - } - } - for _, idx := range tbl.Indices { - // loadStatsFromJSON doesn't support partition table now. - // The table level count and modify_count would be overridden by the SaveMetaToStorage below, so we don't need - // to care about them here. - err = h.SaveStatsToStorage(tbl.PhysicalID, tbl.RealtimeCount, 0, 1, &idx.Histogram, idx.CMSketch, idx.TopN, int(idx.GetStatsVer()), 1, false, StatsMetaHistorySourceLoadStats) - if err != nil { - return errors.Trace(err) - } - } - err = h.SaveExtendedStatsToStorage(tbl.PhysicalID, tbl.ExtendedStats, true) - if err != nil { - return errors.Trace(err) - } - return h.SaveMetaToStorage(tbl.PhysicalID, tbl.RealtimeCount, tbl.ModifyCount, StatsMetaHistorySourceLoadStats) -} diff --git a/statistics/handle/dump_test.go b/statistics/handle/dump_test.go deleted file mode 100644 index e9c4419f31c31..0000000000000 --- a/statistics/handle/dump_test.go +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package handle_test - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/statistics/handle/globalstats" - "github.com/pingcap/tidb/statistics/handle/internal" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func requireTableEqual(t *testing.T, a *statistics.Table, b *statistics.Table) { - require.Equal(t, b.RealtimeCount, a.RealtimeCount) - require.Equal(t, b.ModifyCount, a.ModifyCount) - require.Equal(t, len(b.Columns), len(a.Columns)) - for i := range a.Columns { - require.True(t, statistics.HistogramEqual(&a.Columns[i].Histogram, &b.Columns[i].Histogram, false)) - if a.Columns[i].CMSketch == nil { - require.Nil(t, b.Columns[i].CMSketch) - } else { - require.True(t, a.Columns[i].CMSketch.Equal(b.Columns[i].CMSketch)) - } - // The nil case has been considered in (*TopN).Equal() so we don't need to consider it here. - require.Truef(t, a.Columns[i].TopN.Equal(b.Columns[i].TopN), "%v, %v", a.Columns[i].TopN, b.Columns[i].TopN) - } - require.Equal(t, len(b.Indices), len(a.Indices)) - for i := range a.Indices { - require.True(t, statistics.HistogramEqual(&a.Indices[i].Histogram, &b.Indices[i].Histogram, false)) - if a.Indices[i].CMSketch == nil { - require.Nil(t, b.Indices[i].CMSketch) - } else { - require.True(t, a.Indices[i].CMSketch.Equal(b.Indices[i].CMSketch)) - } - require.True(t, a.Indices[i].TopN.Equal(b.Indices[i].TopN)) - } - require.True(t, internal.IsSameExtendedStats(a.ExtendedStats, b.ExtendedStats)) -} - -func cleanStats(tk *testkit.TestKit, do *domain.Domain) { - tk.MustExec("use test") - r := tk.MustQuery("show tables") - for _, tb := range r.Rows() { - tableName := tb[0] - tk.MustExec(fmt.Sprintf("drop table %v", tableName)) - } - tk.MustExec("delete from mysql.stats_meta") - tk.MustExec("delete from mysql.stats_histograms") - tk.MustExec("delete from mysql.stats_buckets") - tk.MustExec("delete from mysql.stats_extended") - tk.MustExec("delete from mysql.stats_fm_sketch") - tk.MustExec("delete from mysql.schema_index_usage") - tk.MustExec("delete from mysql.column_stats_usage") - do.StatsHandle().Clear() -} - -func TestConversion(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - tk.MustExec("create table t (a int, b int)") - tk.MustExec("create index c on t(a,b)") - tk.MustExec("insert into t(a,b) values (3, 1),(2, 1),(1, 10)") - tk.MustExec("analyze table t") - tk.MustExec("insert into t(a,b) values (1, 1),(3, 1),(5, 10)") - is := dom.InfoSchema() - h := dom.StatsHandle() - require.Nil(t, h.DumpStatsDeltaToKV(true)) - require.Nil(t, h.Update(is)) - - tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - jsonTbl, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, jsonTbl) - require.NoError(t, err) - - tbl := h.GetTableStats(tableInfo.Meta()) - requireTableEqual(t, loadTbl, tbl) - cleanStats(tk, dom) - var wg util.WaitGroupWrapper - wg.Run(func() { - require.Nil(t, h.Update(is)) - }) - err = h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0) - wg.Wait() - require.NoError(t, err) - loadTblInStorage := h.GetTableStats(tableInfo.Meta()) - requireTableEqual(t, loadTblInStorage, tbl) -} - -func getStatsJSON(t *testing.T, dom *domain.Domain, db, tableName string) *storage.JSONTable { - is := dom.InfoSchema() - h := dom.StatsHandle() - require.Nil(t, h.Update(is)) - table, err := is.TableByName(model.NewCIStr(db), model.NewCIStr(tableName)) - require.NoError(t, err) - tableInfo := table.Meta() - jsonTbl, err := h.DumpStatsToJSON("test", tableInfo, nil, true) - require.NoError(t, err) - return jsonTbl -} - -func TestDumpGlobalStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 2") - tk.MustExec("insert into t values (1), (2)") - tk.MustExec("analyze table t") - - // global-stats is not existed - stats := getStatsJSON(t, dom, "test", "t") - require.NotNil(t, stats.Partitions["p0"]) - require.NotNil(t, stats.Partitions["p1"]) - require.Nil(t, stats.Partitions[globalstats.TiDBGlobalStats]) - - // global-stats is existed - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t") - stats = getStatsJSON(t, dom, "test", "t") - require.NotNil(t, stats.Partitions["p0"]) - require.NotNil(t, stats.Partitions["p1"]) - require.NotNil(t, stats.Partitions[globalstats.TiDBGlobalStats]) -} - -func TestLoadGlobalStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 2") - tk.MustExec("insert into t values (1), (2)") - tk.MustExec("analyze table t") - globalStats := getStatsJSON(t, dom, "test", "t") - - // remove all statistics - tk.MustExec("delete from mysql.stats_meta") - tk.MustExec("delete from mysql.stats_histograms") - tk.MustExec("delete from mysql.stats_buckets") - dom.StatsHandle().Clear() - clearedStats := getStatsJSON(t, dom, "test", "t") - require.Equal(t, 0, len(clearedStats.Partitions)) - - // load global-stats back - require.Nil(t, dom.StatsHandle().LoadStatsFromJSON(context.Background(), dom.InfoSchema(), globalStats, 0)) - loadedStats := getStatsJSON(t, dom, "test", "t") - require.Equal(t, 3, len(loadedStats.Partitions)) // p0, p1, global -} - -func TestLoadPartitionStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 8") - vals := make([]string, 0, 5000) - for i := 0; i < 5000; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i)) - } - tk.MustExec("insert into t values " + strings.Join(vals, ",")) - tk.MustExec("analyze table t") - - table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - jsonTbl, err := dom.StatsHandle().DumpStatsToJSON("test", tableInfo, nil, true) - require.NoError(t, err) - pi := tableInfo.GetPartitionInfo() - originPartStats := make([]*statistics.Table, 0, len(pi.Definitions)) - for _, def := range pi.Definitions { - originPartStats = append(originPartStats, dom.StatsHandle().GetPartitionStats(tableInfo, def.ID)) - } - originGlobalStats := dom.StatsHandle().GetTableStats(tableInfo) - - // remove all statistics - tk.MustExec("delete from mysql.stats_meta") - tk.MustExec("delete from mysql.stats_histograms") - tk.MustExec("delete from mysql.stats_buckets") - dom.StatsHandle().Clear() - clearedStats := getStatsJSON(t, dom, "test", "t") - require.Equal(t, 0, len(clearedStats.Partitions)) - - // load stats back - require.Nil(t, dom.StatsHandle().LoadStatsFromJSON(context.Background(), dom.InfoSchema(), jsonTbl, 0)) - - // compare - for i, def := range pi.Definitions { - newPartStats := dom.StatsHandle().GetPartitionStats(tableInfo, def.ID) - requireTableEqual(t, originPartStats[i], newPartStats) - } - requireTableEqual(t, originGlobalStats, dom.StatsHandle().GetTableStats(tableInfo)) -} - -func TestLoadPartitionStatsErrPanic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, key(a)) partition by hash(a) partitions 8") - vals := make([]string, 0, 5000) - for i := 0; i < 5000; i++ { - vals = append(vals, fmt.Sprintf("(%v)", i)) - } - tk.MustExec("insert into t values " + strings.Join(vals, ",")) - tk.MustExec("analyze table t") - - table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - jsonTbl, err := dom.StatsHandle().DumpStatsToJSON("test", tableInfo, nil, true) - require.NoError(t, err) - - ctx := context.WithValue(context.Background(), handle.TestLoadStatsErr{}, func(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { - return errors.New("ERROR") - }) - err = dom.StatsHandle().LoadStatsFromJSON(ctx, dom.InfoSchema(), jsonTbl, 0) - require.ErrorContains(t, err, "ERROR") - ctx = context.WithValue(context.Background(), handle.TestLoadStatsErr{}, func(tableInfo *model.TableInfo, physicalID int64, jsonTbl *storage.JSONTable) error { - panic("PANIC") - }) - err = dom.StatsHandle().LoadStatsFromJSON(ctx, dom.InfoSchema(), jsonTbl, 0) - require.ErrorContains(t, err, "PANIC") // recover panic as an error -} - -func TestDumpPartitions(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - createTable := `CREATE TABLE t (a int, b int, primary key(a), index idx(b)) -PARTITION BY RANGE ( a ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21) -)` - tk.MustExec(createTable) - for i := 1; i < 21; i++ { - tk.MustExec(fmt.Sprintf(`insert into t values (%d, %d)`, i, i)) - } - tk.MustExec("analyze table t") - is := dom.InfoSchema() - h := dom.StatsHandle() - require.Nil(t, h.Update(is)) - - table, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := table.Meta() - jsonTbl, err := h.DumpStatsToJSON("test", tableInfo, nil, true) - require.NoError(t, err) - pi := tableInfo.GetPartitionInfo() - originTables := make([]*statistics.Table, 0, len(pi.Definitions)) - for _, def := range pi.Definitions { - originTables = append(originTables, h.GetPartitionStats(tableInfo, def.ID)) - } - - tk.MustExec("delete from mysql.stats_meta") - tk.MustExec("delete from mysql.stats_histograms") - tk.MustExec("delete from mysql.stats_buckets") - h.Clear() - - err = h.LoadStatsFromJSON(context.Background(), dom.InfoSchema(), jsonTbl, 0) - require.NoError(t, err) - for i, def := range pi.Definitions { - tt := h.GetPartitionStats(tableInfo, def.ID) - requireTableEqual(t, originTables[i], tt) - } -} - -func TestDumpAlteredTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - h := dom.StatsHandle() - oriLease := h.Lease() - h.SetLease(1) - defer func() { h.SetLease(oriLease) }() - tk.MustExec("create table t(a int, b int)") - tk.MustExec("analyze table t") - tk.MustExec("alter table t drop column a") - table, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - _, err = h.DumpStatsToJSON("test", table.Meta(), nil, true) - require.NoError(t, err) -} - -func TestDumpCMSketchWithTopN(t *testing.T) { - // Just test if we can store and recover the Top N elements stored in database. - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t(a int)") - testKit.MustExec("insert into t values (1),(3),(4),(2),(5)") - testKit.MustExec("analyze table t") - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := dom.StatsHandle() - require.Nil(t, h.Update(is)) - - // Insert 30 fake data - fakeData := make([][]byte, 0, 30) - for i := 0; i < 30; i++ { - fakeData = append(fakeData, []byte(fmt.Sprintf("%01024d", i))) - } - cms, _, _, _ := statistics.NewCMSketchAndTopN(5, 2048, fakeData, 20, 100) - - stat := h.GetTableStats(tableInfo) - err = h.SaveStatsToStorage(tableInfo.ID, 1, 0, 0, &stat.Columns[tableInfo.Columns[0].ID].Histogram, cms, nil, statistics.Version2, 1, false, handle.StatsMetaHistorySourceLoadStats) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - - stat = h.GetTableStats(tableInfo) - cmsFromStore := stat.Columns[tableInfo.Columns[0].ID].CMSketch - require.NotNil(t, cmsFromStore) - require.True(t, cms.Equal(cmsFromStore)) - - jsonTable, err := h.DumpStatsToJSON("test", tableInfo, nil, true) - require.NoError(t, err) - err = h.LoadStatsFromJSON(context.Background(), is, jsonTable, 0) - require.NoError(t, err) - stat = h.GetTableStats(tableInfo) - cmsFromJSON := stat.Columns[tableInfo.Columns[0].ID].CMSketch.Copy() - require.True(t, cms.Equal(cmsFromJSON)) -} - -func TestDumpPseudoColumns(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t(a int, b int, index idx(a))") - // Force adding an pseudo tables in stats cache. - testKit.MustQuery("select * from t") - testKit.MustExec("analyze table t index idx") - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - h := dom.StatsHandle() - _, err = h.DumpStatsToJSON("test", tbl.Meta(), nil, true) - require.NoError(t, err) -} - -func TestDumpExtendedStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,5),(2,4),(3,3),(4,2),(5,1)") - h := dom.StatsHandle() - require.Nil(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - - is := dom.InfoSchema() - tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tbl := h.GetTableStats(tableInfo.Meta()) - jsonTbl, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, jsonTbl) - require.NoError(t, err) - requireTableEqual(t, loadTbl, tbl) - - cleanStats(tk, dom) - wg := util.WaitGroupWrapper{} - wg.Run(func() { - require.Nil(t, h.Update(is)) - }) - err = h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0) - wg.Wait() - require.NoError(t, err) - loadTblInStorage := h.GetTableStats(tableInfo.Meta()) - requireTableEqual(t, loadTblInStorage, tbl) -} - -func TestDumpVer2Stats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10))") - tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") - // mark column stats as needed - tk.MustExec("select * from t where a = 3") - tk.MustExec("select * from t where b = 'bbb'") - tk.MustExec("alter table t add index single(a)") - tk.MustExec("alter table t add index multi(a, b)") - tk.MustExec("analyze table t with 2 topn") - h := dom.StatsHandle() - is := dom.InfoSchema() - tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - storageTbl, err := h.TableStatsFromStorage(tableInfo.Meta(), tableInfo.Meta().ID, false, 0) - require.NoError(t, err) - - dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - - jsonBytes, err := json.MarshalIndent(dumpJSONTable, "", " ") - require.NoError(t, err) - - loadJSONTable := &storage.JSONTable{} - err = json.Unmarshal(jsonBytes, loadJSONTable) - require.NoError(t, err) - - loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, loadJSONTable) - require.NoError(t, err) - - // assert that a statistics.Table from storage dumped into JSON text and then unmarshalled into a statistics.Table keeps unchanged - requireTableEqual(t, loadTbl, storageTbl) - - // assert that this statistics.Table is the same as the one in stats cache - statsCacheTbl := h.GetTableStats(tableInfo.Meta()) - requireTableEqual(t, loadTbl, statsCacheTbl) - - err = h.LoadStatsFromJSON(context.Background(), is, loadJSONTable, 0) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - statsCacheTbl = h.GetTableStats(tableInfo.Meta()) - // assert that after the JSONTable above loaded into storage then updated into the stats cache, - // the statistics.Table in the stats cache is the same as the unmarshalled statistics.Table - requireTableEqual(t, statsCacheTbl, loadTbl) -} - -func TestLoadStatsForNewCollation(t *testing.T) { - // This test is almost the same as TestDumpVer2Stats, except that: b varchar(10) => b varchar(3) collate utf8mb4_unicode_ci - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(3) collate utf8mb4_unicode_ci)") - tk.MustExec("insert into t value(1, 'aaa'), (1, 'aaa'), (3, 'aab'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc'), (7, 'Ste')") - // mark column stats as needed - tk.MustExec("select * from t where a = 3") - tk.MustExec("select * from t where b = 'bbb'") - tk.MustExec("alter table t add index single(a)") - tk.MustExec("alter table t add index multi(a, b)") - tk.MustExec("analyze table t with 2 topn") - h := dom.StatsHandle() - is := dom.InfoSchema() - tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - storageTbl, err := h.TableStatsFromStorage(tableInfo.Meta(), tableInfo.Meta().ID, false, 0) - require.NoError(t, err) - - dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - - jsonBytes, err := json.MarshalIndent(dumpJSONTable, "", " ") - require.NoError(t, err) - - loadJSONTable := &storage.JSONTable{} - err = json.Unmarshal(jsonBytes, loadJSONTable) - require.NoError(t, err) - - loadTbl, err := storage.TableStatsFromJSON(tableInfo.Meta(), tableInfo.Meta().ID, loadJSONTable) - require.NoError(t, err) - - // assert that a statistics.Table from storage dumped into JSON text and then unmarshalled into a statistics.Table keeps unchanged - requireTableEqual(t, loadTbl, storageTbl) - - // assert that this statistics.Table is the same as the one in stats cache - statsCacheTbl := h.GetTableStats(tableInfo.Meta()) - requireTableEqual(t, loadTbl, statsCacheTbl) - - err = h.LoadStatsFromJSON(context.Background(), is, loadJSONTable, 0) - require.NoError(t, err) - require.Nil(t, h.Update(is)) - statsCacheTbl = h.GetTableStats(tableInfo.Meta()) - // assert that after the JSONTable above loaded into storage then updated into the stats cache, - // the statistics.Table in the stats cache is the same as the unmarshalled statistics.Table - requireTableEqual(t, statsCacheTbl, loadTbl) -} - -func TestJSONTableToBlocks(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10))") - tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") - // mark column stats as needed - tk.MustExec("select * from t where a = 3") - tk.MustExec("select * from t where b = 'bbb'") - tk.MustExec("alter table t add index single(a)") - tk.MustExec("alter table t add index multi(a, b)") - tk.MustExec("analyze table t with 2 topn") - h := dom.StatsHandle() - is := dom.InfoSchema() - tableInfo, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - dumpJSONTable, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - jsOrigin, _ := json.Marshal(dumpJSONTable) - - blockSize := 30 - js, err := h.DumpStatsToJSON("test", tableInfo.Meta(), nil, true) - require.NoError(t, err) - dumpJSONBlocks, err := storage.JSONTableToBlocks(js, blockSize) - require.NoError(t, err) - jsConverted, err := storage.BlocksToJSONTable(dumpJSONBlocks) - require.NoError(t, err) - jsonStr, err := json.Marshal(jsConverted) - require.NoError(t, err) - require.JSONEq(t, string(jsOrigin), string(jsonStr)) -} - -func TestLoadStatsFromOldVersion(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, index idx(b))") - h := dom.StatsHandle() - is := dom.InfoSchema() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - require.NoError(t, h.Update(is)) - - statsJSONFromOldVersion := `{ - "database_name": "test", - "table_name": "t", - "columns": { - "a": { - "histogram": { - "ndv": 0 - }, - "cm_sketch": null, - "null_count": 0, - "tot_col_size": 256, - "last_update_version": 440735055846047747, - "correlation": 0 - }, - "b": { - "histogram": { - "ndv": 0 - }, - "cm_sketch": null, - "null_count": 0, - "tot_col_size": 256, - "last_update_version": 440735055846047747, - "correlation": 0 - } - }, - "indices": { - "idx": { - "histogram": { - "ndv": 0 - }, - "cm_sketch": null, - "null_count": 0, - "tot_col_size": 0, - "last_update_version": 440735055846047747, - "correlation": 0 - } - }, - "count": 256, - "modify_count": 256, - "partitions": null -}` - jsonTbl := &storage.JSONTable{} - require.NoError(t, json.Unmarshal([]byte(statsJSONFromOldVersion), jsonTbl)) - require.NoError(t, h.LoadStatsFromJSON(context.Background(), is, jsonTbl, 0)) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - statsTbl := h.GetTableStats(tbl.Meta()) - for _, col := range statsTbl.Columns { - require.False(t, col.IsStatsInitialized()) - } - for _, idx := range statsTbl.Indices { - require.False(t, idx.IsStatsInitialized()) - } -} diff --git a/statistics/handle/extstats/BUILD.bazel b/statistics/handle/extstats/BUILD.bazel deleted file mode 100644 index 21bfdf18bb78c..0000000000000 --- a/statistics/handle/extstats/BUILD.bazel +++ /dev/null @@ -1,19 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "extstats", - srcs = ["extended_stats.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/extstats", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/model", - "//sessionctx", - "//statistics", - "//statistics/handle/util", - "//util/logutil", - "//util/mathutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) diff --git a/statistics/handle/globalstats/BUILD.bazel b/statistics/handle/globalstats/BUILD.bazel deleted file mode 100644 index 56a311629a2c7..0000000000000 --- a/statistics/handle/globalstats/BUILD.bazel +++ /dev/null @@ -1,50 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "globalstats", - srcs = [ - "global_stats.go", - "global_stats_async.go", - "merge_worker.go", - "topn.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/globalstats", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//sessionctx/stmtctx", - "//statistics", - "//statistics/handle/storage", - "//statistics/handle/util", - "//table", - "//types", - "//util/hack", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tiancaiamao_gp//:gp", - "@org_golang_x_sync//errgroup", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "globalstats_test", - timeout = "short", - srcs = ["topn_bench_test.go"], - embed = [":globalstats"], - flaky = True, - deps = [ - "//parser/mysql", - "//sessionctx/stmtctx", - "//statistics", - "//types", - "//util/chunk", - "//util/codec", - "@com_github_stretchr_testify//require", - "@com_github_tiancaiamao_gp//:gp", - ], -) diff --git a/statistics/handle/globalstats/topn.go b/statistics/handle/globalstats/topn.go deleted file mode 100644 index cab648e31be39..0000000000000 --- a/statistics/handle/globalstats/topn.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package globalstats - -import ( - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics" - "github.com/tiancaiamao/gp" -) - -func mergeGlobalStatsTopN(gp *gp.Pool, sc sessionctx.Context, wrapper *StatsWrapper, - timeZone *time.Location, version int, n uint32, isIndex bool) (*statistics.TopN, - []statistics.TopNMeta, []*statistics.Histogram, error) { - mergeConcurrency := sc.GetSessionVars().AnalyzePartitionMergeConcurrency - killed := &sc.GetSessionVars().Killed - // use original method if concurrency equals 1 or for version1 - if mergeConcurrency < 2 { - return statistics.MergePartTopN2GlobalTopN(timeZone, version, wrapper.AllTopN, n, wrapper.AllHg, isIndex, killed) - } - batchSize := len(wrapper.AllTopN) / mergeConcurrency - if batchSize < 1 { - batchSize = 1 - } else if batchSize > MaxPartitionMergeBatchSize { - batchSize = MaxPartitionMergeBatchSize - } - return MergeGlobalStatsTopNByConcurrency(gp, mergeConcurrency, batchSize, wrapper, timeZone, version, n, isIndex, killed) -} - -// MergeGlobalStatsTopNByConcurrency merge partition topN by concurrency -// To merge global stats topn by concurrency, we will separate the partition topn in concurrency part and deal it with different worker. -// mergeConcurrency is used to control the total concurrency of the running worker, and mergeBatchSize is sued to control -// the partition size for each worker to solve it -func MergeGlobalStatsTopNByConcurrency(gp *gp.Pool, mergeConcurrency, mergeBatchSize int, wrapper *StatsWrapper, - timeZone *time.Location, version int, n uint32, isIndex bool, killed *uint32) (*statistics.TopN, - []statistics.TopNMeta, []*statistics.Histogram, error) { - if len(wrapper.AllTopN) < mergeConcurrency { - mergeConcurrency = len(wrapper.AllTopN) - } - tasks := make([]*TopnStatsMergeTask, 0) - for start := 0; start < len(wrapper.AllTopN); { - end := start + mergeBatchSize - if end > len(wrapper.AllTopN) { - end = len(wrapper.AllTopN) - } - task := NewTopnStatsMergeTask(start, end) - tasks = append(tasks, task) - start = end - } - var wg sync.WaitGroup - taskNum := len(tasks) - taskCh := make(chan *TopnStatsMergeTask, taskNum) - respCh := make(chan *TopnStatsMergeResponse, taskNum) - for i := 0; i < mergeConcurrency; i++ { - worker := NewTopnStatsMergeWorker(taskCh, respCh, wrapper, killed) - wg.Add(1) - gp.Go(func() { - defer wg.Done() - worker.Run(timeZone, isIndex, n, version) - }) - } - for _, task := range tasks { - taskCh <- task - } - close(taskCh) - wg.Wait() - close(respCh) - resps := make([]*TopnStatsMergeResponse, 0) - - // handle Error - hasErr := false - errMsg := make([]string, 0) - for resp := range respCh { - if resp.Err != nil { - hasErr = true - errMsg = append(errMsg, resp.Err.Error()) - } - resps = append(resps, resp) - } - if hasErr { - return nil, nil, nil, errors.New(strings.Join(errMsg, ",")) - } - - // fetch the response from each worker and merge them into global topn stats - sorted := make([]statistics.TopNMeta, 0, mergeConcurrency) - leftTopn := make([]statistics.TopNMeta, 0) - for _, resp := range resps { - if resp.TopN != nil { - sorted = append(sorted, resp.TopN.TopN...) - } - leftTopn = append(leftTopn, resp.PopedTopn...) - } - - globalTopN, popedTopn := statistics.GetMergedTopNFromSortedSlice(sorted, n) - - result := append(leftTopn, popedTopn...) - statistics.SortTopnMeta(result) - return globalTopN, result, wrapper.AllHg, nil -} diff --git a/statistics/handle/handle.go b/statistics/handle/handle.go deleted file mode 100644 index a0d95941e37ca..0000000000000 --- a/statistics/handle/handle.go +++ /dev/null @@ -1,528 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "fmt" - "math" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - ddlUtil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/statistics/handle/cache" - "github.com/pingcap/tidb/statistics/handle/extstats" - "github.com/pingcap/tidb/statistics/handle/globalstats" - "github.com/pingcap/tidb/statistics/handle/history" - "github.com/pingcap/tidb/statistics/handle/lockstats" - handle_metrics "github.com/pingcap/tidb/statistics/handle/metrics" - "github.com/pingcap/tidb/statistics/handle/storage" - "github.com/pingcap/tidb/statistics/handle/usage" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/tiancaiamao/gp" - atomic2 "go.uber.org/atomic" - "go.uber.org/zap" -) - -// Handle can update stats info periodically. -type Handle struct { - pool util.SessionPool - - // initStatsCtx is the ctx only used for initStats - initStatsCtx sessionctx.Context - - // sysProcTracker is used to track sys process like analyze - sysProcTracker sessionctx.SysProcTracker - - // TableInfoGetter is used to fetch table meta info. - util.TableInfoGetter - - // StatsGC is used to GC stats. - util.StatsGC - - // StatsUsage is used to track the usage of column / index statistics. - util.StatsUsage - - // StatsHistory is used to manage historical stats. - util.StatsHistory - - // StatsAnalyze is used to handle auto-analyze and manage analyze jobs. - util.StatsAnalyze - - // StatsLock is used to manage locked stats. - util.StatsLock - - // This gpool is used to reuse goroutine in the mergeGlobalStatsTopN. - gpool *gp.Pool - - // autoAnalyzeProcIDGetter is used to generate auto analyze ID. - autoAnalyzeProcIDGetter func() uint64 - - InitStatsDone chan struct{} - - // ddlEventCh is a channel to notify a ddl operation has happened. - // It is sent only by owner or the drop stats executor, and read by stats handle. - ddlEventCh chan *ddlUtil.Event - - // StatsCache ... - util.StatsCache - - // StatsLoad is used to load stats concurrently - StatsLoad StatsLoad - - lease atomic2.Duration -} - -func (h *Handle) execRows(sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, rerr error) { - _ = h.callWithSCtx(func(sctx sessionctx.Context) error { - rows, fields, rerr = util.ExecRows(sctx, sql, args...) - return nil - }) - return -} - -// Clear the statsCache, only for test. -func (h *Handle) Clear() { - h.StatsCache.Clear() - for len(h.ddlEventCh) > 0 { - <-h.ddlEventCh - } - h.ResetSessionStatsList() -} - -// NewHandle creates a Handle for update stats. -func NewHandle(_, initStatsCtx sessionctx.Context, lease time.Duration, pool util.SessionPool, tracker sessionctx.SysProcTracker, autoAnalyzeProcIDGetter func() uint64) (*Handle, error) { - cfg := config.GetGlobalConfig() - handle := &Handle{ - gpool: gp.New(math.MaxInt16, time.Minute), - ddlEventCh: make(chan *ddlUtil.Event, 1000), - pool: pool, - sysProcTracker: tracker, - autoAnalyzeProcIDGetter: autoAnalyzeProcIDGetter, - InitStatsDone: make(chan struct{}), - TableInfoGetter: util.NewTableInfoGetter(), - StatsAnalyze: autoanalyze.NewStatsAnalyze(pool), - StatsLock: lockstats.NewStatsLock(pool), - } - handle.StatsGC = storage.NewStatsGC(pool, lease, handle.TableInfoGetter, handle.MarkExtendedStatsDeleted) - - handle.initStatsCtx = initStatsCtx - handle.lease.Store(lease) - statsCache, err := cache.NewStatsCacheImpl() - if err != nil { - return nil, err - } - handle.StatsCache = statsCache - handle.StatsHistory = history.NewStatsHistory(pool, handle.StatsCache) - handle.StatsUsage = usage.NewStatsUsageImpl(pool, handle.TableInfoGetter, handle.StatsCache, - handle.StatsHistory, handle.GetLockedTables, handle.GetPartitionStats) - handle.StatsLoad.SubCtxs = make([]sessionctx.Context, cfg.Performance.StatsLoadConcurrency) - handle.StatsLoad.NeededItemsCh = make(chan *NeededItemTask, cfg.Performance.StatsLoadQueueSize) - handle.StatsLoad.TimeoutItemsCh = make(chan *NeededItemTask, cfg.Performance.StatsLoadQueueSize) - handle.StatsLoad.WorkingColMap = map[model.TableItemID][]chan stmtctx.StatsLoadResult{} - return handle, nil -} - -// Lease returns the stats lease. -func (h *Handle) Lease() time.Duration { - return h.lease.Load() -} - -// SetLease sets the stats lease. -func (h *Handle) SetLease(lease time.Duration) { - h.lease.Store(lease) -} - -// UpdateStatsHealthyMetrics updates stats healthy distribution metrics according to stats cache. -func (h *Handle) UpdateStatsHealthyMetrics() { - distribution := make([]int64, 5) - for _, tbl := range h.Values() { - healthy, ok := tbl.GetStatsHealthy() - if !ok { - continue - } - if healthy < 50 { - distribution[0]++ - } else if healthy < 80 { - distribution[1]++ - } else if healthy < 100 { - distribution[2]++ - } else { - distribution[3]++ - } - distribution[4]++ - } - for i, val := range distribution { - handle_metrics.StatsHealthyGauges[i].Set(float64(val)) - } -} - -// Update reads stats meta from store and updates the stats map. -func (h *Handle) Update(is infoschema.InfoSchema) error { - lastVersion := h.MaxTableStatsVersion() - // We need this because for two tables, the smaller version may write later than the one with larger version. - // Consider the case that there are two tables A and B, their version and commit time is (A0, A1) and (B0, B1), - // and A0 < B0 < B1 < A1. We will first read the stats of B, and update the lastVersion to B0, but we cannot read - // the table stats of A0 if we read stats that greater than lastVersion which is B0. - // We can read the stats if the diff between commit time and version is less than three lease. - offset := util.DurationToTS(3 * h.Lease()) - if h.MaxTableStatsVersion() >= offset { - lastVersion = lastVersion - offset - } else { - lastVersion = 0 - } - rows, _, err := h.execRows("SELECT version, table_id, modify_count, count from mysql.stats_meta where version > %? order by version", lastVersion) - if err != nil { - return errors.Trace(err) - } - tables := make([]*statistics.Table, 0, len(rows)) - deletedTableIDs := make([]int64, 0, len(rows)) - for _, row := range rows { - version := row.GetUint64(0) - physicalID := row.GetInt64(1) - modifyCount := row.GetInt64(2) - count := row.GetInt64(3) - table, ok := h.TableInfoByID(is, physicalID) - if !ok { - logutil.BgLogger().Debug("unknown physical ID in stats meta table, maybe it has been dropped", zap.Int64("ID", physicalID)) - deletedTableIDs = append(deletedTableIDs, physicalID) - continue - } - tableInfo := table.Meta() - if oldTbl, ok := h.Get(physicalID); ok && oldTbl.Version >= version && tableInfo.UpdateTS == oldTbl.TblInfoUpdateTS { - continue - } - tbl, err := h.TableStatsFromStorage(tableInfo, physicalID, false, 0) - // Error is not nil may mean that there are some ddl changes on this table, we will not update it. - if err != nil { - logutil.BgLogger().Error("error occurred when read table stats", zap.String("category", "stats"), zap.String("table", tableInfo.Name.O), zap.Error(err)) - continue - } - if tbl == nil { - deletedTableIDs = append(deletedTableIDs, physicalID) - continue - } - tbl.Version = version - tbl.RealtimeCount = count - tbl.ModifyCount = modifyCount - tbl.Name = getFullTableName(is, tableInfo) - tbl.TblInfoUpdateTS = tableInfo.UpdateTS - tables = append(tables, tbl) - } - h.UpdateStatsCache(tables, deletedTableIDs) - return nil -} - -// MergePartitionStats2GlobalStatsByTableID merge the partition-level stats to global-level stats based on the tableID. -func (h *Handle) MergePartitionStats2GlobalStatsByTableID(sc sessionctx.Context, - opts map[ast.AnalyzeOptionType]uint64, is infoschema.InfoSchema, - physicalID int64, - isIndex bool, - histIDs []int64, - _ map[int64]*statistics.Table, -) (globalStats *globalstats.GlobalStats, err error) { - return globalstats.MergePartitionStats2GlobalStatsByTableID(sc, h.gpool, opts, is, physicalID, isIndex, histIDs, h.TableInfoByID, h.callWithSCtx) -} - -func (h *Handle) loadTablePartitionStats(tableInfo *model.TableInfo, partitionDef *model.PartitionDefinition) (*statistics.Table, error) { - var partitionStats *statistics.Table - partitionStats, err := h.TableStatsFromStorage(tableInfo, partitionDef.ID, true, 0) - if err != nil { - return nil, err - } - // if the err == nil && partitionStats == nil, it means we lack the partition-level stats which the physicalID is equal to partitionID. - if partitionStats == nil { - errMsg := fmt.Sprintf("table `%s` partition `%s`", tableInfo.Name.L, partitionDef.Name.L) - err = types.ErrPartitionStatsMissing.GenWithStackByArgs(errMsg) - return nil, err - } - return partitionStats, nil -} - -// MergePartitionStats2GlobalStatsByTableID merge the partition-level stats to global-level stats based on the tableInfo. -func (h *Handle) mergePartitionStats2GlobalStats( - opts map[ast.AnalyzeOptionType]uint64, - is infoschema.InfoSchema, - globalTableInfo *model.TableInfo, - isIndex bool, - histIDs []int64, - _ map[int64]*statistics.Table, -) (gstats *globalstats.GlobalStats, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - gstats, err = globalstats.MergePartitionStats2GlobalStats(sctx, h.gpool, opts, is, globalTableInfo, isIndex, - histIDs, h.TableInfoByID, h.callWithSCtx) - return err - }) - return -} - -// GetTableStats retrieves the statistics table from cache, and the cache will be updated by a goroutine. -// TODO: remove GetTableStats later on. -func (h *Handle) GetTableStats(tblInfo *model.TableInfo) *statistics.Table { - return h.GetPartitionStats(tblInfo, tblInfo.ID) -} - -// GetPartitionStats retrieves the partition stats from cache. -// TODO: remove GetPartitionStats later on. -func (h *Handle) GetPartitionStats(tblInfo *model.TableInfo, pid int64) *statistics.Table { - var tbl *statistics.Table - if h == nil { - tbl = statistics.PseudoTable(tblInfo, false) - tbl.PhysicalID = pid - return tbl - } - tbl, ok := h.Get(pid) - if !ok { - tbl = statistics.PseudoTable(tblInfo, false) - tbl.PhysicalID = pid - if tblInfo.GetPartitionInfo() == nil || h.Len() < 64 { - h.UpdateStatsCache([]*statistics.Table{tbl}, nil) - } - return tbl - } - return tbl -} - -// LoadNeededHistograms will load histograms for those needed columns/indices. -func (h *Handle) LoadNeededHistograms() (err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - loadFMSketch := config.GetGlobalConfig().Performance.EnableLoadFMSketch - return storage.LoadNeededHistograms(sctx, h.StatsCache, loadFMSketch) - }, util.FlagWrapTxn) - return err -} - -// FlushStats flushes the cached stats update into store. -func (h *Handle) FlushStats() { - for len(h.ddlEventCh) > 0 { - e := <-h.ddlEventCh - if err := h.HandleDDLEvent(e); err != nil { - logutil.BgLogger().Error("handle ddl event fail", zap.String("category", "stats"), zap.Error(err)) - } - } - if err := h.DumpStatsDeltaToKV(true); err != nil { - logutil.BgLogger().Error("dump stats delta fail", zap.String("category", "stats"), zap.Error(err)) - } -} - -// TableStatsFromStorage loads table stats info from storage. -func (h *Handle) TableStatsFromStorage(tableInfo *model.TableInfo, physicalID int64, loadAll bool, snapshot uint64) (statsTbl *statistics.Table, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - var ok bool - statsTbl, ok = h.Get(physicalID) - if !ok { - statsTbl = nil - } - statsTbl, err = storage.TableStatsFromStorage(sctx, snapshot, tableInfo, physicalID, loadAll, h.Lease(), statsTbl) - return err - }, util.FlagWrapTxn) - return -} - -// StatsMetaCountAndModifyCount reads count and modify_count for the given table from mysql.stats_meta. -func (h *Handle) StatsMetaCountAndModifyCount(tableID int64) (count, modifyCount int64, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - count, modifyCount, _, err = storage.StatsMetaCountAndModifyCount(sctx, tableID) - return err - }, util.FlagWrapTxn) - return -} - -// SaveTableStatsToStorage saves the stats of a table to storage. -func (h *Handle) SaveTableStatsToStorage(results *statistics.AnalyzeResults, analyzeSnapshot bool, source string) (err error) { - return h.callWithSCtx(func(sctx sessionctx.Context) error { - return SaveTableStatsToStorage(sctx, results, analyzeSnapshot, source) - }) -} - -// SaveTableStatsToStorage saves the stats of a table to storage. -func SaveTableStatsToStorage(sctx sessionctx.Context, results *statistics.AnalyzeResults, analyzeSnapshot bool, source string) error { - statsVer, err := storage.SaveTableStatsToStorage(sctx, results, analyzeSnapshot) - if err == nil && statsVer != 0 { - tableID := results.TableID.GetStatisticsID() - if err1 := history.RecordHistoricalStatsMeta(sctx, tableID, statsVer, source); err1 != nil { - logutil.BgLogger().Error("record historical stats meta failed", - zap.Int64("table-id", tableID), - zap.Uint64("version", statsVer), - zap.String("source", source), - zap.Error(err1)) - } - } - return err -} - -// SaveStatsToStorage saves the stats to storage. -// If count is negative, both count and modify count would not be used and not be written to the table. Unless, corresponding -// fields in the stats_meta table will be updated. -// TODO: refactor to reduce the number of parameters -func (h *Handle) SaveStatsToStorage(tableID int64, count, modifyCount int64, isIndex int, hg *statistics.Histogram, - cms *statistics.CMSketch, topN *statistics.TopN, statsVersion int, isAnalyzed int64, updateAnalyzeTime bool, source string) (err error) { - var statsVer uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - statsVer, err = storage.SaveStatsToStorage(sctx, tableID, - count, modifyCount, isIndex, hg, cms, topN, statsVersion, isAnalyzed, updateAnalyzeTime) - return err - }) - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(tableID, statsVer, source) - } - return -} - -// SaveMetaToStorage will save stats_meta to storage. -func (h *Handle) SaveMetaToStorage(tableID, count, modifyCount int64, source string) (err error) { - var statsVer uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - statsVer, err = storage.SaveMetaToStorage(sctx, tableID, count, modifyCount) - return err - }) - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(tableID, statsVer, source) - } - return -} - -// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta. -func (h *Handle) InsertExtendedStats(statsName string, colIDs []int64, tp int, tableID int64, ifNotExists bool) (err error) { - var statsVer uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - statsVer, err = storage.InsertExtendedStats(sctx, h.StatsCache, statsName, colIDs, tp, tableID, ifNotExists) - return err - }) - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) - } - return -} - -// MarkExtendedStatsDeleted update the status of mysql.stats_extended to be `deleted` and the version of mysql.stats_meta. -func (h *Handle) MarkExtendedStatsDeleted(statsName string, tableID int64, ifExists bool) (err error) { - var statsVer uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - statsVer, err = storage.MarkExtendedStatsDeleted(sctx, h.StatsCache, statsName, tableID, ifExists) - return err - }) - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) - } - return -} - -const updateStatsCacheRetryCnt = 5 - -// ReloadExtendedStatistics drops the cache for extended statistics and reload data from mysql.stats_extended. -// TODO: move this method to the `extstats` package. -func (h *Handle) ReloadExtendedStatistics() error { - return h.callWithSCtx(func(sctx sessionctx.Context) error { - tables := make([]*statistics.Table, 0, h.Len()) - for _, tbl := range h.Values() { - t, err := storage.ExtendedStatsFromStorage(sctx, tbl.Copy(), tbl.PhysicalID, true) - if err != nil { - return err - } - tables = append(tables, t) - } - h.UpdateStatsCache(tables, nil) - return nil - }, util.FlagWrapTxn) -} - -// BuildExtendedStats build extended stats for column groups if needed based on the column samples. -func (h *Handle) BuildExtendedStats(tableID int64, cols []*model.ColumnInfo, collectors []*statistics.SampleCollector) (es *statistics.ExtendedStatsColl, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - es, err = extstats.BuildExtendedStats(sctx, tableID, cols, collectors) - return err - }) - return es, err -} - -// SaveExtendedStatsToStorage writes extended stats of a table into mysql.stats_extended. -func (h *Handle) SaveExtendedStatsToStorage(tableID int64, extStats *statistics.ExtendedStatsColl, isLoad bool) (err error) { - var statsVer uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - statsVer, err = storage.SaveExtendedStatsToStorage(sctx, tableID, extStats, isLoad) - return err - }) - if err == nil && statsVer != 0 { - h.RecordHistoricalStatsMeta(tableID, statsVer, StatsMetaHistorySourceExtendedStats) - } - return -} - -// CheckAnalyzeVersion checks whether all the statistics versions of this table's columns and indexes are the same. -func (h *Handle) CheckAnalyzeVersion(tblInfo *model.TableInfo, physicalIDs []int64, version *int) bool { - // We simply choose one physical id to get its stats. - var tbl *statistics.Table - for _, pid := range physicalIDs { - tbl = h.GetPartitionStats(tblInfo, pid) - if !tbl.Pseudo { - break - } - } - if tbl == nil || tbl.Pseudo { - return true - } - return statistics.CheckAnalyzeVerOnTable(tbl, version) -} - -// RecordHistoricalStatsToStorage records the given table's stats data to mysql.stats_history -func (h *Handle) RecordHistoricalStatsToStorage(dbName string, tableInfo *model.TableInfo, physicalID int64, isPartition bool) (uint64, error) { - var js *storage.JSONTable - var err error - if isPartition { - js, err = h.tableStatsToJSON(dbName, tableInfo, physicalID, 0) - } else { - js, err = h.DumpStatsToJSON(dbName, tableInfo, nil, true) - } - if err != nil { - return 0, errors.Trace(err) - } - - var version uint64 - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - version, err = history.RecordHistoricalStatsToStorage(sctx, physicalID, js) - return err - }, util.FlagWrapTxn) - return version, err -} - -// Close stops the background -func (h *Handle) Close() { - h.gpool.Close() - h.StatsCache.Close() -} - -const ( - // StatsMetaHistorySourceAnalyze indicates stats history meta source from analyze - StatsMetaHistorySourceAnalyze = "analyze" - // StatsMetaHistorySourceLoadStats indicates stats history meta source from load stats - StatsMetaHistorySourceLoadStats = "load stats" - // StatsMetaHistorySourceFlushStats indicates stats history meta source from flush stats - StatsMetaHistorySourceFlushStats = "flush stats" - // StatsMetaHistorySourceSchemaChange indicates stats history meta source from schema change - StatsMetaHistorySourceSchemaChange = "schema change" - // StatsMetaHistorySourceExtendedStats indicates stats history meta source from extended stats - StatsMetaHistorySourceExtendedStats = "extended stats" -) diff --git a/statistics/handle/handletest/BUILD.bazel b/statistics/handle/handletest/BUILD.bazel deleted file mode 100644 index 16a644e7d6f44..0000000000000 --- a/statistics/handle/handletest/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "handletest_test", - timeout = "short", - srcs = [ - "handle_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 37, - deps = [ - "//config", - "//domain", - "//parser/model", - "//planner/cardinality", - "//session", - "//sessionctx/variable", - "//statistics", - "//statistics/handle", - "//statistics/handle/internal", - "//statistics/handle/util", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/collate", - "//util/mock", - "//util/ranger", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/handletest/analyze/BUILD.bazel b/statistics/handle/handletest/analyze/BUILD.bazel deleted file mode 100644 index 0315b85e7a04f..0000000000000 --- a/statistics/handle/handletest/analyze/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "analyze_test", - timeout = "short", - srcs = [ - "analyze_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//domain", - "//parser/model", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/handletest/analyze/analyze_test.go b/statistics/handle/handletest/analyze/analyze_test.go deleted file mode 100644 index e4914a2f066f8..0000000000000 --- a/statistics/handle/handletest/analyze/analyze_test.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package analyze - -import ( - "bytes" - "fmt" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -// nolint:unused -func checkForGlobalStatsWithOpts(t *testing.T, dom *domain.Domain, db, tt, pp string, topn, buckets int) { - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(db), model.NewCIStr(tt)) - require.NoError(t, err) - - tblInfo := tbl.Meta() - physicalID := tblInfo.ID - if pp != "global" { - for _, def := range tbl.Meta().GetPartitionInfo().Definitions { - if def.Name.L == pp { - physicalID = def.ID - } - } - } - tblStats, err := dom.StatsHandle().TableStatsFromStorage(tblInfo, physicalID, true, 0) - require.NoError(t, err) - - delta := buckets/2 + 10 - for _, idxStats := range tblStats.Indices { - if len(idxStats.Buckets) == 0 { - continue // it's not loaded - } - numTopN := idxStats.TopN.Num() - numBuckets := len(idxStats.Buckets) - // since the hist-building algorithm doesn't stipulate the final bucket number to be equal to the expected number exactly, - // we have to check the results by a range here. - require.Equal(t, topn, numTopN) - require.GreaterOrEqual(t, numBuckets, buckets-delta) - require.LessOrEqual(t, numBuckets, buckets+delta) - } - for _, colStats := range tblStats.Columns { - if len(colStats.Buckets) == 0 { - continue // it's not loaded - } - numTopN := colStats.TopN.Num() - numBuckets := len(colStats.Buckets) - require.Equal(t, topn, numTopN) - require.GreaterOrEqual(t, numBuckets, buckets-delta) - require.LessOrEqual(t, numBuckets, buckets+delta) - } -} - -// nolint:unused -func prepareForGlobalStatsWithOptsV2(t *testing.T, dom *domain.Domain, tk *testkit.TestKit, tblName, dbName string) { - tk.MustExec("create database if not exists " + dbName) - tk.MustExec("use " + dbName) - tk.MustExec("drop table if exists " + tblName) - tk.MustExec(` create table ` + tblName + ` (a int, key(a)) partition by range (a) ` + - `(partition p0 values less than (100000), partition p1 values less than (200000))`) - buf1 := bytes.NewBufferString("insert into " + tblName + " values (0)") - buf2 := bytes.NewBufferString("insert into " + tblName + " values (100000)") - for i := 0; i < 1000; i++ { - buf1.WriteString(fmt.Sprintf(", (%v)", 2)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100002)) - buf1.WriteString(fmt.Sprintf(", (%v)", 1)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100001)) - buf1.WriteString(fmt.Sprintf(", (%v)", 0)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100000)) - } - for i := 0; i < 5000; i += 3 { - buf1.WriteString(fmt.Sprintf(", (%v)", i)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100000+i)) - } - tk.MustExec(buf1.String()) - tk.MustExec(buf2.String()) - tk.MustExec("set @@tidb_analyze_version=2") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) -} - -// nolint:unused -func prepareForGlobalStatsWithOpts(t *testing.T, dom *domain.Domain, tk *testkit.TestKit, tblName, dbName string) { - tk.MustExec("create database if not exists " + dbName) - tk.MustExec("use " + dbName) - tk.MustExec("drop table if exists " + tblName) - tk.MustExec(` create table ` + tblName + ` (a int, key(a)) partition by range (a) ` + - `(partition p0 values less than (100000), partition p1 values less than (200000))`) - buf1 := bytes.NewBufferString("insert into " + tblName + " values (0)") - buf2 := bytes.NewBufferString("insert into " + tblName + " values (100000)") - for i := 0; i < 5000; i += 3 { - buf1.WriteString(fmt.Sprintf(", (%v)", i)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100000+i)) - } - for i := 0; i < 1000; i++ { - buf1.WriteString(fmt.Sprintf(", (%v)", 0)) - buf2.WriteString(fmt.Sprintf(", (%v)", 100000)) - } - tk.MustExec(buf1.String()) - tk.MustExec(buf2.String()) - tk.MustExec("set @@tidb_analyze_version=2") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) -} - -func TestAnalyzeVirtualCol(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int generated always as (-a) virtual, c int generated always as (-a) stored, index (c))") - tk.MustExec("insert into t(a) values(2),(1),(1),(3),(NULL)") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("analyze table t") - require.Len(t, tk.MustQuery("show stats_histograms where table_name ='t'").Rows(), 3) -} - -func TestAnalyzeGlobalStatsWithOpts1(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - prepareForGlobalStatsWithOpts(t, dom, tk, "test_gstats_opt", "test_gstats_opt") - - // nolint:unused - type opt struct { - topn int - buckets int - err bool - } - - cases := []opt{ - {1, 37, false}, - {2, 47, false}, - {10, 77, false}, - {77, 219, false}, - {-31, 222, true}, - {10, -77, true}, - {100000, 47, true}, - {77, 47000, true}, - } - for _, ca := range cases { - sql := fmt.Sprintf("analyze table test_gstats_opt with %v topn, %v buckets", ca.topn, ca.buckets) - if !ca.err { - tk.MustExec(sql) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "global", ca.topn, ca.buckets) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "p0", ca.topn, ca.buckets) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt", "test_gstats_opt", "p1", ca.topn, ca.buckets) - } else { - err := tk.ExecToErr(sql) - require.Error(t, err) - } - } -} - -func TestAnalyzeGlobalStatsWithOpts2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - }() - tk.MustExec("set global tidb_persist_analyze_options=false") - prepareForGlobalStatsWithOptsV2(t, dom, tk, "test_gstats_opt2", "test_gstats_opt2") - - tk.MustExec("analyze table test_gstats_opt2 with 2 topn, 10 buckets, 1000 samples") - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 2, 10) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 2, 10) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 2, 10) - - // analyze a partition to let its options be different with others' - tk.MustExec("analyze table test_gstats_opt2 partition p0 with 3 topn, 20 buckets") - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 3, 20) // use new options - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 3, 20) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 2, 10) - - tk.MustExec("analyze table test_gstats_opt2 partition p1 with 1 topn, 15 buckets") - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 1, 15) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 3, 20) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 1, 15) - - tk.MustExec("analyze table test_gstats_opt2 partition p0 with 2 topn, 10 buckets") // change back to 2 topn - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "global", 2, 10) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p0", 2, 10) - checkForGlobalStatsWithOpts(t, dom, "test_gstats_opt2", "test_gstats_opt2", "p1", 1, 15) -} - -func TestAnalyzeWithDynamicPartitionPruneMode(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec(`create table t (a int, key(a)) partition by range(a) - (partition p0 values less than (10), - partition p1 values less than (22))`) - tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) - tk.MustExec(`analyze table t with 1 topn, 2 buckets`) - rows := tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() - require.Len(t, rows, 2) - require.Equal(t, "4", rows[1][6]) - tk.MustExec("insert into t values (1), (2), (2)") - tk.MustExec("analyze table t partition p0 with 1 topn, 2 buckets") - rows = tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() - require.Len(t, rows, 2) - require.Equal(t, "5", rows[1][6]) - tk.MustExec("insert into t values (3)") - tk.MustExec("analyze table t partition p0 index a with 1 topn, 2 buckets") - rows = tk.MustQuery("show stats_buckets where partition_name = 'global' and is_index=1").Rows() - require.Len(t, rows, 1) - require.Equal(t, "6", rows[0][6]) -} - -func TestFMSWithAnalyzePartition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec(`create table t (a int, key(a)) partition by range(a) - (partition p0 values less than (10), - partition p1 values less than (22))`) - tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("0")) - tk.MustExec("analyze table t partition p0 with 1 topn, 2 buckets") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows( - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t's partition p0, reason to use this rate is \"use min(1, 110000/10000) as the sample-rate=1\"", - "Warning 1105 Ignore columns and options when analyze partition in dynamic mode", - )) - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("2")) -} diff --git a/statistics/handle/handletest/analyze/main_test.go b/statistics/handle/handletest/analyze/main_test.go deleted file mode 100644 index 9a2293a71cd4f..0000000000000 --- a/statistics/handle/handletest/analyze/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package analyze - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/handletest/globalstats/BUILD.bazel b/statistics/handle/handletest/globalstats/BUILD.bazel deleted file mode 100644 index 0337ff0059c26..0000000000000 --- a/statistics/handle/handletest/globalstats/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "globalstats_test", - timeout = "short", - srcs = [ - "globalstats_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 14, - deps = [ - "//config", - "//parser/model", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/handletest/globalstats/main_test.go b/statistics/handle/handletest/globalstats/main_test.go deleted file mode 100644 index 5bae6a1f870ef..0000000000000 --- a/statistics/handle/handletest/globalstats/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package globalstats - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/handletest/handle_test.go b/statistics/handle/handletest/handle_test.go deleted file mode 100644 index 8285ec5dab534..0000000000000 --- a/statistics/handle/handletest/handle_test.go +++ /dev/null @@ -1,1687 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handletest - -import ( - "fmt" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle" - "github.com/pingcap/tidb/statistics/handle/internal" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" -) - -func TestEmptyTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int, key cc1(c1), key cc2(c2))") - testKit.MustExec("analyze table t") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - count := cardinality.ColumnGreaterRowCount(mock.NewContext(), statsTbl, types.NewDatum(1), tableInfo.Columns[0].ID) - require.Equal(t, 0.0, count) -} - -func TestColumnIDs(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - testKit.MustExec("insert into t values(1, 2)") - testKit.MustExec("analyze table t") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - sctx := mock.NewContext() - ran := &ranger.Range{ - LowVal: []types.Datum{types.MinNotNullDatum()}, - HighVal: []types.Datum{types.NewIntDatum(2)}, - LowExclude: false, - HighExclude: true, - Collators: collate.GetBinaryCollatorSlice(1), - } - count, err := cardinality.GetRowCountByColumnRanges(sctx, &statsTbl.HistColl, tableInfo.Columns[0].ID, []*ranger.Range{ran}) - require.NoError(t, err) - require.Equal(t, float64(1), count) - - // Drop a column and the offset changed, - testKit.MustExec("alter table t drop column c1") - is = do.InfoSchema() - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - // At that time, we should get c2's stats instead of c1's. - count, err = cardinality.GetRowCountByColumnRanges(sctx, &statsTbl.HistColl, tableInfo.Columns[0].ID, []*ranger.Range{ran}) - require.NoError(t, err) - require.Equal(t, 0.0, count) -} - -func TestDurationToTS(t *testing.T) { - tests := []time.Duration{time.Millisecond, time.Second, time.Minute, time.Hour} - for _, test := range tests { - ts := util.DurationToTS(test) - require.Equal(t, int64(test), oracle.ExtractPhysical(ts)*int64(time.Millisecond)) - } -} - -func TestVersion(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit2 := testkit.NewTestKit(t, store) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t1 (c1 int, c2 int)") - testKit.MustExec("analyze table t1") - do := dom - is := do.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - h, err := handle.NewHandle(testKit.Session(), testKit2.Session(), time.Millisecond, do.SysSessionPool(), do.SysProcTracker(), do.GetAutoAnalyzeProcID) - defer func() { - h.Close() - }() - require.NoError(t, err) - unit := oracle.ComposeTS(1, 0) - testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", 2*unit, tableInfo1.ID) - - require.NoError(t, h.Update(is)) - require.Equal(t, 2*unit, h.MaxTableStatsVersion()) - statsTbl1 := h.GetTableStats(tableInfo1) - require.False(t, statsTbl1.Pseudo) - - testKit.MustExec("create table t2 (c1 int, c2 int)") - testKit.MustExec("analyze table t2") - is = do.InfoSchema() - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tableInfo2 := tbl2.Meta() - // A smaller version write, and we can still read it. - testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", unit, tableInfo2.ID) - require.NoError(t, h.Update(is)) - require.Equal(t, 2*unit, h.MaxTableStatsVersion()) - statsTbl2 := h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - - testKit.MustExec("insert t1 values(1,2)") - testKit.MustExec("analyze table t1") - offset := 3 * unit - testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", offset+4, tableInfo1.ID) - require.NoError(t, h.Update(is)) - require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) - statsTbl1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(1), statsTbl1.RealtimeCount) - - testKit.MustExec("insert t2 values(1,2)") - testKit.MustExec("analyze table t2") - // A smaller version write, and we can still read it. - testKit.MustExec("update mysql.stats_meta set version = ? where table_id = ?", offset+3, tableInfo2.ID) - require.NoError(t, h.Update(is)) - require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) - statsTbl2 = h.GetTableStats(tableInfo2) - require.Equal(t, int64(1), statsTbl2.RealtimeCount) - - testKit.MustExec("insert t2 values(1,2)") - testKit.MustExec("analyze table t2") - // A smaller version write, and we cannot read it. Because at this time, lastThree Version is 4. - testKit.MustExec("update mysql.stats_meta set version = 1 where table_id = ?", tableInfo2.ID) - require.NoError(t, h.Update(is)) - require.Equal(t, offset+uint64(4), h.MaxTableStatsVersion()) - statsTbl2 = h.GetTableStats(tableInfo2) - require.Equal(t, int64(1), statsTbl2.RealtimeCount) - - // We add an index and analyze it, but DDL doesn't load. - testKit.MustExec("alter table t2 add column c3 int") - testKit.MustExec("analyze table t2") - // load it with old schema. - require.NoError(t, h.Update(is)) - statsTbl2 = h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - require.Nil(t, statsTbl2.Columns[int64(3)]) - // Next time DDL updated. - is = do.InfoSchema() - require.NoError(t, h.Update(is)) - statsTbl2 = h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - // We can read it without analyze again! Thanks for PrevLastVersion. - require.NotNil(t, statsTbl2.Columns[int64(3)]) - // assert WithGetTableStatsByQuery get the same result - statsTbl2 = h.GetTableStats(tableInfo2) - require.False(t, statsTbl2.Pseudo) - require.NotNil(t, statsTbl2.Columns[int64(3)]) -} - -func TestLoadHist(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 varchar(12), c2 char(12))") - do := dom - h := do.StatsHandle() - err := h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - rowCount := 10 - for i := 0; i < rowCount; i++ { - testKit.MustExec("insert into t values('a','ddd')") - } - testKit.MustExec("analyze table t") - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - oldStatsTbl := h.GetTableStats(tableInfo) - for i := 0; i < rowCount; i++ { - testKit.MustExec("insert into t values('bb','sdfga')") - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - err = h.Update(do.InfoSchema()) - require.NoError(t, err) - newStatsTbl := h.GetTableStats(tableInfo) - // The stats table is updated. - require.False(t, oldStatsTbl == newStatsTbl) - // Only the TotColSize of histograms is updated. - for id, hist := range oldStatsTbl.Columns { - require.Less(t, hist.TotColSize, newStatsTbl.Columns[id].TotColSize) - - temp := hist.TotColSize - hist.TotColSize = newStatsTbl.Columns[id].TotColSize - require.True(t, statistics.HistogramEqual(&hist.Histogram, &newStatsTbl.Columns[id].Histogram, false)) - hist.TotColSize = temp - - require.True(t, hist.CMSketch.Equal(newStatsTbl.Columns[id].CMSketch)) - require.Equal(t, newStatsTbl.Columns[id].Info, hist.Info) - } - // Add column c3, we only update c3. - testKit.MustExec("alter table t add column c3 int") - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - require.NoError(t, h.Update(is)) - newStatsTbl2 := h.GetTableStats(tableInfo) - require.False(t, newStatsTbl2 == newStatsTbl) - // The histograms is not updated. - for id, hist := range newStatsTbl.Columns { - require.Equal(t, newStatsTbl2.Columns[id], hist) - } - require.Greater(t, newStatsTbl2.Columns[int64(3)].LastUpdateVersion, newStatsTbl2.Columns[int64(1)].LastUpdateVersion) -} - -func TestCorrelation(t *testing.T) { - store := testkit.CreateMockStore(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t(c1 int primary key, c2 int)") - testKit.MustExec("select * from t where c1 > 10 and c2 > 10") - testKit.MustExec("insert into t values(1,1),(3,12),(4,20),(2,7),(5,21)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result := testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - testKit.MustExec("insert into t values(8,18)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) - - testKit.MustExec("truncate table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 0) - testKit.MustExec("insert into t values(1,21),(3,12),(4,7),(2,20),(5,1)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "-1", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "-1", result.Rows()[1][9]) - testKit.MustExec("insert into t values(8,4)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "-0.9428571428571428", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "-0.9428571428571428", result.Rows()[1][9]) - - testKit.MustExec("truncate table t") - testKit.MustExec("insert into t values (1,1),(2,1),(3,1),(4,1),(5,1),(6,1),(7,1),(8,1),(9,1),(10,1),(11,1),(12,1),(13,1),(14,1),(15,1),(16,1),(17,1),(18,1),(19,1),(20,2),(21,2),(22,2),(23,2),(24,2),(25,2)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - - testKit.MustExec("drop table t") - testKit.MustExec("create table t(c1 int, c2 int)") - testKit.MustExec("insert into t values(1,1),(2,7),(3,12),(4,20),(5,21),(8,18)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "0.8285714285714286", result.Rows()[1][9]) - - testKit.MustExec("truncate table t") - testKit.MustExec("insert into t values(1,1),(2,7),(3,12),(8,18),(4,20),(5,21)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0.8285714285714286", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - testKit.MustExec("set @@session.tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't'").Sort() - require.Len(t, result.Rows(), 2) - require.Equal(t, "0.8285714285714286", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - - testKit.MustExec("drop table t") - testKit.MustExec("create table t(c1 int primary key, c2 int, c3 int, key idx_c2(c2))") - testKit.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3)") - testKit.MustExec("set @@session.tidb_analyze_version=1") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 0").Sort() - require.Len(t, result.Rows(), 3) - require.Equal(t, "0", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - require.Equal(t, "1", result.Rows()[2][9]) - result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 1").Sort() - require.Len(t, result.Rows(), 1) - require.Equal(t, "0", result.Rows()[0][9]) - testKit.MustExec("set @@tidb_analyze_version=2") - testKit.MustExec("analyze table t") - result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 0").Sort() - require.Len(t, result.Rows(), 3) - require.Equal(t, "1", result.Rows()[0][9]) - require.Equal(t, "1", result.Rows()[1][9]) - require.Equal(t, "1", result.Rows()[2][9]) - result = testKit.MustQuery("show stats_histograms where Table_name = 't' and Is_index = 1").Sort() - require.Len(t, result.Rows(), 1) - require.Equal(t, "0", result.Rows()[0][9]) -} - -func TestMergeGlobalTopN(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test;") - tk.MustExec("drop table if exists t;") - tk.MustExec("set @@session.tidb_analyze_version=2;") - tk.MustExec("set @@session.tidb_partition_prune_mode='dynamic';") - tk.MustExec(`create table t (a int, b int, key(b)) partition by range (a) ( - partition p0 values less than (10), - partition p1 values less than (20) - );`) - tk.MustExec("insert into t values(1, 1), (1, 1), (1, 1), (1, 1), (2, 2), (2, 2), (3, 3), (3, 3), (3, 3), " + - "(11, 11), (11, 11), (11, 11), (12, 12), (12, 12), (12, 12), (13, 3), (13, 3);") - tk.MustExec("analyze table t with 2 topn;") - // The top2 values in partition p0 are 1(count = 4) and 3(count = 3). - tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'p0';").Check(testkit.Rows( - ("test t p0 b 0 1 4"), - ("test t p0 b 0 3 3"), - ("test t p0 b 1 1 4"), - ("test t p0 b 1 3 3"))) - // The top2 values in partition p1 are 11(count = 3) and 12(count = 3). - tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'p1';").Check(testkit.Rows( - ("test t p1 b 0 11 3"), - ("test t p1 b 0 12 3"), - ("test t p1 b 1 11 3"), - ("test t p1 b 1 12 3"))) - // The top2 values in global are 1(count = 4) and 3(count = 5). - // Notice: The value 3 does not appear in the topN structure of partition one. - // But we can still use the histogram to calculate its accurate value. - tk.MustQuery("show stats_topn where table_name = 't' and column_name = 'b' and partition_name = 'global';").Check(testkit.Rows( - ("test t global b 0 1 4"), - ("test t global b 0 3 5"), - ("test t global b 1 1 4"), - ("test t global b 1 3 5"))) -} - -func TestExtendedStatsOps(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int primary key, b int, c int, d int)") - tk.MustExec("insert into t values(1,1,5,1),(2,2,4,2),(3,3,3,3),(4,4,2,4),(5,5,1,5)") - tk.MustExec("analyze table t") - err := tk.ExecToErr("alter table not_exist_db.t add stats_extended s1 correlation(b,c)") - require.Equal(t, "[schema:1146]Table 'not_exist_db.t' doesn't exist", err.Error()) - err = tk.ExecToErr("alter table not_exist_tbl add stats_extended s1 correlation(b,c)") - require.Equal(t, "[schema:1146]Table 'test.not_exist_tbl' doesn't exist", err.Error()) - err = tk.ExecToErr("alter table t add stats_extended s1 correlation(b,e)") - require.Equal(t, "[schema:1054]Unknown column 'e' in 't'", err.Error()) - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustQuery("show warnings").Check(testkit.Rows( - "Warning 1105 No need to create correlation statistics on the integer primary key column", - )) - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) - err = tk.ExecToErr("alter table t add stats_extended s1 correlation(b,c,d)") - require.Equal(t, "Only support Correlation and Dependency statistics types on 2 columns", err.Error()) - - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) - tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "2 [2,3] 0", - )) - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) - - tk.MustExec("update mysql.stats_extended set status = 1 where name = 's1'") - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 1) - - tk.MustExec("alter table t drop stats_extended s1") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "2 [2,3] 2", - )) - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) -} - -func TestAdminReloadStatistics1(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int primary key, b int, c int, d int)") - tk.MustExec("insert into t values(1,1,5,1),(2,2,4,2),(3,3,3,3),(4,4,2,4),(5,5,1,5)") - tk.MustExec("analyze table t") - tk.MustExec("alter table t add stats_extended s1 correlation(b,c)") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "2 [2,3] 0", - )) - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) - - tk.MustExec("update mysql.stats_extended set status = 1 where name = 's1'") - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 1) - - tk.MustExec("delete from mysql.stats_extended where name = 's1'") - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 1) - - tk.MustExec("admin reload stats_extended") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) -} - -func TestAdminReloadStatistics2(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "1.000000 1", - )) - rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - - tk.MustExec("delete from mysql.stats_extended where name = 's1'") - is := dom.InfoSchema() - dom.StatsHandle().Update(is) - tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - - tk.MustExec("admin reload stats_extended") - tk.MustQuery("select stats, status from mysql.stats_extended where name = 's1'").Check(testkit.Rows()) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 0) -} - -func TestCorrelationStatsCompute(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("insert into t values(1,1,5),(2,2,4),(3,3,3),(4,4,2),(5,5,1)") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Check(testkit.Rows()) - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("alter table t add stats_extended s2 correlation(a,c)") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 0", - "2 [1,3] 0", - )) - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) - - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 1.000000 1", - "2 [1,3] -1.000000 1", - )) - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 1.000000 1", - "2 [1,3] -1.000000 1", - )) - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 2) - foundS1, foundS2 := false, false - for name, item := range statsTbl.ExtendedStats.Stats { - switch name { - case "s1": - foundS1 = true - require.Equal(t, float64(1), item.ScalarVals) - case "s2": - foundS2 = true - require.Equal(t, float64(-1), item.ScalarVals) - default: - require.FailNow(t, "Unexpected extended stats in cache") - } - } - require.True(t, foundS1 && foundS2) - - // Check that table with NULLs won't cause panic - tk.MustExec("delete from t") - tk.MustExec("insert into t values(1,null,2), (2,null,null)") - tk.MustExec("set @@session.tidb_analyze_version=1") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 0.000000 1", - "2 [1,3] 1.000000 1", - )) - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 0.000000 1", - "2 [1,3] 1.000000 1", - )) - tk.MustExec("insert into t values(3,3,3)") - tk.MustExec("set @@session.tidb_analyze_version=1") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 1.000000 1", - "2 [1,3] 1.000000 1", - )) - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("analyze table t") - tk.MustQuery("select type, column_ids, stats, status from mysql.stats_extended").Sort().Check(testkit.Rows( - "2 [1,2] 1.000000 1", - "2 [1,3] 1.000000 1", - )) -} - -func TestSyncStatsExtendedRemoval(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 1) - item := statsTbl.ExtendedStats.Stats["s1"] - require.NotNil(t, item) - result := tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 1) - - tk.MustExec("alter table t drop stats_extended s1") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.NotNil(t, statsTbl) - require.NotNil(t, statsTbl.ExtendedStats) - require.Len(t, statsTbl.ExtendedStats.Stats, 0) - result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 0) -} - -func TestStaticPartitionPruneMode(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") - tk.MustExec("use test") - tk.MustExec(`create table t (a int, key(a)) partition by range(a) - (partition p0 values less than (10), - partition p1 values less than (22))`) - tk.MustExec(`insert into t values (1), (2), (3), (10), (11)`) - tk.MustExec(`analyze table t`) - require.True(t, tk.MustNoGlobalStats("t")) - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") - require.True(t, tk.MustNoGlobalStats("t")) - - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") - tk.MustExec(`insert into t values (4), (5), (6)`) - tk.MustExec(`analyze table t partition p0`) - require.True(t, tk.MustNoGlobalStats("t")) - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") - require.True(t, tk.MustNoGlobalStats("t")) - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") -} - -func TestMergeIdxHist(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Dynamic) + "'") - defer tk.MustExec("set @@tidb_partition_prune_mode='" + string(variable.Static) + "'") - tk.MustExec("use test") - tk.MustExec(` - create table t (a int, key(a)) - partition by range (a) ( - partition p0 values less than (10), - partition p1 values less than (20))`) - tk.MustExec("set @@tidb_analyze_version=2") - defer tk.MustExec("set @@tidb_analyze_version=1") - tk.MustExec("insert into t values (1), (2), (3), (4), (5), (6), (6), (null), (11), (12), (13), (14), (15), (16), (17), (18), (19), (19)") - - tk.MustExec("analyze table t with 2 topn, 2 buckets") - rows := tk.MustQuery("show stats_buckets where partition_name like 'global'") - require.Len(t, rows.Rows(), 4) -} - -func TestPartitionPruneModeSessionVariable(t *testing.T) { - failpoint.Enable("github.com/pingcap/tidb/planner/core/forceDynamicPrune", `return(true)`) - defer failpoint.Disable("github.com/pingcap/tidb/planner/core/forceDynamicPrune") - - store := testkit.CreateMockStore(t) - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk1.MustExec("set tidb_cost_model_version=1") - tk1.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") - tk1.MustExec(`set @@tidb_analyze_version=2`) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - tk2.MustExec("set tidb_cost_model_version=1") - tk2.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Static) + "'") - tk2.MustExec(`set @@tidb_analyze_version=2`) - - tk1.MustExec(`create table t (a int, key(a)) partition by range(a) - (partition p0 values less than (10), - partition p1 values less than (22))`) - - tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "TableReader 10000.00 root partition:all data:TableFullScan", - "└─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo", - )) - tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "PartitionUnion 20000.00 root ", - "├─TableReader 10000.00 root data:TableFullScan", - "│ └─TableFullScan 10000.00 cop[tikv] table:t, partition:p0 keep order:false, stats:pseudo", - "└─TableReader 10000.00 root data:TableFullScan", - " └─TableFullScan 10000.00 cop[tikv] table:t, partition:p1 keep order:false, stats:pseudo", - )) - - tk1.MustExec(`insert into t values (1), (2), (3), (10), (11)`) - tk1.MustExec(`analyze table t with 1 topn, 2 buckets`) - tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "TableReader 5.00 root partition:all data:TableFullScan", - "└─TableFullScan 5.00 cop[tikv] table:t keep order:false", - )) - tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "PartitionUnion 5.00 root ", - "├─TableReader 3.00 root data:TableFullScan", - "│ └─TableFullScan 3.00 cop[tikv] table:t, partition:p0 keep order:false", - "└─TableReader 2.00 root data:TableFullScan", - " └─TableFullScan 2.00 cop[tikv] table:t, partition:p1 keep order:false", - )) - - tk1.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Static) + "'") - tk1.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "PartitionUnion 5.00 root ", - "├─TableReader 3.00 root data:TableFullScan", - "│ └─TableFullScan 3.00 cop[tikv] table:t, partition:p0 keep order:false", - "└─TableReader 2.00 root data:TableFullScan", - " └─TableFullScan 2.00 cop[tikv] table:t, partition:p1 keep order:false", - )) - tk2.MustExec("set @@tidb_partition_prune_mode = '" + string(variable.Dynamic) + "'") - tk2.MustQuery("explain format = 'brief' select * from t").Check(testkit.Rows( - "TableReader 5.00 root partition:all data:TableFullScan", - "└─TableFullScan 5.00 cop[tikv] table:t keep order:false", - )) -} - -func TestIndexUsageInformation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - session.SetIndexUsageSyncLease(1) - defer session.SetIndexUsageSyncLease(0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t_idx(a int, b int)") - tk.MustExec("create unique index idx_a on t_idx(a)") - tk.MustExec("create unique index idx_b on t_idx(b)") - tk.MustQuery("select a from t_idx where a=1") - querySQL := `select idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected - from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables - where tables.table_schema = idx.table_schema - AND tables.table_name = idx.table_name - AND tables.tidb_table_id = stats.table_id - AND idx.index_id = stats.index_id - AND idx.table_name = "t_idx"` - do := dom - err := do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 1 0", - )) - tk.MustExec("insert into t_idx values(1, 0)") - tk.MustQuery("select a from t_idx where a=1") - tk.MustQuery("select a from t_idx where a=1") - err = do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 3 2", - )) - tk.MustQuery("select b from t_idx where b=0") - tk.MustQuery("select b from t_idx where b=0") - err = do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Sort().Check(testkit.Rows( - "test t_idx idx_a 3 2", - "test t_idx idx_b 2 2", - )) -} - -// Functional Test:test batch insert -func TestIndexUsageInformationMultiIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - session.SetIndexUsageSyncLease(1) - defer session.SetIndexUsageSyncLease(0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - //len(column) = 11.len(index) = 11 - tk.MustExec("create table t_idx(a int, b int, c int, d int, e int, f int, g int, h int, i int, j int, k int)") - tk.MustExec("create unique index idx_a on t_idx(a)") - tk.MustExec("create unique index idx_b on t_idx(b)") - tk.MustExec("create unique index idx_c on t_idx(c)") - tk.MustExec("create unique index idx_d on t_idx(d)") - tk.MustExec("create unique index idx_e on t_idx(e)") - tk.MustExec("create unique index idx_f on t_idx(f)") - tk.MustExec("create unique index idx_g on t_idx(g)") - tk.MustExec("create unique index idx_h on t_idx(h)") - tk.MustExec("create unique index idx_i on t_idx(i)") - tk.MustExec("create unique index idx_j on t_idx(j)") - tk.MustExec("create unique index idx_k on t_idx(k)") - - tk.MustQuery("select a from t_idx where a=1") - querySQL := `select idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected - from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables - where tables.table_schema = idx.table_schema - AND tables.table_name = idx.table_name - AND tables.tidb_table_id = stats.table_id - AND idx.index_id = stats.index_id - AND idx.table_name = "t_idx" ORDER BY idx.key_name` - do := dom - err := do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 1 0", - )) - tk.MustExec("insert into t_idx values(1,1,1,1,1,1,1,1,1,1,1)") - tk.MustQuery("select a from t_idx where a=1") - tk.MustQuery("select a from t_idx where a=1") - err = do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 3 2", - )) - - tk.MustQuery("select a from t_idx where a=1") - tk.MustQuery("select b from t_idx where b=1") - tk.MustQuery("select c from t_idx where c=1") - tk.MustQuery("select d from t_idx where d=1") - tk.MustQuery("select e from t_idx where e=1") - tk.MustQuery("select f from t_idx where f=1") - tk.MustQuery("select g from t_idx where g=1") - tk.MustQuery("select h from t_idx where h=1") - tk.MustQuery("select i from t_idx where i=1") - tk.MustQuery("select j from t_idx where j=1") - tk.MustQuery("select k from t_idx where k=1") - - err = do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 4 3", - "test t_idx idx_b 1 1", - "test t_idx idx_c 1 1", - "test t_idx idx_d 1 1", - "test t_idx idx_e 1 1", - "test t_idx idx_f 1 1", - "test t_idx idx_g 1 1", - "test t_idx idx_h 1 1", - "test t_idx idx_i 1 1", - "test t_idx idx_j 1 1", - "test t_idx idx_k 1 1", - )) - - tk.MustQuery("select a from t_idx where a=1") - tk.MustQuery("select b from t_idx where b=1") - tk.MustQuery("select c from t_idx where c=1") - tk.MustQuery("select d from t_idx where d=1") - tk.MustQuery("select e from t_idx where e=1") - tk.MustQuery("select f from t_idx where f=1") - tk.MustQuery("select g from t_idx where g=1") - tk.MustQuery("select h from t_idx where h=1") - tk.MustQuery("select i from t_idx where i=1") - tk.MustQuery("select j from t_idx where j=1") - tk.MustQuery("select k from t_idx where k=1") - - err = do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows( - "test t_idx idx_a 5 4", - "test t_idx idx_b 2 2", - "test t_idx idx_c 2 2", - "test t_idx idx_d 2 2", - "test t_idx idx_e 2 2", - "test t_idx idx_f 2 2", - "test t_idx idx_g 2 2", - "test t_idx idx_h 2 2", - "test t_idx idx_i 2 2", - "test t_idx idx_j 2 2", - "test t_idx idx_k 2 2", - )) -} - -//cd statistics/handle -//go test -run BenchmarkIndexUsageInformationInsert -bench BenchmarkIndexUsageInformationInsert -benchmem -benchtime=20s -//old 6998 3379135 ns/op 994594 B/op 12659 allocs/op -//new 18472 1299401 ns/op 473919 B/op 5628 allocs/op - -func BenchmarkIndexUsageInformationInsert(b *testing.B) { - //init - b.StopTimer() - store, dom := testkit.CreateMockStoreAndDomain(b) - session.SetIndexUsageSyncLease(1) - defer session.SetIndexUsageSyncLease(0) - tk := testkit.NewTestKit(b, store) - tk.MustExec("use test") - //len(column) = 11.len(index) = 11 - tk.MustExec("create table t_idx(a int, b int, c int, d int, e int, f int, g int, h int, i int, j int, k int)") - tk.MustExec("create unique index idx_a on t_idx(a)") - tk.MustExec("create unique index idx_b on t_idx(b)") - tk.MustExec("create unique index idx_c on t_idx(c)") - tk.MustExec("create unique index idx_d on t_idx(d)") - tk.MustExec("create unique index idx_e on t_idx(e)") - tk.MustExec("create unique index idx_f on t_idx(f)") - tk.MustExec("create unique index idx_g on t_idx(g)") - tk.MustExec("create unique index idx_h on t_idx(h)") - tk.MustExec("create unique index idx_i on t_idx(i)") - tk.MustExec("create unique index idx_j on t_idx(j)") - tk.MustExec("create unique index idx_k on t_idx(k)") - b.StartTimer() - - for i := 0; i < b.N; i++ { - tk.MustQuery("select a from t_idx where a=1") - tk.MustQuery("select b from t_idx where b=1") - tk.MustQuery("select c from t_idx where c=1") - tk.MustQuery("select d from t_idx where d=1") - tk.MustQuery("select e from t_idx where e=1") - tk.MustQuery("select f from t_idx where f=1") - tk.MustQuery("select g from t_idx where g=1") - tk.MustQuery("select h from t_idx where h=1") - tk.MustQuery("select i from t_idx where i=1") - tk.MustQuery("select j from t_idx where j=1") - tk.MustQuery("select k from t_idx where k=1") - do := dom - err := do.StatsHandle().DumpIndexUsageToKV() - require.NoError(b, err) - } -} - -func TestGCIndexUsageInformation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - session.SetIndexUsageSyncLease(1) - defer session.SetIndexUsageSyncLease(0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t_idx(a int, b int)") - tk.MustExec("create unique index idx_a on t_idx(a)") - tk.MustQuery("select a from t_idx where a=1") - do := dom - err := do.StatsHandle().DumpIndexUsageToKV() - require.NoError(t, err) - querySQL := `select count(distinct idx.table_schema, idx.table_name, idx.key_name, stats.query_count, stats.rows_selected) - from mysql.schema_index_usage as stats, information_schema.tidb_indexes as idx, information_schema.tables as tables - where tables.table_schema = idx.table_schema - AND tables.table_name = idx.table_name - AND tables.tidb_table_id = stats.table_id - AND idx.index_id = stats.index_id - AND idx.table_name = "t_idx"` - tk.MustQuery(querySQL).Check(testkit.Rows("1")) - tk.MustExec("drop index `idx_a` on t_idx") - err = do.StatsHandle().GCIndexUsage() - require.NoError(t, err) - tk.MustQuery(querySQL).Check(testkit.Rows("0")) -} - -func TestRepetitiveAddDropExtendedStats(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( - "s1 0", - )) - result := tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 0) - tk.MustExec("analyze table t") - tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( - "s1 1", - )) - result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 1) - tk.MustExec("alter table t drop stats_extended s1") - tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( - "s1 2", - )) - result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 0) - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( - "s1 0", - )) - result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 0) - tk.MustExec("analyze table t") - tk.MustQuery("select name, status from mysql.stats_extended where name = 's1'").Sort().Check(testkit.Rows( - "s1 1", - )) - result = tk.MustQuery("show stats_extended where db_name = 'test' and table_name = 't'") - require.Len(t, result.Rows(), 1) -} - -func TestDuplicateFMSketch(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - defer tk.MustExec("set @@tidb_partition_prune_mode='static'") - tk.MustExec("create table t(a int, b int, c int) partition by hash(a) partitions 3") - tk.MustExec("insert into t values (1, 1, 1)") - tk.MustExec("analyze table t") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("9")) - tk.MustExec("analyze table t") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("9")) - - tk.MustExec("alter table t drop column b") - require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), time.Duration(0))) - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) -} - -func TestIndexFMSketch(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, index ia(a), index ibc(b, c)) partition by hash(a) partitions 3") - tk.MustExec("insert into t values (1, 1, 1)") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - defer tk.MustExec("set @@tidb_partition_prune_mode='static'") - tk.MustExec("analyze table t index ia") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("3")) - tk.MustExec("analyze table t index ibc") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) - tk.MustExec("analyze table t") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("15")) - tk.MustExec("drop table if exists t") - require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) - - // clustered index - tk.MustExec("drop table if exists t") - tk.MustExec("set @@tidb_enable_clustered_index=ON") - tk.MustExec("create table t (a datetime, b datetime, primary key (a)) partition by hash(year(a)) partitions 3") - tk.MustExec("insert into t values ('2000-01-01', '2000-01-01')") - tk.MustExec("analyze table t") - tk.MustQuery("select count(*) from mysql.stats_fm_sketch").Check(testkit.Rows("6")) - tk.MustExec("drop table if exists t") - require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) - - // test NDV - checkNDV := func(rows, ndv int) { - tk.MustExec("analyze table t") - rs := tk.MustQuery("select value from mysql.stats_fm_sketch").Rows() - require.Len(t, rs, rows) - for i := range rs { - fm, err := statistics.DecodeFMSketch([]byte(rs[i][0].(string))) - require.NoError(t, err) - require.Equal(t, int64(ndv), fm.NDV()) - } - } - - tk.MustExec("set @@tidb_enable_clustered_index=OFF") - tk.MustExec("create table t(a int, key(a)) partition by hash(a) partitions 3") - tk.MustExec("insert into t values (1), (2), (2), (3)") - checkNDV(6, 1) - tk.MustExec("insert into t values (4), (5), (6)") - checkNDV(6, 2) - tk.MustExec("insert into t values (2), (5)") - checkNDV(6, 2) - tk.MustExec("drop table if exists t") - require.NoError(t, dom.StatsHandle().GCStats(dom.InfoSchema(), 0)) - - // clustered index - tk.MustExec("set @@tidb_enable_clustered_index=ON") - tk.MustExec("create table t (a datetime, b datetime, primary key (a)) partition by hash(year(a)) partitions 3") - tk.MustExec("insert into t values ('2000-01-01', '2001-01-01'), ('2001-01-01', '2001-01-01'), ('2002-01-01', '2001-01-01')") - checkNDV(6, 1) - tk.MustExec("insert into t values ('1999-01-01', '1998-01-01'), ('1997-01-02', '1999-01-02'), ('1998-01-03', '1999-01-03')") - checkNDV(6, 2) -} - -func TestShowExtendedStats4DropColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int)") - tk.MustExec("insert into t values(1,1,1),(2,2,2),(3,3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("alter table t add stats_extended s2 correlation(b,c)") - tk.MustExec("analyze table t") - rows := tk.MustQuery("show stats_extended").Sort().Rows() - require.Len(t, rows, 2) - require.Equal(t, "s1", rows[0][2]) - require.Equal(t, "[a,b]", rows[0][3]) - require.Equal(t, "s2", rows[1][2]) - require.Equal(t, "[b,c]", rows[1][3]) - - tk.MustExec("alter table t drop column b") - rows = tk.MustQuery("show stats_extended").Rows() - require.Len(t, rows, 0) - - // Previously registered extended stats should be invalid for re-created columns. - tk.MustExec("alter table t add column b int") - rows = tk.MustQuery("show stats_extended").Rows() - require.Len(t, rows, 0) -} - -func TestExtStatsOnReCreatedTable(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - rows := tk.MustQuery("select table_id, stats from mysql.stats_extended where name = 's1'").Rows() - require.Len(t, rows, 1) - tableID1 := rows[0][0] - require.Equal(t, "1.000000", rows[0][1]) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "1.000000", rows[0][5]) - - tk.MustExec("drop table t") - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 0) - - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,3),(2,2),(3,1)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - rows = tk.MustQuery("select table_id, stats from mysql.stats_extended where name = 's1' order by stats").Rows() - require.Len(t, rows, 2) - tableID2 := rows[0][0] - require.NotEqual(t, tableID1, tableID2) - require.Equal(t, tableID1, rows[1][0]) - require.Equal(t, "-1.000000", rows[0][1]) - require.Equal(t, "1.000000", rows[1][1]) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "-1.000000", rows[0][5]) -} - -func TestExtStatsOnReCreatedColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] 1.000000", - )) - rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "[a,b]", rows[0][3]) - require.Equal(t, "1.000000", rows[0][5]) - - tk.MustExec("alter table t drop column b") - tk.MustExec("alter table t add column b int") - tk.MustQuery("select * from t").Sort().Check(testkit.Rows( - "1 ", - "2 ", - "3 ", - )) - tk.MustExec("update t set b = 3 where a = 1") - tk.MustExec("update t set b = 2 where a = 2") - tk.MustExec("update t set b = 1 where a = 3") - tk.MustQuery("select * from t").Sort().Check(testkit.Rows( - "1 3", - "2 2", - "3 1", - )) - tk.MustExec("analyze table t") - // Previous extended stats would not be collected and would not take effect anymore, it will be removed by stats GC. - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] 1.000000", - )) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 0) -} - -func TestExtStatsOnRenamedColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] 1.000000", - )) - rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "[a,b]", rows[0][3]) - require.Equal(t, "1.000000", rows[0][5]) - - tk.MustExec("alter table t rename column b to c") - tk.MustExec("update t set c = 3 where a = 1") - tk.MustExec("update t set c = 2 where a = 2") - tk.MustExec("update t set c = 1 where a = 3") - tk.MustQuery("select * from t").Sort().Check(testkit.Rows( - "1 3", - "2 2", - "3 1", - )) - tk.MustExec("analyze table t") - // Previous extended stats would still be collected and take effect. - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] -1.000000", - )) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "[a,c]", rows[0][3]) - require.Equal(t, "-1.000000", rows[0][5]) -} - -func TestExtStatsOnModifiedColumn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set session tidb_enable_extended_stats = on") - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values(1,1),(2,2),(3,3)") - tk.MustExec("alter table t add stats_extended s1 correlation(a,b)") - tk.MustExec("analyze table t") - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] 1.000000", - )) - rows := tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "[a,b]", rows[0][3]) - require.Equal(t, "1.000000", rows[0][5]) - - tk.MustExec("alter table t modify column b bigint") - tk.MustExec("update t set b = 3 where a = 1") - tk.MustExec("update t set b = 2 where a = 2") - tk.MustExec("update t set b = 1 where a = 3") - tk.MustQuery("select * from t").Sort().Check(testkit.Rows( - "1 3", - "2 2", - "3 1", - )) - tk.MustExec("analyze table t") - // Previous extended stats would still be collected and take effect. - tk.MustQuery("select column_ids, stats from mysql.stats_extended where name = 's1'").Check(testkit.Rows( - "[1,2] -1.000000", - )) - rows = tk.MustQuery("show stats_extended where stats_name = 's1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "[a,b]", rows[0][3]) - require.Equal(t, "-1.000000", rows[0][5]) -} - -func TestCorrelationWithDefinedCollate(t *testing.T) { - store := testkit.CreateMockStore(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t(a int primary key, b varchar(8) character set utf8mb4 collate utf8mb4_general_ci, c varchar(8) character set utf8mb4 collate utf8mb4_bin)") - testKit.MustExec("insert into t values(1,'aa','aa'),(2,'Cb','Cb'),(3,'CC','CC')") - testKit.MustExec("analyze table t") - testKit.MustQuery("select a from t order by b").Check(testkit.Rows( - "1", - "2", - "3", - )) - testKit.MustQuery("select a from t order by c").Check(testkit.Rows( - "3", - "2", - "1", - )) - rows := testKit.MustQuery("show stats_histograms where table_name = 't'").Sort().Rows() - require.Len(t, rows, 3) - require.Equal(t, "1", rows[1][9]) - require.Equal(t, "-1", rows[2][9]) - testKit.MustExec("set session tidb_enable_extended_stats = on") - testKit.MustExec("alter table t add stats_extended s1 correlation(b,c)") - testKit.MustExec("analyze table t") - rows = testKit.MustQuery("show stats_extended where stats_name = 's1'").Sort().Rows() - require.Len(t, rows, 1) - require.Equal(t, "[b,c]", rows[0][3]) - require.Equal(t, "-1.000000", rows[0][5]) -} - -func TestLoadHistogramWithCollate(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t(a varchar(10) collate utf8mb4_unicode_ci);") - testKit.MustExec("insert into t values('abcdefghij');") - testKit.MustExec("insert into t values('abcdufghij');") - testKit.MustExec("analyze table t with 0 topn;") - do := dom - h := do.StatsHandle() - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - _, err = h.TableStatsFromStorage(tblInfo, tblInfo.ID, true, 0) - require.NoError(t, err) -} - -func TestStatsCacheUpdateSkip(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - do := dom - h := do.StatsHandle() - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - testKit.MustExec("insert into t values(1, 2)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec("analyze table t") - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl1 := h.GetTableStats(tableInfo) - require.False(t, statsTbl1.Pseudo) - h.Update(is) - statsTbl2 := h.GetTableStats(tableInfo) - require.Equal(t, statsTbl2, statsTbl1) -} - -func TestIssues24349(t *testing.T) { - store := testkit.CreateMockStore(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set @@tidb_partition_prune_mode='dynamic'") - testKit.MustExec("set @@tidb_analyze_version=2") - defer testKit.MustExec("set @@tidb_analyze_version=1") - defer testKit.MustExec("set @@tidb_partition_prune_mode='static'") - testKit.MustExec("create table t (a int, b int) partition by hash(a) partitions 3") - testKit.MustExec("insert into t values (0, 3), (0, 3), (0, 3), (0, 2), (1, 1), (1, 2), (1, 2), (1, 2), (1, 3), (1, 4), (2, 1), (2, 1)") - testKit.MustExec("analyze table t with 1 topn, 3 buckets") - testKit.MustQuery("show stats_buckets where partition_name='global'").Check(testkit.Rows( - "test t global a 0 0 2 2 0 2 0", - "test t global b 0 0 3 1 1 2 0", - "test t global b 0 1 10 1 4 4 0", - )) -} - -func testIncrementalModifyCountUpdateHelper(analyzeSnapshot bool) func(*testing.T) { - return func(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - if analyzeSnapshot { - tk.MustExec("set @@session.tidb_enable_analyze_snapshot = on") - } else { - tk.MustExec("set @@session.tidb_enable_analyze_snapshot = off") - } - tk.MustExec("create table t(a int)") - tk.MustExec("set @@session.tidb_analyze_version = 2") - h := dom.StatsHandle() - err := h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - tid := tblInfo.ID - - tk.MustExec("insert into t values(1),(2),(3)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - err = h.Update(dom.InfoSchema()) - require.NoError(t, err) - tk.MustExec("analyze table t") - tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( - "3 0", - )) - - tk.MustExec("begin") - txn, err := tk.Session().Txn(false) - require.NoError(t, err) - startTS := txn.StartTS() - tk.MustExec("commit") - - tk.MustExec("insert into t values(4),(5),(6)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - err = h.Update(dom.InfoSchema()) - require.NoError(t, err) - - // Simulate that the analyze would start before and finish after the second insert. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot", fmt.Sprintf("return(%d)", startTS))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectBaseCount", "return(3)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/injectBaseModifyCount", "return(0)")) - tk.MustExec("analyze table t") - if analyzeSnapshot { - // Check the count / modify_count changes during the analyze are not lost. - tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( - "6 3", - )) - // Check the histogram is correct for the snapshot analyze. - tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( - "3", - )) - } else { - // Since analyze use max ts to read data, it finds the row count is 6 and directly set count to 6 rather than incrementally update it. - // But it still incrementally updates modify_count. - tk.MustQuery(fmt.Sprintf("select count, modify_count from mysql.stats_meta where table_id = %d", tid)).Check(testkit.Rows( - "6 3", - )) - // Check the histogram is collected from the latest data rather than the snapshot at startTS. - tk.MustQuery(fmt.Sprintf("select distinct_count from mysql.stats_histograms where table_id = %d", tid)).Check(testkit.Rows( - "6", - )) - } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectAnalyzeSnapshot")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectBaseCount")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/injectBaseModifyCount")) - } -} - -func TestIncrementalModifyCountUpdate(t *testing.T) { - for _, analyzeSnapshot := range []bool{true, false} { - t.Run(fmt.Sprintf("%s-%t", t.Name(), analyzeSnapshot), testIncrementalModifyCountUpdateHelper(analyzeSnapshot)) - } -} - -func TestRecordHistoricalStatsToStorage(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 2") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10))") - tk.MustExec("insert into t value(1, 'aaa'), (3, 'aab'), (5, 'bba'), (2, 'bbb'), (4, 'cca'), (6, 'ccc')") - // mark column stats as needed - tk.MustExec("select * from t where a = 3") - tk.MustExec("select * from t where b = 'bbb'") - tk.MustExec("alter table t add index single(a)") - tk.MustExec("alter table t add index multi(a, b)") - tk.MustExec("analyze table t with 2 topn") - - tableInfo, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - version, err := dom.StatsHandle().RecordHistoricalStatsToStorage("t", tableInfo.Meta(), tableInfo.Meta().ID, false) - require.NoError(t, err) - - rows := tk.MustQuery(fmt.Sprintf("select count(*) from mysql.stats_history where version = '%d'", version)).Rows() - num, _ := strconv.Atoi(rows[0][0].(string)) - require.GreaterOrEqual(t, num, 1) -} - -func TestEvictedColumnLoadedStatus(t *testing.T) { - t.Skip("skip this test because it is useless") - restore := config.RestoreFunc() - defer restore() - config.UpdateGlobal(func(conf *config.Config) { - conf.Performance.EnableStatsCacheMemQuota = true - }) - store, dom := testkit.CreateMockStoreAndDomain(t) - dom.StatsHandle().SetLease(0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_analyze_version = 1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("analyze table test.t") - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.Nil(t, err) - tblStats := domain.GetDomain(tk.Session()).StatsHandle().GetTableStats(tbl.Meta()) - for _, col := range tblStats.Columns { - require.True(t, col.IsStatsInitialized()) - } - - domain.GetDomain(tk.Session()).StatsHandle().SetStatsCacheCapacity(1) - tblStats = domain.GetDomain(tk.Session()).StatsHandle().GetTableStats(tbl.Meta()) - for _, col := range tblStats.Columns { - require.True(t, col.IsStatsInitialized()) - } -} - -func TestUninitializedStatsStatus(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - dom.StatsHandle().SetLease(0) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b int, c int, index idx_a(a))") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1,2,2), (3,4,4), (5,6,6), (7,8,8), (9,10,10)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - is := dom.InfoSchema() - require.NoError(t, h.Update(is)) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - tblStats := h.GetTableStats(tblInfo) - for _, col := range tblStats.Columns { - require.False(t, col.IsStatsInitialized()) - } - for _, idx := range tblStats.Indices { - require.False(t, idx.IsStatsInitialized()) - } - tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Check(testkit.Rows()) - checkStatsPseudo := func() { - rows := tk.MustQuery("explain select * from t").Rows() - operatorInfo := rows[len(rows)-1][4].(string) - require.True(t, strings.Contains(operatorInfo, "stats:pseudo")) - } - tk.MustExec("set @@tidb_enable_pseudo_for_outdated_stats = true") - checkStatsPseudo() - tk.MustExec("set @@tidb_enable_pseudo_for_outdated_stats = false") - checkStatsPseudo() -} - -func TestIssue39336(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(` -create table t1 ( - a datetime(3) default null, - b int -) partition by range (b) ( - partition p0 values less than (1000), - partition p1 values less than (maxvalue) -)`) - tk.MustExec("set @@sql_mode=''") - tk.MustExec("set @@tidb_analyze_version=2") - tk.MustExec("set @@tidb_partition_prune_mode='dynamic'") - tk.MustExec(` -insert into t1 values -('1000-00-09 00:00:00.000', 1), -('1000-00-06 00:00:00.000', 1), -('1000-00-06 00:00:00.000', 1), -('2022-11-23 14:24:30.000', 1), -('2022-11-23 14:24:32.000', 1), -('2022-11-23 14:24:33.000', 1), -('2022-11-23 14:24:35.000', 1), -('2022-11-23 14:25:08.000', 1001), -('2022-11-23 14:25:09.000', 1001)`) - tk.MustExec("analyze table t1 with 0 topn") - rows := tk.MustQuery("show analyze status where job_info like 'merge global stats%'").Rows() - require.Len(t, rows, 1) - require.Equal(t, "finished", rows[0][7]) -} - -func checkAllEvicted(t *testing.T, statsTbl *statistics.Table) { - for _, col := range statsTbl.Columns { - require.True(t, col.IsAllEvicted()) - } - for _, idx := range statsTbl.Indices { - require.True(t, idx.IsAllEvicted()) - } -} - -func TestInitStatsLite(t *testing.T) { - oriVal := config.GetGlobalConfig().Performance.LiteInitStats - config.GetGlobalConfig().Performance.LiteInitStats = true - defer func() { - config.GetGlobalConfig().Performance.LiteInitStats = oriVal - }() - - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c int, primary key(a), key idxb(b), key idxc(c))") - tk.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,6,6),(7,7,7),(8,8,8),(9,9,9)") - - h := dom.StatsHandle() - // set lease > 0 to trigger on-demand stats load. - h.SetLease(time.Millisecond) - defer func() { - h.SetLease(0) - }() - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - colBID := tblInfo.Columns[1].ID - colCID := tblInfo.Columns[2].ID - idxBID := tblInfo.Indices[0].ID - idxCID := tblInfo.Indices[1].ID - - tk.MustExec("analyze table t with 2 topn, 2 buckets") - statsTbl0 := h.GetTableStats(tblInfo) - checkAllEvicted(t, statsTbl0) - - h.Clear() - require.NoError(t, h.InitStatsLite(is)) - statsTbl1 := h.GetTableStats(tblInfo) - checkAllEvicted(t, statsTbl1) - internal.AssertTableEqual(t, statsTbl0, statsTbl1) - - // async stats load - tk.MustExec("set @@tidb_stats_load_sync_wait = 0") - tk.MustExec("explain select * from t where b > 1") - require.NoError(t, h.LoadNeededHistograms()) - statsTbl2 := h.GetTableStats(tblInfo) - colBStats1 := statsTbl2.Columns[colBID] - require.True(t, colBStats1.IsFullLoad()) - idxBStats1 := statsTbl2.Indices[idxBID] - require.True(t, idxBStats1.IsFullLoad()) - - // sync stats load - tk.MustExec("set @@tidb_stats_load_sync_wait = 60000") - tk.MustExec("explain select * from t where c > 1") - statsTbl3 := h.GetTableStats(tblInfo) - colCStats1 := statsTbl3.Columns[colCID] - require.True(t, colCStats1.IsFullLoad()) - idxCStats1 := statsTbl3.Indices[idxCID] - require.True(t, idxCStats1.IsFullLoad()) - - // update stats - tk.MustExec("analyze table t with 1 topn, 3 buckets") - statsTbl4 := h.GetTableStats(tblInfo) - colBStats2 := statsTbl4.Columns[colBID] - require.True(t, colBStats2.IsFullLoad()) - require.Greater(t, colBStats2.LastUpdateVersion, colBStats1.LastUpdateVersion) - idxBStats2 := statsTbl4.Indices[idxBID] - require.True(t, idxBStats2.IsFullLoad()) - require.Greater(t, idxBStats2.LastUpdateVersion, idxBStats1.LastUpdateVersion) - colCStats2 := statsTbl4.Columns[colCID] - require.True(t, colCStats2.IsFullLoad()) - require.Greater(t, colCStats2.LastUpdateVersion, colCStats1.LastUpdateVersion) - idxCStats2 := statsTbl4.Indices[idxCID] - require.True(t, idxCStats2.IsFullLoad()) - require.Greater(t, idxCStats2.LastUpdateVersion, idxCStats1.LastUpdateVersion) -} - -func TestSkipMissingPartitionStats(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("set @@tidb_skip_missing_partition_stats = 1") - tk.MustExec("create table t (a int, b int, c int, index idx_b(b)) partition by range (a) (partition p0 values less than (100), partition p1 values less than (200), partition p2 values less than (300))") - tk.MustExec("insert into t values (1,1,1), (2,2,2), (101,101,101), (102,102,102), (201,201,201), (202,202,202)") - h := dom.StatsHandle() - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t partition p0, p1") - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - globalStats := h.GetTableStats(tblInfo) - require.Equal(t, 6, int(globalStats.RealtimeCount)) - require.Equal(t, 2, int(globalStats.ModifyCount)) - for _, col := range globalStats.Columns { - require.True(t, col.IsStatsInitialized()) - } - for _, idx := range globalStats.Indices { - require.True(t, idx.IsStatsInitialized()) - } -} diff --git a/statistics/handle/handletest/lockstats/BUILD.bazel b/statistics/handle/handletest/lockstats/BUILD.bazel deleted file mode 100644 index 876a35b5c282d..0000000000000 --- a/statistics/handle/handletest/lockstats/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "lockstats_test", - timeout = "short", - srcs = [ - "lock_partition_stats_test.go", - "lock_table_stats_test.go", - "main_test.go", - ], - flaky = True, - shard_count = 21, - deps = [ - "//config", - "//domain", - "//kv", - "//parser/model", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/handletest/lockstats/main_test.go b/statistics/handle/handletest/lockstats/main_test.go deleted file mode 100644 index 80ab994fc847a..0000000000000 --- a/statistics/handle/handletest/lockstats/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package lockstats - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/handletest/main_test.go b/statistics/handle/handletest/main_test.go deleted file mode 100644 index 6b156edc1f17e..0000000000000 --- a/statistics/handle/handletest/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package handletest - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/handletest/statstest/BUILD.bazel b/statistics/handle/handletest/statstest/BUILD.bazel deleted file mode 100644 index 1ff5f22493f58..0000000000000 --- a/statistics/handle/handletest/statstest/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "statstest_test", - timeout = "short", - srcs = [ - "main_test.go", - "stats_test.go", - ], - flaky = True, - race = "on", - shard_count = 8, - deps = [ - "//config", - "//parser/model", - "//statistics/handle/internal", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/handletest/statstest/main_test.go b/statistics/handle/handletest/statstest/main_test.go deleted file mode 100644 index e26425da2be80..0000000000000 --- a/statistics/handle/handletest/statstest/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package statstest - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/handletest/statstest/stats_test.go b/statistics/handle/handletest/statstest/stats_test.go deleted file mode 100644 index 25d9c73fae09d..0000000000000 --- a/statistics/handle/handletest/statstest/stats_test.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package statstest - -import ( - "fmt" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics/handle/internal" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestStatsCache(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - testKit.MustExec("insert into t values(1, 2)") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.True(t, statsTbl.Pseudo) - testKit.MustExec("analyze table t") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - testKit.MustExec("create index idx_t on t(c1)") - do.InfoSchema() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - // If index is build, but stats is not updated. statsTbl can also work. - require.False(t, statsTbl.Pseudo) - // But the added index will not work. - require.Nil(t, statsTbl.Indices[int64(1)]) - - testKit.MustExec("analyze table t") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - // If the new schema drop a column, the table stats can still work. - testKit.MustExec("alter table t drop column c2") - is = do.InfoSchema() - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) - - // If the new schema add a column, the table stats can still work. - testKit.MustExec("alter table t add column c10 int") - is = do.InfoSchema() - - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) -} - -func TestStatsCacheMemTracker(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int,c3 int)") - testKit.MustExec("insert into t values(1, 2, 3)") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - statsTbl := do.StatsHandle().GetTableStats(tableInfo) - require.True(t, statsTbl.MemoryUsage().TotalMemUsage == 0) - require.True(t, statsTbl.Pseudo) - - testKit.MustExec("analyze table t") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - - require.False(t, statsTbl.Pseudo) - testKit.MustExec("create index idx_t on t(c1)") - do.InfoSchema() - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - - // If index is build, but stats is not updated. statsTbl can also work. - require.False(t, statsTbl.Pseudo) - // But the added index will not work. - require.Nil(t, statsTbl.Indices[int64(1)]) - - testKit.MustExec("analyze table t") - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - - require.False(t, statsTbl.Pseudo) - - // If the new schema drop a column, the table stats can still work. - testKit.MustExec("alter table t drop column c2") - is = do.InfoSchema() - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.True(t, statsTbl.MemoryUsage().TotalMemUsage > 0) - require.False(t, statsTbl.Pseudo) - - // If the new schema add a column, the table stats can still work. - testKit.MustExec("alter table t add column c10 int") - is = do.InfoSchema() - - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl = do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl.Pseudo) -} - -func TestStatsStoreAndLoad(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (c1 int, c2 int)") - recordCount := 1000 - for i := 0; i < recordCount; i++ { - testKit.MustExec("insert into t values (?, ?)", i, i+1) - } - testKit.MustExec("create index idx_t on t(c2)") - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - - testKit.MustExec("analyze table t") - statsTbl1 := do.StatsHandle().GetTableStats(tableInfo) - - do.StatsHandle().Clear() - err = do.StatsHandle().Update(is) - require.NoError(t, err) - statsTbl2 := do.StatsHandle().GetTableStats(tableInfo) - require.False(t, statsTbl2.Pseudo) - require.Equal(t, int64(recordCount), statsTbl2.RealtimeCount) - internal.AssertTableEqual(t, statsTbl1, statsTbl2) -} - -func testInitStatsMemTrace(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a int, b int, c int, primary key(a), key idx(b))") - tk.MustExec("insert into t1 values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,7,8)") - tk.MustExec("analyze table t1") - for i := 2; i < 10; i++ { - tk.MustExec(fmt.Sprintf("create table t%v (a int, b int, c int, primary key(a), key idx(b))", i)) - tk.MustExec(fmt.Sprintf("insert into t%v select * from t1", i)) - tk.MustExec(fmt.Sprintf("analyze table t%v", i)) - } - h := dom.StatsHandle() - is := dom.InfoSchema() - h.Clear() - require.Equal(t, h.MemConsumed(), int64(0)) - require.NoError(t, h.InitStats(is)) - - var memCostTot int64 - for i := 1; i < 10; i++ { - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr(fmt.Sprintf("t%v", i))) - require.NoError(t, err) - tStats := h.GetTableStats(tbl.Meta()) - memCostTot += tStats.MemoryUsage().TotalMemUsage - } - - require.Equal(t, h.MemConsumed(), memCostTot) -} - -func TestInitStatsMemTraceWithLite(t *testing.T) { - testInitStatsMemTraceFunc(t, true) -} - -func TestInitStatsMemTraceWithoutLite(t *testing.T) { - testInitStatsMemTraceFunc(t, false) -} - -func testInitStatsMemTraceFunc(t *testing.T, liteInitStats bool) { - originValue := config.GetGlobalConfig().Performance.LiteInitStats - defer func() { - config.GetGlobalConfig().Performance.LiteInitStats = originValue - }() - config.GetGlobalConfig().Performance.LiteInitStats = liteInitStats - testInitStatsMemTrace(t) -} - -func TestInitStats(t *testing.T) { - originValue := config.GetGlobalConfig().Performance.LiteInitStats - defer func() { - config.GetGlobalConfig().Performance.LiteInitStats = originValue - }() - config.GetGlobalConfig().Performance.LiteInitStats = false - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set @@session.tidb_analyze_version = 1") - testKit.MustExec("create table t(a int, b int, c int, primary key(a), key idx(b))") - testKit.MustExec("insert into t values (1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5),(6,7,8)") - testKit.MustExec("analyze table t") - h := dom.StatsHandle() - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - // `Update` will not use load by need strategy when `Lease` is 0, and `InitStats` is only called when - // `Lease` is not 0, so here we just change it. - h.SetLease(time.Millisecond) - - h.Clear() - require.NoError(t, h.InitStats(is)) - table0 := h.GetTableStats(tbl.Meta()) - cols := table0.Columns - require.Equal(t, uint8(0x36), cols[1].LastAnalyzePos.GetBytes()[0]) - require.Equal(t, uint8(0x37), cols[2].LastAnalyzePos.GetBytes()[0]) - require.Equal(t, uint8(0x38), cols[3].LastAnalyzePos.GetBytes()[0]) - h.Clear() - require.NoError(t, h.Update(is)) - table1 := h.GetTableStats(tbl.Meta()) - internal.AssertTableEqual(t, table0, table1) - h.SetLease(0) -} - -func TestInitStatsVer2(t *testing.T) { - originValue := config.GetGlobalConfig().Performance.LiteInitStats - defer func() { - config.GetGlobalConfig().Performance.LiteInitStats = originValue - }() - config.GetGlobalConfig().Performance.LiteInitStats = false - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("create table t(a int, b int, c int, index idx(a), index idxab(a, b))") - tk.MustExec("insert into t values(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (4, 4, 4), (4, 4, 4)") - tk.MustExec("analyze table t with 2 topn, 3 buckets") - h := dom.StatsHandle() - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - // `Update` will not use load by need strategy when `Lease` is 0, and `InitStats` is only called when - // `Lease` is not 0, so here we just change it. - h.SetLease(time.Millisecond) - - h.Clear() - require.NoError(t, h.InitStats(is)) - table0 := h.GetTableStats(tbl.Meta()) - cols := table0.Columns - require.Equal(t, uint8(0x33), cols[1].LastAnalyzePos.GetBytes()[0]) - require.Equal(t, uint8(0x33), cols[2].LastAnalyzePos.GetBytes()[0]) - require.Equal(t, uint8(0x33), cols[3].LastAnalyzePos.GetBytes()[0]) - h.Clear() - require.NoError(t, h.InitStats(is)) - table1 := h.GetTableStats(tbl.Meta()) - internal.AssertTableEqual(t, table0, table1) - h.SetLease(0) -} - -func TestInitStatsIssue41938(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set @@global.tidb_analyze_version=1") - tk.MustExec("set @@session.tidb_analyze_version=1") - tk.MustExec("create table t1 (a timestamp primary key)") - tk.MustExec("insert into t1 values ('2023-03-07 14:24:30'), ('2023-03-07 14:24:31'), ('2023-03-07 14:24:32'), ('2023-03-07 14:24:33')") - tk.MustExec("analyze table t1 with 0 topn") - h := dom.StatsHandle() - // `InitStats` is only called when `Lease` is not 0, so here we just change it. - h.SetLease(time.Millisecond) - h.Clear() - require.NoError(t, h.InitStats(dom.InfoSchema())) - h.SetLease(0) -} diff --git a/statistics/handle/history/BUILD.bazel b/statistics/handle/history/BUILD.bazel deleted file mode 100644 index a9673a5df9d42..0000000000000 --- a/statistics/handle/history/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "history", - srcs = ["history_stats.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/history", - visibility = ["//visibility:public"], - deps = [ - "//sessionctx", - "//statistics/handle/cache", - "//statistics/handle/storage", - "//statistics/handle/util", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) diff --git a/statistics/handle/internal/BUILD.bazel b/statistics/handle/internal/BUILD.bazel deleted file mode 100644 index 50a402f86cc02..0000000000000 --- a/statistics/handle/internal/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "internal", - srcs = ["testutil.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/internal", - visibility = ["//statistics/handle:__subpackages__"], - deps = [ - "//statistics", - "@com_github_stretchr_testify//require", - ], -) diff --git a/statistics/handle/internal/testutil.go b/statistics/handle/internal/testutil.go deleted file mode 100644 index c582ba94007ff..0000000000000 --- a/statistics/handle/internal/testutil.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package internal - -import ( - "testing" - - "github.com/pingcap/tidb/statistics" - "github.com/stretchr/testify/require" -) - -// AssertTableEqual is to assert whether two table is equal -func AssertTableEqual(t *testing.T, a *statistics.Table, b *statistics.Table) { - require.Equal(t, b.RealtimeCount, a.RealtimeCount) - require.Equal(t, b.ModifyCount, a.ModifyCount) - require.Len(t, a.Columns, len(b.Columns)) - for i := range a.Columns { - require.True(t, statistics.HistogramEqual(&a.Columns[i].Histogram, &b.Columns[i].Histogram, false)) - if a.Columns[i].CMSketch == nil { - require.Nil(t, b.Columns[i].CMSketch) - } else { - require.True(t, a.Columns[i].CMSketch.Equal(b.Columns[i].CMSketch)) - } - // The nil case has been considered in (*TopN).Equal() so we don't need to consider it here. - require.Truef(t, a.Columns[i].TopN.Equal(b.Columns[i].TopN), "%v, %v", a.Columns[i].TopN, b.Columns[i].TopN) - } - require.Len(t, a.Indices, len(b.Indices)) - for i := range a.Indices { - require.True(t, statistics.HistogramEqual(&a.Indices[i].Histogram, &b.Indices[i].Histogram, false)) - if a.Indices[i].CMSketch == nil { - require.Nil(t, b.Indices[i].CMSketch) - } else { - require.True(t, a.Indices[i].CMSketch.Equal(b.Indices[i].CMSketch)) - } - require.True(t, a.Indices[i].TopN.Equal(b.Indices[i].TopN)) - } - require.True(t, IsSameExtendedStats(a.ExtendedStats, b.ExtendedStats)) -} - -// IsSameExtendedStats is to judge whether the extended states is the same. -func IsSameExtendedStats(a, b *statistics.ExtendedStatsColl) bool { - aEmpty := (a == nil) || len(a.Stats) == 0 - bEmpty := (b == nil) || len(b.Stats) == 0 - if (aEmpty && !bEmpty) || (!aEmpty && bEmpty) { - return false - } - if aEmpty && bEmpty { - return true - } - if len(a.Stats) != len(b.Stats) { - return false - } - for aKey, aItem := range a.Stats { - bItem, ok := b.Stats[aKey] - if !ok { - return false - } - for i, id := range aItem.ColIDs { - if id != bItem.ColIDs[i] { - return false - } - } - if (aItem.Tp != bItem.Tp) || (aItem.ScalarVals != bItem.ScalarVals) || (aItem.StringVals != bItem.StringVals) { - return false - } - } - return true -} diff --git a/statistics/handle/lockstats/BUILD.bazel b/statistics/handle/lockstats/BUILD.bazel deleted file mode 100644 index 9336d82c38e1c..0000000000000 --- a/statistics/handle/lockstats/BUILD.bazel +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "lockstats", - srcs = [ - "lock_stats.go", - "query_lock.go", - "unlock_stats.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/lockstats", - visibility = ["//visibility:public"], - deps = [ - "//sessionctx", - "//statistics/handle/cache", - "//statistics/handle/util", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "lockstats_test", - timeout = "short", - srcs = [ - "lock_stats_test.go", - "query_lock_test.go", - "unlock_stats_test.go", - ], - embed = [":lockstats"], - flaky = True, - shard_count = 13, - deps = [ - "//kv", - "//parser/mysql", - "//sessionctx", - "//statistics/handle/util", - "//types", - "//util/chunk", - "//util/mock", - "//util/sqlexec/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_mock//gomock", - ], -) diff --git a/statistics/handle/main_test.go b/statistics/handle/main_test.go deleted file mode 100644 index 285de7283a6d9..0000000000000 --- a/statistics/handle/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/metrics/BUILD.bazel b/statistics/handle/metrics/BUILD.bazel deleted file mode 100644 index 0c12cf8d2ee2d..0000000000000 --- a/statistics/handle/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/statistics/handle/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/statistics/handle/metrics/metrics.go b/statistics/handle/metrics/metrics.go deleted file mode 100644 index 91b6aee22bc4f..0000000000000 --- a/statistics/handle/metrics/metrics.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// statistics metrics vars -var ( - StatsHealthyGauges []prometheus.Gauge - - DumpHistoricalStatsSuccessCounter prometheus.Counter - DumpHistoricalStatsFailedCounter prometheus.Counter -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init statistics metrics vars. -func InitMetricsVars() { - StatsHealthyGauges = []prometheus.Gauge{ - metrics.StatsHealthyGauge.WithLabelValues("[0,50)"), - metrics.StatsHealthyGauge.WithLabelValues("[50,80)"), - metrics.StatsHealthyGauge.WithLabelValues("[80,100)"), - metrics.StatsHealthyGauge.WithLabelValues("[100,100]"), - // [0,100] should always be the last - metrics.StatsHealthyGauge.WithLabelValues("[0,100]"), - } - - DumpHistoricalStatsSuccessCounter = metrics.HistoricalStatsCounter.WithLabelValues("dump", "success") - DumpHistoricalStatsFailedCounter = metrics.HistoricalStatsCounter.WithLabelValues("dump", "fail") -} diff --git a/statistics/handle/storage/BUILD.bazel b/statistics/handle/storage/BUILD.bazel deleted file mode 100644 index 1b15d4eb398e3..0000000000000 --- a/statistics/handle/storage/BUILD.bazel +++ /dev/null @@ -1,56 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "storage", - srcs = [ - "gc.go", - "json.go", - "read.go", - "save.go", - "update.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/storage", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//infoschema", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/cache", - "//statistics/handle/lockstats", - "//statistics/handle/util", - "//types", - "//util/chunk", - "//util/compress", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/sqlexec", - "@com_github_klauspost_compress//gzip", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "storage_test", - timeout = "short", - srcs = ["read_test.go"], - flaky = True, - deps = [ - "//parser/model", - "//planner/cardinality", - "//testkit", - "//types", - "@com_github_stretchr_testify//require", - ], -) diff --git a/statistics/handle/storage/update.go b/statistics/handle/storage/update.go deleted file mode 100644 index 08d523b781e72..0000000000000 --- a/statistics/handle/storage/update.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package storage - -import ( - "encoding/json" - "fmt" - "slices" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/cache" - statsutil "github.com/pingcap/tidb/statistics/handle/util" -) - -// UpdateStatsVersion will set statistics version to the newest TS, -// then tidb-server will reload automatic. -func UpdateStatsVersion(sctx sessionctx.Context) error { - startTS, err := statsutil.GetStartTS(sctx) - if err != nil { - return errors.Trace(err) - } - if _, err = statsutil.Exec(sctx, "update mysql.stats_meta set version = %?", startTS); err != nil { - return err - } - if _, err = statsutil.Exec(sctx, "update mysql.stats_extended set version = %?", startTS); err != nil { - return err - } - if _, err = statsutil.Exec(sctx, "update mysql.stats_histograms set version = %?", startTS); err != nil { - return err - } - return nil -} - -// UpdateStatsMeta update the stats meta stat for this Table. -func UpdateStatsMeta( - sctx sessionctx.Context, - startTS uint64, - delta variable.TableDelta, - id int64, - isLocked bool, -) (err error) { - if isLocked { - // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_table_locked. - // Note: For locked tables, it is possible that the record gets deleted. So it can be negative. - _, err = statsutil.Exec(sctx, "insert into mysql.stats_table_locked (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+ - "update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", - startTS, id, delta.Count, delta.Delta) - } else { - if delta.Delta < 0 { - // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. - _, err = statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, 0) on duplicate key "+ - "update version = values(version), modify_count = modify_count + values(modify_count), count = if(count > %?, count - %?, 0)", - startTS, id, delta.Count, -delta.Delta, -delta.Delta) - } else { - // use INSERT INTO ... ON DUPLICATE KEY UPDATE here to fill missing stats_meta. - _, err = statsutil.Exec(sctx, "insert into mysql.stats_meta (version, table_id, modify_count, count) values (%?, %?, %?, %?) on duplicate key "+ - "update version = values(version), modify_count = modify_count + values(modify_count), count = count + values(count)", startTS, - id, delta.Count, delta.Delta) - } - cache.TableRowStatsCache.Invalidate(id) - } - return err -} - -// DumpTableStatColSizeToKV dumps the column size stats to storage. -func DumpTableStatColSizeToKV(sctx sessionctx.Context, id int64, delta variable.TableDelta) error { - if len(delta.ColSize) == 0 { - return nil - } - values := make([]string, 0, len(delta.ColSize)) - for histID, deltaColSize := range delta.ColSize { - if deltaColSize == 0 { - continue - } - values = append(values, fmt.Sprintf("(%d, 0, %d, 0, %d)", id, histID, deltaColSize)) - } - if len(values) == 0 { - return nil - } - sql := fmt.Sprintf("insert into mysql.stats_histograms (table_id, is_index, hist_id, distinct_count, tot_col_size) "+ - "values %s on duplicate key update tot_col_size = tot_col_size + values(tot_col_size)", strings.Join(values, ",")) - _, _, err := statsutil.ExecRows(sctx, sql) - return errors.Trace(err) -} - -// InsertExtendedStats inserts a record into mysql.stats_extended and update version in mysql.stats_meta. -func InsertExtendedStats(sctx sessionctx.Context, - statsCache statsutil.StatsCache, - statsName string, colIDs []int64, tp int, tableID int64, ifNotExists bool) (statsVer uint64, err error) { - slices.Sort(colIDs) - bytes, err := json.Marshal(colIDs) - if err != nil { - return 0, errors.Trace(err) - } - strColIDs := string(bytes) - - _, err = statsutil.Exec(sctx, "begin pessimistic") - if err != nil { - return 0, errors.Trace(err) - } - defer func() { - err = statsutil.FinishTransaction(sctx, err) - }() - // No need to use `exec.ExecuteInternal` since we have acquired the lock. - rows, _, err := statsutil.ExecRows(sctx, "SELECT name, type, column_ids FROM mysql.stats_extended WHERE table_id = %? and status in (%?, %?)", tableID, statistics.ExtendedStatsInited, statistics.ExtendedStatsAnalyzed) - if err != nil { - return 0, errors.Trace(err) - } - for _, row := range rows { - currStatsName := row.GetString(0) - currTp := row.GetInt64(1) - currStrColIDs := row.GetString(2) - if currStatsName == statsName { - if ifNotExists { - return 0, nil - } - return 0, errors.Errorf("extended statistics '%s' for the specified table already exists", statsName) - } - if tp == int(currTp) && currStrColIDs == strColIDs { - return 0, errors.Errorf("extended statistics '%s' with same type on same columns already exists", statsName) - } - } - version, err := statsutil.GetStartTS(sctx) - if err != nil { - return 0, errors.Trace(err) - } - // Bump version in `mysql.stats_meta` to trigger stats cache refresh. - if _, err = statsutil.Exec(sctx, "UPDATE mysql.stats_meta SET version = %? WHERE table_id = %?", version, tableID); err != nil { - return 0, err - } - statsVer = version - // Remove the existing 'deleted' records. - if _, err = statsutil.Exec(sctx, "DELETE FROM mysql.stats_extended WHERE name = %? and table_id = %?", statsName, tableID); err != nil { - return 0, err - } - // Remove the cache item, which is necessary for cases like a cluster with 3 tidb instances, e.g, a, b and c. - // If tidb-a executes `alter table drop stats_extended` to mark the record as 'deleted', and before this operation - // is synchronized to other tidb instances, tidb-b executes `alter table add stats_extended`, which would delete - // the record from the table, tidb-b should delete the cached item synchronously. While for tidb-c, it has to wait for - // next `Update()` to remove the cached item then. - removeExtendedStatsItem(statsCache, tableID, statsName) - const sql = "INSERT INTO mysql.stats_extended(name, type, table_id, column_ids, version, status) VALUES (%?, %?, %?, %?, %?, %?)" - if _, err = statsutil.Exec(sctx, sql, statsName, tp, tableID, strColIDs, version, statistics.ExtendedStatsInited); err != nil { - return 0, err - } - return -} - -// SaveExtendedStatsToStorage writes extended stats of a table into mysql.stats_extended. -func SaveExtendedStatsToStorage(sctx sessionctx.Context, - tableID int64, extStats *statistics.ExtendedStatsColl, isLoad bool) (statsVer uint64, err error) { - if extStats == nil || len(extStats.Stats) == 0 { - return 0, nil - } - - _, err = statsutil.Exec(sctx, "begin pessimistic") - if err != nil { - return 0, errors.Trace(err) - } - defer func() { - err = statsutil.FinishTransaction(sctx, err) - }() - version, err := statsutil.GetStartTS(sctx) - if err != nil { - return 0, errors.Trace(err) - } - for name, item := range extStats.Stats { - bytes, err := json.Marshal(item.ColIDs) - if err != nil { - return 0, errors.Trace(err) - } - strColIDs := string(bytes) - var statsStr string - switch item.Tp { - case ast.StatsTypeCardinality, ast.StatsTypeCorrelation: - statsStr = fmt.Sprintf("%f", item.ScalarVals) - case ast.StatsTypeDependency: - statsStr = item.StringVals - } - // If isLoad is true, it's INSERT; otherwise, it's UPDATE. - if _, err := statsutil.Exec(sctx, "replace into mysql.stats_extended values (%?, %?, %?, %?, %?, %?, %?)", name, item.Tp, tableID, strColIDs, statsStr, version, statistics.ExtendedStatsAnalyzed); err != nil { - return 0, err - } - } - if !isLoad { - if _, err := statsutil.Exec(sctx, "UPDATE mysql.stats_meta SET version = %? WHERE table_id = %?", version, tableID); err != nil { - return 0, err - } - statsVer = version - } - return statsVer, nil -} - -func removeExtendedStatsItem(statsCache statsutil.StatsCache, - tableID int64, statsName string) { - tbl, ok := statsCache.Get(tableID) - if !ok || tbl.ExtendedStats == nil || len(tbl.ExtendedStats.Stats) == 0 { - return - } - newTbl := tbl.Copy() - delete(newTbl.ExtendedStats.Stats, statsName) - statsCache.UpdateStatsCache([]*statistics.Table{newTbl}, nil) -} diff --git a/statistics/handle/update.go b/statistics/handle/update.go deleted file mode 100644 index c6aa9096b2b9d..0000000000000 --- a/statistics/handle/update.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package handle - -import ( - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - utilstats "github.com/pingcap/tidb/statistics/handle/util" -) - -func (h *Handle) callWithSCtx(f func(sctx sessionctx.Context) error, flags ...int) (err error) { - return utilstats.CallWithSCtx(h.pool, f, flags...) -} - -const ( - // StatsOwnerKey is the stats owner path that is saved to etcd. - StatsOwnerKey = "/tidb/stats/owner" - // StatsPrompt is the prompt for stats owner manager. - StatsPrompt = "stats" -) - -// HandleAutoAnalyze analyzes the newly created table or index. -func (h *Handle) HandleAutoAnalyze(is infoschema.InfoSchema) (analyzed bool) { - _ = h.callWithSCtx(func(sctx sessionctx.Context) error { - analyzed = autoanalyze.HandleAutoAnalyze(sctx, &autoanalyze.Opt{ - StatsLease: h.Lease(), - GetLockedTables: h.GetLockedTables, - GetTableStats: h.GetTableStats, - GetPartitionStats: h.GetPartitionStats, - SysProcTracker: h.sysProcTracker, - AutoAnalyzeProcIDGetter: h.autoAnalyzeProcIDGetter, - }, is) - return nil - }) - return -} - -// GetCurrentPruneMode returns the current latest partitioning table prune mode. -func (h *Handle) GetCurrentPruneMode() (mode string, err error) { - err = h.callWithSCtx(func(sctx sessionctx.Context) error { - mode = sctx.GetSessionVars().PartitionPruneMode.Load() - return nil - }) - return -} diff --git a/statistics/handle/updatetest/BUILD.bazel b/statistics/handle/updatetest/BUILD.bazel deleted file mode 100644 index ab74e910095cb..0000000000000 --- a/statistics/handle/updatetest/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "updatetest_test", - timeout = "short", - srcs = [ - "main_test.go", - "update_test.go", - ], - flaky = True, - shard_count = 23, - deps = [ - "//parser/model", - "//parser/mysql", - "//planner/cardinality", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/autoanalyze", - "//statistics/handle/usage", - "//statistics/handle/util", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/collate", - "//util/ranger", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/statistics/handle/updatetest/main_test.go b/statistics/handle/updatetest/main_test.go deleted file mode 100644 index 4b799462c9323..0000000000000 --- a/statistics/handle/updatetest/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package updatetest - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/statistics/handle/updatetest/update_test.go b/statistics/handle/updatetest/update_test.go deleted file mode 100644 index 14b8a58471d5f..0000000000000 --- a/statistics/handle/updatetest/update_test.go +++ /dev/null @@ -1,1310 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package updatetest - -import ( - "fmt" - "math/rand" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/cardinality" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/statistics/handle/usage" - "github.com/pingcap/tidb/statistics/handle/util" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/ranger" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" -) - -func TestSingleSessionInsert(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set @@session.tidb_analyze_version = 1") - testKit.MustExec("create table t1 (c1 int, c2 int)") - testKit.MustExec("create table t2 (c1 int, c2 int)") - - rowCount1 := 10 - rowCount2 := 20 - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - for i := 0; i < rowCount2; i++ { - testKit.MustExec("insert into t2 values(1, 2)") - } - - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - h := dom.StatsHandle() - - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 := h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tableInfo2 := tbl2.Meta() - stats2 := h.GetTableStats(tableInfo2) - require.Equal(t, int64(rowCount2), stats2.RealtimeCount) - - testKit.MustExec("analyze table t1") - // Test update in a txn. - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1*2), stats1.RealtimeCount) - - // Test IncreaseFactor. - count, err := cardinality.ColumnEqualRowCount(testKit.Session(), stats1, types.NewIntDatum(1), tableInfo1.Columns[0].ID) - require.NoError(t, err) - require.Equal(t, float64(rowCount1*2), count) - - testKit.MustExec("begin") - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - testKit.MustExec("commit") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1*3), stats1.RealtimeCount) - - testKit.MustExec("begin") - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - for i := 0; i < rowCount1; i++ { - testKit.MustExec("delete from t1 limit 1") - } - for i := 0; i < rowCount2; i++ { - testKit.MustExec("update t2 set c2 = c1") - } - testKit.MustExec("commit") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1*3), stats1.RealtimeCount) - stats2 = h.GetTableStats(tableInfo2) - require.Equal(t, int64(rowCount2), stats2.RealtimeCount) - - testKit.MustExec("begin") - testKit.MustExec("delete from t1") - testKit.MustExec("commit") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(0), stats1.RealtimeCount) - - rs := testKit.MustQuery("select modify_count from mysql.stats_meta") - rs.Check(testkit.Rows("40", "70")) - - rs = testKit.MustQuery("select tot_col_size from mysql.stats_histograms").Sort() - rs.Check(testkit.Rows("0", "0", "20", "20")) - - // test dump delta only when `modify count / count` is greater than the ratio. - originValue := usage.DumpStatsDeltaRatio - usage.DumpStatsDeltaRatio = 0.5 - defer func() { - usage.DumpStatsDeltaRatio = originValue - }() - usage.DumpStatsDeltaRatio = 0.5 - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values (1,2)") - } - err = h.DumpStatsDeltaToKV(false) - require.NoError(t, err) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - // not dumped - testKit.MustExec("insert into t1 values (1,2)") - err = h.DumpStatsDeltaToKV(false) - require.NoError(t, err) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - h.FlushStats() - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1+1), stats1.RealtimeCount) -} - -func TestRollback(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (a int, b int)") - testKit.MustExec("begin") - testKit.MustExec("insert into t values (1,2)") - testKit.MustExec("rollback") - - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := dom.StatsHandle() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - - stats := h.GetTableStats(tableInfo) - require.Equal(t, int64(0), stats.RealtimeCount) - require.Equal(t, int64(0), stats.ModifyCount) -} - -func TestMultiSession(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t1 (c1 int, c2 int)") - - rowCount1 := 10 - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - - testKit1 := testkit.NewTestKit(t, store) - for i := 0; i < rowCount1; i++ { - testKit1.MustExec("insert into test.t1 values(1, 2)") - } - testKit2 := testkit.NewTestKit(t, store) - for i := 0; i < rowCount1; i++ { - testKit2.MustExec("delete from test.t1 limit 1") - } - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - h := dom.StatsHandle() - - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 := h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - - for i := 0; i < rowCount1; i++ { - testKit1.MustExec("insert into test.t1 values(1, 2)") - } - - for i := 0; i < rowCount1; i++ { - testKit2.MustExec("delete from test.t1 limit 1") - } - - testKit.Session().Close() - testKit2.Session().Close() - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1*2), stats1.RealtimeCount) - testKit.RefreshSession() - rs := testKit.MustQuery("select modify_count from mysql.stats_meta") - rs.Check(testkit.Rows("60")) -} - -func TestTxnWithFailure(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t1 (c1 int primary key, c2 int)") - - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - h := dom.StatsHandle() - - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - - rowCount1 := 10 - testKit.MustExec("begin") - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(?, 2)", i) - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 := h.GetTableStats(tableInfo1) - // have not commit - require.Equal(t, int64(0), stats1.RealtimeCount) - testKit.MustExec("commit") - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - _, err = testKit.Exec("insert into t1 values(0, 2)") - require.Error(t, err) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1), stats1.RealtimeCount) - - testKit.MustExec("insert into t1 values(-1, 2)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(rowCount1+1), stats1.RealtimeCount) -} - -func TestUpdatePartition(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - pruneMode, err := dom.StatsHandle().GetCurrentPruneMode() - require.NoError(t, err) - testKit.MustQuery("select @@tidb_partition_prune_mode").Check(testkit.Rows(pruneMode)) - testKit.MustExec("use test") - testkit.WithPruneMode(testKit, variable.Static, func() { - require.NoError(t, err) - testKit.MustExec("drop table if exists t") - createTable := `CREATE TABLE t (a int, b char(5)) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11))` - testKit.MustExec(createTable) - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := do.StatsHandle() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - pi := tableInfo.GetPartitionInfo() - require.Len(t, pi.Definitions, 2) - bColID := tableInfo.Columns[1].ID - - testKit.MustExec(`insert into t values (1, "a"), (7, "a")`) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.Equal(t, int64(1), statsTbl.ModifyCount) - require.Equal(t, int64(1), statsTbl.RealtimeCount) - require.Equal(t, int64(2), statsTbl.Columns[bColID].TotColSize) - } - - testKit.MustExec(`update t set a = a + 1, b = "aa"`) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.Equal(t, int64(2), statsTbl.ModifyCount) - require.Equal(t, int64(1), statsTbl.RealtimeCount) - require.Equal(t, int64(3), statsTbl.Columns[bColID].TotColSize) - } - - testKit.MustExec("delete from t") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.Equal(t, int64(3), statsTbl.ModifyCount) - require.Equal(t, int64(0), statsTbl.RealtimeCount) - require.Equal(t, int64(0), statsTbl.Columns[bColID].TotColSize) - } - // assert WithGetTableStatsByQuery get the same result - for _, def := range pi.Definitions { - statsTbl := h.GetPartitionStats(tableInfo, def.ID) - require.Equal(t, int64(3), statsTbl.ModifyCount) - require.Equal(t, int64(0), statsTbl.RealtimeCount) - require.Equal(t, int64(0), statsTbl.Columns[bColID].TotColSize) - } - }) -} - -func TestAutoUpdate(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testkit.WithPruneMode(testKit, variable.Static, func() { - testKit.MustExec("use test") - testKit.MustExec("create table t (a varchar(20))") - - autoanalyze.AutoAnalyzeMinCnt = 0 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.2") - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") - }() - - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := do.StatsHandle() - - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - require.NoError(t, h.Update(is)) - stats := h.GetTableStats(tableInfo) - require.Equal(t, int64(0), stats.RealtimeCount) - - _, err = testKit.Exec("insert into t values ('ss'), ('ss'), ('ss'), ('ss'), ('ss')") - require.NoError(t, err) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - stats = h.GetTableStats(tableInfo) - require.Equal(t, int64(5), stats.RealtimeCount) - require.Equal(t, int64(0), stats.ModifyCount) - for _, item := range stats.Columns { - // TotColSize = 5*(2(length of 'ss') + 1(size of len byte)). - require.Equal(t, int64(15), item.TotColSize) - break - } - - // Test that even if the table is recently modified, we can still analyze the table. - h.SetLease(time.Second) - defer func() { h.SetLease(0) }() - _, err = testKit.Exec("insert into t values ('fff')") - require.NoError(t, err) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - stats = h.GetTableStats(tableInfo) - require.Equal(t, int64(6), stats.RealtimeCount) - require.Equal(t, int64(1), stats.ModifyCount) - - _, err = testKit.Exec("insert into t values ('fff')") - require.NoError(t, err) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - stats = h.GetTableStats(tableInfo) - require.Equal(t, int64(7), stats.RealtimeCount) - require.Equal(t, int64(0), stats.ModifyCount) - - _, err = testKit.Exec("insert into t values ('eee')") - require.NoError(t, err) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - stats = h.GetTableStats(tableInfo) - require.Equal(t, int64(8), stats.RealtimeCount) - // Modify count is non-zero means that we do not analyze the table. - require.Equal(t, int64(1), stats.ModifyCount) - for _, item := range stats.Columns { - // TotColSize = 27, because the table has not been analyzed, and insert statement will add 3(length of 'eee') to TotColSize. - require.Equal(t, int64(27), item.TotColSize) - break - } - - testKit.MustExec("analyze table t") - _, err = testKit.Exec("create index idx on t(a)") - require.NoError(t, err) - is = do.InfoSchema() - tbl, err = is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo = tbl.Meta() - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - testKit.MustExec("explain select * from t where a > 'a'") - require.NoError(t, h.LoadNeededHistograms()) - stats = h.GetTableStats(tableInfo) - require.Equal(t, int64(8), stats.RealtimeCount) - require.Equal(t, int64(0), stats.ModifyCount) - hg, ok := stats.Indices[tableInfo.Indices[0].ID] - require.True(t, ok) - require.Equal(t, int64(3), hg.NDV) - require.Equal(t, 0, hg.Len()) - require.Equal(t, 3, hg.TopN.Num()) - }) -} - -func TestAutoUpdatePartition(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testkit.WithPruneMode(testKit, variable.Static, func() { - testKit.MustExec("use test") - testKit.MustExec("drop table if exists t") - testKit.MustExec("create table t (a int) PARTITION BY RANGE (a) (PARTITION p0 VALUES LESS THAN (6))") - testKit.MustExec("analyze table t") - - autoanalyze.AutoAnalyzeMinCnt = 0 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.6") - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") - }() - - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - pi := tableInfo.GetPartitionInfo() - h := do.StatsHandle() - - require.NoError(t, h.Update(is)) - stats := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, int64(0), stats.RealtimeCount) - - testKit.MustExec("insert into t values (1)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - h.HandleAutoAnalyze(is) - stats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, int64(1), stats.RealtimeCount) - require.Equal(t, int64(0), stats.ModifyCount) - }) -} - -func TestIssue25700(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("CREATE TABLE `t` ( `ldecimal` decimal(32,4) DEFAULT NULL, `rdecimal` decimal(32,4) DEFAULT NULL, `gen_col` decimal(36,4) GENERATED ALWAYS AS (`ldecimal` + `rdecimal`) VIRTUAL, `col_timestamp` timestamp(3) NULL DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;") - tk.MustExec("analyze table t") - tk.MustExec("INSERT INTO `t` (`ldecimal`, `rdecimal`, `col_timestamp`) VALUES (2265.2200, 9843.4100, '1999-12-31 16:00:00')" + strings.Repeat(", (2265.2200, 9843.4100, '1999-12-31 16:00:00')", int(autoanalyze.AutoAnalyzeMinCnt))) - require.NoError(t, dom.StatsHandle().DumpStatsDeltaToKV(true)) - require.NoError(t, dom.StatsHandle().Update(dom.InfoSchema())) - - require.True(t, dom.StatsHandle().HandleAutoAnalyze(dom.InfoSchema())) - require.Equal(t, "finished", tk.MustQuery("show analyze status").Rows()[1][7]) -} - -func appendBucket(h *statistics.Histogram, l, r int64) { - lower, upper := types.NewIntDatum(l), types.NewIntDatum(r) - h.AppendBucket(&lower, &upper, 0, 0) -} - -func TestSplitRange(t *testing.T) { - h := statistics.NewHistogram(0, 0, 0, 0, types.NewFieldType(mysql.TypeLong), 5, 0) - appendBucket(h, 1, 1) - appendBucket(h, 2, 5) - appendBucket(h, 7, 7) - appendBucket(h, 8, 8) - appendBucket(h, 10, 13) - - tests := []struct { - points []int64 - exclude []bool - result string - }{ - { - points: []int64{1, 1}, - exclude: []bool{false, false}, - result: "[1,1]", - }, - { - points: []int64{0, 1, 3, 8, 8, 20}, - exclude: []bool{true, false, true, false, true, false}, - result: "(0,1],(3,7),[7,8),[8,8],(8,10),[10,20]", - }, - { - points: []int64{8, 10, 20, 30}, - exclude: []bool{false, false, true, true}, - result: "[8,10),[10,10],(20,30)", - }, - { - // test remove invalid range - points: []int64{8, 9}, - exclude: []bool{false, true}, - result: "[8,9)", - }, - } - for _, test := range tests { - ranges := make([]*ranger.Range, 0, len(test.points)/2) - for i := 0; i < len(test.points); i += 2 { - ranges = append(ranges, &ranger.Range{ - LowVal: []types.Datum{types.NewIntDatum(test.points[i])}, - LowExclude: test.exclude[i], - HighVal: []types.Datum{types.NewIntDatum(test.points[i+1])}, - HighExclude: test.exclude[i+1], - Collators: collate.GetBinaryCollatorSlice(1), - }) - } - ranges, _ = h.SplitRange(nil, ranges, false) - var ranStrs []string - for _, ran := range ranges { - ranStrs = append(ranStrs, ran.String()) - } - require.Equal(t, test.result, strings.Join(ranStrs, ",")) - } -} - -func TestOutOfOrderUpdate(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("create table t (a int, b int)") - testKit.MustExec("insert into t values (1,2)") - - do := dom - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - h := do.StatsHandle() - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - - // Simulate the case that another tidb has inserted some value, but delta info has not been dumped to kv yet. - testKit.MustExec("insert into t values (2,2),(4,5)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 1 where table_id = %d", tableInfo.ID)) - - testKit.MustExec("delete from t") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - // If count < -Delta, then update count to 0. - // Check https://github.com/pingcap/tidb/pull/38301#discussion_r1094050951 for details. - testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("0")) - - // Now another tidb has updated the delta info. - testKit.MustExec(fmt.Sprintf("update mysql.stats_meta set count = 3 where table_id = %d", tableInfo.ID)) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustQuery(fmt.Sprintf("select count from mysql.stats_meta where table_id = %d", tableInfo.ID)).Check(testkit.Rows("3")) -} - -func TestLoadHistCorrelation(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - origLease := h.Lease() - h.SetLease(time.Second) - defer func() { h.SetLease(origLease) }() - testKit.MustExec("use test") - testKit.MustExec("create table t(c int)") - testKit.MustExec("insert into t values(1),(2),(3),(4),(5)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec("analyze table t") - h.Clear() - require.NoError(t, h.Update(dom.InfoSchema())) - result := testKit.MustQuery("show stats_histograms where Table_name = 't'") - // After https://github.com/pingcap/tidb/pull/37444, `show stats_histograms` displays the columns whose hist/topn/cmsketch - // are not loaded and their stats status is allEvicted. - require.Len(t, result.Rows(), 1) - testKit.MustExec("explain select * from t where c = 1") - require.NoError(t, h.LoadNeededHistograms()) - result = testKit.MustQuery("show stats_histograms where Table_name = 't'") - require.Len(t, result.Rows(), 1) - require.Equal(t, "1", result.Rows()[0][9]) -} - -func BenchmarkHandleAutoAnalyze(b *testing.B) { - store, dom := testkit.CreateMockStoreAndDomain(b) - testKit := testkit.NewTestKit(b, store) - testKit.MustExec("use test") - h := dom.StatsHandle() - is := dom.InfoSchema() - for i := 0; i < b.N; i++ { - h.HandleAutoAnalyze(is) - } -} - -// subtraction parses the number for counter and returns new - old. -// string for counter will be `label: counter: ` -func subtraction(newMetric *dto.Metric, oldMetric *dto.Metric) int { - return int(*(newMetric.Counter.Value) - *(oldMetric.Counter.Value)) -} - -func TestMergeTopN(t *testing.T) { - // Move this test to here to avoid race test. - tests := []struct { - topnNum int - n int - maxTopNVal int - maxTopNCnt int - }{ - { - topnNum: 10, - n: 5, - maxTopNVal: 50, - maxTopNCnt: 100, - }, - { - topnNum: 1, - n: 5, - maxTopNVal: 50, - maxTopNCnt: 100, - }, - { - topnNum: 5, - n: 5, - maxTopNVal: 5, - maxTopNCnt: 100, - }, - { - topnNum: 5, - n: 5, - maxTopNVal: 10, - maxTopNCnt: 100, - }, - } - for _, test := range tests { - topnNum, n := test.topnNum, test.n - maxTopNVal, maxTopNCnt := test.maxTopNVal, test.maxTopNCnt - - // the number of maxTopNVal should be bigger than n. - ok := maxTopNVal >= n - require.Equal(t, true, ok) - - topNs := make([]*statistics.TopN, 0, topnNum) - res := make(map[int]uint64) - rand.Seed(time.Now().Unix()) - for i := 0; i < topnNum; i++ { - topN := statistics.NewTopN(n) - occur := make(map[int]bool) - for j := 0; j < n; j++ { - // The range of numbers in the topn structure is in [0, maxTopNVal) - // But there cannot be repeated occurrences of value in a topN structure. - randNum := rand.Intn(maxTopNVal) - for occur[randNum] { - randNum = rand.Intn(maxTopNVal) - } - occur[randNum] = true - tString := []byte(fmt.Sprintf("%d", randNum)) - // The range of the number of occurrences in the topn structure is in [0, maxTopNCnt) - randCnt := uint64(rand.Intn(maxTopNCnt)) - res[randNum] += randCnt - topNMeta := statistics.TopNMeta{Encoded: tString, Count: randCnt} - topN.TopN = append(topN.TopN, topNMeta) - } - topNs = append(topNs, topN) - } - topN, remainTopN := statistics.MergeTopN(topNs, uint32(n)) - cnt := len(topN.TopN) - var minTopNCnt uint64 - for _, topNMeta := range topN.TopN { - val, err := strconv.Atoi(string(topNMeta.Encoded)) - require.NoError(t, err) - require.Equal(t, res[val], topNMeta.Count) - minTopNCnt = topNMeta.Count - } - if remainTopN != nil { - cnt += len(remainTopN) - for _, remainTopNMeta := range remainTopN { - val, err := strconv.Atoi(string(remainTopNMeta.Encoded)) - require.NoError(t, err) - require.Equal(t, res[val], remainTopNMeta.Count) - // The count of value in remainTopN may equal to the min count of value in TopN. - ok = minTopNCnt >= remainTopNMeta.Count - require.Equal(t, true, ok) - } - } - require.Equal(t, len(res), cnt) - } -} - -func TestStatsVariables(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - sctx := tk.Session().(sessionctx.Context) - - pruneMode, err := h.GetCurrentPruneMode() - require.NoError(t, err) - require.Equal(t, string(variable.Dynamic), pruneMode) - err = util.UpdateSCtxVarsForStats(sctx) - require.NoError(t, err) - require.Equal(t, 2, sctx.GetSessionVars().AnalyzeVersion) - require.Equal(t, true, sctx.GetSessionVars().EnableHistoricalStats) - require.Equal(t, string(variable.Dynamic), sctx.GetSessionVars().PartitionPruneMode.Load()) - require.Equal(t, false, sctx.GetSessionVars().EnableAnalyzeSnapshot) - require.Equal(t, true, sctx.GetSessionVars().SkipMissingPartitionStats) - - tk.MustExec(`set global tidb_analyze_version=1`) - tk.MustExec(`set global tidb_partition_prune_mode='static'`) - tk.MustExec(`set global tidb_enable_historical_stats=0`) - tk.MustExec(`set global tidb_enable_analyze_snapshot=1`) - tk.MustExec(`set global tidb_skip_missing_partition_stats=0`) - - pruneMode, err = h.GetCurrentPruneMode() - require.NoError(t, err) - require.Equal(t, string(variable.Static), pruneMode) - err = util.UpdateSCtxVarsForStats(sctx) - require.NoError(t, err) - require.Equal(t, 1, sctx.GetSessionVars().AnalyzeVersion) - require.Equal(t, false, sctx.GetSessionVars().EnableHistoricalStats) - require.Equal(t, string(variable.Static), sctx.GetSessionVars().PartitionPruneMode.Load()) - require.Equal(t, true, sctx.GetSessionVars().EnableAnalyzeSnapshot) - require.Equal(t, false, sctx.GetSessionVars().SkipMissingPartitionStats) -} - -func TestAutoUpdatePartitionInDynamicOnlyMode(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testkit.WithPruneMode(testKit, variable.DynamicOnly, func() { - testKit.MustExec("use test") - testKit.MustExec("set @@tidb_analyze_version = 2;") - testKit.MustExec("drop table if exists t") - testKit.MustExec(`create table t (a int, b varchar(10), index idx_ab(a, b)) - partition by range (a) ( - partition p0 values less than (10), - partition p1 values less than (20), - partition p2 values less than (30))`) - - do := dom - is := do.InfoSchema() - h := do.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - - testKit.MustExec("insert into t values (1, 'a'), (2, 'b'), (11, 'c'), (12, 'd'), (21, 'e'), (22, 'f')") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - testKit.MustExec("set @@tidb_analyze_version = 2") - testKit.MustExec("analyze table t") - - autoanalyze.AutoAnalyzeMinCnt = 0 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.1") - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - testKit.MustExec("set global tidb_auto_analyze_ratio = 0.0") - }() - - require.NoError(t, h.Update(is)) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tableInfo := tbl.Meta() - pi := tableInfo.GetPartitionInfo() - globalStats := h.GetTableStats(tableInfo) - partitionStats := h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, int64(6), globalStats.RealtimeCount) - require.Equal(t, int64(0), globalStats.ModifyCount) - require.Equal(t, int64(2), partitionStats.RealtimeCount) - require.Equal(t, int64(0), partitionStats.ModifyCount) - - testKit.MustExec("insert into t values (3, 'g')") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - globalStats = h.GetTableStats(tableInfo) - partitionStats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, int64(7), globalStats.RealtimeCount) - require.Equal(t, int64(1), globalStats.ModifyCount) - require.Equal(t, int64(3), partitionStats.RealtimeCount) - require.Equal(t, int64(1), partitionStats.ModifyCount) - - h.HandleAutoAnalyze(is) - require.NoError(t, h.Update(is)) - globalStats = h.GetTableStats(tableInfo) - partitionStats = h.GetPartitionStats(tableInfo, pi.Definitions[0].ID) - require.Equal(t, int64(7), globalStats.RealtimeCount) - require.Equal(t, int64(0), globalStats.ModifyCount) - require.Equal(t, int64(3), partitionStats.RealtimeCount) - require.Equal(t, int64(0), partitionStats.ModifyCount) - }) -} - -func TestAutoAnalyzeRatio(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - is := dom.InfoSchema() - require.NoError(t, h.Update(is)) - // To pass the stats.Pseudo check in autoAnalyzeTable - tk.MustExec("analyze table t") - tk.MustExec("explain select * from t where a = 1") - require.NoError(t, h.LoadNeededHistograms()) - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - - getStatsHealthy := func() int { - rows := tk.MustQuery("show stats_healthy where db_name = 'test' and table_name = 't'").Rows() - require.Len(t, rows, 1) - healthy, err := strconv.Atoi(rows[0][3].(string)) - require.NoError(t, err) - return healthy - } - - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 10)) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 44) - require.True(t, h.HandleAutoAnalyze(is)) - - tk.MustExec("delete from t limit 12") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 61) - require.False(t, h.HandleAutoAnalyze(is)) - - tk.MustExec("delete from t limit 4") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 48) - require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) -} - -func TestDumpColumnStatsUsage(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("create table t2(a int, b int)") - tk.MustExec("create table t3(a int, b int) partition by range(a) (partition p0 values less than (10), partition p1 values less than maxvalue)") - tk.MustExec("insert into t1 values (1, 2), (3, 4)") - tk.MustExec("insert into t2 values (5, 6), (7, 8)") - tk.MustExec("insert into t3 values (1, 2), (3, 4), (11, 12), (13, 14)") - tk.MustExec("select * from t1 where a > 1") - tk.MustExec("select * from t2 where b < 10") - require.NoError(t, h.DumpColStatsUsageToKV()) - // t1.a is collected as predicate column - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't2'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t2", "", "b"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - - tk.MustExec("analyze table t1") - tk.MustExec("select * from t1 where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - // t1.a updates last_used_at first and then updates last_analyzed_at while t1.b updates last_analyzed_at first and then updates last_used_at. - // Check both of them behave as expected. - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() - require.Len(t, rows, 2) - require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) != "") - require.Equal(t, []interface{}{"test", "t1", "", "b"}, rows[1][:4]) - require.True(t, rows[1][4].(string) != "") - require.True(t, rows[1][5].(string) != "") - - // Test partition table. - // No matter whether it is static or dynamic pruning mode, we record predicate columns using table ID rather than partition ID. - for _, val := range []string{string(variable.Static), string(variable.Dynamic)} { - tk.MustExec(fmt.Sprintf("set @@tidb_partition_prune_mode = '%v'", val)) - tk.MustExec("delete from mysql.column_stats_usage") - tk.MustExec("select * from t3 where a < 5") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't3'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t3", "global", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - } - - // Test non-correlated subquery. - // Non-correlated subquery will be executed during the plan building phase, which cannot be done by mock in (*testPlanSuite).TestCollectPredicateColumns. - // Hence we put the test of collecting predicate columns for non-correlated subquery here. - tk.MustExec("delete from mysql.column_stats_usage") - tk.MustExec("select * from t2 where t2.a > (select count(*) from t1 where t1.b > 1)") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t1", "", "b"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't2'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t2", "", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") -} - -func TestCollectPredicateColumnsFromExecute(t *testing.T) { - for _, val := range []bool{false, true} { - func(planCache bool) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set tidb_enable_prepared_plan_cache=" + variable.BoolToOnOff(planCache)) - - originalVal2 := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal2)) - }() - tk.MustExec("set global tidb_enable_column_tracking = 1") - - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t1(a int, b int)") - tk.MustExec("prepare stmt from 'select * from t1 where a > ?'") - require.NoError(t, h.DumpColStatsUsageToKV()) - // Prepare only converts sql string to ast and doesn't do optimization, so no predicate column is collected. - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Check(testkit.Rows()) - tk.MustExec("set @p1 = 1") - tk.MustExec("execute stmt using @p1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - - tk.MustExec("delete from mysql.column_stats_usage") - tk.MustExec("set @p2 = 2") - tk.MustExec("execute stmt using @p2") - if planCache { - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("1")) - require.NoError(t, h.DumpColStatsUsageToKV()) - // If the second execution uses the cached plan, no predicate column is collected. - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Check(testkit.Rows()) - } else { - tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) - require.NoError(t, h.DumpColStatsUsageToKV()) - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't1'").Rows() - require.Len(t, rows, 1) - require.Equal(t, []interface{}{"test", "t1", "", "a"}, rows[0][:4]) - require.True(t, rows[0][4].(string) != "") - require.True(t, rows[0][5].(string) == "") - } - }(val) - } -} - -func TestEnableAndDisableColumnTracking(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t (a int, b int, c int)") - - originalVal := tk.MustQuery("select @@tidb_enable_column_tracking").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_enable_column_tracking = %v", originalVal)) - }() - - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows := tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Rows() - require.Len(t, rows, 1) - require.Equal(t, "b", rows[0][3]) - - tk.MustExec("set global tidb_enable_column_tracking = 0") - // After tidb_enable_column_tracking is set to 0, the predicate columns collected before are invalidated. - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) - - // Sleep for 1.5s to let `last_used_at` be larger than `tidb_disable_tracking_time`. - time.Sleep(1500 * time.Millisecond) - tk.MustExec("select * from t where a > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - // We don't collect predicate columns when tidb_enable_column_tracking = 0 - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) - - tk.MustExec("set global tidb_enable_column_tracking = 1") - tk.MustExec("select * from t where b < 1 and c > 1") - require.NoError(t, h.DumpColStatsUsageToKV()) - rows = tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Sort().Rows() - require.Len(t, rows, 2) - require.Equal(t, "b", rows[0][3]) - require.Equal(t, "c", rows[1][3]) - - // Test invalidating predicate columns again in order to check that tidb_disable_tracking_time can be updated. - tk.MustExec("set global tidb_enable_column_tracking = 0") - tk.MustQuery("show column_stats_usage where db_name = 'test' and table_name = 't' and last_used_at is not null").Check(testkit.Rows()) -} - -func TestStatsLockUnlockForAutoAnalyze(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - is := dom.InfoSchema() - require.NoError(t, h.Update(is)) - // To pass the stats.Pseudo check in autoAnalyzeTable - tk.MustExec("analyze table t") - tk.MustExec("explain select * from t where a = 1") - require.NoError(t, h.LoadNeededHistograms()) - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 10)) - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.True(t, h.HandleAutoAnalyze(is)) - - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.Nil(t, err) - - tblStats := h.GetTableStats(tbl.Meta()) - for _, col := range tblStats.Columns { - require.True(t, col.IsStatsInitialized()) - } - - tk.MustExec("lock stats t") - - tk.MustExec("delete from t limit 12") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.False(t, h.HandleAutoAnalyze(is)) - - tblStats1 := h.GetTableStats(tbl.Meta()) - require.Equal(t, tblStats, tblStats1) - - tk.MustExec("unlock stats t") - - tk.MustExec("delete from t limit 4") - - rows := tk.MustQuery("select count(*) from t").Rows() - num, _ := strconv.Atoi(rows[0][0].(string)) - require.Equal(t, num, 15) - - tk.MustExec("analyze table t") - - tblStats2 := h.GetTableStats(tbl.Meta()) - require.Equal(t, int64(15), tblStats2.RealtimeCount) -} - -func TestStatsLockForDelta(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - testKit := testkit.NewTestKit(t, store) - testKit.MustExec("use test") - testKit.MustExec("set @@session.tidb_analyze_version = 1") - testKit.MustExec("create table t1 (c1 int, c2 int)") - testKit.MustExec("create table t2 (c1 int, c2 int)") - - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tableInfo1 := tbl1.Meta() - h := dom.StatsHandle() - - testKit.MustExec("lock stats t1") - - rowCount1 := 10 - rowCount2 := 20 - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - for i := 0; i < rowCount2; i++ { - testKit.MustExec("insert into t2 values(1, 2)") - } - - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - err = h.HandleDDLEvent(<-h.DDLEventCh()) - require.NoError(t, err) - - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 := h.GetTableStats(tableInfo1) - require.Equal(t, stats1.RealtimeCount, int64(0)) - - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tableInfo2 := tbl2.Meta() - stats2 := h.GetTableStats(tableInfo2) - require.Equal(t, int64(rowCount2), stats2.RealtimeCount) - - testKit.MustExec("analyze table t1") - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, stats1.RealtimeCount, int64(0)) - - testKit.MustExec("unlock stats t1") - - testKit.MustExec("analyze table t1") - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(20), stats1.RealtimeCount) - - for i := 0; i < rowCount1; i++ { - testKit.MustExec("insert into t1 values(1, 2)") - } - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - stats1 = h.GetTableStats(tableInfo1) - require.Equal(t, int64(30), stats1.RealtimeCount) -} - -func TestFillMissingStatsMeta(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a int, b int)") - tk.MustExec("create table t2 (a int, b int) partition by range (a) (partition p0 values less than (10), partition p1 values less than (maxvalue))") - - tk.MustQuery("select * from mysql.stats_meta").Check(testkit.Rows()) - - is := dom.InfoSchema() - tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tbl1ID := tbl1.Meta().ID - tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tbl2Info := tbl2.Meta() - tbl2ID := tbl2Info.ID - require.Len(t, tbl2Info.Partition.Definitions, 2) - p0ID := tbl2Info.Partition.Definitions[0].ID - p1ID := tbl2Info.Partition.Definitions[1].ID - h := dom.StatsHandle() - - checkStatsMeta := func(id int64, expectedModifyCount, expectedCount string) int64 { - rows := tk.MustQuery(fmt.Sprintf("select version, modify_count, count from mysql.stats_meta where table_id = %v", id)).Rows() - require.Len(t, rows, 1) - ver, err := strconv.ParseInt(rows[0][0].(string), 10, 64) - require.NoError(t, err) - require.Equal(t, expectedModifyCount, rows[0][1]) - require.Equal(t, expectedCount, rows[0][2]) - return ver - } - - tk.MustExec("insert into t1 values (1, 2), (3, 4)") - require.NoError(t, h.DumpStatsDeltaToKV(false)) - require.NoError(t, h.Update(is)) - ver1 := checkStatsMeta(tbl1ID, "2", "2") - tk.MustExec("delete from t1 where a = 1") - require.NoError(t, h.DumpStatsDeltaToKV(false)) - require.NoError(t, h.Update(is)) - ver2 := checkStatsMeta(tbl1ID, "3", "1") - require.Greater(t, ver2, ver1) - - tk.MustExec("insert into t2 values (1, 2), (3, 4)") - require.NoError(t, h.DumpStatsDeltaToKV(false)) - require.NoError(t, h.Update(is)) - checkStatsMeta(p0ID, "2", "2") - globalVer1 := checkStatsMeta(tbl2ID, "2", "2") - tk.MustExec("insert into t2 values (11, 12)") - require.NoError(t, h.DumpStatsDeltaToKV(false)) - require.NoError(t, h.Update(is)) - checkStatsMeta(p1ID, "1", "1") - globalVer2 := checkStatsMeta(tbl2ID, "3", "3") - require.Greater(t, globalVer2, globalVer1) -} - -func TestNotDumpSysTable(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t1 (a int, b int)") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustQuery("select count(1) from mysql.stats_meta").Check(testkit.Rows("1")) - // After executing `delete from mysql.stats_meta`, a delta for mysql.stats_meta is created but it would not be dumped. - tk.MustExec("delete from mysql.stats_meta") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("mysql"), model.NewCIStr("stats_meta")) - require.NoError(t, err) - tblID := tbl.Meta().ID - tk.MustQuery(fmt.Sprintf("select * from mysql.stats_meta where table_id = %v", tblID)).Check(testkit.Rows()) -} - -func TestAutoAnalyzePartitionTableAfterAddingIndex(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - oriMinCnt := autoanalyze.AutoAnalyzeMinCnt - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - defer func() { - autoanalyze.AutoAnalyzeMinCnt = oriMinCnt - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - autoanalyze.AutoAnalyzeMinCnt = 0 - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - tk.MustExec("set global tidb_analyze_version = 2") - tk.MustExec("set global tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("use test") - tk.MustExec("create table t (a int, b int) partition by range (a) (PARTITION p0 VALUES LESS THAN (10), PARTITION p1 VALUES LESS THAN MAXVALUE)") - h := dom.StatsHandle() - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1,2), (3,4), (11,12),(13,14)") - tk.MustExec("set session tidb_analyze_version = 2") - tk.MustExec("set session tidb_partition_prune_mode = 'dynamic'") - tk.MustExec("analyze table t") - require.False(t, h.HandleAutoAnalyze(dom.InfoSchema())) - tk.MustExec("alter table t add index idx(a)") - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - idxInfo := tblInfo.Indices[0] - require.Nil(t, h.GetTableStats(tblInfo).Indices[idxInfo.ID]) - require.True(t, h.HandleAutoAnalyze(dom.InfoSchema())) - require.NotNil(t, h.GetTableStats(tblInfo).Indices[idxInfo.ID]) -} diff --git a/statistics/handle/usage/BUILD.bazel b/statistics/handle/usage/BUILD.bazel deleted file mode 100644 index 1a0a4bc84b691..0000000000000 --- a/statistics/handle/usage/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "usage", - srcs = [ - "index_usage.go", - "predicate_column.go", - "session_stats_collect.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/usage", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//statistics/handle/storage", - "//statistics/handle/util", - "//types", - "//util", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "usage_test", - timeout = "short", - srcs = ["session_stats_collect_test.go"], - embed = [":usage"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/statistics/handle/util/BUILD.bazel b/statistics/handle/util/BUILD.bazel deleted file mode 100644 index f2e338ea6804a..0000000000000 --- a/statistics/handle/util/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "util", - srcs = [ - "interfaces.go", - "table_info.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/statistics/handle/util", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//parser/ast", - "//parser/model", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//statistics", - "//table", - "//types", - "//util/chunk", - "//util/intest", - "//util/sqlexec", - "//util/sqlexec/mock", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//oracle", - ], -) diff --git a/statistics/handle/util/util.go b/statistics/handle/util/util.go deleted file mode 100644 index 0fa0a7d95386c..0000000000000 --- a/statistics/handle/util/util.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package util - -import ( - "context" - "strconv" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/sqlexec/mock" - "github.com/tikv/client-go/v2/oracle" -) - -var ( - // UseCurrentSessionOpt to make sure the sql is executed in current session. - UseCurrentSessionOpt = []sqlexec.OptionFuncAlias{sqlexec.ExecOptionUseCurSession} - - // StatsCtx is used to mark the request is from stats module. - StatsCtx = kv.WithInternalSourceType(context.Background(), kv.InternalTxnStats) -) - -// SessionPool is used to recycle sessionctx. -type SessionPool interface { - Get() (pools.Resource, error) - Put(pools.Resource) -} - -// FinishTransaction will execute `commit` when error is nil, otherwise `rollback`. -func FinishTransaction(sctx sessionctx.Context, err error) error { - if err == nil { - _, _, err = ExecRows(sctx, "commit") - } else { - _, _, err1 := ExecRows(sctx, "rollback") - terror.Log(errors.Trace(err1)) - } - return errors.Trace(err) -} - -var ( - // FlagWrapTxn indicates whether to wrap a transaction. - FlagWrapTxn = 0 -) - -// CallWithSCtx allocates a sctx from the pool and call the f(). -func CallWithSCtx(pool SessionPool, f func(sctx sessionctx.Context) error, flags ...int) (err error) { - se, err := pool.Get() - if err != nil { - return err - } - defer func() { - if err == nil { // only recycle when no error - pool.Put(se) - } - }() - sctx := se.(sessionctx.Context) - if err := UpdateSCtxVarsForStats(sctx); err != nil { // update stats variables automatically - return err - } - - wrapTxn := false - for _, flag := range flags { - if flag == FlagWrapTxn { - wrapTxn = true - } - } - if wrapTxn { - err = WrapTxn(sctx, f) - } else { - err = f(sctx) - } - return err -} - -// UpdateSCtxVarsForStats updates all necessary variables that may affect the behavior of statistics. -func UpdateSCtxVarsForStats(sctx sessionctx.Context) error { - // analyzer version - verInString, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBAnalyzeVersion) - if err != nil { - return err - } - ver, err := strconv.ParseInt(verInString, 10, 64) - if err != nil { - return err - } - sctx.GetSessionVars().AnalyzeVersion = int(ver) - - // enable historical stats - val, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableHistoricalStats) - if err != nil { - return err - } - sctx.GetSessionVars().EnableHistoricalStats = variable.TiDBOptOn(val) - - // partition mode - pruneMode, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBPartitionPruneMode) - if err != nil { - return err - } - sctx.GetSessionVars().PartitionPruneMode.Store(pruneMode) - - // enable analyze snapshot - analyzeSnapshot, err := sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableAnalyzeSnapshot) - if err != nil { - return err - } - sctx.GetSessionVars().EnableAnalyzeSnapshot = variable.TiDBOptOn(analyzeSnapshot) - - // enable skip column types - val, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBAnalyzeSkipColumnTypes) - if err != nil { - return err - } - sctx.GetSessionVars().AnalyzeSkipColumnTypes = variable.ParseAnalyzeSkipColumnTypes(val) - - // skip missing partition stats - val, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBSkipMissingPartitionStats) - if err != nil { - return err - } - sctx.GetSessionVars().SkipMissingPartitionStats = variable.TiDBOptOn(val) - verInString, err = sctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBMergePartitionStatsConcurrency) - if err != nil { - return err - } - ver, err = strconv.ParseInt(verInString, 10, 64) - if err != nil { - return err - } - sctx.GetSessionVars().AnalyzePartitionMergeConcurrency = int(ver) - return nil -} - -// WrapTxn uses a transaction here can let different SQLs in this operation have the same data visibility. -func WrapTxn(sctx sessionctx.Context, f func(sctx sessionctx.Context) error) (err error) { - // TODO: check whether this sctx is already in a txn - if _, _, err := ExecRows(sctx, "begin"); err != nil { - return err - } - defer func() { - err = FinishTransaction(sctx, err) - }() - err = f(sctx) - return -} - -// GetStartTS gets the start ts from current transaction. -func GetStartTS(sctx sessionctx.Context) (uint64, error) { - txn, err := sctx.Txn(true) - if err != nil { - return 0, err - } - return txn.StartTS(), nil -} - -// Exec is a helper function to execute sql and return RecordSet. -func Exec(sctx sessionctx.Context, sql string, args ...interface{}) (sqlexec.RecordSet, error) { - sqlExec, ok := sctx.(sqlexec.SQLExecutor) - if !ok { - return nil, errors.Errorf("invalid sql executor") - } - // TODO: use RestrictedSQLExecutor + ExecOptionUseCurSession instead of SQLExecutor - return sqlExec.ExecuteInternal(StatsCtx, sql, args...) -} - -// ExecRows is a helper function to execute sql and return rows and fields. -func ExecRows(sctx sessionctx.Context, sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, err error) { - if intest.InTest { - if v := sctx.Value(mock.MockRestrictedSQLExecutorKey{}); v != nil { - return v.(*mock.MockRestrictedSQLExecutor).ExecRestrictedSQL(StatsCtx, - UseCurrentSessionOpt, sql, args...) - } - } - - sqlExec, ok := sctx.(sqlexec.RestrictedSQLExecutor) - if !ok { - return nil, nil, errors.Errorf("invalid sql executor") - } - return sqlExec.ExecRestrictedSQL(StatsCtx, UseCurrentSessionOpt, sql, args...) -} - -// ExecWithOpts is a helper function to execute sql and return rows and fields. -func ExecWithOpts(sctx sessionctx.Context, opts []sqlexec.OptionFuncAlias, sql string, args ...interface{}) (rows []chunk.Row, fields []*ast.ResultField, err error) { - sqlExec, ok := sctx.(sqlexec.RestrictedSQLExecutor) - if !ok { - return nil, nil, errors.Errorf("invalid sql executor") - } - return sqlExec.ExecRestrictedSQL(StatsCtx, opts, sql, args...) -} - -// DurationToTS converts duration to timestamp. -func DurationToTS(d time.Duration) uint64 { - return oracle.ComposeTS(d.Nanoseconds()/int64(time.Millisecond), 0) -} diff --git a/statistics/index.go b/statistics/index.go deleted file mode 100644 index bd4accc4a20d5..0000000000000 --- a/statistics/index.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/planner/util/debugtrace" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/twmb/murmur3" -) - -// Index represents an index histogram. -type Index struct { - LastAnalyzePos types.Datum - CMSketch *CMSketch - TopN *TopN - FMSketch *FMSketch - Info *model.IndexInfo - Histogram - StatsLoadedStatus - StatsVer int64 // StatsVer is the version of the current stats, used to maintain compatibility - Flag int64 - // PhysicalID is the physical table id, - // or it could possibly be -1, which means "stats not available". - // The -1 case could happen in a pseudo stats table, and in this case, this stats should not trigger stats loading. - PhysicalID int64 -} - -// Copy copies the index. -func (idx *Index) Copy() *Index { - if idx == nil { - return nil - } - nc := &Index{ - PhysicalID: idx.PhysicalID, - Flag: idx.Flag, - StatsVer: idx.StatsVer, - } - idx.LastAnalyzePos.Copy(&nc.LastAnalyzePos) - if idx.CMSketch != nil { - nc.CMSketch = idx.CMSketch.Copy() - } - if idx.TopN != nil { - nc.TopN = idx.TopN.Copy() - } - if idx.FMSketch != nil { - nc.FMSketch = idx.FMSketch.Copy() - } - if idx.Info != nil { - nc.Info = idx.Info.Clone() - } - nc.Histogram = *idx.Histogram.Copy() - nc.StatsLoadedStatus = idx.StatsLoadedStatus.Copy() - return nc -} - -// ItemID implements TableCacheItem -func (idx *Index) ItemID() int64 { - return idx.Info.ID -} - -// IsAllEvicted indicates whether all stats evicted -func (idx *Index) IsAllEvicted() bool { - return idx.statsInitialized && idx.evictedStatus >= AllEvicted -} - -// GetEvictedStatus returns the evicted status -func (idx *Index) GetEvictedStatus() int { - return idx.evictedStatus -} - -// DropUnnecessaryData drops unnecessary data for index. -func (idx *Index) DropUnnecessaryData() { - if idx.GetStatsVer() < Version2 { - idx.CMSketch = nil - } - idx.TopN = nil - idx.Histogram.Bounds = chunk.NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeBlob)}, 0) - idx.Histogram.Buckets = make([]Bucket, 0) - idx.Histogram.Scalars = make([]scalar, 0) - idx.evictedStatus = AllEvicted -} - -func (idx *Index) isStatsInitialized() bool { - return idx.statsInitialized -} - -// GetStatsVer returns the version of the current stats -func (idx *Index) GetStatsVer() int64 { - return idx.StatsVer -} - -// IsCMSExist returns whether CMSketch exists. -func (idx *Index) IsCMSExist() bool { - return idx.CMSketch != nil -} - -// IsEvicted returns whether index statistics got evicted -func (idx *Index) IsEvicted() bool { - return idx.evictedStatus != AllLoaded -} - -func (idx *Index) String() string { - return idx.Histogram.ToString(len(idx.Info.Columns)) -} - -// TotalRowCount returns the total count of this index. -func (idx *Index) TotalRowCount() float64 { - if idx.StatsVer >= Version2 { - return idx.Histogram.TotalRowCount() + float64(idx.TopN.TotalCount()) - } - return idx.Histogram.TotalRowCount() -} - -// IsInvalid checks if this index is invalid. -func (idx *Index) IsInvalid(sctx sessionctx.Context, collPseudo bool) (res bool) { - idx.CheckStats() - var totalCount float64 - if sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(sctx) - defer func() { - debugtrace.RecordAnyValuesWithNames(sctx, - "IsInvalid", res, - "CollPseudo", collPseudo, - "TotalCount", totalCount, - ) - debugtrace.LeaveContextCommon(sctx) - }() - } - totalCount = idx.TotalRowCount() - return (collPseudo) || totalCount == 0 -} - -// EvictAllStats evicts all stats -// Note that this function is only used for test -func (idx *Index) EvictAllStats() { - idx.Histogram.Buckets = nil - idx.CMSketch = nil - idx.TopN = nil - idx.StatsLoadedStatus.evictedStatus = AllEvicted -} - -// MemoryUsage returns the total memory usage of a Histogram and CMSketch in Index. -// We ignore the size of other metadata in Index. -func (idx *Index) MemoryUsage() CacheItemMemoryUsage { - var sum int64 - indexMemUsage := &IndexMemUsage{ - IndexID: idx.Info.ID, - } - histMemUsage := idx.Histogram.MemoryUsage() - indexMemUsage.HistogramMemUsage = histMemUsage - sum = histMemUsage - if idx.CMSketch != nil { - cmSketchMemUsage := idx.CMSketch.MemoryUsage() - indexMemUsage.CMSketchMemUsage = cmSketchMemUsage - sum += cmSketchMemUsage - } - if idx.TopN != nil { - topnMemUsage := idx.TopN.MemoryUsage() - indexMemUsage.TopNMemUsage = topnMemUsage - sum += topnMemUsage - } - indexMemUsage.TotalMemUsage = sum - return indexMemUsage -} - -// QueryBytes is used to query the count of specified bytes. -// The input sctx is just for debug trace, you can pass nil safely if that's not needed. -func (idx *Index) QueryBytes(sctx sessionctx.Context, d []byte) (result uint64) { - if sctx != nil && sctx.GetSessionVars().StmtCtx.EnableOptimizerDebugTrace { - debugtrace.EnterContextCommon(sctx) - defer func() { - debugtrace.RecordAnyValuesWithNames(sctx, "Result", result) - debugtrace.LeaveContextCommon(sctx) - }() - } - h1, h2 := murmur3.Sum128(d) - if idx.TopN != nil { - if count, ok := idx.TopN.QueryTopN(sctx, d); ok { - return count - } - } - if idx.CMSketch != nil { - return idx.CMSketch.queryHashValue(sctx, h1, h2) - } - v, _ := idx.Histogram.EqualRowCount(sctx, types.NewBytesDatum(d), idx.StatsVer >= Version2) - return uint64(v) -} - -// CheckStats will check if the index stats need to be updated. -func (idx *Index) CheckStats() { - // When we are using stats from PseudoTable(), all column/index ID will be -1. - if idx.IsFullLoad() || idx.PhysicalID <= 0 { - return - } - HistogramNeededItems.insert(model.TableItemID{TableID: idx.PhysicalID, ID: idx.Info.ID, IsIndex: true}) -} - -// GetIncreaseFactor get the increase factor to adjust the final estimated count when the table is modified. -func (idx *Index) GetIncreaseFactor(realtimeRowCount int64) float64 { - columnCount := idx.TotalRowCount() - if columnCount == 0 { - return 1.0 - } - return float64(realtimeRowCount) / columnCount -} - -// IsAnalyzed indicates whether the index is analyzed. -func (idx *Index) IsAnalyzed() bool { - return idx.StatsVer != Version0 -} diff --git a/statistics/integration_test.go b/statistics/integration_test.go deleted file mode 100644 index 7270a0941538b..0000000000000 --- a/statistics/integration_test.go +++ /dev/null @@ -1,483 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package statistics_test - -import ( - "fmt" - "math" - "strconv" - "strings" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/statistics/handle/autoanalyze" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/stretchr/testify/require" -) - -func TestChangeVerTo2Behavior(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - }() - tk.MustExec("set global tidb_persist_analyze_options=false") - - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("insert into t values(1, 1), (1, 2), (1, 3)") - tk.MustExec("analyze table t") - is := dom.InfoSchema() - tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - h := dom.StatsHandle() - require.NoError(t, h.Update(is)) - statsTblT := h.GetTableStats(tblT.Meta()) - // Analyze table with version 1 success, all statistics are version 1. - for _, col := range statsTblT.Columns { - require.Equal(t, int64(1), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("analyze table t index idx") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("analyze table t index") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("analyze table t ") - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, col := range statsTblT.Columns { - require.Equal(t, int64(2), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("analyze table t index idx") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", - "Warning 1105 The version 2 would collect all statistics not only the selected indexes")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("analyze table t index") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", - "Warning 1105 The version 2 would collect all statistics not only the selected indexes")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("analyze table t ") - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, col := range statsTblT.Columns { - require.Equal(t, int64(1), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } -} - -func TestChangeVerTo2BehaviorWithPersistedOptions(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - originalVal1 := tk.MustQuery("select @@tidb_persist_analyze_options").Rows()[0][0].(string) - defer func() { - tk.MustExec(fmt.Sprintf("set global tidb_persist_analyze_options = %v", originalVal1)) - }() - tk.MustExec("set global tidb_persist_analyze_options=true") - - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index idx(a))") - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("insert into t values(1, 1), (1, 2), (1, 3)") - tk.MustExec("analyze table t") - is := dom.InfoSchema() - tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - h := dom.StatsHandle() - require.NoError(t, h.Update(is)) - statsTblT := h.GetTableStats(tblT.Meta()) - // Analyze table with version 1 success, all statistics are version 1. - for _, col := range statsTblT.Columns { - require.Equal(t, int64(1), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("set @@session.tidb_analyze_version = 2") - tk.MustExec("analyze table t index idx") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("analyze table t index") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } - tk.MustExec("analyze table t ") - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, col := range statsTblT.Columns { - require.Equal(t, int64(2), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("set @@session.tidb_analyze_version = 1") - tk.MustExec("analyze table t index idx") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", - "Warning 1105 The version 2 would collect all statistics not only the selected indexes", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) // since fallback to ver2 path, should do samplerate adjustment - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("analyze table t index") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 The analyze version from the session is not compatible with the existing statistics of the table. Use the existing version instead", - "Warning 1105 The version 2 would collect all statistics not only the selected indexes", - "Note 1105 Analyze use auto adjusted sample rate 1.000000 for table test.t, reason to use this rate is \"use min(1, 110000/3) as the sample-rate=1\"")) - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(2), idx.GetStatsVer()) - } - tk.MustExec("analyze table t ") - require.NoError(t, h.Update(is)) - statsTblT = h.GetTableStats(tblT.Meta()) - for _, col := range statsTblT.Columns { - require.Equal(t, int64(1), col.GetStatsVer()) - } - for _, idx := range statsTblT.Indices { - require.Equal(t, int64(1), idx.GetStatsVer()) - } -} - -func TestExpBackoffEstimation(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec(`set @@tidb_enable_non_prepared_plan_cache=0`) // estRows won't be updated if hit cache. - tk.MustExec("set tidb_cost_model_version=2") - tk.MustExec("create table exp_backoff(a int, b int, c int, d int, index idx(a, b, c, d))") - tk.MustExec("insert into exp_backoff values(1, 1, 1, 1), (1, 1, 1, 2), (1, 1, 2, 3), (1, 2, 2, 4), (1, 2, 3, 5)") - tk.MustExec("set @@session.tidb_analyze_version=2") - tk.MustExec("analyze table exp_backoff") - var ( - input []string - output [][]string - ) - integrationSuiteData := statistics.GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - inputLen := len(input) - // The test cases are: - // Query a = 1, b = 1, c = 1, d >= 3 and d <= 5 separately. We got 5, 3, 2, 3. - // And then query and a = 1 and b = 1 and c = 1 and d >= 3 and d <= 5. It's result should follow the exp backoff, - // which is 2/5 * (3/5)^{1/2} * (3/5)*{1/4} * 1^{1/8} * 5 = 1.3634. - for i := 0; i < inputLen-1; i++ { - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(input[i]).Rows()) - }) - tk.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) - } - - // The last case is that no column is loaded and we get no stats at all. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/planner/cardinality/cleanEstResults", `return(true)`)) - testdata.OnRecord(func() { - output[inputLen-1] = testdata.ConvertRowsToStrings(tk.MustQuery(input[inputLen-1]).Rows()) - }) - tk.MustQuery(input[inputLen-1]).Check(testkit.Rows(output[inputLen-1]...)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/planner/cardinality/cleanEstResults")) -} - -func TestNULLOnFullSampling(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("set @@session.tidb_analyze_version = 2;") - tk.MustExec("create table t(a int, index idx(a))") - tk.MustExec("insert into t values(1), (1), (1), (2), (2), (3), (4), (null), (null), (null)") - var ( - input []string - output [][]string - ) - tk.MustExec("analyze table t with 2 topn") - is := dom.InfoSchema() - tblT, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - h := dom.StatsHandle() - require.NoError(t, h.Update(is)) - statsTblT := h.GetTableStats(tblT.Meta()) - // Check the null count is 3. - for _, col := range statsTblT.Columns { - require.Equal(t, int64(3), col.NullCount) - } - integrationSuiteData := statistics.GetIntegrationSuiteData() - integrationSuiteData.LoadTestCases(t, &input, &output) - // Check the topn and buckets contains no null values. - for i := 0; i < len(input); i++ { - testdata.OnRecord(func() { - output[i] = testdata.ConvertRowsToStrings(tk.MustQuery(input[i]).Rows()) - }) - tk.MustQuery(input[i]).Check(testkit.Rows(output[i]...)) - } -} - -func TestAnalyzeSnapshot(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("set @@session.tidb_analyze_version = 2;") - tk.MustExec("create table t(a int, index(a))") - tk.MustExec("insert into t values(1), (1), (1)") - tk.MustExec("analyze table t") - rows := tk.MustQuery("select count, snapshot, version from mysql.stats_meta").Rows() - require.Len(t, rows, 1) - require.Equal(t, "3", rows[0][0]) - s1Str := rows[0][1].(string) - s1, err := strconv.ParseUint(s1Str, 10, 64) - require.NoError(t, err) - require.True(t, s1 < math.MaxUint64) - - // TestHistogramsWithSameTxnTS - v1 := rows[0][2].(string) - rows = tk.MustQuery("select version from mysql.stats_histograms").Rows() - require.Len(t, rows, 2) - v2 := rows[0][0].(string) - require.Equal(t, v1, v2) - v3 := rows[1][0].(string) - require.Equal(t, v2, v3) - - tk.MustExec("insert into t values(1), (1), (1)") - tk.MustExec("analyze table t") - rows = tk.MustQuery("select count, snapshot from mysql.stats_meta").Rows() - require.Len(t, rows, 1) - require.Equal(t, "6", rows[0][0]) - s2Str := rows[0][1].(string) - s2, err := strconv.ParseUint(s2Str, 10, 64) - require.NoError(t, err) - require.True(t, s2 < math.MaxUint64) - require.True(t, s2 > s1) -} - -func TestOutdatedStatsCheck(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - oriStart := tk.MustQuery("select @@tidb_auto_analyze_start_time").Rows()[0][0].(string) - oriEnd := tk.MustQuery("select @@tidb_auto_analyze_end_time").Rows()[0][0].(string) - autoanalyze.AutoAnalyzeMinCnt = 0 - defer func() { - autoanalyze.AutoAnalyzeMinCnt = 1000 - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_start_time='%v'", oriStart)) - tk.MustExec(fmt.Sprintf("set global tidb_auto_analyze_end_time='%v'", oriEnd)) - }() - tk.MustExec("set global tidb_auto_analyze_start_time='00:00 +0000'") - tk.MustExec("set global tidb_auto_analyze_end_time='23:59 +0000'") - tk.MustExec("set session tidb_enable_pseudo_for_outdated_stats=1") - - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t (a int)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 19)) // 20 rows - require.NoError(t, h.DumpStatsDeltaToKV(true)) - is := dom.InfoSchema() - require.NoError(t, h.Update(is)) - // To pass the stats.Pseudo check in autoAnalyzeTable - tk.MustExec("analyze table t") - tk.MustExec("explain select * from t where a = 1") - require.NoError(t, h.LoadNeededHistograms()) - - getStatsHealthy := func() int { - rows := tk.MustQuery("show stats_healthy where db_name = 'test' and table_name = 't'").Rows() - require.Len(t, rows, 1) - healthy, err := strconv.Atoi(rows[0][3].(string)) - require.NoError(t, err) - return healthy - } - - tk.MustExec("insert into t values (1)" + strings.Repeat(", (1)", 13)) // 34 rows - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 30) - require.False(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) - tk.MustExec("insert into t values (1)") // 35 rows - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 25) - require.True(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) - - tk.MustExec("analyze table t") - - tk.MustExec("delete from t limit 24") // 11 rows - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 31) - require.False(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) - - tk.MustExec("delete from t limit 1") // 10 rows - require.NoError(t, h.DumpStatsDeltaToKV(true)) - require.NoError(t, h.Update(is)) - require.Equal(t, getStatsHealthy(), 28) - require.True(t, hasPseudoStats(tk.MustQuery("explain select * from t where a = 1").Rows())) -} - -func hasPseudoStats(rows [][]interface{}) bool { - for i := range rows { - if strings.Contains(rows[i][4].(string), "stats:pseudo") { - return true - } - } - return false -} - -func TestShowHistogramsLoadStatus(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - origLease := h.Lease() - h.SetLease(time.Second) - defer func() { h.SetLease(origLease) }() - tk.MustExec("use test") - tk.MustExec("create table t(a int primary key, b int, c int, index idx(b, c))") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1,2,3), (4,5,6)") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t") - require.NoError(t, h.Update(dom.InfoSchema())) - rows := tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Rows() - for _, row := range rows { - require.Equal(t, "allEvicted", row[10].(string)) - } -} - -func TestSingleColumnIndexNDV(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, c varchar(20), d varchar(20), index idx_a(a), index idx_b(b), index idx_c(c), index idx_d(d))") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t values (1, 1, 'xxx', 'zzz'), (2, 2, 'yyy', 'zzz'), (1, 3, null, 'zzz')") - for i := 0; i < 5; i++ { - tk.MustExec("insert into t select * from t") - } - tk.MustExec("analyze table t") - rows := tk.MustQuery("show stats_histograms where db_name = 'test' and table_name = 't'").Sort().Rows() - expectedResults := [][]string{ - {"a", "2", "0"}, {"b", "3", "0"}, {"c", "2", "32"}, {"d", "1", "0"}, - {"idx_a", "2", "0"}, {"idx_b", "3", "0"}, {"idx_c", "2", "32"}, {"idx_d", "1", "0"}, - } - for i, row := range rows { - require.Equal(t, expectedResults[i][0], row[3]) // column_name - require.Equal(t, expectedResults[i][1], row[6]) // distinct_count - require.Equal(t, expectedResults[i][2], row[7]) // null_count - } -} - -func TestColumnStatsLazyLoad(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - originLease := h.Lease() - defer h.SetLease(originLease) - // Set `Lease` to `Millisecond` to enable column stats lazy load. - h.SetLease(time.Millisecond) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int)") - tk.MustExec("insert into t values (1,2), (3,4), (5,6), (7,8)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("analyze table t") - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - c1 := tblInfo.Columns[0] - c2 := tblInfo.Columns[1] - require.True(t, h.GetTableStats(tblInfo).Columns[c1.ID].IsAllEvicted()) - require.True(t, h.GetTableStats(tblInfo).Columns[c2.ID].IsAllEvicted()) - tk.MustExec("analyze table t") - require.True(t, h.GetTableStats(tblInfo).Columns[c1.ID].IsAllEvicted()) - require.True(t, h.GetTableStats(tblInfo).Columns[c2.ID].IsAllEvicted()) -} - -func TestUpdateNotLoadIndexFMSketch(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - h := dom.StatsHandle() - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index idx(a)) partition by range (a) (partition p0 values less than (10),partition p1 values less than maxvalue)") - tk.MustExec("insert into t values (1,2), (3,4), (5,6), (7,8)") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("analyze table t") - is := dom.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tbl.Meta() - idxInfo := tblInfo.Indices[0] - p0 := tblInfo.Partition.Definitions[0] - p1 := tblInfo.Partition.Definitions[1] - require.Nil(t, h.GetPartitionStats(tblInfo, p0.ID).Indices[idxInfo.ID].FMSketch) - require.Nil(t, h.GetPartitionStats(tblInfo, p1.ID).Indices[idxInfo.ID].FMSketch) - h.Clear() - require.NoError(t, h.Update(is)) - require.Nil(t, h.GetPartitionStats(tblInfo, p0.ID).Indices[idxInfo.ID].FMSketch) - require.Nil(t, h.GetPartitionStats(tblInfo, p1.ID).Indices[idxInfo.ID].FMSketch) -} - -func TestIssue44369(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - h := dom.StatsHandle() - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int, b int, index iab(a,b));") - require.NoError(t, h.HandleDDLEvent(<-h.DDLEventCh())) - tk.MustExec("insert into t value(1,1);") - require.NoError(t, h.DumpStatsDeltaToKV(true)) - tk.MustExec("analyze table t;") - is := dom.InfoSchema() - require.NoError(t, h.Update(is)) - tk.MustExec("alter table t rename column b to bb;") - tk.MustExec("select * from t where a = 10 and bb > 20;") -} diff --git a/statistics/main_test.go b/statistics/main_test.go deleted file mode 100644 index 8d61b87ff0842..0000000000000 --- a/statistics/main_test.go +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "flag" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit/testdata" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" -) - -var testDataMap = make(testdata.BookKeeper, 3) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - if !flag.Parsed() { - flag.Parse() - } - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - testDataMap.LoadTestSuiteData("testdata", "integration_suite") - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - callback := func(i int) int { - testDataMap.GenerateOutputIfNeeded() - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} - -func GetIntegrationSuiteData() testdata.TestData { - return testDataMap["integration_suite"] -} - -// TestStatistics batches tests sharing a test suite to reduce the setups -// overheads. -func TestStatistics(t *testing.T) { - // fmsketch_test.go - t.Run("SubTestSketch", SubTestSketch()) - t.Run("SubTestSketchProtoConversion", SubTestSketchProtoConversion()) - t.Run("SubTestFMSketchCoding", SubTestFMSketchCoding()) - - // statistics_test.go - t.Run("SubTestColumnRange", SubTestColumnRange()) - t.Run("SubTestIntColumnRanges", SubTestIntColumnRanges()) - t.Run("SubTestIndexRanges", SubTestIndexRanges()) - - // statistics_serial_test.go - t.Run("SubTestBuild", SubTestBuild()) - t.Run("SubTestHistogramProtoConversion", SubTestHistogramProtoConversion()) -} - -func createTestStatisticsSamples(t *testing.T) *testStatisticsSamples { - s := new(testStatisticsSamples) - - s.count = 100000 - samples := make([]*SampleItem, 10000) - for i := 0; i < len(samples); i++ { - samples[i] = &SampleItem{} - } - start := 1000 - samples[0].Value.SetInt64(0) - for i := 1; i < start; i++ { - samples[i].Value.SetInt64(2) - } - for i := start; i < len(samples); i++ { - samples[i].Value.SetInt64(int64(i)) - } - for i := start; i < len(samples); i += 3 { - samples[i].Value.SetInt64(samples[i].Value.GetInt64() + 1) - } - for i := start; i < len(samples); i += 5 { - samples[i].Value.SetInt64(samples[i].Value.GetInt64() + 2) - } - sc := stmtctx.NewStmtCtx() - - var err error - s.samples, err = SortSampleItems(sc, samples) - require.NoError(t, err) - - rc := &recordSet{ - data: make([]types.Datum, s.count), - count: s.count, - cursor: 0, - } - rc.setFields(mysql.TypeLonglong) - rc.data[0].SetInt64(0) - for i := 1; i < start; i++ { - rc.data[i].SetInt64(2) - } - for i := start; i < rc.count; i++ { - rc.data[i].SetInt64(int64(i)) - } - for i := start; i < rc.count; i += 3 { - rc.data[i].SetInt64(rc.data[i].GetInt64() + 1) - } - for i := start; i < rc.count; i += 5 { - rc.data[i].SetInt64(rc.data[i].GetInt64() + 2) - } - require.NoError(t, types.SortDatums(sc, rc.data)) - - s.rc = rc - - pk := &recordSet{ - data: make([]types.Datum, s.count), - count: s.count, - cursor: 0, - } - pk.setFields(mysql.TypeLonglong) - for i := 0; i < rc.count; i++ { - pk.data[i].SetInt64(int64(i)) - } - s.pk = pk - - return s -} diff --git a/statistics/sample.go b/statistics/sample.go deleted file mode 100644 index 8b843614caa3d..0000000000000 --- a/statistics/sample.go +++ /dev/null @@ -1,318 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "context" - "slices" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/fastrand" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tipb/go-tipb" - "github.com/twmb/murmur3" -) - -// SampleItem is an item of sampled column value. -type SampleItem struct { - // Value is the sampled column value. - Value types.Datum - // Handle is the handle of the sample in its key. - // This property is used to calculate Ordinal in fast analyze. - Handle kv.Handle - // Ordinal is original position of this item in SampleCollector before sorting. This - // is used for computing correlation. - Ordinal int -} - -// EmptySampleItemSize is the size of empty SampleItem, 96 = 72 (datum) + 8 (int) + 16. -const EmptySampleItemSize = int64(unsafe.Sizeof(SampleItem{})) - -// CopySampleItems returns a deep copy of SampleItem slice. -func CopySampleItems(items []*SampleItem) []*SampleItem { - n := make([]*SampleItem, len(items)) - for i, item := range items { - ni := *item - n[i] = &ni - } - return n -} - -// SortSampleItems shallow copies and sorts a slice of SampleItem. -func SortSampleItems(sc *stmtctx.StatementContext, items []*SampleItem) ([]*SampleItem, error) { - sortedItems := make([]*SampleItem, len(items)) - copy(sortedItems, items) - var err error - slices.SortStableFunc(sortedItems, func(i, j *SampleItem) int { - var cmp int - cmp, err = i.Value.Compare(sc, &j.Value, collate.GetBinaryCollator()) - if err != nil { - return -1 - } - return cmp - }) - return sortedItems, err -} - -// SampleCollector will collect Samples and calculate the count and ndv of an attribute. -type SampleCollector struct { - FMSketch *FMSketch - CMSketch *CMSketch - TopN *TopN - Samples []*SampleItem - seenValues int64 // seenValues is the current seen values. - NullCount int64 - Count int64 // Count is the number of non-null rows. - MaxSampleSize int64 - TotalSize int64 // TotalSize is the total size of column. - MemSize int64 // major memory size of this sample collector. - IsMerger bool -} - -// MergeSampleCollector merges two sample collectors. -func (c *SampleCollector) MergeSampleCollector(sc *stmtctx.StatementContext, rc *SampleCollector) { - c.NullCount += rc.NullCount - c.Count += rc.Count - c.TotalSize += rc.TotalSize - c.FMSketch.MergeFMSketch(rc.FMSketch) - if rc.CMSketch != nil { - err := c.CMSketch.MergeCMSketch(rc.CMSketch) - terror.Log(errors.Trace(err)) - } - for _, item := range rc.Samples { - err := c.collect(sc, item.Value) - terror.Log(errors.Trace(err)) - } -} - -// SampleCollectorToProto converts SampleCollector to its protobuf representation. -func SampleCollectorToProto(c *SampleCollector) *tipb.SampleCollector { - collector := &tipb.SampleCollector{ - NullCount: c.NullCount, - Count: c.Count, - FmSketch: FMSketchToProto(c.FMSketch), - TotalSize: &c.TotalSize, - } - if c.CMSketch != nil { - collector.CmSketch = CMSketchToProto(c.CMSketch, nil) - } - for _, item := range c.Samples { - collector.Samples = append(collector.Samples, item.Value.GetBytes()) - } - return collector -} - -// MaxSampleValueLength defines the max length of the useful samples. If one sample value exceeds the max length, we drop it before building the stats. -const MaxSampleValueLength = mysql.MaxFieldVarCharLength / 2 - -// SampleCollectorFromProto converts SampleCollector from its protobuf representation. -func SampleCollectorFromProto(collector *tipb.SampleCollector) *SampleCollector { - s := &SampleCollector{ - NullCount: collector.NullCount, - Count: collector.Count, - FMSketch: FMSketchFromProto(collector.FmSketch), - } - if collector.TotalSize != nil { - s.TotalSize = *collector.TotalSize - } - s.CMSketch, s.TopN = CMSketchAndTopNFromProto(collector.CmSketch) - for _, val := range collector.Samples { - // When store the histogram bucket boundaries to kv, we need to limit the length of the value. - if len(val) <= MaxSampleValueLength { - item := &SampleItem{Value: types.NewBytesDatum(val)} - s.Samples = append(s.Samples, item) - } - } - return s -} - -func (c *SampleCollector) collect(sc *stmtctx.StatementContext, d types.Datum) error { - if !c.IsMerger { - if d.IsNull() { - c.NullCount++ - return nil - } - c.Count++ - if err := c.FMSketch.InsertValue(sc, d); err != nil { - return errors.Trace(err) - } - if c.CMSketch != nil { - c.CMSketch.InsertBytes(d.GetBytes()) - } - // Minus one is to remove the flag byte. - c.TotalSize += int64(len(d.GetBytes()) - 1) - } - c.seenValues++ - // The following code use types.CloneDatum(d) because d may have a deep reference - // to the underlying slice, GC can't free them which lead to memory leak eventually. - // TODO: Refactor the proto to avoid copying here. - if len(c.Samples) < int(c.MaxSampleSize) { - newItem := &SampleItem{} - d.Copy(&newItem.Value) - c.Samples = append(c.Samples, newItem) - } else { - shouldAdd := int64(fastrand.Uint64N(uint64(c.seenValues))) < c.MaxSampleSize - if shouldAdd { - idx := int(fastrand.Uint32N(uint32(c.MaxSampleSize))) - newItem := &SampleItem{} - d.Copy(&newItem.Value) - // To keep the order of the elements, we use delete and append, not direct replacement. - c.Samples = append(c.Samples[:idx], c.Samples[idx+1:]...) - c.Samples = append(c.Samples, newItem) - } - } - return nil -} - -// CalcTotalSize is to calculate total size based on samples. -func (c *SampleCollector) CalcTotalSize() { - c.TotalSize = 0 - for _, item := range c.Samples { - c.TotalSize += int64(len(item.Value.GetBytes())) - } -} - -// SampleBuilder is used to build samples for columns. -// Also, if primary key is handle, it will directly build histogram for it. -type SampleBuilder struct { - RecordSet sqlexec.RecordSet - Sc *stmtctx.StatementContext - PkBuilder *SortedBuilder - Collators []collate.Collator - ColsFieldType []*types.FieldType - ColLen int // ColLen is the number of columns need to be sampled. - MaxBucketSize int64 - MaxSampleSize int64 - MaxFMSketchSize int64 - CMSketchDepth int32 - CMSketchWidth int32 -} - -// CollectColumnStats collects sample from the result set using Reservoir Sampling algorithm, -// and estimates NDVs using FM Sketch during the collecting process. -// It returns the sample collectors which contain total count, null count, distinct values count and CM Sketch. -// It also returns the statistic builder for PK which contains the histogram. -// See https://en.wikipedia.org/wiki/Reservoir_sampling -func (s SampleBuilder) CollectColumnStats() ([]*SampleCollector, *SortedBuilder, error) { - collectors := make([]*SampleCollector, s.ColLen) - for i := range collectors { - collectors[i] = &SampleCollector{ - MaxSampleSize: s.MaxSampleSize, - FMSketch: NewFMSketch(int(s.MaxFMSketchSize)), - } - } - if s.CMSketchDepth > 0 && s.CMSketchWidth > 0 { - for i := range collectors { - collectors[i].CMSketch = NewCMSketch(s.CMSketchDepth, s.CMSketchWidth) - } - } - ctx := context.TODO() - req := s.RecordSet.NewChunk(nil) - it := chunk.NewIterator4Chunk(req) - for { - err := s.RecordSet.Next(ctx, req) - if err != nil { - return nil, nil, errors.Trace(err) - } - if req.NumRows() == 0 { - return collectors, s.PkBuilder, nil - } - if len(s.RecordSet.Fields()) == 0 { - return nil, nil, errors.Errorf("collect column stats failed: record set has 0 field") - } - for row := it.Begin(); row != it.End(); row = it.Next() { - datums := RowToDatums(row, s.RecordSet.Fields()) - if s.PkBuilder != nil { - err = s.PkBuilder.Iterate(datums[0]) - if err != nil { - return nil, nil, errors.Trace(err) - } - datums = datums[1:] - } - for i, val := range datums { - if s.Collators[i] != nil && !val.IsNull() { - decodedVal, err := tablecodec.DecodeColumnValue(val.GetBytes(), s.ColsFieldType[i], s.Sc.TimeZone()) - if err != nil { - return nil, nil, err - } - decodedVal.SetBytesAsString(s.Collators[i].Key(decodedVal.GetString()), decodedVal.Collation(), uint32(decodedVal.Length())) - encodedKey, err := tablecodec.EncodeValue(s.Sc, nil, decodedVal) - if err != nil { - return nil, nil, err - } - val.SetBytes(encodedKey) - } - err = collectors[i].collect(s.Sc, val) - if err != nil { - return nil, nil, errors.Trace(err) - } - } - } - } -} - -// RowToDatums converts row to datum slice. -func RowToDatums(row chunk.Row, fields []*ast.ResultField) []types.Datum { - datums := make([]types.Datum, len(fields)) - for i, f := range fields { - datums[i] = row.GetDatum(i, &f.Column.FieldType) - } - return datums -} - -// ExtractTopN extracts the topn from the CM Sketch. -func (c *SampleCollector) ExtractTopN(numTop uint32, sc *stmtctx.StatementContext, tp *types.FieldType, timeZone *time.Location) error { - if numTop == 0 { - return nil - } - values := make([][]byte, 0, len(c.Samples)) - for _, sample := range c.Samples { - values = append(values, sample.Value.GetBytes()) - } - helper := newTopNHelper(values, numTop) - cms := c.CMSketch - c.TopN = NewTopN(int(helper.actualNumTop)) - // Process them decreasingly so we can handle most frequent values first and reduce the probability of hash collision - // by small values. - for i := uint32(0); i < helper.actualNumTop; i++ { - h1, h2 := murmur3.Sum128(helper.sorted[i].data) - realCnt := cms.queryHashValue(nil, h1, h2) - // Because the encode of topn is the new encode type. But analyze proto returns the old encode type for a sample datum, - // we should decode it and re-encode it to get the correct bytes. - d, err := tablecodec.DecodeColumnValue(helper.sorted[i].data, tp, timeZone) - if err != nil { - return err - } - data, err := tablecodec.EncodeValue(sc, nil, d) - if err != nil { - return err - } - cms.SubValue(h1, h2, realCnt) - c.TopN.AppendTopN(data, realCnt) - } - c.TopN.Sort() - return nil -} diff --git a/statistics/sample_test.go b/statistics/sample_test.go deleted file mode 100644 index fb69f0a8e77d1..0000000000000 --- a/statistics/sample_test.go +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "math/rand" - "testing" - "time" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" -) - -func recordSetForWeightSamplingTest(size int) *recordSet { - r := &recordSet{ - data: make([]types.Datum, 0, size), - count: size, - } - for i := 0; i < size; i++ { - r.data = append(r.data, types.NewIntDatum(int64(i))) - } - r.setFields(mysql.TypeLonglong) - return r -} - -func recordSetForDistributedSamplingTest(size, batch int) []*recordSet { - sets := make([]*recordSet, 0, batch) - batchSize := size / batch - for i := 0; i < batch; i++ { - r := &recordSet{ - data: make([]types.Datum, 0, batchSize), - count: batchSize, - } - for j := 0; j < size/batch; j++ { - r.data = append(r.data, types.NewIntDatum(int64(j+batchSize*i))) - } - r.setFields(mysql.TypeLonglong) - sets = append(sets, r) - } - return sets -} - -func TestWeightedSampling(t *testing.T) { - sampleNum := int64(20) - rowNum := 100 - loopCnt := 1000 - rs := recordSetForWeightSamplingTest(rowNum) - sc := mock.NewContext().GetSessionVars().StmtCtx - // The loop which is commented out is used for stability test. - // This test can run 800 times in a row without any failure. - // for x := 0; x < 800; x++ { - itemCnt := make([]int, rowNum) - for loopI := 0; loopI < loopCnt; loopI++ { - builder := &RowSampleBuilder{ - Sc: sc, - RecordSet: rs, - ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, - Collators: make([]collate.Collator, 1), - ColGroups: nil, - MaxSampleSize: int(sampleNum), - MaxFMSketchSize: 1000, - Rng: rand.New(rand.NewSource(time.Now().UnixNano())), - } - collector, err := builder.Collect() - require.NoError(t, err) - for i := 0; i < int(sampleNum); i++ { - a := collector.Base().Samples[i].Columns[0].GetInt64() - itemCnt[a]++ - } - require.Nil(t, rs.Close()) - } - expFrequency := float64(sampleNum) * float64(loopCnt) / float64(rowNum) - delta := 0.5 - for _, cnt := range itemCnt { - if float64(cnt) < expFrequency/(1+delta) || float64(cnt) > expFrequency*(1+delta) { - require.Truef(t, false, "The frequency %v is exceed the Chernoff Bound", cnt) - } - } -} - -func TestDistributedWeightedSampling(t *testing.T) { - sampleNum := int64(10) - rowNum := 100 - loopCnt := 1500 - batch := 5 - sets := recordSetForDistributedSamplingTest(rowNum, batch) - sc := mock.NewContext().GetSessionVars().StmtCtx - // The loop which is commented out is used for stability test. - // This test can run 800 times in a row without any failure. - // for x := 0; x < 800; x++ { - itemCnt := make([]int, rowNum) - for loopI := 1; loopI < loopCnt; loopI++ { - rootRowCollector := NewReservoirRowSampleCollector(int(sampleNum), 1) - rootRowCollector.FMSketches = append(rootRowCollector.FMSketches, NewFMSketch(1000)) - for i := 0; i < batch; i++ { - builder := &RowSampleBuilder{ - Sc: sc, - RecordSet: sets[i], - ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, - Collators: make([]collate.Collator, 1), - ColGroups: nil, - MaxSampleSize: int(sampleNum), - MaxFMSketchSize: 1000, - Rng: rand.New(rand.NewSource(time.Now().UnixNano())), - } - collector, err := builder.Collect() - require.NoError(t, err) - rootRowCollector.MergeCollector(collector) - require.Nil(t, sets[i].Close()) - } - for _, sample := range rootRowCollector.Samples { - itemCnt[sample.Columns[0].GetInt64()]++ - } - } - expFrequency := float64(sampleNum) * float64(loopCnt) / float64(rowNum) - delta := 0.5 - for _, cnt := range itemCnt { - if float64(cnt) < expFrequency/(1+delta) || float64(cnt) > expFrequency*(1+delta) { - require.Truef(t, false, "the frequency %v is exceed the Chernoff Bound", cnt) - } - } -} - -func TestBuildStatsOnRowSample(t *testing.T) { - ctx := mock.NewContext() - sketch := NewFMSketch(1000) - data := make([]*SampleItem, 0, 8) - for i := 1; i <= 1000; i++ { - d := types.NewIntDatum(int64(i)) - err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) - require.NoError(t, err) - data = append(data, &SampleItem{Value: d}) - } - for i := 1; i < 10; i++ { - d := types.NewIntDatum(int64(2)) - err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) - require.NoError(t, err) - data = append(data, &SampleItem{Value: d}) - } - for i := 1; i < 7; i++ { - d := types.NewIntDatum(int64(4)) - err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) - require.NoError(t, err) - data = append(data, &SampleItem{Value: d}) - } - for i := 1; i < 5; i++ { - d := types.NewIntDatum(int64(7)) - err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) - require.NoError(t, err) - data = append(data, &SampleItem{Value: d}) - } - for i := 1; i < 3; i++ { - d := types.NewIntDatum(int64(11)) - err := sketch.InsertValue(ctx.GetSessionVars().StmtCtx, d) - require.NoError(t, err) - data = append(data, &SampleItem{Value: d}) - } - collector := &SampleCollector{ - Samples: data, - NullCount: 0, - Count: int64(len(data)), - FMSketch: sketch, - TotalSize: int64(len(data)) * 8, - } - tp := types.NewFieldType(mysql.TypeLonglong) - hist, topN, err := BuildHistAndTopN(ctx, 5, 4, 1, collector, tp, true, nil) - require.Nilf(t, err, "%+v", err) - topNStr, err := topN.DecodedString(ctx, []byte{tp.GetType()}) - require.NoError(t, err) - require.Equal(t, "TopN{length: 4, [(2, 10), (4, 7), (7, 5), (11, 3)]}", topNStr) - require.Equal(t, "column:1 ndv:1000 totColSize:8168\n"+ - "num: 200 lower_bound: 1 upper_bound: 204 repeats: 1 ndv: 0\n"+ - "num: 200 lower_bound: 205 upper_bound: 404 repeats: 1 ndv: 0\n"+ - "num: 200 lower_bound: 405 upper_bound: 604 repeats: 1 ndv: 0\n"+ - "num: 200 lower_bound: 605 upper_bound: 804 repeats: 1 ndv: 0\n"+ - "num: 196 lower_bound: 805 upper_bound: 1000 repeats: 1 ndv: 0", hist.ToString(0)) -} - -type testSampleSuite struct { - count int - rs sqlexec.RecordSet -} - -func TestSampleSerial(t *testing.T) { - s := createTestSampleSuite() - t.Run("SubTestCollectColumnStats", SubTestCollectColumnStats(s)) - t.Run("SubTestMergeSampleCollector", SubTestMergeSampleCollector(s)) - t.Run("SubTestCollectorProtoConversion", SubTestCollectorProtoConversion(s)) -} - -func createTestSampleSuite() *testSampleSuite { - s := new(testSampleSuite) - s.count = 10000 - rs := &recordSet{ - data: make([]types.Datum, s.count), - count: s.count, - cursor: 0, - firstIsID: true, - } - rs.setFields(mysql.TypeLonglong, mysql.TypeLonglong) - start := 1000 // 1000 values is null - for i := start; i < rs.count; i++ { - rs.data[i].SetInt64(int64(i)) - } - for i := start; i < rs.count; i += 3 { - rs.data[i].SetInt64(rs.data[i].GetInt64() + 1) - } - for i := start; i < rs.count; i += 5 { - rs.data[i].SetInt64(rs.data[i].GetInt64() + 2) - } - s.rs = rs - return s -} - -func SubTestCollectColumnStats(s *testSampleSuite) func(*testing.T) { - return func(t *testing.T) { - sc := mock.NewContext().GetSessionVars().StmtCtx - builder := SampleBuilder{ - Sc: sc, - RecordSet: s.rs, - ColLen: 1, - PkBuilder: NewSortedBuilder(sc, 256, 1, types.NewFieldType(mysql.TypeLonglong), Version2), - MaxSampleSize: 10000, - MaxBucketSize: 256, - MaxFMSketchSize: 1000, - CMSketchWidth: 2048, - CMSketchDepth: 8, - Collators: make([]collate.Collator, 1), - ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, - } - require.Nil(t, s.rs.Close()) - collectors, pkBuilder, err := builder.CollectColumnStats() - require.NoError(t, err) - - require.Equal(t, int64(s.count), collectors[0].NullCount+collectors[0].Count) - require.Equal(t, int64(6232), collectors[0].FMSketch.NDV()) - require.Equal(t, uint64(collectors[0].Count), collectors[0].CMSketch.TotalCount()) - require.Equal(t, int64(s.count), pkBuilder.Count) - require.Equal(t, int64(s.count), pkBuilder.Hist().NDV) - } -} - -func SubTestMergeSampleCollector(s *testSampleSuite) func(*testing.T) { - return func(t *testing.T) { - builder := SampleBuilder{ - Sc: mock.NewContext().GetSessionVars().StmtCtx, - RecordSet: s.rs, - ColLen: 2, - MaxSampleSize: 1000, - MaxBucketSize: 256, - MaxFMSketchSize: 1000, - CMSketchWidth: 2048, - CMSketchDepth: 8, - Collators: make([]collate.Collator, 2), - ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong), types.NewFieldType(mysql.TypeLonglong)}, - } - require.Nil(t, s.rs.Close()) - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - collectors, pkBuilder, err := builder.CollectColumnStats() - require.NoError(t, err) - require.Nil(t, pkBuilder) - require.Len(t, collectors, 2) - collectors[0].IsMerger = true - collectors[0].MergeSampleCollector(sc, collectors[1]) - require.Equal(t, int64(9280), collectors[0].FMSketch.NDV()) - require.Len(t, collectors[0].Samples, 1000) - require.Equal(t, int64(1000), collectors[0].NullCount) - require.Equal(t, int64(19000), collectors[0].Count) - require.Equal(t, uint64(collectors[0].Count), collectors[0].CMSketch.TotalCount()) - } -} - -func SubTestCollectorProtoConversion(s *testSampleSuite) func(*testing.T) { - return func(t *testing.T) { - builder := SampleBuilder{ - Sc: mock.NewContext().GetSessionVars().StmtCtx, - RecordSet: s.rs, - ColLen: 2, - MaxSampleSize: 10000, - MaxBucketSize: 256, - MaxFMSketchSize: 1000, - CMSketchWidth: 2048, - CMSketchDepth: 8, - Collators: make([]collate.Collator, 2), - ColsFieldType: []*types.FieldType{types.NewFieldType(mysql.TypeLonglong), types.NewFieldType(mysql.TypeLonglong)}, - } - require.Nil(t, s.rs.Close()) - collectors, pkBuilder, err := builder.CollectColumnStats() - require.NoError(t, err) - require.Nil(t, pkBuilder) - for _, collector := range collectors { - p := SampleCollectorToProto(collector) - s := SampleCollectorFromProto(p) - require.Equal(t, s.Count, collector.Count) - require.Equal(t, s.NullCount, collector.NullCount) - require.Equal(t, s.CMSketch.TotalCount(), collector.CMSketch.TotalCount()) - require.Equal(t, s.FMSketch.NDV(), collector.FMSketch.NDV()) - require.Equal(t, s.TotalSize, collector.TotalSize) - require.Equal(t, len(s.Samples), len(collector.Samples)) - } - } -} diff --git a/statistics/table.go b/statistics/table.go deleted file mode 100644 index 9e70acd76ba20..0000000000000 --- a/statistics/table.go +++ /dev/null @@ -1,685 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package statistics - -import ( - "cmp" - "fmt" - "slices" - "strings" - "sync" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/ranger" - "go.uber.org/atomic" - "golang.org/x/exp/maps" -) - -const ( - // PseudoVersion means the pseudo statistics version is 0. - PseudoVersion uint64 = 0 - - // PseudoRowCount export for other pkg to use. - // When we haven't analyzed a table, we use pseudo statistics to estimate costs. - // It has row count 10000, equal condition selects 1/1000 of total rows, less condition selects 1/3 of total rows, - // between condition selects 1/40 of total rows. - PseudoRowCount = 10000 -) - -var ( - // Below functions are used to solve cycle import problem. - // Note: all functions below will be removed after finishing moving all estimation functions into the cardinality package. - - // GetRowCountByIndexRanges is a function type to get row count by index ranges. - GetRowCountByIndexRanges func(sctx sessionctx.Context, coll *HistColl, idxID int64, indexRanges []*ranger.Range) (result float64, err error) - - // GetRowCountByIntColumnRanges is a function type to get row count by int column ranges. - GetRowCountByIntColumnRanges func(sctx sessionctx.Context, coll *HistColl, colID int64, intRanges []*ranger.Range) (result float64, err error) - - // GetRowCountByColumnRanges is a function type to get row count by column ranges. - GetRowCountByColumnRanges func(sctx sessionctx.Context, coll *HistColl, colID int64, colRanges []*ranger.Range) (result float64, err error) -) - -// Table represents statistics for a table. -type Table struct { - ExtendedStats *ExtendedStatsColl - Name string - HistColl - Version uint64 - // TblInfoUpdateTS is the UpdateTS of the TableInfo used when filling this struct. - // It is the schema version of the corresponding table. It is used to skip redundant - // loading of stats, i.e, if the cached stats is already update-to-date with mysql.stats_xxx tables, - // and the schema of the table does not change, we don't need to load the stats for this - // table again. - TblInfoUpdateTS uint64 -} - -// ExtendedStatsItem is the cached item of a mysql.stats_extended record. -type ExtendedStatsItem struct { - StringVals string - ColIDs []int64 - ScalarVals float64 - Tp uint8 -} - -// ExtendedStatsColl is a collection of cached items for mysql.stats_extended records. -type ExtendedStatsColl struct { - Stats map[string]*ExtendedStatsItem - LastUpdateVersion uint64 -} - -// NewExtendedStatsColl allocate an ExtendedStatsColl struct. -func NewExtendedStatsColl() *ExtendedStatsColl { - return &ExtendedStatsColl{Stats: make(map[string]*ExtendedStatsItem)} -} - -const ( - // ExtendedStatsInited is the status for extended stats which are just registered but have not been analyzed yet. - ExtendedStatsInited uint8 = iota - // ExtendedStatsAnalyzed is the status for extended stats which have been collected in analyze. - ExtendedStatsAnalyzed - // ExtendedStatsDeleted is the status for extended stats which were dropped. These "deleted" records would be removed from storage by GCStats(). - ExtendedStatsDeleted -) - -// HistColl is a collection of histogram. It collects enough information for plan to calculate the selectivity. -type HistColl struct { - Columns map[int64]*Column - Indices map[int64]*Index - // Idx2ColumnIDs maps the index id to its column ids. It's used to calculate the selectivity in planner. - Idx2ColumnIDs map[int64][]int64 - // ColID2IdxIDs maps the column id to a list index ids whose first column is it. It's used to calculate the selectivity in planner. - ColID2IdxIDs map[int64][]int64 - PhysicalID int64 - // TODO: add AnalyzeCount here - RealtimeCount int64 // RealtimeCount is the current table row count, maintained by applying stats delta based on AnalyzeCount. - ModifyCount int64 // Total modify count in a table. - - // HavePhysicalID is true means this HistColl is from single table and have its ID's information. - // The physical id is used when try to load column stats from storage. - HavePhysicalID bool - Pseudo bool -} - -// TableMemoryUsage records tbl memory usage -type TableMemoryUsage struct { - ColumnsMemUsage map[int64]CacheItemMemoryUsage - IndicesMemUsage map[int64]CacheItemMemoryUsage - TableID int64 - TotalMemUsage int64 -} - -// TotalIdxTrackingMemUsage returns total indices' tracking memory usage -func (t *TableMemoryUsage) TotalIdxTrackingMemUsage() (sum int64) { - for _, idx := range t.IndicesMemUsage { - sum += idx.TrackingMemUsage() - } - return sum -} - -// TotalColTrackingMemUsage returns total columns' tracking memory usage -func (t *TableMemoryUsage) TotalColTrackingMemUsage() (sum int64) { - for _, col := range t.ColumnsMemUsage { - sum += col.TrackingMemUsage() - } - return sum -} - -// TotalTrackingMemUsage return total tracking memory usage -func (t *TableMemoryUsage) TotalTrackingMemUsage() int64 { - return t.TotalIdxTrackingMemUsage() + t.TotalColTrackingMemUsage() -} - -// TableCacheItem indicates the unit item stored in statsCache, eg: Column/Index -type TableCacheItem interface { - ItemID() int64 - MemoryUsage() CacheItemMemoryUsage - IsAllEvicted() bool - GetEvictedStatus() int - - DropUnnecessaryData() - IsStatsInitialized() bool - GetStatsVer() int64 -} - -// CacheItemMemoryUsage indicates the memory usage of TableCacheItem -type CacheItemMemoryUsage interface { - ItemID() int64 - TotalMemoryUsage() int64 - TrackingMemUsage() int64 - HistMemUsage() int64 - TopnMemUsage() int64 - CMSMemUsage() int64 -} - -// ColumnMemUsage records column memory usage -type ColumnMemUsage struct { - ColumnID int64 - HistogramMemUsage int64 - CMSketchMemUsage int64 - FMSketchMemUsage int64 - TopNMemUsage int64 - TotalMemUsage int64 -} - -// TotalMemoryUsage implements CacheItemMemoryUsage -func (c *ColumnMemUsage) TotalMemoryUsage() int64 { - return c.TotalMemUsage -} - -// ItemID implements CacheItemMemoryUsage -func (c *ColumnMemUsage) ItemID() int64 { - return c.ColumnID -} - -// TrackingMemUsage implements CacheItemMemoryUsage -func (c *ColumnMemUsage) TrackingMemUsage() int64 { - return c.CMSketchMemUsage + c.TopNMemUsage + c.HistogramMemUsage -} - -// HistMemUsage implements CacheItemMemoryUsage -func (c *ColumnMemUsage) HistMemUsage() int64 { - return c.HistogramMemUsage -} - -// TopnMemUsage implements CacheItemMemoryUsage -func (c *ColumnMemUsage) TopnMemUsage() int64 { - return c.TopNMemUsage -} - -// CMSMemUsage implements CacheItemMemoryUsage -func (c *ColumnMemUsage) CMSMemUsage() int64 { - return c.CMSketchMemUsage -} - -// IndexMemUsage records index memory usage -type IndexMemUsage struct { - IndexID int64 - HistogramMemUsage int64 - CMSketchMemUsage int64 - TopNMemUsage int64 - TotalMemUsage int64 -} - -// TotalMemoryUsage implements CacheItemMemoryUsage -func (c *IndexMemUsage) TotalMemoryUsage() int64 { - return c.TotalMemUsage -} - -// ItemID implements CacheItemMemoryUsage -func (c *IndexMemUsage) ItemID() int64 { - return c.IndexID -} - -// TrackingMemUsage implements CacheItemMemoryUsage -func (c *IndexMemUsage) TrackingMemUsage() int64 { - return c.CMSketchMemUsage + c.TopNMemUsage + c.HistogramMemUsage -} - -// HistMemUsage implements CacheItemMemoryUsage -func (c *IndexMemUsage) HistMemUsage() int64 { - return c.HistogramMemUsage -} - -// TopnMemUsage implements CacheItemMemoryUsage -func (c *IndexMemUsage) TopnMemUsage() int64 { - return c.TopNMemUsage -} - -// CMSMemUsage implements CacheItemMemoryUsage -func (c *IndexMemUsage) CMSMemUsage() int64 { - return c.CMSketchMemUsage -} - -// MemoryUsage returns the total memory usage of this Table. -// it will only calc the size of Columns and Indices stats data of table. -// We ignore the size of other metadata in Table -func (t *Table) MemoryUsage() *TableMemoryUsage { - tMemUsage := &TableMemoryUsage{ - TableID: t.PhysicalID, - ColumnsMemUsage: make(map[int64]CacheItemMemoryUsage), - IndicesMemUsage: make(map[int64]CacheItemMemoryUsage), - } - for _, col := range t.Columns { - if col != nil { - colMemUsage := col.MemoryUsage() - tMemUsage.ColumnsMemUsage[colMemUsage.ItemID()] = colMemUsage - tMemUsage.TotalMemUsage += colMemUsage.TotalMemoryUsage() - } - } - for _, index := range t.Indices { - if index != nil { - idxMemUsage := index.MemoryUsage() - tMemUsage.IndicesMemUsage[idxMemUsage.ItemID()] = idxMemUsage - tMemUsage.TotalMemUsage += idxMemUsage.TotalMemoryUsage() - } - } - return tMemUsage -} - -// Copy copies the current table. -func (t *Table) Copy() *Table { - newHistColl := HistColl{ - PhysicalID: t.PhysicalID, - HavePhysicalID: t.HavePhysicalID, - RealtimeCount: t.RealtimeCount, - Columns: make(map[int64]*Column, len(t.Columns)), - Indices: make(map[int64]*Index, len(t.Indices)), - Pseudo: t.Pseudo, - ModifyCount: t.ModifyCount, - } - for id, col := range t.Columns { - newHistColl.Columns[id] = col.Copy() - } - for id, idx := range t.Indices { - newHistColl.Indices[id] = idx.Copy() - } - nt := &Table{ - HistColl: newHistColl, - Version: t.Version, - Name: t.Name, - TblInfoUpdateTS: t.TblInfoUpdateTS, - } - if t.ExtendedStats != nil { - newExtStatsColl := &ExtendedStatsColl{ - Stats: make(map[string]*ExtendedStatsItem), - LastUpdateVersion: t.ExtendedStats.LastUpdateVersion, - } - for name, item := range t.ExtendedStats.Stats { - newExtStatsColl.Stats[name] = item - } - nt.ExtendedStats = newExtStatsColl - } - return nt -} - -// ShallowCopy copies the current table. -// It's different from Copy(). Only the struct Table (and also the embedded HistColl) is copied here. -// The internal containers, like t.Columns and t.Indices, and the stats, like TopN and Histogram are not copied. -func (t *Table) ShallowCopy() *Table { - newHistColl := HistColl{ - PhysicalID: t.PhysicalID, - HavePhysicalID: t.HavePhysicalID, - RealtimeCount: t.RealtimeCount, - Columns: t.Columns, - Indices: t.Indices, - Pseudo: t.Pseudo, - ModifyCount: t.ModifyCount, - } - nt := &Table{ - HistColl: newHistColl, - Version: t.Version, - Name: t.Name, - TblInfoUpdateTS: t.TblInfoUpdateTS, - ExtendedStats: t.ExtendedStats, - } - return nt -} - -// String implements Stringer interface. -func (t *Table) String() string { - strs := make([]string, 0, len(t.Columns)+1) - strs = append(strs, fmt.Sprintf("Table:%d RealtimeCount:%d", t.PhysicalID, t.RealtimeCount)) - cols := make([]*Column, 0, len(t.Columns)) - for _, col := range t.Columns { - cols = append(cols, col) - } - slices.SortFunc(cols, func(i, j *Column) int { return cmp.Compare(i.ID, j.ID) }) - for _, col := range cols { - strs = append(strs, col.String()) - } - idxs := make([]*Index, 0, len(t.Indices)) - for _, idx := range t.Indices { - idxs = append(idxs, idx) - } - slices.SortFunc(idxs, func(i, j *Index) int { return cmp.Compare(i.ID, j.ID) }) - for _, idx := range idxs { - strs = append(strs, idx.String()) - } - // TODO: concat content of ExtendedStatsColl - return strings.Join(strs, "\n") -} - -// IndexStartWithColumn finds the first index whose first column is the given column. -func (t *Table) IndexStartWithColumn(colName string) *Index { - for _, index := range t.Indices { - if index.Info.Columns[0].Name.L == colName { - return index - } - } - return nil -} - -// ColumnByName finds the statistics.Column for the given column. -func (t *Table) ColumnByName(colName string) *Column { - for _, c := range t.Columns { - if c.Info.Name.L == colName { - return c - } - } - return nil -} - -// GetStatsInfo returns their statistics according to the ID of the column or index, including histogram, CMSketch, TopN and FMSketch. -// -// needCopy: In order to protect the item in the cache from being damaged, we need to copy the item. -func (t *Table) GetStatsInfo(id int64, isIndex bool, needCopy bool) (*Histogram, *CMSketch, *TopN, *FMSketch, bool) { - if isIndex { - if idxStatsInfo, ok := t.Indices[id]; ok { - if needCopy { - return idxStatsInfo.Histogram.Copy(), - idxStatsInfo.CMSketch.Copy(), idxStatsInfo.TopN.Copy(), idxStatsInfo.FMSketch.Copy(), true - } - return &idxStatsInfo.Histogram, - idxStatsInfo.CMSketch, idxStatsInfo.TopN, idxStatsInfo.FMSketch, true - } - // newly added index which is not analyzed yet - return nil, nil, nil, nil, false - } - if colStatsInfo, ok := t.Columns[id]; ok { - if needCopy { - return colStatsInfo.Histogram.Copy(), colStatsInfo.CMSketch.Copy(), - colStatsInfo.TopN.Copy(), colStatsInfo.FMSketch.Copy(), true - } - return &colStatsInfo.Histogram, colStatsInfo.CMSketch, - colStatsInfo.TopN, colStatsInfo.FMSketch, true - } - // newly added column which is not analyzed yet - return nil, nil, nil, nil, false -} - -// GetAnalyzeRowCount tries to get the row count of a column or an index if possible. -// This method is useful because this row count doesn't consider the modify count. -func (coll *HistColl) GetAnalyzeRowCount() float64 { - ids := maps.Keys(coll.Columns) - slices.Sort(ids) - for _, id := range ids { - col := coll.Columns[id] - if col != nil && col.IsFullLoad() { - return col.TotalRowCount() - } - } - ids = maps.Keys(coll.Indices) - slices.Sort(ids) - for _, id := range ids { - idx := coll.Indices[id] - if idx == nil { - continue - } - if idx.Info != nil && idx.Info.MVIndex { - continue - } - if idx.IsFullLoad() { - return idx.TotalRowCount() - } - } - return -1 -} - -// GetStatsHealthy calculates stats healthy if the table stats is not pseudo. -// If the table stats is pseudo, it returns 0, false, otherwise it returns stats healthy, true. -func (t *Table) GetStatsHealthy() (int64, bool) { - if t == nil || t.Pseudo { - return 0, false - } - var healthy int64 - count := float64(t.RealtimeCount) - if histCount := t.GetAnalyzeRowCount(); histCount > 0 { - count = histCount - } - if float64(t.ModifyCount) < count { - healthy = int64((1.0 - float64(t.ModifyCount)/count) * 100.0) - } else if t.ModifyCount == 0 { - healthy = 100 - } - return healthy, true -} - -type neededStatsMap struct { - items map[model.TableItemID]struct{} - m sync.RWMutex -} - -func (n *neededStatsMap) AllItems() []model.TableItemID { - n.m.RLock() - keys := make([]model.TableItemID, 0, len(n.items)) - for key := range n.items { - keys = append(keys, key) - } - n.m.RUnlock() - return keys -} - -func (n *neededStatsMap) insert(col model.TableItemID) { - n.m.Lock() - n.items[col] = struct{}{} - n.m.Unlock() -} - -func (n *neededStatsMap) Delete(col model.TableItemID) { - n.m.Lock() - delete(n.items, col) - n.m.Unlock() -} - -func (n *neededStatsMap) Length() int { - n.m.RLock() - defer n.m.RUnlock() - return len(n.items) -} - -// RatioOfPseudoEstimate means if modifyCount / statsTblCount is greater than this ratio, we think the stats is invalid -// and use pseudo estimation. -var RatioOfPseudoEstimate = atomic.NewFloat64(0.7) - -// IsInitialized returns true if any column/index stats of the table is initialized. -func (t *Table) IsInitialized() bool { - for _, col := range t.Columns { - if col != nil && col.IsStatsInitialized() { - return true - } - } - for _, idx := range t.Indices { - if idx != nil && idx.IsStatsInitialized() { - return true - } - } - return false -} - -// IsOutdated returns true if the table stats is outdated. -func (t *Table) IsOutdated() bool { - rowcount := t.GetAnalyzeRowCount() - if rowcount < 0 { - rowcount = float64(t.RealtimeCount) - } - if rowcount > 0 && float64(t.ModifyCount)/rowcount > RatioOfPseudoEstimate.Load() { - return true - } - return false -} - -// ReleaseAndPutToPool releases data structures of Table and put itself back to pool. -func (t *Table) ReleaseAndPutToPool() { - for _, col := range t.Columns { - col.FMSketch.DestroyAndPutToPool() - } - maps.Clear(t.Columns) - for _, idx := range t.Indices { - idx.FMSketch.DestroyAndPutToPool() - } - maps.Clear(t.Indices) -} - -// ID2UniqueID generates a new HistColl whose `Columns` is built from UniqueID of given columns. -func (coll *HistColl) ID2UniqueID(columns []*expression.Column) *HistColl { - cols := make(map[int64]*Column) - for _, col := range columns { - colHist, ok := coll.Columns[col.ID] - if ok { - cols[col.UniqueID] = colHist - } - } - newColl := &HistColl{ - PhysicalID: coll.PhysicalID, - HavePhysicalID: coll.HavePhysicalID, - Pseudo: coll.Pseudo, - RealtimeCount: coll.RealtimeCount, - ModifyCount: coll.ModifyCount, - Columns: cols, - } - return newColl -} - -// GenerateHistCollFromColumnInfo generates a new HistColl whose ColID2IdxIDs and IdxID2ColIDs is built from the given parameter. -func (coll *HistColl) GenerateHistCollFromColumnInfo(tblInfo *model.TableInfo, columns []*expression.Column) *HistColl { - newColHistMap := make(map[int64]*Column) - colInfoID2UniqueID := make(map[int64]int64, len(columns)) - idxID2idxInfo := make(map[int64]*model.IndexInfo) - for _, col := range columns { - colInfoID2UniqueID[col.ID] = col.UniqueID - } - for id, colHist := range coll.Columns { - uniqueID, ok := colInfoID2UniqueID[id] - // Collect the statistics by the given columns. - if ok { - newColHistMap[uniqueID] = colHist - } - } - for _, idxInfo := range tblInfo.Indices { - idxID2idxInfo[idxInfo.ID] = idxInfo - } - newIdxHistMap := make(map[int64]*Index) - idx2Columns := make(map[int64][]int64) - colID2IdxIDs := make(map[int64][]int64) - for id, idxHist := range coll.Indices { - idxInfo := idxID2idxInfo[id] - if idxInfo == nil { - continue - } - ids := make([]int64, 0, len(idxInfo.Columns)) - for _, idxCol := range idxInfo.Columns { - uniqueID, ok := colInfoID2UniqueID[tblInfo.Columns[idxCol.Offset].ID] - if !ok { - break - } - ids = append(ids, uniqueID) - } - // If the length of the id list is 0, this index won't be used in this query. - if len(ids) == 0 { - continue - } - colID2IdxIDs[ids[0]] = append(colID2IdxIDs[ids[0]], idxHist.ID) - newIdxHistMap[idxHist.ID] = idxHist - idx2Columns[idxHist.ID] = ids - } - for _, idxIDs := range colID2IdxIDs { - slices.Sort(idxIDs) - } - newColl := &HistColl{ - PhysicalID: coll.PhysicalID, - HavePhysicalID: coll.HavePhysicalID, - Pseudo: coll.Pseudo, - RealtimeCount: coll.RealtimeCount, - ModifyCount: coll.ModifyCount, - Columns: newColHistMap, - Indices: newIdxHistMap, - ColID2IdxIDs: colID2IdxIDs, - Idx2ColumnIDs: idx2Columns, - } - return newColl -} - -// PseudoTable creates a pseudo table statistics. -// Usually, we don't want to trigger stats loading for pseudo table. -// But there are exceptional cases. In such cases, we should pass allowTriggerLoading as true. -// Such case could possibly happen in getStatsTable(). -func PseudoTable(tblInfo *model.TableInfo, allowTriggerLoading bool) *Table { - const fakePhysicalID int64 = -1 - pseudoHistColl := HistColl{ - RealtimeCount: PseudoRowCount, - PhysicalID: tblInfo.ID, - HavePhysicalID: true, - Columns: make(map[int64]*Column, len(tblInfo.Columns)), - Indices: make(map[int64]*Index, len(tblInfo.Indices)), - Pseudo: true, - } - t := &Table{ - HistColl: pseudoHistColl, - } - for _, col := range tblInfo.Columns { - // The column is public to use. Also we should check the column is not hidden since hidden means that it's used by expression index. - // We would not collect stats for the hidden column and we won't use the hidden column to estimate. - // Thus we don't create pseudo stats for it. - if col.State == model.StatePublic && !col.Hidden { - t.Columns[col.ID] = &Column{ - PhysicalID: fakePhysicalID, - Info: col, - IsHandle: tblInfo.PKIsHandle && mysql.HasPriKeyFlag(col.GetFlag()), - Histogram: *NewHistogram(col.ID, 0, 0, 0, &col.FieldType, 0, 0), - } - if allowTriggerLoading { - t.Columns[col.ID].PhysicalID = tblInfo.ID - } - } - } - for _, idx := range tblInfo.Indices { - if idx.State == model.StatePublic { - t.Indices[idx.ID] = &Index{ - PhysicalID: fakePhysicalID, - Info: idx, - Histogram: *NewHistogram(idx.ID, 0, 0, 0, types.NewFieldType(mysql.TypeBlob), 0, 0), - } - if allowTriggerLoading { - t.Indices[idx.ID].PhysicalID = tblInfo.ID - } - } - } - return t -} - -// CheckAnalyzeVerOnTable checks whether the given version is the one from the tbl. -// If not, it will return false and set the version to the tbl's. -// We use this check to make sure all the statistics of the table are in the same version. -func CheckAnalyzeVerOnTable(tbl *Table, version *int) bool { - for _, col := range tbl.Columns { - if !col.IsAnalyzed() { - continue - } - if col.StatsVer != int64(*version) { - *version = int(col.StatsVer) - return false - } - // If we found one column and the version is the same, we can directly return since all the versions from this table is the same. - return true - } - for _, idx := range tbl.Indices { - if !idx.IsAnalyzed() { - continue - } - if idx.StatsVer != int64(*version) { - *version = int(idx.StatsVer) - return false - } - // If we found one column and the version is the same, we can directly return since all the versions from this table is the same. - return true - } - // This table has no statistics yet. We can directly return true. - return true -} diff --git a/store/BUILD.bazel b/store/BUILD.bazel deleted file mode 100644 index 244f8b8a25980..0000000000000 --- a/store/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "store", - srcs = ["store.go"], - importpath = "github.com/pingcap/tidb/store", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//util", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/pdpb", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "store_test", - timeout = "short", - srcs = [ - "batch_coprocessor_test.go", - "main_test.go", - "store_test.go", - ], - embed = [":store"], - flaky = True, - shard_count = 23, - deps = [ - "//domain", - "//kv", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//testutils", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/batch_coprocessor_test.go b/store/batch_coprocessor_test.go deleted file mode 100644 index c06c71f9b82ae..0000000000000 --- a/store/batch_coprocessor_test.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package store - -import ( - "context" - "fmt" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" -) - -func createMockTiKVStoreOptions(tiflashNum int) []mockstore.MockTiKVStoreOption { - return []mockstore.MockTiKVStoreOption{ - mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockCluster := c.(*unistore.Cluster) - _, _, region1 := mockstore.BootstrapWithSingleStore(c) - tiflashIdx := 0 - for tiflashIdx < tiflashNum { - store2 := c.AllocID() - peer2 := c.AllocID() - addr2 := fmt.Sprintf("tiflash%d", tiflashIdx) - mockCluster.AddStore(store2, addr2, &metapb.StoreLabel{Key: "engine", Value: "tiflash"}) - mockCluster.AddPeer(region1, store2, peer2) - tiflashIdx++ - } - }), - mockstore.WithStoreType(mockstore.EmbedUnistore), - } -} - -func TestStoreErr(t *testing.T) { - store := testkit.CreateMockStore(t, createMockTiKVStoreOptions(1)...) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) - }() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int not null, b int not null)") - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tk.MustExec("insert into t values(1,0)") - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") - tk.MustExec("set @@session.tidb_allow_mpp=OFF") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopCancelled", "1*return(true)")) - - err = tk.QueryToErr("select count(*) from t") - require.Equal(t, context.Canceled, errors.Cause(err)) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0", "1*return(\"tiflash0\")")) - - tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) - err = tk.QueryToErr("select count(*) from t") - require.Error(t, err) -} - -func TestStoreSwitchPeer(t *testing.T) { - store := testkit.CreateMockStore(t, createMockTiKVStoreOptions(2)...) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount", `return(true)`)) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/infoschema/mockTiFlashStoreCount")) - }() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(a int not null, b int not null)") - tk.MustExec("alter table t set tiflash replica 1") - tb := external.GetTableByName(t, tk, "test", "t") - tk.MustExec("set @@session.tidb_allow_tiflash_cop=ON") - - err := domain.GetDomain(tk.Session()).DDL().UpdateTableReplicaInfo(tk.Session(), tb.Meta().ID, true) - require.NoError(t, err) - - tk.MustExec("insert into t values(1,0)") - tk.MustExec("set @@session.tidb_isolation_read_engines=\"tiflash\"") - tk.MustExec("set @@session.tidb_allow_mpp=OFF") - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash0", "return(\"tiflash0\")")) - - tk.MustQuery("select count(*) from t").Check(testkit.Rows("1")) - - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/store/mockstore/unistore/BatchCopRpcErrtiflash1", "return(\"tiflash1\")")) - err = tk.QueryToErr("select count(*) from t") - require.Error(t, err) -} diff --git a/store/copr/BUILD.bazel b/store/copr/BUILD.bazel deleted file mode 100644 index 0d935136404ba..0000000000000 --- a/store/copr/BUILD.bazel +++ /dev/null @@ -1,105 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "copr", - srcs = [ - "batch_coprocessor.go", - "batch_request_sender.go", - "coprocessor.go", - "coprocessor_cache.go", - "key_ranges.go", - "mpp.go", - "mpp_probe.go", - "region_cache.go", - "store.go", - ], - importpath = "github.com/pingcap/tidb/store/copr", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//ddl/placement", - "//domain/infosync", - "//domain/resourcegroup", - "//errno", - "//kv", - "//metrics", - "//parser/terror", - "//sessionctx/variable", - "//store/copr/metrics", - "//store/driver/backoff", - "//store/driver/error", - "//store/driver/options", - "//util", - "//util/execdetails", - "//util/intest", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "//util/paging", - "//util/tiflash", - "//util/tiflashcompute", - "//util/tracing", - "//util/trxevents", - "@com_github_dgraph_io_ristretto//:ristretto", - "@com_github_gogo_protobuf//proto", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//metrics", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@com_github_tikv_client_go_v2//txnkv/txnlock", - "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@com_github_twmb_murmur3//:murmur3", - "@org_golang_google_grpc//codes", - "@org_golang_google_grpc//status", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "copr_test", - timeout = "short", - srcs = [ - "batch_coprocessor_test.go", - "coprocessor_cache_test.go", - "coprocessor_test.go", - "key_ranges_test.go", - "main_test.go", - "mpp_probe_test.go", - ], - embed = [":copr"], - flaky = True, - race = "on", - shard_count = 28, - deps = [ - "//kv", - "//store/driver/backoff", - "//testkit/testsetup", - "//util/logutil", - "//util/paging", - "//util/trxevents", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_stathat_consistent//:consistent", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/store/copr/batch_coprocessor_test.go b/store/copr/batch_coprocessor_test.go deleted file mode 100644 index 895583943a31f..0000000000000 --- a/store/copr/batch_coprocessor_test.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "context" - "math/rand" - "sort" - "strconv" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/util/logutil" - "github.com/stathat/consistent" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" -) - -// StoreID: [1, storeCount] -func buildStoreTaskMap(storeCount int) map[uint64]*batchCopTask { - storeTasks := make(map[uint64]*batchCopTask) - for i := 0; i < storeCount; i++ { - storeTasks[uint64(i+1)] = &batchCopTask{} - } - return storeTasks -} - -func buildRegionInfos(storeCount, regionCount, replicaNum int) []RegionInfo { - var ss []string - for i := 0; i < regionCount; i++ { - s := strconv.Itoa(i) - ss = append(ss, s) - } - sort.Strings(ss) - - storeIDExist := func(storeID uint64, storeIDs []uint64) bool { - for _, i := range storeIDs { - if i == storeID { - return true - } - } - return false - } - - randomStores := func(storeCount, replicaNum int) []uint64 { - var storeIDs []uint64 - for len(storeIDs) < replicaNum { - t := uint64(rand.Intn(storeCount) + 1) - if storeIDExist(t, storeIDs) { - continue - } - storeIDs = append(storeIDs, t) - } - return storeIDs - } - - var startKey string - regionInfos := make([]RegionInfo, 0, len(ss)) - for i, s := range ss { - var ri RegionInfo - ri.Region = tikv.NewRegionVerID(uint64(i), 1, 1) - ri.Meta = nil - ri.AllStores = randomStores(storeCount, replicaNum) - - var keyRange kv.KeyRange - if len(startKey) == 0 { - keyRange.StartKey = nil - } else { - keyRange.StartKey = kv.Key(startKey) - } - keyRange.EndKey = kv.Key(s) - ri.Ranges = NewKeyRanges([]kv.KeyRange{keyRange}) - regionInfos = append(regionInfos, ri) - startKey = s - } - return regionInfos -} - -func calcReginCount(tasks []*batchCopTask) int { - count := 0 - for _, task := range tasks { - count += len(task.regionInfos) - } - return count -} - -func TestBalanceBatchCopTaskWithContinuity(t *testing.T) { - for replicaNum := 1; replicaNum < 6; replicaNum++ { - storeCount := 10 - regionCount := 100000 - storeTasks := buildStoreTaskMap(storeCount) - regionInfos := buildRegionInfos(storeCount, regionCount, replicaNum) - tasks, score := balanceBatchCopTaskWithContinuity(storeTasks, regionInfos, 20) - require.True(t, isBalance(score)) - require.Equal(t, regionCount, calcReginCount(tasks)) - } - - { - storeCount := 10 - regionCount := 100 - replicaNum := 2 - storeTasks := buildStoreTaskMap(storeCount) - regionInfos := buildRegionInfos(storeCount, regionCount, replicaNum) - tasks, _ := balanceBatchCopTaskWithContinuity(storeTasks, regionInfos, 20) - require.True(t, tasks == nil) - } -} - -func TestBalanceBatchCopTaskWithEmptyTaskSet(t *testing.T) { - { - var nilTaskSet []*batchCopTask - nilResult := balanceBatchCopTask(nil, nil, nilTaskSet, false, 0) - require.True(t, nilResult == nil) - } - - { - emptyTaskSet := make([]*batchCopTask, 0) - emptyResult := balanceBatchCopTask(nil, nil, emptyTaskSet, false, 0) - require.True(t, emptyResult != nil) - require.True(t, len(emptyResult) == 0) - } -} - -func TestDeepCopyStoreTaskMap(t *testing.T) { - storeTasks1 := buildStoreTaskMap(10) - for _, task := range storeTasks1 { - task.regionInfos = append(task.regionInfos, RegionInfo{}) - } - - storeTasks2 := deepCopyStoreTaskMap(storeTasks1) - for _, task := range storeTasks2 { - task.regionInfos = append(task.regionInfos, RegionInfo{}) - } - - for _, task := range storeTasks1 { - require.Equal(t, 1, len(task.regionInfos)) - } - - for _, task := range storeTasks2 { - require.Equal(t, 2, len(task.regionInfos)) - } -} - -// Make sure no duplicated ip:addr. -func generateOneAddr() string { - var ip string - for i := 0; i < 4; i++ { - if i != 0 { - ip += "." - } - ip += strconv.Itoa(rand.Intn(255)) - } - return ip + ":" + strconv.Itoa(rand.Intn(65535)) -} - -func generateDifferentAddrs(num int) (res []string) { - addrMap := make(map[string]struct{}) - for len(addrMap) < num { - addr := generateOneAddr() - if _, ok := addrMap[addr]; !ok { - addrMap[addr] = struct{}{} - } - } - for addr := range addrMap { - res = append(res, addr) - } - return -} - -func TestConsistentHash(t *testing.T) { - allAddrs := generateDifferentAddrs(100) - - computeNodes := allAddrs[:30] - storageNodes := allAddrs[30:] - firstRoundMap := make(map[string]string) - for round := 0; round < 100; round++ { - hasher := consistent.New() - rand.Shuffle(len(computeNodes), func(i, j int) { - computeNodes[i], computeNodes[j] = computeNodes[j], computeNodes[i] - }) - for _, computeNode := range computeNodes { - hasher.Add(computeNode) - } - for _, storageNode := range storageNodes { - computeNode, err := hasher.Get(storageNode) - require.NoError(t, err) - if round == 0 { - firstRoundMap[storageNode] = computeNode - } else { - firstRoundAddr, ok := firstRoundMap[storageNode] - require.True(t, ok) - require.Equal(t, firstRoundAddr, computeNode) - } - } - } -} - -func TestDispatchPolicyRR(t *testing.T) { - allAddrs := generateDifferentAddrs(100) - for i := 0; i < 100; i++ { - regCnt := rand.Intn(10000) - regIDs := make([]tikv.RegionVerID, 0, regCnt) - for i := 0; i < regCnt; i++ { - regIDs = append(regIDs, tikv.NewRegionVerID(uint64(i), 0, 0)) - } - - rpcCtxs, err := getTiFlashComputeRPCContextByRoundRobin(regIDs, allAddrs) - require.NoError(t, err) - require.Equal(t, len(rpcCtxs), len(regIDs)) - checkMap := make(map[string]int, len(rpcCtxs)) - for _, c := range rpcCtxs { - if v, ok := checkMap[c.Addr]; !ok { - checkMap[c.Addr] = 1 - } else { - checkMap[c.Addr] = v + 1 - } - } - actCnt := 0 - for _, v := range checkMap { - actCnt += v - } - require.Equal(t, regCnt, actCnt) - if len(regIDs) < len(allAddrs) { - require.Equal(t, len(regIDs), len(checkMap)) - exp := -1 - for _, v := range checkMap { - if exp == -1 { - exp = v - } else { - require.Equal(t, exp, v) - } - } - } else { - // Using RR, it means region cnt for each tiflash_compute node should be almost same. - minV := regCnt - for _, v := range checkMap { - if v < minV { - minV = v - } - } - for k, v := range checkMap { - checkMap[k] = v - minV - } - for _, v := range checkMap { - require.True(t, v == 0 || v == 1) - } - } - } -} - -func TestTopoFetcherBackoff(t *testing.T) { - fetchTopoBo := backoff.NewBackofferWithVars(context.Background(), fetchTopoMaxBackoff, nil) - expectErr := errors.New("Cannot find proper topo from AutoScaler") - var retryNum int - start := time.Now() - for { - retryNum++ - if err := fetchTopoBo.Backoff(tikv.BoTiFlashRPC(), expectErr); err != nil { - break - } - logutil.BgLogger().Info("TestTopoFetcherBackoff", zap.Any("retryNum", retryNum)) - } - dura := time.Since(start) - // fetchTopoMaxBackoff is milliseconds. - require.GreaterOrEqual(t, dura, time.Duration(fetchTopoMaxBackoff*1000)) - require.GreaterOrEqual(t, dura, 30*time.Second) - require.LessOrEqual(t, dura, 50*time.Second) -} diff --git a/store/copr/copr_test/BUILD.bazel b/store/copr/copr_test/BUILD.bazel deleted file mode 100644 index e0ebf384543a2..0000000000000 --- a/store/copr/copr_test/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "copr_test_test", - timeout = "short", - srcs = [ - "coprocessor_test.go", - "main_test.go", - ], - flaky = True, - deps = [ - "//config", - "//kv", - "//store/copr", - "//store/mockstore", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/copr/copr_test/coprocessor_test.go b/store/copr/copr_test/coprocessor_test.go deleted file mode 100644 index 30247b8694a72..0000000000000 --- a/store/copr/copr_test/coprocessor_test.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package copr_test - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/mockstore" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" -) - -func TestBuildCopIteratorWithRowCountHint(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - store, err := mockstore.NewMockStore( - mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithMultiRegions(c, []byte("g"), []byte("n"), []byte("t")) - }), - ) - require.NoError(t, err) - defer require.NoError(t, store.Close()) - copClient := store.GetClient().(*copr.CopClient) - ctx := context.Background() - killed := uint32(0) - vars := kv.NewVariables(&killed) - opt := &kv.ClientSendOption{} - - ranges := copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") - req := &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, copr.CopSmallTaskRow}), - Concurrency: 15, - } - it, errRes := copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - conc, smallConc := it.GetConcurrency() - rateLimit := it.GetSendRate() - require.Equal(t, conc, 1) - require.Equal(t, smallConc, 1) - require.Equal(t, rateLimit.GetCapacity(), 2) - - ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), - Concurrency: 15, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - conc, smallConc = it.GetConcurrency() - rateLimit = it.GetSendRate() - require.Equal(t, conc, 1) - require.Equal(t, smallConc, 2) - require.Equal(t, rateLimit.GetCapacity(), 3) - - // cross-region long range - ranges = copr.BuildKeyRanges("a", "z") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{10}), - Concurrency: 15, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - conc, smallConc = it.GetConcurrency() - rateLimit = it.GetSendRate() - require.Equal(t, conc, 1) - require.Equal(t, smallConc, 2) - require.Equal(t, rateLimit.GetCapacity(), 3) - - ranges = copr.BuildKeyRanges("a", "z") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{copr.CopSmallTaskRow + 1}), - Concurrency: 15, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - conc, smallConc = it.GetConcurrency() - rateLimit = it.GetSendRate() - require.Equal(t, conc, 4) - require.Equal(t, smallConc, 0) - require.Equal(t, rateLimit.GetCapacity(), 4) -} - -func TestBuildCopIteratorWithBatchStoreCopr(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - store, err := mockstore.NewMockStore( - mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithMultiRegions(c, []byte("g"), []byte("n"), []byte("t")) - }), - ) - require.NoError(t, err) - defer require.NoError(t, store.Close()) - copClient := store.GetClient().(*copr.CopClient) - ctx := context.Background() - killed := uint32(0) - vars := kv.NewVariables(&killed) - opt := &kv.ClientSendOption{} - - ranges := copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") - req := &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), - Concurrency: 15, - StoreBatchSize: 1, - } - it, errRes := copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - tasks := it.GetTasks() - require.Equal(t, len(tasks), 2) - require.Equal(t, len(tasks[0].ToPBBatchTasks()), 1) - require.Equal(t, tasks[0].RowCountHint, 5) - require.Equal(t, len(tasks[1].ToPBBatchTasks()), 1) - require.Equal(t, tasks[1].RowCountHint, 9) - - ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), - Concurrency: 15, - StoreBatchSize: 3, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - tasks = it.GetTasks() - require.Equal(t, len(tasks), 1) - require.Equal(t, len(tasks[0].ToPBBatchTasks()), 3) - require.Equal(t, tasks[0].RowCountHint, 14) - - // paging will disable store batch. - ranges = copr.BuildKeyRanges("a", "c", "d", "e", "h", "x", "y", "z") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 1, 3, 3}), - Concurrency: 15, - StoreBatchSize: 3, - Paging: struct { - Enable bool - MinPagingSize uint64 - MaxPagingSize uint64 - }{ - Enable: true, - MinPagingSize: 1, - MaxPagingSize: 1024, - }, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - tasks = it.GetTasks() - require.Equal(t, len(tasks), 4) - - // only small tasks will be batched. - ranges = copr.BuildKeyRanges("a", "b", "h", "i", "o", "p") - req = &kv.Request{ - Tp: kv.ReqTypeDAG, - KeyRanges: kv.NewNonParitionedKeyRangesWithHint(ranges, []int{1, 33, 32}), - Concurrency: 15, - StoreBatchSize: 3, - } - it, errRes = copClient.BuildCopIterator(ctx, req, vars, opt) - require.Nil(t, errRes) - tasks = it.GetTasks() - require.Equal(t, len(tasks), 2) - require.Equal(t, len(tasks[0].ToPBBatchTasks()), 1) - require.Equal(t, len(tasks[1].ToPBBatchTasks()), 0) -} diff --git a/store/copr/copr_test/main_test.go b/store/copr/copr_test/main_test.go deleted file mode 100644 index d2d70af80a4e9..0000000000000 --- a/store/copr/copr_test/main_test.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package copr_test - -import ( - "flag" - "testing" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testmain.ShortCircuitForBench(m) - - testsetup.SetupForCommonTest() - - flag.Parse() - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - tikv.EnableFailpoints() - opts := []goleak.Option{ - // TODO: figure the reason and shorten this list - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/tikv/client-go/v2/internal/retry.newBackoffFn.func1"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/v3.waitRetryBackoff"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*addrConn).resetTransport"), - goleak.IgnoreTopFunction("google.golang.org/grpc.(*ccBalancerWrapper).watcher"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*controlBuffer).get"), - goleak.IgnoreTopFunction("google.golang.org/grpc/internal/transport.(*http2Client).keepalive"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/store/copr/coprocessor.go b/store/copr/coprocessor.go deleted file mode 100644 index 9a6135a991149..0000000000000 --- a/store/copr/coprocessor.go +++ /dev/null @@ -1,2091 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "context" - "fmt" - "math" - "net" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/gogo/protobuf/proto" - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/kvproto/pkg/errorpb" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/domain/resourcegroup" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - tidbmetrics "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/variable" - copr_metrics "github.com/pingcap/tidb/store/copr/metrics" - "github.com/pingcap/tidb/store/driver/backoff" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/driver/options" - "github.com/pingcap/tidb/util/execdetails" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/paging" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tidb/util/trxevents" - "github.com/pingcap/tipb/go-tipb" - "github.com/tikv/client-go/v2/metrics" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - "github.com/tikv/client-go/v2/tikvrpc/interceptor" - "github.com/tikv/client-go/v2/txnkv/txnlock" - "github.com/tikv/client-go/v2/txnkv/txnsnapshot" - "github.com/tikv/client-go/v2/util" - "go.uber.org/zap" -) - -// Maximum total sleep time(in ms) for kv/cop commands. -const ( - copBuildTaskMaxBackoff = 5000 - CopNextMaxBackoff = 20000 - CopSmallTaskRow = 32 // 32 is the initial batch size of TiKV - smallTaskSigma = 0.5 - smallConcPerCore = 20 -) - -// CopClient is coprocessor client. -type CopClient struct { - kv.RequestTypeSupportedChecker - store *Store - replicaReadSeed uint32 -} - -// Send builds the request and gets the coprocessor iterator response. -func (c *CopClient) Send(ctx context.Context, req *kv.Request, variables interface{}, option *kv.ClientSendOption) kv.Response { - vars, ok := variables.(*tikv.Variables) - if !ok { - return copErrorResponse{errors.Errorf("unsupported variables:%+v", variables)} - } - if req.StoreType == kv.TiFlash && req.BatchCop { - logutil.BgLogger().Debug("send batch requests") - return c.sendBatch(ctx, req, vars, option) - } - ctx = context.WithValue(ctx, tikv.TxnStartKey(), req.StartTs) - ctx = context.WithValue(ctx, util.RequestSourceKey, req.RequestSource) - ctx = interceptor.WithRPCInterceptor(ctx, interceptor.GetRPCInterceptorFromCtx(ctx)) - enabledRateLimitAction := option.EnabledRateLimitAction - sessionMemTracker := option.SessionMemTracker - it, errRes := c.BuildCopIterator(ctx, req, vars, option) - if errRes != nil { - return errRes - } - ctx = context.WithValue(ctx, tikv.RPCCancellerCtxKey{}, it.rpcCancel) - if sessionMemTracker != nil && enabledRateLimitAction { - sessionMemTracker.FallbackOldAndSetNewAction(it.actionOnExceed) - } - it.open(ctx, enabledRateLimitAction, option.EnableCollectExecutionInfo) - return it -} - -// BuildCopIterator builds the iterator without calling `open`. -func (c *CopClient) BuildCopIterator(ctx context.Context, req *kv.Request, vars *tikv.Variables, option *kv.ClientSendOption) (*copIterator, kv.Response) { - eventCb := option.EventCb - failpoint.Inject("DisablePaging", func(_ failpoint.Value) { - req.Paging.Enable = false - }) - if req.StoreType == kv.TiDB { - // coprocessor on TiDB doesn't support paging - req.Paging.Enable = false - } - if req.Tp != kv.ReqTypeDAG { - // coprocessor request but type is not DAG - req.Paging.Enable = false - } - failpoint.Inject("checkKeyRangeSortedForPaging", func(_ failpoint.Value) { - if req.Paging.Enable { - if !req.KeyRanges.IsFullySorted() { - logutil.BgLogger().Fatal("distsql request key range not sorted!") - } - } - }) - if !checkStoreBatchCopr(req) { - req.StoreBatchSize = 0 - } - - bo := backoff.NewBackofferWithVars(ctx, copBuildTaskMaxBackoff, vars) - var ( - tasks []*copTask - err error - ) - tryRowHint := optRowHint(req) - elapsed := time.Duration(0) - buildOpt := &buildCopTaskOpt{ - req: req, - cache: c.store.GetRegionCache(), - eventCb: eventCb, - respChan: req.KeepOrder, - elapsed: &elapsed, - } - buildTaskFunc := func(ranges []kv.KeyRange, hints []int) error { - keyRanges := NewKeyRanges(ranges) - if tryRowHint { - buildOpt.rowHints = hints - } - tasksFromRanges, err := buildCopTasks(bo, keyRanges, buildOpt) - if err != nil { - return err - } - if len(tasks) == 0 { - tasks = tasksFromRanges - return nil - } - tasks = append(tasks, tasksFromRanges...) - return nil - } - // Here we build the task by partition, not directly by region. - // This is because it's possible that TiDB merge multiple small partition into one region which break some assumption. - // Keep it split by partition would be more safe. - err = req.KeyRanges.ForEachPartitionWithErr(buildTaskFunc) - // only batch store requests in first build. - req.StoreBatchSize = 0 - reqType := "null" - if req.ClosestReplicaReadAdjuster != nil { - reqType = "miss" - if req.ClosestReplicaReadAdjuster(req, len(tasks)) { - reqType = "hit" - } - } - tidbmetrics.DistSQLCoprClosestReadCounter.WithLabelValues(reqType).Inc() - if err != nil { - return nil, copErrorResponse{err} - } - it := &copIterator{ - store: c.store, - req: req, - concurrency: req.Concurrency, - finishCh: make(chan struct{}), - vars: vars, - memTracker: req.MemTracker, - replicaReadSeed: c.replicaReadSeed, - rpcCancel: tikv.NewRPCanceller(), - buildTaskElapsed: *buildOpt.elapsed, - runawayChecker: req.RunawayChecker, - } - it.tasks = tasks - if it.concurrency > len(tasks) { - it.concurrency = len(tasks) - } - if tryRowHint { - var smallTasks int - smallTasks, it.smallTaskConcurrency = smallTaskConcurrency(tasks, c.store.numcpu) - if len(tasks)-smallTasks < it.concurrency { - it.concurrency = len(tasks) - smallTasks - } - } - if it.concurrency < 1 { - // Make sure that there is at least one worker. - it.concurrency = 1 - } - - if it.req.KeepOrder { - // Don't set high concurrency for the keep order case. It wastes a lot of memory and gains nothing. - // TL;DR - // Because for a keep order coprocessor request, the cop tasks are handled one by one, if we set a - // higher concurrency, the data is just cached and not consumed for a while, this increase the memory usage. - // Set concurrency to 2 can reduce the memory usage and I've tested that it does not necessarily - // decrease the performance. - // For ReqTypeAnalyze, we keep its concurrency to avoid slow analyze(see https://github.com/pingcap/tidb/issues/40162 for details). - if it.concurrency > 2 && it.req.Tp != kv.ReqTypeAnalyze { - oldConcurrency := it.concurrency - partitionNum := req.KeyRanges.PartitionNum() - if partitionNum > it.concurrency { - partitionNum = it.concurrency - } - it.concurrency = 2 - if it.concurrency < partitionNum { - it.concurrency = partitionNum - } - - failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { - if val.(bool) { - // When the concurrency is too small, test case tests/realtikvtest/sessiontest.TestCoprocessorOOMAction can't trigger OOM condition - it.concurrency = oldConcurrency - } - }) - } - if it.smallTaskConcurrency > 20 { - it.smallTaskConcurrency = 20 - } - it.sendRate = util.NewRateLimit(2 * (it.concurrency + it.smallTaskConcurrency)) - it.respChan = nil - } else { - it.respChan = make(chan *copResponse) - it.sendRate = util.NewRateLimit(it.concurrency + it.smallTaskConcurrency) - } - it.actionOnExceed = newRateLimitAction(uint(it.sendRate.GetCapacity())) - return it, nil -} - -// copTask contains a related Region and KeyRange for a kv.Request. -type copTask struct { - taskID uint64 - region tikv.RegionVerID - bucketsVer uint64 - ranges *KeyRanges - - respChan chan *copResponse - storeAddr string - cmdType tikvrpc.CmdType - storeType kv.StoreType - - eventCb trxevents.EventCallback - paging bool - pagingSize uint64 - pagingTaskIdx uint32 - - partitionIndex int64 // used by balanceBatchCopTask in PartitionTableScan - requestSource util.RequestSource - RowCountHint int // used for extra concurrency of small tasks, -1 for unknown row count - batchTaskList map[uint64]*batchedCopTask - - // when this task is batched and the leader's wait duration exceeds the load-based threshold, - // we set this field to the target replica store ID and redirect the request to the replica. - redirect2Replica *uint64 - busyThreshold time.Duration - meetLockFallback bool - - // timeout value for one kv readonly request - tikvClientReadTimeout uint64 - // firstReadType is used to indicate the type of first read when retrying. - firstReadType string -} - -type batchedCopTask struct { - task *copTask - region coprocessor.RegionInfo - storeID uint64 - peer *metapb.Peer - loadBasedReplicaRetry bool -} - -func (r *copTask) String() string { - return fmt.Sprintf("region(%d %d %d) ranges(%d) store(%s)", - r.region.GetID(), r.region.GetConfVer(), r.region.GetVer(), r.ranges.Len(), r.storeAddr) -} - -func (r *copTask) ToPBBatchTasks() []*coprocessor.StoreBatchTask { - if len(r.batchTaskList) == 0 { - return nil - } - pbTasks := make([]*coprocessor.StoreBatchTask, 0, len(r.batchTaskList)) - for _, task := range r.batchTaskList { - storeBatchTask := &coprocessor.StoreBatchTask{ - RegionId: task.region.GetRegionId(), - RegionEpoch: task.region.GetRegionEpoch(), - Peer: task.peer, - Ranges: task.region.GetRanges(), - TaskId: task.task.taskID, - } - pbTasks = append(pbTasks, storeBatchTask) - } - return pbTasks -} - -// rangesPerTask limits the length of the ranges slice sent in one copTask. -const rangesPerTask = 25000 - -type buildCopTaskOpt struct { - req *kv.Request - cache *RegionCache - eventCb trxevents.EventCallback - respChan bool - rowHints []int - elapsed *time.Duration - // ignoreTiKVClientReadTimeout is used to ignore tikv_client_read_timeout configuration, use default timeout instead. - ignoreTiKVClientReadTimeout bool -} - -func buildCopTasks(bo *Backoffer, ranges *KeyRanges, opt *buildCopTaskOpt) ([]*copTask, error) { - req, cache, eventCb, hints := opt.req, opt.cache, opt.eventCb, opt.rowHints - start := time.Now() - cmdType := tikvrpc.CmdCop - if req.StoreType == kv.TiDB { - return buildTiDBMemCopTasks(ranges, req) - } - rangesLen := ranges.Len() - // something went wrong, disable hints to avoid out of range index. - if len(hints) != rangesLen { - hints = nil - } - - rangesPerTaskLimit := rangesPerTask - failpoint.Inject("setRangesPerTask", func(val failpoint.Value) { - if v, ok := val.(int); ok { - rangesPerTaskLimit = v - } - }) - - // TODO(youjiali1995): is there any request type that needn't be splitted by buckets? - locs, err := cache.SplitKeyRangesByBuckets(bo, ranges) - if err != nil { - return nil, errors.Trace(err) - } - // Channel buffer is 2 for handling region split. - // In a common case, two region split tasks will not be blocked. - chanSize := 2 - // in paging request, a request will be returned in multi batches, - // enlarge the channel size to avoid the request blocked by buffer full. - if req.Paging.Enable { - chanSize = 18 - } - - var builder taskBuilder - if req.StoreBatchSize > 0 && hints != nil { - builder = newBatchTaskBuilder(bo, req, cache, req.ReplicaRead) - } else { - builder = newLegacyTaskBuilder(len(locs)) - } - origRangeIdx := 0 - for _, loc := range locs { - // TiKV will return gRPC error if the message is too large. So we need to limit the length of the ranges slice - // to make sure the message can be sent successfully. - rLen := loc.Ranges.Len() - // If this is a paging request, we set the paging size to minPagingSize, - // the size will grow every round. - pagingSize := uint64(0) - if req.Paging.Enable { - pagingSize = req.Paging.MinPagingSize - } - for i := 0; i < rLen; { - nextI := mathutil.Min(i+rangesPerTaskLimit, rLen) - hint := -1 - // calculate the row count hint - if hints != nil { - startKey, endKey := loc.Ranges.RefAt(i).StartKey, loc.Ranges.RefAt(nextI-1).EndKey - // move to the previous range if startKey of current range is lower than endKey of previous location. - // In the following example, task1 will move origRangeIdx to region(i, z). - // When counting the row hint for task2, we need to move origRangeIdx back to region(a, h). - // |<- region(a, h) ->| |<- region(i, z) ->| - // |<- task1 ->| |<- task2 ->| ... - if origRangeIdx > 0 && ranges.RefAt(origRangeIdx-1).EndKey.Cmp(startKey) > 0 { - origRangeIdx-- - } - hint = 0 - for nextOrigRangeIdx := origRangeIdx; nextOrigRangeIdx < ranges.Len(); nextOrigRangeIdx++ { - rangeStart := ranges.RefAt(nextOrigRangeIdx).StartKey - if rangeStart.Cmp(endKey) > 0 { - origRangeIdx = nextOrigRangeIdx - break - } - hint += hints[nextOrigRangeIdx] - } - } - task := &copTask{ - region: loc.Location.Region, - bucketsVer: loc.getBucketVersion(), - ranges: loc.Ranges.Slice(i, nextI), - cmdType: cmdType, - storeType: req.StoreType, - eventCb: eventCb, - paging: req.Paging.Enable, - pagingSize: pagingSize, - requestSource: req.RequestSource, - RowCountHint: hint, - busyThreshold: req.StoreBusyThreshold, - } - if !opt.ignoreTiKVClientReadTimeout { - task.tikvClientReadTimeout = req.TiKVClientReadTimeout - } - // only keep-order need chan inside task. - // tasks by region error will reuse the channel of parent task. - if req.KeepOrder && opt.respChan { - task.respChan = make(chan *copResponse, chanSize) - } - if err = builder.handle(task); err != nil { - return nil, err - } - i = nextI - if req.Paging.Enable { - if req.LimitSize != 0 && req.LimitSize < pagingSize { - // disable paging for small limit. - task.paging = false - task.pagingSize = 0 - } else { - pagingSize = paging.GrowPagingSize(pagingSize, req.Paging.MaxPagingSize) - } - } - } - } - - if req.Desc { - builder.reverse() - } - tasks := builder.build() - elapsed := time.Since(start) - if elapsed > time.Millisecond*500 { - logutil.BgLogger().Warn("buildCopTasks takes too much time", - zap.Duration("elapsed", elapsed), - zap.Int("range len", rangesLen), - zap.Int("task len", len(tasks))) - } - if elapsed > time.Millisecond { - defer tracing.StartRegion(bo.GetCtx(), "copr.buildCopTasks").End() - } - if opt.elapsed != nil { - *opt.elapsed = *opt.elapsed + elapsed - } - metrics.TxnRegionsNumHistogramWithCoprocessor.Observe(float64(builder.regionNum())) - return tasks, nil -} - -type taskBuilder interface { - handle(*copTask) error - reverse() - build() []*copTask - regionNum() int -} - -type legacyTaskBuilder struct { - tasks []*copTask -} - -func newLegacyTaskBuilder(hint int) *legacyTaskBuilder { - return &legacyTaskBuilder{ - tasks: make([]*copTask, 0, hint), - } -} - -func (b *legacyTaskBuilder) handle(task *copTask) error { - b.tasks = append(b.tasks, task) - return nil -} - -func (b *legacyTaskBuilder) regionNum() int { - return len(b.tasks) -} - -func (b *legacyTaskBuilder) reverse() { - reverseTasks(b.tasks) -} - -func (b *legacyTaskBuilder) build() []*copTask { - return b.tasks -} - -type storeReplicaKey struct { - storeID uint64 - replicaRead bool -} - -type batchStoreTaskBuilder struct { - bo *Backoffer - req *kv.Request - cache *RegionCache - taskID uint64 - limit int - store2Idx map[storeReplicaKey]int - tasks []*copTask - replicaRead kv.ReplicaReadType -} - -func newBatchTaskBuilder(bo *Backoffer, req *kv.Request, cache *RegionCache, replicaRead kv.ReplicaReadType) *batchStoreTaskBuilder { - return &batchStoreTaskBuilder{ - bo: bo, - req: req, - cache: cache, - taskID: 0, - limit: req.StoreBatchSize, - store2Idx: make(map[storeReplicaKey]int, 16), - tasks: make([]*copTask, 0, 16), - replicaRead: replicaRead, - } -} - -func (b *batchStoreTaskBuilder) handle(task *copTask) (err error) { - b.taskID++ - task.taskID = b.taskID - handled := false - defer func() { - if !handled && err == nil { - // fallback to non-batch way. It's mainly caused by region miss. - b.tasks = append(b.tasks, task) - } - }() - // only batch small tasks for memory control. - if b.limit <= 0 || !isSmallTask(task) { - return nil - } - batchedTask, err := b.cache.BuildBatchTask(b.bo, b.req, task, b.replicaRead) - if err != nil { - return err - } - if batchedTask == nil { - return nil - } - key := storeReplicaKey{ - storeID: batchedTask.storeID, - replicaRead: batchedTask.loadBasedReplicaRetry, - } - if idx, ok := b.store2Idx[key]; !ok || len(b.tasks[idx].batchTaskList) >= b.limit { - if batchedTask.loadBasedReplicaRetry { - // If the task is dispatched to leader because all followers are busy, - // task.redirect2Replica != nil means the busy threshold shouldn't take effect again. - batchedTask.task.redirect2Replica = &batchedTask.storeID - } - b.tasks = append(b.tasks, batchedTask.task) - b.store2Idx[key] = len(b.tasks) - 1 - } else { - if b.tasks[idx].batchTaskList == nil { - b.tasks[idx].batchTaskList = make(map[uint64]*batchedCopTask, b.limit) - // disable paging for batched task. - b.tasks[idx].paging = false - b.tasks[idx].pagingSize = 0 - } - if task.RowCountHint > 0 { - b.tasks[idx].RowCountHint += task.RowCountHint - } - b.tasks[idx].batchTaskList[task.taskID] = batchedTask - } - handled = true - return nil -} - -func (b *batchStoreTaskBuilder) regionNum() int { - // we allocate b.taskID for each region task, so the final b.taskID is equal to the related region number. - return int(b.taskID) -} - -func (b *batchStoreTaskBuilder) reverse() { - reverseTasks(b.tasks) -} - -func (b *batchStoreTaskBuilder) build() []*copTask { - return b.tasks -} - -func buildTiDBMemCopTasks(ranges *KeyRanges, req *kv.Request) ([]*copTask, error) { - servers, err := infosync.GetAllServerInfo(context.Background()) - if err != nil { - return nil, err - } - cmdType := tikvrpc.CmdCop - tasks := make([]*copTask, 0, len(servers)) - for _, ser := range servers { - if req.TiDBServerID > 0 && req.TiDBServerID != ser.ServerIDGetter() { - continue - } - - addr := net.JoinHostPort(ser.IP, strconv.FormatUint(uint64(ser.StatusPort), 10)) - tasks = append(tasks, &copTask{ - ranges: ranges, - respChan: make(chan *copResponse, 2), - cmdType: cmdType, - storeType: req.StoreType, - storeAddr: addr, - RowCountHint: -1, - }) - } - return tasks, nil -} - -func reverseTasks(tasks []*copTask) { - for i := 0; i < len(tasks)/2; i++ { - j := len(tasks) - i - 1 - tasks[i], tasks[j] = tasks[j], tasks[i] - } -} - -func isSmallTask(task *copTask) bool { - // strictly, only RowCountHint == -1 stands for unknown task rows, - // but when RowCountHint == 0, it may be caused by initialized value, - // to avoid the future bugs, let the tasks with RowCountHint == 0 be non-small tasks. - return task.RowCountHint > 0 && - (len(task.batchTaskList) == 0 && task.RowCountHint <= CopSmallTaskRow) || - (len(task.batchTaskList) > 0 && task.RowCountHint <= 2*CopSmallTaskRow) -} - -// smallTaskConcurrency counts the small tasks of tasks, -// then returns the task count and extra concurrency for small tasks. -func smallTaskConcurrency(tasks []*copTask, numcpu int) (int, int) { - res := 0 - for _, task := range tasks { - if isSmallTask(task) { - res++ - } - } - if res == 0 { - return 0, 0 - } - // Calculate the extra concurrency for small tasks - // extra concurrency = tasks / (1 + sigma * sqrt(log(tasks ^ 2))) - extraConc := int(float64(res) / (1 + smallTaskSigma*math.Sqrt(2*math.Log(float64(res))))) - if numcpu <= 0 { - numcpu = 1 - } - smallTaskConcurrencyLimit := smallConcPerCore * numcpu - if extraConc > smallTaskConcurrencyLimit { - extraConc = smallTaskConcurrencyLimit - } - return res, extraConc -} - -// CopInfo is used to expose functions of copIterator. -type CopInfo interface { - // GetConcurrency returns the concurrency and small task concurrency. - GetConcurrency() (int, int) - // GetStoreBatchInfo returns the batched and fallback num. - GetStoreBatchInfo() (uint64, uint64) - // GetBuildTaskElapsed returns the duration of building task. - GetBuildTaskElapsed() time.Duration -} - -type copIterator struct { - store *Store - req *kv.Request - concurrency int - smallTaskConcurrency int - finishCh chan struct{} - - // If keepOrder, results are stored in copTask.respChan, read them out one by one. - tasks []*copTask - // curr indicates the curr id of the finished copTask - curr int - - // sendRate controls the sending rate of copIteratorTaskSender - sendRate *util.RateLimit - - // Otherwise, results are stored in respChan. - respChan chan *copResponse - - vars *tikv.Variables - - memTracker *memory.Tracker - - replicaReadSeed uint32 - - rpcCancel *tikv.RPCCanceller - - wg sync.WaitGroup - // closed represents when the Close is called. - // There are two cases we need to close the `finishCh` channel, one is when context is done, the other one is - // when the Close is called. we use atomic.CompareAndSwap `closed` to make sure the channel is not closed twice. - closed uint32 - - resolvedLocks util.TSSet - committedLocks util.TSSet - - actionOnExceed *rateLimitAction - pagingTaskIdx uint32 - - buildTaskElapsed time.Duration - storeBatchedNum atomic.Uint64 - storeBatchedFallbackNum atomic.Uint64 - - runawayChecker *resourcegroup.RunawayChecker -} - -// copIteratorWorker receives tasks from copIteratorTaskSender, handles tasks and sends the copResponse to respChan. -type copIteratorWorker struct { - taskCh <-chan *copTask - wg *sync.WaitGroup - store *Store - req *kv.Request - respChan chan<- *copResponse - finishCh <-chan struct{} - vars *tikv.Variables - kvclient *txnsnapshot.ClientHelper - - memTracker *memory.Tracker - - replicaReadSeed uint32 - - enableCollectExecutionInfo bool - pagingTaskIdx *uint32 - - storeBatchedNum *atomic.Uint64 - storeBatchedFallbackNum *atomic.Uint64 -} - -// copIteratorTaskSender sends tasks to taskCh then wait for the workers to exit. -type copIteratorTaskSender struct { - taskCh chan<- *copTask - smallTaskCh chan<- *copTask - wg *sync.WaitGroup - tasks []*copTask - finishCh <-chan struct{} - respChan chan<- *copResponse - sendRate *util.RateLimit -} - -type copResponse struct { - pbResp *coprocessor.Response - detail *CopRuntimeStats - startKey kv.Key - err error - respSize int64 - respTime time.Duration -} - -const sizeofExecDetails = int(unsafe.Sizeof(execdetails.ExecDetails{})) - -// GetData implements the kv.ResultSubset GetData interface. -func (rs *copResponse) GetData() []byte { - return rs.pbResp.Data -} - -// GetStartKey implements the kv.ResultSubset GetStartKey interface. -func (rs *copResponse) GetStartKey() kv.Key { - return rs.startKey -} - -func (rs *copResponse) GetCopRuntimeStats() *CopRuntimeStats { - return rs.detail -} - -// MemSize returns how many bytes of memory this response use -func (rs *copResponse) MemSize() int64 { - if rs.respSize != 0 { - return rs.respSize - } - if rs == finCopResp { - return 0 - } - - // ignore rs.err - rs.respSize += int64(cap(rs.startKey)) - if rs.detail != nil { - rs.respSize += int64(sizeofExecDetails) - } - if rs.pbResp != nil { - // Using a approximate size since it's hard to get a accurate value. - rs.respSize += int64(rs.pbResp.Size()) - } - return rs.respSize -} - -func (rs *copResponse) RespTime() time.Duration { - return rs.respTime -} - -const minLogCopTaskTime = 300 * time.Millisecond - -// When the worker finished `handleTask`, we need to notify the copIterator that there is one task finished. -// For the non-keep-order case, we send a finCopResp into the respCh after `handleTask`. When copIterator recv -// finCopResp from the respCh, it will be aware that there is one task finished. -var finCopResp *copResponse - -func init() { - finCopResp = &copResponse{} -} - -// run is a worker function that get a copTask from channel, handle it and -// send the result back. -func (worker *copIteratorWorker) run(ctx context.Context) { - defer func() { - failpoint.Inject("ticase-4169", func(val failpoint.Value) { - if val.(bool) { - worker.memTracker.Consume(10 * MockResponseSizeForTest) - worker.memTracker.Consume(10 * MockResponseSizeForTest) - } - }) - worker.wg.Done() - }() - for task := range worker.taskCh { - respCh := worker.respChan - if respCh == nil { - respCh = task.respChan - } - worker.handleTask(ctx, task, respCh) - if worker.respChan != nil { - // When a task is finished by the worker, send a finCopResp into channel to notify the copIterator that - // there is a task finished. - worker.sendToRespCh(finCopResp, worker.respChan, false) - } - if task.respChan != nil { - close(task.respChan) - } - if worker.finished() { - return - } - } -} - -// open starts workers and sender goroutines. -func (it *copIterator) open(ctx context.Context, enabledRateLimitAction, enableCollectExecutionInfo bool) { - taskCh := make(chan *copTask, 1) - smallTaskCh := make(chan *copTask, 1) - it.wg.Add(it.concurrency + it.smallTaskConcurrency) - // Start it.concurrency number of workers to handle cop requests. - for i := 0; i < it.concurrency+it.smallTaskConcurrency; i++ { - var ch chan *copTask - if i < it.concurrency { - ch = taskCh - } else { - ch = smallTaskCh - } - worker := &copIteratorWorker{ - taskCh: ch, - wg: &it.wg, - store: it.store, - req: it.req, - respChan: it.respChan, - finishCh: it.finishCh, - vars: it.vars, - kvclient: txnsnapshot.NewClientHelper(it.store.store, &it.resolvedLocks, &it.committedLocks, false), - memTracker: it.memTracker, - replicaReadSeed: it.replicaReadSeed, - enableCollectExecutionInfo: enableCollectExecutionInfo, - pagingTaskIdx: &it.pagingTaskIdx, - storeBatchedNum: &it.storeBatchedNum, - storeBatchedFallbackNum: &it.storeBatchedFallbackNum, - } - go worker.run(ctx) - } - taskSender := &copIteratorTaskSender{ - taskCh: taskCh, - smallTaskCh: smallTaskCh, - wg: &it.wg, - tasks: it.tasks, - finishCh: it.finishCh, - sendRate: it.sendRate, - } - taskSender.respChan = it.respChan - it.actionOnExceed.setEnabled(enabledRateLimitAction) - failpoint.Inject("ticase-4171", func(val failpoint.Value) { - if val.(bool) { - it.memTracker.Consume(10 * MockResponseSizeForTest) - it.memTracker.Consume(10 * MockResponseSizeForTest) - } - }) - go taskSender.run(it.req.ConnID) -} - -func (sender *copIteratorTaskSender) run(connID uint64) { - // Send tasks to feed the worker goroutines. - for _, t := range sender.tasks { - // we control the sending rate to prevent all tasks - // being done (aka. all of the responses are buffered) by copIteratorWorker. - // We keep the number of inflight tasks within the number of 2 * concurrency when Keep Order is true. - // If KeepOrder is false, the number equals the concurrency. - // It sends one more task if a task has been finished in copIterator.Next. - exit := sender.sendRate.GetToken(sender.finishCh) - if exit { - break - } - var sendTo chan<- *copTask - if isSmallTask(t) { - sendTo = sender.smallTaskCh - } else { - sendTo = sender.taskCh - } - exit = sender.sendToTaskCh(t, sendTo) - if exit { - break - } - if connID > 0 { - failpoint.Inject("pauseCopIterTaskSender", func() {}) - } - } - close(sender.taskCh) - close(sender.smallTaskCh) - - // Wait for worker goroutines to exit. - sender.wg.Wait() - if sender.respChan != nil { - close(sender.respChan) - } -} - -func (it *copIterator) recvFromRespCh(ctx context.Context, respCh <-chan *copResponse) (resp *copResponse, ok bool, exit bool) { - ticker := time.NewTicker(3 * time.Second) - defer ticker.Stop() - for { - select { - case resp, ok = <-respCh: - if it.memTracker != nil && resp != nil { - consumed := resp.MemSize() - failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { - if val.(bool) { - if resp != finCopResp { - consumed = MockResponseSizeForTest - } - } - }) - it.memTracker.Consume(-consumed) - } - return - case <-it.finishCh: - exit = true - return - case <-ticker.C: - if atomic.LoadUint32(it.vars.Killed) == 1 { - resp = &copResponse{err: derr.ErrQueryInterrupted} - ok = true - return - } - case <-ctx.Done(): - // We select the ctx.Done() in the thread of `Next` instead of in the worker to avoid the cost of `WithCancel`. - if atomic.CompareAndSwapUint32(&it.closed, 0, 1) { - close(it.finishCh) - } - exit = true - return - } - } -} - -// GetConcurrency returns the concurrency and small task concurrency. -func (it *copIterator) GetConcurrency() (int, int) { - return it.concurrency, it.smallTaskConcurrency -} - -// GetStoreBatchInfo returns the batched and fallback num. -func (it *copIterator) GetStoreBatchInfo() (uint64, uint64) { - return it.storeBatchedNum.Load(), it.storeBatchedFallbackNum.Load() -} - -// GetBuildTaskElapsed returns the duration of building task. -func (it *copIterator) GetBuildTaskElapsed() time.Duration { - return it.buildTaskElapsed -} - -// GetSendRate returns the rate-limit object. -func (it *copIterator) GetSendRate() *util.RateLimit { - return it.sendRate -} - -// GetTasks returns the built tasks. -func (it *copIterator) GetTasks() []*copTask { - return it.tasks -} - -func (sender *copIteratorTaskSender) sendToTaskCh(t *copTask, sendTo chan<- *copTask) (exit bool) { - select { - case sendTo <- t: - case <-sender.finishCh: - exit = true - } - return -} - -func (worker *copIteratorWorker) sendToRespCh(resp *copResponse, respCh chan<- *copResponse, checkOOM bool) (exit bool) { - if worker.memTracker != nil && checkOOM { - consumed := resp.MemSize() - failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { - if val.(bool) { - if resp != finCopResp { - consumed = MockResponseSizeForTest - } - } - }) - failpoint.Inject("ConsumeRandomPanic", nil) - worker.memTracker.Consume(consumed) - } - select { - case respCh <- resp: - case <-worker.finishCh: - exit = true - } - return -} - -// MockResponseSizeForTest mock the response size -const MockResponseSizeForTest = 100 * 1024 * 1024 - -// Next returns next coprocessor result. -// NOTE: Use nil to indicate finish, so if the returned ResultSubset is not nil, reader should continue to call Next(). -func (it *copIterator) Next(ctx context.Context) (kv.ResultSubset, error) { - var ( - resp *copResponse - ok bool - closed bool - ) - defer func() { - if resp == nil { - failpoint.Inject("ticase-4170", func(val failpoint.Value) { - if val.(bool) { - it.memTracker.Consume(10 * MockResponseSizeForTest) - it.memTracker.Consume(10 * MockResponseSizeForTest) - } - }) - } - }() - // wait unit at least 5 copResponse received. - failpoint.Inject("testRateLimitActionMockWaitMax", func(val failpoint.Value) { - if val.(bool) { - // we only need to trigger oom at least once. - if len(it.tasks) > 9 { - for it.memTracker.MaxConsumed() < 5*MockResponseSizeForTest { - time.Sleep(10 * time.Millisecond) - } - } - } - }) - // If data order matters, response should be returned in the same order as copTask slice. - // Otherwise all responses are returned from a single channel. - if it.respChan != nil { - // Get next fetched resp from chan - resp, ok, closed = it.recvFromRespCh(ctx, it.respChan) - if !ok || closed { - it.actionOnExceed.close() - return nil, nil - } - if resp == finCopResp { - it.actionOnExceed.destroyTokenIfNeeded(func() { - it.sendRate.PutToken() - }) - return it.Next(ctx) - } - } else { - for { - if it.curr >= len(it.tasks) { - // Resp will be nil if iterator is finishCh. - it.actionOnExceed.close() - return nil, nil - } - task := it.tasks[it.curr] - resp, ok, closed = it.recvFromRespCh(ctx, task.respChan) - if closed { - // Close() is already called, so Next() is invalid. - return nil, nil - } - if ok { - break - } - it.actionOnExceed.destroyTokenIfNeeded(func() { - it.sendRate.PutToken() - }) - // Switch to next task. - it.tasks[it.curr] = nil - it.curr++ - } - } - - if resp.err != nil { - return nil, errors.Trace(resp.err) - } - - err := it.store.CheckVisibility(it.req.StartTs) - if err != nil { - return nil, errors.Trace(err) - } - return resp, nil -} - -// Associate each region with an independent backoffer. In this way, when multiple regions are -// unavailable, TiDB can execute very quickly without blocking -func chooseBackoffer(ctx context.Context, backoffermap map[uint64]*Backoffer, task *copTask, worker *copIteratorWorker) *Backoffer { - bo, ok := backoffermap[task.region.GetID()] - if ok { - return bo - } - boMaxSleep := CopNextMaxBackoff - failpoint.Inject("ReduceCopNextMaxBackoff", func(value failpoint.Value) { - if value.(bool) { - boMaxSleep = 2 - } - }) - newbo := backoff.NewBackofferWithVars(ctx, boMaxSleep, worker.vars) - backoffermap[task.region.GetID()] = newbo - return newbo -} - -// handleTask handles single copTask, sends the result to channel, retry automatically on error. -func (worker *copIteratorWorker) handleTask(ctx context.Context, task *copTask, respCh chan<- *copResponse) { - defer func() { - r := recover() - if r != nil { - logutil.BgLogger().Error("copIteratorWork meet panic", - zap.Any("r", r), - zap.Stack("stack trace")) - resp := &copResponse{err: errors.Errorf("%v", r)} - // if panic has happened, set checkOOM to false to avoid another panic. - worker.sendToRespCh(resp, respCh, false) - } - }() - remainTasks := []*copTask{task} - backoffermap := make(map[uint64]*Backoffer) - for len(remainTasks) > 0 { - curTask := remainTasks[0] - bo := chooseBackoffer(ctx, backoffermap, curTask, worker) - tasks, err := worker.handleTaskOnce(bo, curTask, respCh) - if err != nil { - resp := &copResponse{err: errors.Trace(err)} - worker.sendToRespCh(resp, respCh, true) - return - } - if worker.finished() { - break - } - if len(tasks) > 0 { - remainTasks = append(tasks, remainTasks[1:]...) - } else { - remainTasks = remainTasks[1:] - } - } -} - -// handleTaskOnce handles single copTask, successful results are send to channel. -// If error happened, returns error. If region split or meet lock, returns the remain tasks. -func (worker *copIteratorWorker) handleTaskOnce(bo *Backoffer, task *copTask, ch chan<- *copResponse) ([]*copTask, error) { - failpoint.Inject("handleTaskOnceError", func(val failpoint.Value) { - if val.(bool) { - failpoint.Return(nil, errors.New("mock handleTaskOnce error")) - } - }) - - if task.paging { - task.pagingTaskIdx = atomic.AddUint32(worker.pagingTaskIdx, 1) - } - - copReq := coprocessor.Request{ - Tp: worker.req.Tp, - StartTs: worker.req.StartTs, - Data: worker.req.Data, - Ranges: task.ranges.ToPBRanges(), - SchemaVer: worker.req.SchemaVar, - PagingSize: task.pagingSize, - Tasks: task.ToPBBatchTasks(), - } - - cacheKey, cacheValue := worker.buildCacheKey(task, &copReq) - - replicaRead := worker.req.ReplicaRead - rgName := worker.req.ResourceGroupName - if task.storeType == kv.TiFlash && !variable.EnableResourceControl.Load() { - // By calling variable.EnableGlobalResourceControlFunc() and setting global variables, - // tikv/client-go can sense whether the rg function is enabled - // But for tiflash, it check if rgName is empty to decide if resource control is enabled or not. - rgName = "" - } - req := tikvrpc.NewReplicaReadRequest(task.cmdType, &copReq, options.GetTiKVReplicaReadType(replicaRead), &worker.replicaReadSeed, kvrpcpb.Context{ - IsolationLevel: isolationLevelToPB(worker.req.IsolationLevel), - Priority: priorityToPB(worker.req.Priority), - NotFillCache: worker.req.NotFillCache, - RecordTimeStat: true, - RecordScanStat: true, - TaskId: worker.req.TaskID, - ResourceControlContext: &kvrpcpb.ResourceControlContext{ - ResourceGroupName: rgName, - }, - BusyThresholdMs: uint32(task.busyThreshold.Milliseconds()), - BucketsVersion: task.bucketsVer, - }) - req.InputRequestSource = task.requestSource.GetRequestSource() - if task.firstReadType != "" { - req.ReadType = task.firstReadType - req.IsRetryRequest = true - } - if worker.req.ResourceGroupTagger != nil { - worker.req.ResourceGroupTagger(req) - } - timeout := config.GetGlobalConfig().TiKVClient.CoprReqTimeout - if task.tikvClientReadTimeout > 0 { - timeout = time.Duration(task.tikvClientReadTimeout) * time.Millisecond - } - failpoint.Inject("sleepCoprRequest", func(v failpoint.Value) { - //nolint:durationcheck - time.Sleep(time.Millisecond * time.Duration(v.(int))) - }) - - if worker.req.RunawayChecker != nil { - if err := worker.req.RunawayChecker.BeforeCopRequest(req); err != nil { - return nil, err - } - } - req.StoreTp = getEndPointType(task.storeType) - startTime := time.Now() - if worker.kvclient.Stats == nil { - worker.kvclient.Stats = make(map[tikvrpc.CmdType]*tikv.RPCRuntimeStats) - } - // set ReadReplicaScope and TxnScope so that req.IsStaleRead will be true when it's a global scope stale read. - req.ReadReplicaScope = worker.req.ReadReplicaScope - req.TxnScope = worker.req.TxnScope - if task.meetLockFallback { - req.DisableStaleReadMeetLock() - } else if worker.req.IsStaleness { - req.EnableStaleWithMixedReplicaRead() - } - staleRead := req.GetStaleRead() - ops := make([]tikv.StoreSelectorOption, 0, 2) - if len(worker.req.MatchStoreLabels) > 0 { - ops = append(ops, tikv.WithMatchLabels(worker.req.MatchStoreLabels)) - } - if task.redirect2Replica != nil { - req.ReplicaRead = true - req.ReplicaReadType = options.GetTiKVReplicaReadType(kv.ReplicaReadFollower) - ops = append(ops, tikv.WithMatchStores([]uint64{*task.redirect2Replica})) - } - resp, rpcCtx, storeAddr, err := worker.kvclient.SendReqCtx(bo.TiKVBackoffer(), req, task.region, - timeout, getEndPointType(task.storeType), task.storeAddr, ops...) - err = derr.ToTiDBErr(err) - if err != nil { - if task.storeType == kv.TiDB { - err = worker.handleTiDBSendReqErr(err, task, ch) - return nil, err - } - return nil, errors.Trace(err) - } - - // Set task.storeAddr field so its task.String() method have the store address information. - task.storeAddr = storeAddr - - costTime := time.Since(startTime) - copResp := resp.Resp.(*coprocessor.Response) - - if costTime > minLogCopTaskTime { - worker.logTimeCopTask(costTime, task, bo, copResp) - } - if worker.req.RunawayChecker != nil { - worker.req.RunawayChecker.AfterCopRequest() - } - - storeID := strconv.FormatUint(req.Context.GetPeer().GetStoreId(), 10) - isInternal := util.IsRequestSourceInternal(&task.requestSource) - scope := metrics.LblGeneral - if isInternal { - scope = metrics.LblInternal - } - metrics.TiKVCoprocessorHistogram.WithLabelValues(storeID, strconv.FormatBool(staleRead), scope).Observe(costTime.Seconds()) - if copResp != nil { - tidbmetrics.DistSQLCoprRespBodySize.WithLabelValues(storeAddr).Observe(float64(len(copResp.Data))) - } - - var remains []*copTask - if worker.req.Paging.Enable { - remains, err = worker.handleCopPagingResult(bo, rpcCtx, &copResponse{pbResp: copResp}, cacheKey, cacheValue, task, ch, costTime) - } else { - // Handles the response for non-paging copTask. - remains, err = worker.handleCopResponse(bo, rpcCtx, &copResponse{pbResp: copResp}, cacheKey, cacheValue, task, ch, nil, costTime) - } - if req.ReadType != "" { - for _, remain := range remains { - remain.firstReadType = req.ReadType - } - } - return remains, err -} - -const ( - minLogBackoffTime = 100 - minLogKVProcessTime = 100 -) - -func (worker *copIteratorWorker) logTimeCopTask(costTime time.Duration, task *copTask, bo *Backoffer, resp *coprocessor.Response) { - logStr := fmt.Sprintf("[TIME_COP_PROCESS] resp_time:%s txnStartTS:%d region_id:%d store_addr:%s", costTime, worker.req.StartTs, task.region.GetID(), task.storeAddr) - if bo.GetTotalSleep() > minLogBackoffTime { - backoffTypes := strings.ReplaceAll(fmt.Sprintf("%v", bo.TiKVBackoffer().GetTypes()), " ", ",") - logStr += fmt.Sprintf(" backoff_ms:%d backoff_types:%s", bo.GetTotalSleep(), backoffTypes) - } - // resp might be nil, but it is safe to call resp.GetXXX here. - detailV2 := resp.GetExecDetailsV2() - detail := resp.GetExecDetails() - var timeDetail *kvrpcpb.TimeDetail - if detailV2 != nil && detailV2.TimeDetail != nil { - timeDetail = detailV2.TimeDetail - } else if detail != nil && detail.TimeDetail != nil { - timeDetail = detail.TimeDetail - } - if timeDetail != nil { - logStr += fmt.Sprintf(" kv_process_ms:%d", timeDetail.ProcessWallTimeMs) - logStr += fmt.Sprintf(" kv_wait_ms:%d", timeDetail.WaitWallTimeMs) - logStr += fmt.Sprintf(" kv_read_ms:%d", timeDetail.KvReadWallTimeMs) - if timeDetail.ProcessWallTimeMs <= minLogKVProcessTime { - logStr = strings.Replace(logStr, "TIME_COP_PROCESS", "TIME_COP_WAIT", 1) - } - } - - if detailV2 != nil && detailV2.ScanDetailV2 != nil { - logStr += fmt.Sprintf(" processed_versions:%d", detailV2.ScanDetailV2.ProcessedVersions) - logStr += fmt.Sprintf(" total_versions:%d", detailV2.ScanDetailV2.TotalVersions) - logStr += fmt.Sprintf(" rocksdb_delete_skipped_count:%d", detailV2.ScanDetailV2.RocksdbDeleteSkippedCount) - logStr += fmt.Sprintf(" rocksdb_key_skipped_count:%d", detailV2.ScanDetailV2.RocksdbKeySkippedCount) - logStr += fmt.Sprintf(" rocksdb_cache_hit_count:%d", detailV2.ScanDetailV2.RocksdbBlockCacheHitCount) - logStr += fmt.Sprintf(" rocksdb_read_count:%d", detailV2.ScanDetailV2.RocksdbBlockReadCount) - logStr += fmt.Sprintf(" rocksdb_read_byte:%d", detailV2.ScanDetailV2.RocksdbBlockReadByte) - } else if detail != nil && detail.ScanDetail != nil { - logStr = appendScanDetail(logStr, "write", detail.ScanDetail.Write) - logStr = appendScanDetail(logStr, "data", detail.ScanDetail.Data) - logStr = appendScanDetail(logStr, "lock", detail.ScanDetail.Lock) - } - logutil.Logger(bo.GetCtx()).Info(logStr) -} - -func appendScanDetail(logStr string, columnFamily string, scanInfo *kvrpcpb.ScanInfo) string { - if scanInfo != nil { - logStr += fmt.Sprintf(" scan_total_%s:%d", columnFamily, scanInfo.Total) - logStr += fmt.Sprintf(" scan_processed_%s:%d", columnFamily, scanInfo.Processed) - } - return logStr -} - -func (worker *copIteratorWorker) handleCopPagingResult(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue, task *copTask, ch chan<- *copResponse, costTime time.Duration) ([]*copTask, error) { - remainedTasks, err := worker.handleCopResponse(bo, rpcCtx, resp, cacheKey, cacheValue, task, ch, nil, costTime) - if err != nil || len(remainedTasks) != 0 { - // If there is region error or lock error, keep the paging size and retry. - for _, remainedTask := range remainedTasks { - remainedTask.pagingSize = task.pagingSize - } - return remainedTasks, errors.Trace(err) - } - pagingRange := resp.pbResp.Range - // only paging requests need to calculate the next ranges - if pagingRange == nil { - // If the storage engine doesn't support paging protocol, it should have return all the region data. - // So we finish here. - return nil, nil - } - - // calculate next ranges and grow the paging size - task.ranges = worker.calculateRemain(task.ranges, pagingRange, worker.req.Desc) - if task.ranges.Len() == 0 { - return nil, nil - } - - task.pagingSize = paging.GrowPagingSize(task.pagingSize, worker.req.Paging.MaxPagingSize) - return []*copTask{task}, nil -} - -// handleCopResponse checks coprocessor Response for region split and lock, -// returns more tasks when that happens, or handles the response if no error. -// if we're handling coprocessor paging response, lastRange is the range of last -// successful response, otherwise it's nil. -func (worker *copIteratorWorker) handleCopResponse(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue, task *copTask, ch chan<- *copResponse, lastRange *coprocessor.KeyRange, costTime time.Duration) ([]*copTask, error) { - if ver := resp.pbResp.GetLatestBucketsVersion(); task.bucketsVer < ver { - worker.store.GetRegionCache().UpdateBucketsIfNeeded(task.region, ver) - } - if regionErr := resp.pbResp.GetRegionError(); regionErr != nil { - if rpcCtx != nil && task.storeType == kv.TiDB { - resp.err = errors.Errorf("error: %v", regionErr) - worker.sendToRespCh(resp, ch, true) - return nil, nil - } - errStr := fmt.Sprintf("region_id:%v, region_ver:%v, store_type:%s, peer_addr:%s, error:%s", - task.region.GetID(), task.region.GetVer(), task.storeType.Name(), task.storeAddr, regionErr.String()) - if err := bo.Backoff(tikv.BoRegionMiss(), errors.New(errStr)); err != nil { - return nil, errors.Trace(err) - } - // We may meet RegionError at the first packet, but not during visiting the stream. - remains, err := buildCopTasks(bo, task.ranges, &buildCopTaskOpt{ - req: worker.req, - cache: worker.store.GetRegionCache(), - respChan: false, - eventCb: task.eventCb, - ignoreTiKVClientReadTimeout: true, - }) - if err != nil { - return remains, err - } - return worker.handleBatchRemainsOnErr(bo, rpcCtx, remains, resp.pbResp, task, ch) - } - if lockErr := resp.pbResp.GetLocked(); lockErr != nil { - if err := worker.handleLockErr(bo, lockErr, task); err != nil { - return nil, err - } - task.meetLockFallback = true - return worker.handleBatchRemainsOnErr(bo, rpcCtx, []*copTask{task}, resp.pbResp, task, ch) - } - if otherErr := resp.pbResp.GetOtherError(); otherErr != "" { - err := errors.Errorf("other error: %s", otherErr) - - firstRangeStartKey := task.ranges.At(0).StartKey - lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey - - logutil.Logger(bo.GetCtx()).Warn("other error", - zap.Uint64("txnStartTS", worker.req.StartTs), - zap.Uint64("regionID", task.region.GetID()), - zap.Uint64("bucketsVer", task.bucketsVer), - zap.Uint64("latestBucketsVer", resp.pbResp.GetLatestBucketsVersion()), - zap.Int("rangeNums", task.ranges.Len()), - zap.ByteString("firstRangeStartKey", firstRangeStartKey), - zap.ByteString("lastRangeEndKey", lastRangeEndKey), - zap.String("storeAddr", task.storeAddr), - zap.Error(err)) - if strings.Contains(err.Error(), "write conflict") { - return nil, kv.ErrWriteConflict.FastGen("%s", otherErr) - } - return nil, errors.Trace(err) - } - // When the request is using paging API, the `Range` is not nil. - if resp.pbResp.Range != nil { - resp.startKey = resp.pbResp.Range.Start - } else if task.ranges != nil && task.ranges.Len() > 0 { - resp.startKey = task.ranges.At(0).StartKey - } - worker.handleCollectExecutionInfo(bo, rpcCtx, resp) - resp.respTime = costTime - - if err := worker.handleCopCache(task, resp, cacheKey, cacheValue); err != nil { - return nil, err - } - - pbResp := resp.pbResp - worker.sendToRespCh(resp, ch, true) - return worker.handleBatchCopResponse(bo, rpcCtx, pbResp, task.batchTaskList, ch) -} - -func (worker *copIteratorWorker) handleBatchRemainsOnErr(bo *Backoffer, rpcCtx *tikv.RPCContext, remains []*copTask, resp *coprocessor.Response, task *copTask, ch chan<- *copResponse) ([]*copTask, error) { - if len(task.batchTaskList) == 0 { - return remains, nil - } - batchedTasks := task.batchTaskList - task.batchTaskList = nil - batchedRemains, err := worker.handleBatchCopResponse(bo, rpcCtx, resp, batchedTasks, ch) - if err != nil { - return nil, err - } - return append(remains, batchedRemains...), nil -} - -// handle the batched cop response. -// tasks will be changed, so the input tasks should not be used after calling this function. -func (worker *copIteratorWorker) handleBatchCopResponse(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *coprocessor.Response, - tasks map[uint64]*batchedCopTask, ch chan<- *copResponse) (remainTasks []*copTask, err error) { - if len(tasks) == 0 { - return nil, nil - } - batchedNum := len(tasks) - busyThresholdFallback := false - defer func() { - if err != nil { - return - } - if !busyThresholdFallback { - worker.storeBatchedNum.Add(uint64(batchedNum - len(remainTasks))) - worker.storeBatchedFallbackNum.Add(uint64(len(remainTasks))) - } - }() - appendRemainTasks := func(tasks ...*copTask) { - if remainTasks == nil { - // allocate size fo remain length - remainTasks = make([]*copTask, 0, len(tasks)) - } - remainTasks = append(remainTasks, tasks...) - } - // need Addr for recording details. - var dummyRPCCtx *tikv.RPCContext - if rpcCtx != nil { - dummyRPCCtx = &tikv.RPCContext{ - Addr: rpcCtx.Addr, - } - } - batchResps := resp.GetBatchResponses() - for _, batchResp := range batchResps { - taskID := batchResp.GetTaskId() - batchedTask, ok := tasks[taskID] - if !ok { - return nil, errors.Errorf("task id %d not found", batchResp.GetTaskId()) - } - delete(tasks, taskID) - resp := &copResponse{ - pbResp: &coprocessor.Response{ - Data: batchResp.Data, - ExecDetailsV2: batchResp.ExecDetailsV2, - }, - } - task := batchedTask.task - failpoint.Inject("batchCopRegionError", func() { - batchResp.RegionError = &errorpb.Error{} - }) - if regionErr := batchResp.GetRegionError(); regionErr != nil { - errStr := fmt.Sprintf("region_id:%v, region_ver:%v, store_type:%s, peer_addr:%s, error:%s", - task.region.GetID(), task.region.GetVer(), task.storeType.Name(), task.storeAddr, regionErr.String()) - if err := bo.Backoff(tikv.BoRegionMiss(), errors.New(errStr)); err != nil { - return nil, errors.Trace(err) - } - remains, err := buildCopTasks(bo, task.ranges, &buildCopTaskOpt{ - req: worker.req, - cache: worker.store.GetRegionCache(), - respChan: false, - eventCb: task.eventCb, - ignoreTiKVClientReadTimeout: true, - }) - if err != nil { - return nil, err - } - appendRemainTasks(remains...) - continue - } - //TODO: handle locks in batch - if lockErr := batchResp.GetLocked(); lockErr != nil { - if err := worker.handleLockErr(bo, resp.pbResp.GetLocked(), task); err != nil { - return nil, err - } - task.meetLockFallback = true - appendRemainTasks(task) - continue - } - if otherErr := batchResp.GetOtherError(); otherErr != "" { - err := errors.Errorf("other error: %s", otherErr) - - firstRangeStartKey := task.ranges.At(0).StartKey - lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey - - logutil.Logger(bo.GetCtx()).Warn("other error", - zap.Uint64("txnStartTS", worker.req.StartTs), - zap.Uint64("regionID", task.region.GetID()), - zap.Uint64("bucketsVer", task.bucketsVer), - // TODO: add bucket version in log - //zap.Uint64("latestBucketsVer", batchResp.GetLatestBucketsVersion()), - zap.Int("rangeNums", task.ranges.Len()), - zap.ByteString("firstRangeStartKey", firstRangeStartKey), - zap.ByteString("lastRangeEndKey", lastRangeEndKey), - zap.String("storeAddr", task.storeAddr), - zap.Error(err)) - if strings.Contains(err.Error(), "write conflict") { - return nil, kv.ErrWriteConflict.FastGen("%s", otherErr) - } - return nil, errors.Trace(err) - } - worker.handleCollectExecutionInfo(bo, dummyRPCCtx, resp) - worker.sendToRespCh(resp, ch, true) - } - for _, t := range tasks { - task := t.task - // when the error is generated by client or a load-based server busy, - // response is empty by design, skip warning for this case. - if len(batchResps) != 0 { - firstRangeStartKey := task.ranges.At(0).StartKey - lastRangeEndKey := task.ranges.At(task.ranges.Len() - 1).EndKey - logutil.Logger(bo.GetCtx()).Error("response of batched task missing", - zap.Uint64("id", task.taskID), - zap.Uint64("txnStartTS", worker.req.StartTs), - zap.Uint64("regionID", task.region.GetID()), - zap.Uint64("bucketsVer", task.bucketsVer), - zap.Int("rangeNums", task.ranges.Len()), - zap.ByteString("firstRangeStartKey", firstRangeStartKey), - zap.ByteString("lastRangeEndKey", lastRangeEndKey), - zap.String("storeAddr", task.storeAddr)) - } - appendRemainTasks(t.task) - } - if regionErr := resp.GetRegionError(); regionErr != nil && regionErr.ServerIsBusy != nil && - regionErr.ServerIsBusy.EstimatedWaitMs > 0 && len(remainTasks) != 0 { - if len(batchResps) != 0 { - return nil, errors.New("store batched coprocessor with server is busy error shouldn't contain responses") - } - busyThresholdFallback = true - handler := newBatchTaskBuilder(bo, worker.req, worker.store.GetRegionCache(), kv.ReplicaReadFollower) - for _, task := range remainTasks { - // do not set busy threshold again. - task.busyThreshold = 0 - if err = handler.handle(task); err != nil { - return nil, err - } - } - remainTasks = handler.build() - } - return remainTasks, nil -} - -func (worker *copIteratorWorker) handleLockErr(bo *Backoffer, lockErr *kvrpcpb.LockInfo, task *copTask) error { - if lockErr == nil { - return nil - } - resolveLockDetail := worker.getLockResolverDetails() - // Be care that we didn't redact the SQL statement because the log is DEBUG level. - if task.eventCb != nil { - task.eventCb(trxevents.WrapCopMeetLock(&trxevents.CopMeetLock{ - LockInfo: lockErr, - })) - } else { - logutil.Logger(bo.GetCtx()).Debug("coprocessor encounters lock", - zap.Stringer("lock", lockErr)) - } - resolveLocksOpts := txnlock.ResolveLocksOptions{ - CallerStartTS: worker.req.StartTs, - Locks: []*txnlock.Lock{txnlock.NewLock(lockErr)}, - Detail: resolveLockDetail, - } - resolveLocksRes, err1 := worker.kvclient.ResolveLocksWithOpts(bo.TiKVBackoffer(), resolveLocksOpts) - err1 = derr.ToTiDBErr(err1) - if err1 != nil { - return errors.Trace(err1) - } - msBeforeExpired := resolveLocksRes.TTL - if msBeforeExpired > 0 { - if err := bo.BackoffWithMaxSleepTxnLockFast(int(msBeforeExpired), errors.New(lockErr.String())); err != nil { - return errors.Trace(err) - } - } - return nil -} - -func (worker *copIteratorWorker) buildCacheKey(task *copTask, copReq *coprocessor.Request) (cacheKey []byte, cacheValue *coprCacheValue) { - // If there are many ranges, it is very likely to be a TableLookupRequest. They are not worth to cache since - // computing is not the main cost. Ignore requests with many ranges directly to avoid slowly building the cache key. - if task.cmdType == tikvrpc.CmdCop && worker.store.coprCache != nil && worker.req.Cacheable && worker.store.coprCache.CheckRequestAdmission(len(copReq.Ranges)) { - cKey, err := coprCacheBuildKey(copReq) - if err == nil { - cacheKey = cKey - cValue := worker.store.coprCache.Get(cKey) - copReq.IsCacheEnabled = true - - if cValue != nil && cValue.RegionID == task.region.GetID() && cValue.TimeStamp <= worker.req.StartTs { - // Append cache version to the request to skip Coprocessor computation if possible - // when request result is cached - copReq.CacheIfMatchVersion = cValue.RegionDataVersion - cacheValue = cValue - } else { - copReq.CacheIfMatchVersion = 0 - } - } else { - logutil.BgLogger().Warn("Failed to build copr cache key", zap.Error(err)) - } - } - return -} - -func (worker *copIteratorWorker) handleCopCache(task *copTask, resp *copResponse, cacheKey []byte, cacheValue *coprCacheValue) error { - if resp.pbResp.IsCacheHit { - if cacheValue == nil { - return errors.New("Internal error: received illegal TiKV response") - } - copr_metrics.CoprCacheCounterHit.Add(1) - // Cache hit and is valid: use cached data as response data and we don't update the cache. - data := make([]byte, len(cacheValue.Data)) - copy(data, cacheValue.Data) - resp.pbResp.Data = data - if worker.req.Paging.Enable { - var start, end []byte - if cacheValue.PageStart != nil { - start = make([]byte, len(cacheValue.PageStart)) - copy(start, cacheValue.PageStart) - } - if cacheValue.PageEnd != nil { - end = make([]byte, len(cacheValue.PageEnd)) - copy(end, cacheValue.PageEnd) - } - // When paging protocol is used, the response key range is part of the cache data. - if start != nil || end != nil { - resp.pbResp.Range = &coprocessor.KeyRange{ - Start: start, - End: end, - } - } else { - resp.pbResp.Range = nil - } - } - resp.detail.CoprCacheHit = true - return nil - } - copr_metrics.CoprCacheCounterMiss.Add(1) - // Cache not hit or cache hit but not valid: update the cache if the response can be cached. - if cacheKey != nil && resp.pbResp.CanBeCached && resp.pbResp.CacheLastVersion > 0 { - if resp.detail != nil { - if worker.store.coprCache.CheckResponseAdmission(resp.pbResp.Data.Size(), resp.detail.TimeDetail.ProcessTime, task.pagingTaskIdx) { - data := make([]byte, len(resp.pbResp.Data)) - copy(data, resp.pbResp.Data) - - newCacheValue := coprCacheValue{ - Data: data, - TimeStamp: worker.req.StartTs, - RegionID: task.region.GetID(), - RegionDataVersion: resp.pbResp.CacheLastVersion, - } - // When paging protocol is used, the response key range is part of the cache data. - if r := resp.pbResp.GetRange(); r != nil { - newCacheValue.PageStart = append([]byte{}, r.GetStart()...) - newCacheValue.PageEnd = append([]byte{}, r.GetEnd()...) - } - worker.store.coprCache.Set(cacheKey, &newCacheValue) - } - } - } - return nil -} - -func (worker *copIteratorWorker) getLockResolverDetails() *util.ResolveLockDetail { - if !worker.enableCollectExecutionInfo { - return nil - } - return &util.ResolveLockDetail{} -} - -func (worker *copIteratorWorker) handleCollectExecutionInfo(bo *Backoffer, rpcCtx *tikv.RPCContext, resp *copResponse) { - defer func() { - worker.kvclient.Stats = nil - }() - if !worker.enableCollectExecutionInfo { - return - } - failpoint.Inject("disable-collect-execution", func(val failpoint.Value) { - if val.(bool) { - panic("shouldn't reachable") - } - }) - if resp.detail == nil { - resp.detail = new(CopRuntimeStats) - } - resp.detail.Stats = worker.kvclient.Stats - backoffTimes := bo.GetBackoffTimes() - resp.detail.BackoffTime = time.Duration(bo.GetTotalSleep()) * time.Millisecond - resp.detail.BackoffSleep = make(map[string]time.Duration, len(backoffTimes)) - resp.detail.BackoffTimes = make(map[string]int, len(backoffTimes)) - for backoff := range backoffTimes { - resp.detail.BackoffTimes[backoff] = backoffTimes[backoff] - resp.detail.BackoffSleep[backoff] = time.Duration(bo.GetBackoffSleepMS()[backoff]) * time.Millisecond - } - if rpcCtx != nil { - resp.detail.CalleeAddress = rpcCtx.Addr - } - sd := &util.ScanDetail{} - td := util.TimeDetail{} - if pbDetails := resp.pbResp.ExecDetailsV2; pbDetails != nil { - // Take values in `ExecDetailsV2` first. - if pbDetails.TimeDetail != nil || pbDetails.TimeDetailV2 != nil { - td.MergeFromTimeDetail(pbDetails.TimeDetailV2, pbDetails.TimeDetail) - } - if scanDetailV2 := pbDetails.ScanDetailV2; scanDetailV2 != nil { - sd.MergeFromScanDetailV2(scanDetailV2) - } - } else if pbDetails := resp.pbResp.ExecDetails; pbDetails != nil { - if timeDetail := pbDetails.TimeDetail; timeDetail != nil { - td.MergeFromTimeDetail(nil, timeDetail) - } - if scanDetail := pbDetails.ScanDetail; scanDetail != nil { - if scanDetail.Write != nil { - sd.ProcessedKeys = scanDetail.Write.Processed - sd.TotalKeys = scanDetail.Write.Total - } - } - } - resp.detail.ScanDetail = sd - resp.detail.TimeDetail = td -} - -// CopRuntimeStats contains execution detail information. -type CopRuntimeStats struct { - execdetails.ExecDetails - tikv.RegionRequestRuntimeStats - - CoprCacheHit bool -} - -func (worker *copIteratorWorker) handleTiDBSendReqErr(err error, task *copTask, ch chan<- *copResponse) error { - errCode := errno.ErrUnknown - errMsg := err.Error() - if terror.ErrorEqual(err, derr.ErrTiKVServerTimeout) { - errCode = errno.ErrTiKVServerTimeout - errMsg = "TiDB server timeout, address is " + task.storeAddr - } - if terror.ErrorEqual(err, derr.ErrTiFlashServerTimeout) { - errCode = errno.ErrTiFlashServerTimeout - errMsg = "TiDB server timeout, address is " + task.storeAddr - } - selResp := tipb.SelectResponse{ - Warnings: []*tipb.Error{ - { - Code: int32(errCode), - Msg: errMsg, - }, - }, - } - data, err := proto.Marshal(&selResp) - if err != nil { - return errors.Trace(err) - } - resp := &copResponse{ - pbResp: &coprocessor.Response{ - Data: data, - }, - detail: &CopRuntimeStats{}, - } - worker.sendToRespCh(resp, ch, true) - return nil -} - -// calculateRetry splits the input ranges into two, and take one of them according to desc flag. -// It's used in paging API, to calculate which range is consumed and what needs to be retry. -// For example: -// ranges: [r1 --> r2) [r3 --> r4) -// split: [s1 --> s2) -// In normal scan order, all data before s1 is consumed, so the retry ranges should be [s1 --> r2) [r3 --> r4) -// In reverse scan order, all data after s2 is consumed, so the retry ranges should be [r1 --> r2) [r3 --> s2) -func (worker *copIteratorWorker) calculateRetry(ranges *KeyRanges, split *coprocessor.KeyRange, desc bool) *KeyRanges { - if split == nil { - return ranges - } - if desc { - left, _ := ranges.Split(split.End) - return left - } - _, right := ranges.Split(split.Start) - return right -} - -// calculateRemain calculates the remain ranges to be processed, it's used in paging API. -// For example: -// ranges: [r1 --> r2) [r3 --> r4) -// split: [s1 --> s2) -// In normal scan order, all data before s2 is consumed, so the remained ranges should be [s2 --> r4) -// In reverse scan order, all data after s1 is consumed, so the remained ranges should be [r1 --> s1) -func (worker *copIteratorWorker) calculateRemain(ranges *KeyRanges, split *coprocessor.KeyRange, desc bool) *KeyRanges { - if split == nil { - return ranges - } - if desc { - left, _ := ranges.Split(split.Start) - return left - } - _, right := ranges.Split(split.End) - return right -} - -// finished checks the flags and finished channel, it tells whether the worker is finished. -func (worker *copIteratorWorker) finished() bool { - if worker.vars != nil && worker.vars.Killed != nil && atomic.LoadUint32(worker.vars.Killed) == 1 { - return true - } - select { - case <-worker.finishCh: - return true - default: - return false - } -} - -func (it *copIterator) Close() error { - if atomic.CompareAndSwapUint32(&it.closed, 0, 1) { - close(it.finishCh) - } - it.rpcCancel.CancelAll() - it.actionOnExceed.close() - it.wg.Wait() - return nil -} - -// copErrorResponse returns error when calling Next() -type copErrorResponse struct{ error } - -func (it copErrorResponse) Next(ctx context.Context) (kv.ResultSubset, error) { - return nil, it.error -} - -func (it copErrorResponse) Close() error { - return nil -} - -// rateLimitAction an OOM Action which is used to control the token if OOM triggered. The token number should be -// set on initial. Each time the Action is triggered, one token would be destroyed. If the count of the token is less -// than 2, the action would be delegated to the fallback action. -type rateLimitAction struct { - memory.BaseOOMAction - // enabled indicates whether the rateLimitAction is permitted to Action. 1 means permitted, 0 denied. - enabled uint32 - // totalTokenNum indicates the total token at initial - totalTokenNum uint - cond struct { - sync.Mutex - // exceeded indicates whether have encountered OOM situation. - exceeded bool - // remainingTokenNum indicates the count of tokens which still exists - remainingTokenNum uint - once sync.Once - // triggerCountForTest indicates the total count of the rateLimitAction's Action being executed - triggerCountForTest uint - } -} - -func newRateLimitAction(totalTokenNumber uint) *rateLimitAction { - return &rateLimitAction{ - totalTokenNum: totalTokenNumber, - cond: struct { - sync.Mutex - exceeded bool - remainingTokenNum uint - once sync.Once - triggerCountForTest uint - }{ - Mutex: sync.Mutex{}, - exceeded: false, - remainingTokenNum: totalTokenNumber, - once: sync.Once{}, - }, - } -} - -// Action implements ActionOnExceed.Action -func (e *rateLimitAction) Action(t *memory.Tracker) { - if !e.isEnabled() { - if fallback := e.GetFallback(); fallback != nil { - fallback.Action(t) - } - return - } - e.conditionLock() - defer e.conditionUnlock() - e.cond.once.Do(func() { - if e.cond.remainingTokenNum < 2 { - e.setEnabled(false) - logutil.BgLogger().Info("memory exceeds quota, rateLimitAction delegate to fallback action", - zap.Uint("total token count", e.totalTokenNum)) - if fallback := e.GetFallback(); fallback != nil { - fallback.Action(t) - } - return - } - failpoint.Inject("testRateLimitActionMockConsumeAndAssert", func(val failpoint.Value) { - if val.(bool) { - if e.cond.triggerCountForTest+e.cond.remainingTokenNum != e.totalTokenNum { - panic("triggerCount + remainingTokenNum not equal to totalTokenNum") - } - } - }) - logutil.BgLogger().Info("memory exceeds quota, destroy one token now.", - zap.Int64("consumed", t.BytesConsumed()), - zap.Int64("quota", t.GetBytesLimit()), - zap.Uint("total token count", e.totalTokenNum), - zap.Uint("remaining token count", e.cond.remainingTokenNum)) - e.cond.exceeded = true - e.cond.triggerCountForTest++ - }) -} - -// GetPriority get the priority of the Action. -func (e *rateLimitAction) GetPriority() int64 { - return memory.DefRateLimitPriority -} - -// destroyTokenIfNeeded will check the `exceed` flag after copWorker finished one task. -// If the exceed flag is true and there is no token been destroyed before, one token will be destroyed, -// or the token would be return back. -func (e *rateLimitAction) destroyTokenIfNeeded(returnToken func()) { - if !e.isEnabled() { - returnToken() - return - } - e.conditionLock() - defer e.conditionUnlock() - if !e.cond.exceeded { - returnToken() - return - } - // If actionOnExceed has been triggered and there is no token have been destroyed before, - // destroy one token. - e.cond.remainingTokenNum = e.cond.remainingTokenNum - 1 - e.cond.exceeded = false - e.cond.once = sync.Once{} -} - -func (e *rateLimitAction) conditionLock() { - e.cond.Lock() -} - -func (e *rateLimitAction) conditionUnlock() { - e.cond.Unlock() -} - -func (e *rateLimitAction) close() { - if !e.isEnabled() { - return - } - e.setEnabled(false) - e.conditionLock() - defer e.conditionUnlock() - e.cond.exceeded = false - e.SetFinished() -} - -func (e *rateLimitAction) setEnabled(enabled bool) { - newValue := uint32(0) - if enabled { - newValue = uint32(1) - } - atomic.StoreUint32(&e.enabled, newValue) -} - -func (e *rateLimitAction) isEnabled() bool { - return atomic.LoadUint32(&e.enabled) > 0 -} - -// priorityToPB converts priority type to wire type. -func priorityToPB(pri int) kvrpcpb.CommandPri { - switch pri { - case kv.PriorityLow: - return kvrpcpb.CommandPri_Low - case kv.PriorityHigh: - return kvrpcpb.CommandPri_High - default: - return kvrpcpb.CommandPri_Normal - } -} - -func isolationLevelToPB(level kv.IsoLevel) kvrpcpb.IsolationLevel { - switch level { - case kv.RC: - return kvrpcpb.IsolationLevel_RC - case kv.SI: - return kvrpcpb.IsolationLevel_SI - case kv.RCCheckTS: - return kvrpcpb.IsolationLevel_RCCheckTS - default: - return kvrpcpb.IsolationLevel_SI - } -} - -// BuildKeyRanges is used for test, quickly build key ranges from paired keys. -func BuildKeyRanges(keys ...string) []kv.KeyRange { - var ranges []kv.KeyRange - for i := 0; i < len(keys); i += 2 { - ranges = append(ranges, kv.KeyRange{ - StartKey: []byte(keys[i]), - EndKey: []byte(keys[i+1]), - }) - } - return ranges -} - -func optRowHint(req *kv.Request) bool { - opt := true - if req.StoreType == kv.TiDB { - return false - } - if req.RequestSource.RequestSourceInternal || req.Tp != kv.ReqTypeDAG { - // disable extra concurrency for internal tasks. - return false - } - failpoint.Inject("disableFixedRowCountHint", func(_ failpoint.Value) { - opt = false - }) - return opt -} - -func checkStoreBatchCopr(req *kv.Request) bool { - if req.Tp != kv.ReqTypeDAG || req.StoreType != kv.TiKV { - return false - } - // TODO: support keep-order batch - if req.ReplicaRead != kv.ReplicaReadLeader || req.KeepOrder { - // Disable batch copr for follower read - return false - } - // Disable batch copr when paging is enabled. - if req.Paging.Enable { - return false - } - // Disable it for internal requests to avoid regression. - if req.RequestSource.RequestSourceInternal { - return false - } - return true -} diff --git a/store/copr/coprocessor_test.go b/store/copr/coprocessor_test.go deleted file mode 100644 index ed6f2c6f3cb81..0000000000000 --- a/store/copr/coprocessor_test.go +++ /dev/null @@ -1,793 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "context" - "testing" - - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/util/paging" - "github.com/pingcap/tidb/util/trxevents" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" - "github.com/tikv/client-go/v2/tikv" -) - -func buildTestCopTasks(bo *Backoffer, cache *RegionCache, ranges *KeyRanges, req *kv.Request, eventCb trxevents.EventCallback) ([]*copTask, error) { - return buildCopTasks(bo, ranges, &buildCopTaskOpt{ - req: req, - cache: cache, - eventCb: eventCb, - respChan: true, - }) -} - -func TestBuildTasksWithoutBuckets(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - - _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - req := &kv.Request{} - flashReq := &kv.Request{} - flashReq.StoreType = kv.TiFlash - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("m", "n"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[1], 0, "m", "n") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("m", "n"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[1], 0, "m", "n") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "k"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") - taskEqual(t, tasks[1], regionIDs[1], 0, "g", "k") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "k"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") - taskEqual(t, tasks[1], regionIDs[1], 0, "g", "k") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "x"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 4) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") - taskEqual(t, tasks[1], regionIDs[1], 0, "g", "n") - taskEqual(t, tasks[2], regionIDs[2], 0, "n", "t") - taskEqual(t, tasks[3], regionIDs[3], 0, "t", "x") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "x"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 4) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "g") - taskEqual(t, tasks[1], regionIDs[1], 0, "g", "n") - taskEqual(t, tasks[2], regionIDs[2], 0, "n", "t") - taskEqual(t, tasks[3], regionIDs[3], 0, "t", "x") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "b", "c"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "b", "c") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "b", "c"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "b", "c") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "e", "f"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "e", "f") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "b", "e", "f"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "b", "e", "f") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n", "o", "p"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") - taskEqual(t, tasks[1], regionIDs[2], 0, "o", "p") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("g", "n", "o", "p"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[1], 0, "g", "n") - taskEqual(t, tasks[1], regionIDs[2], 0, "o", "p") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("h", "k", "m", "p"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[1], 0, "h", "k", "m", "n") - taskEqual(t, tasks[1], regionIDs[2], 0, "n", "p") - - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("h", "k", "m", "p"), flashReq, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[1], 0, "h", "k", "m", "n") - taskEqual(t, tasks[1], regionIDs[2], 0, "n", "p") -} - -func TestBuildTasksByBuckets(t *testing.T) { - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - - // region: nil------------------n-----------x-----------nil - // buckets: nil----c----g----k---n----t------x-----------nil - _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("n"), []byte("x")) - cluster.SplitRegionBuckets(regionIDs[0], [][]byte{{}, {'c'}, {'g'}, {'k'}, {'n'}}, regionIDs[0]) - cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'t'}, {'x'}}, regionIDs[1]) - cluster.SplitRegionBuckets(regionIDs[2], [][]byte{{'x'}, {}}, regionIDs[2]) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - // one range per bucket - // region: nil------------------n-----------x-----------nil - // buckets: nil----c----g----k---n----t------x-----------nil - // range&task: a-b c-d h-i k---n o-p u--x-----------nil - req := &kv.Request{} - regionRanges := []struct { - regionID uint64 - ranges []string - }{ - {regionIDs[0], []string{"a", "b", "c", "d", "h", "i", "k", "n"}}, - {regionIDs[1], []string{"o", "p", "u", "x"}}, - {regionIDs[2], []string{"x", ""}}, - } - for _, regionRange := range regionRanges { - regionID, ranges := regionRange.regionID, regionRange.ranges - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges(ranges...), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(ranges)/2) - for i, task := range tasks { - taskEqual(t, task, regionID, regionID, ranges[2*i], ranges[2*i+1]) - } - } - - // one request multiple regions - allRanges := []string{} - for _, regionRange := range regionRanges { - allRanges = append(allRanges, regionRange.ranges...) - } - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges(allRanges...), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(allRanges)/2) - taskIdx := 0 - for _, regionRange := range regionRanges { - regionID, ranges := regionRange.regionID, regionRange.ranges - for i := 0; i < len(ranges); i += 2 { - taskEqual(t, tasks[taskIdx], regionID, regionID, ranges[i], ranges[i+1]) - taskIdx++ - } - } - - // serveral ranges per bucket - // region: nil---------------------------n-----------x-----------nil - // buckets: nil-----c-------g-------k-----n----t------x-----------nil - // ranges: nil-a b-c d-e f-g h-i j-k-l m-n - // tasks: nil-a b-c - // d-e f-g - // h-i j-k - // k-l m-n - keyRanges := []string{ - "", "a", "b", "c", - "d", "e", "f", "g", - "h", "i", "j", "k", - "k", "l", "m", "n", - } - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges(keyRanges...), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(keyRanges)/4) - for i, task := range tasks { - taskEqual(t, task, regionIDs[0], regionIDs[0], keyRanges[4*i], keyRanges[4*i+1], keyRanges[4*i+2], keyRanges[4*i+3]) - } - - // cross bucket ranges - // buckets: nil-----c-------g---------k---n----t------x-----------nil - // ranges: nil-------d e---h i---j - // tasks: nil-----c - // c-d e-g - // g-h i---j - keyRanges = []string{ - "", "d", "e", "h", "i", "j", - } - expectedTaskRanges := [][]string{ - {"", "c"}, - {"c", "d", "e", "g"}, - {"g", "h", "i", "j"}, - } - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges(keyRanges...), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(expectedTaskRanges)) - for i, task := range tasks { - taskEqual(t, task, regionIDs[0], regionIDs[0], expectedTaskRanges[i]...) - } - - // cross several buckets ranges - // region: n ----------------------------- x - // buckets: n -- q -- r -- t -- u -- v -- x - // ranges: n--o p--q s ------------ w - // tasks: n--o p--q - // s--t - // t -- u - // u -- v - // v--w - expectedTaskRanges = [][]string{ - {"n", "o", "p", "q"}, - {"s", "t"}, - {"t", "u"}, - {"u", "v"}, - {"v", "w"}, - } - cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'q'}, {'r'}, {'t'}, {'u'}, {'v'}, {'x'}}, regionIDs[1]) - cache = NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "o", "p", "q", "s", "w"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(expectedTaskRanges)) - for i, task := range tasks { - taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) - } - - // out of range buckets - // region: n------------------x - // buckets: q---s---u - // ranges: n-o p ----s t---v w-x - // tasks: n-o p-q - // q--s - // t-u - // u-v w-x - expectedTaskRanges = [][]string{ - {"n", "o", "p", "q"}, - {"q", "s"}, - {"t", "u"}, - {"u", "v", "w", "x"}, - } - cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'q'}, {'s'}, {'u'}}, regionIDs[1]) - cache = NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "o", "p", "s", "t", "v", "w", "x"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(expectedTaskRanges)) - for i, task := range tasks { - taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) - } - - // out of range buckets - // region: n------------x - // buckets: g-------t---------z - // ranges: o-p u-w - // tasks: o-p - // u-w - expectedTaskRanges = [][]string{ - {"o", "p"}, - {"u", "w"}, - } - cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'g'}, {'t'}, {'z'}}, regionIDs[1]) - cache = NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("o", "p", "u", "w"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(expectedTaskRanges)) - for i, task := range tasks { - taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) - } - - // cover the whole region - // region: n--------------x - // buckets: n -- q -- r -- x - // ranges: n--------------x - // tasks: o -- q - // q -- r - // r -- x - expectedTaskRanges = [][]string{ - {"n", "q"}, - {"q", "r"}, - {"r", "x"}, - } - cluster.SplitRegionBuckets(regionIDs[1], [][]byte{{'n'}, {'q'}, {'r'}, {'x'}}, regionIDs[1]) - cache = NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("n", "x"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, len(expectedTaskRanges)) - for i, task := range tasks { - taskEqual(t, task, regionIDs[1], regionIDs[1], expectedTaskRanges[i]...) - } -} - -func TestSplitRegionRanges(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - - testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - ranges, err := cache.SplitRegionRanges(bo, BuildKeyRanges("a", "c"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 1) - rangeEqual(t, ranges, "a", "c") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("h", "y"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 3) - rangeEqual(t, ranges, "h", "n", "n", "t", "t", "y") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("s", "z"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 2) - rangeEqual(t, ranges, "s", "t", "t", "z") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("s", "s"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 1) - rangeEqual(t, ranges, "s", "s") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("t", "t"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 1) - rangeEqual(t, ranges, "t", "t") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("t", "u"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 1) - rangeEqual(t, ranges, "t", "u") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("u", "z"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 1) - rangeEqual(t, ranges, "u", "z") - - // min --> max - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("a", "z"), UnspecifiedLimit) - require.NoError(t, err) - require.Len(t, ranges, 4) - rangeEqual(t, ranges, "a", "g", "g", "n", "n", "t", "t", "z") - - ranges, err = cache.SplitRegionRanges(bo, BuildKeyRanges("a", "z"), 3) - require.NoError(t, err) - require.Len(t, ranges, 3) - rangeEqual(t, ranges, "a", "g", "g", "n", "n", "t") -} - -func TestRebuild(t *testing.T) { - // nil --- 'm' --- nil - // <- 0 -> <- 1 -> - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - - storeID, regionIDs, peerIDs := testutils.BootstrapWithMultiRegions(cluster, []byte("m")) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - req := &kv.Request{} - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "z"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 2) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "m") - taskEqual(t, tasks[1], regionIDs[1], 0, "m", "z") - - // nil -- 'm' -- 'q' -- nil - // <- 0 -> <--1-> <-2--> - regionIDs = append(regionIDs, cluster.AllocID()) - peerIDs = append(peerIDs, cluster.AllocID()) - cluster.Split(regionIDs[1], regionIDs[2], []byte("q"), []uint64{peerIDs[2]}, storeID) - cache.InvalidateCachedRegion(tasks[1].region) - - req.Desc = true - tasks, err = buildTestCopTasks(bo, cache, buildCopRanges("a", "z"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 3) - taskEqual(t, tasks[2], regionIDs[0], 0, "a", "m") - taskEqual(t, tasks[1], regionIDs[1], 0, "m", "q") - taskEqual(t, tasks[0], regionIDs[2], 0, "q", "z") -} - -func buildCopRanges(keys ...string) *KeyRanges { - return NewKeyRanges(BuildKeyRanges(keys...)) -} - -func taskEqual(t *testing.T, task *copTask, regionID, bucketsVer uint64, keys ...string) { - require.Equal(t, task.region.GetID(), regionID) - require.Equal(t, task.bucketsVer, bucketsVer) - for i := 0; i < task.ranges.Len(); i++ { - r := task.ranges.At(i) - require.Equal(t, string(r.StartKey), keys[2*i]) - require.Equal(t, string(r.EndKey), keys[2*i+1]) - } -} - -func rangeEqual(t *testing.T, ranges []kv.KeyRange, keys ...string) { - for i := 0; i < len(ranges); i++ { - r := ranges[i] - require.Equal(t, string(r.StartKey), keys[2*i]) - require.Equal(t, string(r.EndKey), keys[2*i+1]) - } -} - -func TestBuildPagingTasks(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - - _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - req := &kv.Request{} - req.Paging.Enable = true - req.Paging.MinPagingSize = paging.MinPagingSize - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") - require.True(t, tasks[0].paging) - require.Equal(t, tasks[0].pagingSize, paging.MinPagingSize) -} - -func TestBuildPagingTasksDisablePagingForSmallLimit(t *testing.T) { - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - _, regionIDs, _ := testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) - - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - - req := &kv.Request{} - req.Paging.Enable = true - req.Paging.MinPagingSize = paging.MinPagingSize - req.LimitSize = 1 - tasks, err := buildTestCopTasks(bo, cache, buildCopRanges("a", "c"), req, nil) - require.NoError(t, err) - require.Len(t, tasks, 1) - require.Len(t, tasks, 1) - taskEqual(t, tasks[0], regionIDs[0], 0, "a", "c") - require.False(t, tasks[0].paging) - require.Equal(t, tasks[0].pagingSize, uint64(0)) -} - -func toCopRange(r kv.KeyRange) *coprocessor.KeyRange { - coprRange := coprocessor.KeyRange{} - coprRange.Start = r.StartKey - coprRange.End = r.EndKey - return &coprRange -} - -func toRange(r *KeyRanges) []kv.KeyRange { - ranges := make([]kv.KeyRange, 0, r.Len()) - if r.first != nil { - ranges = append(ranges, *r.first) - } - ranges = append(ranges, r.mid...) - if r.last != nil { - ranges = append(ranges, *r.last) - } - return ranges -} - -func TestCalculateRetry(t *testing.T) { - worker := copIteratorWorker{} - - // split in one range - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("b", "c")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) - rangeEqual(t, toRange(retry), "b", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("e", "f")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) - rangeEqual(t, toRange(retry), "a", "c", "e", "f") - } - - // across ranges - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("b", "f")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) - rangeEqual(t, toRange(retry), "b", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("b", "f")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) - rangeEqual(t, toRange(retry), "a", "c", "e", "f") - } - - // exhaust the ranges - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "g")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), false) - rangeEqual(t, toRange(retry), "a", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "g")[0] - retry := worker.calculateRetry(NewKeyRanges(ranges), toCopRange(split), true) - rangeEqual(t, toRange(retry), "a", "c", "e", "g") - } - - // nil range - { - ranges := BuildKeyRanges("a", "c", "e", "g") - retry := worker.calculateRetry(NewKeyRanges(ranges), nil, false) - rangeEqual(t, toRange(retry), "a", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - retry := worker.calculateRetry(NewKeyRanges(ranges), nil, true) - rangeEqual(t, toRange(retry), "a", "c", "e", "g") - } -} - -func TestCalculateRemain(t *testing.T) { - worker := copIteratorWorker{} - - // split in one range - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "b")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) - rangeEqual(t, toRange(remain), "b", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("f", "g")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) - rangeEqual(t, toRange(remain), "a", "c", "e", "f") - } - - // across ranges - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "f")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) - rangeEqual(t, toRange(remain), "f", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("b", "g")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) - rangeEqual(t, toRange(remain), "a", "b") - } - - // exhaust the ranges - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "g")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), false) - require.Equal(t, remain.Len(), 0) - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - split := BuildKeyRanges("a", "g")[0] - remain := worker.calculateRemain(NewKeyRanges(ranges), toCopRange(split), true) - require.Equal(t, remain.Len(), 0) - } - - // nil range - { - ranges := BuildKeyRanges("a", "c", "e", "g") - remain := worker.calculateRemain(NewKeyRanges(ranges), nil, false) - rangeEqual(t, toRange(remain), "a", "c", "e", "g") - } - { - ranges := BuildKeyRanges("a", "c", "e", "g") - remain := worker.calculateRemain(NewKeyRanges(ranges), nil, true) - rangeEqual(t, toRange(remain), "a", "c", "e", "g") - } -} - -func TestBasicSmallTaskConc(t *testing.T) { - require.False(t, isSmallTask(&copTask{RowCountHint: -1})) - require.False(t, isSmallTask(&copTask{RowCountHint: 0})) - require.True(t, isSmallTask(&copTask{RowCountHint: 1})) - require.True(t, isSmallTask(&copTask{RowCountHint: 6})) - require.True(t, isSmallTask(&copTask{RowCountHint: CopSmallTaskRow})) - require.False(t, isSmallTask(&copTask{RowCountHint: CopSmallTaskRow + 1})) - _, conc := smallTaskConcurrency([]*copTask{}, 16) - require.GreaterOrEqual(t, conc, 0) -} - -func TestBuildCopTasksWithRowCountHint(t *testing.T) { - // nil --- 'g' --- 'n' --- 't' --- nil - // <- 0 -> <- 1 -> <- 2 -> <- 3 -> - mockClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - defer func() { - pdClient.Close() - err = mockClient.Close() - require.NoError(t, err) - }() - _, _, _ = testutils.BootstrapWithMultiRegions(cluster, []byte("g"), []byte("n"), []byte("t")) - pdCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - defer pdCli.Close() - cache := NewRegionCache(tikv.NewRegionCache(pdCli)) - defer cache.Close() - - bo := backoff.NewBackofferWithVars(context.Background(), 3000, nil) - req := &kv.Request{} - ranges := buildCopRanges("a", "c", "d", "e", "h", "x", "y", "z") - tasks, err := buildCopTasks(bo, ranges, &buildCopTaskOpt{ - req: req, - cache: cache, - rowHints: []int{1, 1, 3, CopSmallTaskRow}, - }) - require.Nil(t, err) - require.Equal(t, len(tasks), 4) - // task[0] ["a"-"c", "d"-"e"] - require.Equal(t, tasks[0].RowCountHint, 2) - // task[1] ["h"-"n"] - require.Equal(t, tasks[1].RowCountHint, 3) - // task[2] ["n"-"t"] - require.Equal(t, tasks[2].RowCountHint, 3) - // task[3] ["t"-"x", "y"-"z"] - require.Equal(t, tasks[3].RowCountHint, 3+CopSmallTaskRow) - _, conc := smallTaskConcurrency(tasks, 16) - require.Equal(t, conc, 1) - - ranges = buildCopRanges("a", "c", "d", "e", "h", "x", "y", "z") - tasks, err = buildCopTasks(bo, ranges, &buildCopTaskOpt{ - req: req, - cache: cache, - rowHints: []int{1, 1, 3, 3}, - }) - require.Nil(t, err) - require.Equal(t, len(tasks), 4) - // task[0] ["a"-"c", "d"-"e"] - require.Equal(t, tasks[0].RowCountHint, 2) - // task[1] ["h"-"n"] - require.Equal(t, tasks[1].RowCountHint, 3) - // task[2] ["n"-"t"] - require.Equal(t, tasks[2].RowCountHint, 3) - // task[3] ["t"-"x", "y"-"z"] - require.Equal(t, tasks[3].RowCountHint, 6) - _, conc = smallTaskConcurrency(tasks, 16) - require.Equal(t, conc, 2) - - // cross-region long range - ranges = buildCopRanges("a", "z") - tasks, err = buildCopTasks(bo, ranges, &buildCopTaskOpt{ - req: req, - cache: cache, - rowHints: []int{10}, - }) - require.Nil(t, err) - require.Equal(t, len(tasks), 4) - // task[0] ["a"-"g"] - require.Equal(t, tasks[0].RowCountHint, 10) - // task[1] ["g"-"n"] - require.Equal(t, tasks[1].RowCountHint, 10) - // task[2] ["n"-"t"] - require.Equal(t, tasks[2].RowCountHint, 10) - // task[3] ["t"-"z"] - require.Equal(t, tasks[3].RowCountHint, 10) -} - -func TestSmallTaskConcurrencyLimit(t *testing.T) { - smallTaskCount := 1000 - tasks := make([]*copTask, 0, smallTaskCount) - for i := 0; i < smallTaskCount; i++ { - tasks = append(tasks, &copTask{ - RowCountHint: 1, - }) - } - count, conc := smallTaskConcurrency(tasks, 1) - require.Equal(t, smallConcPerCore, conc) - require.Equal(t, smallTaskCount, count) - // also handle 0 value. - count, conc = smallTaskConcurrency(tasks, 0) - require.Equal(t, smallConcPerCore, conc) - require.Equal(t, smallTaskCount, count) -} diff --git a/store/copr/main_test.go b/store/copr/main_test.go deleted file mode 100644 index 5e4a515a086fd..0000000000000 --- a/store/copr/main_test.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -type main struct { - m *testing.M -} - -func (m *main) Run() int { - c := m.m.Run() - // In order for MVCCLevelDB to close, we need to wait one second - time.Sleep(time.Second) - return c -} - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/pingcap/goleveldb/leveldb.(*DB).mpoolDrain"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(&main{m: m}, opts...) -} diff --git a/store/copr/metrics/BUILD.bazel b/store/copr/metrics/BUILD.bazel deleted file mode 100644 index fca7dcf58b045..0000000000000 --- a/store/copr/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/store/copr/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/store/copr/metrics/metrics.go b/store/copr/metrics/metrics.go deleted file mode 100644 index 92b59ecbc10dd..0000000000000 --- a/store/copr/metrics/metrics.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// copr metrics vars -var ( - CoprCacheCounterEvict prometheus.Counter - CoprCacheCounterHit prometheus.Counter - CoprCacheCounterMiss prometheus.Counter -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init copr metrics vars. -func InitMetricsVars() { - CoprCacheCounterEvict = metrics.DistSQLCoprCacheCounter.WithLabelValues("evict") - CoprCacheCounterHit = metrics.DistSQLCoprCacheCounter.WithLabelValues("hit") - CoprCacheCounterMiss = metrics.DistSQLCoprCacheCounter.WithLabelValues("miss") -} diff --git a/store/copr/mpp.go b/store/copr/mpp.go deleted file mode 100644 index 31794f7c90e2e..0000000000000 --- a/store/copr/mpp.go +++ /dev/null @@ -1,338 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "context" - "strconv" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/driver/backoff" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mathutil" - "github.com/pingcap/tidb/util/tiflash" - "github.com/pingcap/tidb/util/tiflashcompute" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - pd "github.com/tikv/pd/client" - "go.uber.org/zap" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// MPPClient servers MPP requests. -type MPPClient struct { - store *kvStore -} - -type mppStoreCnt struct { - cnt int32 - lastUpdate int64 - initFlag int32 -} - -// GetAddress returns the network address. -func (c *batchCopTask) GetAddress() string { - return c.storeAddr -} - -// ConstructMPPTasks receives ScheduleRequest, which are actually collects of kv ranges. We allocates MPPTaskMeta for them and returns. -func (c *MPPClient) ConstructMPPTasks(ctx context.Context, req *kv.MPPBuildTasksRequest, ttl time.Duration, dispatchPolicy tiflashcompute.DispatchPolicy, tiflashReplicaReadPolicy tiflash.ReplicaRead, appendWarning func(error)) ([]kv.MPPTaskMeta, error) { - ctx = context.WithValue(ctx, tikv.TxnStartKey(), req.StartTS) - bo := backoff.NewBackofferWithVars(ctx, copBuildTaskMaxBackoff, nil) - var tasks []*batchCopTask - var err error - if req.PartitionIDAndRanges != nil { - rangesForEachPartition := make([]*KeyRanges, len(req.PartitionIDAndRanges)) - partitionIDs := make([]int64, len(req.PartitionIDAndRanges)) - for i, p := range req.PartitionIDAndRanges { - rangesForEachPartition[i] = NewKeyRanges(p.KeyRanges) - partitionIDs[i] = p.ID - } - tasks, err = buildBatchCopTasksForPartitionedTable(ctx, bo, c.store, rangesForEachPartition, kv.TiFlash, true, ttl, true, 20, partitionIDs, dispatchPolicy, tiflashReplicaReadPolicy, appendWarning) - } else { - if req.KeyRanges == nil { - return nil, errors.New("KeyRanges in MPPBuildTasksRequest is nil") - } - ranges := NewKeyRanges(req.KeyRanges) - tasks, err = buildBatchCopTasksForNonPartitionedTable(ctx, bo, c.store, ranges, kv.TiFlash, true, ttl, true, 20, dispatchPolicy, tiflashReplicaReadPolicy, appendWarning) - } - - if err != nil { - return nil, errors.Trace(err) - } - mppTasks := make([]kv.MPPTaskMeta, 0, len(tasks)) - for _, copTask := range tasks { - mppTasks = append(mppTasks, copTask) - } - return mppTasks, nil -} - -// DispatchMPPTask dispatch mpp task, and returns valid response when retry = false and err is nil -func (c *MPPClient) DispatchMPPTask(param kv.DispatchMPPTaskParam) (resp *mpp.DispatchTaskResponse, retry bool, err error) { - req := param.Req - var regionInfos []*coprocessor.RegionInfo - originalTask, ok := req.Meta.(*batchCopTask) - if ok { - for _, ri := range originalTask.regionInfos { - regionInfos = append(regionInfos, ri.toCoprocessorRegionInfo()) - } - } - - // meta for current task. - taskMeta := &mpp.TaskMeta{StartTs: req.StartTs, QueryTs: req.MppQueryID.QueryTs, LocalQueryId: req.MppQueryID.LocalQueryID, TaskId: req.ID, ServerId: req.MppQueryID.ServerID, - GatherId: req.GatherID, - Address: req.Meta.GetAddress(), - CoordinatorAddress: req.CoordinatorAddress, - ReportExecutionSummary: req.ReportExecutionSummary, - MppVersion: req.MppVersion.ToInt64(), - ResourceGroupName: req.ResourceGroupName, - } - - mppReq := &mpp.DispatchTaskRequest{ - Meta: taskMeta, - EncodedPlan: req.Data, - // TODO: This is only an experience value. It's better to be configurable. - Timeout: 60, - SchemaVer: req.SchemaVar, - Regions: regionInfos, - } - if originalTask != nil { - mppReq.TableRegions = originalTask.PartitionTableRegions - if mppReq.TableRegions != nil { - mppReq.Regions = nil - } - } - - wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPTask, mppReq, kvrpcpb.Context{}) - wrappedReq.StoreTp = getEndPointType(kv.TiFlash) - - // TODO: Handle dispatch task response correctly, including retry logic and cancel logic. - var rpcResp *tikvrpc.Response - invalidPDCache := config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler - bo := backoff.NewBackofferWithTikvBo(param.Bo) - - // If copTasks is not empty, we should send request according to region distribution. - // Or else it's the task without region, which always happens in high layer task without table. - // In that case - if originalTask != nil { - sender := NewRegionBatchRequestSender(c.store.GetRegionCache(), c.store.GetTiKVClient(), param.EnableCollectExecutionInfo) - rpcResp, retry, _, err = sender.SendReqToAddr(bo, originalTask.ctx, originalTask.regionInfos, wrappedReq, tikv.ReadTimeoutMedium) - // No matter what the rpc error is, we won't retry the mpp dispatch tasks. - // TODO: If we want to retry, we must redo the plan fragment cutting and task scheduling. - // That's a hard job but we can try it in the future. - if sender.GetRPCError() != nil { - logutil.BgLogger().Warn("mpp dispatch meet io error", zap.String("error", sender.GetRPCError().Error()), zap.Uint64("timestamp", taskMeta.StartTs), zap.Int64("task", taskMeta.TaskId), zap.Int64("mpp-version", taskMeta.MppVersion)) - if invalidPDCache { - c.store.GetRegionCache().InvalidateTiFlashComputeStores() - } - err = sender.GetRPCError() - } - } else { - rpcResp, err = c.store.GetTiKVClient().SendRequest(param.Ctx, req.Meta.GetAddress(), wrappedReq, tikv.ReadTimeoutMedium) - if errors.Cause(err) == context.Canceled || status.Code(errors.Cause(err)) == codes.Canceled { - retry = false - } else if err != nil { - if invalidPDCache { - c.store.GetRegionCache().InvalidateTiFlashComputeStores() - } - if bo.Backoff(tikv.BoTiFlashRPC(), err) == nil { - retry = true - } - } - } - - if err != nil || retry { - return nil, retry, err - } - - realResp := rpcResp.Resp.(*mpp.DispatchTaskResponse) - if realResp.Error != nil { - return realResp, false, nil - } - - if len(realResp.RetryRegions) > 0 { - logutil.BgLogger().Info("TiFlash found " + strconv.Itoa(len(realResp.RetryRegions)) + " stale regions. Only first " + strconv.Itoa(mathutil.Min(10, len(realResp.RetryRegions))) + " regions will be logged if the log level is higher than Debug") - for index, retry := range realResp.RetryRegions { - id := tikv.NewRegionVerID(retry.Id, retry.RegionEpoch.ConfVer, retry.RegionEpoch.Version) - if index < 10 || log.GetLevel() <= zap.DebugLevel { - logutil.BgLogger().Info("invalid region because tiflash detected stale region", zap.String("region id", id.String())) - } - c.store.GetRegionCache().InvalidateCachedRegionWithReason(id, tikv.EpochNotMatch) - } - } - return realResp, retry, err -} - -// CancelMPPTasks cancels mpp tasks -// NOTE: We do not retry here, because retry is helpless when errors result from TiFlash or Network. If errors occur, the execution on TiFlash will finally stop after some minutes. -// This function is exclusively called, and only the first call succeeds sending tasks and setting all tasks as cancelled, while others will not work. -func (c *MPPClient) CancelMPPTasks(param kv.CancelMPPTasksParam) { - usedStoreAddrs := param.StoreAddr - reqs := param.Reqs - if len(usedStoreAddrs) == 0 || len(reqs) == 0 { - return - } - - firstReq := reqs[0] - killReq := &mpp.CancelTaskRequest{ - Meta: &mpp.TaskMeta{StartTs: firstReq.StartTs, GatherId: firstReq.GatherID, QueryTs: firstReq.MppQueryID.QueryTs, LocalQueryId: firstReq.MppQueryID.LocalQueryID, ServerId: firstReq.MppQueryID.ServerID, MppVersion: firstReq.MppVersion.ToInt64(), ResourceGroupName: firstReq.ResourceGroupName}, - } - - wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPCancel, killReq, kvrpcpb.Context{}) - wrappedReq.StoreTp = getEndPointType(kv.TiFlash) - - // send cancel cmd to all stores where tasks run - invalidPDCache := config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler - wg := util.WaitGroupWrapper{} - gotErr := atomic.Bool{} - for addr := range usedStoreAddrs { - storeAddr := addr - wg.Run(func() { - _, err := c.store.GetTiKVClient().SendRequest(context.Background(), storeAddr, wrappedReq, tikv.ReadTimeoutShort) - logutil.BgLogger().Debug("cancel task", zap.Uint64("query id ", firstReq.StartTs), zap.String("on addr", storeAddr), zap.Int64("mpp-version", firstReq.MppVersion.ToInt64())) - if err != nil { - logutil.BgLogger().Error("cancel task error", zap.Error(err), zap.Uint64("query id", firstReq.StartTs), zap.String("on addr", storeAddr), zap.Int64("mpp-version", firstReq.MppVersion.ToInt64())) - if invalidPDCache { - gotErr.CompareAndSwap(false, true) - } - } - }) - } - wg.Wait() - if invalidPDCache && gotErr.Load() { - c.store.GetRegionCache().InvalidateTiFlashComputeStores() - } -} - -// EstablishMPPConns build a mpp connection to receive data, return valid response when err is nil -func (c *MPPClient) EstablishMPPConns(param kv.EstablishMPPConnsParam) (*tikvrpc.MPPStreamResponse, error) { - req := param.Req - taskMeta := param.TaskMeta - connReq := &mpp.EstablishMPPConnectionRequest{ - SenderMeta: taskMeta, - ReceiverMeta: &mpp.TaskMeta{ - StartTs: req.StartTs, - GatherId: req.GatherID, - QueryTs: req.MppQueryID.QueryTs, - LocalQueryId: req.MppQueryID.LocalQueryID, - ServerId: req.MppQueryID.ServerID, - MppVersion: req.MppVersion.ToInt64(), - TaskId: -1, - ResourceGroupName: req.ResourceGroupName, - }, - } - - var err error - - wrappedReq := tikvrpc.NewRequest(tikvrpc.CmdMPPConn, connReq, kvrpcpb.Context{}) - wrappedReq.StoreTp = getEndPointType(kv.TiFlash) - - // Drain results from root task. - // We don't need to process any special error. When we meet errors, just let it fail. - rpcResp, err := c.store.GetTiKVClient().SendRequest(param.Ctx, req.Meta.GetAddress(), wrappedReq, TiFlashReadTimeoutUltraLong) - - if err != nil { - logutil.BgLogger().Warn("establish mpp connection meet error and cannot retry", zap.String("error", err.Error()), zap.Uint64("timestamp", taskMeta.StartTs), zap.Int64("task", taskMeta.TaskId), zap.Int64("mpp-version", taskMeta.MppVersion)) - if config.GetGlobalConfig().DisaggregatedTiFlash && !config.GetGlobalConfig().UseAutoScaler { - c.store.GetRegionCache().InvalidateTiFlashComputeStores() - } - return nil, err - } - - streamResponse := rpcResp.Resp.(*tikvrpc.MPPStreamResponse) - return streamResponse, nil -} - -// CheckVisibility checks if it is safe to read using given ts. -func (c *MPPClient) CheckVisibility(startTime uint64) error { - return c.store.CheckVisibility(startTime) -} - -func (c *mppStoreCnt) getMPPStoreCount(ctx context.Context, pdClient pd.Client, TTL int64) (int, error) { - failpoint.Inject("mppStoreCountSetLastUpdateTime", func(value failpoint.Value) { - v, _ := strconv.ParseInt(value.(string), 10, 0) - c.lastUpdate = v - }) - - lastUpdate := atomic.LoadInt64(&c.lastUpdate) - now := time.Now().UnixMicro() - isInit := atomic.LoadInt32(&c.initFlag) != 0 - - if now-lastUpdate < TTL { - if isInit { - return int(atomic.LoadInt32(&c.cnt)), nil - } - } - - failpoint.Inject("mppStoreCountSetLastUpdateTimeP2", func(value failpoint.Value) { - v, _ := strconv.ParseInt(value.(string), 10, 0) - c.lastUpdate = v - }) - - if !atomic.CompareAndSwapInt64(&c.lastUpdate, lastUpdate, now) { - if isInit { - return int(atomic.LoadInt32(&c.cnt)), nil - } - // if has't initialized, always fetch latest mpp store info - } - - // update mpp store cache - cnt := 0 - stores, err := pdClient.GetAllStores(ctx, pd.WithExcludeTombstone()) - - failpoint.Inject("mppStoreCountPDError", func(value failpoint.Value) { - if value.(bool) { - err = errors.New("failed to get mpp store count") - } - }) - - if err != nil { - // always to update cache next time - atomic.StoreInt32(&c.initFlag, 0) - return 0, err - } - for _, s := range stores { - if !tikv.LabelFilterNoTiFlashWriteNode(s.GetLabels()) { - continue - } - cnt += 1 - } - failpoint.Inject("mppStoreCountSetMPPCnt", func(value failpoint.Value) { - cnt = value.(int) - }) - - if !isInit || atomic.LoadInt64(&c.lastUpdate) == now { - atomic.StoreInt32(&c.cnt, int32(cnt)) - atomic.StoreInt32(&c.initFlag, 1) - } - - return cnt, nil -} - -// GetMPPStoreCount returns number of TiFlash stores -func (c *MPPClient) GetMPPStoreCount() (int, error) { - return c.store.mppStoreCnt.getMPPStoreCount(c.store.store.Ctx(), c.store.store.GetPDClient(), 120*1e6 /* TTL 120sec */) -} diff --git a/store/copr/store.go b/store/copr/store.go deleted file mode 100644 index 97e17f685f2d3..0000000000000 --- a/store/copr/store.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package copr - -import ( - "context" - "math/rand" - "runtime" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - tidb_config "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/driver/backoff" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/tikv/client-go/v2/config" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" -) - -type kvStore struct { - store *tikv.KVStore - mppStoreCnt *mppStoreCnt -} - -// GetRegionCache returns the region cache instance. -func (s *kvStore) GetRegionCache() *RegionCache { - return &RegionCache{s.store.GetRegionCache()} -} - -// CheckVisibility checks if it is safe to read using given ts. -func (s *kvStore) CheckVisibility(startTime uint64) error { - err := s.store.CheckVisibility(startTime) - return derr.ToTiDBErr(err) -} - -// GetTiKVClient gets the client instance. -func (s *kvStore) GetTiKVClient() tikv.Client { - client := s.store.GetTiKVClient() - return &tikvClient{c: client} -} - -type tikvClient struct { - c tikv.Client -} - -func (c *tikvClient) Close() error { - err := c.c.Close() - return derr.ToTiDBErr(err) -} - -func (c *tikvClient) CloseAddr(addr string) error { - err := c.c.CloseAddr(addr) - return derr.ToTiDBErr(err) -} - -// SendRequest sends Request. -func (c *tikvClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { - res, err := c.c.SendRequest(ctx, addr, req, timeout) - return res, derr.ToTiDBErr(err) -} - -// Store wraps tikv.KVStore and provides coprocessor utilities. -type Store struct { - *kvStore - coprCache *coprCache - replicaReadSeed uint32 - numcpu int -} - -// NewStore creates a new store instance. -func NewStore(s *tikv.KVStore, coprCacheConfig *config.CoprocessorCache) (*Store, error) { - coprCache, err := newCoprCache(coprCacheConfig) - if err != nil { - return nil, errors.Trace(err) - } - - /* #nosec G404 */ - return &Store{ - kvStore: &kvStore{store: s, mppStoreCnt: &mppStoreCnt{}}, - coprCache: coprCache, - replicaReadSeed: rand.Uint32(), - numcpu: runtime.GOMAXPROCS(0), - }, nil -} - -// Close releases resources allocated for coprocessor. -func (s *Store) Close() { - if s.coprCache != nil { - s.coprCache.cache.Close() - } -} - -func (s *Store) nextReplicaReadSeed() uint32 { - return atomic.AddUint32(&s.replicaReadSeed, 1) -} - -// GetClient gets a client instance. -func (s *Store) GetClient() kv.Client { - return &CopClient{ - store: s, - replicaReadSeed: s.nextReplicaReadSeed(), - } -} - -// GetMPPClient gets a mpp client instance. -func (s *Store) GetMPPClient() kv.MPPClient { - return &MPPClient{ - store: s.kvStore, - } -} - -func getEndPointType(t kv.StoreType) tikvrpc.EndpointType { - switch t { - case kv.TiKV: - return tikvrpc.TiKV - case kv.TiFlash: - if tidb_config.GetGlobalConfig().DisaggregatedTiFlash { - return tikvrpc.TiFlashCompute - } - return tikvrpc.TiFlash - case kv.TiDB: - return tikvrpc.TiDB - default: - return tikvrpc.TiKV - } -} - -// Backoffer wraps tikv.Backoffer and converts the error which returns by the functions of tikv.Backoffer to tidb error. -type Backoffer = backoff.Backoffer diff --git a/store/driver/BUILD.bazel b/store/driver/BUILD.bazel deleted file mode 100644 index dc3537af64c42..0000000000000 --- a/store/driver/BUILD.bazel +++ /dev/null @@ -1,67 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "driver", - srcs = ["tikv_driver.go"], - importpath = "github.com/pingcap/tidb/store/driver", - visibility = ["//visibility:public"], - deps = [ - "//executor/importer", - "//kv", - "//sessionctx/variable", - "//store/copr", - "//store/driver/error", - "//store/driver/txn", - "//store/gcworker", - "//util/logutil", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//keepalive", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "driver_test", - timeout = "short", - srcs = [ - "client_test.go", - "config_test.go", - "main_test.go", - "snap_interceptor_test.go", - "sql_fail_test.go", - "txn_test.go", - ], - embed = [":driver"], - flaky = True, - shard_count = 8, - deps = [ - "//domain", - "//kv", - "//parser/model", - "//session", - "//store/copr", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit/testsetup", - "//util", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//mock", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/driver/backoff/BUILD.bazel b/store/driver/backoff/BUILD.bazel deleted file mode 100644 index 2076a9c801a7d..0000000000000 --- a/store/driver/backoff/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "backoff", - srcs = ["backoff.go"], - importpath = "github.com/pingcap/tidb/store/driver/backoff", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//store/driver/error", - "@com_github_tikv_client_go_v2//tikv", - ], -) diff --git a/store/driver/client_test.go b/store/driver/client_test.go deleted file mode 100644 index 473e9fe5b623a..0000000000000 --- a/store/driver/client_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package driver - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/tracing" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" -) - -type mockTiKVClient struct { - tikv.Client - mock.Mock -} - -func (c *mockTiKVClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { - args := c.Called(ctx, addr, req, timeout) - var resp *tikvrpc.Response - if v := args.Get(0); v != nil { - resp = v.(*tikvrpc.Response) - } - return resp, args.Error(1) -} - -func TestInjectTracingClient(t *testing.T) { - cases := []struct { - name string - trace *model.TraceInfo - existSourceStmt *kvrpcpb.SourceStmt - }{ - { - name: "trace is nil", - trace: nil, - }, - { - name: "trace not nil", - trace: &model.TraceInfo{ - ConnectionID: 123, - SessionAlias: "alias123", - }, - }, - { - name: "only connection id in trace valid", - trace: &model.TraceInfo{ - ConnectionID: 456, - }, - }, - { - name: "only session alias in trace valid and sourceStmt exists", - trace: &model.TraceInfo{ - SessionAlias: "alias456", - }, - existSourceStmt: &kvrpcpb.SourceStmt{}, - }, - } - - cli := &mockTiKVClient{} - inject := injectTraceClient{Client: cli} - for _, c := range cases { - ctx := context.Background() - if c.trace != nil { - ctx = tracing.ContextWithTraceInfo(ctx, c.trace) - } - - req := &tikvrpc.Request{} - expectedResp := &tikvrpc.Response{} - verifySendRequest := func(args mock.Arguments) { - inj := args.Get(2).(*tikvrpc.Request) - if c.trace == nil { - require.Nil(t, inj.Context.SourceStmt, c.name) - } else { - require.NotNil(t, inj.Context.SourceStmt, c.name) - require.Equal(t, c.trace.ConnectionID, inj.Context.SourceStmt.ConnectionId, c.name) - require.Equal(t, c.trace.SessionAlias, inj.Context.SourceStmt.SessionAlias, c.name) - } - } - - cli.On("SendRequest", ctx, "addr1", req, time.Second).Return(expectedResp, nil).Once().Run(verifySendRequest) - resp, err := inject.SendRequest(ctx, "addr1", req, time.Second) - cli.AssertExpectations(t) - require.NoError(t, err) - require.Same(t, expectedResp, resp) - - expectedErr := errors.New("mockErr") - cli.On("SendRequest", ctx, "addr2", req, time.Minute).Return(nil, expectedErr).Once().Run(verifySendRequest) - resp, err = inject.SendRequest(ctx, "addr2", req, time.Minute) - require.Same(t, expectedErr, err) - require.Nil(t, resp) - } -} diff --git a/store/driver/error/BUILD.bazel b/store/driver/error/BUILD.bazel deleted file mode 100644 index d7bff2d85a136..0000000000000 --- a/store/driver/error/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "error", - srcs = ["error.go"], - importpath = "github.com/pingcap/tidb/store/driver/error", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//kv", - "//parser/terror", - "//util/dbterror", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_pd_client//errs", - ], -) - -go_test( - name = "error_test", - timeout = "short", - srcs = ["error_test.go"], - embed = [":error"], - flaky = True, - deps = [ - "//parser/terror", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//assert", - "@com_github_tikv_client_go_v2//error", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/driver/error/error.go b/store/driver/error/error.go deleted file mode 100644 index 7716628bdbc6e..0000000000000 --- a/store/driver/error/error.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package error //nolint: predeclared - -import ( - stderrs "errors" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/dbterror" - tikverr "github.com/tikv/client-go/v2/error" - pderr "github.com/tikv/pd/client/errs" -) - -// tikv error instance -var ( - // ErrTokenLimit is the error that token is up to the limit. - ErrTokenLimit = dbterror.ClassTiKV.NewStd(errno.ErrTiKVStoreLimit) - // ErrTiKVServerTimeout is the error when tikv server is timeout. - ErrTiKVServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrTiKVServerTimeout) - ErrTiFlashServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrTiFlashServerTimeout) - // ErrGCTooEarly is the error that GC life time is shorter than transaction duration - ErrGCTooEarly = dbterror.ClassTiKV.NewStd(errno.ErrGCTooEarly) - // ErrTiKVStaleCommand is the error that the command is stale in tikv. - ErrTiKVStaleCommand = dbterror.ClassTiKV.NewStd(errno.ErrTiKVStaleCommand) - // ErrQueryInterrupted is the error when the query is interrupted. - ErrQueryInterrupted = dbterror.ClassTiKV.NewStd(errno.ErrQueryInterrupted) - // ErrTiKVMaxTimestampNotSynced is the error that tikv's max timestamp is not synced. - ErrTiKVMaxTimestampNotSynced = dbterror.ClassTiKV.NewStd(errno.ErrTiKVMaxTimestampNotSynced) - // ErrLockAcquireFailAndNoWaitSet is the error that acquire the lock failed while no wait is setted. - ErrLockAcquireFailAndNoWaitSet = dbterror.ClassTiKV.NewStd(errno.ErrLockAcquireFailAndNoWaitSet) - ErrResolveLockTimeout = dbterror.ClassTiKV.NewStd(errno.ErrResolveLockTimeout) - // ErrLockWaitTimeout is the error that wait for the lock is timeout. - ErrLockWaitTimeout = dbterror.ClassTiKV.NewStd(errno.ErrLockWaitTimeout) - // ErrTiKVServerBusy is the error when tikv server is busy. - ErrTiKVServerBusy = dbterror.ClassTiKV.NewStd(errno.ErrTiKVServerBusy) - // ErrTiFlashServerBusy is the error that tiflash server is busy. - ErrTiFlashServerBusy = dbterror.ClassTiKV.NewStd(errno.ErrTiFlashServerBusy) - // ErrPDServerTimeout is the error when pd server is timeout. - ErrPDServerTimeout = dbterror.ClassTiKV.NewStd(errno.ErrPDServerTimeout) - // ErrRegionUnavailable is the error when region is not available. - ErrRegionUnavailable = dbterror.ClassTiKV.NewStd(errno.ErrRegionUnavailable) - // ErrResourceGroupNotExists is the error when resource group does not exist. - ErrResourceGroupNotExists = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupNotExists) - // ErrResourceGroupConfigUnavailable is the error when resource group configuration is unavailable. - ErrResourceGroupConfigUnavailable = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupConfigUnavailable) - // ErrResourceGroupThrottled is the error when resource group is exceeded quota limitation - ErrResourceGroupThrottled = dbterror.ClassTiKV.NewStd(errno.ErrResourceGroupThrottled) - // ErrUnknown is the unknow error. - ErrUnknown = dbterror.ClassTiKV.NewStd(errno.ErrUnknown) -) - -// Registers error returned from TiKV. -var ( - _ = dbterror.ClassTiKV.NewStd(errno.ErrDataOutOfRange) - _ = dbterror.ClassTiKV.NewStd(errno.ErrTruncatedWrongValue) - _ = dbterror.ClassTiKV.NewStd(errno.ErrDivisionByZero) -) - -// ToTiDBErr checks and converts a tikv error to a tidb error. -func ToTiDBErr(err error) error { - if err == nil { - return nil - } - if tikverr.IsErrNotFound(err) { - return kv.ErrNotExist - } - - var writeConflictInLatch *tikverr.ErrWriteConflictInLatch - if stderrs.As(err, &writeConflictInLatch) { - return kv.ErrWriteConflictInTiDB.FastGenByArgs(writeConflictInLatch.StartTS) - } - - var txnTooLarge *tikverr.ErrTxnTooLarge - if stderrs.As(err, &txnTooLarge) { - return kv.ErrTxnTooLarge.GenWithStackByArgs(txnTooLarge.Size) - } - - if stderrs.Is(err, tikverr.ErrCannotSetNilValue) { - return kv.ErrCannotSetNilValue - } - - var entryTooLarge *tikverr.ErrEntryTooLarge - if stderrs.As(err, &entryTooLarge) { - return kv.ErrEntryTooLarge.GenWithStackByArgs(entryTooLarge.Limit, entryTooLarge.Size) - } - - if stderrs.Is(err, tikverr.ErrInvalidTxn) { - return kv.ErrInvalidTxn - } - - if stderrs.Is(err, tikverr.ErrTiKVServerTimeout) { - return ErrTiKVServerTimeout - } - - var pdServerTimeout *tikverr.ErrPDServerTimeout - if stderrs.As(err, &pdServerTimeout) { - return ErrPDServerTimeout.GenWithStackByArgs(pdServerTimeout.Error()) - } - - if stderrs.Is(err, tikverr.ErrTiFlashServerTimeout) { - return ErrTiFlashServerTimeout - } - - if stderrs.Is(err, tikverr.ErrQueryInterrupted) { - return ErrQueryInterrupted - } - - if stderrs.Is(err, tikverr.ErrTiKVServerBusy) { - return ErrTiKVServerBusy - } - - if stderrs.Is(err, tikverr.ErrTiFlashServerBusy) { - return ErrTiFlashServerBusy - } - - var gcTooEarly *tikverr.ErrGCTooEarly - if stderrs.As(err, &gcTooEarly) { - return ErrGCTooEarly.GenWithStackByArgs(gcTooEarly.TxnStartTS, gcTooEarly.GCSafePoint) - } - - if stderrs.Is(err, tikverr.ErrTiKVStaleCommand) { - return ErrTiKVStaleCommand - } - - if stderrs.Is(err, tikverr.ErrTiKVMaxTimestampNotSynced) { - return ErrTiKVMaxTimestampNotSynced - } - - if stderrs.Is(err, tikverr.ErrLockAcquireFailAndNoWaitSet) { - return ErrLockAcquireFailAndNoWaitSet - } - - if stderrs.Is(err, tikverr.ErrResolveLockTimeout) { - return ErrResolveLockTimeout - } - - if stderrs.Is(err, tikverr.ErrLockWaitTimeout) { - return ErrLockWaitTimeout - } - - if stderrs.Is(err, tikverr.ErrRegionUnavailable) { - return ErrRegionUnavailable - } - - var tokenLimit *tikverr.ErrTokenLimit - if stderrs.As(err, &tokenLimit) { - return ErrTokenLimit.GenWithStackByArgs(tokenLimit.StoreID) - } - - if stderrs.Is(err, tikverr.ErrUnknown) { - return ErrUnknown - } - - if tikverr.IsErrorUndetermined(err) { - return terror.ErrResultUndetermined - } - - var errGetResourceGroup *pderr.ErrClientGetResourceGroup - if stderrs.As(err, &errGetResourceGroup) { - return ErrResourceGroupNotExists.FastGenByArgs(errGetResourceGroup.ResourceGroupName) - } - - if stderrs.Is(err, pderr.ErrClientResourceGroupConfigUnavailable) { - return ErrResourceGroupConfigUnavailable - } - - if stderrs.Is(err, pderr.ErrClientResourceGroupThrottled) { - return ErrResourceGroupThrottled - } - - return errors.Trace(err) -} diff --git a/store/driver/error/error_test.go b/store/driver/error/error_test.go deleted file mode 100644 index 2088d6fee5940..0000000000000 --- a/store/driver/error/error_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package error //nolint: predeclared - -import ( - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/assert" - tikverr "github.com/tikv/client-go/v2/error" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} - -func TestConvertError(t *testing.T) { - wrapFuncs := []func(error) error{ - func(e error) error { return e }, - errors.Trace, - errors.WithStack, - func(e error) error { return errors.Wrap(e, "dummy") }, - } - - // All derived versions converts to `terror.ErrResultUndetermined`. - e := tikverr.ErrResultUndetermined - for _, f := range wrapFuncs { - tidbErr := ToTiDBErr(f(e)) - assert.True(t, errors.ErrorEqual(tidbErr, terror.ErrResultUndetermined)) - } -} diff --git a/store/driver/main_test.go b/store/driver/main_test.go deleted file mode 100644 index 9da08210c56ec..0000000000000 --- a/store/driver/main_test.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package driver - -import ( - "context" - "flag" - "fmt" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/copr" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -var ( - pdAddrs = flag.String("pd-addrs", "127.0.0.1:2379", "pd addrs") - withTiKV = flag.Bool("with-tikv", false, "run tests with TiKV cluster started. (not use the mock server)") -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} - -func createTestStore(t *testing.T) (kv.Storage, *domain.Domain) { - if *withTiKV { - return createTiKVStore(t) - } - return createUnistore(t) -} - -func createTiKVStore(t *testing.T) (kv.Storage, *domain.Domain) { - var d TiKVDriver - store, err := d.Open(fmt.Sprintf("tikv://%s", *pdAddrs)) - require.NoError(t, err) - - // clear storage - txn, err := store.Begin() - require.NoError(t, err) - iter, err := txn.Iter(nil, nil) - require.NoError(t, err) - for iter.Valid() { - require.NoError(t, txn.Delete(iter.Key())) - require.NoError(t, iter.Next()) - } - require.NoError(t, txn.Commit(context.Background())) - - session.ResetStoreForWithTiKVTest(store) - - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - - t.Cleanup(func() { - dom.Close() - require.NoError(t, store.Close()) - }) - - return store, dom -} - -func createUnistore(t *testing.T) (kv.Storage, *domain.Domain) { - client, pdClient, cluster, err := unistore.New("") - require.NoError(t, err) - - unistore.BootstrapWithSingleStore(cluster) - kvStore, err := tikv.NewTestTiKVStore(client, pdClient, nil, nil, 0) - require.NoError(t, err) - - coprStore, err := copr.NewStore(kvStore, nil) - require.NoError(t, err) - - store := &tikvStore{KVStore: kvStore, coprStore: coprStore} - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - - t.Cleanup(func() { - dom.Close() - require.NoError(t, store.Close()) - }) - - return store, dom -} - -func prepareSnapshot(t *testing.T, store kv.Storage, data [][]interface{}) kv.Snapshot { - txn, err := store.Begin() - require.NoError(t, err) - defer func() { - if txn.Valid() { - require.NoError(t, txn.Rollback()) - } - }() - - for _, d := range data { - err = txn.Set(makeBytes(d[0]), makeBytes(d[1])) - require.NoError(t, err) - } - - err = txn.Commit(context.Background()) - require.NoError(t, err) - - return store.GetSnapshot(kv.MaxVersion) -} - -func makeBytes(s interface{}) []byte { - if s == nil { - return nil - } - - switch key := s.(type) { - case string: - return []byte(key) - default: - return key.([]byte) - } -} - -func clearStoreData(t *testing.T, store kv.Storage) { - txn, err := store.Begin() - require.NoError(t, err) - defer func() { - if txn.Valid() { - require.NoError(t, txn.Rollback()) - } - }() - - iter, err := txn.Iter(nil, nil) - require.NoError(t, err) - defer iter.Close() - - for iter.Valid() { - require.NoError(t, txn.Delete(iter.Key())) - require.NoError(t, iter.Next()) - } - - require.NoError(t, txn.Commit(context.Background())) -} diff --git a/store/driver/options/BUILD.bazel b/store/driver/options/BUILD.bazel deleted file mode 100644 index 7a9f093c19f49..0000000000000 --- a/store/driver/options/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "options", - srcs = ["options.go"], - importpath = "github.com/pingcap/tidb/store/driver/options", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "@com_github_tikv_client_go_v2//kv", - ], -) diff --git a/store/driver/options/options.go b/store/driver/options/options.go deleted file mode 100644 index c2b3b52e177b8..0000000000000 --- a/store/driver/options/options.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package options - -import ( - "github.com/pingcap/tidb/kv" - storekv "github.com/tikv/client-go/v2/kv" -) - -// GetTiKVReplicaReadType maps kv.ReplicaReadType to tikv/kv.ReplicaReadType. -func GetTiKVReplicaReadType(t kv.ReplicaReadType) storekv.ReplicaReadType { - switch t { - case kv.ReplicaReadLeader: - return storekv.ReplicaReadLeader - case kv.ReplicaReadFollower: - return storekv.ReplicaReadFollower - case kv.ReplicaReadMixed: - return storekv.ReplicaReadMixed - case kv.ReplicaReadClosest: - return storekv.ReplicaReadMixed - case kv.ReplicaReadClosestAdaptive: - return storekv.ReplicaReadMixed - case kv.ReplicaReadLearner: - return storekv.ReplicaReadLearner - case kv.ReplicaReadPreferLeader: - return storekv.ReplicaReadPreferLeader - } - return 0 -} diff --git a/store/driver/txn/BUILD.bazel b/store/driver/txn/BUILD.bazel deleted file mode 100644 index c2dca28ea46b1..0000000000000 --- a/store/driver/txn/BUILD.bazel +++ /dev/null @@ -1,70 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "txn", - srcs = [ - "batch_getter.go", - "binlog.go", - "error.go", - "scanner.go", - "snapshot.go", - "txn_driver.go", - "union_iter.go", - "unionstore_driver.go", - ], - importpath = "github.com/pingcap/tidb/store/driver/txn", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/model", - "//parser/mysql", - "//sessionctx/binloginfo", - "//store/driver/error", - "//store/driver/options", - "//table/tables", - "//tablecodec", - "//types", - "//util", - "//util/logutil", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@com_github_tikv_client_go_v2//txnkv", - "@com_github_tikv_client_go_v2//txnkv/transaction", - "@com_github_tikv_client_go_v2//txnkv/txnsnapshot", - "@com_github_tikv_client_go_v2//txnkv/txnutil", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "txn_test", - timeout = "short", - srcs = [ - "batch_getter_test.go", - "driver_test.go", - "main_test.go", - "union_iter_test.go", - ], - embed = [":txn"], - flaky = True, - shard_count = 5, - deps = [ - "//kv", - "//testkit/testsetup", - "//util/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/driver/txn/error.go b/store/driver/txn/error.go deleted file mode 100644 index 3ab33579a0a24..0000000000000 --- a/store/driver/txn/error.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package txn - -import ( - "bytes" - "context" - "encoding/hex" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - tikverr "github.com/tikv/client-go/v2/error" - "go.uber.org/zap" -) - -func genKeyExistsError(name string, value string, err error) error { - if err != nil { - logutil.BgLogger().Info("extractKeyExistsErr meets error", zap.Error(err)) - } - return kv.ErrKeyExists.FastGenByArgs(value, name) -} - -// ExtractKeyExistsErrFromHandle returns a ErrKeyExists error from a handle key. -func ExtractKeyExistsErrFromHandle(key kv.Key, value []byte, tblInfo *model.TableInfo) error { - name := tblInfo.Name.String() + ".PRIMARY" - _, handle, err := tablecodec.DecodeRecordKey(key) - if err != nil { - return genKeyExistsError(name, key.String(), err) - } - - if handle.IsInt() { - if pkInfo := tblInfo.GetPkColInfo(); pkInfo != nil { - if mysql.HasUnsignedFlag(pkInfo.GetFlag()) { - handleStr := strconv.FormatUint(uint64(handle.IntValue()), 10) - return genKeyExistsError(name, handleStr, nil) - } - } - return genKeyExistsError(name, handle.String(), nil) - } - - if len(value) == 0 { - return genKeyExistsError(name, handle.String(), errors.New("missing value")) - } - - idxInfo := tables.FindPrimaryIndex(tblInfo) - if idxInfo == nil { - return genKeyExistsError(name, handle.String(), errors.New("cannot find index info")) - } - - cols := make(map[int64]*types.FieldType, len(tblInfo.Columns)) - for _, col := range tblInfo.Columns { - cols[col.ID] = &(col.FieldType) - } - handleColIDs := make([]int64, 0, len(idxInfo.Columns)) - for _, col := range idxInfo.Columns { - handleColIDs = append(handleColIDs, tblInfo.Columns[col.Offset].ID) - } - - row, err := tablecodec.DecodeRowToDatumMap(value, cols, time.Local) - if err != nil { - return genKeyExistsError(name, handle.String(), err) - } - - data, err := tablecodec.DecodeHandleToDatumMap(handle, handleColIDs, cols, time.Local, row) - if err != nil { - return genKeyExistsError(name, handle.String(), err) - } - - valueStr := make([]string, 0, len(data)) - for _, col := range idxInfo.Columns { - d := data[tblInfo.Columns[col.Offset].ID] - str, err := d.ToString() - if err != nil { - return genKeyExistsError(name, key.String(), err) - } - if col.Length > 0 && len(str) > col.Length { - str = str[:col.Length] - } - if types.IsBinaryStr(&tblInfo.Columns[col.Offset].FieldType) || types.IsTypeBit(&tblInfo.Columns[col.Offset].FieldType) { - str = util.FmtNonASCIIPrintableCharToHex(str) - } - valueStr = append(valueStr, str) - } - return genKeyExistsError(name, strings.Join(valueStr, "-"), nil) -} - -// ExtractKeyExistsErrFromIndex returns a ErrKeyExists error from a index key. -func ExtractKeyExistsErrFromIndex(key kv.Key, value []byte, tblInfo *model.TableInfo, indexID int64) error { - var idxInfo *model.IndexInfo - for _, index := range tblInfo.Indices { - if index.ID == indexID { - idxInfo = index - } - } - if idxInfo == nil { - return genKeyExistsError("UNKNOWN", key.String(), errors.New("cannot find index info")) - } - name := tblInfo.Name.String() + "." + idxInfo.Name.String() - - if len(value) == 0 { - return genKeyExistsError(name, key.String(), errors.New("missing value")) - } - - colInfo := tables.BuildRowcodecColInfoForIndexColumns(idxInfo, tblInfo) - values, err := tablecodec.DecodeIndexKV(key, value, len(idxInfo.Columns), tablecodec.HandleNotNeeded, colInfo) - if err != nil { - return genKeyExistsError(name, key.String(), err) - } - valueStr := make([]string, 0, len(values)) - for i, val := range values { - d, err := tablecodec.DecodeColumnValue(val, colInfo[i].Ft, time.Local) - if err != nil { - return genKeyExistsError(name, key.String(), err) - } - str, err := d.ToString() - if err != nil { - return genKeyExistsError(name, key.String(), err) - } - if types.IsBinaryStr(colInfo[i].Ft) || types.IsTypeBit(colInfo[i].Ft) { - str = util.FmtNonASCIIPrintableCharToHex(str) - } - valueStr = append(valueStr, str) - } - return genKeyExistsError(name, strings.Join(valueStr, "-"), nil) -} - -func extractKeyErr(err error) error { - if err == nil { - return nil - } - if e, ok := errors.Cause(err).(*tikverr.ErrWriteConflict); ok { - return newWriteConflictError(e.WriteConflict) - } - if e, ok := errors.Cause(err).(*tikverr.ErrRetryable); ok { - notFoundDetail := prettyLockNotFoundKey(e.Retryable) - return kv.ErrTxnRetryable.GenWithStackByArgs(e.Retryable + " " + notFoundDetail) - } - return derr.ToTiDBErr(err) -} - -func newWriteConflictError(conflict *kvrpcpb.WriteConflict) error { - if conflict == nil { - return kv.ErrWriteConflict - } - var bufConflictKeyTableID bytes.Buffer // table id part of conflict key, which is used to be parsed by upper level to provide more information about the table - var bufConflictKeyRest bytes.Buffer // the rest part of conflict key - var bufPrimaryKeyTableID bytes.Buffer // table id part of primary key - var bufPrimaryKeyRest bytes.Buffer // the rest part of primary key - prettyWriteKey(&bufConflictKeyTableID, &bufConflictKeyRest, conflict.Key) - bufConflictKeyRest.WriteString(", originalKey=" + hex.EncodeToString(conflict.Key)) - bufConflictKeyRest.WriteString(", primary=") - prettyWriteKey(&bufPrimaryKeyTableID, &bufPrimaryKeyRest, conflict.Primary) - bufPrimaryKeyRest.WriteString(", originalPrimaryKey=" + hex.EncodeToString(conflict.Primary)) - return kv.ErrWriteConflict.FastGenByArgs(conflict.StartTs, conflict.ConflictTs, conflict.ConflictCommitTs, - bufConflictKeyTableID.String(), bufConflictKeyRest.String(), bufPrimaryKeyTableID.String(), - bufPrimaryKeyRest.String(), conflict.Reason.String(), - ) -} - -func prettyWriteKey(bufTableID, bufRest *bytes.Buffer, key []byte) { - tableID, indexID, indexValues, err := tablecodec.DecodeIndexKey(key) - if err == nil { - _, err1 := fmt.Fprintf(bufTableID, "{tableID=%d", tableID) - if err1 != nil { - logutil.BgLogger().Error("error", zap.Error(err1)) - } - _, err1 = fmt.Fprintf(bufRest, ", indexID=%d, indexValues={", indexID) - if err1 != nil { - logutil.BgLogger().Error("error", zap.Error(err1)) - } - for _, v := range indexValues { - _, err2 := fmt.Fprintf(bufRest, "%s, ", v) - if err2 != nil { - logutil.BgLogger().Error("error", zap.Error(err2)) - } - } - bufRest.WriteString("}}") - return - } - - tableID, handle, err := tablecodec.DecodeRecordKey(key) - if err == nil { - _, err3 := fmt.Fprintf(bufTableID, "{tableID=%d", tableID) - if err3 != nil { - logutil.BgLogger().Error("error", zap.Error(err3)) - } - _, err3 = fmt.Fprintf(bufRest, ", handle=%s}", handle.String()) - if err3 != nil { - logutil.BgLogger().Error("error", zap.Error(err3)) - } - return - } - - mKey, mField, err := tablecodec.DecodeMetaKey(key) - if err == nil { - _, err3 := fmt.Fprintf(bufRest, "{metaKey=true, key=%s, field=%s}", string(mKey), string(mField)) - if err3 != nil { - logutil.Logger(context.Background()).Error("error", zap.Error(err3)) - } - return - } - - _, err4 := fmt.Fprintf(bufRest, "%#v", key) - if err4 != nil { - logutil.BgLogger().Error("error", zap.Error(err4)) - } -} - -func prettyLockNotFoundKey(rawRetry string) string { - if !strings.Contains(rawRetry, "TxnLockNotFound") { - return "" - } - start := strings.Index(rawRetry, "[") - if start == -1 { - return "" - } - rawRetry = rawRetry[start:] - end := strings.Index(rawRetry, "]") - if end == -1 { - return "" - } - rawRetry = rawRetry[:end+1] - var key []byte - err := json.Unmarshal([]byte(rawRetry), &key) - if err != nil { - return "" - } - var buf1 bytes.Buffer - var buf2 bytes.Buffer - prettyWriteKey(&buf1, &buf2, key) - return buf1.String() + buf2.String() -} diff --git a/store/driver/txn/main_test.go b/store/driver/txn/main_test.go deleted file mode 100644 index 59abd5c7df824..0000000000000 --- a/store/driver/txn/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package txn - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/driver/txn_test.go b/store/driver/txn_test.go deleted file mode 100644 index 95c3964c59716..0000000000000 --- a/store/driver/txn_test.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package driver - -import ( - "context" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore" - "github.com/stretchr/testify/require" -) - -type mockErrInterceptor struct { - err error -} - -func (m *mockErrInterceptor) OnGet(_ context.Context, _ kv.Snapshot, _ kv.Key) ([]byte, error) { - return nil, m.err -} - -func (m *mockErrInterceptor) OnBatchGet(_ context.Context, _ kv.Snapshot, _ []kv.Key) (map[string][]byte, error) { - return nil, m.err -} - -func (m *mockErrInterceptor) OnIter(_ kv.Snapshot, _ kv.Key, _ kv.Key) (kv.Iterator, error) { - return nil, m.err -} - -func (m *mockErrInterceptor) OnIterReverse(_ kv.Snapshot, _ kv.Key, _ kv.Key) (kv.Iterator, error) { - return nil, m.err -} - -func TestTxnGet(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - clearStoreData(t, store) - - prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}}) - txn, err := store.Begin() - require.NoError(t, err) - require.NotNil(t, txn) - - // should return snapshot value if no dirty data - v, err := txn.Get(context.Background(), kv.Key("k1")) - require.NoError(t, err) - require.Equal(t, []byte("v1"), v) - - // insert but not commit - err = txn.Set(kv.Key("k1"), kv.Key("v1+")) - require.NoError(t, err) - - // should return dirty data if dirty data exists - v, err = txn.Get(context.Background(), kv.Key("k1")) - require.NoError(t, err) - require.Equal(t, []byte("v1+"), v) - - err = txn.Set(kv.Key("k2"), []byte("v2+")) - require.NoError(t, err) - - // should return dirty data if dirty data exists - v, err = txn.Get(context.Background(), kv.Key("k2")) - require.NoError(t, err) - require.Equal(t, []byte("v2+"), v) - - // delete but not commit - err = txn.Delete(kv.Key("k1")) - require.NoError(t, err) - - // should return kv.ErrNotExist if deleted - v, err = txn.Get(context.Background(), kv.Key("k1")) - require.Nil(t, v) - require.True(t, kv.ErrNotExist.Equal(err)) - - // should return kv.ErrNotExist if not exist - v, err = txn.Get(context.Background(), kv.Key("kn")) - require.Nil(t, v) - require.True(t, kv.ErrNotExist.Equal(err)) - - // make snapshot returns error - errInterceptor := &mockErrInterceptor{err: errors.New("error")} - txn.SetOption(kv.SnapInterceptor, errInterceptor) - - // should return kv.ErrNotExist because k1 is deleted in memBuff - v, err = txn.Get(context.Background(), kv.Key("k1")) - require.Nil(t, v) - require.True(t, kv.ErrNotExist.Equal(err)) - - // should return dirty data because k2 is in memBuff - v, err = txn.Get(context.Background(), kv.Key("k2")) - require.NoError(t, err) - require.Equal(t, []byte("v2+"), v) - - // should return error because kn is read from snapshot - v, err = txn.Get(context.Background(), kv.Key("kn")) - require.Nil(t, v) - require.Equal(t, errInterceptor.err, err) -} - -func TestTxnBatchGet(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - clearStoreData(t, store) - - prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}, {"k4", "v4"}}) - txn, err := store.Begin() - require.NoError(t, err) - - result, err := txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k2"), kv.Key("k3"), kv.Key("kn")}) - require.NoError(t, err) - require.Equal(t, 3, len(result)) - require.Equal(t, []byte("v1"), result["k1"]) - require.Equal(t, []byte("v2"), result["k2"]) - require.Equal(t, []byte("v3"), result["k3"]) - - // make some dirty data - err = txn.Set(kv.Key("k1"), []byte("v1+")) - require.NoError(t, err) - err = txn.Set(kv.Key("k4"), []byte("v4+")) - require.NoError(t, err) - err = txn.Delete(kv.Key("k2")) - require.NoError(t, err) - - result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k2"), kv.Key("k3"), kv.Key("k4"), kv.Key("kn")}) - require.NoError(t, err) - require.Equal(t, 3, len(result)) - require.Equal(t, []byte("v1+"), result["k1"]) - require.Equal(t, []byte("v3"), result["k3"]) - require.Equal(t, []byte("v4+"), result["k4"]) - - // return data if not read from snapshot - result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k4")}) - require.NoError(t, err) - require.Equal(t, 2, len(result)) - require.Equal(t, []byte("v1+"), result["k1"]) - require.Equal(t, []byte("v4+"), result["k4"]) - - // make snapshot returns error - errInterceptor := &mockErrInterceptor{err: errors.New("error")} - txn.SetOption(kv.SnapInterceptor, errInterceptor) - - // fails if read from snapshot - result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k3")}) - require.Nil(t, result) - require.Equal(t, errInterceptor.err, err) - result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k3"), kv.Key("k4")}) - require.Nil(t, result) - require.Equal(t, errInterceptor.err, err) - result, err = txn.BatchGet(context.Background(), []kv.Key{kv.Key("k1"), kv.Key("k4"), kv.Key("kn")}) - require.Nil(t, result) - require.Equal(t, errInterceptor.err, err) -} - -func TestTxnScan(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - clearStoreData(t, store) - - prepareSnapshot(t, store, [][]interface{}{{"k1", "v1"}, {"k3", "v3"}, {"k5", "v5"}, {"k7", "v7"}, {"k9", "v9"}}) - txn, err := store.Begin() - require.NoError(t, err) - - iter, err := txn.Iter(kv.Key("k3"), kv.Key("k9")) - require.NoError(t, err) - checkIter(t, iter, [][]interface{}{{"k3", "v3"}, {"k5", "v5"}, {"k7", "v7"}}) - - iter, err = txn.IterReverse(kv.Key("k9"), nil) - require.NoError(t, err) - checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k5", "v5"}, {"k3", "v3"}, {"k1", "v1"}}) - - iter, err = txn.IterReverse(kv.Key("k9"), kv.Key("k3")) - require.NoError(t, err) - checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k5", "v5"}, {"k3", "v3"}}) - - // make some dirty data - err = txn.Set(kv.Key("k1"), []byte("v1+")) - require.NoError(t, err) - err = txn.Set(kv.Key("k3"), []byte("v3+")) - require.NoError(t, err) - err = txn.Set(kv.Key("k31"), []byte("v31+")) - require.NoError(t, err) - err = txn.Delete(kv.Key("k5")) - require.NoError(t, err) - - iter, err = txn.Iter(kv.Key("k3"), kv.Key("k9")) - require.NoError(t, err) - checkIter(t, iter, [][]interface{}{{"k3", "v3+"}, {"k31", "v31+"}, {"k7", "v7"}}) - - iter, err = txn.IterReverse(kv.Key("k9"), nil) - require.NoError(t, err) - checkIter(t, iter, [][]interface{}{{"k7", "v7"}, {"k31", "v31+"}, {"k3", "v3+"}, {"k1", "v1+"}}) - - // make snapshot returns error - errInterceptor := &mockErrInterceptor{err: errors.New("error")} - txn.SetOption(kv.SnapInterceptor, errInterceptor) - - iter, err = txn.Iter(kv.Key("k1"), kv.Key("k2")) - require.Equal(t, errInterceptor.err, err) - require.Nil(t, iter) -} diff --git a/store/gcworker/BUILD.bazel b/store/gcworker/BUILD.bazel deleted file mode 100644 index 98a5caffa3453..0000000000000 --- a/store/gcworker/BUILD.bazel +++ /dev/null @@ -1,80 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "gcworker", - srcs = ["gc_worker.go"], - importpath = "github.com/pingcap/tidb/store/gcworker", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//ddl/label", - "//ddl/placement", - "//ddl/util", - "//domain/infosync", - "//kv", - "//metrics", - "//parser/model", - "//parser/terror", - "//privilege", - "//session", - "//sessionctx/variable", - "//tablecodec", - "//util/codec", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_tikv_client_go_v2//error", - "@com_github_tikv_client_go_v2//kv", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//txnkv/rangetask", - "@com_github_tikv_client_go_v2//txnkv/txnlock", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "gcworker_test", - timeout = "short", - srcs = [ - "gc_worker_test.go", - "main_test.go", - ], - embed = [":gcworker"], - flaky = True, - race = "on", - shard_count = 29, - deps = [ - "//ddl/placement", - "//ddl/util", - "//domain", - "//domain/infosync", - "//kv", - "//parser/model", - "//session", - "//store/mockstore", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//oracle/oracles", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//txnkv/txnlock", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/gcworker/main_test.go b/store/gcworker/main_test.go deleted file mode 100644 index 0e8c044a3ef0b..0000000000000 --- a/store/gcworker/main_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package gcworker - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - tikv.EnableFailpoints() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - } - callback := func(i int) int { - // wait for MVCCLevelDB to close, MVCCLevelDB will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/store/helper/BUILD.bazel b/store/helper/BUILD.bazel deleted file mode 100644 index d3d22715d2fea..0000000000000 --- a/store/helper/BUILD.bazel +++ /dev/null @@ -1,54 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "helper", - srcs = ["helper.go"], - importpath = "github.com/pingcap/tidb/store/helper", - visibility = ["//visibility:public"], - deps = [ - "//ddl/placement", - "//kv", - "//metrics", - "//parser/model", - "//store/driver/error", - "//tablecodec", - "//util", - "//util/codec", - "//util/logutil", - "//util/pdapi", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//txnkv/txnlock", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "helper_test", - timeout = "short", - srcs = [ - "helper_test.go", - "main_test.go", - ], - embed = [":helper"], - flaky = True, - shard_count = 6, - deps = [ - "//parser/model", - "//store/mockstore", - "//tablecodec", - "//testkit/testsetup", - "//util/pdapi", - "@com_github_gorilla_mux//:mux", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/store/helper/helper.go b/store/helper/helper.go deleted file mode 100644 index bfc48d661edd1..0000000000000 --- a/store/helper/helper.go +++ /dev/null @@ -1,1221 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package helper - -import ( - "bufio" - "bytes" - "cmp" - "context" - "encoding/hex" - "encoding/json" - "fmt" - "io" - "math" - "net/http" - "net/url" - "slices" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/ddl/placement" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/model" - derr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/pdapi" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - "github.com/tikv/client-go/v2/txnkv/txnlock" - "go.uber.org/zap" -) - -// Storage represents a storage that connects TiKV. -// Methods copied from kv.Storage and tikv.Storage due to limitation of go1.13. -type Storage interface { - Begin(opts ...tikv.TxnOption) (kv.Transaction, error) - GetSnapshot(ver kv.Version) kv.Snapshot - GetClient() kv.Client - GetMPPClient() kv.MPPClient - Close() error - UUID() string - CurrentVersion(txnScope string) (kv.Version, error) - CurrentTimestamp(txnScop string) (uint64, error) - GetOracle() oracle.Oracle - SupportDeleteRange() (supported bool) - Name() string - Describe() string - ShowStatus(ctx context.Context, key string) (interface{}, error) - GetMemCache() kv.MemManager - GetRegionCache() *tikv.RegionCache - SendReq(bo *tikv.Backoffer, req *tikvrpc.Request, regionID tikv.RegionVerID, timeout time.Duration) (*tikvrpc.Response, error) - GetLockResolver() *txnlock.LockResolver - GetSafePointKV() tikv.SafePointKV - UpdateSPCache(cachedSP uint64, cachedTime time.Time) - SetOracle(oracle oracle.Oracle) - SetTiKVClient(client tikv.Client) - GetTiKVClient() tikv.Client - Closed() <-chan struct{} - GetMinSafeTS(txnScope string) uint64 - GetLockWaits() ([]*deadlockpb.WaitForEntry, error) - GetCodec() tikv.Codec -} - -// Helper is a middleware to get some information from tikv/pd. It can be used for TiDB's http api or mem table. -type Helper struct { - Store Storage - RegionCache *tikv.RegionCache -} - -// NewHelper gets a Helper from Storage -func NewHelper(store Storage) *Helper { - return &Helper{ - Store: store, - RegionCache: store.GetRegionCache(), - } -} - -// GetMvccByEncodedKey get the MVCC value by the specific encoded key. -func (h *Helper) GetMvccByEncodedKey(encodedKey kv.Key) (*kvrpcpb.MvccGetByKeyResponse, error) { - keyLocation, err := h.RegionCache.LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) - if err != nil { - return nil, derr.ToTiDBErr(err) - } - - tikvReq := tikvrpc.NewRequest(tikvrpc.CmdMvccGetByKey, &kvrpcpb.MvccGetByKeyRequest{Key: encodedKey}) - kvResp, err := h.Store.SendReq(tikv.NewBackofferWithVars(context.Background(), 500, nil), tikvReq, keyLocation.Region, time.Minute) - if err != nil { - logutil.BgLogger().Info("get MVCC by encoded key failed", - zap.Stringer("encodeKey", encodedKey), - zap.Reflect("region", keyLocation.Region), - zap.Stringer("keyLocation", keyLocation), - zap.Reflect("kvResp", kvResp), - zap.Error(err)) - return nil, errors.Trace(err) - } - return kvResp.Resp.(*kvrpcpb.MvccGetByKeyResponse), nil -} - -// MvccKV wraps the key's mvcc info in tikv. -type MvccKV struct { - Key string `json:"key"` - RegionID uint64 `json:"region_id"` - Value *kvrpcpb.MvccGetByKeyResponse `json:"value"` -} - -// GetMvccByStartTs gets Mvcc info by startTS from tikv. -func (h *Helper) GetMvccByStartTs(startTS uint64, startKey, endKey kv.Key) (*MvccKV, error) { - bo := tikv.NewBackofferWithVars(context.Background(), 5000, nil) - for { - curRegion, err := h.RegionCache.LocateKey(bo, startKey) - if err != nil { - logutil.BgLogger().Error("get MVCC by startTS failed", zap.Uint64("txnStartTS", startTS), - zap.Stringer("startKey", startKey), zap.Error(err)) - return nil, derr.ToTiDBErr(err) - } - - tikvReq := tikvrpc.NewRequest(tikvrpc.CmdMvccGetByStartTs, &kvrpcpb.MvccGetByStartTsRequest{ - StartTs: startTS, - }) - tikvReq.Context.Priority = kvrpcpb.CommandPri_Low - kvResp, err := h.Store.SendReq(bo, tikvReq, curRegion.Region, time.Hour) - if err != nil { - logutil.BgLogger().Error("get MVCC by startTS failed", - zap.Uint64("txnStartTS", startTS), - zap.Stringer("startKey", startKey), - zap.Reflect("region", curRegion.Region), - zap.Stringer("curRegion", curRegion), - zap.Reflect("kvResp", kvResp), - zap.Error(err)) - return nil, errors.Trace(err) - } - data := kvResp.Resp.(*kvrpcpb.MvccGetByStartTsResponse) - if err := data.GetRegionError(); err != nil { - logutil.BgLogger().Warn("get MVCC by startTS failed", - zap.Uint64("txnStartTS", startTS), - zap.Stringer("startKey", startKey), - zap.Reflect("region", curRegion.Region), - zap.Stringer("curRegion", curRegion), - zap.Reflect("kvResp", kvResp), - zap.Stringer("error", err)) - continue - } - - if len(data.GetError()) > 0 { - logutil.BgLogger().Error("get MVCC by startTS failed", - zap.Uint64("txnStartTS", startTS), - zap.Stringer("startKey", startKey), - zap.Reflect("region", curRegion.Region), - zap.Stringer("curRegion", curRegion), - zap.Reflect("kvResp", kvResp), - zap.String("error", data.GetError())) - return nil, errors.New(data.GetError()) - } - - key := data.GetKey() - if len(key) > 0 { - resp := &kvrpcpb.MvccGetByKeyResponse{Info: data.Info, RegionError: data.RegionError, Error: data.Error} - return &MvccKV{Key: strings.ToUpper(hex.EncodeToString(key)), Value: resp, RegionID: curRegion.Region.GetID()}, nil - } - - if len(endKey) > 0 && curRegion.Contains(endKey) { - return nil, nil - } - if len(curRegion.EndKey) == 0 { - return nil, nil - } - startKey = curRegion.EndKey - } -} - -// StoreHotRegionInfos records all hog region stores. -// it's the response of PD. -type StoreHotRegionInfos struct { - AsPeer map[uint64]*HotRegionsStat `json:"as_peer"` - AsLeader map[uint64]*HotRegionsStat `json:"as_leader"` -} - -// HotRegionsStat records echo store's hot region. -// it's the response of PD. -type HotRegionsStat struct { - RegionsStat []RegionStat `json:"statistics"` -} - -// RegionStat records each hot region's statistics -// it's the response of PD. -type RegionStat struct { - RegionID uint64 `json:"region_id"` - FlowBytes float64 `json:"flow_bytes"` - HotDegree int `json:"hot_degree"` -} - -// RegionMetric presents the final metric output entry. -type RegionMetric struct { - FlowBytes uint64 `json:"flow_bytes"` - MaxHotDegree int `json:"max_hot_degree"` - Count int `json:"region_count"` -} - -// ScrapeHotInfo gets the needed hot region information by the url given. -func (h *Helper) ScrapeHotInfo(rw string, allSchemas []*model.DBInfo) ([]HotTableIndex, error) { - regionMetrics, err := h.FetchHotRegion(rw) - if err != nil { - return nil, err - } - return h.FetchRegionTableIndex(regionMetrics, allSchemas) -} - -// FetchHotRegion fetches the hot region information from PD's http api. -func (h *Helper) FetchHotRegion(rw string) (map[uint64]RegionMetric, error) { - var regionResp StoreHotRegionInfos - if err := h.requestPD("FetchHotRegion", "GET", rw, nil, ®ionResp); err != nil { - return nil, err - } - metricCnt := 0 - for _, hotRegions := range regionResp.AsLeader { - metricCnt += len(hotRegions.RegionsStat) - } - metric := make(map[uint64]RegionMetric, metricCnt) - for _, hotRegions := range regionResp.AsLeader { - for _, region := range hotRegions.RegionsStat { - metric[region.RegionID] = RegionMetric{FlowBytes: uint64(region.FlowBytes), MaxHotDegree: region.HotDegree} - } - } - return metric, nil -} - -// TblIndex stores the things to index one table. -type TblIndex struct { - DbName string - TableName string - TableID int64 - IndexName string - IndexID int64 -} - -// FrameItem includes a index's or record's meta data with table's info. -type FrameItem struct { - DBName string `json:"db_name"` - TableName string `json:"table_name"` - TableID int64 `json:"table_id"` - IsRecord bool `json:"is_record"` - RecordID int64 `json:"record_id,omitempty"` - IndexName string `json:"index_name,omitempty"` - IndexID int64 `json:"index_id,omitempty"` - IndexValues []string `json:"index_values,omitempty"` -} - -// RegionFrameRange contains a frame range info which the region covered. -type RegionFrameRange struct { - First *FrameItem // start frame of the region - Last *FrameItem // end frame of the region - region *tikv.KeyLocation // the region -} - -// HotTableIndex contains region and its table/index info. -type HotTableIndex struct { - RegionID uint64 `json:"region_id"` - RegionMetric *RegionMetric `json:"region_metric"` - DbName string `json:"db_name"` - TableName string `json:"table_name"` - TableID int64 `json:"table_id"` - IndexName string `json:"index_name"` - IndexID int64 `json:"index_id"` -} - -// FetchRegionTableIndex constructs a map that maps a table to its hot region information by the given raw hot RegionMetric metrics. -func (h *Helper) FetchRegionTableIndex(metrics map[uint64]RegionMetric, allSchemas []*model.DBInfo) ([]HotTableIndex, error) { - hotTables := make([]HotTableIndex, 0, len(metrics)) - for regionID, regionMetric := range metrics { - regionMetric := regionMetric - t := HotTableIndex{RegionID: regionID, RegionMetric: ®ionMetric} - region, err := h.RegionCache.LocateRegionByID(tikv.NewBackofferWithVars(context.Background(), 500, nil), regionID) - if err != nil { - logutil.BgLogger().Error("locate region failed", zap.Error(err)) - continue - } - - hotRange, err := NewRegionFrameRange(region) - if err != nil { - return nil, err - } - f := h.FindTableIndexOfRegion(allSchemas, hotRange) - if f != nil { - t.DbName = f.DBName - t.TableName = f.TableName - t.TableID = f.TableID - t.IndexName = f.IndexName - t.IndexID = f.IndexID - } - hotTables = append(hotTables, t) - } - - return hotTables, nil -} - -// FindTableIndexOfRegion finds what table is involved in this hot region. And constructs the new frame item for future use. -func (*Helper) FindTableIndexOfRegion(allSchemas []*model.DBInfo, hotRange *RegionFrameRange) *FrameItem { - for _, db := range allSchemas { - for _, tbl := range db.Tables { - if f := findRangeInTable(hotRange, db, tbl); f != nil { - return f - } - } - } - return nil -} - -func findRangeInTable(hotRange *RegionFrameRange, db *model.DBInfo, tbl *model.TableInfo) *FrameItem { - pi := tbl.GetPartitionInfo() - if pi == nil { - return findRangeInPhysicalTable(hotRange, tbl.ID, db.Name.O, tbl.Name.O, tbl.Indices, tbl.IsCommonHandle) - } - - for _, def := range pi.Definitions { - tablePartition := fmt.Sprintf("%s(%s)", tbl.Name.O, def.Name) - if f := findRangeInPhysicalTable(hotRange, def.ID, db.Name.O, tablePartition, tbl.Indices, tbl.IsCommonHandle); f != nil { - return f - } - } - return nil -} - -func findRangeInPhysicalTable(hotRange *RegionFrameRange, physicalID int64, dbName, tblName string, indices []*model.IndexInfo, isCommonHandle bool) *FrameItem { - if f := hotRange.GetRecordFrame(physicalID, dbName, tblName, isCommonHandle); f != nil { - return f - } - for _, idx := range indices { - if f := hotRange.GetIndexFrame(physicalID, idx.ID, dbName, tblName, idx.Name.O); f != nil { - return f - } - } - return nil -} - -// NewRegionFrameRange init a NewRegionFrameRange with region info. -func NewRegionFrameRange(region *tikv.KeyLocation) (idxRange *RegionFrameRange, err error) { - var first, last *FrameItem - // check and init first frame - if len(region.StartKey) > 0 { - first, err = NewFrameItemFromRegionKey(region.StartKey) - if err != nil { - return - } - } else { // empty startKey means start with -infinite - first = &FrameItem{ - IndexID: int64(math.MinInt64), - IsRecord: false, - TableID: int64(math.MinInt64), - } - } - - // check and init last frame - if len(region.EndKey) > 0 { - last, err = NewFrameItemFromRegionKey(region.EndKey) - if err != nil { - return - } - } else { // empty endKey means end with +infinite - last = &FrameItem{ - TableID: int64(math.MaxInt64), - IndexID: int64(math.MaxInt64), - IsRecord: true, - } - } - - idxRange = &RegionFrameRange{ - region: region, - First: first, - Last: last, - } - return idxRange, nil -} - -// NewFrameItemFromRegionKey creates a FrameItem with region's startKey or endKey, -// returns err when key is illegal. -func NewFrameItemFromRegionKey(key []byte) (frame *FrameItem, err error) { - frame = &FrameItem{} - frame.TableID, frame.IndexID, frame.IsRecord, err = tablecodec.DecodeKeyHead(key) - if err == nil { - if frame.IsRecord { - var handle kv.Handle - _, handle, err = tablecodec.DecodeRecordKey(key) - if err == nil { - if handle.IsInt() { - frame.RecordID = handle.IntValue() - } else { - data, err := handle.Data() - if err != nil { - return nil, err - } - frame.IndexName = "PRIMARY" - frame.IndexValues = make([]string, 0, len(data)) - for _, datum := range data { - str, err := datum.ToString() - if err != nil { - return nil, err - } - frame.IndexValues = append(frame.IndexValues, str) - } - } - } - } else { - _, _, frame.IndexValues, err = tablecodec.DecodeIndexKey(key) - } - logutil.BgLogger().Warn("decode region key failed", zap.ByteString("key", key), zap.Error(err)) - // Ignore decode errors. - err = nil - return - } - if bytes.HasPrefix(key, tablecodec.TablePrefix()) { - // If SplitTable is enabled, the key may be `t{id}`. - if len(key) == tablecodec.TableSplitKeyLen { - frame.TableID = tablecodec.DecodeTableID(key) - return frame, nil - } - return nil, errors.Trace(err) - } - - // key start with tablePrefix must be either record key or index key - // That's means table's record key and index key are always together - // in the continuous interval. And for key with prefix smaller than - // tablePrefix, is smaller than all tables. While for key with prefix - // bigger than tablePrefix, means is bigger than all tables. - err = nil - if bytes.Compare(key, tablecodec.TablePrefix()) < 0 { - frame.TableID = math.MinInt64 - frame.IndexID = math.MinInt64 - frame.IsRecord = false - return - } - // bigger than tablePrefix, means is bigger than all tables. - frame.TableID = math.MaxInt64 - frame.TableID = math.MaxInt64 - frame.IsRecord = true - return -} - -// GetRecordFrame returns the record frame of a table. If the table's records -// are not covered by this frame range, it returns nil. -func (r *RegionFrameRange) GetRecordFrame(tableID int64, dbName, tableName string, isCommonHandle bool) (f *FrameItem) { - if tableID == r.First.TableID && r.First.IsRecord { - r.First.DBName, r.First.TableName = dbName, tableName - f = r.First - } else if tableID == r.Last.TableID && r.Last.IsRecord { - r.Last.DBName, r.Last.TableName = dbName, tableName - f = r.Last - } else if tableID >= r.First.TableID && tableID < r.Last.TableID { - f = &FrameItem{ - DBName: dbName, - TableName: tableName, - TableID: tableID, - IsRecord: true, - } - } - if f != nil && f.IsRecord && isCommonHandle { - f.IndexName = "PRIMARY" - } - return -} - -// GetIndexFrame returns the indnex frame of a table. If the table's indices are -// not covered by this frame range, it returns nil. -func (r *RegionFrameRange) GetIndexFrame(tableID, indexID int64, dbName, tableName, indexName string) *FrameItem { - if tableID == r.First.TableID && !r.First.IsRecord && indexID == r.First.IndexID { - r.First.DBName, r.First.TableName, r.First.IndexName = dbName, tableName, indexName - return r.First - } - if tableID == r.Last.TableID && indexID == r.Last.IndexID { - r.Last.DBName, r.Last.TableName, r.Last.IndexName = dbName, tableName, indexName - return r.Last - } - - greaterThanFirst := tableID > r.First.TableID || (tableID == r.First.TableID && !r.First.IsRecord && indexID > r.First.IndexID) - lessThanLast := tableID < r.Last.TableID || (tableID == r.Last.TableID && (r.Last.IsRecord || indexID < r.Last.IndexID)) - if greaterThanFirst && lessThanLast { - return &FrameItem{ - DBName: dbName, - TableName: tableName, - TableID: tableID, - IsRecord: false, - IndexName: indexName, - IndexID: indexID, - } - } - return nil -} - -// RegionPeer stores information of one peer. -type RegionPeer struct { - ID int64 `json:"id"` - StoreID int64 `json:"store_id"` - IsLearner bool `json:"is_learner"` -} - -// RegionEpoch stores the information about its epoch. -type RegionEpoch struct { - ConfVer int64 `json:"conf_ver"` - Version int64 `json:"version"` -} - -// RegionPeerStat stores one field `DownSec` which indicates how long it's down than `RegionPeer`. -type RegionPeerStat struct { - Peer RegionPeer `json:"peer"` - DownSec int64 `json:"down_seconds"` -} - -// RegionInfo stores the information of one region. -type RegionInfo struct { - ID int64 `json:"id"` - StartKey string `json:"start_key"` - EndKey string `json:"end_key"` - Epoch RegionEpoch `json:"epoch"` - Peers []RegionPeer `json:"peers"` - Leader RegionPeer `json:"leader"` - DownPeers []RegionPeerStat `json:"down_peers"` - PendingPeers []RegionPeer `json:"pending_peers"` - WrittenBytes uint64 `json:"written_bytes"` - ReadBytes uint64 `json:"read_bytes"` - ApproximateSize int64 `json:"approximate_size"` - ApproximateKeys int64 `json:"approximate_keys"` - - ReplicationStatus *ReplicationStatus `json:"replication_status,omitempty"` -} - -// RegionsInfo stores the information of regions. -type RegionsInfo struct { - Count int64 `json:"count"` - Regions []RegionInfo `json:"regions"` -} - -// NewRegionsInfo returns RegionsInfo -func NewRegionsInfo() *RegionsInfo { - return &RegionsInfo{ - Regions: make([]RegionInfo, 0), - } -} - -// Merge merged 2 regionsInfo into one -func (r *RegionsInfo) Merge(other *RegionsInfo) *RegionsInfo { - newRegionsInfo := &RegionsInfo{ - Regions: make([]RegionInfo, 0, r.Count+other.Count), - } - m := make(map[int64]RegionInfo, r.Count+other.Count) - for _, region := range r.Regions { - m[region.ID] = region - } - for _, region := range other.Regions { - m[region.ID] = region - } - for _, region := range m { - newRegionsInfo.Regions = append(newRegionsInfo.Regions, region) - } - newRegionsInfo.Count = int64(len(newRegionsInfo.Regions)) - return newRegionsInfo -} - -// ReplicationStatus represents the replication mode status of the region. -type ReplicationStatus struct { - State string `json:"state"` - StateID int64 `json:"state_id"` -} - -// TableInfo stores the information of a table or an index -type TableInfo struct { - DB *model.DBInfo - Table *model.TableInfo - IsPartition bool - Partition *model.PartitionDefinition - IsIndex bool - Index *model.IndexInfo -} - -type withKeyRange interface { - getStartKey() string - getEndKey() string -} - -// isIntersecting returns true if x and y intersect. -func isIntersecting(x, y withKeyRange) bool { - return isIntersectingKeyRange(x, y.getStartKey(), y.getEndKey()) -} - -// isIntersectingKeyRange returns true if [startKey, endKey) intersect with x. -func isIntersectingKeyRange(x withKeyRange, startKey, endKey string) bool { - return !isBeforeKeyRange(x, startKey, endKey) && !isBehindKeyRange(x, startKey, endKey) -} - -// isBehind returns true is x is behind y -func isBehind(x, y withKeyRange) bool { - return isBehindKeyRange(x, y.getStartKey(), y.getEndKey()) -} - -// IsBefore returns true is x is before [startKey, endKey) -func isBeforeKeyRange(x withKeyRange, startKey, _ string) bool { - return x.getEndKey() != "" && x.getEndKey() <= startKey -} - -// IsBehind returns true is x is behind [startKey, endKey) -func isBehindKeyRange(x withKeyRange, _, endKey string) bool { - return endKey != "" && x.getStartKey() >= endKey -} - -func (r *RegionInfo) getStartKey() string { return r.StartKey } -func (r *RegionInfo) getEndKey() string { return r.EndKey } - -// TableInfoWithKeyRange stores table or index informations with its key range. -type TableInfoWithKeyRange struct { - *TableInfo - StartKey string - EndKey string -} - -func (t TableInfoWithKeyRange) getStartKey() string { return t.StartKey } -func (t TableInfoWithKeyRange) getEndKey() string { return t.EndKey } - -// NewTableWithKeyRange constructs TableInfoWithKeyRange for given table, it is exported only for test. -func NewTableWithKeyRange(db *model.DBInfo, table *model.TableInfo) TableInfoWithKeyRange { - return newTableInfoWithKeyRange(db, table, nil, nil) -} - -// NewIndexWithKeyRange constructs TableInfoWithKeyRange for given index, it is exported only for test. -func NewIndexWithKeyRange(db *model.DBInfo, table *model.TableInfo, index *model.IndexInfo) TableInfoWithKeyRange { - return newTableInfoWithKeyRange(db, table, nil, index) -} - -// FilterMemDBs filters memory databases in the input schemas. -func (*Helper) FilterMemDBs(oldSchemas []*model.DBInfo) (schemas []*model.DBInfo) { - for _, dbInfo := range oldSchemas { - if util.IsMemDB(dbInfo.Name.L) { - continue - } - schemas = append(schemas, dbInfo) - } - return -} - -// GetRegionsTableInfo returns a map maps region id to its tables or indices. -// Assuming tables or indices key ranges never intersect. -// Regions key ranges can intersect. -func (h *Helper) GetRegionsTableInfo(regionsInfo *RegionsInfo, schemas []*model.DBInfo) map[int64][]TableInfo { - tables := h.GetTablesInfoWithKeyRange(schemas) - - regions := make([]*RegionInfo, 0, len(regionsInfo.Regions)) - for i := 0; i < len(regionsInfo.Regions); i++ { - regions = append(regions, ®ionsInfo.Regions[i]) - } - - tableInfos := h.ParseRegionsTableInfos(regions, tables) - return tableInfos -} - -func newTableInfoWithKeyRange(db *model.DBInfo, table *model.TableInfo, partition *model.PartitionDefinition, index *model.IndexInfo) TableInfoWithKeyRange { - var sk, ek []byte - if partition == nil && index == nil { - sk, ek = tablecodec.GetTableHandleKeyRange(table.ID) - } else if partition != nil && index == nil { - sk, ek = tablecodec.GetTableHandleKeyRange(partition.ID) - } else if partition == nil && index != nil { - sk, ek = tablecodec.GetTableIndexKeyRange(table.ID, index.ID) - } else { - sk, ek = tablecodec.GetTableIndexKeyRange(partition.ID, index.ID) - } - startKey := bytesKeyToHex(codec.EncodeBytes(nil, sk)) - endKey := bytesKeyToHex(codec.EncodeBytes(nil, ek)) - return TableInfoWithKeyRange{ - &TableInfo{ - DB: db, - Table: table, - IsPartition: partition != nil, - Partition: partition, - IsIndex: index != nil, - Index: index, - }, - startKey, - endKey, - } -} - -// GetTablesInfoWithKeyRange returns a slice containing tableInfos with key ranges of all tables in schemas. -func (*Helper) GetTablesInfoWithKeyRange(schemas []*model.DBInfo) []TableInfoWithKeyRange { - tables := []TableInfoWithKeyRange{} - for _, db := range schemas { - for _, table := range db.Tables { - if table.Partition != nil { - for i := range table.Partition.Definitions { - tables = append(tables, newTableInfoWithKeyRange(db, table, &table.Partition.Definitions[i], nil)) - } - } else { - tables = append(tables, newTableInfoWithKeyRange(db, table, nil, nil)) - } - for _, index := range table.Indices { - if table.Partition == nil || index.Global { - tables = append(tables, newTableInfoWithKeyRange(db, table, nil, index)) - continue - } - for i := range table.Partition.Definitions { - tables = append(tables, newTableInfoWithKeyRange(db, table, &table.Partition.Definitions[i], index)) - } - } - } - } - slices.SortFunc(tables, func(i, j TableInfoWithKeyRange) int { - return cmp.Compare(i.getStartKey(), j.getStartKey()) - }) - return tables -} - -// ParseRegionsTableInfos parses the tables or indices in regions according to key range. -func (*Helper) ParseRegionsTableInfos(regionsInfo []*RegionInfo, tables []TableInfoWithKeyRange) map[int64][]TableInfo { - tableInfos := make(map[int64][]TableInfo, len(regionsInfo)) - - if len(tables) == 0 || len(regionsInfo) == 0 { - return tableInfos - } - // tables is sorted in GetTablesInfoWithKeyRange func - slices.SortFunc(regionsInfo, func(i, j *RegionInfo) int { - return cmp.Compare(i.getStartKey(), j.getStartKey()) - }) - - idx := 0 -OutLoop: - for _, region := range regionsInfo { - id := region.ID - tableInfos[id] = []TableInfo{} - for isBehind(region, &tables[idx]) { - idx++ - if idx >= len(tables) { - break OutLoop - } - } - for i := idx; i < len(tables) && isIntersecting(region, &tables[i]); i++ { - tableInfos[id] = append(tableInfos[id], *tables[i].TableInfo) - } - } - - return tableInfos -} - -func bytesKeyToHex(key []byte) string { - return strings.ToUpper(hex.EncodeToString(key)) -} - -// GetRegionsInfo gets the region information of current store by using PD's api. -func (h *Helper) GetRegionsInfo() (*RegionsInfo, error) { - var regionsInfo RegionsInfo - err := h.requestPD("GetRegions", "GET", pdapi.Regions, nil, ®ionsInfo) - return ®ionsInfo, err -} - -// GetStoreRegionsInfo gets the region in given store. -func (h *Helper) GetStoreRegionsInfo(storeID uint64) (*RegionsInfo, error) { - var regionsInfo RegionsInfo - err := h.requestPD("GetStoreRegions", "GET", pdapi.StoreRegions+"/"+strconv.FormatUint(storeID, 10), nil, ®ionsInfo) - return ®ionsInfo, err -} - -// GetRegionInfoByID gets the region information of the region ID by using PD's api. -func (h *Helper) GetRegionInfoByID(regionID uint64) (*RegionInfo, error) { - var regionInfo RegionInfo - err := h.requestPD("GetRegionByID", "GET", pdapi.RegionByID+"/"+strconv.FormatUint(regionID, 10), nil, ®ionInfo) - return ®ionInfo, err -} - -// GetRegionsInfoByRange scans region by key range -func (h *Helper) GetRegionsInfoByRange(sk, ek []byte) (*RegionsInfo, error) { - var regionsInfo RegionsInfo - err := h.requestPD("GetRegionByRange", "GET", fmt.Sprintf("%v?key=%s&end_key=%s&limit=-1", pdapi.ScanRegions, - url.QueryEscape(string(sk)), url.QueryEscape(string(ek))), nil, ®ionsInfo) - return ®ionsInfo, err -} - -// GetRegionByKey gets regioninfo by key -func (h *Helper) GetRegionByKey(k []byte) (*RegionInfo, error) { - var regionInfo RegionInfo - err := h.requestPD("GetRegionByKey", "GET", fmt.Sprintf("%v/%v", pdapi.RegionKey, url.QueryEscape(string(k))), nil, ®ionInfo) - return ®ionInfo, err -} - -// request PD API, decode the response body into res -func (h *Helper) requestPD(apiName, method, uri string, body io.Reader, res interface{}) error { - etcd, ok := h.Store.(kv.EtcdBackend) - if !ok { - return errors.WithStack(errors.New("not implemented")) - } - pdHosts, err := etcd.EtcdAddrs() - if err != nil { - return err - } - if len(pdHosts) == 0 { - return errors.New("pd unavailable") - } - for _, host := range pdHosts { - err = requestPDForOneHost(host, apiName, method, uri, body, res) - if err == nil { - break - } - // Try to request from another PD node when some nodes may down. - } - return err -} - -func requestPDForOneHost(host, apiName, method, uri string, body io.Reader, res interface{}) error { - urlVar := fmt.Sprintf("%s://%s%s", util.InternalHTTPSchema(), host, uri) - logutil.BgLogger().Debug("RequestPD URL", zap.String("url", urlVar)) - req, err := http.NewRequest(method, urlVar, body) - if err != nil { - logutil.BgLogger().Warn("requestPDForOneHost new request failed", - zap.String("url", urlVar), zap.Error(err)) - return errors.Trace(err) - } - start := time.Now() - resp, err := util.InternalHTTPClient().Do(req) - if err != nil { - metrics.PDAPIRequestCounter.WithLabelValues(apiName, "network error").Inc() - logutil.BgLogger().Warn("requestPDForOneHost do request failed", - zap.String("url", urlVar), zap.Error(err)) - return errors.Trace(err) - } - metrics.PDAPIExecutionHistogram.WithLabelValues(apiName).Observe(time.Since(start).Seconds()) - metrics.PDAPIRequestCounter.WithLabelValues(apiName, resp.Status).Inc() - defer func() { - err = resp.Body.Close() - if err != nil { - logutil.BgLogger().Warn("requestPDForOneHost close body failed", - zap.String("url", urlVar), zap.Error(err)) - } - }() - - if resp.StatusCode != http.StatusOK { - logFields := []zap.Field{ - zap.String("url", urlVar), - zap.String("status", resp.Status), - } - - bs, readErr := io.ReadAll(resp.Body) - if readErr != nil { - logFields = append(logFields, zap.NamedError("readBodyError", err)) - } else { - logFields = append(logFields, zap.ByteString("body", bs)) - } - - logutil.BgLogger().Warn("requestPDForOneHost failed with non 200 status", logFields...) - return errors.Errorf("PD request failed with status: '%s'", resp.Status) - } - - err = json.NewDecoder(resp.Body).Decode(res) - if err != nil { - return errors.Trace(err) - } - return nil -} - -// StoresStat stores all information get from PD's api. -type StoresStat struct { - Count int `json:"count"` - Stores []StoreStat `json:"stores"` -} - -// StoreStat stores information of one store. -type StoreStat struct { - Store StoreBaseStat `json:"store"` - Status StoreDetailStat `json:"status"` -} - -// StoreBaseStat stores the basic information of one store. -type StoreBaseStat struct { - ID int64 `json:"id"` - Address string `json:"address"` - State int64 `json:"state"` - StateName string `json:"state_name"` - Version string `json:"version"` - Labels []StoreLabel `json:"labels"` - StatusAddress string `json:"status_address"` - GitHash string `json:"git_hash"` - StartTimestamp int64 `json:"start_timestamp"` -} - -// StoreLabel stores the information of one store label. -type StoreLabel struct { - Key string `json:"key"` - Value string `json:"value"` -} - -// StoreDetailStat stores the detail information of one store. -type StoreDetailStat struct { - Capacity string `json:"capacity"` - Available string `json:"available"` - LeaderCount int64 `json:"leader_count"` - LeaderWeight float64 `json:"leader_weight"` - LeaderScore float64 `json:"leader_score"` - LeaderSize int64 `json:"leader_size"` - RegionCount int64 `json:"region_count"` - RegionWeight float64 `json:"region_weight"` - RegionScore float64 `json:"region_score"` - RegionSize int64 `json:"region_size"` - StartTs time.Time `json:"start_ts"` - LastHeartbeatTs time.Time `json:"last_heartbeat_ts"` - Uptime string `json:"uptime"` -} - -// GetStoresStat gets the TiKV store information by accessing PD's api. -func (h *Helper) GetStoresStat() (*StoresStat, error) { - var storesStat StoresStat - err := h.requestPD("GetStoresStat", "GET", pdapi.Stores, nil, &storesStat) - return &storesStat, err -} - -// GetPDAddr return the PD Address. -func (h *Helper) GetPDAddr() ([]string, error) { - etcd, ok := h.Store.(kv.EtcdBackend) - if !ok { - return nil, errors.New("not implemented") - } - pdAddrs, err := etcd.EtcdAddrs() - if err != nil { - return nil, err - } - if len(pdAddrs) == 0 { - return nil, errors.New("pd unavailable") - } - return pdAddrs, nil -} - -// PDRegionStats is the json response from PD. -type PDRegionStats struct { - Count int `json:"count"` - EmptyCount int `json:"empty_count"` - StorageSize int64 `json:"storage_size"` - StorageKeys int64 `json:"storage_keys"` - StoreLeaderCount map[uint64]int `json:"store_leader_count"` - StorePeerCount map[uint64]int `json:"store_peer_count"` -} - -// GetPDRegionStats get the RegionStats by tableID. -func (h *Helper) GetPDRegionStats(tableID int64, stats *PDRegionStats, noIndexStats bool) error { - pdAddrs, err := h.GetPDAddr() - if err != nil { - return errors.Trace(err) - } - - var startKey, endKey []byte - if noIndexStats { - startKey = tablecodec.GenTableRecordPrefix(tableID) - endKey = kv.Key(startKey).PrefixNext() - } else { - startKey = tablecodec.EncodeTablePrefix(tableID) - endKey = kv.Key(startKey).PrefixNext() - } - startKey = codec.EncodeBytes([]byte{}, startKey) - endKey = codec.EncodeBytes([]byte{}, endKey) - - statURL := fmt.Sprintf("%s://%s/pd/api/v1/stats/region?start_key=%s&end_key=%s", - util.InternalHTTPSchema(), - pdAddrs[0], - url.QueryEscape(string(startKey)), - url.QueryEscape(string(endKey))) - - resp, err := util.InternalHTTPClient().Get(statURL) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = resp.Body.Close(); err != nil { - logutil.BgLogger().Error("err", zap.Error(err)) - } - }() - if resp.StatusCode != http.StatusOK { - body, err := io.ReadAll(resp.Body) - if err != nil { - return errors.Errorf("GetPDRegionStats %d: %s", resp.StatusCode, err) - } - return errors.Errorf("GetPDRegionStats %d: %s", resp.StatusCode, string(body)) - } - dec := json.NewDecoder(resp.Body) - - return dec.Decode(stats) -} - -// DeletePlacementRule is to delete placement rule for certain group. -func (h *Helper) DeletePlacementRule(group string, ruleID string) error { - pdAddrs, err := h.GetPDAddr() - if err != nil { - return errors.Trace(err) - } - - deleteURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rule/%v/%v", - util.InternalHTTPSchema(), - pdAddrs[0], - group, - ruleID, - ) - - req, err := http.NewRequest("DELETE", deleteURL, nil) - if err != nil { - return errors.Trace(err) - } - - resp, err := util.InternalHTTPClient().Do(req) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = resp.Body.Close(); err != nil { - logutil.BgLogger().Error("err", zap.Error(err)) - } - }() - if resp.StatusCode != http.StatusOK { - return errors.New("DeletePlacementRule returns error") - } - return nil -} - -// SetPlacementRule is a helper function to set placement rule. -func (h *Helper) SetPlacementRule(rule placement.Rule) error { - pdAddrs, err := h.GetPDAddr() - if err != nil { - return errors.Trace(err) - } - m, _ := json.Marshal(rule) - - postURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rule", - util.InternalHTTPSchema(), - pdAddrs[0], - ) - buf := bytes.NewBuffer(m) - resp, err := util.InternalHTTPClient().Post(postURL, "application/json", buf) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = resp.Body.Close(); err != nil { - logutil.BgLogger().Error("err", zap.Error(err)) - } - }() - if resp.StatusCode != http.StatusOK { - return errors.New("SetPlacementRule returns error") - } - return nil -} - -// GetGroupRules to get all placement rule in a certain group. -func (h *Helper) GetGroupRules(group string) ([]placement.Rule, error) { - pdAddrs, err := h.GetPDAddr() - if err != nil { - return nil, errors.Trace(err) - } - - getURL := fmt.Sprintf("%s://%s/pd/api/v1/config/rules/group/%s", - util.InternalHTTPSchema(), - pdAddrs[0], - group, - ) - - resp, err := util.InternalHTTPClient().Get(getURL) - if err != nil { - return nil, errors.Trace(err) - } - defer func() { - if err = resp.Body.Close(); err != nil { - logutil.BgLogger().Error("err", zap.Error(err)) - } - }() - - if resp.StatusCode != http.StatusOK { - return nil, errors.New("GetGroupRules returns error") - } - - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(resp.Body) - if err != nil { - return nil, errors.Trace(err) - } - - var rules []placement.Rule - err = json.Unmarshal(buf.Bytes(), &rules) - if err != nil { - return nil, errors.Trace(err) - } - - return rules, nil -} - -// PostAccelerateSchedule sends `regions/accelerate-schedule` request. -func (h *Helper) PostAccelerateSchedule(tableID int64) error { - pdAddrs, err := h.GetPDAddr() - if err != nil { - return errors.Trace(err) - } - startKey := tablecodec.GenTableRecordPrefix(tableID) - endKey := tablecodec.EncodeTablePrefix(tableID + 1) - startKey = codec.EncodeBytes([]byte{}, startKey) - endKey = codec.EncodeBytes([]byte{}, endKey) - - postURL := fmt.Sprintf("%s://%s/pd/api/v1/regions/accelerate-schedule", - util.InternalHTTPSchema(), - pdAddrs[0]) - - input := map[string]string{ - "start_key": url.QueryEscape(string(startKey)), - "end_key": url.QueryEscape(string(endKey)), - } - v, err := json.Marshal(input) - if err != nil { - return errors.Trace(err) - } - resp, err := util.InternalHTTPClient().Post(postURL, "application/json", bytes.NewBuffer(v)) - if err != nil { - return errors.Trace(err) - } - defer func() { - if err = resp.Body.Close(); err != nil { - logutil.BgLogger().Error("err", zap.Error(err)) - } - }() - return nil -} - -// GetTiFlashTableIDFromEndKey computes tableID from pd rule's endKey. -func GetTiFlashTableIDFromEndKey(endKey string) int64 { - e, _ := hex.DecodeString(endKey) - _, decodedEndKey, _ := codec.DecodeBytes(e, []byte{}) - tableID := tablecodec.DecodeTableID(decodedEndKey) - tableID-- - return tableID -} - -// ComputeTiFlashStatus is helper function for CollectTiFlashStatus. -func ComputeTiFlashStatus(reader *bufio.Reader, regionReplica *map[int64]int) error { - ns, err := reader.ReadString('\n') - if err != nil { - return errors.Trace(err) - } - // The count - ns = strings.Trim(ns, "\r\n\t") - n, err := strconv.ParseInt(ns, 10, 64) - if err != nil { - return errors.Trace(err) - } - // The regions - regions, err := reader.ReadString('\n') - if err != nil { - return errors.Trace(err) - } - regions = strings.Trim(regions, "\r\n\t") - splits := strings.Split(regions, " ") - realN := int64(0) - for _, s := range splits { - // For (`table`, `store`), has region `r` - if s == "" { - continue - } - realN++ - r, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return errors.Trace(err) - } - if c, ok := (*regionReplica)[r]; ok { - (*regionReplica)[r] = c + 1 - } else { - (*regionReplica)[r] = 1 - } - } - if n != realN { - logutil.BgLogger().Warn("ComputeTiFlashStatus count check failed", zap.Int64("claim", n), zap.Int64("real", realN)) - } - return nil -} - -// CollectTiFlashStatus query sync status of one table from TiFlash store. -// `regionReplica` is a map from RegionID to count of TiFlash Replicas in this region. -func CollectTiFlashStatus(statusAddress string, keyspaceID tikv.KeyspaceID, tableID int64, regionReplica *map[int64]int) error { - // The new query schema is like: http:///tiflash/sync-status/keyspace//table/. - // For TiDB forward compatibility, we define the Nullspace as the "keyspace" of the old table. - // The query URL is like: http:///sync-status/keyspace//table/ - // The old query schema is like: http:///sync-status/ - // This API is preserved in TiFlash for compatibility with old versions of TiDB. - statURL := fmt.Sprintf("%s://%s/tiflash/sync-status/keyspace/%d/table/%d", - util.InternalHTTPSchema(), - statusAddress, - keyspaceID, - tableID, - ) - resp, err := util.InternalHTTPClient().Get(statURL) - if err != nil { - return errors.Trace(err) - } - - defer func() { - err = resp.Body.Close() - if err != nil { - logutil.BgLogger().Error("close body failed", zap.Error(err)) - } - }() - - reader := bufio.NewReader(resp.Body) - if err = ComputeTiFlashStatus(reader, regionReplica); err != nil { - return errors.Trace(err) - } - return nil -} diff --git a/store/helper/helper_test.go b/store/helper/helper_test.go deleted file mode 100644 index 88c12a104a53a..0000000000000 --- a/store/helper/helper_test.go +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package helper_test - -import ( - "bufio" - "crypto/tls" - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "strings" - "testing" - "time" - - "github.com/gorilla/mux" - "github.com/pingcap/log" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/util/pdapi" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" - "go.opencensus.io/stats/view" - "go.uber.org/zap" -) - -func TestHotRegion(t *testing.T) { - store := createMockStore(t) - - h := helper.Helper{ - Store: store, - RegionCache: store.GetRegionCache(), - } - regionMetric, err := h.FetchHotRegion(pdapi.HotRead) - require.NoError(t, err) - - expected := map[uint64]helper.RegionMetric{ - 2: { - FlowBytes: 100, - MaxHotDegree: 1, - Count: 0, - }, - 4: { - FlowBytes: 200, - MaxHotDegree: 2, - Count: 0, - }, - } - require.Equal(t, expected, regionMetric) - - dbInfo := &model.DBInfo{ - Name: model.NewCIStr("test"), - } - require.NoError(t, err) - - res, err := h.FetchRegionTableIndex(regionMetric, []*model.DBInfo{dbInfo}) - require.NotEqual(t, res[0].RegionMetric, res[1].RegionMetric) - require.NoError(t, err) -} - -func TestGetRegionsTableInfo(t *testing.T) { - store := createMockStore(t) - - h := helper.NewHelper(store) - regionsInfo := getMockTiKVRegionsInfo() - schemas := getMockRegionsTableInfoSchema() - tableInfos := h.GetRegionsTableInfo(regionsInfo, schemas) - require.Equal(t, getRegionsTableInfoAns(schemas), tableInfos) -} - -func TestTiKVRegionsInfo(t *testing.T) { - store := createMockStore(t) - - h := helper.Helper{ - Store: store, - RegionCache: store.GetRegionCache(), - } - regionsInfo, err := h.GetRegionsInfo() - require.NoError(t, err) - require.Equal(t, getMockTiKVRegionsInfo(), regionsInfo) -} - -func TestTiKVStoresStat(t *testing.T) { - store := createMockStore(t) - - h := helper.Helper{ - Store: store, - RegionCache: store.GetRegionCache(), - } - - stat, err := h.GetStoresStat() - require.NoError(t, err) - - data, err := json.Marshal(stat) - require.NoError(t, err) - - expected := `{"count":1,"stores":[{"store":{"id":1,"address":"127.0.0.1:20160","state":0,"state_name":"Up","version":"3.0.0-beta","labels":[{"key":"test","value":"test"}],"status_address":"","git_hash":"","start_timestamp":0},"status":{"capacity":"60 GiB","available":"100 GiB","leader_count":10,"leader_weight":999999.999999,"leader_score":999999.999999,"leader_size":1000,"region_count":200,"region_weight":999999.999999,"region_score":999999.999999,"region_size":1000,"start_ts":"2019-04-23T19:30:30+08:00","last_heartbeat_ts":"2019-04-23T19:31:30+08:00","uptime":"1h30m"}}]}` - require.Equal(t, expected, string(data)) -} - -type mockStore struct { - helper.Storage - pdAddrs []string -} - -func (s *mockStore) EtcdAddrs() ([]string, error) { - return s.pdAddrs, nil -} - -func (s *mockStore) StartGCWorker() error { - panic("not implemented") -} - -func (s *mockStore) TLSConfig() *tls.Config { - panic("not implemented") -} - -func (s *mockStore) Name() string { - return "mock store" -} - -func (s *mockStore) Describe() string { - return "" -} - -func createMockStore(t *testing.T) (store helper.Storage) { - s, err := mockstore.NewMockStore( - mockstore.WithClusterInspector(func(c testutils.Cluster) { - mockstore.BootstrapWithMultiRegions(c, []byte("x")) - }), - ) - require.NoError(t, err) - - server := mockPDHTTPServer() - - store = &mockStore{ - s.(helper.Storage), - []string{"invalid_pd_address", server.URL[len("http://"):]}, - } - - t.Cleanup(func() { - server.Close() - view.Stop() - require.NoError(t, store.Close()) - }) - - return -} - -func mockPDHTTPServer() *httptest.Server { - router := mux.NewRouter() - router.HandleFunc(pdapi.HotRead, mockHotRegionResponse) - router.HandleFunc(pdapi.Regions, mockTiKVRegionsInfoResponse) - router.HandleFunc(pdapi.Stores, mockStoreStatResponse) - serverMux := http.NewServeMux() - serverMux.Handle("/", router) - return httptest.NewServer(serverMux) -} - -func mockHotRegionResponse(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - regionsStat := helper.HotRegionsStat{ - RegionsStat: []helper.RegionStat{ - { - FlowBytes: 100, - RegionID: 2, - HotDegree: 1, - }, - { - FlowBytes: 200, - RegionID: 4, - HotDegree: 2, - }, - }, - } - resp := helper.StoreHotRegionInfos{ - AsLeader: make(map[uint64]*helper.HotRegionsStat), - } - resp.AsLeader[0] = ®ionsStat - data, err := json.MarshalIndent(resp, "", " ") - if err != nil { - log.Panic("json marshal failed", zap.Error(err)) - } - _, err = w.Write(data) - if err != nil { - log.Panic("write http response failed", zap.Error(err)) - } -} - -func getMockRegionsTableInfoSchema() []*model.DBInfo { - return []*model.DBInfo{ - { - Name: model.NewCIStr("test"), - Tables: []*model.TableInfo{ - { - ID: 41, - Indices: []*model.IndexInfo{{ID: 1}}, - }, - { - ID: 63, - Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}}, - }, - { - ID: 66, - Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}, {ID: 3}}, - }, - }, - }, - } -} - -func getRegionsTableInfoAns(dbs []*model.DBInfo) map[int64][]helper.TableInfo { - ans := make(map[int64][]helper.TableInfo) - db := dbs[0] - ans[1] = []helper.TableInfo{} - ans[2] = []helper.TableInfo{ - {db, db.Tables[0], false, nil, true, db.Tables[0].Indices[0]}, - {db, db.Tables[0], false, nil, false, nil}, - } - ans[3] = []helper.TableInfo{ - {db, db.Tables[1], false, nil, true, db.Tables[1].Indices[0]}, - {db, db.Tables[1], false, nil, true, db.Tables[1].Indices[1]}, - {db, db.Tables[1], false, nil, false, nil}, - } - ans[4] = []helper.TableInfo{ - {db, db.Tables[2], false, nil, false, nil}, - } - ans[5] = []helper.TableInfo{ - {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[2]}, - {db, db.Tables[2], false, nil, false, nil}, - } - ans[6] = []helper.TableInfo{ - {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[0]}, - } - ans[7] = []helper.TableInfo{ - {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[1]}, - } - ans[8] = []helper.TableInfo{ - {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[1]}, - {db, db.Tables[2], false, nil, true, db.Tables[2].Indices[2]}, - {db, db.Tables[2], false, nil, false, nil}, - } - return ans -} - -func getMockTiKVRegionsInfo() *helper.RegionsInfo { - regions := []helper.RegionInfo{ - { - ID: 1, - StartKey: "", - EndKey: "12341234", - Epoch: helper.RegionEpoch{ - ConfVer: 1, - Version: 1, - }, - Peers: []helper.RegionPeer{ - {ID: 2, StoreID: 1}, - {ID: 15, StoreID: 51}, - {ID: 66, StoreID: 99, IsLearner: true}, - {ID: 123, StoreID: 111, IsLearner: true}, - }, - Leader: helper.RegionPeer{ - ID: 2, - StoreID: 1, - }, - DownPeers: []helper.RegionPeerStat{ - { - helper.RegionPeer{ID: 66, StoreID: 99, IsLearner: true}, - 120, - }, - }, - PendingPeers: []helper.RegionPeer{ - {ID: 15, StoreID: 51}, - }, - WrittenBytes: 100, - ReadBytes: 1000, - ApproximateKeys: 200, - ApproximateSize: 500, - }, - // table: 41, record + index: 1 - { - ID: 2, - StartKey: "7480000000000000FF295F698000000000FF0000010000000000FA", - EndKey: "7480000000000000FF2B5F698000000000FF0000010000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 3, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 3, StoreID: 1}, - }, - // table: 63, record + index: 1, 2 - { - ID: 3, - StartKey: "7480000000000000FF3F5F698000000000FF0000010000000000FA", - EndKey: "7480000000000000FF425F698000000000FF0000010000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 4, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 4, StoreID: 1}, - }, - // table: 66, record - { - ID: 4, - StartKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", - EndKey: "", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 5, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 5, StoreID: 1}, - }, - // table: 66, record + index: 3 - { - ID: 5, - StartKey: "7480000000000000FF425F698000000000FF0000030000000000FA", - EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 6, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 6, StoreID: 1}, - }, - // table: 66, index: 1 - { - ID: 6, - StartKey: "7480000000000000FF425F698000000000FF0000010000000000FA", - EndKey: "7480000000000000FF425F698000000000FF0000020000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 7, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 7, StoreID: 1}, - }, - // table: 66, index: 2 - { - ID: 7, - StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA", - EndKey: "7480000000000000FF425F698000000000FF0000030000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 8, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 8, StoreID: 1}, - }, - // merge region 7, 5 - { - ID: 8, - StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA", - EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA", - Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []helper.RegionPeer{{ID: 9, StoreID: 1}}, - Leader: helper.RegionPeer{ID: 9, StoreID: 1}, - }, - } - return &helper.RegionsInfo{ - Count: int64(len(regions)), - Regions: regions, - } -} - -func mockTiKVRegionsInfoResponse(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - resp := getMockTiKVRegionsInfo() - data, err := json.MarshalIndent(resp, "", " ") - if err != nil { - log.Panic("json marshal failed", zap.Error(err)) - } - _, err = w.Write(data) - if err != nil { - log.Panic("write http response failed", zap.Error(err)) - } -} - -func mockStoreStatResponse(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - startTs, err := time.Parse(time.RFC3339, "2019-04-23T19:30:30+08:00") - if err != nil { - log.Panic("mock tikv store api response failed", zap.Error(err)) - } - lastHeartbeatTs, err := time.Parse(time.RFC3339, "2019-04-23T19:31:30+08:00") - if err != nil { - log.Panic("mock tikv store api response failed", zap.Error(err)) - } - storesStat := helper.StoresStat{ - Count: 1, - Stores: []helper.StoreStat{ - { - Store: helper.StoreBaseStat{ - ID: 1, - Address: "127.0.0.1:20160", - State: 0, - StateName: "Up", - Version: "3.0.0-beta", - Labels: []helper.StoreLabel{ - { - Key: "test", - Value: "test", - }, - }, - }, - Status: helper.StoreDetailStat{ - Capacity: "60 GiB", - Available: "100 GiB", - LeaderCount: 10, - LeaderWeight: 999999.999999, - LeaderScore: 999999.999999, - LeaderSize: 1000, - RegionCount: 200, - RegionWeight: 999999.999999, - RegionScore: 999999.999999, - RegionSize: 1000, - StartTs: startTs, - LastHeartbeatTs: lastHeartbeatTs, - Uptime: "1h30m", - }, - }, - }, - } - data, err := json.MarshalIndent(storesStat, "", " ") - if err != nil { - log.Panic("json marshal failed", zap.Error(err)) - } - _, err = w.Write(data) - if err != nil { - log.Panic("write http response failed", zap.Error(err)) - } -} - -func TestComputeTiFlashStatus(t *testing.T) { - regionReplica := make(map[int64]int) - // There are no region in this TiFlash store. - br1 := bufio.NewReader(strings.NewReader("0\n\n")) - // There are 2 regions 1009/1010 in this TiFlash store. - br2 := bufio.NewReader(strings.NewReader("2\n1009 1010 \n")) - err := helper.ComputeTiFlashStatus(br1, ®ionReplica) - require.NoError(t, err) - err = helper.ComputeTiFlashStatus(br2, ®ionReplica) - require.NoError(t, err) - require.Equal(t, len(regionReplica), 2) - v, ok := regionReplica[1009] - require.Equal(t, v, 1) - require.Equal(t, ok, true) - v, ok = regionReplica[1010] - require.Equal(t, v, 1) - require.Equal(t, ok, true) - - regionReplica2 := make(map[int64]int) - var sb strings.Builder - for i := 1000; i < 3000; i++ { - sb.WriteString(fmt.Sprintf("%v ", i)) - } - s := fmt.Sprintf("2000\n%v\n", sb.String()) - require.NoError(t, helper.ComputeTiFlashStatus(bufio.NewReader(strings.NewReader(s)), ®ionReplica2)) - require.Equal(t, 2000, len(regionReplica2)) - for i := 1000; i < 3000; i++ { - _, ok := regionReplica2[int64(i)] - require.True(t, ok) - } -} - -// TestTableRange tests the first part of GetPDRegionStats. -func TestTableRange(t *testing.T) { - startKey := tablecodec.GenTableRecordPrefix(1) - endKey := startKey.PrefixNext() - // t+id+_r - require.Equal(t, "7480000000000000015f72", startKey.String()) - // t+id+_s - require.Equal(t, "7480000000000000015f73", endKey.String()) - - startKey = tablecodec.EncodeTablePrefix(1) - endKey = startKey.PrefixNext() - // t+id - require.Equal(t, "748000000000000001", startKey.String()) - // t+(id+1) - require.Equal(t, "748000000000000002", endKey.String()) -} diff --git a/store/helper/main_test.go b/store/helper/main_test.go deleted file mode 100644 index d8521eeea02a7..0000000000000 --- a/store/helper/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package helper - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/main_test.go b/store/main_test.go deleted file mode 100644 index db9fa6e12c004..0000000000000 --- a/store/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package store - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/mockstore/BUILD.bazel b/store/mockstore/BUILD.bazel deleted file mode 100644 index f01bf10a18186..0000000000000 --- a/store/mockstore/BUILD.bazel +++ /dev/null @@ -1,56 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mockstore", - srcs = [ - "mockstore.go", - "redirector.go", - "tikv.go", - "unistore.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//kv", - "//store/mockstore/mockcopr", - "//store/mockstore/mockstorage", - "//store/mockstore/unistore", - "//testkit/testenv", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//util", - "@com_github_tikv_pd_client//:client", - ], -) - -go_test( - name = "mockstore_test", - timeout = "short", - srcs = [ - "cluster_test.go", - "main_test.go", - "tikv_test.go", - ], - embed = [":mockstore"], - flaky = True, - deps = [ - "//config", - "//kv", - "//sessionctx/stmtctx", - "//tablecodec", - "//testkit/testmain", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/rowcodec", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/cluster_test.go b/store/mockstore/cluster_test.go deleted file mode 100644 index 3f6d5654921cd..0000000000000 --- a/store/mockstore/cluster_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2016-present, PingCAP, Inc. -// -// 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. - -package mockstore - -import ( - "bytes" - "context" - "math" - "strconv" - "testing" - "time" - - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/testutils" - "github.com/tikv/client-go/v2/tikv" -) - -func TestClusterSplit(t *testing.T) { - rpcClient, cluster, pdClient, err := testutils.NewMockTiKV("", nil) - require.NoError(t, err) - testutils.BootstrapWithSingleStore(cluster) - mvccStore := rpcClient.MvccStore - - store, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) - require.NoError(t, err) - defer store.Close() - - txn, err := store.Begin() - require.NoError(t, err) - - // Mock inserting many rows in a table. - tblID := int64(1) - idxID := int64(2) - colID := int64(3) - handle := int64(1) - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - for i := 0; i < 1000; i++ { - rowKey := tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(handle)) - colValue := types.NewStringDatum(strconv.Itoa(int(handle))) - // TODO: Should use session's TimeZone instead of UTC. - rd := rowcodec.Encoder{Enable: true} - rowValue, err1 := tablecodec.EncodeRow(sc, []types.Datum{colValue}, []int64{colID}, nil, nil, &rd) - require.NoError(t, err1) - txn.Set(rowKey, rowValue) - - encodedIndexValue, err1 := codec.EncodeKey(sc, nil, []types.Datum{colValue, types.NewIntDatum(handle)}...) - require.NoError(t, err1) - idxKey := tablecodec.EncodeIndexSeekKey(tblID, idxID, encodedIndexValue) - txn.Set(idxKey, []byte{'0'}) - handle++ - } - err = txn.Commit(context.Background()) - require.NoError(t, err) - - // Split Table into 10 regions. - tableStart := tablecodec.GenTableRecordPrefix(tblID) - cluster.SplitKeys(tableStart, tableStart.PrefixNext(), 10) - - // 10 table regions and first region and last region. - regions := cluster.GetAllRegions() - require.Len(t, regions, 12) - - allKeysMap := make(map[string]bool) - recordPrefix := tablecodec.GenTableRecordPrefix(tblID) - for _, region := range regions { - startKey := toRawKey(region.Meta.StartKey) - endKey := toRawKey(region.Meta.EndKey) - if !bytes.HasPrefix(startKey, recordPrefix) { - continue - } - pairs := mvccStore.Scan(startKey, endKey, math.MaxInt64, math.MaxUint64, kvrpcpb.IsolationLevel_SI, nil) - if len(pairs) > 0 { - require.Len(t, pairs, 100) - } - for _, pair := range pairs { - allKeysMap[string(pair.Key)] = true - } - } - require.Len(t, allKeysMap, 1000) - - indexStart := tablecodec.EncodeTableIndexPrefix(tblID, idxID) - cluster.SplitKeys(indexStart, indexStart.PrefixNext(), 10) - - allIndexMap := make(map[string]bool) - indexPrefix := tablecodec.EncodeTableIndexPrefix(tblID, idxID) - regions = cluster.GetAllRegions() - for _, region := range regions { - startKey := toRawKey(region.Meta.StartKey) - endKey := toRawKey(region.Meta.EndKey) - if !bytes.HasPrefix(startKey, indexPrefix) { - continue - } - pairs := mvccStore.Scan(startKey, endKey, math.MaxInt64, math.MaxUint64, kvrpcpb.IsolationLevel_SI, nil) - if len(pairs) > 0 { - require.Len(t, pairs, 100) - } - for _, pair := range pairs { - allIndexMap[string(pair.Key)] = true - } - } - require.Len(t, allIndexMap, 1000) -} - -func toRawKey(k []byte) []byte { - if len(k) == 0 { - return nil - } - _, k, err := codec.DecodeBytes(k, nil) - if err != nil { - panic(err) - } - return k -} diff --git a/store/mockstore/main_test.go b/store/mockstore/main_test.go deleted file mode 100644 index d9e651ee3ec0e..0000000000000 --- a/store/mockstore/main_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mockstore - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - callback := func(i int) int { - // wait for leveldb to close, leveldb will be closed in one second - time.Sleep(time.Second) - return i - } - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/store/mockstore/mockcopr/BUILD.bazel b/store/mockstore/mockcopr/BUILD.bazel deleted file mode 100644 index 86ed50c32a984..0000000000000 --- a/store/mockstore/mockcopr/BUILD.bazel +++ /dev/null @@ -1,76 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mockcopr", - srcs = [ - "aggregate.go", - "analyze.go", - "checksum.go", - "cop_handler_dag.go", - "copr_handler.go", - "executor.go", - "rpc_copr.go", - "topn.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/mockcopr", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//expression/aggregation", - "//kv", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//statistics", - "//tablecodec", - "//types", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/rowcodec", - "//util/timeutil", - "@com_github_golang_protobuf//proto", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//metadata", - ], -) - -go_test( - name = "mockcopr_test", - timeout = "short", - srcs = [ - "executor_test.go", - "main_test.go", - ], - embed = [":mockcopr"], - flaky = True, - deps = [ - "//domain", - "//kv", - "//parser/model", - "//session", - "//store/mockstore/mockstorage", - "//tablecodec", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/mockcopr/analyze.go b/store/mockstore/mockcopr/analyze.go deleted file mode 100644 index 5570249a06a1d..0000000000000 --- a/store/mockstore/mockcopr/analyze.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package mockcopr - -import ( - "context" - - "github.com/golang/protobuf/proto" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/timeutil" - "github.com/pingcap/tipb/go-tipb" -) - -func (h coprHandler) handleCopAnalyzeRequest(req *coprocessor.Request) *coprocessor.Response { - resp := &coprocessor.Response{} - if len(req.Ranges) == 0 { - return resp - } - if req.GetTp() != kv.ReqTypeAnalyze { - return resp - } - if err := h.CheckRequestContext(req.GetContext()); err != nil { - resp.RegionError = err - return resp - } - analyzeReq := new(tipb.AnalyzeReq) - err := proto.Unmarshal(req.Data, analyzeReq) - if err != nil { - resp.OtherError = err.Error() - return resp - } - if analyzeReq.Tp == tipb.AnalyzeType_TypeIndex { - resp, err = h.handleAnalyzeIndexReq(req, analyzeReq) - } else { - resp, err = h.handleAnalyzeColumnsReq(req, analyzeReq) - } - if err != nil { - resp.OtherError = err.Error() - } - return resp -} - -func (h coprHandler) handleAnalyzeIndexReq(req *coprocessor.Request, analyzeReq *tipb.AnalyzeReq) (*coprocessor.Response, error) { - ranges, err := h.extractKVRanges(req.Ranges, false) - if err != nil { - return nil, errors.Trace(err) - } - startTS := req.StartTs - if startTS == 0 { - startTS = analyzeReq.GetStartTsFallback() - } - e := &indexScanExec{ - colsLen: int(analyzeReq.IdxReq.NumColumns), - kvRanges: ranges, - startTS: startTS, - isolationLevel: h.GetIsolationLevel(), - mvccStore: h.GetMVCCStore(), - IndexScan: &tipb.IndexScan{Desc: false}, - execDetail: new(execDetail), - hdStatus: tablecodec.HandleNotNeeded, - } - statsBuilder := statistics.NewSortedBuilder(flagsToStatementContext(analyzeReq.Flags), analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), statistics.Version1) - var cms *statistics.CMSketch - if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { - cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) - } - ctx := context.TODO() - var values [][]byte - for { - values, err = e.Next(ctx) - if err != nil { - return nil, errors.Trace(err) - } - if values == nil { - break - } - var value []byte - for _, val := range values { - value = append(value, val...) - if cms != nil { - cms.InsertBytes(value) - } - } - err = statsBuilder.Iterate(types.NewBytesDatum(value)) - if err != nil { - return nil, errors.Trace(err) - } - } - hg := statistics.HistogramToProto(statsBuilder.Hist()) - var cm *tipb.CMSketch - if cms != nil { - cm = statistics.CMSketchToProto(cms, nil) - } - data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm}) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -type analyzeColumnsExec struct { - tblExec *tableScanExec - fields []*ast.ResultField -} - -func (h coprHandler) handleAnalyzeColumnsReq(req *coprocessor.Request, analyzeReq *tipb.AnalyzeReq) (_ *coprocessor.Response, err error) { - sc := flagsToStatementContext(analyzeReq.Flags) - tz, err := timeutil.ConstructTimeZone("", int(analyzeReq.TimeZoneOffset)) - if err != nil { - return nil, errors.Trace(err) - } - sc.SetTimeZone(tz) - - evalCtx := &evalContext{sc: sc} - columns := analyzeReq.ColReq.ColumnsInfo - evalCtx.setColumnInfo(columns) - ranges, err := h.extractKVRanges(req.Ranges, false) - if err != nil { - return nil, errors.Trace(err) - } - startTS := req.StartTs - if startTS == 0 { - startTS = analyzeReq.GetStartTsFallback() - } - colInfos := make([]rowcodec.ColInfo, len(columns)) - for i := range columns { - col := columns[i] - colInfos[i] = rowcodec.ColInfo{ - ID: col.ColumnId, - Ft: evalCtx.fieldTps[i], - IsPKHandle: col.GetPkHandle(), - } - } - defVal := func(i int) ([]byte, error) { - col := columns[i] - if col.DefaultVal == nil { - return nil, nil - } - // col.DefaultVal always be varint `[flag]+[value]`. - if len(col.DefaultVal) < 1 { - panic("invalid default value") - } - return col.DefaultVal, nil - } - rd := rowcodec.NewByteDecoder(colInfos, []int64{-1}, defVal, nil) - e := &analyzeColumnsExec{ - tblExec: &tableScanExec{ - TableScan: &tipb.TableScan{Columns: columns}, - kvRanges: ranges, - colIDs: evalCtx.colIDs, - startTS: startTS, - isolationLevel: h.GetIsolationLevel(), - mvccStore: h.GetMVCCStore(), - execDetail: new(execDetail), - rd: rd, - }, - } - e.fields = make([]*ast.ResultField, len(columns)) - for i := range e.fields { - rf := new(ast.ResultField) - rf.Column = new(model.ColumnInfo) - ft := types.FieldType{} - ft.SetType(mysql.TypeBlob) - ft.SetFlen(mysql.MaxBlobWidth) - ft.SetCharset(mysql.DefaultCharset) - ft.SetCollate(mysql.DefaultCollationName) - rf.Column.FieldType = ft - e.fields[i] = rf - } - - pkID := int64(-1) - numCols := len(columns) - if columns[0].GetPkHandle() { - pkID = columns[0].ColumnId - columns = columns[1:] - numCols-- - } - collators := make([]collate.Collator, numCols) - fts := make([]*types.FieldType, numCols) - for i, col := range columns { - ft := fieldTypeFromPBColumn(col) - fts[i] = ft - if ft.EvalType() == types.ETString { - collators[i] = collate.GetCollator(ft.GetCollate()) - } - } - colReq := analyzeReq.ColReq - builder := statistics.SampleBuilder{ - Sc: sc, - RecordSet: e, - ColLen: numCols, - MaxBucketSize: colReq.BucketSize, - MaxFMSketchSize: colReq.SketchSize, - MaxSampleSize: colReq.SampleSize, - Collators: collators, - ColsFieldType: fts, - } - if pkID != -1 { - builder.PkBuilder = statistics.NewSortedBuilder(sc, builder.MaxBucketSize, pkID, types.NewFieldType(mysql.TypeBlob), statistics.Version1) - } - if colReq.CmsketchWidth != nil && colReq.CmsketchDepth != nil { - builder.CMSketchWidth = *colReq.CmsketchWidth - builder.CMSketchDepth = *colReq.CmsketchDepth - } - collectors, pkBuilder, err := builder.CollectColumnStats() - if err != nil { - return nil, errors.Trace(err) - } - colResp := &tipb.AnalyzeColumnsResp{} - if pkID != -1 { - colResp.PkHist = statistics.HistogramToProto(pkBuilder.Hist()) - } - for _, c := range collectors { - colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) - } - data, err := proto.Marshal(colResp) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -// Fields implements the sqlexec.RecordSet Fields interface. -func (e *analyzeColumnsExec) Fields() []*ast.ResultField { - return e.fields -} - -func (e *analyzeColumnsExec) getNext(ctx context.Context) ([]types.Datum, error) { - values, err := e.tblExec.Next(ctx) - if err != nil { - return nil, errors.Trace(err) - } - if values == nil { - return nil, nil - } - datumRow := make([]types.Datum, 0, len(values)) - for _, val := range values { - d := types.NewBytesDatum(val) - if len(val) == 1 && val[0] == codec.NilFlag { - d.SetNull() - } - datumRow = append(datumRow, d) - } - return datumRow, nil -} - -func (e *analyzeColumnsExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - row, err := e.getNext(ctx) - if row == nil || err != nil { - return errors.Trace(err) - } - for i := 0; i < len(row); i++ { - req.AppendDatum(i, &row[i]) - } - return nil -} - -func (e *analyzeColumnsExec) NewChunk(_ chunk.Allocator) *chunk.Chunk { - fields := make([]*types.FieldType, 0, len(e.fields)) - for _, field := range e.fields { - fields = append(fields, &field.Column.FieldType) - } - return chunk.NewChunkWithCapacity(fields, 1) -} - -// Close implements the sqlexec.RecordSet Close interface. -func (e *analyzeColumnsExec) Close() error { - return nil -} diff --git a/store/mockstore/mockcopr/executor.go b/store/mockstore/mockcopr/executor.go deleted file mode 100644 index 57f24cbc1112c..0000000000000 --- a/store/mockstore/mockcopr/executor.go +++ /dev/null @@ -1,675 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package mockcopr - -import ( - "bytes" - "context" - "sort" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tipb/go-tipb" - "github.com/tikv/client-go/v2/testutils" -) - -var ( - _ executor = &tableScanExec{} - _ executor = &indexScanExec{} - _ executor = &selectionExec{} - _ executor = &limitExec{} - _ executor = &topNExec{} -) - -type execDetail struct { - timeProcessed time.Duration - numProducedRows int - numIterations int -} - -func (e *execDetail) update(begin time.Time, row [][]byte) { - e.timeProcessed += time.Since(begin) - e.numIterations++ - if row != nil { - e.numProducedRows++ - } -} - -type executor interface { - SetSrcExec(executor) - GetSrcExec() executor - ResetCounts() - Counts() []int64 - Next(ctx context.Context) ([][]byte, error) - // ExecDetails returns its and its children's execution details. - // The order is same as DAGRequest.Executors, which children are in front of parents. - ExecDetails() []*execDetail -} - -type tableScanExec struct { - *tipb.TableScan - colIDs map[int64]int - kvRanges []kv.KeyRange - startTS uint64 - isolationLevel kvrpcpb.IsolationLevel - resolvedLocks []uint64 - mvccStore testutils.MVCCStore - cursor int - seekKey []byte - start int - counts []int64 - execDetail *execDetail - rd *rowcodec.BytesDecoder - - src executor -} - -func (e *tableScanExec) ExecDetails() []*execDetail { - var suffix []*execDetail - if e.src != nil { - suffix = e.src.ExecDetails() - } - return append(suffix, e.execDetail) -} - -func (e *tableScanExec) SetSrcExec(exec executor) { - e.src = exec -} - -func (e *tableScanExec) GetSrcExec() executor { - return e.src -} - -func (e *tableScanExec) ResetCounts() { - if e.counts != nil { - e.start = e.cursor - e.counts[e.start] = 0 - } -} - -func (e *tableScanExec) Counts() []int64 { - if e.counts == nil { - return nil - } - if e.seekKey == nil { - return e.counts[e.start:e.cursor] - } - return e.counts[e.start : e.cursor+1] -} - -func (e *tableScanExec) Next(ctx context.Context) (value [][]byte, err error) { - defer func(begin time.Time) { - e.execDetail.update(begin, value) - }(time.Now()) - for e.cursor < len(e.kvRanges) { - ran := e.kvRanges[e.cursor] - if ran.IsPoint() { - value, err = e.getRowFromPoint(ran) - if err != nil { - return nil, errors.Trace(err) - } - e.cursor++ - if value == nil { - continue - } - if e.counts != nil { - e.counts[e.cursor-1]++ - } - return value, nil - } - value, err = e.getRowFromRange(ran) - if err != nil { - return nil, errors.Trace(err) - } - if value == nil { - e.seekKey = nil - e.cursor++ - continue - } - if e.counts != nil { - e.counts[e.cursor]++ - } - return value, nil - } - - return nil, nil -} - -func (e *tableScanExec) getRowFromPoint(ran kv.KeyRange) ([][]byte, error) { - val, err := e.mvccStore.Get(ran.StartKey, e.startTS, e.isolationLevel, e.resolvedLocks) - if err != nil { - return nil, errors.Trace(err) - } - if len(val) == 0 { - return nil, nil - } - handle, err := tablecodec.DecodeRowKey(ran.StartKey) - if err != nil { - return nil, errors.Trace(err) - } - row, err := getRowData(e.Columns, e.colIDs, handle.IntValue(), val, e.rd) - if err != nil { - return nil, errors.Trace(err) - } - return row, nil -} - -func (e *tableScanExec) getRowFromRange(ran kv.KeyRange) ([][]byte, error) { - if e.seekKey == nil { - if e.Desc { - e.seekKey = ran.EndKey - } else { - e.seekKey = ran.StartKey - } - } - var pairs []testutils.MVCCPair - var pair testutils.MVCCPair - if e.Desc { - pairs = e.mvccStore.ReverseScan(ran.StartKey, e.seekKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) - } else { - pairs = e.mvccStore.Scan(e.seekKey, ran.EndKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) - } - if len(pairs) > 0 { - pair = pairs[0] - } - if pair.Err != nil { - // TODO: Handle lock error. - return nil, errors.Trace(pair.Err) - } - if pair.Key == nil { - return nil, nil - } - if e.Desc { - if bytes.Compare(pair.Key, ran.StartKey) < 0 { - return nil, nil - } - e.seekKey = tablecodec.TruncateToRowKeyLen(pair.Key) - } else { - if bytes.Compare(pair.Key, ran.EndKey) >= 0 { - return nil, nil - } - e.seekKey = kv.Key(pair.Key).PrefixNext() - } - - handle, err := tablecodec.DecodeRowKey(pair.Key) - if err != nil { - return nil, errors.Trace(err) - } - row, err := getRowData(e.Columns, e.colIDs, handle.IntValue(), pair.Value, e.rd) - if err != nil { - return nil, errors.Trace(err) - } - return row, nil -} - -type indexScanExec struct { - *tipb.IndexScan - colsLen int - kvRanges []kv.KeyRange - startTS uint64 - isolationLevel kvrpcpb.IsolationLevel - resolvedLocks []uint64 - mvccStore testutils.MVCCStore - cursor int - seekKey []byte - hdStatus tablecodec.HandleStatus - start int - counts []int64 - execDetail *execDetail - colInfos []rowcodec.ColInfo - - src executor -} - -func (e *indexScanExec) ExecDetails() []*execDetail { - var suffix []*execDetail - if e.src != nil { - suffix = e.src.ExecDetails() - } - return append(suffix, e.execDetail) -} - -func (e *indexScanExec) SetSrcExec(exec executor) { - e.src = exec -} - -func (e *indexScanExec) GetSrcExec() executor { - return e.src -} - -func (e *indexScanExec) ResetCounts() { - if e.counts != nil { - e.start = e.cursor - e.counts[e.start] = 0 - } -} - -func (e *indexScanExec) Counts() []int64 { - if e.counts == nil { - return nil - } - if e.seekKey == nil { - return e.counts[e.start:e.cursor] - } - return e.counts[e.start : e.cursor+1] -} - -func (e *indexScanExec) isUnique() bool { - return e.Unique != nil && *e.Unique -} - -func (e *indexScanExec) Next(ctx context.Context) (value [][]byte, err error) { - defer func(begin time.Time) { - e.execDetail.update(begin, value) - }(time.Now()) - for e.cursor < len(e.kvRanges) { - ran := e.kvRanges[e.cursor] - if ran.IsPoint() && e.isUnique() { - value, err = e.getRowFromPoint(ran) - if err != nil { - return nil, errors.Trace(err) - } - e.cursor++ - if value == nil { - continue - } - if e.counts != nil { - e.counts[e.cursor-1]++ - } - } else { - value, err = e.getRowFromRange(ran) - if err != nil { - return nil, errors.Trace(err) - } - if value == nil { - e.cursor++ - e.seekKey = nil - continue - } - if e.counts != nil { - e.counts[e.cursor]++ - } - } - return value, nil - } - - return nil, nil -} - -// getRowFromPoint is only used for unique key. -func (e *indexScanExec) getRowFromPoint(ran kv.KeyRange) ([][]byte, error) { - val, err := e.mvccStore.Get(ran.StartKey, e.startTS, e.isolationLevel, e.resolvedLocks) - if err != nil { - return nil, errors.Trace(err) - } - if len(val) == 0 { - return nil, nil - } - return tablecodec.DecodeIndexKV(ran.StartKey, val, e.colsLen, e.hdStatus, e.colInfos) -} - -func (e *indexScanExec) getRowFromRange(ran kv.KeyRange) ([][]byte, error) { - if e.seekKey == nil { - if e.Desc { - e.seekKey = ran.EndKey - } else { - e.seekKey = ran.StartKey - } - } - var pairs []testutils.MVCCPair - var pair testutils.MVCCPair - if e.Desc { - pairs = e.mvccStore.ReverseScan(ran.StartKey, e.seekKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) - } else { - pairs = e.mvccStore.Scan(e.seekKey, ran.EndKey, 1, e.startTS, e.isolationLevel, e.resolvedLocks) - } - if len(pairs) > 0 { - pair = pairs[0] - } - if pair.Err != nil { - // TODO: Handle lock error. - return nil, errors.Trace(pair.Err) - } - if pair.Key == nil { - return nil, nil - } - if e.Desc { - if bytes.Compare(pair.Key, ran.StartKey) < 0 { - return nil, nil - } - e.seekKey = pair.Key - } else { - if bytes.Compare(pair.Key, ran.EndKey) >= 0 { - return nil, nil - } - e.seekKey = kv.Key(pair.Key).PrefixNext() - } - return tablecodec.DecodeIndexKV(pair.Key, pair.Value, e.colsLen, e.hdStatus, e.colInfos) -} - -type selectionExec struct { - conditions []expression.Expression - relatedColOffsets []int - row []types.Datum - evalCtx *evalContext - src executor - execDetail *execDetail -} - -func (e *selectionExec) ExecDetails() []*execDetail { - var suffix []*execDetail - if e.src != nil { - suffix = e.src.ExecDetails() - } - return append(suffix, e.execDetail) -} - -func (e *selectionExec) SetSrcExec(exec executor) { - e.src = exec -} - -func (e *selectionExec) GetSrcExec() executor { - return e.src -} - -func (e *selectionExec) ResetCounts() { - e.src.ResetCounts() -} - -func (e *selectionExec) Counts() []int64 { - return e.src.Counts() -} - -// evalBool evaluates expression to a boolean value. -func evalBool(exprs []expression.Expression, row []types.Datum, ctx *stmtctx.StatementContext) (bool, error) { - for _, expr := range exprs { - data, err := expr.Eval(chunk.MutRowFromDatums(row).ToRow()) - if err != nil { - return false, errors.Trace(err) - } - if data.IsNull() { - return false, nil - } - - isBool, err := data.ToBool(ctx) - isBool, err = expression.HandleOverflowOnSelection(ctx, isBool, err) - if err != nil { - return false, errors.Trace(err) - } - if isBool == 0 { - return false, nil - } - } - return true, nil -} - -func (e *selectionExec) Next(ctx context.Context) (value [][]byte, err error) { - defer func(begin time.Time) { - e.execDetail.update(begin, value) - }(time.Now()) - for { - value, err = e.src.Next(ctx) - if err != nil { - return nil, errors.Trace(err) - } - if value == nil { - return nil, nil - } - - err = e.evalCtx.decodeRelatedColumnVals(e.relatedColOffsets, value, e.row) - if err != nil { - return nil, errors.Trace(err) - } - match, err := evalBool(e.conditions, e.row, e.evalCtx.sc) - if err != nil { - return nil, errors.Trace(err) - } - if match { - return value, nil - } - } -} - -type topNExec struct { - heap *topNHeap - evalCtx *evalContext - relatedColOffsets []int - orderByExprs []expression.Expression - row []types.Datum - cursor int - executed bool - execDetail *execDetail - - src executor -} - -func (e *topNExec) ExecDetails() []*execDetail { - var suffix []*execDetail - if e.src != nil { - suffix = e.src.ExecDetails() - } - return append(suffix, e.execDetail) -} - -func (e *topNExec) SetSrcExec(src executor) { - e.src = src -} - -func (e *topNExec) GetSrcExec() executor { - return e.src -} - -func (e *topNExec) ResetCounts() { - e.src.ResetCounts() -} - -func (e *topNExec) Counts() []int64 { - return e.src.Counts() -} - -func (e *topNExec) innerNext(ctx context.Context) (bool, error) { - value, err := e.src.Next(ctx) - if err != nil { - return false, errors.Trace(err) - } - if value == nil { - return false, nil - } - err = e.evalTopN(value) - if err != nil { - return false, errors.Trace(err) - } - return true, nil -} - -func (e *topNExec) Next(ctx context.Context) (value [][]byte, err error) { - defer func(begin time.Time) { - e.execDetail.update(begin, value) - }(time.Now()) - if !e.executed { - for { - hasMore, err := e.innerNext(ctx) - if err != nil { - return nil, errors.Trace(err) - } - if !hasMore { - sort.Sort(&e.heap.topNSorter) - break - } - } - e.executed = true - } - if e.cursor >= len(e.heap.rows) { - return nil, nil - } - row := e.heap.rows[e.cursor] - e.cursor++ - - return row.data, nil -} - -// evalTopN evaluates the top n elements from the data. The input receives a record including its handle and data. -// And this function will check if this record can replace one of the old records. -func (e *topNExec) evalTopN(value [][]byte) error { - newRow := &sortRow{ - key: make([]types.Datum, len(e.orderByExprs)), - } - err := e.evalCtx.decodeRelatedColumnVals(e.relatedColOffsets, value, e.row) - if err != nil { - return errors.Trace(err) - } - for i, expr := range e.orderByExprs { - newRow.key[i], err = expr.Eval(chunk.MutRowFromDatums(e.row).ToRow()) - if err != nil { - return errors.Trace(err) - } - } - - if e.heap.tryToAddRow(newRow) { - newRow.data = append(newRow.data, value...) - } - return errors.Trace(e.heap.err) -} - -type limitExec struct { - limit uint64 - cursor uint64 - - src executor - - execDetail *execDetail -} - -func (e *limitExec) ExecDetails() []*execDetail { - var suffix []*execDetail - if e.src != nil { - suffix = e.src.ExecDetails() - } - return append(suffix, e.execDetail) -} - -func (e *limitExec) SetSrcExec(src executor) { - e.src = src -} - -func (e *limitExec) GetSrcExec() executor { - return e.src -} - -func (e *limitExec) ResetCounts() { - e.src.ResetCounts() -} - -func (e *limitExec) Counts() []int64 { - return e.src.Counts() -} - -func (e *limitExec) Next(ctx context.Context) (value [][]byte, err error) { - defer func(begin time.Time) { - e.execDetail.update(begin, value) - }(time.Now()) - if e.cursor >= e.limit { - return nil, nil - } - - value, err = e.src.Next(ctx) - if err != nil { - return nil, errors.Trace(err) - } - if value == nil { - return nil, nil - } - e.cursor++ - return value, nil -} - -func hasColVal(data [][]byte, colIDs map[int64]int, id int64) bool { - offset, ok := colIDs[id] - if ok && data[offset] != nil { - return true - } - return false -} - -// getRowData decodes raw byte slice to row data. -func getRowData(columns []*tipb.ColumnInfo, colIDs map[int64]int, handle int64, value []byte, rd *rowcodec.BytesDecoder) ([][]byte, error) { - if rowcodec.IsNewFormat(value) { - return rd.DecodeToBytes(colIDs, kv.IntHandle(handle), value, nil) - } - values, err := tablecodec.CutRowNew(value, colIDs) - if err != nil { - return nil, errors.Trace(err) - } - if values == nil { - values = make([][]byte, len(colIDs)) - } - // Fill the handle and null columns. - for _, col := range columns { - id := col.GetColumnId() - offset := colIDs[id] - if col.GetPkHandle() || id == model.ExtraHandleID { - var handleDatum types.Datum - if mysql.HasUnsignedFlag(uint(col.GetFlag())) { - // PK column is Unsigned. - handleDatum = types.NewUintDatum(uint64(handle)) - } else { - handleDatum = types.NewIntDatum(handle) - } - handleData, err1 := codec.EncodeValue(nil, nil, handleDatum) - if err1 != nil { - return nil, errors.Trace(err1) - } - values[offset] = handleData - continue - } - if hasColVal(values, colIDs, id) { - continue - } - if len(col.DefaultVal) > 0 { - values[offset] = col.DefaultVal - continue - } - if mysql.HasNotNullFlag(uint(col.GetFlag())) { - return nil, errors.Errorf("Miss column %d", id) - } - - values[offset] = []byte{codec.NilFlag} - } - - return values, nil -} - -func convertToExprs(sc *stmtctx.StatementContext, fieldTps []*types.FieldType, pbExprs []*tipb.Expr) ([]expression.Expression, error) { - exprs := make([]expression.Expression, 0, len(pbExprs)) - for _, expr := range pbExprs { - e, err := expression.PBToExpr(expr, fieldTps, sc) - if err != nil { - return nil, errors.Trace(err) - } - exprs = append(exprs, e) - } - return exprs, nil -} diff --git a/store/mockstore/mockcopr/executor_test.go b/store/mockstore/mockcopr/executor_test.go deleted file mode 100644 index c939518b32314..0000000000000 --- a/store/mockstore/mockcopr/executor_test.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2019-present, PingCAP, Inc. -// -// 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. - -package mockcopr_test - -import ( - "context" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore/mockcopr" - "github.com/pingcap/tidb/store/mockstore/mockstorage" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/testutils" - "github.com/tikv/client-go/v2/tikv" -) - -// This test checks the resolve lock functionality. When a txn meets the lock of a large transaction, -// it should not block by the lock. -func TestResolvedLargeTxnLocks(t *testing.T) { - // This is required since mock tikv does not support paging. - failpoint.Enable("github.com/pingcap/tidb/store/copr/DisablePaging", `return`) - defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/copr/DisablePaging")) - }() - - rpcClient, cluster, pdClient, err := testutils.NewMockTiKV("", mockcopr.NewCoprRPCHandler()) - require.NoError(t, err) - - testutils.BootstrapWithSingleStore(cluster) - mvccStore := rpcClient.MvccStore - tikvStore, err := tikv.NewTestTiKVStore(rpcClient, pdClient, nil, nil, 0) - require.NoError(t, err) - - store, err := mockstorage.NewMockStorage(tikvStore) - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - session.SetSchemaLease(0) - session.DisableStats4Test() - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - defer dom.Close() - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t (id int primary key, val int)") - dom = domain.GetDomain(tk.Session()) - schema := dom.InfoSchema() - tbl, err := schema.TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - tk.MustExec("insert into t values (1, 1)") - - o := store.GetOracle() - tso, err := o.GetTimestamp(context.Background(), &oracle.Option{TxnScope: kv.GlobalTxnScope}) - require.NoError(t, err) - - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - pairs := mvccStore.Scan(key, nil, 1, tso, kvrpcpb.IsolationLevel_SI, nil) - require.Len(t, pairs, 1) - require.Nil(t, pairs[0].Err) - - // Simulate a large txn (holding a pk lock with large TTL). - // Secondary lock 200ms, primary lock 100s - require.True(t, prewriteMVCCStore(mvccStore, putMutations("primary", "value"), "primary", tso, 100000)) - require.True(t, prewriteMVCCStore(mvccStore, putMutations(string(key), "value"), "primary", tso, 200)) - - // Simulate the action of reading meet the lock of a large txn. - // The lock of the large transaction should not block read. - // The first time, this query should meet a lock on the secondary key, then resolve lock. - // After that, the query should read the previous version data. - tk.MustQuery("select * from t").Check(testkit.Rows("1 1")) - - // Cover BatchGet. - tk.MustQuery("select * from t where id in (1)").Check(testkit.Rows("1 1")) - - // Cover PointGet. - tk.MustExec("begin") - tk.MustQuery("select * from t where id = 1").Check(testkit.Rows("1 1")) - tk.MustExec("rollback") - - // And check the large txn is still alive. - pairs = mvccStore.Scan([]byte("primary"), nil, 1, tso, kvrpcpb.IsolationLevel_SI, nil) - require.Len(t, pairs, 1) - _, ok := errors.Cause(pairs[0].Err).(*testutils.ErrLocked) - require.True(t, ok) -} - -func putMutations(kvpairs ...string) []*kvrpcpb.Mutation { - var mutations []*kvrpcpb.Mutation - for i := 0; i < len(kvpairs); i += 2 { - mutations = append(mutations, &kvrpcpb.Mutation{ - Op: kvrpcpb.Op_Put, - Key: []byte(kvpairs[i]), - Value: []byte(kvpairs[i+1]), - }) - } - return mutations -} - -func prewriteMVCCStore(store testutils.MVCCStore, mutations []*kvrpcpb.Mutation, primary string, startTS uint64, ttl uint64) bool { - req := &kvrpcpb.PrewriteRequest{ - Mutations: mutations, - PrimaryLock: []byte(primary), - StartVersion: startTS, - LockTtl: ttl, - MinCommitTs: startTS + 1, - } - errs := store.Prewrite(req) - for _, err := range errs { - if err != nil { - return false - } - } - return true -} diff --git a/store/mockstore/mockcopr/main_test.go b/store/mockstore/mockcopr/main_test.go deleted file mode 100644 index d2649eacec8b4..0000000000000 --- a/store/mockstore/mockcopr/main_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mockcopr - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - callback := func(i int) int { - // wait for leveldb to close, leveldb will be closed in one second - time.Sleep(time.Second) - return i - } - goleak.VerifyTestMain(testmain.WrapTestingM(m, callback), opts...) -} diff --git a/store/mockstore/mockcopr/topn.go b/store/mockstore/mockcopr/topn.go deleted file mode 100644 index bf6833df4f48f..0000000000000 --- a/store/mockstore/mockcopr/topn.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package mockcopr - -import ( - "container/heap" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tipb/go-tipb" -) - -type sortRow struct { - key []types.Datum - data [][]byte -} - -// topNSorter implements sort.Interface. When all rows have been processed, the topNSorter will sort the whole data in heap. -type topNSorter struct { - orderByItems []*tipb.ByItem - rows []*sortRow - err error - sc *stmtctx.StatementContext -} - -func (t *topNSorter) Len() int { - return len(t.rows) -} - -func (t *topNSorter) Swap(i, j int) { - t.rows[i], t.rows[j] = t.rows[j], t.rows[i] -} - -func (t *topNSorter) Less(i, j int) bool { - for index, by := range t.orderByItems { - v1 := t.rows[i].key[index] - v2 := t.rows[j].key[index] - - ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) - if err != nil { - t.err = errors.Trace(err) - return true - } - - if by.Desc { - ret = -ret - } - - if ret < 0 { - return true - } else if ret > 0 { - return false - } - } - - return false -} - -// topNHeap holds the top n elements using heap structure. It implements heap.Interface. -// When we insert a row, topNHeap will check if the row can become one of the top n element or not. -type topNHeap struct { - topNSorter - - // totalCount is equal to the limit count, which means the max size of heap. - totalCount int - // heapSize means the current size of this heap. - heapSize int -} - -func (t *topNHeap) Len() int { - return t.heapSize -} - -func (t *topNHeap) Push(x interface{}) { - t.rows = append(t.rows, x.(*sortRow)) - t.heapSize++ -} - -func (t *topNHeap) Pop() interface{} { - return nil -} - -func (t *topNHeap) Less(i, j int) bool { - for index, by := range t.orderByItems { - v1 := t.rows[i].key[index] - v2 := t.rows[j].key[index] - - ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) - if err != nil { - t.err = errors.Trace(err) - return true - } - - if by.Desc { - ret = -ret - } - - if ret > 0 { - return true - } else if ret < 0 { - return false - } - } - - return false -} - -// tryToAddRow tries to add a row to heap. -// When this row is not less than any rows in heap, it will never become the top n element. -// Then this function returns false. -func (t *topNHeap) tryToAddRow(row *sortRow) bool { - success := false - if t.heapSize == t.totalCount { - t.rows = append(t.rows, row) - // When this row is less than the top element, it will replace it and adjust the heap structure. - if t.Less(0, t.heapSize) { - t.Swap(0, t.heapSize) - heap.Fix(t, 0) - success = true - } - t.rows = t.rows[:t.heapSize] - } else { - heap.Push(t, row) - success = true - } - return success -} diff --git a/store/mockstore/mockstorage/BUILD.bazel b/store/mockstore/mockstorage/BUILD.bazel deleted file mode 100644 index 316301bb73242..0000000000000 --- a/store/mockstore/mockstorage/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mockstorage", - srcs = ["storage.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/mockstorage", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//store/copr", - "//store/driver/txn", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_client_go_v2//tikv", - ], -) diff --git a/store/mockstore/mockstorage/storage.go b/store/mockstore/mockstorage/storage.go deleted file mode 100644 index 15453ad925ff3..0000000000000 --- a/store/mockstore/mockstorage/storage.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mockstorage - -import ( - "context" - "crypto/tls" - - deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/copr" - driver "github.com/pingcap/tidb/store/driver/txn" - "github.com/tikv/client-go/v2/config" - "github.com/tikv/client-go/v2/tikv" -) - -// Wraps tikv.KVStore and make it compatible with kv.Storage. -type mockStorage struct { - *tikv.KVStore - *copr.Store - memCache kv.MemManager - LockWaits []*deadlockpb.WaitForEntry -} - -// NewMockStorage wraps tikv.KVStore as kv.Storage. -func NewMockStorage(tikvStore *tikv.KVStore) (kv.Storage, error) { - coprConfig := config.DefaultConfig().TiKVClient.CoprCache - coprStore, err := copr.NewStore(tikvStore, &coprConfig) - if err != nil { - return nil, err - } - return &mockStorage{ - KVStore: tikvStore, - Store: coprStore, - memCache: kv.NewCacheDB(), - }, nil -} - -func (s *mockStorage) EtcdAddrs() ([]string, error) { - return nil, nil -} - -func (s *mockStorage) TLSConfig() *tls.Config { - return nil -} - -// GetMemCache return memory mamager of the storage -func (s *mockStorage) GetMemCache() kv.MemManager { - return s.memCache -} - -func (s *mockStorage) StartGCWorker() error { - return nil -} - -func (s *mockStorage) Name() string { - return "mock-storage" -} - -func (s *mockStorage) Describe() string { - return "" -} - -// Begin a global transaction. -func (s *mockStorage) Begin(opts ...tikv.TxnOption) (kv.Transaction, error) { - txn, err := s.KVStore.Begin(opts...) - return newTiKVTxn(txn, err) -} - -// ShowStatus returns the specified status of the storage -func (s *mockStorage) ShowStatus(ctx context.Context, key string) (interface{}, error) { - return nil, kv.ErrNotImplemented -} - -// GetSnapshot gets a snapshot that is able to read any data which data is <= ver. -// if ver is MaxVersion or > current max committed version, we will use current version for this snapshot. -func (s *mockStorage) GetSnapshot(ver kv.Version) kv.Snapshot { - return driver.NewSnapshot(s.KVStore.GetSnapshot(ver.Ver)) -} - -// CurrentVersion returns current max committed version with the given txnScope (local or global). -func (s *mockStorage) CurrentVersion(txnScope string) (kv.Version, error) { - ver, err := s.KVStore.CurrentTimestamp(txnScope) - return kv.NewVersion(ver), err -} - -// GetMinSafeTS return the minimal SafeTS of the storage with given txnScope. -func (s *mockStorage) GetMinSafeTS(txnScope string) uint64 { - return 0 -} - -func newTiKVTxn(txn *tikv.KVTxn, err error) (kv.Transaction, error) { - if err != nil { - return nil, err - } - return driver.NewTiKVTxn(txn), nil -} - -func (s *mockStorage) GetLockWaits() ([]*deadlockpb.WaitForEntry, error) { - return s.LockWaits, nil -} - -func (s *mockStorage) Close() error { - select { - case <-s.KVStore.Closed(): - return nil - default: - s.Store.Close() - return s.KVStore.Close() - } -} - -func (s *mockStorage) GetCodec() tikv.Codec { - pdClient := s.KVStore.GetPDClient() - pdCodecCli := tikv.NewCodecPDClient(tikv.ModeTxn, pdClient) - return pdCodecCli.GetCodec() -} - -// MockLockWaitSetter is used to set the mocked lock wait information, which helps implementing tests that uses the -// GetLockWaits function. -type MockLockWaitSetter interface { - SetMockLockWaits(lockWaits []*deadlockpb.WaitForEntry) -} - -func (s *mockStorage) SetMockLockWaits(lockWaits []*deadlockpb.WaitForEntry) { - s.LockWaits = lockWaits -} diff --git a/store/mockstore/mockstore.go b/store/mockstore/mockstore.go deleted file mode 100644 index d498ea1956c74..0000000000000 --- a/store/mockstore/mockstore.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package mockstore - -import ( - "net/url" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore/unistore" - "github.com/pingcap/tidb/testkit/testenv" - "github.com/tikv/client-go/v2/testutils" - "github.com/tikv/client-go/v2/tikv" - pd "github.com/tikv/pd/client" -) - -// MockTiKVDriver is in memory mock TiKV driver. -type MockTiKVDriver struct{} - -// Open creates a MockTiKV storage. -func (d MockTiKVDriver) Open(path string) (kv.Storage, error) { - u, err := url.Parse(path) - if err != nil { - return nil, errors.Trace(err) - } - if !strings.EqualFold(u.Scheme, "mocktikv") { - return nil, errors.Errorf("Uri scheme expected(mocktikv) but found (%s)", u.Scheme) - } - - opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(MockTiKV)} - txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches - if txnLocalLatches.Enabled { - opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) - } - - return NewMockStore(opts...) -} - -// EmbedUnistoreDriver is in embedded unistore driver. -type EmbedUnistoreDriver struct{} - -// Open creates a EmbedUnistore storage. -func (d EmbedUnistoreDriver) Open(path string) (kv.Storage, error) { - u, err := url.Parse(path) - if err != nil { - return nil, errors.Trace(err) - } - if !strings.EqualFold(u.Scheme, "unistore") { - return nil, errors.Errorf("Uri scheme expected(unistore) but found (%s)", u.Scheme) - } - - opts := []MockTiKVStoreOption{WithPath(u.Path), WithStoreType(EmbedUnistore)} - txnLocalLatches := config.GetGlobalConfig().TxnLocalLatches - if txnLocalLatches.Enabled { - opts = append(opts, WithTxnLocalLatches(txnLocalLatches.Capacity)) - } - - return NewMockStore(opts...) -} - -// StoreType is the type of backend mock storage. -type StoreType uint8 - -const ( - // MockTiKV is the mock storage based on goleveldb. - MockTiKV StoreType = iota - // EmbedUnistore is the mock storage based on unistore. - EmbedUnistore - - defaultStoreType = EmbedUnistore -) - -type mockOptions struct { - clusterInspector func(testutils.Cluster) - clientHijacker func(tikv.Client) tikv.Client - pdClientHijacker func(pd.Client) pd.Client - path string - txnLocalLatches uint - storeType StoreType - ddlCheckerHijack bool -} - -// MockTiKVStoreOption is used to control some behavior of mock tikv. -type MockTiKVStoreOption func(*mockOptions) - -// WithMultipleOptions merges multiple options into one option. -func WithMultipleOptions(opts ...MockTiKVStoreOption) MockTiKVStoreOption { - return func(args *mockOptions) { - for _, opt := range opts { - opt(args) - } - } -} - -// WithClientHijacker hijacks KV client's behavior, makes it easy to simulate the network -// problem between TiDB and TiKV. -func WithClientHijacker(hijacker func(tikv.Client) tikv.Client) MockTiKVStoreOption { - return func(c *mockOptions) { - c.clientHijacker = hijacker - } -} - -// WithPDClientHijacker hijacks PD client's behavior, makes it easy to simulate the network -// problem between TiDB and PD. -func WithPDClientHijacker(hijacker func(pd.Client) pd.Client) MockTiKVStoreOption { - return func(c *mockOptions) { - c.pdClientHijacker = hijacker - } -} - -// WithClusterInspector lets user to inspect the mock cluster handler. -func WithClusterInspector(inspector func(testutils.Cluster)) MockTiKVStoreOption { - return func(c *mockOptions) { - c.clusterInspector = inspector - } -} - -// WithStoreType lets user choose the backend storage's type. -func WithStoreType(tp StoreType) MockTiKVStoreOption { - return func(c *mockOptions) { - c.storeType = tp - } -} - -// WithPath specifies the mocktikv path. -func WithPath(path string) MockTiKVStoreOption { - return func(c *mockOptions) { - c.path = path - } -} - -// WithTxnLocalLatches enable txnLocalLatches, when capacity > 0. -func WithTxnLocalLatches(capacity uint) MockTiKVStoreOption { - return func(c *mockOptions) { - c.txnLocalLatches = capacity - } -} - -// WithDDLChecker prepare injected DDL implementation for the domain of this store. It must be done before bootstrap to -// avoid data race with dom.ddl. -func WithDDLChecker() MockTiKVStoreOption { - return func(c *mockOptions) { - c.ddlCheckerHijack = true - } -} - -// DDLCheckerInjector is used to break import cycle. -var DDLCheckerInjector func(kv.Storage) kv.Storage - -// NewMockStore creates a mocked tikv store, the path is the file path to store the data. -// If path is an empty string, a memory storage will be created. -func NewMockStore(options ...MockTiKVStoreOption) (kv.Storage, error) { - testenv.SetGOMAXPROCSForTest() - opt := mockOptions{ - clusterInspector: func(c testutils.Cluster) { - BootstrapWithSingleStore(c) - }, - storeType: defaultStoreType, - } - for _, f := range options { - f(&opt) - } - - var ( - store kv.Storage - err error - ) - - switch opt.storeType { - case MockTiKV: - store, err = newMockTikvStore(&opt) - case EmbedUnistore: - store, err = newUnistore(&opt) - default: - panic("unsupported mockstore") - } - if err != nil { - return nil, errors.Trace(err) - } - - if opt.ddlCheckerHijack { - store = DDLCheckerInjector(store) - } - return store, nil -} - -// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. -func BootstrapWithSingleStore(cluster testutils.Cluster) (storeID, peerID, regionID uint64) { - switch x := cluster.(type) { - case *testutils.MockCluster: - return testutils.BootstrapWithSingleStore(x) - case *unistore.Cluster: - return unistore.BootstrapWithSingleStore(x) - default: - panic("unsupported cluster type") - } -} - -// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. -func BootstrapWithMultiStores(cluster testutils.Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { - switch x := cluster.(type) { - case *testutils.MockCluster: - return testutils.BootstrapWithMultiStores(x, n) - case *unistore.Cluster: - return unistore.BootstrapWithMultiStores(x, n) - default: - panic("unsupported cluster type") - } -} - -// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 -// Store. The number of Regions will be len(splitKeys) + 1. -func BootstrapWithMultiRegions(cluster testutils.Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { - switch x := cluster.(type) { - case *testutils.MockCluster: - return testutils.BootstrapWithMultiRegions(x, splitKeys...) - case *unistore.Cluster: - return unistore.BootstrapWithMultiRegions(x, splitKeys...) - default: - panic("unsupported cluster type") - } -} diff --git a/store/mockstore/tikv.go b/store/mockstore/tikv.go deleted file mode 100644 index 1cac5abd32d5b..0000000000000 --- a/store/mockstore/tikv.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package mockstore - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore/mockcopr" - "github.com/pingcap/tidb/store/mockstore/mockstorage" - "github.com/tikv/client-go/v2/testutils" - "github.com/tikv/client-go/v2/tikv" -) - -// newMockTikvStore creates a mocked tikv store, the path is the file path to store the data. -// If path is an empty string, a memory storage will be created. -func newMockTikvStore(opt *mockOptions) (kv.Storage, error) { - client, cluster, pdClient, err := testutils.NewMockTiKV(opt.path, mockcopr.NewCoprRPCHandler()) - if err != nil { - return nil, errors.Trace(err) - } - opt.clusterInspector(cluster) - - kvstore, err := tikv.NewTestTiKVStore(newClientRedirector(client), pdClient, opt.clientHijacker, opt.pdClientHijacker, opt.txnLocalLatches) - if err != nil { - return nil, err - } - return mockstorage.NewMockStorage(kvstore) -} diff --git a/store/mockstore/unistore/BUILD.bazel b/store/mockstore/unistore/BUILD.bazel deleted file mode 100644 index 53120c16d32fc..0000000000000 --- a/store/mockstore/unistore/BUILD.bazel +++ /dev/null @@ -1,64 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "unistore", - srcs = [ - "cluster.go", - "mock.go", - "pd.go", - "raw_handler.go", - "rpc.go", - "testutil.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore", - visibility = ["//visibility:public"], - deps = [ - "//domain/infosync", - "//parser/terror", - "//store/mockstore/unistore/config", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/server", - "//store/mockstore/unistore/tikv", - "//tablecodec", - "//util/codec", - "//util/topsql/state", - "@com_github_golang_protobuf//proto", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/debugpb", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/keyspacepb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/meta_storagepb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_kvproto//pkg/pdpb", - "@com_github_pingcap_kvproto//pkg/resource_manager", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_pd_client//:client", - "@org_golang_google_grpc//metadata", - ], -) - -go_test( - name = "unistore_test", - timeout = "short", - srcs = [ - "main_test.go", - "pd_test.go", - "raw_handler_test.go", - ], - embed = [":unistore"], - flaky = True, - shard_count = 4, - deps = [ - "//testkit/testsetup", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/unistore/client/BUILD.bazel b/store/mockstore/unistore/client/BUILD.bazel deleted file mode 100644 index d036f5eeb2ed3..0000000000000 --- a/store/mockstore/unistore/client/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "client", - srcs = ["client.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/client", - visibility = ["//visibility:public"], - deps = ["@com_github_tikv_client_go_v2//tikvrpc"], -) diff --git a/store/mockstore/unistore/cluster.go b/store/mockstore/unistore/cluster.go deleted file mode 100644 index 38e41db832e79..0000000000000 --- a/store/mockstore/unistore/cluster.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package unistore - -import ( - "fmt" - "sync" - "time" - - "github.com/pingcap/kvproto/pkg/metapb" - us "github.com/pingcap/tidb/store/mockstore/unistore/tikv" - "github.com/pingcap/tidb/util/codec" - "github.com/tikv/client-go/v2/testutils" -) - -type delayKey struct { - startTS uint64 - regionID uint64 -} - -var _ testutils.Cluster = new(Cluster) - -// Cluster simulates a TiKV cluster. It focuses on management and the change of -// meta data. A Cluster mainly includes following 3 kinds of meta data: -// 1. Region: A Region is a fragment of TiKV's data whose range is [start, end). -// The data of a Region is duplicated to multiple Peers and distributed in -// multiple Stores. -// 2. Peer: A Peer is a replica of a Region's data. All peers of a Region form -// a group, each group elects a Leader to provide services. -// 3. Store: A Store is a storage/service node. Try to think it as a TiKV server -// process. Only the store with request's Region's leader Peer could respond -// to client's request. -type Cluster struct { - *us.MockRegionManager - - // delayEvents is used to control the execution sequence of rpc requests for test. - delayEvents map[delayKey]time.Duration - delayMu sync.Mutex -} - -func newCluster(rm *us.MockRegionManager) *Cluster { - return &Cluster{ - MockRegionManager: rm, - delayEvents: make(map[delayKey]time.Duration), - } -} - -// ScheduleDelay schedules a delay event for a transaction on a region. -func (c *Cluster) ScheduleDelay(startTS, regionID uint64, dur time.Duration) { - c.delayMu.Lock() - c.delayEvents[delayKey{startTS: startTS, regionID: regionID}] = dur - c.delayMu.Unlock() -} - -func (c *Cluster) handleDelay(startTS, regionID uint64) { - key := delayKey{startTS: startTS, regionID: regionID} - c.delayMu.Lock() - dur, ok := c.delayEvents[key] - if ok { - delete(c.delayEvents, key) - } - c.delayMu.Unlock() - if ok { - time.Sleep(dur) - } -} - -// SplitRaw splits region for raw KV. -func (c *Cluster) SplitRaw(regionID, newRegionID uint64, rawKey []byte, peerIDs []uint64, leaderPeerID uint64) *metapb.Region { - encodedKey := codec.EncodeBytes(nil, rawKey) - return c.MockRegionManager.SplitRaw(regionID, newRegionID, encodedKey, peerIDs, leaderPeerID) -} - -// SplitKeys evenly splits the start, end key into "count" regions. -func (c *Cluster) SplitKeys(start, end []byte, count int) { - c.MockRegionManager.SplitKeys(start, end, count) -} - -// BootstrapWithSingleStore initializes a Cluster with 1 Region and 1 Store. -func BootstrapWithSingleStore(cluster *Cluster) (storeID, peerID, regionID uint64) { - storeID, regionID, peerID = cluster.AllocID(), cluster.AllocID(), cluster.AllocID() - store := &metapb.Store{ - Id: storeID, - Address: fmt.Sprintf("store%d", storeID), - } - region := &metapb.Region{ - Id: regionID, - RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []*metapb.Peer{{Id: peerID, StoreId: storeID}}, - } - if err := cluster.Bootstrap([]*metapb.Store{store}, region); err != nil { - panic(err) - } - return -} - -// BootstrapWithMultiStores initializes a Cluster with 1 Region and n Stores. -func BootstrapWithMultiStores(cluster *Cluster, n int) (storeIDs, peerIDs []uint64, regionID uint64, leaderPeer uint64) { - storeIDs = cluster.AllocIDs(n) - peerIDs = cluster.AllocIDs(n) - leaderPeer = peerIDs[0] - regionID = cluster.AllocID() - stores := make([]*metapb.Store, n) - for i, storeID := range storeIDs { - stores[i] = &metapb.Store{ - Id: storeID, - Address: fmt.Sprintf("store%d", storeID), - } - } - peers := make([]*metapb.Peer, n) - for i, peerID := range peerIDs { - peers[i] = &metapb.Peer{ - Id: peerID, - StoreId: storeIDs[i], - } - } - region := &metapb.Region{ - Id: regionID, - RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: peers, - } - if err := cluster.Bootstrap(stores, region); err != nil { - panic(err) - } - return -} - -// BootstrapWithMultiRegions initializes a Cluster with multiple Regions and 1 -// Store. The number of Regions will be len(splitKeys) + 1. -func BootstrapWithMultiRegions(cluster *Cluster, splitKeys ...[]byte) (storeID uint64, regionIDs, peerIDs []uint64) { - var firstRegionID, firstPeerID uint64 - storeID, firstPeerID, firstRegionID = BootstrapWithSingleStore(cluster) - regionIDs = append([]uint64{firstRegionID}, cluster.AllocIDs(len(splitKeys))...) - peerIDs = append([]uint64{firstPeerID}, cluster.AllocIDs(len(splitKeys))...) - for i, k := range splitKeys { - cluster.Split(regionIDs[i], regionIDs[i+1], k, []uint64{peerIDs[i]}, peerIDs[i]) - } - return -} diff --git a/store/mockstore/unistore/config/BUILD.bazel b/store/mockstore/unistore/config/BUILD.bazel deleted file mode 100644 index 2f104d3fa0f9b..0000000000000 --- a/store/mockstore/unistore/config/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "config", - srcs = ["config.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/config", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_badger//options", - "@com_github_pingcap_log//:log", - ], -) diff --git a/store/mockstore/unistore/cophandler/BUILD.bazel b/store/mockstore/unistore/cophandler/BUILD.bazel deleted file mode 100644 index d4dcc21200db7..0000000000000 --- a/store/mockstore/unistore/cophandler/BUILD.bazel +++ /dev/null @@ -1,87 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cophandler", - srcs = [ - "analyze.go", - "closure_exec.go", - "cop_handler.go", - "mpp.go", - "mpp_exec.go", - "topn.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/cophandler", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//expression/aggregation", - "//kv", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/stmtctx", - "//statistics", - "//store/mockstore/unistore/client", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/tikv/dbreader", - "//store/mockstore/unistore/tikv/kverrors", - "//store/mockstore/unistore/tikv/mvcc", - "//tablecodec", - "//types", - "//util", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/mock", - "//util/rowcodec", - "//util/timeutil", - "@com_github_golang_protobuf//proto", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_twmb_murmur3//:murmur3", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "cophandler_test", - timeout = "short", - srcs = [ - "cop_handler_test.go", - "main_test.go", - ], - embed = [":cophandler"], - flaky = True, - shard_count = 4, - deps = [ - "//expression", - "//kv", - "//parser/mysql", - "//sessionctx/stmtctx", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/tikv/dbreader", - "//store/mockstore/unistore/tikv/mvcc", - "//tablecodec", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/collate", - "//util/rowcodec", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/unistore/cophandler/analyze.go b/store/mockstore/unistore/cophandler/analyze.go deleted file mode 100644 index bed3c71db83a3..0000000000000 --- a/store/mockstore/unistore/cophandler/analyze.go +++ /dev/null @@ -1,664 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package cophandler - -import ( - "bytes" - "context" - "math" - "math/rand" - "slices" - "time" - - "github.com/golang/protobuf/proto" - "github.com/pingcap/badger/y" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tipb/go-tipb" - "github.com/twmb/murmur3" -) - -// handleCopAnalyzeRequest handles coprocessor analyze request. -func handleCopAnalyzeRequest(dbReader *dbreader.DBReader, req *coprocessor.Request) *coprocessor.Response { - resp := &coprocessor.Response{} - if len(req.Ranges) == 0 { - return resp - } - if req.GetTp() != kv.ReqTypeAnalyze { - return resp - } - analyzeReq := new(tipb.AnalyzeReq) - err := proto.Unmarshal(req.Data, analyzeReq) - if err != nil { - resp.OtherError = err.Error() - return resp - } - ranges, err := extractKVRanges(dbReader.StartKey, dbReader.EndKey, req.Ranges, false) - if err != nil { - resp.OtherError = err.Error() - return resp - } - y.Assert(len(ranges) >= 1) - if analyzeReq.Tp == tipb.AnalyzeType_TypeIndex { - resp, err = handleAnalyzeIndexReq(dbReader, ranges, analyzeReq, req.StartTs) - } else if analyzeReq.Tp == tipb.AnalyzeType_TypeCommonHandle { - resp, err = handleAnalyzeCommonHandleReq(dbReader, ranges, analyzeReq, req.StartTs) - } else if analyzeReq.Tp == tipb.AnalyzeType_TypeColumn { - resp, err = handleAnalyzeColumnsReq(dbReader, ranges, analyzeReq, req.StartTs) - } else if analyzeReq.Tp == tipb.AnalyzeType_TypeMixed { - resp, err = handleAnalyzeMixedReq(dbReader, ranges, analyzeReq, req.StartTs) - } else { - resp, err = handleAnalyzeFullSamplingReq(dbReader, ranges, analyzeReq, req.StartTs) - } - if err != nil { - resp = &coprocessor.Response{ - OtherError: err.Error(), - } - } - return resp -} - -func handleAnalyzeIndexReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { - statsVer := int32(statistics.Version1) - if analyzeReq.IdxReq.Version != nil { - statsVer = *analyzeReq.IdxReq.Version - } - sctx := flagsToStatementContext(analyzeReq.Flags) - processor := &analyzeIndexProcessor{ - sctx: sctx, - colLen: int(analyzeReq.IdxReq.NumColumns), - statsBuilder: statistics.NewSortedBuilder(sctx, analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), int(statsVer)), - statsVer: statsVer, - } - if analyzeReq.IdxReq.TopNSize != nil { - processor.topNCount = *analyzeReq.IdxReq.TopNSize - } - if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { - processor.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) - } - processor.fms = statistics.NewFMSketch(int(analyzeReq.IdxReq.SketchSize)) - for _, ran := range rans { - err := dbReader.Scan(ran.StartKey, ran.EndKey, math.MaxInt64, startTS, processor) - if err != nil { - return nil, err - } - } - if statsVer >= statistics.Version2 { - if processor.topNCurValuePair.Count != 0 { - processor.topNValuePairs = append(processor.topNValuePairs, processor.topNCurValuePair) - } - slices.SortFunc(processor.topNValuePairs, statistics.TopnMetaCompare) - if len(processor.topNValuePairs) > int(processor.topNCount) { - processor.topNValuePairs = processor.topNValuePairs[:processor.topNCount] - } - } - hg := statistics.HistogramToProto(processor.statsBuilder.Hist()) - var cm *tipb.CMSketch - if processor.cms != nil { - if statsVer >= statistics.Version2 { - for _, valueCnt := range processor.topNValuePairs { - h1, h2 := murmur3.Sum128(valueCnt.Encoded) - processor.cms.SubValue(h1, h2, valueCnt.Count) - } - } - cm = statistics.CMSketchToProto(processor.cms, &statistics.TopN{TopN: processor.topNValuePairs}) - } - data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm, Collector: &tipb.SampleCollector{FmSketch: statistics.FMSketchToProto(processor.fms)}}) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -func handleAnalyzeCommonHandleReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { - statsVer := statistics.Version1 - if analyzeReq.IdxReq.Version != nil { - statsVer = int(*analyzeReq.IdxReq.Version) - } - processor := &analyzeCommonHandleProcessor{ - colLen: int(analyzeReq.IdxReq.NumColumns), - statsBuilder: statistics.NewSortedBuilder(flagsToStatementContext(analyzeReq.Flags), analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), statsVer), - } - if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { - processor.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) - } - for _, ran := range rans { - err := dbReader.Scan(ran.StartKey, ran.EndKey, math.MaxInt64, startTS, processor) - if err != nil { - return nil, err - } - } - hg := statistics.HistogramToProto(processor.statsBuilder.Hist()) - var cm *tipb.CMSketch - if processor.cms != nil { - cm = statistics.CMSketchToProto(processor.cms, nil) - } - data, err := proto.Marshal(&tipb.AnalyzeIndexResp{Hist: hg, Cms: cm}) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -type analyzeIndexProcessor struct { - skipVal - - sctx *stmtctx.StatementContext - colLen int - statsBuilder *statistics.SortedBuilder - cms *statistics.CMSketch - fms *statistics.FMSketch - rowBuf []byte - - statsVer int32 - topNCount int32 - topNValuePairs []statistics.TopNMeta - topNCurValuePair statistics.TopNMeta -} - -func (p *analyzeIndexProcessor) Process(key, _ []byte) error { - values, _, err := tablecodec.CutIndexKeyNew(key, p.colLen) - if err != nil { - return err - } - p.rowBuf = p.rowBuf[:0] - - for _, val := range values { - p.rowBuf = append(p.rowBuf, val...) - if p.cms != nil { - p.cms.InsertBytes(p.rowBuf) - } - } - - if p.fms != nil { - if err := p.fms.InsertValue(p.sctx, types.NewBytesDatum(safeCopy(p.rowBuf))); err != nil { - return err - } - } - - if p.statsVer >= statistics.Version2 { - if bytes.Equal(p.topNCurValuePair.Encoded, p.rowBuf) { - p.topNCurValuePair.Count++ - } else { - if p.topNCurValuePair.Count > 0 { - p.topNValuePairs = append(p.topNValuePairs, p.topNCurValuePair) - } - p.topNCurValuePair.Encoded = safeCopy(p.rowBuf) - p.topNCurValuePair.Count = 1 - } - } - rowData := safeCopy(p.rowBuf) - err = p.statsBuilder.Iterate(types.NewBytesDatum(rowData)) - if err != nil { - return err - } - return nil -} - -type analyzeCommonHandleProcessor struct { - skipVal - - colLen int - statsBuilder *statistics.SortedBuilder - cms *statistics.CMSketch - rowBuf []byte -} - -func (p *analyzeCommonHandleProcessor) Process(key, value []byte) error { - values, _, err := tablecodec.CutCommonHandle(key, p.colLen) - if err != nil { - return err - } - p.rowBuf = p.rowBuf[:0] - for _, val := range values { - p.rowBuf = append(p.rowBuf, val...) - if p.cms != nil { - p.cms.InsertBytes(p.rowBuf) - } - } - rowData := safeCopy(p.rowBuf) - err = p.statsBuilder.Iterate(types.NewBytesDatum(rowData)) - if err != nil { - return err - } - return nil -} - -type analyzeColumnsExec struct { - skipVal - reader *dbreader.DBReader - ranges []kv.KeyRange - curRan int - seekKey []byte - endKey []byte - startTS uint64 - - chk *chunk.Chunk - decoder *rowcodec.ChunkDecoder - req *chunk.Chunk - evalCtx *evalContext - fields []*ast.ResultField -} - -func buildBaseAnalyzeColumnsExec(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*analyzeColumnsExec, *statistics.SampleBuilder, int64, error) { - sc := flagsToStatementContext(analyzeReq.Flags) - sc.SetTimeZone(time.FixedZone("UTC", int(analyzeReq.TimeZoneOffset))) - evalCtx := &evalContext{sc: sc} - columns := analyzeReq.ColReq.ColumnsInfo - evalCtx.setColumnInfo(columns) - if len(analyzeReq.ColReq.PrimaryColumnIds) > 0 { - evalCtx.primaryCols = analyzeReq.ColReq.PrimaryColumnIds - } - decoder, err := newRowDecoder(evalCtx.columnInfos, evalCtx.fieldTps, evalCtx.primaryCols, evalCtx.sc.TimeZone()) - if err != nil { - return nil, nil, -1, err - } - e := &analyzeColumnsExec{ - reader: dbReader, - seekKey: rans[0].StartKey, - endKey: rans[0].EndKey, - ranges: rans, - curRan: 0, - startTS: startTS, - chk: chunk.NewChunkWithCapacity(evalCtx.fieldTps, 1), - decoder: decoder, - evalCtx: evalCtx, - } - e.fields = make([]*ast.ResultField, len(columns)) - for i := range e.fields { - rf := new(ast.ResultField) - rf.Column = new(model.ColumnInfo) - ft := types.FieldType{} - ft.SetType(mysql.TypeBlob) - ft.SetFlen(mysql.MaxBlobWidth) - ft.SetCharset(charset.CharsetUTF8) - ft.SetCollate(charset.CollationUTF8) - rf.Column.FieldType = ft - e.fields[i] = rf - } - - pkID := int64(-1) - numCols := len(columns) - if columns[0].GetPkHandle() { - pkID = columns[0].ColumnId - columns = columns[1:] - numCols-- - } - collators := make([]collate.Collator, numCols) - fts := make([]*types.FieldType, numCols) - for i, col := range columns { - ft := fieldTypeFromPBColumn(col) - fts[i] = ft - if ft.EvalType() == types.ETString { - collators[i] = collate.GetCollator(ft.GetCollate()) - } - } - colReq := analyzeReq.ColReq - builder := statistics.SampleBuilder{ - Sc: sc, - ColLen: numCols, - MaxBucketSize: colReq.BucketSize, - MaxFMSketchSize: colReq.SketchSize, - MaxSampleSize: colReq.SampleSize, - Collators: collators, - ColsFieldType: fts, - } - statsVer := statistics.Version1 - if analyzeReq.ColReq.Version != nil { - statsVer = int(*analyzeReq.ColReq.Version) - } - if pkID != -1 { - builder.PkBuilder = statistics.NewSortedBuilder(sc, builder.MaxBucketSize, pkID, types.NewFieldType(mysql.TypeBlob), statsVer) - } - if colReq.CmsketchWidth != nil && colReq.CmsketchDepth != nil { - builder.CMSketchWidth = *colReq.CmsketchWidth - builder.CMSketchDepth = *colReq.CmsketchDepth - } - return e, &builder, pkID, nil -} - -func handleAnalyzeColumnsReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { - recordSet, builder, pkID, err := buildBaseAnalyzeColumnsExec(dbReader, rans, analyzeReq, startTS) - if err != nil { - return nil, err - } - builder.RecordSet = recordSet - collectors, pkBuilder, err := builder.CollectColumnStats() - if err != nil { - return nil, errors.Trace(err) - } - colResp := &tipb.AnalyzeColumnsResp{} - if pkID != -1 { - colResp.PkHist = statistics.HistogramToProto(pkBuilder.Hist()) - } - for _, c := range collectors { - colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) - } - data, err := proto.Marshal(colResp) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -func handleAnalyzeFullSamplingReq( - dbReader *dbreader.DBReader, - rans []kv.KeyRange, - analyzeReq *tipb.AnalyzeReq, - startTS uint64, -) (*coprocessor.Response, error) { - sc := flagsToStatementContext(analyzeReq.Flags) - sc.SetTimeZone(time.FixedZone("UTC", int(analyzeReq.TimeZoneOffset))) - evalCtx := &evalContext{sc: sc} - columns := analyzeReq.ColReq.ColumnsInfo - evalCtx.setColumnInfo(columns) - if len(analyzeReq.ColReq.PrimaryColumnIds) > 0 { - evalCtx.primaryCols = analyzeReq.ColReq.PrimaryColumnIds - } - decoder, err := newRowDecoder(evalCtx.columnInfos, evalCtx.fieldTps, evalCtx.primaryCols, evalCtx.sc.TimeZone()) - if err != nil { - return nil, err - } - e := &analyzeColumnsExec{ - reader: dbReader, - seekKey: rans[0].StartKey, - endKey: rans[0].EndKey, - ranges: rans, - curRan: 0, - startTS: startTS, - chk: chunk.NewChunkWithCapacity(evalCtx.fieldTps, 1), - decoder: decoder, - evalCtx: evalCtx, - } - e.fields = make([]*ast.ResultField, len(columns)) - for i := range e.fields { - rf := new(ast.ResultField) - rf.Column = new(model.ColumnInfo) - ft := types.FieldType{} - ft.SetType(mysql.TypeBlob) - ft.SetFlen(mysql.MaxBlobWidth) - ft.SetCharset(charset.CharsetUTF8) - ft.SetCollate(charset.CollationUTF8) - rf.Column.FieldType = ft - e.fields[i] = rf - } - - numCols := len(columns) - collators := make([]collate.Collator, numCols) - fts := make([]*types.FieldType, numCols) - for i, col := range columns { - ft := fieldTypeFromPBColumn(col) - fts[i] = ft - if ft.EvalType() == types.ETString { - collators[i] = collate.GetCollator(ft.GetCollate()) - } - } - colGroups := make([][]int64, 0, len(analyzeReq.ColReq.ColumnGroups)) - for _, group := range analyzeReq.ColReq.ColumnGroups { - colOffsets := make([]int64, len(group.ColumnOffsets)) - copy(colOffsets, group.ColumnOffsets) - colGroups = append(colGroups, colOffsets) - } - colReq := analyzeReq.ColReq - /* #nosec G404 */ - builder := &statistics.RowSampleBuilder{ - Sc: sc, - RecordSet: e, - ColsFieldType: fts, - Collators: collators, - ColGroups: colGroups, - MaxSampleSize: int(colReq.SampleSize), - MaxFMSketchSize: int(colReq.SketchSize), - SampleRate: colReq.GetSampleRate(), - Rng: rand.New(rand.NewSource(time.Now().UnixNano())), - } - collector, err := builder.Collect() - if err != nil { - return nil, err - } - colResp := &tipb.AnalyzeColumnsResp{} - colResp.RowCollector = collector.Base().ToProto() - data, err := colResp.Marshal() - if err != nil { - return nil, err - } - return &coprocessor.Response{Data: data}, nil -} - -// Fields implements the sqlexec.RecordSet Fields interface. -func (e *analyzeColumnsExec) Fields() []*ast.ResultField { - return e.fields -} - -func (e *analyzeColumnsExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - e.req = req - err := e.reader.Scan(e.seekKey, e.endKey, math.MaxInt64, e.startTS, e) - if err != nil { - return err - } - if req.NumRows() < req.Capacity() { - if e.curRan == len(e.ranges)-1 { - e.seekKey = e.endKey - } else { - e.curRan++ - e.seekKey = e.ranges[e.curRan].StartKey - e.endKey = e.ranges[e.curRan].EndKey - } - } - return nil -} - -func (e *analyzeColumnsExec) Process(key, value []byte) error { - handle, err := tablecodec.DecodeRowKey(key) - if err != nil { - return errors.Trace(err) - } - err = e.decoder.DecodeToChunk(value, handle, e.chk) - if err != nil { - return errors.Trace(err) - } - row := e.chk.GetRow(0) - for i, tp := range e.evalCtx.fieldTps { - d := row.GetDatum(i, tp) - if d.IsNull() { - e.req.AppendNull(i) - continue - } - - value, err := tablecodec.EncodeValue(e.evalCtx.sc, nil, d) - if err != nil { - return err - } - e.req.AppendBytes(i, value) - } - e.chk.Reset() - if e.req.NumRows() == e.req.Capacity() { - e.seekKey = kv.Key(key).PrefixNext() - return dbreader.ErrScanBreak - } - return nil -} - -func (e *analyzeColumnsExec) NewChunk(_ chunk.Allocator) *chunk.Chunk { - fields := make([]*types.FieldType, 0, len(e.fields)) - for _, field := range e.fields { - fields = append(fields, &field.Column.FieldType) - } - return chunk.NewChunkWithCapacity(fields, 1024) -} - -// Close implements the sqlexec.RecordSet Close interface. -func (e *analyzeColumnsExec) Close() error { - return nil -} - -func handleAnalyzeMixedReq(dbReader *dbreader.DBReader, rans []kv.KeyRange, analyzeReq *tipb.AnalyzeReq, startTS uint64) (*coprocessor.Response, error) { - statsVer := int32(statistics.Version1) - if analyzeReq.IdxReq.Version != nil { - statsVer = *analyzeReq.IdxReq.Version - } - colExec, builder, _, err := buildBaseAnalyzeColumnsExec(dbReader, rans, analyzeReq, startTS) - if err != nil { - return nil, err - } - sctx := flagsToStatementContext(analyzeReq.Flags) - e := &analyzeMixedExec{ - sctx: sctx, - analyzeColumnsExec: *colExec, - colLen: int(analyzeReq.IdxReq.NumColumns), - statsBuilder: statistics.NewSortedBuilder(sctx, analyzeReq.IdxReq.BucketSize, 0, types.NewFieldType(mysql.TypeBlob), int(statsVer)), - statsVer: statsVer, - } - builder.RecordSet = e - if analyzeReq.IdxReq.TopNSize != nil { - e.topNCount = *analyzeReq.IdxReq.TopNSize - } - if analyzeReq.IdxReq.CmsketchDepth != nil && analyzeReq.IdxReq.CmsketchWidth != nil { - e.cms = statistics.NewCMSketch(*analyzeReq.IdxReq.CmsketchDepth, *analyzeReq.IdxReq.CmsketchWidth) - } - e.fms = statistics.NewFMSketch(int(analyzeReq.IdxReq.SketchSize)) - collectors, _, err := builder.CollectColumnStats() - if err != nil { - return nil, errors.Trace(err) - } - // columns - colResp := &tipb.AnalyzeColumnsResp{} - for _, c := range collectors { - colResp.Collectors = append(colResp.Collectors, statistics.SampleCollectorToProto(c)) - } - // common handle - if statsVer >= statistics.Version2 { - if e.topNCurValuePair.Count != 0 { - e.topNValuePairs = append(e.topNValuePairs, e.topNCurValuePair) - } - slices.SortFunc(e.topNValuePairs, statistics.TopnMetaCompare) - if len(e.topNValuePairs) > int(e.topNCount) { - e.topNValuePairs = e.topNValuePairs[:e.topNCount] - } - } - hg := statistics.HistogramToProto(e.statsBuilder.Hist()) - var cm *tipb.CMSketch - if e.cms != nil { - if statsVer >= statistics.Version2 { - for _, valueCnt := range e.topNValuePairs { - h1, h2 := murmur3.Sum128(valueCnt.Encoded) - e.cms.SubValue(h1, h2, valueCnt.Count) - } - } - cm = statistics.CMSketchToProto(e.cms, &statistics.TopN{TopN: e.topNValuePairs}) - } - fms := statistics.FMSketchToProto(e.fms) - commonHandleResp := &tipb.AnalyzeIndexResp{Hist: hg, Cms: cm, Collector: &tipb.SampleCollector{FmSketch: fms}} - resp := &tipb.AnalyzeMixedResp{ - ColumnsResp: colResp, - IndexResp: commonHandleResp, - } - data, err := proto.Marshal(resp) - if err != nil { - return nil, errors.Trace(err) - } - return &coprocessor.Response{Data: data}, nil -} - -type analyzeMixedExec struct { - analyzeColumnsExec - - sctx *stmtctx.StatementContext - colLen int - statsBuilder *statistics.SortedBuilder - fms *statistics.FMSketch - cms *statistics.CMSketch - rowBuf []byte - - statsVer int32 - topNCount int32 - topNValuePairs []statistics.TopNMeta - topNCurValuePair statistics.TopNMeta -} - -func (e *analyzeMixedExec) Process(key, value []byte) error { - // common handle - values, _, err := tablecodec.CutCommonHandle(key, e.colLen) - if err != nil { - return err - } - e.rowBuf = e.rowBuf[:0] - for _, val := range values { - e.rowBuf = append(e.rowBuf, val...) - if e.cms != nil { - e.cms.InsertBytes(e.rowBuf) - } - } - - if e.fms != nil { - if err := e.fms.InsertValue(e.sctx, types.NewBytesDatum(e.rowBuf)); err != nil { - return err - } - } - - if e.statsVer >= statistics.Version2 { - if bytes.Equal(e.topNCurValuePair.Encoded, e.rowBuf) { - e.topNCurValuePair.Count++ - } else { - if e.topNCurValuePair.Count > 0 { - e.topNValuePairs = append(e.topNValuePairs, e.topNCurValuePair) - } - e.topNCurValuePair.Encoded = safeCopy(e.rowBuf) - e.topNCurValuePair.Count = 1 - } - } - rowData := safeCopy(e.rowBuf) - err = e.statsBuilder.Iterate(types.NewBytesDatum(rowData)) - if err != nil { - return err - } - - // columns - err = e.analyzeColumnsExec.Process(key, value) - return err -} - -func (e *analyzeMixedExec) Next(ctx context.Context, req *chunk.Chunk) error { - req.Reset() - e.req = req - err := e.reader.Scan(e.seekKey, e.endKey, math.MaxInt64, e.startTS, e) - if err != nil { - return err - } - if req.NumRows() < req.Capacity() { - if e.curRan == len(e.ranges)-1 { - e.seekKey = e.endKey - } else { - e.curRan++ - e.seekKey = e.ranges[e.curRan].StartKey - e.endKey = e.ranges[e.curRan].EndKey - } - } - return nil -} diff --git a/store/mockstore/unistore/cophandler/main_test.go b/store/mockstore/unistore/cophandler/main_test.go deleted file mode 100644 index 44f1a68ca9748..0000000000000 --- a/store/mockstore/unistore/cophandler/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cophandler - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/mockstore/unistore/cophandler/mpp.go b/store/mockstore/unistore/cophandler/mpp.go deleted file mode 100644 index 37c41e5e850cb..0000000000000 --- a/store/mockstore/unistore/cophandler/mpp.go +++ /dev/null @@ -1,680 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package cophandler - -import ( - "context" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/coprocessor" - "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/expression/aggregation" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/client" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/atomic" -) - -const ( - // MPPErrTunnelNotFound means you can't find an expected tunnel. - MPPErrTunnelNotFound = iota - // MPPErrEstablishConnMultiTimes means we receive the Establish requests at least twice. - MPPErrEstablishConnMultiTimes - // MPPErrMPPGatherIDMismatch means we get mismatched gather id, usually a bug in MPP coordinator - MPPErrMPPGatherIDMismatch -) - -const ( - // ErrExecutorNotSupportedMsg is the message for executor not supported. - ErrExecutorNotSupportedMsg = "executor not supported: " -) - -type mppExecBuilder struct { - sc *stmtctx.StatementContext - dbReader *dbreader.DBReader - mppCtx *MPPCtx - dagReq *tipb.DAGRequest - dagCtx *dagContext - counts []int64 - ndvs []int64 - paging *coprocessor.KeyRange - pagingSize uint64 -} - -func (b *mppExecBuilder) buildMPPTableScan(pb *tipb.TableScan) (*tableScanExec, error) { - ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, pb.Desc) - if err != nil { - return nil, errors.Trace(err) - } - ts := &tableScanExec{ - baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx}, - startTS: b.dagCtx.startTS, - kvRanges: ranges, - dbReader: b.dbReader, - counts: b.counts, - ndvs: b.ndvs, - desc: pb.Desc, - paging: b.paging, - } - if b.dagCtx != nil { - ts.lockStore = b.dagCtx.lockStore - ts.resolvedLocks = b.dagCtx.resolvedLocks - } - for i, col := range pb.Columns { - if col.ColumnId == model.ExtraPhysTblID { - ts.physTblIDColIdx = new(int) - *ts.physTblIDColIdx = i - } - ft := fieldTypeFromPBColumn(col) - ts.fieldTypes = append(ts.fieldTypes, ft) - } - ts.decoder, err = newRowDecoder(pb.Columns, ts.fieldTypes, pb.PrimaryColumnIds, b.sc.TimeZone()) - return ts, err -} - -func (b *mppExecBuilder) buildMPPPartitionTableScan(pb *tipb.PartitionTableScan) (*tableScanExec, error) { - ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, false) - if err != nil { - return nil, errors.Trace(err) - } - ts := &tableScanExec{ - baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx}, - startTS: b.dagCtx.startTS, - kvRanges: ranges, - dbReader: b.dbReader, - } - for i, col := range pb.Columns { - if col.ColumnId == model.ExtraPhysTblID { - ts.physTblIDColIdx = new(int) - *ts.physTblIDColIdx = i - } - ft := fieldTypeFromPBColumn(col) - ts.fieldTypes = append(ts.fieldTypes, ft) - } - ts.decoder, err = newRowDecoder(pb.Columns, ts.fieldTypes, pb.PrimaryColumnIds, b.sc.TimeZone()) - return ts, err -} - -func (b *mppExecBuilder) buildIdxScan(pb *tipb.IndexScan) (*indexScanExec, error) { - ranges, err := extractKVRanges(b.dbReader.StartKey, b.dbReader.EndKey, b.dagCtx.keyRanges, pb.Desc) - if err != nil { - return nil, errors.Trace(err) - } - numCols := len(pb.Columns) - numIdxCols := numCols - colInfos := make([]rowcodec.ColInfo, 0, numCols) - fieldTypes := make([]*types.FieldType, 0, numCols) - primaryColIds := pb.GetPrimaryColumnIds() - - lastCol := pb.Columns[numCols-1] - var physTblIDColIdx *int - if lastCol.GetColumnId() == model.ExtraPhysTblID { - numIdxCols-- - physTblIDColIdx = new(int) - *physTblIDColIdx = numIdxCols - lastCol = pb.Columns[numIdxCols-1] - } - if lastCol.GetColumnId() == model.ExtraPidColID { - numIdxCols-- - lastCol = pb.Columns[numIdxCols-1] - } - - hdlStatus := tablecodec.HandleDefault - if len(primaryColIds) == 0 { - if lastCol.GetPkHandle() { - if mysql.HasUnsignedFlag(uint(lastCol.GetFlag())) { - hdlStatus = tablecodec.HandleIsUnsigned - } - numIdxCols-- - } else if lastCol.ColumnId == model.ExtraHandleID { - numIdxCols-- - } - } else { - numIdxCols -= len(primaryColIds) - } - - for _, col := range pb.Columns { - ft := fieldTypeFromPBColumn(col) - fieldTypes = append(fieldTypes, ft) - colInfos = append(colInfos, rowcodec.ColInfo{ - ID: col.ColumnId, - Ft: ft, - IsPKHandle: col.GetPkHandle(), - }) - } - - var prevVals [][]byte - if b.dagReq.GetCollectRangeCounts() { - prevVals = make([][]byte, numIdxCols) - } - idxScan := &indexScanExec{ - baseMPPExec: baseMPPExec{sc: b.sc, fieldTypes: fieldTypes}, - startTS: b.dagCtx.startTS, - kvRanges: ranges, - dbReader: b.dbReader, - lockStore: b.dagCtx.lockStore, - resolvedLocks: b.dagCtx.resolvedLocks, - counts: b.counts, - ndvs: b.ndvs, - prevVals: prevVals, - colInfos: colInfos, - numIdxCols: numIdxCols, - hdlStatus: hdlStatus, - desc: pb.Desc, - physTblIDColIdx: physTblIDColIdx, - paging: b.paging, - } - return idxScan, nil -} - -func (b *mppExecBuilder) buildLimit(pb *tipb.Limit) (*limitExec, error) { - child, err := b.buildMPPExecutor(pb.Child) - if err != nil { - return nil, err - } - exec := &limitExec{ - baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, fieldTypes: child.getFieldTypes(), children: []mppExec{child}}, - limit: pb.GetLimit(), - } - return exec, nil -} - -func (b *mppExecBuilder) buildExpand(pb *tipb.Expand) (mppExec, error) { - child, err := b.buildMPPExecutor(pb.Child) - if err != nil { - return nil, err - } - exec := &expandExec{ - baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, children: []mppExec{child}}, - } - - childFieldTypes := child.getFieldTypes() - // convert the grouping sets. - tidbGss := expression.GroupingSets{} - for _, gs := range pb.GroupingSets { - tidbGs := expression.GroupingSet{} - for _, groupingExprs := range gs.GroupingExprs { - tidbGroupingExprs, err := convertToExprs(b.sc, childFieldTypes, groupingExprs.GroupingExpr) - if err != nil { - return nil, err - } - tidbGs = append(tidbGs, tidbGroupingExprs) - } - tidbGss = append(tidbGss, tidbGs) - } - exec.groupingSets = tidbGss - inGroupingSetMap := make(map[int]struct{}, len(exec.groupingSets)) - for _, gs := range exec.groupingSets { - // for every grouping set, collect column offsets under this grouping set. - for _, groupingExprs := range gs { - for _, groupingExpr := range groupingExprs { - col, ok := groupingExpr.(*expression.Column) - if !ok { - return nil, errors.New("grouping set expr is not column ref") - } - inGroupingSetMap[col.Index] = struct{}{} - } - } - } - mutatedFieldTypes := make([]*types.FieldType, 0, len(childFieldTypes)) - // change the field types return from children tobe nullable. - for offset, f := range childFieldTypes { - cf := f.Clone() - if _, ok := inGroupingSetMap[offset]; ok { - // remove the not null flag, make it nullable. - cf.SetFlag(cf.GetFlag() & ^mysql.NotNullFlag) - } - mutatedFieldTypes = append(mutatedFieldTypes, cf) - } - - // adding groupingID uint64|not-null as last one field types. - groupingIDFieldType := types.NewFieldType(mysql.TypeLonglong) - groupingIDFieldType.SetFlag(mysql.NotNullFlag | mysql.UnsignedFlag) - mutatedFieldTypes = append(mutatedFieldTypes, groupingIDFieldType) - - exec.fieldTypes = mutatedFieldTypes - return exec, nil -} - -func (b *mppExecBuilder) buildTopN(pb *tipb.TopN) (mppExec, error) { - child, err := b.buildMPPExecutor(pb.Child) - if err != nil { - return nil, err - } - pbConds := make([]*tipb.Expr, len(pb.OrderBy)) - for i, item := range pb.OrderBy { - pbConds[i] = item.Expr - } - heap := &topNHeap{ - totalCount: int(pb.Limit), - topNSorter: topNSorter{ - orderByItems: pb.OrderBy, - sc: b.sc, - }, - } - fieldTps := child.getFieldTypes() - var conds []expression.Expression - if conds, err = convertToExprs(b.sc, fieldTps, pbConds); err != nil { - return nil, errors.Trace(err) - } - exec := &topNExec{ - baseMPPExec: baseMPPExec{sc: b.sc, mppCtx: b.mppCtx, fieldTypes: fieldTps, children: []mppExec{child}}, - heap: heap, - conds: conds, - row: newTopNSortRow(len(conds)), - topn: pb.Limit, - } - - // When using paging protocol, if paging size < topN limit, the topN exec degenerate to do nothing. - if b.paging != nil && b.pagingSize < pb.Limit { - exec.dummy = true - } - - return exec, nil -} - -func (b *mppExecBuilder) buildMPPExchangeSender(pb *tipb.ExchangeSender) (*exchSenderExec, error) { - child, err := b.buildMPPExecutor(pb.Child) - if err != nil { - return nil, err - } - - e := &exchSenderExec{ - baseMPPExec: baseMPPExec{ - sc: b.sc, - mppCtx: b.mppCtx, - children: []mppExec{child}, - fieldTypes: child.getFieldTypes(), - }, - exchangeTp: pb.Tp, - } - if pb.Tp == tipb.ExchangeType_Hash { - // remove the limitation of len(pb.PartitionKeys) == 1 - for _, partitionKey := range pb.PartitionKeys { - expr, err := expression.PBToExpr(partitionKey, child.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - col, ok := expr.(*expression.Column) - if !ok { - return nil, errors.New("Hash key must be column type") - } - e.hashKeyOffsets = append(e.hashKeyOffsets, col.Index) - e.hashKeyTypes = append(e.hashKeyTypes, e.fieldTypes[col.Index]) - } - } - - for _, taskMeta := range pb.EncodedTaskMeta { - targetTask := new(mpp.TaskMeta) - err := targetTask.Unmarshal(taskMeta) - if err != nil { - return nil, err - } - tunnel := &ExchangerTunnel{ - DataCh: make(chan *tipb.Chunk, 10), - sourceTask: b.mppCtx.TaskHandler.Meta, - targetTask: targetTask, - connectedCh: make(chan struct{}), - ErrCh: make(chan error, 1), - } - e.tunnels = append(e.tunnels, tunnel) - err = b.mppCtx.TaskHandler.registerTunnel(tunnel) - if err != nil { - return nil, err - } - } - e.outputOffsets = b.dagReq.OutputOffsets - return e, nil -} - -func (b *mppExecBuilder) buildMPPExchangeReceiver(pb *tipb.ExchangeReceiver) (*exchRecvExec, error) { - e := &exchRecvExec{ - baseMPPExec: baseMPPExec{ - sc: b.sc, - mppCtx: b.mppCtx, - }, - exchangeReceiver: pb, - } - - for _, pbType := range pb.FieldTypes { - tp := expression.FieldTypeFromPB(pbType) - if tp.GetType() == mysql.TypeEnum { - tp.SetElems(append(tp.GetElems(), pbType.Elems...)) - } - e.fieldTypes = append(e.fieldTypes, tp) - } - return e, nil -} - -func (b *mppExecBuilder) buildMPPJoin(pb *tipb.Join, children []*tipb.Executor) (*joinExec, error) { - e := &joinExec{ - baseMPPExec: baseMPPExec{ - sc: b.sc, - mppCtx: b.mppCtx, - }, - Join: pb, - hashMap: make(map[string][]chunk.Row), - buildSideIdx: pb.InnerIdx, - } - leftCh, err := b.buildMPPExecutor(children[0]) - if err != nil { - return nil, errors.Trace(err) - } - rightCh, err := b.buildMPPExecutor(children[1]) - if err != nil { - return nil, errors.Trace(err) - } - if pb.JoinType == tipb.JoinType_TypeLeftOuterJoin { - for _, tp := range rightCh.getFieldTypes() { - tp.DelFlag(mysql.NotNullFlag) - } - defaultInner := chunk.MutRowFromTypes(rightCh.getFieldTypes()) - for i := range rightCh.getFieldTypes() { - defaultInner.SetDatum(i, types.NewDatum(nil)) - } - e.defaultInner = defaultInner.ToRow() - } else if pb.JoinType == tipb.JoinType_TypeRightOuterJoin { - for _, tp := range leftCh.getFieldTypes() { - tp.DelFlag(mysql.NotNullFlag) - } - defaultInner := chunk.MutRowFromTypes(leftCh.getFieldTypes()) - for i := range leftCh.getFieldTypes() { - defaultInner.SetDatum(i, types.NewDatum(nil)) - } - e.defaultInner = defaultInner.ToRow() - } - // because the field type is immutable, so this kind of appending is safe. - e.fieldTypes = append(leftCh.getFieldTypes(), rightCh.getFieldTypes()...) - if pb.InnerIdx == 1 { - e.probeChild = leftCh - e.buildChild = rightCh - probeExpr, err := expression.PBToExpr(pb.LeftJoinKeys[0], leftCh.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.probeKey = probeExpr.(*expression.Column) - buildExpr, err := expression.PBToExpr(pb.RightJoinKeys[0], rightCh.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.buildKey = buildExpr.(*expression.Column) - } else { - e.probeChild = rightCh - e.buildChild = leftCh - buildExpr, err := expression.PBToExpr(pb.LeftJoinKeys[0], leftCh.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.buildKey = buildExpr.(*expression.Column) - probeExpr, err := expression.PBToExpr(pb.RightJoinKeys[0], rightCh.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.probeKey = probeExpr.(*expression.Column) - } - e.comKeyTp = types.AggFieldType([]*types.FieldType{e.probeKey.RetType, e.buildKey.RetType}) - if e.comKeyTp.GetType() == mysql.TypeNewDecimal { - e.comKeyTp.SetFlen(mysql.MaxDecimalWidth) - e.comKeyTp.SetDecimal(mysql.MaxDecimalScale) - } - return e, nil -} - -func (b *mppExecBuilder) buildMPPProj(proj *tipb.Projection) (*projExec, error) { - e := &projExec{} - - chExec, err := b.buildMPPExecutor(proj.Child) - if err != nil { - return nil, errors.Trace(err) - } - e.children = []mppExec{chExec} - - for _, pbExpr := range proj.Exprs { - expr, err := expression.PBToExpr(pbExpr, chExec.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.exprs = append(e.exprs, expr) - e.fieldTypes = append(e.fieldTypes, expr.GetType()) - } - return e, nil -} - -func (b *mppExecBuilder) buildMPPSel(sel *tipb.Selection) (*selExec, error) { - chExec, err := b.buildMPPExecutor(sel.Child) - if err != nil { - return nil, errors.Trace(err) - } - e := &selExec{ - baseMPPExec: baseMPPExec{ - fieldTypes: chExec.getFieldTypes(), - sc: b.sc, - mppCtx: b.mppCtx, - children: []mppExec{chExec}, - }, - } - - for _, pbExpr := range sel.Conditions { - expr, err := expression.PBToExpr(pbExpr, chExec.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.conditions = append(e.conditions, expr) - } - return e, nil -} - -func (b *mppExecBuilder) buildMPPAgg(agg *tipb.Aggregation) (*aggExec, error) { - e := &aggExec{ - groups: make(map[string]struct{}), - aggCtxsMap: make(map[string][]*aggregation.AggEvaluateContext), - processed: false, - } - - chExec, err := b.buildMPPExecutor(agg.Child) - if err != nil { - return nil, errors.Trace(err) - } - e.children = []mppExec{chExec} - for _, aggFunc := range agg.AggFunc { - ft := expression.PbTypeToFieldType(aggFunc.FieldType) - e.fieldTypes = append(e.fieldTypes, ft) - aggExpr, err := aggregation.NewDistAggFunc(aggFunc, chExec.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.aggExprs = append(e.aggExprs, aggExpr) - } - e.sc = b.sc - - for _, gby := range agg.GroupBy { - ft := expression.PbTypeToFieldType(gby.FieldType) - e.fieldTypes = append(e.fieldTypes, ft) - e.groupByTypes = append(e.groupByTypes, ft) - gbyExpr, err := expression.PBToExpr(gby, chExec.getFieldTypes(), b.sc) - if err != nil { - return nil, errors.Trace(err) - } - e.groupByExprs = append(e.groupByExprs, gbyExpr) - } - return e, nil -} - -func (b *mppExecBuilder) buildMPPExecutor(exec *tipb.Executor) (mppExec, error) { - switch exec.Tp { - case tipb.ExecType_TypeTableScan: - ts := exec.TblScan - return b.buildMPPTableScan(ts) - case tipb.ExecType_TypeExchangeReceiver: - rec := exec.ExchangeReceiver - return b.buildMPPExchangeReceiver(rec) - case tipb.ExecType_TypeExchangeSender: - send := exec.ExchangeSender - return b.buildMPPExchangeSender(send) - case tipb.ExecType_TypeJoin: - join := exec.Join - return b.buildMPPJoin(join, join.Children) - case tipb.ExecType_TypeAggregation, tipb.ExecType_TypeStreamAgg: - agg := exec.Aggregation - return b.buildMPPAgg(agg) - case tipb.ExecType_TypeProjection: - return b.buildMPPProj(exec.Projection) - case tipb.ExecType_TypeSelection: - return b.buildMPPSel(exec.Selection) - case tipb.ExecType_TypeIndexScan: - return b.buildIdxScan(exec.IdxScan) - case tipb.ExecType_TypeLimit: - return b.buildLimit(exec.Limit) - case tipb.ExecType_TypeTopN: - return b.buildTopN(exec.TopN) - case tipb.ExecType_TypePartitionTableScan: - ts := exec.PartitionTableScan - return b.buildMPPPartitionTableScan(ts) - case tipb.ExecType_TypeExpand: - return b.buildExpand(exec.Expand) - default: - return nil, errors.Errorf(ErrExecutorNotSupportedMsg + exec.Tp.String()) - } -} - -// HandleMPPDAGReq handles a cop request that is converted from mpp request. -// It returns nothing. Real data will return by stream rpc. -func HandleMPPDAGReq(dbReader *dbreader.DBReader, req *coprocessor.Request, mppCtx *MPPCtx) *coprocessor.Response { - dagReq := new(tipb.DAGRequest) - err := proto.Unmarshal(req.Data, dagReq) - if err != nil { - return &coprocessor.Response{OtherError: err.Error()} - } - dagCtx := &dagContext{ - dbReader: dbReader, - startTS: req.StartTs, - keyRanges: req.Ranges, - } - builder := mppExecBuilder{ - dbReader: dbReader, - mppCtx: mppCtx, - sc: flagsToStatementContext(dagReq.Flags), - dagReq: dagReq, - dagCtx: dagCtx, - } - mppExec, err := builder.buildMPPExecutor(dagReq.RootExecutor) - if err != nil { - panic("build error: " + err.Error()) - } - err = mppExec.open() - if err != nil { - panic("open phase find error: " + err.Error()) - } - _, err = mppExec.next() - if err != nil { - panic("running phase find error: " + err.Error()) - } - return &coprocessor.Response{} -} - -// MPPTaskHandler exists in a single store. -type MPPTaskHandler struct { - // When a connect request comes, it contains server task (source) and client task (target), Exchanger dataCh set will find dataCh by client task. - tunnelSetLock sync.Mutex - TunnelSet map[int64]*ExchangerTunnel - - Meta *mpp.TaskMeta - RPCClient client.Client - - Status atomic.Int32 - Err error -} - -// HandleEstablishConn handles EstablishMPPConnectionRequest -func (h *MPPTaskHandler) HandleEstablishConn(_ context.Context, req *mpp.EstablishMPPConnectionRequest) (*ExchangerTunnel, error) { - meta := req.ReceiverMeta - for i := 0; i < 10; i++ { - tunnel, err := h.getAndActiveTunnel(req) - if err == nil { - return tunnel, nil - } - if err.Code == MPPErrMPPGatherIDMismatch { - return nil, errors.Errorf(err.Msg) - } - time.Sleep(time.Second) - } - return nil, errors.Errorf("cannot find client task %d registered in server task %d", meta.TaskId, req.SenderMeta.TaskId) -} - -func (h *MPPTaskHandler) registerTunnel(tunnel *ExchangerTunnel) error { - if h.Meta.GatherId != tunnel.sourceTask.GatherId { - return errors.Errorf("mpp gather id mismatch, maybe a bug in MPP coordinator") - } - if h.Meta.GatherId != tunnel.targetTask.GatherId { - return errors.Errorf("mpp gather id mismatch, maybe a bug in MPP coordinator") - } - taskID := tunnel.targetTask.TaskId - h.tunnelSetLock.Lock() - defer h.tunnelSetLock.Unlock() - _, ok := h.TunnelSet[taskID] - if ok { - return errors.Errorf("task id %d has been registered", taskID) - } - h.TunnelSet[taskID] = tunnel - return nil -} - -func (h *MPPTaskHandler) getAndActiveTunnel(req *mpp.EstablishMPPConnectionRequest) (*ExchangerTunnel, *mpp.Error) { - if h.Meta.GatherId != req.ReceiverMeta.GatherId { - return nil, &mpp.Error{Code: MPPErrMPPGatherIDMismatch, Msg: "mpp gather id mismatch, maybe a bug in MPP coordinator"} - } - targetID := req.ReceiverMeta.TaskId - h.tunnelSetLock.Lock() - defer h.tunnelSetLock.Unlock() - if tunnel, ok := h.TunnelSet[targetID]; ok { - close(tunnel.connectedCh) - return tunnel, nil - } - // We dont find this dataCh, may be task not ready or have been deleted. - return nil, &mpp.Error{Code: MPPErrTunnelNotFound, Msg: "task not found, please wait for a while"} -} - -// ExchangerTunnel contains a channel that can transfer data. -// Only One Sender and Receiver use this channel, so it's safe to close it by sender. -type ExchangerTunnel struct { - DataCh chan *tipb.Chunk - - sourceTask *mpp.TaskMeta // source task is nearer to the data source - targetTask *mpp.TaskMeta // target task is nearer to the client end , as tidb. - - connectedCh chan struct{} - ErrCh chan error -} - -// RecvChunk recive tipb chunk -func (tunnel *ExchangerTunnel) RecvChunk() (tipbChunk *tipb.Chunk, err error) { - tipbChunk = <-tunnel.DataCh - select { - case err = <-tunnel.ErrCh: - default: - } - return tipbChunk, err -} diff --git a/store/mockstore/unistore/cophandler/topn.go b/store/mockstore/unistore/cophandler/topn.go deleted file mode 100644 index 5992af0b270ec..0000000000000 --- a/store/mockstore/unistore/cophandler/topn.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package cophandler - -import ( - "cmp" - "container/heap" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tipb/go-tipb" -) - -type sortRow struct { - key []types.Datum - data [][]byte -} - -// topNSorter implements sort.Interface. When all rows have been processed, the topNSorter will sort the whole data in heap. -type topNSorter struct { - orderByItems []*tipb.ByItem - rows []*sortRow - err error - sc *stmtctx.StatementContext -} - -func (t *topNSorter) Len() int { - return len(t.rows) -} - -func (t *topNSorter) Swap(i, j int) { - t.rows[i], t.rows[j] = t.rows[j], t.rows[i] -} - -func (t *topNSorter) Less(i, j int) bool { - for index, by := range t.orderByItems { - v1 := t.rows[i].key[index] - v2 := t.rows[j].key[index] - - ret, err := v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) - if err != nil { - t.err = errors.Trace(err) - return true - } - - if by.Desc { - ret = -ret - } - - if ret < 0 { - return true - } else if ret > 0 { - return false - } - } - - return false -} - -// topNHeap holds the top n elements using heap structure. It implements heap.Interface. -// When we insert a row, topNHeap will check if the row can become one of the top n element or not. -type topNHeap struct { - topNSorter - - // totalCount is equal to the limit count, which means the max size of heap. - totalCount int - // heapSize means the current size of this heap. - heapSize int -} - -func (t *topNHeap) Len() int { - return t.heapSize -} - -func (t *topNHeap) Push(x interface{}) { - t.rows = append(t.rows, x.(*sortRow)) - t.heapSize++ -} - -func (t *topNHeap) Pop() interface{} { - return nil -} - -func (t *topNHeap) Less(i, j int) bool { - for index, by := range t.orderByItems { - v1 := t.rows[i].key[index] - v2 := t.rows[j].key[index] - - var ret int - var err error - if expression.FieldTypeFromPB(by.GetExpr().GetFieldType()).GetType() == mysql.TypeEnum { - ret = cmp.Compare(v1.GetUint64(), v2.GetUint64()) - } else { - ret, err = v1.Compare(t.sc, &v2, collate.GetCollator(collate.ProtoToCollation(by.Expr.FieldType.Collate))) - if err != nil { - t.err = errors.Trace(err) - return true - } - } - - if by.Desc { - ret = -ret - } - - if ret > 0 { - return true - } else if ret < 0 { - return false - } - } - - return false -} - -// tryToAddRow tries to add a row to heap. -// When this row is not less than any rows in heap, it will never become the top n element. -// Then this function returns false. -func (t *topNHeap) tryToAddRow(row *sortRow) bool { - success := false - if t.heapSize == t.totalCount { - t.rows = append(t.rows, row) - // When this row is less than the top element, it will replace it and adjust the heap structure. - if t.Less(0, t.heapSize) { - t.Swap(0, t.heapSize) - heap.Fix(t, 0) - success = true - } - t.rows = t.rows[:t.heapSize] - } else { - heap.Push(t, row) - success = true - } - return success -} diff --git a/store/mockstore/unistore/lockstore/BUILD.bazel b/store/mockstore/unistore/lockstore/BUILD.bazel deleted file mode 100644 index fb8e8f534f0e2..0000000000000 --- a/store/mockstore/unistore/lockstore/BUILD.bazel +++ /dev/null @@ -1,35 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "lockstore", - srcs = [ - "arena.go", - "iterator.go", - "load_dump.go", - "lockstore.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/lockstore", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "lockstore_test", - timeout = "short", - srcs = [ - "lockstore_test.go", - "main_test.go", - ], - embed = [":lockstore"], - flaky = True, - shard_count = 4, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/unistore/lockstore/main_test.go b/store/mockstore/unistore/lockstore/main_test.go deleted file mode 100644 index 90e0fe6f2c2cd..0000000000000 --- a/store/mockstore/unistore/lockstore/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package lockstore - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/mockstore/unistore/main_test.go b/store/mockstore/unistore/main_test.go deleted file mode 100644 index 894979442e47d..0000000000000 --- a/store/mockstore/unistore/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package unistore - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/mockstore/unistore/metrics/BUILD.bazel b/store/mockstore/unistore/metrics/BUILD.bazel deleted file mode 100644 index 6bc783d0ce4b3..0000000000000 --- a/store/mockstore/unistore/metrics/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/metrics", - visibility = ["//visibility:public"], - deps = ["@com_github_prometheus_client_golang//prometheus"], -) diff --git a/store/mockstore/unistore/mock.go b/store/mockstore/unistore/mock.go deleted file mode 100644 index 06bfe7396abc4..0000000000000 --- a/store/mockstore/unistore/mock.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package unistore - -import ( - "os" - - "github.com/pingcap/errors" - usconf "github.com/pingcap/tidb/store/mockstore/unistore/config" - ussvr "github.com/pingcap/tidb/store/mockstore/unistore/server" - pd "github.com/tikv/pd/client" -) - -// New creates an embed unistore client, pd client and cluster handler. -func New(path string) (*RPCClient, pd.Client, *Cluster, error) { - persistent := true - if path == "" { - var err error - if path, err = os.MkdirTemp("", "tidb-unistore-temp"); err != nil { - return nil, nil, nil, err - } - persistent = false - } - - if err := os.MkdirAll(path, 0750); err != nil { - return nil, nil, nil, err - } - - conf := usconf.DefaultConf - conf.Engine.ValueThreshold = 0 - conf.Engine.DBPath = path - conf.Server.Raft = false - - if !persistent { - conf.Engine.VolatileMode = true - conf.Engine.MaxMemTableSize = 12 << 20 - conf.Engine.SyncWrite = false - conf.Engine.NumCompactors = 1 - conf.Engine.CompactL0WhenClose = false - conf.Engine.VlogFileSize = 16 << 20 - } - - srv, rm, pd, err := ussvr.NewMock(&conf, 1) - if err != nil { - return nil, nil, nil, errors.Trace(err) - } - - cluster := newCluster(rm) - client := &RPCClient{ - usSvr: srv, - cluster: cluster, - path: path, - persistent: persistent, - rawHandler: newRawHandler(), - } - srv.RPCClient = client - pdClient := newPDClient(pd) - - return client, pdClient, cluster, nil -} diff --git a/store/mockstore/unistore/pd.go b/store/mockstore/unistore/pd.go deleted file mode 100644 index 70d106566bbd5..0000000000000 --- a/store/mockstore/unistore/pd.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package unistore - -import ( - "context" - "errors" - "math" - "sync" - "sync/atomic" - - "github.com/pingcap/kvproto/pkg/keyspacepb" - "github.com/pingcap/kvproto/pkg/meta_storagepb" - "github.com/pingcap/kvproto/pkg/pdpb" - rmpb "github.com/pingcap/kvproto/pkg/resource_manager" - "github.com/pingcap/tidb/domain/infosync" - us "github.com/pingcap/tidb/store/mockstore/unistore/tikv" - "github.com/tikv/client-go/v2/oracle" - pd "github.com/tikv/pd/client" -) - -var _ pd.Client = new(pdClient) - -type pdClient struct { - *us.MockPD - pd.ResourceManagerClient - - serviceSafePoints map[string]uint64 - gcSafePointMu sync.Mutex - globalConfig map[string]string - externalTimestamp atomic.Uint64 -} - -func newPDClient(pd *us.MockPD) *pdClient { - return &pdClient{ - MockPD: pd, - ResourceManagerClient: infosync.NewMockResourceManagerClient(), - serviceSafePoints: make(map[string]uint64), - globalConfig: make(map[string]string), - } -} - -func (c *pdClient) LoadGlobalConfig(ctx context.Context, names []string, configPath string) ([]pd.GlobalConfigItem, int64, error) { - ret := make([]pd.GlobalConfigItem, len(names)) - for i, name := range names { - if r, ok := c.globalConfig["/global/config/"+name]; ok { - ret[i] = pd.GlobalConfigItem{Name: "/global/config/" + name, Value: r, EventType: pdpb.EventType_PUT} - } else { - ret[i] = pd.GlobalConfigItem{Name: "/global/config/" + name, Value: ""} - } - } - return ret, 0, nil -} - -func (c *pdClient) StoreGlobalConfig(ctx context.Context, configPath string, items []pd.GlobalConfigItem) error { - for _, item := range items { - c.globalConfig["/global/config/"+item.Name] = item.Value - } - return nil -} - -func (c *pdClient) WatchGlobalConfig(ctx context.Context, configPath string, revision int64) (chan []pd.GlobalConfigItem, error) { - globalConfigWatcherCh := make(chan []pd.GlobalConfigItem, 16) - go func() { - defer func() { - if r := recover(); r != nil { - return - } - }() - for i := 0; i < 10; i++ { - for k, v := range c.globalConfig { - globalConfigWatcherCh <- []pd.GlobalConfigItem{{Name: k, Value: v}} - } - } - }() - return globalConfigWatcherCh, nil -} - -func (c *pdClient) GetLocalTS(ctx context.Context, dcLocation string) (int64, int64, error) { - return c.GetTS(ctx) -} - -func (c *pdClient) GetTSAsync(ctx context.Context) pd.TSFuture { - return &mockTSFuture{c, ctx, false} -} - -func (c *pdClient) GetLocalTSAsync(ctx context.Context, dcLocation string) pd.TSFuture { - return &mockTSFuture{c, ctx, false} -} - -type mockTSFuture struct { - pdc *pdClient - ctx context.Context - used bool -} - -func (m *mockTSFuture) Wait() (int64, int64, error) { - if m.used { - return 0, 0, errors.New("cannot wait tso twice") - } - m.used = true - return m.pdc.GetTS(m.ctx) -} - -func (c *pdClient) GetLeaderAddr() string { return "mockpd" } - -func (c *pdClient) UpdateServiceGCSafePoint(ctx context.Context, serviceID string, ttl int64, safePoint uint64) (uint64, error) { - c.gcSafePointMu.Lock() - defer c.gcSafePointMu.Unlock() - - if ttl == 0 { - delete(c.serviceSafePoints, serviceID) - } else { - var minSafePoint uint64 = math.MaxUint64 - for _, ssp := range c.serviceSafePoints { - if ssp < minSafePoint { - minSafePoint = ssp - } - } - - if len(c.serviceSafePoints) == 0 || minSafePoint <= safePoint { - c.serviceSafePoints[serviceID] = safePoint - } - } - - // The minSafePoint may have changed. Reload it. - var minSafePoint uint64 = math.MaxUint64 - for _, ssp := range c.serviceSafePoints { - if ssp < minSafePoint { - minSafePoint = ssp - } - } - return minSafePoint, nil -} - -func (c *pdClient) GetOperator(ctx context.Context, regionID uint64) (*pdpb.GetOperatorResponse, error) { - return &pdpb.GetOperatorResponse{Status: pdpb.OperatorStatus_SUCCESS}, nil -} - -func (c *pdClient) GetAllMembers(ctx context.Context) ([]*pdpb.Member, error) { - return nil, nil -} - -func (c *pdClient) ScatterRegions(ctx context.Context, regionsID []uint64, opts ...pd.RegionsOption) (*pdpb.ScatterRegionResponse, error) { - return nil, nil -} - -func (c *pdClient) SplitRegions(ctx context.Context, splitKeys [][]byte, opts ...pd.RegionsOption) (*pdpb.SplitRegionsResponse, error) { - return nil, nil -} - -func (c *pdClient) SplitAndScatterRegions(ctx context.Context, splitKeys [][]byte, opts ...pd.RegionsOption) (*pdpb.SplitAndScatterRegionsResponse, error) { - return nil, nil -} - -func (c *pdClient) GetRegionFromMember(ctx context.Context, key []byte, memberURLs []string) (*pd.Region, error) { - return nil, nil -} - -func (c *pdClient) UpdateOption(option pd.DynamicOption, value interface{}) error { - return nil -} - -func (c *pdClient) GetAllKeyspaces(ctx context.Context, startID uint32, limit uint32) ([]*keyspacepb.KeyspaceMeta, error) { - return nil, nil -} - -// LoadKeyspace loads and returns target keyspace's metadata. -func (c *pdClient) LoadKeyspace(ctx context.Context, name string) (*keyspacepb.KeyspaceMeta, error) { - return nil, nil -} - -// WatchKeyspaces watches keyspace meta changes. -// It returns a stream of slices of keyspace metadata. -// The first message in stream contains all current keyspaceMeta, -// all subsequent messages contains new put events for all keyspaces. -func (c *pdClient) WatchKeyspaces(ctx context.Context) (chan []*keyspacepb.KeyspaceMeta, error) { - return nil, nil -} - -func (c *pdClient) UpdateKeyspaceState(ctx context.Context, id uint32, state keyspacepb.KeyspaceState) (*keyspacepb.KeyspaceMeta, error) { - return nil, nil -} - -func (c *pdClient) AcquireTokenBuckets(ctx context.Context, request *rmpb.TokenBucketsRequest) ([]*rmpb.TokenBucketResponse, error) { - return nil, nil -} - -func (c *pdClient) SetExternalTimestamp(ctx context.Context, newTimestamp uint64) error { - p, l, err := c.GetTS(ctx) - if err != nil { - return err - } - - currentTSO := oracle.ComposeTS(p, l) - if newTimestamp > currentTSO { - return errors.New("external timestamp is greater than global tso") - } - for { - externalTimestamp := c.externalTimestamp.Load() - if externalTimestamp > newTimestamp { - return errors.New("cannot decrease the external timestamp") - } else if externalTimestamp == newTimestamp { - return nil - } - - if c.externalTimestamp.CompareAndSwap(externalTimestamp, newTimestamp) { - return nil - } - } -} - -func (c *pdClient) GetExternalTimestamp(ctx context.Context) (uint64, error) { - return c.externalTimestamp.Load(), nil -} - -func (c *pdClient) GetTSWithinKeyspace(ctx context.Context, keyspaceID uint32) (int64, int64, error) { - return 0, 0, nil -} - -func (c *pdClient) GetTSWithinKeyspaceAsync(ctx context.Context, keyspaceID uint32) pd.TSFuture { - return nil -} - -func (c *pdClient) GetLocalTSWithinKeyspace(ctx context.Context, dcLocation string, keyspaceID uint32) (int64, int64, error) { - return 0, 0, nil -} - -func (c *pdClient) GetLocalTSWithinKeyspaceAsync(ctx context.Context, dcLocation string, keyspaceID uint32) pd.TSFuture { - return nil -} - -func (c *pdClient) Get(ctx context.Context, key []byte, opts ...pd.OpOption) (*meta_storagepb.GetResponse, error) { - return nil, nil -} - -func (c *pdClient) Put(ctx context.Context, key []byte, value []byte, opts ...pd.OpOption) (*meta_storagepb.PutResponse, error) { - return nil, nil -} - -func (c *pdClient) GetMinTS(ctx context.Context) (int64, int64, error) { - return 0, 0, nil -} - -func (c *pdClient) LoadResourceGroups(ctx context.Context) ([]*rmpb.ResourceGroup, int64, error) { - return nil, 0, nil -} - -func (c *pdClient) UpdateGCSafePointV2(ctx context.Context, keyspaceID uint32, safePoint uint64) (uint64, error) { - panic("unimplemented") -} - -func (c *pdClient) UpdateServiceSafePointV2(ctx context.Context, keyspaceID uint32, serviceID string, ttl int64, safePoint uint64) (uint64, error) { - panic("unimplemented") -} - -func (c *pdClient) WatchGCSafePointV2(ctx context.Context, revision int64) (chan []*pdpb.SafePointEvent, error) { - panic("unimplemented") -} diff --git a/store/mockstore/unistore/pd/BUILD.bazel b/store/mockstore/unistore/pd/BUILD.bazel deleted file mode 100644 index 5d5563cbdc153..0000000000000 --- a/store/mockstore/unistore/pd/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "pd", - srcs = ["client.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/pd", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/pdpb", - "@com_github_pingcap_log//:log", - "@com_github_tikv_pd_client//:client", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_zap//:zap", - ], -) diff --git a/store/mockstore/unistore/server/BUILD.bazel b/store/mockstore/unistore/server/BUILD.bazel deleted file mode 100644 index 63326b48e9afe..0000000000000 --- a/store/mockstore/unistore/server/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "server", - srcs = ["server.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/server", - visibility = ["//visibility:public"], - deps = [ - "//store/mockstore/unistore/config", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/pd", - "//store/mockstore/unistore/tikv", - "//store/mockstore/unistore/tikv/mvcc", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//options", - "@com_github_pingcap_errors//:errors", - ], -) diff --git a/store/mockstore/unistore/server/server.go b/store/mockstore/unistore/server/server.go deleted file mode 100644 index 206a744ad0ae8..0000000000000 --- a/store/mockstore/unistore/server/server.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package server - -import ( - "context" - "path/filepath" - - "github.com/pingcap/badger" - "github.com/pingcap/badger/options" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/store/mockstore/unistore/config" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/pd" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" -) - -const ( - subPathRaft = "raft" - subPathKV = "kv" -) - -// NewMock returns a new *tikv.Server, a new *tikv.MockRegionManager and a new *tikv.MockPD. -func NewMock(conf *config.Config, clusterID uint64) (*tikv.Server, *tikv.MockRegionManager, *tikv.MockPD, error) { - physical, logical := tikv.GetTS() - ts := uint64(physical)<<18 + uint64(logical) - - safePoint := &tikv.SafePoint{} - db, err := createDB(subPathKV, safePoint, &conf.Engine) - if err != nil { - return nil, nil, nil, err - } - bundle := &mvcc.DBBundle{ - DB: db, - LockStore: lockstore.NewMemStore(8 << 20), - StateTS: ts, - } - - rm, err := tikv.NewMockRegionManager(bundle, clusterID, tikv.RegionOptions{ - StoreAddr: conf.Server.StoreAddr, - PDAddr: conf.Server.PDAddr, - RegionSize: conf.Server.RegionSize, - }) - if err != nil { - return nil, nil, nil, err - } - pdClient := tikv.NewMockPD(rm) - svr, err := setupStandAlongInnerServer(bundle, safePoint, rm, pdClient, conf) - if err != nil { - return nil, nil, nil, err - } - return svr, rm, pdClient, nil -} - -// New returns a new *tikv.Server. -func New(conf *config.Config, pdClient pd.Client) (*tikv.Server, error) { - physical, logical, err := pdClient.GetTS(context.Background()) - if err != nil { - return nil, err - } - ts := uint64(physical)<<18 + uint64(logical) - - safePoint := &tikv.SafePoint{} - db, err := createDB(subPathKV, safePoint, &conf.Engine) - if err != nil { - return nil, err - } - bundle := &mvcc.DBBundle{ - DB: db, - LockStore: lockstore.NewMemStore(8 << 20), - StateTS: ts, - } - if conf.Server.Raft { - return nil, errors.New("not support raftstore") - } - rm := tikv.NewStandAloneRegionManager(bundle, getRegionOptions(conf), pdClient) - return setupStandAlongInnerServer(bundle, safePoint, rm, pdClient, conf) -} - -func getRegionOptions(conf *config.Config) tikv.RegionOptions { - return tikv.RegionOptions{ - StoreAddr: conf.Server.StoreAddr, - PDAddr: conf.Server.PDAddr, - RegionSize: conf.Server.RegionSize, - } -} - -func setupStandAlongInnerServer(bundle *mvcc.DBBundle, safePoint *tikv.SafePoint, rm tikv.RegionManager, pdClient pd.Client, conf *config.Config) (*tikv.Server, error) { - innerServer := tikv.NewStandAlongInnerServer(bundle) - innerServer.Setup(pdClient) - store := tikv.NewMVCCStore(conf, bundle, conf.Engine.DBPath, safePoint, tikv.NewDBWriter(bundle), pdClient) - store.DeadlockDetectSvr.ChangeRole(tikv.Leader) - - if err := innerServer.Start(pdClient); err != nil { - return nil, err - } - - store.StartDeadlockDetection(false) - - return tikv.NewServer(rm, store, innerServer), nil -} - -func createDB(subPath string, safePoint *tikv.SafePoint, conf *config.Engine) (*badger.DB, error) { - opts := badger.DefaultOptions - opts.NumCompactors = conf.NumCompactors - opts.ValueThreshold = conf.ValueThreshold - if subPath == subPathRaft { - // Do not need to write blob for raft engine because it will be deleted soon. - return nil, errors.New("not support " + subPathRaft) - } - opts.ManagedTxns = true - opts.ValueLogWriteOptions.WriteBufferSize = 4 * 1024 * 1024 - opts.Dir = filepath.Join(conf.DBPath, subPath) - opts.ValueDir = opts.Dir - opts.ValueLogFileSize = conf.VlogFileSize - opts.ValueLogMaxNumFiles = 3 - opts.MaxMemTableSize = conf.MaxMemTableSize - opts.TableBuilderOptions.MaxTableSize = conf.MaxTableSize - opts.NumMemtables = conf.NumMemTables - opts.NumLevelZeroTables = conf.NumL0Tables - opts.NumLevelZeroTablesStall = conf.NumL0TablesStall - opts.LevelOneSize = conf.L1Size - opts.SyncWrites = conf.SyncWrite - compressionPerLevel := make([]options.CompressionType, len(conf.Compression)) - for i := range opts.TableBuilderOptions.CompressionPerLevel { - compressionPerLevel[i] = config.ParseCompression(conf.Compression[i]) - } - opts.TableBuilderOptions.CompressionPerLevel = compressionPerLevel - opts.MaxBlockCacheSize = conf.BlockCacheSize - opts.MaxIndexCacheSize = conf.IndexCacheSize - opts.TableBuilderOptions.SuRFStartLevel = conf.SurfStartLevel - if safePoint != nil { - opts.CompactionFilterFactory = safePoint.CreateCompactionFilter - } - opts.CompactL0WhenClose = conf.CompactL0WhenClose - opts.VolatileMode = conf.VolatileMode - return badger.Open(opts) -} diff --git a/store/mockstore/unistore/testutil.go b/store/mockstore/unistore/testutil.go deleted file mode 100644 index d085a24ba5788..0000000000000 --- a/store/mockstore/unistore/testutil.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package unistore - -import ( - "errors" - "fmt" - "runtime" - - "github.com/pingcap/tidb/tablecodec" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/tikv/client-go/v2/tikvrpc" -) - -func checkResourceTagForTopSQL(req *tikvrpc.Request) error { - if !topsqlstate.TopSQLEnabled() { - return nil - } - tag := req.GetResourceGroupTag() - if len(tag) > 0 { - return nil - } - - startKey, err := getReqStartKey(req) - if err != nil { - return err - } - var tid int64 - if tablecodec.IsRecordKey(startKey) { - tid, _, _ = tablecodec.DecodeRecordKey(startKey) - } - if tablecodec.IsIndexKey(startKey) { - tid, _, _, _ = tablecodec.DecodeIndexKey(startKey) - } - // since the error maybe "invalid record key", should just ignore check resource tag for this request. - if tid > 0 { - stack := getStack() - return fmt.Errorf("%v req does not set the resource tag, tid: %v, stack: %v", - req.Type.String(), tid, string(stack)) - } - return nil -} - -func getReqStartKey(req *tikvrpc.Request) ([]byte, error) { - switch req.Type { - case tikvrpc.CmdGet: - request := req.Get() - return request.Key, nil - case tikvrpc.CmdScan: - request := req.Scan() - return request.StartKey, nil - case tikvrpc.CmdPrewrite: - request := req.Prewrite() - return request.Mutations[0].Key, nil - case tikvrpc.CmdCommit: - request := req.Commit() - return request.Keys[0], nil - case tikvrpc.CmdCleanup: - request := req.Cleanup() - return request.Key, nil - case tikvrpc.CmdBatchGet: - request := req.BatchGet() - return request.Keys[0], nil - case tikvrpc.CmdBatchRollback: - request := req.BatchRollback() - return request.Keys[0], nil - case tikvrpc.CmdScanLock: - request := req.ScanLock() - return request.StartKey, nil - case tikvrpc.CmdPessimisticLock: - request := req.PessimisticLock() - return request.PrimaryLock, nil - case tikvrpc.CmdCheckSecondaryLocks: - request := req.CheckSecondaryLocks() - return request.Keys[0], nil - case tikvrpc.CmdCop, tikvrpc.CmdCopStream: - request := req.Cop() - return request.Ranges[0].Start, nil - case tikvrpc.CmdGC, tikvrpc.CmdDeleteRange, tikvrpc.CmdTxnHeartBeat, tikvrpc.CmdRawGet, - tikvrpc.CmdRawBatchGet, tikvrpc.CmdRawPut, tikvrpc.CmdRawBatchPut, tikvrpc.CmdRawDelete, tikvrpc.CmdRawBatchDelete, tikvrpc.CmdRawDeleteRange, - tikvrpc.CmdRawScan, tikvrpc.CmdGetKeyTTL, tikvrpc.CmdRawCompareAndSwap, tikvrpc.CmdUnsafeDestroyRange, tikvrpc.CmdRegisterLockObserver, - tikvrpc.CmdCheckLockObserver, tikvrpc.CmdRemoveLockObserver, tikvrpc.CmdPhysicalScanLock, tikvrpc.CmdStoreSafeTS, - tikvrpc.CmdLockWaitInfo, tikvrpc.CmdMvccGetByKey, tikvrpc.CmdMvccGetByStartTs, tikvrpc.CmdSplitRegion, - tikvrpc.CmdDebugGetRegionProperties, tikvrpc.CmdEmpty: - // Ignore those requests since now, since it is no business with TopSQL. - return nil, nil - case tikvrpc.CmdBatchCop, tikvrpc.CmdMPPTask, tikvrpc.CmdMPPConn, tikvrpc.CmdMPPCancel, tikvrpc.CmdMPPAlive: - // Ignore mpp requests. - return nil, nil - case tikvrpc.CmdResolveLock, tikvrpc.CmdCheckTxnStatus, tikvrpc.CmdPessimisticRollback: - // TODO: add resource tag for those request. https://github.com/pingcap/tidb/issues/33621 - return nil, nil - default: - return nil, errors.New("unknown request, check the new type RPC request here") - } -} - -func getStack() []byte { - const size = 1024 * 64 - buf := make([]byte, size) - stackSize := runtime.Stack(buf, false) - buf = buf[:stackSize] - return buf -} diff --git a/store/mockstore/unistore/tikv/BUILD.bazel b/store/mockstore/unistore/tikv/BUILD.bazel deleted file mode 100644 index 10bb689f2cfb7..0000000000000 --- a/store/mockstore/unistore/tikv/BUILD.bazel +++ /dev/null @@ -1,89 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "tikv", - srcs = [ - "deadlock.go", - "detector.go", - "inner_server.go", - "mock_region.go", - "mvcc.go", - "region.go", - "server.go", - "server_batch.go", - "util.go", - "write.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/tikv", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//sessionctx/stmtctx", - "//store/mockstore/unistore/client", - "//store/mockstore/unistore/config", - "//store/mockstore/unistore/cophandler", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/metrics", - "//store/mockstore/unistore/pd", - "//store/mockstore/unistore/tikv/dbreader", - "//store/mockstore/unistore/tikv/kverrors", - "//store/mockstore/unistore/tikv/mvcc", - "//store/mockstore/unistore/tikv/pberror", - "//store/mockstore/unistore/util/lockwaiter", - "//tablecodec", - "//types", - "//util/codec", - "//util/mathutil", - "//util/rowcodec", - "@com_github_dgryski_go_farm//:go-farm", - "@com_github_gogo_protobuf//proto", - "@com_github_google_btree//:btree", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/errorpb", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/mpp", - "@com_github_pingcap_kvproto//pkg/pdpb", - "@com_github_pingcap_kvproto//pkg/tikvpb", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_pd_client//:client", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "tikv_test", - timeout = "short", - srcs = [ - "detector_test.go", - "main_test.go", - "mvcc_test.go", - ], - embed = [":tikv"], - flaky = True, - shard_count = 28, - deps = [ - "//store/mockstore/unistore/config", - "//store/mockstore/unistore/lockstore", - "//store/mockstore/unistore/tikv/kverrors", - "//store/mockstore/unistore/tikv/mvcc", - "//store/mockstore/unistore/util/lockwaiter", - "//testkit/testsetup", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/unistore/tikv/dbreader/BUILD.bazel b/store/mockstore/unistore/tikv/dbreader/BUILD.bazel deleted file mode 100644 index ef608d64f4331..0000000000000 --- a/store/mockstore/unistore/tikv/dbreader/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "dbreader", - srcs = ["db_reader.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader", - visibility = ["//visibility:public"], - deps = [ - "//store/mockstore/unistore/tikv/kverrors", - "//store/mockstore/unistore/tikv/mvcc", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - ], -) diff --git a/store/mockstore/unistore/tikv/kverrors/BUILD.bazel b/store/mockstore/unistore/tikv/kverrors/BUILD.bazel deleted file mode 100644 index bcec46e225a9b..0000000000000 --- a/store/mockstore/unistore/tikv/kverrors/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "kverrors", - srcs = ["errors.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors", - visibility = ["//visibility:public"], - deps = [ - "//store/mockstore/unistore/tikv/mvcc", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - ], -) diff --git a/store/mockstore/unistore/tikv/kverrors/errors.go b/store/mockstore/unistore/tikv/kverrors/errors.go deleted file mode 100644 index 613e45b274ab5..0000000000000 --- a/store/mockstore/unistore/tikv/kverrors/errors.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package kverrors - -import ( - "encoding/hex" - "fmt" - - deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" -) - -// ErrLocked is returned when trying to Read/Write on a locked key. Client should -// backoff or cleanup the lock then retry. -type ErrLocked struct { - Key []byte - Lock *mvcc.Lock -} - -// BuildLockErr generates ErrKeyLocked objects -func BuildLockErr(key []byte, lock *mvcc.Lock) *ErrLocked { - errLocked := &ErrLocked{ - Key: key, - Lock: lock, - } - return errLocked -} - -// Error formats the lock to a string. -func (e *ErrLocked) Error() string { - lock := e.Lock - return fmt.Sprintf( - "key is locked, key: %v, lock: %v", - hex.EncodeToString(e.Key), lock.String(), - ) -} - -// ErrRetryable suggests that client may restart the txn. e.g. write conflict. -type ErrRetryable string - -func (e ErrRetryable) Error() string { - return fmt.Sprintf("retryable: %s", string(e)) -} - -// ErrRetryable -var ( - ErrLockNotFound = ErrRetryable("lock not found") - ErrAlreadyRollback = ErrRetryable("already rollback") - ErrReplaced = ErrRetryable("replaced by another transaction") -) - -// ErrInvalidOp is returned when an operation cannot be completed. -type ErrInvalidOp struct { - Op kvrpcpb.Op -} - -func (e ErrInvalidOp) Error() string { - return fmt.Sprintf("invalid op: %s", e.Op.String()) -} - -// ErrAlreadyCommitted is returned specially when client tries to rollback a -// committed lock. -type ErrAlreadyCommitted uint64 - -func (ErrAlreadyCommitted) Error() string { - return "txn already committed" -} - -// ErrKeyAlreadyExists is returned when a key already exists. -type ErrKeyAlreadyExists struct { - Key []byte -} - -func (ErrKeyAlreadyExists) Error() string { - return "key already exists" -} - -// ErrDeadlock is returned when deadlock is detected. -type ErrDeadlock struct { - LockKey []byte - LockTS uint64 - DeadlockKeyHash uint64 - WaitChain []*deadlockpb.WaitForEntry -} - -func (ErrDeadlock) Error() string { - return "deadlock" -} - -// ErrConflict is the error when the commit meets an write conflict error. -type ErrConflict struct { - StartTS uint64 - ConflictTS uint64 - ConflictCommitTS uint64 - Key []byte - Reason kvrpcpb.WriteConflict_Reason -} - -func (*ErrConflict) Error() string { - return "write conflict" -} - -// ErrCommitExpire is returned when commit key commitTs smaller than lock.MinCommitTs -type ErrCommitExpire struct { - StartTs uint64 - CommitTs uint64 - MinCommitTs uint64 - Key []byte -} - -func (*ErrCommitExpire) Error() string { - return "commit expired" -} - -// ErrTxnNotFound is returned if the required txn info not found on storage -type ErrTxnNotFound struct { - StartTS uint64 - PrimaryKey []byte -} - -func (*ErrTxnNotFound) Error() string { - return "txn not found" -} - -// ErrAssertionFailed is returned if any assertion fails on a transaction request. -type ErrAssertionFailed struct { - StartTS uint64 - Key []byte - Assertion kvrpcpb.Assertion - ExistingStartTS uint64 - ExistingCommitTS uint64 -} - -func (e *ErrAssertionFailed) Error() string { - return fmt.Sprintf("AssertionFailed { StartTS: %v, Key: %v, Assertion: %v, ExistingStartTS: %v, ExistingCommitTS: %v }", - e.StartTS, hex.EncodeToString(e.Key), e.Assertion.String(), e.ExistingStartTS, e.ExistingCommitTS) -} - -// ErrPrimaryMismatch is returned if CheckTxnStatus request is sent to a secondary lock. -type ErrPrimaryMismatch struct { - Key []byte - Lock *mvcc.Lock -} - -func (e *ErrPrimaryMismatch) Error() string { - return fmt.Sprintf("primary mismatch, key: %v, lock: %v", hex.EncodeToString(e.Key), e.Lock.String()) -} diff --git a/store/mockstore/unistore/tikv/main_test.go b/store/mockstore/unistore/tikv/main_test.go deleted file mode 100644 index 7edf80555f051..0000000000000 --- a/store/mockstore/unistore/tikv/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tikv - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/mockstore/unistore/tikv/mvcc.go b/store/mockstore/unistore/tikv/mvcc.go deleted file mode 100644 index d8f30ade2dcae..0000000000000 --- a/store/mockstore/unistore/tikv/mvcc.go +++ /dev/null @@ -1,1966 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package tikv - -import ( - "bufio" - "bytes" - "cmp" - "context" - "fmt" - "math" - "os" - "slices" - "sort" - "sync/atomic" - "time" - "unsafe" - - "github.com/dgryski/go-farm" - "github.com/pingcap/badger" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/log" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/store/mockstore/unistore/config" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/pd" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/store/mockstore/unistore/util/lockwaiter" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/tikv/client-go/v2/oracle" - "go.uber.org/zap" -) - -// MVCCStore is a wrapper of badger.DB to provide MVCC functions. -type MVCCStore struct { - dir string - db *badger.DB - lockStore *lockstore.MemStore - dbWriter mvcc.DBWriter - safePoint *SafePoint - pdClient pd.Client - closeCh chan bool - - conf *config.Config - - latestTS uint64 - lockWaiterManager *lockwaiter.Manager - DeadlockDetectCli *DetectorClient - DeadlockDetectSvr *DetectorServer -} - -// NewMVCCStore creates a new MVCCStore -func NewMVCCStore(conf *config.Config, bundle *mvcc.DBBundle, dataDir string, safePoint *SafePoint, - writer mvcc.DBWriter, pdClient pd.Client) *MVCCStore { - store := &MVCCStore{ - db: bundle.DB, - dir: dataDir, - lockStore: bundle.LockStore, - safePoint: safePoint, - pdClient: pdClient, - closeCh: make(chan bool), - dbWriter: writer, - conf: conf, - lockWaiterManager: lockwaiter.NewManager(conf), - } - store.DeadlockDetectSvr = NewDetectorServer() - store.DeadlockDetectCli = NewDetectorClient(store.lockWaiterManager, pdClient) - writer.Open() - if pdClient != nil { - // pdClient is nil in unit test. - go store.runUpdateSafePointLoop() - } - return store -} - -func (store *MVCCStore) updateLatestTS(ts uint64) { - for { - old := atomic.LoadUint64(&store.latestTS) - if old < ts { - if !atomic.CompareAndSwapUint64(&store.latestTS, old, ts) { - continue - } - } - return - } -} - -func (store *MVCCStore) getLatestTS() uint64 { - return atomic.LoadUint64(&store.latestTS) -} - -// Close closes the MVCCStore. -func (store *MVCCStore) Close() error { - store.dbWriter.Close() - close(store.closeCh) - - err := store.dumpMemLocks() - if err != nil { - log.Fatal("dump mem locks failed", zap.Error(err)) - } - return nil -} - -type lockEntryHdr struct { - keyLen uint32 - valLen uint32 -} - -func (store *MVCCStore) dumpMemLocks() error { - tmpFileName := store.dir + "/lock_store.tmp" - f, err := os.OpenFile(tmpFileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0600) - if err != nil { - return errors.Trace(err) - } - writer := bufio.NewWriter(f) - cnt := 0 - it := store.lockStore.NewIterator() - hdrBuf := make([]byte, 8) - hdr := (*lockEntryHdr)(unsafe.Pointer(&hdrBuf[0])) - for it.SeekToFirst(); it.Valid(); it.Next() { - hdr.keyLen = uint32(len(it.Key())) - hdr.valLen = uint32(len(it.Value())) - _, err = writer.Write(hdrBuf) - if err != nil { - return errors.Trace(err) - } - _, err = writer.Write(it.Key()) - if err != nil { - return errors.Trace(err) - } - _, err = writer.Write(it.Value()) - if err != nil { - return errors.Trace(err) - } - cnt++ - } - err = writer.Flush() - if err != nil { - return errors.Trace(err) - } - err = f.Sync() - if err != nil { - return errors.Trace(err) - } - err = f.Close() - if err != nil { - return errors.Trace(err) - } - return os.Rename(tmpFileName, store.dir+"/lock_store") -} - -func (store *MVCCStore) getDBItems(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation) (items []*badger.Item, err error) { - txn := reqCtx.getDBReader().GetTxn() - keys := make([][]byte, len(mutations)) - for i, m := range mutations { - keys[i] = m.Key - } - return txn.MultiGet(keys) -} - -func sortMutations(mutations []*kvrpcpb.Mutation) []*kvrpcpb.Mutation { - fn := func(i, j *kvrpcpb.Mutation) int { - return bytes.Compare(i.Key, j.Key) - } - if slices.IsSortedFunc(mutations, fn) { - return mutations - } - slices.SortFunc(mutations, fn) - return mutations -} - -func sortPrewrite(req *kvrpcpb.PrewriteRequest) []*kvrpcpb.Mutation { - if len(req.PessimisticActions) == 0 { - return sortMutations(req.Mutations) - } - sorter := pessimisticPrewriteSorter{PrewriteRequest: req} - if sort.IsSorted(sorter) { - return req.Mutations - } - sort.Sort(sorter) - return req.Mutations -} - -type pessimisticPrewriteSorter struct { - *kvrpcpb.PrewriteRequest -} - -func (sorter pessimisticPrewriteSorter) Less(i, j int) bool { - return bytes.Compare(sorter.Mutations[i].Key, sorter.Mutations[j].Key) < 0 -} - -func (sorter pessimisticPrewriteSorter) Len() int { - return len(sorter.Mutations) -} - -func (sorter pessimisticPrewriteSorter) Swap(i, j int) { - sorter.Mutations[i], sorter.Mutations[j] = sorter.Mutations[j], sorter.Mutations[i] - sorter.PessimisticActions[i], sorter.PessimisticActions[j] = sorter.PessimisticActions[j], sorter.PessimisticActions[i] -} - -func sortKeys(keys [][]byte) [][]byte { - if slices.IsSortedFunc(keys, bytes.Compare) { - return keys - } - slices.SortFunc(keys, bytes.Compare) - return keys -} - -// PessimisticLock will add pessimistic lock on key -func (store *MVCCStore) PessimisticLock(reqCtx *requestCtx, req *kvrpcpb.PessimisticLockRequest, resp *kvrpcpb.PessimisticLockResponse) (*lockwaiter.Waiter, error) { - waiter, err := store.pessimisticLockInner(reqCtx, req, resp) - if err != nil && req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { - // The execution of `pessimisticLockInner` is broken by error. If resp.Results is not completely set yet, fill it with LockResultFailed. - for len(resp.Results) < len(req.Mutations) { - resp.Results = append(resp.Results, &kvrpcpb.PessimisticLockKeyResult{ - Type: kvrpcpb.PessimisticLockKeyResultType_LockResultFailed, - }) - } - } - - return waiter, err -} - -func (store *MVCCStore) pessimisticLockInner(reqCtx *requestCtx, req *kvrpcpb.PessimisticLockRequest, resp *kvrpcpb.PessimisticLockResponse) (*lockwaiter.Waiter, error) { - mutations := req.Mutations - if !req.ReturnValues { - mutations = sortMutations(req.Mutations) - } - startTS := req.StartVersion - regCtx := reqCtx.regCtx - hashVals := mutationsToHashVals(mutations) - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - if req.LockOnlyIfExists && !req.ReturnValues { - return nil, errors.New("LockOnlyIfExists is set for LockKeys but ReturnValues is not set") - } - if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock && len(req.Mutations) > 1 { - return nil, errors.New("Trying to lock more than one key in WakeUpModeForceLock, which is not supported yet") - } - batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) - var dup bool - for _, m := range mutations { - lock, err := store.checkConflictInLockStore(reqCtx, m, startTS) - if err != nil { - var resourceGroupTag []byte - if req.Context != nil { - resourceGroupTag = req.Context.ResourceGroupTag - } - return store.handleCheckPessimisticErr(startTS, err, req.IsFirstLock, req.WaitTimeout, m.Key, resourceGroupTag) - } - if lock != nil { - if lock.Op != uint8(kvrpcpb.Op_PessimisticLock) { - return nil, errors.New("lock type not match") - } - if lock.ForUpdateTS >= req.ForUpdateTs { - // It's a duplicate command, we can simply return values. - dup = true - break - } - // Single statement rollback key, we can overwrite it. - } - if bytes.Equal(m.Key, req.PrimaryLock) { - txnStatus := store.checkExtraTxnStatus(reqCtx, m.Key, startTS) - if txnStatus.isRollback { - return nil, kverrors.ErrAlreadyRollback - } else if txnStatus.isOpLockCommitted() { - dup = true - break - } - } - } - items, err := store.getDBItems(reqCtx, mutations) - lockedWithConflictTSList := make([]uint64, 0, len(mutations)) - if err != nil { - return nil, err - } - if !dup { - for i, m := range mutations { - lock, lockedWithConflictTS, err1 := store.buildPessimisticLock(m, items[i], req) - lockedWithConflictTSList = append(lockedWithConflictTSList, lockedWithConflictTS) - if err1 != nil { - return nil, err1 - } - if lock == nil { - continue - } - batch.PessimisticLock(m.Key, lock) - } - err = store.dbWriter.Write(batch) - if err != nil { - return nil, err - } - } - if req.Force { - dbMeta := mvcc.DBUserMeta(items[0].UserMeta()) - val, err1 := items[0].ValueCopy(nil) - if err1 != nil { - return nil, err1 - } - resp.Value = val - resp.CommitTs = dbMeta.CommitTS() - } - - if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeNormal { - if req.ReturnValues || req.CheckExistence { - for _, item := range items { - if item == nil { - if req.ReturnValues { - resp.Values = append(resp.Values, nil) - } - resp.NotFounds = append(resp.NotFounds, true) - continue - } - val, err1 := item.ValueCopy(nil) - if err1 != nil { - return nil, err1 - } - if req.ReturnValues { - resp.Values = append(resp.Values, val) - } - resp.NotFounds = append(resp.NotFounds, len(val) == 0) - } - } - } else if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { - for i, item := range items { - res := &kvrpcpb.PessimisticLockKeyResult{ - Type: kvrpcpb.PessimisticLockKeyResultType_LockResultNormal, - Value: nil, - Existence: false, - LockedWithConflictTs: 0, - } - - if lockedWithConflictTSList[i] != 0 { - res.Type = kvrpcpb.PessimisticLockKeyResultType_LockResultLockedWithConflict - res.LockedWithConflictTs = lockedWithConflictTSList[i] - if item == nil { - res.Value = nil - res.Existence = false - } else { - val, err1 := item.ValueCopy(nil) - if err1 != nil { - return nil, err1 - } - res.Value = val - res.Existence = len(val) != 0 - } - } else if req.ReturnValues { - if item != nil { - val, err1 := item.ValueCopy(nil) - if err1 != nil { - return nil, err1 - } - res.Value = val - res.Existence = len(val) != 0 - } - } else if req.CheckExistence { - if item != nil { - val, err1 := item.ValueCopy(nil) - if err1 != nil { - return nil, err1 - } - res.Existence = len(val) != 0 - } - } - - resp.Results = append(resp.Results, res) - } - } else { - panic("unreachable") - } - return nil, err -} - -// extraTxnStatus can be rollback or Op_Lock that only contains transaction status info, no values. -type extraTxnStatus struct { - commitTS uint64 - isRollback bool -} - -func (s extraTxnStatus) isOpLockCommitted() bool { - return s.commitTS > 0 -} - -func (store *MVCCStore) checkExtraTxnStatus(reqCtx *requestCtx, key []byte, startTS uint64) extraTxnStatus { - txn := reqCtx.getDBReader().GetTxn() - txnStatusKey := mvcc.EncodeExtraTxnStatusKey(key, startTS) - item, err := txn.Get(txnStatusKey) - if err != nil { - return extraTxnStatus{} - } - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if userMeta.CommitTS() == 0 { - return extraTxnStatus{isRollback: true} - } - return extraTxnStatus{commitTS: userMeta.CommitTS()} -} - -// PessimisticRollback implements the MVCCStore interface. -func (store *MVCCStore) PessimisticRollback(reqCtx *requestCtx, req *kvrpcpb.PessimisticRollbackRequest) error { - keys := sortKeys(req.Keys) - hashVals := keysToHashVals(keys...) - regCtx := reqCtx.regCtx - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - startTS := req.StartVersion - var batch mvcc.WriteBatch - for _, k := range keys { - lock := store.getLock(reqCtx, k) - if lock != nil && - lock.Op == uint8(kvrpcpb.Op_PessimisticLock) && - lock.StartTS == startTS && - lock.ForUpdateTS <= req.ForUpdateTs { - if batch == nil { - batch = store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) - } - batch.PessimisticRollback(k) - } - } - var err error - if batch != nil { - err = store.dbWriter.Write(batch) - } - store.lockWaiterManager.WakeUp(startTS, 0, hashVals) - store.DeadlockDetectCli.CleanUp(startTS) - return err -} - -// TxnHeartBeat implements the MVCCStore interface. -func (store *MVCCStore) TxnHeartBeat(reqCtx *requestCtx, req *kvrpcpb.TxnHeartBeatRequest) (lockTTL uint64, err error) { - hashVals := keysToHashVals(req.PrimaryLock) - regCtx := reqCtx.regCtx - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - lock := store.getLock(reqCtx, req.PrimaryLock) - if lock != nil && lock.StartTS == req.StartVersion { - if !bytes.Equal(lock.Primary, req.PrimaryLock) { - return 0, errors.New("heartbeat on non-primary key") - } - if lock.TTL < uint32(req.AdviseLockTtl) { - lock.TTL = uint32(req.AdviseLockTtl) - batch := store.dbWriter.NewWriteBatch(req.StartVersion, 0, reqCtx.rpcCtx) - batch.PessimisticLock(req.PrimaryLock, lock) - err = store.dbWriter.Write(batch) - if err != nil { - return 0, err - } - } - return uint64(lock.TTL), nil - } - return 0, errors.New("lock doesn't exists") -} - -// TxnStatus is the result of `CheckTxnStatus` API. -type TxnStatus struct { - commitTS uint64 - action kvrpcpb.Action - lockInfo *kvrpcpb.LockInfo -} - -// CheckTxnStatus implements the MVCCStore interface. -func (store *MVCCStore) CheckTxnStatus(reqCtx *requestCtx, - req *kvrpcpb.CheckTxnStatusRequest) (txnStatusRes TxnStatus, err error) { - hashVals := keysToHashVals(req.PrimaryKey) - regCtx := reqCtx.regCtx - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - lock := store.getLock(reqCtx, req.PrimaryKey) - batch := store.dbWriter.NewWriteBatch(req.LockTs, 0, reqCtx.rpcCtx) - if lock != nil && lock.StartTS == req.LockTs { - if !bytes.Equal(req.PrimaryKey, lock.Primary) { - return TxnStatus{}, &kverrors.ErrPrimaryMismatch{ - Key: req.PrimaryKey, - Lock: lock, - } - } - - // For an async-commit lock, never roll it back or push forward it MinCommitTS. - if lock.UseAsyncCommit && !req.ForceSyncCommit { - log.S().Debugf("async commit startTS=%v secondaries=%v minCommitTS=%v", lock.StartTS, lock.Secondaries, lock.MinCommitTS) - return TxnStatus{0, kvrpcpb.Action_NoAction, lock.ToLockInfo(req.PrimaryKey)}, nil - } - - // If the lock has already outdated, clean up it. - if uint64(oracle.ExtractPhysical(lock.StartTS))+uint64(lock.TTL) < uint64(oracle.ExtractPhysical(req.CurrentTs)) { - // If the resolving lock and primary lock are both pessimistic type, just pessimistic rollback locks. - if req.ResolvingPessimisticLock && lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { - batch.PessimisticRollback(req.PrimaryKey) - return TxnStatus{0, kvrpcpb.Action_TTLExpirePessimisticRollback, nil}, store.dbWriter.Write(batch) - } - batch.Rollback(req.PrimaryKey, true) - return TxnStatus{0, kvrpcpb.Action_TTLExpireRollback, nil}, store.dbWriter.Write(batch) - } - // If this is a large transaction and the lock is active, push forward the minCommitTS. - // lock.minCommitTS == 0 may be a secondary lock, or not a large transaction. - // For async commit protocol, the minCommitTS is always greater than zero, but async commit will not be a large transaction. - action := kvrpcpb.Action_NoAction - if req.CallerStartTs == maxSystemTS { - action = kvrpcpb.Action_MinCommitTSPushed - } else if lock.MinCommitTS > 0 && !lock.UseAsyncCommit { - action = kvrpcpb.Action_MinCommitTSPushed - // We *must* guarantee the invariance lock.minCommitTS >= callerStartTS + 1 - if lock.MinCommitTS < req.CallerStartTs+1 { - lock.MinCommitTS = req.CallerStartTs + 1 - - // Remove this condition should not affect correctness. - // We do it because pushing forward minCommitTS as far as possible could avoid - // the lock been pushed again several times, and thus reduce write operations. - if lock.MinCommitTS < req.CurrentTs { - lock.MinCommitTS = req.CurrentTs - } - batch.PessimisticLock(req.PrimaryKey, lock) - if err = store.dbWriter.Write(batch); err != nil { - return TxnStatus{0, action, nil}, err - } - } - } - return TxnStatus{0, action, lock.ToLockInfo(req.PrimaryKey)}, nil - } - - // The current transaction lock not exists, check the transaction commit info - commitTS, err := store.checkCommitted(reqCtx.getDBReader(), req.PrimaryKey, req.LockTs) - if commitTS > 0 { - return TxnStatus{commitTS, kvrpcpb.Action_NoAction, nil}, nil - } - // Check if the transaction already rollbacked - status := store.checkExtraTxnStatus(reqCtx, req.PrimaryKey, req.LockTs) - if status.isRollback { - return TxnStatus{0, kvrpcpb.Action_NoAction, nil}, nil - } - if status.isOpLockCommitted() { - commitTS = status.commitTS - return TxnStatus{commitTS, kvrpcpb.Action_NoAction, nil}, nil - } - // If current transaction is not prewritted before, it may be pessimistic lock. - // When pessimistic txn rollback statement, it may not leave a 'rollbacked' tombstone. - // Or maybe caused by concurrent prewrite operation. - // Especially in the non-block reading case, the secondary lock is likely to be - // written before the primary lock. - // Currently client will always set this flag to true when resolving locks - if req.RollbackIfNotExist { - if req.ResolvingPessimisticLock { - return TxnStatus{0, kvrpcpb.Action_LockNotExistDoNothing, nil}, nil - } - batch.Rollback(req.PrimaryKey, false) - err = store.dbWriter.Write(batch) - return TxnStatus{0, kvrpcpb.Action_LockNotExistRollback, nil}, nil - } - return TxnStatus{0, kvrpcpb.Action_NoAction, nil}, &kverrors.ErrTxnNotFound{ - PrimaryKey: req.PrimaryKey, - StartTS: req.LockTs, - } -} - -// SecondaryLocksStatus is the result of `CheckSecondaryLocksStatus` API. -type SecondaryLocksStatus struct { - locks []*kvrpcpb.LockInfo - commitTS uint64 -} - -// CheckSecondaryLocks implements the MVCCStore interface. -func (store *MVCCStore) CheckSecondaryLocks(reqCtx *requestCtx, keys [][]byte, startTS uint64) (SecondaryLocksStatus, error) { - sortKeys(keys) - hashVals := keysToHashVals(keys...) - log.S().Debugf("%d check secondary %v", startTS, hashVals) - regCtx := reqCtx.regCtx - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) - locks := make([]*kvrpcpb.LockInfo, 0, len(keys)) - for i, key := range keys { - lock := store.getLock(reqCtx, key) - if !(lock != nil && lock.StartTS == startTS) { - commitTS, err := store.checkCommitted(reqCtx.getDBReader(), key, startTS) - if err != nil { - return SecondaryLocksStatus{}, err - } - if commitTS > 0 { - return SecondaryLocksStatus{commitTS: commitTS}, nil - } - status := store.checkExtraTxnStatus(reqCtx, key, startTS) - if status.isOpLockCommitted() { - return SecondaryLocksStatus{commitTS: status.commitTS}, nil - } - if !status.isRollback { - batch.Rollback(key, false) - err = store.dbWriter.Write(batch) - } - return SecondaryLocksStatus{commitTS: 0}, err - } - if lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { - batch.Rollback(key, true) - err := store.dbWriter.Write(batch) - if err != nil { - return SecondaryLocksStatus{}, err - } - store.lockWaiterManager.WakeUp(startTS, 0, []uint64{hashVals[i]}) - store.DeadlockDetectCli.CleanUp(startTS) - return SecondaryLocksStatus{commitTS: 0}, nil - } - locks = append(locks, lock.ToLockInfo(key)) - } - return SecondaryLocksStatus{locks: locks}, nil -} - -func (store *MVCCStore) normalizeWaitTime(lockWaitTime int64) time.Duration { - if lockWaitTime > store.conf.PessimisticTxn.WaitForLockTimeout { - lockWaitTime = store.conf.PessimisticTxn.WaitForLockTimeout - } else if lockWaitTime == 0 { - lockWaitTime = store.conf.PessimisticTxn.WaitForLockTimeout - } - return time.Duration(lockWaitTime) * time.Millisecond -} - -func (store *MVCCStore) handleCheckPessimisticErr(startTS uint64, err error, isFirstLock bool, lockWaitTime int64, key []byte, resourceGroupTag []byte) (*lockwaiter.Waiter, error) { - if locked, ok := err.(*kverrors.ErrLocked); ok { - if lockWaitTime != lockwaiter.LockNoWait { - keyHash := farm.Fingerprint64(locked.Key) - waitTimeDuration := store.normalizeWaitTime(lockWaitTime) - lock := locked.Lock - log.S().Debugf("%d blocked by %d on key %d", startTS, lock.StartTS, keyHash) - waiter := store.lockWaiterManager.NewWaiter(startTS, lock.StartTS, keyHash, waitTimeDuration) - if !isFirstLock { - store.DeadlockDetectCli.Detect(startTS, lock.StartTS, keyHash, key, resourceGroupTag) - } - return waiter, err - } - } - return nil, err -} - -// buildPessimisticLock builds the lock according to the request and the current state of the key. -// Returns the built lock, and the LockedWithConflictTS (if any, otherwise 0). -func (store *MVCCStore) buildPessimisticLock(m *kvrpcpb.Mutation, item *badger.Item, - req *kvrpcpb.PessimisticLockRequest) (*mvcc.Lock, uint64, error) { - var lockedWithConflictTS uint64 = 0 - - var writeConflictError error - - if item != nil { - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if !req.Force { - if userMeta.CommitTS() > req.ForUpdateTs { - writeConflictError = &kverrors.ErrConflict{ - StartTS: req.StartVersion, - ConflictTS: userMeta.StartTS(), - ConflictCommitTS: userMeta.CommitTS(), - Key: item.KeyCopy(nil), - Reason: kvrpcpb.WriteConflict_PessimisticRetry, - } - - if req.GetWakeUpMode() == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeNormal { - return nil, 0, writeConflictError - } else if req.GetWakeUpMode() != kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { - panic("unreachable") - } - lockedWithConflictTS = userMeta.CommitTS() - } - } - if m.Assertion == kvrpcpb.Assertion_NotExist && !item.IsEmpty() { - if lockedWithConflictTS != 0 { - // If constraint is violated, disable locking with conflict behavior. - if writeConflictError == nil { - panic("unreachable") - } - return nil, 0, writeConflictError - } - return nil, 0, &kverrors.ErrKeyAlreadyExists{Key: m.Key} - } - } - - if ok, err := doesNeedLock(item, req); !ok { - if err != nil { - return nil, 0, err - } - if lockedWithConflictTS > 0 { - // If lockIfOnlyExist is used on a not-existing key, disable locking with conflict behavior. - if writeConflictError == nil { - panic("unreachable") - } - return nil, 0, writeConflictError - } - return nil, 0, nil - } - actualWrittenForUpdateTS := req.ForUpdateTs - if lockedWithConflictTS > 0 { - actualWrittenForUpdateTS = lockedWithConflictTS - } - lock := &mvcc.Lock{ - LockHdr: mvcc.LockHdr{ - StartTS: req.StartVersion, - ForUpdateTS: actualWrittenForUpdateTS, - Op: uint8(kvrpcpb.Op_PessimisticLock), - TTL: uint32(req.LockTtl), - PrimaryLen: uint16(len(req.PrimaryLock)), - }, - Primary: req.PrimaryLock, - } - return lock, lockedWithConflictTS, nil -} - -// Prewrite implements the MVCCStore interface. -func (store *MVCCStore) Prewrite(reqCtx *requestCtx, req *kvrpcpb.PrewriteRequest) error { - mutations := sortPrewrite(req) - regCtx := reqCtx.regCtx - hashVals := mutationsToHashVals(mutations) - - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - isPessimistic := req.ForUpdateTs > 0 - var err error - if isPessimistic { - err = store.prewritePessimistic(reqCtx, mutations, req) - } else { - err = store.prewriteOptimistic(reqCtx, mutations, req) - } - if err != nil { - return err - } - - if reqCtx.onePCCommitTS != 0 { - // TODO: Is it correct to pass the hashVals directly here, considering that some of the keys may - // have no pessimistic lock? - if isPessimistic { - store.lockWaiterManager.WakeUp(req.StartVersion, reqCtx.onePCCommitTS, hashVals) - store.DeadlockDetectCli.CleanUp(req.StartVersion) - } - } - return nil -} - -func (store *MVCCStore) prewriteOptimistic(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, req *kvrpcpb.PrewriteRequest) error { - startTS := req.StartVersion - // Must check the LockStore first. - for _, m := range mutations { - lock, err := store.checkConflictInLockStore(reqCtx, m, startTS) - if err != nil { - return err - } - if lock != nil { - // duplicated command - return nil - } - if bytes.Equal(m.Key, req.PrimaryLock) { - status := store.checkExtraTxnStatus(reqCtx, m.Key, req.StartVersion) - if status.isRollback { - return kverrors.ErrAlreadyRollback - } - if status.isOpLockCommitted() { - // duplicated command - return nil - } - } - } - items, err := store.getDBItems(reqCtx, mutations) - if err != nil { - return err - } - for i, m := range mutations { - item := items[i] - if item != nil { - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if userMeta.CommitTS() > startTS { - return &kverrors.ErrConflict{ - StartTS: startTS, - ConflictTS: userMeta.StartTS(), - ConflictCommitTS: userMeta.CommitTS(), - Key: item.KeyCopy(nil), - Reason: kvrpcpb.WriteConflict_Optimistic, - } - } - } - // Op_CheckNotExists type requests should not add lock - if m.Op == kvrpcpb.Op_CheckNotExists { - if item != nil { - val, err := item.Value() - if err != nil { - return err - } - if len(val) > 0 { - return &kverrors.ErrKeyAlreadyExists{Key: m.Key} - } - } - continue - } - // TODO add memory lock for async commit protocol. - } - return store.prewriteMutations(reqCtx, mutations, req, items) -} - -func (store *MVCCStore) prewritePessimistic(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, req *kvrpcpb.PrewriteRequest) error { - startTS := req.StartVersion - - expectedForUpdateTSMap := make(map[int]uint64, len(req.GetForUpdateTsConstraints())) - for _, constraint := range req.GetForUpdateTsConstraints() { - index := int(constraint.Index) - if index >= len(mutations) { - return errors.Errorf("prewrite request invalid: for_update_ts constraint set for index %v while %v mutations were given", index, len(mutations)) - } - - expectedForUpdateTSMap[index] = constraint.ExpectedForUpdateTs - } - - reader := reqCtx.getDBReader() - txn := reader.GetTxn() - - for i, m := range mutations { - if m.Op == kvrpcpb.Op_CheckNotExists { - return kverrors.ErrInvalidOp{Op: m.Op} - } - lock := store.getLock(reqCtx, m.Key) - isPessimisticLock := len(req.PessimisticActions) > 0 && req.PessimisticActions[i] == kvrpcpb.PrewriteRequest_DO_PESSIMISTIC_CHECK - needConstraintCheck := len(req.PessimisticActions) > 0 && req.PessimisticActions[i] == kvrpcpb.PrewriteRequest_DO_CONSTRAINT_CHECK - lockExists := lock != nil - lockMatch := lockExists && lock.StartTS == startTS - lockConstraintPasses := true - if expectedForUpdateTS, ok := expectedForUpdateTSMap[i]; ok { - if lock.ForUpdateTS != expectedForUpdateTS { - lockConstraintPasses = false - } - } - if isPessimisticLock { - valid := lockExists && lockMatch && lockConstraintPasses - if !valid { - return errors.New("pessimistic lock not found") - } - if lock.Op != uint8(kvrpcpb.Op_PessimisticLock) { - // Duplicated command. - return nil - } - // Do not overwrite lock ttl if prewrite ttl smaller than pessimisitc lock ttl - if uint64(lock.TTL) > req.LockTtl { - req.LockTtl = uint64(lock.TTL) - } - } else if needConstraintCheck { - item, err := txn.Get(m.Key) - if err != nil && err != badger.ErrKeyNotFound { - return errors.Trace(err) - } - // check conflict - if item != nil { - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if userMeta.CommitTS() > startTS { - return &kverrors.ErrConflict{ - StartTS: startTS, - ConflictTS: userMeta.StartTS(), - ConflictCommitTS: userMeta.CommitTS(), - Key: item.KeyCopy(nil), - Reason: kvrpcpb.WriteConflict_LazyUniquenessCheck, - } - } - } - } else { - // non pessimistic lock in pessimistic transaction, e.g. non-unique index. - valid := !lockExists || lockMatch - if !valid { - // Safe to set TTL to zero because the transaction of the lock is committed - // or rollbacked or must be rollbacked. - lock.TTL = 0 - return kverrors.BuildLockErr(m.Key, lock) - } - if lockMatch { - // Duplicate command. - return nil - } - } - // TODO add memory lock for async commit protocol. - } - items, err := store.getDBItems(reqCtx, mutations) - if err != nil { - return err - } - return store.prewriteMutations(reqCtx, mutations, req, items) -} - -func (store *MVCCStore) prewriteMutations(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, - req *kvrpcpb.PrewriteRequest, items []*badger.Item) error { - var minCommitTS uint64 - if req.UseAsyncCommit || req.TryOnePc { - // Get minCommitTS for async commit protocol. After all keys are locked in memory lock. - physical, logical, tsErr := store.pdClient.GetTS(context.Background()) - if tsErr != nil { - return tsErr - } - minCommitTS = uint64(physical)<<18 + uint64(logical) - if req.MaxCommitTs > 0 && minCommitTS > req.MaxCommitTs { - req.UseAsyncCommit = false - req.TryOnePc = false - } - if req.UseAsyncCommit { - reqCtx.asyncMinCommitTS = minCommitTS - } - } - - if req.UseAsyncCommit && minCommitTS > req.MinCommitTs { - req.MinCommitTs = minCommitTS - } - - if req.TryOnePc { - committed, err := store.tryOnePC(reqCtx, mutations, req, items, minCommitTS, req.MaxCommitTs) - if err != nil { - return err - } - // If 1PC succeeded, exit immediately. - if committed { - return nil - } - } - - batch := store.dbWriter.NewWriteBatch(req.StartVersion, 0, reqCtx.rpcCtx) - - for i, m := range mutations { - if m.Op == kvrpcpb.Op_CheckNotExists { - continue - } - lock, err1 := store.buildPrewriteLock(reqCtx, m, items[i], req) - if err1 != nil { - return err1 - } - batch.Prewrite(m.Key, lock) - } - - return store.dbWriter.Write(batch) -} - -func (store *MVCCStore) tryOnePC(reqCtx *requestCtx, mutations []*kvrpcpb.Mutation, - req *kvrpcpb.PrewriteRequest, items []*badger.Item, minCommitTS uint64, maxCommitTS uint64) (bool, error) { - if maxCommitTS != 0 && minCommitTS > maxCommitTS { - log.Debug("1pc transaction fallbacks due to minCommitTS exceeds maxCommitTS", - zap.Uint64("startTS", req.StartVersion), - zap.Uint64("minCommitTS", minCommitTS), - zap.Uint64("maxCommitTS", maxCommitTS)) - return false, nil - } - if minCommitTS < req.StartVersion { - log.Fatal("1pc commitTS less than startTS", zap.Uint64("startTS", req.StartVersion), zap.Uint64("minCommitTS", minCommitTS)) - } - - reqCtx.onePCCommitTS = minCommitTS - store.updateLatestTS(minCommitTS) - batch := store.dbWriter.NewWriteBatch(req.StartVersion, minCommitTS, reqCtx.rpcCtx) - - for i, m := range mutations { - if m.Op == kvrpcpb.Op_CheckNotExists { - continue - } - lock, err1 := store.buildPrewriteLock(reqCtx, m, items[i], req) - if err1 != nil { - return false, err1 - } - // batch.Commit will panic if the key is not locked. So there need to be a special function - // for it to commit without deleting lock. - batch.Commit(m.Key, lock) - } - - if err := store.dbWriter.Write(batch); err != nil { - return false, err - } - - return true, nil -} - -func encodeFromOldRow(oldRow, buf []byte) ([]byte, error) { - var ( - colIDs []int64 - datums []types.Datum - ) - for len(oldRow) > 1 { - var d types.Datum - var err error - oldRow, d, err = codec.DecodeOne(oldRow) - if err != nil { - return nil, err - } - colID := d.GetInt64() - oldRow, d, err = codec.DecodeOne(oldRow) - if err != nil { - return nil, err - } - colIDs = append(colIDs, colID) - datums = append(datums, d) - } - var encoder rowcodec.Encoder - return encoder.Encode(stmtctx.NewStmtCtx(), colIDs, datums, buf) -} - -func (store *MVCCStore) buildPrewriteLock(reqCtx *requestCtx, m *kvrpcpb.Mutation, item *badger.Item, - req *kvrpcpb.PrewriteRequest) (*mvcc.Lock, error) { - lock := &mvcc.Lock{ - LockHdr: mvcc.LockHdr{ - StartTS: req.StartVersion, - TTL: uint32(req.LockTtl), - PrimaryLen: uint16(len(req.PrimaryLock)), - MinCommitTS: req.MinCommitTs, - UseAsyncCommit: req.UseAsyncCommit, - SecondaryNum: uint32(len(req.Secondaries)), - }, - Primary: req.PrimaryLock, - Value: m.Value, - Secondaries: req.Secondaries, - } - // Note that this is not fully consistent with TiKV. TiKV doesn't always get the value from Write CF. In - // AssertionLevel_Fast, TiKV skips checking assertion if Write CF is not read, in order not to harm the performance. - // However, unistore can always check it. It's better not to assume the store's behavior about assertion when the - // mode is set to AssertionLevel_Fast. - if req.AssertionLevel != kvrpcpb.AssertionLevel_Off { - if item == nil || item.IsEmpty() { - if m.Assertion == kvrpcpb.Assertion_Exist { - log.Error("ASSERTION FAIL!!! non-exist for must exist key", zap.Stringer("mutation", m)) - return nil, &kverrors.ErrAssertionFailed{ - StartTS: req.StartVersion, - Key: m.Key, - Assertion: m.Assertion, - ExistingStartTS: 0, - ExistingCommitTS: 0, - } - } - } else { - if m.Assertion == kvrpcpb.Assertion_NotExist { - log.Error("ASSERTION FAIL!!! exist for must non-exist key", zap.Stringer("mutation", m)) - userMeta := mvcc.DBUserMeta(item.UserMeta()) - return nil, &kverrors.ErrAssertionFailed{ - StartTS: req.StartVersion, - Key: m.Key, - Assertion: m.Assertion, - ExistingStartTS: userMeta.StartTS(), - ExistingCommitTS: userMeta.CommitTS(), - } - } - } - } - var err error - lock.Op = uint8(m.Op) - if lock.Op == uint8(kvrpcpb.Op_Insert) { - if item != nil && item.ValueSize() > 0 { - return nil, &kverrors.ErrKeyAlreadyExists{Key: m.Key} - } - lock.Op = uint8(kvrpcpb.Op_Put) - } - if rowcodec.IsRowKey(m.Key) && lock.Op == uint8(kvrpcpb.Op_Put) { - if !rowcodec.IsNewFormat(m.Value) { - reqCtx.buf, err = encodeFromOldRow(m.Value, reqCtx.buf) - if err != nil { - log.Error("encode data failed", zap.Binary("value", m.Value), zap.Binary("key", m.Key), zap.Stringer("op", m.Op), zap.Error(err)) - return nil, err - } - - lock.Value = make([]byte, len(reqCtx.buf)) - copy(lock.Value, reqCtx.buf) - } - } - - lock.ForUpdateTS = req.ForUpdateTs - return lock, nil -} - -func (store *MVCCStore) getLock(req *requestCtx, key []byte) *mvcc.Lock { - req.buf = store.lockStore.Get(key, req.buf) - if len(req.buf) == 0 { - return nil - } - lock := mvcc.DecodeLock(req.buf) - return &lock -} - -func (store *MVCCStore) checkConflictInLockStore( - req *requestCtx, mutation *kvrpcpb.Mutation, startTS uint64) (*mvcc.Lock, error) { - req.buf = store.lockStore.Get(mutation.Key, req.buf) - if len(req.buf) == 0 { - return nil, nil - } - lock := mvcc.DecodeLock(req.buf) - if lock.StartTS == startTS { - // Same ts, no need to overwrite. - return &lock, nil - } - return nil, kverrors.BuildLockErr(mutation.Key, &lock) -} - -const maxSystemTS uint64 = math.MaxUint64 - -// Commit implements the MVCCStore interface. -func (store *MVCCStore) Commit(req *requestCtx, keys [][]byte, startTS, commitTS uint64) error { - sortKeys(keys) - store.updateLatestTS(commitTS) - regCtx := req.regCtx - hashVals := keysToHashVals(keys...) - batch := store.dbWriter.NewWriteBatch(startTS, commitTS, req.rpcCtx) - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - var buf []byte - var tmpDiff int - var isPessimisticTxn bool - for _, key := range keys { - var lockErr error - var checkErr error - var lock mvcc.Lock - buf = store.lockStore.Get(key, buf) - if len(buf) == 0 { - // We never commit partial keys in Commit request, so if one lock is not found, - // the others keys must not be found too. - lockErr = kverrors.ErrLockNotFound - } else { - lock = mvcc.DecodeLock(buf) - if lock.StartTS != startTS { - lockErr = kverrors.ErrReplaced - } - } - if lockErr != nil { - // Maybe the secondary keys committed by other concurrent transactions using lock resolver, - // check commit info from store - checkErr = store.handleLockNotFound(req, key, startTS, commitTS) - if checkErr == nil { - continue - } - log.Error("commit failed, no correspond lock found", - zap.Binary("key", key), zap.Uint64("start ts", startTS), zap.String("lock", fmt.Sprintf("%v", lock)), zap.Error(lockErr)) - return lockErr - } - if commitTS < lock.MinCommitTS { - log.Info("trying to commit with smaller commitTs than minCommitTs", - zap.Uint64("commit ts", commitTS), zap.Uint64("min commit ts", lock.MinCommitTS), zap.Binary("key", key)) - return &kverrors.ErrCommitExpire{ - StartTs: startTS, - CommitTs: commitTS, - MinCommitTs: lock.MinCommitTS, - Key: key, - } - } - isPessimisticTxn = lock.ForUpdateTS > 0 - tmpDiff += len(key) + len(lock.Value) - batch.Commit(key, &lock) - } - atomic.AddInt64(regCtx.Diff(), int64(tmpDiff)) - err := store.dbWriter.Write(batch) - store.lockWaiterManager.WakeUp(startTS, commitTS, hashVals) - if isPessimisticTxn { - store.DeadlockDetectCli.CleanUp(startTS) - } - return err -} - -func (store *MVCCStore) handleLockNotFound(reqCtx *requestCtx, key []byte, startTS, commitTS uint64) error { - txn := reqCtx.getDBReader().GetTxn() - txn.SetReadTS(commitTS) - item, err := txn.Get(key) - if err != nil && err != badger.ErrKeyNotFound { - return errors.Trace(err) - } - if item == nil { - return kverrors.ErrLockNotFound - } - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if userMeta.StartTS() == startTS { - // Already committed. - return nil - } - return kverrors.ErrLockNotFound -} - -const ( - rollbackStatusDone = 0 - rollbackStatusNoLock = 1 - rollbackStatusNewLock = 2 - rollbackPessimistic = 3 - rollbackStatusLocked = 4 -) - -// Rollback implements the MVCCStore interface. -func (store *MVCCStore) Rollback(reqCtx *requestCtx, keys [][]byte, startTS uint64) error { - sortKeys(keys) - hashVals := keysToHashVals(keys...) - log.S().Debugf("%d rollback %v", startTS, hashVals) - regCtx := reqCtx.regCtx - batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) - - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - statuses := make([]int, len(keys)) - for i, key := range keys { - var rollbackErr error - statuses[i], rollbackErr = store.rollbackKeyReadLock(reqCtx, batch, key, startTS, 0) - if rollbackErr != nil { - return errors.Trace(rollbackErr) - } - } - for i, key := range keys { - status := statuses[i] - if status == rollbackStatusDone || status == rollbackPessimistic { - // rollback pessimistic lock doesn't need to read db. - continue - } - err := store.rollbackKeyReadDB(reqCtx, batch, key, startTS, statuses[i] == rollbackStatusNewLock) - if err != nil { - return err - } - } - store.DeadlockDetectCli.CleanUp(startTS) - err := store.dbWriter.Write(batch) - return errors.Trace(err) -} - -func (store *MVCCStore) rollbackKeyReadLock(reqCtx *requestCtx, batch mvcc.WriteBatch, key []byte, - startTS, currentTs uint64) (int, error) { - reqCtx.buf = store.lockStore.Get(key, reqCtx.buf) - hasLock := len(reqCtx.buf) > 0 - if hasLock { - lock := mvcc.DecodeLock(reqCtx.buf) - if lock.StartTS < startTS { - // The lock is old, means this is written by an old transaction, and the current transaction may not arrive. - // We should write a rollback lock. - batch.Rollback(key, false) - return rollbackStatusDone, nil - } - if lock.StartTS == startTS { - if currentTs > 0 && uint64(oracle.ExtractPhysical(lock.StartTS))+uint64(lock.TTL) >= uint64(oracle.ExtractPhysical(currentTs)) { - return rollbackStatusLocked, kverrors.BuildLockErr(key, &lock) - } - // We can not simply delete the lock because the prewrite may be sent multiple times. - // To prevent that we update it a rollback lock. - batch.Rollback(key, true) - return rollbackStatusDone, nil - } - // lock.startTS > startTS, go to DB to check if the key is committed. - return rollbackStatusNewLock, nil - } - return rollbackStatusNoLock, nil -} - -func (store *MVCCStore) rollbackKeyReadDB(req *requestCtx, batch mvcc.WriteBatch, key []byte, startTS uint64, hasLock bool) error { - commitTS, err := store.checkCommitted(req.getDBReader(), key, startTS) - if err != nil { - return err - } - if commitTS != 0 { - return kverrors.ErrAlreadyCommitted(commitTS) - } - // commit not found, rollback this key - batch.Rollback(key, false) - return nil -} - -func (store *MVCCStore) checkCommitted(reader *dbreader.DBReader, key []byte, startTS uint64) (uint64, error) { - txn := reader.GetTxn() - item, err := txn.Get(key) - if err != nil && err != badger.ErrKeyNotFound { - return 0, errors.Trace(err) - } - if item == nil { - return 0, nil - } - userMeta := mvcc.DBUserMeta(item.UserMeta()) - if userMeta.StartTS() == startTS { - return userMeta.CommitTS(), nil - } - it := reader.GetIter() - it.SetAllVersions(true) - for it.Seek(key); it.Valid(); it.Next() { - item = it.Item() - if !bytes.Equal(item.Key(), key) { - break - } - userMeta = mvcc.DBUserMeta(item.UserMeta()) - if userMeta.StartTS() == startTS { - return userMeta.CommitTS(), nil - } - } - return 0, nil -} - -// LockPair contains a pair of key and lock. It's used for reading through locks. -type LockPair struct { - key []byte - lock *mvcc.Lock -} - -func getValueFromLock(lock *mvcc.Lock) []byte { - if lock.Op == byte(kvrpcpb.Op_Put) { - // lock owns the value so needn't to safeCopy it. - return lock.Value - } - return nil -} - -// *LockPair is not nil if the lock in the committed timestamp set. Read operations can get value from it without deep copy. -func checkLock(lock mvcc.Lock, key []byte, startTS uint64, resolved []uint64, committed []uint64) (*LockPair, error) { - if inTSSet(lock.StartTS, resolved) { - return nil, nil - } - lockVisible := lock.StartTS <= startTS - isWriteLock := lock.Op == uint8(kvrpcpb.Op_Put) || lock.Op == uint8(kvrpcpb.Op_Del) - isPrimaryGet := startTS == maxSystemTS && bytes.Equal(lock.Primary, key) && !lock.UseAsyncCommit - if lockVisible && isWriteLock && !isPrimaryGet { - if inTSSet(lock.StartTS, committed) { - return &LockPair{safeCopy(key), &lock}, nil - } - return nil, kverrors.BuildLockErr(safeCopy(key), &lock) - } - return nil, nil -} - -// checkLockForRcCheckTS checks the lock for `RcCheckTS` isolation level in transaction read. -func checkLockForRcCheckTS(lock mvcc.Lock, key []byte, startTS uint64, resolved []uint64) error { - if inTSSet(lock.StartTS, resolved) { - return nil - } - isWriteLock := lock.Op == uint8(kvrpcpb.Op_Put) || lock.Op == uint8(kvrpcpb.Op_Del) - if !isWriteLock { - return nil - } - return &kverrors.ErrConflict{ - StartTS: startTS, - ConflictTS: lock.StartTS, - Key: safeCopy(key), - Reason: kvrpcpb.WriteConflict_RcCheckTs, - } -} - -// CheckKeysLockForRcCheckTS is used to check version timestamp if `RcCheckTS` isolation level is used. -func (store *MVCCStore) CheckKeysLockForRcCheckTS(startTS uint64, resolved []uint64, keys ...[]byte) error { - var buf []byte - for _, key := range keys { - buf = store.lockStore.Get(key, buf) - if len(buf) == 0 { - continue - } - lock := mvcc.DecodeLock(buf) - err := checkLockForRcCheckTS(lock, key, startTS, resolved) - if err != nil { - return err - } - } - return nil -} - -// CheckKeysLock implements the MVCCStore interface. -func (store *MVCCStore) CheckKeysLock(startTS uint64, resolved, committed []uint64, keys ...[]byte) ([]*LockPair, error) { - var buf []byte - var lockPairs []*LockPair - for _, key := range keys { - buf = store.lockStore.Get(key, buf) - if len(buf) == 0 { - continue - } - lock := mvcc.DecodeLock(buf) - lockPair, err := checkLock(lock, key, startTS, resolved, committed) - if lockPair != nil { - lockPairs = append(lockPairs, lockPair) - } - if err != nil { - return nil, err - } - } - return lockPairs, nil -} - -// CheckRangeLock implements the MVCCStore interface. -func (store *MVCCStore) CheckRangeLock(startTS uint64, startKey, endKey []byte, resolved []uint64) error { - it := store.lockStore.NewIterator() - for it.Seek(startKey); it.Valid(); it.Next() { - if exceedEndKey(it.Key(), endKey) { - break - } - lock := mvcc.DecodeLock(it.Value()) - _, err := checkLock(lock, it.Key(), startTS, resolved, nil) - if err != nil { - return err - } - } - return nil -} - -// Cleanup implements the MVCCStore interface. -func (store *MVCCStore) Cleanup(reqCtx *requestCtx, key []byte, startTS, currentTs uint64) error { - hashVals := keysToHashVals(key) - regCtx := reqCtx.regCtx - batch := store.dbWriter.NewWriteBatch(startTS, 0, reqCtx.rpcCtx) - - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - status, err := store.rollbackKeyReadLock(reqCtx, batch, key, startTS, currentTs) - if err != nil { - return err - } - if status != rollbackStatusDone { - err := store.rollbackKeyReadDB(reqCtx, batch, key, startTS, status == rollbackStatusNewLock) - if err != nil { - return err - } - rbStatus := store.checkExtraTxnStatus(reqCtx, key, startTS) - if rbStatus.isOpLockCommitted() { - return kverrors.ErrAlreadyCommitted(rbStatus.commitTS) - } - } - err = store.dbWriter.Write(batch) - store.lockWaiterManager.WakeUp(startTS, 0, hashVals) - return err -} - -func (store *MVCCStore) appendScannedLock(locks []*kvrpcpb.LockInfo, it *lockstore.Iterator, maxTS uint64) []*kvrpcpb.LockInfo { - lock := mvcc.DecodeLock(it.Value()) - if lock.StartTS < maxTS { - locks = append(locks, lock.ToLockInfo(append([]byte{}, it.Key()...))) - } - return locks -} - -// ScanLock implements the MVCCStore interface. -func (store *MVCCStore) ScanLock(reqCtx *requestCtx, maxTS uint64, limit int) ([]*kvrpcpb.LockInfo, error) { - var locks []*kvrpcpb.LockInfo - it := store.lockStore.NewIterator() - for it.Seek(reqCtx.regCtx.RawStart()); it.Valid(); it.Next() { - if exceedEndKey(it.Key(), reqCtx.regCtx.RawEnd()) { - return locks, nil - } - if len(locks) == limit { - return locks, nil - } - locks = store.appendScannedLock(locks, it, maxTS) - } - return locks, nil -} - -// PhysicalScanLock implements the MVCCStore interface. -func (store *MVCCStore) PhysicalScanLock(startKey []byte, maxTS uint64, limit int) []*kvrpcpb.LockInfo { - var locks []*kvrpcpb.LockInfo - it := store.lockStore.NewIterator() - for it.Seek(startKey); it.Valid(); it.Next() { - if len(locks) == limit { - break - } - locks = store.appendScannedLock(locks, it, maxTS) - } - return locks -} - -// ResolveLock implements the MVCCStore interface. -func (store *MVCCStore) ResolveLock(reqCtx *requestCtx, lockKeys [][]byte, startTS, commitTS uint64) error { - regCtx := reqCtx.regCtx - if len(lockKeys) == 0 { - it := store.lockStore.NewIterator() - for it.Seek(regCtx.RawStart()); it.Valid(); it.Next() { - if exceedEndKey(it.Key(), regCtx.RawEnd()) { - break - } - lock := mvcc.DecodeLock(it.Value()) - if lock.StartTS != startTS { - continue - } - lockKeys = append(lockKeys, safeCopy(it.Key())) - } - if len(lockKeys) == 0 { - return nil - } - } - hashVals := keysToHashVals(lockKeys...) - batch := store.dbWriter.NewWriteBatch(startTS, commitTS, reqCtx.rpcCtx) - - regCtx.AcquireLatches(hashVals) - defer regCtx.ReleaseLatches(hashVals) - - var buf []byte - var tmpDiff int - for _, lockKey := range lockKeys { - buf = store.lockStore.Get(lockKey, buf) - if len(buf) == 0 { - continue - } - lock := mvcc.DecodeLock(buf) - if lock.StartTS != startTS { - continue - } - if commitTS > 0 { - tmpDiff += len(lockKey) + len(lock.Value) - batch.Commit(lockKey, &lock) - } else { - batch.Rollback(lockKey, true) - } - } - atomic.AddInt64(regCtx.Diff(), int64(tmpDiff)) - err := store.dbWriter.Write(batch) - return err -} - -// UpdateSafePoint implements the MVCCStore interface. -func (store *MVCCStore) UpdateSafePoint(safePoint uint64) { - // We use the gcLock to make sure safePoint can only increase. - store.db.UpdateSafeTs(safePoint) - store.safePoint.UpdateTS(safePoint) - log.Info("safePoint is updated to", zap.Uint64("ts", safePoint), zap.Time("time", tsToTime(safePoint))) -} - -func tsToTime(ts uint64) time.Time { - return time.UnixMilli(int64(ts >> 18)) -} - -// StartDeadlockDetection implements the MVCCStore interface. -func (store *MVCCStore) StartDeadlockDetection(isRaft bool) { - if isRaft { - go store.DeadlockDetectCli.sendReqLoop() - return - } - - go func() { - for { - select { - case req := <-store.DeadlockDetectCli.sendCh: - resp := store.DeadlockDetectSvr.Detect(req) - if resp != nil { - store.DeadlockDetectCli.waitMgr.WakeUpForDeadlock(resp) - } - case <-store.closeCh: - return - } - } - }() -} - -// MvccGetByKey gets mvcc information using input key as rawKey -func (store *MVCCStore) MvccGetByKey(reqCtx *requestCtx, key []byte) (*kvrpcpb.MvccInfo, error) { - mvccInfo := &kvrpcpb.MvccInfo{} - lock := store.getLock(reqCtx, key) - if lock != nil { - mvccInfo.Lock = &kvrpcpb.MvccLock{ - Type: kvrpcpb.Op(lock.Op), - StartTs: lock.StartTS, - Primary: lock.Primary, - ShortValue: lock.Value, - } - } - reader := reqCtx.getDBReader() - isRowKey := rowcodec.IsRowKey(key) - // Get commit writes from db - err := reader.GetMvccInfoByKey(key, isRowKey, mvccInfo) - if err != nil { - return nil, err - } - // Get rollback writes from rollback store - err = store.getExtraMvccInfo(key, reqCtx, mvccInfo) - if err != nil { - return nil, err - } - slices.SortFunc(mvccInfo.Writes, func(i, j *kvrpcpb.MvccWrite) int { - return cmp.Compare(j.CommitTs, i.CommitTs) - }) - mvccInfo.Values = make([]*kvrpcpb.MvccValue, len(mvccInfo.Writes)) - for i := 0; i < len(mvccInfo.Writes); i++ { - write := mvccInfo.Writes[i] - mvccInfo.Values[i] = &kvrpcpb.MvccValue{ - StartTs: write.StartTs, - Value: write.ShortValue, - } - } - return mvccInfo, nil -} - -func (store *MVCCStore) getExtraMvccInfo(rawkey []byte, - reqCtx *requestCtx, mvccInfo *kvrpcpb.MvccInfo) error { - it := reqCtx.getDBReader().GetExtraIter() - rbStartKey := mvcc.EncodeExtraTxnStatusKey(rawkey, math.MaxUint64) - rbEndKey := mvcc.EncodeExtraTxnStatusKey(rawkey, 0) - for it.Seek(rbStartKey); it.Valid(); it.Next() { - item := it.Item() - if len(rbEndKey) != 0 && bytes.Compare(item.Key(), rbEndKey) > 0 { - break - } - key := item.Key() - if len(key) == 0 || (key[0] != tableExtraPrefix && key[0] != metaExtraPrefix) { - continue - } - rollbackTs := mvcc.DecodeKeyTS(key) - curRecord := &kvrpcpb.MvccWrite{ - Type: kvrpcpb.Op_Rollback, - StartTs: rollbackTs, - CommitTs: rollbackTs, - } - mvccInfo.Writes = append(mvccInfo.Writes, curRecord) - } - return nil -} - -// MvccGetByStartTs implements the MVCCStore interface. -func (store *MVCCStore) MvccGetByStartTs(reqCtx *requestCtx, startTs uint64) (*kvrpcpb.MvccInfo, []byte, error) { - reader := reqCtx.getDBReader() - startKey := reqCtx.regCtx.RawStart() - endKey := reqCtx.regCtx.RawEnd() - rawKey, err := reader.GetKeyByStartTs(startKey, endKey, startTs) - if err != nil { - return nil, nil, err - } - if rawKey == nil { - return nil, nil, nil - } - res, err := store.MvccGetByKey(reqCtx, rawKey) - if err != nil { - return nil, nil, err - } - return res, rawKey, nil -} - -// DeleteFileInRange implements the MVCCStore interface. -func (store *MVCCStore) DeleteFileInRange(start, end []byte) { - store.db.DeleteFilesInRange(start, end) - start[0]++ - end[0]++ - store.db.DeleteFilesInRange(start, end) -} - -// Get implements the MVCCStore interface. -func (store *MVCCStore) Get(reqCtx *requestCtx, key []byte, version uint64) ([]byte, error) { - if reqCtx.isSnapshotIsolation() { - lockPairs, err := store.CheckKeysLock(version, reqCtx.rpcCtx.ResolvedLocks, reqCtx.rpcCtx.CommittedLocks, key) - if err != nil { - return nil, err - } - if len(lockPairs) != 0 { - return getValueFromLock(lockPairs[0].lock), nil - } - } else if reqCtx.isRcCheckTSIsolationLevel() { - err := store.CheckKeysLockForRcCheckTS(version, reqCtx.rpcCtx.ResolvedLocks, key) - if err != nil { - return nil, err - } - } - val, err := reqCtx.getDBReader().Get(key, version) - if val == nil { - return nil, err - } - return safeCopy(val), err -} - -// BatchGet implements the MVCCStore interface. -func (store *MVCCStore) BatchGet(reqCtx *requestCtx, keys [][]byte, version uint64) []*kvrpcpb.KvPair { - pairs := make([]*kvrpcpb.KvPair, 0, len(keys)) - var remain [][]byte - if reqCtx.isSnapshotIsolation() { - remain = make([][]byte, 0, len(keys)) - for _, key := range keys { - lockPairs, err := store.CheckKeysLock(version, reqCtx.rpcCtx.ResolvedLocks, reqCtx.rpcCtx.CommittedLocks, key) - if err != nil { - pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Error: convertToKeyError(err)}) - } else if len(lockPairs) != 0 { - value := getValueFromLock(lockPairs[0].lock) - if value != nil { - pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Value: value}) - } - } else { - remain = append(remain, key) - } - } - } else if reqCtx.isRcCheckTSIsolationLevel() { - remain = make([][]byte, 0, len(keys)) - for _, key := range keys { - err := store.CheckKeysLockForRcCheckTS(version, reqCtx.rpcCtx.ResolvedLocks, key) - if err != nil { - pairs = append(pairs, &kvrpcpb.KvPair{Key: key, Error: convertToKeyError(err)}) - } else { - remain = append(remain, key) - } - } - } else { - remain = keys - } - batchGetFunc := func(key, value []byte, err error) { - if len(value) != 0 { - pairs = append(pairs, &kvrpcpb.KvPair{ - Key: safeCopy(key), - Value: safeCopy(value), - Error: convertToKeyError(err), - }) - } - } - reqCtx.getDBReader().BatchGet(remain, version, batchGetFunc) - return pairs -} - -func (store *MVCCStore) collectRangeLock(startTS uint64, startKey, endKey []byte, resolved, committed []uint64, - isolationLEvel kvrpcpb.IsolationLevel) []*kvrpcpb.KvPair { - var pairs []*kvrpcpb.KvPair - it := store.lockStore.NewIterator() - for it.Seek(startKey); it.Valid(); it.Next() { - if exceedEndKey(it.Key(), endKey) { - break - } - lock := mvcc.DecodeLock(it.Value()) - if isolationLEvel == kvrpcpb.IsolationLevel_SI { - lockPair, err := checkLock(lock, it.Key(), startTS, resolved, committed) - if lockPair != nil { - pairs = append(pairs, &kvrpcpb.KvPair{ - Key: lockPair.key, - // deleted key's value is nil - Value: getValueFromLock(lockPair.lock), - }) - } else if err != nil { - pairs = append(pairs, &kvrpcpb.KvPair{ - Error: convertToKeyError(err), - Key: safeCopy(it.Key()), - }) - } - } else if isolationLEvel == kvrpcpb.IsolationLevel_RCCheckTS { - err := checkLockForRcCheckTS(lock, it.Key(), startTS, resolved) - if err != nil { - pairs = append(pairs, &kvrpcpb.KvPair{ - Error: convertToKeyError(err), - Key: safeCopy(it.Key()), - }) - } - } - } - return pairs -} - -func inTSSet(startTS uint64, tsSet []uint64) bool { - for _, v := range tsSet { - if startTS == v { - return true - } - } - return false -} - -type kvScanProcessor struct { - pairs []*kvrpcpb.KvPair - sampleStep uint32 - scanCnt uint32 -} - -func (p *kvScanProcessor) Process(key, value []byte) (err error) { - if p.sampleStep > 0 { - p.scanCnt++ - if (p.scanCnt-1)%p.sampleStep != 0 { - return nil - } - } - p.pairs = append(p.pairs, &kvrpcpb.KvPair{ - Key: safeCopy(key), - Value: safeCopy(value), - }) - return nil -} - -func (p *kvScanProcessor) SkipValue() bool { - return false -} - -// Scan implements the MVCCStore interface. -func (store *MVCCStore) Scan(reqCtx *requestCtx, req *kvrpcpb.ScanRequest) []*kvrpcpb.KvPair { - var startKey, endKey []byte - if req.Reverse { - startKey = req.EndKey - if len(startKey) == 0 { - startKey = reqCtx.regCtx.RawStart() - } - endKey = req.StartKey - } else { - startKey = req.StartKey - endKey = req.EndKey - if len(endKey) == 0 { - endKey = reqCtx.regCtx.RawEnd() - } - if len(endKey) == 0 { - // Don't scan internal keys. - endKey = InternalKeyPrefix - } - } - var lockPairs []*kvrpcpb.KvPair - limit := req.GetLimit() - if req.SampleStep == 0 { - if reqCtx.isSnapshotIsolation() || reqCtx.isRcCheckTSIsolationLevel() { - if bytes.Compare(startKey, endKey) <= 0 { - lockPairs = store.collectRangeLock(req.GetVersion(), startKey, endKey, reqCtx.rpcCtx.ResolvedLocks, - reqCtx.rpcCtx.CommittedLocks, reqCtx.rpcCtx.IsolationLevel) - } else { - lockPairs = store.collectRangeLock(req.GetVersion(), endKey, startKey, reqCtx.rpcCtx.ResolvedLocks, - reqCtx.rpcCtx.CommittedLocks, reqCtx.rpcCtx.IsolationLevel) - } - } - } else { - limit = req.SampleStep * limit - } - var scanProc = &kvScanProcessor{ - sampleStep: req.SampleStep, - } - reader := reqCtx.getDBReader() - var err error - if req.Reverse { - err = reader.ReverseScan(startKey, endKey, int(limit), req.GetVersion(), scanProc) - } else { - err = reader.Scan(startKey, endKey, int(limit), req.GetVersion(), scanProc) - } - if err != nil { - scanProc.pairs = append(scanProc.pairs[:0], &kvrpcpb.KvPair{ - Error: convertToKeyError(err), - }) - return scanProc.pairs - } - pairs := append(lockPairs, scanProc.pairs...) - sort.SliceStable(pairs, func(i, j int) bool { - cmp := bytes.Compare(pairs[i].Key, pairs[j].Key) - if req.Reverse { - cmp = -cmp - } - return cmp < 0 - }) - validPairs := pairs[:0] - var prev *kvrpcpb.KvPair - for _, pair := range pairs { - if prev != nil && bytes.Equal(prev.Key, pair.Key) { - continue - } - prev = pair - if pair.Error != nil || len(pair.Value) != 0 { - validPairs = append(validPairs, pair) - if len(validPairs) >= int(limit) { - break - } - } - } - return validPairs -} - -func (store *MVCCStore) runUpdateSafePointLoop() { - var lastSafePoint uint64 - ticker := time.NewTicker(time.Minute) - for { - safePoint, err := store.pdClient.GetGCSafePoint(context.Background()) - if err != nil { - log.Error("get GC safePoint error", zap.Error(err)) - } else if lastSafePoint < safePoint { - store.UpdateSafePoint(safePoint) - lastSafePoint = safePoint - } - select { - case <-store.closeCh: - return - case <-ticker.C: - } - } -} - -// SafePoint represents a safe point. -type SafePoint struct { - timestamp uint64 -} - -// UpdateTS is used to record the timestamp of updating the table's schema information. -// These changing schema operations don't include 'truncate table' and 'rename table'. -func (sp *SafePoint) UpdateTS(ts uint64) { - for { - old := atomic.LoadUint64(&sp.timestamp) - if old < ts { - if !atomic.CompareAndSwapUint64(&sp.timestamp, old, ts) { - continue - } - } - break - } -} - -// CreateCompactionFilter implements badger.CompactionFilterFactory function. -func (sp *SafePoint) CreateCompactionFilter(targetLevel int, startKey, endKey []byte) badger.CompactionFilter { - return &GCCompactionFilter{ - targetLevel: targetLevel, - safePoint: atomic.LoadUint64(&sp.timestamp), - } -} - -// GCCompactionFilter implements the badger.CompactionFilter interface. -type GCCompactionFilter struct { - targetLevel int - safePoint uint64 -} - -const ( - metaPrefix byte = 'm' - // 'm' + 1 = 'n' - metaExtraPrefix byte = 'n' - tablePrefix byte = 't' - // 't' + 1 = 'u - tableExtraPrefix byte = 'u' -) - -// Filter implements the badger.CompactionFilter interface. -// Since we use txn ts as badger version, we only need to filter Delete, Rollback and Op_Lock. -// It is called for the first valid version before safe point, older versions are discarded automatically. -func (f *GCCompactionFilter) Filter(key, value, userMeta []byte) badger.Decision { - switch key[0] { - case metaPrefix, tablePrefix: - // For latest version, we need to remove `delete` key, which has value len 0. - if mvcc.DBUserMeta(userMeta).CommitTS() < f.safePoint && len(value) == 0 { - return badger.DecisionMarkTombstone - } - case metaExtraPrefix, tableExtraPrefix: - // For latest version, we can only remove `delete` key, which has value len 0. - if mvcc.DBUserMeta(userMeta).StartTS() < f.safePoint { - return badger.DecisionDrop - } - } - // Older version are discarded automatically, we need to keep the first valid version. - return badger.DecisionKeep -} - -var ( - baseGuard = badger.Guard{MatchLen: 64, MinSize: 64 * 1024} - raftGuard = badger.Guard{Prefix: []byte{0}, MatchLen: 1, MinSize: 64 * 1024} - metaGuard = badger.Guard{Prefix: []byte{'m'}, MatchLen: 1, MinSize: 64 * 1024} - metaExtraGuard = badger.Guard{Prefix: []byte{'n'}, MatchLen: 1, MinSize: 1} - tableGuard = badger.Guard{Prefix: []byte{'t'}, MatchLen: 9, MinSize: 1 * 1024 * 1024} - tableIndexGuard = badger.Guard{Prefix: []byte{'t'}, MatchLen: 11, MinSize: 1 * 1024 * 1024} - tableExtraGuard = badger.Guard{Prefix: []byte{'u'}, MatchLen: 1, MinSize: 1} -) - -// Guards implements the badger.CompactionFilter interface. -// Guards returns specifications that may splits the SST files -// A key is associated to a guard that has the longest matched Prefix. -func (f *GCCompactionFilter) Guards() []badger.Guard { - if f.targetLevel < 4 { - // do not split index and row for top levels. - return []badger.Guard{ - baseGuard, raftGuard, metaGuard, metaExtraGuard, tableGuard, tableExtraGuard, - } - } - // split index and row for bottom levels. - return []badger.Guard{ - baseGuard, raftGuard, metaGuard, metaExtraGuard, tableIndexGuard, tableExtraGuard, - } -} - -func doesNeedLock(item *badger.Item, - req *kvrpcpb.PessimisticLockRequest) (bool, error) { - if req.LockOnlyIfExists { - if item == nil { - return false, nil - } - val, err := item.Value() - if err != nil { - return false, err - } - if len(val) == 0 { - return false, nil - } - } - return true, nil -} diff --git a/store/mockstore/unistore/tikv/mvcc/BUILD.bazel b/store/mockstore/unistore/tikv/mvcc/BUILD.bazel deleted file mode 100644 index 080afaf5bf4fd..0000000000000 --- a/store/mockstore/unistore/tikv/mvcc/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mvcc", - srcs = [ - "db_writer.go", - "mvcc.go", - "tikv.go", - ], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc", - visibility = ["//visibility:public"], - deps = [ - "//store/mockstore/unistore/lockstore", - "//util/codec", - "@com_github_pingcap_badger//:badger", - "@com_github_pingcap_badger//y", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - ], -) diff --git a/store/mockstore/unistore/tikv/mvcc/mvcc.go b/store/mockstore/unistore/tikv/mvcc/mvcc.go deleted file mode 100644 index e8184007b8e53..0000000000000 --- a/store/mockstore/unistore/tikv/mvcc/mvcc.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package mvcc - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "unsafe" - - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/util/codec" -) - -var defaultEndian = binary.LittleEndian - -// DBUserMeta is the user meta used in DB. -type DBUserMeta []byte - -// DecodeLock decodes data to lock, the primary and value is copied, the secondaries are copied if async commit is enabled. -func DecodeLock(data []byte) (l Lock) { - l.LockHdr = *(*LockHdr)(unsafe.Pointer(&data[0])) - cursor := mvccLockHdrSize - lockBuf := append([]byte{}, data[cursor:]...) - l.Primary = lockBuf[:l.PrimaryLen] - cursor = int(l.PrimaryLen) - if l.LockHdr.SecondaryNum > 0 { - l.Secondaries = make([][]byte, l.LockHdr.SecondaryNum) - for i := uint32(0); i < l.LockHdr.SecondaryNum; i++ { - keyLen := binary.LittleEndian.Uint16(lockBuf[cursor:]) - cursor += 2 - l.Secondaries[i] = lockBuf[cursor : cursor+int(keyLen)] - cursor += int(keyLen) - } - } - l.Value = lockBuf[cursor:] - return -} - -// LockHdr holds fixed size fields for mvcc Lock. -type LockHdr struct { - StartTS uint64 - ForUpdateTS uint64 - MinCommitTS uint64 - TTL uint32 - Op uint8 - HasOldVer bool - PrimaryLen uint16 - UseAsyncCommit bool - SecondaryNum uint32 -} - -const mvccLockHdrSize = int(unsafe.Sizeof(LockHdr{})) - -// Lock is the structure for MVCC lock. -type Lock struct { - LockHdr - Primary []byte - Value []byte - Secondaries [][]byte -} - -// MarshalBinary implements encoding.BinaryMarshaler interface. -func (l *Lock) MarshalBinary() []byte { - lockLen := mvccLockHdrSize + len(l.Primary) + len(l.Value) - length := lockLen - if l.LockHdr.SecondaryNum > 0 { - for _, secondaryKey := range l.Secondaries { - length += 2 - length += len(secondaryKey) - } - } - buf := make([]byte, length) - hdr := (*LockHdr)(unsafe.Pointer(&buf[0])) - *hdr = l.LockHdr - cursor := mvccLockHdrSize - copy(buf[cursor:], l.Primary) - cursor += len(l.Primary) - if l.LockHdr.SecondaryNum > 0 { - for _, secondaryKey := range l.Secondaries { - binary.LittleEndian.PutUint16(buf[cursor:], uint16(len(secondaryKey))) - cursor += 2 - copy(buf[cursor:], secondaryKey) - cursor += len(secondaryKey) - } - } - copy(buf[cursor:], l.Value) - return buf -} - -// ToLockInfo converts an mvcc Lock to kvrpcpb.LockInfo -func (l *Lock) ToLockInfo(key []byte) *kvrpcpb.LockInfo { - return &kvrpcpb.LockInfo{ - PrimaryLock: l.Primary, - LockVersion: l.StartTS, - Key: key, - LockTtl: uint64(l.TTL), - LockType: kvrpcpb.Op(l.Op), - LockForUpdateTs: l.ForUpdateTS, - UseAsyncCommit: l.UseAsyncCommit, - MinCommitTs: l.MinCommitTS, - Secondaries: l.Secondaries, - } -} - -// String implements fmt.Stringer for Lock. -func (l *Lock) String() string { - return fmt.Sprintf( - "Lock { Type: %v, StartTS: %v, ForUpdateTS: %v, Primary: %v, UseAsyncCommit: %v }", - kvrpcpb.Op(l.Op).String(), - l.StartTS, - l.ForUpdateTS, - hex.EncodeToString(l.Primary), - l.UseAsyncCommit, - ) -} - -// UserMeta value for lock. -const ( - LockUserMetaNoneByte = 0 - LockUserMetaDeleteByte = 2 -) - -// UserMeta byte slices for lock. -var ( - LockUserMetaNone = []byte{LockUserMetaNoneByte} - LockUserMetaDelete = []byte{LockUserMetaDeleteByte} -) - -// DecodeKeyTS decodes the TS in a key. -func DecodeKeyTS(buf []byte) uint64 { - tsBin := buf[len(buf)-8:] - _, ts, err := codec.DecodeUintDesc(tsBin) - if err != nil { - panic(err) - } - return ts -} - -// NewDBUserMeta creates a new DBUserMeta. -func NewDBUserMeta(startTS, commitTS uint64) DBUserMeta { - m := make(DBUserMeta, 16) - defaultEndian.PutUint64(m, startTS) - defaultEndian.PutUint64(m[8:], commitTS) - return m -} - -// CommitTS reads the commitTS from the DBUserMeta. -func (m DBUserMeta) CommitTS() uint64 { - return defaultEndian.Uint64(m[8:]) -} - -// StartTS reads the startTS from the DBUserMeta. -func (m DBUserMeta) StartTS() uint64 { - return defaultEndian.Uint64(m[:8]) -} - -// EncodeExtraTxnStatusKey encodes a extra transaction status key. -// It is only used for Rollback and Op_Lock. -func EncodeExtraTxnStatusKey(key []byte, startTS uint64) []byte { - b := append([]byte{}, key...) - ret := codec.EncodeUintDesc(b, startTS) - ret[0]++ - return ret -} - -// DecodeExtraTxnStatusKey decodes a extra transaction status key. -func DecodeExtraTxnStatusKey(extraKey []byte) (key []byte) { - if len(extraKey) <= 9 { - return nil - } - key = append([]byte{}, extraKey[:len(extraKey)-8]...) - key[0]-- - return -} diff --git a/store/mockstore/unistore/tikv/mvcc/tikv.go b/store/mockstore/unistore/tikv/mvcc/tikv.go deleted file mode 100644 index ff62a8057602f..0000000000000 --- a/store/mockstore/unistore/tikv/mvcc/tikv.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package mvcc - -import ( - "github.com/pingcap/badger/y" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/util/codec" -) - -// WriteType defines a write type. -type WriteType = byte - -// WriteType -const ( - WriteTypeLock WriteType = 'L' - WriteTypeRollback WriteType = 'R' - WriteTypeDelete WriteType = 'D' - WriteTypePut WriteType = 'P' -) - -// WriteCFValue represents a write CF value. -type WriteCFValue struct { - Type WriteType - StartTS uint64 - ShortVal []byte -} - -var errInvalidWriteCFValue = errors.New("invalid write CF value") - -// ParseWriteCFValue parses the []byte data and returns a WriteCFValue. -func ParseWriteCFValue(data []byte) (wv WriteCFValue, err error) { - if len(data) == 0 { - err = errInvalidWriteCFValue - return - } - wv.Type = data[0] - switch wv.Type { - case WriteTypePut, WriteTypeDelete, WriteTypeLock, WriteTypeRollback: - default: - err = errInvalidWriteCFValue - return - } - wv.ShortVal, wv.StartTS, err = codec.DecodeUvarint(data[1:]) - return -} - -const ( - shortValuePrefix = 'v' - forUpdatePrefix = 'f' - minCommitTsPrefix = 'm' - - //ShortValueMaxLen defines max length of short value. - ShortValueMaxLen = 64 -) - -// EncodeWriteCFValue accepts a write cf parameters and return the encoded bytes data. -// Just like the tikv encoding form. See tikv/src/storage/mvcc/write.rs for more detail. -func EncodeWriteCFValue(t WriteType, startTs uint64, shortVal []byte) []byte { - data := make([]byte, 0) - data = append(data, t) - data = codec.EncodeUvarint(data, startTs) - if len(shortVal) != 0 { - data = append(data, byte(shortValuePrefix), byte(len(shortVal))) - return append(data, shortVal...) - } - return data -} - -// EncodeLockCFValue encodes the mvcc lock and returns putLock value and putDefault value if exists. -func EncodeLockCFValue(lock *Lock) ([]byte, []byte) { - data := make([]byte, 0) - switch lock.Op { - case byte(kvrpcpb.Op_Put): - data = append(data, LockTypePut) - case byte(kvrpcpb.Op_Del): - data = append(data, LockTypeDelete) - case byte(kvrpcpb.Op_Lock): - data = append(data, LockTypeLock) - case byte(kvrpcpb.Op_PessimisticLock): - data = append(data, LockTypePessimistic) - default: - panic("invalid lock op") - } - var longValue []byte - data = codec.EncodeUvarint(codec.EncodeCompactBytes(data, lock.Primary), lock.StartTS) - data = codec.EncodeUvarint(data, uint64(lock.TTL)) - if len(lock.Value) <= ShortValueMaxLen { - if len(lock.Value) != 0 { - data = append(data, byte(shortValuePrefix), byte(len(lock.Value))) - data = append(data, lock.Value...) - } - } else { - longValue = y.SafeCopy(nil, lock.Value) - } - if lock.ForUpdateTS > 0 { - data = append(data, byte(forUpdatePrefix)) - data = codec.EncodeUint(data, lock.ForUpdateTS) - } - if lock.MinCommitTS > 0 { - data = append(data, byte(minCommitTsPrefix)) - data = codec.EncodeUint(data, lock.MinCommitTS) - } - return data, longValue -} - -// LockType defines a lock type. -type LockType = byte - -// LockType -const ( - LockTypePut LockType = 'P' - LockTypeDelete LockType = 'D' - LockTypeLock LockType = 'L' - LockTypePessimistic LockType = 'S' -) - -var errInvalidLockCFValue = errors.New("invalid lock CF value") diff --git a/store/mockstore/unistore/tikv/pberror/BUILD.bazel b/store/mockstore/unistore/tikv/pberror/BUILD.bazel deleted file mode 100644 index b2508521dc295..0000000000000 --- a/store/mockstore/unistore/tikv/pberror/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "pberror", - srcs = ["pberror.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/tikv/pberror", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_kvproto//pkg/errorpb"], -) diff --git a/store/mockstore/unistore/tikv/region.go b/store/mockstore/unistore/tikv/region.go deleted file mode 100644 index 623a3b584689d..0000000000000 --- a/store/mockstore/unistore/tikv/region.go +++ /dev/null @@ -1,798 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package tikv - -import ( - "bytes" - "context" - "encoding/binary" - "strconv" - "sync" - "sync/atomic" - "time" - "unsafe" - - "github.com/pingcap/badger" - "github.com/pingcap/badger/y" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/errorpb" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/kvproto/pkg/pdpb" - "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/metrics" - "github.com/pingcap/tidb/store/mockstore/unistore/pd" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/util/codec" - "go.uber.org/zap" -) - -// InternalKey -var ( - InternalKeyPrefix = []byte{0xff} - InternalRegionMetaPrefix = append(InternalKeyPrefix, "region"...) - InternalStoreMetaKey = append(InternalKeyPrefix, "store"...) - InternalSafePointKey = append(InternalKeyPrefix, "safepoint"...) -) - -// InternalRegionMetaKey returns internal region meta key with the given region id. -func InternalRegionMetaKey(regionID uint64) []byte { - return []byte(string(InternalRegionMetaPrefix) + strconv.FormatUint(regionID, 10)) -} - -// RegionCtx defines the region context interface. -type RegionCtx interface { - Meta() *metapb.Region - Diff() *int64 - RawStart() []byte - RawEnd() []byte - AcquireLatches(hashes []uint64) - ReleaseLatches(hashes []uint64) -} - -type regionCtx struct { - meta *metapb.Region - regionEpoch unsafe.Pointer // *metapb.RegionEpoch - rawStartKey []byte - rawEndKey []byte - approximateSize int64 - diff int64 - - latches *latches -} - -type latches struct { - slots [256]map[uint64]*sync.WaitGroup - locks [256]sync.Mutex -} - -func newLatches() *latches { - l := &latches{} - for i := 0; i < 256; i++ { - l.slots[i] = map[uint64]*sync.WaitGroup{} - } - return l -} - -func (l *latches) acquire(keyHashes []uint64) (waitCnt int) { - wg := new(sync.WaitGroup) - wg.Add(1) - for _, hash := range keyHashes { - waitCnt += l.acquireOne(hash, wg) - } - return -} - -func (l *latches) acquireOne(hash uint64, wg *sync.WaitGroup) (waitCnt int) { - slotID := hash >> 56 - for { - m := l.slots[slotID] - l.locks[slotID].Lock() - w, ok := m[hash] - if !ok { - m[hash] = wg - } - l.locks[slotID].Unlock() - if ok { - w.Wait() - waitCnt++ - continue - } - return - } -} - -func (l *latches) release(keyHashes []uint64) { - var w *sync.WaitGroup - for _, hash := range keyHashes { - slotID := hash >> 56 - l.locks[slotID].Lock() - m := l.slots[slotID] - if w == nil { - w = m[hash] - } - delete(m, hash) - l.locks[slotID].Unlock() - } - if w != nil { - w.Done() - } -} - -func newRegionCtx(meta *metapb.Region, latches *latches, _ interface{}) *regionCtx { - regCtx := ®ionCtx{ - meta: meta, - latches: latches, - regionEpoch: unsafe.Pointer(meta.GetRegionEpoch()), - } - regCtx.rawStartKey = regCtx.decodeRawStartKey() - regCtx.rawEndKey = regCtx.decodeRawEndKey() - if len(regCtx.rawEndKey) == 0 { - // Avoid reading internal meta data. - regCtx.rawEndKey = InternalKeyPrefix - } - return regCtx -} - -func (ri *regionCtx) Meta() *metapb.Region { - return ri.meta -} - -func (ri *regionCtx) Diff() *int64 { - return &ri.diff -} - -func (ri *regionCtx) RawStart() []byte { - return ri.rawStartKey -} - -func (ri *regionCtx) RawEnd() []byte { - return ri.rawEndKey -} - -func (ri *regionCtx) getRegionEpoch() *metapb.RegionEpoch { - return (*metapb.RegionEpoch)(atomic.LoadPointer(&ri.regionEpoch)) -} - -func (ri *regionCtx) updateRegionEpoch(epoch *metapb.RegionEpoch) { - atomic.StorePointer(&ri.regionEpoch, (unsafe.Pointer)(epoch)) -} - -func (ri *regionCtx) decodeRawStartKey() []byte { - if len(ri.meta.StartKey) == 0 { - return nil - } - _, rawKey, err := codec.DecodeBytes(ri.meta.StartKey, nil) - if err != nil { - panic("invalid region start key") - } - return rawKey -} - -func (ri *regionCtx) decodeRawEndKey() []byte { - if len(ri.meta.EndKey) == 0 { - return nil - } - _, rawKey, err := codec.DecodeBytes(ri.meta.EndKey, nil) - if err != nil { - panic("invalid region end key") - } - return rawKey -} - -func (ri *regionCtx) greaterEqualEndKey(key []byte) bool { - return len(ri.rawEndKey) > 0 && bytes.Compare(key, ri.rawEndKey) >= 0 -} - -func newPeerMeta(peerID, storeID uint64) *metapb.Peer { - return &metapb.Peer{ - Id: peerID, - StoreId: storeID, - } -} - -func (ri *regionCtx) incConfVer() { - ri.meta.RegionEpoch = &metapb.RegionEpoch{ - ConfVer: ri.meta.GetRegionEpoch().GetConfVer() + 1, - Version: ri.meta.GetRegionEpoch().GetVersion(), - } - ri.updateRegionEpoch(ri.meta.RegionEpoch) -} - -func (ri *regionCtx) addPeer(peerID, storeID uint64) { - ri.meta.Peers = append(ri.meta.Peers, newPeerMeta(peerID, storeID)) - ri.incConfVer() -} - -func (ri *regionCtx) unmarshal(data []byte) error { - ri.approximateSize = int64(binary.LittleEndian.Uint64(data)) - data = data[8:] - ri.meta = &metapb.Region{} - err := ri.meta.Unmarshal(data) - if err != nil { - return errors.Trace(err) - } - ri.rawStartKey = ri.decodeRawStartKey() - ri.rawEndKey = ri.decodeRawEndKey() - ri.regionEpoch = unsafe.Pointer(ri.meta.RegionEpoch) - return nil -} - -func (ri *regionCtx) marshal() []byte { - data := make([]byte, 8+ri.meta.Size()) - binary.LittleEndian.PutUint64(data, uint64(ri.approximateSize)) - _, err := ri.meta.MarshalTo(data[8:]) - if err != nil { - log.Error("region ctx marshal failed", zap.Error(err)) - } - return data -} - -// AcquireLatches add latches for all input hashVals, the input hashVals should be -// sorted and have no duplicates -func (ri *regionCtx) AcquireLatches(hashVals []uint64) { - start := time.Now() - waitCnt := ri.latches.acquire(hashVals) - dur := time.Since(start) - metrics.LatchWait.Observe(dur.Seconds()) - if dur > time.Millisecond*50 { - var id string - if ri.meta == nil { - id = "unknown" - } else { - id = strconv.FormatUint(ri.meta.Id, 10) - } - log.S().Warnf("region %s acquire %d locks takes %v, waitCnt %d", id, len(hashVals), dur, waitCnt) - } -} - -func (ri *regionCtx) ReleaseLatches(hashVals []uint64) { - ri.latches.release(hashVals) -} - -// RegionOptions represents the region options. -type RegionOptions struct { - StoreAddr string - PDAddr string - RegionSize int64 -} - -// RegionManager defines the region manager interface. -type RegionManager interface { - GetRegionFromCtx(ctx *kvrpcpb.Context) (RegionCtx, *errorpb.Error) - GetStoreInfoFromCtx(ctx *kvrpcpb.Context) (string, uint64, *errorpb.Error) - SplitRegion(req *kvrpcpb.SplitRegionRequest) *kvrpcpb.SplitRegionResponse - GetStoreIDByAddr(addr string) (uint64, error) - GetStoreAddrByStoreID(storeID uint64) (string, error) - Close() error -} - -type regionManager struct { - storeMeta *metapb.Store - mu sync.RWMutex - regions map[uint64]*regionCtx - latches *latches -} - -func (rm *regionManager) GetStoreIDByAddr(addr string) (uint64, error) { - if rm.storeMeta.Address != addr { - return 0, errors.New("store not match") - } - return rm.storeMeta.Id, nil -} - -func (rm *regionManager) GetStoreAddrByStoreID(storeID uint64) (string, error) { - if rm.storeMeta.Id != storeID { - return "", errors.New("store not match") - } - return rm.storeMeta.Address, nil -} - -func (rm *regionManager) GetStoreInfoFromCtx(ctx *kvrpcpb.Context) (string, uint64, *errorpb.Error) { - if ctx.GetPeer() != nil && ctx.GetPeer().GetStoreId() != rm.storeMeta.Id { - return "", 0, &errorpb.Error{ - Message: "store not match", - StoreNotMatch: &errorpb.StoreNotMatch{}, - } - } - return rm.storeMeta.Address, rm.storeMeta.Id, nil -} - -func (rm *regionManager) GetRegionFromCtx(ctx *kvrpcpb.Context) (RegionCtx, *errorpb.Error) { - ctxPeer := ctx.GetPeer() - if ctxPeer != nil && ctxPeer.GetStoreId() != rm.storeMeta.Id { - return nil, &errorpb.Error{ - Message: "store not match", - StoreNotMatch: &errorpb.StoreNotMatch{}, - } - } - rm.mu.RLock() - ri := rm.regions[ctx.RegionId] - rm.mu.RUnlock() - if ri == nil { - return nil, &errorpb.Error{ - Message: "region not found", - RegionNotFound: &errorpb.RegionNotFound{ - RegionId: ctx.GetRegionId(), - }, - } - } - // Region epoch does not match. - if rm.isEpochStale(ri.getRegionEpoch(), ctx.GetRegionEpoch()) { - return nil, &errorpb.Error{ - Message: "stale epoch", - EpochNotMatch: &errorpb.EpochNotMatch{ - CurrentRegions: []*metapb.Region{{ - Id: ri.meta.Id, - StartKey: ri.meta.StartKey, - EndKey: ri.meta.EndKey, - RegionEpoch: ri.getRegionEpoch(), - Peers: ri.meta.Peers, - }}, - }, - } - } - return ri, nil -} - -func (rm *regionManager) isEpochStale(lhs, rhs *metapb.RegionEpoch) bool { - return lhs.GetConfVer() != rhs.GetConfVer() || lhs.GetVersion() != rhs.GetVersion() -} - -func (rm *regionManager) loadFromLocal(bundle *mvcc.DBBundle, f func(*regionCtx)) error { - err := bundle.DB.View(func(txn *badger.Txn) error { - item, err1 := txn.Get(InternalStoreMetaKey) - if err1 != nil { - return err1 - } - val, err1 := item.Value() - if err1 != nil { - return err1 - } - err1 = rm.storeMeta.Unmarshal(val) - if err1 != nil { - return err1 - } - // load region meta - opts := badger.DefaultIteratorOptions - it := txn.NewIterator(opts) - defer it.Close() - prefix := InternalRegionMetaPrefix - for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() { - item := it.Item() - val, err1 = item.Value() - if err1 != nil { - return err1 - } - r := new(regionCtx) - err := r.unmarshal(val) - if err != nil { - return errors.Trace(err) - } - r.latches = rm.latches - rm.regions[r.meta.Id] = r - f(r) - } - return nil - }) - if err == badger.ErrKeyNotFound { - err = nil - } - return err -} - -// StandAloneRegionManager represents a standalone region manager. -type StandAloneRegionManager struct { - regionManager - bundle *mvcc.DBBundle - pdc pd.Client - clusterID uint64 - regionSize int64 - closeCh chan struct{} - wg sync.WaitGroup -} - -// NewStandAloneRegionManager returns a new standalone region manager. -func NewStandAloneRegionManager(bundle *mvcc.DBBundle, opts RegionOptions, pdc pd.Client) *StandAloneRegionManager { - var err error - clusterID := pdc.GetClusterID(context.TODO()) - log.S().Infof("cluster id %v", clusterID) - rm := &StandAloneRegionManager{ - bundle: bundle, - pdc: pdc, - clusterID: clusterID, - regionSize: opts.RegionSize, - closeCh: make(chan struct{}), - regionManager: regionManager{ - regions: make(map[uint64]*regionCtx), - storeMeta: new(metapb.Store), - latches: newLatches(), - }, - } - err = rm.loadFromLocal(bundle, func(r *regionCtx) { - req := &pdpb.RegionHeartbeatRequest{ - Region: r.meta, - Leader: r.meta.Peers[0], - ApproximateSize: uint64(r.approximateSize), - } - rm.pdc.ReportRegion(req) - }) - if err != nil { - log.Fatal("load from local failed", zap.Error(err)) - } - if rm.storeMeta.Id == 0 { - err = rm.initStore(opts.StoreAddr) - if err != nil { - log.Fatal("init store failed", zap.Error(err)) - } - } - rm.storeMeta.Address = opts.StoreAddr - err = rm.pdc.PutStore(context.TODO(), rm.storeMeta) - if err != nil { - log.Fatal("put store failed", zap.Error(err)) - } - rm.wg.Add(2) - go rm.runSplitWorker() - go rm.storeHeartBeatLoop() - return rm -} - -func (rm *StandAloneRegionManager) initStore(storeAddr string) error { - log.Info("initializing store") - ids, err := rm.allocIDs(3) - if err != nil { - return err - } - storeID, regionID, peerID := ids[0], ids[1], ids[2] - rm.storeMeta.Id = storeID - rm.storeMeta.Address = storeAddr - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - rootRegion := &metapb.Region{ - Id: regionID, - RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []*metapb.Peer{{Id: peerID, StoreId: storeID}}, - } - rm.regions[rootRegion.Id] = newRegionCtx(rootRegion, rm.latches, nil) - _, err = rm.pdc.Bootstrap(ctx, rm.storeMeta, rootRegion) - cancel() - if err != nil { - log.Fatal("initialize failed", zap.Error(err)) - } - rm.initialSplit(rootRegion) - storeBuf, err := rm.storeMeta.Marshal() - if err != nil { - log.Fatal("marshal store meta failed", zap.Error(err)) - } - err = rm.bundle.DB.Update(func(txn *badger.Txn) error { - ts := atomic.AddUint64(&rm.bundle.StateTS, 1) - err = txn.SetEntry(&badger.Entry{ - Key: y.KeyWithTs(InternalStoreMetaKey, ts), - Value: storeBuf, - }) - if err != nil { - return err - } - for rid, region := range rm.regions { - regionBuf := region.marshal() - err = txn.SetEntry(&badger.Entry{ - Key: y.KeyWithTs(InternalRegionMetaKey(rid), ts), - Value: regionBuf, - }) - if err != nil { - log.Fatal("save region info failed", zap.Error(err)) - } - } - return nil - }) - for _, region := range rm.regions { - req := &pdpb.RegionHeartbeatRequest{ - Region: region.meta, - Leader: region.meta.Peers[0], - ApproximateSize: uint64(region.approximateSize), - } - rm.pdc.ReportRegion(req) - } - log.Info("Initialize success") - return nil -} - -// initSplit splits the cluster into multiple regions. -func (rm *StandAloneRegionManager) initialSplit(root *metapb.Region) { - root.EndKey = codec.EncodeBytes(nil, []byte{'m'}) - root.RegionEpoch.Version = 2 - rm.regions[root.Id] = newRegionCtx(root, rm.latches, nil) - preSplitStartKeys := [][]byte{{'m'}, {'n'}, {'t'}, {'u'}} - ids, err := rm.allocIDs(len(preSplitStartKeys) * 2) - if err != nil { - log.Fatal("alloc ids failed", zap.Error(err)) - } - for i, startKey := range preSplitStartKeys { - var endKey []byte - if i < len(preSplitStartKeys)-1 { - endKey = codec.EncodeBytes(nil, preSplitStartKeys[i+1]) - } - newRegion := &metapb.Region{ - Id: ids[i*2], - RegionEpoch: &metapb.RegionEpoch{ConfVer: 1, Version: 1}, - Peers: []*metapb.Peer{{Id: ids[i*2+1], StoreId: rm.storeMeta.Id}}, - StartKey: codec.EncodeBytes(nil, startKey), - EndKey: endKey, - } - rm.regions[newRegion.Id] = newRegionCtx(newRegion, rm.latches, nil) - } -} - -func (rm *StandAloneRegionManager) allocIDs(n int) ([]uint64, error) { - ids := make([]uint64, n) - for i := 0; i < n; i++ { - id, err := rm.pdc.AllocID(context.Background()) - if err != nil { - return nil, errors.Trace(err) - } - ids[i] = id - } - return ids, nil -} - -func (rm *StandAloneRegionManager) storeHeartBeatLoop() { - defer rm.wg.Done() - ticker := time.Tick(time.Second * 3) - for { - select { - case <-rm.closeCh: - return - case <-ticker: - } - storeStats := new(pdpb.StoreStats) - storeStats.StoreId = rm.storeMeta.Id - storeStats.Available = 1024 * 1024 * 1024 - rm.mu.RLock() - storeStats.RegionCount = uint32(len(rm.regions)) - rm.mu.RUnlock() - storeStats.Capacity = 2048 * 1024 * 1024 - if err := rm.pdc.StoreHeartbeat(context.Background(), storeStats); err != nil { - log.Warn("store heartbeat failed", zap.Error(err)) - } - } -} - -type keySample struct { - key []byte - leftSize int64 -} - -// sampler samples keys in a region for later pick a split key. -type sampler struct { - samples [64]keySample - length int - step int - scanned int - totalSize int64 -} - -func newSampler() *sampler { - return &sampler{step: 1} -} - -func (s *sampler) shrinkIfNeeded() { - if s.length < len(s.samples) { - return - } - for i := 0; i < len(s.samples)/2; i++ { - s.samples[i], s.samples[i*2] = s.samples[i*2], s.samples[i] - } - s.length /= 2 - s.step *= 2 -} - -func (s *sampler) shouldSample() bool { - // It's an optimization for 's.scanned % s.step == 0' - return s.scanned&(s.step-1) == 0 -} - -func (s *sampler) scanKey(key []byte, size int64) { - s.totalSize += size - s.scanned++ - if s.shouldSample() { - sample := s.samples[s.length] - // safe copy the key. - sample.key = append(sample.key[:0], key...) - sample.leftSize = s.totalSize - s.samples[s.length] = sample - s.length++ - s.shrinkIfNeeded() - } -} - -func (s *sampler) getSplitKeyAndSize() ([]byte, int64) { - targetSize := s.totalSize * 2 / 3 - for _, sample := range s.samples[:s.length] { - if sample.leftSize >= targetSize { - return sample.key, sample.leftSize - } - } - return []byte{}, 0 -} - -func (rm *StandAloneRegionManager) runSplitWorker() { - defer rm.wg.Done() - ticker := time.NewTicker(time.Second * 5) - var regionsToCheck []*regionCtx - var regionsToSave []*regionCtx - for { - regionsToCheck = regionsToCheck[:0] - rm.mu.RLock() - for _, ri := range rm.regions { - if ri.approximateSize+atomic.LoadInt64(&ri.diff) > rm.regionSize*3/2 { - regionsToCheck = append(regionsToCheck, ri) - } - } - rm.mu.RUnlock() - for _, ri := range regionsToCheck { - err := rm.splitCheckRegion(ri) - if err != nil { - log.Error("split region failed", zap.Error(err)) - } - } - - regionsToSave = regionsToSave[:0] - rm.mu.RLock() - for _, ri := range rm.regions { - if atomic.LoadInt64(&ri.diff) > rm.regionSize/8 { - regionsToSave = append(regionsToSave, ri) - } - } - rm.mu.RUnlock() - rm.saveSize(regionsToSave) - select { - case <-rm.closeCh: - return - case <-ticker.C: - } - } -} - -func (rm *StandAloneRegionManager) saveSize(regionsToSave []*regionCtx) { - err1 := rm.bundle.DB.Update(func(txn *badger.Txn) error { - ts := atomic.AddUint64(&rm.bundle.StateTS, 1) - for _, ri := range regionsToSave { - ri.approximateSize += atomic.LoadInt64(&ri.diff) - err := txn.SetEntry(&badger.Entry{ - Key: y.KeyWithTs(InternalRegionMetaKey(ri.meta.Id), ts), - Value: ri.marshal(), - }) - if err != nil { - return err - } - } - return nil - }) - if err1 != nil { - log.Error("region manager save size failed", zap.Error(err1)) - } -} - -func (rm *StandAloneRegionManager) splitCheckRegion(region *regionCtx) error { - s := newSampler() - err := rm.bundle.DB.View(func(txn *badger.Txn) error { - iter := txn.NewIterator(badger.IteratorOptions{}) - defer iter.Close() - for iter.Seek(region.rawStartKey); iter.Valid(); iter.Next() { - item := iter.Item() - if region.greaterEqualEndKey(item.Key()) { - break - } - s.scanKey(item.Key(), int64(len(item.Key())+item.ValueSize())) - } - return nil - }) - if err != nil { - log.Error("sample region failed", zap.Error(err)) - return errors.Trace(err) - } - // Need to update the diff to avoid split check again. - atomic.StoreInt64(®ion.diff, s.totalSize-region.approximateSize) - if s.totalSize < rm.regionSize { - return nil - } - splitKey, leftSize := s.getSplitKeyAndSize() - log.Info("try to split region", zap.Uint64("id", region.meta.Id), zap.Binary("split key", splitKey), - zap.Int64("left size", leftSize), zap.Int64("right size", s.totalSize-leftSize)) - err = rm.splitRegion(region, splitKey, s.totalSize, leftSize) - if err != nil { - log.Error("split region failed", zap.Error(err)) - } - return errors.Trace(err) -} - -func (rm *StandAloneRegionManager) splitRegion(oldRegionCtx *regionCtx, splitKey []byte, oldSize, leftSize int64) error { - oldRegion := oldRegionCtx.meta - rightMeta := &metapb.Region{ - Id: oldRegion.Id, - StartKey: codec.EncodeBytes(nil, splitKey), - EndKey: oldRegion.EndKey, - RegionEpoch: &metapb.RegionEpoch{ - ConfVer: oldRegion.RegionEpoch.ConfVer, - Version: oldRegion.RegionEpoch.Version + 1, - }, - Peers: oldRegion.Peers, - } - right := newRegionCtx(rightMeta, rm.latches, nil) - right.approximateSize = oldSize - leftSize - id, err := rm.pdc.AllocID(context.Background()) - if err != nil { - return errors.Trace(err) - } - leftMeta := &metapb.Region{ - Id: id, - StartKey: oldRegion.StartKey, - EndKey: codec.EncodeBytes(nil, splitKey), - RegionEpoch: &metapb.RegionEpoch{ - ConfVer: 1, - Version: 1, - }, - Peers: oldRegion.Peers, - } - left := newRegionCtx(leftMeta, rm.latches, nil) - left.approximateSize = leftSize - err1 := rm.bundle.DB.Update(func(txn *badger.Txn) error { - ts := atomic.AddUint64(&rm.bundle.StateTS, 1) - err := txn.SetEntry(&badger.Entry{ - Key: y.KeyWithTs(InternalRegionMetaKey(left.meta.Id), ts), - Value: left.marshal(), - }) - if err != nil { - return errors.Trace(err) - } - err = txn.SetEntry(&badger.Entry{ - Key: y.KeyWithTs(InternalRegionMetaKey(right.meta.Id), ts), - Value: right.marshal(), - }) - return errors.Trace(err) - }) - if err1 != nil { - return errors.Trace(err1) - } - rm.mu.Lock() - rm.regions[left.meta.Id] = left - rm.regions[right.meta.Id] = right - rm.mu.Unlock() - rm.pdc.ReportRegion(&pdpb.RegionHeartbeatRequest{ - Region: right.meta, - Leader: right.meta.Peers[0], - ApproximateSize: uint64(right.approximateSize), - }) - rm.pdc.ReportRegion(&pdpb.RegionHeartbeatRequest{ - Region: left.meta, - Leader: left.meta.Peers[0], - ApproximateSize: uint64(left.approximateSize), - }) - log.Info("region splitted", zap.Uint64("old id", oldRegion.Id), - zap.Uint64("left id", left.meta.Id), zap.Int64("left size", left.approximateSize), - zap.Uint64("right id", right.meta.Id), zap.Int64("right size", right.approximateSize)) - return nil -} - -// SplitRegion splits a region. -func (rm *StandAloneRegionManager) SplitRegion(req *kvrpcpb.SplitRegionRequest) *kvrpcpb.SplitRegionResponse { - return &kvrpcpb.SplitRegionResponse{} -} - -// Close closes the standalone region manager. -func (rm *StandAloneRegionManager) Close() error { - close(rm.closeCh) - rm.wg.Wait() - return nil -} diff --git a/store/mockstore/unistore/tikv/server.go b/store/mockstore/unistore/tikv/server.go deleted file mode 100644 index 1d63418da7968..0000000000000 --- a/store/mockstore/unistore/tikv/server.go +++ /dev/null @@ -1,1175 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package tikv - -import ( - "context" - "io" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/coprocessor" - deadlockPb "github.com/pingcap/kvproto/pkg/deadlock" - "github.com/pingcap/kvproto/pkg/errorpb" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/kvproto/pkg/mpp" - "github.com/pingcap/kvproto/pkg/tikvpb" - "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore/unistore/client" - "github.com/pingcap/tidb/store/mockstore/unistore/cophandler" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/kverrors" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/pberror" - "github.com/pingcap/tidb/store/mockstore/unistore/util/lockwaiter" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -var _ tikvpb.TikvServer = new(Server) - -// Server implements the tikvpb.TikvServer interface. -type Server struct { - // After updating the kvproto, some methods of TikvServer are not implemented. - // Construct `Server` based on `UnimplementedTikvServer`, in order to compile successfully - tikvpb.UnimplementedTikvServer - mvccStore *MVCCStore - regionManager RegionManager - innerServer InnerServer - RPCClient client.Client - refCount int32 - stopped int32 -} - -// NewServer returns a new server. -func NewServer(rm RegionManager, store *MVCCStore, innerServer InnerServer) *Server { - return &Server{ - mvccStore: store, - regionManager: rm, - innerServer: innerServer, - } -} - -// Stop stops the server. -func (svr *Server) Stop() { - atomic.StoreInt32(&svr.stopped, 1) - for { - if atomic.LoadInt32(&svr.refCount) == 0 { - break - } - time.Sleep(time.Millisecond * 10) - } - - if err := svr.mvccStore.Close(); err != nil { - log.Error("close mvcc store failed", zap.Error(err)) - } - if err := svr.regionManager.Close(); err != nil { - log.Error("close region manager failed", zap.Error(err)) - } - if err := svr.innerServer.Stop(); err != nil { - log.Error("close inner server failed", zap.Error(err)) - } -} - -// GetStoreIDByAddr gets a store id by the store address. -func (svr *Server) GetStoreIDByAddr(addr string) (uint64, error) { - return svr.regionManager.GetStoreIDByAddr(addr) -} - -// GetStoreAddrByStoreID gets a store address by the store id. -func (svr *Server) GetStoreAddrByStoreID(storeID uint64) (string, error) { - return svr.regionManager.GetStoreAddrByStoreID(storeID) -} - -type requestCtx struct { - svr *Server - regCtx RegionCtx - regErr *errorpb.Error - buf []byte - reader *dbreader.DBReader - method string - startTime time.Time - rpcCtx *kvrpcpb.Context - storeAddr string - storeID uint64 - asyncMinCommitTS uint64 - onePCCommitTS uint64 -} - -func newRequestCtx(svr *Server, ctx *kvrpcpb.Context, method string) (*requestCtx, error) { - atomic.AddInt32(&svr.refCount, 1) - if atomic.LoadInt32(&svr.stopped) > 0 { - atomic.AddInt32(&svr.refCount, -1) - return nil, kverrors.ErrRetryable("server is closed") - } - req := &requestCtx{ - svr: svr, - method: method, - startTime: time.Now(), - rpcCtx: ctx, - } - req.regCtx, req.regErr = svr.regionManager.GetRegionFromCtx(ctx) - storeAddr, storeID, regErr := svr.regionManager.GetStoreInfoFromCtx(ctx) - req.storeAddr = storeAddr - req.storeID = storeID - if regErr != nil { - req.regErr = regErr - } - return req, nil -} - -// For read-only requests that doesn't acquire latches, this function must be called after all locks has been checked. -func (req *requestCtx) getDBReader() *dbreader.DBReader { - if req.reader == nil { - mvccStore := req.svr.mvccStore - txn := mvccStore.db.NewTransaction(false) - req.reader = dbreader.NewDBReader(req.regCtx.RawStart(), req.regCtx.RawEnd(), txn) - req.reader.RcCheckTS = req.isRcCheckTSIsolationLevel() - } - return req.reader -} - -func (req *requestCtx) isSnapshotIsolation() bool { - return req.rpcCtx.IsolationLevel == kvrpcpb.IsolationLevel_SI -} - -func (req *requestCtx) isRcCheckTSIsolationLevel() bool { - return req.rpcCtx.IsolationLevel == kvrpcpb.IsolationLevel_RCCheckTS -} - -func (req *requestCtx) finish() { - atomic.AddInt32(&req.svr.refCount, -1) - if req.reader != nil { - req.reader.Close() - } -} - -// KvGet implements the tikvpb.TikvServer interface. -func (svr *Server) KvGet(ctx context.Context, req *kvrpcpb.GetRequest) (*kvrpcpb.GetResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvGet") - if err != nil { - return &kvrpcpb.GetResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.GetResponse{RegionError: reqCtx.regErr}, nil - } - val, err := svr.mvccStore.Get(reqCtx, req.Key, req.Version) - return &kvrpcpb.GetResponse{ - Value: val, - Error: convertToKeyError(err), - }, nil -} - -// KvScan implements the tikvpb.TikvServer interface. -func (svr *Server) KvScan(ctx context.Context, req *kvrpcpb.ScanRequest) (*kvrpcpb.ScanResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvScan") - if err != nil { - return &kvrpcpb.ScanResponse{Pairs: []*kvrpcpb.KvPair{{Error: convertToKeyError(err)}}}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.ScanResponse{RegionError: reqCtx.regErr}, nil - } - pairs := svr.mvccStore.Scan(reqCtx, req) - return &kvrpcpb.ScanResponse{ - Pairs: pairs, - }, nil -} - -// KvPessimisticLock implements the tikvpb.TikvServer interface. -func (svr *Server) KvPessimisticLock(ctx context.Context, req *kvrpcpb.PessimisticLockRequest) (*kvrpcpb.PessimisticLockResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "PessimisticLock") - if err != nil { - return &kvrpcpb.PessimisticLockResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.PessimisticLockResponse{RegionError: reqCtx.regErr}, nil - } - resp := &kvrpcpb.PessimisticLockResponse{} - waiter, err := svr.mvccStore.PessimisticLock(reqCtx, req, resp) - resp.Errors, resp.RegionError = convertToPBErrors(err) - if waiter == nil { - return resp, nil - } - result := waiter.Wait() - svr.mvccStore.DeadlockDetectCli.CleanUpWaitFor(req.StartVersion, waiter.LockTS, waiter.KeyHash) - svr.mvccStore.lockWaiterManager.CleanUp(waiter) - if result.WakeupSleepTime == lockwaiter.WaitTimeout { - return resp, nil - } - if result.DeadlockResp != nil { - log.Error("deadlock found", zap.Stringer("entry", &result.DeadlockResp.Entry)) - errLocked := err.(*kverrors.ErrLocked) - deadlockErr := &kverrors.ErrDeadlock{ - LockKey: errLocked.Key, - LockTS: errLocked.Lock.StartTS, - DeadlockKeyHash: result.DeadlockResp.DeadlockKeyHash, - WaitChain: result.DeadlockResp.WaitChain, - } - resp.Errors, resp.RegionError = convertToPBErrors(deadlockErr) - if req.WakeUpMode == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { - resp.Results = []*kvrpcpb.PessimisticLockKeyResult{ - { - Type: kvrpcpb.PessimisticLockKeyResultType_LockResultFailed, - }, - } - } - return resp, nil - } - if result.WakeupSleepTime == lockwaiter.WakeUpThisWaiter { - if req.Force || req.WakeUpMode == kvrpcpb.PessimisticLockWakeUpMode_WakeUpModeForceLock { - req.WaitTimeout = lockwaiter.LockNoWait - resp = &kvrpcpb.PessimisticLockResponse{} - _, err := svr.mvccStore.PessimisticLock(reqCtx, req, resp) - resp.Errors, resp.RegionError = convertToPBErrors(err) - if err == nil { - return resp, nil - } - if _, ok := err.(*kverrors.ErrLocked); !ok { - resp.Errors, resp.RegionError = convertToPBErrors(err) - return resp, nil - } - log.Warn("wakeup force lock request, try lock still failed", zap.Error(err)) - } - } - // The key is rollbacked, we don't have the exact commitTS, but we can use the server's latest. - // Always use the store latest ts since the waiter result commitTs may not be the real conflict ts - conflictCommitTS := svr.mvccStore.getLatestTS() - err = &kverrors.ErrConflict{ - StartTS: req.GetForUpdateTs(), - ConflictTS: waiter.LockTS, - ConflictCommitTS: conflictCommitTS, - } - resp.Errors, _ = convertToPBErrors(err) - return resp, nil -} - -// KVPessimisticRollback implements the tikvpb.TikvServer interface. -func (svr *Server) KVPessimisticRollback(ctx context.Context, req *kvrpcpb.PessimisticRollbackRequest) (*kvrpcpb.PessimisticRollbackResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "PessimisticRollback") - if err != nil { - return &kvrpcpb.PessimisticRollbackResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.PessimisticRollbackResponse{RegionError: reqCtx.regErr}, nil - } - err = svr.mvccStore.PessimisticRollback(reqCtx, req) - resp := &kvrpcpb.PessimisticRollbackResponse{} - resp.Errors, resp.RegionError = convertToPBErrors(err) - return resp, nil -} - -// KvTxnHeartBeat implements the tikvpb.TikvServer interface. -func (svr *Server) KvTxnHeartBeat(ctx context.Context, req *kvrpcpb.TxnHeartBeatRequest) (*kvrpcpb.TxnHeartBeatResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "TxnHeartBeat") - if err != nil { - return &kvrpcpb.TxnHeartBeatResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.TxnHeartBeatResponse{RegionError: reqCtx.regErr}, nil - } - lockTTL, err := svr.mvccStore.TxnHeartBeat(reqCtx, req) - resp := &kvrpcpb.TxnHeartBeatResponse{LockTtl: lockTTL} - resp.Error, resp.RegionError = convertToPBError(err) - return resp, nil -} - -// KvCheckTxnStatus implements the tikvpb.TikvServer interface. -func (svr *Server) KvCheckTxnStatus(ctx context.Context, req *kvrpcpb.CheckTxnStatusRequest) (*kvrpcpb.CheckTxnStatusResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvCheckTxnStatus") - if err != nil { - return &kvrpcpb.CheckTxnStatusResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.CheckTxnStatusResponse{RegionError: reqCtx.regErr}, nil - } - txnStatus, err := svr.mvccStore.CheckTxnStatus(reqCtx, req) - ttl := uint64(0) - if txnStatus.lockInfo != nil { - ttl = txnStatus.lockInfo.LockTtl - } - resp := &kvrpcpb.CheckTxnStatusResponse{ - LockTtl: ttl, - CommitVersion: txnStatus.commitTS, - Action: txnStatus.action, - LockInfo: txnStatus.lockInfo, - } - resp.Error, resp.RegionError = convertToPBError(err) - return resp, nil -} - -// KvCheckSecondaryLocks implements the tikvpb.TikvServer interface. -func (svr *Server) KvCheckSecondaryLocks(ctx context.Context, req *kvrpcpb.CheckSecondaryLocksRequest) (*kvrpcpb.CheckSecondaryLocksResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvCheckSecondaryLocks") - if err != nil { - return &kvrpcpb.CheckSecondaryLocksResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.CheckSecondaryLocksResponse{RegionError: reqCtx.regErr}, nil - } - locksStatus, err := svr.mvccStore.CheckSecondaryLocks(reqCtx, req.Keys, req.StartVersion) - resp := &kvrpcpb.CheckSecondaryLocksResponse{} - if err == nil { - resp.Locks = locksStatus.locks - resp.CommitTs = locksStatus.commitTS - } else { - resp.Error, resp.RegionError = convertToPBError(err) - } - return resp, nil -} - -// KvPrewrite implements the tikvpb.TikvServer interface. -func (svr *Server) KvPrewrite(ctx context.Context, req *kvrpcpb.PrewriteRequest) (*kvrpcpb.PrewriteResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvPrewrite") - if err != nil { - return &kvrpcpb.PrewriteResponse{Errors: []*kvrpcpb.KeyError{convertToKeyError(err)}}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.PrewriteResponse{RegionError: reqCtx.regErr}, nil - } - err = svr.mvccStore.Prewrite(reqCtx, req) - resp := &kvrpcpb.PrewriteResponse{} - if reqCtx.asyncMinCommitTS > 0 { - resp.MinCommitTs = reqCtx.asyncMinCommitTS - } - if reqCtx.onePCCommitTS > 0 { - resp.OnePcCommitTs = reqCtx.onePCCommitTS - } - resp.Errors, resp.RegionError = convertToPBErrors(err) - return resp, nil -} - -// KvCommit implements the tikvpb.TikvServer interface. -func (svr *Server) KvCommit(ctx context.Context, req *kvrpcpb.CommitRequest) (*kvrpcpb.CommitResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvCommit") - if err != nil { - return &kvrpcpb.CommitResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.CommitResponse{RegionError: reqCtx.regErr}, nil - } - resp := new(kvrpcpb.CommitResponse) - err = svr.mvccStore.Commit(reqCtx, req.Keys, req.GetStartVersion(), req.GetCommitVersion()) - if err != nil { - resp.Error, resp.RegionError = convertToPBError(err) - } - return resp, nil -} - -// RawGetKeyTTL implements the tikvpb.TikvServer interface. -func (svr *Server) RawGetKeyTTL(ctx context.Context, req *kvrpcpb.RawGetKeyTTLRequest) (*kvrpcpb.RawGetKeyTTLResponse, error) { - // TODO - return &kvrpcpb.RawGetKeyTTLResponse{}, nil -} - -// KvImport implements the tikvpb.TikvServer interface. -func (svr *Server) KvImport(context.Context, *kvrpcpb.ImportRequest) (*kvrpcpb.ImportResponse, error) { - // TODO - return &kvrpcpb.ImportResponse{}, nil -} - -// KvCleanup implements the tikvpb.TikvServer interface. -func (svr *Server) KvCleanup(ctx context.Context, req *kvrpcpb.CleanupRequest) (*kvrpcpb.CleanupResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvCleanup") - if err != nil { - return &kvrpcpb.CleanupResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.CleanupResponse{RegionError: reqCtx.regErr}, nil - } - err = svr.mvccStore.Cleanup(reqCtx, req.Key, req.StartVersion, req.CurrentTs) - resp := new(kvrpcpb.CleanupResponse) - if committed, ok := err.(kverrors.ErrAlreadyCommitted); ok { - resp.CommitVersion = uint64(committed) - } else if err != nil { - log.Error("cleanup failed", zap.Error(err)) - resp.Error, resp.RegionError = convertToPBError(err) - } - return resp, nil -} - -// KvBatchGet implements the tikvpb.TikvServer interface. -func (svr *Server) KvBatchGet(ctx context.Context, req *kvrpcpb.BatchGetRequest) (*kvrpcpb.BatchGetResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvBatchGet") - if err != nil { - return &kvrpcpb.BatchGetResponse{Pairs: []*kvrpcpb.KvPair{{Error: convertToKeyError(err)}}}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.BatchGetResponse{RegionError: reqCtx.regErr}, nil - } - pairs := svr.mvccStore.BatchGet(reqCtx, req.Keys, req.GetVersion()) - return &kvrpcpb.BatchGetResponse{ - Pairs: pairs, - }, nil -} - -// KvBatchRollback implements the tikvpb.TikvServer interface. -func (svr *Server) KvBatchRollback(ctx context.Context, req *kvrpcpb.BatchRollbackRequest) (*kvrpcpb.BatchRollbackResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvBatchRollback") - if err != nil { - return &kvrpcpb.BatchRollbackResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.BatchRollbackResponse{RegionError: reqCtx.regErr}, nil - } - resp := new(kvrpcpb.BatchRollbackResponse) - err = svr.mvccStore.Rollback(reqCtx, req.Keys, req.StartVersion) - resp.Error, resp.RegionError = convertToPBError(err) - return resp, nil -} - -// KvScanLock implements the tikvpb.TikvServer interface. -func (svr *Server) KvScanLock(ctx context.Context, req *kvrpcpb.ScanLockRequest) (*kvrpcpb.ScanLockResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvScanLock") - if err != nil { - return &kvrpcpb.ScanLockResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.ScanLockResponse{RegionError: reqCtx.regErr}, nil - } - log.Debug("kv scan lock") - locks, err := svr.mvccStore.ScanLock(reqCtx, req.MaxVersion, int(req.Limit)) - return &kvrpcpb.ScanLockResponse{Error: convertToKeyError(err), Locks: locks}, nil -} - -// KvResolveLock implements the tikvpb.TikvServer interface. -func (svr *Server) KvResolveLock(ctx context.Context, req *kvrpcpb.ResolveLockRequest) (*kvrpcpb.ResolveLockResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvResolveLock") - if err != nil { - return &kvrpcpb.ResolveLockResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.ResolveLockResponse{RegionError: reqCtx.regErr}, nil - } - resp := &kvrpcpb.ResolveLockResponse{} - if len(req.TxnInfos) > 0 { - for _, txnInfo := range req.TxnInfos { - log.S().Debugf("kv resolve lock region:%d txn:%v", reqCtx.regCtx.Meta().Id, txnInfo.Txn) - err := svr.mvccStore.ResolveLock(reqCtx, nil, txnInfo.Txn, txnInfo.Status) - if err != nil { - resp.Error, resp.RegionError = convertToPBError(err) - break - } - } - } else { - log.S().Debugf("kv resolve lock region:%d txn:%v", reqCtx.regCtx.Meta().Id, req.StartVersion) - err := svr.mvccStore.ResolveLock(reqCtx, req.Keys, req.StartVersion, req.CommitVersion) - resp.Error, resp.RegionError = convertToPBError(err) - } - return resp, nil -} - -// KvGC implements the tikvpb.TikvServer interface. -func (svr *Server) KvGC(ctx context.Context, req *kvrpcpb.GCRequest) (*kvrpcpb.GCResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvGC") - if err != nil { - return &kvrpcpb.GCResponse{Error: convertToKeyError(err)}, nil - } - defer reqCtx.finish() - svr.mvccStore.UpdateSafePoint(req.SafePoint) - return &kvrpcpb.GCResponse{}, nil -} - -// KvDeleteRange implements the tikvpb.TikvServer interface. -func (svr *Server) KvDeleteRange(ctx context.Context, req *kvrpcpb.DeleteRangeRequest) (*kvrpcpb.DeleteRangeResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "KvDeleteRange") - if err != nil { - return &kvrpcpb.DeleteRangeResponse{Error: convertToKeyError(err).String()}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.DeleteRangeResponse{RegionError: reqCtx.regErr}, nil - } - err = svr.mvccStore.dbWriter.DeleteRange(req.StartKey, req.EndKey, reqCtx.regCtx) - if err != nil { - log.Error("delete range failed", zap.Error(err)) - } - return &kvrpcpb.DeleteRangeResponse{}, nil -} - -// RawKV commands. - -// RawGet implements the tikvpb.TikvServer interface. -func (svr *Server) RawGet(context.Context, *kvrpcpb.RawGetRequest) (*kvrpcpb.RawGetResponse, error) { - return &kvrpcpb.RawGetResponse{}, nil -} - -// RawPut implements the tikvpb.TikvServer interface. -func (svr *Server) RawPut(context.Context, *kvrpcpb.RawPutRequest) (*kvrpcpb.RawPutResponse, error) { - return &kvrpcpb.RawPutResponse{}, nil -} - -// RawDelete implements the tikvpb.TikvServer interface. -func (svr *Server) RawDelete(context.Context, *kvrpcpb.RawDeleteRequest) (*kvrpcpb.RawDeleteResponse, error) { - return &kvrpcpb.RawDeleteResponse{}, nil -} - -// RawScan implements the tikvpb.TikvServer interface. -func (svr *Server) RawScan(context.Context, *kvrpcpb.RawScanRequest) (*kvrpcpb.RawScanResponse, error) { - return &kvrpcpb.RawScanResponse{}, nil -} - -// RawBatchDelete implements the tikvpb.TikvServer interface. -func (svr *Server) RawBatchDelete(context.Context, *kvrpcpb.RawBatchDeleteRequest) (*kvrpcpb.RawBatchDeleteResponse, error) { - return &kvrpcpb.RawBatchDeleteResponse{}, nil -} - -// RawBatchGet implements the tikvpb.TikvServer interface. -func (svr *Server) RawBatchGet(context.Context, *kvrpcpb.RawBatchGetRequest) (*kvrpcpb.RawBatchGetResponse, error) { - return &kvrpcpb.RawBatchGetResponse{}, nil -} - -// RawBatchPut implements the tikvpb.TikvServer interface. -func (svr *Server) RawBatchPut(context.Context, *kvrpcpb.RawBatchPutRequest) (*kvrpcpb.RawBatchPutResponse, error) { - return &kvrpcpb.RawBatchPutResponse{}, nil -} - -// RawBatchScan implements the tikvpb.TikvServer interface. -func (svr *Server) RawBatchScan(context.Context, *kvrpcpb.RawBatchScanRequest) (*kvrpcpb.RawBatchScanResponse, error) { - return &kvrpcpb.RawBatchScanResponse{}, nil -} - -// RawDeleteRange implements the tikvpb.TikvServer interface. -func (svr *Server) RawDeleteRange(context.Context, *kvrpcpb.RawDeleteRangeRequest) (*kvrpcpb.RawDeleteRangeResponse, error) { - return &kvrpcpb.RawDeleteRangeResponse{}, nil -} - -// SQL push down commands. - -// Coprocessor implements the tikvpb.TikvServer interface. -func (svr *Server) Coprocessor(ctx context.Context, req *coprocessor.Request) (*coprocessor.Response, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "Coprocessor") - if err != nil { - return &coprocessor.Response{OtherError: convertToKeyError(err).String()}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &coprocessor.Response{RegionError: reqCtx.regErr}, nil - } - resp := cophandler.HandleCopRequest(reqCtx.getDBReader(), svr.mvccStore.lockStore, req) - resp.BatchResponses = svr.StoreBatchCoprocessor(ctx, req) - return resp, nil -} - -// StoreBatchCoprocessor handle batched tasks in the same store. -func (svr *Server) StoreBatchCoprocessor(ctx context.Context, req *coprocessor.Request) []*coprocessor.StoreBatchTaskResponse { - if len(req.Tasks) == 0 { - return nil - } - tasks := req.Tasks - batchResps := make([]*coprocessor.StoreBatchTaskResponse, 0, len(tasks)) - handleBatchResp := func(task *coprocessor.StoreBatchTask) { - var err error - batchResp := &coprocessor.StoreBatchTaskResponse{ - TaskId: task.TaskId, - } - defer func() { - if err != nil { - batchResp.OtherError = err.Error() - } - batchResps = append(batchResps, batchResp) - }() - bytes, err := req.Marshal() - if err != nil { - return - } - taskReq := &coprocessor.Request{} - // deep clone req - if err = taskReq.Unmarshal(bytes); err != nil { - return - } - taskReq.Tasks = nil - taskReq.IsCacheEnabled = false - taskReq.Ranges = task.Ranges - taskReq.Context.RegionId = task.RegionId - taskReq.Context.RegionEpoch = task.RegionEpoch - taskReq.Context.Peer = task.Peer - resp, err := svr.Coprocessor(ctx, taskReq) - if err != nil { - return - } - batchResp.RegionError = resp.RegionError - batchResp.Locked = resp.Locked - batchResp.OtherError = resp.OtherError - batchResp.ExecDetailsV2 = resp.ExecDetailsV2 - batchResp.Data = resp.Data - } - for _, task := range tasks { - handleBatchResp(task) - } - return batchResps -} - -// CoprocessorStream implements the tikvpb.TikvServer interface. -func (svr *Server) CoprocessorStream(*coprocessor.Request, tikvpb.Tikv_CoprocessorStreamServer) error { - // TODO - return nil -} - -// GetLockWaitHistory implements the tikvpb.TikvServer interface. -func (svr *Server) GetLockWaitHistory(context.Context, *kvrpcpb.GetLockWaitHistoryRequest) (*kvrpcpb.GetLockWaitHistoryResponse, error) { - return &kvrpcpb.GetLockWaitHistoryResponse{}, nil -} - -// RegionError represents a region error -type RegionError struct { - err *errorpb.Error -} - -// Error implements Error method. -func (regionError *RegionError) Error() string { - return regionError.err.Message -} - -// BatchCoprocessor implements the tikvpb.TikvServer interface. -func (svr *Server) BatchCoprocessor(req *coprocessor.BatchRequest, batchCopServer tikvpb.Tikv_BatchCoprocessorServer) error { - reqCtxs := make([]*requestCtx, 0, len(req.Regions)) - defer func() { - for _, ctx := range reqCtxs { - ctx.finish() - } - }() - if req.TableRegions != nil { - // Support PartitionTableScan for BatchCop - req.Regions = req.Regions[:] - for _, tr := range req.TableRegions { - req.Regions = append(req.Regions, tr.Regions...) - } - } - for _, ri := range req.Regions { - cop := coprocessor.Request{ - Tp: kv.ReqTypeDAG, - Data: req.Data, - StartTs: req.StartTs, - Ranges: ri.Ranges, - } - regionCtx := *req.Context - regionCtx.RegionEpoch = ri.RegionEpoch - regionCtx.RegionId = ri.RegionId - cop.Context = ®ionCtx - - reqCtx, err := newRequestCtx(svr, ®ionCtx, "Coprocessor") - if err != nil { - return err - } - reqCtxs = append(reqCtxs, reqCtx) - if reqCtx.regErr != nil { - return &RegionError{err: reqCtx.regErr} - } - copResponse := cophandler.HandleCopRequestWithMPPCtx(reqCtx.getDBReader(), svr.mvccStore.lockStore, &cop, nil) - err = batchCopServer.Send(&coprocessor.BatchResponse{Data: copResponse.Data}) - if err != nil { - return err - } - } - return nil -} - -// RawCoprocessor implements the tikvpb.TikvServer interface. -func (svr *Server) RawCoprocessor(context.Context, *kvrpcpb.RawCoprocessorRequest) (*kvrpcpb.RawCoprocessorResponse, error) { - panic("unimplemented") -} - -func (mrm *MockRegionManager) removeMPPTaskHandler(taskID int64, storeID uint64) error { - set := mrm.getMPPTaskSet(storeID) - if set == nil { - return errors.New("cannot find mpp task set for store") - } - set.mu.Lock() - defer set.mu.Unlock() - if _, ok := set.taskHandlers[taskID]; ok { - delete(set.taskHandlers, taskID) - return nil - } - return errors.New("cannot find mpp task") -} - -// IsAlive implements the tikvpb.TikvServer interface. -func (svr *Server) IsAlive(_ context.Context, _ *mpp.IsAliveRequest) (*mpp.IsAliveResponse, error) { - return &mpp.IsAliveResponse{Available: true}, nil -} - -// DispatchMPPTask implements the tikvpb.TikvServer interface. -func (svr *Server) DispatchMPPTask(_ context.Context, _ *mpp.DispatchTaskRequest) (*mpp.DispatchTaskResponse, error) { - panic("todo") -} - -func (svr *Server) executeMPPDispatch(ctx context.Context, req *mpp.DispatchTaskRequest, storeAddr string, storeID uint64, handler *cophandler.MPPTaskHandler) error { - var reqCtx *requestCtx - if len(req.TableRegions) > 0 { - // Simple unistore logic for PartitionTableScan. - for _, tr := range req.TableRegions { - req.Regions = append(req.Regions, tr.Regions...) - } - } - - if len(req.Regions) > 0 { - kvContext := &kvrpcpb.Context{ - RegionId: req.Regions[0].RegionId, - RegionEpoch: req.Regions[0].RegionEpoch, - // this is a hack to reuse task id in kvContext to pass mpp task id - TaskId: uint64(handler.Meta.TaskId), - Peer: &metapb.Peer{StoreId: storeID}, - } - var err error - reqCtx, err = newRequestCtx(svr, kvContext, "Mpp") - if err != nil { - return errors.Trace(err) - } - } - copReq := &coprocessor.Request{ - Tp: kv.ReqTypeDAG, - Data: req.EncodedPlan, - StartTs: req.Meta.StartTs, - } - for _, regionMeta := range req.Regions { - copReq.Ranges = append(copReq.Ranges, regionMeta.Ranges...) - } - var dbreader *dbreader.DBReader - if reqCtx != nil { - dbreader = reqCtx.getDBReader() - } - go func() { - resp := cophandler.HandleCopRequestWithMPPCtx(dbreader, svr.mvccStore.lockStore, copReq, &cophandler.MPPCtx{ - RPCClient: svr.RPCClient, - StoreAddr: storeAddr, - TaskHandler: handler, - Ctx: ctx, - }) - handler.Err = svr.RemoveMPPTaskHandler(req.Meta.TaskId, storeID) - if len(resp.OtherError) > 0 { - handler.Err = errors.New(resp.OtherError) - } - if reqCtx != nil { - reqCtx.finish() - } - }() - return nil -} - -// DispatchMPPTaskWithStoreID implements the tikvpb.TikvServer interface. -func (svr *Server) DispatchMPPTaskWithStoreID(ctx context.Context, req *mpp.DispatchTaskRequest, storeID uint64) (*mpp.DispatchTaskResponse, error) { - mppHandler, err := svr.CreateMPPTaskHandler(req.Meta, storeID) - if err != nil { - return nil, errors.Trace(err) - } - storeAddr, err := svr.GetStoreAddrByStoreID(storeID) - if err != nil { - return nil, err - } - err = svr.executeMPPDispatch(ctx, req, storeAddr, storeID, mppHandler) - resp := &mpp.DispatchTaskResponse{} - if err != nil { - resp.Error = &mpp.Error{Msg: err.Error()} - } - return resp, nil -} - -// CancelMPPTask implements the tikvpb.TikvServer interface. -func (svr *Server) CancelMPPTask(_ context.Context, _ *mpp.CancelTaskRequest) (*mpp.CancelTaskResponse, error) { - panic("todo") -} - -// GetMPPTaskHandler implements the tikvpb.TikvServer interface. -func (svr *Server) GetMPPTaskHandler(taskID int64, storeID uint64) (*cophandler.MPPTaskHandler, error) { - if mrm, ok := svr.regionManager.(*MockRegionManager); ok { - set := mrm.getMPPTaskSet(storeID) - if set == nil { - return nil, errors.New("cannot find mpp task set for store") - } - set.mu.Lock() - defer set.mu.Unlock() - if handler, ok := set.taskHandlers[taskID]; ok { - return handler, nil - } - return nil, nil - } - return nil, errors.New("Only mock region mgr supports get mpp task") -} - -// RemoveMPPTaskHandler implements the tikvpb.TikvServer interface. -func (svr *Server) RemoveMPPTaskHandler(taskID int64, storeID uint64) error { - if mrm, ok := svr.regionManager.(*MockRegionManager); ok { - err := mrm.removeMPPTaskHandler(taskID, storeID) - return errors.Trace(err) - } - return errors.New("Only mock region mgr supports remove mpp task") -} - -// CreateMPPTaskHandler implements the tikvpb.TikvServer interface. -func (svr *Server) CreateMPPTaskHandler(meta *mpp.TaskMeta, storeID uint64) (*cophandler.MPPTaskHandler, error) { - if mrm, ok := svr.regionManager.(*MockRegionManager); ok { - set := mrm.getMPPTaskSet(storeID) - if set == nil { - return nil, errors.New("cannot find mpp task set for store") - } - set.mu.Lock() - defer set.mu.Unlock() - if handler, ok := set.taskHandlers[meta.TaskId]; ok { - return handler, errors.Errorf("Task %d has been created", meta.TaskId) - } - handler := &cophandler.MPPTaskHandler{ - TunnelSet: make(map[int64]*cophandler.ExchangerTunnel), - Meta: meta, - RPCClient: svr.RPCClient, - } - set.taskHandlers[meta.TaskId] = handler - return handler, nil - } - return nil, errors.New("Only mock region mgr supports get mpp task") -} - -// EstablishMPPConnection implements the tikvpb.TikvServer interface. -func (svr *Server) EstablishMPPConnection(*mpp.EstablishMPPConnectionRequest, tikvpb.Tikv_EstablishMPPConnectionServer) error { - panic("todo") -} - -// EstablishMPPConnectionWithStoreID implements the tikvpb.TikvServer interface. -func (svr *Server) EstablishMPPConnectionWithStoreID(req *mpp.EstablishMPPConnectionRequest, server tikvpb.Tikv_EstablishMPPConnectionServer, storeID uint64) error { - var ( - mppHandler *cophandler.MPPTaskHandler - err error - ) - maxRetryTime := 5 - for i := 0; i < maxRetryTime; i++ { - mppHandler, err = svr.GetMPPTaskHandler(req.SenderMeta.TaskId, storeID) - if err != nil { - return errors.Trace(err) - } - if mppHandler != nil { - break - } - time.Sleep(time.Second) - } - if mppHandler == nil { - return errors.New("task not found") - } - ctx1, cancel := context.WithCancel(context.Background()) - defer cancel() - tunnel, err := mppHandler.HandleEstablishConn(ctx1, req) - if err != nil { - return errors.Trace(err) - } - var sendError error - for sendError == nil { - chunk, err := tunnel.RecvChunk() - if err != nil { - sendError = server.Send(&mpp.MPPDataPacket{Error: &mpp.Error{Msg: err.Error()}}) - break - } - if chunk == nil { - // todo return io.EOF error? - break - } - res := tipb.SelectResponse{ - Chunks: []tipb.Chunk{*chunk}, - } - raw, err := res.Marshal() - if err != nil { - sendError = server.Send(&mpp.MPPDataPacket{Error: &mpp.Error{Msg: err.Error()}}) - break - } - sendError = server.Send(&mpp.MPPDataPacket{Data: raw}) - } - return sendError -} - -// Raft commands (tikv <-> tikv). - -// Raft implements the tikvpb.TikvServer interface. -func (svr *Server) Raft(stream tikvpb.Tikv_RaftServer) error { - return svr.innerServer.Raft(stream) -} - -// Snapshot implements the tikvpb.TikvServer interface. -func (svr *Server) Snapshot(stream tikvpb.Tikv_SnapshotServer) error { - return svr.innerServer.Snapshot(stream) -} - -// BatchRaft implements the tikvpb.TikvServer interface. -func (svr *Server) BatchRaft(stream tikvpb.Tikv_BatchRaftServer) error { - return svr.innerServer.BatchRaft(stream) -} - -// Region commands. - -// SplitRegion implements the tikvpb.TikvServer interface. -func (svr *Server) SplitRegion(ctx context.Context, req *kvrpcpb.SplitRegionRequest) (*kvrpcpb.SplitRegionResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "SplitRegion") - if err != nil { - return &kvrpcpb.SplitRegionResponse{RegionError: &errorpb.Error{Message: err.Error()}}, nil - } - defer reqCtx.finish() - return svr.regionManager.SplitRegion(req), nil -} - -// Compact implements the tikvpb.TikvServer interface. -func (svr *Server) Compact(ctx context.Context, req *kvrpcpb.CompactRequest) (*kvrpcpb.CompactResponse, error) { - panic("unimplemented") -} - -// ReadIndex implements the tikvpb.TikvServer interface. -func (svr *Server) ReadIndex(context.Context, *kvrpcpb.ReadIndexRequest) (*kvrpcpb.ReadIndexResponse, error) { - // TODO: - return &kvrpcpb.ReadIndexResponse{}, nil -} - -// transaction debugger commands. - -// MvccGetByKey implements the tikvpb.TikvServer interface. -func (svr *Server) MvccGetByKey(ctx context.Context, req *kvrpcpb.MvccGetByKeyRequest) (*kvrpcpb.MvccGetByKeyResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "MvccGetByKey") - if err != nil { - return &kvrpcpb.MvccGetByKeyResponse{Error: err.Error()}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.MvccGetByKeyResponse{RegionError: reqCtx.regErr}, nil - } - resp := new(kvrpcpb.MvccGetByKeyResponse) - mvccInfo, err := svr.mvccStore.MvccGetByKey(reqCtx, req.GetKey()) - if err != nil { - resp.Error = err.Error() - } - resp.Info = mvccInfo - return resp, nil -} - -// MvccGetByStartTs implements the tikvpb.TikvServer interface. -func (svr *Server) MvccGetByStartTs(ctx context.Context, req *kvrpcpb.MvccGetByStartTsRequest) (*kvrpcpb.MvccGetByStartTsResponse, error) { - reqCtx, err := newRequestCtx(svr, req.Context, "MvccGetByStartTs") - if err != nil { - return &kvrpcpb.MvccGetByStartTsResponse{Error: err.Error()}, nil - } - defer reqCtx.finish() - if reqCtx.regErr != nil { - return &kvrpcpb.MvccGetByStartTsResponse{RegionError: reqCtx.regErr}, nil - } - resp := new(kvrpcpb.MvccGetByStartTsResponse) - mvccInfo, key, err := svr.mvccStore.MvccGetByStartTs(reqCtx, req.StartTs) - if err != nil { - resp.Error = err.Error() - } - resp.Info = mvccInfo - resp.Key = key - return resp, nil -} - -// UnsafeDestroyRange implements the tikvpb.TikvServer interface. -func (svr *Server) UnsafeDestroyRange(ctx context.Context, req *kvrpcpb.UnsafeDestroyRangeRequest) (*kvrpcpb.UnsafeDestroyRangeResponse, error) { - start, end := req.GetStartKey(), req.GetEndKey() - svr.mvccStore.DeleteFileInRange(start, end) - return &kvrpcpb.UnsafeDestroyRangeResponse{}, nil -} - -// GetWaitForEntries tries to get the waitFor entries -// deadlock detection related services -func (svr *Server) GetWaitForEntries(ctx context.Context, - req *deadlockPb.WaitForEntriesRequest) (*deadlockPb.WaitForEntriesResponse, error) { - // TODO - return &deadlockPb.WaitForEntriesResponse{}, nil -} - -// Detect will handle detection rpc from other nodes -func (svr *Server) Detect(stream deadlockPb.Deadlock_DetectServer) error { - for { - req, err := stream.Recv() - if err != nil { - if err == io.EOF { - break - } - return err - } - if !svr.mvccStore.DeadlockDetectSvr.IsLeader() { - log.Warn("detection requests received on non leader node") - break - } - resp := svr.mvccStore.DeadlockDetectSvr.Detect(req) - if resp != nil { - if sendErr := stream.Send(resp); sendErr != nil { - log.Error("send deadlock response failed", zap.Error(sendErr)) - break - } - } - } - return nil -} - -// CheckLockObserver implements the tikvpb.TikvServer interface. -func (svr *Server) CheckLockObserver(context.Context, *kvrpcpb.CheckLockObserverRequest) (*kvrpcpb.CheckLockObserverResponse, error) { - // TODO: implement Observer - return &kvrpcpb.CheckLockObserverResponse{IsClean: true}, nil -} - -// PhysicalScanLock implements the tikvpb.TikvServer interface. -func (svr *Server) PhysicalScanLock(ctx context.Context, req *kvrpcpb.PhysicalScanLockRequest) (*kvrpcpb.PhysicalScanLockResponse, error) { - resp := &kvrpcpb.PhysicalScanLockResponse{} - resp.Locks = svr.mvccStore.PhysicalScanLock(req.StartKey, req.MaxTs, int(req.Limit)) - return resp, nil -} - -// RegisterLockObserver implements the tikvpb.TikvServer interface. -func (svr *Server) RegisterLockObserver(context.Context, *kvrpcpb.RegisterLockObserverRequest) (*kvrpcpb.RegisterLockObserverResponse, error) { - // TODO: implement Observer - return &kvrpcpb.RegisterLockObserverResponse{}, nil -} - -// RemoveLockObserver implements the tikvpb.TikvServer interface. -func (svr *Server) RemoveLockObserver(context.Context, *kvrpcpb.RemoveLockObserverRequest) (*kvrpcpb.RemoveLockObserverResponse, error) { - // TODO: implement Observer - return &kvrpcpb.RemoveLockObserverResponse{}, nil -} - -// CheckLeader implements the tikvpb.TikvServer interface. -func (svr *Server) CheckLeader(context.Context, *kvrpcpb.CheckLeaderRequest) (*kvrpcpb.CheckLeaderResponse, error) { - panic("unimplemented") -} - -// RawCompareAndSwap implements the tikvpb.TikvServer interface. -func (svr *Server) RawCompareAndSwap(context.Context, *kvrpcpb.RawCASRequest) (*kvrpcpb.RawCASResponse, error) { - panic("implement me") -} - -// GetStoreSafeTS implements the tikvpb.TikvServer interface. -func (svr *Server) GetStoreSafeTS(context.Context, *kvrpcpb.StoreSafeTSRequest) (*kvrpcpb.StoreSafeTSResponse, error) { - return &kvrpcpb.StoreSafeTSResponse{}, nil -} - -// GetLockWaitInfo implements the tikvpb.TikvServer interface. -func (svr *Server) GetLockWaitInfo(context.Context, *kvrpcpb.GetLockWaitInfoRequest) (*kvrpcpb.GetLockWaitInfoResponse, error) { - panic("unimplemented") -} - -// RawChecksum implements the tikvpb.TikvServer interface. -func (svr *Server) RawChecksum(context.Context, *kvrpcpb.RawChecksumRequest) (*kvrpcpb.RawChecksumResponse, error) { - panic("unimplemented") -} - -func convertToKeyError(err error) *kvrpcpb.KeyError { - if err == nil { - return nil - } - causeErr := errors.Cause(err) - switch x := causeErr.(type) { - case *kverrors.ErrLocked: - return &kvrpcpb.KeyError{ - Locked: x.Lock.ToLockInfo(x.Key), - } - case kverrors.ErrRetryable: - return &kvrpcpb.KeyError{ - Retryable: x.Error(), - } - case *kverrors.ErrKeyAlreadyExists: - return &kvrpcpb.KeyError{ - AlreadyExist: &kvrpcpb.AlreadyExist{ - Key: x.Key, - }, - } - case *kverrors.ErrConflict: - return &kvrpcpb.KeyError{ - Conflict: &kvrpcpb.WriteConflict{ - StartTs: x.StartTS, - ConflictTs: x.ConflictTS, - ConflictCommitTs: x.ConflictCommitTS, - Key: x.Key, - Reason: x.Reason, - }, - } - case *kverrors.ErrDeadlock: - return &kvrpcpb.KeyError{ - Deadlock: &kvrpcpb.Deadlock{ - LockKey: x.LockKey, - LockTs: x.LockTS, - DeadlockKeyHash: x.DeadlockKeyHash, - WaitChain: x.WaitChain, - }, - } - case *kverrors.ErrCommitExpire: - return &kvrpcpb.KeyError{ - CommitTsExpired: &kvrpcpb.CommitTsExpired{ - StartTs: x.StartTs, - AttemptedCommitTs: x.CommitTs, - Key: x.Key, - MinCommitTs: x.MinCommitTs, - }, - } - case *kverrors.ErrTxnNotFound: - return &kvrpcpb.KeyError{ - TxnNotFound: &kvrpcpb.TxnNotFound{ - StartTs: x.StartTS, - PrimaryKey: x.PrimaryKey, - }, - } - case *kverrors.ErrAssertionFailed: - return &kvrpcpb.KeyError{ - AssertionFailed: &kvrpcpb.AssertionFailed{ - StartTs: x.StartTS, - Key: x.Key, - Assertion: x.Assertion, - ExistingStartTs: x.ExistingStartTS, - ExistingCommitTs: x.ExistingCommitTS, - }, - } - case *kverrors.ErrPrimaryMismatch: - return &kvrpcpb.KeyError{ - PrimaryMismatch: &kvrpcpb.PrimaryMismatch{ - LockInfo: x.Lock.ToLockInfo(x.Key), - }, - } - default: - return &kvrpcpb.KeyError{ - Abort: err.Error(), - } - } -} - -func convertToPBError(err error) (*kvrpcpb.KeyError, *errorpb.Error) { - if regErr := extractRegionError(err); regErr != nil { - return nil, regErr - } - return convertToKeyError(err), nil -} - -func convertToPBErrors(err error) ([]*kvrpcpb.KeyError, *errorpb.Error) { - if err != nil { - if regErr := extractRegionError(err); regErr != nil { - return nil, regErr - } - return []*kvrpcpb.KeyError{convertToKeyError(err)}, nil - } - return nil, nil -} - -func extractRegionError(err error) *errorpb.Error { - if pbError, ok := err.(*pberror.PBError); ok { - return pbError.RequestErr - } - return nil -} diff --git a/store/mockstore/unistore/tikv/write.go b/store/mockstore/unistore/tikv/write.go deleted file mode 100644 index 5947fc2b7c786..0000000000000 --- a/store/mockstore/unistore/tikv/write.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2019-present PingCAP, Inc. -// -// 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. - -package tikv - -import ( - "bytes" - "math" - "sync" - "sync/atomic" - - "github.com/pingcap/badger" - "github.com/pingcap/badger/y" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/log" - "github.com/pingcap/tidb/store/mockstore/unistore/lockstore" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/dbreader" - "github.com/pingcap/tidb/store/mockstore/unistore/tikv/mvcc" - "github.com/pingcap/tidb/util/mathutil" - "go.uber.org/zap" -) - -const ( - batchChanSize = 1024 -) - -type writeDBBatch struct { - entries []*badger.Entry - err error - wg sync.WaitGroup -} - -func newWriteDBBatch() *writeDBBatch { - return &writeDBBatch{} -} - -func (batch *writeDBBatch) set(key y.Key, val []byte, userMeta []byte) { - batch.entries = append(batch.entries, &badger.Entry{ - Key: key, - Value: val, - UserMeta: userMeta, - }) -} - -// delete is a badger level operation, only used in DeleteRange, so we don't need to set UserMeta. -// Then we can tell the entry is delete if UserMeta is nil. -func (batch *writeDBBatch) delete(key y.Key) { - batch.entries = append(batch.entries, &badger.Entry{ - Key: key, - }) -} - -type writeLockBatch struct { - entries []*badger.Entry - err error - wg sync.WaitGroup -} - -func (batch *writeLockBatch) set(key, val []byte) { - batch.entries = append(batch.entries, &badger.Entry{ - Key: y.KeyWithTs(key, 0), - Value: val, - UserMeta: mvcc.LockUserMetaNone, - }) -} - -func (batch *writeLockBatch) delete(key []byte) { - batch.entries = append(batch.entries, &badger.Entry{ - Key: y.KeyWithTs(key, 0), - UserMeta: mvcc.LockUserMetaDelete, - }) -} - -type writeDBWorker struct { - batchCh chan *writeDBBatch - writer *dbWriter -} - -func (w writeDBWorker) run() { - defer w.writer.wg.Done() - var batches []*writeDBBatch - for { - for i := range batches { - batches[i] = nil - } - batches = batches[:0] - select { - case <-w.writer.closeCh: - return - case batch := <-w.batchCh: - batches = append(batches, batch) - } - chLen := len(w.batchCh) - for i := 0; i < chLen; i++ { - batches = append(batches, <-w.batchCh) - } - if len(batches) > 0 { - w.updateBatchGroup(batches) - } - } -} - -func (w writeDBWorker) updateBatchGroup(batchGroup []*writeDBBatch) { - e := w.writer.bundle.DB.Update(func(txn *badger.Txn) error { - for _, batch := range batchGroup { - for _, entry := range batch.entries { - var err error - if len(entry.UserMeta) == 0 { - entry.SetDelete() - } - err = txn.SetEntry(entry) - if err != nil { - return err - } - } - } - return nil - }) - for _, batch := range batchGroup { - batch.err = e - batch.wg.Done() - } -} - -type writeLockWorker struct { - batchCh chan *writeLockBatch - writer *dbWriter -} - -func (w writeLockWorker) run() { - defer w.writer.wg.Done() - ls := w.writer.bundle.LockStore - var batches []*writeLockBatch - for { - for i := range batches { - batches[i] = nil - } - batches = batches[:0] - select { - case <-w.writer.closeCh: - return - case batch := <-w.batchCh: - batches = append(batches, batch) - } - chLen := len(w.batchCh) - for i := 0; i < chLen; i++ { - batches = append(batches, <-w.batchCh) - } - hint := new(lockstore.Hint) - var delCnt, insertCnt int - for _, batch := range batches { - for _, entry := range batch.entries { - switch entry.UserMeta[0] { - case mvcc.LockUserMetaDeleteByte: - delCnt++ - // Ignore if the key doesn't exist - ls.DeleteWithHint(entry.Key.UserKey, hint) - default: - insertCnt++ - ls.PutWithHint(entry.Key.UserKey, entry.Value, hint) - } - } - batch.wg.Done() - } - } -} - -type dbWriter struct { - bundle *mvcc.DBBundle - dbCh chan<- *writeDBBatch - lockCh chan<- *writeLockBatch - wg sync.WaitGroup - closeCh chan struct{} - latestTS uint64 -} - -// NewDBWriter returns a new DBWriter. -func NewDBWriter(bundle *mvcc.DBBundle) mvcc.DBWriter { - return &dbWriter{ - bundle: bundle, - closeCh: make(chan struct{}), - } -} - -func (writer *dbWriter) Open() { - writer.wg.Add(2) - - dbCh := make(chan *writeDBBatch, batchChanSize) - writer.dbCh = dbCh - go writeDBWorker{ - batchCh: dbCh, - writer: writer, - }.run() - - lockCh := make(chan *writeLockBatch, batchChanSize) - writer.lockCh = lockCh - go writeLockWorker{ - batchCh: lockCh, - writer: writer, - }.run() -} - -func (writer *dbWriter) Close() { - close(writer.closeCh) - writer.wg.Wait() -} - -func (writer *dbWriter) Write(batch mvcc.WriteBatch) error { - wb := batch.(*writeBatch) - if len(wb.dbBatch.entries) > 0 { - wb.dbBatch.wg.Add(1) - writer.dbCh <- &wb.dbBatch - wb.dbBatch.wg.Wait() - err := wb.dbBatch.err - if err != nil { - return err - } - } - if len(wb.lockBatch.entries) > 0 { - // We must delete lock after commit succeed, or there will be inconsistency. - wb.lockBatch.wg.Add(1) - writer.lockCh <- &wb.lockBatch - wb.lockBatch.wg.Wait() - return wb.lockBatch.err - } - return nil -} - -type writeBatch struct { - startTS uint64 - commitTS uint64 - dbBatch writeDBBatch - lockBatch writeLockBatch -} - -func (wb *writeBatch) Prewrite(key []byte, lock *mvcc.Lock) { - wb.lockBatch.set(key, lock.MarshalBinary()) -} - -func (wb *writeBatch) Commit(key []byte, lock *mvcc.Lock) { - userMeta := mvcc.NewDBUserMeta(wb.startTS, wb.commitTS) - k := y.KeyWithTs(key, wb.commitTS) - if lock.Op == uint8(kvrpcpb.Op_PessimisticLock) { - log.Info("removing a pessimistic lock when committing", zap.Binary("key", key), zap.Uint64("startTS", wb.startTS), zap.Uint64("commitTS", wb.commitTS)) - // Write nothing as if PessimisticRollback is called. - } else if lock.Op != uint8(kvrpcpb.Op_Lock) { - wb.dbBatch.set(k, lock.Value, userMeta) - } else if bytes.Equal(key, lock.Primary) { - opLockKey := y.KeyWithTs(mvcc.EncodeExtraTxnStatusKey(key, wb.startTS), wb.startTS) - wb.dbBatch.set(opLockKey, nil, userMeta) - } - wb.lockBatch.delete(key) -} - -func (wb *writeBatch) Rollback(key []byte, deleteLock bool) { - rollbackKey := y.KeyWithTs(mvcc.EncodeExtraTxnStatusKey(key, wb.startTS), wb.startTS) - userMeta := mvcc.NewDBUserMeta(wb.startTS, 0) - wb.dbBatch.set(rollbackKey, nil, userMeta) - if deleteLock { - wb.lockBatch.delete(key) - } -} - -func (wb *writeBatch) PessimisticLock(key []byte, lock *mvcc.Lock) { - wb.lockBatch.set(key, lock.MarshalBinary()) -} - -func (wb *writeBatch) PessimisticRollback(key []byte) { - wb.lockBatch.delete(key) -} - -func (writer *dbWriter) NewWriteBatch(startTS, commitTS uint64, ctx *kvrpcpb.Context) mvcc.WriteBatch { - if commitTS > 0 { - writer.updateLatestTS(commitTS) - } else { - writer.updateLatestTS(startTS) - } - return &writeBatch{ - startTS: startTS, - commitTS: commitTS, - } -} - -func (writer *dbWriter) getLatestTS() uint64 { - return atomic.LoadUint64(&writer.latestTS) -} - -func (writer *dbWriter) updateLatestTS(ts uint64) { - latestTS := writer.getLatestTS() - if ts != math.MaxUint64 && ts > latestTS { - atomic.CompareAndSwapUint64(&writer.latestTS, latestTS, ts) - } -} - -const delRangeBatchSize = 4096 - -func (writer *dbWriter) DeleteRange(startKey, endKey []byte, latchHandle mvcc.LatchHandle) error { - keys := make([]y.Key, 0, delRangeBatchSize) - txn := writer.bundle.DB.NewTransaction(false) - defer txn.Discard() - reader := dbreader.NewDBReader(startKey, endKey, txn) - keys = writer.collectRangeKeys(reader.GetIter(), startKey, endKey, keys) - reader.Close() - return writer.deleteKeysInBatch(latchHandle, keys, delRangeBatchSize) -} - -func (writer *dbWriter) collectRangeKeys(it *badger.Iterator, startKey, endKey []byte, keys []y.Key) []y.Key { - if len(endKey) == 0 { - panic("invalid end key") - } - for it.Seek(startKey); it.Valid(); it.Next() { - item := it.Item() - key := item.KeyCopy(nil) - if exceedEndKey(key, endKey) { - break - } - keys = append(keys, y.KeyWithTs(key, item.Version())) - } - return keys -} - -func (writer *dbWriter) deleteKeysInBatch(latchHandle mvcc.LatchHandle, keys []y.Key, batchSize int) error { - for len(keys) > 0 { - batchSize := mathutil.Min(len(keys), batchSize) - batchKeys := keys[:batchSize] - keys = keys[batchSize:] - hashVals := userKeysToHashVals(batchKeys...) - dbBatch := newWriteDBBatch() - for _, key := range batchKeys { - key.Version++ - dbBatch.delete(key) - } - latchHandle.AcquireLatches(hashVals) - dbBatch.wg.Add(1) - writer.dbCh <- dbBatch - dbBatch.wg.Wait() - latchHandle.ReleaseLatches(hashVals) - if dbBatch.err != nil { - return dbBatch.err - } - } - return nil -} diff --git a/store/mockstore/unistore/util/lockwaiter/BUILD.bazel b/store/mockstore/unistore/util/lockwaiter/BUILD.bazel deleted file mode 100644 index 8c873655987a6..0000000000000 --- a/store/mockstore/unistore/util/lockwaiter/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "lockwaiter", - srcs = ["lockwaiter.go"], - importpath = "github.com/pingcap/tidb/store/mockstore/unistore/util/lockwaiter", - visibility = ["//visibility:public"], - deps = [ - "//store/mockstore/unistore/config", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "lockwaiter_test", - timeout = "short", - srcs = [ - "lockwaiter_test.go", - "main_test.go", - ], - embed = [":lockwaiter"], - flaky = True, - deps = [ - "//store/mockstore/unistore/config", - "//testkit/testsetup", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/store/mockstore/unistore/util/lockwaiter/main_test.go b/store/mockstore/unistore/util/lockwaiter/main_test.go deleted file mode 100644 index d9d870c15f67d..0000000000000 --- a/store/mockstore/unistore/util/lockwaiter/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package lockwaiter - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/store/pdtypes/BUILD.bazel b/store/pdtypes/BUILD.bazel deleted file mode 100644 index fe59445be494a..0000000000000 --- a/store/pdtypes/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "pdtypes", - srcs = [ - "api.go", - "config.go", - "placement.go", - "region_tree.go", - "statistics.go", - "typeutil.go", - ], - importpath = "github.com/pingcap/tidb/store/pdtypes", - visibility = ["//visibility:public"], - deps = [ - "@com_github_docker_go_units//:go-units", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_kvproto//pkg/pdpb", - ], -) diff --git a/store/store.go b/store/store.go deleted file mode 100644 index cbc91a4fce259..0000000000000 --- a/store/store.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package store - -import ( - "net/url" - "strings" - "sync" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/pdpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -var stores = make(map[string]kv.Driver) -var storesLock sync.RWMutex - -// Register registers a kv storage with unique name and its associated Driver. -func Register(name string, driver kv.Driver) error { - storesLock.Lock() - defer storesLock.Unlock() - - name = strings.ToLower(name) - - if _, ok := stores[name]; ok { - return errors.Errorf("%s is already registered", name) - } - - stores[name] = driver - return nil -} - -// New creates a kv Storage with path. -// -// The path must be a URL format 'engine://path?params' like the one for -// session.Open() but with the dbname cut off. -// Examples: -// -// goleveldb://relative/path -// boltdb:///absolute/path -// -// The engine should be registered before creating storage. -func New(path string) (kv.Storage, error) { - return newStoreWithRetry(path, util.DefaultMaxRetries) -} - -func newStoreWithRetry(path string, maxRetries int) (kv.Storage, error) { - storeURL, err := url.Parse(path) - if err != nil { - return nil, err - } - - name := strings.ToLower(storeURL.Scheme) - d, ok := loadDriver(name) - if !ok { - return nil, errors.Errorf("invalid uri format, storage %s is not registered", name) - } - - var s kv.Storage - err = util.RunWithRetry(maxRetries, util.RetryInterval, func() (bool, error) { - logutil.BgLogger().Info("new store", zap.String("path", path)) - s, err = d.Open(path) - return isNewStoreRetryableError(err), err - }) - - if err == nil { - logutil.BgLogger().Info("new store with retry success") - } else { - logutil.BgLogger().Warn("new store with retry failed", zap.Error(err)) - } - return s, errors.Trace(err) -} - -func loadDriver(name string) (kv.Driver, bool) { - storesLock.RLock() - defer storesLock.RUnlock() - d, ok := stores[name] - return d, ok -} - -// isOpenRetryableError check if the new store operation should be retried under given error -// currently, it should be retried if: -// -// Transaction conflict and is retryable (kv.IsTxnRetryableError) -// PD is not bootstrapped at the time of request -// Keyspace requested does not exist (request prior to PD keyspace pre-split) -func isNewStoreRetryableError(err error) bool { - if err == nil { - return false - } - return kv.IsTxnRetryableError(err) || IsNotBootstrappedError(err) || IsKeyspaceNotExistError(err) -} - -// IsNotBootstrappedError returns true if the error is pd not bootstrapped error. -func IsNotBootstrappedError(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), pdpb.ErrorType_NOT_BOOTSTRAPPED.String()) -} - -// IsKeyspaceNotExistError returns true the error is caused by keyspace not exists. -func IsKeyspaceNotExistError(err error) bool { - if err == nil { - return false - } - return strings.Contains(err.Error(), pdpb.ErrorType_ENTRY_NOT_FOUND.String()) -} diff --git a/store/store_test.go b/store/store_test.go deleted file mode 100644 index 081462bb9bc85..0000000000000 --- a/store/store_test.go +++ /dev/null @@ -1,875 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package store - -import ( - "context" - "fmt" - "strconv" - "sync" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/store/mockstore" - "github.com/stretchr/testify/require" - kv2 "github.com/tikv/client-go/v2/kv" -) - -const ( - startIndex = 0 - testCount = 2 - indexStep = 2 -) - -type brokenStore struct{} - -func (s *brokenStore) Open(_ string) (kv.Storage, error) { - return nil, kv.ErrTxnRetryable -} - -func insertData(t *testing.T, txn kv.Transaction) { - for i := startIndex; i < testCount; i++ { - val := encodeInt(i * indexStep) - err := txn.Set(val, val) - require.NoError(t, err) - } -} - -func mustDel(t *testing.T, txn kv.Transaction) { - for i := startIndex; i < testCount; i++ { - val := encodeInt(i * indexStep) - err := txn.Delete(val) - require.NoError(t, err) - } -} - -func encodeInt(n int) []byte { - return []byte(fmt.Sprintf("%010d", n)) -} - -func decodeInt(s []byte) int { - var n int - _, _ = fmt.Sscanf(string(s), "%010d", &n) - return n -} - -func valToStr(iter kv.Iterator) string { - val := iter.Value() - return string(val) -} - -func checkSeek(t *testing.T, txn kv.Transaction) { - for i := startIndex; i < testCount; i++ { - val := encodeInt(i * indexStep) - iter, err := txn.Iter(val, nil) - require.NoError(t, err) - require.Equal(t, val, []byte(iter.Key())) - require.Equal(t, i*indexStep, decodeInt([]byte(valToStr(iter)))) - iter.Close() - } - - // Test iterator Next() - for i := startIndex; i < testCount-1; i++ { - val := encodeInt(i * indexStep) - iter, err := txn.Iter(val, nil) - require.NoError(t, err) - require.Equal(t, val, []byte(iter.Key())) - require.Equal(t, string(val), valToStr(iter)) - - err = iter.Next() - require.NoError(t, err) - require.True(t, iter.Valid()) - - val = encodeInt((i + 1) * indexStep) - require.Equal(t, val, []byte(iter.Key())) - require.Equal(t, string(val), valToStr(iter)) - iter.Close() - } - - // Non exist and beyond maximum seek test - iter, err := txn.Iter(encodeInt(testCount*indexStep), nil) - require.NoError(t, err) - require.False(t, iter.Valid()) - - // Non exist but between existing keys seek test, - // it returns the smallest key that larger than the one we are seeking - inBetween := encodeInt((testCount-1)*indexStep - 1) - last := encodeInt((testCount - 1) * indexStep) - iter, err = txn.Iter(inBetween, nil) - require.NoError(t, err) - require.True(t, iter.Valid()) - require.NotEqual(t, inBetween, []byte(iter.Key())) - require.Equal(t, last, []byte(iter.Key())) - iter.Close() -} - -func mustNotGet(t *testing.T, txn kv.Transaction) { - for i := startIndex; i < testCount; i++ { - s := encodeInt(i * indexStep) - _, err := txn.Get(context.TODO(), s) - require.Error(t, err) - } -} - -func mustGet(t *testing.T, txn kv.Transaction) { - for i := startIndex; i < testCount; i++ { - s := encodeInt(i * indexStep) - val, err := txn.Get(context.TODO(), s) - require.NoError(t, err) - require.Equal(t, string(s), string(val)) - } -} - -func TestNew(t *testing.T) { - store, err := New("goleveldb://relative/path") - require.Error(t, err) - require.Nil(t, store) -} - -func TestGetSet(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - insertData(t, txn) - - mustGet(t, txn) - - // Check transaction results - err = txn.Commit(context.Background()) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - - mustGet(t, txn) - mustDel(t, txn) -} - -func TestSeek(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - insertData(t, txn) - checkSeek(t, txn) - - // Check transaction results - err = txn.Commit(context.Background()) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - - checkSeek(t, txn) - mustDel(t, txn) -} - -func TestInc(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - key := []byte("incKey") - n, err := kv.IncInt64(txn, key, 100) - require.NoError(t, err) - require.Equal(t, int64(100), n) - - // Check transaction results - err = txn.Commit(context.Background()) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - - n, err = kv.IncInt64(txn, key, -200) - require.NoError(t, err) - require.Equal(t, int64(-100), n) - - err = txn.Delete(key) - require.NoError(t, err) - - n, err = kv.IncInt64(txn, key, 100) - require.NoError(t, err) - require.Equal(t, int64(100), n) - - err = txn.Delete(key) - require.NoError(t, err) - - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestDelete(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - insertData(t, txn) - - mustDel(t, txn) - - mustNotGet(t, txn) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - // Try get - txn, err = store.Begin() - require.NoError(t, err) - - mustNotGet(t, txn) - - // Insert again - insertData(t, txn) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - // Delete all - txn, err = store.Begin() - require.NoError(t, err) - - mustDel(t, txn) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - - mustNotGet(t, txn) - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestDelete2(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - val := []byte("test") - require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000001_0003"), val)) - require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000001_0004"), val)) - require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000002_0003"), val)) - require.NoError(t, txn.Set([]byte("DATA_test_tbl_department_record__0000000002_0004"), val)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - // Delete all - txn, err = store.Begin() - require.NoError(t, err) - - it, err := txn.Iter([]byte("DATA_test_tbl_department_record__0000000001_0003"), nil) - require.NoError(t, err) - for it.Valid() { - err = txn.Delete(it.Key()) - require.NoError(t, err) - err = it.Next() - require.NoError(t, err) - } - err = txn.Commit(context.Background()) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - it, _ = txn.Iter([]byte("DATA_test_tbl_department_record__000000000"), nil) - require.False(t, it.Valid()) - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestSetNil(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - require.NoError(t, err) - err = txn.Set([]byte("1"), nil) - require.Error(t, err) -} - -func TestBasicSeek(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - require.NoError(t, txn.Set([]byte("1"), []byte("1"))) - err = txn.Commit(context.Background()) - require.NoError(t, err) - txn, err = store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - - it, err := txn.Iter([]byte("2"), nil) - require.NoError(t, err) - require.False(t, it.Valid()) - require.NoError(t, txn.Delete([]byte("1"))) -} - -func TestBasicTable(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - for i := 1; i < 5; i++ { - b := []byte(strconv.Itoa(i)) - require.NoError(t, txn.Set(b, b)) - } - err = txn.Commit(context.Background()) - require.NoError(t, err) - txn, err = store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - - err = txn.Set([]byte("1"), []byte("1")) - require.NoError(t, err) - - it, err := txn.Iter([]byte("0"), nil) - require.NoError(t, err) - require.Equal(t, "1", string(it.Key())) - - err = txn.Set([]byte("0"), []byte("0")) - require.NoError(t, err) - it, err = txn.Iter([]byte("0"), nil) - require.NoError(t, err) - require.Equal(t, "0", string(it.Key())) - err = txn.Delete([]byte("0")) - require.NoError(t, err) - - require.NoError(t, txn.Delete([]byte("1"))) - it, err = txn.Iter([]byte("0"), nil) - require.NoError(t, err) - require.Equal(t, "2", string(it.Key())) - - err = txn.Delete([]byte("3")) - require.NoError(t, err) - it, err = txn.Iter([]byte("2"), nil) - require.NoError(t, err) - require.Equal(t, "2", string(it.Key())) - - it, err = txn.Iter([]byte("3"), nil) - require.NoError(t, err) - require.Equal(t, "4", string(it.Key())) - err = txn.Delete([]byte("2")) - require.NoError(t, err) - err = txn.Delete([]byte("4")) - require.NoError(t, err) -} - -func TestRollback(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - err = txn.Rollback() - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - - insertData(t, txn) - - mustGet(t, txn) - - err = txn.Rollback() - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - - for i := startIndex; i < testCount; i++ { - _, err := txn.Get(context.TODO(), []byte(strconv.Itoa(i))) - require.Error(t, err) - } -} - -func TestSeekMin(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - rows := []struct { - key string - value string - }{ - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001", "lock-version"}, - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0002", "1"}, - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000001_0003", "hello"}, - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002", "lock-version"}, - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0002", "2"}, - {"DATA_test_main_db_tbl_tbl_test_record__00000000000000000002_0003", "hello"}, - } - - txn, err := store.Begin() - require.NoError(t, err) - for _, row := range rows { - require.NoError(t, txn.Set([]byte(row.key), []byte(row.value))) - } - - it, err := txn.Iter(nil, nil) - require.NoError(t, err) - for it.Valid() { - require.NoError(t, it.Next()) - } - - it, err = txn.Iter([]byte("DATA_test_main_db_tbl_tbl_test_record__00000000000000000000"), nil) - require.NoError(t, err) - require.Equal(t, "DATA_test_main_db_tbl_tbl_test_record__00000000000000000001", string(it.Key())) - - for _, row := range rows { - require.NoError(t, txn.Delete([]byte(row.key))) - } -} - -func TestConditionIfNotExist(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - var success int64 - cnt := 100 - b := []byte("1") - var wg sync.WaitGroup - wg.Add(cnt) - for i := 0; i < cnt; i++ { - go func() { - defer wg.Done() - txn, err := store.Begin() - require.NoError(t, err) - err = txn.Set(b, b) - if err != nil { - return - } - err = txn.Commit(context.Background()) - if err == nil { - atomic.AddInt64(&success, 1) - } - }() - } - wg.Wait() - // At least one txn can success. - require.Greater(t, success, int64(0)) - - // Clean up - txn, err := store.Begin() - require.NoError(t, err) - err = txn.Delete(b) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestConditionIfEqual(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - var success int64 - cnt := 100 - b := []byte("1") - var wg sync.WaitGroup - wg.Add(cnt) - - txn, err := store.Begin() - require.NoError(t, err) - require.NoError(t, txn.Set(b, b)) - err = txn.Commit(context.Background()) - require.NoError(t, err) - - for i := 0; i < cnt; i++ { - go func() { - defer wg.Done() - // Use txn1/err1 instead of txn/err is - // to pass `go tool vet -shadow` check. - txn1, err1 := store.Begin() - require.NoError(t, err1) - require.NoError(t, txn1.Set(b, []byte("newValue"))) - err1 = txn1.Commit(context.Background()) - if err1 == nil { - atomic.AddInt64(&success, 1) - } - }() - } - wg.Wait() - require.Greater(t, success, int64(0)) - - // Clean up - txn, err = store.Begin() - require.NoError(t, err) - err = txn.Delete(b) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestConditionUpdate(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - require.NoError(t, txn.Delete([]byte("b"))) - _, err = kv.IncInt64(txn, []byte("a"), 1) - require.NoError(t, err) - err = txn.Commit(context.Background()) - require.NoError(t, err) -} - -func TestDBClose(t *testing.T) { - t.Skip("don't know why it fails.") - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - - txn, err := store.Begin() - require.NoError(t, err) - - err = txn.Set([]byte("a"), []byte("b")) - require.NoError(t, err) - - err = txn.Commit(context.Background()) - require.NoError(t, err) - - ver, err := store.CurrentVersion(kv.GlobalTxnScope) - require.NoError(t, err) - require.Equal(t, 1, kv.MaxVersion.Cmp(ver)) - - snap := store.GetSnapshot(kv.MaxVersion) - - _, err = snap.Get(context.TODO(), []byte("a")) - require.NoError(t, err) - - txn, err = store.Begin() - require.NoError(t, err) - - err = store.Close() - require.NoError(t, err) - - _, err = store.Begin() - require.Error(t, err) - - _ = store.GetSnapshot(kv.MaxVersion) - - err = txn.Set([]byte("a"), []byte("b")) - require.NoError(t, err) - - err = txn.Commit(context.Background()) - require.Error(t, err) -} - -func TestIsolationInc(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - threadCnt := 4 - - ids := make(map[int64]struct{}, threadCnt*100) - var m sync.Mutex - var wg sync.WaitGroup - - wg.Add(threadCnt) - for i := 0; i < threadCnt; i++ { - go func() { - defer wg.Done() - for j := 0; j < 100; j++ { - var id int64 - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - var err1 error - id, err1 = kv.IncInt64(txn, []byte("key"), 1) - return err1 - }) - require.NoError(t, err) - - m.Lock() - _, ok := ids[id] - ids[id] = struct{}{} - m.Unlock() - require.False(t, ok) - } - }() - } - - wg.Wait() - - // delete - txn, err := store.Begin() - require.NoError(t, err) - defer func() { - require.NoError(t, txn.Commit(context.Background())) - }() - require.NoError(t, txn.Delete([]byte("key"))) -} - -func TestIsolationMultiInc(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - threadCnt := 4 - incCnt := 100 - keyCnt := 4 - - keys := make([][]byte, 0, keyCnt) - for i := 0; i < keyCnt; i++ { - keys = append(keys, []byte(fmt.Sprintf("test_key_%d", i))) - } - - var wg sync.WaitGroup - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - wg.Add(threadCnt) - for i := 0; i < threadCnt; i++ { - go func() { - defer wg.Done() - for j := 0; j < incCnt; j++ { - err := kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - for _, key := range keys { - _, err1 := kv.IncInt64(txn, key, 1) - if err1 != nil { - return err1 - } - } - - return nil - }) - require.NoError(t, err) - } - }() - } - - wg.Wait() - - err = kv.RunInNewTxn(ctx, store, false, func(ctx context.Context, txn kv.Transaction) error { - for _, key := range keys { - id, err1 := kv.GetInt64(context.TODO(), txn, key) - if err1 != nil { - return err1 - } - require.Equal(t, int64(threadCnt*incCnt), id) - require.NoError(t, txn.Delete(key)) - } - return nil - }) - require.NoError(t, err) -} - -func TestRetryOpenStore(t *testing.T) { - begin := time.Now() - require.NoError(t, Register("dummy", &brokenStore{})) - store, err := newStoreWithRetry("dummy://dummy-store", 3) - if store != nil { - defer func() { - require.NoError(t, store.Close()) - }() - } - require.Error(t, err) - elapse := time.Since(begin) - require.GreaterOrEqual(t, uint64(elapse), uint64(3*time.Second)) -} - -func TestOpenStore(t *testing.T) { - require.NoError(t, Register("open", &brokenStore{})) - store, err := newStoreWithRetry(":", 3) - if store != nil { - defer func() { - require.NoError(t, store.Close()) - }() - } - require.Error(t, err) -} - -func TestRegister(t *testing.T) { - err := Register("retry", &brokenStore{}) - require.NoError(t, err) - err = Register("retry", &brokenStore{}) - require.Error(t, err) -} - -func TestSetAssertion(t *testing.T) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - defer func() { - require.NoError(t, store.Close()) - }() - - txn, err := store.Begin() - require.NoError(t, err) - - mustHaveAssertion := func(key []byte, assertion kv.FlagsOp) { - f, err1 := txn.GetMemBuffer().GetFlags(key) - require.NoError(t, err1) - if assertion == kv.SetAssertExist { - require.True(t, f.HasAssertExists()) - require.False(t, f.HasAssertUnknown()) - } else if assertion == kv.SetAssertNotExist { - require.True(t, f.HasAssertNotExists()) - require.False(t, f.HasAssertUnknown()) - } else if assertion == kv.SetAssertUnknown { - require.True(t, f.HasAssertUnknown()) - } else if assertion == kv.SetAssertNone { - require.False(t, f.HasAssertionFlags()) - } else { - require.FailNow(t, "unreachable") - } - } - - testUnchangeable := func(key []byte, expectAssertion kv.FlagsOp) { - err = txn.SetAssertion(key, kv.SetAssertExist) - require.NoError(t, err) - mustHaveAssertion(key, expectAssertion) - err = txn.SetAssertion(key, kv.SetAssertNotExist) - require.NoError(t, err) - mustHaveAssertion(key, expectAssertion) - err = txn.SetAssertion(key, kv.SetAssertUnknown) - require.NoError(t, err) - mustHaveAssertion(key, expectAssertion) - err = txn.SetAssertion(key, kv.SetAssertNone) - require.NoError(t, err) - mustHaveAssertion(key, expectAssertion) - } - - k1 := []byte("k1") - err = txn.SetAssertion(k1, kv.SetAssertExist) - require.NoError(t, err) - mustHaveAssertion(k1, kv.SetAssertExist) - testUnchangeable(k1, kv.SetAssertExist) - - k2 := []byte("k2") - err = txn.SetAssertion(k2, kv.SetAssertNotExist) - require.NoError(t, err) - mustHaveAssertion(k2, kv.SetAssertNotExist) - testUnchangeable(k2, kv.SetAssertNotExist) - - k3 := []byte("k3") - err = txn.SetAssertion(k3, kv.SetAssertUnknown) - require.NoError(t, err) - mustHaveAssertion(k3, kv.SetAssertUnknown) - testUnchangeable(k3, kv.SetAssertUnknown) - - k4 := []byte("k4") - err = txn.SetAssertion(k4, kv.SetAssertNone) - require.NoError(t, err) - mustHaveAssertion(k4, kv.SetAssertNone) - err = txn.SetAssertion(k4, kv.SetAssertExist) - require.NoError(t, err) - mustHaveAssertion(k4, kv.SetAssertExist) - testUnchangeable(k4, kv.SetAssertExist) - - k5 := []byte("k5") - err = txn.Set(k5, []byte("v5")) - require.NoError(t, err) - mustHaveAssertion(k5, kv.SetAssertNone) - err = txn.SetAssertion(k5, kv.SetAssertNotExist) - require.NoError(t, err) - mustHaveAssertion(k5, kv.SetAssertNotExist) - testUnchangeable(k5, kv.SetAssertNotExist) - - k6 := []byte("k6") - err = txn.SetAssertion(k6, kv.SetAssertNotExist) - require.NoError(t, err) - err = txn.GetMemBuffer().SetWithFlags(k6, []byte("v6"), kv.SetPresumeKeyNotExists) - require.NoError(t, err) - mustHaveAssertion(k6, kv.SetAssertNotExist) - testUnchangeable(k6, kv.SetAssertNotExist) - flags, err := txn.GetMemBuffer().GetFlags(k6) - require.NoError(t, err) - require.True(t, flags.HasPresumeKeyNotExists()) - err = txn.GetMemBuffer().DeleteWithFlags(k6, kv.SetNeedLocked) - mustHaveAssertion(k6, kv.SetAssertNotExist) - testUnchangeable(k6, kv.SetAssertNotExist) - flags, err = txn.GetMemBuffer().GetFlags(k6) - require.NoError(t, err) - require.True(t, flags.HasPresumeKeyNotExists()) - require.True(t, flags.HasNeedLocked()) - - k7 := []byte("k7") - lockCtx := kv2.NewLockCtx(txn.StartTS(), 2000, time.Now()) - err = txn.LockKeys(context.Background(), lockCtx, k7) - require.NoError(t, err) - mustHaveAssertion(k7, kv.SetAssertNone) - - require.NoError(t, txn.Rollback()) -} diff --git a/structure/BUILD.bazel b/structure/BUILD.bazel deleted file mode 100644 index 0fcf53831a8fa..0000000000000 --- a/structure/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "structure", - srcs = [ - "hash.go", - "list.go", - "string.go", - "structure.go", - "type.go", - ], - importpath = "github.com/pingcap/tidb/structure", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//kv", - "//util/codec", - "//util/dbterror", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "structure_test", - timeout = "short", - srcs = [ - "main_test.go", - "structure_test.go", - ], - embed = [":structure"], - flaky = True, - shard_count = 4, - deps = [ - "//kv", - "//parser/mysql", - "//parser/terror", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/structure/list.go b/structure/list.go deleted file mode 100644 index 63ca285cbc5e3..0000000000000 --- a/structure/list.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package structure - -import ( - "context" - "encoding/binary" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" -) - -type listMeta struct { - LIndex int64 - RIndex int64 -} - -func (meta listMeta) Value() []byte { - buf := make([]byte, 16) - binary.BigEndian.PutUint64(buf[0:8], uint64(meta.LIndex)) - binary.BigEndian.PutUint64(buf[8:16], uint64(meta.RIndex)) - return buf -} - -func (meta listMeta) IsEmpty() bool { - return meta.LIndex >= meta.RIndex -} - -// LPush prepends one or multiple values to a list. -func (t *TxStructure) LPush(key []byte, values ...[]byte) error { - return t.listPush(key, true, values...) -} - -// RPush appends one or multiple values to a list. -func (t *TxStructure) RPush(key []byte, values ...[]byte) error { - return t.listPush(key, false, values...) -} - -func (t *TxStructure) listPush(key []byte, left bool, values ...[]byte) error { - if t.readWriter == nil { - return ErrWriteOnSnapshot - } - if len(values) == 0 { - return nil - } - - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil { - return errors.Trace(err) - } - - var index int64 - for _, v := range values { - if left { - meta.LIndex-- - index = meta.LIndex - } else { - index = meta.RIndex - meta.RIndex++ - } - - dataKey := t.encodeListDataKey(key, index) - if err = t.readWriter.Set(dataKey, v); err != nil { - return errors.Trace(err) - } - } - - return t.readWriter.Set(metaKey, meta.Value()) -} - -// LPop removes and gets the first element in a list. -func (t *TxStructure) LPop(key []byte) ([]byte, error) { - return t.listPop(key, true) -} - -// RPop removes and gets the last element in a list. -func (t *TxStructure) RPop(key []byte) ([]byte, error) { - return t.listPop(key, false) -} - -func (t *TxStructure) listPop(key []byte, left bool) ([]byte, error) { - if t.readWriter == nil { - return nil, ErrWriteOnSnapshot - } - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil || meta.IsEmpty() { - return nil, errors.Trace(err) - } - - var index int64 - if left { - index = meta.LIndex - meta.LIndex++ - } else { - meta.RIndex-- - index = meta.RIndex - } - - dataKey := t.encodeListDataKey(key, index) - - var data []byte - data, err = t.reader.Get(context.TODO(), dataKey) - if err != nil { - return nil, errors.Trace(err) - } - - if err = t.readWriter.Delete(dataKey); err != nil { - return nil, errors.Trace(err) - } - - if !meta.IsEmpty() { - err = t.readWriter.Set(metaKey, meta.Value()) - } else { - err = t.readWriter.Delete(metaKey) - } - - return data, errors.Trace(err) -} - -// LLen gets the length of a list. -func (t *TxStructure) LLen(key []byte) (int64, error) { - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - return meta.RIndex - meta.LIndex, errors.Trace(err) -} - -// LGetAll gets all elements of this list in order from right to left. -func (t *TxStructure) LGetAll(key []byte) ([][]byte, error) { - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil || meta.IsEmpty() { - return nil, errors.Trace(err) - } - - length := int(meta.RIndex - meta.LIndex) - elements := make([][]byte, 0, length) - for index := meta.RIndex - 1; index >= meta.LIndex; index-- { - e, err := t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) - if err != nil { - return nil, errors.Trace(err) - } - elements = append(elements, e) - } - return elements, nil -} - -// LIndex gets an element from a list by its index. -func (t *TxStructure) LIndex(key []byte, index int64) ([]byte, error) { - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil || meta.IsEmpty() { - return nil, errors.Trace(err) - } - - index = adjustIndex(index, meta.LIndex, meta.RIndex) - - if index >= meta.LIndex && index < meta.RIndex { - return t.reader.Get(context.TODO(), t.encodeListDataKey(key, index)) - } - return nil, nil -} - -// LSet updates an element in the list by its index. -func (t *TxStructure) LSet(key []byte, index int64, value []byte) error { - if t.readWriter == nil { - return ErrWriteOnSnapshot - } - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil || meta.IsEmpty() { - return errors.Trace(err) - } - - index = adjustIndex(index, meta.LIndex, meta.RIndex) - - if index >= meta.LIndex && index < meta.RIndex { - return t.readWriter.Set(t.encodeListDataKey(key, index), value) - } - return ErrInvalidListIndex.GenWithStack("invalid list index %d", index) -} - -// LClear removes the list of the key. -func (t *TxStructure) LClear(key []byte) error { - if t.readWriter == nil { - return ErrWriteOnSnapshot - } - metaKey := t.encodeListMetaKey(key) - meta, err := t.loadListMeta(metaKey) - if err != nil || meta.IsEmpty() { - return errors.Trace(err) - } - - for index := meta.LIndex; index < meta.RIndex; index++ { - dataKey := t.encodeListDataKey(key, index) - if err = t.readWriter.Delete(dataKey); err != nil { - return errors.Trace(err) - } - } - - return t.readWriter.Delete(metaKey) -} - -func (t *TxStructure) loadListMeta(metaKey []byte) (listMeta, error) { - v, err := t.reader.Get(context.TODO(), metaKey) - if kv.ErrNotExist.Equal(err) { - err = nil - } - if err != nil { - return listMeta{}, errors.Trace(err) - } - - meta := listMeta{0, 0} - if v == nil { - return meta, nil - } - - if len(v) != 16 { - return meta, ErrInvalidListMetaData - } - - meta.LIndex = int64(binary.BigEndian.Uint64(v[0:8])) - meta.RIndex = int64(binary.BigEndian.Uint64(v[8:16])) - return meta, nil -} - -func adjustIndex(index int64, min, max int64) int64 { - if index >= 0 { - return index + min - } - - return index + max -} diff --git a/structure/main_test.go b/structure/main_test.go deleted file mode 100644 index 1c3e350826051..0000000000000 --- a/structure/main_test.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package structure - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlDeleteWorker).loop"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/ttl/ttlworker.(*ttlScanWorker).loop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/structure/type.go b/structure/type.go deleted file mode 100644 index e92128be6eded..0000000000000 --- a/structure/type.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package structure - -import ( - "bytes" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/codec" -) - -// TypeFlag is for data structure meta/data flag. -type TypeFlag byte - -const ( - // StringMeta is the flag for string meta. - StringMeta TypeFlag = 'S' - // StringData is the flag for string data. - StringData TypeFlag = 's' - // HashMeta is the flag for hash meta. - HashMeta TypeFlag = 'H' - // HashData is the flag for hash data. - HashData TypeFlag = 'h' - // ListMeta is the flag for list meta. - ListMeta TypeFlag = 'L' - // ListData is the flag for list data. - ListData TypeFlag = 'l' -) - -// Make linter happy, since encodeHashMetaKey is unused in this repo. -var _ = (&TxStructure{}).encodeHashMetaKey - -// EncodeStringDataKey will encode string key. -func (t *TxStructure) EncodeStringDataKey(key []byte) kv.Key { - // for codec Encode, we may add extra bytes data, so here and following encode - // we will use extra length like 4 for a little optimization. - ek := make([]byte, 0, len(t.prefix)+len(key)+24) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - return codec.EncodeUint(ek, uint64(StringData)) -} - -// nolint:unused -func (t *TxStructure) encodeHashMetaKey(key []byte) kv.Key { - ek := make([]byte, 0, len(t.prefix)+codec.EncodedBytesLength(len(key))+8) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - return codec.EncodeUint(ek, uint64(HashMeta)) -} - -func (t *TxStructure) encodeHashDataKey(key []byte, field []byte) kv.Key { - ek := make([]byte, 0, len(t.prefix)+codec.EncodedBytesLength(len(key))+8+codec.EncodedBytesLength(len(field))) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - ek = codec.EncodeUint(ek, uint64(HashData)) - return codec.EncodeBytes(ek, field) -} - -// EncodeHashDataKey exports for tests. -func (t *TxStructure) EncodeHashDataKey(key []byte, field []byte) kv.Key { - return t.encodeHashDataKey(key, field) -} - -func (t *TxStructure) decodeHashDataKey(ek kv.Key) ([]byte, []byte, error) { - var ( - key []byte - field []byte - err error - tp uint64 - ) - - if !bytes.HasPrefix(ek, t.prefix) { - return nil, nil, errors.New("invalid encoded hash data key prefix") - } - - ek = ek[len(t.prefix):] - - ek, key, err = codec.DecodeBytes(ek, nil) - if err != nil { - return nil, nil, errors.Trace(err) - } - - ek, tp, err = codec.DecodeUint(ek) - if err != nil { - return nil, nil, errors.Trace(err) - } else if TypeFlag(tp) != HashData { - return nil, nil, ErrInvalidHashKeyFlag.GenWithStack("invalid encoded hash data key flag %c", byte(tp)) - } - - _, field, err = codec.DecodeBytes(ek, nil) - return key, field, errors.Trace(err) -} - -func (t *TxStructure) hashDataKeyPrefix(key []byte) kv.Key { - ek := make([]byte, 0, len(t.prefix)+len(key)+24) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - return codec.EncodeUint(ek, uint64(HashData)) -} - -func (t *TxStructure) encodeListMetaKey(key []byte) kv.Key { - ek := make([]byte, 0, len(t.prefix)+len(key)+24) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - return codec.EncodeUint(ek, uint64(ListMeta)) -} - -func (t *TxStructure) encodeListDataKey(key []byte, index int64) kv.Key { - ek := make([]byte, 0, len(t.prefix)+len(key)+36) - ek = append(ek, t.prefix...) - ek = codec.EncodeBytes(ek, key) - ek = codec.EncodeUint(ek, uint64(ListData)) - return codec.EncodeInt(ek, index) -} diff --git a/table/BUILD.bazel b/table/BUILD.bazel deleted file mode 100644 index 971be24bca269..0000000000000 --- a/table/BUILD.bazel +++ /dev/null @@ -1,69 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "table", - srcs = [ - "column.go", - "constraint.go", - "index.go", - "table.go", - ], - importpath = "github.com/pingcap/tidb/table", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//expression", - "//kv", - "//meta/autoid", - "//parser", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//sessionctx", - "//sessionctx/stmtctx", - "//types", - "//util/chunk", - "//util/dbterror", - "//util/hack", - "//util/logutil", - "//util/mock", - "//util/sqlexec", - "//util/timeutil", - "//util/tracing", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "table_test", - timeout = "short", - srcs = [ - "column_test.go", - "main_test.go", - "table_test.go", - ], - embed = [":table"], - flaky = True, - race = "on", - shard_count = 9, - deps = [ - "//errno", - "//expression", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//types", - "//util/collate", - "//util/mock", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/table/column.go b/table/column.go deleted file mode 100644 index dc63eff2221d5..0000000000000 --- a/table/column.go +++ /dev/null @@ -1,742 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2016 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package table - -import ( - "fmt" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - field_types "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" - "go.uber.org/zap" -) - -// Column provides meta data describing a table column. -type Column struct { - *model.ColumnInfo - // If this column is a generated column, the expression will be stored here. - GeneratedExpr ast.ExprNode - // If this column has default expr value, this expression will be stored here. - DefaultExpr ast.ExprNode -} - -// String implements fmt.Stringer interface. -func (c *Column) String() string { - ans := []string{c.Name.O, types.TypeToStr(c.GetType(), c.GetCharset())} - if mysql.HasAutoIncrementFlag(c.GetFlag()) { - ans = append(ans, "AUTO_INCREMENT") - } - if mysql.HasNotNullFlag(c.GetFlag()) { - ans = append(ans, "NOT NULL") - } - return strings.Join(ans, " ") -} - -// ToInfo casts Column to model.ColumnInfo -// NOTE: DONT modify return value. -func (c *Column) ToInfo() *model.ColumnInfo { - return c.ColumnInfo -} - -// FindCol finds column in cols by name. -func FindCol(cols []*Column, name string) *Column { - for _, col := range cols { - if strings.EqualFold(col.Name.O, name) { - return col - } - } - return nil -} - -// FindColLowerCase finds column in cols by name. It assumes the name is lowercase. -func FindColLowerCase(cols []*Column, name string) *Column { - for _, col := range cols { - if col.Name.L == name { - return col - } - } - return nil -} - -// ToColumn converts a *model.ColumnInfo to *Column. -func ToColumn(col *model.ColumnInfo) *Column { - return &Column{ - col, - nil, - nil, - } -} - -// FindCols finds columns in cols by names. -// If pkIsHandle is false and name is ExtraHandleName, the extra handle column will be added. -// If any columns don't match, return nil and the first missing column's name. -// Please consider FindColumns() first for a better performance. -func FindCols(cols []*Column, names []string, pkIsHandle bool) ([]*Column, string) { - var rcols []*Column - for _, name := range names { - col := FindCol(cols, name) - if col != nil { - rcols = append(rcols, col) - } else if name == model.ExtraHandleName.L && !pkIsHandle { - col := &Column{} - col.ColumnInfo = model.NewExtraHandleColInfo() - col.ColumnInfo.Offset = len(cols) - rcols = append(rcols, col) - } else { - return nil, name - } - } - - return rcols, "" -} - -// FindColumns finds columns in cols by names with a better performance than FindCols(). -// It assumes names are lowercase. -func FindColumns(cols []*Column, names []string, pkIsHandle bool) (foundCols []*Column, missingOffset int) { - var rcols []*Column - for i, name := range names { - col := FindColLowerCase(cols, name) - if col != nil { - rcols = append(rcols, col) - } else if name == model.ExtraHandleName.L && !pkIsHandle { - col := &Column{} - col.ColumnInfo = model.NewExtraHandleColInfo() - col.ColumnInfo.Offset = len(cols) - rcols = append(rcols, col) - } else { - return nil, i - } - } - return rcols, -1 -} - -// FindOnUpdateCols finds columns which have OnUpdateNow flag. -func FindOnUpdateCols(cols []*Column) []*Column { - var rcols []*Column - for _, col := range cols { - if mysql.HasOnUpdateNowFlag(col.GetFlag()) { - rcols = append(rcols, col) - } - } - - return rcols -} - -// truncateTrailingSpaces truncates trailing spaces for CHAR[(M)] column. -// fix: https://github.com/pingcap/tidb/issues/3660 -func truncateTrailingSpaces(v *types.Datum) { - if v.Kind() == types.KindNull { - return - } - b := v.GetBytes() - length := len(b) - for length > 0 && b[length-1] == ' ' { - length-- - } - b = b[:length] - str := string(hack.String(b)) - v.SetString(str, v.Collation()) -} - -// convertToIncorrectStringErr converts ErrInvalidCharacterString to ErrTruncatedWrongValueForField. -// The first argument is the invalid character in bytes. -func convertToIncorrectStringErr(err error, colName string) error { - inErr, ok := errors.Cause(err).(*errors.Error) - if !ok { - return err - } - args := inErr.Args() - if len(args) != 2 { - return err - } - invalidStrHex, ok := args[1].(string) - if !ok { - return err - } - var res strings.Builder - for i := 0; i < len(invalidStrHex); i++ { - if i%2 == 0 { - res.WriteString("\\x") - } - res.WriteByte(invalidStrHex[i]) - } - return ErrTruncatedWrongValueForField.FastGen("Incorrect string value '%s' for column '%s'", res.String(), colName) -} - -// handleZeroDatetime handles Timestamp/Datetime/Date zero date and invalid dates. -// Currently only called from CastValue. -// returns: -// -// value (possibly adjusted) -// boolean; true if break error/warning handling in CastValue and return what was returned from this -// error -func handleZeroDatetime(ctx sessionctx.Context, col *model.ColumnInfo, casted types.Datum, str string, tmIsInvalid bool) (types.Datum, bool, error) { - sc := ctx.GetSessionVars().StmtCtx - tm := casted.GetMysqlTime() - mode := ctx.GetSessionVars().SQLMode - - var ( - zeroV types.Time - zeroT string - ) - switch col.GetType() { - case mysql.TypeDate: - zeroV, zeroT = types.ZeroDate, types.DateStr - case mysql.TypeDatetime: - zeroV, zeroT = types.ZeroDatetime, types.DateTimeStr - case mysql.TypeTimestamp: - zeroV, zeroT = types.ZeroTimestamp, types.TimestampStr - } - - // ref https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_date - // if NO_ZERO_DATE is not enabled, '0000-00-00' is permitted and inserts produce no warning - // if NO_ZERO_DATE is enabled, '0000-00-00' is permitted and inserts produce a warning - // If NO_ZERO_DATE mode and strict mode are enabled, '0000-00-00' is not permitted and inserts produce an error, unless IGNORE is given as well. For INSERT IGNORE and UPDATE IGNORE, '0000-00-00' is permitted and inserts produce a warning. - // if NO_ZERO_IN_DATE is not enabled, dates with zero parts are permitted and inserts produce no warning - // if NO_ZERO_IN_DATE is enabled, dates with zero parts are inserted as '0000-00-00' and produce a warning - // If NO_ZERO_IN_DATE mode and strict mode are enabled, dates with zero parts are not permitted and inserts produce an error, unless IGNORE is given as well. For INSERT IGNORE and UPDATE IGNORE, dates with zero parts are inserted as '0000-00-00' and produce a warning. - - ignoreErr := sc.DupKeyAsWarning - - // Timestamp in MySQL is since EPOCH 1970-01-01 00:00:00 UTC and can by definition not have invalid dates! - // Zero date is special for MySQL timestamp and *NOT* 1970-01-01 00:00:00, but 0000-00-00 00:00:00! - // in MySQL 8.0, the Timestamp's case is different to Datetime/Date, as shown below: - // - // | | NZD | NZD|ST | ELSE | ELSE|ST | - // | ------------ | ----------------- | ------- | ----------------- | -------- | - // | `0000-00-01` | Truncate + Warning| Error | Truncate + Warning| Error | - // | `0000-00-00` | Success + Warning | Error | Success | Success | - // - // * **NZD**: NO_ZERO_DATE_MODE - // * **ST**: STRICT_TRANS_TABLES - // * **ELSE**: empty or NO_ZERO_IN_DATE_MODE - if tm.IsZero() && col.GetType() == mysql.TypeTimestamp { - innerErr := types.ErrWrongValue.GenWithStackByArgs(zeroT, str) - if mode.HasStrictMode() && !ignoreErr && (tmIsInvalid || mode.HasNoZeroDateMode()) { - return types.NewDatum(zeroV), true, innerErr - } - - if tmIsInvalid || mode.HasNoZeroDateMode() { - sc.AppendWarning(innerErr) - } - return types.NewDatum(zeroV), true, nil - } else if tmIsInvalid && col.GetType() == mysql.TypeTimestamp { - // Prevent from being stored! Invalid timestamp! - if mode.HasStrictMode() { - return types.NewDatum(zeroV), true, types.ErrWrongValue.GenWithStackByArgs(zeroT, str) - } - // no strict mode, truncate to 0000-00-00 00:00:00 - sc.AppendWarning(types.ErrWrongValue.GenWithStackByArgs(zeroT, str)) - return types.NewDatum(zeroV), true, nil - } else if tm.IsZero() || tm.InvalidZero() { - if tm.IsZero() { - // Don't care NoZeroDate mode if time val is invalid. - if !tmIsInvalid && !mode.HasNoZeroDateMode() { - return types.NewDatum(zeroV), true, nil - } - } else if tm.InvalidZero() { - if !mode.HasNoZeroInDateMode() { - return casted, true, nil - } - } - - innerErr := types.ErrWrongValue.GenWithStackByArgs(zeroT, str) - if mode.HasStrictMode() && !ignoreErr { - return types.NewDatum(zeroV), true, innerErr - } - - // TODO: as in MySQL 8.0's implement, warning message is `types.ErrWarnDataOutOfRange`, - // but this error message need a `rowIdx` argument, in this context, the `rowIdx` is missing. - // And refactor this function seems too complicated, so we set the warning message the same to error's. - sc.AppendWarning(innerErr) - return types.NewDatum(zeroV), true, nil - } - - return casted, false, nil -} - -// CastValue casts a value based on column type. -// If forceIgnoreTruncate is true, truncated errors will be ignored. -// If returnErr is true, directly return any conversion errors. -// It's safe now and it's the same as the behavior of select statement. -// Set it to true only in FillVirtualColumnValue and UnionScanExec.Next() -// If the handle of err is changed latter, the behavior of forceIgnoreTruncate also need to change. -// TODO: change the third arg to TypeField. Not pass ColumnInfo. -func CastValue(ctx sessionctx.Context, val types.Datum, col *model.ColumnInfo, returnErr, forceIgnoreTruncate bool) (casted types.Datum, err error) { - sc := ctx.GetSessionVars().StmtCtx - casted, err = val.ConvertTo(sc, &col.FieldType) - // TODO: make sure all truncate errors are handled by ConvertTo. - if returnErr && err != nil { - return casted, err - } - if err != nil && types.ErrTruncated.Equal(err) && col.GetType() != mysql.TypeSet && col.GetType() != mysql.TypeEnum { - str, err1 := val.ToString() - if err1 != nil { - logutil.BgLogger().Warn("Datum ToString failed", zap.Stringer("Datum", val), zap.Error(err1)) - } - err = types.ErrTruncatedWrongVal.GenWithStackByArgs(col.FieldType.CompactStr(), str) - } else if (sc.InInsertStmt || sc.InUpdateStmt) && !casted.IsNull() && - (col.GetType() == mysql.TypeDate || col.GetType() == mysql.TypeDatetime || col.GetType() == mysql.TypeTimestamp) { - str, err1 := val.ToString() - if err1 != nil { - logutil.BgLogger().Warn("Datum ToString failed", zap.Stringer("Datum", val), zap.Error(err1)) - str = val.GetString() - } - if innCasted, exit, innErr := handleZeroDatetime(ctx, col, casted, str, types.ErrWrongValue.Equal(err)); exit { - return innCasted, innErr - } - } else if err != nil && charset.ErrInvalidCharacterString.Equal(err) { - err = convertToIncorrectStringErr(err, col.Name.O) - logutil.BgLogger().Debug("incorrect string value", - zap.Uint64("conn", ctx.GetSessionVars().ConnectionID), zap.Error(err)) - } - - err = sc.HandleTruncate(err) - err = sc.HandleOverflow(err, err) - - if forceIgnoreTruncate { - err = nil - } else if err != nil { - return casted, err - } - - if col.GetType() == mysql.TypeString && !types.IsBinaryStr(&col.FieldType) { - truncateTrailingSpaces(&casted) - } - return casted, err -} - -// ColDesc describes column information like MySQL desc and show columns do. -type ColDesc struct { - Field string - Type string - // Charset is nil if the column doesn't have a charset, or a string indicating the charset name. - Charset interface{} - // Collation is nil if the column doesn't have a collation, or a string indicating the collation name. - Collation interface{} - Null string - Key string - DefaultValue interface{} - Extra string - Privileges string - Comment string -} - -const defaultPrivileges = "select,insert,update,references" - -// NewColDesc returns a new ColDesc for a column. -func NewColDesc(col *Column) *ColDesc { - // TODO: if we have no primary key and a unique index which's columns are all not null - // we will set these columns' flag as PriKeyFlag - // see https://dev.mysql.com/doc/refman/5.7/en/show-columns.html - // create table - name := col.Name - nullFlag := "YES" - if mysql.HasNotNullFlag(col.GetFlag()) { - nullFlag = "NO" - } - keyFlag := "" - if mysql.HasPriKeyFlag(col.GetFlag()) { - keyFlag = "PRI" - } else if mysql.HasUniKeyFlag(col.GetFlag()) { - keyFlag = "UNI" - } else if mysql.HasMultipleKeyFlag(col.GetFlag()) { - keyFlag = "MUL" - } - var defaultValue interface{} - if !mysql.HasNoDefaultValueFlag(col.GetFlag()) { - defaultValue = col.GetDefaultValue() - if defaultValStr, ok := defaultValue.(string); ok { - if (col.GetType() == mysql.TypeTimestamp || col.GetType() == mysql.TypeDatetime) && - strings.EqualFold(defaultValStr, ast.CurrentTimestamp) && - col.GetDecimal() > 0 { - defaultValue = fmt.Sprintf("%s(%d)", defaultValStr, col.GetDecimal()) - } - } - } - - extra := "" - if mysql.HasAutoIncrementFlag(col.GetFlag()) { - extra = "auto_increment" - } else if mysql.HasOnUpdateNowFlag(col.GetFlag()) { - // in order to match the rules of mysql 8.0.16 version - // see https://github.com/pingcap/tidb/issues/10337 - extra = "DEFAULT_GENERATED on update CURRENT_TIMESTAMP" + OptionalFsp(&col.FieldType) - } else if col.IsGenerated() { - if col.GeneratedStored { - extra = "STORED GENERATED" - } else { - extra = "VIRTUAL GENERATED" - } - } - - desc := &ColDesc{ - Field: name.O, - Type: col.GetTypeDesc(), - Charset: col.GetCharset(), - Collation: col.GetCollate(), - Null: nullFlag, - Key: keyFlag, - DefaultValue: defaultValue, - Extra: extra, - Privileges: defaultPrivileges, - Comment: col.Comment, - } - if !field_types.HasCharset(&col.ColumnInfo.FieldType) { - desc.Charset = nil - desc.Collation = nil - } - return desc -} - -// ColDescFieldNames returns the fields name in result set for desc and show columns. -func ColDescFieldNames(full bool) []string { - if full { - return []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"} - } - return []string{"Field", "Type", "Null", "Key", "Default", "Extra"} -} - -// CheckOnce checks if there are duplicated column names in cols. -func CheckOnce(cols []*Column) error { - m := map[string]struct{}{} - for _, col := range cols { - name := col.Name - _, ok := m[name.L] - if ok { - return errDuplicateColumn.GenWithStackByArgs(name) - } - - m[name.L] = struct{}{} - } - - return nil -} - -// CheckNotNull checks if nil value set to a column with NotNull flag is set. -// When caller is LOAD DATA, `rowCntInLoadData` should be greater than 0 and it -// will return a ErrWarnNullToNotnull when error. -// Otherwise, it will return a ErrColumnCantNull when error. -func (c *Column) CheckNotNull(data *types.Datum, rowCntInLoadData uint64) error { - if (mysql.HasNotNullFlag(c.GetFlag()) || mysql.HasPreventNullInsertFlag(c.GetFlag())) && data.IsNull() { - if rowCntInLoadData > 0 { - return ErrWarnNullToNotnull.GenWithStackByArgs(c.Name, rowCntInLoadData) - } - return ErrColumnCantNull.GenWithStackByArgs(c.Name) - } - return nil -} - -// HandleBadNull handles the bad null error. -// When caller is LOAD DATA, `rowCntInLoadData` should be greater than 0 the -// error is ErrWarnNullToNotnull. -// Otherwise, the error is ErrColumnCantNull. -// If BadNullAsWarning is true, it will append the error as a warning, else return the error. -func (c *Column) HandleBadNull(d *types.Datum, sc *stmtctx.StatementContext, rowCntInLoadData uint64) error { - if err := c.CheckNotNull(d, rowCntInLoadData); err != nil { - if sc.BadNullAsWarning { - sc.AppendWarning(err) - *d = GetZeroValue(c.ToInfo()) - return nil - } - return err - } - return nil -} - -// IsPKHandleColumn checks if the column is primary key handle column. -func (c *Column) IsPKHandleColumn(tbInfo *model.TableInfo) bool { - return mysql.HasPriKeyFlag(c.GetFlag()) && tbInfo.PKIsHandle -} - -// IsCommonHandleColumn checks if the column is common handle column. -func (c *Column) IsCommonHandleColumn(tbInfo *model.TableInfo) bool { - return mysql.HasPriKeyFlag(c.GetFlag()) && tbInfo.IsCommonHandle -} - -type getColOriginDefaultValue struct { - StrictSQLMode bool -} - -// GetColOriginDefaultValue gets default value of the column from original default value. -func GetColOriginDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { - return getColDefaultValue(ctx, col, col.GetOriginDefaultValue(), nil) -} - -// GetColOriginDefaultValueWithoutStrictSQLMode gets default value of the column from original default value with Strict SQL mode. -func GetColOriginDefaultValueWithoutStrictSQLMode(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { - return getColDefaultValue(ctx, col, col.GetOriginDefaultValue(), &getColOriginDefaultValue{ - StrictSQLMode: false, - }) -} - -// GetColDefaultValue gets default value of the column. -func GetColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo) (types.Datum, error) { - defaultValue := col.GetDefaultValue() - if !col.DefaultIsExpr { - return getColDefaultValue(ctx, col, defaultValue, nil) - } - return getColDefaultExprValue(ctx, col, defaultValue.(string)) -} - -// EvalColDefaultExpr eval default expr node to explicit default value. -func EvalColDefaultExpr(ctx sessionctx.Context, col *model.ColumnInfo, defaultExpr ast.ExprNode) (types.Datum, error) { - d, err := expression.EvalAstExpr(ctx, defaultExpr) - if err != nil { - return types.Datum{}, err - } - // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col, false, false) - if err != nil { - return types.Datum{}, err - } - return value, nil -} - -func getColDefaultExprValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultValue string) (types.Datum, error) { - var defaultExpr ast.ExprNode - expr := fmt.Sprintf("select %s", defaultValue) - stmts, _, err := parser.New().ParseSQL(expr) - if err == nil { - defaultExpr = stmts[0].(*ast.SelectStmt).Fields.Fields[0].Expr - } - d, err := expression.EvalAstExpr(ctx, defaultExpr) - if err != nil { - return types.Datum{}, err - } - // Check the evaluated data type by cast. - value, err := CastValue(ctx, d, col, false, false) - if err != nil { - return types.Datum{}, err - } - return value, nil -} - -func getColDefaultValue(ctx sessionctx.Context, col *model.ColumnInfo, defaultVal interface{}, args *getColOriginDefaultValue) (types.Datum, error) { - if defaultVal == nil { - return getColDefaultValueFromNil(ctx, col, args) - } - - switch col.GetType() { - case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDatetime: - default: - value, err := CastValue(ctx, types.NewDatum(defaultVal), col, false, false) - if err != nil { - return types.Datum{}, err - } - return value, nil - } - - // Check and get timestamp/datetime default value. - var needChangeTimeZone bool - var explicitTz *time.Location - // If the column's default value is not ZeroDatetimeStr nor CurrentTimestamp, should use the time zone of the default value itself. - if col.GetType() == mysql.TypeTimestamp { - if vv, ok := defaultVal.(string); ok && vv != types.ZeroDatetimeStr && !strings.EqualFold(vv, ast.CurrentTimestamp) { - needChangeTimeZone = true - // For col.Version = 0, the timezone information of default value is already lost, so use the system timezone as the default value timezone. - explicitTz = timeutil.SystemLocation() - if col.Version >= model.ColumnInfoVersion1 { - explicitTz = time.UTC - } - } - } - value, err := expression.GetTimeValue(ctx, defaultVal, col.GetType(), col.GetDecimal(), explicitTz) - if err != nil { - return types.Datum{}, errGetDefaultFailed.GenWithStackByArgs(col.Name) - } - // If the column's default value is not ZeroDatetimeStr or CurrentTimestamp, convert the default value to the current session time zone. - if needChangeTimeZone { - t := value.GetMysqlTime() - err = t.ConvertTimeZone(explicitTz, ctx.GetSessionVars().Location()) - if err != nil { - return value, err - } - value.SetMysqlTime(t) - } - return value, nil -} - -func getColDefaultValueFromNil(ctx sessionctx.Context, col *model.ColumnInfo, args *getColOriginDefaultValue) (types.Datum, error) { - if !mysql.HasNotNullFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) { - return types.Datum{}, nil - } - if col.GetType() == mysql.TypeEnum { - // For enum type, if no default value and not null is set, - // the default value is the first element of the enum list - if mysql.HasNotNullFlag(col.GetFlag()) { - defEnum, err := types.ParseEnumValue(col.FieldType.GetElems(), 1) - if err != nil { - return types.Datum{}, err - } - return types.NewCollateMysqlEnumDatum(defEnum, col.GetCollate()), nil - } - return types.Datum{}, nil - } - if mysql.HasAutoIncrementFlag(col.GetFlag()) && !mysql.HasNoDefaultValueFlag(col.GetFlag()) { - // Auto increment column doesn't have default value and we should not return error. - return GetZeroValue(col), nil - } - vars := ctx.GetSessionVars() - sc := vars.StmtCtx - var strictSQLMode bool - if args != nil { - strictSQLMode = args.StrictSQLMode - } else { - strictSQLMode = vars.StrictSQLMode - } - if !strictSQLMode { - sc.AppendWarning(ErrNoDefaultValue.FastGenByArgs(col.Name)) - if mysql.HasNotNullFlag(col.GetFlag()) { - return GetZeroValue(col), nil - } - if mysql.HasNoDefaultValueFlag(col.GetFlag()) { - return types.Datum{}, nil - } - } - if sc.BadNullAsWarning { - sc.AppendWarning(ErrColumnCantNull.FastGenByArgs(col.Name)) - return GetZeroValue(col), nil - } - return types.Datum{}, ErrNoDefaultValue.GenWithStackByArgs(col.Name) -} - -// GetZeroValue gets zero value for given column type. -func GetZeroValue(col *model.ColumnInfo) types.Datum { - var d types.Datum - switch col.GetType() { - case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong: - if mysql.HasUnsignedFlag(col.GetFlag()) { - d.SetUint64(0) - } else { - d.SetInt64(0) - } - case mysql.TypeYear: - d.SetInt64(0) - case mysql.TypeFloat: - d.SetFloat32(0) - case mysql.TypeDouble: - d.SetFloat64(0) - case mysql.TypeNewDecimal: - d.SetLength(col.GetFlen()) - d.SetFrac(col.GetDecimal()) - d.SetMysqlDecimal(new(types.MyDecimal)) - case mysql.TypeString: - if col.GetFlen() > 0 && col.GetCharset() == charset.CharsetBin { - d.SetBytes(make([]byte, col.GetFlen())) - } else { - d.SetString("", col.GetCollate()) - } - case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - d.SetString("", col.GetCollate()) - case mysql.TypeDuration: - d.SetMysqlDuration(types.ZeroDuration) - case mysql.TypeDate: - d.SetMysqlTime(types.ZeroDate) - case mysql.TypeTimestamp: - d.SetMysqlTime(types.ZeroTimestamp) - case mysql.TypeDatetime: - d.SetMysqlTime(types.ZeroDatetime) - case mysql.TypeBit: - d.SetMysqlBit(types.ZeroBinaryLiteral) - case mysql.TypeSet: - d.SetMysqlSet(types.Set{}, col.GetCollate()) - case mysql.TypeEnum: - d.SetMysqlEnum(types.Enum{}, col.GetCollate()) - case mysql.TypeJSON: - d.SetMysqlJSON(types.CreateBinaryJSON(nil)) - } - return d -} - -// OptionalFsp convert a FieldType.GetDecimal() to string. -func OptionalFsp(fieldType *types.FieldType) string { - fsp := fieldType.GetDecimal() - if fsp == 0 { - return "" - } - return "(" + strconv.Itoa(fsp) + ")" -} - -// FillVirtualColumnValue will calculate the virtual column value by evaluating generated -// expression using rows from a chunk, and then fill this value into the chunk. -func FillVirtualColumnValue(virtualRetTypes []*types.FieldType, virtualColumnIndex []int, - expCols []*expression.Column, colInfos []*model.ColumnInfo, sctx sessionctx.Context, req *chunk.Chunk) error { - if len(virtualColumnIndex) == 0 { - return nil - } - - virCols := chunk.NewChunkWithCapacity(virtualRetTypes, req.Capacity()) - iter := chunk.NewIterator4Chunk(req) - for i, idx := range virtualColumnIndex { - for row := iter.Begin(); row != iter.End(); row = iter.Next() { - datum, err := expCols[idx].EvalVirtualColumn(row) - if err != nil { - return err - } - // Because the expression might return different type from - // the generated column, we should wrap a CAST on the result. - castDatum, err := CastValue(sctx, datum, colInfos[idx], false, true) - if err != nil { - return err - } - - // Clip to zero if get negative value after cast to unsigned. - if mysql.HasUnsignedFlag(colInfos[idx].FieldType.GetFlag()) && !castDatum.IsNull() && !sctx.GetSessionVars().StmtCtx.ShouldClipToZero() { - switch datum.Kind() { - case types.KindInt64: - if datum.GetInt64() < 0 { - castDatum = GetZeroValue(colInfos[idx]) - } - case types.KindFloat32, types.KindFloat64: - if types.RoundFloat(datum.GetFloat64()) < 0 { - castDatum = GetZeroValue(colInfos[idx]) - } - case types.KindMysqlDecimal: - if datum.GetMysqlDecimal().IsNegative() { - castDatum = GetZeroValue(colInfos[idx]) - } - } - } - - // Handle the bad null error. - if (mysql.HasNotNullFlag(colInfos[idx].GetFlag()) || mysql.HasPreventNullInsertFlag(colInfos[idx].GetFlag())) && castDatum.IsNull() { - castDatum = GetZeroValue(colInfos[idx]) - } - virCols.AppendDatum(i, &castDatum) - } - req.SetCol(idx, virCols.Column(i)) - } - return nil -} diff --git a/table/column_test.go b/table/column_test.go deleted file mode 100644 index bb20858aba81d..0000000000000 --- a/table/column_test.go +++ /dev/null @@ -1,498 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package table - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestString(t *testing.T) { - col := ToColumn(&model.ColumnInfo{ - FieldType: *types.NewFieldType(mysql.TypeTiny), - State: model.StatePublic, - }) - col.SetFlen(2) - col.SetDecimal(1) - col.SetCharset(mysql.DefaultCharset) - col.SetCollate(mysql.DefaultCollationName) - col.AddFlag(mysql.ZerofillFlag | mysql.UnsignedFlag | mysql.BinaryFlag | mysql.AutoIncrementFlag | mysql.NotNullFlag) - - require.Equal(t, "tinyint(2) unsigned zerofill", col.GetTypeDesc()) - col.ToInfo() - tbInfo := &model.TableInfo{} - require.False(t, col.IsPKHandleColumn(tbInfo)) - tbInfo.PKIsHandle = true - col.AddFlag(mysql.PriKeyFlag) - require.True(t, col.IsPKHandleColumn(tbInfo)) - - cs := col.String() - require.Greater(t, len(cs), 0) - - col.SetType(mysql.TypeEnum) - col.SetFlag(0) - col.SetElems([]string{"a", "b"}) - - require.Equal(t, "enum('a','b')", col.GetTypeDesc()) - - col.SetElems([]string{"'a'", "b"}) - require.Equal(t, "enum('''a''','b')", col.GetTypeDesc()) - - col.SetType(mysql.TypeFloat) - col.SetFlen(8) - col.SetDecimal(-1) - require.Equal(t, "float", col.GetTypeDesc()) - - col.SetDecimal(1) - require.Equal(t, "float(8,1)", col.GetTypeDesc()) - - col.SetType(mysql.TypeDatetime) - col.SetDecimal(6) - require.Equal(t, "datetime(6)", col.GetTypeDesc()) - - col.SetDecimal(0) - require.Equal(t, "datetime", col.GetTypeDesc()) - - col.SetDecimal(-1) - require.Equal(t, "datetime", col.GetTypeDesc()) -} - -func TestFind(t *testing.T) { - cols := []*Column{ - newCol("a"), - newCol("b"), - newCol("c"), - } - c, s := FindCols(cols, []string{"a"}, true) - require.Equal(t, cols[:1], c) - require.Equal(t, "", s) - - c1, s1 := FindCols(cols, []string{"d"}, true) - require.Nil(t, c1) - require.Equal(t, "d", s1) - - cols[0].AddFlag(mysql.OnUpdateNowFlag) - c2 := FindOnUpdateCols(cols) - require.Equal(t, cols[:1], c2) -} - -// checkNotNull checks if row has nil value set to a column with NotNull flag set. -func checkNotNull(cols []*Column, row []types.Datum) error { - for _, c := range cols { - if err := c.CheckNotNull(&row[c.Offset], 0); err != nil { - return err - } - } - return nil -} - -func TestCheck(t *testing.T) { - col := newCol("a") - col.SetFlag(mysql.AutoIncrementFlag) - cols := []*Column{col, col} - err := CheckOnce(cols) - require.Error(t, err) - cols = cols[:1] - err = checkNotNull(cols, types.MakeDatums(nil)) - require.NoError(t, err) - cols[0].AddFlag(mysql.NotNullFlag) - err = checkNotNull(cols, types.MakeDatums(nil)) - require.Error(t, err) - err = CheckOnce([]*Column{}) - require.NoError(t, err) -} - -func TestHandleBadNull(t *testing.T) { - col := newCol("a") - sc := stmtctx.NewStmtCtx() - d := types.Datum{} - err := col.HandleBadNull(&d, sc, 0) - require.NoError(t, err) - cmp, err := d.Compare(sc, &types.Datum{}, collate.GetBinaryCollator()) - require.NoError(t, err) - require.Equal(t, 0, cmp) - - col.AddFlag(mysql.NotNullFlag) - err = col.HandleBadNull(&types.Datum{}, sc, 0) - require.Error(t, err) - - sc.BadNullAsWarning = true - err = col.HandleBadNull(&types.Datum{}, sc, 0) - require.NoError(t, err) -} - -func TestDesc(t *testing.T) { - col := newCol("a") - col.SetFlag(mysql.AutoIncrementFlag | mysql.NotNullFlag | mysql.PriKeyFlag) - NewColDesc(col) - col.SetFlag(mysql.MultipleKeyFlag) - NewColDesc(col) - col.SetFlag(mysql.UniqueKeyFlag | mysql.OnUpdateNowFlag) - desc := NewColDesc(col) - require.Equal(t, "DEFAULT_GENERATED on update CURRENT_TIMESTAMP", desc.Extra) - col.SetFlag(0) - col.GeneratedExprString = "test" - col.GeneratedStored = true - desc = NewColDesc(col) - require.Equal(t, "STORED GENERATED", desc.Extra) - col.GeneratedStored = false - desc = NewColDesc(col) - require.Equal(t, "VIRTUAL GENERATED", desc.Extra) - ColDescFieldNames(false) - ColDescFieldNames(true) -} - -func TestGetZeroValue(t *testing.T) { - tp1 := &types.FieldType{} - tp1.SetType(mysql.TypeLonglong) - tp1.SetFlag(mysql.UnsignedFlag) - - tp2 := &types.FieldType{} - tp2.SetType(mysql.TypeString) - tp2.SetFlen(2) - tp2.SetCharset(charset.CharsetBin) - tp2.SetCollate(charset.CollationBin) - - tp3 := &types.FieldType{} - tp3.SetType(mysql.TypeString) - tp3.SetFlen(2) - tp3.SetCharset(charset.CharsetUTF8MB4) - tp3.SetCollate(charset.CollationBin) - - tests := []struct { - ft *types.FieldType - value types.Datum - }{ - { - types.NewFieldType(mysql.TypeLong), - types.NewIntDatum(0), - }, - { - tp1, - types.NewUintDatum(0), - }, - { - types.NewFieldType(mysql.TypeFloat), - types.NewFloat32Datum(0), - }, - { - types.NewFieldType(mysql.TypeDouble), - types.NewFloat64Datum(0), - }, - { - types.NewFieldType(mysql.TypeNewDecimal), - types.NewDecimalDatum(types.NewDecFromInt(0)), - }, - { - types.NewFieldType(mysql.TypeVarchar), - types.NewStringDatum(""), - }, - { - types.NewFieldType(mysql.TypeBlob), - types.NewStringDatum(""), - }, - { - types.NewFieldType(mysql.TypeDuration), - types.NewDurationDatum(types.ZeroDuration), - }, - { - types.NewFieldType(mysql.TypeDatetime), - types.NewDatum(types.ZeroDatetime), - }, - { - types.NewFieldType(mysql.TypeTimestamp), - types.NewDatum(types.ZeroTimestamp), - }, - { - types.NewFieldType(mysql.TypeDate), - types.NewDatum(types.ZeroDate), - }, - { - types.NewFieldType(mysql.TypeBit), - types.NewMysqlBitDatum(types.ZeroBinaryLiteral), - }, - { - types.NewFieldType(mysql.TypeSet), - types.NewDatum(types.Set{}), - }, - { - types.NewFieldType(mysql.TypeEnum), - types.NewDatum(types.Enum{}), - }, - { - tp2, - types.NewDatum(make([]byte, 2)), - }, - { - tp3, - types.NewDatum(""), - }, - { - types.NewFieldType(mysql.TypeJSON), - types.NewDatum(types.CreateBinaryJSON(nil)), - }, - } - sc := stmtctx.NewStmtCtx() - for _, tt := range tests { - t.Run(fmt.Sprintf("%+v", tt.ft), func(t *testing.T) { - colInfo := &model.ColumnInfo{FieldType: *tt.ft} - zv := GetZeroValue(colInfo) - require.Equal(t, tt.value.Kind(), zv.Kind()) - cmp, err := zv.Compare(sc, &tt.value, collate.GetCollator(tt.ft.GetCollate())) - require.NoError(t, err) - require.Equal(t, 0, cmp) - }) - } -} - -func TestCastValue(t *testing.T) { - ctx := mock.NewContext() - colInfo := model.ColumnInfo{ - FieldType: *types.NewFieldType(mysql.TypeLong), - State: model.StatePublic, - } - colInfo.SetCharset(mysql.UTF8Charset) - val, err := CastValue(ctx, types.Datum{}, &colInfo, false, false) - require.NoError(t, err) - require.Equal(t, int64(0), val.GetInt64()) - - val, err = CastValue(ctx, types.NewDatum("test"), &colInfo, false, false) - require.Error(t, err) - require.Equal(t, int64(0), val.GetInt64()) - - colInfoS := model.ColumnInfo{ - FieldType: *types.NewFieldType(mysql.TypeString), - State: model.StatePublic, - } - val, err = CastValue(ctx, types.NewDatum("test"), &colInfoS, false, false) - require.NoError(t, err) - require.NotNil(t, val) - - colInfoS.SetCharset(mysql.UTF8Charset) - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false, false) - require.Error(t, err) - - colInfoS.SetCharset(mysql.UTF8Charset) - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x8c, 0x80}), &colInfoS, false, true) - require.NoError(t, err) - - colInfoS.SetCharset(mysql.UTF8MB4Charset) - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false, false) - require.Error(t, err) - - colInfoS.SetCharset(mysql.UTF8MB4Charset) - _, err = CastValue(ctx, types.NewDatum([]byte{0xf0, 0x9f, 0x80}), &colInfoS, false, true) - require.NoError(t, err) - - colInfoS.SetCharset(charset.CharsetASCII) - _, err = CastValue(ctx, types.NewDatum([]byte{0x32, 0xf0}), &colInfoS, false, false) - require.Error(t, err) - - colInfoS.SetCharset(charset.CharsetASCII) - _, err = CastValue(ctx, types.NewDatum([]byte{0x32, 0xf0}), &colInfoS, false, true) - require.NoError(t, err) - - colInfoS.SetCharset(charset.CharsetUTF8MB4) - colInfoS.SetCollate("utf8mb4_general_ci") - val, err = CastValue(ctx, types.NewBinaryLiteralDatum([]byte{0xE5, 0xA5, 0xBD}), &colInfoS, false, false) - require.NoError(t, err) - require.Equal(t, "utf8mb4_general_ci", val.Collation()) - val, err = CastValue(ctx, types.NewBinaryLiteralDatum([]byte{0xE5, 0xA5, 0xBD, 0x81}), &colInfoS, false, false) - require.Error(t, err, "[table:1366]Incorrect string value '\\x81' for column ''") - require.Equal(t, "utf8mb4_general_ci", val.Collation()) - val, err = CastValue(ctx, types.NewDatum([]byte{0xE5, 0xA5, 0xBD, 0x81}), &colInfoS, false, false) - require.Error(t, err, "[table:1366]Incorrect string value '\\x81' for column ''") - require.Equal(t, "utf8mb4_general_ci", val.Collation()) -} - -func TestGetDefaultValue(t *testing.T) { - var nilDt types.Datum - nilDt.SetNull() - ctx := mock.NewContext() - zeroTimestamp := types.ZeroTimestamp - timestampValue := types.NewTime(types.FromDate(2019, 5, 6, 12, 48, 49, 0), mysql.TypeTimestamp, types.DefaultFsp) - - tp0 := types.FieldType{} - tp0.SetType(mysql.TypeLonglong) - - tp1 := types.FieldType{} - tp1.SetType(mysql.TypeLonglong) - tp1.SetFlag(mysql.NotNullFlag) - - tp2 := types.FieldType{} - tp2.SetType(mysql.TypeEnum) - tp2.SetFlag(mysql.NotNullFlag) - tp2.SetElems([]string{"abc", "def"}) - tp2.SetCollate(mysql.DefaultCollationName) - - tp3 := types.FieldType{} - tp3.SetType(mysql.TypeTimestamp) - tp3.SetFlag(mysql.TimestampFlag) - - tp4 := types.FieldType{} - tp4.SetType(mysql.TypeLonglong) - tp4.SetFlag(mysql.NotNullFlag | mysql.AutoIncrementFlag) - - tests := []struct { - colInfo *model.ColumnInfo - strict bool - val types.Datum - err error - }{ - { - &model.ColumnInfo{ - FieldType: tp1, - OriginDefaultValue: 1.0, - DefaultValue: 1.0, - }, - false, - types.NewIntDatum(1), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp1, - }, - false, - types.NewIntDatum(0), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp0, - }, - false, - types.Datum{}, - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp2, - }, - false, - types.NewMysqlEnumDatum(types.Enum{Name: "abc", Value: 1}), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp3, - OriginDefaultValue: "0000-00-00 00:00:00", - DefaultValue: "0000-00-00 00:00:00", - }, - false, - types.NewDatum(zeroTimestamp), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp3, - OriginDefaultValue: timestampValue.String(), - DefaultValue: timestampValue.String(), - }, - true, - types.NewDatum(timestampValue), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp3, - OriginDefaultValue: "not valid date", - DefaultValue: "not valid date", - }, - true, - types.NewDatum(zeroTimestamp), - errGetDefaultFailed, - }, - { - &model.ColumnInfo{ - FieldType: tp1, - }, - true, - types.NewDatum(zeroTimestamp), - ErrNoDefaultValue, - }, - { - &model.ColumnInfo{ - FieldType: tp4, - }, - true, - types.NewIntDatum(0), - nil, - }, - { - &model.ColumnInfo{ - FieldType: tp1, - DefaultIsExpr: true, - DefaultValue: "1", - }, - false, - nilDt, - nil, - }, - } - - exp := expression.EvalAstExpr - expression.EvalAstExpr = func(sctx sessionctx.Context, expr ast.ExprNode) (types.Datum, error) { - return types.NewIntDatum(1), nil - } - defer func() { - expression.EvalAstExpr = exp - }() - - for _, tt := range tests { - ctx.GetSessionVars().StmtCtx.BadNullAsWarning = !tt.strict - val, err := GetColDefaultValue(ctx, tt.colInfo) - if err != nil { - require.Errorf(t, tt.err, "%v", err) - continue - } - if tt.colInfo.DefaultIsExpr { - require.Equal(t, types.NewIntDatum(1), val) - } else { - require.Equal(t, tt.val, val) - } - } - - for _, tt := range tests { - ctx.GetSessionVars().StmtCtx.BadNullAsWarning = !tt.strict - val, err := GetColOriginDefaultValue(ctx, tt.colInfo) - if err != nil { - require.Errorf(t, tt.err, "%v", err) - continue - } - if !tt.colInfo.DefaultIsExpr { - require.Equal(t, tt.val, val) - } - } -} - -func newCol(name string) *Column { - return ToColumn(&model.ColumnInfo{ - Name: model.NewCIStr(name), - State: model.StatePublic, - }) -} diff --git a/table/constraint.go b/table/constraint.go deleted file mode 100644 index 16d79fb7f904e..0000000000000 --- a/table/constraint.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2023-2023 PingCAP, Inc. -// -// 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. - -package table - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" - "go.uber.org/zap" -) - -// Constraint provides meta and map dependency describing a table constraint. -type Constraint struct { - *model.ConstraintInfo - ConstraintExpr expression.Expression -} - -// LoadCheckConstraint load check constraint -func LoadCheckConstraint(tblInfo *model.TableInfo) ([]*Constraint, error) { - removeInvalidCheckConstraintsInfo(tblInfo) - - constraints := make([]*Constraint, 0, len(tblInfo.Constraints)) - for _, conInfo := range tblInfo.Constraints { - con, err := ToConstraint(conInfo, tblInfo) - if err != nil { - return nil, err - } - constraints = append(constraints, con) - } - return constraints, nil -} - -// Delete invalid check constraint lazily -func removeInvalidCheckConstraintsInfo(tblInfo *model.TableInfo) { - // check if all the columns referenced by check constraint exist - var conInfos []*model.ConstraintInfo - for _, cons := range tblInfo.Constraints { - valid := true - for _, col := range cons.ConstraintCols { - if tblInfo.FindPublicColumnByName(col.L) == nil { - valid = false - break - } - } - if valid { - conInfos = append(conInfos, cons) - } - } - tblInfo.Constraints = conInfos -} - -// ToConstraint converts model.ConstraintInfo to Constraint -func ToConstraint(constraintInfo *model.ConstraintInfo, tblInfo *model.TableInfo) (*Constraint, error) { - ctx := mock.NewContext() - dbName := model.NewCIStr(ctx.GetSessionVars().CurrentDB) - columns, names, err := expression.ColumnInfos2ColumnsAndNames(ctx, dbName, tblInfo.Name, tblInfo.Columns, tblInfo) - if err != nil { - return nil, errors.Trace(err) - } - expr, err := buildConstraintExpression(ctx, constraintInfo.ExprString, columns, names) - if err != nil { - return nil, errors.Trace(err) - } - return &Constraint{ - constraintInfo, - expr, - }, nil -} - -func buildConstraintExpression(ctx sessionctx.Context, exprString string, - columns []*expression.Column, names types.NameSlice) (expression.Expression, error) { - schema := expression.NewSchema(columns...) - exprs, err := expression.ParseSimpleExprsWithNames(ctx, exprString, schema, names) - if err != nil { - // If it got an error here, ddl may hang forever, so this error log is important. - logutil.BgLogger().Error("wrong check constraint expression", zap.String("expression", exprString), zap.Error(err)) - return nil, errors.Trace(err) - } - return exprs[0], nil -} - -// IsSupportedExpr checks whether the check constraint expression is allowed -func IsSupportedExpr(constr *ast.Constraint) (bool, error) { - checker := &checkConstraintChecker{ - allowed: true, - name: constr.Name, - } - constr.Expr.Accept(checker) - return checker.allowed, checker.reason -} - -var unsupportedNodeForCheckConstraint = map[string]struct{}{ - ast.Now: {}, - ast.CurrentTimestamp: {}, - ast.Curdate: {}, - ast.CurrentDate: {}, - ast.Curtime: {}, - ast.CurrentTime: {}, - ast.LocalTime: {}, - ast.LocalTimestamp: {}, - ast.UnixTimestamp: {}, - ast.UTCDate: {}, - ast.UTCTimestamp: {}, - ast.UTCTime: {}, - ast.ConnectionID: {}, - ast.CurrentUser: {}, - ast.SessionUser: {}, - ast.Version: {}, - ast.FoundRows: {}, - ast.LastInsertId: {}, - ast.SystemUser: {}, - ast.User: {}, - ast.Rand: {}, - ast.RowCount: {}, - ast.GetLock: {}, - ast.IsFreeLock: {}, - ast.IsUsedLock: {}, - ast.ReleaseLock: {}, - ast.ReleaseAllLocks: {}, - ast.LoadFile: {}, - ast.UUID: {}, - ast.UUIDShort: {}, - ast.Sleep: {}, -} - -type checkConstraintChecker struct { - allowed bool - reason error - name string -} - -// Enter implements Visitor interface. -func (checker *checkConstraintChecker) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - switch x := in.(type) { - case *ast.FuncCallExpr: - if _, ok := unsupportedNodeForCheckConstraint[x.FnName.L]; ok { - checker.allowed = false - checker.reason = dbterror.ErrCheckConstraintNamedFuncIsNotAllowed.GenWithStackByArgs(checker.name, x.FnName.L) - return in, true - } - case *ast.VariableExpr: - // user defined or system variable is not allowed - checker.allowed = false - checker.reason = dbterror.ErrCheckConstraintVariables.GenWithStackByArgs(checker.name) - return in, true - case *ast.SubqueryExpr, *ast.ExistsSubqueryExpr: - // subquery is not allowed - checker.allowed = false - checker.reason = dbterror.ErrCheckConstraintFuncIsNotAllowed.GenWithStackByArgs(checker.name) - return in, true - case *ast.DefaultExpr: - // default expr is not allowed - checker.allowed = false - checker.reason = dbterror.ErrCheckConstraintNamedFuncIsNotAllowed.GenWithStackByArgs(checker.name, "default") - return in, true - } - return in, false -} - -// Leave implements Visitor interface. -func (checker *checkConstraintChecker) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, checker.allowed -} - -// ContainsAutoIncrementCol checks if there is auto-increment col in given cols -func ContainsAutoIncrementCol(cols []model.CIStr, tblInfo *model.TableInfo) bool { - if autoIncCol := tblInfo.GetAutoIncrementColInfo(); autoIncCol != nil { - for _, col := range cols { - if col.L == autoIncCol.Name.L { - return true - } - } - } - return false -} - -// HasForeignKeyRefAction checks if there is foreign key with referential action in check constraints -func HasForeignKeyRefAction(fkInfos []*model.FKInfo, constraints []*ast.Constraint, checkConstr *ast.Constraint, dependedCols []model.CIStr) error { - if fkInfos != nil { - return checkForeignKeyRefActionByFKInfo(fkInfos, checkConstr, dependedCols) - } - for _, cons := range constraints { - if cons.Tp != ast.ConstraintForeignKey { - continue - } - refCol := cons.Refer - if refCol.OnDelete.ReferOpt != model.ReferOptionNoOption || refCol.OnUpdate.ReferOpt != model.ReferOptionNoOption { - var fkCols []model.CIStr - for _, key := range cons.Keys { - fkCols = append(fkCols, key.Column.Name) - } - for _, col := range dependedCols { - if hasSpecifiedCol(fkCols, col) { - return dbterror.ErrCheckConstraintUsingFKReferActionColumn.GenWithStackByArgs(col.L, checkConstr.Name) - } - } - } - } - return nil -} - -func checkForeignKeyRefActionByFKInfo(fkInfos []*model.FKInfo, checkConstr *ast.Constraint, dependedCols []model.CIStr) error { - for _, fkInfo := range fkInfos { - if fkInfo.OnDelete != 0 || fkInfo.OnUpdate != 0 { - for _, col := range dependedCols { - if hasSpecifiedCol(fkInfo.Cols, col) { - return dbterror.ErrCheckConstraintUsingFKReferActionColumn.GenWithStackByArgs(col.L, checkConstr.Name) - } - } - } - } - return nil -} - -func hasSpecifiedCol(cols []model.CIStr, col model.CIStr) bool { - for _, c := range cols { - if c.L == col.L { - return true - } - } - return false -} - -// IfCheckConstraintExprBoolType checks whether the check expression is bool type -func IfCheckConstraintExprBoolType(info *model.ConstraintInfo, tableInfo *model.TableInfo) error { - cons, err := ToConstraint(info, tableInfo) - if err != nil { - return err - } - if !mysql.HasIsBooleanFlag(cons.ConstraintExpr.GetType().GetFlag()) { - return dbterror.ErrNonBooleanExprForCheckConstraint.GenWithStackByArgs(cons.Name) - } - return nil -} diff --git a/table/index.go b/table/index.go deleted file mode 100644 index a33b9c05f0049..0000000000000 --- a/table/index.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package table - -import ( - "context" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" -) - -// IndexIterator is the interface for iterator of index data on KV store. -type IndexIterator interface { - Next() (k []types.Datum, h kv.Handle, err error) - Close() -} - -// CreateIdxOpt contains the options will be used when creating an index. -type CreateIdxOpt struct { - Ctx context.Context - Untouched bool // If true, the index key/value is no need to commit. - IgnoreAssertion bool - FromBackFill bool -} - -// CreateIdxOptFunc is defined for the Create() method of Index interface. -// Here is a blog post about how to use this pattern: -// https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis -type CreateIdxOptFunc func(*CreateIdxOpt) - -// IndexIsUntouched uses to indicate the index kv is untouched. -var IndexIsUntouched CreateIdxOptFunc = func(opt *CreateIdxOpt) { - opt.Untouched = true -} - -// WithIgnoreAssertion uses to indicate the process can ignore assertion. -var WithIgnoreAssertion = func(opt *CreateIdxOpt) { - opt.IgnoreAssertion = true -} - -// FromBackfill indicates that the index is created by DDL backfill worker. -// In the backfill-merge process, the index KVs from DML will be redirected to -// the temp index. On the other hand, the index KVs from DDL backfill worker should -// never be redirected to the temp index. -var FromBackfill = func(opt *CreateIdxOpt) { - opt.FromBackFill = true -} - -// WithCtx returns a CreateIdxFunc. -// This option is used to pass context.Context. -func WithCtx(ctx context.Context) CreateIdxOptFunc { - return func(opt *CreateIdxOpt) { - opt.Ctx = ctx - } -} - -// IndexIter is index kvs iter. -type IndexIter interface { - Next(kb []byte) ([]byte, []byte, bool, error) - Valid() bool -} - -// Index is the interface for index data on KV store. -type Index interface { - // Meta returns IndexInfo. - Meta() *model.IndexInfo - // TableMeta returns TableInfo - TableMeta() *model.TableInfo - // Create supports insert into statement. - Create(ctx sessionctx.Context, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle, handleRestoreData []types.Datum, opts ...CreateIdxOptFunc) (kv.Handle, error) - // Delete supports delete from statement. - Delete(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle) error - // GenIndexKVIter generate index key and value for multi-valued index, use iterator to reduce the memory allocation. - GenIndexKVIter(sc *stmtctx.StatementContext, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum) IndexIter - // Exist supports check index exists or not. - Exist(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValues []types.Datum, h kv.Handle) (bool, kv.Handle, error) - // GenIndexKey generates an index key. If the index is a multi-valued index, use GenIndexKVIter instead. - GenIndexKey(sc *stmtctx.StatementContext, indexedValues []types.Datum, h kv.Handle, buf []byte) (key []byte, distinct bool, err error) - // GenIndexValue generates an index value. - GenIndexValue(sc *stmtctx.StatementContext, distinct bool, indexedValues []types.Datum, h kv.Handle, restoredData []types.Datum) ([]byte, error) - // FetchValues fetched index column values in a row. - // Param columns is a reused buffer, if it is not nil, FetchValues will fill the index values in it, - // and return the buffer, if it is nil, FetchValues will allocate the buffer instead. - FetchValues(row []types.Datum, columns []types.Datum) ([]types.Datum, error) -} diff --git a/table/main_test.go b/table/main_test.go deleted file mode 100644 index ad265d56a2254..0000000000000 --- a/table/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package table - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/table/table.go b/table/table.go deleted file mode 100644 index 5f34351c2a6a3..0000000000000 --- a/table/table.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package table - -import ( - "context" - "time" - - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/tracing" -) - -// Type is used to distinguish between different tables that store data in different ways. -type Type int16 - -const ( - // NormalTable stores data in tikv, mocktikv and so on. - NormalTable Type = iota - // VirtualTable stores no data, just extract data from the memory struct. - VirtualTable - // ClusterTable contains the `VirtualTable` in the all cluster tidb nodes. - ClusterTable -) - -// IsNormalTable checks whether the table is a normal table type. -func (tp Type) IsNormalTable() bool { - return tp == NormalTable -} - -// IsVirtualTable checks whether the table is a virtual table type. -func (tp Type) IsVirtualTable() bool { - return tp == VirtualTable -} - -// IsClusterTable checks whether the table is a cluster table type. -func (tp Type) IsClusterTable() bool { - return tp == ClusterTable -} - -var ( - // ErrColumnCantNull is used for inserting null to a not null column. - ErrColumnCantNull = dbterror.ClassTable.NewStd(mysql.ErrBadNull) - // ErrUnknownColumn is returned when accessing an unknown column. - ErrUnknownColumn = dbterror.ClassTable.NewStd(mysql.ErrBadField) - errDuplicateColumn = dbterror.ClassTable.NewStd(mysql.ErrFieldSpecifiedTwice) - - // ErrWarnNullToNotnull is like ErrColumnCantNull but it's used in LOAD DATA - ErrWarnNullToNotnull = dbterror.ClassExecutor.NewStd(mysql.ErrWarnNullToNotnull) - - errGetDefaultFailed = dbterror.ClassTable.NewStd(mysql.ErrFieldGetDefaultFailed) - - // ErrNoDefaultValue is used when insert a row, the column value is not given, and the column has not null flag - // and it doesn't have a default value. - ErrNoDefaultValue = dbterror.ClassTable.NewStd(mysql.ErrNoDefaultForField) - // ErrIndexOutBound returns for index column offset out of bound. - ErrIndexOutBound = dbterror.ClassTable.NewStd(mysql.ErrIndexOutBound) - // ErrUnsupportedOp returns for unsupported operation. - ErrUnsupportedOp = dbterror.ClassTable.NewStd(mysql.ErrUnsupportedOp) - // ErrRowNotFound returns for row not found. - ErrRowNotFound = dbterror.ClassTable.NewStd(mysql.ErrRowNotFound) - // ErrTableStateCantNone returns for table none state. - ErrTableStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrTableStateCantNone) - // ErrColumnStateCantNone returns for column none state. - ErrColumnStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrColumnStateCantNone) - // ErrColumnStateNonPublic returns for column non-public state. - ErrColumnStateNonPublic = dbterror.ClassTable.NewStd(mysql.ErrColumnStateNonPublic) - // ErrIndexStateCantNone returns for index none state. - ErrIndexStateCantNone = dbterror.ClassTable.NewStd(mysql.ErrIndexStateCantNone) - // ErrInvalidRecordKey returns for invalid record key. - ErrInvalidRecordKey = dbterror.ClassTable.NewStd(mysql.ErrInvalidRecordKey) - // ErrTruncatedWrongValueForField returns for truncate wrong value for field. - ErrTruncatedWrongValueForField = dbterror.ClassTable.NewStd(mysql.ErrTruncatedWrongValueForField) - // ErrUnknownPartition returns unknown partition error. - ErrUnknownPartition = dbterror.ClassTable.NewStd(mysql.ErrUnknownPartition) - // ErrNoPartitionForGivenValue returns table has no partition for value. - ErrNoPartitionForGivenValue = dbterror.ClassTable.NewStd(mysql.ErrNoPartitionForGivenValue) - // ErrLockOrActiveTransaction returns when execute unsupported statement in a lock session or an active transaction. - ErrLockOrActiveTransaction = dbterror.ClassTable.NewStd(mysql.ErrLockOrActiveTransaction) - // ErrSequenceHasRunOut returns when sequence has run out. - ErrSequenceHasRunOut = dbterror.ClassTable.NewStd(mysql.ErrSequenceRunOut) - // ErrRowDoesNotMatchGivenPartitionSet returns when the destination partition conflict with the partition selection. - ErrRowDoesNotMatchGivenPartitionSet = dbterror.ClassTable.NewStd(mysql.ErrRowDoesNotMatchGivenPartitionSet) - // ErrTempTableFull returns a table is full error, it's used by temporary table now. - ErrTempTableFull = dbterror.ClassTable.NewStd(mysql.ErrRecordFileFull) - // ErrOptOnCacheTable returns when exec unsupported opt at cache mode - ErrOptOnCacheTable = dbterror.ClassDDL.NewStd(mysql.ErrOptOnCacheTable) - // ErrCheckConstraintViolated return when check constraint is violated. - ErrCheckConstraintViolated = dbterror.ClassTable.NewStd(mysql.ErrCheckConstraintViolated) -) - -// RecordIterFunc is used for low-level record iteration. -type RecordIterFunc func(h kv.Handle, rec []types.Datum, cols []*Column) (more bool, err error) - -// AddRecordOpt contains the options will be used when adding a record. -type AddRecordOpt struct { - CreateIdxOpt - IsUpdate bool - ReserveAutoID int -} - -// AddRecordOption is defined for the AddRecord() method of the Table interface. -type AddRecordOption interface { - ApplyOn(*AddRecordOpt) -} - -// WithReserveAutoIDHint tells the AddRecord operation to reserve a batch of auto ID in the stmtctx. -type WithReserveAutoIDHint int - -// ApplyOn implements the AddRecordOption interface. -func (n WithReserveAutoIDHint) ApplyOn(opt *AddRecordOpt) { - opt.ReserveAutoID = int(n) -} - -// ApplyOn implements the AddRecordOption interface, so any CreateIdxOptFunc -// can be passed as the optional argument to the table.AddRecord method. -func (f CreateIdxOptFunc) ApplyOn(opt *AddRecordOpt) { - f(&opt.CreateIdxOpt) -} - -// IsUpdate is a defined value for AddRecordOptFunc. -var IsUpdate AddRecordOption = isUpdate{} - -type isUpdate struct{} - -func (i isUpdate) ApplyOn(opt *AddRecordOpt) { - opt.IsUpdate = true -} - -type columnAPI interface { - // Cols returns the columns of the table which is used in select, including hidden columns. - Cols() []*Column - - // VisibleCols returns the columns of the table which is used in select, excluding hidden columns. - VisibleCols() []*Column - - // HiddenCols returns the hidden columns of the table. - HiddenCols() []*Column - - // WritableCols returns columns of the table in writable states. - // Writable states includes Public, WriteOnly, WriteOnlyReorganization. - WritableCols() []*Column - - // DeletableCols returns columns of the table in deletable states. - // Deletable states includes Public, WriteOnly, WriteOnlyReorganization, DeleteOnly, DeleteReorganization. - DeletableCols() []*Column - - // FullHiddenColsAndVisibleCols returns hidden columns in all states and unhidden columns in public states. - FullHiddenColsAndVisibleCols() []*Column -} - -// Table is used to retrieve and modify rows in table. -type Table interface { - columnAPI - - // Indices returns the indices of the table. - // The caller must be aware of that not all the returned indices are public. - Indices() []Index - - // RecordPrefix returns the record key prefix. - RecordPrefix() kv.Key - // IndexPrefix returns the index key prefix. - IndexPrefix() kv.Key - - // AddRecord inserts a row which should contain only public columns - AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...AddRecordOption) (recordID kv.Handle, err error) - - // UpdateRecord updates a row which should contain only writable columns. - UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error - - // RemoveRecord removes a row in the table. - RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error - - // Allocators returns all allocators. - Allocators(ctx sessionctx.Context) autoid.Allocators - - // Meta returns TableInfo. - Meta() *model.TableInfo - - // Type returns the type of table - Type() Type - - // GetPartitionedTable returns nil if not partitioned - GetPartitionedTable() PartitionedTable -} - -// AllocAutoIncrementValue allocates an auto_increment value for a new row. -func AllocAutoIncrementValue(ctx context.Context, t Table, sctx sessionctx.Context) (int64, error) { - r, ctx := tracing.StartRegionEx(ctx, "table.AllocAutoIncrementValue") - defer r.End() - increment := sctx.GetSessionVars().AutoIncrementIncrement - offset := sctx.GetSessionVars().AutoIncrementOffset - alloc := t.Allocators(sctx).Get(autoid.AutoIncrementType) - _, max, err := alloc.Alloc(ctx, uint64(1), int64(increment), int64(offset)) - if err != nil { - return 0, err - } - return max, err -} - -// AllocBatchAutoIncrementValue allocates batch auto_increment value for rows, returning firstID, increment and err. -// The caller can derive the autoID by adding increment to firstID for N-1 times. -func AllocBatchAutoIncrementValue(ctx context.Context, t Table, sctx sessionctx.Context, N int) (firstID int64, increment int64, err error) { - increment = int64(sctx.GetSessionVars().AutoIncrementIncrement) - offset := int64(sctx.GetSessionVars().AutoIncrementOffset) - alloc := t.Allocators(sctx).Get(autoid.AutoIncrementType) - min, max, err := alloc.Alloc(ctx, uint64(N), increment, offset) - if err != nil { - return min, max, err - } - // SeekToFirstAutoIDUnSigned seeks to first autoID. Because AutoIncrement always allocate from 1, - // signed and unsigned value can be unified as the unsigned handle. - nr := int64(autoid.SeekToFirstAutoIDUnSigned(uint64(min), uint64(increment), uint64(offset))) - return nr, increment, nil -} - -// PhysicalTable is an abstraction for two kinds of table representation: partition or non-partitioned table. -// PhysicalID is a ID that can be used to construct a key ranges, all the data in the key range belongs to the corresponding PhysicalTable. -// For a non-partitioned table, its PhysicalID equals to its TableID; For a partition of a partitioned table, its PhysicalID is the partition's ID. -type PhysicalTable interface { - Table - GetPhysicalID() int64 -} - -// PartitionedTable is a Table, and it has a GetPartition() method. -// GetPartition() gets the partition from a partition table by a physical table ID, -type PartitionedTable interface { - Table - GetPartition(physicalID int64) PhysicalTable - GetPartitionByRow(sessionctx.Context, []types.Datum) (PhysicalTable, error) - GetAllPartitionIDs() []int64 - GetPartitionColumnIDs() []int64 - GetPartitionColumnNames() []model.CIStr - CheckForExchangePartition(ctx sessionctx.Context, pi *model.PartitionInfo, r []types.Datum, partID, ntID int64) error -} - -// TableFromMeta builds a table.Table from *model.TableInfo. -// Currently, it is assigned to tables.TableFromMeta in tidb package's init function. -var TableFromMeta func(allocators autoid.Allocators, tblInfo *model.TableInfo) (Table, error) - -// MockTableFromMeta only serves for test. -var MockTableFromMeta func(tableInfo *model.TableInfo) Table - -// CachedTable is a Table, and it has a UpdateLockForRead() method -// UpdateLockForRead() according to the reasons for not meeting the read conditions, update the lock information, -// And at the same time reload data from the original table. -type CachedTable interface { - Table - - Init(exec sqlexec.SQLExecutor) error - - // TryReadFromCache checks if the cache table is readable. - TryReadFromCache(ts uint64, leaseDuration time.Duration) (kv.MemBuffer, bool) - - // UpdateLockForRead if you cannot meet the conditions of the read buffer, - // you need to update the lock information and read the data from the original table - UpdateLockForRead(ctx context.Context, store kv.Storage, ts uint64, leaseDuration time.Duration) - - // WriteLockAndKeepAlive first obtain the write lock, then it renew the lease to keep the lock alive. - // 'exit' is a channel to tell the keep alive goroutine to exit. - // The result is sent to the 'wg' channel. - WriteLockAndKeepAlive(ctx context.Context, exit chan struct{}, leasePtr *uint64, wg chan error) -} diff --git a/table/table_test.go b/table/table_test.go deleted file mode 100644 index 3cc6f16c4fcdd..0000000000000 --- a/table/table_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package table - -import ( - "testing" - - mysql "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/require" -) - -func TestErrorCode(t *testing.T) { - require.Equal(t, mysql.ErrBadNull, int(terror.ToSQLError(ErrColumnCantNull).Code)) - require.Equal(t, mysql.ErrBadField, int(terror.ToSQLError(ErrUnknownColumn).Code)) - require.Equal(t, mysql.ErrFieldSpecifiedTwice, int(terror.ToSQLError(errDuplicateColumn).Code)) - require.Equal(t, mysql.ErrFieldGetDefaultFailed, int(terror.ToSQLError(errGetDefaultFailed).Code)) - require.Equal(t, mysql.ErrNoDefaultForField, int(terror.ToSQLError(ErrNoDefaultValue).Code)) - require.Equal(t, mysql.ErrIndexOutBound, int(terror.ToSQLError(ErrIndexOutBound).Code)) - require.Equal(t, mysql.ErrUnsupportedOp, int(terror.ToSQLError(ErrUnsupportedOp).Code)) - require.Equal(t, mysql.ErrRowNotFound, int(terror.ToSQLError(ErrRowNotFound).Code)) - require.Equal(t, mysql.ErrTableStateCantNone, int(terror.ToSQLError(ErrTableStateCantNone).Code)) - require.Equal(t, mysql.ErrColumnStateCantNone, int(terror.ToSQLError(ErrColumnStateCantNone).Code)) - require.Equal(t, mysql.ErrColumnStateNonPublic, int(terror.ToSQLError(ErrColumnStateNonPublic).Code)) - require.Equal(t, mysql.ErrIndexStateCantNone, int(terror.ToSQLError(ErrIndexStateCantNone).Code)) - require.Equal(t, mysql.ErrInvalidRecordKey, int(terror.ToSQLError(ErrInvalidRecordKey).Code)) - require.Equal(t, mysql.ErrTruncatedWrongValueForField, int(terror.ToSQLError(ErrTruncatedWrongValueForField).Code)) - require.Equal(t, mysql.ErrUnknownPartition, int(terror.ToSQLError(ErrUnknownPartition).Code)) - require.Equal(t, mysql.ErrNoPartitionForGivenValue, int(terror.ToSQLError(ErrNoPartitionForGivenValue).Code)) - require.Equal(t, mysql.ErrLockOrActiveTransaction, int(terror.ToSQLError(ErrLockOrActiveTransaction).Code)) -} diff --git a/table/tables/BUILD.bazel b/table/tables/BUILD.bazel deleted file mode 100644 index 7c1f9ffe03fc5..0000000000000 --- a/table/tables/BUILD.bazel +++ /dev/null @@ -1,116 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "tables", - srcs = [ - "cache.go", - "index.go", - "mutation_checker.go", - "partition.go", - "state_remote.go", - "tables.go", - "testutil.go", - ], - importpath = "github.com/pingcap/tidb/table/tables", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//expression", - "//kv", - "//meta", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//sessionctx/binloginfo", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//statistics", - "//table", - "//tablecodec", - "//types", - "//util", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/generatedexpr", - "//util/hack", - "//util/logutil", - "//util/mock", - "//util/ranger", - "//util/rowcodec", - "//util/sqlexec", - "//util/stringutil", - "//util/tableutil", - "//util/tracing", - "@com_github_google_btree//:btree", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "tables_test", - timeout = "short", - srcs = [ - "cache_test.go", - "index_test.go", - "main_test.go", - "mutation_checker_test.go", - "state_remote_test.go", - "tables_test.go", - ], - embed = [":tables"], - flaky = True, - shard_count = 30, - deps = [ - "//ddl", - "//ddl/util/callback", - "//domain", - "//infoschema", - "//kv", - "//meta/autoid", - "//metrics", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//session", - "//sessionctx", - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//sessiontxn", - "//store/helper", - "//table", - "//tablecodec", - "//testkit", - "//testkit/external", - "//testkit/testsetup", - "//types", - "//util", - "//util/codec", - "//util/collate", - "//util/mock", - "//util/rowcodec", - "//util/stmtsummary", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//oracle", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/table/tables/cache.go b/table/tables/cache.go deleted file mode 100644 index 5061491a11c45..0000000000000 --- a/table/tables/cache.go +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tables - -import ( - "context" - "sync/atomic" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/log" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" -) - -var ( - _ table.CachedTable = &cachedTable{} -) - -type cachedTable struct { - TableCommon - cacheData atomic.Value - totalSize int64 - // StateRemote is not thread-safe, this tokenLimit is used to keep only one visitor. - tokenLimit -} - -type tokenLimit chan StateRemote - -func (t tokenLimit) TakeStateRemoteHandle() StateRemote { - handle := <-t - return handle -} - -func (t tokenLimit) TakeStateRemoteHandleNoWait() StateRemote { - select { - case handle := <-t: - return handle - default: - return nil - } -} - -func (t tokenLimit) PutStateRemoteHandle(handle StateRemote) { - t <- handle -} - -// cacheData pack the cache data and lease. -type cacheData struct { - Start uint64 - Lease uint64 - kv.MemBuffer -} - -func leaseFromTS(ts uint64, leaseDuration time.Duration) uint64 { - physicalTime := oracle.GetTimeFromTS(ts) - lease := oracle.GoTimeToTS(physicalTime.Add(leaseDuration)) - return lease -} - -func newMemBuffer(store kv.Storage) (kv.MemBuffer, error) { - // Here is a trick to get a MemBuffer data, because the internal API is not exposed. - // Create a transaction with start ts 0, and take the MemBuffer out. - buffTxn, err := store.Begin(tikv.WithStartTS(0)) - if err != nil { - return nil, err - } - return buffTxn.GetMemBuffer(), nil -} - -func (c *cachedTable) TryReadFromCache(ts uint64, leaseDuration time.Duration) (kv.MemBuffer, bool /*loading*/) { - tmp := c.cacheData.Load() - if tmp == nil { - return nil, false - } - data := tmp.(*cacheData) - if ts >= data.Start && ts < data.Lease { - leaseTime := oracle.GetTimeFromTS(data.Lease) - nowTime := oracle.GetTimeFromTS(ts) - distance := leaseTime.Sub(nowTime) - - var triggerFailpoint bool - failpoint.Inject("mockRenewLeaseABA1", func(_ failpoint.Value) { - triggerFailpoint = true - }) - - if distance >= 0 && distance <= leaseDuration/2 || triggerFailpoint { - if h := c.TakeStateRemoteHandleNoWait(); h != nil { - go c.renewLease(h, ts, data, leaseDuration) - } - } - // If data is not nil, but data.MemBuffer is nil, it means the data is being - // loading by a background goroutine. - return data.MemBuffer, data.MemBuffer == nil - } - return nil, false -} - -// newCachedTable creates a new CachedTable Instance -func newCachedTable(tbl *TableCommon) (table.Table, error) { - ret := &cachedTable{ - TableCommon: *tbl, - tokenLimit: make(chan StateRemote, 1), - } - return ret, nil -} - -// Init is an extra operation for cachedTable after TableFromMeta, -// Because cachedTable need some additional parameter that can't be passed in TableFromMeta. -func (c *cachedTable) Init(exec sqlexec.SQLExecutor) error { - raw, ok := exec.(sqlExec) - if !ok { - return errors.New("Need sqlExec rather than sqlexec.SQLExecutor") - } - handle := NewStateRemote(raw) - c.PutStateRemoteHandle(handle) - return nil -} - -func (c *cachedTable) loadDataFromOriginalTable(store kv.Storage) (kv.MemBuffer, uint64, int64, error) { - buffer, err := newMemBuffer(store) - if err != nil { - return nil, 0, 0, err - } - var startTS uint64 - totalSize := int64(0) - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnCacheTable) - err = kv.RunInNewTxn(ctx, store, true, func(ctx context.Context, txn kv.Transaction) error { - prefix := tablecodec.GenTablePrefix(c.tableID) - if err != nil { - return errors.Trace(err) - } - startTS = txn.StartTS() - it, err := txn.Iter(prefix, prefix.PrefixNext()) - if err != nil { - return errors.Trace(err) - } - defer it.Close() - - for it.Valid() && it.Key().HasPrefix(prefix) { - key := it.Key() - value := it.Value() - err = buffer.Set(key, value) - if err != nil { - return errors.Trace(err) - } - totalSize += int64(len(key)) - totalSize += int64(len(value)) - err = it.Next() - if err != nil { - return errors.Trace(err) - } - } - return nil - }) - if err != nil { - return nil, 0, totalSize, err - } - - return buffer, startTS, totalSize, nil -} - -func (c *cachedTable) UpdateLockForRead(ctx context.Context, store kv.Storage, ts uint64, leaseDuration time.Duration) { - if h := c.TakeStateRemoteHandleNoWait(); h != nil { - go c.updateLockForRead(ctx, h, store, ts, leaseDuration) - } -} - -func (c *cachedTable) updateLockForRead(ctx context.Context, handle StateRemote, store kv.Storage, ts uint64, leaseDuration time.Duration) { - defer func() { - if r := recover(); r != nil { - log.Error("panic in the recoverable goroutine", - zap.Any("r", r), - zap.Stack("stack trace")) - } - c.PutStateRemoteHandle(handle) - }() - - // Load data from original table and the update lock information. - tid := c.Meta().ID - lease := leaseFromTS(ts, leaseDuration) - succ, err := handle.LockForRead(ctx, tid, lease) - if err != nil { - log.Warn("lock cached table for read", zap.Error(err)) - return - } - if succ { - c.cacheData.Store(&cacheData{ - Start: ts, - Lease: lease, - MemBuffer: nil, // Async loading, this will be set later. - }) - - // Make the load data process async, in case that loading data takes longer the - // lease duration, then the loaded data get staled and that process repeats forever. - go func() { - start := time.Now() - mb, startTS, totalSize, err := c.loadDataFromOriginalTable(store) - metrics.LoadTableCacheDurationHistogram.Observe(time.Since(start).Seconds()) - if err != nil { - log.Info("load data from table fail", zap.Error(err)) - return - } - - tmp := c.cacheData.Load().(*cacheData) - if tmp != nil && tmp.Start == ts { - c.cacheData.Store(&cacheData{ - Start: startTS, - Lease: tmp.Lease, - MemBuffer: mb, - }) - atomic.StoreInt64(&c.totalSize, totalSize) - } - }() - } - // Current status is not suitable to cache. -} - -const cachedTableSizeLimit = 64 * (1 << 20) - -// AddRecord implements the AddRecord method for the table.Table interface. -func (c *cachedTable) AddRecord(sctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - if atomic.LoadInt64(&c.totalSize) > cachedTableSizeLimit { - return nil, table.ErrOptOnCacheTable.GenWithStackByArgs("table too large") - } - txnCtxAddCachedTable(sctx, c.Meta().ID, c) - return c.TableCommon.AddRecord(sctx, r, opts...) -} - -func txnCtxAddCachedTable(sctx sessionctx.Context, tid int64, handle *cachedTable) { - txnCtx := sctx.GetSessionVars().TxnCtx - if txnCtx.CachedTables == nil { - txnCtx.CachedTables = make(map[int64]interface{}) - } - if _, ok := txnCtx.CachedTables[tid]; !ok { - txnCtx.CachedTables[tid] = handle - } -} - -// UpdateRecord implements table.Table -func (c *cachedTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { - // Prevent furthur writing when the table is already too large. - if atomic.LoadInt64(&c.totalSize) > cachedTableSizeLimit { - return table.ErrOptOnCacheTable.GenWithStackByArgs("table too large") - } - txnCtxAddCachedTable(sctx, c.Meta().ID, c) - return c.TableCommon.UpdateRecord(ctx, sctx, h, oldData, newData, touched) -} - -// RemoveRecord implements table.Table RemoveRecord interface. -func (c *cachedTable) RemoveRecord(sctx sessionctx.Context, h kv.Handle, r []types.Datum) error { - txnCtxAddCachedTable(sctx, c.Meta().ID, c) - return c.TableCommon.RemoveRecord(sctx, h, r) -} - -// TestMockRenewLeaseABA2 is used by test function TestRenewLeaseABAFailPoint. -var TestMockRenewLeaseABA2 chan struct{} - -func (c *cachedTable) renewLease(handle StateRemote, ts uint64, data *cacheData, leaseDuration time.Duration) { - failpoint.Inject("mockRenewLeaseABA2", func(_ failpoint.Value) { - c.PutStateRemoteHandle(handle) - <-TestMockRenewLeaseABA2 - c.TakeStateRemoteHandle() - }) - - defer c.PutStateRemoteHandle(handle) - - tid := c.Meta().ID - lease := leaseFromTS(ts, leaseDuration) - newLease, err := handle.RenewReadLease(context.Background(), tid, data.Lease, lease) - if err != nil { - if !kv.IsTxnRetryableError(err) { - log.Warn("Renew read lease error", zap.Error(err)) - } - return - } - if newLease > 0 { - c.cacheData.Store(&cacheData{ - Start: data.Start, - Lease: newLease, - MemBuffer: data.MemBuffer, - }) - } - - failpoint.Inject("mockRenewLeaseABA2", func(_ failpoint.Value) { - TestMockRenewLeaseABA2 <- struct{}{} - }) -} - -const cacheTableWriteLease = 5 * time.Second - -func (c *cachedTable) WriteLockAndKeepAlive(ctx context.Context, exit chan struct{}, leasePtr *uint64, wg chan error) { - writeLockLease, err := c.lockForWrite(ctx) - atomic.StoreUint64(leasePtr, writeLockLease) - wg <- err - if err != nil { - logutil.Logger(ctx).Warn("lock for write lock fail", zap.String("category", "cached table"), zap.Error(err)) - return - } - - t := time.NewTicker(cacheTableWriteLease / 2) - defer t.Stop() - for { - select { - case <-t.C: - if err := c.renew(ctx, leasePtr); err != nil { - logutil.Logger(ctx).Warn("renew write lock lease fail", zap.String("category", "cached table"), zap.Error(err)) - return - } - case <-exit: - return - } - } -} - -func (c *cachedTable) renew(ctx context.Context, leasePtr *uint64) error { - oldLease := atomic.LoadUint64(leasePtr) - physicalTime := oracle.GetTimeFromTS(oldLease) - newLease := oracle.GoTimeToTS(physicalTime.Add(cacheTableWriteLease)) - - h := c.TakeStateRemoteHandle() - defer c.PutStateRemoteHandle(h) - - succ, err := h.RenewWriteLease(ctx, c.Meta().ID, newLease) - if err != nil { - return errors.Trace(err) - } - if succ { - atomic.StoreUint64(leasePtr, newLease) - } - return nil -} - -func (c *cachedTable) lockForWrite(ctx context.Context) (uint64, error) { - handle := c.TakeStateRemoteHandle() - defer c.PutStateRemoteHandle(handle) - - return handle.LockForWrite(ctx, c.Meta().ID, cacheTableWriteLease) -} diff --git a/table/tables/cache_test.go b/table/tables/cache_test.go deleted file mode 100644 index e87d45c0bbf75..0000000000000 --- a/table/tables/cache_test.go +++ /dev/null @@ -1,560 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tables_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/stmtsummary" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/oracle" -) - -func lastReadFromCache(tk *testkit.TestKit) bool { - return tk.Session().GetSessionVars().StmtCtx.ReadFromTableCache -} - -func TestCacheTableBasicScan(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists tmp1") - tk.MustExec("create table tmp1 (id int primary key auto_increment, u int unique, v int)") - tk.MustExec("insert into tmp1 values" + - "(1, 101, 1001), (3, 113, 1003), (5, 105, 1005), (7, 117, 1007), (9, 109, 1009)," + - "(10, 110, 1010), (12, 112, 1012), (14, 114, 1014), (16, 116, 1016), (18, 118, 1018)", - ) - tk.MustExec("alter table tmp1 cache") - - // For TableReader - // First read will read from original table - tk.MustQuery("select * from tmp1 where id>3 order by id").Check(testkit.Rows( - "5 105 1005", "7 117 1007", "9 109 1009", - "10 110 1010", "12 112 1012", "14 114 1014", "16 116 1016", "18 118 1018", - )) - // Test for join two cache table - tk.MustExec("drop table if exists join_t1, join_t2, join_t3") - tk.MustExec("create table join_t1 (id int)") - tk.MustExec("insert into join_t1 values(1)") - tk.MustExec("alter table join_t1 cache") - tk.MustQuery("select *from join_t1").Check(testkit.Rows("1")) - tk.MustExec("create table join_t2 (id int)") - tk.MustExec("insert into join_t2 values(2)") - tk.MustExec("alter table join_t2 cache") - tk.MustQuery("select *from join_t2").Check(testkit.Rows("2")) - tk.MustExec("create table join_t3 (id int)") - tk.MustExec("insert into join_t3 values(3)") - planUsed := false - for i := 0; i < 10; i++ { - tk.MustQuery("select *from join_t1 join join_t2").Check(testkit.Rows("1 2")) - if lastReadFromCache(tk) { - planUsed = true - break - } - } - require.True(t, planUsed) - - // Test for join a cache table and a normal table - for i := 0; i < 10; i++ { - tk.MustQuery("select * from join_t1 join join_t3").Check(testkit.Rows("1 3")) - if lastReadFromCache(tk) { - planUsed = true - break - } - } - require.True(t, planUsed) - - // Second read will from cache table - for i := 0; i < 100; i++ { - tk.MustQuery("select * from tmp1 where id>4 order by id").Check(testkit.Rows( - "5 105 1005", "7 117 1007", "9 109 1009", - "10 110 1010", "12 112 1012", "14 114 1014", "16 116 1016", "18 118 1018", - )) - if lastReadFromCache(tk) { - planUsed = true - break - } - } - require.True(t, planUsed) - - // For IndexLookUpReader - for i := 0; i < 10; i++ { - tk.MustQuery("select /*+ use_index(tmp1, u) */ * from tmp1 where u>101 order by u").Check(testkit.Rows( - "5 105 1005", "9 109 1009", "10 110 1010", - "12 112 1012", "3 113 1003", "14 114 1014", "16 116 1016", "7 117 1007", "18 118 1018", - )) - if lastReadFromCache(tk) { - planUsed = true - break - } - } - require.True(t, planUsed) - - // For IndexReader - tk.MustQuery("select /*+ use_index(tmp1, u) */ id,u from tmp1 where u>101 order by id").Check(testkit.Rows( - "3 113", "5 105", "7 117", "9 109", "10 110", - "12 112", "14 114", "16 116", "18 118", - )) - tk.MustQuery("show warnings").Check(testkit.Rows()) - - // For IndexMerge, cache table should not use index merge - tk.MustQuery("select /*+ use_index_merge(tmp1, primary, u) */ * from tmp1 where id>5 or u>110 order by u").Check(testkit.Rows( - "9 109 1009", "10 110 1010", - "12 112 1012", "3 113 1003", "14 114 1014", "16 116 1016", "7 117 1007", "18 118 1018", - )) - - tk.MustQuery("show warnings").Check(testkit.Rows()) -} - -func TestCacheCondition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t2") - tk.MustExec("create table t2 (id int primary key, v int)") - tk.MustExec("alter table t2 cache") - - // Explain should not trigger cache. - for i := 0; i < 10; i++ { - tk.MustQuery("explain select * from t2") - time.Sleep(100 * time.Millisecond) - require.False(t, lastReadFromCache(tk)) - } - - // Insert should not trigger cache. - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("insert into t2 values (%d,%d)", i, i)) - time.Sleep(100 * time.Millisecond) - require.False(t, lastReadFromCache(tk)) - } - - // Update should not trigger cache. - for i := 0; i < 10; i++ { - tk.MustExec("update t2 set v = v + 1 where id > 0") - time.Sleep(100 * time.Millisecond) - require.False(t, lastReadFromCache(tk)) - } - - // Contains PointGet Update should not trigger cache. - for i := 0; i < 10; i++ { - tk.MustExec("update t2 set v = v + 1 where id = 2") - time.Sleep(100 * time.Millisecond) - require.False(t, lastReadFromCache(tk)) - } - - // Contains PointGet Delete should not trigger cache. - for i := 0; i < 10; i++ { - tk.MustExec(fmt.Sprintf("delete from t2 where id = %d", i)) - time.Sleep(100 * time.Millisecond) - require.False(t, lastReadFromCache(tk)) - } - - // Normal query should trigger cache. - tk.MustQuery("select * from t2") - cacheUsed := false - for i := 0; i < 100; i++ { - tk.MustQuery("select * from t2") - if lastReadFromCache(tk) { - cacheUsed = true - break - } - time.Sleep(100 * time.Millisecond) - } - require.True(t, cacheUsed) -} - -func TestCacheTableBasicReadAndWrite(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk1 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk.MustExec("drop table if exists write_tmp1") - tk.MustExec("create table write_tmp1 (id int primary key auto_increment, u int unique, v int)") - tk.MustExec("insert into write_tmp1 values" + - "(1, 101, 1001), (3, 113, 1003)", - ) - - tk.MustExec("alter table write_tmp1 cache") - // Read and add read lock - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "3 113 1003")) - // read lock should valid - var i int - for i = 0; i < 10; i++ { - if lastReadFromCache(tk) { - break - } - // Wait for the cache to be loaded. - time.Sleep(50 * time.Millisecond) - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "3 113 1003")) - } - require.True(t, i < 10) - - tk.MustExec("use test") - tk1.MustExec("insert into write_tmp1 values (2, 222, 222)") - // write lock exists - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", - "2 222 222", - "3 113 1003")) - require.False(t, lastReadFromCache(tk)) - - // wait write lock expire and check cache can be used again - for !lastReadFromCache(tk) { - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows( - "1 101 1001", - "2 222 222", - "3 113 1003")) - } - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 222", "3 113 1003")) - tk1.MustExec("update write_tmp1 set v = 3333 where id = 2") - for !lastReadFromCache(tk) { - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 3333", "3 113 1003")) - } - tk.MustQuery("select * from write_tmp1").Check(testkit.Rows("1 101 1001", "2 222 3333", "3 113 1003")) -} - -func TestCacheTableComplexRead(t *testing.T) { - store := testkit.CreateMockStore(t) - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - tk1.MustExec("create table complex_cache (id int primary key auto_increment, u int unique, v int)") - tk1.MustExec("insert into complex_cache values" + "(5, 105, 1005), (7, 117, 1007), (9, 109, 1009)") - tk1.MustExec("alter table complex_cache cache") - tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) - var i int - for i = 0; i < 100; i++ { - time.Sleep(100 * time.Millisecond) - tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) - if lastReadFromCache(tk1) { - break - } - } - require.True(t, i < 10) - - tk1.MustExec("begin") - tk2.MustExec("begin") - tk2.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) - for i = 0; i < 10; i++ { - time.Sleep(100 * time.Millisecond) - tk2.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) - if lastReadFromCache(tk2) { - break - } - } - require.True(t, i < 10) - tk2.MustExec("commit") - - tk1.MustQuery("select * from complex_cache where id > 7").Check(testkit.Rows("9 109 1009")) - require.True(t, lastReadFromCache(tk1)) - tk1.MustExec("commit") -} - -func TestBeginSleepABA(t *testing.T) { - // During the change "cache1 -> no cache -> cache2", - // cache1 and cache2 may be not the same anymore - // A transaction should not only check the cache exists, but also check the cache unchanged. - - store := testkit.CreateMockStore(t) - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - tk1.MustExec("drop table if exists aba") - tk1.MustExec("create table aba (id int, v int)") - tk1.MustExec("insert into aba values (1, 1)") - tk1.MustExec("alter table aba cache") - tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) - cacheUsed := false - for i := 0; i < 100; i++ { - tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) - if lastReadFromCache(tk1) { - cacheUsed = true - break - } - } - require.True(t, cacheUsed) - - // Begin, read from cache. - tk1.MustExec("begin") - tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) - if !lastReadFromCache(tk1) { - // TODO: should read from cache, but it is not stable - // It is a bug, ref https://github.com/pingcap/tidb/issues/36838 - t.Skip("unstable now, skip") - return - } - - // Another session change the data and make the cache unavailable. - tk2.MustExec("update aba set v = 2") - - // And then make the cache available again. - cacheUsed = false - for i := 0; i < 100; i++ { - tk2.MustQuery("select * from aba").Check(testkit.Rows("1 2")) - if lastReadFromCache(tk2) { - cacheUsed = true - break - } - time.Sleep(100 * time.Millisecond) - } - require.True(t, cacheUsed) - - // tk1 should not use the staled cache, because the data is changed. - tk1.MustQuery("select * from aba").Check(testkit.Rows("1 1")) - require.False(t, lastReadFromCache(tk1)) -} - -func TestRenewLease(t *testing.T) { - // Test RenewLeaseForRead - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - se := tk.Session() - tk.MustExec("create table cache_renew_t (id int)") - tk.MustExec("alter table cache_renew_t cache") - tbl, err := se.GetInfoSchema().(infoschema.InfoSchema).TableByName(model.NewCIStr("test"), model.NewCIStr("cache_renew_t")) - require.NoError(t, err) - var i int - tk.MustExec("select * from cache_renew_t") - - tk1 := testkit.NewTestKit(t, store) - remote := tables.NewStateRemote(tk1.Session()) - var leaseBefore uint64 - for i = 0; i < 20; i++ { - time.Sleep(200 * time.Millisecond) - lockType, lease, err := remote.Load(context.Background(), tbl.Meta().ID) - require.NoError(t, err) - if lockType == tables.CachedTableLockRead { - leaseBefore = lease - break - } - } - require.True(t, i < 20) - - for i = 0; i < 20; i++ { - time.Sleep(200 * time.Millisecond) - tk.MustExec("select * from cache_renew_t") - lockType, lease, err := remote.Load(context.Background(), tbl.Meta().ID) - require.NoError(t, err) - require.Equal(t, lockType, tables.CachedTableLockRead) - if leaseBefore != lease { - break - } - } - require.True(t, i < 20) -} - -func TestCacheTableWriteOperatorWaitLockLease(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - se := tk.Session() - - // This line is a hack, if auth user string is "", the statement summary is skipped, - // so it's added to make the later code been covered. - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) - - tk.MustExec("drop table if exists wait_tb1") - tk.MustExec("create table wait_tb1(id int)") - tk.MustExec("alter table wait_tb1 cache") - var i int - for i = 0; i < 10; i++ { - tk.MustQuery("select * from wait_tb1").Check(testkit.Rows()) - if lastReadFromCache(tk) { - break - } - time.Sleep(100 * time.Millisecond) - } - require.True(t, i < 10) - stmtsummary.StmtSummaryByDigestMap.Clear() - tk.MustExec("insert into wait_tb1 values(1)") - require.True(t, se.GetSessionVars().StmtCtx.WaitLockLeaseTime > 0) - - tk.MustQuery("select DIGEST_TEXT from INFORMATION_SCHEMA.STATEMENTS_SUMMARY where MAX_BACKOFF_TIME > 0 or MAX_WAIT_TIME > 0").Check(testkit.Rows("insert into `wait_tb1` values ( ? )")) -} - -func TestTableCacheLeaseVariable(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - // Check default value. - tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("3")) - - // Check a valid value. - tk.MustExec("set @@global.tidb_table_cache_lease = 1;") - tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("1")) - - // Check a invalid value, the valid range is [2, 10] - tk.MustExec("set @@global.tidb_table_cache_lease = 111;") - tk.MustQuery("SHOW WARNINGS").Check(testkit.Rows("Warning 1292 Truncated incorrect tidb_table_cache_lease value: '111'")) - tk.MustQuery("select @@global.tidb_table_cache_lease").Check(testkit.Rows("10")) - - // Change to a non-default value and verify the behaviour. - tk.MustExec("set @@global.tidb_table_cache_lease = 2;") - - tk.MustExec("drop table if exists test_lease_variable;") - tk.MustExec(`create table test_lease_variable(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) - tk.MustExec(`insert into test_lease_variable(c0, c1, c2) values (1, null, 'green');`) - tk.MustExec(`alter table test_lease_variable cache;`) - - cached := false - for i := 0; i < 20; i++ { - tk.MustQuery("select * from test_lease_variable").Check(testkit.Rows("1 green")) - if lastReadFromCache(tk) { - cached = true - break - } - time.Sleep(50 * time.Millisecond) - } - require.True(t, cached) - - start := time.Now() - tk.MustExec("update test_lease_variable set c0 = 2") - duration := time.Since(start) - - // The lease is 2s, check how long the write operation takes. - require.True(t, duration > time.Second) - require.True(t, duration < 3*time.Second) -} - -func TestMetrics(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists test_metrics;") - tk.MustExec(`create table test_metrics(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) - tk.MustExec(`create table nt (c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) - tk.MustExec(`insert into test_metrics(c0, c1, c2) values (1, null, 'green');`) - tk.MustExec(`alter table test_metrics cache;`) - - tk.MustQuery("select * from test_metrics").Check(testkit.Rows("1 green")) - cached := false - for i := 0; i < 20; i++ { - if lastReadFromCache(tk) { - cached = true - break - } - time.Sleep(50 * time.Millisecond) - tk.MustQuery("select * from test_metrics").Check(testkit.Rows("1 green")) - } - require.True(t, cached) - - counter := metrics.ReadFromTableCacheCounter - pb := &dto.Metric{} - - queries := []string{ - // Table scan - "select * from test_metrics", - // Index scan - "select c0 from test_metrics use index(uk) where c0 > 1", - // Index Lookup - "select c1 from test_metrics use index(uk) where c0 = 1", - // Point Get - "select c0 from test_metrics use index(uk) where c0 = 1", - // // Aggregation - "select count(*) from test_metrics", - // Join - "select * from test_metrics as a join test_metrics as b on a.c0 = b.c0 where a.c1 != 'xxx'", - } - counter.Write(pb) - i := pb.GetCounter().GetValue() - - for _, query := range queries { - tk.MustQuery(query) - i++ - counter.Write(pb) - hit := pb.GetCounter().GetValue() - require.Equal(t, i, hit) - } - - // A counter-example that doesn't increase metrics.ReadFromTableCacheCounter. - tk.MustQuery("select * from nt") - counter.Write(pb) - hit := pb.GetCounter().GetValue() - require.Equal(t, i, hit) -} - -func TestRenewLeaseABAFailPoint(t *testing.T) { - store := testkit.CreateMockStore(t) - - tables.TestMockRenewLeaseABA2 = make(chan struct{}) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t_lease;") - tk.MustExec(`create table t_lease(a int, b int);`) - tk.MustExec(`insert into t_lease values (1, 1)`) - tk.MustExec(`alter table t_lease cache`) - - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("use test") - tk2.MustExec("use test") - - // Load the cache data by this query. - var cacheUsed bool - for i := 0; i < 10; i++ { - tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 1")) - if lastReadFromCache(tk) { - cacheUsed = true - break - } - time.Sleep(50 * time.Millisecond) - } - require.True(t, cacheUsed) - - // Renew lease by this query, mock the operation is delayed. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/table/tables/mockRenewLeaseABA1", `return`)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/table/tables/mockRenewLeaseABA2", `return`)) - tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 1")) - - // Make the cache data stale after writing: read lock-> write lock - tk1.MustExec("update t_lease set b = 2 where a = 1") - - // Mock reading from another TiDB instance: write lock -> read lock - is := tk2.Session().GetInfoSchema().(infoschema.InfoSchema) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t_lease")) - require.NoError(t, err) - lease := oracle.GoTimeToTS(time.Now().Add(20 * time.Second)) // A big enough future time - tk2.MustExec("update mysql.table_cache_meta set lock_type = 'READ', lease = ? where tid = ?", lease, tbl.Meta().ID) - - // Then the stagnant renew lease operation finally arrive. - tables.TestMockRenewLeaseABA2 <- struct{}{} - - <-tables.TestMockRenewLeaseABA2 - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/table/tables/mockRenewLeaseABA1")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/table/tables/mockRenewLeaseABA2")) - - // The renew lease operation should not success, - // And the session should not read from a staled cache data. - tk.MustQuery("select * from t_lease").Check(testkit.Rows("1 2")) - require.False(t, lastReadFromCache(tk)) -} diff --git a/table/tables/index.go b/table/tables/index.go deleted file mode 100644 index c6994f01ed824..0000000000000 --- a/table/tables/index.go +++ /dev/null @@ -1,742 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package tables - -import ( - "bytes" - "context" - "sync" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/tracing" -) - -// index is the data structure for index data in the KV store. -type index struct { - idxInfo *model.IndexInfo - tblInfo *model.TableInfo - prefix kv.Key - phyTblID int64 - // initNeedRestoreData is used to initialize `needRestoredData` in `index.Create()`. - // This routine cannot be done in `NewIndex()` because `needRestoreData` relies on `NewCollationEnabled()` and - // the collation global variable is initialized *after* `NewIndex()`. - initNeedRestoreData sync.Once - needRestoredData bool -} - -// NeedRestoredData checks whether the index columns needs restored data. -func NeedRestoredData(idxCols []*model.IndexColumn, colInfos []*model.ColumnInfo) bool { - for _, idxCol := range idxCols { - col := colInfos[idxCol.Offset] - if types.NeedRestoredData(&col.FieldType) { - return true - } - } - return false -} - -// NewIndex builds a new Index object. -func NewIndex(physicalID int64, tblInfo *model.TableInfo, indexInfo *model.IndexInfo) table.Index { - // The prefix can't encode from tblInfo.ID, because table partition may change the id to partition id. - var prefix kv.Key - if indexInfo.Global { - // In glabal index of partition table, prefix start with tblInfo.ID. - prefix = tablecodec.EncodeTableIndexPrefix(tblInfo.ID, indexInfo.ID) - } else { - // Otherwise, start with physicalID. - prefix = tablecodec.EncodeTableIndexPrefix(physicalID, indexInfo.ID) - } - index := &index{ - idxInfo: indexInfo, - tblInfo: tblInfo, - prefix: prefix, - phyTblID: physicalID, - } - return index -} - -// Meta returns index info. -func (c *index) Meta() *model.IndexInfo { - return c.idxInfo -} - -// TableMeta returns table info. -func (c *index) TableMeta() *model.TableInfo { - return c.tblInfo -} - -// GenIndexKey generates storage key for index values. Returned distinct indicates whether the -// indexed values should be distinct in storage (i.e. whether handle is encoded in the key). -func (c *index) GenIndexKey(sc *stmtctx.StatementContext, indexedValues []types.Datum, h kv.Handle, buf []byte) (key []byte, distinct bool, err error) { - idxTblID := c.phyTblID - if c.idxInfo.Global { - idxTblID = c.tblInfo.ID - } - return tablecodec.GenIndexKey(sc, c.tblInfo, c.idxInfo, idxTblID, indexedValues, h, buf) -} - -// GenIndexValue generates the index value. -func (c *index) GenIndexValue(sc *stmtctx.StatementContext, distinct bool, indexedValues []types.Datum, h kv.Handle, restoredData []types.Datum) ([]byte, error) { - c.initNeedRestoreData.Do(func() { - c.needRestoredData = NeedRestoredData(c.idxInfo.Columns, c.tblInfo.Columns) - }) - return tablecodec.GenIndexValuePortal(sc, c.tblInfo, c.idxInfo, c.needRestoredData, distinct, false, indexedValues, h, c.phyTblID, restoredData) -} - -// getIndexedValue will produce the result like: -// 1. If not multi-valued index, return directly. -// 2. (i1, [m1,m2], i2, ...) ==> [(i1, m1, i2, ...), (i1, m2, i2, ...)] -// 3. (i1, null, i2, ...) ==> [(i1, null, i2, ...)] -// 4. (i1, [], i2, ...) ==> nothing. -func (c *index) getIndexedValue(indexedValues []types.Datum) [][]types.Datum { - if !c.idxInfo.MVIndex { - return [][]types.Datum{indexedValues} - } - - vals := make([][]types.Datum, 0, 16) - jsonIdx := 0 - jsonIsNull := false - existsVals := make(map[string]struct{}) - var buf []byte - for !jsonIsNull { - val := make([]types.Datum, 0, len(indexedValues)) - for i, v := range indexedValues { - if !c.tblInfo.Columns[c.idxInfo.Columns[i].Offset].FieldType.IsArray() { - val = append(val, v) - } else { - // if the datum type is not JSON, it must come from cleanup index. - if v.IsNull() || v.Kind() != types.KindMysqlJSON { - val = append(val, v) - jsonIsNull = true - continue - } - elemCount := v.GetMysqlJSON().GetElemCount() - for { - // JSON cannot be indexed, if the value is JSON type, it must be multi-valued index. - if jsonIdx >= elemCount { - goto out - } - binaryJSON := v.GetMysqlJSON().ArrayGetElem(jsonIdx) - jsonIdx++ - buf = buf[:0] - key := string(binaryJSON.HashValue(buf)) - if _, exists := existsVals[key]; exists { - continue - } - existsVals[key] = struct{}{} - val = append(val, types.NewDatum(binaryJSON.GetValue())) - break - } - } - } - vals = append(vals, val) - } -out: - return vals -} - -// Create creates a new entry in the kvIndex data. -// If the index is unique and there is an existing entry with the same key, -// Create will return the existing entry's handle as the first return value, ErrKeyExists as the second return value. -func (c *index) Create(sctx sessionctx.Context, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum, opts ...table.CreateIdxOptFunc) (kv.Handle, error) { - if c.Meta().Unique { - txn.CacheTableInfo(c.phyTblID, c.tblInfo) - } - var opt table.CreateIdxOpt - for _, fn := range opts { - fn(&opt) - } - - indexedValues := c.getIndexedValue(indexedValue) - ctx := opt.Ctx - if ctx != nil { - var r tracing.Region - r, ctx = tracing.StartRegionEx(ctx, "index.Create") - defer r.End() - } else { - ctx = context.TODO() - } - vars := sctx.GetSessionVars() - writeBufs := vars.GetWriteStmtBufs() - skipCheck := vars.StmtCtx.BatchCheck - for _, value := range indexedValues { - key, distinct, err := c.GenIndexKey(vars.StmtCtx, value, h, writeBufs.IndexKeyBuf) - if err != nil { - return nil, err - } - - var ( - tempKey []byte - keyVer byte - keyIsTempIdxKey bool - ) - if !opt.FromBackFill { - key, tempKey, keyVer = GenTempIdxKeyByState(c.idxInfo, key) - if keyVer == TempIndexKeyTypeBackfill || keyVer == TempIndexKeyTypeDelete { - key, tempKey = tempKey, nil - keyIsTempIdxKey = true - } - } - - if opt.Untouched { - txn, err1 := sctx.Txn(true) - if err1 != nil { - return nil, err1 - } - // If the index kv was untouched(unchanged), and the key/value already exists in mem-buffer, - // should not overwrite the key with un-commit flag. - // So if the key exists, just do nothing and return. - v, err := txn.GetMemBuffer().Get(ctx, key) - if err == nil { - if len(v) != 0 { - continue - } - // The key is marked as deleted in the memory buffer, as the existence check is done lazily - // for optimistic transactions by default. The "untouched" key could still exist in the store, - // it's needed to commit this key to do the existence check so unset the untouched flag. - if !txn.IsPessimistic() { - keyFlags, err := txn.GetMemBuffer().GetFlags(key) - if err != nil { - return nil, err - } - if keyFlags.HasPresumeKeyNotExists() { - opt.Untouched = false - } - } - } - } - - // save the key buffer to reuse. - writeBufs.IndexKeyBuf = key - c.initNeedRestoreData.Do(func() { - c.needRestoredData = NeedRestoredData(c.idxInfo.Columns, c.tblInfo.Columns) - }) - idxVal, err := tablecodec.GenIndexValuePortal(sctx.GetSessionVars().StmtCtx, c.tblInfo, c.idxInfo, c.needRestoredData, distinct, opt.Untouched, value, h, c.phyTblID, handleRestoreData) - if err != nil { - return nil, err - } - - opt.IgnoreAssertion = opt.IgnoreAssertion || c.idxInfo.State != model.StatePublic - - if !distinct || skipCheck || opt.Untouched { - val := idxVal - if opt.Untouched && (keyIsTempIdxKey || len(tempKey) > 0) { - // Untouched key-values never occur in the storage and the temp index is not public. - // It is unnecessary to write the untouched temp index key-values. - continue - } - if keyIsTempIdxKey { - tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: distinct} - val = tempVal.Encode(nil) - } - err = txn.GetMemBuffer().Set(key, val) - if err != nil { - return nil, err - } - if len(tempKey) > 0 { - tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: distinct} - val = tempVal.Encode(nil) - err = txn.GetMemBuffer().Set(tempKey, val) - if err != nil { - return nil, err - } - } - if !opt.IgnoreAssertion && (!opt.Untouched) { - if sctx.GetSessionVars().LazyCheckKeyNotExists() && !txn.IsPessimistic() { - err = txn.SetAssertion(key, kv.SetAssertUnknown) - } else { - err = txn.SetAssertion(key, kv.SetAssertNotExist) - } - } - if err != nil { - return nil, err - } - continue - } - - var value []byte - if c.tblInfo.TempTableType != model.TempTableNone { - // Always check key for temporary table because it does not write to TiKV - value, err = txn.Get(ctx, key) - } else if sctx.GetSessionVars().LazyCheckKeyNotExists() && !keyIsTempIdxKey { - // For temp index keys, we can't get the temp value from memory buffer, even if the lazy check is enabled. - // Otherwise, it may cause the temp index value to be overwritten, leading to data inconsistency. - value, err = txn.GetMemBuffer().Get(ctx, key) - } else { - value, err = txn.Get(ctx, key) - } - if err != nil && !kv.IsErrNotFound(err) { - return nil, err - } - var tempIdxVal tablecodec.TempIndexValue - if len(value) > 0 && keyIsTempIdxKey { - tempIdxVal, err = tablecodec.DecodeTempIndexValue(value) - if err != nil { - return nil, err - } - } - // The index key value is not found or deleted. - if err != nil || len(value) == 0 || (!tempIdxVal.IsEmpty() && tempIdxVal.Current().Delete) { - val := idxVal - lazyCheck := sctx.GetSessionVars().LazyCheckKeyNotExists() && err != nil - if keyIsTempIdxKey { - tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: true} - val = tempVal.Encode(value) - } - needPresumeNotExists, err := needPresumeKeyNotExistsFlag(ctx, txn, key, tempKey, h, - keyIsTempIdxKey, c.tblInfo.IsCommonHandle, c.tblInfo.ID) - if err != nil { - return nil, err - } - if lazyCheck { - var flags []kv.FlagsOp - if needPresumeNotExists { - flags = []kv.FlagsOp{kv.SetPresumeKeyNotExists} - } - if !vars.ConstraintCheckInPlacePessimistic && vars.TxnCtx.IsPessimistic && vars.InTxn() && - !vars.InRestrictedSQL && vars.ConnectionID > 0 { - flags = append(flags, kv.SetNeedConstraintCheckInPrewrite) - } - err = txn.GetMemBuffer().SetWithFlags(key, val, flags...) - } else { - err = txn.GetMemBuffer().Set(key, val) - } - if err != nil { - return nil, err - } - if len(tempKey) > 0 { - tempVal := tablecodec.TempIndexValueElem{Value: idxVal, KeyVer: keyVer, Distinct: true} - val = tempVal.Encode(value) - if lazyCheck && needPresumeNotExists { - err = txn.GetMemBuffer().SetWithFlags(tempKey, val, kv.SetPresumeKeyNotExists) - } else { - err = txn.GetMemBuffer().Set(tempKey, val) - } - if err != nil { - return nil, err - } - } - if opt.IgnoreAssertion { - continue - } - if lazyCheck && !txn.IsPessimistic() { - err = txn.SetAssertion(key, kv.SetAssertUnknown) - } else { - err = txn.SetAssertion(key, kv.SetAssertNotExist) - } - if err != nil { - return nil, err - } - continue - } - if c.idxInfo.Global && len(value) != 0 && !bytes.Equal(value, idxVal) { - val := idxVal - err = txn.GetMemBuffer().Set(key, val) - if err != nil { - return nil, err - } - continue - } - - if keyIsTempIdxKey && !tempIdxVal.IsEmpty() { - value = tempIdxVal.Current().Value - } - handle, err := tablecodec.DecodeHandleInUniqueIndexValue(value, c.tblInfo.IsCommonHandle) - if err != nil { - return nil, err - } - return handle, kv.ErrKeyExists - } - return nil, nil -} - -func needPresumeKeyNotExistsFlag(ctx context.Context, txn kv.Transaction, key, tempKey kv.Key, - h kv.Handle, keyIsTempIdxKey bool, isCommon bool, tblID int64) (needFlag bool, err error) { - var uniqueTempKey kv.Key - if keyIsTempIdxKey { - uniqueTempKey = key - } else if len(tempKey) > 0 { - uniqueTempKey = tempKey - } else { - return true, nil - } - foundKey, dupHandle, err := FetchDuplicatedHandle(ctx, uniqueTempKey, true, txn, tblID, isCommon) - if err != nil { - return false, err - } - if foundKey && dupHandle != nil && !dupHandle.Equal(h) { - return false, kv.ErrKeyExists - } - return false, nil -} - -// Delete removes the entry for handle h and indexedValues from KV index. -func (c *index) Delete(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle) error { - indexedValues := c.getIndexedValue(indexedValue) - for _, value := range indexedValues { - key, distinct, err := c.GenIndexKey(sc, value, h, nil) - if err != nil { - return err - } - - key, tempKey, tempKeyVer := GenTempIdxKeyByState(c.idxInfo, key) - var originTempVal []byte - if len(tempKey) > 0 && c.idxInfo.Unique { - // Get the origin value of the unique temporary index key. - // Append the new delete operations to the end of the origin value. - originTempVal, err = getKeyInTxn(context.TODO(), txn, tempKey) - if err != nil { - return err - } - } - tempValElem := tablecodec.TempIndexValueElem{Handle: h, KeyVer: tempKeyVer, Delete: true, Distinct: distinct} - - // If index is global, decode the pid from value (if exists) and compare with c.physicalID. - // Only when pid in value equals to c.physicalID, the key can be deleted. - if c.idxInfo.Global { - if val, err := txn.GetMemBuffer().Get(context.Background(), key); err == nil { - segs := tablecodec.SplitIndexValue(val) - if len(segs.PartitionID) != 0 { - _, pid, err := codec.DecodeInt(segs.PartitionID) - if err != nil { - return err - } - if pid != c.phyTblID { - continue - } - } - } - } - - if distinct { - if len(key) > 0 { - err = txn.GetMemBuffer().DeleteWithFlags(key, kv.SetNeedLocked) - if err != nil { - return err - } - } - if len(tempKey) > 0 { - // Append to the end of the origin value for distinct value. - tempVal := tempValElem.Encode(originTempVal) - err = txn.GetMemBuffer().Set(tempKey, tempVal) - if err != nil { - return err - } - } - } else { - if len(key) > 0 { - err = txn.GetMemBuffer().Delete(key) - if err != nil { - return err - } - } - if len(tempKey) > 0 { - tempVal := tempValElem.Encode(nil) - err = txn.GetMemBuffer().Set(tempKey, tempVal) - if err != nil { - return err - } - } - } - if c.idxInfo.State == model.StatePublic { - // If the index is in public state, delete this index means it must exists. - err = txn.SetAssertion(key, kv.SetAssertExist) - } - if err != nil { - return err - } - } - return nil -} - -func (c *index) GenIndexKVIter(sc *stmtctx.StatementContext, indexedValue []types.Datum, h kv.Handle, handleRestoreData []types.Datum) table.IndexIter { - indexedValues := c.getIndexedValue(indexedValue) - return &indexGenerator{ - c: c, - sctx: sc, - indexedVals: indexedValues, - h: h, - handleRestoreData: handleRestoreData, - i: 0, - } -} - -type indexGenerator struct { - c *index - sctx *stmtctx.StatementContext - indexedVals [][]types.Datum - h kv.Handle - handleRestoreData []types.Datum - - i int -} - -func (s *indexGenerator) Next(kb []byte) ([]byte, []byte, bool, error) { - val := s.indexedVals[s.i] - key, distinct, err := s.c.GenIndexKey(s.sctx, val, s.h, kb) - if err != nil { - return nil, nil, false, err - } - idxVal, err := s.c.GenIndexValue(s.sctx, distinct, val, s.h, s.handleRestoreData) - if err != nil { - return nil, nil, false, err - } - s.i++ - return key, idxVal, distinct, err -} - -func (s *indexGenerator) Valid() bool { - return s.i < len(s.indexedVals) -} - -const ( - // TempIndexKeyTypeNone means the key is not a temporary index key. - TempIndexKeyTypeNone byte = 0 - // TempIndexKeyTypeDelete indicates this value is written in the delete-only stage. - TempIndexKeyTypeDelete byte = 'd' - // TempIndexKeyTypeBackfill indicates this value is written in the backfill stage. - TempIndexKeyTypeBackfill byte = 'b' - // TempIndexKeyTypeMerge indicates this value is written in the merge stage. - TempIndexKeyTypeMerge byte = 'm' -) - -// GenTempIdxKeyByState is used to get the key version and the temporary key. -// The tempKeyVer means the temp index key/value version. -func GenTempIdxKeyByState(indexInfo *model.IndexInfo, indexKey kv.Key) (key, tempKey kv.Key, tempKeyVer byte) { - if indexInfo.State != model.StatePublic { - switch indexInfo.BackfillState { - case model.BackfillStateInapplicable: - return indexKey, nil, TempIndexKeyTypeNone - case model.BackfillStateRunning: - // Write to the temporary index. - tablecodec.IndexKey2TempIndexKey(indexKey) - if indexInfo.State == model.StateDeleteOnly { - return nil, indexKey, TempIndexKeyTypeDelete - } - return nil, indexKey, TempIndexKeyTypeBackfill - case model.BackfillStateReadyToMerge, model.BackfillStateMerging: - // Double write - tmp := make([]byte, len(indexKey)) - copy(tmp, indexKey) - tablecodec.IndexKey2TempIndexKey(tmp) - return indexKey, tmp, TempIndexKeyTypeMerge - } - } - return indexKey, nil, TempIndexKeyTypeNone -} - -func (c *index) Exist(sc *stmtctx.StatementContext, txn kv.Transaction, indexedValue []types.Datum, h kv.Handle) (bool, kv.Handle, error) { - indexedValues := c.getIndexedValue(indexedValue) - for _, val := range indexedValues { - key, distinct, err := c.GenIndexKey(sc, val, h, nil) - if err != nil { - return false, nil, err - } - // If index current is in creating status and using ingest mode, we need first - // check key exist status in temp index. - key, tempKey, _ := GenTempIdxKeyByState(c.idxInfo, key) - if len(tempKey) > 0 { - key = tempKey - } - foundKey, dupHandle, err := FetchDuplicatedHandle(context.TODO(), key, distinct, txn, c.tblInfo.ID, c.tblInfo.IsCommonHandle) - if err != nil || !foundKey { - return false, nil, err - } - if dupHandle != nil && !dupHandle.Equal(h) { - return false, nil, err - } - continue - } - return true, h, nil -} - -// FetchDuplicatedHandle is used to find the duplicated row's handle for a given unique index key. -func FetchDuplicatedHandle(ctx context.Context, key kv.Key, distinct bool, - txn kv.Transaction, tableID int64, isCommon bool) (foundKey bool, dupHandle kv.Handle, err error) { - if tablecodec.IsTempIndexKey(key) { - return fetchDuplicatedHandleForTempIndexKey(ctx, key, distinct, txn, tableID, isCommon) - } - // The index key is not from temp index. - val, err := getKeyInTxn(ctx, txn, key) - if err != nil || len(val) == 0 { - return false, nil, err - } - if distinct { - h, err := tablecodec.DecodeHandleInUniqueIndexValue(val, isCommon) - return true, h, err - } - return true, nil, nil -} - -func fetchDuplicatedHandleForTempIndexKey(ctx context.Context, tempKey kv.Key, distinct bool, - txn kv.Transaction, tableID int64, isCommon bool) (foundKey bool, dupHandle kv.Handle, err error) { - tempRawVal, err := getKeyInTxn(ctx, txn, tempKey) - if err != nil { - return false, nil, err - } - if tempRawVal == nil { - originKey := tempKey.Clone() - tablecodec.TempIndexKey2IndexKey(originKey) - originVal, err := getKeyInTxn(ctx, txn, originKey) - if err != nil || originVal == nil { - return false, nil, err - } - if distinct { - originHandle, err := tablecodec.DecodeHandleInUniqueIndexValue(originVal, isCommon) - if err != nil { - return false, nil, err - } - return true, originHandle, err - } - return false, nil, nil - } - tempVal, err := tablecodec.DecodeTempIndexValue(tempRawVal) - if err != nil { - return false, nil, err - } - curElem := tempVal.Current() - if curElem.Delete { - originKey := tempKey.Clone() - tablecodec.TempIndexKey2IndexKey(originKey) - originVal, err := getKeyInTxn(ctx, txn, originKey) - if err != nil || originVal == nil { - return false, nil, err - } - if distinct { - originHandle, err := tablecodec.DecodeHandleInUniqueIndexValue(originVal, isCommon) - if err != nil { - return false, nil, err - } - if originHandle.Equal(curElem.Handle) { - // The key has been deleted. This is not a duplicated key. - return false, nil, nil - } - // The inequality means multiple modifications happened in the same key. - // We use the handle in origin index value to check if the row exists. - recPrefix := tablecodec.GenTableRecordPrefix(tableID) - rowKey := tablecodec.EncodeRecordKey(recPrefix, originHandle) - rowVal, err := getKeyInTxn(ctx, txn, rowKey) - if err != nil || rowVal == nil { - return false, nil, err - } - // The row exists. This is the duplicated key. - return true, originHandle, nil - } - return false, nil, nil - } - // The value in temp index is not the delete marker. - if distinct { - h, err := tablecodec.DecodeHandleInUniqueIndexValue(curElem.Value, isCommon) - return true, h, err - } - return true, nil, nil -} - -// getKeyInTxn gets the value of the key in the transaction, and ignore the ErrNotExist error. -func getKeyInTxn(ctx context.Context, txn kv.Transaction, key kv.Key) ([]byte, error) { - val, err := txn.Get(ctx, key) - if err != nil { - if kv.IsErrNotFound(err) { - return nil, nil - } - return nil, err - } - return val, nil -} - -func (c *index) FetchValues(r []types.Datum, vals []types.Datum) ([]types.Datum, error) { - needLength := len(c.idxInfo.Columns) - if vals == nil || cap(vals) < needLength { - vals = make([]types.Datum, needLength) - } - vals = vals[:needLength] - for i, ic := range c.idxInfo.Columns { - if ic.Offset < 0 || ic.Offset >= len(r) { - return nil, table.ErrIndexOutBound.GenWithStackByArgs(ic.Name, ic.Offset, r) - } - vals[i] = r[ic.Offset] - } - return vals, nil -} - -// FindChangingCol finds the changing column in idxInfo. -func FindChangingCol(cols []*table.Column, idxInfo *model.IndexInfo) *table.Column { - for _, ic := range idxInfo.Columns { - if col := cols[ic.Offset]; col.ChangeStateInfo != nil { - return col - } - } - return nil -} - -// IsIndexWritable check whether the index is writable. -func IsIndexWritable(idx table.Index) bool { - s := idx.Meta().State - if s != model.StateDeleteOnly && s != model.StateDeleteReorganization { - return true - } - return false -} - -// BuildRowcodecColInfoForIndexColumns builds []rowcodec.ColInfo for the given index. -// The result can be used for decoding index key-values. -func BuildRowcodecColInfoForIndexColumns(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) []rowcodec.ColInfo { - colInfo := make([]rowcodec.ColInfo, 0, len(idxInfo.Columns)) - for _, idxCol := range idxInfo.Columns { - col := tblInfo.Columns[idxCol.Offset] - colInfo = append(colInfo, rowcodec.ColInfo{ - ID: col.ID, - IsPKHandle: tblInfo.PKIsHandle && mysql.HasPriKeyFlag(col.GetFlag()), - Ft: rowcodec.FieldTypeFromModelColumn(col), - }) - } - return colInfo -} - -// BuildFieldTypesForIndexColumns builds the index columns field types. -func BuildFieldTypesForIndexColumns(idxInfo *model.IndexInfo, tblInfo *model.TableInfo) []*types.FieldType { - tps := make([]*types.FieldType, 0, len(idxInfo.Columns)) - for _, idxCol := range idxInfo.Columns { - col := tblInfo.Columns[idxCol.Offset] - tps = append(tps, rowcodec.FieldTypeFromModelColumn(col)) - } - return tps -} - -// TryAppendCommonHandleRowcodecColInfos tries to append common handle columns to `colInfo`. -func TryAppendCommonHandleRowcodecColInfos(colInfo []rowcodec.ColInfo, tblInfo *model.TableInfo) []rowcodec.ColInfo { - if !tblInfo.IsCommonHandle || tblInfo.CommonHandleVersion == 0 { - return colInfo - } - if pkIdx := FindPrimaryIndex(tblInfo); pkIdx != nil { - for _, idxCol := range pkIdx.Columns { - col := tblInfo.Columns[idxCol.Offset] - colInfo = append(colInfo, rowcodec.ColInfo{ - ID: col.ID, - Ft: rowcodec.FieldTypeFromModelColumn(col), - }) - } - } - return colInfo -} diff --git a/table/tables/index_test.go b/table/tables/index_test.go deleted file mode 100644 index a9d8ba9afeb59..0000000000000 --- a/table/tables/index_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tables_test - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/stretchr/testify/require" -) - -func TestMultiColumnCommonHandle(t *testing.T) { - tblInfo := buildTableInfo(t, "create table t (a int, b int, u varchar(64) unique, nu varchar(64), primary key (a, b), index nu (nu))") - var idxUnique, idxNonUnique table.Index - for _, idxInfo := range tblInfo.Indices { - idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) - if idxInfo.Name.L == "u" { - idxUnique = idx - } else if idxInfo.Name.L == "nu" { - idxNonUnique = idx - } - } - var a, b *model.ColumnInfo - for _, col := range tblInfo.Columns { - if col.Name.String() == "a" { - a = col - } else if col.Name.String() == "b" { - b = col - } - } - require.NotNil(t, a) - require.NotNil(t, b) - - store := testkit.CreateMockStore(t) - txn, err := store.Begin() - require.NoError(t, err) - mockCtx := mock.NewContext() - sc := mockCtx.GetSessionVars().StmtCtx - // create index for "insert t values (3, 2, "abc", "abc") - idxColVals := types.MakeDatums("abc") - handleColVals := types.MakeDatums(3, 2) - encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) - require.NoError(t, err) - commonHandle, err := kv.NewCommonHandle(encodedHandle) - require.NoError(t, err) - _ = idxNonUnique - for _, idx := range []table.Index{idxUnique, idxNonUnique} { - key, _, err := idx.GenIndexKey(sc, idxColVals, commonHandle, nil) - require.NoError(t, err) - _, err = idx.Create(mockCtx, txn, idxColVals, commonHandle, nil) - require.NoError(t, err) - val, err := txn.Get(context.Background(), key) - require.NoError(t, err) - colInfo := tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo) - colInfo = append(colInfo, rowcodec.ColInfo{ - ID: a.ID, - IsPKHandle: false, - Ft: rowcodec.FieldTypeFromModelColumn(a), - }) - colInfo = append(colInfo, rowcodec.ColInfo{ - ID: b.ID, - IsPKHandle: false, - Ft: rowcodec.FieldTypeFromModelColumn(b), - }) - colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, colInfo) - require.NoError(t, err) - require.Len(t, colVals, 3) - _, d, err := codec.DecodeOne(colVals[0]) - require.NoError(t, err) - require.Equal(t, "abc", d.GetString()) - _, d, err = codec.DecodeOne(colVals[1]) - require.NoError(t, err) - require.Equal(t, int64(3), d.GetInt64()) - _, d, err = codec.DecodeOne(colVals[2]) - require.NoError(t, err) - require.Equal(t, int64(2), d.GetInt64()) - handle, err := tablecodec.DecodeIndexHandle(key, val, 1) - require.NoError(t, err) - require.False(t, handle.IsInt()) - require.Equal(t, commonHandle.Encoded(), handle.Encoded()) - } -} - -func TestSingleColumnCommonHandle(t *testing.T) { - tblInfo := buildTableInfo(t, "create table t (a varchar(255) primary key, u int unique, nu int, index nu (nu))") - var idxUnique, idxNonUnique table.Index - for _, idxInfo := range tblInfo.Indices { - idx := tables.NewIndex(tblInfo.ID, tblInfo, idxInfo) - if idxInfo.Name.L == "u" { - idxUnique = idx - } else if idxInfo.Name.L == "nu" { - idxNonUnique = idx - } - } - store := testkit.CreateMockStore(t) - txn, err := store.Begin() - require.NoError(t, err) - - mockCtx := mock.NewContext() - sc := mockCtx.GetSessionVars().StmtCtx - // create index for "insert t values ('abc', 1, 1)" - idxColVals := types.MakeDatums(1) - handleColVals := types.MakeDatums("abc") - encodedHandle, err := codec.EncodeKey(sc, nil, handleColVals...) - require.NoError(t, err) - commonHandle, err := kv.NewCommonHandle(encodedHandle) - require.NoError(t, err) - - for _, idx := range []table.Index{idxUnique, idxNonUnique} { - key, _, err := idx.GenIndexKey(sc, idxColVals, commonHandle, nil) - require.NoError(t, err) - _, err = idx.Create(mockCtx, txn, idxColVals, commonHandle, nil) - require.NoError(t, err) - val, err := txn.Get(context.Background(), key) - require.NoError(t, err) - colVals, err := tablecodec.DecodeIndexKV(key, val, 1, tablecodec.HandleDefault, - tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) - require.NoError(t, err) - require.Len(t, colVals, 2) - _, d, err := codec.DecodeOne(colVals[0]) - require.NoError(t, err) - require.Equal(t, int64(1), d.GetInt64()) - _, d, err = codec.DecodeOne(colVals[1]) - require.NoError(t, err) - require.Equal(t, "abc", d.GetString()) - handle, err := tablecodec.DecodeIndexHandle(key, val, 1) - require.NoError(t, err) - require.False(t, handle.IsInt()) - require.Equal(t, commonHandle.Encoded(), handle.Encoded()) - - unTouchedVal := append([]byte{1}, val[1:]...) - unTouchedVal = append(unTouchedVal, kv.UnCommitIndexKVFlag) - _, err = tablecodec.DecodeIndexKV(key, unTouchedVal, 1, tablecodec.HandleDefault, - tables.BuildRowcodecColInfoForIndexColumns(idx.Meta(), tblInfo)) - require.NoError(t, err) - } -} - -func buildTableInfo(t *testing.T, sql string) *model.TableInfo { - stmt, err := parser.New().ParseOneStmt(sql, "", "") - require.NoError(t, err) - tblInfo, err := ddl.BuildTableInfoFromAST(stmt.(*ast.CreateTableStmt)) - require.NoError(t, err) - return tblInfo -} diff --git a/table/tables/main_test.go b/table/tables/main_test.go deleted file mode 100644 index c4051b28de223..0000000000000 --- a/table/tables/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tables_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/table/tables/partition.go b/table/tables/partition.go deleted file mode 100644 index b0f1b8ebddca7..0000000000000 --- a/table/tables/partition.go +++ /dev/null @@ -1,1902 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package tables - -import ( - "bytes" - "context" - stderr "errors" - "fmt" - "hash/crc32" - "sort" - "strconv" - "strings" - "sync" - - "github.com/google/btree" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/ranger" - "github.com/pingcap/tidb/util/stringutil" - "go.uber.org/zap" -) - -const ( - btreeDegree = 32 -) - -// Both partition and partitionedTable implement the table.Table interface. -var _ table.PhysicalTable = &partition{} -var _ table.Table = &partitionedTable{} - -// partitionedTable implements the table.PartitionedTable interface. -var _ table.PartitionedTable = &partitionedTable{} - -// partition is a feature from MySQL: -// See https://dev.mysql.com/doc/refman/8.0/en/partitioning.html -// A partition table may contain many partitions, each partition has a unique partition -// id. The underlying representation of a partition and a normal table (a table with no -// partitions) is basically the same. -// partition also implements the table.Table interface. -type partition struct { - TableCommon - table *partitionedTable -} - -// GetPhysicalID implements table.Table GetPhysicalID interface. -func (p *partition) GetPhysicalID() int64 { - return p.physicalTableID -} - -// GetPartitionedTable implements table.Table GetPartitionedTable interface. -func (p *partition) GetPartitionedTable() table.PartitionedTable { - return p.table -} - -// GetPartitionedTable implements table.Table GetPartitionedTable interface. -func (t *partitionedTable) GetPartitionedTable() table.PartitionedTable { - return t -} - -// partitionedTable implements the table.PartitionedTable interface. -// partitionedTable is a table, it contains many Partitions. -type partitionedTable struct { - TableCommon - partitionExpr *PartitionExpr - partitions map[int64]*partition - evalBufferTypes []*types.FieldType - evalBufferPool sync.Pool - - // Only used during Reorganize partition - // reorganizePartitions is the currently used partitions that are reorganized - reorganizePartitions map[int64]interface{} - // doubleWritePartitions are the partitions not visible, but we should double write to - doubleWritePartitions map[int64]interface{} - reorgPartitionExpr *PartitionExpr -} - -// TODO: Check which data structures that can be shared between all partitions and which -// needs to be copies -func newPartitionedTable(tbl *TableCommon, tblInfo *model.TableInfo) (table.PartitionedTable, error) { - pi := tblInfo.GetPartitionInfo() - if pi == nil || len(pi.Definitions) == 0 { - return nil, table.ErrUnknownPartition - } - ret := &partitionedTable{TableCommon: *tbl} - partitionExpr, err := newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.Definitions) - if err != nil { - return nil, errors.Trace(err) - } - ret.partitionExpr = partitionExpr - initEvalBufferType(ret) - ret.evalBufferPool = sync.Pool{ - New: func() interface{} { - return initEvalBuffer(ret) - }, - } - if err := initTableIndices(&ret.TableCommon); err != nil { - return nil, errors.Trace(err) - } - partitions := make(map[int64]*partition, len(pi.Definitions)) - for _, p := range pi.Definitions { - var t partition - err := initTableCommonWithIndices(&t.TableCommon, tblInfo, p.ID, tbl.Columns, tbl.allocs, tbl.Constraints) - if err != nil { - return nil, errors.Trace(err) - } - t.table = ret - partitions[p.ID] = &t - } - ret.partitions = partitions - // In StateWriteReorganization we are using the 'old' partition definitions - // and if any new change happens in DroppingDefinitions, it needs to be done - // also in AddingDefinitions (with new evaluation of the new expression) - // In StateDeleteReorganization we are using the 'new' partition definitions - // and if any new change happens in AddingDefinitions, it needs to be done - // also in DroppingDefinitions (since session running on schema version -1) - // should also see the changes - if pi.DDLState == model.StateDeleteReorganization { - origIdx := setIndexesState(ret, pi.DDLState) - defer unsetIndexesState(ret, origIdx) - // TODO: Explicitly explain the different DDL/New fields! - if pi.NewTableID != 0 { - ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.DDLType, pi.DDLExpr, pi.DDLColumns, pi.DroppingDefinitions) - } else { - ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.DroppingDefinitions) - } - if err != nil { - return nil, errors.Trace(err) - } - ret.reorganizePartitions = make(map[int64]interface{}, len(pi.AddingDefinitions)) - for _, def := range pi.AddingDefinitions { - ret.reorganizePartitions[def.ID] = nil - } - ret.doubleWritePartitions = make(map[int64]interface{}, len(pi.DroppingDefinitions)) - for _, def := range pi.DroppingDefinitions { - p, err := initPartition(ret, def) - if err != nil { - return nil, err - } - partitions[def.ID] = p - ret.doubleWritePartitions[def.ID] = nil - } - } else { - if len(pi.AddingDefinitions) > 0 { - origIdx := setIndexesState(ret, pi.DDLState) - defer unsetIndexesState(ret, origIdx) - if pi.NewTableID != 0 { - // REMOVE PARTITIONING or PARTITION BY - ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.DDLType, pi.DDLExpr, pi.DDLColumns, pi.AddingDefinitions) - } else { - // REORGANIZE PARTITION - ret.reorgPartitionExpr, err = newPartitionExpr(tblInfo, pi.Type, pi.Expr, pi.Columns, pi.AddingDefinitions) - } - if err != nil { - return nil, errors.Trace(err) - } - ret.doubleWritePartitions = make(map[int64]interface{}, len(pi.AddingDefinitions)) - for _, def := range pi.AddingDefinitions { - ret.doubleWritePartitions[def.ID] = nil - p, err := initPartition(ret, def) - if err != nil { - return nil, err - } - partitions[def.ID] = p - } - } - if len(pi.DroppingDefinitions) > 0 { - ret.reorganizePartitions = make(map[int64]interface{}, len(pi.DroppingDefinitions)) - for _, def := range pi.DroppingDefinitions { - ret.reorganizePartitions[def.ID] = nil - } - } - } - return ret, nil -} - -func setIndexesState(t *partitionedTable, state model.SchemaState) []*model.IndexInfo { - orig := t.meta.Indices - t.meta.Indices = make([]*model.IndexInfo, 0, len(orig)) - for i := range orig { - t.meta.Indices = append(t.meta.Indices, orig[i].Clone()) - if t.meta.Indices[i].State == model.StatePublic { - switch state { - case model.StateDeleteOnly, model.StateNone: - t.meta.Indices[i].State = model.StateDeleteOnly - case model.StatePublic: - // Keep as is - default: - // use the 'StateWriteReorganization' here, since StateDeleteReorganization - // would skip index writes. - t.meta.Indices[i].State = model.StateWriteReorganization - } - } - } - return orig -} - -func unsetIndexesState(t *partitionedTable, orig []*model.IndexInfo) { - t.meta.Indices = orig -} - -func initPartition(t *partitionedTable, def model.PartitionDefinition) (*partition, error) { - var newPart partition - err := initTableCommonWithIndices(&newPart.TableCommon, t.meta, def.ID, t.Columns, t.allocs, t.Constraints) - if err != nil { - return nil, err - } - newPart.table = t - return &newPart, nil -} - -func newPartitionExpr(tblInfo *model.TableInfo, tp model.PartitionType, expr string, partCols []model.CIStr, defs []model.PartitionDefinition) (*PartitionExpr, error) { - // a partitioned table cannot rely on session context/sql modes, so use a default one! - ctx := mock.NewContext() - dbName := model.NewCIStr(ctx.GetSessionVars().CurrentDB) - columns, names, err := expression.ColumnInfos2ColumnsAndNames(ctx, dbName, tblInfo.Name, tblInfo.Cols(), tblInfo) - if err != nil { - return nil, err - } - switch tp { - case model.PartitionTypeNone: - // Nothing to do - return nil, nil - case model.PartitionTypeRange: - return generateRangePartitionExpr(ctx, expr, partCols, defs, columns, names) - case model.PartitionTypeHash: - return generateHashPartitionExpr(ctx, expr, columns, names) - case model.PartitionTypeKey: - return generateKeyPartitionExpr(ctx, expr, partCols, columns, names) - case model.PartitionTypeList: - return generateListPartitionExpr(ctx, tblInfo, expr, partCols, defs, columns, names) - } - panic("cannot reach here") -} - -// PartitionExpr is the partition definition expressions. -type PartitionExpr struct { - // UpperBounds: (x < y1); (x < y2); (x < y3), used by locatePartition. - UpperBounds []expression.Expression - // OrigExpr is the partition expression ast used in point get. - OrigExpr ast.ExprNode - // Expr is the hash partition expression. - Expr expression.Expression - // Used in the key partition - *ForKeyPruning - // Used in the range pruning process. - *ForRangePruning - // Used in the range column pruning process. - *ForRangeColumnsPruning - // ColOffset is the offsets of partition columns. - ColumnOffset []int - *ForListPruning -} - -// GetPartColumnsForKeyPartition is used to get partition columns for key partition table -func (pe *PartitionExpr) GetPartColumnsForKeyPartition(columns []*expression.Column) ([]*expression.Column, []int) { - schema := expression.NewSchema(columns...) - partCols := make([]*expression.Column, len(pe.ColumnOffset)) - colLen := make([]int, 0, len(pe.ColumnOffset)) - for i, offset := range pe.ColumnOffset { - partCols[i] = schema.Columns[offset] - partCols[i].Index = i - colLen = append(colLen, partCols[i].RetType.GetFlen()) - } - return partCols, colLen -} - -// LocateKeyPartition is the common interface used to locate the destination partition -func (kp *ForKeyPruning) LocateKeyPartition(numParts uint64, r []types.Datum) (int, error) { - h := crc32.NewIEEE() - for _, col := range kp.KeyPartCols { - val := r[col.Index] - if val.Kind() == types.KindNull { - h.Write([]byte{0}) - } else { - data, err := val.ToHashKey() - if err != nil { - return 0, err - } - h.Write(data) - } - } - return int(h.Sum32() % uint32(numParts)), nil -} - -func initEvalBufferType(t *partitionedTable) { - hasExtraHandle := false - numCols := len(t.Cols()) - if !t.Meta().PKIsHandle { - hasExtraHandle = true - numCols++ - } - t.evalBufferTypes = make([]*types.FieldType, numCols) - for i, col := range t.Cols() { - t.evalBufferTypes[i] = &col.FieldType - } - - if hasExtraHandle { - t.evalBufferTypes[len(t.evalBufferTypes)-1] = types.NewFieldType(mysql.TypeLonglong) - } -} - -func initEvalBuffer(t *partitionedTable) *chunk.MutRow { - evalBuffer := chunk.MutRowFromTypes(t.evalBufferTypes) - return &evalBuffer -} - -// ForRangeColumnsPruning is used for range partition pruning. -type ForRangeColumnsPruning struct { - // LessThan contains expressions for [Partition][column]. - // If Maxvalue, then nil - LessThan [][]*expression.Expression -} - -func dataForRangeColumnsPruning(ctx sessionctx.Context, defs []model.PartitionDefinition, schema *expression.Schema, names []*types.FieldName, p *parser.Parser, colOffsets []int) (*ForRangeColumnsPruning, error) { - var res ForRangeColumnsPruning - res.LessThan = make([][]*expression.Expression, 0, len(defs)) - for i := 0; i < len(defs); i++ { - lessThanCols := make([]*expression.Expression, 0, len(defs[i].LessThan)) - for j := range defs[i].LessThan { - if strings.EqualFold(defs[i].LessThan[j], "MAXVALUE") { - // Use a nil pointer instead of math.MaxInt64 to avoid the corner cases. - lessThanCols = append(lessThanCols, nil) - // No column after MAXVALUE matters - break - } - tmp, err := parseSimpleExprWithNames(p, ctx, defs[i].LessThan[j], schema, names) - if err != nil { - return nil, err - } - _, ok := tmp.(*expression.Constant) - if !ok { - return nil, dbterror.ErrPartitionConstDomain - } - // TODO: Enable this for all types! - // Currently it will trigger changes for collation differences - switch schema.Columns[colOffsets[j]].RetType.GetType() { - case mysql.TypeDatetime, mysql.TypeDate: - // Will also fold constant - tmp = expression.BuildCastFunction(ctx, tmp, schema.Columns[colOffsets[j]].RetType) - } - lessThanCols = append(lessThanCols, &tmp) - } - res.LessThan = append(res.LessThan, lessThanCols) - } - return &res, nil -} - -// parseSimpleExprWithNames parses simple expression string to Expression. -// The expression string must only reference the column in the given NameSlice. -func parseSimpleExprWithNames(p *parser.Parser, ctx sessionctx.Context, exprStr string, schema *expression.Schema, names types.NameSlice) (expression.Expression, error) { - exprNode, err := parseExpr(p, exprStr) - if err != nil { - return nil, errors.Trace(err) - } - return expression.RewriteSimpleExprWithNames(ctx, exprNode, schema, names) -} - -// ForKeyPruning is used for key partition pruning. -type ForKeyPruning struct { - KeyPartCols []*expression.Column -} - -// ForListPruning is used for list partition pruning. -type ForListPruning struct { - // LocateExpr uses to locate list partition by row. - LocateExpr expression.Expression - // PruneExpr uses to prune list partition in partition pruner. - PruneExpr expression.Expression - // PruneExprCols is the columns of PruneExpr, it has removed the duplicate columns. - PruneExprCols []*expression.Column - // valueMap is column value -> partition idx, uses to locate list partition. - valueMap map[int64]int - // nullPartitionIdx is the partition idx for null value. - nullPartitionIdx int - // defaultPartitionIdx is the partition idx for default value/fallback. - defaultPartitionIdx int - - // For list columns partition pruning - ColPrunes []*ForListColumnPruning -} - -// btreeListColumnItem is BTree's Item that uses string to compare. -type btreeListColumnItem struct { - key string - location ListPartitionLocation -} - -func newBtreeListColumnItem(key string, location ListPartitionLocation) *btreeListColumnItem { - return &btreeListColumnItem{ - key: key, - location: location, - } -} - -func newBtreeListColumnSearchItem(key string) *btreeListColumnItem { - return &btreeListColumnItem{ - key: key, - } -} - -func (item *btreeListColumnItem) Less(other btree.Item) bool { - return item.key < other.(*btreeListColumnItem).key -} - -func lessBtreeListColumnItem(a, b *btreeListColumnItem) bool { - return a.key < b.key -} - -// ForListColumnPruning is used for list columns partition pruning. -type ForListColumnPruning struct { - ExprCol *expression.Column - valueTp *types.FieldType - valueMap map[string]ListPartitionLocation - sorted *btree.BTreeG[*btreeListColumnItem] - - // To deal with the location partition failure caused by inconsistent NewCollationEnabled values(see issue #32416). - // The following fields are used to delay building valueMap. - ctx sessionctx.Context - tblInfo *model.TableInfo - schema *expression.Schema - names types.NameSlice - colIdx int - - // catch-all partition / DEFAULT - defaultPartID int64 -} - -// ListPartitionGroup indicate the group index of the column value in a partition. -type ListPartitionGroup struct { - // Such as: list columns (a,b) (partition p0 values in ((1,5),(1,6))); - // For the column a which value is 1, the ListPartitionGroup is: - // ListPartitionGroup { - // PartIdx: 0, // 0 is the partition p0 index in all partitions. - // GroupIdxs: []int{0,1}, // p0 has 2 value group: (1,5) and (1,6), and they both contain the column a where value is 1; - // } // the value of GroupIdxs `0,1` is the index of the value group that contain the column a which value is 1. - PartIdx int - GroupIdxs []int -} - -// ListPartitionLocation indicate the partition location for the column value in list columns partition. -// Here is an example: -// Suppose the list columns partition is: list columns (a,b) (partition p0 values in ((1,5),(1,6)), partition p1 values in ((1,7),(9,9))); -// How to express the location of the column a which value is 1? -// For the column a which value is 1, both partition p0 and p1 contain the column a which value is 1. -// In partition p0, both value group0 (1,5) and group1 (1,6) are contain the column a which value is 1. -// In partition p1, value group0 (1,7) contains the column a which value is 1. -// So, the ListPartitionLocation of column a which value is 1 is: -// -// []ListPartitionGroup{ -// { -// PartIdx: 0, // `0` is the partition p0 index in all partitions. -// GroupIdxs: []int{0, 1} // `0,1` is the index of the value group0, group1. -// }, -// { -// PartIdx: 1, // `1` is the partition p1 index in all partitions. -// GroupIdxs: []int{0} // `0` is the index of the value group0. -// }, -// } -type ListPartitionLocation []ListPartitionGroup - -// IsEmpty returns true if the ListPartitionLocation is empty. -func (ps ListPartitionLocation) IsEmpty() bool { - for _, pg := range ps { - if len(pg.GroupIdxs) > 0 { - return false - } - } - return true -} - -func (ps ListPartitionLocation) findByPartitionIdx(partIdx int) int { - for i, p := range ps { - if p.PartIdx == partIdx { - return i - } - } - return -1 -} - -type listPartitionLocationHelper struct { - initialized bool - location ListPartitionLocation -} - -// NewListPartitionLocationHelper returns a new listPartitionLocationHelper. -func NewListPartitionLocationHelper() *listPartitionLocationHelper { - return &listPartitionLocationHelper{} -} - -// GetLocation gets the list partition location. -func (p *listPartitionLocationHelper) GetLocation() ListPartitionLocation { - return p.location -} - -// UnionPartitionGroup unions with the list-partition-value-group. -func (p *listPartitionLocationHelper) UnionPartitionGroup(pg ListPartitionGroup) { - idx := p.location.findByPartitionIdx(pg.PartIdx) - if idx < 0 { - // copy the group idx. - groupIdxs := make([]int, len(pg.GroupIdxs)) - copy(groupIdxs, pg.GroupIdxs) - p.location = append(p.location, ListPartitionGroup{ - PartIdx: pg.PartIdx, - GroupIdxs: groupIdxs, - }) - return - } - p.location[idx].union(pg) -} - -// Union unions with the other location. -func (p *listPartitionLocationHelper) Union(location ListPartitionLocation) { - for _, pg := range location { - p.UnionPartitionGroup(pg) - } -} - -// Intersect intersect with other location. -func (p *listPartitionLocationHelper) Intersect(location ListPartitionLocation) bool { - if !p.initialized { - p.initialized = true - p.location = make([]ListPartitionGroup, 0, len(location)) - p.location = append(p.location, location...) - return true - } - currPgs := p.location - remainPgs := make([]ListPartitionGroup, 0, len(location)) - for _, pg := range location { - idx := currPgs.findByPartitionIdx(pg.PartIdx) - if idx < 0 { - continue - } - if !currPgs[idx].intersect(pg) { - continue - } - remainPgs = append(remainPgs, currPgs[idx]) - } - p.location = remainPgs - return len(remainPgs) > 0 -} - -func (pg *ListPartitionGroup) intersect(otherPg ListPartitionGroup) bool { - if pg.PartIdx != otherPg.PartIdx { - return false - } - var groupIdxs []int - for _, gidx := range otherPg.GroupIdxs { - if pg.findGroupIdx(gidx) { - groupIdxs = append(groupIdxs, gidx) - } - } - pg.GroupIdxs = groupIdxs - return len(groupIdxs) > 0 -} - -func (pg *ListPartitionGroup) union(otherPg ListPartitionGroup) { - if pg.PartIdx != otherPg.PartIdx { - return - } - pg.GroupIdxs = append(pg.GroupIdxs, otherPg.GroupIdxs...) -} - -func (pg *ListPartitionGroup) findGroupIdx(groupIdx int) bool { - for _, gidx := range pg.GroupIdxs { - if gidx == groupIdx { - return true - } - } - return false -} - -// ForRangePruning is used for range partition pruning. -type ForRangePruning struct { - LessThan []int64 - MaxValue bool - Unsigned bool -} - -// dataForRangePruning extracts the less than parts from 'partition p0 less than xx ... partition p1 less than ...' -func dataForRangePruning(sctx sessionctx.Context, defs []model.PartitionDefinition) (*ForRangePruning, error) { - var maxValue bool - var unsigned bool - lessThan := make([]int64, len(defs)) - for i := 0; i < len(defs); i++ { - if strings.EqualFold(defs[i].LessThan[0], "MAXVALUE") { - // Use a bool flag instead of math.MaxInt64 to avoid the corner cases. - maxValue = true - } else { - var err error - lessThan[i], err = strconv.ParseInt(defs[i].LessThan[0], 10, 64) - var numErr *strconv.NumError - if stderr.As(err, &numErr) && numErr.Err == strconv.ErrRange { - var tmp uint64 - tmp, err = strconv.ParseUint(defs[i].LessThan[0], 10, 64) - lessThan[i] = int64(tmp) - unsigned = true - } - if err != nil { - val, ok := fixOldVersionPartitionInfo(sctx, defs[i].LessThan[0]) - if !ok { - logutil.BgLogger().Error("wrong partition definition", zap.String("less than", defs[i].LessThan[0])) - return nil, errors.WithStack(err) - } - lessThan[i] = val - } - } - } - return &ForRangePruning{ - LessThan: lessThan, - MaxValue: maxValue, - Unsigned: unsigned, - }, nil -} - -func fixOldVersionPartitionInfo(sctx sessionctx.Context, str string) (int64, bool) { - // less than value should be calculate to integer before persistent. - // Old version TiDB may not do it and store the raw expression. - tmp, err := parseSimpleExprWithNames(parser.New(), sctx, str, nil, nil) - if err != nil { - return 0, false - } - ret, isNull, err := tmp.EvalInt(sctx, chunk.Row{}) - if err != nil || isNull { - return 0, false - } - return ret, true -} - -func rangePartitionExprStrings(cols []model.CIStr, expr string) []string { - var s []string - if len(cols) > 0 { - s = make([]string, 0, len(cols)) - for _, col := range cols { - s = append(s, stringutil.Escape(col.O, mysql.ModeNone)) - } - } else { - s = []string{expr} - } - return s -} - -func generateKeyPartitionExpr(ctx sessionctx.Context, expr string, partCols []model.CIStr, - columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { - ret := &PartitionExpr{ - ForKeyPruning: &ForKeyPruning{}, - } - _, partColumns, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) - if err != nil { - return nil, errors.Trace(err) - } - ret.ColumnOffset = offset - ret.KeyPartCols = partColumns - - return ret, nil -} - -func generateRangePartitionExpr(ctx sessionctx.Context, expr string, partCols []model.CIStr, - defs []model.PartitionDefinition, columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { - // The caller should assure partition info is not nil. - p := parser.New() - schema := expression.NewSchema(columns...) - partStrs := rangePartitionExprStrings(partCols, expr) - locateExprs, err := getRangeLocateExprs(ctx, p, defs, partStrs, schema, names) - if err != nil { - return nil, errors.Trace(err) - } - ret := &PartitionExpr{ - UpperBounds: locateExprs, - } - - partExpr, _, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) - if err != nil { - return nil, errors.Trace(err) - } - ret.ColumnOffset = offset - - if len(partCols) < 1 { - tmp, err := dataForRangePruning(ctx, defs) - if err != nil { - return nil, errors.Trace(err) - } - ret.Expr = partExpr - ret.ForRangePruning = tmp - } else { - tmp, err := dataForRangeColumnsPruning(ctx, defs, schema, names, p, offset) - if err != nil { - return nil, errors.Trace(err) - } - ret.ForRangeColumnsPruning = tmp - } - return ret, nil -} - -func getRangeLocateExprs(ctx sessionctx.Context, p *parser.Parser, defs []model.PartitionDefinition, partStrs []string, schema *expression.Schema, names types.NameSlice) ([]expression.Expression, error) { - var buf bytes.Buffer - locateExprs := make([]expression.Expression, 0, len(defs)) - for i := 0; i < len(defs); i++ { - if strings.EqualFold(defs[i].LessThan[0], "MAXVALUE") { - // Expr less than maxvalue is always true. - fmt.Fprintf(&buf, "true") - } else { - maxValueFound := false - for j := range partStrs[1:] { - if strings.EqualFold(defs[i].LessThan[j+1], "MAXVALUE") { - // if any column will be less than MAXVALUE, so change < to <= of the previous prefix of columns - fmt.Fprintf(&buf, "((%s) <= (%s))", strings.Join(partStrs[:j+1], ","), strings.Join(defs[i].LessThan[:j+1], ",")) - maxValueFound = true - break - } - } - if !maxValueFound { - fmt.Fprintf(&buf, "((%s) < (%s))", strings.Join(partStrs, ","), strings.Join(defs[i].LessThan, ",")) - } - } - - expr, err := parseSimpleExprWithNames(p, ctx, buf.String(), schema, names) - if err != nil { - // If it got an error here, ddl may hang forever, so this error log is important. - logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", buf.String()), zap.Error(err)) - return nil, errors.Trace(err) - } - locateExprs = append(locateExprs, expr) - buf.Reset() - } - return locateExprs, nil -} - -func getColumnsOffset(cols, columns []*expression.Column) []int { - colsOffset := make([]int, len(cols)) - for i, col := range columns { - if idx := findIdxByColUniqueID(cols, col); idx >= 0 { - colsOffset[idx] = i - } - } - return colsOffset -} - -func findIdxByColUniqueID(cols []*expression.Column, col *expression.Column) int { - for idx, c := range cols { - if c.UniqueID == col.UniqueID { - return idx - } - } - return -1 -} - -func extractPartitionExprColumns(ctx sessionctx.Context, expr string, partCols []model.CIStr, columns []*expression.Column, names types.NameSlice) (expression.Expression, []*expression.Column, []int, error) { - var cols []*expression.Column - var partExpr expression.Expression - if len(partCols) == 0 { - schema := expression.NewSchema(columns...) - exprs, err := expression.ParseSimpleExprsWithNames(ctx, expr, schema, names) - if err != nil { - return nil, nil, nil, err - } - cols = expression.ExtractColumns(exprs[0]) - partExpr = exprs[0] - } else { - for _, col := range partCols { - idx := expression.FindFieldNameIdxByColName(names, col.L) - if idx < 0 { - panic("should never happen") - } - cols = append(cols, columns[idx]) - } - } - offset := getColumnsOffset(cols, columns) - deDupCols := make([]*expression.Column, 0, len(cols)) - for _, col := range cols { - if findIdxByColUniqueID(deDupCols, col) < 0 { - c := col.Clone().(*expression.Column) - deDupCols = append(deDupCols, c) - } - } - return partExpr, deDupCols, offset, nil -} - -func generateListPartitionExpr(ctx sessionctx.Context, tblInfo *model.TableInfo, expr string, partCols []model.CIStr, - defs []model.PartitionDefinition, columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { - // The caller should assure partition info is not nil. - partExpr, exprCols, offset, err := extractPartitionExprColumns(ctx, expr, partCols, columns, names) - if err != nil { - return nil, err - } - listPrune := &ForListPruning{} - if len(partCols) == 0 { - err = listPrune.buildListPruner(ctx, expr, defs, exprCols, columns, names) - } else { - err = listPrune.buildListColumnsPruner(ctx, tblInfo, partCols, defs, columns, names) - } - if err != nil { - return nil, err - } - ret := &PartitionExpr{ - ForListPruning: listPrune, - ColumnOffset: offset, - Expr: partExpr, - } - return ret, nil -} - -// Clone a copy of ForListPruning -func (lp *ForListPruning) Clone() *ForListPruning { - ret := *lp - if ret.LocateExpr != nil { - ret.LocateExpr = lp.LocateExpr.Clone() - } - if ret.PruneExpr != nil { - ret.PruneExpr = lp.PruneExpr.Clone() - } - ret.PruneExprCols = make([]*expression.Column, 0, len(lp.PruneExprCols)) - for i := range lp.PruneExprCols { - c := lp.PruneExprCols[i].Clone().(*expression.Column) - ret.PruneExprCols = append(ret.PruneExprCols, c) - } - ret.ColPrunes = make([]*ForListColumnPruning, 0, len(lp.ColPrunes)) - for i := range lp.ColPrunes { - l := *lp.ColPrunes[i] - l.ExprCol = l.ExprCol.Clone().(*expression.Column) - ret.ColPrunes = append(ret.ColPrunes, &l) - } - return &ret -} - -func (lp *ForListPruning) buildListPruner(ctx sessionctx.Context, exprStr string, defs []model.PartitionDefinition, exprCols []*expression.Column, - columns []*expression.Column, names types.NameSlice) error { - schema := expression.NewSchema(columns...) - p := parser.New() - expr, err := parseSimpleExprWithNames(p, ctx, exprStr, schema, names) - if err != nil { - // If it got an error here, ddl may hang forever, so this error log is important. - logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", exprStr), zap.Error(err)) - return errors.Trace(err) - } - // Since need to change the column index of the expression, clone the expression first. - lp.LocateExpr = expr.Clone() - lp.PruneExprCols = exprCols - lp.PruneExpr = expr.Clone() - cols := expression.ExtractColumns(lp.PruneExpr) - for _, c := range cols { - idx := findIdxByColUniqueID(exprCols, c) - if idx < 0 { - return table.ErrUnknownColumn.GenWithStackByArgs(c.OrigName) - } - c.Index = idx - } - err = lp.buildListPartitionValueMap(ctx, defs, schema, names, p) - if err != nil { - return err - } - return nil -} - -func (lp *ForListPruning) buildListColumnsPruner(ctx sessionctx.Context, - tblInfo *model.TableInfo, partCols []model.CIStr, defs []model.PartitionDefinition, - columns []*expression.Column, names types.NameSlice) error { - schema := expression.NewSchema(columns...) - p := parser.New() - colPrunes := make([]*ForListColumnPruning, 0, len(partCols)) - lp.defaultPartitionIdx = -1 - for colIdx := range partCols { - colInfo := model.FindColumnInfo(tblInfo.Columns, partCols[colIdx].L) - if colInfo == nil { - return table.ErrUnknownColumn.GenWithStackByArgs(partCols[colIdx].L) - } - idx := expression.FindFieldNameIdxByColName(names, partCols[colIdx].L) - if idx < 0 { - return table.ErrUnknownColumn.GenWithStackByArgs(partCols[colIdx].L) - } - colPrune := &ForListColumnPruning{ - ctx: ctx, - tblInfo: tblInfo, - schema: schema, - names: names, - colIdx: colIdx, - ExprCol: columns[idx], - valueTp: &colInfo.FieldType, - valueMap: make(map[string]ListPartitionLocation), - sorted: btree.NewG[*btreeListColumnItem](btreeDegree, lessBtreeListColumnItem), - } - err := colPrune.buildPartitionValueMapAndSorted(p, defs) - if err != nil { - return err - } - if colPrune.defaultPartID > 0 { - for i := range defs { - if defs[i].ID == colPrune.defaultPartID { - if lp.defaultPartitionIdx >= 0 && i != lp.defaultPartitionIdx { - // Should be same for all columns, i.e. should never happen! - return table.ErrUnknownPartition - } - lp.defaultPartitionIdx = i - } - } - } - colPrunes = append(colPrunes, colPrune) - } - lp.ColPrunes = colPrunes - return nil -} - -// buildListPartitionValueMap builds list partition value map. -// The map is column value -> partition index. -// colIdx is the column index in the list columns. -func (lp *ForListPruning) buildListPartitionValueMap(ctx sessionctx.Context, defs []model.PartitionDefinition, - schema *expression.Schema, names types.NameSlice, p *parser.Parser) error { - lp.valueMap = map[int64]int{} - lp.nullPartitionIdx = -1 - lp.defaultPartitionIdx = -1 - for partitionIdx, def := range defs { - for _, vs := range def.InValues { - if strings.EqualFold(vs[0], "DEFAULT") { - lp.defaultPartitionIdx = partitionIdx - continue - } - expr, err := parseSimpleExprWithNames(p, ctx, vs[0], schema, names) - if err != nil { - return errors.Trace(err) - } - v, isNull, err := expr.EvalInt(ctx, chunk.Row{}) - if err != nil { - return errors.Trace(err) - } - if isNull { - lp.nullPartitionIdx = partitionIdx - continue - } - lp.valueMap[v] = partitionIdx - } - } - return nil -} - -// LocatePartition locates partition by the column value -func (lp *ForListPruning) LocatePartition(value int64, isNull bool) int { - if isNull { - if lp.nullPartitionIdx >= 0 { - return lp.nullPartitionIdx - } - return lp.defaultPartitionIdx - } - partitionIdx, ok := lp.valueMap[value] - if !ok { - return lp.defaultPartitionIdx - } - return partitionIdx -} - -func (lp *ForListPruning) locateListPartitionByRow(ctx sessionctx.Context, r []types.Datum) (int, error) { - value, isNull, err := lp.LocateExpr.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) - if err != nil { - return -1, errors.Trace(err) - } - idx := lp.LocatePartition(value, isNull) - if idx >= 0 { - return idx, nil - } - if isNull { - return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs("NULL") - } - var valueMsg string - if mysql.HasUnsignedFlag(lp.LocateExpr.GetType().GetFlag()) { - // Handle unsigned value - valueMsg = fmt.Sprintf("%d", uint64(value)) - } else { - valueMsg = fmt.Sprintf("%d", value) - } - return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) -} - -func (lp *ForListPruning) locateListColumnsPartitionByRow(ctx sessionctx.Context, r []types.Datum) (int, error) { - helper := NewListPartitionLocationHelper() - sc := ctx.GetSessionVars().StmtCtx - for _, colPrune := range lp.ColPrunes { - location, err := colPrune.LocatePartition(sc, r[colPrune.ExprCol.Index]) - if err != nil { - return -1, errors.Trace(err) - } - if !helper.Intersect(location) { - break - } - } - location := helper.GetLocation() - if location.IsEmpty() { - if lp.defaultPartitionIdx >= 0 { - return lp.defaultPartitionIdx, nil - } - return -1, table.ErrNoPartitionForGivenValue.GenWithStackByArgs("from column_list") - } - return location[0].PartIdx, nil -} - -// GetDefaultIdx return the Default partitions index. -func (lp *ForListPruning) GetDefaultIdx() int { - return lp.defaultPartitionIdx -} - -// buildPartitionValueMapAndSorted builds list columns partition value map for the specified column. -// It also builds list columns partition value btree for the specified column. -// colIdx is the specified column index in the list columns. -func (lp *ForListColumnPruning) buildPartitionValueMapAndSorted(p *parser.Parser, - defs []model.PartitionDefinition) error { - l := len(lp.valueMap) - if l != 0 { - return nil - } - - return lp.buildListPartitionValueMapAndSorted(p, defs) -} - -// HasDefault return true if the partition has the DEFAULT value -func (lp *ForListColumnPruning) HasDefault() bool { - return lp.defaultPartID > 0 -} - -// RebuildPartitionValueMapAndSorted rebuilds list columns partition value map for the specified column. -func (lp *ForListColumnPruning) RebuildPartitionValueMapAndSorted(p *parser.Parser, - defs []model.PartitionDefinition) error { - lp.valueMap = make(map[string]ListPartitionLocation, len(lp.valueMap)) - lp.sorted.Clear(false) - return lp.buildListPartitionValueMapAndSorted(p, defs) -} - -func (lp *ForListColumnPruning) buildListPartitionValueMapAndSorted(p *parser.Parser, defs []model.PartitionDefinition) error { - sc := lp.ctx.GetSessionVars().StmtCtx -DEFS: - for partitionIdx, def := range defs { - for groupIdx, vs := range def.InValues { - if len(vs) == 1 && vs[0] == "DEFAULT" { - lp.defaultPartID = def.ID - continue DEFS - } - keyBytes, err := lp.genConstExprKey(lp.ctx, sc, vs[lp.colIdx], lp.schema, lp.names, p) - if err != nil { - return errors.Trace(err) - } - key := string(keyBytes) - location, ok := lp.valueMap[key] - if ok { - idx := location.findByPartitionIdx(partitionIdx) - if idx != -1 { - location[idx].GroupIdxs = append(location[idx].GroupIdxs, groupIdx) - continue - } - } - location = append(location, ListPartitionGroup{ - PartIdx: partitionIdx, - GroupIdxs: []int{groupIdx}, - }) - lp.valueMap[key] = location - lp.sorted.ReplaceOrInsert(newBtreeListColumnItem(key, location)) - } - } - return nil -} - -func (lp *ForListColumnPruning) genConstExprKey(ctx sessionctx.Context, sc *stmtctx.StatementContext, exprStr string, - schema *expression.Schema, names types.NameSlice, p *parser.Parser) ([]byte, error) { - expr, err := parseSimpleExprWithNames(p, ctx, exprStr, schema, names) - if err != nil { - return nil, errors.Trace(err) - } - v, err := expr.Eval(chunk.Row{}) - if err != nil { - return nil, errors.Trace(err) - } - key, err := lp.genKey(sc, v) - if err != nil { - return nil, errors.Trace(err) - } - return key, nil -} - -func (lp *ForListColumnPruning) genKey(sc *stmtctx.StatementContext, v types.Datum) ([]byte, error) { - v, err := v.ConvertTo(sc, lp.valueTp) - if err != nil { - return nil, errors.Trace(err) - } - valByte, err := codec.EncodeKey(sc, nil, v) - return valByte, err -} - -// LocatePartition locates partition by the column value -func (lp *ForListColumnPruning) LocatePartition(sc *stmtctx.StatementContext, v types.Datum) (ListPartitionLocation, error) { - key, err := lp.genKey(sc, v) - if err != nil { - return nil, errors.Trace(err) - } - location, ok := lp.valueMap[string(key)] - if !ok { - return nil, nil - } - return location, nil -} - -// LocateRanges locates partition ranges by the column range -func (lp *ForListColumnPruning) LocateRanges(sc *stmtctx.StatementContext, r *ranger.Range, defaultPartIdx int) ([]ListPartitionLocation, error) { - var lowKey, highKey []byte - var err error - lowVal := r.LowVal[0] - if r.LowVal[0].Kind() == types.KindMinNotNull { - lowVal = types.GetMinValue(lp.ExprCol.GetType()) - } - highVal := r.HighVal[0] - if r.HighVal[0].Kind() == types.KindMaxValue { - highVal = types.GetMaxValue(lp.ExprCol.GetType()) - } - - // For string type, values returned by GetMinValue and GetMaxValue are already encoded, - // so it's unnecessary to invoke genKey to encode them. - if lp.ExprCol.GetType().EvalType() == types.ETString && r.LowVal[0].Kind() == types.KindMinNotNull { - lowKey = (&lowVal).GetBytes() - } else { - lowKey, err = lp.genKey(sc, lowVal) - if err != nil { - return nil, errors.Trace(err) - } - } - - if lp.ExprCol.GetType().EvalType() == types.ETString && r.HighVal[0].Kind() == types.KindMaxValue { - highKey = (&highVal).GetBytes() - } else { - highKey, err = lp.genKey(sc, highVal) - if err != nil { - return nil, errors.Trace(err) - } - } - - if r.LowExclude { - lowKey = kv.Key(lowKey).PrefixNext() - } - if !r.HighExclude { - highKey = kv.Key(highKey).PrefixNext() - } - - locations := make([]ListPartitionLocation, 0, lp.sorted.Len()) - lp.sorted.AscendRange(newBtreeListColumnSearchItem(string(hack.String(lowKey))), newBtreeListColumnSearchItem(string(hack.String(highKey))), func(item *btreeListColumnItem) bool { - locations = append(locations, item.location) - return true - }) - if lp.HasDefault() { - // Add the default partition since there may be a gap - // between the conditions range and the LIST COLUMNS values - locations = append(locations, ListPartitionLocation{ - ListPartitionGroup{ - PartIdx: defaultPartIdx, - GroupIdxs: []int{-1}, // Special group! - }, - }) - } - return locations, nil -} - -func generateHashPartitionExpr(ctx sessionctx.Context, exprStr string, - columns []*expression.Column, names types.NameSlice) (*PartitionExpr, error) { - // The caller should assure partition info is not nil. - schema := expression.NewSchema(columns...) - origExpr, err := parseExpr(parser.New(), exprStr) - if err != nil { - return nil, err - } - exprs, err := rewritePartitionExpr(ctx, origExpr, schema, names) - if err != nil { - // If it got an error here, ddl may hang forever, so this error log is important. - logutil.BgLogger().Error("wrong table partition expression", zap.String("expression", exprStr), zap.Error(err)) - return nil, errors.Trace(err) - } - // build column offset. - partitionCols := expression.ExtractColumns(exprs) - offset := make([]int, len(partitionCols)) - for i, col := range columns { - for j, partitionCol := range partitionCols { - if partitionCol.UniqueID == col.UniqueID { - offset[j] = i - } - } - } - exprs.HashCode(ctx.GetSessionVars().StmtCtx) - return &PartitionExpr{ - Expr: exprs, - OrigExpr: origExpr, - ColumnOffset: offset, - }, nil -} - -// PartitionExpr returns the partition expression. -func (t *partitionedTable) PartitionExpr() *PartitionExpr { - return t.partitionExpr -} - -func (t *partitionedTable) GetPartitionColumnIDs() []int64 { - // PARTITION BY {LIST|RANGE} COLUMNS uses columns directly without expressions - pi := t.Meta().Partition - if len(pi.Columns) > 0 { - colIDs := make([]int64, 0, len(pi.Columns)) - for _, name := range pi.Columns { - col := table.FindColLowerCase(t.Cols(), name.L) - if col == nil { - // For safety, should not happen - continue - } - colIDs = append(colIDs, col.ID) - } - return colIDs - } - if t.partitionExpr == nil { - return nil - } - - partitionCols := expression.ExtractColumns(t.partitionExpr.Expr) - colIDs := make([]int64, 0, len(partitionCols)) - for _, col := range partitionCols { - colIDs = append(colIDs, col.ID) - } - return colIDs -} - -func (t *partitionedTable) GetPartitionColumnNames() []model.CIStr { - pi := t.Meta().Partition - if len(pi.Columns) > 0 { - return pi.Columns - } - colIDs := t.GetPartitionColumnIDs() - colNames := make([]model.CIStr, 0, len(colIDs)) - for _, colID := range colIDs { - for _, col := range t.Cols() { - if col.ID == colID { - colNames = append(colNames, col.Name) - } - } - } - return colNames -} - -// PartitionRecordKey is exported for test. -func PartitionRecordKey(pid int64, handle int64) kv.Key { - recordPrefix := tablecodec.GenTableRecordPrefix(pid) - return tablecodec.EncodeRecordKey(recordPrefix, kv.IntHandle(handle)) -} - -func (t *partitionedTable) CheckForExchangePartition(ctx sessionctx.Context, pi *model.PartitionInfo, r []types.Datum, partID, ntID int64) error { - defID, err := t.locatePartition(ctx, r) - if err != nil { - return err - } - if defID != partID && defID != ntID { - return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) - } - return nil -} - -// locatePartitionCommon returns the partition idx of the input record. -func (t *partitionedTable) locatePartitionCommon(ctx sessionctx.Context, tp model.PartitionType, partitionExpr *PartitionExpr, num uint64, columnsPartitioned bool, r []types.Datum) (int, error) { - var err error - var idx int - switch tp { - case model.PartitionTypeRange: - if columnsPartitioned { - idx, err = t.locateRangeColumnPartition(ctx, partitionExpr, r) - } else { - idx, err = t.locateRangePartition(ctx, partitionExpr, r) - } - case model.PartitionTypeHash: - // Note that only LIST and RANGE supports REORGANIZE PARTITION - idx, err = t.locateHashPartition(ctx, partitionExpr, num, r) - case model.PartitionTypeKey: - idx, err = partitionExpr.LocateKeyPartition(num, r) - case model.PartitionTypeList: - idx, err = partitionExpr.locateListPartition(ctx, r) - case model.PartitionTypeNone: - idx = 0 - } - if err != nil { - return 0, errors.Trace(err) - } - return idx, nil -} - -func (t *partitionedTable) locatePartition(ctx sessionctx.Context, r []types.Datum) (int64, error) { - pi := t.Meta().GetPartitionInfo() - columnsSet := len(t.meta.Partition.Columns) > 0 - idx, err := t.locatePartitionCommon(ctx, pi.Type, t.partitionExpr, pi.Num, columnsSet, r) - if err != nil { - return 0, errors.Trace(err) - } - return pi.Definitions[idx].ID, nil -} - -func (t *partitionedTable) locateReorgPartition(ctx sessionctx.Context, r []types.Datum) (int64, error) { - pi := t.Meta().GetPartitionInfo() - columnsSet := len(pi.DDLColumns) > 0 - // Note that for KEY/HASH partitioning, since we do not support LINEAR, - // all partitions will be reorganized, - // so we can use the number in Dropping or AddingDefinitions, - // depending on current state. - num := len(pi.AddingDefinitions) - if pi.DDLState == model.StateDeleteReorganization { - num = len(pi.DroppingDefinitions) - } - idx, err := t.locatePartitionCommon(ctx, pi.DDLType, t.reorgPartitionExpr, uint64(num), columnsSet, r) - if err != nil { - return 0, errors.Trace(err) - } - if pi.DDLState == model.StateDeleteReorganization { - return pi.DroppingDefinitions[idx].ID, nil - } - return pi.AddingDefinitions[idx].ID, nil -} - -func (t *partitionedTable) locateRangeColumnPartition(ctx sessionctx.Context, partitionExpr *PartitionExpr, r []types.Datum) (int, error) { - upperBounds := partitionExpr.UpperBounds - var lastError error - evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) - defer t.evalBufferPool.Put(evalBuffer) - idx := sort.Search(len(upperBounds), func(i int) bool { - evalBuffer.SetDatums(r...) - ret, isNull, err := upperBounds[i].EvalInt(ctx, evalBuffer.ToRow()) - if err != nil { - lastError = err - return true // Does not matter, will propagate the last error anyway. - } - if isNull { - // If the column value used to determine the partition is NULL, the row is inserted into the lowest partition. - // See https://dev.mysql.com/doc/mysql-partitioning-excerpt/5.7/en/partitioning-handling-nulls.html - return true // Always less than any other value (NULL cannot be in the partition definition VALUE LESS THAN). - } - return ret > 0 - }) - if lastError != nil { - return 0, errors.Trace(lastError) - } - if idx >= len(upperBounds) { - // The data does not belong to any of the partition returns `table has no partition for value %s`. - var valueMsg string - if t.meta.Partition.Expr != "" { - e, err := expression.ParseSimpleExprWithTableInfo(ctx, t.meta.Partition.Expr, t.meta) - if err == nil { - val, _, err := e.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) - if err == nil { - valueMsg = strconv.FormatInt(val, 10) - } - } - } else { - // When the table is partitioned by range columns. - valueMsg = "from column_list" - } - return 0, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) - } - return idx, nil -} - -func (pe *PartitionExpr) locateListPartition(ctx sessionctx.Context, r []types.Datum) (int, error) { - lp := pe.ForListPruning - if len(lp.ColPrunes) == 0 { - return lp.locateListPartitionByRow(ctx, r) - } - return lp.locateListColumnsPartitionByRow(ctx, r) -} - -func (t *partitionedTable) locateRangePartition(ctx sessionctx.Context, partitionExpr *PartitionExpr, r []types.Datum) (int, error) { - var ( - ret int64 - val int64 - isNull bool - err error - ) - if col, ok := partitionExpr.Expr.(*expression.Column); ok { - if r[col.Index].IsNull() { - isNull = true - } - ret = r[col.Index].GetInt64() - } else { - evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) - defer t.evalBufferPool.Put(evalBuffer) - evalBuffer.SetDatums(r...) - val, isNull, err = partitionExpr.Expr.EvalInt(ctx, evalBuffer.ToRow()) - if err != nil { - return 0, err - } - ret = val - } - unsigned := mysql.HasUnsignedFlag(partitionExpr.Expr.GetType().GetFlag()) - ranges := partitionExpr.ForRangePruning - length := len(ranges.LessThan) - pos := sort.Search(length, func(i int) bool { - if isNull { - return true - } - return ranges.Compare(i, ret, unsigned) > 0 - }) - if isNull { - pos = 0 - } - if pos < 0 || pos >= length { - // The data does not belong to any of the partition returns `table has no partition for value %s`. - var valueMsg string - // TODO: Test with ALTER TABLE t PARTITION BY with a different expression / type - if t.meta.Partition.Expr != "" { - e, err := expression.ParseSimpleExprWithTableInfo(ctx, t.meta.Partition.Expr, t.meta) - if err == nil { - val, _, err := e.EvalInt(ctx, chunk.MutRowFromDatums(r).ToRow()) - if err == nil { - if unsigned { - valueMsg = fmt.Sprintf("%d", uint64(val)) - } else { - valueMsg = fmt.Sprintf("%d", val) - } - } - } - } else { - // When the table is partitioned by range columns. - valueMsg = "from column_list" - } - return 0, table.ErrNoPartitionForGivenValue.GenWithStackByArgs(valueMsg) - } - return pos, nil -} - -// TODO: supports linear hashing -func (t *partitionedTable) locateHashPartition(ctx sessionctx.Context, partExpr *PartitionExpr, numParts uint64, r []types.Datum) (int, error) { - if col, ok := partExpr.Expr.(*expression.Column); ok { - var data types.Datum - switch r[col.Index].Kind() { - case types.KindInt64, types.KindUint64: - data = r[col.Index] - default: - var err error - data, err = r[col.Index].ConvertTo(ctx.GetSessionVars().StmtCtx, types.NewFieldType(mysql.TypeLong)) - if err != nil { - return 0, err - } - } - ret := data.GetInt64() - ret = ret % int64(numParts) - if ret < 0 { - ret = -ret - } - return int(ret), nil - } - evalBuffer := t.evalBufferPool.Get().(*chunk.MutRow) - defer t.evalBufferPool.Put(evalBuffer) - evalBuffer.SetDatums(r...) - ret, isNull, err := partExpr.Expr.EvalInt(ctx, evalBuffer.ToRow()) - if err != nil { - return 0, err - } - if isNull { - return 0, nil - } - ret = ret % int64(numParts) - if ret < 0 { - ret = -ret - } - return int(ret), nil -} - -// GetPartition returns a Table, which is actually a partition. -func (t *partitionedTable) GetPartition(pid int64) table.PhysicalTable { - // Attention, can't simply use `return t.partitions[pid]` here. - // Because A nil of type *partition is a kind of `table.PhysicalTable` - part, ok := t.partitions[pid] - if !ok { - // Should never happen! - return nil - } - return part -} - -// GetReorganizedPartitionedTable returns the same table -// but only with the AddingDefinitions used. -func GetReorganizedPartitionedTable(t table.Table) (table.PartitionedTable, error) { - // This is used during Reorganize partitions; All data from DroppingDefinitions - // will be copied to AddingDefinitions, so only setup with AddingDefinitions! - - // Do not change any Definitions of t, but create a new struct. - if t.GetPartitionedTable() == nil { - return nil, dbterror.ErrUnsupportedReorganizePartition.GenWithStackByArgs() - } - tblInfo := t.Meta().Clone() - pi := tblInfo.Partition - pi.Definitions = pi.AddingDefinitions - pi.Num = uint64(len(pi.Definitions)) - pi.AddingDefinitions = nil - pi.DroppingDefinitions = nil - - // Reorganized status, use the new values - pi.Type = pi.DDLType - pi.Expr = pi.DDLExpr - pi.Columns = pi.DDLColumns - tblInfo.ID = pi.NewTableID - - constraints, err := table.LoadCheckConstraint(tblInfo) - if err != nil { - return nil, err - } - var tc TableCommon - initTableCommon(&tc, tblInfo, tblInfo.ID, t.Cols(), t.Allocators(nil), constraints) - - // and rebuild the partitioning structure - return newPartitionedTable(&tc, tblInfo) -} - -// GetPartitionByRow returns a Table, which is actually a Partition. -func (t *partitionedTable) GetPartitionByRow(ctx sessionctx.Context, r []types.Datum) (table.PhysicalTable, error) { - pid, err := t.locatePartition(ctx, r) - if err != nil { - return nil, errors.Trace(err) - } - return t.partitions[pid], nil -} - -// GetPartitionByRow returns a Table, which is actually a Partition. -func (t *partitionTableWithGivenSets) GetPartitionByRow(ctx sessionctx.Context, r []types.Datum) (table.PhysicalTable, error) { - pid, err := t.locatePartition(ctx, r) - if err != nil { - return nil, errors.Trace(err) - } - if _, ok := t.givenSetPartitions[pid]; !ok { - return nil, errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) - } - return t.partitions[pid], nil -} - -// checkConstraintForExchangePartition is only used for ExchangePartition by partitionTable during write only state. -// It check if rowData inserted or updated violate checkConstraints of non-partitionTable. -func checkConstraintForExchangePartition(sctx sessionctx.Context, row []types.Datum, partID, ntID int64) error { - type InfoSchema interface { - TableByID(id int64) (val table.Table, ok bool) - } - is, ok := sctx.GetDomainInfoSchema().(InfoSchema) - if !ok { - return errors.Errorf("exchange partition process assert inforSchema failed") - } - nt, tableFound := is.TableByID(ntID) - if !tableFound { - // Now partID is nt tableID. - nt, tableFound = is.TableByID(partID) - if !tableFound { - return errors.Errorf("exchange partition process table by id failed") - } - } - type CheckConstraintTable interface { - CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error - } - cc, ok := nt.(CheckConstraintTable) - if !ok { - return errors.Errorf("exchange partition process assert check constraint failed") - } - err := cc.CheckRowConstraint(sctx, row) - if err != nil { - // TODO: make error include ExchangePartition info. - return err - } - return nil -} - -// AddRecord implements the AddRecord method for the table.Table interface. -func (t *partitionedTable) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - return partitionedTableAddRecord(ctx, t, r, nil, opts) -} - -func partitionedTableAddRecord(ctx sessionctx.Context, t *partitionedTable, r []types.Datum, partitionSelection map[int64]struct{}, opts []table.AddRecordOption) (recordID kv.Handle, err error) { - pid, err := t.locatePartition(ctx, r) - if err != nil { - return nil, errors.Trace(err) - } - - if partitionSelection != nil { - if _, ok := partitionSelection[pid]; !ok { - return nil, errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) - } - } - if t.Meta().Partition.HasTruncatingPartitionID(pid) { - return nil, errors.WithStack(dbterror.ErrInvalidDDLState.GenWithStack("the partition is in not in public")) - } - exchangePartitionInfo := t.Meta().ExchangePartitionInfo - if exchangePartitionInfo != nil && exchangePartitionInfo.ExchangePartitionDefID == pid && - variable.EnableCheckConstraint.Load() { - err = checkConstraintForExchangePartition(ctx, r, pid, exchangePartitionInfo.ExchangePartitionTableID) - if err != nil { - return nil, errors.WithStack(err) - } - } - tbl := t.GetPartition(pid) - recordID, err = tbl.AddRecord(ctx, r, opts...) - if err != nil { - return - } - if t.Meta().Partition.DDLState == model.StateDeleteOnly { - return - } - if _, ok := t.reorganizePartitions[pid]; ok { - // Double write to the ongoing reorganized partition - pid, err = t.locateReorgPartition(ctx, r) - if err != nil { - return nil, errors.Trace(err) - } - tbl = t.GetPartition(pid) - recordID, err = tbl.AddRecord(ctx, r, opts...) - if err != nil { - return - } - } - return -} - -// partitionTableWithGivenSets is used for this kind of grammar: partition (p0,p1) -// Basically it is the same as partitionedTable except that partitionTableWithGivenSets -// checks the given partition set for AddRecord/UpdateRecord operations. -type partitionTableWithGivenSets struct { - *partitionedTable - givenSetPartitions map[int64]struct{} -} - -// NewPartitionTableWithGivenSets creates a new partition table from a partition table. -func NewPartitionTableWithGivenSets(tbl table.PartitionedTable, partitions map[int64]struct{}) table.PartitionedTable { - if raw, ok := tbl.(*partitionedTable); ok { - return &partitionTableWithGivenSets{ - partitionedTable: raw, - givenSetPartitions: partitions, - } - } - return tbl -} - -// AddRecord implements the AddRecord method for the table.Table interface. -func (t *partitionTableWithGivenSets) AddRecord(ctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - return partitionedTableAddRecord(ctx, t.partitionedTable, r, t.givenSetPartitions, opts) -} - -func (t *partitionTableWithGivenSets) GetAllPartitionIDs() []int64 { - ptIDs := make([]int64, 0, len(t.partitions)) - for id := range t.givenSetPartitions { - ptIDs = append(ptIDs, id) - } - return ptIDs -} - -// RemoveRecord implements table.Table RemoveRecord interface. -func (t *partitionedTable) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { - pid, err := t.locatePartition(ctx, r) - if err != nil { - return errors.Trace(err) - } - - tbl := t.GetPartition(pid) - err = tbl.RemoveRecord(ctx, h, r) - if err != nil { - return errors.Trace(err) - } - - if _, ok := t.reorganizePartitions[pid]; ok { - pid, err = t.locateReorgPartition(ctx, r) - if err != nil { - return errors.Trace(err) - } - tbl = t.GetPartition(pid) - err = tbl.RemoveRecord(ctx, h, r) - if err != nil { - return errors.Trace(err) - } - } - return nil -} - -func (t *partitionedTable) GetAllPartitionIDs() []int64 { - ptIDs := make([]int64, 0, len(t.partitions)) - for id := range t.partitions { - if _, ok := t.doubleWritePartitions[id]; ok { - continue - } - ptIDs = append(ptIDs, id) - } - return ptIDs -} - -// UpdateRecord implements table.Table UpdateRecord interface. -// `touched` means which columns are really modified, used for secondary indices. -// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. -func (t *partitionedTable) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { - return partitionedTableUpdateRecord(ctx, sctx, t, h, currData, newData, touched, nil) -} - -func (t *partitionTableWithGivenSets) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, currData, newData []types.Datum, touched []bool) error { - return partitionedTableUpdateRecord(ctx, sctx, t.partitionedTable, h, currData, newData, touched, t.givenSetPartitions) -} - -func partitionedTableUpdateRecord(gctx context.Context, ctx sessionctx.Context, t *partitionedTable, h kv.Handle, currData, newData []types.Datum, touched []bool, partitionSelection map[int64]struct{}) error { - from, err := t.locatePartition(ctx, currData) - if err != nil { - return errors.Trace(err) - } - to, err := t.locatePartition(ctx, newData) - if err != nil { - return errors.Trace(err) - } - if partitionSelection != nil { - if _, ok := partitionSelection[to]; !ok { - return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) - } - // Should not have been read from this partition! Checked already in GetPartitionByRow() - if _, ok := partitionSelection[from]; !ok { - return errors.WithStack(table.ErrRowDoesNotMatchGivenPartitionSet) - } - } - if t.Meta().Partition.HasTruncatingPartitionID(to) { - return errors.WithStack(dbterror.ErrInvalidDDLState.GenWithStack("the partition is in not in public")) - } - exchangePartitionInfo := t.Meta().ExchangePartitionInfo - if exchangePartitionInfo != nil && exchangePartitionInfo.ExchangePartitionDefID == to && - variable.EnableCheckConstraint.Load() { - err = checkConstraintForExchangePartition(ctx, newData, to, exchangePartitionInfo.ExchangePartitionTableID) - if err != nil { - return errors.WithStack(err) - } - } - - // The old and new data locate in different partitions. - // Remove record from old partition and add record to new partition. - if from != to { - _, err = t.GetPartition(to).AddRecord(ctx, newData) - if err != nil { - return errors.Trace(err) - } - // UpdateRecord should be side effect free, but there're two steps here. - // What would happen if step1 succeed but step2 meets error? It's hard - // to rollback. - // So this special order is chosen: add record first, errors such as - // 'Key Already Exists' will generally happen during step1, errors are - // unlikely to happen in step2. - err = t.GetPartition(from).RemoveRecord(ctx, h, currData) - if err != nil { - logutil.BgLogger().Error("update partition record fails", zap.String("message", "new record inserted while old record is not removed"), zap.Error(err)) - return errors.Trace(err) - } - newTo, newFrom := int64(0), int64(0) - if _, ok := t.reorganizePartitions[to]; ok { - newTo, err = t.locateReorgPartition(ctx, newData) - // There might be valid cases when errors should be accepted? - if err != nil { - return errors.Trace(err) - } - } - if _, ok := t.reorganizePartitions[from]; ok { - newFrom, err = t.locateReorgPartition(ctx, currData) - // There might be valid cases when errors should be accepted? - if err != nil { - return errors.Trace(err) - } - } - if newTo == newFrom && newTo != 0 { - // Update needs to be done in StateDeleteOnly as well - tbl := t.GetPartition(newTo) - return tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) - } - if newTo != 0 && t.Meta().GetPartitionInfo().DDLState != model.StateDeleteOnly { - tbl := t.GetPartition(newTo) - _, err = tbl.AddRecord(ctx, newData) - if err != nil { - return errors.Trace(err) - } - } - if newFrom != 0 { - tbl := t.GetPartition(newFrom) - err = tbl.RemoveRecord(ctx, h, currData) - // TODO: Can this happen? When the data is not yet backfilled? - if err != nil { - return errors.Trace(err) - } - } - return nil - } - tbl := t.GetPartition(to) - err = tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) - if err != nil { - return errors.Trace(err) - } - if _, ok := t.reorganizePartitions[to]; ok { - // Even if to == from, in the reorganized partitions they may differ - // like in case of a split - newTo, err := t.locateReorgPartition(ctx, newData) - if err != nil { - return errors.Trace(err) - } - newFrom, err := t.locateReorgPartition(ctx, currData) - if err != nil { - return errors.Trace(err) - } - if newTo == newFrom { - tbl = t.GetPartition(newTo) - if t.Meta().Partition.DDLState == model.StateDeleteOnly { - err = tbl.RemoveRecord(ctx, h, currData) - } else { - err = tbl.UpdateRecord(gctx, ctx, h, currData, newData, touched) - } - if err != nil { - return errors.Trace(err) - } - return nil - } - if t.Meta().GetPartitionInfo().DDLState != model.StateDeleteOnly { - tbl = t.GetPartition(newTo) - _, err = tbl.AddRecord(ctx, newData) - if err != nil { - return errors.Trace(err) - } - } - tbl = t.GetPartition(newFrom) - err = tbl.RemoveRecord(ctx, h, currData) - if err != nil { - return errors.Trace(err) - } - } - return nil -} - -// FindPartitionByName finds partition in table meta by name. -func FindPartitionByName(meta *model.TableInfo, parName string) (int64, error) { - // Hash partition table use p0, p1, p2, p3 as partition names automatically. - parName = strings.ToLower(parName) - for _, def := range meta.Partition.Definitions { - if strings.EqualFold(def.Name.L, parName) { - return def.ID, nil - } - } - return -1, errors.Trace(table.ErrUnknownPartition.GenWithStackByArgs(parName, meta.Name.O)) -} - -func parseExpr(p *parser.Parser, exprStr string) (ast.ExprNode, error) { - exprStr = "select " + exprStr - stmts, _, err := p.ParseSQL(exprStr) - if err != nil { - return nil, util.SyntaxWarn(err) - } - fields := stmts[0].(*ast.SelectStmt).Fields.Fields - return fields[0].Expr, nil -} - -func rewritePartitionExpr(ctx sessionctx.Context, field ast.ExprNode, schema *expression.Schema, names types.NameSlice) (expression.Expression, error) { - expr, err := expression.RewriteSimpleExprWithNames(ctx, field, schema, names) - return expr, err -} - -func compareUnsigned(v1, v2 int64) int { - switch { - case uint64(v1) > uint64(v2): - return 1 - case uint64(v1) == uint64(v2): - return 0 - } - return -1 -} - -// Compare is to be used in the binary search to locate partition -func (lt *ForRangePruning) Compare(ith int, v int64, unsigned bool) int { - if ith == len(lt.LessThan)-1 { - if lt.MaxValue { - return 1 - } - } - if unsigned { - return compareUnsigned(lt.LessThan[ith], v) - } - switch { - case lt.LessThan[ith] > v: - return 1 - case lt.LessThan[ith] == v: - return 0 - } - return -1 -} diff --git a/table/tables/tables.go b/table/tables/tables.go deleted file mode 100644 index 785989846e70e..0000000000000 --- a/table/tables/tables.go +++ /dev/null @@ -1,2383 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2013 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package tables - -import ( - "context" - "fmt" - "math" - "sort" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/statistics" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/generatedexpr" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/pingcap/tidb/util/stringutil" - "github.com/pingcap/tidb/util/tableutil" - "github.com/pingcap/tidb/util/tracing" - "github.com/pingcap/tipb/go-binlog" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -// TableCommon is shared by both Table and partition. -type TableCommon struct { - // TODO: Why do we need tableID, when it is already in meta.ID ? - tableID int64 - // physicalTableID is a unique int64 to identify a physical table. - physicalTableID int64 - Columns []*table.Column - PublicColumns []*table.Column - VisibleColumns []*table.Column - HiddenColumns []*table.Column - WritableColumns []*table.Column - FullHiddenColsAndVisibleColumns []*table.Column - indices []table.Index - meta *model.TableInfo - allocs autoid.Allocators - sequence *sequenceCommon - dependencyColumnOffsets []int - Constraints []*table.Constraint - WritableConstraints []*table.Constraint - - // recordPrefix and indexPrefix are generated using physicalTableID. - recordPrefix kv.Key - indexPrefix kv.Key -} - -// MockTableFromMeta only serves for test. -func MockTableFromMeta(tblInfo *model.TableInfo) table.Table { - columns := make([]*table.Column, 0, len(tblInfo.Columns)) - for _, colInfo := range tblInfo.Columns { - col := table.ToColumn(colInfo) - columns = append(columns, col) - } - - constraints, err := table.LoadCheckConstraint(tblInfo) - if err != nil { - return nil - } - var t TableCommon - initTableCommon(&t, tblInfo, tblInfo.ID, columns, autoid.NewAllocators(false), constraints) - if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { - ret, err := newCachedTable(&t) - if err != nil { - return nil - } - return ret - } - if tblInfo.GetPartitionInfo() == nil { - if err := initTableIndices(&t); err != nil { - return nil - } - return &t - } - - ret, err := newPartitionedTable(&t, tblInfo) - if err != nil { - return nil - } - return ret -} - -// TableFromMeta creates a Table instance from model.TableInfo. -func TableFromMeta(allocs autoid.Allocators, tblInfo *model.TableInfo) (table.Table, error) { - if tblInfo.State == model.StateNone { - return nil, table.ErrTableStateCantNone.GenWithStackByArgs(tblInfo.Name) - } - - colsLen := len(tblInfo.Columns) - columns := make([]*table.Column, 0, colsLen) - for i, colInfo := range tblInfo.Columns { - if colInfo.State == model.StateNone { - return nil, table.ErrColumnStateCantNone.GenWithStackByArgs(colInfo.Name) - } - - // Print some information when the column's offset isn't equal to i. - if colInfo.Offset != i { - logutil.BgLogger().Error("wrong table schema", zap.Any("table", tblInfo), zap.Any("column", colInfo), zap.Int("index", i), zap.Int("offset", colInfo.Offset), zap.Int("columnNumber", colsLen)) - } - - col := table.ToColumn(colInfo) - if col.IsGenerated() { - expr, err := generatedexpr.ParseExpression(colInfo.GeneratedExprString) - if err != nil { - return nil, err - } - expr, err = generatedexpr.SimpleResolveName(expr, tblInfo) - if err != nil { - return nil, err - } - col.GeneratedExpr = expr - } - // default value is expr. - if col.DefaultIsExpr { - expr, err := generatedexpr.ParseExpression(colInfo.DefaultValue.(string)) - if err != nil { - return nil, err - } - col.DefaultExpr = expr - } - columns = append(columns, col) - } - - constraints, err := table.LoadCheckConstraint(tblInfo) - if err != nil { - return nil, err - } - var t TableCommon - initTableCommon(&t, tblInfo, tblInfo.ID, columns, allocs, constraints) - if tblInfo.GetPartitionInfo() == nil { - if err := initTableIndices(&t); err != nil { - return nil, err - } - if tblInfo.TableCacheStatusType != model.TableCacheStatusDisable { - return newCachedTable(&t) - } - return &t, nil - } - return newPartitionedTable(&t, tblInfo) -} - -// initTableCommon initializes a TableCommon struct. -func initTableCommon(t *TableCommon, tblInfo *model.TableInfo, physicalTableID int64, cols []*table.Column, allocs autoid.Allocators, constraints []*table.Constraint) { - t.tableID = tblInfo.ID - t.physicalTableID = physicalTableID - t.allocs = allocs - t.meta = tblInfo - t.Columns = cols - t.PublicColumns = t.Cols() - t.VisibleColumns = t.VisibleCols() - t.HiddenColumns = t.HiddenCols() - t.WritableColumns = t.WritableCols() - t.FullHiddenColsAndVisibleColumns = t.FullHiddenColsAndVisibleCols() - t.Constraints = constraints - t.WritableConstraints = t.WritableConstraint() - t.recordPrefix = tablecodec.GenTableRecordPrefix(physicalTableID) - t.indexPrefix = tablecodec.GenTableIndexPrefix(physicalTableID) - if tblInfo.IsSequence() { - t.sequence = &sequenceCommon{meta: tblInfo.Sequence} - } - for _, col := range cols { - if col.ChangeStateInfo != nil { - t.dependencyColumnOffsets = append(t.dependencyColumnOffsets, col.ChangeStateInfo.DependencyColumnOffset) - } - } -} - -// initTableIndices initializes the indices of the TableCommon. -func initTableIndices(t *TableCommon) error { - tblInfo := t.meta - for _, idxInfo := range tblInfo.Indices { - if idxInfo.State == model.StateNone { - return table.ErrIndexStateCantNone.GenWithStackByArgs(idxInfo.Name) - } - - // Use partition ID for index, because TableCommon may be table or partition. - idx := NewIndex(t.physicalTableID, tblInfo, idxInfo) - t.indices = append(t.indices, idx) - } - return nil -} - -func initTableCommonWithIndices(t *TableCommon, tblInfo *model.TableInfo, physicalTableID int64, cols []*table.Column, allocs autoid.Allocators, constraints []*table.Constraint) error { - initTableCommon(t, tblInfo, physicalTableID, cols, allocs, constraints) - return initTableIndices(t) -} - -// Indices implements table.Table Indices interface. -func (t *TableCommon) Indices() []table.Index { - return t.indices -} - -// GetWritableIndexByName gets the index meta from the table by the index name. -func GetWritableIndexByName(idxName string, t table.Table) table.Index { - for _, idx := range t.Indices() { - if !IsIndexWritable(idx) { - continue - } - if idxName == idx.Meta().Name.L { - return idx - } - } - return nil -} - -// deletableIndices implements table.Table deletableIndices interface. -func (t *TableCommon) deletableIndices() []table.Index { - // All indices are deletable because we don't need to check StateNone. - return t.indices -} - -// Meta implements table.Table Meta interface. -func (t *TableCommon) Meta() *model.TableInfo { - return t.meta -} - -// GetPhysicalID implements table.Table GetPhysicalID interface. -func (t *TableCommon) GetPhysicalID() int64 { - return t.physicalTableID -} - -// GetPartitionedTable implements table.Table GetPhysicalID interface. -func (t *TableCommon) GetPartitionedTable() table.PartitionedTable { - return nil -} - -type getColsMode int64 - -const ( - _ getColsMode = iota - visible - hidden - full -) - -func (t *TableCommon) getCols(mode getColsMode) []*table.Column { - columns := make([]*table.Column, 0, len(t.Columns)) - for _, col := range t.Columns { - if col.State != model.StatePublic { - continue - } - if (mode == visible && col.Hidden) || (mode == hidden && !col.Hidden) { - continue - } - columns = append(columns, col) - } - return columns -} - -// Cols implements table.Table Cols interface. -func (t *TableCommon) Cols() []*table.Column { - if len(t.PublicColumns) > 0 { - return t.PublicColumns - } - return t.getCols(full) -} - -// VisibleCols implements table.Table VisibleCols interface. -func (t *TableCommon) VisibleCols() []*table.Column { - if len(t.VisibleColumns) > 0 { - return t.VisibleColumns - } - return t.getCols(visible) -} - -// HiddenCols implements table.Table HiddenCols interface. -func (t *TableCommon) HiddenCols() []*table.Column { - if len(t.HiddenColumns) > 0 { - return t.HiddenColumns - } - return t.getCols(hidden) -} - -// WritableCols implements table WritableCols interface. -func (t *TableCommon) WritableCols() []*table.Column { - if len(t.WritableColumns) > 0 { - return t.WritableColumns - } - writableColumns := make([]*table.Column, 0, len(t.Columns)) - for _, col := range t.Columns { - if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { - continue - } - writableColumns = append(writableColumns, col) - } - return writableColumns -} - -// DeletableCols implements table DeletableCols interface. -func (t *TableCommon) DeletableCols() []*table.Column { - return t.Columns -} - -// WritableConstraint returns constraints of the table in writable states. -func (t *TableCommon) WritableConstraint() []*table.Constraint { - if len(t.WritableConstraints) > 0 { - return t.WritableConstraints - } - if t.Constraints == nil { - return nil - } - writeableConstraint := make([]*table.Constraint, 0, len(t.Constraints)) - for _, con := range t.Constraints { - if !con.Enforced { - continue - } - if con.State == model.StateDeleteOnly || con.State == model.StateDeleteReorganization { - continue - } - writeableConstraint = append(writeableConstraint, con) - } - return writeableConstraint -} - -// CheckRowConstraint verify row check constraints. -func (t *TableCommon) CheckRowConstraint(sctx sessionctx.Context, rowToCheck []types.Datum) error { - for _, constraint := range t.WritableConstraint() { - ok, isNull, err := constraint.ConstraintExpr.EvalInt(sctx, chunk.MutRowFromDatums(rowToCheck).ToRow()) - if err != nil { - return err - } - if ok == 0 && !isNull { - return table.ErrCheckConstraintViolated.FastGenByArgs(constraint.Name.O) - } - } - return nil -} - -// FullHiddenColsAndVisibleCols implements table FullHiddenColsAndVisibleCols interface. -func (t *TableCommon) FullHiddenColsAndVisibleCols() []*table.Column { - if len(t.FullHiddenColsAndVisibleColumns) > 0 { - return t.FullHiddenColsAndVisibleColumns - } - - cols := make([]*table.Column, 0, len(t.Columns)) - for _, col := range t.Columns { - if col.Hidden || col.State == model.StatePublic { - cols = append(cols, col) - } - } - return cols -} - -// RecordPrefix implements table.Table interface. -func (t *TableCommon) RecordPrefix() kv.Key { - return t.recordPrefix -} - -// IndexPrefix implements table.Table interface. -func (t *TableCommon) IndexPrefix() kv.Key { - return t.indexPrefix -} - -// RecordKey implements table.Table interface. -func (t *TableCommon) RecordKey(h kv.Handle) kv.Key { - return tablecodec.EncodeRecordKey(t.recordPrefix, h) -} - -// shouldAssert checks if the partition should be in consistent -// state and can have assertion. -func (t *TableCommon) shouldAssert(sctx sessionctx.Context) bool { - p := t.Meta().Partition - if p != nil { - // This disables asserting during Reorganize Partition. - switch sctx.GetSessionVars().AssertionLevel { - case variable.AssertionLevelFast: - // Fast option, just skip assertion for all partitions. - if p.DDLState != model.StateNone && p.DDLState != model.StatePublic { - return false - } - case variable.AssertionLevelStrict: - // Strict, only disable assertion for intermediate partitions. - // If there were an easy way to get from a TableCommon back to the partitioned table... - for i := range p.AddingDefinitions { - if t.physicalTableID == p.AddingDefinitions[i].ID { - return false - } - } - } - } - return true -} - -// UpdateRecord implements table.Table UpdateRecord interface. -// `touched` means which columns are really modified, used for secondary indices. -// Length of `oldData` and `newData` equals to length of `t.WritableCols()`. -func (t *TableCommon) UpdateRecord(ctx context.Context, sctx sessionctx.Context, h kv.Handle, oldData, newData []types.Datum, touched []bool) error { - txn, err := sctx.Txn(true) - if err != nil { - return err - } - - memBuffer := txn.GetMemBuffer() - sh := memBuffer.Staging() - defer memBuffer.Cleanup(sh) - - if m := t.Meta(); m.TempTableType != model.TempTableNone { - if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil { - if err := checkTempTableSize(sctx, tmpTable, m); err != nil { - return err - } - defer handleTempTableSize(tmpTable, txn.Size(), txn) - } - } - - var colIDs, binlogColIDs []int64 - var row, binlogOldRow, binlogNewRow []types.Datum - var checksums []uint32 - numColsCap := len(newData) + 1 // +1 for the extra handle column that we may need to append. - colIDs = make([]int64, 0, numColsCap) - row = make([]types.Datum, 0, numColsCap) - if shouldWriteBinlog(sctx, t.meta) { - binlogColIDs = make([]int64, 0, numColsCap) - binlogOldRow = make([]types.Datum, 0, numColsCap) - binlogNewRow = make([]types.Datum, 0, numColsCap) - } - checksumData := t.initChecksumData(sctx, h) - needChecksum := len(checksumData) > 0 - rowToCheck := make([]types.Datum, 0, numColsCap) - - for _, col := range t.Columns { - var value types.Datum - if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { - if col.ChangeStateInfo != nil { - // TODO: Check overflow or ignoreTruncate. - value, err = table.CastValue(sctx, oldData[col.DependencyColumnOffset], col.ColumnInfo, false, false) - if err != nil { - logutil.BgLogger().Info("update record cast value failed", zap.Any("col", col), zap.Uint64("txnStartTS", txn.StartTS()), - zap.String("handle", h.String()), zap.Any("val", oldData[col.DependencyColumnOffset]), zap.Error(err)) - return err - } - oldData = append(oldData, value) - touched = append(touched, touched[col.DependencyColumnOffset]) - } - if needChecksum { - if col.ChangeStateInfo != nil { - // TODO: Check overflow or ignoreTruncate. - v, err := table.CastValue(sctx, newData[col.DependencyColumnOffset], col.ColumnInfo, false, false) - if err != nil { - return err - } - checksumData = t.appendInChangeColForChecksum(sctx, h, checksumData, col.ToInfo(), &newData[col.DependencyColumnOffset], &v) - } else { - v, err := table.GetColOriginDefaultValue(sctx, col.ToInfo()) - if err != nil { - return err - } - checksumData = t.appendNonPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &v) - } - } - continue - } - if col.State != model.StatePublic { - // If col is in write only or write reorganization state we should keep the oldData. - // Because the oldData must be the original data(it's changed by other TiDBs.) or the original default value. - // TODO: Use newData directly. - value = oldData[col.Offset] - if col.ChangeStateInfo != nil { - // TODO: Check overflow or ignoreTruncate. - value, err = table.CastValue(sctx, newData[col.DependencyColumnOffset], col.ColumnInfo, false, false) - if err != nil { - return err - } - newData[col.Offset] = value - touched[col.Offset] = touched[col.DependencyColumnOffset] - checksumData = t.appendInChangeColForChecksum(sctx, h, checksumData, col.ToInfo(), &newData[col.DependencyColumnOffset], &value) - } else if needChecksum { - checksumData = t.appendNonPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &value) - } - } else { - value = newData[col.Offset] - checksumData = t.appendPublicColForChecksum(sctx, h, checksumData, col.ToInfo(), &value) - } - if !t.canSkip(col, &value) { - colIDs = append(colIDs, col.ID) - row = append(row, value) - } - rowToCheck = append(rowToCheck, value) - if shouldWriteBinlog(sctx, t.meta) && !t.canSkipUpdateBinlog(col, value) { - binlogColIDs = append(binlogColIDs, col.ID) - binlogOldRow = append(binlogOldRow, oldData[col.Offset]) - binlogNewRow = append(binlogNewRow, value) - } - } - // check data constraint - err = t.CheckRowConstraint(sctx, rowToCheck) - if err != nil { - return err - } - sessVars := sctx.GetSessionVars() - // rebuild index - if !sessVars.InTxn() { - savePresumeKeyNotExist := sessVars.PresumeKeyNotExists - if !sessVars.ConstraintCheckInPlace && sessVars.TxnCtx.IsPessimistic { - sessVars.PresumeKeyNotExists = true - } - err = t.rebuildIndices(sctx, txn, h, touched, oldData, newData, table.WithCtx(ctx)) - sessVars.PresumeKeyNotExists = savePresumeKeyNotExist - if err != nil { - return err - } - } else { - err = t.rebuildIndices(sctx, txn, h, touched, oldData, newData, table.WithCtx(ctx)) - if err != nil { - return err - } - } - - writeBufs := sessVars.GetWriteStmtBufs() - adjustRowValuesBuf(writeBufs, len(row)) - key := t.RecordKey(h) - sc, rd := sessVars.StmtCtx, &sessVars.RowEncoder - checksums, writeBufs.RowValBuf = t.calcChecksums(sctx, h, checksumData, writeBufs.RowValBuf) - writeBufs.RowValBuf, err = tablecodec.EncodeRow(sc, row, colIDs, writeBufs.RowValBuf, writeBufs.AddRowValues, rd, checksums...) - if err != nil { - return err - } - if err = memBuffer.Set(key, writeBufs.RowValBuf); err != nil { - return err - } - - failpoint.Inject("updateRecordForceAssertNotExist", func() { - // Assert the key doesn't exist while it actually exists. This is helpful to test if assertion takes effect. - // Since only the first assertion takes effect, set the injected assertion before setting the correct one to - // override it. - if sctx.GetSessionVars().ConnectionID != 0 { - logutil.BgLogger().Info("force asserting not exist on UpdateRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) - if err = txn.SetAssertion(key, kv.SetAssertNotExist); err != nil { - failpoint.Return(err) - } - } - }) - - if t.shouldAssert(sctx) { - err = txn.SetAssertion(key, kv.SetAssertExist) - } else { - err = txn.SetAssertion(key, kv.SetAssertUnknown) - } - if err != nil { - return err - } - - if err = injectMutationError(t, txn, sh); err != nil { - return err - } - if sessVars.EnableMutationChecker { - if err = CheckDataConsistency(txn, sessVars, t, newData, oldData, memBuffer, sh); err != nil { - return errors.Trace(err) - } - } - - memBuffer.Release(sh) - if shouldWriteBinlog(sctx, t.meta) { - if !t.meta.PKIsHandle && !t.meta.IsCommonHandle { - binlogColIDs = append(binlogColIDs, model.ExtraHandleID) - binlogOldRow = append(binlogOldRow, types.NewIntDatum(h.IntValue())) - binlogNewRow = append(binlogNewRow, types.NewIntDatum(h.IntValue())) - } - err = t.addUpdateBinlog(sctx, binlogOldRow, binlogNewRow, binlogColIDs) - if err != nil { - return err - } - } - colSize := make(map[int64]int64, len(t.Cols())) - for id, col := range t.Cols() { - size, err := codec.EstimateValueSize(sc, newData[id]) - if err != nil { - continue - } - newLen := size - 1 - size, err = codec.EstimateValueSize(sc, oldData[id]) - if err != nil { - continue - } - oldLen := size - 1 - colSize[col.ID] = int64(newLen - oldLen) - } - sessVars.TxnCtx.UpdateDeltaForTable(t.physicalTableID, 0, 1, colSize) - return nil -} - -func (t *TableCommon) rebuildIndices(ctx sessionctx.Context, txn kv.Transaction, h kv.Handle, touched []bool, oldData []types.Datum, newData []types.Datum, opts ...table.CreateIdxOptFunc) error { - for _, idx := range t.deletableIndices() { - if t.meta.IsCommonHandle && idx.Meta().Primary { - continue - } - for _, ic := range idx.Meta().Columns { - if !touched[ic.Offset] { - continue - } - oldVs, err := idx.FetchValues(oldData, nil) - if err != nil { - return err - } - if err = t.removeRowIndex(ctx.GetSessionVars().StmtCtx, h, oldVs, idx, txn); err != nil { - return err - } - break - } - } - for _, idx := range t.Indices() { - if !IsIndexWritable(idx) { - continue - } - if t.meta.IsCommonHandle && idx.Meta().Primary { - continue - } - untouched := true - for _, ic := range idx.Meta().Columns { - if !touched[ic.Offset] { - continue - } - untouched = false - break - } - // If txn is auto commit and index is untouched, no need to write index value. - // If InHandleForeignKeyTrigger or ForeignKeyTriggerCtx.HasFKCascades is true indicate we may have - // foreign key cascade need to handle later, then we still need to write index value, - // otherwise, the later foreign cascade executor may see data-index inconsistency in txn-mem-buffer. - sessVars := ctx.GetSessionVars() - if untouched && !sessVars.InTxn() && - !sessVars.StmtCtx.InHandleForeignKeyTrigger && !sessVars.StmtCtx.ForeignKeyTriggerCtx.HasFKCascades { - continue - } - newVs, err := idx.FetchValues(newData, nil) - if err != nil { - return err - } - if err := t.buildIndexForRow(ctx, h, newVs, newData, idx, txn, untouched, opts...); err != nil { - return err - } - } - return nil -} - -// adjustRowValuesBuf adjust writeBufs.AddRowValues length, AddRowValues stores the inserting values that is used -// by tablecodec.EncodeRow, the encoded row format is `id1, colval, id2, colval`, so the correct length is rowLen * 2. If -// the inserting row has null value, AddRecord will skip it, so the rowLen will be different, so we need to adjust it. -func adjustRowValuesBuf(writeBufs *variable.WriteStmtBufs, rowLen int) { - adjustLen := rowLen * 2 - if writeBufs.AddRowValues == nil || cap(writeBufs.AddRowValues) < adjustLen { - writeBufs.AddRowValues = make([]types.Datum, adjustLen) - } - writeBufs.AddRowValues = writeBufs.AddRowValues[:adjustLen] -} - -// FindPrimaryIndex uses to find primary index in tableInfo. -func FindPrimaryIndex(tblInfo *model.TableInfo) *model.IndexInfo { - var pkIdx *model.IndexInfo - for _, idx := range tblInfo.Indices { - if idx.Primary { - pkIdx = idx - break - } - } - return pkIdx -} - -// CommonAddRecordCtx is used in `AddRecord` to avoid memory malloc for some temp slices. -// This is useful in lightning parse row data to key-values pairs. This can gain upto 5% performance -// improvement in lightning's local mode. -type CommonAddRecordCtx struct { - colIDs []int64 - row []types.Datum -} - -// commonAddRecordKey is used as key in `sessionctx.Context.Value(key)` -type commonAddRecordKey struct{} - -// String implement `stringer.String` for CommonAddRecordKey -func (c commonAddRecordKey) String() string { - return "_common_add_record_context_key" -} - -// addRecordCtxKey is key in `sessionctx.Context` for CommonAddRecordCtx -var addRecordCtxKey = commonAddRecordKey{} - -// SetAddRecordCtx set a CommonAddRecordCtx to session context -func SetAddRecordCtx(ctx sessionctx.Context, r *CommonAddRecordCtx) { - ctx.SetValue(addRecordCtxKey, r) -} - -// ClearAddRecordCtx remove `CommonAddRecordCtx` from session context -func ClearAddRecordCtx(ctx sessionctx.Context) { - ctx.ClearValue(addRecordCtxKey) -} - -// NewCommonAddRecordCtx create a context used for `AddRecord` -func NewCommonAddRecordCtx(size int) *CommonAddRecordCtx { - return &CommonAddRecordCtx{ - colIDs: make([]int64, 0, size), - row: make([]types.Datum, 0, size), - } -} - -// TryGetCommonPkColumnIds get the IDs of primary key column if the table has common handle. -func TryGetCommonPkColumnIds(tbl *model.TableInfo) []int64 { - if !tbl.IsCommonHandle { - return nil - } - pkIdx := FindPrimaryIndex(tbl) - pkColIds := make([]int64, 0, len(pkIdx.Columns)) - for _, idxCol := range pkIdx.Columns { - pkColIds = append(pkColIds, tbl.Columns[idxCol.Offset].ID) - } - return pkColIds -} - -// PrimaryPrefixColumnIDs get prefix column ids in primary key. -func PrimaryPrefixColumnIDs(tbl *model.TableInfo) (prefixCols []int64) { - for _, idx := range tbl.Indices { - if !idx.Primary { - continue - } - for _, col := range idx.Columns { - if col.Length > 0 && tbl.Columns[col.Offset].GetFlen() > col.Length { - prefixCols = append(prefixCols, tbl.Columns[col.Offset].ID) - } - } - } - return -} - -// TryGetCommonPkColumns get the primary key columns if the table has common handle. -func TryGetCommonPkColumns(tbl table.Table) []*table.Column { - if !tbl.Meta().IsCommonHandle { - return nil - } - pkIdx := FindPrimaryIndex(tbl.Meta()) - cols := tbl.Cols() - pkCols := make([]*table.Column, 0, len(pkIdx.Columns)) - for _, idxCol := range pkIdx.Columns { - pkCols = append(pkCols, cols[idxCol.Offset]) - } - return pkCols -} - -func addTemporaryTable(sctx sessionctx.Context, tblInfo *model.TableInfo) tableutil.TempTable { - tempTable := sctx.GetSessionVars().GetTemporaryTable(tblInfo) - tempTable.SetModified(true) - return tempTable -} - -// The size of a temporary table is calculated by accumulating the transaction size delta. -func handleTempTableSize(t tableutil.TempTable, txnSizeBefore int, txn kv.Transaction) { - txnSizeNow := txn.Size() - delta := txnSizeNow - txnSizeBefore - - oldSize := t.GetSize() - newSize := oldSize + int64(delta) - t.SetSize(newSize) -} - -func checkTempTableSize(ctx sessionctx.Context, tmpTable tableutil.TempTable, tblInfo *model.TableInfo) error { - tmpTableSize := tmpTable.GetSize() - if tempTableData := ctx.GetSessionVars().TemporaryTableData; tempTableData != nil { - tmpTableSize += tempTableData.GetTableSize(tblInfo.ID) - } - - if tmpTableSize > ctx.GetSessionVars().TMPTableSize { - return table.ErrTempTableFull.GenWithStackByArgs(tblInfo.Name.O) - } - - return nil -} - -// AddRecord implements table.Table AddRecord interface. -func (t *TableCommon) AddRecord(sctx sessionctx.Context, r []types.Datum, opts ...table.AddRecordOption) (recordID kv.Handle, err error) { - txn, err := sctx.Txn(true) - if err != nil { - return nil, err - } - - var opt table.AddRecordOpt - for _, fn := range opts { - fn.ApplyOn(&opt) - } - - if m := t.Meta(); m.TempTableType != model.TempTableNone { - if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil { - if err := checkTempTableSize(sctx, tmpTable, m); err != nil { - return nil, err - } - defer handleTempTableSize(tmpTable, txn.Size(), txn) - } - } - - var ctx context.Context - if opt.Ctx != nil { - ctx = opt.Ctx - var r tracing.Region - r, ctx = tracing.StartRegionEx(ctx, "table.AddRecord") - defer r.End() - } else { - ctx = context.Background() - } - var hasRecordID bool - cols := t.Cols() - // opt.IsUpdate is a flag for update. - // If handle ID is changed when update, update will remove the old record first, and then call `AddRecord` to add a new record. - // Currently, only insert can set _tidb_rowid, update can not update _tidb_rowid. - if len(r) > len(cols) && !opt.IsUpdate { - // The last value is _tidb_rowid. - recordID = kv.IntHandle(r[len(r)-1].GetInt64()) - hasRecordID = true - } else { - tblInfo := t.Meta() - txn.CacheTableInfo(t.physicalTableID, tblInfo) - if tblInfo.PKIsHandle { - recordID = kv.IntHandle(r[tblInfo.GetPkColInfo().Offset].GetInt64()) - hasRecordID = true - } else if tblInfo.IsCommonHandle { - pkIdx := FindPrimaryIndex(tblInfo) - pkDts := make([]types.Datum, 0, len(pkIdx.Columns)) - for _, idxCol := range pkIdx.Columns { - pkDts = append(pkDts, r[idxCol.Offset]) - } - tablecodec.TruncateIndexValues(tblInfo, pkIdx, pkDts) - var handleBytes []byte - handleBytes, err = codec.EncodeKey(sctx.GetSessionVars().StmtCtx, nil, pkDts...) - if err != nil { - return - } - recordID, err = kv.NewCommonHandle(handleBytes) - if err != nil { - return - } - hasRecordID = true - } - } - if !hasRecordID { - if opt.ReserveAutoID > 0 { - // Reserve a batch of auto ID in the statement context. - // The reserved ID could be used in the future within this statement, by the - // following AddRecord() operation. - // Make the IDs continuous benefit for the performance of TiKV. - stmtCtx := sctx.GetSessionVars().StmtCtx - stmtCtx.BaseRowID, stmtCtx.MaxRowID, err = allocHandleIDs(ctx, sctx, t, uint64(opt.ReserveAutoID)) - if err != nil { - return nil, err - } - } - - recordID, err = AllocHandle(ctx, sctx, t) - if err != nil { - return nil, err - } - } - - var colIDs, binlogColIDs []int64 - var row, binlogRow []types.Datum - var checksums []uint32 - if recordCtx, ok := sctx.Value(addRecordCtxKey).(*CommonAddRecordCtx); ok { - colIDs = recordCtx.colIDs[:0] - row = recordCtx.row[:0] - } else { - colIDs = make([]int64, 0, len(r)) - row = make([]types.Datum, 0, len(r)) - } - memBuffer := txn.GetMemBuffer() - sh := memBuffer.Staging() - defer memBuffer.Cleanup(sh) - - sessVars := sctx.GetSessionVars() - checksumData := t.initChecksumData(sctx, recordID) - needChecksum := len(checksumData) > 0 - - for _, col := range t.Columns { - var value types.Datum - if col.State == model.StateDeleteOnly || col.State == model.StateDeleteReorganization { - if needChecksum { - if col.ChangeStateInfo != nil { - // TODO: Check overflow or ignoreTruncate. - v, err := table.CastValue(sctx, r[col.DependencyColumnOffset], col.ColumnInfo, false, false) - if err != nil { - return nil, err - } - checksumData = t.appendInChangeColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &r[col.DependencyColumnOffset], &v) - } else { - v, err := table.GetColOriginDefaultValue(sctx, col.ToInfo()) - if err != nil { - return nil, err - } - checksumData = t.appendNonPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &v) - } - } - continue - } - // In column type change, since we have set the origin default value for changing col, but - // for the new insert statement, we should use the casted value of relative column to insert. - if col.ChangeStateInfo != nil && col.State != model.StatePublic { - // TODO: Check overflow or ignoreTruncate. - value, err = table.CastValue(sctx, r[col.DependencyColumnOffset], col.ColumnInfo, false, false) - if err != nil { - return nil, err - } - if len(r) < len(t.WritableCols()) { - r = append(r, value) - } else { - r[col.Offset] = value - } - row = append(row, value) - colIDs = append(colIDs, col.ID) - checksumData = t.appendInChangeColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &r[col.DependencyColumnOffset], &value) - continue - } - if col.State == model.StatePublic { - value = r[col.Offset] - checksumData = t.appendPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &value) - } else { - // col.ChangeStateInfo must be nil here. - // because `col.State != model.StatePublic` is true here, if col.ChangeStateInfo is not nil, the col should - // be handle by the previous if-block. - - if opt.IsUpdate { - // If `AddRecord` is called by an update, the default value should be handled the update. - value = r[col.Offset] - } else { - // If `AddRecord` is called by an insert and the col is in write only or write reorganization state, we must - // add it with its default value. - value, err = table.GetColOriginDefaultValue(sctx, col.ToInfo()) - if err != nil { - return nil, err - } - // add value to `r` for dirty db in transaction. - // Otherwise when update will panic cause by get value of column in write only state from dirty db. - if col.Offset < len(r) { - r[col.Offset] = value - } else { - r = append(r, value) - } - } - checksumData = t.appendNonPublicColForChecksum(sctx, recordID, checksumData, col.ToInfo(), &value) - } - if !t.canSkip(col, &value) { - colIDs = append(colIDs, col.ID) - row = append(row, value) - } - } - // check data constraint - err = t.CheckRowConstraint(sctx, r) - if err != nil { - return nil, err - } - writeBufs := sessVars.GetWriteStmtBufs() - adjustRowValuesBuf(writeBufs, len(row)) - key := t.RecordKey(recordID) - logutil.BgLogger().Debug("addRecord", - zap.Stringer("key", key)) - sc, rd := sessVars.StmtCtx, &sessVars.RowEncoder - checksums, writeBufs.RowValBuf = t.calcChecksums(sctx, recordID, checksumData, writeBufs.RowValBuf) - writeBufs.RowValBuf, err = tablecodec.EncodeRow(sc, row, colIDs, writeBufs.RowValBuf, writeBufs.AddRowValues, rd, checksums...) - if err != nil { - return nil, err - } - value := writeBufs.RowValBuf - - var setPresume bool - if !sctx.GetSessionVars().StmtCtx.BatchCheck { - if t.meta.TempTableType != model.TempTableNone { - // Always check key for temporary table because it does not write to TiKV - _, err = txn.Get(ctx, key) - } else if sctx.GetSessionVars().LazyCheckKeyNotExists() { - var v []byte - v, err = txn.GetMemBuffer().Get(ctx, key) - if err != nil { - setPresume = true - } - if err == nil && len(v) == 0 { - err = kv.ErrNotExist - } - } else { - _, err = txn.Get(ctx, key) - } - if err == nil { - handleStr := getDuplicateErrorHandleString(t, recordID, r) - return recordID, kv.ErrKeyExists.FastGenByArgs(handleStr, t.Meta().Name.String()+".PRIMARY") - } else if !kv.ErrNotExist.Equal(err) { - return recordID, err - } - } - - if setPresume { - flags := []kv.FlagsOp{kv.SetPresumeKeyNotExists} - if !sessVars.ConstraintCheckInPlacePessimistic && sessVars.TxnCtx.IsPessimistic && sessVars.InTxn() && - !sessVars.InRestrictedSQL && sessVars.ConnectionID > 0 { - flags = append(flags, kv.SetNeedConstraintCheckInPrewrite) - } - err = memBuffer.SetWithFlags(key, value, flags...) - } else { - err = memBuffer.Set(key, value) - } - if err != nil { - return nil, err - } - - failpoint.Inject("addRecordForceAssertExist", func() { - // Assert the key exists while it actually doesn't. This is helpful to test if assertion takes effect. - // Since only the first assertion takes effect, set the injected assertion before setting the correct one to - // override it. - if sctx.GetSessionVars().ConnectionID != 0 { - logutil.BgLogger().Info("force asserting exist on AddRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) - if err = txn.SetAssertion(key, kv.SetAssertExist); err != nil { - failpoint.Return(nil, err) - } - } - }) - if setPresume && !txn.IsPessimistic() { - err = txn.SetAssertion(key, kv.SetAssertUnknown) - } else { - err = txn.SetAssertion(key, kv.SetAssertNotExist) - } - if err != nil { - return nil, err - } - - var createIdxOpts []table.CreateIdxOptFunc - if len(opts) > 0 { - createIdxOpts = make([]table.CreateIdxOptFunc, 0, len(opts)) - for _, fn := range opts { - if raw, ok := fn.(table.CreateIdxOptFunc); ok { - createIdxOpts = append(createIdxOpts, raw) - } - } - } - // Insert new entries into indices. - h, err := t.addIndices(sctx, recordID, r, txn, createIdxOpts) - if err != nil { - return h, err - } - - if err = injectMutationError(t, txn, sh); err != nil { - return nil, err - } - if sessVars.EnableMutationChecker { - if err = CheckDataConsistency(txn, sessVars, t, r, nil, memBuffer, sh); err != nil { - return nil, errors.Trace(err) - } - } - - memBuffer.Release(sh) - - if shouldWriteBinlog(sctx, t.meta) { - // For insert, TiDB and Binlog can use same row and schema. - binlogRow = row - binlogColIDs = colIDs - err = t.addInsertBinlog(sctx, recordID, binlogRow, binlogColIDs) - if err != nil { - return nil, err - } - } - - if sessVars.TxnCtx == nil { - return recordID, nil - } - - if shouldIncreaseTTLMetricCount(t.meta) { - sessVars.TxnCtx.InsertTTLRowsCount += 1 - } - - colSize := make(map[int64]int64, len(r)) - for id, col := range t.Cols() { - size, err := codec.EstimateValueSize(sc, r[id]) - if err != nil { - continue - } - colSize[col.ID] = int64(size) - 1 - } - sessVars.TxnCtx.UpdateDeltaForTable(t.physicalTableID, 1, 1, colSize) - return recordID, nil -} - -// genIndexKeyStr generates index content string representation. -func genIndexKeyStr(colVals []types.Datum) (string, error) { - // Pass pre-composed error to txn. - strVals := make([]string, 0, len(colVals)) - for _, cv := range colVals { - cvs := "NULL" - var err error - if !cv.IsNull() { - cvs, err = types.ToString(cv.GetValue()) - if err != nil { - return "", err - } - } - strVals = append(strVals, cvs) - } - return strings.Join(strVals, "-"), nil -} - -// addIndices adds data into indices. If any key is duplicated, returns the original handle. -func (t *TableCommon) addIndices(sctx sessionctx.Context, recordID kv.Handle, r []types.Datum, txn kv.Transaction, opts []table.CreateIdxOptFunc) (kv.Handle, error) { - writeBufs := sctx.GetSessionVars().GetWriteStmtBufs() - indexVals := writeBufs.IndexValsBuf - skipCheck := sctx.GetSessionVars().StmtCtx.BatchCheck - for _, v := range t.Indices() { - if !IsIndexWritable(v) { - continue - } - if t.meta.IsCommonHandle && v.Meta().Primary { - continue - } - indexVals, err := v.FetchValues(r, indexVals) - if err != nil { - return nil, err - } - var dupErr error - if !skipCheck && v.Meta().Unique { - entryKey, err := genIndexKeyStr(indexVals) - if err != nil { - return nil, err - } - dupErr = kv.ErrKeyExists.FastGenByArgs(entryKey, fmt.Sprintf("%s.%s", v.TableMeta().Name.String(), v.Meta().Name.String())) - } - rsData := TryGetHandleRestoredDataWrapper(t.meta, r, nil, v.Meta()) - if dupHandle, err := v.Create(sctx, txn, indexVals, recordID, rsData, opts...); err != nil { - if kv.ErrKeyExists.Equal(err) { - return dupHandle, dupErr - } - return nil, err - } - } - // save the buffer, multi rows insert can use it. - writeBufs.IndexValsBuf = indexVals - return nil, nil -} - -// RowWithCols is used to get the corresponding column datum values with the given handle. -func RowWithCols(t table.Table, ctx sessionctx.Context, h kv.Handle, cols []*table.Column) ([]types.Datum, error) { - // Get raw row data from kv. - key := tablecodec.EncodeRecordKey(t.RecordPrefix(), h) - txn, err := ctx.Txn(true) - if err != nil { - return nil, err - } - value, err := txn.Get(context.TODO(), key) - if err != nil { - return nil, err - } - v, _, err := DecodeRawRowData(ctx, t.Meta(), h, cols, value) - if err != nil { - return nil, err - } - return v, nil -} - -func containFullColInHandle(meta *model.TableInfo, col *table.Column) (containFullCol bool, idxInHandle int) { - pkIdx := FindPrimaryIndex(meta) - for i, idxCol := range pkIdx.Columns { - if meta.Columns[idxCol.Offset].ID == col.ID { - idxInHandle = i - containFullCol = idxCol.Length == types.UnspecifiedLength - return - } - } - return -} - -// DecodeRawRowData decodes raw row data into a datum slice and a (columnID:columnValue) map. -func DecodeRawRowData(ctx sessionctx.Context, meta *model.TableInfo, h kv.Handle, cols []*table.Column, - value []byte) ([]types.Datum, map[int64]types.Datum, error) { - v := make([]types.Datum, len(cols)) - colTps := make(map[int64]*types.FieldType, len(cols)) - prefixCols := make(map[int64]struct{}) - for i, col := range cols { - if col == nil { - continue - } - if col.IsPKHandleColumn(meta) { - if mysql.HasUnsignedFlag(col.GetFlag()) { - v[i].SetUint64(uint64(h.IntValue())) - } else { - v[i].SetInt64(h.IntValue()) - } - continue - } - if col.IsCommonHandleColumn(meta) && !types.NeedRestoredData(&col.FieldType) { - if containFullCol, idxInHandle := containFullColInHandle(meta, col); containFullCol { - dtBytes := h.EncodedCol(idxInHandle) - _, dt, err := codec.DecodeOne(dtBytes) - if err != nil { - return nil, nil, err - } - dt, err = tablecodec.Unflatten(dt, &col.FieldType, ctx.GetSessionVars().Location()) - if err != nil { - return nil, nil, err - } - v[i] = dt - continue - } - prefixCols[col.ID] = struct{}{} - } - colTps[col.ID] = &col.FieldType - } - rowMap, err := tablecodec.DecodeRowToDatumMap(value, colTps, ctx.GetSessionVars().Location()) - if err != nil { - return nil, rowMap, err - } - defaultVals := make([]types.Datum, len(cols)) - for i, col := range cols { - if col == nil { - continue - } - if col.IsPKHandleColumn(meta) || (col.IsCommonHandleColumn(meta) && !types.NeedRestoredData(&col.FieldType)) { - if _, isPrefix := prefixCols[col.ID]; !isPrefix { - continue - } - } - ri, ok := rowMap[col.ID] - if ok { - v[i] = ri - continue - } - if col.IsVirtualGenerated() { - continue - } - if col.ChangeStateInfo != nil { - v[i], _, err = GetChangingColVal(ctx, cols, col, rowMap, defaultVals) - } else { - v[i], err = GetColDefaultValue(ctx, col, defaultVals) - } - if err != nil { - return nil, rowMap, err - } - } - return v, rowMap, nil -} - -// GetChangingColVal gets the changing column value when executing "modify/change column" statement. -// For statement like update-where, it will fetch the old row out and insert it into kv again. -// Since update statement can see the writable columns, it is responsible for the casting relative column / get the fault value here. -// old row : a-b-[nil] -// new row : a-b-[a'/default] -// Thus the writable new row is corresponding to Write-Only constraints. -func GetChangingColVal(ctx sessionctx.Context, cols []*table.Column, col *table.Column, rowMap map[int64]types.Datum, defaultVals []types.Datum) (_ types.Datum, isDefaultVal bool, err error) { - relativeCol := cols[col.ChangeStateInfo.DependencyColumnOffset] - idxColumnVal, ok := rowMap[relativeCol.ID] - if ok { - idxColumnVal, err = table.CastValue(ctx, idxColumnVal, col.ColumnInfo, false, false) - // TODO: Consider sql_mode and the error msg(encounter this error check whether to rollback). - if err != nil { - return idxColumnVal, false, errors.Trace(err) - } - return idxColumnVal, false, nil - } - - idxColumnVal, err = GetColDefaultValue(ctx, col, defaultVals) - if err != nil { - return idxColumnVal, false, errors.Trace(err) - } - - return idxColumnVal, true, nil -} - -// RemoveRecord implements table.Table RemoveRecord interface. -func (t *TableCommon) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []types.Datum) error { - txn, err := ctx.Txn(true) - if err != nil { - return err - } - - memBuffer := txn.GetMemBuffer() - sh := memBuffer.Staging() - defer memBuffer.Cleanup(sh) - - logutil.BgLogger().Debug("RemoveRecord", - zap.Stringer("key", t.RecordKey(h))) - err = t.removeRowData(ctx, h) - if err != nil { - return err - } - - if m := t.Meta(); m.TempTableType != model.TempTableNone { - if tmpTable := addTemporaryTable(ctx, m); tmpTable != nil { - if err := checkTempTableSize(ctx, tmpTable, m); err != nil { - return err - } - defer handleTempTableSize(tmpTable, txn.Size(), txn) - } - } - - // The table has non-public column and this column is doing the operation of "modify/change column". - if len(t.Columns) > len(r) && t.Columns[len(r)].ChangeStateInfo != nil { - // The changing column datum derived from related column should be casted here. - // Otherwise, the existed changing indexes will not be deleted. - relatedColDatum := r[t.Columns[len(r)].ChangeStateInfo.DependencyColumnOffset] - value, err := table.CastValue(ctx, relatedColDatum, t.Columns[len(r)].ColumnInfo, false, false) - if err != nil { - logutil.BgLogger().Info("remove record cast value failed", zap.Any("col", t.Columns[len(r)]), - zap.String("handle", h.String()), zap.Any("val", relatedColDatum), zap.Error(err)) - return err - } - r = append(r, value) - } - err = t.removeRowIndices(ctx, h, r) - if err != nil { - return err - } - - sessVars := ctx.GetSessionVars() - sc := sessVars.StmtCtx - if err = injectMutationError(t, txn, sh); err != nil { - return err - } - if sessVars.EnableMutationChecker { - if err = CheckDataConsistency(txn, sessVars, t, nil, r, memBuffer, sh); err != nil { - return errors.Trace(err) - } - } - memBuffer.Release(sh) - - if shouldWriteBinlog(ctx, t.meta) { - cols := t.Cols() - colIDs := make([]int64, 0, len(cols)+1) - for _, col := range cols { - colIDs = append(colIDs, col.ID) - } - var binlogRow []types.Datum - if !t.meta.PKIsHandle && !t.meta.IsCommonHandle { - colIDs = append(colIDs, model.ExtraHandleID) - binlogRow = make([]types.Datum, 0, len(r)+1) - binlogRow = append(binlogRow, r...) - handleData, err := h.Data() - if err != nil { - return err - } - binlogRow = append(binlogRow, handleData...) - } else { - binlogRow = r - } - err = t.addDeleteBinlog(ctx, binlogRow, colIDs) - } - if ctx.GetSessionVars().TxnCtx == nil { - return nil - } - colSize := make(map[int64]int64, len(t.Cols())) - for id, col := range t.Cols() { - size, err := codec.EstimateValueSize(sc, r[id]) - if err != nil { - continue - } - colSize[col.ID] = -int64(size - 1) - } - ctx.GetSessionVars().TxnCtx.UpdateDeltaForTable(t.physicalTableID, -1, 1, colSize) - return err -} - -func (t *TableCommon) addInsertBinlog(ctx sessionctx.Context, h kv.Handle, row []types.Datum, colIDs []int64) error { - mutation := t.getMutation(ctx) - handleData, err := h.Data() - if err != nil { - return err - } - pk, err := codec.EncodeValue(ctx.GetSessionVars().StmtCtx, nil, handleData...) - if err != nil { - return err - } - value, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, row, colIDs, nil, nil) - if err != nil { - return err - } - bin := append(pk, value...) - mutation.InsertedRows = append(mutation.InsertedRows, bin) - mutation.Sequence = append(mutation.Sequence, binlog.MutationType_Insert) - return nil -} - -func (t *TableCommon) addUpdateBinlog(ctx sessionctx.Context, oldRow, newRow []types.Datum, colIDs []int64) error { - old, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, oldRow, colIDs, nil, nil) - if err != nil { - return err - } - newVal, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, newRow, colIDs, nil, nil) - if err != nil { - return err - } - bin := append(old, newVal...) - mutation := t.getMutation(ctx) - mutation.UpdatedRows = append(mutation.UpdatedRows, bin) - mutation.Sequence = append(mutation.Sequence, binlog.MutationType_Update) - return nil -} - -func (t *TableCommon) addDeleteBinlog(ctx sessionctx.Context, r []types.Datum, colIDs []int64) error { - data, err := tablecodec.EncodeOldRow(ctx.GetSessionVars().StmtCtx, r, colIDs, nil, nil) - if err != nil { - return err - } - mutation := t.getMutation(ctx) - mutation.DeletedRows = append(mutation.DeletedRows, data) - mutation.Sequence = append(mutation.Sequence, binlog.MutationType_DeleteRow) - return nil -} - -func writeSequenceUpdateValueBinlog(sctx sessionctx.Context, db, sequence string, end int64) error { - // 1: when sequenceCommon update the local cache passively. - // 2: When sequenceCommon setval to the allocator actively. - // Both of this two case means the upper bound the sequence has changed in meta, which need to write the binlog - // to the downstream. - // Sequence sends `select setval(seq, num)` sql string to downstream via `setDDLBinlog`, which is mocked as a DDL binlog. - binlogCli := sctx.GetSessionVars().BinlogClient - sqlMode := sctx.GetSessionVars().SQLMode - sequenceFullName := stringutil.Escape(db, sqlMode) + "." + stringutil.Escape(sequence, sqlMode) - sql := "select setval(" + sequenceFullName + ", " + strconv.FormatInt(end, 10) + ")" - - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnMeta) - err := kv.RunInNewTxn(ctx, sctx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - mockJobID, err := m.GenGlobalID() - if err != nil { - return err - } - binloginfo.SetDDLBinlog(binlogCli, txn, mockJobID, int32(model.StatePublic), sql) - return nil - }) - return err -} - -func (t *TableCommon) removeRowData(ctx sessionctx.Context, h kv.Handle) error { - // Remove row data. - txn, err := ctx.Txn(true) - if err != nil { - return err - } - - key := t.RecordKey(h) - failpoint.Inject("removeRecordForceAssertNotExist", func() { - // Assert the key doesn't exist while it actually exists. This is helpful to test if assertion takes effect. - // Since only the first assertion takes effect, set the injected assertion before setting the correct one to - // override it. - if ctx.GetSessionVars().ConnectionID != 0 { - logutil.BgLogger().Info("force asserting not exist on RemoveRecord", zap.String("category", "failpoint"), zap.Uint64("startTS", txn.StartTS())) - if err = txn.SetAssertion(key, kv.SetAssertNotExist); err != nil { - failpoint.Return(err) - } - } - }) - if t.shouldAssert(ctx) { - err = txn.SetAssertion(key, kv.SetAssertExist) - } else { - err = txn.SetAssertion(key, kv.SetAssertUnknown) - } - if err != nil { - return err - } - return txn.Delete(key) -} - -// removeRowIndices removes all the indices of a row. -func (t *TableCommon) removeRowIndices(ctx sessionctx.Context, h kv.Handle, rec []types.Datum) error { - txn, err := ctx.Txn(true) - if err != nil { - return err - } - for _, v := range t.deletableIndices() { - if v.Meta().Primary && (t.Meta().IsCommonHandle || t.Meta().PKIsHandle) { - continue - } - vals, err := v.FetchValues(rec, nil) - if err != nil { - logutil.BgLogger().Info("remove row index failed", zap.Any("index", v.Meta()), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("handle", h.String()), zap.Any("record", rec), zap.Error(err)) - return err - } - if err = v.Delete(ctx.GetSessionVars().StmtCtx, txn, vals, h); err != nil { - if v.Meta().State != model.StatePublic && kv.ErrNotExist.Equal(err) { - // If the index is not in public state, we may have not created the index, - // or already deleted the index, so skip ErrNotExist error. - logutil.BgLogger().Debug("row index not exists", zap.Any("index", v.Meta()), zap.Uint64("txnStartTS", txn.StartTS()), zap.String("handle", h.String())) - continue - } - return err - } - } - return nil -} - -// removeRowIndex implements table.Table RemoveRowIndex interface. -func (t *TableCommon) removeRowIndex(sc *stmtctx.StatementContext, h kv.Handle, vals []types.Datum, idx table.Index, txn kv.Transaction) error { - return idx.Delete(sc, txn, vals, h) -} - -// buildIndexForRow implements table.Table BuildIndexForRow interface. -func (t *TableCommon) buildIndexForRow(ctx sessionctx.Context, h kv.Handle, vals []types.Datum, newData []types.Datum, idx table.Index, txn kv.Transaction, untouched bool, popts ...table.CreateIdxOptFunc) error { - var opts []table.CreateIdxOptFunc - opts = append(opts, popts...) - if untouched { - opts = append(opts, table.IndexIsUntouched) - } - rsData := TryGetHandleRestoredDataWrapper(t.meta, newData, nil, idx.Meta()) - if _, err := idx.Create(ctx, txn, vals, h, rsData, opts...); err != nil { - if kv.ErrKeyExists.Equal(err) { - // Make error message consistent with MySQL. - entryKey, err1 := genIndexKeyStr(vals) - if err1 != nil { - // if genIndexKeyStr failed, return the original error. - return err - } - - return kv.ErrKeyExists.FastGenByArgs(entryKey, fmt.Sprintf("%s.%s", idx.TableMeta().Name.String(), idx.Meta().Name.String())) - } - return err - } - return nil -} - -// IterRecords iterates records in the table and calls fn. -func IterRecords(t table.Table, ctx sessionctx.Context, cols []*table.Column, - fn table.RecordIterFunc) error { - prefix := t.RecordPrefix() - txn, err := ctx.Txn(true) - if err != nil { - return err - } - - startKey := tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) - it, err := txn.Iter(startKey, prefix.PrefixNext()) - if err != nil { - return err - } - defer it.Close() - - if !it.Valid() { - return nil - } - - logutil.BgLogger().Debug("iterate records", zap.ByteString("startKey", startKey), zap.ByteString("key", it.Key()), zap.ByteString("value", it.Value())) - - colMap := make(map[int64]*types.FieldType, len(cols)) - for _, col := range cols { - colMap[col.ID] = &col.FieldType - } - defaultVals := make([]types.Datum, len(cols)) - for it.Valid() && it.Key().HasPrefix(prefix) { - // first kv pair is row lock information. - // TODO: check valid lock - // get row handle - handle, err := tablecodec.DecodeRowKey(it.Key()) - if err != nil { - return err - } - rowMap, err := tablecodec.DecodeRowToDatumMap(it.Value(), colMap, ctx.GetSessionVars().Location()) - if err != nil { - return err - } - pkIds, decodeLoc := TryGetCommonPkColumnIds(t.Meta()), ctx.GetSessionVars().Location() - data := make([]types.Datum, len(cols)) - for _, col := range cols { - if col.IsPKHandleColumn(t.Meta()) { - if mysql.HasUnsignedFlag(col.GetFlag()) { - data[col.Offset].SetUint64(uint64(handle.IntValue())) - } else { - data[col.Offset].SetInt64(handle.IntValue()) - } - continue - } else if mysql.HasPriKeyFlag(col.GetFlag()) { - data[col.Offset], err = tryDecodeColumnFromCommonHandle(col, handle, pkIds, decodeLoc) - if err != nil { - return err - } - continue - } - if _, ok := rowMap[col.ID]; ok { - data[col.Offset] = rowMap[col.ID] - continue - } - data[col.Offset], err = GetColDefaultValue(ctx, col, defaultVals) - if err != nil { - return err - } - } - more, err := fn(handle, data, cols) - if !more || err != nil { - return err - } - - rk := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) - err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) - if err != nil { - return err - } - } - - return nil -} - -func tryDecodeColumnFromCommonHandle(col *table.Column, handle kv.Handle, pkIds []int64, decodeLoc *time.Location) (types.Datum, error) { - for i, hid := range pkIds { - if hid != col.ID { - continue - } - _, d, err := codec.DecodeOne(handle.EncodedCol(i)) - if err != nil { - return types.Datum{}, errors.Trace(err) - } - if d, err = tablecodec.Unflatten(d, &col.FieldType, decodeLoc); err != nil { - return types.Datum{}, err - } - return d, nil - } - return types.Datum{}, nil -} - -// GetColDefaultValue gets a column default value. -// The defaultVals is used to avoid calculating the default value multiple times. -func GetColDefaultValue(ctx sessionctx.Context, col *table.Column, defaultVals []types.Datum) ( - colVal types.Datum, err error) { - if col.GetOriginDefaultValue() == nil && mysql.HasNotNullFlag(col.GetFlag()) { - return colVal, errors.New("Miss column") - } - if defaultVals[col.Offset].IsNull() { - colVal, err = table.GetColOriginDefaultValue(ctx, col.ToInfo()) - if err != nil { - return colVal, err - } - defaultVals[col.Offset] = colVal - } else { - colVal = defaultVals[col.Offset] - } - - return colVal, nil -} - -// AllocHandle allocate a new handle. -// A statement could reserve some ID in the statement context, try those ones first. -func AllocHandle(ctx context.Context, sctx sessionctx.Context, t table.Table) (kv.Handle, error) { - if sctx != nil { - if stmtCtx := sctx.GetSessionVars().StmtCtx; stmtCtx != nil { - // First try to alloc if the statement has reserved auto ID. - if stmtCtx.BaseRowID < stmtCtx.MaxRowID { - stmtCtx.BaseRowID++ - return kv.IntHandle(stmtCtx.BaseRowID), nil - } - } - } - - _, rowID, err := allocHandleIDs(ctx, sctx, t, 1) - return kv.IntHandle(rowID), err -} - -func allocHandleIDs(ctx context.Context, sctx sessionctx.Context, t table.Table, n uint64) (int64, int64, error) { - meta := t.Meta() - base, maxID, err := t.Allocators(sctx).Get(autoid.RowIDAllocType).Alloc(ctx, n, 1, 1) - if err != nil { - return 0, 0, err - } - if meta.ShardRowIDBits > 0 { - shardFmt := autoid.NewShardIDFormat(types.NewFieldType(mysql.TypeLonglong), meta.ShardRowIDBits, autoid.RowIDBitLength) - // Use max record ShardRowIDBits to check overflow. - if OverflowShardBits(maxID, meta.MaxShardRowIDBits, autoid.RowIDBitLength, true) { - // If overflow, the rowID may be duplicated. For examples, - // t.meta.ShardRowIDBits = 4 - // rowID = 0010111111111111111111111111111111111111111111111111111111111111 - // shard = 0100000000000000000000000000000000000000000000000000000000000000 - // will be duplicated with: - // rowID = 0100111111111111111111111111111111111111111111111111111111111111 - // shard = 0010000000000000000000000000000000000000000000000000000000000000 - return 0, 0, autoid.ErrAutoincReadFailed - } - shard := sctx.GetSessionVars().GetCurrentShard(int(n)) - base = shardFmt.Compose(shard, base) - maxID = shardFmt.Compose(shard, maxID) - } - return base, maxID, nil -} - -// OverflowShardBits checks whether the recordID overflow `1<<(typeBitsLength-shardRowIDBits-1) -1`. -func OverflowShardBits(recordID int64, shardRowIDBits uint64, typeBitsLength uint64, reservedSignBit bool) bool { - var signBit uint64 - if reservedSignBit { - signBit = 1 - } - mask := (1< 0 -} - -// Allocators implements table.Table Allocators interface. -func (t *TableCommon) Allocators(ctx sessionctx.Context) autoid.Allocators { - if ctx == nil { - return t.allocs - } else if ctx.GetSessionVars().IDAllocator == nil { - // Use an independent allocator for global temporary tables. - if t.meta.TempTableType == model.TempTableGlobal { - if alloc := ctx.GetSessionVars().GetTemporaryTable(t.meta).GetAutoIDAllocator(); alloc != nil { - return autoid.NewAllocators(false, alloc) - } - // If the session is not in a txn, for example, in "show create table", use the original allocator. - // Otherwise the would be a nil pointer dereference. - } - return t.allocs - } - - // Replace the row id allocator with the one in session variables. - sessAlloc := ctx.GetSessionVars().IDAllocator - allocs := t.allocs.Allocs - retAllocs := make([]autoid.Allocator, 0, len(allocs)) - copy(retAllocs, allocs) - - overwritten := false - for i, a := range retAllocs { - if a.GetType() == autoid.RowIDAllocType { - retAllocs[i] = sessAlloc - overwritten = true - break - } - } - if !overwritten { - retAllocs = append(retAllocs, sessAlloc) - } - return autoid.NewAllocators(t.allocs.SepAutoInc, retAllocs...) -} - -// Type implements table.Table Type interface. -func (t *TableCommon) Type() table.Type { - return table.NormalTable -} - -func shouldWriteBinlog(ctx sessionctx.Context, tblInfo *model.TableInfo) bool { - failpoint.Inject("forceWriteBinlog", func() { - // Just to cover binlog related code in this package, since the `BinlogClient` is - // still nil, mutations won't be written to pump on commit. - failpoint.Return(true) - }) - if ctx.GetSessionVars().BinlogClient == nil { - return false - } - if tblInfo.TempTableType != model.TempTableNone { - return false - } - return !ctx.GetSessionVars().InRestrictedSQL -} - -func shouldIncreaseTTLMetricCount(tblInfo *model.TableInfo) bool { - return tblInfo.TTLInfo != nil -} - -func (t *TableCommon) getMutation(ctx sessionctx.Context) *binlog.TableMutation { - return ctx.StmtGetMutation(t.tableID) -} - -// initChecksumData allocates data for checksum calculation, returns nil if checksum is disabled or unavailable. The -// length of returned data can be considered as the number of checksums we need to write. -func (t *TableCommon) initChecksumData(sctx sessionctx.Context, h kv.Handle) [][]rowcodec.ColData { - if !sctx.GetSessionVars().IsRowLevelChecksumEnabled() { - return nil - } - numNonPubCols := len(t.Columns) - len(t.Cols()) - if numNonPubCols > 1 { - logWithContext(sctx, logutil.BgLogger().Warn, - "skip checksum since the number of non-public columns is greater than 1", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("cols", t.meta.Columns)) - return nil - } - return make([][]rowcodec.ColData, 1+numNonPubCols) -} - -// calcChecksums calculates the checksums of input data. The arg `buf` is used to hold the temporary encoded col data -// and it will be reset for each col, so do NOT pass a buf that contains data you may use later. If the capacity of -// `buf` is enough, it gets returned directly, otherwise a new bytes with larger capacity will be returned, and you can -// hold the returned buf for later use (to avoid memory allocation). -func (t *TableCommon) calcChecksums(sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, buf []byte) ([]uint32, []byte) { - if len(data) == 0 { - return nil, buf - } - checksums := make([]uint32, len(data)) - for i, cols := range data { - row := rowcodec.RowData{Cols: cols, Data: buf} - if !sort.IsSorted(row) { - sort.Sort(row) - } - checksum, err := row.Checksum() - buf = row.Data - if err != nil { - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to encode error", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Error(err)) - return nil, buf - } - checksums[i] = checksum - } - return checksums, buf -} - -// appendPublicColForChecksum appends a public column data for checksum. If the column is in changing, that is, it's the -// old column of an on-going modify-column ddl, then skip it since it will be handle by `appendInChangeColForChecksum`. -func (t *TableCommon) appendPublicColForChecksum( - sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, d *types.Datum, -) [][]rowcodec.ColData { - if len(data) == 0 { // no need for checksum - return nil - } - if c.State != model.StatePublic { // assert col is public - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to inconsistent column state", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) - return nil - } - for _, offset := range t.dependencyColumnOffsets { - if c.Offset == offset { - // the col is in changing, skip it. - return data - } - } - // calculate the checksum with this col - data[0] = appendColForChecksum(data[0], t, c, d) - if len(data) > 1 { - // calculate the extra checksum with this col - data[1] = appendColForChecksum(data[1], t, c, d) - } - return data -} - -// appendNonPublicColForChecksum appends a non-public (but not in-changing) column data for checksum. Two checksums are -// required because there is a non-public column. The first checksum should be calculate with the original (or default) -// value of this column. The extra checksum shall be calculated without this non-public column, thus nothing to do with -// data[1]. -func (t *TableCommon) appendNonPublicColForChecksum( - sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, d *types.Datum, -) [][]rowcodec.ColData { - if size := len(data); size == 0 { // no need for checksum - return nil - } else if size == 1 { // assert that 2 checksums are required - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to inconsistent length of column data", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID)) - return nil - } - if c.State == model.StatePublic || c.ChangeStateInfo != nil { // assert col is not public and is not in changing - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to inconsistent column state", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) - return nil - } - data[0] = appendColForChecksum(data[0], t, c, d) - - return data -} - -// appendInChangeColForChecksum appends an in-changing column data for checksum. Two checksums are required because -// there is a non-public column. The first checksum should be calculate with the old version of this column and the extra -// checksum should be calculated with the new version of column. -func (t *TableCommon) appendInChangeColForChecksum( - sctx sessionctx.Context, h kv.Handle, data [][]rowcodec.ColData, c *model.ColumnInfo, oldVal *types.Datum, newVal *types.Datum, -) [][]rowcodec.ColData { - if size := len(data); size == 0 { // no need for checksum - return nil - } else if size == 1 { // assert that 2 checksums are required - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to inconsistent length of column data", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID)) - return nil - } - if c.State == model.StatePublic || c.ChangeStateInfo == nil { // assert col is not public and is in changing - logWithContext(sctx, logutil.BgLogger().Error, - "skip checksum due to inconsistent column state", - zap.Stringer("key", t.RecordKey(h)), zap.Int64("tblID", t.meta.ID), zap.Any("col", c)) - return nil - } - // calculate the checksum with the old version of col - data[0] = appendColForChecksum(data[0], t, t.meta.Columns[c.DependencyColumnOffset], oldVal) - // calculate the extra checksum with the new version of col - data[1] = appendColForChecksum(data[1], t, c, newVal) - - return data -} - -func appendColForChecksum(dst []rowcodec.ColData, t *TableCommon, c *model.ColumnInfo, d *types.Datum) []rowcodec.ColData { - if c.IsGenerated() && !c.GeneratedStored { - return dst - } - if dst == nil { - dst = make([]rowcodec.ColData, 0, len(t.Columns)) - } - return append(dst, rowcodec.ColData{ColumnInfo: c, Datum: d}) -} - -func logWithContext(sctx sessionctx.Context, log func(msg string, fields ...zap.Field), msg string, fields ...zap.Field) { - sessVars := sctx.GetSessionVars() - ctxFields := make([]zap.Field, 0, len(fields)+2) - ctxFields = append(ctxFields, zap.Uint64("conn", sessVars.ConnectionID)) - if sessVars.TxnCtx != nil { - ctxFields = append(ctxFields, zap.Uint64("txnStartTS", sessVars.TxnCtx.StartTS)) - } - ctxFields = append(ctxFields, fields...) - log(msg, ctxFields...) -} - -func (t *TableCommon) canSkip(col *table.Column, value *types.Datum) bool { - return CanSkip(t.Meta(), col, value) -} - -// CanSkip is for these cases, we can skip the columns in encoded row: -// 1. the column is included in primary key; -// 2. the column's default value is null, and the value equals to that but has no origin default; -// 3. the column is virtual generated. -func CanSkip(info *model.TableInfo, col *table.Column, value *types.Datum) bool { - if col.IsPKHandleColumn(info) { - return true - } - if col.IsCommonHandleColumn(info) { - pkIdx := FindPrimaryIndex(info) - for _, idxCol := range pkIdx.Columns { - if info.Columns[idxCol.Offset].ID != col.ID { - continue - } - canSkip := idxCol.Length == types.UnspecifiedLength - canSkip = canSkip && !types.NeedRestoredData(&col.FieldType) - return canSkip - } - } - if col.GetDefaultValue() == nil && value.IsNull() && col.GetOriginDefaultValue() == nil { - return true - } - if col.IsVirtualGenerated() { - return true - } - return false -} - -// canSkipUpdateBinlog checks whether the column can be skipped or not. -func (t *TableCommon) canSkipUpdateBinlog(col *table.Column, value types.Datum) bool { - return col.IsVirtualGenerated() -} - -// FindIndexByColName returns a public table index containing only one column named `name`. -func FindIndexByColName(t table.Table, name string) table.Index { - for _, idx := range t.Indices() { - // only public index can be read. - if idx.Meta().State != model.StatePublic { - continue - } - - if len(idx.Meta().Columns) == 1 && strings.EqualFold(idx.Meta().Columns[0].Name.L, name) { - return idx - } - } - return nil -} - -func getDuplicateErrorHandleString(t table.Table, handle kv.Handle, row []types.Datum) string { - if handle.IsInt() { - return kv.GetDuplicateErrorHandleString(handle) - } - var pk table.Index - for _, idx := range t.Indices() { - if idx.Meta().Primary { - pk = idx - break - } - } - if pk == nil { - return kv.GetDuplicateErrorHandleString(handle) - } - var err error - str := make([]string, len(pk.Meta().Columns)) - for i, col := range pk.Meta().Columns { - str[i], err = row[col.Offset].ToString() - if err != nil { - return kv.GetDuplicateErrorHandleString(handle) - } - } - return strings.Join(str, "-") -} - -func init() { - table.TableFromMeta = TableFromMeta - table.MockTableFromMeta = MockTableFromMeta - tableutil.TempTableFromMeta = TempTableFromMeta -} - -// sequenceCommon cache the sequence value. -// `alter sequence` will invalidate the cached range. -// `setval` will recompute the start position of cached value. -type sequenceCommon struct { - meta *model.SequenceInfo - // base < end when increment > 0. - // base > end when increment < 0. - end int64 - base int64 - // round is used to count the cycle times. - round int64 - mu sync.RWMutex -} - -// GetSequenceBaseEndRound is used in test. -func (s *sequenceCommon) GetSequenceBaseEndRound() (int64, int64, int64) { - s.mu.RLock() - defer s.mu.RUnlock() - return s.base, s.end, s.round -} - -// GetSequenceNextVal implements util.SequenceTable GetSequenceNextVal interface. -// Caching the sequence value in table, we can easily be notified with the cache empty, -// and write the binlogInfo in table level rather than in allocator. -func (t *TableCommon) GetSequenceNextVal(ctx interface{}, dbName, seqName string) (nextVal int64, err error) { - seq := t.sequence - if seq == nil { - // TODO: refine the error. - return 0, errors.New("sequenceCommon is nil") - } - seq.mu.Lock() - defer seq.mu.Unlock() - - err = func() error { - // Check if need to update the cache batch from storage. - // Because seq.base is not always the last allocated value (may be set by setval()). - // So we should try to seek the next value in cache (not just add increment to seq.base). - var ( - updateCache bool - offset int64 - ok bool - ) - if seq.base == seq.end { - // There is no cache yet. - updateCache = true - } else { - // Seek the first valid value in cache. - offset = seq.getOffset() - if seq.meta.Increment > 0 { - nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.base, seq.end) - } else { - nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.end, seq.base) - } - if !ok { - updateCache = true - } - } - if !updateCache { - return nil - } - // Update batch alloc from kv storage. - sequenceAlloc, err1 := getSequenceAllocator(t.allocs) - if err1 != nil { - return err1 - } - var base, end, round int64 - base, end, round, err1 = sequenceAlloc.AllocSeqCache() - if err1 != nil { - return err1 - } - // Only update local cache when alloc succeed. - seq.base = base - seq.end = end - seq.round = round - // write sequence binlog to the pumpClient. - if ctx.(sessionctx.Context).GetSessionVars().BinlogClient != nil { - err = writeSequenceUpdateValueBinlog(ctx.(sessionctx.Context), dbName, seqName, seq.end) - if err != nil { - return err - } - } - // Seek the first valid value in new cache. - // Offset may have changed cause the round is updated. - offset = seq.getOffset() - if seq.meta.Increment > 0 { - nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.base, seq.end) - } else { - nextVal, ok = autoid.SeekToFirstSequenceValue(seq.base, seq.meta.Increment, offset, seq.end, seq.base) - } - if !ok { - return errors.New("can't find the first value in sequence cache") - } - return nil - }() - // Sequence alloc in kv store error. - if err != nil { - if err == autoid.ErrAutoincReadFailed { - return 0, table.ErrSequenceHasRunOut.GenWithStackByArgs(dbName, seqName) - } - return 0, err - } - seq.base = nextVal - return nextVal, nil -} - -// SetSequenceVal implements util.SequenceTable SetSequenceVal interface. -// The returned bool indicates the newVal is already under the base. -func (t *TableCommon) SetSequenceVal(ctx interface{}, newVal int64, dbName, seqName string) (int64, bool, error) { - seq := t.sequence - if seq == nil { - // TODO: refine the error. - return 0, false, errors.New("sequenceCommon is nil") - } - seq.mu.Lock() - defer seq.mu.Unlock() - - if seq.meta.Increment > 0 { - if newVal <= t.sequence.base { - return 0, true, nil - } - if newVal <= t.sequence.end { - t.sequence.base = newVal - return newVal, false, nil - } - } else { - if newVal >= t.sequence.base { - return 0, true, nil - } - if newVal >= t.sequence.end { - t.sequence.base = newVal - return newVal, false, nil - } - } - - // Invalid the current cache. - t.sequence.base = t.sequence.end - - // Rebase from kv storage. - sequenceAlloc, err := getSequenceAllocator(t.allocs) - if err != nil { - return 0, false, err - } - res, alreadySatisfied, err := sequenceAlloc.RebaseSeq(newVal) - if err != nil { - return 0, false, err - } - if !alreadySatisfied { - // Write sequence binlog to the pumpClient. - if ctx.(sessionctx.Context).GetSessionVars().BinlogClient != nil { - err = writeSequenceUpdateValueBinlog(ctx.(sessionctx.Context), dbName, seqName, seq.end) - if err != nil { - return 0, false, err - } - } - } - // Record the current end after setval succeed. - // Consider the following case. - // create sequence seq - // setval(seq, 100) setval(seq, 50) - // Because no cache (base, end keep 0), so the second setval won't return NULL. - t.sequence.base, t.sequence.end = newVal, newVal - return res, alreadySatisfied, nil -} - -// getOffset is used in under GetSequenceNextVal & SetSequenceVal, which mu is locked. -func (s *sequenceCommon) getOffset() int64 { - offset := s.meta.Start - if s.meta.Cycle && s.round > 0 { - if s.meta.Increment > 0 { - offset = s.meta.MinValue - } else { - offset = s.meta.MaxValue - } - } - return offset -} - -// GetSequenceID implements util.SequenceTable GetSequenceID interface. -func (t *TableCommon) GetSequenceID() int64 { - return t.tableID -} - -// GetSequenceCommon is used in test to get sequenceCommon. -func (t *TableCommon) GetSequenceCommon() *sequenceCommon { - return t.sequence -} - -// TryGetHandleRestoredDataWrapper tries to get the restored data for handle if needed. The argument can be a slice or a map. -func TryGetHandleRestoredDataWrapper(tblInfo *model.TableInfo, row []types.Datum, rowMap map[int64]types.Datum, idx *model.IndexInfo) []types.Datum { - if !collate.NewCollationEnabled() || !tblInfo.IsCommonHandle || tblInfo.CommonHandleVersion == 0 { - return nil - } - rsData := make([]types.Datum, 0, 4) - pkIdx := FindPrimaryIndex(tblInfo) - for _, pkIdxCol := range pkIdx.Columns { - pkCol := tblInfo.Columns[pkIdxCol.Offset] - if !types.NeedRestoredData(&pkCol.FieldType) { - continue - } - var datum types.Datum - if len(rowMap) > 0 { - datum = rowMap[pkCol.ID] - } else { - datum = row[pkCol.Offset] - } - TryTruncateRestoredData(&datum, pkCol, pkIdxCol, idx) - ConvertDatumToTailSpaceCount(&datum, pkCol) - rsData = append(rsData, datum) - } - return rsData -} - -// TryTruncateRestoredData tries to truncate index values. -// Says that primary key(a (8)), -// For index t(a), don't truncate the value. -// For index t(a(9)), truncate to a(9). -// For index t(a(7)), truncate to a(8). -func TryTruncateRestoredData(datum *types.Datum, pkCol *model.ColumnInfo, - pkIdxCol *model.IndexColumn, idx *model.IndexInfo) { - truncateTargetCol := pkIdxCol - for _, idxCol := range idx.Columns { - if idxCol.Offset == pkIdxCol.Offset { - truncateTargetCol = maxIndexLen(pkIdxCol, idxCol) - break - } - } - tablecodec.TruncateIndexValue(datum, truncateTargetCol, pkCol) -} - -// ConvertDatumToTailSpaceCount converts a string datum to an int datum that represents the tail space count. -func ConvertDatumToTailSpaceCount(datum *types.Datum, col *model.ColumnInfo) { - if collate.IsBinCollation(col.GetCollate()) { - *datum = types.NewIntDatum(stringutil.GetTailSpaceCount(datum.GetString())) - } -} - -func maxIndexLen(idxA, idxB *model.IndexColumn) *model.IndexColumn { - if idxA.Length == types.UnspecifiedLength { - return idxA - } - if idxB.Length == types.UnspecifiedLength { - return idxB - } - if idxA.Length > idxB.Length { - return idxA - } - return idxB -} - -func getSequenceAllocator(allocs autoid.Allocators) (autoid.Allocator, error) { - for _, alloc := range allocs.Allocs { - if alloc.GetType() == autoid.SequenceType { - return alloc, nil - } - } - // TODO: refine the error. - return nil, errors.New("sequence allocator is nil") -} - -// BuildTableScanFromInfos build tipb.TableScan with *model.TableInfo and *model.ColumnInfo. -func BuildTableScanFromInfos(tableInfo *model.TableInfo, columnInfos []*model.ColumnInfo) *tipb.TableScan { - pkColIds := TryGetCommonPkColumnIds(tableInfo) - tsExec := &tipb.TableScan{ - TableId: tableInfo.ID, - Columns: util.ColumnsToProto(columnInfos, tableInfo.PKIsHandle, false), - PrimaryColumnIds: pkColIds, - } - if tableInfo.IsCommonHandle { - tsExec.PrimaryPrefixColumnIds = PrimaryPrefixColumnIDs(tableInfo) - } - return tsExec -} - -// BuildPartitionTableScanFromInfos build tipb.PartitonTableScan with *model.TableInfo and *model.ColumnInfo. -func BuildPartitionTableScanFromInfos(tableInfo *model.TableInfo, columnInfos []*model.ColumnInfo, fastScan bool) *tipb.PartitionTableScan { - pkColIds := TryGetCommonPkColumnIds(tableInfo) - tsExec := &tipb.PartitionTableScan{ - TableId: tableInfo.ID, - Columns: util.ColumnsToProto(columnInfos, tableInfo.PKIsHandle, false), - PrimaryColumnIds: pkColIds, - IsFastScan: &fastScan, - } - if tableInfo.IsCommonHandle { - tsExec.PrimaryPrefixColumnIds = PrimaryPrefixColumnIDs(tableInfo) - } - return tsExec -} - -// SetPBColumnsDefaultValue sets the default values of tipb.ColumnInfo. -func SetPBColumnsDefaultValue(ctx sessionctx.Context, pbColumns []*tipb.ColumnInfo, columns []*model.ColumnInfo) error { - for i, c := range columns { - // For virtual columns, we set their default values to NULL so that TiKV will return NULL properly, - // They real values will be computed later. - if c.IsGenerated() && !c.GeneratedStored { - pbColumns[i].DefaultVal = []byte{codec.NilFlag} - } - if c.GetOriginDefaultValue() == nil { - continue - } - - sessVars := ctx.GetSessionVars() - originStrict := sessVars.StrictSQLMode - sessVars.StrictSQLMode = false - d, err := table.GetColOriginDefaultValue(ctx, c) - sessVars.StrictSQLMode = originStrict - if err != nil { - return err - } - - pbColumns[i].DefaultVal, err = tablecodec.EncodeValue(sessVars.StmtCtx, nil, d) - if err != nil { - return err - } - } - return nil -} - -// TemporaryTable is used to store transaction-specific or session-specific information for global / local temporary tables. -// For example, stats and autoID should have their own copies of data, instead of being shared by all sessions. -type TemporaryTable struct { - // Whether it's modified in this transaction. - modified bool - // The stats of this table. So far it's always pseudo stats. - stats *statistics.Table - // The autoID allocator of this table. - autoIDAllocator autoid.Allocator - // Table size. - size int64 - - meta *model.TableInfo -} - -// TempTableFromMeta builds a TempTable from model.TableInfo. -func TempTableFromMeta(tblInfo *model.TableInfo) tableutil.TempTable { - return &TemporaryTable{ - modified: false, - stats: statistics.PseudoTable(tblInfo, false), - autoIDAllocator: autoid.NewAllocatorFromTempTblInfo(tblInfo), - meta: tblInfo, - } -} - -// GetAutoIDAllocator is implemented from TempTable.GetAutoIDAllocator. -func (t *TemporaryTable) GetAutoIDAllocator() autoid.Allocator { - return t.autoIDAllocator -} - -// SetModified is implemented from TempTable.SetModified. -func (t *TemporaryTable) SetModified(modified bool) { - t.modified = modified -} - -// GetModified is implemented from TempTable.GetModified. -func (t *TemporaryTable) GetModified() bool { - return t.modified -} - -// GetStats is implemented from TempTable.GetStats. -func (t *TemporaryTable) GetStats() interface{} { - return t.stats -} - -// GetSize gets the table size. -func (t *TemporaryTable) GetSize() int64 { - return t.size -} - -// SetSize sets the table size. -func (t *TemporaryTable) SetSize(v int64) { - t.size = v -} - -// GetMeta gets the table meta. -func (t *TemporaryTable) GetMeta() *model.TableInfo { - return t.meta -} diff --git a/table/tables/tables_test.go b/table/tables/tables_test.go deleted file mode 100644 index 7cae233cd35d5..0000000000000 --- a/table/tables/tables_test.go +++ /dev/null @@ -1,1710 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package tables_test - -import ( - "context" - "fmt" - "math" - "sort" - "strconv" - "sync/atomic" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/rowcodec" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func firstKey(t table.Table) kv.Key { - return tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) -} - -func indexPrefix(t table.PhysicalTable) kv.Key { - return tablecodec.GenTableIndexPrefix(t.GetPhysicalID()) -} - -func seek(t table.PhysicalTable, ctx sessionctx.Context, h kv.Handle) (kv.Handle, bool, error) { - txn, err := ctx.Txn(true) - if err != nil { - return nil, false, err - } - recordPrefix := t.RecordPrefix() - seekKey := tablecodec.EncodeRowKeyWithHandle(t.GetPhysicalID(), h) - iter, err := txn.Iter(seekKey, recordPrefix.PrefixNext()) - if err != nil { - return nil, false, err - } - if !iter.Valid() || !iter.Key().HasPrefix(recordPrefix) { - // No more records in the table, skip to the end. - return nil, false, nil - } - handle, err := tablecodec.DecodeRowKey(iter.Key()) - if err != nil { - return nil, false, err - } - return handle, true, nil -} - -func TestBasic(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "CREATE TABLE test.t (a int primary key auto_increment, b varchar(255) unique)") - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - require.Greater(t, tb.Meta().ID, int64(0)) - require.Equal(t, "t", tb.Meta().Name.L) - require.NotNil(t, tb.Meta()) - require.NotNil(t, tb.Indices()) - require.NotEqual(t, "", string(firstKey(tb))) - require.NotEqual(t, "", string(indexPrefix(tb.(table.PhysicalTable)))) - require.NotEqual(t, "", string(tb.RecordPrefix())) - require.NotNil(t, tables.FindIndexByColName(tb, "b")) - - autoID, err := table.AllocAutoIncrementValue(context.Background(), tb, tk.Session()) - require.NoError(t, err) - require.Greater(t, autoID, int64(0)) - - handle, err := tables.AllocHandle(context.Background(), nil, tb) - require.NoError(t, err) - require.Greater(t, handle.IntValue(), int64(0)) - - ctx := tk.Session() - rid, err := tb.AddRecord(ctx, types.MakeDatums(1, "abc")) - require.NoError(t, err) - require.Greater(t, rid.IntValue(), int64(0)) - row, err := tables.RowWithCols(tb, ctx, rid, tb.Cols()) - require.NoError(t, err) - require.Equal(t, 2, len(row)) - require.Equal(t, int64(1), row[0].GetInt64()) - - _, err = tb.AddRecord(ctx, types.MakeDatums(1, "aba")) - require.Error(t, err) - _, err = tb.AddRecord(ctx, types.MakeDatums(2, "abc")) - require.Error(t, err) - - require.Nil(t, tb.UpdateRecord(context.Background(), ctx, rid, types.MakeDatums(1, "abc"), types.MakeDatums(1, "cba"), []bool{false, true})) - - err = tables.IterRecords(tb, ctx, tb.Cols(), func(_ kv.Handle, data []types.Datum, cols []*table.Column) (bool, error) { - return true, nil - }) - require.NoError(t, err) - - indexCnt := func() int { - cnt, err1 := countEntriesWithPrefix(ctx, indexPrefix(tb.(table.PhysicalTable))) - require.Nil(t, err1) - return cnt - } - - // RowWithCols test - vals, err := tables.RowWithCols(tb, ctx, kv.IntHandle(1), tb.Cols()) - require.NoError(t, err) - require.Len(t, vals, 2) - require.Equal(t, int64(1), vals[0].GetInt64()) - cols := []*table.Column{tb.Cols()[1]} - vals, err = tables.RowWithCols(tb, ctx, kv.IntHandle(1), cols) - require.NoError(t, err) - require.Len(t, vals, 1) - require.Equal(t, []byte("cba"), vals[0].GetBytes()) - - // Make sure there is index data in the storage. - require.Greater(t, indexCnt(), 0) - require.Nil(t, tb.RemoveRecord(ctx, rid, types.MakeDatums(1, "cba"))) - // Make sure index data is also removed after tb.RemoveRecord(). - require.Equal(t, 0, indexCnt()) - _, err = tb.AddRecord(ctx, types.MakeDatums(1, "abc")) - require.NoError(t, err) - require.Greater(t, indexCnt(), 0) - handle, found, err := seek(tb.(table.PhysicalTable), ctx, kv.IntHandle(0)) - require.Equal(t, int64(1), handle.IntValue()) - require.Equal(t, true, found) - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "drop table test.t") - require.NoError(t, err) - - table.MockTableFromMeta(tb.Meta()) - alc := tb.Allocators(nil).Get(autoid.RowIDAllocType) - require.NotNil(t, alc) - - err = alc.Rebase(context.Background(), 0, false) - require.NoError(t, err) -} - -func countEntriesWithPrefix(ctx sessionctx.Context, prefix []byte) (int, error) { - cnt := 0 - txn, err := ctx.Txn(true) - if err != nil { - return 0, errors.Trace(err) - } - err = util.ScanMetaWithPrefix(txn, prefix, func(k kv.Key, v []byte) bool { - cnt++ - return true - }) - return cnt, err -} - -func TestTypes(t *testing.T) { - ctx := context.Background() - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "CREATE TABLE test.t (c1 tinyint, c2 smallint, c3 int, c4 bigint, c5 text, c6 blob, c7 varchar(64), c8 time, c9 timestamp null default CURRENT_TIMESTAMP, c10 decimal(10,1))") - require.NoError(t, err) - _, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, "insert test.t values (1, 2, 3, 4, '5', '6', '7', '10:10:10', null, 1.4)") - require.NoError(t, err) - rs, err := tk.Session().Execute(ctx, "select * from test.t where c1 = 1") - require.NoError(t, err) - req := rs[0].NewChunk(nil) - err = rs[0].Next(ctx, req) - require.NoError(t, err) - require.False(t, req.NumRows() == 0) - require.Nil(t, rs[0].Close()) - _, err = tk.Session().Execute(ctx, "drop table test.t") - require.NoError(t, err) - - _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (c1 tinyint unsigned, c2 smallint unsigned, c3 int unsigned, c4 bigint unsigned, c5 double, c6 bit(8))") - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, "insert test.t values (1, 2, 3, 4, 5, 6)") - require.NoError(t, err) - rs, err = tk.Session().Execute(ctx, "select * from test.t where c1 = 1") - require.NoError(t, err) - req = rs[0].NewChunk(nil) - err = rs[0].Next(ctx, req) - require.NoError(t, err) - require.False(t, req.NumRows() == 0) - row := req.GetRow(0) - require.Equal(t, types.NewBinaryLiteralFromUint(6, -1), types.BinaryLiteral(row.GetBytes(5))) - require.Nil(t, rs[0].Close()) - _, err = tk.Session().Execute(ctx, "drop table test.t") - require.NoError(t, err) - - _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (c1 enum('a', 'b', 'c'))") - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, "insert test.t values ('a'), (2), ('c')") - require.NoError(t, err) - rs, err = tk.Session().Execute(ctx, "select c1 + 1 from test.t where c1 = 1") - require.NoError(t, err) - req = rs[0].NewChunk(nil) - err = rs[0].Next(ctx, req) - require.NoError(t, err) - require.False(t, req.NumRows() == 0) - require.Equal(t, float64(2), req.GetRow(0).GetFloat64(0)) - require.Nil(t, rs[0].Close()) - _, err = tk.Session().Execute(ctx, "drop table test.t") - require.NoError(t, err) -} - -func TestUniqueIndexMultipleNullEntries(t *testing.T) { - ctx := context.Background() - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(ctx, "drop table if exists test.t") - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, "CREATE TABLE test.t (a int primary key auto_increment, b varchar(255) unique)") - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - require.Greater(t, tb.Meta().ID, int64(0)) - require.Equal(t, "t", tb.Meta().Name.L) - require.NotNil(t, tb.Meta()) - require.NotNil(t, tb.Indices()) - require.NotEqual(t, "", string(firstKey(tb))) - require.NotEqual(t, "", string(indexPrefix(tb.(table.PhysicalTable)))) - require.NotEqual(t, "", string(tb.RecordPrefix())) - require.NotNil(t, tables.FindIndexByColName(tb, "b")) - - handle, err := tables.AllocHandle(context.Background(), nil, tb) - require.NoError(t, err) - require.Greater(t, handle.IntValue(), int64(0)) - - autoid, err := table.AllocAutoIncrementValue(context.Background(), tb, tk.Session()) - require.NoError(t, err) - require.Greater(t, autoid, int64(0)) - - sctx := tk.Session() - require.Nil(t, sessiontxn.NewTxn(ctx, sctx)) - _, err = tb.AddRecord(sctx, types.MakeDatums(1, nil)) - require.NoError(t, err) - _, err = tb.AddRecord(sctx, types.MakeDatums(2, nil)) - require.NoError(t, err) - txn, err := sctx.Txn(true) - require.NoError(t, err) - require.Nil(t, txn.Rollback()) - _, err = tk.Session().Execute(context.Background(), "drop table test.t") - require.NoError(t, err) -} - -func TestRowKeyCodec(t *testing.T) { - tableVal := []struct { - tableID int64 - h int64 - ID int64 - }{ - {1, 1234567890, 0}, - {2, 1, 0}, - {3, -1, 0}, - {4, -1, 1}, - } - - for _, v := range tableVal { - b := tablecodec.EncodeRowKeyWithHandle(v.tableID, kv.IntHandle(v.h)) - tableID, handle, err := tablecodec.DecodeRecordKey(b) - require.NoError(t, err) - require.Equal(t, v.tableID, tableID) - require.Equal(t, v.h, handle.IntValue()) - - handle, err = tablecodec.DecodeRowKey(b) - require.NoError(t, err) - require.Equal(t, v.h, handle.IntValue()) - } - - // test error - tbl := []string{ - "", - "x", - "t1", - "t12345678", - "t12345678_i", - "t12345678_r1", - "t12345678_r1234567", - } - - for _, v := range tbl { - _, err := tablecodec.DecodeRowKey(kv.Key(v)) - require.Error(t, err) - } -} - -func TestUnsignedPK(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tPK") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tPK (a bigint unsigned primary key, b varchar(255))") - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tPK")) - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(1, "abc")) - require.NoError(t, err) - pt := tb.(table.PhysicalTable) - row, err := tables.RowWithCols(pt, tk.Session(), rid, tb.Cols()) - require.NoError(t, err) - require.Equal(t, 2, len(row)) - require.Equal(t, types.KindUint64, row[0].Kind()) - tk.Session().StmtCommit(context.Background()) - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.Nil(t, txn.Commit(context.Background())) -} - -func TestIterRecords(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tIter") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tIter (a int primary key, b int)") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "INSERT test.tIter VALUES (-1, 2), (2, NULL)") - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tIter")) - require.NoError(t, err) - totalCount := 0 - err = tables.IterRecords(tb, tk.Session(), tb.Cols(), func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { - totalCount++ - require.False(t, rec[0].IsNull()) - return true, nil - }) - require.NoError(t, err) - require.Equal(t, 2, totalCount) - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.Nil(t, txn.Commit(context.Background())) -} - -func TestTableFromMeta(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("CREATE TABLE meta (a int primary key auto_increment, b varchar(255) unique)") - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - _, err := tk.Session().Txn(true) - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("meta")) - require.NoError(t, err) - tbInfo := tb.Meta().Clone() - - // For test coverage - tbInfo.Columns[0].GeneratedExprString = "a" - _, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) - require.NoError(t, err) - - tbInfo.Columns[0].GeneratedExprString = "test" - _, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) - require.Error(t, err) - tbInfo.Columns[0].State = model.StateNone - tb, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) - require.Nil(t, tb) - require.Error(t, err) - tbInfo.State = model.StateNone - tb, err = tables.TableFromMeta(autoid.NewAllocators(false), tbInfo) - require.Nil(t, tb) - require.Error(t, err) - - tk.MustExec(`create table t_mock (id int) partition by range (id) (partition p0 values less than maxvalue)`) - tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t_mock")) - require.NoError(t, err) - tt := table.MockTableFromMeta(tb.Meta()) - _, ok := tt.(table.PartitionedTable) - require.True(t, ok) - tk.MustExec("drop table t_mock") - require.Equal(t, table.NormalTable, tt.Type()) - - tk.MustExec("create table t_meta (a int) shard_row_id_bits = 15") - tb, err = domain.GetDomain(tk.Session()).InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t_meta")) - require.NoError(t, err) - _, err = tables.AllocHandle(context.Background(), tk.Session(), tb) - require.NoError(t, err) - - maxID := 1<<(64-15-1) - 1 - err = tb.Allocators(tk.Session()).Get(autoid.RowIDAllocType).Rebase(context.Background(), int64(maxID), false) - require.NoError(t, err) - - _, err = tables.AllocHandle(context.Background(), tk.Session(), tb) - require.Error(t, err) -} - -func TestHiddenColumn(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("DROP DATABASE IF EXISTS test_hidden;") - tk.MustExec("CREATE DATABASE test_hidden;") - tk.MustExec("USE test_hidden;") - tk.MustExec("CREATE TABLE t (a int primary key, b int as (a+1), c int, d int as (c+1) stored, e int, f tinyint as (a+1));") - tk.MustExec("insert into t values (1, default, 3, default, 5, default);") - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test_hidden"), model.NewCIStr("t")) - require.NoError(t, err) - colInfo := tb.Meta().Columns - // Set column b, d, f to hidden - colInfo[1].Hidden = true - colInfo[3].Hidden = true - colInfo[5].Hidden = true - tc := tb.(*tables.TableCommon) - // Reset related caches - tc.VisibleColumns = nil - tc.WritableColumns = nil - tc.HiddenColumns = nil - tc.FullHiddenColsAndVisibleColumns = nil - - // Basic test - cols := tb.VisibleCols() - require.NotNil(t, table.FindCol(cols, "a")) - require.Nil(t, table.FindCol(cols, "b")) - require.NotNil(t, table.FindCol(cols, "c")) - require.Nil(t, table.FindCol(cols, "d")) - require.NotNil(t, table.FindCol(cols, "e")) - hiddenCols := tb.HiddenCols() - require.Nil(t, table.FindCol(hiddenCols, "a")) - require.NotNil(t, table.FindCol(hiddenCols, "b")) - require.Nil(t, table.FindCol(hiddenCols, "c")) - require.NotNil(t, table.FindCol(hiddenCols, "d")) - require.Nil(t, table.FindCol(hiddenCols, "e")) - colInfo[1].State = model.StateDeleteOnly - colInfo[2].State = model.StateDeleteOnly - fullHiddenColsAndVisibleColumns := tb.FullHiddenColsAndVisibleCols() - require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "a")) - require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "b")) - require.Nil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "c")) - require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "d")) - require.NotNil(t, table.FindCol(fullHiddenColsAndVisibleColumns, "e")) - // Reset schema states. - colInfo[1].State = model.StatePublic - colInfo[2].State = model.StatePublic - - // Test show create table - tk.MustQuery("show create table t;").Check(testkit.Rows( - "t CREATE TABLE `t` (\n" + - " `a` int(11) NOT NULL,\n" + - " `c` int(11) DEFAULT NULL,\n" + - " `e` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - // Test show (extended) columns - tk.MustQuery("show columns from t").Check(testkit.RowsWithSep("|", - "a|int(11)|NO|PRI||", - "c|int(11)|YES|||", - "e|int(11)|YES|||")) - tk.MustQuery("show extended columns from t").Check(testkit.RowsWithSep("|", - "a|int(11)|NO|PRI||", - "b|int(11)|YES|||VIRTUAL GENERATED", - "c|int(11)|YES|||", - "d|int(11)|YES|||STORED GENERATED", - "e|int(11)|YES|||", - "f|tinyint(4)|YES|||VIRTUAL GENERATED")) - - // `SELECT` statement - tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) - tk.MustQuery("select a, c, e from t;").Check(testkit.Rows("1 3 5")) - - // Can't use hidden columns in `SELECT` statement - tk.MustGetErrMsg("select b from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("select b+1 from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("select b, c from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("select a, d from t;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("select d, b from t;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("select * from t where b > 1;", "[planner:1054]Unknown column 'b' in 'where clause'") - tk.MustGetErrMsg("select * from t order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") - tk.MustGetErrMsg("select * from t group by b;", "[planner:1054]Unknown column 'b' in 'group statement'") - - // Can't use hidden columns in `INSERT` statement - // 1. insert into ... values ... - tk.MustGetErrMsg("insert into t values (1, 2, 3, 4, 5, 6);", "[planner:1136]Column count doesn't match value count at row 1") - tk.MustGetErrMsg("insert into t(b) values (2)", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t(b, c) values (2, 3);", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t(a, d) values (1, 4);", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t(d, b) values (4, 2);", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t(a) values (b);", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t(a) values (d+1);", "[planner:1054]Unknown column 'd' in 'field list'") - // 2. insert into ... set ... - tk.MustGetErrMsg("insert into t set b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t set d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t set a = b;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set a = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") - // 3. insert into ... on duplicated key update ... - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = 3, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = b;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t set a = 1 on duplicate key update c = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") - // 4. replace into ... set ... - tk.MustGetErrMsg("replace into t set a = 1, b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("replace into t set a = 1, b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("replace into t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("replace into t set a = 1, d = 4, b = 2;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("replace into t set a = 1, c = b;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("replace into t set a = 1, c = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") - // 5. insert into ... select ... - tk.MustExec("create table t1(a int, b int, c int, d int);") - tk.MustGetErrMsg("insert into t1 select b from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t1 select b+1 from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t1 select b, c from t;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("insert into t1 select a, d from t;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t1 select d, b from t;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("insert into t1 select a from t where b > 1;", "[planner:1054]Unknown column 'b' in 'where clause'") - tk.MustGetErrMsg("insert into t1 select a from t order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") - tk.MustGetErrMsg("insert into t1 select a from t group by b;", "[planner:1054]Unknown column 'b' in 'group statement'") - tk.MustExec("drop table t1") - - // `UPDATE` statement - tk.MustGetErrMsg("update t set b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("update t set b = 2, c = 3;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("update t set a = 1, d = 4;", "[planner:1054]Unknown column 'd' in 'field list'") - - // FIXME: This sql return unknown column 'd' in MySQL - tk.MustGetErrMsg("update t set d = 4, b = 2;", "[planner:1054]Unknown column 'b' in 'field list'") - - tk.MustGetErrMsg("update t set a = b;", "[planner:1054]Unknown column 'b' in 'field list'") - tk.MustGetErrMsg("update t set a = d + 1;", "[planner:1054]Unknown column 'd' in 'field list'") - tk.MustGetErrMsg("update t set a=1 where b=1;", "[planner:1054]Unknown column 'b' in 'where clause'") - tk.MustGetErrMsg("update t set a=1 where c=3 order by b;", "[planner:1054]Unknown column 'b' in 'order clause'") - - // `DELETE` statement - tk.MustExec("delete from t;") - tk.MustQuery("select count(*) from t;").Check(testkit.Rows("0")) - tk.MustExec("insert into t values (1, 3, 5);") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) - tk.MustGetErrMsg("delete from t where b = 1;", "[planner:1054]Unknown column 'b' in 'where clause'") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) - tk.MustGetErrMsg("delete from t order by d = 1;", "[planner:1054]Unknown column 'd' in 'order clause'") - tk.MustQuery("select * from t;").Check(testkit.Rows("1 3 5")) - - // `DROP COLUMN` statement - tk.MustGetErrMsg("ALTER TABLE t DROP COLUMN b;", "[ddl:1091]Can't DROP 'b'; check that column/key exists") - tk.MustQuery("show create table t;").Check(testkit.Rows( - "t CREATE TABLE `t` (\n" + - " `a` int(11) NOT NULL,\n" + - " `c` int(11) DEFAULT NULL,\n" + - " `e` int(11) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustQuery("show extended columns from t").Check(testkit.RowsWithSep("|", - "a|int(11)|NO|PRI||", - "b|int(11)|YES|||VIRTUAL GENERATED", - "c|int(11)|YES|||", - "d|int(11)|YES|||STORED GENERATED", - "e|int(11)|YES|||", - "f|tinyint(4)|YES|||VIRTUAL GENERATED")) -} - -func TestAddRecordWithCtx(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "DROP TABLE IF EXISTS test.tRecord") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "CREATE TABLE test.tRecord (a bigint unsigned primary key, b varchar(255))") - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("tRecord")) - require.NoError(t, err) - defer func() { - _, err := tk.Session().Execute(context.Background(), "DROP TABLE test.tRecord") - require.NoError(t, err) - }() - - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - _, err = tk.Session().Txn(true) - require.NoError(t, err) - recordCtx := tables.NewCommonAddRecordCtx(len(tb.Cols())) - tables.SetAddRecordCtx(tk.Session(), recordCtx) - defer tables.ClearAddRecordCtx(tk.Session()) - - records := [][]types.Datum{types.MakeDatums(uint64(1), "abc"), types.MakeDatums(uint64(2), "abcd")} - for _, r := range records { - rid, err := tb.AddRecord(tk.Session(), r) - require.NoError(t, err) - row, err := tables.RowWithCols(tb.(table.PhysicalTable), tk.Session(), rid, tb.Cols()) - require.NoError(t, err) - require.Equal(t, len(r), len(row)) - require.Equal(t, types.KindUint64, row[0].Kind()) - } - - i := 0 - err = tables.IterRecords(tb, tk.Session(), tb.Cols(), func(_ kv.Handle, rec []types.Datum, cols []*table.Column) (bool, error) { - i++ - return true, nil - }) - require.NoError(t, err) - require.Equal(t, len(records), i) - - tk.Session().StmtCommit(context.Background()) - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - require.Nil(t, txn.Commit(context.Background())) -} - -func TestConstraintCheckForUniqueIndex(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@autocommit = 1") - tk.MustExec("use test") - tk.MustExec("drop table if exists ttt") - tk.MustExec("create table ttt(id int(11) NOT NULL AUTO_INCREMENT,k int(11) NOT NULL DEFAULT '0',c char(120) NOT NULL DEFAULT '',PRIMARY KEY (id),UNIQUE KEY k_1 (k,c))") - tk.MustExec("insert into ttt(k,c) values(1, 'tidb')") - tk.MustExec("insert into ttt(k,c) values(2, 'tidb')") - _, err := tk.Exec("update ttt set k=1 where id=2") - require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) - tk.MustExec("rollback") - - // no auto-commit - tk.MustExec("set @@autocommit = 0") - tk.MustExec("set @@tidb_constraint_check_in_place = 0") - tk.MustExec("begin") - _, err = tk.Exec("update ttt set k=1 where id=2") - require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) - tk.MustExec("rollback") - - tk.MustExec("set @@tidb_constraint_check_in_place = 1") - tk.MustExec("begin") - _, err = tk.Exec("update ttt set k=1 where id=2") - require.Equal(t, "[kv:1062]Duplicate entry '1-tidb' for key 'ttt.k_1'", err.Error()) - tk.MustExec("rollback") - - // This test check that with @@tidb_constraint_check_in_place = 0, although there is not KV request for the unique index, the pessimistic lock should still be written. - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk1.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk1.MustExec("set @@tidb_constraint_check_in_place = 0") - tk2.MustExec("set @@tidb_txn_mode = 'pessimistic'") - tk1.MustExec("use test") - tk1.MustExec("begin") - tk1.MustExec("update ttt set k=3 where id=2") - - ch := make(chan int, 2) - go func() { - tk2.MustExec("use test") - _, err := tk2.Exec("insert into ttt(k,c) values(3, 'tidb')") - require.Error(t, err) - ch <- 2 - }() - // Sleep 100ms for tk2 to execute, if it's not blocked, 2 should have been sent to the channel. - time.Sleep(100 * time.Millisecond) - ch <- 1 - _, err = tk1.Exec("commit") - require.NoError(t, err) - // The data in channel is 1 means tk2 is blocked, that's the expected behavior. - require.Equal(t, 1, <-ch) - - // Unrelated to the test logic, just wait for the goroutine to exit to avoid leak in test - require.Equal(t, 2, <-ch) -} - -func TestViewColumns(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int primary key, b varchar(20))") - tk.MustExec("drop view if exists v") - tk.MustExec("create view v as select * from t") - tk.MustExec("drop view if exists va") - tk.MustExec("create view va as select count(a) from t") - testCases := []struct { - query string - expected []string - }{ - {"select data_type from INFORMATION_SCHEMA.columns where table_name = 'v'", []string{types.TypeToStr(mysql.TypeLong, ""), types.TypeToStr(mysql.TypeVarchar, "")}}, - {"select data_type from INFORMATION_SCHEMA.columns where table_name = 'va'", []string{types.TypeToStr(mysql.TypeLonglong, "")}}, - } - for _, testCase := range testCases { - tk.MustQuery(testCase.query).Check(testkit.RowsWithSep("|", testCase.expected...)) - } - tk.MustExec("drop table if exists t") - for _, testCase := range testCases { - require.Len(t, tk.MustQuery(testCase.query).Rows(), 0) - tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", - "Warning|1356|View 'test.v' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them", - "Warning|1356|View 'test.va' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them")) - } - - // For issue 43264 - tk.MustExec(`CREATE TABLE User ( - id INT PRIMARY KEY, - first_name VARCHAR(255) NOT NULL, - last_name VARCHAR(255) NULL -);`) - tk.MustExec(`CREATE VIEW Schwuser AS SELECT u.id, CONCAT(u.first_name, ' ', u.last_name) AS name FROM User u;`) - tk.MustQuery(`select DATA_TYPE from information_schema.columns where TABLE_NAME = 'Schwuser' and column_name = 'name';`).Check( - testkit.Rows("varchar")) -} - -func TestConstraintCheckForOptimisticUntouched(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists test_optimistic_untouched_flag;") - tk.MustExec(`create table test_optimistic_untouched_flag(c0 int, c1 varchar(20), c2 varchar(20), unique key uk(c0));`) - tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, null, 'green');`) - - // Insert a row with duplicated entry on the unique key, the commit should fail. - tk.MustExec("begin optimistic;") - tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, 'red', 'white');`) - tk.MustExec(`delete from test_optimistic_untouched_flag where c1 is null;`) - tk.MustExec("update test_optimistic_untouched_flag set c2 = 'green' where c2 between 'purple' and 'white';") - err := tk.ExecToErr("commit") - require.Error(t, err) - - tk.MustExec("begin optimistic;") - tk.MustExec(`insert into test_optimistic_untouched_flag(c0, c1, c2) values (1, 'red', 'white');`) - tk.MustExec("update test_optimistic_untouched_flag set c2 = 'green' where c2 between 'purple' and 'white';") - err = tk.ExecToErr("commit") - require.Error(t, err) -} - -func TestTxnAssertion(t *testing.T) { - store := testkit.CreateMockStore(t) - - se, err := session.CreateSession4Test(store) - se.SetConnectionID(1) - require.NoError(t, err) - require.NoError(t, se.Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - tk := testkit.NewTestKit(t, store) - tk.SetSession(se) - - fpAdd := "github.com/pingcap/tidb/table/tables/addRecordForceAssertExist" - fpUpdate := "github.com/pingcap/tidb/table/tables/updateRecordForceAssertNotExist" - fpRemove := "github.com/pingcap/tidb/table/tables/removeRecordForceAssertNotExist" - - runStmtInTxn := func(pessimistic bool, stmts ...string) error { - if pessimistic { - tk.MustExec("begin pessimistic") - } else { - tk.MustExec("begin optimistic") - } - for _, stmt := range stmts { - tk.MustExec(stmt) - } - return tk.ExecToErr("commit") - } - - withFailpoint := func(fp string, f func()) { - require.NoError(t, failpoint.Enable(fp, "return")) - defer func() { - require.NoError(t, failpoint.Disable(fp)) - }() - f() - } - - expectAssertionErr := func(assertionLevel string, err error) { - if assertionLevel == "STRICT" { - require.NotNil(t, err) - require.Contains(t, err.Error(), "assertion failed") - } else { - require.NoError(t, err) - } - } - - testAssertionBasicImpl := func(level string, lock bool, lockIdx bool, useCommonHandle bool) { - tk.MustExec("set @@tidb_txn_assertion_level = " + level) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - if useCommonHandle { - tk.MustExec("create table t(id varchar(64) primary key clustered, v int, v2 int, v3 int, v4 varchar(64), index(v2), unique index(v3), index(v4))") - } else { - tk.MustExec("create table t(id int primary key, v int, v2 int, v3 int, v4 varchar(64), index(v2), unique index(v3), index(v4))") - } - - var id1, id2, id3 interface{} - if useCommonHandle { - id1, id2, id3 = "1", "2", "3" - } else { - id1, id2, id3 = 1, 2, 3 - } - - // Auto commit - tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) - tk.MustExec("update t set v = v + 1 where id = ?", id1) - tk.MustExec("delete from t where id = 1") - - // Optimistic - tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) - tk.MustExec("begin optimistic") - if lock { - tk.MustExec("select * from t where id in (?, ?, ?) for update", id1, id2, id3) - } - if lockIdx { - tk.MustExec("select * from t where v3 in (1000, 2000, 3000) for update") - } - tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) - tk.MustExec("update t set v = v + 1 where id = ?", id2) - tk.MustExec("delete from t where id = ?", id3) - tk.MustExec("commit") - - // Pessimistic - tk.MustExec("delete from t") - tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) - tk.MustExec("begin pessimistic") - if lock { - tk.MustExec("select * from t where id in (?, ?, ?) for update", id1, id2, id3) - } - if lockIdx { - tk.MustExec("select * from t where v3 in (1000, 2000, 3000) for update") - } - tk.MustExec("insert into t values (?, 10, 100, 1000, '10000')", id1) - tk.MustExec("update t set v = v + 1 where id = ?", id2) - tk.MustExec("delete from t where id = ?", id3) - tk.MustExec("commit") - - // Inject incorrect assertion so that it must fail. - - // Auto commit - tk.MustExec("delete from t") - tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) - withFailpoint(fpAdd, func() { - err = tk.ExecToErr("insert into t values (?, 10, 100, 1000, '10000')", id1) - expectAssertionErr(level, err) - }) - withFailpoint(fpUpdate, func() { - err = tk.ExecToErr("update t set v = v + 1 where id = ?", id2) - expectAssertionErr(level, err) - }) - withFailpoint(fpRemove, func() { - err = tk.ExecToErr("delete from t where id = ?", id3) - expectAssertionErr(level, err) - }) - - var lockStmts []string = nil - if lock { - lockStmts = append(lockStmts, fmt.Sprintf("select * from t where id in (%#v, %#v, %#v) for update", id1, id2, id3)) - } - if lockIdx { - lockStmts = append(lockStmts, "select * from t where v3 in (1000, 2000, 3000) for update") - } - - // Optimistic - tk.MustExec("delete from t") - tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) - withFailpoint(fpAdd, func() { - err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("insert into t values (%#v, 10, 100, 1000, '10000')", id1))...) - expectAssertionErr(level, err) - }) - withFailpoint(fpUpdate, func() { - err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("update t set v = v + 1 where id = %#v", id2))...) - expectAssertionErr(level, err) - }) - withFailpoint(fpRemove, func() { - err = runStmtInTxn(false, append(lockStmts, fmt.Sprintf("delete from t where id = %#v", id3))...) - expectAssertionErr(level, err) - }) - - // Pessimistic - tk.MustExec("delete from t") - tk.MustExec("insert into t values (?, 20, 200, 2000, '20000'), (?, 30, 300, 3000, '30000')", id2, id3) - withFailpoint(fpAdd, func() { - err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("insert into t values (%#v, 10, 100, 1000, '10000')", id1))...) - expectAssertionErr(level, err) - }) - withFailpoint(fpUpdate, func() { - err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("update t set v = v + 1 where id = %#v", id2))...) - expectAssertionErr(level, err) - }) - withFailpoint(fpRemove, func() { - err = runStmtInTxn(true, append(lockStmts, fmt.Sprintf("delete from t where id = %#v", id3))...) - expectAssertionErr(level, err) - }) - } - - for _, level := range []string{"STRICT", "OFF"} { - for _, lock := range []bool{false, true} { - for _, lockIdx := range []bool{false, true} { - for _, useCommonHandle := range []bool{false, true} { - t.Logf("testing testAssertionBasicImpl level: %v, lock: %v, lockIdx: %v, useCommonHandle: %v...", level, lock, lockIdx, useCommonHandle) - testAssertionBasicImpl(level, lock, lockIdx, useCommonHandle) - } - } - } - } - - testUntouchedIndexImpl := func(level string, pessimistic bool) { - tk.MustExec("set @@tidb_txn_assertion_level = " + level) - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(id int primary key, v int, v2 int, v3 int, index(v2), unique index(v3))") - tk.MustExec("insert into t values (1, 10, 100, 1000)") - - if pessimistic { - tk.MustExec("begin pessimistic") - } else { - tk.MustExec("begin optimistic") - } - tk.MustExec("update t set v = v + 1 where id = 1") - tk.MustExec("delete from t where id = 1") - tk.MustExec("insert into t values (1, 11, 101, 1001)") - tk.MustExec("commit") - } - - testUntouchedIndexImpl("STRICT", false) - testUntouchedIndexImpl("STRICT", true) - testUntouchedIndexImpl("OFF", false) - testUntouchedIndexImpl("OFF", true) -} - -func TestWriteWithChecksums(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - h := helper.NewHelper(store.(helper.Storage)) - - tkDDL := testkit.NewTestKit(t, store) - tkDDL.MustExec("set global tidb_enable_row_level_checksum = 1") - tkDDL.MustExec("use test") - - tkDML := testkit.NewTestKit(t, store) - tkDML.MustExec("use test") - - type col struct { - ID int64 - Type byte - Data types.Datum - } - isDMLBeforeDDL := func(seq int64) bool { return seq == -1 } - isDMLAfterDDL := func(seq int64) bool { return seq == -2 } - - for _, tt := range []struct { - name string - init []string - schema []col - ddl string - dml func(seq int64, job *model.Job) ([]byte, [][]col) - }{ - { - name: "AddRecord/AddColumn", - init: []string{"create table t (id int primary key, c1 int)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "AddRecord/AddColumnWithDefault", - init: []string{"create table t (id int primary key, c1 int)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int default 42", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(42)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "AddRecord/AddColumnNotNull", - init: []string{"create table t (id int primary key, c1 int)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int not null", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - if isDMLAfterDDL(seq) { - tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) - } else { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - } - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(seq + 2)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "AddRecord/DropColumn", - init: []string{"create table t (id int primary key, c1 int, c2 int)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "AddRecord/DropColumnWithDefault", - init: []string{"create table t (id int primary key, c1 int, c2 int default 42)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(42)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - return key, [][]col{col2, col3} - }, - }, - { - name: "AddRecord/DropColumnNotNull", - init: []string{"create table t (id int primary key, c1 int, c2 int not null)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - if isDMLBeforeDDL(seq) { - tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) - } else { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - } - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(seq + 2)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - return key, [][]col{col2, col3} - }, - }, - { - name: "AddRecord/ChangeColumnType", - init: []string{"create table t (id int primary key, c1 int)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeVarchar}, - }, - ddl: "alter table t change column c1 c1 varchar(10)", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeVarchar, types.NewDatum(strconv.FormatInt(seq+1, 10))}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "AddRecord/ChangeColumnTypeFloat", - init: []string{"create table t (id int primary key, c1 float)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeFloat}, - {ID: 3, Type: mysql.TypeDouble}, - }, - ddl: "alter table t change column c1 c1 double", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - v := float64(seq) * 3.14 - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, v) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeFloat, types.NewDatum(float32(v))}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeFloat, types.NewDatum(float64(float32(v)))}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeDouble, types.NewDatum(v)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "AddRecord/ChangeColumnTypeDouble", - init: []string{"create table t (id int primary key, c1 double)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeDouble}, - {ID: 3, Type: mysql.TypeFloat}, - }, - ddl: "alter table t change column c1 c1 float", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - v := float64(seq) * 3.14 - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, v) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeDouble, types.NewDatum(v)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeFloat, types.NewDatum(float32(v))}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "AddRecord/SetColumnDefault", - init: []string{"create table t (id int primary key, c1 int, c2 int default 1)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t alter column c2 set default 42", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(1)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(42)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, nil - }, - }, - { - name: "AddRecord/DropColumnDefault", - init: []string{"create table t (id int primary key, c1 int, c2 int default 42)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t alter column c2 drop default", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - if isDMLAfterDDL(seq) { - tkDML.MustExec("insert into t (id, c1, c2) values (?, ?, ?)", seq, seq+1, seq+2) - } else { - tkDML.MustExec("insert into t (id, c1) values (?, ?)", seq, seq+1) - } - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(seq)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(42)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(seq)}, - {2, mysql.TypeLong, types.NewDatum(seq + 1)}, - {3, mysql.TypeLong, types.NewDatum(seq + 2)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, nil - }, - }, - { - name: "UpdateRecord/AddColumn", - init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "UpdateRecord/AddColumnWithDefault", - init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int default 42", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(42)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "UpdateRecord/AddColumnNotNull", - init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t add column c2 int not null", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col2, col1} - }, - }, - { - name: "UpdateRecord/DropColumn", - init: []string{"create table t (id int primary key, c1 int, c2 int)", "insert into t values (1, 0, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - if job.SchemaState == model.StateWriteOnly { - return key, [][]col{col1, col3} - } - return key, [][]col{col2, col3} - }, - }, - { - name: "UpdateRecord/DropColumnWithDefault", - init: []string{"create table t (id int primary key, c1 int, c2 int default 42)", "insert into t values (1, 0, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(nil)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - if job.SchemaState == model.StateWriteOnly { - return key, [][]col{col1, col3} - } - return key, [][]col{col2, col3} - }, - }, - { - name: "UpdateRecord/DropColumnNotNull", - init: []string{"create table t (id int primary key, c1 int, c2 int not null)", "insert into t values (1, 0, 10)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeLong}, - }, - ddl: "alter table t drop column c2", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(10)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - {3, mysql.TypeLong, types.NewDatum(0)}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - if job.SchemaState == model.StateWriteOnly { - return key, [][]col{col1, col3} - } - return key, [][]col{col2, col3} - }, - }, - { - name: "UpdateRecord/ChangeColumnType", - init: []string{"create table t (id int primary key, c1 int)", "insert into t values (1, 0)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeLong}, - {ID: 3, Type: mysql.TypeVarchar}, - }, - ddl: "alter table t change column c1 c1 varchar(10)", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - tkDML.MustExec("update t set c1 = ? where id = 1", seq) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeLong, types.NewDatum(seq)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {3, mysql.TypeVarchar, types.NewDatum(strconv.FormatInt(seq, 10))}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "UpdateRecord/ChangeColumnTypeFloat", - init: []string{"create table t (id int primary key, c1 float)", "insert into t values (1, 3.14)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeFloat}, - {ID: 3, Type: mysql.TypeDouble}, - }, - ddl: "alter table t change column c1 c1 double", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - v := float64(seq) * 3.14 - tkDML.MustExec("update t set c1 = ? where id = 1", v) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeFloat, types.NewDatum(float32(v))}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeFloat, types.NewDatum(float64(float32(v)))}, - } - col3 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {3, mysql.TypeDouble, types.NewDatum(v)}, - } - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col3} - } - return key, [][]col{col1, col2} - }, - }, - { - name: "UpdateRecord/ChangeColumnTypeDouble", - init: []string{"create table t (id int primary key, c1 double)", "insert into t values (1, 3.14)"}, - schema: []col{ - {ID: 1, Type: mysql.TypeLong}, - {ID: 2, Type: mysql.TypeDouble}, - {ID: 3, Type: mysql.TypeFloat}, - }, - ddl: "alter table t change column c1 c1 float", - dml: func(seq int64, job *model.Job) ([]byte, [][]col) { - v := float64(seq) * 3.14 - tkDML.MustExec("update t set c1 = ? where id = 1", v) - tbl := external.GetTableByName(t, tkDML, "test", "t") - key := tablecodec.EncodeRowKeyWithHandle(tbl.Meta().ID, kv.IntHandle(1)) - col1 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeDouble, types.NewDatum(v)}, - } - col2 := []col{ - {1, mysql.TypeLong, types.NewDatum(1)}, - {2, mysql.TypeFloat, types.NewDatum(float32(v))}, - } - - if isDMLBeforeDDL(seq) { - return key, [][]col{col1} - } - if isDMLAfterDDL(seq) { - return key, [][]col{col2} - } - return key, [][]col{col1, col2} - }, - }, - } { - // build row decoder for extracting checksums from row - cols := make([]rowcodec.ColInfo, len(tt.schema)) - for i, col := range tt.schema { - cols[i] = rowcodec.ColInfo{ID: col.ID, Ft: types.NewFieldType(col.Type)} - } - dec := rowcodec.NewDatumMapDecoder(cols, time.UTC) - // build a function for executing dml and validating results - doDML := func(t *testing.T, seq int64, job *model.Job) { - key, rows := tt.dml(seq, job) - // get actualChecksums in row value - actualChecksums := make([]uint32, 0, 2) - data, err := h.GetMvccByEncodedKey(key) - assert.NoError(t, err) - _, err = dec.DecodeToDatumMap(data.Info.Writes[0].ShortValue, nil) - assert.NoError(t, err) - if checksum, ok := dec.GetChecksum(); ok { - actualChecksums = append(actualChecksums, checksum) - if checksum, ok := dec.GetExtraChecksum(); ok { - actualChecksums = append(actualChecksums, checksum) - } - } - // calc expected checksums from row data - expectChecksums := make([]uint32, 0, 2) - for _, row := range rows { - cols := make([]rowcodec.ColData, len(row)) - for i := range row { - ft := types.NewFieldType(row[i].Type) - cols[i] = rowcodec.ColData{ - ColumnInfo: &model.ColumnInfo{ID: row[i].ID, FieldType: *ft}, - Datum: &row[i].Data, - } - } - data := rowcodec.RowData{Cols: cols} - sort.Sort(data) - checksum, err := data.Checksum() - assert.NoError(t, err) - expectChecksums = append(expectChecksums, checksum) - } - // validate checksums - assert.Equal(t, expectChecksums, actualChecksums) - } - - // init and run sub test - tkDDL.MustExec("drop table if exists t") - for _, sql := range tt.init { - tkDDL.MustExec(sql) - } - t.Run(tt.name, func(t *testing.T) { - origHook := dom.DDL().GetHook() - defer dom.DDL().SetHook(origHook) - - var seq int64 - fn := func(job *model.Job) { - if job.State != model.JobStateRunning { - return - } - doDML(t, atomic.AddInt64(&seq, 1), job) - } - cb := &callback.TestDDLCallback{} - cb.OnJobUpdatedExported.Store(&fn) - - dom.DDL().SetHook(cb) - - doDML(t, -1, nil) - tkDDL.MustExec(tt.ddl) - doDML(t, -2, nil) - }) - tkDDL.MustExec("admin check table t") - } -} diff --git a/table/tables/test/partition/BUILD.bazel b/table/tables/test/partition/BUILD.bazel deleted file mode 100644 index 5f55528240dc1..0000000000000 --- a/table/tables/test/partition/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "partition_test", - timeout = "long", - srcs = [ - "main_test.go", - "partition_test.go", - ], - flaky = True, - shard_count = 17, - deps = [ - "//ddl", - "//domain", - "//kv", - "//parser/model", - "//sessiontxn", - "//table", - "//table/tables", - "//testkit", - "//testkit/testsetup", - "//types", - "//util", - "//util/logutil", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - ], -) diff --git a/table/tables/test/partition/main_test.go b/table/tables/test/partition/main_test.go deleted file mode 100644 index 1eb8faa493b29..0000000000000 --- a/table/tables/test/partition/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package partition - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/table/tables/test/partition/partition_test.go b/table/tables/test/partition/partition_test.go deleted file mode 100644 index 44defb4f1a9c3..0000000000000 --- a/table/tables/test/partition/partition_test.go +++ /dev/null @@ -1,3049 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package partition - -import ( - "context" - "fmt" - "math/rand" - "strconv" - "strings" - "testing" - gotime "time" - - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -func TestPartitionAddRecord(t *testing.T) { - createTable1 := `CREATE TABLE test.t1 (id int(11), index(id)) -PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21) -)` - ctx := context.Background() - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - _, err := tk.Session().Execute(ctx, "use test") - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, "drop table if exists t1, t2;") - require.NoError(t, err) - _, err = tk.Session().Execute(ctx, createTable1) - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tbInfo := tb.Meta() - p0 := tbInfo.Partition.Definitions[0] - require.Equal(t, model.NewCIStr("p0"), p0.Name) - require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) - rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(1)) - require.NoError(t, err) - - // Check that add record writes to the partition, rather than the table. - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - val, err := txn.Get(context.TODO(), tables.PartitionRecordKey(p0.ID, rid.IntValue())) - require.NoError(t, err) - require.Greater(t, len(val), 0) - _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) - require.True(t, kv.ErrNotExist.Equal(err)) - - // Cover more code. - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(7)) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(12)) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(16)) - require.NoError(t, err) - - // Make the changes visible. - _, err = tk.Session().Execute(context.Background(), "commit") - require.NoError(t, err) - - // Check index count equals to data count. - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("4")) - tk.MustQuery("select count(*) from t1 use index(id)").Check(testkit.Rows("4")) - tk.MustQuery("select count(*) from t1 use index(id) where id > 6").Check(testkit.Rows("3")) - - // Value must locates in one partition. - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(22)) - require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) - _, err = tk.Session().Execute(context.Background(), "rollback") - require.NoError(t, err) - - createTable2 := `CREATE TABLE test.t2 (id int(11)) -PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p3 VALUES LESS THAN MAXVALUE -)` - _, err = tk.Session().Execute(context.Background(), createTable2) - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) - tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(22)) - require.NoError(t, err) - - createTable3 := `create table test.t3 (id int) partition by range (id) - ( - partition p0 values less than (10) - )` - _, err = tk.Session().Execute(context.Background(), createTable3) - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) - tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t3")) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(11)) - require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(10)) - require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(0)) - require.NoError(t, err) - - createTable4 := `create table test.t4 (a int,b int) partition by range (a+b) - ( - partition p0 values less than (10) - );` - _, err = tk.Session().Execute(context.Background(), createTable4) - require.NoError(t, err) - require.Nil(t, sessiontxn.NewTxn(ctx, tk.Session())) - tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t4")) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(1, 11)) - require.True(t, table.ErrNoPartitionForGivenValue.Equal(err)) -} - -func TestHashPartitionAddRecord(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "use test") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "drop table if exists t1;") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "set @@session.tidb_enable_table_partition = '1';") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), `CREATE TABLE test.t1 (id int(11), index(id)) PARTITION BY HASH (id) partitions 4;`) - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tbInfo := tb.Meta() - p0 := tbInfo.Partition.Definitions[0] - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - rid, err := tb.AddRecord(tk.Session(), types.MakeDatums(8)) - require.NoError(t, err) - - // Check that add record writes to the partition, rather than the table. - txn, err := tk.Session().Txn(true) - require.NoError(t, err) - val, err := txn.Get(context.TODO(), tables.PartitionRecordKey(p0.ID, rid.IntValue())) - require.NoError(t, err) - require.Greater(t, len(val), 0) - _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) - require.True(t, kv.ErrNotExist.Equal(err)) - - // Cover more code. - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(-1)) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(3)) - require.NoError(t, err) - _, err = tb.AddRecord(tk.Session(), types.MakeDatums(6)) - require.NoError(t, err) - - // Make the changes visible. - _, err = tk.Session().Execute(context.Background(), "commit") - require.NoError(t, err) - - // Check index count equals to data count. - tk.MustQuery("select count(*) from t1").Check(testkit.Rows("4")) - tk.MustQuery("select count(*) from t1 use index(id)").Check(testkit.Rows("4")) - tk.MustQuery("select count(*) from t1 use index(id) where id > 2").Check(testkit.Rows("3")) - - // Test for partition expression is negative number. - _, err = tk.Session().Execute(context.Background(), `CREATE TABLE test.t2 (id int(11), index(id)) PARTITION BY HASH (id) partitions 11;`) - require.NoError(t, err) - tb, err = dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tbInfo = tb.Meta() - for i := 0; i < 11; i++ { - require.Nil(t, sessiontxn.NewTxn(context.Background(), tk.Session())) - rid, err = tb.AddRecord(tk.Session(), types.MakeDatums(-i)) - require.NoError(t, err) - txn, err = tk.Session().Txn(true) - require.NoError(t, err) - val, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.Partition.Definitions[i].ID, rid.IntValue())) - require.NoError(t, err) - require.Greater(t, len(val), 0) - _, err = txn.Get(context.TODO(), tables.PartitionRecordKey(tbInfo.ID, rid.IntValue())) - require.True(t, kv.ErrNotExist.Equal(err)) - } - _, err = tk.Session().Execute(context.Background(), "drop table if exists t1, t2;") - require.NoError(t, err) -} - -// TestPartitionGetPhysicalID tests partition.GetPhysicalID(). -func TestPartitionGetPhysicalID(t *testing.T) { - createTable1 := `CREATE TABLE test.t1 (id int(11), index(id)) -PARTITION BY RANGE ( id ) ( - PARTITION p0 VALUES LESS THAN (6), - PARTITION p1 VALUES LESS THAN (11), - PARTITION p2 VALUES LESS THAN (16), - PARTITION p3 VALUES LESS THAN (21) -)` - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "Drop table if exists test.t1;") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), createTable1) - require.NoError(t, err) - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - tbInfo := tb.Meta() - ps := tbInfo.GetPartitionInfo() - require.NotNil(t, ps) - for _, pd := range ps.Definitions { - p := tb.(table.PartitionedTable).GetPartition(pd.ID) - require.NotNil(t, p) - require.Equal(t, p.GetPhysicalID(), pd.ID) - } -} - -func TestGeneratePartitionExpr(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - _, err := tk.Session().Execute(context.Background(), "use test") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), "drop table if exists t1;") - require.NoError(t, err) - _, err = tk.Session().Execute(context.Background(), `create table t1 (id int) - partition by range (id) ( - partition p0 values less than (4), - partition p1 values less than (7), - partition p3 values less than maxvalue)`) - require.NoError(t, err) - - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t1")) - require.NoError(t, err) - type partitionExpr interface { - PartitionExpr() *tables.PartitionExpr - } - pe := tbl.(partitionExpr).PartitionExpr() - - upperBounds := []string{ - "lt(t1.id, 4)", - "lt(t1.id, 7)", - "1", - } - for i, expr := range pe.UpperBounds { - require.Equal(t, upperBounds[i], expr.String()) - } -} - -func TestLocatePartition(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - - tk.MustExec(`CREATE TABLE t ( - id bigint(20) DEFAULT NULL, - type varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci - PARTITION BY LIST COLUMNS(type) - (PARTITION push_event VALUES IN ("PushEvent"), - PARTITION watch_event VALUES IN ("WatchEvent") - )`) - tk.MustExec(`insert into t values (1,"PushEvent"),(2,"WatchEvent"),(3, "WatchEvent")`) - tk.MustExec(`analyze table t`) - - tk1 := testkit.NewTestKit(t, store) - tk2 := testkit.NewTestKit(t, store) - tk3 := testkit.NewTestKit(t, store) - tks := []*testkit.TestKit{tk1, tk2, tk3} - - wg := util.WaitGroupWrapper{} - exec := func(tk0 *testkit.TestKit) { - tk0.MustExec("use test") - tk0.MustQuery("explain format = 'brief' select id, type from t where type = 'WatchEvent';").Check(testkit.Rows(""+ - `TableReader 2.00 root partition:watch_event data:Selection`, - `└─Selection 2.00 cop[tikv] eq(test.t.type, "WatchEvent")`, - ` └─TableFullScan 3.00 cop[tikv] table:t keep order:false`)) - } - - run := func(num int) { - tk := tks[num] - wg.Run(func() { - exec(tk) - }) - } - for i := 0; i < len(tks); i++ { - run(i) - } - wg.Wait() -} - -func TestIssue31629(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_enable_list_partition = 1") - tk.MustExec("create database Issue31629") - defer tk.MustExec("drop database Issue31629") - tk.MustExec("use Issue31629") - // Test following partition types: - // HASH, RANGE, LIST: - // - directly on a single int column - // - with expression on multiple columns - // RANGE/LIST COLUMNS single column - // RANGE/LIST COLUMNS -- Verify that only single column is allowed and no expression - tests := []struct { - create string - fail bool - cols []string - }{ - {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by range(col1) (partition p0 values less than (5),partition p1 values less than (10), partition p2 values less than maxvalue)", false, []string{"col1"}}, - {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by range(Col1+col3) (partition p0 values less than (5),partition p1 values less than (10), partition p2 values less than maxvalue)", false, []string{"Col1", "col3"}}, - {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by hash(col1) partitions 3", false, []string{"col1"}}, - {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by hash(Col1+col3) partitions 3", false, []string{"Col1", "col3"}}, - {"(col1 int, col2 varchar(60), col3 int, primary key(col1)) partition by list(col1) (partition p0 values in (5,6,7,8,9),partition p1 values in (10,11,12,13,14), partition p2 values in (20,21,22,23,24))", false, []string{"col1"}}, - {"(Col1 int, col2 varchar(60), col3 int, primary key(Col1,col3)) partition by list(Col1+col3) (partition p0 values in (5,6,7,8,9),partition p1 values in (10,11,12,13,14), partition p2 values in (20,21,22,23,24))", false, []string{"Col1", "col3"}}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col2) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, false, []string{"col2"}}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col2,col3) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, true, nil}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by range columns (col1+1) (partition p0 values less than (""),partition p1 values less than ("MID"), partition p2 values less than maxvalue)`, true, nil}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col2) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, false, []string{"col2"}}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col2,col3) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, true, nil}, - {`(col1 int, col2 varchar(60), col3 int, primary key(col2)) partition by list columns (col1+1) (partition p0 values in ("","First"),partition p1 values in ("MID","Middle"), partition p2 values in ("Last","Unknown"))`, true, nil}, - } - - for i, tt := range tests { - createTable := "create table t1 " + tt.create - res, err := tk.Exec(createTable) - if res != nil { - res.Close() - } - if err != nil { - if tt.fail { - continue - } - } - require.Falsef(t, tt.fail, "test %d succeeded but was expected to fail! %s", i, createTable) - require.NoError(t, err) - tk.MustQuery("show warnings").Check(testkit.Rows()) - - tb, err := dom.InfoSchema().TableByName(model.NewCIStr("Issue31629"), model.NewCIStr("t1")) - require.NoError(t, err) - tbp, ok := tb.(table.PartitionedTable) - require.Truef(t, ok, "test %d does not generate a table.PartitionedTable: %s (%T, %+v)", i, createTable, tb, tb) - colNames := tbp.GetPartitionColumnNames() - checkNames := []model.CIStr{model.NewCIStr(tt.cols[0])} - for i := 1; i < len(tt.cols); i++ { - checkNames = append(checkNames, model.NewCIStr(tt.cols[i])) - } - require.ElementsMatchf(t, colNames, checkNames, "test %d %s", i, createTable) - tk.MustExec("drop table t1") - } -} - -func TestExchangePartitionStates(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - dbName := "partSchemaVer" - tk.MustExec("create database " + dbName) - tk.MustExec("use " + dbName) - tk.MustExec(`set @@global.tidb_enable_metadata_lock = ON`) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + dbName) - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec("use " + dbName) - tk4 := testkit.NewTestKit(t, store) - tk4.MustExec("use " + dbName) - tk.MustExec(`create table t (a int primary key, b varchar(255), key (b))`) - tk.MustExec(`create table tp (a int primary key, b varchar(255), key (b)) partition by range (a) (partition p0 values less than (1000000), partition p1M values less than (2000000))`) - tk.MustExec(`insert into t values (1, "1")`) - tk.MustExec(`insert into tp values (2, "2")`) - tk.MustExec(`analyze table t,tp`) - tk.MustExec("BEGIN") - tk.MustQuery(`select * from t`).Check(testkit.Rows("1 1")) - tk.MustQuery(`select * from tp`).Check(testkit.Rows("2 2")) - alterChan := make(chan error) - go func() { - // WITH VALIDATION is the default - err := tk2.ExecToErr(`alter table tp exchange partition p0 with table t`) - alterChan <- err - }() - waitFor := func(tableName, s string, pos int) { - for { - select { - case alterErr := <-alterChan: - require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) - default: - // Alter still running - } - res := tk4.MustQuery(`admin show ddl jobs where db_name = '` + strings.ToLower(dbName) + `' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() - if len(res) == 1 && res[0][pos] == s { - logutil.BgLogger().Info("Got state", zap.String("State", s)) - break - } - gotime.Sleep(50 * gotime.Millisecond) - } - // Sleep 50ms to wait load InforSchema finish, issue #46815. - gotime.Sleep(50 * gotime.Millisecond) - } - waitFor("t", "write only", 4) - tk3.MustExec(`BEGIN`) - tk3.MustExec(`insert into t values (4,"4")`) - tk3.MustContainErrMsg(`insert into t values (1000004,"1000004")`, "[table:1748]Found a row not matching the given partition set") - tk.MustExec(`insert into t values (5,"5")`) - // This should fail the alter table! - tk.MustExec(`insert into t values (1000005,"1000005")`) - - // MDL will block the alter to not continue until all clients - // are in StateWriteOnly, which tk is blocking until it commits - tk.MustExec(`COMMIT`) - waitFor("t", "rollback done", 11) - // MDL will block the alter from finish, tk is in 'rollbacked' schema version - // but the alter is still waiting for tk3 to commit, before continuing - tk.MustExec("BEGIN") - tk.MustExec(`insert into t values (1000006,"1000006")`) - tk.MustExec(`insert into t values (6,"6")`) - tk3.MustExec(`insert into t values (7,"7")`) - tk3.MustContainErrMsg(`insert into t values (1000007,"1000007")`, - "[table:1748]Found a row not matching the given partition set") - tk3.MustExec("COMMIT") - require.ErrorContains(t, <-alterChan, - "[ddl:1737]Found a row that does not match the partition") - tk3.MustExec(`BEGIN`) - tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows( - "1 1", "1000005 1000005", "1000006 1000006", "5 5", "6 6")) - tk.MustQuery(`select * from tp`).Sort().Check(testkit.Rows("2 2")) - tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows( - "1 1", "1000005 1000005", "4 4", "5 5", "7 7")) - tk3.MustQuery(`select * from tp`).Sort().Check(testkit.Rows("2 2")) - tk.MustContainErrMsg(`insert into t values (7,"7")`, - "[kv:1062]Duplicate entry '7' for key 't.PRIMARY'") - tk.MustExec(`insert into t values (8,"8")`) - tk.MustExec(`insert into t values (1000008,"1000008")`) - tk.MustExec(`insert into tp values (9,"9")`) - tk.MustExec(`insert into tp values (1000009,"1000009")`) - tk3.MustExec(`insert into t values (10,"10")`) - tk3.MustExec(`insert into t values (1000010,"1000010")`) - - tk3.MustExec(`COMMIT`) - tk.MustQuery(`show create table tp`).Check(testkit.Rows("" + - "tp CREATE TABLE `tp` (\n" + - " `a` int(11) NOT NULL,\n" + - " `b` varchar(255) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - "PARTITION BY RANGE (`a`)\n" + - "(PARTITION `p0` VALUES LESS THAN (1000000),\n" + - " PARTITION `p1M` VALUES LESS THAN (2000000))")) - tk.MustQuery(`show create table t`).Check(testkit.Rows("" + - "t CREATE TABLE `t` (\n" + - " `a` int(11) NOT NULL,\n" + - " `b` varchar(255) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - tk.MustExec(`commit`) - tk.MustExec(`insert into t values (11,"11")`) - tk.MustExec(`insert into t values (1000011,"1000011")`) - tk.MustExec(`insert into tp values (12,"12")`) - tk.MustExec(`insert into tp values (1000012,"1000012")`) -} - -// Test partition and non-partition both have check constraints. -func TestExchangePartitionCheckConstraintStates(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec(`create database check_constraint`) - tk.MustExec(`set @@global.tidb_enable_check_constraint = 1`) - tk.MustExec(`use check_constraint`) - tk.MustExec(`create table nt (a int check (a > 75) not ENFORCED, b int check (b > 50) ENFORCED)`) - tk.MustExec(`create table pt (a int check (a < 75) ENFORCED, b int check (b < 75) ENFORCED) partition by range (a) (partition p0 values less than (50), partition p1 values less than (100) )`) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec(`use check_constraint`) - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec(`use check_constraint`) - tk4 := testkit.NewTestKit(t, store) - tk4.MustExec(`use check_constraint`) - // TODO: error message to check. - errMsg := "[table:3819]Check constraint" - - tk2.MustExec("begin") - // Get table mdl. - tk2.MustQuery(`select * from nt`).Check(testkit.Rows()) - tk2.MustQuery(`select * from pt`).Check(testkit.Rows()) - alterChan := make(chan error) - go func() { - err := tk3.ExecToErr(`alter table pt exchange partition p1 with table nt`) - alterChan <- err - }() - waitFor := func(tableName, s string, pos int) { - for { - select { - case alterErr := <-alterChan: - require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) - default: - // Alter still running - } - res := tk4.MustQuery(`admin show ddl jobs where db_name = 'check_constraint' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() - if len(res) == 1 && res[0][pos] == s { - logutil.BgLogger().Info("Got state", zap.String("State", s)) - break - } - gotime.Sleep(50 * gotime.Millisecond) - } - // Sleep 50ms to wait load InforSchema finish. - gotime.Sleep(50 * gotime.Millisecond) - } - waitFor("nt", "write only", 4) - - tk.MustExec(`insert into nt values (60, 60)`) - // violate pt (a < 75) - tk.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) - // violate pt (b < 75) - tk.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) - // violate pt (a < 75) - tk.MustContainErrMsg(`update nt set a = 80 where a = 60`, errMsg) - // violate pt (b < 75) - tk.MustContainErrMsg(`update nt set b = 80 where b = 60`, errMsg) - - tk.MustExec(`insert into pt values (60, 60)`) - // violate nt (b > 50) - tk.MustContainErrMsg(`insert into pt values (60, 50)`, errMsg) - // violate nt (b > 50) - tk.MustContainErrMsg(`update pt set b = 50 where b = 60`, errMsg) - // row in partition p0(less than (50)), is ok. - tk.MustExec(`insert into pt values (30, 50)`) - - tk5 := testkit.NewTestKit(t, store) - tk5.MustExec(`use check_constraint`) - tk5.MustExec("begin") - // Let tk5 get mdl of pt with the version of write-only state. - tk5.MustQuery(`select * from pt`) - - tk6 := testkit.NewTestKit(t, store) - tk6.MustExec(`use check_constraint`) - tk6.MustExec("begin") - // Let tk6 get mdl of nt with the version of write-only state. - tk6.MustQuery(`select * from nt`) - - // Release tk2 mdl, wait ddl enter next state. - tk2.MustExec("commit") - waitFor("pt", "none", 4) - - // violate nt (b > 50) - // Now tk5 handle the sql with MDL: pt version state is write-only, nt version state is none. - tk5.MustContainErrMsg(`insert into pt values (60, 50)`, errMsg) - // Verify exists row(60, 60) in pt. - tk5.MustQuery(`select * from pt where a = 60 and b = 60`).Check(testkit.Rows("60 60")) - // Update oldData and newData both in p1, violate nt (b > 50) - tk5.MustContainErrMsg(`update pt set b = 50 where a = 60 and b = 60`, errMsg) - // Verify exists row(30, 50) in pt. - tk5.MustQuery(`select * from pt where a = 30 and b = 50`).Check(testkit.Rows("30 50")) - // update oldData in p0, newData in p1, violate nt (b > 50) - tk5.MustContainErrMsg(`update pt set a = 60 where a = 30 and b = 50`, errMsg) - - // violate pt (a < 75) - tk6.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) - // violate pt (b < 75) - tk6.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) - // Verify exists row(60, 60) in nt. - tk6.MustQuery(`select * from pt where a = 60 and b = 60`).Check(testkit.Rows("60 60")) - // violate pt (a < 75) - tk6.MustContainErrMsg(`update nt set a = 80 where a = 60 and b = 60`, errMsg) - - // Let tk5, tk6 release mdl. - tk5.MustExec("commit") - tk6.MustExec("commit") - - // Wait ddl finish. - <-alterChan -} - -// Test partition table has check constraints while non-partition table do not have. -func TestExchangePartitionCheckConstraintStatesTwo(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tk.MustExec(`create database check_constraint`) - tk.MustExec(`set @@global.tidb_enable_check_constraint = 1`) - tk.MustExec(`use check_constraint`) - tk.MustExec(`create table nt (a int, b int)`) - tk.MustExec(`create table pt (a int check (a < 75) ENFORCED, b int check (b < 75) ENFORCED) partition by range (a) (partition p0 values less than (50), partition p1 values less than (100) )`) - - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec(`use check_constraint`) - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec(`use check_constraint`) - tk4 := testkit.NewTestKit(t, store) - tk4.MustExec(`use check_constraint`) - // TODO: error message to check. - errMsg := "[table:3819]Check constraint" - - tk2.MustExec("begin") - // Get table mdl. - tk2.MustQuery(`select * from nt`).Check(testkit.Rows()) - alterChan := make(chan error) - go func() { - err := tk3.ExecToErr(`alter table pt exchange partition p1 with table nt`) - alterChan <- err - }() - waitFor := func(tableName, s string, pos int) { - for { - select { - case alterErr := <-alterChan: - require.Fail(t, "Alter completed unexpectedly", "With error %v", alterErr) - default: - // Alter still running - } - res := tk4.MustQuery(`admin show ddl jobs where db_name = 'check_constraint' and table_name = '` + tableName + `' and job_type = 'exchange partition'`).Rows() - if len(res) == 1 && res[0][pos] == s { - logutil.BgLogger().Info("Got state", zap.String("State", s)) - break - } - gotime.Sleep(50 * gotime.Millisecond) - } - // Sleep 50ms to wait load InforSchema finish. - gotime.Sleep(50 * gotime.Millisecond) - } - waitFor("nt", "write only", 4) - - tk.MustExec(`insert into nt values (60, 60)`) - // violate pt (a < 75) - tk.MustContainErrMsg(`insert into nt values (80, 60)`, errMsg) - // violate pt (b < 75) - tk.MustContainErrMsg(`insert into nt values (60, 80)`, errMsg) - // violate pt (a < 75) - tk.MustContainErrMsg(`update nt set a = 80 where a = 60`, errMsg) - // violate pt (b < 75) - tk.MustContainErrMsg(`update nt set b = 80 where b = 60`, errMsg) - - tk.MustExec(`insert into pt values (60, 60)`) - tk.MustExec(`insert into pt values (60, 50)`) - tk.MustExec(`update pt set b = 50 where b = 60`) - // row in partition p0(less than (50)), is ok. - tk.MustExec(`insert into pt values (30, 50)`) - - // Release tk2 mdl. - tk2.MustExec("commit") - // Wait ddl finish. - <-alterChan -} - -func TestAddKeyPartitionStates(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - dbName := "partSchemaVer" - tk.MustExec("create database " + dbName) - tk.MustExec("use " + dbName) - tk.MustExec(`set @@global.tidb_enable_metadata_lock = ON`) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + dbName) - tk3 := testkit.NewTestKit(t, store) - tk3.MustExec("use " + dbName) - tk4 := testkit.NewTestKit(t, store) - tk4.MustExec("use " + dbName) - tk.MustExec(`create table t (a int primary key, b varchar(255), key (b)) partition by hash (a) partitions 3`) - tk.MustExec(`insert into t values (1, "1")`) - tk.MustExec(`analyze table t`) - tk.MustExec("BEGIN") - tk.MustQuery(`select * from t`).Check(testkit.Rows("1 1")) - tk.MustExec(`insert into t values (2, "2")`) - syncChan := make(chan bool) - go func() { - tk2.MustExec(`alter table t add partition partitions 1`) - syncChan <- true - }() - waitFor := func(i int, s string) { - for { - res := tk4.MustQuery(`admin show ddl jobs where db_name = '` + strings.ToLower(dbName) + `' and table_name = 't' and job_type like 'alter table%'`).Rows() - if len(res) == 1 && res[0][i] == s { - break - } - gotime.Sleep(10 * gotime.Millisecond) - } - // Sleep 50ms to wait load InforSchema finish. - gotime.Sleep(50 * gotime.Millisecond) - } - waitFor(4, "delete only") - tk3.MustExec(`BEGIN`) - tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1")) - tk3.MustExec(`insert into t values (3,"3")`) - - tk.MustExec(`COMMIT`) - waitFor(4, "write only") - tk.MustExec(`BEGIN`) - tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2")) - tk.MustExec(`insert into t values (4,"4")`) - - tk3.MustExec(`COMMIT`) - waitFor(4, "write reorganization") - tk3.MustExec(`BEGIN`) - tk3.MustQuery(`show create table t`).Check(testkit.Rows("" + - "t CREATE TABLE `t` (\n" + - " `a` int(11) NOT NULL,\n" + - " `b` varchar(255) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - "PARTITION BY HASH (`a`) PARTITIONS 3")) - tk3.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3")) - tk3.MustExec(`insert into t values (5,"5")`) - - tk.MustExec(`COMMIT`) - waitFor(4, "delete reorganization") - tk.MustExec(`BEGIN`) - tk.MustQuery(`show create table t`).Check(testkit.Rows("" + - "t CREATE TABLE `t` (\n" + - " `a` int(11) NOT NULL,\n" + - " `b` varchar(255) DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - "PARTITION BY HASH (`a`) PARTITIONS 4")) - tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 4")) - tk.MustExec(`insert into t values (6,"6")`) - - tk3.MustExec(`COMMIT`) - tk.MustExec(`COMMIT`) - <-syncChan - tk.MustQuery(`select * from t`).Sort().Check(testkit.Rows("1 1", "2 2", "3 3", "4 4", "5 5", "6 6")) -} - -type compoundSQL struct { - selectSQL string - point bool - batchPoint bool - pruned bool - executeExplain bool - usedPartition []string - notUsedPartition []string - rowCount int -} - -type partTableCase struct { - partitionbySQL string - selectInfo []compoundSQL -} - -func executePartTableCase(t *testing.T, tk *testkit.TestKit, testCases []partTableCase, - createSQL string, insertSQLs []string, dropSQL string) { - for i, testCase := range testCases { - // create table ... partition by key ... - ddlSQL := createSQL + testCase.partitionbySQL - logutil.BgLogger().Info("Partition DDL test", zap.Int("i", i), zap.String("ddlSQL", ddlSQL)) - executeSQLWrapper(t, tk, ddlSQL) - // insert data - for _, insertsql := range insertSQLs { - executeSQLWrapper(t, tk, insertsql) - } - // execute testcases - for j, selInfo := range testCase.selectInfo { - logutil.BgLogger().Info("Select", zap.Int("j", j), zap.String("selectSQL", selInfo.selectSQL)) - tk.MustQuery(selInfo.selectSQL).Check(testkit.Rows(strconv.Itoa(selInfo.rowCount))) - if selInfo.executeExplain { - result := tk.MustQuery("EXPLAIN " + selInfo.selectSQL) - if selInfo.point { - result.CheckContain("Point_Get") - } - if selInfo.batchPoint { - result.CheckContain("Batch_Point_Get") - } - if selInfo.pruned { - for _, part := range selInfo.usedPartition { - result.CheckContain(part) - } - for _, part := range selInfo.notUsedPartition { - result.CheckNotContain(part) - } - } - } - } - executeSQLWrapper(t, tk, dropSQL) - } -} - -func executeSQLWrapper(t *testing.T, tk *testkit.TestKit, SQLString string) { - res, err := tk.Exec(SQLString) - if res != nil { - res.Close() - } - require.Nil(t, err) -} - -func TestKeyPartitionTableBasic(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database partitiondb") - defer tk.MustExec("drop database partitiondb") - tk.MustExec("use partitiondb") - testCases := []struct { - createSQL string - dropSQL string - insertSQL string - selectInfo []compoundSQL - }{ - { - createSQL: "CREATE TABLE tkey0 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3)) PARTITION BY KEY(col3) PARTITIONS 4", - insertSQL: "INSERT INTO tkey0 VALUES(1, '2023-02-22', 1, 1), (2, '2023-02-22', 2, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4)", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey0", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey0 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey0 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey0 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 0, - }, - { - "SELECT count(*) FROM tkey0 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey0 WHERE col3 = 3", - true, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey0 WHERE col3 = 3 or col3 = 4", - false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey0 WHERE col3 >1 AND col3 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, - }, - }, - - dropSQL: "DROP TABLE IF EXISTS tkey0", - }, - { - createSQL: "CREATE TABLE tkey7 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3,col1)) PARTITION BY KEY(col3,col1) PARTITIONS 4", - insertSQL: "INSERT INTO tkey7 VALUES(1, '2023-02-22', 1, 1), (1, '2023-02-22', 2, 1),(2, '2023-02-22', 2, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4),(4, '2023-02-22', 5, 4)", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey7", - false, false, false, false, []string{}, []string{}, 6, - }, - { - "SELECT count(*) FROM tkey7 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey7 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey7 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey7 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey7 WHERE col3 = 3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey7 WHERE col3 = 3 and col1 = 3", - true, false, true, true, []string{"partition:p1"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey7 WHERE col3 = 3 or col3 = 4", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey7 WHERE col3 = 3 and col1 = 3 OR col3 = 4 and col1 = 4", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey7 WHERE col1>1 and col3 >1 AND col3 < 4 and col1<3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey7", - }, - { - createSQL: "CREATE TABLE tkey8 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 INT NOT NULL, col4 INT NOT NULL,PRIMARY KEY (col3,col1)) PARTITION BY KEY(col3,col1) PARTITIONS 4", - insertSQL: "INSERT INTO tkey8 VALUES(1, '2023-02-22', 111, 1), (1, '2023-02-22', 2, 1),(2, '2023-02-22', 218, 2), (3, '2023-02-22', 3, 3), (4, '2023-02-22', 4, 4),(4, '2023-02-22', 5, 4),(5, '2023-02-22', 5, 5),(5, '2023-02-22', 50, 2),(6, '2023-02-22', 62, 2),(60, '2023-02-22', 6, 5),(70, '2023-02-22', 50, 2),(80, '2023-02-22', 62, 2),(100, '2023-02-22', 62, 2),(2000, '2023-02-22', 6, 5),(400, '2023-02-22', 50, 2),(90, '2023-02-22', 62, 2)", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey8", - false, false, false, false, []string{}, []string{}, 16, - }, - { - "SELECT count(*) FROM tkey8 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey8 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 7, - }, - { - "SELECT count(*) FROM tkey8 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey8 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey8 WHERE col3 = 3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey8 WHERE col3 = 3 and col1 = 3", - true, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey8 WHERE col3 = 3 or col3 = 4", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey8 WHERE col3 = 3 and col1 = 3 OR col3 = 4 and col1 = 4", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey8 WHERE col1>1 and col3 >1 AND col3 < 4 and col1<3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 0, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey8", - }, - { - createSQL: "CREATE TABLE tkey6 (col1 INT NOT NULL, col2 DATE NOT NULL, col3 VARCHAR(12) NOT NULL, col4 INT NOT NULL,UNIQUE KEY (col3)) PARTITION BY KEY(col3) PARTITIONS 4", - insertSQL: "INSERT INTO tkey6 VALUES(1, '2023-02-22', 'linpin', 1), (2, '2023-02-22', 'zhangsan', 2), (3, '2023-02-22', 'anqila', 3), (4, '2023-02-22', 'xingtian', 4),(1, '2023-02-22', 'renleifeng', 5), (2, '2023-02-22', 'peilin', 2),(1, '2023-02-22', 'abcdeeg', 7), (2, '2023-02-22', 'rpstdfed', 8)", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey6", - false, false, false, false, []string{}, []string{}, 8, - }, - { - "SELECT count(*) FROM tkey6 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey6 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey6 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey6 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey6 WHERE col3 = 'linpin'", - true, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey6 WHERE col3 = 'zhangsan' or col3 = 'linpin'", - true, true, true, true, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey6 WHERE col3 > 'linpin' AND col3 < 'qing'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey6", - }, - { - createSQL: "CREATE TABLE tkey2 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(KHH) partitions 4", - insertSQL: "INSERT INTO tkey2 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530')", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey2", - false, false, false, false, []string{}, []string{}, 17, - }, - { - "SELECT count(*) FROM tkey2 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey2 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey2 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey2 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 6, - }, - { - "SELECT count(*) FROM tkey2 WHERE KHH = 'huaian'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey2 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey2 WHERE KHH > 'nanjing' AND KHH < 'suzhou'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey2", - }, - { - createSQL: "CREATE TABLE tkey5 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (KHH, JYRQ, ZJZH))PARTITION BY KEY(KHH) partitions 4", - insertSQL: "INSERT INTO tkey5 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530')", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey5", - false, false, false, false, []string{}, []string{}, 17, - }, - { - "SELECT count(*) FROM tkey5 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey5 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey5 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey5 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 6, - }, - { - "SELECT count(*) FROM tkey5 WHERE KHH = 'huaian'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey5 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p0", "partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey5 WHERE KHH > 'nanjing' AND KHH < 'suzhou'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey5", - }, - { - createSQL: "CREATE TABLE tkey4 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(JYRQ, KHH) partitions 4", - insertSQL: "INSERT INTO tkey4 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530'),(1,'beijing','010'),(2,'beijing','010'),(2,'zzzzwuhan','027')", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey4", - false, false, false, false, []string{}, []string{}, 20, - }, - { - "SELECT count(*) FROM tkey4 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 7, - }, - { - "SELECT count(*) FROM tkey4 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey4 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey4 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey4 WHERE JYRQ = 2", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2 or KHH = 'zhenjiang' and JYRQ = 3", - false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' and JYRQ = 2 or KHH = 'zhenjiang' and JYRQ = 3 or KHH = 'HUAIAN' and JYRQ = 15", - false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey4 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey4 WHERE JYRQ = 2 OR JYRQ = 3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey4 WHERE JYRQ = 2 OR JYRQ = 3 OR JYRQ = 15", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey4 WHERE JYRQ >6 AND JYRQ < 10", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey4 WHERE JYRQ >6 and KHH>'lianyungang' AND JYRQ < 10 and KHH<'xuzhou'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey4", - }, - { - createSQL: "CREATE TABLE tkey9 (JYRQ INT not null,KHH VARCHAR(12) not null,ZJZH CHAR(14) not null,primary key (JYRQ, KHH, ZJZH))PARTITION BY KEY(JYRQ, KHH, ZJZH) partitions 4", - insertSQL: "INSERT INTO tkey9 VALUES(1,'nanjing','025'),(2,'huaian','0517'),(3,'zhenjiang','0518'),(4,'changzhou','0519'),(5,'wuxi','0511'),(6,'suzhou','0512'),(7,'xuzhou','0513'),(8,'suqian','0513'),(9,'lianyungang','0514'),(10,'yangzhou','0515'),(11,'taizhou','0516'),(12,'nantong','0520'),(13,'yancheng','0521'),(14,'NANJING','025'),(15,'HUAIAN','0527'),(16,'ZHENJIANG','0529'),(17,'CHANGZHOU','0530'),(1,'beijing','010'),(2,'beijing','010'),(2,'zzzzwuhan','027')", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey9", - false, false, false, false, []string{}, []string{}, 20, - }, - { - "SELECT count(*) FROM tkey9 PARTITION(p0)", - false, false, false, false, []string{}, []string{}, 6, - }, - { - "SELECT count(*) FROM tkey9 PARTITION(p1)", - false, false, false, false, []string{}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey9 PARTITION(p2)", - false, false, false, false, []string{}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey9 PARTITION(p3)", - false, false, false, false, []string{}, []string{}, 8, - }, - { - "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH = '0517'", - true, false, true, true, []string{"partition:p0"}, []string{"partition:p3", "partition:p1", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - { - "SELECT count(*) FROM tkey9 WHERE JYRQ = 2", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH='0517' or KHH = 'zhenjiang' and JYRQ = 3 and ZJZH = '0518'", - false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' and JYRQ = 2 and ZJZH='0517' or KHH = 'zhenjiang' and JYRQ = 3 and ZJZH = '0518' or KHH = 'NANJING' and JYRQ = 14 and ZJZH = '025'", - false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p2", "partition:p1"}, 3, - }, - { - "SELECT count(*) FROM tkey9 WHERE KHH = 'huaian' or KHH = 'zhenjiang'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - { - "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 OR JYRQ = 3", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 4, - }, - { - "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 OR JYRQ = 3 OR JYRQ = 15", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey9 WHERE JYRQ >6 AND JYRQ < 10", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, - }, - { - "SELECT count(*) FROM tkey9 WHERE JYRQ = 2 and KHH = 'huaian' OR JYRQ = 3 and KHH = 'zhenjiang'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 2, - }, - }, - dropSQL: "DROP TABLE IF EXISTS tkey9", - }, - } - - for i, testCase := range testCases { - logutil.BgLogger().Info("Partition DDL test", zap.Int("i", i), zap.String("createSQL", testCase.createSQL)) - executeSQLWrapper(t, tk, testCase.createSQL) - executeSQLWrapper(t, tk, testCase.insertSQL) - for j, selInfo := range testCase.selectInfo { - logutil.BgLogger().Info("Select", zap.Int("j", j), zap.String("selectSQL", selInfo.selectSQL)) - tk.MustQuery(selInfo.selectSQL).Check(testkit.Rows(strconv.Itoa(selInfo.rowCount))) - if selInfo.executeExplain { - result := tk.MustQuery("EXPLAIN " + selInfo.selectSQL) - if selInfo.point { - result.CheckContain("Point_Get") - } - if selInfo.batchPoint { - result.CheckContain("Batch_Point_Get") - } - if selInfo.pruned { - for _, part := range selInfo.usedPartition { - result.CheckContain(part) - } - } - } - } - executeSQLWrapper(t, tk, testCase.dropSQL) - } -} - -func TestKeyPartitionTableAllFeildType(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create database partitiondb3") - defer tk.MustExec("drop database partitiondb3") - tk.MustExec("use partitiondb3") - // partition column is numeric family - createSQL := "create table tkey_numeric(\n" + - "id1 BIT(8) not null,\n" + - "id2 TINYINT not null,\n" + - "id3 BOOL not null,\n" + - "id4 SMALLINT not null,\n" + - "id5 MEDIUMINT not null,\n" + - "id6 INT not null,\n" + - "id7 BIGINT not null,\n" + - "id8 DECIMAL(12,4) not null,\n" + - "id9 FLOAT not null,\n" + - "id10 DOUBLE not null,\n" + - "name varchar(20),\n" + - "primary key(id1,id2,id3,id4,id5,id6,id7,id8,id9,id10)\n" + - ")\n" - dropSQL := "drop table tkey_numeric" - insertSQLS := []string{ - "INSERT INTO tkey_numeric VALUES(1,1,0,1,1,1,1,1.1,120.1,367.45,'linpin'),(12,12,12,12,12,12,12,12.1,1220.1,3267.45,'anqila')", - "INSERT INTO tkey_numeric VALUES(0,2,1,2,2,2,2,2.78,16.78,17.25,'ring'),(33,33,33,33,33,33,33,33.78,336.78,37.25,'black')", - "INSERT INTO tkey_numeric VALUES(2,3,1,3,3,3,3,3.78,26.78,417.25,'liudehua'),(22,23,21,23,23,23,23,32.78,26.72,27.15,'chenchen')", - "INSERT INTO tkey_numeric VALUES(3,3,2,4,4,4,4,4.78,46.48,89.35,'guofucheng'), (4,4,4,5,5,5,5,5.78,56.48,59.35,'zhangxuyou')", - "INSERT INTO tkey_numeric VALUES(5,5,5,5,5,5,5,5.78,56.48,59.35,'xietingfeng'),(34,34,34,34,34,34,34,34.78,346.78,34.25,'dongxu')", - "INSERT INTO tkey_numeric VALUES(250,120,120,250,250,258,348,38.78,186.48,719.35,'chenguanxi'),(35,35,35,250,35,35,35,35.78,356.48,35.35,'chenguanxi')", - } - testCases := []partTableCase{ - { - partitionbySQL: "PARTITION BY KEY(id1) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 5, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id1 = 3", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id1 = 3 or id1 = 4", - false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id1 >1 AND id1 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id2) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id2 = 3", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id2 = 3 or id2 = 4", - false, false, true, true, []string{"partition:p0", "partition:p3"}, []string{"partition:p1", "partition:p2"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id2 >1 AND id2 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 3, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id3) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p0", "partition:p1", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id3 = 5", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id3 = 5 or id3 = 4", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id3 >1 AND id3 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id4) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 5, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id4 = 5", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id4 = 5 or id4 = 4", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id4 >1 AND id4 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id5) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p3", "partition:p0"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id5 = 5", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id5 = 5 or id5 = 4", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id5 >1 AND id5 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id6) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id6 = 5", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id6 = 5 or id6 = 4", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id6 >1 AND id6 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id7) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id7 = 5", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id7 = 5 or id7 = 4", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id7 >1 AND id7 < 4", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p2", "partition:p0"}, 2, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id8) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id8 = 1.1", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p2", "partition:p0", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id8 = 1.1 or id8 = 33.78", - false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id8 >1 AND id8 < 4", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 3, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id9) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id9 = 46.48", - false, false, true, true, []string{}, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id9 = 46.48 or id9 = 336.78", - false, false, true, true, []string{}, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id9 >45 AND id9 < 47", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id10) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_numeric", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 12, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_numeric PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id10 = 46.48", - false, false, true, true, []string{}, []string{}, 0, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id10 = 46.48 or id10 = 336.78", - false, false, true, true, []string{}, []string{}, 0, - }, - { - "SELECT count(*) FROM tkey_numeric WHERE id10 >366 AND id10 < 368", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - } - executePartTableCase(t, tk, testCases, createSQL, insertSQLS, dropSQL) - - // partition column is date/time family - createSQL2 := "create table tkey_datetime(\n" + - "id1 DATE not null,\n" + - "id2 TIME not null,\n" + - "id3 DATETIME not null,\n" + - "id4 TIMESTAMP not null,\n" + - "id5 YEAR not null,\n" + - "name varchar(20),\n" + - "primary key(id1, id2, id3, id4, id5)\n" + - ")\n" - dropSQL2 := "drop table tkey_datetime" - insertSQLS2 := []string{ - "insert into tkey_datetime values('2012-04-10', '12:12:12', '2012-04-10 12:12:12', '2012-04-10 12:12:12.12345', 2012, 'linpin')", - "insert into tkey_datetime values('2013-05-11', '13:13:13', '2013-05-11 13:13:13', '2013-05-11 13:13:13.43133', 2013, 'minghua')", - "insert into tkey_datetime values('2014-06-12', '14:14:14', '2014-06-12 14:14:14', '2014-06-12 14:14:14.32344', 2014, 'oyangfeng')", - "insert into tkey_datetime values('2015-07-13', '15:15:15', '2015-07-13 15:15:15', '2015-07-13 15:15:15.42544', 2015, 'pengdehuai')", - "insert into tkey_datetime values('2021-08-14', '16:16:16', '2021-08-14 16:16:16', '2021-08-14 16:16:16.18945', 2021, 'shenwanshan')", - "insert into tkey_datetime values('2022-12-23', '23:12:15', '2022-12-23 23:12:15', '2022-12-23 23:12:15.43133', 2022, 'tangchap')", - "insert into tkey_datetime values('2023-01-12', '20:38:14', '2023-01-12 20:38:14', '2023-01-12 20:38:14.32344', 2023, 'xinyu')", - "insert into tkey_datetime values('2018-07-13', '07:15:15', '2018-07-13 07:15:15', '2018-07-13 07:15:15.42544', 2018, 'zongyang')", - "insert into tkey_datetime values('1980-01-30', '00:12:15', '1980-01-30 00:12:15', '1980-01-30 00:12:15.42544', 1980, 'MAYUWEI')", - "insert into tkey_datetime values('1980-03-30', '00:13:15', '1980-03-30 00:13:15', '1980-03-30 00:13:15.42544', 1980, 'maqinwei')", - } - testCases2 := []partTableCase{ - { - partitionbySQL: "PARTITION BY KEY(id1) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_datetime", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id1 = '2012-04-10'", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id1 = '2012-04-10' or id1 = '2018-07-13'", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id1 >'2012-04-10' AND id1 < '2014-04-10'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id3) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_datetime", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id3 = '2012-04-10 12:12:12'", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id3 = '2012-04-10 12:12:12' or id3 = '2021-08-14 16:16:16'", - false, false, true, true, []string{"partition:p3", "partition:p1"}, []string{"partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id3 >'2012-04-10 12:12:12' AND id3 < '2014-04-10 12:12:12'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id4) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_datetime", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 4, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id4 = '2012-04-10 12:12:12'", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id4 = '2012-04-10 12:12:12' or id4 = '2021-08-14 16:16:16'", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id4 >'2012-04-10 12:12:12' AND id4 < '2014-04-10 12:12:12'", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id5) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_datetime", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 10, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 3, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id5 = 2012", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id5 = 2012 or id5 = 2018", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_datetime WHERE id5 >2012 AND id5 < 2014", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p3", "partition:p0"}, 1, - }, - }, - }, - } - executePartTableCase(t, tk, testCases2, createSQL2, insertSQLS2, dropSQL2) - - // partition column is string family - createSQL3 := "create table tkey_string(\n" + - "id1 CHAR(16) not null,\n" + - "id2 VARCHAR(16) not null,\n" + - "id3 BINARY(16) not null,\n" + - "id4 VARBINARY(16) not null,\n" + - "id5 BLOB not null,\n" + - "id6 TEXT not null,\n" + - "id7 ENUM('x-small', 'small', 'medium', 'large', 'x-large') not null,\n" + - "id8 SET ('a', 'b', 'c', 'd') not null,\n" + - "name varchar(16),\n" + - "primary key(id1, id2, id3, id4, id7, id8)\n" + - ")\n" - dropSQL3 := "drop table tkey_string" - insertSQLS3 := []string{ - "INSERT INTO tkey_string VALUES('huaian','huaian','huaian','huaian','huaian','huaian','x-small','a','linpin')", - "INSERT INTO tkey_string VALUES('nanjing','nanjing','nanjing','nanjing','nanjing','nanjing','small','b','linpin')", - "INSERT INTO tkey_string VALUES('zhenjiang','zhenjiang','zhenjiang','zhenjiang','zhenjiang','zhenjiang','medium','c','linpin')", - "INSERT INTO tkey_string VALUES('suzhou','suzhou','suzhou','suzhou','suzhou','suzhou','large','d','linpin')", - "INSERT INTO tkey_string VALUES('wuxi','wuxi','wuxi','wuxi','wuxi','wuxi','x-large','a','linpin')", - } - testCases3 := []partTableCase{ - { - partitionbySQL: "PARTITION BY KEY(id1) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_string WHERE id1 = 'huaian'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id1 = 'huaian' or id1 = 'suzhou'", - false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id1 >'huaian' AND id1 < 'suzhou'", - false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id2) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_string WHERE id2 = 'huaian'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id2 = 'huaian' or id2 = 'suzhou'", - false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id2 >'huaian' AND id2 < 'suzhou'", - false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id3) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id3 = 0x73757A686F7500000000000000000000", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id3 = 0x73757A686F7500000000000000000000 or id3 = 0x6E616E6A696E67000000000000000000", - false, false, true, true, []string{"partition:p0", "partition:p1"}, []string{"partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id3 >0x67756169616E00000000000000000000 AND id3 < 0x6E616E6A696E67000000000000000000", - false, false, true, true, []string{"partition:p1", "partition:p0", "partition:p2", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id4) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_string WHERE id4 = 0x68756169616E", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p0", "partition:p2"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id4 = 0x68756169616E or id4 = 0x73757A686F75", - false, false, true, true, []string{"partition:p3", "partition:p0"}, []string{"partition:p1", "partition:p2"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id4 >0x73757A686F75 AND id4 < 0x78757869", - false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id7) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id7 = 'x-small'", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string WHERE id7 = 'x-small' or id7 = 'large'", - false, false, true, true, []string{"partition:p0", "partition:p2"}, []string{"partition:p1", "partition:p3"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id7 > 'large' AND id7 < 'x-small'", - false, false, true, true, []string{"partition:p1", "partition:p0", "partition:p3"}, []string{"partition:p2"}, 3, - }, - }, - }, - { - partitionbySQL: "PARTITION BY KEY(id8) partitions 4", - selectInfo: []compoundSQL{ - { - "SELECT count(*) FROM tkey_string", - false, false, true, true, []string{"partition:p0", "partition:p1", "partition:p2", "partition:p3"}, []string{}, 5, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p0)", - false, false, true, true, []string{"partition:p0"}, []string{"partition:p1", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p1)", - false, false, true, true, []string{"partition:p1"}, []string{"partition:p0", "partition:p2", "partition:p3"}, 1, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p2)", - false, false, true, true, []string{"partition:p2"}, []string{"partition:p1", "partition:p0", "partition:p3"}, 0, - }, - { - "SELECT count(*) FROM tkey_string PARTITION(p3)", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 3, - }, - { - "SELECT count(*) FROM tkey_string WHERE id8 = 'a'", - false, false, true, true, []string{"partition:p3"}, []string{"partition:p1", "partition:p2", "partition:p0"}, 2, - }, - { - "SELECT count(*) FROM tkey_string WHERE id8 = 'a' or id8 = 'b'", - false, false, true, true, []string{"partition:p1", "partition:p3"}, []string{"partition:p0", "partition:p2"}, 3, - }, - { - "SELECT count(*) FROM tkey_string WHERE id8 > 'a' AND id8 < 'c'", - false, false, true, true, []string{"partition:p1", "partition:p2", "partition:p0", "partition:p3"}, []string{}, 1, - }, - }, - }, - } - executePartTableCase(t, tk, testCases3, createSQL3, insertSQLS3, dropSQL3) -} - -func TestPruneModeWarningInfo(t *testing.T) { - store := testkit.CreateMockStore(t) - - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@tidb_partition_prune_mode = 'static'") - tk.MustQuery("show warnings").Check(testkit.Rows()) - tk.MustExec("set session tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats", - "Warning 1105 Please avoid setting partition prune mode to dynamic at session level and set partition prune mode to dynamic at global level")) - tk.MustExec("set global tidb_partition_prune_mode = 'dynamic'") - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Please analyze all partition tables again for consistency between partition and global stats")) -} - -type testCallback struct { - ddl.Callback - OnJobRunBeforeExported func(job *model.Job) -} - -func newTestCallBack(t *testing.T, dom *domain.Domain) *testCallback { - defHookFactory, err := ddl.GetCustomizedHook("default_hook") - require.NoError(t, err) - return &testCallback{ - Callback: defHookFactory(dom), - } -} - -func (c *testCallback) OnJobRunBefore(job *model.Job) { - if c.OnJobRunBeforeExported != nil { - c.OnJobRunBeforeExported(job) - } -} - -func TestPartitionByIntListExtensivePart(t *testing.T) { - limitSizeOfTest := true - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - schemaName := "PartitionByIntListExtensive" - tk.MustExec("create database " + schemaName) - tk.MustExec("use " + schemaName) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + schemaName) - - tBase := `(lp tinyint unsigned, a int unsigned, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, key (b), key (c,b), key (d,c), key(e), primary key (a, lp))` - t2Str := `create table t2 ` + tBase - tStr := `create table t ` + tBase - - rows := 100 - pkInserts := 20 - pkUpdates := 20 - pkDeletes := 10 // Enough to delete half of what is inserted? - tStart := []string{ - // Non partitioned - tStr, - // RANGE COLUMNS - tStr + ` partition by list (lp) (partition p0 values in (0,6),partition p1 values in (1), partition p2 values in (2), partition p3 values in (3), partition p4 values in (4,5))`, - // KEY - tStr + ` partition by key(a) partitions 5`, - // HASH - tStr + ` partition by hash(a) partitions 5`, - // HASH with function - tStr + ` partition by hash(a DIV 3) partitions 5`, - } - if limitSizeOfTest { - tStart = tStart[:2] - } - quarterUintRange := 1 << 30 - quarterUintRangeStr := fmt.Sprintf("%d", quarterUintRange) - halfUintRangeStr := fmt.Sprintf("%d", 2*quarterUintRange) - threeQuarterUintRangeStr := fmt.Sprintf("%d", 3*quarterUintRange) - tAlter := []string{ - // LIST - `alter table t partition by list (lp) (partition p0 values in (2), partition p1 values in (1,3,5), partition p2 values in (0,4,6))`, - `alter table t partition by list (lp) (partition p3 values in (3), partition p4 values in (4), partition p2 values in (2), partition p6 values in (6), partition p5 values in (5), partition p1 values in (1), partition p0 values in (0))`, - // LIST COLUMNS - `alter table t partition by list columns (lp) (partition p0 values in (2), partition p1 values in (1,3,5), partition p2 values in (0,4,6))`, - `alter table t partition by list columns (lp) (partition p3 values in (3), partition p4 values in (4), partition p2 values in (2), partition p6 values in (6), partition p5 values in (5), partition p1 values in (1), partition p0 values in (0))`, - // RANGE COLUMNS - `alter table t partition by range (a) (partition pFirst values less than (` + halfUintRangeStr + `), partition pLast values less than (MAXVALUE))`, - // RANGE COLUMNS - `alter table t partition by range (a) (partition pFirst values less than (` + quarterUintRangeStr + `),` + - `partition pLowMid values less than (` + halfUintRangeStr + `),` + - `partition pHighMid values less than (` + threeQuarterUintRangeStr + `),` + - `partition pLast values less than (maxvalue))`, - // KEY - `alter table t partition by key(a) partitions 7`, - `alter table t partition by key(a) partitions 3`, - // Hash - `alter table t partition by hash(a) partitions 7`, - `alter table t partition by hash(a) partitions 3`, - // Hash - `alter table t partition by hash(a DIV 13) partitions 7`, - `alter table t partition by hash(a DIV 13) partitions 3`, - } - if limitSizeOfTest { - tAlter = tAlter[:2] - } - - seed := gotime.Now().UnixNano() - logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) - reorgRand := rand.New(rand.NewSource(seed)) - for _, createSQL := range tStart { - for _, alterSQL := range tAlter { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewIntPK() - getValues := getInt7ValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } - } - for _, createSQL := range tStart[1:] { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewIntPK() - getValues := getInt7ValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } -} - -func getInt7ValuesFunc() func(string, bool, *rand.Rand) string { - cnt := 0 - return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { - s := `(%d, %s, '%s', %d, '%s', '%s', %f, '%s')` - if asAssignment { - s = `lp = %d, a = %s, b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` - } - cnt++ - lp, err := strconv.Atoi(pk) - if err != nil { - lp = 0 - } - return fmt.Sprintf(s, - lp%7, - pk, - randStr(reorgRand.Intn(19), reorgRand), - cnt, //reorgRand.Int31(), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - reorgRand.Float64(), - randStr(512+reorgRand.Intn(1024), reorgRand)) - } -} - -func TestPartitionByIntExtensivePart(t *testing.T) { - limitSizeOfTest := true - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - schemaName := "PartitionByIntExtensive" - tk.MustExec("create database " + schemaName) - tk.MustExec("use " + schemaName) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + schemaName) - - tBase := `(a int unsigned, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))` - t2Str := `create table t2 ` + tBase - tStr := `create table t ` + tBase - - rows := 100 - pkInserts := 20 - pkUpdates := 20 - pkDeletes := 10 // Enough to delete half of what is inserted? - thirdUintRange := 1 << 32 / 2 - thirdUintRangeStr := fmt.Sprintf("%d", thirdUintRange) - twoThirdUintRangeStr := fmt.Sprintf("%d", 2*thirdUintRange) - tStart := []string{ - // Non partitioned - tStr, - // RANGE COLUMNS - tStr + ` partition by range (a) (partition pFirst values less than (` + thirdUintRangeStr + `),` + - `partition pMid values less than (` + twoThirdUintRangeStr + `), partition pLast values less than (maxvalue))`, - // KEY - tStr + ` partition by key(a) partitions 5`, - // HASH - tStr + ` partition by hash(a) partitions 5`, - // HASH with function - tStr + ` partition by hash(a DIV 3) partitions 5`, - } - if limitSizeOfTest { - tStart = tStart[:2] - } - quarterUintRange := 1 << 30 - quarterUintRangeStr := fmt.Sprintf("%d", quarterUintRange) - halfUintRangeStr := fmt.Sprintf("%d", 2*quarterUintRange) - threeQuarterUintRangeStr := fmt.Sprintf("%d", 3*quarterUintRange) - tAlter := []string{ - // RANGE COLUMNS - `alter table t partition by range (a) (partition pFirst values less than (` + halfUintRangeStr + `), partition pLast values less than (MAXVALUE))`, - // RANGE COLUMNS - `alter table t partition by range (a) (partition pFirst values less than (` + quarterUintRangeStr + `),` + - `partition pLowMid values less than (` + halfUintRangeStr + `),` + - `partition pHighMid values less than (` + threeQuarterUintRangeStr + `),` + - `partition pLast values less than (maxvalue))`, - // KEY - `alter table t partition by key(a) partitions 7`, - `alter table t partition by key(a) partitions 3`, - // Hash - `alter table t partition by hash(a) partitions 7`, - `alter table t partition by hash(a) partitions 3`, - // Hash - `alter table t partition by hash(a DIV 13) partitions 7`, - `alter table t partition by hash(a DIV 13) partitions 3`, - } - if limitSizeOfTest { - tAlter = tAlter[:2] - } - - seed := gotime.Now().UnixNano() - logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) - reorgRand := rand.New(rand.NewSource(seed)) - for _, createSQL := range tStart { - for _, alterSQL := range tAlter { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewIntPK() - getValues := getIntValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } - } - for _, createSQL := range tStart[1:] { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewIntPK() - getValues := getIntValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } -} - -func getNewIntPK() func(map[string]struct{}, string, *rand.Rand) string { - return func(m map[string]struct{}, suf string, reorgRand *rand.Rand) string { - uintPK := reorgRand.Uint32() - newPK := strconv.FormatUint(uint64(uintPK), 10) - for _, ok := m[newPK]; ok; { - uintPK = reorgRand.Uint32() - newPK = strconv.FormatUint(uint64(uintPK), 10) - _, ok = m[newPK] - } - m[newPK] = struct{}{} - return newPK - } -} - -func getIntValuesFunc() func(string, bool, *rand.Rand) string { - cnt := 0 - return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { - s := `(%s, '%s', %d, '%s', '%s', %f, '%s')` - if asAssignment { - s = `a = %s, b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` - } - cnt++ - return fmt.Sprintf(s, - pk, - randStr(reorgRand.Intn(19), reorgRand), - cnt, //reorgRand.Int31(), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - reorgRand.Float64(), - randStr(512+reorgRand.Intn(1024), reorgRand)) - } -} - -func TestPartitionByExtensivePart(t *testing.T) { - limitSizeOfTest := true - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - schemaName := "PartitionByExtensive" - tk.MustExec("create database " + schemaName) - tk.MustExec("use " + schemaName) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + schemaName) - - tBase := `(a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))` - t2Str := `create table t2 ` + tBase - tStr := `create table t ` + tBase - - rows := 100 - pkInserts := 20 - pkUpdates := 20 - pkDeletes := 10 // Enough to delete half of what is inserted? - tStart := []string{ - // Non partitioned - tStr, - // RANGE COLUMNS - tStr + ` partition by range columns (a) (partition pNull values less than (""), partition pM values less than ("M"), partition pLast values less than (maxvalue))`, - // KEY - tStr + ` partition by key(a) partitions 5`, - } - if limitSizeOfTest { - tStart = tStart[:2] - } - showCreateStr := "t CREATE TABLE `t` (\n" + - " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + - " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + - " `c` int(11) DEFAULT NULL,\n" + - " `d` datetime DEFAULT NULL,\n" + - " `e` timestamp NULL DEFAULT NULL,\n" + - " `f` double DEFAULT NULL,\n" + - " `g` text DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`),\n" + - " KEY `c` (`c`,`b`),\n" + - " KEY `d` (`d`,`c`),\n" + - " KEY `e` (`e`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" - tAlter := []struct{ alter, result string }{ - { - // RANGE COLUMNS - alter: `alter table t partition by range columns (a) (partition pH values less than ("H"), partition pLast values less than (MAXVALUE))`, - result: showCreateStr + - "PARTITION BY RANGE COLUMNS(`a`)\n" + - "(PARTITION `pH` VALUES LESS THAN ('H'),\n" + - " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))", - }, - { - // RANGE COLUMNS - alter: `alter table t partition by range columns (a) (partition pNull values less than (""), partition pG values less than ("G"), partition pR values less than ("R"), partition pLast values less than (maxvalue))`, - result: showCreateStr + - "PARTITION BY RANGE COLUMNS(`a`)\n" + - "(PARTITION `pNull` VALUES LESS THAN (''),\n" + - " PARTITION `pG` VALUES LESS THAN ('G'),\n" + - " PARTITION `pR` VALUES LESS THAN ('R'),\n" + - " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))", - }, - // KEY - { - alter: `alter table t partition by key(a) partitions 7`, - result: showCreateStr + - "PARTITION BY KEY (`a`) PARTITIONS 7", - }, - { - alter: `alter table t partition by key(a) partitions 3`, - result: showCreateStr + - "PARTITION BY KEY (`a`) PARTITIONS 3", - }, - } - if limitSizeOfTest { - tAlter = tAlter[:2] - } - - seed := gotime.Now().UnixNano() - logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) - reorgRand := rand.New(rand.NewSource(seed)) - for _, createSQL := range tStart { - for _, alterSQL := range tAlter { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewStringPK() - getValues := getValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, alterSQL.alter, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - res := tk.MustQuery(`show create table t`) - res.AddComment("create SQL: " + createSQL + "\nalterSQL: " + alterSQL.alter) - res.Check(testkit.Rows(alterSQL.result)) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } - } - - for _, createSQL := range tStart[1:] { - tk.MustExec(createSQL) - tk.MustExec(t2Str) - getNewPK := getNewStringPK() - getValues := getValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, "alter table t remove partitioning", rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) - tk.MustExec(`drop table t`) - tk.MustExec(`drop table t2`) - } -} - -func TestReorgPartExtensivePart(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - schemaName := "ReorgPartExtensive" - tk.MustExec("create database " + schemaName) - tk.MustExec("use " + schemaName) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use " + schemaName) - // TODO: Handle different column types? - // TODO: Handle index for different column types / combinations as well? - - // Steps: - // - create a table (should at least test both LIST and RANGE partition, Including COLUMNS) - // - add base data - // - start DDL - // - before each (and after?) each state transition: - // - insert, update and delete concurrently, to verify that the states are correctly handled. - // - TODO: Crash (if rollback is needed, then OK, but the rest need to be tested - // - TODO: Fail - // - TODO: run queries that could clash with backfill etc. (How to handle expected errors?) - // - TODO: on both the 'current' state and 'previous' state! - // - run ADMIN CHECK TABLE - // - - tk.MustExec(`create table t (a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a)) partition by range columns (a) (partition pNull values less than (""), partition pM values less than ("M"), partition pLast values less than (maxvalue))`) - tk.MustExec(`create table t2 (a varchar(255) collate utf8mb4_unicode_ci, b varchar(255) collate utf8mb4_general_ci, c int, d datetime, e timestamp, f double, g text, primary key (a), key (b), key (c,b), key (d,c), key(e))`) - - // TODO: Test again with timestamp in col e!! - tk.MustQuery(`show create table t`).Check(testkit.Rows("" + - "t CREATE TABLE `t` (\n" + - " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + - " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + - " `c` int(11) DEFAULT NULL,\n" + - " `d` datetime DEFAULT NULL,\n" + - " `e` timestamp NULL DEFAULT NULL,\n" + - " `f` double DEFAULT NULL,\n" + - " `g` text DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin\n" + - "PARTITION BY RANGE COLUMNS(`a`)\n" + - "(PARTITION `pNull` VALUES LESS THAN (''),\n" + - " PARTITION `pM` VALUES LESS THAN ('M'),\n" + - " PARTITION `pLast` VALUES LESS THAN (MAXVALUE))")) - - tk.MustQuery(`show create table t2`).Check(testkit.Rows("" + - "t2 CREATE TABLE `t2` (\n" + - " `a` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,\n" + - " `b` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,\n" + - " `c` int(11) DEFAULT NULL,\n" + - " `d` datetime DEFAULT NULL,\n" + - " `e` timestamp NULL DEFAULT NULL,\n" + - " `f` double DEFAULT NULL,\n" + - " `g` text DEFAULT NULL,\n" + - " PRIMARY KEY (`a`) /*T![clustered_index] CLUSTERED */,\n" + - " KEY `b` (`b`),\n" + - " KEY `c` (`c`,`b`),\n" + - " KEY `d` (`d`,`c`),\n" + - " KEY `e` (`e`)\n" + - ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin")) - - rows := 1000 - pkInserts := 200 - pkUpdates := 200 - pkDeletes := 100 // Enough to delete half of what is inserted? - alterStr := `alter table t reorganize partition pNull, pM, pLast into (partition pI values less than ("I"), partition pQ values less than ("q"), partition pLast values less than (MAXVALUE))` - seed := rand.Int63() - logutil.BgLogger().Info("Seeding rand", zap.Int64("seed", seed)) - reorgRand := rand.New(rand.NewSource(seed)) - getNewPK := getNewStringPK() - getValues := getValuesFunc() - checkDMLInAllStates(t, tk, tk2, schemaName, alterStr, rows, pkInserts, pkUpdates, pkDeletes, reorgRand, getNewPK, getValues) -} - -func getNewStringPK() func(map[string]struct{}, string, *rand.Rand) string { - return func(m map[string]struct{}, suf string, reorgRand *rand.Rand) string { - newPK := randStr(2+reorgRand.Intn(5), reorgRand) + suf - lowerPK := strings.ToLower(newPK) - for _, ok := m[lowerPK]; ok; { - newPK = randStr(2+reorgRand.Intn(5), reorgRand) - lowerPK = strings.ToLower(newPK) - _, ok = m[lowerPK] - } - m[lowerPK] = struct{}{} - return newPK - } -} - -func getValuesFunc() func(string, bool, *rand.Rand) string { - cnt := 0 - return func(pk string, asAssignment bool, reorgRand *rand.Rand) string { - s := `('%s', '%s', %d, '%s', '%s', %f, '%s')` - if asAssignment { - s = `a = '%s', b = '%s', c = %d, d = '%s', e = '%s', f = %f, g = '%s'` - } - cnt++ - return fmt.Sprintf(s, - pk, - randStr(reorgRand.Intn(19), reorgRand), - cnt, //reorgRand.Int31(), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - gotime.Unix(413487608+int64(reorgRand.Intn(1705689644)), 0).Format("2006-01-02T15:04:05"), - reorgRand.Float64(), - randStr(512+reorgRand.Intn(1024), reorgRand)) - } -} - -func checkDMLInAllStates(t *testing.T, tk, tk2 *testkit.TestKit, schemaName, alterStr string, - rows, pkInserts, pkUpdates, pkDeletes int, - reorgRand *rand.Rand, - getNewPK func(map[string]struct{}, string, *rand.Rand) string, - getValues func(string, bool, *rand.Rand) string) { - dom := domain.GetDomain(tk.Session()) - originHook := dom.DDL().GetHook() - defer dom.DDL().SetHook(originHook) - hook := newTestCallBack(t, dom) - dom.DDL().SetHook(hook) - - pkMap := make(map[string]struct{}, rows) - pkArray := make([]string, 0, len(pkMap)) - // Generate a start set: - for i := 0; i < rows; i++ { - pk := getNewPK(pkMap, "-o", reorgRand) - pkArray = append(pkArray, pk) - values := getValues(pk, false, reorgRand) - tk.MustExec(`insert into t values ` + values) - tk.MustExec(`insert into t2 values ` + values) - } - tk.MustExec(`analyze table t`) - tk.MustExec(`analyze table t2`) - tk.MustQuery(`select * from t except select * from t2`).Check(testkit.Rows()) - tk.MustQuery(`select * from t2 except select * from t`).Check(testkit.Rows()) - - // How to arrange data for possible collisions? - // change both PK column, SK column and non indexed column! - // Run various changes in transactions, in two concurrent sessions - // + mirror those transactions on a copy of the same table and data without DDL - // to verify expected transaction conflicts! - // We should try to collide: - // Current data : 1-1000 - // insert vN 1-200 // random order, random length of transaction? - // insert vN-1 100-300 // interleaved with vN, random order+length of txn? - // update vN 1-20, 100-120, 200-220, 300-320.. - // update vN-1 10-30, 110-130, 210-230, ... - // delete vN - // delete vN-1 - // insert update delete <- - // insert - // update - // delete - // Note: update the PK so it moves between different before and after partitions - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - currentState := model.StateNone - transitions := 0 - var currTbl table.Table - currSchema := sessiontxn.GetTxnManager(tk2.Session()).GetTxnInfoSchema() - prevTbl, err := currSchema.TableByName(model.NewCIStr(schemaName), model.NewCIStr("t")) - require.NoError(t, err) - var hookErr error - hook.OnJobRunBeforeExported = func(job *model.Job) { - if hookErr != nil { - // Enough to find a single error - return - } - if job.Type == model.ActionReorganizePartition && job.SchemaState != currentState { - transitions++ - // use random generation to possibly trigger txn collisions / deadlocks? - // insert (dup in new/old , non dup) - // update (dup in new/old , non dup as in same old/new partition -> update, different new/old -> insert + delete) - // delete - // verify with select after commit? - - logutil.BgLogger().Info("State before ins/upd/del", zap.Int("transitions", transitions), - zap.Int("rows", len(pkMap)), zap.Stringer("SchemaState", job.SchemaState)) - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - // Start with PK changes (non duplicate keys) - insPK := make([]string, 0, pkInserts) - values := make([]string, 0, pkInserts) - for i := 0; i < pkInserts; i += 2 { - pk := getNewPK(pkMap, "-i0", reorgRand) - logutil.BgLogger().Debug("insert1", zap.String("pk", pk)) - pkArray = append(pkArray, pk) - insPK = append(insPK, pk) - values = append(values, getValues(pk, false, reorgRand)) - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - hookErr = tk2.ExecToErr(`insert into t values ` + strings.Join(values, ",")) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`insert into t2 values ` + strings.Join(values, ",")) - if hookErr != nil { - return - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - currSchema = sessiontxn.GetTxnManager(tk2.Session()).GetTxnInfoSchema() - currTbl, hookErr = currSchema.TableByName(model.NewCIStr(schemaName), model.NewCIStr("t")) - - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using previous schema version - - values = values[:0] - for i := 1; i < pkInserts; i += 2 { - pk := getNewPK(pkMap, "-i1", reorgRand) - logutil.BgLogger().Debug("insert2", zap.String("pk", pk)) - pkArray = append(pkArray, pk) - insPK = append(insPK, pk) - values = append(values, getValues(pk, false, reorgRand)) - } - hookErr = tk2.ExecToErr(`insert into t values ` + strings.Join(values, ",")) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`insert into t2 values ` + strings.Join(values, ",")) - if hookErr != nil { - return - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - rs, err := tk2.Exec(`select count(*) from t`) - if err != nil { - hookErr = err - return - } - tRows := tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - rs, err = tk2.Exec(`select count(*) from t2`) - if err != nil { - hookErr = err - return - } - t2Rows := tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - if tRows != t2Rows { - logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) - } - - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using current schema version - - // Half from insert (1/4 in current schema version) - values = values[:0] - for i := 0; i < pkUpdates; i += 4 { - insIdx := reorgRand.Intn(len(insPK)) - oldPK := insPK[insIdx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - newPK := getNewPK(pkMap, "-u0", reorgRand) - insPK[insIdx] = newPK - idx := len(pkArray) - len(insPK) + insIdx - pkArray[idx] = newPK - value := getValues(newPK, true, reorgRand) - - logutil.BgLogger().Debug("update1", zap.String("old", oldPK), zap.String("value", value)) - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - - // Also do some non-pk column updates! - insIdx = reorgRand.Intn(len(insPK)) - oldPK = insPK[insIdx] - value = getValues(oldPK, true, reorgRand) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using previous schema version - - // Half from insert (1/4 in previous schema version) - values = values[:0] - for i := 1; i < pkUpdates; i += 4 { - insIdx := reorgRand.Intn(len(insPK)) - oldPK := insPK[insIdx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - newPK := getNewPK(pkMap, "-u1", reorgRand) - insPK[insIdx] = newPK - idx := len(pkArray) - len(insPK) + insIdx - pkArray[idx] = newPK - value := getValues(newPK, true, reorgRand) - logutil.BgLogger().Debug("update2", zap.String("old", oldPK), zap.String("value", value)) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - - // Also do some non-pk column updates! - // Note: if PK changes it does RemoveRecord + AddRecord - insIdx = reorgRand.Intn(len(insPK)) - oldPK = insPK[insIdx] - value = getValues(oldPK, true, reorgRand) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - // Half from Old - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using current schema version - - // Half from old (1/4 in current schema version) - values = values[:0] - for i := 2; i < pkUpdates; i += 4 { - idx := reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK := pkArray[idx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - newPK := getNewPK(pkMap, "-u2", reorgRand) - pkArray[idx] = newPK - value := getValues(newPK, true, reorgRand) - logutil.BgLogger().Debug("update3", zap.String("old", oldPK), zap.String("value", value)) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - - // Also do some non-pk column updates! - idx = reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK = pkArray[idx] - value = getValues(oldPK, true, reorgRand) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using previous schema version - - // Half from old (1/4 in previous schema version) - values = values[:0] - for i := 3; i < pkUpdates; i += 4 { - idx := reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK := pkArray[idx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - newPK := getNewPK(pkMap, "-u3", reorgRand) - pkArray[idx] = newPK - value := getValues(newPK, true, reorgRand) - logutil.BgLogger().Debug("update4", zap.String("old", oldPK), zap.String("value", value)) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - - // Also do some non-pk column updates! - idx = reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK = pkArray[idx] - value = getValues(oldPK, true, reorgRand) - - hookErr = tk2.ExecToErr(`update t set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`update t2 set ` + value + ` where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - rs, err = tk2.Exec(`select count(*) from t`) - if err != nil { - hookErr = err - return - } - tRows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - rs, err = tk2.Exec(`select count(*) from t2`) - if err != nil { - hookErr = err - return - } - t2Rows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - if tRows != t2Rows { - logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) - } - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using current schema version - - // Half from insert (1/4 in current schema version) - values = values[:0] - for i := 0; i < pkDeletes; i += 4 { - insIdx := reorgRand.Intn(len(insPK)) - oldPK := insPK[insIdx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - idx := len(pkArray) - len(insPK) + insIdx - insPK = append(insPK[:insIdx], insPK[insIdx+1:]...) - pkArray = append(pkArray[:idx], pkArray[idx+1:]...) - logutil.BgLogger().Debug("delete0", zap.String("pk", oldPK)) - - hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using previous schema version - - // Half from insert (1/4 in previous schema version) - values = values[:0] - for i := 1; i < pkDeletes; i += 4 { - insIdx := reorgRand.Intn(len(insPK)) - oldPK := insPK[insIdx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - idx := len(pkArray) - len(insPK) + insIdx - insPK = append(insPK[:insIdx], insPK[insIdx+1:]...) - pkArray = append(pkArray[:idx], pkArray[idx+1:]...) - logutil.BgLogger().Debug("delete1", zap.String("pk", oldPK)) - - hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - // Half from Old - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using current schema version - - // Half from old (1/4 in current schema version) - values = values[:0] - for i := 2; i < pkDeletes; i += 4 { - idx := reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK := pkArray[idx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - pkArray = append(pkArray[:idx], pkArray[idx+1:]...) - logutil.BgLogger().Debug("delete2", zap.String("pk", oldPK)) - - hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - if len(pkMap) != len(pkArray) { - panic("Different length!!!") - } - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using previous schema version - - // Half from old (1/4 in previous schema version) - values = values[:0] - for i := 3; i < pkDeletes; i += 4 { - idx := reorgRand.Intn(len(pkArray) - len(insPK)) - oldPK := pkArray[idx] - lowerPK := strings.ToLower(oldPK) - delete(pkMap, lowerPK) - pkArray = append(pkArray[:idx], pkArray[idx+1:]...) - logutil.BgLogger().Debug("delete3", zap.String("pk", oldPK)) - - hookErr = tk2.ExecToErr(`delete from t where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - hookErr = tk2.ExecToErr(`delete from t2 where a = "` + oldPK + `"`) - if hookErr != nil { - return - } - } - tk2.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk2.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - rs, err = tk2.Exec(`select count(*) from t`) - if err != nil { - hookErr = err - return - } - tRows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - rs, err = tk2.Exec(`select count(*) from t2`) - if err != nil { - hookErr = err - return - } - t2Rows = tk2.ResultSetToResult(rs, "").Rows()[0][0].(string) - if tRows != t2Rows { - logutil.BgLogger().Error("rows do not match", zap.String("t", tRows), zap.String("t2", t2Rows), zap.Stringer("state", job.SchemaState)) - } - - require.True(t, tables.SwapReorgPartFields(currTbl, prevTbl)) - // Now using current schema version - tk2.MustQuery(`select count(*) from t2`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - tk2.MustQuery(`select count(*) from t`).Check(testkit.Rows(fmt.Sprintf("%d", len(pkMap)))) - prevTbl = currTbl - logutil.BgLogger().Info("State after ins/upd/del", zap.Int("transitions", transitions), - zap.Int("rows", len(pkMap)), zap.Stringer("SchemaState", job.SchemaState)) - } - } - tk.MustExec(alterStr) - require.NoError(t, hookErr) - tk.MustExec(`admin check table t`) - tk.MustExec(`admin check table t2`) - tk.MustQuery(`select count(*) from (select a from t except select a from t2) a`).Check(testkit.Rows("0")) - tk.MustQuery(`select count(*) from (select a from t2 except select a from t) a`).Check(testkit.Rows("0")) - tk.MustQuery(`select * from t except select * from t2 LIMIT 1`).Check(testkit.Rows()) - tk.MustQuery(`select * from t2 except select * from t LIMIT 1`).Check(testkit.Rows()) -} - -// Emojis fold to a single rune, and ö compares as o, so just complicated having other runes. -// Enough to just distribute between A and Z + testing simple folding -var runes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - -func randStr(n int, r *rand.Rand) string { - var sb strings.Builder - sb.Grow(n) - for i := 0; i < n; i++ { - _, _ = sb.WriteRune(runes[r.Intn(len(runes))]) - } - return sb.String() -} diff --git a/table/tables/testutil.go b/table/tables/testutil.go deleted file mode 100644 index f6d513253cab9..0000000000000 --- a/table/tables/testutil.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tables - -import "github.com/pingcap/tidb/table" - -// SwapReorgPartFields swaps the reorganizePartitions field of two partitioned tables. used in tests. -func SwapReorgPartFields(src, dst table.Table) bool { - s, ok := src.(*partitionedTable) - if !ok { - return false - } - d, ok := dst.(*partitionedTable) - if !ok { - return false - } - d.reorganizePartitions, s.reorganizePartitions = s.reorganizePartitions, d.reorganizePartitions - d.meta, s.meta = s.meta, d.meta - d.partitions, s.partitions = s.partitions, d.partitions - d.doubleWritePartitions, s.doubleWritePartitions = s.doubleWritePartitions, d.doubleWritePartitions - d.reorgPartitionExpr, s.reorgPartitionExpr = s.reorgPartitionExpr, d.reorgPartitionExpr - d.partitionExpr, s.partitionExpr = s.partitionExpr, d.partitionExpr - return true -} diff --git a/table/temptable/BUILD.bazel b/table/temptable/BUILD.bazel deleted file mode 100644 index 115557b5dc0d5..0000000000000 --- a/table/temptable/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "temptable", - srcs = [ - "ddl.go", - "infoschema.go", - "interceptor.go", - ], - importpath = "github.com/pingcap/tidb/table/temptable", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//meta", - "//meta/autoid", - "//parser/ast", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//store/driver/txn", - "//table", - "//table/tables", - "//tablecodec", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//tikv", - "@org_golang_x_exp//maps", - ], -) - -go_test( - name = "temptable_test", - timeout = "short", - srcs = [ - "ddl_test.go", - "interceptor_test.go", - "main_test.go", - ], - embed = [":temptable"], - flaky = True, - shard_count = 16, - deps = [ - "//infoschema", - "//kv", - "//meta/autoid", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//store/driver/txn", - "//store/mockstore", - "//table", - "//tablecodec", - "//testkit/testsetup", - "//types", - "//util/codec", - "//util/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/table/temptable/ddl.go b/table/temptable/ddl.go deleted file mode 100644 index d464cb3c48618..0000000000000 --- a/table/temptable/ddl.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package temptable - -import ( - "bytes" - "context" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/tikv/client-go/v2/tikv" -) - -// TemporaryTableDDL is an interface providing ddl operations for temporary table -type TemporaryTableDDL interface { - CreateLocalTemporaryTable(db *model.DBInfo, info *model.TableInfo) error - DropLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error - TruncateLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error -} - -// temporaryTableDDL implements temptable.TemporaryTableDDL -type temporaryTableDDL struct { - sctx sessionctx.Context -} - -func (d *temporaryTableDDL) CreateLocalTemporaryTable(db *model.DBInfo, info *model.TableInfo) error { - if _, err := ensureSessionData(d.sctx); err != nil { - return err - } - - tbl, err := newTemporaryTableFromTableInfo(d.sctx, info) - if err != nil { - return err - } - - return ensureLocalTemporaryTables(d.sctx).AddTable(db, tbl) -} - -func (d *temporaryTableDDL) DropLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error { - tbl, err := checkLocalTemporaryExistsAndReturn(d.sctx, schema, tblName) - if err != nil { - return err - } - - getLocalTemporaryTables(d.sctx).RemoveTable(schema, tblName) - return d.clearTemporaryTableRecords(tbl.Meta().ID) -} - -func (d *temporaryTableDDL) TruncateLocalTemporaryTable(schema model.CIStr, tblName model.CIStr) error { - oldTbl, err := checkLocalTemporaryExistsAndReturn(d.sctx, schema, tblName) - if err != nil { - return err - } - - oldTblInfo := oldTbl.Meta() - newTbl, err := newTemporaryTableFromTableInfo(d.sctx, oldTblInfo.Clone()) - if err != nil { - return err - } - - localTempTables := getLocalTemporaryTables(d.sctx) - db, _ := localTempTables.SchemaByTable(oldTblInfo) - localTempTables.RemoveTable(schema, tblName) - if err = localTempTables.AddTable(db, newTbl); err != nil { - return err - } - - return d.clearTemporaryTableRecords(oldTblInfo.ID) -} - -func (d *temporaryTableDDL) clearTemporaryTableRecords(tblID int64) error { - sessionData := getSessionData(d.sctx) - if sessionData == nil { - return nil - } - - tblPrefix := tablecodec.EncodeTablePrefix(tblID) - endKey := tablecodec.EncodeTablePrefix(tblID + 1) - iter, err := sessionData.Iter(tblPrefix, endKey) - if err != nil { - return err - } - - for iter.Valid() { - key := iter.Key() - if !bytes.HasPrefix(key, tblPrefix) { - break - } - - err = sessionData.DeleteTableKey(tblID, key) - if err != nil { - return err - } - - err = iter.Next() - if err != nil { - return err - } - } - - return nil -} - -func checkLocalTemporaryExistsAndReturn(sctx sessionctx.Context, schema model.CIStr, tblName model.CIStr) (table.Table, error) { - ident := ast.Ident{Schema: schema, Name: tblName} - localTemporaryTables := getLocalTemporaryTables(sctx) - if localTemporaryTables == nil { - return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(ident.String()) - } - - tbl, exist := localTemporaryTables.TableByName(schema, tblName) - if !exist { - return nil, infoschema.ErrTableNotExists.GenWithStackByArgs(ident.String()) - } - - return tbl, nil -} - -func getSessionData(sctx sessionctx.Context) variable.TemporaryTableData { - return sctx.GetSessionVars().TemporaryTableData -} - -func ensureSessionData(sctx sessionctx.Context) (variable.TemporaryTableData, error) { - sessVars := sctx.GetSessionVars() - if sessVars.TemporaryTableData == nil { - // Create this txn just for getting a MemBuffer. It's a little tricky - bufferTxn, err := sctx.GetStore().Begin(tikv.WithStartTS(0)) - if err != nil { - return nil, err - } - - sessVars.TemporaryTableData = variable.NewTemporaryTableData(bufferTxn.GetMemBuffer()) - } - - return sessVars.TemporaryTableData, nil -} - -func newTemporaryTableFromTableInfo(sctx sessionctx.Context, tbInfo *model.TableInfo) (table.Table, error) { - // Local temporary table uses a real table ID. - // We could mock a table ID, but the mocked ID might be identical to an existing - // real table, and then we'll get into trouble. - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnCacheTable) - err := kv.RunInNewTxn(ctx, sctx.GetStore(), true, func(ctx context.Context, txn kv.Transaction) error { - m := meta.NewMeta(txn) - tblID, err := m.GenGlobalID() - if err != nil { - return errors.Trace(err) - } - tbInfo.ID = tblID - tbInfo.State = model.StatePublic - return nil - }) - if err != nil { - return nil, err - } - - // AutoID is allocated in mocked.. - alloc := autoid.NewAllocatorFromTempTblInfo(tbInfo) - allocs := make([]autoid.Allocator, 0, 1) - if alloc != nil { - allocs = append(allocs, alloc) - } - return tables.TableFromMeta(autoid.NewAllocators(false, allocs...), tbInfo) -} - -// GetTemporaryTableDDL gets the temptable.TemporaryTableDDL from session context -func GetTemporaryTableDDL(sctx sessionctx.Context) TemporaryTableDDL { - return &temporaryTableDDL{ - sctx: sctx, - } -} diff --git a/table/temptable/ddl_test.go b/table/temptable/ddl_test.go deleted file mode 100644 index fbca5f6bcdc71..0000000000000 --- a/table/temptable/ddl_test.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package temptable - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func createTestSuite(t *testing.T) (sessionctx.Context, *temporaryTableDDL) { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - - sctx := mock.NewContext() - sctx.Store = store - ddl := GetTemporaryTableDDL(sctx).(*temporaryTableDDL) - t.Cleanup(func() { - require.NoError(t, store.Close()) - }) - - return sctx, ddl -} - -func TestAddLocalTemporaryTable(t *testing.T) { - sctx, ddl := createTestSuite(t) - - sessVars := sctx.GetSessionVars() - - db1 := newMockSchema("db1") - db2 := newMockSchema("db2") - tbl1 := newMockTable("t1") - tbl2 := newMockTable("t2") - - require.Nil(t, sessVars.LocalTemporaryTables) - require.Nil(t, sessVars.TemporaryTableData) - - // insert t1 - err := ddl.CreateLocalTemporaryTable(db1, tbl1) - require.NoError(t, err) - require.NotNil(t, sessVars.LocalTemporaryTables) - require.NotNil(t, sessVars.TemporaryTableData) - require.Equal(t, int64(1), tbl1.ID) - got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1) - - // insert t2 with data - err = ddl.CreateLocalTemporaryTable(db1, tbl2) - require.NoError(t, err) - require.Equal(t, int64(2), tbl2.ID) - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t2")) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl2) - - // should success to set a key for a table - k := tablecodec.EncodeRowKeyWithHandle(tbl1.ID, kv.IntHandle(1)) - err = sessVars.TemporaryTableData.SetTableKey(tbl1.ID, k, []byte("v1")) - require.NoError(t, err) - - val, err := sessVars.TemporaryTableData.Get(context.Background(), k) - require.NoError(t, err) - require.Equal(t, []byte("v1"), val) - - // insert dup table - tbl1x := newMockTable("t1") - err = ddl.CreateLocalTemporaryTable(db1, tbl1x) - require.True(t, infoschema.ErrTableExists.Equal(err)) - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1) - - // insert should be success for same table name in different db - err = ddl.CreateLocalTemporaryTable(db2, tbl1x) - require.NoError(t, err) - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db2"), model.NewCIStr("t1")) - require.Equal(t, int64(4), got.Meta().ID) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1x) - - // tbl1 still exist - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1) -} - -func TestRemoveLocalTemporaryTable(t *testing.T) { - sctx, ddl := createTestSuite(t) - - sessVars := sctx.GetSessionVars() - db1 := newMockSchema("db1") - - // remove when empty - err := ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - - // add one table - tbl1 := newMockTable("t1") - err = ddl.CreateLocalTemporaryTable(db1, tbl1) - require.NoError(t, err) - require.Equal(t, int64(1), tbl1.ID) - k := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) - err = sessVars.TemporaryTableData.SetTableKey(tbl1.ID, k, []byte("v1")) - require.NoError(t, err) - - // remove failed when table not found - err = ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t2")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - - // remove failed when table not found (same table name in different db) - err = ddl.DropLocalTemporaryTable(model.NewCIStr("db2"), model.NewCIStr("t1")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - - // check failed remove should have no effects - got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByID(tbl1.ID) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1) - val, err := sessVars.TemporaryTableData.Get(context.Background(), k) - require.NoError(t, err) - require.Equal(t, []byte("v1"), val) - - // remove success - err = ddl.DropLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.NoError(t, err) - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.Nil(t, got) - require.False(t, exists) - val, err = sessVars.TemporaryTableData.Get(context.Background(), k) - require.NoError(t, err) - require.Equal(t, []byte{}, val) -} - -func TestTruncateLocalTemporaryTable(t *testing.T) { - sctx, ddl := createTestSuite(t) - - sessVars := sctx.GetSessionVars() - db1 := newMockSchema("db1") - - // truncate when empty - err := ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - require.Nil(t, sessVars.LocalTemporaryTables) - require.Nil(t, sessVars.TemporaryTableData) - - // add one table - tbl1 := newMockTable("t1") - err = ddl.CreateLocalTemporaryTable(db1, tbl1) - require.Equal(t, int64(1), tbl1.ID) - require.NoError(t, err) - k := tablecodec.EncodeRowKeyWithHandle(1, kv.IntHandle(1)) - err = sessVars.TemporaryTableData.SetTableKey(1, k, []byte("v1")) - require.NoError(t, err) - - // truncate failed for table not exist - err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t2")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db2"), model.NewCIStr("t1")) - require.True(t, infoschema.ErrTableNotExists.Equal(err)) - - // check failed should have no effects - got, exists := sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, exists) - require.Equal(t, got.Meta(), tbl1) - val, err := sessVars.TemporaryTableData.Get(context.Background(), k) - require.NoError(t, err) - require.Equal(t, []byte("v1"), val) - - // insert a new tbl - tbl2 := newMockTable("t2") - err = ddl.CreateLocalTemporaryTable(db1, tbl2) - require.Equal(t, int64(2), tbl2.ID) - require.NoError(t, err) - k2 := tablecodec.EncodeRowKeyWithHandle(2, kv.IntHandle(1)) - err = sessVars.TemporaryTableData.SetTableKey(2, k2, []byte("v2")) - require.NoError(t, err) - - // truncate success - err = ddl.TruncateLocalTemporaryTable(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.NoError(t, err) - got, exists = sessVars.LocalTemporaryTables.(*infoschema.SessionTables).TableByName(model.NewCIStr("db1"), model.NewCIStr("t1")) - require.True(t, exists) - require.NotEqual(t, got.Meta(), tbl1) - require.Equal(t, int64(3), got.Meta().ID) - val, err = sessVars.TemporaryTableData.Get(context.Background(), k) - require.NoError(t, err) - require.Equal(t, []byte{}, val) - - // truncate just effect its own data - val, err = sessVars.TemporaryTableData.Get(context.Background(), k2) - require.NoError(t, err) - require.Equal(t, []byte("v2"), val) -} - -func newMockTable(tblName string) *model.TableInfo { - c1 := &model.ColumnInfo{ID: 1, Name: model.NewCIStr("c1"), State: model.StatePublic, Offset: 0, FieldType: *types.NewFieldType(mysql.TypeLonglong)} - c2 := &model.ColumnInfo{ID: 2, Name: model.NewCIStr("c2"), State: model.StatePublic, Offset: 1, FieldType: *types.NewFieldType(mysql.TypeVarchar)} - - tblInfo := &model.TableInfo{Name: model.NewCIStr(tblName), Columns: []*model.ColumnInfo{c1, c2}, PKIsHandle: true} - return tblInfo -} - -func newMockSchema(schemaName string) *model.DBInfo { - return &model.DBInfo{ID: 10, Name: model.NewCIStr(schemaName), State: model.StatePublic} -} diff --git a/table/temptable/infoschema.go b/table/temptable/infoschema.go deleted file mode 100644 index 515474f06e325..0000000000000 --- a/table/temptable/infoschema.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package temptable - -import ( - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/sessionctx" -) - -// AttachLocalTemporaryTableInfoSchema attach local temporary table information schema to is -func AttachLocalTemporaryTableInfoSchema(sctx sessionctx.Context, is infoschema.InfoSchema) infoschema.InfoSchema { - localTemporaryTables := getLocalTemporaryTables(sctx) - if localTemporaryTables == nil { - return is - } - if se, ok := is.(*infoschema.SessionExtendedInfoSchema); ok { - se.LocalTemporaryTablesOnce.Do(func() { - se.LocalTemporaryTables = localTemporaryTables - }) - return is - } - - return &infoschema.SessionExtendedInfoSchema{ - InfoSchema: is, - LocalTemporaryTables: localTemporaryTables, - } -} - -// DetachLocalTemporaryTableInfoSchema detach local temporary table information schema from is -func DetachLocalTemporaryTableInfoSchema(is infoschema.InfoSchema) infoschema.InfoSchema { - if attachedInfoSchema, ok := is.(*infoschema.SessionExtendedInfoSchema); ok { - return attachedInfoSchema.DetachTemporaryTableInfoSchema() - } - - return is -} - -func getLocalTemporaryTables(sctx sessionctx.Context) *infoschema.SessionTables { - localTemporaryTables := sctx.GetSessionVars().LocalTemporaryTables - if localTemporaryTables == nil { - return nil - } - - return localTemporaryTables.(*infoschema.SessionTables) -} - -func ensureLocalTemporaryTables(sctx sessionctx.Context) *infoschema.SessionTables { - sessVars := sctx.GetSessionVars() - if sessVars.LocalTemporaryTables == nil { - localTempTables := infoschema.NewSessionTables() - sessVars.LocalTemporaryTables = localTempTables - return localTempTables - } - - return sessVars.LocalTemporaryTables.(*infoschema.SessionTables) -} diff --git a/table/temptable/main_test.go b/table/temptable/main_test.go deleted file mode 100644 index 94bc60c619666..0000000000000 --- a/table/temptable/main_test.go +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package temptable - -import ( - "bytes" - "context" - "fmt" - "slices" - "testing" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} - -type mockedInfoSchema struct { - t *testing.T - infoschema.InfoSchema - tables map[int64]model.TempTableType -} - -func newMockedInfoSchema(t *testing.T) *mockedInfoSchema { - return &mockedInfoSchema{ - t: t, - tables: make(map[int64]model.TempTableType), - } -} - -func (is *mockedInfoSchema) AddTable(tempType model.TempTableType, id ...int64) *mockedInfoSchema { - for _, tblID := range id { - is.tables[tblID] = tempType - } - - return is -} - -func (is *mockedInfoSchema) TableByID(tblID int64) (table.Table, bool) { - tempType, ok := is.tables[tblID] - if !ok { - return nil, false - } - - tblInfo := &model.TableInfo{ - ID: tblID, - Name: model.NewCIStr(fmt.Sprintf("tb%d", tblID)), - Columns: []*model.ColumnInfo{{ - ID: 1, - Name: model.NewCIStr("col1"), - Offset: 0, - FieldType: *types.NewFieldType(mysql.TypeLonglong), - State: model.StatePublic, - }}, - Indices: []*model.IndexInfo{}, - TempTableType: tempType, - State: model.StatePublic, - } - - tbl, err := table.TableFromMeta(autoid.NewAllocators(false), tblInfo) - require.NoError(is.t, err) - - return tbl, true -} - -type mockedSnapshot struct { - *mockedRetriever -} - -func newMockedSnapshot(retriever *mockedRetriever) *mockedSnapshot { - return &mockedSnapshot{mockedRetriever: retriever} -} - -func (s *mockedSnapshot) SetOption(_ int, _ interface{}) { - require.FailNow(s.t, "SetOption not supported") -} - -type methodInvoke struct { - Method string - Args []interface{} - Ret []interface{} -} - -type mockedRetriever struct { - t *testing.T - data []*kv.Entry - dataMap map[string][]byte - invokes []*methodInvoke - - allowInvokes map[string]interface{} - errorMap map[string]error -} - -func newMockedRetriever(t *testing.T) *mockedRetriever { - return &mockedRetriever{t: t} -} - -func (r *mockedRetriever) SetData(data []*kv.Entry) *mockedRetriever { - lessFunc := func(i, j *kv.Entry) int { return bytes.Compare(i.Key, j.Key) } - if !slices.IsSortedFunc(data, lessFunc) { - data = append([]*kv.Entry{}, data...) - slices.SortFunc(data, lessFunc) - } - - r.data = data - r.dataMap = make(map[string][]byte) - for _, item := range r.data { - r.dataMap[string(item.Key)] = item.Value - } - return r -} - -func (r *mockedRetriever) InjectMethodError(method string, err error) *mockedRetriever { - if r.errorMap == nil { - r.errorMap = make(map[string]error) - } - r.errorMap[method] = err - return r -} - -func (r *mockedRetriever) SetAllowedMethod(methods ...string) *mockedRetriever { - r.allowInvokes = make(map[string]interface{}) - for _, m := range methods { - r.allowInvokes[m] = struct{}{} - } - return r -} - -func (r *mockedRetriever) ResetInvokes() { - r.invokes = nil -} - -func (r *mockedRetriever) GetInvokes() []*methodInvoke { - return r.invokes -} - -func (r *mockedRetriever) Get(ctx context.Context, k kv.Key) (val []byte, err error) { - r.checkMethodInvokeAllowed("Get") - if err = r.getMethodErr("Get"); err == nil { - var ok bool - val, ok = r.dataMap[string(k)] - if !ok { - err = kv.ErrNotExist - } - } - r.appendInvoke("Get", []interface{}{ctx, k}, []interface{}{val, err}) - return -} - -func (r *mockedRetriever) BatchGet(ctx context.Context, keys []kv.Key) (data map[string][]byte, err error) { - r.checkMethodInvokeAllowed("BatchGet") - if err = r.getMethodErr("BatchGet"); err == nil { - data = make(map[string][]byte) - for _, k := range keys { - val, ok := r.dataMap[string(k)] - if ok { - data[string(k)] = val - } - } - } - - r.appendInvoke("BatchGet", []interface{}{ctx, keys}, []interface{}{data, err}) - return -} - -func (r *mockedRetriever) checkMethodInvokeAllowed(method string) { - require.NotNil(r.t, r.allowInvokes, fmt.Sprintf("Invoke for '%s' is not allowed, should allow it first", method)) - require.Contains(r.t, r.allowInvokes, method, fmt.Sprintf("Invoke for '%s' is not allowed, should allow it first", method)) -} - -func (r *mockedRetriever) Iter(k kv.Key, upperBound kv.Key) (iter kv.Iterator, err error) { - r.checkMethodInvokeAllowed("Iter") - if err = r.getMethodErr("Iter"); err == nil { - data := make([]*kv.Entry, 0) - for _, item := range r.data { - if bytes.Compare(item.Key, k) >= 0 && (len(upperBound) == 0 || bytes.Compare(item.Key, upperBound) < 0) { - data = append(data, item) - } - } - mockIter := mock.NewMockIterFromRecords(r.t, data, true) - if nextErr := r.getMethodErr("IterNext"); nextErr != nil { - mockIter.InjectNextError(nextErr) - } - iter = mockIter - } - r.appendInvoke("Iter", []interface{}{k, upperBound}, []interface{}{iter, err}) - return -} - -func (r *mockedRetriever) IterReverse(k kv.Key, lowerBound kv.Key) (iter kv.Iterator, err error) { - r.checkMethodInvokeAllowed("IterReverse") - if err = r.getMethodErr("IterReverse"); err == nil { - data := make([]*kv.Entry, 0) - for i := 0; i < len(r.data); i++ { - item := r.data[len(r.data)-i-1] - if (len(k) == 0 || bytes.Compare(item.Key, k) < 0) && (len(lowerBound) == 0 || bytes.Compare(item.Key, lowerBound) >= 0) { - data = append(data, item) - } - } - mockIter := mock.NewMockIterFromRecords(r.t, data, true) - if nextErr := r.getMethodErr("IterReverseNext"); nextErr != nil { - mockIter.InjectNextError(nextErr) - } - iter = mockIter - } - r.appendInvoke("IterReverse", []interface{}{k}, []interface{}{iter, err}) - return -} - -func (r *mockedRetriever) appendInvoke(method string, args []interface{}, ret []interface{}) { - r.invokes = append(r.invokes, &methodInvoke{ - Method: method, - Args: args, - Ret: ret, - }) -} - -func (r *mockedRetriever) getMethodErr(method string) error { - if r.errorMap == nil { - return nil - } - - if err, ok := r.errorMap[method]; ok && err != nil { - return err - } - - return nil -} diff --git a/tablecodec/BUILD.bazel b/tablecodec/BUILD.bazel deleted file mode 100644 index 2a5f82acb90fb..0000000000000 --- a/tablecodec/BUILD.bazel +++ /dev/null @@ -1,56 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "tablecodec", - srcs = ["tablecodec.go"], - importpath = "github.com/pingcap/tidb/tablecodec", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//kv", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//structure", - "//types", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/rowcodec", - "//util/stringutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_tikv_client_go_v2//tikv", - ], -) - -go_test( - name = "tablecodec_test", - timeout = "short", - srcs = [ - "bench_test.go", - "main_test.go", - "tablecodec_test.go", - ], - embed = [":tablecodec"], - flaky = True, - shard_count = 23, - deps = [ - "//kv", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//types", - "//util/benchdaily", - "//util/codec", - "//util/collate", - "//util/rowcodec", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/tablecodec/bench_test.go b/tablecodec/bench_test.go deleted file mode 100644 index 65af86cd405d6..0000000000000 --- a/tablecodec/bench_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package tablecodec - -import ( - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/benchdaily" -) - -func BenchmarkEncodeRowKeyWithHandle(b *testing.B) { - for i := 0; i < b.N; i++ { - EncodeRowKeyWithHandle(100, kv.IntHandle(100)) - } -} - -func BenchmarkEncodeEndKey(b *testing.B) { - for i := 0; i < b.N; i++ { - EncodeRowKeyWithHandle(100, kv.IntHandle(100)) - EncodeRowKeyWithHandle(100, kv.IntHandle(101)) - } -} - -// BenchmarkEncodeRowKeyWithPrefixNex tests the performance of encoding row key with prefixNext -// PrefixNext() is slow than using EncodeRowKeyWithHandle. -// BenchmarkEncodeEndKey-4 20000000 97.2 ns/op -// BenchmarkEncodeRowKeyWithPrefixNex-4 10000000 121 ns/op -func BenchmarkEncodeRowKeyWithPrefixNex(b *testing.B) { - for i := 0; i < b.N; i++ { - sk := EncodeRowKeyWithHandle(100, kv.IntHandle(100)) - sk.PrefixNext() - } -} - -func BenchmarkDecodeRowKey(b *testing.B) { - rowKey := EncodeRowKeyWithHandle(100, kv.IntHandle(100)) - for i := 0; i < b.N; i++ { - _, err := DecodeRowKey(rowKey) - if err != nil { - b.Fatal(err) - } - } -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkEncodeRowKeyWithHandle, - BenchmarkEncodeEndKey, - BenchmarkEncodeRowKeyWithPrefixNex, - BenchmarkDecodeRowKey, - BenchmarkHasTablePrefix, - BenchmarkHasTablePrefixBuiltin, - BenchmarkEncodeValue, - ) -} diff --git a/tablecodec/main_test.go b/tablecodec/main_test.go deleted file mode 100644 index e8bd50fb0d1c8..0000000000000 --- a/tablecodec/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tablecodec - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/tablecodec/rowindexcodec/BUILD.bazel b/tablecodec/rowindexcodec/BUILD.bazel deleted file mode 100644 index 7fcc37045b167..0000000000000 --- a/tablecodec/rowindexcodec/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "rowindexcodec", - srcs = ["rowindexcodec.go"], - importpath = "github.com/pingcap/tidb/tablecodec/rowindexcodec", - visibility = ["//visibility:public"], -) - -go_test( - name = "rowindexcodec_test", - timeout = "short", - srcs = [ - "main_test.go", - "rowindexcodec_test.go", - ], - embed = [":rowindexcodec"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/tablecodec/rowindexcodec/main_test.go b/tablecodec/rowindexcodec/main_test.go deleted file mode 100644 index 8a1ee5618b228..0000000000000 --- a/tablecodec/rowindexcodec/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package rowindexcodec - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/telemetry/BUILD.bazel b/telemetry/BUILD.bazel deleted file mode 100644 index dacd85c8b394c..0000000000000 --- a/telemetry/BUILD.bazel +++ /dev/null @@ -1,87 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "telemetry", - srcs = [ - "data.go", - "data_cluster_hardware.go", - "data_cluster_info.go", - "data_feature_usage.go", - "data_slow_query.go", - "data_telemetry_host_extra.go", - "data_window.go", - "id.go", - "status.go", - "telemetry.go", - "ttl.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/telemetry", - visibility = ["//visibility:public"], - deps = [ - "//br/pkg/utils", - "//config", - "//domain/infosync", - "//infoschema", - "//kv", - "//metrics", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//sessionctx/variable", - "//util/logutil", - "//util/memory", - "//util/sqlexec", - "@com_github_google_uuid//:uuid", - "@com_github_iancoleman_strcase//:strcase", - "@com_github_pingcap_errors//:errors", - "@com_github_prometheus_client_golang//api", - "@com_github_prometheus_client_golang//api/prometheus/v1:prometheus", - "@com_github_prometheus_common//model", - "@com_github_shirou_gopsutil_v3//cpu", - "@com_github_shirou_gopsutil_v3//host", - "@com_github_tikv_client_go_v2//metrics", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "telemetry_test", - timeout = "short", - srcs = [ - "data_cluster_hardware_test.go", - "data_feature_usage_test.go", - "data_window_test.go", - "main_test.go", - "telemetry_test.go", - "util_test.go", - ], - embed = [":telemetry"], - flaky = True, - shard_count = 35, - deps = [ - "//autoid_service", - "//config", - "//ddl", - "//domain", - "//kv", - "//parser/model", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//store/mockstore", - "//store/mockstore/unistore", - "//testkit", - "//testkit/testsetup", - "@com_github_jeffail_gabs_v2//:gabs", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/telemetry/cte_test/BUILD.bazel b/telemetry/cte_test/BUILD.bazel deleted file mode 100644 index d0b5f15f75561..0000000000000 --- a/telemetry/cte_test/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "cte_test_test", - timeout = "short", - srcs = ["cte_test.go"], - flaky = True, - race = "on", - deps = [ - "//domain", - "//kv", - "//session", - "//store/mockstore", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_tests_v3//integration", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/telemetry/cte_test/cte_test.go b/telemetry/cte_test/cte_test.go deleted file mode 100644 index e53bacc19c4d6..0000000000000 --- a/telemetry/cte_test/cte_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cte_test - -import ( - "runtime" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/require" - "go.etcd.io/etcd/tests/v3/integration" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("syscall.Syscall"), - } - - goleak.VerifyTestMain(m, opts...) -} - -// TestCTEPreviewAndReport requires a separated binary -func TestCTEPreviewAndReport(t *testing.T) { - if runtime.GOOS == "windows" { - t.Skip("integration.NewClusterV3 will create file contains a colon which is not allowed on Windows") - } - integration.BeforeTestExternal(t) - - s := newSuite(t) - defer s.close() - - // By disableing telemetry by default, the global sysvar **and** config file defaults - // are all set to false, so that enabling telemetry in test become more complex. - // As telemetry is a feature that almost no user will manually enable, I'd remove these - // tests for now. - // They should be uncommented once the default behavious changed back to enabled in the - // future, otherwise they could just be deleted. - /* - config.GetGlobalConfig().EnableTelemetry = true - - tk := testkit.NewTestKit(t, s.store) - tk.MustExec("use test") - tk.MustExec("with cte as (select 1) select * from cte") - tk.MustExec("with recursive cte as (select 1) select * from cte") - tk.MustExec("with recursive cte(n) as (select 1 union select * from cte where n < 5) select * from cte") - tk.MustExec("select 1") - - res, err := telemetry.PreviewUsageData(s.se, s.etcdCluster.RandClient()) - require.NoError(t, err) - - jsonParsed, err := gabs.ParseJSON([]byte(res)) - require.NoError(t, err) - require.Equal(t, 1, int(jsonParsed.Path("featureUsage.cte.nonRecursiveCTEUsed").Data().(float64))) - require.Equal(t, 1, int(jsonParsed.Path("featureUsage.cte.recursiveUsed").Data().(float64))) - require.Equal(t, 4, int(jsonParsed.Path("featureUsage.cte.nonCTEUsed").Data().(float64))) - - err = telemetry.ReportUsageData(s.se, s.etcdCluster.RandClient()) - require.NoError(t, err) - - res, err = telemetry.PreviewUsageData(s.se, s.etcdCluster.RandClient()) - require.NoError(t, err) - - jsonParsed, err = gabs.ParseJSON([]byte(res)) - require.NoError(t, err) - require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.nonRecursiveCTEUsed").Data().(float64))) - require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.recursiveUsed").Data().(float64))) - require.Equal(t, 0, int(jsonParsed.Path("featureUsage.cte.nonCTEUsed").Data().(float64))) - */ -} - -type testSuite struct { - store kv.Storage - dom *domain.Domain - etcdCluster *integration.ClusterV3 - se session.Session - close func() -} - -func newSuite(t *testing.T) *testSuite { - suite := new(testSuite) - - store, err := mockstore.NewMockStore() - require.NoError(t, err) - suite.store = store - - session.SetSchemaLease(0) - session.DisableStats4Test() - - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - suite.dom = dom - - etcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - suite.etcdCluster = etcdCluster - - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - suite.se = se - - suite.close = func() { - suite.se.Close() - suite.etcdCluster.Terminate(t) - suite.dom.Close() - err = suite.store.Close() - require.NoError(t, err) - view.Stop() - } - - return suite -} diff --git a/telemetry/main_test.go b/telemetry/main_test.go deleted file mode 100644 index d6d1f24a58d84..0000000000000 --- a/telemetry/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package telemetry - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -var ( - GetTxnUsageInfo = getTxnUsageInfo -) - -func GetFeatureUsage(sctx sessionctx.Context) (*featureUsage, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnTelemetry) - return getFeatureUsage(ctx, sctx) -} - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("net/http.(*persistConn).writeLoop"), - goleak.IgnoreTopFunction("internal/poll.runtime_pollWait"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/telemetry/telemetry.go b/telemetry/telemetry.go deleted file mode 100644 index 645aad0e90c08..0000000000000 --- a/telemetry/telemetry.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package telemetry - -import ( - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - clientv3 "go.etcd.io/etcd/client/v3" - "go.uber.org/zap" -) - -const ( - // OwnerKey is the telemetry owner path that is saved to etcd. - OwnerKey = "/tidb/telemetry/owner" - // Prompt is the prompt for telemetry owner manager. - Prompt = "telemetry" - // ReportInterval is the interval of the report. - ReportInterval = 6 * time.Hour -) - -const ( - etcdOpTimeout = 3 * time.Second - uploadTimeout = 60 * time.Second - apiEndpoint = "https://telemetry.pingcap.com/api/v1/tidb/report" -) - -func getTelemetryGlobalVariable(ctx sessionctx.Context) (bool, error) { - val, err := ctx.GetSessionVars().GlobalVarsAccessor.GetGlobalSysVar(variable.TiDBEnableTelemetry) - return variable.TiDBOptOn(val), err -} - -// IsTelemetryEnabled check whether telemetry enabled. -func IsTelemetryEnabled(ctx sessionctx.Context) (bool, error) { - if !config.GetGlobalConfig().EnableTelemetry { - return false, nil - } - enabled, err := getTelemetryGlobalVariable(ctx) - if err != nil { - return false, errors.Trace(err) - } - return enabled, nil -} - -// PreviewUsageData returns a preview of the usage data that is going to be reported. -func PreviewUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) (string, error) { - if etcdClient == nil { - return "", nil - } - if enabled, err := IsTelemetryEnabled(ctx); err != nil || !enabled { - return "", err - } - - trackingID, err := GetTrackingID(etcdClient) - if err != nil { - return "", errors.Trace(err) - } - - // NOTE: trackingID may be empty. However, as a preview data, it is fine. - data := generateTelemetryData(ctx, trackingID) - - prettyJSON, err := json.MarshalIndent(data, "", " ") - if err != nil { - return "", errors.Trace(err) - } - - return string(prettyJSON), nil -} - -func reportUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) (bool, error) { - if etcdClient == nil { - // silently ignore - return false, nil - } - enabled, err := IsTelemetryEnabled(ctx) - if err != nil { - return false, err - } - if !enabled { - return false, errors.Errorf("telemetry is disabled") - } - - trackingID, err := GetTrackingID(etcdClient) - if err != nil { - return false, errors.Trace(err) - } - if len(trackingID) == 0 { - trackingID, err = ResetTrackingID(etcdClient) - if err != nil { - return false, errors.Trace(err) - } - } - - data := generateTelemetryData(ctx, trackingID) - postReportTelemetryData() - - rawJSON, err := json.Marshal(data) - if err != nil { - return false, errors.Trace(err) - } - - // TODO: We should use the context from domain, so that when request is blocked for a long time it will not - // affect TiDB shutdown. - reqCtx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnTelemetry) - reqCtx, cancel := context.WithTimeout(reqCtx, uploadTimeout) - defer cancel() - - req, err := http.NewRequestWithContext(reqCtx, "POST", apiEndpoint, bytes.NewReader(rawJSON)) - if err != nil { - return false, errors.Trace(err) - } - - req.Header.Add("Content-Type", "application/json") - logutil.BgLogger().Info(fmt.Sprintf("Uploading telemetry data to %s", apiEndpoint)) - resp, err := http.DefaultClient.Do(req) - if err != nil { - return false, errors.Trace(err) - } - err = resp.Body.Close() // We don't even want to know any response body. Just close it. - _ = err - if resp.StatusCode != http.StatusOK { - return false, errors.Errorf("Received non-Ok response when reporting usage data, http code: %d", resp.StatusCode) - } - - return true, nil -} - -// ReportUsageData generates the latest usage data and sends it to PingCAP. Status will be saved to etcd. Status update failures will be returned. -func ReportUsageData(ctx sessionctx.Context, etcdClient *clientv3.Client) error { - if etcdClient == nil { - // silently ignore - return nil - } - s := status{ - CheckAt: time.Now().Format(time.RFC3339), - } - reported, err := reportUsageData(ctx, etcdClient) - if err != nil { - s.IsError = true - s.ErrorMessage = err.Error() - } else { - s.IsRequestSent = reported - } - - return updateTelemetryStatus(s, etcdClient) -} - -// InitialRun reports the Telmetry configuration and trigger an initial run -func InitialRun(ctx sessionctx.Context, etcdClient *clientv3.Client) error { - enabled, err := IsTelemetryEnabled(ctx) - if err != nil { - return err - } - logutil.BgLogger().Info("Telemetry configuration", zap.String("endpoint", apiEndpoint), zap.Duration("report_interval", ReportInterval), zap.Bool("enabled", enabled)) - return ReportUsageData(ctx, etcdClient) -} diff --git a/telemetry/ttl.go b/telemetry/ttl.go deleted file mode 100644 index cb7b015589d06..0000000000000 --- a/telemetry/ttl.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package telemetry - -import ( - "context" - "fmt" - "math" - "time" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -const ( - // selectDeletedRowsOneDaySQL selects the deleted rows for each table of last day - selectDeletedRowsOneDaySQL = `SELECT parent_table_id, CAST(SUM(deleted_rows) AS SIGNED) - FROM - mysql.tidb_ttl_job_history - WHERE - status != 'running' - AND create_time >= CURDATE() - INTERVAL 7 DAY - AND finish_time >= CURDATE() - INTERVAL 1 DAY - AND finish_time < CURDATE() - GROUP BY parent_table_id;` - // selectDelaySQL selects the deletion delay in minute for each table at the end of last day - selectDelaySQL = `SELECT - parent_table_id, TIMESTAMPDIFF(MINUTE, MIN(tm), CURDATE()) AS ttl_minutes - FROM - ( - SELECT - table_id, - parent_table_id, - MAX(ttl_expire) AS tm - FROM - mysql.tidb_ttl_job_history - WHERE - create_time > CURDATE() - INTERVAL 7 DAY - AND finish_time < CURDATE() - AND status = 'finished' - AND JSON_VALID(summary_text) - AND summary_text ->> "$.scan_task_err" IS NULL - GROUP BY - table_id, parent_table_id - ) t - GROUP BY parent_table_id;` -) - -type ttlHistItem struct { - // LessThan is not null means it collects the count of items with condition [prevLessThan, LessThan) - // Notice that it's type is an int64 pointer to forbid serializing it when it is not set. - LessThan *int64 `json:"less_than,omitempty"` - // LessThanMax is true means the condition is [prevLessThan, MAX) - LessThanMax bool `json:"less_than_max,omitempty"` - // Count is the count of items that fit the condition - Count int64 `json:"count"` -} - -type ttlUsageCounter struct { - TTLJobEnabled bool `json:"ttl_job_enabled"` - TTLTables int64 `json:"ttl_table_count"` - TTLJobEnabledTables int64 `json:"ttl_job_enabled_tables"` - TTLHistDate string `json:"ttl_hist_date"` - TableHistWithDeleteRows []*ttlHistItem `json:"table_hist_with_delete_rows"` - TableHistWithDelayTime []*ttlHistItem `json:"table_hist_with_delay_time"` -} - -func int64Pointer(val int64) *int64 { - v := val - return &v -} - -func (c *ttlUsageCounter) UpdateTableHistWithDeleteRows(rows int64) { - for _, item := range c.TableHistWithDeleteRows { - if item.LessThanMax || rows < *item.LessThan { - item.Count++ - return - } - } -} - -func (c *ttlUsageCounter) UpdateTableHistWithDelayTime(tblCnt int, hours int64) { - for _, item := range c.TableHistWithDelayTime { - if item.LessThanMax || hours < *item.LessThan { - item.Count += int64(tblCnt) - return - } - } -} - -func getTTLUsageInfo(ctx context.Context, sctx sessionctx.Context) (counter *ttlUsageCounter) { - counter = &ttlUsageCounter{ - TTLJobEnabled: variable.EnableTTLJob.Load(), - TTLHistDate: time.Now().Add(-24 * time.Hour).Format(time.DateOnly), - TableHistWithDeleteRows: []*ttlHistItem{ - { - LessThan: int64Pointer(10 * 1000), - }, - { - LessThan: int64Pointer(100 * 1000), - }, - { - LessThan: int64Pointer(1000 * 1000), - }, - { - LessThan: int64Pointer(10000 * 1000), - }, - { - LessThanMax: true, - }, - }, - TableHistWithDelayTime: []*ttlHistItem{ - { - LessThan: int64Pointer(1), - }, - { - LessThan: int64Pointer(6), - }, - { - LessThan: int64Pointer(24), - }, - { - LessThan: int64Pointer(72), - }, - { - LessThanMax: true, - }, - }, - } - - is, ok := sctx.GetDomainInfoSchema().(infoschema.InfoSchema) - if !ok { - // it should never happen - logutil.BgLogger().Error(fmt.Sprintf("GetDomainInfoSchema returns a invalid type: %T", is)) - return - } - - ttlTables := make(map[int64]*model.TableInfo) - for _, db := range is.AllSchemas() { - for _, tbl := range is.SchemaTables(db.Name) { - tblInfo := tbl.Meta() - if tblInfo.State != model.StatePublic || tblInfo.TTLInfo == nil { - continue - } - - counter.TTLTables++ - if tblInfo.TTLInfo.Enable { - counter.TTLJobEnabledTables++ - } - ttlTables[tblInfo.ID] = tblInfo - } - } - - exec := sctx.(sqlexec.RestrictedSQLExecutor) - rows, _, err := exec.ExecRestrictedSQL(ctx, nil, selectDeletedRowsOneDaySQL) - if err != nil { - logutil.BgLogger().Error("exec sql error", zap.String("SQL", selectDeletedRowsOneDaySQL), zap.Error(err)) - } else { - for _, row := range rows { - counter.UpdateTableHistWithDeleteRows(row.GetInt64(1)) - } - } - - rows, _, err = exec.ExecRestrictedSQL(ctx, nil, selectDelaySQL) - if err != nil { - logutil.BgLogger().Error("exec sql error", zap.String("SQL", selectDelaySQL), zap.Error(err)) - } else { - noHistoryTables := len(ttlTables) - for _, row := range rows { - tblID := row.GetInt64(0) - tbl, ok := ttlTables[tblID] - if !ok { - // table not exist, maybe truncated or deleted - continue - } - noHistoryTables-- - - evalIntervalSQL := fmt.Sprintf( - "SELECT TIMESTAMPDIFF(HOUR, CURDATE() - INTERVAL %d MINUTE, CURDATE() - INTERVAL %s %s)", - row.GetInt64(1), tbl.TTLInfo.IntervalExprStr, ast.TimeUnitType(tbl.TTLInfo.IntervalTimeUnit).String(), - ) - - innerRows, _, err := exec.ExecRestrictedSQL(ctx, nil, evalIntervalSQL) - if err != nil || len(innerRows) == 0 { - logutil.BgLogger().Error("exec sql error or empty rows returned", zap.String("SQL", evalIntervalSQL), zap.Error(err)) - continue - } - - hours := innerRows[0].GetInt64(0) - counter.UpdateTableHistWithDelayTime(1, hours) - } - - // When no history found for a table, use max delay - counter.UpdateTableHistWithDelayTime(noHistoryTables, math.MaxInt64) - } - return -} diff --git a/testkit/BUILD.bazel b/testkit/BUILD.bazel deleted file mode 100644 index 599df02f24dca..0000000000000 --- a/testkit/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "testkit", - srcs = [ - "asynctestkit.go", - "dbtestkit.go", - "mocksessionmanager.go", - "mockstore.go", - "result.go", - "stepped.go", - "testkit.go", - ], - importpath = "github.com/pingcap/tidb/testkit", - visibility = ["//visibility:public"], - deps = [ - "//ddl/schematracker", - "//domain", - "//domain/infosync", - "//expression", - "//kv", - "//parser/ast", - "//parser/auth", - "//parser/terror", - "//planner/core", - "//resourcemanager", - "//session", - "//session/txninfo", - "//sessionctx", - "//sessionctx/variable", - "//store/driver", - "//store/mockstore", - "//testkit/testenv", - "//types", - "//util", - "//util/breakpoint", - "//util/chunk", - "//util/gctuner", - "//util/intest", - "//util/logutil", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@io_opencensus_go//stats/view", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "testkit_test", - timeout = "short", - srcs = ["testkit_test.go"], - embed = [":testkit"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/testkit/ddlhelper/BUILD.bazel b/testkit/ddlhelper/BUILD.bazel deleted file mode 100644 index 6aa73f18be149..0000000000000 --- a/testkit/ddlhelper/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "ddlhelper", - srcs = ["helper.go"], - importpath = "github.com/pingcap/tidb/testkit/ddlhelper", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//parser/ast", - "//parser/model", - ], -) diff --git a/testkit/ddlhelper/helper.go b/testkit/ddlhelper/helper.go deleted file mode 100644 index ade95056b97c7..0000000000000 --- a/testkit/ddlhelper/helper.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ddlhelper - -import ( - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" -) - -// BuildTableInfoFromAST builds model.TableInfo from a SQL statement. -// Note: TableID and PartitionID are left as uninitialized value. -func BuildTableInfoFromAST(s *ast.CreateTableStmt) (*model.TableInfo, error) { - return ddl.BuildTableInfoFromAST(s) -} diff --git a/testkit/external/BUILD.bazel b/testkit/external/BUILD.bazel deleted file mode 100644 index 4bd1adb891949..0000000000000 --- a/testkit/external/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "external", - srcs = ["util.go"], - importpath = "github.com/pingcap/tidb/testkit/external", - visibility = ["//visibility:public"], - deps = [ - "//domain", - "//parser/model", - "//table", - "//table/tables", - "//testkit", - "@com_github_stretchr_testify//require", - ], -) diff --git a/testkit/external/util.go b/testkit/external/util.go deleted file mode 100644 index 496330e0596fc..0000000000000 --- a/testkit/external/util.go +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package external - -import ( - "fmt" - "strings" - "testing" - - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -// GetTableByName gets table by name for test. -func GetTableByName(t *testing.T, tk *testkit.TestKit, db, table string) table.Table { - dom := domain.GetDomain(tk.Session()) - // Make sure the table schema is the new schema. - require.NoError(t, dom.Reload()) - tbl, err := dom.InfoSchema().TableByName(model.NewCIStr(db), model.NewCIStr(table)) - require.NoError(t, err) - return tbl -} - -// GetModifyColumn is used to get the changed column name after ALTER TABLE. -func GetModifyColumn(t *testing.T, tk *testkit.TestKit, db, tbl, colName string, allColumn bool) *table.Column { - tt := GetTableByName(t, tk, db, tbl) - colName = strings.ToLower(colName) - var cols []*table.Column - if allColumn { - cols = tt.(*tables.TableCommon).Columns - } else { - cols = tt.Cols() - } - for _, col := range cols { - if col.Name.L == colName { - return col - } - } - return nil -} - -// GetIndexID is used to get the index ID from full qualified name. -func GetIndexID(t *testing.T, tk *testkit.TestKit, dbName, tblName, idxName string) int64 { - is := domain.GetDomain(tk.Session()).InfoSchema() - tt, err := is.TableByName(model.NewCIStr(dbName), model.NewCIStr(tblName)) - require.NoError(t, err) - - for _, idx := range tt.Indices() { - if idx.Meta().Name.L == idxName { - return idx.Meta().ID - } - } - - require.FailNow(t, fmt.Sprintf("index %s not found(db: %s, tbl: %s)", idxName, dbName, tblName)) - return -1 -} diff --git a/testkit/mockstore.go b/testkit/mockstore.go deleted file mode 100644 index f5b64423b2fc4..0000000000000 --- a/testkit/mockstore.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -//go:build !codes - -package testkit - -import ( - "flag" - "sync" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl/schematracker" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/resourcemanager" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/store/mockstore" - tidbutil "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/gctuner" - "github.com/pingcap/tidb/util/logutil" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" - "go.uber.org/zap" -) - -// WithTiKV flag is only used for debugging locally with real tikv cluster. -var WithTiKV = flag.String("with-tikv", "", "address of tikv cluster, if set, running test with real tikv cluster") - -// CreateMockStore return a new mock kv.Storage. -func CreateMockStore(t testing.TB, opts ...mockstore.MockTiKVStoreOption) kv.Storage { - if *WithTiKV != "" { - var d driver.TiKVDriver - var err error - store, err := d.Open("tikv://" + *WithTiKV) - require.NoError(t, err) - - var dom *domain.Domain - dom, err = session.BootstrapSession(store) - t.Cleanup(func() { - dom.Close() - err := store.Close() - require.NoError(t, err) - view.Stop() - }) - require.NoError(t, err) - return store - } - t.Cleanup(func() { - view.Stop() - }) - gctuner.GlobalMemoryLimitTuner.Stop() - store, _ := CreateMockStoreAndDomain(t, opts...) - return store -} - -// DistExecutionContext is the context -// that used in Distributed execution test for Dist task framework and DDL. -type DistExecutionContext struct { - Store kv.Storage - domains []*domain.Domain - deletedDomains []*domain.Domain - t testing.TB - mu sync.Mutex -} - -// InitOwner select the last domain as DDL owner. -func (d *DistExecutionContext) InitOwner() { - d.mu.Lock() - defer d.mu.Unlock() - for _, dom := range d.domains { - dom.DDL().OwnerManager().RetireOwner() - } - err := d.domains[len(d.domains)-1].DDL().OwnerManager().CampaignOwner() - require.NoError(d.t, err) -} - -// SetOwner set one mock domain to DDL Owner by idx. -func (d *DistExecutionContext) SetOwner(idx int) { - d.mu.Lock() - defer d.mu.Unlock() - if idx >= len(d.domains) || idx < 0 { - require.NoError(d.t, errors.New("server idx out of bound")) - return - } - for _, dom := range d.domains { - dom.DDL().OwnerManager().RetireOwner() - } - err := d.domains[idx].DDL().OwnerManager().CampaignOwner() - require.NoError(d.t, err) -} - -// AddDomain add 1 domain which is not ddl owner. -func (d *DistExecutionContext) AddDomain() { - d.mu.Lock() - defer d.mu.Unlock() - dom := bootstrap4DistExecution(d.t, d.Store, 500*time.Millisecond) - dom.InfoSyncer().SetSessionManager(d.domains[0].InfoSyncer().GetSessionManager()) - dom.DDL().OwnerManager().RetireOwner() - d.domains = append(d.domains, dom) -} - -// DeleteDomain delete 1 domain by idx, set server0 as ddl owner if the deleted owner is ddl owner. -func (d *DistExecutionContext) DeleteDomain(idx int) { - d.mu.Lock() - defer d.mu.Unlock() - if idx >= len(d.domains) || idx < 0 { - require.NoError(d.t, errors.New("server idx out of bound")) - return - } - if len(d.domains) == 1 { - require.NoError(d.t, errors.New("can't delete server, since server num = 1")) - return - } - if d.domains[idx].DDL().OwnerManager().IsOwner() { - d.mu.Unlock() - d.SetOwner(0) - d.mu.Lock() - } - - d.deletedDomains = append(d.deletedDomains, d.domains[idx]) - d.domains = append(d.domains[:idx], d.domains[idx+1:]...) - - err := infosync.MockGlobalServerInfoManagerEntry.Delete(idx) - require.NoError(d.t, err) -} - -// Close cleanup running goroutines, release resources used. -func (d *DistExecutionContext) Close() { - d.t.Cleanup(func() { - d.mu.Lock() - defer d.mu.Unlock() - gctuner.GlobalMemoryLimitTuner.Stop() - - var wg tidbutil.WaitGroupWrapper - for _, dom := range d.deletedDomains { - wg.Run(dom.Close) - } - - for _, dom := range d.domains { - wg.Run(dom.Close) - } - - wg.Wait() - err := d.Store.Close() - require.NoError(d.t, err) - }) -} - -// GetDomain get domain by index. -func (d *DistExecutionContext) GetDomain(idx int) *domain.Domain { - return d.domains[idx] -} - -// NewDistExecutionContext create DistExecutionContext for testing. -func NewDistExecutionContext(t testing.TB, serverNum int) *DistExecutionContext { - store, err := mockstore.NewMockStore() - require.NoError(t, err) - gctuner.GlobalMemoryLimitTuner.Stop() - domains := make([]*domain.Domain, 0, serverNum) - sm := MockSessionManager{} - - var domInfo []string - for i := 0; i < serverNum; i++ { - dom := bootstrap4DistExecution(t, store, 500*time.Millisecond) - if i != serverNum-1 { - dom.SetOnClose(func() { /* don't delete the store in domain map */ }) - } - domains = append(domains, dom) - domains[i].InfoSyncer().SetSessionManager(&sm) - domInfo = append(domInfo, dom.DDL().GetID()) - } - logutil.BgLogger().Info("domain DDL IDs", zap.Strings("IDs", domInfo)) - - res := DistExecutionContext{ - schematracker.UnwrapStorage(store), domains, []*domain.Domain{}, t, sync.Mutex{}} - res.InitOwner() - return &res -} - -// CreateMockStoreAndDomain return a new mock kv.Storage and *domain.Domain. -func CreateMockStoreAndDomain(t testing.TB, opts ...mockstore.MockTiKVStoreOption) (kv.Storage, *domain.Domain) { - store, err := mockstore.NewMockStore(opts...) - require.NoError(t, err) - dom := bootstrap(t, store, 500*time.Millisecond) - sm := MockSessionManager{} - dom.InfoSyncer().SetSessionManager(&sm) - t.Cleanup(func() { - view.Stop() - gctuner.GlobalMemoryLimitTuner.Stop() - }) - return schematracker.UnwrapStorage(store), dom -} - -func bootstrap4DistExecution(t testing.TB, store kv.Storage, lease time.Duration) *domain.Domain { - session.SetSchemaLease(lease) - session.DisableStats4Test() - domain.DisablePlanReplayerBackgroundJob4Test() - domain.DisableDumpHistoricalStats4Test() - dom, err := session.BootstrapSession4DistExecution(store) - require.NoError(t, err) - - dom.SetStatsUpdating(true) - return dom -} - -func bootstrap(t testing.TB, store kv.Storage, lease time.Duration) *domain.Domain { - session.SetSchemaLease(lease) - session.DisableStats4Test() - domain.DisablePlanReplayerBackgroundJob4Test() - domain.DisableDumpHistoricalStats4Test() - dom, err := session.BootstrapSession(store) - require.NoError(t, err) - - dom.SetStatsUpdating(true) - - t.Cleanup(func() { - dom.Close() - view.Stop() - err := store.Close() - require.NoError(t, err) - resourcemanager.InstanceResourceManager.Reset() - }) - return dom -} - -// CreateMockStoreWithSchemaLease return a new mock kv.Storage. -func CreateMockStoreWithSchemaLease(t testing.TB, lease time.Duration, opts ...mockstore.MockTiKVStoreOption) kv.Storage { - store, _ := CreateMockStoreAndDomainWithSchemaLease(t, lease, opts...) - return schematracker.UnwrapStorage(store) -} - -// CreateMockStoreAndDomainWithSchemaLease return a new mock kv.Storage and *domain.Domain. -func CreateMockStoreAndDomainWithSchemaLease(t testing.TB, lease time.Duration, opts ...mockstore.MockTiKVStoreOption) (kv.Storage, *domain.Domain) { - store, err := mockstore.NewMockStore(opts...) - require.NoError(t, err) - dom := bootstrap(t, store, lease) - sm := MockSessionManager{} - dom.InfoSyncer().SetSessionManager(&sm) - return schematracker.UnwrapStorage(store), dom -} diff --git a/testkit/testdata/BUILD.bazel b/testkit/testdata/BUILD.bazel deleted file mode 100644 index ae3afb7a669e1..0000000000000 --- a/testkit/testdata/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testdata", - srcs = ["testdata.go"], - importpath = "github.com/pingcap/tidb/testkit/testdata", - visibility = ["//visibility:public"], - deps = [ - "//sessionctx/stmtctx", - "@com_github_stretchr_testify//require", - ], -) diff --git a/testkit/testenv/BUILD.bazel b/testkit/testenv/BUILD.bazel deleted file mode 100644 index 5f96a6c9dc517..0000000000000 --- a/testkit/testenv/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testenv", - srcs = ["testenv.go"], - importpath = "github.com/pingcap/tidb/testkit/testenv", - visibility = ["//visibility:public"], - deps = ["//util/mathutil"], -) diff --git a/testkit/testfork/BUILD.bazel b/testkit/testfork/BUILD.bazel deleted file mode 100644 index 0763499d939ed..0000000000000 --- a/testkit/testfork/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "testfork", - srcs = ["fork.go"], - importpath = "github.com/pingcap/tidb/testkit/testfork", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - ], -) - -go_test( - name = "testfork_test", - timeout = "short", - srcs = ["fork_test.go"], - embed = [":testfork"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/testkit/testkit.go b/testkit/testkit.go deleted file mode 100644 index 624a32c442f3d..0000000000000 --- a/testkit/testkit.go +++ /dev/null @@ -1,676 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -//go:build !codes - -package testkit - -import ( - "context" - "fmt" - "strings" - "sync" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testenv" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/intest" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tipb/go-binlog" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - "github.com/tikv/client-go/v2/tikvrpc" - "go.uber.org/atomic" - "google.golang.org/grpc" -) - -var testKitIDGenerator atomic.Uint64 - -// TestKit is a utility to run sql test. -type TestKit struct { - require *require.Assertions - assert *assert.Assertions - t testing.TB - store kv.Storage - session session.Session - alloc chunk.Allocator -} - -// NewTestKit returns a new *TestKit. -func NewTestKit(t testing.TB, store kv.Storage) *TestKit { - require.True(t, intest.InTest, "you should add --tags=intest when to test, see https://pingcap.github.io/tidb-dev-guide/get-started/setup-an-ide.html for help") - testenv.SetGOMAXPROCSForTest() - tk := &TestKit{ - require: require.New(t), - assert: assert.New(t), - t: t, - store: store, - alloc: chunk.NewAllocator(), - } - tk.RefreshSession() - - dom, _ := session.GetDomain(store) - sm := dom.InfoSyncer().GetSessionManager() - if sm != nil { - mockSm, ok := sm.(*MockSessionManager) - if ok { - mockSm.mu.Lock() - if mockSm.Conn == nil { - mockSm.Conn = make(map[uint64]session.Session) - } - mockSm.Conn[tk.session.GetSessionVars().ConnectionID] = tk.session - mockSm.mu.Unlock() - } - tk.session.SetSessionManager(sm) - } - - return tk -} - -// NewTestKitWithSession returns a new *TestKit. -func NewTestKitWithSession(t testing.TB, store kv.Storage, se session.Session) *TestKit { - return &TestKit{ - require: require.New(t), - assert: assert.New(t), - t: t, - store: store, - session: se, - alloc: chunk.NewAllocator(), - } -} - -// RefreshSession set a new session for the testkit -func (tk *TestKit) RefreshSession() { - tk.session = newSession(tk.t, tk.store) - // enforce sysvar cache loading, ref loadCommonGlobalVariableIfNeeded - tk.MustExec("select 3") -} - -// SetSession set the session of testkit -func (tk *TestKit) SetSession(session session.Session) { - tk.session = session - // enforce sysvar cache loading, ref loadCommonGlobalVariableIfNeeded - tk.MustExec("select 3") -} - -// Session return the session associated with the testkit -func (tk *TestKit) Session() session.Session { - return tk.session -} - -// MustExec executes a sql statement and asserts nil error. -func (tk *TestKit) MustExec(sql string, args ...interface{}) { - defer func() { - if tk.alloc != nil { - tk.alloc.Reset() - } - }() - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - tk.MustExecWithContext(ctx, sql, args...) -} - -// MustExecWithContext executes a sql statement and asserts nil error. -func (tk *TestKit) MustExecWithContext(ctx context.Context, sql string, args ...interface{}) { - res, err := tk.ExecWithContext(ctx, sql, args...) - comment := fmt.Sprintf("sql:%s, %v, error stack %v", sql, args, errors.ErrorStack(err)) - tk.require.NoError(err, comment) - - if res != nil { - tk.require.NoError(res.Close()) - } -} - -// MustQuery query the statements and returns result rows. -// If expected result is set it asserts the query result equals expected result. -func (tk *TestKit) MustQuery(sql string, args ...interface{}) *Result { - defer func() { - if tk.alloc != nil { - tk.alloc.Reset() - } - }() - return tk.MustQueryWithContext(context.Background(), sql, args...) -} - -// EventuallyMustQueryAndCheck query the statements and assert that -// result rows.lt will equal the expected results in waitFor time, periodically checking equality each tick. -// Note: retry can't ignore error of the statements. If statements returns error, it will break out. -func (tk *TestKit) EventuallyMustQueryAndCheck(sql string, args []interface{}, - expected [][]interface{}, waitFor time.Duration, tick time.Duration) { - defer func() { - if tk.alloc != nil { - tk.alloc.Reset() - } - }() - tk.require.Eventually(func() bool { - res := tk.MustQueryWithContext(context.Background(), sql, args...) - return res.Equal(expected) - }, waitFor, tick) -} - -// MustQueryWithContext query the statements and returns result rows. -func (tk *TestKit) MustQueryWithContext(ctx context.Context, sql string, args ...interface{}) *Result { - comment := fmt.Sprintf("sql:%s, args:%v", sql, args) - rs, err := tk.ExecWithContext(ctx, sql, args...) - tk.require.NoError(err, comment) - tk.require.NotNil(rs, comment) - return tk.ResultSetToResultWithCtx(ctx, rs, comment) -} - -// MustIndexLookup checks whether the plan for the sql is IndexLookUp. -func (tk *TestKit) MustIndexLookup(sql string, args ...interface{}) *Result { - tk.MustHavePlan(sql, "IndexLookUp", args...) - return tk.MustQuery(sql, args...) -} - -// MustPartition checks if the result execution plan must read specific partitions. -func (tk *TestKit) MustPartition(sql string, partitions string, args ...interface{}) *Result { - rs := tk.MustQuery("explain "+sql, args...) - ok := len(partitions) == 0 - for i := range rs.rows { - if len(partitions) == 0 && strings.Contains(rs.rows[i][3], "partition:") { - ok = false - } - // The data format is "table: t1, partition: p0,p1,p2" - if len(partitions) != 0 && strings.HasSuffix(rs.rows[i][3], "partition:"+partitions) { - ok = true - } - } - tk.require.True(ok) - return tk.MustQuery(sql, args...) -} - -// MustPartitionByList checks if the result execution plan must read specific partitions by list. -func (tk *TestKit) MustPartitionByList(sql string, partitions []string, args ...interface{}) *Result { - rs := tk.MustQuery("explain "+sql, args...) - ok := len(partitions) == 0 - for i := range rs.rows { - if ok { - tk.require.NotContains(rs.rows[i][3], "partition:") - } - for index, partition := range partitions { - if !ok && strings.Contains(rs.rows[i][3], "partition:"+partition) { - partitions = append(partitions[:index], partitions[index+1:]...) - } - } - } - if !ok { - tk.require.Len(partitions, 0) - } - return tk.MustQuery(sql, args...) -} - -// QueryToErr executes a sql statement and discard results. -func (tk *TestKit) QueryToErr(sql string, args ...interface{}) error { - comment := fmt.Sprintf("sql:%s, args:%v", sql, args) - res, err := tk.Exec(sql, args...) - tk.require.NoError(err, comment) - tk.require.NotNil(res, comment) - _, resErr := session.GetRows4Test(context.Background(), tk.session, res) - tk.require.NoError(res.Close()) - return resErr -} - -// ResultSetToResult converts sqlexec.RecordSet to testkit.Result. -// It is used to check results of execute statement in binary mode. -func (tk *TestKit) ResultSetToResult(rs sqlexec.RecordSet, comment string) *Result { - return tk.ResultSetToResultWithCtx(context.Background(), rs, comment) -} - -// ResultSetToResultWithCtx converts sqlexec.RecordSet to testkit.Result. -func (tk *TestKit) ResultSetToResultWithCtx(ctx context.Context, rs sqlexec.RecordSet, comment string) *Result { - rows, err := session.ResultSetToStringSlice(ctx, tk.session, rs) - tk.require.NoError(err, comment) - return &Result{rows: rows, comment: comment, assert: tk.assert, require: tk.require} -} - -func (tk *TestKit) hasPlan(sql string, plan string, args ...interface{}) (bool, *Result) { - rs := tk.MustQuery("explain "+sql, args...) - for i := range rs.rows { - if strings.Contains(rs.rows[i][0], plan) { - return true, rs - } - } - return false, rs -} - -// MustHavePlan checks if the result execution plan contains specific plan. -func (tk *TestKit) MustHavePlan(sql string, plan string, args ...interface{}) { - has, rs := tk.hasPlan(sql, plan, args...) - tk.require.True(has, fmt.Sprintf("%s doesn't have plan %s, full plan %v", sql, plan, rs.Rows())) -} - -// MustNotHavePlan checks if the result execution plan contains specific plan. -func (tk *TestKit) MustNotHavePlan(sql string, plan string, args ...interface{}) { - has, rs := tk.hasPlan(sql, plan, args...) - tk.require.False(has, fmt.Sprintf("%s shouldn't have plan %s, full plan %v", sql, plan, rs.Rows())) -} - -// HasTiFlashPlan checks if the result execution plan contains TiFlash plan. -func (tk *TestKit) HasTiFlashPlan(sql string, args ...interface{}) bool { - rs := tk.MustQuery("explain "+sql, args...) - for i := range rs.rows { - if strings.Contains(rs.rows[i][2], "tiflash") { - return true - } - } - return false -} - -// HasPlanForLastExecution checks if the execution plan of the last execution contains specific plan. -func (tk *TestKit) HasPlanForLastExecution(plan string) bool { - connID := tk.session.GetSessionVars().ConnectionID - rs := tk.MustQuery(fmt.Sprintf("explain for connection %d", connID)) - for i := range rs.rows { - if strings.Contains(rs.rows[i][0], plan) { - return true - } - } - return false -} - -// HasKeywordInOperatorInfo checks if the result execution plan contains specific keyword in the operator info. -func (tk *TestKit) HasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { - rs := tk.MustQuery("explain "+sql, args...) - for i := range rs.rows { - if strings.Contains(rs.rows[i][4], keyword) { - return true - } - } - return false -} - -// NotHasKeywordInOperatorInfo checks if the result execution plan doesn't contain specific keyword in the operator info. -func (tk *TestKit) NotHasKeywordInOperatorInfo(sql string, keyword string, args ...interface{}) bool { - rs := tk.MustQuery("explain "+sql, args...) - for i := range rs.rows { - if strings.Contains(rs.rows[i][4], keyword) { - return false - } - } - return true -} - -// HasPlan4ExplainFor checks if the result execution plan contains specific plan. -func (tk *TestKit) HasPlan4ExplainFor(result *Result, plan string) bool { - for i := range result.rows { - if strings.Contains(result.rows[i][0], plan) { - return true - } - } - return false -} - -// Exec executes a sql statement using the prepared stmt API -func (tk *TestKit) Exec(sql string, args ...interface{}) (sqlexec.RecordSet, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnOthers) - return tk.ExecWithContext(ctx, sql, args...) -} - -// ExecWithContext executes a sql statement using the prepared stmt API -func (tk *TestKit) ExecWithContext(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, err error) { - defer tk.Session().GetSessionVars().ClearAlloc(&tk.alloc, err != nil) - if len(args) == 0 { - sc := tk.session.GetSessionVars().StmtCtx - prevWarns := sc.GetWarnings() - var stmts []ast.StmtNode - if len(stmts) == 0 { - var err error - stmts, err = tk.session.Parse(ctx, sql) - if err != nil { - return nil, errors.Trace(err) - } - } - warns := sc.GetWarnings() - parserWarns := warns[len(prevWarns):] - tk.Session().GetSessionVars().SetAlloc(tk.alloc) - var rs0 sqlexec.RecordSet - for i, stmt := range stmts { - var rs sqlexec.RecordSet - var err error - if s, ok := stmt.(*ast.NonTransactionalDMLStmt); ok { - rs, err = session.HandleNonTransactionalDML(ctx, s, tk.Session()) - } else { - rs, err = tk.Session().ExecuteStmt(ctx, stmt) - } - if i == 0 { - rs0 = rs - if len(stmts) > 1 && rs != nil { - // The result of the first statement will be drained and closed later. To avoid leaking - // resource on the statement context, we'll need to store the result and close the - // resultSet before executing other statements. - rs0 = buildRowsRecordSet(ctx, rs) - // the `rs` can be closed safely now - terror.Call(rs.Close) - } - } else if rs != nil { - // other statements are executed, but the `ResultSet` is not returned, so close them here - terror.Call(rs.Close) - } - if err != nil { - tk.session.GetSessionVars().StmtCtx.AppendError(err) - return rs, errors.Trace(err) - } - } - if len(parserWarns) > 0 { - tk.session.GetSessionVars().StmtCtx.AppendWarnings(parserWarns) - } - return rs0, nil - } - - stmtID, _, _, err := tk.session.PrepareStmt(sql) - if err != nil { - return nil, errors.Trace(err) - } - params := expression.Args2Expressions4Test(args...) - tk.Session().GetSessionVars().SetAlloc(tk.alloc) - rs, err = tk.session.ExecutePreparedStmt(ctx, stmtID, params) - if err != nil { - return rs, errors.Trace(err) - } - err = tk.session.DropPreparedStmt(stmtID) - if err != nil { - return rs, errors.Trace(err) - } - return rs, nil -} - -// ExecToErr executes a sql statement and discard results. -func (tk *TestKit) ExecToErr(sql string, args ...interface{}) error { - res, err := tk.Exec(sql, args...) - if res != nil { - tk.require.NoError(res.Close()) - } - return err -} - -// MustExecToErr executes a sql statement and must return Error. -func (tk *TestKit) MustExecToErr(sql string, args ...interface{}) { - res, err := tk.Exec(sql, args...) - if res != nil { - tk.require.NoError(res.Close()) - } - tk.require.Error(err) -} - -func newSession(t testing.TB, store kv.Storage) session.Session { - se, err := session.CreateSession4Test(store) - require.NoError(t, err) - se.SetConnectionID(testKitIDGenerator.Inc()) - return se -} - -// RefreshConnectionID refresh the connection ID for session of the testkit -func (tk *TestKit) RefreshConnectionID() { - if tk.session != nil { - tk.session.SetConnectionID(testKitIDGenerator.Inc()) - } -} - -// MustGetErrCode executes a sql statement and assert it's error code. -func (tk *TestKit) MustGetErrCode(sql string, errCode int) { - _, err := tk.Exec(sql) - tk.require.Errorf(err, "sql: %s", sql) - originErr := errors.Cause(err) - tErr, ok := originErr.(*terror.Error) - tk.require.Truef(ok, "sql: %s, expect type 'terror.Error', but obtain '%T': %v", sql, originErr, originErr) - sqlErr := terror.ToSQLError(tErr) - tk.require.Equalf(errCode, int(sqlErr.Code), "sql: %s, Assertion failed, origin err:\n %v", sql, sqlErr) -} - -// MustGetErrMsg executes a sql statement and assert its error message. -func (tk *TestKit) MustGetErrMsg(sql string, errStr string) { - err := tk.ExecToErr(sql) - tk.require.EqualError(err, errStr) -} - -// MustGetDBError executes a sql statement and assert its terror. -func (tk *TestKit) MustGetDBError(sql string, dberr *terror.Error) { - err := tk.ExecToErr(sql) - tk.require.Truef(terror.ErrorEqual(err, dberr), "err %v", err) -} - -// MustContainErrMsg executes a sql statement and assert its error message containing errStr. -func (tk *TestKit) MustContainErrMsg(sql string, errStr interface{}) { - err := tk.ExecToErr(sql) - tk.require.Error(err) - tk.require.Contains(err.Error(), errStr) -} - -// MustMatchErrMsg executes a sql statement and assert its error message matching errRx. -func (tk *TestKit) MustMatchErrMsg(sql string, errRx interface{}) { - err := tk.ExecToErr(sql) - tk.require.Error(err) - tk.require.Regexp(errRx, err.Error()) -} - -// MustUseIndex checks if the result execution plan contains specific index(es). -func (tk *TestKit) MustUseIndex(sql string, index string, args ...interface{}) bool { - rs := tk.MustQuery("explain "+sql, args...) - for i := range rs.rows { - if strings.Contains(rs.rows[i][3], "index:"+index) { - return true - } - } - return false -} - -// MustUseIndex4ExplainFor checks if the result execution plan contains specific index(es). -func (tk *TestKit) MustUseIndex4ExplainFor(result *Result, index string) bool { - for i := range result.rows { - // It depends on whether we enable to collect the execution info. - if strings.Contains(result.rows[i][3], "index:"+index) { - return true - } - if strings.Contains(result.rows[i][4], "index:"+index) { - return true - } - } - return false -} - -// CheckExecResult checks the affected rows and the insert id after executing MustExec. -func (tk *TestKit) CheckExecResult(affectedRows, insertID int64) { - tk.require.Equal(int64(tk.Session().AffectedRows()), affectedRows) - tk.require.Equal(int64(tk.Session().LastInsertID()), insertID) -} - -// MustPointGet checks whether the plan for the sql is Point_Get. -func (tk *TestKit) MustPointGet(sql string, args ...interface{}) *Result { - rs := tk.MustQuery("explain "+sql, args...) - tk.require.Len(rs.rows, 1) - tk.require.Contains(rs.rows[0][0], "Point_Get", "plan %v", rs.rows[0][0]) - return tk.MustQuery(sql, args...) -} - -// UsedPartitions returns the partition names that will be used or all/dual. -func (tk *TestKit) UsedPartitions(sql string, args ...interface{}) *Result { - rs := tk.MustQuery("explain "+sql, args...) - var usedPartitions [][]string - for i := range rs.rows { - index := strings.Index(rs.rows[i][3], "partition:") - if index != -1 { - p := rs.rows[i][3][index+len("partition:"):] - partitions := strings.Split(strings.SplitN(p, " ", 2)[0], ",") - usedPartitions = append(usedPartitions, partitions) - } - } - comment := fmt.Sprintf("sql:%s, args:%v", sql, args) - return &Result{rows: usedPartitions, comment: comment, assert: tk.assert, require: tk.require} -} - -// WithPruneMode run test case under prune mode. -func WithPruneMode(tk *TestKit, mode variable.PartitionPruneMode, f func()) { - tk.MustExec("set @@tidb_partition_prune_mode=`" + string(mode) + "`") - tk.MustExec("set global tidb_partition_prune_mode=`" + string(mode) + "`") - f() -} - -func containGlobal(rs *Result) bool { - partitionNameCol := 2 - for i := range rs.rows { - if strings.Contains(rs.rows[i][partitionNameCol], "global") { - return true - } - } - return false -} - -// MustNoGlobalStats checks if there is no global stats. -func (tk *TestKit) MustNoGlobalStats(table string) bool { - if containGlobal(tk.MustQuery("show stats_meta where table_name like '" + table + "'")) { - return false - } - if containGlobal(tk.MustQuery("show stats_buckets where table_name like '" + table + "'")) { - return false - } - if containGlobal(tk.MustQuery("show stats_histograms where table_name like '" + table + "'")) { - return false - } - return true -} - -// CheckLastMessage checks last message after executing MustExec -func (tk *TestKit) CheckLastMessage(msg string) { - tk.require.Equal(tk.Session().LastMessage(), msg) -} - -// RequireEqual checks if actual is equal to the expected -func (tk *TestKit) RequireEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - tk.require.Equal(expected, actual, msgAndArgs...) -} - -// RequireNotEqual checks if actual is not equal to the expected -func (tk *TestKit) RequireNotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - tk.require.NotEqual(expected, actual, msgAndArgs...) -} - -// RequireNoError checks if error happens -func (tk *TestKit) RequireNoError(err error, msgAndArgs ...interface{}) { - tk.require.NoError(err, msgAndArgs) -} - -// RegionProperityClient is to get region properties. -type RegionProperityClient struct { - tikv.Client - mu struct { - sync.Mutex - failedOnce bool - count int64 - } -} - -// SendRequest is to mock send request. -func (c *RegionProperityClient) SendRequest(ctx context.Context, addr string, req *tikvrpc.Request, timeout time.Duration) (*tikvrpc.Response, error) { - if req.Type == tikvrpc.CmdDebugGetRegionProperties { - c.mu.Lock() - defer c.mu.Unlock() - c.mu.count++ - // Mock failure once. - if !c.mu.failedOnce { - c.mu.failedOnce = true - return &tikvrpc.Response{}, nil - } - } - return c.Client.SendRequest(ctx, addr, req, timeout) -} - -// MockPumpClient is a mock pump client. -type MockPumpClient struct{} - -// WriteBinlog is a mock method. -func (m MockPumpClient) WriteBinlog(ctx context.Context, in *binlog.WriteBinlogReq, opts ...grpc.CallOption) (*binlog.WriteBinlogResp, error) { - return &binlog.WriteBinlogResp{}, nil -} - -// PullBinlogs is a mock method. -func (m MockPumpClient) PullBinlogs(ctx context.Context, in *binlog.PullBinlogReq, opts ...grpc.CallOption) (binlog.Pump_PullBinlogsClient, error) { - return nil, nil -} - -var _ sqlexec.RecordSet = &rowsRecordSet{} - -type rowsRecordSet struct { - fields []*ast.ResultField - rows []chunk.Row - - idx int - - // this error is stored here to return in the future - err error -} - -func (r *rowsRecordSet) Fields() []*ast.ResultField { - return r.fields -} - -func (r *rowsRecordSet) Next(ctx context.Context, req *chunk.Chunk) error { - if r.err != nil { - return r.err - } - - req.Reset() - for r.idx < len(r.rows) { - if req.IsFull() { - return nil - } - req.AppendRow(r.rows[r.idx]) - r.idx++ - } - return nil -} - -func (r *rowsRecordSet) NewChunk(alloc chunk.Allocator) *chunk.Chunk { - fields := make([]*types.FieldType, 0, len(r.fields)) - for _, field := range r.fields { - fields = append(fields, &field.Column.FieldType) - } - if alloc != nil { - return alloc.Alloc(fields, 0, 1024) - } - return chunk.New(fields, 1024, 1024) -} - -func (r *rowsRecordSet) Close() error { - // do nothing - return nil -} - -// buildRowsRecordSet builds a `rowsRecordSet` from any `RecordSet` by draining all rows from it. -// It's used to store the result temporarily. After building a new RecordSet, the original rs (in the argument) can be -// closed safely. -func buildRowsRecordSet(ctx context.Context, rs sqlexec.RecordSet) sqlexec.RecordSet { - rows, err := session.GetRows4Test(ctx, nil, rs) - if err != nil { - return &rowsRecordSet{ - fields: rs.Fields(), - err: err, - } - } - - return &rowsRecordSet{ - fields: rs.Fields(), - rows: rows, - idx: 0, - } -} diff --git a/testkit/testmain/BUILD.bazel b/testkit/testmain/BUILD.bazel deleted file mode 100644 index 0b9be885c768b..0000000000000 --- a/testkit/testmain/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testmain", - srcs = [ - "bench.go", - "wrapper.go", - ], - importpath = "github.com/pingcap/tidb/testkit/testmain", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_goleak//:goleak"], -) diff --git a/testkit/testsetup/BUILD.bazel b/testkit/testsetup/BUILD.bazel deleted file mode 100644 index 928183d85e694..0000000000000 --- a/testkit/testsetup/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testsetup", - srcs = ["bridge.go"], - importpath = "github.com/pingcap/tidb/testkit/testsetup", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) diff --git a/testkit/testutil/BUILD.bazel b/testkit/testutil/BUILD.bazel deleted file mode 100644 index 25e0a5265f6d4..0000000000000 --- a/testkit/testutil/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "testutil", - srcs = [ - "handle.go", - "loghook.go", - "require.go", - ], - importpath = "github.com/pingcap/tidb/testkit/testutil", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/mysql", - "//sessionctx/stmtctx", - "//types", - "//util/codec", - "//util/collate", - "//util/logutil", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "testutil_test", - timeout = "short", - srcs = ["require_test.go"], - embed = [":testutil"], - flaky = True, - deps = [ - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/testkit/testutil/handle.go b/testkit/testutil/handle.go deleted file mode 100644 index 7e96b063e8ea6..0000000000000 --- a/testkit/testutil/handle.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -//go:build !codes - -package testutil - -import ( - "slices" - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/stretchr/testify/require" -) - -// MustNewCommonHandle create a common handle with given values. -func MustNewCommonHandle(t *testing.T, values ...interface{}) kv.Handle { - encoded, err := codec.EncodeKey(stmtctx.NewStmtCtx(), nil, types.MakeDatums(values...)...) - require.NoError(t, err) - ch, err := kv.NewCommonHandle(encoded) - require.NoError(t, err) - return ch -} - -// MaskSortHandles sorts the handles by lowest (fieldTypeBits - 1 - shardBitsCount) bits. -func MaskSortHandles(handles []int64, shardBitsCount int, fieldType byte) []int64 { - typeBitsLength := mysql.DefaultLengthOfMysqlTypes[fieldType] * 8 - const signBitCount = 1 - shiftBitsCount := 64 - typeBitsLength + shardBitsCount + signBitCount - ordered := make([]int64, len(handles)) - for i, h := range handles { - ordered[i] = h << shiftBitsCount >> shiftBitsCount - } - slices.Sort(ordered) - return ordered -} diff --git a/tests/globalkilltest/BUILD.bazel b/tests/globalkilltest/BUILD.bazel index ed31eb10ef0f8..ad929a04e2df0 100644 --- a/tests/globalkilltest/BUILD.bazel +++ b/tests/globalkilltest/BUILD.bazel @@ -9,8 +9,8 @@ go_test( ], flaky = True, deps = [ - "//testkit/testsetup", - "//util/logutil", + "//pkg/testkit/testsetup", + "//pkg/util/logutil", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", diff --git a/tests/globalkilltest/global_kill_test.go b/tests/globalkilltest/global_kill_test.go index eaadbdedf85ed..f6d6210d35fd6 100644 --- a/tests/globalkilltest/global_kill_test.go +++ b/tests/globalkilltest/global_kill_test.go @@ -29,7 +29,7 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/pingcap/errors" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" clientv3 "go.etcd.io/etcd/client/v3" "go.uber.org/zap" @@ -742,7 +742,7 @@ func TestServerIDUpgradeAndDowngrade(t *testing.T) { return s.mustConnectTiDB(t, *tidbStartPort+idx) } - // MaxTiDB32 is determined by `github.com/pingcap/tidb/util/globalconn.ldflagServerIDBits32` + // MaxTiDB32 is determined by `github.com/pingcap/tidb/pkg/util/globalconn.ldflagServerIDBits32` // See the ldflags in `Makefile`. // Also see `Domain.proposeServerID`. const MaxTiDB32 = 2 // (3^2 -1) x 0.9 @@ -807,7 +807,7 @@ func TestConnIDUpgradeAndDowngrade(t *testing.T) { tidb := s.mustStartTiDBWithPD(t, *tidbStartPort, *tidbStatusPort, *pdClientPath) defer s.stopService("tidb0", tidb, true) - // MaxConn32 is determined by `github.com/pingcap/tidb/util/globalconn.ldflagLocalConnIDBits32` + // MaxConn32 is determined by `github.com/pingcap/tidb/pkg/util/globalconn.ldflagLocalConnIDBits32` // See the ldflags in `Makefile`. // Also see `LockFreeCircularPool.Cap`. const MaxConn32 = 1<<4 - 1 diff --git a/tests/globalkilltest/main_test.go b/tests/globalkilltest/main_test.go index ef5e3eb152f82..2d27f1be3dfd8 100644 --- a/tests/globalkilltest/main_test.go +++ b/tests/globalkilltest/main_test.go @@ -18,7 +18,7 @@ import ( "os" "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" ) func TestMain(m *testing.M) { diff --git a/tests/graceshutdown/BUILD.bazel b/tests/graceshutdown/BUILD.bazel index 62727422a2665..54a1333610629 100644 --- a/tests/graceshutdown/BUILD.bazel +++ b/tests/graceshutdown/BUILD.bazel @@ -9,7 +9,7 @@ go_test( ], flaky = True, deps = [ - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_log//:log", diff --git a/tests/graceshutdown/main_test.go b/tests/graceshutdown/main_test.go index 8bf8392820147..f72736582352a 100644 --- a/tests/graceshutdown/main_test.go +++ b/tests/graceshutdown/main_test.go @@ -17,7 +17,7 @@ package graceshutdown import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/tests/readonlytest/BUILD.bazel b/tests/readonlytest/BUILD.bazel index d9870d4e96a66..43be31b700440 100644 --- a/tests/readonlytest/BUILD.bazel +++ b/tests/readonlytest/BUILD.bazel @@ -9,7 +9,7 @@ go_test( ], flaky = True, deps = [ - "//testkit/testsetup", + "//pkg/testkit/testsetup", "@com_github_go_sql_driver_mysql//:mysql", "@com_github_stretchr_testify//require", "@io_opencensus_go//stats/view", diff --git a/tests/readonlytest/main_test.go b/tests/readonlytest/main_test.go index 2dd04285455a1..3a84b229cbc5b 100644 --- a/tests/readonlytest/main_test.go +++ b/tests/readonlytest/main_test.go @@ -17,7 +17,7 @@ package readonlytest import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "go.uber.org/goleak" ) diff --git a/tests/realtikvtest/BUILD.bazel b/tests/realtikvtest/BUILD.bazel index 6403884a655e4..9a50f776291e2 100644 --- a/tests/realtikvtest/BUILD.bazel +++ b/tests/realtikvtest/BUILD.bazel @@ -6,16 +6,16 @@ go_library( importpath = "github.com/pingcap/tidb/tests/realtikvtest", visibility = ["//visibility:public"], deps = [ - "//config", - "//domain", - "//kv", - "//session", - "//sessionctx/variable", - "//store/driver", - "//store/mockstore", - "//testkit", - "//testkit/testmain", - "//testkit/testsetup", + "//pkg/config", + "//pkg/domain", + "//pkg/kv", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/store/driver", + "//pkg/store/mockstore", + "//pkg/testkit", + "//pkg/testkit/testmain", + "//pkg/testkit/testsetup", "@com_github_stretchr_testify//require", "@com_github_tikv_client_go_v2//tikv", "@com_github_tikv_client_go_v2//txnkv/transaction", diff --git a/tests/realtikvtest/addindextest/BUILD.bazel b/tests/realtikvtest/addindextest/BUILD.bazel index a64b378b48d89..53910ff9b5d98 100644 --- a/tests/realtikvtest/addindextest/BUILD.bazel +++ b/tests/realtikvtest/addindextest/BUILD.bazel @@ -10,9 +10,9 @@ go_library( importpath = "github.com/pingcap/tidb/tests/realtikvtest/addindextest", visibility = ["//visibility:public"], deps = [ - "//kv", - "//testkit", - "//util/logutil", + "//pkg/kv", + "//pkg/testkit", + "//pkg/util/logutil", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", "@org_uber_go_zap//:zap", @@ -38,26 +38,26 @@ go_test( "//br/pkg/lightning/backend/external", "//br/pkg/lightning/backend/local", "//br/pkg/storage", - "//config", - "//ddl", - "//ddl/copr", - "//ddl/ingest", - "//ddl/testutil", - "//ddl/util/callback", - "//disttask/framework/dispatcher", - "//disttask/framework/proto", - "//disttask/operator", - "//domain", - "//errno", - "//kv", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//table", - "//table/tables", - "//testkit", + "//pkg/config", + "//pkg/ddl", + "//pkg/ddl/copr", + "//pkg/ddl/ingest", + "//pkg/ddl/testutil", + "//pkg/ddl/util/callback", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/proto", + "//pkg/disttask/operator", + "//pkg/domain", + "//pkg/errno", + "//pkg/kv", + "//pkg/parser/model", + "//pkg/sessionctx", + "//pkg/sessionctx/variable", + "//pkg/table", + "//pkg/table/tables", + "//pkg/testkit", + "//pkg/util/chunk", "//tests/realtikvtest", - "//util/chunk", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_ngaut_pools//:pools", "@com_github_phayes_freeport//:freeport", diff --git a/tests/realtikvtest/addindextest/add_index_test.go b/tests/realtikvtest/addindextest/add_index_test.go index f0ca7905cb496..409e3f1ba99df 100644 --- a/tests/realtikvtest/addindextest/add_index_test.go +++ b/tests/realtikvtest/addindextest/add_index_test.go @@ -18,13 +18,13 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) @@ -165,10 +165,10 @@ func TestAddIndexDistBasic(t *testing.T) { tk.MustExec("alter table t1 add index idx(a);") tk.MustExec("admin check index t1 idx;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/scheduler/MockRunSubtaskContextCanceled", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockRunSubtaskContextCanceled", "1*return(true)")) tk.MustExec("alter table t1 add index idx1(a);") tk.MustExec("admin check index t1 idx1;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/MockRunSubtaskContextCanceled")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/MockRunSubtaskContextCanceled")) tk.MustExec(`set global tidb_enable_dist_task=0;`) } @@ -199,9 +199,9 @@ func TestAddIndexDistCancel(t *testing.T) { tk1.MustExec("admin cancel ddl jobs " + jobID) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionAddIndexSubTaskFinish", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionAddIndexSubTaskFinish", "1*return(true)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionAddIndexSubTaskFinish")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionAddIndexSubTaskFinish")) }() require.Error(t, tk.ExecToErr("alter table t add index idx(a);")) @@ -252,15 +252,15 @@ func TestAddIndexDistPauseAndResume(t *testing.T) { tk1.MustExec("admin pause ddl jobs " + jobID) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionAddIndexSubTaskFinish", "3*return(true)")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDMLExecutionOnPausedState", "return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionAddIndexSubTaskFinish", "3*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDMLExecutionOnPausedState", "return(true)")) tk.MustExec(`set global tidb_enable_dist_task=1;`) tk.MustExec("alter table t add index idx1(a);") tk.MustExec("admin check table t;") tk.MustExec("alter table t add index idx2(a);") tk.MustExec("admin check table t;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionAddIndexSubTaskFinish")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/mockDMLExecutionOnPausedState")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionAddIndexSubTaskFinish")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/mockDMLExecutionOnPausedState")) // dist task succeed, job paused and resumed. var hook = &callback.TestDDLCallback{Do: dom} @@ -274,9 +274,9 @@ func TestAddIndexDistPauseAndResume(t *testing.T) { } hook.OnJobUpdatedExported.Store(&resumeFunc) dom.DDL().SetHook(hook.Clone()) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/pauseAfterDistTaskSuccess", "1*return(true)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/pauseAfterDistTaskSuccess", "1*return(true)")) tk.MustExec("alter table t add index idx3(a);") tk.MustExec("admin check table t;") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/pauseAfterDistTaskSuccess")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/pauseAfterDistTaskSuccess")) tk.MustExec(`set global tidb_enable_dist_task=0;`) } diff --git a/tests/realtikvtest/addindextest/common.go b/tests/realtikvtest/addindextest/common.go index 7271c45e24a11..d91ff72dfdcbc 100644 --- a/tests/realtikvtest/addindextest/common.go +++ b/tests/realtikvtest/addindextest/common.go @@ -22,9 +22,9 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -505,13 +505,13 @@ type failpointsPath struct { } var failpoints = []failpointsPath{ - {"github.com/pingcap/tidb/ddl/mockHighLoadForAddIndex", "return"}, - {"github.com/pingcap/tidb/ddl/mockBackfillRunErr", "1*return"}, - {"github.com/pingcap/tidb/ddl/mockBackfillSlow", "return"}, - {"github.com/pingcap/tidb/ddl/MockCaseWhenParseFailure", "return(true)"}, - {"github.com/pingcap/tidb/ddl/mockHighLoadForMergeIndex", "return"}, - {"github.com/pingcap/tidb/ddl/mockMergeRunErr", "1*return"}, - {"github.com/pingcap/tidb/ddl/mockMergeSlow", "return"}, + {"github.com/pingcap/tidb/pkg/ddl/mockHighLoadForAddIndex", "return"}, + {"github.com/pingcap/tidb/pkg/ddl/mockBackfillRunErr", "1*return"}, + {"github.com/pingcap/tidb/pkg/ddl/mockBackfillSlow", "return"}, + {"github.com/pingcap/tidb/pkg/ddl/MockCaseWhenParseFailure", "return(true)"}, + {"github.com/pingcap/tidb/pkg/ddl/mockHighLoadForMergeIndex", "return"}, + {"github.com/pingcap/tidb/pkg/ddl/mockMergeRunErr", "1*return"}, + {"github.com/pingcap/tidb/pkg/ddl/mockMergeSlow", "return"}, } func useFailpoints(ctx *suiteContext, failpos int) { diff --git a/tests/realtikvtest/addindextest/compatibility.go b/tests/realtikvtest/addindextest/compatibility.go index 0e8521a6da9f6..5cd0a69141c08 100644 --- a/tests/realtikvtest/addindextest/compatibility.go +++ b/tests/realtikvtest/addindextest/compatibility.go @@ -17,8 +17,8 @@ package addindextest import ( "strconv" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/tests/realtikvtest/addindextest/global_sort_test.go b/tests/realtikvtest/addindextest/global_sort_test.go index 3a6d76cb529ba..bc324c74d763d 100644 --- a/tests/realtikvtest/addindextest/global_sort_test.go +++ b/tests/realtikvtest/addindextest/global_sort_test.go @@ -26,11 +26,11 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/backend/external" "github.com/pingcap/tidb/br/pkg/storage" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) @@ -61,7 +61,7 @@ func TestGlobalSortCleanupCloudFiles(t *testing.T) { store, dom := realtikvtest.CreateMockStoreAndDomainAndSetup(t) tk := testkit.NewTestKit(t, store) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished", "return()")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished", "return()")) tk.MustExec("drop database if exists addindexlit;") tk.MustExec("create database addindexlit;") tk.MustExec("use addindexlit;") @@ -109,7 +109,7 @@ func TestGlobalSortCleanupCloudFiles(t *testing.T) { require.Greater(t, jobID, int64(0)) require.Equal(t, 0, len(dataFiles)) require.Equal(t, 0, len(statFiles)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished")) } func TestGlobalSortMultiSchemaChange(t *testing.T) { diff --git a/tests/realtikvtest/addindextest/integration_test.go b/tests/realtikvtest/addindextest/integration_test.go index becc553e5a98d..b42fc23cc1192 100644 --- a/tests/realtikvtest/addindextest/integration_test.go +++ b/tests/realtikvtest/addindextest/integration_test.go @@ -24,14 +24,14 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/lightning/backend/local" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/ddl/testutil" - "github.com/pingcap/tidb/ddl/util/callback" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/ddl/testutil" + "github.com/pingcap/tidb/pkg/ddl/util/callback" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -209,7 +209,7 @@ func TestAddIndexIngestAdjustBackfillWorker(t *testing.T) { tk.MustExec(sb.String()) tk.MustQuery("split table t between (0) and (20000) regions 20;").Check(testkit.Rows("19 1")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum", `return(true)`)) done := make(chan error, 1) atomic.StoreInt32(&ddl.TestCheckWorkerNumber, 2) testutil.SessionExecInGoroutine(store, "addindexlit", "alter table t add index idx(a);", done) @@ -230,7 +230,7 @@ func TestAddIndexIngestAdjustBackfillWorker(t *testing.T) { } } - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/checkBackfillWorkerNum")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/checkBackfillWorkerNum")) tk.MustExec("admin check table t;") rows := tk.MustQuery("admin show ddl jobs 1;").Rows() require.Len(t, rows, 1) @@ -386,9 +386,9 @@ func TestAddIndexFinishImportError(t *testing.T) { for i := 0; i < 4; i++ { tk.MustExec(fmt.Sprintf("insert into t values (%d, %d);", i*10000, i*10000)) } - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/ingest/mockFinishImportErr", "1*return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/ingest/mockFinishImportErr", "1*return")) tk.MustExec("alter table t add index idx(a);") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/ingest/mockFinishImportErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/ingest/mockFinishImportErr")) tk.MustExec("admin check table t;") rows := tk.MustQuery("admin show ddl jobs 1;").Rows() //nolint: forcetypeassert @@ -471,11 +471,11 @@ func TestAddIndexBackfillLostUpdate(t *testing.T) { assert.NoError(t, err) } d.SetHook(hook) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateBeforeImport", "1*return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateBeforeImport", "1*return")) tk.MustExec("alter table t add unique index idx(b);") tk.MustExec("admin check table t;") tk.MustQuery("select * from t;").Check(testkit.Rows("1 2 1")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockDMLExecutionStateBeforeImport")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockDMLExecutionStateBeforeImport")) } func TestAddIndexPreCheckFailed(t *testing.T) { @@ -488,7 +488,7 @@ func TestAddIndexPreCheckFailed(t *testing.T) { tk.MustExec("create table t(id int primary key, b int, k int);") tk.MustExec("insert into t values (1, 1, 1);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/ingest/mockIngestCheckEnvFailed", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/ingest/mockIngestCheckEnvFailed", "return")) tk.MustGetErrMsg("alter table t add index idx(b);", "[ddl:8256]Check ingest environment failed: mock error") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/ingest/mockIngestCheckEnvFailed")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/ingest/mockIngestCheckEnvFailed")) } diff --git a/tests/realtikvtest/addindextest/main_test.go b/tests/realtikvtest/addindextest/main_test.go index a308c7831a249..5dfd2cce3dd5f 100644 --- a/tests/realtikvtest/addindextest/main_test.go +++ b/tests/realtikvtest/addindextest/main_test.go @@ -18,7 +18,7 @@ import ( "flag" "testing" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/pingcap/tidb/tests/realtikvtest" ) diff --git a/tests/realtikvtest/addindextest/operator_test.go b/tests/realtikvtest/addindextest/operator_test.go index 211bf0804e3a0..2eb087c381a92 100644 --- a/tests/realtikvtest/addindextest/operator_test.go +++ b/tests/realtikvtest/addindextest/operator_test.go @@ -22,19 +22,19 @@ import ( "github.com/ngaut/pools" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/ddl/copr" - "github.com/pingcap/tidb/ddl/ingest" - "github.com/pingcap/tidb/disttask/operator" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + "github.com/pingcap/tidb/pkg/ddl/copr" + "github.com/pingcap/tidb/pkg/ddl/ingest" + "github.com/pingcap/tidb/pkg/disttask/operator" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/sessionctx" + "github.com/pingcap/tidb/pkg/table" + "github.com/pingcap/tidb/pkg/table/tables" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/pingcap/tidb/util/chunk" "github.com/stretchr/testify/require" "golang.org/x/sync/errgroup" ) @@ -214,27 +214,27 @@ func TestBackfillOperatorPipelineException(t *testing.T) { operatorErrMsg string }{ { - failPointPath: "github.com/pingcap/tidb/ddl/mockScanRecordError", + failPointPath: "github.com/pingcap/tidb/pkg/ddl/mockScanRecordError", closeErrMsg: "context canceled", operatorErrMsg: "mock scan record error", }, { - failPointPath: "github.com/pingcap/tidb/ddl/scanRecordExec", + failPointPath: "github.com/pingcap/tidb/pkg/ddl/scanRecordExec", closeErrMsg: "context canceled", operatorErrMsg: "", }, { - failPointPath: "github.com/pingcap/tidb/ddl/mockWriteLocalError", + failPointPath: "github.com/pingcap/tidb/pkg/ddl/mockWriteLocalError", closeErrMsg: "context canceled", operatorErrMsg: "mock write local error", }, { - failPointPath: "github.com/pingcap/tidb/ddl/writeLocalExec", + failPointPath: "github.com/pingcap/tidb/pkg/ddl/writeLocalExec", closeErrMsg: "context canceled", operatorErrMsg: "", }, { - failPointPath: "github.com/pingcap/tidb/ddl/mockFlushError", + failPointPath: "github.com/pingcap/tidb/pkg/ddl/mockFlushError", closeErrMsg: "mock flush error", operatorErrMsg: "mock flush error", }, diff --git a/tests/realtikvtest/addindextest/workload.go b/tests/realtikvtest/addindextest/workload.go index 98067cbec0141..d337b119a592c 100644 --- a/tests/realtikvtest/addindextest/workload.go +++ b/tests/realtikvtest/addindextest/workload.go @@ -22,8 +22,8 @@ import ( "sync" "time" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/logutil" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/logutil" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/tests/realtikvtest/brietest/BUILD.bazel b/tests/realtikvtest/brietest/BUILD.bazel index 56b4b8e176cf0..a2cadcef3823b 100644 --- a/tests/realtikvtest/brietest/BUILD.bazel +++ b/tests/realtikvtest/brietest/BUILD.bazel @@ -12,13 +12,13 @@ go_test( flaky = True, race = "on", deps = [ - "//config", - "//executor", - "//parser/mysql", - "//sessionctx/binloginfo", - "//store/mockstore/mockcopr", - "//testkit", - "//testkit/testsetup", + "//pkg/config", + "//pkg/executor", + "//pkg/parser/mysql", + "//pkg/sessionctx/binloginfo", + "//pkg/store/mockstore/mockcopr", + "//pkg/testkit", + "//pkg/testkit/testsetup", "//tests/realtikvtest", "@com_github_pingcap_failpoint//:failpoint", "@com_github_pingcap_log//:log", diff --git a/tests/realtikvtest/brietest/backup_restore_test.go b/tests/realtikvtest/brietest/backup_restore_test.go index 2fa0ba6bebfd5..370a49e4b6ea2 100644 --- a/tests/realtikvtest/brietest/backup_restore_test.go +++ b/tests/realtikvtest/brietest/backup_restore_test.go @@ -19,8 +19,8 @@ import ( "path" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) diff --git a/tests/realtikvtest/brietest/binlog_test.go b/tests/realtikvtest/brietest/binlog_test.go index 988f69981bb4a..260b7fd170015 100644 --- a/tests/realtikvtest/brietest/binlog_test.go +++ b/tests/realtikvtest/brietest/binlog_test.go @@ -18,10 +18,10 @@ import ( "context" "testing" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/binloginfo" - "github.com/pingcap/tidb/store/mockstore/mockcopr" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/sessionctx/binloginfo" + "github.com/pingcap/tidb/pkg/store/mockstore/mockcopr" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/pingcap/tipb/go-binlog" "github.com/stretchr/testify/require" diff --git a/tests/realtikvtest/brietest/brie_test.go b/tests/realtikvtest/brietest/brie_test.go index cb7f14f101553..c72b164b66058 100644 --- a/tests/realtikvtest/brietest/brie_test.go +++ b/tests/realtikvtest/brietest/brie_test.go @@ -23,8 +23,8 @@ import ( "github.com/pingcap/failpoint" "github.com/pingcap/log" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" "go.uber.org/zap/zapcore" ) @@ -67,7 +67,7 @@ func TestShowBackupQueryRedact(t *testing.T) { tk := initTestKit(t) executor.ResetGlobalBRIEQueueForTest() - failpoint.Enable("github.com/pingcap/tidb/executor/block-on-brie", "return") + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/block-on-brie", "return") ch := make(chan any) go func() { tk := testkit.NewTestKit(t, tk.Session().GetStore()) @@ -101,7 +101,7 @@ func TestCancel(t *testing.T) { tk := initTestKit(t) executor.ResetGlobalBRIEQueueForTest() tk.MustExec("use test;") - failpoint.Enable("github.com/pingcap/tidb/executor/block-on-brie", "return") + failpoint.Enable("github.com/pingcap/tidb/pkg/executor/block-on-brie", "return") req := require.New(t) ch := make(chan struct{}) diff --git a/tests/realtikvtest/brietest/main_test.go b/tests/realtikvtest/brietest/main_test.go index 49ce3f9675220..cb7011d295488 100644 --- a/tests/realtikvtest/brietest/main_test.go +++ b/tests/realtikvtest/brietest/main_test.go @@ -17,7 +17,7 @@ package brietest import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "github.com/pingcap/tidb/tests/realtikvtest" "go.uber.org/goleak" ) diff --git a/tests/realtikvtest/flashbacktest/BUILD.bazel b/tests/realtikvtest/flashbacktest/BUILD.bazel index 857a9554d09a3..1af86e76f1c04 100644 --- a/tests/realtikvtest/flashbacktest/BUILD.bazel +++ b/tests/realtikvtest/flashbacktest/BUILD.bazel @@ -10,14 +10,14 @@ go_test( flaky = True, race = "on", deps = [ - "//ddl", - "//ddl/util", - "//domain", - "//errno", - "//meta", - "//parser/model", - "//testkit", - "//testkit/testsetup", + "//pkg/ddl", + "//pkg/ddl/util", + "//pkg/domain", + "//pkg/errno", + "//pkg/meta", + "//pkg/parser/model", + "//pkg/testkit", + "//pkg/testkit/testsetup", "//tests/realtikvtest", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//assert", diff --git a/tests/realtikvtest/flashbacktest/flashback_test.go b/tests/realtikvtest/flashbacktest/flashback_test.go index f40632484c4d5..bdf75056074bf 100644 --- a/tests/realtikvtest/flashbacktest/flashback_test.go +++ b/tests/realtikvtest/flashbacktest/flashback_test.go @@ -22,13 +22,13 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/ddl" - ddlutil "github.com/pingcap/tidb/ddl/util" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/meta" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/ddl" + ddlutil "github.com/pingcap/tidb/pkg/ddl/util" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/meta" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -81,7 +81,7 @@ func TestFlashback(t *testing.T) { require.NoError(t, err) injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec("insert t values (4), (5), (6)") @@ -91,7 +91,7 @@ func TestFlashback(t *testing.T) { require.Equal(t, tk.MustQuery("select max(a) from t").Rows()[0][0], "3") require.Equal(t, tk.MustQuery("select max(a) from t use index(i)").Rows()[0][0], "3") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -116,9 +116,9 @@ func TestPrepareFlashbackFailed(t *testing.T) { require.NoError(t, err) injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/mockPrepareMeetsEpochNotMatch", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/mockPrepareMeetsEpochNotMatch", `return(true)`)) tk.MustExec("insert t values (4), (5), (6)") tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -132,8 +132,8 @@ func TestPrepareFlashbackFailed(t *testing.T) { require.NoError(t, job.Decode([]byte(jobMeta))) require.Equal(t, job.ErrorCount, int64(0)) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/mockPrepareMeetsEpochNotMatch")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/mockPrepareMeetsEpochNotMatch")) } } @@ -165,7 +165,7 @@ func TestFlashbackAddDropIndex(t *testing.T) { require.Greater(t, tk.MustQuery("select count(*) from mysql.gc_delete_range").Rows()[0][0], prevGCCount) injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec("insert t values (4), (5), (6)") @@ -176,7 +176,7 @@ func TestFlashbackAddDropIndex(t *testing.T) { tk.MustGetErrCode("select max(a) from t use index(k)", errno.ErrKeyDoesNotExist) require.Equal(t, tk.MustQuery("select count(*) from mysql.gc_delete_range").Rows()[0][0], prevGCCount) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -210,7 +210,7 @@ func TestFlashbackAddDropModifyColumn(t *testing.T) { ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin") injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec("insert t values (4, 4), (5, 5), (6, 6)") @@ -224,7 +224,7 @@ func TestFlashbackAddDropModifyColumn(t *testing.T) { ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin") require.Equal(t, tk.MustQuery("select max(b) from t").Rows()[0][0], "3") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -263,7 +263,7 @@ func TestFlashbackBasicRenameDropCreateTable(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -273,7 +273,7 @@ func TestFlashbackBasicRenameDropCreateTable(t *testing.T) { require.Equal(t, tk.MustQuery("select max(a) from t1").Rows()[0][0], "6") require.Equal(t, tk.MustQuery("select count(*) from mysql.gc_delete_range").Rows()[0][0], prevGCCount) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -301,14 +301,14 @@ func TestFlashbackCreateDropTableWithData(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) tk.MustExec("admin check table t") require.Equal(t, tk.MustQuery("select count(a) from t").Rows()[0][0], "0") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -340,7 +340,7 @@ func TestFlashbackCreateDropSchema(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -350,7 +350,7 @@ func TestFlashbackCreateDropSchema(t *testing.T) { tk.MustGetErrCode("use test1", errno.ErrBadDB) tk.MustGetErrCode("use test2", errno.ErrBadDB) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -379,7 +379,7 @@ func TestFlashbackAutoID(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -390,7 +390,7 @@ func TestFlashbackAutoID(t *testing.T) { res = tk.MustQuery("select max(a) from t").Rows() require.Equal(t, res[0][0], "101") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -419,7 +419,7 @@ func TestFlashbackSequence(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -427,7 +427,7 @@ func TestFlashbackSequence(t *testing.T) { res = tk.MustQuery("select nextval(seq)").Rows() require.Equal(t, res[0][0], "101") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -463,7 +463,7 @@ func TestFlashbackPartitionTable(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -478,7 +478,7 @@ func TestFlashbackPartitionTable(t *testing.T) { require.Equal(t, res[0][1], "-1") require.Equal(t, res[0][2], "102") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -504,7 +504,7 @@ func TestFlashbackTmpTable(t *testing.T) { injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) @@ -520,13 +520,13 @@ func TestFlashbackTmpTable(t *testing.T) { injectSafeTS = oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) tk.MustExec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) tk.MustGetErrCode("select * from t", errno.ErrNoSuchTable) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } @@ -554,7 +554,7 @@ func TestFlashbackInProcessErrorMsg(t *testing.T) { tk.MustExec("insert into t values (1), (2), (3)") injectSafeTS := oracle.GoTimeToTS(oracle.GetTimeFromTS(ts).Add(100 * time.Second)) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/ddl/injectSafeTS", + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS", fmt.Sprintf("return(%v)", injectSafeTS))) hook := newTestCallBack(t, dom) @@ -575,7 +575,7 @@ func TestFlashbackInProcessErrorMsg(t *testing.T) { tk.Exec(fmt.Sprintf("flashback cluster to timestamp '%s'", oracle.GetTimeFromTS(ts))) dom.DDL().SetHook(originHook) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/ddl/injectSafeTS")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/injectSafeTS")) } } diff --git a/tests/realtikvtest/flashbacktest/main_test.go b/tests/realtikvtest/flashbacktest/main_test.go index 3c01059cef1f6..9223b647d4958 100644 --- a/tests/realtikvtest/flashbacktest/main_test.go +++ b/tests/realtikvtest/flashbacktest/main_test.go @@ -17,7 +17,7 @@ package flashbacktest import ( "testing" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "github.com/pingcap/tidb/tests/realtikvtest" "go.uber.org/goleak" ) diff --git a/tests/realtikvtest/importintotest/BUILD.bazel b/tests/realtikvtest/importintotest/BUILD.bazel index f7e1bf8c9bc84..64abe375bc580 100644 --- a/tests/realtikvtest/importintotest/BUILD.bazel +++ b/tests/realtikvtest/importintotest/BUILD.bazel @@ -23,24 +23,24 @@ go_test( "//br/pkg/mock/mocklocal", "//br/pkg/streamhelper", "//br/pkg/utils", - "//config", - "//disttask/framework/dispatcher", - "//disttask/framework/proto", - "//disttask/framework/scheduler", - "//disttask/framework/storage", - "//disttask/importinto", - "//domain/infosync", - "//executor", - "//executor/importer", - "//kv", - "//parser/auth", - "//parser/terror", - "//planner/core", - "//session", - "//testkit", + "//pkg/config", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/proto", + "//pkg/disttask/framework/scheduler", + "//pkg/disttask/framework/storage", + "//pkg/disttask/importinto", + "//pkg/domain/infosync", + "//pkg/executor", + "//pkg/executor/importer", + "//pkg/kv", + "//pkg/parser/auth", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/testkit", + "//pkg/util/dbterror/exeerrors", + "//pkg/util/sem", "//tests/realtikvtest", - "//util/dbterror/exeerrors", - "//util/sem", "@com_github_docker_go_units//:go-units", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_ngaut_pools//:pools", diff --git a/tests/realtikvtest/importintotest/detach_test.go b/tests/realtikvtest/importintotest/detach_test.go index 482a834a844bd..83ffa5ae03070 100644 --- a/tests/realtikvtest/importintotest/detach_test.go +++ b/tests/realtikvtest/importintotest/detach_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/executor" + "github.com/pingcap/tidb/pkg/executor" "github.com/stretchr/testify/require" ) @@ -44,7 +44,7 @@ func (s *mockGCSSuite) TestSameBehaviourDetachedOrNot() { executor.TestDetachedTaskFinished.Store(false) }) - s.enableFailpoint("github.com/pingcap/tidb/executor/testDetachedTaskFinished", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/testDetachedTaskFinished", "return(true)") s.tk.MustExec("SET SESSION TIME_ZONE = '+08:00';") for _, ca := range detachedCases { s.tk.MustExec("DROP DATABASE IF EXISTS test_detached;") diff --git a/tests/realtikvtest/importintotest/from_server_test.go b/tests/realtikvtest/importintotest/from_server_test.go index 787ca5a9aa0c9..8ac7597a4c49d 100644 --- a/tests/realtikvtest/importintotest/from_server_test.go +++ b/tests/realtikvtest/importintotest/from_server_test.go @@ -19,8 +19,8 @@ import ( "os" "path" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" ) func (s *mockGCSSuite) TestImportFromServer() { diff --git a/tests/realtikvtest/importintotest/import_into_test.go b/tests/realtikvtest/importintotest/import_into_test.go index 57b872eb4299a..50967db18d1a3 100644 --- a/tests/realtikvtest/importintotest/import_into_test.go +++ b/tests/realtikvtest/importintotest/import_into_test.go @@ -35,17 +35,17 @@ import ( "github.com/pingcap/tidb/br/pkg/mock" "github.com/pingcap/tidb/br/pkg/mock/mocklocal" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/domain/infosync" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" - "github.com/pingcap/tidb/util/sem" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/domain/infosync" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/util/sem" "github.com/stretchr/testify/require" pd "github.com/tikv/pd/client" clientv3 "go.etcd.io/etcd/client/v3" @@ -724,7 +724,7 @@ func (s *mockGCSSuite) TestChecksumNotMatch() { } func (s *mockGCSSuite) TestColumnsAndUserVars() { - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/storage/testSetLastTaskID", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/storage/testSetLastTaskID", "return(true)") s.tk.MustExec("DROP DATABASE IF EXISTS load_data;") s.tk.MustExec("CREATE DATABASE load_data;") s.tk.MustExec(`CREATE TABLE load_data.cols_and_vars (a INT, b INT, c int);`) @@ -820,8 +820,8 @@ func (s *mockGCSSuite) TestImportMode() { // NOTE: this case only runs when current instance is TiDB owner, if you run it locally, // better start a cluster without TiDB instance. - s.enableFailpoint("github.com/pingcap/tidb/parser/ast/forceRedactURL", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished", "return()") + s.enableFailpoint("github.com/pingcap/tidb/pkg/parser/ast/forceRedactURL", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished", "return()") sql := fmt.Sprintf(`IMPORT INTO load_data.import_mode FROM 'gs://test-load/import_mode-*.tsv?access-key=aaaaaa&secret-access-key=bbbbbb&endpoint=%s'`, gcsEndpoint) rows := s.tk.MustQuery(sql).Rows() s.Len(rows, 1) @@ -835,16 +835,16 @@ func (s *mockGCSSuite) TestImportMode() { intoNormalTime, intoImportTime = time.Time{}, time.Time{} switcher.EXPECT().ToImportMode(gomock.Any(), gomock.Any()).DoAndReturn(toImportModeFn).Times(1) switcher.EXPECT().ToNormalMode(gomock.Any(), gomock.Any()).DoAndReturn(toNormalModeFn).Times(1) - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/clearLastSwitchTime", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/waitBeforePostProcess", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/clearLastSwitchTime", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforePostProcess", "return(true)") s.tk.MustExec("truncate table load_data.import_mode;") sql = fmt.Sprintf(`IMPORT INTO load_data.import_mode FROM 'gs://test-load/import_mode-*.tsv?endpoint=%s'`, gcsEndpoint) s.tk.MustQuery(sql) s.tk.MustQuery("SELECT * FROM load_data.import_mode;").Sort().Check(testkit.Rows("1 11 111")) s.tk.MustExec("truncate table load_data.import_mode;") s.Greater(intoNormalTime, intoImportTime) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/clearLastSwitchTime")) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/waitBeforePostProcess")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/clearLastSwitchTime")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforePostProcess")) <-dispatcher.WaitCleanUpFinished // test disable_tikv_import_mode, should not call ToImportMode and ToNormalMode @@ -856,7 +856,7 @@ func (s *mockGCSSuite) TestImportMode() { <-dispatcher.WaitCleanUpFinished // test with multirocksdb - s.enableFailpoint("github.com/pingcap/tidb/ddl/util/IsRaftKv2", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/ddl/util/IsRaftKv2", "return(true)") s.tk.MustExec("truncate table load_data.import_mode;") sql = fmt.Sprintf(`IMPORT INTO load_data.import_mode FROM 'gs://test-load/import_mode-*.tsv?endpoint=%s'`, gcsEndpoint) s.tk.MustQuery(sql) @@ -864,15 +864,15 @@ func (s *mockGCSSuite) TestImportMode() { s.tk.MustExec("truncate table load_data.import_mode;") <-dispatcher.WaitCleanUpFinished - s.NoError(failpoint.Disable("github.com/pingcap/tidb/ddl/util/IsRaftKv2")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/ddl/util/IsRaftKv2")) // to normal mode should be called on error intoNormalTime, intoImportTime = time.Time{}, time.Time{} switcher.EXPECT().ToImportMode(gomock.Any(), gomock.Any()).DoAndReturn(toImportModeFn).Times(1) switcher.EXPECT().ToNormalMode(gomock.Any(), gomock.Any()).DoAndReturn(toNormalModeFn).Times(1) - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/waitBeforeSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/errorWhenSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/executor/importer/setLastImportJobID", `return(true)`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforeSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/errorWhenSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/importer/setLastImportJobID", `return(true)`) sql = fmt.Sprintf(`IMPORT INTO load_data.import_mode FROM 'gs://test-load/import_mode-*.tsv?access-key=aaaaaa&secret-access-key=bbbbbb&endpoint=%s'`, gcsEndpoint) err = s.tk.QueryToErr(sql) @@ -880,7 +880,7 @@ func (s *mockGCSSuite) TestImportMode() { s.Greater(intoNormalTime, intoImportTime) <-dispatcher.WaitCleanUpFinished s.checkTaskMetaRedacted(importer.TestLastImportJobID.Load()) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished")) } func (s *mockGCSSuite) TestRegisterTask() { @@ -930,17 +930,17 @@ func (s *mockGCSSuite) TestRegisterTask() { taskRegister.EXPECT().RegisterTaskOnce(gomock.Any()).DoAndReturn(mockedRegister).Times(1) taskRegister.EXPECT().Close(gomock.Any()).DoAndReturn(mockedClose).Times(1) s.tk.MustExec("truncate table load_data.register_task;") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/waitBeforeSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/errorWhenSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforeSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/errorWhenSortChunk", "return(true)") err := s.tk.QueryToErr(sql) s.Error(err) s.Greater(unregisterTime, registerTime) importinto.NewTaskRegisterWithTTL = backup - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/waitBeforeSortChunk")) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/errorWhenSortChunk")) - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/syncBeforeSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/storage/testSetLastTaskID", "return(true)") + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforeSortChunk")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/errorWhenSortChunk")) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/syncBeforeSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/storage/testSetLastTaskID", "return(true)") wg := sync.WaitGroup{} wg.Add(1) go func() { diff --git a/tests/realtikvtest/importintotest/job_test.go b/tests/realtikvtest/importintotest/job_test.go index 437af1a5b9710..b2a5a49dc6b55 100644 --- a/tests/realtikvtest/importintotest/job_test.go +++ b/tests/realtikvtest/importintotest/job_test.go @@ -28,17 +28,17 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/tidb/br/pkg/utils" - "github.com/pingcap/tidb/disttask/framework/proto" - "github.com/pingcap/tidb/disttask/framework/scheduler" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/disttask/framework/proto" + "github.com/pingcap/tidb/pkg/disttask/framework/scheduler" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" ) func (s *mockGCSSuite) compareJobInfoWithoutTime(jobInfo *importer.JobInfo, row []interface{}) { @@ -98,9 +98,9 @@ func (s *mockGCSSuite) TestShowJob() { s.ErrorIs(err, exeerrors.ErrLoadDataJobNotFound) // test show job by id using test_show_job1 - s.enableFailpoint("github.com/pingcap/tidb/executor/importer/setLastImportJobID", `return(true)`) - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/storage/testSetLastTaskID", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/parser/ast/forceRedactURL", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/importer/setLastImportJobID", `return(true)`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/storage/testSetLastTaskID", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/parser/ast/forceRedactURL", "return(true)") s.NoError(s.tk.Session().Auth(&auth.UserIdentity{Username: "test_show_job1", Hostname: "localhost"}, nil, nil, nil)) result1 := s.tk.MustQuery(fmt.Sprintf(`import into t1 FROM 'gs://test-show-job/t.csv?access-key=aaaaaa&secret-access-key=bbbbbb&endpoint=%s'`, gcsEndpoint)).Rows() @@ -183,7 +183,7 @@ func (s *mockGCSSuite) TestShowJob() { checkJobsMatch(rows) // show running jobs with 2 subtasks - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/scheduler/syncAfterSubtaskFinish", `return(true)`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/syncAfterSubtaskFinish", `return(true)`) s.server.CreateObject(fakestorage.Object{ ObjectAttrs: fakestorage.ObjectAttrs{BucketName: "test-show-job", Name: "t2.csv"}, Content: []byte("3\n4"), @@ -243,7 +243,7 @@ func (s *mockGCSSuite) TestShowJob() { jobInfo.Summary.ImportedRows = 4 s.compareJobInfoWithoutTime(jobInfo, rows[0]) // resume the scheduler, need disable failpoint first, otherwise the post-process subtask will be blocked - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/framework/scheduler/syncAfterSubtaskFinish")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/framework/scheduler/syncAfterSubtaskFinish")) scheduler.TestSyncChan <- struct{}{} }() s.tk.MustQuery(fmt.Sprintf(`import into t3 FROM 'gs://test-show-job/t*.csv?access-key=aaaaaa&secret-access-key=bbbbbb&endpoint=%s' with thread=1, __max_engine_size='1'`, gcsEndpoint)) @@ -338,7 +338,7 @@ func (s *mockGCSSuite) TestShowDetachedJob() { s.compareJobInfoWithoutTime(jobInfo, rows[0]) // subtask fail with error - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/errorWhenSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/errorWhenSortChunk", "return(true)") result3 := s.tk.MustQuery(fmt.Sprintf(`import into t3 FROM 'gs://test-show-detached-job/t.csv?endpoint=%s' with detached`, gcsEndpoint)).Rows() s.Len(result3, 1) @@ -407,8 +407,8 @@ func (s *mockGCSSuite) TestCancelJob() { } // cancel a running job created by self - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/waitBeforeSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/syncAfterJobStarted", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforeSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/syncAfterJobStarted", "return(true)") s.NoError(s.tk.Session().Auth(&auth.UserIdentity{Username: "test_cancel_job1", Hostname: "localhost"}, nil, nil, nil)) result1 := s.tk.MustQuery(fmt.Sprintf(`import into t1 FROM 'gs://test_cancel_job/t.csv?endpoint=%s' with detached`, gcsEndpoint)).Rows() @@ -456,10 +456,10 @@ func (s *mockGCSSuite) TestCancelJob() { // cancel job in post-process phase, using test_cancel_job2 s.NoError(s.tk.Session().Auth(&auth.UserIdentity{Username: "test_cancel_job2", Hostname: "localhost"}, nil, nil, nil)) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/waitBeforeSortChunk")) - s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/syncAfterJobStarted")) - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/syncBeforePostProcess", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/waitCtxDone", "return(true)") + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/waitBeforeSortChunk")) + s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/syncAfterJobStarted")) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/syncBeforePostProcess", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/waitCtxDone", "return(true)") result2 := s.tk.MustQuery(fmt.Sprintf(`import into t2 FROM 'gs://test_cancel_job/t.csv?endpoint=%s' with detached`, gcsEndpoint)).Rows() s.Len(result2, 1) @@ -510,8 +510,8 @@ func (s *mockGCSSuite) TestCancelJob() { // todo: enable it when https://github.com/pingcap/tidb/issues/44443 fixed //// cancel a pending job created by test_cancel_job2 using root - //s.NoError(failpoint.Disable("github.com/pingcap/tidb/disttask/importinto/syncAfterJobStarted")) - //s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/syncBeforeJobStarted", "return(true)") + //s.NoError(failpoint.Disable("github.com/pingcap/tidb/pkg/disttask/importinto/syncAfterJobStarted")) + //s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/syncBeforeJobStarted", "return(true)") //result2 := s.tk.MustQuery(fmt.Sprintf(`import into t2 FROM 'gs://test_cancel_job/t.csv?endpoint=%s' with detached`, // gcsEndpoint)).Rows() //s.Len(result2, 1) @@ -572,8 +572,8 @@ func (s *mockGCSSuite) TestJobFailWhenDispatchSubtask() { Step: importer.JobStepValidating, ErrorMessage: "injected error after StepImport", } - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/failWhenDispatchPostProcessSubtask", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/executor/importer/setLastImportJobID", `return(true)`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/failWhenDispatchPostProcessSubtask", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/importer/setLastImportJobID", `return(true)`) s.NoError(s.tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "localhost"}, nil, nil, nil)) err = s.tk.QueryToErr(fmt.Sprintf(`import into t1 FROM 'gs://fail_job_after_import/t.csv?endpoint=%s'`, gcsEndpoint)) s.ErrorContains(err, "injected error after StepImport") @@ -595,9 +595,9 @@ func (s *mockGCSSuite) TestKillBeforeFinish() { Content: []byte("1,11,111"), }) - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/syncBeforeSortChunk", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/executor/cancellableCtx", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/executor/importer/setLastImportJobID", `return(true)`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/syncBeforeSortChunk", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/cancellableCtx", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/importer/setLastImportJobID", `return(true)`) wg := sync.WaitGroup{} wg.Add(1) go func() { diff --git a/tests/realtikvtest/importintotest/main_test.go b/tests/realtikvtest/importintotest/main_test.go index 84fc0519a47f5..f9efd5ab42ab2 100644 --- a/tests/realtikvtest/importintotest/main_test.go +++ b/tests/realtikvtest/importintotest/main_test.go @@ -17,7 +17,7 @@ package importintotest import ( "testing" - "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/pkg/config" "github.com/pingcap/tidb/tests/realtikvtest" ) diff --git a/tests/realtikvtest/importintotest/one_parquet_test.go b/tests/realtikvtest/importintotest/one_parquet_test.go index 56cce384f7400..a26ef17793d06 100644 --- a/tests/realtikvtest/importintotest/one_parquet_test.go +++ b/tests/realtikvtest/importintotest/one_parquet_test.go @@ -22,8 +22,8 @@ import ( "time" "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -60,7 +60,7 @@ func (s *mockGCSSuite) TestDetachedLoadParquet() { s.tk.MustExec("TRUNCATE TABLE t;") s.T().Cleanup(func() { executor.TestDetachedTaskFinished.Store(false) }) - s.enableFailpoint("github.com/pingcap/tidb/executor/testDetachedTaskFinished", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/executor/testDetachedTaskFinished", "return(true)") sql := fmt.Sprintf(`IMPORT INTO t FROM 'gs://test-load-parquet/p?endpoint=%s' FORMAT 'parquet' WITH detached;`, gcsEndpoint) rows := s.tk.MustQuery(sql).Rows() diff --git a/tests/realtikvtest/importintotest/precheck_test.go b/tests/realtikvtest/importintotest/precheck_test.go index 37950b4544df7..c321e63df88fd 100644 --- a/tests/realtikvtest/importintotest/precheck_test.go +++ b/tests/realtikvtest/importintotest/precheck_test.go @@ -22,8 +22,8 @@ import ( brpb "github.com/pingcap/kvproto/pkg/brpb" "github.com/pingcap/log" "github.com/pingcap/tidb/br/pkg/streamhelper" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/util/dbterror/exeerrors" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/util/dbterror/exeerrors" "github.com/stretchr/testify/require" "go.uber.org/zap" ) diff --git a/tests/realtikvtest/importintotest/util_test.go b/tests/realtikvtest/importintotest/util_test.go index fd2dc487a4441..2afa9a2c5d41d 100644 --- a/tests/realtikvtest/importintotest/util_test.go +++ b/tests/realtikvtest/importintotest/util_test.go @@ -21,8 +21,8 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/tests/realtikvtest/importintotest2/BUILD.bazel b/tests/realtikvtest/importintotest2/BUILD.bazel index a5a26b714e371..0511ae63ec02a 100644 --- a/tests/realtikvtest/importintotest2/BUILD.bazel +++ b/tests/realtikvtest/importintotest2/BUILD.bazel @@ -11,12 +11,12 @@ go_test( race = "on", deps = [ "//br/pkg/lightning/common", - "//config", - "//infoschema", - "//kv", - "//meta/autoid", - "//parser/model", - "//testkit", + "//pkg/config", + "//pkg/infoschema", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser/model", + "//pkg/testkit", "//tests/realtikvtest", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_pingcap_failpoint//:failpoint", diff --git a/tests/realtikvtest/importintotest2/main_test.go b/tests/realtikvtest/importintotest2/main_test.go index c5557eff4f19d..645422f2a68cb 100644 --- a/tests/realtikvtest/importintotest2/main_test.go +++ b/tests/realtikvtest/importintotest2/main_test.go @@ -20,9 +20,9 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/tests/realtikvtest/importintotest2/write_after_import_test.go b/tests/realtikvtest/importintotest2/write_after_import_test.go index 81742193d8521..9fda65cca6998 100644 --- a/tests/realtikvtest/importintotest2/write_after_import_test.go +++ b/tests/realtikvtest/importintotest2/write_after_import_test.go @@ -22,10 +22,10 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/tidb/br/pkg/lightning/common" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/infoschema" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/testkit" clientv3 "go.etcd.io/etcd/client/v3" ) diff --git a/tests/realtikvtest/importintotest3/BUILD.bazel b/tests/realtikvtest/importintotest3/BUILD.bazel index fe21f916e73cc..bc7bc64b036c7 100644 --- a/tests/realtikvtest/importintotest3/BUILD.bazel +++ b/tests/realtikvtest/importintotest3/BUILD.bazel @@ -11,9 +11,9 @@ go_test( race = "on", deps = [ "//br/pkg/lightning/mydump", - "//config", - "//kv", - "//testkit", + "//pkg/config", + "//pkg/kv", + "//pkg/testkit", "//tests/realtikvtest", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_golang_snappy//:snappy", diff --git a/tests/realtikvtest/importintotest3/file_compression_test.go b/tests/realtikvtest/importintotest3/file_compression_test.go index 9a64b3b40cfdb..5bbf5fc8baa5e 100644 --- a/tests/realtikvtest/importintotest3/file_compression_test.go +++ b/tests/realtikvtest/importintotest3/file_compression_test.go @@ -24,7 +24,7 @@ import ( "github.com/golang/snappy" "github.com/klauspost/compress/zstd" "github.com/pingcap/tidb/br/pkg/lightning/mydump" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" ) func (s *mockGCSSuite) getCompressedData(compression mydump.Compression, data []byte) []byte { diff --git a/tests/realtikvtest/importintotest3/main_test.go b/tests/realtikvtest/importintotest3/main_test.go index c5557eff4f19d..645422f2a68cb 100644 --- a/tests/realtikvtest/importintotest3/main_test.go +++ b/tests/realtikvtest/importintotest3/main_test.go @@ -20,9 +20,9 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/tests/realtikvtest/importintotest4/BUILD.bazel b/tests/realtikvtest/importintotest4/BUILD.bazel index 9ae963e045b25..af8a76ef8a202 100644 --- a/tests/realtikvtest/importintotest4/BUILD.bazel +++ b/tests/realtikvtest/importintotest4/BUILD.bazel @@ -12,13 +12,13 @@ go_test( race = "on", deps = [ "//br/pkg/lightning/config", - "//config", - "//disttask/framework/dispatcher", - "//disttask/framework/storage", - "//disttask/importinto", - "//executor/importer", - "//kv", - "//testkit", + "//pkg/config", + "//pkg/disttask/framework/dispatcher", + "//pkg/disttask/framework/storage", + "//pkg/disttask/importinto", + "//pkg/executor/importer", + "//pkg/kv", + "//pkg/testkit", "//tests/realtikvtest", "@com_github_fsouza_fake_gcs_server//fakestorage", "@com_github_pingcap_failpoint//:failpoint", diff --git a/tests/realtikvtest/importintotest4/global_sort_test.go b/tests/realtikvtest/importintotest4/global_sort_test.go index e015286cf99af..4796ccb3edc09 100644 --- a/tests/realtikvtest/importintotest4/global_sort_test.go +++ b/tests/realtikvtest/importintotest4/global_sort_test.go @@ -25,11 +25,11 @@ import ( "time" "github.com/fsouza/fake-gcs-server/fakestorage" - "github.com/pingcap/tidb/disttask/framework/dispatcher" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/executor/importer" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/dispatcher" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/executor/importer" + "github.com/pingcap/tidb/pkg/testkit" "github.com/stretchr/testify/require" ) @@ -57,8 +57,8 @@ func (s *mockGCSSuite) TestGlobalSortBasic() { s.prepareAndUseDB("gsort_basic") s.tk.MustExec(`create table t (a bigint primary key, b varchar(100), c varchar(100), d int, key(a), key(c,d), key(d));`) - s.enableFailpoint("github.com/pingcap/tidb/parser/ast/forceRedactURL", "return(true)") - s.enableFailpoint("github.com/pingcap/tidb/disttask/framework/dispatcher/WaitCleanUpFinished", "return()") + s.enableFailpoint("github.com/pingcap/tidb/pkg/parser/ast/forceRedactURL", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/framework/dispatcher/WaitCleanUpFinished", "return()") sortStorageURI := fmt.Sprintf("gs://sorted/import?endpoint=%s&access-key=aaaaaa&secret-access-key=bbbbbb", gcsEndpoint) importSQL := fmt.Sprintf(`import into t FROM 'gs://gs-basic/t.*.csv?endpoint=%s' @@ -94,7 +94,7 @@ func (s *mockGCSSuite) TestGlobalSortBasic() { urlEqual(s.T(), redactedSortStorageURI, taskMeta.Plan.CloudStorageURI) // merge-sort data kv - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/forceMergeSort", `return("data")`) + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/forceMergeSort", `return("data")`) s.tk.MustExec("truncate table t") result = s.tk.MustQuery(importSQL).Rows() s.Len(result, 1) @@ -105,7 +105,7 @@ func (s *mockGCSSuite) TestGlobalSortBasic() { <-dispatcher.WaitCleanUpFinished // failed task, should clean up all sorted data too. - s.enableFailpoint("github.com/pingcap/tidb/disttask/importinto/failWhenDispatchWriteIngestSubtask", "return(true)") + s.enableFailpoint("github.com/pingcap/tidb/pkg/disttask/importinto/failWhenDispatchWriteIngestSubtask", "return(true)") s.tk.MustExec("truncate table t") result = s.tk.MustQuery(importSQL + ", detached").Rows() s.Len(result, 1) diff --git a/tests/realtikvtest/importintotest4/main_test.go b/tests/realtikvtest/importintotest4/main_test.go index c5557eff4f19d..645422f2a68cb 100644 --- a/tests/realtikvtest/importintotest4/main_test.go +++ b/tests/realtikvtest/importintotest4/main_test.go @@ -20,9 +20,9 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" diff --git a/tests/realtikvtest/importintotest4/split_file_test.go b/tests/realtikvtest/importintotest4/split_file_test.go index 1226073823eb9..13a0613fb9b48 100644 --- a/tests/realtikvtest/importintotest4/split_file_test.go +++ b/tests/realtikvtest/importintotest4/split_file_test.go @@ -22,9 +22,9 @@ import ( "github.com/fsouza/fake-gcs-server/fakestorage" "github.com/pingcap/tidb/br/pkg/lightning/config" - "github.com/pingcap/tidb/disttask/framework/storage" - "github.com/pingcap/tidb/disttask/importinto" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/disttask/framework/storage" + "github.com/pingcap/tidb/pkg/disttask/importinto" + "github.com/pingcap/tidb/pkg/testkit" ) func (s *mockGCSSuite) TestSplitFile() { diff --git a/tests/realtikvtest/pessimistictest/BUILD.bazel b/tests/realtikvtest/pessimistictest/BUILD.bazel index 2d6ed2f9cda29..7810a11bc791f 100644 --- a/tests/realtikvtest/pessimistictest/BUILD.bazel +++ b/tests/realtikvtest/pessimistictest/BUILD.bazel @@ -9,30 +9,30 @@ go_test( ], flaky = True, deps = [ - "//config", - "//domain", - "//errno", - "//expression", - "//kv", - "//parser", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/core", - "//session", - "//sessionctx/variable", - "//sessiontxn", - "//store/driver/error", - "//store/gcworker", - "//store/mockstore", - "//tablecodec", - "//testkit", - "//testkit/external", + "//pkg/config", + "//pkg/domain", + "//pkg/errno", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser", + "//pkg/parser/auth", + "//pkg/parser/model", + "//pkg/parser/mysql", + "//pkg/parser/terror", + "//pkg/planner/core", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/sessiontxn", + "//pkg/store/driver/error", + "//pkg/store/gcworker", + "//pkg/store/mockstore", + "//pkg/tablecodec", + "//pkg/testkit", + "//pkg/testkit/external", + "//pkg/types", + "//pkg/util/codec", + "//pkg/util/deadlockhistory", "//tests/realtikvtest", - "//types", - "//util/codec", - "//util/deadlockhistory", "@com_github_pingcap_errors//:errors", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", diff --git a/tests/realtikvtest/pessimistictest/pessimistic_test.go b/tests/realtikvtest/pessimistictest/pessimistic_test.go index 19f6162cf17cc..09df963b08d55 100644 --- a/tests/realtikvtest/pessimistictest/pessimistic_test.go +++ b/tests/realtikvtest/pessimistictest/pessimistic_test.go @@ -26,30 +26,30 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - storeerr "github.com/pingcap/tidb/store/driver/error" - "github.com/pingcap/tidb/store/gcworker" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/external" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/errno" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/auth" + "github.com/pingcap/tidb/pkg/parser/model" + "github.com/pingcap/tidb/pkg/parser/mysql" + "github.com/pingcap/tidb/pkg/parser/terror" + plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/sessiontxn" + storeerr "github.com/pingcap/tidb/pkg/store/driver/error" + "github.com/pingcap/tidb/pkg/store/gcworker" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/tablecodec" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/external" + "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/codec" + "github.com/pingcap/tidb/pkg/util/deadlockhistory" "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/deadlockhistory" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/oracle" "github.com/tikv/client-go/v2/testutils" @@ -665,10 +665,10 @@ func TestAsyncRollBackNoWait(t *testing.T) { // test get ts failed for handlePessimisticLockError when using nowait // even though async rollback for pessimistic lock may rollback later locked key if get ts failed from pd // the txn correctness should be ensured - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/ExecStmtGetTsError", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/ExecStmtGetTsError", "return")) require.NoError(t, failpoint.Enable("tikvclient/beforeAsyncPessimisticRollback", "sleep(100)")) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/ExecStmtGetTsError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/ExecStmtGetTsError")) require.NoError(t, failpoint.Disable("tikvclient/beforeAsyncPessimisticRollback")) }() tk.MustExec("begin pessimistic") @@ -2814,7 +2814,7 @@ func TestLazyUniquenessCheckWithStatementRetry(t *testing.T) { } func TestRCPointWriteLockIfExists(t *testing.T) { - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/assertPessimisticLockErr", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr", "return")) store := realtikvtest.CreateMockStoreAndSetup(t) tk := testkit.NewTestKit(t, store) @@ -2969,7 +2969,7 @@ func TestRCPointWriteLockIfExists(t *testing.T) { tk.MustExec("rollback") tk2.MustExec("rollback") - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/assertPessimisticLockErr")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/assertPessimisticLockErr")) } func TestLazyUniquenessCheckWithInconsistentReadResult(t *testing.T) { @@ -3248,8 +3248,8 @@ func TestFairLockingRetry(t *testing.T) { mustTimeout(t, res, time.Millisecond*50) // Pause on pessimistic retry. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/pessimisticSelectForUpdateRetry", "pause")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/pessimisticDMLRetry", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/pessimisticSelectForUpdateRetry", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/pessimisticDMLRetry", "pause")) tk2.MustExec("commit") mustTimeout(t, res, time.Millisecond*50) @@ -3257,8 +3257,8 @@ func TestFairLockingRetry(t *testing.T) { mustLocked("select * from t2 where id = 10 for update nowait") // Still locked after the retry. - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/pessimisticSelectForUpdateRetry")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/pessimisticDMLRetry")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/pessimisticSelectForUpdateRetry")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/pessimisticDMLRetry")) mustRecv(t, res) mustLocked("select * from t2 where id = 10 for update nowait") @@ -3280,8 +3280,8 @@ func TestFairLockingRetry(t *testing.T) { tk2.MustExec("update t1 set v = 11 where id = 1") // Pause on pessimistic retry. - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/pessimisticSelectForUpdateRetry", "pause")) - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/executor/pessimisticDMLRetry", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/pessimisticSelectForUpdateRetry", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/executor/pessimisticDMLRetry", "pause")) tk2.MustExec("commit") mustTimeout(t, res, time.Millisecond*50) @@ -3289,8 +3289,8 @@ func TestFairLockingRetry(t *testing.T) { mustLocked("select * from t2 where id = 10 for update nowait") // The lock is released after the pessimistic retry, but the other row is locked instead. - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/pessimisticSelectForUpdateRetry")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/executor/pessimisticDMLRetry")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/pessimisticSelectForUpdateRetry")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/executor/pessimisticDMLRetry")) mustRecv(t, res) tk2.MustExec("begin pessimistic") tk2.MustQuery("select * from t2 where id = 10 for update").Check(testkit.Rows("10 100")) @@ -3363,12 +3363,12 @@ func TestPointLockNonExistentKeyWithFairLockingUnderRC(t *testing.T) { tk.MustExec("begin pessimistic") tk2.MustExec("begin pessimistic") tk2.MustExec("insert into t values (1, 2)") - require.NoError(t, failpoint.EnableWith("github.com/pingcap/tidb/store/driver/txn/lockedWithConflictOccurs", "return", func() error { + require.NoError(t, failpoint.EnableWith("github.com/pingcap/tidb/pkg/store/driver/txn/lockedWithConflictOccurs", "return", func() error { lockedWithConflictCounter++ return nil })) defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/store/driver/txn/lockedWithConflictOccurs")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/store/driver/txn/lockedWithConflictOccurs")) }() ch := mustQueryAsync(tk, "select * from t where a = 1 for update") mustTimeout(t, ch, time.Millisecond*100) diff --git a/tests/realtikvtest/sessiontest/BUILD.bazel b/tests/realtikvtest/sessiontest/BUILD.bazel index 98f9f2e0b2335..579b68fd5d194 100644 --- a/tests/realtikvtest/sessiontest/BUILD.bazel +++ b/tests/realtikvtest/sessiontest/BUILD.bazel @@ -12,17 +12,17 @@ go_test( flaky = True, race = "on", deps = [ - "//config", - "//executor", - "//kv", - "//meta/autoid", - "//parser", - "//parser/terror", - "//session", - "//sessionctx/variable", - "//testkit", + "//pkg/config", + "//pkg/executor", + "//pkg/kv", + "//pkg/meta/autoid", + "//pkg/parser", + "//pkg/parser/terror", + "//pkg/session", + "//pkg/sessionctx/variable", + "//pkg/testkit", + "//pkg/util", "//tests/realtikvtest", - "//util", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", ], diff --git a/tests/realtikvtest/sessiontest/paging_test.go b/tests/realtikvtest/sessiontest/paging_test.go index 31cab81e461ec..5bc6e183bd86a 100644 --- a/tests/realtikvtest/sessiontest/paging_test.go +++ b/tests/realtikvtest/sessiontest/paging_test.go @@ -21,8 +21,8 @@ import ( "strings" "testing" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) diff --git a/tests/realtikvtest/sessiontest/retry_test.go b/tests/realtikvtest/sessiontest/retry_test.go index 307a93d20085f..1d09fdf48cb87 100644 --- a/tests/realtikvtest/sessiontest/retry_test.go +++ b/tests/realtikvtest/sessiontest/retry_test.go @@ -19,15 +19,15 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/executor" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/meta/autoid" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/executor" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/meta/autoid" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/parser/terror" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/pingcap/tidb/util" "github.com/stretchr/testify/require" ) diff --git a/tests/realtikvtest/sessiontest/session_fail_test.go b/tests/realtikvtest/sessiontest/session_fail_test.go index 001c2fbc463d9..8c0952eb4fb59 100644 --- a/tests/realtikvtest/sessiontest/session_fail_test.go +++ b/tests/realtikvtest/sessiontest/session_fail_test.go @@ -21,10 +21,10 @@ import ( "testing" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) @@ -42,10 +42,10 @@ func TestFailStatementCommitInRetry(t *testing.T) { tk.MustExec("insert into t values (2),(3),(4),(5)") tk.MustExec("insert into t values (6)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockCommitError8942", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockCommitError8942", `return(true)`)) _, err := tk.Exec("commit") require.Error(t, err) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockCommitError8942")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockCommitError8942")) tk.MustExec("insert into t values (6)") tk.MustQuery(`select * from t`).Check(testkit.Rows("6")) @@ -59,9 +59,9 @@ func TestGetTSFailDirtyState(t *testing.T) { tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn tk.MustExec("create table t (id int)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockGetTSFail", "return")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockGetTSFail", "return")) ctx := failpoint.WithHook(context.Background(), func(ctx context.Context, fpname string) bool { - return fpname == "github.com/pingcap/tidb/session/mockGetTSFail" + return fpname == "github.com/pingcap/tidb/pkg/session/mockGetTSFail" }) _, err := tk.Session().Execute(ctx, "select * from t") if config.GetGlobalConfig().Store == "unistore" { @@ -74,12 +74,12 @@ func TestGetTSFailDirtyState(t *testing.T) { // affected by this fail flag. tk.MustExec("insert into t values (1)") tk.MustQuery(`select * from t`).Check(testkit.Rows("1")) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockGetTSFail")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockGetTSFail")) } func TestGetTSFailDirtyStateInretry(t *testing.T) { defer func() { - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockCommitError")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockCommitError")) require.NoError(t, failpoint.Disable("tikvclient/mockGetTSErrorInRetry")) }() @@ -90,7 +90,7 @@ func TestGetTSFailDirtyStateInretry(t *testing.T) { tk.Session().GetSessionVars().EnableClusteredIndex = variable.ClusteredIndexDefModeOn tk.MustExec("create table t (id int)") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockCommitError", `return(true)`)) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockCommitError", `return(true)`)) // This test will mock a PD timeout error, and recover then. // Just make mockGetTSErrorInRetry return true once, and then return false. require.NoError(t, failpoint.Enable("tikvclient/mockGetTSErrorInRetry", diff --git a/tests/realtikvtest/statisticstest/BUILD.bazel b/tests/realtikvtest/statisticstest/BUILD.bazel index eae757e6e9e92..b9ee7985a9952 100644 --- a/tests/realtikvtest/statisticstest/BUILD.bazel +++ b/tests/realtikvtest/statisticstest/BUILD.bazel @@ -10,7 +10,7 @@ go_test( flaky = True, race = "on", deps = [ - "//testkit", + "//pkg/testkit", "//tests/realtikvtest", "@com_github_stretchr_testify//require", "@org_uber_go_goleak//:goleak", diff --git a/tests/realtikvtest/statisticstest/statistics_test.go b/tests/realtikvtest/statisticstest/statistics_test.go index f3d88f5df043d..6dcc5d65f5e50 100644 --- a/tests/realtikvtest/statisticstest/statistics_test.go +++ b/tests/realtikvtest/statisticstest/statistics_test.go @@ -18,7 +18,7 @@ import ( "fmt" "testing" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) diff --git a/tests/realtikvtest/testkit.go b/tests/realtikvtest/testkit.go index 59c49aafd9266..e624172c24e67 100644 --- a/tests/realtikvtest/testkit.go +++ b/tests/realtikvtest/testkit.go @@ -24,16 +24,16 @@ import ( "testing" "time" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/testkit/testmain" - "github.com/pingcap/tidb/testkit/testsetup" + "github.com/pingcap/tidb/pkg/config" + "github.com/pingcap/tidb/pkg/domain" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/session" + "github.com/pingcap/tidb/pkg/sessionctx/variable" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/store/mockstore" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/testkit/testmain" + "github.com/pingcap/tidb/pkg/testkit/testsetup" "github.com/stretchr/testify/require" "github.com/tikv/client-go/v2/tikv" "github.com/tikv/client-go/v2/txnkv/transaction" diff --git a/tests/realtikvtest/txntest/BUILD.bazel b/tests/realtikvtest/txntest/BUILD.bazel index a688a8caac32d..05ef8ea59fd30 100644 --- a/tests/realtikvtest/txntest/BUILD.bazel +++ b/tests/realtikvtest/txntest/BUILD.bazel @@ -12,14 +12,14 @@ go_test( flaky = True, race = "on", deps = [ - "//expression", - "//kv", - "//parser", - "//session/txninfo", - "//store/driver", - "//testkit", + "//pkg/expression", + "//pkg/kv", + "//pkg/parser", + "//pkg/session/txninfo", + "//pkg/store/driver", + "//pkg/testkit", + "//pkg/util", "//tests/realtikvtest", - "//util", "@com_github_pingcap_failpoint//:failpoint", "@com_github_stretchr_testify//require", "@io_opencensus_go//stats/view", diff --git a/tests/realtikvtest/txntest/isolation_test.go b/tests/realtikvtest/txntest/isolation_test.go index 508b63307e146..a303773dab3c9 100644 --- a/tests/realtikvtest/txntest/isolation_test.go +++ b/tests/realtikvtest/txntest/isolation_test.go @@ -17,10 +17,10 @@ package txntest import ( "testing" - "github.com/pingcap/tidb/store/driver" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/store/driver" + "github.com/pingcap/tidb/pkg/testkit" + "github.com/pingcap/tidb/pkg/util" "github.com/pingcap/tidb/tests/realtikvtest" - "github.com/pingcap/tidb/util" "github.com/stretchr/testify/require" "go.opencensus.io/stats/view" ) diff --git a/tests/realtikvtest/txntest/txn_state_test.go b/tests/realtikvtest/txntest/txn_state_test.go index b8a687a074964..671e4b7e216e2 100644 --- a/tests/realtikvtest/txntest/txn_state_test.go +++ b/tests/realtikvtest/txntest/txn_state_test.go @@ -21,10 +21,10 @@ import ( "time" "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/session/txninfo" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/parser" + "github.com/pingcap/tidb/pkg/session/txninfo" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) @@ -160,7 +160,7 @@ func TestRunning(t *testing.T) { tk.MustExec("create table t(a int);") tk.MustExec("insert into t(a) values (1);") tk.MustExec("begin pessimistic;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockStmtSlow", "return(200)")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockStmtSlow", "return(200)")) ch := make(chan struct{}) go func() { tk.MustExec("select * from t for update /* sleep */;") @@ -170,7 +170,7 @@ func TestRunning(t *testing.T) { time.Sleep(100 * time.Millisecond) info := tk.Session().TxnInfo() require.Equal(t, txninfo.TxnRunning, info.State) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockStmtSlow")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockStmtSlow")) <-ch } @@ -217,13 +217,13 @@ func TestCommitting(t *testing.T) { tk2.MustExec("begin pessimistic") require.NotNil(t, tk2.Session().TxnInfo()) tk2.MustExec("select * from t where a = 2 for update;") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockSlowCommit", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockSlowCommit", "pause")) tk2.MustExec("commit;") ch <- struct{}{} }() time.Sleep(100 * time.Millisecond) require.Equal(t, txninfo.TxnCommitting, tk2.Session().TxnInfo().State) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockSlowCommit")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockSlowCommit")) tk1.MustExec("commit;") <-ch } @@ -239,12 +239,12 @@ func TestRollbackTxnState(t *testing.T) { go func() { tk.MustExec("begin pessimistic") tk.MustExec("insert into t(a) values (3);") - require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/session/mockSlowRollback", "pause")) + require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/session/mockSlowRollback", "pause")) tk.MustExec("rollback;") ch <- struct{}{} }() time.Sleep(100 * time.Millisecond) - require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/session/mockSlowRollback")) + require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/session/mockSlowRollback")) require.Equal(t, txninfo.TxnRollingBack, tk.Session().TxnInfo().State) <-ch } diff --git a/tests/realtikvtest/txntest/txn_test.go b/tests/realtikvtest/txntest/txn_test.go index babb64f33071c..b22e917ae4c3a 100644 --- a/tests/realtikvtest/txntest/txn_test.go +++ b/tests/realtikvtest/txntest/txn_test.go @@ -20,9 +20,9 @@ import ( "testing" "time" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/testkit" + "github.com/pingcap/tidb/pkg/expression" + "github.com/pingcap/tidb/pkg/kv" + "github.com/pingcap/tidb/pkg/testkit" "github.com/pingcap/tidb/tests/realtikvtest" "github.com/stretchr/testify/require" ) diff --git a/tidb-binlog/driver/reader/BUILD.bazel b/tidb-binlog/driver/reader/BUILD.bazel deleted file mode 100644 index 32f271d627085..0000000000000 --- a/tidb-binlog/driver/reader/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "reader", - srcs = [ - "offset.go", - "reader.go", - ], - importpath = "github.com/pingcap/tidb/tidb-binlog/driver/reader", - visibility = ["//visibility:public"], - deps = [ - "//tidb-binlog/proto/go-binlog", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_shopify_sarama//:sarama", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "reader_test", - timeout = "short", - srcs = ["offset_test.go"], - embed = [":reader"], - flaky = True, - deps = [ - "//tidb-binlog/proto/go-binlog", - "@com_github_shopify_sarama//:sarama", - "@com_github_stretchr_testify//require", - ], -) diff --git a/tidb-binlog/driver/reader/reader.go b/tidb-binlog/driver/reader/reader.go deleted file mode 100644 index 0418ce5324c79..0000000000000 --- a/tidb-binlog/driver/reader/reader.go +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package reader - -import ( - "time" - - "github.com/Shopify/sarama" - "github.com/pingcap/errors" - "github.com/pingcap/log" - pb "github.com/pingcap/tidb/tidb-binlog/proto/go-binlog" - "go.uber.org/zap" -) - -const ( - // KafkaReadTimeout is the timeout for reading from kafka. - KafkaReadTimeout = 10 * time.Minute - // KafkaWaitTimeout is the timeout for waiting for kafka. - KafkaWaitTimeout = 11 * time.Minute -) - -func init() { - // log.SetLevel(log.LOG_LEVEL_NONE) - sarama.MaxResponseSize = 1<<31 - 1 -} - -// Config for Reader -type Config struct { - KafkaAddr []string - // the CommitTs of binlog return by reader will bigger than the config CommitTs - CommitTS int64 - Offset int64 // start at kafka offset - // if Topic is empty, use the default name in drainer _obinlog - Topic string - ClusterID string - // buffer size of messages of the reader internal. - // default value 1. - // suggest only setting the buffer size of this if you want the reader to buffer - // message internal and leave `SaramaBufferSize` as 1 default. - MessageBufferSize int - // the sarama internal buffer size of messages. - SaramaBufferSize int -} - -// nolint: unused, deadcode -func (c *Config) getSaramaBufferSize() int { - if c.SaramaBufferSize > 0 { - return c.SaramaBufferSize - } - - return 1 -} - -func (c *Config) getMessageBufferSize() int { - if c.MessageBufferSize > 0 { - return c.MessageBufferSize - } - - return 1 -} - -func (c *Config) valid() error { - if len(c.Topic) == 0 && len(c.ClusterID) == 0 { - return errors.New("Topic or ClusterID must be set") - } - - return nil -} - -// Message read from reader -type Message struct { - Binlog *pb.Binlog - Offset int64 // kafka offset -} - -// Reader to read binlog from kafka -type Reader struct { - cfg *Config - client sarama.Client - - msgs chan *Message - stop chan struct{} - clusterID string -} - -func (r *Reader) getTopic() (string, int32) { - if r.cfg.Topic != "" { - return r.cfg.Topic, 0 - } - - return r.cfg.ClusterID + "_obinlog", 0 -} - -// NewReader creates an instance of Reader -func NewReader(cfg *Config) (r *Reader, err error) { - err = cfg.valid() - if err != nil { - return r, errors.Trace(err) - } - - r = &Reader{ - cfg: cfg, - stop: make(chan struct{}), - msgs: make(chan *Message, cfg.getMessageBufferSize()), - clusterID: cfg.ClusterID, - } - - conf := sarama.NewConfig() - // set to 10 minutes to prevent i/o timeout when reading huge message - conf.Net.ReadTimeout = KafkaReadTimeout - if cfg.SaramaBufferSize > 0 { - conf.ChannelBufferSize = cfg.SaramaBufferSize - } - - r.client, err = sarama.NewClient(r.cfg.KafkaAddr, conf) - if err != nil { - err = errors.Trace(err) - r = nil - return - } - - if r.cfg.CommitTS > 0 { - r.cfg.Offset, err = r.getOffsetByTS(r.cfg.CommitTS, conf) - if err != nil { - err = errors.Trace(err) - r = nil - return - } - log.Debug("set offset to", zap.Int64("offset", r.cfg.Offset)) - } - - go r.run() - - return -} - -// Close shuts down the reader -func (r *Reader) Close() { - close(r.stop) - - _ = r.client.Close() -} - -// Messages returns a chan that contains unread buffered message -func (r *Reader) Messages() (msgs <-chan *Message) { - return r.msgs -} - -func (r *Reader) getOffsetByTS(ts int64, conf *sarama.Config) (offset int64, err error) { - // set true to retrieve error - conf.Consumer.Return.Errors = true - seeker, err := NewKafkaSeeker(r.cfg.KafkaAddr, conf) - if err != nil { - err = errors.Trace(err) - return - } - - topic, partition := r.getTopic() - log.Debug("get offset", - zap.String("topic", topic), - zap.Int32("partition", partition), - zap.Int64("ts", ts)) - offsets, err := seeker.Seek(topic, ts, []int32{partition}) - if err != nil { - err = errors.Trace(err) - return - } - - offset = offsets[0] - - return -} - -func (r *Reader) run() { - offset := r.cfg.Offset - log.Debug("start at", zap.Int64("offset", offset)) - - consumer, err := sarama.NewConsumerFromClient(r.client) - if err != nil { - log.Fatal("create kafka consumer failed", zap.Error(err)) - } - defer func() { - _ = consumer.Close() - }() - topic, partition := r.getTopic() - partitionConsumer, err := consumer.ConsumePartition(topic, partition, offset) - if err != nil { - log.Fatal("create kafka partition consumer failed", zap.Error(err)) - } - defer func() { - _ = partitionConsumer.Close() - }() - // add select to avoid message blocking while reading - for { - select { - case <-r.stop: - // clean environment - _ = partitionConsumer.Close() - close(r.msgs) - log.Info("reader stop to run") - return - case kmsg := <-partitionConsumer.Messages(): - log.Debug("get kafka message", zap.Int64("offset", kmsg.Offset)) - binlog := new(pb.Binlog) - err := binlog.Unmarshal(kmsg.Value) - if err != nil { - log.Warn("unmarshal binlog failed", zap.Error(err)) - continue - } - if r.cfg.CommitTS > 0 && binlog.CommitTs <= r.cfg.CommitTS { - log.Warn("skip binlog CommitTs", zap.Int64("commitTS", binlog.CommitTs)) - continue - } - - msg := &Message{ - Binlog: binlog, - Offset: kmsg.Offset, - } - select { - case r.msgs <- msg: - case <-r.stop: - // In the next iteration, the <-r.stop would match again and prepare to quit - continue - } - } - } -} diff --git a/tidb-binlog/node/BUILD.bazel b/tidb-binlog/node/BUILD.bazel deleted file mode 100644 index 8c5116e05327d..0000000000000 --- a/tidb-binlog/node/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "node", - srcs = [ - "node.go", - "registry.go", - ], - importpath = "github.com/pingcap/tidb/tidb-binlog/node", - visibility = ["//visibility:public"], - deps = [ - "//util/etcd", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//oracle", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "node_test", - timeout = "short", - srcs = ["registry_test.go"], - embed = [":node"], - flaky = True, - shard_count = 3, - deps = [ - "//util/etcd", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_tests_v3//integration", - ], -) diff --git a/tidb-binlog/node/registry_test.go b/tidb-binlog/node/registry_test.go deleted file mode 100644 index f6c7a69d15e3a..0000000000000 --- a/tidb-binlog/node/registry_test.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package node - -import ( - "context" - "path" - "testing" - "time" - - "github.com/pingcap/tidb/util/etcd" - "github.com/stretchr/testify/require" - "go.etcd.io/etcd/tests/v3/integration" -) - -var nodePrefix = path.Join(DefaultRootPath, NodePrefix[PumpNode]) - -type RegisrerTestClient interface { - Node(context.Context, string, string) (*Status, int64, error) -} - -func TestUpdateNodeInfo(t *testing.T) { - integration.BeforeTestExternal(t) - testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - defer testEtcdCluster.Terminate(t) - - etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) - r := NewEtcdRegistry(etcdclient, time.Duration(30)*time.Second) - ns := &Status{ - NodeID: "test", - Addr: "test", - State: Online, - IsAlive: true, - } - - err := r.UpdateNode(context.Background(), nodePrefix, ns) - require.NoError(t, err) - mustEqualStatus(t, r, ns.NodeID, ns) - - ns.Addr = "localhost:1234" - err = r.UpdateNode(context.Background(), nodePrefix, ns) - require.NoError(t, err) - mustEqualStatus(t, r, ns.NodeID, ns) - // use Nodes() to query node status - ss, _, err := r.Nodes(context.Background(), nodePrefix) - require.NoError(t, err) - require.Len(t, ss, 1) -} - -func TestRegisterNode(t *testing.T) { - integration.BeforeTestExternal(t) - testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - defer testEtcdCluster.Terminate(t) - - etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) - r := NewEtcdRegistry(etcdclient, time.Duration(5)*time.Second) - - ns := &Status{ - NodeID: "test", - Addr: "test", - State: Online, - IsAlive: true, - } - err := r.UpdateNode(context.Background(), nodePrefix, ns) - require.NoError(t, err) - mustEqualStatus(t, r, ns.NodeID, ns) - - ns.State = Offline - err = r.UpdateNode(context.Background(), nodePrefix, ns) - require.NoError(t, err) - mustEqualStatus(t, r, ns.NodeID, ns) -} - -func TestRefreshNode(t *testing.T) { - integration.BeforeTestExternal(t) - testEtcdCluster := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 1}) - defer testEtcdCluster.Terminate(t) - - etcdclient := etcd.NewClient(testEtcdCluster.RandClient(), DefaultRootPath) - r := NewEtcdRegistry(etcdclient, time.Duration(5)*time.Second) - - ns := &Status{ - NodeID: "test", - Addr: "test", - State: Online, - IsAlive: true, - } - err := r.UpdateNode(context.Background(), nodePrefix, ns) - require.NoError(t, err) - - ns.IsAlive = true - mustEqualStatus(t, r, ns.NodeID, ns) -} - -func mustEqualStatus(t *testing.T, r RegisrerTestClient, nodeID string, status *Status) { - ns, _, err := r.Node(context.Background(), nodePrefix, nodeID) - require.NoError(t, err) - require.Equal(t, status, ns) -} diff --git a/tidb-binlog/proto/go-binlog/BUILD.bazel b/tidb-binlog/proto/go-binlog/BUILD.bazel deleted file mode 100644 index aadd5181f9734..0000000000000 --- a/tidb-binlog/proto/go-binlog/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go-binlog", - srcs = ["secondary_binlog.pb.go"], - importpath = "github.com/pingcap/tidb/tidb-binlog/proto/go-binlog", - visibility = ["//visibility:public"], - deps = ["@com_github_golang_protobuf//proto"], -) diff --git a/tidb-binlog/pump_client/BUILD.bazel b/tidb-binlog/pump_client/BUILD.bazel deleted file mode 100644 index 863fe7b61f8f9..0000000000000 --- a/tidb-binlog/pump_client/BUILD.bazel +++ /dev/null @@ -1,47 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "pump_client", - srcs = [ - "client.go", - "pump.go", - "selector.go", - ], - importpath = "github.com/pingcap/tidb/tidb-binlog/pump_client", - visibility = ["//visibility:public"], - deps = [ - "//tidb-binlog/node", - "//util", - "//util/etcd", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_tikv_pd_client//:client", - "@io_etcd_go_etcd_api_v3//mvccpb", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//codes", - "@org_golang_google_grpc//credentials", - "@org_golang_google_grpc//credentials/insecure", - "@org_golang_google_grpc//status", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "pump_client_test", - timeout = "short", - srcs = [ - "bench_test.go", - "client_test.go", - ], - embed = [":pump_client"], - flaky = True, - deps = [ - "//tidb-binlog/node", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_stretchr_testify//require", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - ], -) diff --git a/tidb-binlog/pump_client/client.go b/tidb-binlog/pump_client/client.go deleted file mode 100644 index 1848462e67d1d..0000000000000 --- a/tidb-binlog/pump_client/client.go +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package client - -import ( - "context" - "crypto/tls" - "encoding/json" - "path" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/tidb-binlog/node" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/etcd" - pb "github.com/pingcap/tipb/go-binlog" - pd "github.com/tikv/pd/client" - "go.etcd.io/etcd/api/v3/mvccpb" - "go.uber.org/zap" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -const ( - // DefaultEtcdTimeout is the default timeout config for etcd. - DefaultEtcdTimeout = 5 * time.Second - - // DefaultRetryTime is the default retry time for each pump. - DefaultRetryTime = 10 - - // DefaultBinlogWriteTimeout is the default max time binlog can use to write to pump. - DefaultBinlogWriteTimeout = 15 * time.Second - - // CheckInterval is the default interval for check unavailable pumps. - CheckInterval = 30 * time.Second -) - -var ( - // ErrNoAvaliablePump means no available pump to write binlog. - ErrNoAvaliablePump = errors.New("no available pump to write binlog") - - // CommitBinlogTimeout is the max retry duration time for write commit/rollback binlog. - CommitBinlogTimeout = 10 * time.Minute - - // RetryInterval is the interval of retrying to write binlog. - RetryInterval = 100 * time.Millisecond -) - -// PumpInfos saves pumps' information in pumps client. -type PumpInfos struct { - // Pumps saves the map of pump's nodeID and pump status. - Pumps map[string]*PumpStatus - - // AvliablePumps saves the whole available pumps' status. - AvaliablePumps map[string]*PumpStatus - - // UnAvaliablePumps saves the unavailable pumps. - // And only pump with Online state in this map need check is it available. - UnAvaliablePumps map[string]*PumpStatus -} - -// NewPumpInfos returns a PumpInfos. -func NewPumpInfos() *PumpInfos { - return &PumpInfos{ - Pumps: make(map[string]*PumpStatus), - AvaliablePumps: make(map[string]*PumpStatus), - UnAvaliablePumps: make(map[string]*PumpStatus), - } -} - -// PumpsClient is the client of pumps. -type PumpsClient struct { - sync.RWMutex - - ctx context.Context - - cancel context.CancelFunc - - wg sync.WaitGroup - - // ClusterID is the cluster ID of this tidb cluster. - ClusterID uint64 - - // the registry of etcd. - EtcdRegistry *node.EtcdRegistry - - // Pumps saves the pumps' information. - Pumps *PumpInfos - - // Selector will select a suitable pump. - Selector PumpSelector - - // the max retry time if write binlog failed, obsolete now. - RetryTime int - - // BinlogWriteTimeout is the max time binlog can use to write to pump. - BinlogWriteTimeout time.Duration - - // Security is the security config - Security *tls.Config - - // binlog socket file path, for compatible with kafka version pump. - binlogSocket string - - nodePath string -} - -// NewPumpsClient returns a PumpsClient. -// TODO: get strategy from etcd, and can update strategy in real-time. Use Range as default now. -func NewPumpsClient(etcdURLs, strategy string, timeout time.Duration, securityOpt pd.SecurityOption) (*PumpsClient, error) { - ectdEndpoints, err := util.ParseHostPortAddr(etcdURLs) - if err != nil { - return nil, errors.Trace(err) - } - - // get clusterid - pdCli, err := pd.NewClient(ectdEndpoints, securityOpt) - if err != nil { - return nil, errors.Trace(err) - } - clusterID := pdCli.GetClusterID(context.Background()) - pdCli.Close() - - security, err := util.ToTLSConfig(securityOpt.CAPath, securityOpt.CertPath, securityOpt.KeyPath) - if err != nil { - return nil, errors.Trace(err) - } - - cli, err := etcd.NewClientFromCfg(ectdEndpoints, DefaultEtcdTimeout, node.DefaultRootPath, security) - if err != nil { - return nil, errors.Trace(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - newPumpsClient := &PumpsClient{ - ctx: ctx, - cancel: cancel, - ClusterID: clusterID, - EtcdRegistry: node.NewEtcdRegistry(cli, DefaultEtcdTimeout), - Pumps: NewPumpInfos(), - Selector: NewSelector(strategy), - BinlogWriteTimeout: timeout, - Security: security, - nodePath: path.Join(node.DefaultRootPath, node.NodePrefix[node.PumpNode]), - } - - revision, err := newPumpsClient.getPumpStatus(ctx) - if err != nil { - return nil, errors.Trace(err) - } - - if len(newPumpsClient.Pumps.Pumps) == 0 { - return nil, errors.New("no pump found in pd") - } - newPumpsClient.Selector.SetPumps(copyPumps(newPumpsClient.Pumps.AvaliablePumps)) - - newPumpsClient.wg.Add(2) - go func() { - newPumpsClient.watchStatus(revision) - newPumpsClient.wg.Done() - }() - - go func() { - newPumpsClient.detect() - newPumpsClient.wg.Done() - }() - - return newPumpsClient, nil -} - -// NewLocalPumpsClient returns a PumpsClient, this PumpsClient will write binlog by socket file. For compatible with kafka version pump. -func NewLocalPumpsClient(etcdURLs, binlogSocket string, timeout time.Duration, securityOpt pd.SecurityOption) (*PumpsClient, error) { - ectdEndpoints, err := util.ParseHostPortAddr(etcdURLs) - if err != nil { - return nil, errors.Trace(err) - } - - // get clusterid - pdCli, err := pd.NewClient(ectdEndpoints, securityOpt) - if err != nil { - return nil, errors.Trace(err) - } - clusterID := pdCli.GetClusterID(context.Background()) - pdCli.Close() - - security, err := util.ToTLSConfig(securityOpt.CAPath, securityOpt.CertPath, securityOpt.KeyPath) - if err != nil { - return nil, errors.Trace(err) - } - - ctx, cancel := context.WithCancel(context.Background()) - newPumpsClient := &PumpsClient{ - ctx: ctx, - cancel: cancel, - ClusterID: clusterID, - Pumps: NewPumpInfos(), - Selector: NewSelector(LocalUnix), - BinlogWriteTimeout: timeout, - Security: security, - binlogSocket: binlogSocket, - } - newPumpsClient.syncLocalPumpStatus(ctx) - - return newPumpsClient, nil -} - -// getLocalPumpStatus sync the local pump. For compatible with kafka version tidb-binlog. -func (c *PumpsClient) syncLocalPumpStatus(_ context.Context) { - nodeStatus := &node.Status{ - NodeID: localPump, - Addr: c.binlogSocket, - IsAlive: true, - State: node.Online, - } - c.addPump(NewPumpStatus(nodeStatus, c.Security), true) -} - -// getPumpStatus gets all the pumps status in the etcd. -func (c *PumpsClient) getPumpStatus(pctx context.Context) (revision int64, err error) { - nodesStatus, revision, err := c.EtcdRegistry.Nodes(pctx, node.NodePrefix[node.PumpNode]) - if err != nil { - return -1, errors.Trace(err) - } - - for _, status := range nodesStatus { - log.Debug("get pump from pd", zap.String("category", "pumps client"), zap.Stringer("pump", status)) - c.addPump(NewPumpStatus(status, c.Security), false) - } - - return revision, nil -} - -// WriteBinlog writes binlog to a suitable pump. Tips: will never return error for commit/rollback binlog. -func (c *PumpsClient) WriteBinlog(binlog *pb.Binlog) error { - c.RLock() - pumpNum := len(c.Pumps.AvaliablePumps) - selector := c.Selector - c.RUnlock() - - var choosePump *PumpStatus - meetError := false - defer func() { - if meetError { - c.checkPumpAvaliable() - } - - selector.Feedback(binlog.StartTs, binlog.Tp, choosePump) - }() - - commitData, err := binlog.Marshal() - if err != nil { - return errors.Trace(err) - } - req := &pb.WriteBinlogReq{ClusterID: c.ClusterID, Payload: commitData} - - retryTime := 0 - var pump *PumpStatus - var resp *pb.WriteBinlogResp - startTime := time.Now() - - for { - if pump == nil || binlog.Tp == pb.BinlogType_Prewrite { - pump = selector.Select(binlog, retryTime) - } - if pump == nil { - err = ErrNoAvaliablePump - break - } - - resp, err = pump.WriteBinlog(req, c.BinlogWriteTimeout) - if err == nil && resp.Errmsg != "" { - err = errors.New(resp.Errmsg) - } - if err == nil { - choosePump = pump - return nil - } - - meetError = true - log.Warn("write binlog to pump failed", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Stringer("binlog type", binlog.Tp), zap.Int64("start ts", binlog.StartTs), zap.Int64("commit ts", binlog.CommitTs), zap.Int("length", len(commitData)), zap.Error(err)) - - if binlog.Tp != pb.BinlogType_Prewrite { - // only use one pump to write commit/rollback binlog, util write success or blocked for ten minutes. And will not return error to tidb. - if time.Since(startTime) > CommitBinlogTimeout { - break - } - } else { - if !isRetryableError(err) { - // this kind of error is not retryable, return directly. - return errors.Trace(err) - } - - // make sure already retry every available pump. - if time.Since(startTime) > c.BinlogWriteTimeout && retryTime > pumpNum { - break - } - - if isConnUnAvliable(err) { - // this kind of error indicate that the grpc connection is not available, may be created the connection again can write success. - pump.ResetGrpcClient() - } - - retryTime++ - } - - if binlog.Tp != pb.BinlogType_Prewrite { - time.Sleep(RetryInterval * 10) - } else { - time.Sleep(RetryInterval) - } - } - - log.Info("write binlog to available pumps all failed, will try unavailable pumps", zap.String("category", "pumps client")) - pump, err1 := c.backoffWriteBinlog(req, binlog.Tp) - if err1 == nil { - choosePump = pump - return nil - } - - return errors.Errorf("write binlog failed, the last error %v", err) -} - -// Return directly for non p-binlog. -// Try every online pump for p-binlog. -func (c *PumpsClient) backoffWriteBinlog(req *pb.WriteBinlogReq, binlogType pb.BinlogType) (pump *PumpStatus, err error) { - if binlogType != pb.BinlogType_Prewrite { - // never return error for commit/rollback binlog. - return nil, nil - } - - c.RLock() - allPumps := copyPumps(c.Pumps.Pumps) - c.RUnlock() - - var resp *pb.WriteBinlogResp - // send binlog to unavailable pumps to retry again. - for _, pump := range allPumps { - if !pump.ShouldBeUsable() { - continue - } - - pump.ResetGrpcClient() - - resp, err = pump.WriteBinlog(req, c.BinlogWriteTimeout) - if err == nil && resp.Errmsg != "" { - err = errors.New(resp.Errmsg) - } - - if err != nil { - log.Warn("try write binlog failed", zap.String("category", "pumps client"), - zap.String("error", err.Error()), - zap.String("NodeID", pump.NodeID)) - continue - } - - // if this pump can write binlog success, set this pump to available. - log.Info("write binlog to pump success, set this pump to available", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID)) - c.setPumpAvailable(pump, true) - return pump, nil - } - - return nil, errors.New("write binlog to unavailable pump failed") -} - -func (c *PumpsClient) checkPumpAvaliable() { - c.RLock() - allPumps := copyPumps(c.Pumps.Pumps) - c.RUnlock() - - for _, pump := range allPumps { - if !pump.IsUsable() { - c.setPumpAvailable(pump, false) - } - } -} - -// setPumpAvailable set pump's isAvailable, and modify UnAvailablePumps or AvailablePumps. -func (c *PumpsClient) setPumpAvailable(pump *PumpStatus, available bool) { - c.Lock() - defer c.Unlock() - - log.Info("set pump available", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Bool("available", available)) - - pump.Reset() - - if available { - delete(c.Pumps.UnAvaliablePumps, pump.NodeID) - if _, ok := c.Pumps.Pumps[pump.NodeID]; ok { - c.Pumps.AvaliablePumps[pump.NodeID] = pump - } - } else { - delete(c.Pumps.AvaliablePumps, pump.NodeID) - if _, ok := c.Pumps.Pumps[pump.NodeID]; ok { - c.Pumps.UnAvaliablePumps[pump.NodeID] = pump - } - } - - c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) -} - -// addPump add a new pump. -func (c *PumpsClient) addPump(pump *PumpStatus, updateSelector bool) { - c.Lock() - defer c.Unlock() - - if pump.ShouldBeUsable() { - c.Pumps.AvaliablePumps[pump.NodeID] = pump - } else { - c.Pumps.UnAvaliablePumps[pump.NodeID] = pump - } - c.Pumps.Pumps[pump.NodeID] = pump - - if updateSelector { - c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) - } -} - -// SetSelectStrategy sets the selector's strategy, strategy should be 'range' or 'hash' now. -func (c *PumpsClient) SetSelectStrategy(strategy string) error { - if strategy != Range && strategy != Hash { - return errors.Errorf("strategy %s is not support", strategy) - } - - c.Lock() - c.Selector = NewSelector(strategy) - c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) - c.Unlock() - return nil -} - -// updatePump update pump's status, and return whether pump's IsAvailable should be changed. -func (c *PumpsClient) updatePump(status *node.Status) (pump *PumpStatus, availableChanged, available bool) { - var ok bool - c.Lock() - if pump, ok = c.Pumps.Pumps[status.NodeID]; ok { - if pump.Status.State != status.State { - if status.State == node.Online { - availableChanged = true - available = true - } else if pump.IsUsable() { - availableChanged = true - available = false - } - } - pump.Status = *status - } - c.Unlock() - - return -} - -// removePump removes a pump, used when pump is offline. -func (c *PumpsClient) removePump(nodeID string) { - c.Lock() - if pump, ok := c.Pumps.Pumps[nodeID]; ok { - pump.Reset() - } - delete(c.Pumps.Pumps, nodeID) - delete(c.Pumps.UnAvaliablePumps, nodeID) - delete(c.Pumps.AvaliablePumps, nodeID) - c.Selector.SetPumps(copyPumps(c.Pumps.AvaliablePumps)) - c.Unlock() -} - -// exist returns true if pumps client has pump matched this nodeID. -func (c *PumpsClient) exist(nodeID string) bool { - c.RLock() - _, ok := c.Pumps.Pumps[nodeID] - c.RUnlock() - return ok -} - -// watchStatus watchs pump's status in etcd. -func (c *PumpsClient) watchStatus(revision int64) { - rch := c.EtcdRegistry.WatchNode(c.ctx, c.nodePath, revision) - - for { - select { - case <-c.ctx.Done(): - log.Info("watch status finished", zap.String("category", "pumps client")) - return - case wresp := <-rch: - if wresp.Err() != nil { - // meet error, watch from the latest revision. - // pump will update the key periodly, it's ok for we to lost some event here - log.Warn("watch status meet error", zap.String("category", "pumps client"), zap.Error(wresp.Err())) - rch = c.EtcdRegistry.WatchNode(c.ctx, c.nodePath, 0) - continue - } - - for _, ev := range wresp.Events { - status := &node.Status{} - err := json.Unmarshal(ev.Kv.Value, &status) - if err != nil { - log.Error("unmarshal pump status failed", zap.String("category", "pumps client"), zap.ByteString("value", ev.Kv.Value), zap.Error(err)) - continue - } - - switch ev.Type { - case mvccpb.PUT: - if !c.exist(status.NodeID) { - log.Info("find a new pump", zap.String("category", "pumps client"), zap.String("NodeID", status.NodeID)) - c.addPump(NewPumpStatus(status, c.Security), true) - continue - } - - pump, availableChanged, available := c.updatePump(status) - if availableChanged { - log.Info("pump's state is changed", zap.String("category", "pumps client"), zap.String("NodeID", pump.Status.NodeID), zap.String("state", status.State)) - c.setPumpAvailable(pump, available) - } - - case mvccpb.DELETE: - // now will not delete pump node in fact, just for compatibility. - nodeID := node.AnalyzeNodeID(string(ev.Kv.Key)) - log.Info("remove pump", zap.String("category", "pumps client"), zap.String("NodeID", nodeID)) - c.removePump(nodeID) - } - } - } - } -} - -// detect send detect binlog to pumps with online state in UnAvaliablePumps, -func (c *PumpsClient) detect() { - checkTick := time.NewTicker(CheckInterval) - defer checkTick.Stop() - - for { - select { - case <-c.ctx.Done(): - log.Info("heartbeat finished", zap.String("category", "pumps client")) - return - case <-checkTick.C: - // send detect binlog to pump, if this pump can return response without error - // means this pump is available. - needCheckPumps := make([]*PumpStatus, 0, len(c.Pumps.UnAvaliablePumps)) - checkPassPumps := make([]*PumpStatus, 0, 1) - - // TODO: send a more normal request, currently pump will just return success for this empty payload - // not write to Storage - req := &pb.WriteBinlogReq{ClusterID: c.ClusterID, Payload: nil} - c.RLock() - for _, pump := range c.Pumps.UnAvaliablePumps { - if pump.ShouldBeUsable() { - needCheckPumps = append(needCheckPumps, pump) - } - } - c.RUnlock() - - for _, pump := range needCheckPumps { - _, err := pump.WriteBinlog(req, c.BinlogWriteTimeout) - if err == nil { - log.Info("write detect binlog to unavailable pump success", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID)) - checkPassPumps = append(checkPassPumps, pump) - } else { - log.Warn("write detect binlog to pump failed", zap.String("category", "pumps client"), zap.String("NodeID", pump.NodeID), zap.Error(err)) - } - } - - for _, pump := range checkPassPumps { - c.setPumpAvailable(pump, true) - } - } - } -} - -// Close closes the PumpsClient. -func (c *PumpsClient) Close() { - log.Info("is closing", zap.String("category", "pumps client")) - c.cancel() - c.wg.Wait() - log.Info("is closed", zap.String("category", "pumps client")) -} - -func isRetryableError(err error) bool { - // ResourceExhausted is a error code in grpc. - // ResourceExhausted indicates some resource has been exhausted, perhaps - // a per-user quota, or perhaps the entire file system is out of space. - // https://github.com/grpc/grpc-go/blob/9cc4fdbde2304827ffdbc7896f49db40c5536600/codes/codes.go#L76 - return !strings.Contains(err.Error(), "ResourceExhausted") -} - -func isConnUnAvliable(err error) bool { - // Unavailable indicates the service is currently unavailable. - // This is a most likely a transient condition and may be corrected - // by retrying with a backoff. - // https://github.com/grpc/grpc-go/blob/76cc50721c5fde18bae10a36f4c202f5f2f95bb7/codes/codes.go#L139 - return status.Code(err) == codes.Unavailable -} - -func copyPumps(pumps map[string]*PumpStatus) []*PumpStatus { - ps := make([]*PumpStatus, 0, len(pumps)) - for _, pump := range pumps { - ps = append(ps, pump) - } - - return ps -} diff --git a/tidb-binlog/pump_client/client_test.go b/tidb-binlog/pump_client/client_test.go deleted file mode 100644 index 974ed76d29aa8..0000000000000 --- a/tidb-binlog/pump_client/client_test.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package client - -import ( - "context" - "fmt" - "net" - "os" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/tidb-binlog/node" - "github.com/pingcap/tipb/go-binlog" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" -) - -var ( - testMaxRecvMsgSize = 1024 - testRetryTime = 5 -) - -type testCase struct { - binlogs []*binlog.Binlog - choosePumps []*PumpStatus - setAvliable []bool - setNodeID []string -} - -func TestSelector(t *testing.T) { - strategys := []string{Hash, Range} - for _, strategy := range strategys { - testSelector(t, strategy) - } -} - -func testSelector(t *testing.T, strategy string) { - pumpsClient := &PumpsClient{ - Pumps: NewPumpInfos(), - Selector: NewSelector(strategy), - BinlogWriteTimeout: DefaultBinlogWriteTimeout, - } - - pumps := []*PumpStatus{{}, {}, {}} - for i, pump := range pumps { - pump.NodeID = fmt.Sprintf("pump%d", i) - pump.State = node.Offline - // set pump client to avoid create grpc client. - pump.Client = binlog.NewPumpClient(nil) - } - - for _, pump := range pumps { - pumpsClient.addPump(pump, false) - } - pumpsClient.Selector.SetPumps(copyPumps(pumpsClient.Pumps.AvaliablePumps)) - - tCase := &testCase{} - - tCase.binlogs = []*binlog.Binlog{ - { - Tp: binlog.BinlogType_Prewrite, - StartTs: 1, - }, { - Tp: binlog.BinlogType_Commit, - StartTs: 1, - CommitTs: 2, - }, { - Tp: binlog.BinlogType_Prewrite, - StartTs: 3, - }, { - Tp: binlog.BinlogType_Commit, - StartTs: 3, - CommitTs: 4, - }, { - Tp: binlog.BinlogType_Prewrite, - StartTs: 5, - }, { - Tp: binlog.BinlogType_Commit, - StartTs: 5, - CommitTs: 6, - }, - } - - tCase.setNodeID = []string{"pump0", "", "pump0", "pump1", "", "pump2"} - tCase.setAvliable = []bool{true, false, false, true, false, true} - tCase.choosePumps = []*PumpStatus{pumpsClient.Pumps.Pumps["pump0"], pumpsClient.Pumps.Pumps["pump0"], nil, - nil, pumpsClient.Pumps.Pumps["pump1"], pumpsClient.Pumps.Pumps["pump1"]} - - for i, nodeID := range tCase.setNodeID { - if nodeID != "" { - pumpsClient.setPumpAvailable(pumpsClient.Pumps.Pumps[nodeID], tCase.setAvliable[i]) - } - pump := pumpsClient.Selector.Select(tCase.binlogs[i], 0) - pumpsClient.Selector.Feedback(tCase.binlogs[i].StartTs, tCase.binlogs[i].Tp, pump) - require.Equal(t, pump, tCase.choosePumps[i]) - } - - for j := 0; j < 10; j++ { - prewriteBinlog := &binlog.Binlog{ - Tp: binlog.BinlogType_Prewrite, - StartTs: int64(j), - } - commitBinlog := &binlog.Binlog{ - Tp: binlog.BinlogType_Commit, - StartTs: int64(j), - } - - pump1 := pumpsClient.Selector.Select(prewriteBinlog, 0) - if j%2 == 0 { - pump1 = pumpsClient.Selector.Select(prewriteBinlog, 1) - } - pumpsClient.Selector.Feedback(prewriteBinlog.StartTs, prewriteBinlog.Tp, pump1) - - pumpsClient.setPumpAvailable(pump1, false) - pump2 := pumpsClient.Selector.Select(commitBinlog, 0) - pumpsClient.Selector.Feedback(commitBinlog.StartTs, commitBinlog.Tp, pump2) - // prewrite binlog and commit binlog with same start ts should choose same pump - require.Equal(t, pump1.NodeID, pump2.NodeID) - pumpsClient.setPumpAvailable(pump1, true) - - // after change strategy, prewrite binlog and commit binlog will choose same pump - pump1 = pumpsClient.Selector.Select(prewriteBinlog, 0) - pumpsClient.Selector.Feedback(prewriteBinlog.StartTs, prewriteBinlog.Tp, pump1) - if strategy == Range { - err := pumpsClient.SetSelectStrategy(Hash) - require.NoError(t, err) - } else { - err := pumpsClient.SetSelectStrategy(Range) - require.NoError(t, err) - } - pump2 = pumpsClient.Selector.Select(commitBinlog, 0) - require.Equal(t, pump1.NodeID, pump2.NodeID) - - // set back - err := pumpsClient.SetSelectStrategy(strategy) - require.NoError(t, err) - } -} - -func TestWriteBinlog(t *testing.T) { - pumpServerConfig := []struct { - addr string - serverMode string - }{ - { - "/tmp/mock-pump.sock", - "unix", - }, { - "127.0.0.1:15049", - "tcp", - }, - } - - // make test faster - RetryInterval = 100 * time.Millisecond - CommitBinlogTimeout = time.Second - - for _, cfg := range pumpServerConfig { - pumpServer, err := createMockPumpServer(cfg.addr, cfg.serverMode, true) - require.NoError(t, err) - - opt := grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout(cfg.serverMode, addr, timeout) - }) - clientCon, err := grpc.Dial(cfg.addr, opt, grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - require.NotNil(t, clientCon) - pumpClient := mockPumpsClient(binlog.NewPumpClient(clientCon), true) - - // test binlog size bigger than grpc's MaxRecvMsgSize - blog := &binlog.Binlog{ - Tp: binlog.BinlogType_Prewrite, - PrewriteValue: make([]byte, testMaxRecvMsgSize+1), - } - err = pumpClient.WriteBinlog(blog) - require.Error(t, err) - - for i := 0; i < 10; i++ { - // test binlog size smaller than grpc's MaxRecvMsgSize - blog = &binlog.Binlog{ - Tp: binlog.BinlogType_Prewrite, - PrewriteValue: make([]byte, 1), - } - err = pumpClient.WriteBinlog(blog) - require.NoError(t, err) - } - - // after write some binlog, the pump without grpc client will move to unavailable list in pump client. - require.Len(t, pumpClient.Pumps.UnAvaliablePumps, 1) - - // test write commit binlog, will not return error although write binlog failed. - preWriteBinlog := &binlog.Binlog{ - Tp: binlog.BinlogType_Prewrite, - StartTs: 123, - PrewriteValue: make([]byte, 1), - } - commitBinlog := &binlog.Binlog{ - Tp: binlog.BinlogType_Commit, - StartTs: 123, - CommitTs: 123, - PrewriteValue: make([]byte, 1), - } - - err = pumpClient.WriteBinlog(preWriteBinlog) - require.NoError(t, err) - - // test when pump is down - pumpServer.Close() - - // write commit binlog failed will not return error - err = pumpClient.WriteBinlog(commitBinlog) - require.NoError(t, err) - - err = pumpClient.WriteBinlog(blog) - require.Error(t, err) - } -} - -type mockPumpServer struct { - mode string - addr string - server *grpc.Server - - withError bool - retryTime int -} - -// WriteBinlog implements PumpServer interface. -func (p *mockPumpServer) WriteBinlog(ctx context.Context, req *binlog.WriteBinlogReq) (*binlog.WriteBinlogResp, error) { - if !p.withError { - return &binlog.WriteBinlogResp{}, nil - } - - p.retryTime++ - if p.retryTime < testRetryTime { - return &binlog.WriteBinlogResp{}, errors.New("fake error") - } - - // only the last retry will return succuess - p.retryTime = 0 - return &binlog.WriteBinlogResp{}, nil -} - -// PullBinlogs implements PumpServer interface. -func (p *mockPumpServer) PullBinlogs(req *binlog.PullBinlogReq, srv binlog.Pump_PullBinlogsServer) error { - return nil -} - -func (p *mockPumpServer) Close() { - p.server.Stop() - if p.mode == "unix" { - os.Remove(p.addr) - } -} - -func createMockPumpServer(addr string, mode string, withError bool) (*mockPumpServer, error) { - if mode == "unix" { - os.Remove(addr) - } - - l, err := net.Listen(mode, addr) - if err != nil { - return nil, err - } - serv := grpc.NewServer(grpc.MaxRecvMsgSize(testMaxRecvMsgSize)) - pump := &mockPumpServer{ - mode: mode, - addr: addr, - server: serv, - withError: withError, - } - binlog.RegisterPumpServer(serv, pump) - go serv.Serve(l) - - return pump, nil -} - -// mockPumpsClient creates a PumpsClient, used for test. -func mockPumpsClient(client binlog.PumpClient, withBadPump bool) *PumpsClient { - // add a available pump - nodeID1 := "pump-1" - pump1 := &PumpStatus{ - Status: node.Status{ - NodeID: nodeID1, - State: node.Online, - }, - Client: client, - } - - pumps := []*PumpStatus{pump1} - - // add a pump without grpc client - nodeID2 := "pump-2" - pump2 := &PumpStatus{ - Status: node.Status{ - NodeID: nodeID2, - State: node.Online, - }, - } - - pumpInfos := NewPumpInfos() - pumpInfos.Pumps[nodeID1] = pump1 - pumpInfos.AvaliablePumps[nodeID1] = pump1 - - if withBadPump { - pumpInfos.Pumps[nodeID2] = pump2 - pumpInfos.AvaliablePumps[nodeID2] = pump2 - pumps = append(pumps, pump2) - } - - pCli := &PumpsClient{ - ClusterID: 1, - Pumps: pumpInfos, - Selector: NewSelector(Range), - BinlogWriteTimeout: time.Second, - } - pCli.Selector.SetPumps(pumps) - - return pCli -} diff --git a/tidb-server/BUILD.bazel b/tidb-server/BUILD.bazel deleted file mode 100644 index 213b5d82ebb55..0000000000000 --- a/tidb-server/BUILD.bazel +++ /dev/null @@ -1,112 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") - -go_library( - name = "tidb-server_lib", - srcs = ["main.go"], - importpath = "github.com/pingcap/tidb/tidb-server", - visibility = ["//visibility:private"], - deps = [ - "//bindinfo", - "//config", - "//ddl", - "//domain", - "//executor", - "//executor/mppcoordmanager", - "//extension", - "//extension/_import", - "//keyspace", - "//kv", - "//metrics", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "//planner/core", - "//plugin", - "//privilege/privileges", - "//resourcemanager", - "//server", - "//session", - "//session/txninfo", - "//sessionctx/binloginfo", - "//sessionctx/variable", - "//statistics", - "//store", - "//store/copr", - "//store/driver", - "//store/mockstore", - "//tidb-binlog/pump_client", - "//util", - "//util/chunk", - "//util/cpuprofile", - "//util/deadlockhistory", - "//util/disk", - "//util/distrole", - "//util/domainutil", - "//util/kvcache", - "//util/logutil", - "//util/memory", - "//util/metricsutil", - "//util/printer", - "//util/sem", - "//util/signal", - "//util/stmtsummary/v2:stmtsummary", - "//util/sys/linux", - "//util/sys/storage", - "//util/systimemon", - "//util/tiflashcompute", - "//util/topsql", - "//util/versioninfo", - "@com_github_opentracing_opentracing_go//:opentracing-go", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_prometheus_client_golang//prometheus/push", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//txnkv/transaction", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_automaxprocs//maxprocs", - "@org_uber_go_zap//:zap", - ], -) - -go_binary( - name = "tidb-server", - embed = [":tidb-server_lib"], - visibility = ["//visibility:public"], - x_defs = { - "github.com/pingcap/tidb/parser/mysql.TiDBReleaseVersion": "{STABLE_TiDB_RELEASE_VERSION}", - "github.com/pingcap/tidb/util/versioninfo.TiDBBuildTS": "{STABLE_TiDB_BUILD_UTCTIME}", - "github.com/pingcap/tidb/util/versioninfo.TiDBGitHash": "{STABLE_TIDB_GIT_HASH}", - "github.com/pingcap/tidb/util/versioninfo.TiDBEnterpriseExtensionGitHash": "{STABLE_TIDB_ENTERPRISE_EXTENSION_GIT_HASH}", - "github.com/pingcap/tidb/util/versioninfo.TiDBGitBranch": "{STABLE_TIDB_GIT_BRANCH}", - "github.com/pingcap/tidb/util/versioninfo.TiDBEdition": "{STABLE_TIDB_EDITION}", - }, -) - -go_binary( - name = "tidb-server-check", - embed = [":tidb-server_lib"], - gc_linkopts = [ - "-X", - "github.com/pingcap/tidb/config.checkBeforeDropLDFlag=1", - ], - visibility = ["//visibility:public"], -) - -go_test( - name = "tidb-server_test", - timeout = "short", - srcs = ["main_test.go"], - embed = [":tidb-server_lib"], - flaky = True, - deps = [ - "//config", - "//parser/mysql", - "//sessionctx/variable", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/tidb-server/main_test.go b/tidb-server/main_test.go deleted file mode 100644 index 34b98fc53fc22..0000000000000 --- a/tidb-server/main_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package main - -import ( - "os" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/stretchr/testify/require" - "go.opencensus.io/stats/view" - "go.uber.org/goleak" -) - -var isCoverageServer string - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} - -// TestRunMain is a dummy test case, which contains only the main function of tidb-server, -// and it is used to generate coverage_server. -func TestRunMain(t *testing.T) { - if isCoverageServer == "1" { - main() - } -} - -func TestSetGlobalVars(t *testing.T) { - defer view.Stop() - require.Equal(t, "tikv,tiflash,tidb", variable.GetSysVar(variable.TiDBIsolationReadEngines).Value) - require.Equal(t, "1073741824", variable.GetSysVar(variable.TiDBMemQuotaQuery).Value) - require.NotEqual(t, "test", variable.GetSysVar(variable.Version).Value) - - config.UpdateGlobal(func(conf *config.Config) { - conf.IsolationRead.Engines = []string{"tikv", "tidb"} - conf.ServerVersion = "test" - }) - setGlobalVars() - - require.Equal(t, "tikv,tidb", variable.GetSysVar(variable.TiDBIsolationReadEngines).Value) - require.Equal(t, "test", variable.GetSysVar(variable.Version).Value) - require.Equal(t, variable.GetSysVar(variable.Version).Value, mysql.ServerVersion) - - config.UpdateGlobal(func(conf *config.Config) { - conf.ServerVersion = "" - }) - setGlobalVars() - - // variable.Version won't change when len(conf.ServerVersion) == 0 - require.Equal(t, "test", variable.GetSysVar(variable.Version).Value) - require.Equal(t, variable.GetSysVar(variable.Version).Value, mysql.ServerVersion) - - cfg := config.GetGlobalConfig() - require.Equal(t, cfg.Socket, variable.GetSysVar(variable.Socket).Value) - - if hostname, err := os.Hostname(); err == nil { - require.Equal(t, variable.GetSysVar(variable.Hostname).Value, hostname) - } -} diff --git a/timer/BUILD.bazel b/timer/BUILD.bazel deleted file mode 100644 index 14b7e47e93080..0000000000000 --- a/timer/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "timer_test", - timeout = "short", - srcs = [ - "main_test.go", - "store_intergartion_test.go", - ], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//sessionctx", - "//testkit", - "//testkit/testsetup", - "//timer/api", - "//timer/runtime", - "//timer/tablestore", - "//util/timeutil", - "@com_github_google_uuid//:uuid", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_tests_v3//integration", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/timer/api/BUILD.bazel b/timer/api/BUILD.bazel deleted file mode 100644 index 6b54c6945742f..0000000000000 --- a/timer/api/BUILD.bazel +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "api", - srcs = [ - "client.go", - "error.go", - "hook.go", - "mem_store.go", - "store.go", - "timer.go", - ], - importpath = "github.com/pingcap/tidb/timer/api", - visibility = ["//visibility:public"], - deps = [ - "//parser/duration", - "//util", - "//util/logutil", - "//util/timeutil", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_robfig_cron_v3//:cron", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "api_test", - timeout = "short", - srcs = [ - "client_test.go", - "main_test.go", - "schedule_policy_test.go", - "store_test.go", - "timer_test.go", - ], - embed = [":api"], - flaky = True, - race = "on", - shard_count = 13, - deps = [ - "//testkit/testsetup", - "//util/timeutil", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/timer/api/client.go b/timer/api/client.go deleted file mode 100644 index 5632a57cbf371..0000000000000 --- a/timer/api/client.go +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package api - -import ( - "context" - "encoding/hex" - "strings" - "time" - "unsafe" - - "github.com/google/uuid" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -const ( - clientMaxRetry = 5 - clientRetryBackoff = uint64(1000) -) - -// GetTimerOption is the option to get timers. -type GetTimerOption func(*TimerCond) - -// WithKey indicates to get a timer with the specified key. -func WithKey(key string) GetTimerOption { - return func(cond *TimerCond) { - cond.Key.Set(key) - cond.KeyPrefix = false - } -} - -// WithKeyPrefix to get timers with the indicated key prefix. -func WithKeyPrefix(keyPrefix string) GetTimerOption { - return func(cond *TimerCond) { - cond.Key.Set(keyPrefix) - cond.KeyPrefix = true - } -} - -// WithID indicates to get a timer with the specified id. -func WithID(id string) GetTimerOption { - return func(cond *TimerCond) { - cond.ID.Set(id) - } -} - -// WithTag indicates to get a timer with the specified tags. -func WithTag(tags ...string) GetTimerOption { - return func(cond *TimerCond) { - cond.Tags.Set(tags) - } -} - -// UpdateTimerOption is the option to update the timer. -type UpdateTimerOption func(*TimerUpdate) - -// WithSetEnable indicates to set the timer's `Enable` field. -func WithSetEnable(enable bool) UpdateTimerOption { - return func(update *TimerUpdate) { - update.Enable.Set(enable) - } -} - -// WithSetTimeZone sets the timezone of the timer -func WithSetTimeZone(name string) UpdateTimerOption { - return func(update *TimerUpdate) { - update.TimeZone.Set(name) - } -} - -// WithSetSchedExpr indicates to set the timer's schedule policy. -func WithSetSchedExpr(tp SchedPolicyType, expr string) UpdateTimerOption { - return func(update *TimerUpdate) { - update.SchedPolicyType.Set(tp) - update.SchedPolicyExpr.Set(expr) - } -} - -// WithSetWatermark indicates to set the timer's watermark. -func WithSetWatermark(watermark time.Time) UpdateTimerOption { - return func(update *TimerUpdate) { - update.Watermark.Set(watermark) - } -} - -// WithSetSummaryData indicates to set the timer's summary. -func WithSetSummaryData(summary []byte) UpdateTimerOption { - return func(update *TimerUpdate) { - update.SummaryData.Set(summary) - } -} - -// WithSetTags indicates to set the timer's tags. -func WithSetTags(tags []string) UpdateTimerOption { - return func(update *TimerUpdate) { - update.Tags.Set(tags) - } -} - -// TimerClient is an interface exposed to user to manage timers. -type TimerClient interface { - // GetDefaultNamespace returns the default namespace of this client. - GetDefaultNamespace() string - // CreateTimer creates a new timer. - CreateTimer(ctx context.Context, spec TimerSpec) (*TimerRecord, error) - // GetTimerByID queries the timer by ID. - GetTimerByID(ctx context.Context, timerID string) (*TimerRecord, error) - // GetTimerByKey queries the timer by key. - GetTimerByKey(ctx context.Context, key string) (*TimerRecord, error) - // GetTimers queries timers by options. - GetTimers(ctx context.Context, opts ...GetTimerOption) ([]*TimerRecord, error) - // UpdateTimer updates a timer. - UpdateTimer(ctx context.Context, timerID string, opts ...UpdateTimerOption) error - // ManualTriggerEvent triggers event manually. - ManualTriggerEvent(ctx context.Context, timerID string) (string, error) - // CloseTimerEvent closes the triggering event of a timer. - CloseTimerEvent(ctx context.Context, timerID string, eventID string, opts ...UpdateTimerOption) error - // DeleteTimer deletes a timer. - DeleteTimer(ctx context.Context, timerID string) (bool, error) -} - -// DefaultStoreNamespace is the default namespace. -const DefaultStoreNamespace = "default" - -// defaultTimerClient is the default implement of timer client. -type defaultTimerClient struct { - namespace string - store *TimerStore - retryBackoff uint64 -} - -// NewDefaultTimerClient creates a new defaultTimerClient. -func NewDefaultTimerClient(store *TimerStore) TimerClient { - return &defaultTimerClient{ - namespace: DefaultStoreNamespace, - store: store, - retryBackoff: clientRetryBackoff, - } -} - -func (c *defaultTimerClient) GetDefaultNamespace() string { - return c.namespace -} - -func (c *defaultTimerClient) CreateTimer(ctx context.Context, spec TimerSpec) (*TimerRecord, error) { - if spec.Namespace == "" { - spec.Namespace = c.namespace - } - - timerID, err := c.store.Create(ctx, &TimerRecord{ - TimerSpec: spec, - }) - - if err != nil { - return nil, err - } - return c.store.GetByID(ctx, timerID) -} - -func (c *defaultTimerClient) GetTimerByID(ctx context.Context, timerID string) (*TimerRecord, error) { - return c.store.GetByID(ctx, timerID) -} - -func (c *defaultTimerClient) GetTimerByKey(ctx context.Context, key string) (*TimerRecord, error) { - return c.store.GetByKey(ctx, c.namespace, key) -} - -func (c *defaultTimerClient) GetTimers(ctx context.Context, opts ...GetTimerOption) ([]*TimerRecord, error) { - cond := &TimerCond{} - for _, opt := range opts { - opt(cond) - } - return c.store.List(ctx, cond) -} - -func (c *defaultTimerClient) UpdateTimer(ctx context.Context, timerID string, opts ...UpdateTimerOption) error { - update := &TimerUpdate{} - for _, opt := range opts { - opt(update) - } - return c.store.Update(ctx, timerID, update) -} - -func (c *defaultTimerClient) ManualTriggerEvent(ctx context.Context, timerID string) (string, error) { - reqUUID := uuid.New() - requestID := hex.EncodeToString(reqUUID[:]) - - err := util.RunWithRetry(clientMaxRetry, c.retryBackoff, func() (bool, error) { - timer, err := c.store.GetByID(ctx, timerID) - if err != nil { - return false, err - } - - if timer.EventID != "" { - return false, errors.New("manual trigger is not allowed when event is not closed") - } - - if !timer.Enable { - return false, errors.New("manual trigger is not allowed when timer is disabled") - } - - err = c.store.Update(ctx, timerID, &TimerUpdate{ - ManualRequest: NewOptionalVal(ManualRequest{ - ManualRequestID: requestID, - ManualRequestTime: time.Now(), - ManualTimeout: 2 * time.Minute, - }), - CheckVersion: NewOptionalVal(timer.Version), - }) - - if errors.ErrorEqual(ErrVersionNotMatch, err) { - logutil.BgLogger().Warn("failed to update timer for version not match, retry", zap.String("timerID", timerID)) - return true, err - } - - return false, err - }) - - if err != nil { - return "", err - } - - return requestID, nil -} - -func (c *defaultTimerClient) CloseTimerEvent(ctx context.Context, timerID string, eventID string, opts ...UpdateTimerOption) error { - update := &TimerUpdate{} - for _, opt := range opts { - opt(update) - } - - fields := update.FieldsSet(unsafe.Pointer(&update.Watermark), unsafe.Pointer(&update.SummaryData)) - if len(fields) > 0 { - return errors.Errorf("The field(s) [%s] are not allowed to update when close event", strings.Join(fields, ", ")) - } - - timer, err := c.GetTimerByID(ctx, timerID) - if err != nil { - return err - } - - var zeroTime time.Time - update.CheckEventID.Set(eventID) - update.EventStatus.Set(SchedEventIdle) - update.EventID.Set("") - update.EventData.Set(nil) - update.EventStart.Set(zeroTime) - if !update.Watermark.Present() { - update.Watermark.Set(timer.EventStart) - } - update.EventExtra.Set(EventExtra{}) - return c.store.Update(ctx, timerID, update) -} - -func (c *defaultTimerClient) DeleteTimer(ctx context.Context, timerID string) (bool, error) { - return c.store.Delete(ctx, timerID) -} diff --git a/timer/api/main_test.go b/timer/api/main_test.go deleted file mode 100644 index 6d4377c05c27d..0000000000000 --- a/timer/api/main_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package api - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m) -} diff --git a/timer/api/store_test.go b/timer/api/store_test.go deleted file mode 100644 index 58a730cbbea91..0000000000000 --- a/timer/api/store_test.go +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package api - -import ( - "reflect" - "testing" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/timeutil" - "github.com/stretchr/testify/require" -) - -func TestFieldOptional(t *testing.T) { - var opt1 OptionalVal[string] - require.False(t, opt1.Present()) - s, ok := opt1.Get() - require.False(t, ok) - require.Equal(t, "", s) - - opt1.Set("a1") - require.True(t, opt1.Present()) - s, ok = opt1.Get() - require.True(t, ok) - require.Equal(t, "a1", s) - - opt1.Set("a2") - require.True(t, opt1.Present()) - s, ok = opt1.Get() - require.True(t, ok) - require.Equal(t, "a2", s) - - opt1.Clear() - require.False(t, opt1.Present()) - s, ok = opt1.Get() - require.False(t, ok) - require.Equal(t, "", s) - - type Foo struct { - v int - } - var opt2 OptionalVal[*Foo] - foo := &Foo{v: 1} - - f, ok := opt2.Get() - require.False(t, ok) - require.Nil(t, f) - - opt2.Set(foo) - require.True(t, opt2.Present()) - f, ok = opt2.Get() - require.True(t, ok) - require.Same(t, foo, f) - - opt2.Set(nil) - require.True(t, opt2.Present()) - f, ok = opt2.Get() - require.True(t, ok) - require.Nil(t, f) - - opt2.Clear() - f, ok = opt2.Get() - require.False(t, ok) - require.Nil(t, f) -} - -func TestFieldsReflect(t *testing.T) { - var cond TimerCond - require.Empty(t, cond.FieldsSet()) - - cond.Key.Set("k1") - require.Equal(t, []string{"Key"}, cond.FieldsSet()) - - cond.ID.Set("22") - require.Equal(t, []string{"ID", "Key"}, cond.FieldsSet()) - require.Equal(t, []string{"Key"}, cond.FieldsSet(unsafe.Pointer(&cond.ID))) - - cond.Key.Clear() - require.Equal(t, []string{"ID"}, cond.FieldsSet()) - - cond.KeyPrefix = true - cond.Clear() - require.Empty(t, cond.FieldsSet()) - require.False(t, cond.KeyPrefix) - - var update TimerUpdate - require.Empty(t, update.FieldsSet()) - - update.Watermark.Set(time.Now()) - require.Equal(t, []string{"Watermark"}, update.FieldsSet()) - - update.Enable.Set(true) - require.Equal(t, []string{"Enable", "Watermark"}, update.FieldsSet()) - require.Equal(t, []string{"Watermark"}, update.FieldsSet(unsafe.Pointer(&update.Enable))) - - update.Watermark.Clear() - require.Equal(t, []string{"Enable"}, update.FieldsSet()) - - update.Clear() - require.Empty(t, update.FieldsSet()) -} - -func TestTimerRecordCond(t *testing.T) { - tm := &TimerRecord{ - ID: "123", - TimerSpec: TimerSpec{ - Namespace: "n1", - Key: "/path/to/key", - Tags: []string{"tagA1", "tagA2"}, - }, - } - - // ID - cond := &TimerCond{ID: NewOptionalVal("123")} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{ID: NewOptionalVal("1")} - require.False(t, cond.Match(tm)) - - // Namespace - cond = &TimerCond{Namespace: NewOptionalVal("n1")} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{Namespace: NewOptionalVal("n2")} - require.False(t, cond.Match(tm)) - - // Key - cond = &TimerCond{Key: NewOptionalVal("/path/to/key")} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{Key: NewOptionalVal("/path/to/")} - require.False(t, cond.Match(tm)) - - // keyPrefix - cond = &TimerCond{Key: NewOptionalVal("/path/to/"), KeyPrefix: true} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{Key: NewOptionalVal("/path/to2"), KeyPrefix: true} - require.False(t, cond.Match(tm)) - - // Tags - tm2 := tm.Clone() - tm2.Tags = nil - - cond = &TimerCond{Tags: NewOptionalVal([]string{})} - require.True(t, cond.Match(tm)) - require.True(t, cond.Match(tm2)) - - cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA"})} - require.False(t, cond.Match(tm)) - require.False(t, cond.Match(tm2)) - - cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1"})} - require.True(t, cond.Match(tm)) - require.False(t, cond.Match(tm2)) - - cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1", "tagA2"})} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{Tags: NewOptionalVal([]string{"tagA1", "tagB1"})} - require.False(t, cond.Match(tm)) - - // Combined condition - cond = &TimerCond{ID: NewOptionalVal("123"), Key: NewOptionalVal("/path/to/key")} - require.True(t, cond.Match(tm)) - - cond = &TimerCond{ID: NewOptionalVal("123"), Key: NewOptionalVal("/path/to/")} - require.False(t, cond.Match(tm)) -} - -func TestOperatorCond(t *testing.T) { - tm := &TimerRecord{ - ID: "123", - TimerSpec: TimerSpec{ - Namespace: "n1", - Key: "/path/to/key", - }, - } - - cond1 := &TimerCond{ID: NewOptionalVal("123")} - cond2 := &TimerCond{ID: NewOptionalVal("456")} - cond3 := &TimerCond{Namespace: NewOptionalVal("n1")} - cond4 := &TimerCond{Namespace: NewOptionalVal("n2")} - - require.True(t, And(cond1, cond3).Match(tm)) - require.False(t, And(cond1, cond2, cond3).Match(tm)) - require.False(t, Or(cond2, cond4).Match(tm)) - require.True(t, Or(cond2, cond1, cond4).Match(tm)) - - require.False(t, Not(And(cond1, cond3)).Match(tm)) - require.True(t, Not(And(cond1, cond2, cond3)).Match(tm)) - require.True(t, Not(Or(cond2, cond4)).Match(tm)) - require.False(t, Not(Or(cond2, cond1, cond4)).Match(tm)) - - require.False(t, Not(cond1).Match(tm)) - require.True(t, Not(cond2).Match(tm)) -} - -func TestTimerUpdate(t *testing.T) { - timeutil.SetSystemTZ("Asia/Shanghai") - tpl := TimerRecord{ - ID: "123", - TimerSpec: TimerSpec{ - Namespace: "n1", - Key: "/path/to/key", - }, - Version: 567, - } - tm := tpl.Clone() - - // test check version - update := &TimerUpdate{ - Enable: NewOptionalVal(true), - CheckVersion: NewOptionalVal(uint64(0)), - } - _, err := update.apply(tm) - require.Error(t, err) - require.True(t, errors.ErrorEqual(err, ErrVersionNotMatch)) - require.Equal(t, tpl, *tm) - - // test check event id - update = &TimerUpdate{ - Enable: NewOptionalVal(true), - CheckEventID: NewOptionalVal("aa"), - } - _, err = update.apply(tm) - require.Error(t, err) - require.True(t, errors.ErrorEqual(err, ErrEventIDNotMatch)) - require.Equal(t, tpl, *tm) - - // test apply without check for some common fields - now := time.Now() - update = &TimerUpdate{ - Enable: NewOptionalVal(true), - TimeZone: NewOptionalVal("UTC"), - SchedPolicyType: NewOptionalVal(SchedEventInterval), - SchedPolicyExpr: NewOptionalVal("5h"), - Watermark: NewOptionalVal(now), - SummaryData: NewOptionalVal([]byte("summarydata1")), - EventStatus: NewOptionalVal(SchedEventTrigger), - EventID: NewOptionalVal("event1"), - EventData: NewOptionalVal([]byte("eventdata1")), - EventStart: NewOptionalVal(now.Add(time.Second)), - Tags: NewOptionalVal([]string{"l1", "l2"}), - ManualRequest: NewOptionalVal(ManualRequest{ - ManualRequestID: "req1", - ManualRequestTime: time.Unix(123, 0), - ManualTimeout: time.Minute, - ManualProcessed: true, - ManualEventID: "event1", - }), - EventExtra: NewOptionalVal(EventExtra{ - EventManualRequestID: "req", - EventWatermark: time.Unix(456, 0), - }), - } - - require.Equal(t, reflect.ValueOf(update).Elem().NumField()-2, len(update.FieldsSet())) - record, err := update.apply(tm) - require.NoError(t, err) - require.True(t, record.Enable) - require.Equal(t, "UTC", record.TimeZone) - require.Equal(t, time.UTC, record.Location) - require.Equal(t, SchedEventInterval, record.SchedPolicyType) - require.Equal(t, "5h", record.SchedPolicyExpr) - require.Equal(t, now, record.Watermark) - require.Equal(t, []byte("summarydata1"), record.SummaryData) - require.Equal(t, SchedEventTrigger, record.EventStatus) - require.Equal(t, "event1", record.EventID) - require.Equal(t, []byte("eventdata1"), record.EventData) - require.Equal(t, now.Add(time.Second), record.EventStart) - require.Equal(t, []string{"l1", "l2"}, record.Tags) - require.Equal(t, ManualRequest{ - ManualRequestID: "req1", - ManualRequestTime: time.Unix(123, 0), - ManualTimeout: time.Minute, - ManualProcessed: true, - ManualEventID: "event1", - }, record.ManualRequest) - require.False(t, record.IsManualRequesting()) - require.Equal(t, EventExtra{ - EventManualRequestID: "req", - EventWatermark: time.Unix(456, 0), - }, record.EventExtra) - require.Equal(t, tpl, *tm) - - // test apply without check for ManualRequest and EventExtra - tpl = *record.Clone() - tm = tpl.Clone() - update = &TimerUpdate{ - ManualRequest: NewOptionalVal(ManualRequest{ - ManualRequestID: "req2", - ManualRequestTime: time.Unix(789, 0), - ManualTimeout: time.Minute, - }), - EventExtra: NewOptionalVal(EventExtra{ - EventManualRequestID: "req2", - }), - } - record, err = update.apply(tm) - require.NoError(t, err) - require.Equal(t, ManualRequest{ - ManualRequestID: "req2", - ManualRequestTime: time.Unix(789, 0), - ManualTimeout: time.Minute, - }, record.ManualRequest) - require.True(t, record.IsManualRequesting()) - require.Equal(t, EventExtra{ - EventManualRequestID: "req2", - }, record.EventExtra) - require.Equal(t, tpl, *tm) - - // test apply without check for empty ManualRequest and EventExtra - tpl = *record.Clone() - tm = tpl.Clone() - update = &TimerUpdate{ - ManualRequest: NewOptionalVal(ManualRequest{}), - EventExtra: NewOptionalVal(EventExtra{}), - } - record, err = update.apply(tm) - require.NoError(t, err) - require.Equal(t, ManualRequest{}, record.ManualRequest) - require.False(t, record.IsManualRequesting()) - require.Equal(t, EventExtra{}, record.EventExtra) - require.Equal(t, tpl, *tm) - - emptyUpdate := &TimerUpdate{} - record, err = emptyUpdate.apply(tm) - require.NoError(t, err) - require.Equal(t, tpl, *record) -} diff --git a/timer/api/timer.go b/timer/api/timer.go deleted file mode 100644 index a6c6471ede7f7..0000000000000 --- a/timer/api/timer.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package api - -import ( - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/duration" - "github.com/pingcap/tidb/util/timeutil" - "github.com/robfig/cron/v3" -) - -// SchedPolicyType is the type of the event schedule policy. -type SchedPolicyType string - -const ( - // SchedEventInterval indicates to schedule events every fixed interval. - SchedEventInterval SchedPolicyType = "INTERVAL" - // SchedEventCron indicates to schedule events by cron expression. - SchedEventCron SchedPolicyType = "CRON" -) - -// SchedEventPolicy is an interface to tell the runtime how to schedule a timer's events. -type SchedEventPolicy interface { - // NextEventTime returns the time to schedule the next timer event. If the second return value is true, - // it means we have a next event schedule after `watermark`. Otherwise, it means there is no more event after `watermark`. - NextEventTime(watermark time.Time) (time.Time, bool) -} - -// SchedIntervalPolicy implements SchedEventPolicy, it is the policy of type `SchedEventInterval`. -type SchedIntervalPolicy struct { - expr string - interval time.Duration -} - -// NewSchedIntervalPolicy creates a new SchedIntervalPolicy. -func NewSchedIntervalPolicy(expr string) (*SchedIntervalPolicy, error) { - interval, err := duration.ParseDuration(expr) - if err != nil { - return nil, errors.Wrapf(err, "invalid schedule event expr '%s'", expr) - } - - return &SchedIntervalPolicy{ - expr: expr, - interval: interval, - }, nil -} - -// NextEventTime returns the next time of the timer event. -// A next event should be triggered after a time indicated by `interval` after watermark. -func (p *SchedIntervalPolicy) NextEventTime(watermark time.Time) (time.Time, bool) { - if watermark.IsZero() { - return watermark, true - } - return watermark.Add(p.interval), true -} - -// CronPolicy implements SchedEventPolicy, it is the policy of type `SchedEventCron`. -type CronPolicy struct { - cronSchedule cron.Schedule -} - -// NewCronPolicy creates a new CronPolicy. -func NewCronPolicy(expr string) (*CronPolicy, error) { - cronSchedule, err := cron.ParseStandard(expr) - if err != nil { - return nil, errors.Wrapf(err, "invalid cron expr '%s'", expr) - } - - return &CronPolicy{ - cronSchedule: cronSchedule, - }, nil -} - -// NextEventTime returns the next time of the timer event. -func (p *CronPolicy) NextEventTime(watermark time.Time) (time.Time, bool) { - next := p.cronSchedule.Next(watermark) - return next, !next.IsZero() -} - -// ManualRequest is the request info to trigger timer manually. -type ManualRequest struct { - // ManualRequestID is the id of manual request. - ManualRequestID string - // ManualRequestTime is the request time. - ManualRequestTime time.Time - // ManualTimeout is the timeout for the request, if the timer is not triggered after timeout, processed will be set to true - // with empty event id. - ManualTimeout time.Duration - // ManualProcessed indicates the request is processed (triggered or timeout). - ManualProcessed bool - // ManualEventID means the triggered event id for the current request. - ManualEventID string -} - -// IsManualRequesting indicates that whether this timer is requesting to trigger event manually. -func (r *ManualRequest) IsManualRequesting() bool { - return r.ManualRequestID != "" && !r.ManualProcessed -} - -// SetProcessed sets the timer's manual request to processed. -func (r *ManualRequest) SetProcessed(eventID string) ManualRequest { - newManual := *r - newManual.ManualProcessed = true - newManual.ManualEventID = eventID - return newManual -} - -// EventExtra stores some extra attributes for event. -type EventExtra struct { - // EventManualRequestID is the related request id of the manual trigger. - // If current event is not triggered manually, it is empty. - EventManualRequestID string - // EventWatermark is the watermark when event triggers. - EventWatermark time.Time -} - -// TimerSpec is the specification of a timer without any runtime status. -type TimerSpec struct { - // Namespace is the namespace of the timer. - Namespace string - // Key is the key of the timer. Key is unique in each namespace. - Key string - // Tags is used to tag a timer. - Tags []string - // Data is a binary which is defined by user. - Data []byte - // TimeZone is the time zone name of the timer to evaluate the schedule policy. - // If TimeZone is empty, it means to use the tidb cluster's time zone. - TimeZone string - // SchedPolicyType is the type of the event schedule policy. - SchedPolicyType SchedPolicyType - // SchedPolicyExpr is the expression of event schedule policy with the type specified by SchedPolicyType. - SchedPolicyExpr string - // HookClass is the class of the hook. - HookClass string - // Watermark indicates the progress the timer's event schedule. - Watermark time.Time - // Enable indicated whether the timer is enabled. - // If it is false, the new timer event will not be scheduled even it is up to time. - Enable bool -} - -// Clone returns a cloned TimerSpec. -func (t *TimerSpec) Clone() *TimerSpec { - clone := *t - return &clone -} - -// Validate validates the TimerSpec. -func (t *TimerSpec) Validate() error { - if t.Namespace == "" { - return errors.New("field 'Namespace' should not be empty") - } - - if t.Key == "" { - return errors.New("field 'Key' should not be empty") - } - - if err := ValidateTimeZone(t.TimeZone); err != nil { - return err - } - - if t.SchedPolicyType == "" { - return errors.New("field 'SchedPolicyType' should not be empty") - } - - if _, err := t.CreateSchedEventPolicy(); err != nil { - return errors.Wrap(err, "schedule event configuration is not valid") - } - - return nil -} - -// CreateSchedEventPolicy creates a SchedEventPolicy according to `SchedPolicyType` and `SchedPolicyExpr`. -func (t *TimerSpec) CreateSchedEventPolicy() (SchedEventPolicy, error) { - return CreateSchedEventPolicy(t.SchedPolicyType, t.SchedPolicyExpr) -} - -// CreateSchedEventPolicy creates a SchedEventPolicy according to `SchedPolicyType` and `SchedPolicyExpr`. -func CreateSchedEventPolicy(tp SchedPolicyType, expr string) (SchedEventPolicy, error) { - switch tp { - case SchedEventInterval: - return NewSchedIntervalPolicy(expr) - case SchedEventCron: - return NewCronPolicy(expr) - default: - return nil, errors.Errorf("invalid schedule event type: '%s'", tp) - } -} - -// SchedEventStatus is the current schedule status of timer's event. -type SchedEventStatus string - -const ( - // SchedEventIdle means the timer is not in trigger state currently. - SchedEventIdle SchedEventStatus = "IDLE" - // SchedEventTrigger means the timer is in trigger state. - SchedEventTrigger SchedEventStatus = "TRIGGER" -) - -// TimerRecord is the timer record saved in the timer store. -type TimerRecord struct { - TimerSpec - // ID is the id of timer, it is unique and auto assigned by the store when created. - ID string - // ManualRequest is the request to trigger timer event manually. - ManualRequest - // EventStatus indicates the current schedule status of the timer's event. - EventStatus SchedEventStatus - // EventID indicates the id of current triggered event. - // If the `EventStatus` is `IDLE`, this value should be empty. - EventID string - // EventData indicates the data of current triggered event. - // If the `EventStatus` is `IDLE`, this value should be empty. - EventData []byte - // EventStart indicates the start time of current triggered event. - // If the `EventStatus` is `IDLE`, `EventStart.IsZero()` should returns true. - EventStart time.Time - // EventExtra stores some extra attributes for event - EventExtra - // SummaryData is a binary which is used to store some summary information of the timer. - // User can update it when closing a timer's event to update the summary. - SummaryData []byte - // CreateTime is the creation time of the timer. - CreateTime time.Time - // Version is the version of the record, when the record updated, version will be increased. - Version uint64 - // Location is used to get the alias of TiDB timezone. - Location *time.Location -} - -// NextEventTime returns the next time for timer to schedule -func (r *TimerRecord) NextEventTime() (tm time.Time, _ bool, _ error) { - if !r.Enable { - return tm, false, nil - } - - watermark := r.Watermark - if loc := r.Location; loc != nil { - watermark = watermark.In(loc) - } - - policy, err := CreateSchedEventPolicy(r.SchedPolicyType, r.SchedPolicyExpr) - if err != nil { - return tm, false, err - } - - tm, ok := policy.NextEventTime(watermark) - return tm, ok, nil -} - -// Clone returns a cloned TimerRecord. -func (r *TimerRecord) Clone() *TimerRecord { - cloned := *r - cloned.TimerSpec = *r.TimerSpec.Clone() - return &cloned -} - -// ValidateTimeZone validates the TimeZone field. -func ValidateTimeZone(tz string) error { - if tz != "" { - if _, err := timeutil.ParseTimeZone(tz); err != nil { - return err - } - } - return nil -} diff --git a/timer/main_test.go b/timer/main_test.go deleted file mode 100644 index b212303082c0c..0000000000000 --- a/timer/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package timer - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/golang/glog.(*loggingT).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/timer/metrics/BUILD.bazel b/timer/metrics/BUILD.bazel deleted file mode 100644 index 9c5e47875624b..0000000000000 --- a/timer/metrics/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/timer/metrics", - visibility = ["//visibility:public"], - deps = ["@com_github_prometheus_client_golang//prometheus"], -) diff --git a/timer/runtime/BUILD.bazel b/timer/runtime/BUILD.bazel deleted file mode 100644 index 63ad49c01bc11..0000000000000 --- a/timer/runtime/BUILD.bazel +++ /dev/null @@ -1,53 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "runtime", - srcs = [ - "cache.go", - "runtime.go", - "worker.go", - ], - importpath = "github.com/pingcap/tidb/timer/runtime", - visibility = ["//visibility:public"], - deps = [ - "//timer/api", - "//timer/metrics", - "//util", - "//util/logutil", - "//util/timeutil", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_prometheus_client_golang//prometheus", - "@org_golang_x_exp//maps", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "runtime_test", - timeout = "short", - srcs = [ - "cache_test.go", - "main_test.go", - "runtime_test.go", - "worker_test.go", - ], - embed = [":runtime"], - flaky = True, - race = "on", - shard_count = 23, - deps = [ - "//testkit/testsetup", - "//timer/api", - "//util", - "//util/mock", - "//util/timeutil", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@com_github_robfig_cron_v3//:cron", - "@com_github_stretchr_testify//mock", - "@com_github_stretchr_testify//require", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/timer/runtime/cache.go b/timer/runtime/cache.go deleted file mode 100644 index 61a436fe3ed15..0000000000000 --- a/timer/runtime/cache.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package runtime - -import ( - "container/list" - "time" - - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/timeutil" -) - -type runtimeProcStatus int8 - -const ( - procIdle runtimeProcStatus = iota - procTriggering - procWaitTriggerClose -) - -type timerCacheItem struct { - timer *api.TimerRecord - nextEventTime *time.Time - nextTryTriggerTime time.Time - sortEle *list.Element - procStatus runtimeProcStatus - triggerEventID string -} - -func (c *timerCacheItem) update(timer *api.TimerRecord, nowFunc func() time.Time) bool { - if c.timer != nil { - if timer.Version < c.timer.Version { - return false - } - - if timer.Version == c.timer.Version && !locationChanged(timer.Location, c.timer.Location) { - return false - } - } - - timer = timer.Clone() - c.timer = timer - c.nextEventTime = nil - c.nextTryTriggerTime = time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC) - - if timer.Enable { - t, ok, err := timer.NextEventTime() - if err == nil && ok { - c.nextEventTime = &t - } - - if timer.IsManualRequesting() { - now := nowFunc() - c.nextEventTime = &now - } - } - - switch timer.EventStatus { - case api.SchedEventIdle: - if c.nextEventTime != nil { - c.nextTryTriggerTime = *c.nextEventTime - } - case api.SchedEventTrigger: - c.nextTryTriggerTime = timer.EventStart - } - - return true -} - -type timersCache struct { - items map[string]*timerCacheItem - // sorted is the sorted timers by `nextTryTriggerTime` - sorted *list.List - waitCloseTimerIDs map[string]struct{} - nowFunc func() time.Time -} - -func newTimersCache() *timersCache { - return &timersCache{ - items: make(map[string]*timerCacheItem), - sorted: list.New(), - waitCloseTimerIDs: make(map[string]struct{}), - nowFunc: time.Now, - } -} - -func (c *timersCache) updateTimer(timer *api.TimerRecord) bool { - item, ok := c.items[timer.ID] - if !ok { - item = &timerCacheItem{} - c.items[timer.ID] = item - } - - var change bool - if change = item.update(timer, c.nowFunc); change { - c.resort(item) - } - - if item.procStatus == procWaitTriggerClose && item.triggerEventID != timer.EventID { - c.setTimerProcStatus(timer.ID, procIdle, "") - } - - return change -} - -func (c *timersCache) removeTimer(timerID string) bool { - item, ok := c.items[timerID] - if !ok { - return false - } - - delete(c.items, timerID) - c.sorted.Remove(item.sortEle) - delete(c.waitCloseTimerIDs, timerID) - return true -} - -func (c *timersCache) hasTimer(timerID string) (exist bool) { - _, exist = c.items[timerID] - return -} - -func (c *timersCache) partialBatchUpdateTimers(timers []*api.TimerRecord) bool { - change := false - for _, timer := range timers { - if c.updateTimer(timer) { - change = true - } - } - return change -} - -func (c *timersCache) fullUpdateTimers(timers []*api.TimerRecord) { - id2Timer := make(map[string]*api.TimerRecord, len(timers)) - for _, timer := range timers { - id2Timer[timer.ID] = timer - } - - for id := range c.items { - _, ok := id2Timer[id] - if !ok { - c.removeTimer(id) - } - } - c.partialBatchUpdateTimers(timers) -} - -func (c *timersCache) setTimerProcStatus(timerID string, status runtimeProcStatus, triggerEventID string) { - item, ok := c.items[timerID] - if ok { - item.procStatus = status - item.triggerEventID = triggerEventID - if item.procStatus == procWaitTriggerClose { - c.waitCloseTimerIDs[timerID] = struct{}{} - } else { - delete(c.waitCloseTimerIDs, timerID) - } - } -} - -func (c *timersCache) updateNextTryTriggerTime(timerID string, time time.Time) { - item, ok := c.items[timerID] - if !ok { - return - } - - // to make sure try trigger time is always after next event time - if item.timer.EventStatus == api.SchedEventIdle && (item.nextEventTime == nil || time.Before(*item.nextEventTime)) { - return - } - - item.nextTryTriggerTime = time - c.resort(item) -} - -func (c *timersCache) iterTryTriggerTimers(fn func(timer *api.TimerRecord, tryTriggerTime time.Time, nextEventTime *time.Time) bool) { - ele := c.sorted.Front() - for ele != nil { - next := ele.Next() - if item, ok := ele.Value.(*timerCacheItem); ok && item.procStatus == procIdle { - if !fn(item.timer, item.nextTryTriggerTime, item.nextEventTime) { - break - } - } - ele = next - } -} - -func (c *timersCache) resort(item *timerCacheItem) { - ele := item.sortEle - if ele == nil { - ele = c.sorted.PushBack(item) - item.sortEle = ele - } - - nextTrigger := item.nextTryTriggerTime - - if cur := ele.Prev(); cur != nil && cur.Value.(*timerCacheItem).nextTryTriggerTime.After(nextTrigger) { - prev := cur.Prev() - for prev != nil && prev.Value.(*timerCacheItem).nextTryTriggerTime.After(nextTrigger) { - cur = prev - prev = cur.Prev() - } - c.sorted.MoveBefore(ele, cur) - return - } - - if cur := ele.Next(); cur != nil && cur.Value.(*timerCacheItem).nextTryTriggerTime.Before(nextTrigger) { - next := cur.Next() - for next != nil && next.Value.(*timerCacheItem).nextTryTriggerTime.Before(nextTrigger) { - cur = next - next = cur.Next() - } - c.sorted.MoveAfter(ele, cur) - return - } -} - -func locationChanged(a *time.Location, b *time.Location) bool { - if a == b { - return false - } - - if a == nil || b == nil { - return true - } - - _, offset1 := timeutil.Zone(a) - _, offset2 := timeutil.Zone(b) - return offset1 != offset2 -} diff --git a/timer/runtime/cache_test.go b/timer/runtime/cache_test.go deleted file mode 100644 index 4e3b8f01b0aef..0000000000000 --- a/timer/runtime/cache_test.go +++ /dev/null @@ -1,477 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package runtime - -import ( - "fmt" - "testing" - "time" - - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/timeutil" - "github.com/robfig/cron/v3" - "github.com/stretchr/testify/require" -) - -func newTestTimer(id string, policyExpr string, watermark time.Time) *api.TimerRecord { - return &api.TimerRecord{ - ID: id, - TimerSpec: api.TimerSpec{ - Namespace: "n1", - Key: fmt.Sprintf("key-" + id), - SchedPolicyType: api.SchedEventInterval, - SchedPolicyExpr: policyExpr, - HookClass: "hook1", - Watermark: watermark, - Enable: true, - }, - Location: watermark.Location(), - EventStatus: api.SchedEventIdle, - Version: 1, - } -} - -func TestCacheUpdate(t *testing.T) { - now := time.Now().In(time.UTC) - nowFunc := func() time.Time { - return now - } - - cache := newTimersCache() - cache.nowFunc = nowFunc - - // update - t1 := newTestTimer("t1", "10m", now) - require.True(t, cache.updateTimer(t1)) - require.NotSame(t, t1, cache.items[t1.ID].timer) - checkSortedCache(t, cache, [][]any{{t1, now.Add(10 * time.Minute)}}) - require.Equal(t, 1, len(cache.items)) - - // dup update with same version - require.False(t, cache.updateTimer(t1.Clone())) - checkSortedCache(t, cache, [][]any{{t1, now.Add(10 * time.Minute)}}) - require.Equal(t, 1, len(cache.items)) - - // policy changed - t1.SchedPolicyType = api.SchedEventCron - t1.SchedPolicyExpr = "* 1 * * *" - t1.Version++ - require.True(t, cache.updateTimer(t1)) - require.NotSame(t, t1, cache.items[t1.ID].timer) - c, err := cron.ParseStandard(t1.SchedPolicyExpr) - require.NoError(t, err) - checkSortedCache(t, cache, [][]any{{t1, c.Next(now)}}) - require.Equal(t, 1, len(cache.items)) - - // update with same version but loc changed - t1.Location = time.FixedZone("name1", 2*60*60) - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{{t1, c.Next(now.In(t1.Location))}}) - require.Equal(t, 1, len(cache.items)) - - // invalid policy - t1.Location = now.Location() - t1.SchedPolicyType = api.SchedEventInterval - t1.SchedPolicyExpr = "invalid" - t1.Version++ - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) - require.Equal(t, 1, len(cache.items)) - - // manual set next try trigger time for invalid timer - cache.updateNextTryTriggerTime(t1.ID, now.Add(7*time.Second)) - checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) - require.Equal(t, 1, len(cache.items)) - - // not enable - t1.SchedPolicyExpr = "1m" - t1.Enable = false - t1.Version++ - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{{t1, time.Date(2999, 1, 1, 0, 0, 0, 0, time.UTC)}}) - require.Equal(t, 1, len(cache.items)) - - // manual set next try trigger time but before nextEventTime - t1.Enable = true - t1.Version++ - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) - cache.updateNextTryTriggerTime(t1.ID, now.Add(time.Minute-time.Second)) - checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) - - // manual set next try trigger - cache.updateNextTryTriggerTime(t1.ID, now.Add(time.Minute+time.Second)) - checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute + time.Second)}}) - - // should not change procTriggering state - t1.Enable = true - t1.Version++ - cache.setTimerProcStatus(t1.ID, procTriggering, "event1") - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, nil) - require.Equal(t, 1, len(cache.items)) - require.Equal(t, procTriggering, cache.items[t1.ID].procStatus) - require.Equal(t, "event1", cache.items[t1.ID].triggerEventID) - - // test SchedEventTrigger but procIdle - t1.SchedPolicyExpr = "1m" - t1.EventStatus = api.SchedEventTrigger - t1.EventStart = now.Add(-10 * time.Second) - t1.EventID = "event1" - t1.Version++ - require.True(t, cache.updateTimer(t1)) - cache.setTimerProcStatus(t1.ID, procIdle, "event1") - checkSortedCache(t, cache, [][]any{{t1, now.Add(-10 * time.Second)}}) - require.Equal(t, 1, len(cache.items)) - require.Equal(t, 0, len(cache.waitCloseTimerIDs)) - - // should reset procWaitTriggerClose to procIdle - cache.setTimerProcStatus(t1.ID, procWaitTriggerClose, "event1") - checkSortedCache(t, cache, nil) - require.Equal(t, 1, len(cache.items)) - require.Equal(t, 1, len(cache.waitCloseTimerIDs)) - require.Contains(t, cache.waitCloseTimerIDs, t1.ID) - - t1.Version++ - require.True(t, cache.updateTimer(t1)) - require.Equal(t, 1, len(cache.items)) - require.Equal(t, 1, len(cache.waitCloseTimerIDs)) - require.Contains(t, cache.waitCloseTimerIDs, t1.ID) - require.Equal(t, procWaitTriggerClose, cache.items[t1.ID].procStatus) - - t1.EventStatus = api.SchedEventIdle - t1.EventID = "" - t1.Version++ - require.True(t, cache.updateTimer(t1)) - require.Equal(t, procIdle, cache.items[t1.ID].procStatus) - require.Equal(t, "", cache.items[t1.ID].triggerEventID) - require.Equal(t, 0, len(cache.waitCloseTimerIDs)) - require.NotContains(t, cache.waitCloseTimerIDs, t1.ID) - - t1.Version++ - t1.ManualRequest = api.ManualRequest{ - ManualRequestID: "req1", - ManualRequestTime: now, - ManualTimeout: time.Minute, - ManualProcessed: true, - } - require.True(t, cache.updateTimer(t1)) - require.Equal(t, procIdle, cache.items[t1.ID].procStatus) - require.Equal(t, "", cache.items[t1.ID].triggerEventID) - require.Equal(t, 0, len(cache.waitCloseTimerIDs)) - checkSortedCache(t, cache, [][]any{{t1, now.Add(time.Minute)}}) - - t1.Version++ - t1.ManualRequest = api.ManualRequest{ - ManualRequestID: "req2", - ManualRequestTime: now, - ManualTimeout: time.Minute, - } - require.True(t, cache.updateTimer(t1)) - require.Equal(t, procIdle, cache.items[t1.ID].procStatus) - require.Equal(t, "", cache.items[t1.ID].triggerEventID) - require.Equal(t, 0, len(cache.waitCloseTimerIDs)) - checkSortedCache(t, cache, [][]any{{t1, now}}) -} - -func TestCacheSort(t *testing.T) { - now := time.Now().In(time.UTC) - nowFunc := func() time.Time { - return now - } - - cache := newTimersCache() - cache.nowFunc = nowFunc - - checkSortedCache(t, cache, nil) - - t1 := newTestTimer("t1", "10m", now) - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(10 * time.Minute)}, - }) - - t2 := newTestTimer("t2", "20m", now) - require.True(t, cache.updateTimer(t2)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(10 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - }) - - t3 := newTestTimer("t3", "5m", now) - require.True(t, cache.updateTimer(t3)) - checkSortedCache(t, cache, [][]any{ - {t3, now.Add(5 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - }) - - t4 := newTestTimer("t4", "3m", now) - require.True(t, cache.updateTimer(t4)) - checkSortedCache(t, cache, [][]any{ - {t4, now.Add(3 * time.Minute)}, - {t3, now.Add(5 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - }) - - // move left 1 - t3.SchedPolicyExpr = "1m" - t3.Version++ - require.True(t, cache.updateTimer(t3)) - checkSortedCache(t, cache, [][]any{ - {t3, now.Add(1 * time.Minute)}, - {t4, now.Add(3 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - }) - - // move left 2 - t2.SchedPolicyExpr = "2m" - t2.Version++ - require.True(t, cache.updateTimer(t2)) - checkSortedCache(t, cache, [][]any{ - {t3, now.Add(1 * time.Minute)}, - {t2, now.Add(2 * time.Minute)}, - {t4, now.Add(3 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - }) - - // move right 1 - t4.SchedPolicyExpr = "15m" - t4.Version++ - require.True(t, cache.updateTimer(t4)) - checkSortedCache(t, cache, [][]any{ - {t3, now.Add(1 * time.Minute)}, - {t2, now.Add(2 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t4, now.Add(15 * time.Minute)}, - }) - - // move right 2 - t3.SchedPolicyExpr = "12m" - t3.Version++ - require.True(t, cache.updateTimer(t3)) - checkSortedCache(t, cache, [][]any{ - {t2, now.Add(2 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t4, now.Add(15 * time.Minute)}, - }) - - // unchanged - t2.SchedPolicyExpr = "1m" - t2.Version++ - require.True(t, cache.updateTimer(t2)) - checkSortedCache(t, cache, [][]any{ - {t2, now.Add(1 * time.Minute)}, - {t1, now.Add(10 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t4, now.Add(15 * time.Minute)}, - }) - - t1.SchedPolicyExpr = "11m" - t1.Version++ - require.True(t, cache.updateTimer(t1)) - checkSortedCache(t, cache, [][]any{ - {t2, now.Add(1 * time.Minute)}, - {t1, now.Add(11 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t4, now.Add(15 * time.Minute)}, - }) - - t4.SchedPolicyExpr = "16m" - t4.Version++ - require.True(t, cache.updateTimer(t4)) - checkSortedCache(t, cache, [][]any{ - {t2, now.Add(1 * time.Minute)}, - {t1, now.Add(11 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t4, now.Add(16 * time.Minute)}, - }) - - // test updateNextTryTriggerTime - cache.updateNextTryTriggerTime(t2.ID, now.Add(20*time.Minute)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(11 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t4, now.Add(16 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - }) - - cache.updateNextTryTriggerTime(t2.ID, now.Add(14*time.Minute)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(11 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t2, now.Add(14 * time.Minute)}, - {t4, now.Add(16 * time.Minute)}, - }) - - cache.updateNextTryTriggerTime(t3.ID, now.Add(15*time.Minute)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(11 * time.Minute)}, - {t2, now.Add(14 * time.Minute)}, - {t3, now.Add(15 * time.Minute)}, - {t4, now.Add(16 * time.Minute)}, - }) - - // test version update should reset updateNextTryTriggerTime - t3.Version++ - require.True(t, cache.updateTimer(t3)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(11 * time.Minute)}, - {t3, now.Add(12 * time.Minute)}, - {t2, now.Add(14 * time.Minute)}, - {t4, now.Add(16 * time.Minute)}, - }) -} - -func TestFullUpdateCache(t *testing.T) { - now := time.Now().In(time.UTC) - cache := newTimersCache() - cache.nowFunc = func() time.Time { - return now - } - - t1 := newTestTimer("t1", "10m", now) - t2 := newTestTimer("t2", "20m", now) - t3 := newTestTimer("t3", "30m", now) - t4 := newTestTimer("t4", "40m", now) - - require.True(t, cache.updateTimer(t1)) - require.True(t, cache.updateTimer(t2)) - require.True(t, cache.updateTimer(t3)) - require.True(t, cache.updateTimer(t4)) - checkSortedCache(t, cache, [][]any{ - {t1, now.Add(10 * time.Minute)}, - {t2, now.Add(20 * time.Minute)}, - {t3, now.Add(30 * time.Minute)}, - {t4, now.Add(40 * time.Minute)}, - }) - - t1.SchedPolicyExpr = "15m" - t1.Version++ - t3.SchedPolicyExpr = "1m" - t3.Version++ - t5 := newTestTimer("t5", "25m", now) - cache.fullUpdateTimers([]*api.TimerRecord{t1, t3, t5}) - checkSortedCache(t, cache, [][]any{ - {t3, now.Add(1 * time.Minute)}, - {t1, now.Add(15 * time.Minute)}, - {t5, now.Add(25 * time.Minute)}, - }) - require.Equal(t, 3, len(cache.items)) -} - -func checkSortedCache(t *testing.T, cache *timersCache, sorted [][]any) { - i := 0 - cache.iterTryTriggerTimers(func(timer *api.TimerRecord, tryTriggerTime time.Time, nextEventTime *time.Time) bool { - expectedTimer := sorted[i][0].(*api.TimerRecord) - require.NotSame(t, expectedTimer, timer) - require.Equal(t, *expectedTimer, *timer) - item, ok := cache.items[timer.ID] - require.True(t, ok) - require.Equal(t, *expectedTimer, *item.timer) - - if timer.IsManualRequesting() { - require.Equal(t, tryTriggerTime, *nextEventTime) - } else { - if tm, ok, err := timer.NextEventTime(); err == nil { - if !timer.Enable { - require.True(t, tm.IsZero()) - require.False(t, ok) - } else { - require.True(t, ok) - require.NotNil(t, nextEventTime) - require.Equal(t, tm, *nextEventTime) - } - } else { - require.Nil(t, nextEventTime) - } - } - - require.Equal(t, sorted[i][1].(time.Time), tryTriggerTime) - i++ - return true - }) - require.Equal(t, len(sorted), i) -} - -func TestLocationChanged(t *testing.T) { - loc1, _ := time.LoadLocation("America/New_York") - loc2, _ := time.LoadLocation("America/Los_Angeles") - loc3, _ := time.LoadLocation("America/New_York") - loc4 := time.FixedZone("name1", 2*60*60) - loc5 := time.FixedZone("name2", 2*60*60) - loc6 := time.FixedZone("name1", 60*60) - - testCases := []struct { - a *time.Location - b *time.Location - changed bool - }{ - { - a: nil, - b: nil, - changed: false, - }, - { - a: loc1, - b: nil, - changed: true, - }, - { - a: nil, - b: loc1, - changed: true, - }, - { - a: loc1, - b: loc2, - changed: true, - }, - { - a: loc1, - b: loc3, - changed: false, - }, - { - a: loc4, - b: loc5, - changed: false, - }, - { - a: loc4, - b: loc6, - changed: true, - }, - } - - for i, tc := range testCases { - result := locationChanged(tc.a, tc.b) - a, b := "", "" - if tc.a != nil { - n, offset := timeutil.Zone(tc.a) - a = fmt.Sprintf("%s(%d)", n, offset) - } - - if tc.b != nil { - n, offset := timeutil.Zone(tc.b) - b = fmt.Sprintf("%s(%d)", n, offset) - } - - require.Equalf(t, tc.changed, result, "%d: compare %q and %q", i, a, b) - } -} diff --git a/timer/runtime/main_test.go b/timer/runtime/main_test.go deleted file mode 100644 index 6782ad98bb01e..0000000000000 --- a/timer/runtime/main_test.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package runtime - -import ( - "context" - "fmt" - "sync" - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/mock" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m) -} - -type mockHook struct { - mock.Mock - started chan struct{} - stopped chan struct{} -} - -type newHookFn struct { - mock.Mock -} - -func (n *newHookFn) OnFuncCall() *mock.Call { - return n.On("Func") -} - -func (n *newHookFn) Func() api.Hook { - args := n.Called() - if v := args.Get(0); v != nil { - return v.(api.Hook) - } - return nil -} - -func onlyOnceNewHook(hook api.Hook) func() api.Hook { - n := newHookFn{} - n.OnFuncCall().Return(hook).Once() - return n.Func -} - -func newMockHook() *mockHook { - return &mockHook{ - started: make(chan struct{}), - stopped: make(chan struct{}), - } -} - -func (h *mockHook) Start() { - h.Called() - close(h.started) -} - -func (h *mockHook) Stop() { - h.Called() - close(h.stopped) -} - -func (h *mockHook) OnPreSchedEvent(ctx context.Context, event api.TimerShedEvent) (api.PreSchedEventResult, error) { - args := h.Called(ctx, event) - return args.Get(0).(api.PreSchedEventResult), args.Error(1) -} - -func (h *mockHook) OnSchedEvent(ctx context.Context, event api.TimerShedEvent) error { - args := h.Called(ctx, event) - return args.Error(0) -} - -type mockStoreCore struct { - mock.Mock -} - -func newMockStore() (*mockStoreCore, *api.TimerStore) { - core := &mockStoreCore{} - return core, &api.TimerStore{TimerStoreCore: core} -} - -func (s *mockStoreCore) mock() *mock.Mock { - return &s.Mock -} - -func (s *mockStoreCore) Create(ctx context.Context, record *api.TimerRecord) (string, error) { - args := s.Called(ctx, record) - return args.String(0), args.Error(1) -} - -func (s *mockStoreCore) List(ctx context.Context, cond api.Cond) ([]*api.TimerRecord, error) { - args := s.Called(ctx, cond) - return args.Get(0).([]*api.TimerRecord), args.Error(1) -} - -func (s *mockStoreCore) Update(ctx context.Context, timerID string, update *api.TimerUpdate) error { - args := s.Called(ctx, timerID, update) - return args.Error(0) -} - -func (s *mockStoreCore) Delete(ctx context.Context, timerID string) (bool, error) { - args := s.Called(ctx, timerID) - return args.Bool(0), args.Error(1) -} - -func (s *mockStoreCore) WatchSupported() bool { - args := s.Called() - return args.Bool(0) -} - -func (s *mockStoreCore) Watch(ctx context.Context) api.WatchTimerChan { - args := s.Called(ctx) - return args.Get(0).(api.WatchTimerChan) -} - -func (s *mockStoreCore) Close() { -} - -func waitDone(obj any, timeout time.Duration) { - var ch <-chan struct{} - switch o := obj.(type) { - case chan struct{}: - ch = o - case <-chan struct{}: - ch = o - case *util.WaitGroupWrapper: - newCh := make(chan struct{}) - ch = newCh - - go func() { - o.Wait() - close(newCh) - }() - case *sync.WaitGroup: - newCh := make(chan struct{}) - ch = newCh - - go func() { - o.Wait() - close(newCh) - }() - default: - panic(fmt.Sprintf("unsupported type: %T", obj)) - } - - tm := time.NewTimer(timeout) - defer tm.Stop() - select { - case <-ch: - return - case <-tm.C: - panic("wait done timeout") - } -} - -func checkNotDone(ch <-chan struct{}, after time.Duration) { - if after != 0 { - time.Sleep(after) - } - select { - case <-ch: - panic("the channel is expected not done") - default: - } -} diff --git a/timer/runtime/worker.go b/timer/runtime/worker.go deleted file mode 100644 index df36d24a73130..0000000000000 --- a/timer/runtime/worker.go +++ /dev/null @@ -1,450 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package runtime - -import ( - "context" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/timer/metrics" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/prometheus/client_golang/prometheus" - "go.uber.org/zap" -) - -type hookWorkerRetryLoopKeyType struct{} -type hookWorkerRetryRequestKeyType struct{} - -// keys for to set retry interval -// only used for test -var ( - hookWorkerRetryLoopKey = hookWorkerRetryLoopKeyType{} - hookWorkerRetryRequestKey = hookWorkerRetryRequestKeyType{} -) - -const ( - workerRecvChanCap = 128 - workerRespChanCap = 128 - workerEventDefaultRetryInterval = 10 * time.Second - chanBlockInterval = time.Minute -) - -type triggerEventRequest struct { - eventID string - timer *api.TimerRecord - store *api.TimerStore - resp chan<- *triggerEventResponse -} - -func (r *triggerEventRequest) DoneResponse() *triggerEventResponse { - return &triggerEventResponse{ - timerID: r.timer.ID, - eventID: r.eventID, - success: true, - } -} - -func (r *triggerEventRequest) RetryDefaultResponse() *triggerEventResponse { - return &triggerEventResponse{ - timerID: r.timer.ID, - eventID: r.eventID, - retryAfter: api.NewOptionalVal(workerEventDefaultRetryInterval), - success: false, - } -} - -func (r *triggerEventRequest) TimerMetaChangedResponse(t *api.TimerRecord) *triggerEventResponse { - return r.RetryDefaultResponse(). - WithNewTimerRecord(t). - WithRetryImmediately() -} - -type triggerEventResponse struct { - success bool - timerID string - eventID string - newTimerRecord api.OptionalVal[*api.TimerRecord] - retryAfter api.OptionalVal[time.Duration] -} - -func (r *triggerEventResponse) WithRetryImmediately() *triggerEventResponse { - r.retryAfter.Clear() - return r -} - -func (r *triggerEventResponse) WithRetryAfter(d time.Duration) *triggerEventResponse { - r.retryAfter.Set(d) - return r -} - -func (r *triggerEventResponse) WithNewTimerRecord(timer *api.TimerRecord) *triggerEventResponse { - r.newTimerRecord.Set(timer) - return r -} - -type timerEvent struct { - eventID string - record *api.TimerRecord -} - -func (e *timerEvent) EventID() string { - return e.eventID -} - -func (e *timerEvent) Timer() *api.TimerRecord { - return e.record -} - -type hookWorker struct { - ctx context.Context - groupID string - hookClass string - hookFn func() api.Hook - ch chan *triggerEventRequest - logger *zap.Logger - nowFunc func() time.Time - // metrics for worker - triggerRequestCounter prometheus.Counter - onPreSchedEventCounter prometheus.Counter - onPreSchedEventErrCounter prometheus.Counter - onPreSchedEventDelayCounter prometheus.Counter - onSchedEventCounter prometheus.Counter - onSchedEventErrCounter prometheus.Counter - // retryLoopWait/retryRequestWait indicates the wait time before restarting the loop after panic. - retryLoopWait time.Duration - retryRequestWait time.Duration -} - -func newHookWorker(ctx context.Context, wg *util.WaitGroupWrapper, groupID string, hookClass string, hookFn func() api.Hook, nowFunc func() time.Time) *hookWorker { - if nowFunc == nil { - nowFunc = time.Now - } - - w := &hookWorker{ - ctx: ctx, - groupID: groupID, - hookClass: hookClass, - hookFn: hookFn, - ch: make(chan *triggerEventRequest, workerRecvChanCap), - logger: logutil.BgLogger().With( - zap.String("groupID", groupID), - zap.String("hookClass", hookClass), - ), - nowFunc: nowFunc, - triggerRequestCounter: metrics.TimerHookWorkerCounter(hookClass, "trigger"), - onPreSchedEventCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent"), - onPreSchedEventErrCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent_error"), - onPreSchedEventDelayCounter: metrics.TimerHookWorkerCounter(hookClass, "OnPreSchedEvent_delay"), - onSchedEventCounter: metrics.TimerHookWorkerCounter(hookClass, "OnSchedEvent"), - onSchedEventErrCounter: metrics.TimerHookWorkerCounter(hookClass, "OnSchedEvent_error"), - retryLoopWait: 10 * time.Second, - retryRequestWait: 5 * time.Second, - } - - if v, ok := ctx.Value(hookWorkerRetryLoopKey).(time.Duration); ok { - w.retryLoopWait = v - } - - if v, ok := ctx.Value(hookWorkerRetryRequestKey).(time.Duration); ok { - w.retryRequestWait = v - } - - wg.Run(func() { - withRecoverUntil(w.ctx, w.loop) - }) - return w -} - -func (w *hookWorker) loop(totalPanic uint64) { - if totalPanic > 0 { - sleep(w.ctx, w.retryLoopWait) - w.logger.Info("timer hookWorker loop resumed from panic", - zap.Uint64("totalPanic", totalPanic), - zap.Duration("delay", w.retryLoopWait)) - } else { - w.logger.Info("timer hookWorker loop started") - } - defer w.logger.Info("timer hookWorker loop exited") - - var hook api.Hook - if w.hookFn != nil { - hook = w.hookFn() - } - - if hook != nil { - defer hook.Stop() - hook.Start() - } - - // TODO: we can have multiple `handleRequestLoop` goroutines running concurrently. - w.handleRequestLoop(hook) -} - -type unhandledRequest struct { - req *triggerEventRequest - retry int -} - -func (w *hookWorker) handleRequestLoop(hook api.Hook) { - var unhandled *unhandledRequest - withRecoverUntil(w.ctx, func(totalPanic uint64) { - wait := w.retryRequestWait - if totalPanic == 0 || (unhandled != nil && unhandled.retry == 0) { - // when retry a request, it will send a response to runtime without calling hook. - // So we can do the first retry immediately to assumption that it will succeed. - wait = 0 - } - - if totalPanic > 0 { - time.Sleep(wait) - w.logger.Info("handleRequestLoop resumed from panic", - zap.Uint64("totalPanic", totalPanic), - zap.Duration("delay", wait)) - } - - if unhandled != nil { - unhandled.retry++ - w.handleRequestOnce(hook, unhandled) - unhandled = nil - } - - for { - select { - case <-w.ctx.Done(): - return - case req := <-w.ch: - unhandled = &unhandledRequest{ - req: req, - } - w.handleRequestOnce(hook, unhandled) - unhandled = nil - } - } - }) -} - -func (w *hookWorker) handleRequestOnce(hook api.Hook, u *unhandledRequest) { - if u == nil || u.req == nil { - return - } - - if u.req.timer == nil { - w.logger.Warn("invalid triggerEventRequest, timer is nil") - return - } - - req := u.req - logger := w.logger.With( - zap.String("timerID", req.timer.ID), - zap.String("timerNamespace", req.timer.Namespace), - zap.String("timerKey", req.timer.Key), - zap.String("eventID", req.eventID), - zap.Int("requestRetry", u.retry), - ) - - if req.resp == nil { - logger.Warn("invalid triggerEventRequest, resp chan is nil") - return - } - - var resp *triggerEventResponse - if u.retry > 0 { - logger.Info("retry triggerEventRequest") - resp = req.RetryDefaultResponse() - } else { - resp = w.triggerEvent(hook, req, logger) - } - w.responseChan(req.resp, resp, logger) -} - -func (w *hookWorker) triggerEvent(hook api.Hook, req *triggerEventRequest, logger *zap.Logger) *triggerEventResponse { - w.triggerRequestCounter.Inc() - timer := req.timer - - if timer.IsManualRequesting() { - logger.Info("manual trigger request detected", - zap.String("requestID", timer.ManualRequestID), - zap.Time("requestTime", timer.ManualRequestTime), - zap.Duration("timeout", timer.ManualTimeout), - ) - timeout := timer.ManualRequestTime.Add(timer.ManualTimeout) - if w.nowFunc().After(timeout) { - logger.Warn( - "cancel manual trigger for timer is disabled for request timeout", - zap.String("requestID", timer.ManualRequestID), - zap.Bool("timerEnable", timer.Enable), - ) - - processed := timer.ManualRequest.SetProcessed("") - err := req.store.Update(w.ctx, timer.ID, &api.TimerUpdate{ - ManualRequest: api.NewOptionalVal(processed), - CheckVersion: api.NewOptionalVal(timer.Version), - }) - - if err == nil { - timer, err = req.store.GetByID(w.ctx, timer.ID) - } - - if err == nil || errors.ErrorEqual(err, api.ErrTimerNotExist) { - return req.TimerMetaChangedResponse(timer) - } - - logger.Error( - "error occurs when close manual request", - zap.Error(err), - zap.Duration("retryAfter", workerEventDefaultRetryInterval), - ) - return req.RetryDefaultResponse() - } - } - - if timer.EventStatus == api.SchedEventIdle { - var preResult api.PreSchedEventResult - if hook != nil { - logger.Debug("call OnPreSchedEvent") - w.onPreSchedEventCounter.Inc() - result, err := hook.OnPreSchedEvent(w.ctx, &timerEvent{ - eventID: req.eventID, - record: timer, - }) - - if err != nil { - logger.Error( - "error occurs when invoking hook.OnPreSchedEvent", - zap.Error(err), - zap.Duration("retryAfter", workerEventDefaultRetryInterval), - ) - w.onPreSchedEventErrCounter.Inc() - return req.RetryDefaultResponse() - } - - if result.Delay > 0 { - w.onPreSchedEventDelayCounter.Inc() - return req.RetryDefaultResponse().WithRetryAfter(result.Delay) - } - preResult = result - } - - update := buildEventUpdate(req, preResult, w.nowFunc) - if err := req.store.Update(w.ctx, timer.ID, update); err != nil { - if errors.ErrorEqual(err, api.ErrVersionNotMatch) { - logger.Info("cannot change timer to trigger state, timer version not match", - zap.Uint64("timerVersion", timer.Version), - ) - var newTimer *api.TimerRecord - newTimer, err = req.store.GetByID(w.ctx, timer.ID) - if err == nil { - return req.TimerMetaChangedResponse(newTimer) - } - } - - if errors.ErrorEqual(err, api.ErrTimerNotExist) { - logger.Info("cannot change timer to trigger state, timer deleted") - return req.TimerMetaChangedResponse(nil) - } - - logger.Error("error occurs to change timer to trigger state,", - zap.Error(err), - zap.Duration("retryAfter", workerEventDefaultRetryInterval), - ) - return req.RetryDefaultResponse() - } - } - - timer, err := req.store.GetByID(w.ctx, timer.ID) - if errors.ErrorEqual(err, api.ErrTimerNotExist) { - logger.Info("cannot trigger timer event, timer deleted") - return req.TimerMetaChangedResponse(timer) - } - - if err != nil { - logger.Error( - "error occurs when getting timer record to trigger timer event", - zap.Duration("retryAfter", workerEventDefaultRetryInterval), - ) - return req.RetryDefaultResponse() - } - - if timer.EventID != req.eventID { - logger.Info("cannot trigger timer event, timer event closed") - return req.TimerMetaChangedResponse(timer) - } - - if hook != nil { - logger.Debug("call OnSchedEvent") - w.onSchedEventCounter.Inc() - err = hook.OnSchedEvent(w.ctx, &timerEvent{ - eventID: req.eventID, - record: timer, - }) - - if err != nil { - w.onSchedEventErrCounter.Inc() - logger.Error( - "error occurs when invoking hook OnTimerEvent", - zap.Error(err), - zap.Duration("retryAfter", workerEventDefaultRetryInterval), - ) - return req.RetryDefaultResponse().WithNewTimerRecord(timer) - } - } - - return req.DoneResponse().WithNewTimerRecord(timer) -} - -func (w *hookWorker) responseChan(ch chan<- *triggerEventResponse, resp *triggerEventResponse, logger *zap.Logger) bool { - sendStart := time.Now() - ticker := time.NewTicker(chanBlockInterval) - defer ticker.Stop() - for { - select { - case <-w.ctx.Done(): - logger.Info("sending resp to chan aborted for context cancelled") - zap.Duration("totalBlock", time.Since(sendStart)) - return false - case ch <- resp: - return true - case <-ticker.C: - logger.Warn( - "sending resp to chan is blocked for a long time", - zap.Duration("totalBlock", time.Since(sendStart)), - ) - } - } -} - -func buildEventUpdate(req *triggerEventRequest, result api.PreSchedEventResult, nowFunc func() time.Time) *api.TimerUpdate { - var update api.TimerUpdate - update.EventStatus.Set(api.SchedEventTrigger) - update.EventID.Set(req.eventID) - update.EventStart.Set(nowFunc()) - update.EventData.Set(result.EventData) - update.CheckVersion.Set(req.timer.Version) - - eventExtra := api.EventExtra{ - EventWatermark: req.timer.Watermark, - } - - if manual := req.timer.ManualRequest; manual.IsManualRequesting() { - eventExtra.EventManualRequestID = manual.ManualRequestID - update.ManualRequest.Set(manual.SetProcessed(req.eventID)) - } - - update.EventExtra.Set(eventExtra) - return &update -} diff --git a/timer/tablestore/BUILD.bazel b/timer/tablestore/BUILD.bazel deleted file mode 100644 index 5a11fc9cf3358..0000000000000 --- a/timer/tablestore/BUILD.bazel +++ /dev/null @@ -1,52 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "tablestore", - srcs = [ - "notifier.go", - "sql.go", - "store.go", - ], - importpath = "github.com/pingcap/tidb/timer/tablestore", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//timer/api", - "//util/chunk", - "//util/logutil", - "//util/sqlexec", - "//util/timeutil", - "@com_github_google_uuid//:uuid", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//util", - "@io_etcd_go_etcd_api_v3//mvccpb", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "tablestore_test", - timeout = "short", - srcs = ["sql_test.go"], - embed = [":tablestore"], - flaky = True, - race = "on", - shard_count = 8, - deps = [ - "//kv", - "//sessionctx", - "//sessionctx/variable", - "//timer/api", - "//util/sqlexec", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_stretchr_testify//mock", - "@com_github_stretchr_testify//require", - ], -) diff --git a/timer/tablestore/sql.go b/timer/tablestore/sql.go deleted file mode 100644 index 8f9bf7b9cc10d..0000000000000 --- a/timer/tablestore/sql.go +++ /dev/null @@ -1,484 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tablestore - -import ( - "encoding/json" - "fmt" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/timer/api" -) - -type timerExt struct { - Tags []string `json:"tags,omitempty"` - Manual *manualRequestObj `json:"manual,omitempty"` - Event *eventExtObj `json:"event,omitempty"` -} - -// CreateTimerTableSQL returns a SQL to create timer table -func CreateTimerTableSQL(dbName, tableName string) string { - return fmt.Sprintf(`CREATE TABLE IF NOT EXISTS %s ( - ID BIGINT(64) UNSIGNED NOT NULL AUTO_INCREMENT, - NAMESPACE VARCHAR(256) NOT NULL, - TIMER_KEY VARCHAR(256) NOT NULL, - TIMER_DATA BLOB, - TIMEZONE VARCHAR(64) NOT NULL, - SCHED_POLICY_TYPE VARCHAR(32) NOT NULL, - SCHED_POLICY_EXPR VARCHAR(256) NOT NULL, - HOOK_CLASS VARCHAR(64) NOT NULL, - WATERMARK TIMESTAMP DEFAULT NULL, - ENABLE TINYINT(2) NOT NULL, - TIMER_EXT JSON NOT NULL, - EVENT_STATUS VARCHAR(32) NOT NULL, - EVENT_ID VARCHAR(64) NOT NULL, - EVENT_DATA BLOB, - EVENT_START TIMESTAMP DEFAULT NULL, - SUMMARY_DATA BLOB, - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - UPDATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - VERSION BIGINT(64) UNSIGNED NOT NULL, - PRIMARY KEY (ID), - UNIQUE KEY timer_key(NAMESPACE, TIMER_KEY), - KEY hook_class(HOOK_CLASS) - )`, indentString(dbName, tableName)) -} - -func indentString(dbName, tableName string) string { - return fmt.Sprintf("`%s`.`%s`", dbName, tableName) -} - -func buildInsertTimerSQL(dbName, tableName string, record *api.TimerRecord) (string, []any, error) { - var watermark, eventStart any - watermarkFormat, eventStartFormat := "%?", "%?" - if !record.Watermark.IsZero() { - watermark = record.Watermark.Unix() - watermarkFormat = "FROM_UNIXTIME(%?)" - } - - if !record.EventStart.IsZero() { - eventStart = record.EventStart.Unix() - eventStartFormat = "FROM_UNIXTIME(%?)" - } - - eventStatus := record.EventStatus - if eventStatus == "" { - eventStatus = api.SchedEventIdle - } - - ext := &timerExt{ - Tags: record.Tags, - Manual: newManualRequestObj(record.ManualRequest), - Event: newEventExtObj(record.EventExtra), - } - - extJSON, err := json.Marshal(ext) - if err != nil { - return "", nil, err - } - - sql := fmt.Sprintf("INSERT INTO %s ("+ - "NAMESPACE, "+ - "TIMER_KEY, "+ - "TIMER_DATA, "+ - "TIMEZONE, "+ - "SCHED_POLICY_TYPE, "+ - "SCHED_POLICY_EXPR, "+ - "HOOK_CLASS, "+ - "WATERMARK, "+ - "ENABLE, "+ - "TIMER_EXT, "+ - "EVENT_ID, "+ - "EVENT_STATUS, "+ - "EVENT_START, "+ - "EVENT_DATA, "+ - "SUMMARY_DATA, "+ - "VERSION) "+ - "VALUES (%%?, %%?, %%?, %%?, %%?, %%?, %%?, %s, %%?, JSON_MERGE_PATCH('{}', %%?), %%?, %%?, %s, %%?, %%?, 1)", - indentString(dbName, tableName), - watermarkFormat, - eventStartFormat, - ) - - return sql, []any{ - record.Namespace, - record.Key, - record.Data, - record.TimeZone, - string(record.SchedPolicyType), - record.SchedPolicyExpr, - record.HookClass, - watermark, - record.Enable, - json.RawMessage(extJSON), - record.EventID, - string(eventStatus), - eventStart, - record.EventData, - record.SummaryData, - }, nil -} - -func buildSelectTimerSQL(dbName, tableName string, cond api.Cond) (string, []any, error) { - criteria, args, err := buildCondCriteria(cond, make([]any, 0, 8)) - if err != nil { - return "", nil, err - } - - sql := fmt.Sprintf("SELECT "+ - "ID, "+ - "NAMESPACE, "+ - "TIMER_KEY, "+ - "TIMER_DATA, "+ - "TIMEZONE, "+ - "SCHED_POLICY_TYPE, "+ - "SCHED_POLICY_EXPR, "+ - "HOOK_CLASS, "+ - "WATERMARK, "+ - "ENABLE, "+ - "TIMER_EXT, "+ - "EVENT_STATUS, "+ - "EVENT_ID, "+ - "EVENT_DATA, "+ - "EVENT_START, "+ - "SUMMARY_DATA, "+ - "CREATE_TIME, "+ - "UPDATE_TIME, "+ - "VERSION "+ - "FROM %s WHERE %s", - indentString(dbName, tableName), - criteria, - ) - return sql, args, nil -} - -func buildCondCriteria(cond api.Cond, args []any) (criteria string, _ []any, err error) { - if cond == nil { - return "1", args, nil - } - - switch c := cond.(type) { - case *api.TimerCond: - criteria, args, err = buildTimerCondCriteria(c, args) - if err != nil { - return "", nil, err - } - return criteria, args, nil - case *api.Operator: - return buildOperatorCriteria(c, args) - default: - return "", nil, errors.Errorf("unsupported condition type: %T", cond) - } -} - -func buildTimerCondCriteria(cond *api.TimerCond, args []any) (string, []any, error) { - items := make([]string, 0, cap(args)-len(args)) - if val, ok := cond.ID.Get(); ok { - items = append(items, "ID = %?") - args = append(args, val) - } - - if val, ok := cond.Namespace.Get(); ok { - items = append(items, "NAMESPACE = %?") - args = append(args, val) - } - - if val, ok := cond.Key.Get(); ok { - if cond.KeyPrefix { - items = append(items, "TIMER_KEY LIKE %?") - args = append(args, val+"%") - } else { - items = append(items, "TIMER_KEY = %?") - args = append(args, val) - } - } - - if vals, ok := cond.Tags.Get(); ok && len(vals) > 0 { - bs, err := json.Marshal(vals) - if err != nil { - return "", nil, err - } - items = append(items, - "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL", - "JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", - ) - args = append(args, json.RawMessage(bs)) - } - - if len(items) == 0 { - return "1", args, nil - } - - return strings.Join(items, " AND "), args, nil -} - -func buildOperatorCriteria(op *api.Operator, args []any) (string, []any, error) { - if len(op.Children) == 0 { - return "", nil, errors.New("children should not be empty") - } - - var opStr string - switch op.Op { - case api.OperatorAnd: - opStr = "AND" - case api.OperatorOr: - opStr = "OR" - default: - return "", nil, errors.Errorf("unsupported operator: %v", op.Op) - } - - criteriaList := make([]string, 0, len(op.Children)) - for _, child := range op.Children { - var criteria string - var err error - criteria, args, err = buildCondCriteria(child, args) - if err != nil { - return "", nil, err - } - - if len(op.Children) > 1 && criteria != "1" && criteria != "0" { - criteria = fmt.Sprintf("(%s)", criteria) - } - - criteriaList = append(criteriaList, criteria) - } - - criteria := strings.Join(criteriaList, " "+opStr+" ") - if op.Not { - switch criteria { - case "0": - criteria = "1" - case "1": - criteria = "0" - default: - criteria = fmt.Sprintf("!(%s)", criteria) - } - } - return criteria, args, nil -} - -func buildUpdateTimerSQL(dbName, tblName string, timerID string, update *api.TimerUpdate) (string, []any, error) { - criteria, args, err := buildUpdateCriteria(update, make([]any, 0, 6)) - if err != nil { - return "", nil, err - } - - sql := fmt.Sprintf("UPDATE %s SET %s WHERE ID = %%?", indentString(dbName, tblName), criteria) - return sql, append(args, timerID), nil -} - -type manualRequestObj struct { - RequestID *string `json:"request_id"` - RequestTimeUnix *int64 `json:"request_time_unix"` - TimeoutSec *int64 `json:"timeout_sec"` - Processed *bool `json:"processed"` - EventID *string `json:"event_id"` -} - -func newManualRequestObj(manual api.ManualRequest) *manualRequestObj { - var empty api.ManualRequest - if manual == empty { - return nil - } - - obj := &manualRequestObj{} - if v := manual.ManualRequestID; v != "" { - obj.RequestID = &v - } - - if v := manual.ManualRequestTime; !v.IsZero() { - unix := v.Unix() - obj.RequestTimeUnix = &unix - } - - if v := manual.ManualTimeout; v != 0 { - sec := int64(v / time.Second) - obj.TimeoutSec = &sec - } - - if v := manual.ManualProcessed; v { - processed := true - obj.Processed = &processed - } - - if v := manual.ManualEventID; v != "" { - obj.EventID = &v - } - - return obj -} - -func (o *manualRequestObj) ToManualRequest() (r api.ManualRequest) { - if o == nil { - return - } - - if v := o.RequestID; v != nil { - r.ManualRequestID = *v - } - - if v := o.RequestTimeUnix; v != nil { - r.ManualRequestTime = time.Unix(*v, 0) - } - - if v := o.TimeoutSec; v != nil { - r.ManualTimeout = time.Duration(*v) * time.Second - } - - if v := o.Processed; v != nil { - r.ManualProcessed = *v - } - - if v := o.EventID; v != nil { - r.ManualEventID = *v - } - - return r -} - -type eventExtObj struct { - ManualRequestID *string `json:"manual_request_id"` - WatermarkUnix *int64 `json:"watermark_unix"` -} - -func newEventExtObj(e api.EventExtra) *eventExtObj { - var empty api.EventExtra - if e == empty { - return nil - } - - obj := &eventExtObj{} - if v := e.EventManualRequestID; v != "" { - obj.ManualRequestID = &v - } - - if v := e.EventWatermark; !v.IsZero() { - unix := v.Unix() - obj.WatermarkUnix = &unix - } - - return obj -} - -func (o *eventExtObj) ToEventExtra() (e api.EventExtra) { - if o == nil { - return - } - - if v := o.ManualRequestID; v != nil { - e.EventManualRequestID = *v - } - - if v := o.WatermarkUnix; v != nil { - e.EventWatermark = time.Unix(*o.WatermarkUnix, 0) - } - - return -} - -func buildUpdateCriteria(update *api.TimerUpdate, args []any) (string, []any, error) { - updateFields := make([]string, 0, cap(args)-len(args)) - if val, ok := update.Enable.Get(); ok { - updateFields = append(updateFields, "ENABLE = %?") - args = append(args, val) - } - - extFields := make(map[string]any) - if val, ok := update.Tags.Get(); ok { - if len(val) == 0 { - val = nil - } - extFields["tags"] = val - } - - if val, ok := update.ManualRequest.Get(); ok { - extFields["manual"] = newManualRequestObj(val) - } - - if val, ok := update.EventExtra.Get(); ok { - extFields["event"] = newEventExtObj(val) - } - - if val, ok := update.TimeZone.Get(); ok { - updateFields = append(updateFields, "TIMEZONE = %?") - args = append(args, val) - } - - if val, ok := update.SchedPolicyType.Get(); ok { - updateFields = append(updateFields, "SCHED_POLICY_TYPE = %?") - args = append(args, string(val)) - } - - if val, ok := update.SchedPolicyExpr.Get(); ok { - updateFields = append(updateFields, "SCHED_POLICY_EXPR = %?") - args = append(args, val) - } - - if val, ok := update.EventStatus.Get(); ok { - updateFields = append(updateFields, "EVENT_STATUS = %?") - args = append(args, string(val)) - } - - if val, ok := update.EventID.Get(); ok { - updateFields = append(updateFields, "EVENT_ID = %?") - args = append(args, val) - } - - if val, ok := update.EventData.Get(); ok { - updateFields = append(updateFields, "EVENT_DATA = %?") - args = append(args, val) - } - - if val, ok := update.EventStart.Get(); ok { - if val.IsZero() { - updateFields = append(updateFields, "EVENT_START = NULL") - } else { - updateFields = append(updateFields, "EVENT_START = FROM_UNIXTIME(%?)") - args = append(args, val.Unix()) - } - } - - if val, ok := update.Watermark.Get(); ok { - if val.IsZero() { - updateFields = append(updateFields, "WATERMARK = NULL") - } else { - updateFields = append(updateFields, "WATERMARK = FROM_UNIXTIME(%?)") - args = append(args, val.Unix()) - } - } - - if val, ok := update.SummaryData.Get(); ok { - updateFields = append(updateFields, "SUMMARY_DATA = %?") - args = append(args, val) - } - - if len(extFields) > 0 { - jsonBytes, err := json.Marshal(extFields) - if err != nil { - return "", nil, err - } - updateFields = append(updateFields, "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?)") - args = append(args, json.RawMessage(jsonBytes)) - } - - updateFields = append(updateFields, "VERSION = VERSION + 1") - return strings.Join(updateFields, ", "), args, nil -} - -func buildDeleteTimerSQL(dbName, tblName string, timerID string) (string, []any) { - return fmt.Sprintf("DELETE FROM %s WHERE ID = %%?", indentString(dbName, tblName)), []any{timerID} -} diff --git a/timer/tablestore/sql_test.go b/timer/tablestore/sql_test.go deleted file mode 100644 index 56ea54ce6d48f..0000000000000 --- a/timer/tablestore/sql_test.go +++ /dev/null @@ -1,715 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tablestore - -import ( - "context" - "encoding/json" - "strings" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -func TestBuildInsertTimerSQL(t *testing.T) { - now := time.Now() - sql1 := "INSERT INTO `db1`.`t1` (NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + - "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_ID, EVENT_STATUS, EVENT_START, EVENT_DATA, SUMMARY_DATA, VERSION) " + - "VALUES (%?, %?, %?, %?, %?, %?, %?, FROM_UNIXTIME(%?), %?, JSON_MERGE_PATCH('{}', %?), %?, %?, FROM_UNIXTIME(%?), %?, %?, 1)" - sql2 := "INSERT INTO `db1`.`t1` (NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + - "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_ID, EVENT_STATUS, EVENT_START, EVENT_DATA, SUMMARY_DATA, VERSION) " + - "VALUES (%?, %?, %?, %?, %?, %?, %?, %?, %?, JSON_MERGE_PATCH('{}', %?), %?, %?, %?, %?, %?, 1)" - - cases := []struct { - sql string - record *api.TimerRecord - args []any - }{ - { - sql: sql1, - record: &api.TimerRecord{ - TimerSpec: api.TimerSpec{ - Namespace: "n1", - Key: "k1", - Data: []byte("data1"), - TimeZone: "Asia/Shanghai", - SchedPolicyType: api.SchedEventInterval, - SchedPolicyExpr: "1h", - HookClass: "h1", - Watermark: now, - Enable: true, - Tags: []string{"l1", "l2"}, - }, - ManualRequest: api.ManualRequest{ - ManualRequestID: "req1", - ManualRequestTime: time.Unix(123, 0), - ManualTimeout: time.Minute, - ManualProcessed: true, - ManualEventID: "event1", - }, - EventExtra: api.EventExtra{ - EventManualRequestID: "req1", - EventWatermark: time.Unix(456, 0), - }, - EventID: "e1", - EventStatus: api.SchedEventTrigger, - EventStart: now.Add(time.Second), - EventData: []byte("event1"), - SummaryData: []byte("summary1"), - }, - args: []any{ - "n1", "k1", []byte("data1"), "Asia/Shanghai", "INTERVAL", "1h", "h1", now.Unix(), - true, json.RawMessage(`{"tags":["l1","l2"],` + - `"manual":{"request_id":"req1","request_time_unix":123,"timeout_sec":60,"processed":true,"event_id":"event1"},` + - `"event":{"manual_request_id":"req1","watermark_unix":456}}`), - "e1", "TRIGGER", now.Unix() + 1, []byte("event1"), []byte("summary1"), - }, - }, - { - sql: sql2, - record: &api.TimerRecord{ - TimerSpec: api.TimerSpec{ - Namespace: "n1", - Key: "k1", - SchedPolicyType: api.SchedEventInterval, - SchedPolicyExpr: "1h", - }, - }, - args: []any{ - "n1", "k1", []byte(nil), "", "INTERVAL", "1h", "", nil, - false, json.RawMessage("{}"), "", "IDLE", nil, []byte(nil), []byte(nil), - }, - }, - } - - for _, c := range cases { - require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) - sql, args, err := buildInsertTimerSQL("db1", "t1", c.record) - require.NoError(t, err) - require.Equal(t, c.sql, sql) - require.Equal(t, c.args, args) - } -} - -func TestBuildCondCriteria(t *testing.T) { - cases := []struct { - cond api.Cond - criteria string - args []any - }{ - { - cond: nil, - criteria: "1", - args: []any{}, - }, - { - cond: &api.TimerCond{}, - criteria: "1", - args: []any{}, - }, - { - cond: &api.TimerCond{ - ID: api.NewOptionalVal("1"), - }, - criteria: "ID = %?", - args: []any{"1"}, - }, - { - cond: &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - }, - criteria: "NAMESPACE = %?", - args: []any{"ns1"}, - }, - { - cond: &api.TimerCond{ - Key: api.NewOptionalVal("key1"), - }, - criteria: "TIMER_KEY = %?", - args: []any{"key1"}, - }, - { - cond: &api.TimerCond{ - Key: api.NewOptionalVal("key1"), - KeyPrefix: true, - }, - criteria: "TIMER_KEY LIKE %?", - args: []any{"key1%"}, - }, - { - cond: &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }, - criteria: "NAMESPACE = %? AND TIMER_KEY = %?", - args: []any{"ns1", "key1"}, - }, - { - cond: &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - KeyPrefix: true, - }, - criteria: "NAMESPACE = %? AND TIMER_KEY LIKE %?", - args: []any{"ns1", "key1%"}, - }, - { - cond: &api.TimerCond{ - Tags: api.NewOptionalVal([]string{}), - }, - criteria: "1", - args: []any{}, - }, - { - cond: &api.TimerCond{ - Tags: api.NewOptionalVal([]string{"l1"}), - }, - criteria: "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL AND JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", - args: []any{json.RawMessage(`["l1"]`)}, - }, - { - cond: &api.TimerCond{ - Tags: api.NewOptionalVal([]string{"l1", "l2"}), - }, - criteria: "JSON_EXTRACT(TIMER_EXT, '$.tags') IS NOT NULL AND JSON_CONTAINS((TIMER_EXT->'$.tags'), %?)", - args: []any{json.RawMessage(`["l1","l2"]`)}, - }, - { - cond: api.And( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "(NAMESPACE = %? AND TIMER_KEY = %?) AND (ID = %?)", - args: []any{"ns1", "key1", "2"}, - }, - { - cond: api.And( - &api.TimerCond{}, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "1 AND (ID = %?)", - args: []any{"2"}, - }, - { - cond: api.And( - api.Not(&api.TimerCond{}), - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "0 AND (ID = %?)", - args: []any{"2"}, - }, - { - cond: api.And( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - }, - &api.TimerCond{}, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "(NAMESPACE = %?) AND 1 AND (ID = %?)", - args: []any{"ns1", "2"}, - }, - { - cond: api.Not(api.And( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - )), - criteria: "!((NAMESPACE = %? AND TIMER_KEY = %?) AND (ID = %?))", - args: []any{"ns1", "key1", "2"}, - }, - { - cond: api.Or( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "(NAMESPACE = %? AND TIMER_KEY = %?) OR (ID = %?)", - args: []any{"ns1", "key1", "2"}, - }, - { - cond: api.Not(api.Or( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - )), - criteria: "!((NAMESPACE = %? AND TIMER_KEY = %?) OR (ID = %?))", - args: []any{"ns1", "key1", "2"}, - }, - { - cond: api.Or( - &api.TimerCond{}, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "1 OR (ID = %?)", - args: []any{"2"}, - }, - { - cond: api.Or( - &api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - }, - &api.TimerCond{}, - &api.TimerCond{ - ID: api.NewOptionalVal("2"), - }, - ), - criteria: "(NAMESPACE = %?) OR 1 OR (ID = %?)", - args: []any{"ns1", "2"}, - }, - { - cond: api.Not(&api.TimerCond{ - ID: api.NewOptionalVal("3"), - }), - criteria: "!(ID = %?)", - args: []any{"3"}, - }, - { - cond: api.Not(&api.TimerCond{}), - criteria: "0", - args: []any{}, - }, - { - cond: api.Not(api.Not(&api.TimerCond{})), - criteria: "1", - args: []any{}, - }, - { - cond: api.Not(&api.TimerCond{ - Namespace: api.NewOptionalVal("ns1"), - Key: api.NewOptionalVal("key1"), - }), - criteria: "!(NAMESPACE = %? AND TIMER_KEY = %?)", - args: []any{"ns1", "key1"}, - }, - } - - for _, c := range cases { - require.Equal(t, strings.Count(c.criteria, "%?"), len(c.args)) - args := make([]any, 0) - criteria, args, err := buildCondCriteria(c.cond, args) - require.NoError(t, err) - require.Equal(t, c.criteria, criteria) - require.Equal(t, c.args, args) - - args = []any{"a", "b"} - criteria, args, err = buildCondCriteria(c.cond, args) - require.NoError(t, err) - require.Equal(t, c.criteria, criteria) - require.Equal(t, append([]any{"a", "b"}, c.args...), args) - } -} - -func TestBuildSelectTimerSQL(t *testing.T) { - prefix := "SELECT " + - "ID, NAMESPACE, TIMER_KEY, TIMER_DATA, TIMEZONE, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR, " + - "HOOK_CLASS, WATERMARK, ENABLE, TIMER_EXT, EVENT_STATUS, EVENT_ID, EVENT_DATA, EVENT_START, SUMMARY_DATA, " + - "CREATE_TIME, UPDATE_TIME, VERSION FROM `db1`.`t1`" - - cases := []struct { - cond api.Cond - sql string - args []any - }{ - { - cond: nil, - sql: prefix + " WHERE 1", - args: []any{}, - }, - { - cond: &api.TimerCond{ID: api.NewOptionalVal("2")}, - sql: prefix + " WHERE ID = %?", - args: []any{"2"}, - }, - { - cond: &api.TimerCond{Namespace: api.NewOptionalVal("ns1"), Key: api.NewOptionalVal("key1")}, - sql: prefix + " WHERE NAMESPACE = %? AND TIMER_KEY = %?", - args: []any{"ns1", "key1"}, - }, - { - cond: api.Or( - &api.TimerCond{ID: api.NewOptionalVal("3")}, - &api.TimerCond{Namespace: api.NewOptionalVal("ns1")}, - ), - sql: prefix + " WHERE (ID = %?) OR (NAMESPACE = %?)", - args: []any{"3", "ns1"}, - }, - } - - for _, c := range cases { - require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) - sql, args, err := buildSelectTimerSQL("db1", "t1", c.cond) - require.NoError(t, err) - require.Equal(t, c.sql, sql) - require.Equal(t, c.args, args) - } -} - -func TestBuildUpdateCriteria(t *testing.T) { - now := time.Now() - var zeroTime time.Time - cases := []struct { - update *api.TimerUpdate - criteria string - args []any - }{ - { - update: &api.TimerUpdate{}, - criteria: "VERSION = VERSION + 1", - args: []any{}, - }, - { - update: &api.TimerUpdate{ - Enable: api.NewOptionalVal(true), - }, - criteria: "ENABLE = %?, VERSION = VERSION + 1", - args: []any{true}, - }, - { - update: &api.TimerUpdate{ - Enable: api.NewOptionalVal(false), - Tags: api.NewOptionalVal([]string{"l1", "l2"}), - TimeZone: api.NewOptionalVal("Asia/Shanghai"), - SchedPolicyType: api.NewOptionalVal(api.SchedEventInterval), - SchedPolicyExpr: api.NewOptionalVal("1h"), - ManualRequest: api.NewOptionalVal(api.ManualRequest{ - ManualRequestID: "req1", - ManualRequestTime: time.Unix(123, 0), - ManualTimeout: time.Minute, - ManualProcessed: true, - ManualEventID: "event1", - }), - EventStatus: api.NewOptionalVal(api.SchedEventTrigger), - EventID: api.NewOptionalVal("event1"), - EventData: api.NewOptionalVal([]byte("data1")), - EventStart: api.NewOptionalVal(now), - EventExtra: api.NewOptionalVal(api.EventExtra{ - EventManualRequestID: "req2", - EventWatermark: time.Unix(456, 0), - }), - Watermark: api.NewOptionalVal(now.Add(time.Second)), - SummaryData: api.NewOptionalVal([]byte("summary")), - CheckEventID: api.NewOptionalVal("ee"), - CheckVersion: api.NewOptionalVal(uint64(1)), - }, - criteria: "ENABLE = %?, TIMEZONE = %?, SCHED_POLICY_TYPE = %?, SCHED_POLICY_EXPR = %?, EVENT_STATUS = %?, " + - "EVENT_ID = %?, EVENT_DATA = %?, EVENT_START = FROM_UNIXTIME(%?), " + - "WATERMARK = FROM_UNIXTIME(%?), SUMMARY_DATA = %?, " + - "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), " + - "VERSION = VERSION + 1", - args: []any{ - false, "Asia/Shanghai", "INTERVAL", "1h", "TRIGGER", "event1", []byte("data1"), now.Unix(), - now.Unix() + 1, []byte("summary"), - json.RawMessage(`{` + - `"event":{"manual_request_id":"req2","watermark_unix":456},` + - `"manual":{"request_id":"req1","request_time_unix":123,"timeout_sec":60,"processed":true,"event_id":"event1"},` + - `"tags":["l1","l2"]` + - `}`), - }, - }, - { - update: &api.TimerUpdate{ - EventExtra: api.NewOptionalVal(api.EventExtra{EventManualRequestID: "req1"}), - ManualRequest: api.NewOptionalVal(api.ManualRequest{ManualRequestID: "req2"}), - }, - criteria: "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), VERSION = VERSION + 1", - args: []any{json.RawMessage(`{` + - `"event":{"manual_request_id":"req1","watermark_unix":null},` + - `"manual":{"request_id":"req2","request_time_unix":null,"timeout_sec":null,"processed":null,"event_id":null}` + - `}`)}, - }, - { - update: &api.TimerUpdate{ - EventExtra: api.NewOptionalVal(api.EventExtra{EventWatermark: time.Unix(123, 0)}), - ManualRequest: api.NewOptionalVal(api.ManualRequest{ManualRequestTime: time.Unix(456, 0)}), - }, - criteria: "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), VERSION = VERSION + 1", - args: []any{json.RawMessage(`{` + - `"event":{"manual_request_id":null,"watermark_unix":123},` + - `"manual":{"request_id":null,"request_time_unix":456,"timeout_sec":null,"processed":null,"event_id":null}` + - `}`)}, - }, - { - update: &api.TimerUpdate{ - TimeZone: api.NewOptionalVal(""), - SchedPolicyExpr: api.NewOptionalVal(""), - EventID: api.NewOptionalVal(""), - EventData: api.NewOptionalVal([]byte(nil)), - EventStart: api.NewOptionalVal(zeroTime), - EventExtra: api.NewOptionalVal(api.EventExtra{}), - ManualRequest: api.NewOptionalVal(api.ManualRequest{}), - Watermark: api.NewOptionalVal(zeroTime), - SummaryData: api.NewOptionalVal([]byte(nil)), - Tags: api.NewOptionalVal([]string(nil)), - }, - criteria: "TIMEZONE = %?, SCHED_POLICY_EXPR = %?, EVENT_ID = %?, EVENT_DATA = %?, " + - "EVENT_START = NULL, WATERMARK = NULL, SUMMARY_DATA = %?, " + - "TIMER_EXT = JSON_MERGE_PATCH(TIMER_EXT, %?), " + - "VERSION = VERSION + 1", - args: []any{"", "", "", []byte(nil), []byte(nil), json.RawMessage(`{"event":null,"manual":null,"tags":null}`)}, - }, - { - update: &api.TimerUpdate{ - CheckEventID: api.NewOptionalVal("ee"), - CheckVersion: api.NewOptionalVal(uint64(1)), - }, - criteria: "VERSION = VERSION + 1", - args: []any{}, - }, - } - - for _, c := range cases { - require.Equal(t, strings.Count(c.criteria, "%?"), len(c.args)) - criteria, args, err := buildUpdateCriteria(c.update, []any{}) - require.NoError(t, err) - require.Equal(t, c.criteria, criteria) - require.Equal(t, c.args, args) - - criteria, args, err = buildUpdateCriteria(c.update, []any{1, "2", "3"}) - require.NoError(t, err) - require.Equal(t, c.criteria, criteria) - require.Equal(t, append([]any{1, "2", "3"}, c.args...), args) - } -} - -func TestBuildUpdateTimerSQL(t *testing.T) { - timerID := "123" - cases := []struct { - update *api.TimerUpdate - sql string - args []any - }{ - { - update: &api.TimerUpdate{}, - sql: "UPDATE `db1`.`tbl1` SET VERSION = VERSION + 1 WHERE ID = %?", - args: []any{timerID}, - }, - { - update: &api.TimerUpdate{ - SchedPolicyType: api.NewOptionalVal(api.SchedEventInterval), - SchedPolicyExpr: api.NewOptionalVal("1h"), - }, - sql: "UPDATE `db1`.`tbl1` SET SCHED_POLICY_TYPE = %?, SCHED_POLICY_EXPR = %?, VERSION = VERSION + 1 WHERE ID = %?", - args: []any{"INTERVAL", "1h", timerID}, - }, - } - - for _, c := range cases { - require.Equal(t, strings.Count(c.sql, "%?"), len(c.args)) - sql, args, err := buildUpdateTimerSQL("db1", "tbl1", timerID, c.update) - require.NoError(t, err) - require.Equal(t, c.sql, sql) - require.Equal(t, c.args, args) - } -} - -func TestBuildDeleteTimerSQL(t *testing.T) { - sql, args := buildDeleteTimerSQL("db1", "tbl1", "123") - require.Equal(t, "DELETE FROM `db1`.`tbl1` WHERE ID = %?", sql) - require.Equal(t, []any{"123"}, args) -} - -type mockSessionPool struct { - mock.Mock -} - -func (p *mockSessionPool) Get() (resource pools.Resource, _ error) { - ret := p.Called() - if r := ret.Get(0); r != nil { - resource = r.(pools.Resource) - } - return resource, ret.Error(1) -} - -func (p *mockSessionPool) Put(r pools.Resource) { - p.Called(r) -} - -type mockSession struct { - mock.Mock - sessionctx.Context - sqlexec.SQLExecutor -} - -func (p *mockSession) ExecuteInternal(ctx context.Context, sql string, args ...interface{}) (rs sqlexec.RecordSet, _ error) { - ret := p.Called(ctx, sql, args) - if r := ret.Get(0); r != nil { - rs = r.(sqlexec.RecordSet) - } - return rs, ret.Error(1) -} - -func (p *mockSession) GetSessionVars() *variable.SessionVars { - return p.Context.GetSessionVars() -} - -func (p *mockSession) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { - p.Context.SetDiskFullOpt(level) -} - -func (p *mockSession) Close() { - p.Called() -} - -var matchCtx = mock.MatchedBy(func(ctx context.Context) bool { - return kv.GetInternalSourceType(ctx) == kv.InternalTimer -}) - -func TestTakeSession(t *testing.T) { - pool := &mockSessionPool{} - core := tableTimerStoreCore{pool: pool} - - // Get returns error - pool.On("Get").Return(nil, errors.New("mockErr")).Once() - r, back, err := core.takeSession() - require.Nil(t, r) - require.Nil(t, back) - require.EqualError(t, err, "mockErr") - pool.AssertExpectations(t) - - // Get returns a session - se := &mockSession{} - pool.On("Get").Return(se, nil).Once() - r, back, err = core.takeSession() - require.Equal(t, r, se) - require.NotNil(t, back) - require.Nil(t, err) - pool.AssertExpectations(t) - se.AssertExpectations(t) - - // Put session failed - se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). - Return(nil, errors.New("mockErr")). - Once() - se.On("Close").Once() - back() - pool.AssertExpectations(t) - se.AssertExpectations(t) - - // Put session success - pool.On("Get").Return(se, nil).Once() - r, back, err = core.takeSession() - require.Equal(t, r, se) - require.NotNil(t, back) - require.Nil(t, err) - se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). - Return(nil, nil). - Once() - pool.On("Put", se).Once() - back() - pool.AssertExpectations(t) - se.AssertExpectations(t) -} - -func TestRunInTxn(t *testing.T) { - se := &mockSession{} - - // success - se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). - Return(nil, nil). - Once() - se.On("ExecuteInternal", matchCtx, mock.MatchedBy(func(sql string) bool { - return strings.HasPrefix(sql, "insert") - }), mock.Anything). - Return(nil, nil). - Once() - se.On("ExecuteInternal", matchCtx, "COMMIT", []interface{}(nil)). - Return(nil, nil). - Once() - require.Nil(t, runInTxn(context.Background(), se, func() error { - _, err := executeSQL(context.Background(), se, "insert into t value(?)", 1) - return err - })) - se.AssertExpectations(t) - - // start txn failed - se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). - Return(nil, errors.New("mockBeginErr")). - Once() - err := runInTxn(context.Background(), se, func() error { return nil }) - require.EqualError(t, err, "mockBeginErr") - se.AssertExpectations(t) - - // exec failed, rollback success - se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). - Return(nil, nil). - Once() - se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). - Return(nil, nil). - Once() - err = runInTxn(context.Background(), se, func() error { return errors.New("mockFuncErr") }) - require.EqualError(t, err, "mockFuncErr") - se.AssertExpectations(t) - - // commit failed - se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). - Return(nil, nil). - Once() - se.On("ExecuteInternal", matchCtx, "COMMIT", []interface{}(nil)). - Return(nil, errors.New("commitErr")). - Once() - se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). - Return(nil, nil). - Once() - err = runInTxn(context.Background(), se, func() error { return nil }) - require.EqualError(t, err, "commitErr") - se.AssertExpectations(t) - - // rollback failed - se.On("ExecuteInternal", matchCtx, "BEGIN PESSIMISTIC", []interface{}(nil)). - Return(nil, nil). - Once() - se.On("ExecuteInternal", matchCtx, "ROLLBACK", []interface{}(nil)). - Return(nil, errors.New("rollbackErr")). - Once() - err = runInTxn(context.Background(), se, func() error { return errors.New("mockFuncErr") }) - require.EqualError(t, err, "mockFuncErr") - se.AssertExpectations(t) -} diff --git a/timer/tablestore/store.go b/timer/tablestore/store.go deleted file mode 100644 index 170b0b8ad4393..0000000000000 --- a/timer/tablestore/store.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tablestore - -import ( - "context" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/timer/api" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/timeutil" - "github.com/tikv/client-go/v2/util" - clientv3 "go.etcd.io/etcd/client/v3" -) - -type sessionPool interface { - Get() (pools.Resource, error) - Put(pools.Resource) -} - -type tableTimerStoreCore struct { - pool sessionPool - dbName string - tblName string - etcd *clientv3.Client - notifier api.TimerWatchEventNotifier -} - -// NewTableTimerStore create a new timer store based on table -func NewTableTimerStore(clusterID uint64, pool sessionPool, dbName, tblName string, etcd *clientv3.Client) *api.TimerStore { - var notifier api.TimerWatchEventNotifier - if etcd != nil { - notifier = NewEtcdNotifier(clusterID, etcd) - } else { - notifier = api.NewMemTimerWatchEventNotifier() - } - - return &api.TimerStore{ - TimerStoreCore: &tableTimerStoreCore{ - pool: pool, - dbName: dbName, - tblName: tblName, - notifier: notifier, - }, - } -} - -func (s *tableTimerStoreCore) Create(ctx context.Context, record *api.TimerRecord) (string, error) { - if record == nil { - return "", errors.New("timer should not be nil") - } - - if record.ID != "" { - return "", errors.New("ID should not be specified when create record") - } - - if record.Version != 0 { - return "", errors.New("Version should not be specified when create record") - } - - if !record.CreateTime.IsZero() { - return "", errors.New("CreateTime should not be specified when create record") - } - - if err := record.Validate(); err != nil { - return "", err - } - - sctx, back, err := s.takeSession() - if err != nil { - return "", err - } - defer back() - - sql, args, err := buildInsertTimerSQL(s.dbName, s.tblName, record) - if err != nil { - return "", err - } - - _, err = executeSQL(ctx, sctx, sql, args...) - if err != nil { - return "", err - } - - rows, err := executeSQL(ctx, sctx, "select @@last_insert_id") - if err != nil { - return "", err - } - - timerID := strconv.FormatUint(rows[0].GetUint64(0), 10) - s.notifier.Notify(api.WatchTimerEventCreate, timerID) - return timerID, nil -} - -func (s *tableTimerStoreCore) List(ctx context.Context, cond api.Cond) ([]*api.TimerRecord, error) { - sctx, back, err := s.takeSession() - if err != nil { - return nil, err - } - defer back() - - if sessVars := sctx.GetSessionVars(); sessVars.GetEnableIndexMerge() { - // Enable index merge is used to make sure filtering timers with tags quickly. - // Currently, we are using multi-value index to index tags for timers which requires index merge enabled. - // see: https://docs.pingcap.com/tidb/dev/choose-index#use-a-multi-valued-index - sessVars.SetEnableIndexMerge(true) - defer sessVars.SetEnableIndexMerge(false) - } - - seTZ := sctx.GetSessionVars().Location() - sql, args, err := buildSelectTimerSQL(s.dbName, s.tblName, cond) - if err != nil { - return nil, err - } - - rows, err := executeSQL(ctx, sctx, sql, args...) - if err != nil { - return nil, err - } - - timers := make([]*api.TimerRecord, 0, len(rows)) - for _, row := range rows { - var timerData []byte - if !row.IsNull(3) { - timerData = row.GetBytes(3) - } - - var watermark time.Time - if !row.IsNull(8) { - watermark, err = row.GetTime(8).GoTime(seTZ) - if err != nil { - return nil, err - } - } - - var ext timerExt - if !row.IsNull(10) { - extJSON := row.GetJSON(10).String() - if err = json.Unmarshal([]byte(extJSON), &ext); err != nil { - return nil, err - } - } - - var eventData []byte - if !row.IsNull(13) { - eventData = row.GetBytes(13) - } - - var eventStart time.Time - if !row.IsNull(14) { - eventStart, err = row.GetTime(14).GoTime(seTZ) - if err != nil { - return nil, err - } - } - - var summaryData []byte - if !row.IsNull(15) { - summaryData = row.GetBytes(15) - } - - var createTime time.Time - if !row.IsNull(16) { - createTime, err = row.GetTime(16).GoTime(seTZ) - if err != nil { - return nil, err - } - } - - timer := &api.TimerRecord{ - ID: strconv.FormatUint(row.GetUint64(0), 10), - TimerSpec: api.TimerSpec{ - Namespace: row.GetString(1), - Key: row.GetString(2), - Tags: ext.Tags, - Data: timerData, - TimeZone: row.GetString(4), - SchedPolicyType: api.SchedPolicyType(row.GetString(5)), - SchedPolicyExpr: row.GetString(6), - HookClass: row.GetString(7), - Watermark: watermark, - Enable: row.GetInt64(9) != 0, - }, - ManualRequest: ext.Manual.ToManualRequest(), - EventStatus: api.SchedEventStatus(row.GetString(11)), - EventID: row.GetString(12), - EventData: eventData, - EventStart: eventStart, - EventExtra: ext.Event.ToEventExtra(), - SummaryData: summaryData, - CreateTime: createTime, - Version: row.GetUint64(18), - } - - tz := timer.TimeZone - // handling value "TIDB" is for compatibility of version 7.3.0 - if tz == "" || strings.EqualFold(tz, "TIDB") { - if tz, err = sctx.GetSessionVars().GetGlobalSystemVar(ctx, variable.TimeZone); err != nil { - return nil, err - } - } - - loc, err := timeutil.ParseTimeZone(tz) - if err == nil { - timer.Location = loc - } else { - timer.Location = timeutil.SystemLocation() - } - - timers = append(timers, timer) - } - return timers, nil -} - -func (s *tableTimerStoreCore) Update(ctx context.Context, timerID string, update *api.TimerUpdate) error { - sctx, back, err := s.takeSession() - if err != nil { - return err - } - defer back() - - err = runInTxn(ctx, sctx, func() error { - /* #nosec G202: SQL string concatenation */ - getCheckColsSQL := fmt.Sprintf( - "SELECT EVENT_ID, VERSION, SCHED_POLICY_TYPE, SCHED_POLICY_EXPR FROM %s WHERE ID=%%?", - indentString(s.dbName, s.tblName), - ) - - rows, err := executeSQL(ctx, sctx, getCheckColsSQL, timerID) - if err != nil { - return err - } - - if len(rows) == 0 { - return api.ErrTimerNotExist - } - - err = checkUpdateConstraints( - update, - rows[0].GetString(0), - rows[0].GetUint64(1), - api.SchedPolicyType(rows[0].GetString(2)), - rows[0].GetString(3), - ) - - if err != nil { - return err - } - - updateSQL, args, err := buildUpdateTimerSQL(s.dbName, s.tblName, timerID, update) - if err != nil { - return err - } - - if _, err = executeSQL(ctx, sctx, updateSQL, args...); err != nil { - return err - } - - return nil - }) - - if err != nil { - return err - } - - s.notifier.Notify(api.WatchTimerEventUpdate, timerID) - return nil -} - -func (s *tableTimerStoreCore) Delete(ctx context.Context, timerID string) (bool, error) { - sctx, back, err := s.takeSession() - if err != nil { - return false, err - } - defer back() - - deleteSQL, args := buildDeleteTimerSQL(s.dbName, s.tblName, timerID) - _, err = executeSQL(ctx, sctx, deleteSQL, args...) - if err != nil { - return false, err - } - - rows, err := executeSQL(ctx, sctx, "SELECT ROW_COUNT()") - if err != nil { - return false, err - } - - exist := rows[0].GetInt64(0) > 0 - if exist { - s.notifier.Notify(api.WatchTimerEventDelete, timerID) - } - return exist, nil -} - -func (*tableTimerStoreCore) WatchSupported() bool { - return true -} - -func (s *tableTimerStoreCore) Watch(ctx context.Context) api.WatchTimerChan { - return s.notifier.Watch(ctx) -} - -func (s *tableTimerStoreCore) Close() { - s.notifier.Close() -} - -func (s *tableTimerStoreCore) takeSession() (sessionctx.Context, func(), error) { - r, err := s.pool.Get() - if err != nil { - return nil, nil, err - } - - sctx, ok := r.(sessionctx.Context) - if !ok { - s.pool.Put(r) - return nil, nil, errors.New("session is not the type sessionctx.Context") - } - - back := func() { - if _, err = executeSQL(context.Background(), sctx, "ROLLBACK"); err != nil { - // Though this branch is rarely to be called because "ROLLBACK" will always be successfully, we still need - // to handle it here to make sure the code is strong. - terror.Log(err) - // call `r.Close()` to make sure the resource is released to avoid memory leak - r.Close() - return - } - s.pool.Put(r) - } - - return sctx, back, nil -} - -func checkUpdateConstraints(update *api.TimerUpdate, eventID string, version uint64, policy api.SchedPolicyType, expr string) error { - if val, ok := update.CheckEventID.Get(); ok && eventID != val { - return api.ErrEventIDNotMatch - } - - if val, ok := update.CheckVersion.Get(); ok && version != val { - return api.ErrVersionNotMatch - } - - if val, ok := update.TimeZone.Get(); ok { - if err := api.ValidateTimeZone(val); err != nil { - return err - } - } - - checkPolicy := false - if val, ok := update.SchedPolicyType.Get(); ok { - checkPolicy = true - policy = val - } - - if val, ok := update.SchedPolicyExpr.Get(); ok { - checkPolicy = true - expr = val - } - - if checkPolicy { - if _, err := api.CreateSchedEventPolicy(policy, expr); err != nil { - return errors.Wrap(err, "schedule event configuration is not valid") - } - } - - return nil -} - -func executeSQL(ctx context.Context, sctx sessionctx.Context, sql string, args ...any) ([]chunk.Row, error) { - ctx = util.WithInternalSourceType(ctx, kv.InternalTimer) - sqlExec, ok := sctx.(sqlexec.SQLExecutor) - if !ok { - return nil, errors.New("session is not the type of SQLExecutor") - } - - rs, err := sqlExec.ExecuteInternal(ctx, sql, args...) - if err != nil { - return nil, err - } - - if rs == nil { - return nil, nil - } - - defer terror.Call(rs.Close) - return sqlexec.DrainRecordSet(ctx, rs, 1) -} - -func runInTxn(ctx context.Context, sctx sessionctx.Context, fn func() error) error { - if _, err := executeSQL(ctx, sctx, "BEGIN PESSIMISTIC"); err != nil { - return err - } - - success := false - defer func() { - if !success { - _, err := executeSQL(ctx, sctx, "ROLLBACK") - terror.Log(err) - } - }() - - if err := fn(); err != nil { - return err - } - - if _, err := executeSQL(ctx, sctx, "COMMIT"); err != nil { - return err - } - - success = true - return nil -} diff --git a/tools/check/check-errdoc.sh b/tools/check/check-errdoc.sh index 0d5d6a3bdfd5b..2b2aab6b40d26 100755 --- a/tools/check/check-errdoc.sh +++ b/tools/check/check-errdoc.sh @@ -23,5 +23,5 @@ set -euo pipefail cd -P . cp errors.toml /tmp/errors.toml.before -./tools/bin/errdoc-gen --source . --ignore parser,tidb-binlog --module github.com/pingcap/tidb --output errors.toml +./tools/bin/errdoc-gen --source . --ignore pkg/parser,pkg/tidb-binlog --module github.com/pingcap/tidb --output errors.toml diff -q errors.toml /tmp/errors.toml.before diff --git a/tools/check/xprog.go b/tools/check/xprog.go index 48bb1593afb01..9cb434f144472 100644 --- a/tools/check/xprog.go +++ b/tools/check/xprog.go @@ -46,7 +46,7 @@ func main() { os.Exit(-3) } - // github.com/pingcap/tidb/util/topsql.test => util/topsql + // github.com/pingcap/tidb/pkg/util/topsql.test => util/topsql pkg = pkg[len(prefix) : len(pkg)-len(".test")] _, file := filepath.Split(pkg) @@ -73,7 +73,7 @@ func getPackageInfo(dir string) string { defer f.Close() r := bufio.NewReader(f) - // packagefile github.com/pingcap/tidb/session.test=/home/genius/.cache/go-build/fb/fb1587cce5727fa9461131eab8260a52878da04f5c8da49dd3c7b2d941430c63-d + // packagefile github.com/pingcap/tidb/pkg/session.test=/home/genius/.cache/go-build/fb/fb1587cce5727fa9461131eab8260a52878da04f5c8da49dd3c7b2d941430c63-d line, _, err := r.ReadLine() if err != nil { os.Exit(-2) diff --git a/tools/tazel/BUILD.bazel b/tools/tazel/BUILD.bazel index 2a909468cf1da..d087ef079ece1 100644 --- a/tools/tazel/BUILD.bazel +++ b/tools/tazel/BUILD.bazel @@ -10,8 +10,8 @@ go_library( importpath = "github.com/pingcap/tidb/tools/tazel", visibility = ["//visibility:private"], deps = [ - "//util/mathutil", - "//util/set", + "//pkg/util/mathutil", + "//pkg/util/set", "@com_github_bazelbuild_buildtools//build:go_default_library", "@com_github_pingcap_log//:log", "@org_uber_go_zap//:zap", diff --git a/tools/tazel/main.go b/tools/tazel/main.go index 420e5833b06e7..6071d879c3100 100644 --- a/tools/tazel/main.go +++ b/tools/tazel/main.go @@ -23,7 +23,7 @@ import ( "github.com/bazelbuild/buildtools/build" "github.com/pingcap/log" - "github.com/pingcap/tidb/util/mathutil" + "github.com/pingcap/tidb/pkg/util/mathutil" "go.uber.org/zap" ) diff --git a/tools/tazel/util.go b/tools/tazel/util.go index 0fc7a04f0dadb..7e7a0d48a29d8 100644 --- a/tools/tazel/util.go +++ b/tools/tazel/util.go @@ -19,7 +19,7 @@ import ( "strings" "github.com/bazelbuild/buildtools/build" - "github.com/pingcap/tidb/util/set" + "github.com/pingcap/tidb/pkg/util/set" ) func write(path string, f *build.File) error { @@ -42,8 +42,8 @@ func skipTazel(path string) bool { func skipShardCount(path string) bool { return strings.HasPrefix(path, "tests") || - (strings.HasPrefix(path, "util") && - !strings.HasPrefix(path, "util/admin") && - !strings.HasPrefix(path, "util/chunk") && - !strings.HasPrefix(path, "util/stmtsummary")) + (strings.HasPrefix(path, "pkg/util") && + !strings.HasPrefix(path, "pkg/util/admin") && + !strings.HasPrefix(path, "pkg/util/chunk") && + !strings.HasPrefix(path, "pkg/util/stmtsummary")) } diff --git a/ttl/cache/BUILD.bazel b/ttl/cache/BUILD.bazel deleted file mode 100644 index 6b91715c72635..0000000000000 --- a/ttl/cache/BUILD.bazel +++ /dev/null @@ -1,71 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cache", - srcs = [ - "base.go", - "infoschema.go", - "table.go", - "task.go", - "ttlstatus.go", - ], - importpath = "github.com/pingcap/tidb/ttl/cache", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx", - "//table/tables", - "//tablecodec", - "//ttl/session", - "//types", - "//util/chunk", - "//util/codec", - "//util/logutil", - "//util/mathutil", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "cache_test", - timeout = "short", - srcs = [ - "base_test.go", - "infoschema_test.go", - "main_test.go", - "split_test.go", - "table_test.go", - "task_test.go", - "ttlstatus_test.go", - ], - embed = [":cache"], - flaky = True, - shard_count = 13, - deps = [ - "//infoschema", - "//kv", - "//parser/model", - "//server", - "//session", - "//store/helper", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "//ttl/session", - "//types", - "//util/codec", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_pd_client//:client", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ttl/cache/infoschema.go b/ttl/cache/infoschema.go deleted file mode 100644 index 83082e060a4c2..0000000000000 --- a/ttl/cache/infoschema.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache - -import ( - "time" - - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// InfoSchemaCache is the cache for InfoSchema, it builds a map from physical table id to physical table information -type InfoSchemaCache struct { - baseCache - - schemaVer int64 - Tables map[int64]*PhysicalTable -} - -// NewInfoSchemaCache creates the cache for info schema -func NewInfoSchemaCache(updateInterval time.Duration) *InfoSchemaCache { - return &InfoSchemaCache{ - baseCache: newBaseCache(updateInterval), - Tables: make(map[int64]*PhysicalTable), - } -} - -// Update updates the info schema cache -func (isc *InfoSchemaCache) Update(se session.Session) error { - is := se.GetDomainInfoSchema().(infoschema.InfoSchema) - - if isc.schemaVer == is.SchemaMetaVersion() { - return nil - } - - newTables := make(map[int64]*PhysicalTable, len(isc.Tables)) - for _, db := range is.AllSchemas() { - for _, tbl := range is.SchemaTables(db.Name) { - tblInfo := tbl.Meta() - if tblInfo.TTLInfo == nil || !tblInfo.TTLInfo.Enable || tblInfo.State != model.StatePublic { - continue - } - - logger := logutil.BgLogger(). - With(zap.String("schema", db.Name.L), - zap.Int64("tableID", tblInfo.ID), zap.String("tableName", tblInfo.Name.L)) - - if tblInfo.Partition == nil { - ttlTable, err := isc.newTable(db.Name, tblInfo, nil) - if err != nil { - logger.Warn("fail to build info schema cache", zap.Error(err)) - continue - } - newTables[tblInfo.ID] = ttlTable - continue - } - - for _, par := range tblInfo.Partition.Definitions { - par := par - ttlTable, err := isc.newTable(db.Name, tblInfo, &par) - if err != nil { - logger.Warn("fail to build info schema cache", - zap.Int64("partitionID", par.ID), - zap.String("partition", par.Name.L), zap.Error(err)) - continue - } - newTables[par.ID] = ttlTable - } - } - } - - isc.schemaVer = is.SchemaMetaVersion() - isc.Tables = newTables - isc.updateTime = time.Now() - return nil -} - -func (isc *InfoSchemaCache) newTable(schema model.CIStr, tblInfo *model.TableInfo, - par *model.PartitionDefinition) (*PhysicalTable, error) { - id := tblInfo.ID - if par != nil { - id = par.ID - } - - if isc.Tables != nil { - ttlTable, ok := isc.Tables[id] - if ok && ttlTable.TableInfo == tblInfo { - return ttlTable, nil - } - } - - partitionName := model.NewCIStr("") - if par != nil { - partitionName = par.Name - } - return NewPhysicalTable(schema, tblInfo, partitionName) -} diff --git a/ttl/cache/infoschema_test.go b/ttl/cache/infoschema_test.go deleted file mode 100644 index ef428e4399c25..0000000000000 --- a/ttl/cache/infoschema_test.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache_test - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/server" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/stretchr/testify/assert" -) - -func TestInfoSchemaCache(t *testing.T) { - store, dom := testkit.CreateMockStoreAndDomain(t) - sv := server.CreateMockServer(t, store) - sv.SetDomain(dom) - defer sv.Close() - - conn := server.CreateMockConn(t, sv) - sctx := conn.Context().Session - tk := testkit.NewTestKitWithSession(t, store, sctx) - se := session.NewSession(sctx, sctx, func(_ session.Session) {}) - - isc := cache.NewInfoSchemaCache(time.Hour) - - // test should update - assert.True(t, isc.ShouldUpdate()) - assert.NoError(t, isc.Update(se)) - assert.False(t, isc.ShouldUpdate()) - - // test new tables are synced - assert.Equal(t, 0, len(isc.Tables)) - tk.MustExec("create table test.t(created_at datetime) ttl = created_at + INTERVAL 5 YEAR") - assert.NoError(t, isc.Update(se)) - assert.Equal(t, 1, len(isc.Tables)) - for _, table := range isc.Tables { - assert.Equal(t, "t", table.TableInfo.Name.L) - } - - // test new partitioned table are synced - tk.MustExec("drop table test.t") - tk.MustExec(`create table test.t(created_at datetime) - ttl = created_at + INTERVAL 5 YEAR - partition by range (YEAR(created_at)) ( - partition p0 values less than (1991), - partition p1 values less than (2000) - ) - `) - assert.NoError(t, isc.Update(se)) - assert.Equal(t, 2, len(isc.Tables)) - partitions := []string{} - for id, table := range isc.Tables { - assert.Equal(t, "t", table.TableInfo.Name.L) - assert.Equal(t, id, table.PartitionDef.ID) - partitions = append(partitions, table.PartitionDef.Name.L) - } - assert.ElementsMatch(t, []string{"p0", "p1"}, partitions) -} diff --git a/ttl/cache/main_test.go b/ttl/cache/main_test.go deleted file mode 100644 index 8b90f0a4dc116..0000000000000 --- a/ttl/cache/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ttl/cache/split_test.go b/ttl/cache/split_test.go deleted file mode 100644 index 0b49a2e56f9fa..0000000000000 --- a/ttl/cache/split_test.go +++ /dev/null @@ -1,826 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache_test - -import ( - "context" - "fmt" - "math" - "sort" - "testing" - - "github.com/pingcap/kvproto/pkg/metapb" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/stretchr/testify/require" - "github.com/tikv/client-go/v2/tikv" - pd "github.com/tikv/pd/client" -) - -func newMockRegion(regionID uint64, startKey []byte, endKey []byte) *pd.Region { - leader := &metapb.Peer{ - Id: regionID, - StoreId: 1, - Role: metapb.PeerRole_Voter, - } - - return &pd.Region{ - Meta: &metapb.Region{ - Id: regionID, - StartKey: startKey, - EndKey: endKey, - Peers: []*metapb.Peer{leader}, - }, - Leader: leader, - } -} - -type mockPDClient struct { - t *testing.T - pd.Client - regions []*pd.Region - regionsSorted bool -} - -func (c *mockPDClient) ScanRegions(_ context.Context, key, endKey []byte, limit int) ([]*pd.Region, error) { - if len(c.regions) == 0 { - return []*pd.Region{newMockRegion(1, []byte{}, []byte{0xFF, 0xFF})}, nil - } - - if !c.regionsSorted { - sort.Slice(c.regions, func(i, j int) bool { - return kv.Key(c.regions[i].Meta.StartKey).Cmp(c.regions[j].Meta.StartKey) < 0 - }) - c.regionsSorted = true - } - - regions := []*pd.Region{newMockRegion(1, []byte{}, c.regions[0].Meta.StartKey)} - regions = append(regions, c.regions...) - regions = append(regions, newMockRegion(2, c.regions[len(c.regions)-1].Meta.EndKey, []byte{0xFF, 0xFF, 0xFF})) - - result := make([]*pd.Region, 0) - for _, r := range regions { - if kv.Key(r.Meta.StartKey).Cmp(endKey) >= 0 { - continue - } - - if kv.Key(r.Meta.EndKey).Cmp(key) <= 0 { - continue - } - - if len(result) >= limit { - break - } - - result = append(result, r) - } - return result, nil -} - -func (c *mockPDClient) GetStore(_ context.Context, storeID uint64) (*metapb.Store, error) { - return &metapb.Store{ - Id: storeID, - Address: fmt.Sprintf("127.0.0.%d", storeID), - }, nil -} - -type mockTiKVStore struct { - t *testing.T - helper.Storage - pdClient *mockPDClient - cache *tikv.RegionCache - nextRegionID uint64 -} - -func newMockTiKVStore(t *testing.T) *mockTiKVStore { - pdClient := &mockPDClient{t: t} - s := &mockTiKVStore{ - t: t, - pdClient: pdClient, - cache: tikv.NewRegionCache(pdClient), - nextRegionID: 1000, - } - s.refreshCache() - t.Cleanup(func() { - s.cache.Close() - }) - return s -} - -func (s *mockTiKVStore) addRegionBeginWithTablePrefix(tableID int64, handle kv.Handle) *mockTiKVStore { - start := tablecodec.GenTablePrefix(tableID) - end := tablecodec.EncodeRowKeyWithHandle(tableID, handle) - return s.addRegion(start, end) -} - -func (s *mockTiKVStore) addRegionEndWithTablePrefix(handle kv.Handle, tableID int64) *mockTiKVStore { - start := tablecodec.EncodeRowKeyWithHandle(tableID, handle) - end := tablecodec.GenTablePrefix(tableID + 1) - return s.addRegion(start, end) -} - -func (s *mockTiKVStore) addRegionWithTablePrefix(tableID int64, start kv.Handle, end kv.Handle) *mockTiKVStore { - startKey := tablecodec.EncodeRowKeyWithHandle(tableID, start) - endKey := tablecodec.EncodeRowKeyWithHandle(tableID, end) - return s.addRegion(startKey, endKey) -} - -func (s *mockTiKVStore) addRegion(key, endKey []byte) *mockTiKVStore { - require.True(s.t, kv.Key(endKey).Cmp(key) > 0) - if len(s.pdClient.regions) > 0 { - lastRegion := s.pdClient.regions[len(s.pdClient.regions)-1] - require.True(s.t, kv.Key(endKey).Cmp(lastRegion.Meta.EndKey) >= 0) - } - - regionID := s.nextRegionID - s.nextRegionID++ - leader := &metapb.Peer{ - Id: regionID, - StoreId: 1, - Role: metapb.PeerRole_Voter, - } - - s.pdClient.regions = append(s.pdClient.regions, &pd.Region{ - Meta: &metapb.Region{ - Id: regionID, - StartKey: key, - EndKey: endKey, - Peers: []*metapb.Peer{leader}, - }, - Leader: leader, - }) - - s.pdClient.regionsSorted = false - s.refreshCache() - return s -} - -func (s *mockTiKVStore) refreshCache() { - _, err := s.cache.LoadRegionsInKeyRange( - tikv.NewBackofferWithVars(context.Background(), 1000, nil), - []byte{}, - []byte{0xFF}, - ) - require.NoError(s.t, err) -} - -func (s *mockTiKVStore) batchAddIntHandleRegions(tblID int64, regionCnt, regionSize int, - offset int64) (end kv.IntHandle) { - for i := 0; i < regionCnt; i++ { - start := kv.IntHandle(offset + int64(i*regionSize)) - end = kv.IntHandle(start.IntValue() + int64(regionSize)) - s.addRegionWithTablePrefix(tblID, start, end) - } - return -} - -func (s *mockTiKVStore) clearRegions() { - s.pdClient.regions = nil - s.cache.Close() - s.cache = tikv.NewRegionCache(s.pdClient) - s.refreshCache() -} - -func (s *mockTiKVStore) GetRegionCache() *tikv.RegionCache { - return s.cache -} - -func bytesHandle(t *testing.T, data []byte) kv.Handle { - encoded, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(data)) - require.NoError(t, err) - h, err := kv.NewCommonHandle(encoded) - require.NoError(t, err) - return h -} - -func createTTLTable(t *testing.T, tk *testkit.TestKit, name string, option string) *cache.PhysicalTable { - if option == "" { - return createTTLTableWithSQL(t, tk, name, - fmt.Sprintf("create table test.%s(t timestamp) TTL = `t` + interval 1 day", name)) - } - - return createTTLTableWithSQL(t, tk, name, - fmt.Sprintf("create table test.%s(id %s primary key, t timestamp) TTL = `t` + interval 1 day", - name, option)) -} - -func create2PKTTLTable(t *testing.T, tk *testkit.TestKit, name string, option string) *cache.PhysicalTable { - return createTTLTableWithSQL(t, tk, name, - fmt.Sprintf( - "create table test.%s(id %s, id2 int, t timestamp, primary key(id, id2)) TTL = `t` + interval 1 day", - name, option)) -} - -func createTTLTableWithSQL(t *testing.T, tk *testkit.TestKit, name string, sql string) *cache.PhysicalTable { - tk.MustExec(sql) - is, ok := tk.Session().GetDomainInfoSchema().(infoschema.InfoSchema) - require.True(t, ok) - tbl, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr(name)) - require.NoError(t, err) - ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tbl.Meta(), model.NewCIStr("")) - require.NoError(t, err) - return ttlTbl -} - -func checkRange(t *testing.T, r cache.ScanRange, start, end types.Datum) { - if start.IsNull() { - require.Nil(t, r.Start) - } else { - require.Equal(t, 1, len(r.Start)) - require.Equal(t, start.Kind(), r.Start[0].Kind()) - require.Equal(t, start.GetValue(), r.Start[0].GetValue()) - } - - if end.IsNull() { - require.Nil(t, r.End) - } else { - require.Equal(t, 1, len(r.End)) - require.Equal(t, end.Kind(), r.End[0].Kind()) - require.Equal(t, end.GetValue(), r.End[0].GetValue()) - } -} - -func TestSplitTTLScanRangesWithSignedInt(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tbls := []*cache.PhysicalTable{ - createTTLTable(t, tk, "t1", "tinyint"), - createTTLTable(t, tk, "t2", "smallint"), - createTTLTable(t, tk, "t3", "mediumint"), - createTTLTable(t, tk, "t4", "int"), - createTTLTable(t, tk, "t5", "bigint"), - createTTLTable(t, tk, "t6", ""), // no clustered - create2PKTTLTable(t, tk, "t7", "tinyint"), - } - - tikvStore := newMockTiKVStore(t) - for _, tbl := range tbls { - // test only one region - tikvStore.clearRegions() - ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test share regions with other table - tikvStore.clearRegions() - tikvStore.addRegion( - tablecodec.GenTablePrefix(tbl.ID-1), - tablecodec.GenTablePrefix(tbl.ID+1), - ) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test one table has multiple regions - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(0)) - end := tikvStore.batchAddIntHandleRegions(tbl.ID, 8, 100, 0) - tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 4, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.NewIntDatum(200)) - checkRange(t, ranges[1], types.NewIntDatum(200), types.NewIntDatum(500)) - checkRange(t, ranges[2], types.NewIntDatum(500), types.NewIntDatum(700)) - checkRange(t, ranges[3], types.NewIntDatum(700), types.Datum{}) - - // test one table has multiple regions and one table region across 0 - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-350)) - end = tikvStore.batchAddIntHandleRegions(tbl.ID, 8, 100, -350) - tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 5) - require.NoError(t, err) - require.Equal(t, 5, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.NewIntDatum(-250)) - checkRange(t, ranges[1], types.NewIntDatum(-250), types.NewIntDatum(-50)) - checkRange(t, ranges[2], types.NewIntDatum(-50), types.NewIntDatum(150)) - checkRange(t, ranges[3], types.NewIntDatum(150), types.NewIntDatum(350)) - checkRange(t, ranges[4], types.NewIntDatum(350), types.Datum{}) - } -} - -func TestSplitTTLScanRangesWithUnsignedInt(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tbls := []*cache.PhysicalTable{ - createTTLTable(t, tk, "t1", "tinyint unsigned"), - createTTLTable(t, tk, "t2", "smallint unsigned"), - createTTLTable(t, tk, "t3", "mediumint unsigned"), - createTTLTable(t, tk, "t4", "int unsigned"), - createTTLTable(t, tk, "t5", "bigint unsigned"), - create2PKTTLTable(t, tk, "t6", "tinyint unsigned"), - } - - tikvStore := newMockTiKVStore(t) - for _, tbl := range tbls { - // test only one region - tikvStore.clearRegions() - ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test share regions with other table - tikvStore.clearRegions() - tikvStore.addRegion( - tablecodec.GenTablePrefix(tbl.ID-1), - tablecodec.GenTablePrefix(tbl.ID+1), - ) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test one table has multiple regions: [MinInt64, a) [a, b) [b, 0) [0, c) [c, d) [d, MaxInt64] - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-200)) - end := tikvStore.batchAddIntHandleRegions(tbl.ID, 4, 100, -200) - tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 6) - require.NoError(t, err) - require.Equal(t, 6, len(ranges)) - checkRange(t, ranges[0], - types.NewUintDatum(uint64(math.MaxInt64)+1), types.NewUintDatum(uint64(math.MaxUint64)-199)) - checkRange(t, ranges[1], - types.NewUintDatum(uint64(math.MaxUint64)-199), types.NewUintDatum(uint64(math.MaxUint64)-99)) - checkRange(t, ranges[2], - types.NewUintDatum(uint64(math.MaxUint64)-99), types.Datum{}) - checkRange(t, ranges[3], - types.Datum{}, types.NewUintDatum(100)) - checkRange(t, ranges[4], - types.NewUintDatum(100), types.NewUintDatum(200)) - checkRange(t, ranges[5], - types.NewUintDatum(200), types.NewUintDatum(uint64(math.MaxInt64)+1)) - - // test one table has multiple regions: [MinInt64, a) [a, b) [b, c) [c, d) [d, MaxInt64], b < 0 < c - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, kv.IntHandle(-150)) - end = tikvStore.batchAddIntHandleRegions(tbl.ID, 3, 100, -150) - tikvStore.addRegionEndWithTablePrefix(end, tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 5) - require.NoError(t, err) - require.Equal(t, 6, len(ranges)) - checkRange(t, ranges[0], - types.NewUintDatum(uint64(math.MaxInt64)+1), types.NewUintDatum(uint64(math.MaxUint64)-149)) - checkRange(t, ranges[1], - types.NewUintDatum(uint64(math.MaxUint64)-149), types.NewUintDatum(uint64(math.MaxUint64)-49)) - checkRange(t, ranges[2], - types.NewUintDatum(uint64(math.MaxUint64)-49), types.Datum{}) - checkRange(t, ranges[3], - types.Datum{}, types.NewUintDatum(50)) - checkRange(t, ranges[4], - types.NewUintDatum(50), types.NewUintDatum(150)) - checkRange(t, ranges[5], - types.NewUintDatum(150), types.NewUintDatum(uint64(math.MaxInt64)+1)) - } -} - -func TestSplitTTLScanRangesWithBytes(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tbls := []*cache.PhysicalTable{ - createTTLTable(t, tk, "t1", "binary(32)"), - createTTLTable(t, tk, "t2", "char(32) CHARACTER SET BINARY"), - createTTLTable(t, tk, "t3", "varchar(32) CHARACTER SET BINARY"), - createTTLTable(t, tk, "t4", "bit(32)"), - create2PKTTLTable(t, tk, "t5", "binary(32)"), - } - - tikvStore := newMockTiKVStore(t) - for _, tbl := range tbls { - // test only one region - tikvStore.clearRegions() - ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test share regions with other table - tikvStore.clearRegions() - tikvStore.addRegion( - tablecodec.GenTablePrefix(tbl.ID-1), - tablecodec.GenTablePrefix(tbl.ID+1), - ) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test one table has multiple regions - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3})) - tikvStore.addRegionWithTablePrefix( - tbl.ID, bytesHandle(t, []byte{1, 2, 3}), bytesHandle(t, []byte{1, 2, 3, 4})) - tikvStore.addRegionWithTablePrefix( - tbl.ID, bytesHandle(t, []byte{1, 2, 3, 4}), bytesHandle(t, []byte{1, 2, 3, 4, 5})) - tikvStore.addRegionWithTablePrefix( - tbl.ID, bytesHandle(t, []byte{1, 2, 3, 4, 5}), bytesHandle(t, []byte{1, 2, 4})) - tikvStore.addRegionWithTablePrefix( - tbl.ID, bytesHandle(t, []byte{1, 2, 4}), bytesHandle(t, []byte{1, 2, 5})) - tikvStore.addRegionEndWithTablePrefix(bytesHandle(t, []byte{1, 2, 5}), tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 4, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.NewBytesDatum([]byte{1, 2, 3, 4})) - checkRange(t, ranges[1], types.NewBytesDatum([]byte{1, 2, 3, 4}), types.NewBytesDatum([]byte{1, 2, 4})) - checkRange(t, ranges[2], types.NewBytesDatum([]byte{1, 2, 4}), types.NewBytesDatum([]byte{1, 2, 5})) - checkRange(t, ranges[3], types.NewBytesDatum([]byte{1, 2, 5}), types.Datum{}) - } -} - -func TestNoTTLSplitSupportTables(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - - tbls := []*cache.PhysicalTable{ - createTTLTable(t, tk, "t1", "char(32) CHARACTER SET UTF8MB4"), - createTTLTable(t, tk, "t2", "varchar(32) CHARACTER SET UTF8MB4"), - createTTLTable(t, tk, "t4", "decimal(32, 2)"), - create2PKTTLTable(t, tk, "t5", "char(32) CHARACTER SET UTF8MB4"), - } - - tikvStore := newMockTiKVStore(t) - for _, tbl := range tbls { - // test only one region - tikvStore.clearRegions() - ranges, err := tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test share regions with other table - tikvStore.clearRegions() - tikvStore.addRegion( - tablecodec.GenTablePrefix(tbl.ID-1), - tablecodec.GenTablePrefix(tbl.ID+1), - ) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 4) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - - // test one table has multiple regions - tikvStore.clearRegions() - tikvStore.addRegionBeginWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3})) - tikvStore.addRegionWithTablePrefix(tbl.ID, bytesHandle(t, []byte{1, 2, 3}), bytesHandle(t, []byte{1, 2, 3, 4})) - tikvStore.addRegionEndWithTablePrefix(bytesHandle(t, []byte{1, 2, 3, 4}), tbl.ID) - ranges, err = tbl.SplitScanRanges(context.TODO(), tikvStore, 3) - require.NoError(t, err) - require.Equal(t, 1, len(ranges)) - checkRange(t, ranges[0], types.Datum{}, types.Datum{}) - } -} - -func TestGetNextBytesHandleDatum(t *testing.T) { - tblID := int64(7) - buildHandleBytes := func(data []byte) []byte { - handleBytes, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(data)) - require.NoError(t, err) - return handleBytes - } - - buildRowKey := func(handleBytes []byte) kv.Key { - return tablecodec.EncodeRowKey(tblID, handleBytes) - } - - buildBytesRowKey := func(data []byte) kv.Key { - return buildRowKey(buildHandleBytes(data)) - } - - binaryDataStartPos := len(tablecodec.GenTableRecordPrefix(tblID)) + 1 - cases := []struct { - key interface{} - result []byte - isNull bool - }{ - { - key: buildBytesRowKey([]byte{}), - result: []byte{}, - }, - { - key: buildBytesRowKey([]byte{1, 2, 3}), - result: []byte{1, 2, 3}, - }, - { - key: buildBytesRowKey([]byte{1, 2, 3, 0}), - result: []byte{1, 2, 3, 0}, - }, - { - key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}), - result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }, - { - key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}), - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - }, - { - key: buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0}, - }, - { - key: append(buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), 0), - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0}, - }, - { - key: append(buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 0}), 1), - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 0, 0}, - }, - { - key: []byte{}, - result: []byte{}, - }, - { - key: tablecodec.GenTableRecordPrefix(tblID), - result: []byte{}, - }, - { - key: tablecodec.GenTableRecordPrefix(tblID - 1), - result: []byte{}, - }, - { - key: tablecodec.GenTablePrefix(tblID).PrefixNext(), - isNull: true, - }, - { - key: buildRowKey([]byte{0}), - result: []byte{}, - }, - { - key: buildRowKey([]byte{1}), - result: []byte{}, - }, - { - key: buildRowKey([]byte{2}), - isNull: true, - }, - { - // recordPrefix + bytesFlag + [0] - key: buildBytesRowKey([]byte{})[:binaryDataStartPos+1], - result: []byte{}, - }, - { - // recordPrefix + bytesFlag + [0, 0, 0, 0, 0, 0, 0, 0] - key: buildBytesRowKey([]byte{})[:binaryDataStartPos+8], - result: []byte{}, - }, - { - // recordPrefix + bytesFlag + [1] - key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+1], - result: []byte{1}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3] - key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+3], - result: []byte{1, 2, 3}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 0] - key: buildBytesRowKey([]byte{1, 2, 3})[:binaryDataStartPos+4], - result: []byte{1, 2, 3}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 0, 0, 0, 0, 0, 247] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3}) - bs[len(bs)-1] = 247 - return bs - }, - result: []byte{1, 2, 3}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 0, 0, 0, 0, 0, 0] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3}) - bs[len(bs)-1] = 0 - return bs - }, - result: []byte{1, 2, 3}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 254, 9, 0, 0, 0, 0, 0, 0, 0, 248] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) - bs[len(bs)-10] = 254 - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 0, 254, 9, 0, 0, 0, 0, 0, 0, 0, 248] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 0, 9}) - bs[len(bs)-10] = 254 - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 0}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 0, 253, 9, 0, 0, 0, 0, 0, 0, 0, 248] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 0, 9}) - bs[len(bs)-10] = 253 - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 247] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) - bs[len(bs)-1] = 247 - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 0] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) - bs[len(bs)-1] = 0 - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) - bs = bs[:len(bs)-1] - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}) - bs = bs[:len(bs)-1] - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }, - { - // recordPrefix + bytesFlag + [1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 246] - key: func() []byte { - bs := buildBytesRowKey([]byte{1, 2, 3, 4, 5, 6, 7, 8}) - bs = bs[:len(bs)-1] - return bs - }, - result: []byte{1, 2, 3, 4, 5, 6, 7, 8}, - }, - } - - for i, c := range cases { - var key kv.Key - switch k := c.key.(type) { - case kv.Key: - key = k - case []byte: - key = k - case func() []byte: - key = k() - case func() kv.Key: - key = k() - default: - require.FailNow(t, "%d", i) - } - - d := cache.GetNextBytesHandleDatum(key, tablecodec.GenTableRecordPrefix(tblID)) - if c.isNull { - require.True(t, d.IsNull(), i) - } else { - require.Equal(t, types.KindBytes, d.Kind(), i) - require.Equal(t, c.result, d.GetBytes(), i) - } - } -} -func TestGetNextIntHandle(t *testing.T) { - tblID := int64(7) - cases := []struct { - key interface{} - result int64 - isNull bool - }{ - { - key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(0)), - result: 0, - }, - { - key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(3)), - result: 3, - }, - { - key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MaxInt64)), - result: math.MaxInt64, - }, - { - key: tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MinInt64)), - result: math.MinInt64, - }, - { - key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(7)), 0), - result: 8, - }, - { - key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MaxInt64)), 0), - isNull: true, - }, - { - key: append(tablecodec.EncodeRowKeyWithHandle(tblID, kv.IntHandle(math.MinInt64)), 0), - result: math.MinInt64 + 1, - }, - { - key: []byte{}, - result: math.MinInt64, - }, - { - key: tablecodec.GenTableRecordPrefix(tblID), - result: math.MinInt64, - }, - { - key: tablecodec.GenTableRecordPrefix(tblID - 1), - result: math.MinInt64, - }, - { - key: tablecodec.GenTablePrefix(tblID).PrefixNext(), - isNull: true, - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{0}), - result: codec.DecodeCmpUintToInt(0), - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{0, 1, 2, 3}), - result: codec.DecodeCmpUintToInt(0x0001020300000000), - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{8, 1, 2, 3}), - result: codec.DecodeCmpUintToInt(0x0801020300000000), - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{0, 1, 2, 3, 4, 5, 6, 7, 0}), - result: codec.DecodeCmpUintToInt(0x0001020304050607) + 1, - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{8, 1, 2, 3, 4, 5, 6, 7, 0}), - result: codec.DecodeCmpUintToInt(0x0801020304050607) + 1, - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), - result: math.MaxInt64, - }, - { - key: tablecodec.EncodeRowKey(tblID, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0}), - isNull: true, - }, - } - - for i, c := range cases { - var key kv.Key - switch k := c.key.(type) { - case kv.Key: - key = k - case []byte: - key = k - case func() []byte: - key = k() - case func() kv.Key: - key = k() - default: - require.FailNow(t, "%d", i) - } - - v := cache.GetNextIntHandle(key, tablecodec.GenTableRecordPrefix(tblID)) - if c.isNull { - require.Nil(t, v, i) - } else { - require.IsType(t, kv.IntHandle(0), v, i) - require.Equal(t, c.result, v.IntValue()) - } - } -} diff --git a/ttl/cache/table.go b/ttl/cache/table.go deleted file mode 100644 index 1a79065d2c495..0000000000000 --- a/ttl/cache/table.go +++ /dev/null @@ -1,489 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache - -import ( - "context" - "encoding/binary" - "fmt" - "math" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/mathutil" - "github.com/tikv/client-go/v2/tikv" -) - -func getTableKeyColumns(tbl *model.TableInfo) ([]*model.ColumnInfo, []*types.FieldType, error) { - if tbl.PKIsHandle { - for i, col := range tbl.Columns { - if mysql.HasPriKeyFlag(col.GetFlag()) { - return []*model.ColumnInfo{tbl.Columns[i]}, []*types.FieldType{&tbl.Columns[i].FieldType}, nil - } - } - return nil, nil, errors.Errorf("Cannot find primary key for table: %s", tbl.Name) - } - - if tbl.IsCommonHandle { - idxInfo := tables.FindPrimaryIndex(tbl) - columns := make([]*model.ColumnInfo, len(idxInfo.Columns)) - fieldTypes := make([]*types.FieldType, len(idxInfo.Columns)) - for i, idxCol := range idxInfo.Columns { - columns[i] = tbl.Columns[idxCol.Offset] - fieldTypes[i] = &tbl.Columns[idxCol.Offset].FieldType - } - return columns, fieldTypes, nil - } - - extraHandleColInfo := model.NewExtraHandleColInfo() - return []*model.ColumnInfo{extraHandleColInfo}, []*types.FieldType{&extraHandleColInfo.FieldType}, nil -} - -// ScanRange is the range to scan. The range is: [Start, End) -type ScanRange struct { - Start []types.Datum - End []types.Datum -} - -func newFullRange() ScanRange { - return ScanRange{} -} - -func newDatumRange(start types.Datum, end types.Datum) (r ScanRange) { - if !start.IsNull() { - r.Start = []types.Datum{start} - } - if !end.IsNull() { - r.End = []types.Datum{end} - } - return r -} - -func nullDatum() types.Datum { - d := types.Datum{} - d.SetNull() - return d -} - -// PhysicalTable is used to provide some information for a physical table in TTL job -type PhysicalTable struct { - // ID is the physical ID of the table - ID int64 - // Schema is the database name of the table - Schema model.CIStr - *model.TableInfo - // Partition is the partition name - Partition model.CIStr - // PartitionDef is the partition definition - PartitionDef *model.PartitionDefinition - // KeyColumns is the cluster index key columns for the table - KeyColumns []*model.ColumnInfo - // KeyColumnTypes is the types of the key columns - KeyColumnTypes []*types.FieldType - // TimeColum is the time column used for TTL - TimeColumn *model.ColumnInfo -} - -// NewBasePhysicalTable create a new PhysicalTable with specific timeColunm. -func NewBasePhysicalTable(schema model.CIStr, - tbl *model.TableInfo, - partition model.CIStr, - timeColumn *model.ColumnInfo, -) (*PhysicalTable, error) { - if tbl.State != model.StatePublic { - return nil, errors.Errorf("table '%s.%s' is not a public table", schema, tbl.Name) - } - - keyColumns, keyColumTypes, err := getTableKeyColumns(tbl) - if err != nil { - return nil, err - } - - var physicalID int64 - var partitionDef *model.PartitionDefinition - if tbl.Partition == nil { - if partition.L != "" { - return nil, errors.Errorf("table '%s.%s' is not a partitioned table", schema, tbl.Name) - } - physicalID = tbl.ID - } else { - if partition.L == "" { - return nil, errors.Errorf("partition name is required, table '%s.%s' is a partitioned table", schema, tbl.Name) - } - - for i := range tbl.Partition.Definitions { - def := &tbl.Partition.Definitions[i] - if def.Name.L == partition.L { - partitionDef = def - } - } - - if partitionDef == nil { - return nil, errors.Errorf("partition '%s' is not found in ttl table '%s.%s'", partition.O, schema, tbl.Name) - } - - physicalID = partitionDef.ID - } - - return &PhysicalTable{ - ID: physicalID, - Schema: schema, - TableInfo: tbl, - Partition: partition, - PartitionDef: partitionDef, - KeyColumns: keyColumns, - KeyColumnTypes: keyColumTypes, - TimeColumn: timeColumn, - }, nil -} - -// NewPhysicalTable create a new PhysicalTable -func NewPhysicalTable(schema model.CIStr, tbl *model.TableInfo, partition model.CIStr) (*PhysicalTable, error) { - ttlInfo := tbl.TTLInfo - if ttlInfo == nil { - return nil, errors.Errorf("table '%s.%s' is not a ttl table", schema, tbl.Name) - } - - timeColumn := tbl.FindPublicColumnByName(ttlInfo.ColumnName.L) - if timeColumn == nil { - return nil, errors.Errorf("time column '%s' is not public in ttl table '%s.%s'", ttlInfo.ColumnName, schema, tbl.Name) - } - - return NewBasePhysicalTable(schema, tbl, partition, timeColumn) -} - -// ValidateKeyPrefix validates a key prefix -func (t *PhysicalTable) ValidateKeyPrefix(key []types.Datum) error { - if len(key) > len(t.KeyColumns) { - return errors.Errorf("invalid key length: %d, expected %d", len(key), len(t.KeyColumns)) - } - return nil -} - -// EvalExpireTime returns the expired time -func (t *PhysicalTable) EvalExpireTime(ctx context.Context, se session.Session, - now time.Time) (expire time.Time, err error) { - tz := se.GetSessionVars().Location() - - expireExpr := t.TTLInfo.IntervalExprStr - unit := ast.TimeUnitType(t.TTLInfo.IntervalTimeUnit) - - var rows []chunk.Row - rows, err = se.ExecuteSQL( - ctx, - // FROM_UNIXTIME does not support negative value, so we use `FROM_UNIXTIME(0) + INTERVAL ` - // to present current time - fmt.Sprintf("SELECT FROM_UNIXTIME(0) + INTERVAL %d SECOND - INTERVAL %s %s", now.Unix(), expireExpr, unit.String()), - ) - - if err != nil { - return - } - - tm := rows[0].GetTime(0) - return tm.CoreTime().GoTime(tz) -} - -// SplitScanRanges split ranges for TTL scan -func (t *PhysicalTable) SplitScanRanges(ctx context.Context, store kv.Storage, splitCnt int) ([]ScanRange, error) { - if len(t.KeyColumns) < 1 || splitCnt <= 1 { - return []ScanRange{newFullRange()}, nil - } - - tikvStore, ok := store.(tikv.Storage) - if !ok { - return []ScanRange{newFullRange()}, nil - } - - ft := t.KeyColumns[0].FieldType - switch ft.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24: - return t.splitIntRanges(ctx, tikvStore, splitCnt) - case mysql.TypeBit: - return t.splitBinaryRanges(ctx, tikvStore, splitCnt) - case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar: - if mysql.HasBinaryFlag(ft.GetFlag()) { - return t.splitBinaryRanges(ctx, tikvStore, splitCnt) - } - } - return []ScanRange{newFullRange()}, nil -} - -func unsignedEdge(d types.Datum) types.Datum { - if d.IsNull() { - return types.NewUintDatum(uint64(math.MaxInt64 + 1)) - } - if d.GetInt64() == 0 { - return nullDatum() - } - return types.NewUintDatum(uint64(d.GetInt64())) -} - -func (t *PhysicalTable) splitIntRanges(ctx context.Context, store tikv.Storage, splitCnt int) ([]ScanRange, error) { - recordPrefix := tablecodec.GenTableRecordPrefix(t.ID) - startKey, endKey := tablecodec.GetTableHandleKeyRange(t.ID) - keyRanges, err := t.splitRawKeyRanges(ctx, store, startKey, endKey, splitCnt) - if err != nil { - return nil, err - } - - if len(keyRanges) <= 1 { - return []ScanRange{newFullRange()}, nil - } - - ft := t.KeyColumnTypes[0] - unsigned := mysql.HasUnsignedFlag(ft.GetFlag()) - scanRanges := make([]ScanRange, 0, len(keyRanges)+1) - curScanStart := nullDatum() - for i, keyRange := range keyRanges { - if i != 0 && curScanStart.IsNull() { - break - } - - curScanEnd := nullDatum() - if i < len(keyRanges)-1 { - if val := GetNextIntHandle(keyRange.EndKey, recordPrefix); val != nil { - curScanEnd = types.NewIntDatum(val.IntValue()) - } - } - - if !curScanStart.IsNull() && !curScanEnd.IsNull() && curScanStart.GetInt64() >= curScanEnd.GetInt64() { - continue - } - - if !unsigned { - // primary key is signed or range - scanRanges = append(scanRanges, newDatumRange(curScanStart, curScanEnd)) - } else if !curScanStart.IsNull() && curScanStart.GetInt64() >= 0 { - // primary key is unsigned and range is in the right half side - scanRanges = append(scanRanges, newDatumRange(unsignedEdge(curScanStart), unsignedEdge(curScanEnd))) - } else if !curScanEnd.IsNull() && curScanEnd.GetInt64() <= 0 { - // primary key is unsigned and range is in the left half side - scanRanges = append(scanRanges, newDatumRange(unsignedEdge(curScanStart), unsignedEdge(curScanEnd))) - } else { - // primary key is unsigned and the start > math.MaxInt64 && end < math.MaxInt64 - // we must split it to two ranges - scanRanges = append(scanRanges, - newDatumRange(unsignedEdge(curScanStart), nullDatum()), - newDatumRange(nullDatum(), unsignedEdge(curScanEnd)), - ) - } - curScanStart = curScanEnd - } - return scanRanges, nil -} - -func (t *PhysicalTable) splitBinaryRanges(ctx context.Context, store tikv.Storage, splitCnt int) ([]ScanRange, error) { - recordPrefix := tablecodec.GenTableRecordPrefix(t.ID) - startKey, endKey := recordPrefix, recordPrefix.PrefixNext() - keyRanges, err := t.splitRawKeyRanges(ctx, store, startKey, endKey, splitCnt) - if err != nil { - return nil, err - } - - if len(keyRanges) <= 1 { - return []ScanRange{newFullRange()}, nil - } - - scanRanges := make([]ScanRange, 0, len(keyRanges)) - curScanStart := nullDatum() - for i, keyRange := range keyRanges { - if i != 0 && curScanStart.IsNull() { - break - } - - curScanEnd := nullDatum() - if i != len(keyRanges)-1 { - curScanEnd = GetNextBytesHandleDatum(keyRange.EndKey, recordPrefix) - } - - if !curScanStart.IsNull() && !curScanEnd.IsNull() && kv.Key(curScanStart.GetBytes()).Cmp(curScanEnd.GetBytes()) >= 0 { - continue - } - - scanRanges = append(scanRanges, newDatumRange(curScanStart, curScanEnd)) - curScanStart = curScanEnd - } - return scanRanges, nil -} - -func (t *PhysicalTable) splitRawKeyRanges(ctx context.Context, store tikv.Storage, - startKey, endKey kv.Key, splitCnt int) ([]kv.KeyRange, error) { - regionCache := store.GetRegionCache() - regionIDs, err := regionCache.ListRegionIDsInKeyRange( - tikv.NewBackofferWithVars(ctx, 20000, nil), startKey, endKey) - if err != nil { - return nil, err - } - - regionsPerRange := len(regionIDs) / splitCnt - oversizeCnt := len(regionIDs) % splitCnt - ranges := make([]kv.KeyRange, 0, mathutil.Min(len(regionIDs), splitCnt)) - for len(regionIDs) > 0 { - startRegion, err := regionCache.LocateRegionByID(tikv.NewBackofferWithVars(ctx, 20000, nil), - regionIDs[0]) - if err != nil { - return nil, err - } - - endRegionIdx := regionsPerRange - 1 - if oversizeCnt > 0 { - endRegionIdx++ - } - - endRegion, err := regionCache.LocateRegionByID(tikv.NewBackofferWithVars(ctx, 20000, nil), - regionIDs[endRegionIdx]) - if err != nil { - return nil, err - } - - rangeStartKey := kv.Key(startRegion.StartKey) - if rangeStartKey.Cmp(startKey) < 0 { - rangeStartKey = startKey - } - - rangeEndKey := kv.Key(endRegion.EndKey) - if rangeEndKey.Cmp(endKey) > 0 { - rangeEndKey = endKey - } - - ranges = append(ranges, kv.KeyRange{StartKey: rangeStartKey, EndKey: rangeEndKey}) - oversizeCnt-- - regionIDs = regionIDs[endRegionIdx+1:] - } - return ranges, nil -} - -var emptyBytesHandleKey kv.Key - -func init() { - key, err := codec.EncodeKey(nil, nil, types.NewBytesDatum(nil)) - terror.MustNil(err) - emptyBytesHandleKey = key -} - -// GetNextIntHandle is used for int handle tables. -// It returns the min handle whose encoded key is or after argument `key` -// If it cannot find a valid value, a null datum will be returned. -func GetNextIntHandle(key kv.Key, recordPrefix []byte) kv.Handle { - if key.Cmp(recordPrefix) > 0 && !key.HasPrefix(recordPrefix) { - return nil - } - - if key.Cmp(recordPrefix) <= 0 { - return kv.IntHandle(math.MinInt64) - } - - suffix := key[len(recordPrefix):] - encodedVal := suffix - if len(suffix) < 8 { - encodedVal = make([]byte, 8) - copy(encodedVal, suffix) - } - - findNext := false - if len(suffix) > 8 { - findNext = true - encodedVal = encodedVal[:8] - } - - u := codec.DecodeCmpUintToInt(binary.BigEndian.Uint64(encodedVal)) - if !findNext { - return kv.IntHandle(u) - } - - if u == math.MaxInt64 { - return nil - } - - return kv.IntHandle(u + 1) -} - -// GetNextBytesHandleDatum is used for a table with one binary or string column common handle. -// It returns the minValue whose encoded key is or after argument `key` -// If it cannot find a valid value, a null datum will be returned. -func GetNextBytesHandleDatum(key kv.Key, recordPrefix []byte) (d types.Datum) { - if key.Cmp(recordPrefix) > 0 && !key.HasPrefix(recordPrefix) { - d.SetNull() - return d - } - - if key.Cmp(recordPrefix) <= 0 { - d.SetBytes([]byte{}) - return d - } - - encodedVal := key[len(recordPrefix):] - if encodedVal[0] < emptyBytesHandleKey[0] { - d.SetBytes([]byte{}) - return d - } - - if encodedVal[0] > emptyBytesHandleKey[0] { - d.SetNull() - return d - } - - if remain, v, err := codec.DecodeOne(encodedVal); err == nil { - if len(remain) > 0 { - v.SetBytes(kv.Key(v.GetBytes()).Next()) - } - return v - } - - encodedVal = encodedVal[1:] - brokenGroupEndIdx := len(encodedVal) - 1 - brokenGroupEmptyBytes := len(encodedVal) % 9 - for i := 7; i+1 < len(encodedVal); i += 9 { - if emptyBytes := 255 - int(encodedVal[i+1]); emptyBytes != 0 || i+1 == len(encodedVal)-1 { - brokenGroupEndIdx = i - brokenGroupEmptyBytes = emptyBytes - break - } - } - - for i := 0; i < brokenGroupEmptyBytes; i++ { - if encodedVal[brokenGroupEndIdx] > 0 { - break - } - brokenGroupEndIdx-- - } - - if brokenGroupEndIdx < 0 { - d.SetBytes(nil) - return d - } - - val := make([]byte, 0, len(encodedVal)) - for i := 0; i <= brokenGroupEndIdx; i++ { - if i%9 == 8 { - continue - } - val = append(val, encodedVal[i]) - } - d.SetBytes(val) - return d -} diff --git a/ttl/cache/table_test.go b/ttl/cache/table_test.go deleted file mode 100644 index e6fc764b4503e..0000000000000 --- a/ttl/cache/table_test.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cache_test - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/stretchr/testify/require" -) - -func TestNewTTLTable(t *testing.T) { - cases := []struct { - db string - tbl string - def string - timeCol string - keyCols []string - }{ - { - db: "test", - tbl: "t1", - def: "(a int)", - }, - { - db: "test", - tbl: "ttl1", - def: "(a int, t datetime) ttl = `t` + interval 2 hour", - timeCol: "t", - keyCols: []string{"_tidb_rowid"}, - }, - { - db: "test", - tbl: "ttl2", - def: "(id int primary key, t datetime) ttl = `t` + interval 3 hour", - timeCol: "t", - keyCols: []string{"id"}, - }, - { - db: "test", - tbl: "ttl3", - def: "(a int, b varchar(32), c binary(32), t datetime, primary key (a, b, c)) ttl = `t` + interval 1 month", - timeCol: "t", - keyCols: []string{"a", "b", "c"}, - }, - { - db: "test", - tbl: "ttl4", - def: "(id int primary key, t datetime) " + - "ttl = `t` + interval 1 day " + - "PARTITION BY RANGE (id) (" + - " PARTITION p0 VALUES LESS THAN (10)," + - " PARTITION p1 VALUES LESS THAN (100)," + - " PARTITION p2 VALUES LESS THAN (1000)," + - " PARTITION p3 VALUES LESS THAN MAXVALUE)", - timeCol: "t", - keyCols: []string{"id"}, - }, - { - db: "test", - tbl: "ttl5", - def: "(id int primary key nonclustered, t datetime) ttl = `t` + interval 3 hour", - timeCol: "t", - keyCols: []string{"_tidb_rowid"}, - }, - } - - store, do := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - - for _, c := range cases { - tk.MustExec("use " + c.db) - tk.MustExec("create table " + c.tbl + c.def) - } - - for _, c := range cases { - is := do.InfoSchema() - tbl, err := is.TableByName(model.NewCIStr(c.db), model.NewCIStr(c.tbl)) - require.NoError(t, err) - tblInfo := tbl.Meta() - var physicalTbls []*cache.PhysicalTable - if tblInfo.Partition == nil { - ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr(c.db), tblInfo, model.NewCIStr("")) - if c.timeCol == "" { - require.Error(t, err) - continue - } - require.NoError(t, err) - physicalTbls = append(physicalTbls, ttlTbl) - } else { - for _, partition := range tblInfo.Partition.Definitions { - ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr(c.db), tblInfo, partition.Name) - if c.timeCol == "" { - require.Error(t, err) - continue - } - require.NoError(t, err) - physicalTbls = append(physicalTbls, ttlTbl) - } - if c.timeCol == "" { - continue - } - } - - for i, ttlTbl := range physicalTbls { - require.Equal(t, c.db, ttlTbl.Schema.O) - require.Same(t, tblInfo, ttlTbl.TableInfo) - timeColumn := tblInfo.FindPublicColumnByName(c.timeCol) - require.NotNil(t, timeColumn) - require.Same(t, timeColumn, ttlTbl.TimeColumn) - - if tblInfo.Partition == nil { - require.Equal(t, ttlTbl.TableInfo.ID, ttlTbl.ID) - require.Equal(t, "", ttlTbl.Partition.L) - require.Nil(t, ttlTbl.PartitionDef) - } else { - def := tblInfo.Partition.Definitions[i] - require.Equal(t, def.ID, ttlTbl.ID) - require.Equal(t, def.Name.L, ttlTbl.Partition.L) - require.Equal(t, def, *(ttlTbl.PartitionDef)) - } - - require.Equal(t, len(c.keyCols), len(ttlTbl.KeyColumns)) - require.Equal(t, len(c.keyCols), len(ttlTbl.KeyColumnTypes)) - - for j, keyCol := range c.keyCols { - msg := fmt.Sprintf("%s, col: %s", c.tbl, keyCol) - var col *model.ColumnInfo - if keyCol == model.ExtraHandleName.L { - col = model.NewExtraHandleColInfo() - } else { - col = tblInfo.FindPublicColumnByName(keyCol) - } - colJ := ttlTbl.KeyColumns[j] - colFieldJ := ttlTbl.KeyColumnTypes[j] - - require.NotNil(t, col, msg) - require.Equal(t, col.ID, colJ.ID, msg) - require.Equal(t, col.Name.L, colJ.Name.L, msg) - require.Equal(t, col.FieldType, colJ.FieldType, msg) - require.Equal(t, col.FieldType, *colFieldJ, msg) - } - } - } -} - -func TestEvalTTLExpireTime(t *testing.T) { - store, do := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("create table test.t(a int, t datetime) ttl = `t` + interval 1 day") - tk.MustExec("create table test.t2(a int, t datetime) ttl = `t` + interval 3 month") - - tb, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - tblInfo := tb.Meta() - ttlTbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo, model.NewCIStr("")) - require.NoError(t, err) - - tb2, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t2")) - require.NoError(t, err) - tblInfo2 := tb2.Meta() - ttlTbl2, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo2, model.NewCIStr("")) - require.NoError(t, err) - - se := session.NewSession(tk.Session(), tk.Session(), nil) - - now := time.UnixMilli(0) - tz1, err := time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - tz2, err := time.LoadLocation("Europe/Berlin") - require.NoError(t, err) - - se.GetSessionVars().TimeZone = tz1 - tm, err := ttlTbl.EvalExpireTime(context.TODO(), se, now) - require.NoError(t, err) - require.Equal(t, now.Add(-time.Hour*24).Unix(), tm.Unix()) - require.Equal(t, "1969-12-31 08:00:00", tm.Format(time.DateTime)) - require.Equal(t, tz1.String(), tm.Location().String()) - - se.GetSessionVars().TimeZone = tz2 - tm, err = ttlTbl.EvalExpireTime(context.TODO(), se, now) - require.NoError(t, err) - require.Equal(t, now.Add(-time.Hour*24).Unix(), tm.Unix()) - require.Equal(t, "1969-12-31 01:00:00", tm.Format(time.DateTime)) - require.Equal(t, tz2.String(), tm.Location().String()) - - se.GetSessionVars().TimeZone = tz1 - tm, err = ttlTbl2.EvalExpireTime(context.TODO(), se, now) - require.NoError(t, err) - require.Equal(t, "1969-10-01 08:00:00", tm.Format(time.DateTime)) - require.Equal(t, tz1.String(), tm.Location().String()) - - se.GetSessionVars().TimeZone = tz2 - tm, err = ttlTbl2.EvalExpireTime(context.TODO(), se, now) - require.NoError(t, err) - require.Equal(t, "1969-10-01 01:00:00", tm.Format(time.DateTime)) - require.Equal(t, tz2.String(), tm.Location().String()) -} diff --git a/ttl/cache/task.go b/ttl/cache/task.go deleted file mode 100644 index eae212fc8aab9..0000000000000 --- a/ttl/cache/task.go +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package cache - -import ( - "encoding/json" - "time" - - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" -) - -const selectFromTTLTask = `SELECT LOW_PRIORITY - job_id, - table_id, - scan_id, - scan_range_start, - scan_range_end, - expire_time, - owner_id, - owner_addr, - owner_hb_time, - status, - status_update_time, - state, - created_time FROM mysql.tidb_ttl_task` -const insertIntoTTLTask = `INSERT LOW_PRIORITY INTO mysql.tidb_ttl_task SET - job_id = %?, - table_id = %?, - scan_id = %?, - scan_range_start = %?, - scan_range_end = %?, - expire_time = %?, - created_time = %?` - -// SelectFromTTLTaskWithJobID returns an SQL statement to get all tasks of the specified job in mysql.tidb_ttl_task -func SelectFromTTLTaskWithJobID(jobID string) (string, []interface{}) { - return selectFromTTLTask + " WHERE job_id = %?", []interface{}{jobID} -} - -// SelectFromTTLTaskWithID returns an SQL statement to get all tasks of the specified job -// and scanID in mysql.tidb_ttl_task -func SelectFromTTLTaskWithID(jobID string, scanID int64) (string, []interface{}) { - return selectFromTTLTask + " WHERE job_id = %? AND scan_id = %?", []interface{}{jobID, scanID} -} - -// PeekWaitingTTLTask returns an SQL statement to get `limit` waiting ttl task -func PeekWaitingTTLTask(hbExpire time.Time) (string, []interface{}) { - return selectFromTTLTask + - " WHERE status = 'waiting' OR (owner_hb_time < %? AND status = 'running') ORDER BY created_time ASC", - []interface{}{hbExpire.Format(time.DateTime)} -} - -// InsertIntoTTLTask returns an SQL statement to insert a ttl task into mysql.tidb_ttl_task -func InsertIntoTTLTask(sctx sessionctx.Context, jobID string, tableID int64, scanID int, scanRangeStart []types.Datum, - scanRangeEnd []types.Datum, expireTime time.Time, createdTime time.Time) (string, []interface{}, error) { - rangeStart, err := codec.EncodeKey(sctx.GetSessionVars().StmtCtx, []byte{}, scanRangeStart...) - if err != nil { - return "", nil, err - } - rangeEnd, err := codec.EncodeKey(sctx.GetSessionVars().StmtCtx, []byte{}, scanRangeEnd...) - if err != nil { - return "", nil, err - } - return insertIntoTTLTask, []interface{}{jobID, tableID, int64(scanID), - rangeStart, rangeEnd, expireTime, createdTime}, nil -} - -// TaskStatus represents the current status of a task -type TaskStatus string - -const ( - // TaskStatusWaiting means the task hasn't started - TaskStatusWaiting TaskStatus = "waiting" - // TaskStatusRunning means this task is running - TaskStatusRunning = "running" - // TaskStatusFinished means this task has finished - TaskStatusFinished = "finished" -) - -// TTLTask is a row recorded in mysql.tidb_ttl_task -type TTLTask struct { - JobID string - TableID int64 - ScanID int64 - ScanRangeStart []types.Datum - ScanRangeEnd []types.Datum - ExpireTime time.Time - OwnerID string - OwnerAddr string - OwnerHBTime time.Time - Status TaskStatus - StatusUpdateTime time.Time - State *TTLTaskState - CreatedTime time.Time -} - -// TTLTaskState records the internal states of the ttl task -type TTLTaskState struct { - TotalRows uint64 `json:"total_rows"` - SuccessRows uint64 `json:"success_rows"` - ErrorRows uint64 `json:"error_rows"` - - ScanTaskErr string `json:"scan_task_err"` -} - -// RowToTTLTask converts a row into TTL task -func RowToTTLTask(sctx sessionctx.Context, row chunk.Row) (*TTLTask, error) { - var err error - timeZone := sctx.GetSessionVars().Location() - - task := &TTLTask{ - JobID: row.GetString(0), - TableID: row.GetInt64(1), - ScanID: row.GetInt64(2), - } - if !row.IsNull(3) { - scanRangeStartBuf := row.GetBytes(3) - // it's still posibble to be empty even this column is not NULL - if len(scanRangeStartBuf) > 0 { - task.ScanRangeStart, err = codec.Decode(scanRangeStartBuf, len(scanRangeStartBuf)) - if err != nil { - return nil, err - } - } - } - if !row.IsNull(4) { - scanRangeEndBuf := row.GetBytes(4) - // it's still posibble to be empty even this column is not NULL - if len(scanRangeEndBuf) > 0 { - task.ScanRangeEnd, err = codec.Decode(scanRangeEndBuf, len(scanRangeEndBuf)) - if err != nil { - return nil, err - } - } - } - - task.ExpireTime, err = row.GetTime(5).GoTime(timeZone) - if err != nil { - return nil, err - } - - if !row.IsNull(6) { - task.OwnerID = row.GetString(6) - } - if !row.IsNull(7) { - task.OwnerAddr = row.GetString(7) - } - if !row.IsNull(8) { - task.OwnerHBTime, err = row.GetTime(8).GoTime(timeZone) - if err != nil { - return nil, err - } - } - if !row.IsNull(9) { - status := row.GetString(9) - if len(status) == 0 { - status = "waiting" - } - task.Status = TaskStatus(status) - } - if !row.IsNull(10) { - task.StatusUpdateTime, err = row.GetTime(10).GoTime(timeZone) - if err != nil { - return nil, err - } - } - if !row.IsNull(11) { - stateStr := row.GetString(11) - state := &TTLTaskState{} - err = json.Unmarshal([]byte(stateStr), state) - if err != nil { - return nil, err - } - task.State = state - } - - task.CreatedTime, err = row.GetTime(12).GoTime(timeZone) - if err != nil { - return nil, err - } - - return task, nil -} diff --git a/ttl/client/BUILD.bazel b/ttl/client/BUILD.bazel deleted file mode 100644 index 8c558b724abd6..0000000000000 --- a/ttl/client/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "client", - srcs = [ - "command.go", - "notification.go", - ], - importpath = "github.com/pingcap/tidb/ttl/client", - visibility = ["//visibility:public"], - deps = [ - "//ddl/util", - "//util/logutil", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_errors//:errors", - "@io_etcd_go_etcd_client_v3//:client", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "client_test", - timeout = "short", - srcs = ["command_test.go"], - embed = [":client"], - flaky = True, - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_tests_v3//integration", - ], -) diff --git a/ttl/metrics/BUILD.bazel b/ttl/metrics/BUILD.bazel deleted file mode 100644 index 85e31cf8dff57..0000000000000 --- a/ttl/metrics/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/ttl/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) - -go_test( - name = "metrics_test", - timeout = "short", - srcs = ["metrics_test.go"], - embed = [":metrics"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/ttl/metrics/metrics.go b/ttl/metrics/metrics.go deleted file mode 100644 index b1477c7d63887..0000000000000 --- a/ttl/metrics/metrics.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package metrics - -import ( - "context" - "math" - "time" - - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// Phases to trace -var ( - PhaseIdle = "idle" - PhaseBeginTxn = "begin_txn" - PhaseCommitTxn = "commit_txn" - PhaseQuery = "query" - PhaseCheckTTL = "check_ttl" - PhaseWaitRetry = "wait_retry" - PhaseDispatch = "dispatch" - PhaseWaitToken = "wait_token" - PhaseOther = "other" -) - -// TTL metrics -var ( - SelectSuccessDuration prometheus.Observer - SelectErrorDuration prometheus.Observer - DeleteSuccessDuration prometheus.Observer - DeleteErrorDuration prometheus.Observer - - ScannedExpiredRows prometheus.Counter - DeleteSuccessExpiredRows prometheus.Counter - DeleteErrorExpiredRows prometheus.Counter - - RunningJobsCnt prometheus.Gauge - CancellingJobsCnt prometheus.Gauge - - ScanningTaskCnt prometheus.Gauge - DeletingTaskCnt prometheus.Gauge - - WaterMarkScheduleDelayNames = []struct { - Name string - Delay time.Duration - }{ - { - Name: "01 hour", - Delay: time.Hour, - }, - { - Name: "02 hour", - Delay: time.Hour, - }, - { - Name: "06 hour", - Delay: 6 * time.Hour, - }, - { - Name: "12 hour", - Delay: 12 * time.Hour, - }, - { - Name: "24 hour", - Delay: 24 * time.Hour, - }, - { - Name: "72 hour", - Delay: 72 * time.Hour, - }, - { - Name: "one week", - Delay: 72 * time.Hour, - }, - { - Name: "others", - Delay: math.MaxInt64, - }, - } -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init ttl metrics vars vars. -func InitMetricsVars() { - SelectSuccessDuration = metrics.TTLQueryDuration.With( - prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblOK}) - SelectErrorDuration = metrics.TTLQueryDuration.With( - prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblError}) - DeleteSuccessDuration = metrics.TTLQueryDuration.With( - prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblOK}) - DeleteErrorDuration = metrics.TTLQueryDuration.With( - prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblError}) - - ScannedExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( - prometheus.Labels{metrics.LblSQLType: "select", metrics.LblResult: metrics.LblOK}) - DeleteSuccessExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( - prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblOK}) - DeleteErrorExpiredRows = metrics.TTLProcessedExpiredRowsCounter.With( - prometheus.Labels{metrics.LblSQLType: "delete", metrics.LblResult: metrics.LblError}) - - RunningJobsCnt = metrics.TTLJobStatus.With(prometheus.Labels{metrics.LblType: "running"}) - CancellingJobsCnt = metrics.TTLJobStatus.With(prometheus.Labels{metrics.LblType: "cancelling"}) - - ScanningTaskCnt = metrics.TTLTaskStatus.With(prometheus.Labels{metrics.LblType: "scanning"}) - DeletingTaskCnt = metrics.TTLTaskStatus.With(prometheus.Labels{metrics.LblType: "deleting"}) - - scanWorkerPhases = initWorkerPhases("scan_worker") - deleteWorkerPhases = initWorkerPhases("delete_worker") -} - -func initWorkerPhases(workerType string) map[string]prometheus.Counter { - return map[string]prometheus.Counter{ - PhaseIdle: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseIdle), - PhaseBeginTxn: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseBeginTxn), - PhaseCommitTxn: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseCommitTxn), - PhaseQuery: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseQuery), - PhaseWaitRetry: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseWaitRetry), - PhaseDispatch: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseDispatch), - PhaseCheckTTL: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseCheckTTL), - PhaseWaitToken: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseWaitToken), - PhaseOther: metrics.TTLPhaseTime.WithLabelValues(workerType, PhaseOther), - } -} - -var scanWorkerPhases map[string]prometheus.Counter -var deleteWorkerPhases map[string]prometheus.Counter - -// PhaseTracer is used to tracer the phases duration -type PhaseTracer struct { - getTime func() time.Time - recordDuration func(phase string, duration time.Duration) - - phase string - phaseTime time.Time -} - -// NewScanWorkerPhaseTracer returns a tracer for scan worker -func NewScanWorkerPhaseTracer() *PhaseTracer { - return newPhaseTracer(time.Now, func(status string, duration time.Duration) { - if counter, ok := scanWorkerPhases[status]; ok { - counter.Add(duration.Seconds()) - } - }) -} - -// NewDeleteWorkerPhaseTracer returns a tracer for delete worker -func NewDeleteWorkerPhaseTracer() *PhaseTracer { - return newPhaseTracer(time.Now, func(status string, duration time.Duration) { - if counter, ok := deleteWorkerPhases[status]; ok { - counter.Add(duration.Seconds()) - } - }) -} - -func newPhaseTracer(getTime func() time.Time, recordDuration func(status string, duration time.Duration)) *PhaseTracer { - return &PhaseTracer{ - getTime: getTime, - recordDuration: recordDuration, - phaseTime: getTime(), - } -} - -// Phase returns the current phase -func (t *PhaseTracer) Phase() string { - if t == nil { - return "" - } - return t.phase -} - -// EnterPhase enters into a new phase -func (t *PhaseTracer) EnterPhase(phase string) { - if t == nil { - return - } - - now := t.getTime() - if t.phase != "" { - t.recordDuration(t.phase, now.Sub(t.phaseTime)) - } - - t.phase = phase - t.phaseTime = now -} - -// EndPhase ends the current phase -func (t *PhaseTracer) EndPhase() { - if t == nil { - return - } - t.EnterPhase("") -} - -type ttlPhaseTraceKey struct{} - -// CtxWithPhaseTracer create a new context with tracer -func CtxWithPhaseTracer(ctx context.Context, tracer *PhaseTracer) context.Context { - return context.WithValue(ctx, ttlPhaseTraceKey{}, tracer) -} - -// PhaseTracerFromCtx returns a tracer from a given context -func PhaseTracerFromCtx(ctx context.Context) *PhaseTracer { - if tracer, ok := ctx.Value(ttlPhaseTraceKey{}).(*PhaseTracer); ok { - return tracer - } - return nil -} - -// DelayMetricsRecord is the delay metric record for a table -type DelayMetricsRecord struct { - TableID int64 - LastJobTime time.Time - AbsoluteDelay time.Duration - ScheduleRelativeDelay time.Duration -} - -func getWaterMarkScheduleDelayName(t time.Duration) string { - for _, l := range WaterMarkScheduleDelayNames { - if t <= l.Delay { - return l.Name - } - } - return WaterMarkScheduleDelayNames[len(WaterMarkScheduleDelayNames)-1].Name -} - -// UpdateDelayMetrics updates the metrics of TTL delay -func UpdateDelayMetrics(records map[int64]*DelayMetricsRecord) { - scheduleMetrics := make(map[string]float64, len(WaterMarkScheduleDelayNames)) - for _, l := range WaterMarkScheduleDelayNames { - scheduleMetrics[l.Name] = 0 - } - - for _, r := range records { - name := getWaterMarkScheduleDelayName(r.ScheduleRelativeDelay) - scheduleMetrics[name] = scheduleMetrics[name] + 1 - } - - for delay, v := range scheduleMetrics { - metrics.TTLWatermarkDelay.With(prometheus.Labels{metrics.LblType: "schedule", metrics.LblName: delay}).Set(v) - } -} diff --git a/ttl/session/BUILD.bazel b/ttl/session/BUILD.bazel deleted file mode 100644 index ced8b7727f994..0000000000000 --- a/ttl/session/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "session", - srcs = ["session.go"], - importpath = "github.com/pingcap/tidb/ttl/session", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//sessiontxn", - "//ttl/metrics", - "//util/chunk", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "session_test", - timeout = "short", - srcs = [ - "main_test.go", - "session_test.go", - "sysvar_test.go", - ], - flaky = True, - shard_count = 6, - deps = [ - ":session", - "//sessionctx/variable", - "//testkit", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ttl/session/main_test.go b/ttl/session/main_test.go deleted file mode 100644 index d6c93194f026a..0000000000000 --- a/ttl/session/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package session_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ttl/session/session.go b/ttl/session/session.go deleted file mode 100644 index a413cdadf03ff..0000000000000 --- a/ttl/session/session.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package session - -import ( - "context" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/sessiontxn" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/sqlexec" -) - -// TxnMode represents using optimistic or pessimistic mode in the transaction -type TxnMode int - -const ( - // TxnModeOptimistic means using the optimistic transaction with "BEGIN OPTIMISTIC" - TxnModeOptimistic TxnMode = iota - // TxnModePessimistic means using the pessimistic transaction with "BEGIN PESSIMISTIC" - TxnModePessimistic -) - -// Session is used to execute queries for TTL case -type Session interface { - sessionctx.Context - // SessionInfoSchema returns information schema of current session - SessionInfoSchema() infoschema.InfoSchema - // ExecuteSQL executes the sql - ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) - // RunInTxn executes the specified function in a txn - RunInTxn(ctx context.Context, fn func() error, mode TxnMode) (err error) - // ResetWithGlobalTimeZone resets the session time zone to global time zone - ResetWithGlobalTimeZone(ctx context.Context) error - // Close closes the session - Close() - // Now returns the current time in location specified by session var - Now() time.Time -} - -type session struct { - sessionctx.Context - sqlExec sqlexec.SQLExecutor - closeFn func(Session) -} - -// NewSession creates a new Session -func NewSession(sctx sessionctx.Context, sqlExec sqlexec.SQLExecutor, closeFn func(Session)) Session { - return &session{ - Context: sctx, - sqlExec: sqlExec, - closeFn: closeFn, - } -} - -// SessionInfoSchema returns information schema of current session -func (s *session) SessionInfoSchema() infoschema.InfoSchema { - if s.Context == nil { - return nil - } - return sessiontxn.GetTxnManager(s.Context).GetTxnInfoSchema() -} - -// ExecuteSQL executes the sql -func (s *session) ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) { - if s.sqlExec == nil { - return nil, errors.New("session is closed") - } - - ctx = kv.WithInternalSourceType(ctx, kv.InternalTxnTTL) - rs, err := s.sqlExec.ExecuteInternal(ctx, sql, args...) - if err != nil { - return nil, err - } - - if rs == nil { - return nil, nil - } - - defer func() { - terror.Log(rs.Close()) - }() - - return sqlexec.DrainRecordSet(ctx, rs, 8) -} - -// RunInTxn executes the specified function in a txn -func (s *session) RunInTxn(ctx context.Context, fn func() error, txnMode TxnMode) (err error) { - tracer := metrics.PhaseTracerFromCtx(ctx) - defer tracer.EnterPhase(tracer.Phase()) - - tracer.EnterPhase(metrics.PhaseBeginTxn) - sql := "BEGIN " - switch txnMode { - case TxnModeOptimistic: - sql += "OPTIMISTIC" - case TxnModePessimistic: - sql += "PESSIMISTIC" - default: - return errors.New("unknown transaction mode") - } - if _, err = s.ExecuteSQL(ctx, sql); err != nil { - return err - } - tracer.EnterPhase(metrics.PhaseOther) - - success := false - defer func() { - if !success { - _, rollbackErr := s.ExecuteSQL(ctx, "ROLLBACK") - terror.Log(rollbackErr) - } - }() - - if err = fn(); err != nil { - return err - } - - tracer.EnterPhase(metrics.PhaseCommitTxn) - if _, err = s.ExecuteSQL(ctx, "COMMIT"); err != nil { - return err - } - tracer.EnterPhase(metrics.PhaseOther) - - success = true - return err -} - -// ResetWithGlobalTimeZone resets the session time zone to global time zone -func (s *session) ResetWithGlobalTimeZone(ctx context.Context) error { - sessVar := s.GetSessionVars() - if sessVar.TimeZone != nil { - globalTZ, err := sessVar.GetGlobalSystemVar(ctx, variable.TimeZone) - if err != nil { - return err - } - - tz, err := sessVar.GetSessionOrGlobalSystemVar(ctx, variable.TimeZone) - if err != nil { - return err - } - - if globalTZ == tz { - return nil - } - } - - _, err := s.ExecuteSQL(ctx, "SET @@time_zone=@@global.time_zone") - return err -} - -// Close closes the session -func (s *session) Close() { - if s.closeFn != nil { - s.closeFn(s) - s.Context = nil - s.sqlExec = nil - s.closeFn = nil - } -} - -// Now returns the current time in the location of time_zone session var -func (s *session) Now() time.Time { - return time.Now().In(s.Context.GetSessionVars().Location()) -} diff --git a/ttl/session/session_test.go b/ttl/session/session_test.go deleted file mode 100644 index 607c7a2319342..0000000000000 --- a/ttl/session/session_test.go +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package session_test - -import ( - "context" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/session" - "github.com/stretchr/testify/require" -) - -func TestSessionRunInTxn(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - tk.MustExec("create table t(id int primary key, v int)") - se := session.NewSession(tk.Session(), tk.Session(), nil) - tk2 := testkit.NewTestKit(t, store) - tk2.MustExec("use test") - - require.NoError(t, se.RunInTxn(context.TODO(), func() error { - tk.MustExec("insert into t values (1, 10)") - return nil - }, session.TxnModeOptimistic)) - tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10")) - - err := se.RunInTxn(context.TODO(), func() error { - tk.MustExec("insert into t values (2, 20)") - return errors.New("mockErr") - }, session.TxnModeOptimistic) - require.EqualError(t, err, "mockErr") - tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10")) - - require.NoError(t, se.RunInTxn(context.TODO(), func() error { - tk.MustExec("insert into t values (3, 30)") - return nil - }, session.TxnModeOptimistic)) - tk2.MustQuery("select * from t order by id asc").Check(testkit.Rows("1 10", "3 30")) -} - -func TestSessionResetTimeZone(t *testing.T) { - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.time_zone='UTC'") - tk.MustExec("set @@time_zone='Asia/Shanghai'") - - se := session.NewSession(tk.Session(), tk.Session(), nil) - tk.MustQuery("select @@time_zone").Check(testkit.Rows("Asia/Shanghai")) - require.NoError(t, se.ResetWithGlobalTimeZone(context.TODO())) - tk.MustQuery("select @@time_zone").Check(testkit.Rows("UTC")) -} diff --git a/ttl/session/sysvar_test.go b/ttl/session/sysvar_test.go deleted file mode 100644 index 58f61c3cc88bb..0000000000000 --- a/ttl/session/sysvar_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package session_test - -import ( - "fmt" - "strconv" - "testing" - - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/testkit" - "github.com/stretchr/testify/require" -) - -func TestSysVarTTLJobEnable(t *testing.T) { - origEnableDDL := variable.EnableTTLJob.Load() - defer func() { - variable.EnableTTLJob.Store(origEnableDDL) - }() - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_ttl_job_enable=0") - require.False(t, variable.EnableTTLJob.Load()) - tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("0")) - tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("0")) - - tk.MustExec("set @@global.tidb_ttl_job_enable=1") - require.True(t, variable.EnableTTLJob.Load()) - tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("1")) - tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("1")) - - tk.MustExec("set @@global.tidb_ttl_job_enable=0") - require.False(t, variable.EnableTTLJob.Load()) - tk.MustQuery("select @@global.tidb_ttl_job_enable").Check(testkit.Rows("0")) - tk.MustQuery("select @@tidb_ttl_job_enable").Check(testkit.Rows("0")) -} - -func TestSysVarTTLScanBatchSize(t *testing.T) { - origScanBatchSize := variable.TTLScanBatchSize.Load() - defer func() { - variable.TTLScanBatchSize.Store(origScanBatchSize) - }() - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_ttl_scan_batch_size=789") - require.Equal(t, int64(789), variable.TTLScanBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows("789")) - tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows("789")) - - tk.MustExec("set @@global.tidb_ttl_scan_batch_size=0") - require.Equal(t, int64(1), variable.TTLScanBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows("1")) - tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows("1")) - - maxVal := int64(variable.DefTiDBTTLScanBatchMaxSize) - tk.MustExec(fmt.Sprintf("set @@global.tidb_ttl_scan_batch_size=%d", maxVal+1)) - require.Equal(t, maxVal, variable.TTLScanBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_scan_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) - tk.MustQuery("select @@tidb_ttl_scan_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) -} - -func TestSysVarTTLScanDeleteBatchSize(t *testing.T) { - origScanBatchSize := variable.TTLScanBatchSize.Load() - defer func() { - variable.TTLScanBatchSize.Store(origScanBatchSize) - }() - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("set @@global.tidb_ttl_delete_batch_size=789") - require.Equal(t, int64(789), variable.TTLDeleteBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows("789")) - tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows("789")) - - tk.MustExec("set @@global.tidb_ttl_delete_batch_size=0") - require.Equal(t, int64(1), variable.TTLDeleteBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows("1")) - tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows("1")) - - maxVal := int64(variable.DefTiDBTTLDeleteBatchMaxSize) - tk.MustExec(fmt.Sprintf("set @@global.tidb_ttl_delete_batch_size=%d", maxVal+1)) - require.Equal(t, maxVal, variable.TTLDeleteBatchSize.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) - tk.MustQuery("select @@tidb_ttl_delete_batch_size").Check(testkit.Rows(strconv.FormatInt(maxVal, 10))) -} - -func TestSysVarTTLScanDeleteLimit(t *testing.T) { - origDeleteLimit := variable.TTLDeleteRateLimit.Load() - defer func() { - variable.TTLDeleteRateLimit.Store(origDeleteLimit) - }() - - store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) - tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) - - tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=100000") - require.Equal(t, int64(100000), variable.TTLDeleteRateLimit.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("100000")) - tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("100000")) - - tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=0") - require.Equal(t, int64(0), variable.TTLDeleteRateLimit.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) - tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) - - tk.MustExec("set @@global.tidb_ttl_delete_rate_limit=-1") - require.Equal(t, int64(0), variable.TTLDeleteRateLimit.Load()) - tk.MustQuery("select @@global.tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) - tk.MustQuery("select @@tidb_ttl_delete_rate_limit").Check(testkit.Rows("0")) -} diff --git a/ttl/sqlbuilder/BUILD.bazel b/ttl/sqlbuilder/BUILD.bazel deleted file mode 100644 index 5eafc4a7748d3..0000000000000 --- a/ttl/sqlbuilder/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sqlbuilder", - srcs = ["sql.go"], - importpath = "github.com/pingcap/tidb/ttl/sqlbuilder", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//ttl/cache", - "//types", - "//util/sqlexec", - "@com_github_pkg_errors//:errors", - ], -) - -go_test( - name = "sqlbuilder_test", - timeout = "short", - srcs = [ - "main_test.go", - "sql_test.go", - ], - flaky = True, - shard_count = 5, - deps = [ - ":sqlbuilder", - "//kv", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//testkit", - "//testkit/testsetup", - "//ttl/cache", - "//types", - "//util/dbterror", - "//util/sqlexec", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/ttl/sqlbuilder/main_test.go b/ttl/sqlbuilder/main_test.go deleted file mode 100644 index 8198b07e6bdc5..0000000000000 --- a/ttl/sqlbuilder/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package sqlbuilder_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/ttl/sqlbuilder/sql.go b/ttl/sqlbuilder/sql.go deleted file mode 100644 index 0282a0da57724..0000000000000 --- a/ttl/sqlbuilder/sql.go +++ /dev/null @@ -1,482 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package sqlbuilder - -import ( - "encoding/hex" - "fmt" - "io" - "strconv" - "strings" - "time" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pkg/errors" -) - -func writeHex(in io.Writer, d types.Datum) error { - _, err := fmt.Fprintf(in, "x'%s'", hex.EncodeToString(d.GetBytes())) - return err -} - -func writeDatum(restoreCtx *format.RestoreCtx, d types.Datum, ft *types.FieldType) error { - switch ft.GetType() { - case mysql.TypeBit, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeTinyBlob: - return writeHex(restoreCtx.In, d) - case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeEnum, mysql.TypeSet: - if mysql.HasBinaryFlag(ft.GetFlag()) { - return writeHex(restoreCtx.In, d) - } - _, err := fmt.Fprintf(restoreCtx.In, "'%s'", sqlexec.EscapeString(d.GetString())) - return err - } - expr := ast.NewValueExpr(d.GetValue(), ft.GetCharset(), ft.GetCollate()) - return expr.Restore(restoreCtx) -} - -// FormatSQLDatum formats the datum to a value string in sql -func FormatSQLDatum(d types.Datum, ft *types.FieldType) (string, error) { - var sb strings.Builder - ctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) - if err := writeDatum(ctx, d, ft); err != nil { - return "", err - } - return sb.String(), nil -} - -type sqlBuilderState int - -const ( - writeBegin sqlBuilderState = iota - writeSelOrDel - writeWhere - writeOrderBy - writeLimit - writeDone -) - -// SQLBuilder is used to build SQLs for TTL -type SQLBuilder struct { - tbl *cache.PhysicalTable - sb strings.Builder - restoreCtx *format.RestoreCtx - state sqlBuilderState - - isReadOnly bool - hasWriteExpireCond bool -} - -// NewSQLBuilder creates a new TTLSQLBuilder -func NewSQLBuilder(tbl *cache.PhysicalTable) *SQLBuilder { - b := &SQLBuilder{tbl: tbl, state: writeBegin} - b.restoreCtx = format.NewRestoreCtx(format.DefaultRestoreFlags, &b.sb) - return b -} - -// Build builds the final sql -func (b *SQLBuilder) Build() (string, error) { - if b.state == writeBegin { - return "", errors.Errorf("invalid state: %v", b.state) - } - - if !b.isReadOnly && !b.hasWriteExpireCond { - // check whether the `timeRow < expire_time` condition has been written to make sure this SQL is safe. - return "", errors.New("expire condition not write") - } - - if b.state != writeDone { - b.state = writeDone - } - - return b.sb.String(), nil -} - -// WriteSelect writes a select statement to select key columns without any condition -func (b *SQLBuilder) WriteSelect() error { - if b.state != writeBegin { - return errors.Errorf("invalid state: %v", b.state) - } - b.restoreCtx.WritePlain("SELECT LOW_PRIORITY SQL_NO_CACHE ") - b.writeColNames(b.tbl.KeyColumns, false) - b.restoreCtx.WritePlain(" FROM ") - if err := b.writeTblName(); err != nil { - return err - } - if par := b.tbl.PartitionDef; par != nil { - b.restoreCtx.WritePlain(" PARTITION(") - b.restoreCtx.WriteName(par.Name.O) - b.restoreCtx.WritePlain(")") - } - b.state = writeSelOrDel - b.isReadOnly = true - return nil -} - -// WriteDelete writes a delete statement without any condition -func (b *SQLBuilder) WriteDelete() error { - if b.state != writeBegin { - return errors.Errorf("invalid state: %v", b.state) - } - b.restoreCtx.WritePlain("DELETE LOW_PRIORITY FROM ") - if err := b.writeTblName(); err != nil { - return err - } - if par := b.tbl.PartitionDef; par != nil { - b.restoreCtx.WritePlain(" PARTITION(") - b.restoreCtx.WriteName(par.Name.O) - b.restoreCtx.WritePlain(")") - } - b.state = writeSelOrDel - return nil -} - -// WriteCommonCondition writes a new condition -func (b *SQLBuilder) WriteCommonCondition(cols []*model.ColumnInfo, op string, dp []types.Datum) error { - switch b.state { - case writeSelOrDel: - b.restoreCtx.WritePlain(" WHERE ") - b.state = writeWhere - case writeWhere: - b.restoreCtx.WritePlain(" AND ") - default: - return errors.Errorf("invalid state: %v", b.state) - } - - b.writeColNames(cols, len(cols) > 1) - b.restoreCtx.WritePlain(" ") - b.restoreCtx.WritePlain(op) - b.restoreCtx.WritePlain(" ") - return b.writeDataPoint(cols, dp) -} - -// WriteExpireCondition writes a condition with the time column -func (b *SQLBuilder) WriteExpireCondition(expire time.Time) error { - switch b.state { - case writeSelOrDel: - b.restoreCtx.WritePlain(" WHERE ") - b.state = writeWhere - case writeWhere: - b.restoreCtx.WritePlain(" AND ") - default: - return errors.Errorf("invalid state: %v", b.state) - } - - b.writeColNames([]*model.ColumnInfo{b.tbl.TimeColumn}, false) - b.restoreCtx.WritePlain(" < ") - b.restoreCtx.WritePlain("FROM_UNIXTIME(") - b.restoreCtx.WritePlain(strconv.FormatInt(expire.Unix(), 10)) - b.restoreCtx.WritePlain(")") - b.hasWriteExpireCond = true - return nil -} - -// WriteInCondition writes an IN condition -func (b *SQLBuilder) WriteInCondition(cols []*model.ColumnInfo, dps ...[]types.Datum) error { - switch b.state { - case writeSelOrDel: - b.restoreCtx.WritePlain(" WHERE ") - b.state = writeWhere - case writeWhere: - b.restoreCtx.WritePlain(" AND ") - default: - return errors.Errorf("invalid state: %v", b.state) - } - - b.writeColNames(cols, len(cols) > 1) - b.restoreCtx.WritePlain(" IN ") - b.restoreCtx.WritePlain("(") - first := true - for _, v := range dps { - if first { - first = false - } else { - b.restoreCtx.WritePlain(", ") - } - if err := b.writeDataPoint(cols, v); err != nil { - return err - } - } - b.restoreCtx.WritePlain(")") - return nil -} - -// WriteOrderBy writes order by -func (b *SQLBuilder) WriteOrderBy(cols []*model.ColumnInfo, desc bool) error { - if b.state != writeSelOrDel && b.state != writeWhere { - return errors.Errorf("invalid state: %v", b.state) - } - b.state = writeOrderBy - b.restoreCtx.WritePlain(" ORDER BY ") - b.writeColNames(cols, false) - if desc { - b.restoreCtx.WritePlain(" DESC") - } else { - b.restoreCtx.WritePlain(" ASC") - } - return nil -} - -// WriteLimit writes the limit -func (b *SQLBuilder) WriteLimit(n int) error { - if b.state != writeSelOrDel && b.state != writeWhere && b.state != writeOrderBy { - return errors.Errorf("invalid state: %v", b.state) - } - b.state = writeLimit - b.restoreCtx.WritePlain(" LIMIT ") - b.restoreCtx.WritePlain(strconv.Itoa(n)) - return nil -} - -func (b *SQLBuilder) writeTblName() error { - tn := ast.TableName{Schema: b.tbl.Schema, Name: b.tbl.Name} - return tn.Restore(b.restoreCtx) -} - -func (b *SQLBuilder) writeColName(col *model.ColumnInfo) { - b.restoreCtx.WriteName(col.Name.O) -} - -func (b *SQLBuilder) writeColNames(cols []*model.ColumnInfo, writeBrackets bool) { - if writeBrackets { - b.restoreCtx.WritePlain("(") - } - - first := true - for _, col := range cols { - if first { - first = false - } else { - b.restoreCtx.WritePlain(", ") - } - b.writeColName(col) - } - - if writeBrackets { - b.restoreCtx.WritePlain(")") - } -} - -func (b *SQLBuilder) writeDataPoint(cols []*model.ColumnInfo, dp []types.Datum) error { - writeBrackets := len(cols) > 1 - if len(cols) != len(dp) { - return errors.Errorf("col count not match %d != %d", len(cols), len(dp)) - } - - if writeBrackets { - b.restoreCtx.WritePlain("(") - } - - first := true - for i, d := range dp { - if first { - first = false - } else { - b.restoreCtx.WritePlain(", ") - } - if err := writeDatum(b.restoreCtx, d, &cols[i].FieldType); err != nil { - return err - } - } - - if writeBrackets { - b.restoreCtx.WritePlain(")") - } - - return nil -} - -// ScanQueryGenerator generates SQLs for scan task -type ScanQueryGenerator struct { - tbl *cache.PhysicalTable - expire time.Time - keyRangeStart []types.Datum - keyRangeEnd []types.Datum - stack [][]types.Datum - limit int - firstBuild bool - exhausted bool -} - -// NewScanQueryGenerator creates a new ScanQueryGenerator -func NewScanQueryGenerator(tbl *cache.PhysicalTable, expire time.Time, - rangeStart, rangeEnd []types.Datum) (*ScanQueryGenerator, error) { - if err := tbl.ValidateKeyPrefix(rangeStart); err != nil { - return nil, err - } - - if err := tbl.ValidateKeyPrefix(rangeEnd); err != nil { - return nil, err - } - - return &ScanQueryGenerator{ - tbl: tbl, - expire: expire, - keyRangeStart: rangeStart, - keyRangeEnd: rangeEnd, - firstBuild: true, - }, nil -} - -// NextSQL creates next sql of the scan task -func (g *ScanQueryGenerator) NextSQL(continueFromResult [][]types.Datum, nextLimit int) (string, error) { - if g.exhausted { - return "", errors.New("generator is exhausted") - } - - if nextLimit <= 0 { - return "", errors.Errorf("invalid limit '%d'", nextLimit) - } - - defer func() { - g.firstBuild = false - }() - - if g.stack == nil { - g.stack = make([][]types.Datum, 0, len(g.tbl.KeyColumns)) - } - - if len(continueFromResult) >= g.limit { - var continueFromKey []types.Datum - if cnt := len(continueFromResult); cnt > 0 { - continueFromKey = continueFromResult[cnt-1] - } - if err := g.setStack(continueFromKey); err != nil { - return "", err - } - } else { - if l := len(g.stack); l > 0 { - g.stack = g.stack[:l-1] - } - if len(g.stack) == 0 { - g.exhausted = true - } - } - g.limit = nextLimit - return g.buildSQL() -} - -// IsExhausted returns whether the generator is exhausted -func (g *ScanQueryGenerator) IsExhausted() bool { - return g.exhausted -} - -func (g *ScanQueryGenerator) setStack(key []types.Datum) error { - if key == nil { - key = g.keyRangeStart - } - - if key == nil { - g.stack = g.stack[:0] - return nil - } - - if err := g.tbl.ValidateKeyPrefix(key); err != nil { - return err - } - - g.stack = g.stack[:len(key)] - for i := 0; i < len(key); i++ { - g.stack[i] = key[0 : i+1] - } - return nil -} - -func (g *ScanQueryGenerator) buildSQL() (string, error) { - if g.limit <= 0 { - return "", errors.Errorf("invalid limit '%d'", g.limit) - } - - if g.exhausted { - return "", nil - } - - b := NewSQLBuilder(g.tbl) - if err := b.WriteSelect(); err != nil { - return "", err - } - if len(g.stack) > 0 { - for i, d := range g.stack[len(g.stack)-1] { - col := []*model.ColumnInfo{g.tbl.KeyColumns[i]} - val := []types.Datum{d} - var err error - if i < len(g.stack)-1 { - err = b.WriteCommonCondition(col, "=", val) - } else if g.firstBuild { - // When `g.firstBuild == true`, that means we are querying rows after range start, because range is defined - // as [start, end), we should use ">=" to find the rows including start key. - err = b.WriteCommonCondition(col, ">=", val) - } else { - // Otherwise when `g.firstBuild != true`, that means we are continuing with the previous result, we should use - // ">" to exclude the previous row. - err = b.WriteCommonCondition(col, ">", val) - } - if err != nil { - return "", err - } - } - } - - if len(g.keyRangeEnd) > 0 { - if err := b.WriteCommonCondition(g.tbl.KeyColumns[0:len(g.keyRangeEnd)], "<", g.keyRangeEnd); err != nil { - return "", err - } - } - - if err := b.WriteExpireCondition(g.expire); err != nil { - return "", err - } - - if err := b.WriteOrderBy(g.tbl.KeyColumns, false); err != nil { - return "", err - } - - if err := b.WriteLimit(g.limit); err != nil { - return "", err - } - - return b.Build() -} - -// BuildDeleteSQL builds a delete SQL -func BuildDeleteSQL(tbl *cache.PhysicalTable, rows [][]types.Datum, expire time.Time) (string, error) { - if len(rows) == 0 { - return "", errors.New("Cannot build delete SQL with empty rows") - } - - b := NewSQLBuilder(tbl) - if err := b.WriteDelete(); err != nil { - return "", err - } - - if err := b.WriteInCondition(tbl.KeyColumns, rows...); err != nil { - return "", err - } - - if err := b.WriteExpireCondition(expire); err != nil { - return "", err - } - - if err := b.WriteLimit(len(rows)); err != nil { - return "", err - } - - return b.Build() -} diff --git a/ttl/sqlbuilder/sql_test.go b/ttl/sqlbuilder/sql_test.go deleted file mode 100644 index c3822f9066b32..0000000000000 --- a/ttl/sqlbuilder/sql_test.go +++ /dev/null @@ -1,949 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package sqlbuilder_test - -import ( - "context" - "fmt" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/sqlbuilder" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/stretchr/testify/require" -) - -func TestEscape(t *testing.T) { - tb := &cache.PhysicalTable{ - Schema: model.NewCIStr("testp;\"';123`456"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("tp\"';123`456"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("col1\"';123`456"), FieldType: *types.NewFieldType(mysql.TypeString)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time\"';123`456"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - PartitionDef: &model.PartitionDefinition{ - Name: model.NewCIStr("p1\"';123`456"), - }, - } - - buildSelect := func(d []types.Datum) string { - b := sqlbuilder.NewSQLBuilder(tb) - require.NoError(t, b.WriteSelect()) - require.NoError(t, b.WriteCommonCondition(tb.KeyColumns, ">", d)) - require.NoError(t, b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - s, err := b.Build() - require.NoError(t, err) - return s - } - - buildDelete := func(ds ...[]types.Datum) string { - b := sqlbuilder.NewSQLBuilder(tb) - require.NoError(t, b.WriteDelete()) - require.NoError(t, b.WriteInCondition(tb.KeyColumns, ds...)) - require.NoError(t, b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - s, err := b.Build() - require.NoError(t, err) - return s - } - - cases := []struct { - tp string - ds [][]types.Datum - sql string - }{ - { - tp: "select", - ds: [][]types.Datum{d("key1'\";123`456")}, - sql: "SELECT LOW_PRIORITY SQL_NO_CACHE `col1\"';123``456` FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` > 'key1\\'\\\";123`456' AND `time\"';123``456` < FROM_UNIXTIME(0)", - }, - { - tp: "delete", - ds: [][]types.Datum{d("key2'\";123`456")}, - sql: "DELETE LOW_PRIORITY FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` IN ('key2\\'\\\";123`456') AND `time\"';123``456` < FROM_UNIXTIME(0)", - }, - { - tp: "delete", - ds: [][]types.Datum{d("key3'\";123`456"), d("key4'`\"")}, - sql: "DELETE LOW_PRIORITY FROM `testp;\"';123``456`.`tp\"';123``456` PARTITION(`p1\"';123``456`) WHERE `col1\"';123``456` IN ('key3\\'\\\";123`456', 'key4\\'`\\\"') AND `time\"';123``456` < FROM_UNIXTIME(0)", - }, - } - - for _, c := range cases { - switch c.tp { - case "select": - require.Equal(t, 1, len(c.ds)) - require.Equal(t, c.sql, buildSelect(c.ds[0])) - case "delete": - require.Equal(t, c.sql, buildDelete(c.ds...)) - default: - require.FailNow(t, "invalid tp: %s", c.tp) - } - - p := parser.New() - stmts, _, err := p.Parse(c.sql, "", "") - require.Equal(t, 1, len(stmts)) - require.NoError(t, err) - - var tbName *ast.TableName - var keyColumnName, timeColumnName string - var values []string - var timeFunc string - var timeTS int64 - switch c.tp { - case "select": - stmt, ok := stmts[0].(*ast.SelectStmt) - require.True(t, ok) - tbName = stmt.From.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) - and := stmt.Where.(*ast.BinaryOperationExpr) - cond1 := and.L.(*ast.BinaryOperationExpr) - keyColumnName = cond1.L.(*ast.ColumnNameExpr).Name.Name.O - values = []string{cond1.R.(ast.ValueExpr).GetValue().(string)} - cond2 := and.R.(*ast.BinaryOperationExpr) - timeColumnName = cond2.L.(*ast.ColumnNameExpr).Name.Name.O - timeFunc = cond2.R.(*ast.FuncCallExpr).FnName.L - timeTS = cond2.R.(*ast.FuncCallExpr).Args[0].(ast.ValueExpr).GetValue().(int64) - case "delete": - stmt, ok := stmts[0].(*ast.DeleteStmt) - require.True(t, ok) - tbName = stmt.TableRefs.TableRefs.Left.(*ast.TableSource).Source.(*ast.TableName) - and := stmt.Where.(*ast.BinaryOperationExpr) - cond1 := and.L.(*ast.PatternInExpr) - keyColumnName = cond1.Expr.(*ast.ColumnNameExpr).Name.Name.O - require.Equal(t, len(c.ds), len(cond1.List)) - values = make([]string, 0, len(c.ds)) - for _, expr := range cond1.List { - values = append(values, expr.(ast.ValueExpr).GetValue().(string)) - } - cond2 := and.R.(*ast.BinaryOperationExpr) - timeColumnName = cond2.L.(*ast.ColumnNameExpr).Name.Name.O - timeFunc = cond2.R.(*ast.FuncCallExpr).FnName.L - timeTS = cond2.R.(*ast.FuncCallExpr).Args[0].(ast.ValueExpr).GetValue().(int64) - default: - require.FailNow(t, "invalid tp: %s", c.tp) - } - - require.Equal(t, tb.Schema.O, tbName.Schema.O) - require.Equal(t, tb.Name.O, tbName.Name.O) - require.Equal(t, 1, len(tbName.PartitionNames)) - require.Equal(t, tb.PartitionDef.Name.O, tbName.PartitionNames[0].O) - require.Equal(t, tb.KeyColumns[0].Name.O, keyColumnName) - require.Equal(t, tb.TimeColumn.Name.O, timeColumnName) - for i, row := range c.ds { - require.Equal(t, row[0].GetString(), values[i]) - } - require.Equal(t, "from_unixtime", timeFunc) - require.Equal(t, int64(0), timeTS) - } -} - -func TestFormatSQLDatum(t *testing.T) { - // invalid pk types contains the types that should not exist in primary keys of a TTL table. - // We do not need to check sqlbuilder.FormatSQLDatum for these types - invalidPKTypes := []struct { - types []string - err *terror.Error - }{ - { - types: []string{"json"}, - err: dbterror.ErrJSONUsedAsKey, - }, - { - types: []string{"blob"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"blob(8)"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"text"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"text(8)"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"int", "json"}, - err: dbterror.ErrJSONUsedAsKey, - }, - { - types: []string{"int", "blob"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"int", "text"}, - err: dbterror.ErrBlobKeyWithoutLength, - }, - { - types: []string{"float"}, - err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, - }, - { - types: []string{"double"}, - err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, - }, - { - types: []string{"int", "float"}, - err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, - }, - { - types: []string{"int", "double"}, - err: dbterror.ErrUnsupportedPrimaryKeyTypeWithTTL, - }, - } - - cases := []struct { - ft string - values []interface{} - hex bool - }{ - { - ft: "int", - values: []interface{}{1, 2, 3, -12}, - }, - { - ft: "decimal(5, 2)", - values: []interface{}{"0.3", "128.71", "-245.32"}, - }, - { - ft: "varchar(32) CHARACTER SET latin1", - values: []interface{}{ - "aa';delete from t where 1;", - string([]byte{0xf1, 0xf2}), - string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), - }, - }, - { - ft: "char(32) CHARACTER SET utf8mb4", - values: []interface{}{ - "demo", - "\n123", - "aa';delete from t where 1;", - "你好👋", - }, - }, - { - ft: "varchar(32) CHARACTER SET utf8mb4", - values: []interface{}{ - "demo", - "aa';delete from t where 1;", - "你好👋", - }, - }, - { - ft: "varchar(32) CHARACTER SET binary", - values: []interface{}{ - string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), - "你好👋", - "abcdef", - }, - hex: true, - }, - { - ft: "binary(8)", - values: []interface{}{ - string([]byte{0xf1, 0xf2}), - string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), - }, - hex: true, - }, - { - ft: "blob", - values: []interface{}{ - string([]byte{0xf1, 0xf2}), - string([]byte{0xf1, 0xf2, 0xf3, 0xf4}), - }, - hex: true, - }, - { - ft: "bit(1)", - values: []interface{}{0, 1}, - hex: true, - }, - { - ft: "date", - values: []interface{}{"2022-01-02", "1900-12-31"}, - }, - { - ft: "time", - values: []interface{}{"00:00", "01:23", "13:51:22"}, - }, - { - ft: "datetime", - values: []interface{}{"2022-01-02 12:11:11", "2022-01-02"}, - }, - { - ft: "datetime(6)", - values: []interface{}{"2022-01-02 12:11:11.123456"}, - }, - { - ft: "timestamp", - values: []interface{}{"2022-01-02 12:11:11", "2022-01-02"}, - }, - { - ft: "timestamp(6)", - values: []interface{}{"2022-01-02 12:11:11.123456"}, - }, - { - ft: "enum('e1', 'e2', \"e3'\", 'e4\"', ';你好👋')", - values: []interface{}{"e1", "e2", "e3'", "e4\"", ";你好👋"}, - }, - { - ft: "set('e1', 'e2', \"e3'\", 'e4\"', ';你好👋')", - values: []interface{}{"", "e1", "e2", "e3'", "e4\"", ";你好👋"}, - }, - } - - store, do := testkit.CreateMockStoreAndDomain(t) - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - - for _, c := range invalidPKTypes { - var sb strings.Builder - sb.WriteString("create table t(") - cols := make([]string, 0, len(invalidPKTypes)) - for i, tp := range c.types { - colName := fmt.Sprintf("pk%d", i) - cols = append(cols, colName) - sb.WriteString(colName) - sb.WriteString(" ") - sb.WriteString(tp) - sb.WriteString(", ") - } - sb.WriteString("t timestamp, ") - sb.WriteString("primary key (") - sb.WriteString(strings.Join(cols, ", ")) - sb.WriteString(")) TTL=`t` + INTERVAL 1 DAY") - tk.MustGetDBError(sb.String(), c.err) - } - - // create a table with n columns - var sb strings.Builder - sb.WriteString("CREATE TABLE t (id varchar(32) primary key") - for i, c := range cases { - _, err := fmt.Fprintf(&sb, ",\n col%d %s DEFAULT NULL", i, c.ft) - require.NoError(t, err) - } - sb.WriteString("\n);") - tk.MustExec(sb.String()) - - tbl, err := do.InfoSchema().TableByName(model.NewCIStr("test"), model.NewCIStr("t")) - require.NoError(t, err) - - for i, c := range cases { - for j, v := range c.values { - tk.MustExec(fmt.Sprintf("insert into t (id, col%d) values ('%d-%d', ?)", i, i, j), v) - } - } - - ctx := kv.WithInternalSourceType(context.TODO(), kv.InternalTxnOthers) - for i, c := range cases { - for j := range c.values { - rowID := fmt.Sprintf("%d-%d", i, j) - colName := fmt.Sprintf("col%d", i) - exec, ok := tk.Session().(sqlexec.SQLExecutor) - require.True(t, ok) - selectSQL := fmt.Sprintf("select %s from t where id='%s'", colName, rowID) - rs, err := exec.ExecuteInternal(ctx, selectSQL) - require.NoError(t, err, selectSQL) - rows, err := sqlexec.DrainRecordSet(ctx, rs, 1) - require.NoError(t, err, selectSQL) - require.Equal(t, 1, len(rows), selectSQL) - col := tbl.Meta().FindPublicColumnByName(colName) - d := rows[0].GetDatum(0, &col.FieldType) - s, err := sqlbuilder.FormatSQLDatum(d, &col.FieldType) - require.NoError(t, err) - tk.MustQuery("select id from t where " + colName + "=" + s).Check(testkit.Rows(rowID)) - if c.hex { - require.True(t, strings.HasPrefix(s, "x'"), "ft: %s, got: %s", c.ft, s) - } - } - } -} - -func TestSQLBuilder(t *testing.T) { - must := func(err error) { - require.NoError(t, err) - } - - mustBuild := func(b *sqlbuilder.SQLBuilder, str string) { - s, err := b.Build() - require.NoError(t, err) - require.Equal(t, str, s) - } - - var b *sqlbuilder.SQLBuilder - - t1 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t1"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - t2 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test2"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t2"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, - {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - tp := &cache.PhysicalTable{ - Schema: model.NewCIStr("testp"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("tp"), - }, - KeyColumns: t1.KeyColumns, - TimeColumn: t1.TimeColumn, - PartitionDef: &model.PartitionDefinition{ - Name: model.NewCIStr("p1"), - }, - } - - // test build select queries - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1`") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1'") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) - must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("c3"))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1' AND `id` <= 'c3'") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - shLoc, err := time.LoadLocation("Asia/Shanghai") - require.NoError(t, err) - must(b.WriteExpireCondition(time.UnixMilli(0).In(shLoc))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0)") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1"))) - must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("c3"))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1' AND `id` <= 'c3' AND `time` < FROM_UNIXTIME(0)") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteOrderBy(t1.KeyColumns, false)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` ASC") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteOrderBy(t1.KeyColumns, true)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` DESC") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteOrderBy(t1.KeyColumns, false)) - must(b.WriteLimit(128)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` ORDER BY `id` ASC LIMIT 128") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t1.KeyColumns, ">", d("';``~?%\"\n"))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > '\\';``~?%\\\"\\n'") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t1.KeyColumns, ">", d("a1';'"))) - must(b.WriteCommonCondition(t1.KeyColumns, "<=", d("a2\""))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - must(b.WriteOrderBy(t1.KeyColumns, false)) - must(b.WriteLimit(128)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 'a1\\';\\'' AND `id` <= 'a2\\\"' AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 128") - - b = sqlbuilder.NewSQLBuilder(t2) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t2.KeyColumns, ">", d("x1", 20))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE (`a`, `b`) > ('x1', 20)") - - b = sqlbuilder.NewSQLBuilder(t2) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t2.KeyColumns, "<=", d("x2", 21))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - must(b.WriteOrderBy(t2.KeyColumns, false)) - must(b.WriteLimit(100)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE (`a`, `b`) <= ('x2', 21) AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b` ASC LIMIT 100") - - b = sqlbuilder.NewSQLBuilder(t2) - must(b.WriteSelect()) - must(b.WriteCommonCondition(t2.KeyColumns[0:1], "=", d("x3"))) - must(b.WriteCommonCondition(t2.KeyColumns[1:2], ">", d(31))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - must(b.WriteOrderBy(t2.KeyColumns, false)) - must(b.WriteLimit(100)) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b` FROM `test2`.`t2` WHERE `a` = 'x3' AND `b` > 31 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b` ASC LIMIT 100") - - // test build delete queries - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - _, err = b.Build() - require.EqualError(t, err, "expire condition not write") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - must(b.WriteInCondition(t1.KeyColumns, d("a"))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN ('a') AND `time` < FROM_UNIXTIME(0)") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - must(b.WriteInCondition(t1.KeyColumns, d("a"), d("b"))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN ('a', 'b') AND `time` < FROM_UNIXTIME(0)") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - must(b.WriteInCondition(t2.KeyColumns, d("a", 1))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - must(b.WriteLimit(100)) - mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1)) AND `time` < FROM_UNIXTIME(0) LIMIT 100") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - must(b.WriteInCondition(t2.KeyColumns, d("a", 1), d("b", 2))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - must(b.WriteLimit(100)) - mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1), ('b', 2)) AND `time` < FROM_UNIXTIME(0) LIMIT 100") - - b = sqlbuilder.NewSQLBuilder(t1) - must(b.WriteDelete()) - must(b.WriteInCondition(t2.KeyColumns, d("a", 1), d("b", 2))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE (`a`, `b`) IN (('a', 1), ('b', 2)) AND `time` < FROM_UNIXTIME(0)") - - // test select partition table - b = sqlbuilder.NewSQLBuilder(tp) - must(b.WriteSelect()) - must(b.WriteCommonCondition(tp.KeyColumns, ">", d("a1"))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `testp`.`tp` PARTITION(`p1`) WHERE `id` > 'a1' AND `time` < FROM_UNIXTIME(0)") - - b = sqlbuilder.NewSQLBuilder(tp) - must(b.WriteDelete()) - must(b.WriteInCondition(tp.KeyColumns, d("a"), d("b"))) - must(b.WriteExpireCondition(time.UnixMilli(0).In(time.UTC))) - mustBuild(b, "DELETE LOW_PRIORITY FROM `testp`.`tp` PARTITION(`p1`) WHERE `id` IN ('a', 'b') AND `time` < FROM_UNIXTIME(0)") -} - -func TestScanQueryGenerator(t *testing.T) { - t1 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t1"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - t2 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test2"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t2"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, - {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, - {Name: model.NewCIStr("c"), FieldType: types.NewFieldTypeBuilder().SetType(mysql.TypeString).SetFlag(mysql.BinaryFlag).Build()}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - result := func(last []types.Datum, n int) [][]types.Datum { - ds := make([][]types.Datum, n) - ds[n-1] = last - return ds - } - - cases := []struct { - tbl *cache.PhysicalTable - expire time.Time - rangeStart []types.Datum - rangeEnd []types.Datum - path [][]interface{} - }{ - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 3, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", - }, - { - nil, 5, "", - }, - }, - }, - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 3, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", - }, - { - [][]types.Datum{}, 5, "", - }, - }, - }, - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - rangeStart: d(1), - rangeEnd: d(100), - path: [][]interface{}{ - { - nil, 3, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` >= 1 AND `id` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", - }, - { - result(d(10), 3), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 10 AND `id` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 5", - }, - { - result(d(15), 4), 5, - "", - }, - }, - }, - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 3, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 3", - }, - { - result(d(2), 3), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 2 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 5", - }, - { - result(d(4), 5), 6, - "SELECT LOW_PRIORITY SQL_NO_CACHE `id` FROM `test`.`t1` WHERE `id` > 4 AND `time` < FROM_UNIXTIME(0) ORDER BY `id` ASC LIMIT 6", - }, - { - result(d(7), 5), 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - nil, 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - nil, 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - [][]types.Datum{}, 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0xf0}), 4), 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - rangeStart: d(1, "x", []byte{0xe}), - rangeEnd: d(100, "z", []byte{0xff}), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` >= x'0e' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x1a}), 5), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x20}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "y", []byte{0x0a}), 5), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'y' AND `c` > x'0a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "y", []byte{0x11}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'y' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "z", []byte{0x02}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(3, "a", []byte{0x01}), 5), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 3 AND `b` = 'a' AND `c` > x'01' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(3, "a", []byte{0x11}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 3 AND `b` > 'a' AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(3, "c", []byte{0x12}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 3 AND (`a`, `b`, `c`) < (100, 'z', x'ff') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(5, "e", []byte{0xa1}), 4), 5, "", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - rangeStart: d(1), - rangeEnd: d(100), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` >= 1 AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x1a}), 5), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x20}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "y", []byte{0x0a}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND `a` < 100 AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - }, - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - rangeStart: d(1, "x"), - rangeEnd: d(100, "z"), - path: [][]interface{}{ - { - nil, 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` >= 'x' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x1a}), 5), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` = 'x' AND `c` > x'1a' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "x", []byte{0x20}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` = 1 AND `b` > 'x' AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - { - result(d(1, "y", []byte{0x0a}), 4), 5, - "SELECT LOW_PRIORITY SQL_NO_CACHE `a`, `b`, `c` FROM `test2`.`t2` WHERE `a` > 1 AND (`a`, `b`) < (100, 'z') AND `time` < FROM_UNIXTIME(0) ORDER BY `a`, `b`, `c` ASC LIMIT 5", - }, - }, - }, - } - - for i, c := range cases { - g, err := sqlbuilder.NewScanQueryGenerator(c.tbl, c.expire, c.rangeStart, c.rangeEnd) - require.NoError(t, err, fmt.Sprintf("%d", i)) - for j, p := range c.path { - msg := fmt.Sprintf("%d-%d", i, j) - var result [][]types.Datum - require.Equal(t, 3, len(p), msg) - if arg := p[0]; arg != nil { - r, ok := arg.([][]types.Datum) - require.True(t, ok, msg) - result = r - } - limit, ok := p[1].(int) - require.True(t, ok, msg) - sql, ok := p[2].(string) - require.True(t, ok, msg) - s, err := g.NextSQL(result, limit) - require.NoError(t, err, msg) - require.Equal(t, sql, s, msg) - require.Equal(t, s == "", g.IsExhausted(), msg) - } - } -} - -func TestBuildDeleteSQL(t *testing.T) { - t1 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t1"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("id"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - t2 := &cache.PhysicalTable{ - Schema: model.NewCIStr("test2"), - TableInfo: &model.TableInfo{ - Name: model.NewCIStr("t2"), - }, - KeyColumns: []*model.ColumnInfo{ - {Name: model.NewCIStr("a"), FieldType: *types.NewFieldType(mysql.TypeInt24)}, - {Name: model.NewCIStr("b"), FieldType: *types.NewFieldType(mysql.TypeVarchar)}, - }, - TimeColumn: &model.ColumnInfo{ - Name: model.NewCIStr("time"), - FieldType: *types.NewFieldType(mysql.TypeDatetime), - }, - } - - cases := []struct { - tbl *cache.PhysicalTable - expire time.Time - rows [][]types.Datum - sql string - }{ - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - rows: [][]types.Datum{d(1)}, - sql: "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN (1) AND `time` < FROM_UNIXTIME(0) LIMIT 1", - }, - { - tbl: t1, - expire: time.UnixMilli(0).In(time.UTC), - rows: [][]types.Datum{d(2), d(3), d(4)}, - sql: "DELETE LOW_PRIORITY FROM `test`.`t1` WHERE `id` IN (2, 3, 4) AND `time` < FROM_UNIXTIME(0) LIMIT 3", - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - rows: [][]types.Datum{d(1, "a")}, - sql: "DELETE LOW_PRIORITY FROM `test2`.`t2` WHERE (`a`, `b`) IN ((1, 'a')) AND `time` < FROM_UNIXTIME(0) LIMIT 1", - }, - { - tbl: t2, - expire: time.UnixMilli(0).In(time.UTC), - rows: [][]types.Datum{d(1, "a"), d(2, "b")}, - sql: "DELETE LOW_PRIORITY FROM `test2`.`t2` WHERE (`a`, `b`) IN ((1, 'a'), (2, 'b')) AND `time` < FROM_UNIXTIME(0) LIMIT 2", - }, - } - - for _, c := range cases { - sql, err := sqlbuilder.BuildDeleteSQL(c.tbl, c.rows, c.expire) - require.NoError(t, err) - require.Equal(t, c.sql, sql) - } -} - -func d(vs ...interface{}) []types.Datum { - datums := make([]types.Datum, len(vs)) - for i, v := range vs { - switch val := v.(type) { - case string: - datums[i] = types.NewStringDatum(val) - case int: - datums[i] = types.NewIntDatum(int64(val)) - case []byte: - datums[i] = types.NewBytesDatum(val) - default: - panic(fmt.Sprintf("invalid value type: %T, value: %v", v, v)) - } - } - return datums -} diff --git a/ttl/ttlworker/BUILD.bazel b/ttl/ttlworker/BUILD.bazel deleted file mode 100644 index aabab9d4a22e5..0000000000000 --- a/ttl/ttlworker/BUILD.bazel +++ /dev/null @@ -1,112 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ttlworker", - srcs = [ - "config.go", - "del.go", - "job.go", - "job_manager.go", - "scan.go", - "session.go", - "task_manager.go", - "timer.go", - "timer_sync.go", - "worker.go", - ], - importpath = "github.com/pingcap/tidb/ttl/ttlworker", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//metrics", - "//parser/model", - "//parser/terror", - "//sessionctx", - "//sessionctx/variable", - "//store/driver/error", - "//timer/api", - "//timer/runtime", - "//timer/tablestore", - "//ttl/cache", - "//ttl/client", - "//ttl/metrics", - "//ttl/session", - "//ttl/sqlbuilder", - "//types", - "//util", - "//util/chunk", - "//util/logutil", - "//util/sqlexec", - "//util/timeutil", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@io_etcd_go_etcd_client_v3//:client", - "@org_golang_x_exp//maps", - "@org_golang_x_time//rate", - "@org_uber_go_multierr//:multierr", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "ttlworker_test", - timeout = "moderate", - srcs = [ - "del_test.go", - "job_manager_integration_test.go", - "job_manager_test.go", - "scan_test.go", - "session_test.go", - "task_manager_integration_test.go", - "task_manager_test.go", - "timer_sync_test.go", - "timer_test.go", - ], - embed = [":ttlworker"], - flaky = True, - race = "on", - shard_count = 46, - deps = [ - "//domain", - "//infoschema", - "//kv", - "//metrics", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//session", - "//sessionctx", - "//sessionctx/variable", - "//statistics/handle/autoanalyze", - "//store/mockstore", - "//testkit", - "//timer/api", - "//timer/tablestore", - "//ttl/cache", - "//ttl/client", - "//ttl/metrics", - "//ttl/session", - "//types", - "//util/chunk", - "//util/logutil", - "//util/mock", - "@com_github_google_uuid//:uuid", - "@com_github_ngaut_pools//:pools", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_prometheus_client_model//go", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//mock", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//testutils", - "@com_github_tikv_client_go_v2//tikv", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_golang_x_time//rate", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) diff --git a/ttl/ttlworker/job.go b/ttl/ttlworker/job.go deleted file mode 100644 index 8f2e25c0ff30e..0000000000000 --- a/ttl/ttlworker/job.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ttlworker - -import ( - "context" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -const updateJobCurrentStatusTemplate = "UPDATE mysql.tidb_ttl_table_status SET current_job_status = %? WHERE table_id = %? AND current_job_status = %? AND current_job_id = %?" -const finishJobTemplate = `UPDATE mysql.tidb_ttl_table_status - SET last_job_id = current_job_id, - last_job_start_time = current_job_start_time, - last_job_finish_time = %?, - last_job_ttl_expire = current_job_ttl_expire, - last_job_summary = %?, - current_job_id = NULL, - current_job_owner_id = NULL, - current_job_owner_hb_time = NULL, - current_job_start_time = NULL, - current_job_ttl_expire = NULL, - current_job_state = NULL, - current_job_status = NULL, - current_job_status_update_time = NULL - WHERE table_id = %? AND current_job_id = %?` -const removeTaskForJobTemplate = "DELETE FROM mysql.tidb_ttl_task WHERE job_id = %?" -const createJobHistoryRowTemplate = `INSERT INTO - mysql.tidb_ttl_job_history ( - job_id, - table_id, - parent_table_id, - table_schema, - table_name, - partition_name, - create_time, - finish_time, - ttl_expire, - status - ) -VALUES - (%?, %?, %?, %?, %?, %?, %?, FROM_UNIXTIME(1), %?, %?)` -const finishJobHistoryTemplate = `UPDATE mysql.tidb_ttl_job_history - SET finish_time = %?, - summary_text = %?, - expired_rows = %?, - deleted_rows = %?, - error_delete_rows = %?, - status = %? - WHERE job_id = %?` - -func updateJobCurrentStatusSQL(tableID int64, oldStatus cache.JobStatus, newStatus cache.JobStatus, jobID string) (string, []interface{}) { - return updateJobCurrentStatusTemplate, []interface{}{string(newStatus), tableID, string(oldStatus), jobID} -} - -func finishJobSQL(tableID int64, finishTime time.Time, summary string, jobID string) (string, []interface{}) { - return finishJobTemplate, []interface{}{finishTime.Format(timeFormat), summary, tableID, jobID} -} - -func removeTaskForJob(jobID string) (string, []interface{}) { - return removeTaskForJobTemplate, []interface{}{jobID} -} - -func createJobHistorySQL(jobID string, tbl *cache.PhysicalTable, expire time.Time, now time.Time) (string, []interface{}) { - var partitionName interface{} - if tbl.Partition.O != "" { - partitionName = tbl.Partition.O - } - - return createJobHistoryRowTemplate, []interface{}{ - jobID, - tbl.ID, - tbl.TableInfo.ID, - tbl.Schema.O, - tbl.Name.O, - partitionName, - now.Format(timeFormat), - expire.Format(timeFormat), - string(cache.JobStatusRunning), - } -} - -func finishJobHistorySQL(jobID string, finishTime time.Time, summary *TTLSummary) (string, []interface{}) { - return finishJobHistoryTemplate, []interface{}{ - finishTime.Format(timeFormat), - summary.SummaryText, - summary.TotalRows, - summary.SuccessRows, - summary.ErrorRows, - string(cache.JobStatusFinished), - jobID, - } -} - -type ttlJob struct { - id string - ownerID string - - createTime time.Time - ttlExpireTime time.Time - - tbl *cache.PhysicalTable - - // status is the only field which should be protected by a mutex, as `Cancel` may be called at any time, and will - // change the status - statusMutex sync.Mutex - status cache.JobStatus -} - -// finish turns current job into last job, and update the error message and statistics summary -func (job *ttlJob) finish(se session.Session, now time.Time, summary *TTLSummary) { - // at this time, the job.ctx may have been canceled (to cancel this job) - // even when it's canceled, we'll need to update the states, so use another context - err := se.RunInTxn(context.TODO(), func() error { - sql, args := finishJobSQL(job.tbl.ID, now, summary.SummaryText, job.id) - _, err := se.ExecuteSQL(context.TODO(), sql, args...) - if err != nil { - return errors.Wrapf(err, "execute sql: %s", sql) - } - - sql, args = removeTaskForJob(job.id) - _, err = se.ExecuteSQL(context.TODO(), sql, args...) - if err != nil { - return errors.Wrapf(err, "execute sql: %s", sql) - } - - sql, args = finishJobHistorySQL(job.id, now, summary) - _, err = se.ExecuteSQL(context.TODO(), sql, args...) - if err != nil { - return errors.Wrapf(err, "execute sql: %s", sql) - } - - return nil - }, session.TxnModeOptimistic) - - if err != nil { - logutil.BgLogger().Error("fail to finish a ttl job", zap.Error(err), zap.Int64("tableID", job.tbl.ID), zap.String("jobID", job.id)) - } -} diff --git a/ttl/ttlworker/session.go b/ttl/ttlworker/session.go deleted file mode 100644 index d3e0c940e78c7..0000000000000 --- a/ttl/ttlworker/session.go +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ttlworker - -import ( - "context" - "fmt" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// The following two functions are using `sqlexec.SQLExecutor` to represent session -// which is actually not correct. It's a work around for the cyclic dependency problem. -// It actually doesn't accept arbitrary SQLExecutor, but just `*session.session`, which means -// you cannot pass the `(ttl/session).Session` into it. -// Use `sqlexec.SQLExecutor` and `sessionctx.Session` or another other interface (including -// `interface{}`) here is the same, I just pick one small enough interface. -// Also, we cannot use the functions in `session/session.go` (to avoid cyclic dependency), so -// registering function here is really needed. - -// AttachStatsCollector attaches the stats collector for the session. -// this function is registered in BootstrapSession in /session/session.go -var AttachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { - return s -} - -// DetachStatsCollector removes the stats collector for the session -// this function is registered in BootstrapSession in /session/session.go -var DetachStatsCollector = func(s sqlexec.SQLExecutor) sqlexec.SQLExecutor { - return s -} - -type sessionPool interface { - Get() (pools.Resource, error) - Put(pools.Resource) -} - -func getSession(pool sessionPool) (session.Session, error) { - resource, err := pool.Get() - if err != nil { - return nil, err - } - - if se, ok := resource.(session.Session); ok { - // Only for test, in this case, the return session is mockSession - return se, nil - } - - sctx, ok := resource.(sessionctx.Context) - if !ok { - pool.Put(resource) - return nil, errors.Errorf("%T cannot be casted to sessionctx.Context", sctx) - } - - exec, ok := resource.(sqlexec.SQLExecutor) - if !ok { - pool.Put(resource) - return nil, errors.Errorf("%T cannot be casted to sqlexec.SQLExecutor", sctx) - } - - originalRetryLimit := sctx.GetSessionVars().RetryLimit - originalEnable1PC := sctx.GetSessionVars().Enable1PC - originalEnableAsyncCommit := sctx.GetSessionVars().EnableAsyncCommit - se := session.NewSession(sctx, exec, func(se session.Session) { - _, err = se.ExecuteSQL(context.Background(), fmt.Sprintf("set tidb_retry_limit=%d", originalRetryLimit)) - if err != nil { - logutil.BgLogger().Error("fail to reset tidb_retry_limit", zap.Int64("originalRetryLimit", originalRetryLimit), zap.Error(err)) - } - - if !originalEnable1PC { - _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_1pc=OFF") - terror.Log(err) - } - - if !originalEnableAsyncCommit { - _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_async_commit=OFF") - terror.Log(err) - } - - DetachStatsCollector(exec) - - pool.Put(resource) - }) - - exec = AttachStatsCollector(exec) - - // store and set the retry limit to 0 - _, err = se.ExecuteSQL(context.Background(), "set tidb_retry_limit=0") - if err != nil { - se.Close() - return nil, err - } - - // set enable 1pc to ON - _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_1pc=ON") - if err != nil { - se.Close() - return nil, err - } - - // set enable async commit to ON - _, err = se.ExecuteSQL(context.Background(), "set tidb_enable_async_commit=ON") - if err != nil { - se.Close() - return nil, err - } - - // Force rollback the session to guarantee the session is not in any explicit transaction - if _, err = se.ExecuteSQL(context.Background(), "ROLLBACK"); err != nil { - se.Close() - return nil, err - } - - return se, nil -} - -func newTableSession(se session.Session, tbl *cache.PhysicalTable, expire time.Time) *ttlTableSession { - return &ttlTableSession{ - Session: se, - tbl: tbl, - expire: expire, - } -} - -type ttlTableSession struct { - session.Session - tbl *cache.PhysicalTable - expire time.Time -} - -func (s *ttlTableSession) ExecuteSQLWithCheck(ctx context.Context, sql string) ([]chunk.Row, bool, error) { - tracer := metrics.PhaseTracerFromCtx(ctx) - defer tracer.EnterPhase(tracer.Phase()) - - tracer.EnterPhase(metrics.PhaseOther) - if !variable.EnableTTLJob.Load() { - return nil, false, errors.New("global TTL job is disabled") - } - - if err := s.ResetWithGlobalTimeZone(ctx); err != nil { - return nil, false, err - } - - var result []chunk.Row - shouldRetry := true - err := s.RunInTxn(ctx, func() error { - tracer.EnterPhase(metrics.PhaseQuery) - defer tracer.EnterPhase(tracer.Phase()) - rows, err := s.ExecuteSQL(ctx, sql) - tracer.EnterPhase(metrics.PhaseCheckTTL) - // We must check the configuration after ExecuteSQL because of MDL and the meta the current transaction used - // can only be determined after executed one query. - if validateErr := validateTTLWork(ctx, s.Session, s.tbl, s.expire); validateErr != nil { - shouldRetry = false - return errors.Annotatef(validateErr, "table '%s.%s' meta changed, should abort current job", s.tbl.Schema, s.tbl.Name) - } - - if err != nil { - return err - } - - result = rows - return nil - }, session.TxnModeOptimistic) - - if err != nil { - return nil, shouldRetry, err - } - - return result, false, nil -} - -func validateTTLWork(ctx context.Context, s session.Session, tbl *cache.PhysicalTable, expire time.Time) error { - curTbl, err := s.SessionInfoSchema().TableByName(tbl.Schema, tbl.Name) - if err != nil { - return err - } - - newTblInfo := curTbl.Meta() - if tbl.TableInfo == newTblInfo { - return nil - } - - if tbl.TableInfo.ID != newTblInfo.ID { - return errors.New("table id changed") - } - - newTTLTbl, err := cache.NewPhysicalTable(tbl.Schema, newTblInfo, tbl.Partition) - if err != nil { - return err - } - - if newTTLTbl.ID != tbl.ID { - return errors.New("physical id changed") - } - - if tbl.Partition.L != "" { - if newTTLTbl.PartitionDef.Name.L != tbl.PartitionDef.Name.L { - return errors.New("partition name changed") - } - } - - if !newTTLTbl.TTLInfo.Enable { - return errors.New("table TTL disabled") - } - - if newTTLTbl.TimeColumn.Name.L != tbl.TimeColumn.Name.L { - return errors.New("time column name changed") - } - - if newTblInfo.TTLInfo.IntervalExprStr != tbl.TTLInfo.IntervalExprStr || - newTblInfo.TTLInfo.IntervalTimeUnit != tbl.TTLInfo.IntervalTimeUnit { - newExpireTime, err := newTTLTbl.EvalExpireTime(ctx, s, s.Now()) - if err != nil { - return err - } - - if newExpireTime.Before(expire) { - return errors.New("expire interval changed") - } - } - - return nil -} diff --git a/ttl/ttlworker/session_test.go b/ttl/ttlworker/session_test.go deleted file mode 100644 index 62b0d4764c12a..0000000000000 --- a/ttl/ttlworker/session_test.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ttlworker - -import ( - "context" - "errors" - "strings" - "testing" - "time" - - "github.com/ngaut/pools" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/ttl/cache" - "github.com/pingcap/tidb/ttl/session" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/stretchr/testify/require" -) - -func newMockTTLTbl(t *testing.T, name string) *cache.PhysicalTable { - tblInfo := &model.TableInfo{ - Name: model.NewCIStr(name), - Columns: []*model.ColumnInfo{ - { - ID: 1, - Name: model.NewCIStr("time"), - Offset: 0, - FieldType: *types.NewFieldType(mysql.TypeDatetime), - State: model.StatePublic, - }, - }, - TTLInfo: &model.TTLInfo{ - ColumnName: model.NewCIStr("time"), - IntervalExprStr: "1", - IntervalTimeUnit: int(ast.TimeUnitSecond), - Enable: true, - JobInterval: "1h", - }, - State: model.StatePublic, - } - - tbl, err := cache.NewPhysicalTable(model.NewCIStr("test"), tblInfo, model.NewCIStr("")) - require.NoError(t, err) - return tbl -} - -func newMockInfoSchema(tbl ...*model.TableInfo) infoschema.InfoSchema { - return infoschema.MockInfoSchema(tbl) -} - -func newMockInfoSchemaWithVer(ver int64, tbl ...*model.TableInfo) infoschema.InfoSchema { - return infoschema.MockInfoSchemaWithSchemaVer(tbl, ver) -} - -type mockRows struct { - t *testing.T - fieldTypes []*types.FieldType - *chunk.Chunk -} - -func newMockRows(t *testing.T, fieldTypes ...*types.FieldType) *mockRows { - return &mockRows{ - t: t, - fieldTypes: fieldTypes, - Chunk: chunk.NewChunkWithCapacity(fieldTypes, 8), - } -} - -func (r *mockRows) Append(row ...interface{}) *mockRows { - require.Equal(r.t, len(r.fieldTypes), len(row)) - for i, ft := range r.fieldTypes { - tp := ft.GetType() - switch tp { - case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDatetime: - tm, ok := row[i].(time.Time) - require.True(r.t, ok) - r.AppendTime(i, types.NewTime(types.FromGoTime(tm), tp, types.DefaultFsp)) - case mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - val, ok := row[i].(int) - require.True(r.t, ok) - r.AppendInt64(i, int64(val)) - default: - require.FailNow(r.t, "unsupported tp %v", tp) - } - } - return r -} - -func (r *mockRows) Rows() []chunk.Row { - rows := make([]chunk.Row, r.NumRows()) - for i := 0; i < r.NumRows(); i++ { - rows[i] = r.GetRow(i) - } - return rows -} - -type mockSessionPool struct { - t *testing.T - se *mockSession - lastSession *mockSession -} - -func (p *mockSessionPool) Get() (pools.Resource, error) { - se := *(p.se) - p.lastSession = &se - return p.lastSession, nil -} - -func (p *mockSessionPool) Put(pools.Resource) {} - -func newMockSessionPool(t *testing.T, tbl ...*cache.PhysicalTable) *mockSessionPool { - return &mockSessionPool{ - se: newMockSession(t, tbl...), - } -} - -type mockSession struct { - t *testing.T - sessionctx.Context - sessionVars *variable.SessionVars - sessionInfoSchema infoschema.InfoSchema - executeSQL func(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) - rows []chunk.Row - execErr error - evalExpire time.Time - resetTimeZoneCalls int - closed bool - commitErr error -} - -func newMockSession(t *testing.T, tbl ...*cache.PhysicalTable) *mockSession { - tbls := make([]*model.TableInfo, len(tbl)) - for i, ttlTbl := range tbl { - tbls[i] = ttlTbl.TableInfo - } - sessVars := variable.NewSessionVars(nil) - sessVars.TimeZone = time.UTC - return &mockSession{ - t: t, - sessionInfoSchema: newMockInfoSchema(tbls...), - evalExpire: time.Now(), - sessionVars: sessVars, - } -} - -func (s *mockSession) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { - return s.sessionInfoSchema -} - -func (s *mockSession) SessionInfoSchema() infoschema.InfoSchema { - require.False(s.t, s.closed) - return s.sessionInfoSchema -} - -func (s *mockSession) GetSessionVars() *variable.SessionVars { - require.False(s.t, s.closed) - return s.sessionVars -} - -func (s *mockSession) ExecuteSQL(ctx context.Context, sql string, args ...interface{}) ([]chunk.Row, error) { - require.False(s.t, s.closed) - if strings.HasPrefix(strings.ToUpper(sql), "SELECT FROM_UNIXTIME") { - return newMockRows(s.t, types.NewFieldType(mysql.TypeTimestamp)).Append(s.evalExpire.In(s.GetSessionVars().TimeZone)).Rows(), nil - } - - if strings.HasPrefix(strings.ToUpper(sql), "SET ") { - return nil, nil - } - - if s.executeSQL != nil { - return s.executeSQL(ctx, sql, args...) - } - return s.rows, s.execErr -} - -func (s *mockSession) RunInTxn(_ context.Context, fn func() error, _ session.TxnMode) error { - require.False(s.t, s.closed) - if err := fn(); err != nil { - return err - } - return s.commitErr -} - -func (s *mockSession) ResetWithGlobalTimeZone(_ context.Context) (err error) { - require.False(s.t, s.closed) - s.resetTimeZoneCalls++ - return nil -} - -func (s *mockSession) Close() { - s.closed = true -} - -func (s *mockSession) Now() time.Time { - tz := s.sessionVars.TimeZone - if tz != nil { - tz = time.UTC - } - return time.Now().In(tz) -} - -func TestExecuteSQLWithCheck(t *testing.T) { - ctx := context.TODO() - tbl := newMockTTLTbl(t, "t1") - s := newMockSession(t, tbl) - s.execErr = errors.New("mockErr") - s.rows = newMockRows(t, types.NewFieldType(mysql.TypeInt24)).Append(12).Rows() - tblSe := newTableSession(s, tbl, time.UnixMilli(0).In(time.UTC)) - - rows, shouldRetry, err := tblSe.ExecuteSQLWithCheck(ctx, "select 1") - require.EqualError(t, err, "mockErr") - require.True(t, shouldRetry) - require.Nil(t, rows) - require.Equal(t, 1, s.resetTimeZoneCalls) - - s.sessionInfoSchema = newMockInfoSchema() - rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") - require.EqualError(t, err, "table 'test.t1' meta changed, should abort current job: [schema:1146]Table 'test.t1' doesn't exist") - require.False(t, shouldRetry) - require.Nil(t, rows) - require.Equal(t, 2, s.resetTimeZoneCalls) - - s.sessionInfoSchema = newMockInfoSchema(tbl.TableInfo) - s.execErr = nil - rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") - require.NoError(t, err) - require.False(t, shouldRetry) - require.Equal(t, 1, len(rows)) - require.Equal(t, int64(12), rows[0].GetInt64(0)) - require.Equal(t, 3, s.resetTimeZoneCalls) - - s.commitErr = errors.New("mockCommitErr") - rows, shouldRetry, err = tblSe.ExecuteSQLWithCheck(ctx, "select 1") - require.EqualError(t, err, "mockCommitErr") - require.True(t, shouldRetry) - require.Nil(t, rows) - require.Equal(t, 4, s.resetTimeZoneCalls) -} - -func TestValidateTTLWork(t *testing.T) { - ctx := context.TODO() - tbl := newMockTTLTbl(t, "t1") - expire := time.UnixMilli(0).In(time.UTC) - - s := newMockSession(t, tbl) - s.execErr = errors.New("mockErr") - s.evalExpire = time.UnixMilli(0).In(time.UTC) - - // test table dropped - s.sessionInfoSchema = newMockInfoSchema() - err := validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "[schema:1146]Table 'test.t1' doesn't exist") - - // test TTL option removed - tbl2 := tbl.TableInfo.Clone() - tbl2.TTLInfo = nil - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "table 'test.t1' is not a ttl table") - - // test table state not public - tbl2 = tbl.TableInfo.Clone() - tbl2.State = model.StateDeleteOnly - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "table 'test.t1' is not a public table") - - // test table name changed - tbl2 = tbl.TableInfo.Clone() - tbl2.Name = model.NewCIStr("testcc") - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "[schema:1146]Table 'test.t1' doesn't exist") - - // test table id changed - tbl2 = tbl.TableInfo.Clone() - tbl2.ID = 123 - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "table id changed") - - // test time column name changed - tbl2 = tbl.TableInfo.Clone() - tbl2.Columns[0] = tbl2.Columns[0].Clone() - tbl2.Columns[0].Name = model.NewCIStr("time2") - tbl2.TTLInfo.ColumnName = model.NewCIStr("time2") - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "time column name changed") - - // test interval changed and expire time before previous - tbl2 = tbl.TableInfo.Clone() - tbl2.TTLInfo.IntervalExprStr = "10" - s.sessionInfoSchema = newMockInfoSchema(tbl2) - s.evalExpire = time.UnixMilli(-1) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "expire interval changed") - - tbl2 = tbl.TableInfo.Clone() - tbl2.TTLInfo.IntervalTimeUnit = int(ast.TimeUnitDay) - s.evalExpire = time.UnixMilli(-1) - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "expire interval changed") - - // test for safe meta change - tbl2 = tbl.TableInfo.Clone() - tbl2.Columns[0] = tbl2.Columns[0].Clone() - tbl2.Columns[0].ID += 10 - tbl2.Columns[0].FieldType = *types.NewFieldType(mysql.TypeDate) - tbl2.TTLInfo.IntervalExprStr = "100" - s.evalExpire = time.UnixMilli(1000) - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.NoError(t, err) - - // test table partition name changed - tp := tbl.TableInfo.Clone() - tp.Partition = &model.PartitionInfo{ - Definitions: []model.PartitionDefinition{ - {ID: 1023, Name: model.NewCIStr("p0")}, - }, - } - tbl, err = cache.NewPhysicalTable(model.NewCIStr("test"), tp, model.NewCIStr("p0")) - require.NoError(t, err) - tbl2 = tp.Clone() - tbl2.Partition = tp.Partition.Clone() - tbl2.Partition.Definitions[0].Name = model.NewCIStr("p1") - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "partition 'p0' is not found in ttl table 'test.t1'") - - // test table partition id changed - tbl2 = tp.Clone() - tbl2.Partition = tp.Partition.Clone() - tbl2.Partition.Definitions[0].ID += 100 - s.sessionInfoSchema = newMockInfoSchema(tbl2) - err = validateTTLWork(ctx, s, tbl, expire) - require.EqualError(t, err, "physical id changed") -} diff --git a/ttl/ttlworker/timer.go b/ttl/ttlworker/timer.go deleted file mode 100644 index 8ea176515aa14..0000000000000 --- a/ttl/ttlworker/timer.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package ttlworker - -import ( - "context" - "encoding/json" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/sessionctx/variable" - timerapi "github.com/pingcap/tidb/timer/api" - timerrt "github.com/pingcap/tidb/timer/runtime" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/timeutil" - "go.uber.org/zap" -) - -const ( - defaultCheckTTLJobInterval = 10 * time.Second -) - -type ttlTimerSummary struct { - LastJobRequestID string `json:"last_job_request_id,omitempty"` - LastJobSummary *TTLSummary `json:"last_job_summary,omitempty"` -} - -// TTLJobTrace contains some TTL job information to trace -type TTLJobTrace struct { - // RequestID is the request id when job submitted, we can use it to trace a job - RequestID string - // Finished indicates whether the job is finished - Finished bool - // Summary indicates the summary of the job - Summary *TTLSummary -} - -// TTLJobAdapter is used to submit TTL job and trace job status -type TTLJobAdapter interface { - // CanSubmitJob returns whether a new job can be created for the specified table - CanSubmitJob(tableID, physicalID int64) bool - // SubmitJob submits a new job - SubmitJob(ctx context.Context, tableID, physicalID int64, requestID string, watermark time.Time) (*TTLJobTrace, error) - // GetJob returns the job to trace - GetJob(ctx context.Context, tableID, physicalID int64, requestID string) (*TTLJobTrace, error) -} - -type ttlTimerHook struct { - adapter TTLJobAdapter - cli timerapi.TimerClient - ctx context.Context - cancel func() - wg sync.WaitGroup - nowFunc func() time.Time - checkTTLJobInterval time.Duration - // waitJobLoopCounter is only used for test - waitJobLoopCounter int64 -} - -func newTTLTimerHook(adapter TTLJobAdapter, cli timerapi.TimerClient) *ttlTimerHook { - ctx, cancel := context.WithCancel(context.Background()) - return &ttlTimerHook{ - adapter: adapter, - cli: cli, - ctx: ctx, - cancel: cancel, - nowFunc: time.Now, - checkTTLJobInterval: defaultCheckTTLJobInterval, - } -} - -func (t *ttlTimerHook) Start() {} - -func (t *ttlTimerHook) Stop() { - t.cancel() - t.wg.Wait() -} - -func (t *ttlTimerHook) OnPreSchedEvent(_ context.Context, event timerapi.TimerShedEvent) (r timerapi.PreSchedEventResult, err error) { - if !variable.EnableTTLJob.Load() { - r.Delay = time.Minute - return - } - - windowStart, windowEnd := variable.TTLJobScheduleWindowStartTime.Load(), variable.TTLJobScheduleWindowEndTime.Load() - if !timeutil.WithinDayTimePeriod(windowStart, windowEnd, t.nowFunc()) { - r.Delay = time.Minute - return - } - - timer := event.Timer() - var data TTLTimerData - if err = json.Unmarshal(event.Timer().Data, &data); err != nil { - logutil.BgLogger().Error("invalid TTL timer data", - zap.String("timerID", timer.ID), - zap.String("timerKey", timer.Key), - zap.ByteString("data", timer.Data), - ) - r.Delay = time.Minute - return - } - - if !t.adapter.CanSubmitJob(data.TableID, data.PhysicalID) { - r.Delay = time.Minute - return - } - - return -} - -func (t *ttlTimerHook) OnSchedEvent(ctx context.Context, event timerapi.TimerShedEvent) error { - timer := event.Timer() - eventID := event.EventID() - logger := logutil.BgLogger().With( - zap.String("key", timer.Key), - zap.String("eventID", eventID), - zap.Time("eventStart", timer.EventStart), - zap.Strings("tags", timer.Tags), - ) - - logger.Info("timer triggered to run TTL job", zap.String("manualRequest", timer.EventManualRequestID)) - if err := t.ctx.Err(); err != nil { - return err - } - - var data TTLTimerData - if err := json.Unmarshal(timer.Data, &data); err != nil { - logger.Error("invalid TTL timer data", zap.ByteString("data", timer.Data)) - return err - } - - job, err := t.adapter.GetJob(ctx, data.TableID, data.PhysicalID, eventID) - if err != nil { - return err - } - - if job == nil { - cancel := false - if !timer.Enable || !t.adapter.CanSubmitJob(data.TableID, data.PhysicalID) { - cancel = true - logger.Warn("cancel current TTL timer event because table's ttl is not enabled") - } - - if t.nowFunc().Sub(timer.EventStart) > 10*time.Minute { - cancel = true - logger.Warn("cancel current TTL timer event because job not submitted for a long time") - } - - if cancel { - return t.cli.CloseTimerEvent(ctx, timer.ID, eventID, timerapi.WithSetWatermark(timer.Watermark)) - } - - logger.Info("submit TTL job for current timer event") - if job, err = t.adapter.SubmitJob(ctx, data.TableID, data.PhysicalID, eventID, timer.EventStart); err != nil { - return err - } - } - - logger = logger.With(zap.String("jobRequestID", job.RequestID)) - logger.Info("start to wait TTL job") - t.wg.Add(1) - t.waitJobLoopCounter++ - go t.waitJobFinished(logger, &data, timer.ID, eventID, timer.EventStart) - return nil -} - -func (t *ttlTimerHook) waitJobFinished(logger *zap.Logger, data *TTLTimerData, timerID string, eventID string, eventStart time.Time) { - defer func() { - t.wg.Done() - logger.Info("stop to wait TTL job") - }() - - ticker := time.NewTicker(t.checkTTLJobInterval) - defer ticker.Stop() - - for { - select { - case <-t.ctx.Done(): - logger.Info("stop waiting TTL job because of context cancelled") - return - case <-ticker.C: - } - - timer, err := t.cli.GetTimerByID(t.ctx, timerID) - if err != nil { - if errors.ErrorEqual(timerapi.ErrTimerNotExist, err) { - logger.Warn("stop waiting TTL job because of timer is deleted") - return - } - - logger.Error("GetTimerByID failed", zap.Error(err)) - continue - } - - if timer.EventID != eventID { - logger.Warn("stop waiting TTL job because of current event id changed", zap.String("newEventID", timer.EventID)) - return - } - - job, err := t.adapter.GetJob(t.ctx, data.TableID, data.PhysicalID, eventID) - if err != nil { - logger.Error("GetJob error", zap.Error(err)) - continue - } - - if job != nil && !job.Finished { - continue - } - - timerSummary := &ttlTimerSummary{ - LastJobRequestID: eventID, - } - - if job != nil { - timerSummary.LastJobSummary = job.Summary - } else { - logger.Warn("job for current TTL timer event not found") - } - - logger.Info("TTL job is finished, close current timer event") - summaryData, err := json.Marshal(timerSummary) - if err != nil { - logger.Error("marshal summary error", zap.Error(err)) - continue - } - - if err = t.cli.CloseTimerEvent(t.ctx, timerID, eventID, timerapi.WithSetWatermark(eventStart), timerapi.WithSetSummaryData(summaryData)); err != nil { - logger.Error("CloseTimerEvent error", zap.Error(err)) - continue - } - - return - } -} - -type ttlTimerRuntime struct { - rt *timerrt.TimerGroupRuntime - store *timerapi.TimerStore - adapter TTLJobAdapter -} - -func newTTLTimerRuntime(store *timerapi.TimerStore, adapter TTLJobAdapter) *ttlTimerRuntime { - return &ttlTimerRuntime{ - store: store, - adapter: adapter, - } -} - -func (r *ttlTimerRuntime) Resume() { - if r.rt != nil { - return - } - - r.rt = timerrt.NewTimerRuntimeBuilder("ttl", r.store). - SetCond(&timerapi.TimerCond{Key: timerapi.NewOptionalVal(timerKeyPrefix), KeyPrefix: true}). - RegisterHookFactory(timerHookClass, func(hookClass string, cli timerapi.TimerClient) timerapi.Hook { - return newTTLTimerHook(r.adapter, cli) - }). - Build() - r.rt.Start() -} - -func (r *ttlTimerRuntime) Pause() { - if rt := r.rt; rt != nil { - r.rt = nil - rt.Stop() - } -} diff --git a/ttl/ttlworker/worker.go b/ttl/ttlworker/worker.go deleted file mode 100644 index c89d90cb1d061..0000000000000 --- a/ttl/ttlworker/worker.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package ttlworker - -import ( - "context" - "sync" - "time" - - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -type workerStatus int - -const ( - workerStatusCreated workerStatus = iota - workerStatusRunning - workerStatusStopping - workerStatusStopped -) - -type worker interface { - Start() - Stop() - Status() workerStatus - Error() error - Send() chan<- interface{} - WaitStopped(ctx context.Context, timeout time.Duration) error -} - -type baseWorker struct { - sync.Mutex - ctx context.Context - cancel func() - ch chan interface{} - loopFunc func() error - - err error - status workerStatus - wg util.WaitGroupWrapper -} - -func (w *baseWorker) init(loop func() error) { - w.ctx, w.cancel = context.WithCancel(context.Background()) - w.status = workerStatusCreated - w.loopFunc = loop - w.ch = make(chan interface{}) -} - -func (w *baseWorker) Start() { - w.Lock() - defer w.Unlock() - if w.status != workerStatusCreated { - return - } - - w.wg.Run(w.loop) - w.status = workerStatusRunning -} - -func (w *baseWorker) Stop() { - w.Lock() - defer w.Unlock() - switch w.status { - case workerStatusCreated: - w.cancel() - w.toStopped(nil) - case workerStatusRunning: - w.cancel() - w.status = workerStatusStopping - } -} - -func (w *baseWorker) Status() workerStatus { - w.Lock() - defer w.Unlock() - return w.status -} - -func (w *baseWorker) Error() error { - w.Lock() - defer w.Unlock() - return w.err -} - -func (w *baseWorker) WaitStopped(ctx context.Context, timeout time.Duration) error { - // consider the situation when the worker has stopped, but the context has also stopped. We should - // return without error - if w.Status() == workerStatusStopped { - return nil - } - - ctx, cancel := context.WithTimeout(ctx, timeout) - go func() { - w.wg.Wait() - cancel() - }() - - <-ctx.Done() - if w.Status() != workerStatusStopped { - return ctx.Err() - } - return nil -} - -func (w *baseWorker) Send() chan<- interface{} { - return w.ch -} - -func (w *baseWorker) loop() { - var err error - defer func() { - if r := recover(); r != nil { - logutil.BgLogger().Info("ttl worker panic", zap.Any("recover", r), zap.Stack("stack")) - } - w.Lock() - w.toStopped(err) - w.Unlock() - }() - err = w.loopFunc() -} - -func (w *baseWorker) toStopped(err error) { - w.status = workerStatusStopped - w.err = err - close(w.ch) -} diff --git a/types/BUILD.bazel b/types/BUILD.bazel deleted file mode 100644 index 6eb1fe6886c96..0000000000000 --- a/types/BUILD.bazel +++ /dev/null @@ -1,116 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package_group( - name = "types_friend", - packages = [ - "-//config/...", - "//...", - ], -) - -go_library( - name = "types", - srcs = [ - "binary_literal.go", - "compare.go", - "context.go", - "convert.go", - "core_time.go", - "datum.go", - "datum_eval.go", - "enum.go", - "errors.go", - "etc.go", - "eval_type.go", - "explain_format.go", - "field_name.go", - "field_type.go", - "field_type_builder.go", - "fsp.go", - "helper.go", - "json_binary.go", - "json_binary_functions.go", - "json_constants.go", - "json_path_expr.go", - "mydecimal.go", - "overflow.go", - "set.go", - "time.go", - ], - importpath = "github.com/pingcap/tidb/types", - visibility = [ - ":types_friend", - ], - deps = [ - "//errno", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//parser/mysql", - "//parser/opcode", - "//parser/terror", - "//parser/types", - "//sessionctx/stmtctx", - "//types/context", - "//util/collate", - "//util/dbterror", - "//util/hack", - "//util/kvcache", - "//util/logutil", - "//util/mathutil", - "//util/parser", - "//util/size", - "//util/stringutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "types_test", - timeout = "short", - srcs = [ - "benchmark_test.go", - "binary_literal_test.go", - "compare_test.go", - "const_test.go", - "convert_test.go", - "core_time_test.go", - "datum_test.go", - "enum_test.go", - "errors_test.go", - "etc_test.go", - "export_test.go", - "field_type_test.go", - "format_test.go", - "fsp_test.go", - "helper_test.go", - "json_binary_functions_test.go", - "json_binary_test.go", - "json_path_expr_test.go", - "main_test.go", - "mydecimal_benchmark_test.go", - "mydecimal_test.go", - "overflow_test.go", - "set_test.go", - "time_test.go", - ], - embed = [":types"], - flaky = True, - shard_count = 50, - deps = [ - "//parser/charset", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//util/collate", - "//util/hack", - "//util/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/types/benchmark_test.go b/types/benchmark_test.go deleted file mode 100644 index 1cd12ac338e2e..0000000000000 --- a/types/benchmark_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package types - -import ( - "math/rand" - "testing" - - "github.com/pingcap/tidb/parser/mysql" -) - -func BenchmarkDefaultTypeForValue(b *testing.B) { - lenNums := 1000000 - numsFull := make([]uint64, lenNums) - nums64k := make([]uint64, lenNums) - nums512 := make([]uint64, lenNums) - - for i := range numsFull { - r := rand.Uint64() - numsFull[i] = r - nums64k[i] = r % 64000 - nums512[i] = r % 512 - } - - b.Run("LenOfUint64_input full range", func(b *testing.B) { - b.StartTimer() - var ft FieldType - for i := 0; i < b.N; i++ { - DefaultTypeForValue(numsFull[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) - } - }) - - b.Run("LenOfUint64_input 0 to 64K ", func(b *testing.B) { - b.StartTimer() - var ft FieldType - for i := 0; i < b.N; i++ { - DefaultTypeForValue(nums64k[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) - } - }) - - b.Run("LenOfUint64_input 0 to 512 ", func(b *testing.B) { - b.StartTimer() - var ft FieldType - for i := 0; i < b.N; i++ { - DefaultTypeForValue(nums512[i%lenNums], &ft, mysql.DefaultCharset, mysql.DefaultCollationName) - } - }) -} diff --git a/types/compare.go b/types/compare.go deleted file mode 100644 index c7a097241cf5e..0000000000000 --- a/types/compare.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "math" - - "github.com/pingcap/tidb/util/collate" -) - -// VecCompareUU returns []int64 comparing the []uint64 x to []uint64 y -func VecCompareUU(x, y []uint64, res []int64) { - n := len(x) - for i := 0; i < n; i++ { - if x[i] < y[i] { - res[i] = -1 - } else if x[i] == y[i] { - res[i] = 0 - } else { - res[i] = 1 - } - } -} - -// VecCompareII returns []int64 comparing the []int64 x to []int64 y -func VecCompareII(x, y, res []int64) { - n := len(x) - for i := 0; i < n; i++ { - if x[i] < y[i] { - res[i] = -1 - } else if x[i] == y[i] { - res[i] = 0 - } else { - res[i] = 1 - } - } -} - -// VecCompareUI returns []int64 comparing the []uint64 x to []int64y -func VecCompareUI(x []uint64, y, res []int64) { - n := len(x) - for i := 0; i < n; i++ { - if y[i] < 0 || x[i] > math.MaxInt64 { - res[i] = 1 - } else if int64(x[i]) < y[i] { - res[i] = -1 - } else if int64(x[i]) == y[i] { - res[i] = 0 - } else { - res[i] = 1 - } - } -} - -// VecCompareIU returns []int64 comparing the []int64 x to []uint64y -func VecCompareIU(x []int64, y []uint64, res []int64) { - n := len(x) - for i := 0; i < n; i++ { - if x[i] < 0 || y[i] > math.MaxInt64 { - res[i] = -1 - } else if x[i] < int64(y[i]) { - res[i] = -1 - } else if x[i] == int64(y[i]) { - res[i] = 0 - } else { - res[i] = 1 - } - } -} - -// CompareString returns an integer comparing the string x to y with the specified collation and length. -func CompareString(x, y, collation string) int { - return collate.GetCollator(collation).Compare(x, y) -} diff --git a/types/context.go b/types/context.go deleted file mode 100644 index 3094f6efffd9b..0000000000000 --- a/types/context.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package types - -import "github.com/pingcap/tidb/types/context" - -// TODO: move a contents in `types/context/context.go` to this file after refactor finished. -// Because package `types` has a dependency on `sessionctx/stmtctx`, we need a separate package `type/context` to define -// context objects during refactor works. - -// Context is an alias of `context.Context` -type Context = context.Context - -// Flags is an alias of `Flags` -type Flags = context.Flags - -// StrictFlags is a flags with a fields unset and has the most strict behavior. -const StrictFlags = context.StrictFlags - -// NewContext creates a new `Context` -var NewContext = context.NewContext diff --git a/types/context/BUILD.bazel b/types/context/BUILD.bazel deleted file mode 100644 index 3e95427a3b537..0000000000000 --- a/types/context/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "context", - srcs = ["context.go"], - importpath = "github.com/pingcap/tidb/types/context", - visibility = ["//visibility:public"], - deps = ["//util/intest"], -) - -go_test( - name = "context_test", - timeout = "short", - srcs = ["context_test.go"], - embed = [":context"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/types/context/context.go b/types/context/context.go deleted file mode 100644 index 2e57b1690e811..0000000000000 --- a/types/context/context.go +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package context - -import ( - "time" - - "github.com/pingcap/tidb/util/intest" -) - -// StrictFlags is a flags with a fields unset and has the most strict behavior. -const StrictFlags Flags = 0 - -// Flags indicates how to handle the conversion of a value. -type Flags uint16 - -const ( - // FlagIgnoreTruncateErr indicates to ignore the truncate error. - // If this flag is set, `FlagTruncateAsWarning` will be ignored. - FlagIgnoreTruncateErr Flags = 1 << iota - // FlagTruncateAsWarning indicates to append the truncate error to warnings instead of returning it to user. - FlagTruncateAsWarning - // FlagClipNegativeToZero indicates to clip the value to zero when casting a negative value to an unsigned integer. - // When this flag is set and the clip happens, an overflow error occurs and how to handle it will be determined by flags - // `FlagIgnoreOverflowError` and `FlagOverflowAsWarning`. - FlagClipNegativeToZero - // FlagIgnoreOverflowError indicates to ignore the overflow error. - // If this flag is set, `FlagOverflowAsWarning` will be ignored. - FlagIgnoreOverflowError - // FlagOverflowAsWarning indicates to append the overflow error to warnings instead of returning it to user. - FlagOverflowAsWarning - // FlagIgnoreZeroDateErr indicates to ignore the zero-date error. - // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_date for details about the "zero-date" error. - // If this flag is set, `FlagZeroDateAsWarning` will be ignored. - FlagIgnoreZeroDateErr - // FlagZeroDateAsWarning indicates to append the zero-date error to warnings instead of returning it to user. - FlagZeroDateAsWarning - // FlagIgnoreZeroInDateErr indicates to ignore the zero-in-date error. - // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_no_zero_in_date for details about the "zero-in-date" error. - FlagIgnoreZeroInDateErr - // FlagZeroInDateAsWarning indicates to append the zero-in-date error to warnings instead of returning it to user. - FlagZeroInDateAsWarning - // FlagIgnoreInvalidDateErr indicates to ignore the invalid-date error. - // See: https://dev.mysql.com/doc/refman/8.0/en/sql-mode.html#sqlmode_allow_invalid_dates for details about the "invalid-date" error. - FlagIgnoreInvalidDateErr - // FlagInvalidDateAsWarning indicates to append the invalid-date error to warnings instead of returning it to user. - FlagInvalidDateAsWarning - // FlagSkipASCIICheck indicates to skip the ASCII check when converting the value to an ASCII string. - FlagSkipASCIICheck - // FlagSkipUTF8Check indicates to skip the UTF8 check when converting the value to an UTF8MB3 string. - FlagSkipUTF8Check - // FlagSkipUTF8MB4Check indicates to skip the UTF8MB4 check when converting the value to an UTF8 string. - FlagSkipUTF8MB4Check -) - -// SkipASCIICheck indicates whether the flag `FlagSkipASCIICheck` is set -func (f Flags) SkipASCIICheck() bool { - return f&FlagSkipASCIICheck != 0 -} - -// WithSkipSACIICheck returns a new flags with `FlagSkipASCIICheck` set/unset according to the skip parameter -func (f Flags) WithSkipSACIICheck(skip bool) Flags { - if skip { - return f | FlagSkipASCIICheck - } - return f &^ FlagSkipASCIICheck -} - -// SkipUTF8Check indicates whether the flag `FlagSkipUTF8Check` is set -func (f Flags) SkipUTF8Check() bool { - return f&FlagSkipUTF8Check != 0 -} - -// WithSkipUTF8Check returns a new flags with `FlagSkipUTF8Check` set/unset according to the skip parameter -func (f Flags) WithSkipUTF8Check(skip bool) Flags { - if skip { - return f | FlagSkipUTF8Check - } - return f &^ FlagSkipUTF8Check -} - -// SkipUTF8MB4Check indicates whether the flag `FlagSkipUTF8MB4Check` is set -func (f Flags) SkipUTF8MB4Check() bool { - return f&FlagSkipUTF8MB4Check != 0 -} - -// WithSkipUTF8MB4Check returns a new flags with `FlagSkipUTF8MB4Check` set/unset according to the skip parameter -func (f Flags) WithSkipUTF8MB4Check(skip bool) Flags { - if skip { - return f | FlagSkipUTF8MB4Check - } - return f &^ FlagSkipUTF8MB4Check -} - -// Context provides the information when converting between different types. -type Context struct { - flags Flags - loc *time.Location - appendWarningFn func(err error) -} - -// NewContext creates a new `Context` -func NewContext(flags Flags, loc *time.Location, appendWarningFn func(err error)) Context { - intest.Assert(loc != nil && appendWarningFn != nil) - return Context{ - flags: flags, - loc: loc, - appendWarningFn: appendWarningFn, - } -} - -// Flags returns the flags of the context -func (c *Context) Flags() Flags { - return c.flags -} - -// WithFlags returns a new context with the flags set to the given value -func (c *Context) WithFlags(f Flags) Context { - ctx := *c - ctx.flags = f - return ctx -} - -// WithLocation returns a new context with the given location -func (c *Context) WithLocation(loc *time.Location) Context { - intest.Assert(loc) - ctx := *c - ctx.loc = loc - return ctx -} - -// Location returns the location of the context -func (c *Context) Location() *time.Location { - intest.Assert(c.loc) - if c.loc == nil { - // c.loc should always not be nil, just make the code safe here. - return time.UTC - } - return c.loc -} - -// AppendWarning appends the error to warning. If the inner `appendWarningFn` is nil, do nothing. -func (c *Context) AppendWarning(err error) { - intest.Assert(c.appendWarningFn != nil) - if fn := c.appendWarningFn; fn != nil { - // appendWarningFn should always not be nil, check fn != nil here to just make code safe. - fn(err) - } -} - -// AppendWarningFunc returns the inner `appendWarningFn` -func (c *Context) AppendWarningFunc() func(err error) { - return c.appendWarningFn -} diff --git a/types/convert.go b/types/convert.go deleted file mode 100644 index 9791aa3160642..0000000000000 --- a/types/convert.go +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2014 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package types - -import ( - "math" - "math/big" - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/hack" -) - -func truncateStr(str string, flen int) string { - if flen != UnspecifiedLength && len(str) > flen { - str = str[:flen] - } - return str -} - -// IntergerUnsignedUpperBound indicates the max uint64 values of different mysql types. -func IntergerUnsignedUpperBound(intType byte) uint64 { - switch intType { - case mysql.TypeTiny: - return math.MaxUint8 - case mysql.TypeShort: - return math.MaxUint16 - case mysql.TypeInt24: - return mysql.MaxUint24 - case mysql.TypeLong: - return math.MaxUint32 - case mysql.TypeLonglong: - return math.MaxUint64 - case mysql.TypeBit: - return math.MaxUint64 - case mysql.TypeEnum: - // enum can have at most 65535 distinct elements - // it would be better to use len(FieldType.GetElems()), but we only have a byte type here - return 65535 - case mysql.TypeSet: - return math.MaxUint64 - default: - panic("Input byte is not a mysql type") - } -} - -// IntergerSignedUpperBound indicates the max int64 values of different mysql types. -func IntergerSignedUpperBound(intType byte) int64 { - switch intType { - case mysql.TypeTiny: - return math.MaxInt8 - case mysql.TypeShort: - return math.MaxInt16 - case mysql.TypeInt24: - return mysql.MaxInt24 - case mysql.TypeLong: - return math.MaxInt32 - case mysql.TypeLonglong: - return math.MaxInt64 - case mysql.TypeEnum: - // enum can have at most 65535 distinct elements - // it would be better to use len(FieldType.GetElems()), but we only have a byte type here - return 65535 - default: - panic("Input byte is not a mysql int type") - } -} - -// IntergerSignedLowerBound indicates the min int64 values of different mysql types. -func IntergerSignedLowerBound(intType byte) int64 { - switch intType { - case mysql.TypeTiny: - return math.MinInt8 - case mysql.TypeShort: - return math.MinInt16 - case mysql.TypeInt24: - return mysql.MinInt24 - case mysql.TypeLong: - return math.MinInt32 - case mysql.TypeLonglong: - return math.MinInt64 - case mysql.TypeEnum: - return 0 - default: - panic("Input byte is not a mysql type") - } -} - -// ConvertFloatToInt converts a float64 value to a int value. -// `tp` is used in err msg, if there is overflow, this func will report err according to `tp` -func ConvertFloatToInt(fval float64, lowerBound, upperBound int64, tp byte) (int64, error) { - val := RoundFloat(fval) - if val < float64(lowerBound) { - return lowerBound, overflow(val, tp) - } - - if val >= float64(upperBound) { - if val == float64(upperBound) { - return upperBound, nil - } - return upperBound, overflow(val, tp) - } - return int64(val), nil -} - -// ConvertIntToInt converts an int value to another int value of different precision. -func ConvertIntToInt(val int64, lowerBound int64, upperBound int64, tp byte) (int64, error) { - if val < lowerBound { - return lowerBound, overflow(val, tp) - } - - if val > upperBound { - return upperBound, overflow(val, tp) - } - - return val, nil -} - -// ConvertUintToInt converts an uint value to an int value. -func ConvertUintToInt(val uint64, upperBound int64, tp byte) (int64, error) { - if val > uint64(upperBound) { - return upperBound, overflow(val, tp) - } - - return int64(val), nil -} - -// ConvertIntToUint converts an int value to an uint value. -func ConvertIntToUint(sc *stmtctx.StatementContext, val int64, upperBound uint64, tp byte) (uint64, error) { - if sc.ShouldClipToZero() && val < 0 { - return 0, overflow(val, tp) - } - - if uint64(val) > upperBound { - return upperBound, overflow(val, tp) - } - - return uint64(val), nil -} - -// ConvertUintToUint converts an uint value to another uint value of different precision. -func ConvertUintToUint(val uint64, upperBound uint64, tp byte) (uint64, error) { - if val > upperBound { - return upperBound, overflow(val, tp) - } - - return val, nil -} - -// ConvertFloatToUint converts a float value to an uint value. -func ConvertFloatToUint(sc *stmtctx.StatementContext, fval float64, upperBound uint64, tp byte) (uint64, error) { - val := RoundFloat(fval) - if val < 0 { - if sc.ShouldClipToZero() { - return 0, overflow(val, tp) - } - return uint64(int64(val)), overflow(val, tp) - } - - ret, acc := new(big.Float).SetFloat64(val).Uint64() - if acc == big.Below || ret > upperBound { - return upperBound, overflow(val, tp) - } - return ret, nil -} - -// convertScientificNotation converts a decimal string with scientific notation to a normal decimal string. -// 1E6 => 1000000, .12345E+5 => 12345 -func convertScientificNotation(str string) (string, error) { - // https://golang.org/ref/spec#Floating-point_literals - eIdx := -1 - point := -1 - for i := 0; i < len(str); i++ { - if str[i] == '.' { - point = i - } - if str[i] == 'e' || str[i] == 'E' { - eIdx = i - if point == -1 { - point = i - } - break - } - } - if eIdx == -1 { - return str, nil - } - exp, err := strconv.ParseInt(str[eIdx+1:], 10, 64) - if err != nil { - return "", errors.WithStack(err) - } - - f := str[:eIdx] - if exp == 0 { - return f, nil - } else if exp > 0 { // move point right - if point+int(exp) == len(f)-1 { // 123.456 >> 3 = 123456. = 123456 - return f[:point] + f[point+1:], nil - } else if point+int(exp) < len(f)-1 { // 123.456 >> 2 = 12345.6 - return f[:point] + f[point+1:point+1+int(exp)] + "." + f[point+1+int(exp):], nil - } - // 123.456 >> 5 = 12345600 - return f[:point] + f[point+1:] + strings.Repeat("0", point+int(exp)-len(f)+1), nil - } else { // move point left - exp = -exp - if int(exp) < point { // 123.456 << 2 = 1.23456 - return f[:point-int(exp)] + "." + f[point-int(exp):point] + f[point+1:], nil - } - // 123.456 << 5 = 0.00123456 - return "0." + strings.Repeat("0", int(exp)-point) + f[:point] + f[point+1:], nil - } -} - -func convertDecimalStrToUint(sc *stmtctx.StatementContext, str string, upperBound uint64, tp byte) (uint64, error) { - str, err := convertScientificNotation(str) - if err != nil { - return 0, err - } - - var intStr, fracStr string - p := strings.Index(str, ".") - if p == -1 { - intStr = str - } else { - intStr = str[:p] - fracStr = str[p+1:] - } - intStr = strings.TrimLeft(intStr, "0") - if intStr == "" { - intStr = "0" - } - if intStr[0] == '-' { - return 0, overflow(str, tp) - } - - var round uint64 - if fracStr != "" && fracStr[0] >= '5' { - round++ - } - - upperStr := strconv.FormatUint(upperBound-round, 10) - if len(intStr) > len(upperStr) || - (len(intStr) == len(upperStr) && intStr > upperStr) { - return upperBound, overflow(str, tp) - } - - val, err := strconv.ParseUint(intStr, 10, 64) - if err != nil { - return val, overflow(str, tp) - } - return val + round, nil -} - -// ConvertDecimalToUint converts a decimal to a uint by converting it to a string first to avoid float overflow (#10181). -func ConvertDecimalToUint(sc *stmtctx.StatementContext, d *MyDecimal, upperBound uint64, tp byte) (uint64, error) { - return convertDecimalStrToUint(sc, string(d.ToString()), upperBound, tp) -} - -// StrToInt converts a string to an integer at the best-effort. -func StrToInt(sc *stmtctx.StatementContext, str string, isFuncCast bool) (int64, error) { - str = strings.TrimSpace(str) - validPrefix, err := getValidIntPrefix(sc, str, isFuncCast) - iVal, err1 := strconv.ParseInt(validPrefix, 10, 64) - if err1 != nil { - return iVal, ErrOverflow.GenWithStackByArgs("BIGINT", validPrefix) - } - return iVal, errors.Trace(err) -} - -// StrToUint converts a string to an unsigned integer at the best-effort. -func StrToUint(sc *stmtctx.StatementContext, str string, isFuncCast bool) (uint64, error) { - str = strings.TrimSpace(str) - validPrefix, err := getValidIntPrefix(sc, str, isFuncCast) - uVal := uint64(0) - hasParseErr := false - - if validPrefix[0] == '-' { - // only `-000*` is valid to be converted into unsigned integer - for _, v := range validPrefix[1:] { - if v != '0' { - hasParseErr = true - break - } - } - } else { - if validPrefix[0] == '+' { - validPrefix = validPrefix[1:] - } - v, e := strconv.ParseUint(validPrefix, 10, 64) - uVal, hasParseErr = v, e != nil - } - - if hasParseErr { - return uVal, ErrOverflow.GenWithStackByArgs("BIGINT UNSIGNED", validPrefix) - } - return uVal, errors.Trace(err) -} - -// StrToDateTime converts str to MySQL DateTime. -func StrToDateTime(sc *stmtctx.StatementContext, str string, fsp int) (Time, error) { - return ParseTime(sc, str, mysql.TypeDatetime, fsp, nil) -} - -// StrToDuration converts str to Duration. It returns Duration in normal case, -// and returns Time when str is in datetime format. -// when isDuration is true, the d is returned, when it is false, the t is returned. -// See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html. -func StrToDuration(sc *stmtctx.StatementContext, str string, fsp int) (d Duration, t Time, isDuration bool, err error) { - str = strings.TrimSpace(str) - length := len(str) - if length > 0 && str[0] == '-' { - length-- - } - if n := strings.IndexByte(str, '.'); n >= 0 { - length = length - len(str[n:]) - } - // Timestamp format is 'YYYYMMDDHHMMSS' or 'YYMMDDHHMMSS', which length is 12. - // See #3923, it explains what we do here. - if length >= 12 { - t, err = StrToDateTime(sc, str, fsp) - if err == nil { - return d, t, false, nil - } - } - - d, _, err = ParseDuration(sc, str, fsp) - if ErrTruncatedWrongVal.Equal(err) { - err = sc.HandleTruncate(err) - } - return d, t, true, errors.Trace(err) -} - -// NumberToDuration converts number to Duration. -func NumberToDuration(number int64, fsp int) (Duration, error) { - if number > TimeMaxValue { - // Try to parse DATETIME. - if number >= 10000000000 { // '2001-00-00 00-00-00' - if t, err := ParseDatetimeFromNum(nil, number); err == nil { - dur, err1 := t.ConvertToDuration() - return dur, errors.Trace(err1) - } - } - dur := MaxMySQLDuration(fsp) - return dur, ErrOverflow.GenWithStackByArgs("Duration", strconv.Itoa(int(number))) - } else if number < -TimeMaxValue { - dur := MaxMySQLDuration(fsp) - dur.Duration = -dur.Duration - return dur, ErrOverflow.GenWithStackByArgs("Duration", strconv.Itoa(int(number))) - } - var neg bool - if neg = number < 0; neg { - number = -number - } - - if number/10000 > TimeMaxHour || number%100 >= 60 || (number/100)%100 >= 60 { - return ZeroDuration, errors.Trace(ErrTruncatedWrongVal.GenWithStackByArgs(TimeStr, strconv.FormatInt(number, 10))) - } - dur := NewDuration(int(number/10000), int((number/100)%100), int(number%100), 0, fsp) - if neg { - dur.Duration = -dur.Duration - } - return dur, nil -} - -// getValidIntPrefix gets prefix of the string which can be successfully parsed as int. -func getValidIntPrefix(sc *stmtctx.StatementContext, str string, isFuncCast bool) (string, error) { - if !isFuncCast { - floatPrefix, err := getValidFloatPrefix(sc, str, isFuncCast) - if err != nil { - return floatPrefix, errors.Trace(err) - } - return floatStrToIntStr(sc, floatPrefix, str) - } - - validLen := 0 - - for i := 0; i < len(str); i++ { - c := str[i] - if (c == '+' || c == '-') && i == 0 { - continue - } - - if c >= '0' && c <= '9' { - validLen = i + 1 - continue - } - - break - } - valid := str[:validLen] - if valid == "" { - valid = "0" - } - if validLen == 0 || validLen != len(str) { - return valid, errors.Trace(sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", str))) - } - return valid, nil -} - -// roundIntStr is to round a **valid int string** base on the number following dot. -func roundIntStr(numNextDot byte, intStr string) string { - if numNextDot < '5' { - return intStr - } - retStr := []byte(intStr) - idx := len(intStr) - 1 - for ; idx >= 1; idx-- { - if retStr[idx] != '9' { - retStr[idx]++ - break - } - retStr[idx] = '0' - } - if idx == 0 { - if intStr[0] == '9' { - retStr[0] = '1' - retStr = append(retStr, '0') - } else if isDigit(intStr[0]) { - retStr[0]++ - } else { - retStr[1] = '1' - retStr = append(retStr, '0') - } - } - return string(retStr) -} - -// floatStrToIntStr converts a valid float string into valid integer string which can be parsed by -// strconv.ParseInt, we can't parse float first then convert it to string because precision will -// be lost. For example, the string value "18446744073709551615" which is the max number of unsigned -// int will cause some precision to lose. intStr[0] may be a positive and negative sign like '+' or '-'. -// -// This func will find serious overflow such as the len of intStr > 20 (without prefix `+/-`) -// however, it will not check whether the intStr overflow BIGINT. -func floatStrToIntStr(sc *stmtctx.StatementContext, validFloat string, oriStr string) (intStr string, _ error) { - var dotIdx = -1 - var eIdx = -1 - for i := 0; i < len(validFloat); i++ { - switch validFloat[i] { - case '.': - dotIdx = i - case 'e', 'E': - eIdx = i - } - } - if eIdx == -1 { - if dotIdx == -1 { - return validFloat, nil - } - var digits []byte - if validFloat[0] == '-' || validFloat[0] == '+' { - dotIdx-- - digits = []byte(validFloat[1:]) - } else { - digits = []byte(validFloat) - } - if dotIdx == 0 { - intStr = "0" - } else { - intStr = string(digits)[:dotIdx] - } - if len(digits) > dotIdx+1 { - intStr = roundIntStr(digits[dotIdx+1], intStr) - } - if (len(intStr) > 1 || intStr[0] != '0') && validFloat[0] == '-' { - intStr = "-" + intStr - } - return intStr, nil - } - // intCnt and digits contain the prefix `+/-` if validFloat[0] is `+/-` - var intCnt int - digits := make([]byte, 0, len(validFloat)) - if dotIdx == -1 { - digits = append(digits, validFloat[:eIdx]...) - intCnt = len(digits) - } else { - digits = append(digits, validFloat[:dotIdx]...) - intCnt = len(digits) - digits = append(digits, validFloat[dotIdx+1:eIdx]...) - } - exp, err := strconv.Atoi(validFloat[eIdx+1:]) - if err != nil { - return validFloat, errors.Trace(err) - } - intCnt += exp - if exp >= 0 && (intCnt > 21 || intCnt < 0) { - // MaxInt64 has 19 decimal digits. - // MaxUint64 has 20 decimal digits. - // And the intCnt may contain the len of `+/-`, - // so I use 21 here as the early detection. - sc.AppendWarning(ErrOverflow.GenWithStackByArgs("BIGINT", oriStr)) - return validFloat[:eIdx], nil - } - if intCnt <= 0 { - intStr = "0" - if intCnt == 0 && len(digits) > 0 && isDigit(digits[0]) { - intStr = roundIntStr(digits[0], intStr) - } - return intStr, nil - } - if intCnt == 1 && (digits[0] == '-' || digits[0] == '+') { - intStr = "0" - if len(digits) > 1 { - intStr = roundIntStr(digits[1], intStr) - } - if intStr[0] == '1' { - intStr = string(digits[:1]) + intStr - } - return intStr, nil - } - if intCnt <= len(digits) { - intStr = string(digits[:intCnt]) - if intCnt < len(digits) { - intStr = roundIntStr(digits[intCnt], intStr) - } - } else { - // convert scientific notation decimal number - extraZeroCount := intCnt - len(digits) - intStr = string(digits) + strings.Repeat("0", extraZeroCount) - } - return intStr, nil -} - -// StrToFloat converts a string to a float64 at the best-effort. -func StrToFloat(sc *stmtctx.StatementContext, str string, isFuncCast bool) (float64, error) { - str = strings.TrimSpace(str) - validStr, err := getValidFloatPrefix(sc, str, isFuncCast) - f, err1 := strconv.ParseFloat(validStr, 64) - if err1 != nil { - if err2, ok := err1.(*strconv.NumError); ok { - // value will truncate to MAX/MIN if out of range. - if err2.Err == strconv.ErrRange { - err1 = sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("DOUBLE", str)) - if math.IsInf(f, 1) { - f = math.MaxFloat64 - } else if math.IsInf(f, -1) { - f = -math.MaxFloat64 - } - } - } - return f, errors.Trace(err1) - } - return f, errors.Trace(err) -} - -// ConvertJSONToInt64 casts JSON into int64. -func ConvertJSONToInt64(sc *stmtctx.StatementContext, j BinaryJSON, unsigned bool) (int64, error) { - return ConvertJSONToInt(sc, j, unsigned, mysql.TypeLonglong) -} - -// ConvertJSONToInt casts JSON into int by type. -func ConvertJSONToInt(sc *stmtctx.StatementContext, j BinaryJSON, unsigned bool, tp byte) (int64, error) { - switch j.TypeCode { - case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: - return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", j.String())) - case JSONTypeCodeLiteral: - switch j.Value[0] { - case JSONLiteralFalse: - return 0, nil - case JSONLiteralNil: - return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("INTEGER", j.String())) - default: - return 1, nil - } - case JSONTypeCodeInt64: - i := j.GetInt64() - if unsigned { - uBound := IntergerUnsignedUpperBound(tp) - u, err := ConvertIntToUint(sc, i, uBound, tp) - return int64(u), sc.HandleOverflow(err, err) - } - - lBound := IntergerSignedLowerBound(tp) - uBound := IntergerSignedUpperBound(tp) - i, err := ConvertIntToInt(i, lBound, uBound, tp) - return i, sc.HandleOverflow(err, err) - case JSONTypeCodeUint64: - u := j.GetUint64() - if unsigned { - uBound := IntergerUnsignedUpperBound(tp) - u, err := ConvertUintToUint(u, uBound, tp) - return int64(u), sc.HandleOverflow(err, err) - } - - uBound := IntergerSignedUpperBound(tp) - i, err := ConvertUintToInt(u, uBound, tp) - return i, sc.HandleOverflow(err, err) - case JSONTypeCodeFloat64: - f := j.GetFloat64() - if !unsigned { - lBound := IntergerSignedLowerBound(tp) - uBound := IntergerSignedUpperBound(tp) - u, e := ConvertFloatToInt(f, lBound, uBound, tp) - return u, sc.HandleOverflow(e, e) - } - bound := IntergerUnsignedUpperBound(tp) - u, err := ConvertFloatToUint(sc, f, bound, tp) - return int64(u), sc.HandleOverflow(err, err) - case JSONTypeCodeString: - str := string(hack.String(j.GetString())) - if !unsigned { - r, e := StrToInt(sc, str, false) - return r, sc.HandleOverflow(e, e) - } - u, err := StrToUint(sc, str, false) - return int64(u), sc.HandleOverflow(err, err) - } - return 0, errors.New("Unknown type code in JSON") -} - -// ConvertJSONToFloat casts JSON into float64. -func ConvertJSONToFloat(sc *stmtctx.StatementContext, j BinaryJSON) (float64, error) { - switch j.TypeCode { - case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: - return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("FLOAT", j.String())) - case JSONTypeCodeLiteral: - switch j.Value[0] { - case JSONLiteralFalse: - return 0, nil - case JSONLiteralNil: - return 0, sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("FLOAT", j.String())) - default: - return 1, nil - } - case JSONTypeCodeInt64: - return float64(j.GetInt64()), nil - case JSONTypeCodeUint64: - return float64(j.GetUint64()), nil - case JSONTypeCodeFloat64: - return j.GetFloat64(), nil - case JSONTypeCodeString: - str := string(hack.String(j.GetString())) - return StrToFloat(sc, str, false) - } - return 0, errors.New("Unknown type code in JSON") -} - -// ConvertJSONToDecimal casts JSON into decimal. -func ConvertJSONToDecimal(sc *stmtctx.StatementContext, j BinaryJSON) (*MyDecimal, error) { - var err error = nil - res := new(MyDecimal) - switch j.TypeCode { - case JSONTypeCodeObject, JSONTypeCodeArray, JSONTypeCodeOpaque, JSONTypeCodeDate, JSONTypeCodeDatetime, JSONTypeCodeTimestamp, JSONTypeCodeDuration: - err = ErrTruncatedWrongVal.GenWithStackByArgs("DECIMAL", j.String()) - case JSONTypeCodeLiteral: - switch j.Value[0] { - case JSONLiteralFalse: - res = res.FromInt(0) - case JSONLiteralNil: - err = ErrTruncatedWrongVal.GenWithStackByArgs("DECIMAL", j.String()) - default: - res = res.FromInt(1) - } - case JSONTypeCodeInt64: - res = res.FromInt(j.GetInt64()) - case JSONTypeCodeUint64: - res = res.FromUint(j.GetUint64()) - case JSONTypeCodeFloat64: - err = res.FromFloat64(j.GetFloat64()) - case JSONTypeCodeString: - err = res.FromString(j.GetString()) - } - err = sc.HandleTruncate(err) - if err != nil { - return res, errors.Trace(err) - } - return res, errors.Trace(err) -} - -// getValidFloatPrefix gets prefix of string which can be successfully parsed as float. -func getValidFloatPrefix(sc *stmtctx.StatementContext, s string, isFuncCast bool) (valid string, err error) { - if isFuncCast && s == "" { - return "0", nil - } - - var ( - sawDot bool - sawDigit bool - validLen int - eIdx = -1 - ) - for i := 0; i < len(s); i++ { - c := s[i] - if c == '+' || c == '-' { - if i != 0 && i != eIdx+1 { // "1e+1" is valid. - break - } - } else if c == '.' { - if sawDot || eIdx > 0 { // "1.1." or "1e1.1" - break - } - sawDot = true - if sawDigit { // "123." is valid. - validLen = i + 1 - } - } else if c == 'e' || c == 'E' { - if !sawDigit { // "+.e" - break - } - if eIdx != -1 { // "1e5e" - break - } - eIdx = i - } else if c == '\u0000' { - s = s[:validLen] - break - } else if c < '0' || c > '9' { - break - } else { - sawDigit = true - validLen = i + 1 - } - } - valid = s[:validLen] - if valid == "" { - valid = "0" - } - if validLen == 0 || validLen != len(s) { - err = errors.Trace(sc.HandleTruncate(ErrTruncatedWrongVal.GenWithStackByArgs("DOUBLE", s))) - } - return valid, err -} - -// ToString converts an interface to a string. -func ToString(value interface{}) (string, error) { - switch v := value.(type) { - case bool: - if v { - return "1", nil - } - return "0", nil - case int: - return strconv.FormatInt(int64(v), 10), nil - case int64: - return strconv.FormatInt(v, 10), nil - case uint64: - return strconv.FormatUint(v, 10), nil - case float32: - return strconv.FormatFloat(float64(v), 'f', -1, 32), nil - case float64: - return strconv.FormatFloat(v, 'f', -1, 64), nil - case string: - return v, nil - case []byte: - return string(v), nil - case Time: - return v.String(), nil - case Duration: - return v.String(), nil - case *MyDecimal: - return v.String(), nil - case BinaryLiteral: - return v.ToString(), nil - case Enum: - return v.String(), nil - case Set: - return v.String(), nil - case BinaryJSON: - return v.String(), nil - default: - return "", errors.Errorf("cannot convert %v(type %T) to string", value, value) - } -} diff --git a/types/errors.go b/types/errors.go deleted file mode 100644 index 0542f3f5f2bd0..0000000000000 --- a/types/errors.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package types - -import ( - mysql "github.com/pingcap/tidb/errno" - parser_types "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/util/dbterror" -) - -// const strings for ErrWrongValue -const ( - DateTimeStr = "datetime" - DateStr = "date" - TimeStr = "time" - TimestampStr = "timestamp" -) - -var ( - // ErrInvalidDefault is returned when meet a invalid default value. - ErrInvalidDefault = parser_types.ErrInvalidDefault - // ErrDataTooLong is returned when converts a string value that is longer than field type length. - ErrDataTooLong = dbterror.ClassTypes.NewStd(mysql.ErrDataTooLong) - // ErrIllegalValueForType is returned when value of type is illegal. - ErrIllegalValueForType = dbterror.ClassTypes.NewStd(mysql.ErrIllegalValueForType) - // ErrTruncated is returned when data has been truncated during conversion. - ErrTruncated = dbterror.ClassTypes.NewStd(mysql.WarnDataTruncated) - // ErrOverflow is returned when data is out of range for a field type. - ErrOverflow = dbterror.ClassTypes.NewStd(mysql.ErrDataOutOfRange) - // ErrDivByZero is return when do division by 0. - ErrDivByZero = dbterror.ClassTypes.NewStd(mysql.ErrDivisionByZero) - // ErrTooBigDisplayWidth is return when display width out of range for column. - ErrTooBigDisplayWidth = dbterror.ClassTypes.NewStd(mysql.ErrTooBigDisplaywidth) - // ErrTooBigFieldLength is return when column length too big for column. - ErrTooBigFieldLength = dbterror.ClassTypes.NewStd(mysql.ErrTooBigFieldlength) - // ErrTooBigSet is returned when too many strings for column. - ErrTooBigSet = dbterror.ClassTypes.NewStd(mysql.ErrTooBigSet) - // ErrTooBigScale is returned when type DECIMAL/NUMERIC scale is bigger than mysql.MaxDecimalScale. - ErrTooBigScale = dbterror.ClassTypes.NewStd(mysql.ErrTooBigScale) - // ErrTooBigPrecision is returned when type DECIMAL/NUMERIC or DATETIME precision is bigger than mysql.MaxDecimalWidth or types.MaxFsp - ErrTooBigPrecision = dbterror.ClassTypes.NewStd(mysql.ErrTooBigPrecision) - // ErrBadNumber is return when parsing an invalid binary decimal number. - ErrBadNumber = dbterror.ClassTypes.NewStd(mysql.ErrBadNumber) - // ErrInvalidFieldSize is returned when the precision of a column is out of range. - ErrInvalidFieldSize = dbterror.ClassTypes.NewStd(mysql.ErrInvalidFieldSize) - // ErrMBiggerThanD is returned when precision less than the scale. - ErrMBiggerThanD = dbterror.ClassTypes.NewStd(mysql.ErrMBiggerThanD) - // ErrWarnDataOutOfRange is returned when the value in a numeric column that is outside the permissible range of the column data type. - // See https://dev.mysql.com/doc/refman/5.5/en/out-of-range-and-overflow.html for details - ErrWarnDataOutOfRange = dbterror.ClassTypes.NewStd(mysql.ErrWarnDataOutOfRange) - // ErrDuplicatedValueInType is returned when enum column has duplicated value. - ErrDuplicatedValueInType = dbterror.ClassTypes.NewStd(mysql.ErrDuplicatedValueInType) - // ErrDatetimeFunctionOverflow is returned when the calculation in datetime function cause overflow. - ErrDatetimeFunctionOverflow = dbterror.ClassTypes.NewStd(mysql.ErrDatetimeFunctionOverflow) - // ErrCastAsSignedOverflow is returned when positive out-of-range integer, and convert to it's negative complement. - ErrCastAsSignedOverflow = dbterror.ClassTypes.NewStd(mysql.ErrCastAsSignedOverflow) - // ErrCastNegIntAsUnsigned is returned when a negative integer be casted to an unsigned int. - ErrCastNegIntAsUnsigned = dbterror.ClassTypes.NewStd(mysql.ErrCastNegIntAsUnsigned) - // ErrInvalidYearFormat is returned when the input is not a valid year format. - ErrInvalidYearFormat = dbterror.ClassTypes.NewStd(mysql.ErrInvalidYearFormat) - // ErrInvalidYear is returned when the input value is not a valid year. - ErrInvalidYear = dbterror.ClassTypes.NewStd(mysql.ErrInvalidYear) - // ErrTruncatedWrongVal is returned when data has been truncated during conversion. - ErrTruncatedWrongVal = dbterror.ClassTypes.NewStd(mysql.ErrTruncatedWrongValue) - // ErrInvalidWeekModeFormat is returned when the week mode is wrong. - ErrInvalidWeekModeFormat = dbterror.ClassTypes.NewStd(mysql.ErrInvalidWeekModeFormat) - // ErrWrongFieldSpec is returned when the column specifier incorrect. - ErrWrongFieldSpec = dbterror.ClassTypes.NewStd(mysql.ErrWrongFieldSpec) - // ErrSyntax is returned when the syntax is not allowed. - ErrSyntax = dbterror.ClassTypes.NewStdErr(mysql.ErrParse, mysql.MySQLErrName[mysql.ErrSyntax]) - // ErrWrongValue is returned when the input value is in wrong format. - ErrWrongValue = dbterror.ClassTypes.NewStdErr(mysql.ErrTruncatedWrongValue, mysql.MySQLErrName[mysql.ErrWrongValue]) - // ErrWrongValue2 is returned when the input value is in wrong format. - ErrWrongValue2 = dbterror.ClassTypes.NewStdErr(mysql.ErrWrongValue, mysql.MySQLErrName[mysql.ErrWrongValue]) - // ErrWrongValueForType is returned when the input value is in wrong format for function. - ErrWrongValueForType = dbterror.ClassTypes.NewStdErr(mysql.ErrWrongValueForType, mysql.MySQLErrName[mysql.ErrWrongValueForType]) - // ErrPartitionStatsMissing is returned when the partition-level stats is missing and the build global-level stats fails. - // Put this error here is to prevent `import cycle not allowed`. - ErrPartitionStatsMissing = dbterror.ClassTypes.NewStd(mysql.ErrPartitionStatsMissing) - // ErrPartitionColumnStatsMissing is returned when the partition-level column stats is missing and the build global-level stats fails. - // Put this error here is to prevent `import cycle not allowed`. - ErrPartitionColumnStatsMissing = dbterror.ClassTypes.NewStd(mysql.ErrPartitionColumnStatsMissing) - // ErrIncorrectDatetimeValue is returned when the input value is in wrong format for datetime. - ErrIncorrectDatetimeValue = dbterror.ClassTypes.NewStd(mysql.ErrIncorrectDatetimeValue) -) diff --git a/types/errors_test.go b/types/errors_test.go deleted file mode 100644 index 00e2dd4e211e4..0000000000000 --- a/types/errors_test.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package types - -import ( - "testing" - - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/require" -) - -func TestError(t *testing.T) { - kvErrs := []*terror.Error{ - ErrInvalidDefault, - ErrDataTooLong, - ErrIllegalValueForType, - ErrTruncated, - ErrOverflow, - ErrDivByZero, - ErrTooBigDisplayWidth, - ErrTooBigFieldLength, - ErrTooBigSet, - ErrTooBigScale, - ErrTooBigPrecision, - ErrBadNumber, - ErrInvalidFieldSize, - ErrMBiggerThanD, - ErrWarnDataOutOfRange, - ErrDuplicatedValueInType, - ErrDatetimeFunctionOverflow, - ErrCastAsSignedOverflow, - ErrCastNegIntAsUnsigned, - ErrInvalidYearFormat, - ErrTruncatedWrongVal, - ErrInvalidWeekModeFormat, - ErrWrongValue, - } - - for _, err := range kvErrs { - code := terror.ToSQLError(err).Code - require.Equalf(t, code, uint16(err.Code()), "err: %v", err) - } -} diff --git a/types/etc.go b/types/etc.go deleted file mode 100644 index b8ad064d5e72b..0000000000000 --- a/types/etc.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Copyright 2014 The ql Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSES/QL-LICENSE file. - -package types - -import ( - "io" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/opcode" - "github.com/pingcap/tidb/parser/terror" - ast "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/util/collate" -) - -// IsTypeBlob returns a boolean indicating whether the tp is a blob type. -var IsTypeBlob = ast.IsTypeBlob - -// IsTypeChar returns a boolean indicating -// whether the tp is the char type like a string type or a varchar type. -var IsTypeChar = ast.IsTypeChar - -// IsTypeVarchar returns a boolean indicating -// whether the tp is the varchar type like a varstring type or a varchar type. -func IsTypeVarchar(tp byte) bool { - return tp == mysql.TypeVarString || tp == mysql.TypeVarchar -} - -// IsTypeUnspecified returns a boolean indicating whether the tp is the Unspecified type. -func IsTypeUnspecified(tp byte) bool { - return tp == mysql.TypeUnspecified -} - -// IsTypePrefixable returns a boolean indicating -// whether an index on a column with the tp can be defined with a prefix. -func IsTypePrefixable(tp byte) bool { - return IsTypeBlob(tp) || IsTypeChar(tp) -} - -// IsTypeFractionable returns a boolean indicating -// whether the tp can has time fraction. -func IsTypeFractionable(tp byte) bool { - return tp == mysql.TypeDatetime || tp == mysql.TypeDuration || tp == mysql.TypeTimestamp -} - -// IsTypeTime returns a boolean indicating -// whether the tp is time type like datetime, date or timestamp. -func IsTypeTime(tp byte) bool { - return tp == mysql.TypeDatetime || tp == mysql.TypeDate || tp == mysql.TypeTimestamp -} - -// IsTypeFloat indicates whether the type is TypeFloat -func IsTypeFloat(tp byte) bool { - return tp == mysql.TypeFloat -} - -// IsTypeInteger returns a boolean indicating whether the tp is integer type. -func IsTypeInteger(tp byte) bool { - switch tp { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: - return true - } - return false -} - -// IsTypeNumeric returns a boolean indicating whether the tp is numeric type. -func IsTypeNumeric(tp byte) bool { - switch tp { - case mysql.TypeBit, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeNewDecimal, - mysql.TypeFloat, mysql.TypeDouble, mysql.TypeShort: - return true - } - return false -} - -// IsTypeBit returns a boolean indicating whether the tp is bit type. -func IsTypeBit(ft *FieldType) bool { - return ft.GetType() == mysql.TypeBit -} - -// IsTemporalWithDate returns a boolean indicating -// whether the tp is time type with date. -func IsTemporalWithDate(tp byte) bool { - return IsTypeTime(tp) -} - -// IsBinaryStr returns a boolean indicating -// whether the field type is a binary string type. -func IsBinaryStr(ft *FieldType) bool { - return ft.GetCollate() == charset.CollationBin && IsString(ft.GetType()) -} - -// IsNonBinaryStr returns a boolean indicating -// whether the field type is a non-binary string type. -func IsNonBinaryStr(ft *FieldType) bool { - if ft.GetCollate() != charset.CollationBin && IsString(ft.GetType()) { - return true - } - return false -} - -// NeedRestoredData returns if a type needs restored data. -// If the type is char and the collation is _bin, NeedRestoredData() returns false. -func NeedRestoredData(ft *FieldType) bool { - if collate.NewCollationEnabled() && - IsNonBinaryStr(ft) && - (!collate.IsBinCollation(ft.GetCollate()) || IsTypeVarchar(ft.GetType())) && - ft.GetCollate() != "utf8mb4_0900_bin" { - return true - } - return false -} - -// IsString returns a boolean indicating -// whether the field type is a string type. -func IsString(tp byte) bool { - return IsTypeChar(tp) || IsTypeBlob(tp) || IsTypeVarchar(tp) || IsTypeUnspecified(tp) -} - -// IsStringKind returns a boolean indicating whether the tp is a string type. -func IsStringKind(kind byte) bool { - return kind == KindString || kind == KindBytes -} - -var kind2Str = map[byte]string{ - KindNull: "null", - KindInt64: "bigint", - KindUint64: "unsigned bigint", - KindFloat32: "float", - KindFloat64: "double", - KindString: "char", - KindBytes: "bytes", - KindBinaryLiteral: "bit/hex literal", - KindMysqlDecimal: "decimal", - KindMysqlDuration: "time", - KindMysqlEnum: "enum", - KindMysqlBit: "bit", - KindMysqlSet: "set", - KindMysqlTime: "datetime", - KindInterface: "interface", - KindMinNotNull: "min_not_null", - KindMaxValue: "max_value", - KindRaw: "raw", - KindMysqlJSON: "json", -} - -// TypeStr converts tp to a string. -var TypeStr = ast.TypeStr - -// KindStr converts kind to a string. -func KindStr(kind byte) (r string) { - return kind2Str[kind] -} - -// TypeToStr converts a field to a string. -// It is used for converting Text to Blob, -// or converting Char to Binary. -// Args: -// -// tp: type enum -// cs: charset -var TypeToStr = ast.TypeToStr - -// EOFAsNil filtrates errors, -// If err is equal to io.EOF returns nil. -func EOFAsNil(err error) error { - if terror.ErrorEqual(err, io.EOF) { - return nil - } - return errors.Trace(err) -} - -// InvOp2 returns an invalid operation error. -func InvOp2(x, y interface{}, o opcode.Op) (interface{}, error) { - return nil, errors.Errorf("Invalid operation: %v %v %v (mismatched types %T and %T)", x, o, y, x, y) -} - -// overflow returns an overflowed error. -func overflow(v interface{}, tp byte) error { - return ErrOverflow.GenWithStack("constant %v overflows %s", v, TypeStr(tp)) -} - -// IsTypeTemporal checks if a type is a temporal type. -func IsTypeTemporal(tp byte) bool { - switch tp { - case mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeTimestamp, - mysql.TypeDate, mysql.TypeNewDate: - return true - } - return false -} diff --git a/types/etc_test.go b/types/etc_test.go deleted file mode 100644 index e2ec84a8d0cbd..0000000000000 --- a/types/etc_test.go +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "io" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/stretchr/testify/require" -) - -func testIsTypeBlob(t *testing.T, tp byte, expect bool) { - v := IsTypeBlob(tp) - require.Equal(t, expect, v) -} - -func testIsTypeChar(t *testing.T, tp byte, expect bool) { - v := IsTypeChar(tp) - require.Equal(t, expect, v) -} - -func TestIsType(t *testing.T) { - testIsTypeBlob(t, mysql.TypeTinyBlob, true) - testIsTypeBlob(t, mysql.TypeMediumBlob, true) - testIsTypeBlob(t, mysql.TypeBlob, true) - testIsTypeBlob(t, mysql.TypeLongBlob, true) - testIsTypeBlob(t, mysql.TypeInt24, false) - - testIsTypeChar(t, mysql.TypeString, true) - testIsTypeChar(t, mysql.TypeVarchar, true) - testIsTypeChar(t, mysql.TypeLong, false) -} - -func testTypeStr(t *testing.T, tp byte, expect string) { - v := TypeStr(tp) - require.Equal(t, expect, v) -} - -func testTypeToStr(t *testing.T, tp byte, charset string, expect string) { - v := TypeToStr(tp, charset) - require.Equal(t, expect, v) -} - -func TestTypeToStr(t *testing.T) { - testTypeStr(t, mysql.TypeYear, "year") - testTypeStr(t, 0xdd, "") - - testTypeToStr(t, mysql.TypeBlob, "utf8", "text") - testTypeToStr(t, mysql.TypeLongBlob, "utf8", "longtext") - testTypeToStr(t, mysql.TypeTinyBlob, "utf8", "tinytext") - testTypeToStr(t, mysql.TypeMediumBlob, "utf8", "mediumtext") - testTypeToStr(t, mysql.TypeVarchar, "binary", "varbinary") - testTypeToStr(t, mysql.TypeString, "binary", "binary") - testTypeToStr(t, mysql.TypeTiny, "binary", "tinyint") - testTypeToStr(t, mysql.TypeBlob, "binary", "blob") - testTypeToStr(t, mysql.TypeLongBlob, "binary", "longblob") - testTypeToStr(t, mysql.TypeTinyBlob, "binary", "tinyblob") - testTypeToStr(t, mysql.TypeMediumBlob, "binary", "mediumblob") - testTypeToStr(t, mysql.TypeVarchar, "utf8", "varchar") - testTypeToStr(t, mysql.TypeString, "utf8", "char") - testTypeToStr(t, mysql.TypeShort, "binary", "smallint") - testTypeToStr(t, mysql.TypeInt24, "binary", "mediumint") - testTypeToStr(t, mysql.TypeLong, "binary", "int") - testTypeToStr(t, mysql.TypeLonglong, "binary", "bigint") - testTypeToStr(t, mysql.TypeFloat, "binary", "float") - testTypeToStr(t, mysql.TypeDouble, "binary", "double") - testTypeToStr(t, mysql.TypeYear, "binary", "year") - testTypeToStr(t, mysql.TypeDuration, "binary", "time") - testTypeToStr(t, mysql.TypeDatetime, "binary", "datetime") - testTypeToStr(t, mysql.TypeDate, "binary", "date") - testTypeToStr(t, mysql.TypeTimestamp, "binary", "timestamp") - testTypeToStr(t, mysql.TypeNewDecimal, "binary", "decimal") - testTypeToStr(t, mysql.TypeUnspecified, "binary", "unspecified") - testTypeToStr(t, 0xdd, "binary", "") - testTypeToStr(t, mysql.TypeBit, "binary", "bit") - testTypeToStr(t, mysql.TypeEnum, "binary", "enum") - testTypeToStr(t, mysql.TypeSet, "binary", "set") -} - -func TestEOFAsNil(t *testing.T) { - err := EOFAsNil(io.EOF) - require.NoError(t, err) - err = EOFAsNil(errors.New("test")) - require.EqualError(t, err, "test") -} - -func TestMaxFloat(t *testing.T) { - tests := []struct { - flen int - decimal int - expect float64 - }{ - {3, 2, 9.99}, - {5, 2, 999.99}, - {10, 1, 999999999.9}, - {5, 5, 0.99999}, - } - - for _, test := range tests { - require.Equal(t, test.expect, GetMaxFloat(test.flen, test.decimal)) - } -} - -func TestRoundFloat(t *testing.T) { - tests := []struct { - input float64 - expect float64 - }{ - {2.5, 2}, - {1.5, 2}, - {0.5, 0}, - {0.49999999999999997, 0}, - {0, 0}, - {-0.49999999999999997, 0}, - {-0.5, 0}, - {-2.5, -2}, - {-1.5, -2}, - } - - for _, test := range tests { - require.Equal(t, test.expect, RoundFloat(test.input)) - } -} - -func TestRound(t *testing.T) { - tests := []struct { - input float64 - dec int - expect float64 - }{ - {-1.23, 0, -1}, - {-1.58, 0, -2}, - {1.58, 0, 2}, - {1.298, 1, 1.3}, - {1.298, 0, 1}, - {23.298, -1, 20}, - } - - for _, test := range tests { - require.Equal(t, test.expect, Round(test.input, test.dec)) - } -} - -func TestTruncateFloat(t *testing.T) { - tests := []struct { - input float64 - flen int - decimal int - expect float64 - err error - }{ - {100.114, 10, 2, 100.11, nil}, - {100.115, 10, 2, 100.12, nil}, - {100.1156, 10, 3, 100.116, nil}, - {100.1156, 3, 1, 99.9, ErrOverflow}, - {1.36, 10, 2, 1.36, nil}, - } - - for _, test := range tests { - f, err := TruncateFloat(test.input, test.flen, test.decimal) - require.Equal(t, test.expect, f) - require.Truef(t, terror.ErrorEqual(err, test.err), "err: %v", err) - } -} - -func TestIsTypeTemporal(t *testing.T) { - res := IsTypeTemporal(mysql.TypeDuration) - require.True(t, res) - res = IsTypeTemporal(mysql.TypeDatetime) - require.True(t, res) - res = IsTypeTemporal(mysql.TypeTimestamp) - require.True(t, res) - res = IsTypeTemporal(mysql.TypeDate) - require.True(t, res) - res = IsTypeTemporal(mysql.TypeNewDate) - require.True(t, res) - res = IsTypeTemporal('t') - require.False(t, res) -} - -func TestIsBinaryStr(t *testing.T) { - in := &FieldType{} - in.SetType(mysql.TypeBit) - in.SetFlag(mysql.UnsignedFlag) - in.SetFlen(1) - in.SetDecimal(0) - in.SetCharset(charset.CharsetUTF8) - in.SetCollate(charset.CollationUTF8) - - in.SetCollate(charset.CollationUTF8) - res := IsBinaryStr(in) - require.False(t, res) - - in.SetCollate(charset.CollationBin) - res = IsBinaryStr(in) - require.False(t, res) - - in.SetType(mysql.TypeBlob) - res = IsBinaryStr(in) - require.True(t, res) -} - -func TestIsNonBinaryStr(t *testing.T) { - in := NewFieldType(mysql.TypeBit) - in.SetFlag(mysql.UnsignedFlag) - in.SetFlen(1) - in.SetDecimal(0) - in.SetCharset(charset.CharsetUTF8) - in.SetCollate(charset.CollationUTF8) - - in.SetCollate(charset.CollationBin) - res := IsBinaryStr(in) - require.False(t, res) - - in.SetCollate(charset.CollationUTF8) - res = IsBinaryStr(in) - require.False(t, res) - - in.SetType(mysql.TypeBlob) - res = IsBinaryStr(in) - require.False(t, res) -} - -func TestIsTemporalWithDate(t *testing.T) { - res := IsTemporalWithDate(mysql.TypeDatetime) - require.True(t, res) - - res = IsTemporalWithDate(mysql.TypeDate) - require.True(t, res) - - res = IsTemporalWithDate(mysql.TypeTimestamp) - require.True(t, res) - - res = IsTemporalWithDate('t') - require.False(t, res) -} - -func TestIsTypePrefixable(t *testing.T) { - res := IsTypePrefixable('t') - require.False(t, res) - - res = IsTypePrefixable(mysql.TypeBlob) - require.True(t, res) -} - -func TestIsTypeFractionable(t *testing.T) { - res := IsTypeFractionable(mysql.TypeDatetime) - require.True(t, res) - - res = IsTypeFractionable(mysql.TypeDuration) - require.True(t, res) - - res = IsTypeFractionable(mysql.TypeTimestamp) - require.True(t, res) - - res = IsTypeFractionable('t') - require.False(t, res) -} - -func TestIsTypeNumeric(t *testing.T) { - res := IsTypeNumeric(mysql.TypeBit) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeTiny) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeInt24) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeLong) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeLonglong) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeNewDecimal) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeUnspecified) - require.False(t, res) - - res = IsTypeNumeric(mysql.TypeFloat) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeDouble) - require.True(t, res) - - res = IsTypeNumeric(mysql.TypeShort) - require.True(t, res) - - res = IsTypeNumeric('t') - require.False(t, res) -} - -func TestNeedRestoredData(t *testing.T) { - type testCase struct { - tp byte - charset string - collate string - result bool - } - cases := []testCase{ - {mysql.TypeString, "binary", "binary", false}, - {mysql.TypeVarString, "binary", "binary", false}, - {mysql.TypeString, "utf8mb4", "utf8mb4_bin", false}, - {mysql.TypeVarString, "utf8mb4", "utf8mb4_bin", true}, - {mysql.TypeString, "utf8mb4", "utf8mb4_general_ci", true}, - {mysql.TypeVarString, "utf8mb4", "utf8mb4_general_ci", true}, - {mysql.TypeString, "utf8mb4", "utf8mb4_unicode_ci", true}, - {mysql.TypeVarString, "utf8mb4", "utf8mb4_unicode_ci", true}, - {mysql.TypeString, "utf8mb4", "utf8mb4_0900_ai_ci", true}, - {mysql.TypeVarString, "utf8mb4", "utf8mb4_0900_ai_ci", true}, - {mysql.TypeString, "utf8mb4", "utf8mb4_0900_bin", false}, - {mysql.TypeVarString, "utf8mb4", "utf8mb4_0900_bin", false}, - {mysql.TypeString, "gbk", "gbk_bin", true}, - {mysql.TypeVarString, "gbk", "gbk_bin", true}, - {mysql.TypeString, "gbk", "gbk_chinese_ci", true}, - {mysql.TypeVarString, "gbk", "gbk_chinese_ci", true}, - } - - for _, c := range cases { - ft := NewFieldTypeBuilder(). - SetType(c.tp). - SetCharset(c.charset). - SetCollate(c.collate). - Build() - - require.Equal(t, c.result, NeedRestoredData(&ft), "NeedRestoredData of tp: %d charset: %s collate: %s should be %t", c.tp, c.charset, c.collate, c.result) - } -} diff --git a/types/field_type.go b/types/field_type.go deleted file mode 100644 index d5f3439722a6d..0000000000000 --- a/types/field_type.go +++ /dev/null @@ -1,1490 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "fmt" - "strconv" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - ast "github.com/pingcap/tidb/parser/types" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/mathutil" -) - -// UnspecifiedLength is unspecified length. -const UnspecifiedLength = -1 - -// ErrorLength is error length for blob or text. -const ErrorLength = 0 - -// FieldType records field type information. -type FieldType = ast.FieldType - -// NewFieldType returns a FieldType, -// with a type and other information about field type. -func NewFieldType(tp byte) *FieldType { - charset1, collate1 := DefaultCharsetForType(tp) - flen, decimal := minFlenAndDecimalForType(tp) - return NewFieldTypeBuilder(). - SetType(tp). - SetCharset(charset1). - SetCollate(collate1). - SetFlen(flen). - SetDecimal(decimal). - BuildP() -} - -// NewFieldTypeWithCollation returns a FieldType, -// with a type and other information about field type. -func NewFieldTypeWithCollation(tp byte, collation string, length int) *FieldType { - coll, _ := charset.GetCollationByName(collation) - return NewFieldTypeBuilder().SetType(tp).SetFlen(length).SetCharset(coll.CharsetName).SetCollate(collation).SetDecimal(UnspecifiedLength).BuildP() -} - -// AggFieldType aggregates field types for a multi-argument function like `IF`, `IFNULL`, `COALESCE` -// whose return type is determined by the arguments' FieldTypes. -// Aggregation is performed by MergeFieldType function. -func AggFieldType(tps []*FieldType) *FieldType { - var currType FieldType - isMixedSign := false - for i, t := range tps { - if i == 0 && currType.GetType() == mysql.TypeUnspecified { - currType = *t - continue - } - mtp := MergeFieldType(currType.GetType(), t.GetType()) - isMixedSign = isMixedSign || (mysql.HasUnsignedFlag(currType.GetFlag()) != mysql.HasUnsignedFlag(t.GetFlag())) - currType.SetType(mtp) - currType.SetFlag(mergeTypeFlag(currType.GetFlag(), t.GetFlag())) - } - // integral promotion when tps contains signed and unsigned - if isMixedSign && IsTypeInteger(currType.GetType()) { - bumpRange := false // indicate one of tps bump currType range - for _, t := range tps { - bumpRange = bumpRange || (mysql.HasUnsignedFlag(t.GetFlag()) && (t.GetType() == currType.GetType() || - t.GetType() == mysql.TypeBit)) - } - if bumpRange { - switch currType.GetType() { - case mysql.TypeTiny: - currType.SetType(mysql.TypeShort) - case mysql.TypeShort: - currType.SetType(mysql.TypeInt24) - case mysql.TypeInt24: - currType.SetType(mysql.TypeLong) - case mysql.TypeLong: - currType.SetType(mysql.TypeLonglong) - case mysql.TypeLonglong: - currType.SetType(mysql.TypeNewDecimal) - } - } - } - - if mysql.HasUnsignedFlag(currType.GetFlag()) && !isMixedSign { - currType.AddFlag(mysql.UnsignedFlag) - } - - return &currType -} - -// TryToFixFlenOfDatetime try to fix flen of Datetime for specific func or other field merge cases -func TryToFixFlenOfDatetime(resultTp *FieldType) { - if resultTp.GetType() == mysql.TypeDatetime { - resultTp.SetFlen(mysql.MaxDatetimeWidthNoFsp) - if resultTp.GetDecimal() > 0 { - resultTp.SetFlen(resultTp.GetFlen() + resultTp.GetDecimal() + 1) - } - } -} - -// AggregateEvalType aggregates arguments' EvalType of a multi-argument function. -func AggregateEvalType(fts []*FieldType, flag *uint) EvalType { - var ( - aggregatedEvalType = ETString - unsigned bool - gotFirst bool - gotBinString bool - ) - lft := fts[0] - for _, ft := range fts { - if ft.GetType() == mysql.TypeNull { - continue - } - et := ft.EvalType() - rft := ft - if (IsTypeBlob(ft.GetType()) || IsTypeVarchar(ft.GetType()) || IsTypeChar(ft.GetType())) && mysql.HasBinaryFlag(ft.GetFlag()) { - gotBinString = true - } - if !gotFirst { - gotFirst = true - aggregatedEvalType = et - unsigned = mysql.HasUnsignedFlag(ft.GetFlag()) - } else { - aggregatedEvalType = mergeEvalType(aggregatedEvalType, et, lft, rft, unsigned, mysql.HasUnsignedFlag(ft.GetFlag())) - unsigned = unsigned && mysql.HasUnsignedFlag(ft.GetFlag()) - } - lft = rft - } - SetTypeFlag(flag, mysql.UnsignedFlag, unsigned) - SetTypeFlag(flag, mysql.BinaryFlag, !aggregatedEvalType.IsStringKind() || gotBinString) - return aggregatedEvalType -} - -func mergeEvalType(lhs, rhs EvalType, lft, rft *FieldType, isLHSUnsigned, isRHSUnsigned bool) EvalType { - if lft.GetType() == mysql.TypeUnspecified || rft.GetType() == mysql.TypeUnspecified { - if lft.GetType() == rft.GetType() { - return ETString - } - if lft.GetType() == mysql.TypeUnspecified { - lhs = rhs - } else { - rhs = lhs - } - } - if lhs.IsStringKind() || rhs.IsStringKind() { - return ETString - } else if lhs == ETReal || rhs == ETReal { - return ETReal - } else if lhs == ETDecimal || rhs == ETDecimal || isLHSUnsigned != isRHSUnsigned { - return ETDecimal - } - return ETInt -} - -// SetTypeFlag turns the flagItem on or off. -func SetTypeFlag(flag *uint, flagItem uint, on bool) { - if on { - *flag |= flagItem - } else { - *flag &= ^flagItem - } -} - -// InferParamTypeFromDatum is used for plan cache to infer the type of a parameter from its datum. -func InferParamTypeFromDatum(d *Datum, tp *FieldType) { - InferParamTypeFromUnderlyingValue(d.GetValue(), tp) - if IsStringKind(d.k) { - // consider charset and collation here - c, err := collate.GetCollationByName(d.collation) - if err != nil || c == nil { - return // use default charset and collation - } - tp.SetCharset(c.CharsetName) - tp.SetCollate(d.collation) - } -} - -// InferParamTypeFromUnderlyingValue is used for plan cache to infer the type of a parameter from its underlying value. -func InferParamTypeFromUnderlyingValue(value interface{}, tp *FieldType) { - switch value.(type) { - case nil: - tp.SetType(mysql.TypeVarString) - tp.SetFlen(UnspecifiedLength) - tp.SetDecimal(UnspecifiedLength) - default: - DefaultTypeForValue(value, tp, mysql.DefaultCharset, mysql.DefaultCollationName) - if hasVariantFieldLength(tp) { - tp.SetFlen(UnspecifiedLength) - } - if tp.GetType() == mysql.TypeUnspecified { - tp.SetType(mysql.TypeVarString) - } - } -} - -func hasVariantFieldLength(tp *FieldType) bool { - switch tp.GetType() { - case mysql.TypeLonglong, mysql.TypeVarString, mysql.TypeDouble, mysql.TypeBlob, - mysql.TypeBit, mysql.TypeDuration, mysql.TypeEnum, mysql.TypeSet: - return true - } - return false -} - -// DefaultTypeForValue returns the default FieldType for the value. -func DefaultTypeForValue(value interface{}, tp *FieldType, char string, collate string) { - if value != nil { - tp.AddFlag(mysql.NotNullFlag) - } - switch x := value.(type) { - case nil: - tp.SetType(mysql.TypeNull) - tp.SetFlen(0) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - case bool: - tp.SetType(mysql.TypeLonglong) - tp.SetFlen(1) - tp.SetDecimal(0) - tp.AddFlag(mysql.IsBooleanFlag) - SetBinChsClnFlag(tp) - case int: - tp.SetType(mysql.TypeLonglong) - tp.SetFlen(mathutil.StrLenOfInt64Fast(int64(x))) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - case int64: - tp.SetType(mysql.TypeLonglong) - tp.SetFlen(mathutil.StrLenOfInt64Fast(x)) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - case uint64: - tp.SetType(mysql.TypeLonglong) - tp.AddFlag(mysql.UnsignedFlag) - tp.SetFlen(mathutil.StrLenOfUint64Fast(x)) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - case string: - tp.SetType(mysql.TypeVarString) - // TODO: tp.flen should be len(x) * 3 (max bytes length of CharsetUTF8) - tp.SetFlen(len(x)) - tp.SetDecimal(UnspecifiedLength) - tp.SetCharset(char) - tp.SetCollate(collate) - case float32: - tp.SetType(mysql.TypeFloat) - s := strconv.FormatFloat(float64(x), 'f', -1, 32) - tp.SetFlen(len(s)) - tp.SetDecimal(UnspecifiedLength) - SetBinChsClnFlag(tp) - case float64: - tp.SetType(mysql.TypeDouble) - s := strconv.FormatFloat(x, 'f', -1, 64) - tp.SetFlen(len(s)) - tp.SetDecimal(UnspecifiedLength) - SetBinChsClnFlag(tp) - case []byte: - tp.SetType(mysql.TypeBlob) - tp.SetFlen(len(x)) - tp.SetDecimal(UnspecifiedLength) - SetBinChsClnFlag(tp) - case BitLiteral: - tp.SetType(mysql.TypeVarString) - tp.SetFlen(len(x) * 3) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - case HexLiteral: - tp.SetType(mysql.TypeVarString) - tp.SetFlen(len(x) * 3) - tp.SetDecimal(0) - tp.AddFlag(mysql.UnsignedFlag) - SetBinChsClnFlag(tp) - case BinaryLiteral: - tp.SetType(mysql.TypeVarString) - tp.SetFlen(len(x)) - tp.SetDecimal(0) - SetBinChsClnFlag(tp) - tp.DelFlag(mysql.BinaryFlag) - tp.AddFlag(mysql.UnsignedFlag) - case Time: - tp.SetType(x.Type()) - switch x.Type() { - case mysql.TypeDate: - tp.SetFlen(mysql.MaxDateWidth) - tp.SetDecimal(UnspecifiedLength) - case mysql.TypeDatetime, mysql.TypeTimestamp: - tp.SetFlen(mysql.MaxDatetimeWidthNoFsp) - if x.Fsp() > DefaultFsp { // consider point('.') and the fractional part. - tp.SetFlen(tp.GetFlen() + x.Fsp() + 1) - } - tp.SetDecimal(x.Fsp()) - } - SetBinChsClnFlag(tp) - case Duration: - tp.SetType(mysql.TypeDuration) - tp.SetFlen(len(x.String())) - if x.Fsp > DefaultFsp { // consider point('.') and the fractional part. - tp.SetFlen(x.Fsp + 1) - } - tp.SetDecimal(x.Fsp) - SetBinChsClnFlag(tp) - case *MyDecimal: - tp.SetType(mysql.TypeNewDecimal) - tp.SetFlenUnderLimit(len(x.ToString())) - tp.SetDecimalUnderLimit(int(x.digitsFrac)) - // Add the length for `.`. - tp.SetFlenUnderLimit(tp.GetFlen() + 1) - SetBinChsClnFlag(tp) - case Enum: - tp.SetType(mysql.TypeEnum) - tp.SetFlen(len(x.Name)) - tp.SetDecimal(UnspecifiedLength) - SetBinChsClnFlag(tp) - case Set: - tp.SetType(mysql.TypeSet) - tp.SetFlen(len(x.Name)) - tp.SetDecimal(UnspecifiedLength) - SetBinChsClnFlag(tp) - case BinaryJSON: - tp.SetType(mysql.TypeJSON) - tp.SetFlen(UnspecifiedLength) - tp.SetDecimal(0) - tp.SetCharset(charset.CharsetUTF8MB4) - tp.SetCollate(charset.CollationUTF8MB4) - default: - tp.SetType(mysql.TypeUnspecified) - tp.SetFlen(UnspecifiedLength) - tp.SetDecimal(UnspecifiedLength) - tp.SetCharset(charset.CharsetUTF8MB4) - tp.SetCollate(charset.CollationUTF8MB4) - } -} - -// minFlenAndDecimalForType returns the minimum flen/decimal that can hold all the data for `tp`. -func minFlenAndDecimalForType(tp byte) (int, int) { - switch tp { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: - return mysql.GetDefaultFieldLengthAndDecimal(tp) - default: - // todo support non-integer type - return UnspecifiedLength, UnspecifiedLength - } -} - -// DefaultCharsetForType returns the default charset/collation for mysql type. -func DefaultCharsetForType(tp byte) (defaultCharset string, defaultCollation string) { - switch tp { - case mysql.TypeVarString, mysql.TypeString, mysql.TypeVarchar: - // Default charset for string types is utf8mb4. - return mysql.DefaultCharset, mysql.DefaultCollationName - } - return charset.CharsetBin, charset.CollationBin -} - -// MergeFieldType merges two MySQL type to a new type. -// This is used in hybrid field type expression. -// For example "select case c when 1 then 2 when 2 then 'tidb' from t;" -// The result field type of the case expression is the merged type of the two when clause. -// See https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L1042 -func MergeFieldType(a byte, b byte) byte { - ia := getFieldTypeIndex(a) - ib := getFieldTypeIndex(b) - return fieldTypeMergeRules[ia][ib] -} - -// mergeTypeFlag merges two MySQL type flag to a new one -// currently only NotNullFlag and UnsignedFlag is checked -// todo more flag need to be checked -func mergeTypeFlag(a, b uint) uint { - return a & (b&mysql.NotNullFlag | ^mysql.NotNullFlag) & (b&mysql.UnsignedFlag | ^mysql.UnsignedFlag) -} - -func getFieldTypeIndex(tp byte) int { - itp := int(tp) - if itp < fieldTypeTearFrom { - return itp - } - return fieldTypeTearFrom + itp - fieldTypeTearTo - 1 -} - -const ( - fieldTypeTearFrom = int(mysql.TypeBit) + 1 - fieldTypeTearTo = int(mysql.TypeJSON) - 1 - fieldTypeNum = fieldTypeTearFrom + (255 - fieldTypeTearTo) -) - -// https://github.com/mysql/mysql-server/blob/8.0/sql/field.cc#L248 -var fieldTypeMergeRules = [fieldTypeNum][fieldTypeNum]byte{ - /* mysql.TypeUnspecified -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeNewDecimal, - // mysql.TypeShort mysql.TypeLong - mysql.TypeNewDecimal, mysql.TypeNewDecimal, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeUnspecified, mysql.TypeUnspecified, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeTiny -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeTiny, - // mysql.TypeShort mysql.TypeLong - mysql.TypeShort, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeTiny, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeInt24, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeTiny, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeShort -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeShort, - // mysql.TypeShort mysql.TypeLong - mysql.TypeShort, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeShort, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeInt24, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeShort, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeLong -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeLong, - // mysql.TypeShort mysql.TypeLong - mysql.TypeLong, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeLong, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeLong, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeLong, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeFloat -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeDouble, mysql.TypeFloat, - // mysql.TypeShort mysql.TypeLong - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeFloat, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeFloat, mysql.TypeFloat, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeFloat, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeDouble, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeDouble, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeDouble -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeShort mysql.TypeLong - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeDouble, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeDouble, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeDouble, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeDouble, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeNull -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeTiny, - // mysql.TypeShort mysql.TypeLong - mysql.TypeShort, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeNull, mysql.TypeTimestamp, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeLonglong, - // mysql.TypeDate mysql.TypeTime - mysql.TypeDate, mysql.TypeDuration, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeYear, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeBit, - // mysql.TypeJSON - mysql.TypeJSON, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeEnum, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeSet, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeGeometry, - }, - /* mysql.TypeTimestamp -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeTimestamp, mysql.TypeTimestamp, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeDatetime, mysql.TypeDatetime, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeLonglong -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeLonglong, - // mysql.TypeShort mysql.TypeLong - mysql.TypeLonglong, mysql.TypeLonglong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeLonglong, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeLong, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeLonglong, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeInt24 -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeInt24, - // mysql.TypeShort mysql.TypeLong - mysql.TypeInt24, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeInt24, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeInt24, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeInt24, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeDate -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeDate, mysql.TypeDatetime, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeDate, mysql.TypeDatetime, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeTime -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeDuration, mysql.TypeDatetime, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeDatetime, mysql.TypeDuration, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeDatetime -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeDatetime, mysql.TypeDatetime, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeDatetime, mysql.TypeDatetime, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeYear -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeUnspecified, mysql.TypeTiny, - // mysql.TypeShort mysql.TypeLong - mysql.TypeShort, mysql.TypeLong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeFloat, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeYear, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeInt24, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeYear, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeLonglong, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeNewDate -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeNewDate, mysql.TypeDatetime, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeNewDate, mysql.TypeDatetime, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeDatetime, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeNewDate, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeVarchar -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeVarchar, mysql.TypeVarchar, - }, - /* mysql.TypeBit -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeLonglong, - // mysql.TypeShort mysql.TypeLong - mysql.TypeLonglong, mysql.TypeLonglong, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeBit, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLonglong, mysql.TypeLonglong, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeLonglong, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeBit, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeJSON -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeJSON, mysql.TypeVarchar, - // mysql.TypeLongLONG mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate MYSQL_TYPE_TIME - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime MYSQL_TYPE_YEAR - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeJSON, - // mysql.TypeNewDecimal MYSQL_TYPE_ENUM - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeLongBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeLongBlob, mysql.TypeVarchar, - // mysql.TypeString MYSQL_TYPE_GEOMETRY - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeNewDecimal -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeNewDecimal, mysql.TypeNewDecimal, - // mysql.TypeShort mysql.TypeLong - mysql.TypeNewDecimal, mysql.TypeNewDecimal, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeDouble, mysql.TypeDouble, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeNewDecimal, mysql.TypeNewDecimal, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeNewDecimal, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeNewDecimal, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeNewDecimal, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeEnum -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeEnum, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeSet -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeSet, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeVarchar, - }, - /* mysql.TypeTinyBlob -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeShort mysql.TypeLong - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeDate mysql.TypeTime - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeBit <16>-<244> - mysql.TypeTinyBlob, - // mysql.TypeJSON - mysql.TypeLongBlob, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeTinyBlob, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeTinyBlob, mysql.TypeTinyBlob, - }, - /* mysql.TypeMediumBlob -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeShort mysql.TypeLong - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeDate mysql.TypeTime - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeBit <16>-<244> - mysql.TypeMediumBlob, - // mysql.TypeJSON - mysql.TypeLongBlob, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeMediumBlob, mysql.TypeMediumBlob, - }, - /* mysql.TypeLongBlob -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeShort mysql.TypeLong - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeDate mysql.TypeTime - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeBit <16>-<244> - mysql.TypeLongBlob, - // mysql.TypeJSON - mysql.TypeLongBlob, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeLongBlob, mysql.TypeLongBlob, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeLongBlob, mysql.TypeLongBlob, - }, - /* mysql.TypeBlob -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeShort mysql.TypeLong - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeDate mysql.TypeTime - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeBit <16>-<244> - mysql.TypeBlob, - // mysql.TypeJSON - mysql.TypeLongBlob, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeBlob, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeBlob, mysql.TypeBlob, - }, - /* mysql.TypeVarString -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeVarchar, mysql.TypeVarchar, - }, - /* mysql.TypeString -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeString, mysql.TypeString, - // mysql.TypeShort mysql.TypeLong - mysql.TypeString, mysql.TypeString, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeString, mysql.TypeString, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeString, mysql.TypeString, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeString, mysql.TypeString, - // mysql.TypeDate mysql.TypeTime - mysql.TypeString, mysql.TypeString, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeString, mysql.TypeString, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeString, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeString, - // mysql.TypeJSON - mysql.TypeString, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeString, mysql.TypeString, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeString, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeString, - }, - /* mysql.TypeGeometry -> */ - { - // mysql.TypeUnspecified mysql.TypeTiny - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeShort mysql.TypeLong - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeFloat mysql.TypeDouble - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNull mysql.TypeTimestamp - mysql.TypeGeometry, mysql.TypeVarchar, - // mysql.TypeLonglong mysql.TypeInt24 - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDate mysql.TypeTime - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeDatetime mysql.TypeYear - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeNewDate mysql.TypeVarchar - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeBit <16>-<244> - mysql.TypeVarchar, - // mysql.TypeJSON - mysql.TypeVarchar, - // mysql.TypeNewDecimal mysql.TypeEnum - mysql.TypeVarchar, mysql.TypeVarchar, - // mysql.TypeSet mysql.TypeTinyBlob - mysql.TypeVarchar, mysql.TypeTinyBlob, - // mysql.TypeMediumBlob mysql.TypeLongBlob - mysql.TypeMediumBlob, mysql.TypeLongBlob, - // mysql.TypeBlob mysql.TypeVarString - mysql.TypeBlob, mysql.TypeVarchar, - // mysql.TypeString mysql.TypeGeometry - mysql.TypeString, mysql.TypeGeometry, - }, -} - -// SetBinChsClnFlag sets charset, collation as 'binary' and adds binaryFlag to FieldType. -func SetBinChsClnFlag(ft *FieldType) { - ft.SetCharset(charset.CharsetBin) - ft.SetCollate(charset.CollationBin) - ft.AddFlag(mysql.BinaryFlag) -} - -// VarStorageLen indicates this column is a variable length column. -const VarStorageLen = ast.VarStorageLen - -// CheckModifyTypeCompatible checks whether changes column type to another is compatible and can be changed. -// If types are compatible and can be directly changed, nil err will be returned; otherwise the types are incompatible. -// There are two cases when types incompatible: -// 1. returned canReorg == true: types can be changed by reorg -// 2. returned canReorg == false: type change not supported yet -func CheckModifyTypeCompatible(origin *FieldType, to *FieldType) (canReorg bool, err error) { - // Deal with the same type. - if origin.GetType() == to.GetType() { - if origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet { - typeVar := "set" - if origin.GetType() == mysql.TypeEnum { - typeVar = "enum" - } - if len(to.GetElems()) < len(origin.GetElems()) { - msg := fmt.Sprintf("the number of %s column's elements is less than the original: %d", typeVar, len(origin.GetElems())) - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) - } - for index, originElem := range origin.GetElems() { - toElem := to.GetElems()[index] - if originElem != toElem { - msg := fmt.Sprintf("cannot modify %s column value %s to %s", typeVar, originElem, toElem) - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) - } - } - } - - if origin.GetType() == mysql.TypeNewDecimal { - // Floating-point and fixed-point types also can be UNSIGNED. As with integer types, this attribute prevents - // negative values from being stored in the column. Unlike the integer types, the upper range of column values - // remains the same. - if to.GetFlen() != origin.GetFlen() || to.GetDecimal() != origin.GetDecimal() || mysql.HasUnsignedFlag(to.GetFlag()) != mysql.HasUnsignedFlag(origin.GetFlag()) { - msg := fmt.Sprintf("decimal change from decimal(%d, %d) to decimal(%d, %d)", origin.GetFlen(), origin.GetDecimal(), to.GetFlen(), to.GetDecimal()) - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(msg) - } - } - - needReorg, reason := needReorgToChange(origin, to) - if !needReorg { - return false, nil - } - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(reason) - } - - // Deal with the different type. - if !checkTypeChangeSupported(origin, to) { - unsupportedMsg := fmt.Sprintf("change from original type %v to %v is currently unsupported yet", origin.CompactStr(), to.CompactStr()) - return false, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(unsupportedMsg) - } - - // Check if different type can directly convert and no need to reorg. - stringToString := IsString(origin.GetType()) && IsString(to.GetType()) - integerToInteger := mysql.IsIntegerType(origin.GetType()) && mysql.IsIntegerType(to.GetType()) - if stringToString || integerToInteger { - needReorg, reason := needReorgToChange(origin, to) - if !needReorg { - return false, nil - } - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(reason) - } - - notCompatibleMsg := fmt.Sprintf("type %v not match origin %v", to.CompactStr(), origin.CompactStr()) - return true, dbterror.ErrUnsupportedModifyColumn.GenWithStackByArgs(notCompatibleMsg) -} - -func needReorgToChange(origin *FieldType, to *FieldType) (needReorg bool, reasonMsg string) { - toFlen := to.GetFlen() - originFlen := origin.GetFlen() - if mysql.IsIntegerType(to.GetType()) && mysql.IsIntegerType(origin.GetType()) { - // For integers, we should ignore the potential display length represented by flen, using - // the default flen of the type. - originFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(origin.GetType()) - toFlen, _ = mysql.GetDefaultFieldLengthAndDecimal(to.GetType()) - } - - if ConvertBetweenCharAndVarchar(origin.GetType(), to.GetType()) { - return true, "conversion between char and varchar string needs reorganization" - } - - if toFlen > 0 && toFlen != originFlen { - if toFlen < originFlen { - return true, fmt.Sprintf("length %d is less than origin %d", toFlen, originFlen) - } - - // Due to the behavior of padding \x00 at binary type, we need to reorg when binary length changed - isBinaryType := func(tp *FieldType) bool { return tp.GetType() == mysql.TypeString && IsBinaryStr(tp) } - if isBinaryType(origin) && isBinaryType(to) { - return true, "can't change binary types of different length" - } - } - if to.GetDecimal() > 0 && to.GetDecimal() < origin.GetDecimal() { - return true, fmt.Sprintf("decimal %d is less than origin %d", to.GetDecimal(), origin.GetDecimal()) - } - if mysql.HasUnsignedFlag(origin.GetFlag()) != mysql.HasUnsignedFlag(to.GetFlag()) { - return true, "can't change unsigned integer to signed or vice versa" - } - return false, "" -} - -func checkTypeChangeSupported(origin *FieldType, to *FieldType) bool { - if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear || - IsString(origin.GetType()) || origin.GetType() == mysql.TypeJSON) && - to.GetType() == mysql.TypeBit { - // TODO: Currently date/datetime/timestamp/time/year/string/json data type cast to bit are not compatible with mysql, should fix here after compatible. - return false - } - - if (IsTypeTime(origin.GetType()) || origin.GetType() == mysql.TypeDuration || origin.GetType() == mysql.TypeYear || - origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble || origin.GetType() == mysql.TypeJSON || origin.GetType() == mysql.TypeBit) && - (to.GetType() == mysql.TypeEnum || to.GetType() == mysql.TypeSet) { - // TODO: Currently date/datetime/timestamp/time/year/decimal/float/double/json/bit cast to enum/set are not support yet, should fix here after supported. - return false - } - - if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit || - origin.GetType() == mysql.TypeNewDecimal || origin.GetType() == mysql.TypeFloat || origin.GetType() == mysql.TypeDouble) && - (IsTypeTime(to.GetType())) { - // TODO: Currently enum/set/bit/decimal/float/double cast to date/datetime/timestamp type are not support yet, should fix here after supported. - return false - } - - if (origin.GetType() == mysql.TypeEnum || origin.GetType() == mysql.TypeSet || origin.GetType() == mysql.TypeBit) && - to.GetType() == mysql.TypeDuration { - // TODO: Currently enum/set/bit cast to time are not support yet, should fix here after supported. - return false - } - - return true -} - -// ConvertBetweenCharAndVarchar is that Column type conversion between varchar to char need reorganization because -// 1. varchar -> char: char type is stored with the padding removed. All the indexes need to be rewritten. -// 2. char -> varchar: the index value encoding of secondary index on clustered primary key tables is different. -// These secondary indexes need to be rewritten. -func ConvertBetweenCharAndVarchar(oldCol, newCol byte) bool { - return (IsTypeVarchar(oldCol) && newCol == mysql.TypeString) || - (oldCol == mysql.TypeString && IsTypeVarchar(newCol) && collate.NewCollationEnabled()) -} - -// IsVarcharTooBigFieldLength check if the varchar type column exceeds the maximum length limit. -func IsVarcharTooBigFieldLength(colDefTpFlen int, colDefName, setCharset string) error { - desc, err := charset.GetCharsetInfo(setCharset) - if err != nil { - return errors.Trace(err) - } - maxFlen := mysql.MaxFieldVarCharLength - maxFlen /= desc.Maxlen - if colDefTpFlen != UnspecifiedLength && colDefTpFlen > maxFlen { - return ErrTooBigFieldLength.GenWithStack("Column length too big for column '%s' (max = %d); use BLOB or TEXT instead", colDefName, maxFlen) - } - return nil -} diff --git a/types/field_type_test.go b/types/field_type_test.go deleted file mode 100644 index c25720920e0d3..0000000000000 --- a/types/field_type_test.go +++ /dev/null @@ -1,466 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "testing" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/stretchr/testify/require" -) - -func TestFieldType(t *testing.T) { - ft := NewFieldType(mysql.TypeDuration) - require.Equal(t, UnspecifiedLength, ft.GetFlen()) - require.Equal(t, UnspecifiedLength, ft.GetDecimal()) - - ft.SetDecimal(5) - require.Equal(t, "time(5)", ft.String()) - - ft = NewFieldType(mysql.TypeLong) - ft.SetFlen(5) - ft.SetFlen(5) - ft.SetFlag(mysql.UnsignedFlag | mysql.ZerofillFlag) - require.Equal(t, "int(5) UNSIGNED ZEROFILL", ft.String()) - require.Equal(t, "int(5) unsigned", ft.InfoSchemaStr()) - - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(12) // Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "float(12,3)", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(12) // Default - ft.SetDecimal(-1) // Default - require.Equal(t, "float", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(5) // Not Default - ft.SetDecimal(-1) // Default - require.Equal(t, "float", ft.String()) - ft = NewFieldType(mysql.TypeFloat) - ft.SetFlen(7) // Not Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "float(7,3)", ft.String()) - - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(22) // Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "double(22,3)", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(22) // Default - ft.SetDecimal(-1) // Default - require.Equal(t, "double", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(5) // Not Default - ft.SetDecimal(-1) // Default - require.Equal(t, "double", ft.String()) - ft = NewFieldType(mysql.TypeDouble) - ft.SetFlen(7) // Not Default - ft.SetDecimal(3) // Not Default - require.Equal(t, "double(7,3)", ft.String()) - - ft = NewFieldType(mysql.TypeBlob) - ft.SetFlen(10) - ft.SetCharset("UTF8") - ft.SetCollate("UTF8_UNICODE_GI") - require.Equal(t, "text CHARACTER SET UTF8 COLLATE UTF8_UNICODE_GI", ft.String()) - - ft = NewFieldType(mysql.TypeVarchar) - ft.SetFlen(10) - ft.AddFlag(mysql.BinaryFlag) - require.Equal(t, "varchar(10) BINARY CHARACTER SET utf8mb4 COLLATE utf8mb4_bin", ft.String()) - - ft = NewFieldType(mysql.TypeString) - ft.SetCharset(charset.CollationBin) - ft.AddFlag(mysql.BinaryFlag) - require.Equal(t, "binary(1) COLLATE utf8mb4_bin", ft.String()) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a", "b"}) - require.Equal(t, "enum('a','b')", ft.String()) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"'a'", "'b'"}) - require.Equal(t, "enum('''a''','''b''')", ft.String()) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a\nb", "a\tb", "a\rb"}) - require.Equal(t, "enum('a\\nb','a\tb','a\\rb')", ft.String()) - - ft = NewFieldType(mysql.TypeEnum) - ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) - require.Equal(t, "enum('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a", "b"}) - require.Equal(t, "set('a','b')", ft.String()) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"'a'", "'b'"}) - require.Equal(t, "set('''a''','''b''')", ft.String()) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a\nb", "a'\t\r\nb", "a\rb"}) - require.Equal(t, "set('a\\nb','a'' \\r\\nb','a\\rb')", ft.String()) - - ft = NewFieldType(mysql.TypeSet) - ft.SetElems([]string{"a'\nb", "a'b\tc"}) - require.Equal(t, "set('a''\\nb','a''b c')", ft.String()) - - ft = NewFieldType(mysql.TypeTimestamp) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "timestamp(2)", ft.String()) - ft = NewFieldType(mysql.TypeTimestamp) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "timestamp", ft.String()) - - ft = NewFieldType(mysql.TypeDatetime) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "datetime(2)", ft.String()) - ft = NewFieldType(mysql.TypeDatetime) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "datetime", ft.String()) - - ft = NewFieldType(mysql.TypeDate) - ft.SetFlen(8) - ft.SetDecimal(2) - require.Equal(t, "date", ft.String()) - ft = NewFieldType(mysql.TypeDate) - ft.SetFlen(8) - ft.SetDecimal(0) - require.Equal(t, "date", ft.String()) - - ft = NewFieldType(mysql.TypeYear) - ft.SetFlen(4) - ft.SetDecimal(0) - require.Equal(t, "year(4)", ft.String()) - ft = NewFieldType(mysql.TypeYear) - ft.SetFlen(2) - ft.SetDecimal(2) - require.Equal(t, "year(2)", ft.String()) // Note: Invalid year. -} - -func TestDefaultTypeForValue(t *testing.T) { - tests := []struct { - value interface{} - tp byte - flen int - decimal int - charset string - collation string - flag uint - }{ - {nil, mysql.TypeNull, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag}, - {1, mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {0, mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {432, mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {4321, mysql.TypeLonglong, 4, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {1234567, mysql.TypeLonglong, 7, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {12345678, mysql.TypeLonglong, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {12345678901234567, mysql.TypeLonglong, 17, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {-42, mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {uint64(1), mysql.TypeLonglong, 1, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {uint64(123), mysql.TypeLonglong, 3, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {uint64(1234), mysql.TypeLonglong, 4, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {uint64(1234567), mysql.TypeLonglong, 7, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {uint64(12345678), mysql.TypeLonglong, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {uint64(12345678901234567), mysql.TypeLonglong, 17, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {"abc", mysql.TypeVarString, 3, UnspecifiedLength, charset.CharsetUTF8MB4, charset.CollationUTF8MB4, mysql.NotNullFlag}, - {1.1, mysql.TypeDouble, 3, -1, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {[]byte("abc"), mysql.TypeBlob, 3, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {HexLiteral{}, mysql.TypeVarString, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.UnsignedFlag | mysql.NotNullFlag}, - {BitLiteral{}, mysql.TypeVarString, 0, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {NewTime(ZeroCoreTime, mysql.TypeDatetime, DefaultFsp), mysql.TypeDatetime, 19, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {NewTime(FromDate(2017, 12, 12, 12, 59, 59, 0), mysql.TypeDatetime, 3), mysql.TypeDatetime, 23, 3, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {Duration{}, mysql.TypeDuration, 8, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {&MyDecimal{}, mysql.TypeNewDecimal, 2, 0, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {Enum{Name: "a", Value: 1}, mysql.TypeEnum, 1, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - {Set{Name: "a", Value: 1}, mysql.TypeSet, 1, UnspecifiedLength, charset.CharsetBin, charset.CharsetBin, mysql.BinaryFlag | mysql.NotNullFlag}, - } - - for i, tt := range tests { - var ft FieldType - DefaultTypeForValue(tt.value, &ft, mysql.DefaultCharset, mysql.DefaultCollationName) - require.Equalf(t, tt.tp, ft.GetType(), "%v %v %v", i, ft.GetType(), tt.tp) - require.Equalf(t, tt.flen, ft.GetFlen(), "%v %v %v", i, ft.GetFlen(), tt.flen) - require.Equalf(t, tt.charset, ft.GetCharset(), "%v %v %v", i, ft.GetCharset(), tt.charset) - require.Equalf(t, tt.decimal, ft.GetDecimal(), "%v %v %v", i, ft.GetDecimal(), tt.decimal) - require.Equalf(t, tt.collation, ft.GetCollate(), "%v %v %v", i, ft.GetCollate(), tt.collation) - require.Equalf(t, tt.flag, ft.GetFlag(), "%v %v %v", i, ft.GetFlag(), tt.flag) - } -} - -func TestAggFieldType(t *testing.T) { - fts := []*FieldType{ - NewFieldType(mysql.TypeUnspecified), - NewFieldType(mysql.TypeTiny), - NewFieldType(mysql.TypeShort), - NewFieldType(mysql.TypeLong), - NewFieldType(mysql.TypeFloat), - NewFieldType(mysql.TypeDouble), - NewFieldType(mysql.TypeNull), - NewFieldType(mysql.TypeTimestamp), - NewFieldType(mysql.TypeLonglong), - NewFieldType(mysql.TypeInt24), - NewFieldType(mysql.TypeDate), - NewFieldType(mysql.TypeDuration), - NewFieldType(mysql.TypeDatetime), - NewFieldType(mysql.TypeYear), - NewFieldType(mysql.TypeNewDate), - NewFieldType(mysql.TypeVarchar), - NewFieldType(mysql.TypeBit), - NewFieldType(mysql.TypeJSON), - NewFieldType(mysql.TypeNewDecimal), - NewFieldType(mysql.TypeEnum), - NewFieldType(mysql.TypeSet), - NewFieldType(mysql.TypeTinyBlob), - NewFieldType(mysql.TypeMediumBlob), - NewFieldType(mysql.TypeLongBlob), - NewFieldType(mysql.TypeBlob), - NewFieldType(mysql.TypeVarString), - NewFieldType(mysql.TypeString), - NewFieldType(mysql.TypeGeometry), - } - - for i := range fts { - aggTp := AggFieldType(fts[i : i+1]) - require.Equal(t, fts[i].GetType(), aggTp.GetType()) - - aggTp = AggFieldType([]*FieldType{fts[i], fts[i]}) - switch fts[i].GetType() { - case mysql.TypeDate: - require.Equal(t, mysql.TypeDate, aggTp.GetType()) - case mysql.TypeJSON: - require.Equal(t, mysql.TypeJSON, aggTp.GetType()) - case mysql.TypeEnum, mysql.TypeSet, mysql.TypeVarString: - require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) - case mysql.TypeUnspecified: - require.Equal(t, mysql.TypeNewDecimal, aggTp.GetType()) - default: - require.Equal(t, fts[i].GetType(), aggTp.GetType()) - } - - aggTp = AggFieldType([]*FieldType{fts[i], NewFieldType(mysql.TypeLong)}) - switch fts[i].GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, - mysql.TypeYear, mysql.TypeInt24, mysql.TypeNull: - require.Equal(t, mysql.TypeLong, aggTp.GetType()) - case mysql.TypeLonglong: - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - case mysql.TypeFloat, mysql.TypeDouble: - require.Equal(t, mysql.TypeDouble, aggTp.GetType()) - case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDuration, - mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, - mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, - mysql.TypeVarString, mysql.TypeGeometry: - require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) - case mysql.TypeBit: - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - case mysql.TypeString: - require.Equal(t, mysql.TypeString, aggTp.GetType()) - case mysql.TypeUnspecified, mysql.TypeNewDecimal: - require.Equal(t, mysql.TypeNewDecimal, aggTp.GetType()) - case mysql.TypeTinyBlob: - require.Equal(t, mysql.TypeTinyBlob, aggTp.GetType()) - case mysql.TypeBlob: - require.Equal(t, mysql.TypeBlob, aggTp.GetType()) - case mysql.TypeMediumBlob: - require.Equal(t, mysql.TypeMediumBlob, aggTp.GetType()) - case mysql.TypeLongBlob: - require.Equal(t, mysql.TypeLongBlob, aggTp.GetType()) - } - - aggTp = AggFieldType([]*FieldType{fts[i], NewFieldType(mysql.TypeJSON)}) - switch fts[i].GetType() { - case mysql.TypeJSON, mysql.TypeNull: - require.Equal(t, mysql.TypeJSON, aggTp.GetType()) - case mysql.TypeLongBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeBlob: - require.Equal(t, mysql.TypeLongBlob, aggTp.GetType()) - case mysql.TypeString: - require.Equal(t, mysql.TypeString, aggTp.GetType()) - default: - require.Equal(t, mysql.TypeVarchar, aggTp.GetType()) - } - } -} - -func TestAggFieldTypeForTypeFlag(t *testing.T) { - types := []*FieldType{ - NewFieldType(mysql.TypeLonglong), - NewFieldType(mysql.TypeLonglong), - } - - aggTp := AggFieldType(types) - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - - types[0].SetFlag(mysql.NotNullFlag) - aggTp = AggFieldType(types) - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - - types[0].SetFlag(0) - types[1].SetFlag(mysql.NotNullFlag) - aggTp = AggFieldType(types) - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - - types[0].SetFlag(mysql.NotNullFlag) - aggTp = AggFieldType(types) - require.Equal(t, mysql.TypeLonglong, aggTp.GetType()) - require.Equal(t, mysql.NotNullFlag, aggTp.GetFlag()) -} - -func TestAggFieldTypeForIntegralPromotion(t *testing.T) { - fts := []*FieldType{ - NewFieldType(mysql.TypeTiny), - NewFieldType(mysql.TypeShort), - NewFieldType(mysql.TypeInt24), - NewFieldType(mysql.TypeLong), - NewFieldType(mysql.TypeLonglong), - NewFieldType(mysql.TypeNewDecimal), - } - - for i := 1; i < len(fts)-1; i++ { - tps := fts[i-1 : i+1] - - tps[0].SetFlag(0) - tps[1].SetFlag(0) - aggTp := AggFieldType(tps) - require.Equal(t, fts[i].GetType(), aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - - tps[0].SetFlag(mysql.UnsignedFlag) - aggTp = AggFieldType(tps) - require.Equal(t, fts[i].GetType(), aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - - tps[0].SetFlag(mysql.UnsignedFlag) - tps[1].SetFlag(mysql.UnsignedFlag) - aggTp = AggFieldType(tps) - require.Equal(t, fts[i].GetType(), aggTp.GetType()) - require.Equal(t, mysql.UnsignedFlag, aggTp.GetFlag()) - - tps[0].SetFlag(0) - tps[1].SetFlag(mysql.UnsignedFlag) - aggTp = AggFieldType(tps) - require.Equal(t, fts[i+1].GetType(), aggTp.GetType()) - require.Equal(t, uint(0), aggTp.GetFlag()) - } -} - -func TestAggregateEvalType(t *testing.T) { - fts := []*FieldType{ - NewFieldType(mysql.TypeUnspecified), - NewFieldType(mysql.TypeTiny), - NewFieldType(mysql.TypeShort), - NewFieldType(mysql.TypeLong), - NewFieldType(mysql.TypeFloat), - NewFieldType(mysql.TypeDouble), - NewFieldType(mysql.TypeNull), - NewFieldType(mysql.TypeTimestamp), - NewFieldType(mysql.TypeLonglong), - NewFieldType(mysql.TypeInt24), - NewFieldType(mysql.TypeDate), - NewFieldType(mysql.TypeDuration), - NewFieldType(mysql.TypeDatetime), - NewFieldType(mysql.TypeYear), - NewFieldType(mysql.TypeNewDate), - NewFieldType(mysql.TypeVarchar), - NewFieldType(mysql.TypeBit), - NewFieldType(mysql.TypeJSON), - NewFieldType(mysql.TypeNewDecimal), - NewFieldType(mysql.TypeEnum), - NewFieldType(mysql.TypeSet), - NewFieldType(mysql.TypeTinyBlob), - NewFieldType(mysql.TypeMediumBlob), - NewFieldType(mysql.TypeLongBlob), - NewFieldType(mysql.TypeBlob), - NewFieldType(mysql.TypeVarString), - NewFieldType(mysql.TypeString), - NewFieldType(mysql.TypeGeometry), - } - - for i := range fts { - var flag uint - aggregatedEvalType := AggregateEvalType(fts[i:i+1], &flag) - switch fts[i].GetType() { - case mysql.TypeUnspecified, mysql.TypeNull, mysql.TypeTimestamp, mysql.TypeDate, - mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, - mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, - mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob, - mysql.TypeVarString, mysql.TypeString, mysql.TypeGeometry: - require.True(t, aggregatedEvalType.IsStringKind()) - require.Equal(t, uint(0), flag) - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeBit, - mysql.TypeInt24, mysql.TypeYear: - require.Equal(t, ETInt, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeFloat, mysql.TypeDouble: - require.Equal(t, ETReal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeNewDecimal: - require.Equal(t, ETDecimal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - } - - flag = 0 - aggregatedEvalType = AggregateEvalType([]*FieldType{fts[i], fts[i]}, &flag) - switch fts[i].GetType() { - case mysql.TypeUnspecified, mysql.TypeNull, mysql.TypeTimestamp, mysql.TypeDate, - mysql.TypeDuration, mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, - mysql.TypeJSON, mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, - mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob, - mysql.TypeVarString, mysql.TypeString, mysql.TypeGeometry: - require.True(t, aggregatedEvalType.IsStringKind()) - require.Equal(t, uint(0), flag) - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeBit, - mysql.TypeInt24, mysql.TypeYear: - require.Equal(t, ETInt, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeFloat, mysql.TypeDouble: - require.Equal(t, ETReal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeNewDecimal: - require.Equal(t, ETDecimal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - } - flag = 0 - aggregatedEvalType = AggregateEvalType([]*FieldType{fts[i], NewFieldType(mysql.TypeLong)}, &flag) - switch fts[i].GetType() { - case mysql.TypeTimestamp, mysql.TypeDate, mysql.TypeDuration, - mysql.TypeDatetime, mysql.TypeNewDate, mysql.TypeVarchar, mysql.TypeJSON, - mysql.TypeEnum, mysql.TypeSet, mysql.TypeTinyBlob, mysql.TypeMediumBlob, - mysql.TypeLongBlob, mysql.TypeBlob, mysql.TypeVarString, - mysql.TypeString, mysql.TypeGeometry: - require.True(t, aggregatedEvalType.IsStringKind()) - require.Equal(t, uint(0), flag) - case mysql.TypeUnspecified, mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeNull, mysql.TypeBit, - mysql.TypeLonglong, mysql.TypeYear, mysql.TypeInt24: - require.Equal(t, ETInt, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeFloat, mysql.TypeDouble: - require.Equal(t, ETReal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - case mysql.TypeNewDecimal: - require.Equal(t, ETDecimal, aggregatedEvalType) - require.Equal(t, mysql.BinaryFlag, flag) - } - } -} diff --git a/types/format_test.go b/types/format_test.go deleted file mode 100644 index b852fbb678103..0000000000000 --- a/types/format_test.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package types_test - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mock" - "github.com/stretchr/testify/require" -) - -func TestTimeFormatMethod(t *testing.T) { - sc := mock.NewContext().GetSessionVars().StmtCtx - sc.IgnoreZeroInDate = true - tblDate := []struct { - Input string - Format string - Expect string - }{ - { - "2010-01-07 23:12:34.12345", - `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, - `Jan January 01 1 7th 07 7 007 23 11 12 PM 11:12:34 PM 23:12:34 34 123450 01 01 01 01 Thu Thursday 4 2010 2010 2010 10 %`, - }, - { - "2012-12-21 23:12:34.123456", - `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, - `Dec December 12 12 21st 21 21 356 23 11 12 PM 11:12:34 PM 23:12:34 34 123456 51 51 51 51 Fri Friday 5 2012 2012 2012 12 %`, - }, - { - "0000-01-01 00:00:00.123456", - // Functions week() and yearweek() don't support multi mode, - // so the result of "%U %u %V %Y" is different from MySQL. - `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %v %Y %y %%`, - `Jan January 01 1 1st 01 1 001 0 12 00 AM 12:00:00 AM 00:00:00 00 123456 52 0000 00 %`, - }, - { - "2016-09-3 00:59:59.123456", - `abc%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y!123 %%xyz %z`, - `abcSep September 09 9 3rd 03 3 247 0 12 59 AM 12:59:59 AM 00:59:59 59 123456 35 35 35 35 Sat Saturday 6 2016 2016 2016 16!123 %xyz z`, - }, - { - "2012-10-01 00:00:00", - `%b %M %m %c %D %d %e %j %k %H %i %p %r %T %s %f %v %x %Y %y %%`, - `Oct October 10 10 1st 01 1 275 0 00 00 AM 12:00:00 AM 00:00:00 00 000000 40 2012 2012 12 %`, - }, - { - // For invalid date month or year = 0, MySQL behavior is confusing, %U (which format Week()) is 52, but Week() is 0. - // It's because in MySQL, Week() checks invalid date before processing, but DateFormat() don't. - // So there are some difference to MySQL here (%U %u %V %v), TiDB user should not rely on those corner case behavior. - // %W %w %a is not compatible in this case because Week() use GoTime() currently. - "0000-01-00 00:00:00.123456", - `%b %M %m %c %D %d %e %j %k %h %i %p %r %T %s %f %U %u %V %v %a %W %w %X %x %Y %y %%`, - `Jan January 01 1 0th 00 0 000 0 12 00 AM 12:00:00 AM 00:00:00 00 123456 00 00 00 52 Fri Friday 5 4294967295 4294967295 0000 00 %`, - }, - } - for i, tt := range tblDate { - tm, err := types.ParseTime(sc, tt.Input, mysql.TypeDatetime, 6, nil) - require.NoErrorf(t, err, "Parse time fail: %s", tt.Input) - - str, err := tm.DateFormat(tt.Format) - require.NoErrorf(t, err, "time format fail: %d", i) - require.Equalf(t, tt.Expect, str, "no.%d \nobtain:%v \nexpect:%v\n", i, str, tt.Expect) - } -} - -func TestStrToDate(t *testing.T) { - sc := mock.NewContext().GetSessionVars().StmtCtx - sc.IgnoreZeroInDate = true - tests := []struct { - input string - format string - expect types.CoreTime - }{ - {`01,05,2013`, `%d,%m,%Y`, types.FromDate(2013, 5, 1, 0, 0, 0, 0)}, - {`5 12 2021`, `%m%d%Y`, types.FromDate(2021, 5, 12, 0, 0, 0, 0)}, - {`May 01, 2013`, `%M %d,%Y`, types.FromDate(2013, 5, 1, 0, 0, 0, 0)}, - {`a09:30:17`, `a%h:%i:%s`, types.FromDate(0, 0, 0, 9, 30, 17, 0)}, - {`09:30:17a`, `%h:%i:%s`, types.FromDate(0, 0, 0, 9, 30, 17, 0)}, - {`12:43:24`, `%h:%i:%s`, types.FromDate(0, 0, 0, 0, 43, 24, 0)}, - {`abc`, `abc`, types.ZeroCoreTime}, - {`09`, `%m`, types.FromDate(0, 9, 0, 0, 0, 0, 0)}, - {`09`, `%s`, types.FromDate(0, 0, 0, 0, 0, 9, 0)}, - {`12:43:24 AM`, `%r`, types.FromDate(0, 0, 0, 0, 43, 24, 0)}, - {`12:43:24 PM`, `%r`, types.FromDate(0, 0, 0, 12, 43, 24, 0)}, - {`11:43:24 PM`, `%r`, types.FromDate(0, 0, 0, 23, 43, 24, 0)}, - {`00:12:13`, `%T`, types.FromDate(0, 0, 0, 0, 12, 13, 0)}, - {`23:59:59`, `%T`, types.FromDate(0, 0, 0, 23, 59, 59, 0)}, - {`00/00/0000`, `%m/%d/%Y`, types.ZeroCoreTime}, - {`04/30/2004`, `%m/%d/%Y`, types.FromDate(2004, 4, 30, 0, 0, 0, 0)}, - {`15:35:00`, `%H:%i:%s`, types.FromDate(0, 0, 0, 15, 35, 0, 0)}, - {`Jul 17 33`, `%b %k %S`, types.FromDate(0, 7, 0, 17, 0, 33, 0)}, - {`2016-January:7 432101`, `%Y-%M:%l %f`, types.FromDate(2016, 1, 0, 7, 0, 0, 432101)}, - {`10:13 PM`, `%l:%i %p`, types.FromDate(0, 0, 0, 22, 13, 0, 0)}, - {`12:00:00 AM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 0, 0, 0, 0)}, - {`12:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, - {`12:00:00 PM`, `%I:%i:%s %p`, types.FromDate(0, 0, 0, 12, 0, 0, 0)}, - {`1:00:00 PM`, `%h:%i:%s %p`, types.FromDate(0, 0, 0, 13, 0, 0, 0)}, - {`18/10/22`, `%y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, - {`8/10/22`, `%y/%m/%d`, types.FromDate(2008, 10, 22, 0, 0, 0, 0)}, - {`69/10/22`, `%y/%m/%d`, types.FromDate(2069, 10, 22, 0, 0, 0, 0)}, - {`70/10/22`, `%y/%m/%d`, types.FromDate(1970, 10, 22, 0, 0, 0, 0)}, - {`18/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, - {`2018/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, - {`8/10/22`, `%Y/%m/%d`, types.FromDate(2008, 10, 22, 0, 0, 0, 0)}, - {`69/10/22`, `%Y/%m/%d`, types.FromDate(2069, 10, 22, 0, 0, 0, 0)}, - {`70/10/22`, `%Y/%m/%d`, types.FromDate(1970, 10, 22, 0, 0, 0, 0)}, - {`18/10/22`, `%Y/%m/%d`, types.FromDate(2018, 10, 22, 0, 0, 0, 0)}, - {`100/10/22`, `%Y/%m/%d`, types.FromDate(100, 10, 22, 0, 0, 0, 0)}, - {`09/10/1021`, `%d/%m/%y`, types.FromDate(2010, 10, 9, 0, 0, 0, 0)}, // '%y' only accept up to 2 digits for year - {`09/10/1021`, `%d/%m/%Y`, types.FromDate(1021, 10, 9, 0, 0, 0, 0)}, // '%Y' accept up to 4 digits for year - {`09/10/10`, `%d/%m/%Y`, types.FromDate(2010, 10, 9, 0, 0, 0, 0)}, // '%Y' will fix the year for only 2 digits - //'%b'/'%M' should be case insensitive - {"31/may/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 5, 31, 12, 34, 56, 123400)}, - {"30/april/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 30, 12, 34, 56, 0)}, - {"31/mAy/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 5, 31, 12, 34, 56, 123400)}, - {"30/apRil/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 30, 12, 34, 56, 0)}, - // '%r' - {" 04 :13:56 AM13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 4, 13, 56, 0)}, // - {"12: 13:56 AM 13/05/2019", "%r%d/%c/%Y", types.FromDate(2019, 5, 13, 0, 13, 56, 0)}, // - {"12:13 :56 pm 13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 13, 56, 0)}, // - {"12:3: 56pm 13/05/2019", "%r %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 3, 56, 0)}, // - {"11:13:56", "%r", types.FromDate(0, 0, 0, 11, 13, 56, 0)}, // EOF before parsing "AM"/"PM" - {"11:13", "%r", types.FromDate(0, 0, 0, 11, 13, 0, 0)}, // EOF after hh:mm - {"11:", "%r", types.FromDate(0, 0, 0, 11, 0, 0, 0)}, // EOF after hh: - {"11", "%r", types.FromDate(0, 0, 0, 11, 0, 0, 0)}, // EOF after hh: - {"12", "%r", types.FromDate(0, 0, 0, 0, 0, 0, 0)}, // EOF after hh:, and hh=12 -> 0 - // '%T' - {" 4 :13:56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 4, 13, 56, 0)}, - {"23: 13:56 13/05/2019", "%T%d/%c/%Y", types.FromDate(2019, 5, 13, 23, 13, 56, 0)}, - {"12:13 :56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 12, 13, 56, 0)}, - {"19:3: 56 13/05/2019", "%T %d/%c/%Y", types.FromDate(2019, 5, 13, 19, 3, 56, 0)}, - {"21:13", "%T", types.FromDate(0, 0, 0, 21, 13, 0, 0)}, // EOF after hh:mm - {"21:", "%T", types.FromDate(0, 0, 0, 21, 0, 0, 0)}, // EOF after hh: - // More patterns than input string - {" 2/Jun", "%d/%b/%Y", types.FromDate(0, 6, 2, 0, 0, 0, 0)}, - {" liter", "lit era l", types.ZeroCoreTime}, - // Feb 29 in leap-year - {"29/Feb/2020 12:34:56.", "%d/%b/%Y %H:%i:%s.%f", types.FromDate(2020, 2, 29, 12, 34, 56, 0)}, - // When `AllowInvalidDate` is true, check only that the month is in the range from 1 to 12 and the day is in the range from 1 to 31 - {"31/April/2016 12:34:56.", "%d/%M/%Y %H:%i:%s.%f", types.FromDate(2016, 4, 31, 12, 34, 56, 0)}, // April 31th - {"29/Feb/2021 12:34:56.", "%d/%b/%Y %H:%i:%s.%f", types.FromDate(2021, 2, 29, 12, 34, 56, 0)}, // Feb 29 in non-leap-year - {"30/Feb/2016 12:34:56.1234", "%d/%b/%Y %H:%i:%S.%f", types.FromDate(2016, 2, 30, 12, 34, 56, 123400)}, // Feb 30th - } - for i, tt := range tests { - sc.AllowInvalidDate = true - var time types.Time - require.Truef(t, time.StrToDate(sc, tt.input, tt.format), "no.%d failed input=%s format=%s", i, tt.input, tt.format) - require.Equalf(t, tt.expect, time.CoreTime(), "no.%d failed input=%s format=%s", i, tt.input, tt.format) - } - - errTests := []struct { - input string - format string - }{ - // invalid days when `AllowInvalidDate` is false - {`04/31/2004`, `%m/%d/%Y`}, // not exists in the real world - {"29/Feb/2021 12:34:56.", "%d/%b/%Y %H:%i:%s.%f"}, // Feb 29 in non-leap-year - - {`512 2021`, `%m%d %Y`}, // MySQL will try to parse '51' for '%m', fail - - {`a09:30:17`, `%h:%i:%s`}, // format mismatch - {`12:43:24 a`, `%r`}, // followed by incomplete 'AM'/'PM' - {`23:60:12`, `%T`}, // invalid minute - {`18`, `%l`}, - {`00:21:22 AM`, `%h:%i:%s %p`}, - {`100/10/22`, `%y/%m/%d`}, - {"2010-11-12 11 am", `%Y-%m-%d %H %p`}, - {"2010-11-12 13 am", `%Y-%m-%d %h %p`}, - {"2010-11-12 0 am", `%Y-%m-%d %h %p`}, - // MySQL accept `SEPTEMB` as `SEPTEMBER`, but we don't want this "feature" in TiDB - // unless we have to. - {"15 SEPTEMB 2001", "%d %M %Y"}, - // '%r' - {"13:13:56 AM13/5/2019", "%r"}, // hh = 13 with am is invalid - {"00:13:56 AM13/05/2019", "%r"}, // hh = 0 with am is invalid - {"00:13:56 pM13/05/2019", "%r"}, // hh = 0 with pm is invalid - {"11:13:56a", "%r"}, // EOF while parsing "AM"/"PM" - } - for i, tt := range errTests { - sc.AllowInvalidDate = false - var time types.Time - require.Falsef(t, time.StrToDate(sc, tt.input, tt.format), "no.%d failed input=%s format=%s", i, tt.input, tt.format) - } -} diff --git a/types/main_test.go b/types/main_test.go deleted file mode 100644 index 8caf6e228dfc0..0000000000000 --- a/types/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package types - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/types/parser_driver/BUILD.bazel b/types/parser_driver/BUILD.bazel deleted file mode 100644 index 4c2260dd5bc43..0000000000000 --- a/types/parser_driver/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "parser_driver", - srcs = ["value_expr.go"], - importpath = "github.com/pingcap/tidb/types/parser_driver", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/format", - "//parser/mysql", - "//types", - "//util/hack", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "parser_driver_test", - timeout = "short", - srcs = [ - "main_test.go", - "value_expr_test.go", - ], - embed = [":parser_driver"], - flaky = True, - deps = [ - "//parser/format", - "//testkit/testsetup", - "//types", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/types/parser_driver/main_test.go b/types/parser_driver/main_test.go deleted file mode 100644 index eb42a285f3a87..0000000000000 --- a/types/parser_driver/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package driver - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/types/set.go b/types/set.go deleted file mode 100644 index 2be763951eca6..0000000000000 --- a/types/set.go +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/stringutil" -) - -var zeroSet = Set{Name: "", Value: 0} - -// Set is for MySQL Set type. -type Set struct { - Name string - Value uint64 -} - -// String implements fmt.Stringer interface. -func (e Set) String() string { - return e.Name -} - -// ToNumber changes Set to float64 for numeric operation. -func (e Set) ToNumber() float64 { - return float64(e.Value) -} - -// Copy deep copy a Set. -func (e Set) Copy() Set { - return Set{ - Name: stringutil.Copy(e.Name), - Value: e.Value, - } -} - -// ParseSet creates a Set with name or value. -func ParseSet(elems []string, name string, collation string) (Set, error) { - if setName, err := ParseSetName(elems, name, collation); err == nil { - return setName, nil - } - // name doesn't exist, maybe an integer? - if num, err := strconv.ParseUint(name, 0, 64); err == nil { - return ParseSetValue(elems, num) - } - - return Set{}, errors.Errorf("item %s is not in Set %v", name, elems) -} - -// ParseSetName creates a Set with name. -func ParseSetName(elems []string, name string, collation string) (Set, error) { - if len(name) == 0 { - return zeroSet, nil - } - - ctor := collate.GetCollator(collation) - - seps := strings.Split(name, ",") - marked := make(map[string]struct{}, len(seps)) - for _, s := range seps { - marked[string(ctor.Key(s))] = struct{}{} - } - items := make([]string, 0, len(seps)) - - value := uint64(0) - for i, n := range elems { - key := string(ctor.Key(n)) - if _, ok := marked[key]; ok { - value |= 1 << uint64(i) - delete(marked, key) - items = append(items, n) - } - } - - if len(marked) == 0 { - return Set{Name: strings.Join(items, ","), Value: value}, nil - } - - return Set{}, errors.Errorf("item %s is not in Set %v", name, elems) -} - -var ( - setIndexValue []uint64 - setIndexInvertValue []uint64 -) - -func init() { - setIndexValue = make([]uint64, 64) - setIndexInvertValue = make([]uint64, 64) - - for i := 0; i < 64; i++ { - setIndexValue[i] = 1 << uint64(i) - setIndexInvertValue[i] = ^setIndexValue[i] - } -} - -// ParseSetValue creates a Set with special number. -func ParseSetValue(elems []string, number uint64) (Set, error) { - if number == 0 { - return zeroSet, nil - } - - value := number - var items []string - for i := 0; i < len(elems); i++ { - if number&setIndexValue[i] > 0 { - items = append(items, elems[i]) - number &= setIndexInvertValue[i] - } - } - - if number != 0 { - return Set{}, errors.Errorf("invalid number %d for Set %v", number, elems) - } - - return Set{Name: strings.Join(items, ","), Value: value}, nil -} diff --git a/types/set_test.go b/types/set_test.go deleted file mode 100644 index fcc4fe5b850ee..0000000000000 --- a/types/set_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package types - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/stretchr/testify/require" -) - -func TestSet(t *testing.T) { - elems := []string{"a", "b", "c", "d"} - - t.Run("ParseSet", func(t *testing.T) { - tests := []struct { - Name string - ExpectedValue uint64 - ExpectedName string - }{ - {"a", 1, "a"}, - {"a,b,a", 3, "a,b"}, - {"b,a", 3, "a,b"}, - {"a,b,c,d", 15, "a,b,c,d"}, - {"d", 8, "d"}, - {"", 0, ""}, - {"0", 0, ""}, - } - - for _, collation := range []string{mysql.DefaultCollationName, "utf8_unicode_ci"} { - for _, test := range tests { - e, err := ParseSet(elems, test.Name, collation) - require.NoError(t, err) - require.Equal(t, float64(test.ExpectedValue), e.ToNumber()) - require.Equal(t, test.ExpectedName, e.String()) - } - } - }) - - t.Run("ParseSet_ci", func(t *testing.T) { - tests := []struct { - Name string - ExpectedValue uint64 - ExpectedName string - }{ - {"A ", 1, "a"}, - {"a,B,a", 3, "a,b"}, - } - - for _, test := range tests { - e, err := ParseSet(elems, test.Name, "utf8_general_ci") - require.NoError(t, err) - require.Equal(t, float64(test.ExpectedValue), e.ToNumber()) - require.Equal(t, test.ExpectedName, e.String()) - } - }) - - t.Run("ParseSetValue", func(t *testing.T) { - tests := []struct { - Number uint64 - ExpectedName string - }{ - {0, ""}, - {1, "a"}, - {3, "a,b"}, - {9, "a,d"}, - } - - for _, test := range tests { - e, err := ParseSetValue(elems, test.Number) - require.NoError(t, err) - require.Equal(t, float64(test.Number), e.ToNumber()) - require.Equal(t, test.ExpectedName, e.String()) - } - }) - - t.Run("ParseSet_err", func(t *testing.T) { - tests := []string{"a.e", "e.f"} - for _, test := range tests { - _, err := ParseSet(elems, test, mysql.DefaultCollationName) - require.Error(t, err) - } - }) - - t.Run("ParseSetValue_err", func(t *testing.T) { - tests := []uint64{100, 16, 64} - for _, test := range tests { - _, err := ParseSetValue(elems, test) - require.Error(t, err) - } - }) -} diff --git a/util/BUILD.bazel b/util/BUILD.bazel deleted file mode 100644 index 972c76bc4200f..0000000000000 --- a/util/BUILD.bazel +++ /dev/null @@ -1,90 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "util", - srcs = [ - "cpu_posix.go", - "cpu_windows.go", - "errors.go", - "etcd.go", - "gogc.go", - "id_generator.go", - "misc.go", - "prefix_helper.go", - "printer.go", - "processinfo.go", - "rlimit_other.go", - "rlimit_windows.go", - "security.go", - "urls.go", - "util.go", - "wait_group_wrapper.go", - ], - importpath = "github.com/pingcap/tidb/util", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//kv", - "//metrics", - "//parser", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//session/txninfo", - "//sessionctx/stmtctx", - "//util/collate", - "//util/disk", - "//util/execdetails", - "//util/logutil", - "//util/memory", - "//util/tls", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_log//:log", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//oracle", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//concurrency", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "util_test", - timeout = "short", - srcs = [ - "errors_test.go", - "main_test.go", - "misc_test.go", - "prefix_helper_test.go", - "security_test.go", - "urls_test.go", - "util_test.go", - "wait_group_wrapper_test.go", - ], - data = glob(["tls_test/**"]), - embed = [":util"], - flaky = True, - shard_count = 50, - deps = [ - "//kv", - "//parser", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//store/mockstore", - "//testkit/testsetup", - "//types", - "//util/fastrand", - "//util/memory", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/admin/BUILD.bazel b/util/admin/BUILD.bazel deleted file mode 100644 index 1df58edd205e1..0000000000000 --- a/util/admin/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "admin", - srcs = ["admin.go"], - importpath = "github.com/pingcap/tidb/util/admin", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//expression", - "//kv", - "//parser/model", - "//parser/mysql", - "//sessionctx", - "//table", - "//tablecodec", - "//types", - "//util", - "//util/dbterror", - "//util/logutil", - "//util/logutil/consistency", - "//util/rowDecoder", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "admin_test", - timeout = "short", - srcs = [ - "admin_integration_test.go", - "main_test.go", - ], - embed = [":admin"], - flaky = True, - deps = [ - "//config", - "//tablecodec", - "//testkit", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/admin/admin.go b/util/admin/admin.go deleted file mode 100644 index 51209c94aa1a8..0000000000000 --- a/util/admin/admin.go +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package admin - -import ( - "context" - "math" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/logutil/consistency" - decoder "github.com/pingcap/tidb/util/rowDecoder" - "github.com/pingcap/tidb/util/sqlexec" - "go.uber.org/zap" -) - -// RecordData is the record data composed of a handle and values. -type RecordData struct { - Handle kv.Handle - Values []types.Datum -} - -func getCount(exec sqlexec.RestrictedSQLExecutor, snapshot uint64, sql string, args ...interface{}) (int64, error) { - ctx := kv.WithInternalSourceType(context.Background(), kv.InternalTxnAdmin) - rows, _, err := exec.ExecRestrictedSQL(ctx, []sqlexec.OptionFuncAlias{sqlexec.ExecOptionWithSnapshot(snapshot)}, sql, args...) - if err != nil { - return 0, errors.Trace(err) - } - if len(rows) != 1 { - return 0, errors.Errorf("can not get count, rows count = %d", len(rows)) - } - return rows[0].GetInt64(0), nil -} - -// Count greater Types -const ( - // TblCntGreater means that the number of table rows is more than the number of index rows. - TblCntGreater byte = 1 - // IdxCntGreater means that the number of index rows is more than the number of table rows. - IdxCntGreater byte = 2 -) - -// CheckIndicesCount compares indices count with table count. -// It returns the count greater type, the index offset and an error. -// It returns nil if the count from the index is equal to the count from the table columns, -// otherwise it returns an error and the corresponding index's offset. -func CheckIndicesCount(ctx sessionctx.Context, dbName, tableName string, indices []string) (byte, int, error) { - // Here we need check all indexes, includes invisible index - ctx.GetSessionVars().OptimizerUseInvisibleIndexes = true - defer func() { - ctx.GetSessionVars().OptimizerUseInvisibleIndexes = false - }() - - var snapshot uint64 - txn, err := ctx.Txn(false) - if err != nil { - return 0, 0, err - } - if txn.Valid() { - snapshot = txn.StartTS() - } - if ctx.GetSessionVars().SnapshotTS != 0 { - snapshot = ctx.GetSessionVars().SnapshotTS - } - - // Add `` for some names like `table name`. - exec := ctx.(sqlexec.RestrictedSQLExecutor) - tblCnt, err := getCount(exec, snapshot, "SELECT COUNT(*) FROM %n.%n USE INDEX()", dbName, tableName) - if err != nil { - return 0, 0, errors.Trace(err) - } - for i, idx := range indices { - idxCnt, err := getCount(exec, snapshot, "SELECT COUNT(*) FROM %n.%n USE INDEX(%n)", dbName, tableName, idx) - if err != nil { - return 0, i, errors.Trace(err) - } - logutil.Logger(context.Background()).Info("check indices count", - zap.String("table", tableName), zap.Int64("tblCnt", tblCnt), zap.Reflect("index", idx), zap.Int64("idxCnt", idxCnt)) - if tblCnt == idxCnt { - continue - } - - var ret byte - if tblCnt > idxCnt { - ret = TblCntGreater - } else if idxCnt > tblCnt { - ret = IdxCntGreater - } - return ret, i, ErrAdminCheckTable.GenWithStack("table count %d != index(%s) count %d", tblCnt, idx, idxCnt) - } - return 0, 0, nil -} - -// CheckRecordAndIndex is exported for testing. -func CheckRecordAndIndex(ctx context.Context, sessCtx sessionctx.Context, txn kv.Transaction, t table.Table, idx table.Index) error { - sc := sessCtx.GetSessionVars().StmtCtx - cols := make([]*table.Column, len(idx.Meta().Columns)) - for i, col := range idx.Meta().Columns { - cols[i] = t.Cols()[col.Offset] - } - - ir := func() *consistency.Reporter { - return &consistency.Reporter{ - HandleEncode: func(handle kv.Handle) kv.Key { - return tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) - }, - IndexEncode: func(idxRow *consistency.RecordData) kv.Key { - var matchingIdx table.Index - for _, v := range t.Indices() { - if strings.EqualFold(v.Meta().Name.String(), idx.Meta().Name.O) { - matchingIdx = v - break - } - } - if matchingIdx == nil { - return nil - } - k, _, err := matchingIdx.GenIndexKey(sessCtx.GetSessionVars().StmtCtx, idxRow.Values, idxRow.Handle, nil) - if err != nil { - return nil - } - return k - }, - Tbl: t.Meta(), - Idx: idx.Meta(), - Sctx: sessCtx, - } - } - - startKey := tablecodec.EncodeRecordKey(t.RecordPrefix(), kv.IntHandle(math.MinInt64)) - filterFunc := func(h1 kv.Handle, vals1 []types.Datum, cols []*table.Column) (bool, error) { - for i, val := range vals1 { - col := cols[i] - if val.IsNull() { - if mysql.HasNotNullFlag(col.GetFlag()) && col.ToInfo().GetOriginDefaultValue() == nil { - return false, errors.Errorf("Column %v define as not null, but can't find the value where handle is %v", col.Name, h1) - } - // NULL value is regarded as its default value. - colDefVal, err := table.GetColOriginDefaultValue(sessCtx, col.ToInfo()) - if err != nil { - return false, errors.Trace(err) - } - vals1[i] = colDefVal - } - } - isExist, h2, err := idx.Exist(sc, txn, vals1, h1) - if kv.ErrKeyExists.Equal(err) { - record1 := &consistency.RecordData{Handle: h1, Values: vals1} - record2 := &consistency.RecordData{Handle: h2, Values: vals1} - return false, ir().ReportAdminCheckInconsistent(ctx, h1, record2, record1) - } - if err != nil { - return false, errors.Trace(err) - } - if !isExist { - record := &consistency.RecordData{Handle: h1, Values: vals1} - return false, ir().ReportAdminCheckInconsistent(ctx, h1, nil, record) - } - - return true, nil - } - err := iterRecords(sessCtx, txn, t, startKey, cols, filterFunc) - if err != nil { - return errors.Trace(err) - } - - return nil -} - -func makeRowDecoder(t table.Table, sctx sessionctx.Context) (*decoder.RowDecoder, error) { - dbName := model.NewCIStr(sctx.GetSessionVars().CurrentDB) - exprCols, _, err := expression.ColumnInfos2ColumnsAndNames(sctx, dbName, t.Meta().Name, t.Meta().Cols(), t.Meta()) - if err != nil { - return nil, err - } - mockSchema := expression.NewSchema(exprCols...) - decodeColsMap := decoder.BuildFullDecodeColMap(t.Cols(), mockSchema) - - return decoder.NewRowDecoder(t, t.Cols(), decodeColsMap), nil -} - -func iterRecords(sessCtx sessionctx.Context, retriever kv.Retriever, t table.Table, startKey kv.Key, cols []*table.Column, fn table.RecordIterFunc) error { - prefix := t.RecordPrefix() - keyUpperBound := prefix.PrefixNext() - - it, err := retriever.Iter(startKey, keyUpperBound) - if err != nil { - return errors.Trace(err) - } - defer it.Close() - - if !it.Valid() { - return nil - } - - logutil.BgLogger().Debug("record", - zap.Stringer("startKey", startKey), - zap.Stringer("key", it.Key()), - zap.Binary("value", it.Value())) - rowDecoder, err := makeRowDecoder(t, sessCtx) - if err != nil { - return err - } - for it.Valid() && it.Key().HasPrefix(prefix) { - // first kv pair is row lock information. - // TODO: check valid lock - // get row handle - handle, err := tablecodec.DecodeRowKey(it.Key()) - if err != nil { - return errors.Trace(err) - } - - rowMap, err := rowDecoder.DecodeAndEvalRowWithMap(sessCtx, handle, it.Value(), sessCtx.GetSessionVars().Location(), nil) - if err != nil { - return errors.Trace(err) - } - data := make([]types.Datum, 0, len(cols)) - for _, col := range cols { - data = append(data, rowMap[col.ID]) - } - more, err := fn(handle, data, cols) - if !more || err != nil { - return errors.Trace(err) - } - - rk := tablecodec.EncodeRecordKey(t.RecordPrefix(), handle) - err = kv.NextUntil(it, util.RowKeyPrefixFilter(rk)) - if err != nil { - return errors.Trace(err) - } - } - - return nil -} - -var ( - // ErrAdminCheckTable returns when the table records is inconsistent with the index values. - ErrAdminCheckTable = dbterror.ClassAdmin.NewStd(errno.ErrAdminCheckTable) -) diff --git a/util/admin/main_test.go b/util/admin/main_test.go deleted file mode 100644 index c426d6d3c456b..0000000000000 --- a/util/admin/main_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package admin - -import ( - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - config.UpdateGlobal(func(conf *config.Config) { - conf.TiKVClient.AsyncCommit.SafeWindow = 0 - conf.TiKVClient.AsyncCommit.AllowedClockDrift = 0 - }) - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/arena/BUILD.bazel b/util/arena/BUILD.bazel deleted file mode 100644 index b38c606afd35a..0000000000000 --- a/util/arena/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "arena", - srcs = ["arena.go"], - importpath = "github.com/pingcap/tidb/util/arena", - visibility = ["//visibility:public"], -) - -go_test( - name = "arena_test", - timeout = "short", - srcs = [ - "arena_test.go", - "main_test.go", - ], - embed = [":arena"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/arena/main_test.go b/util/arena/main_test.go deleted file mode 100644 index fa57e3c65717a..0000000000000 --- a/util/arena/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package arena - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/backoff/BUILD.bazel b/util/backoff/BUILD.bazel deleted file mode 100644 index f04de556b217e..0000000000000 --- a/util/backoff/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "backoff", - srcs = ["backoff.go"], - importpath = "github.com/pingcap/tidb/util/backoff", - visibility = ["//visibility:public"], -) - -go_test( - name = "backoff_test", - timeout = "short", - srcs = ["backoff_test.go"], - embed = [":backoff"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/benchdaily/BUILD.bazel b/util/benchdaily/BUILD.bazel deleted file mode 100644 index ebf2b340d9334..0000000000000 --- a/util/benchdaily/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "benchdaily", - srcs = ["bench_daily.go"], - importpath = "github.com/pingcap/tidb/util/benchdaily", - visibility = ["//visibility:public"], -) - -go_test( - name = "benchdaily_test", - timeout = "short", - srcs = [ - "bench_daily_test.go", - "main_test.go", - ], - embed = [":benchdaily"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/benchdaily/main_test.go b/util/benchdaily/main_test.go deleted file mode 100644 index a7996b81990fb..0000000000000 --- a/util/benchdaily/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package benchdaily - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/bitmap/BUILD.bazel b/util/bitmap/BUILD.bazel deleted file mode 100644 index 533eb910bb40b..0000000000000 --- a/util/bitmap/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "bitmap", - srcs = ["concurrent.go"], - importpath = "github.com/pingcap/tidb/util/bitmap", - visibility = ["//visibility:public"], -) - -go_test( - name = "bitmap_test", - timeout = "short", - srcs = [ - "concurrent_test.go", - "main_test.go", - ], - embed = [":bitmap"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/bitmap/main_test.go b/util/bitmap/main_test.go deleted file mode 100644 index 465b1ff1d89c5..0000000000000 --- a/util/bitmap/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package bitmap - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/breakpoint/BUILD.bazel b/util/breakpoint/BUILD.bazel deleted file mode 100644 index ecc1f6e7f6e03..0000000000000 --- a/util/breakpoint/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "breakpoint", - srcs = ["breakpoint.go"], - importpath = "github.com/pingcap/tidb/util/breakpoint", - visibility = ["//visibility:public"], - deps = [ - "//sessionctx", - "//util/stringutil", - "@com_github_pingcap_failpoint//:failpoint", - ], -) diff --git a/util/cgroup/BUILD.bazel b/util/cgroup/BUILD.bazel deleted file mode 100644 index 9e5c95f146db3..0000000000000 --- a/util/cgroup/BUILD.bazel +++ /dev/null @@ -1,76 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cgroup", - srcs = [ - "cgroup.go", - "cgroup_cpu.go", - "cgroup_cpu_linux.go", - "cgroup_cpu_unsupport.go", - "cgroup_memory.go", - ], - importpath = "github.com/pingcap/tidb/util/cgroup", - visibility = ["//visibility:public"], - deps = [ - "@com_github_cockroachdb_errors//:errors", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ] + select({ - "@io_bazel_rules_go//go/platform:aix": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:android": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:darwin": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:dragonfly": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:freebsd": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:illumos": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:ios": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:js": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:netbsd": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:openbsd": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:plan9": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:solaris": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "@io_bazel_rules_go//go/platform:windows": [ - "@com_github_pingcap_failpoint//:failpoint", - ], - "//conditions:default": [], - }), -) - -go_test( - name = "cgroup_test", - timeout = "short", - srcs = [ - "cgroup_cpu_test.go", - "cgroup_mock_test.go", - ], - embed = [":cgroup"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/channel/BUILD.bazel b/util/channel/BUILD.bazel deleted file mode 100644 index e04118364ed1d..0000000000000 --- a/util/channel/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "channel", - srcs = ["channel.go"], - importpath = "github.com/pingcap/tidb/util/channel", - visibility = ["//visibility:public"], -) diff --git a/util/checksum/BUILD.bazel b/util/checksum/BUILD.bazel deleted file mode 100644 index a2f10512f66aa..0000000000000 --- a/util/checksum/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "checksum", - srcs = ["checksum.go"], - importpath = "github.com/pingcap/tidb/util/checksum", - visibility = ["//visibility:public"], - deps = ["//util/zeropool"], -) - -go_test( - name = "checksum_test", - timeout = "short", - srcs = [ - "checksum_test.go", - "main_test.go", - ], - embed = [":checksum"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/encrypt", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/checksum/checksum.go b/util/checksum/checksum.go deleted file mode 100644 index cabd5120a358b..0000000000000 --- a/util/checksum/checksum.go +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package checksum - -import ( - "encoding/binary" - "errors" - "hash/crc32" - "io" - - "github.com/pingcap/tidb/util/zeropool" -) - -const ( - // the size of whole checksum block - checksumBlockSize = 1024 - // the size of checksum field, we use CRC-32 algorithm to generate a 4 bytes checksum - checksumSize = 4 - // the size of the payload of a checksum block - checksumPayloadSize = checksumBlockSize - checksumSize -) - -var checksumReaderBufPool = zeropool.New[[]byte](func() []byte { - return make([]byte, checksumBlockSize) -}) - -// Writer implements an io.WriteCloser, it calculates and stores a CRC-32 checksum for the payload before -// writing to the underlying object. -// -// For example, a layout of the checksum block which payload is 2100 bytes is as follow: -// -// | -- 4B -- | -- 1020B -- || -- 4B -- | -- 1020B -- || -- 4B -- | -- 60B -- | -// | -- checksum -- | -- payload -- || -- checksum -- | -- payload -- || -- checksum -- | -- payload -- | -type Writer struct { - err error - w io.WriteCloser - buf []byte - payload []byte - payloadUsed int - flushedUserDataCnt int64 -} - -// NewWriter returns a new Writer which calculates and stores a CRC-32 checksum for the payload before -// writing to the underlying object. -func NewWriter(w io.WriteCloser) *Writer { - checksumWriter := &Writer{w: w} - checksumWriter.buf = make([]byte, checksumBlockSize) - checksumWriter.payload = checksumWriter.buf[checksumSize:] - checksumWriter.payloadUsed = 0 - return checksumWriter -} - -// AvailableSize returns how many bytes are unused in the buffer. -func (w *Writer) AvailableSize() int { return checksumPayloadSize - w.payloadUsed } - -// Write implements the io.Writer interface. -func (w *Writer) Write(p []byte) (n int, err error) { - for len(p) > w.AvailableSize() && w.err == nil { - copiedNum := copy(w.payload[w.payloadUsed:], p) - w.payloadUsed += copiedNum - err = w.Flush() - if err != nil { - return - } - n += copiedNum - p = p[copiedNum:] - } - if w.err != nil { - return n, w.err - } - copiedNum := copy(w.payload[w.payloadUsed:], p) - w.payloadUsed += copiedNum - n += copiedNum - return -} - -// Buffered returns the number of bytes that have been written into the current buffer. -func (w *Writer) Buffered() int { return w.payloadUsed } - -// Flush writes all the buffered data to the underlying object. -func (w *Writer) Flush() error { - if w.err != nil { - return w.err - } - if w.payloadUsed == 0 { - return nil - } - checksum := crc32.Checksum(w.payload[:w.payloadUsed], crc32.MakeTable(crc32.IEEE)) - binary.LittleEndian.PutUint32(w.buf, checksum) - n, err := w.w.Write(w.buf[:w.payloadUsed+checksumSize]) - if n < w.payloadUsed && err == nil { - err = io.ErrShortWrite - } - if err != nil { - w.err = err - return err - } - w.flushedUserDataCnt += int64(w.payloadUsed) - w.payloadUsed = 0 - return nil -} - -// GetCache returns the byte slice that holds the data not flushed to disk. -func (w *Writer) GetCache() []byte { - return w.payload[:w.payloadUsed] -} - -// GetCacheDataOffset return the user data offset in cache. -func (w *Writer) GetCacheDataOffset() int64 { - return w.flushedUserDataCnt -} - -// Close implements the io.Closer interface. -func (w *Writer) Close() (err error) { - err = w.Flush() - if err != nil { - return - } - return w.w.Close() -} - -// Reader implements an io.ReadAt, reading from the input source after verifying the checksum. -type Reader struct { - r io.ReaderAt -} - -// NewReader returns a new Reader which can read from the input source after verifying the checksum. -func NewReader(r io.ReaderAt) *Reader { - checksumReader := &Reader{r: r} - return checksumReader -} - -var errChecksumFail = errors.New("error checksum") - -// ReadAt implements the io.ReadAt interface. -func (r *Reader) ReadAt(p []byte, off int64) (nn int, err error) { - if len(p) == 0 { - return 0, nil - } - offsetInPayload := off % checksumPayloadSize - cursor := off / checksumPayloadSize * checksumBlockSize - - buf := checksumReaderBufPool.Get() - defer checksumReaderBufPool.Put(buf) - - var n int - for len(p) > 0 && err == nil { - n, err = r.r.ReadAt(buf, cursor) - if err != nil { - if n == 0 || err != io.EOF { - return nn, err - } - err = nil - // continue if n > 0 and r.err is io.EOF - } - if n < checksumSize { - return nn, errChecksumFail - } - cursor += int64(n) - originChecksum := binary.LittleEndian.Uint32(buf) - checksum := crc32.Checksum(buf[checksumSize:n], crc32.MakeTable(crc32.IEEE)) - if originChecksum != checksum { - return nn, errChecksumFail - } - n1 := copy(p, buf[checksumSize+offsetInPayload:n]) - nn += n1 - p = p[n1:] - offsetInPayload = 0 - } - return nn, err -} diff --git a/util/checksum/main_test.go b/util/checksum/main_test.go deleted file mode 100644 index 71f0d1d4e1c42..0000000000000 --- a/util/checksum/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package checksum - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/chunk/BUILD.bazel b/util/chunk/BUILD.bazel deleted file mode 100644 index ca59bb6a94700..0000000000000 --- a/util/chunk/BUILD.bazel +++ /dev/null @@ -1,78 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "chunk", - srcs = [ - "alloc.go", - "chunk.go", - "chunk_util.go", - "codec.go", - "column.go", - "compare.go", - "disk.go", - "iterator.go", - "list.go", - "mutrow.go", - "pool.go", - "row.go", - "row_container.go", - "row_container_reader.go", - ], - importpath = "github.com/pingcap/tidb/util/chunk", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//parser/mysql", - "//parser/terror", - "//types", - "//util/checksum", - "//util/disk", - "//util/encrypt", - "//util/hack", - "//util/logutil", - "//util/mathutil", - "//util/memory", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_golang_x_sys//cpu", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "chunk_test", - timeout = "short", - srcs = [ - "alloc_test.go", - "chunk_test.go", - "chunk_util_test.go", - "codec_test.go", - "column_test.go", - "disk_test.go", - "iterator_test.go", - "list_test.go", - "main_test.go", - "mutrow_test.go", - "pool_test.go", - "row_container_test.go", - ], - embed = [":chunk"], - flaky = True, - race = "on", - shard_count = 50, - deps = [ - "//config", - "//parser/mysql", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//types", - "//util/collate", - "//util/mathutil", - "//util/memory", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/chunk/codec.go b/util/chunk/codec.go deleted file mode 100644 index beb4996a681f5..0000000000000 --- a/util/chunk/codec.go +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "encoding/binary" - "reflect" - "unsafe" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/mathutil" -) - -// Codec is used to: -// 1. encode a Chunk to a byte slice. -// 2. decode a Chunk from a byte slice. -type Codec struct { - // colTypes is used to check whether a Column is fixed sized and what the - // fixed size for every element. - // NOTE: It's only used for decoding. - colTypes []*types.FieldType -} - -// NewCodec creates a new Codec object for encode or decode a Chunk. -func NewCodec(colTypes []*types.FieldType) *Codec { - return &Codec{colTypes} -} - -// Encode encodes a Chunk to a byte slice. -func (c *Codec) Encode(chk *Chunk) []byte { - buffer := make([]byte, 0, chk.MemoryUsage()) - for _, col := range chk.columns { - buffer = c.encodeColumn(buffer, col) - } - return buffer -} - -func (*Codec) encodeColumn(buffer []byte, col *Column) []byte { - var lenBuffer [4]byte - // encode length. - binary.LittleEndian.PutUint32(lenBuffer[:], uint32(col.length)) - buffer = append(buffer, lenBuffer[:4]...) - - // encode nullCount. - binary.LittleEndian.PutUint32(lenBuffer[:], uint32(col.nullCount())) - buffer = append(buffer, lenBuffer[:4]...) - - // encode nullBitmap. - if col.nullCount() > 0 { - numNullBitmapBytes := (col.length + 7) / 8 - buffer = append(buffer, col.nullBitmap[:numNullBitmapBytes]...) - } - - // encode offsets. - if !col.isFixed() { - numOffsetBytes := (col.length + 1) * 8 - offsetBytes := i64SliceToBytes(col.offsets) - buffer = append(buffer, offsetBytes[:numOffsetBytes]...) - } - - // encode data. - buffer = append(buffer, col.data...) - return buffer -} - -func i64SliceToBytes(i64s []int64) (b []byte) { - if len(i64s) == 0 { - return nil - } - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - hdr.Len = len(i64s) * 8 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&i64s[0])) - return b -} - -// Decode decodes a Chunk from a byte slice, return the remained unused bytes. -func (c *Codec) Decode(buffer []byte) (*Chunk, []byte) { - chk := &Chunk{} - for ordinal := 0; len(buffer) > 0; ordinal++ { - col := &Column{} - buffer = c.decodeColumn(buffer, col, ordinal) - chk.columns = append(chk.columns, col) - } - return chk, buffer -} - -// DecodeToChunk decodes a Chunk from a byte slice, return the remained unused bytes. -func (c *Codec) DecodeToChunk(buffer []byte, chk *Chunk) (remained []byte) { - for i := 0; i < len(chk.columns); i++ { - buffer = c.decodeColumn(buffer, chk.columns[i], i) - } - return buffer -} - -// decodeColumn decodes a Column from a byte slice, return the remained unused bytes. -func (c *Codec) decodeColumn(buffer []byte, col *Column, ordinal int) (remained []byte) { - // Todo(Shenghui Wu): Optimize all data is null. - // decode length. - col.length = int(binary.LittleEndian.Uint32(buffer)) - buffer = buffer[4:] - - // decode nullCount. - nullCount := int(binary.LittleEndian.Uint32(buffer)) - buffer = buffer[4:] - - // decode nullBitmap. - if nullCount > 0 { - numNullBitmapBytes := (col.length + 7) / 8 - col.nullBitmap = buffer[:numNullBitmapBytes:numNullBitmapBytes] - buffer = buffer[numNullBitmapBytes:] - } else { - c.setAllNotNull(col) - } - - // decode offsets. - numFixedBytes := getFixedLen(c.colTypes[ordinal]) - numDataBytes := int64(numFixedBytes * col.length) - if numFixedBytes == -1 { - numOffsetBytes := (col.length + 1) * 8 - col.offsets = bytesToI64Slice(buffer[:numOffsetBytes:numOffsetBytes]) - buffer = buffer[numOffsetBytes:] - numDataBytes = col.offsets[col.length] - } else if cap(col.elemBuf) < numFixedBytes { - col.elemBuf = make([]byte, numFixedBytes) - } - - // decode data. - col.data = buffer[:numDataBytes:numDataBytes] - // The column reference the data of the grpc response, the memory of the grpc message cannot be GCed if we reuse - // this column. Thus, we set `avoidReusing` to true. - col.avoidReusing = true - return buffer[numDataBytes:] -} - -var allNotNullBitmap [128]byte - -func (*Codec) setAllNotNull(col *Column) { - numNullBitmapBytes := (col.length + 7) / 8 - col.nullBitmap = col.nullBitmap[:0] - for i := 0; i < numNullBitmapBytes; { - numAppendBytes := mathutil.Min(numNullBitmapBytes-i, cap(allNotNullBitmap)) - col.nullBitmap = append(col.nullBitmap, allNotNullBitmap[:numAppendBytes]...) - i += numAppendBytes - } -} - -func bytesToI64Slice(b []byte) (i64s []int64) { - if len(b) == 0 { - return nil - } - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&i64s)) - hdr.Len = len(b) / 8 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&b[0])) - return i64s -} - -// varElemLen indicates this Column is a variable length Column. -const varElemLen = -1 - -func getFixedLen(colType *types.FieldType) int { - switch colType.GetType() { - case mysql.TypeFloat: - return 4 - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, - mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeYear, mysql.TypeDuration: - return 8 - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - return sizeTime - case mysql.TypeNewDecimal: - return types.MyDecimalStructSize - default: - return varElemLen - } -} - -// GetFixedLen get the memory size of a fixed-length type. -// if colType is not fixed-length, it returns varElemLen, aka -1. -func GetFixedLen(colType *types.FieldType) int { - return getFixedLen(colType) -} - -// EstimateTypeWidth estimates the average width of values of the type. -// This is used by the planner, which doesn't require absolutely correct results; -// it's OK (and expected) to guess if we don't know for sure. -// -// mostly study from https://github.com/postgres/postgres/blob/REL_12_STABLE/src/backend/utils/cache/lsyscache.c#L2356 -func EstimateTypeWidth(colType *types.FieldType) int { - colLen := getFixedLen(colType) - // Easy if it's a fixed-width type - if colLen != varElemLen { - return colLen - } - - colLen = colType.GetFlen() - if colLen > 0 { - if colLen <= 32 { - return colLen - } - if colLen < 1000 { - return 32 + (colLen-32)/2 // assume 50% - } - /* - * Beyond 1000, assume we're looking at something like - * "varchar(10000)" where the limit isn't actually reached often, and - * use a fixed estimate. - */ - return 32 + (1000-32)/2 - } - // Oops, we have no idea ... wild guess time. - return 32 -} - -func init() { - for i := 0; i < 128; i++ { - allNotNullBitmap[i] = 0xFF - } -} - -// Decoder decodes the data returned from the coprocessor and stores the result in Chunk. -// How Decoder works: -// 1. Initialization phase: Decode a whole input byte slice to Decoder.intermChk(intermediate chunk) using Codec.Decode. -// intermChk is introduced to simplify the implementation of decode phase. This phase uses pointer operations with -// less CPU and memory cost. -// 2. Decode phase: -// 2.1 Set the number of rows to be decoded to a value that is a multiple of 8 and greater than -// `chk.RequiredRows() - chk.NumRows()`. This reduces the overhead of copying the srcCol.nullBitMap into -// destCol.nullBitMap. -// 2.2 Append srcCol.offsets to destCol.offsets when the elements is of var-length type. And further adjust the -// offsets according to descCol.offsets[destCol.length]-srcCol.offsets[0]. -// 2.3 Append srcCol.nullBitMap to destCol.nullBitMap. -// 3. Go to step 1 when the input byte slice is consumed. -type Decoder struct { - intermChk *Chunk - codec *Codec - remainedRows int -} - -// NewDecoder creates a new Decoder object for decode a Chunk. -func NewDecoder(chk *Chunk, colTypes []*types.FieldType) *Decoder { - return &Decoder{intermChk: chk, codec: NewCodec(colTypes), remainedRows: 0} -} - -// Decode decodes multiple rows of Decoder.intermChk and stores the result in chk. -func (c *Decoder) Decode(chk *Chunk) { - requiredRows := chk.RequiredRows() - chk.NumRows() - // Set the requiredRows to a multiple of 8. - requiredRows = (requiredRows + 7) >> 3 << 3 - if requiredRows > c.remainedRows { - requiredRows = c.remainedRows - } - for i := 0; i < chk.NumCols(); i++ { - c.decodeColumn(chk, i, requiredRows) - } - c.remainedRows -= requiredRows -} - -// Reset decodes data and store the result in Decoder.intermChk. This decode phase uses pointer operations with less -// CPU and memory costs. -func (c *Decoder) Reset(data []byte) { - c.codec.DecodeToChunk(data, c.intermChk) - c.remainedRows = c.intermChk.NumRows() -} - -// IsFinished indicates whether Decoder.intermChk has been dried up. -func (c *Decoder) IsFinished() bool { - return c.remainedRows == 0 -} - -// RemainedRows indicates Decoder.intermChk has remained rows. -func (c *Decoder) RemainedRows() int { - return c.remainedRows -} - -// ReuseIntermChk swaps `Decoder.intermChk` with `chk` directly when `Decoder.intermChk.NumRows()` is no less -// than `chk.requiredRows * factor` where `factor` is 0.8 now. This can avoid the overhead of appending the -// data from `Decoder.intermChk` to `chk`. Moreover, the column.offsets needs to be further adjusted -// according to column.offset[0]. -func (c *Decoder) ReuseIntermChk(chk *Chunk) { - for i, col := range c.intermChk.columns { - col.length = c.remainedRows - elemLen := getFixedLen(c.codec.colTypes[i]) - if elemLen == varElemLen { - // For var-length types, we need to adjust the offsets before reuse. - if deltaOffset := col.offsets[0]; deltaOffset != 0 { - for j := 0; j < len(col.offsets); j++ { - col.offsets[j] -= deltaOffset - } - } - } - } - chk.SwapColumns(c.intermChk) - c.remainedRows = 0 -} - -func (c *Decoder) decodeColumn(chk *Chunk, ordinal int, requiredRows int) { - elemLen := getFixedLen(c.codec.colTypes[ordinal]) - numDataBytes := int64(elemLen * requiredRows) - srcCol := c.intermChk.columns[ordinal] - destCol := chk.columns[ordinal] - - if elemLen == varElemLen { - // For var-length types, we need to adjust the offsets after appending to destCol. - numDataBytes = srcCol.offsets[requiredRows] - srcCol.offsets[0] - deltaOffset := destCol.offsets[destCol.length] - srcCol.offsets[0] - destCol.offsets = append(destCol.offsets, srcCol.offsets[1:requiredRows+1]...) - for i := destCol.length + 1; i <= destCol.length+requiredRows; i++ { - destCol.offsets[i] = destCol.offsets[i] + deltaOffset - } - srcCol.offsets = srcCol.offsets[requiredRows:] - } - - numNullBitmapBytes := (requiredRows + 7) >> 3 - if destCol.length%8 == 0 { - destCol.nullBitmap = append(destCol.nullBitmap, srcCol.nullBitmap[:numNullBitmapBytes]...) - } else { - destCol.appendMultiSameNullBitmap(false, requiredRows) - bitMapLen := len(destCol.nullBitmap) - // bitOffset indicates the number of valid bits in destCol.nullBitmap's last byte. - bitOffset := destCol.length % 8 - startIdx := (destCol.length - 1) >> 3 - for i := 0; i < numNullBitmapBytes; i++ { - destCol.nullBitmap[startIdx+i] |= srcCol.nullBitmap[i] << bitOffset - // The high order 8-bitOffset bits in `srcCol.nullBitmap[i]` should be appended to the low order of the next slot. - if startIdx+i+1 < bitMapLen { - destCol.nullBitmap[startIdx+i+1] |= srcCol.nullBitmap[i] >> (8 - bitOffset) - } - } - } - // Set all the redundant bits in the last slot of destCol.nullBitmap to 0. - numRedundantBits := uint(len(destCol.nullBitmap)*8 - destCol.length - requiredRows) - bitMask := byte(1<<(8-numRedundantBits)) - 1 - destCol.nullBitmap[len(destCol.nullBitmap)-1] &= bitMask - - srcCol.nullBitmap = srcCol.nullBitmap[numNullBitmapBytes:] - destCol.length += requiredRows - - destCol.data = append(destCol.data, srcCol.data[:numDataBytes]...) - srcCol.data = srcCol.data[numDataBytes:] -} diff --git a/util/chunk/codec_test.go b/util/chunk/codec_test.go deleted file mode 100644 index 1e5644b3fda28..0000000000000 --- a/util/chunk/codec_test.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "fmt" - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestCodec(t *testing.T) { - numCols := 6 - numRows := 10 - - colTypes := make([]*types.FieldType, 0, numCols) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeNewDecimal)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeJSON)) - - oldChk := NewChunkWithCapacity(colTypes, numRows) - for i := 0; i < numRows; i++ { - str := fmt.Sprintf("%d.12345", i) - oldChk.AppendNull(0) - oldChk.AppendInt64(1, int64(i)) - oldChk.AppendString(2, str) - oldChk.AppendString(3, str) - oldChk.AppendMyDecimal(4, types.NewDecFromStringForTest(str)) - oldChk.AppendJSON(5, types.CreateBinaryJSON(str)) - } - - codec := NewCodec(colTypes) - buffer := codec.Encode(oldChk) - - newChk := NewChunkWithCapacity(colTypes, numRows) - remained := codec.DecodeToChunk(buffer, newChk) - - require.Empty(t, remained) - require.Equal(t, numCols, newChk.NumCols()) - require.Equal(t, numRows, newChk.NumRows()) - for i := 0; i < numRows; i++ { - row := newChk.GetRow(i) - str := fmt.Sprintf("%d.12345", i) - require.True(t, row.IsNull(0)) - require.False(t, row.IsNull(1)) - require.False(t, row.IsNull(2)) - require.False(t, row.IsNull(3)) - require.False(t, row.IsNull(4)) - require.False(t, row.IsNull(5)) - - require.Equal(t, int64(i), row.GetInt64(1)) - require.Equal(t, str, row.GetString(2)) - require.Equal(t, str, row.GetString(3)) - require.Equal(t, str, row.GetMyDecimal(4).String()) - require.Equal(t, str, string(row.GetJSON(5).GetString())) - } -} - -func TestEstimateTypeWidth(t *testing.T) { - var colType *types.FieldType - - colType = types.NewFieldType(mysql.TypeLonglong) - require.Equal(t, 8, EstimateTypeWidth(colType)) // fixed-witch type - - colType = types.NewFieldType(mysql.TypeString) - colType.SetFlen(31) - require.Equal(t, 31, EstimateTypeWidth(colType)) // colLen <= 32 - - colType = types.NewFieldType(mysql.TypeString) - colType.SetFlen(999) - require.Equal(t, 515, EstimateTypeWidth(colType)) // colLen < 1000 - - colType = types.NewFieldType(mysql.TypeString) - colType.SetFlen(2000) - require.Equal(t, 516, EstimateTypeWidth(colType)) // colLen < 1000 - - colType = types.NewFieldType(mysql.TypeString) - require.Equal(t, 32, EstimateTypeWidth(colType)) // value after guessing -} - -func BenchmarkEncodeChunk(b *testing.B) { - numCols := 4 - numRows := 1024 - - colTypes := make([]*types.FieldType, numCols) - for i := 0; i < numCols; i++ { - colTypes[i] = types.NewFieldType(mysql.TypeLonglong) - } - chk := NewChunkWithCapacity(colTypes, numRows) - - codec := &Codec{} - - b.ResetTimer() - for i := 0; i < b.N; i++ { - codec.Encode(chk) - } -} - -func BenchmarkDecode(b *testing.B) { - numCols := 4 - numRows := 1024 - - colTypes := make([]*types.FieldType, numCols) - for i := 0; i < numCols; i++ { - colTypes[i] = types.NewFieldType(mysql.TypeLonglong) - } - chk := NewChunkWithCapacity(colTypes, numRows) - codec := &Codec{colTypes} - buffer := codec.Encode(chk) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - codec.Decode(buffer) - } -} - -func BenchmarkDecodeToChunk(b *testing.B) { - numCols := 4 - numRows := 1024 - - colTypes := make([]*types.FieldType, numCols) - chk := &Chunk{ - columns: make([]*Column, numCols), - } - for i := 0; i < numCols; i++ { - chk.columns[i] = &Column{ - length: numRows, - nullBitmap: make([]byte, numRows/8+1), - data: make([]byte, numRows*8), - elemBuf: make([]byte, 8), - } - colTypes[i] = types.NewFieldType(mysql.TypeLonglong) - } - codec := &Codec{colTypes} - buffer := codec.Encode(chk) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - codec.DecodeToChunk(buffer, chk) - } -} - -func BenchmarkDecodeToChunkWithVariableType(b *testing.B) { - numCols := 6 - numRows := 1024 - - colTypes := make([]*types.FieldType, 0, numCols) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeLonglong)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeVarchar)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeNewDecimal)) - colTypes = append(colTypes, types.NewFieldType(mysql.TypeJSON)) - - chk := NewChunkWithCapacity(colTypes, numRows) - for i := 0; i < numRows; i++ { - str := fmt.Sprintf("%d.12345", i) - chk.AppendNull(0) - chk.AppendInt64(1, int64(i)) - chk.AppendString(2, str) - chk.AppendString(3, str) - chk.AppendMyDecimal(4, types.NewDecFromStringForTest(str)) - chk.AppendJSON(5, types.CreateBinaryJSON(str)) - } - codec := &Codec{colTypes} - buffer := codec.Encode(chk) - - chk.Reset() - - b.ResetTimer() - for i := 0; i < b.N; i++ { - codec.DecodeToChunk(buffer, chk) - } -} diff --git a/util/chunk/column.go b/util/chunk/column.go deleted file mode 100644 index 61610087c9b48..0000000000000 --- a/util/chunk/column.go +++ /dev/null @@ -1,764 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "fmt" - "math/bits" - "reflect" - "time" - "unsafe" - - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" -) - -// AppendDuration appends a duration value into this Column. -// Fsp is ignored. -func (c *Column) AppendDuration(dur types.Duration) { - c.AppendInt64(int64(dur.Duration)) -} - -// AppendMyDecimal appends a MyDecimal value into this Column. -func (c *Column) AppendMyDecimal(dec *types.MyDecimal) { - *(*types.MyDecimal)(unsafe.Pointer(&c.elemBuf[0])) = *dec - c.finishAppendFixed() -} - -func (c *Column) appendNameValue(name string, val uint64) { - var buf [8]byte - copy(buf[:], (*[8]byte)(unsafe.Pointer(&val))[:]) - c.data = append(c.data, buf[:]...) - c.data = append(c.data, name...) - c.finishAppendVar() -} - -// AppendJSON appends a BinaryJSON value into this Column. -func (c *Column) AppendJSON(j types.BinaryJSON) { - c.data = append(c.data, j.TypeCode) - c.data = append(c.data, j.Value...) - c.finishAppendVar() -} - -// AppendSet appends a Set value into this Column. -func (c *Column) AppendSet(set types.Set) { - c.appendNameValue(set.Name, set.Value) -} - -// Column stores one column of data in Apache Arrow format. -// See https://arrow.apache.org/docs/format/Columnar.html#format-columnar -type Column struct { - length int - nullBitmap []byte // bit 0 is null, 1 is not null - offsets []int64 // used for varLen column. Row i starts from data[offsets[i]] - data []byte - elemBuf []byte - - avoidReusing bool // avoid reusing the Column by allocator -} - -// ColumnAllocator defines an allocator for Column. -type ColumnAllocator interface { - NewColumn(ft *types.FieldType, count int) *Column -} - -// DefaultColumnAllocator is the default implementation of ColumnAllocator. -type DefaultColumnAllocator struct{} - -// NewColumn implements the ColumnAllocator interface. -func (DefaultColumnAllocator) NewColumn(ft *types.FieldType, capacity int) *Column { - return newColumn(getFixedLen(ft), capacity) -} - -// NewColumn creates a new column with the specific type and capacity. -func NewColumn(ft *types.FieldType, capacity int) *Column { - return newColumn(getFixedLen(ft), capacity) -} - -func newColumn(ts, capacity int) *Column { - var col *Column - if ts == varElemLen { - col = newVarLenColumn(capacity) - } else { - col = newFixedLenColumn(ts, capacity) - } - return col -} - -// newFixedLenColumn creates a fixed length Column with elemLen and initial data capacity. -func newFixedLenColumn(elemLen, capacity int) *Column { - return &Column{ - elemBuf: make([]byte, elemLen), - data: make([]byte, 0, capacity*elemLen), - nullBitmap: make([]byte, 0, (capacity+7)>>3), - } -} - -// newVarLenColumn creates a variable length Column with initial data capacity. -func newVarLenColumn(capacity int) *Column { - estimatedElemLen := 8 - // For varLenColumn (e.g. varchar), the accurate length of an element is unknown. - // Therefore, in the first executor.Next we use an experience value -- 8 (so it may make runtime.growslice) - - return &Column{ - offsets: make([]int64, 1, capacity+1), - data: make([]byte, 0, capacity*estimatedElemLen), - nullBitmap: make([]byte, 0, (capacity+7)>>3), - } -} - -func (c *Column) typeSize() int { - if len(c.elemBuf) > 0 { - return len(c.elemBuf) - } - return varElemLen -} - -func (c *Column) isFixed() bool { - return c.elemBuf != nil -} - -// Reset resets this Column according to the EvalType. -// Different from reset, Reset will reset the elemBuf. -func (c *Column) Reset(eType types.EvalType) { - switch eType { - case types.ETInt: - c.ResizeInt64(0, false) - case types.ETReal: - c.ResizeFloat64(0, false) - case types.ETDecimal: - c.ResizeDecimal(0, false) - case types.ETString: - c.ReserveString(0) - case types.ETDatetime, types.ETTimestamp: - c.ResizeTime(0, false) - case types.ETDuration: - c.ResizeGoDuration(0, false) - case types.ETJson: - c.ReserveJSON(0) - default: - panic(fmt.Sprintf("invalid EvalType %v", eType)) - } -} - -// reset resets the underlying data of this Column but doesn't modify its data type. -func (c *Column) reset() { - c.length = 0 - c.nullBitmap = c.nullBitmap[:0] - if len(c.offsets) > 0 { - // The first offset is always 0, it makes slicing the data easier, we need to keep it. - c.offsets = c.offsets[:1] - } - c.data = c.data[:0] -} - -// IsNull returns if this row is null. -func (c *Column) IsNull(rowIdx int) bool { - nullByte := c.nullBitmap[rowIdx/8] - return nullByte&(1<<(uint(rowIdx)&7)) == 0 -} - -// CopyConstruct copies this Column to dst. -// If dst is nil, it creates a new Column and returns it. -func (c *Column) CopyConstruct(dst *Column) *Column { - if dst != nil { - dst.length = c.length - dst.nullBitmap = append(dst.nullBitmap[:0], c.nullBitmap...) - dst.offsets = append(dst.offsets[:0], c.offsets...) - dst.data = append(dst.data[:0], c.data...) - dst.elemBuf = append(dst.elemBuf[:0], c.elemBuf...) - return dst - } - newCol := &Column{length: c.length} - newCol.nullBitmap = append(newCol.nullBitmap, c.nullBitmap...) - newCol.offsets = append(newCol.offsets, c.offsets...) - newCol.data = append(newCol.data, c.data...) - newCol.elemBuf = append(newCol.elemBuf, c.elemBuf...) - return newCol -} - -func (c *Column) appendNullBitmap(notNull bool) { - idx := c.length >> 3 - if idx >= len(c.nullBitmap) { - c.nullBitmap = append(c.nullBitmap, 0) - } - if notNull { - pos := uint(c.length) & 7 - c.nullBitmap[idx] |= byte(1 << pos) - } -} - -// appendMultiSameNullBitmap appends multiple same bit value to `nullBitMap`. -// notNull means not null. -// num means the number of bits that should be appended. -func (c *Column) appendMultiSameNullBitmap(notNull bool, num int) { - numNewBytes := ((c.length + num + 7) >> 3) - len(c.nullBitmap) - b := byte(0) - if notNull { - b = 0xff - } - for i := 0; i < numNewBytes; i++ { - c.nullBitmap = append(c.nullBitmap, b) - } - if !notNull { - return - } - // 1. Set all the remaining bits in the last slot of old c.numBitMap to 1. - numRemainingBits := uint(c.length % 8) - bitMask := byte(^((1 << numRemainingBits) - 1)) - c.nullBitmap[c.length/8] |= bitMask - // 2. Set all the redundant bits in the last slot of new c.numBitMap to 0. - numRedundantBits := uint(len(c.nullBitmap)*8 - c.length - num) - bitMask = byte(1<<(8-numRedundantBits)) - 1 - c.nullBitmap[len(c.nullBitmap)-1] &= bitMask -} - -// AppendNull appends a null value into this Column. -func (c *Column) AppendNull() { - c.appendNullBitmap(false) - if c.isFixed() { - c.data = append(c.data, c.elemBuf...) - } else { - c.offsets = append(c.offsets, c.offsets[c.length]) - } - c.length++ -} - -func (c *Column) finishAppendFixed() { - c.data = append(c.data, c.elemBuf...) - c.appendNullBitmap(true) - c.length++ -} - -// AppendInt64 appends an int64 value into this Column. -func (c *Column) AppendInt64(i int64) { - *(*int64)(unsafe.Pointer(&c.elemBuf[0])) = i - c.finishAppendFixed() -} - -// AppendUint64 appends a uint64 value into this Column. -func (c *Column) AppendUint64(u uint64) { - *(*uint64)(unsafe.Pointer(&c.elemBuf[0])) = u - c.finishAppendFixed() -} - -// AppendFloat32 appends a float32 value into this Column. -func (c *Column) AppendFloat32(f float32) { - *(*float32)(unsafe.Pointer(&c.elemBuf[0])) = f - c.finishAppendFixed() -} - -// AppendFloat64 appends a float64 value into this Column. -func (c *Column) AppendFloat64(f float64) { - *(*float64)(unsafe.Pointer(&c.elemBuf[0])) = f - c.finishAppendFixed() -} - -func (c *Column) finishAppendVar() { - c.appendNullBitmap(true) - c.offsets = append(c.offsets, int64(len(c.data))) - c.length++ -} - -// AppendString appends a string value into this Column. -func (c *Column) AppendString(str string) { - c.data = append(c.data, str...) - c.finishAppendVar() -} - -// AppendBytes appends a byte slice into this Column. -func (c *Column) AppendBytes(b []byte) { - c.data = append(c.data, b...) - c.finishAppendVar() -} - -// AppendTime appends a time value into this Column. -func (c *Column) AppendTime(t types.Time) { - *(*types.Time)(unsafe.Pointer(&c.elemBuf[0])) = t - c.finishAppendFixed() -} - -// AppendEnum appends a Enum value into this Column. -func (c *Column) AppendEnum(enum types.Enum) { - c.appendNameValue(enum.Name, enum.Value) -} - -const ( - sizeInt64 = int(unsafe.Sizeof(int64(0))) - sizeUint64 = int(unsafe.Sizeof(uint64(0))) - sizeFloat32 = int(unsafe.Sizeof(float32(0))) - sizeFloat64 = int(unsafe.Sizeof(float64(0))) - sizeMyDecimal = int(unsafe.Sizeof(types.MyDecimal{})) - sizeGoDuration = int(unsafe.Sizeof(time.Duration(0))) - sizeTime = int(unsafe.Sizeof(types.ZeroTime)) -) - -var ( - emptyBuf = make([]byte, 4*1024) -) - -// resize resizes the column so that it contains n elements, only valid for fixed-length types. -func (c *Column) resize(n, typeSize int, isNull bool) { - sizeData := n * typeSize - if cap(c.data) >= sizeData { - (*reflect.SliceHeader)(unsafe.Pointer(&c.data)).Len = sizeData - } else { - c.data = make([]byte, sizeData) - } - if !isNull { - for j := 0; j < sizeData; j += len(emptyBuf) { - copy(c.data[j:], emptyBuf) - } - } - - newNulls := false - sizeNulls := (n + 7) >> 3 - if cap(c.nullBitmap) >= sizeNulls { - (*reflect.SliceHeader)(unsafe.Pointer(&c.nullBitmap)).Len = sizeNulls - } else { - c.nullBitmap = make([]byte, sizeNulls) - newNulls = true - } - if !isNull || !newNulls { - var nullVal, lastByte byte - if !isNull { - nullVal = 0xFF - } - - // Fill the null bitmap - for i := range c.nullBitmap { - c.nullBitmap[i] = nullVal - } - // Revise the last byte if necessary, when it's not divided by 8. - if x := (n % 8); x != 0 { - if !isNull { - lastByte = byte((1 << x) - 1) - if len(c.nullBitmap) > 0 { - c.nullBitmap[len(c.nullBitmap)-1] = lastByte - } - } - } - } - - if cap(c.elemBuf) >= typeSize { - (*reflect.SliceHeader)(unsafe.Pointer(&c.elemBuf)).Len = typeSize - } else { - c.elemBuf = make([]byte, typeSize) - } - - c.length = n -} - -// reserve makes the column capacity be at least enough to contain n elements. -// this method is only valid for var-length types and estElemSize is the estimated size of this type. -func (c *Column) reserve(n, estElemSize int) { - sizeData := n * estElemSize - if cap(c.data) >= sizeData { - c.data = c.data[:0] - } else { - c.data = make([]byte, 0, sizeData) - } - - sizeNulls := (n + 7) >> 3 - if cap(c.nullBitmap) >= sizeNulls { - c.nullBitmap = c.nullBitmap[:0] - } else { - c.nullBitmap = make([]byte, 0, sizeNulls) - } - - sizeOffs := n + 1 - if cap(c.offsets) >= sizeOffs { - c.offsets = c.offsets[:1] - } else { - c.offsets = make([]int64, 1, sizeOffs) - } - - c.elemBuf = nil - c.length = 0 -} - -// SetNull sets the rowIdx to null. -func (c *Column) SetNull(rowIdx int, isNull bool) { - if isNull { - c.nullBitmap[rowIdx>>3] &= ^(1 << uint(rowIdx&7)) - } else { - c.nullBitmap[rowIdx>>3] |= 1 << uint(rowIdx&7) - } -} - -// SetNulls sets rows in [begin, end) to null. -func (c *Column) SetNulls(begin, end int, isNull bool) { - i := ((begin + 7) >> 3) << 3 - for ; begin < i && begin < end; begin++ { - c.SetNull(begin, isNull) - } - var v uint8 - if !isNull { - v = (1 << 8) - 1 - } - for ; begin+8 <= end; begin += 8 { - c.nullBitmap[begin>>3] = v - } - for ; begin < end; begin++ { - c.SetNull(begin, isNull) - } -} - -// nullCount returns the number of nulls in this Column. -func (c *Column) nullCount() int { - var cnt, i int - for ; i+8 <= c.length; i += 8 { - // 0 is null and 1 is not null - cnt += 8 - bits.OnesCount8(c.nullBitmap[i>>3]) - } - for ; i < c.length; i++ { - if c.IsNull(i) { - cnt++ - } - } - return cnt -} - -// ResizeInt64 resizes the column so that it contains n int64 elements. -func (c *Column) ResizeInt64(n int, isNull bool) { - c.resize(n, sizeInt64, isNull) -} - -// ResizeUint64 resizes the column so that it contains n uint64 elements. -func (c *Column) ResizeUint64(n int, isNull bool) { - c.resize(n, sizeUint64, isNull) -} - -// ResizeFloat32 resizes the column so that it contains n float32 elements. -func (c *Column) ResizeFloat32(n int, isNull bool) { - c.resize(n, sizeFloat32, isNull) -} - -// ResizeFloat64 resizes the column so that it contains n float64 elements. -func (c *Column) ResizeFloat64(n int, isNull bool) { - c.resize(n, sizeFloat64, isNull) -} - -// ResizeDecimal resizes the column so that it contains n decimal elements. -func (c *Column) ResizeDecimal(n int, isNull bool) { - c.resize(n, sizeMyDecimal, isNull) -} - -// ResizeGoDuration resizes the column so that it contains n duration elements. -func (c *Column) ResizeGoDuration(n int, isNull bool) { - c.resize(n, sizeGoDuration, isNull) -} - -// ResizeTime resizes the column so that it contains n Time elements. -func (c *Column) ResizeTime(n int, isNull bool) { - c.resize(n, sizeTime, isNull) -} - -// ReserveString changes the column capacity to store n string elements and set the length to zero. -func (c *Column) ReserveString(n int) { - c.reserve(n, 8) -} - -// ReserveBytes changes the column capacity to store n bytes elements and set the length to zero. -func (c *Column) ReserveBytes(n int) { - c.reserve(n, 8) -} - -// ReserveJSON changes the column capacity to store n JSON elements and set the length to zero. -func (c *Column) ReserveJSON(n int) { - c.reserve(n, 8) -} - -// ReserveSet changes the column capacity to store n set elements and set the length to zero. -func (c *Column) ReserveSet(n int) { - c.reserve(n, 8) -} - -// ReserveEnum changes the column capacity to store n enum elements and set the length to zero. -func (c *Column) ReserveEnum(n int) { - c.reserve(n, 8) -} - -func (c *Column) castSliceHeader(header *reflect.SliceHeader, typeSize int) { - header.Data = (*reflect.SliceHeader)(unsafe.Pointer(&c.data)).Data - header.Len = c.length - header.Cap = cap(c.data) / typeSize -} - -// Int64s returns an int64 slice stored in this Column. -func (c *Column) Int64s() []int64 { - var res []int64 - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeInt64) - return res -} - -// Uint64s returns a uint64 slice stored in this Column. -func (c *Column) Uint64s() []uint64 { - var res []uint64 - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeUint64) - return res -} - -// Float32s returns a float32 slice stored in this Column. -func (c *Column) Float32s() []float32 { - var res []float32 - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeFloat32) - return res -} - -// Float64s returns a float64 slice stored in this Column. -func (c *Column) Float64s() []float64 { - var res []float64 - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeFloat64) - return res -} - -// GoDurations returns a Golang time.Duration slice stored in this Column. -// Different from the Row.GetDuration method, the argument Fsp is ignored, so the user should handle it outside. -func (c *Column) GoDurations() []time.Duration { - var res []time.Duration - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeGoDuration) - return res -} - -// Decimals returns a MyDecimal slice stored in this Column. -func (c *Column) Decimals() []types.MyDecimal { - var res []types.MyDecimal - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeMyDecimal) - return res -} - -// Times returns a Time slice stored in this Column. -func (c *Column) Times() []types.Time { - var res []types.Time - c.castSliceHeader((*reflect.SliceHeader)(unsafe.Pointer(&res)), sizeTime) - return res -} - -// GetInt64 returns the int64 in the specific row. -func (c *Column) GetInt64(rowID int) int64 { - return *(*int64)(unsafe.Pointer(&c.data[rowID*8])) -} - -// GetUint64 returns the uint64 in the specific row. -func (c *Column) GetUint64(rowID int) uint64 { - return *(*uint64)(unsafe.Pointer(&c.data[rowID*8])) -} - -// GetFloat32 returns the float32 in the specific row. -func (c *Column) GetFloat32(rowID int) float32 { - return *(*float32)(unsafe.Pointer(&c.data[rowID*4])) -} - -// GetFloat64 returns the float64 in the specific row. -func (c *Column) GetFloat64(rowID int) float64 { - return *(*float64)(unsafe.Pointer(&c.data[rowID*8])) -} - -// GetDecimal returns the decimal in the specific row. -func (c *Column) GetDecimal(rowID int) *types.MyDecimal { - return (*types.MyDecimal)(unsafe.Pointer(&c.data[rowID*types.MyDecimalStructSize])) -} - -// GetString returns the string in the specific row. -func (c *Column) GetString(rowID int) string { - return string(hack.String(c.data[c.offsets[rowID]:c.offsets[rowID+1]])) -} - -// GetJSON returns the JSON in the specific row. -func (c *Column) GetJSON(rowID int) types.BinaryJSON { - start := c.offsets[rowID] - return types.BinaryJSON{TypeCode: c.data[start], Value: c.data[start+1 : c.offsets[rowID+1]]} -} - -// GetBytes returns the byte slice in the specific row. -func (c *Column) GetBytes(rowID int) []byte { - return c.data[c.offsets[rowID]:c.offsets[rowID+1]] -} - -// GetEnum returns the Enum in the specific row. -func (c *Column) GetEnum(rowID int) types.Enum { - name, val := c.getNameValue(rowID) - return types.Enum{Name: name, Value: val} -} - -// GetSet returns the Set in the specific row. -func (c *Column) GetSet(rowID int) types.Set { - name, val := c.getNameValue(rowID) - return types.Set{Name: name, Value: val} -} - -// GetTime returns the Time in the specific row. -func (c *Column) GetTime(rowID int) types.Time { - return *(*types.Time)(unsafe.Pointer(&c.data[rowID*sizeTime])) -} - -// GetDuration returns the Duration in the specific row. -func (c *Column) GetDuration(rowID int, fillFsp int) types.Duration { - dur := *(*int64)(unsafe.Pointer(&c.data[rowID*8])) - return types.Duration{Duration: time.Duration(dur), Fsp: fillFsp} -} - -func (c *Column) getNameValue(rowID int) (string, uint64) { - start, end := c.offsets[rowID], c.offsets[rowID+1] - if start == end { - return "", 0 - } - var val uint64 - copy((*[8]byte)(unsafe.Pointer(&val))[:], c.data[start:]) - return string(hack.String(c.data[start+8 : end])), val -} - -// GetRaw returns the underlying raw bytes in the specific row. -func (c *Column) GetRaw(rowID int) []byte { - var data []byte - if c.isFixed() { - elemLen := len(c.elemBuf) - data = c.data[rowID*elemLen : rowID*elemLen+elemLen] - } else { - data = c.data[c.offsets[rowID]:c.offsets[rowID+1]] - } - return data -} - -// SetRaw sets the raw bytes for the rowIdx-th element. -// NOTE: Two conditions must be satisfied before calling this function: -// 1. The column should be stored with variable-length elements. -// 2. The length of the new element should be exactly the same as the old one. -func (c *Column) SetRaw(rowID int, bs []byte) { - copy(c.data[c.offsets[rowID]:c.offsets[rowID+1]], bs) -} - -// reconstruct reconstructs this Column by removing all filtered rows in it according to sel. -func (c *Column) reconstruct(sel []int) { - if sel == nil { - return - } - if c.isFixed() { - elemLen := len(c.elemBuf) - for dst, src := range sel { - idx := dst >> 3 - pos := uint16(dst & 7) - if c.IsNull(src) { - c.nullBitmap[idx] &= ^byte(1 << pos) - } else { - copy(c.data[dst*elemLen:dst*elemLen+elemLen], c.data[src*elemLen:src*elemLen+elemLen]) - c.nullBitmap[idx] |= byte(1 << pos) - } - } - c.data = c.data[:len(sel)*elemLen] - } else { - tail := 0 - for dst, src := range sel { - idx := dst >> 3 - pos := uint(dst & 7) - if c.IsNull(src) { - c.nullBitmap[idx] &= ^byte(1 << pos) - c.offsets[dst+1] = int64(tail) - } else { - start, end := c.offsets[src], c.offsets[src+1] - copy(c.data[tail:], c.data[start:end]) - tail += int(end - start) - c.offsets[dst+1] = int64(tail) - c.nullBitmap[idx] |= byte(1 << pos) - } - } - c.data = c.data[:tail] - c.offsets = c.offsets[:len(sel)+1] - } - c.length = len(sel) - - // clean nullBitmap - c.nullBitmap = c.nullBitmap[:(len(sel)+7)>>3] - idx := len(sel) >> 3 - if idx < len(c.nullBitmap) { - pos := uint16(len(sel) & 7) - c.nullBitmap[idx] &= byte((1 << pos) - 1) - } -} - -// CopyReconstruct copies this Column to dst and removes unselected rows. -// If dst is nil, it creates a new Column and returns it. -func (c *Column) CopyReconstruct(sel []int, dst *Column) *Column { - if sel == nil { - return c.CopyConstruct(dst) - } - - selLength := len(sel) - if selLength == c.length { - // The variable 'ascend' is used to check if the sel array is in ascending order - ascend := true - for i := 1; i < selLength; i++ { - if sel[i] < sel[i-1] { - ascend = false - break - } - } - if ascend { - return c.CopyConstruct(dst) - } - } - - if dst == nil { - dst = newColumn(c.typeSize(), len(sel)) - } else { - dst.reset() - } - - if c.isFixed() { - elemLen := len(c.elemBuf) - dst.elemBuf = make([]byte, elemLen) - for _, i := range sel { - dst.appendNullBitmap(!c.IsNull(i)) - dst.data = append(dst.data, c.data[i*elemLen:i*elemLen+elemLen]...) - dst.length++ - } - } else { - dst.elemBuf = nil - if len(dst.offsets) == 0 { - dst.offsets = append(dst.offsets, 0) - } - for _, i := range sel { - dst.appendNullBitmap(!c.IsNull(i)) - start, end := c.offsets[i], c.offsets[i+1] - dst.data = append(dst.data, c.data[start:end]...) - dst.offsets = append(dst.offsets, int64(len(dst.data))) - dst.length++ - } - } - return dst -} - -// MergeNulls merges these columns' null bitmaps. -// For a row, if any column of it is null, the result is null. -// It works like: if col1.IsNull || col2.IsNull || col3.IsNull. -// The caller should ensure that all these columns have the same -// length, and data stored in the result column is fixed-length type. -func (c *Column) MergeNulls(cols ...*Column) { - if !c.isFixed() { - panic("result column should be fixed-length type") - } - for _, col := range cols { - if c.length != col.length { - panic(fmt.Sprintf("should ensure all columns have the same length, expect %v, but got %v", c.length, col.length)) - } - } - for _, col := range cols { - for i := range c.nullBitmap { - // bit 0 is null, 1 is not null, so do AND operations here. - c.nullBitmap[i] &= col.nullBitmap[i] - } - } -} diff --git a/util/chunk/column_test.go b/util/chunk/column_test.go deleted file mode 100644 index 20f83791d185d..0000000000000 --- a/util/chunk/column_test.go +++ /dev/null @@ -1,995 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "fmt" - "math/rand" - "testing" - "time" - "unsafe" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestColumnCopy(t *testing.T) { - col := newFixedLenColumn(8, 10) - for i := 0; i < 10; i++ { - col.AppendInt64(int64(i)) - } - - c1 := col.CopyConstruct(nil) - require.Equal(t, col, c1) - - c2 := newFixedLenColumn(8, 10) - c2 = col.CopyConstruct(c2) - require.Equal(t, col, c2) -} - -func TestColumnCopyReconstructFixedLen(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) - results := make([]int64, 0, 1024) - nulls := make([]bool, 0, 1024) - sel := make([]int, 0, 1024) - for i := 0; i < 1024; i++ { - if rand.Intn(10) < 6 { - sel = append(sel, i) - } - - if rand.Intn(10) < 2 { - col.AppendNull() - nulls = append(nulls, true) - results = append(results, 0) - continue - } - - v := rand.Int63() - col.AppendInt64(v) - results = append(results, v) - nulls = append(nulls, false) - } - - col = col.CopyReconstruct(sel, nil) - nullCnt := 0 - for n, i := range sel { - if nulls[i] { - nullCnt++ - require.True(t, col.IsNull(n)) - } else { - require.Equal(t, results[i], col.GetInt64(n)) - } - } - require.Equal(t, col.nullCount(), nullCnt) - require.Len(t, sel, col.length) - - for i := 0; i < 128; i++ { - if i%2 == 0 { - col.AppendNull() - } else { - col.AppendInt64(int64(i * i * i)) - } - } - - require.Len(t, sel, col.length-128) - require.Equal(t, nullCnt+128/2, col.nullCount()) - for i := 0; i < 128; i++ { - if i%2 == 0 { - require.True(t, col.IsNull(len(sel)+i)) - } else { - require.Equal(t, int64(i*i*i), col.GetInt64(len(sel)+i)) - require.False(t, col.IsNull(len(sel)+i)) - } - } -} - -func TestColumnCopyReconstructVarLen(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeVarString), 1024) - results := make([]string, 0, 1024) - nulls := make([]bool, 0, 1024) - sel := make([]int, 0, 1024) - for i := 0; i < 1024; i++ { - if rand.Intn(10) < 6 { - sel = append(sel, i) - } - - if rand.Intn(10) < 2 { - col.AppendNull() - nulls = append(nulls, true) - results = append(results, "") - continue - } - - v := fmt.Sprintf("%v", rand.Int63()) - col.AppendString(v) - results = append(results, v) - nulls = append(nulls, false) - } - - col = col.CopyReconstruct(sel, nil) - nullCnt := 0 - for n, i := range sel { - if nulls[i] { - nullCnt++ - require.True(t, col.IsNull(n)) - } else { - require.Equal(t, results[i], col.GetString(n)) - } - } - require.Equal(t, col.nullCount(), nullCnt) - require.Len(t, sel, col.length) - - for i := 0; i < 128; i++ { - if i%2 == 0 { - col.AppendNull() - } else { - col.AppendString(fmt.Sprintf("%v", i*i*i)) - } - } - - require.Len(t, sel, col.length-128) - require.Equal(t, nullCnt+128/2, col.nullCount()) - for i := 0; i < 128; i++ { - if i%2 == 0 { - require.True(t, col.IsNull(len(sel)+i)) - } else { - require.Equal(t, fmt.Sprintf("%v", i*i*i), col.GetString(len(sel)+i)) - require.False(t, col.IsNull(len(sel)+i)) - } - } -} - -func TestLargeStringColumnOffset(t *testing.T) { - numRows := 1 - col := newVarLenColumn(numRows) - // The max-length of a string field can be 6M, a typical batch size for Chunk is 1024, which is 1K. - // That is to say, the memory offset of a string column can be 6GB, which exceeds int32 - col.offsets[0] = 6 << 30 - require.Equal(t, int64(6<<30), col.offsets[0]) // test no overflow. -} - -func TestI64Column(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendInt64(int64(i)) - } - - i64s := col.Int64s() - for i := 0; i < 1024; i++ { - require.Equal(t, int64(i), i64s[i]) - i64s[i]++ - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, int64(i+1), row.GetInt64(0)) - require.Equal(t, int64(i+1), col.GetInt64(i)) - i++ - } -} - -func TestF64Column(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDouble)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendFloat64(float64(i)) - } - - f64s := col.Float64s() - for i := 0; i < 1024; i++ { - require.Equal(t, float64(i), f64s[i]) - f64s[i] /= 2 - } - - it := NewIterator4Chunk(chk) - var i int64 - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, float64(i)/2, row.GetFloat64(0)) - require.Equal(t, float64(i)/2, col.GetFloat64(int(i))) - i++ - } -} - -func TestF32Column(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeFloat)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendFloat32(float32(i)) - } - - f32s := col.Float32s() - for i := 0; i < 1024; i++ { - require.Equal(t, float32(i), f32s[i]) - f32s[i] /= 2 - } - - it := NewIterator4Chunk(chk) - var i int64 - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, float32(i)/2, row.GetFloat32(0)) - require.Equal(t, float32(i)/2, col.GetFloat32(int(i))) - i++ - } -} - -func TestDurationSliceColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendDuration(types.Duration{Duration: time.Duration(i)}) - } - - ds := col.GoDurations() - for i := 0; i < 1024; i++ { - require.Equal(t, time.Duration(i), ds[i]) - d := types.Duration{Duration: ds[i]} - d, _ = d.Add(d) - ds[i] = d.Duration - } - - it := NewIterator4Chunk(chk) - var i int64 - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, time.Duration(i)*2, row.GetDuration(0, 0).Duration) - require.Equal(t, time.Duration(i)*2, col.GetDuration(int(i), 0).Duration) - i++ - } -} - -func TestMyDecimal(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeNewDecimal)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - d := new(types.MyDecimal) - err := d.FromFloat64(float64(i) * 1.1) - require.NoError(t, err) - col.AppendMyDecimal(d) - } - - ds := col.Decimals() - for i := 0; i < 1024; i++ { - d := new(types.MyDecimal) - err := d.FromFloat64(float64(i) * 1.1) - require.NoError(t, err) - require.Zero(t, d.Compare(&ds[i])) - - types.DecimalAdd(&ds[i], d, &ds[i]) - require.NoError(t, err) - } - - it := NewIterator4Chunk(chk) - var i int64 - for row := it.Begin(); row != it.End(); row = it.Next() { - d := new(types.MyDecimal) - err := d.FromFloat64(float64(i) * 1.1 * 2) - require.NoError(t, err) - - delta := new(types.MyDecimal) - err = types.DecimalSub(d, row.GetMyDecimal(0), delta) - require.NoError(t, err) - - fDelta, err := delta.ToFloat64() - require.NoError(t, err) - require.InDelta(t, 0, fDelta, 0.0001) - - i++ - } -} - -func TestStringColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeVarString)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendString(fmt.Sprintf("%v", i*i)) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, fmt.Sprintf("%v", i*i), row.GetString(0)) - require.Equal(t, fmt.Sprintf("%v", i*i), col.GetString(i)) - i++ - } -} - -func TestSetColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeSet)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendSet(types.Set{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - s1 := col.GetSet(i) - s2 := row.GetSet(0) - require.Equal(t, s2.Name, s1.Name) - require.Equal(t, s2.Value, s1.Value) - require.Equal(t, fmt.Sprintf("%v", i), s1.Name) - require.Equal(t, uint64(i), s1.Value) - i++ - } -} - -func TestJSONColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeJSON)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - j := new(types.BinaryJSON) - err := j.UnmarshalJSON([]byte(fmt.Sprintf(`{"%v":%v}`, i, i))) - require.NoError(t, err) - col.AppendJSON(*j) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - j1 := col.GetJSON(i) - j2 := row.GetJSON(0) - require.Equal(t, j2.String(), j1.String()) - i++ - } -} - -func TestTimeColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDatetime)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendTime(types.CurrentTime(mysql.TypeDatetime)) - time.Sleep(time.Millisecond / 10) - } - - it := NewIterator4Chunk(chk) - ts := col.Times() - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - j1 := col.GetTime(i) - j2 := row.GetTime(0) - j3 := ts[i] - require.Zero(t, j1.Compare(j2)) - require.Zero(t, j1.Compare(j3)) - i++ - } -} - -func TestDurationColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - j1 := col.GetDuration(i, 0) - j2 := row.GetDuration(0, 0) - require.Zero(t, j1.Compare(j2)) - i++ - } -} - -func TestEnumColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeEnum)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendEnum(types.Enum{Name: fmt.Sprintf("%v", i), Value: uint64(i)}) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - s1 := col.GetEnum(i) - s2 := row.GetEnum(0) - require.Equal(t, s2.Name, s1.Name) - require.Equal(t, s2.Value, s1.Value) - require.Equal(t, fmt.Sprintf("%v", i), s1.Name) - require.Equal(t, uint64(i), s1.Value) - i++ - } -} - -func TestNullsColumn(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - if i%2 == 0 { - col.AppendNull() - continue - } - col.AppendInt64(int64(i)) - } - - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - if i%2 == 0 { - require.True(t, row.IsNull(0)) - require.True(t, col.IsNull(i)) - } else { - require.Equal(t, int64(i), row.GetInt64(0)) - } - i++ - } -} - -func TestReconstructFixedLen(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) - results := make([]int64, 0, 1024) - nulls := make([]bool, 0, 1024) - sel := make([]int, 0, 1024) - for i := 0; i < 1024; i++ { - if rand.Intn(10) < 6 { - sel = append(sel, i) - } - - if rand.Intn(10) < 2 { - col.AppendNull() - nulls = append(nulls, true) - results = append(results, 0) - continue - } - - v := rand.Int63() - col.AppendInt64(v) - results = append(results, v) - nulls = append(nulls, false) - } - - col.reconstruct(sel) - nullCnt := 0 - for n, i := range sel { - if nulls[i] { - nullCnt++ - require.True(t, col.IsNull(n)) - } else { - require.Equal(t, results[i], col.GetInt64(n)) - } - } - require.Equal(t, col.nullCount(), nullCnt) - require.Len(t, sel, col.length) - - for i := 0; i < 128; i++ { - if i%2 == 0 { - col.AppendNull() - } else { - col.AppendInt64(int64(i * i * i)) - } - } - - require.Len(t, sel, col.length-128) - require.Equal(t, nullCnt+128/2, col.nullCount()) - for i := 0; i < 128; i++ { - if i%2 == 0 { - require.True(t, col.IsNull(len(sel)+i)) - } else { - require.Equal(t, int64(i*i*i), col.GetInt64(len(sel)+i)) - require.False(t, col.IsNull(len(sel)+i)) - } - } -} - -func TestReconstructVarLen(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeVarString), 1024) - results := make([]string, 0, 1024) - nulls := make([]bool, 0, 1024) - sel := make([]int, 0, 1024) - for i := 0; i < 1024; i++ { - if rand.Intn(10) < 6 { - sel = append(sel, i) - } - - if rand.Intn(10) < 2 { - col.AppendNull() - nulls = append(nulls, true) - results = append(results, "") - continue - } - - v := fmt.Sprintf("%v", rand.Int63()) - col.AppendString(v) - results = append(results, v) - nulls = append(nulls, false) - } - - col.reconstruct(sel) - nullCnt := 0 - for n, i := range sel { - if nulls[i] { - nullCnt++ - require.True(t, col.IsNull(n)) - } else { - require.Equal(t, results[i], col.GetString(n)) - } - } - require.Equal(t, col.nullCount(), nullCnt) - require.Len(t, sel, col.length) - - for i := 0; i < 128; i++ { - if i%2 == 0 { - col.AppendNull() - } else { - col.AppendString(fmt.Sprintf("%v", i*i*i)) - } - } - - require.Len(t, sel, col.length-128) - require.Equal(t, nullCnt+128/2, col.nullCount()) - for i := 0; i < 128; i++ { - if i%2 == 0 { - require.True(t, col.IsNull(len(sel)+i)) - } else { - require.Equal(t, fmt.Sprintf("%v", i*i*i), col.GetString(len(sel)+i)) - require.False(t, col.IsNull(len(sel)+i)) - } - } -} - -func TestPreAllocInt64(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 128) - col.ResizeInt64(256, true) - i64s := col.Int64s() - require.Equal(t, 256, len(i64s)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendInt64(2333) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Int64s())) - require.Equal(t, int64(2333), col.Int64s()[256]) -} - -func TestPreAllocUint64(t *testing.T) { - tll := types.NewFieldType(mysql.TypeLonglong) - tll.AddFlag(mysql.UnsignedFlag) - col := NewColumn(tll, 128) - col.ResizeUint64(256, true) - u64s := col.Uint64s() - require.Equal(t, 256, len(u64s)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendUint64(2333) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Uint64s())) - require.Equal(t, uint64(2333), col.Uint64s()[256]) -} - -func TestPreAllocFloat32(t *testing.T) { - col := newFixedLenColumn(sizeFloat32, 128) - col.ResizeFloat32(256, true) - f32s := col.Float32s() - require.Equal(t, 256, len(f32s)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendFloat32(2333) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Float32s())) - require.Equal(t, float32(2333), col.Float32s()[256]) -} - -func TestPreAllocFloat64(t *testing.T) { - col := newFixedLenColumn(sizeFloat64, 128) - col.ResizeFloat64(256, true) - f64s := col.Float64s() - require.Equal(t, 256, len(f64s)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendFloat64(2333) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Float64s())) - require.Equal(t, float64(2333), col.Float64s()[256]) -} - -func TestPreAllocDecimal(t *testing.T) { - col := newFixedLenColumn(sizeMyDecimal, 128) - col.ResizeDecimal(256, true) - ds := col.Decimals() - require.Equal(t, 256, len(ds)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendMyDecimal(new(types.MyDecimal)) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Float64s())) -} - -func TestPreAllocTime(t *testing.T) { - col := newFixedLenColumn(sizeTime, 128) - col.ResizeTime(256, true) - ds := col.Times() - require.Equal(t, 256, len(ds)) - for i := 0; i < 256; i++ { - require.True(t, col.IsNull(i)) - } - col.AppendTime(types.ZeroDatetime) - require.False(t, col.IsNull(256)) - require.Equal(t, 257, len(col.Times())) -} - -func TestNull(t *testing.T) { - col := newFixedLenColumn(sizeFloat64, 32) - col.ResizeFloat64(1024, true) - require.Equal(t, 1024, col.nullCount()) - - notNulls := make(map[int]struct{}) - for i := 0; i < 512; i++ { - idx := rand.Intn(1024) - notNulls[idx] = struct{}{} - col.SetNull(idx, false) - } - - require.Equal(t, 1024-len(notNulls), col.nullCount()) - for idx := range notNulls { - require.False(t, col.IsNull(idx)) - } - - col.ResizeFloat64(8, true) - col.SetNulls(0, 8, true) - col.SetNull(7, false) - require.Equal(t, 7, col.nullCount()) - - col.ResizeFloat64(8, true) - col.SetNulls(0, 8, true) - require.Equal(t, 8, col.nullCount()) - - col.ResizeFloat64(9, true) - col.SetNulls(0, 9, true) - col.SetNull(8, false) - require.Equal(t, 8, col.nullCount()) -} - -func TestSetNulls(t *testing.T) { - col := newFixedLenColumn(sizeFloat64, 32) - col.ResizeFloat64(1024, true) - require.Equal(t, 1024, col.nullCount()) - - col.SetNulls(0, 1024, false) - require.Zero(t, col.nullCount()) - - nullMap := make(map[int]struct{}) - for i := 0; i < 100; i++ { - begin := rand.Intn(1024) - l := rand.Intn(37) - end := begin + l - if end > 1024 { - end = 1024 - } - for i := begin; i < end; i++ { - nullMap[i] = struct{}{} - } - col.SetNulls(begin, end, true) - - require.Len(t, nullMap, col.nullCount()) - for k := range nullMap { - require.True(t, col.IsNull(k)) - } - } -} - -func TestResizeReserve(t *testing.T) { - cI64s := newFixedLenColumn(sizeInt64, 0) - require.Zero(t, cI64s.length) - for i := 0; i < 100; i++ { - n := rand.Intn(1024) - cI64s.ResizeInt64(n, true) - require.Equal(t, n, cI64s.length) - require.Equal(t, n, len(cI64s.Int64s())) - } - cI64s.ResizeInt64(0, true) - require.Zero(t, cI64s.length) - require.Zero(t, len(cI64s.Int64s())) - - cStrs := newVarLenColumn(0) - for i := 0; i < 100; i++ { - n := rand.Intn(1024) - cStrs.ReserveString(n) - require.Zero(t, cStrs.length) - } - cStrs.ReserveString(0) - require.Zero(t, cStrs.length) -} - -func TestGetRaw(t *testing.T) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeFloat)}, 1024) - col := chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendFloat32(float32(i)) - } - it := NewIterator4Chunk(chk) - var i int - for row := it.Begin(); row != it.End(); row = it.Next() { - f := float32(i) - b := (*[unsafe.Sizeof(f)]byte)(unsafe.Pointer(&f))[:] - require.Equal(t, b, row.GetRaw(0)) - require.Equal(t, b, col.GetRaw(i)) - i++ - } - - chk = NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeVarString)}, 1024) - col = chk.Column(0) - for i := 0; i < 1024; i++ { - col.AppendString(fmt.Sprint(i)) - } - it = NewIterator4Chunk(chk) - i = 0 - for row := it.Begin(); row != it.End(); row = it.Next() { - require.Equal(t, []byte(fmt.Sprint(i)), row.GetRaw(0)) - require.Equal(t, []byte(fmt.Sprint(i)), col.GetRaw(i)) - i++ - } -} - -func TestResize(t *testing.T) { - col := NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) - for i := 0; i < 1024; i++ { - col.AppendInt64(int64(i)) - } - col.ResizeInt64(1024, false) - for i := 0; i < 1024; i++ { - require.Equal(t, int64(0), col.Int64s()[i]) - } - - col = NewColumn(types.NewFieldType(mysql.TypeFloat), 1024) - for i := 0; i < 1024; i++ { - col.AppendFloat32(float32(i)) - } - col.ResizeFloat32(1024, false) - for i := 0; i < 1024; i++ { - require.Equal(t, float32(0), col.Float32s()[i]) - } - - col = NewColumn(types.NewFieldType(mysql.TypeDouble), 1024) - for i := 0; i < 1024; i++ { - col.AppendFloat64(float64(i)) - } - col.ResizeFloat64(1024, false) - for i := 0; i < 1024; i++ { - require.Equal(t, float64(0), col.Float64s()[i]) - } - - col = NewColumn(types.NewFieldType(mysql.TypeNewDecimal), 1024) - for i := 0; i < 1024; i++ { - col.AppendMyDecimal(new(types.MyDecimal).FromInt(int64(i))) - } - col.ResizeDecimal(1024, false) - for i := 0; i < 1024; i++ { - var d types.MyDecimal - require.Equal(t, d, col.Decimals()[i]) - } - - col = NewColumn(types.NewFieldType(mysql.TypeDuration), 1024) - for i := 0; i < 1024; i++ { - col.AppendDuration(types.Duration{Duration: time.Duration(i), Fsp: i}) - } - col.ResizeGoDuration(1024, false) - for i := 0; i < 1024; i++ { - require.Equal(t, time.Duration(0), col.GoDurations()[i]) - } - - col = NewColumn(types.NewFieldType(mysql.TypeDatetime), 1024) - for i := 0; i < 1024; i++ { - gt := types.FromDate(rand.Intn(2200), rand.Intn(10)+1, rand.Intn(20)+1, rand.Intn(12), rand.Intn(60), rand.Intn(60), rand.Intn(1000000)) - t := types.NewTime(gt, 0, 0) - col.AppendTime(t) - } - col.ResizeTime(1024, false) - for i := 0; i < 1024; i++ { - var time types.Time - require.Equal(t, time, col.Times()[i]) - } -} - -func BenchmarkDurationRow(b *testing.B) { - chk1 := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) - col1 := chk1.Column(0) - for i := 0; i < 1024; i++ { - col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) - } - chk2 := chk1.CopyConstruct() - result := chk1.CopyConstruct() - - b.ResetTimer() - for k := 0; k < b.N; k++ { - result.Reset() - it1 := NewIterator4Chunk(chk1) - it2 := NewIterator4Chunk(chk2) - for r1, r2 := it1.Begin(), it2.Begin(); r1 != it1.End() && r2 != it2.End(); r1, r2 = it1.Next(), it2.Next() { - d1 := r1.GetDuration(0, 0) - d2 := r2.GetDuration(0, 0) - r, err := d1.Add(d2) - if err != nil { - b.Fatal(err) - } - result.AppendDuration(0, r) - } - } -} - -func BenchmarkDurationVec(b *testing.B) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDuration)}, 1024) - col1 := chk.Column(0) - for i := 0; i < 1024; i++ { - col1.AppendDuration(types.Duration{Duration: time.Second * time.Duration(i)}) - } - col2 := col1.CopyConstruct(nil) - result := col1.CopyConstruct(nil) - - ds1 := col1.GoDurations() - ds2 := col2.GoDurations() - rs := result.GoDurations() - - b.ResetTimer() - for k := 0; k < b.N; k++ { - result.ResizeGoDuration(1024, true) - for i := 0; i < 1024; i++ { - d1 := types.Duration{Duration: ds1[i]} - d2 := types.Duration{Duration: ds2[i]} - r, err := d1.Add(d2) - if err != nil { - b.Fatal(err) - } - rs[i] = r.Duration - } - } -} - -func BenchmarkTimeRow(b *testing.B) { - chk1 := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDate)}, 1024) - col1 := chk1.Column(0) - for i := 0; i < 1024; i++ { - col1.AppendTime(types.ZeroDate) - } - chk2 := chk1.CopyConstruct() - result := chk1.CopyConstruct() - - b.ResetTimer() - for k := 0; k < b.N; k++ { - result.Reset() - it1 := NewIterator4Chunk(chk1) - it2 := NewIterator4Chunk(chk2) - for r1, r2 := it1.Begin(), it2.Begin(); r1 != it1.End() && r2 != it2.End(); r1, r2 = it1.Next(), it2.Next() { - d1 := r1.GetTime(0) - d2 := r2.GetTime(0) - if r := d1.Compare(d2); r > 0 { - result.AppendTime(0, d1) - } else { - result.AppendTime(0, d2) - } - } - } -} - -func BenchmarkTimeVec(b *testing.B) { - chk := NewChunkWithCapacity([]*types.FieldType{types.NewFieldType(mysql.TypeDate)}, 1024) - col1 := chk.Column(0) - for i := 0; i < 1024; i++ { - col1.AppendTime(types.ZeroDate) - } - col2 := col1.CopyConstruct(nil) - result := col1.CopyConstruct(nil) - - ds1 := col1.Times() - ds2 := col2.Times() - rs := result.Times() - - b.ResetTimer() - for k := 0; k < b.N; k++ { - result.ResizeTime(1024, true) - for i := 0; i < 1024; i++ { - if r := ds1[i].Compare(ds2[i]); r > 0 { - rs[i] = ds1[i] - } else { - rs[i] = ds2[i] - } - } - } -} - -func genNullCols(n int) []*Column { - cols := make([]*Column, n) - for i := range cols { - cols[i] = NewColumn(types.NewFieldType(mysql.TypeLonglong), 1024) - cols[i].ResizeInt64(1024, false) - for j := 0; j < 1024; j++ { - if rand.Intn(10) < 5 { - cols[i].SetNull(j, true) - } - } - } - return cols -} - -func TestVectorizedNulls(t *testing.T) { - for i := 0; i < 256; i++ { - cols := genNullCols(4) - lCol, rCol := cols[0], cols[1] - vecResult, rowResult := cols[2], cols[3] - vecResult.SetNulls(0, 1024, false) - rowResult.SetNulls(0, 1024, false) - vecResult.MergeNulls(lCol, rCol) - for i := 0; i < 1024; i++ { - rowResult.SetNull(i, lCol.IsNull(i) || rCol.IsNull(i)) - } - - for i := 0; i < 1024; i++ { - require.Equal(t, vecResult.IsNull(i), rowResult.IsNull(i)) - } - } -} - -func TestResetColumn(t *testing.T) { - col0 := NewColumn(types.NewFieldType(mysql.TypeVarString), 0) - col1 := NewColumn(types.NewFieldType(mysql.TypeLonglong), 0) - - // using col0.reset() here will cause panic since it doesn't reset the elemBuf field which - // is used by MergeNulls. - col0.Reset(types.ETInt) - col0.MergeNulls(col1) - - col := NewColumn(types.NewFieldType(mysql.TypeDatetime), 0) - col.Reset(types.ETDuration) - col.AppendDuration(types.Duration{}) - // using col.reset() above will let this assertion fail since the length of initialized elemBuf - // is sizeTime. - require.Equal(t, sizeGoDuration, len(col.data)) -} - -func BenchmarkMergeNullsVectorized(b *testing.B) { - cols := genNullCols(3) - b.ResetTimer() - for i := 0; i < b.N; i++ { - cols[0].MergeNulls(cols[1:]...) - } -} - -func BenchmarkMergeNullsNonVectorized(b *testing.B) { - cols := genNullCols(3) - b.ResetTimer() - for i := 0; i < b.N; i++ { - for i := 0; i < 1024; i++ { - cols[0].SetNull(i, cols[1].IsNull(i) || cols[2].IsNull(i)) - } - } -} - -func TestColumnResizeInt64(t *testing.T) { - var col = NewColumn(types.NewFieldType(mysql.TypeLonglong), 2) - col.AppendUint64(11) - col.AppendUint64(11) - - col.ResizeInt64(4, false) - require.Equal(t, col.nullBitmap, []byte{0b1111}) - col.AppendUint64(11) - require.Equal(t, col.nullBitmap, []byte{0b11111}) - col.AppendNull() - require.Equal(t, col.nullBitmap, []byte{0b011111}) - - col.ResizeUint64(11, false) - require.Equal(t, col.nullBitmap, []byte{0b11111111, 0b111}) - - col.ResizeUint64(7, true) - require.Equal(t, col.nullBitmap, []byte{0}) - - col.AppendUint64(32) - col.AppendUint64(32) - require.Equal(t, col.nullBitmap, []byte{0b10000000, 0b1}) -} diff --git a/util/chunk/compare.go b/util/chunk/compare.go deleted file mode 100644 index cbb6b562b57cf..0000000000000 --- a/util/chunk/compare.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "bytes" - "cmp" - "sort" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" -) - -// CompareFunc is a function to compare the two values in Row, the two columns must have the same type. -type CompareFunc = func(l Row, lCol int, r Row, rCol int) int - -// GetCompareFunc gets a compare function for the field type. -func GetCompareFunc(tp *types.FieldType) CompareFunc { - switch tp.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: - if mysql.HasUnsignedFlag(tp.GetFlag()) { - return cmpUint64 - } - return cmpInt64 - case mysql.TypeFloat: - return cmpFloat32 - case mysql.TypeDouble: - return cmpFloat64 - case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar, - mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - return genCmpStringFunc(tp.GetCollate()) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - return cmpTime - case mysql.TypeDuration: - return cmpDuration - case mysql.TypeNewDecimal: - return cmpMyDecimal - case mysql.TypeSet, mysql.TypeEnum: - return cmpNameValue - case mysql.TypeBit: - return cmpBit - case mysql.TypeJSON: - return cmpJSON - } - return nil -} - -func cmpNull(lNull, rNull bool) int { - if lNull && rNull { - return 0 - } - if lNull { - return -1 - } - return 1 -} - -func cmpInt64(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - return cmp.Compare(l.GetInt64(lCol), r.GetInt64(rCol)) -} - -func cmpUint64(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - return cmp.Compare(l.GetUint64(lCol), r.GetUint64(rCol)) -} - -func genCmpStringFunc(collation string) func(l Row, lCol int, r Row, rCol int) int { - return func(l Row, lCol int, r Row, rCol int) int { - return cmpStringWithCollationInfo(l, lCol, r, rCol, collation) - } -} - -func cmpStringWithCollationInfo(l Row, lCol int, r Row, rCol int, collation string) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - return types.CompareString(l.GetString(lCol), r.GetString(rCol), collation) -} - -func cmpFloat32(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - return cmp.Compare(float64(l.GetFloat32(lCol)), float64(r.GetFloat32(rCol))) -} - -func cmpFloat64(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - return cmp.Compare(l.GetFloat64(lCol), r.GetFloat64(rCol)) -} - -func cmpMyDecimal(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - lDec, rDec := l.GetMyDecimal(lCol), r.GetMyDecimal(rCol) - return lDec.Compare(rDec) -} - -func cmpTime(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - lTime, rTime := l.GetTime(lCol), r.GetTime(rCol) - return lTime.Compare(rTime) -} - -func cmpDuration(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - lDur, rDur := l.GetDuration(lCol, 0).Duration, r.GetDuration(rCol, 0).Duration - return cmp.Compare(int64(lDur), int64(rDur)) -} - -func cmpNameValue(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - _, lVal := l.getNameValue(lCol) - _, rVal := r.getNameValue(rCol) - return cmp.Compare(lVal, rVal) -} - -func cmpBit(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - lBit := types.BinaryLiteral(l.GetBytes(lCol)) - rBit := types.BinaryLiteral(r.GetBytes(rCol)) - return lBit.Compare(rBit) -} - -func cmpJSON(l Row, lCol int, r Row, rCol int) int { - lNull, rNull := l.IsNull(lCol), r.IsNull(rCol) - if lNull || rNull { - return cmpNull(lNull, rNull) - } - lJ, rJ := l.GetJSON(lCol), r.GetJSON(rCol) - return types.CompareBinaryJSON(lJ, rJ) -} - -// Compare compares the value with ad. -// We assume that the collation information of the column is the same with the datum. -func Compare(row Row, colIdx int, ad *types.Datum) int { - switch ad.Kind() { - case types.KindNull: - if row.IsNull(colIdx) { - return 0 - } - return 1 - case types.KindMinNotNull: - if row.IsNull(colIdx) { - return -1 - } - return 1 - case types.KindMaxValue: - return -1 - case types.KindInt64: - return cmp.Compare(row.GetInt64(colIdx), ad.GetInt64()) - case types.KindUint64: - return cmp.Compare(row.GetUint64(colIdx), ad.GetUint64()) - case types.KindFloat32: - return cmp.Compare(float64(row.GetFloat32(colIdx)), float64(ad.GetFloat32())) - case types.KindFloat64: - return cmp.Compare(row.GetFloat64(colIdx), ad.GetFloat64()) - case types.KindString: - return types.CompareString(row.GetString(colIdx), ad.GetString(), ad.Collation()) - case types.KindBytes, types.KindBinaryLiteral, types.KindMysqlBit: - return bytes.Compare(row.GetBytes(colIdx), ad.GetBytes()) - case types.KindMysqlDecimal: - l, r := row.GetMyDecimal(colIdx), ad.GetMysqlDecimal() - return l.Compare(r) - case types.KindMysqlDuration: - l, r := row.GetDuration(colIdx, 0).Duration, ad.GetMysqlDuration().Duration - return cmp.Compare(int64(l), int64(r)) - case types.KindMysqlEnum: - l, r := row.GetEnum(colIdx).Value, ad.GetMysqlEnum().Value - return cmp.Compare(l, r) - case types.KindMysqlSet: - l, r := row.GetSet(colIdx).Value, ad.GetMysqlSet().Value - return cmp.Compare(l, r) - case types.KindMysqlJSON: - l, r := row.GetJSON(colIdx), ad.GetMysqlJSON() - return types.CompareBinaryJSON(l, r) - case types.KindMysqlTime: - l, r := row.GetTime(colIdx), ad.GetMysqlTime() - return l.Compare(r) - default: - return 0 - } -} - -// LowerBound searches on the non-decreasing Column colIdx, -// returns the smallest index i such that the value at row i is not less than `d`. -func (c *Chunk) LowerBound(colIdx int, d *types.Datum) (index int, match bool) { - if Compare(c.GetRow(c.NumRows()-1), colIdx, d) < 0 { - return c.NumRows(), false - } - index = sort.Search(c.NumRows(), func(i int) bool { - cmp := Compare(c.GetRow(i), colIdx, d) - if cmp == 0 { - match = true - } - return cmp >= 0 - }) - return -} - -// UpperBound searches on the non-decreasing Column colIdx, -// returns the smallest index i such that the value at row i is larger than `d`. -func (c *Chunk) UpperBound(colIdx int, d *types.Datum) int { - return sort.Search(c.NumRows(), func(i int) bool { - return Compare(c.GetRow(i), colIdx, d) > 0 - }) -} diff --git a/util/chunk/list.go b/util/chunk/list.go deleted file mode 100644 index 4f0b8417583b4..0000000000000 --- a/util/chunk/list.go +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "github.com/pingcap/errors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/memory" -) - -// List holds a slice of chunks, use to append rows with max chunk size properly handled. -type List struct { - fieldTypes []*types.FieldType - initChunkSize int - maxChunkSize int - length int - chunks []*Chunk - freelist []*Chunk - - memTracker *memory.Tracker // track memory usage. - consumedIdx int // chunk index in "chunks", has been consumed. -} - -// RowPtr is used to get a row from a list. -// It is only valid for the list that returns it. -type RowPtr struct { - ChkIdx uint32 - RowIdx uint32 -} - -// NewList creates a new List with field types, init chunk size and max chunk size. -func NewList(fieldTypes []*types.FieldType, initChunkSize, maxChunkSize int) *List { - l := &List{ - fieldTypes: fieldTypes, - initChunkSize: initChunkSize, - maxChunkSize: maxChunkSize, - memTracker: memory.NewTracker(memory.LabelForChunkList, -1), - consumedIdx: -1, - } - return l -} - -// GetMemTracker returns the memory tracker of this List. -func (l *List) GetMemTracker() *memory.Tracker { - return l.memTracker -} - -// Len returns the length of the List. -func (l *List) Len() int { - return l.length -} - -// NumChunks returns the number of chunks in the List. -func (l *List) NumChunks() int { - return len(l.chunks) -} - -// FieldTypes returns the fieldTypes of the list -func (l *List) FieldTypes() []*types.FieldType { - return l.fieldTypes -} - -// NumRowsOfChunk returns the number of rows of a chunk in the ListInDisk. -func (l *List) NumRowsOfChunk(chkID int) int { - return l.chunks[chkID].NumRows() -} - -// GetChunk gets the Chunk by ChkIdx. -func (l *List) GetChunk(chkIdx int) *Chunk { - return l.chunks[chkIdx] -} - -// AppendRow appends a row to the List, the row is copied to the List. -func (l *List) AppendRow(row Row) RowPtr { - chkIdx := len(l.chunks) - 1 - if chkIdx == -1 || l.chunks[chkIdx].NumRows() >= l.chunks[chkIdx].Capacity() || chkIdx == l.consumedIdx { - newChk := l.allocChunk() - l.chunks = append(l.chunks, newChk) - if chkIdx != l.consumedIdx { - l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage()) - l.consumedIdx = chkIdx - } - chkIdx++ - } - chk := l.chunks[chkIdx] - rowIdx := chk.NumRows() - chk.AppendRow(row) - l.length++ - return RowPtr{ChkIdx: uint32(chkIdx), RowIdx: uint32(rowIdx)} -} - -// Add adds a chunk to the List, the chunk may be modified later by the list. -// Caller must make sure the input chk is not empty and not used any more and has the same field types. -func (l *List) Add(chk *Chunk) { - // FixMe: we should avoid add a Chunk that chk.NumRows() > list.maxChunkSize. - if chk.NumRows() == 0 { - // TODO: return error here. - panic("chunk appended to List should have at least 1 row") - } - if chkIdx := len(l.chunks) - 1; l.consumedIdx != chkIdx { - l.memTracker.Consume(l.chunks[chkIdx].MemoryUsage()) - l.consumedIdx = chkIdx - } - l.memTracker.Consume(chk.MemoryUsage()) - l.consumedIdx++ - l.chunks = append(l.chunks, chk) - l.length += chk.NumRows() -} - -func (l *List) allocChunk() (chk *Chunk) { - if len(l.freelist) > 0 { - lastIdx := len(l.freelist) - 1 - chk = l.freelist[lastIdx] - l.freelist = l.freelist[:lastIdx] - l.memTracker.Consume(-chk.MemoryUsage()) - chk.Reset() - return - } - if len(l.chunks) > 0 { - return Renew(l.chunks[len(l.chunks)-1], l.maxChunkSize) - } - return New(l.fieldTypes, l.initChunkSize, l.maxChunkSize) -} - -// GetRow gets a Row from the list by RowPtr. -func (l *List) GetRow(ptr RowPtr) Row { - chk := l.chunks[ptr.ChkIdx] - return chk.GetRow(int(ptr.RowIdx)) -} - -// Reset resets the List. -func (l *List) Reset() { - if lastIdx := len(l.chunks) - 1; lastIdx != l.consumedIdx { - l.memTracker.Consume(l.chunks[lastIdx].MemoryUsage()) - } - l.freelist = append(l.freelist, l.chunks...) - l.chunks = l.chunks[:0] - l.length = 0 - l.consumedIdx = -1 -} - -// Clear triggers GC for all the allocated chunks and reset the list -func (l *List) Clear() { - l.memTracker.Consume(-l.memTracker.BytesConsumed()) - l.freelist = nil - l.chunks = nil - l.length = 0 - l.consumedIdx = -1 -} - -// ListWalkFunc is used to walk the list. -// If error is returned, it will stop walking. -type ListWalkFunc = func(row Row) error - -// Walk iterate the list and call walkFunc for each row. -func (l *List) Walk(walkFunc ListWalkFunc) error { - for i := 0; i < len(l.chunks); i++ { - chk := l.chunks[i] - for j := 0; j < chk.NumRows(); j++ { - err := walkFunc(chk.GetRow(j)) - if err != nil { - return errors.Trace(err) - } - } - } - return nil -} diff --git a/util/chunk/main_test.go b/util/chunk/main_test.go deleted file mode 100644 index 811141e79fcef..0000000000000 --- a/util/chunk/main_test.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "os" - "testing" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - path, _ := os.MkdirTemp("", "tidb_enable_tmp_storage_on_oom") - config.UpdateGlobal(func(conf *config.Config) { - conf.TempStoragePath = path - }) - - goleak.VerifyTestMain(wrapper{ - M: m, - cleanUp: func() { os.RemoveAll(path) }, - }) -} - -// wrap *testing.M to do the clean up after m.Run() and before os.Exit() -type wrapper struct { - *testing.M - cleanUp func() -} - -func (m wrapper) Run() int { - defer m.cleanUp() - return m.M.Run() -} diff --git a/util/chunk/pool_test.go b/util/chunk/pool_test.go deleted file mode 100644 index 9785d3c1f6123..0000000000000 --- a/util/chunk/pool_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package chunk - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestNewPool(t *testing.T) { - pool := NewPool(1024) - require.Equal(t, 1024, pool.initCap) - require.NotNil(t, pool.varLenColPool) - require.NotNil(t, pool.fixLenColPool4) - require.NotNil(t, pool.fixLenColPool8) - require.NotNil(t, pool.fixLenColPool16) - require.NotNil(t, pool.fixLenColPool40) -} - -func TestPoolGetChunk(t *testing.T) { - initCap := 1024 - pool := NewPool(initCap) - - fieldTypes := []*types.FieldType{ - types.NewFieldType(mysql.TypeVarchar), - types.NewFieldType(mysql.TypeJSON), - types.NewFieldType(mysql.TypeFloat), - types.NewFieldType(mysql.TypeNewDecimal), - types.NewFieldType(mysql.TypeDouble), - types.NewFieldType(mysql.TypeLonglong), - // types.NewFieldType(mysql.TypeTimestamp), - // types.NewFieldType(mysql.TypeDatetime), - } - - chk := pool.GetChunk(fieldTypes) - require.NotNil(t, chk) - require.Len(t, fieldTypes, chk.NumCols()) - require.Nil(t, chk.columns[0].elemBuf) - require.Nil(t, chk.columns[1].elemBuf) - require.Equal(t, getFixedLen(fieldTypes[2]), len(chk.columns[2].elemBuf)) - require.Equal(t, getFixedLen(fieldTypes[3]), len(chk.columns[3].elemBuf)) - require.Equal(t, getFixedLen(fieldTypes[4]), len(chk.columns[4].elemBuf)) - require.Equal(t, getFixedLen(fieldTypes[5]), len(chk.columns[5].elemBuf)) - // require.Equal(t, getFixedLen(fieldTypes[6]), len(chk.columns[6].elemBuf)) - // require.Equal(t, getFixedLen(fieldTypes[7]), len(chk.columns[7].elemBuf)) - - require.Equal(t, initCap*getFixedLen(fieldTypes[2]), cap(chk.columns[2].data)) - require.Equal(t, initCap*getFixedLen(fieldTypes[3]), cap(chk.columns[3].data)) - require.Equal(t, initCap*getFixedLen(fieldTypes[4]), cap(chk.columns[4].data)) - require.Equal(t, initCap*getFixedLen(fieldTypes[5]), cap(chk.columns[5].data)) - // require.Equal(t, initCap*getFixedLen(fieldTypes[6]), cap(chk.columns[6].data)) - // require.Equal(t, initCap*getFixedLen(fieldTypes[7]), cap(chk.columns[7].data)) -} - -func TestPoolPutChunk(t *testing.T) { - initCap := 1024 - pool := NewPool(initCap) - - fieldTypes := []*types.FieldType{ - types.NewFieldType(mysql.TypeVarchar), - types.NewFieldType(mysql.TypeJSON), - types.NewFieldType(mysql.TypeFloat), - types.NewFieldType(mysql.TypeNewDecimal), - types.NewFieldType(mysql.TypeDouble), - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeTimestamp), - types.NewFieldType(mysql.TypeDatetime), - } - - chk := pool.GetChunk(fieldTypes) - pool.PutChunk(fieldTypes, chk) - require.Equal(t, 0, len(chk.columns)) -} - -func BenchmarkPoolChunkOperation(b *testing.B) { - pool := NewPool(1024) - - fieldTypes := []*types.FieldType{ - types.NewFieldType(mysql.TypeVarchar), - types.NewFieldType(mysql.TypeJSON), - types.NewFieldType(mysql.TypeFloat), - types.NewFieldType(mysql.TypeNewDecimal), - types.NewFieldType(mysql.TypeDouble), - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeTimestamp), - types.NewFieldType(mysql.TypeDatetime), - } - - b.ResetTimer() - b.RunParallel(func(pb *testing.PB) { - for pb.Next() { - pool.PutChunk(fieldTypes, pool.GetChunk(fieldTypes)) - } - }) -} diff --git a/util/codec/BUILD.bazel b/util/codec/BUILD.bazel deleted file mode 100644 index 17d57bcbe22f5..0000000000000 --- a/util/codec/BUILD.bazel +++ /dev/null @@ -1,54 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "codec", - srcs = [ - "bytes.go", - "codec.go", - "decimal.go", - "float.go", - "number.go", - ], - importpath = "github.com/pingcap/tidb/util/codec", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//types", - "//util/chunk", - "//util/collate", - "//util/hack", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "codec_test", - timeout = "short", - srcs = [ - "bench_test.go", - "bytes_test.go", - "codec_test.go", - "collation_test.go", - "decimal_test.go", - "main_test.go", - ], - embed = [":codec"], - flaky = True, - deps = [ - "//parser/mysql", - "//parser/terror", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//types", - "//util/benchdaily", - "//util/chunk", - "//util/collate", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/codec/bench_test.go b/util/codec/bench_test.go deleted file mode 100644 index 6081de900a30c..0000000000000 --- a/util/codec/bench_test.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package codec - -import ( - "testing" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/chunk" -) - -var valueCnt = 100 - -func composeEncodedData(size int) []byte { - values := make([]types.Datum, 0, size) - for i := 0; i < size; i++ { - values = append(values, types.NewDatum(i)) - } - bs, _ := EncodeValue(nil, nil, values...) - return bs -} - -func BenchmarkDecodeWithSize(b *testing.B) { - b.StopTimer() - bs := composeEncodedData(valueCnt) - b.StartTimer() - for i := 0; i < b.N; i++ { - _, err := Decode(bs, valueCnt) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkDecodeWithOutSize(b *testing.B) { - b.StopTimer() - bs := composeEncodedData(valueCnt) - b.StartTimer() - for i := 0; i < b.N; i++ { - _, err := Decode(bs, 1) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkEncodeIntWithSize(b *testing.B) { - for i := 0; i < b.N; i++ { - data := make([]byte, 0, 8) - EncodeInt(data, 10) - } -} - -func BenchmarkEncodeIntWithOutSize(b *testing.B) { - for i := 0; i < b.N; i++ { - EncodeInt(nil, 10) - } -} - -func BenchmarkDecodeDecimal(b *testing.B) { - dec := &types.MyDecimal{} - err := dec.FromFloat64(1211.1211113) - if err != nil { - b.Fatal(err) - } - precision, frac := dec.PrecisionAndFrac() - raw, _ := EncodeDecimal([]byte{}, dec, precision, frac) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _, _, _, err := DecodeDecimal(raw) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkDecodeOneToChunk(b *testing.B) { - str := new(types.Datum) - *str = types.NewStringDatum("a") - var raw []byte - raw = append(raw, bytesFlag) - raw = EncodeBytes(raw, str.GetBytes()) - intType := types.NewFieldType(mysql.TypeLonglong) - b.ResetTimer() - decoder := NewDecoder(chunk.New([]*types.FieldType{intType}, 32, 32), nil) - for i := 0; i < b.N; i++ { - _, err := decoder.DecodeOne(raw, 0, intType) - if err != nil { - b.Fatal(err) - } - } -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkDecodeWithSize, - BenchmarkDecodeWithOutSize, - BenchmarkEncodeIntWithSize, - BenchmarkEncodeIntWithOutSize, - BenchmarkDecodeDecimal, - BenchmarkDecodeOneToChunk, - ) -} diff --git a/util/codec/codec.go b/util/codec/codec.go deleted file mode 100644 index 68d3d46321b0e..0000000000000 --- a/util/codec/codec.go +++ /dev/null @@ -1,1381 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package codec - -import ( - "bytes" - "encoding/binary" - "fmt" - "hash" - "io" - "time" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// First byte in the encoded value which specifies the encoding type. -const ( - NilFlag byte = 0 - bytesFlag byte = 1 - compactBytesFlag byte = 2 - intFlag byte = 3 - uintFlag byte = 4 - floatFlag byte = 5 - decimalFlag byte = 6 - durationFlag byte = 7 - varintFlag byte = 8 - uvarintFlag byte = 9 - jsonFlag byte = 10 - maxFlag byte = 250 -) - -const ( - sizeUint64 = unsafe.Sizeof(uint64(0)) - sizeFloat64 = unsafe.Sizeof(float64(0)) -) - -func preRealloc(b []byte, vals []types.Datum, comparable1 bool) []byte { - var size int - for i := range vals { - switch vals[i].Kind() { - case types.KindInt64, types.KindUint64, types.KindMysqlEnum, types.KindMysqlSet, types.KindMysqlBit, types.KindBinaryLiteral: - size += sizeInt(comparable1) - case types.KindString, types.KindBytes: - size += sizeBytes(vals[i].GetBytes(), comparable1) - case types.KindMysqlTime, types.KindMysqlDuration, types.KindFloat32, types.KindFloat64: - size += 9 - case types.KindNull, types.KindMinNotNull, types.KindMaxValue: - size++ - case types.KindMysqlJSON: - size += 2 + len(vals[i].GetBytes()) - case types.KindMysqlDecimal: - size += 1 + types.MyDecimalStructSize - default: - return b - } - } - return reallocBytes(b, size) -} - -// encode will encode a datum and append it to a byte slice. If comparable1 is true, the encoded bytes can be sorted as it's original order. -// If hash is true, the encoded bytes can be checked equal as it's original value. -func encode(sc *stmtctx.StatementContext, b []byte, vals []types.Datum, comparable1 bool) (_ []byte, err error) { - b = preRealloc(b, vals, comparable1) - for i, length := 0, len(vals); i < length; i++ { - switch vals[i].Kind() { - case types.KindInt64: - b = encodeSignedInt(b, vals[i].GetInt64(), comparable1) - case types.KindUint64: - b = encodeUnsignedInt(b, vals[i].GetUint64(), comparable1) - case types.KindFloat32, types.KindFloat64: - b = append(b, floatFlag) - b = EncodeFloat(b, vals[i].GetFloat64()) - case types.KindString: - b = encodeString(b, vals[i], comparable1) - case types.KindBytes: - b = encodeBytes(b, vals[i].GetBytes(), comparable1) - case types.KindMysqlTime: - b = append(b, uintFlag) - b, err = EncodeMySQLTime(sc, vals[i].GetMysqlTime(), mysql.TypeUnspecified, b) - if err != nil { - return b, err - } - case types.KindMysqlDuration: - // duration may have negative value, so we cannot use String to encode directly. - b = append(b, durationFlag) - b = EncodeInt(b, int64(vals[i].GetMysqlDuration().Duration)) - case types.KindMysqlDecimal: - b = append(b, decimalFlag) - b, err = EncodeDecimal(b, vals[i].GetMysqlDecimal(), vals[i].Length(), vals[i].Frac()) - if terror.ErrorEqual(err, types.ErrTruncated) { - err = sc.HandleTruncate(err) - } else if terror.ErrorEqual(err, types.ErrOverflow) { - err = sc.HandleOverflow(err, err) - } - case types.KindMysqlEnum: - b = encodeUnsignedInt(b, vals[i].GetMysqlEnum().Value, comparable1) - case types.KindMysqlSet: - b = encodeUnsignedInt(b, vals[i].GetMysqlSet().Value, comparable1) - case types.KindMysqlBit, types.KindBinaryLiteral: - // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. - var val uint64 - val, err = vals[i].GetBinaryLiteral().ToInt(sc) - terror.Log(errors.Trace(err)) - b = encodeUnsignedInt(b, val, comparable1) - case types.KindMysqlJSON: - b = append(b, jsonFlag) - j := vals[i].GetMysqlJSON() - b = append(b, j.TypeCode) - b = append(b, j.Value...) - case types.KindNull: - b = append(b, NilFlag) - case types.KindMinNotNull: - b = append(b, bytesFlag) - case types.KindMaxValue: - b = append(b, maxFlag) - default: - return b, errors.Errorf("unsupport encode type %d", vals[i].Kind()) - } - } - - return b, errors.Trace(err) -} - -// EstimateValueSize uses to estimate the value size of the encoded values. -func EstimateValueSize(sc *stmtctx.StatementContext, val types.Datum) (int, error) { - l := 0 - switch val.Kind() { - case types.KindInt64: - l = valueSizeOfSignedInt(val.GetInt64()) - case types.KindUint64: - l = valueSizeOfUnsignedInt(val.GetUint64()) - case types.KindFloat32, types.KindFloat64, types.KindMysqlTime, types.KindMysqlDuration: - l = 9 - case types.KindString, types.KindBytes: - l = valueSizeOfBytes(val.GetBytes()) - case types.KindMysqlDecimal: - var err error - l, err = valueSizeOfDecimal(val.GetMysqlDecimal(), val.Length(), val.Frac()) - if err != nil { - return 0, err - } - l = l + 1 - case types.KindMysqlEnum: - l = valueSizeOfUnsignedInt(val.GetMysqlEnum().Value) - case types.KindMysqlSet: - l = valueSizeOfUnsignedInt(val.GetMysqlSet().Value) - case types.KindMysqlBit, types.KindBinaryLiteral: - val, err := val.GetBinaryLiteral().ToInt(sc) - terror.Log(errors.Trace(err)) - l = valueSizeOfUnsignedInt(val) - case types.KindMysqlJSON: - l = 2 + len(val.GetMysqlJSON().Value) - case types.KindNull, types.KindMinNotNull, types.KindMaxValue: - l = 1 - default: - return l, errors.Errorf("unsupported encode type %d", val.Kind()) - } - return l, nil -} - -// EncodeMySQLTime encodes datum of `KindMysqlTime` to []byte. -func EncodeMySQLTime(sc *stmtctx.StatementContext, t types.Time, tp byte, b []byte) (_ []byte, err error) { - // Encoding timestamp need to consider timezone. If it's not in UTC, transform to UTC first. - // This is compatible with `PBToExpr > convertTime`, and coprocessor assumes the passed timestamp is in UTC as well. - if tp == mysql.TypeUnspecified { - tp = t.Type() - } - if tp == mysql.TypeTimestamp && sc.TimeZone() != time.UTC { - err = t.ConvertTimeZone(sc.TimeZone(), time.UTC) - if err != nil { - return nil, err - } - } - var v uint64 - v, err = t.ToPackedUint() - if err != nil { - return nil, err - } - b = EncodeUint(b, v) - return b, nil -} - -func encodeString(b []byte, val types.Datum, comparable1 bool) []byte { - if collate.NewCollationEnabled() && comparable1 { - return encodeBytes(b, collate.GetCollator(val.Collation()).Key(val.GetString()), true) - } - return encodeBytes(b, val.GetBytes(), comparable1) -} - -func encodeBytes(b []byte, v []byte, comparable1 bool) []byte { - if comparable1 { - b = append(b, bytesFlag) - b = EncodeBytes(b, v) - } else { - b = append(b, compactBytesFlag) - b = EncodeCompactBytes(b, v) - } - return b -} - -func valueSizeOfBytes(v []byte) int { - return valueSizeOfSignedInt(int64(len(v))) + len(v) -} - -func sizeBytes(v []byte, comparable1 bool) int { - if comparable1 { - reallocSize := (len(v)/encGroupSize + 1) * (encGroupSize + 1) - return 1 + reallocSize - } - reallocSize := binary.MaxVarintLen64 + len(v) - return 1 + reallocSize -} - -func encodeSignedInt(b []byte, v int64, comparable1 bool) []byte { - if comparable1 { - b = append(b, intFlag) - b = EncodeInt(b, v) - } else { - b = append(b, varintFlag) - b = EncodeVarint(b, v) - } - return b -} - -func valueSizeOfSignedInt(v int64) int { - if v < 0 { - v = 0 - v - 1 - } - // flag occupy 1 bit and at lease 1 bit. - size := 2 - v = v >> 6 - for v > 0 { - size++ - v = v >> 7 - } - return size -} - -func encodeUnsignedInt(b []byte, v uint64, comparable1 bool) []byte { - if comparable1 { - b = append(b, uintFlag) - b = EncodeUint(b, v) - } else { - b = append(b, uvarintFlag) - b = EncodeUvarint(b, v) - } - return b -} - -func valueSizeOfUnsignedInt(v uint64) int { - // flag occupy 1 bit and at lease 1 bit. - size := 2 - v = v >> 7 - for v > 0 { - size++ - v = v >> 7 - } - return size -} - -func sizeInt(comparable1 bool) int { - if comparable1 { - return 9 - } - return 1 + binary.MaxVarintLen64 -} - -// EncodeKey appends the encoded values to byte slice b, returns the appended -// slice. It guarantees the encoded value is in ascending order for comparison. -// For decimal type, datum must set datum's length and frac. -func EncodeKey(sc *stmtctx.StatementContext, b []byte, v ...types.Datum) ([]byte, error) { - return encode(sc, b, v, true) -} - -// EncodeValue appends the encoded values to byte slice b, returning the appended -// slice. It does not guarantee the order for comparison. -func EncodeValue(sc *stmtctx.StatementContext, b []byte, v ...types.Datum) ([]byte, error) { - return encode(sc, b, v, false) -} - -func encodeHashChunkRowIdx(sc *stmtctx.StatementContext, row chunk.Row, tp *types.FieldType, idx int) (flag byte, b []byte, err error) { - if row.IsNull(idx) { - flag = NilFlag - return - } - switch tp.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: - flag = uvarintFlag - if !mysql.HasUnsignedFlag(tp.GetFlag()) && row.GetInt64(idx) < 0 { - flag = varintFlag - } - b = row.GetRaw(idx) - case mysql.TypeFloat: - flag = floatFlag - f := float64(row.GetFloat32(idx)) - // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. - // It makes -0's hash val different from 0's. - if f == 0 { - f = 0 - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), unsafe.Sizeof(f)) - case mysql.TypeDouble: - flag = floatFlag - f := row.GetFloat64(idx) - // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. - // It makes -0's hash val different from 0's. - if f == 0 { - f = 0 - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), unsafe.Sizeof(f)) - case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - flag = compactBytesFlag - b = row.GetBytes(idx) - b = ConvertByCollation(b, tp) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - flag = uintFlag - t := row.GetTime(idx) - - var v uint64 - v, err = t.ToPackedUint() - if err != nil { - return - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), unsafe.Sizeof(v)) - case mysql.TypeDuration: - flag = durationFlag - // duration may have negative value, so we cannot use String to encode directly. - b = row.GetRaw(idx) - case mysql.TypeNewDecimal: - flag = decimalFlag - // If hash is true, we only consider the original value of this decimal and ignore it's precision. - dec := row.GetMyDecimal(idx) - b, err = dec.ToHashKey() - if err != nil { - return - } - case mysql.TypeEnum: - if mysql.HasEnumSetAsIntFlag(tp.GetFlag()) { - flag = uvarintFlag - v := row.GetEnum(idx).Value - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) - } else { - flag = compactBytesFlag - v := row.GetEnum(idx).Value - str := "" - if enum, err := types.ParseEnumValue(tp.GetElems(), v); err == nil { - // str will be empty string if v out of definition of enum. - str = enum.Name - } - b = ConvertByCollation(hack.Slice(str), tp) - } - case mysql.TypeSet: - flag = compactBytesFlag - s, err := types.ParseSetValue(tp.GetElems(), row.GetSet(idx).Value) - if err != nil { - return 0, nil, err - } - b = ConvertByCollation(hack.Slice(s.Name), tp) - case mysql.TypeBit: - // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. - flag = uvarintFlag - v, err1 := types.BinaryLiteral(row.GetBytes(idx)).ToInt(sc) - terror.Log(errors.Trace(err1)) - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), unsafe.Sizeof(v)) - case mysql.TypeJSON: - flag = jsonFlag - json := row.GetJSON(idx) - b = json.HashValue(b) - default: - return 0, nil, errors.Errorf("unsupport column type for encode %d", tp.GetType()) - } - return -} - -// HashChunkColumns writes the encoded value of each row's column, which of index `colIdx`, to h. -func HashChunkColumns(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk.Chunk, tp *types.FieldType, colIdx int, buf []byte, isNull []bool) (err error) { - return HashChunkSelected(sc, h, chk, tp, colIdx, buf, isNull, nil, false) -} - -// HashChunkSelected writes the encoded value of selected row's column, which of index `colIdx`, to h. -// sel indicates which rows are selected. If it is nil, all rows are selected. -func HashChunkSelected(sc *stmtctx.StatementContext, h []hash.Hash64, chk *chunk.Chunk, tp *types.FieldType, colIdx int, buf []byte, - isNull, sel []bool, ignoreNull bool) (err error) { - var b []byte - column := chk.Column(colIdx) - rows := chk.NumRows() - switch tp.GetType() { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeYear: - i64s := column.Int64s() - for i, v := range i64s { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = uvarintFlag - if !mysql.HasUnsignedFlag(tp.GetFlag()) && v < 0 { - buf[0] = varintFlag - } - b = column.GetRaw(i) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeFloat: - f32s := column.Float32s() - for i, f := range f32s { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = floatFlag - d := float64(f) - // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. - // It makes -0's hash val different from 0's. - if d == 0 { - d = 0 - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&d)), sizeFloat64) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeDouble: - f64s := column.Float64s() - for i := range f64s { - f := f64s[i] - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = floatFlag - // For negative zero. In memory, 0 is [0, 0, 0, 0, 0, 0, 0, 0] and -0 is [0, 0, 0, 0, 0, 0, 0, 128]. - // It makes -0's hash val different from 0's. - if f == 0 { - f = 0 - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&f)), sizeFloat64) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = compactBytesFlag - b = column.GetBytes(i) - b = ConvertByCollation(b, tp) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - ts := column.Times() - for i, t := range ts { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = uintFlag - - var v uint64 - v, err = t.ToPackedUint() - if err != nil { - return - } - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeDuration: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = durationFlag - // duration may have negative value, so we cannot use String to encode directly. - b = column.GetRaw(i) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeNewDecimal: - ds := column.Decimals() - for i, d := range ds { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = decimalFlag - // If hash is true, we only consider the original value of this decimal and ignore it's precision. - b, err = d.ToHashKey() - if err != nil { - return - } - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeEnum: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else if mysql.HasEnumSetAsIntFlag(tp.GetFlag()) { - buf[0] = uvarintFlag - v := column.GetEnum(i).Value - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) - } else { - buf[0] = compactBytesFlag - v := column.GetEnum(i).Value - str := "" - if enum, err := types.ParseEnumValue(tp.GetElems(), v); err == nil { - // str will be empty string if v out of definition of enum. - str = enum.Name - } - b = ConvertByCollation(hack.Slice(str), tp) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeSet: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = compactBytesFlag - s, err := types.ParseSetValue(tp.GetElems(), column.GetSet(i).Value) - if err != nil { - return err - } - b = ConvertByCollation(hack.Slice(s.Name), tp) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeBit: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - // We don't need to handle errors here since the literal is ensured to be able to store in uint64 in convertToMysqlBit. - buf[0] = uvarintFlag - v, err1 := types.BinaryLiteral(column.GetBytes(i)).ToInt(sc) - terror.Log(errors.Trace(err1)) - b = unsafe.Slice((*byte)(unsafe.Pointer(&v)), sizeUint64) - } - - // As the golang doc described, `Hash.Write` never returns an error. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeJSON: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - if column.IsNull(i) { - buf[0], b = NilFlag, nil - isNull[i] = !ignoreNull - } else { - buf[0] = jsonFlag - json := column.GetJSON(i) - b = b[:0] - b = json.HashValue(b) - } - - // As the golang doc described, `Hash.Write` never returns an error.. - // See https://golang.org/pkg/hash/#Hash - _, _ = h[i].Write(buf) - _, _ = h[i].Write(b) - } - case mysql.TypeNull: - for i := 0; i < rows; i++ { - if sel != nil && !sel[i] { - continue - } - isNull[i] = !ignoreNull - buf[0] = NilFlag - _, _ = h[i].Write(buf) - } - default: - return errors.Errorf("unsupport column type for encode %d", tp.GetType()) - } - return -} - -// HashChunkRow writes the encoded values to w. -// If two rows are logically equal, it will generate the same bytes. -func HashChunkRow(sc *stmtctx.StatementContext, w io.Writer, row chunk.Row, allTypes []*types.FieldType, colIdx []int, buf []byte) (err error) { - var b []byte - for i, idx := range colIdx { - buf[0], b, err = encodeHashChunkRowIdx(sc, row, allTypes[i], idx) - if err != nil { - return errors.Trace(err) - } - _, err = w.Write(buf) - if err != nil { - return - } - _, err = w.Write(b) - if err != nil { - return - } - } - return err -} - -// EqualChunkRow returns a boolean reporting whether row1 and row2 -// with their types and column index are logically equal. -func EqualChunkRow(sc *stmtctx.StatementContext, - row1 chunk.Row, allTypes1 []*types.FieldType, colIdx1 []int, - row2 chunk.Row, allTypes2 []*types.FieldType, colIdx2 []int, -) (bool, error) { - if len(colIdx1) != len(colIdx2) { - return false, errors.Errorf("Internal error: Hash columns count mismatch, col1: %d, col2: %d", len(colIdx1), len(colIdx2)) - } - for i := range colIdx1 { - idx1, idx2 := colIdx1[i], colIdx2[i] - flag1, b1, err := encodeHashChunkRowIdx(sc, row1, allTypes1[i], idx1) - if err != nil { - return false, errors.Trace(err) - } - flag2, b2, err := encodeHashChunkRowIdx(sc, row2, allTypes2[i], idx2) - if err != nil { - return false, errors.Trace(err) - } - if !(flag1 == flag2 && bytes.Equal(b1, b2)) { - return false, nil - } - } - return true, nil -} - -// Decode decodes values from a byte slice generated with EncodeKey or EncodeValue -// before. -// size is the size of decoded datum slice. -func Decode(b []byte, size int) ([]types.Datum, error) { - if len(b) < 1 { - return nil, errors.New("invalid encoded key") - } - - var ( - err error - values = make([]types.Datum, 0, size) - ) - - for len(b) > 0 { - var d types.Datum - b, d, err = DecodeOne(b) - if err != nil { - return nil, errors.Trace(err) - } - - values = append(values, d) - } - - return values, nil -} - -// DecodeRange decodes the range values from a byte slice that generated by EncodeKey. -// It handles some special values like `MinNotNull` and `MaxValueDatum`. -// loc can be nil and only used in when the corresponding type is `mysql.TypeTimestamp`. -func DecodeRange(b []byte, size int, idxColumnTypes []byte, loc *time.Location) ([]types.Datum, []byte, error) { - if len(b) < 1 { - return nil, b, errors.New("invalid encoded key: length of key is zero") - } - - var ( - err error - values = make([]types.Datum, 0, size) - ) - - i := 0 - for len(b) > 1 { - var d types.Datum - if idxColumnTypes == nil { - b, d, err = DecodeOne(b) - } else { - if i >= len(idxColumnTypes) { - return values, b, errors.New("invalid length of index's columns") - } - if types.IsTypeTime(idxColumnTypes[i]) { - // handle datetime values specially since they are encoded to int and we'll get int values if using DecodeOne. - b, d, err = DecodeAsDateTime(b, idxColumnTypes[i], loc) - } else if types.IsTypeFloat(idxColumnTypes[i]) { - b, d, err = DecodeAsFloat32(b, idxColumnTypes[i]) - } else { - b, d, err = DecodeOne(b) - } - } - if err != nil { - return values, b, errors.Trace(err) - } - values = append(values, d) - i++ - } - - if len(b) == 1 { - switch b[0] { - case NilFlag: - values = append(values, types.Datum{}) - case bytesFlag: - values = append(values, types.MinNotNullDatum()) - // `maxFlag + 1` for PrefixNext - case maxFlag, maxFlag + 1: - values = append(values, types.MaxValueDatum()) - default: - return values, b, errors.Errorf("invalid encoded key flag %v", b[0]) - } - } - return values, nil, nil -} - -// DecodeOne decodes on datum from a byte slice generated with EncodeKey or EncodeValue. -func DecodeOne(b []byte) (remain []byte, d types.Datum, err error) { - if len(b) < 1 { - return nil, d, errors.New("invalid encoded key") - } - flag := b[0] - b = b[1:] - switch flag { - case intFlag: - var v int64 - b, v, err = DecodeInt(b) - d.SetInt64(v) - case uintFlag: - var v uint64 - b, v, err = DecodeUint(b) - d.SetUint64(v) - case varintFlag: - var v int64 - b, v, err = DecodeVarint(b) - d.SetInt64(v) - case uvarintFlag: - var v uint64 - b, v, err = DecodeUvarint(b) - d.SetUint64(v) - case floatFlag: - var v float64 - b, v, err = DecodeFloat(b) - d.SetFloat64(v) - case bytesFlag: - var v []byte - b, v, err = DecodeBytes(b, nil) - d.SetBytes(v) - case compactBytesFlag: - var v []byte - b, v, err = DecodeCompactBytes(b) - d.SetBytes(v) - case decimalFlag: - var ( - dec *types.MyDecimal - precision, frac int - ) - b, dec, precision, frac, err = DecodeDecimal(b) - if err == nil { - d.SetMysqlDecimal(dec) - d.SetLength(precision) - d.SetFrac(frac) - } - case durationFlag: - var r int64 - b, r, err = DecodeInt(b) - if err == nil { - // use max fsp, let outer to do round manually. - v := types.Duration{Duration: time.Duration(r), Fsp: types.MaxFsp} - d.SetMysqlDuration(v) - } - case jsonFlag: - var size int - size, err = types.PeekBytesAsJSON(b) - if err != nil { - return b, d, err - } - j := types.BinaryJSON{TypeCode: b[0], Value: b[1:size]} - d.SetMysqlJSON(j) - b = b[size:] - case NilFlag: - default: - return b, d, errors.Errorf("invalid encoded key flag %v", flag) - } - if err != nil { - return b, d, errors.Trace(err) - } - return b, d, nil -} - -// DecodeAsDateTime decodes on datum from []byte of `KindMysqlTime`. -func DecodeAsDateTime(b []byte, tp byte, loc *time.Location) (remain []byte, d types.Datum, err error) { - if len(b) < 1 { - return nil, d, errors.New("invalid encoded key") - } - flag := b[0] - b = b[1:] - var v uint64 - switch flag { - case uintFlag: - b, v, err = DecodeUint(b) - case uvarintFlag: - // Datetime can be encoded as Uvarint - b, v, err = DecodeUvarint(b) - - default: - return b, d, errors.Errorf("invalid encoded key flag %v", flag) - } - if err != nil { - return b, d, err - } - t := types.NewTime(types.ZeroCoreTime, tp, 0) - err = t.FromPackedUint(v) - if err != nil { - return b, d, errors.Trace(err) - } - if tp == mysql.TypeTimestamp && !t.IsZero() && loc != nil { - err = t.ConvertTimeZone(time.UTC, loc) - if err != nil { - return b, d, err - } - } - d.SetMysqlTime(t) - return b, d, nil -} - -// DecodeAsFloat32 decodes value for mysql.TypeFloat -func DecodeAsFloat32(b []byte, tp byte) (remain []byte, d types.Datum, err error) { - if len(b) < 1 || tp != mysql.TypeFloat { - return nil, d, errors.New("invalid encoded key") - } - flag := b[0] - b = b[1:] - if flag != floatFlag { - return b, d, errors.Errorf("invalid encoded key flag %v for DecodeAsFloat32", flag) - } - var v float64 - b, v, err = DecodeFloat(b) - if err != nil { - return nil, d, err - } - d.SetFloat32FromF64(v) - return b, d, nil -} - -// CutOne cuts the first encoded value from b. -// It will return the first encoded item and the remains as byte slice. -func CutOne(b []byte) (data []byte, remain []byte, err error) { - l, err := peek(b) - if err != nil { - return nil, nil, errors.Trace(err) - } - return b[:l], b[l:], nil -} - -// CutColumnID cuts the column ID from b. -// It will return the remains as byte slice and column ID -func CutColumnID(b []byte) (remain []byte, n int64, err error) { - if len(b) < 1 { - return nil, 0, errors.New("invalid encoded key") - } - // skip the flag - b = b[1:] - return DecodeVarint(b) -} - -// SetRawValues set raw datum values from a row data. -func SetRawValues(data []byte, values []types.Datum) error { - for i := 0; i < len(values); i++ { - l, err := peek(data) - if err != nil { - return errors.Trace(err) - } - values[i].SetRaw(data[:l:l]) - data = data[l:] - } - return nil -} - -// peek peeks the first encoded value from b and returns its length. -func peek(b []byte) (length int, err error) { - originLength := len(b) - if len(b) < 1 { - return 0, errors.New("invalid encoded key") - } - flag := b[0] - length++ - b = b[1:] - var l int - switch flag { - case NilFlag: - case intFlag, uintFlag, floatFlag, durationFlag: - // Those types are stored in 8 bytes. - l = 8 - case bytesFlag: - l, err = peekBytes(b) - case compactBytesFlag: - l, err = peekCompactBytes(b) - case decimalFlag: - l, err = types.DecimalPeak(b) - case varintFlag: - l, err = peekVarint(b) - case uvarintFlag: - l, err = peekUvarint(b) - case jsonFlag: - l, err = types.PeekBytesAsJSON(b) - default: - return 0, errors.Errorf("invalid encoded key flag %v", flag) - } - if err != nil { - return 0, errors.Trace(err) - } - length += l - if length <= 0 { - return 0, errors.New("invalid encoded key") - } else if length > originLength { - return 0, errors.Errorf("invalid encoded key, "+ - "expected length: %d, actual length: %d", length, originLength) - } - return -} - -func peekBytes(b []byte) (int, error) { - offset := 0 - for { - if len(b) < offset+encGroupSize+1 { - return 0, errors.New("insufficient bytes to decode value") - } - // The byte slice is encoded into many groups. - // For each group, there are 8 bytes for data and 1 byte for marker. - marker := b[offset+encGroupSize] - padCount := encMarker - marker - offset += encGroupSize + 1 - // When padCount is not zero, it means we get the end of the byte slice. - if padCount != 0 { - break - } - } - return offset, nil -} - -func peekCompactBytes(b []byte) (int, error) { - // Get length. - v, n := binary.Varint(b) - vi := int(v) - if n < 0 { - return 0, errors.New("value larger than 64 bits") - } else if n == 0 { - return 0, errors.New("insufficient bytes to decode value") - } - if len(b) < vi+n { - return 0, errors.Errorf("insufficient bytes to decode value, expected length: %v", n) - } - return n + vi, nil -} - -func peekVarint(b []byte) (int, error) { - _, n := binary.Varint(b) - if n < 0 { - return 0, errors.New("value larger than 64 bits") - } - return n, nil -} - -func peekUvarint(b []byte) (int, error) { - _, n := binary.Uvarint(b) - if n < 0 { - return 0, errors.New("value larger than 64 bits") - } - return n, nil -} - -// Decoder is used to decode value to chunk. -type Decoder struct { - chk *chunk.Chunk - timezone *time.Location - - // buf is only used for DecodeBytes to avoid the cost of makeslice. - buf []byte -} - -// NewDecoder creates a Decoder. -func NewDecoder(chk *chunk.Chunk, timezone *time.Location) *Decoder { - return &Decoder{ - chk: chk, - timezone: timezone, - } -} - -// DecodeOne decodes one value to chunk and returns the remained bytes. -func (decoder *Decoder) DecodeOne(b []byte, colIdx int, ft *types.FieldType) (remain []byte, err error) { - if len(b) < 1 { - return nil, errors.New("invalid encoded key") - } - chk := decoder.chk - flag := b[0] - b = b[1:] - switch flag { - case intFlag: - var v int64 - b, v, err = DecodeInt(b) - if err != nil { - return nil, errors.Trace(err) - } - appendIntToChunk(v, chk, colIdx, ft) - case uintFlag: - var v uint64 - b, v, err = DecodeUint(b) - if err != nil { - return nil, errors.Trace(err) - } - err = appendUintToChunk(v, chk, colIdx, ft, decoder.timezone) - case varintFlag: - var v int64 - b, v, err = DecodeVarint(b) - if err != nil { - return nil, errors.Trace(err) - } - appendIntToChunk(v, chk, colIdx, ft) - case uvarintFlag: - var v uint64 - b, v, err = DecodeUvarint(b) - if err != nil { - return nil, errors.Trace(err) - } - err = appendUintToChunk(v, chk, colIdx, ft, decoder.timezone) - case floatFlag: - var v float64 - b, v, err = DecodeFloat(b) - if err != nil { - return nil, errors.Trace(err) - } - appendFloatToChunk(v, chk, colIdx, ft) - case bytesFlag: - b, decoder.buf, err = DecodeBytes(b, decoder.buf) - if err != nil { - return nil, errors.Trace(err) - } - chk.AppendBytes(colIdx, decoder.buf) - case compactBytesFlag: - var v []byte - b, v, err = DecodeCompactBytes(b) - if err != nil { - return nil, errors.Trace(err) - } - chk.AppendBytes(colIdx, v) - case decimalFlag: - var dec *types.MyDecimal - var frac int - b, dec, _, frac, err = DecodeDecimal(b) - if err != nil { - return nil, errors.Trace(err) - } - if ft.GetDecimal() != types.UnspecifiedLength && frac > ft.GetDecimal() { - to := new(types.MyDecimal) - err := dec.Round(to, ft.GetDecimal(), types.ModeHalfUp) - if err != nil { - return nil, errors.Trace(err) - } - dec = to - } - chk.AppendMyDecimal(colIdx, dec) - case durationFlag: - var r int64 - b, r, err = DecodeInt(b) - if err != nil { - return nil, errors.Trace(err) - } - v := types.Duration{Duration: time.Duration(r), Fsp: ft.GetDecimal()} - chk.AppendDuration(colIdx, v) - case jsonFlag: - var size int - size, err = types.PeekBytesAsJSON(b) - if err != nil { - return nil, errors.Trace(err) - } - chk.AppendJSON(colIdx, types.BinaryJSON{TypeCode: b[0], Value: b[1:size]}) - b = b[size:] - case NilFlag: - chk.AppendNull(colIdx) - default: - return nil, errors.Errorf("invalid encoded key flag %v", flag) - } - if err != nil { - return nil, errors.Trace(err) - } - return b, nil -} - -func appendIntToChunk(val int64, chk *chunk.Chunk, colIdx int, ft *types.FieldType) { - switch ft.GetType() { - case mysql.TypeDuration: - v := types.Duration{Duration: time.Duration(val), Fsp: ft.GetDecimal()} - chk.AppendDuration(colIdx, v) - default: - chk.AppendInt64(colIdx, val) - } -} - -func appendUintToChunk(val uint64, chk *chunk.Chunk, colIdx int, ft *types.FieldType, loc *time.Location) error { - switch ft.GetType() { - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - t := types.NewTime(types.ZeroCoreTime, ft.GetType(), ft.GetDecimal()) - var err error - err = t.FromPackedUint(val) - if err != nil { - return errors.Trace(err) - } - if ft.GetType() == mysql.TypeTimestamp && !t.IsZero() { - err = t.ConvertTimeZone(time.UTC, loc) - if err != nil { - return errors.Trace(err) - } - } - chk.AppendTime(colIdx, t) - case mysql.TypeEnum: - // ignore error deliberately, to read empty enum value. - enum, err := types.ParseEnumValue(ft.GetElems(), val) - if err != nil { - enum = types.Enum{} - } - chk.AppendEnum(colIdx, enum) - case mysql.TypeSet: - set, err := types.ParseSetValue(ft.GetElems(), val) - if err != nil { - return errors.Trace(err) - } - chk.AppendSet(colIdx, set) - case mysql.TypeBit: - byteSize := (ft.GetFlen() + 7) >> 3 - chk.AppendBytes(colIdx, types.NewBinaryLiteralFromUint(val, byteSize)) - default: - chk.AppendUint64(colIdx, val) - } - return nil -} - -func appendFloatToChunk(val float64, chk *chunk.Chunk, colIdx int, ft *types.FieldType) { - if ft.GetType() == mysql.TypeFloat { - chk.AppendFloat32(colIdx, float32(val)) - } else { - chk.AppendFloat64(colIdx, val) - } -} - -// HashGroupKey encodes each row of this column and append encoded data into buf. -// Only use in the aggregate executor. -func HashGroupKey(sc *stmtctx.StatementContext, n int, col *chunk.Column, buf [][]byte, ft *types.FieldType) ([][]byte, error) { - var err error - switch ft.EvalType() { - case types.ETInt: - i64s := col.Int64s() - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = encodeSignedInt(buf[i], i64s[i], false) - } - } - case types.ETReal: - f64s := col.Float64s() - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = append(buf[i], floatFlag) - buf[i] = EncodeFloat(buf[i], f64s[i]) - } - } - case types.ETDecimal: - ds := col.Decimals() - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = append(buf[i], decimalFlag) - buf[i], err = EncodeDecimal(buf[i], &ds[i], ft.GetFlen(), ft.GetDecimal()) - if terror.ErrorEqual(err, types.ErrTruncated) { - err = sc.HandleTruncate(err) - } else if terror.ErrorEqual(err, types.ErrOverflow) { - err = sc.HandleOverflow(err, err) - } - if err != nil { - return nil, err - } - } - } - case types.ETDatetime, types.ETTimestamp: - ts := col.Times() - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = append(buf[i], uintFlag) - buf[i], err = EncodeMySQLTime(sc, ts[i], mysql.TypeUnspecified, buf[i]) - if err != nil { - return nil, err - } - } - } - case types.ETDuration: - ds := col.GoDurations() - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = append(buf[i], durationFlag) - buf[i] = EncodeInt(buf[i], int64(ds[i])) - } - } - case types.ETJson: - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = append(buf[i], jsonFlag) - buf[i] = col.GetJSON(i).HashValue(buf[i]) - } - } - case types.ETString: - for i := 0; i < n; i++ { - if col.IsNull(i) { - buf[i] = append(buf[i], NilFlag) - } else { - buf[i] = encodeBytes(buf[i], ConvertByCollation(col.GetBytes(i), ft), false) - } - } - default: - return nil, fmt.Errorf("invalid eval type %v", ft.EvalType()) - } - return buf, nil -} - -// ConvertByCollation converts these bytes according to its collation. -func ConvertByCollation(raw []byte, tp *types.FieldType) []byte { - collator := collate.GetCollator(tp.GetCollate()) - return collator.Key(string(hack.String(raw))) -} - -// ConvertByCollationStr converts this string according to its collation. -func ConvertByCollationStr(str string, tp *types.FieldType) string { - collator := collate.GetCollator(tp.GetCollate()) - return string(hack.String(collator.Key(str))) -} - -// HashCode encodes a Datum into a unique byte slice. -// It is mostly the same as EncodeValue, but it doesn't contain truncation or verification logic in order to make the encoding lossless. -func HashCode(b []byte, d types.Datum) []byte { - switch d.Kind() { - case types.KindInt64: - b = encodeSignedInt(b, d.GetInt64(), false) - case types.KindUint64: - b = encodeUnsignedInt(b, d.GetUint64(), false) - case types.KindFloat32, types.KindFloat64: - b = append(b, floatFlag) - b = EncodeFloat(b, d.GetFloat64()) - case types.KindString: - b = encodeString(b, d, false) - case types.KindBytes: - b = encodeBytes(b, d.GetBytes(), false) - case types.KindMysqlTime: - b = append(b, uintFlag) - t := d.GetMysqlTime().CoreTime() - b = encodeUnsignedInt(b, uint64(t), true) - case types.KindMysqlDuration: - // duration may have negative value, so we cannot use String to encode directly. - b = append(b, durationFlag) - b = EncodeInt(b, int64(d.GetMysqlDuration().Duration)) - case types.KindMysqlDecimal: - b = append(b, decimalFlag) - decStr := d.GetMysqlDecimal().ToString() - b = encodeBytes(b, decStr, false) - case types.KindMysqlEnum: - b = encodeUnsignedInt(b, d.GetMysqlEnum().Value, false) - case types.KindMysqlSet: - b = encodeUnsignedInt(b, d.GetMysqlSet().Value, false) - case types.KindMysqlBit, types.KindBinaryLiteral: - val := d.GetBinaryLiteral() - b = encodeBytes(b, val, false) - case types.KindMysqlJSON: - b = append(b, jsonFlag) - j := d.GetMysqlJSON() - b = append(b, j.TypeCode) - b = append(b, j.Value...) - case types.KindNull: - b = append(b, NilFlag) - case types.KindMinNotNull: - b = append(b, bytesFlag) - case types.KindMaxValue: - b = append(b, maxFlag) - default: - logutil.BgLogger().Warn("trying to calculate HashCode of an unexpected type of Datum", - zap.Uint8("Datum Kind", d.Kind()), - zap.Stack("stack")) - } - return b -} diff --git a/util/codec/codec_test.go b/util/codec/codec_test.go deleted file mode 100644 index 63618af18adee..0000000000000 --- a/util/codec/codec_test.go +++ /dev/null @@ -1,1300 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -package codec - -import ( - "bytes" - "fmt" - "hash" - "hash/crc32" - "hash/fnv" - "math" - "testing" - "time" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/collate" - "github.com/stretchr/testify/require" -) - -func TestCodecKey(t *testing.T) { - table := []struct { - Input []types.Datum - Expect []types.Datum - }{ - { - types.MakeDatums(int64(1)), - types.MakeDatums(int64(1)), - }, - - { - types.MakeDatums(float32(1), float64(3.15), []byte("123"), "123"), - types.MakeDatums(float64(1), float64(3.15), []byte("123"), []byte("123")), - }, - { - types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), - types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), - }, - - { - types.MakeDatums(true, false), - types.MakeDatums(int64(1), int64(0)), - }, - - { - types.MakeDatums(nil), - types.MakeDatums(nil), - }, - - { - types.MakeDatums(types.NewBinaryLiteralFromUint(100, -1), types.NewBinaryLiteralFromUint(100, 4)), - types.MakeDatums(uint64(100), uint64(100)), - }, - - { - types.MakeDatums(types.Enum{Name: "a", Value: 1}, types.Set{Name: "a", Value: 1}), - types.MakeDatums(uint64(1), uint64(1)), - }, - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for i, datums := range table { - comment := fmt.Sprintf("%d %v", i, datums) - b, err := EncodeKey(sc, nil, datums.Input...) - require.NoError(t, err, comment) - - args, err := Decode(b, 1) - require.NoError(t, err, comment) - require.Equal(t, datums.Expect, args, comment) - - b, err = EncodeValue(sc, nil, datums.Input...) - require.NoError(t, err, comment) - - size, err := estimateValuesSize(sc, datums.Input) - require.NoError(t, err, comment) - require.Len(t, b, size, comment) - - args, err = Decode(b, 1) - require.NoError(t, err, comment) - require.Equal(t, datums.Expect, args, comment) - } - - var raw types.Datum - raw.SetRaw([]byte("raw")) - _, err := EncodeKey(sc, nil, raw) - require.Error(t, err) -} - -func estimateValuesSize(sc *stmtctx.StatementContext, vals []types.Datum) (int, error) { - size := 0 - for _, val := range vals { - length, err := EstimateValueSize(sc, val) - if err != nil { - return 0, err - } - size += length - } - return size, nil -} - -func TestCodecKeyCompare(t *testing.T) { - table := []struct { - Left []types.Datum - Right []types.Datum - Expect int - }{ - { - types.MakeDatums(1), - types.MakeDatums(1), - 0, - }, - { - types.MakeDatums(-1), - types.MakeDatums(1), - -1, - }, - { - types.MakeDatums(3.15), - types.MakeDatums(3.12), - 1, - }, - { - types.MakeDatums("abc"), - types.MakeDatums("abcd"), - -1, - }, - { - types.MakeDatums("abcdefgh"), - types.MakeDatums("abcdefghi"), - -1, - }, - { - types.MakeDatums(1, "abc"), - types.MakeDatums(1, "abcd"), - -1, - }, - { - types.MakeDatums(1, "abc", "def"), - types.MakeDatums(1, "abcd", "af"), - -1, - }, - { - types.MakeDatums(3.12, "ebc", "def"), - types.MakeDatums(2.12, "abcd", "af"), - 1, - }, - { - types.MakeDatums([]byte{0x01, 0x00}, []byte{0xFF}), - types.MakeDatums([]byte{0x01, 0x00, 0xFF}), - -1, - }, - { - types.MakeDatums([]byte{0x01}, uint64(0xFFFFFFFFFFFFFFF)), - types.MakeDatums([]byte{0x01, 0x10}, 0), - -1, - }, - { - types.MakeDatums(0), - types.MakeDatums(nil), - 1, - }, - { - types.MakeDatums([]byte{0x00}), - types.MakeDatums(nil), - 1, - }, - { - types.MakeDatums(math.SmallestNonzeroFloat64), - types.MakeDatums(nil), - 1, - }, - { - types.MakeDatums(int64(math.MinInt64)), - types.MakeDatums(nil), - 1, - }, - { - types.MakeDatums(1, int64(math.MinInt64), nil), - types.MakeDatums(1, nil, uint64(math.MaxUint64)), - 1, - }, - { - types.MakeDatums(1, []byte{}, nil), - types.MakeDatums(1, nil, 123), - 1, - }, - { - types.MakeDatums(parseTime(t, "2011-11-11 00:00:00"), 1), - types.MakeDatums(parseTime(t, "2011-11-11 00:00:00"), 0), - 1, - }, - { - types.MakeDatums(parseDuration(t, "00:00:00"), 1), - types.MakeDatums(parseDuration(t, "00:00:01"), 0), - -1, - }, - { - []types.Datum{types.MinNotNullDatum()}, - []types.Datum{types.MaxValueDatum()}, - -1, - }, - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for _, datums := range table { - b1, err := EncodeKey(sc, nil, datums.Left...) - require.NoError(t, err) - - b2, err := EncodeKey(sc, nil, datums.Right...) - require.NoError(t, err) - - comparedRes := bytes.Compare(b1, b2) - require.Equalf(t, datums.Expect, comparedRes, "%v - %v - %v - %v - %v", datums.Left, datums.Right, b1, b2, datums.Expect) - } -} - -func TestNumberCodec(t *testing.T) { - tblInt64 := []int64{ - math.MinInt64, - math.MinInt32, - math.MinInt16, - math.MinInt8, - 0, - math.MaxInt8, - math.MaxInt16, - math.MaxInt32, - math.MaxInt64, - 1<<47 - 1, - -1 << 47, - 1<<23 - 1, - -1 << 23, - 1<<33 - 1, - -1 << 33, - 1<<55 - 1, - -1 << 55, - 1, - -1, - } - - for _, intNum := range tblInt64 { - b := EncodeInt(nil, intNum) - _, v, err := DecodeInt(b) - require.NoError(t, err) - require.Equal(t, intNum, v) - - b = EncodeIntDesc(nil, intNum) - _, v, err = DecodeIntDesc(b) - require.NoError(t, err) - require.Equal(t, intNum, v) - - b = EncodeVarint(nil, intNum) - _, v, err = DecodeVarint(b) - require.NoError(t, err) - require.Equal(t, intNum, v) - - b = EncodeComparableVarint(nil, intNum) - _, v, err = DecodeComparableVarint(b) - require.NoError(t, err) - require.Equal(t, intNum, v) - } - - tblUint64 := []uint64{ - 0, - math.MaxUint8, - math.MaxUint16, - math.MaxUint32, - math.MaxUint64, - 1<<24 - 1, - 1<<48 - 1, - 1<<56 - 1, - 1, - math.MaxInt16, - math.MaxInt8, - math.MaxInt32, - math.MaxInt64, - } - - for _, uintNum := range tblUint64 { - b := EncodeUint(nil, uintNum) - _, v, err := DecodeUint(b) - require.NoError(t, err) - require.Equal(t, uintNum, v) - - b = EncodeUintDesc(nil, uintNum) - _, v, err = DecodeUintDesc(b) - require.NoError(t, err) - require.Equal(t, uintNum, v) - - b = EncodeUvarint(nil, uintNum) - _, v, err = DecodeUvarint(b) - require.NoError(t, err) - require.Equal(t, uintNum, v) - - b = EncodeComparableUvarint(nil, uintNum) - _, v, err = DecodeComparableUvarint(b) - require.NoError(t, err) - require.Equal(t, uintNum, v) - } - - var b []byte - b = EncodeComparableVarint(b, -1) - b = EncodeComparableUvarint(b, 1) - b = EncodeComparableVarint(b, 2) - b, i, err := DecodeComparableVarint(b) - require.NoError(t, err) - require.Equal(t, int64(-1), i) - - b, u, err := DecodeComparableUvarint(b) - require.NoError(t, err) - require.Equal(t, uint64(1), u) - - _, i, err = DecodeComparableVarint(b) - require.NoError(t, err) - require.Equal(t, int64(2), i) -} - -func TestNumberOrder(t *testing.T) { - tblInt64 := []struct { - Arg1 int64 - Arg2 int64 - Ret int - }{ - {-1, 1, -1}, - {math.MaxInt64, math.MinInt64, 1}, - {math.MaxInt64, math.MaxInt32, 1}, - {math.MinInt32, math.MaxInt16, -1}, - {math.MinInt64, math.MaxInt8, -1}, - {0, math.MaxInt8, -1}, - {math.MinInt8, 0, -1}, - {math.MinInt16, math.MaxInt16, -1}, - {1, -1, 1}, - {1, 0, 1}, - {-1, 0, -1}, - {0, 0, 0}, - {math.MaxInt16, math.MaxInt16, 0}, - } - - for _, intNums := range tblInt64 { - b1 := EncodeInt(nil, intNums.Arg1) - b2 := EncodeInt(nil, intNums.Arg2) - require.Equal(t, intNums.Ret, bytes.Compare(b1, b2)) - - b1 = EncodeIntDesc(nil, intNums.Arg1) - b2 = EncodeIntDesc(nil, intNums.Arg2) - require.Equal(t, -intNums.Ret, bytes.Compare(b1, b2)) - - b1 = EncodeComparableVarint(nil, intNums.Arg1) - b2 = EncodeComparableVarint(nil, intNums.Arg2) - require.Equal(t, intNums.Ret, bytes.Compare(b1, b2)) - } - - tblUint64 := []struct { - Arg1 uint64 - Arg2 uint64 - Ret int - }{ - {0, 0, 0}, - {1, 0, 1}, - {0, 1, -1}, - {math.MaxInt8, math.MaxInt16, -1}, - {math.MaxUint32, math.MaxInt32, 1}, - {math.MaxUint8, math.MaxInt8, 1}, - {math.MaxUint16, math.MaxInt32, -1}, - {math.MaxUint64, math.MaxInt64, 1}, - {math.MaxInt64, math.MaxUint32, 1}, - {math.MaxUint64, 0, 1}, - {0, math.MaxUint64, -1}, - } - - for _, uintNums := range tblUint64 { - b1 := EncodeUint(nil, uintNums.Arg1) - b2 := EncodeUint(nil, uintNums.Arg2) - require.Equal(t, uintNums.Ret, bytes.Compare(b1, b2)) - - b1 = EncodeUintDesc(nil, uintNums.Arg1) - b2 = EncodeUintDesc(nil, uintNums.Arg2) - require.Equal(t, -uintNums.Ret, bytes.Compare(b1, b2)) - - b1 = EncodeComparableUvarint(nil, uintNums.Arg1) - b2 = EncodeComparableUvarint(nil, uintNums.Arg2) - require.Equal(t, uintNums.Ret, bytes.Compare(b1, b2)) - } -} - -func TestFloatCodec(t *testing.T) { - tblFloat := []float64{ - -1, - 0, - 1, - math.MaxFloat64, - math.MaxFloat32, - math.SmallestNonzeroFloat32, - math.SmallestNonzeroFloat64, - math.Inf(-1), - math.Inf(1), - } - - for _, floatNum := range tblFloat { - b := EncodeFloat(nil, floatNum) - _, v, err := DecodeFloat(b) - require.NoError(t, err) - require.Equal(t, floatNum, v) - - b = EncodeFloatDesc(nil, floatNum) - _, v, err = DecodeFloatDesc(b) - require.NoError(t, err) - require.Equal(t, floatNum, v) - } - - tblCmp := []struct { - Arg1 float64 - Arg2 float64 - Ret int - }{ - {1, -1, 1}, - {1, 0, 1}, - {0, -1, 1}, - {0, 0, 0}, - {math.MaxFloat64, 1, 1}, - {math.MaxFloat32, math.MaxFloat64, -1}, - {math.MaxFloat64, 0, 1}, - {math.MaxFloat64, math.SmallestNonzeroFloat64, 1}, - {math.Inf(-1), 0, -1}, - {math.Inf(1), 0, 1}, - {math.Inf(-1), math.Inf(1), -1}, - } - - for _, floatNums := range tblCmp { - b1 := EncodeFloat(nil, floatNums.Arg1) - b2 := EncodeFloat(nil, floatNums.Arg2) - - ret := bytes.Compare(b1, b2) - require.Equal(t, floatNums.Ret, ret) - - b1 = EncodeFloatDesc(nil, floatNums.Arg1) - b2 = EncodeFloatDesc(nil, floatNums.Arg2) - - ret = bytes.Compare(b1, b2) - require.Equal(t, -floatNums.Ret, ret) - } -} - -func TestBytes(t *testing.T) { - tblBytes := [][]byte{ - {}, - {0x00, 0x01}, - {0xff, 0xff}, - {0x01, 0x00}, - []byte("abc"), - []byte("hello world"), - } - - for _, bytesDatum := range tblBytes { - b := EncodeBytes(nil, bytesDatum) - _, v, err := DecodeBytes(b, nil) - require.NoError(t, err) - require.Equalf(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) - - b = EncodeBytesDesc(nil, bytesDatum) - _, v, err = DecodeBytesDesc(b, nil) - require.NoError(t, err) - require.Equalf(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) - - b = EncodeCompactBytes(nil, bytesDatum) - _, v, err = DecodeCompactBytes(b) - require.NoError(t, err) - require.Equal(t, bytesDatum, v, "%v - %v - %v", bytesDatum, b, v) - } - - tblCmp := []struct { - Arg1 []byte - Arg2 []byte - Ret int - }{ - {[]byte{}, []byte{0x00}, -1}, - {[]byte{0x00}, []byte{0x00}, 0}, - {[]byte{0xFF}, []byte{0x00}, 1}, - {[]byte{0xFF}, []byte{0xFF, 0x00}, -1}, - {[]byte("a"), []byte("b"), -1}, - {[]byte("a"), []byte{0x00}, 1}, - {[]byte{0x00}, []byte{0x01}, -1}, - {[]byte{0x00, 0x01}, []byte{0x00, 0x00}, 1}, - {[]byte{0x00, 0x00, 0x00}, []byte{0x00, 0x00}, 1}, - {[]byte{0x00, 0x00, 0x00}, []byte{0x00, 0x00}, 1}, - {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, -1}, - {[]byte{0x01, 0x02, 0x03, 0x00}, []byte{0x01, 0x02, 0x03}, 1}, - {[]byte{0x01, 0x03, 0x03, 0x04}, []byte{0x01, 0x03, 0x03, 0x05}, -1}, - {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, -1}, - {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 1}, - {[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00}, []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}, 1}, - } - - for _, bytesData := range tblCmp { - b1 := EncodeBytes(nil, bytesData.Arg1) - b2 := EncodeBytes(nil, bytesData.Arg2) - - ret := bytes.Compare(b1, b2) - require.Equal(t, bytesData.Ret, ret) - - b1 = EncodeBytesDesc(nil, bytesData.Arg1) - b2 = EncodeBytesDesc(nil, bytesData.Arg2) - - ret = bytes.Compare(b1, b2) - require.Equal(t, -bytesData.Ret, ret) - } -} - -func parseTime(t *testing.T, s string) types.Time { - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - m, err := types.ParseTime(sc, s, mysql.TypeDatetime, types.DefaultFsp, nil) - require.NoError(t, err) - return m -} - -func parseDuration(t *testing.T, s string) types.Duration { - m, _, err := types.ParseDuration(nil, s, types.DefaultFsp) - require.NoError(t, err) - return m -} - -func TestTime(t *testing.T) { - tbl := []string{ - "2011-01-01 00:00:00", - "2011-01-01 00:00:00", - "0001-01-01 00:00:00", - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for _, timeDatum := range tbl { - m := types.NewDatum(parseTime(t, timeDatum)) - - b, err := EncodeKey(sc, nil, m) - require.NoError(t, err) - v, err := Decode(b, 1) - require.NoError(t, err) - - var rawTime types.Time - rawTime.SetType(mysql.TypeDatetime) - err = rawTime.FromPackedUint(v[0].GetUint64()) - require.NoError(t, err) - - require.Equal(t, m, types.NewDatum(rawTime)) - } - - tblCmp := []struct { - Arg1 string - Arg2 string - Ret int - }{ - {"2011-10-10 00:00:00", "2000-12-12 11:11:11", 1}, - {"2000-10-10 00:00:00", "2001-10-10 00:00:00", -1}, - {"2000-10-10 00:00:00", "2000-10-10 00:00:00", 0}, - } - - for _, timeData := range tblCmp { - m1 := types.NewDatum(parseTime(t, timeData.Arg1)) - m2 := types.NewDatum(parseTime(t, timeData.Arg2)) - - b1, err := EncodeKey(sc, nil, m1) - require.NoError(t, err) - b2, err := EncodeKey(sc, nil, m2) - require.NoError(t, err) - - ret := bytes.Compare(b1, b2) - require.Equal(t, timeData.Ret, ret) - } -} - -func TestDuration(t *testing.T) { - tbl := []string{ - "11:11:11", - "00:00:00", - "1 11:11:11", - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for _, duration := range tbl { - m := parseDuration(t, duration) - - b, err := EncodeKey(sc, nil, types.NewDatum(m)) - require.NoError(t, err) - v, err := Decode(b, 1) - require.NoError(t, err) - m.Fsp = types.MaxFsp - require.Equal(t, types.MakeDatums(m), v) - } - - tblCmp := []struct { - Arg1 string - Arg2 string - Ret int - }{ - {"20:00:00", "11:11:11", 1}, - {"00:00:00", "00:00:01", -1}, - {"00:00:00", "00:00:00", 0}, - } - - for _, durations := range tblCmp { - m1 := parseDuration(t, durations.Arg1) - m2 := parseDuration(t, durations.Arg2) - - b1, err := EncodeKey(sc, nil, types.NewDatum(m1)) - require.NoError(t, err) - b2, err := EncodeKey(sc, nil, types.NewDatum(m2)) - require.NoError(t, err) - - ret := bytes.Compare(b1, b2) - require.Equal(t, durations.Ret, ret) - } -} - -func TestDecimal(t *testing.T) { - tbl := []string{ - "1234.00", - "1234", - "12.34", - "12.340", - "0.1234", - "0.0", - "0", - "-0.0", - "-0.0000", - "-1234.00", - "-1234", - "-12.34", - "-12.340", - "-0.1234", - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for _, decimalNum := range tbl { - dec := new(types.MyDecimal) - err := dec.FromString([]byte(decimalNum)) - require.NoError(t, err) - - b, err := EncodeKey(sc, nil, types.NewDatum(dec)) - require.NoError(t, err) - v, err := Decode(b, 1) - require.NoError(t, err) - require.Len(t, v, 1) - - vv := v[0].GetMysqlDecimal() - require.Equal(t, 0, vv.Compare(dec)) - } - - tblCmp := []struct { - Arg1 interface{} - Arg2 interface{} - Ret int - }{ - // Test for float type decimal. - {"1234", "123400", -1}, - {"12340", "123400", -1}, - {"1234", "1234.5", -1}, - {"1234", "1234.0000", 0}, - {"1234", "12.34", 1}, - {"12.34", "12.35", -1}, - {"0.12", "0.1234", -1}, - {"0.1234", "12.3400", -1}, - {"0.1234", "0.1235", -1}, - {"0.123400", "12.34", -1}, - {"12.34000", "12.34", 0}, - {"0.01234", "0.01235", -1}, - {"0.1234", "0", 1}, - {"0.0000", "0", 0}, - {"0.0001", "0", 1}, - {"0.0001", "0.0000", 1}, - {"0", "-0.0000", 0}, - {"-0.0001", "0", -1}, - {"-0.1234", "0", -1}, - {"-0.1234", "-0.12", -1}, - {"-0.12", "-0.1234", 1}, - {"-0.12", "-0.1200", 0}, - {"-0.1234", "0.1234", -1}, - {"-1.234", "-12.34", 1}, - {"-0.1234", "-12.34", 1}, - {"-12.34", "1234", -1}, - {"-12.34", "-12.35", 1}, - {"-0.01234", "-0.01235", 1}, - {"-1234", "-123400", 1}, - {"-12340", "-123400", 1}, - - // Test for int type decimal. - {int64(-1), int64(1), -1}, - {int64(math.MaxInt64), int64(math.MinInt64), 1}, - {int64(math.MaxInt64), int64(math.MaxInt32), 1}, - {int64(math.MinInt32), int64(math.MaxInt16), -1}, - {int64(math.MinInt64), int64(math.MaxInt8), -1}, - {int64(0), int64(math.MaxInt8), -1}, - {int64(math.MinInt8), int64(0), -1}, - {int64(math.MinInt16), int64(math.MaxInt16), -1}, - {int64(1), int64(-1), 1}, - {int64(1), int64(0), 1}, - {int64(-1), int64(0), -1}, - {int64(0), int64(0), 0}, - {int64(math.MaxInt16), int64(math.MaxInt16), 0}, - - // Test for uint type decimal. - {uint64(0), uint64(0), 0}, - {uint64(1), uint64(0), 1}, - {uint64(0), uint64(1), -1}, - {uint64(math.MaxInt8), uint64(math.MaxInt16), -1}, - {uint64(math.MaxUint32), uint64(math.MaxInt32), 1}, - {uint64(math.MaxUint8), uint64(math.MaxInt8), 1}, - {uint64(math.MaxUint16), uint64(math.MaxInt32), -1}, - {uint64(math.MaxUint64), uint64(math.MaxInt64), 1}, - {uint64(math.MaxInt64), uint64(math.MaxUint32), 1}, - {uint64(math.MaxUint64), uint64(0), 1}, - {uint64(0), uint64(math.MaxUint64), -1}, - } - for _, decimalNums := range tblCmp { - d1 := types.NewDatum(decimalNums.Arg1) - dec1, err := d1.ToDecimal(sc) - require.NoError(t, err) - d1.SetMysqlDecimal(dec1) - - d2 := types.NewDatum(decimalNums.Arg2) - dec2, err := d2.ToDecimal(sc) - require.NoError(t, err) - d2.SetMysqlDecimal(dec2) - - d1.SetLength(30) - d1.SetFrac(6) - d2.SetLength(30) - d2.SetFrac(6) - - b1, err := EncodeKey(sc, nil, d1) - require.NoError(t, err) - b2, err := EncodeKey(sc, nil, d2) - require.NoError(t, err) - - ret := bytes.Compare(b1, b2) - require.Equalf(t, decimalNums.Ret, ret, "%v %x %x", decimalNums, b1, b2) - - b1, err = EncodeValue(sc, b1[:0], d1) - require.NoError(t, err) - size, err := EstimateValueSize(sc, d1) - require.NoError(t, err) - require.Len(t, b1, size) - } - - floats := []float64{-123.45, -123.40, -23.45, -1.43, -0.93, -0.4333, -0.068, - -0.0099, 0, 0.001, 0.0012, 0.12, 1.2, 1.23, 123.3, 2424.242424} - decs := make([][]byte, 0, len(floats)) - for i := range floats { - dec := types.NewDecFromFloatForTest(floats[i]) - var d types.Datum - d.SetLength(20) - d.SetFrac(6) - d.SetMysqlDecimal(dec) - b, err := EncodeDecimal(nil, d.GetMysqlDecimal(), d.Length(), d.Frac()) - require.NoError(t, err) - decs = append(decs, b) - size, err := EstimateValueSize(sc, d) - require.NoError(t, err) - // size - 1 because the flag occupy 1 bit. - require.Len(t, b, size-1) - } - for i := 0; i < len(decs)-1; i++ { - cmpRes := bytes.Compare(decs[i], decs[i+1]) - require.LessOrEqual(t, cmpRes, 0) - } - - d := types.NewDecFromStringForTest("-123.123456789") - _, err := EncodeDecimal(nil, d, 20, 5) - require.Truef(t, terror.ErrorEqual(err, types.ErrTruncated), "err %v", err) - - _, err = EncodeDecimal(nil, d, 12, 10) - require.Truef(t, terror.ErrorEqual(err, types.ErrOverflow), "err %v", err) - - sc.IgnoreTruncate.Store(true) - decimalDatum := types.NewDatum(d) - decimalDatum.SetLength(20) - decimalDatum.SetFrac(5) - _, err = EncodeValue(sc, nil, decimalDatum) - require.NoError(t, err) - - sc.OverflowAsWarning = true - decimalDatum.SetLength(12) - decimalDatum.SetFrac(10) - _, err = EncodeValue(sc, nil, decimalDatum) - require.NoError(t, err) -} - -func TestJSON(t *testing.T) { - tbl := []string{ - "1234.00", - `{"a": "b"}`, - } - - originalDatums := make([]types.Datum, 0, len(tbl)) - for _, jsonDatum := range tbl { - var d types.Datum - j, err := types.ParseBinaryJSONFromString(jsonDatum) - require.NoError(t, err) - d.SetMysqlJSON(j) - originalDatums = append(originalDatums, d) - } - - buf := make([]byte, 0, 4096) - buf, err := encode(nil, buf, originalDatums, false) - require.NoError(t, err) - - decodedDatums, err := Decode(buf, 2) - require.NoError(t, err) - - for i := range decodedDatums { - lhs := originalDatums[i].GetMysqlJSON().String() - rhs := decodedDatums[i].GetMysqlJSON().String() - require.Equal(t, lhs, rhs) - } -} - -func TestCut(t *testing.T) { - table := []struct { - Input []types.Datum - Expect []types.Datum - }{ - { - types.MakeDatums(int64(1)), - types.MakeDatums(int64(1)), - }, - - { - types.MakeDatums(float32(1), float64(3.15), []byte("123"), "123"), - types.MakeDatums(float64(1), float64(3.15), []byte("123"), []byte("123")), - }, - { - types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), - types.MakeDatums(uint64(1), float64(3.15), []byte("123"), int64(-1)), - }, - - { - types.MakeDatums(true, false), - types.MakeDatums(int64(1), int64(0)), - }, - - { - types.MakeDatums(nil), - types.MakeDatums(nil), - }, - - { - types.MakeDatums(types.NewBinaryLiteralFromUint(100, -1), types.NewBinaryLiteralFromUint(100, 4)), - types.MakeDatums(uint64(100), uint64(100)), - }, - - { - types.MakeDatums(types.Enum{Name: "a", Value: 1}, types.Set{Name: "a", Value: 1}), - types.MakeDatums(uint64(1), uint64(1)), - }, - { - types.MakeDatums(float32(1), float64(3.15), []byte("123456789012345")), - types.MakeDatums(float64(1), float64(3.15), []byte("123456789012345")), - }, - { - types.MakeDatums(types.NewDecFromInt(0), types.NewDecFromFloatForTest(-1.3)), - types.MakeDatums(types.NewDecFromInt(0), types.NewDecFromFloatForTest(-1.3)), - }, - { - types.MakeDatums(types.CreateBinaryJSON("abc")), - types.MakeDatums(types.CreateBinaryJSON("abc")), - }, - { - types.MakeDatums(types.CreateBinaryJSON(types.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), - types.MakeDatums(types.CreateBinaryJSON(types.Opaque{TypeCode: mysql.TypeString, Buf: []byte("abc")})), - }, - } - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - for i, datums := range table { - b, err := EncodeKey(sc, nil, datums.Input...) - require.NoErrorf(t, err, "%d %v", i, datums) - - var d []byte - for j, e := range datums.Expect { - d, b, err = CutOne(b) - require.NoError(t, err) - require.NotNil(t, d) - - ed, err1 := EncodeKey(sc, nil, e) - require.NoError(t, err1) - require.Equalf(t, ed, d, "%d:%d %#v", i, j, e) - } - require.Len(t, b, 0) - } - - for i, datums := range table { - b, err := EncodeValue(sc, nil, datums.Input...) - require.NoErrorf(t, err, "%d %v", i, datums) - - var d []byte - for j, e := range datums.Expect { - d, b, err = CutOne(b) - require.NoError(t, err) - require.NotNil(t, d) - - ed, err1 := EncodeValue(sc, nil, e) - require.NoError(t, err1) - require.Equalf(t, ed, d, "%d:%d %#v", i, j, e) - } - require.Len(t, b, 0) - } - - input := 42 - b, err := EncodeValue(sc, nil, types.NewDatum(input)) - require.NoError(t, err) - rem, n, err := CutColumnID(b) - require.NoError(t, err) - require.Len(t, rem, 0) - require.Equal(t, int64(input), n) -} - -func TestCutOneError(t *testing.T) { - var b []byte - _, _, err := CutOne(b) - require.Error(t, err) - require.EqualError(t, err, "invalid encoded key") - - b = []byte{4 /* codec.uintFlag */, 0, 0, 0} - _, _, err = CutOne(b) - require.Error(t, err) - require.Regexp(t, "^invalid encoded key", err.Error()) -} - -func TestSetRawValues(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - datums := types.MakeDatums(1, "abc", 1.1, []byte("def")) - rowData, err := EncodeValue(sc, nil, datums...) - require.NoError(t, err) - - values := make([]types.Datum, 4) - err = SetRawValues(rowData, values) - require.NoError(t, err) - - for i, rawVal := range values { - require.IsType(t, types.KindRaw, rawVal.Kind()) - encoded, encodedErr := EncodeValue(sc, nil, datums[i]) - require.NoError(t, encodedErr) - require.Equal(t, rawVal.GetBytes(), encoded) - } -} - -func TestDecodeOneToChunk(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - datums, tps := datumsForTest(sc) - rowCount := 3 - chk := chunkForTest(t, sc, datums, tps, rowCount) - for colIdx, tp := range tps { - for rowIdx := 0; rowIdx < rowCount; rowIdx++ { - got := chk.GetRow(rowIdx).GetDatum(colIdx, tp) - expect := datums[colIdx] - if got.IsNull() { - require.True(t, expect.IsNull()) - } else { - if got.Kind() != types.KindMysqlDecimal { - cmp, err := got.Compare(sc, &expect, collate.GetCollator(tp.GetCollate())) - require.NoError(t, err) - require.Equalf(t, 0, cmp, "expect: %v, got %v", expect, got) - } else { - require.Equal(t, expect.GetString(), got.GetString(), "expect: %v, got %v", expect, got) - } - } - } - } -} - -func TestHashGroup(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - tp := types.NewFieldType(mysql.TypeNewDecimal) - tps := []*types.FieldType{tp} - chk1 := chunk.New(tps, 3, 3) - chk1.Reset() - chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) - chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) - chk1.Column(0).AppendMyDecimal(types.NewDecFromStringForTest("-123.123456789")) - - buf1 := make([][]byte, 3) - tp1 := tp - tp1.SetFlen(20) - tp1.SetDecimal(5) - _, err := HashGroupKey(sc, 3, chk1.Column(0), buf1, tp1) - require.Error(t, err) - - tp2 := tp - tp2.SetFlen(12) - tp2.SetDecimal(10) - _, err = HashGroupKey(sc, 3, chk1.Column(0), buf1, tp2) - require.Error(t, err) -} - -func datumsForTest(_ *stmtctx.StatementContext) ([]types.Datum, []*types.FieldType) { - decType := types.NewFieldType(mysql.TypeNewDecimal) - decType.SetDecimal(2) - _tp1 := types.NewFieldType(mysql.TypeEnum) - _tp1.SetElems([]string{"a"}) - _tp2 := types.NewFieldType(mysql.TypeSet) - _tp2.SetElems([]string{"a"}) - _tp3 := types.NewFieldType(mysql.TypeSet) - _tp3.SetElems([]string{"a", "b", "c", "d", "e", "f"}) - _tp4 := types.NewFieldType(mysql.TypeBit) - _tp4.SetFlen(8) - table := []struct { - value interface{} - tp *types.FieldType - }{ - {nil, types.NewFieldType(mysql.TypeNull)}, - {nil, types.NewFieldType(mysql.TypeLonglong)}, - {nil, types.NewFieldType(mysql.TypeFloat)}, - {nil, types.NewFieldType(mysql.TypeDate)}, - {nil, types.NewFieldType(mysql.TypeDuration)}, - {nil, types.NewFieldType(mysql.TypeNewDecimal)}, - {nil, types.NewFieldType(mysql.TypeEnum)}, - {nil, types.NewFieldType(mysql.TypeSet)}, - {nil, types.NewFieldType(mysql.TypeBit)}, - {nil, types.NewFieldType(mysql.TypeJSON)}, - {nil, types.NewFieldType(mysql.TypeVarchar)}, - {nil, types.NewFieldType(mysql.TypeDouble)}, - {int64(1), types.NewFieldType(mysql.TypeTiny)}, - {int64(1), types.NewFieldType(mysql.TypeShort)}, - {int64(1), types.NewFieldType(mysql.TypeInt24)}, - {int64(1), types.NewFieldType(mysql.TypeLong)}, - {int64(-1), types.NewFieldType(mysql.TypeLong)}, - {int64(1), types.NewFieldType(mysql.TypeLonglong)}, - {uint64(1), types.NewFieldType(mysql.TypeLonglong)}, - {float32(1), types.NewFieldType(mysql.TypeFloat)}, - {float64(1), types.NewFieldType(mysql.TypeDouble)}, - {types.NewDecFromInt(1), types.NewFieldType(mysql.TypeNewDecimal)}, - {types.NewDecFromStringForTest("1.123"), decType}, - {"abc", types.NewFieldType(mysql.TypeString)}, - {"def", types.NewFieldType(mysql.TypeVarchar)}, - {"ghi", types.NewFieldType(mysql.TypeVarString)}, - {[]byte("abc"), types.NewFieldType(mysql.TypeBlob)}, - {[]byte("abc"), types.NewFieldType(mysql.TypeTinyBlob)}, - {[]byte("abc"), types.NewFieldType(mysql.TypeMediumBlob)}, - {[]byte("abc"), types.NewFieldType(mysql.TypeLongBlob)}, - {types.CurrentTime(mysql.TypeDatetime), types.NewFieldType(mysql.TypeDatetime)}, - {types.CurrentTime(mysql.TypeDate), types.NewFieldType(mysql.TypeDate)}, - {types.NewTime(types.FromGoTime(time.Now()), mysql.TypeTimestamp, types.DefaultFsp), types.NewFieldType(mysql.TypeTimestamp)}, - {types.Duration{Duration: time.Second, Fsp: 1}, types.NewFieldType(mysql.TypeDuration)}, - {types.Enum{Name: "a", Value: 1}, _tp1}, - {types.Set{Name: "a", Value: 1}, _tp2}, - {types.Set{Name: "f", Value: 32}, _tp3}, - {types.BinaryLiteral{100}, _tp4}, - {types.CreateBinaryJSON("abc"), types.NewFieldType(mysql.TypeJSON)}, - {int64(1), types.NewFieldType(mysql.TypeYear)}, - } - - datums := make([]types.Datum, 0, len(table)+2) - tps := make([]*types.FieldType, 0, len(table)+2) - for _, t := range table { - tps = append(tps, t.tp) - d := types.NewDatum(t.value) - datums = append(datums, d) - } - return datums, tps -} - -func chunkForTest(t *testing.T, sc *stmtctx.StatementContext, datums []types.Datum, tps []*types.FieldType, rowCount int) *chunk.Chunk { - decoder := NewDecoder(chunk.New(tps, 32, 32), sc.TimeZone()) - for rowIdx := 0; rowIdx < rowCount; rowIdx++ { - encoded, err := EncodeValue(sc, nil, datums...) - require.NoError(t, err) - decoder.buf = make([]byte, 0, len(encoded)) - for colIdx, tp := range tps { - encoded, err = decoder.DecodeOne(encoded, colIdx, tp) - require.NoError(t, err) - } - } - return decoder.chk -} - -func TestDecodeRange(t *testing.T) { - _, _, err := DecodeRange(nil, 0, nil, nil) - require.Error(t, err) - - datums := types.MakeDatums(1, "abc", 1.1, []byte("def")) - rowData, err := EncodeValue(nil, nil, datums...) - require.NoError(t, err) - - datums1, _, err := DecodeRange(rowData, len(datums), nil, nil) - require.NoError(t, err) - for i, datum := range datums1 { - cmp, err := datum.Compare(nil, &datums[i], collate.GetBinaryCollator()) - require.NoError(t, err) - require.Equal(t, 0, cmp) - } - - for _, b := range []byte{NilFlag, bytesFlag, maxFlag, maxFlag + 1} { - newData := append(rowData, b) - _, _, err := DecodeRange(newData, len(datums)+1, nil, nil) - require.NoError(t, err) - } -} - -func testHashChunkRowEqual(t *testing.T, a, b interface{}, equal bool) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - buf1 := make([]byte, 1) - buf2 := make([]byte, 1) - - tp1 := new(types.FieldType) - types.DefaultTypeForValue(a, tp1, mysql.DefaultCharset, mysql.DefaultCollationName) - chk1 := chunk.New([]*types.FieldType{tp1}, 1, 1) - d := types.Datum{} - d.SetValue(a, tp1) - chk1.AppendDatum(0, &d) - - tp2 := new(types.FieldType) - types.DefaultTypeForValue(b, tp2, mysql.DefaultCharset, mysql.DefaultCollationName) - chk2 := chunk.New([]*types.FieldType{tp2}, 1, 1) - d = types.Datum{} - d.SetValue(b, tp2) - chk2.AppendDatum(0, &d) - - h := crc32.NewIEEE() - err1 := HashChunkRow(sc, h, chk1.GetRow(0), []*types.FieldType{tp1}, []int{0}, buf1) - sum1 := h.Sum32() - h.Reset() - err2 := HashChunkRow(sc, h, chk2.GetRow(0), []*types.FieldType{tp2}, []int{0}, buf2) - sum2 := h.Sum32() - require.NoError(t, err1) - require.NoError(t, err2) - if equal { - require.Equal(t, sum2, sum1) - } else { - require.NotEqual(t, sum2, sum1) - } - e, err := EqualChunkRow(sc, - chk1.GetRow(0), []*types.FieldType{tp1}, []int{0}, - chk2.GetRow(0), []*types.FieldType{tp2}, []int{0}) - require.NoError(t, err) - if equal { - require.True(t, e) - } else { - require.False(t, e) - } -} - -func TestHashChunkRow(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - buf := make([]byte, 1) - datums, tps := datumsForTest(sc) - chk := chunkForTest(t, sc, datums, tps, 1) - - colIdx := make([]int, len(tps)) - for i := 0; i < len(tps); i++ { - colIdx[i] = i - } - h := crc32.NewIEEE() - err1 := HashChunkRow(sc, h, chk.GetRow(0), tps, colIdx, buf) - sum1 := h.Sum32() - h.Reset() - err2 := HashChunkRow(sc, h, chk.GetRow(0), tps, colIdx, buf) - sum2 := h.Sum32() - - require.NoError(t, err1) - require.NoError(t, err2) - require.Equal(t, sum2, sum1) - e, err := EqualChunkRow(sc, - chk.GetRow(0), tps, colIdx, - chk.GetRow(0), tps, colIdx) - require.NoError(t, err) - require.True(t, e) - - testHashChunkRowEqual(t, nil, nil, true) - testHashChunkRowEqual(t, uint64(1), int64(1), true) - testHashChunkRowEqual(t, uint64(18446744073709551615), int64(-1), false) - - dec1 := types.NewDecFromStringForTest("1.1") - dec2 := types.NewDecFromStringForTest("01.100") - testHashChunkRowEqual(t, dec1, dec2, true) - dec1 = types.NewDecFromStringForTest("1.1") - dec2 = types.NewDecFromStringForTest("01.200") - testHashChunkRowEqual(t, dec1, dec2, false) - - testHashChunkRowEqual(t, float32(1.0), float64(1.0), true) - testHashChunkRowEqual(t, float32(1.0), float64(1.1), false) - - testHashChunkRowEqual(t, "x", []byte("x"), true) - testHashChunkRowEqual(t, "x", []byte("y"), false) - - testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(1)), types.CreateBinaryJSON(float64(1.0)), true) - testHashChunkRowEqual(t, types.CreateBinaryJSON(uint64(math.MaxUint64)), types.CreateBinaryJSON(float64(math.MaxUint64)), false) - testHashChunkRowEqual(t, types.CreateBinaryJSON(int64(math.MinInt64)), types.CreateBinaryJSON(float64(math.MinInt64)), true) -} - -func TestValueSizeOfSignedInt(t *testing.T) { - testCase := []int64{64, 8192, 1048576, 134217728, 17179869184, 2199023255552, 281474976710656, 36028797018963968, 4611686018427387904} - var b []byte - for _, v := range testCase { - b := encodeSignedInt(b[:0], v-10, false) - require.Equal(t, valueSizeOfSignedInt(v-10), len(b)) - - b = encodeSignedInt(b[:0], v, false) - require.Equal(t, valueSizeOfSignedInt(v), len(b)) - - b = encodeSignedInt(b[:0], v+10, false) - require.Equal(t, valueSizeOfSignedInt(v+10), len(b)) - - // Test for negative value. - b = encodeSignedInt(b[:0], 0-v, false) - require.Equal(t, valueSizeOfSignedInt(0-v), len(b)) - - b = encodeSignedInt(b[:0], 0-v+10, false) - require.Equal(t, valueSizeOfSignedInt(0-v+10), len(b)) - - b = encodeSignedInt(b[:0], 0-v-10, false) - require.Equal(t, valueSizeOfSignedInt(0-v-10), len(b)) - } -} - -func TestValueSizeOfUnsignedInt(t *testing.T) { - testCase := []uint64{128, 16384, 2097152, 268435456, 34359738368, 4398046511104, 562949953421312, 72057594037927936, 9223372036854775808} - var b []byte - for _, v := range testCase { - b := encodeUnsignedInt(b[:0], v-10, false) - require.Equal(t, valueSizeOfUnsignedInt(v-10), len(b)) - - b = encodeUnsignedInt(b[:0], v, false) - require.Equal(t, valueSizeOfUnsignedInt(v), len(b)) - - b = encodeUnsignedInt(b[:0], v+10, false) - require.Equal(t, valueSizeOfUnsignedInt(v+10), len(b)) - } -} - -func TestHashChunkColumns(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - buf := make([]byte, 1) - datums, tps := datumsForTest(sc) - chk := chunkForTest(t, sc, datums, tps, 4) - - colIdx := make([]int, len(tps)) - for i := 0; i < len(tps); i++ { - colIdx[i] = i - } - hasNull := []bool{false, false, false} - vecHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - rowHash := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - - sel := make([]bool, len(datums)) - for i := 0; i < 3; i++ { - sel[i] = true - } - - // Test hash value of the first 12 `Null` columns - for i := 0; i < 12; i++ { - require.True(t, chk.GetRow(0).IsNull(i)) - err1 := HashChunkSelected(sc, vecHash, chk, tps[i], i, buf, hasNull, sel, false) - err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps[i:i+1], colIdx[i:i+1], buf) - err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps[i:i+1], colIdx[i:i+1], buf) - err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps[i:i+1], colIdx[i:i+1], buf) - require.NoError(t, err1) - require.NoError(t, err2) - require.NoError(t, err3) - require.NoError(t, err4) - - require.True(t, hasNull[0]) - require.True(t, hasNull[1]) - require.True(t, hasNull[2]) - require.Equal(t, rowHash[0].Sum64(), vecHash[0].Sum64()) - require.Equal(t, rowHash[1].Sum64(), vecHash[1].Sum64()) - require.Equal(t, rowHash[2].Sum64(), vecHash[2].Sum64()) - } - - // Test hash value of every single column that is not `Null` - for i := 12; i < len(tps); i++ { - hasNull = []bool{false, false, false} - vecHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - rowHash = []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - - require.False(t, chk.GetRow(0).IsNull(i)) - - err1 := HashChunkSelected(sc, vecHash, chk, tps[i], i, buf, hasNull, sel, false) - err2 := HashChunkRow(sc, rowHash[0], chk.GetRow(0), tps[i:i+1], colIdx[i:i+1], buf) - err3 := HashChunkRow(sc, rowHash[1], chk.GetRow(1), tps[i:i+1], colIdx[i:i+1], buf) - err4 := HashChunkRow(sc, rowHash[2], chk.GetRow(2), tps[i:i+1], colIdx[i:i+1], buf) - - require.NoError(t, err1) - require.NoError(t, err2) - require.NoError(t, err3) - require.NoError(t, err4) - - require.False(t, hasNull[0]) - require.False(t, hasNull[1]) - require.False(t, hasNull[2]) - require.Equal(t, rowHash[0].Sum64(), vecHash[0].Sum64()) - require.Equal(t, rowHash[1].Sum64(), vecHash[1].Sum64()) - require.Equal(t, rowHash[2].Sum64(), vecHash[2].Sum64()) - } -} diff --git a/util/codec/collation_test.go b/util/codec/collation_test.go deleted file mode 100644 index db73ba78c5dbd..0000000000000 --- a/util/codec/collation_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package codec - -import ( - "hash" - "hash/crc32" - "hash/fnv" - "testing" - "time" - - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/stretchr/testify/require" -) - -func prepareCollationData() (int, *chunk.Chunk, *chunk.Chunk) { - tp := types.NewFieldType(mysql.TypeString) - tps := []*types.FieldType{tp} - chk1 := chunk.New(tps, 3, 3) - chk2 := chunk.New(tps, 3, 3) - chk1.Reset() - chk2.Reset() - chk1.Column(0).AppendString("aaa") - chk2.Column(0).AppendString("AAA") - chk1.Column(0).AppendString("😜") - chk2.Column(0).AppendString("😃") - chk1.Column(0).AppendString("À") - chk2.Column(0).AppendString("A") - return 3, chk1, chk2 -} - -func TestHashGroupKeyCollation(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - tp := types.NewFieldType(mysql.TypeString) - n, chk1, chk2 := prepareCollationData() - - tp.SetCollate("utf8_general_ci") - buf1 := make([][]byte, n) - buf2 := make([][]byte, n) - buf1, err := HashGroupKey(sc, n, chk1.Column(0), buf1, tp) - require.NoError(t, err) - - buf2, err = HashGroupKey(sc, n, chk2.Column(0), buf2, tp) - require.NoError(t, err) - - for i := 0; i < n; i++ { - require.Equal(t, len(buf2[i]), len(buf1[i])) - for j := range buf1 { - require.Equal(t, buf2[i][j], buf1[i][j]) - } - } - - tp.SetCollate("utf8_unicode_ci") - buf1 = make([][]byte, n) - buf2 = make([][]byte, n) - buf1, err = HashGroupKey(sc, n, chk1.Column(0), buf1, tp) - require.NoError(t, err) - buf2, err = HashGroupKey(sc, n, chk2.Column(0), buf2, tp) - require.NoError(t, err) - - for i := 0; i < n; i++ { - require.Equal(t, len(buf2[i]), len(buf1[i])) - for j := range buf1 { - require.Equal(t, buf2[i][j], buf1[i][j]) - } - } -} - -func TestHashChunkRowCollation(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - tp := types.NewFieldType(mysql.TypeString) - tps := []*types.FieldType{tp} - n, chk1, chk2 := prepareCollationData() - cols := []int{0} - buf := make([]byte, 1) - - tp.SetCollate("binary") - for i := 0; i < n; i++ { - h1 := crc32.NewIEEE() - h2 := crc32.NewIEEE() - require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) - require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) - require.NotEqual(t, h2.Sum32(), h1.Sum32()) - h1.Reset() - h2.Reset() - } - - tp.SetCollate("utf8_general_ci") - for i := 0; i < n; i++ { - h1 := crc32.NewIEEE() - h2 := crc32.NewIEEE() - require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) - require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) - require.Equal(t, h2.Sum32(), h1.Sum32()) - h1.Reset() - h2.Reset() - } - - tp.SetCollate("utf8_unicode_ci") - for i := 0; i < n; i++ { - h1 := crc32.NewIEEE() - h2 := crc32.NewIEEE() - require.NoError(t, HashChunkRow(sc, h1, chk1.GetRow(i), tps, cols, buf)) - require.NoError(t, HashChunkRow(sc, h2, chk2.GetRow(i), tps, cols, buf)) - require.Equal(t, h2.Sum32(), h1.Sum32()) - h1.Reset() - h2.Reset() - } -} - -func TestHashChunkColumnsCollation(t *testing.T) { - sc := stmtctx.NewStmtCtxWithTimeZone(time.Local) - tp := types.NewFieldType(mysql.TypeString) - n, chk1, chk2 := prepareCollationData() - buf := make([]byte, 1) - hasNull := []bool{false, false, false} - h1s := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - h2s := []hash.Hash64{fnv.New64(), fnv.New64(), fnv.New64()} - - tp.SetCollate("binary") - require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) - require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) - - for i := 0; i < n; i++ { - require.NotEqual(t, h2s[i].Sum64(), h1s[i].Sum64()) - h1s[i].Reset() - h2s[i].Reset() - } - - tp.SetCollate("utf8_general_ci") - require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) - require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) - for i := 0; i < n; i++ { - require.Equal(t, h2s[i].Sum64(), h1s[i].Sum64()) - } - - tp.SetCollate("utf8_unicode_ci") - require.NoError(t, HashChunkColumns(sc, h1s, chk1, tp, 0, buf, hasNull)) - require.NoError(t, HashChunkColumns(sc, h2s, chk2, tp, 0, buf, hasNull)) - for i := 0; i < n; i++ { - require.Equal(t, h2s[i].Sum64(), h1s[i].Sum64()) - } -} diff --git a/util/codec/main_test.go b/util/codec/main_test.go deleted file mode 100644 index 4557d994e0ef0..0000000000000 --- a/util/codec/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package codec - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/collate/BUILD.bazel b/util/collate/BUILD.bazel deleted file mode 100644 index 67c279949fa5e..0000000000000 --- a/util/collate/BUILD.bazel +++ /dev/null @@ -1,51 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "collate", - srcs = [ - "bin.go", - "charset.go", - "collate.go", - "gbk_bin.go", - "gbk_chinese_ci.go", - "gbk_chinese_ci_data.go", - "general_ci.go", - "pinyin_tidb_as_cs.go", - "unicode_0400_ci_generated.go", - "unicode_0400_ci_impl.go", - "unicode_0900_ai_ci_generated.go", - "unicode_0900_ai_ci_impl.go", - ], - importpath = "github.com/pingcap/tidb/util/collate", - visibility = ["//visibility:public"], - deps = [ - "//parser/charset", - "//parser/mysql", - "//parser/terror", - "//util/collate/ucadata", - "//util/dbterror", - "//util/hack", - "//util/logutil", - "//util/stringutil", - "@com_github_pingcap_errors//:errors", - "@org_golang_x_text//encoding", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "collate_test", - timeout = "short", - srcs = [ - "collate_bench_test.go", - "collate_test.go", - "main_test.go", - ], - embed = [":collate"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/collate/charset.go b/util/collate/charset.go deleted file mode 100644 index c1d630745ac65..0000000000000 --- a/util/collate/charset.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package collate - -import "github.com/pingcap/tidb/parser/charset" - -// switchDefaultCollation switch the default collation for charset according to the new collation config. -func switchDefaultCollation(flag bool) { - if flag { - charset.CharacterSetInfos[charset.CharsetGBK].DefaultCollation = charset.CollationGBKChineseCI - } else { - charset.CharacterSetInfos[charset.CharsetGBK].DefaultCollation = charset.CollationGBKBin - } - charset.CharacterSetInfos[charset.CharsetGBK].Collations[charset.CollationGBKBin].IsDefault = !flag - charset.CharacterSetInfos[charset.CharsetGBK].Collations[charset.CollationGBKChineseCI].IsDefault = flag -} diff --git a/util/collate/main_test.go b/util/collate/main_test.go deleted file mode 100644 index c405ac95d08e4..0000000000000 --- a/util/collate/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package collate - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/collate/ucadata/BUILD.bazel b/util/collate/ucadata/BUILD.bazel deleted file mode 100644 index 7c66cb99ede63..0000000000000 --- a/util/collate/ucadata/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ucadata", - srcs = [ - "data.go", - "unicode_0900_ai_ci_data_generated.go", - "unicode_ci_data_generated.go", - ], - importpath = "github.com/pingcap/tidb/util/collate/ucadata", - visibility = ["//visibility:public"], -) - -go_test( - name = "ucadata_test", - timeout = "short", - srcs = [ - "unicode_0900_ai_ci_data_test.go", - "unicode_ci_data_original_test.go", - "unicode_ci_data_test.go", - ], - embed = [":ucadata"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/collate/ucadata/generator/BUILD.bazel b/util/collate/ucadata/generator/BUILD.bazel deleted file mode 100644 index 1243fa4e1162a..0000000000000 --- a/util/collate/ucadata/generator/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -go_library( - name = "generator_lib", - srcs = [ - "magic.go", - "main.go", - ], - embedsrcs = [ - "allkeys-4.0.0.txt", - "allkeys-9.0.0.txt", - "data.go.tpl", - ], - importpath = "github.com/pingcap/tidb/util/collate/ucadata/generator", - visibility = ["//visibility:private"], -) - -go_binary( - name = "generator", - embed = [":generator_lib"], - visibility = ["//visibility:public"], -) diff --git a/util/collate/ucaimpl/BUILD.bazel b/util/collate/ucaimpl/BUILD.bazel deleted file mode 100644 index 74c128f0966e0..0000000000000 --- a/util/collate/ucaimpl/BUILD.bazel +++ /dev/null @@ -1,15 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") - -go_library( - name = "ucaimpl_lib", - srcs = ["main.go"], - embedsrcs = ["unicode_ci.go.tpl"], - importpath = "github.com/pingcap/tidb/util/collate/ucaimpl", - visibility = ["//visibility:private"], -) - -go_binary( - name = "ucaimpl", - embed = [":ucaimpl_lib"], - visibility = ["//visibility:public"], -) diff --git a/util/column-mapping/BUILD.bazel b/util/column-mapping/BUILD.bazel deleted file mode 100644 index 03a35005bf28c..0000000000000 --- a/util/column-mapping/BUILD.bazel +++ /dev/null @@ -1,21 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "column-mapping", - srcs = ["column.go"], - importpath = "github.com/pingcap/tidb/util/column-mapping", - visibility = ["//visibility:public"], - deps = [ - "//util/table-rule-selector", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "column-mapping_test", - timeout = "short", - srcs = ["column_test.go"], - embed = [":column-mapping"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/column-mapping/column.go b/util/column-mapping/column.go deleted file mode 100644 index 054c561bd3661..0000000000000 --- a/util/column-mapping/column.go +++ /dev/null @@ -1,539 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package column - -import ( - "fmt" - "strconv" - "strings" - "sync" - - "github.com/pingcap/errors" - selector "github.com/pingcap/tidb/util/table-rule-selector" -) - -var ( - // for partition ID, ref definition of partitionID - instanceIDBitSize = 4 - schemaIDBitSize = 7 - tableIDBitSize = 8 - maxOriginID int64 = 17592186044416 -) - -// SetPartitionRule sets bit size of schema ID and table ID -func SetPartitionRule(instanceIDSize, schemaIDSize, tableIDSize int) { - instanceIDBitSize = instanceIDSize - schemaIDBitSize = schemaIDSize - tableIDBitSize = tableIDSize - maxOriginID = 1 << uint(64-instanceIDSize-schemaIDSize-tableIDSize-1) -} - -// Expr indicates how to handle column mapping -type Expr string - -// poor Expr -const ( - AddPrefix Expr = "add prefix" - AddSuffix Expr = "add suffix" - PartitionID Expr = "partition id" -) - -// Exprs is some built-in expression for column mapping -// only supports some poor expressions now, -// we would unify tableInfo later and support more -var Exprs = map[Expr]func(*mappingInfo, []interface{}) ([]interface{}, error){ - AddPrefix: addPrefix, // arguments contains prefix - AddSuffix: addSuffix, // arguments contains suffix - // arguments contains [instance_id, prefix of schema, prefix of table] - // we would compute a ID like - // [1:1 bit][2:9 bits][3:10 bits][4:44 bits] int64 (using default bits length) - // # 1 useless, no reason - // # 2 schema ID (schema suffix) - // # 3 table ID (table suffix) - // # 4 origin ID (>= 0, <= 17592186044415) - // - // others: schema = arguments[1] or arguments[1] + arguments[3] + schema suffix - // table = arguments[2] or arguments[2] + arguments[3] + table suffix - // example: schema = schema_1 table = t_1 - // => arguments[1] = "schema", arguments[2] = "t", arguments[3] = "_" - // if arguments[1]/arguments[2] == "", it means we don't use schemaID/tableID to compute partition ID - // if length of arguments is < 4, arguments[3] is set to "" (empty string) - PartitionID: partitionID, -} - -// Rule is a rule to map column -// TODO: we will do it later, if we need to implement a real column mapping, we need table structure of source and target system -type Rule struct { - PatternSchema string `yaml:"schema-pattern" json:"schema-pattern" toml:"schema-pattern"` - PatternTable string `yaml:"table-pattern" json:"table-pattern" toml:"table-pattern"` - SourceColumn string `yaml:"source-column" json:"source-column" toml:"source-column"` // modify, add refer column, ignore - TargetColumn string `yaml:"target-column" json:"target-column" toml:"target-column"` // add column, modify - Expression Expr `yaml:"expression" json:"expression" toml:"expression"` - Arguments []string `yaml:"arguments" json:"arguments" toml:"arguments"` - CreateTableQuery string `yaml:"create-table-query" json:"create-table-query" toml:"create-table-query"` -} - -// ToLower covert schema/table parttern to lower case -func (r *Rule) ToLower() { - r.PatternSchema = strings.ToLower(r.PatternSchema) - r.PatternTable = strings.ToLower(r.PatternTable) -} - -// Valid checks validity of rule. -// add prefix/suffix: it should have target column and one argument -// partition id: it should have 3 to 4 arguments -func (r *Rule) Valid() error { - if _, ok := Exprs[r.Expression]; !ok { - return errors.NotFoundf("expression %s", r.Expression) - } - - if r.TargetColumn == "" { - return errors.NotValidf("rule need to be applied a target column") - } - - if r.Expression == AddPrefix || r.Expression == AddSuffix { - if len(r.Arguments) != 1 { - return errors.NotValidf("arguments %v for add prefix/suffix", r.Arguments) - } - } - - if r.Expression == PartitionID { - switch len(r.Arguments) { - case 3, 4: - return nil - default: - return errors.NotValidf("arguments %v for patition id", r.Arguments) - } - } - - return nil -} - -// Adjust normalizes the rule into an easier-to-process form, e.g. filling in -// optional arguments with the default values. -func (r *Rule) Adjust() { - if r.Expression == PartitionID && len(r.Arguments) == 3 { - r.Arguments = append(r.Arguments, "") - } -} - -// check source and target position -func (r *Rule) adjustColumnPosition(source, target int) (src int, targ int, err error) { - // if not found target, ignore it - if target == -1 { - return source, target, errors.NotFoundf("target column %s", r.TargetColumn) - } - - return source, target, nil -} - -type mappingInfo struct { - ignore bool - sourcePosition int - targetPosition int - rule *Rule - - instanceID int64 - schemaID int64 - tableID int64 -} - -// Mapping maps column to something by rules -type Mapping struct { - selector.Selector - - caseSensitive bool - - cache struct { - sync.RWMutex - infos map[string]*mappingInfo - } -} - -// NewMapping returns a column mapping -func NewMapping(caseSensitive bool, rules []*Rule) (*Mapping, error) { - m := &Mapping{ - Selector: selector.NewTrieSelector(), - caseSensitive: caseSensitive, - } - m.resetCache() - - for _, rule := range rules { - if err := m.AddRule(rule); err != nil { - return nil, errors.Annotatef(err, "initial rule %+v in mapping", rule) - } - } - - return m, nil -} - -func (m *Mapping) addOrUpdateRule(rule *Rule, isUpdate bool) error { - if m == nil || rule == nil { - return nil - } - - err := rule.Valid() - if err != nil { - return errors.Trace(err) - } - if !m.caseSensitive { - rule.ToLower() - } - rule.Adjust() - - m.resetCache() - if isUpdate { - err = m.Insert(rule.PatternSchema, rule.PatternTable, rule, selector.Replace) - } else { - err = m.Insert(rule.PatternSchema, rule.PatternTable, rule, selector.Insert) - } - if err != nil { - var method string - if isUpdate { - method = "update" - } else { - method = "add" - } - return errors.Annotatef(err, "%s rule %+v into mapping", method, rule) - } - - return nil -} - -// AddRule adds a rule into mapping -func (m *Mapping) AddRule(rule *Rule) error { - return m.addOrUpdateRule(rule, false) -} - -// UpdateRule updates mapping rule -func (m *Mapping) UpdateRule(rule *Rule) error { - return m.addOrUpdateRule(rule, true) -} - -// RemoveRule removes a rule from mapping -func (m *Mapping) RemoveRule(rule *Rule) error { - if m == nil || rule == nil { - return nil - } - if !m.caseSensitive { - rule.ToLower() - } - - m.resetCache() - err := m.Remove(rule.PatternSchema, rule.PatternTable) - if err != nil { - return errors.Annotatef(err, "remove rule %+v from mapping", rule) - } - - return nil -} - -// HandleRowValue handles row value -func (m *Mapping) HandleRowValue(schema, table string, columns []string, vals []interface{}) ([]interface{}, []int, error) { - if m == nil { - return vals, nil, nil - } - - schemaL, tableL := schema, table - if !m.caseSensitive { - schemaL, tableL = strings.ToLower(schema), strings.ToLower(table) - } - - info, err := m.queryColumnInfo(schemaL, tableL, columns) - if err != nil { - return nil, nil, errors.Trace(err) - } - if info.ignore { - return vals, nil, nil - } - - exp, ok := Exprs[info.rule.Expression] - if !ok { - return nil, nil, errors.NotFoundf("column mapping expression %s", info.rule.Expression) - } - - vals, err = exp(info, vals) - if err != nil { - return nil, nil, errors.Trace(err) - } - - return vals, []int{info.sourcePosition, info.targetPosition}, nil -} - -// HandleDDL handles ddl -func (m *Mapping) HandleDDL(schema, table string, columns []string, statement string) (string, []int, error) { - if m == nil { - return statement, nil, nil - } - - schemaL, tableL := schema, table - if !m.caseSensitive { - schemaL, tableL = strings.ToLower(schema), strings.ToLower(table) - } - - info, err := m.queryColumnInfo(schemaL, tableL, columns) - if err != nil { - return statement, nil, errors.Trace(err) - } - - if info.ignore { - return statement, nil, nil - } - - m.resetCache() - // only output erro now, wait fix it manually - return statement, nil, errors.Errorf("ddl %s @ column mapping rule %s/%s:%+v not implemented", statement, schema, table, info.rule) -} - -func (m *Mapping) queryColumnInfo(schema, table string, columns []string) (*mappingInfo, error) { - m.cache.RLock() - ci, ok := m.cache.infos[tableName(schema, table)] - m.cache.RUnlock() - if ok { - return ci, nil - } - - var info = &mappingInfo{ - ignore: true, - } - rules := m.Match(schema, table) - if len(rules) == 0 { - m.cache.Lock() - m.cache.infos[tableName(schema, table)] = info - m.cache.Unlock() - - return info, nil - } - - var ( - schemaRules []*Rule - tableRules = make([]*Rule, 0, 1) - ) - // classify rules into schema level rules and table level - // table level rules have highest priority - for i := range rules { - rule, ok := rules[i].(*Rule) - if !ok { - return nil, errors.NotValidf("column mapping rule %+v", rules[i]) - } - - if len(rule.PatternTable) == 0 { - schemaRules = append(schemaRules, rule) - } else { - tableRules = append(tableRules, rule) - } - } - - // only support one expression for one table now, refine it later - var rule *Rule - if len(table) == 0 || len(tableRules) == 0 { - if len(schemaRules) != 1 { - return nil, errors.NotSupportedf("`%s`.`%s` matches %d schema column mapping rules which should be one. It's", schema, table, len(schemaRules)) - } - - rule = schemaRules[0] - } else { - if len(tableRules) != 1 { - return nil, errors.NotSupportedf("`%s`.`%s` matches %d table column mapping rules which should be one. It's", schema, table, len(tableRules)) - } - - rule = tableRules[0] - } - if rule == nil { - m.cache.Lock() - m.cache.infos[tableName(schema, table)] = info - m.cache.Unlock() - - return info, nil - } - - // compute source and target column position - sourcePosition := findColumnPosition(columns, rule.SourceColumn) - targetPosition := findColumnPosition(columns, rule.TargetColumn) - - sourcePosition, targetPosition, err := rule.adjustColumnPosition(sourcePosition, targetPosition) - if err != nil { - return nil, errors.Trace(err) - } - - info = &mappingInfo{ - sourcePosition: sourcePosition, - targetPosition: targetPosition, - rule: rule, - } - - // if expr is partition ID, compute schema and table ID - if rule.Expression == PartitionID { - info.instanceID, info.schemaID, info.tableID, err = computePartitionID(schema, table, rule) - if err != nil { - return nil, errors.Trace(err) - } - } - - m.cache.Lock() - m.cache.infos[tableName(schema, table)] = info - m.cache.Unlock() - - return info, nil -} - -func (m *Mapping) resetCache() { - m.cache.Lock() - m.cache.infos = make(map[string]*mappingInfo) - m.cache.Unlock() -} - -func findColumnPosition(cols []string, col string) int { - for i := range cols { - if cols[i] == col { - return i - } - } - - return -1 -} - -func tableName(schema, table string) string { - return fmt.Sprintf("`%s`.`%s`", schema, table) -} - -func addPrefix(info *mappingInfo, vals []interface{}) ([]interface{}, error) { - prefix := info.rule.Arguments[0] - originStr, ok := vals[info.targetPosition].(string) - if !ok { - return nil, errors.NotValidf("column %d value is not string, but %v, which is", info.targetPosition, vals[info.targetPosition]) - } - - // fast to concatenated string - rawByte := make([]byte, 0, len(prefix)+len(originStr)) - rawByte = append(rawByte, prefix...) - rawByte = append(rawByte, originStr...) - - vals[info.targetPosition] = string(rawByte) - return vals, nil -} - -func addSuffix(info *mappingInfo, vals []interface{}) ([]interface{}, error) { - suffix := info.rule.Arguments[0] - originStr, ok := vals[info.targetPosition].(string) - if !ok { - return nil, errors.NotValidf("column %d value is not string, but %v, which is", info.targetPosition, vals[info.targetPosition]) - } - - rawByte := make([]byte, 0, len(suffix)+len(originStr)) - rawByte = append(rawByte, originStr...) - rawByte = append(rawByte, suffix...) - - vals[info.targetPosition] = string(rawByte) - return vals, nil -} - -func partitionID(info *mappingInfo, vals []interface{}) ([]interface{}, error) { - // only int64 now - var ( - originID int64 - err error - isChars bool - ) - - switch rawID := vals[info.targetPosition].(type) { - case int: - originID = int64(rawID) - case int8: - originID = int64(rawID) - case int32: - originID = int64(rawID) - case int64: - originID = rawID - case uint: - originID = int64(rawID) - case uint16: - originID = int64(rawID) - case uint32: - originID = int64(rawID) - case uint64: - originID = int64(rawID) - case string: - originID, err = strconv.ParseInt(rawID, 10, 64) - if err != nil { - return nil, errors.NotValidf("column %d value is not int, but %v, which is", info.targetPosition, vals[info.targetPosition]) - } - isChars = true - default: - return nil, errors.NotValidf("type %T(%v)", vals[info.targetPosition], vals[info.targetPosition]) - } - - if originID >= maxOriginID || originID < 0 { - return nil, errors.NotValidf("id must less than %d, greater than or equal to 0, but got %d, which is", maxOriginID, originID) - } - - originID = info.instanceID | info.schemaID | info.tableID | originID - if isChars { - vals[info.targetPosition] = strconv.FormatInt(originID, 10) - } else { - vals[info.targetPosition] = originID - } - - return vals, nil -} - -func computePartitionID(schema, table string, rule *Rule) (instanceID int64, schemaID int64, tableID int64, err error) { - shiftCnt := uint(63) - if instanceIDBitSize > 0 && len(rule.Arguments[0]) > 0 { - var instanceIDUnsign uint64 - shiftCnt = shiftCnt - uint(instanceIDBitSize) - instanceIDUnsign, err = strconv.ParseUint(rule.Arguments[0], 10, instanceIDBitSize) - if err != nil { - return - } - instanceID = int64(instanceIDUnsign << shiftCnt) - } - - sep := rule.Arguments[3] - - if schemaIDBitSize > 0 && len(rule.Arguments[1]) > 0 { - shiftCnt = shiftCnt - uint(schemaIDBitSize) - schemaID, err = computeID(schema, rule.Arguments[1], sep, schemaIDBitSize, shiftCnt) - if err != nil { - return - } - } - - if tableIDBitSize > 0 && len(rule.Arguments[2]) > 0 { - shiftCnt = shiftCnt - uint(tableIDBitSize) - tableID, err = computeID(table, rule.Arguments[2], sep, tableIDBitSize, shiftCnt) - } - - return -} - -func computeID(name string, prefix, sep string, bitSize int, shiftCount uint) (int64, error) { - if name == prefix { - return 0, nil - } - - prefix += sep - if len(prefix) >= len(name) || prefix != name[:len(prefix)] { - return 0, errors.NotValidf("%s is not the prefix of %s", prefix, name) - } - - idStr := name[len(prefix):] - id, err := strconv.ParseUint(idStr, 10, bitSize) - if err != nil { - return 0, errors.NotValidf("the suffix of %s can't be converted to int64", idStr) - } - - return int64(id << shiftCount), nil -} diff --git a/util/compress/BUILD.bazel b/util/compress/BUILD.bazel deleted file mode 100644 index 484e2e3a0226d..0000000000000 --- a/util/compress/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "compress", - srcs = ["gzip.go"], - importpath = "github.com/pingcap/tidb/util/compress", - visibility = ["//visibility:public"], - deps = ["@com_github_klauspost_compress//gzip"], -) diff --git a/util/cpu/BUILD.bazel b/util/cpu/BUILD.bazel deleted file mode 100644 index c391f4e55cb02..0000000000000 --- a/util/cpu/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cpu", - srcs = ["cpu.go"], - importpath = "github.com/pingcap/tidb/util/cpu", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//util/cgroup", - "//util/mathutil", - "@com_github_cloudfoundry_gosigar//:gosigar", - "@com_github_pingcap_log//:log", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "cpu_test", - timeout = "short", - srcs = [ - "cpu_test.go", - "main_test.go", - ], - flaky = True, - race = "on", - shard_count = 2, - deps = [ - ":cpu", - "//resourcemanager/scheduler", - "//resourcemanager/util", - "//testkit/testsetup", - "//util/cgroup", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/cpu/cpu.go b/util/cpu/cpu.go deleted file mode 100644 index 6caa0af26037a..0000000000000 --- a/util/cpu/cpu.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cpu - -import ( - "os" - "sync" - "time" - - "github.com/cloudfoundry/gosigar" - "github.com/pingcap/log" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/cgroup" - "github.com/pingcap/tidb/util/mathutil" - "go.uber.org/atomic" - "go.uber.org/zap" -) - -var cpuUsage atomic.Float64 - -// If your kernel is lower than linux 4.7, you cannot get the cpu usage in the container. -var unsupported atomic.Bool - -// GetCPUUsage returns the cpu usage of the current process. -func GetCPUUsage() (float64, bool) { - return cpuUsage.Load(), unsupported.Load() -} - -// Observer is used to observe the cpu usage of the current process. -type Observer struct { - utime int64 - stime int64 - now int64 - exit chan struct{} - cpu mathutil.ExponentialMovingAverage - wg sync.WaitGroup -} - -// NewCPUObserver returns a cpu observer. -func NewCPUObserver() *Observer { - return &Observer{ - exit: make(chan struct{}), - now: time.Now().UnixNano(), - cpu: *mathutil.NewExponentialMovingAverage(0.95, 10), - } -} - -// Start starts the cpu observer. -func (c *Observer) Start() { - _, err := cgroup.GetCgroupCPU() - if err != nil { - unsupported.Store(true) - log.Error("GetCgroupCPU", zap.Error(err)) - return - } - c.wg.Add(1) - go func() { - ticker := time.NewTicker(100 * time.Millisecond) - defer func() { - ticker.Stop() - c.wg.Done() - }() - for { - select { - case <-ticker.C: - curr := c.observe() - c.cpu.Add(curr) - cpuUsage.Store(c.cpu.Get()) - metrics.EMACPUUsageGauge.Set(c.cpu.Get()) - case <-c.exit: - return - } - } - }() -} - -// Stop stops the cpu observer. -func (c *Observer) Stop() { - close(c.exit) - c.wg.Wait() -} - -func (c *Observer) observe() float64 { - user, sys, err := getCPUTime() - if err != nil { - log.Error("getCPUTime", zap.Error(err)) - } - cgroupCPU, _ := cgroup.GetCgroupCPU() - cpuShare := cgroupCPU.CPUShares() - now := time.Now().UnixNano() - dur := float64(now - c.now) - utime := user * 1e6 - stime := sys * 1e6 - urate := float64(utime-c.utime) / dur - srate := float64(stime-c.stime) / dur - c.now = now - c.utime = utime - c.stime = stime - return (srate + urate) / cpuShare -} - -// getCPUTime returns the cumulative user/system time (in ms) since the process start. -func getCPUTime() (userTimeMillis, sysTimeMillis int64, err error) { - pid := os.Getpid() - cpuTime := sigar.ProcTime{} - if err := cpuTime.Get(pid); err != nil { - return 0, 0, err - } - return int64(cpuTime.User), int64(cpuTime.Sys), nil -} diff --git a/util/cpu/main_test.go b/util/cpu/main_test.go deleted file mode 100644 index cd776c6344056..0000000000000 --- a/util/cpu/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package cpu_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/cpuprofile/BUILD.bazel b/util/cpuprofile/BUILD.bazel deleted file mode 100644 index 79b6e341b479f..0000000000000 --- a/util/cpuprofile/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cpuprofile", - srcs = [ - "cpuprofile.go", - "pprof_api.go", - ], - importpath = "github.com/pingcap/tidb/util/cpuprofile", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//util", - "//util/logutil", - "@com_github_google_pprof//profile", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "cpuprofile_test", - timeout = "short", - srcs = ["cpuprofile_test.go"], - embed = [":cpuprofile"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/cpuprofile/testutil", - "@com_github_google_pprof//profile", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/cpuprofile/testutil/BUILD.bazel b/util/cpuprofile/testutil/BUILD.bazel deleted file mode 100644 index 1635fab206753..0000000000000 --- a/util/cpuprofile/testutil/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "testutil", - srcs = ["util.go"], - importpath = "github.com/pingcap/tidb/util/cpuprofile/testutil", - visibility = ["//visibility:public"], -) diff --git a/util/cteutil/BUILD.bazel b/util/cteutil/BUILD.bazel deleted file mode 100644 index 86a9174e45809..0000000000000 --- a/util/cteutil/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "cteutil", - srcs = ["storage.go"], - importpath = "github.com/pingcap/tidb/util/cteutil", - visibility = ["//visibility:public"], - deps = [ - "//types", - "//util/chunk", - "//util/disk", - "//util/memory", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "cteutil_test", - timeout = "short", - srcs = [ - "main_test.go", - "storage_test.go", - ], - embed = [":cteutil"], - flaky = True, - deps = [ - "//parser/mysql", - "//testkit/testsetup", - "//types", - "//util/chunk", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/cteutil/main_test.go b/util/cteutil/main_test.go deleted file mode 100644 index db43cd22437c1..0000000000000 --- a/util/cteutil/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cteutil - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/cteutil/storage.go b/util/cteutil/storage.go deleted file mode 100644 index dea6fd632e42b..0000000000000 --- a/util/cteutil/storage.go +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package cteutil - -import ( - "sync" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/memory" -) - -var _ Storage = &StorageRC{} - -// Storage is a temporary storage to store the intermidate data of CTE. -// -// Common usage as follows: -// -// storage.Lock() -// if !storage.Done() { -// fill all data into storage -// } -// storage.UnLock() -// read data from storage -type Storage interface { - // If is first called, will open underlying storage. Otherwise will add ref count by one. - OpenAndRef() error - - // Minus ref count by one, if ref count is zero, close underlying storage. - DerefAndClose() (err error) - - // SwapData swaps data of two storage. - // Other metainfo is not touched, such ref count/done flag etc. - SwapData(other Storage) error - - // Reopen reset storage and related info. - // So the status of Storage is like a new created one. - Reopen() error - - // Add chunk into underlying storage. - // Should return directly if chk is empty. - Add(chk *chunk.Chunk) error - - // Get Chunk by index. - GetChunk(chkIdx int) (*chunk.Chunk, error) - - // Get row by RowPtr. - GetRow(ptr chunk.RowPtr) (chunk.Row, error) - - // NumChunks return chunk number of the underlying storage. - NumChunks() int - - // NumRows return row number of the underlying storage. - NumRows() int - - // Storage is not thread-safe. - // By using Lock(), users can achieve the purpose of ensuring thread safety. - Lock() - Unlock() - - // Usually, Storage is filled first, then user can read it. - // User can check whether Storage is filled first, if not, they can fill it. - Done() bool - SetDone() - - // Store error message, so we can return directly. - Error() error - SetError(err error) - - // Readers use iter information to determine - // whether they need to read data from the beginning. - SetIter(iter int) - GetIter() int - - GetMemTracker() *memory.Tracker - GetDiskTracker() *disk.Tracker - ActionSpill() *chunk.SpillDiskAction -} - -// StorageRC implements Storage interface using RowContainer. -type StorageRC struct { - err error - rc *chunk.RowContainer - tp []*types.FieldType - refCnt int - chkSize int - iter int - mu sync.Mutex - done bool -} - -// NewStorageRowContainer create a new StorageRC. -func NewStorageRowContainer(tp []*types.FieldType, chkSize int) *StorageRC { - return &StorageRC{tp: tp, chkSize: chkSize} -} - -// OpenAndRef impls Storage OpenAndRef interface. -func (s *StorageRC) OpenAndRef() (err error) { - if !s.valid() { - s.rc = chunk.NewRowContainer(s.tp, s.chkSize) - s.refCnt = 1 - s.iter = 0 - } else { - s.refCnt++ - } - return nil -} - -// DerefAndClose impls Storage DerefAndClose interface. -func (s *StorageRC) DerefAndClose() (err error) { - if !s.valid() { - return errors.New("Storage not opend yet") - } - s.refCnt-- - if s.refCnt < 0 { - return errors.New("Storage ref count is less than zero") - } else if s.refCnt == 0 { - s.refCnt = -1 - s.done = false - s.err = nil - s.iter = 0 - if err = s.rc.Close(); err != nil { - return err - } - s.rc = nil - } - return nil -} - -// SwapData impls Storage Swap interface. -func (s *StorageRC) SwapData(other Storage) (err error) { - otherRC, ok := other.(*StorageRC) - if !ok { - return errors.New("cannot swap if underlying storages are different") - } - s.tp, otherRC.tp = otherRC.tp, s.tp - s.chkSize, otherRC.chkSize = otherRC.chkSize, s.chkSize - - s.rc, otherRC.rc = otherRC.rc, s.rc - return nil -} - -// Reopen impls Storage Reopen interface. -func (s *StorageRC) Reopen() (err error) { - if err = s.rc.Close(); err != nil { - return err - } - s.iter = 0 - s.done = false - s.err = nil - // Create a new RowContainer. - // Because some meta infos in old RowContainer are not resetted. - // Such as memTracker/actionSpill etc. So we just use a new one. - s.rc = chunk.NewRowContainer(s.tp, s.chkSize) - return nil -} - -// Add impls Storage Add interface. -func (s *StorageRC) Add(chk *chunk.Chunk) (err error) { - if !s.valid() { - return errors.New("Storage is not valid") - } - if chk.NumRows() == 0 { - return nil - } - return s.rc.Add(chk) -} - -// GetChunk impls Storage GetChunk interface. -func (s *StorageRC) GetChunk(chkIdx int) (*chunk.Chunk, error) { - if !s.valid() { - return nil, errors.New("Storage is not valid") - } - return s.rc.GetChunk(chkIdx) -} - -// GetRow impls Storage GetRow interface. -func (s *StorageRC) GetRow(ptr chunk.RowPtr) (chunk.Row, error) { - if !s.valid() { - return chunk.Row{}, errors.New("Storage is not valid") - } - return s.rc.GetRow(ptr) -} - -// NumChunks impls Storage NumChunks interface. -func (s *StorageRC) NumChunks() int { - return s.rc.NumChunks() -} - -// NumRows impls Storage NumRows interface. -func (s *StorageRC) NumRows() int { - return s.rc.NumRow() -} - -// Lock impls Storage Lock interface. -func (s *StorageRC) Lock() { - s.mu.Lock() -} - -// Unlock impls Storage Unlock interface. -func (s *StorageRC) Unlock() { - s.mu.Unlock() -} - -// Done impls Storage Done interface. -func (s *StorageRC) Done() bool { - return s.done -} - -// SetDone impls Storage SetDone interface. -func (s *StorageRC) SetDone() { - s.done = true -} - -// Error impls Storage Error interface. -func (s *StorageRC) Error() error { - return s.err -} - -// SetError impls Storage SetError interface. -func (s *StorageRC) SetError(err error) { - s.err = err -} - -// SetIter impls Storage SetIter interface. -func (s *StorageRC) SetIter(iter int) { - s.iter = iter -} - -// GetIter impls Storage GetIter interface. -func (s *StorageRC) GetIter() int { - return s.iter -} - -// GetMemTracker impls Storage GetMemTracker interface. -func (s *StorageRC) GetMemTracker() *memory.Tracker { - return s.rc.GetMemTracker() -} - -// GetDiskTracker impls Storage GetDiskTracker interface. -func (s *StorageRC) GetDiskTracker() *memory.Tracker { - return s.rc.GetDiskTracker() -} - -// ActionSpill impls Storage ActionSpill interface. -func (s *StorageRC) ActionSpill() *chunk.SpillDiskAction { - return s.rc.ActionSpill() -} - -// ActionSpillForTest is for test. -func (s *StorageRC) ActionSpillForTest() *chunk.SpillDiskAction { - return s.rc.ActionSpillForTest() -} - -func (s *StorageRC) valid() bool { - return s.refCnt > 0 && s.rc != nil -} diff --git a/util/dbterror/BUILD.bazel b/util/dbterror/BUILD.bazel deleted file mode 100644 index bea6593acf626..0000000000000 --- a/util/dbterror/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "dbterror", - srcs = [ - "ddl_terror.go", - "terror.go", - ], - importpath = "github.com/pingcap/tidb/util/dbterror", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//parser/mysql", - "//parser/terror", - ], -) - -go_test( - name = "dbterror_test", - timeout = "short", - srcs = [ - "main_test.go", - "terror_test.go", - ], - embed = [":dbterror"], - flaky = True, - deps = [ - "//errno", - "//testkit/testsetup", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/dbterror/exeerrors/BUILD.bazel b/util/dbterror/exeerrors/BUILD.bazel deleted file mode 100644 index d4c6025ecb1e2..0000000000000 --- a/util/dbterror/exeerrors/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "exeerrors", - srcs = ["errors.go"], - importpath = "github.com/pingcap/tidb/util/dbterror/exeerrors", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//parser/mysql", - "//util/dbterror", - ], -) diff --git a/util/dbterror/exeerrors/errors.go b/util/dbterror/exeerrors/errors.go deleted file mode 100644 index f03454a97feb9..0000000000000 --- a/util/dbterror/exeerrors/errors.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package exeerrors - -import ( - mysql "github.com/pingcap/tidb/errno" - parser_mysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbterror" -) - -// Error instances. -var ( - ErrGetStartTS = dbterror.ClassExecutor.NewStd(mysql.ErrGetStartTS) - ErrUnknownPlan = dbterror.ClassExecutor.NewStd(mysql.ErrUnknownPlan) - ErrPrepareMulti = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareMulti) - ErrPrepareDDL = dbterror.ClassExecutor.NewStd(mysql.ErrPrepareDDL) - ErrResultIsEmpty = dbterror.ClassExecutor.NewStd(mysql.ErrResultIsEmpty) - ErrBuildExecutor = dbterror.ClassExecutor.NewStd(mysql.ErrBuildExecutor) - ErrBatchInsertFail = dbterror.ClassExecutor.NewStd(mysql.ErrBatchInsertFail) - ErrUnsupportedPs = dbterror.ClassExecutor.NewStd(mysql.ErrUnsupportedPs) - ErrSubqueryMoreThan1Row = dbterror.ClassExecutor.NewStd(mysql.ErrSubqueryNo1Row) - ErrIllegalGrantForTable = dbterror.ClassExecutor.NewStd(mysql.ErrIllegalGrantForTable) - ErrColumnsNotMatched = dbterror.ClassExecutor.NewStd(mysql.ErrColumnNotMatched) - - ErrCantCreateUserWithGrant = dbterror.ClassExecutor.NewStd(mysql.ErrCantCreateUserWithGrant) - ErrPasswordNoMatch = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordNoMatch) - ErrCannotUser = dbterror.ClassExecutor.NewStd(mysql.ErrCannotUser) - ErrGrantRole = dbterror.ClassExecutor.NewStd(mysql.ErrGrantRole) - ErrPasswordFormat = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordFormat) - ErrCantChangeTxCharacteristics = dbterror.ClassExecutor.NewStd(mysql.ErrCantChangeTxCharacteristics) - ErrPsManyParam = dbterror.ClassExecutor.NewStd(mysql.ErrPsManyParam) - ErrAdminCheckTable = dbterror.ClassExecutor.NewStd(mysql.ErrAdminCheckTable) - ErrDBaccessDenied = dbterror.ClassExecutor.NewStd(mysql.ErrDBaccessDenied) - ErrTableaccessDenied = dbterror.ClassExecutor.NewStd(mysql.ErrTableaccessDenied) - ErrBadDB = dbterror.ClassExecutor.NewStd(mysql.ErrBadDB) - ErrWrongObject = dbterror.ClassExecutor.NewStd(mysql.ErrWrongObject) - ErrWrongUsage = dbterror.ClassExecutor.NewStd(mysql.ErrWrongUsage) - ErrRoleNotGranted = dbterror.ClassPrivilege.NewStd(mysql.ErrRoleNotGranted) - ErrDeadlock = dbterror.ClassExecutor.NewStd(mysql.ErrLockDeadlock) - ErrQueryInterrupted = dbterror.ClassExecutor.NewStd(mysql.ErrQueryInterrupted) - ErrMaxExecTimeExceeded = dbterror.ClassExecutor.NewStd(mysql.ErrMaxExecTimeExceeded) - ErrResourceGroupQueryRunawayInterrupted = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupQueryRunawayInterrupted) - ErrResourceGroupQueryRunawayQuarantine = dbterror.ClassExecutor.NewStd(mysql.ErrResourceGroupQueryRunawayQuarantine) - ErrDynamicPrivilegeNotRegistered = dbterror.ClassExecutor.NewStd(mysql.ErrDynamicPrivilegeNotRegistered) - ErrIllegalPrivilegeLevel = dbterror.ClassExecutor.NewStd(mysql.ErrIllegalPrivilegeLevel) - ErrInvalidSplitRegionRanges = dbterror.ClassExecutor.NewStd(mysql.ErrInvalidSplitRegionRanges) - ErrViewInvalid = dbterror.ClassExecutor.NewStd(mysql.ErrViewInvalid) - ErrInstanceScope = dbterror.ClassExecutor.NewStd(mysql.ErrInstanceScope) - ErrSettingNoopVariable = dbterror.ClassExecutor.NewStd(mysql.ErrSettingNoopVariable) - ErrLazyUniquenessCheckFailure = dbterror.ClassExecutor.NewStd(mysql.ErrLazyUniquenessCheckFailure) - - ErrBRIEBackupFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEBackupFailed) - ErrBRIERestoreFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIERestoreFailed) - ErrBRIEImportFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEImportFailed) - ErrBRIEExportFailed = dbterror.ClassExecutor.NewStd(mysql.ErrBRIEExportFailed) - ErrBRJobNotFound = dbterror.ClassExecutor.NewStd(mysql.ErrBRJobNotFound) - ErrCTEMaxRecursionDepth = dbterror.ClassExecutor.NewStd(mysql.ErrCTEMaxRecursionDepth) - ErrNotSupportedWithSem = dbterror.ClassOptimizer.NewStd(mysql.ErrNotSupportedWithSem) - ErrPluginIsNotLoaded = dbterror.ClassExecutor.NewStd(mysql.ErrPluginIsNotLoaded) - ErrSetPasswordAuthPlugin = dbterror.ClassExecutor.NewStd(mysql.ErrSetPasswordAuthPlugin) - ErrFuncNotEnabled = dbterror.ClassExecutor.NewStdErr(mysql.ErrNotSupportedYet, parser_mysql.Message("%-.32s is not supported. To enable this experimental feature, set '%-.32s' in the configuration file.", nil)) - ErrSavepointNotExists = dbterror.ClassExecutor.NewStd(mysql.ErrSpDoesNotExist) - ErrForeignKeyCascadeDepthExceeded = dbterror.ClassExecutor.NewStd(mysql.ErrForeignKeyCascadeDepthExceeded) - ErrPasswordExpireAnonymousUser = dbterror.ClassExecutor.NewStd(mysql.ErrPasswordExpireAnonymousUser) - ErrMustChangePassword = dbterror.ClassExecutor.NewStd(mysql.ErrMustChangePassword) - - ErrWrongStringLength = dbterror.ClassDDL.NewStd(mysql.ErrWrongStringLength) - ErrUnsupportedFlashbackTmpTable = dbterror.ClassDDL.NewStdErr(mysql.ErrUnsupportedDDLOperation, parser_mysql.Message("Recover/flashback table is not supported on temporary tables", nil)) - ErrTruncateWrongInsertValue = dbterror.ClassTable.NewStdErr(mysql.ErrTruncatedWrongValue, parser_mysql.Message("Incorrect %-.32s value: '%-.128s' for column '%.192s' at row %d", nil)) - ErrExistsInHistoryPassword = dbterror.ClassExecutor.NewStd(mysql.ErrExistsInHistoryPassword) - - ErrWarnTooFewRecords = dbterror.ClassExecutor.NewStd(mysql.ErrWarnTooFewRecords) - ErrWarnTooManyRecords = dbterror.ClassExecutor.NewStd(mysql.ErrWarnTooManyRecords) - ErrLoadDataFromServerDisk = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataFromServerDisk) - ErrLoadParquetFromLocal = dbterror.ClassExecutor.NewStd(mysql.ErrLoadParquetFromLocal) - ErrLoadDataEmptyPath = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataEmptyPath) - ErrLoadDataUnsupportedFormat = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataUnsupportedFormat) - ErrLoadDataInvalidURI = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataInvalidURI) - ErrLoadDataCantAccess = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataCantAccess) - ErrLoadDataCantRead = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataCantRead) - ErrLoadDataWrongFormatConfig = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataWrongFormatConfig) - ErrUnknownOption = dbterror.ClassExecutor.NewStd(mysql.ErrUnknownOption) - ErrInvalidOptionVal = dbterror.ClassExecutor.NewStd(mysql.ErrInvalidOptionVal) - ErrDuplicateOption = dbterror.ClassExecutor.NewStd(mysql.ErrDuplicateOption) - ErrLoadDataUnsupportedOption = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataUnsupportedOption) - ErrLoadDataJobNotFound = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataJobNotFound) - ErrLoadDataInvalidOperation = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataInvalidOperation) - ErrLoadDataLocalUnsupportedOption = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataLocalUnsupportedOption) - ErrLoadDataPreCheckFailed = dbterror.ClassExecutor.NewStd(mysql.ErrLoadDataPreCheckFailed) -) diff --git a/util/dbterror/main_test.go b/util/dbterror/main_test.go deleted file mode 100644 index 43f8dab796378..0000000000000 --- a/util/dbterror/main_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package dbterror - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/dbterror/terror.go b/util/dbterror/terror.go deleted file mode 100644 index 47d523e80db7d..0000000000000 --- a/util/dbterror/terror.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package dbterror - -import ( - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/parser/terror" -) - -// ErrClass represents a class of errors. -type ErrClass struct{ terror.ErrClass } - -// Error classes. -var ( - ClassAutoid = ErrClass{terror.ClassAutoid} - ClassDDL = ErrClass{terror.ClassDDL} - ClassDomain = ErrClass{terror.ClassDomain} - ClassExecutor = ErrClass{terror.ClassExecutor} - ClassExpression = ErrClass{terror.ClassExpression} - ClassAdmin = ErrClass{terror.ClassAdmin} - ClassKV = ErrClass{terror.ClassKV} - ClassMeta = ErrClass{terror.ClassMeta} - ClassOptimizer = ErrClass{terror.ClassOptimizer} - ClassPrivilege = ErrClass{terror.ClassPrivilege} - ClassSchema = ErrClass{terror.ClassSchema} - ClassServer = ErrClass{terror.ClassServer} - ClassStructure = ErrClass{terror.ClassStructure} - ClassVariable = ErrClass{terror.ClassVariable} - ClassXEval = ErrClass{terror.ClassXEval} - ClassTable = ErrClass{terror.ClassTable} - ClassTypes = ErrClass{terror.ClassTypes} - ClassJSON = ErrClass{terror.ClassJSON} - ClassTiKV = ErrClass{terror.ClassTiKV} - ClassSession = ErrClass{terror.ClassSession} - ClassPlugin = ErrClass{terror.ClassPlugin} - ClassUtil = ErrClass{terror.ClassUtil} -) - -// NewStd calls New using the standard message for the error code -// Attention: -// this method is not goroutine-safe and -// usually be used in global variable initializer -func (ec ErrClass) NewStd(code terror.ErrCode) *terror.Error { - return ec.NewStdErr(code, errno.MySQLErrName[uint16(code)]) -} diff --git a/util/dbutil/BUILD.bazel b/util/dbutil/BUILD.bazel deleted file mode 100644 index 827896678e94c..0000000000000 --- a/util/dbutil/BUILD.bazel +++ /dev/null @@ -1,65 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "dbutil", - srcs = [ - "common.go", - "index.go", - "interface.go", - "query.go", - "retry.go", - "table.go", - "types.go", - "variable.go", - ], - importpath = "github.com/pingcap/tidb/util/dbutil", - visibility = ["//visibility:public"], - deps = [ - "//ddl", - "//errno", - "//infoschema", - "//parser", - "//parser/ast", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//sessionctx/stmtctx", - "//types", - "//types/parser_driver", - "//util", - "//util/collate", - "//util/dbterror", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "dbutil_test", - timeout = "short", - srcs = [ - "common_test.go", - "index_test.go", - "retry_test.go", - "table_test.go", - "variable_test.go", - ], - embed = [":dbutil"], - flaky = True, - deps = [ - "//errno", - "//infoschema", - "//parser", - "//parser/model", - "//parser/mysql", - "//types", - "//util/schemacmp", - "@com_github_data_dog_go_sqlmock//:go-sqlmock", - "@com_github_go_sql_driver_mysql//:mysql", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/dbutil/common.go b/util/dbutil/common.go deleted file mode 100644 index 44199ac11e6d3..0000000000000 --- a/util/dbutil/common.go +++ /dev/null @@ -1,891 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "context" - "database/sql" - "encoding/json" - "fmt" - "net" - "os" - "strconv" - "strings" - "time" - - "github.com/go-sql-driver/mysql" - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - tmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/dbterror" - "go.uber.org/zap" -) - -const ( - // DefaultRetryTime is the default retry time to execute sql - DefaultRetryTime = 10 - - // DefaultTimeout is the default timeout for execute sql - DefaultTimeout time.Duration = 10 * time.Second - - // SlowLogThreshold defines the duration to log debug log of sql when exec time greater than - SlowLogThreshold = 200 * time.Millisecond - - // DefaultDeleteRowsNum is the default rows num for delete one time - DefaultDeleteRowsNum int64 = 100000 -) - -var ( - // ErrVersionNotFound means can't get the database's version - ErrVersionNotFound = errors.New("can't get the database's version") - - // ErrNoData means no data in table - ErrNoData = errors.New("no data found in table") -) - -// DBConfig is database configuration. -type DBConfig struct { - Host string `toml:"host" json:"host"` - User string `toml:"user" json:"user"` - Password string `toml:"password" json:"-"` - Schema string `toml:"schema" json:"schema"` - Snapshot string `toml:"snapshot" json:"snapshot"` - Port int `toml:"port" json:"port"` -} - -// String returns native format of database configuration -func (c *DBConfig) String() string { - cfg, err := json.Marshal(c) - if err != nil { - return "" - } - return string(cfg) -} - -// GetDBConfigFromEnv returns DBConfig from environment -func GetDBConfigFromEnv(schema string) DBConfig { - host := os.Getenv("MYSQL_HOST") - if host == "" { - host = "127.0.0.1" - } - port, _ := strconv.Atoi(os.Getenv("MYSQL_PORT")) - if port == 0 { - port = 3306 - } - user := os.Getenv("MYSQL_USER") - if user == "" { - user = "root" - } - pswd := os.Getenv("MYSQL_PSWD") - - return DBConfig{ - Host: host, - Port: port, - User: user, - Password: pswd, - Schema: schema, - } -} - -// OpenDB opens a mysql connection FD -func OpenDB(cfg DBConfig, vars map[string]string) (*sql.DB, error) { - driverCfg := mysql.NewConfig() - driverCfg.Params = make(map[string]string) - driverCfg.User = cfg.User - driverCfg.Passwd = cfg.Password - driverCfg.Net = "tcp" - driverCfg.Addr = net.JoinHostPort(cfg.Host, strconv.Itoa(cfg.Port)) - driverCfg.Params["charset"] = "utf8mb4" - - if len(cfg.Snapshot) != 0 { - log.Info("create connection with snapshot", zap.String("snapshot", cfg.Snapshot)) - driverCfg.Params["tidb_snapshot"] = cfg.Snapshot - } - - for key, val := range vars { - // key='val'. add single quote for better compatibility. - driverCfg.Params[key] = fmt.Sprintf("'%s'", val) - } - - c, err := mysql.NewConnector(driverCfg) - if err != nil { - return nil, errors.Trace(err) - } - db := sql.OpenDB(c) - err = db.Ping() - return db, errors.Trace(err) -} - -// CloseDB closes the mysql fd -func CloseDB(db *sql.DB) error { - if db == nil { - return nil - } - - return errors.Trace(db.Close()) -} - -// GetCreateTableSQL returns the create table statement. -func GetCreateTableSQL(ctx context.Context, db QueryExecutor, schemaName string, tableName string) (string, error) { - /* - show create table example result: - mysql> SHOW CREATE TABLE `test`.`itest`; - +-------+--------------------------------------------------------------------+ - | Table | Create Table | - +-------+--------------------------------------------------------------------+ - | itest | CREATE TABLE `itest` ( - `id` int(11) DEFAULT NULL, - `name` varchar(24) DEFAULT NULL - ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin | - +-------+--------------------------------------------------------------------+ - */ - query := fmt.Sprintf("SHOW CREATE TABLE %s", TableName(schemaName, tableName)) - - var tbl, createTable sql.NullString - err := db.QueryRowContext(ctx, query).Scan(&tbl, &createTable) - if err != nil { - return "", errors.Trace(err) - } - if !tbl.Valid || !createTable.Valid { - return "", errors.NotFoundf("table %s", tableName) - } - - return createTable.String, nil -} - -// GetRowCount returns row count of the table. -// if not specify where condition, return total row count of the table. -func GetRowCount(ctx context.Context, db QueryExecutor, schemaName string, tableName string, where string, args []interface{}) (int64, error) { - /* - select count example result: - mysql> SELECT count(1) cnt from `test`.`itest` where id > 0; - +------+ - | cnt | - +------+ - | 100 | - +------+ - */ - - query := fmt.Sprintf("SELECT COUNT(1) cnt FROM %s", TableName(schemaName, tableName)) - if len(where) > 0 { - query += fmt.Sprintf(" WHERE %s", where) - } - log.Debug("get row count", zap.String("sql", query), zap.Reflect("args", args)) - - var cnt sql.NullInt64 - err := db.QueryRowContext(ctx, query, args...).Scan(&cnt) - if err != nil { - return 0, errors.Trace(err) - } - if !cnt.Valid { - return 0, errors.NotFoundf("table `%s`.`%s`", schemaName, tableName) - } - - return cnt.Int64, nil -} - -// GetRandomValues returns some random value. Tips: limitArgs is the value in limitRange. -func GetRandomValues(ctx context.Context, db QueryExecutor, schemaName, table, column string, num int, limitRange string, limitArgs []interface{}, collation string) ([]string, error) { - /* - example: - mysql> SELECT `id` FROM (SELECT `id`, rand() rand_value FROM `test`.`test` WHERE `id` COLLATE "latin1_bin" > 0 AND `id` COLLATE "latin1_bin" < 100 ORDER BY rand_value LIMIT 5) rand_tmp ORDER BY `id` COLLATE "latin1_bin"; - +------+ - | id | - +------+ - | 1 | - | 2 | - | 3 | - +------+ - */ - - if limitRange == "" { - limitRange = "TRUE" - } - - if collation != "" { - collation = fmt.Sprintf(" COLLATE \"%s\"", collation) - } - - query := fmt.Sprintf("SELECT %[1]s FROM (SELECT %[1]s, rand() rand_value FROM %[2]s WHERE %[3]s ORDER BY rand_value LIMIT %[4]d)rand_tmp ORDER BY %[1]s%[5]s", - ColumnName(column), TableName(schemaName, table), limitRange, num, collation) - log.Debug("get random values", zap.String("sql", query), zap.Reflect("args", limitArgs)) - - rows, err := db.QueryContext(ctx, query, limitArgs...) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - randomValue := make([]string, 0, num) - for rows.Next() { - var value sql.NullString - err = rows.Scan(&value) - if err != nil { - return nil, errors.Trace(err) - } - if value.Valid { - randomValue = append(randomValue, value.String) - } - } - - return randomValue, errors.Trace(rows.Err()) -} - -// GetMinMaxValue return min and max value of given column by specified limitRange condition. -func GetMinMaxValue(ctx context.Context, db QueryExecutor, schema, table, column string, limitRange string, limitArgs []interface{}, collation string) (minStr string, maxStr string, err error) { - /* - example: - mysql> SELECT MIN(`id`) as MIN, MAX(`id`) as MAX FROM `test`.`testa` WHERE id > 0 AND id < 10; - +------+------+ - | MIN | MAX | - +------+------+ - | 1 | 2 | - +------+------+ - */ - - if limitRange == "" { - limitRange = "TRUE" - } - - if collation != "" { - collation = fmt.Sprintf(" COLLATE \"%s\"", collation) - } - - query := fmt.Sprintf("SELECT /*!40001 SQL_NO_CACHE */ MIN(%s%s) as MIN, MAX(%s%s) as MAX FROM %s WHERE %s", - ColumnName(column), collation, ColumnName(column), collation, TableName(schema, table), limitRange) - log.Debug("GetMinMaxValue", zap.String("sql", query), zap.Reflect("args", limitArgs)) - - var min, max sql.NullString - rows, err := db.QueryContext(ctx, query, limitArgs...) - if err != nil { - return "", "", errors.Trace(err) - } - defer rows.Close() - - for rows.Next() { - err = rows.Scan(&min, &max) - if err != nil { - return "", "", errors.Trace(err) - } - } - - if !min.Valid || !max.Valid { - // don't have any data - return "", "", ErrNoData - } - - return min.String, max.String, errors.Trace(rows.Err()) -} - -// GetTimeZoneOffset is to get offset of timezone. -func GetTimeZoneOffset(ctx context.Context, db QueryExecutor) (time.Duration, error) { - var timeStr string - err := db.QueryRowContext(ctx, "SELECT cast(TIMEDIFF(NOW(6), UTC_TIMESTAMP(6)) as time);").Scan(&timeStr) - if err != nil { - return 0, errors.Trace(err) - } - factor := time.Duration(1) - if timeStr[0] == '-' || timeStr[0] == '+' { - if timeStr[0] == '-' { - factor *= -1 - } - timeStr = timeStr[1:] - } - t, err := time.Parse(time.TimeOnly, timeStr) - if err != nil { - return 0, errors.Trace(err) - } - - if t.IsZero() { - return 0, nil - } - - hour, minute, second := t.Clock() - //nolint:durationcheck - return time.Duration(hour*3600+minute*60+second) * time.Second * factor, nil -} - -// FormatTimeZoneOffset is to format offset of timezone. -func FormatTimeZoneOffset(offset time.Duration) string { - prefix := "+" - if offset < 0 { - prefix = "-" - offset *= -1 - } - hours := offset / time.Hour - minutes := (offset % time.Hour) / time.Minute - - return fmt.Sprintf("%s%02d:%02d", prefix, hours, minutes) -} - -func queryTables(ctx context.Context, db QueryExecutor, q string) (tables []string, err error) { - log.Debug("query tables", zap.String("query", q)) - rows, err := db.QueryContext(ctx, q) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - tables = make([]string, 0, 8) - for rows.Next() { - var table, tType sql.NullString - err = rows.Scan(&table, &tType) - if err != nil { - return nil, errors.Trace(err) - } - - if !table.Valid || !tType.Valid { - continue - } - - tables = append(tables, table.String) - } - - return tables, errors.Trace(rows.Err()) -} - -// GetTables returns name of all tables in the specified schema -func GetTables(ctx context.Context, db QueryExecutor, schemaName string) (tables []string, err error) { - /* - show tables without view: https://dev.mysql.com/doc/refman/5.7/en/show-tables.html - - example: - mysql> show full tables in test where Table_Type != 'VIEW'; - +----------------+------------+ - | Tables_in_test | Table_type | - +----------------+------------+ - | NTEST | BASE TABLE | - +----------------+------------+ - */ - query := fmt.Sprintf("SHOW FULL TABLES IN `%s` WHERE Table_Type != 'VIEW';", escapeName(schemaName)) - return queryTables(ctx, db, query) -} - -// GetViews returns names of all views in the specified schema -func GetViews(ctx context.Context, db QueryExecutor, schemaName string) (tables []string, err error) { - query := fmt.Sprintf("SHOW FULL TABLES IN `%s` WHERE Table_Type = 'VIEW';", escapeName(schemaName)) - return queryTables(ctx, db, query) -} - -// GetSchemas returns name of all schemas -func GetSchemas(ctx context.Context, db QueryExecutor) ([]string, error) { - query := "SHOW DATABASES" - rows, err := db.QueryContext(ctx, query) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - // show an example. - /* - mysql> SHOW DATABASES; - +--------------------+ - | Database | - +--------------------+ - | information_schema | - | mysql | - | performance_schema | - | sys | - | test_db | - +--------------------+ - */ - schemas := make([]string, 0, 10) - for rows.Next() { - var schema string - err = rows.Scan(&schema) - if err != nil { - return nil, errors.Trace(err) - } - schemas = append(schemas, schema) - } - return schemas, errors.Trace(rows.Err()) -} - -// GetCRC32Checksum returns checksum code of some data by given condition -func GetCRC32Checksum(ctx context.Context, db QueryExecutor, schemaName, tableName string, tbInfo *model.TableInfo, limitRange string, args []interface{}) (int64, error) { - /* - calculate CRC32 checksum example: - mysql> SELECT BIT_XOR(CAST(CRC32(CONCAT_WS(',', id, name, age, CONCAT(ISNULL(id), ISNULL(name), ISNULL(age))))AS UNSIGNED)) AS checksum FROM test.test WHERE id > 0 AND id < 10; - +------------+ - | checksum | - +------------+ - | 1466098199 | - +------------+ - */ - columnNames := make([]string, 0, len(tbInfo.Columns)) - columnIsNull := make([]string, 0, len(tbInfo.Columns)) - for _, col := range tbInfo.Columns { - columnNames = append(columnNames, ColumnName(col.Name.O)) - columnIsNull = append(columnIsNull, fmt.Sprintf("ISNULL(%s)", ColumnName(col.Name.O))) - } - - query := fmt.Sprintf("SELECT BIT_XOR(CAST(CRC32(CONCAT_WS(',', %s, CONCAT(%s)))AS UNSIGNED)) AS checksum FROM %s WHERE %s;", - strings.Join(columnNames, ", "), strings.Join(columnIsNull, ", "), TableName(schemaName, tableName), limitRange) - log.Debug("checksum", zap.String("sql", query), zap.Reflect("args", args)) - - var checksum sql.NullInt64 - err := db.QueryRowContext(ctx, query, args...).Scan(&checksum) - if err != nil { - return -1, errors.Trace(err) - } - if !checksum.Valid { - // if don't have any data, the checksum will be `NULL` - log.Warn("get empty checksum", zap.String("sql", query), zap.Reflect("args", args)) - return 0, nil - } - - return checksum.Int64, nil -} - -// Bucket saves the bucket information from TiDB. -type Bucket struct { - LowerBound string - UpperBound string - Count int64 -} - -// GetBucketsInfo SHOW STATS_BUCKETS in TiDB. -func GetBucketsInfo(ctx context.Context, db QueryExecutor, schema, table string, tableInfo *model.TableInfo) (map[string][]Bucket, error) { - /* - example in tidb: - mysql> SHOW STATS_BUCKETS WHERE db_name= "test" AND table_name="testa"; - +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ - | Db_name | Table_name | Partition_name | Column_name | Is_index | Bucket_id | Count | Repeats | Lower_Bound | Upper_Bound | - +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ - | test | testa | | PRIMARY | 1 | 0 | 64 | 1 | 1846693550524203008 | 1846838686059069440 | - | test | testa | | PRIMARY | 1 | 1 | 128 | 1 | 1846840885082324992 | 1847056389361369088 | - +---------+------------+----------------+-------------+----------+-----------+-------+---------+---------------------+---------------------+ - */ - buckets := make(map[string][]Bucket) - query := "SHOW STATS_BUCKETS WHERE db_name= ? AND table_name= ?;" - log.Debug("GetBucketsInfo", zap.String("sql", query), zap.String("schema", schema), zap.String("table", table)) - - rows, err := db.QueryContext(ctx, query, schema, table) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - cols, err := rows.Columns() - if err != nil { - return nil, errors.Trace(err) - } - - for rows.Next() { - var dbName, tableName, partitionName, columnName, lowerBound, upperBound sql.NullString - var isIndex, bucketID, count, repeats, ndv sql.NullInt64 - - // add partiton_name in new version - switch len(cols) { - case 9: - err = rows.Scan(&dbName, &tableName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound) - case 10: - err = rows.Scan(&dbName, &tableName, &partitionName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound) - case 11: - err = rows.Scan(&dbName, &tableName, &partitionName, &columnName, &isIndex, &bucketID, &count, &repeats, &lowerBound, &upperBound, &ndv) - default: - return nil, errors.New("Unknown struct for buckets info") - } - if err != nil { - return nil, errors.Trace(err) - } - - if _, ok := buckets[columnName.String]; !ok { - buckets[columnName.String] = make([]Bucket, 0, 100) - } - buckets[columnName.String] = append(buckets[columnName.String], Bucket{ - Count: count.Int64, - LowerBound: lowerBound.String, - UpperBound: upperBound.String, - }) - } - - // when primary key is int type, the columnName will be column's name, not `PRIMARY`, check and transform here. - indices := FindAllIndex(tableInfo) - for _, index := range indices { - if index.Name.O != "PRIMARY" { - continue - } - _, ok := buckets[index.Name.O] - if !ok && len(index.Columns) == 1 { - if _, ok := buckets[index.Columns[0].Name.O]; !ok { - return nil, errors.NotFoundf("primary key on %s in buckets info", index.Columns[0].Name.O) - } - buckets[index.Name.O] = buckets[index.Columns[0].Name.O] - delete(buckets, index.Columns[0].Name.O) - } - } - - return buckets, errors.Trace(rows.Err()) -} - -// AnalyzeValuesFromBuckets analyze upperBound or lowerBound to string for each column. -// upperBound and lowerBound are looks like '(123, abc)' for multiple fields, or '123' for one field. -func AnalyzeValuesFromBuckets(valueString string, cols []*model.ColumnInfo) ([]string, error) { - // FIXME: maybe some values contains '(', ')' or ', ' - vStr := strings.Trim(valueString, "()") - values := strings.Split(vStr, ", ") - if len(values) != len(cols) { - return nil, errors.Errorf("analyze value %s failed", valueString) - } - - for i, col := range cols { - if IsTimeTypeAndNeedDecode(col.GetType()) { - // check if values[i] is already a time string - sc := stmtctx.NewStmtCtxWithTimeZone(time.UTC) - _, err := types.ParseTime(sc, values[i], col.GetType(), types.MinFsp, nil) - if err == nil { - continue - } - - value, err := DecodeTimeInBucket(values[i]) - if err != nil { - log.Error("analyze values from buckets", zap.String("column", col.Name.O), zap.String("value", values[i]), zap.Error(err)) - return nil, errors.Trace(err) - } - - values[i] = value - } - } - - return values, nil -} - -// DecodeTimeInBucket decodes Time from a packed uint64 value. -func DecodeTimeInBucket(packedStr string) (string, error) { - packed, err := strconv.ParseUint(packedStr, 10, 64) - if err != nil { - return "", err - } - - if packed == 0 { - return "", nil - } - - t := new(types.Time) - err = t.FromPackedUint(packed) - if err != nil { - return "", err - } - - return t.String(), nil -} - -// GetTidbLatestTSO returns tidb's current TSO. -func GetTidbLatestTSO(ctx context.Context, db QueryExecutor) (int64, error) { - /* - example in tidb: - mysql> SHOW MASTER STATUS; - +-------------+--------------------+--------------+------------------+-------------------+ - | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | - +-------------+--------------------+--------------+------------------+-------------------+ - | tidb-binlog | 400718757701615617 | | | | - +-------------+--------------------+--------------+------------------+-------------------+ - */ - rows, err := db.QueryContext(ctx, "SHOW MASTER STATUS") - if err != nil { - return 0, errors.Trace(err) - } - defer rows.Close() - - if rows.Next() { - fields, err1 := ScanRow(rows) - if err1 != nil { - return 0, errors.Trace(err1) - } - - ts, err1 := strconv.ParseInt(string(fields["Position"].Data), 10, 64) - if err1 != nil { - return 0, errors.Trace(err1) - } - return ts, nil - } - return 0, errors.New("get secondary cluster's ts failed") -} - -// GetDBVersion returns the database's version -func GetDBVersion(ctx context.Context, db QueryExecutor) (string, error) { - /* - example in TiDB: - mysql> select version(); - +--------------------------------------+ - | version() | - +--------------------------------------+ - | 5.7.10-TiDB-v2.1.0-beta-173-g7e48ab1 | - +--------------------------------------+ - - example in MySQL: - mysql> select version(); - +-----------+ - | version() | - +-----------+ - | 5.7.21 | - +-----------+ - */ - query := "SELECT version()" - result, err := db.QueryContext(ctx, query) //nolint:rowserrcheck - if err != nil { - return "", errors.Trace(err) - } - defer result.Close() - - var version sql.NullString - if result.Next() { - err := result.Scan(&version) - if err != nil { - return "", errors.Trace(err) - } - } - - if version.Valid { - return version.String, nil - } - - return "", ErrVersionNotFound -} - -// GetSessionVariable gets server's session variable, although argument is QueryExecutor, (session) system variables may be -// set through DSN -func GetSessionVariable(ctx context.Context, db QueryExecutor, variable string) (value string, err error) { - query := fmt.Sprintf("SHOW VARIABLES LIKE '%s'", variable) - rows, err := db.QueryContext(ctx, query) - - if err != nil { - return "", errors.Trace(err) - } - defer rows.Close() - - // Show an example. - /* - mysql> SHOW VARIABLES LIKE "binlog_format"; - +---------------+-------+ - | Variable_name | Value | - +---------------+-------+ - | binlog_format | ROW | - +---------------+-------+ - */ - - for rows.Next() { - if err = rows.Scan(&variable, &value); err != nil { - return "", errors.Trace(err) - } - } - - if err := rows.Err(); err != nil { - return "", errors.Trace(err) - } - - return value, nil -} - -// GetSQLMode returns sql_mode. -func GetSQLMode(ctx context.Context, db QueryExecutor) (tmysql.SQLMode, error) { - sqlMode, err := GetSessionVariable(ctx, db, "sql_mode") - if err != nil { - return tmysql.ModeNone, err - } - - mode, err := tmysql.GetSQLMode(sqlMode) - return mode, errors.Trace(err) -} - -// IsTiDB returns true if this database is tidb -func IsTiDB(ctx context.Context, db QueryExecutor) (bool, error) { - version, err := GetDBVersion(ctx, db) - if err != nil { - log.Error("get database's version failed", zap.Error(err)) - return false, errors.Trace(err) - } - - return strings.Contains(strings.ToLower(version), "tidb"), nil -} - -// TableName returns `schema`.`table` -func TableName(schema, table string) string { - return fmt.Sprintf("`%s`.`%s`", escapeName(schema), escapeName(table)) -} - -// ColumnName returns `column` -func ColumnName(column string) string { - return fmt.Sprintf("`%s`", escapeName(column)) -} - -func escapeName(name string) string { - return strings.ReplaceAll(name, "`", "``") -} - -// ReplacePlaceholder will use args to replace '?', used for log. -// tips: make sure the num of "?" is same with len(args) -func ReplacePlaceholder(str string, args []string) string { - /* - for example: - str is "a > ? AND a < ?", args is {'1', '2'}, - this function will return "a > '1' AND a < '2'" - */ - newStr := strings.ReplaceAll(str, "?", "'%s'") - return fmt.Sprintf(newStr, util.StringsToInterfaces(args)...) -} - -// ExecSQLWithRetry executes sql with retry -func ExecSQLWithRetry(ctx context.Context, db DBExecutor, sql string, args ...interface{}) (err error) { - for i := 0; i < DefaultRetryTime; i++ { - startTime := time.Now() - _, err = db.ExecContext(ctx, sql, args...) - takeDuration := time.Since(startTime) - if takeDuration > SlowLogThreshold { - log.Debug("exec sql slow", zap.String("sql", sql), zap.Reflect("args", args), zap.Duration("take", takeDuration)) - } - if err == nil { - return nil - } - - if ignoreError(err) { - log.Warn("ignore execute sql error", zap.Error(err)) - return nil - } - - if !IsRetryableError(err) { - return errors.Trace(err) - } - - log.Warn("exe sql failed, will try again", zap.String("sql", sql), zap.Reflect("args", args), zap.Error(err)) - - if i == DefaultRetryTime-1 { - break - } - - select { - case <-ctx.Done(): - return errors.Trace(ctx.Err()) - case <-time.After(10 * time.Millisecond): - } - } - - return errors.Trace(err) -} - -// ExecuteSQLs executes some sqls in one transaction -func ExecuteSQLs(ctx context.Context, db DBExecutor, sqls []string, args [][]interface{}) error { - txn, err := db.BeginTx(ctx, nil) - if err != nil { - log.Error("exec sqls begin", zap.Error(err)) - return errors.Trace(err) - } - - for i := range sqls { - startTime := time.Now() - - _, err = txn.ExecContext(ctx, sqls[i], args[i]...) - if err != nil { - log.Error("exec sql", zap.String("sql", sqls[i]), zap.Reflect("args", args[i]), zap.Error(err)) - rerr := txn.Rollback() - if rerr != nil { - log.Error("rollback", zap.Error(err)) - } - return errors.Trace(err) - } - - takeDuration := time.Since(startTime) - if takeDuration > SlowLogThreshold { - log.Debug("exec sql slow", zap.String("sql", sqls[i]), zap.Reflect("args", args[i]), zap.Duration("take", takeDuration)) - } - } - - err = txn.Commit() - if err != nil { - log.Error("exec sqls commit", zap.Error(err)) - return errors.Trace(err) - } - - return nil -} - -func ignoreError(err error) bool { - // TODO: now only ignore some ddl error, add some dml error later - return ignoreDDLError(err) -} - -func ignoreDDLError(err error) bool { - err = errors.Cause(err) - mysqlErr, ok := err.(*mysql.MySQLError) - if !ok { - return false - } - - errCode := errors.ErrCode(mysqlErr.Number) - switch errCode { - case infoschema.ErrDatabaseExists.Code(), infoschema.ErrDatabaseDropExists.Code(), - infoschema.ErrTableExists.Code(), infoschema.ErrTableDropExists.Code(), - infoschema.ErrColumnExists.Code(), infoschema.ErrIndexExists.Code(): - return true - case dbterror.ErrDupKeyName.Code(): - return true - default: - return false - } -} - -// DeleteRows delete rows in several times. Only can delete less than 300,000 one time in TiDB. -func DeleteRows(ctx context.Context, db DBExecutor, schemaName string, tableName string, where string, args []interface{}) error { - deleteSQL := fmt.Sprintf("DELETE FROM %s WHERE %s limit %d;", TableName(schemaName, tableName), where, DefaultDeleteRowsNum) - result, err := db.ExecContext(ctx, deleteSQL, args...) - if err != nil { - return errors.Trace(err) - } - - rows, err := result.RowsAffected() - if err != nil { - return errors.Trace(err) - } - - if rows < DefaultDeleteRowsNum { - return nil - } - - return DeleteRows(ctx, db, schemaName, tableName, where, args) -} - -// getParser gets parser according to sql mode -func getParser(sqlModeStr string) (*parser.Parser, error) { - if len(sqlModeStr) == 0 { - return parser.New(), nil - } - - sqlMode, err := tmysql.GetSQLMode(tmysql.FormatSQLModeStr(sqlModeStr)) - if err != nil { - return nil, errors.Annotatef(err, "invalid sql mode %s", sqlModeStr) - } - parser2 := parser.New() - parser2.SetSQLMode(sqlMode) - return parser2, nil -} - -// GetParserForDB discovers ANSI_QUOTES in db's session variables and returns a proper parser -func GetParserForDB(ctx context.Context, db QueryExecutor) (*parser.Parser, error) { - mode, err := GetSQLMode(ctx, db) - if err != nil { - return nil, err - } - - parser2 := parser.New() - parser2.SetSQLMode(mode) - return parser2, nil -} diff --git a/util/dbutil/common_test.go b/util/dbutil/common_test.go deleted file mode 100644 index 3e2079e448912..0000000000000 --- a/util/dbutil/common_test.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "context" - "testing" - "time" - - sqlmock "github.com/DATA-DOG/go-sqlmock" - "github.com/go-sql-driver/mysql" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/infoschema" - "github.com/pingcap/tidb/parser/model" - pmysql "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/stretchr/testify/require" -) - -func TestReplacePlaceholder(t *testing.T) { - testCases := []struct { - originStr string - args []string - expectStr string - }{ - { - "a > ? AND a < ?", - []string{"1", "2"}, - "a > '1' AND a < '2'", - }, { - "a = ? AND b = ?", - []string{"1", "2"}, - "a = '1' AND b = '2'", - }, - } - - for _, testCase := range testCases { - str := ReplacePlaceholder(testCase.originStr, testCase.args) - require.Equal(t, testCase.expectStr, str) - } -} - -func TestTableName(t *testing.T) { - testCases := []struct { - schema string - table string - expectTableName string - }{ - { - "test", - "testa", - "`test`.`testa`", - }, - { - "test-1", - "test-a", - "`test-1`.`test-a`", - }, - { - "test", - "t`esta", - "`test`.`t``esta`", - }, - } - - for _, testCase := range testCases { - tableName := TableName(testCase.schema, testCase.table) - require.Equal(t, testCase.expectTableName, tableName) - } -} - -func TestColumnName(t *testing.T) { - testCases := []struct { - column string - expectColName string - }{ - { - "test", - "`test`", - }, - { - "test-1", - "`test-1`", - }, - { - "t`esta", - "`t``esta`", - }, - } - - for _, testCase := range testCases { - colName := ColumnName(testCase.column) - require.Equal(t, testCase.expectColName, colName) - } -} - -func newMysqlErr(number uint16, message string) *mysql.MySQLError { - return &mysql.MySQLError{ - Number: number, - Message: message, - } -} - -func TestIsIgnoreError(t *testing.T) { - cases := []struct { - err error - canIgnore bool - }{ - {newMysqlErr(uint16(infoschema.ErrDatabaseExists.Code()), "Can't create database, database exists"), true}, - {newMysqlErr(uint16(infoschema.ErrDatabaseDropExists.Code()), "Can't drop database, database doesn't exists"), true}, - {newMysqlErr(uint16(infoschema.ErrTableExists.Code()), "Can't create table, table exists"), true}, - {newMysqlErr(uint16(infoschema.ErrTableDropExists.Code()), "Can't drop table, table dosen't exists"), true}, - {newMysqlErr(uint16(infoschema.ErrColumnExists.Code()), "Duplicate column name"), true}, - {newMysqlErr(uint16(infoschema.ErrIndexExists.Code()), "Duplicate Index"), true}, - - {newMysqlErr(uint16(999), "fake error"), false}, - {errors.New("unknown error"), false}, - } - - for _, tt := range cases { - t.Logf("err %v, expected %v", tt.err, tt.canIgnore) - require.Equal(t, tt.canIgnore, ignoreError(tt.err)) - } -} - -func TestDeleteRows(t *testing.T) { - db, mock, err := sqlmock.New() - require.NoError(t, err) - - // delete twice - mock.ExpectExec("DELETE FROM").WillReturnResult(sqlmock.NewResult(0, DefaultDeleteRowsNum)) - mock.ExpectExec("DELETE FROM").WillReturnResult(sqlmock.NewResult(0, DefaultDeleteRowsNum-1)) - - err = DeleteRows(context.Background(), db, "test", "t", "", nil) - require.NoError(t, err) - - if err := mock.ExpectationsWereMet(); err != nil { - t.Errorf("there were unfulfilled expectations: %s", err) - } -} - -func TestGetParser(t *testing.T) { - testCases := []struct { - sqlModeStr string - hasErr bool - }{ - { - "", - false, - }, { - "ANSI_QUOTES", - false, - }, { - "ANSI_QUOTES,IGNORE_SPACE", - false, - }, { - "ANSI_QUOTES123", - true, - }, { - "ANSI_QUOTES,IGNORE_SPACE123", - true, - }, - } - - for _, testCase := range testCases { - parser, err := getParser(testCase.sqlModeStr) - if testCase.hasErr { - require.Error(t, err) - } else { - require.NoError(t, err) - require.NotNil(t, parser) - } - } -} - -func TestAnalyzeValuesFromBuckets(t *testing.T) { - cases := []struct { - value string - col *model.ColumnInfo - expect string - }{ - { - "2021-03-05 21:31:03", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDatetime).Build()}, - "2021-03-05 21:31:03", - }, - { - "2021-03-05 21:31:03", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeTimestamp).Build()}, - "2021-03-05 21:31:03", - }, - { - "2021-03-05", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDate).Build()}, - "2021-03-05", - }, - { - "1847956477067657216", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDatetime).Build()}, - "2020-01-01 10:00:00", - }, - { - "1847955927311843328", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeTimestamp).Build()}, - "2020-01-01 02:00:00", - }, - { - "1847955789872889856", - &model.ColumnInfo{FieldType: types.NewFieldTypeBuilder().SetType(pmysql.TypeDate).Build()}, - "2020-01-01 00:00:00", - }, - } - for _, ca := range cases { - val, err := AnalyzeValuesFromBuckets(ca.value, []*model.ColumnInfo{ca.col}) - require.NoError(t, err) - require.Len(t, val, 1) - require.Equal(t, ca.expect, val[0]) - } -} - -func TestFormatTimeZoneOffset(t *testing.T) { - cases := map[string]time.Duration{ - "+00:00": 0, - "+01:00": time.Hour, - "-08:03": -1 * (8*time.Hour + 3*time.Minute), - "-12:59": -1 * (12*time.Hour + 59*time.Minute), - "+12:59": 12*time.Hour + 59*time.Minute, - } - - for k, v := range cases { - offset := FormatTimeZoneOffset(v) - require.Equal(t, offset, k) - } -} - -func TestGetTimeZoneOffset(t *testing.T) { - db, mock, err := sqlmock.New() - require.NoError(t, err) - - mock.ExpectQuery("SELECT cast\\(TIMEDIFF\\(NOW\\(6\\), UTC_TIMESTAMP\\(6\\)\\) as time\\);"). - WillReturnRows(mock.NewRows([]string{""}).AddRow("01:00:00")) - d, err := GetTimeZoneOffset(context.Background(), db) - require.NoError(t, err) - require.Equal(t, "1h0m0s", d.String()) -} diff --git a/util/dbutil/index.go b/util/dbutil/index.go deleted file mode 100644 index 284f31adf8386..0000000000000 --- a/util/dbutil/index.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "context" - "fmt" - "sort" - "strconv" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" -) - -// IndexInfo contains information of table index. -type IndexInfo struct { - Table string - KeyName string - ColumnName string - SeqInIndex int - Cardinality int - NoneUnique bool -} - -// ShowIndex returns result of executing `show index` -func ShowIndex(ctx context.Context, db QueryExecutor, schemaName string, table string) ([]*IndexInfo, error) { - /* - show index example result: - mysql> show index from test; - +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ - | Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | - +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ - | test | 0 | PRIMARY | 1 | id | A | 0 | NULL | NULL | | BTREE | | | - | test | 0 | aid | 1 | aid | A | 0 | NULL | NULL | YES | BTREE | | | - +-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ - */ - indices := make([]*IndexInfo, 0, 3) - query := fmt.Sprintf("SHOW INDEX FROM %s", TableName(schemaName, table)) - rows, err := db.QueryContext(ctx, query) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - for rows.Next() { - fields, err1 := ScanRow(rows) - if err1 != nil { - return nil, errors.Trace(err1) - } - seqInIndex, err1 := strconv.Atoi(string(fields["Seq_in_index"].Data)) - if err1 != nil { - return nil, errors.Trace(err1) - } - cardinality, err1 := strconv.Atoi(string(fields["Cardinality"].Data)) - if err1 != nil { - return nil, errors.Trace(err1) - } - index := &IndexInfo{ - Table: string(fields["Table"].Data), - NoneUnique: string(fields["Non_unique"].Data) == "1", - KeyName: string(fields["Key_name"].Data), - ColumnName: string(fields["Column_name"].Data), - SeqInIndex: seqInIndex, - Cardinality: cardinality, - } - indices = append(indices, index) - } - - return indices, nil -} - -// FindSuitableColumnWithIndex returns first column of a suitable index. -// The priority is -// * primary key -// * unique key -// * normal index which has max cardinality -func FindSuitableColumnWithIndex(ctx context.Context, db QueryExecutor, schemaName string, tableInfo *model.TableInfo) (*model.ColumnInfo, error) { - // find primary key - for _, index := range tableInfo.Indices { - if index.Primary { - return FindColumnByName(tableInfo.Columns, index.Columns[0].Name.O), nil - } - } - - // no primary key found, seek unique index - for _, index := range tableInfo.Indices { - if index.Unique { - return FindColumnByName(tableInfo.Columns, index.Columns[0].Name.O), nil - } - } - - // no unique index found, seek index with max cardinality - indices, err := ShowIndex(ctx, db, schemaName, tableInfo.Name.O) - if err != nil { - return nil, errors.Trace(err) - } - var c *model.ColumnInfo - var maxCardinality int - for _, indexInfo := range indices { - // just use the first column in the index, otherwise can't hit the index when select - if indexInfo.SeqInIndex != 1 { - continue - } - - if indexInfo.Cardinality > maxCardinality { - column := FindColumnByName(tableInfo.Columns, indexInfo.ColumnName) - if column == nil { - return nil, errors.NotFoundf("column %s in %s.%s", indexInfo.ColumnName, schemaName, tableInfo.Name.O) - } - maxCardinality = indexInfo.Cardinality - c = column - } - } - - return c, nil -} - -// FindAllIndex returns all index, order is pk, uk, and normal index. -func FindAllIndex(tableInfo *model.TableInfo) []*model.IndexInfo { - indices := make([]*model.IndexInfo, len(tableInfo.Indices)) - copy(indices, tableInfo.Indices) - sort.SliceStable(indices, func(i, j int) bool { - a := indices[i] - b := indices[j] - switch { - case b.Primary: - return false - case a.Primary: - return true - case b.Unique: - return false - case a.Unique: - return true - default: - return false - } - }) - return indices -} - -// FindAllColumnWithIndex returns columns with index, order is pk, uk and normal index. -func FindAllColumnWithIndex(tableInfo *model.TableInfo) []*model.ColumnInfo { - colsMap := make(map[string]interface{}) - cols := make([]*model.ColumnInfo, 0, 2) - - for _, index := range FindAllIndex(tableInfo) { - // index will be guaranteed to be visited in order PK -> UK -> IK - for _, indexCol := range index.Columns { - col := FindColumnByName(tableInfo.Columns, indexCol.Name.O) - if _, ok := colsMap[col.Name.O]; ok { - continue - } - colsMap[col.Name.O] = struct{}{} - cols = append(cols, col) - } - } - - return cols -} - -// SelectUniqueOrderKey returns some columns for order by condition. -func SelectUniqueOrderKey(tbInfo *model.TableInfo) ([]string, []*model.ColumnInfo) { - keys := make([]string, 0, 2) - keyCols := make([]*model.ColumnInfo, 0, 2) - - for _, index := range tbInfo.Indices { - if index.Primary { - keys = keys[:0] - keyCols = keyCols[:0] - for _, indexCol := range index.Columns { - keys = append(keys, indexCol.Name.O) - keyCols = append(keyCols, tbInfo.Columns[indexCol.Offset]) - } - break - } - if index.Unique { - keys = keys[:0] - keyCols = keyCols[:0] - for _, indexCol := range index.Columns { - keys = append(keys, indexCol.Name.O) - keyCols = append(keyCols, tbInfo.Columns[indexCol.Offset]) - } - } - } - - if len(keys) != 0 { - return keys, keyCols - } - - // no primary key or unique found, use all fields as order by key - for _, col := range tbInfo.Columns { - keys = append(keys, col.Name.O) - keyCols = append(keyCols, col) - } - - return keys, keyCols -} diff --git a/util/dbutil/index_test.go b/util/dbutil/index_test.go deleted file mode 100644 index ff144b659147d..0000000000000 --- a/util/dbutil/index_test.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/stretchr/testify/require" -) - -func TestIndex(t *testing.T) { - testCases := []struct { - sql string - indices []string - cols []string - }{ - { - ` - CREATE TABLE itest (a int(11) NOT NULL, - b double NOT NULL DEFAULT '2', - c varchar(10) NOT NULL, - d time DEFAULT NULL, - PRIMARY KEY (a, b), - UNIQUE KEY d(d)) - `, - []string{"PRIMARY", "d"}, - []string{"a", "b", "d"}, - }, { - ` - CREATE TABLE jtest ( - a int(11) NOT NULL, - b varchar(10) DEFAULT NULL, - c varchar(255) DEFAULT NULL, - KEY c(c), - UNIQUE KEY b(b, c), - PRIMARY KEY (a) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin - `, - []string{"PRIMARY", "b", "c"}, - []string{"a", "b", "c"}, - }, { - ` - CREATE TABLE mtest ( - a int(24), - KEY test (a)) - `, - []string{"test"}, - []string{"a"}, - }, - { - ` - CREATE TABLE mtest ( - a int(24), - b int(24), - KEY test1 (a), - KEY test2 (b)) - `, - []string{"test1", "test2"}, - []string{"a", "b"}, - }, - { - ` - CREATE TABLE mtest ( - a int(24), - b int(24), - UNIQUE KEY test1 (a), - UNIQUE KEY test2 (b)) - `, - []string{"test1", "test2"}, - []string{"a", "b"}, - }, - } - - for _, testCase := range testCases { - tableInfo, err := GetTableInfoBySQL(testCase.sql, parser.New()) - require.NoError(t, err) - - indices := FindAllIndex(tableInfo) - for i, index := range indices { - require.Equal(t, testCase.indices[i], index.Name.O) - } - - cols := FindAllColumnWithIndex(tableInfo) - for j, col := range cols { - require.Equal(t, testCase.cols[j], col.Name.O) - } - } -} diff --git a/util/dbutil/table.go b/util/dbutil/table.go deleted file mode 100644 index 57c6484b2810a..0000000000000 --- a/util/dbutil/table.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "context" - "fmt" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - _ "github.com/pingcap/tidb/planner/core" // to setup expression.EvalAstExpr. See: https://github.com/pingcap/tidb/blob/a94cff903cd1e7f3b050db782da84273ef5592f4/planner/core/optimizer.go#L202 - "github.com/pingcap/tidb/types" - _ "github.com/pingcap/tidb/types/parser_driver" // for parser driver - "github.com/pingcap/tidb/util/collate" -) - -func init() { - collate.SetNewCollationEnabledForTest(false) -} - -// GetTableInfo returns table information. -func GetTableInfo(ctx context.Context, db QueryExecutor, schemaName string, tableName string) (*model.TableInfo, error) { - createTableSQL, err := GetCreateTableSQL(ctx, db, schemaName, tableName) - if err != nil { - return nil, errors.Trace(err) - } - - parser2, err := GetParserForDB(ctx, db) - if err != nil { - return nil, errors.Trace(err) - } - return GetTableInfoBySQL(createTableSQL, parser2) -} - -// GetTableInfoBySQL returns table information by given create table sql. -func GetTableInfoBySQL(createTableSQL string, parser2 *parser.Parser) (table *model.TableInfo, err error) { - stmt, err := parser2.ParseOneStmt(createTableSQL, "", "") - if err != nil { - return nil, errors.Trace(err) - } - - s, ok := stmt.(*ast.CreateTableStmt) - if ok { - table, err := ddl.BuildTableInfoFromAST(s) - if err != nil { - return nil, errors.Trace(err) - } - - // put primary key in indices - if table.PKIsHandle { - pkIndex := &model.IndexInfo{ - Name: model.NewCIStr("PRIMARY"), - Primary: true, - State: model.StatePublic, - Unique: true, - Tp: model.IndexTypeBtree, - Columns: []*model.IndexColumn{ - { - Name: table.GetPkName(), - Length: types.UnspecifiedLength, - }, - }, - } - - table.Indices = append(table.Indices, pkIndex) - } - - return table, nil - } - - return nil, errors.Errorf("get table info from sql %s failed", createTableSQL) -} - -// FindColumnByName finds column by name. -func FindColumnByName(cols []*model.ColumnInfo, name string) *model.ColumnInfo { - // column name don't distinguish capital and small letter - name = strings.ToLower(name) - for _, col := range cols { - if col.Name.L == name { - return col - } - } - - return nil -} - -// EqualTableInfo returns true if this two table info have same columns and indices -func EqualTableInfo(tableInfo1, tableInfo2 *model.TableInfo) (bool, string) { - // check columns - if len(tableInfo1.Columns) != len(tableInfo2.Columns) { - return false, fmt.Sprintf("column num not equal, one is %d another is %d", len(tableInfo1.Columns), len(tableInfo2.Columns)) - } - - for j, col := range tableInfo1.Columns { - if col.Name.O != tableInfo2.Columns[j].Name.O { - return false, fmt.Sprintf("column name not equal, one is %s another is %s", col.Name.O, tableInfo2.Columns[j].Name.O) - } - if col.GetType() != tableInfo2.Columns[j].GetType() { - return false, fmt.Sprintf("column %s's type not equal, one is %v another is %v", col.Name.O, col.GetType(), tableInfo2.Columns[j].GetType()) - } - } - - // check index - if len(tableInfo1.Indices) != len(tableInfo2.Indices) { - return false, fmt.Sprintf("index num not equal, one is %d another is %d", len(tableInfo1.Indices), len(tableInfo2.Indices)) - } - - index2Map := make(map[string]*model.IndexInfo) - for _, index := range tableInfo2.Indices { - index2Map[index.Name.O] = index - } - - for _, index1 := range tableInfo1.Indices { - index2, ok := index2Map[index1.Name.O] - if !ok { - return false, fmt.Sprintf("index %s not exists", index1.Name.O) - } - - if len(index1.Columns) != len(index2.Columns) { - return false, fmt.Sprintf("index %s's columns num not equal, one is %d another is %d", index1.Name.O, len(index1.Columns), len(index2.Columns)) - } - for j, col := range index1.Columns { - if col.Name.O != index2.Columns[j].Name.O { - return false, fmt.Sprintf("index %s's column not equal, one has %s another has %s", index1.Name.O, col.Name.O, index2.Columns[j].Name.O) - } - } - } - - return true, "" -} diff --git a/util/dbutil/table_test.go b/util/dbutil/table_test.go deleted file mode 100644 index 4c9156e3ef4a5..0000000000000 --- a/util/dbutil/table_test.go +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "testing" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/schemacmp" - "github.com/stretchr/testify/require" -) - -type testCase struct { - sql string - columns []string - indexs []string - colLen [][]int - colName string - fineCol bool -} - -func TestTable(t *testing.T) { - testCases := []*testCase{ - { - ` - CREATE TABLE htest ( - a int(11) PRIMARY KEY - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin - `, - []string{"a"}, - []string{mysql.PrimaryKeyName}, - [][]int{{types.UnspecifiedLength}}, - "c", - false, - }, { - ` - CREATE TABLE itest (a int(11) NOT NULL, - b double NOT NULL DEFAULT '2', - c varchar(10) NOT NULL, - d time DEFAULT NULL, - PRIMARY KEY (a, b), - UNIQUE KEY d (d)) - `, - []string{"a", "b", "c", "d"}, - []string{mysql.PrimaryKeyName, "d"}, - [][]int{{types.UnspecifiedLength, types.UnspecifiedLength}, {types.UnspecifiedLength}}, - "a", - true, - }, { - ` - CREATE TABLE jtest ( - a int(11) NOT NULL, - b varchar(10) DEFAULT NULL, - c varchar(255) DEFAULT NULL, - PRIMARY KEY (a) - ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_bin - `, - []string{"a", "b", "c"}, - []string{mysql.PrimaryKeyName}, - [][]int{{types.UnspecifiedLength}}, - "c", - true, - }, { - ` - CREATE TABLE mtest ( - a int(24), - KEY test (a)) - `, - []string{"a"}, - []string{"test"}, - [][]int{{types.UnspecifiedLength}}, - "d", - false, - }, { - ` - CREATE TABLE ntest ( - a int(24) PRIMARY KEY CLUSTERED - ) - `, - []string{"a"}, - []string{mysql.PrimaryKeyName}, - [][]int{{types.UnspecifiedLength}}, - "d", - false, - }, { - ` - CREATE TABLE otest ( - a int(11) NOT NULL, - b varchar(10) DEFAULT NULL, - c varchar(255) DEFAULT NULL, - PRIMARY KEY (a) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci - `, - []string{"a", "b", "c"}, - []string{mysql.PrimaryKeyName}, - [][]int{{types.UnspecifiedLength}}, - "c", - true, - }, - } - - for _, testCase := range testCases { - tableInfo, err := GetTableInfoBySQL(testCase.sql, parser.New()) - require.NoError(t, err) - for i, column := range tableInfo.Columns { - require.Equal(t, column.Name.O, testCase.columns[i]) - } - - require.Len(t, tableInfo.Indices, len(testCase.indexs)) - for j, index := range tableInfo.Indices { - require.Equal(t, index.Name.O, testCase.indexs[j]) - for k, indexCol := range index.Columns { - require.Equal(t, testCase.colLen[j][k], indexCol.Length) - } - } - - col := FindColumnByName(tableInfo.Columns, testCase.colName) - require.Equal(t, col != nil, testCase.fineCol) - } -} - -func TestTableStructEqual(t *testing.T) { - createTableSQL1 := "CREATE TABLE `test`.`atest` (`id` int(24), `name` varchar(24), `birthday` datetime, `update_time` time, `money` decimal(20,2), primary key(`id`))" - tableInfo1, err := GetTableInfoBySQL(createTableSQL1, parser.New()) - require.NoError(t, err) - - createTableSQL2 := "CREATE TABLE `test`.`atest` (`id` int(24) NOT NULL, `name` varchar(24), `birthday` datetime, `update_time` time, `money` decimal(20,2), primary key(`id`))" - tableInfo2, err := GetTableInfoBySQL(createTableSQL2, parser.New()) - require.NoError(t, err) - - createTableSQL3 := `CREATE TABLE "test"."atest" ("id" int(24), "name" varchar(24), "birthday" datetime, "update_time" time, "money" decimal(20,2), unique key("id"))` - p := parser.New() - p.SetSQLMode(mysql.ModeANSIQuotes) - tableInfo3, err := GetTableInfoBySQL(createTableSQL3, p) - require.NoError(t, err) - - equal, _ := EqualTableInfo(tableInfo1, tableInfo2) - require.Equal(t, true, equal) - - equal, _ = EqualTableInfo(tableInfo1, tableInfo3) - require.Equal(t, false, equal) -} - -func TestSchemacmpEncode(t *testing.T) { - createTableSQL := "CREATE TABLE `test`.`atest` (`id` int(24), primary key(`id`))" - tableInfo, err := GetTableInfoBySQL(createTableSQL, parser.New()) - require.NoError(t, err) - - table := schemacmp.Encode(tableInfo) - require.Equal(t, "CREATE TABLE `tbl`(`id` INT(24) NOT NULL, PRIMARY KEY (`id`)) CHARSET UTF8MB4 COLLATE UTF8MB4_BIN", table.String()) -} diff --git a/util/dbutil/types.go b/util/dbutil/types.go deleted file mode 100644 index e98acc8d1d99b..0000000000000 --- a/util/dbutil/types.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "github.com/pingcap/tidb/parser/mysql" -) - -// IsNumberType returns true if tp is number type -func IsNumberType(tp byte) bool { - switch tp { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24, mysql.TypeYear: - return true - } - - return false -} - -// IsFloatType returns true if tp is float type -func IsFloatType(tp byte) bool { - switch tp { - case mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: - return true - } - - return false -} - -// IsTimeTypeAndNeedDecode returns true if tp is time type and encoded in tidb buckets. -func IsTimeTypeAndNeedDecode(tp byte) bool { - if tp == mysql.TypeDatetime || tp == mysql.TypeTimestamp || tp == mysql.TypeDate { - return true - } - return false -} diff --git a/util/dbutil/variable.go b/util/dbutil/variable.go deleted file mode 100644 index 760e935612d6e..0000000000000 --- a/util/dbutil/variable.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package dbutil - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/auth" -) - -// ShowVersion queries variable 'version' and returns its value. -func ShowVersion(ctx context.Context, db QueryExecutor) (value string, err error) { - return ShowMySQLVariable(ctx, db, "version") -} - -// ShowLogBin queries variable 'log_bin' and returns its value. -func ShowLogBin(ctx context.Context, db QueryExecutor) (value string, err error) { - return ShowMySQLVariable(ctx, db, "log_bin") -} - -// ShowBinlogFormat queries variable 'binlog_format' and returns its value. -func ShowBinlogFormat(ctx context.Context, db QueryExecutor) (value string, err error) { - return ShowMySQLVariable(ctx, db, "binlog_format") -} - -// ShowBinlogRowImage queries variable 'binlog_row_image' and returns its values. -func ShowBinlogRowImage(ctx context.Context, db QueryExecutor) (value string, err error) { - return ShowMySQLVariable(ctx, db, "binlog_row_image") -} - -// ShowServerID queries variable 'server_id' and returns its value. -func ShowServerID(ctx context.Context, db QueryExecutor) (serverID uint64, err error) { - value, err := ShowMySQLVariable(ctx, db, "server_id") - if err != nil { - return 0, errors.Trace(err) - } - - serverID, err = strconv.ParseUint(value, 10, 64) - return serverID, errors.Annotatef(err, "parse server_id %s failed", value) -} - -// ShowMySQLVariable queries MySQL variable and returns its value. -func ShowMySQLVariable(ctx context.Context, db QueryExecutor, variable string) (value string, err error) { - query := fmt.Sprintf("SHOW GLOBAL VARIABLES LIKE '%s';", variable) - err = db.QueryRowContext(ctx, query).Scan(&variable, &value) - if err != nil { - return "", errors.Trace(err) - } - return value, nil -} - -// ShowGrants queries privileges for a mysql user. -// For mysql 8.0, if user has granted roles, ShowGrants also extract privilege from roles. -func ShowGrants(ctx context.Context, db QueryExecutor, user, host string) ([]string, error) { - if host == "" { - host = "%" - } - - var query string - if user == "" { - // for current user. - query = "SHOW GRANTS FOR CURRENT_USER" - } else { - query = fmt.Sprintf("SHOW GRANTS FOR '%s'@'%s'", user, host) - } - - readGrantsFunc := func() ([]string, error) { - rows, err := db.QueryContext(ctx, query) - if err != nil { - return nil, errors.Trace(err) - } - defer rows.Close() - - grants := make([]string, 0, 8) - for rows.Next() { - var grant string - err = rows.Scan(&grant) - if err != nil { - return nil, errors.Trace(err) - } - - // TiDB parser does not support parse `IDENTIFIED BY PASSWORD `, - // but it may appear in some cases, ref: https://dev.mysql.com/doc/refman/5.6/en/show-grants.html. - // We do not need the password in grant statement, so we can replace it. - grant = strings.Replace(grant, "IDENTIFIED BY PASSWORD ", "IDENTIFIED BY PASSWORD 'secret'", 1) - - // support parse `IDENTIFIED BY PASSWORD WITH {GRANT OPTION | resource_option} ...` - grant = strings.Replace(grant, "IDENTIFIED BY PASSWORD WITH", "IDENTIFIED BY PASSWORD 'secret' WITH", 1) - - // support parse `IDENTIFIED BY PASSWORD` - if strings.HasSuffix(grant, "IDENTIFIED BY PASSWORD") { - grant = grant + " 'secret'" - } - - grants = append(grants, grant) - } - if err := rows.Err(); err != nil { - return nil, errors.Trace(err) - } - return grants, nil - } - - grants, err := readGrantsFunc() - if err != nil { - return nil, errors.Trace(err) - } - - // for mysql 8.0, we should collect granted roles - var roles []*auth.RoleIdentity - p := parser.New() - for _, grant := range grants { - node, err := p.ParseOneStmt(grant, "", "") - if err != nil { - return nil, err - } - if grantRoleStmt, ok := node.(*ast.GrantRoleStmt); ok { - roles = append(roles, grantRoleStmt.Roles...) - } - } - - if len(roles) == 0 { - return grants, nil - } - - var s strings.Builder - s.WriteString(query) - s.WriteString(" USING ") - for i, role := range roles { - if i > 0 { - s.WriteString(", ") - } - s.WriteString(role.String()) - } - query = s.String() - - return readGrantsFunc() -} diff --git a/util/ddl-checker/BUILD.bazel b/util/ddl-checker/BUILD.bazel deleted file mode 100644 index 4cbd315e96817..0000000000000 --- a/util/ddl-checker/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ddl-checker", - srcs = [ - "ddl_syncer.go", - "executable_checker.go", - ], - importpath = "github.com/pingcap/tidb/util/ddl-checker", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//parser/ast", - "//session", - "//store/mockstore", - "//util/dbutil", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "ddl-checker_test", - timeout = "short", - srcs = ["executable_checker_test.go"], - embed = [":ddl-checker"], - flaky = True, - deps = [ - "//testkit", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/deadlockhistory/BUILD.bazel b/util/deadlockhistory/BUILD.bazel deleted file mode 100644 index 89eb6921903cd..0000000000000 --- a/util/deadlockhistory/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "deadlockhistory", - srcs = ["deadlock_history.go"], - importpath = "github.com/pingcap/tidb/util/deadlockhistory", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//types", - "//util/logutil", - "//util/resourcegrouptag", - "@com_github_tikv_client_go_v2//error", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "deadlockhistory_test", - timeout = "short", - srcs = [ - "deadlock_history_test.go", - "main_test.go", - ], - embed = [":deadlockhistory"], - flaky = True, - deps = [ - "//parser", - "//parser/model", - "//testkit/testsetup", - "//types", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//assert", - "@com_github_tikv_client_go_v2//error", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/deadlockhistory/main_test.go b/util/deadlockhistory/main_test.go deleted file mode 100644 index 94886db8018c3..0000000000000 --- a/util/deadlockhistory/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package deadlockhistory - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/disjointset/BUILD.bazel b/util/disjointset/BUILD.bazel deleted file mode 100644 index 77a6b05b7a6e4..0000000000000 --- a/util/disjointset/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "disjointset", - srcs = ["int_set.go"], - importpath = "github.com/pingcap/tidb/util/disjointset", - visibility = ["//visibility:public"], -) - -go_test( - name = "disjointset_test", - timeout = "short", - srcs = [ - "int_set_test.go", - "main_test.go", - ], - embed = [":disjointset"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/disjointset/main_test.go b/util/disjointset/main_test.go deleted file mode 100644 index 3681f9fdb7790..0000000000000 --- a/util/disjointset/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package disjointset - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/disk/BUILD.bazel b/util/disk/BUILD.bazel deleted file mode 100644 index 77b1f49c08b81..0000000000000 --- a/util/disk/BUILD.bazel +++ /dev/null @@ -1,38 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "disk", - srcs = [ - "tempDir.go", - "tracker.go", - ], - importpath = "github.com/pingcap/tidb/util/disk", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//parser/terror", - "//util/memory", - "@com_github_danjacques_gofslock//fslock", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_golang_x_sync//singleflight", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "disk_test", - timeout = "short", - srcs = [ - "main_test.go", - "tempDir_test.go", - ], - embed = [":disk"], - flaky = True, - deps = [ - "//config", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/disk/main_test.go b/util/disk/main_test.go deleted file mode 100644 index e7aff82d5adc5..0000000000000 --- a/util/disk/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package disk - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/disk/tracker.go b/util/disk/tracker.go deleted file mode 100644 index e8740f3c070ae..0000000000000 --- a/util/disk/tracker.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package disk - -import ( - "github.com/pingcap/tidb/util/memory" -) - -// Tracker is used to track the disk usage during query execution. -type Tracker = memory.Tracker - -// NewTracker creates a disk tracker. -// 1. "label" is the label used in the usage string. -// 2. "bytesLimit <= 0" means no limit. -var NewTracker = memory.NewTracker - -// NewGlobalTrcaker creates a global disk tracker. -var NewGlobalTrcaker = memory.NewGlobalTracker diff --git a/util/distrole/BUILD.bazel b/util/distrole/BUILD.bazel deleted file mode 100644 index 6282e64685b61..0000000000000 --- a/util/distrole/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "distrole", - srcs = ["role.go"], - importpath = "github.com/pingcap/tidb/util/distrole", - visibility = ["//visibility:public"], -) diff --git a/util/disttask/BUILD.bazel b/util/disttask/BUILD.bazel deleted file mode 100644 index c4417ff794424..0000000000000 --- a/util/disttask/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "disttask", - srcs = ["idservice.go"], - importpath = "github.com/pingcap/tidb/util/disttask", - visibility = ["//visibility:public"], - deps = ["//domain/infosync"], -) - -go_test( - name = "disttask_test", - timeout = "short", - srcs = ["idservice_test.go"], - embed = [":disttask"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/domainutil/BUILD.bazel b/util/domainutil/BUILD.bazel deleted file mode 100644 index 4053412e0406f..0000000000000 --- a/util/domainutil/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "domainutil", - srcs = ["repair_vars.go"], - importpath = "github.com/pingcap/tidb/util/domainutil", - visibility = ["//visibility:public"], - deps = ["//parser/model"], -) diff --git a/util/encrypt/BUILD.bazel b/util/encrypt/BUILD.bazel deleted file mode 100644 index dfafb966b16c8..0000000000000 --- a/util/encrypt/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "encrypt", - srcs = [ - "aes.go", - "aes_layer.go", - "crypt.go", - ], - importpath = "github.com/pingcap/tidb/util/encrypt", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "encrypt_test", - timeout = "short", - srcs = [ - "aes_layer_test.go", - "aes_test.go", - "crypt_test.go", - "main_test.go", - ], - embed = [":encrypt"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/checksum", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/encrypt/main_test.go b/util/encrypt/main_test.go deleted file mode 100644 index a63aa4ef2a8e0..0000000000000 --- a/util/encrypt/main_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package encrypt - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - goleak.VerifyTestMain(m) -} diff --git a/util/engine/BUILD.bazel b/util/engine/BUILD.bazel deleted file mode 100644 index 3d8ea7cfa6fa7..0000000000000 --- a/util/engine/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "engine", - srcs = ["engine.go"], - importpath = "github.com/pingcap/tidb/util/engine", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_kvproto//pkg/metapb"], -) diff --git a/util/errors_test.go b/util/errors_test.go deleted file mode 100644 index 81338645f3a2f..0000000000000 --- a/util/errors_test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package util_test - -import ( - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/util" - "github.com/stretchr/testify/require" -) - -func TestOriginError(t *testing.T) { - require.Nil(t, util.OriginError(nil)) - - err1 := errors.New("err1") - require.Equal(t, err1, util.OriginError(err1)) - - err2 := errors.Trace(err1) - require.Equal(t, err1, util.OriginError(err2)) - - err3 := errors.Trace(err2) - require.Equal(t, err1, util.OriginError(err3)) -} diff --git a/util/etcd/BUILD.bazel b/util/etcd/BUILD.bazel deleted file mode 100644 index f832e254d74a7..0000000000000 --- a/util/etcd/BUILD.bazel +++ /dev/null @@ -1,27 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "etcd", - srcs = ["etcd.go"], - importpath = "github.com/pingcap/tidb/util/etcd", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_client_v3//namespace", - ], -) - -go_test( - name = "etcd_test", - timeout = "short", - srcs = ["etcd_test.go"], - embed = [":etcd"], - flaky = True, - deps = [ - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@io_etcd_go_etcd_client_v3//:client", - "@io_etcd_go_etcd_tests_v3//integration", - ], -) diff --git a/util/execdetails/BUILD.bazel b/util/execdetails/BUILD.bazel deleted file mode 100644 index 597e5710efd36..0000000000000 --- a/util/execdetails/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "execdetails", - srcs = ["execdetails.go"], - importpath = "github.com/pingcap/tidb/util/execdetails", - visibility = ["//visibility:public"], - deps = [ - "@com_github_influxdata_tdigest//:tdigest", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "execdetails_test", - timeout = "short", - srcs = [ - "execdetails_test.go", - "main_test.go", - ], - embed = [":execdetails"], - flaky = True, - race = "on", - deps = [ - "//testkit/testsetup", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/execdetails/main_test.go b/util/execdetails/main_test.go deleted file mode 100644 index 880c233c16313..0000000000000 --- a/util/execdetails/main_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. -package execdetails - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/expensivequery/BUILD.bazel b/util/expensivequery/BUILD.bazel deleted file mode 100644 index 1d937427325f4..0000000000000 --- a/util/expensivequery/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "expensivequery", - srcs = ["expensivequery.go"], - importpath = "github.com/pingcap/tidb/util/expensivequery", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "//sessionctx/variable", - "//util", - "//util/logutil", - "@com_github_pingcap_log//:log", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "expensivequery_test", - timeout = "short", - srcs = ["expensivequerey_test.go"], - embed = [":expensivequery"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/extsort/BUILD.bazel b/util/extsort/BUILD.bazel deleted file mode 100644 index 2aa04292a7cf3..0000000000000 --- a/util/extsort/BUILD.bazel +++ /dev/null @@ -1,42 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "extsort", - srcs = [ - "disk_sorter.go", - "external_sorter.go", - ], - importpath = "github.com/pingcap/tidb/util/extsort", - visibility = ["//visibility:public"], - deps = [ - "//util/generic", - "//util/syncutil", - "@com_github_cockroachdb_pebble//:pebble", - "@com_github_cockroachdb_pebble//sstable", - "@com_github_cockroachdb_pebble//vfs", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@org_golang_x_sync//errgroup", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "extsort_test", - timeout = "short", - srcs = [ - "disk_sorter_test.go", - "external_sorter_test.go", - ], - embed = [":extsort"], - flaky = True, - deps = [ - "@com_github_cockroachdb_pebble//:pebble", - "@com_github_cockroachdb_pebble//sstable", - "@com_github_cockroachdb_pebble//vfs", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - "@org_golang_x_sync//errgroup", - "@org_uber_go_zap//:zap", - ], -) diff --git a/util/fastrand/BUILD.bazel b/util/fastrand/BUILD.bazel deleted file mode 100644 index 8604c94d8a32a..0000000000000 --- a/util/fastrand/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "fastrand", - srcs = ["random.go"], - importpath = "github.com/pingcap/tidb/util/fastrand", - visibility = ["//visibility:public"], -) - -go_test( - name = "fastrand_test", - timeout = "short", - srcs = [ - "main_test.go", - "random_test.go", - ], - embed = [":fastrand"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/fastrand/main_test.go b/util/fastrand/main_test.go deleted file mode 100644 index 5d3eb374e072e..0000000000000 --- a/util/fastrand/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package fastrand - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/filter/BUILD.bazel b/util/filter/BUILD.bazel deleted file mode 100644 index 0398ae0371ba9..0000000000000 --- a/util/filter/BUILD.bazel +++ /dev/null @@ -1,28 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "filter", - srcs = [ - "filter.go", - "schema.go", - ], - importpath = "github.com/pingcap/tidb/util/filter", - visibility = ["//visibility:public"], - deps = [ - "//util/table-filter", - "//util/table-rule-selector", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "filter_test", - timeout = "short", - srcs = [ - "filter_test.go", - "schema_test.go", - ], - embed = [":filter"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/format/BUILD.bazel b/util/format/BUILD.bazel deleted file mode 100644 index c100d198aa6ba..0000000000000 --- a/util/format/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "format", - srcs = ["format.go"], - importpath = "github.com/pingcap/tidb/util/format", - visibility = ["//visibility:public"], -) - -go_test( - name = "format_test", - timeout = "short", - srcs = [ - "format_test.go", - "main_test.go", - ], - embed = [":format"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/format/main_test.go b/util/format/main_test.go deleted file mode 100644 index 807f4fac72817..0000000000000 --- a/util/format/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package format - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/gctuner/BUILD.bazel b/util/gctuner/BUILD.bazel deleted file mode 100644 index 26cac9a73e88e..0000000000000 --- a/util/gctuner/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "gctuner", - srcs = [ - "finalizer.go", - "mem.go", - "memory_limit_tuner.go", - "tuner.go", - ], - importpath = "github.com/pingcap/tidb/util/gctuner", - visibility = ["//visibility:public"], - deps = [ - "//util", - "//util/intest", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "gctuner_test", - timeout = "short", - srcs = [ - "finalizer_test.go", - "mem_test.go", - "memory_limit_tuner_test.go", - "tuner_test.go", - ], - embed = [":gctuner"], - flaky = True, - race = "on", - shard_count = 5, - deps = [ - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/gcutil/BUILD.bazel b/util/gcutil/BUILD.bazel deleted file mode 100644 index fc6c882078726..0000000000000 --- a/util/gcutil/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "gcutil", - srcs = ["gcutil.go"], - importpath = "github.com/pingcap/tidb/util/gcutil", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/model", - "//sessionctx", - "//sessionctx/variable", - "//util/sqlexec", - "@com_github_pingcap_errors//:errors", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//util", - ], -) diff --git a/util/generatedexpr/BUILD.bazel b/util/generatedexpr/BUILD.bazel deleted file mode 100644 index e8d1c6c412dc9..0000000000000 --- a/util/generatedexpr/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "generatedexpr", - srcs = ["generated_expr.go"], - importpath = "github.com/pingcap/tidb/util/generatedexpr", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//parser/ast", - "//parser/charset", - "//parser/model", - "//util", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "generatedexpr_test", - timeout = "short", - srcs = [ - "gen_expr_test.go", - "main_test.go", - ], - embed = [":generatedexpr"], - flaky = True, - deps = [ - "//parser/ast", - "//testkit/testsetup", - "//types/parser_driver", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/generatedexpr/main_test.go b/util/generatedexpr/main_test.go deleted file mode 100644 index b6fc95b22e181..0000000000000 --- a/util/generatedexpr/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package generatedexpr - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/generic/BUILD.bazel b/util/generic/BUILD.bazel deleted file mode 100644 index 609168becf49b..0000000000000 --- a/util/generic/BUILD.bazel +++ /dev/null @@ -1,19 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "generic", - srcs = ["sync_map.go"], - importpath = "github.com/pingcap/tidb/util/generic", - visibility = ["//visibility:public"], -) - -go_test( - name = "generic_test", - timeout = "short", - srcs = ["sync_map_test.go"], - flaky = True, - deps = [ - ":generic", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/globalconn/BUILD.bazel b/util/globalconn/BUILD.bazel deleted file mode 100644 index fb99924ea9ffa..0000000000000 --- a/util/globalconn/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "globalconn", - srcs = [ - "globalconn.go", - "pool.go", - ], - importpath = "github.com/pingcap/tidb/util/globalconn", - visibility = ["//visibility:public"], - deps = [ - "//util/logutil", - "@com_github_cznic_mathutil//:mathutil", - "@com_github_ngaut_sync2//:sync2", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "globalconn_test", - timeout = "short", - srcs = [ - "globalconn_test.go", - "pool_test.go", - ], - flaky = True, - deps = [ - ":globalconn", - "@com_github_cznic_mathutil//:mathutil", - "@com_github_stretchr_testify//assert", - ], -) diff --git a/util/globalconn/pool_test.go b/util/globalconn/pool_test.go deleted file mode 100644 index a6d7cfaa77edc..0000000000000 --- a/util/globalconn/pool_test.go +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package globalconn_test - -import ( - "fmt" - "math" - "runtime" - "sync" - "sync/atomic" - "testing" - - "github.com/cznic/mathutil" - "github.com/pingcap/tidb/util/globalconn" - "github.com/stretchr/testify/assert" -) - -func TestAutoIncPool(t *testing.T) { - assert := assert.New(t) - - const SizeInBits uint32 = 8 - const Size uint64 = 1 << SizeInBits - const TryCnt = 4 - - var ( - pool globalconn.AutoIncPool - val uint64 - ok bool - i uint64 - ) - - pool.InitExt(Size, true, TryCnt) - assert.Equal(int(Size), pool.Cap()) - assert.Equal(0, pool.Len()) - - // get all. - for i = 1; i < Size; i++ { - val, ok = pool.Get() - assert.True(ok) - assert.Equal(i, val) - } - val, ok = pool.Get() - assert.True(ok) - assert.Equal(uint64(0), val) // wrap around to 0 - assert.Equal(int(Size), pool.Len()) - - _, ok = pool.Get() // exhausted. try TryCnt times, lastID is added to 0+TryCnt. - assert.False(ok) - - nextVal := uint64(TryCnt + 1) - pool.Put(nextVal) - val, ok = pool.Get() - assert.True(ok) - assert.Equal(nextVal, val) - - nextVal += TryCnt - 1 - pool.Put(nextVal) - val, ok = pool.Get() - assert.True(ok) - assert.Equal(nextVal, val) - - nextVal += TryCnt + 1 - pool.Put(nextVal) - _, ok = pool.Get() - assert.False(ok) -} - -func TestLockFreePoolBasic(t *testing.T) { - assert := assert.New(t) - - const SizeInBits uint32 = 8 - const Size uint64 = 1< 0 { - pool.InitForTest(headPos, fillCount) - } - - return &pool -} - -func prepareConcurrencyTest(pool globalconn.IDPool, producers int, consumers int, requests int, total *int64) (ready chan struct{}, done chan struct{}, wgProducer *sync.WaitGroup, wgConsumer *sync.WaitGroup) { - ready = make(chan struct{}) - done = make(chan struct{}) - - wgProducer = &sync.WaitGroup{} - if producers > 0 { - reqsPerProducer := (requests + producers - 1) / producers - wgProducer.Add(producers) - for p := 0; p < producers; p++ { - go func(p int) { - defer wgProducer.Done() - <-ready - - for i := p * reqsPerProducer; i < (p+1)*reqsPerProducer && i < requests; i++ { - for !pool.Put(uint64(i)) { - runtime.Gosched() - } - } - }(p) - } - } - - wgConsumer = &sync.WaitGroup{} - if consumers > 0 { - wgConsumer.Add(consumers) - for c := 0; c < consumers; c++ { - go func(c int) { - defer wgConsumer.Done() - <-ready - - var sum int64 - Loop: - for { - val, ok := pool.Get() - if ok { - sum += int64(val) - continue - } - select { - case <-done: - break Loop - default: - runtime.Gosched() - } - } - atomic.AddInt64(total, sum) - }(c) - } - } - - return ready, done, wgProducer, wgConsumer -} - -func doConcurrencyTest(ready chan struct{}, done chan struct{}, wgProducer *sync.WaitGroup, wgConsumer *sync.WaitGroup) { - // logutil.BgLogger().Info("Init", zap.Stringer("pool", q)) - close(ready) - wgProducer.Wait() - // logutil.BgLogger().Info("Snapshot on producing done", zap.Stringer("pool", q)) - close(done) - wgConsumer.Wait() - // logutil.BgLogger().Info("Finally", zap.Stringer("pool", q)) -} - -func expectedConcurrencyTestResult(poolSizeInBits uint32, fillCount uint32, producers int, consumers int, requests int) (expected int64) { - if producers > 0 && consumers > 0 { - expected += (int64(requests) - 1) * int64(requests) / 2 - } - if fillCount > 0 { - fillCount = mathutil.MinUint32(1<" - } - return fmt.Sprintf("Config(%+v)", *c) -} diff --git a/util/importer/parser.go b/util/importer/parser.go deleted file mode 100644 index 2bfcf323527a3..0000000000000 --- a/util/importer/parser.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package importer - -import ( - "fmt" - "strconv" - "strings" - - "github.com/pingcap/errors" - "github.com/pingcap/log" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/types" - _ "github.com/pingcap/tidb/types/parser_driver" // for parser driver - "github.com/pingcap/tidb/util/dbutil" - "go.uber.org/zap" -) - -type column struct { - data *datum - tp *types.FieldType - table *table - name string - comment string - min string - max string - set []string - idx int - step int64 -} - -func (col *column) String() string { - if col == nil { - return "" - } - - return fmt.Sprintf("[column]idx: %d, name: %s, tp: %v, min: %s, max: %s, step: %d, set: %v\n", - col.idx, col.name, col.tp, col.min, col.max, col.step, col.set) -} - -func (col *column) parseRule(kvs []string) { - if len(kvs) != 2 { - return - } - - key := strings.TrimSpace(kvs[0]) - value := strings.TrimSpace(kvs[1]) - if key == "range" { - fields := strings.Split(value, ",") - if len(fields) == 1 { - col.min = strings.TrimSpace(fields[0]) - } else if len(fields) == 2 { - col.min = strings.TrimSpace(fields[0]) - col.max = strings.TrimSpace(fields[1]) - } - } else if key == "step" { - var err error - col.step, err = strconv.ParseInt(value, 10, 64) - if err != nil { - log.Fatal("parseRule", zap.Error(err)) - } - } else if key == "set" { - fields := strings.Split(value, ",") - for _, field := range fields { - col.set = append(col.set, strings.TrimSpace(field)) - } - } -} - -// parse the data rules. -// rules like `a int unique comment '[[range=1,10;step=1]]'`, -// then we will get value from 1,2...10 -func (col *column) parseColumnComment() { - comment := strings.TrimSpace(col.comment) - start := strings.Index(comment, "[[") - end := strings.Index(comment, "]]") - var content string - if start < end { - content = comment[start+2 : end] - } - - fields := strings.Split(content, ";") - for _, field := range fields { - field = strings.TrimSpace(field) - kvs := strings.Split(field, "=") - col.parseRule(kvs) - } -} - -func (col *column) parseColumn(cd *ast.ColumnDef) { - col.name = cd.Name.Name.L - col.tp = cd.Tp - col.parseColumnOptions(cd.Options) - col.parseColumnComment() - col.table.columns = append(col.table.columns, col) -} - -func (col *column) parseColumnOptions(ops []*ast.ColumnOption) { - for _, op := range ops { - switch op.Tp { - case ast.ColumnOptionPrimaryKey, ast.ColumnOptionAutoIncrement, ast.ColumnOptionUniqKey: - col.table.uniqIndices[col.name] = col - case ast.ColumnOptionComment: - col.comment = op.Expr.(ast.ValueExpr).GetDatumString() - } - } -} - -type table struct { - indices map[string]*column - uniqIndices map[string]*column - unsignedCols map[string]*column - name string - columnList string - columns []*column -} - -func (t *table) printColumns() string { - ret := "" - for _, col := range t.columns { - ret += fmt.Sprintf("%v", col) - } - - return ret -} - -func (t *table) String() string { - if t == nil { - return "" - } - - ret := fmt.Sprintf("[table]name: %s\n", t.name) - ret += "[table]columns:\n" - ret += t.printColumns() - - ret += fmt.Sprintf("[table]column list: %s\n", t.columnList) - - ret += "[table]indices:\n" - for k, v := range t.indices { - ret += fmt.Sprintf("key->%s, value->%v", k, v) - } - - ret += "[table]unique indices:\n" - for k, v := range t.uniqIndices { - ret += fmt.Sprintf("key->%s, value->%v", k, v) - } - - return ret -} - -func newTable() *table { - return &table{ - indices: make(map[string]*column), - uniqIndices: make(map[string]*column), - unsignedCols: make(map[string]*column), - } -} - -func (*table) findCol(cols []*column, name string) *column { - for _, col := range cols { - if col.name == name { - return col - } - } - return nil -} - -func (t *table) parseTableConstraint(cons *ast.Constraint) { - switch cons.Tp { - case ast.ConstraintPrimaryKey, ast.ConstraintKey, ast.ConstraintUniq, - ast.ConstraintUniqKey, ast.ConstraintUniqIndex: - for _, indexCol := range cons.Keys { - name := indexCol.Column.Name.L - t.uniqIndices[name] = t.findCol(t.columns, name) - } - case ast.ConstraintIndex: - for _, indexCol := range cons.Keys { - name := indexCol.Column.Name.L - t.indices[name] = t.findCol(t.columns, name) - } - } -} - -func (t *table) buildColumnList() { - columns := make([]string, 0, len(t.columns)) - for _, column := range t.columns { - columns = append(columns, dbutil.ColumnName(column.name)) - } - - t.columnList = strings.Join(columns, ",") -} - -func parseTable(t *table, stmt *ast.CreateTableStmt) error { - t.name = stmt.Table.Name.L - t.columns = make([]*column, 0, len(stmt.Cols)) - - for i, col := range stmt.Cols { - column := &column{idx: i + 1, table: t, step: defaultStep, data: newDatum()} - column.parseColumn(col) - } - - for _, cons := range stmt.Constraints { - t.parseTableConstraint(cons) - } - - t.buildColumnList() - - return nil -} - -func parseTableSQL(table *table, sql string) error { - stmt, err := parser.New().ParseOneStmt(sql, "", "") - if err != nil { - return errors.Trace(err) - } - - switch node := stmt.(type) { - case *ast.CreateTableStmt: - err = parseTable(table, node) - default: - err = errors.Errorf("invalid statement - %v", stmt.Text()) - } - - return errors.Trace(err) -} - -func parseIndex(table *table, stmt *ast.CreateIndexStmt) error { - if table.name != stmt.Table.Name.L { - return errors.Errorf("mismatch table name for create index - %s : %s", table.name, stmt.Table.Name.L) - } - - for _, indexCol := range stmt.IndexPartSpecifications { - name := indexCol.Column.Name.L - if stmt.KeyType == ast.IndexKeyTypeUnique { - table.uniqIndices[name] = table.findCol(table.columns, name) - } else { - table.indices[name] = table.findCol(table.columns, name) - } - } - - return nil -} - -func parseIndexSQL(table *table, sql string) error { - if len(sql) == 0 { - return nil - } - - stmt, err := parser.New().ParseOneStmt(sql, "", "") - if err != nil { - return errors.Trace(err) - } - - switch node := stmt.(type) { - case *ast.CreateIndexStmt: - err = parseIndex(table, node) - default: - err = errors.Errorf("invalid statement - %v", stmt.Text()) - } - - return errors.Trace(err) -} diff --git a/util/intest/BUILD.bazel b/util/intest/BUILD.bazel deleted file mode 100644 index 1e2a23bddf634..0000000000000 --- a/util/intest/BUILD.bazel +++ /dev/null @@ -1,23 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "intest", - srcs = [ - "assert.go", #keep - "common.go", - "intest.go", #keep - ], - importpath = "github.com/pingcap/tidb/util/intest", - visibility = ["//visibility:public"], -) - -go_test( - name = "intest_test", - timeout = "short", - srcs = ["assert_test.go"], - flaky = True, - deps = [ - ":intest", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/israce/BUILD.bazel b/util/israce/BUILD.bazel deleted file mode 100644 index 7b9291a02722f..0000000000000 --- a/util/israce/BUILD.bazel +++ /dev/null @@ -1,11 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "israce", - srcs = [ - "israce.go", - "norace.go", - ], - importpath = "github.com/pingcap/tidb/util/israce", - visibility = ["//visibility:public"], -) diff --git a/util/keydecoder/BUILD.bazel b/util/keydecoder/BUILD.bazel deleted file mode 100644 index e0a0b9c2c1307..0000000000000 --- a/util/keydecoder/BUILD.bazel +++ /dev/null @@ -1,43 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "keydecoder", - srcs = ["keydecoder.go"], - importpath = "github.com/pingcap/tidb/util/keydecoder", - visibility = ["//visibility:public"], - deps = [ - "//infoschema", - "//kv", - "//parser/model", - "//tablecodec", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "keydecoder_test", - timeout = "short", - srcs = [ - "keydecoder_test.go", - "main_test.go", - ], - embed = [":keydecoder"], - flaky = True, - deps = [ - "//infoschema", - "//parser/model", - "//planner/core", - "//sessionctx/stmtctx", - "//table", - "//table/tables", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util/codec", - "@com_github_stretchr_testify//assert", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/keydecoder/main_test.go b/util/keydecoder/main_test.go deleted file mode 100644 index 6d035d482fd6f..0000000000000 --- a/util/keydecoder/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package keydecoder - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/kvcache/BUILD.bazel b/util/kvcache/BUILD.bazel deleted file mode 100644 index 658b5c57e0b5c..0000000000000 --- a/util/kvcache/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "kvcache", - srcs = ["simple_lru.go"], - importpath = "github.com/pingcap/tidb/util/kvcache", - visibility = ["//visibility:public"], - deps = [ - "//util/memory", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "kvcache_test", - timeout = "short", - srcs = [ - "main_test.go", - "simple_lru_test.go", - ], - embed = [":kvcache"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/memory", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/kvcache/main_test.go b/util/kvcache/main_test.go deleted file mode 100644 index 3906961f9abd1..0000000000000 --- a/util/kvcache/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package kvcache - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/localpool/BUILD.bazel b/util/localpool/BUILD.bazel deleted file mode 100644 index 97d3ec04775b8..0000000000000 --- a/util/localpool/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "localpool", - srcs = [ - "localpool.go", - "localpool_norace.go", - "localpool_race.go", - ], - importpath = "github.com/pingcap/tidb/util/localpool", - visibility = ["//visibility:public"], -) - -go_test( - name = "localpool_test", - timeout = "short", - srcs = [ - "localpool_test.go", - "main_test.go", - ], - embed = [":localpool"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/localpool/main_test.go b/util/localpool/main_test.go deleted file mode 100644 index 821e8eb3475c7..0000000000000 --- a/util/localpool/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package localpool - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/logutil/BUILD.bazel b/util/logutil/BUILD.bazel deleted file mode 100644 index c032019d3c608..0000000000000 --- a/util/logutil/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "logutil", - srcs = [ - "hex.go", - "log.go", - "slow_query_logger.go", - ], - importpath = "github.com/pingcap/tidb/util/logutil", - visibility = ["//visibility:public"], - deps = [ - "//parser/model", - "@com_github_golang_protobuf//proto", - "@com_github_grpc_ecosystem_go_grpc_middleware//logging/zap", - "@com_github_opentracing_opentracing_go//:opentracing-go", - "@com_github_opentracing_opentracing_go//log", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//buffer", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "logutil_test", - timeout = "short", - srcs = [ - "hex_test.go", - "log_test.go", - "main_test.go", - ], - embed = [":logutil"], - flaky = True, - deps = [ - "//kv", - "//parser/model", - "//testkit/testsetup", - "@com_github_google_uuid//:uuid", - "@com_github_pingcap_kvproto//pkg/metapb", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) diff --git a/util/logutil/consistency/BUILD.bazel b/util/logutil/consistency/BUILD.bazel deleted file mode 100644 index a361cc392428a..0000000000000 --- a/util/logutil/consistency/BUILD.bazel +++ /dev/null @@ -1,22 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "consistency", - srcs = ["reporter.go"], - importpath = "github.com/pingcap/tidb/util/logutil/consistency", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//kv", - "//parser/model", - "//sessionctx", - "//store/helper", - "//tablecodec", - "//types", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_zap//:zap", - ], -) diff --git a/util/logutil/consistency/reporter.go b/util/logutil/consistency/reporter.go deleted file mode 100644 index fd7fd655eb26a..0000000000000 --- a/util/logutil/consistency/reporter.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package consistency - -import ( - "context" - "encoding/hex" - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/errno" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/store/helper" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/dbterror" - "github.com/pingcap/tidb/util/logutil" - "github.com/tikv/client-go/v2/tikv" - "go.uber.org/zap" -) - -var ( - // ErrAdminCheckInconsistent returns for data inconsistency for admin check. - ErrAdminCheckInconsistent = dbterror.ClassAdmin.NewStd(errno.ErrDataInconsistent) - // ErrLookupInconsistent returns for data inconsistency for index lookup. - ErrLookupInconsistent = dbterror.ClassExecutor.NewStd(errno.ErrDataInconsistentMismatchCount) - // ErrAdminCheckInconsistentWithColInfo returns for data inconsistency for admin check but with column info. - ErrAdminCheckInconsistentWithColInfo = dbterror.ClassExecutor.NewStd(errno.ErrDataInconsistentMismatchIndex) -) - -// GetMvccByKey gets the MVCC value by key, and returns a json string including decoded data -func GetMvccByKey(tikvStore helper.Storage, key kv.Key, decodeMvccFn func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{})) string { - if key == nil { - return "" - } - h := helper.NewHelper(tikvStore) - data, err := h.GetMvccByEncodedKey(key) - if err != nil { - return "" - } - regionID := getRegionIDByKey(tikvStore, key) - - decodeKey := strings.ToUpper(hex.EncodeToString(key)) - - resp := map[string]interface{}{ - "key": decodeKey, - "regionID": regionID, - "mvcc": data, - } - - if decodeMvccFn != nil { - decodeMvccFn(key, data, resp) - } - - rj, err := json.Marshal(resp) - if err != nil { - return "" - } - const maxMvccInfoLen = 5000 - s := string(rj) - if len(s) > maxMvccInfoLen { - s = s[:maxMvccInfoLen] + "[truncated]..." - } - - return s -} - -func getRegionIDByKey(tikvStore helper.Storage, encodedKey []byte) uint64 { - keyLocation, err := tikvStore.GetRegionCache().LocateKey(tikv.NewBackofferWithVars(context.Background(), 500, nil), encodedKey) - if err != nil { - return 0 - } - return keyLocation.Region.GetID() -} - -// Reporter is a helper to generate report. -type Reporter struct { - HandleEncode func(handle kv.Handle) kv.Key - IndexEncode func(idxRow *RecordData) kv.Key - Tbl *model.TableInfo - Idx *model.IndexInfo - Sctx sessionctx.Context -} - -// DecodeRowMvccData creates a closure that captures the tableInfo to be used a decode function in GetMvccByKey. -func DecodeRowMvccData(tableInfo *model.TableInfo) func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) { - return func(key kv.Key, respValue *kvrpcpb.MvccGetByKeyResponse, outMap map[string]interface{}) { - colMap := make(map[int64]*types.FieldType, 3) - for _, col := range tableInfo.Columns { - var fieldType = col.FieldType - colMap[col.ID] = &fieldType - } - - if respValue.Info != nil { - var err error - datas := make(map[string]map[string]string) - for _, w := range respValue.Info.Writes { - if len(w.ShortValue) > 0 { - datas[strconv.FormatUint(w.StartTs, 10)], err = decodeMvccRecordValue(w.ShortValue, colMap, tableInfo) - } - } - - for _, v := range respValue.Info.Values { - if len(v.Value) > 0 { - datas[strconv.FormatUint(v.StartTs, 10)], err = decodeMvccRecordValue(v.Value, colMap, tableInfo) - } - } - if len(datas) > 0 { - outMap["decoded"] = datas - if err != nil { - outMap["decode_error"] = err.Error() - } - } - } - } -} - -// DecodeIndexMvccData creates a closure that captures the indexInfo to be used a decode function in GetMvccByKey. -func DecodeIndexMvccData(indexInfo *model.IndexInfo) func(kv.Key, *kvrpcpb.MvccGetByKeyResponse, map[string]interface{}) { - return func(key kv.Key, respValue *kvrpcpb.MvccGetByKeyResponse, outMap map[string]interface{}) { - if respValue.Info != nil { - var ( - hd kv.Handle - err error - datas = make(map[string]map[string]string) - ) - for _, w := range respValue.Info.Writes { - if len(w.ShortValue) > 0 { - hd, err = tablecodec.DecodeIndexHandle(key, w.ShortValue, len(indexInfo.Columns)) - if err == nil { - datas[strconv.FormatUint(w.StartTs, 10)] = map[string]string{"handle": hd.String()} - } - } - } - for _, v := range respValue.Info.Values { - if len(v.Value) > 0 { - hd, err = tablecodec.DecodeIndexHandle(key, v.Value, len(indexInfo.Columns)) - if err == nil { - datas[strconv.FormatUint(v.StartTs, 10)] = map[string]string{"handle": hd.String()} - } - } - } - if len(datas) > 0 { - outMap["decoded"] = datas - if err != nil { - outMap["decode_error"] = err.Error() - } - } - } - } -} - -func decodeMvccRecordValue(bs []byte, colMap map[int64]*types.FieldType, tb *model.TableInfo) (map[string]string, error) { - rs, err := tablecodec.DecodeRowToDatumMap(bs, colMap, time.UTC) - record := make(map[string]string, len(tb.Columns)) - for _, col := range tb.Columns { - if c, ok := rs[col.ID]; ok { - data := "nil" - if !c.IsNull() { - data, err = c.ToString() - } - record[col.Name.O] = data - } - } - return record, err -} - -// ReportLookupInconsistent reports inconsistent when index rows is more than record rows. -func (r *Reporter) ReportLookupInconsistent(ctx context.Context, idxCnt, tblCnt int, missHd, fullHd []kv.Handle, missRowIdx []RecordData) error { - if r.Sctx.GetSessionVars().EnableRedactLog { - logutil.Logger(ctx).Error("indexLookup found data inconsistency", - zap.String("table_name", r.Tbl.Name.O), - zap.String("index_name", r.Idx.Name.O), - zap.Int("index_cnt", idxCnt), - zap.Int("table_cnt", tblCnt), - zap.Stack("stack")) - } else { - const maxFullHandleCnt = 50 - displayFullHdCnt := len(fullHd) - if displayFullHdCnt > maxFullHandleCnt { - displayFullHdCnt = maxFullHandleCnt - } - fs := []zap.Field{ - zap.String("table_name", r.Tbl.Name.O), - zap.String("index_name", r.Idx.Name.O), - zap.Int("index_cnt", idxCnt), zap.Int("table_cnt", tblCnt), - zap.String("missing_handles", fmt.Sprint(missHd)), - zap.String("total_handles", fmt.Sprint(fullHd[:displayFullHdCnt])), - } - store, ok := r.Sctx.GetStore().(helper.Storage) - if ok { - for i, hd := range missHd { - fs = append(fs, zap.String("row_mvcc_"+strconv.Itoa(i), GetMvccByKey(store, r.HandleEncode(hd), DecodeRowMvccData(r.Tbl)))) - } - for i := range missRowIdx { - fs = append(fs, zap.String("index_mvcc_"+strconv.Itoa(i), GetMvccByKey(store, r.IndexEncode(&missRowIdx[i]), DecodeIndexMvccData(r.Idx)))) - } - } - - logutil.Logger(ctx).Error("indexLookup found data inconsistency", fs...) - } - return ErrLookupInconsistent.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, idxCnt, tblCnt) -} - -// ReportAdminCheckInconsistentWithColInfo reports inconsistent when the value of index row is different from record row. -func (r *Reporter) ReportAdminCheckInconsistentWithColInfo(ctx context.Context, handle kv.Handle, colName string, idxDat, tblDat fmt.Stringer, err error, idxRow *RecordData) error { - if r.Sctx.GetSessionVars().EnableRedactLog { - logutil.Logger(ctx).Error("admin check found data inconsistency", - zap.String("table_name", r.Tbl.Name.O), - zap.String("index", r.Idx.Name.O), - zap.String("col", colName), - zap.Error(err), - zap.Stack("stack"), - ) - } else { - fs := []zap.Field{ - zap.String("table_name", r.Tbl.Name.O), - zap.String("index_name", r.Idx.Name.O), - zap.String("col", colName), - zap.Stringer("row_id", handle), - zap.Stringer("idxDatum", idxDat), - zap.Stringer("rowDatum", tblDat), - } - store, ok := r.Sctx.GetStore().(helper.Storage) - if ok { - fs = append(fs, zap.String("row_mvcc", GetMvccByKey(store, r.HandleEncode(handle), DecodeRowMvccData(r.Tbl)))) - fs = append(fs, zap.String("index_mvcc", GetMvccByKey(store, r.IndexEncode(idxRow), DecodeIndexMvccData(r.Idx)))) - } - fs = append(fs, zap.Error(err)) - fs = append(fs, zap.Stack("stack")) - logutil.Logger(ctx).Error("admin check found data inconsistency", fs...) - } - return ErrAdminCheckInconsistentWithColInfo.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, colName, fmt.Sprint(handle), fmt.Sprint(idxDat), fmt.Sprint(tblDat), err) -} - -// RecordData is the record data composed of a handle and values. -type RecordData struct { - Handle kv.Handle - Values []types.Datum -} - -func (r *RecordData) String() string { - if r == nil { - return "" - } - return fmt.Sprintf("handle: %s, values: %s", fmt.Sprint(r.Handle), fmt.Sprint(r.Values)) -} - -// ReportAdminCheckInconsistent reports inconsistent when single index row not found in record rows. -func (r *Reporter) ReportAdminCheckInconsistent(ctx context.Context, handle kv.Handle, idxRow, tblRow *RecordData) error { - if r.Sctx.GetSessionVars().EnableRedactLog { - logutil.Logger(ctx).Error("admin check found data inconsistency", - zap.String("table_name", r.Tbl.Name.O), - zap.String("index", r.Idx.Name.O), - zap.Stack("stack"), - ) - } else { - fs := []zap.Field{ - zap.String("table_name", r.Tbl.Name.O), - zap.String("index_name", r.Idx.Name.O), - zap.Stringer("row_id", handle), - zap.Stringer("index", idxRow), - zap.Stringer("row", tblRow), - } - store, ok := r.Sctx.GetStore().(helper.Storage) - if ok { - fs = append(fs, zap.String("row_mvcc", GetMvccByKey(store, r.HandleEncode(handle), DecodeRowMvccData(r.Tbl)))) - if idxRow != nil { - fs = append(fs, zap.String("index_mvcc", GetMvccByKey(store, r.IndexEncode(idxRow), DecodeIndexMvccData(r.Idx)))) - } - } - fs = append(fs, zap.Stack("stack")) - logutil.Logger(ctx).Error("admin check found data inconsistency", fs...) - } - return ErrAdminCheckInconsistent.GenWithStackByArgs(r.Tbl.Name.O, r.Idx.Name.O, fmt.Sprint(handle), fmt.Sprint(idxRow), fmt.Sprint(tblRow)) -} diff --git a/util/logutil/main_test.go b/util/logutil/main_test.go deleted file mode 100644 index e4680ab7780c5..0000000000000 --- a/util/logutil/main_test.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package logutil - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -const ( - // zapLogWithoutCheckKeyPattern is used to match the zap log format but do not check some specified key, such as the following log: - // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"]["str key"=val] ["int key"=123] - zapLogWithoutCheckKeyPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] (\[.*=.*\]).*\n` - // zapLogPatern is used to match the zap log format, such as the following log: - // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [conn=conn1] ["str key"=val] ["int key"=123] - zapLogWithConnIDPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[conn=.*\] (\[.*=.*\]).*\n` - // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [conn=conn1] [session_alias=alias] ["str key"=val] ["int key"=123] - zapLogWithTraceInfoPattern = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[conn=.*\] \[session_alias=.*\] (\[.*=.*\]).*\n` - // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [ctxKey=ctxKey1] ["str key"=val] ["int key"=123] - zapLogWithKeyValPatternByCtx = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[ctxKey=.*\] (\[.*=.*\]).*\n` - // [2019/02/13 15:56:05.385 +08:00] [INFO] [log_test.go:167] ["info message"] [coreKey=coreKey1] ["str key"=val] ["int key"=123] - zapLogWithKeyValPatternByCore = `\[\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d.\d\d\d\ (\+|-)\d\d:\d\d\] \[(FATAL|ERROR|WARN|INFO|DEBUG)\] \[([\w_%!$@.,+~-]+|\\.)+:\d+\] \[.*\] \[coreKey=.*\] (\[.*=.*\]).*\n` -) - -var ( - PrettyPrint = prettyPrint -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/main_test.go b/util/main_test.go deleted file mode 100644 index d02ded71f902b..0000000000000 --- a/util/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package util_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/mathutil/BUILD.bazel b/util/mathutil/BUILD.bazel deleted file mode 100644 index 4d0b9810eaa57..0000000000000 --- a/util/mathutil/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mathutil", - srcs = [ - "exponential_average.go", - "math.go", - "rand.go", - ], - importpath = "github.com/pingcap/tidb/util/mathutil", - visibility = ["//visibility:public"], - deps = ["@org_golang_x_exp//constraints"], -) - -go_test( - name = "mathutil_test", - timeout = "short", - srcs = [ - "exponential_average_test.go", - "main_test.go", - "math_test.go", - "rand_test.go", - ], - embed = [":mathutil"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/mathutil/main_test.go b/util/mathutil/main_test.go deleted file mode 100644 index 6d682151ee0ea..0000000000000 --- a/util/mathutil/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mathutil - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/memory/BUILD.bazel b/util/memory/BUILD.bazel deleted file mode 100644 index 66a360862376b..0000000000000 --- a/util/memory/BUILD.bazel +++ /dev/null @@ -1,47 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "memory", - srcs = [ - "action.go", - "meminfo.go", - "memstats.go", - "tracker.go", - ], - importpath = "github.com/pingcap/tidb/util/memory", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//metrics", - "//parser/terror", - "//util/cgroup", - "//util/dbterror", - "//util/logutil", - "//util/mathutil", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_sysutil//:sysutil", - "@com_github_shirou_gopsutil_v3//mem", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "memory_test", - timeout = "short", - srcs = [ - "bench_test.go", - "main_test.go", - "tracker_test.go", - ], - embed = [":memory"], - flaky = True, - deps = [ - "//errno", - "//parser/terror", - "//testkit/testsetup", - "//util/mathutil", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/memory/main_test.go b/util/memory/main_test.go deleted file mode 100644 index a61c6337fcfe3..0000000000000 --- a/util/memory/main_test.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package memory - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - goleak.VerifyTestMain(m) -} diff --git a/util/memory/tracker.go b/util/memory/tracker.go deleted file mode 100644 index cd57e4029a5fb..0000000000000 --- a/util/memory/tracker.go +++ /dev/null @@ -1,868 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package memory - -import ( - "bytes" - "fmt" - "runtime" - "slices" - "strconv" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/util/logutil" - atomicutil "go.uber.org/atomic" - "go.uber.org/zap" -) - -// TrackMemWhenExceeds is the threshold when memory usage needs to be tracked. -const TrackMemWhenExceeds = 104857600 // 100MB - -// Process global variables for memory limit. -var ( - ServerMemoryLimitOriginText = atomicutil.NewString("0") - ServerMemoryLimit = atomicutil.NewUint64(0) - ServerMemoryLimitSessMinSize = atomicutil.NewUint64(128 << 20) - - QueryForceDisk = atomicutil.NewInt64(0) - TriggerMemoryLimitGC = atomicutil.NewBool(false) - MemoryLimitGCLast = atomicutil.NewTime(time.Time{}) - MemoryLimitGCTotal = atomicutil.NewInt64(0) -) - -// Tracker is used to track the memory usage during query execution. -// It contains an optional limit and can be arranged into a tree structure -// such that the consumption tracked by a Tracker is also tracked by -// its ancestors. The main idea comes from Apache Impala: -// -// https://github.com/cloudera/Impala/blob/cdh5-trunk/be/src/runtime/mem-tracker.h -// -// By default, memory consumption is tracked via calls to "Consume()", either to -// the tracker itself or to one of its descendents. A typical sequence of calls -// for a single Tracker is: -// 1. tracker.SetLabel() / tracker.SetActionOnExceed() / tracker.AttachTo() -// 2. tracker.Consume() / tracker.ReplaceChild() / tracker.BytesConsumed() -// -// NOTE: We only protect concurrent access to "bytesConsumed" and "children", -// that is to say: -// 1. Only "BytesConsumed()", "Consume()" and "AttachTo()" are thread-safe. -// 2. Other operations of a Tracker tree is not thread-safe. -// -// We have two limits for the memory quota: soft limit and hard limit. -// If the soft limit is exceeded, we will trigger the action that alleviates the -// speed of memory growth. The soft limit is hard-coded as `0.8*hard limit`. -// The actions that could be triggered are: AggSpillDiskAction. -// -// If the hard limit is exceeded, we will trigger the action that immediately -// reduces memory usage. The hard limit is set by the system variable `tidb_mem_query_quota`. -// The actions that could be triggered are: SpillDiskAction, SortAndSpillDiskAction, rateLimitAction, -// PanicOnExceed, globalPanicOnExceed, LogOnExceed. -type Tracker struct { - bytesLimit atomic.Value - actionMuForHardLimit actionMu - actionMuForSoftLimit actionMu - mu struct { - // The children memory trackers. If the Tracker is the Global Tracker, like executor.GlobalDiskUsageTracker, - // we wouldn't maintain its children in order to avoiding mutex contention. - children map[int][]*Tracker - sync.Mutex - } - parMu struct { - parent *Tracker // The parent memory tracker. - sync.Mutex - } - label int // Label of this "Tracker". - // following fields are used with atomic operations, so make them 64-byte aligned. - bytesConsumed int64 // Consumed bytes. - bytesReleased int64 // Released bytes. - maxConsumed atomicutil.Int64 // max number of bytes consumed during execution. - SessionID atomicutil.Uint64 // SessionID indicates the sessionID the tracker is bound. - NeedKill atomic.Bool // NeedKill indicates whether this session need kill because OOM - NeedKillReceived sync.Once - IsRootTrackerOfSess bool // IsRootTrackerOfSess indicates whether this tracker is bound for session - isGlobal bool // isGlobal indicates whether this tracker is global tracker -} - -type actionMu struct { - actionOnExceed ActionOnExceed - sync.Mutex -} - -// EnableGCAwareMemoryTrack is used to turn on/off the GC-aware memory track -var EnableGCAwareMemoryTrack = atomicutil.NewBool(false) - -// https://golang.google.cn/pkg/runtime/#SetFinalizer -// It is not guaranteed that a finalizer will run if the size of *obj is zero bytes. -type finalizerRef struct { - byte //nolint:unused -} - -// softScale means the scale of the soft limit to the hard limit. -const softScale = 0.8 - -// bytesLimits holds limit config atomically. -type bytesLimits struct { - bytesHardLimit int64 // bytesHardLimit <= 0 means no limit, used for actionMuForHardLimit. - bytesSoftLimit int64 // bytesSoftLimit <= 0 means no limit, used for actionMuForSoftLimit. -} - -// MemUsageTop1Tracker record the use memory top1 session's tracker for kill. -var MemUsageTop1Tracker atomic.Pointer[Tracker] - -// InitTracker initializes a memory tracker. -// 1. "label" is the label used in the usage string. -// 2. "bytesLimit <= 0" means no limit. -// -// For the common tracker, isGlobal is default as false -func InitTracker(t *Tracker, label int, bytesLimit int64, action ActionOnExceed) { - t.mu.children = nil - t.actionMuForHardLimit.actionOnExceed = action - t.actionMuForSoftLimit.actionOnExceed = nil - t.parMu.parent = nil - - t.label = label - t.bytesLimit.Store(&bytesLimits{ - bytesHardLimit: bytesLimit, - bytesSoftLimit: int64(float64(bytesLimit) * softScale), - }) - t.maxConsumed.Store(0) - t.isGlobal = false -} - -// NewTracker creates a memory tracker. -// 1. "label" is the label used in the usage string. -// 2. "bytesLimit <= 0" means no limit. -// -// For the common tracker, isGlobal is default as false -func NewTracker(label int, bytesLimit int64) *Tracker { - t := &Tracker{ - label: label, - } - t.bytesLimit.Store(&bytesLimits{ - bytesHardLimit: bytesLimit, - bytesSoftLimit: int64(float64(bytesLimit) * softScale), - }) - t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} - t.isGlobal = false - return t -} - -// NewGlobalTracker creates a global tracker, its isGlobal is default as true -func NewGlobalTracker(label int, bytesLimit int64) *Tracker { - t := &Tracker{ - label: label, - } - t.bytesLimit.Store(&bytesLimits{ - bytesHardLimit: bytesLimit, - bytesSoftLimit: int64(float64(bytesLimit) * softScale), - }) - t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} - t.isGlobal = true - return t -} - -// CheckBytesLimit check whether the bytes limit of the tracker is equal to a value. -// Only used in test. -func (t *Tracker) CheckBytesLimit(val int64) bool { - return t.bytesLimit.Load().(*bytesLimits).bytesHardLimit == val -} - -// SetBytesLimit sets the bytes limit for this tracker. -// "bytesHardLimit <= 0" means no limit. -func (t *Tracker) SetBytesLimit(bytesLimit int64) { - t.bytesLimit.Store(&bytesLimits{ - bytesHardLimit: bytesLimit, - bytesSoftLimit: int64(float64(bytesLimit) * softScale), - }) -} - -// GetBytesLimit gets the bytes limit for this tracker. -// "bytesHardLimit <= 0" means no limit. -func (t *Tracker) GetBytesLimit() int64 { - return t.bytesLimit.Load().(*bytesLimits).bytesHardLimit -} - -// CheckExceed checks whether the consumed bytes is exceed for this tracker. -func (t *Tracker) CheckExceed() bool { - bytesHardLimit := t.bytesLimit.Load().(*bytesLimits).bytesHardLimit - return atomic.LoadInt64(&t.bytesConsumed) >= bytesHardLimit && bytesHardLimit > 0 -} - -// SetActionOnExceed sets the action when memory usage exceeds bytesHardLimit. -func (t *Tracker) SetActionOnExceed(a ActionOnExceed) { - t.actionMuForHardLimit.Lock() - t.actionMuForHardLimit.actionOnExceed = a - t.actionMuForHardLimit.Unlock() -} - -// FallbackOldAndSetNewAction sets the action when memory usage exceeds bytesHardLimit -// and set the original action as its fallback. -func (t *Tracker) FallbackOldAndSetNewAction(a ActionOnExceed) { - t.actionMuForHardLimit.Lock() - defer t.actionMuForHardLimit.Unlock() - t.actionMuForHardLimit.actionOnExceed = reArrangeFallback(a, t.actionMuForHardLimit.actionOnExceed) -} - -// FallbackOldAndSetNewActionForSoftLimit sets the action when memory usage exceeds bytesSoftLimit -// and set the original action as its fallback. -func (t *Tracker) FallbackOldAndSetNewActionForSoftLimit(a ActionOnExceed) { - t.actionMuForSoftLimit.Lock() - defer t.actionMuForSoftLimit.Unlock() - t.actionMuForSoftLimit.actionOnExceed = reArrangeFallback(a, t.actionMuForSoftLimit.actionOnExceed) -} - -// GetFallbackForTest get the oom action used by test. -func (t *Tracker) GetFallbackForTest(ignoreFinishedAction bool) ActionOnExceed { - t.actionMuForHardLimit.Lock() - defer t.actionMuForHardLimit.Unlock() - if t.actionMuForHardLimit.actionOnExceed != nil && t.actionMuForHardLimit.actionOnExceed.IsFinished() && ignoreFinishedAction { - t.actionMuForHardLimit.actionOnExceed = t.actionMuForHardLimit.actionOnExceed.GetFallback() - } - return t.actionMuForHardLimit.actionOnExceed -} - -// UnbindActions unbinds actionForHardLimit and actionForSoftLimit. -func (t *Tracker) UnbindActions() { - t.actionMuForSoftLimit.Lock() - defer t.actionMuForSoftLimit.Unlock() - t.actionMuForSoftLimit.actionOnExceed = nil - - t.actionMuForHardLimit.Lock() - defer t.actionMuForHardLimit.Unlock() - t.actionMuForHardLimit.actionOnExceed = &LogOnExceed{} -} - -// reArrangeFallback merge two action chains and rearrange them by priority in descending order. -func reArrangeFallback(a ActionOnExceed, b ActionOnExceed) ActionOnExceed { - if a == nil { - return b - } - if b == nil { - return a - } - if a.GetPriority() < b.GetPriority() { - a, b = b, a - } - a.SetFallback(reArrangeFallback(a.GetFallback(), b)) - return a -} - -// SetLabel sets the label of a Tracker. -func (t *Tracker) SetLabel(label int) { - parent := t.getParent() - t.Detach() - t.label = label - if parent != nil { - t.AttachTo(parent) - } -} - -// Label gets the label of a Tracker. -func (t *Tracker) Label() int { - return t.label -} - -// AttachTo attaches this memory tracker as a child to another Tracker. If it -// already has a parent, this function will remove it from the old parent. -// Its consumed memory usage is used to update all its ancestors. -func (t *Tracker) AttachTo(parent *Tracker) { - if parent.isGlobal { - t.AttachToGlobalTracker(parent) - return - } - oldParent := t.getParent() - if oldParent != nil { - oldParent.remove(t) - } - parent.mu.Lock() - if parent.mu.children == nil { - parent.mu.children = make(map[int][]*Tracker) - } - parent.mu.children[t.label] = append(parent.mu.children[t.label], t) - parent.mu.Unlock() - - t.setParent(parent) - parent.Consume(t.BytesConsumed()) -} - -// Detach de-attach the tracker child from its parent, then set its parent property as nil -func (t *Tracker) Detach() { - if t == nil { - return - } - parent := t.getParent() - if parent == nil { - return - } - if parent.isGlobal { - t.DetachFromGlobalTracker() - return - } - if parent.IsRootTrackerOfSess && t.label != LabelForMemDB { - parent.actionMuForHardLimit.Lock() - parent.actionMuForHardLimit.actionOnExceed = nil - parent.actionMuForHardLimit.Unlock() - - parent.actionMuForSoftLimit.Lock() - parent.actionMuForSoftLimit.actionOnExceed = nil - parent.actionMuForSoftLimit.Unlock() - parent.NeedKill.Store(false) - parent.NeedKillReceived = sync.Once{} - } - parent.remove(t) - t.mu.Lock() - defer t.mu.Unlock() - t.setParent(nil) -} - -func (t *Tracker) remove(oldChild *Tracker) { - found := false - label := oldChild.label - t.mu.Lock() - if t.mu.children != nil { - children := t.mu.children[label] - for i, child := range children { - if child == oldChild { - children = append(children[:i], children[i+1:]...) - if len(children) > 0 { - t.mu.children[label] = children - } else { - delete(t.mu.children, label) - } - found = true - break - } - } - } - t.mu.Unlock() - if found { - oldChild.setParent(nil) - t.Consume(-oldChild.BytesConsumed()) - } -} - -// ReplaceChild removes the old child specified in "oldChild" and add a new -// child specified in "newChild". old child's memory consumption will be -// removed and new child's memory consumption will be added. -func (t *Tracker) ReplaceChild(oldChild, newChild *Tracker) { - if newChild == nil { - t.remove(oldChild) - return - } - - if oldChild.label != newChild.label { - t.remove(oldChild) - newChild.AttachTo(t) - return - } - - newConsumed := newChild.BytesConsumed() - newChild.setParent(t) - - label := oldChild.label - t.mu.Lock() - if t.mu.children != nil { - children := t.mu.children[label] - for i, child := range children { - if child != oldChild { - continue - } - - newConsumed -= oldChild.BytesConsumed() - oldChild.setParent(nil) - children[i] = newChild - t.mu.children[label] = children - break - } - } - t.mu.Unlock() - - t.Consume(newConsumed) -} - -// Consume is used to consume a memory usage. "bytes" can be a negative value, -// which means this is a memory release operation. When memory usage of a tracker -// exceeds its bytesSoftLimit/bytesHardLimit, the tracker calls its action, so does each of its ancestors. -func (t *Tracker) Consume(bs int64) { - if bs == 0 { - return - } - var rootExceed, rootExceedForSoftLimit, sessionRootTracker *Tracker - for tracker := t; tracker != nil; tracker = tracker.getParent() { - if tracker.IsRootTrackerOfSess { - sessionRootTracker = tracker - } - bytesConsumed := atomic.AddInt64(&tracker.bytesConsumed, bs) - bytesReleased := atomic.LoadInt64(&tracker.bytesReleased) - limits := tracker.bytesLimit.Load().(*bytesLimits) - if bytesConsumed+bytesReleased >= limits.bytesHardLimit && limits.bytesHardLimit > 0 { - rootExceed = tracker - } - if bytesConsumed+bytesReleased >= limits.bytesSoftLimit && limits.bytesSoftLimit > 0 { - rootExceedForSoftLimit = tracker - } - - for { - maxNow := tracker.maxConsumed.Load() - consumed := atomic.LoadInt64(&tracker.bytesConsumed) - if consumed > maxNow && !tracker.maxConsumed.CompareAndSwap(maxNow, consumed) { - continue - } - if label, ok := MetricsTypes[tracker.label]; ok { - metrics.MemoryUsage.WithLabelValues(label[0], label[1]).Set(float64(consumed)) - } - break - } - } - - tryAction := func(mu *actionMu, tracker *Tracker) { - mu.Lock() - defer mu.Unlock() - for mu.actionOnExceed != nil && mu.actionOnExceed.IsFinished() { - mu.actionOnExceed = mu.actionOnExceed.GetFallback() - } - if mu.actionOnExceed != nil { - mu.actionOnExceed.Action(tracker) - } - } - - tryActionLastOne := func(mu *actionMu, tracker *Tracker) { - mu.Lock() - defer mu.Unlock() - if currentAction := mu.actionOnExceed; currentAction != nil { - for nextAction := currentAction.GetFallback(); nextAction != nil; { - currentAction = nextAction - nextAction = currentAction.GetFallback() - } - if action, ok := currentAction.(ActionCareInvoker); ok { - action.SetInvoker(Instance) - } - currentAction.Action(tracker) - } - } - - if bs > 0 && sessionRootTracker != nil { - // Kill the Top1 session - if sessionRootTracker.NeedKill.Load() { - sessionRootTracker.NeedKillReceived.Do( - func() { - logutil.BgLogger().Warn("global memory controller, NeedKill signal is received successfully", - zap.Uint64("conn", sessionRootTracker.SessionID.Load())) - }) - tryActionLastOne(&sessionRootTracker.actionMuForHardLimit, sessionRootTracker) - } - // Update the Top1 session - memUsage := sessionRootTracker.BytesConsumed() - limitSessMinSize := ServerMemoryLimitSessMinSize.Load() - if uint64(memUsage) >= limitSessMinSize { - oldTracker := MemUsageTop1Tracker.Load() - for oldTracker.LessThan(sessionRootTracker) { - if MemUsageTop1Tracker.CompareAndSwap(oldTracker, sessionRootTracker) { - break - } - oldTracker = MemUsageTop1Tracker.Load() - } - } - } - - if bs > 0 && rootExceed != nil { - tryAction(&rootExceed.actionMuForHardLimit, rootExceed) - } - - if bs > 0 && rootExceedForSoftLimit != nil { - tryAction(&rootExceedForSoftLimit.actionMuForSoftLimit, rootExceedForSoftLimit) - } -} - -// BufferedConsume is used to buffer memory usage and do late consume -// not thread-safe, should be called in one goroutine -func (t *Tracker) BufferedConsume(bufferedMemSize *int64, bytes int64) { - *bufferedMemSize += bytes - if *bufferedMemSize >= int64(TrackMemWhenExceeds) { - t.Consume(*bufferedMemSize) - *bufferedMemSize = int64(0) - } -} - -// Release is used to release memory tracked, track the released memory until GC triggered if needed -// If you want your track to be GC-aware, please use Release(bytes) instead of Consume(-bytes), and pass the memory size of the real object. -// Only Analyze is integrated with Release so far. -func (t *Tracker) Release(bytes int64) { - if bytes == 0 { - return - } - defer t.Consume(-bytes) - for tracker := t; tracker != nil; tracker = tracker.getParent() { - if tracker.shouldRecordRelease() { - // use fake ref instead of obj ref, otherwise obj will be reachable again and gc in next cycle - newRef := &finalizerRef{} - finalizer := func(tracker *Tracker) func(ref *finalizerRef) { - return func(ref *finalizerRef) { - tracker.release(bytes) // finalizer func is called async - } - } - runtime.SetFinalizer(newRef, finalizer(tracker)) - tracker.recordRelease(bytes) - return - } - } -} - -// BufferedRelease is used to buffer memory release and do late release -// not thread-safe, should be called in one goroutine -func (t *Tracker) BufferedRelease(bufferedMemSize *int64, bytes int64) { - *bufferedMemSize += bytes - if *bufferedMemSize >= int64(TrackMemWhenExceeds) { - t.Release(*bufferedMemSize) - *bufferedMemSize = int64(0) - } -} - -func (t *Tracker) shouldRecordRelease() bool { - return EnableGCAwareMemoryTrack.Load() && t.label == LabelForGlobalAnalyzeMemory -} - -func (t *Tracker) recordRelease(bytes int64) { - for tracker := t; tracker != nil; tracker = tracker.getParent() { - bytesReleased := atomic.AddInt64(&tracker.bytesReleased, bytes) - if label, ok := MetricsTypes[tracker.label]; ok { - metrics.MemoryUsage.WithLabelValues(label[0], label[2]).Set(float64(bytesReleased)) - } - } -} - -func (t *Tracker) release(bytes int64) { - for tracker := t; tracker != nil; tracker = tracker.getParent() { - bytesReleased := atomic.AddInt64(&tracker.bytesReleased, -bytes) - if label, ok := MetricsTypes[tracker.label]; ok { - metrics.MemoryUsage.WithLabelValues(label[0], label[2]).Set(float64(bytesReleased)) - } - } -} - -// BytesConsumed returns the consumed memory usage value in bytes. -func (t *Tracker) BytesConsumed() int64 { - return atomic.LoadInt64(&t.bytesConsumed) -} - -// BytesReleased returns the released memory value in bytes. -func (t *Tracker) BytesReleased() int64 { - return atomic.LoadInt64(&t.bytesReleased) -} - -// MaxConsumed returns max number of bytes consumed during execution. -// Note: Don't make this method return -1 for special meanings in the future. Because binary plan has used -1 to -// distinguish between "0 bytes" and "N/A". ref: binaryOpFromFlatOp() -func (t *Tracker) MaxConsumed() int64 { - return t.maxConsumed.Load() -} - -// ResetMaxConsumed should be invoked before executing a new statement in a session. -func (t *Tracker) ResetMaxConsumed() { - t.maxConsumed.Store(t.BytesConsumed()) -} - -// SearchTrackerWithoutLock searches the specific tracker under this tracker without lock. -func (t *Tracker) SearchTrackerWithoutLock(label int) *Tracker { - if t.label == label { - return t - } - children := t.mu.children[label] - if len(children) > 0 { - return children[0] - } - return nil -} - -// SearchTrackerConsumedMoreThanNBytes searches the specific tracker that consumes more than NBytes. -func (t *Tracker) SearchTrackerConsumedMoreThanNBytes(limit int64) (res []*Tracker) { - t.mu.Lock() - defer t.mu.Unlock() - for _, childSlice := range t.mu.children { - for _, tracker := range childSlice { - if tracker.BytesConsumed() > limit { - res = append(res, tracker) - } - } - } - return -} - -// String returns the string representation of this Tracker tree. -func (t *Tracker) String() string { - buffer := bytes.NewBufferString("\n") - t.toString("", buffer) - return buffer.String() -} - -func (t *Tracker) toString(indent string, buffer *bytes.Buffer) { - fmt.Fprintf(buffer, "%s\"%d\"{\n", indent, t.label) - bytesLimit := t.GetBytesLimit() - if bytesLimit > 0 { - fmt.Fprintf(buffer, "%s \"quota\": %s\n", indent, t.FormatBytes(bytesLimit)) - } - fmt.Fprintf(buffer, "%s \"consumed\": %s\n", indent, t.FormatBytes(t.BytesConsumed())) - - t.mu.Lock() - labels := make([]int, 0, len(t.mu.children)) - for label := range t.mu.children { - labels = append(labels, label) - } - slices.Sort(labels) - for _, label := range labels { - children := t.mu.children[label] - for _, child := range children { - child.toString(indent+" ", buffer) - } - } - t.mu.Unlock() - buffer.WriteString(indent + "}\n") -} - -// FormatBytes uses to format bytes, this function will prune precision before format bytes. -func (*Tracker) FormatBytes(numBytes int64) string { - return FormatBytes(numBytes) -} - -// LessThan indicates whether t byteConsumed is less than t2 byteConsumed. -func (t *Tracker) LessThan(t2 *Tracker) bool { - if t == nil { - return true - } - if t2 == nil { - return false - } - return t.BytesConsumed() < t2.BytesConsumed() -} - -// BytesToString converts the memory consumption to a readable string. -func BytesToString(numBytes int64) string { - gb := float64(numBytes) / float64(byteSizeGB) - if gb > 1 { - return fmt.Sprintf("%v GB", gb) - } - - mb := float64(numBytes) / float64(byteSizeMB) - if mb > 1 { - return fmt.Sprintf("%v MB", mb) - } - - kb := float64(numBytes) / float64(byteSizeKB) - if kb > 1 { - return fmt.Sprintf("%v KB", kb) - } - - return fmt.Sprintf("%v Bytes", numBytes) -} - -const ( - byteSizeGB = int64(1 << 30) - byteSizeMB = int64(1 << 20) - byteSizeKB = int64(1 << 10) - byteSizeBB = int64(1) -) - -// FormatBytes uses to format bytes, this function will prune precision before format bytes. -func FormatBytes(numBytes int64) string { - if numBytes <= byteSizeKB { - return BytesToString(numBytes) - } - unit, unitStr := getByteUnit(numBytes) - if unit == byteSizeBB { - return BytesToString(numBytes) - } - v := float64(numBytes) / float64(unit) - decimal := 1 - if numBytes%unit == 0 { - decimal = 0 - } else if v < 10 { - decimal = 2 - } - return fmt.Sprintf("%v %s", strconv.FormatFloat(v, 'f', decimal, 64), unitStr) -} - -func getByteUnit(b int64) (int64, string) { - if b > byteSizeGB { - return byteSizeGB, "GB" - } else if b > byteSizeMB { - return byteSizeMB, "MB" - } else if b > byteSizeKB { - return byteSizeKB, "KB" - } - return byteSizeBB, "Bytes" -} - -// AttachToGlobalTracker attach the tracker to the global tracker -// AttachToGlobalTracker should be called at the initialization for the session executor's tracker -func (t *Tracker) AttachToGlobalTracker(globalTracker *Tracker) { - if globalTracker == nil { - return - } - if !globalTracker.isGlobal { - panic("Attach to a non-GlobalTracker") - } - parent := t.getParent() - if parent != nil { - if parent.isGlobal { - parent.Consume(-t.BytesConsumed()) - } else { - parent.remove(t) - } - } - t.setParent(globalTracker) - globalTracker.Consume(t.BytesConsumed()) -} - -// DetachFromGlobalTracker detach itself from its parent -// Note that only the parent of this tracker is Global Tracker could call this function -// Otherwise it should use Detach -func (t *Tracker) DetachFromGlobalTracker() { - parent := t.getParent() - if parent == nil { - return - } - if !parent.isGlobal { - panic("Detach from a non-GlobalTracker") - } - parent.Consume(-t.BytesConsumed()) - t.setParent(nil) -} - -// ReplaceBytesUsed replace bytesConsume for the tracker -func (t *Tracker) ReplaceBytesUsed(bytes int64) { - t.Consume(bytes - t.BytesConsumed()) -} - -// Reset detach the tracker from the old parent and clear the old children. The label and byteLimit would not be reset. -func (t *Tracker) Reset() { - t.Detach() - t.ReplaceBytesUsed(0) - t.mu.children = nil -} - -func (t *Tracker) getParent() *Tracker { - t.parMu.Lock() - defer t.parMu.Unlock() - return t.parMu.parent -} - -func (t *Tracker) setParent(parent *Tracker) { - t.parMu.Lock() - defer t.parMu.Unlock() - t.parMu.parent = parent -} - -// CountAllChildrenMemUse return memory used tree for the tracker -func (t *Tracker) CountAllChildrenMemUse() map[string]int64 { - trackerMemUseMap := make(map[string]int64, 1024) - countChildMem(t, "", trackerMemUseMap) - return trackerMemUseMap -} - -// GetChildrenForTest returns children trackers -func (t *Tracker) GetChildrenForTest() []*Tracker { - t.mu.Lock() - defer t.mu.Unlock() - trackers := make([]*Tracker, 0) - for _, list := range t.mu.children { - trackers = append(trackers, list...) - } - return trackers -} - -func countChildMem(t *Tracker, familyTreeName string, trackerMemUseMap map[string]int64) { - if len(familyTreeName) > 0 { - familyTreeName += " <- " - } - familyTreeName += "[" + strconv.Itoa(t.Label()) + "]" - trackerMemUseMap[familyTreeName] += t.BytesConsumed() - t.mu.Lock() - defer t.mu.Unlock() - for _, sli := range t.mu.children { - for _, tracker := range sli { - countChildMem(tracker, familyTreeName, trackerMemUseMap) - } - } -} - -const ( - // LabelForSQLText represents the label of the SQL Text - LabelForSQLText int = -1 - // LabelForIndexWorker represents the label of the index worker - LabelForIndexWorker int = -2 - // LabelForInnerList represents the label of the inner list - LabelForInnerList int = -3 - // LabelForInnerTable represents the label of the inner table - LabelForInnerTable int = -4 - // LabelForOuterTable represents the label of the outer table - LabelForOuterTable int = -5 - // LabelForCoprocessor represents the label of the coprocessor - LabelForCoprocessor int = -6 - // LabelForChunkList represents the label of the chunk list - LabelForChunkList int = -7 - // LabelForGlobalSimpleLRUCache represents the label of the Global SimpleLRUCache - LabelForGlobalSimpleLRUCache int = -8 - // LabelForChunkListInDisk represents the label of the chunk list in disk - LabelForChunkListInDisk int = -9 - // LabelForRowContainer represents the label of the row container - LabelForRowContainer int = -10 - // LabelForGlobalStorage represents the label of the Global Storage - LabelForGlobalStorage int = -11 - // LabelForGlobalMemory represents the label of the Global Memory - LabelForGlobalMemory int = -12 - // LabelForBuildSideResult represents the label of the BuildSideResult - LabelForBuildSideResult int = -13 - // LabelForRowChunks represents the label of the row chunks - LabelForRowChunks int = -14 - // LabelForStatsCache represents the label of the stats cache - LabelForStatsCache int = -15 - // LabelForOuterList represents the label of the outer list - LabelForOuterList int = -16 - // LabelForApplyCache represents the label of the apply cache - LabelForApplyCache int = -17 - // LabelForSimpleTask represents the label of the simple task - LabelForSimpleTask int = -18 - // LabelForCTEStorage represents the label of CTE storage - LabelForCTEStorage int = -19 - // LabelForIndexJoinInnerWorker represents the label of IndexJoin InnerWorker - LabelForIndexJoinInnerWorker int = -20 - // LabelForIndexJoinOuterWorker represents the label of IndexJoin OuterWorker - LabelForIndexJoinOuterWorker int = -21 - // LabelForBindCache represents the label of the bind cache - LabelForBindCache int = -22 - // LabelForNonTransactionalDML represents the label of the non-transactional DML - LabelForNonTransactionalDML = -23 - // LabelForAnalyzeMemory represents the label of the memory of each analyze job - LabelForAnalyzeMemory int = -24 - // LabelForGlobalAnalyzeMemory represents the label of the global memory of all analyze jobs - LabelForGlobalAnalyzeMemory int = -25 - // LabelForPreparedPlanCache represents the label of the prepared plan cache memory usage - LabelForPreparedPlanCache int = -26 - // LabelForSession represents the label of a session. - LabelForSession int = -27 - // LabelForMemDB represents the label of the MemDB - LabelForMemDB int = -28 - // LabelForCursorFetch represents the label of the execution of cursor fetch - LabelForCursorFetch int = -29 -) - -// MetricsTypes is used to get label for metrics -// string[0] is LblModule, string[1] is heap-in-use type, string[2] is released type -var MetricsTypes = map[int][]string{ - LabelForGlobalAnalyzeMemory: {"analyze", "inuse", "released"}, -} diff --git a/util/memoryusagealarm/BUILD.bazel b/util/memoryusagealarm/BUILD.bazel deleted file mode 100644 index fad2c48f20d65..0000000000000 --- a/util/memoryusagealarm/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "memoryusagealarm", - srcs = ["memoryusagealarm.go"], - importpath = "github.com/pingcap/tidb/util/memoryusagealarm", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//sessionctx/variable", - "//util", - "//util/disk", - "//util/logutil", - "//util/memory", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "memoryusagealarm_test", - timeout = "short", - srcs = ["memoryusagealarm_test.go"], - embed = [":memoryusagealarm"], - flaky = True, - race = "on", - deps = [ - "//sessionctx/stmtctx", - "//sessionctx/variable", - "//util", - "//util/memory", - "@com_github_stretchr_testify//assert", - ], -) diff --git a/util/metricsutil/BUILD.bazel b/util/metricsutil/BUILD.bazel deleted file mode 100644 index 0d01a684a2746..0000000000000 --- a/util/metricsutil/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metricsutil", - srcs = ["common.go"], - importpath = "github.com/pingcap/tidb/util/metricsutil", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//domain/metrics", - "//executor/metrics", - "//infoschema/metrics", - "//keyspace", - "//metrics", - "//planner/core/metrics", - "//server/metrics", - "//session/metrics", - "//session/txninfo", - "//sessiontxn/isolation/metrics", - "//statistics/handle/metrics", - "//store", - "//store/copr/metrics", - "//store/mockstore/unistore/metrics", - "//ttl/metrics", - "//util", - "//util/topsql/reporter/metrics", - "@com_github_pingcap_kvproto//pkg/keyspacepb", - "@com_github_tikv_client_go_v2//config", - "@com_github_tikv_pd_client//:client", - ], -) diff --git a/util/metricsutil/common.go b/util/metricsutil/common.go deleted file mode 100644 index e62233754272a..0000000000000 --- a/util/metricsutil/common.go +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package metricsutil - -import ( - "context" - "fmt" - "strings" - "time" - - "github.com/pingcap/kvproto/pkg/keyspacepb" - "github.com/pingcap/tidb/config" - domain_metrics "github.com/pingcap/tidb/domain/metrics" - executor_metrics "github.com/pingcap/tidb/executor/metrics" - infoschema_metrics "github.com/pingcap/tidb/infoschema/metrics" - "github.com/pingcap/tidb/keyspace" - "github.com/pingcap/tidb/metrics" - plannercore "github.com/pingcap/tidb/planner/core/metrics" - server_metrics "github.com/pingcap/tidb/server/metrics" - session_metrics "github.com/pingcap/tidb/session/metrics" - txninfo "github.com/pingcap/tidb/session/txninfo" - isolation_metrics "github.com/pingcap/tidb/sessiontxn/isolation/metrics" - statshandler_metrics "github.com/pingcap/tidb/statistics/handle/metrics" - kvstore "github.com/pingcap/tidb/store" - copr_metrics "github.com/pingcap/tidb/store/copr/metrics" - unimetrics "github.com/pingcap/tidb/store/mockstore/unistore/metrics" - ttlmetrics "github.com/pingcap/tidb/ttl/metrics" - "github.com/pingcap/tidb/util" - topsqlreporter_metrics "github.com/pingcap/tidb/util/topsql/reporter/metrics" - tikvconfig "github.com/tikv/client-go/v2/config" - pd "github.com/tikv/pd/client" -) - -// RegisterMetrics register metrics with const label 'keyspace_id' if keyspaceName set. -func RegisterMetrics() error { - cfg := config.GetGlobalConfig() - if keyspace.IsKeyspaceNameEmpty(cfg.KeyspaceName) || strings.ToLower(cfg.Store) != "tikv" { - return registerMetrics(nil) // register metrics without label 'keyspace_id'. - } - - pdAddrs, _, _, err := tikvconfig.ParsePath("tikv://" + cfg.Path) - if err != nil { - return err - } - - timeoutSec := time.Duration(cfg.PDClient.PDServerTimeout) * time.Second - pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{ - CAPath: cfg.Security.ClusterSSLCA, - CertPath: cfg.Security.ClusterSSLCert, - KeyPath: cfg.Security.ClusterSSLKey, - }, pd.WithCustomTimeoutOption(timeoutSec)) - if err != nil { - return err - } - defer pdCli.Close() - - keyspaceMeta, err := getKeyspaceMeta(pdCli, cfg.KeyspaceName) - if err != nil { - return err - } - - return registerMetrics(keyspaceMeta) -} - -// RegisterMetricsForBR register metrics with const label keyspace_id for BR. -func RegisterMetricsForBR(pdAddrs []string, keyspaceName string) error { - if keyspace.IsKeyspaceNameEmpty(keyspaceName) { - return registerMetrics(nil) // register metrics without label 'keyspace_id'. - } - - timeoutSec := 10 * time.Second - pdCli, err := pd.NewClient(pdAddrs, pd.SecurityOption{}, - pd.WithCustomTimeoutOption(timeoutSec)) - if err != nil { - return err - } - defer pdCli.Close() - - keyspaceMeta, err := getKeyspaceMeta(pdCli, keyspaceName) - if err != nil { - return err - } - - return registerMetrics(keyspaceMeta) -} - -func registerMetrics(keyspaceMeta *keyspacepb.KeyspaceMeta) error { - if keyspaceMeta != nil { - metrics.SetConstLabels("keyspace_id", fmt.Sprint(keyspaceMeta.GetId())) - } - - metrics.InitMetrics() - metrics.RegisterMetrics() - - copr_metrics.InitMetricsVars() - domain_metrics.InitMetricsVars() - executor_metrics.InitMetricsVars() - infoschema_metrics.InitMetricsVars() - isolation_metrics.InitMetricsVars() - plannercore.InitMetricsVars() - server_metrics.InitMetricsVars() - session_metrics.InitMetricsVars() - statshandler_metrics.InitMetricsVars() - topsqlreporter_metrics.InitMetricsVars() - ttlmetrics.InitMetricsVars() - txninfo.InitMetricsVars() - - if config.GetGlobalConfig().Store == "unistore" { - unimetrics.RegisterMetrics() - } - return nil -} - -func getKeyspaceMeta(pdCli pd.Client, keyspaceName string) (*keyspacepb.KeyspaceMeta, error) { - // Load Keyspace meta with retry. - var keyspaceMeta *keyspacepb.KeyspaceMeta - err := util.RunWithRetry(util.DefaultMaxRetries, util.RetryInterval, func() (bool, error) { - var errInner error - keyspaceMeta, errInner = pdCli.LoadKeyspace(context.TODO(), keyspaceName) - // Retry when pd not bootstrapped or if keyspace not exists. - if kvstore.IsNotBootstrappedError(errInner) || kvstore.IsKeyspaceNotExistError(errInner) { - return true, errInner - } - // Do not retry when success or encountered unexpected error. - return false, errInner - }) - if err != nil { - return nil, err - } - - return keyspaceMeta, nil -} diff --git a/util/misc.go b/util/misc.go deleted file mode 100644 index ff3f133217bbd..0000000000000 --- a/util/misc.go +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package util - -import ( - "context" - "crypto" - "crypto/ecdsa" - "crypto/ed25519" - "crypto/elliptic" - "crypto/rand" - "crypto/rsa" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "fmt" - "math/big" - "net" - "net/http" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/metrics" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/util/collate" - "github.com/pingcap/tidb/util/logutil" - tlsutil "github.com/pingcap/tidb/util/tls" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -const ( - // DefaultMaxRetries indicates the max retry count. - DefaultMaxRetries = 30 - // RetryInterval indicates retry interval. - RetryInterval uint64 = 500 -) - -// RunWithRetry will run the f with backoff and retry. -// retryCnt: Max retry count -// backoff: When run f failed, it will sleep backoff * triedCount time.Millisecond. -// Function f should have two return value. The first one is an bool which indicate if the err if retryable. -// The second is if the f meet any error. -func RunWithRetry(retryCnt int, backoff uint64, f func() (bool, error)) (err error) { - for i := 1; i <= retryCnt; i++ { - var retryAble bool - retryAble, err = f() - if err == nil || !retryAble { - return errors.Trace(err) - } - sleepTime := time.Duration(backoff*uint64(i)) * time.Millisecond - time.Sleep(sleepTime) - } - return errors.Trace(err) -} - -// WithRecovery wraps goroutine startup call with force recovery. -// it will dump current goroutine stack into log if catch any recover result. -// -// exec: execute logic function. -// recoverFn: handler will be called after recover and before dump stack, passing `nil` means noop. -func WithRecovery(exec func(), recoverFn func(r interface{})) { - defer func() { - r := recover() - if recoverFn != nil { - recoverFn(r) - } - if r != nil { - logutil.BgLogger().Error("panic in the recoverable goroutine", - zap.Any("r", r), - zap.Stack("stack trace")) - } - }() - exec() -} - -// Recover includes operations such as recovering, clearing,and printing information. -// It will dump current goroutine stack into log if catch any recover result. -// -// metricsLabel: The label of PanicCounter metrics. -// funcInfo: Some information for the panic function. -// recoverFn: Handler will be called after recover and before dump stack, passing `nil` means noop. -// quit: If this value is true, the current program exits after recovery. -func Recover(metricsLabel, funcInfo string, recoverFn func(), quit bool) { - //nolint: revive - r := recover() - if r == nil { - return - } - - if recoverFn != nil { - recoverFn() - } - logutil.BgLogger().Error("panic in the recoverable goroutine", - zap.String("label", metricsLabel), - zap.String("funcInfo", funcInfo), - zap.Any("r", r), - zap.Stack("stack")) - metrics.PanicCounter.WithLabelValues(metricsLabel).Inc() - if quit { - // Wait for metrics to be pushed. - time.Sleep(time.Second * 15) - os.Exit(1) - } -} - -// HasCancelled checks whether context has be cancelled. -func HasCancelled(ctx context.Context) (cancel bool) { - select { - case <-ctx.Done(): - cancel = true - default: - } - return -} - -const ( - // syntaxErrorPrefix is the common prefix for SQL syntax error in TiDB. - syntaxErrorPrefix = "You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use" -) - -// SyntaxError converts parser error to TiDB's syntax error. -func SyntaxError(err error) error { - if err == nil { - return nil - } - logutil.BgLogger().Debug("syntax error", zap.Error(err)) - - // If the error is already a terror with stack, pass it through. - if errors.HasStack(err) { - cause := errors.Cause(err) - if _, ok := cause.(*terror.Error); ok { - return err - } - } - - return parser.ErrParse.GenWithStackByArgs(syntaxErrorPrefix, err.Error()) -} - -// SyntaxWarn converts parser warn to TiDB's syntax warn. -func SyntaxWarn(err error) error { - if err == nil { - return nil - } - logutil.BgLogger().Debug("syntax error", zap.Error(err)) - - // If the warn is already a terror with stack, pass it through. - if errors.HasStack(err) { - cause := errors.Cause(err) - if _, ok := cause.(*terror.Error); ok { - return err - } - } - - return parser.ErrParse.GenWithStackByArgs(syntaxErrorPrefix, err.Error()) -} - -var ( - // InformationSchemaName is the `INFORMATION_SCHEMA` database name. - InformationSchemaName = model.NewCIStr("INFORMATION_SCHEMA") - // PerformanceSchemaName is the `PERFORMANCE_SCHEMA` database name. - PerformanceSchemaName = model.NewCIStr("PERFORMANCE_SCHEMA") - // MetricSchemaName is the `METRICS_SCHEMA` database name. - MetricSchemaName = model.NewCIStr("METRICS_SCHEMA") - // ClusterTableInstanceColumnName is the `INSTANCE` column name of the cluster table. - ClusterTableInstanceColumnName = "INSTANCE" -) - -// IsMemOrSysDB uses to check whether dbLowerName is memory database or system database. -func IsMemOrSysDB(dbLowerName string) bool { - return IsMemDB(dbLowerName) || IsSysDB(dbLowerName) -} - -// IsMemDB checks whether dbLowerName is memory database. -func IsMemDB(dbLowerName string) bool { - switch dbLowerName { - case InformationSchemaName.L, - PerformanceSchemaName.L, - MetricSchemaName.L: - return true - } - return false -} - -// IsSysDB checks whether dbLowerName is system database. -func IsSysDB(dbLowerName string) bool { - return dbLowerName == mysql.SystemDB -} - -// IsSystemView is similar to IsMemOrSyDB, but does not include the mysql schema -func IsSystemView(dbLowerName string) bool { - switch dbLowerName { - case InformationSchemaName.L, - PerformanceSchemaName.L, - MetricSchemaName.L: - return true - } - return false -} - -// X509NameOnline prints pkix.Name into old X509_NAME_oneline format. -// https://www.openssl.org/docs/manmaster/man3/X509_NAME_oneline.html -func X509NameOnline(n pkix.Name) string { - s := make([]string, 0, len(n.Names)) - for _, name := range n.Names { - oid := name.Type.String() - // unlike MySQL, TiDB only support check pkixAttributeTypeNames fields - if n, exist := pkixAttributeTypeNames[oid]; exist { - s = append(s, n+"="+fmt.Sprint(name.Value)) - } - } - if len(s) == 0 { - return "" - } - return "/" + strings.Join(s, "/") -} - -const ( - // Country is type name for country. - Country = "C" - // Organization is type name for organization. - Organization = "O" - // OrganizationalUnit is type name for organizational unit. - OrganizationalUnit = "OU" - // Locality is type name for locality. - Locality = "L" - // Email is type name for email. - Email = "emailAddress" - // CommonName is type name for common name. - CommonName = "CN" - // Province is type name for province or state. - Province = "ST" -) - -// see go/src/crypto/x509/pkix/pkix.go:attributeTypeNames -var pkixAttributeTypeNames = map[string]string{ - "2.5.4.6": Country, - "2.5.4.10": Organization, - "2.5.4.11": OrganizationalUnit, - "2.5.4.3": CommonName, - "2.5.4.5": "SERIALNUMBER", - "2.5.4.7": Locality, - "2.5.4.8": Province, - "2.5.4.9": "STREET", - "2.5.4.17": "POSTALCODE", - "1.2.840.113549.1.9.1": Email, -} - -var pkixTypeNameAttributes = make(map[string]string) - -// MockPkixAttribute generates mock AttributeTypeAndValue. -// only used for test. -func MockPkixAttribute(name, value string) pkix.AttributeTypeAndValue { - n, exists := pkixTypeNameAttributes[name] - if !exists { - panic(fmt.Sprintf("unsupport mock type: %s", name)) - } - split := strings.Split(n, ".") - vs := make([]int, 0, len(split)) - for _, v := range split { - i, err := strconv.Atoi(v) - if err != nil { - panic(err) - } - vs = append(vs, i) - } - return pkix.AttributeTypeAndValue{ - Type: vs, - Value: value, - } -} - -// SANType is enum value for GlobalPrivValue.SANs keys. -type SANType string - -const ( - // URI indicates uri info in SAN. - URI = SANType("URI") - // DNS indicates dns info in SAN. - DNS = SANType("DNS") - // IP indicates ip info in SAN. - IP = SANType("IP") -) - -var supportSAN = map[SANType]struct{}{ - URI: {}, - DNS: {}, - IP: {}, -} - -// ParseAndCheckSAN parses and check SAN str. -func ParseAndCheckSAN(san string) (map[SANType][]string, error) { - sanMap := make(map[SANType][]string) - sans := strings.Split(san, ",") - for _, san := range sans { - kv := strings.SplitN(san, ":", 2) - if len(kv) != 2 { - return nil, errors.Errorf("invalid SAN value %s", san) - } - k, v := SANType(strings.ToUpper(strings.TrimSpace(kv[0]))), strings.TrimSpace(kv[1]) - if _, s := supportSAN[k]; !s { - return nil, errors.Errorf("unsupported SAN key %s, current only support %v", k, supportSAN) - } - sanMap[k] = append(sanMap[k], v) - } - return sanMap, nil -} - -// CheckSupportX509NameOneline parses and validate input str is X509_NAME_oneline format -// and precheck check-item is supported by TiDB -// https://www.openssl.org/docs/manmaster/man3/X509_NAME_oneline.html -func CheckSupportX509NameOneline(oneline string) (err error) { - entries := strings.Split(oneline, `/`) - for _, entry := range entries { - if len(entry) == 0 { - continue - } - kvs := strings.Split(entry, "=") - if len(kvs) != 2 { - err = errors.Errorf("invalid X509_NAME input: %s", oneline) - return - } - k := kvs[0] - if _, support := pkixTypeNameAttributes[k]; !support { - err = errors.Errorf("Unsupport check '%s' in current version TiDB", k) - return - } - } - return -} - -var tlsCipherString = map[uint16]string{ - tls.TLS_RSA_WITH_RC4_128_SHA: "RC4-SHA", - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "DES-CBC3-SHA", - tls.TLS_RSA_WITH_AES_128_CBC_SHA: "AES128-SHA", - tls.TLS_RSA_WITH_AES_256_CBC_SHA: "AES256-SHA", - tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "AES128-SHA256", - tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "AES128-GCM-SHA256", - tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "AES256-GCM-SHA384", - tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "ECDHE-ECDSA-RC4-SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "ECDHE-ECDSA-AES128-SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "ECDHE-ECDSA-AES256-SHA", - tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "ECDHE-RSA-RC4-SHA", - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "ECDHE-RSA-DES-CBC3-SHA", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "ECDHE-RSA-AES128-SHA", - tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "ECDHE-RSA-AES256-SHA", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "ECDHE-ECDSA-AES128-SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "ECDHE-RSA-AES128-SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "ECDHE-RSA-AES128-GCM-SHA256", - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "ECDHE-ECDSA-AES128-GCM-SHA256", - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "ECDHE-RSA-AES256-GCM-SHA384", - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "ECDHE-ECDSA-AES256-GCM-SHA384", - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "ECDHE-RSA-CHACHA20-POLY1305", - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "ECDHE-ECDSA-CHACHA20-POLY1305", - // TLS 1.3 cipher suites, compatible with mysql using '_'. - tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256", - tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384", - tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256", -} - -// SupportCipher maintains cipher supported by TiDB. -var SupportCipher = make(map[string]struct{}, len(tlsCipherString)) - -// TLSCipher2String convert tls num to string. -// Taken from https://testssl.sh/openssl-rfc.mapping.html . -func TLSCipher2String(n uint16) string { - s, ok := tlsCipherString[n] - if !ok { - return "" - } - return s -} - -// ColumnsToProto converts a slice of model.ColumnInfo to a slice of tipb.ColumnInfo. -func ColumnsToProto(columns []*model.ColumnInfo, pkIsHandle bool, forIndex bool) []*tipb.ColumnInfo { - cols := make([]*tipb.ColumnInfo, 0, len(columns)) - for _, c := range columns { - col := ColumnToProto(c, forIndex) - // TODO: Here `PkHandle`'s meaning is changed, we will change it to `IsHandle` when tikv's old select logic - // is abandoned. - if (pkIsHandle && mysql.HasPriKeyFlag(c.GetFlag())) || c.ID == model.ExtraHandleID { - col.PkHandle = true - } else { - col.PkHandle = false - } - cols = append(cols, col) - } - return cols -} - -// ColumnToProto converts model.ColumnInfo to tipb.ColumnInfo. -func ColumnToProto(c *model.ColumnInfo, forIndex bool) *tipb.ColumnInfo { - pc := &tipb.ColumnInfo{ - ColumnId: c.ID, - Collation: collate.RewriteNewCollationIDIfNeeded(int32(mysql.CollationNames[c.GetCollate()])), - ColumnLen: int32(c.GetFlen()), - Decimal: int32(c.GetDecimal()), - Flag: int32(c.GetFlag()), - Elems: c.GetElems(), - } - if forIndex { - // Use array type for read the multi-valued index. - pc.Tp = int32(c.FieldType.ArrayType().GetType()) - if c.FieldType.IsArray() { - // Use "binary" collation for read the multi-valued index. Most of the time, the `Collation` of this hidden - // column should already been set to "binary". However, in old versions, the collation is set to the default - // value. See https://github.com/pingcap/tidb/issues/46717 - pc.Collation = int32(mysql.CollationNames["binary"]) - } - } else { - pc.Tp = int32(c.GetType()) - } - return pc -} - -func init() { - for _, value := range tlsCipherString { - SupportCipher[value] = struct{}{} - } - for key, value := range pkixAttributeTypeNames { - pkixTypeNameAttributes[value] = key - } -} - -// GetSequenceByName could be used in expression package without import cycle problem. -var GetSequenceByName func(is interface{}, schema, sequence model.CIStr) (SequenceTable, error) - -// SequenceTable is implemented by tableCommon, -// and it is specialised in handling sequence operation. -// Otherwise calling table will cause import cycle problem. -type SequenceTable interface { - GetSequenceID() int64 - GetSequenceNextVal(ctx interface{}, dbName, seqName string) (int64, error) - SetSequenceVal(ctx interface{}, newVal int64, dbName, seqName string) (int64, bool, error) -} - -// LoadTLSCertificates loads CA/KEY/CERT for special paths. -func LoadTLSCertificates(ca, key, cert string, autoTLS bool, rsaKeySize int) (tlsConfig *tls.Config, autoReload bool, err error) { - autoReload = false - if len(cert) == 0 || len(key) == 0 { - if !autoTLS { - logutil.BgLogger().Warn("Automatic TLS Certificate creation is disabled", zap.Error(err)) - return - } - autoReload = true - tempStoragePath := config.GetGlobalConfig().TempStoragePath - cert = filepath.Join(tempStoragePath, "/cert.pem") - key = filepath.Join(tempStoragePath, "/key.pem") - err = createTLSCertificates(cert, key, rsaKeySize) - if err != nil { - logutil.BgLogger().Warn("TLS Certificate creation failed", zap.Error(err)) - return - } - } - - var tlsCert tls.Certificate - tlsCert, err = tls.LoadX509KeyPair(cert, key) - if err != nil { - logutil.BgLogger().Warn("load x509 failed", zap.Error(err)) - err = errors.Trace(err) - return - } - - requireTLS := tlsutil.RequireSecureTransport.Load() - - var minTLSVersion uint16 = tls.VersionTLS11 - switch tlsver := config.GetGlobalConfig().Security.MinTLSVersion; tlsver { - case "TLSv1.0": - minTLSVersion = tls.VersionTLS10 - case "TLSv1.1": - minTLSVersion = tls.VersionTLS11 - case "TLSv1.2": - minTLSVersion = tls.VersionTLS12 - case "TLSv1.3": - minTLSVersion = tls.VersionTLS13 - case "": - default: - logutil.BgLogger().Warn( - "Invalid TLS version, using default instead", - zap.String("tls-version", tlsver), - ) - } - if minTLSVersion < tls.VersionTLS12 { - logutil.BgLogger().Warn( - "Minimum TLS version allows pre-TLSv1.2 protocols, this is not recommended", - ) - } - - // Try loading CA cert. - clientAuthPolicy := tls.NoClientCert - if requireTLS { - clientAuthPolicy = tls.RequestClientCert - } - var certPool *x509.CertPool - if len(ca) > 0 { - var caCert []byte - caCert, err = os.ReadFile(ca) - if err != nil { - logutil.BgLogger().Warn("read file failed", zap.Error(err)) - err = errors.Trace(err) - return - } - certPool = x509.NewCertPool() - if certPool.AppendCertsFromPEM(caCert) { - if requireTLS { - clientAuthPolicy = tls.RequireAndVerifyClientCert - } else { - clientAuthPolicy = tls.VerifyClientCertIfGiven - } - } - } - - // This excludes ciphers listed in tls.InsecureCipherSuites() and can be used to filter out more - var cipherSuites []uint16 - var cipherNames []string - for _, sc := range tls.CipherSuites() { - switch sc.ID { - case tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: - logutil.BgLogger().Info("Disabling weak cipherSuite", zap.String("cipherSuite", sc.Name)) - default: - cipherNames = append(cipherNames, sc.Name) - cipherSuites = append(cipherSuites, sc.ID) - } - } - logutil.BgLogger().Info("Enabled ciphersuites", zap.Strings("cipherNames", cipherNames)) - - /* #nosec G402 */ - tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{tlsCert}, - ClientCAs: certPool, - ClientAuth: clientAuthPolicy, - MinVersion: minTLSVersion, - CipherSuites: cipherSuites, - } - return -} - -var ( - internalClientInit sync.Once - internalHTTPClient *http.Client - internalHTTPSchema string -) - -// InternalHTTPClient is used by TiDB-Server to request other components. -func InternalHTTPClient() *http.Client { - internalClientInit.Do(initInternalClient) - return internalHTTPClient -} - -// InternalHTTPSchema specifies use http or https to request other components. -func InternalHTTPSchema() string { - internalClientInit.Do(initInternalClient) - return internalHTTPSchema -} - -func initInternalClient() { - clusterSecurity := config.GetGlobalConfig().Security.ClusterSecurity() - tlsCfg, err := clusterSecurity.ToTLSConfig() - if err != nil { - logutil.BgLogger().Fatal("could not load cluster ssl", zap.Error(err)) - } - if tlsCfg == nil { - internalHTTPSchema = "http" - internalHTTPClient = http.DefaultClient - return - } - internalHTTPSchema = "https" - internalHTTPClient = &http.Client{ - Transport: &http.Transport{TLSClientConfig: tlsCfg}, - } -} - -// ComposeURL adds HTTP schema if missing and concats address with path -func ComposeURL(address, path string) string { - if strings.HasPrefix(address, "http://") || strings.HasPrefix(address, "https://") { - return fmt.Sprintf("%s%s", address, path) - } - return fmt.Sprintf("%s://%s%s", InternalHTTPSchema(), address, path) -} - -// GetLocalIP will return a local IP(non-loopback, non 0.0.0.0), if there is one -func GetLocalIP() string { - addrs, err := net.InterfaceAddrs() - if err == nil { - for _, address := range addrs { - ipnet, ok := address.(*net.IPNet) - if ok && ipnet.IP.IsGlobalUnicast() { - return ipnet.IP.String() - } - } - } - return "" -} - -// CreateCertificates creates and writes a cert based on the params. -func CreateCertificates(certpath string, keypath string, rsaKeySize int, pubKeyAlgo x509.PublicKeyAlgorithm, - signAlgo x509.SignatureAlgorithm) error { - certValidity := 90 * 24 * time.Hour // 90 days - notBefore := time.Now() - notAfter := notBefore.Add(certValidity) - hostname, err := os.Hostname() - if err != nil { - return err - } - - template := x509.Certificate{ - Subject: pkix.Name{ - CommonName: "TiDB_Server_Auto_Generated_Server_Certificate", - }, - SerialNumber: big.NewInt(1), - NotBefore: notBefore, - NotAfter: notAfter, - DNSNames: []string{hostname}, - SignatureAlgorithm: signAlgo, - } - - var privKey crypto.Signer - switch pubKeyAlgo { - case x509.RSA: - privKey, err = rsa.GenerateKey(rand.Reader, rsaKeySize) - case x509.ECDSA: - privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - case x509.Ed25519: - _, privKey, err = ed25519.GenerateKey(rand.Reader) - default: - return errors.Errorf("unknown public key algorithm: %s", pubKeyAlgo.String()) - } - if err != nil { - return err - } - // DER: Distinguished Encoding Rules, this is the ASN.1 encoding rule of the certificate. - derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, privKey.Public(), privKey) - if err != nil { - return err - } - - certOut, err := os.Create(certpath) - if err != nil { - return err - } - if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil { - return err - } - if err := certOut.Close(); err != nil { - return err - } - - keyOut, err := os.OpenFile(keypath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - return err - } - - privBytes, err := x509.MarshalPKCS8PrivateKey(privKey) - if err != nil { - return err - } - - if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil { - return err - } - - if err := keyOut.Close(); err != nil { - return err - } - - logutil.BgLogger().Info("TLS Certificates created", zap.String("cert", certpath), zap.String("key", keypath), - zap.Duration("validity", certValidity), zap.Int("rsaKeySize", rsaKeySize)) - return nil -} - -func createTLSCertificates(certpath string, keypath string, rsaKeySize int) error { - // use RSA and unspecified signature algorithm - return CreateCertificates(certpath, keypath, rsaKeySize, x509.RSA, x509.UnknownSignatureAlgorithm) -} diff --git a/util/misc_test.go b/util/misc_test.go deleted file mode 100644 index aac12016da84d..0000000000000 --- a/util/misc_test.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2016 PingCAP, Inc. -// -// 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. - -package util - -import ( - "bytes" - "crypto/x509/pkix" - "fmt" - "testing" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/fastrand" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/assert" -) - -func TestRunWithRetry(t *testing.T) { - t.Run("success", func(t *testing.T) { - cnt := 0 - err := RunWithRetry(3, 1, func() (bool, error) { - cnt++ - if cnt < 2 { - return true, errors.New("err") - } - return true, nil - }) - assert.Nil(t, err) - assert.Equal(t, 2, cnt) - }) - - t.Run("retry exceeds", func(t *testing.T) { - cnt := 0 - err := RunWithRetry(3, 1, func() (bool, error) { - cnt++ - if cnt < 4 { - return true, errors.New("err") - } - return true, nil - }) - assert.NotNil(t, err) - assert.Equal(t, 3, cnt) - }) - - t.Run("failed result", func(t *testing.T) { - cnt := 0 - err := RunWithRetry(3, 1, func() (bool, error) { - cnt++ - if cnt < 2 { - return false, errors.New("err") - } - return true, nil - }) - assert.NotNil(t, err) - assert.Equal(t, 1, cnt) - }) -} - -func TestX509NameParseMatch(t *testing.T) { - assert.Equal(t, "", X509NameOnline(pkix.Name{})) - - check := pkix.Name{ - Names: []pkix.AttributeTypeAndValue{ - MockPkixAttribute(Country, "SE"), - MockPkixAttribute(Province, "Stockholm2"), - MockPkixAttribute(Locality, "Stockholm"), - MockPkixAttribute(Organization, "MySQL demo client certificate"), - MockPkixAttribute(OrganizationalUnit, "testUnit"), - MockPkixAttribute(CommonName, "client"), - MockPkixAttribute(Email, "client@example.com"), - }, - } - result := "/C=SE/ST=Stockholm2/L=Stockholm/O=MySQL demo client certificate/OU=testUnit/CN=client/emailAddress=client@example.com" - assert.Equal(t, result, X509NameOnline(check)) -} - -func TestBasicFuncWithRecovery(t *testing.T) { - var recovery interface{} - WithRecovery(func() { - panic("test") - }, func(r interface{}) { - recovery = r - }) - assert.Equal(t, "test", recovery) -} - -func TestBasicFuncSyntaxError(t *testing.T) { - assert.Nil(t, SyntaxError(nil)) - assert.True(t, terror.ErrorEqual(SyntaxError(errors.New("test")), parser.ErrParse)) - assert.True(t, terror.ErrorEqual(SyntaxError(parser.ErrSyntax.GenWithStackByArgs()), parser.ErrSyntax)) -} - -func TestBasicFuncSyntaxWarn(t *testing.T) { - assert.Nil(t, SyntaxWarn(nil)) - assert.True(t, terror.ErrorEqual(SyntaxWarn(errors.New("test")), parser.ErrParse)) -} - -func TestBasicFuncProcessInfo(t *testing.T) { - sc := stmtctx.NewStmtCtx() - sc.MemTracker = memory.NewTracker(-1, -1) - pi := ProcessInfo{ - ID: 1, - User: "test", - Host: "www", - DB: "db", - Command: mysql.ComSleep, - Plan: nil, - Time: time.Now(), - State: 3, - Info: "test", - StmtCtx: sc, - } - row := pi.ToRowForShow(false) - row2 := pi.ToRowForShow(true) - assert.Equal(t, row2, row) - assert.Len(t, row, 8) - assert.Equal(t, pi.ID, row[0]) - assert.Equal(t, pi.User, row[1]) - assert.Equal(t, pi.Host, row[2]) - assert.Equal(t, pi.DB, row[3]) - assert.Equal(t, "Sleep", row[4]) - assert.Equal(t, uint64(0), row[5]) - assert.Equal(t, "in transaction; autocommit", row[6]) - assert.Equal(t, "test", row[7]) - - row3 := pi.ToRow(time.UTC) - assert.Equal(t, row, row3[:8]) - assert.Equal(t, int64(0), row3[9]) -} - -func TestBasicFuncRandomBuf(t *testing.T) { - buf := fastrand.Buf(5) - assert.Len(t, buf, 5) - assert.False(t, bytes.Contains(buf, []byte("$"))) - assert.False(t, bytes.Contains(buf, []byte{0})) -} - -func TestToPB(t *testing.T) { - column := &model.ColumnInfo{ - ID: 1, - Name: model.NewCIStr("c"), - Offset: 0, - DefaultValue: 0, - FieldType: *types.NewFieldType(0), - Hidden: true, - } - column.SetCollate("utf8mb4_general_ci") - - column2 := &model.ColumnInfo{ - ID: 1, - Name: model.NewCIStr("c"), - Offset: 0, - DefaultValue: 0, - FieldType: *types.NewFieldType(0), - Hidden: true, - } - column2.SetCollate("utf8mb4_bin") - - assert.Equal(t, "column_id:1 collation:-45 columnLen:-1 decimal:-1 ", ColumnToProto(column, false).String()) - assert.Equal(t, "column_id:1 collation:-45 columnLen:-1 decimal:-1 ", ColumnsToProto([]*model.ColumnInfo{column, column2}, false, false)[0].String()) -} - -func TestComposeURL(t *testing.T) { - // TODO Setup config for TLS and verify https protocol output - assert.Equal(t, ComposeURL("server.example.com", ""), "http://server.example.com") - assert.Equal(t, ComposeURL("httpserver.example.com", ""), "http://httpserver.example.com") - assert.Equal(t, ComposeURL("http://httpserver.example.com", "/"), "http://httpserver.example.com/") - assert.Equal(t, ComposeURL("https://httpserver.example.com", "/api/test"), "https://httpserver.example.com/api/test") - assert.Equal(t, ComposeURL("http://server.example.com", ""), "http://server.example.com") - assert.Equal(t, ComposeURL("https://server.example.com", ""), "https://server.example.com") -} - -func assertChannel[T any](t *testing.T, ch <-chan T, items ...T) { - for i, item := range items { - assert.Equal(t, <-ch, item, "the %d-th item doesn't match", i) - } - select { - case item, ok := <-ch: - assert.False(t, ok, "channel not closed: more item %v", item) - case <-time.After(50 * time.Microsecond): - t.Fatal("channel not closed: blocked") - } -} - -func TestChannelMap(t *testing.T) { - ch := make(chan int, 4) - ch <- 1 - ch <- 2 - ch <- 3 - - tableCh := ChanMap(ch, func(i int) string { - return fmt.Sprintf("table%d", i) - }) - close(ch) - - assertChannel(t, tableCh, "table1", "table2", "table3") -} diff --git a/util/mock/BUILD.bazel b/util/mock/BUILD.bazel deleted file mode 100644 index 556a2ac195477..0000000000000 --- a/util/mock/BUILD.bazel +++ /dev/null @@ -1,57 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mock", - srcs = [ - "client.go", - "context.go", - "iter.go", - "metrics.go", - "store.go", - ], - importpath = "github.com/pingcap/tidb/util/mock", - visibility = ["//visibility:public"], - deps = [ - "//extension", - "//kv", - "//parser/ast", - "//parser/model", - "//parser/terror", - "//sessionctx", - "//sessionctx/sessionstates", - "//sessionctx/variable", - "//util", - "//util/disk", - "//util/memory", - "//util/sli", - "//util/sqlexec", - "//util/topsql/stmtstats", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/deadlock", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-binlog", - "@com_github_prometheus_client_golang//prometheus", - "@com_github_stretchr_testify//assert", - "@com_github_tikv_client_go_v2//oracle", - "@com_github_tikv_client_go_v2//tikv", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "mock_test", - timeout = "short", - srcs = [ - "iter_test.go", - "main_test.go", - "mock_test.go", - ], - embed = [":mock"], - flaky = True, - deps = [ - "//kv", - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/mock/client.go b/util/mock/client.go deleted file mode 100644 index ffcf393cc99fd..0000000000000 --- a/util/mock/client.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "context" - - "github.com/pingcap/tidb/kv" -) - -// Client implement kv.Client interface, mocked from "CopClient" defined in -// "store/tikv/copprocessor.go". -type Client struct { - kv.RequestTypeSupportedChecker - MockResponse kv.Response -} - -// Send implement kv.Client interface. -func (c *Client) Send(_ context.Context, _ *kv.Request, _ interface{}, _ *kv.ClientSendOption) kv.Response { - return c.MockResponse -} diff --git a/util/mock/context.go b/util/mock/context.go deleted file mode 100644 index 38e09ec72a6ec..0000000000000 --- a/util/mock/context.go +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2015 PingCAP, Inc. -// -// 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. - -// Package mock is just for test only. -package mock - -import ( - "context" - "fmt" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/kvproto/pkg/kvrpcpb" - "github.com/pingcap/tidb/extension" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/terror" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/sessionstates" - "github.com/pingcap/tidb/sessionctx/variable" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/disk" - "github.com/pingcap/tidb/util/memory" - "github.com/pingcap/tidb/util/sli" - "github.com/pingcap/tidb/util/sqlexec" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "github.com/pingcap/tipb/go-binlog" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" -) - -var ( - _ sessionctx.Context = (*Context)(nil) - _ sqlexec.SQLExecutor = (*Context)(nil) -) - -// Context represents mocked sessionctx.Context. -type Context struct { - txn wrapTxn // mock global variable - Store kv.Storage // mock global variable - ctx context.Context - sm util.SessionManager - is sessionctx.InfoschemaMetaVersion - values map[fmt.Stringer]interface{} - sessionVars *variable.SessionVars - cancel context.CancelFunc - pcache sessionctx.PlanCache - level kvrpcpb.DiskFullOpt - inSandBoxMode bool -} - -type wrapTxn struct { - kv.Transaction - tsFuture oracle.Future -} - -func (txn *wrapTxn) validOrPending() bool { - return txn.tsFuture != nil || txn.Transaction.Valid() -} - -func (txn *wrapTxn) pending() bool { - return txn.Transaction == nil && txn.tsFuture != nil -} - -// Wait creates a new kvTransaction -func (txn *wrapTxn) Wait(_ context.Context, sctx sessionctx.Context) (kv.Transaction, error) { - if !txn.validOrPending() { - return txn, errors.AddStack(kv.ErrInvalidTxn) - } - if txn.pending() { - ts, err := txn.tsFuture.Wait() - if err != nil { - return nil, err - } - kvTxn, err := sctx.GetStore().Begin(tikv.WithStartTS(ts)) - if err != nil { - return nil, errors.Trace(err) - } - txn.Transaction = kvTxn - } - return txn, nil -} - -func (txn *wrapTxn) Valid() bool { - return txn.Transaction != nil && txn.Transaction.Valid() -} - -func (txn *wrapTxn) CacheTableInfo(id int64, info *model.TableInfo) { - if txn.Transaction == nil { - return - } - txn.Transaction.CacheTableInfo(id, info) -} - -func (txn *wrapTxn) GetTableInfo(id int64) *model.TableInfo { - if txn.Transaction == nil { - return nil - } - return txn.Transaction.GetTableInfo(id) -} - -// Execute implements sqlexec.SQLExecutor Execute interface. -func (*Context) Execute(_ context.Context, _ string) ([]sqlexec.RecordSet, error) { - return nil, errors.Errorf("Not Supported") -} - -// ExecuteStmt implements sqlexec.SQLExecutor ExecuteStmt interface. -func (*Context) ExecuteStmt(_ context.Context, _ ast.StmtNode) (sqlexec.RecordSet, error) { - return nil, errors.Errorf("Not Supported") -} - -// SetDiskFullOpt sets allowed options of current operation in each TiKV disk usage level. -func (c *Context) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) { - c.level = level -} - -// ClearDiskFullOpt clears allowed options of current operation in each TiKV disk usage level. -func (c *Context) ClearDiskFullOpt() { - c.level = kvrpcpb.DiskFullOpt_NotAllowedOnFull -} - -// ExecuteInternal implements sqlexec.SQLExecutor ExecuteInternal interface. -func (*Context) ExecuteInternal(_ context.Context, _ string, _ ...interface{}) (sqlexec.RecordSet, error) { - return nil, errors.Errorf("Not Supported") -} - -// ShowProcess implements sessionctx.Context ShowProcess interface. -func (*Context) ShowProcess() *util.ProcessInfo { - return &util.ProcessInfo{} -} - -// IsDDLOwner checks whether this session is DDL owner. -func (*Context) IsDDLOwner() bool { - return true -} - -// SetValue implements sessionctx.Context SetValue interface. -func (c *Context) SetValue(key fmt.Stringer, value interface{}) { - c.values[key] = value -} - -// Value implements sessionctx.Context Value interface. -func (c *Context) Value(key fmt.Stringer) interface{} { - value := c.values[key] - return value -} - -// ClearValue implements sessionctx.Context ClearValue interface. -func (c *Context) ClearValue(key fmt.Stringer) { - delete(c.values, key) -} - -// HasDirtyContent implements sessionctx.Context ClearValue interface. -func (*Context) HasDirtyContent(_ int64) bool { - return false -} - -// GetSessionVars implements the sessionctx.Context GetSessionVars interface. -func (c *Context) GetSessionVars() *variable.SessionVars { - return c.sessionVars -} - -// Txn implements sessionctx.Context Txn interface. -func (c *Context) Txn(bool) (kv.Transaction, error) { - return &c.txn, nil -} - -// GetClient implements sessionctx.Context GetClient interface. -func (c *Context) GetClient() kv.Client { - if c.Store == nil { - return nil - } - return c.Store.GetClient() -} - -// GetMPPClient implements sessionctx.Context GetMPPClient interface. -func (c *Context) GetMPPClient() kv.MPPClient { - if c.Store == nil { - return nil - } - return c.Store.GetMPPClient() -} - -// GetInfoSchema implements sessionctx.Context GetInfoSchema interface. -func (c *Context) GetInfoSchema() sessionctx.InfoschemaMetaVersion { - vars := c.GetSessionVars() - if snap, ok := vars.SnapshotInfoschema.(sessionctx.InfoschemaMetaVersion); ok { - return snap - } - if vars.TxnCtx != nil && vars.InTxn() { - if is, ok := vars.TxnCtx.InfoSchema.(sessionctx.InfoschemaMetaVersion); ok { - return is - } - } - if c.is == nil { - c.is = MockInfoschema(nil) - } - return c.is -} - -// MockInfoschema only serves for test. -var MockInfoschema func(tbList []*model.TableInfo) sessionctx.InfoschemaMetaVersion - -// GetDomainInfoSchema returns the latest information schema in domain -func (c *Context) GetDomainInfoSchema() sessionctx.InfoschemaMetaVersion { - if c.is == nil { - c.is = MockInfoschema(nil) - } - return c.is -} - -// GetBuiltinFunctionUsage implements sessionctx.Context GetBuiltinFunctionUsage interface. -func (*Context) GetBuiltinFunctionUsage() map[string]uint32 { - return make(map[string]uint32) -} - -// BuiltinFunctionUsageInc implements sessionctx.Context. -func (*Context) BuiltinFunctionUsageInc(_ string) {} - -// GetGlobalSysVar implements GlobalVarAccessor GetGlobalSysVar interface. -func (*Context) GetGlobalSysVar(_ sessionctx.Context, name string) (string, error) { - v := variable.GetSysVar(name) - if v == nil { - return "", variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - return v.Value, nil -} - -// SetGlobalSysVar implements GlobalVarAccessor SetGlobalSysVar interface. -func (*Context) SetGlobalSysVar(_ sessionctx.Context, name string, value string) error { - v := variable.GetSysVar(name) - if v == nil { - return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) - } - v.Value = value - return nil -} - -// GetSessionPlanCache implements the sessionctx.Context interface. -func (c *Context) GetSessionPlanCache() sessionctx.PlanCache { - return c.pcache -} - -// NewTxn implements the sessionctx.Context interface. -func (c *Context) NewTxn(context.Context) error { - if c.Store == nil { - return errors.New("store is not set") - } - if c.txn.Valid() { - err := c.txn.Commit(c.ctx) - if err != nil { - return errors.Trace(err) - } - } - - txn, err := c.Store.Begin() - if err != nil { - return errors.Trace(err) - } - c.txn.Transaction = txn - return nil -} - -// NewStaleTxnWithStartTS implements the sessionctx.Context interface. -func (c *Context) NewStaleTxnWithStartTS(ctx context.Context, _ uint64) error { - return c.NewTxn(ctx) -} - -// RefreshTxnCtx implements the sessionctx.Context interface. -func (c *Context) RefreshTxnCtx(ctx context.Context) error { - return errors.Trace(c.NewTxn(ctx)) -} - -// RollbackTxn indicates an expected call of RollbackTxn. -func (c *Context) RollbackTxn(_ context.Context) { - defer c.sessionVars.SetInTxn(false) - if c.txn.Valid() { - terror.Log(c.txn.Rollback()) - } -} - -// CommitTxn indicates an expected call of CommitTxn. -func (c *Context) CommitTxn(ctx context.Context) error { - defer c.sessionVars.SetInTxn(false) - c.txn.SetDiskFullOpt(c.level) - if c.txn.Valid() { - return c.txn.Commit(ctx) - } - return nil -} - -// GetStore gets the store of session. -func (c *Context) GetStore() kv.Storage { - return c.Store -} - -// GetSessionManager implements the sessionctx.Context interface. -func (c *Context) GetSessionManager() util.SessionManager { - return c.sm -} - -// SetSessionManager set the session manager. -func (c *Context) SetSessionManager(sm util.SessionManager) { - c.sm = sm -} - -// Cancel implements the Session interface. -func (c *Context) Cancel() { - c.cancel() -} - -// GoCtx returns standard sessionctx.Context that bind with current transaction. -func (c *Context) GoCtx() context.Context { - return c.ctx -} - -// UpdateColStatsUsage updates the column stats usage. -func (*Context) UpdateColStatsUsage(_ []model.TableItemID) {} - -// StoreIndexUsage strores the index usage information. -func (*Context) StoreIndexUsage(_ int64, _ int64, _ int64) {} - -// GetTxnWriteThroughputSLI implements the sessionctx.Context interface. -func (*Context) GetTxnWriteThroughputSLI() *sli.TxnWriteThroughputSLI { - return &sli.TxnWriteThroughputSLI{} -} - -// StmtCommit implements the sessionctx.Context interface. -func (*Context) StmtCommit(context.Context) {} - -// StmtRollback implements the sessionctx.Context interface. -func (*Context) StmtRollback(context.Context, bool) {} - -// StmtGetMutation implements the sessionctx.Context interface. -func (*Context) StmtGetMutation(_ int64) *binlog.TableMutation { - return nil -} - -// AddTableLock implements the sessionctx.Context interface. -func (*Context) AddTableLock(_ []model.TableLockTpInfo) { -} - -// ReleaseTableLocks implements the sessionctx.Context interface. -func (*Context) ReleaseTableLocks(_ []model.TableLockTpInfo) { -} - -// ReleaseTableLockByTableIDs implements the sessionctx.Context interface. -func (*Context) ReleaseTableLockByTableIDs(_ []int64) { -} - -// CheckTableLocked implements the sessionctx.Context interface. -func (*Context) CheckTableLocked(_ int64) (bool, model.TableLockType) { - return false, model.TableLockNone -} - -// GetAllTableLocks implements the sessionctx.Context interface. -func (*Context) GetAllTableLocks() []model.TableLockTpInfo { - return nil -} - -// ReleaseAllTableLocks implements the sessionctx.Context interface. -func (*Context) ReleaseAllTableLocks() { -} - -// HasLockedTables implements the sessionctx.Context interface. -func (*Context) HasLockedTables() bool { - return false -} - -// PrepareTSFuture implements the sessionctx.Context interface. -func (c *Context) PrepareTSFuture(_ context.Context, future oracle.Future, _ string) error { - c.txn.Transaction = nil - c.txn.tsFuture = future - return nil -} - -// GetPreparedTxnFuture returns the TxnFuture if it is prepared. -// It returns nil otherwise. -func (c *Context) GetPreparedTxnFuture() sessionctx.TxnFuture { - if !c.txn.validOrPending() { - return nil - } - return &c.txn -} - -// GetStmtStats implements the sessionctx.Context interface. -func (*Context) GetStmtStats() *stmtstats.StatementStats { - return nil -} - -// GetAdvisoryLock acquires an advisory lock -func (*Context) GetAdvisoryLock(_ string, _ int64) error { - return nil -} - -// IsUsedAdvisoryLock check if a lock name is in use -func (*Context) IsUsedAdvisoryLock(_ string) uint64 { - return 0 -} - -// ReleaseAdvisoryLock releases an advisory lock -func (*Context) ReleaseAdvisoryLock(_ string) bool { - return true -} - -// ReleaseAllAdvisoryLocks releases all advisory locks -func (*Context) ReleaseAllAdvisoryLocks() int { - return 0 -} - -// EncodeSessionStates implements sessionctx.Context EncodeSessionStates interface. -func (*Context) EncodeSessionStates(context.Context, sessionctx.Context, *sessionstates.SessionStates) error { - return errors.Errorf("Not Supported") -} - -// DecodeSessionStates implements sessionctx.Context DecodeSessionStates interface. -func (*Context) DecodeSessionStates(context.Context, sessionctx.Context, *sessionstates.SessionStates) error { - return errors.Errorf("Not Supported") -} - -// GetExtensions returns the `*extension.SessionExtensions` object -func (*Context) GetExtensions() *extension.SessionExtensions { - return nil -} - -// EnableSandBoxMode enable the sandbox mode. -func (c *Context) EnableSandBoxMode() { - c.inSandBoxMode = true -} - -// DisableSandBoxMode enable the sandbox mode. -func (c *Context) DisableSandBoxMode() { - c.inSandBoxMode = false -} - -// InSandBoxMode indicates that this Session is in sandbox mode -func (c *Context) InSandBoxMode() bool { - return c.inSandBoxMode -} - -// Close implements the sessionctx.Context interface. -func (*Context) Close() {} - -// NewContext creates a new mocked sessionctx.Context. -func NewContext() *Context { - ctx, cancel := context.WithCancel(context.Background()) - sctx := &Context{ - values: make(map[fmt.Stringer]interface{}), - ctx: ctx, - cancel: cancel, - } - vars := variable.NewSessionVars(sctx) - sctx.sessionVars = vars - vars.InitChunkSize = 2 - vars.MaxChunkSize = 32 - vars.StmtCtx.SetTimeZone(time.UTC) - vars.MemTracker.SetBytesLimit(-1) - vars.DiskTracker.SetBytesLimit(-1) - vars.StmtCtx.MemTracker, vars.StmtCtx.DiskTracker = memory.NewTracker(-1, -1), disk.NewTracker(-1, -1) - vars.MemTracker.AttachTo(vars.MemTracker) - vars.DiskTracker.AttachTo(vars.DiskTracker) - vars.GlobalVarsAccessor = variable.NewMockGlobalAccessor() - vars.EnablePaging = variable.DefTiDBEnablePaging - vars.MinPagingSize = variable.DefMinPagingSize - vars.CostModelVersion = variable.DefTiDBCostModelVer - vars.EnableChunkRPC = true - if err := sctx.GetSessionVars().SetSystemVar(variable.MaxAllowedPacket, "67108864"); err != nil { - panic(err) - } - if err := sctx.GetSessionVars().SetSystemVar(variable.CharacterSetConnection, "utf8mb4"); err != nil { - panic(err) - } - return sctx -} - -// HookKeyForTest is as alias, used by context.WithValue. -// golint forbits using string type as key in context.WithValue. -type HookKeyForTest string diff --git a/util/mock/main_test.go b/util/mock/main_test.go deleted file mode 100644 index 2dc36e6230b7a..0000000000000 --- a/util/mock/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/mock/store.go b/util/mock/store.go deleted file mode 100644 index ea7ca8e55fa3f..0000000000000 --- a/util/mock/store.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "context" - - deadlockpb "github.com/pingcap/kvproto/pkg/deadlock" - "github.com/pingcap/tidb/kv" - "github.com/tikv/client-go/v2/oracle" - "github.com/tikv/client-go/v2/tikv" -) - -// Store implements kv.Storage interface. -type Store struct { - Client kv.Client -} - -// GetClient implements kv.Storage interface. -func (s *Store) GetClient() kv.Client { return s.Client } - -// GetMPPClient implements kv.Storage interface. -func (*Store) GetMPPClient() kv.MPPClient { return nil } - -// GetOracle implements kv.Storage interface. -func (*Store) GetOracle() oracle.Oracle { return nil } - -// Begin implements kv.Storage interface. -func (*Store) Begin(_ ...tikv.TxnOption) (kv.Transaction, error) { return nil, nil } - -// GetSnapshot implements kv.Storage interface. -func (*Store) GetSnapshot(_ kv.Version) kv.Snapshot { return nil } - -// Close implements kv.Storage interface. -func (*Store) Close() error { return nil } - -// UUID implements kv.Storage interface. -func (*Store) UUID() string { return "mock" } - -// CurrentVersion implements kv.Storage interface. -func (*Store) CurrentVersion(_ string) (kv.Version, error) { return kv.Version{}, nil } - -// SupportDeleteRange implements kv.Storage interface. -func (*Store) SupportDeleteRange() bool { return false } - -// Name implements kv.Storage interface. -func (*Store) Name() string { return "UtilMockStorage" } - -// Describe implements kv.Storage interface. -func (*Store) Describe() string { - return "UtilMockStorage is a mock Store implementation, only for unittests in util package" -} - -// GetMemCache implements kv.Storage interface -func (*Store) GetMemCache() kv.MemManager { - return nil -} - -// ShowStatus implements kv.Storage interface. -func (*Store) ShowStatus(_ context.Context, _ string) (interface{}, error) { return nil, nil } - -// GetMinSafeTS implements kv.Storage interface. -func (*Store) GetMinSafeTS(_ string) uint64 { - return 0 -} - -// GetLockWaits implements kv.Storage interface. -func (*Store) GetLockWaits() ([]*deadlockpb.WaitForEntry, error) { - return nil, nil -} - -// GetCodec implements kv.Storage interface. -func (*Store) GetCodec() tikv.Codec { - return nil -} diff --git a/util/mvmap/BUILD.bazel b/util/mvmap/BUILD.bazel deleted file mode 100644 index 88855b41b3549..0000000000000 --- a/util/mvmap/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "mvmap", - srcs = [ - "fnv.go", - "mvmap.go", - ], - importpath = "github.com/pingcap/tidb/util/mvmap", - visibility = ["//visibility:public"], - deps = ["//util/mathutil"], -) - -go_test( - name = "mvmap_test", - timeout = "short", - srcs = [ - "bench_test.go", - "main_test.go", - "mvmap_test.go", - ], - embed = [":mvmap"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/mvmap/main_test.go b/util/mvmap/main_test.go deleted file mode 100644 index 2e56ef1ad4a03..0000000000000 --- a/util/mvmap/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mvmap - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/nocopy/BUILD.bazel b/util/nocopy/BUILD.bazel deleted file mode 100644 index fa034f988cd19..0000000000000 --- a/util/nocopy/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "nocopy", - srcs = ["nocopy.go"], - importpath = "github.com/pingcap/tidb/util/nocopy", - visibility = ["//visibility:public"], -) diff --git a/util/paging/BUILD.bazel b/util/paging/BUILD.bazel deleted file mode 100644 index f89101aac0c29..0000000000000 --- a/util/paging/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "paging", - srcs = ["paging.go"], - importpath = "github.com/pingcap/tidb/util/paging", - visibility = ["//visibility:public"], -) - -go_test( - name = "paging_test", - timeout = "short", - srcs = [ - "main_test.go", - "paging_test.go", - ], - embed = [":paging"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/paging/main_test.go b/util/paging/main_test.go deleted file mode 100644 index ee631933206c0..0000000000000 --- a/util/paging/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package paging - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/parser/BUILD.bazel b/util/parser/BUILD.bazel deleted file mode 100644 index 06779de5d3438..0000000000000 --- a/util/parser/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "parser", - srcs = [ - "ast.go", - "parser.go", - ], - importpath = "github.com/pingcap/tidb/util/parser", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//parser/format", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "parser_test", - timeout = "short", - srcs = [ - "ast_test.go", - "main_test.go", - "parser_test.go", - ], - flaky = True, - deps = [ - ":parser", - "//parser", - "//testkit/testsetup", - "//types/parser_driver", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/parser/ast.go b/util/parser/ast.go deleted file mode 100644 index 0adbf9df37cae..0000000000000 --- a/util/parser/ast.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package parser - -import ( - "strings" - - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" -) - -// GetDefaultDB checks if all tables in the AST have explicit DBName. If not, return specified DBName. -func GetDefaultDB(sel ast.StmtNode, dbName string) string { - implicitDB := &implicitDatabase{} - sel.Accept(implicitDB) - if implicitDB.hasImplicit { - return dbName - } - return "" -} - -type implicitDatabase struct { - hasImplicit bool -} - -func (i *implicitDatabase) Enter(in ast.Node) (out ast.Node, skipChildren bool) { - switch x := in.(type) { - case *ast.TableName: - if x.Schema.L == "" { - i.hasImplicit = true - } - return in, true - default: - return in, i.hasImplicit - } -} - -func (*implicitDatabase) Leave(in ast.Node) (out ast.Node, ok bool) { - return in, true -} - -func findTablePos(s, t string) int { - l := 0 - for i := range s { - if s[i] == ' ' || s[i] == ',' { - if len(t) == i-l && strings.Compare(s[l:i], t) == 0 { - return l - } - l = i + 1 - } - } - if len(t) == len(s)-l && strings.Compare(s[l:], t) == 0 { - return l - } - return -1 -} - -// SimpleCases captures simple SQL statements and uses string replacement instead of `restore` to improve performance. -// See https://github.com/pingcap/tidb/issues/22398. -func SimpleCases(node ast.StmtNode, defaultDB, origin string) (s string, ok bool) { - if len(origin) == 0 { - return "", false - } - insert, ok := node.(*ast.InsertStmt) - if !ok { - return "", false - } - if insert.Select != nil || insert.Setlist || insert.OnDuplicate != nil || (insert.TableHints != nil && len(insert.TableHints) != 0) { - return "", false - } - join := insert.Table.TableRefs - if join.Tp != 0 || join.Right != nil { - return "", false - } - ts, ok := join.Left.(*ast.TableSource) - if !ok { - return "", false - } - tn, ok := ts.Source.(*ast.TableName) - if !ok { - return "", false - } - parenPos := strings.Index(origin, "(") - if parenPos == -1 { - return "", false - } - if strings.Contains(origin[:parenPos], ".") { - return origin, true - } - lower := strings.ToLower(origin[:parenPos]) - pos := findTablePos(lower, tn.Name.L) - if pos == -1 { - return "", false - } - var builder strings.Builder - builder.WriteString(origin[:pos]) - if tn.Schema.String() != "" { - builder.WriteString(tn.Schema.String()) - } else { - builder.WriteString(defaultDB) - } - builder.WriteString(".") - builder.WriteString(origin[pos:]) - return builder.String(), true -} - -// RestoreWithDefaultDB returns restore strings for StmtNode with defaultDB -// This function is customized for SQL bind usage. -func RestoreWithDefaultDB(node ast.StmtNode, defaultDB, origin string) string { - if s, ok := SimpleCases(node, defaultDB, origin); ok { - return s - } - var sb strings.Builder - // Three flags for restore with default DB: - // 1. RestoreStringSingleQuotes specifies to use single quotes to surround the string; - // 2. RestoreSpacesAroundBinaryOperation specifies to add space around binary operation; - // 3. RestoreStringWithoutCharset specifies to not print charset before string; - // 4. RestoreNameBackQuotes specifies to use back quotes to surround the name; - ctx := format.NewRestoreCtx(format.RestoreStringSingleQuotes|format.RestoreSpacesAroundBinaryOperation|format.RestoreStringWithoutCharset|format.RestoreNameBackQuotes, &sb) - ctx.DefaultDB = defaultDB - if err := node.Restore(ctx); err != nil { - logutil.BgLogger().Debug("restore SQL failed", zap.String("category", "sql-bind"), zap.Error(err)) - return "" - } - return sb.String() -} diff --git a/util/parser/main_test.go b/util/parser/main_test.go deleted file mode 100644 index 1329c7f138c70..0000000000000 --- a/util/parser/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package parser_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/parser/parser_test.go b/util/parser/parser_test.go deleted file mode 100644 index d877fbc3a8410..0000000000000 --- a/util/parser/parser_test.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package parser_test - -import ( - "testing" - - utilparser "github.com/pingcap/tidb/util/parser" - "github.com/stretchr/testify/require" -) - -func TestSpace(t *testing.T) { - okTable := []struct { - Times int - Input string - Expected string - }{ - {0, " 1", "1"}, - {0, "1", "1"}, - {1, " 1", "1"}, - {2, " 1", "1"}, - } - for _, test := range okTable { - rest, err := utilparser.Space(test.Input, test.Times) - require.NoError(t, err) - require.Equal(t, test.Expected, rest) - } - - errTable := []struct { - Times int - Input string - }{ - {1, "1"}, - {2, " 1"}, - } - - for _, test := range errTable { - rest, err := utilparser.Space(test.Input, test.Times) - - require.NotNil(t, err) - require.Equal(t, test.Input, rest) - } -} - -func TestDigit(t *testing.T) { - okTable := []struct { - Times int - Input string - ExpectedDigits string - ExpectedRest string - }{ - {0, "123abc", "123", "abc"}, - {1, "123abc", "123", "abc"}, - {2, "123 @)@)", "123", " @)@)"}, - {3, "456 121", "456", " 121"}, - } - - for _, test := range okTable { - digits, rest, err := utilparser.Digit(test.Input, test.Times) - - require.NoError(t, err) - require.Equal(t, test.ExpectedDigits, digits) - require.Equal(t, test.ExpectedRest, rest) - } - - errTable := []struct { - Times int - Input string - }{ - {1, "int"}, - {2, "1int"}, - {3, "12 int"}, - } - - for _, test := range errTable { - digits, rest, err := utilparser.Digit(test.Input, test.Times) - - require.NotNil(t, err) - require.Equal(t, "", digits) - require.Equal(t, test.Input, rest) - } -} - -func TestNumber(t *testing.T) { - okTable := []struct { - Input string - ExpectedNum int - ExpectedRest string - }{ - {"123abc", 123, "abc"}, - {"123abc", 123, "abc"}, - {"123 @)@)", 123, " @)@)"}, - {"456 121", 456, " 121"}, - } - for _, test := range okTable { - digits, rest, err := utilparser.Number(test.Input) - - require.NoError(t, err) - require.Equal(t, test.ExpectedNum, digits) - require.Equal(t, test.ExpectedRest, rest) - } - - errTable := []struct { - Input string - }{ - {"int"}, - {"abcint"}, - {"@)@)int"}, - } - - for _, test := range errTable { - digits, rest, err := utilparser.Number(test.Input) - - require.NotNil(t, err) - require.Equal(t, 0, digits) - require.Equal(t, test.Input, rest) - } -} - -func TestCharAndAnyChar(t *testing.T) { - okTable := []struct { - Char byte - Input string - Expected string - }{ - {'i', "int", "nt"}, - {'1', "1int", "int"}, - {'1', "12 int", "2 int"}, - } - - for _, test := range okTable { - rest, err := utilparser.Char(test.Input, test.Char) - - require.NoError(t, err) - require.Equal(t, test.Expected, rest) - - rest, err = utilparser.AnyChar(test.Input) - - require.NoError(t, err) - require.Equal(t, test.Expected, rest) - } - - errTable := []struct { - Char byte - Input string - }{ - {'i', "xint"}, - {'1', "x1int"}, - {'1', "x12 int"}, - } - - for _, test := range errTable { - rest, err := utilparser.Char(test.Input, test.Char) - - require.NotNil(t, err) - require.Equal(t, test.Input, rest) - } -} diff --git a/util/password-validation/BUILD.bazel b/util/password-validation/BUILD.bazel deleted file mode 100644 index f81e360882b1b..0000000000000 --- a/util/password-validation/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "password-validation", - srcs = ["password_validation.go"], - importpath = "github.com/pingcap/tidb/util/password-validation", - visibility = ["//visibility:public"], - deps = [ - "//sessionctx/variable", - "//util/hack", - ], -) - -go_test( - name = "password-validation_test", - timeout = "short", - srcs = ["password_validation_test.go"], - embed = [":password-validation"], - flaky = True, - deps = [ - "//parser/auth", - "//sessionctx/variable", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/pdapi/BUILD.bazel b/util/pdapi/BUILD.bazel deleted file mode 100644 index 2a2099a763e7e..0000000000000 --- a/util/pdapi/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "pdapi", - srcs = ["const.go"], - importpath = "github.com/pingcap/tidb/util/pdapi", - visibility = ["//visibility:public"], -) diff --git a/util/plancache/BUILD.bazel b/util/plancache/BUILD.bazel deleted file mode 100644 index 0c9f0db3866a0..0000000000000 --- a/util/plancache/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "plancache", - srcs = ["util.go"], - importpath = "github.com/pingcap/tidb/util/plancache", - visibility = ["//visibility:public"], - deps = ["//types"], -) diff --git a/util/plancache/util.go b/util/plancache/util.go deleted file mode 100644 index 71b7241e37467..0000000000000 --- a/util/plancache/util.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package util - -import ( - "github.com/pingcap/tidb/types" -) - -// PlanCacheMatchOpts store some property used to fetch plan from plan cache -// The structure set here is to avoid import cycle -type PlanCacheMatchOpts struct { - // paramTypes stores all parameters' FieldType, some different parameters may share same plan - ParamTypes []*types.FieldType - // limitOffsetAndCount stores all the offset and key parameters extract from limit statement - // only used for cache and pick plan with parameters in limit - LimitOffsetAndCount []uint64 - // HasSubQuery indicate whether this query has sub query - HasSubQuery bool - // StatsVersionHash is the hash value of the statistics version - StatsVersionHash uint64 - - // Below are some variables that can affect the plan - ForeignKeyChecks bool -} diff --git a/util/plancodec/BUILD.bazel b/util/plancodec/BUILD.bazel deleted file mode 100644 index e0e05e6562df4..0000000000000 --- a/util/plancodec/BUILD.bazel +++ /dev/null @@ -1,41 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "plancodec", - srcs = [ - "binary_plan_decode.go", - "codec.go", - "id.go", - ], - importpath = "github.com/pingcap/tidb/util/plancodec", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//util/hack", - "//util/logutil", - "//util/memory", - "//util/texttree", - "@com_github_golang_snappy//:snappy", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_tipb//go-tipb", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "plancodec_test", - timeout = "short", - srcs = [ - "codec_test.go", - "id_test.go", - "main_test.go", - ], - embed = [":plancodec"], - flaky = True, - deps = [ - "//kv", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/plancodec/codec.go b/util/plancodec/codec.go deleted file mode 100644 index 221eef884f5d0..0000000000000 --- a/util/plancodec/codec.go +++ /dev/null @@ -1,448 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package plancodec - -import ( - "bytes" - "encoding/base64" - "math" - "strconv" - "strings" - "sync" - - "github.com/golang/snappy" - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/texttree" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -const ( - rootTaskType = "0" - copTaskType = "1" -) - -const ( - idSeparator = "_" - lineBreaker = '\n' - lineBreakerStr = "\n" - separator = '\t' - separatorStr = "\t" -) - -var ( - // PlanDiscardedEncoded indicates the discard plan because it is too long - PlanDiscardedEncoded = "[discard]" - planDiscardedDecoded = "(plan discarded because too long)" - // BinaryPlanDiscardedEncoded is a special binary plan that represents it's discarded because of too long. - BinaryPlanDiscardedEncoded = func() string { - binary := &tipb.ExplainData{DiscardedDueToTooLong: true} - proto, err := binary.Marshal() - if err != nil { - return "" - } - return Compress(proto) - }() -) - -var decoderPool = sync.Pool{ - New: func() interface{} { - return &planDecoder{} - }, -} - -// DecodePlan use to decode the string to plan tree. -func DecodePlan(planString string) (res string, err error) { - defer func() { - if r := recover(); r != nil { - logutil.BgLogger().Error("DecodePlan panic", zap.Stack("stack"), zap.Any("recover", r)) - err = errors.New("DecodePlan panicked") - } - }() - if len(planString) == 0 { - return "", nil - } - pd := decoderPool.Get().(*planDecoder) - defer decoderPool.Put(pd) - pd.buf.Reset() - pd.addHeader = true - return pd.decode(planString) -} - -// DecodeNormalizedPlan decodes the string to plan tree. -func DecodeNormalizedPlan(planString string) (string, error) { - if len(planString) == 0 { - return "", nil - } - pd := decoderPool.Get().(*planDecoder) - defer decoderPool.Put(pd) - pd.buf.Reset() - pd.addHeader = false - return pd.buildPlanTree(planString) -} - -type planDecoder struct { - buf bytes.Buffer - depths []int - indents [][]rune - planInfos []*planInfo - addHeader bool - cacheParentIdent map[int]int -} - -type planInfo struct { - depth int - fields []string -} - -func (pd *planDecoder) decode(planString string) (string, error) { - b, err := decompress(planString) - if err != nil { - if planString == PlanDiscardedEncoded { - return planDiscardedDecoded, nil - } - return "", err - } - return pd.buildPlanTree(string(hack.String(b))) -} - -func (pd *planDecoder) buildPlanTree(planString string) (string, error) { - nodes := strings.Split(planString, lineBreakerStr) - if len(pd.depths) < len(nodes) { - pd.depths = make([]int, 0, len(nodes)) - pd.planInfos = make([]*planInfo, 0, len(nodes)) - pd.indents = make([][]rune, 0, len(nodes)) - } - pd.depths = pd.depths[:0] - pd.planInfos = pd.planInfos[:0] - for _, node := range nodes { - p, err := decodePlanInfo(node) - if err != nil { - return "", err - } - if p == nil { - continue - } - pd.planInfos = append(pd.planInfos, p) - pd.depths = append(pd.depths, p.depth) - } - - if pd.addHeader { - pd.addPlanHeader() - } - - // Calculated indentation of plans. - pd.initPlanTreeIndents() - pd.cacheParentIdent = make(map[int]int) - for i := 1; i < len(pd.depths); i++ { - parentIndex := pd.findParentIndex(i) - pd.fillIndent(parentIndex, i) - } - - // Align the value of plan fields. - pd.alignFields() - - for i, p := range pd.planInfos { - if i > 0 { - pd.buf.WriteByte(lineBreaker) - } - // This is for alignment. - pd.buf.WriteByte(separator) - pd.buf.WriteString(string(pd.indents[i])) - for j := 0; j < len(p.fields); j++ { - if j > 0 { - pd.buf.WriteByte(separator) - } - pd.buf.WriteString(p.fields[j]) - } - } - return pd.buf.String(), nil -} - -func (pd *planDecoder) addPlanHeader() { - if len(pd.planInfos) == 0 { - return - } - header := &planInfo{ - depth: 0, - fields: []string{"id", "task", "estRows", "operator info", "actRows", "execution info", "memory", "disk"}, - } - if len(pd.planInfos[0].fields) < len(header.fields) { - // plan without runtime information. - header.fields = header.fields[:len(pd.planInfos[0].fields)] - } - planInfos := make([]*planInfo, 0, len(pd.planInfos)+1) - depths := make([]int, 0, len(pd.planInfos)+1) - planInfos = append(planInfos, header) - planInfos = append(planInfos, pd.planInfos...) - depths = append(depths, header.depth) - depths = append(depths, pd.depths...) - pd.planInfos = planInfos - pd.depths = depths -} - -func (pd *planDecoder) initPlanTreeIndents() { - pd.indents = pd.indents[:0] - for i := 0; i < len(pd.depths); i++ { - indent := make([]rune, 2*pd.depths[i]) - pd.indents = append(pd.indents, indent) - if len(indent) == 0 { - continue - } - for i := 0; i < len(indent)-2; i++ { - indent[i] = ' ' - } - indent[len(indent)-2] = texttree.TreeLastNode - indent[len(indent)-1] = texttree.TreeNodeIdentifier - } -} - -func (pd *planDecoder) findParentIndex(childIndex int) int { - pd.cacheParentIdent[pd.depths[childIndex]] = childIndex - parentDepth := pd.depths[childIndex] - 1 - if parentIdx, ok := pd.cacheParentIdent[parentDepth]; ok { - return parentIdx - } - for i := childIndex - 1; i > 0; i-- { - if pd.depths[i] == parentDepth { - pd.cacheParentIdent[pd.depths[i]] = i - return i - } - } - return 0 -} - -func (pd *planDecoder) fillIndent(parentIndex, childIndex int) { - depth := pd.depths[childIndex] - if depth == 0 { - return - } - idx := depth*2 - 2 - for i := childIndex - 1; i > parentIndex; i-- { - if pd.indents[i][idx] == texttree.TreeLastNode { - pd.indents[i][idx] = texttree.TreeMiddleNode - break - } - pd.indents[i][idx] = texttree.TreeBody - } -} - -func (pd *planDecoder) alignFields() { - if len(pd.planInfos) == 0 { - return - } - // Align fields length. Some plan may doesn't have runtime info, need append `` to align with other plan fields. - maxLen := -1 - for _, p := range pd.planInfos { - if len(p.fields) > maxLen { - maxLen = len(p.fields) - } - } - for _, p := range pd.planInfos { - for len(p.fields) < maxLen { - p.fields = append(p.fields, "") - } - } - - fieldsLen := len(pd.planInfos[0].fields) - // Last field no need to align. - fieldsLen-- - var buf []byte - for colIdx := 0; colIdx < fieldsLen; colIdx++ { - maxFieldLen := pd.getMaxFieldLength(colIdx) - for rowIdx, p := range pd.planInfos { - fillLen := maxFieldLen - pd.getPlanFieldLen(rowIdx, colIdx, p) - for len(buf) < fillLen { - buf = append(buf, ' ') - } - buf = buf[:fillLen] - p.fields[colIdx] += string(buf) - } - } -} - -func (pd *planDecoder) getMaxFieldLength(idx int) int { - maxLength := -1 - for rowIdx, p := range pd.planInfos { - l := pd.getPlanFieldLen(rowIdx, idx, p) - if l > maxLength { - maxLength = l - } - } - return maxLength -} - -func (pd *planDecoder) getPlanFieldLen(rowIdx, colIdx int, p *planInfo) int { - if colIdx == 0 { - return len(p.fields[0]) + len(pd.indents[rowIdx]) - } - return len(p.fields[colIdx]) -} - -func decodePlanInfo(str string) (*planInfo, error) { - values := strings.Split(str, separatorStr) - if len(values) < 2 { - return nil, nil - } - - p := &planInfo{ - fields: make([]string, 0, len(values)-1), - } - for i, v := range values { - switch i { - // depth - case 0: - depth, err := strconv.Atoi(v) - if err != nil { - return nil, errors.Errorf("decode plan: %v, depth: %v, error: %v", str, v, err) - } - p.depth = depth - // plan ID - case 1: - ids := strings.Split(v, idSeparator) - if len(ids) != 1 && len(ids) != 2 { - return nil, errors.Errorf("decode plan: %v error, invalid plan id: %v", str, v) - } - planID, err := strconv.Atoi(ids[0]) - if err != nil { - return nil, errors.Errorf("decode plan: %v, plan id: %v, error: %v", str, v, err) - } - if len(ids) == 1 { - p.fields = append(p.fields, PhysicalIDToTypeString(planID)) - } else { - p.fields = append(p.fields, PhysicalIDToTypeString(planID)+idSeparator+ids[1]) - } - // task type - case 2: - task, err := decodeTaskType(v) - if err != nil { - return nil, errors.Errorf("decode plan: %v, task type: %v, error: %v", str, v, err) - } - p.fields = append(p.fields, task) - default: - p.fields = append(p.fields, v) - } - } - return p, nil -} - -// EncodePlanNode is used to encode the plan to a string. -func EncodePlanNode(depth int, pid, planType string, rowCount float64, - taskTypeInfo, explainInfo, actRows, analyzeInfo, memoryInfo, diskInfo string, buf *bytes.Buffer) { - explainInfo = escapeString(explainInfo) - buf.WriteString(strconv.Itoa(depth)) - buf.WriteByte(separator) - buf.WriteString(encodeID(planType, pid)) - buf.WriteByte(separator) - buf.WriteString(taskTypeInfo) - buf.WriteByte(separator) - if math.Round(rowCount) == rowCount { - buf.WriteString(strconv.FormatFloat(rowCount, 'f', 0, 64)) - } else { - buf.WriteString(strconv.FormatFloat(rowCount, 'f', 2, 64)) - } - buf.WriteByte(separator) - buf.WriteString(explainInfo) - // Check whether has runtime info. - if len(actRows) > 0 || len(analyzeInfo) > 0 || len(memoryInfo) > 0 || len(diskInfo) > 0 { - buf.WriteByte(separator) - buf.WriteString(actRows) - buf.WriteByte(separator) - buf.WriteString(analyzeInfo) - buf.WriteByte(separator) - buf.WriteString(memoryInfo) - buf.WriteByte(separator) - buf.WriteString(diskInfo) - } - buf.WriteByte(lineBreaker) -} - -func escapeString(s string) string { - s = strings.ReplaceAll(s, string([]byte{separator}), "\\t") - return strings.ReplaceAll(s, string([]byte{lineBreaker}), "\\n") -} - -// NormalizePlanNode is used to normalize the plan to a string. -func NormalizePlanNode(depth int, planType string, taskTypeInfo string, explainInfo string, buf *bytes.Buffer) { - buf.WriteString(strconv.Itoa(depth)) - buf.WriteByte(separator) - planID := TypeStringToPhysicalID(planType) - buf.WriteString(strconv.Itoa(planID)) - buf.WriteByte(separator) - buf.WriteString(taskTypeInfo) - buf.WriteByte(separator) - buf.WriteString(explainInfo) - buf.WriteByte(lineBreaker) -} - -func encodeID(planType, id string) string { - planID := TypeStringToPhysicalID(planType) - return strconv.Itoa(planID) + idSeparator + id -} - -// EncodeTaskType is used to encode task type to a string. -func EncodeTaskType(isRoot bool, storeType kv.StoreType) string { - if isRoot { - return rootTaskType - } - return copTaskType + idSeparator + strconv.Itoa((int)(storeType)) -} - -// EncodeTaskTypeForNormalize is used to encode task type to a string. Only use for normalize plan. -func EncodeTaskTypeForNormalize(isRoot bool, storeType kv.StoreType) string { - if isRoot { - return rootTaskType - } else if storeType == kv.TiKV { - return copTaskType - } - return copTaskType + idSeparator + strconv.Itoa((int)(storeType)) -} - -func decodeTaskType(str string) (string, error) { - segs := strings.Split(str, idSeparator) - if segs[0] == rootTaskType { - return "root", nil - } - if len(segs) == 1 { // be compatible to `NormalizePlanNode`, which doesn't encode storeType in task field. - return "cop", nil - } - storeType, err := strconv.Atoi(segs[1]) - if err != nil { - return "", err - } - return "cop[" + ((kv.StoreType)(storeType)).Name() + "]", nil -} - -// Compress compresses the input with snappy then encodes it with base64. -func Compress(input []byte) string { - compressBytes := snappy.Encode(nil, input) - return base64.StdEncoding.EncodeToString(compressBytes) -} - -func decompress(str string) ([]byte, error) { - decodeBytes, err := base64.StdEncoding.DecodeString(str) - if err != nil { - return nil, err - } - - bs, err := snappy.Decode(nil, decodeBytes) - if err != nil { - return nil, err - } - return bs, nil -} diff --git a/util/plancodec/codec_test.go b/util/plancodec/codec_test.go deleted file mode 100644 index ed1a8fe2f4833..0000000000000 --- a/util/plancodec/codec_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package plancodec - -import ( - "testing" - - "github.com/pingcap/tidb/kv" - "github.com/stretchr/testify/require" -) - -type encodeTaskTypeCase struct { - IsRoot bool - StoreType kv.StoreType - EncodedStr string - DecodedStr string -} - -func TestEncodeTaskType(t *testing.T) { - cases := []encodeTaskTypeCase{ - {true, kv.UnSpecified, "0", "root"}, - {false, kv.TiKV, "1_0", "cop[tikv]"}, - {false, kv.TiFlash, "1_1", "cop[tiflash]"}, - {false, kv.TiDB, "1_2", "cop[tidb]"}, - } - for _, cas := range cases { - require.Equal(t, cas.EncodedStr, EncodeTaskType(cas.IsRoot, cas.StoreType)) - str, err := decodeTaskType(cas.EncodedStr) - require.NoError(t, err) - require.Equal(t, cas.DecodedStr, str) - } - - str, err := decodeTaskType("1") - require.NoError(t, err) - require.Equal(t, "cop", str) - - _, err = decodeTaskType("1_x") - require.Error(t, err) -} - -func TestDecodeDiscardPlan(t *testing.T) { - plan, err := DecodePlan(PlanDiscardedEncoded) - require.NoError(t, err) - require.Equal(t, planDiscardedDecoded, plan) -} diff --git a/util/plancodec/main_test.go b/util/plancodec/main_test.go deleted file mode 100644 index f794d84b4f6b3..0000000000000 --- a/util/plancodec/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package plancodec - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/printer/BUILD.bazel b/util/printer/BUILD.bazel deleted file mode 100644 index 0eeabc5bb7d6b..0000000000000 --- a/util/printer/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "printer", - srcs = ["printer.go"], - importpath = "github.com/pingcap/tidb/util/printer", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//parser/mysql", - "//util/israce", - "//util/logutil", - "//util/versioninfo", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "printer_test", - timeout = "short", - srcs = [ - "main_test.go", - "printer_test.go", - ], - embed = [":printer"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/printer/main_test.go b/util/printer/main_test.go deleted file mode 100644 index 2457870f75511..0000000000000 --- a/util/printer/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package printer - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/profile/BUILD.bazel b/util/profile/BUILD.bazel deleted file mode 100644 index 78ec86a1b5898..0000000000000 --- a/util/profile/BUILD.bazel +++ /dev/null @@ -1,45 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "profile", - srcs = [ - "flamegraph.go", - "profile.go", - ], - importpath = "github.com/pingcap/tidb/util/profile", - visibility = ["//visibility:public"], - deps = [ - "//types", - "//util/cpuprofile", - "//util/texttree", - "@com_github_google_pprof//profile", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "profile_test", - timeout = "short", - srcs = [ - "flamegraph_test.go", - "main_test.go", - "profile_test.go", - ], - data = glob(["testdata/**"]), - embed = [":profile"], - flaky = True, - deps = [ - "//domain", - "//kv", - "//session", - "//store/mockstore", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/collate", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/profile/main_test.go b/util/profile/main_test.go deleted file mode 100644 index f812e444a6cdd..0000000000000 --- a/util/profile/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package profile - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/promutil/BUILD.bazel b/util/promutil/BUILD.bazel deleted file mode 100644 index d34e4a0d6cc4c..0000000000000 --- a/util/promutil/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "promutil", - srcs = [ - "factory.go", - "registry.go", - ], - importpath = "github.com/pingcap/tidb/util/promutil", - visibility = ["//visibility:public"], - deps = ["@com_github_prometheus_client_golang//prometheus"], -) - -go_test( - name = "promutil_test", - timeout = "short", - srcs = ["registry_test.go"], - embed = [":promutil"], - flaky = True, - deps = [ - "@com_github_prometheus_client_golang//prometheus", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/ranger/BUILD.bazel b/util/ranger/BUILD.bazel deleted file mode 100644 index 91d81bbc56e42..0000000000000 --- a/util/ranger/BUILD.bazel +++ /dev/null @@ -1,67 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "ranger", - srcs = [ - "checker.go", - "detacher.go", - "points.go", - "ranger.go", - "types.go", - ], - importpath = "github.com/pingcap/tidb/util/ranger", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//expression", - "//kv", - "//parser/ast", - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//planner/util/fixcontrol", - "//sessionctx", - "//sessionctx/stmtctx", - "//types", - "//types/parser_driver", - "//util/chunk", - "//util/codec", - "//util/collate", - "//util/dbterror", - "//util/mathutil", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "ranger_test", - timeout = "short", - srcs = [ - "bench_test.go", - "main_test.go", - "ranger_test.go", - "types_test.go", - ], - flaky = True, - shard_count = 26, - deps = [ - ":ranger", - "//config", - "//domain", - "//expression", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//session", - "//sessionctx", - "//testkit", - "//testkit/testsetup", - "//types", - "//util/collate", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/ranger/bench_test.go b/util/ranger/bench_test.go deleted file mode 100644 index c980a806f6066..0000000000000 --- a/util/ranger/bench_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package ranger_test - -import ( - "context" - "testing" - - "github.com/pingcap/tidb/expression" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/testkit" - "github.com/pingcap/tidb/util/ranger" - "github.com/stretchr/testify/require" -) - -func BenchmarkDetachCondAndBuildRangeForIndex(b *testing.B) { - store := testkit.CreateMockStore(b) - testKit := testkit.NewTestKit(b, store) - testKit.MustExec("USE test") - testKit.MustExec("DROP TABLE IF EXISTS t") - testKit.MustExec(` -CREATE TABLE t ( - id bigint(20) NOT NULL, - flag tinyint(1) NOT NULL, - start_time bigint(20) NOT NULL, - end_time bigint(20) NOT NULL, - org_id bigint(20) NOT NULL, - work_type varchar(20) CHARACTER SET latin1 DEFAULT NULL, - KEY idx (work_type, flag, org_id, end_time) -)`) - const longInListQuery = ` -SELECT - * -FROM - test.t -WHERE - flag = false - AND work_type = 'ART' - AND start_time < 1437233819011 - AND org_id IN ( - 6914445279216,13370400597894,23573357446410,25887731612857,47914138043476,55587486395595,81718641401629,116900287316705,145561135354847,146149695520705,150737101640717, - 156461412195734,159282589442914,172009309011258,177247737846954,186431984199126,192702036012003,198028383844036,203822128217766,204361899227892,222307981616040, - 223676791310456,239710781952147,284504079630201,293577747630605,294033490212963,295616321229667,296595862564733,306758307660237,312453546107536,341725138386801, - 365564508529854,367823101074613,372136375994705,379986503803645,391811651805728,400703265358976,402101198418969,418909618067022,431707392441212,433127041410999, - 453871162499705,459423393488794,468411967568789,470295426508062,474431880357867,486212302560292,491505628336549,501258676986205,512885499269076,541562269064881, - 573472004072939,588733301257768,615894236361514,615983097695492,632401842501612,647142957465854,718424118160213,727429900786891,730535283550760,738198024914914, - 739163331366841,752647396960214,756654620675819,771528730062551,780002237988119,819197371932508,841300462416232,850412457085478,874244695288775,881570046324012, - 888197716219703,891639557310370,891974587948562,894056936863746,896673560435518,907536217395121,911688951561329,915825484067466,955508673412475,970655461426927, - 972378619997200,995940920336141,1001616070548723,1002328390838858,1008841511217759,1027157044666161,1048868628313964,1066571897813670,1077847212985966,1080168008312256, - 1096474319900512,1102806753403857,1117700255389200,1121170709282654,1160894105829436,1173396752247253,1174823990738043,1176756778119821,1183639496028232,1224377491431012, - 1240466389734677,1265076660322902,1269569491282720,1272581574031499,1294849094933502,1295099688484884,1298881176988511,1299992642087679,1307669929489952,1338074385647926, - 1342415156765380,1360218717881999,1377658957083475,1392922543432192,1407444098594050,1438256495838735,1445134088155147,1486929584894557,1494681055271250,1500940354882616, - 1530516421560327,1532590570974295,1544648947254643,1561923580960451,1563587565476473,1565067823935596,1573069534228327,1573167213450271,1573297960637648,1577324450386436, - 1595234199645128,1595320706602899,1595934401297767,1616085587597345,1652295812716667,1655495487920136,1663475672388133,1668352817492466,1681094348275341,1689623403182214, - 1701682724927093,1705012823832699,1710393138044599,1716535649128474,1733575964270463,1750190609804974,1754580077690816,1756061776687456,1758058273255859,1775158332937577, - 1786728287430927,1816461420376899,1828580334315536,1858008005313000,1878841219054602,1878932921719554,1904081347331116,1904820184794904,1913069596895373,1941380005857044, - 1954836070968071,1955618820347782,1983296066429676,1987819713385690,1998098943250021,2013403425656222,2026046763398088,2026621786756595,2042249014990205,2046741004470190, - 2054370107316514,2082578854015062,2087183461591329,2087192311688265,2095277921868705,2103249621417272,2105159282073449,2106385538583803,2115025577188264,2115892671475192, - 2144855844328126,2145526421460724,2155282047243675,2170620433275766,2189596848694026,2194468311513858,2213049505974092,2213713514793282,2213911591088204,2215904332099994, - 2220235232411590,2226449138693468,2281727415070895,2288760906462231,2292201263531973,2292800860074434,2305015986098173,2311091146951322,2345959699274993,2356192386220089, - 2358508761766515,2372253813770528,2372994254274287,2374279183118353,2382395547546489,2397593929551113,2414904392525386,2416320859150803,2432407185506251,2439133858011514, - 2456909988876966,2490057196713078,2491000908839300,2501843499019962,2539856169115632,2542691236689315,2554165079560216,2565016421484028,2579373020942520,2583310536030502, - 2601385761450563,2603243552830302,2609138752573551,2628285706286347,2631734058797888,2638342575912817,2640167419150776,2651402302096646,2652627728219962,2677347449018607, - 2680209147172480,2683108753662485,2697695717735514,2699481485241986,2706019556864874,2707225343321107,2707841703322085,2724535386459144,2751805187614069,2759827465036125, - 2761426575202406,2768997857008184,2780782950787373,2789872834409453,2801402100587551,2806075464399632,2811893029385689,2816433481597910,2817217705468440,2817327738406355, - 2834191662385443,2838661299874842,2841835162527294,2842790844846179,2873548708258327,2879581389559553,2881798195775557,2943715564248539,2951224334569493,2958397742216527, - 2962117760574537,2977264272542363,2982743447396379,3009349759215772,3010012117130847,3010096874529870,3010115852485268,3012535704934837,3019483201458596,3036314602733381, - 3036837746136599,3039514679368453,3052506678397436,3059152178913030,3061095276596365,3063233426258797,3085115838323303,3093308450248420,3101208987984055,3106059275305341, - 3107559394454149,3110050933644367,3138545183162445,3140678914029638,3142109724205254,3142527081183238,3160670144726081,3160829106050702,3172902656372574,3181996104936476, - 3185563316673259,3189751274857877,3211729550313903,3213599609783958,3213659878256809,3215918533522245,3225137047012900,3231023124763183,3237856608607317,3247879729114765, - 3253596244997212,3256221808530784,3259756319203329,3260467132021662,3270706364495298,3273179837457878,3281263986182695,3296686215236784,3315584830022847,3317781471030011, - 3338120669693067,3342373672540130,3348941534532661,3359715878023917,3383970565360566,3397868551281252,3428808017724584,3449238661063482,3450439330534765,3471286956110444, - 3472931370488350,3473088266701087,3474445823605231,3477506057124755,3477541077050002,3488929919781737,3502330373943603,3504844077112105,3508329172255490,3510690524473209, - 3512324709745663,3514007754921924,3558011694836438,3559330712604565,3562145253615132,3581148885399840,3591312468185522,3597256243542091,3618160287855458,3625686898155792, - 3650194338715713,3650610878210125,3668345904483321,3668494673430762,3677776463522008,3694233081600122,3709632134619598,3725419513943971,3730099951927002,3734961839633730, - 3788380331922768,3811246425255446,3815582365292463,3829203122180282,3847292141308076,3877507310017402,3882136043994493,3887872593644033,3891280433757250,3924114035682754, - 3930635798027692,3940634174809349,3955900287161214,3957844806020309,3973236219509940,3981294421878412,3984846206013660,3993802859865565,3996764828980278,4023461371356880, - 4030830455174294,4032434581680299,4041625011713530,4041957068079946,4058955264781991,4083680454731905,4095581705542542,4112420671677445,4121292361441010,4133226631387396, - 4139004365572538,4161250756754756,4163706760683594,4170471653379067,4171134267004311,4177796537235481,4180802682160942,4203191696595400,4209464578955045,4223422057959415, - 4237541104444937,4257691911774311,4260020795088571,4301574030764989,4301922471400280,4304478206038048,4314941265701364,4320330498423583,4339739390410992,4342413486284428, - 4347136230283432,4351145740656078,4358837874704787,4362622126951624,4364582223851552,4366497646764759,4385815379876479,4406431382404050,4409339407622327,4411432076559821, - 4449849757340102,4453892102562139,4466153465045159,4479272299804907,4486938493241801,4500505590495671,4502678993390350,4508608408879950,4515778758013390,4516708986545589, - 4534650929880461,4537213242644302,4537280160911816,4544757774741059,4544796825985803,4567299173366450,4586846032456054,4607174945068672,4613242210966642,4625035792839901, - 4634756593700865,4635123568273790,4659875963875224,4670322637424975,4683035442855866,4689033135620199,4714163851388857,4714214015125119,4744848633979578,4750937375207328, - 4776805289846989,4804031931975645,4837280540915990,4840957238353452,4868526553354967,4875063418864529,4909882543513230,4912268119820614,4919123728900332,4929754602909549, - 4941072993543698,4941174020949904,4948032640819331,4955057670206957,4989823480030237,4990195550280933,5011619499820208,5013143564325843,5016786248387969,5019292677276101, - 5045230878000223,5053158166772953,5058611677018883,5083565032770599,5100847523417394,5105223137691724,5116076462386020,5117104556161083,5137323839372187,5159591155175114, - 5174145884534911,5182009696238822,5207529188216676,5229363397364492,5231081768308950,5233170527517877,5238337785560206,5259149223152958,5259347545688076,5265284989590946, - 5288711160650011,5297955243309025,5300535720011230,5311590629866593,5313248950470856,5313960220028487,5321218075331795,5334031591400346,5340934779949776,5380276587818617, - 5380604989545853,5392427172834832,5419648071490001,5436430269421440,5438720576124743,5442272167466546,5443531545450195,5462404261617760,5484761325677647 - ) -` - sctx := testKit.Session().(sessionctx.Context) - stmts, err := session.Parse(sctx, longInListQuery) - require.NoError(b, err) - require.Len(b, stmts, 1) - ret := &plannercore.PreprocessorReturn{} - err = plannercore.Preprocess(context.Background(), sctx, stmts[0], plannercore.WithPreprocessorReturn(ret)) - require.NoError(b, err) - ctx := context.Background() - p, _, err := plannercore.BuildLogicalPlanForTest(ctx, sctx, stmts[0], ret.InfoSchema) - require.NoError(b, err) - selection := p.(plannercore.LogicalPlan).Children()[0].(*plannercore.LogicalSelection) - tbl := selection.Children()[0].(*plannercore.DataSource).TableInfo() - require.NotNil(b, selection) - conds := make([]expression.Expression, len(selection.Conditions)) - for i, cond := range selection.Conditions { - conds[i] = expression.PushDownNot(sctx, cond) - } - cols, lengths := expression.IndexInfo2PrefixCols(tbl.Columns, selection.Schema().Columns, tbl.Indices[0]) - require.NotNil(b, cols) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err = ranger.DetachCondAndBuildRangeForIndex(sctx, conds, cols, lengths, 0) - require.NoError(b, err) - } - b.StopTimer() -} diff --git a/util/ranger/checker.go b/util/ranger/checker.go deleted file mode 100644 index 0468d3c92472e..0000000000000 --- a/util/ranger/checker.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package ranger - -import ( - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/collate" -) - -// conditionChecker checks if this condition can be pushed to index planner. -type conditionChecker struct { - checkerCol *expression.Column - length int - optPrefixIndexSingleScan bool -} - -func (c *conditionChecker) isFullLengthColumn() bool { - return c.length == types.UnspecifiedLength || c.length == c.checkerCol.GetType().GetFlen() -} - -// check returns two values, isAccessCond and shouldReserve. -// isAccessCond indicates whether the condition can be used to build ranges. -// shouldReserve indicates whether the condition should be reserved in filter conditions. -func (c *conditionChecker) check(condition expression.Expression) (isAccessCond, shouldReserve bool) { - switch x := condition.(type) { - case *expression.ScalarFunction: - return c.checkScalarFunction(x) - case *expression.Column: - if x.RetType.EvalType() == types.ETString { - return false, true - } - return c.checkColumn(x) - case *expression.Constant: - return true, false - } - return false, true -} - -func (c *conditionChecker) checkScalarFunction(scalar *expression.ScalarFunction) (isAccessCond, shouldReserve bool) { - _, collation := scalar.CharsetAndCollation() - switch scalar.FuncName.L { - case ast.LogicOr, ast.LogicAnd: - isAccessCond0, shouldReserve0 := c.check(scalar.GetArgs()[0]) - isAccessCond1, shouldReserve1 := c.check(scalar.GetArgs()[1]) - if isAccessCond0 && isAccessCond1 { - return true, shouldReserve0 || shouldReserve1 - } - return false, true - case ast.EQ, ast.NE, ast.GE, ast.GT, ast.LE, ast.LT, ast.NullEQ: - if _, ok := scalar.GetArgs()[0].(*expression.Constant); ok { - if c.matchColumn(scalar.GetArgs()[1]) { - // Checks whether the scalar function is calculated use the collation compatible with the column. - if scalar.GetArgs()[1].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[1].GetType().GetCollate(), collation) { - return false, true - } - isFullLength := c.isFullLengthColumn() - if scalar.FuncName.L == ast.NE { - return isFullLength, !isFullLength - } - return true, !isFullLength - } - } - if _, ok := scalar.GetArgs()[1].(*expression.Constant); ok { - if c.matchColumn(scalar.GetArgs()[0]) { - // Checks whether the scalar function is calculated use the collation compatible with the column. - if scalar.GetArgs()[0].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { - return false, true - } - isFullLength := c.isFullLengthColumn() - if scalar.FuncName.L == ast.NE { - return isFullLength, !isFullLength - } - return true, !isFullLength - } - } - case ast.IsNull: - if c.matchColumn(scalar.GetArgs()[0]) { - var isNullReserve bool // We can know whether the column is null from prefix column of any length. - if !c.optPrefixIndexSingleScan { - isNullReserve = !c.isFullLengthColumn() - } - return true, isNullReserve - } - return false, true - case ast.IsTruthWithoutNull, ast.IsFalsity, ast.IsTruthWithNull: - if s, ok := scalar.GetArgs()[0].(*expression.Column); ok { - if s.RetType.EvalType() == types.ETString { - return false, true - } - } - return c.checkColumn(scalar.GetArgs()[0]) - case ast.UnaryNot: - // TODO: support "not like" convert to access conditions. - s, ok := scalar.GetArgs()[0].(*expression.ScalarFunction) - if !ok { - // "not column" or "not constant" can't lead to a range. - return false, true - } - if s.FuncName.L == ast.Like || s.FuncName.L == ast.NullEQ { - return false, true - } - return c.check(scalar.GetArgs()[0]) - case ast.In: - if !c.matchColumn(scalar.GetArgs()[0]) { - return false, true - } - if scalar.GetArgs()[0].GetType().EvalType() == types.ETString && !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { - return false, true - } - for _, v := range scalar.GetArgs()[1:] { - if _, ok := v.(*expression.Constant); !ok { - return false, true - } - } - return true, !c.isFullLengthColumn() - case ast.Like: - return c.checkLikeFunc(scalar) - case ast.GetParam: - // TODO - return true, false - } - return false, true -} - -func (c *conditionChecker) checkLikeFunc(scalar *expression.ScalarFunction) (isAccessCond, shouldReserve bool) { - _, collation := scalar.CharsetAndCollation() - if collate.NewCollationEnabled() && !collate.IsBinCollation(collation) { - // The algorithm constructs the range in byte-level: for example, ab% is mapped to [ab, ac] by adding 1 to the last byte. - // However, this is incorrect for non-binary collation strings because the sort key order is not the same as byte order. - // For example, "`%" is mapped to the range [`, a](where ` is 0x60 and a is 0x61). - // Because the collation utf8_general_ci is case-insensitive, a and A have the same sort key. - // Finally, the range comes to be [`, A], which is actually an empty range. - // See https://github.com/pingcap/tidb/issues/31174 for more details. - // In short, when the column type is non-binary collation string, we cannot use `like` expressions to generate the range. - return false, true - } - if !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { - return false, true - } - if !c.matchColumn(scalar.GetArgs()[0]) { - return false, true - } - pattern, ok := scalar.GetArgs()[1].(*expression.Constant) - if !ok { - return false, true - } - if pattern.Value.IsNull() { - return false, true - } - patternStr, err := pattern.Value.ToString() - if err != nil { - return false, true - } - if len(patternStr) == 0 { - return true, !c.isFullLengthColumn() - } - escape := byte(scalar.GetArgs()[2].(*expression.Constant).Value.GetInt64()) - likeFuncReserve := !c.isFullLengthColumn() - for i := 0; i < len(patternStr); i++ { - if patternStr[i] == escape { - i++ - if i < len(patternStr)-1 { - continue - } - break - } - if i == 0 && (patternStr[i] == '%' || patternStr[i] == '_') { - return false, true - } - if patternStr[i] == '%' { - // We currently do not support using `enum like 'xxx%'` to build range - // see https://github.com/pingcap/tidb/issues/27130 for more details - if scalar.GetArgs()[0].GetType().GetType() == mysql.TypeEnum { - return false, true - } - if i != len(patternStr)-1 { - likeFuncReserve = true - } - break - } - if patternStr[i] == '_' { - // We currently do not support using `enum like 'xxx_'` to build range - // see https://github.com/pingcap/tidb/issues/27130 for more details - if scalar.GetArgs()[0].GetType().GetType() == mysql.TypeEnum { - return false, true - } - likeFuncReserve = true - break - } - } - return true, likeFuncReserve -} - -func (c *conditionChecker) matchColumn(expr expression.Expression) bool { - // Check if virtual expression column matched - if c.checkerCol != nil { - return c.checkerCol.EqualByExprAndID(nil, expr) - } - return false -} - -func (c *conditionChecker) checkColumn(expr expression.Expression) (isAccessCond, shouldReserve bool) { - if c.matchColumn(expr) { - return true, !c.isFullLengthColumn() - } - return false, true -} diff --git a/util/ranger/main_test.go b/util/ranger/main_test.go deleted file mode 100644 index f5580dc4361ba..0000000000000 --- a/util/ranger/main_test.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package ranger_test - -import ( - "flag" - "fmt" - "os" - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - flag.Parse() - - if exitCode := m.Run(); exitCode != 0 { - os.Exit(exitCode) - } - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - - if err := goleak.Find(opts...); err != nil { - _, _ = fmt.Fprintf(os.Stderr, "goleak: Errors on successful test run: %v\n", err) - os.Exit(1) - } -} diff --git a/util/ranger/types.go b/util/ranger/types.go deleted file mode 100644 index 0024a8b039c18..0000000000000 --- a/util/ranger/types.go +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// 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. - -package ranger - -import ( - "fmt" - "math" - "strings" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "github.com/pingcap/tidb/util/collate" -) - -// MutableRanges represents a range may change after it is created. -// It's mainly designed for plan-cache, since some ranges in a cached plan have to be rebuild when reusing. -type MutableRanges interface { - // Range returns the underlying range values. - Range() Ranges - // Rebuild rebuilds the underlying ranges again. - Rebuild() error -} - -// Ranges implements the MutableRanges interface for range array. -type Ranges []*Range - -// Range returns the range array. -func (rs Ranges) Range() Ranges { - return rs -} - -// Rebuild rebuilds this range. -func (Ranges) Rebuild() error { - return nil -} - -// MemUsage gets the memory usage of ranges. -func (rs Ranges) MemUsage() (sum int64) { - for _, ran := range rs { - sum += ran.MemUsage() - } - return -} - -// Range represents a range generated in physical plan building phase. -type Range struct { - LowVal []types.Datum // Low value is exclusive. - HighVal []types.Datum // High value is exclusive. - Collators []collate.Collator - LowExclude bool - HighExclude bool -} - -// Width returns the width of this range. -func (ran *Range) Width() int { - return len(ran.LowVal) -} - -// Clone clones a Range. -func (ran *Range) Clone() *Range { - if ran == nil { - return nil - } - newRange := &Range{ - LowVal: make([]types.Datum, 0, len(ran.LowVal)), - HighVal: make([]types.Datum, 0, len(ran.HighVal)), - LowExclude: ran.LowExclude, - HighExclude: ran.HighExclude, - } - for i, length := 0, len(ran.LowVal); i < length; i++ { - newRange.LowVal = append(newRange.LowVal, ran.LowVal[i]) - } - for i, length := 0, len(ran.HighVal); i < length; i++ { - newRange.HighVal = append(newRange.HighVal, ran.HighVal[i]) - } - newRange.Collators = append(newRange.Collators, ran.Collators...) - return newRange -} - -// IsPoint returns if the range is a point. -func (ran *Range) IsPoint(sctx sessionctx.Context) bool { - return ran.isPoint(sctx.GetSessionVars().StmtCtx, sctx.GetSessionVars().RegardNULLAsPoint) -} - -func (ran *Range) isPoint(stmtCtx *stmtctx.StatementContext, regardNullAsPoint bool) bool { - if len(ran.LowVal) != len(ran.HighVal) { - return false - } - for i := range ran.LowVal { - a := ran.LowVal[i] - b := ran.HighVal[i] - if a.Kind() == types.KindMinNotNull || b.Kind() == types.KindMaxValue { - return false - } - cmp, err := a.Compare(stmtCtx, &b, ran.Collators[i]) - if err != nil { - return false - } - if cmp != 0 { - return false - } - - if a.IsNull() && b.IsNull() { // [NULL, NULL] - if !regardNullAsPoint { - return false - } - } - } - return !ran.LowExclude && !ran.HighExclude -} - -// IsPointNonNullable returns if the range is a point without NULL. -func (ran *Range) IsPointNonNullable(sctx sessionctx.Context) bool { - return ran.isPoint(sctx.GetSessionVars().StmtCtx, false) -} - -// IsPointNullable returns if the range is a point. -// TODO: unify the parameter type with IsPointNullable and IsPoint -func (ran *Range) IsPointNullable(sctx sessionctx.Context) bool { - return ran.isPoint(sctx.GetSessionVars().StmtCtx, true) -} - -// IsFullRange check if the range is full scan range -func (ran *Range) IsFullRange(unsignedIntHandle bool) bool { - if unsignedIntHandle { - if len(ran.LowVal) != 1 || len(ran.HighVal) != 1 { - return false - } - lowValRawString := formatDatum(ran.LowVal[0], true) - highValRawString := formatDatum(ran.HighVal[0], false) - return lowValRawString == "0" && highValRawString == "+inf" - } - if len(ran.LowVal) != len(ran.HighVal) { - return false - } - for i := range ran.LowVal { - lowValRawString := formatDatum(ran.LowVal[i], true) - highValRawString := formatDatum(ran.HighVal[i], false) - if ("-inf" != lowValRawString && "NULL" != lowValRawString) || - ("+inf" != highValRawString && "NULL" != highValRawString) || - ("NULL" == lowValRawString && "NULL" == highValRawString) { - return false - } - } - return true -} - -// HasFullRange checks if any range in the slice is a full range. -func HasFullRange(ranges []*Range, unsignedIntHandle bool) bool { - for _, ran := range ranges { - if ran.IsFullRange(unsignedIntHandle) { - return true - } - } - return false -} - -// String implements the Stringer interface. -func (ran *Range) String() string { - lowStrs := make([]string, 0, len(ran.LowVal)) - for _, d := range ran.LowVal { - lowStrs = append(lowStrs, formatDatum(d, true)) - } - highStrs := make([]string, 0, len(ran.LowVal)) - for _, d := range ran.HighVal { - highStrs = append(highStrs, formatDatum(d, false)) - } - l, r := "[", "]" - if ran.LowExclude { - l = "(" - } - if ran.HighExclude { - r = ")" - } - return l + strings.Join(lowStrs, " ") + "," + strings.Join(highStrs, " ") + r -} - -// Encode encodes the range to its encoded value. -func (ran *Range) Encode(sc *stmtctx.StatementContext, lowBuffer, highBuffer []byte) ([]byte, []byte, error) { - var err error - lowBuffer, err = codec.EncodeKey(sc, lowBuffer[:0], ran.LowVal...) - if err != nil { - return nil, nil, err - } - if ran.LowExclude { - lowBuffer = kv.Key(lowBuffer).PrefixNext() - } - highBuffer, err = codec.EncodeKey(sc, highBuffer[:0], ran.HighVal...) - if err != nil { - return nil, nil, err - } - if !ran.HighExclude { - highBuffer = kv.Key(highBuffer).PrefixNext() - } - return lowBuffer, highBuffer, nil -} - -// PrefixEqualLen tells you how long the prefix of the range is a point. -// e.g. If this range is (1 2 3, 1 2 +inf), then the return value is 2. -func (ran *Range) PrefixEqualLen(sc *stmtctx.StatementContext) (int, error) { - // Here, len(ran.LowVal) always equal to len(ran.HighVal) - for i := 0; i < len(ran.LowVal); i++ { - cmp, err := ran.LowVal[i].Compare(sc, &ran.HighVal[i], ran.Collators[i]) - if err != nil { - return 0, errors.Trace(err) - } - if cmp != 0 { - return i, nil - } - } - return len(ran.LowVal), nil -} - -// EmptyRangeSize is the size of empty range. -const EmptyRangeSize = int64(unsafe.Sizeof(Range{})) - -// MemUsage gets the memory usage of range. -func (ran *Range) MemUsage() (sum int64) { - // 16 is the size of Collator interface. - sum = EmptyRangeSize + int64(len(ran.Collators))*16 - for _, val := range ran.LowVal { - sum += val.MemUsage() - } - for _, val := range ran.HighVal { - sum += val.MemUsage() - } - // We ignore size of collator currently. - return sum -} - -func formatDatum(d types.Datum, isLeftSide bool) string { - switch d.Kind() { - case types.KindNull: - return "NULL" - case types.KindMinNotNull: - return "-inf" - case types.KindMaxValue: - return "+inf" - case types.KindInt64: - switch d.GetInt64() { - case math.MinInt64: - if isLeftSide { - return "-inf" - } - case math.MaxInt64: - if !isLeftSide { - return "+inf" - } - } - case types.KindUint64: - if d.GetUint64() == math.MaxUint64 && !isLeftSide { - return "+inf" - } - case types.KindBytes: - return fmt.Sprintf("0x%X", d.GetValue()) - case types.KindString: - return fmt.Sprintf("%q", d.GetValue()) - case types.KindMysqlEnum, types.KindMysqlSet, - types.KindMysqlJSON, types.KindBinaryLiteral, types.KindMysqlBit: - return fmt.Sprintf("\"%v\"", d.GetValue()) - } - return fmt.Sprintf("%v", d.GetValue()) -} diff --git a/util/regexpr-router/BUILD.bazel b/util/regexpr-router/BUILD.bazel deleted file mode 100644 index 04e8c022ea10e..0000000000000 --- a/util/regexpr-router/BUILD.bazel +++ /dev/null @@ -1,26 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "regexpr-router", - srcs = ["regexpr_router.go"], - importpath = "github.com/pingcap/tidb/util/regexpr-router", - visibility = ["//visibility:public"], - deps = [ - "//util/filter", - "//util/table-router", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "regexpr-router_test", - timeout = "short", - srcs = ["regexpr_router_test.go"], - embed = [":regexpr-router"], - flaky = True, - deps = [ - "//util/filter", - "//util/table-router", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/replayer/BUILD.bazel b/util/replayer/BUILD.bazel deleted file mode 100644 index 3dee9377ff764..0000000000000 --- a/util/replayer/BUILD.bazel +++ /dev/null @@ -1,13 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "replayer", - srcs = ["replayer.go"], - importpath = "github.com/pingcap/tidb/util/replayer", - visibility = ["//visibility:public"], - deps = [ - "//config", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - ], -) diff --git a/util/resourcegrouptag/BUILD.bazel b/util/resourcegrouptag/BUILD.bazel deleted file mode 100644 index 3ae8ec1487d3e..0000000000000 --- a/util/resourcegrouptag/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "resourcegrouptag", - srcs = ["resource_group_tag.go"], - importpath = "github.com/pingcap/tidb/util/resourcegrouptag", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//tablecodec/rowindexcodec", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_tikv_client_go_v2//tikvrpc", - ], -) - -go_test( - name = "resourcegrouptag_test", - timeout = "short", - srcs = [ - "main_test.go", - "resource_group_tag_test.go", - ], - embed = [":resourcegrouptag"], - flaky = True, - deps = [ - "//parser", - "//testkit/testsetup", - "//util/hack", - "@com_github_pingcap_kvproto//pkg/coprocessor", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/resourcegrouptag/main_test.go b/util/resourcegrouptag/main_test.go deleted file mode 100644 index a4c1196901461..0000000000000 --- a/util/resourcegrouptag/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package resourcegrouptag - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/rowDecoder/BUILD.bazel b/util/rowDecoder/BUILD.bazel deleted file mode 100644 index 699b23c15e878..0000000000000 --- a/util/rowDecoder/BUILD.bazel +++ /dev/null @@ -1,50 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "rowDecoder", - srcs = ["decoder.go"], - importpath = "github.com/pingcap/tidb/util/rowDecoder", - visibility = ["//visibility:public"], - deps = [ - "//expression", - "//kv", - "//parser/model", - "//sessionctx", - "//table", - "//table/tables", - "//tablecodec", - "//types", - "//util/chunk", - "//util/rowcodec", - ], -) - -go_test( - name = "rowDecoder_test", - timeout = "short", - srcs = [ - "decoder_test.go", - "main_test.go", - ], - flaky = True, - deps = [ - ":rowDecoder", - "//expression", - "//kv", - "//parser/model", - "//parser/mysql", - "//planner/core", - "//sessionctx/stmtctx", - "//table/tables", - "//tablecodec", - "//testkit/testsetup", - "//testkit/testutil", - "//types", - "//util/collate", - "//util/mock", - "//util/rowcodec", - "@com_github_stretchr_testify//require", - "@io_opencensus_go//stats/view", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/rowDecoder/decoder.go b/util/rowDecoder/decoder.go deleted file mode 100644 index d16ef9ef99708..0000000000000 --- a/util/rowDecoder/decoder.go +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package decoder - -import ( - "slices" - "time" - - "github.com/pingcap/tidb/expression" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/table" - "github.com/pingcap/tidb/table/tables" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/rowcodec" -) - -// Column contains the info and generated expr of column. -type Column struct { - Col *table.Column - GenExpr expression.Expression -} - -// RowDecoder decodes a byte slice into datums and eval the generated column value. -type RowDecoder struct { - tbl table.Table - mutRow chunk.MutRow - colMap map[int64]Column - colTypes map[int64]*types.FieldType - defaultVals []types.Datum - cols []*table.Column - pkCols []int64 -} - -// NewRowDecoder returns a new RowDecoder. -func NewRowDecoder(tbl table.Table, cols []*table.Column, decodeColMap map[int64]Column) *RowDecoder { - tblInfo := tbl.Meta() - colFieldMap := make(map[int64]*types.FieldType, len(decodeColMap)) - for id, col := range decodeColMap { - colFieldMap[id] = &col.Col.ColumnInfo.FieldType - } - - tps := make([]*types.FieldType, len(cols)) - for _, col := range cols { - // Even for changing column in column type change, we target field type uniformly. - tps[col.Offset] = &col.FieldType - } - var pkCols []int64 - switch { - case tblInfo.IsCommonHandle: - pkCols = tables.TryGetCommonPkColumnIds(tbl.Meta()) - case tblInfo.PKIsHandle: - pkCols = []int64{tblInfo.GetPkColInfo().ID} - default: // support decoding _tidb_rowid. - pkCols = []int64{model.ExtraHandleID} - } - return &RowDecoder{ - tbl: tbl, - mutRow: chunk.MutRowFromTypes(tps), - colMap: decodeColMap, - colTypes: colFieldMap, - defaultVals: make([]types.Datum, len(cols)), - cols: cols, - pkCols: pkCols, - } -} - -// DecodeAndEvalRowWithMap decodes a byte slice into datums and evaluates the generated column value. -func (rd *RowDecoder) DecodeAndEvalRowWithMap(ctx sessionctx.Context, handle kv.Handle, b []byte, decodeLoc *time.Location, row map[int64]types.Datum) (map[int64]types.Datum, error) { - var err error - if rowcodec.IsNewFormat(b) { - row, err = tablecodec.DecodeRowWithMapNew(b, rd.colTypes, decodeLoc, row) - } else { - row, err = tablecodec.DecodeRowWithMap(b, rd.colTypes, decodeLoc, row) - } - if err != nil { - return nil, err - } - row, err = tablecodec.DecodeHandleToDatumMap(handle, rd.pkCols, rd.colTypes, decodeLoc, row) - if err != nil { - return nil, err - } - for _, dCol := range rd.colMap { - colInfo := dCol.Col.ColumnInfo - val, ok := row[colInfo.ID] - if ok || dCol.GenExpr != nil { - rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) - continue - } - if dCol.Col.ChangeStateInfo != nil { - val, _, err = tables.GetChangingColVal(ctx, rd.cols, dCol.Col, row, rd.defaultVals) - } else { - // Get the default value of the column in the generated column expression. - val, err = tables.GetColDefaultValue(ctx, dCol.Col, rd.defaultVals) - } - if err != nil { - return nil, err - } - rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) - } - return rd.EvalRemainedExprColumnMap(ctx, row) -} - -// BuildFullDecodeColMap builds a map that contains [columnID -> struct{*table.Column, expression.Expression}] from all columns. -func BuildFullDecodeColMap(cols []*table.Column, schema *expression.Schema) map[int64]Column { - decodeColMap := make(map[int64]Column, len(cols)) - for _, col := range cols { - decodeColMap[col.ID] = Column{ - Col: col, - GenExpr: schema.Columns[col.Offset].VirtualExpr, - } - } - return decodeColMap -} - -// CurrentRowWithDefaultVal returns current decoding row with default column values set properly. -// Please make sure calling DecodeAndEvalRowWithMap first. -func (rd *RowDecoder) CurrentRowWithDefaultVal() chunk.Row { - return rd.mutRow.ToRow() -} - -// DecodeTheExistedColumnMap is used by ddl column-type-change first column reorg stage. -// In the function, we only decode the existed column in the row and fill the default value. -// For changing column, we shouldn't cast it here, because we will do a unified cast operation latter. -// For generated column, we didn't cast it here too, because the eval process will depend on the changing column. -func (rd *RowDecoder) DecodeTheExistedColumnMap(ctx sessionctx.Context, handle kv.Handle, b []byte, decodeLoc *time.Location, row map[int64]types.Datum) (map[int64]types.Datum, error) { - var err error - if rowcodec.IsNewFormat(b) { - row, err = tablecodec.DecodeRowWithMapNew(b, rd.colTypes, decodeLoc, row) - } else { - row, err = tablecodec.DecodeRowWithMap(b, rd.colTypes, decodeLoc, row) - } - if err != nil { - return nil, err - } - row, err = tablecodec.DecodeHandleToDatumMap(handle, rd.pkCols, rd.colTypes, decodeLoc, row) - if err != nil { - return nil, err - } - for _, dCol := range rd.colMap { - colInfo := dCol.Col.ColumnInfo - val, ok := row[colInfo.ID] - if ok || dCol.GenExpr != nil || dCol.Col.ChangeStateInfo != nil { - rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) - continue - } - // Get the default value of the column in the generated column expression. - val, err = tables.GetColDefaultValue(ctx, dCol.Col, rd.defaultVals) - if err != nil { - return nil, err - } - // Fill the default value into map. - row[colInfo.ID] = val - rd.mutRow.SetValue(colInfo.Offset, val.GetValue()) - } - // return the existed column map here. - return row, nil -} - -// EvalRemainedExprColumnMap is used by ddl column-type-change first column reorg stage. -// It is always called after DecodeTheExistedColumnMap to finish the generated column evaluation. -func (rd *RowDecoder) EvalRemainedExprColumnMap(ctx sessionctx.Context, row map[int64]types.Datum) (map[int64]types.Datum, error) { - keys := make([]int, 0, len(rd.colMap)) - ids := make(map[int]int, len(rd.colMap)) - for k, col := range rd.colMap { - keys = append(keys, col.Col.Offset) - ids[col.Col.Offset] = int(k) - } - slices.Sort(keys) - for _, id := range keys { - col := rd.colMap[int64(ids[id])] - if col.GenExpr == nil { - continue - } - // Eval the column value - val, err := col.GenExpr.Eval(rd.mutRow.ToRow()) - if err != nil { - return nil, err - } - val, err = table.CastValue(ctx, *val.Clone(), col.Col.ColumnInfo, false, true) - if err != nil { - return nil, err - } - - rd.mutRow.SetValue(col.Col.Offset, val.GetValue()) - row[int64(ids[id])] = val - } - // return the existed and evaluated column map here. - return row, nil -} diff --git a/util/rowDecoder/main_test.go b/util/rowDecoder/main_test.go deleted file mode 100644 index 22a09c3e619fb..0000000000000 --- a/util/rowDecoder/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package decoder_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - testsetup.SetupForCommonTest() - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/rowcodec/BUILD.bazel b/util/rowcodec/BUILD.bazel deleted file mode 100644 index 41ea8eadfca89..0000000000000 --- a/util/rowcodec/BUILD.bazel +++ /dev/null @@ -1,52 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "rowcodec", - srcs = [ - "common.go", - "decoder.go", - "encoder.go", - "row.go", - ], - importpath = "github.com/pingcap/tidb/util/rowcodec", - visibility = ["//visibility:public"], - deps = [ - "//kv", - "//parser/model", - "//parser/mysql", - "//parser/terror", - "//parser/types", - "//sessionctx/stmtctx", - "//types", - "//util/chunk", - "//util/codec", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "rowcodec_test", - timeout = "short", - srcs = [ - "bench_test.go", - "main_test.go", - "rowcodec_test.go", - ], - embed = [":rowcodec"], - flaky = True, - deps = [ - "//kv", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//tablecodec", - "//testkit/testsetup", - "//types", - "//util/benchdaily", - "//util/chunk", - "//util/codec", - "//util/collate", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/rowcodec/bench_test.go b/util/rowcodec/bench_test.go deleted file mode 100644 index e8b7150f2045c..0000000000000 --- a/util/rowcodec/bench_test.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package rowcodec_test - -import ( - "testing" - "time" - - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/tablecodec" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/benchdaily" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/rowcodec" -) - -func BenchmarkChecksum(b *testing.B) { - b.ReportAllocs() - datums := types.MakeDatums(1, "abc", 1.1) - tp1 := types.NewFieldType(mysql.TypeLong) - tp2 := types.NewFieldType(mysql.TypeVarchar) - tp3 := types.NewFieldType(mysql.TypeDouble) - cols := []rowcodec.ColData{ - {&model.ColumnInfo{ID: 1, FieldType: *tp1}, &datums[0]}, - {&model.ColumnInfo{ID: 2, FieldType: *tp2}, &datums[1]}, - {&model.ColumnInfo{ID: 3, FieldType: *tp3}, &datums[2]}, - } - row := rowcodec.RowData{Cols: cols} - for i := 0; i < b.N; i++ { - _, err := row.Checksum() - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkEncode(b *testing.B) { - b.ReportAllocs() - oldRow := types.MakeDatums(1, "abc", 1.1) - var xb rowcodec.Encoder - var buf []byte - colIDs := []int64{1, 2, 3} - var err error - for i := 0; i < b.N; i++ { - buf, err = xb.Encode(nil, colIDs, oldRow, buf) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkEncodeFromOldRow(b *testing.B) { - b.ReportAllocs() - oldRow := types.MakeDatums(1, "abc", 1.1) - oldRowData, err := tablecodec.EncodeOldRow(stmtctx.NewStmtCtx(), oldRow, []int64{1, 2, 3}, nil, nil) - if err != nil { - b.Fatal(err) - } - var xb rowcodec.Encoder - var buf []byte - for i := 0; i < b.N; i++ { - buf, err = rowcodec.EncodeFromOldRow(&xb, nil, oldRowData, buf) - if err != nil { - b.Fatal(err) - } - } -} - -func BenchmarkDecode(b *testing.B) { - b.ReportAllocs() - oldRow := types.MakeDatums(1, "abc", 1.1) - colIDs := []int64{-1, 2, 3} - tps := []*types.FieldType{ - types.NewFieldType(mysql.TypeLonglong), - types.NewFieldType(mysql.TypeString), - types.NewFieldType(mysql.TypeDouble), - } - var xb rowcodec.Encoder - xRowData, err := xb.Encode(nil, colIDs, oldRow, nil) - if err != nil { - b.Fatal(err) - } - cols := make([]rowcodec.ColInfo, len(tps)) - for i, tp := range tps { - cols[i] = rowcodec.ColInfo{ - ID: colIDs[i], - Ft: tp, - } - } - decoder := rowcodec.NewChunkDecoder(cols, []int64{-1}, nil, time.Local) - chk := chunk.NewChunkWithCapacity(tps, 1) - for i := 0; i < b.N; i++ { - chk.Reset() - err = decoder.DecodeToChunk(xRowData, kv.IntHandle(1), chk) - if err != nil { - b.Fatal(err) - } - } -} - -func TestBenchDaily(t *testing.T) { - benchdaily.Run( - BenchmarkEncode, - BenchmarkDecode, - BenchmarkEncodeFromOldRow, - ) -} diff --git a/util/rowcodec/common.go b/util/rowcodec/common.go deleted file mode 100644 index 7cd31a32ebc22..0000000000000 --- a/util/rowcodec/common.go +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package rowcodec - -import ( - "encoding/binary" - "hash/crc32" - "math" - "reflect" - "unsafe" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" - data "github.com/pingcap/tidb/types" -) - -// CodecVer is the constant number that represent the new row format. -const CodecVer = 128 - -var ( - errInvalidCodecVer = errors.New("invalid codec version") - errInvalidChecksumVer = errors.New("invalid checksum version") - errInvalidChecksumTyp = errors.New("invalid type for checksum") -) - -// First byte in the encoded value which specifies the encoding type. -const ( - NilFlag byte = 0 - BytesFlag byte = 1 - CompactBytesFlag byte = 2 - IntFlag byte = 3 - UintFlag byte = 4 - FloatFlag byte = 5 - DecimalFlag byte = 6 - VarintFlag byte = 8 - VaruintFlag byte = 9 - JSONFlag byte = 10 -) - -func bytesToU32Slice(b []byte) []uint32 { - if len(b) == 0 { - return nil - } - var u32s []uint32 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u32s)) - hdr.Len = len(b) / 4 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&b[0])) - return u32s -} - -func bytes2U16Slice(b []byte) []uint16 { - if len(b) == 0 { - return nil - } - var u16s []uint16 - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&u16s)) - hdr.Len = len(b) / 2 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&b[0])) - return u16s -} - -func u16SliceToBytes(u16s []uint16) []byte { - if len(u16s) == 0 { - return nil - } - var b []byte - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - hdr.Len = len(u16s) * 2 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&u16s[0])) - return b -} - -func u32SliceToBytes(u32s []uint32) []byte { - if len(u32s) == 0 { - return nil - } - var b []byte - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - hdr.Len = len(u32s) * 4 - hdr.Cap = hdr.Len - hdr.Data = uintptr(unsafe.Pointer(&u32s[0])) - return b -} - -func encodeInt(buf []byte, iVal int64) []byte { - var tmp [8]byte - if int64(int8(iVal)) == iVal { - buf = append(buf, byte(iVal)) - } else if int64(int16(iVal)) == iVal { - binary.LittleEndian.PutUint16(tmp[:], uint16(iVal)) - buf = append(buf, tmp[:2]...) - } else if int64(int32(iVal)) == iVal { - binary.LittleEndian.PutUint32(tmp[:], uint32(iVal)) - buf = append(buf, tmp[:4]...) - } else { - binary.LittleEndian.PutUint64(tmp[:], uint64(iVal)) - buf = append(buf, tmp[:8]...) - } - return buf -} - -func decodeInt(val []byte) int64 { - switch len(val) { - case 1: - return int64(int8(val[0])) - case 2: - return int64(int16(binary.LittleEndian.Uint16(val))) - case 4: - return int64(int32(binary.LittleEndian.Uint32(val))) - default: - return int64(binary.LittleEndian.Uint64(val)) - } -} - -func encodeUint(buf []byte, uVal uint64) []byte { - var tmp [8]byte - if uint64(uint8(uVal)) == uVal { - buf = append(buf, byte(uVal)) - } else if uint64(uint16(uVal)) == uVal { - binary.LittleEndian.PutUint16(tmp[:], uint16(uVal)) - buf = append(buf, tmp[:2]...) - } else if uint64(uint32(uVal)) == uVal { - binary.LittleEndian.PutUint32(tmp[:], uint32(uVal)) - buf = append(buf, tmp[:4]...) - } else { - binary.LittleEndian.PutUint64(tmp[:], uVal) - buf = append(buf, tmp[:8]...) - } - return buf -} - -func decodeUint(val []byte) uint64 { - switch len(val) { - case 1: - return uint64(val[0]) - case 2: - return uint64(binary.LittleEndian.Uint16(val)) - case 4: - return uint64(binary.LittleEndian.Uint32(val)) - default: - return binary.LittleEndian.Uint64(val) - } -} - -type largeNotNullSorter Encoder - -func (s *largeNotNullSorter) Less(i, j int) bool { - return s.colIDs32[i] < s.colIDs32[j] -} - -func (s *largeNotNullSorter) Len() int { - return int(s.numNotNullCols) -} - -func (s *largeNotNullSorter) Swap(i, j int) { - s.colIDs32[i], s.colIDs32[j] = s.colIDs32[j], s.colIDs32[i] - s.values[i], s.values[j] = s.values[j], s.values[i] -} - -type smallNotNullSorter Encoder - -func (s *smallNotNullSorter) Less(i, j int) bool { - return s.colIDs[i] < s.colIDs[j] -} - -func (s *smallNotNullSorter) Len() int { - return int(s.numNotNullCols) -} - -func (s *smallNotNullSorter) Swap(i, j int) { - s.colIDs[i], s.colIDs[j] = s.colIDs[j], s.colIDs[i] - s.values[i], s.values[j] = s.values[j], s.values[i] -} - -type smallNullSorter Encoder - -func (s *smallNullSorter) Less(i, j int) bool { - nullCols := s.colIDs[s.numNotNullCols:] - return nullCols[i] < nullCols[j] -} - -func (s *smallNullSorter) Len() int { - return int(s.numNullCols) -} - -func (s *smallNullSorter) Swap(i, j int) { - nullCols := s.colIDs[s.numNotNullCols:] - nullCols[i], nullCols[j] = nullCols[j], nullCols[i] -} - -type largeNullSorter Encoder - -func (s *largeNullSorter) Less(i, j int) bool { - nullCols := s.colIDs32[s.numNotNullCols:] - return nullCols[i] < nullCols[j] -} - -func (s *largeNullSorter) Len() int { - return int(s.numNullCols) -} - -func (s *largeNullSorter) Swap(i, j int) { - nullCols := s.colIDs32[s.numNotNullCols:] - nullCols[i], nullCols[j] = nullCols[j], nullCols[i] -} - -const ( - // Length of rowkey. - rowKeyLen = 19 - // Index of record flag 'r' in rowkey used by tidb-server. - // The rowkey format is t{8 bytes id}_r{8 bytes handle} - recordPrefixIdx = 10 -) - -// IsRowKey determine whether key is row key. -// this method will be used in unistore. -func IsRowKey(key []byte) bool { - return len(key) >= rowKeyLen && key[0] == 't' && key[recordPrefixIdx] == 'r' -} - -// IsNewFormat checks whether row data is in new-format. -func IsNewFormat(rowData []byte) bool { - return rowData[0] == CodecVer -} - -// FieldTypeFromModelColumn creates a types.FieldType from model.ColumnInfo. -// export for test case and CDC. -func FieldTypeFromModelColumn(col *model.ColumnInfo) *types.FieldType { - return col.FieldType.Clone() -} - -// ColData combines the column info as well as its datum. It's used to calculate checksum. -type ColData struct { - *model.ColumnInfo - Datum *data.Datum -} - -// Encode encodes the column datum into bytes for checksum. If buf provided, append encoded data to it. -func (c ColData) Encode(buf []byte) ([]byte, error) { - return appendDatumForChecksum(buf, c.Datum, c.GetType()) -} - -// RowData is a list of ColData for row checksum calculation. -type RowData struct { - // Cols is a list of ColData which is expected to be sorted by id before calling Encode/Checksum. - Cols []ColData - // Data stores the result of Encode. However, it mostly acts as a buffer for encoding columns on checksum - // calculation. - Data []byte -} - -// Len implements sort.Interface for RowData. -func (r RowData) Len() int { return len(r.Cols) } - -// Less implements sort.Interface for RowData. -func (r RowData) Less(i int, j int) bool { return r.Cols[i].ID < r.Cols[j].ID } - -// Swap implements sort.Interface for RowData. -func (r RowData) Swap(i int, j int) { r.Cols[i], r.Cols[j] = r.Cols[j], r.Cols[i] } - -// Encode encodes all columns into bytes (for test purpose). -func (r *RowData) Encode() ([]byte, error) { - var err error - if len(r.Data) > 0 { - r.Data = r.Data[:0] - } - for _, col := range r.Cols { - r.Data, err = col.Encode(r.Data) - if err != nil { - return nil, err - } - } - return r.Data, nil -} - -// Checksum calculates the checksum of columns. Callers should make sure columns are sorted by id. -func (r *RowData) Checksum() (checksum uint32, err error) { - for _, col := range r.Cols { - if len(r.Data) > 0 { - r.Data = r.Data[:0] - } - r.Data, err = col.Encode(r.Data) - if err != nil { - return 0, err - } - checksum = crc32.Update(checksum, crc32.IEEETable, r.Data) - } - return checksum, nil -} - -func appendDatumForChecksum(buf []byte, dat *data.Datum, typ byte) (out []byte, err error) { - defer func() { - if x := recover(); x != nil { - // catch panic when datum and type mismatch - err = errors.Annotatef(x.(error), "encode datum(%s) as %s for checksum", dat.String(), types.TypeStr(typ)) - } - }() - if dat.IsNull() { - return buf, nil - } - switch typ { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24, mysql.TypeYear: - out = binary.LittleEndian.AppendUint64(buf, dat.GetUint64()) - case mysql.TypeVarchar, mysql.TypeVarString, mysql.TypeString, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, mysql.TypeBlob: - out = appendLengthValue(buf, dat.GetBytes()) - case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate, mysql.TypeNewDate: - out = appendLengthValue(buf, []byte(dat.GetMysqlTime().String())) - case mysql.TypeDuration: - out = appendLengthValue(buf, []byte(dat.GetMysqlDuration().String())) - case mysql.TypeFloat, mysql.TypeDouble: - v := dat.GetFloat64() - if math.IsInf(v, 0) || math.IsNaN(v) { - v = 0 // because ticdc has such a transform - } - out = binary.LittleEndian.AppendUint64(buf, math.Float64bits(v)) - case mysql.TypeNewDecimal: - out = appendLengthValue(buf, []byte(dat.GetMysqlDecimal().String())) - case mysql.TypeEnum: - out = binary.LittleEndian.AppendUint64(buf, dat.GetMysqlEnum().Value) - case mysql.TypeSet: - out = binary.LittleEndian.AppendUint64(buf, dat.GetMysqlSet().Value) - case mysql.TypeBit: - // ticdc transforms a bit value as the following way, no need to handle truncate error here. - v, _ := dat.GetBinaryLiteral().ToInt(nil) - out = binary.LittleEndian.AppendUint64(buf, v) - case mysql.TypeJSON: - out = appendLengthValue(buf, []byte(dat.GetMysqlJSON().String())) - case mysql.TypeNull, mysql.TypeGeometry: - out = buf - default: - return buf, errInvalidChecksumTyp - } - return -} - -func appendLengthValue(buf []byte, val []byte) []byte { - buf = binary.LittleEndian.AppendUint32(buf, uint32(len(val))) - return append(buf, val...) -} diff --git a/util/rowcodec/decoder.go b/util/rowcodec/decoder.go deleted file mode 100644 index b2b8bfc21dbe9..0000000000000 --- a/util/rowcodec/decoder.go +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. - -package rowcodec - -import ( - "encoding/binary" - "fmt" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/chunk" - "github.com/pingcap/tidb/util/codec" -) - -// decoder contains base util for decode row. -type decoder struct { - row - columns []ColInfo - handleColIDs []int64 - loc *time.Location -} - -// NewDecoder creates a decoder. -func NewDecoder(columns []ColInfo, handleColIDs []int64, loc *time.Location) *decoder { - return &decoder{ - columns: columns, - handleColIDs: handleColIDs, - loc: loc, - } -} - -// ColInfo is used as column meta info for row decoder. -type ColInfo struct { - ID int64 - IsPKHandle bool - VirtualGenCol bool - Ft *types.FieldType -} - -// DatumMapDecoder decodes the row to datum map. -type DatumMapDecoder struct { - decoder -} - -// NewDatumMapDecoder creates a DatumMapDecoder. -func NewDatumMapDecoder(columns []ColInfo, loc *time.Location) *DatumMapDecoder { - return &DatumMapDecoder{decoder{ - columns: columns, - loc: loc, - }} -} - -// DecodeToDatumMap decodes byte slices to datum map. -func (decoder *DatumMapDecoder) DecodeToDatumMap(rowData []byte, row map[int64]types.Datum) (map[int64]types.Datum, error) { - if row == nil { - row = make(map[int64]types.Datum, len(decoder.columns)) - } - err := decoder.fromBytes(rowData) - if err != nil { - return nil, err - } - for i := range decoder.columns { - col := &decoder.columns[i] - idx, isNil, notFound := decoder.row.findColID(col.ID) - if !notFound && !isNil { - colData := decoder.getData(idx) - d, err := decoder.decodeColDatum(col, colData) - if err != nil { - return nil, err - } - row[col.ID] = d - continue - } - - if isNil { - var d types.Datum - d.SetNull() - row[col.ID] = d - continue - } - } - return row, nil -} - -func (decoder *DatumMapDecoder) decodeColDatum(col *ColInfo, colData []byte) (types.Datum, error) { - var d types.Datum - switch col.Ft.GetType() { - case mysql.TypeLonglong, mysql.TypeLong, mysql.TypeInt24, mysql.TypeShort, mysql.TypeTiny: - if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { - d.SetUint64(decodeUint(colData)) - } else { - d.SetInt64(decodeInt(colData)) - } - case mysql.TypeYear: - d.SetInt64(decodeInt(colData)) - case mysql.TypeFloat: - _, fVal, err := codec.DecodeFloat(colData) - if err != nil { - return d, err - } - d.SetFloat32(float32(fVal)) - case mysql.TypeDouble: - _, fVal, err := codec.DecodeFloat(colData) - if err != nil { - return d, err - } - d.SetFloat64(fVal) - case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeString, mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - d.SetString(string(colData), col.Ft.GetCollate()) - case mysql.TypeNewDecimal: - _, dec, precision, frac, err := codec.DecodeDecimal(colData) - if err != nil { - return d, err - } - d.SetMysqlDecimal(dec) - d.SetLength(precision) - d.SetFrac(frac) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - var t types.Time - t.SetType(col.Ft.GetType()) - t.SetFsp(col.Ft.GetDecimal()) - err := t.FromPackedUint(decodeUint(colData)) - if err != nil { - return d, err - } - if col.Ft.GetType() == mysql.TypeTimestamp && !t.IsZero() { - err = t.ConvertTimeZone(time.UTC, decoder.loc) - if err != nil { - return d, err - } - } - d.SetMysqlTime(t) - case mysql.TypeDuration: - var dur types.Duration - dur.Duration = time.Duration(decodeInt(colData)) - dur.Fsp = col.Ft.GetDecimal() - d.SetMysqlDuration(dur) - case mysql.TypeEnum: - // ignore error deliberately, to read empty enum value. - enum, err := types.ParseEnumValue(col.Ft.GetElems(), decodeUint(colData)) - if err != nil { - enum = types.Enum{} - } - d.SetMysqlEnum(enum, col.Ft.GetCollate()) - case mysql.TypeSet: - set, err := types.ParseSetValue(col.Ft.GetElems(), decodeUint(colData)) - if err != nil { - return d, err - } - d.SetMysqlSet(set, col.Ft.GetCollate()) - case mysql.TypeBit: - byteSize := (col.Ft.GetFlen() + 7) >> 3 - d.SetMysqlBit(types.NewBinaryLiteralFromUint(decodeUint(colData), byteSize)) - case mysql.TypeJSON: - var j types.BinaryJSON - j.TypeCode = colData[0] - j.Value = colData[1:] - d.SetMysqlJSON(j) - default: - return d, errors.Errorf("unknown type %d", col.Ft.GetType()) - } - return d, nil -} - -// ChunkDecoder decodes the row to chunk.Chunk. -type ChunkDecoder struct { - decoder - defDatum func(i int, chk *chunk.Chunk) error -} - -// NewChunkDecoder creates a NewChunkDecoder. -func NewChunkDecoder(columns []ColInfo, handleColIDs []int64, defDatum func(i int, chk *chunk.Chunk) error, loc *time.Location) *ChunkDecoder { - return &ChunkDecoder{ - decoder: decoder{ - columns: columns, - handleColIDs: handleColIDs, - loc: loc, - }, - defDatum: defDatum, - } -} - -// DecodeToChunk decodes a row to chunk. -func (decoder *ChunkDecoder) DecodeToChunk(rowData []byte, handle kv.Handle, chk *chunk.Chunk) error { - err := decoder.fromBytes(rowData) - if err != nil { - return err - } - - for colIdx := range decoder.columns { - col := &decoder.columns[colIdx] - // fill the virtual column value after row calculation - if col.VirtualGenCol { - chk.AppendNull(colIdx) - continue - } - if col.ID == model.ExtraRowChecksumID { - if v := decoder.row.getChecksumInfo(); len(v) > 0 { - chk.AppendString(colIdx, v) - } else { - chk.AppendNull(colIdx) - } - continue - } - - idx, isNil, notFound := decoder.row.findColID(col.ID) - if !notFound && !isNil { - colData := decoder.getData(idx) - err := decoder.decodeColToChunk(colIdx, col, colData, chk) - if err != nil { - return err - } - continue - } - - // Only try to decode handle when there is no corresponding column in the value. - // This is because the information in handle may be incomplete in some cases. - // For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle. - if decoder.tryAppendHandleColumn(colIdx, col, handle, chk) { - continue - } - - if isNil { - chk.AppendNull(colIdx) - continue - } - - if decoder.defDatum == nil { - chk.AppendNull(colIdx) - continue - } - - err := decoder.defDatum(colIdx, chk) - if err != nil { - return err - } - } - return nil -} - -func (decoder *ChunkDecoder) tryAppendHandleColumn(colIdx int, col *ColInfo, handle kv.Handle, chk *chunk.Chunk) bool { - if handle == nil { - return false - } - if handle.IsInt() && col.ID == decoder.handleColIDs[0] { - chk.AppendInt64(colIdx, handle.IntValue()) - return true - } - for i, id := range decoder.handleColIDs { - if col.ID == id { - if types.NeedRestoredData(col.Ft) { - return false - } - coder := codec.NewDecoder(chk, decoder.loc) - _, err := coder.DecodeOne(handle.EncodedCol(i), colIdx, col.Ft) - return err == nil - } - } - return false -} - -func (decoder *ChunkDecoder) decodeColToChunk(colIdx int, col *ColInfo, colData []byte, chk *chunk.Chunk) error { - switch col.Ft.GetType() { - case mysql.TypeLonglong, mysql.TypeLong, mysql.TypeInt24, mysql.TypeShort, mysql.TypeTiny: - if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { - chk.AppendUint64(colIdx, decodeUint(colData)) - } else { - chk.AppendInt64(colIdx, decodeInt(colData)) - } - case mysql.TypeYear: - chk.AppendInt64(colIdx, decodeInt(colData)) - case mysql.TypeFloat: - _, fVal, err := codec.DecodeFloat(colData) - if err != nil { - return err - } - chk.AppendFloat32(colIdx, float32(fVal)) - case mysql.TypeDouble: - _, fVal, err := codec.DecodeFloat(colData) - if err != nil { - return err - } - chk.AppendFloat64(colIdx, fVal) - case mysql.TypeVarString, mysql.TypeVarchar, mysql.TypeString, - mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob: - chk.AppendBytes(colIdx, colData) - case mysql.TypeNewDecimal: - _, dec, _, frac, err := codec.DecodeDecimal(colData) - if err != nil { - return err - } - if col.Ft.GetDecimal() != types.UnspecifiedLength && frac > col.Ft.GetDecimal() { - to := new(types.MyDecimal) - err := dec.Round(to, col.Ft.GetDecimal(), types.ModeHalfUp) - if err != nil { - return errors.Trace(err) - } - dec = to - } - chk.AppendMyDecimal(colIdx, dec) - case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: - var t types.Time - t.SetType(col.Ft.GetType()) - t.SetFsp(col.Ft.GetDecimal()) - err := t.FromPackedUint(decodeUint(colData)) - if err != nil { - return err - } - if col.Ft.GetType() == mysql.TypeTimestamp && decoder.loc != nil && !t.IsZero() { - err = t.ConvertTimeZone(time.UTC, decoder.loc) - if err != nil { - return err - } - } - chk.AppendTime(colIdx, t) - case mysql.TypeDuration: - var dur types.Duration - dur.Duration = time.Duration(decodeInt(colData)) - dur.Fsp = col.Ft.GetDecimal() - chk.AppendDuration(colIdx, dur) - case mysql.TypeEnum: - // ignore error deliberately, to read empty enum value. - enum, err := types.ParseEnumValue(col.Ft.GetElems(), decodeUint(colData)) - if err != nil { - enum = types.Enum{} - } - chk.AppendEnum(colIdx, enum) - case mysql.TypeSet: - set, err := types.ParseSetValue(col.Ft.GetElems(), decodeUint(colData)) - if err != nil { - return err - } - chk.AppendSet(colIdx, set) - case mysql.TypeBit: - byteSize := (col.Ft.GetFlen() + 7) >> 3 - chk.AppendBytes(colIdx, types.NewBinaryLiteralFromUint(decodeUint(colData), byteSize)) - case mysql.TypeJSON: - var j types.BinaryJSON - j.TypeCode = colData[0] - j.Value = colData[1:] - chk.AppendJSON(colIdx, j) - default: - return errors.Errorf("unknown type %d", col.Ft.GetType()) - } - return nil -} - -// BytesDecoder decodes the row to old datums bytes. -type BytesDecoder struct { - decoder - defBytes func(i int) ([]byte, error) -} - -// NewByteDecoder creates a BytesDecoder. -// defBytes: provided default value bytes in old datum format(flag+colData). -func NewByteDecoder(columns []ColInfo, handleColIDs []int64, defBytes func(i int) ([]byte, error), loc *time.Location) *BytesDecoder { - return &BytesDecoder{ - decoder: decoder{ - columns: columns, - handleColIDs: handleColIDs, - loc: loc, - }, - defBytes: defBytes, - } -} - -func (decoder *BytesDecoder) decodeToBytesInternal(outputOffset map[int64]int, handle kv.Handle, value []byte, cacheBytes []byte) ([][]byte, error) { - var r row - err := r.fromBytes(value) - if err != nil { - return nil, err - } - values := make([][]byte, len(outputOffset)) - for i := range decoder.columns { - col := &decoder.columns[i] - tp := fieldType2Flag(col.Ft.GetType(), col.Ft.GetFlag()&mysql.UnsignedFlag == 0) - colID := col.ID - offset := outputOffset[colID] - idx, isNil, notFound := r.findColID(colID) - if !notFound && !isNil { - val := r.getData(idx) - values[offset] = decoder.encodeOldDatum(tp, val) - continue - } - - // Only try to decode handle when there is no corresponding column in the value. - // This is because the information in handle may be incomplete in some cases. - // For example, prefixed clustered index like 'primary key(col1(1))' only store the leftmost 1 char in the handle. - if decoder.tryDecodeHandle(values, offset, col, handle, cacheBytes) { - continue - } - - if isNil { - values[offset] = []byte{NilFlag} - continue - } - - if decoder.defBytes != nil { - defVal, err := decoder.defBytes(i) - if err != nil { - return nil, err - } - if len(defVal) > 0 { - values[offset] = defVal - continue - } - } - - values[offset] = []byte{NilFlag} - } - return values, nil -} - -func (decoder *BytesDecoder) tryDecodeHandle(values [][]byte, offset int, col *ColInfo, - handle kv.Handle, cacheBytes []byte) bool { - if handle == nil { - return false - } - if types.NeedRestoredData(col.Ft) { - return false - } - if col.IsPKHandle || col.ID == model.ExtraHandleID { - handleData := cacheBytes - if mysql.HasUnsignedFlag(col.Ft.GetFlag()) { - handleData = append(handleData, UintFlag) - handleData = codec.EncodeUint(handleData, uint64(handle.IntValue())) - } else { - handleData = append(handleData, IntFlag) - handleData = codec.EncodeInt(handleData, handle.IntValue()) - } - values[offset] = handleData - return true - } - var handleData []byte - for i, hid := range decoder.handleColIDs { - if col.ID == hid { - handleData = append(handleData, handle.EncodedCol(i)...) - } - } - if len(handleData) > 0 { - values[offset] = handleData - return true - } - return false -} - -// DecodeToBytesNoHandle decodes raw byte slice to row data without handle. -func (decoder *BytesDecoder) DecodeToBytesNoHandle(outputOffset map[int64]int, value []byte) ([][]byte, error) { - return decoder.decodeToBytesInternal(outputOffset, nil, value, nil) -} - -// DecodeToBytes decodes raw byte slice to row data. -func (decoder *BytesDecoder) DecodeToBytes(outputOffset map[int64]int, handle kv.Handle, value []byte, cacheBytes []byte) ([][]byte, error) { - return decoder.decodeToBytesInternal(outputOffset, handle, value, cacheBytes) -} - -func (*BytesDecoder) encodeOldDatum(tp byte, val []byte) []byte { - buf := make([]byte, 0, 1+binary.MaxVarintLen64+len(val)) - switch tp { - case BytesFlag: - buf = append(buf, CompactBytesFlag) - buf = codec.EncodeCompactBytes(buf, val) - case IntFlag: - buf = append(buf, VarintFlag) - buf = codec.EncodeVarint(buf, decodeInt(val)) - case UintFlag: - buf = append(buf, VaruintFlag) - buf = codec.EncodeUvarint(buf, decodeUint(val)) - default: - buf = append(buf, tp) - buf = append(buf, val...) - } - return buf -} - -// fieldType2Flag transforms field type into kv type flag. -func fieldType2Flag(tp byte, signed bool) (flag byte) { - switch tp { - case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong: - if signed { - flag = IntFlag - } else { - flag = UintFlag - } - case mysql.TypeFloat, mysql.TypeDouble: - flag = FloatFlag - case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob, - mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString: - flag = BytesFlag - case mysql.TypeDatetime, mysql.TypeDate, mysql.TypeTimestamp: - flag = UintFlag - case mysql.TypeDuration: - flag = IntFlag - case mysql.TypeNewDecimal: - flag = DecimalFlag - case mysql.TypeYear: - flag = IntFlag - case mysql.TypeEnum, mysql.TypeBit, mysql.TypeSet: - flag = UintFlag - case mysql.TypeJSON: - flag = JSONFlag - case mysql.TypeNull: - flag = NilFlag - default: - panic(fmt.Sprintf("unknown field type %d", tp)) - } - return -} diff --git a/util/rowcodec/main_test.go b/util/rowcodec/main_test.go deleted file mode 100644 index 9c634dc20d841..0000000000000 --- a/util/rowcodec/main_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package rowcodec - -import ( - "testing" - - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/codec" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} - -// EncodeFromOldRow encodes a row from an old-format row. -// this method will be used in test. -func EncodeFromOldRow(encoder *Encoder, sc *stmtctx.StatementContext, oldRow, buf []byte) ([]byte, error) { - if len(oldRow) > 0 && oldRow[0] == CodecVer { - return oldRow, nil - } - encoder.reset() - for len(oldRow) > 1 { - var d types.Datum - var err error - oldRow, d, err = codec.DecodeOne(oldRow) - if err != nil { - return nil, err - } - colID := d.GetInt64() - oldRow, d, err = codec.DecodeOne(oldRow) - if err != nil { - return nil, err - } - encoder.appendColVal(colID, &d) - } - numCols, notNullIdx := encoder.reformatCols() - err := encoder.encodeRowCols(sc, numCols, notNullIdx) - if err != nil { - return nil, err - } - return encoder.row.toBytes(buf[:0]), nil -} diff --git a/util/schemacmp/BUILD.bazel b/util/schemacmp/BUILD.bazel deleted file mode 100644 index 26b49f8158fb3..0000000000000 --- a/util/schemacmp/BUILD.bazel +++ /dev/null @@ -1,46 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "schemacmp", - srcs = [ - "lattice.go", - "table.go", - "type.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/util/schemacmp", - visibility = ["//visibility:public"], - deps = [ - "//parser/charset", - "//parser/format", - "//parser/model", - "//parser/mysql", - "//parser/types", - "//types", - ], -) - -go_test( - name = "schemacmp_test", - timeout = "short", - srcs = [ - "lattice_test.go", - "table_test.go", - "type_test.go", - ], - flaky = True, - deps = [ - ":schemacmp", - "//ddl", - "//parser", - "//parser/ast", - "//parser/model", - "//parser/mysql", - "//planner", - "//sessionctx", - "//types", - "//util/mock", - "@com_github_pingcap_errors//:errors", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/schemacmp/table.go b/util/schemacmp/table.go deleted file mode 100644 index 2fc645c03469f..0000000000000 --- a/util/schemacmp/table.go +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schemacmp - -import ( - "cmp" - "slices" - "strings" - - "github.com/pingcap/tidb/parser/format" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/parser/types" -) - -const ( - columnInfoTupleIndexDefaultValue = iota - columnInfoTupleIndexGeneratedExprString - columnInfoTupleIndexGeneratedStored - columnInfoTupleIndexFieldTypes -) - -// encodeColumnInfoToLattice collects the necessary information for comparing a column. -func encodeColumnInfoToLattice(ci *model.ColumnInfo) Tuple { - return Tuple{ - MaybeSingletonInterface(ci.DefaultValue), - Singleton(ci.GeneratedExprString), - Singleton(ci.GeneratedStored), - Type(&ci.FieldType), - } -} - -// restoreColumnInfoFromUnwrapped restores the text representation of a column. -func restoreColumnInfoFromUnwrapped(ctx *format.RestoreCtx, col []interface{}, colName string) { - typ := col[columnInfoTupleIndexFieldTypes].(*types.FieldType) - - ctx.WriteName(colName) - ctx.WritePlain(" ") - _ = typ.Restore(ctx) - if genExpr := col[columnInfoTupleIndexGeneratedExprString].(string); len(genExpr) != 0 { - ctx.WriteKeyWord(" GENERATED ALWAYS AS ") - ctx.WritePlainf("(%s)", genExpr) - } - if col[columnInfoTupleIndexGeneratedStored].(bool) { - ctx.WriteKeyWord(" STORED") - } - if mysql.HasNotNullFlag(typ.GetFlag()) { - ctx.WriteKeyWord(" NOT NULL") - } - if defVal := col[columnInfoTupleIndexDefaultValue]; defVal != nil { - ctx.WriteKeyWord(" DEFAULT ") - ctx.WritePlainf("%v", defVal) - } - if mysql.HasAutoIncrementFlag(typ.GetFlag()) { - ctx.WriteKeyWord(" AUTO_INCREMENT") - } -} - -const ( - indexInfoTupleIndexColumns = iota - indexInfoTupleIndexNotUnique - indexInfoTupleIndexNotPrimary - indexInfoTupleIndexType -) - -type indexColumn struct { - colName string - length int -} - -type indexColumnSlice []indexColumn - -// Equals implements Equality -func (a indexColumnSlice) Equals(other Equality) bool { - b, ok := other.(indexColumnSlice) - if !ok || len(a) != len(b) { - return false - } - for i, av := range a { - if av.colName != b[i].colName || av.length != b[i].length { - return false - } - } - return true -} - -func encodeIndexInfoToLattice(ii *model.IndexInfo) Tuple { - indexColumns := make(indexColumnSlice, 0, len(ii.Columns)) - for _, column := range ii.Columns { - indexColumns = append(indexColumns, indexColumn{colName: column.Name.L, length: column.Length}) - } - - return Tuple{ - EqualitySingleton(indexColumns), - Bool(!ii.Unique), - Bool(!ii.Primary), - Singleton(ii.Tp), - } -} -func encodeImplicitPrimaryKeyToLattice(ci *model.ColumnInfo) Tuple { - return Tuple{ - EqualitySingleton(indexColumnSlice{indexColumn{colName: ci.Name.L, length: types.UnspecifiedLength}}), - Bool(false), - Bool(false), - Singleton(model.IndexTypeBtree), - } -} - -func restoreIndexInfoFromUnwrapped(ctx *format.RestoreCtx, index []interface{}, keyName string) { - isPrimary := !index[indexInfoTupleIndexNotPrimary].(bool) - - switch { - case isPrimary: - ctx.WriteKeyWord("PRIMARY KEY") - case !index[indexInfoTupleIndexNotUnique].(bool): - ctx.WriteKeyWord("UNIQUE KEY ") - ctx.WriteName(keyName) - default: - ctx.WriteKeyWord("KEY ") - ctx.WriteName(keyName) - } - - if tp := index[indexInfoTupleIndexType].(model.IndexType); tp != model.IndexTypeBtree { - ctx.WriteKeyWord(" USING ") - ctx.WriteKeyWord(tp.String()) - } - - ctx.WritePlain(" (") - for i, column := range index[indexInfoTupleIndexColumns].(indexColumnSlice) { - if i != 0 { - ctx.WritePlain(", ") - } - ctx.WriteName(column.colName) - if column.length != types.UnspecifiedLength { - ctx.WritePlainf("(%d)", column.length) - } - } - ctx.WritePlain(")") -} - -type columnMap map[string]Tuple - -func (columnMap) New() LatticeMap { - return make(columnMap) -} - -func (a columnMap) Get(key string) Lattice { - val, ok := a[key] - if !ok { - return nil - } - return val -} - -func (a columnMap) Insert(key string, value Lattice) { - a[key] = value.(Tuple) -} - -func (a columnMap) ForEach(f func(key string, value Lattice) error) error { - for key, value := range a { - if err := f(key, value); err != nil { - return err - } - } - return nil -} - -func (columnMap) CompareWithNil(value Lattice) (int, error) { - if value.(Tuple)[columnInfoTupleIndexFieldTypes].(typ).hasDefault() { - return 1, nil - } - return 0, &IncompatibleError{Msg: "column with no default value cannot be missing"} -} - -func (columnMap) ShouldDeleteIncompatibleJoin() bool { - return false -} - -func (columnMap) JoinWithNil(value Lattice) (Lattice, error) { - col := append(make(Tuple, 0), value.(Tuple)...) - ty := col[columnInfoTupleIndexFieldTypes].(typ).clone() - if ty.setFlagForMissingColumn() && ty.isNotNull() { - col[columnInfoTupleIndexDefaultValue] = Maybe(Singleton(ty.getStandardDefaultValue())) - } - col[columnInfoTupleIndexFieldTypes] = ty - return col, nil -} - -type indexMap map[string]Tuple - -func (indexMap) New() LatticeMap { - return make(indexMap) -} - -func (a indexMap) Get(key string) Lattice { - val, ok := a[key] - if !ok { - return nil - } - return val -} - -func (a indexMap) Insert(key string, value Lattice) { - a[key] = value.(Tuple) -} - -func (a indexMap) ForEach(f func(key string, value Lattice) error) error { - for key, value := range a { - if err := f(key, value); err != nil { - return err - } - } - return nil -} - -func (indexMap) CompareWithNil(_ Lattice) (int, error) { - return -1, nil -} - -func (indexMap) ShouldDeleteIncompatibleJoin() bool { - return true -} - -func (indexMap) JoinWithNil(_ Lattice) (Lattice, error) { - return nil, nil -} - -const ( - tableInfoTupleIndexCharset = iota - tableInfoTupleIndexCollate - tableInfoTupleIndexColumns - tableInfoTupleIndexIndices - // nolint:unused, varcheck, deadcode - tableInfoTupleIndexAutoIncID - tableInfoTupleIndexShardRowIDBits - tableInfoTupleIndexAutoRandomBits - // nolint: unused, varcheck, deadcode - tableInfoTupleIndexPreSplitRegions - tableInfoTupleIndexCompression -) - -func encodeTableInfoToLattice(ti *model.TableInfo) Tuple { - // TODO: Handle VIEW and PARTITION and SEQUENCE - hasExplicitPrimaryKey := false - indices := make(indexMap) - for _, ii := range ti.Indices { - if ii.Primary { - hasExplicitPrimaryKey = true - } - indices[ii.Name.L] = encodeIndexInfoToLattice(ii) - } - columns := make(columnMap) - for _, ci := range ti.Columns { - columns[ci.Name.L] = encodeColumnInfoToLattice(ci) - if !hasExplicitPrimaryKey && (ci.GetFlag()&mysql.PriKeyFlag) != 0 { - indices["primary"] = encodeImplicitPrimaryKeyToLattice(ci) - } - } - - return Tuple{ - Singleton(ti.Charset), - Singleton(ti.Collate), - Map(columns), - Map(indices), - // TODO ForeignKeys? - Int64(ti.AutoIncID), - // TODO Relax these? - Singleton(ti.ShardRowIDBits), - Singleton(ti.AutoRandomBits), - Singleton(ti.PreSplitRegions), - MaybeSingletonString(ti.Compression), - } -} - -type kvPair struct { - value interface{} - key string -} - -func sortedMap(input map[string]interface{}) []kvPair { - res := make([]kvPair, 0, len(input)) - for key, value := range input { - res = append(res, kvPair{key: key, value: value}) - } - - slices.SortFunc(res, func(a, b kvPair) int { - return cmp.Compare(a.key, b.key) - }) - return res -} - -func restoreTableInfoFromUnwrapped(ctx *format.RestoreCtx, table []interface{}, tableName string) { - ctx.WriteKeyWord("CREATE TABLE ") - ctx.WriteName(tableName) - ctx.WritePlain("(") - - for i, pair := range sortedMap(table[tableInfoTupleIndexColumns].(map[string]interface{})) { - if i != 0 { - ctx.WritePlain(", ") - } - colName := pair.key - column := pair.value.([]interface{}) - restoreColumnInfoFromUnwrapped(ctx, column, colName) - } - - for _, pair := range sortedMap(table[tableInfoTupleIndexIndices].(map[string]interface{})) { - ctx.WritePlain(", ") - indexName := pair.key - index := pair.value.([]interface{}) - restoreIndexInfoFromUnwrapped(ctx, index, indexName) - } - - ctx.WritePlain(")") - if charset := table[tableInfoTupleIndexCharset].(string); charset != "" { - ctx.WriteKeyWord(" CHARSET ") - ctx.WriteKeyWord(charset) - } - if collate := table[tableInfoTupleIndexCollate].(string); collate != "" { - ctx.WriteKeyWord(" COLLATE ") - ctx.WriteKeyWord(collate) - } - if bits := table[tableInfoTupleIndexShardRowIDBits].(uint64); bits > 0 { - ctx.WriteKeyWord(" SHARD_ROW_ID_BITS ") - ctx.WritePlainf("%d", bits) - } - if bits := table[tableInfoTupleIndexAutoRandomBits].(uint64); bits > 0 { - ctx.WritePlain("/*") - ctx.WriteKeyWord(" AUTO_RANDOM_BITS ") - ctx.WritePlainf("%d */", bits) - } - if compression, ok := table[tableInfoTupleIndexCompression].(string); ok && len(compression) != 0 { - ctx.WriteKeyWord(" COMPRESSION ") - ctx.WriteString(compression) - } -} - -// Table is a table in the database. -type Table struct{ value Lattice } - -// Encode is used to encode a Table. -func Encode(ti *model.TableInfo) Table { - return Table{value: encodeTableInfoToLattice(ti)} -} - -// DecodeColumnFieldTypes is used to decode column field types from Lattice. -func DecodeColumnFieldTypes(t Table) map[string]*types.FieldType { - table := t.value.Unwrap().([]interface{}) - columnMaps := table[tableInfoTupleIndexColumns].(map[string]interface{}) - cols := make(map[string]*types.FieldType, len(columnMaps)) - for key, value := range columnMaps { - cols[key] = value.([]interface{})[columnInfoTupleIndexFieldTypes].(*types.FieldType) - } - return cols -} - -// Restore is for debug use only. -func (t Table) Restore(ctx *format.RestoreCtx, tableName string) { - restoreTableInfoFromUnwrapped(ctx, t.value.Unwrap().([]interface{}), tableName) -} - -// Compare is the implementation of Lattice interface. -func (t Table) Compare(other Table) (int, error) { - return t.value.Compare(other.value) -} - -// Join is a helper function to join two tables. -func (t Table) Join(other Table) (Table, error) { - res, err := t.value.Join(other.value) - if err != nil { - return Table{value: nil}, err - } - - // fix up the type's key flags. - // unfortunately we cannot count on the type's own flag joining - // because an index's joining rule is more complex than 3 bits. - columnKeyFlags := make(map[string]uint) - table := res.(Tuple) - for _, index := range table[tableInfoTupleIndexIndices].(latticeMap).LatticeMap.(indexMap) { - cols := index[indexInfoTupleIndexColumns].Unwrap().(indexColumnSlice) - if len(cols) == 0 { - continue - } - switch { - case !index[indexInfoTupleIndexNotPrimary].Unwrap().(bool): - for _, col := range cols { - columnKeyFlags[col.colName] |= mysql.PriKeyFlag - } - case !index[indexInfoTupleIndexNotUnique].Unwrap().(bool) && len(cols) == 1: - columnKeyFlags[cols[0].colName] |= mysql.UniqueKeyFlag - default: - // Only the first column can be set if index or unique index has multiple columns. - // See https://dev.mysql.com/doc/refman/5.7/en/show-columns.html. - columnKeyFlags[cols[0].colName] |= mysql.MultipleKeyFlag - } - } - columns := table[tableInfoTupleIndexColumns].(latticeMap).LatticeMap.(columnMap) - for name, column := range columns { - ty := column[columnInfoTupleIndexFieldTypes].(typ) - flag, ok := columnKeyFlags[name] - if !ok && ty.inAutoIncrement() { - return Table{value: nil}, &IncompatibleError{ - Msg: ErrMsgAtMapKey, - Args: []interface{}{name, &IncompatibleError{Msg: ErrMsgAutoTypeWithoutKey}}, - } - } - ty.setAntiKeyFlags(flag) - } - - return Table{value: table}, nil -} - -func (t Table) String() string { - var sb strings.Builder - ctx := format.NewRestoreCtx(format.DefaultRestoreFlags, &sb) - t.Restore(ctx, "tbl") - return sb.String() -} diff --git a/util/schemacmp/table_test.go b/util/schemacmp/table_test.go deleted file mode 100644 index 425f3a64ac1dd..0000000000000 --- a/util/schemacmp/table_test.go +++ /dev/null @@ -1,517 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schemacmp_test - -import ( - "strings" - "testing" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/ddl" - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/parser/ast" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - _ "github.com/pingcap/tidb/planner" - "github.com/pingcap/tidb/sessionctx" - "github.com/pingcap/tidb/util/mock" - "github.com/pingcap/tidb/util/schemacmp" - "github.com/stretchr/testify/require" -) - -func toTableInfo(parser *parser.Parser, sctx sessionctx.Context, createTableStmt string) (*model.TableInfo, error) { - node, err := parser.ParseOneStmt(createTableStmt, "", "") - if err != nil { - return nil, err - } - createStmtNode, ok := node.(*ast.CreateTableStmt) - if !ok { - return nil, errors.New("not a create table statement") - } - return ddl.MockTableInfo(sctx, createStmtNode, 1) -} - -func checkDecodeFieldTypes(t *testing.T, info *model.TableInfo, tt schemacmp.Table) { - fieldTyps := schemacmp.DecodeColumnFieldTypes(tt) - require.Len(t, fieldTyps, len(info.Columns)) - for _, col := range info.Columns { - typ, ok := fieldTyps[col.Name.O] - require.True(t, ok) - require.Equal(t, *typ, col.FieldType) - } -} - -func TestJoinSchemas(t *testing.T) { - p := parser.New() - sctx := mock.NewContext() - testCases := []struct { - name string - a string - b string - cmp int - cmpErr string - join string - joinErr string - }{ - { - name: "DM_002/1", - a: "CREATE TABLE tb1 (col1 INT)", - b: "CREATE TABLE tb2 (col1 INT, new_col1 INT)", - cmp: -1, - join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", - }, - { - name: "DM_002/1/unordered", - a: "CREATE TABLE tb1 (col1 INT)", - b: "CREATE TABLE tb2 (new_col1 INT, col1 INT)", - cmp: -1, - join: "CREATE TABLE tb3 (new_col1 INT, col1 INT)", - }, - { - name: "DM_002/2", - a: "CREATE TABLE tb1 (col1 INT, new_col1 INT)", - b: "CREATE TABLE tb2 (col1 INT, new_col1 INT)", - cmp: 0, - join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", - }, - { - name: "DM_002/2/unordered", - a: "CREATE TABLE tb1 (col1 INT, new_col1 INT)", - b: "CREATE TABLE tb2 (new_col1 INT, col1 INT)", - cmp: 0, - join: "CREATE TABLE tb3 (col1 INT, new_col1 INT)", - }, - { - name: "DM_010", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmp: 1, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", - }, - { - name: "DM_011", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 FLOAT)", - cmpErr: `.*"new_col1".*incompatible mysql type.*`, - joinErr: `.*"new_col1".*incompatible mysql type.*`, - }, - { - name: "DM_014", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col2 INT)", - cmpErr: `.*combining contradicting orders.*`, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), new_col1 INT, new_col2 INT)", - }, - { - name: "DM_031/VARCHAR", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 VARCHAR(10))", - cmpErr: `.*"new_col1".*incompatible mysql type.*`, - joinErr: `.*"new_col1".*incompatible mysql type.*`, - }, - { - name: "DM_031/TEXT", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 TEXT)", - cmpErr: `.*"new_col1".*incompatible mysql type.*`, - joinErr: `.*"new_col1".*incompatible mysql type.*`, - }, - { - name: "DM_031/JSON", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 JSON)", - cmpErr: `.*"new_col1".*incompatible mysql type.*`, - joinErr: `.*"new_col1".*incompatible mysql type.*`, - }, - { - name: "DM_033", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), c FLOAT NOT NULL)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmpErr: `.*"c": column with no default value cannot be missing`, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), c FLOAT NOT NULL DEFAULT 0)", - }, - { - name: "DM_034", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT UNIQUE AUTO_INCREMENT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmpErr: `.*combining contradicting orders.*`, - joinErr: `.*"new_col1".*auto type but not defined as a key`, - }, - { - name: "DM_035", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 INT, col2 INT)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col2 INT, col1 INT)", - cmp: 0, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 INT, col2 INT)", - }, - { - name: "DM_037", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 INT DEFAULT 0)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 INT DEFAULT -1)", - cmpErr: `.*"col1".*distinct singletons.*`, - joinErr: `.*"col1".*distinct singletons.*`, - }, - { - name: "DM_039/1", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmp: 1, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - }, - { - name: "DM_039/2", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - cmp: 0, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - }, - { - name: "DM_040", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8 COLLATE utf8_bin)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), col1 VARCHAR(10) CHARSET utf8mb4 COLLATE utf8mb4_bin)", - cmpErr: `.*"col1".*distinct singletons.*`, - joinErr: `.*"col1".*distinct singletons.*`, - }, - { - name: "DM_041/1", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmp: 1, - join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - }, - { - name: "DM_041/2", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - cmp: 0, - join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - }, - { - name: "DM_042", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmp: 1, - join: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", - }, - { - name: "DM_043", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 2))", - cmpErr: `.*"new_col1".*distinct singletons.*`, - joinErr: `.*"new_col1".*distinct singletons.*`, - }, - { - name: "DM_044", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) VIRTUAL)", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), new_col1 INT AS (a + 1) STORED)", - cmpErr: `.*"new_col1".*distinct singletons.*`, - joinErr: `.*"new_col1".*distinct singletons.*`, - }, - { - name: "DM_052", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (c BIGINT, b VARCHAR(10))", - cmpErr: `.*combining contradicting orders.*`, - join: `CREATE TABLE tb3 (a INT, b VARCHAR(10), c BIGINT)`, - }, - { - name: "DM_053", - a: "CREATE TABLE tb1 (c BIGINT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (c DOUBLE, b VARCHAR(10))", - cmpErr: `.*"c".*incompatible mysql type.*`, - joinErr: `.*"c".*incompatible mysql type.*`, - }, - { - name: "DM_055", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (a BIGINT, b VARCHAR(10))", - cmp: -1, - join: "CREATE TABLE tb2 (a BIGINT, b VARCHAR(10))", - }, - { - name: "DM_057", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (c INT DEFAULT 1, b VARCHAR(10))", - cmpErr: `.*combining contradicting orders.*`, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), c INT DEFAULT 1)", - }, - { - name: "DM_061", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10) CHARSET utf8)", - cmpErr: `.*"b".*distinct singletons.*`, - joinErr: `.*"b".*distinct singletons.*`, - }, - { - name: "DM_066", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (a INT DEFAULT 1, b VARCHAR(10))", - cmp: -1, - join: "CREATE TABLE tb3 (a INT DEFAULT 1, b VARCHAR(10))", - }, - // { // these table options are somehow ignored by the parser. - // name: "DM_074", - // a: "CREATE TABLE tbl1 (a INT, b VARCHAR(10)) CHARSET utf8 COLLATE utf8_bin", - // b: "CREATE TABLE tbl2 (a INT, b VARCHAR(10)) CHARSET utf8mb4 COLLATE utf8mb4_bin", - // joinErr: `.*distinct singletons.*`, - // }, - { - name: "DM_078", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10))", - b: "CREATE TABLE tb2 (a INT PRIMARY KEY, b VARCHAR(10))", - cmp: 1, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10))", - }, - { - name: "DM_080/1", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b), UNIQUE KEY idx_ab(a, b))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10))", - cmp: -1, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10))", - }, - { - name: "DM_080/2", - a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b), UNIQUE KEY idx_ab(a, b))", - b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b))", - cmp: -1, - join: "CREATE TABLE tb3 (a INT, b VARCHAR(10), UNIQUE KEY idx_a(a), UNIQUE KEY idx_b(b))", - }, - // { // index visibility is not visible in IndexInfo yet. - // name: "DM_086", - // a: "CREATE TABLE tb1 (a INT, b VARCHAR(10), UNIQUE KEY a(a) VISIBLE)", - // b: "CREATE TABLE tb2 (a INT, b VARCHAR(10), UNIQUE KEY a(a) INVISIBLE)", - // joinErr: `.*distinct singletons.*`, - // }, - { - name: "Different index components", - a: "CREATE TABLE tbl1 (a INT, b INT, KEY i(a))", - b: "CREATE TABLE tbl2 (a INT, b INT, KEY i(b))", - cmpErr: `.*combining contradicting orders.*`, - join: "CREATE TABLE tbl3 (a INT, b INT)", - }, - { - name: "Different index order", - a: "CREATE TABLE tbl1 (a INT, b INT, KEY i(a, b))", - b: "CREATE TABLE tbl2 (a INT, b INT, KEY i(b, a))", - cmpErr: `.*combining contradicting orders.*`, - join: "CREATE TABLE tbl3 (a INT, b INT)", - }, - { - name: "Different index length", - a: "CREATE TABLE tbl1 (a TEXT, KEY i(a(14)))", - b: "CREATE TABLE tbl2 (a TEXT, KEY i(a(15)))", - cmpErr: `.*distinct singletons.*`, - join: "CREATE TABLE tbl3 (a TEXT)", - }, - { - name: "Cannot drop key tied to AUTO_INC column", - a: "CREATE TABLE tbl1(a INT AUTO_INCREMENT, b INT, KEY i(a))", - b: "CREATE TABLE tbl2(a INT AUTO_INCREMENT, b INT, KEY i(a, b))", - cmpErr: `.*distinct singletons.*`, - joinErr: `.*"a".*auto type but not defined as a key`, - }, - { - name: "not-null column with special types", - a: `CREATE TABLE tbl1( - a1 INT NOT NULL, - b1 DECIMAL NOT NULL, - c1 VARCHAR(20) NOT NULL, - d1 DATETIME(3) NOT NULL, - e1 ENUM('abc', 'def') NOT NULL - )`, - b: `CREATE TABLE tbl2( - a2 TIME NOT NULL, - b2 DATE NOT NULL, - c2 BINARY(50) NOT NULL, - d2 YEAR(4) NOT NULL, - e2 SET('abc', 'def') NOT NULL - )`, - cmpErr: `.*column with no default value cannot be missing`, - join: `CREATE TABLE tbl3( - a1 INT NOT NULL DEFAULT 0, - b1 DECIMAL NOT NULL DEFAULT 0, - c1 VARCHAR(20) NOT NULL DEFAULT '', - d1 DATETIME(3) NOT NULL DEFAULT '0000-00-00 00:00:00', - e1 ENUM('abc', 'def') NOT NULL DEFAULT 'abc', - a2 TIME NOT NULL DEFAULT '00:00:00', - b2 DATE NOT NULL DEFAULT '0000-00-00', - c2 BINARY(50) NOT NULL DEFAULT '', - d2 YEAR(4) NOT NULL DEFAULT '0000', - e2 SET('abc', 'def') NOT NULL DEFAULT '' - )`, - }, - { - name: "test case 2020-03-17", - a: `CREATE TABLE bar (id INT PRIMARY KEY)`, - b: `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`, - cmp: -1, - join: `CREATE TABLE bar (id INT PRIMARY KEY, c1 INT)`, - }, - { - name: "test case 2020-03-17-alt", - a: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY)`, - b: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY, c1 INT)`, - cmp: -1, - join: `CREATE TABLE bar (id VARCHAR(10) PRIMARY KEY, c1 INT)`, - }, - { - name: "test case 2020-03-17-alt-2", - a: `CREATE TABLE bar (id INT PRIMARY KEY)`, - b: `CREATE TABLE bar (id INT, c1 INT)`, - cmp: -1, - join: `CREATE TABLE bar (id INT, c1 INT)`, - }, - { - name: "test case 2020-03-17-alt-3", - a: `CREATE TABLE bar (id1 INT PRIMARY KEY, id2 INT)`, - b: `CREATE TABLE bar (id1 INT, id2 INT PRIMARY KEY)`, - cmpErr: `.*combining contradicting orders.*`, - join: `CREATE TABLE bar (id1 INT, id2 INT)`, - }, - { - name: "test case 2020-04-28-blob", - a: "CREATE TABLE tb1 (a BLOB, b VARCHAR(10))", - b: "CREATE TABLE tb2 (a LONGBLOB, b VARCHAR(10))", - cmp: -1, - join: "CREATE TABLE tb2 (a LONGBLOB, b VARCHAR(10))", - }, - { - name: "join equal single primary key", - a: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", - b: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", - cmp: 0, - join: "CREATE TABLE t(a INT, b INT, PRIMARY KEY(a))", - }, - { - name: "join equal composite primary key", - a: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", - b: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", - cmp: 0, - join: "CREATE TABLE t(a INT, b INT, c INT, PRIMARY KEY(a, b))", - }, - { - name: "join equal single index", - a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", - b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", - cmp: 0, - join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_b(b))", - }, - { - name: "join equal unique index", - a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", - b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", - cmp: 0, - join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE KEY uni_b(b))", - }, - { - name: "join equal composite index", - a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", - b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", - cmp: 0, - join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, INDEX idx_bc(b, c))", - }, - { - name: "join equal composite unique index", - a: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", - b: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", - cmp: 0, - join: "CREATE TABLE t(a INT PRIMARY KEY, b INT, c INT, UNIQUE INDEX idx_bc(b, c))", - }, - } - - for _, tc := range testCases { - tia, err := toTableInfo(p, sctx, tc.a) - require.NoError(t, err) - tib, err := toTableInfo(p, sctx, tc.b) - require.NoError(t, err) - - a := schemacmp.Encode(tia) - b := schemacmp.Encode(tib) - checkDecodeFieldTypes(t, tia, a) - checkDecodeFieldTypes(t, tib, b) - var j schemacmp.Table - if len(tc.joinErr) == 0 { - tij, err := toTableInfo(p, sctx, tc.join) - require.NoError(t, err) - j = schemacmp.Encode(tij) - } - - cmp, err := a.Compare(b) - if len(tc.cmpErr) != 0 { - require.Regexp(t, tc.cmpErr, err) - } else { - require.NoError(t, err) - require.Equal(t, tc.cmp, cmp) - } - - cmp, err = b.Compare(a) - if len(tc.cmpErr) != 0 { - require.Regexp(t, tc.cmpErr, err) - } else { - require.NoError(t, err) - require.Equal(t, cmp, -tc.cmp) - } - - joined, err := a.Join(b) - if len(tc.joinErr) != 0 { - if err == nil { - t.Log("a = ", a) - t.Log("b = ", b) - t.Log("j = ", joined) - } - require.Regexp(t, tc.joinErr, err) - } else { - require.NoError(t, err) - require.Equal(t, joined, j) - } - - joined, err = b.Join(a) - if len(tc.joinErr) != 0 { - require.Regexp(t, tc.joinErr, err) - } else { - require.NoError(t, err) - require.Equal(t, joined, j) - cmp, err = joined.Compare(a) - - require.NoError(t, err) - require.GreaterOrEqual(t, cmp, 0) - - cmp, err = joined.Compare(b) - require.NoError(t, err) - require.GreaterOrEqual(t, cmp, 0) - } - } -} - -func TestTableString(t *testing.T) { - p := parser.New() - sctx := mock.NewContext() - ti, err := toTableInfo(p, sctx, "CREATE TABLE tb (a INT, b INT)") - require.NoError(t, err) - - charsets := []string{"", mysql.DefaultCharset} - collates := []string{"", mysql.DefaultCollationName} - for _, charset := range charsets { - for _, collate := range collates { - ti.Charset = charset - ti.Collate = collate - sql := strings.ToLower(schemacmp.Encode(ti).String()) - require.Equal(t, charset != "", strings.Contains(sql, "charset")) - require.Equal(t, collate != "", strings.Contains(sql, "collate")) - _, err := toTableInfo(p, sctx, sql) - require.NoError(t, err) - } - } -} diff --git a/util/schemacmp/type.go b/util/schemacmp/type.go deleted file mode 100644 index c5e227f349811..0000000000000 --- a/util/schemacmp/type.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schemacmp - -import ( - "math/bits" - "strings" - - "github.com/pingcap/tidb/parser/charset" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" -) - -const ( - flagMaskKeys = mysql.PriKeyFlag | mysql.UniqueKeyFlag | mysql.MultipleKeyFlag - flagMaskDefVal = mysql.AutoIncrementFlag | mysql.NoDefaultValueFlag - - notPartOfKeys = ^byte(0) -) - -// Please ensure this list is synchronized with the order of Tuple{} in encodeFieldTypeToLattice(). -const ( - fieldTypeTupleIndexTp = iota - fieldTypeTupleIndexFlen - fieldTypeTupleIndexDec - fieldTypeTupleIndexFlagSingleton - fieldTypeTupleIndexFlagNull - fieldTypeTupleIndexFlagAntiKeys - fieldTypeTupleIndexFlagDefVal - fieldTypeTupleIndexCharset - fieldTypeTupleIndexCollate - fieldTypeTupleIndexElems - - ErrMsgAutoTypeWithoutKey = "auto type but not defined as a key" -) - -func encodeAntiKeys(flag uint) byte { - // this ensure we get this order: - // 1. "not part of keys" (flag = 0) is the maximum - // 2. multiple keys (8) > unique key (4) > primary key (2). - return ^bits.Reverse8(byte(flag & flagMaskKeys)) -} - -func decodeAntiKeys(encoded byte) uint { - return uint(bits.Reverse8(^encoded)) -} - -// encodeTypeTpAsLattice -func encodeFieldTypeToLattice(ft *types.FieldType) Tuple { - var flen, dec Lattice - if ft.GetType() == mysql.TypeNewDecimal { - flen = Singleton(ft.GetFlen()) - dec = Singleton(ft.GetDecimal()) - } else { - flen = Int(ft.GetFlen()) - dec = Int(ft.GetDecimal()) - } - - var defVal Lattice - if mysql.HasAutoIncrementFlag(ft.GetFlag()) || !mysql.HasNoDefaultValueFlag(ft.GetFlag()) { - defVal = Maybe(Singleton(ft.GetFlag() & flagMaskDefVal)) - } else { - defVal = Maybe(nil) - } - - return Tuple{ - FieldTp(ft.GetType()), - flen, - dec, - - // TODO: recognize if the remaining flags can be merged or not. - Singleton(ft.GetFlag() &^ (flagMaskDefVal | mysql.NotNullFlag | flagMaskKeys)), - Bool(!mysql.HasNotNullFlag(ft.GetFlag())), - Byte(encodeAntiKeys(ft.GetFlag())), - defVal, - - Singleton(ft.GetCharset()), - Singleton(ft.GetCollate()), - StringList(ft.GetElems()), - } -} - -func decodeFieldTypeFromLattice(tup Tuple) *types.FieldType { - lst := tup.Unwrap().([]interface{}) - - flags := lst[fieldTypeTupleIndexFlagSingleton].(uint) - flags |= decodeAntiKeys(lst[fieldTypeTupleIndexFlagAntiKeys].(byte)) - if !lst[fieldTypeTupleIndexFlagNull].(bool) { - flags |= mysql.NotNullFlag - } - if x, ok := lst[fieldTypeTupleIndexFlagDefVal].(uint); ok { - flags |= x - } else { - flags |= mysql.NoDefaultValueFlag - } - - return types.NewFieldTypeBuilder().SetType(lst[fieldTypeTupleIndexTp].(byte)).SetFlen(lst[fieldTypeTupleIndexFlen].(int)).SetDecimal(lst[fieldTypeTupleIndexDec].(int)).SetFlag(flags).SetCharset(lst[fieldTypeTupleIndexCharset].(string)).SetCollate(lst[fieldTypeTupleIndexCollate].(string)).SetElems(lst[fieldTypeTupleIndexElems].([]string)).BuildP() -} - -type typ struct{ Tuple } - -// Type is to create type. -func Type(ft *types.FieldType) typ { - return typ{Tuple: encodeFieldTypeToLattice(ft)} -} - -func (a typ) hasDefault() bool { - return a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap() != nil -} - -// setFlagForMissingColumn adjusts the flags of the type for filling in a -// missing column. Returns whether the column had no default values. -// If the column is AUTO_INCREMENT, returns an incompatible error, because such -// column cannot be part of any keys in the joined table which is invalid. -func (a typ) setFlagForMissingColumn() (hadNoDefault bool) { - a.Tuple[fieldTypeTupleIndexFlagAntiKeys] = Byte(notPartOfKeys) - defVal, ok := a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) - if !ok || mysql.HasNoDefaultValueFlag(defVal) { - a.Tuple[fieldTypeTupleIndexFlagDefVal] = Maybe(Singleton(defVal &^ mysql.NoDefaultValueFlag)) - return true - } - return false -} - -func (a typ) isNotNull() bool { - return !a.Tuple[fieldTypeTupleIndexFlagNull].Unwrap().(bool) -} - -func (a typ) inAutoIncrement() bool { - defVal, ok := a.Tuple[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) - return ok && mysql.HasAutoIncrementFlag(defVal) -} - -func (a typ) setAntiKeyFlags(flag uint) { - a.Tuple[fieldTypeTupleIndexFlagAntiKeys] = Byte(encodeAntiKeys(flag)) -} - -func (a typ) getStandardDefaultValue() interface{} { - var tail string - if dec := a.Tuple[fieldTypeTupleIndexDec].Unwrap().(int); dec > 0 { - tail = "." + strings.Repeat("0", dec) - } - - switch a.Tuple[fieldTypeTupleIndexTp].Unwrap().(byte) { - case mysql.TypeTiny, mysql.TypeInt24, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeFloat, mysql.TypeDouble, mysql.TypeNewDecimal: - return "0" - case mysql.TypeTimestamp, mysql.TypeDatetime: - return "0000-00-00 00:00:00" + tail - case mysql.TypeDate: - return "0000-00-00" - case mysql.TypeDuration: - return "00:00:00" + tail - case mysql.TypeYear: - return "0000" - case mysql.TypeJSON: - return "null" - case mysql.TypeEnum: - return a.Tuple[fieldTypeTupleIndexElems].(StringList)[0] - case mysql.TypeString: - // ref https://github.com/pingcap/tidb/blob/66948b2fd9bec8ea11644770a2fa746c7eba1a1f/ddl/ddl_api.go#L3916 - if a.Tuple[fieldTypeTupleIndexCollate].Unwrap().(string) == charset.CollationBin { - return string(make([]byte, a.Tuple[fieldTypeTupleIndexFlen].Unwrap().(int))) - } - return "" - default: - return "" - } -} - -func (a typ) clone() typ { - return typ{Tuple: append(make(Tuple, 0, len(a.Tuple)), a.Tuple...)} -} - -func (a typ) Unwrap() interface{} { - return decodeFieldTypeFromLattice(a.Tuple) -} - -func (a typ) Compare(other Lattice) (int, error) { - if b, ok := other.(typ); ok { - return a.Tuple.Compare(b.Tuple) - } - return 0, typeMismatchError(a, other) -} - -func (a typ) Join(other Lattice) (Lattice, error) { - if b, ok := other.(typ); ok { - genJoin, err := a.Tuple.Join(b.Tuple) - if err != nil { - return nil, err - } - join := genJoin.(Tuple) - - // Special check: we can't have an AUTO_INCREMENT column without being a KEY. - x, ok := join[fieldTypeTupleIndexFlagDefVal].Unwrap().(uint) - if ok && x&mysql.AutoIncrementFlag != 0 && join[fieldTypeTupleIndexFlagAntiKeys].Unwrap() == notPartOfKeys { - return nil, &IncompatibleError{Msg: ErrMsgAutoTypeWithoutKey} - } - - return typ{Tuple: join}, nil - } - return nil, typeMismatchError(a, other) -} diff --git a/util/schemacmp/util.go b/util/schemacmp/util.go deleted file mode 100644 index ccce6f6c616f2..0000000000000 --- a/util/schemacmp/util.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package schemacmp - -import "github.com/pingcap/tidb/parser/mysql" - -// compareMySQLIntegerType compares two MySQL integer types, -// return -1 if a < b, 0 if a == b and 1 if a > b. -func compareMySQLIntegerType(a, b byte) int { - if a == b { - return 0 - } - - // TypeTiny(1) < TypeShort(2) < TypeInt24(9) < TypeLong(3) < TypeLonglong(8) - switch { - case a == mysql.TypeInt24: - if b <= mysql.TypeShort { - return 1 - } - return -1 - case b == mysql.TypeInt24: - if a <= mysql.TypeShort { - return -1 - } - return 1 - case a < b: - return -1 - default: - return 1 - } -} - -// compareMySQLBlobType compares two MySQL blob types, -// return -1 if a < b, 0 if a == b and 1 if a > b. -func compareMySQLBlobType(a, b byte) int { - if a == b { - return 0 - } - - // TypeTinyBlob(0xf9, 249) < TypeBlob(0xfc, 252) < TypeMediumBlob(0xfa, 250) < TypeLongBlob(0xfb, 251) - switch { - case a == mysql.TypeBlob: - if b == mysql.TypeTinyBlob { - return 1 - } - return -1 - case b == mysql.TypeBlob: - if a == mysql.TypeTinyBlob { - return -1 - } - return 1 - case a < b: - return -1 - default: - return 1 - } -} diff --git a/util/selection/BUILD.bazel b/util/selection/BUILD.bazel deleted file mode 100644 index eec28d34ec54c..0000000000000 --- a/util/selection/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "selection", - srcs = ["selection.go"], - importpath = "github.com/pingcap/tidb/util/selection", - visibility = ["//visibility:public"], -) - -go_test( - name = "selection_test", - timeout = "short", - srcs = [ - "main_test.go", - "selection_test.go", - ], - embed = [":selection"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/selection/main_test.go b/util/selection/main_test.go deleted file mode 100644 index d39f68d3171bc..0000000000000 --- a/util/selection/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package selection - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/sem/BUILD.bazel b/util/sem/BUILD.bazel deleted file mode 100644 index f1a6d5ae2716e..0000000000000 --- a/util/sem/BUILD.bazel +++ /dev/null @@ -1,31 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sem", - srcs = ["sem.go"], - importpath = "github.com/pingcap/tidb/util/sem", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//sessionctx/variable", - "//util/logutil", - ], -) - -go_test( - name = "sem_test", - timeout = "short", - srcs = [ - "main_test.go", - "sem_test.go", - ], - embed = [":sem"], - flaky = True, - deps = [ - "//parser/mysql", - "//sessionctx/variable", - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/sem/main_test.go b/util/sem/main_test.go deleted file mode 100644 index 51f622233ed16..0000000000000 --- a/util/sem/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package sem - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/servermemorylimit/BUILD.bazel b/util/servermemorylimit/BUILD.bazel deleted file mode 100644 index 4abe43930abc8..0000000000000 --- a/util/servermemorylimit/BUILD.bazel +++ /dev/null @@ -1,32 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "servermemorylimit", - srcs = ["servermemorylimit.go"], - importpath = "github.com/pingcap/tidb/util/servermemorylimit", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//types", - "//util", - "//util/logutil", - "//util/memory", - "@com_github_pingcap_failpoint//:failpoint", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "servermemorylimit_test", - timeout = "short", - srcs = ["servermemorylimit_test.go"], - embed = [":servermemorylimit"], - flaky = True, - race = "on", - deps = [ - "//types", - "//util", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/set/BUILD.bazel b/util/set/BUILD.bazel deleted file mode 100644 index 7c6201a6286fe..0000000000000 --- a/util/set/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "set", - srcs = [ - "float64_set.go", - "int_set.go", - "mem_aware_map.go", - "set_with_memory_usage.go", - "string_set.go", - ], - importpath = "github.com/pingcap/tidb/util/set", - visibility = ["//visibility:public"], - deps = [ - "//util/hack", - "//util/memory", - "@org_golang_x_exp//maps", - ], -) - -go_test( - name = "set_test", - timeout = "short", - srcs = [ - "float64_set_test.go", - "int_set_test.go", - "main_test.go", - "mem_aware_map_test.go", - "set_with_memory_usage_test.go", - "string_set_test.go", - ], - embed = [":set"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//assert", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/set/main_test.go b/util/set/main_test.go deleted file mode 100644 index 71bbcc5dacf6c..0000000000000 --- a/util/set/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package set - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/signal/BUILD.bazel b/util/signal/BUILD.bazel deleted file mode 100644 index 27860e5f7da2d..0000000000000 --- a/util/signal/BUILD.bazel +++ /dev/null @@ -1,63 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "signal", - srcs = [ - "signal_posix.go", - "signal_wasm.go", - "signal_windows.go", - ], - importpath = "github.com/pingcap/tidb/util/signal", - visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:aix": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:android": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:darwin": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:dragonfly": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:freebsd": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:illumos": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:ios": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:netbsd": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:openbsd": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:solaris": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "@io_bazel_rules_go//go/platform:windows": [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], - "//conditions:default": [], - }), -) diff --git a/util/size/BUILD.bazel b/util/size/BUILD.bazel deleted file mode 100644 index 6b316c1b95ad2..0000000000000 --- a/util/size/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "size", - srcs = ["size.go"], - importpath = "github.com/pingcap/tidb/util/size", - visibility = ["//visibility:public"], -) diff --git a/util/skip/BUILD.bazel b/util/skip/BUILD.bazel deleted file mode 100644 index e6b6df469e4fc..0000000000000 --- a/util/skip/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "skip", - srcs = ["skip.go"], - importpath = "github.com/pingcap/tidb/util/skip", - visibility = ["//visibility:public"], -) diff --git a/util/sli/BUILD.bazel b/util/sli/BUILD.bazel deleted file mode 100644 index 2469865a1a207..0000000000000 --- a/util/sli/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "sli", - srcs = ["sli.go"], - importpath = "github.com/pingcap/tidb/util/sli", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_pingcap_failpoint//:failpoint", - ], -) diff --git a/util/slice/BUILD.bazel b/util/slice/BUILD.bazel deleted file mode 100644 index 209661ebd4edf..0000000000000 --- a/util/slice/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "slice", - srcs = ["slice.go"], - importpath = "github.com/pingcap/tidb/util/slice", - visibility = ["//visibility:public"], -) - -go_test( - name = "slice_test", - timeout = "short", - srcs = [ - "main_test.go", - "slice_test.go", - ], - embed = [":slice"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/slice/main_test.go b/util/slice/main_test.go deleted file mode 100644 index f1203e68b8368..0000000000000 --- a/util/slice/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package slice - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/sqlexec/BUILD.bazel b/util/sqlexec/BUILD.bazel deleted file mode 100644 index 8a15382edc71e..0000000000000 --- a/util/sqlexec/BUILD.bazel +++ /dev/null @@ -1,39 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "sqlexec", - srcs = [ - "restricted_sql_executor.go", - "simple_record_set.go", - "utils.go", - ], - importpath = "github.com/pingcap/tidb/util/sqlexec", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//parser/ast", - "//sessionctx", - "//sessionctx/variable", - "//types", - "//util/chunk", - "//util/hack", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_kvproto//pkg/kvrpcpb", - ], -) - -go_test( - name = "sqlexec_test", - timeout = "short", - srcs = [ - "main_test.go", - "utils_test.go", - ], - embed = [":sqlexec"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/sqlexec/main_test.go b/util/sqlexec/main_test.go deleted file mode 100644 index 48381c898f1f1..0000000000000 --- a/util/sqlexec/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package sqlexec - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/sqlexec/mock/BUILD.bazel b/util/sqlexec/mock/BUILD.bazel deleted file mode 100644 index e9166a55e758f..0000000000000 --- a/util/sqlexec/mock/BUILD.bazel +++ /dev/null @@ -1,14 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mock", - srcs = ["restricted_sql_executor_mock.go"], - importpath = "github.com/pingcap/tidb/util/sqlexec/mock", - visibility = ["//visibility:public"], - deps = [ - "//parser/ast", - "//util/chunk", - "//util/sqlexec", - "@org_uber_go_mock//gomock", - ], -) diff --git a/util/stmtsummary/BUILD.bazel b/util/stmtsummary/BUILD.bazel deleted file mode 100644 index fba5901d3b0b7..0000000000000 --- a/util/stmtsummary/BUILD.bazel +++ /dev/null @@ -1,59 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "stmtsummary", - srcs = [ - "evicted.go", - "reader.go", - "statement_summary.go", - ], - importpath = "github.com/pingcap/tidb/util/stmtsummary", - visibility = ["//visibility:public"], - deps = [ - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//types", - "//util/chunk", - "//util/execdetails", - "//util/hack", - "//util/kvcache", - "//util/logutil", - "//util/plancodec", - "//util/set", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_tikv_client_go_v2//util", - "@org_golang_x_exp//maps", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "stmtsummary_test", - timeout = "short", - srcs = [ - "evicted_test.go", - "main_test.go", - "statement_summary_test.go", - ], - embed = [":stmtsummary"], - flaky = True, - shard_count = 24, - deps = [ - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//testkit/testsetup", - "//types", - "//util", - "//util/execdetails", - "//util/plancodec", - "@com_github_pingcap_log//:log", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/stmtsummary/main_test.go b/util/stmtsummary/main_test.go deleted file mode 100644 index a7176cf31aa07..0000000000000 --- a/util/stmtsummary/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/stmtsummary/reader.go b/util/stmtsummary/reader.go deleted file mode 100644 index e5e9a2db39705..0000000000000 --- a/util/stmtsummary/reader.go +++ /dev/null @@ -1,635 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "fmt" - "strings" - "time" - - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/plancodec" - "github.com/pingcap/tidb/util/set" - "go.uber.org/zap" -) - -// stmtSummaryReader uses to read the statement summaries data and convert to []datum row. -type stmtSummaryReader struct { - user *auth.UserIdentity - // If the user has the 'PROCESS' privilege, he can read all the statements. - hasProcessPriv bool - columns []*model.ColumnInfo - instanceAddr string - ssMap *stmtSummaryByDigestMap - columnValueFactories []columnValueFactory - checker *stmtSummaryChecker - tz *time.Location -} - -// NewStmtSummaryReader return a new statement summaries reader. -func NewStmtSummaryReader(user *auth.UserIdentity, hasProcessPriv bool, cols []*model.ColumnInfo, instanceAddr string, tz *time.Location) *stmtSummaryReader { - reader := &stmtSummaryReader{ - user: user, - hasProcessPriv: hasProcessPriv, - columns: cols, - instanceAddr: instanceAddr, - ssMap: StmtSummaryByDigestMap, - tz: tz, - } - // initialize column value factories. - reader.columnValueFactories = make([]columnValueFactory, len(reader.columns)) - for i, col := range reader.columns { - factory, ok := columnValueFactoryMap[col.Name.O] - if !ok { - panic(fmt.Sprintf("should never happen, should register new column %v into columnValueFactoryMap", col.Name.O)) - } - reader.columnValueFactories[i] = factory - } - return reader -} - -// GetStmtSummaryCurrentRows gets all current statement summaries rows. -func (ssr *stmtSummaryReader) GetStmtSummaryCurrentRows() [][]types.Datum { - ssMap := ssr.ssMap - ssMap.Lock() - values := ssMap.summaryMap.Values() - beginTime := ssMap.beginTimeForCurInterval - other := ssMap.other - ssMap.Unlock() - - rows := make([][]types.Datum, 0, len(values)) - for _, value := range values { - ssbd := value.(*stmtSummaryByDigest) - if ssr.checker != nil && !ssr.checker.isDigestValid(ssbd.digest) { - continue - } - record := ssr.getStmtByDigestRow(ssbd, beginTime) - if record != nil { - rows = append(rows, record) - } - } - if ssr.checker == nil { - if otherDatum := ssr.getStmtEvictedOtherRow(other); otherDatum != nil { - rows = append(rows, otherDatum) - } - } - return rows -} - -// GetStmtSummaryHistoryRows gets all history statement summaries rows. -func (ssr *stmtSummaryReader) GetStmtSummaryHistoryRows() [][]types.Datum { - ssMap := ssr.ssMap - ssMap.Lock() - values := ssMap.summaryMap.Values() - other := ssMap.other - ssMap.Unlock() - - historySize := ssMap.historySize() - rows := make([][]types.Datum, 0, len(values)*historySize) - for _, value := range values { - records := ssr.getStmtByDigestHistoryRow(value.(*stmtSummaryByDigest), historySize) - rows = append(rows, records...) - } - - if ssr.checker == nil { - otherDatum := ssr.getStmtEvictedOtherHistoryRow(other, historySize) - rows = append(rows, otherDatum...) - } - return rows -} - -func (ssr *stmtSummaryReader) SetChecker(checker *stmtSummaryChecker) { - ssr.checker = checker -} - -func (ssr *stmtSummaryReader) getStmtByDigestRow(ssbd *stmtSummaryByDigest, beginTimeForCurInterval int64) []types.Datum { - var ssElement *stmtSummaryByDigestElement - - ssbd.Lock() - if ssbd.initialized && ssbd.history.Len() > 0 { - ssElement = ssbd.history.Back().Value.(*stmtSummaryByDigestElement) - } - ssbd.Unlock() - - // `ssElement` is lazy expired, so expired elements could also be read. - // `beginTime` won't change since `ssElement` is created, so locking is not needed here. - if ssElement == nil || ssElement.beginTime < beginTimeForCurInterval { - return nil - } - return ssr.getStmtByDigestElementRow(ssElement, ssbd) -} - -func (ssr *stmtSummaryReader) getStmtByDigestElementRow(ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) []types.Datum { - ssElement.Lock() - defer ssElement.Unlock() - isAuthed := true - if ssr.user != nil && !ssr.hasProcessPriv { - _, isAuthed = ssElement.authUsers[ssr.user.Username] - } - if !isAuthed { - return nil - } - - datums := make([]types.Datum, len(ssr.columnValueFactories)) - for i, factory := range ssr.columnValueFactories { - datums[i] = types.NewDatum(factory(ssr, ssElement, ssbd)) - } - return datums -} - -func (ssr *stmtSummaryReader) getStmtByDigestHistoryRow(ssbd *stmtSummaryByDigest, historySize int) [][]types.Datum { - // Collect all history summaries to an array. - ssElements := ssbd.collectHistorySummaries(ssr.checker, historySize) - - rows := make([][]types.Datum, 0, len(ssElements)) - for _, ssElement := range ssElements { - record := ssr.getStmtByDigestElementRow(ssElement, ssbd) - if record != nil { - rows = append(rows, record) - } - } - return rows -} - -func (ssr *stmtSummaryReader) getStmtEvictedOtherRow(ssbde *stmtSummaryByDigestEvicted) []types.Datum { - var seElement *stmtSummaryByDigestEvictedElement - - ssbde.Lock() - if ssbde.history.Len() > 0 { - seElement = ssbde.history.Back().Value.(*stmtSummaryByDigestEvictedElement) - } - ssbde.Unlock() - - if seElement == nil { - return nil - } - - return ssr.getStmtByDigestElementRow(seElement.otherSummary, new(stmtSummaryByDigest)) -} - -func (ssr *stmtSummaryReader) getStmtEvictedOtherHistoryRow(ssbde *stmtSummaryByDigestEvicted, historySize int) [][]types.Datum { - // Collect all history summaries to an array. - ssbde.Lock() - seElements := ssbde.collectHistorySummaries(historySize) - ssbde.Unlock() - rows := make([][]types.Datum, 0, len(seElements)) - - ssbd := new(stmtSummaryByDigest) - for _, seElement := range seElements { - record := ssr.getStmtByDigestElementRow(seElement.otherSummary, ssbd) - if record != nil { - rows = append(rows, record) - } - } - return rows -} - -type stmtSummaryChecker struct { - digests set.StringSet -} - -// NewStmtSummaryChecker return a new statement summaries checker. -func NewStmtSummaryChecker(digests set.StringSet) *stmtSummaryChecker { - return &stmtSummaryChecker{ - digests: digests, - } -} - -func (ssc *stmtSummaryChecker) isDigestValid(digest string) bool { - return ssc.digests.Exist(digest) -} - -// Statements summary table column name. -const ( - ClusterTableInstanceColumnNameStr = "INSTANCE" - SummaryBeginTimeStr = "SUMMARY_BEGIN_TIME" - SummaryEndTimeStr = "SUMMARY_END_TIME" - StmtTypeStr = "STMT_TYPE" - SchemaNameStr = "SCHEMA_NAME" - DigestStr = "DIGEST" - DigestTextStr = "DIGEST_TEXT" - TableNamesStr = "TABLE_NAMES" - IndexNamesStr = "INDEX_NAMES" - SampleUserStr = "SAMPLE_USER" - ExecCountStr = "EXEC_COUNT" - SumErrorsStr = "SUM_ERRORS" - SumWarningsStr = "SUM_WARNINGS" - SumLatencyStr = "SUM_LATENCY" - MaxLatencyStr = "MAX_LATENCY" - MinLatencyStr = "MIN_LATENCY" - AvgLatencyStr = "AVG_LATENCY" - AvgParseLatencyStr = "AVG_PARSE_LATENCY" - MaxParseLatencyStr = "MAX_PARSE_LATENCY" - AvgCompileLatencyStr = "AVG_COMPILE_LATENCY" - MaxCompileLatencyStr = "MAX_COMPILE_LATENCY" - SumCopTaskNumStr = "SUM_COP_TASK_NUM" - MaxCopProcessTimeStr = "MAX_COP_PROCESS_TIME" - MaxCopProcessAddressStr = "MAX_COP_PROCESS_ADDRESS" - MaxCopWaitTimeStr = "MAX_COP_WAIT_TIME" // #nosec G101 - MaxCopWaitAddressStr = "MAX_COP_WAIT_ADDRESS" // #nosec G101 - AvgProcessTimeStr = "AVG_PROCESS_TIME" - MaxProcessTimeStr = "MAX_PROCESS_TIME" - AvgWaitTimeStr = "AVG_WAIT_TIME" - MaxWaitTimeStr = "MAX_WAIT_TIME" - AvgBackoffTimeStr = "AVG_BACKOFF_TIME" - MaxBackoffTimeStr = "MAX_BACKOFF_TIME" - AvgTotalKeysStr = "AVG_TOTAL_KEYS" - MaxTotalKeysStr = "MAX_TOTAL_KEYS" - AvgProcessedKeysStr = "AVG_PROCESSED_KEYS" - MaxProcessedKeysStr = "MAX_PROCESSED_KEYS" - AvgRocksdbDeleteSkippedCountStr = "AVG_ROCKSDB_DELETE_SKIPPED_COUNT" - MaxRocksdbDeleteSkippedCountStr = "MAX_ROCKSDB_DELETE_SKIPPED_COUNT" - AvgRocksdbKeySkippedCountStr = "AVG_ROCKSDB_KEY_SKIPPED_COUNT" - MaxRocksdbKeySkippedCountStr = "MAX_ROCKSDB_KEY_SKIPPED_COUNT" - AvgRocksdbBlockCacheHitCountStr = "AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT" - MaxRocksdbBlockCacheHitCountStr = "MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT" - AvgRocksdbBlockReadCountStr = "AVG_ROCKSDB_BLOCK_READ_COUNT" - MaxRocksdbBlockReadCountStr = "MAX_ROCKSDB_BLOCK_READ_COUNT" - AvgRocksdbBlockReadByteStr = "AVG_ROCKSDB_BLOCK_READ_BYTE" - MaxRocksdbBlockReadByteStr = "MAX_ROCKSDB_BLOCK_READ_BYTE" - AvgPrewriteTimeStr = "AVG_PREWRITE_TIME" - MaxPrewriteTimeStr = "MAX_PREWRITE_TIME" - AvgCommitTimeStr = "AVG_COMMIT_TIME" - MaxCommitTimeStr = "MAX_COMMIT_TIME" - AvgGetCommitTsTimeStr = "AVG_GET_COMMIT_TS_TIME" - MaxGetCommitTsTimeStr = "MAX_GET_COMMIT_TS_TIME" - AvgCommitBackoffTimeStr = "AVG_COMMIT_BACKOFF_TIME" - MaxCommitBackoffTimeStr = "MAX_COMMIT_BACKOFF_TIME" - AvgResolveLockTimeStr = "AVG_RESOLVE_LOCK_TIME" - MaxResolveLockTimeStr = "MAX_RESOLVE_LOCK_TIME" - AvgLocalLatchWaitTimeStr = "AVG_LOCAL_LATCH_WAIT_TIME" - MaxLocalLatchWaitTimeStr = "MAX_LOCAL_LATCH_WAIT_TIME" - AvgWriteKeysStr = "AVG_WRITE_KEYS" - MaxWriteKeysStr = "MAX_WRITE_KEYS" - AvgWriteSizeStr = "AVG_WRITE_SIZE" - MaxWriteSizeStr = "MAX_WRITE_SIZE" - AvgPrewriteRegionsStr = "AVG_PREWRITE_REGIONS" - MaxPrewriteRegionsStr = "MAX_PREWRITE_REGIONS" - AvgTxnRetryStr = "AVG_TXN_RETRY" - MaxTxnRetryStr = "MAX_TXN_RETRY" - SumExecRetryStr = "SUM_EXEC_RETRY" - SumExecRetryTimeStr = "SUM_EXEC_RETRY_TIME" - SumBackoffTimesStr = "SUM_BACKOFF_TIMES" - BackoffTypesStr = "BACKOFF_TYPES" - AvgMemStr = "AVG_MEM" - MaxMemStr = "MAX_MEM" - AvgDiskStr = "AVG_DISK" - MaxDiskStr = "MAX_DISK" - AvgKvTimeStr = "AVG_KV_TIME" - AvgPdTimeStr = "AVG_PD_TIME" - AvgBackoffTotalTimeStr = "AVG_BACKOFF_TOTAL_TIME" - AvgWriteSQLRespTimeStr = "AVG_WRITE_SQL_RESP_TIME" - MaxResultRowsStr = "MAX_RESULT_ROWS" - MinResultRowsStr = "MIN_RESULT_ROWS" - AvgResultRowsStr = "AVG_RESULT_ROWS" - PreparedStr = "PREPARED" - AvgAffectedRowsStr = "AVG_AFFECTED_ROWS" - FirstSeenStr = "FIRST_SEEN" - LastSeenStr = "LAST_SEEN" - PlanInCacheStr = "PLAN_IN_CACHE" - PlanCacheHitsStr = "PLAN_CACHE_HITS" - PlanInBindingStr = "PLAN_IN_BINDING" - QuerySampleTextStr = "QUERY_SAMPLE_TEXT" - PrevSampleTextStr = "PREV_SAMPLE_TEXT" - PlanDigestStr = "PLAN_DIGEST" - PlanStr = "PLAN" - BinaryPlan = "BINARY_PLAN" - Charset = "CHARSET" - Collation = "COLLATION" - PlanHint = "PLAN_HINT" -) - -type columnValueFactory func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} - -var columnValueFactoryMap = map[string]columnValueFactory{ - ClusterTableInstanceColumnNameStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return reader.instanceAddr - }, - SummaryBeginTimeStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - beginTime := time.Unix(ssElement.beginTime, 0) - if beginTime.Location() != reader.tz { - beginTime = beginTime.In(reader.tz) - } - return types.NewTime(types.FromGoTime(beginTime), mysql.TypeTimestamp, 0) - }, - SummaryEndTimeStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - endTime := time.Unix(ssElement.endTime, 0) - if endTime.Location() != reader.tz { - endTime = endTime.In(reader.tz) - } - return types.NewTime(types.FromGoTime(endTime), mysql.TypeTimestamp, 0) - }, - StmtTypeStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return ssbd.stmtType - }, - SchemaNameStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(ssbd.schemaName) - }, - DigestStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(ssbd.digest) - }, - DigestTextStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return ssbd.normalizedSQL - }, - TableNamesStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(ssbd.tableNames) - }, - IndexNamesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(strings.Join(ssElement.indexNames, ",")) - }, - SampleUserStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - sampleUser := "" - for key := range ssElement.authUsers { - sampleUser = key - break - } - return convertEmptyToNil(sampleUser) - }, - ExecCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.execCount - }, - SumErrorsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sumErrors - }, - SumWarningsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sumWarnings - }, - SumLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.sumLatency) - }, - MaxLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxLatency) - }, - MinLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.minLatency) - }, - AvgLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumLatency), ssElement.execCount) - }, - AvgParseLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumParseLatency), ssElement.execCount) - }, - MaxParseLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxParseLatency) - }, - AvgCompileLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumCompileLatency), ssElement.execCount) - }, - MaxCompileLatencyStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxCompileLatency) - }, - SumCopTaskNumStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sumNumCopTasks - }, - MaxCopProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxCopProcessTime) - }, - MaxCopProcessAddressStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(ssElement.maxCopProcessAddress) - }, - MaxCopWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxCopWaitTime) - }, - MaxCopWaitAddressStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return convertEmptyToNil(ssElement.maxCopWaitAddress) - }, - AvgProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumProcessTime), ssElement.execCount) - }, - MaxProcessTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxProcessTime) - }, - AvgWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumWaitTime), ssElement.execCount) - }, - MaxWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxWaitTime) - }, - AvgBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumBackoffTime), ssElement.execCount) - }, - MaxBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxBackoffTime) - }, - AvgTotalKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumTotalKeys, ssElement.execCount) - }, - MaxTotalKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxTotalKeys - }, - AvgProcessedKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumProcessedKeys, ssElement.execCount) - }, - MaxProcessedKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxProcessedKeys - }, - AvgRocksdbDeleteSkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumRocksdbDeleteSkippedCount), ssElement.execCount) - }, - MaxRocksdbDeleteSkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxRocksdbDeleteSkippedCount - }, - AvgRocksdbKeySkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumRocksdbKeySkippedCount), ssElement.execCount) - }, - MaxRocksdbKeySkippedCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxRocksdbKeySkippedCount - }, - AvgRocksdbBlockCacheHitCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumRocksdbBlockCacheHitCount), ssElement.execCount) - }, - MaxRocksdbBlockCacheHitCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxRocksdbBlockCacheHitCount - }, - AvgRocksdbBlockReadCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumRocksdbBlockReadCount), ssElement.execCount) - }, - MaxRocksdbBlockReadCountStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxRocksdbBlockReadCount - }, - AvgRocksdbBlockReadByteStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumRocksdbBlockReadByte), ssElement.execCount) - }, - MaxRocksdbBlockReadByteStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxRocksdbBlockReadByte - }, - AvgPrewriteTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumPrewriteTime), ssElement.commitCount) - }, - MaxPrewriteTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxPrewriteTime) - }, - AvgCommitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumCommitTime), ssElement.commitCount) - }, - MaxCommitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxCommitTime) - }, - AvgGetCommitTsTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumGetCommitTsTime), ssElement.commitCount) - }, - MaxGetCommitTsTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxGetCommitTsTime) - }, - AvgCommitBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumCommitBackoffTime, ssElement.commitCount) - }, - MaxCommitBackoffTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxCommitBackoffTime - }, - AvgResolveLockTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumResolveLockTime, ssElement.commitCount) - }, - MaxResolveLockTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxResolveLockTime - }, - AvgLocalLatchWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumLocalLatchTime), ssElement.commitCount) - }, - MaxLocalLatchWaitTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.maxLocalLatchTime) - }, - AvgWriteKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgFloat(ssElement.sumWriteKeys, ssElement.commitCount) - }, - MaxWriteKeysStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxWriteKeys - }, - AvgWriteSizeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgFloat(ssElement.sumWriteSize, ssElement.commitCount) - }, - MaxWriteSizeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxWriteSize - }, - AvgPrewriteRegionsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgFloat(ssElement.sumPrewriteRegionNum, ssElement.commitCount) - }, - MaxPrewriteRegionsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int(ssElement.maxPrewriteRegionNum) - }, - AvgTxnRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgFloat(ssElement.sumTxnRetry, ssElement.commitCount) - }, - MaxTxnRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxTxnRetry - }, - SumExecRetryStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int(ssElement.execRetryCount) - }, - SumExecRetryTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return int64(ssElement.execRetryTime) - }, - SumBackoffTimesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sumBackoffTimes - }, - BackoffTypesStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return formatBackoffTypes(ssElement.backoffTypes) - }, - AvgMemStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumMem, ssElement.execCount) - }, - MaxMemStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxMem - }, - AvgDiskStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumDisk, ssElement.execCount) - }, - MaxDiskStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxDisk - }, - AvgKvTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumKVTotal), ssElement.commitCount) - }, - AvgPdTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumPDTotal), ssElement.commitCount) - }, - AvgBackoffTotalTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumBackoffTotal), ssElement.commitCount) - }, - AvgWriteSQLRespTimeStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(int64(ssElement.sumWriteSQLRespTotal), ssElement.commitCount) - }, - MaxResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.maxResultRows - }, - MinResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.minResultRows - }, - AvgResultRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgInt(ssElement.sumResultRows, ssElement.execCount) - }, - PreparedStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.prepared - }, - AvgAffectedRowsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return avgFloat(int64(ssElement.sumAffectedRows), ssElement.execCount) - }, - FirstSeenStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - firstSeen := ssElement.firstSeen - if firstSeen.Location() != reader.tz { - firstSeen = firstSeen.In(reader.tz) - } - return types.NewTime(types.FromGoTime(firstSeen), mysql.TypeTimestamp, 0) - }, - LastSeenStr: func(reader *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - lastSeen := ssElement.lastSeen - if lastSeen.Location() != reader.tz { - lastSeen = lastSeen.In(reader.tz) - } - return types.NewTime(types.FromGoTime(lastSeen), mysql.TypeTimestamp, 0) - }, - PlanInCacheStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.planInCache - }, - PlanCacheHitsStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.planCacheHits - }, - PlanInBindingStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.planInBinding - }, - QuerySampleTextStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sampleSQL - }, - PrevSampleTextStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.prevSQL - }, - PlanDigestStr: func(_ *stmtSummaryReader, _ *stmtSummaryByDigestElement, ssbd *stmtSummaryByDigest) interface{} { - return ssbd.planDigest - }, - PlanStr: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - plan, err := plancodec.DecodePlan(ssElement.samplePlan) - if err != nil { - logutil.BgLogger().Error("decode plan in statement summary failed", zap.String("plan", ssElement.samplePlan), zap.String("query", ssElement.sampleSQL), zap.Error(err)) - plan = "" - } - return plan - }, - BinaryPlan: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.sampleBinaryPlan - }, - Charset: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.charset - }, - Collation: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.collation - }, - PlanHint: func(_ *stmtSummaryReader, ssElement *stmtSummaryByDigestElement, _ *stmtSummaryByDigest) interface{} { - return ssElement.planHint - }, -} diff --git a/util/stmtsummary/v2/BUILD.bazel b/util/stmtsummary/v2/BUILD.bazel deleted file mode 100644 index 07ce88de4819e..0000000000000 --- a/util/stmtsummary/v2/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "stmtsummary", - srcs = [ - "column.go", - "logger.go", - "reader.go", - "record.go", - "stmtsummary.go", - ], - importpath = "github.com/pingcap/tidb/util/stmtsummary/v2", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//parser/auth", - "//parser/model", - "//parser/mysql", - "//sessionctx/stmtctx", - "//types", - "//util", - "//util/execdetails", - "//util/hack", - "//util/kvcache", - "//util/logutil", - "//util/plancodec", - "//util/set", - "//util/stmtsummary", - "@com_github_pingcap_log//:log", - "@com_github_tikv_client_go_v2//util", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - "@org_uber_go_zap//buffer", - "@org_uber_go_zap//zapcore", - ], -) - -go_test( - name = "stmtsummary_test", - timeout = "short", - srcs = [ - "column_test.go", - "main_test.go", - "reader_test.go", - "record_test.go", - "stmtsummary_test.go", - ], - embed = [":stmtsummary"], - flaky = True, - shard_count = 13, - deps = [ - "//parser/auth", - "//parser/model", - "//testkit/testsetup", - "//types", - "//util", - "//util/set", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/stmtsummary/v2/column.go b/util/stmtsummary/v2/column.go deleted file mode 100644 index d8baeebb26c92..0000000000000 --- a/util/stmtsummary/v2/column.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "bytes" - "cmp" - "fmt" - "slices" - "strings" - "time" - - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/plancodec" - "go.uber.org/zap" -) - -// Statements summary table column name. -const ( - ClusterTableInstanceColumnNameStr = "INSTANCE" - SummaryBeginTimeStr = "SUMMARY_BEGIN_TIME" - SummaryEndTimeStr = "SUMMARY_END_TIME" - StmtTypeStr = "STMT_TYPE" - SchemaNameStr = "SCHEMA_NAME" - DigestStr = "DIGEST" - DigestTextStr = "DIGEST_TEXT" - TableNamesStr = "TABLE_NAMES" - IndexNamesStr = "INDEX_NAMES" - SampleUserStr = "SAMPLE_USER" - ExecCountStr = "EXEC_COUNT" - SumErrorsStr = "SUM_ERRORS" - SumWarningsStr = "SUM_WARNINGS" - SumLatencyStr = "SUM_LATENCY" - MaxLatencyStr = "MAX_LATENCY" - MinLatencyStr = "MIN_LATENCY" - AvgLatencyStr = "AVG_LATENCY" - AvgParseLatencyStr = "AVG_PARSE_LATENCY" - MaxParseLatencyStr = "MAX_PARSE_LATENCY" - AvgCompileLatencyStr = "AVG_COMPILE_LATENCY" - MaxCompileLatencyStr = "MAX_COMPILE_LATENCY" - SumCopTaskNumStr = "SUM_COP_TASK_NUM" - MaxCopProcessTimeStr = "MAX_COP_PROCESS_TIME" - MaxCopProcessAddressStr = "MAX_COP_PROCESS_ADDRESS" - MaxCopWaitTimeStr = "MAX_COP_WAIT_TIME" // #nosec G101 - MaxCopWaitAddressStr = "MAX_COP_WAIT_ADDRESS" // #nosec G101 - AvgProcessTimeStr = "AVG_PROCESS_TIME" - MaxProcessTimeStr = "MAX_PROCESS_TIME" - AvgWaitTimeStr = "AVG_WAIT_TIME" - MaxWaitTimeStr = "MAX_WAIT_TIME" - AvgBackoffTimeStr = "AVG_BACKOFF_TIME" - MaxBackoffTimeStr = "MAX_BACKOFF_TIME" - AvgTotalKeysStr = "AVG_TOTAL_KEYS" - MaxTotalKeysStr = "MAX_TOTAL_KEYS" - AvgProcessedKeysStr = "AVG_PROCESSED_KEYS" - MaxProcessedKeysStr = "MAX_PROCESSED_KEYS" - AvgRocksdbDeleteSkippedCountStr = "AVG_ROCKSDB_DELETE_SKIPPED_COUNT" - MaxRocksdbDeleteSkippedCountStr = "MAX_ROCKSDB_DELETE_SKIPPED_COUNT" - AvgRocksdbKeySkippedCountStr = "AVG_ROCKSDB_KEY_SKIPPED_COUNT" - MaxRocksdbKeySkippedCountStr = "MAX_ROCKSDB_KEY_SKIPPED_COUNT" - AvgRocksdbBlockCacheHitCountStr = "AVG_ROCKSDB_BLOCK_CACHE_HIT_COUNT" - MaxRocksdbBlockCacheHitCountStr = "MAX_ROCKSDB_BLOCK_CACHE_HIT_COUNT" - AvgRocksdbBlockReadCountStr = "AVG_ROCKSDB_BLOCK_READ_COUNT" - MaxRocksdbBlockReadCountStr = "MAX_ROCKSDB_BLOCK_READ_COUNT" - AvgRocksdbBlockReadByteStr = "AVG_ROCKSDB_BLOCK_READ_BYTE" - MaxRocksdbBlockReadByteStr = "MAX_ROCKSDB_BLOCK_READ_BYTE" - AvgPrewriteTimeStr = "AVG_PREWRITE_TIME" - MaxPrewriteTimeStr = "MAX_PREWRITE_TIME" - AvgCommitTimeStr = "AVG_COMMIT_TIME" - MaxCommitTimeStr = "MAX_COMMIT_TIME" - AvgGetCommitTsTimeStr = "AVG_GET_COMMIT_TS_TIME" - MaxGetCommitTsTimeStr = "MAX_GET_COMMIT_TS_TIME" - AvgCommitBackoffTimeStr = "AVG_COMMIT_BACKOFF_TIME" - MaxCommitBackoffTimeStr = "MAX_COMMIT_BACKOFF_TIME" - AvgResolveLockTimeStr = "AVG_RESOLVE_LOCK_TIME" - MaxResolveLockTimeStr = "MAX_RESOLVE_LOCK_TIME" - AvgLocalLatchWaitTimeStr = "AVG_LOCAL_LATCH_WAIT_TIME" - MaxLocalLatchWaitTimeStr = "MAX_LOCAL_LATCH_WAIT_TIME" - AvgWriteKeysStr = "AVG_WRITE_KEYS" - MaxWriteKeysStr = "MAX_WRITE_KEYS" - AvgWriteSizeStr = "AVG_WRITE_SIZE" - MaxWriteSizeStr = "MAX_WRITE_SIZE" - AvgPrewriteRegionsStr = "AVG_PREWRITE_REGIONS" - MaxPrewriteRegionsStr = "MAX_PREWRITE_REGIONS" - AvgTxnRetryStr = "AVG_TXN_RETRY" - MaxTxnRetryStr = "MAX_TXN_RETRY" - SumExecRetryStr = "SUM_EXEC_RETRY" - SumExecRetryTimeStr = "SUM_EXEC_RETRY_TIME" - SumBackoffTimesStr = "SUM_BACKOFF_TIMES" - BackoffTypesStr = "BACKOFF_TYPES" - AvgMemStr = "AVG_MEM" - MaxMemStr = "MAX_MEM" - AvgDiskStr = "AVG_DISK" - MaxDiskStr = "MAX_DISK" - AvgKvTimeStr = "AVG_KV_TIME" - AvgPdTimeStr = "AVG_PD_TIME" - AvgBackoffTotalTimeStr = "AVG_BACKOFF_TOTAL_TIME" - AvgWriteSQLRespTimeStr = "AVG_WRITE_SQL_RESP_TIME" - MaxResultRowsStr = "MAX_RESULT_ROWS" - MinResultRowsStr = "MIN_RESULT_ROWS" - AvgResultRowsStr = "AVG_RESULT_ROWS" - PreparedStr = "PREPARED" - AvgAffectedRowsStr = "AVG_AFFECTED_ROWS" - FirstSeenStr = "FIRST_SEEN" - LastSeenStr = "LAST_SEEN" - PlanInCacheStr = "PLAN_IN_CACHE" - PlanCacheHitsStr = "PLAN_CACHE_HITS" - PlanInBindingStr = "PLAN_IN_BINDING" - QuerySampleTextStr = "QUERY_SAMPLE_TEXT" - PrevSampleTextStr = "PREV_SAMPLE_TEXT" - PlanDigestStr = "PLAN_DIGEST" - PlanStr = "PLAN" - BinaryPlan = "BINARY_PLAN" - Charset = "CHARSET" - Collation = "COLLATION" - PlanHint = "PLAN_HINT" -) - -type columnInfo interface { - getInstanceAddr() string - getTimeLocation() *time.Location -} - -type columnFactory func(info columnInfo, record *StmtRecord) interface{} - -var columnFactoryMap = map[string]columnFactory{ - ClusterTableInstanceColumnNameStr: func(info columnInfo, record *StmtRecord) interface{} { - return info.getInstanceAddr() - }, - SummaryBeginTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - beginTime := time.Unix(record.Begin, 0) - if beginTime.Location() != info.getTimeLocation() { - beginTime = beginTime.In(info.getTimeLocation()) - } - return types.NewTime(types.FromGoTime(beginTime), mysql.TypeTimestamp, 0) - }, - SummaryEndTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - endTime := time.Unix(record.End, 0) - if endTime.Location() != info.getTimeLocation() { - endTime = endTime.In(info.getTimeLocation()) - } - return types.NewTime(types.FromGoTime(endTime), mysql.TypeTimestamp, 0) - }, - StmtTypeStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.StmtType - }, - SchemaNameStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(record.SchemaName) - }, - DigestStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(record.Digest) - }, - DigestTextStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.NormalizedSQL - }, - TableNamesStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(record.TableNames) - }, - IndexNamesStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(strings.Join(record.IndexNames, ",")) - }, - SampleUserStr: func(info columnInfo, record *StmtRecord) interface{} { - sampleUser := "" - for key := range record.AuthUsers { - sampleUser = key - break - } - return convertEmptyToNil(sampleUser) - }, - ExecCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.ExecCount - }, - SumErrorsStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.SumErrors - }, - SumWarningsStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.SumWarnings - }, - SumLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.SumLatency) - }, - MaxLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxLatency) - }, - MinLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MinLatency) - }, - AvgLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumLatency), record.ExecCount) - }, - AvgParseLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumParseLatency), record.ExecCount) - }, - MaxParseLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxParseLatency) - }, - AvgCompileLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumCompileLatency), record.ExecCount) - }, - MaxCompileLatencyStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxCompileLatency) - }, - SumCopTaskNumStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.SumNumCopTasks - }, - MaxCopProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxCopProcessTime) - }, - MaxCopProcessAddressStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(record.MaxCopProcessAddress) - }, - MaxCopWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxCopWaitTime) - }, - MaxCopWaitAddressStr: func(info columnInfo, record *StmtRecord) interface{} { - return convertEmptyToNil(record.MaxCopWaitAddress) - }, - AvgProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumProcessTime), record.ExecCount) - }, - MaxProcessTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxProcessTime) - }, - AvgWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumWaitTime), record.ExecCount) - }, - MaxWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxWaitTime) - }, - AvgBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumBackoffTime), record.ExecCount) - }, - MaxBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxBackoffTime) - }, - AvgTotalKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumTotalKeys, record.ExecCount) - }, - MaxTotalKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxTotalKeys - }, - AvgProcessedKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumProcessedKeys, record.ExecCount) - }, - MaxProcessedKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxProcessedKeys - }, - AvgRocksdbDeleteSkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumRocksdbDeleteSkippedCount), record.ExecCount) - }, - MaxRocksdbDeleteSkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxRocksdbDeleteSkippedCount - }, - AvgRocksdbKeySkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumRocksdbKeySkippedCount), record.ExecCount) - }, - MaxRocksdbKeySkippedCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxRocksdbKeySkippedCount - }, - AvgRocksdbBlockCacheHitCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumRocksdbBlockCacheHitCount), record.ExecCount) - }, - MaxRocksdbBlockCacheHitCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxRocksdbBlockCacheHitCount - }, - AvgRocksdbBlockReadCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumRocksdbBlockReadCount), record.ExecCount) - }, - MaxRocksdbBlockReadCountStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxRocksdbBlockReadCount - }, - AvgRocksdbBlockReadByteStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumRocksdbBlockReadByte), record.ExecCount) - }, - MaxRocksdbBlockReadByteStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxRocksdbBlockReadByte - }, - AvgPrewriteTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumPrewriteTime), record.CommitCount) - }, - MaxPrewriteTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxPrewriteTime) - }, - AvgCommitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumCommitTime), record.CommitCount) - }, - MaxCommitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxCommitTime) - }, - AvgGetCommitTsTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumGetCommitTsTime), record.CommitCount) - }, - MaxGetCommitTsTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxGetCommitTsTime) - }, - AvgCommitBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumCommitBackoffTime, record.CommitCount) - }, - MaxCommitBackoffTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxCommitBackoffTime - }, - AvgResolveLockTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumResolveLockTime, record.CommitCount) - }, - MaxResolveLockTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxResolveLockTime - }, - AvgLocalLatchWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumLocalLatchTime), record.CommitCount) - }, - MaxLocalLatchWaitTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.MaxLocalLatchTime) - }, - AvgWriteKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgFloat(record.SumWriteKeys, record.CommitCount) - }, - MaxWriteKeysStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxWriteKeys - }, - AvgWriteSizeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgFloat(record.SumWriteSize, record.CommitCount) - }, - MaxWriteSizeStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxWriteSize - }, - AvgPrewriteRegionsStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgFloat(record.SumPrewriteRegionNum, record.CommitCount) - }, - MaxPrewriteRegionsStr: func(info columnInfo, record *StmtRecord) interface{} { - return int(record.MaxPrewriteRegionNum) - }, - AvgTxnRetryStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgFloat(record.SumTxnRetry, record.CommitCount) - }, - MaxTxnRetryStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxTxnRetry - }, - SumExecRetryStr: func(info columnInfo, record *StmtRecord) interface{} { - return int(record.ExecRetryCount) - }, - SumExecRetryTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return int64(record.ExecRetryTime) - }, - SumBackoffTimesStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.SumBackoffTimes - }, - BackoffTypesStr: func(info columnInfo, record *StmtRecord) interface{} { - return formatBackoffTypes(record.BackoffTypes) - }, - AvgMemStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumMem, record.ExecCount) - }, - MaxMemStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxMem - }, - AvgDiskStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumDisk, record.ExecCount) - }, - MaxDiskStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxDisk - }, - AvgKvTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumKVTotal), record.CommitCount) - }, - AvgPdTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumPDTotal), record.CommitCount) - }, - AvgBackoffTotalTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumBackoffTotal), record.CommitCount) - }, - AvgWriteSQLRespTimeStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(int64(record.SumWriteSQLRespTotal), record.CommitCount) - }, - MaxResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MaxResultRows - }, - MinResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.MinResultRows - }, - AvgResultRowsStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgInt(record.SumResultRows, record.ExecCount) - }, - PreparedStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.Prepared - }, - AvgAffectedRowsStr: func(info columnInfo, record *StmtRecord) interface{} { - return avgFloat(int64(record.SumAffectedRows), record.ExecCount) - }, - FirstSeenStr: func(info columnInfo, record *StmtRecord) interface{} { - firstSeen := record.FirstSeen - if firstSeen.Location() != info.getTimeLocation() { - firstSeen = firstSeen.In(info.getTimeLocation()) - } - return types.NewTime(types.FromGoTime(firstSeen), mysql.TypeTimestamp, 0) - }, - LastSeenStr: func(info columnInfo, record *StmtRecord) interface{} { - lastSeen := record.LastSeen - if lastSeen.Location() != info.getTimeLocation() { - lastSeen = lastSeen.In(info.getTimeLocation()) - } - return types.NewTime(types.FromGoTime(lastSeen), mysql.TypeTimestamp, 0) - }, - PlanInCacheStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.PlanInCache - }, - PlanCacheHitsStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.PlanCacheHits - }, - PlanInBindingStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.PlanInBinding - }, - QuerySampleTextStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.SampleSQL - }, - PrevSampleTextStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.PrevSQL - }, - PlanDigestStr: func(info columnInfo, record *StmtRecord) interface{} { - return record.PlanDigest - }, - PlanStr: func(info columnInfo, record *StmtRecord) interface{} { - plan, err := plancodec.DecodePlan(record.SamplePlan) - if err != nil { - logutil.BgLogger().Error("decode plan in statement summary failed", - zap.String("plan", record.SamplePlan), - zap.String("query", record.SampleSQL), zap.Error(err)) - plan = "" - } - return plan - }, - BinaryPlan: func(info columnInfo, record *StmtRecord) interface{} { - return record.SampleBinaryPlan - }, - Charset: func(info columnInfo, record *StmtRecord) interface{} { - return record.Charset - }, - Collation: func(info columnInfo, record *StmtRecord) interface{} { - return record.Collation - }, - PlanHint: func(info columnInfo, record *StmtRecord) interface{} { - return record.PlanHint - }, -} - -func makeColumnFactories(columns []*model.ColumnInfo) []columnFactory { - columnFactories := make([]columnFactory, len(columns)) - for i, col := range columns { - factory, ok := columnFactoryMap[col.Name.O] - if !ok { - panic(fmt.Sprintf("should never happen, should register new column %v into columnValueFactoryMap", col.Name.O)) - } - columnFactories[i] = factory - } - return columnFactories -} - -// Format the backoffType map to a string or nil. -func formatBackoffTypes(backoffMap map[string]int) interface{} { - type backoffStat struct { - backoffType string - count int - } - - size := len(backoffMap) - if size == 0 { - return nil - } - - backoffArray := make([]backoffStat, 0, len(backoffMap)) - for backoffType, count := range backoffMap { - backoffArray = append(backoffArray, backoffStat{backoffType, count}) - } - slices.SortFunc(backoffArray, func(i, j backoffStat) int { - return cmp.Compare(j.count, i.count) - }) - - var buffer bytes.Buffer - for index, stat := range backoffArray { - if _, err := fmt.Fprintf(&buffer, "%v:%d", stat.backoffType, stat.count); err != nil { - return "FORMAT ERROR" - } - if index < len(backoffArray)-1 { - buffer.WriteString(",") - } - } - return buffer.String() -} - -func avgInt(sum int64, count int64) int64 { - if count > 0 { - return sum / count - } - return 0 -} - -func avgFloat(sum int64, count int64) float64 { - if count > 0 { - return float64(sum) / float64(count) - } - return 0 -} - -func convertEmptyToNil(str string) interface{} { - if str == "" { - return nil - } - return str -} diff --git a/util/stmtsummary/v2/column_test.go b/util/stmtsummary/v2/column_test.go deleted file mode 100644 index 5e87cbe89a01c..0000000000000 --- a/util/stmtsummary/v2/column_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/parser/model" - "github.com/stretchr/testify/require" -) - -func TestColumn(t *testing.T) { - columns := []*model.ColumnInfo{ - {Name: model.NewCIStr(ClusterTableInstanceColumnNameStr)}, - {Name: model.NewCIStr(StmtTypeStr)}, - {Name: model.NewCIStr(SchemaNameStr)}, - {Name: model.NewCIStr(DigestStr)}, - {Name: model.NewCIStr(DigestTextStr)}, - {Name: model.NewCIStr(TableNamesStr)}, - {Name: model.NewCIStr(IndexNamesStr)}, - {Name: model.NewCIStr(SampleUserStr)}, - {Name: model.NewCIStr(ExecCountStr)}, - {Name: model.NewCIStr(SumLatencyStr)}, - {Name: model.NewCIStr(MaxLatencyStr)}, - } - factories := makeColumnFactories(columns) - info := GenerateStmtExecInfo4Test("digest") - record := NewStmtRecord(info) - record.Add(info) - for n, f := range factories { - column := f(mockColumnInfo{}, record) - switch columns[n].Name.O { - case ClusterTableInstanceColumnNameStr: - require.Equal(t, "instance_addr", column) - case StmtTypeStr: - require.Equal(t, record.StmtType, column) - case SchemaNameStr: - require.Equal(t, record.SchemaName, column) - case DigestStr: - require.Equal(t, record.Digest, column) - case DigestTextStr: - require.Equal(t, record.NormalizedSQL, column) - case TableNamesStr: - require.Equal(t, record.TableNames, column) - case IndexNamesStr: - require.Equal(t, strings.Join(record.IndexNames, ","), column) - case SampleUserStr: - require.Equal(t, info.User, column) - case ExecCountStr: - require.Equal(t, int64(1), column) - case SumLatencyStr: - require.Equal(t, int64(record.SumLatency), column) - case MaxLatencyStr: - require.Equal(t, int64(record.MaxLatency), column) - } - } -} - -type mockColumnInfo struct{} - -func (mockColumnInfo) getInstanceAddr() string { - return "instance_addr" -} - -func (mockColumnInfo) getTimeLocation() *time.Location { - loc, _ := time.LoadLocation("Asia/Shanghai") - return loc -} diff --git a/util/stmtsummary/v2/main_test.go b/util/stmtsummary/v2/main_test.go deleted file mode 100644 index 3c787adf06ce1..0000000000000 --- a/util/stmtsummary/v2/main_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/stmtsummary/v2/reader.go b/util/stmtsummary/v2/reader.go deleted file mode 100644 index dd7ce97978cf8..0000000000000 --- a/util/stmtsummary/v2/reader.go +++ /dev/null @@ -1,862 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "bufio" - "cmp" - "context" - "encoding/json" - "io" - "math" - "os" - "path/filepath" - "slices" - "strings" - "sync" - "time" - - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/auth" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/set" - "go.uber.org/zap" -) - -const ( - logFileTimeFormat = "2006-01-02T15-04-05.000" // depends on lumberjack.go#backupTimeFormat - maxLineSize = 1073741824 - - batchScanSize = 64 -) - -// StmtTimeRange is the time range type used in the stmtsummary package. -// [Begin, End) -type StmtTimeRange struct { - Begin int64 - End int64 -} - -// MemReader is used to read the current window's data maintained in memory by StmtSummary. -type MemReader struct { - s *StmtSummary - columns []*model.ColumnInfo - instanceAddr string - timeLocation *time.Location - - columnFactories []columnFactory - checker *stmtChecker -} - -// NewMemReader creates a MemReader from StmtSummary and other necessary parameters. -func NewMemReader(s *StmtSummary, - columns []*model.ColumnInfo, - instanceAddr string, - timeLocation *time.Location, - user *auth.UserIdentity, - hasProcessPriv bool, - digests set.StringSet, - timeRanges []*StmtTimeRange) *MemReader { - return &MemReader{ - s: s, - columns: columns, - instanceAddr: instanceAddr, - timeLocation: timeLocation, - columnFactories: makeColumnFactories(columns), - checker: &stmtChecker{ - user: user, - hasProcessPriv: hasProcessPriv, - digests: digests, - timeRanges: timeRanges, - }, - } -} - -// Rows returns rows converted from the current window's data maintained -// in memory by StmtSummary. All evicted data will be aggregated into a -// single row appended at the end. -func (r *MemReader) Rows() [][]types.Datum { - if r.s == nil { - return nil - } - end := timeNow().Unix() - r.s.windowLock.Lock() - w := r.s.window - if !r.checker.isTimeValid(w.begin.Unix(), end) { - r.s.windowLock.Unlock() - return nil - } - values := w.lru.Values() - evicted := w.evicted - r.s.windowLock.Unlock() - rows := make([][]types.Datum, 0, len(values)+1) - for _, v := range values { - record := v.(*lockedStmtRecord) - if !r.checker.isDigestValid(record.Digest) { - continue - } - func() { - record.Lock() - defer record.Unlock() - if !r.checker.hasPrivilege(record.AuthUsers) { - return - } - record.Begin = w.begin.Unix() - record.End = end - row := make([]types.Datum, len(r.columnFactories)) - for i, factory := range r.columnFactories { - row[i] = types.NewDatum(factory(r, record.StmtRecord)) - } - rows = append(rows, row) - }() - } - if r.checker.digests == nil { - func() { - evicted.Lock() - defer evicted.Unlock() - if evicted.other.ExecCount == 0 { - return - } - if !r.checker.hasPrivilege(evicted.other.AuthUsers) { - return - } - evicted.other.Begin = w.begin.Unix() - evicted.other.End = end - row := make([]types.Datum, len(r.columnFactories)) - for i, factory := range r.columnFactories { - row[i] = types.NewDatum(factory(r, evicted.other)) - } - rows = append(rows, row) - }() - } - return rows -} - -// getInstanceAddr implements columnInfo. -func (r *MemReader) getInstanceAddr() string { - return r.instanceAddr -} - -// getInstanceAddr implements columnInfo. -func (r *MemReader) getTimeLocation() *time.Location { - return r.timeLocation -} - -// HistoryReader is used to read data that has been persisted to files. -type HistoryReader struct { - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup - - instanceAddr string - timeLocation *time.Location - - columnFactories []columnFactory - checker *stmtChecker - files *stmtFiles - - concurrent int - rowsCh <-chan [][]types.Datum - errCh <-chan error -} - -// NewHistoryReader creates a HisroryReader from StmtSummary and other -// necessary parameters. If timeRanges is present, only files within -// the time range will be read. -func NewHistoryReader( - ctx context.Context, - columns []*model.ColumnInfo, - instanceAddr string, - timeLocation *time.Location, - user *auth.UserIdentity, - hasProcessPriv bool, - digests set.StringSet, - timeRanges []*StmtTimeRange, - concurrent int, -) (*HistoryReader, error) { - files, err := newStmtFiles(ctx, timeRanges) - if err != nil { - return nil, err - } - - if concurrent < 2 { - concurrent = 2 - } - rowsCh := make(chan [][]types.Datum, concurrent) - errCh := make(chan error, concurrent) - - ctx, cancel := context.WithCancel(ctx) - r := &HistoryReader{ - ctx: ctx, - cancel: cancel, - - instanceAddr: instanceAddr, - timeLocation: timeLocation, - columnFactories: makeColumnFactories(columns), - checker: &stmtChecker{ - user: user, - hasProcessPriv: hasProcessPriv, - digests: digests, - timeRanges: timeRanges, - }, - files: files, - concurrent: concurrent, - rowsCh: rowsCh, - errCh: errCh, - } - - r.wg.Add(1) - go func() { - defer r.wg.Done() - r.scheduleTasks(rowsCh, errCh) - }() - return r, nil -} - -// Rows returns rows converted from records in files. Reading and parsing -// works asynchronously. If (nil, nil) is returned, it means that the -// reading has been completed. -func (r *HistoryReader) Rows() ([][]types.Datum, error) { - ctx := r.ctx - for { - select { - case err := <-r.errCh: - return nil, err - case rows, ok := <-r.rowsCh: - if !ok { - select { - case err := <-r.errCh: - return nil, err - default: - return nil, nil - } - } - if len(rows) == 0 { - continue - } - return rows, nil - case <-ctx.Done(): - return nil, ctx.Err() - } - } -} - -// Close ends reading and closes all files. -func (r *HistoryReader) Close() error { - r.files.close() - if r.cancel != nil { - r.cancel() - } - r.wg.Wait() - return nil -} - -// 4 roles to handle the read task in pipeline: -// -// ## Pipeline -// . +--------------+ +---------------+ -// == files => | scan workers | == lines => | parse workers | == rows => -// . filesCh +--------------+ linesCh +---------------+ rowsCh -// -// ## Roles -// +--------------+--------------+------------------------------------+ -// | ROLE | COUNT | DESCRIPTION | -// +--------------+--------------+------------------------------------+ -// | Scan Worker | concurrent/2 | Scan files (I/O) first, then help | -// | | | parse workers to parse lines (CPU) | -// +--------------+--------------+------------------------------------+ -// | Parse Worker | concurrent- | Parse lines (CPU) to rows | -// | | concurrent/2 | | -// +--------------+--------------+------------------------------------+ -// | Manager | 1 | Drive the whole process and notify | -// | | | scan workers to switch role | -// +--------------+--------------+------------------------------------+ -// | Monitor | 1 | Cover failures and notify workers | -// | | | to exit | -// +--------------+--------------+------------------------------------+ -func (r *HistoryReader) scheduleTasks( - rowsCh chan<- [][]types.Datum, - errCh chan<- error, -) { - if r.files == nil || len(r.files.files) == 0 { - close(rowsCh) - return - } - - ctx, cancel := context.WithCancel(r.ctx) - defer cancel() - - scanWorker := &stmtScanWorker{ - ctx: ctx, - batchSize: batchScanSize, - checker: r.checker, - } - parseWorker := &stmtParseWorker{ - ctx: ctx, - instanceAddr: r.instanceAddr, - timeLocation: r.timeLocation, - checker: r.checker, - columnFactories: r.columnFactories, - } - - concurrent := r.concurrent - filesCh := make(chan *os.File, concurrent) - linesCh := make(chan [][]byte, concurrent) - innerErrCh := make(chan error, concurrent) - - var scanWg sync.WaitGroup - scanWg.Add(concurrent / 2) - scanDone := scanWg.Done - waitScanAllDone := scanWg.Wait - - var parseWg sync.WaitGroup - parseWg.Add(concurrent) // finally all workers will become parse workers - parseDone := parseWg.Done - waitParseAllDone := parseWg.Wait - - // Half of workers are scheduled to scan files and then parse lines. - for i := 0; i < concurrent/2; i++ { - go func() { - scanWorker.run(filesCh, linesCh, innerErrCh) - scanDone() - - parseWorker.run(linesCh, rowsCh, innerErrCh) - parseDone() - }() - } - - // Remaining workers are scheduled to parse lines. - for i := concurrent / 2; i < concurrent; i++ { - go func() { - parseWorker.run(linesCh, rowsCh, innerErrCh) - parseDone() - }() - } - - // Manager drives the whole process - var mgrWg sync.WaitGroup - mgrWg.Add(1) - go func() { - defer mgrWg.Done() - - func() { - for _, file := range r.files.files { - select { - case filesCh <- file.file: - case <-ctx.Done(): - return - } - } - }() - // No scan tasks to be generating. Notify idle scan - // workers to become parse workers - close(filesCh) - - // No parse tasks to be generating once all scan - // tasks are done. Notify idle parse workers to exit - waitScanAllDone() - close(linesCh) - - // No rows to be generating once all parse tasks - // are done. Notify monitor to close rowsCh - waitParseAllDone() - cancel() - }() - - // Monitor to cover failures and notify workers to exit - select { - case err := <-innerErrCh: - select { - case errCh <- err: - default: - } - cancel() // notify workers to exit - case <-ctx.Done(): - // notified by manager or parent ctx is canceled - } - mgrWg.Wait() - close(rowsCh) // task done -} - -type stmtChecker struct { - user *auth.UserIdentity - hasProcessPriv bool // If the user has the 'PROCESS' privilege, he can read all statements. - digests set.StringSet - timeRanges []*StmtTimeRange -} - -func (c *stmtChecker) hasPrivilege(authUsers map[string]struct{}) bool { - authed := true - if c.user != nil && !c.hasProcessPriv { - if len(authUsers) == 0 { - return false - } - _, authed = authUsers[c.user.Username] - } - return authed -} - -func (c *stmtChecker) isDigestValid(digest string) bool { - if c.digests == nil { - return true - } - return c.digests.Exist(digest) -} - -func (c *stmtChecker) isTimeValid(begin, end int64) bool { - if len(c.timeRanges) == 0 { - return true - } - for _, tr := range c.timeRanges { - if timeRangeOverlap(begin, end, tr.Begin, tr.End) { - return true - } - } - return false -} - -func (c *stmtChecker) needStop(curBegin int64) bool { - if len(c.timeRanges) == 0 { - return false - } - stop := true - for _, tr := range c.timeRanges { - if tr.End == 0 || tr.End >= curBegin { - stop = false - } - } - return stop -} - -type stmtTinyRecord struct { - Begin int64 `json:"begin"` - End int64 `json:"end"` -} - -type stmtFile struct { - file *os.File - begin int64 - end int64 -} - -func openStmtFile(path string) (*stmtFile, error) { - file, err := os.OpenFile(path, os.O_RDONLY, os.ModePerm) - if err != nil { - return nil, err - } - begin, err := parseBeginTsAndReseek(file) - if err != nil { - if err != io.EOF { - _ = file.Close() - return nil, err - } - } - end, err := parseEndTs(file) - if err != nil { - _ = file.Close() - return nil, err - } - - return &stmtFile{ - file: file, - begin: begin, - end: end, - }, nil -} - -func parseBeginTsAndReseek(file *os.File) (int64, error) { - if _, err := file.Seek(0, io.SeekStart); err != nil { - return 0, err - } - - reader := bufio.NewReader(file) - var record stmtTinyRecord - for { // ignore invalid lines - line, err := readLine(reader) - if err != nil { - return 0, err - } - err = json.Unmarshal(line, &record) - if err == nil { - break - } - } - - if _, err := file.Seek(0, io.SeekStart); err != nil { - return 0, err - } - return record.Begin, nil -} - -func parseEndTs(file *os.File) (int64, error) { - // tidb-statements.log - filename := config.GetGlobalConfig().Instance.StmtSummaryFilename - // .log - ext := filepath.Ext(filename) - // tidb-statements - prefix := filename[:len(filename)-len(ext)] - - // tidb-statements-2022-12-27T16-21-20.245.log - filename = filepath.Base(file.Name()) - // .log - ext = filepath.Ext(file.Name()) - // tidb-statements-2022-12-27T16-21-20.245 - filename = filename[:len(filename)-len(ext)] - - if strings.HasPrefix(filename, prefix+"-") { - // 2022-12-27T16-21-20.245 - timeStr := strings.TrimPrefix(filename, prefix+"-") - end, err := time.ParseInLocation(logFileTimeFormat, timeStr, time.Local) - if err != nil { - return 0, err - } - return end.Unix(), nil - } - return 0, nil -} - -func (f *stmtFile) close() error { - if f.file != nil { - return f.file.Close() - } - return nil -} - -type stmtFiles struct { - files []*stmtFile -} - -func newStmtFiles(ctx context.Context, timeRanges []*StmtTimeRange) (*stmtFiles, error) { - filename := config.GetGlobalConfig().Instance.StmtSummaryFilename - ext := filepath.Ext(filename) - prefix := filename[:len(filename)-len(ext)] - var files []*stmtFile - walkFn := func(path string, info os.DirEntry) error { - if info.IsDir() { - return nil - } - if !strings.HasPrefix(path, prefix) { - return nil - } - if isCtxDone(ctx) { - return ctx.Err() - } - file, err := openStmtFile(path) - if err != nil { - logutil.BgLogger().Warn("failed to open or parse statements file", zap.Error(err), zap.String("path", path)) - return nil - } - if len(timeRanges) == 0 { - files = append(files, file) - return nil - } - for _, tr := range timeRanges { - if timeRangeOverlap(file.begin, file.end, tr.Begin, tr.End) { - files = append(files, file) - return nil - } - } - return nil - } - - dir := filepath.Dir(filename) - entries, err := os.ReadDir(dir) - if err != nil { - return nil, err - } - for _, entry := range entries { - if err := walkFn(filepath.Join(dir, entry.Name()), entry); err != nil { - for _, f := range files { - _ = f.close() - } - return nil, err - } - } - slices.SortFunc(files, func(i, j *stmtFile) int { - return cmp.Compare(i.begin, j.begin) - }) - return &stmtFiles{files: files}, nil -} - -func (f *stmtFiles) close() { - for _, f := range f.files { - _ = f.close() - } -} - -type stmtScanWorker struct { - ctx context.Context - batchSize int - checker *stmtChecker -} - -func (w *stmtScanWorker) run( - fileCh <-chan *os.File, - linesCh chan<- [][]byte, - errCh chan<- error, -) { - for { - select { - case file, ok := <-fileCh: - if !ok { - return - } - w.handleFile(file, linesCh, errCh) - case <-w.ctx.Done(): - return - } - } -} - -func (w *stmtScanWorker) handleFile( - file *os.File, - linesCh chan<- [][]byte, - errCh chan<- error, -) { - if file == nil { - return - } - - reader := bufio.NewReader(file) - for { - if isCtxDone(w.ctx) { - return - } - - lines, err := w.readlines(reader) - if err == io.EOF { - return - } - if err != nil { - w.putErr(err, errCh) - return - } - - w.putLines(lines, linesCh) - } -} - -func (w *stmtScanWorker) putErr( - err error, - errCh chan<- error, -) { - select { - case errCh <- err: - case <-w.ctx.Done(): - } -} - -func (w *stmtScanWorker) putLines( - lines [][]byte, - linesCh chan<- [][]byte, -) { - select { - case linesCh <- lines: - case <-w.ctx.Done(): - } -} - -func (w *stmtScanWorker) readlines(reader *bufio.Reader) ([][]byte, error) { - var firstLine []byte - var record *stmtTinyRecord - for { // ingore invalid lines - var err error - firstLine, err = readLine(reader) - if err != nil { - return nil, err - } - - record, err = w.parse(firstLine) - if err == nil { - break - } - } - - if w.needStop(record) { - // done because remaining lines in file - // are not in the time range - return nil, io.EOF - } - - lines := make([][]byte, 0, w.batchSize) - lines = append(lines, firstLine) - - newLines, err := readLines(reader, w.batchSize-1) - if err == io.EOF { - return lines, nil - } - if err != nil { - return nil, err - } - - lines = append(lines, newLines...) - return lines, nil -} - -func (*stmtScanWorker) parse(raw []byte) (*stmtTinyRecord, error) { - var record stmtTinyRecord - if err := json.Unmarshal(raw, &record); err != nil { - return nil, err - } - return &record, nil -} - -func (w *stmtScanWorker) needStop(record *stmtTinyRecord) bool { - return w.checker.needStop(record.Begin) -} - -type stmtParseWorker struct { - ctx context.Context - instanceAddr string - timeLocation *time.Location - checker *stmtChecker - columnFactories []columnFactory -} - -func (w *stmtParseWorker) run( - linesCh <-chan [][]byte, - rowsCh chan<- [][]types.Datum, - errCh chan<- error, -) { - for { - select { - case lines, ok := <-linesCh: - if !ok { - return - } - w.handleLines(lines, rowsCh, errCh) - case <-w.ctx.Done(): - return - } - } -} - -func (w *stmtParseWorker) handleLines( - lines [][]byte, - rowsCh chan<- [][]types.Datum, - _ chan<- error, -) { - if len(lines) == 0 { - return - } - - rows := make([][]types.Datum, 0, len(lines)) - for _, line := range lines { - record, err := w.parse(line) - if err != nil { - // ignore invalid lines - continue - } - - if w.needStop(record) { - break - } - - if !w.matchConds(record) { - continue - } - - row := w.buildRow(record) - rows = append(rows, row) - } - - if len(rows) > 0 { - w.putRows(rows, rowsCh) - } -} - -func (w *stmtParseWorker) putRows( - rows [][]types.Datum, - rowsCh chan<- [][]types.Datum, -) { - select { - case rowsCh <- rows: - case <-w.ctx.Done(): - } -} - -func (*stmtParseWorker) parse(raw []byte) (*StmtRecord, error) { - var record StmtRecord - if err := json.Unmarshal(raw, &record); err != nil { - return nil, err - } - return &record, nil -} - -func (w *stmtParseWorker) needStop(record *StmtRecord) bool { - return w.checker.needStop(record.Begin) -} - -func (w *stmtParseWorker) matchConds(record *StmtRecord) bool { - if !w.checker.isTimeValid(record.Begin, record.End) { - return false - } - if !w.checker.isDigestValid(record.Digest) { - return false - } - if !w.checker.hasPrivilege(record.AuthUsers) { - return false - } - return true -} - -func (w *stmtParseWorker) buildRow(record *StmtRecord) []types.Datum { - row := make([]types.Datum, len(w.columnFactories)) - for n, factory := range w.columnFactories { - row[n] = types.NewDatum(factory(w, record)) - } - return row -} - -// getInstanceAddr implements columnInfo. -func (w *stmtParseWorker) getInstanceAddr() string { - return w.instanceAddr -} - -// getInstanceAddr implements columnInfo. -func (w *stmtParseWorker) getTimeLocation() *time.Location { - return w.timeLocation -} - -func isCtxDone(ctx context.Context) bool { - select { - case <-ctx.Done(): - return true - default: - return false - } -} - -func readLine(reader *bufio.Reader) ([]byte, error) { - return util.ReadLine(reader, maxLineSize) -} - -func readLines(reader *bufio.Reader, count int) ([][]byte, error) { - return util.ReadLines(reader, count, maxLineSize) -} - -func timeRangeOverlap(aBegin, aEnd, bBegin, bEnd int64) bool { - if aEnd == 0 || aEnd < aBegin { - aEnd = math.MaxInt64 - } - if bEnd == 0 || bEnd < bBegin { - bEnd = math.MaxInt64 - } - // https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap - return aBegin <= bEnd && aEnd >= bBegin -} diff --git a/util/stmtsummary/v2/stmtsummary.go b/util/stmtsummary/v2/stmtsummary.go deleted file mode 100644 index f526dfcc13475..0000000000000 --- a/util/stmtsummary/v2/stmtsummary.go +++ /dev/null @@ -1,613 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package stmtsummary - -import ( - "context" - "errors" - "maps" - "math" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/log" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/types" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/kvcache" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/stmtsummary" - atomic2 "go.uber.org/atomic" - "go.uber.org/zap" -) - -const ( - defaultEnabled = true - defaultEnableInternalQuery = false - defaultMaxStmtCount = 3000 - defaultMaxSQLLength = 4096 - defaultRefreshInterval = 30 * 60 // 30 min - defaultRotateCheckInterval = 1 // s -) - -var ( - // GlobalStmtSummary is the global StmtSummary instance, we need - // to explicitly call Setup() to initialize it. It will then be - // referenced by SessionVars.StmtSummary for each session. - GlobalStmtSummary *StmtSummary - - timeNow = time.Now -) - -// Setup initializes the GlobalStmtSummary. -func Setup(cfg *Config) (err error) { - GlobalStmtSummary, err = NewStmtSummary(cfg) - return -} - -// Close closes the GlobalStmtSummary. -func Close() { - if GlobalStmtSummary != nil { - GlobalStmtSummary.Close() - } -} - -// Config is the static configuration of StmtSummary. It cannot be -// modified at runtime. -type Config struct { - Filename string - FileMaxSize int - FileMaxDays int - FileMaxBackups int -} - -// StmtSummary represents the complete statements summary statistics. -// It controls data rotation and persistence internally, and provides -// reading interface through MemReader and HistoryReader. -type StmtSummary struct { - ctx context.Context - cancel context.CancelFunc - - optEnabled *atomic2.Bool - optEnableInternalQuery *atomic2.Bool - optMaxStmtCount *atomic2.Uint32 - optMaxSQLLength *atomic2.Uint32 - optRefreshInterval *atomic2.Uint32 - - window *stmtWindow - windowLock sync.Mutex - storage stmtStorage - closeWg sync.WaitGroup - closed atomic.Bool -} - -// NewStmtSummary creates a new StmtSummary from Config. -func NewStmtSummary(cfg *Config) (*StmtSummary, error) { - if cfg.Filename == "" { - return nil, errors.New("stmtsummary: empty filename") - } - - ctx, cancel := context.WithCancel(context.Background()) - s := &StmtSummary{ - ctx: ctx, - cancel: cancel, - - // These options can be changed dynamically at runtime. - // The default values here are just placeholders, and the real values in - // sessionctx/variables/tidb_vars.go will overwrite them after TiDB starts. - optEnabled: atomic2.NewBool(defaultEnabled), - optEnableInternalQuery: atomic2.NewBool(defaultEnableInternalQuery), - optMaxStmtCount: atomic2.NewUint32(defaultMaxStmtCount), - optMaxSQLLength: atomic2.NewUint32(defaultMaxSQLLength), - optRefreshInterval: atomic2.NewUint32(defaultRefreshInterval), - window: newStmtWindow(timeNow(), uint(defaultMaxStmtCount)), - storage: newStmtLogStorage(&log.Config{ - File: log.FileLogConfig{ - Filename: cfg.Filename, - MaxSize: cfg.FileMaxSize, - MaxDays: cfg.FileMaxDays, - MaxBackups: cfg.FileMaxBackups, - }, - }), - } - - s.closeWg.Add(1) - go func() { - defer s.closeWg.Done() - s.rotateLoop() - }() - - return s, nil -} - -// NewStmtSummary4Test creates a new StmtSummary for testing purposes. -func NewStmtSummary4Test(maxStmtCount uint) *StmtSummary { - ctx, cancel := context.WithCancel(context.Background()) - - ss := &StmtSummary{ - ctx: ctx, - cancel: cancel, - - optEnabled: atomic2.NewBool(defaultEnabled), - optEnableInternalQuery: atomic2.NewBool(defaultEnableInternalQuery), - optMaxStmtCount: atomic2.NewUint32(defaultMaxStmtCount), - optMaxSQLLength: atomic2.NewUint32(defaultMaxSQLLength), - optRefreshInterval: atomic2.NewUint32(60 * 60 * 24 * 365), // 1 year - window: newStmtWindow(timeNow(), maxStmtCount), - storage: &mockStmtStorage{}, - } - - return ss -} - -// Enabled returns whether the StmtSummary is enabled. -func (s *StmtSummary) Enabled() bool { - return s.optEnabled.Load() -} - -// SetEnabled is used to enable or disable StmtSummary. If disabled, in-memory -// data will be cleared, (persisted data will still be remained). -func (s *StmtSummary) SetEnabled(v bool) error { - s.optEnabled.Store(v) - if !v { - s.Clear() - } - - return nil -} - -// EnableInternalQuery returns whether the StmtSummary counts internal queries. -func (s *StmtSummary) EnableInternalQuery() bool { - return s.optEnableInternalQuery.Load() -} - -// SetEnableInternalQuery is used to enable or disable StmtSummary's internal -// query statistics. If disabled, in-memory internal queries will be cleared, -// (persisted internal queries will still be remained). -func (s *StmtSummary) SetEnableInternalQuery(v bool) error { - s.optEnableInternalQuery.Store(v) - if !v { - s.ClearInternal() - } - - return nil -} - -// MaxStmtCount returns the maximum number of statements. -func (s *StmtSummary) MaxStmtCount() uint32 { - return s.optMaxStmtCount.Load() -} - -// SetMaxStmtCount is used to set the maximum number of statements. -// If the current number exceeds the maximum number, the excess will be evicted. -func (s *StmtSummary) SetMaxStmtCount(v uint32) error { - if v < 1 { - v = 1 - } - s.optMaxStmtCount.Store(v) - s.windowLock.Lock() - _ = s.window.lru.SetCapacity(uint(v)) - s.windowLock.Unlock() - - return nil -} - -// MaxSQLLength returns the maximum size of a single SQL statement. -func (s *StmtSummary) MaxSQLLength() uint32 { - return s.optMaxSQLLength.Load() -} - -// SetMaxSQLLength sets the maximum size of a single SQL statement. -func (s *StmtSummary) SetMaxSQLLength(v uint32) error { - s.optMaxSQLLength.Store(v) - - return nil -} - -// RefreshInterval returns the period (in seconds) at which the statistics -// window is refreshed (persisted). -func (s *StmtSummary) RefreshInterval() uint32 { - return s.optRefreshInterval.Load() -} - -// SetRefreshInterval sets the period (in seconds) for the statistics window -// to be refreshed (persisted). This may trigger a refresh (persistence) of -// the current statistics window early. -func (s *StmtSummary) SetRefreshInterval(v uint32) error { - if v < 1 { - v = 1 - } - s.optRefreshInterval.Store(v) - - return nil -} - -// Add adds a single stmtsummary.StmtExecInfo to the current statistics window -// of StmtSummary. Before adding, it will check whether the current window has -// expired, and if it has expired, the window will be persisted asynchronously -// and a new window will be created to replace the current one. -func (s *StmtSummary) Add(info *stmtsummary.StmtExecInfo) { - if s.closed.Load() { - return - } - - k := &stmtKey{ - schemaName: info.SchemaName, - digest: info.Digest, - prevDigest: info.PrevSQLDigest, - planDigest: info.PlanDigest, - } - k.Hash() // Calculate hash value in advance, to reduce the time holding the window lock. - - // Add info to the current statistics window. - s.windowLock.Lock() - var record *lockedStmtRecord - if v, ok := s.window.lru.Get(k); ok { - record = v.(*lockedStmtRecord) - } else { - record = &lockedStmtRecord{StmtRecord: NewStmtRecord(info)} - s.window.lru.Put(k, record) - } - s.windowLock.Unlock() - - record.Lock() - record.Add(info) - record.Unlock() -} - -// Evicted returns the number of statements evicted for the current -// time window. The returned type is one row consisting of three -// columns: [BEGIN_TIME, END_TIME, EVICTED_COUNT]. -func (s *StmtSummary) Evicted() []types.Datum { - s.windowLock.Lock() - count := int64(s.window.evicted.count()) - s.windowLock.Unlock() - if count == 0 { - return nil - } - begin := types.NewTime(types.FromGoTime(s.window.begin), mysql.TypeTimestamp, 0) - end := types.NewTime(types.FromGoTime(timeNow()), mysql.TypeTimestamp, 0) - return types.MakeDatums(begin, end, count) -} - -// Clear clears all data in the current window, and the data that -// has been persisted will not be cleared. -func (s *StmtSummary) Clear() { - s.windowLock.Lock() - defer s.windowLock.Unlock() - s.window.clear() -} - -// ClearInternal clears all internal queries of the current window, -// and the data that has been persisted will not be cleared. -func (s *StmtSummary) ClearInternal() { - s.windowLock.Lock() - defer s.windowLock.Unlock() - for _, k := range s.window.lru.Keys() { - v, _ := s.window.lru.Get(k) - if v.(*lockedStmtRecord).IsInternal { - s.window.lru.Delete(k) - } - } -} - -// Close closes the work of StmtSummary. -func (s *StmtSummary) Close() { - if s.cancel != nil { - s.cancel() - s.closeWg.Wait() - } - s.closed.Store(true) - s.flush() -} - -func (s *StmtSummary) flush() { - now := timeNow() - - s.windowLock.Lock() - window := s.window - s.window = newStmtWindow(now, uint(s.MaxStmtCount())) - s.windowLock.Unlock() - - if window.lru.Size() > 0 { - s.storage.persist(window, now) - } - err := s.storage.sync() - if err != nil { - logutil.BgLogger().Error("sync stmt summary failed", zap.Error(err)) - } -} - -// GetMoreThanCntBindableStmt is used to get bindable statements. -// Statements whose execution times exceed the threshold will be -// returned. Since the historical data has been persisted, we only -// refer to the statistics data of the current window in memory. -func (s *StmtSummary) GetMoreThanCntBindableStmt(cnt int64) []*stmtsummary.BindableStmt { - s.windowLock.Lock() - values := s.window.lru.Values() - s.windowLock.Unlock() - stmts := make([]*stmtsummary.BindableStmt, 0, len(values)) - for _, value := range values { - record := value.(*lockedStmtRecord) - func() { - record.Lock() - defer record.Unlock() - if record.StmtType == "Select" || - record.StmtType == "Delete" || - record.StmtType == "Update" || - record.StmtType == "Insert" || - record.StmtType == "Replace" { - if len(record.AuthUsers) > 0 && record.ExecCount > cnt { - stmt := &stmtsummary.BindableStmt{ - Schema: record.SchemaName, - Query: record.SampleSQL, - PlanHint: record.PlanHint, - Charset: record.Charset, - Collation: record.Collation, - Users: make(map[string]struct{}), - } - maps.Copy(stmt.Users, record.AuthUsers) - - // If it is SQL command prepare / execute, the ssElement.sampleSQL - // is `execute ...`, we should get the original select query. - // If it is binary protocol prepare / execute, ssbd.normalizedSQL - // should be same as ssElement.sampleSQL. - if record.Prepared { - stmt.Query = record.NormalizedSQL - } - stmts = append(stmts, stmt) - } - } - }() - } - return stmts -} - -func (s *StmtSummary) rotateLoop() { - tick := time.NewTicker(defaultRotateCheckInterval * time.Second) - defer tick.Stop() - - for { - select { - case <-s.ctx.Done(): - return - case <-tick.C: - now := timeNow() - s.windowLock.Lock() - // The current window has expired and needs to be refreshed and persisted. - if now.After(s.window.begin.Add(time.Duration(s.RefreshInterval()) * time.Second)) { - s.rotate(now) - } - s.windowLock.Unlock() - } - } -} - -func (s *StmtSummary) rotate(now time.Time) { - w := s.window - s.window = newStmtWindow(now, uint(s.MaxStmtCount())) - size := w.lru.Size() - if size > 0 { - // Persist window asynchronously. - s.closeWg.Add(1) - go func() { - defer s.closeWg.Done() - s.storage.persist(w, now) - }() - } -} - -// stmtWindow represents a single statistical window, which has a begin -// time and an end time. Data within a single window is eliminated -// according to the LRU strategy. All evicted data will be aggregated -// into stmtEvicted. -type stmtWindow struct { - begin time.Time - lru *kvcache.SimpleLRUCache // *stmtKey => *lockedStmtRecord - evicted *stmtEvicted -} - -func newStmtWindow(begin time.Time, capacity uint) *stmtWindow { - w := &stmtWindow{ - begin: begin, - lru: kvcache.NewSimpleLRUCache(capacity, 0, 0), - evicted: newStmtEvicted(), - } - w.lru.SetOnEvict(func(k kvcache.Key, v kvcache.Value) { - r := v.(*lockedStmtRecord) - r.Lock() - defer r.Unlock() - w.evicted.add(k.(*stmtKey), r.StmtRecord) - }) - return w -} - -func (w *stmtWindow) clear() { - w.lru.DeleteAll() - w.evicted = newStmtEvicted() -} - -type stmtStorage interface { - persist(w *stmtWindow, end time.Time) - sync() error -} - -// stmtKey defines key for stmtElement. -type stmtKey struct { - // Same statements may appear in different schema, but they refer to different tables. - schemaName string - digest string - // The digest of the previous statement. - prevDigest string - // The digest of the plan of this SQL. - planDigest string - // `hash` is the hash value of this object. - hash []byte -} - -// Hash implements SimpleLRUCache.Key. -// Only when current SQL is `commit` do we record `prevSQL`. Otherwise, `prevSQL` is empty. -// `prevSQL` is included in the key To distinguish different transactions. -func (k *stmtKey) Hash() []byte { - if len(k.hash) == 0 { - k.hash = make([]byte, 0, len(k.schemaName)+len(k.digest)+len(k.prevDigest)+len(k.planDigest)) - k.hash = append(k.hash, hack.Slice(k.digest)...) - k.hash = append(k.hash, hack.Slice(k.schemaName)...) - k.hash = append(k.hash, hack.Slice(k.prevDigest)...) - k.hash = append(k.hash, hack.Slice(k.planDigest)...) - } - return k.hash -} - -type stmtEvicted struct { - sync.Mutex - keys map[string]struct{} - other *StmtRecord -} - -func newStmtEvicted() *stmtEvicted { - return &stmtEvicted{ - keys: make(map[string]struct{}), - other: &StmtRecord{ - AuthUsers: make(map[string]struct{}), - MinLatency: time.Duration(math.MaxInt64), - BackoffTypes: make(map[string]int), - FirstSeen: time.Unix(math.MaxInt64, 0), - }, - } -} - -func (e *stmtEvicted) add(key *stmtKey, record *StmtRecord) { - if key == nil || record == nil { - return - } - e.Lock() - defer e.Unlock() - e.keys[string(key.Hash())] = struct{}{} - e.other.Merge(record) -} - -func (e *stmtEvicted) count() int { - e.Lock() - defer e.Unlock() - return len(e.keys) -} - -type lockedStmtRecord struct { - sync.Mutex - *StmtRecord -} - -type mockStmtStorage struct { - sync.Mutex - windows []*stmtWindow -} - -func (s *mockStmtStorage) persist(w *stmtWindow, _ time.Time) { - s.Lock() - s.windows = append(s.windows, w) - s.Unlock() -} - -func (*mockStmtStorage) sync() error { - return nil -} - -/* Public proxy functions between v1 and v2 */ - -// Add wraps GlobalStmtSummary.Add and stmtsummary.StmtSummaryByDigestMap.AddStatement. -func Add(stmtExecInfo *stmtsummary.StmtExecInfo) { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - GlobalStmtSummary.Add(stmtExecInfo) - } else { - stmtsummary.StmtSummaryByDigestMap.AddStatement(stmtExecInfo) - } -} - -// Enabled wraps GlobalStmtSummary.Enabled and stmtsummary.StmtSummaryByDigestMap.Enabled. -func Enabled() bool { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.Enabled() - } - return stmtsummary.StmtSummaryByDigestMap.Enabled() -} - -// EnabledInternal wraps GlobalStmtSummary.EnableInternalQuery and stmtsummary.StmtSummaryByDigestMap.EnabledInternal. -func EnabledInternal() bool { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.EnableInternalQuery() - } - return stmtsummary.StmtSummaryByDigestMap.EnabledInternal() -} - -// SetEnabled wraps GlobalStmtSummary.SetEnabled and stmtsummary.StmtSummaryByDigestMap.SetEnabled. -func SetEnabled(v bool) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.SetEnabled(v) - } - return stmtsummary.StmtSummaryByDigestMap.SetEnabled(v) -} - -// SetEnableInternalQuery wraps GlobalStmtSummary.SetEnableInternalQuery and -// stmtsummary.StmtSummaryByDigestMap.SetEnabledInternalQuery. -func SetEnableInternalQuery(v bool) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.SetEnableInternalQuery(v) - } - return stmtsummary.StmtSummaryByDigestMap.SetEnabledInternalQuery(v) -} - -// SetRefreshInterval wraps GlobalStmtSummary.SetRefreshInterval and stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval. -func SetRefreshInterval(v int64) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.SetRefreshInterval(uint32(v)) - } - return stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval(v) -} - -// SetHistorySize wraps stmtsummary.StmtSummaryByDigestMap.SetHistorySize. -func SetHistorySize(v int) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return nil // not support - } - return stmtsummary.StmtSummaryByDigestMap.SetHistorySize(v) -} - -// SetMaxStmtCount wraps GlobalStmtSummary.SetMaxStmtCount and stmtsummary.StmtSummaryByDigestMap.SetMaxStmtCount. -func SetMaxStmtCount(v int) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.SetMaxStmtCount(uint32(v)) - } - return stmtsummary.StmtSummaryByDigestMap.SetMaxStmtCount(uint(v)) -} - -// SetMaxSQLLength wraps GlobalStmtSummary.SetMaxSQLLength and stmtsummary.StmtSummaryByDigestMap.SetMaxSQLLength. -func SetMaxSQLLength(v int) error { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.SetMaxSQLLength(uint32(v)) - } - return stmtsummary.StmtSummaryByDigestMap.SetMaxSQLLength(v) -} - -// GetMoreThanCntBindableStmt wraps GlobalStmtSummary.GetMoreThanCntBindableStmt and -// stmtsummary.StmtSummaryByDigestMap.GetMoreThanCntBindableStmt. -func GetMoreThanCntBindableStmt(frequency int64) []*stmtsummary.BindableStmt { - if config.GetGlobalConfig().Instance.StmtSummaryEnablePersistent { - return GlobalStmtSummary.GetMoreThanCntBindableStmt(frequency) - } - return stmtsummary.StmtSummaryByDigestMap.GetMoreThanCntBindableStmt(frequency) -} diff --git a/util/stmtsummary/v2/tests/BUILD.bazel b/util/stmtsummary/v2/tests/BUILD.bazel deleted file mode 100644 index 35b360a70f8bc..0000000000000 --- a/util/stmtsummary/v2/tests/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_test") - -go_test( - name = "tests_test", - timeout = "short", - srcs = [ - "main_test.go", - "table_test.go", - ], - flaky = True, - shard_count = 10, - deps = [ - "//config", - "//kv", - "//parser/auth", - "//planner/core", - "//session", - "//testkit", - "//testkit/testsetup", - "//util/stmtsummary/v2:stmtsummary", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/stmtsummary/v2/tests/main_test.go b/util/stmtsummary/v2/tests/main_test.go deleted file mode 100644 index 4eb5cf5dcabf1..0000000000000 --- a/util/stmtsummary/v2/tests/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.opencensus.io/stats/view.(*worker).start"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - goleak.IgnoreTopFunction("gopkg.in/natefinch/lumberjack%2ev2.(*Logger).millRun"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/stmtsummary/v2/tests/table_test.go b/util/stmtsummary/v2/tests/table_test.go deleted file mode 100644 index 9ff792b4d0b8c..0000000000000 --- a/util/stmtsummary/v2/tests/table_test.go +++ /dev/null @@ -1,570 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package tests - -import ( - "fmt" - "math" - "os" - "testing" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/config" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/parser/auth" - plannercore "github.com/pingcap/tidb/planner/core" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/testkit" - stmtsummaryv2 "github.com/pingcap/tidb/util/stmtsummary/v2" - "github.com/stretchr/testify/require" -) - -func TestStmtSummaryTable(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - tk.MustExec("set @@tidb_enable_collect_execution_info=0;") - tk.MustQuery("select column_comment from information_schema.columns " + - "where table_name='STATEMENTS_SUMMARY' and column_name='STMT_TYPE'", - ).Check(testkit.Rows("Statement type")) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("1")) - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - // Test INSERT - tk.MustExec("insert into t values(1, 'a')") - tk.MustExec("insert into t values(2, 'b')") - tk.MustExec("insert into t VALUES(3, 'c')") - tk.MustExec("/**/insert into t values(4, 'd')") - - sql := "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text " + - "from information_schema.statements_summary " + - "where digest_text like 'insert into `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) - - // Test point get. - tk.MustExec("drop table if exists p") - tk.MustExec("create table p(a int primary key, b int)") - for i := 1; i < 3; i++ { - tk.MustQuery("select b from p where a=1") - expectedResult := fmt.Sprintf("%d \tid \ttask\testRows\toperator info\n\tPoint_Get_1\troot\t1 \ttable:p, handle:1 %s", i, "test.p") - // Also make sure that the plan digest is not empty - sql = "select exec_count, plan, table_names from information_schema.statements_summary " + - "where digest_text like 'select `b` from `p`%' and plan_digest != ''" - tk.MustQuery(sql).Check(testkit.Rows(expectedResult)) - } - - // Point get another database. - tk.MustQuery("select variable_value from mysql.tidb where variable_name = 'system_tz'") - // Test for Encode plan cache. - p1 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() - require.Greater(t, len(p1), 0) - rows := tk.MustQuery("select tidb_decode_plan('" + p1 + "');").Rows() - require.Equal(t, 1, len(rows)) - require.Equal(t, 1, len(rows[0])) - require.Regexp(t, "\n.*Point_Get.*table.tidb, index.PRIMARY.VARIABLE_NAME", rows[0][0]) - - sql = "select table_names from information_schema.statements_summary " + - "where digest_text like 'select `variable_value`%' and `schema_name`='test'" - tk.MustQuery(sql).Check(testkit.Rows("mysql.tidb")) - - // Test `create database`. - tk.MustExec("create database if not exists test") - // Test for Encode plan cache. - p2 := tk.Session().GetSessionVars().StmtCtx.GetEncodedPlan() - require.Equal(t, "", p2) - tk.MustQuery(`select table_names - from information_schema.statements_summary - where digest_text like 'create database%' and schema_name='test'`, - ).Check(testkit.Rows("")) - - // Test SELECT. - const failpointName = "github.com/pingcap/tidb/planner/core/mockPlanRowCount" - require.NoError(t, failpoint.Enable(failpointName, "return(100)")) - defer func() { require.NoError(t, failpoint.Disable(failpointName)) }() - tk.MustQuery("select * from t where a=2") - - // sum_cop_task_num is always 0 if tidb_enable_collect_execution_info disabled - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t100 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) - - // select ... order by - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text - from information_schema.statements_summary - order by exec_count desc limit 1`, - ).Check(testkit.Rows("Insert test test.t 4 0 0 0 0 0 2 2 1 1 1 insert into t values(1, 'a')")) - - // Test different plans with same digest. - require.NoError(t, failpoint.Enable(failpointName, "return(1000)")) - tk.MustQuery("select * from t where a=3") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows( - "Select test test.t t:k 2 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t100 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t100 \ttable:t, keep order:false, stats:pseudo")) - - // Disable it again. - tk.MustExec("set global tidb_enable_stmt_summary = false") - defer tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustQuery("select @@global.tidb_enable_stmt_summary").Check(testkit.Rows("0")) - - // Create a new session to test - tk = newTestKitWithRoot(t, store) - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) // affect est-rows in this UT - - // This statement shouldn't be summarized. - tk.MustQuery("select * from t where a=2") - - // The table should be cleared. - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, plan - from information_schema.statements_summary`, - ).Check(testkit.Rows()) - - tk.MustExec("SET GLOBAL tidb_enable_stmt_summary = on") - // It should work immediately. - tk.MustExec("begin") - tk.MustExec("insert into t values(1, 'a')") - tk.MustExec("commit") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text " + - "from information_schema.statements_summary " + - "where digest_text like 'insert into `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Insert test test.t 1 0 0 0 0 0 0 0 0 0 1 insert into t values(1, 'a') ")) - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, prev_sample_text - from information_schema.statements_summary - where digest_text='commit'`, - ).Check(testkit.Rows("Commit test 1 0 0 0 0 0 2 2 1 1 0 commit insert into t values(1, 'a')")) - - tk.MustQuery("select * from t where a=2") - sql = "select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, " + - "max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, " + - "max_prewrite_regions, avg_affected_rows, query_sample_text, plan " + - "from information_schema.statements_summary " + - "where digest_text like 'select * from `t`%'" - tk.MustQuery(sql).Check(testkit.Rows("Select test test.t t:k 1 0 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask \testRows\toperator info\n" + - "\tIndexLookUp_10 \troot \t1000 \t\n" + - "\t├─IndexRangeScan_8(Build)\tcop[tikv]\t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableRowIDScan_9(Probe)\tcop[tikv]\t1000 \ttable:t, keep order:false, stats:pseudo")) - - // Disable it in global scope. - tk.MustExec("set global tidb_enable_stmt_summary = false") - - // Create a new session to test. - tk = newTestKitWithRoot(t, store) - - // Statement summary is disabled. - tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, - max_total_keys, avg_processed_keys, max_processed_keys, avg_write_keys, max_write_keys, avg_prewrite_regions, - max_prewrite_regions, avg_affected_rows, query_sample_text, plan - from information_schema.statements_summary`, - ).Check(testkit.Rows()) - - tk.MustExec("set global tidb_enable_stmt_summary = on") - tk.MustExec("set global tidb_stmt_summary_history_size = 24") -} - -func TestStmtSummaryTablePrivilege(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - result := tk.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - result = tk.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - - tk1 := newTestKit(t, store) - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Ordinary users can not see others' records - require.Equal(t, 0, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 0, len(result.Rows())) - tk1.MustExec("select * from t where b=1") - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Ordinary users can see his own records - require.Equal(t, 1, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 1, len(result.Rows())) - - tk.MustExec("grant process on *.* to 'test_user'@'localhost'") - result = tk1.MustQuery("select * from information_schema.statements_summary where digest_text like 'select * from `t`%'") - // Users with 'PROCESS' privileges can query all records. - require.Equal(t, 2, len(result.Rows())) - result = tk1.MustQuery("select * from information_schema.statements_summary_history where digest_text like 'select * from `t`%'") - require.Equal(t, 2, len(result.Rows())) -} - -func TestCapturePrivilege(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t") - - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1(a int, b varchar(10), key k(a))") - defer tk.MustExec("drop table if exists t1") - - // Clear all statements. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - // Create a new user to test statements summary table privilege - tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("create user 'test_user'@'localhost'") - defer tk.MustExec("drop user if exists 'test_user'@'localhost'") - tk.MustExec("grant select on test.t1 to 'test_user'@'localhost'") - tk.MustExec("select * from t where a=1") - tk.MustExec("select * from t where a=1") - tk.MustExec("admin capture bindings") - rows := tk.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk1 := newTestKit(t, store) - tk1.Session().Auth(&auth.UserIdentity{ - Username: "test_user", - Hostname: "localhost", - AuthUsername: "test_user", - AuthHostname: "localhost", - }, nil, nil, nil) - - rows = tk1.MustQuery("show global bindings").Rows() - // Ordinary users can not see others' records - require.Len(t, rows, 0) - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("select * from t1 where b=1") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 1) - - tk.MustExec("grant all on *.* to 'test_user'@'localhost'") - tk1.MustExec("admin capture bindings") - rows = tk1.MustQuery("show global bindings").Rows() - require.Len(t, rows, 2) -} - -func TestStmtSummaryErrorCount(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - // Clear summaries. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - tk.MustExec("use test") - tk.MustExec("drop table if exists stmt_summary_test") - tk.MustExec("create table stmt_summary_test(id int primary key)") - tk.MustExec("insert into stmt_summary_test values(1)") - _, err := tk.Exec("insert into stmt_summary_test values(1)") - require.Error(t, err) - - sql := "select exec_count, sum_errors, sum_warnings from information_schema.statements_summary where digest_text like \"insert into `stmt_summary_test`%\"" - tk.MustQuery(sql).Check(testkit.Rows("2 1 0")) - - tk.MustExec("insert ignore into stmt_summary_test values(1)") - sql = "select exec_count, sum_errors, sum_warnings from information_schema.statements_summary where digest_text like \"insert ignore into `stmt_summary_test`%\"" - tk.MustQuery(sql).Check(testkit.Rows("1 0 1")) -} - -func TestStmtSummaryPreparedStatements(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - // Clear summaries. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - - tk.MustExec("use test") - tk.MustExec("prepare stmt from 'select ?'") - tk.MustExec("set @number=1") - tk.MustExec("execute stmt using @number") - - tk.MustQuery(`select exec_count - from information_schema.statements_summary - where digest_text like "prepare%"`).Check(testkit.Rows()) - tk.MustQuery(`select exec_count - from information_schema.statements_summary - where digest_text like "select ?"`).Check(testkit.Rows("1")) -} - -func TestStmtSummarySensitiveQuery(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustExec("drop user if exists user_sensitive;") - tk.MustExec("create user user_sensitive identified by '123456789';") - tk.MustExec("alter user 'user_sensitive'@'%' identified by 'abcdefg';") - tk.MustExec("set password for 'user_sensitive'@'%' = 'xyzuvw';") - tk.MustQuery("select query_sample_text from `information_schema`.`STATEMENTS_SUMMARY` " + - "where query_sample_text like '%user_sensitive%' and " + - "(query_sample_text like 'set password%' or query_sample_text like 'create user%' or query_sample_text like 'alter user%') " + - "order by query_sample_text;"). - Check(testkit.Rows( - "alter user {user_sensitive@% password = ***}", - "create user {user_sensitive@% password = ***}", - "set password for user user_sensitive@%", - )) -} - -func TestStmtSummaryTableOther(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("set global tidb_enable_stmt_summary=0") - tk.MustExec("set global tidb_enable_stmt_summary=1") - // set stmt size to 1 - // first sql - tk.MustExec("set global tidb_stmt_summary_max_stmt_count=1") - defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count=100") - // second sql, evict first sql from stmt_summary - tk.MustExec("show databases;") - // third sql, evict second sql from stmt_summary - tk.MustQuery("SELECT DIGEST_TEXT, DIGEST FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY`;"). - Check(testkit.Rows( - // digest in cache - // "show databases ;" - "show databases 0e247706bf6e791fbf4af8c8e7658af5ffc45c63179871202d8f91551ee03161", - // digest evicted - " ", - )) - // forth sql, evict third sql from stmt_summary - tk.MustQuery("SELECT SCHEMA_NAME FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY`;"). - Check(testkit.Rows( - // digest in cache - "test", // select xx from yy; - // digest evicted - "", - )) -} - -func TestStmtSummaryHistoryTableOther(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - - tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 1") - defer tk.MustExec("set global tidb_stmt_summary_max_stmt_count = 100") - - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - // first sql - tk.MustExec("set global tidb_stmt_summary_max_stmt_count=1") - // second sql, evict first sql from stmt_summary - tk.MustExec("show databases;") - // third sql, evict second sql from stmt_summary - tk.MustQuery("SELECT DIGEST_TEXT, DIGEST FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY_HISTORY`;"). - Check(testkit.Rows( - // digest in cache - // "show databases ;" - "show databases 0e247706bf6e791fbf4af8c8e7658af5ffc45c63179871202d8f91551ee03161", - // digest evicted - " ", - )) - // forth sql, evict third sql from stmt_summary - tk.MustQuery("SELECT SCHEMA_NAME FROM `INFORMATION_SCHEMA`.`STATEMENTS_SUMMARY_HISTORY`;"). - Check(testkit.Rows( - // digest in cache - "test", // select xx from yy; - // digest evicted - "", - )) -} - -func TestPerformanceSchemaforNonPrepPlanCache(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tk := newTestKitWithRoot(t, store) - tk.MustExec(`use test`) - tk.MustExec(`create table t (a int, key(a))`) - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=1`) - - tk.MustExec(`select * from t where a=1`) - tk.MustExec(`select * from t where a=1`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) - - tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + - "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( - "2 select * from `t` where `a` = ? 0 1 1 select * from t where a=1")) - - tk.MustExec(`select * from t where a=2`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) - tk.MustExec(`select * from t where a=3`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("1")) - - // exec_count 2->4, plan_cache_hits 1->3 - tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + - "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( - "4 select * from `t` where `a` = ? 0 1 3 select * from t where a=1")) - - tk.MustExec(`set tidb_enable_non_prepared_plan_cache=0`) - tk.MustExec(`select * from t where a=2`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) - tk.MustExec(`select * from t where a=3`) - tk.MustQuery(`select @@last_plan_from_cache`).Check(testkit.Rows("0")) - - // exec_count 4->6, plan_cache_hits 3->3 - tk.MustQuery("select exec_count, digest_text, prepared, plan_in_cache, plan_cache_hits, query_sample_text " + - "from information_schema.statements_summary where digest_text='select * from `t` where `a` = ?'").Check(testkit.Rows( - "6 select * from `t` where `a` = ? 0 0 3 select * from t where a=1")) -} - -func TestPerformanceSchemaforPlanCache(t *testing.T) { - setupStmtSummary() - defer closeStmtSummary() - - store := testkit.CreateMockStore(t) - tmp := testkit.NewTestKit(t, store) - tmp.MustExec("set tidb_enable_prepared_plan_cache=ON") - tk := newTestKitWithPlanCache(t, store) - - // Clear summaries. - tk.MustExec("set global tidb_enable_stmt_summary = 0") - tk.MustExec("set global tidb_enable_stmt_summary = 1") - tk.MustExec("use test") - tk.MustExec("drop table if exists t") - tk.MustExec("create table t(a int)") - tk.MustExec("prepare stmt from 'select * from t'") - tk.MustExec("execute stmt") - tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check( - testkit.Rows("0 0")) - tk.MustExec("execute stmt") - tk.MustExec("execute stmt") - tk.MustExec("execute stmt") - tk.MustQuery("select plan_cache_hits, plan_in_cache from information_schema.statements_summary where digest_text='select * from `t`'").Check( - testkit.Rows("3 1")) -} - -func newTestKit(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := testkit.NewTestKit(t, store) - tk.MustExec("use test") - return tk -} - -func newTestKitWithRoot(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := newTestKit(t, store) - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - return tk -} - -func newTestKitWithPlanCache(t *testing.T, store kv.Storage) *testkit.TestKit { - tk := testkit.NewTestKit(t, store) - se, err := session.CreateSession4TestWithOpt(store, &session.Opt{ - PreparedPlanCache: plannercore.NewLRUPlanCache(100, 0.1, math.MaxUint64, tk.Session(), false), - }) - require.NoError(t, err) - tk.SetSession(se) - tk.RefreshConnectionID() - require.NoError(t, tk.Session().Auth(&auth.UserIdentity{Username: "root", Hostname: "%"}, nil, nil, nil)) - return tk -} - -func setupStmtSummary() { - stmtsummaryv2.Setup(&stmtsummaryv2.Config{ - Filename: "tidb-statements.log", - }) - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = true - }) -} - -func closeStmtSummary() { - config.UpdateGlobal(func(conf *config.Config) { - conf.Instance.StmtSummaryEnablePersistent = false - }) - stmtsummaryv2.GlobalStmtSummary.Close() - stmtsummaryv2.GlobalStmtSummary = nil - _ = os.Remove(config.GetGlobalConfig().Instance.StmtSummaryFilename) -} diff --git a/util/stringutil/BUILD.bazel b/util/stringutil/BUILD.bazel deleted file mode 100644 index 7ce843bcb265e..0000000000000 --- a/util/stringutil/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "stringutil", - srcs = ["string_util.go"], - importpath = "github.com/pingcap/tidb/util/stringutil", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//util/hack", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "stringutil_test", - timeout = "short", - srcs = [ - "main_test.go", - "string_util_test.go", - ], - embed = [":stringutil"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/stringutil/main_test.go b/util/stringutil/main_test.go deleted file mode 100644 index 8c221bfbc060c..0000000000000 --- a/util/stringutil/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package stringutil - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/syncutil/BUILD.bazel b/util/syncutil/BUILD.bazel deleted file mode 100644 index 7703cfd35f89b..0000000000000 --- a/util/syncutil/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "syncutil", - srcs = [ - "mutex_deadlock.go", #keep - "mutex_sync.go", - ], - importpath = "github.com/pingcap/tidb/util/syncutil", - visibility = ["//visibility:public"], - deps = ["@com_github_sasha_s_go_deadlock//:go-deadlock"], #keep -) diff --git a/util/sys/linux/BUILD.bazel b/util/sys/linux/BUILD.bazel deleted file mode 100644 index f1363c2cb0d71..0000000000000 --- a/util/sys/linux/BUILD.bazel +++ /dev/null @@ -1,70 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "linux", - srcs = [ - "sys_linux.go", - "sys_other.go", - "sys_windows.go", - ], - importpath = "github.com/pingcap/tidb/util/sys/linux", - visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:aix": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:android": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:darwin": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:dragonfly": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:freebsd": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:illumos": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:ios": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:js": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:linux": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:netbsd": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:openbsd": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:plan9": [ - "@org_golang_x_sys//unix", - ], - "@io_bazel_rules_go//go/platform:solaris": [ - "@org_golang_x_sys//unix", - ], - "//conditions:default": [], - }), -) - -go_test( - name = "linux_test", - timeout = "short", - srcs = [ - "main_test.go", - "sys_test.go", - ], - flaky = True, - deps = [ - ":linux", - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/sys/linux/main_test.go b/util/sys/linux/main_test.go deleted file mode 100644 index ad4b3abef844d..0000000000000 --- a/util/sys/linux/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package linux_test - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/sys/linux/sys_test.go b/util/sys/linux/sys_test.go deleted file mode 100644 index 675e83db02c26..0000000000000 --- a/util/sys/linux/sys_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019 PingCAP, Inc. -// -// 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. -package linux_test - -import ( - "testing" - - . "github.com/pingcap/tidb/util/sys/linux" - "github.com/stretchr/testify/require" -) - -func TestGetOSVersion(t *testing.T) { - osRelease, err := OSVersion() - require.NoError(t, err) - require.NotEmpty(t, osRelease) -} diff --git a/util/sys/storage/BUILD.bazel b/util/sys/storage/BUILD.bazel deleted file mode 100644 index 5c5613d532eb8..0000000000000 --- a/util/sys/storage/BUILD.bazel +++ /dev/null @@ -1,34 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "storage", - srcs = [ - "sys_other.go", - "sys_posix.go", - "sys_windows.go", - ], - importpath = "github.com/pingcap/tidb/util/sys/storage", - visibility = ["//visibility:public"], - deps = select({ - "@io_bazel_rules_go//go/platform:windows": [ - "@org_golang_x_sys//windows", - ], - "//conditions:default": [], - }), -) - -go_test( - name = "storage_test", - timeout = "short", - srcs = [ - "main_test.go", - "sys_test.go", - ], - embed = [":storage"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/sys/storage/main_test.go b/util/sys/storage/main_test.go deleted file mode 100644 index e84a868f977e8..0000000000000 --- a/util/sys/storage/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package storage - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/sys/storage/sys_test.go b/util/sys/storage/sys_test.go deleted file mode 100644 index 0f8c39ce3c50c..0000000000000 --- a/util/sys/storage/sys_test.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2020 PingCAP, Inc. -// -// 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. - -package storage_test - -import ( - "testing" - - "github.com/pingcap/tidb/util/sys/storage" - "github.com/stretchr/testify/require" -) - -func TestGetTargetDirectoryCapacity(t *testing.T) { - r, err := storage.GetTargetDirectoryCapacity(".") - require.NoError(t, err) - require.GreaterOrEqual(t, r, uint64(1), "couldn't get capacity") - - //TODO: check the value of r with `df` in linux -} diff --git a/util/systimemon/BUILD.bazel b/util/systimemon/BUILD.bazel deleted file mode 100644 index 92a2b04db7050..0000000000000 --- a/util/systimemon/BUILD.bazel +++ /dev/null @@ -1,29 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "systimemon", - srcs = ["systime_mon.go"], - importpath = "github.com/pingcap/tidb/util/systimemon", - visibility = ["//visibility:public"], - deps = [ - "//util/logutil", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "systimemon_test", - timeout = "short", - srcs = [ - "main_test.go", - "systime_mon_test.go", - ], - embed = [":systimemon"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/systimemon/main_test.go b/util/systimemon/main_test.go deleted file mode 100644 index 4a680afbae61d..0000000000000 --- a/util/systimemon/main_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package systimemon - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("github.com/pingcap/tidb/util/systimemon.StartMonitor"), - } - - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/table-filter/BUILD.bazel b/util/table-filter/BUILD.bazel deleted file mode 100644 index 7181e79254c93..0000000000000 --- a/util/table-filter/BUILD.bazel +++ /dev/null @@ -1,30 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "table-filter", - srcs = [ - "column_filter.go", - "compat.go", - "matchers.go", - "parser.go", - "table_filter.go", - ], - importpath = "github.com/pingcap/tidb/util/table-filter", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "table-filter_test", - timeout = "short", - srcs = [ - "column_filter_test.go", - "compat_test.go", - "table_filter_test.go", - ], - flaky = True, - deps = [ - ":table-filter", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/table-router/BUILD.bazel b/util/table-router/BUILD.bazel deleted file mode 100644 index 77adca60bc89f..0000000000000 --- a/util/table-router/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "table-router", - srcs = ["router.go"], - importpath = "github.com/pingcap/tidb/util/table-router", - visibility = ["//visibility:public"], - deps = [ - "//util/table-rule-selector", - "@com_github_pingcap_errors//:errors", - ], -) - -go_test( - name = "table-router_test", - timeout = "short", - srcs = ["router_test.go"], - embed = [":table-router"], - flaky = True, - deps = [ - "//util/table-rule-selector", - "@com_github_stretchr_testify//require", - ], -) diff --git a/util/table-rule-selector/BUILD.bazel b/util/table-rule-selector/BUILD.bazel deleted file mode 100644 index d1f59e9bac75c..0000000000000 --- a/util/table-rule-selector/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "table-rule-selector", - srcs = ["trie_selector.go"], - importpath = "github.com/pingcap/tidb/util/table-rule-selector", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "table-rule-selector_test", - timeout = "short", - srcs = ["selector_test.go"], - embed = [":table-rule-selector"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/tableutil/BUILD.bazel b/util/tableutil/BUILD.bazel deleted file mode 100644 index 2a84448dba6f7..0000000000000 --- a/util/tableutil/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tableutil", - srcs = ["tableutil.go"], - importpath = "github.com/pingcap/tidb/util/tableutil", - visibility = ["//visibility:public"], - deps = [ - "//meta/autoid", - "//parser/model", - ], -) diff --git a/util/texttree/BUILD.bazel b/util/texttree/BUILD.bazel deleted file mode 100644 index 47293bb45baf8..0000000000000 --- a/util/texttree/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "texttree", - srcs = ["texttree.go"], - importpath = "github.com/pingcap/tidb/util/texttree", - visibility = ["//visibility:public"], -) - -go_test( - name = "texttree_test", - timeout = "short", - srcs = [ - "main_test.go", - "texttree_test.go", - ], - embed = [":texttree"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/texttree/main_test.go b/util/texttree/main_test.go deleted file mode 100644 index 6d79442706a0d..0000000000000 --- a/util/texttree/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package texttree - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/tiflash/BUILD.bazel b/util/tiflash/BUILD.bazel deleted file mode 100644 index abede5d4ca2d1..0000000000000 --- a/util/tiflash/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tiflash", - srcs = ["tiflash_replica_read.go"], - importpath = "github.com/pingcap/tidb/util/tiflash", - visibility = ["//visibility:public"], -) diff --git a/util/tiflashcompute/BUILD.bazel b/util/tiflashcompute/BUILD.bazel deleted file mode 100644 index b9d43f5f530d4..0000000000000 --- a/util/tiflashcompute/BUILD.bazel +++ /dev/null @@ -1,18 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tiflashcompute", - srcs = [ - "dispatch_policy.go", - "topo_fetcher.go", - ], - importpath = "github.com/pingcap/tidb/util/tiflashcompute", - visibility = ["//visibility:public"], - deps = [ - "//errno", - "//util/dbterror", - "//util/logutil", - "@com_github_pingcap_errors//:errors", - "@org_uber_go_zap//:zap", - ], -) diff --git a/util/tikvutil/BUILD.bazel b/util/tikvutil/BUILD.bazel deleted file mode 100644 index 3c70c340eddc6..0000000000000 --- a/util/tikvutil/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tikvutil", - srcs = ["tikvutil.go"], - importpath = "github.com/pingcap/tidb/util/tikvutil", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_atomic//:atomic"], -) diff --git a/util/timeutil/BUILD.bazel b/util/timeutil/BUILD.bazel deleted file mode 100644 index f018c4ff812e6..0000000000000 --- a/util/timeutil/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "timeutil", - srcs = [ - "errors.go", - "time.go", - "time_zone.go", - ], - importpath = "github.com/pingcap/tidb/util/timeutil", - visibility = ["//visibility:public"], - deps = [ - "//parser/mysql", - "//types", - "//util/dbterror", - "//util/logutil", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "timeutil_test", - timeout = "short", - srcs = [ - "main_test.go", - "time_test.go", - "time_zone_test.go", - ], - embed = [":timeutil"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/timeutil/errors.go b/util/timeutil/errors.go deleted file mode 100644 index 01287bd283950..0000000000000 --- a/util/timeutil/errors.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package timeutil - -import ( - "github.com/pingcap/tidb/parser/mysql" - "github.com/pingcap/tidb/util/dbterror" -) - -// ErrUnknownTimeZone indicates timezone is unknown. -var ErrUnknownTimeZone = dbterror.ClassVariable.NewStd(mysql.ErrUnknownTimeZone) diff --git a/util/timeutil/main_test.go b/util/timeutil/main_test.go deleted file mode 100644 index e903b59b1d5a7..0000000000000 --- a/util/timeutil/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package timeutil - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/tls/BUILD.bazel b/util/tls/BUILD.bazel deleted file mode 100644 index af169cd086d17..0000000000000 --- a/util/tls/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "tls", - srcs = ["tls.go"], - importpath = "github.com/pingcap/tidb/util/tls", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_atomic//:atomic"], -) diff --git a/util/topsql/BUILD.bazel b/util/topsql/BUILD.bazel deleted file mode 100644 index 9cfac0291454c..0000000000000 --- a/util/topsql/BUILD.bazel +++ /dev/null @@ -1,49 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "topsql", - srcs = ["topsql.go"], - importpath = "github.com/pingcap/tidb/util/topsql", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//util/logutil", - "//util/plancodec", - "//util/topsql/collector", - "//util/topsql/reporter", - "//util/topsql/stmtstats", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "topsql_test", - timeout = "short", - srcs = [ - "main_test.go", - "topsql_test.go", - ], - embed = [":topsql"], - flaky = True, - deps = [ - "//config", - "//parser", - "//testkit/testsetup", - "//util", - "//util/cpuprofile", - "//util/topsql/collector", - "//util/topsql/collector/mock", - "//util/topsql/reporter", - "//util/topsql/reporter/mock", - "//util/topsql/state", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//require", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//credentials/insecure", - "@org_golang_google_grpc//keepalive", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/topsql/collector/BUILD.bazel b/util/topsql/collector/BUILD.bazel deleted file mode 100644 index 0bb7e073a9d4b..0000000000000 --- a/util/topsql/collector/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "collector", - srcs = ["cpu.go"], - importpath = "github.com/pingcap/tidb/util/topsql/collector", - visibility = ["//visibility:public"], - deps = [ - "//util", - "//util/cpuprofile", - "//util/hack", - "//util/logutil", - "//util/topsql/state", - "@com_github_google_pprof//profile", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "collector_test", - timeout = "short", - srcs = ["main_test.go"], - embed = [":collector"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/cpuprofile", - "//util/cpuprofile/testutil", - "//util/topsql/state", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/topsql/collector/cpu.go b/util/topsql/collector/cpu.go deleted file mode 100644 index fbd7fc2d9c025..0000000000000 --- a/util/topsql/collector/cpu.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package collector - -import ( - "context" - "runtime/pprof" - "sync" - "time" - - "github.com/google/pprof/profile" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "go.uber.org/zap" -) - -const ( - labelSQLDigest = "sql_digest" - labelPlanDigest = "plan_digest" -) - -// Collector uses to collect SQL execution cpu time. -type Collector interface { - // Collect uses to collect the SQL execution cpu time. - Collect(stats []SQLCPUTimeRecord) -} - -// SQLCPUTimeRecord represents a single record of how much cpu time a sql plan consumes in one second. -// -// PlanDigest can be empty, because: -// 1. some sql statements has no plan, like `COMMIT` -// 2. when a sql statement is being compiled, there's no plan yet -type SQLCPUTimeRecord struct { - SQLDigest []byte - PlanDigest []byte - CPUTimeMs uint32 -} - -// SQLCPUCollector uses to consume cpu profile from globalCPUProfiler, then parse the SQL CPU usage from the cpu profile data. -// It is not thread-safe, should only be used in one goroutine. -type SQLCPUCollector struct { - ctx context.Context - collector Collector - cancel context.CancelFunc - wg sync.WaitGroup - started bool - registered bool -} - -// NewSQLCPUCollector create a SQLCPUCollector. -func NewSQLCPUCollector(c Collector) *SQLCPUCollector { - return &SQLCPUCollector{ - collector: c, - } -} - -// Start uses to start to run SQLCPUCollector. -// This will register a consumer into globalCPUProfiler, then SQLCPUCollector will receive cpu profile data per seconds. -// WARN: this function is not thread-safe. -func (sp *SQLCPUCollector) Start() { - if sp.started { - return - } - sp.started = true - sp.ctx, sp.cancel = context.WithCancel(context.Background()) - sp.wg.Add(1) - go sp.collectSQLCPULoop() - logutil.BgLogger().Info("sql cpu collector started") -} - -// Stop uses to stop the SQLCPUCollector. -// WARN: this function is not thread-safe. -func (sp *SQLCPUCollector) Stop() { - if !sp.started { - return - } - sp.started = false - if sp.cancel != nil { - sp.cancel() - } - - sp.wg.Wait() - logutil.BgLogger().Info("sql cpu collector stopped") -} - -var defCollectTickerInterval = time.Second - -func (sp *SQLCPUCollector) collectSQLCPULoop() { - profileConsumer := make(cpuprofile.ProfileConsumer, 1) - ticker := time.NewTicker(defCollectTickerInterval) - defer func() { - sp.wg.Done() - sp.doUnregister(profileConsumer) - ticker.Stop() - }() - defer util.Recover("top-sql", "startAnalyzeProfileWorker", nil, false) - - for { - if topsqlstate.TopSQLEnabled() { - sp.doRegister(profileConsumer) - } else { - sp.doUnregister(profileConsumer) - } - - select { - case <-sp.ctx.Done(): - return - case <-ticker.C: - case data := <-profileConsumer: - sp.handleProfileData(data) - } - } -} - -func (sp *SQLCPUCollector) handleProfileData(data *cpuprofile.ProfileData) { - if data.Error != nil { - return - } - - p, err := profile.ParseData(data.Data.Bytes()) - if err != nil { - logutil.BgLogger().Error("parse profile error", zap.Error(err)) - return - } - stats := sp.parseCPUProfileBySQLLabels(p) - sp.collector.Collect(stats) -} - -func (sp *SQLCPUCollector) doRegister(profileConsumer cpuprofile.ProfileConsumer) { - if sp.registered { - return - } - sp.registered = true - cpuprofile.Register(profileConsumer) -} - -func (sp *SQLCPUCollector) doUnregister(profileConsumer cpuprofile.ProfileConsumer) { - if !sp.registered { - return - } - sp.registered = false - cpuprofile.Unregister(profileConsumer) -} - -// parseCPUProfileBySQLLabels uses to aggregate the cpu-profile sample data by sql_digest and plan_digest labels, -// output the TopSQLCPUTimeRecord slice. Want to know more information about profile labels, see https://rakyll.org/profiler-labels/ -// The sql_digest label is been set by `SetSQLLabels` function after parse the SQL. -// The plan_digest label is been set by `SetSQLAndPlanLabels` function after build the SQL plan. -// Since `SQLCPUCollector` only care about the cpu time that consume by (sql_digest,plan_digest), the other sample data -// without those label will be ignore. -func (sp *SQLCPUCollector) parseCPUProfileBySQLLabels(p *profile.Profile) []SQLCPUTimeRecord { - sqlMap := make(map[string]*sqlStats) - idx := len(p.SampleType) - 1 - for _, s := range p.Sample { - digests, ok := s.Label[labelSQLDigest] - if !ok || len(digests) == 0 { - continue - } - for _, digest := range digests { - stmt, ok := sqlMap[digest] - if !ok { - stmt = &sqlStats{ - plans: make(map[string]int64), - total: 0, - } - sqlMap[digest] = stmt - } - stmt.total += s.Value[idx] - - plans := s.Label[labelPlanDigest] - for _, plan := range plans { - stmt.plans[plan] += s.Value[idx] - } - } - } - return sp.createSQLStats(sqlMap) -} - -func (*SQLCPUCollector) createSQLStats(sqlMap map[string]*sqlStats) []SQLCPUTimeRecord { - stats := make([]SQLCPUTimeRecord, 0, len(sqlMap)) - for sqlDigest, stmt := range sqlMap { - stmt.tune() - for planDigest, val := range stmt.plans { - stats = append(stats, SQLCPUTimeRecord{ - SQLDigest: []byte(sqlDigest), - PlanDigest: []byte(planDigest), - CPUTimeMs: uint32(time.Duration(val).Milliseconds()), - }) - } - } - return stats -} - -type sqlStats struct { - plans map[string]int64 - total int64 -} - -// tune use to adjust sql stats. Consider following situation: -// The `sqlStats` maybe: -// -// plans: { -// "table_scan": 200ms, // The cpu time of the sql that plan with `table_scan` is 200ms. -// "index_scan": 300ms, // The cpu time of the sql that plan with `index_scan` is 300ms. -// }, -// total: 600ms, // The total cpu time of the sql is 600ms. -// -// total_time - table_scan_time - index_scan_time = 100ms, and this 100ms means those sample data only contain the -// sql_digest label, doesn't contain the plan_digest label. This is cause by the `pprof profile` is base on sample, -// and the plan digest can only be set after optimizer generated execution plan. So the remain 100ms means the plan -// optimizer takes time to generated plan. -// After this tune function, the `sqlStats` become to: -// -// plans: { -// "" : 100ms, // 600 - 200 - 300 = 100ms, indicate the optimizer generated plan time cost. -// "table_scan": 200ms, -// "index_scan": 300ms, -// }, -// total: 600ms, -func (s *sqlStats) tune() { - if len(s.plans) == 0 { - s.plans[""] = s.total - return - } - if len(s.plans) == 1 { - for k := range s.plans { - s.plans[k] = s.total - return - } - } - planTotal := int64(0) - for _, v := range s.plans { - planTotal += v - } - optimize := s.total - planTotal - if optimize <= 0 { - return - } - s.plans[""] += optimize -} - -// CtxWithSQLDigest wrap the ctx with sql digest. -func CtxWithSQLDigest(ctx context.Context, sqlDigest []byte) context.Context { - return pprof.WithLabels(ctx, pprof.Labels(labelSQLDigest, string(hack.String(sqlDigest)))) -} - -// CtxWithSQLAndPlanDigest wrap the ctx with sql digest and plan digest. -func CtxWithSQLAndPlanDigest(ctx context.Context, sqlDigest, planDigest []byte) context.Context { - return pprof.WithLabels(ctx, pprof.Labels(labelSQLDigest, string(hack.String(sqlDigest)), - labelPlanDigest, string(hack.String(planDigest)))) -} diff --git a/util/topsql/collector/main_test.go b/util/topsql/collector/main_test.go deleted file mode 100644 index 6a4f187350c64..0000000000000 --- a/util/topsql/collector/main_test.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package collector - -import ( - "context" - "testing" - "time" - - "github.com/pingcap/tidb/testkit/testsetup" - "github.com/pingcap/tidb/util/cpuprofile" - "github.com/pingcap/tidb/util/cpuprofile/testutil" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} - -func TestPProfCPUProfile(t *testing.T) { - // short the interval to speed up the test. - interval := time.Millisecond * 400 - defCollectTickerInterval = interval - cpuprofile.DefProfileDuration = interval - - err := cpuprofile.StartCPUProfiler() - require.NoError(t, err) - defer cpuprofile.StopCPUProfiler() - - topsqlstate.EnableTopSQL() - mc := &mockCollector{ - dataCh: make(chan []SQLCPUTimeRecord, 10), - } - sqlCPUCollector := NewSQLCPUCollector(mc) - sqlCPUCollector.Start() - defer sqlCPUCollector.Stop() - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - testutil.MockCPULoad(ctx, "sql", "sql_digest", "plan_digest") - - data := <-mc.dataCh - require.True(t, len(data) > 0) - require.Equal(t, []byte("sql_digest value"), data[0].SQLDigest) - - // Test disable then re-enable. - topsqlstate.DisableTopSQL() - time.Sleep(interval * 2) - dataChLen := len(mc.dataCh) - deltaLen := 0 - topsqlstate.EnableTopSQL() - for i := 0; i < 10; i++ { - t1 := time.Now() - data = <-mc.dataCh - require.True(t, time.Since(t1) < interval*4) - if len(data) > 0 { - deltaLen++ - if deltaLen > dataChLen { - // Here we can ensure that we receive new data after "re-enable". - break - } - } - } - require.True(t, len(data) > 0) - require.True(t, deltaLen > dataChLen) - require.Equal(t, []byte("sql_digest value"), data[0].SQLDigest) -} - -func TestSQLStatsTune(t *testing.T) { - s := &sqlStats{plans: map[string]int64{"plan-1": 80}, total: 100} - s.tune() - require.Equal(t, int64(100), s.total) - require.Equal(t, int64(100), s.plans["plan-1"]) - - s = &sqlStats{plans: map[string]int64{"plan-1": 30, "plan-2": 30}, total: 100} - s.tune() - require.Equal(t, int64(100), s.total) - require.Equal(t, int64(30), s.plans["plan-1"]) - require.Equal(t, int64(30), s.plans["plan-2"]) - require.Equal(t, int64(40), s.plans[""]) -} - -type mockCollector struct { - dataCh chan []SQLCPUTimeRecord -} - -// Collect implements the Collector interface. -func (c *mockCollector) Collect(records []SQLCPUTimeRecord) { - c.dataCh <- records -} diff --git a/util/topsql/collector/mock/BUILD.bazel b/util/topsql/collector/mock/BUILD.bazel deleted file mode 100644 index 6a4a32dfcad0d..0000000000000 --- a/util/topsql/collector/mock/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mock", - srcs = ["mock.go"], - importpath = "github.com/pingcap/tidb/util/topsql/collector/mock", - visibility = ["//visibility:public"], - deps = [ - "//parser", - "//util/hack", - "//util/logutil", - "//util/topsql/collector", - "//util/topsql/stmtstats", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) diff --git a/util/topsql/collector/mock/mock.go b/util/topsql/collector/mock/mock.go deleted file mode 100644 index d2a68703a83d9..0000000000000 --- a/util/topsql/collector/mock/mock.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "bytes" - "sync" - "time" - - "github.com/pingcap/tidb/parser" - "github.com/pingcap/tidb/util/hack" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/topsql/collector" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "go.uber.org/atomic" - "go.uber.org/zap" -) - -// TopSQLCollector uses for testing. -type TopSQLCollector struct { - // sql_digest -> normalized SQL - sqlMap map[string]string - // plan_digest -> normalized plan - planMap map[string]string - // (sql + plan_digest) -> sql stats - sqlStatsMap map[string]*collector.SQLCPUTimeRecord - collectCnt atomic.Int64 - sync.Mutex -} - -// NewTopSQLCollector uses for testing. -func NewTopSQLCollector() *TopSQLCollector { - return &TopSQLCollector{ - sqlMap: make(map[string]string), - planMap: make(map[string]string), - sqlStatsMap: make(map[string]*collector.SQLCPUTimeRecord), - } -} - -// Start implements TopSQLReporter interface. -func (*TopSQLCollector) Start() {} - -// Collect uses for testing. -func (c *TopSQLCollector) Collect(stats []collector.SQLCPUTimeRecord) { - defer c.collectCnt.Inc() - if len(stats) == 0 { - return - } - c.Lock() - defer c.Unlock() - for _, stmt := range stats { - hash := c.hash(stmt) - stats, ok := c.sqlStatsMap[hash] - if !ok { - stats = &collector.SQLCPUTimeRecord{ - SQLDigest: stmt.SQLDigest, - PlanDigest: stmt.PlanDigest, - } - c.sqlStatsMap[hash] = stats - } - stats.CPUTimeMs += stmt.CPUTimeMs - logutil.BgLogger().Info("mock top sql collector collected sql", - zap.String("sql", c.sqlMap[string(stmt.SQLDigest)]), - zap.Bool("has-plan", len(c.planMap[string(stmt.PlanDigest)]) > 0)) - } -} - -// CollectStmtStatsMap implements stmtstats.Collector. -func (*TopSQLCollector) CollectStmtStatsMap(_ stmtstats.StatementStatsMap) {} - -// GetSQLStatsBySQLWithRetry uses for testing. -func (c *TopSQLCollector) GetSQLStatsBySQLWithRetry(sql string, planIsNotNull bool) []*collector.SQLCPUTimeRecord { - after := time.After(time.Second * 10) - for { - select { - case <-after: - return nil - default: - } - stats := c.GetSQLStatsBySQL(sql, planIsNotNull) - if len(stats) > 0 { - return stats - } - c.WaitCollectCnt(1) - } -} - -// GetSQLStatsBySQL uses for testing. -func (c *TopSQLCollector) GetSQLStatsBySQL(sql string, planIsNotNull bool) []*collector.SQLCPUTimeRecord { - stats := make([]*collector.SQLCPUTimeRecord, 0, 2) - sqlDigest := GenSQLDigest(sql) - c.Lock() - for _, stmt := range c.sqlStatsMap { - if bytes.Equal(stmt.SQLDigest, sqlDigest.Bytes()) { - if planIsNotNull { - plan := c.planMap[string(stmt.PlanDigest)] - if len(plan) > 0 { - stats = append(stats, stmt) - } - } else { - stats = append(stats, stmt) - } - } - } - c.Unlock() - return stats -} - -// GetSQLCPUTimeBySQL uses for testing. -func (c *TopSQLCollector) GetSQLCPUTimeBySQL(sql string) uint32 { - sqlDigest := GenSQLDigest(sql) - cpuTime := uint32(0) - c.Lock() - for _, stmt := range c.sqlStatsMap { - if bytes.Equal(stmt.SQLDigest, sqlDigest.Bytes()) { - cpuTime += stmt.CPUTimeMs - } - } - c.Unlock() - return cpuTime -} - -// GetSQL uses for testing. -func (c *TopSQLCollector) GetSQL(sqlDigest []byte) string { - c.Lock() - sql := c.sqlMap[string(sqlDigest)] - c.Unlock() - return sql -} - -// GetPlan uses for testing. -func (c *TopSQLCollector) GetPlan(planDigest []byte) string { - c.Lock() - plan := c.planMap[string(planDigest)] - c.Unlock() - return plan -} - -// RegisterSQL uses for testing. -func (c *TopSQLCollector) RegisterSQL(sqlDigest []byte, normalizedSQL string, _ bool) { - digestStr := string(hack.String(sqlDigest)) - c.Lock() - _, ok := c.sqlMap[digestStr] - if !ok { - c.sqlMap[digestStr] = normalizedSQL - } - c.Unlock() -} - -// RegisterPlan uses for testing. -func (c *TopSQLCollector) RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) { - if isLarge { - return - } - - digestStr := string(hack.String(planDigest)) - c.Lock() - _, ok := c.planMap[digestStr] - if !ok { - c.planMap[digestStr] = normalizedPlan - } - c.Unlock() -} - -// WaitCollectCnt uses for testing. -func (c *TopSQLCollector) WaitCollectCnt(count int64) { - timeout := time.After(time.Second * 10) - end := c.collectCnt.Load() + count - for { - // Wait for reporter to collect sql stats count >= expected count - if c.collectCnt.Load() >= end { - return - } - select { - case <-timeout: - return - default: - time.Sleep(time.Millisecond * 10) - } - } -} - -// Reset cleans all collected data. -func (c *TopSQLCollector) Reset() { - c.Lock() - defer c.Unlock() - c.sqlMap = make(map[string]string) - c.planMap = make(map[string]string) - c.sqlStatsMap = make(map[string]*collector.SQLCPUTimeRecord) - c.collectCnt.Store(0) -} - -// CollectCnt uses for testing. -func (c *TopSQLCollector) CollectCnt() int64 { - return c.collectCnt.Load() -} - -// Close implements the interface. -func (*TopSQLCollector) Close() {} - -func (*TopSQLCollector) hash(stat collector.SQLCPUTimeRecord) string { - return string(stat.SQLDigest) + string(stat.PlanDigest) -} - -// GenSQLDigest uses for testing. -func GenSQLDigest(sql string) *parser.Digest { - _, digest := parser.NormalizeDigest(sql) - return digest -} diff --git a/util/topsql/main_test.go b/util/topsql/main_test.go deleted file mode 100644 index 5d16a6d64b443..0000000000000 --- a/util/topsql/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package topsql - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/topsql/reporter/BUILD.bazel b/util/topsql/reporter/BUILD.bazel deleted file mode 100644 index d910cc5f425cf..0000000000000 --- a/util/topsql/reporter/BUILD.bazel +++ /dev/null @@ -1,61 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "reporter", - srcs = [ - "datamodel.go", - "datasink.go", - "pubsub.go", - "reporter.go", - "single_target.go", - ], - importpath = "github.com/pingcap/tidb/util/topsql/reporter", - visibility = ["//visibility:public"], - deps = [ - "//config", - "//util", - "//util/hack", - "//util/logutil", - "//util/topsql/collector", - "//util/topsql/reporter/metrics", - "//util/topsql/state", - "//util/topsql/stmtstats", - "@com_github_pingcap_errors//:errors", - "@com_github_pingcap_failpoint//:failpoint", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_wangjohn_quickselect//:quickselect", - "@org_golang_google_grpc//:grpc", - "@org_golang_google_grpc//backoff", - "@org_golang_google_grpc//credentials/insecure", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_zap//:zap", - ], -) - -go_test( - name = "reporter_test", - timeout = "short", - srcs = [ - "datamodel_test.go", - "datasink_test.go", - "main_test.go", - "pubsub_test.go", - "reporter_test.go", - "single_target_test.go", - ], - embed = [":reporter"], - flaky = True, - deps = [ - "//config", - "//testkit/testsetup", - "//util/topsql/collector", - "//util/topsql/reporter/mock", - "//util/topsql/state", - "//util/topsql/stmtstats", - "@com_github_pingcap_tipb//go-tipb", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@org_golang_google_grpc//metadata", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/topsql/reporter/main_test.go b/util/topsql/reporter/main_test.go deleted file mode 100644 index 5789feb5ba341..0000000000000 --- a/util/topsql/reporter/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package reporter - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/topsql/reporter/metrics/BUILD.bazel b/util/topsql/reporter/metrics/BUILD.bazel deleted file mode 100644 index cb37cd3dcb3c4..0000000000000 --- a/util/topsql/reporter/metrics/BUILD.bazel +++ /dev/null @@ -1,12 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "metrics", - srcs = ["metrics.go"], - importpath = "github.com/pingcap/tidb/util/topsql/reporter/metrics", - visibility = ["//visibility:public"], - deps = [ - "//metrics", - "@com_github_prometheus_client_golang//prometheus", - ], -) diff --git a/util/topsql/reporter/metrics/metrics.go b/util/topsql/reporter/metrics/metrics.go deleted file mode 100644 index 325b2827946c4..0000000000000 --- a/util/topsql/reporter/metrics/metrics.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package reporter - -import ( - "github.com/pingcap/tidb/metrics" - "github.com/prometheus/client_golang/prometheus" -) - -// reporter metrics vars -var ( - IgnoreExceedSQLCounter prometheus.Counter - IgnoreExceedPlanCounter prometheus.Counter - IgnoreCollectChannelFullCounter prometheus.Counter - IgnoreCollectStmtChannelFullCounter prometheus.Counter - IgnoreReportChannelFullCounter prometheus.Counter - ReportAllDurationSuccHistogram prometheus.Observer - ReportAllDurationFailedHistogram prometheus.Observer - ReportRecordDurationSuccHistogram prometheus.Observer - ReportRecordDurationFailedHistogram prometheus.Observer - ReportSQLDurationSuccHistogram prometheus.Observer - ReportSQLDurationFailedHistogram prometheus.Observer - ReportPlanDurationSuccHistogram prometheus.Observer - ReportPlanDurationFailedHistogram prometheus.Observer - TopSQLReportRecordCounterHistogram prometheus.Observer - TopSQLReportSQLCountHistogram prometheus.Observer - TopSQLReportPlanCountHistogram prometheus.Observer -) - -func init() { - InitMetricsVars() -} - -// InitMetricsVars init topsql reporter metrics vars. -func InitMetricsVars() { - IgnoreExceedSQLCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_exceed_sql") - IgnoreExceedPlanCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_exceed_plan") - IgnoreCollectChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_collect_channel_full") - IgnoreCollectStmtChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_collect_stmt_channel_full") - IgnoreReportChannelFullCounter = metrics.TopSQLIgnoredCounter.WithLabelValues("ignore_report_channel_full") - ReportAllDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("all", metrics.LblOK) - ReportAllDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("all", metrics.LblError) - ReportRecordDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("record", metrics.LblOK) - ReportRecordDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("record", metrics.LblError) - ReportSQLDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("sql", metrics.LblOK) - ReportSQLDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("sql", metrics.LblError) - ReportPlanDurationSuccHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("plan", metrics.LblOK) - ReportPlanDurationFailedHistogram = metrics.TopSQLReportDurationHistogram.WithLabelValues("plan", metrics.LblError) - TopSQLReportRecordCounterHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("record") - TopSQLReportSQLCountHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("sql") - TopSQLReportPlanCountHistogram = metrics.TopSQLReportDataHistogram.WithLabelValues("plan") -} diff --git a/util/topsql/reporter/mock/BUILD.bazel b/util/topsql/reporter/mock/BUILD.bazel deleted file mode 100644 index 87e37c60bffa5..0000000000000 --- a/util/topsql/reporter/mock/BUILD.bazel +++ /dev/null @@ -1,17 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "mock", - srcs = [ - "pubsub.go", - "server.go", - ], - importpath = "github.com/pingcap/tidb/util/topsql/reporter/mock", - visibility = ["//visibility:public"], - deps = [ - "//util/logutil", - "@com_github_pingcap_tipb//go-tipb", - "@org_golang_google_grpc//:grpc", - "@org_uber_go_zap//:zap", - ], -) diff --git a/util/topsql/reporter/mock/pubsub.go b/util/topsql/reporter/mock/pubsub.go deleted file mode 100644 index aebddfec42f57..0000000000000 --- a/util/topsql/reporter/mock/pubsub.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "fmt" - "net" - - "github.com/pingcap/tidb/util/logutil" - "go.uber.org/zap" - "google.golang.org/grpc" -) - -type mockPubSubServer struct { - listen net.Listener - grpcServer *grpc.Server - addr string -} - -// NewMockPubSubServer creates a mock publisher server. -func NewMockPubSubServer() (*mockPubSubServer, error) { - addr := "127.0.0.1:0" - lis, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - server := grpc.NewServer() - - return &mockPubSubServer{ - addr: fmt.Sprintf("127.0.0.1:%d", lis.Addr().(*net.TCPAddr).Port), - listen: lis, - grpcServer: server, - }, nil -} - -func (svr *mockPubSubServer) Serve() { - err := svr.grpcServer.Serve(svr.listen) - if err != nil { - logutil.BgLogger().Warn("mock pubsub server serve failed", zap.String("category", "top-sql"), zap.Error(err)) - } -} - -func (svr *mockPubSubServer) Server() *grpc.Server { - return svr.grpcServer -} - -func (svr *mockPubSubServer) Address() string { - return svr.addr -} - -func (svr *mockPubSubServer) Stop() { - if svr.grpcServer != nil { - svr.grpcServer.Stop() - } -} diff --git a/util/topsql/reporter/mock/server.go b/util/topsql/reporter/mock/server.go deleted file mode 100644 index b209f39a675f0..0000000000000 --- a/util/topsql/reporter/mock/server.go +++ /dev/null @@ -1,241 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package mock - -import ( - "fmt" - "io" - "net" - "sync" - "sync/atomic" - "time" - - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" - "google.golang.org/grpc" -) - -type mockAgentServer struct { - hang struct { - beginTime atomic.Pointer[time.Time] - endTime atomic.Pointer[time.Time] - } - grpcServer *grpc.Server - sqlMetas map[string]tipb.SQLMeta - planMetas map[string]string - addr string - records [][]*tipb.TopSQLRecord - sync.Mutex -} - -// StartMockAgentServer starts the mock agent server. -func StartMockAgentServer() (*mockAgentServer, error) { - addr := "127.0.0.1:0" - lis, err := net.Listen("tcp", addr) - if err != nil { - return nil, err - } - server := grpc.NewServer() - agentServer := &mockAgentServer{ - addr: fmt.Sprintf("127.0.0.1:%d", lis.Addr().(*net.TCPAddr).Port), - grpcServer: server, - sqlMetas: make(map[string]tipb.SQLMeta, 5000), - planMetas: make(map[string]string, 5000), - } - beginTime := time.Now() - endTime := time.Now() - agentServer.hang.beginTime.Store(&beginTime) - agentServer.hang.endTime.Store(&endTime) - tipb.RegisterTopSQLAgentServer(server, agentServer) - - go func() { - err := server.Serve(lis) - if err != nil { - logutil.BgLogger().Warn("mock agent server serve failed", zap.String("category", "top-sql"), zap.Error(err)) - } - }() - - return agentServer, nil -} - -func (svr *mockAgentServer) HangFromNow(duration time.Duration) { - now := time.Now() - svr.hang.beginTime.Store(&now) - endTime := now.Add(duration) - svr.hang.endTime.Store(&endTime) -} - -// mayHang will check the hanging period, and ensure to sleep through it -func (svr *mockAgentServer) mayHang() { - now := time.Now() - beginTime := svr.hang.beginTime.Load() - endTime := svr.hang.endTime.Load() - if now.Before(*endTime) && now.After(*beginTime) { - time.Sleep(endTime.Sub(now)) - } -} - -func (svr *mockAgentServer) ReportTopSQLRecords(stream tipb.TopSQLAgent_ReportTopSQLRecordsServer) error { - records := make([]*tipb.TopSQLRecord, 0, 10) - for { - svr.mayHang() - req, err := stream.Recv() - if err == io.EOF { - break - } else if err != nil { - return err - } - records = append(records, req) - } - svr.Lock() - svr.records = append(svr.records, records) - svr.Unlock() - return stream.SendAndClose(&tipb.EmptyResponse{}) -} - -func (svr *mockAgentServer) ReportSQLMeta(stream tipb.TopSQLAgent_ReportSQLMetaServer) error { - for { - svr.mayHang() - req, err := stream.Recv() - if err == io.EOF { - break - } else if err != nil { - return err - } - svr.Lock() - svr.sqlMetas[string(req.SqlDigest)] = *req - svr.Unlock() - } - return stream.SendAndClose(&tipb.EmptyResponse{}) -} - -func (svr *mockAgentServer) ReportPlanMeta(stream tipb.TopSQLAgent_ReportPlanMetaServer) error { - for { - svr.mayHang() - req, err := stream.Recv() - if err == io.EOF { - break - } else if err != nil { - return err - } - svr.Lock() - svr.planMetas[string(req.PlanDigest)] = req.NormalizedPlan - svr.Unlock() - } - return stream.SendAndClose(&tipb.EmptyResponse{}) -} - -func (svr *mockAgentServer) RecordsCnt() int { - svr.Lock() - defer svr.Unlock() - return len(svr.records) -} - -func (svr *mockAgentServer) SQLMetaCnt() int { - svr.Lock() - defer svr.Unlock() - return len(svr.sqlMetas) -} - -func (svr *mockAgentServer) WaitCollectCnt(old, cnt int, timeout time.Duration) { - start := time.Now() - for { - svr.Lock() - if len(svr.records)-old >= cnt { - svr.Unlock() - return - } - svr.Unlock() - if time.Since(start) > timeout { - return - } - time.Sleep(time.Millisecond) - } -} - -func (svr *mockAgentServer) WaitCollectCntOfSQLMeta(old, cnt int, timeout time.Duration) { - start := time.Now() - for { - svr.Lock() - if len(svr.sqlMetas)-old >= cnt { - svr.Unlock() - return - } - svr.Unlock() - if time.Since(start) > timeout { - return - } - time.Sleep(time.Millisecond) - } -} - -func (svr *mockAgentServer) GetSQLMetaByDigestBlocking(digest []byte, timeout time.Duration) (meta tipb.SQLMeta, exist bool) { - start := time.Now() - for { - svr.Lock() - sqlMeta, exist := svr.sqlMetas[string(digest)] - svr.Unlock() - if exist || time.Since(start) > timeout { - return sqlMeta, exist - } - time.Sleep(time.Millisecond) - } -} - -func (svr *mockAgentServer) GetPlanMetaByDigestBlocking(digest []byte, timeout time.Duration) (normalizedPlan string, exist bool) { - start := time.Now() - for { - svr.Lock() - normalizedPlan, exist = svr.planMetas[string(digest)] - svr.Unlock() - if exist || time.Since(start) > timeout { - return normalizedPlan, exist - } - time.Sleep(time.Millisecond) - } -} - -func (svr *mockAgentServer) GetLatestRecords() []*tipb.TopSQLRecord { - svr.Lock() - records := svr.records - svr.records = [][]*tipb.TopSQLRecord{} - svr.Unlock() - - if len(records) == 0 { - return nil - } - return records[len(records)-1] -} - -func (svr *mockAgentServer) GetTotalSQLMetas() []tipb.SQLMeta { - svr.Lock() - defer svr.Unlock() - metas := make([]tipb.SQLMeta, 0, len(svr.sqlMetas)) - for _, meta := range svr.sqlMetas { - metas = append(metas, meta) - } - return metas -} - -func (svr *mockAgentServer) Address() string { - return svr.addr -} - -func (svr *mockAgentServer) Stop() { - if svr.grpcServer != nil { - svr.grpcServer.Stop() - } -} diff --git a/util/topsql/reporter/pubsub.go b/util/topsql/reporter/pubsub.go deleted file mode 100644 index cdf93c7c2aa3a..0000000000000 --- a/util/topsql/reporter/pubsub.go +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package reporter - -import ( - "context" - "errors" - "time" - - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - reporter_metrics "github.com/pingcap/tidb/util/topsql/reporter/metrics" - "github.com/pingcap/tipb/go-tipb" - "go.uber.org/zap" -) - -// TopSQLPubSubService implements tipb.TopSQLPubSubServer. -// -// If a client subscribes to TopSQL records, the TopSQLPubSubService is responsible -// for registering an associated DataSink to the reporter. Then the DataSink sends -// data to the client periodically. -type TopSQLPubSubService struct { - dataSinkRegisterer DataSinkRegisterer -} - -// NewTopSQLPubSubService creates a new TopSQLPubSubService. -func NewTopSQLPubSubService(dataSinkRegisterer DataSinkRegisterer) *TopSQLPubSubService { - return &TopSQLPubSubService{dataSinkRegisterer: dataSinkRegisterer} -} - -var _ tipb.TopSQLPubSubServer = &TopSQLPubSubService{} - -// Subscribe registers dataSinks to the reporter and redirects data received from reporter -// to subscribers associated with those dataSinks. -func (ps *TopSQLPubSubService) Subscribe(_ *tipb.TopSQLSubRequest, stream tipb.TopSQLPubSub_SubscribeServer) error { - ds := newPubSubDataSink(stream, ps.dataSinkRegisterer) - if err := ps.dataSinkRegisterer.Register(ds); err != nil { - return err - } - return ds.run() -} - -type pubSubDataSink struct { - ctx context.Context - cancel context.CancelFunc - - stream tipb.TopSQLPubSub_SubscribeServer - sendTaskCh chan sendTask - - // for deregister - registerer DataSinkRegisterer -} - -func newPubSubDataSink(stream tipb.TopSQLPubSub_SubscribeServer, registerer DataSinkRegisterer) *pubSubDataSink { - ctx, cancel := context.WithCancel(stream.Context()) - - return &pubSubDataSink{ - ctx: ctx, - cancel: cancel, - - stream: stream, - sendTaskCh: make(chan sendTask, 1), - - registerer: registerer, - } -} - -var _ DataSink = &pubSubDataSink{} - -func (ds *pubSubDataSink) TrySend(data *ReportData, deadline time.Time) error { - select { - case ds.sendTaskCh <- sendTask{data: data, deadline: deadline}: - return nil - case <-ds.ctx.Done(): - return ds.ctx.Err() - default: - reporter_metrics.IgnoreReportChannelFullCounter.Inc() - return errors.New("the channel of pubsub dataSink is full") - } -} - -func (ds *pubSubDataSink) OnReporterClosing() { - ds.cancel() -} - -func (ds *pubSubDataSink) run() error { - defer func() { - ds.registerer.Deregister(ds) - ds.cancel() - }() - - for { - select { - case task := <-ds.sendTaskCh: - ctx, cancel := context.WithDeadline(ds.ctx, task.deadline) - var err error - - start := time.Now() - go util.WithRecovery(func() { - defer cancel() - err = ds.doSend(ctx, task.data) - - if err != nil { - reporter_metrics.ReportAllDurationFailedHistogram.Observe(time.Since(start).Seconds()) - } else { - reporter_metrics.ReportAllDurationSuccHistogram.Observe(time.Since(start).Seconds()) - } - }, nil) - - // When the deadline is exceeded, the closure inside `go util.WithRecovery` above may not notice that - // immediately because it can be blocked by `stream.Send`. - // In order to clean up resources as quickly as possible, we let that closure run in an individual goroutine, - // and wait for timeout here. - <-ctx.Done() - - if errors.Is(ctx.Err(), context.DeadlineExceeded) { - logutil.BgLogger().Warn( - "[top-sql] pubsub datasink failed to send data to subscriber due to deadline exceeded", - zap.Time("deadline", task.deadline), - ) - return ctx.Err() - } - - if err != nil { - logutil.BgLogger().Warn( - "[top-sql] pubsub datasink failed to send data to subscriber", - zap.Error(err), - ) - return err - } - case <-ds.ctx.Done(): - return ds.ctx.Err() - } - } -} - -func (ds *pubSubDataSink) doSend(ctx context.Context, data *ReportData) error { - if err := ds.sendTopSQLRecords(ctx, data.DataRecords); err != nil { - return err - } - if err := ds.sendSQLMeta(ctx, data.SQLMetas); err != nil { - return err - } - return ds.sendPlanMeta(ctx, data.PlanMetas) -} - -func (ds *pubSubDataSink) sendTopSQLRecords(ctx context.Context, records []tipb.TopSQLRecord) (err error) { - if len(records) == 0 { - return - } - - start := time.Now() - sentCount := 0 - defer func() { - reporter_metrics.TopSQLReportRecordCounterHistogram.Observe(float64(sentCount)) - if err != nil { - reporter_metrics.ReportRecordDurationFailedHistogram.Observe(time.Since(start).Seconds()) - } else { - reporter_metrics.ReportRecordDurationSuccHistogram.Observe(time.Since(start).Seconds()) - } - }() - - topSQLRecord := &tipb.TopSQLSubResponse_Record{} - r := &tipb.TopSQLSubResponse{RespOneof: topSQLRecord} - - for i := range records { - topSQLRecord.Record = &records[i] - if err = ds.stream.Send(r); err != nil { - return - } - sentCount++ - - select { - case <-ctx.Done(): - err = ctx.Err() - return - default: - } - } - - return -} - -func (ds *pubSubDataSink) sendSQLMeta(ctx context.Context, sqlMetas []tipb.SQLMeta) (err error) { - if len(sqlMetas) == 0 { - return - } - - start := time.Now() - sentCount := 0 - defer func() { - reporter_metrics.TopSQLReportSQLCountHistogram.Observe(float64(sentCount)) - if err != nil { - reporter_metrics.ReportSQLDurationFailedHistogram.Observe(time.Since(start).Seconds()) - } else { - reporter_metrics.ReportSQLDurationSuccHistogram.Observe(time.Since(start).Seconds()) - } - }() - - sqlMeta := &tipb.TopSQLSubResponse_SqlMeta{} - r := &tipb.TopSQLSubResponse{RespOneof: sqlMeta} - - for i := range sqlMetas { - sqlMeta.SqlMeta = &sqlMetas[i] - if err = ds.stream.Send(r); err != nil { - return - } - sentCount++ - - select { - case <-ctx.Done(): - err = ctx.Err() - return - default: - } - } - - return -} - -func (ds *pubSubDataSink) sendPlanMeta(ctx context.Context, planMetas []tipb.PlanMeta) (err error) { - if len(planMetas) == 0 { - return - } - - start := time.Now() - sentCount := 0 - defer func() { - reporter_metrics.TopSQLReportPlanCountHistogram.Observe(float64(sentCount)) - if err != nil { - reporter_metrics.ReportPlanDurationFailedHistogram.Observe(time.Since(start).Seconds()) - } else { - reporter_metrics.ReportPlanDurationSuccHistogram.Observe(time.Since(start).Seconds()) - } - }() - - planMeta := &tipb.TopSQLSubResponse_PlanMeta{} - r := &tipb.TopSQLSubResponse{RespOneof: planMeta} - - for i := range planMetas { - planMeta.PlanMeta = &planMetas[i] - if err = ds.stream.Send(r); err != nil { - return - } - sentCount++ - - select { - case <-ctx.Done(): - err = ctx.Err() - return - default: - } - } - - return -} diff --git a/util/topsql/reporter/reporter.go b/util/topsql/reporter/reporter.go deleted file mode 100644 index 7bab034fd7327..0000000000000 --- a/util/topsql/reporter/reporter.go +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package reporter - -import ( - "context" - "time" - - "github.com/pingcap/failpoint" - "github.com/pingcap/tidb/util" - "github.com/pingcap/tidb/util/logutil" - "github.com/pingcap/tidb/util/topsql/collector" - reporter_metrics "github.com/pingcap/tidb/util/topsql/reporter/metrics" - topsqlstate "github.com/pingcap/tidb/util/topsql/state" - "github.com/pingcap/tidb/util/topsql/stmtstats" - "go.uber.org/zap" -) - -const ( - reportTimeout = 40 * time.Second - collectChanBufferSize = 2 -) - -var nowFunc = time.Now - -// TopSQLReporter collects Top SQL metrics. -type TopSQLReporter interface { - collector.Collector - stmtstats.Collector - - // Start uses to start the reporter. - Start() - - // RegisterSQL registers a normalizedSQL with SQLDigest. - // - // Note that the normalized SQL string can be of >1M long. - // This function should be thread-safe, which means concurrently calling it - // in several goroutines should be fine. It should also return immediately, - // and do any CPU-intensive job asynchronously. - RegisterSQL(sqlDigest []byte, normalizedSQL string, isInternal bool) - - // RegisterPlan like RegisterSQL, but for normalized plan strings. - // isLarge indicates the size of normalizedPlan is big. - RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) - - // Close uses to close and release the reporter resource. - Close() -} - -var _ TopSQLReporter = &RemoteTopSQLReporter{} -var _ DataSinkRegisterer = &RemoteTopSQLReporter{} - -// RemoteTopSQLReporter implements TopSQLReporter that sends data to a remote agent. -// This should be called periodically to collect TopSQL resource usage metrics. -type RemoteTopSQLReporter struct { - ctx context.Context - reportCollectedDataChan chan collectedData - cancel context.CancelFunc - sqlCPUCollector *collector.SQLCPUCollector - collectCPUTimeChan chan []collector.SQLCPUTimeRecord - collectStmtStatsChan chan stmtstats.StatementStatsMap - collecting *collecting - normalizedSQLMap *normalizedSQLMap - normalizedPlanMap *normalizedPlanMap - stmtStatsBuffer map[uint64]stmtstats.StatementStatsMap // timestamp => stmtstats.StatementStatsMap - // calling decodePlan this can take a while, so should not block critical paths. - decodePlan planBinaryDecodeFunc - // Instead of dropping large plans, we compress it into encoded format and report - compressPlan planBinaryCompressFunc - DefaultDataSinkRegisterer -} - -// NewRemoteTopSQLReporter creates a new RemoteTopSQLReporter. -// -// decodePlan is a decoding function which will be called asynchronously to decode the plan binary to string. -func NewRemoteTopSQLReporter(decodePlan planBinaryDecodeFunc, compressPlan planBinaryCompressFunc) *RemoteTopSQLReporter { - ctx, cancel := context.WithCancel(context.Background()) - tsr := &RemoteTopSQLReporter{ - DefaultDataSinkRegisterer: NewDefaultDataSinkRegisterer(ctx), - ctx: ctx, - cancel: cancel, - collectCPUTimeChan: make(chan []collector.SQLCPUTimeRecord, collectChanBufferSize), - collectStmtStatsChan: make(chan stmtstats.StatementStatsMap, collectChanBufferSize), - reportCollectedDataChan: make(chan collectedData, 1), - collecting: newCollecting(), - normalizedSQLMap: newNormalizedSQLMap(), - normalizedPlanMap: newNormalizedPlanMap(), - stmtStatsBuffer: map[uint64]stmtstats.StatementStatsMap{}, - decodePlan: decodePlan, - compressPlan: compressPlan, - } - tsr.sqlCPUCollector = collector.NewSQLCPUCollector(tsr) - return tsr -} - -// Start implements the TopSQLReporter interface. -func (tsr *RemoteTopSQLReporter) Start() { - tsr.sqlCPUCollector.Start() - go tsr.collectWorker() - go tsr.reportWorker() -} - -// Collect implements tracecpu.Collector. -// -// WARN: It will drop the DataRecords if the processing is not in time. -// This function is thread-safe and efficient. -func (tsr *RemoteTopSQLReporter) Collect(data []collector.SQLCPUTimeRecord) { - if len(data) == 0 { - return - } - select { - case tsr.collectCPUTimeChan <- data: - default: - // ignore if chan blocked - reporter_metrics.IgnoreCollectChannelFullCounter.Inc() - } -} - -// CollectStmtStatsMap implements stmtstats.Collector. -// -// WARN: It will drop the DataRecords if the processing is not in time. -// This function is thread-safe and efficient. -func (tsr *RemoteTopSQLReporter) CollectStmtStatsMap(data stmtstats.StatementStatsMap) { - if len(data) == 0 { - return - } - select { - case tsr.collectStmtStatsChan <- data: - default: - // ignore if chan blocked - reporter_metrics.IgnoreCollectStmtChannelFullCounter.Inc() - } -} - -// RegisterSQL implements TopSQLReporter. -// -// This function is thread-safe and efficient. -func (tsr *RemoteTopSQLReporter) RegisterSQL(sqlDigest []byte, normalizedSQL string, isInternal bool) { - tsr.normalizedSQLMap.register(sqlDigest, normalizedSQL, isInternal) -} - -// RegisterPlan implements TopSQLReporter. -// -// This function is thread-safe and efficient. -func (tsr *RemoteTopSQLReporter) RegisterPlan(planDigest []byte, normalizedPlan string, isLarge bool) { - tsr.normalizedPlanMap.register(planDigest, normalizedPlan, isLarge) -} - -// Close implements TopSQLReporter. -func (tsr *RemoteTopSQLReporter) Close() { - tsr.cancel() - tsr.sqlCPUCollector.Stop() - tsr.onReporterClosing() -} - -// collectWorker consumes and collects data from tracecpu.Collector/stmtstats.Collector. -func (tsr *RemoteTopSQLReporter) collectWorker() { - defer util.Recover("top-sql", "collectWorker", nil, false) - - currentReportInterval := topsqlstate.GlobalState.ReportIntervalSeconds.Load() - reportTicker := time.NewTicker(time.Second * time.Duration(currentReportInterval)) - defer reportTicker.Stop() - for { - select { - case <-tsr.ctx.Done(): - return - case data := <-tsr.collectCPUTimeChan: - timestamp := uint64(nowFunc().Unix()) - tsr.processCPUTimeData(timestamp, data) - case data := <-tsr.collectStmtStatsChan: - timestamp := uint64(nowFunc().Unix()) - tsr.stmtStatsBuffer[timestamp] = data - case <-reportTicker.C: - tsr.processStmtStatsData() - tsr.takeDataAndSendToReportChan() - // Update `reportTicker` if report interval changed. - if newInterval := topsqlstate.GlobalState.ReportIntervalSeconds.Load(); newInterval != currentReportInterval { - currentReportInterval = newInterval - reportTicker.Reset(time.Second * time.Duration(currentReportInterval)) - } - } - } -} - -// processCPUTimeData collects top N cpuRecords of each round into tsr.collecting, and evict the -// data that is not in top N. All the evicted cpuRecords will be summary into the others. -func (tsr *RemoteTopSQLReporter) processCPUTimeData(timestamp uint64, data cpuRecords) { - defer util.Recover("top-sql", "processCPUTimeData", nil, false) - - // Get top N cpuRecords of each round cpuRecords. Collect the top N to tsr.collecting - // for each round. SQL meta will not be evicted, since the evicted SQL can be appeared - // on other components (TiKV) TopN DataRecords. - top, evicted := data.topN(int(topsqlstate.GlobalState.MaxStatementCount.Load())) - for _, r := range top { - tsr.collecting.getOrCreateRecord(r.SQLDigest, r.PlanDigest).appendCPUTime(timestamp, r.CPUTimeMs) - } - if len(evicted) == 0 { - return - } - totalEvictedCPUTime := uint32(0) - for _, e := range evicted { - totalEvictedCPUTime += e.CPUTimeMs - // Mark which digests are evicted under each timestamp. - // We will determine whether the corresponding CPUTime has been evicted - // when collecting stmtstats. If so, then we can ignore it directly. - tsr.collecting.markAsEvicted(timestamp, e.SQLDigest, e.PlanDigest) - } - tsr.collecting.appendOthersCPUTime(timestamp, totalEvictedCPUTime) -} - -// processStmtStatsData collects tsr.stmtStatsBuffer into tsr.collecting. -// All the evicted items will be summary into the others. -func (tsr *RemoteTopSQLReporter) processStmtStatsData() { - defer util.Recover("top-sql", "processStmtStatsData", nil, false) - - for timestamp, data := range tsr.stmtStatsBuffer { - for digest, item := range data { - sqlDigest, planDigest := []byte(digest.SQLDigest), []byte(digest.PlanDigest) - if tsr.collecting.hasEvicted(timestamp, sqlDigest, planDigest) { - // This timestamp+sql+plan has been evicted due to low CPUTime. - tsr.collecting.appendOthersStmtStatsItem(timestamp, *item) - continue - } - tsr.collecting.getOrCreateRecord(sqlDigest, planDigest).appendStmtStatsItem(timestamp, *item) - } - } - tsr.stmtStatsBuffer = map[uint64]stmtstats.StatementStatsMap{} -} - -// takeDataAndSendToReportChan takes records data and then send to the report channel for reporting. -func (tsr *RemoteTopSQLReporter) takeDataAndSendToReportChan() { - // Send to report channel. When channel is full, data will be dropped. - select { - case tsr.reportCollectedDataChan <- collectedData{ - collected: tsr.collecting.take(), - normalizedSQLMap: tsr.normalizedSQLMap.take(), - normalizedPlanMap: tsr.normalizedPlanMap.take(), - }: - default: - // ignore if chan blocked - reporter_metrics.IgnoreReportChannelFullCounter.Inc() - } -} - -// reportWorker sends data to the gRPC endpoint from the `reportCollectedDataChan` one by one. -func (tsr *RemoteTopSQLReporter) reportWorker() { - defer util.Recover("top-sql", "reportWorker", nil, false) - - for { - select { - case data := <-tsr.reportCollectedDataChan: - // When `reportCollectedDataChan` receives something, there could be ongoing - // `RegisterSQL` and `RegisterPlan` running, who writes to the data structure - // that `data` contains. So we wait for a little while to ensure that writes - // are finished. - time.Sleep(time.Millisecond * 100) - rs := data.collected.getReportRecords() - // Convert to protobuf data and do report. - tsr.doReport(&ReportData{ - DataRecords: rs.toProto(), - SQLMetas: data.normalizedSQLMap.toProto(), - PlanMetas: data.normalizedPlanMap.toProto(tsr.decodePlan, tsr.compressPlan), - }) - case <-tsr.ctx.Done(): - return - } - } -} - -// doReport sends ReportData to DataSinks. -func (tsr *RemoteTopSQLReporter) doReport(data *ReportData) { - defer util.Recover("top-sql", "doReport", nil, false) - - if !data.hasData() { - return - } - timeout := reportTimeout - failpoint.Inject("resetTimeoutForTest", func(val failpoint.Value) { - if val.(bool) { - interval := time.Duration(topsqlstate.GlobalState.ReportIntervalSeconds.Load()) * time.Second - if interval < timeout { - timeout = interval - } - } - }) - _ = tsr.trySend(data, time.Now().Add(timeout)) -} - -// trySend sends ReportData to all internal registered DataSinks. -func (tsr *RemoteTopSQLReporter) trySend(data *ReportData, deadline time.Time) error { - tsr.DefaultDataSinkRegisterer.Lock() - dataSinks := make([]DataSink, 0, len(tsr.dataSinks)) - for ds := range tsr.dataSinks { - dataSinks = append(dataSinks, ds) - } - tsr.DefaultDataSinkRegisterer.Unlock() - for _, ds := range dataSinks { - if err := ds.TrySend(data, deadline); err != nil { - logutil.BgLogger().Warn("failed to send data to datasink", zap.String("category", "top-sql"), zap.Error(err)) - } - } - return nil -} - -// onReporterClosing calls the OnReporterClosing method of all internally registered DataSinks. -func (tsr *RemoteTopSQLReporter) onReporterClosing() { - var m map[DataSink]struct{} - tsr.DefaultDataSinkRegisterer.Lock() - m, tsr.dataSinks = tsr.dataSinks, make(map[DataSink]struct{}) - tsr.DefaultDataSinkRegisterer.Unlock() - for d := range m { - d.OnReporterClosing() - } -} - -// collectedData is used for transmission in the channel. -type collectedData struct { - collected *collecting - normalizedSQLMap *normalizedSQLMap - normalizedPlanMap *normalizedPlanMap -} diff --git a/util/topsql/state/BUILD.bazel b/util/topsql/state/BUILD.bazel deleted file mode 100644 index 05cbc87c5cd23..0000000000000 --- a/util/topsql/state/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "state", - srcs = ["state.go"], - importpath = "github.com/pingcap/tidb/util/topsql/state", - visibility = ["//visibility:public"], - deps = ["@org_uber_go_atomic//:atomic"], -) diff --git a/util/topsql/stmtstats/BUILD.bazel b/util/topsql/stmtstats/BUILD.bazel deleted file mode 100644 index a0e7bf0cfbb50..0000000000000 --- a/util/topsql/stmtstats/BUILD.bazel +++ /dev/null @@ -1,40 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "stmtstats", - srcs = [ - "aggregator.go", - "kv_exec_count.go", - "stmtstats.go", - ], - importpath = "github.com/pingcap/tidb/util/topsql/stmtstats", - visibility = ["//visibility:public"], - deps = [ - "//util/topsql/state", - "@com_github_tikv_client_go_v2//tikvrpc", - "@com_github_tikv_client_go_v2//tikvrpc/interceptor", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "stmtstats_test", - timeout = "short", - srcs = [ - "aggregator_test.go", - "kv_exec_count_test.go", - "main_test.go", - "stmtstats_test.go", - ], - embed = [":stmtstats"], - flaky = True, - deps = [ - "//testkit/testsetup", - "//util/topsql/state", - "@com_github_stretchr_testify//assert", - "@com_github_stretchr_testify//require", - "@com_github_tikv_client_go_v2//tikvrpc", - "@org_uber_go_atomic//:atomic", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/topsql/stmtstats/main_test.go b/util/topsql/stmtstats/main_test.go deleted file mode 100644 index 5374b5f9ad331..0000000000000 --- a/util/topsql/stmtstats/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package stmtstats - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/tracing/BUILD.bazel b/util/tracing/BUILD.bazel deleted file mode 100644 index ea2cada504ca3..0000000000000 --- a/util/tracing/BUILD.bazel +++ /dev/null @@ -1,37 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "tracing", - srcs = [ - "opt_trace.go", - "util.go", - ], - importpath = "github.com/pingcap/tidb/util/tracing", - visibility = ["//visibility:public"], - deps = [ - "//parser/model", - "@com_github_opentracing_basictracer_go//:basictracer-go", - "@com_github_opentracing_opentracing_go//:opentracing-go", - ], -) - -go_test( - name = "tracing_test", - timeout = "short", - srcs = [ - "main_test.go", - "noop_bench_test.go", - "opt_trace_test.go", - "util_test.go", - ], - embed = [":tracing"], - flaky = True, - deps = [ - "//parser/model", - "//testkit/testsetup", - "@com_github_opentracing_basictracer_go//:basictracer-go", - "@com_github_opentracing_opentracing_go//:opentracing-go", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/tracing/main_test.go b/util/tracing/main_test.go deleted file mode 100644 index d19585dae66d4..0000000000000 --- a/util/tracing/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package tracing - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/tracing/util.go b/util/tracing/util.go deleted file mode 100644 index e26a15340cd96..0000000000000 --- a/util/tracing/util.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package tracing - -import ( - "context" - "runtime/trace" - - "github.com/opentracing/basictracer-go" - "github.com/opentracing/opentracing-go" - "github.com/pingcap/tidb/parser/model" -) - -// TiDBTrace is set as Baggage on traces which are used for tidb tracing. -const TiDBTrace = "tr" - -type sqlTracingCtxKeyType struct{} - -var sqlTracingCtxKey = sqlTracingCtxKeyType{} - -// A CallbackRecorder immediately invokes itself on received trace spans. -type CallbackRecorder func(sp basictracer.RawSpan) - -// RecordSpan implements basictracer.SpanRecorder. -func (cr CallbackRecorder) RecordSpan(sp basictracer.RawSpan) { - cr(sp) -} - -// NewRecordedTrace returns a Span which records directly via the specified -// callback. -func NewRecordedTrace(opName string, callback func(sp basictracer.RawSpan)) opentracing.Span { - tr := basictracer.New(CallbackRecorder(callback)) - opentracing.SetGlobalTracer(tr) - sp := tr.StartSpan(opName) - sp.SetBaggageItem(TiDBTrace, "1") - return sp -} - -// noopSpan returns a Span which discards all operations. -func noopSpan() opentracing.Span { - return (opentracing.NoopTracer{}).StartSpan("DefaultSpan") -} - -// SpanFromContext returns the span obtained from the context or, if none is found, a new one started through tracer. -func SpanFromContext(ctx context.Context) (sp opentracing.Span) { - if sp = opentracing.SpanFromContext(ctx); sp == nil { - return noopSpan() - } - return sp -} - -// ChildSpanFromContxt return a non-nil span. If span can be got from ctx, then returned span is -// a child of such span. Otherwise, returned span is a noop span. -func ChildSpanFromContxt(ctx context.Context, opName string) (opentracing.Span, context.Context) { - if sp := opentracing.SpanFromContext(ctx); sp != nil { - if _, ok := sp.Tracer().(opentracing.NoopTracer); !ok { - child := opentracing.StartSpan(opName, opentracing.ChildOf(sp.Context())) - return child, opentracing.ContextWithSpan(ctx, child) - } - } - return noopSpan(), ctx -} - -// StartRegion provides better API, integrating both opentracing and runtime.trace facilities into one. -// Recommended usage is -// -// defer tracing.StartRegion(ctx, "myTracedRegion").End() -func StartRegion(ctx context.Context, regionType string) Region { - r := trace.StartRegion(ctx, regionType) - var span1 opentracing.Span - if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { - span1 = span.Tracer().StartSpan(regionType, opentracing.ChildOf(span.Context())) - } - return Region{ - Region: r, - Span: span1, - } -} - -// StartRegionEx returns Region together with the context. -// Recommended usage is -// -// r, ctx := tracing.StartRegionEx(ctx, "myTracedRegion") -// defer r.End() -func StartRegionEx(ctx context.Context, regionType string) (Region, context.Context) { - r := StartRegion(ctx, regionType) - if r.Span != nil { - ctx = opentracing.ContextWithSpan(ctx, r.Span) - } - return r, ctx -} - -// Region is a region of code whose execution time interval is traced. -type Region struct { - *trace.Region - opentracing.Span -} - -// End marks the end of the traced code region. -func (r Region) End() { - if r.Span != nil { - r.Span.Finish() - } - r.Region.End() -} - -// TraceInfoFromContext returns the `model.TraceInfo` in context -func TraceInfoFromContext(ctx context.Context) *model.TraceInfo { - val := ctx.Value(sqlTracingCtxKey) - if info, ok := val.(*model.TraceInfo); ok { - return info - } - return nil -} - -// ContextWithTraceInfo creates a new `model.TraceInfo` for context -func ContextWithTraceInfo(ctx context.Context, info *model.TraceInfo) context.Context { - if info == nil { - return ctx - } - return context.WithValue(ctx, sqlTracingCtxKey, info) -} diff --git a/util/tracing/util_test.go b/util/tracing/util_test.go deleted file mode 100644 index 3af7ac5b99fa4..0000000000000 --- a/util/tracing/util_test.go +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2018 PingCAP, Inc. -// -// 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. - -package tracing_test - -import ( - "context" - "testing" - - "github.com/opentracing/basictracer-go" - "github.com/opentracing/opentracing-go" - "github.com/pingcap/tidb/parser/model" - "github.com/pingcap/tidb/util/tracing" - "github.com/stretchr/testify/require" -) - -func TestSpanFromContext(t *testing.T) { - ctx := context.TODO() - noopSp := tracing.SpanFromContext(ctx) - // test noop span - _, ok := noopSp.Tracer().(opentracing.NoopTracer) - require.True(t, ok) - - // test tidb tracing - collectedSpan := make([]basictracer.RawSpan, 1) - sp := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { - collectedSpan = append(collectedSpan, sp) - }) - sp.Finish() - opentracing.ContextWithSpan(ctx, sp) - child := tracing.SpanFromContext(ctx) - child.Finish() - - // verify second span's operation is not nil, this way we can ensure - // callback logic works. - require.NotNil(t, collectedSpan[0].Operation) -} - -func TestChildSpanFromContext(t *testing.T) { - ctx := context.TODO() - noopSp, _ := tracing.ChildSpanFromContxt(ctx, "") - _, ok := noopSp.Tracer().(opentracing.NoopTracer) - require.True(t, ok) - - // test tidb tracing - collectedSpan := make([]basictracer.RawSpan, 1) - sp := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { - collectedSpan = append(collectedSpan, sp) - }) - sp.Finish() - opentracing.ContextWithSpan(ctx, sp) - child, _ := tracing.ChildSpanFromContxt(ctx, "test_child") - child.Finish() - - // verify second span's operation is not nil, this way we can ensure - // callback logic works. - require.NotNil(t, collectedSpan[1].Operation) -} - -func TestFollowFrom(t *testing.T) { - var collectedSpans []basictracer.RawSpan - // first start a root span - sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { - collectedSpans = append(collectedSpans, sp) - }) - sp2 := sp1.Tracer().StartSpan("follow_from", opentracing.FollowsFrom(sp1.Context())) - sp1.Finish() - sp2.Finish() - require.Equal(t, "follow_from", collectedSpans[1].Operation) - require.NotEqual(t, uint64(0), collectedSpans[1].ParentSpanID) - // only root span has 0 parent id - require.Equal(t, uint64(0), collectedSpans[0].ParentSpanID) -} - -func TestCreateSapnBeforeSetupGlobalTracer(t *testing.T) { - var collectedSpans []basictracer.RawSpan - sp := opentracing.StartSpan("before") - sp.Finish() - - // first start a root span - sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { - collectedSpans = append(collectedSpans, sp) - }) - sp1.Finish() - - // sp is a span started before we setup global tracer; hence such span will be - // droped. - require.Len(t, collectedSpans, 1) -} - -func TestTreeRelationship(t *testing.T) { - var collectedSpans []basictracer.RawSpan - ctx := context.TODO() - // first start a root span - sp1 := tracing.NewRecordedTrace("test", func(sp basictracer.RawSpan) { - collectedSpans = append(collectedSpans, sp) - }) - - // then store such root span into context - ctx = opentracing.ContextWithSpan(ctx, sp1) - - // create children span from context - sp2, ctx := tracing.ChildSpanFromContxt(ctx, "parent") - sp3, _ := tracing.ChildSpanFromContxt(ctx, "child") - - // notify span that we are about to reach end of journey. - sp1.Finish() - sp2.Finish() - sp3.Finish() - - // ensure length of collectedSpans is greather than 0 - require.Greater(t, len(collectedSpans), 0) - if len(collectedSpans) > 0 { - require.Equal(t, "test", collectedSpans[0].Operation) - require.Equal(t, "parent", collectedSpans[1].Operation) - require.Equal(t, "child", collectedSpans[2].Operation) - // check tree relationship - // sp1 is parent of sp2. And sp2 is parent of sp3. - require.Equal(t, collectedSpans[0].Context.SpanID, collectedSpans[1].ParentSpanID) - require.Equal(t, collectedSpans[1].Context.SpanID, collectedSpans[2].ParentSpanID) - } -} - -func TestTraceInfoFromContext(t *testing.T) { - ctx := context.Background() - // get info from a non-tracing context - require.Nil(t, tracing.TraceInfoFromContext(ctx)) - // ContextWithTraceInfo with a nil info will return the original context - require.Equal(t, ctx, tracing.ContextWithTraceInfo(ctx, nil)) - // create a context with trace info - ctx, cancel := context.WithCancel(context.WithValue(ctx, "val1", "a")) - ctx = tracing.ContextWithTraceInfo(ctx, &model.TraceInfo{ConnectionID: 12345, SessionAlias: "alias1"}) - // new context should have the same value as the original one - info := tracing.TraceInfoFromContext(ctx) - require.Equal(t, uint64(12345), info.ConnectionID) - require.Equal(t, "alias1", info.SessionAlias) - require.Equal(t, "a", ctx.Value("val1")) - require.NoError(t, ctx.Err()) - cancel() - require.Error(t, ctx.Err()) -} diff --git a/util/trxevents/BUILD.bazel b/util/trxevents/BUILD.bazel deleted file mode 100644 index 670e94ca4cdcb..0000000000000 --- a/util/trxevents/BUILD.bazel +++ /dev/null @@ -1,9 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "trxevents", - srcs = ["trx_events.go"], - importpath = "github.com/pingcap/tidb/util/trxevents", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_kvproto//pkg/kvrpcpb"], -) diff --git a/util/util.go b/util/util.go deleted file mode 100644 index 00581b4620772..0000000000000 --- a/util/util.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package util - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io" - "net" - "net/http" - "strconv" - "strings" - "time" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb/parser" - "go.uber.org/atomic" - "go.uber.org/zap" -) - -// SliceToMap converts slice to map -// nolint:unused -func SliceToMap(slice []string) map[string]interface{} { - sMap := make(map[string]interface{}) - for _, str := range slice { - sMap[str] = struct{}{} - } - return sMap -} - -// StringsToInterfaces converts string slice to interface slice -func StringsToInterfaces(strs []string) []interface{} { - is := make([]interface{}, 0, len(strs)) - for _, str := range strs { - is = append(is, str) - } - - return is -} - -// GetJSON fetches a page and parses it as JSON. The parsed result will be -// stored into the `v`. The variable `v` must be a pointer to a type that can be -// unmarshalled from JSON. -// -// Example: -// -// client := &http.Client{} -// var resp struct { IP string } -// if err := util.GetJSON(client, "http://api.ipify.org/?format=json", &resp); err != nil { -// return errors.Trace(err) -// } -// fmt.Println(resp.IP) -// -// nolint:unused -func GetJSON(client *http.Client, url string, v interface{}) error { - resp, err := client.Get(url) - if err != nil { - return errors.Trace(err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, err := io.ReadAll(resp.Body) - if err != nil { - return errors.Trace(err) - } - return errors.Errorf("get %s http status code != 200, message %s", url, string(body)) - } - - return errors.Trace(json.NewDecoder(resp.Body).Decode(v)) -} - -// ChanMap creates a channel which applies the function over the input Channel. -// Hint of Resource Leakage: -// In golang, channel isn't an interface so we must create a goroutine for handling the inputs. -// Hence the input channel must be closed properly or this function may leak a goroutine. -func ChanMap[T, R any](c <-chan T, f func(T) R) <-chan R { - outCh := make(chan R) - go func() { - defer close(outCh) - for item := range c { - outCh <- f(item) - } - }() - return outCh -} - -// Str2Int64Map converts a string to a map[int64]struct{}. -func Str2Int64Map(str string) map[int64]struct{} { - strs := strings.Split(str, ",") - res := make(map[int64]struct{}, len(strs)) - for _, s := range strs { - id, _ := strconv.ParseInt(s, 10, 64) - res[id] = struct{}{} - } - return res -} - -// GenLogFields generate log fields. -func GenLogFields(costTime time.Duration, info *ProcessInfo, needTruncateSQL bool) []zap.Field { - if info.RefCountOfStmtCtx != nil && !info.RefCountOfStmtCtx.TryIncrease() { - return nil - } - defer info.RefCountOfStmtCtx.Decrease() - - logFields := make([]zap.Field, 0, 20) - logFields = append(logFields, zap.String("cost_time", strconv.FormatFloat(costTime.Seconds(), 'f', -1, 64)+"s")) - execDetail := info.StmtCtx.GetExecDetails() - logFields = append(logFields, execDetail.ToZapFields()...) - if copTaskInfo := info.StmtCtx.CopTasksDetails(); copTaskInfo != nil { - logFields = append(logFields, copTaskInfo.ToZapFields()...) - } - if statsInfo := info.StatsInfo(info.Plan); len(statsInfo) > 0 { - var buf strings.Builder - firstComma := false - vStr := "" - for k, v := range statsInfo { - if v == 0 { - vStr = "pseudo" - } else { - vStr = strconv.FormatUint(v, 10) - } - if firstComma { - buf.WriteString("," + k + ":" + vStr) - } else { - buf.WriteString(k + ":" + vStr) - firstComma = true - } - } - logFields = append(logFields, zap.String("stats", buf.String())) - } - if info.ID != 0 { - logFields = append(logFields, zap.Uint64("conn", info.ID)) - } - if len(info.User) > 0 { - logFields = append(logFields, zap.String("user", info.User)) - } - if len(info.DB) > 0 { - logFields = append(logFields, zap.String("database", info.DB)) - } - var tableIDs, indexNames string - if len(info.TableIDs) > 0 { - tableIDs = strings.ReplaceAll(fmt.Sprintf("%v", info.TableIDs), " ", ",") - logFields = append(logFields, zap.String("table_ids", tableIDs)) - } - if len(info.IndexNames) > 0 { - indexNames = strings.ReplaceAll(fmt.Sprintf("%v", info.IndexNames), " ", ",") - logFields = append(logFields, zap.String("index_names", indexNames)) - } - logFields = append(logFields, zap.Uint64("txn_start_ts", info.CurTxnStartTS)) - if memTracker := info.MemTracker; memTracker != nil { - logFields = append(logFields, zap.String("mem_max", fmt.Sprintf("%d Bytes (%v)", memTracker.MaxConsumed(), memTracker.FormatBytes(memTracker.MaxConsumed())))) - } - - const logSQLLen = 1024 * 8 - var sql string - if len(info.Info) > 0 { - sql = info.Info - if info.RedactSQL { - sql = parser.Normalize(sql) - } - } - if len(sql) > logSQLLen && needTruncateSQL { - sql = fmt.Sprintf("%s len(%d)", sql[:logSQLLen], len(sql)) - } - logFields = append(logFields, zap.String("sql", sql)) - logFields = append(logFields, zap.String("session_alias", info.SessionAlias)) - return logFields -} - -// PrintableASCII detects if b is a printable ASCII character. -// Ref to:http://facweb.cs.depaul.edu/sjost/it212/documents/ascii-pr.htm -func PrintableASCII(b byte) bool { - if b < 32 || b > 127 { - return false - } - - return true -} - -// FmtNonASCIIPrintableCharToHex turns non-printable-ASCII characters into Hex -func FmtNonASCIIPrintableCharToHex(str string) string { - var b bytes.Buffer - b.Grow(len(str) * 2) - for i := 0; i < len(str); i++ { - if PrintableASCII(str[i]) { - b.WriteByte(str[i]) - continue - } - - b.WriteString(`\x`) - // turns non-printable-ASCII character into hex-string - b.WriteString(fmt.Sprintf("%02X", str[i])) - } - return b.String() -} - -// TCPConnWithIOCounter is a wrapper of net.TCPConn with counter that accumulates -// the bytes this connection reads/writes. -type TCPConnWithIOCounter struct { - *net.TCPConn - c *atomic.Uint64 -} - -// NewTCPConnWithIOCounter creates a new TCPConnWithIOCounter. -func NewTCPConnWithIOCounter(conn *net.TCPConn, c *atomic.Uint64) net.Conn { - return &TCPConnWithIOCounter{ - TCPConn: conn, - c: c, - } -} - -func (t *TCPConnWithIOCounter) Read(b []byte) (n int, err error) { - n, err = t.TCPConn.Read(b) - t.c.Add(uint64(n)) - return n, err -} - -func (t *TCPConnWithIOCounter) Write(b []byte) (n int, err error) { - n, err = t.TCPConn.Write(b) - t.c.Add(uint64(n)) - return n, err -} - -// ReadLine tries to read a complete line from bufio.Reader. -// maxLineSize specifies the maximum size of a single line. -func ReadLine(reader *bufio.Reader, maxLineSize int) ([]byte, error) { - var resByte []byte - lineByte, isPrefix, err := reader.ReadLine() - if isPrefix { - // Need to read more data. - resByte = make([]byte, len(lineByte), len(lineByte)*2) - } else { - resByte = make([]byte, len(lineByte)) - } - // Use copy here to avoid shallow copy problem. - copy(resByte, lineByte) - if err != nil { - return resByte, err - } - var tempLine []byte - for isPrefix { - tempLine, isPrefix, err = reader.ReadLine() - resByte = append(resByte, tempLine...) // nozero - // Use maxLineSize to check the single line length. - if len(resByte) > maxLineSize { - return resByte, errors.Errorf("single line length exceeds limit: %v", maxLineSize) - } - if err != nil { - return resByte, err - } - } - return resByte, err -} - -// ReadLines tries to read lines from bufio.Reader. -// count specifies the number of lines. -// maxLineSize specifies the maximum size of a single line. -func ReadLines(reader *bufio.Reader, count int, maxLineSize int) ([][]byte, error) { - lines := make([][]byte, 0, count) - for i := 0; i < count; i++ { - line, err := ReadLine(reader, maxLineSize) - if err == io.EOF && len(lines) > 0 { - return lines, nil - } - if err != nil { - return nil, err - } - lines = append(lines, line) - } - return lines, nil -} - -// IsInCorrectIdentifierName checks if the identifier is incorrect. -// See https://dev.mysql.com/doc/refman/5.7/en/identifiers.html -func IsInCorrectIdentifierName(name string) bool { - if len(name) == 0 { - return true - } - if name[len(name)-1] == ' ' { - return true - } - return false -} diff --git a/util/util_test.go b/util/util_test.go deleted file mode 100644 index 858fbea11804d..0000000000000 --- a/util/util_test.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2022 PingCAP, Inc. -// -// 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. - -package util - -import ( - "bufio" - "io" - "strings" - "testing" - "time" - - "github.com/pingcap/tidb/sessionctx/stmtctx" - "github.com/pingcap/tidb/util/memory" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestLogFormat(t *testing.T) { - mem := memory.NewTracker(-1, -1) - mem.Consume(1<<30 + 1<<29 + 1<<28 + 1<<27) - mockTooLongQuery := make([]byte, 1024*9) - - var refCount stmtctx.ReferenceCount = 0 - info := &ProcessInfo{ - ID: 233, - User: "PingCAP", - Host: "127.0.0.1", - DB: "Database", - Info: "select * from table where a > 1", - CurTxnStartTS: 23333, - StatsInfo: func(interface{}) map[string]uint64 { - return nil - }, - StmtCtx: stmtctx.NewStmtCtx(), - RefCountOfStmtCtx: &refCount, - MemTracker: mem, - RedactSQL: false, - SessionAlias: "alias123", - } - costTime := time.Second * 233 - logSQLTruncateLen := 1024 * 8 - logFields := GenLogFields(costTime, info, true) - - assert.Len(t, logFields, 8) - assert.Equal(t, "cost_time", logFields[0].Key) - assert.Equal(t, "233s", logFields[0].String) - assert.Equal(t, "conn", logFields[1].Key) - assert.Equal(t, int64(233), logFields[1].Integer) - assert.Equal(t, "user", logFields[2].Key) - assert.Equal(t, "PingCAP", logFields[2].String) - assert.Equal(t, "database", logFields[3].Key) - assert.Equal(t, "Database", logFields[3].String) - assert.Equal(t, "txn_start_ts", logFields[4].Key) - assert.Equal(t, int64(23333), logFields[4].Integer) - assert.Equal(t, "mem_max", logFields[5].Key) - assert.Equal(t, "2013265920 Bytes (1.88 GB)", logFields[5].String) - assert.Equal(t, "sql", logFields[6].Key) - assert.Equal(t, "select * from table where a > 1", logFields[6].String) - - logFields = GenLogFields(costTime, info, true) - assert.Equal(t, "select * from table where a > 1", logFields[6].String) - info.Info = string(mockTooLongQuery) - logFields = GenLogFields(costTime, info, true) - assert.Equal(t, len(logFields[6].String), logSQLTruncateLen+10) - logFields = GenLogFields(costTime, info, false) - assert.Equal(t, len(logFields[6].String), len(mockTooLongQuery)) - assert.Equal(t, logFields[7].String, "alias123") -} - -func TestReadLine(t *testing.T) { - reader := bufio.NewReader(strings.NewReader(`line1 -line2 -line3`)) - line, err := ReadLine(reader, 1024) - require.NoError(t, err) - require.Equal(t, "line1", string(line)) - line, err = ReadLine(reader, 1024) - require.NoError(t, err) - require.Equal(t, "line2", string(line)) - line, err = ReadLine(reader, 1024) - require.NoError(t, err) - require.Equal(t, "line3", string(line)) - line, err = ReadLine(reader, 1024) - require.Equal(t, io.EOF, err) - require.Len(t, line, 0) -} - -func TestIsInCorrectIdentifierName(t *testing.T) { - tests := []struct { - name string - input string - correct bool - }{ - {"Empty identifier", "", true}, - {"Ending space", "test ", true}, - {"Correct identifier", "test", false}, - {"Other correct Identifier", "aaa --\n\txyz", false}, - } - - for _, tc := range tests { - got := IsInCorrectIdentifierName(tc.input) - require.Equalf(t, tc.correct, got, "IsInCorrectIdentifierName(%v) != %v", tc.name, tc.correct) - } -} diff --git a/util/versioninfo/BUILD.bazel b/util/versioninfo/BUILD.bazel deleted file mode 100644 index 2ebbcd41cb047..0000000000000 --- a/util/versioninfo/BUILD.bazel +++ /dev/null @@ -1,8 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "versioninfo", - srcs = ["versioninfo.go"], - importpath = "github.com/pingcap/tidb/util/versioninfo", - visibility = ["//visibility:public"], -) diff --git a/util/vitess/BUILD.bazel b/util/vitess/BUILD.bazel deleted file mode 100644 index e2d41e82a1385..0000000000000 --- a/util/vitess/BUILD.bazel +++ /dev/null @@ -1,25 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "vitess", - srcs = ["vitess_hash.go"], - importpath = "github.com/pingcap/tidb/util/vitess", - visibility = ["//visibility:public"], - deps = ["@com_github_pingcap_errors//:errors"], -) - -go_test( - name = "vitess_test", - timeout = "short", - srcs = [ - "main_test.go", - "vitess_hash_test.go", - ], - embed = [":vitess"], - flaky = True, - deps = [ - "//testkit/testsetup", - "@com_github_stretchr_testify//require", - "@org_uber_go_goleak//:goleak", - ], -) diff --git a/util/vitess/main_test.go b/util/vitess/main_test.go deleted file mode 100644 index 1eb50fd233a44..0000000000000 --- a/util/vitess/main_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2021 PingCAP, Inc. -// -// 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. - -package vitess - -import ( - "testing" - - "github.com/pingcap/tidb/testkit/testsetup" - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - testsetup.SetupForCommonTest() - opts := []goleak.Option{ - goleak.IgnoreTopFunction("github.com/golang/glog.(*fileSink).flushDaemon"), - goleak.IgnoreTopFunction("github.com/lestrrat-go/httprc.runFetchWorker"), - goleak.IgnoreTopFunction("go.etcd.io/etcd/client/pkg/v3/logutil.(*MergeLogger).outputLoop"), - } - goleak.VerifyTestMain(m, opts...) -} diff --git a/util/watcher/BUILD.bazel b/util/watcher/BUILD.bazel deleted file mode 100644 index b9ef440e70a58..0000000000000 --- a/util/watcher/BUILD.bazel +++ /dev/null @@ -1,24 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "watcher", - srcs = [ - "event.go", - "watcher.go", - ], - importpath = "github.com/pingcap/tidb/util/watcher", - visibility = ["//visibility:public"], - deps = [ - "@com_github_pingcap_errors//:errors", - "@org_uber_go_atomic//:atomic", - ], -) - -go_test( - name = "watcher_test", - timeout = "short", - srcs = ["watcher_test.go"], - embed = [":watcher"], - flaky = True, - deps = ["@com_github_stretchr_testify//require"], -) diff --git a/util/zeropool/BUILD.bazel b/util/zeropool/BUILD.bazel deleted file mode 100644 index c0dde3894d231..0000000000000 --- a/util/zeropool/BUILD.bazel +++ /dev/null @@ -1,20 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "zeropool", - srcs = ["pool.go"], - importpath = "github.com/pingcap/tidb/util/zeropool", - visibility = ["//visibility:public"], -) - -go_test( - name = "zeropool_test", - timeout = "short", - srcs = ["pool_test.go"], - flaky = True, - deps = [ - ":zeropool", - "@com_github_stretchr_testify//require", - "@org_uber_go_atomic//:atomic", - ], -) diff --git a/util/zeropool/pool_test.go b/util/zeropool/pool_test.go deleted file mode 100644 index 16a7f3170df11..0000000000000 --- a/util/zeropool/pool_test.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2023 PingCAP, Inc. -// -// 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. - -package zeropool_test - -import ( - "math" - "sync" - "testing" - - "github.com/pingcap/tidb/util/zeropool" - "github.com/stretchr/testify/require" - "go.uber.org/atomic" -) - -func TestPool(t *testing.T) { - t.Run("provides correct values", func(t *testing.T) { - pool := zeropool.New(func() []byte { return make([]byte, 1024) }) - item1 := pool.Get() - require.Equal(t, 1024, len(item1)) - - item2 := pool.Get() - require.Equal(t, 1024, len(item2)) - - pool.Put(item1) - pool.Put(item2) - - item1 = pool.Get() - require.Equal(t, 1024, len(item1)) - - item2 = pool.Get() - require.Equal(t, 1024, len(item2)) - }) - - t.Run("is not racy", func(t *testing.T) { - pool := zeropool.New(func() []byte { return make([]byte, 1024) }) - - const iterations = 1e6 - const concurrency = math.MaxUint8 - var counter atomic.Int64 - - do := make(chan struct{}, 1e6) - for i := 0; i < iterations; i++ { - do <- struct{}{} - } - close(do) - - run := make(chan struct{}) - done := sync.WaitGroup{} - done.Add(concurrency) - for i := 0; i < concurrency; i++ { - go func(worker int) { - <-run - for range do { - item := pool.Get() - item[0] = byte(worker) - counter.Add(1) // Counts and also adds some delay to add raciness. - if item[0] != byte(worker) { - panic("wrong value") - } - pool.Put(item) - } - done.Done() - }(i) - } - close(run) - done.Wait() - t.Logf("Done %d iterations", counter.Load()) - }) - - t.Run("does not allocate", func(t *testing.T) { - pool := zeropool.New(func() []byte { return make([]byte, 1024) }) - // Warm up, this will alloate one slice. - slice := pool.Get() - pool.Put(slice) - - allocs := testing.AllocsPerRun(1000, func() { - slice := pool.Get() - pool.Put(slice) - }) - // Don't compare to 0, as when passing all the tests the GC could flush the pools during this test and we would allocate. - // Just check that it's less than 1 on average, which is mostly the same thing. - require.Less(t, allocs, 1., "Should not allocate.") - }) - - t.Run("zero value is valid", func(t *testing.T) { - var pool zeropool.Pool[[]byte] - slice := pool.Get() - pool.Put(slice) - - allocs := testing.AllocsPerRun(1000, func() { - slice := pool.Get() - pool.Put(slice) - }) - // Don't compare to 0, as when passing all the tests the GC could flush the pools during this test and we would allocate. - // Just check that it's less than 1 on average, which is mostly the same thing. - require.Less(t, allocs, 1., "Should not allocate.") - }) -} - -func BenchmarkZeropoolPool(b *testing.B) { - pool := zeropool.New(func() []byte { return make([]byte, 1024) }) - - // Warmup - item := pool.Get() - pool.Put(item) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - item := pool.Get() - pool.Put(item) - } -} - -// BenchmarkSyncPoolValue uses sync.Pool to store values, which makes an allocation on each Put call. -func BenchmarkSyncPoolValue(b *testing.B) { - pool := sync.Pool{New: func() any { - return make([]byte, 1024) - }} - - // Warmup - item := pool.Get().([]byte) - pool.Put(item) //nolint:staticcheck // This allocates. - - b.ResetTimer() - for i := 0; i < b.N; i++ { - item := pool.Get().([]byte) - pool.Put(item) //nolint:staticcheck // This allocates. - } -} - -// BenchmarkSyncPoolNewPointer uses sync.Pool to store pointers, but it calls Put with a new pointer every time. -func BenchmarkSyncPoolNewPointer(b *testing.B) { - pool := sync.Pool{New: func() any { - v := make([]byte, 1024) - return &v - }} - - // Warmup - item := pool.Get().(*[]byte) - pool.Put(item) //nolint:staticcheck // This allocates. - - b.ResetTimer() - for i := 0; i < b.N; i++ { - item := pool.Get().(*[]byte) - buf := *item - pool.Put(&buf) //nolint:staticcheck // New pointer. - } -} - -// BenchmarkSyncPoolPointer illustrates the optimal usage of sync.Pool, not always possible. -func BenchmarkSyncPoolPointer(b *testing.B) { - pool := sync.Pool{New: func() any { - v := make([]byte, 1024) - return &v - }} - - // Warmup - item := pool.Get().(*[]byte) - pool.Put(item) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - item := pool.Get().(*[]byte) - pool.Put(item) - } -}